From da7347f76fa13917e6af9aeb1f823e07fdc5ab5a Mon Sep 17 00:00:00 2001 From: KimLS Date: Sat, 16 Feb 2013 16:14:39 -0800 Subject: [PATCH] svn -> git Migration --- CMakeLists.txt | 164 + EMuShareMem/CMakeLists.txt | 46 + EMuShareMem/DLLMain.cpp | 37 + EMuShareMem/Doors.cpp | 136 + EMuShareMem/Doors.h | 22 + EMuShareMem/Items.cpp | 152 + EMuShareMem/Items.h | 23 + EMuShareMem/Loot.cpp | 214 + EMuShareMem/Loot.h | 29 + EMuShareMem/MMF.cpp | 353 + EMuShareMem/MMF.h | 46 + EMuShareMem/MMFMutex.cpp | 126 + EMuShareMem/MMFMutex.h | 43 + EMuShareMem/NPCFactionLists.cpp | 178 + EMuShareMem/NPCFactionLists.h | 22 + EMuShareMem/NPCTypes.cpp | 137 + EMuShareMem/NPCTypes.h | 18 + EMuShareMem/Opcodes.cpp | 140 + EMuShareMem/Opcodes.h | 10 + EMuShareMem/SkillCaps.cpp | 142 + EMuShareMem/SkillCaps.h | 10 + EMuShareMem/Spells.cpp | 98 + EMuShareMem/Spells.h | 16 + GPL.txt | 339 + changelog.txt | 7873 +++++++ cmake/FindMySQL.cmake | 85 + common/BasePacket.cpp | 146 + common/BasePacket.h | 93 + common/CMakeLists.txt | 313 + common/CRC16.cpp | 346 + common/CRC16.h | 6 + common/Condition.cpp | 153 + common/Condition.h | 56 + common/DBMemLeak.cpp | 62 + common/DBMemLeak.h | 25 + common/EMuShareMem.cpp | 300 + common/EMuShareMem.h | 187 + common/EQDB.cpp | 78 + common/EQDB.h | 54 + common/EQDBRes.cpp | 51 + common/EQDBRes.h | 48 + common/EQEMuError.cpp | 135 + common/EQEMuError.h | 36 + common/EQEmuConfig.cpp | 472 + common/EQEmuConfig.h | 227 + common/EQEmuConfig_elements.h | 11 + common/EQNetwork.cpp | 414 + common/EQNetwork.h | 121 + common/EQPacket.cpp | 511 + common/EQPacket.h | 155 + common/EQStream.cpp | 1433 ++ common/EQStream.h | 281 + common/EQStreamFactory.cpp | 347 + common/EQStreamFactory.h | 58 + common/EQStreamIdent.cpp | 199 + common/EQStreamIdent.h | 50 + common/EQStreamIntf.h | 39 + common/EQStreamLocator.h | 175 + common/EQStreamProxy.cpp | 101 + common/EQStreamProxy.h | 54 + common/EQStreamType.h | 15 + common/EmuTCPConnection.cpp | 857 + common/EmuTCPConnection.h | 103 + common/EmuTCPServer.cpp | 96 + common/EmuTCPServer.h | 38 + common/Item.cpp | 1951 ++ common/Item.h | 456 + common/MaxSkill.cpp | 1901 ++ common/MiscFunctions.cpp | 650 + common/MiscFunctions.h | 171 + common/Mutex.cpp | 285 + common/Mutex.h | 83 + common/ProcLauncher.cpp | 368 + common/ProcLauncher.h | 124 + common/SharedLibrary.cpp | 117 + common/SharedLibrary.h | 49 + common/SocketLib/Base64.cpp | 266 + common/SocketLib/Base64.h | 67 + common/SocketLib/File.cpp | 126 + common/SocketLib/File.h | 76 + common/SocketLib/HTTPSocket.cpp | 366 + common/SocketLib/HTTPSocket.h | 137 + common/SocketLib/HttpdCookies.cpp | 250 + common/SocketLib/HttpdCookies.h | 91 + common/SocketLib/HttpdForm.cpp | 621 + common/SocketLib/HttpdForm.h | 115 + common/SocketLib/HttpdSocket.cpp | 355 + common/SocketLib/HttpdSocket.h | 99 + common/SocketLib/IFile.h | 65 + common/SocketLib/MemFile.cpp | 214 + common/SocketLib/MemFile.h | 93 + common/SocketLib/Mime.cpp | 92 + common/SocketLib/Mime.h | 55 + common/SocketLib/Parse.cpp | 327 + common/SocketLib/Parse.h | 95 + common/SocketLib/README.eqemu | 5 + common/SocketLib/README.macosx | 5 + common/SocketLib/Utility.cpp | 169 + common/SocketLib/Utility.h | 70 + common/SocketLib/gpl.txt | 340 + common/SocketLib/socket_include.cpp | 87 + common/SocketLib/socket_include.h | 218 + common/StackWalker/StackWalker.cpp | 1222 ++ common/StackWalker/StackWalker.h | 192 + common/StructStrategy.cpp | 99 + common/StructStrategy.h | 73 + common/TCPBasicServer.h | 17 + common/TCPConnection.cpp | 959 + common/TCPConnection.h | 181 + common/TCPServer.cpp | 234 + common/TCPServer.h | 135 + common/XMLParser.cpp | 101 + common/XMLParser.h | 59 + common/ZoneNumbers.h | 490 + common/bodytypes.h | 64 + common/breakdowns.h | 134 + common/classes.cpp | 339 + common/classes.h | 92 + common/common_profile.h | 103 + common/crash.cpp | 116 + common/crash.h | 6 + common/crc32.cpp | 112 + common/crc32.h | 20 + common/database.cpp | 3182 +++ common/database.h | 273 + common/dbasync.cpp | 677 + common/dbasync.h | 176 + common/dbcore.cpp | 216 + common/dbcore.h | 51 + common/dbmemshare.cpp | 128 + common/debug.cpp | 439 + common/debug.h | 173 + common/deity.h | 65 + common/emu_opcodes.cpp | 38 + common/emu_opcodes.h | 53 + common/emu_oplist.h | 540 + common/eq_constants.h | 696 + common/eq_opcodes.h | 522 + common/eq_packet_structs.h | 5045 +++++ common/eqtime.cpp | 280 + common/eqtime.h | 57 + common/errmsg.h | 59 + common/extprofile.cpp | 57 + common/extprofile.h | 66 + common/guild_base.cpp | 1567 ++ common/guild_base.h | 150 + common/guilds.cpp | 341 + common/guilds.h | 46 + common/item_fieldlist.h | 197 + common/item_struct.h | 247 + common/languages.h | 52 + common/linked_list.h | 444 + common/logsys.cpp | 160 + common/logsys.h | 187 + common/logsys_eqemu.cpp | 43 + common/logtypes.h | 265 + common/mail_oplist.h | 13 + common/md5.cpp | 271 + common/md5.h | 45 + common/misc.cpp | 574 + common/misc.h | 43 + common/moremath.cpp | 45 + common/moremath.h | 26 + common/op_codes.h | 19 + common/opcode_dispatch.h | 636 + common/opcode_map.cpp | 309 + common/opcodemgr.cpp | 371 + common/opcodemgr.h | 176 + common/packet_dump.cpp | 195 + common/packet_dump.h | 35 + common/packet_dump_file.cpp | 212 + common/packet_dump_file.h | 37 + common/packet_functions.cpp | 334 + common/packet_functions.h | 43 + common/packetfile.cpp | 445 + common/packetfile.h | 130 + common/patches/Client62.cpp | 1035 + common/patches/Client62.h | 36 + common/patches/Client62_itemfields.h | 166 + common/patches/Client62_ops.h | 36 + common/patches/Client62_structs.h | 3101 +++ common/patches/RoF.cpp | 5314 +++++ common/patches/RoF.h | 36 + common/patches/RoF_itemfields.h | 439 + common/patches/RoF_ops.h | 160 + common/patches/RoF_structs.h | 4795 +++++ common/patches/SSDeclare.h | 6 + common/patches/SSDefine.h | 155 + common/patches/SSRegister.h | 3 + common/patches/SoD.cpp | 3536 ++++ common/patches/SoD.h | 36 + common/patches/SoD_itemfields.h | 439 + common/patches/SoD_ops.h | 117 + common/patches/SoD_structs.h | 4349 ++++ common/patches/SoF.cpp | 2841 +++ common/patches/SoF.h | 36 + common/patches/SoF_itemfields.h | 439 + common/patches/SoF_opcode_list.h | 1229 ++ common/patches/SoF_ops.h | 100 + common/patches/SoF_structs.h | 4066 ++++ common/patches/Titanium.cpp | 1450 ++ common/patches/Titanium.h | 36 + common/patches/Titanium_itemfields.h | 174 + common/patches/Titanium_ops.h | 53 + common/patches/Titanium_structs.h | 3280 +++ common/patches/Underfoot.cpp | 3945 ++++ common/patches/Underfoot.h | 36 + common/patches/Underfoot_itemfields.h | 439 + common/patches/Underfoot_ops.h | 128 + common/patches/Underfoot_structs.h | 4423 ++++ common/patches/makepatch | 12 + common/patches/patches.cpp | 28 + common/patches/patches.h | 15 + common/patches/template.cpp | 173 + common/patches/template.h | 37 + common/patches/template_ops.h | 10 + common/patches/template_structs.h | 28 + common/perl_EQDB.cpp | 256 + common/perl_EQDBRes.cpp | 295 + common/platform.cpp | 11 + common/platform.h | 18 + common/profiler.h | 92 + common/ptimer.cpp | 504 + common/ptimer.h | 145 + common/queue.h | 126 + common/queues.h | 61 + common/races.cpp | 113 + common/races.h | 516 + common/rdtsc.cpp | 176 + common/rdtsc.h | 87 + common/rulesys.cpp | 492 + common/rulesys.h | 152 + common/ruletypes.h | 518 + common/seperator-2.h | 233 + common/seperator.h | 156 + common/serverinfo.cpp | 123 + common/serverinfo.h | 29 + common/servertalk.h | 1236 ++ common/shareddb.cpp | 1708 ++ common/shareddb.h | 154 + common/skills.h | 104 + common/timeoutmgr.cpp | 82 + common/timeoutmgr.h | 67 + common/timer.cpp | 194 + common/timer.h | 70 + common/tinyxml/tinystr.cpp | 318 + common/tinyxml/tinystr.h | 250 + common/tinyxml/tinyxml.cpp | 1592 ++ common/tinyxml/tinyxml.h | 1426 ++ common/tinyxml/tinyxmlerror.cpp | 51 + common/tinyxml/tinyxmlparser.cpp | 1508 ++ common/types.h | 115 + common/unix.cpp | 49 + common/unix.h | 34 + common/useperl.h | 50 + common/version.h | 36 + common/win_getopt.cpp | 124 + common/win_getopt.h | 24 + common/worldconn.cpp | 101 + common/worldconn.h | 59 + dependencies/.gitignore | 0 eqlaunch/CMakeLists.txt | 37 + eqlaunch/ZoneLaunch.cpp | 287 + eqlaunch/ZoneLaunch.h | 87 + eqlaunch/eqlaunch.cpp | 207 + eqlaunch/worldserver.cpp | 155 + eqlaunch/worldserver.h | 46 + loginserver/CMakeLists.txt | 66 + loginserver/Client.cpp | 396 + loginserver/Client.h | 145 + loginserver/ClientManager.cpp | 207 + loginserver/ClientManager.h | 80 + loginserver/Config.cpp | 215 + loginserver/Config.h | 63 + loginserver/Database.h | 81 + loginserver/DatabaseMySQL.cpp | 290 + loginserver/DatabaseMySQL.h | 98 + loginserver/DatabasePostgreSQL.cpp | 234 + loginserver/DatabasePostgreSQL.h | 93 + loginserver/EQCryptoAPI.h | 25 + loginserver/Encryption.cpp | 144 + loginserver/Encryption.h | 106 + loginserver/ErrorLog.cpp | 209 + loginserver/ErrorLog.h | 81 + loginserver/LoginServer.h | 60 + loginserver/LoginStructures.h | 115 + loginserver/Main.cpp | 275 + loginserver/Options.h | 176 + loginserver/ServerManager.cpp | 344 + loginserver/ServerManager.h | 91 + loginserver/WorldServer.cpp | 541 + loginserver/WorldServer.h | 162 + .../login_util/EQEmuLoginServerDBInstall.sql | 57 + .../EQEmuLoginServerPostgreDBInstall.sql | 57 + loginserver/login_util/login.ini | 35 + loginserver/login_util/login_opcodes.conf | 12 + loginserver/login_util/login_opcodes_sod.conf | 12 + .../login_util/tblLoginServerAccounts.sql | 11 + .../login_util/tblServerAdminRegistration.sql | 12 + loginserver/login_util/tblServerListType.sql | 10 + .../login_util/tblWorldServerRegistration.sql | 13 + queryserv/CMakeLists.txt | 43 + queryserv/database.cpp | 323 + queryserv/database.h | 63 + queryserv/lfguild.cpp | 435 + queryserv/lfguild.h | 57 + queryserv/queryserv.cpp | 161 + queryserv/queryservconfig.cpp | 29 + queryserv/queryservconfig.h | 56 + queryserv/worldserver.cpp | 179 + queryserv/worldserver.h | 35 + ucs/CMakeLists.txt | 45 + ucs/chatchannel.cpp | 706 + ucs/chatchannel.h | 92 + ucs/clientlist.cpp | 2409 +++ ucs/clientlist.h | 195 + ucs/database.cpp | 770 + ucs/database.h | 73 + ucs/ucs.cpp | 192 + ucs/ucsconfig.cpp | 29 + ucs/ucsconfig.h | 56 + ucs/worldserver.cpp | 134 + ucs/worldserver.h | 35 + utils/CMakeLists.txt | 22 + utils/EQExtractor2/EQExtractor2.sln | 20 + utils/EQExtractor2/EQExtractor2/ChangeLog.txt | 310 + .../EQExtractor2/EQApplicationLayer.cs | 1730 ++ .../EQExtractor2/EQExtractor2.csproj | 217 + .../EQExtractor2Form1.Designer.cs | 280 + .../EQExtractor2/EQExtractor2Form1.cs | 559 + .../EQExtractor2/EQExtractor2Form1.resx | 132 + utils/EQExtractor2/EQExtractor2/EQPacket.cs | 902 + .../EQExtractor2/GenerateSQLForm.Designer.cs | 625 + .../EQExtractor2/GenerateSQLForm.cs | 103 + .../EQExtractor2/GenerateSQLForm.resx | 126 + .../EQExtractor2/InternalTypes.cs | 691 + .../EQExtractor2/LogForm.Designer.cs | 67 + utils/EQExtractor2/EQExtractor2/LogForm.cs | 26 + utils/EQExtractor2/EQExtractor2/LogForm.resx | 120 + .../EQExtractor2/OpcodeManager.cs | 161 + .../EQExtractor2/PacketDotNet.xml | 5278 +++++ .../EQExtractor2/PatchAug04-2011.cs | 112 + .../EQExtractor2/PatchAugust15-2012.cs | 46 + .../EQExtractor2/PatchDec7-2010.cs | 57 + .../EQExtractor2/PatchDecember10-2012.cs | 1641 ++ .../EQExtractor2/PatchFeb8-2011.cs | 63 + .../EQExtractor2/PatchFebruary11-2013.cs | 23 + .../EQExtractor2/EQExtractor2/PatchGeneric.cs | 307 + .../EQExtractor2/PatchJanuary16-2013.cs | 467 + .../EQExtractor2/PatchJuly13-2010.cs | 143 + .../EQExtractor2/PatchJuly13-2012.cs | 24 + .../EQExtractor2/PatchJune25-2012.cs | 20 + .../EQExtractor2/PatchMar15-2012.cs | 458 + .../EQExtractor2/PatchMarch15-2011.cs | 24 + .../EQExtractor2/PatchMay12-2010.cs | 1449 ++ .../EQExtractor2/PatchMay12-2011.cs | 296 + .../EQExtractor2/PatchMay24-2011.cs | 22 + .../EQExtractor2/PatchNov17-2011.cs | 456 + .../EQExtractor2/PatchOct20-2010.cs | 22 + utils/EQExtractor2/EQExtractor2/PatchSoD.cs | 991 + .../EQExtractor2/PatchTestSep1-2010.cs | 341 + .../EQExtractor2/PatchTestSep22-2010.cs | 349 + .../PatchTestServerFebruary5-2013.cs | 23 + .../PatchTestServerJanuary16-2013.cs | 23 + utils/EQExtractor2/EQExtractor2/Program.cs | 21 + .../EQExtractor2/Properties/AssemblyInfo.cs | 36 + .../Properties/Resources.Designer.cs | 63 + .../EQExtractor2/Properties/Resources.resx | 117 + .../Properties/Settings.Designer.cs | 62 + .../EQExtractor2/Properties/Settings.settings | 15 + utils/EQExtractor2/EQExtractor2/SharpPcap.xml | 1415 ++ .../EQExtractor2/UserOptions.Designer.cs | 153 + .../EQExtractor2/EQExtractor2/UserOptions.cs | 29 + .../EQExtractor2/UserOptions.resx | 123 + utils/EQExtractor2/EQExtractor2/Utils.cs | 314 + utils/EQExtractor2/EQExtractor2/app.config | 21 + .../EQExtractor2/patch_Aug04-2011.conf | 616 + .../EQExtractor2/patch_August15-2012.conf | 633 + .../EQExtractor2/patch_Dec10-2012.conf | 651 + .../EQExtractor2/patch_Dec7-2010.conf | 611 + .../EQExtractor2/patch_Feb11-2013.conf | 653 + .../EQExtractor2/patch_Feb8-2011.conf | 611 + .../EQExtractor2/patch_Jan16-2013.conf | 653 + .../EQExtractor2/patch_July13-2012.conf | 633 + .../EQExtractor2/patch_June25-2012.conf | 633 + .../EQExtractor2/patch_Mar15-2012.conf | 631 + .../EQExtractor2/patch_March15-2011.conf | 611 + .../EQExtractor2/patch_May12-2010.conf | 619 + .../EQExtractor2/patch_May12-2011.conf | 611 + .../EQExtractor2/patch_Nov17-2011.conf | 607 + .../EQExtractor2/patch_Sep01-2010.conf | 610 + .../EQExtractor2/patch_Sep22-2010.conf | 612 + .../EQExtractor2/EQExtractor2/patch_SoD.conf | 609 + .../patch_TestServer-Feb5-2013.conf | 653 + .../patch_TestServer-Jan16-2013.conf | 653 + utils/TaskMaster/EQEmuWxProject.sln | 21 + utils/TaskMaster/EQEmuWxProject.vcproj | 170 + utils/TaskMaster/ErrorLog.cpp | 71 + utils/TaskMaster/ErrorLog.h | 35 + utils/TaskMaster/activities.cpp | 648 + utils/TaskMaster/base.cpp | 672 + utils/TaskMaster/base.h | 255 + utils/TaskMaster/base_utility.cpp | 442 + utils/TaskMaster/changelog.txt | 21 + utils/TaskMaster/goals.cpp | 433 + utils/TaskMaster/items.h | 9 + utils/TaskMaster/proximity.cpp | 468 + utils/TaskMaster/tasks.cpp | 328 + utils/TaskMaster/tasks.h | 75 + utils/TaskMaster/utility.cpp | 29 + utils/TaskMaster/utility.h | 6 + utils/azone2/3d.hpp | 101 + utils/azone2/3d_base.hpp | 107 + utils/azone2/CMakeLists.txt | 119 + utils/azone2/GLModelViewer.cpp | 548 + utils/azone2/GLModelViewer.h | 3 + utils/azone2/README | 79 + utils/azone2/archive.hpp | 32 + utils/azone2/awater.cpp | 375 + utils/azone2/awater.h | 16 + utils/azone2/azone.cpp | 1623 ++ utils/azone2/azone.h | 200 + utils/azone2/azone.ini | 10 + utils/azone2/dat.cpp | 1008 + utils/azone2/dat.hpp | 61 + utils/azone2/file.cpp | 31 + utils/azone2/file.hpp | 8 + utils/azone2/file_loader.hpp | 24 + utils/azone2/global.cpp | 16 + utils/azone2/global.hpp | 34 + utils/azone2/listobj.cpp | 214 + utils/azone2/octree.hpp | 43 + utils/azone2/pfs.cpp | 253 + utils/azone2/pfs.hpp | 21 + utils/azone2/s3d.h | 51 + utils/azone2/ter.cpp | 243 + utils/azone2/ter.hpp | 62 + utils/azone2/types.h | 16 + utils/azone2/wld.cpp | 758 + utils/azone2/wld.hpp | 84 + utils/azone2/wld_structs.hpp | 137 + utils/azone2/zon.cpp | 433 + utils/azone2/zon.hpp | 47 + utils/azone2/zonv4.cpp | 255 + utils/azone2/zonv4.hpp | 43 + utils/cleanipc.cpp | 68 + utils/defaults/Maps/.keep | 0 utils/defaults/commands.pl | 0 utils/defaults/eqemu_config.xml | 7 + utils/defaults/eqemu_config.xml.full | 90 + utils/defaults/log.ini | 0 utils/defaults/logs/.keep | 0 utils/defaults/mime.types | 26 + utils/defaults/plugin.pl | 0 utils/defaults/plugins/.keep | 0 utils/defaults/plugins/check_handin.pl | 78 + utils/defaults/quests/.keep | 0 utils/defaults/templates/TMimages/branch.gif | Bin 0 -> 70 bytes .../templates/TMimages/branchbottom.gif | Bin 0 -> 72 bytes .../defaults/templates/TMimages/branchtop.gif | Bin 0 -> 67 bytes utils/defaults/templates/TMimages/folder.gif | Bin 0 -> 922 bytes utils/defaults/templates/TMimages/line.gif | Bin 0 -> 63 bytes .../templates/TMimages/linebottom.gif | Bin 0 -> 58 bytes utils/defaults/templates/TMimages/minus.gif | Bin 0 -> 155 bytes .../templates/TMimages/minusbottom.gif | Bin 0 -> 152 bytes .../defaults/templates/TMimages/minustop.gif | Bin 0 -> 151 bytes utils/defaults/templates/TMimages/plus.gif | Bin 0 -> 159 bytes .../templates/TMimages/plusbottom.gif | Bin 0 -> 156 bytes utils/defaults/templates/TMimages/plustop.gif | Bin 0 -> 154 bytes utils/defaults/templates/TreeMenu.css | 60 + utils/defaults/templates/TreeMenu.js | 636 + utils/defaults/templates/account.html | 194 + utils/defaults/templates/accounts.html | 77 + utils/defaults/templates/action.html | 43 + utils/defaults/templates/bootzone.html | 41 + utils/defaults/templates/bugs.html | 81 + utils/defaults/templates/chat.html | 15 + utils/defaults/templates/commands.html | 104 + utils/defaults/templates/config.html | 33 + utils/defaults/templates/configdl.html | 10 + utils/defaults/templates/database.html | 19 + utils/defaults/templates/delete.gif | Bin 0 -> 339 bytes utils/defaults/templates/favicon.ico | 1 + utils/defaults/templates/filters.html | 16 + utils/defaults/templates/guild.html | 115 + utils/defaults/templates/guildcreate.html | 47 + utils/defaults/templates/guilds.html | 40 + utils/defaults/templates/guildsearch.html | 40 + utils/defaults/templates/head_01_nb.jpg | Bin 0 -> 7645 bytes utils/defaults/templates/head_02_nb.jpg | Bin 0 -> 14172 bytes utils/defaults/templates/head_03_nb.jpg | Bin 0 -> 10242 bytes utils/defaults/templates/head_04_nb.jpg | Bin 0 -> 1356 bytes utils/defaults/templates/index.html | 17 + utils/defaults/templates/launcher.html | 200 + utils/defaults/templates/launchers.html | 59 + utils/defaults/templates/main.css | 200 + utils/defaults/templates/menu.html | 52 + utils/defaults/templates/minilogin.html | 20 + utils/defaults/templates/petitions.html | 39 + utils/defaults/templates/players.html | 72 + utils/defaults/templates/reset.png | Bin 0 -> 1305 bytes utils/defaults/templates/rule.html | 44 + utils/defaults/templates/rules.html | 103 + utils/defaults/templates/spacer.gif | Bin 0 -> 43 bytes utils/defaults/templates/status.html | 17 + utils/defaults/templates/submit.png | Bin 0 -> 1305 bytes utils/defaults/templates/top.html | 22 + utils/defaults/templates/update.png | Bin 0 -> 1431 bytes utils/defaults/templates/variables.html | 110 + utils/defaults/templates/world.html | 32 + utils/defaults/templates/worldstatus.html | 23 + utils/defaults/templates/zone.html | 17 + utils/defaults/templates/zones.html | 75 + utils/defaults/updated_templates/access.txt | 8 + .../updated_templates/actions/bug_action.html | 25 + .../actions/console_action.html | 25 + .../actions/launcher_action.html | 129 + .../actions/world_action.html | 23 + .../actions/zone_action.html | 16 + .../updated_templates/blank.temp.html | 22 + utils/defaults/updated_templates/bugs.html | 157 + utils/defaults/updated_templates/bugview.html | 141 + utils/defaults/updated_templates/console.html | 138 + .../updated_templates/data/bug_data.html | 44 + .../updated_templates/data/console_data.html | 31 + .../data/launcher_count.html | 6 + .../updated_templates/data/launcher_data.html | 69 + .../updated_templates/data/player_count.html | 6 + .../updated_templates/data/player_data.html | 42 + .../updated_templates/data/world_status.html | 14 + .../updated_templates/data/zone_count.html | 6 + .../updated_templates/data/zone_data.html | 33 + .../updated_templates/images/loading.gif | Bin 0 -> 673 bytes utils/defaults/updated_templates/index.html | 191 + .../defaults/updated_templates/launcher.html | 317 + .../defaults/updated_templates/launchers.html | 166 + .../defaults/updated_templates/petitions.html | 43 + utils/defaults/updated_templates/players.html | 142 + .../updated_templates/playerview.html | 121 + .../updated_templates/scripts/jquery.js | 4 + .../updated_templates/scripts/menu.pl | 18 + .../scripts/menu_noaccess.pl | 13 + .../updated_templates/style/style.css | 268 + utils/defaults/updated_templates/zone.html | 18 + utils/defaults/updated_templates/zones.html | 130 + .../defaults/updated_templates/zoneview.html | 111 + utils/defaults/worldui.pl | 0 utils/deprecated/0.6.1-upgrade.sql | 328 + utils/deprecated/ZONECFG.SQL | 254 + utils/deprecated/apathing/actions.cpp | 2130 ++ utils/deprecated/apathing/apathing.cpp | 591 + utils/deprecated/apathing/apathing.h | 290 + utils/deprecated/apathing/boostcrap.cpp | 759 + utils/deprecated/apathing/boostcrap.h | 32 + utils/deprecated/apathing/gpoint.cpp | 113 + utils/deprecated/apathing/gpoint.h | 49 + utils/deprecated/apathing/load_db.cpp | 276 + utils/deprecated/apathing/pathfinding.cpp | 212 + utils/deprecated/apathing/quadtree.cpp | 366 + utils/deprecated/apathing/quadtree.h | 88 + utils/deprecated/apathing/runall | 23 + utils/deprecated/apathing/shortnames | 131 + utils/deprecated/asmtools/locate_stringids.pl | 32 + utils/deprecated/asmtools/opcodes_to_ida.pl | 17 + utils/deprecated/asmtools/sig_to_ida.pl | 14 + utils/deprecated/asmtools/stringids_to_ida.pl | 20 + utils/deprecated/azone/3d.hpp | 75 + utils/deprecated/azone/3d_base.hpp | 73 + utils/deprecated/azone/Azone.vcproj | 146 + utils/deprecated/azone/README | 24 + utils/deprecated/azone/archive.hpp | 32 + utils/deprecated/azone/awater.cpp | 362 + utils/deprecated/azone/awater.h | 10 + utils/deprecated/azone/azone.cpp | 1348 ++ utils/deprecated/azone/azone.h | 192 + utils/deprecated/azone/file.cpp | 31 + utils/deprecated/azone/file.hpp | 8 + utils/deprecated/azone/file_loader.hpp | 24 + utils/deprecated/azone/global.hpp | 24 + utils/deprecated/azone/octree.hpp | 43 + utils/deprecated/azone/pfs.cpp | 222 + utils/deprecated/azone/pfs.hpp | 21 + utils/deprecated/azone/s3d.c | 136 + utils/deprecated/azone/s3d.h | 51 + utils/deprecated/azone/ter.cpp | 413 + utils/deprecated/azone/ter.hpp | 51 + utils/deprecated/azone/types.h | 16 + utils/deprecated/azone/wld.c | 480 + utils/deprecated/azone/wld.h | 195 + utils/deprecated/azone/zon.cpp | 299 + utils/deprecated/azone/zon.hpp | 42 + utils/deprecated/charmove/Makefile.bsd | 16 + utils/deprecated/charmove/Makefile.common | 24 + utils/deprecated/charmove/char_format.h | 77 + utils/deprecated/charmove/charmove.dsp | 110 + utils/deprecated/charmove/export_char.cpp | 263 + utils/deprecated/charmove/import_char.cpp | 190 + utils/deprecated/convert_ops.pl | 30 + utils/deprecated/hexvis/HexRichEdit.cpp | 75 + utils/deprecated/hexvis/HexRichEdit.h | 52 + utils/deprecated/hexvis/StdAfx.cpp | 8 + utils/deprecated/hexvis/StdAfx.h | 27 + utils/deprecated/hexvis/hexvis.clw | 95 + utils/deprecated/hexvis/hexvis.cpp | 75 + utils/deprecated/hexvis/hexvis.dsp | 154 + utils/deprecated/hexvis/hexvis.dsw | 29 + utils/deprecated/hexvis/hexvis.h | 49 + utils/deprecated/hexvis/hexvis.rc | 239 + utils/deprecated/hexvis/hexvisDlg.cpp | 1050 + utils/deprecated/hexvis/hexvisDlg.h | 100 + utils/deprecated/hexvis/res/hexvis.ico | Bin 0 -> 1078 bytes utils/deprecated/hexvis/res/hexvis.rc2 | 13 + utils/deprecated/hexvis/resource.h | 40 + .../items-0.6.0-DR2-0.6.1-DR1-convert.sql | 80 + utils/deprecated/itemtablechanges.sql | 22 + utils/deprecated/perlxs/EQDB.h | 9 + utils/deprecated/perlxs/EQDB.h.xs | 37 + utils/deprecated/perlxs/EQDBRes.h | 7 + utils/deprecated/perlxs/EQDBRes.h.xs | 33 + utils/deprecated/perlxs/EQLConfig.h | 19 + utils/deprecated/perlxs/EQLConfig.h.xs | 37 + utils/deprecated/perlxs/EQW.h | 36 + utils/deprecated/perlxs/EQW.h.xs | 37 + utils/deprecated/perlxs/HTTPRequest.h | 14 + utils/deprecated/perlxs/HTTPRequest.h.xs | 37 + utils/deprecated/perlxs/PlayerCorpse.h | 28 + utils/deprecated/perlxs/PlayerCorpse.h.xs | 37 + utils/deprecated/perlxs/all_convert | 27 + utils/deprecated/perlxs/class.sh | 3 + utils/deprecated/perlxs/class.typemap | 442 + utils/deprecated/perlxs/client.h | 177 + utils/deprecated/perlxs/client.h.xs | 36 + utils/deprecated/perlxs/convert | 44 + utils/deprecated/perlxs/entity.h | 62 + utils/deprecated/perlxs/entity.h.xs | 37 + utils/deprecated/perlxs/export | 12 + utils/deprecated/perlxs/groups.h | 17 + utils/deprecated/perlxs/groups.h.xs | 37 + utils/deprecated/perlxs/mob.h | 249 + utils/deprecated/perlxs/mob.h.xs | 39 + utils/deprecated/perlxs/npc.h | 63 + utils/deprecated/perlxs/npc.h.xs | 39 + utils/deprecated/perlxs/perlpacket.h | 28 + utils/deprecated/perlxs/perlpacket.h.xs | 36 + utils/deprecated/perlxs/put | 10 + utils/deprecated/ppconvert/Makefile.bsd | 16 + utils/deprecated/ppconvert/Makefile.common | 26 + utils/deprecated/ppconvert/ppconvert.cpp | 325 + utils/deprecated/ppconvert/ppconvert.dsp | 111 + utils/deprecated/ppskillfix/Makefile.bsd | 16 + utils/deprecated/ppskillfix/Makefile.common | 26 + utils/deprecated/ppskillfix/ppskillfix.cpp | 160 + utils/deprecated/ppskillfix/ppskillfix.dsp | 111 + utils/deprecated/ppskillfix/ppskillfix.dsw | 29 + utils/deprecated/spell_explorer.cpp | 188 + utils/deprecated/wr_update.sql | 824 + utils/patches/mail_opcodes.conf | 14 + utils/patches/opcodes.conf | 512 + utils/patches/patch_6.2.conf | 539 + utils/patches/patch_RoF.conf | 653 + utils/patches/patch_SoD.conf | 661 + utils/patches/patch_SoF.conf | 646 + utils/patches/patch_Titanium.conf | 594 + utils/patches/patch_Underfoot.conf | 655 + utils/pfs_list/CMakeLists.txt | 20 + utils/pfs_list/Common/CMakeLists.txt | 17 + utils/pfs_list/Common/Include/Archive.h | 20 + utils/pfs_list/Common/Include/Compression.h | 9 + utils/pfs_list/Common/Include/PFSArchive.h | 35 + .../pfs_list/Common/Include/PFSDataStructs.h | 36 + utils/pfs_list/Common/Source/Compression.cpp | 19 + utils/pfs_list/Common/Source/PFSArchive.cpp | 220 + utils/pfs_list/PFSList/CMakeLists.txt | 23 + utils/pfs_list/PFSList/Source/main.cpp | 35 + utils/player_profile_set/Readme.txt | 1 + utils/player_profile_set/bin/database.ini | 4 + .../player_profile_set/player_profile_set.sln | 20 + .../player_profile_set/MiscFunctions.cpp | 331 + .../player_profile_set/MiscFunctions.h | 79 + .../player_profile_set/database.cpp | 133 + .../player_profile_set/database.h | 45 + .../player_profile_set/eq_player_structs.h | 4027 ++++ .../player_profile_set/eqemu_string.h | 21 + .../player_profile_set/ini.cpp | 118 + .../player_profile_set/ini.h | 32 + .../player_profile_set/main.cpp | 143 + .../player_profile_set/main.h | 16 + .../player_profile_set.vcproj | 233 + .../player_profile_set/types.h | 110 + utils/scripts/Examples/ListIteration.pl | 89 + utils/scripts/Examples/SpawnManipulation.pl | 17 + utils/scripts/export_spells.pl | 87 + utils/scripts/factionmod.pl | 112 + utils/scripts/import_showeq | 72 + utils/scripts/import_spells.pl | 126 + utils/scripts/load_13thfloor_items.pl | 77 + utils/scripts/ppreader.pl | 226 + utils/scripts/schema.xml | 5738 +++++ utils/scripts/serialize_items.pl | 81 + utils/scripts/struct_fields.sh | 3 + utils/scripts/throwpackets.pl | 53 + utils/sql/queryserv/2079_player_speech.sql | 14 + utils/sql/queryserv/2268_QueryServ.sql | 22 + utils/sql/queryserv/2304_QueryServ.sql | 41 + utils/sql/queryserv/2361_QueryServ.sql | 141 + utils/sql/svn/1022_botadventuring.sql | 30 + utils/sql/svn/1027_botactives.sql | 7 + utils/sql/svn/1030_botzoningsupport.sql | 3 + utils/sql/svn/1036_botbuffs.sql | 21 + utils/sql/svn/1038_botpetstatepersists.sql | 36 + ...038_grouptablesuniquecolumndefinitions.sql | 2 + utils/sql/svn/1039_botguilds.sql | 112 + utils/sql/svn/103_optional_chat_rules.sql | 2 + .../svn/1040_DeprecatedBotRaidsSystems.sql | 1 + utils/sql/svn/104_traps.sql | 4 + utils/sql/svn/1057_titles.sql | 9 + utils/sql/svn/106_optional_proc_rules.sql | 5 + utils/sql/svn/1077_botgroups.sql | 35 + utils/sql/svn/1136_spell_globals.sql | 10 + .../svn/1144_optional_rule_return_nodrop.sql | 1 + utils/sql/svn/1195_account_suspendeduntil.sql | 2 + utils/sql/svn/120_damageshieldtypes.sql | 401 + utils/sql/svn/1259_npc_skill_types.sql | 2 + utils/sql/svn/125_aggrozone.sql | 1 + utils/sql/svn/127_optional_spell_rules.sql | 7 + utils/sql/svn/1280_bot_augs.sql | 18 + utils/sql/svn/1290_optional_exp_loss_rule.sql | 1 + utils/sql/svn/1293_guild_bank.sql | 15 + .../sql/svn/129_optional_shared_plat_rule.sql | 1 + utils/sql/svn/131_optional_combat_rules.sql | 6 + utils/sql/svn/133_task_repeatable.sql | 2 + .../svn/1379_loginserver_trusted_server.sql | 1 + utils/sql/svn/1392_recipe_learning.sql | 12 + .../1394_optional_rule_sod_hp_mana_end.sql | 1 + utils/sql/svn/1404_faction_list.sql | 7 + .../svn/1410_optional_sod_aas_ht_and_loh.sql | 40 + .../svn/142_deathpeace_and_lifetap_aas.sql | 26 + utils/sql/svn/1436_login_server_table_fix.sql | 1 + utils/sql/svn/1446_allowrest_optional.sql | 152 + utils/sql/svn/1446_allowrest_required.sql | 1 + utils/sql/svn/1450_cvs.sql | 1 + utils/sql/svn/1451_guilds.sql | 2 + utils/sql/svn/1498_instance_adventure.sql | 8 + utils/sql/svn/14_optional_merchantlist.sql | 1 + utils/sql/svn/1510_global_instances.sql | 2 + utils/sql/svn/1511_map_path_loading.sql | 1 + utils/sql/svn/1513_zone_points.sql | 7 + utils/sql/svn/1519_zone_primary_key_id.sql | 1 + utils/sql/svn/1542_items_table_cleanup.sql | 32 + utils/sql/svn/1548_nimbuseffect_required.sql | 1 + .../svn/1562_instanced_spawnconditions.sql | 8 + utils/sql/svn/1586_waypoints_optional.sql | 1 + utils/sql/svn/158_optional_death_exp_loss.sql | 1 + utils/sql/svn/1610_tradeskill_required.sql | 1 + utils/sql/svn/1618_zone.sql | 2 + ...625_optional_rule_class_race_exp_bonus.sql | 1 + .../1672_optional_rules_respawn_window.sql | 2 + .../svn/1679_optional_rules_blocked_buffs.sql | 2 + .../1696_modify_zone_and_object_tables.sql | 19 + utils/sql/svn/1711_account_restricted_aa.sql | 3 + .../1717_optional_rule_bash_stun_chance.sql | 2 + utils/sql/svn/1718_optional_rules_mod3s.sql | 9 + .../svn/1719_optional_triggerOnCastAAs.sql | 49 + utils/sql/svn/1720_optional_sql_AAs.sql | 218 + .../1720_required_sql_AA_effects_update.sql | 82 + ...721_optional_sql_drakkin_breath_update.sql | 6 + .../1721_required_sql_altadv_vars_update.sql | 1 + ...723_optional_sql_new_stats_window_rule.sql | 1 + .../sql/svn/1723_required_sql_corruption.sql | 3 + .../sql/svn/1736_optional_sql_feral_swipe.sql | 2 + .../1737_required_sql_rule_and_aa_update.sql | 12 + .../svn/1746_optional_sql_bot_manaregen.sql | 1 + .../1747_optional_HoT_zone_and_zonepoints.sql | 111 + .../svn/1750_optional_sql_reflect_rule.sql | 2 + .../sql/svn/1753_optional_haste_cap_rule.sql | 1 + .../1753_required_sql_healing_adept_aa.sql | 5 + ...1754_required_sql_healing_adept_aa_fix.sql | 10 + .../svn/1755_required_sql_fear_resist_aas.sql | 5 + utils/sql/svn/176_melody.sql | 1 + .../svn/1784_optional_corpsedrag_rules.sql | 2 + utils/sql/svn/1786_required_update_to_aas.sql | 20 + .../1790_required_aa_required_level_cost.sql | 54 + utils/sql/svn/1793_resist_adjust.sql | 1 + ...799_optional_rest_regen_endurance_rule.sql | 1 + utils/sql/svn/1802_required_doppelganger.sql | 7 + .../1803_required_tasks_xpreward_signed.sql | 8 + .../svn/1804_required_ae_melee_updates.sql | 11 + utils/sql/svn/1809_optional_rules.sql | 2 + ...813_required_doppelganger_npcid_change.sql | 7 + .../1817_optional_npc_archery_bonus_rule.sql | 1 + utils/sql/svn/1823_optional_delay_death.sql | 6 + .../1847_required_doors_dest_zone_size_32.sql | 1 + ...859_optional_item_casts_use_focus_rule.sql | 1 + .../svn/1884_optional_bot_spells_update.sql | 55 + .../1885_optional_rules_fv_pvp_expansions.sql | 5 + .../sql/svn/1889_optional_skill_cap_rule.sql | 1 + utils/sql/svn/189_character_.sql | 3 + .../1908_required_npc_types_definitions.sql | 2 + utils/sql/svn/1926_optional_stat_cap.sql | 1 + utils/sql/svn/1944_spawn2.sql | 1 + utils/sql/svn/1946_doors.sql | 2 + .../1960_optional_console_timeout_rule.sql | 1 + ...2_optional_guild_creation_window_rules.sql | 5 + .../1963_optional_rule_live_like_focuses.sql | 1 + utils/sql/svn/1968_optional_enrage_rules.sql | 2 + utils/sql/svn/196_trader.sql | 22 + .../svn/1972_optional_extradmg_item_cap.sql | 1 + .../svn/1974_required_bot_spells_update.sql | 34 + utils/sql/svn/1977_underwater.sql | 1 + ...ptional_intoxication_and_looting_rules.sql | 2 + utils/sql/svn/1_task_system.sql | 169 + utils/sql/svn/2004_charges_alt_currency.sql | 21 + ..._optional_specialization_training_rule.sql | 1 + .../2016_optional_rule_bot_aa_expansion.sql | 1 + utils/sql/svn/2023_optional_mysqlcli.sql | 1 + .../sql/svn/2024_optional_update_crystals.sql | 2 + utils/sql/svn/2024_required_update.sql | 804 + .../svn/2057_required_discovered_items.sql | 6 + .../2058_optional_rule_discovered_items.sql | 1 + .../sql/svn/2062_required_version_changes.sql | 7 + utils/sql/svn/2069_required_pets.sql | 57 + ...red_bots_hp_and_mana_and_spell_updates.sql | 224 + ...098_required_zonepoint_version_changes.sql | 1 + ...quired_discovered_items_account_status.sql | 2 + utils/sql/svn/2104_required_group_roles.sql | 2 + utils/sql/svn/2107_required_bot_stances.sql | 6 + utils/sql/svn/210_undyeme.sql | 1 + utils/sql/svn/2129_required_lfguild.sql | 16 + .../2133_required_faction_loot_despawn.sql | 27 + utils/sql/svn/2136_extended_targets.sql | 2 + utils/sql/svn/2142_emotes.sql | 15 + ...ional_rule_spell_procs_resists_falloff.sql | 2 + .../svn/2156_optional_charm_break_rule.sql | 2 + .../svn/2159_optional_defensiveproc_rules.sql | 2 + utils/sql/svn/2164_require_bots_bottimers.sql | 7 + ...171_optional_SpecialAttackACBonus_rule.sql | 1 + .../svn/2176_optional_FrenzyBonus_rule.sql | 1 + .../2176_optional_aa_expansion_SOF_fix.sql | 12 + utils/sql/svn/2176_required_aa_updates.sql | 282 + utils/sql/svn/2178_required_aa_updates.sql | 148 + utils/sql/svn/2183_optional_bot_xp_rule.sql | 1 + .../svn/2185_optional_NPCFlurryChacne_rule | 1 + .../2185_optional_NPCFlurryChacne_rule.sql | 290 + .../2185_optional_NPCFlurryChance_rule.sql | 1 + utils/sql/svn/2185_required_aa_updates | 290 + utils/sql/svn/2185_required_aa_updates.sql | 290 + .../svn/2188_optional_miscspelleffect_rules | 4 + .../2188_optional_miscspelleffect_rules.sql | 4 + utils/sql/svn/2188_required_aa_updates | 93 + utils/sql/svn/2188_required_aa_updates.sql | 93 + utils/sql/svn/2189_optional_taunt_rules | 3 + utils/sql/svn/2189_optional_taunt_rules.sql | 3 + .../svn/2195_required_sharedplatupdates.sql | 2 + .../2208_optional_EnableSoulAbrasionAA.sql | 75 + .../svn/2208_optional_aa_stacking_rule.sql | 1 + utils/sql/svn/2208_required_aa_updates.sql | 1891 ++ .../svn/2209_optional_additive_bonus_rule.sql | 1 + utils/sql/svn/2213_loot_changes.sql | 6 + utils/sql/svn/2214_faction_list_mod.sql | 8 + utils/sql/svn/2215_required_aa_updates.sql | 19 + utils/sql/svn/222_buyer.sql | 13 + .../svn/2243_optional_char_max_level_rule.sql | 1 + utils/sql/svn/2260_probability.sql | 3 + .../2262_required_pet_discipline_update.sql | 6 + utils/sql/svn/2264_required_aa_updates.sql | 40 + utils/sql/svn/2268_required_updates.sql | 25 + utils/sql/svn/226_account_limiting.sql | 2 + ...274_optional_rule_iplimitdisconnectall.sql | 1 + .../2278_optional_rule_targetableswarmpet.sql | 1 + ...ptional_rule_targetableswarmpet-rename.sql | 2 + utils/sql/svn/2283_required_npc_changes.sql | 3 + .../2299_required_inspectmessage_fields.sql | 2 + utils/sql/svn/2300_optional_loot_changes.sql | 3 + utils/sql/svn/230_spells_table.sql | 219 + .../sql/svn/2340_required_maxbuffslotspet.sql | 5 + utils/sql/svn/235_horses_table.sql | 98 + .../sql/svn/2361_required_qs_rule_values.sql | 5 + utils/sql/svn/2370_required_aa_updates.sql | 21 + utils/sql/svn/2376_required_aa_updates.sql | 53 + utils/sql/svn/2380_optional_merc_data.sql | 4958 +++++ ...optional_merc_merchant_npctypes_update.sql | 128 + utils/sql/svn/2380_optional_merc_rules.sql | 8 + utils/sql/svn/2383_required_group_ismerc.sql | 1 + .../svn/2428_optional_levelbasedexpmods.sql | 117 + utils/sql/svn/243_spawn_timers.sql | 9 + .../2448_optional_stun_proc_aggro_rule.sql | 5 + utils/sql/svn/2471_required_aa_updates.sql | 34 + utils/sql/svn/247_mail.sql | 14 + utils/sql/svn/2482_required_start_zones.sql | 1 + utils/sql/svn/249_chatchannels.sql | 44 + utils/sql/svn/2504_required_aa_updates.sql | 32 + utils/sql/svn/250_bot_spell_update.sql | 23 + .../sql/svn/250_optional_bot_spell_update.sql | 12 + .../sql/svn/285_optional_bot_spell_update.sql | 7 + utils/sql/svn/292_augslots.sql | 12 + utils/sql/svn/294_merchant_logging.sql | 3 + utils/sql/svn/2_optional_maxclients.sql | 2 + utils/sql/svn/304_faction_list.sql | 2 + utils/sql/svn/326_aas.sql | 5 + utils/sql/svn/328_bot_management.sql | 10 + utils/sql/svn/328_optional_bot_management.sql | 23 + utils/sql/svn/340_gm_ips.sql | 8 + utils/sql/svn/356_combat.sql | 18 + utils/sql/svn/35_task_stepped.sql | 1 + utils/sql/svn/360_peqzone.sql | 11 + utils/sql/svn/364_ranged_dist_rule.sql | 1 + utils/sql/svn/386_bot_save_raid.sql | 8 + utils/sql/svn/42_task_min_maxlevel.sql | 2 + .../sql/svn/434_optional_rest_state_rules.sql | 3 + utils/sql/svn/447_sof_startzone_rule.sql | 1 + utils/sql/svn/463_altadv_vars.sql | 116 + utils/sql/svn/475_aa_actions.sql | 4 + utils/sql/svn/500_spawn2_optimization.sql | 1 + utils/sql/svn/503_bugs.sql | 17 + .../sql/svn/518_drakkin_npc_type_features.sql | 3 + utils/sql/svn/524_rule_values_notes.sql | 208 + utils/sql/svn/527_npc_armor_tint.sql | 3 + utils/sql/svn/553_saylink_table.sql | 6 + utils/sql/svn/55_zone_shutdowndeleay.sql | 2 + utils/sql/svn/564_nokeyring.sql | 1 + utils/sql/svn/600_group_leadership.sql | 6 + utils/sql/svn/612_instance_changes.sql | 120 + utils/sql/svn/615_adventure_assassination.sql | 4 + .../svn/619_Adventure_Recruiter_Flavor.sql | 7 + utils/sql/svn/621_LDoNTraps.sql | 18 + utils/sql/svn/633_ucs.sql | 6 + .../sql/svn/634_TrapTemplateDefaultValue.sql | 1 + utils/sql/svn/643_BotsTable.sql | 46 + utils/sql/svn/646_archery_penalty_rule.sql | 1 + utils/sql/svn/665_heroic_resists.sql | 7 + utils/sql/svn/667_titles.sql | 114 + utils/sql/svn/687_aa_table_changes.sql | 8 + .../svn/68_optional_character_maxexplevel.sql | 1 + utils/sql/svn/699_peqzone_rule.sql | 1 + .../sql/svn/702_aashieldblock_tint_table.sql | 38 + utils/sql/svn/703_peqzone_rule.sql | 4 + utils/sql/svn/704_rules.sql | 2 + utils/sql/svn/710_tint_set_naming.sql | 1 + utils/sql/svn/721_pathing_rules.sql | 20 + utils/sql/svn/730_smart_delay_moving.sql | 1 + .../sql/svn/731_rule_assist_notarget_self.sql | 1 + utils/sql/svn/732_sacrifice_rules.sql | 3 + utils/sql/svn/745_slow_mitigation.sql | 1 + .../sql/svn/754_archery_base_damage_rule.sql | 1 + utils/sql/svn/755_sof_altadv_vars_updates.sql | 63 + utils/sql/svn/773_monk_rules.sql | 6 + utils/sql/svn/853_optional_rule_aaexp.sql | 1 + .../858_optional_rule_ip_limit_by_status.sql | 1 + utils/sql/svn/892_optional_bots_table_mod.sql | 29 + utils/sql/svn/893_optional_bots_table_mod.sql | 3 + utils/sql/svn/898_npc_maxlevel_scalerate.sql | 2 + utils/sql/svn/902_optional_rule_snareflee.sql | 1 + utils/sql/svn/923_spawn2_enabled.sql | 1 + utils/sql/svn/962_hot_zone.sql | 1 + utils/sql/svn/964_reports.sql | 8 + utils/sql/svn/971_veteran_rewards.sql | 17 + .../sql/svn/977_raid_npc_private_corpses.sql | 1 + utils/sql/svn/979_unique_spawn_by_name.sql | 1 + utils/sql/svn/980_account_ip.sql | 7 + utils/sql/svn/bots.sql | 280 + utils/sql/svn/botsconvert.sql | 16 + utils/sql/svn/mercs.sql | 3929 ++++ world/Adventure.cpp | 438 + world/Adventure.h | 103 + world/AdventureManager.cpp | 2278 ++ world/AdventureManager.h | 95 + world/AdventureTemplate.h | 47 + world/CMakeLists.txt | 91 + world/EQLConfig.cpp | 384 + world/EQLConfig.h | 82 + world/EQW.cpp | 474 + world/EQW.h | 93 + world/EQWHTTPHandler.cpp | 340 + world/EQWHTTPHandler.h | 101 + world/EQWParser.cpp | 352 + world/EQWParser.h | 79 + world/HTTPRequest.cpp | 93 + world/HTTPRequest.h | 64 + world/LauncherLink.cpp | 368 + world/LauncherLink.h | 81 + world/LauncherList.cpp | 222 + world/LauncherList.h | 67 + world/LoginServer.cpp | 339 + world/LoginServer.h | 62 + world/LoginServerList.cpp | 197 + world/LoginServerList.h | 48 + world/SoFCharCreateData.h | 31 + world/WorldConfig.cpp | 54 + world/WorldConfig.h | 74 + world/WorldTCPConnection.h | 35 + world/client.cpp | 2021 ++ world/client.h | 108 + world/cliententry.cpp | 326 + world/cliententry.h | 126 + world/clientlist.cpp | 1362 ++ world/clientlist.h | 85 + world/console.cpp | 843 + world/console.h | 108 + world/lfplist.cpp | 279 + world/lfplist.h | 66 + world/net.cpp | 568 + world/net.h | 88 + world/perl_EQLConfig.cpp | 492 + world/perl_EQW.cpp | 1023 + world/perl_HTTPRequest.cpp | 345 + world/queryserv.cpp | 133 + world/queryserv.h | 23 + world/ucs.cpp | 129 + world/ucs.h | 23 + world/wguild_mgr.cpp | 172 + world/wguild_mgr.h | 31 + world/world_logsys.cpp | 58 + world/worlddb.cpp | 650 + world/worlddb.h | 50 + world/zonelist.cpp | 730 + world/zonelist.h | 82 + world/zoneserver.cpp | 1431 ++ world/zoneserver.h | 93 + zone/AA.cpp | 1968 ++ zone/AA.h | 2169 ++ zone/CMakeLists.txt | 185 + zone/Map.cpp | 1029 + zone/MobAI.cpp | 2419 +++ zone/NpcAI.cpp | 0 zone/NpcAI.h | 41 + zone/Object.cpp | 950 + zone/PlayerCorpse.cpp | 2085 ++ zone/PlayerCorpse.h | 142 + zone/QGlobals.cpp | 138 + zone/QGlobals.h | 42 + zone/Quest.cpp | 101 + zone/Quest.h | 46 + zone/QuestInterface.h | 33 + zone/QuestParserCollection.cpp | 576 + zone/QuestParserCollection.h | 62 + zone/StringIDs.h | 334 + zone/ZoneConfig.cpp | 22 + zone/ZoneConfig.h | 61 + zone/aggro.cpp | 1480 ++ zone/attack.cpp | 4334 ++++ zone/basic_functions.h | 178 + zone/beacon.cpp | 135 + zone/beacon.h | 60 + zone/blah.h | 9 + zone/bonuses.cpp | 3475 +++ zone/bot.cpp | 17491 ++++++++++++++++ zone/bot.h | 691 + zone/botStructs.h | 50 + zone/botspellsai.cpp | 3266 +++ zone/client.cpp | 7279 +++++++ zone/client.h | 1439 ++ zone/client_logs.cpp | 141 + zone/client_logs.h | 64 + zone/client_mods.cpp | 2050 ++ zone/client_packet.cpp | 13842 ++++++++++++ zone/client_packet.h | 290 + zone/client_process.cpp | 2368 +++ zone/command.cpp | 11659 ++++++++++ zone/command.h | 351 + zone/common.h | 486 + zone/doors.cpp | 823 + zone/doors.h | 113 + zone/effects.cpp | 841 + zone/embparser.cpp | 1702 ++ zone/embparser.h | 156 + zone/embperl.cpp | 372 + zone/embperl.h | 160 + zone/embxs.cpp | 136 + zone/embxs.h | 20 + zone/entity.cpp | 5292 +++++ zone/entity.h | 467 + zone/errmsg.h | 59 + zone/event_codes.h | 68 + zone/exp.cpp | 668 + zone/faction.cpp | 1064 + zone/faction.h | 77 + zone/fearpath.cpp | 645 + zone/features.h | 308 + zone/forage.cpp | 487 + zone/forage.h | 26 + zone/groups.cpp | 2134 ++ zone/groups.h | 156 + zone/guild.cpp | 434 + zone/guild_mgr.cpp | 1613 ++ zone/guild_mgr.h | 144 + zone/hate_list.cpp | 577 + zone/hate_list.h | 83 + zone/horse.cpp | 208 + zone/horse.h | 48 + zone/inventory.cpp | 2398 +++ zone/loottable.h | 81 + zone/loottables.cpp | 606 + zone/map.h | 190 + zone/masterentity.h | 19 + zone/maxskill.h | 2338 +++ zone/merc.cpp | 4507 ++++ zone/merc.h | 352 + zone/message.h | 7 + zone/mob.cpp | 4479 ++++ zone/mob.h | 1120 + zone/net.cpp | 1385 ++ zone/net.h | 61 + zone/npc.cpp | 2350 +++ zone/npc.h | 562 + zone/object.h | 231 + zone/oldcode.cpp | 1859 ++ zone/oldcode.h | 25 + zone/parser.cpp | 1698 ++ zone/parser.h | 124 + zone/pathing.cpp | 2297 ++ zone/pathing.h | 112 + zone/perl_PlayerCorpse.cpp | 839 + zone/perl_client.cpp | 5879 ++++++ zone/perl_doors.cpp | 768 + zone/perl_entity.cpp | 2197 ++ zone/perl_groups.cpp | 657 + zone/perl_hateentry.cpp | 137 + zone/perl_mob.cpp | 8351 ++++++++ zone/perl_npc.cpp | 2177 ++ zone/perl_object.cpp | 966 + zone/perl_perlpacket.cpp | 594 + zone/perl_questitem.cpp | 277 + zone/perl_raids.cpp | 618 + zone/perlpacket.cpp | 201 + zone/perlpacket.h | 68 + zone/perlparser.cpp | 3718 ++++ zone/perlparser.h | 46 + zone/petitions.cpp | 317 + zone/petitions.h | 118 + zone/pets.cpp | 722 + zone/pets.h | 64 + zone/questmgr.cpp | 2737 +++ zone/questmgr.h | 295 + zone/queues.h | 61 + zone/raid.h | 161 + zone/raids.cpp | 1465 ++ zone/raids.h | 217 + zone/skills.h | 101 + zone/spawn2.cpp | 1201 ++ zone/spawn2.h | 167 + zone/spawngroup.cpp | 248 + zone/spawngroup.h | 73 + zone/spdat.cpp | 1094 + zone/spdat.h | 830 + zone/special_attacks.cpp | 2188 ++ zone/spell_effects.cpp | 5394 +++++ zone/spells.cpp | 5408 +++++ zone/tasks.cpp | 3558 ++++ zone/tasks.h | 262 + zone/titles.cpp | 437 + zone/titles.h | 72 + zone/tradeskills.cpp | 1523 ++ zone/trading.cpp | 2740 +++ zone/trap.cpp | 339 + zone/trap.h | 79 + zone/tribute.cpp | 710 + zone/updatemgr.cpp | 194 + zone/updatemgr.h | 90 + zone/watermap.cpp | 195 + zone/watermap.h | 68 + zone/waypoints.cpp | 1408 ++ zone/worldserver.cpp | 2198 ++ zone/worldserver.h | 72 + zone/zone.cpp | 2651 +++ zone/zone.h | 310 + zone/zone_logsys.cpp | 78 + zone/zone_profile.cpp | 303 + zone/zone_profile.h | 165 + zone/zonedb.cpp | 2614 +++ zone/zonedb.h | 478 + zone/zonedbasync.cpp | 128 + zone/zonedbasync.h | 22 + zone/zonedump.h | 287 + zone/zoning.cpp | 857 + 1174 files changed, 445622 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 EMuShareMem/CMakeLists.txt create mode 100644 EMuShareMem/DLLMain.cpp create mode 100644 EMuShareMem/Doors.cpp create mode 100644 EMuShareMem/Doors.h create mode 100644 EMuShareMem/Items.cpp create mode 100644 EMuShareMem/Items.h create mode 100644 EMuShareMem/Loot.cpp create mode 100644 EMuShareMem/Loot.h create mode 100644 EMuShareMem/MMF.cpp create mode 100644 EMuShareMem/MMF.h create mode 100644 EMuShareMem/MMFMutex.cpp create mode 100644 EMuShareMem/MMFMutex.h create mode 100644 EMuShareMem/NPCFactionLists.cpp create mode 100644 EMuShareMem/NPCFactionLists.h create mode 100644 EMuShareMem/NPCTypes.cpp create mode 100644 EMuShareMem/NPCTypes.h create mode 100644 EMuShareMem/Opcodes.cpp create mode 100644 EMuShareMem/Opcodes.h create mode 100644 EMuShareMem/SkillCaps.cpp create mode 100644 EMuShareMem/SkillCaps.h create mode 100644 EMuShareMem/Spells.cpp create mode 100644 EMuShareMem/Spells.h create mode 100644 GPL.txt create mode 100644 changelog.txt create mode 100644 cmake/FindMySQL.cmake create mode 100644 common/BasePacket.cpp create mode 100644 common/BasePacket.h create mode 100644 common/CMakeLists.txt create mode 100644 common/CRC16.cpp create mode 100644 common/CRC16.h create mode 100644 common/Condition.cpp create mode 100644 common/Condition.h create mode 100644 common/DBMemLeak.cpp create mode 100644 common/DBMemLeak.h create mode 100644 common/EMuShareMem.cpp create mode 100644 common/EMuShareMem.h create mode 100644 common/EQDB.cpp create mode 100644 common/EQDB.h create mode 100644 common/EQDBRes.cpp create mode 100644 common/EQDBRes.h create mode 100644 common/EQEMuError.cpp create mode 100644 common/EQEMuError.h create mode 100644 common/EQEmuConfig.cpp create mode 100644 common/EQEmuConfig.h create mode 100644 common/EQEmuConfig_elements.h create mode 100644 common/EQNetwork.cpp create mode 100644 common/EQNetwork.h create mode 100644 common/EQPacket.cpp create mode 100644 common/EQPacket.h create mode 100644 common/EQStream.cpp create mode 100644 common/EQStream.h create mode 100644 common/EQStreamFactory.cpp create mode 100644 common/EQStreamFactory.h create mode 100644 common/EQStreamIdent.cpp create mode 100644 common/EQStreamIdent.h create mode 100644 common/EQStreamIntf.h create mode 100644 common/EQStreamLocator.h create mode 100644 common/EQStreamProxy.cpp create mode 100644 common/EQStreamProxy.h create mode 100644 common/EQStreamType.h create mode 100644 common/EmuTCPConnection.cpp create mode 100644 common/EmuTCPConnection.h create mode 100644 common/EmuTCPServer.cpp create mode 100644 common/EmuTCPServer.h create mode 100644 common/Item.cpp create mode 100644 common/Item.h create mode 100644 common/MaxSkill.cpp create mode 100644 common/MiscFunctions.cpp create mode 100644 common/MiscFunctions.h create mode 100644 common/Mutex.cpp create mode 100644 common/Mutex.h create mode 100644 common/ProcLauncher.cpp create mode 100644 common/ProcLauncher.h create mode 100644 common/SharedLibrary.cpp create mode 100644 common/SharedLibrary.h create mode 100644 common/SocketLib/Base64.cpp create mode 100644 common/SocketLib/Base64.h create mode 100644 common/SocketLib/File.cpp create mode 100644 common/SocketLib/File.h create mode 100644 common/SocketLib/HTTPSocket.cpp create mode 100644 common/SocketLib/HTTPSocket.h create mode 100644 common/SocketLib/HttpdCookies.cpp create mode 100644 common/SocketLib/HttpdCookies.h create mode 100644 common/SocketLib/HttpdForm.cpp create mode 100644 common/SocketLib/HttpdForm.h create mode 100644 common/SocketLib/HttpdSocket.cpp create mode 100644 common/SocketLib/HttpdSocket.h create mode 100644 common/SocketLib/IFile.h create mode 100644 common/SocketLib/MemFile.cpp create mode 100644 common/SocketLib/MemFile.h create mode 100644 common/SocketLib/Mime.cpp create mode 100644 common/SocketLib/Mime.h create mode 100644 common/SocketLib/Parse.cpp create mode 100644 common/SocketLib/Parse.h create mode 100644 common/SocketLib/README.eqemu create mode 100644 common/SocketLib/README.macosx create mode 100644 common/SocketLib/Utility.cpp create mode 100644 common/SocketLib/Utility.h create mode 100644 common/SocketLib/gpl.txt create mode 100644 common/SocketLib/socket_include.cpp create mode 100644 common/SocketLib/socket_include.h create mode 100644 common/StackWalker/StackWalker.cpp create mode 100644 common/StackWalker/StackWalker.h create mode 100644 common/StructStrategy.cpp create mode 100644 common/StructStrategy.h create mode 100644 common/TCPBasicServer.h create mode 100644 common/TCPConnection.cpp create mode 100644 common/TCPConnection.h create mode 100644 common/TCPServer.cpp create mode 100644 common/TCPServer.h create mode 100644 common/XMLParser.cpp create mode 100644 common/XMLParser.h create mode 100644 common/ZoneNumbers.h create mode 100644 common/bodytypes.h create mode 100644 common/breakdowns.h create mode 100644 common/classes.cpp create mode 100644 common/classes.h create mode 100644 common/common_profile.h create mode 100644 common/crash.cpp create mode 100644 common/crash.h create mode 100644 common/crc32.cpp create mode 100644 common/crc32.h create mode 100644 common/database.cpp create mode 100644 common/database.h create mode 100644 common/dbasync.cpp create mode 100644 common/dbasync.h create mode 100644 common/dbcore.cpp create mode 100644 common/dbcore.h create mode 100644 common/dbmemshare.cpp create mode 100644 common/debug.cpp create mode 100644 common/debug.h create mode 100644 common/deity.h create mode 100644 common/emu_opcodes.cpp create mode 100644 common/emu_opcodes.h create mode 100644 common/emu_oplist.h create mode 100644 common/eq_constants.h create mode 100644 common/eq_opcodes.h create mode 100644 common/eq_packet_structs.h create mode 100644 common/eqtime.cpp create mode 100644 common/eqtime.h create mode 100644 common/errmsg.h create mode 100644 common/extprofile.cpp create mode 100644 common/extprofile.h create mode 100644 common/guild_base.cpp create mode 100644 common/guild_base.h create mode 100644 common/guilds.cpp create mode 100644 common/guilds.h create mode 100644 common/item_fieldlist.h create mode 100644 common/item_struct.h create mode 100644 common/languages.h create mode 100644 common/linked_list.h create mode 100644 common/logsys.cpp create mode 100644 common/logsys.h create mode 100644 common/logsys_eqemu.cpp create mode 100644 common/logtypes.h create mode 100644 common/mail_oplist.h create mode 100644 common/md5.cpp create mode 100644 common/md5.h create mode 100644 common/misc.cpp create mode 100644 common/misc.h create mode 100644 common/moremath.cpp create mode 100644 common/moremath.h create mode 100644 common/op_codes.h create mode 100644 common/opcode_dispatch.h create mode 100644 common/opcode_map.cpp create mode 100644 common/opcodemgr.cpp create mode 100644 common/opcodemgr.h create mode 100644 common/packet_dump.cpp create mode 100644 common/packet_dump.h create mode 100644 common/packet_dump_file.cpp create mode 100644 common/packet_dump_file.h create mode 100644 common/packet_functions.cpp create mode 100644 common/packet_functions.h create mode 100644 common/packetfile.cpp create mode 100644 common/packetfile.h create mode 100644 common/patches/Client62.cpp create mode 100644 common/patches/Client62.h create mode 100644 common/patches/Client62_itemfields.h create mode 100644 common/patches/Client62_ops.h create mode 100644 common/patches/Client62_structs.h create mode 100644 common/patches/RoF.cpp create mode 100644 common/patches/RoF.h create mode 100644 common/patches/RoF_itemfields.h create mode 100644 common/patches/RoF_ops.h create mode 100644 common/patches/RoF_structs.h create mode 100644 common/patches/SSDeclare.h create mode 100644 common/patches/SSDefine.h create mode 100644 common/patches/SSRegister.h create mode 100644 common/patches/SoD.cpp create mode 100644 common/patches/SoD.h create mode 100644 common/patches/SoD_itemfields.h create mode 100644 common/patches/SoD_ops.h create mode 100644 common/patches/SoD_structs.h create mode 100644 common/patches/SoF.cpp create mode 100644 common/patches/SoF.h create mode 100644 common/patches/SoF_itemfields.h create mode 100644 common/patches/SoF_opcode_list.h create mode 100644 common/patches/SoF_ops.h create mode 100644 common/patches/SoF_structs.h create mode 100644 common/patches/Titanium.cpp create mode 100644 common/patches/Titanium.h create mode 100644 common/patches/Titanium_itemfields.h create mode 100644 common/patches/Titanium_ops.h create mode 100644 common/patches/Titanium_structs.h create mode 100644 common/patches/Underfoot.cpp create mode 100644 common/patches/Underfoot.h create mode 100644 common/patches/Underfoot_itemfields.h create mode 100644 common/patches/Underfoot_ops.h create mode 100644 common/patches/Underfoot_structs.h create mode 100644 common/patches/makepatch create mode 100644 common/patches/patches.cpp create mode 100644 common/patches/patches.h create mode 100644 common/patches/template.cpp create mode 100644 common/patches/template.h create mode 100644 common/patches/template_ops.h create mode 100644 common/patches/template_structs.h create mode 100644 common/perl_EQDB.cpp create mode 100644 common/perl_EQDBRes.cpp create mode 100644 common/platform.cpp create mode 100644 common/platform.h create mode 100644 common/profiler.h create mode 100644 common/ptimer.cpp create mode 100644 common/ptimer.h create mode 100644 common/queue.h create mode 100644 common/queues.h create mode 100644 common/races.cpp create mode 100644 common/races.h create mode 100644 common/rdtsc.cpp create mode 100644 common/rdtsc.h create mode 100644 common/rulesys.cpp create mode 100644 common/rulesys.h create mode 100644 common/ruletypes.h create mode 100644 common/seperator-2.h create mode 100644 common/seperator.h create mode 100644 common/serverinfo.cpp create mode 100644 common/serverinfo.h create mode 100644 common/servertalk.h create mode 100644 common/shareddb.cpp create mode 100644 common/shareddb.h create mode 100644 common/skills.h create mode 100644 common/timeoutmgr.cpp create mode 100644 common/timeoutmgr.h create mode 100644 common/timer.cpp create mode 100644 common/timer.h create mode 100644 common/tinyxml/tinystr.cpp create mode 100644 common/tinyxml/tinystr.h create mode 100644 common/tinyxml/tinyxml.cpp create mode 100644 common/tinyxml/tinyxml.h create mode 100644 common/tinyxml/tinyxmlerror.cpp create mode 100644 common/tinyxml/tinyxmlparser.cpp create mode 100644 common/types.h create mode 100644 common/unix.cpp create mode 100644 common/unix.h create mode 100644 common/useperl.h create mode 100644 common/version.h create mode 100644 common/win_getopt.cpp create mode 100644 common/win_getopt.h create mode 100644 common/worldconn.cpp create mode 100644 common/worldconn.h create mode 100644 dependencies/.gitignore create mode 100644 eqlaunch/CMakeLists.txt create mode 100644 eqlaunch/ZoneLaunch.cpp create mode 100644 eqlaunch/ZoneLaunch.h create mode 100644 eqlaunch/eqlaunch.cpp create mode 100644 eqlaunch/worldserver.cpp create mode 100644 eqlaunch/worldserver.h create mode 100644 loginserver/CMakeLists.txt create mode 100644 loginserver/Client.cpp create mode 100644 loginserver/Client.h create mode 100644 loginserver/ClientManager.cpp create mode 100644 loginserver/ClientManager.h create mode 100644 loginserver/Config.cpp create mode 100644 loginserver/Config.h create mode 100644 loginserver/Database.h create mode 100644 loginserver/DatabaseMySQL.cpp create mode 100644 loginserver/DatabaseMySQL.h create mode 100644 loginserver/DatabasePostgreSQL.cpp create mode 100644 loginserver/DatabasePostgreSQL.h create mode 100644 loginserver/EQCryptoAPI.h create mode 100644 loginserver/Encryption.cpp create mode 100644 loginserver/Encryption.h create mode 100644 loginserver/ErrorLog.cpp create mode 100644 loginserver/ErrorLog.h create mode 100644 loginserver/LoginServer.h create mode 100644 loginserver/LoginStructures.h create mode 100644 loginserver/Main.cpp create mode 100644 loginserver/Options.h create mode 100644 loginserver/ServerManager.cpp create mode 100644 loginserver/ServerManager.h create mode 100644 loginserver/WorldServer.cpp create mode 100644 loginserver/WorldServer.h create mode 100644 loginserver/login_util/EQEmuLoginServerDBInstall.sql create mode 100644 loginserver/login_util/EQEmuLoginServerPostgreDBInstall.sql create mode 100644 loginserver/login_util/login.ini create mode 100644 loginserver/login_util/login_opcodes.conf create mode 100644 loginserver/login_util/login_opcodes_sod.conf create mode 100644 loginserver/login_util/tblLoginServerAccounts.sql create mode 100644 loginserver/login_util/tblServerAdminRegistration.sql create mode 100644 loginserver/login_util/tblServerListType.sql create mode 100644 loginserver/login_util/tblWorldServerRegistration.sql create mode 100644 queryserv/CMakeLists.txt create mode 100644 queryserv/database.cpp create mode 100644 queryserv/database.h create mode 100644 queryserv/lfguild.cpp create mode 100644 queryserv/lfguild.h create mode 100644 queryserv/queryserv.cpp create mode 100644 queryserv/queryservconfig.cpp create mode 100644 queryserv/queryservconfig.h create mode 100644 queryserv/worldserver.cpp create mode 100644 queryserv/worldserver.h create mode 100644 ucs/CMakeLists.txt create mode 100644 ucs/chatchannel.cpp create mode 100644 ucs/chatchannel.h create mode 100644 ucs/clientlist.cpp create mode 100644 ucs/clientlist.h create mode 100644 ucs/database.cpp create mode 100644 ucs/database.h create mode 100644 ucs/ucs.cpp create mode 100644 ucs/ucsconfig.cpp create mode 100644 ucs/ucsconfig.h create mode 100644 ucs/worldserver.cpp create mode 100644 ucs/worldserver.h create mode 100644 utils/CMakeLists.txt create mode 100644 utils/EQExtractor2/EQExtractor2.sln create mode 100644 utils/EQExtractor2/EQExtractor2/ChangeLog.txt create mode 100644 utils/EQExtractor2/EQExtractor2/EQApplicationLayer.cs create mode 100644 utils/EQExtractor2/EQExtractor2/EQExtractor2.csproj create mode 100644 utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.Designer.cs create mode 100644 utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs create mode 100644 utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.resx create mode 100644 utils/EQExtractor2/EQExtractor2/EQPacket.cs create mode 100644 utils/EQExtractor2/EQExtractor2/GenerateSQLForm.Designer.cs create mode 100644 utils/EQExtractor2/EQExtractor2/GenerateSQLForm.cs create mode 100644 utils/EQExtractor2/EQExtractor2/GenerateSQLForm.resx create mode 100644 utils/EQExtractor2/EQExtractor2/InternalTypes.cs create mode 100644 utils/EQExtractor2/EQExtractor2/LogForm.Designer.cs create mode 100644 utils/EQExtractor2/EQExtractor2/LogForm.cs create mode 100644 utils/EQExtractor2/EQExtractor2/LogForm.resx create mode 100644 utils/EQExtractor2/EQExtractor2/OpcodeManager.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PacketDotNet.xml create mode 100644 utils/EQExtractor2/EQExtractor2/PatchAug04-2011.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchAugust15-2012.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchDec7-2010.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchDecember10-2012.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchFeb8-2011.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchFebruary11-2013.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchGeneric.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchJanuary16-2013.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchJuly13-2010.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchJuly13-2012.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchJune25-2012.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchMar15-2012.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchMarch15-2011.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchMay12-2010.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchMay12-2011.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchMay24-2011.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchNov17-2011.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchOct20-2010.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchSoD.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchTestSep1-2010.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchTestSep22-2010.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchTestServerFebruary5-2013.cs create mode 100644 utils/EQExtractor2/EQExtractor2/PatchTestServerJanuary16-2013.cs create mode 100644 utils/EQExtractor2/EQExtractor2/Program.cs create mode 100644 utils/EQExtractor2/EQExtractor2/Properties/AssemblyInfo.cs create mode 100644 utils/EQExtractor2/EQExtractor2/Properties/Resources.Designer.cs create mode 100644 utils/EQExtractor2/EQExtractor2/Properties/Resources.resx create mode 100644 utils/EQExtractor2/EQExtractor2/Properties/Settings.Designer.cs create mode 100644 utils/EQExtractor2/EQExtractor2/Properties/Settings.settings create mode 100644 utils/EQExtractor2/EQExtractor2/SharpPcap.xml create mode 100644 utils/EQExtractor2/EQExtractor2/UserOptions.Designer.cs create mode 100644 utils/EQExtractor2/EQExtractor2/UserOptions.cs create mode 100644 utils/EQExtractor2/EQExtractor2/UserOptions.resx create mode 100644 utils/EQExtractor2/EQExtractor2/Utils.cs create mode 100644 utils/EQExtractor2/EQExtractor2/app.config create mode 100644 utils/EQExtractor2/EQExtractor2/patch_Aug04-2011.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_August15-2012.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_Dec10-2012.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_Dec7-2010.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_Feb11-2013.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_Feb8-2011.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_Jan16-2013.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_July13-2012.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_June25-2012.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_Mar15-2012.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_March15-2011.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_May12-2010.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_May12-2011.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_Nov17-2011.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_Sep01-2010.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_Sep22-2010.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_SoD.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_TestServer-Feb5-2013.conf create mode 100644 utils/EQExtractor2/EQExtractor2/patch_TestServer-Jan16-2013.conf create mode 100644 utils/TaskMaster/EQEmuWxProject.sln create mode 100644 utils/TaskMaster/EQEmuWxProject.vcproj create mode 100644 utils/TaskMaster/ErrorLog.cpp create mode 100644 utils/TaskMaster/ErrorLog.h create mode 100644 utils/TaskMaster/activities.cpp create mode 100644 utils/TaskMaster/base.cpp create mode 100644 utils/TaskMaster/base.h create mode 100644 utils/TaskMaster/base_utility.cpp create mode 100644 utils/TaskMaster/changelog.txt create mode 100644 utils/TaskMaster/goals.cpp create mode 100644 utils/TaskMaster/items.h create mode 100644 utils/TaskMaster/proximity.cpp create mode 100644 utils/TaskMaster/tasks.cpp create mode 100644 utils/TaskMaster/tasks.h create mode 100644 utils/TaskMaster/utility.cpp create mode 100644 utils/TaskMaster/utility.h create mode 100644 utils/azone2/3d.hpp create mode 100644 utils/azone2/3d_base.hpp create mode 100644 utils/azone2/CMakeLists.txt create mode 100644 utils/azone2/GLModelViewer.cpp create mode 100644 utils/azone2/GLModelViewer.h create mode 100644 utils/azone2/README create mode 100644 utils/azone2/archive.hpp create mode 100644 utils/azone2/awater.cpp create mode 100644 utils/azone2/awater.h create mode 100644 utils/azone2/azone.cpp create mode 100644 utils/azone2/azone.h create mode 100644 utils/azone2/azone.ini create mode 100644 utils/azone2/dat.cpp create mode 100644 utils/azone2/dat.hpp create mode 100644 utils/azone2/file.cpp create mode 100644 utils/azone2/file.hpp create mode 100644 utils/azone2/file_loader.hpp create mode 100644 utils/azone2/global.cpp create mode 100644 utils/azone2/global.hpp create mode 100644 utils/azone2/listobj.cpp create mode 100644 utils/azone2/octree.hpp create mode 100644 utils/azone2/pfs.cpp create mode 100644 utils/azone2/pfs.hpp create mode 100644 utils/azone2/s3d.h create mode 100644 utils/azone2/ter.cpp create mode 100644 utils/azone2/ter.hpp create mode 100644 utils/azone2/types.h create mode 100644 utils/azone2/wld.cpp create mode 100644 utils/azone2/wld.hpp create mode 100644 utils/azone2/wld_structs.hpp create mode 100644 utils/azone2/zon.cpp create mode 100644 utils/azone2/zon.hpp create mode 100644 utils/azone2/zonv4.cpp create mode 100644 utils/azone2/zonv4.hpp create mode 100644 utils/cleanipc.cpp create mode 100644 utils/defaults/Maps/.keep create mode 100644 utils/defaults/commands.pl create mode 100644 utils/defaults/eqemu_config.xml create mode 100644 utils/defaults/eqemu_config.xml.full create mode 100644 utils/defaults/log.ini create mode 100644 utils/defaults/logs/.keep create mode 100644 utils/defaults/mime.types create mode 100644 utils/defaults/plugin.pl create mode 100644 utils/defaults/plugins/.keep create mode 100644 utils/defaults/plugins/check_handin.pl create mode 100644 utils/defaults/quests/.keep create mode 100644 utils/defaults/templates/TMimages/branch.gif create mode 100644 utils/defaults/templates/TMimages/branchbottom.gif create mode 100644 utils/defaults/templates/TMimages/branchtop.gif create mode 100644 utils/defaults/templates/TMimages/folder.gif create mode 100644 utils/defaults/templates/TMimages/line.gif create mode 100644 utils/defaults/templates/TMimages/linebottom.gif create mode 100644 utils/defaults/templates/TMimages/minus.gif create mode 100644 utils/defaults/templates/TMimages/minusbottom.gif create mode 100644 utils/defaults/templates/TMimages/minustop.gif create mode 100644 utils/defaults/templates/TMimages/plus.gif create mode 100644 utils/defaults/templates/TMimages/plusbottom.gif create mode 100644 utils/defaults/templates/TMimages/plustop.gif create mode 100644 utils/defaults/templates/TreeMenu.css create mode 100644 utils/defaults/templates/TreeMenu.js create mode 100644 utils/defaults/templates/account.html create mode 100644 utils/defaults/templates/accounts.html create mode 100644 utils/defaults/templates/action.html create mode 100644 utils/defaults/templates/bootzone.html create mode 100644 utils/defaults/templates/bugs.html create mode 100644 utils/defaults/templates/chat.html create mode 100644 utils/defaults/templates/commands.html create mode 100644 utils/defaults/templates/config.html create mode 100644 utils/defaults/templates/configdl.html create mode 100644 utils/defaults/templates/database.html create mode 100644 utils/defaults/templates/delete.gif create mode 100644 utils/defaults/templates/favicon.ico create mode 100644 utils/defaults/templates/filters.html create mode 100644 utils/defaults/templates/guild.html create mode 100644 utils/defaults/templates/guildcreate.html create mode 100644 utils/defaults/templates/guilds.html create mode 100644 utils/defaults/templates/guildsearch.html create mode 100644 utils/defaults/templates/head_01_nb.jpg create mode 100644 utils/defaults/templates/head_02_nb.jpg create mode 100644 utils/defaults/templates/head_03_nb.jpg create mode 100644 utils/defaults/templates/head_04_nb.jpg create mode 100644 utils/defaults/templates/index.html create mode 100644 utils/defaults/templates/launcher.html create mode 100644 utils/defaults/templates/launchers.html create mode 100644 utils/defaults/templates/main.css create mode 100644 utils/defaults/templates/menu.html create mode 100644 utils/defaults/templates/minilogin.html create mode 100644 utils/defaults/templates/petitions.html create mode 100644 utils/defaults/templates/players.html create mode 100644 utils/defaults/templates/reset.png create mode 100644 utils/defaults/templates/rule.html create mode 100644 utils/defaults/templates/rules.html create mode 100644 utils/defaults/templates/spacer.gif create mode 100644 utils/defaults/templates/status.html create mode 100644 utils/defaults/templates/submit.png create mode 100644 utils/defaults/templates/top.html create mode 100644 utils/defaults/templates/update.png create mode 100644 utils/defaults/templates/variables.html create mode 100644 utils/defaults/templates/world.html create mode 100644 utils/defaults/templates/worldstatus.html create mode 100644 utils/defaults/templates/zone.html create mode 100644 utils/defaults/templates/zones.html create mode 100644 utils/defaults/updated_templates/access.txt create mode 100644 utils/defaults/updated_templates/actions/bug_action.html create mode 100644 utils/defaults/updated_templates/actions/console_action.html create mode 100644 utils/defaults/updated_templates/actions/launcher_action.html create mode 100644 utils/defaults/updated_templates/actions/world_action.html create mode 100644 utils/defaults/updated_templates/actions/zone_action.html create mode 100644 utils/defaults/updated_templates/blank.temp.html create mode 100644 utils/defaults/updated_templates/bugs.html create mode 100644 utils/defaults/updated_templates/bugview.html create mode 100644 utils/defaults/updated_templates/console.html create mode 100644 utils/defaults/updated_templates/data/bug_data.html create mode 100644 utils/defaults/updated_templates/data/console_data.html create mode 100644 utils/defaults/updated_templates/data/launcher_count.html create mode 100644 utils/defaults/updated_templates/data/launcher_data.html create mode 100644 utils/defaults/updated_templates/data/player_count.html create mode 100644 utils/defaults/updated_templates/data/player_data.html create mode 100644 utils/defaults/updated_templates/data/world_status.html create mode 100644 utils/defaults/updated_templates/data/zone_count.html create mode 100644 utils/defaults/updated_templates/data/zone_data.html create mode 100644 utils/defaults/updated_templates/images/loading.gif create mode 100644 utils/defaults/updated_templates/index.html create mode 100644 utils/defaults/updated_templates/launcher.html create mode 100644 utils/defaults/updated_templates/launchers.html create mode 100644 utils/defaults/updated_templates/petitions.html create mode 100644 utils/defaults/updated_templates/players.html create mode 100644 utils/defaults/updated_templates/playerview.html create mode 100644 utils/defaults/updated_templates/scripts/jquery.js create mode 100644 utils/defaults/updated_templates/scripts/menu.pl create mode 100644 utils/defaults/updated_templates/scripts/menu_noaccess.pl create mode 100644 utils/defaults/updated_templates/style/style.css create mode 100644 utils/defaults/updated_templates/zone.html create mode 100644 utils/defaults/updated_templates/zones.html create mode 100644 utils/defaults/updated_templates/zoneview.html create mode 100644 utils/defaults/worldui.pl create mode 100644 utils/deprecated/0.6.1-upgrade.sql create mode 100644 utils/deprecated/ZONECFG.SQL create mode 100644 utils/deprecated/apathing/actions.cpp create mode 100644 utils/deprecated/apathing/apathing.cpp create mode 100644 utils/deprecated/apathing/apathing.h create mode 100644 utils/deprecated/apathing/boostcrap.cpp create mode 100644 utils/deprecated/apathing/boostcrap.h create mode 100644 utils/deprecated/apathing/gpoint.cpp create mode 100644 utils/deprecated/apathing/gpoint.h create mode 100644 utils/deprecated/apathing/load_db.cpp create mode 100644 utils/deprecated/apathing/pathfinding.cpp create mode 100644 utils/deprecated/apathing/quadtree.cpp create mode 100644 utils/deprecated/apathing/quadtree.h create mode 100644 utils/deprecated/apathing/runall create mode 100644 utils/deprecated/apathing/shortnames create mode 100644 utils/deprecated/asmtools/locate_stringids.pl create mode 100644 utils/deprecated/asmtools/opcodes_to_ida.pl create mode 100644 utils/deprecated/asmtools/sig_to_ida.pl create mode 100644 utils/deprecated/asmtools/stringids_to_ida.pl create mode 100644 utils/deprecated/azone/3d.hpp create mode 100644 utils/deprecated/azone/3d_base.hpp create mode 100644 utils/deprecated/azone/Azone.vcproj create mode 100644 utils/deprecated/azone/README create mode 100644 utils/deprecated/azone/archive.hpp create mode 100644 utils/deprecated/azone/awater.cpp create mode 100644 utils/deprecated/azone/awater.h create mode 100644 utils/deprecated/azone/azone.cpp create mode 100644 utils/deprecated/azone/azone.h create mode 100644 utils/deprecated/azone/file.cpp create mode 100644 utils/deprecated/azone/file.hpp create mode 100644 utils/deprecated/azone/file_loader.hpp create mode 100644 utils/deprecated/azone/global.hpp create mode 100644 utils/deprecated/azone/octree.hpp create mode 100644 utils/deprecated/azone/pfs.cpp create mode 100644 utils/deprecated/azone/pfs.hpp create mode 100644 utils/deprecated/azone/s3d.c create mode 100644 utils/deprecated/azone/s3d.h create mode 100644 utils/deprecated/azone/ter.cpp create mode 100644 utils/deprecated/azone/ter.hpp create mode 100644 utils/deprecated/azone/types.h create mode 100644 utils/deprecated/azone/wld.c create mode 100644 utils/deprecated/azone/wld.h create mode 100644 utils/deprecated/azone/zon.cpp create mode 100644 utils/deprecated/azone/zon.hpp create mode 100644 utils/deprecated/charmove/Makefile.bsd create mode 100644 utils/deprecated/charmove/Makefile.common create mode 100644 utils/deprecated/charmove/char_format.h create mode 100644 utils/deprecated/charmove/charmove.dsp create mode 100644 utils/deprecated/charmove/export_char.cpp create mode 100644 utils/deprecated/charmove/import_char.cpp create mode 100644 utils/deprecated/convert_ops.pl create mode 100644 utils/deprecated/hexvis/HexRichEdit.cpp create mode 100644 utils/deprecated/hexvis/HexRichEdit.h create mode 100644 utils/deprecated/hexvis/StdAfx.cpp create mode 100644 utils/deprecated/hexvis/StdAfx.h create mode 100644 utils/deprecated/hexvis/hexvis.clw create mode 100644 utils/deprecated/hexvis/hexvis.cpp create mode 100644 utils/deprecated/hexvis/hexvis.dsp create mode 100644 utils/deprecated/hexvis/hexvis.dsw create mode 100644 utils/deprecated/hexvis/hexvis.h create mode 100644 utils/deprecated/hexvis/hexvis.rc create mode 100644 utils/deprecated/hexvis/hexvisDlg.cpp create mode 100644 utils/deprecated/hexvis/hexvisDlg.h create mode 100644 utils/deprecated/hexvis/res/hexvis.ico create mode 100644 utils/deprecated/hexvis/res/hexvis.rc2 create mode 100644 utils/deprecated/hexvis/resource.h create mode 100644 utils/deprecated/items-0.6.0-DR2-0.6.1-DR1-convert.sql create mode 100644 utils/deprecated/itemtablechanges.sql create mode 100644 utils/deprecated/perlxs/EQDB.h create mode 100644 utils/deprecated/perlxs/EQDB.h.xs create mode 100644 utils/deprecated/perlxs/EQDBRes.h create mode 100644 utils/deprecated/perlxs/EQDBRes.h.xs create mode 100644 utils/deprecated/perlxs/EQLConfig.h create mode 100644 utils/deprecated/perlxs/EQLConfig.h.xs create mode 100644 utils/deprecated/perlxs/EQW.h create mode 100644 utils/deprecated/perlxs/EQW.h.xs create mode 100644 utils/deprecated/perlxs/HTTPRequest.h create mode 100644 utils/deprecated/perlxs/HTTPRequest.h.xs create mode 100644 utils/deprecated/perlxs/PlayerCorpse.h create mode 100644 utils/deprecated/perlxs/PlayerCorpse.h.xs create mode 100644 utils/deprecated/perlxs/all_convert create mode 100644 utils/deprecated/perlxs/class.sh create mode 100644 utils/deprecated/perlxs/class.typemap create mode 100644 utils/deprecated/perlxs/client.h create mode 100644 utils/deprecated/perlxs/client.h.xs create mode 100644 utils/deprecated/perlxs/convert create mode 100644 utils/deprecated/perlxs/entity.h create mode 100644 utils/deprecated/perlxs/entity.h.xs create mode 100644 utils/deprecated/perlxs/export create mode 100644 utils/deprecated/perlxs/groups.h create mode 100644 utils/deprecated/perlxs/groups.h.xs create mode 100644 utils/deprecated/perlxs/mob.h create mode 100644 utils/deprecated/perlxs/mob.h.xs create mode 100644 utils/deprecated/perlxs/npc.h create mode 100644 utils/deprecated/perlxs/npc.h.xs create mode 100644 utils/deprecated/perlxs/perlpacket.h create mode 100644 utils/deprecated/perlxs/perlpacket.h.xs create mode 100644 utils/deprecated/perlxs/put create mode 100644 utils/deprecated/ppconvert/Makefile.bsd create mode 100644 utils/deprecated/ppconvert/Makefile.common create mode 100644 utils/deprecated/ppconvert/ppconvert.cpp create mode 100644 utils/deprecated/ppconvert/ppconvert.dsp create mode 100644 utils/deprecated/ppskillfix/Makefile.bsd create mode 100644 utils/deprecated/ppskillfix/Makefile.common create mode 100644 utils/deprecated/ppskillfix/ppskillfix.cpp create mode 100644 utils/deprecated/ppskillfix/ppskillfix.dsp create mode 100644 utils/deprecated/ppskillfix/ppskillfix.dsw create mode 100644 utils/deprecated/spell_explorer.cpp create mode 100644 utils/deprecated/wr_update.sql create mode 100644 utils/patches/mail_opcodes.conf create mode 100644 utils/patches/opcodes.conf create mode 100644 utils/patches/patch_6.2.conf create mode 100644 utils/patches/patch_RoF.conf create mode 100644 utils/patches/patch_SoD.conf create mode 100644 utils/patches/patch_SoF.conf create mode 100644 utils/patches/patch_Titanium.conf create mode 100644 utils/patches/patch_Underfoot.conf create mode 100644 utils/pfs_list/CMakeLists.txt create mode 100644 utils/pfs_list/Common/CMakeLists.txt create mode 100644 utils/pfs_list/Common/Include/Archive.h create mode 100644 utils/pfs_list/Common/Include/Compression.h create mode 100644 utils/pfs_list/Common/Include/PFSArchive.h create mode 100644 utils/pfs_list/Common/Include/PFSDataStructs.h create mode 100644 utils/pfs_list/Common/Source/Compression.cpp create mode 100644 utils/pfs_list/Common/Source/PFSArchive.cpp create mode 100644 utils/pfs_list/PFSList/CMakeLists.txt create mode 100644 utils/pfs_list/PFSList/Source/main.cpp create mode 100644 utils/player_profile_set/Readme.txt create mode 100644 utils/player_profile_set/bin/database.ini create mode 100644 utils/player_profile_set/player_profile_set.sln create mode 100644 utils/player_profile_set/player_profile_set/MiscFunctions.cpp create mode 100644 utils/player_profile_set/player_profile_set/MiscFunctions.h create mode 100644 utils/player_profile_set/player_profile_set/database.cpp create mode 100644 utils/player_profile_set/player_profile_set/database.h create mode 100644 utils/player_profile_set/player_profile_set/eq_player_structs.h create mode 100644 utils/player_profile_set/player_profile_set/eqemu_string.h create mode 100644 utils/player_profile_set/player_profile_set/ini.cpp create mode 100644 utils/player_profile_set/player_profile_set/ini.h create mode 100644 utils/player_profile_set/player_profile_set/main.cpp create mode 100644 utils/player_profile_set/player_profile_set/main.h create mode 100644 utils/player_profile_set/player_profile_set/player_profile_set.vcproj create mode 100644 utils/player_profile_set/player_profile_set/types.h create mode 100644 utils/scripts/Examples/ListIteration.pl create mode 100644 utils/scripts/Examples/SpawnManipulation.pl create mode 100644 utils/scripts/export_spells.pl create mode 100644 utils/scripts/factionmod.pl create mode 100644 utils/scripts/import_showeq create mode 100644 utils/scripts/import_spells.pl create mode 100644 utils/scripts/load_13thfloor_items.pl create mode 100644 utils/scripts/ppreader.pl create mode 100644 utils/scripts/schema.xml create mode 100644 utils/scripts/serialize_items.pl create mode 100644 utils/scripts/struct_fields.sh create mode 100644 utils/scripts/throwpackets.pl create mode 100644 utils/sql/queryserv/2079_player_speech.sql create mode 100644 utils/sql/queryserv/2268_QueryServ.sql create mode 100644 utils/sql/queryserv/2304_QueryServ.sql create mode 100644 utils/sql/queryserv/2361_QueryServ.sql create mode 100644 utils/sql/svn/1022_botadventuring.sql create mode 100644 utils/sql/svn/1027_botactives.sql create mode 100644 utils/sql/svn/1030_botzoningsupport.sql create mode 100644 utils/sql/svn/1036_botbuffs.sql create mode 100644 utils/sql/svn/1038_botpetstatepersists.sql create mode 100644 utils/sql/svn/1038_grouptablesuniquecolumndefinitions.sql create mode 100644 utils/sql/svn/1039_botguilds.sql create mode 100644 utils/sql/svn/103_optional_chat_rules.sql create mode 100644 utils/sql/svn/1040_DeprecatedBotRaidsSystems.sql create mode 100644 utils/sql/svn/104_traps.sql create mode 100644 utils/sql/svn/1057_titles.sql create mode 100644 utils/sql/svn/106_optional_proc_rules.sql create mode 100644 utils/sql/svn/1077_botgroups.sql create mode 100644 utils/sql/svn/1136_spell_globals.sql create mode 100644 utils/sql/svn/1144_optional_rule_return_nodrop.sql create mode 100644 utils/sql/svn/1195_account_suspendeduntil.sql create mode 100644 utils/sql/svn/120_damageshieldtypes.sql create mode 100644 utils/sql/svn/1259_npc_skill_types.sql create mode 100644 utils/sql/svn/125_aggrozone.sql create mode 100644 utils/sql/svn/127_optional_spell_rules.sql create mode 100644 utils/sql/svn/1280_bot_augs.sql create mode 100644 utils/sql/svn/1290_optional_exp_loss_rule.sql create mode 100644 utils/sql/svn/1293_guild_bank.sql create mode 100644 utils/sql/svn/129_optional_shared_plat_rule.sql create mode 100644 utils/sql/svn/131_optional_combat_rules.sql create mode 100644 utils/sql/svn/133_task_repeatable.sql create mode 100644 utils/sql/svn/1379_loginserver_trusted_server.sql create mode 100644 utils/sql/svn/1392_recipe_learning.sql create mode 100644 utils/sql/svn/1394_optional_rule_sod_hp_mana_end.sql create mode 100644 utils/sql/svn/1404_faction_list.sql create mode 100644 utils/sql/svn/1410_optional_sod_aas_ht_and_loh.sql create mode 100644 utils/sql/svn/142_deathpeace_and_lifetap_aas.sql create mode 100644 utils/sql/svn/1436_login_server_table_fix.sql create mode 100644 utils/sql/svn/1446_allowrest_optional.sql create mode 100644 utils/sql/svn/1446_allowrest_required.sql create mode 100644 utils/sql/svn/1450_cvs.sql create mode 100644 utils/sql/svn/1451_guilds.sql create mode 100644 utils/sql/svn/1498_instance_adventure.sql create mode 100644 utils/sql/svn/14_optional_merchantlist.sql create mode 100644 utils/sql/svn/1510_global_instances.sql create mode 100644 utils/sql/svn/1511_map_path_loading.sql create mode 100644 utils/sql/svn/1513_zone_points.sql create mode 100644 utils/sql/svn/1519_zone_primary_key_id.sql create mode 100644 utils/sql/svn/1542_items_table_cleanup.sql create mode 100644 utils/sql/svn/1548_nimbuseffect_required.sql create mode 100644 utils/sql/svn/1562_instanced_spawnconditions.sql create mode 100644 utils/sql/svn/1586_waypoints_optional.sql create mode 100644 utils/sql/svn/158_optional_death_exp_loss.sql create mode 100644 utils/sql/svn/1610_tradeskill_required.sql create mode 100644 utils/sql/svn/1618_zone.sql create mode 100644 utils/sql/svn/1625_optional_rule_class_race_exp_bonus.sql create mode 100644 utils/sql/svn/1672_optional_rules_respawn_window.sql create mode 100644 utils/sql/svn/1679_optional_rules_blocked_buffs.sql create mode 100644 utils/sql/svn/1696_modify_zone_and_object_tables.sql create mode 100644 utils/sql/svn/1711_account_restricted_aa.sql create mode 100644 utils/sql/svn/1717_optional_rule_bash_stun_chance.sql create mode 100644 utils/sql/svn/1718_optional_rules_mod3s.sql create mode 100644 utils/sql/svn/1719_optional_triggerOnCastAAs.sql create mode 100644 utils/sql/svn/1720_optional_sql_AAs.sql create mode 100644 utils/sql/svn/1720_required_sql_AA_effects_update.sql create mode 100644 utils/sql/svn/1721_optional_sql_drakkin_breath_update.sql create mode 100644 utils/sql/svn/1721_required_sql_altadv_vars_update.sql create mode 100644 utils/sql/svn/1723_optional_sql_new_stats_window_rule.sql create mode 100644 utils/sql/svn/1723_required_sql_corruption.sql create mode 100644 utils/sql/svn/1736_optional_sql_feral_swipe.sql create mode 100644 utils/sql/svn/1737_required_sql_rule_and_aa_update.sql create mode 100644 utils/sql/svn/1746_optional_sql_bot_manaregen.sql create mode 100644 utils/sql/svn/1747_optional_HoT_zone_and_zonepoints.sql create mode 100644 utils/sql/svn/1750_optional_sql_reflect_rule.sql create mode 100644 utils/sql/svn/1753_optional_haste_cap_rule.sql create mode 100644 utils/sql/svn/1753_required_sql_healing_adept_aa.sql create mode 100644 utils/sql/svn/1754_required_sql_healing_adept_aa_fix.sql create mode 100644 utils/sql/svn/1755_required_sql_fear_resist_aas.sql create mode 100644 utils/sql/svn/176_melody.sql create mode 100644 utils/sql/svn/1784_optional_corpsedrag_rules.sql create mode 100644 utils/sql/svn/1786_required_update_to_aas.sql create mode 100644 utils/sql/svn/1790_required_aa_required_level_cost.sql create mode 100644 utils/sql/svn/1793_resist_adjust.sql create mode 100644 utils/sql/svn/1799_optional_rest_regen_endurance_rule.sql create mode 100644 utils/sql/svn/1802_required_doppelganger.sql create mode 100644 utils/sql/svn/1803_required_tasks_xpreward_signed.sql create mode 100644 utils/sql/svn/1804_required_ae_melee_updates.sql create mode 100644 utils/sql/svn/1809_optional_rules.sql create mode 100644 utils/sql/svn/1813_required_doppelganger_npcid_change.sql create mode 100644 utils/sql/svn/1817_optional_npc_archery_bonus_rule.sql create mode 100644 utils/sql/svn/1823_optional_delay_death.sql create mode 100644 utils/sql/svn/1847_required_doors_dest_zone_size_32.sql create mode 100644 utils/sql/svn/1859_optional_item_casts_use_focus_rule.sql create mode 100644 utils/sql/svn/1884_optional_bot_spells_update.sql create mode 100644 utils/sql/svn/1885_optional_rules_fv_pvp_expansions.sql create mode 100644 utils/sql/svn/1889_optional_skill_cap_rule.sql create mode 100644 utils/sql/svn/189_character_.sql create mode 100644 utils/sql/svn/1908_required_npc_types_definitions.sql create mode 100644 utils/sql/svn/1926_optional_stat_cap.sql create mode 100644 utils/sql/svn/1944_spawn2.sql create mode 100644 utils/sql/svn/1946_doors.sql create mode 100644 utils/sql/svn/1960_optional_console_timeout_rule.sql create mode 100644 utils/sql/svn/1962_optional_guild_creation_window_rules.sql create mode 100644 utils/sql/svn/1963_optional_rule_live_like_focuses.sql create mode 100644 utils/sql/svn/1968_optional_enrage_rules.sql create mode 100644 utils/sql/svn/196_trader.sql create mode 100644 utils/sql/svn/1972_optional_extradmg_item_cap.sql create mode 100644 utils/sql/svn/1974_required_bot_spells_update.sql create mode 100644 utils/sql/svn/1977_underwater.sql create mode 100644 utils/sql/svn/1998_optional_intoxication_and_looting_rules.sql create mode 100644 utils/sql/svn/1_task_system.sql create mode 100644 utils/sql/svn/2004_charges_alt_currency.sql create mode 100644 utils/sql/svn/2015_optional_specialization_training_rule.sql create mode 100644 utils/sql/svn/2016_optional_rule_bot_aa_expansion.sql create mode 100644 utils/sql/svn/2023_optional_mysqlcli.sql create mode 100644 utils/sql/svn/2024_optional_update_crystals.sql create mode 100644 utils/sql/svn/2024_required_update.sql create mode 100644 utils/sql/svn/2057_required_discovered_items.sql create mode 100644 utils/sql/svn/2058_optional_rule_discovered_items.sql create mode 100644 utils/sql/svn/2062_required_version_changes.sql create mode 100644 utils/sql/svn/2069_required_pets.sql create mode 100644 utils/sql/svn/2087_required_bots_hp_and_mana_and_spell_updates.sql create mode 100644 utils/sql/svn/2098_required_zonepoint_version_changes.sql create mode 100644 utils/sql/svn/2099_required_discovered_items_account_status.sql create mode 100644 utils/sql/svn/2104_required_group_roles.sql create mode 100644 utils/sql/svn/2107_required_bot_stances.sql create mode 100644 utils/sql/svn/210_undyeme.sql create mode 100644 utils/sql/svn/2129_required_lfguild.sql create mode 100644 utils/sql/svn/2133_required_faction_loot_despawn.sql create mode 100644 utils/sql/svn/2136_extended_targets.sql create mode 100644 utils/sql/svn/2142_emotes.sql create mode 100644 utils/sql/svn/2154_optional_rule_spell_procs_resists_falloff.sql create mode 100644 utils/sql/svn/2156_optional_charm_break_rule.sql create mode 100644 utils/sql/svn/2159_optional_defensiveproc_rules.sql create mode 100644 utils/sql/svn/2164_require_bots_bottimers.sql create mode 100644 utils/sql/svn/2171_optional_SpecialAttackACBonus_rule.sql create mode 100644 utils/sql/svn/2176_optional_FrenzyBonus_rule.sql create mode 100644 utils/sql/svn/2176_optional_aa_expansion_SOF_fix.sql create mode 100644 utils/sql/svn/2176_required_aa_updates.sql create mode 100644 utils/sql/svn/2178_required_aa_updates.sql create mode 100644 utils/sql/svn/2183_optional_bot_xp_rule.sql create mode 100644 utils/sql/svn/2185_optional_NPCFlurryChacne_rule create mode 100644 utils/sql/svn/2185_optional_NPCFlurryChacne_rule.sql create mode 100644 utils/sql/svn/2185_optional_NPCFlurryChance_rule.sql create mode 100644 utils/sql/svn/2185_required_aa_updates create mode 100644 utils/sql/svn/2185_required_aa_updates.sql create mode 100644 utils/sql/svn/2188_optional_miscspelleffect_rules create mode 100644 utils/sql/svn/2188_optional_miscspelleffect_rules.sql create mode 100644 utils/sql/svn/2188_required_aa_updates create mode 100644 utils/sql/svn/2188_required_aa_updates.sql create mode 100644 utils/sql/svn/2189_optional_taunt_rules create mode 100644 utils/sql/svn/2189_optional_taunt_rules.sql create mode 100644 utils/sql/svn/2195_required_sharedplatupdates.sql create mode 100644 utils/sql/svn/2208_optional_EnableSoulAbrasionAA.sql create mode 100644 utils/sql/svn/2208_optional_aa_stacking_rule.sql create mode 100644 utils/sql/svn/2208_required_aa_updates.sql create mode 100644 utils/sql/svn/2209_optional_additive_bonus_rule.sql create mode 100644 utils/sql/svn/2213_loot_changes.sql create mode 100644 utils/sql/svn/2214_faction_list_mod.sql create mode 100644 utils/sql/svn/2215_required_aa_updates.sql create mode 100644 utils/sql/svn/222_buyer.sql create mode 100644 utils/sql/svn/2243_optional_char_max_level_rule.sql create mode 100644 utils/sql/svn/2260_probability.sql create mode 100644 utils/sql/svn/2262_required_pet_discipline_update.sql create mode 100644 utils/sql/svn/2264_required_aa_updates.sql create mode 100644 utils/sql/svn/2268_required_updates.sql create mode 100644 utils/sql/svn/226_account_limiting.sql create mode 100644 utils/sql/svn/2274_optional_rule_iplimitdisconnectall.sql create mode 100644 utils/sql/svn/2278_optional_rule_targetableswarmpet.sql create mode 100644 utils/sql/svn/2280_optional_rule_targetableswarmpet-rename.sql create mode 100644 utils/sql/svn/2283_required_npc_changes.sql create mode 100644 utils/sql/svn/2299_required_inspectmessage_fields.sql create mode 100644 utils/sql/svn/2300_optional_loot_changes.sql create mode 100644 utils/sql/svn/230_spells_table.sql create mode 100644 utils/sql/svn/2340_required_maxbuffslotspet.sql create mode 100644 utils/sql/svn/235_horses_table.sql create mode 100644 utils/sql/svn/2361_required_qs_rule_values.sql create mode 100644 utils/sql/svn/2370_required_aa_updates.sql create mode 100644 utils/sql/svn/2376_required_aa_updates.sql create mode 100644 utils/sql/svn/2380_optional_merc_data.sql create mode 100644 utils/sql/svn/2380_optional_merc_merchant_npctypes_update.sql create mode 100644 utils/sql/svn/2380_optional_merc_rules.sql create mode 100644 utils/sql/svn/2383_required_group_ismerc.sql create mode 100644 utils/sql/svn/2428_optional_levelbasedexpmods.sql create mode 100644 utils/sql/svn/243_spawn_timers.sql create mode 100644 utils/sql/svn/2448_optional_stun_proc_aggro_rule.sql create mode 100644 utils/sql/svn/2471_required_aa_updates.sql create mode 100644 utils/sql/svn/247_mail.sql create mode 100644 utils/sql/svn/2482_required_start_zones.sql create mode 100644 utils/sql/svn/249_chatchannels.sql create mode 100644 utils/sql/svn/2504_required_aa_updates.sql create mode 100644 utils/sql/svn/250_bot_spell_update.sql create mode 100644 utils/sql/svn/250_optional_bot_spell_update.sql create mode 100644 utils/sql/svn/285_optional_bot_spell_update.sql create mode 100644 utils/sql/svn/292_augslots.sql create mode 100644 utils/sql/svn/294_merchant_logging.sql create mode 100644 utils/sql/svn/2_optional_maxclients.sql create mode 100644 utils/sql/svn/304_faction_list.sql create mode 100644 utils/sql/svn/326_aas.sql create mode 100644 utils/sql/svn/328_bot_management.sql create mode 100644 utils/sql/svn/328_optional_bot_management.sql create mode 100644 utils/sql/svn/340_gm_ips.sql create mode 100644 utils/sql/svn/356_combat.sql create mode 100644 utils/sql/svn/35_task_stepped.sql create mode 100644 utils/sql/svn/360_peqzone.sql create mode 100644 utils/sql/svn/364_ranged_dist_rule.sql create mode 100644 utils/sql/svn/386_bot_save_raid.sql create mode 100644 utils/sql/svn/42_task_min_maxlevel.sql create mode 100644 utils/sql/svn/434_optional_rest_state_rules.sql create mode 100644 utils/sql/svn/447_sof_startzone_rule.sql create mode 100644 utils/sql/svn/463_altadv_vars.sql create mode 100644 utils/sql/svn/475_aa_actions.sql create mode 100644 utils/sql/svn/500_spawn2_optimization.sql create mode 100644 utils/sql/svn/503_bugs.sql create mode 100644 utils/sql/svn/518_drakkin_npc_type_features.sql create mode 100644 utils/sql/svn/524_rule_values_notes.sql create mode 100644 utils/sql/svn/527_npc_armor_tint.sql create mode 100644 utils/sql/svn/553_saylink_table.sql create mode 100644 utils/sql/svn/55_zone_shutdowndeleay.sql create mode 100644 utils/sql/svn/564_nokeyring.sql create mode 100644 utils/sql/svn/600_group_leadership.sql create mode 100644 utils/sql/svn/612_instance_changes.sql create mode 100644 utils/sql/svn/615_adventure_assassination.sql create mode 100644 utils/sql/svn/619_Adventure_Recruiter_Flavor.sql create mode 100644 utils/sql/svn/621_LDoNTraps.sql create mode 100644 utils/sql/svn/633_ucs.sql create mode 100644 utils/sql/svn/634_TrapTemplateDefaultValue.sql create mode 100644 utils/sql/svn/643_BotsTable.sql create mode 100644 utils/sql/svn/646_archery_penalty_rule.sql create mode 100644 utils/sql/svn/665_heroic_resists.sql create mode 100644 utils/sql/svn/667_titles.sql create mode 100644 utils/sql/svn/687_aa_table_changes.sql create mode 100644 utils/sql/svn/68_optional_character_maxexplevel.sql create mode 100644 utils/sql/svn/699_peqzone_rule.sql create mode 100644 utils/sql/svn/702_aashieldblock_tint_table.sql create mode 100644 utils/sql/svn/703_peqzone_rule.sql create mode 100644 utils/sql/svn/704_rules.sql create mode 100644 utils/sql/svn/710_tint_set_naming.sql create mode 100644 utils/sql/svn/721_pathing_rules.sql create mode 100644 utils/sql/svn/730_smart_delay_moving.sql create mode 100644 utils/sql/svn/731_rule_assist_notarget_self.sql create mode 100644 utils/sql/svn/732_sacrifice_rules.sql create mode 100644 utils/sql/svn/745_slow_mitigation.sql create mode 100644 utils/sql/svn/754_archery_base_damage_rule.sql create mode 100644 utils/sql/svn/755_sof_altadv_vars_updates.sql create mode 100644 utils/sql/svn/773_monk_rules.sql create mode 100644 utils/sql/svn/853_optional_rule_aaexp.sql create mode 100644 utils/sql/svn/858_optional_rule_ip_limit_by_status.sql create mode 100644 utils/sql/svn/892_optional_bots_table_mod.sql create mode 100644 utils/sql/svn/893_optional_bots_table_mod.sql create mode 100644 utils/sql/svn/898_npc_maxlevel_scalerate.sql create mode 100644 utils/sql/svn/902_optional_rule_snareflee.sql create mode 100644 utils/sql/svn/923_spawn2_enabled.sql create mode 100644 utils/sql/svn/962_hot_zone.sql create mode 100644 utils/sql/svn/964_reports.sql create mode 100644 utils/sql/svn/971_veteran_rewards.sql create mode 100644 utils/sql/svn/977_raid_npc_private_corpses.sql create mode 100644 utils/sql/svn/979_unique_spawn_by_name.sql create mode 100644 utils/sql/svn/980_account_ip.sql create mode 100644 utils/sql/svn/bots.sql create mode 100644 utils/sql/svn/botsconvert.sql create mode 100644 utils/sql/svn/mercs.sql create mode 100644 world/Adventure.cpp create mode 100644 world/Adventure.h create mode 100644 world/AdventureManager.cpp create mode 100644 world/AdventureManager.h create mode 100644 world/AdventureTemplate.h create mode 100644 world/CMakeLists.txt create mode 100644 world/EQLConfig.cpp create mode 100644 world/EQLConfig.h create mode 100644 world/EQW.cpp create mode 100644 world/EQW.h create mode 100644 world/EQWHTTPHandler.cpp create mode 100644 world/EQWHTTPHandler.h create mode 100644 world/EQWParser.cpp create mode 100644 world/EQWParser.h create mode 100644 world/HTTPRequest.cpp create mode 100644 world/HTTPRequest.h create mode 100644 world/LauncherLink.cpp create mode 100644 world/LauncherLink.h create mode 100644 world/LauncherList.cpp create mode 100644 world/LauncherList.h create mode 100644 world/LoginServer.cpp create mode 100644 world/LoginServer.h create mode 100644 world/LoginServerList.cpp create mode 100644 world/LoginServerList.h create mode 100644 world/SoFCharCreateData.h create mode 100644 world/WorldConfig.cpp create mode 100644 world/WorldConfig.h create mode 100644 world/WorldTCPConnection.h create mode 100644 world/client.cpp create mode 100644 world/client.h create mode 100644 world/cliententry.cpp create mode 100644 world/cliententry.h create mode 100644 world/clientlist.cpp create mode 100644 world/clientlist.h create mode 100644 world/console.cpp create mode 100644 world/console.h create mode 100644 world/lfplist.cpp create mode 100644 world/lfplist.h create mode 100644 world/net.cpp create mode 100644 world/net.h create mode 100644 world/perl_EQLConfig.cpp create mode 100644 world/perl_EQW.cpp create mode 100644 world/perl_HTTPRequest.cpp create mode 100644 world/queryserv.cpp create mode 100644 world/queryserv.h create mode 100644 world/ucs.cpp create mode 100644 world/ucs.h create mode 100644 world/wguild_mgr.cpp create mode 100644 world/wguild_mgr.h create mode 100644 world/world_logsys.cpp create mode 100644 world/worlddb.cpp create mode 100644 world/worlddb.h create mode 100644 world/zonelist.cpp create mode 100644 world/zonelist.h create mode 100644 world/zoneserver.cpp create mode 100644 world/zoneserver.h create mode 100644 zone/AA.cpp create mode 100644 zone/AA.h create mode 100644 zone/CMakeLists.txt create mode 100644 zone/Map.cpp create mode 100644 zone/MobAI.cpp create mode 100644 zone/NpcAI.cpp create mode 100644 zone/NpcAI.h create mode 100644 zone/Object.cpp create mode 100644 zone/PlayerCorpse.cpp create mode 100644 zone/PlayerCorpse.h create mode 100644 zone/QGlobals.cpp create mode 100644 zone/QGlobals.h create mode 100644 zone/Quest.cpp create mode 100644 zone/Quest.h create mode 100644 zone/QuestInterface.h create mode 100644 zone/QuestParserCollection.cpp create mode 100644 zone/QuestParserCollection.h create mode 100644 zone/StringIDs.h create mode 100644 zone/ZoneConfig.cpp create mode 100644 zone/ZoneConfig.h create mode 100644 zone/aggro.cpp create mode 100644 zone/attack.cpp create mode 100644 zone/basic_functions.h create mode 100644 zone/beacon.cpp create mode 100644 zone/beacon.h create mode 100644 zone/blah.h create mode 100644 zone/bonuses.cpp create mode 100644 zone/bot.cpp create mode 100644 zone/bot.h create mode 100644 zone/botStructs.h create mode 100644 zone/botspellsai.cpp create mode 100644 zone/client.cpp create mode 100644 zone/client.h create mode 100644 zone/client_logs.cpp create mode 100644 zone/client_logs.h create mode 100644 zone/client_mods.cpp create mode 100644 zone/client_packet.cpp create mode 100644 zone/client_packet.h create mode 100644 zone/client_process.cpp create mode 100644 zone/command.cpp create mode 100644 zone/command.h create mode 100644 zone/common.h create mode 100644 zone/doors.cpp create mode 100644 zone/doors.h create mode 100644 zone/effects.cpp create mode 100644 zone/embparser.cpp create mode 100644 zone/embparser.h create mode 100644 zone/embperl.cpp create mode 100644 zone/embperl.h create mode 100644 zone/embxs.cpp create mode 100644 zone/embxs.h create mode 100644 zone/entity.cpp create mode 100644 zone/entity.h create mode 100644 zone/errmsg.h create mode 100644 zone/event_codes.h create mode 100644 zone/exp.cpp create mode 100644 zone/faction.cpp create mode 100644 zone/faction.h create mode 100644 zone/fearpath.cpp create mode 100644 zone/features.h create mode 100644 zone/forage.cpp create mode 100644 zone/forage.h create mode 100644 zone/groups.cpp create mode 100644 zone/groups.h create mode 100644 zone/guild.cpp create mode 100644 zone/guild_mgr.cpp create mode 100644 zone/guild_mgr.h create mode 100644 zone/hate_list.cpp create mode 100644 zone/hate_list.h create mode 100644 zone/horse.cpp create mode 100644 zone/horse.h create mode 100644 zone/inventory.cpp create mode 100644 zone/loottable.h create mode 100644 zone/loottables.cpp create mode 100644 zone/map.h create mode 100644 zone/masterentity.h create mode 100644 zone/maxskill.h create mode 100644 zone/merc.cpp create mode 100644 zone/merc.h create mode 100644 zone/message.h create mode 100644 zone/mob.cpp create mode 100644 zone/mob.h create mode 100644 zone/net.cpp create mode 100644 zone/net.h create mode 100644 zone/npc.cpp create mode 100644 zone/npc.h create mode 100644 zone/object.h create mode 100644 zone/oldcode.cpp create mode 100644 zone/oldcode.h create mode 100644 zone/parser.cpp create mode 100644 zone/parser.h create mode 100644 zone/pathing.cpp create mode 100644 zone/pathing.h create mode 100644 zone/perl_PlayerCorpse.cpp create mode 100644 zone/perl_client.cpp create mode 100644 zone/perl_doors.cpp create mode 100644 zone/perl_entity.cpp create mode 100644 zone/perl_groups.cpp create mode 100644 zone/perl_hateentry.cpp create mode 100644 zone/perl_mob.cpp create mode 100644 zone/perl_npc.cpp create mode 100644 zone/perl_object.cpp create mode 100644 zone/perl_perlpacket.cpp create mode 100644 zone/perl_questitem.cpp create mode 100644 zone/perl_raids.cpp create mode 100644 zone/perlpacket.cpp create mode 100644 zone/perlpacket.h create mode 100644 zone/perlparser.cpp create mode 100644 zone/perlparser.h create mode 100644 zone/petitions.cpp create mode 100644 zone/petitions.h create mode 100644 zone/pets.cpp create mode 100644 zone/pets.h create mode 100644 zone/questmgr.cpp create mode 100644 zone/questmgr.h create mode 100644 zone/queues.h create mode 100644 zone/raid.h create mode 100644 zone/raids.cpp create mode 100644 zone/raids.h create mode 100644 zone/skills.h create mode 100644 zone/spawn2.cpp create mode 100644 zone/spawn2.h create mode 100644 zone/spawngroup.cpp create mode 100644 zone/spawngroup.h create mode 100644 zone/spdat.cpp create mode 100644 zone/spdat.h create mode 100644 zone/special_attacks.cpp create mode 100644 zone/spell_effects.cpp create mode 100644 zone/spells.cpp create mode 100644 zone/tasks.cpp create mode 100644 zone/tasks.h create mode 100644 zone/titles.cpp create mode 100644 zone/titles.h create mode 100644 zone/tradeskills.cpp create mode 100644 zone/trading.cpp create mode 100644 zone/trap.cpp create mode 100644 zone/trap.h create mode 100644 zone/tribute.cpp create mode 100644 zone/updatemgr.cpp create mode 100644 zone/updatemgr.h create mode 100644 zone/watermap.cpp create mode 100644 zone/watermap.h create mode 100644 zone/waypoints.cpp create mode 100644 zone/worldserver.cpp create mode 100644 zone/worldserver.h create mode 100644 zone/zone.cpp create mode 100644 zone/zone.h create mode 100644 zone/zone_logsys.cpp create mode 100644 zone/zone_profile.cpp create mode 100644 zone/zone_profile.h create mode 100644 zone/zonedb.cpp create mode 100644 zone/zonedb.h create mode 100644 zone/zonedbasync.cpp create mode 100644 zone/zonedbasync.h create mode 100644 zone/zonedump.h create mode 100644 zone/zoning.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..6c5bae22a --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,164 @@ +#EQEmu Cmake + +#We set a fairly new version (as of 2013) because I found finding perl was a bit... buggy on older ones +#Can change this if you really want but you should upgrade! +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +#FindMySQL is located here so lets make it so CMake can find it +SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/" ${CMAKE_MODULE_PATH}) + +#For checking includes +INCLUDE (CheckIncludeFiles) + +#Our project name is EQEmu +PROJECT(EQEmu) + +#Default build type is set to RelWithDebInfo for generators that honor that like makefiles +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE) +ENDIF(NOT CMAKE_BUILD_TYPE) + +#Add our various windows definitions +IF(MSVC OR MINGW) + ADD_DEFINITIONS(-D_WINDOWS) + IF(CMAKE_CL_64) + ADD_DEFINITIONS(-DWIN64) + ELSE(CMAKE_CL_64) + ADD_DEFINITIONS(-DWIN32) + ENDIF(CMAKE_CL_64) +ENDIF(MSVC OR MINGW) + +IF(MSVC) + #Set our default locations for zlib/mysql based on x86/x64 + IF(CMAKE_CL_64) + SET(ZLIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/zlib_x64") + SET(MYSQL_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/mysql_x64") + ELSE(CMAKE_CL_64) + SET(ZLIB_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/zlib_x86") + SET(MYSQL_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/mysql_x86") + ENDIF(CMAKE_CL_64) + + #disable CRT warnings on windows cause they're annoying as shit and we use C functions everywhere + OPTION(EQEMU_DISABLE_CRT_SECURE_WARNINGS "Disable Secure CRT Warnings" ON) + IF(EQEMU_DISABLE_CRT_SECURE_WARNINGS) + ADD_DEFINITIONS(-D_CRT_SECURE_NO_WARNINGS) + ENDIF(EQEMU_DISABLE_CRT_SECURE_WARNINGS) + + #fast FP if you'd like it + OPTION(EQEMU_FAST_FLOATINGPOINT "Use MSVC /fp:fast option" ON) + IF(EQEMU_FAST_FLOATINGPOINT) + ADD_DEFINITIONS(/fp:fast) + ENDIF(EQEMU_FAST_FLOATINGPOINT) + + #crash logging currently only works on windows x86/x64 + OPTION(EQEMU_ENABLE_CRASH_LOGGING "Enable crash logging" ON) + IF(EQEMU_ENABLE_CRASH_LOGGING) + ADD_DEFINITIONS(-DCRASH_LOGGING) + ENDIF(EQEMU_ENABLE_CRASH_LOGGING) + + #Disable safe SEH or not? + OPTION(EQEMU_DISABLE_SAFESEH "Disable Safe SEH (Needed for Strawberry Perl)" OFF) + IF(EQEMU_DISABLE_SAFESEH) + SET(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /SAFESEH:NO") + SET(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL} /SAFESEH:NO") + SET(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /SAFESEH:NO") + SET(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} /SAFESEH:NO") + SET(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} /SAFESEH:NO") + SET(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "${CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL} /SAFESEH:NO") + SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /SAFESEH:NO") + SET(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} /SAFESEH:NO") + SET(CMAKE_MODULE_LINKER_FLAGS_DEBUG "${CMAKE_MODULE_LINKER_FLAGS_DEBUG} /SAFESEH:NO") + SET(CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL "${CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL} /SAFESEH:NO") + 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) + + #We want to compile /MT not /MD so we change that + FOREACH(flag_var CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_RELWITHDEBINFO CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELEASE CMAKE_C_FLAGS_RELWITHDEBINFO) + IF(${flag_var} MATCHES "/MD") + STRING(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + ENDIF(${flag_var} MATCHES "/MD") + ENDFOREACH(flag_var) +ELSE(MSVC) + #Normally set by perl but we don't use the perl flags anymore so we set it. + ADD_DEFINITIONS(-DHAS_UNION_SEMUN) +ENDIF(MSVC) + +#use stdint.h types if they exist for this platform (we have to guess otherwise) +CHECK_INCLUDE_FILES(stdint.h HAVE_STDINT_H) +IF(HAVE_STDINT_H) + ADD_DEFINITIONS(-DEQEMU_USE_STDINT) +ENDIF(HAVE_STDINT_H) + +#debug level, 5 is default. Most people wont ever change this but it's there if you want to +SET(EQEMU_DEBUG_LEVEL 5 CACHE STRING "EQEmu debug level: + 0 - Quiet mode Errors to file Status and Normal ignored + 1 - Status and Normal to console, Errors to logfile + 2 - Status, Normal, and Error to console and logfile + 3 - Light debug release errors and status + 4 - Moderate debug release errors and status + 5 - Maximum debug release errors and status + 10 - More errors than you ever wanted to see" +) + +#Bots are a compile time option so on/off +OPTION(EQEMU_ENABLE_BOTS "Enable Bots" OFF) +IF(EQEMU_ENABLE_BOTS) + ADD_DEFINITIONS(-DBOTS) +ENDIF(EQEMU_ENABLE_BOTS) + +#What to build +OPTION(EQEMU_BUILD_SERVER "Build the game server." ON) +OPTION(EQEMU_BUILD_LOGIN "Build the login server." OFF) +OPTION(EQEMU_BUILD_AZONE "Build azone utility." OFF) +OPTION(EQEMU_BUILD_TESTS "Build utility tests." OFF) + +IF(UNIX) + #Whether to build cleanipc or not (probably a good idea if you build server) + OPTION(EQEMU_BUILD_CLEANIPC "Build cleanipc." ON) + + #Use C++11 stuff, support for this is still it infancy + OPTION(EQEMU_CPP_ELEVEN "Enable C++11 extentions in g++" OFF) + IF(EQEMU_CPP_ELEVEN) + ADD_DEFINITIONS(-std=c++0x) + ENDIF(EQEMU_CPP_ELEVEN) +ENDIF(UNIX) + +#Various definitions +ADD_DEFINITIONS(-DEMBPERL) +ADD_DEFINITIONS(-DEMBPERL_PLUGIN) +ADD_DEFINITIONS(-DEQDEBUG=${EQEMU_DEBUG_LEVEL}) +ADD_DEFINITIONS(-DSHAREMEM) +ADD_DEFINITIONS(-DINVERSEXY) +ADD_DEFINITIONS(-DFIELD_ITEMS) +ADD_DEFINITIONS(-DMAP_DIR="./Maps") + +#Find everything we need +FIND_PACKAGE(ZLIB REQUIRED) +FIND_PACKAGE(MySQL REQUIRED) +FIND_PACKAGE(PerlLibs REQUIRED) +INCLUDE_DIRECTORIES("${ZLIB_INCLUDE_DIRS}" "${PERL_INCLUDE_PATH}" "${MySQL_INCLUDE_DIR}") + +IF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS) + ADD_SUBDIRECTORY(common) +ENDIF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS) +IF(EQEMU_BUILD_SERVER) + ADD_SUBDIRECTORY(EMuShareMem) + ADD_SUBDIRECTORY(world) + ADD_SUBDIRECTORY(zone) + ADD_SUBDIRECTORY(ucs) + ADD_SUBDIRECTORY(queryserv) + ADD_SUBDIRECTORY(eqlaunch) +ENDIF(EQEMU_BUILD_SERVER) +IF(EQEMU_BUILD_LOGIN) + ADD_SUBDIRECTORY(loginserver) +ENDIF(EQEMU_BUILD_LOGIN) + +IF(EQEMU_BUILD_AZONE OR EQEMU_BUILD_CLEANIPC) + ADD_SUBDIRECTORY(utils) +ENDIF(EQEMU_BUILD_AZONE OR EQEMU_BUILD_CLEANIPC) + +IF(EQEMU_BUILD_TESTS) +# Testing framework not quite ready for prime time. +# ADD_SUBDIRECTORY(tests) +ENDIF(EQEMU_BUILD_TESTS) diff --git a/EMuShareMem/CMakeLists.txt b/EMuShareMem/CMakeLists.txt new file mode 100644 index 000000000..b39363207 --- /dev/null +++ b/EMuShareMem/CMakeLists.txt @@ -0,0 +1,46 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +SET(sharedmem_src + DLLMain.cpp + Doors.cpp + Items.cpp + Loot.cpp + MMF.cpp + MMFMutex.cpp + NPCFactionLists.cpp + Opcodes.cpp + SkillCaps.cpp + Spells.cpp +) + +SET(sharedmem_headers + Doors.h + Items.h + Loot.h + MMF.h + MMFMutex.h + NPCFactionLists.h + Opcodes.h + SkillCaps.h + Spells.h +) + +SET(EQEMU_MAX_ITEMS 300000 CACHE STRING "Maxium number of items to load into memory. Make sure this is bigger than the total number of items in the server database") +SET(EQEMU_MAX_DOORS 30000 CACHE STRING "Maxium number of doors to load into memory. Make sure this is bigger than the total number of doors in the server database") +SET(EQEMU_MAX_FACTIONLIST_IDS 50000 CACHE STRING "Maxium number of FactionList IDs to load into memory. Make sure this is bigger than the total number of FactionList IDs in the server database") + +ADD_DEFINITIONS(-DMMF_EQMAX_ITEMS=${EQEMU_MAX_ITEMS}) +ADD_DEFINITIONS(-DMMF_MAX_Door_ID=${EQEMU_MAX_DOORS}) +ADD_DEFINITIONS(-DMMF_MAX_NPCFactionList_ID=${EQEMU_MAX_FACTIONLIST_IDS}) + +ADD_LIBRARY(EMuShareMem SHARED ${sharedmem_src} ${sharedmem_headers}) +TARGET_LINK_LIBRARIES(EMuShareMem Common) + +IF(UNIX) + TARGET_LINK_LIBRARIES(EMuShareMem "dl") + TARGET_LINK_LIBRARIES(EMuShareMem "m") + TARGET_LINK_LIBRARIES(EMuShareMem "rt") + TARGET_LINK_LIBRARIES(EMuShareMem "pthread") +ENDIF(UNIX) + +SET(LIBRARY_OUTPUT_PATH ../Bin) diff --git a/EMuShareMem/DLLMain.cpp b/EMuShareMem/DLLMain.cpp new file mode 100644 index 000000000..05015d4b8 --- /dev/null +++ b/EMuShareMem/DLLMain.cpp @@ -0,0 +1,37 @@ +/* + EMuShareMem.dll + by Quagmire + Released under GPL + + This DLL's purpose it to hold a single shared copy of items, npctypes, spells, and other + stuff that's normally cached in memory, thus allowing all processes on the server to share + one copy of the data, greatly reducing the amount of RAM used. +*/ +#ifdef _WINDOWS + +#include +void CloseMemShare(); + +BOOL WINAPI DllMain( + HINSTANCE hinstDLL, // handle to DLL module + DWORD fdwReason, // reason for calling function + LPVOID lpReserved ) // reserved +{ + // Perform actions based on the reason for calling. + switch( fdwReason ) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: { + break; + } + case DLL_THREAD_DETACH: { + break; + } + case DLL_PROCESS_DETACH: { + break; + } + } + return TRUE; // Successful DLL_PROCESS_ATTACH. +} + +#endif //WIN32 diff --git a/EMuShareMem/Doors.cpp b/EMuShareMem/Doors.cpp new file mode 100644 index 000000000..4b9775bf1 --- /dev/null +++ b/EMuShareMem/Doors.cpp @@ -0,0 +1,136 @@ +#include "../common/debug.h" + +#ifdef _WINDOWS +#include +#else +#include "../common/unix.h" +#endif + +#include +#include +using namespace std; +#include "Doors.h" +#include "../common/timer.h" +#include "MMF.h" + +MMF DoorsMMF; +const MMFDoors_Struct* MMFDoorsData = 0; +MMFDoors_Struct* MMFDoorsData_Writable = 0; + +#ifdef _WINDOWS +extern "C" __declspec(dllexport) const Door* GetDoor(uint32 id) { + return pGetDoor(id); +}; + +extern "C" __declspec(dllexport) bool AddDoor(uint32 id, const Door* door) { + return pAddDoor(id, door); +}; + +extern "C" __declspec(dllexport) bool DLLLoadDoors(CALLBACK_DBLoadDoors cbDBLoadDoors, uint32 iDoorstructSize, int32* iDoorsCount, uint32* iMaxDoorID) { + return pDLLLoadDoors(cbDBLoadDoors, iDoorstructSize, iDoorsCount, iMaxDoorID); +}; +#else +extern "C" const Door* GetDoor(uint32 id) { + return pGetDoor(id); +}; + +extern "C" bool AddDoor(uint32 id, const Door* door) { + return pAddDoor(id, door); +}; + +extern "C" bool DLLLoadDoors(CALLBACK_DBLoadDoors cbDBLoadDoors, uint32 iDoorstructSize, int32* iDoorsCount, uint32* iMaxDoorID) { + return pDLLLoadDoors(cbDBLoadDoors, iDoorstructSize, iDoorsCount, iMaxDoorID); +}; + +#endif + +bool pAddDoor(uint32 id, const Door* door) { + if (!MMFDoorsData_Writable) + return false; + if (id > MMF_MAX_Door_ID || MMFDoorsData_Writable->NextFreeIndex >= MMFDoorsData_Writable->DoorCount) + return false; + if (MMFDoorsData_Writable->DoorIndex[id] != 0xFFFFFFFF) + return false; + + MMFDoorsData_Writable->DoorIndex[id] = MMFDoorsData_Writable->NextFreeIndex++; + memcpy(&MMFDoorsData_Writable->Doors[MMFDoorsData_Writable->DoorIndex[id]], door, sizeof(Door)); + + return true; +} + +bool pDLLLoadDoors(CALLBACK_DBLoadDoors cbDBLoadDoors, uint32 iDoorstructSize, int32* iDoorsCount, uint32* iMaxDoorID) { + if (iDoorstructSize != sizeof(Door)) { + cout << "Error: EMuShareMem: DLLLoadDoors: iDoorstructSize != sizeof(Door)" << endl; + cout << "Door struct has changed, EMuShareMem.dll needs to be recompiled." << endl; + return false; + } + if (*iMaxDoorID > MMF_MAX_Door_ID) { + cout << "Error: EMuShareMem: pDLLLoadDoors: iMaxDoorID > MMF_MAX_Door_ID" << endl; + cout << "You need to increase the define in Doors.h." << endl; + return false; + } + uint32 tmpMemSize = sizeof(MMFDoors_Struct) + 256 + (sizeof(Door) * (*iDoorsCount)); + if (DoorsMMF.Open("EQEMuDoors", tmpMemSize)) { + if (DoorsMMF.CanWrite()) { + MMFDoorsData_Writable = (MMFDoors_Struct*) DoorsMMF.GetWriteableHandle(); + if (!MMFDoorsData_Writable) { + cout << "Error: EMuShareMem: DLLLoadDoors: !MMFDoorsData_Writable" << endl; + return false; + } + + memset(MMFDoorsData_Writable, 0, tmpMemSize); + for(int i=0; iDoorIndex[i] = 0xFFFFFFFF; + MMFDoorsData_Writable->MaxDoorID = *iMaxDoorID; + MMFDoorsData_Writable->DoorCount = *iDoorsCount; + // use a callback so the DB functions are done in the main exe + // this way the DLL doesnt have to open a connection to mysql + if (!cbDBLoadDoors(*iDoorsCount, *iMaxDoorID)) { + cout << "Error: EMuShareMem: DLLLoadDoors: !cbDBLoadDoors" << endl; + return false; + } + + MMFDoorsData_Writable = 0; + DoorsMMF.SetLoaded(); + MMFDoorsData = (const MMFDoors_Struct*) DoorsMMF.GetHandle(); + if (!MMFDoorsData) { + cout << "Error: EMuShareMem: DLLLoadDoors: !MMFDoorsData (CanWrite=true)" << endl; + return false; + } + return true; + } + else { + if (!DoorsMMF.IsLoaded()) { + Timer::SetCurrentTime(); + uint32 starttime = Timer::GetCurrentTime(); + while ((!DoorsMMF.IsLoaded()) && ((Timer::GetCurrentTime() - starttime) < 300000)) { + Sleep(10); + Timer::SetCurrentTime(); + } + if (!DoorsMMF.IsLoaded()) { + cout << "Error: EMuShareMem: DLLLoadDoors: !DoorsMMF.IsLoaded() (timeout)" << endl; + return false; + } + } + MMFDoorsData = (const MMFDoors_Struct*) DoorsMMF.GetHandle(); + if (!MMFDoorsData) { + cout << "Error: EMuShareMem: DLLLoadDoors: !MMFDoorsData (CanWrite=false)" << endl; + return false; + } + *iMaxDoorID = MMFDoorsData->MaxDoorID; + *iDoorsCount = MMFDoorsData->DoorCount; + return true; + } + } + else { + cout << "Error Loading Doors: Doors.cpp: pDLLLoadDoors: ret == 0" << endl; + return false; + } + return false; +}; + +const Door* pGetDoor(uint32 id) { + if (MMFDoorsData == 0 || (!DoorsMMF.IsLoaded()) || id > MMF_MAX_Door_ID || MMFDoorsData->DoorIndex[id] == 0xFFFFFFFF) + return 0; + return &MMFDoorsData->Doors[MMFDoorsData->DoorIndex[id]]; +} diff --git a/EMuShareMem/Doors.h b/EMuShareMem/Doors.h new file mode 100644 index 000000000..6a5a7ab47 --- /dev/null +++ b/EMuShareMem/Doors.h @@ -0,0 +1,22 @@ +#include "../common/types.h" +#include "../zone/zonedump.h" +#include "../common/EMuShareMem.h" + +// MMF_MAX_Door_ID: Make sure this is bigger than the highest Door ID# +#ifndef MMF_MAX_Door_ID +#define MMF_MAX_Door_ID 30000 +#endif +// MMF_MAX_Door_MEM: Maxium number of Doors to load into memory. Make sure this is bigger +// than the total number of Doors in the server's database! + +struct MMFDoors_Struct { + uint32 MaxDoorID; + uint32 NextFreeIndex; + uint32 DoorCount; + uint32 DoorIndex[MMF_MAX_Door_ID+1]; + Door Doors[0]; +}; + +bool pDLLLoadDoors(CALLBACK_DBLoadDoors cbDBLoadDoors, uint32 iDoorstructSize, int32* iDoorsCount, uint32* iMaxDoorID); +bool pAddDoor(uint32 id, const Door* door); +const Door* pGetDoor(uint32 id); diff --git a/EMuShareMem/Items.cpp b/EMuShareMem/Items.cpp new file mode 100644 index 000000000..50c5e5b99 --- /dev/null +++ b/EMuShareMem/Items.cpp @@ -0,0 +1,152 @@ +/* + Note: Do NOT change this to load items on an as-needed basis. Since this memory is + accessed from multiple threads, you'd need mutex's all over the place if it was + ever to be modified/updated/added to. The overhead of the mutexes would be alot more + in the long run than the delay in loading. + + -Quagmire +*/ + +#ifdef _WINDOWS +#include +#include +#else +#include "../common/unix.h" +#endif + +#include +#include +using namespace std; +#include "Items.h" +#include "../common/timer.h" +#include "MMF.h" + +MMF ItemsMMF; +const MMFItems_Struct* MMFItemsData = 0; +MMFItems_Struct* MMFItemsData_Writable = 0; + +DLLFUNC bool AddItem(uint32 id, const Item_Struct* item) { + if (!MMFItemsData_Writable) { + return false; + } + if (id > MMF_EQMAX_ITEMS || MMFItemsData_Writable->NextFreeIndex >= MMFItemsData_Writable->ItemCount) { + return false; + } + if (MMFItemsData_Writable->ItemIndex[id] != 0xFFFF) { + return false; + } + + uint32 nextid = MMFItemsData_Writable->NextFreeIndex++; + MMFItemsData_Writable->ItemIndex[id] = nextid; + memcpy(&MMFItemsData_Writable->Items[nextid], item, sizeof(Item_Struct)); + + return true; +} + +DLLFUNC bool DLLLoadItems(CALLBACK_DBLoadItems cbDBLoadItems, uint32 iItemStructSize, int32* iItemCount, uint32* iMaxItemID) { + if (iItemStructSize != sizeof(Item_Struct)) { + cout << "Error: EMuShareMem: DLLLoadItems: iItemStructSize != sizeof(Item_Struct)" << endl; + cout << "Item_Struct has changed, EMuShareMem.dll needs to be recompiled." << endl; + return false; + } + if (*iMaxItemID > MMF_EQMAX_ITEMS) { + cout << "Error: EMuShareMem: pDLLLoadItems: iMaxItemID > MMF_EQMAX_ITEMS" << endl; + cout << "You need to increase the define in Items.h." << endl; + return false; + } + + MMFItemsData_Writable = 0; + //Allocate the shared memory for the item structures + uint32 tmpMemSize = sizeof(MMFItems_Struct) + 256 + (sizeof(Item_Struct) * (*iItemCount)); + //cout << tmpMemSize << endl; + if (ItemsMMF.Open("EQEMuItems", tmpMemSize)) { + if (ItemsMMF.CanWrite()) { + MMFItemsData_Writable = (MMFItems_Struct*) ItemsMMF.GetWriteableHandle(); + if (!MMFItemsData_Writable) { + cout << "Error: EMuShareMem: DLLLoadItems: !MMFItemsData_Writable" << endl; + return false; + } + + memset(MMFItemsData_Writable, 0, tmpMemSize); + for(int i=0; iItemIndex[i] = 0xFFFF; + MMFItemsData_Writable->MaxItemID = *iMaxItemID; + MMFItemsData_Writable->ItemCount = *iItemCount; + //the writable handle has been created, do the load below after we have the + //serialization handle as well. + } else { + if (!ItemsMMF.IsLoaded()) { + Timer::SetCurrentTime(); + uint32 starttime = Timer::GetCurrentTime(); + while ((!ItemsMMF.IsLoaded()) && ((Timer::GetCurrentTime() - starttime) < 300000)) { + Sleep(10); + Timer::SetCurrentTime(); + } + if (!ItemsMMF.IsLoaded()) { + cout << "Error: EMuShareMem: DLLLoadItems: !ItemsMMF.IsLoaded() (timeout)" << endl; + return false; + } + } + MMFItemsData = (const MMFItems_Struct*) ItemsMMF.GetHandle(); + if (!MMFItemsData) { + cout << "Error: EMuShareMem: DLLLoadItems: !MMFItemsData (CanWrite=false)" << endl; + return false; + } + *iMaxItemID = MMFItemsData->MaxItemID; + *iItemCount = MMFItemsData->ItemCount; + + return true; + } + } else { + cout << "Error Loading Items: Items.cpp: pDLLLoadItems: Open() == false" << endl; + return false; + } + /* + + // use a callback so the DB functions are done in the main exe + // this way the DLL doesnt have to open a connection to mysql + if (!cbDBLoadItems(*iItemCount, *iMaxItemID)) { + cout << "Error: EMuShareMem: DLLLoadItems: !cbDBLoadItems" << endl; + return false; + } + + */ + + // use a callback so the DB functions are done in the main exe + // this way the DLL doesnt have to open a connection to mysql + if (!cbDBLoadItems(*iItemCount, *iMaxItemID)) { + cout << "Error: EMuShareMem: DLLLoadItems: !cbDBLoadItems" << endl; + return false; + } + + + //Now, Disable the write handle and get the read handle. + //do this for both item struct and serialization data + + MMFItemsData_Writable = 0; + ItemsMMF.SetLoaded(); + MMFItemsData = (const MMFItems_Struct*) ItemsMMF.GetHandle(); + if (!MMFItemsData) { + cout << "Error: EMuShareMem: DLLLoadItems: !MMFItemsData (CanWrite=true)" << endl; + return false; + } + + return true; +}; + +DLLFUNC const Item_Struct* GetItem(uint32 id) { + if (MMFItemsData == 0 || (!ItemsMMF.IsLoaded()) || id > MMF_EQMAX_ITEMS || MMFItemsData->ItemIndex[id] == 0xFFFF) + return 0; + return &MMFItemsData->Items[MMFItemsData->ItemIndex[id]]; +} + +DLLFUNC const Item_Struct* IterateItems(uint32* NextIndex) { + if (MMFItemsData == 0 || (!ItemsMMF.IsLoaded()) || (*NextIndex) > MMF_EQMAX_ITEMS) + return 0; + do { + if (MMFItemsData->ItemIndex[*NextIndex] != 0xFFFF) + return &MMFItemsData->Items[MMFItemsData->ItemIndex[(*NextIndex)++]]; + } while (++(*NextIndex) < MMF_EQMAX_ITEMS); + + return 0; +} diff --git a/EMuShareMem/Items.h b/EMuShareMem/Items.h new file mode 100644 index 000000000..d8ea3f51c --- /dev/null +++ b/EMuShareMem/Items.h @@ -0,0 +1,23 @@ +#include "../common/types.h" +#include "../common/eq_packet_structs.h" +#include "../common/EMuShareMem.h" + +// MMF_EQMAX_ITEMS: Make sure this is bigger than the highest item ID# +#ifndef MMF_EQMAX_ITEMS +#define MMF_EQMAX_ITEMS 300000 +#endif +// MMF_MEMMAX_ITEMS: Maxium number of items to load into memory. Make sure this is bigger +// than the total number of items in the server's database! + +struct MMFItems_Struct { + uint32 MaxItemID; + uint32 NextFreeIndex; + uint32 ItemCount; + uint32 ItemIndex[MMF_EQMAX_ITEMS+1]; + Item_Struct Items[0]; +}; + +//#define MMF_MAX_ITEMS_MEMSIZE sizeof(MMFItems_Struct) + 256 + + + diff --git a/EMuShareMem/Loot.cpp b/EMuShareMem/Loot.cpp new file mode 100644 index 000000000..8976c90cd --- /dev/null +++ b/EMuShareMem/Loot.cpp @@ -0,0 +1,214 @@ +#include "../common/debug.h" +#include +#include +using namespace std; +#include "Loot.h" +#include "../common/timer.h" +#include "MMF.h" + +MMF LootMMF; +const MMFLoot_Struct* MMFLootData = 0; +MMFLoot_Struct* MMFLootData_Writable = 0; +uint32* LootTable; +uint32* LootDrop; + +#ifdef _WINDOWS + #define exportfunc extern "C" __declspec(dllexport) +#else + #define exportfunc extern "C" +#endif + +exportfunc const LootTable_Struct* GetLootTable(uint32 id) { + return pGetLootTable(id); +}; +exportfunc const LootDrop_Struct* GetLootDrop(uint32 id) { + return pGetLootDrop(id); +}; + +exportfunc bool AddLootTable(uint32 id, const LootTable_Struct* lts) { + return pAddLootTable(id, lts); +}; +exportfunc bool AddLootDrop(uint32 id, const LootDrop_Struct* lds) { + return pAddLootDrop(id, lds); +}; + +exportfunc bool DLLLoadLoot(CALLBACK_DBLoadLoot cbDBLoadLoot, + uint32 iLootTableStructsize, uint32 iLootTableCount, uint32 iMaxLootTable, + uint32 iLootTableEntryStructsize, uint32 iLootTableEntryCount, + uint32 iLootDropStructsize, uint32 iLootDropCount, uint32 iMaxLootDrop, + uint32 iLootDropEntryStructsize, uint32 iLootDropEntryCount + ) { + return pDLLLoadLoot(cbDBLoadLoot, + iLootTableStructsize, iLootTableCount, iMaxLootTable, + iLootTableEntryStructsize, iLootTableEntryCount, + iLootDropStructsize, iLootDropCount, iMaxLootDrop, + iLootDropEntryStructsize, iLootDropEntryCount); +}; + + +bool pAddLootTable(uint32 id, const LootTable_Struct* lts) { + if (!MMFLootData_Writable) + return false; + if (id > MMFLootData_Writable->MaxLootTableID) + return false; + if (!LootTable || LootTable[id] != 0) + return false; + + uint32 tmp = sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * lts->NumEntries); + if (MMFLootData_Writable->dataindex + tmp >= MMFLootData_Writable->datamax) + return false; + LootTable[id] = MMFLootData_Writable->dataindex; + memcpy(&MMFLootData_Writable->data[MMFLootData_Writable->dataindex], lts, tmp); + MMFLootData_Writable->dataindex += tmp; + + return true; +} + +bool pAddLootDrop(uint32 id, const LootDrop_Struct* lds) { + if (!MMFLootData_Writable) + return false; + if (id > MMFLootData_Writable->MaxLootDropID) + return false; + if (!LootDrop || LootDrop[id] != 0) + return false; + + uint32 tmp = sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * lds->NumEntries); + if (MMFLootData_Writable->dataindex + tmp >= MMFLootData_Writable->datamax) + return false; + LootDrop[id] = MMFLootData_Writable->dataindex; + memcpy(&MMFLootData_Writable->data[MMFLootData_Writable->dataindex], lds, tmp); + MMFLootData_Writable->dataindex += tmp; + + return true; +} + +bool pDLLLoadLoot(CALLBACK_DBLoadLoot cbDBLoadLoot, + uint32 iLootTableStructsize, uint32 iLootTableCount, uint32 iMaxLootTable, + uint32 iLootTableEntryStructsize, uint32 iLootTableEntryCount, + uint32 iLootDropStructsize, uint32 iLootDropCount, uint32 iMaxLootDrop, + uint32 iLootDropEntryStructsize, uint32 iLootDropEntryCount + ) { +#if 0 +cout << "iLootTableCount: " << iLootTableCount << endl; +cout << "iMaxLootTable: " << iMaxLootTable << endl; +cout << "iLootTableEntryCount: " << iLootTableEntryCount << endl; +cout << "iLootDropCount: " << iLootDropCount << endl; +cout << "iMaxLootDrop: " << iMaxLootDrop << endl; +cout << "iLootDropEntryCount: " << iLootDropEntryCount << endl; +#endif + if (iLootTableStructsize != sizeof(LootTable_Struct)) { + cout << "Error: EMuShareMem: DLLLoadLoot: iLootTableStructsize != sizeof(LootTable_Struct)" << endl; + cout << "Item_Struct has changed, EMuShareMem.dll needs to be recompiled." << endl; + return false; + } + if (iLootTableEntryStructsize != sizeof(LootTableEntries_Struct)) { + cout << "Error: EMuShareMem: DLLLoadLoot: iLootTableEntryStructsize != sizeof(LootTableEntries_Struct)" << endl; + cout << "Item_Struct has changed, EMuShareMem.dll needs to be recompiled." << endl; + return false; + } + if (iLootDropStructsize != sizeof(LootDrop_Struct)) { + cout << "Error: EMuShareMem: DLLLoadLoot: iLootDropStructsize != sizeof(LootDrop_Struct)" << endl; + cout << "Item_Struct has changed, EMuShareMem.dll needs to be recompiled." << endl; + return false; + } + if (iLootDropEntryStructsize != sizeof(LootDropEntries_Struct)) { + cout << "Error: EMuShareMem: DLLLoadLoot: iLootDropEntryStructsize != sizeof(LootDropEntries_Struct)" << endl; + cout << "Item_Struct has changed, EMuShareMem.dll needs to be recompiled." << endl; + return false; + } + + uint32 tmpMemSize = sizeof(MMFLoot_Struct) + 256 + + (sizeof(uint32) * (iMaxLootTable+1)) + + (sizeof(LootTable_Struct) * iLootTableCount) + (sizeof(LootTableEntries_Struct) * iLootTableEntryCount) + + (sizeof(uint32) * (iMaxLootDrop+1)) + + (sizeof(LootDrop_Struct) * iLootDropCount) + (sizeof(LootDropEntries_Struct) * iLootDropEntryCount) + ; + if (LootMMF.Open("EQEMuLoot", tmpMemSize)) { + if (LootMMF.CanWrite()) { + MMFLootData_Writable = (MMFLoot_Struct*) LootMMF.GetWriteableHandle(); + if (!MMFLootData_Writable) { + cout << "Error: EMuShareMem: DLLLoadLoot: !MMFLootData_Writable" << endl; + return false; + } + + memset(MMFLootData_Writable, 0, tmpMemSize); + MMFLootData_Writable->LootTableCount = iLootTableCount; + MMFLootData_Writable->MaxLootTableID = iMaxLootTable; + MMFLootData_Writable->LootDropCount = iLootDropCount; + MMFLootData_Writable->MaxLootDropID = iMaxLootDrop; + MMFLootData_Writable->datamax = tmpMemSize - sizeof(MMFLoot_Struct); + + MMFLootData_Writable->dataindex = 0; + MMFLootData_Writable->LootTableOffset = MMFLootData_Writable->dataindex; + MMFLootData_Writable->dataindex += (sizeof(uint32) * (iMaxLootTable+1)); + MMFLootData_Writable->LootDropOffset = MMFLootData_Writable->dataindex; + MMFLootData_Writable->dataindex += (sizeof(uint32) * (iMaxLootDrop+1)); + + LootTable = (uint32*) &MMFLootData_Writable->data[MMFLootData_Writable->LootTableOffset]; + LootDrop = (uint32*) &MMFLootData_Writable->data[MMFLootData_Writable->LootDropOffset]; + + // use a callback so the DB functions are done in the main exe + // this way the DLL doesnt have to open a connection to mysql + if (!cbDBLoadLoot()) { + cout << "Error: EMuShareMem: DLLLoadLoot: !cbDBLoadLoot" << endl; + return false; + } + + MMFLootData_Writable = 0; + LootMMF.SetLoaded(); + } + else { + if (!LootMMF.IsLoaded()) { + Timer::SetCurrentTime(); + uint32 starttime = Timer::GetCurrentTime(); + while ((!LootMMF.IsLoaded()) && ((Timer::GetCurrentTime() - starttime) < 300000)) { + Sleep(10); + Timer::SetCurrentTime(); + } + if (!LootMMF.IsLoaded()) { + cout << "Error: EMuShareMem: DLLLoadLoot: !LootMMF.IsLoaded() (timeout)" << endl; + return false; + } + } + } + } + else { + cout << "Error Loading Loot: Loot.cpp: pDLLLoadLoot: Open() == false" << endl; + return false; + } + MMFLootData = (const MMFLoot_Struct*) LootMMF.GetHandle(); + if (!MMFLootData) { + cout << "Error: EMuShareMem: DLLLoadLoot: !MMFLootData" << endl; + MMFLootData = 0; + return false; + } + if (MMFLootData->LootTableCount != iLootTableCount + || MMFLootData->MaxLootTableID != iMaxLootTable + || MMFLootData->LootDropCount != iLootDropCount + || MMFLootData->MaxLootDropID != iMaxLootDrop) { + cout << "Error: EMuShareMem: DLLLoadLoot: Count/Max mismatch" << endl; + MMFLootData = 0; + return false; + } + LootTable = (uint32*) &MMFLootData->data[MMFLootData->LootTableOffset]; + LootDrop = (uint32*) &MMFLootData->data[MMFLootData->LootDropOffset]; + return true; +}; + +const LootTable_Struct* pGetLootTable(uint32 id) { + if (MMFLootData == 0 || !LootMMF.IsLoaded()) + return 0; + if (id > MMFLootData->MaxLootTableID || LootTable[id] == 0) + return 0; + return (LootTable_Struct*) &MMFLootData->data[LootTable[id]]; +} + +const LootDrop_Struct* pGetLootDrop(uint32 id) { + if (MMFLootData == 0 || !LootMMF.IsLoaded()) + return 0; + if (id > MMFLootData->MaxLootDropID || LootDrop[id] == 0) + return 0; + return (LootDrop_Struct*) &MMFLootData->data[LootDrop[id]]; +} + + diff --git a/EMuShareMem/Loot.h b/EMuShareMem/Loot.h new file mode 100644 index 000000000..9710565c4 --- /dev/null +++ b/EMuShareMem/Loot.h @@ -0,0 +1,29 @@ +#include "../common/types.h" +#include "../common/eq_packet_structs.h" +#include "../common/EMuShareMem.h" + +#pragma pack(1) +struct MMFLoot_Struct { + bool Loaded; + uint32 MaxLootTableID; + uint32 LootTableCount; + uint32 LootTableOffset; + uint32 MaxLootDropID; + uint32 LootDropCount; + uint32 LootDropOffset; + uint32 datamax; + uint32 dataindex; + uint8 data[0]; +}; +#pragma pack() + +bool pDLLLoadLoot(CALLBACK_DBLoadLoot cbDBLoadLoot, + uint32 iLootTableStructsize, uint32 iLootTableCount, uint32 iMaxLootTable, + uint32 iLootTableEntryStructsize, uint32 iLootTableEntryCount, + uint32 iLootDropStructsize, uint32 iLootDropCount, uint32 iMaxLootDrop, + uint32 iLootDropEntryStructsize, uint32 iLootDropEntryCount + ); +bool pAddLootTable(uint32 id, const LootTable_Struct* lts); +bool pAddLootDrop(uint32, const LootDrop_Struct* lds); +const LootTable_Struct* pGetLootTable(uint32 id); +const LootDrop_Struct* pGetLootDrop(uint32 id); diff --git a/EMuShareMem/MMF.cpp b/EMuShareMem/MMF.cpp new file mode 100644 index 000000000..1975604f0 --- /dev/null +++ b/EMuShareMem/MMF.cpp @@ -0,0 +1,353 @@ +// start mingw +#ifdef __MINGW32__ +#define __try +#define __finally +#endif +// end mingw + +#include "MMF.h" +#include +using namespace std; +#include +#include +#include +#include +#ifdef _WINDOWS + #define snprintf _snprintf + #define vsnprintf _vsnprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#else + #include "MMFMutex.h" + #include "../common/unix.h" +#endif + +MMF::MMF() { + SharedMemory = 0; + pCanWrite = false; + #ifdef _WINDOWS + hMapObject = NULL; + lpvMem = 0; + #else + lpvMem = 0; + pMMFMutex = 0; + m_alloc = false; + #endif +} + +MMF::~MMF() { + Close(); +} + +bool MMF::Open(const char* iName, uint32 iSize) { + if (iSize < 1) { + cout << "Error Loading MMF: " << __FILE__ << ":" << __LINE__ << " OpenMMF: iSize < 1" << endl; + return false; + } + if (strlen(iName) < 2) { + cout << "Error Loading MMF: " << __FILE__ << ":" << __LINE__ << " OpenMMF: strlen(iName) < 2" << endl; + return false; + } + char MMFname[200]; + memset(MMFname, 0, sizeof(MMFname)); + snprintf(MMFname, sizeof(MMFname), "memfilemap_%s", iName); + uint32 tmpSize = sizeof(MMF_Struct) + iSize; + +#ifdef _WINDOWS + char MMFMutexName[200]; + memset(MMFMutexName, 0, sizeof(MMFMutexName)); + snprintf(MMFMutexName, sizeof(MMFMutexName), "MutexToProtectOpenMMF_%s", iName); + + HANDLE hMutex; + hMutex = CreateMutex( + NULL, // no security attributes + FALSE, // initially not owned + MMFMutexName); // name of mutex + + if (hMutex == NULL) { + cout << "Error Loading MMF: " << __FILE__ << ":" << __LINE__ << " OpenMMF: hMutex == Null" << endl; + return false; + } + + DWORD dwWaitResult; + // Request ownership of mutex. + dwWaitResult = WaitForSingleObject( + hMutex, // handle to mutex + 2000L); // two-second time-out interval + + if (dwWaitResult != WAIT_OBJECT_0) { + // Mutex not aquired, crap out + cout << "Error Loading MMF: " << __FILE__ << ":" << __LINE__ << " OpenMMF: dwWaitResult != WAIT_OBJECT_0" << endl; + return false; + } + + // Finally, ready to rock. + bool fInit = false; + __try { + hMapObject = CreateFileMapping( + INVALID_HANDLE_VALUE, // use paging file + NULL, // default security attributes + PAGE_READWRITE, // read/write access + 0, // size: high 32-bits + tmpSize, // size: low 32-bits + MMFname); // name of map object + if (hMapObject == NULL) { + cout << "Error Loading MMF: " << __FILE__ << ":" << __LINE__ << " OpenMMF: hMapObject == Null" << endl; + return false; + } + + // The first process to attach initializes memory. + + fInit = (bool) (GetLastError() != ERROR_ALREADY_EXISTS); + + // Get a pointer to the file-mapped shared memory. + + lpvMem = MapViewOfFile( + hMapObject, // object to map view of + FILE_MAP_WRITE, // read/write access + 0, // high offset: map from + 0, // low offset: beginning + 0); // default: map entire file + if (lpvMem == NULL) { + cout << "Error Loading MMF: " << __FILE__ << ":" << __LINE__ << " OpenMMF: lpvMem == Null" << endl; + Close(); + return false; + } + + SharedMemory = (MMF_Struct*) lpvMem; + // Initialize memory if this is the first process. + if (fInit) { + memset(lpvMem, 0, sizeof(MMF_Struct)); + pCanWrite = true; + SharedMemory->Loaded = false; + SharedMemory->datasize = iSize; + } + else { + pCanWrite = false; + if (SharedMemory->datasize != iSize) { + cout << "Error Loading MMF: " << __FILE__ << ":" << __LINE__ << " OpenMMF: SharedMemory->datasize != iSize" << endl; + Close(); + return false; + } + } + } // end of try block + + __finally { + // Clean up the Mutex stuff + if (!ReleaseMutex(hMutex)) { + cout << "Error Loading MMF: " << __FILE__ << ":" << __LINE__ << " OpenMMF: !ReleaseMutex(hMutex)" << endl; + Close(); + return false; + } + } + CloseHandle(hMutex); + + return true; +#else //else, NOT WINDOWS + int load_share; + //int max_share = 7; + key_t share_key; + switch (MMFname[16]) { + case 'I': load_share = 0; break; + case 'N': load_share = 1; break; + case 'D': load_share = 2; break; + case 'S': load_share = 3; break; + case 'F': load_share = 4; break; + case 'L': load_share = 5; break; + case 'M': load_share = 6; break; + case 'O': load_share = 7; break; + case 'Z': load_share = 8; break; + case 'K': load_share = 9; break; +#ifdef CATCH_CRASH + default: + cerr<<"Failed to load shared memory segment="<Loaded = false; + SharedMemory->datasize = iSize; + pMMFMutex->Release(this); + delete pMMFMutex; + return true; + } + } + else{ + cout<<"[Warning] Resize not possible"<datasize = iSize; + SharedMemory->Loaded = false; + pMMFMutex->Release(this); + delete pMMFMutex; + return true; + } else if (!lpvMem){ + //LogFile->write(EQEMuLog::Error, "Could not connect to shared memory and allocation of isolated memory failed."); + cout<<"Could not connect to shared memory and allocation of isolated memory failed."<Release(this); + delete pMMFMutex; + exit(1); + } + pCanWrite = false; + SharedMemory = (MMF_Struct*) lpvMem; + if (SharedMemory->datasize != iSize) { + cerr<<"SharedMemory->datasize("<datasize<<") != iSize("<Release(this); + exit(1); + } + pMMFMutex->Release(this); + delete pMMFMutex; + return true; + } + shmid_ds mem_users; + if ((shmctl(share_id, IPC_STAT, &mem_users)) != 0) { + if ((lpvMem = malloc(tmpSize))) { + // Success! + cout<<"[Warning] Could not attach to shared memory proceeding on isolated memory"<datasize = iSize; + SharedMemory->Loaded = false; + pMMFMutex->Release(this); + delete pMMFMutex; + return true; + } else { + //LogFile->write(EQEMuLog::Error, "Could not connect to shared memory and allocation of isolated memory failed."); + cout<<"Could not connect to shared memory and allocation of isolated memory failed."<Release(this); + delete pMMFMutex; + exit(1); + } + } + lpvMem = shmat(share_id, NULL,SHM_RDONLY); + pCanWrite = false; + SharedMemory = (MMF_Struct*) lpvMem; + //cerr << "lpvMem=" << (int)lpvMem << endl; + if (lpvMem==(void *)-1 || SharedMemory->datasize != iSize) { + cerr<<"SharedMemory->datasize("<datasize<<") != iSize("<Release(this); + exit(1); + } + pMMFMutex->Release(this); + delete pMMFMutex; + return true; + } + lpvMem = shmat(share_id, NULL, SHM_R|SHM_W); + memset(lpvMem, 0, sizeof(MMF_Struct)); + pCanWrite = true; + SharedMemory = (MMF_Struct*) lpvMem; + SharedMemory->Loaded = false; + SharedMemory->datasize = iSize; + //} + pMMFMutex->Release(this); + delete pMMFMutex; + return true; + +#endif //end NOT WINDOWS +} + +void MMF::Close() { + SharedMemory = 0; + pCanWrite = false; +#ifdef _WINDOWS + if (lpvMem) { + // Unmap shared memory from the process's address space. + UnmapViewOfFile(lpvMem); + lpvMem = 0; + } + if (hMapObject) { + // Close the process's handle to the file-mapping object. + CloseHandle(hMapObject); + hMapObject = NULL; + } +#else + if (lpvMem) { + if (m_alloc == true) + free(lpvMem); + else + if (shmdt(lpvMem) == -1) + //LogFile->write(EQEMuLog::Error, "Warning something odd happened freeing shared memory"); + cout<<"Warning something odd happened freeing shared memory"< +#else + //#include "MMFMutex.h" + class MMFMutex; +#endif + +class MMF { +public: + struct MMF_Struct { + bool Loaded; + uint32 datasize; + uint8 data[0]; + }; + + MMF(); + virtual ~MMF(); + + bool Open(const char* iName, uint32 iSize); + void Close(); + const void* GetHandle() { if (IsLoaded()) { return SharedMemory->data; } return 0; } + void* GetWriteableHandle() { if (!IsLoaded() && CanWrite()) { return SharedMemory->data; } return 0; } + + inline bool IsOpen() { return (bool) (SharedMemory != 0); } + inline bool IsLoaded() { if (SharedMemory) { return SharedMemory->Loaded; } return false; } + bool SetLoaded() { if (SharedMemory && CanWrite()) { SharedMemory->Loaded = true; return true; } return false; } + inline bool CanWrite() { if (SharedMemory) { return pCanWrite; } return false; } + +#ifndef WIN32 + bool m_alloc; +#endif +private: + bool pCanWrite; + MMF_Struct* SharedMemory; +#ifdef _WINDOWS + HANDLE hMapObject; + LPVOID lpvMem; +#else + void* lpvMem; + MMFMutex* pMMFMutex; +#endif +}; +#endif diff --git a/EMuShareMem/MMFMutex.cpp b/EMuShareMem/MMFMutex.cpp new file mode 100644 index 000000000..3c87d8dcc --- /dev/null +++ b/EMuShareMem/MMFMutex.cpp @@ -0,0 +1,126 @@ +#include "MMFMutex.h" + +#ifndef WIN32 + +MMFMutex::MMFMutex( int key ) + { + m_key = key; + + if( m_key == 0 ) + { + // initialize POSIX semaphore + sem_init( &m_semaphore, 0, 1); + } + else + { + // currently, this thread does not own the semaphore + m_owner = 0; + m_recursive_count = 0; + + // try to get an existing semaphore. the access permissions are "full access for everyone" + m_id = semget(m_key, 1, 0x1b6); + if( m_id == -1 ) + { + // it doesn't exist yet, try to create a new one + m_id = semget(m_key, 1, IPC_CREAT | 0x1b6); + if( m_id != -1 ) + { + // initialize it to 1 + semun data; + data.val = 1; + semctl(m_id, 0, SETVAL, data); + } + } + } +} + +MMFMutex::~MMFMutex() +{ + if( m_key == 0 ) + { + sem_destroy(&m_semaphore); + } + else + { + semctl(m_id, 0, IPC_RMID, 0); + } +} + +bool MMFMutex::Lock( uint32 dwTimeout ) + { + if( m_owner == pthread_self() ) + { + m_recursive_count++; + return true; + } + bool bUseTimeout = (dwTimeout != 0); + while(true) { + bool bGotSemaphore = false; + if( m_key == 0 ) + { + bGotSemaphore = (sem_trywait(&m_semaphore) == 0); + } + else + { + struct sembuf operations[1]; + operations[0].sem_num = 0; + operations[0].sem_op = -1; + operations[0].sem_flg = SEM_UNDO|IPC_NOWAIT; + bGotSemaphore = (semop(m_id, operations, 1) >= 0); + } + if( bGotSemaphore ) + { + m_owner = pthread_self(); + m_recursive_count = 1; + return true; + } + sleep(1); + if( bUseTimeout ) + { + if( dwTimeout > 1000 ) + dwTimeout -= 1000; + else + dwTimeout = 0; + + if( dwTimeout == 0 ) + return false; + } + } +} +void MMFMutex::Release(const MMF* pMMF) +{ + if( m_owner != pthread_self() && pMMF->m_alloc != true ) + { + //We're supposed to explode here with an assert + //assert(false); + } + else if ( pMMF->m_alloc == true ){ + // Just do it nothing is useing but us + return; + } + else if( m_recursive_count > 1 ) + { + m_recursive_count--; + } + else + { + if( m_key == 0 ) + { + sem_post(&m_semaphore); + } + + else + { + struct sembuf operations[1]; + operations[0].sem_num = 0; + operations[0].sem_op = 1; + operations[0].sem_flg = SEM_UNDO; + semop(m_id, operations, 1); + } + m_recursive_count = 0; + m_owner = 0; + } +} +#endif //!WIN32 + + diff --git a/EMuShareMem/MMFMutex.h b/EMuShareMem/MMFMutex.h new file mode 100644 index 000000000..d79230c8b --- /dev/null +++ b/EMuShareMem/MMFMutex.h @@ -0,0 +1,43 @@ +#ifndef MMFMUTEX_H +#define MMFMUTEX_H +#ifndef WIN32 +#include // moved before sys/shm.h for freeBSD +#include +#include +#include +#include +#include +#include +#include +#include "../common/types.h" + +#include "MMF.h" + +// the manuals say you have to define this struct your self. +#if !defined FREEBSD || defined __NetBSD__ // for BSDs +union semun +{ + int val; + struct semid_ds* buf; + unsigned short int *array; + struct seminfo *__buf; +}; +#endif // for freeBSD + +class MMFMutex + { + public: + MMFMutex(int iIndex); + virtual ~MMFMutex(); + bool Lock( uint32 dwTimeout = 0 ); + void Release(const MMF*); + + protected: + int m_id; + key_t m_key; + pthread_t m_owner; + sem_t m_semaphore; + int m_recursive_count; +}; +#endif //!WIN32 +#endif diff --git a/EMuShareMem/NPCFactionLists.cpp b/EMuShareMem/NPCFactionLists.cpp new file mode 100644 index 000000000..9e788bf0a --- /dev/null +++ b/EMuShareMem/NPCFactionLists.cpp @@ -0,0 +1,178 @@ +#include "../common/debug.h" + +#ifdef _WINDOWS +#include +#else +#include "../common/unix.h" +#endif + +#include +#include +using namespace std; +#include "NPCFactionLists.h" +#include "../common/timer.h" +#include "MMF.h" + +MMF NPCFactionListsMMF; +const MMFNPCFactionLists_Struct* MMFNPCFactionListsData = 0; +MMFNPCFactionLists_Struct* MMFNPCFactionListsData_Writable = 0; + +#ifdef _WINDOWS +extern "C" __declspec(dllexport) const NPCFactionList* GetNPCFactionList(uint32 id) { + return pGetNPCFactionList(id); +}; + +extern "C" __declspec(dllexport) bool AddNPCFactionList(uint32 id, const NPCFactionList* nfl) { + return pAddNPCFactionList(id, nfl); +}; + +extern "C" __declspec(dllexport) bool DLLLoadNPCFactionLists(CALLBACK_DBLoadNPCFactionLists cbDBLoadNPCFactionLists, uint32 iNPCFactionListStructSize, int32* iNPCFactionListsCount, uint32* iMaxNPCFactionListID, uint8 iMaxNPCFactions) { + return pDLLLoadNPCFactionLists(cbDBLoadNPCFactionLists, iNPCFactionListStructSize, iNPCFactionListsCount, iMaxNPCFactionListID, iMaxNPCFactions); +}; + +extern "C" __declspec(dllexport) bool SetNPCFaction(uint32 id, uint32* factionid, int32* factionvalue, int8 *factionnpcvalue, uint8 *factiontemp) { + return pSetNPCFaction(id, factionid, factionvalue, factionnpcvalue, factiontemp); +} +#else +extern "C" const NPCFactionList* GetNPCFactionList(uint32 id) { + return pGetNPCFactionList(id); +}; + +extern "C" bool AddNPCFactionList(uint32 id, const NPCFactionList* nfl) { + return pAddNPCFactionList(id, nfl); +}; + +extern "C" bool DLLLoadNPCFactionLists(CALLBACK_DBLoadNPCFactionLists cbDBLoadNPCFactionLists, uint32 iNPCFactionListStructSize, int32* iNPCFactionListsCount, uint32* iMaxNPCFactionListID, uint8 iMaxNPCFactions) { + return pDLLLoadNPCFactionLists(cbDBLoadNPCFactionLists, iNPCFactionListStructSize, iNPCFactionListsCount, iMaxNPCFactionListID, iMaxNPCFactions); +}; + +extern "C" bool SetNPCFaction(uint32 id, uint32* factionid, int32* factionvalue, int8 *factionnpcvalue, uint8 *factiontemp) { + return pSetNPCFaction(id, factionid, factionvalue, factionnpcvalue, factiontemp); +} +#endif + +bool pAddNPCFactionList(uint32 id, const NPCFactionList* nfl) { + if (!MMFNPCFactionListsData_Writable){ + if (EQDEBUG>=1) cout<<"[Debug] !MMFNPCFactionListsData_Writable"< MMF_MAX_NPCFactionList_ID || MMFNPCFactionListsData_Writable->NextFreeIndex >= MMFNPCFactionListsData_Writable->NPCFactionListCount){ + if (EQDEBUG>=1) cout<<"[Debug] id > MMF_MAX_NPCFactionList_ID || MMFNPCFactionListsData_Writable->NextFreeIndex >= MMFNPCFactionListsData_Writable->NPCFactionListCount"<NPCFactionListIndex[id] != 0xFFFFFFFF){ + if (EQDEBUG>=1) cout<<"[Debug] MMFNPCFactionListsData_Writable->NPCFactionListIndex[id] != 0xFFFFFFFF"<NPCFactionListIndex[id] = MMFNPCFactionListsData_Writable->NextFreeIndex++; + memcpy(&MMFNPCFactionListsData_Writable->NPCFactionLists[MMFNPCFactionListsData_Writable->NPCFactionListIndex[id]], nfl, sizeof(NPCFactionList)); + + return true; +} + +bool pSetNPCFaction(uint32 id, uint32* factionid, int32* factionvalue, int8 *factionnpcvalue, uint8 *factiontemp) { + if (!MMFNPCFactionListsData_Writable) { + if(EQDEBUG>=1) cout<<"[Debug] !MMFNPCFactionListsData_Writable"< MMF_MAX_NPCFactionList_ID) { + if(EQDEBUG>=1) cout<<"[Debug] id > MMF_MAX_NPCFactionList_ID"<NPCFactionListIndex[id] == 0xFFFFFFFF) { + if(EQDEBUG>=1) cout<<"[Debug] MMFNPCFactionListsData_Writable->NPCFactionListIndex[id="<NPCFactionLists[MMFNPCFactionListsData_Writable->NPCFactionListIndex[id]].factionid[i] = factionid[i]; + MMFNPCFactionListsData_Writable->NPCFactionLists[MMFNPCFactionListsData_Writable->NPCFactionListIndex[id]].factionvalue[i] = factionvalue[i]; + MMFNPCFactionListsData_Writable->NPCFactionLists[MMFNPCFactionListsData_Writable->NPCFactionListIndex[id]].factionnpcvalue[i] = factionnpcvalue[i]; + MMFNPCFactionListsData_Writable->NPCFactionLists[MMFNPCFactionListsData_Writable->NPCFactionListIndex[id]].factiontemp[i] = factiontemp[i]; + } + return true; +} + +bool pDLLLoadNPCFactionLists(CALLBACK_DBLoadNPCFactionLists cbDBLoadNPCFactionLists, uint32 iNPCFactionListStructSize, int32* iNPCFactionListsCount, uint32* iMaxNPCFactionListID, uint8 iMaxNPCFactions) { + if (iNPCFactionListStructSize != sizeof(NPCFactionList)) { + cout << "Error: EMuShareMem: DLLLoadNPCFactionLists: iNPCFactionListStructSize != sizeof(NPCFactionList)" << endl; + cout << "NPCFactionList struct has changed, EMuShareMem.dll needs to be recompiled." << endl; + return false; + } + if (iMaxNPCFactions != MAX_NPC_FACTIONS) { + cout << "Error: EMuShareMem: DLLLoadNPCFactionLists: iMaxNPCFactions != MAX_NPC_FACTIONS" << endl; + cout << "NPCFactionList struct has changed, EMuShareMem.dll needs to be recompiled." << endl; + return false; + } + if (*iMaxNPCFactionListID > MMF_MAX_NPCFactionList_ID) { + cout << "Error: EMuShareMem: DLLLoadNPCFactionLists: iMaxNPCFactions > MMF_MAX_NPCFactionList_ID" << endl; + cout << "You need to increase the define in NPCFactionList.h." << endl; + return false; + } + uint32 tmpMemSize = sizeof(MMFNPCFactionLists_Struct) + 256 + (sizeof(NPCFactionList) * (*iNPCFactionListsCount)); + if (NPCFactionListsMMF.Open("EQEMuFactionLists", tmpMemSize)) { +// MMFNPCFactionListsData = (const MMFNPCFactionLists_Struct*) NPCFactionListsMMF.GetHandle(); + if (NPCFactionListsMMF.CanWrite()) { + MMFNPCFactionListsData_Writable = (MMFNPCFactionLists_Struct*) NPCFactionListsMMF.GetWriteableHandle(); + if (!MMFNPCFactionListsData_Writable) { + cout << "Error: EMuShareMem: DLLLoadNPCFactionLists: !MMFNPCFactionListsData_Writable" << endl; + return false; + } + + memset(MMFNPCFactionListsData_Writable, 0, tmpMemSize); + for(int i=0; iNPCFactionListIndex[i] = 0xFFFFFFFF; + MMFNPCFactionListsData_Writable->MaxNPCFactionListID = *iMaxNPCFactionListID; + MMFNPCFactionListsData_Writable->NPCFactionListCount = *iNPCFactionListsCount; + // use a callback so the DB functions are done in the main exe + // this way the DLL doesnt have to open a connection to mysql + if (!cbDBLoadNPCFactionLists(MMFNPCFactionListsData_Writable->NPCFactionListCount, MMFNPCFactionListsData_Writable->MaxNPCFactionListID)) { + cout << "Error: EMuShareMem: DLLLoadNPCFactionLists: !cbDBLoadNPCFactionLists" << endl; + return false; + } + + MMFNPCFactionListsData_Writable = 0; + NPCFactionListsMMF.SetLoaded(); + MMFNPCFactionListsData = (const MMFNPCFactionLists_Struct*) NPCFactionListsMMF.GetHandle(); + if (!MMFNPCFactionListsData) { + cout << "Error: EMuShareMem: DLLLoadNPCFactionLists: !MMFNPCFactionListsData (CanWrite=true)" << endl; + return false; + } + return true; + } + else { + if (!NPCFactionListsMMF.IsLoaded()) { + Timer::SetCurrentTime(); + uint32 starttime = Timer::GetCurrentTime(); + while ((!NPCFactionListsMMF.IsLoaded()) && ((Timer::GetCurrentTime() - starttime) < 300000)) { + Sleep(100); + Timer::SetCurrentTime(); + } + if (!NPCFactionListsMMF.IsLoaded()) { + cout << "Error: EMuShareMem: DLLLoadNPCFactionLists: !NPCFactionListsMMF.IsLoaded() (timeout)" << endl; + return false; + } + } + MMFNPCFactionListsData = (const MMFNPCFactionLists_Struct*) NPCFactionListsMMF.GetHandle(); + if (!MMFNPCFactionListsData) { + cout << "Error: EMuShareMem: DLLLoadNPCFactionLists: !MMFNPCFactionListsData (CanWrite=false)" << endl; + return false; + } + *iMaxNPCFactionListID = MMFNPCFactionListsData->MaxNPCFactionListID; + *iNPCFactionListsCount = MMFNPCFactionListsData->NPCFactionListCount; + return true; + } + } + else { + cout << "Error Loading NPCFactionLists: NPCFactionLists.cpp: pDLLLoadNPCFactionLists: Open() == false" << endl; + return false; + } + return false; +}; + +const NPCFactionList* pGetNPCFactionList(uint32 id) { + if (MMFNPCFactionListsData == 0 || (!NPCFactionListsMMF.IsLoaded()) || id > MMF_MAX_NPCFactionList_ID || MMFNPCFactionListsData->NPCFactionListIndex[id] == 0xFFFFFFFF) + return 0; + return &MMFNPCFactionListsData->NPCFactionLists[MMFNPCFactionListsData->NPCFactionListIndex[id]]; +} diff --git a/EMuShareMem/NPCFactionLists.h b/EMuShareMem/NPCFactionLists.h new file mode 100644 index 000000000..df1d222d8 --- /dev/null +++ b/EMuShareMem/NPCFactionLists.h @@ -0,0 +1,22 @@ +#include "../common/types.h" +#include "../zone/features.h" +#include "../zone/faction.h" +#include "../common/EMuShareMem.h" + +// MMF_MAX_NPCFactionList_ID: Make sure this is bigger than the highest NPCFactionList ID# +#ifndef MMF_MAX_NPCFactionList_ID +#define MMF_MAX_NPCFactionList_ID 50000 +#endif + +struct MMFNPCFactionLists_Struct { + uint32 MaxNPCFactionListID; + uint32 NextFreeIndex; + uint32 NPCFactionListCount; + uint32 NPCFactionListIndex[MMF_MAX_NPCFactionList_ID+1]; + NPCFactionList NPCFactionLists[0]; +}; + +bool pDLLLoadNPCFactionLists(CALLBACK_DBLoadNPCFactionLists cbDBLoadNPCFactionLists, uint32 iNPCFactionListStructSize, int32* iNPCFactionListCount, uint32* iMaxNPCFactionListID, uint8 iMaxNPCFactions); +bool pAddNPCFactionList(uint32 id, const NPCFactionList* nfl); +bool pSetNPCFaction(uint32 id, uint32* factionid, int32* factionvalue, int8 *factionnpcvalue, uint8 *factiontemp); +const NPCFactionList* pGetNPCFactionList(uint32 id); diff --git a/EMuShareMem/NPCTypes.cpp b/EMuShareMem/NPCTypes.cpp new file mode 100644 index 000000000..443cfef89 --- /dev/null +++ b/EMuShareMem/NPCTypes.cpp @@ -0,0 +1,137 @@ +#include "../common/debug.h" + +#ifdef _WINDOWS +#include +#else +#include "../common/unix.h" +#endif + +#include +#include +using namespace std; +#include "NPCTypes.h" +#include "../common/timer.h" +#include "MMF.h" + +MMF NPCTypesMMF; +const MMFNPCTypes_Struct* MMFNPCTypesData = 0; +MMFNPCTypes_Struct* MMFNPCTypesData_Writable = 0; + +#ifdef _WINDOWS +extern "C" __declspec(dllexport) const NPCType* GetNPCType(uint32 id) { + return pGetNPCType(id); +}; + +extern "C" __declspec(dllexport) bool AddNPCType(uint32 id, const NPCType* npctype) { + return pAddNPCType(id, npctype); +}; + +/*extern "C" __declspec(dllexport) bool DLLLoadNPCTypes(CALLBACK_DBLoadNPCTypes cbDBLoadNPCTypes, uint32 iNPCTypeStructSize, int32* iNPCTypesCount, uint32* iMaxNPCTypeID) { + return pDLLLoadNPCTypes(cbDBLoadNPCTypes, iNPCTypeStructSize, iNPCTypesCount, iMaxNPCTypeID); +};*/ + +#else +extern "C" const NPCType* GetNPCType(uint32 id) { + return pGetNPCType(id); +}; + +extern "C" bool AddNPCType(uint32 id, const NPCType* npctype) { + return pAddNPCType(id, npctype); +}; + +extern "C" bool DLLLoadNPCTypes(CALLBACK_DBLoadNPCTypes cbDBLoadNPCTypes, uint32 iNPCTypeStructSize, int32* iNPCTypesCount, uint32* iMaxNPCTypeID) { + return pDLLLoadNPCTypes(cbDBLoadNPCTypes, iNPCTypeStructSize, iNPCTypesCount, iMaxNPCTypeID); +}; +#endif + +bool pAddNPCType(uint32 id, const NPCType* npctype) { + if (!MMFNPCTypesData_Writable) + return false; + if (id > MMF_MAX_NPCTYPE_ID || MMFNPCTypesData_Writable->NextFreeIndex >= MMFNPCTypesData_Writable->NPCTypeCount) + return false; + if (MMFNPCTypesData_Writable->NPCTypeIndex[id] != 0xFFFFFFFF) + return false; + + MMFNPCTypesData_Writable->NPCTypeIndex[id] = MMFNPCTypesData_Writable->NextFreeIndex++; + memcpy(&MMFNPCTypesData_Writable->NPCTypes[MMFNPCTypesData_Writable->NPCTypeIndex[id]], npctype, sizeof(NPCType)); + + return true; +} + +/*bool pDLLLoadNPCTypes(CALLBACK_DBLoadNPCTypes cbDBLoadNPCTypes, uint32 iNPCTypeStructSize, int32* iNPCTypesCount, uint32* iMaxNPCTypeID) { + if (iNPCTypeStructSize != sizeof(NPCType)) { + cout << "Error: EMuShareMem: DLLLoadNPCTypes: iNPCTypeStructSize != sizeof(NPCType)" << endl; + cout << "NPCType struct has changed, EMuShareMem.dll needs to be recompiled." << endl; + return false; + } + if (*iMaxNPCTypeID > MMF_MAX_NPCTYPE_ID) { + cout << "Error: EMuShareMem: pDLLLoadNPCTypes: iMaxNPCTypeID > MMF_MAX_NPCTYPE_ID" << endl; + cout << "You need to increase the define in NPCTypes.h." << endl; + return false; + } + uint32 tmpMemSize = sizeof(MMFNPCTypes_Struct) + 256 + (sizeof(NPCType) * (*iNPCTypesCount)); + if (NPCTypesMMF.Open("EQEMuNPCTypes", tmpMemSize)) { +// MMFNPCTypesData = (const MMFNPCTypes_Struct*) NPCTypesMMF.GetHandle(); + if (NPCTypesMMF.CanWrite()) { + MMFNPCTypesData_Writable = (MMFNPCTypes_Struct*) NPCTypesMMF.GetWriteableHandle(); + if (!MMFNPCTypesData_Writable) { + cout << "Error: EMuShareMem: DLLLoadNPCTypes: !MMFNPCTypesData_Writable" << endl; + return false; + } + + memset(MMFNPCTypesData_Writable, 0, tmpMemSize); + for(int i=0; iNPCTypeIndex[i] = 0xFFFFFFFF; + MMFNPCTypesData_Writable->MaxNPCTypeID = *iMaxNPCTypeID; + MMFNPCTypesData_Writable->NPCTypeCount = *iNPCTypesCount; + // use a callback so the DB functions are done in the main exe + // this way the DLL doesnt have to open a connection to mysql + if (!cbDBLoadNPCTypes(MMFNPCTypesData_Writable->NPCTypeCount, MMFNPCTypesData_Writable->MaxNPCTypeID)) { + cout << "Error: EMuShareMem: DLLLoadNPCTypes: !cbDBLoadNPCTypes" << endl; + return false; + } + + MMFNPCTypesData_Writable = 0; + NPCTypesMMF.SetLoaded(); + MMFNPCTypesData = (const MMFNPCTypes_Struct*) NPCTypesMMF.GetHandle(); + if (!MMFNPCTypesData) { + cout << "Error: EMuShareMem: DLLLoadNPCTypes: !MMFNPCTypesData (CanWrite=true)" << endl; + return false; + } + return true; + } + else { + if (!NPCTypesMMF.IsLoaded()) { + Timer::SetCurrentTime(); + uint32 starttime = Timer::GetCurrentTime(); + while ((!NPCTypesMMF.IsLoaded()) && ((Timer::GetCurrentTime() - starttime) < 300000)) { + Sleep(100); + Timer::SetCurrentTime(); + } + if (!NPCTypesMMF.IsLoaded()) { + cout << "Error: EMuShareMem: DLLLoadNPCTypes: !NPCTypesMMF.IsLoaded() (timeout)" << endl; + return false; + } + } + MMFNPCTypesData = (const MMFNPCTypes_Struct*) NPCTypesMMF.GetHandle(); + if (!MMFNPCTypesData) { + cout << "Error: EMuShareMem: DLLLoadNPCTypes: !MMFNPCTypesData (CanWrite=false)" << endl; + return false; + } + *iMaxNPCTypeID = MMFNPCTypesData->MaxNPCTypeID; + *iNPCTypesCount = MMFNPCTypesData->NPCTypeCount; + return true; + } + } + else { + cout << "Error Loading NPCTypes: NPCTypes.cpp: pDLLLoadNPCTypes: Open() == false" << endl; + return false; + } + return false; +};*/ + +const NPCType* pGetNPCType(uint32 id) { + if (MMFNPCTypesData == 0 || (!NPCTypesMMF.IsLoaded()) || id > MMF_MAX_NPCTYPE_ID || MMFNPCTypesData->NPCTypeIndex[id] == 0xFFFFFFFF) + return 0; + return &MMFNPCTypesData->NPCTypes[MMFNPCTypesData->NPCTypeIndex[id]]; +} diff --git a/EMuShareMem/NPCTypes.h b/EMuShareMem/NPCTypes.h new file mode 100644 index 000000000..39fb1d4e5 --- /dev/null +++ b/EMuShareMem/NPCTypes.h @@ -0,0 +1,18 @@ +#include "../common/types.h" +#include "../zone/zonedump.h" +#include "../common/EMuShareMem.h" + +// MMF_MAX_NPCTYPE_ID: Make sure this is bigger than the highest NPCType ID# +#define MMF_MAX_NPCTYPE_ID 400000 + +struct MMFNPCTypes_Struct { + uint32 MaxNPCTypeID; + uint32 NextFreeIndex; + uint32 NPCTypeCount; + uint32 NPCTypeIndex[MMF_MAX_NPCTYPE_ID+1]; + NPCType NPCTypes[0]; +}; + +//bool pDLLLoadNPCTypes(CALLBACK_DBLoadNPCTypes cbDBLoadNPCTypes, uint32 iNPCTypeStructSize, int32* iNPCTypesCount, uint32* iMaxNPCTypeID); +bool pAddNPCType(uint32 id, const NPCType* npctype); +const NPCType* pGetNPCType(uint32 id); diff --git a/EMuShareMem/Opcodes.cpp b/EMuShareMem/Opcodes.cpp new file mode 100644 index 000000000..2dba6b616 --- /dev/null +++ b/EMuShareMem/Opcodes.cpp @@ -0,0 +1,140 @@ +#include "../common/debug.h" + +#ifdef _WINDOWS +#include +#else +#include "../common/unix.h" +#endif + +#include +#include +using namespace std; +#include "Opcodes.h" +#include "../common/timer.h" +#include "MMF.h" + +MMF OpcodesMMF; +const MMFOpcodes_Struct* MMFOpcodesData = 0; +MMFOpcodes_Struct* MMFOpcodesData_Writable = 0; +const uint16 *MMFOpcodesData_emu_to_eq = NULL; +uint16 *MMFOpcodesData_emu_to_eq_write = NULL; + +//we choose to store all opcodes as 16 bits, so if they are a different +//size in emu, they are gunna get casted to 16 bits... prolly will never +//be a problem, but I figured it was noteworthy + + +DLLFUNC uint16 GetEQOpcode(uint16 emu_op) { + if (MMFOpcodesData == 0 || (!OpcodesMMF.IsLoaded()) || emu_op >= MMFOpcodesData->EmuOpcodeCount ) + return 0; + return MMFOpcodesData_emu_to_eq[emu_op]; +} + +DLLFUNC uint16 GetEmuOpcode(uint16 eq_op) { + if (MMFOpcodesData == 0 || (!OpcodesMMF.IsLoaded()) || eq_op >= MMFOpcodesData->EQOpcodeCount ) + return 0; + return MMFOpcodesData->eq_to_emu[eq_op]; +} + +DLLFUNC bool SetOpcodePair(uint16 emu_op, uint16 eq_op) { + if (!MMFOpcodesData_Writable || !MMFOpcodesData_emu_to_eq_write) + return false; + if (emu_op >= MMFOpcodesData_Writable->EmuOpcodeCount || eq_op >= MMFOpcodesData_Writable->EQOpcodeCount) + return false; + + MMFOpcodesData_emu_to_eq_write[emu_op] = eq_op; + MMFOpcodesData_Writable->eq_to_emu[eq_op] = emu_op; + + return true; +} + +DLLFUNC void ClearEQOpcodes() { + if (!MMFOpcodesData_Writable) + return; + + memset(MMFOpcodesData_Writable->eq_to_emu, 0, sizeof(uint16)*MMFOpcodesData->EQOpcodeCount); + +} + +DLLFUNC bool DLLLoadOpcodes(CALLBACK_DBLoadOpcodes cb, uint32 opsize, uint32 eq_count, uint32 emu_count, const char *filename) { + if(opsize != sizeof(uint16)) { + cout << "Error: EMuShareMem: DLLLoadOpcodes: opsize != sizeof(uint16)" << endl; + cout << "Opcode size has changed, EMuShareMem.dll needs to be recompiled." << endl; + return false; + } + uint32 tmpMemSize = sizeof(MMFOpcodes_Struct) + opsize * (eq_count+emu_count); + if (OpcodesMMF.Open("EQEMuOpcodes", tmpMemSize)) { + if (OpcodesMMF.CanWrite()) { + MMFOpcodesData_Writable = (MMFOpcodes_Struct*) OpcodesMMF.GetWriteableHandle(); + if (!MMFOpcodesData_Writable) { + cout << "Error: EMuShareMem: DLLLoadOpcodes: !MMFOpcodesData_Writable" << endl; + return false; + } + + //emu_to_eq is right after eq_to_emu + MMFOpcodesData_emu_to_eq = MMFOpcodesData_Writable->eq_to_emu + eq_count; + MMFOpcodesData_emu_to_eq_write = MMFOpcodesData_Writable->eq_to_emu + eq_count; + + //we need to memset the eq opcodes + memset(MMFOpcodesData_Writable->eq_to_emu, 0, sizeof(uint16)*eq_count); + + MMFOpcodesData_Writable->EQOpcodeCount = eq_count; + MMFOpcodesData_Writable->EmuOpcodeCount = emu_count; + // use a callback so the DB functions are done in the main exe + // this way the DLL doesnt have to open a connection to mysql + if (!cb(filename)) { + cout << "Error: EMuShareMem: DLLLoadOpcodes: !cbDBLoadOpcodes" << endl; + return false; + } + + //we dont disable the write handle here, so we can reload them + //MMFOpcodesData_Writable = 0; + + OpcodesMMF.SetLoaded(); + MMFOpcodesData = (const MMFOpcodes_Struct*) OpcodesMMF.GetHandle(); + if (!MMFOpcodesData) { + cout << "Error: EMuShareMem: DLLLoadOpcodes: !MMFOpcodesData (CanWrite=true)" << endl; + return false; + } + return true; + } else { + if (!OpcodesMMF.IsLoaded()) { + Timer::SetCurrentTime(); + uint32 starttime = Timer::GetCurrentTime(); + while ((!OpcodesMMF.IsLoaded()) && ((Timer::GetCurrentTime() - starttime) < 300000)) { + Sleep(10); + Timer::SetCurrentTime(); + } + if (!OpcodesMMF.IsLoaded()) { + cout << "Error: EMuShareMem: DLLLoadOpcodes: !OpcodesMMF.IsLoaded() (timeout)" << endl; + return false; + } + } + MMFOpcodesData = (const MMFOpcodes_Struct*) OpcodesMMF.GetHandle(); + if (!MMFOpcodesData) { + cout << "Error: EMuShareMem: DLLLoadOpcodes: !MMFOpcodesData (CanWrite=false)" << endl; + return false; + } + + //emu_to_eq is right after eq_to_emu + MMFOpcodesData_emu_to_eq = MMFOpcodesData->eq_to_emu + MMFOpcodesData->EQOpcodeCount; + + //cheat a little so we can retain writeable handles for reloading + MMFOpcodesData_Writable = const_cast(MMFOpcodesData); + MMFOpcodesData_emu_to_eq_write = MMFOpcodesData_Writable->eq_to_emu + eq_count; + + + return true; + } + } + else { + cout << "Error Loading Opcodes: Opcodes.cpp: pDLLLoadOpcodes: ret == 0, size = " << tmpMemSize << endl; + return false; + } + return false; +} + + + + + diff --git a/EMuShareMem/Opcodes.h b/EMuShareMem/Opcodes.h new file mode 100644 index 000000000..16c3cd6c8 --- /dev/null +++ b/EMuShareMem/Opcodes.h @@ -0,0 +1,10 @@ +#include "../common/types.h" +#include "../common/EMuShareMem.h" + +struct MMFOpcodes_Struct { + uint32 EQOpcodeCount; + uint32 EmuOpcodeCount; + uint16 eq_to_emu[0]; + //uint16 emu_to_eq[0]; //logical, not really here... EQOpcodeCount indexes in +}; + diff --git a/EMuShareMem/SkillCaps.cpp b/EMuShareMem/SkillCaps.cpp new file mode 100644 index 000000000..474a456d8 --- /dev/null +++ b/EMuShareMem/SkillCaps.cpp @@ -0,0 +1,142 @@ +#include "../common/debug.h" + +#ifdef _WINDOWS +#include +#else +#include "../common/unix.h" +#endif + +#include +#include +using namespace std; +#include "SkillCaps.h" +#include "../common/timer.h" +#include "MMF.h" + +MMF SkillCapsMMF; +const MMFSkillCaps_Struct* MMFSkillCapsData = 0; +MMFSkillCaps_Struct* MMFSkillCapsData_Writable = 0; + + +DLLFUNC uint16 GetSkillCap(uint8 Class_, uint8 Skill, uint8 Level) { + if (MMFSkillCapsData == 0 || (!SkillCapsMMF.IsLoaded())) + return 0; + if (Class_ >= MMFSkillCapsData->ClassCount || Skill >= MMFSkillCapsData->SkillCount || Level >= MMFSkillCapsData->LevelCount) + return(0); + + uint32 index = + (((Class_ * MMFSkillCapsData->SkillCount) + Skill) * MMFSkillCapsData->LevelCount) + + Level; + + return MMFSkillCapsData->caps[index]; +} + +DLLFUNC bool SetSkillCap(uint8 Class_, uint8 Skill, uint8 Level, uint16 cap) { + if (!MMFSkillCapsData_Writable) + return false; + if (Class_ >= MMFSkillCapsData_Writable->ClassCount || Skill >= MMFSkillCapsData_Writable->SkillCount || Level >= MMFSkillCapsData_Writable->LevelCount) + return false; + + uint32 index = + (((Class_ * MMFSkillCapsData_Writable->SkillCount) + Skill) * MMFSkillCapsData_Writable->LevelCount) + + Level; + + MMFSkillCapsData_Writable->caps[index] = cap; + + return true; +} + +DLLFUNC uint8 GetTrainLevel(uint8 Class_, uint8 Skill, uint8 Level){ + if (MMFSkillCapsData == 0 || (!SkillCapsMMF.IsLoaded())) + return 0; + if (Class_ >= MMFSkillCapsData->ClassCount || Skill >= MMFSkillCapsData->SkillCount || Level >= MMFSkillCapsData->LevelCount) + return(0); + + uint32 index = (((Class_ * MMFSkillCapsData->SkillCount) + Skill) * MMFSkillCapsData->LevelCount); + + for(int x = 0; x < Level; x++){ + if(MMFSkillCapsData->caps[index + x]){ + return (x); + } + } + return(0); +} + +DLLFUNC void ClearSkillCaps() { + if (!MMFSkillCapsData_Writable) + return; + + memset(MMFSkillCapsData_Writable->caps, 0, + sizeof(uint16)*(MMFSkillCapsData->ClassCount*MMFSkillCapsData->SkillCount*MMFSkillCapsData->LevelCount)); + +} + +DLLFUNC bool LoadSkillCaps(CALLBACK_DBLoadSkillCaps cb, uint32 opsize, uint8 ClassCount, uint8 SkillCount, uint8 LevelCount) { + if(opsize != sizeof(uint16)) { + cout << "Error: EMuShareMem: DLLLoadSkillCaps: opsize != sizeof(uint16)" << endl; + cout << "SkillCap size has changed, EMuShareMem.dll needs to be recompiled." << endl; + return false; + } + uint32 tmpMemSize = sizeof(MMFSkillCaps_Struct) + opsize * (ClassCount*SkillCount*LevelCount); + if (SkillCapsMMF.Open("EQEMuKSkillCaps", tmpMemSize)) { + if (SkillCapsMMF.CanWrite()) { + MMFSkillCapsData_Writable = (MMFSkillCaps_Struct*) SkillCapsMMF.GetWriteableHandle(); + if (!MMFSkillCapsData_Writable) { + cout << "Error: EMuShareMem: DLLLoadSkillCaps: !MMFSkillCapsData_Writable" << endl; + return false; + } + //we need to memset the eq SkillCaps + memset(MMFSkillCapsData_Writable->caps, 0, sizeof(uint16)*(ClassCount*SkillCount*LevelCount)); + + MMFSkillCapsData_Writable->ClassCount = ClassCount; + MMFSkillCapsData_Writable->SkillCount = SkillCount; + MMFSkillCapsData_Writable->LevelCount = LevelCount; + // use a callback so the DB functions are done in the main exe + // this way the DLL doesnt have to open a connection to mysql + if (!cb()) { + cout << "Error: EMuShareMem: DLLLoadSkillCaps: !cbDBLoadSkillCaps" << endl; + return false; + } + + MMFSkillCapsData_Writable = 0; + + SkillCapsMMF.SetLoaded(); + MMFSkillCapsData = (const MMFSkillCaps_Struct*) SkillCapsMMF.GetHandle(); + if (!MMFSkillCapsData) { + cout << "Error: EMuShareMem: DLLLoadSkillCaps: !MMFSkillCapsData (CanWrite=true)" << endl; + return false; + } + return true; + } else { + if (!SkillCapsMMF.IsLoaded()) { + Timer::SetCurrentTime(); + uint32 starttime = Timer::GetCurrentTime(); + while ((!SkillCapsMMF.IsLoaded()) && ((Timer::GetCurrentTime() - starttime) < 300000)) { + Sleep(10); + Timer::SetCurrentTime(); + } + if (!SkillCapsMMF.IsLoaded()) { + cout << "Error: EMuShareMem: DLLLoadSkillCaps: !SkillCapsMMF.IsLoaded() (timeout)" << endl; + return false; + } + } + MMFSkillCapsData = (const MMFSkillCaps_Struct*) SkillCapsMMF.GetHandle(); + if (!MMFSkillCapsData) { + cout << "Error: EMuShareMem: DLLLoadSkillCaps: !MMFSkillCapsData (CanWrite=false)" << endl; + return false; + } + + return true; + } + } + else { + cout << "Error Loading SkillCaps: SkillCaps.cpp: pDLLLoadSkillCaps: ret == 0, size = " << tmpMemSize << endl; + return false; + } + return false; +} + + + + + diff --git a/EMuShareMem/SkillCaps.h b/EMuShareMem/SkillCaps.h new file mode 100644 index 000000000..4d7ff4b07 --- /dev/null +++ b/EMuShareMem/SkillCaps.h @@ -0,0 +1,10 @@ +#include "../common/types.h" +#include "../common/EMuShareMem.h" + +struct MMFSkillCaps_Struct { + uint8 ClassCount; + uint8 SkillCount; + uint8 LevelCount; + uint16 caps[0]; +}; + diff --git a/EMuShareMem/Spells.cpp b/EMuShareMem/Spells.cpp new file mode 100644 index 000000000..aa310eaea --- /dev/null +++ b/EMuShareMem/Spells.cpp @@ -0,0 +1,98 @@ +#include "../common/debug.h" +#ifdef _WINDOWS +#include +#else +#include "../common/unix.h" +#endif + +#include +#include +using namespace std; +#include "Spells.h" +#include "../common/timer.h" +//#include "../zone/masterentity.h" +#include "MMF.h" + +MMF SpellsMMF; +const MMFSpells_Struct* MMFSpellsData = 0; +MMFSpells_Struct* MMFSpellsData_Writable = 0; + +#ifdef _WINDOWS +extern "C" __declspec(dllexport) bool DLLLoadSPDat(const CALLBACK_FileLoadSPDat cbFileLoadSPDat, const void** oSpellsPointer, int32* oSPDAT_RECORDS, uint32 iSPDat_Struct_Size) { + return pDLLLoadSPDat(cbFileLoadSPDat, oSpellsPointer, oSPDAT_RECORDS, iSPDat_Struct_Size); +}; +#else +extern "C" bool DLLLoadSPDat(const CALLBACK_FileLoadSPDat cbFileLoadSPDat, const void** oSpellsPointer, int32* oSPDAT_RECORDS, uint32 iSPDat_Struct_Size) { + return pDLLLoadSPDat(cbFileLoadSPDat, oSpellsPointer, oSPDAT_RECORDS, iSPDat_Struct_Size); +}; + +#endif + +bool pDLLLoadSPDat(const CALLBACK_FileLoadSPDat cbFileLoadSPDat, const void** oSpellsPointer, int32* oSPDAT_RECORDS, uint32 iSPDat_Struct_Size) { + if (iSPDat_Struct_Size != sizeof(SPDat_Spell_Struct)) { + cout << "Error: EMuShareMem: DLLLoadSPDat: iSPDat_Struct_Size != sizeof(SPDat_Spell_Struct)" << endl; + cout << "SPDat_Spell_Struct has changed, EMuShareMem.dll needs to be recompiled." << endl; + return false; + } + + uint32 tmpMemSize = sizeof(MMFSpells_Struct) + 256 + (sizeof(SPDat_Spell_Struct) * (*oSPDAT_RECORDS)); + if (SpellsMMF.Open("EQEMuSpells", tmpMemSize)) { + if (SpellsMMF.CanWrite()) { + MMFSpellsData_Writable = (MMFSpells_Struct*) SpellsMMF.GetWriteableHandle(); + if (!MMFSpellsData_Writable) { + cout << "Error: EMuShareMem: DLLLoadSPDat: !MMFSpellsData_Writable" << endl; + return false; + } + + memset(MMFSpellsData_Writable, 0, tmpMemSize); + MMFSpellsData_Writable->SPDAT_RECORDS = *oSPDAT_RECORDS; + // use a callback so the DB functions are done in the main exe + // this way the DLL doesnt have to open a connection to mysql + if (MMFSpellsData_Writable->SPDAT_RECORDS > 0) { + cbFileLoadSPDat(&MMFSpellsData_Writable->spells[0], MMFSpellsData_Writable->SPDAT_RECORDS-1); + *oSpellsPointer = &MMFSpellsData_Writable->spells[0]; + } + else + *oSpellsPointer = 0; + + MMFSpellsData_Writable = 0; + SpellsMMF.SetLoaded(); + MMFSpellsData = (const MMFSpells_Struct*) SpellsMMF.GetHandle(); + if (!MMFSpellsData) { + cout << "Error: EMuShareMem: DLLLoadSPDat: !MMFSpellsData (CanWrite=true)" << endl; + return false; + } + return true; + } + else { + if (!SpellsMMF.IsLoaded()) { + Timer::SetCurrentTime(); + uint32 starttime = Timer::GetCurrentTime(); + while ((!SpellsMMF.IsLoaded()) && ((Timer::GetCurrentTime() - starttime) < 300000)) { + Sleep(100); + Timer::SetCurrentTime(); + } + if (!SpellsMMF.IsLoaded()) { + cout << "Error: EMuShareMem: DLLLoadSPDat: !SpellsMMF.IsLoaded() (timeout)" << endl; + return false; + } + } + MMFSpellsData = (const MMFSpells_Struct*) SpellsMMF.GetHandle(); + if (!MMFSpellsData) { + cout << "Error: EMuShareMem: DLLLoadSPDat: !SpellsMMF (CanWrite=false)" << endl; + return false; + } + *oSPDAT_RECORDS = MMFSpellsData->SPDAT_RECORDS; + if (MMFSpellsData->SPDAT_RECORDS > 0) + *oSpellsPointer = &MMFSpellsData->spells[0]; + else + *oSpellsPointer = 0; + return true; + } + } + else { + cout << "Error Loading SPDat: Spells.cpp: pDLLLoadSPDat: Open() == false" << endl; + return false; + } + return false; +} diff --git a/EMuShareMem/Spells.h b/EMuShareMem/Spells.h new file mode 100644 index 000000000..95e5468a5 --- /dev/null +++ b/EMuShareMem/Spells.h @@ -0,0 +1,16 @@ +#ifndef MEMSHARE_SPELLS_H +#define MEMSHARE_SPELLS_H + +#include "../common/types.h" +#include "../zone/spdat.h" +#include "../common/EMuShareMem.h" + +struct MMFSpells_Struct { + uint32 SPDAT_RECORDS; // maxspellid + 1, size of array + SPDat_Spell_Struct spells[0]; +}; + +bool pDLLLoadSPDat(const CALLBACK_FileLoadSPDat cbFileLoadSPDat, const void** oSpellsPointer, int32* oSPDAT_RECORDS, uint32 iSPDat_Struct_Size); + +#endif + diff --git a/GPL.txt b/GPL.txt new file mode 100644 index 000000000..d511905c1 --- /dev/null +++ b/GPL.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/changelog.txt b/changelog.txt new file mode 100644 index 000000000..564954ec0 --- /dev/null +++ b/changelog.txt @@ -0,0 +1,7873 @@ +EQEMu Changelog (Started on Sept 24, 2003 15:50) +------------------------------------------------------- + +== 02/16/2013 == +Derision: Added optional guildid to quest::gmsay, i.e. quest::gmsay(, [color], [toworld], [guildid]) + +== 02/12/2013 == +Kayen: AA fix + +REQUIRED SQL: utils/sql/svn/2504_required_aa_updates.sql + +== 02/11/2013 == +Derision: RoF: Added ENCODE for OP_BeginCast (fixes no sound during spell casting). Corrected OP_DeleteSpell. + +== 02/10/2013 == +JJ: (demonstar55) Language skill up should use proper function. +JJ: SetLanguageSkill now updates client immediately. Both functions do proper limit checks. + Added two missing languages. Skill level 0 in a spoken language now shows 'in an unknown tongue'. +JJ: Initial implementation of a GarbleMessage function and implemented for languages. Can be shared with drunk speaking. + +== 02/09/2013 == +KLS: Was pointed out to me that we're in violation of ActiveState Licensing terms for their perl distribution. +To rectify this we have removed the Windows perl binaries from the source. + +== 02/06/2013 == +Uleat: Changed conversion of bot armor colors from long to unsigned long. Conversion error led to #FFFFFFFF for all returns. + +== 02/05/2013 == +cavedude00: Added heading to start_zones +Uleat: Fixed the 'nude' bot issue. Mob::texture was not set to the appropriate value and forcing an unclad body model. +Uleat: Fixed the show/hide helm feature. Added rebroadcast of packet so that changes take place immediately instead of after zoning. +KLS: Addressed several (completely stupid and inexcusable) bugs in the avoidance code that made it impossible to dodge and parry in certain situations. +As a note: please don't touch the avoidance code if you don't know what you're doing, seriously. + +Required SQL: utils/sql/svn/2482_required_start_zones.sql + +== 02/03/2013 == +Trevius: RoF: More work on Bazaar Traders. +Sorvani: Items with more than 1 charge should be purchased at max charges again. This does not fix players recharging by sell/buyback method. That will come in the future. + +== 02/02/2013 == +Trevius: RoF: Fixed some opcodes related to surnames and traders. Surnames can now be cleared. +JJ: Fix to #spawn: Automatically truncate name if too long so spawn will happen as expected rather than a NameTooLong NPC. +JJ: Fix to #spawn: Output actual NPC result after creation rather than projected result before. + +== 02/01/2013 == +Trevius: RoF: Loading Spell Sets now unmems spells before meming the new set. +Trevius: RoF: Turning on Trader mode in bazaar now works, but no further trader functionality is available yet. +Akkadius: Fixed an issue where global_npc.pl was not initializing (initially) + +== 01/31/2013 == +cavedude00: (demonstar55) Rune aggro fix +cavedude00: (Drajor) Tradeskill skillneeded fix. + +== 01/30/2013 == +Kayen: Various AA fixes, mostly related to SOF display issues. + +REQUIRED SQL: utils/sql/svn/2471_required_aa_updates.sql + + +== 01/29/2013 == +Secrets: Another attempt to fix this bot/merc group crash on zoning. +cavedude: Added $client->GetInstanceID() Perl export. + +== 01/28/2013 == +Secrets: Fixed a crash issue involving invalid buff slots in SpellEffect code. (Crash fix.) +Secrets: QuestParserCollection no longer processes EventPlayer in the case that a player zones and the group is still valid via linkdeath. (Crash fix.) +Secrets: Added the command #augmentitem. Does the same thing that the #bot augmentitem command does but without the bot command needing to be active. Default 250 status. +JJ: Added quest::depop_withtimer() which will depop NPC and start spawn timer (a replacement for $npc->Depop(1) code if desired) + +== 01/27/2013 == +Trevius: Fixed a bug introduced in Rev2448 that caused some spell casting to use all of a player's mana. +Trevius: Mercenary buffs now display in the Target Buffs window. +Trevius: RoF: Populated a couple more bit fields in the spawn struct. +Trevius: Mercenaries are no longer targetable with NPC targeting hotkeys. + +== 01/26/2013 == +Derision: RoF: Updated spell buffs in the PP to account for instrument_mod/bard_modifier. +Sorvani: Missed a spot on the stun proc aggro change. +Trevius: Fixed Mercenary Upkeep Timers and Stances again for UF and SoD. +Trevius: Fixed Mercenary Upkeep Timers to properly restart after the timer is up for all clients. +Bad_Captain: Mercs - Initial spell casting AI committed. +KLS: Added crash logging for Windows builds. +Trevius: Mercenaries now despawn when a player camps out or disconnects in any way. +Trevius: Players with a Mercenary spawned can now be invited to and join another group with their mercenary. +Sorvani: (Demonstar55): Moved stunproc rule implmentation to catch all cases + +OPTIONAL SQL: utils/sql/svn/mercs.sql -- rerun for updated merc stats & merc spell lists + +== 01/25/2013 == +Sorvani: Implemented max aggro from stun proc set to 400. It is rule based set rule to -1 for unlimited. +Derision: RoF: Should fix x10 Buff bug. +Trevius: Reworked some of the code dealing with Mercenary packets and spawning them. There is still some work to be done, but the basics are more solid now. +Trevius: Mercenaries function much more consistently now for Hiring, Suspending, Unsuspending, Dismissing. +Trevius: Mercenaries no longer fail to group up after being hired, unsuspended or after zoning/logging in. + +== 01/24/2013 == +Trevius: RoF: The #fixmob command can now go up to race 724 when cycling through races. +Trevius: RoF: The #npcstats command no longer truncates itemlinks. + +== 01/23/2013 == +Trevius: RoF: Corrected multiple opcodes. +Trevius: RoF: Apply Poison is now functional. +Trevius: RoF: /getguildmotd is working. +Trevius: RoF: GM commands /changename, /summon, /emotezone, and /emoteworld are working. +Trevius: RoF: Sense Traps is now working. +Trevius: RoF: Potion Belt is now functional. +Trevius: RoF: /who and /who all now display the correct player counts. + +== 01/21/2013 == +Akkadius: Should have fixed some scenarios where the global_npc.pl script wasn't unloading correctly +Trevius: RoF: Alternate Currencies now display correctly in the inventory window. +Trevius: Changed one of the mercenary data packets to use the same packet Live does instead of the Merchant packet. +Uleat: RoF: Aligned the extended client slots back into slottype 0. Previously, corpse looting stopped after looting the ninth slot. (There's still an issue addressing slots 31 and 32..this cannot be efficiently corrected until an inventory rework is performed.) +Uleat: Fixed a corpse looting issue where the power source item (slot 9999) was being omitted from the corpse inventory. A previous oversight on my part. Also refined message reporting. (RoF: increased corpse slot limit to 34) +Uleat: Power Source items will now report in 'worn' instead of 'inv' when using #peekinv. + +== 01/20/2013 == +KLS: intN types have changed to more closely reflect C99 and C++11 types: +intN was an unsigned int of N bits -> it is now a signed int of N bits. +sintN was a signed int of N bits -> it has been removed in favor of intN. +uintN is still unsigned. +The existing types in the code have been changed automatically(intN -> uintN, sintN -> intN), but keep this in mind when developing in the future. + +== 01/19/2013 == +Akkadius: Implemented global_npc.pl, this is very similar to global_player.pl where any normal NPC script will run dual-stack or in tandem with the global_npc.pl located in the templates folder +Akkadius: Implemented the ability to modify EXP and AAEXP modifiers per Level, these are loaded from an optional table (level_exp_mods) that is attached to the commit, you MUST enable the rule first! +Akkadius: Added #reloadlevelmods - which will reload level mods from the `level_exp_mods` table +Derision: RoF: Guild name now appears in the Guild Management window and over player's heads. /guildstatus now works. Guild members status now updates in the GMW. +Uleat: Corrected the BulkSendInventory process for incomplete item trade returns resulting from a server crash. Previous code used an external reference and items were never moved..and hence, lost once a new trade occurred. +Uleat: Added mlog messages to existing BulkSendInventory code to report hidden item movements and deletions. All BSI and helper processes now log movements/deletions. +Uleat: Added two new client pre-entry inventory validation methods - RemoveDuplicateLore and MoveSlotNotAllowed. Duplicate lore was not checked and IsSlotAllowed failures were not handled. Both were causes of persistent desyncs. +Uleat: Corrected a bug in ItemInst::IsSlotAllowed that 'passed' PowerSource slot checks regardless of ItemInst::Slots value. +Uleat: [Client-Server Inventory Desyncronization] Most CSD bugs should now be corrected, or at least, compensated for. Please post any desync issues as a new bug post or pm a Dev or GM if it involves an exploit. + +Optional SQL: \utils\sql\svn\2428_optional_levelbasedexpmods.sql + +== 01/18/2013 == +Akkadius: Fixed an size value of hp_regen_rate for NPC's so values don't roll over into negative values +KLS: EQEmu has been setup to use CMake, this is mandatory. +Visit http://www.eqemulator.net/wiki/wikka.php?wakka=CMake for instructions on Windows/GCC. + +== 01/17/2013 == +Uleat: Added opcode handlers for OpenInventory(SoF and SoD) and OpenContainer(SoF, SoD, UF and RoF). Both still need slot translators. +Uleat: Changed internal SwapItem method from void to bool return. Added appropriate code to handle returns. +Uleat: Added an abbreviated form of the resync code - SwapItemResync. Handles failed item swaps on a case-by-case basis. +Uleat: Fixed a bug that allowed duplication of non-stackable, charged items..lore or otherwise. (Sorry cheaters...) + +== 01/16/2013 == +Derision: RoF: Updated OP_LogServer encode based on a live packet in order to enable 'Enter Tutorial from character select. +Secrets: Added five new functions to perl for clients: GetBindX, GetBindY, GetBindZ, GetBindHeading, and GetBindZoneID. They all have an optional parameter of index of playerprofile's bindpoint. +Secrets: Added an optional parameter to GetBind for index. + +== 01/15/2013 == +Derision: RoF: Added Encode for OP_TaskDescription and removed RoF specific code for it from tasks.cpp +Derision: Fixed some compiler warnings. +Trevius: RoF: Varlinks and Saylinks now display properly for RoF. +Trevius: RoF: Started work on Mercenaries. They are partially functional but need more work. +Trevius: RoF: Started work on Guilds. The guild window can now display members, but the guild name does not yet display. + +== 01/14/2013 == +Derision: RoF: Fixed bug that was causing only one task to show in the Active Task window. +Derision: Fixed potential crash in QuestManager::varlink. Removed some unused variables that gcc was warning about. + +== 01/13/2013 == +Derision: RoF: Encoded OP_TaskHistoryReply and added temporary handling for short OP_TaskActivity packets until encode is down for that. +Derision: Deleted Anniversary/HoT/VoA and Live patches. +Derision: Updated StaticGetZoneName with all zones currently in the PEQ database. +Trevius: RoF: Disciplines now update without zoning. + +== 01/12/2013 == +Derision: RoF: Personal Tribute and the Pet Buff Window now work. +Derision: Fixed potential crash in SendPetBuffsToClient. +Derision: RoF: Accounted for the fact the Duplicate Lore item message now includes the item's name. +Trevius: RoF: The Task Selector Window is now functional. + +== 01/11/2013 == +JJ: Added rule check to zone - no need to load Merc Templates if Mercs aren't allowed. + +== 01/10/2013 == +Trevius: RoF: Corrected multiple opcodes. +Trevius: RoF: #finditem now displays itemlinks correctly. +Trevius: RoF: Started work/testing to get Tasks functioning. Don't click tasks in the history tab or client will freeze. +Trevius: Added some possible crash fixes. +Bad_Captain: Mercs - temp fix for possible crash when dying with RespawnFromHover on, fixed infinite stats (buffs continually adding to stats). + +== 01/09/2013 == +Derision: RoF: Corrected LDoN merchant opcodes, added encode for OP_AdventureMerchantSell. Updated PP with available LDoN points. +Bad_Captain: Mercs - removed debug messages, fixed regen. + +== 01/08/2013 == +Derision: RoF: Marked position of Monk 'Mend' skill cooldown timer in PP. Fixed a typo. +Bad_Captain: Mercs - Merged from branch. Initial merc commit to trunk with semi-functional tank mercs. +Uleat: RoF: Changed opcode so that is handled properly. (Was writing to the log about twice-per-second.) +Trevius: RoF: Death no longer crashes players and the respawn window is now functional. +Trevius: RoF: More work on slot conversions. This time for the cursor buffer. +Trevius: RoF: Some work on Guild Master Training packets, but still only seeing languages displayed. + +REQUIRED SQL: utils/sql/svn/2383_required_group_ismerc.sql -- adds ismerc column to group_id table +OPTIONAL SQL: utils/sql/svn/2380_optional_merc_rules.sql -- Contains rules for mercs including rule to enable mercs +OPTIONAL SQL: utils/sql/svn/2380_optional_merc_merchant_npctypes_update.sql -- Contains npc_types & spawn updates for merc merchants in PoK +OPTIONAL SQL: utils/sql/svn/2380_optional_merc_data.sql -- Contains basic merc data, template info, & merc merchant entries +OPTIONAL SQL: utils/sql/svn/mercs.sql -- Contains merc stats & armor - to be replaced as needed with updated stats, spells, etc. Allows a complete resourcing of file + +== 01/07/2013 == +Trevius: RoF: /who and /who all now function properly. +Trevius: RoF: Adjusted/corrected some of the slot conversion functions. +Kayen: Various AA Fixes including a critical crash bug. + +REQUIRED SQL: utils/sql/svn/2376_required_aa_updates.sql + +== 01/06/2013 == +Kayen: Fix for missing and incorrect Healing Adept AA values. +Trevius: RoF: Possible crash fix for logging in on some server builds. +Trevius: RoF: Added VoA to the #cvs command output. +Derision: RoF: Short buffs should appear in the song/short buff box. +Lerxst/Hateborne: Updated Mob::TryDivineSave to allow someone to be saved even if there are no additional effects to be cast and rearranged the buff fading so the spell effect isn't lost before it can be cast. + +REQUIRED SQL: utils/sql/svn/2370_required_aa_updates.sql + +== 01/05/2013 == +Trevius: RoF: Tradeskill combines (auto-combine and experiment) are now functional. +Trevius: RoF: The Popup Message window is now functional. +Trevius: RoF: Increased the max race id for #race to 724 (the max for RoF). +Trevius: RoF: Corrected the opcode for Augmenting Items and adjusted the struct, but it looks like it may require considerable work to implement the new augment system. +Akkadius: Trap Zone Crash issue where message is empty +Akkadius: Fixed a warning regarding AC +Vaion: RoF: Books now work properly. + +== 01/04/2013 == +Trevius: RoF: Looting items and loot all are now functional. +Trevius: RoF: Trading to NPCs and Players is now functional. +Trevius: RoF: AA Purchases are now functional. +Trevius: RoF: Opening Tradeskill Objects is now possible. +Trevius: RoF: Moving items into Tradeskill Objects is now possible, but combines will bug the client. +Uleat: QS: Added 4 additional logging options [MerchantLogTransactions], [PlayerLogDeletes], [PlayerLogHandins], [PlayerLogMoves] +Uleat: QS: PlayerLogHandins is partially enabled..currently logs NPCtypeID and Client-side trade information only. +Uleat: QS: Renamed 1 existing source files to maintain logging name continuity [PlayerLogTrades]. +Uleat: Fixed minor issue that was causing armor glitches with RoF clients from UF and lower clients. +Uleat: Fixed minor bug in the #iteminfo command where the material value reported 0x20xx. + +REQUIRED SQL: utils/sql/svn/2361_required_qs_rule_values.sql +REQUIRED SQL: utils/sql/queryserv/2361_QueryServ.sql +REQUIRED SQL: utils/sql/queryserv/2304_QueryServ.sql (rename '2304_qs_playertradelog.sql' - Does not need to be re-sourced) + +== 01/03/2013 == +Trevius: RoF: Fixed slot conversion issues for Bank and Shared Bank +Trevius: RoF: Normal Merchants are now fully functional for buy/sell +Trevius: RoF: Item Links now display item stats when clicked, but the links are still truncated. +Trevius: RoF: Corrected the Illusion Struct and corrected some opcodes. +Trevius: RoF: Updated the DeleteSpawn struct and added an encode for it. +Trevius: RoF: Hopefully fixed Consuming food/drink. + +== 01/02/2013 == +Vaion: RoF: Crash prevention for Item Preview window. +Trevius: RoF: Finished identifying basically all of the remaining RoF opcodes. +Akkadius: Fix for Guild Crash again... + +==01/01/2013== +Uleat: Fixed the wear change issue where the server was not translating the SoF, SoD and UF clients' update request properly. +Uleat: Fixed the non-clickable quest reward bug triggered when the item was in a container and the player traded the item or zoned. +Vaion: RoF: Found Opcode for Item Previews. Made base packet support so that Item Preview (Alt+Right Click) works. Still a few issues to hash out. + +==12/31/2012== +Derision: RoF: Added encode/decode for BlockedBuffs and OP_FindPerson. Added opcodes for FindPerson and RestState +Derision: RoF: Found XP/AAXP in the PP. Updated OP_ExpUpdate/OP_LevelUpdate. + +==12/30/2012== +Derision: RoF: Did some work on buffs & buff removal. OP_SetChatServer2 for UCS. Encode for OP_HPUpdate + +==12/29/2012== +Derision: RoF: Added Leadership AA into the PP. Few opcodes to make Track work. +Derision: RoF: Some spell related opcodes and PetCommands struct update. +Vaion: RoF: Fixed OP_PetCommands and OP_TargetCommand to correct opcodes. Identified OP_Jump. +Vaion: Fix for an issue with attuneable augments and an issue in guild bank. +Sorvani: Fix for pets causing a crash. Do not set rule > 25! +Trevius: RoF: Corrected wearchange packet structure. +Trevius: RoF: Added new arguments to #wc ( #wc [wear slot] [material] [ [hero_forge_model] [elite_material] ] ) - Example: "#wc 1 5 6" +Trevius: RoF: Hero's Forge Robes can now be used with #wc by using wear slot 7 - Example: "#wc 7 6 11" +Akkadius: Guild Bank crash fix, checking for valid items before going through the iterator + +REQUIRED SQL: utils/sql/queryserv/2340_required_maxbuffslotspet.sql + +==12/28/2012== +Derision: RoF: Found GM and PVP flags in PP. +Derision: RoF: Found endurance & some group/raid leadership stuff in PP. +Sorvani: Restricted pet buffs to 25. could be made a rule when/if the extpp gets changed to handle it. +Sorvani: Changed a few more VS2010 projects to build as $ProjectName + +==12/27/2012== +Sorvani: Removed #PRAGMA statements around OP_CharacterCreate from patch files now that real problem with RuleI is resolved. +Derision: RoF: Found more of the Spawn_Struct bitfield values. + +==12/26/2102== +Secrets: Fixed an issue where the pets code was counting the wrong number of buffs to load, resulting in invalid memory. "And no one likes getting in the middle of invalid memory!" +Derision: RoF: Updated position/movement related structs (per SEQ), OP_Animation encode and CombatDamage_Struct +Akkadius: Increased max item ID to 300000 (You will need to replace your EmuShareMem.dll for sure next build! +Akkadius: Fixed an issue where initiating CreateDatabaseEntry() for a door would reset version back to 0, it now grabs the current version the door is saved in. +Sorvani: Fixed quest::summonitem to summon charged items with max charges if no charge amount specified +KLS: Fixed an issue with RuleI returning an unsigned int32 instead of a signed int32. This is a critical vulnerability, please update right away. + +==12/24/2012== +Akkadius: Fixed an issue where quest::createdoor would return a door id of 0 when doors exist with version -1 (All Zones) + +==12/23/2012== +Trevius: RoF - Fixed the item struct so items can be loaded properly. +Trevius: RoF - Added back all of the opcodes that were previously identified and a few more. +Derision: RoF - Commented out lots of Guild opcodes that were causing crashes. +Derision: RoF - Updated RequestClientZoneChange struct and encode. Altered default Bitfields setting to only apply tyo NPCs +Derision: RoF - Updated RoF.cpp to remove all the warnings in a Linux compile + +==12/22/2012== +Secrets: Fixed the x64 portability issue reported by Akkadius. No idea how long this has been broke but at least it's fixed. + +==12/21/2012== +Sorvani: Items with zero charges remaining and located in a bag will no longer get set to MaxCharges when looted from a corpse. Round 2. +Sorvani: (Hateborne) copy paste issue in Mob::TryDivineSave corrected +Trevius: RoF - Fixed the Character Select Struct, which should prevent an error when trying to enter world on certain characters. +Trevius: RoF - Character Creation is now functional. +Trevius: RoF - Identified more fields in the Player Profile and added the correct checksum so the current packet should work (thanks Derision). +Trevius: RoF - Added the opcode for max characters to allow up to 10 chars to be created per account instead of 0 (thanks Noport). +Trevius: RoF - Fixed Drakkin Appearances at Character Select. Appears that all facial/body features are now perfectly aligned. +Uleat: QueryServ player trade log code is now enabled. Set the rules_value to true to enable logging. + +REQUIRED SQL: utils/sql/queryserv/2304_qs_playertradelog.sql + +==12/20/2012== +Akkadius: Changed maxlevel default value and value for lootdrop_entries, this is a thing more affecting servers with higher than 127 so this doesn't affect many people. + +OPTIONAL SQL: utils/sql/svn/2300_optional_loot_changes.sql + +==12/19/2012== +Trevius: RoF - More work on RoF Structs and added the Client Version stuff. +Trevius: RoF - Removed/Disabled most opcodes until we are further along and then I will add them all back in and populate the rest. +Trevius: RoF - Added a new packet dealing with membership status, but it may need more work. +Uleat: Added inspect message functionality for SoF+ clients. +Uleat: Added bot command #bot setinspectmessage (use #bot setinspectmessage help) + +REQUIRED SQL: utils/sql/svn/2299_required_inspectmessage_fields.sql + +==12/18/2012== +Lerxst: Added the RoF.cpp files to the patches folder of the project for the vs2008 people. + +==12/17/2012== +Sorvani: Added the RoF.cpp files to the patches folder of the project for the windows people. +Akkadius: Added EntityList::GetDoorsByDoorID(int32 id) which is exported to Perl $entity_list->GetDoorsByDoorID +Akkadius: Added Door Class object $Door->GetDoorID(); to return the obvious Door ID for the entity + +==12/15/2012== +Trevius: Initial addition of the RoF client. Currently disabled by default. Uncomment the 2 RoF lines in common/patches/patches.cpp to enable the client. + +==12/13/2012== +Sorvani: (Uleat) Player Inspect works for Titanium users again. Spell casting from a clicky effect checks that the item is still in the slot it was cast from before applying the affect and removing the charge (if applicable). + +==12/08/2012== +Secrets/Akkadius: Implemented the customary ability to scale an NPC's spell damage or healing % by a modifier loaded from the npc data in npc_types + This will allow an NPC to do for example 150% of the damage of their damage spells if spellscale is set to '150' + 'healscale' field needs to be set to affect heals in a similar manner + Both of these can also be accessed via ModifyNPCStat through 'healscale' and 'spellscale' + +REQUIRED SQL: utils/sql/svn/2283_required_npc_changes.sql + +==12/06/2012== +Secrets: Fixed OP_PVPStats in HoT to the proper opcode, OP_WebLink. You can use $client->SendWebLink("website"); in perl to use this functionality to pop open a web browser ingame. + +==12/05/2012== +Akkadius: Ability to send signals through the Telnet console to character: signalcharbyname + +==12/04/2012== +Sorvani: (Uleat) Update to inspect (SoF+) and corpses to related to handling power sources. Titanium inspect still broken. +Vaion: Renamed the rule for targeting swarm pets so that it make sense with the code. + +OPTIONAL SQL: utils/sql/svn/2280_optional_rule_targetableswarmpet-rename.sql + +==12/3/2012== +Vaion: New rule created for handling whether swarm pets are targetable or not. Set to false for default to allow targeting swarm pets. + +OPTIONAL SQL: utils/sql/svn/2278_optional_rule_targetableswarmpet.sql + +==12/2/2012== +Secrets: Pets can no longer proc from any hand other than their primary hand. This fixes an issue where beastlord pets could proc two, three or four from dual wielding/double attacking pets (as well as a general mechanics fix). +Secrets: NPCs will no longer attempt to heal through walls. There is now a LOS check for friendly NPC heals through walls. This was especially apparent in the PEQ database in Splitpaw, where you could have maybe 10 NPCs healing through walls at a time. I don't recall this ever happening on live past OoW. If this is incorrect revert it and make it a rule. + +==12/1/2012== +Vaion: Swarm Pets are now untargetable. They still gain aggro, are attackable and can be casted on by NPCs, but clients cannot target them. +Sorvani: Items with 0 charges will no longer get a free recharge when players die. Changed the TargetName property for all projects to be $(ProjectName). The only project that will have a new name is chatserver. It is now called ucs matching the linux build. + +==11/27/2012== +Sorvani: fix for newly created items with unlmiited charges to actually be usable. +Sorvani: (Uleat) Lore check is now made for items inside bags being trade before the trade completes. +Akkadius: Started first works of implementing QueryServ for what it was meant to be used +Akkadius: Moved QueryServ to it's own configuration block in the eqemu_config.xml (example): + + localhost + 3306 + root + password + queryserv + +Akkadius: QueryServ can now be used on a separate server and can use an entirely separate database as it was meant to +Akkadius: Added 'loottable_id` to NPC::ModifyNPCStat which also changes quest::ModifyNPCStat(identifier, newValue); as well +Akkadius: Fixed heap corruption in quest_globals that would occur if a value over 64 bytes was created +Akkadius: Changed value size in qglobals from 64 bytes to 128 bytes +Akkadius: Added #reloadworld - This command will reload every single zones Perl quests and repop them +Secrets: Minor code fix for qglobals. Since we removed the id field from qglobals, I suppose we shouldn't use it in code. +Secrets: Warning fix for a quest function. +Secrets: Fixed a crash issue where player corpses did not exist at all in a function (ie; they were disabled) yet we are trying to use an invalid pointer +Secrets: durp, i'm retarded. memleak fix. + +Perl Changes: +Akkadius: Added EVENT_DISCONNECT to global_player.pl/player.pl - Event that triggers whenever a character disconnects from the world +Akkadius: Added EVENT_CONNECT to global_player.pl/player.pl - Event that triggers whenever a character connects to the world +Akkadius: Added EVENT_TASK_UPDATE to global_player.pl/player.pl - Event that triggers whenever a donecount is incremented in a task, exports variables: $donecount, $activity_id and $task_id +Akkadius: Added EVENT_TASK_COMPLETE to global_player.pl/player.pl - Event that triggers whenever a task is complete, this can sort of be checked in EVENT_TASK_STAGE_COMPLETED already but this is more finite, exports variables: $donecount, $activity_id and $task_id +Akkadius: Added EVENT_TASK_FAIL to global_player.pl/player.pl - Event that triggers whenever a task has failed, exports variables: $task_id +Akkadius: Added $client->SignalClient($data), it uses the client as the initiator and the client that gets sent the data, good for entity_list iterations through client +Akkadius: Exported $client->AddAlternateCurrencyValue(currency_id, amount); originally created by KLS +Akkadius: Added quest::gettaskactivitydonecount(task, activity); which obviously will get the amount done in a task based on task and activity +Akkadius: Added cross zone client signalling capability by character ID: quest::crosszonesignalclientbycharid(char_id, data); +Akkadius: Added cross zone client signalling capability by character Name: quest::crosszonesignalclientbyname(Name, data); +Akkadius: Added cross zone client messaging capability by character Name: quest::crosszonemessageplayerbyname(Type, Name, Message); +Akkadius: Added the ability to flow #commands through Perl, by flow meaning if the command doesn't process through the traditional command system, it will dump the output to EVENT_SAY in player.pl/global_player.pl based on rule Chat:FlowCommandstoPerl_EVENT_SAY + +QueryServ Changes: +* I plan on adding much more logging to this server, but for now I've started with the following: +* QueryServ requires rules to be enabled before packets get sent to QueryServ, otherwise World/Zone doesn't need to even craft the packets for logging +Akkadius: Logging all player kills through QueryServ, you need to have QueryServ:PlayerLogNPCKills Rule Enabled, type: 0 = Solo, 1 = Group, 2 = Raid +Akkadius: Player trades logging started but definitely not finished +Akkadius: Removed the rules system from QueryServ, rules should only be read from World/Zone + +REQUIRED SQL: utils/sql/queryserv/2268_QueryServ.sql + +Database Changes: +Akkadius: Added a field in `character_` called `firstlogon` that will essentially be booled true when a player logs on, it handles a characters online status between world and zone and ultimately gets used in EVENT_CONNECT +Akkadius: Completely dropped the `id` column in `quest_globals`, the (`charid`, `npcid`, `zoneid`, `name`) fields will act as the primary keys, tested and working fine. This solves capping out on ID's as they are unecessary. +Akkadius: Changed value size of `quest_globals` field `value` from 64 bytes to 128 bytes +Akkadius: Removed an AA that was causing heap corruption + +REQUIRED SQL: utils/sql/svn/2268_required_updates.sql + +==11/25/2012== +JJ: Initial fix for /MQZone detection to reduce false positives. +Sorvani: Corrected Improved Familiar pre-req and commited Vaion's AA update to fix animation Empathy. +Required SQL: utils/sql/svn/2264_required_aa_updates.sql + +==11/24/2012== +Sorvani: (Vaion) Added /pet no cast and /pet focus +Required SQL: utils/sql/svn/2262_required_pet_discipline_update.sql + +==11/23/2012== +cavedude: (Tabasco) Re-added probability, setting it to 100 (default) effectively bypasses probability and no additional roll is made. Setting it to 0 prevents the table from being rolled at all, so you can disable the table but still keep the entries if needed. +Required SQL: utils/sql/svn/2260_probability.sql + +==11/22/2012== +Sorvani: Tabasco sent in this bugfix: Ranged attack crash +It doesn't happen every time, but if an NPC is flagged for ranged attacks and they kill their target, the AIProcess function continues on under the assumption that the target is still available. + +I just adjusted it to set a flag and do the actual attack at the end of the function. I haven't been able to reproduce the crash since. + +==10/31/2012== +Akkadius: Added the ability to govern player max levels through the source per character. It will look for a qglobal named 'CharMaxLevel', type 5 qglobal, if the rule is enabled and the character flag is set, they will not be able to pass that level. +Optional SQL: utils/sql/svn/2243_optional_char_max_level_rule.sql + +==10/22/2012== +Lerxst2112: Fixed issue in AddMoneyToPP where the amount of silver was being added to copper instead of the leftover copper. + +==10/03/2012== +Sorvani: Disabled optimization around the DECODE(OP_CharacterCreate) in all SoF+ patches to force character creation to validate correctly. + +==10/02/2012== +Sorvani: A NPC that is immune to Mez, Slow, Fear, Charm, or Snare will now generate aggro based on the respective spell's normal aggro. + +==09/28/2012== +Sorvani: SE_SummonCorpse will now work in raid groups. + +==09/27/2012== +Bad_Captain: Bots- Changed #bot healrotation timer to allow decimal values. +Bad_Captain: Bots- Rewrote bot AAs to load from DB. +Bad_Captain: Bots- Synced bot code to client code for aabonuses, attack, spell casting, etc., as the result of the client aabonuses changes. + +==09/23/2012== +Kayen: Fix for crippling blow chance, other minor fixes related to bonuses. + +==09/20/2012== +Kayen: AA dbase table fixes - Archery Mastery, Fury of Magic. + +REQUIRED SQL: utils/sql/svn/2215_required_aa_updates + +==09/19/2012== +KLS: Faction mods (Race, Class, Deity) can now be added on the fly, without adding database columns or editing the code. Source in the database change, run utils/factionmod.pl, and then drop the faction_list table to convert. + +REQUIRED SQL: utils/sql/svn/2214_faction_list_mod.sql + +==09/18/2012== +cavedude: Overhauled loot system. I've outlined the new system on the forums here: http://www.eqemulator.org/forums/showthread.php?t=35770 + +REQUIRED SQL: utils/sql/svn/2213_loot_changes.sql + +==09/16/2012== +Kayen: Implemented SE_HealFromMana changed to SE_HealGroupFromMana - now implemented correctly. + +==09/14/2012== +Kayen: Implemented SE_CriticalHealRate - Focus that increase chance for heals cast on you to critical. +Kayen: Implemented SE_IncreaseNumHits - Focus that can inceases max number of hits a buff can have before being removed. +Kayen: Removed SE_Twinproc - This is not an actual spell effect, Twin proc is now correctly implemented as on live using SE_Twincast. +Kayen: Implemented SE_TwoHandBluntBlock - Chance to block when using two hand blunt weapon (similiar to shield block). +Kayen: SE_NegateEffect will now negate all AA, item and spell bonuses for the specified effects. +Kayen: Added support to allow for certain bonuses to now properly be calculated when cast as debuffs (ie decrease chance to critical hit) +Kayen: Implemented rule to allow certain bonuses to be calculated by adding together the value from each item, instead of taking the highest value. + *Ie. Add together all worn cleave effects, ferocity effects ect. + +OPTIONAL SQL: utils/sql/svn/2209_optional_additive_bonus_rule.sql (disabled by default) + +==09/13/2012== +cavedude: Uleat implemented the following changes: + +The bandolier function should no longer CAUSE desyncs. Its use can still be affected by previous issues, just like any manual swap. Message cues have been temporarily added to certain procedures to indicate to the player that a possible desync condition exists and that they should zone or relog to correct the situation immediately. + +The chance of player corpses becoming bugged should now be minimal. Corpse processing was changed to better emulate client behavior when 'RespawnFromHover' is set to true or false. Message cues and #commands were added to help determine whether a corpse is bugged. (Previously, queued cursor items were sent to the corpse..but, the packet builder did not include the 8000-series of items when a player looted their corpse.) + +Tweaked several areas where item swaps were not handled properly, or at all, and the client-server inventories became desyncronized. Additional areas are still suspect for causing desync's. (A 'ResyncInventory' method is in-work to eliminate the need for item deletion...) + +Client::SummonItem now limits the size of a summoned stack to the value of stacksize in the database. If you are using script to summon stacks greater than item->stacksize, this action was/is incorrect and you will need to modify your 'quest' to summon multiple stacks. + + +command changes: + #equipitem - modified partial stack movements..if a partial stack would be left on cursor, the action now fails. + #corpse - added subcommand 'InspectLoot'..works for client and npc corpses..additional information with client. + #peekinv - added stack size(charges) to report and modified the cursor depth reporting of bag slots. + #zopp - new command for troubleshooting..forces a client 'trade' or 'summon' to desync the client inventory. + +references: + http://www.eqemulator.org/forums/showthread.php?t=35604 - CSD Support Patch + http://www.eqemulator.org/forums/showthread.php?t=35629 - CSD Bugged Corpse Patch + http://www.eqemulator.org/forums/showthread.php?t=35699 - CSD Bandolier Patch + +cavedude: (demonstar55) Damage shields by default will no longer count towards EXP gain. (Rule also added to change this behaviour.) +cavedude: (demonstar55) Extended targets should now clear when aggro is lost using skills. +cavedude: (demonstar55) AAs with shorter reuse timers should now reset if the cast failed (interrupted.) +KLS: Fixed a cause of raids disbanding on zoning. + +OPTIONAL SQL: INSERT INTO `rule_values` VALUES (1, 'Combat:EXPFromDmgShield', 'false', 'Determine if damage from a damage shield counts for EXP gain.'); + +==09/12/2012== +Kayen: Implemented NPC Special Attack 'i'. Immune to Taunt. +Kayen: Implemented: SOF+ live like stacking of AA's that belong to the same series in the AA window (Expection: AA's that use hot keys). +Kayen: Implemented SE_SpellEffectResistChance - Increase chance to resist specific spell effects ie. Charm +Kayen: Revision to how AA focus effects are handled to correclty utilize live AA data. +Kayen: Almost all 'General' and most 'Archetype' AA's implemented through Underfoot. +Kayen: aaID data (AA.h) updated through VoA expansion. + +Following AA effects are now implemented as bonuses: You must update your 'aa_effects' table with 'required_aa_updates.sql' for these to work. +MISC AA: Spell Casting Subtlety now implemented with bonus SE_ChangeAggro +MISC AA: Many AA that are spell focus effects now implemented using live data. +MISC AA: Finishing Blow ect, will now implemented using bonus, SE_FinishingBlow and SE_FinishingBlowLvl. +ENC AA: Total Domination now implemented with bonus SE_CharmBreakChance. +SK AA: Soul Abrasion now implemented with bonus SE_ImprovedDamage2. +*Many fixes to previously implemented AA effects. + +REQUIRED SQL: utils/sql/svn/2208_required_aa_updates +OPTIONAL SQL: utils/sql/svn/2208_optional_aa_stacking_rule.sql (If false will disable AA stacking for all clients) +OPTIONAL SQL: utils/sql/svn/2208_optional_EnableSoulAbrasionAA + *If using an older server spell file (pre SOF), will need to run this to correctly populate the 'spellgroups' field. + +======= +==09/07/2012== +Bad_Captain: Bots- Fixed an issue where bots couldn't have spells with target type of ST_GroupClientAndPet cast on them. +Bad_Captain: Bots- Added #bot healrotations command. Please see forums for more information. + +==09/04/2012== +cavedude: Added NPC:UseMultiQuest rule to turn multiquest support on or off. + +OPTIONAL SQL: INSERT INTO `rule_values` VALUES (1, 'NPC:UseMultiQuest', 'false', 'If true, NPC will remember items handed to them for classic multiquest support.'); + +==09/03/2012== +Akkadius: First set of efforts to clean up Windows compiling. See: + http://www.eqemulator.org/forums/showthread.php?p=212217#post212217 + - Cleaned up x64 compiles, x86 compiles coming right up + - Removed pragma comments and forced them to be used under 'Additional Dependencies' (Windows) +Akkadius: Fixed-up Release & Debug 32-bit compiles +Akkadius: Uploading includes and libraries for both x64 and x86 so that VS2010 can automatically reference those libraries and requires no configuration +Akkadius: All project types have had includes and libraries linked to the appropriate machine type +Akkadius: Most project types have been set to /MP (Multiprocessor compile) and Warning Level 1 to filter most of the garbage +Akkadius: Got the rest of the VS2010 compiles working on a fresh SVN: + Debug x64|x64 = Debug x64|x64 + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + DebugBots x64|x64 = DebugBots x64|x64 + DebugBots|Win32 = DebugBots|Win32 + Release x64|x64 = Release x64|x64 + Release|Win32 = Release|Win32 + ReleaseBots x64|x64 = ReleaseBots x64|x64 + ReleaseBots|Win32 = ReleaseBots|Win32 + ReleaseBots|x64 = ReleaseBots|x64 + +==09/02/2012== +cavedude: (image) Added Classic Multiquest support. +cavedude: Added quest::clearhandin and #clearquestitems so that multiquest can co-exist with out current system without issues. + +REQUIRED plugin: utils/defaults/plugins/check_handin.pl (Or use PEQ quests repo) + +==08/27/2012== +Akkadius: (Uleat) Temporary dupe fix until we can iron this out the way it should be (Not use signed ints in the blob). This should trap a majority of it to client saves. +Akkadius: Account shared platinum field type change (signed to unsigned) to no longer allow negative values... + +REQUIRED SQL: utils/sql/svn/2195_required_sharedplatupdates.sql + +==08/17/2012== +Kayen: Taunt skill updated to work consistent with live. + *You can now taunt NPC's over the users level. (RULE: TauntOverLevel) + *Taunt success chance should be accurate to live. Penalty of not being at max skill lv can be adjusted with (RULE: Combat:TauntSkillFalloff) + *Live messages for taunt success and failure are now implemented. (Note: Only NPC races that can 'talk' will say the success message). +Bad_Captain: Bots- Fixed an issue when with bot spell timers that could lead to a crash. +Kayen: Fixed SQL files from rev2185+ that were not saved as .sql + +OPTIONAL SQL: utils/sql/svn/2189_optional_taunt_rules +OPTIONAL SQL: utils/sql/svn/2185_optional_NPCFlurryChance_rule (run this again) + +==08/16/2012== +Kayen: Complete revision of the Death Save and Divine Save effects and related bonuses to be consistent with live. + *Death Save (Death Pact/DI) will no longer fire on death. It should only fire when less 15% HP but not killed. + *Death Save chance is determined soley by charisma of client with the buff. (RULE: DeathSaveCharismaMod) + *Divine Intervention baseline is now correctly set to 8000HP (from 50k) (RULE: DivineInterventionHeal) + *AA Touch of the Divine provides upon failure of first Death Save, a second roll and if successful will + do a portion of the original heal value. (Ie DI=8000HP with ToTD-3(60%) your heal will be 8000*0.6 = 4800HP) + *Implemented functionality for later expansion Death Save effects (Divine Intercession ect) + These can add heal value to the base heal which is limited by level (ie Heal 10,0000+8000 if client less then level 80) + + *Divine Save (AA Unfailing Divinity) only fire upon death of client with this effect and is independent of Death Save effect. + *Increased ranks of AA only increase the chance of firing upon death. Heal value does NOT increase with rank. + *Upon firing you will still recieve the Divine Aura like buff, which now also correctly removes all determental effects. +Kayen: Implemented: SE_Purify - Removes determental buffs. Value determines how many buffs are removed. +Kayen: Added RULE MaxCastTimeReduction - Max percent your spell cast time can be reduced by spell haste(is/was 50%). + +Following AA effects are now implemented as bonuses: You must update your 'aa_effects' table with 'required_aa_updates.sql' for these to work. +MISC AA: Internal Metronome/Channeling Focus now implemented with bonus, SE_ChannelChanceSpells. +MISC AA: Concentration now correctly implemented with bonus, SE_ChannelChanceItems. +MISC AA: Pet Affinity now implemented with bonus, SE_GivePetGroupTarget. +MISC AA: Spell Casting Expertise, Mastery of the Past ect will prevent fizzles from spells under certain level, SE_MasteryofPast. +BRD AA: Extended Notes/Sionachie's Crescendo will increase ranage of songs with bonus, SE_SongRange. +BRD AA: Jam Fest now implemented with bonus, SE_CastingLevel2. +CLR AA: Unfailing Divinity now implemented with bonus SE_Unfailing Divinity. +CLR AA: Touch of the Divine now implemented with bonus SE_DivineSave. +DRU AA: Enchanted root/Viscid Root (was implemented completely wrong), now uses bonus SE_RootBreakChance. + *If a root is cast on the target and target has this AA it will decrease the chance for that root to break + from any damage spells. The damage spells can be cast by any one, not just the client with the bonus. + Added RULE: RootBreakFromSpells - Baseline is/was set at 20% chance from nukes. + + +REQUIRED SQL: utils/sql/svn/2188_required_aa_updates +OPTIONAL SQL: utils/sql/svn/2188_optional_miscspelleffect_rules + +==08/13/2012== +Kayen: Update to how chance to hit bonuses are applied. + *Accuracy spell effect will now stack with Hit Chance spell effect. (Accuracy/15 = 1% chance to hit). + *Skill specific Hit Chance effects should stack correctly now. + *Accuracy/Hit Chance effects that DECREASE your chance to hit will now function. +Kayen: Minor update to duel wield, chance bonuses will be correctly applied to baseline dw chance. +Kayen: Implemented Perl MOB Quest Object SetFlurryChance(value) (ie 50 = 50% chance for NPC flurry if special atk "F") +Kayen: Implemented Perl MOB Quest Object GetFlurryChance(value) returns flurry chance. +Kayen: Added rule to adjust server wide flurry chance (Default = 20%) *Perl object will override this. + +Following AA effects are now implemented as bonuses: You must update your 'aa_effects' table with 'required_aa_updates.sql' for these to work. +MISC AA: Dead Aim, Precision of the Hunter, Scout's Efficiency will now calculate from bonus SE_Accuracy. +MISC AA: Combat Agility line will now calculate from bonus SE_AvoidMeleeChance. (Now implemented through SoD) +MISC AA: Combat Stability line will now calculate from bonus SE_CombatStability. (Now implemented through SoD) +MISC AA: Double Riposte, Return Kick now calculated from bonus SE_GiveDoubleRiposte. (New AA's implemented that use this) +MISC AA: Natural Durability, should be working correctly now from bonus SE_MaxHP. +MISC AA: Ambidexterity will now apply properly to increase chance to duel wield from bonus SE_Ambidexterity. +MISC AA: Flurry,Rapid Strikes will now properly be applied from bonus SE_Flurry. (New AA's implemented that use this) +MISC AA: Pet AA's that give flurry chance will now be applied by bonus, SE_PetFlurry. +MAG AA: Elemental Agility was incorrectly giving melee mitigation instead of avoidance, to now use SE_PetAvoidance. +MAG AA: Elemental Durability will now add to pet max hp with bonus, SE_PetMaxHP. + +REQUIRED SQL: utils/sql/svn/2185_required_aa_updates +OPTIONAL SQL: utils/sql/svn/2185_optional_NPCFlurryChance_rule + +==08/12/2012== +Bad_Captain: Fixed an issue when using bots where you would not get xp when your pet did most of the damage and you are not grouped. +Bad_Captain: Added rule to enable receiving xp from bots not in your group. Defaults to false. + +OPTIONAL SQL: utils/sql/svn/2183_optional_bot_xp_rule.sql + +==08/08/2012== +Kayen: Updates to critical hit calcuations to be consistent with live. + *Baseline critical rate is determined by DEX stat (255 dex = 2%),this baseline is then modified by item,spell and AA critical chance bonuses. + *Pet critical baseline is determined by the rate from AA effects that give pets the ability to critical. (Can not crit w/o this effect) +Kayen: Slay Undead effect will now be working consistent with live. + *Slay rate is no longer effected by critical hit rate, it uses it's own predetermined rates from the spell effect data. + *Damage modification will now be much closer to that of lives utilizing the spell effects damage modifier. + *Example(AA Slay Undead I - Rate: 225 (2.25%) Damage Mod: 680%) + *Note that that spell effects using Slay Undead (Holyforge) will stack with AA and increase the damage and the rate. +Kayen: Crippling Blow's derived from spell effects will now be calculated consistent with live. + *Cippling blow chance is determined by modifying your critical hit chance. + *Example (Savage Onslaught - 200% chance to crippling blow) If you have a base chance to critical hit of 10% + and you score a critical hit, you will then have a 20% chance to crippling blow. +Kayen: Fixed effect for AA 'War Cry' will now provide group fear immunity for duration. +Kayen: Few new AA's added including shaman Ancestral Aid and Spirit Channeling. + +Following AA effects are now implemented as bonuses: You must update your 'aa_effects' table with 'required_aa_updates.sql' for these to work. +MISC AA: 'Combat Fury', 'Fury of the Ages' will now calculate from bonus SE_CriticalHitChance. +MISC AA: 'Veteran's Wrath' will now calculate from bonus SE_CriticalDmgMod. +MISC AA: All AA that give pet criticals will be calculated from SE_PetCriticalHit. +PAL AA: 'Slay Undead', 'Vanquish Undead' will now calculate from bonus SE_SlayUndead. (Holyforge now works correctly) + +REQUIRED SQL: utils/sql/svn/2178_required_aa_updates + +==08/06/2012== +Kayen: Fixes to rev2176 - Double Attack, EndlessQuiver, Run Speed. +REQUIRED SQL: utils/sql/svn/2176_required_aa_updates will need to be applied again for Run Speed fix. + +==08/05/2012== +Kayen: Complete revision of how double attack chance is calculate to be consistent with live. +Kayen: Berserker 'Frenzy' skill attack will now function as it does on live. Gives a chance for 1-3 attacks using frenzy skill specific damage. +*Note: This will be a considerable nerf to bersekers because it was previously coded to give actual melee rounds using weapon damage. +Kayen: Implemented a few miscellaneous new AA's, mostly from DODH. (These will show in OoW Window in Titanium) +Kayen: Added almost all remaining spell effects into spdat.h (~90% defined). +Kayen: Alternate Advancement Update: In the process of coverting most of the hard coded AA data out of the source and into the 'aa_effects' table +using live data. These effects are then reimplemented using the bonus system in the broadest possible way, most of these effects will +also be useable as regular spell/item effects. This will allow developers in the future to adjust,implement and customize AA effects +without requiring source changes. Beware in doing this, many AA effects that were previously either implemented incorrectly or with values +not consistent with live data will be adjusted to be as accurate as possible. + +Following AA effects are now implemented as bonuses: You must update your 'aa_effects' table with 'required_aa_updates.sql' for these to work. +MISC AA: All innate run speed AA's have been converted to use bonus SE_BaseMovementSpeed. +MISC AA: All bind wound related AA have been converted to use bonuses, SE_ImprovedBindWound, SE_MaxBindWound. +MISC AA: Shield Block will now be calculated using bonuses, SE_ShieldBlock. +MISC AA: Sinister Strike will now correctly allow your off hand weapon to recieve a weapon damage bonus, SE_SecondaryDmgInc. +MISC AA: Strengthened Strike/Vicious Smash/Kick Mastery will now accurately to live add skill damage to respective special abilities. +MISC AA: StrikeThrough, Tactical Mastery - will now be calculated from bonus, SE_Strikethrough2. +MISC AA: Ferocity, Knight's Advantage ect - will now be calculated from bonus SE_DoubleAttackChance. +MISC AA: Harmonious Attacks, Bestial Frenzy - will allow double attack chance using bonus SE_GiveDoubleAttack (any class can be given this) +MISC AA: Weapon Affinity, will now use SE_ProcChance. +MISC AA: PunishingBlade, SpeedoftheKnight will now allow an extra 2 Handed attack using bonus, SE_ExtraAttackChance. +BRD AA: Fleet of Foot will now correctly allow bards to run over the speed cap using, SE_IncreaseRunSpeedCap. +WAR AA: Previously non functional 'Stalwart Endurance' and 'Dauntless Preservence' now working using bonus, SE_FrontalStunResist +MNK AA: HightenedAwareness - Implemented using bonus, SE_BlockBehind. +MNK AA: Technique of Master Wu - Will now correclty use a random monk special attack, chance for double/triple attack as SE_DoubleSpecialAttack. +MNK AA: Dragon Punch - Implemented as now using bonus SE_SpecialAttackKBProc, (base1 = 100 [25% proc chance at 100] base2 = skill) +RNG AA: Endless Quiver - Implemented as SE_ConsumeProjectile which gives a percent chance NOT to consume an arrow. (EQ base1 = 100) +RNG AA: Archery Mastery- Implemented as SE_ArcheryDamageModifier which gives a percent increase to archery attacks. +BER AA: Throwing Mastery - Implemented using SE_DamageModifier. +BER AA: Blur of Axes, Vicious Frenzy - will now correctly add skill damage to 'Frenzy' skill attacks. +ROG AA: Triple Backstab, SiezedOpportunity, Chaotic Stab - as bonus, SE_TripleBackstab, SE_FrontalBackstabChance,SE_FrontalBackstabMinDmg. + +REQUIRED SQL: utils/sql/svn/2176_required_aa_updates +Optional SQL: utils/sql/svn/2176_optional_FrenzyBonus_rule +Optional SQL: utils/sql/svn/2176_optional_aa_expansion_sof_fix (Allow AA to show proper expansion in SOF+ clients) + + +==08/01/2012== +Bad_Captain: Fixed bot compile issue introduced in Rev 2171. +Bad_Captain: Integrated Kayen's skill attack code with bots' code, as well as other changes from Rev 2171. + +==07/31/2012== +Akkadius: (KLS) Fix for global_player.pl synchronization. There was an issue where certain subroutines were not passing as global + +==07/26/2012== +Kayen: Fixed: SE_ImmuneFleeing - Will now disable fleeing if used after mob begins to run, will not effect fleeing from fear. +Kayen: Implemented Perl MOB Quest Object SetDisableMelee(1=Disabled) - Prevents the ability to auto attack. +Kayen: Implemented Perl MOB Quest Object IsMeleeDisabled() +Sorvani: corrected build name for queryserv project in all the various build types. + +==07/24/2012== +Kayen: Implemented: SE_HundredHands (incorrectly marked as already implemented) - Increases/Decrease actual weapon delay by % of value. +Kayen: Implemented: Missing modifications from the Archery/Throw damage pathways. + *SkillAmount/SkillDamageTaken mods, ability to Block/Dodge/Parry ranged attacks (Can not riposte), other new focus/mod effects. +Kayen: Fixed: aaThrowingMastery will no longer be applied 2x per throw. +Kayen: Implemented: Damage bonus to skill attacks from specific armor slot AC (ie KICK from BOOT AC) can adjust with rule 'SpecialAttackACBonus' +Kayen: SE_MinDamageModifier should now apply to skill specific effects and to special attacks. +Kayen: Implemented: Complete revision of SE_SkillAttack. (+ required various fixes/adjustments to functions related to special attacks) + *This spell effect performs a physical attack from a specific skill with a set weapon damage value and chance to hit modifier. + *Attacks will now calculate correctly and use their actual respected pathways utilizing all skill specific mods/bonus. +Kayen: Implemented Perl MOB Quest Object DoMeleeSkillAttackDmg(target, weapon_damage, skill, chance_mod, focus, CanRiposte) +Kayen: Implemented Perl MOB Quest Object DoArcheryAttackDmg(target,NULL,NULL, weapon_damage, chance_mod, focus) +Kayen: Implemented Perl MOB Quest Object DoThrowingAttackDmg(target,NULL,NULL, weapon_damage, chance_mod, focus) + +Optional SQL: utils/sql/svn/2171_optional_SpecialAttackACBonus_rule + +==07/18/2012== +Kayen: Implemented NPC Special Attack 'K'. Immune to Dispell. +Kayen: Fixed SE_RangedProc to be applied after zoning, calculate proc rate correctly, utilize numhits. +Kayen: Fixed a bug that would cause NPC procrates to be incorrectly effected by equipped weapon delay. +Kayen: Modified functions related to obtaining proc chance to no longer recalculate the 'attack rate' every swing. +Instead it will use the already calculated attack timer values. (Not implement for BOTs, but should be no compile problems). +Kayen: Fixed for buff issue with rev2167. + +==07/16/2012== +Kayen: Implemented: SE_SpellProcChance, SE_CharmBreakChance, SE_BalanceMana, SE_DotCritDmgIncrease, SE_TripleAttackChance, SE_Taunt + SE_BardSongRange, SE_ACv2, SE_ManaRegen_v2, SE_SpellCriticalFocus, SE_AdditionalHeal2, SE_HealRate2, + SE_CriticalHealOverTime2, SE_CriticalHealChance2, SE_SkillDamageAmount2, SE_LimitSpellSkill, SE_LimitClass + SE_LimitExcludeSkill, SE_ShieldBlock, SE_BlockBehind +Kayen: Implemented: SE_Empathy - A focus limited debuff which causes (spells/skill attacks) cast on target to do +X amount more damage. + *This spell effect is often limited using SE_LimitSpellSkill. + *(Ie. If empathy value is 1000 and limited to evocation, all nukes on target using evoc. will get +1000 damage) +Kayen: Implemented: SE_SpellPowerIncrease - Can be used to apply a worn/buff focus effect with limits for... + 1) Additional bard instrument modifiers. + 2) Healing/Damage focus to bard songs. (Note: This is only effect that can do this). + 3) Increase effectiviess of casted/disc Skill Attacks. + 4) General use stackable Healing/Damage focus. + 5) Increase the value of melee runes by focus amount. +Kayen: SE_HPtoMana will no longer drain your HP before the spell is finished casting. Effect now handled as a bonus using best value. +Kayen: SE_SpellVulunerability will now correctly calculate and apply the highest value if target has mulitple effects. +Kayen: Fixed an issue where DOTs cast by an NPC on another NPC would not generate hate per tick. Improved how we calc damage from DOTs. +Kayen: SE_BlockSpellEffect was implemented incorrectly and has been revised to SE_NegateSpellEffect. + *It is not meant to block buffs, rather it negates the specific spell bonuses or effects from buffs you already have. + *Currently functional with any effect that is handled under bonuses, and focus effects. +Kayen: Vastly improved how we handle melee/spell runes/partial mitigation runes ect. Should be significantly more efficient. + *Melee/Spell Mitigation runes and ManaAbsorbPercentDamage effects will now use the best mitigation value if multiple effects. +Kayen: Improved the process we use to get spell/item focus effects. An initial check if the client has a specific focusType +is done while checking for item/spell bonuses. GetFocusEffect will no longer check all inventory and buff slots every cast for the +focusType if that focusType doesn't exist on the client. This should improve performance since these checks are done 10-20x per cast/proc. + +==07/15/2012== +Secrets: Fixed an issue with saylinks above 255 characters. If you do want to use saylinks above 255 characters, it is reccomended you adjust the varchar field in the saylinks table to a "TEXT" field, which supports up to 65535 characters. + +==07/13/2012== +Bad_Captain: Fixed another bug that allowed pets to steal xp when using bots. +Bad_Captain: Bots: Persisting spell & discipline timers. +Bad_Captain: Bots: Bot pets will no longer continuously try to get behind a mob if they are tanking. +Bad_Captain: Bots: Fixed potential duplicate lore item bug when trading with bots. +Bad_Captain: Bots: Fixed bot compile issue from Rev 2160. +Bad_Captain: Bots: Fixed multiple issues with #bot spawn and #bot botgroup load (c0ncrete). +Bad_Captain: Bots: Implemented new #bot defensive command for Warriors and Knights. Includes most of the code required to implement disciplines for bots. Requires disc AI & disc lists. + +Required SQL: utils/sql/svn/2164_require_bots_bottimers.sql + +==07/09/2012== +Kayen: Fixed issue causing npc special attacks (kick/bash ect) to do extra damage. +Kayen: Fixed major crash bug with rev2159. +cavedude: AFTERDEATH npc_emote will now use emoteid instead of npcid. + +==07/08/2012== +Kayen: Implemented: SE_SkillProc, SE_SkillProc2 which can be limited by SE_LimitToSkill - Procs an effect when a specific skill is used + *Functional with all melee skills, special attacks, backstab, taunt, dodge,block,parry,riposte. +Kayen: Added checks to attack process to prevent mob from performing certain actions after being killed (riposte, defensive proc ect). +Kayen: Fixed bug where if client had delay death effect NPC would not be able to attack them if HP < -11, clients without delay death will now bleed again. +Kayen: Implemented: SE_CastingLevel2, SE_ReduceHate +Kayen: Fix to bug where some spell procs and defensive procs were not loading after zoning. +Kayen: Updated spdat.h with many new live spell effects that were previously 'unused'. + +==07/05/2012== +Kayen: Implemented: SE_Manaburn: Drains mana for damage/heal at a defined ratio up to a defined maximum amount of mana. +Kayen: Implemented: SE_CastonNumHitFade: Casts a spell when a buff fades due to its numhits being depleted. +Kayen: Implemented: support for all remaining live spell effects that use 'numhits'. + Significantly optimized how all spell effects that utilize 'numhits' are handled throughout the source. +Kayen: Fixed: SE_DefensiveProc - Proc rate will now be calculated similiar to live. *Spell specific rate modifcations will now work. +Kayen: Added Rule: Combat:AvgDefProcsPerMinute (Default = 2.0) Determine defensive procs per minute. +Kayen: Added Rule: Combat:DefProcPerMinAgiContrib (Default = 0.075) Determines how much agility effects proc rate. + +Optional SQL: utils/sql/svn/2159_optional_defensiveproc_rules + +==06/29/2012== +Kayen: Implemented Perl NPC Quest Object GetSlowMitigation() +Kayen: Implemented Perl NPC Quest Object GetAttackSpeed() +Kayen: Implemented Perl NPC Quest Object GetAccuracyRating() +Kayen: Fixed: Slow Mitigation (was not loading from dbase),optimized its application in source and added lives slow mitigation messages. + Slow mitigation Messages: 'slighty' 0.00 - 0.25 'partial' 0.25-0.75 'mostly' 0.75-1 +Kayen: Implemented: SE_AttackSpeed4: 'Inhibit Melee' effect works different then regular haste/slow effect + 1) If target is hasted (or no haste) Inhibit Melee will decrease attack speed by the Inhibit Melee value. + 2) If target is slowed Inhibit Melee is applied as a percent change to remaining attack speed after regular slow. + ie. You cast 75% Slow (25 attack speed remains) then apply 25% Inhibit melee (18.75 attack speed remains): Final slow 81.75% +Kayen: Added Rule: Spells:CharismaEffectiveness (Default = 10) Deterimes how much resist modification charisma applies to charm/pacify checks (10 CHA = -1 resist mod). +Kayen: Added Rule: Spells:CharmBreakCheckChance (Default = 25) Determines percent chance for a charm break check to occur each buff tick. +Kayen: Reworked code to that handles charm breaks/lull to be accurate to live and more functional in general. + 1) Each charm buff tick there is a default 25% [Rule.CharmBreakCheckChance] chance to trigger a spell resistance check. + 2) Spell resistance check then adds an additional bonus resist modification based casters Charisma at a default + ratio of 10 CHA per -1 resist mod [Rule.CharismaEffectiveness] + 3) If resisted (ie Charm is to break) Total Domination AA is then applied to give another chance to maintain the charm. +Kayen: Implemented: SE_AdditionalHeal: Focus Effect that adds an additional heal amount to the casted spell. +Kayen: Implemented: SE_CastOnCure: Casts a spell on cured target. +Kayen: Implemented: SE_CastOnCurer: Casts a spell on the curer of the target. +Kayen: Modified: Mob::TryFadeEffect to avoid interaction wtih Twinproc effect. +cavedude: (Uleat) Multiple changes and additions to doors, per the forums. + +Optional SQL: utils/sql/svn/2156_optional_charm_break_rule.sql + +==06/25/2012== +Trevius: Reviewed and Committed the below by Kayen: +Kayen: Added Rule: AvgSpellProcsPerMinute (Default = 6.0) Determines proc rate of spells applied from sympathetic focus effect +Kayen: Added Rule: ResistFalloff (Default = 67) Max that level that will adjust our resist chance based on level modifiers +Kayen: Adjusted Mob::TrySpellOnKill() + - Will now check AA, Items and Buffs for SE_SpellOnKill (Same logic problem as TrySpellOnDeath) + - Level of the npc you killed and the spell_id used to kill it now both passed into the function. + - SE_TrySpellOnKill will now correctly check if there is a min level of the npc required for the spell effect to work (max value in spelldat) +Kayen: Implemented: SE_SpellOnKill2: For deterimental spells where landing the kill gives x% chance to give a buff. +Kayen: Sympathetic proc now calculates chance to proc a sympathetic spell accurately like live using Procs per minute. +Kayen: Adjusted Mob::TrySpellOnDeath() - Now checks all avialable effects and trys to use each one. +Kayen: Fixed SE_TargetsTarget - Target's Target must be in CombatRange to recieve the effect (Verified from live) +Kayen: Fixed TryApplyEffect - No longer incorrectly lands on the target's target's taget. (Example of spells that use this are Shaman Mending Couterbias) +Kayen: Adjusted Twincast to now select the buff with the highest chance to Twincast and use that, it will also stack with item focus and AA (when implemented). +Kayen: Removed the mana cost portion from twin casting a spell. +Kayen: Fix for SE_SympatheticProc - The focus effect now checks all items/buffs for the focus effect and adds all returned proc spellid's to a list from which one is randomly selected. +Kayen: Implemented SE_FF_Damage_Amount - Focus/Buff that adds damage to the casted spell. Usually found on spell group limited augments and stackable with SE_SpellDamage. +Kayen: Adjusted SE_SpellDamage to be handled properly as a focus effect. +Kayen: Extra damage from Focuses is now calculated correctly for DoTs +Kayen: Implemented Perl NPC Quest Object SetSpellFocusDMG(focus amount) - Focus all npc direct/dot damage spells by value +Kayen: Implemented Perl NPC Quest Object SetSpellFocusHeal(focus amount) - Focus all npc healing spells by value +Kayen: Implemented Perl Mob Quest Object ModSkillDmgTaken(skill_num,value) - Set a weakness/bonus of weapon attacks to a Mob + - Example: mob can be set to take 5% more damage from blunt weapons - ModSkillDmgTaken(0,5) + - Stacks with spell/item bonuses - Setting skill to -1 will effect all skills +Kayen: Implemented Perl Mob Quest Object GetModSkillDmgTaken(skill_num) - Returns only the quest skill mod for specified skill +Kayen: Implemented Perl Mob Quest Object GetSkillDmgTaken(skill_num) - Returns the total skill damage taken mod for the specified skill (item+spell+quest_mod value) +Kayen: Implemented Perl Mob Quest Object SetAllowBeneficial(false) - Allow a specific Mob to be healed/buffed by a client +Kayen: Implemented Perl Mob Quest Object GetAllowBeneficial() - Returns SetAllowBeneficial flag +Kayen: Implemented Perl Mob Quest Object IsBeneficialAllowed(target) - Returns if target can have beneficial spells cast on them +Kayen: Implemented Perl Mob Quest Object ModVulnerability(resist type, value) - Set Spell Vulnerability of mob by resist type(0-9) ALL = -1 + - Example: mob can be set to take 5% more damage from Fire spells (2,5); + - Applied effect will stack with spell buff effects. + - If a resist type SPECIFIC and a resist type ALL mod are applied to the same MOB, the SPECIFIC value will be used if spell cast on MOB is the same resist type. +Kayen: Implemented Perl Mob Quest Object GetModVulnerability(resist type) - Return the quest applied value for each resist (ALL = -1) + +Optional SQL: utils/sql/svn/2154_optional_rule_spell_procs_resists_falloff.sql + +==06/22/2012== +Secrets: (demonstar55) Spells now display to all clients and can be filtered as such. +Secrets: Damage Shields now go to the proper filter, and do not show the non-melee damage to everyone. + +==06/03/2012== +Akkadius: Added the ability to run a dual-stack global_player.pl with the existing player.pl that will run locally in quests/zone/player.pl or quests/templates/player.pl + - This allows the player.pl to be ran globally without interference of a local script (quests/zone/player.pl OR quests/templates/player.pl) + - 'global_player.pl' simply needs to be added to quests/templates/global_player.pl to be parsed otherwise it will not be loaded + - 'global_player.pl' parses after the local player.pl + - 'global_player.pl' parses all of the same events a regular player.pl does and will work with any new subroutines added to it in the future + - Thanks Derision for being a pair of eyes on a part of the diff for this + +==06/02/2012== +cavedude: Exported quest::addldonwin(wins, theme); and quest::addldonloss(losses, theme); + +==05/30/2012== +Bad_Captain: Fixed bug that allowed pets to steal xp when using bots. +Bad_Captain: Bots: Fixed bug that allowed bots to sometimes cast HoT buffs during combat instead of HoT heals. +Bad_Captain: Bots: Tweaked bard casting chances to restore twisting of multiple songs. +Bad_Captain: Bots: Implemented Shaman cannibalization during combat. + +==05/13/2012== +cavedude: Corrected the text merchants give when they refuse to sell to you. +cavedude: The graveyard timer will now resume when a dynamic GY zone boots, instead of resetting. +cavedude: When a corpse is moved to the graveyard, it will no longer attempt to use an instanced version of the zone. +cavedude: Added the ability to load NPC emotes from the DB. Wiki entry for this will be at http://www.eqemulator.net/wiki/wikka.php?wakka=NPCEmotes when I write it up. + +REQUIRED SQL: utils/sql/svn/2142_emotes.sql + +==05/06/2012== +Akkadius: Added the ability to load a default.pl script underneath quests/templates + +==05/05/2012== +Derision: Fixed reference to freed memory in Mob::BardPulse, plus a couple of minor memory leaks in Load/SavePetInfo +==05/01/2012== +Derision: Corpses no longer appear in Extended Target Auto slots. + +==04/30/2012== +Derision: Fixed some minor bugs with Extended Targeting (if the tank/assist/puller had a target before you allocated an XT slot to that function, you would not see the target unti they changed target). + +==04/29/2012== +Derision: UF+: Support for the Extended Target window (aside from the Raid target types). +Derision: Fixed Extended Target bugs where CurrentTargetPC targets were incorrectly cleared when leaving a group, and a CurrentTargetPC wasn't updating when they zoned in. + +REQUIRED SQL: utils/sql/svn/2136_extended_targets.sql + +Note: Default number of Extended Targets is 5. This is stored as a column in character_ +GMs can increase a players slots by targetting them and typing #xtargets where n is between 5 and 20. + +==04/22/2012== +Trevius: VoA - The AA Window now populated AAs. AA hotkeys can be created, but purchasing still needs work. + +==04/21/2012== +sorvani: Bard AE DoTs should now be affected by mods correctly. + +==04/19/2012== +cavedude: quest::buryplayercorpse will now despawn corpses in zones other than the client's current location, preventing dupes. +cavedude: Added quest::summonallplayercorpses (same syntax as quest::summonburriedplayercorpse) which will depop and summon all of the player's corpses, buried or not. +cavedude: Fixed a bug that caused some summoned corpses to use a static 5 minute decay timer. +cavedude: Changed player_corpses_backup schema to match player_corpses. If the rule is enabled, corpse backups will now be created for players that are high enough level to drop items to their corpses. +cavedude: Added a rule to determine whether or not to remove player corpse backups older than 2 weeks. +cavedude: #corpse now has options to depop a single player corpse, or every corpse belonging to a single player. +cavedude: You can now specify if a faction hit is temporary (is removed when player camps/zones) or whether or not to display the faction hit to the player in-game. +If temp in npc_faction_entries is set to: +0 (Default): Faction is permanent, player recieves a message. (Same functionality as we had previously.) +1: Faction is temporary, player does not recieve a message. +2: Faction is temporary, player recieves a message. +3: Faction is permanent, but player does not recieve a message. +cavedude: Added an optional argument to quest::faction to utilize the functionality and values listed above. +cavedude: Added minlevel and maxlevel to lootdrop_entries. The player credited with the kill (most hate) has their level checked against both columns, and if they are lower than the specified minlevel, or higer than the max, that item entry if it exists is removed from the NPC before it becomes a corpse. +cavedude: You can now specify if a NPC automatically repops (rerolls against their spawngroup) or depops after the reverse spawn timer is up. +If despawn in spawngroup is set to: +0 (Default): Do not depop or repop, no depop timer is set. (Same functionality as we had previously.) +1: Despawn the current NPC and repop the spawngroup after the associated spawn2 timer is up (including variance.) +2. Repop, after the value specified in despawn_timer is up (also uses spawn2 variance.) +3: Depop the NPC after the spawn2 timer is up + variance. +4. Depop the NPC after despawn_timer is up + variance. +Example: Setting despawn to 1 will despawn the NPC and immediately respawn the spawngroup after it has been up for the length of its spawn2 timer + variance. +Example: Setting despawn to 4 and despawn_timer to 3600 will depop the NPC after it has been up for an hour + variance. It then will respawn after its normal respawn timer is up. +cavedude: Fixed a crash in groups.cpp. + +Required SQL: utils/sql/svn/2133_required_faction_loot_despawn.sql (Backup your data first!) + +==04/17/2012== +Bad_Captain: Bots: Added Bot Group Messages and #bot groupmessages command to turn them on or off per bot or for all bots. +Bad_Captain: Bots: Fixed bugs that allowed more bots to be spawned than specified in the Bots:SpawnBotCount rule, fixed bug that allowed more bots to be created than specified in the Bots:CreateBotCount rule, and fixed slow buffing bug. +Bad_Captain: Bots: Various spell ai tweaks (including HoT spells should no longer block other heals from being cast in certain situations and hybrids should use less mana by healing less often). +Bad_Captain: Bots: Removed client xp gain from bots outside the player's group. + + +==04/14/2012== +Trevius: Fixed inspect for SoF+ clients, which was broken by Rev2128. + +==04/15/2012== +Derision: Added support for the 'Looking For Guild' window. + +Required SQL: utils/sql/svn/2129_required_lfguild.sql + +IMPORTANT NOTE: + +To use this feature, you must be running the querserv process. If you are not running queryserv, LFGuild will not work, but your server will +run normally in all other respects. + +Underfoot users can bring up the LFGuild Window by pressing the EQ buton and selecting Players/Looking For Guild. + +I think all other clients can only access the feature by clicking on the notice boards in the Guild Lobby. To allow them to do this, you must +add the following to the SUB EVENT_CLICKDOOR of your guildlobby/player.pl + +if(($doorid >= 5) && ($doorid <= 38)) +{ + $client->OpenLFGuildWindow(); +} + +==04/14/2012== +Trevius: VoA - Corrected a few alignment issues with the item struct and it should be fully aligned now. +Trevius: VoA - Identified a few new item fields including: Prestige, SkillModMax, SkillModExtra, and Power +Trevius: VoA - #summonitem and functions that summon items such as quests are now fixed. +Trevius: VoA - Identified multiple Membership fields, which corrects multiple issues such as limited inventory slots, assigned AA limit, level limit, shared bank, and more. +Trevius: Added packet length verification to all packet handling that was missing it. + +==04/13/2012== +Secrets: VoA - Character Creation is now functional. Fixed creation request struct (opcodes were right), and membership is now sent (characters can now be created!) +Secrets: VoA - Membership levels are now sent to the client via the SendMembership opcode. Hardcoded to be like older clients for now. Might need bag slots looked into; upon applying this, bag slots & items went missing. +Secrets: Updated #race command to support races up to 714 (as per the 4/12/2012 client) +Trevius: VoA - Items now display food/drink correctly and consuming them is now functional. +Trevius: VoA - Items no longer display UKNOWN SKILL for skill mods on some items that don't have skill mods set. +Trevius: VoA - Items no longer display Prestige on them. +Trevius: VoA - Buying from merchants is now functional, though selling to merchants is not yet. +Trevius: VoA - Disabled the Guild Member List temporarily to prevent guilded characters from crashing. + +==04/12/2012== +Trevius: VoA - Casting Spells and Click Effects on items is now functional. + +==04/11/2012== +Trevius: VoA - Corrected the Player Profile which should now be perfectly aligned. +Trevius: VoA - More Opcode and Struct corrections/adjustments. + +==04/09/2012== +Trevius: VoA - Identified/corrected some more opcodes and packet structures. +Trevius: VoA - Merchants now display items for sale, but purchasing is not yet functional. +Trevius: VoA - Added new packet for setting the max number of characters allowed to be created on an account (10 for now). + +==04/08/2012== +Trevius: VoA - Identified/corrected some more opcodes. +Trevius: VoA - Items can now be moved around through all slots. +Trevius: VoA - Updated structs that use inventory slots to use the new format. Still need to verify some of the struct changes. + +==04/07/2012== +Trevius: Initial addition of the VoA (March 21 2012) client. To enable, you must uncomment the VoA lines in /common/patches. +Trevius: Many of the core systems are basically functional, but this client is still in an early development phase and not recommended for normal players yet. +Trevius: Non-Player Race spawns will cause the client to crash, so it is recommended to test it in an empty zone. +Trevius: Also, items in the bank will cause it to crash, but normal inventory seems to work fine so far. +Trevius: You cannot create a character on this client yet, so you must use an existing character. +Derision: VOA: Fixed zoning. Adjusted spawn encoding. + +==04/02/2012== +JJ: Removed additional library and include directories from windows projects as they were not standard locations for all. + +==03/29/2012== +Sorvani: Resurrections effects will again be applied to characters who are in a zone where combat is not allowed (GL, PoK etc) when they receive a rez. + +==03/19/2012== +Bad_Captain: Bots: A few fixed for #bot stance command + +==03/19/2012== +Bad_Captain: Bots: Implemented bot stances. See forum post. + +Required SQL: +utils/sql/svn/2107_required_bot_stances.sql + +==03/2/2012== +Bad_Captain: Fixed potential crash in group roles code when a group member is removed. + +==02/28/2012== +Sorvani: Added check for ST_AEBard song before sending spell SPELL_NO_HOLD +Bad_Captain: Implemented Group Roles via UI (SoD+)- Set group roles, maintains across zones, save/load grouprole sets (load doesn't appear to work on UF- may be a client bug as client doesn't send request to the server for saved group roles as SoD client does) +Bad_Captain: Fixed FindType to allow for spell effects > 127. Observed by bots trying to cure corruption (effectid 369) when client has a mount (summonhorse effectid 113: 369-128-128=113). +Bad_Captain: Bots- fixed bot summoning of client corpses, fixed ability to spawn more bots than allowed via server rule (#bot botgroup load was not checking for SpawnBotCount rule), fixed bot issue with receiving buff with Group with Pets target type. + +Required SQL: +utils/sql/svn/2104_required_group_roles.sql + +==02/11/2012== +Secrets: Fixed two more hanging issues introduced regardless of the item discovery feature was on or off. +Secrets: Fixed a hanging issue introduced regardless of the item discovery feature was on or off. Keep in mind the item discovery feature still takes a reasonable amount of time to work, and this will be fixed in a future update. +Secrets: Added OP_IncreaseStats for use with the #incstats command (and other things, in due time!) +Secrets: Added QueryServ to the VS2008 project in preparation of moving eventlog, hackers, discovered items, and other non-essential database tasks over to it. + +==02/03/2012== +Trevius: Added accout_status field to the discovered_items table to make it easier to remove accidental GM item discovery from the table. + +Required SQL: +utils/sql/sv/2099_required_discovered_items_account_status.sql + +==01/28/2012== +Trevius: The version field in the zone_points table can now be set to -1 if you want all versions of the zone to inherit the same zone points. +Trevius: Added extra option to the "#npcspawn create" command that will try to create the npc_type ID within the standard range for the current zone (zone_id * 1000). Usage: "#npcspawn create 1" + +Required SQL: +utils/sql/sv/2098_required_zonepoint_version_changes.sql + +==01/14/2012== +Trevius: Added Grid ID to the #npstats command output. + +==01/06/2012== +Trevius: Increased MAX_ITEM_ID to 200000 to match the new item ID max. This is only used for Min Status on items. + +==01/02/2012== +Trevius: Fix for loading buffs on suspended pets. + +==12/12/2011== +Trevius: Pets set to a petpower of -1 in the pets table will scale stats to the petpower like they used to. + +==12/11/2011== +Bad_Captain: Fixed hatelist/aggro bug I introduced in rev 2087 causing NPCs to attack the closest client instead of the top of their hatelist. +Bad_Captain: Updated some live opcodes for EQExtractor. + +==12/06/2011== +Leere: Added IsFromSpell to Attack() to allow its use from spell effects. This allows the Rampage AA to actually hit mobs again. +Leere: Added a rule for the minium hasted delay that can be reached for weapons. Default set to 400 based on best guesses from the Moss-Covered Twig tests posted on the net. +Congdar: (ptarp)compile warnings in windows +Bad_Captain: Fixed Enchanter's Mezmerization AA - it was extending buffs 35%, now extends mez spells by 1 tick / rank. +Bad_Captain: Fixed NPC aabonuses issue(s) - NPCs were trying to access nonexistent aabonuses within combat code (hatemod as an example) +Bad_Captain: Temporary Fix on crash due to non-player spell proc from Monster Summoning Pets until I can figure out the cause. +Bad_Captain: Bot changes / fixes including maintaining HP & Mana after camp/zone/death, ability to maintain aggro over clients, taunting toggle command (#bot taunting [on|off]), delay death AA, and others. Please see http://www.eqemulator.org/forums/showthread.php?p=205139#post205139 for all Bot changes. + +Required SQL: 2087_required_bots_hp_and_mana_and_spell_updates.sql + +==12/05/2011== +Trevius: Crash fix for trying to zone into a flagged zone without having the flag. + +==12/04/2011== +Secrets: Added QueryServ for VS2010 x86/x64. QueryServ allows queries to be sent to it for use on an external database, through the means of packets. +Currently, the only thing it logs is a #command input, but that will be expanded onto soon. A suggestion for our developers would be to move any statistic logging stuff (hackers table, eventlog, etc) over to it eventually +to save processing power on the main executable's machine. This will allow for things like discovered items to work a bit more better resource-wise. +Akkadius: Added #advnpcspawn setversion (version#) - This will change the instance version in which the targeted NPC resides in +Akkadius: Added #npcedit setanimation (stand, sit, crouch, dead, loot (0-4)) so that setting animations are much easier. This also makes animations much more reliable setting them with the spawn packet rather than through Perl. +Akkadius: Added /MP switch to VS2008 so that it will compile with multiple processors +Akkadius: Fixed compiles for VS2008 from the Queryserv commit +Secrets: Added #qtest , which will populate the remote database through QueryServ with information as a test only. Requires 255 status by default. +Trevius: LDoN points are now capped at 2,000,000,000 instead of ~16,000, and no longer roll back to 0 if they exceed the cap limit. + +Reccomended SQL: (ON QueryServ's database, NOT the main db) utils/sql/queryserver/2079_queryserver.sql + + +==12/01/2011== +Secrets: v90 toolset bugs reported on forums. In order to use Perl 5.14, you MUST upgrade to VS2010 (preferrably ultimate), if you do not have vs2010, it will default to Perl 5.10 and you will be unable to use the much more stable Perl 5.14, an upgrade is reccomended. +Secrets: Fixed Windows x64 rulesys bug where it would store them as an x64 version of an int (long long) and mess up the ordering of pointers, resulting in certain rules not loading properly. +NOTE: For those having issues compiling vs2010, there will be a guide up shortly to help you set up your dev environment. In the meantime, make sure your "Additional Include" and "Additional Library" paths +are correct and pointing at the right location. Failure to do so will result in mysql header/lib and perl header/lib issues. If you are looking for zlib x64, it's in SVN. + +==11/30/2011== +Secrets: Implemented Visual Studio 11 and Visual Studio 10 project/sln files. +Secrets: Added x64 configuration settings to VS11 and VS10 project files/SLN files. +Secrets: Changed WIN32 define to _WINDOWS across the board. In the case where WIN32 is needed over WIN64 (ie; assembly references) there is still WIN32 defines and a new WIN64 define. +Secrets: Upgraded ActiveState Perl lib define to 5.14 -- others still work but it is strongly reccomended to use 5.14 as it contains less memory leaks. +Secrets: Added Zlib 1.2.3 x64 to the SVN for use with the windows solution files. +Secrets: Fixed vs2008 project files to reflect the new _WINDOWS define +Secrets: Perl 5.14 Compile Fix + +NOTE: Since VS11 is in beta, I only set up the debug options. You should use Vs2010 Ultimate to compile in x64 mode. +NOTE2: Perl 5.14 is stable, but if you absolutely need to use 5.10, ctrl f search for "perl514.lib" and replace it with "perl510.lib" +NOTE3: A forum post will be made for help with compiling Windows x64 on EQEmulator.org, it is an experimental feature but has less issues than x64 linux, imo. + +==11/28/2011== +Leere: Fixed a problem with no drop items inside of bags when trading with NPCs. Pets retain buff based procs through zoning now. + +==11/26/2011== +Leere: NPCs now have the ability to ignore level limits for stuns. This ability can be turned off via rule. Also added a rule for what the base stun immunity level is for spells with a max level of 0. +Leere: NPCs (and thus pets) are now able to equip items in non-visble slots. They will also look inside of bags that are traded to them for those items. +The pets table was expanded. It can now map to several npc IDs for the same pet type string, based on pet power. (This has replaced the old approach of letting pet power be a direct boost to NPC stats. The intention is to now have distinct npc_type table entries for the various pet power levels that a pet supports.) Pet naming and pet control was likewise moved out of code logic and to the pets table. (The default pet control is now petFamiliar rather than petOther.) Pets can now have items already equipped when summoned. The equipment sets used for that can be shared between pets and also be nested up to a depth of 5. +Pets are no longer stored as part of the profile blobs. +Pets (charmed and summoned) belonging to clients should no longer attempt to assist other NPCs if they hear a call for help. + +Required SQL: +utils/sql/sv/2069_required_pets.sql + +==11/21/2011== +Akkadius: Implemented NPC Special Attack 'Z'. This will keep clients from being able to attack an NPC altogether similar to live. + +==11/17/2011== +Congdar: Fix charms Platinum Disc and Gold Pin. You'll need updated items\CHRMGold.pl and \items\CHRMPlatinum.pl once pushed to the quest repository. + +==11/17/2011== +Lerxst: Fixed crash when calculating random focus effect from an augment when the base item had no focus effect. +Also added some bulletproofing in case that isn't the only place an invalid spell id might be passed. + +==11/16/2011== +Akkadius: Added the ability to specify doors/objects/ground spawns to load for all versions of the same zone regardless by setting the value to -1. +This reduces serious redundancy of copying the same data over and over for instances. + +Required SQL: +utils/sql/svn/2062_required_version_changes.sql + +==11/15/2011== +Trevius: Potential zone crash fix for Discovered Items pointed out by Congdar. + +==11/09/2011== +Trevius: Fixes for issues caused by the extra SoF+ item click checks. + +==11/05/2011== +Trevius: Added extra checks for SoF+ item clicks. + +==11/01/2011== +Trevius: Added Discovered Items table to track who discovered an item for the first time and when. +Trevius: Added new EVENT_DISCOVER_ITEM player event. It exports the item id to the $itemid variable. +Trevius: Added Rule Character:EnableDiscoveredItems to make item discovery tracking optional. + +Required SQL: +utils/sql/svn/2057_required_discovered_items.sql + +Optional SQL: +utils/sql/svn/2058_optional_rule_discovered_items.sql + +==10/31/2011== +Leere: Changed raid leadership ability costs to match client expectations. +Leere: All stuns should now treat a max level of 0 as meaning level 55. + +==10/24/2011== +Akkadius: Mysql CLI changes +Akkadius: Preliminary work to some future additions, added int32 Database::GetGuildDBIDByCharID(int32 char_id) + +==10/23/2011== +Akkadius: Another memory leak in parser.cpp, NewEventList never deallocated +Akkadius: Reduced code checking redundancy in several places regarding functions that reference ExecWeaponProc, only valid invalid spells are checked and logged. +Akkadius: (Lerxst/Akkadius) Approximately 8 memory leak fixes based on findings from 'cppcheck', some found in loot, popup windows and a few other places. +Akkadius: (Lerxst) Various quest object fixes, sanity checks and initiator adjustments +Akkadius: (Lerxst) Minor trading code adjustments +Akkadius: (Lerxst) Logic fixes with bot code +Sorvani: Critical hit filter should work again. +Secrets: Reverted my changes. + +==10/22/2011== +Derision: Underfoot: Extended Buffs should now last the correct time without needing to zone. + +==10/21/2011== +Derision: Fixed rez bug. +Akkadius: Fixed a spell crash that has been reported on The Hidden Forest with TryFadeEffect +Akkadius: Fixed a spell crash with ExecWeaponProc when a spell or weapon procs a spell that does not exist, you can catch the invalid spells in your spells log if you have them enabled + +==10/20/2011== +Trevius: Fix for Perl Doors GetID() command. +Akkadius: Mysql #command CLI - Fixed an issue where the query string was being escaped +Akkadius: Mysql #command CLI - Added switches -s and -h. Switch -s providing space between the select lines in a query, -h highlighting every other row to be more readable + +==10/19/2011== +KLS: (lerxst2112) Client::Message memory leak +Trevius: Potential crash fix for MoveTo() being used on an NPC that has a grid with wandertype 3. + +==10/14/2011== +Leere: More than 6 members of a raid can now receive raid experience. +Sorvani: Added AE Rampage message and filter + +==10/11/2011== +KLS: Added Ebon/Radiant crystal reclaim for the clients I have.(Titanium/SoD/Underfoot) +KLS: Seperated buffs from the player profile. +KLS: Seperated buffs allowed me to fix the counters on buffs for the newer clients across zone lines, as well as melee and magic runes are no longer limited to 65k as they are no longer uint16s. +KLS: Added ability to save variables to items. Via $client->SetCustomItemData(13, "SomeVariable", 1024); #give our mainhand some variable. +KLS: Added ability to retrieve items from the inventory in perl with $client->GetItemInInventory(slot_id); +KLS: Fixed Item::IsAugmentable() to return correctly if the first augment slot is not used. +KLS: Updated for latest code and readded Derisions' cross-zone /inviting. I've been testing it for a while now and can't find any real issues with it so I readded it. +KLS: Added class and race combo's into the database so they can be potentially changed; only SoF+ clients will correctly display this. +KLS: Fixed a bug in WorldDatabase::GetCharSelectInfo. +KLS: Changed internal faction values to go in order this change will break some quests in the short term but should be more intuitive in the long run. +KLS: Changed player.pl loading slightly: it will now look for player quests in the following order: .\quests\{zone}\player_v{instance_version}.pl, .\quests\{zone}\player.pl, .\quests\templates\player.pl +KLS: CanClassX functions are no longer hardcoded, they will now check the skillcaps data instead. +KLS: EntityVariables are now identified with a string instead of an int ex: $npc->SetEntityVariable("SomeId", 5), this shouldn't break any existing quests. +KLS: Fixed an issue where bard songs could get stuck in the *on* position even if the bard had turned singing off. +KLS: Fixed an issue where merchants would load and send items more than once in cases where npcs had merchants that shared merchant_ids in a zone. + +==10/09/2011== +Akkadius: Fixed an optional argument in client 'NukeItem' +Akkadius: Added two #npcedit commands; armortint_id and alt_currency_id +Akkadius: Exported $client->GetAggroCount();, this will get how many NPC's are aggroed on the client. This can also be used to determine when in combat. +Akkadius: Mysql ingame basic CLI to run select queries or execute queries from in-game, default account access 250, be careful when using this. +Akkadius: (Lerxst) Added a crash fix for spells that do not find a valid item in the database SE_SummonItem + +Optional SQL: +utils/sql/svn/2023_optional_mysqlcli.sql + +==10/01/2011== +Trevius: Added Destructible Objects for Underfoot. + +==09/25/2011== +Derision: Implemented Tune Of Pursuance AA. + +==09/24/2011== +JJ: Added hacker entry for possible instant camp disconnects. + +==09/23/2011== +Trevius: Destructible Objects now change damage states as they reach 75%, 50%, 25% and 0%. +Trevius: Added Perl Mob quest object SendUntargetable(to_client, in) to toggle the ability to be targeted. + +==09/22/2011== +Trevius: Initial implementation of Destructible Objects (SoD only for now). Most of the work was done by Derision. +To Create a Destructible Object, use the 'lastname' field in npc_types to set the model (ie. DEST_DEV_TENT1), and add "o" in the 'npcspecialatks' field. + +==09/21/2011== +Leere: Damage shields should no longer break root spells. + +==09/20/2011== +Trevius: New rule added to allow a cap for GM skill training on specializations (default is 50). +Trevius: #maxskills will now only set specializations to 50 each to prevent them from all resetting to 1. +Trevius: GM Trainers will no longer allow training more than 1 (or 2 if you have the AA) specializations above 50. +Bad_Captain: Added rule for Bot AAs by expansion, bot bash fix (bots with bask/slam skill could bash regardless of weapon(s) or shield), various bot AA fixes, added a few bot AAs + + +Optional SQL: +utils/sql/svn/2015_optional_specialization_training_rule.sql +utils/sql/svn/2016_optional_rule_bot_aa_expansion.sql + +==09/12/2011== +Leere: (Tabasco) Added a deque for signals received by an NPC. +Sorvani: Various message type fixes, most notably NPC Flurry/Enrage/Rampage and Pet Flurry/Rampage. + +==09/05/2011== +Akkadius: (Akkadius/Lerxst) Fixed Beastlord 1.5 and 2.0 pet clickies. + +==09/03/2011== +KLS: Added working Alternate Currency system for SoD and SoF(Untested but hopefully works). +KLS: Added two fields to merchantlist that allows one to filter which items are offered to players by minimum faction and minimum level. +KLS: Max stacks on items increased from 254 to 32767. +Akkadius: Modified items id cap to 200,000 +Akkadius: Changed Perl Client Object 'NukeItem' to use the new fields implemented by KLS's Alternate Currency revision: +$client->NukeItem(itemnum, [where_to_check, 1 = invWhereWorn, 2= invWherePersonal, 4 = invWhereBank, 8 = invWhereSharedBank, 16 = invWhereTrading, 32 = invWhereCursor]); Ex: $client->NukeItem(1001, 2); +Akkadius: (Lerxst) Fixed Enduring Breath bot code, adjusted level requirements and added Beastlord class case. + +Required SQL: utils/sql/svn/2004_charges_alt_currency.sql + +==09/01/2011== +Derision: Spell resist messages now use the same MT as fizzles (for chat filter purposes). + +==08/28/2011== +Bad_Captain: Rewrote Bot AAs to use aabonuses and GetAA(). Also added a few AAs. +Bad_Captain: Fixed Bot chance to hit. (they never missed with their primary weapon attacks, and only missed ~5% with their offhand) +Bad_Captain: Added WipeHateList to bot group attack, bot group follow, and bot group guard to allow target switching and keeps bots and their pets from attacking their target when you no longer want them to. (suggested by Criimson) + +==08/23/2011== +Trevius: Exported to Perl Mob: SetDeltas(delta_x, delta_y, delta_z, delta_h), SetLD(value), SetTargetDestSteps(target_steps). +Trevius: (SuperUserJD) SpellOnTarget fix for casting beneficial spells on group members while in a raid. + +==08/21/2011== +Derision: Added rule to prevent looting corpses when a player has an item on the cursor (to prevent item loss using Loot All). +Derision: Alcohol intoxication is maintained across zoning (can be turned off with a rule). +Derision: Intoxication level affects STR/STA/DEX/AGI/WIS/INT. +Derision: Added 'Update' button to #showstats window for SoD+ clients. + +Optional SQL: utils/sql/svn/1998_optional_intoxication_and_looting_rules.sql + +==08/18/2011== +Leere: Removed lore item from the restrictions for pick pocket. + +==08/13/2011== +Derision: Player pets will now show their owners name. UF users can turn this off in options. +Derision: SoD/UF: Beneficial Pet Buffs can be clicked off in the Pet Window. +Derision: Underfoot: Can now click off buffs in the short buff box. Removed registraion of HoT in patches.cpp + +==08/12/2011== +Bad_Captain: Fixed double class / race stats for bots. + +==08/09/2011== +Derision: Underfoot: OP_ClearSurname + +==08/05/2011== +Akkadius: Corrected a grammatical error with training disciplines. + +==08/04/2011== +Bad_Captain: Updated Bot::MeleeMitigation to match mob version. + +==08/02/2011== +Trevius: (Akkadius) Exported Freeze() and UnFreeze() to Perl Client. + +==07/27/2011== +Caryatis: Added Special Attack 'p' (Makes mob immune to pacify) +Caryatis: Added Special Attack 'j' (Tether - returns mob to spawn point if its distance away from spawn point is greater than its aggro range) +Caryatis: Added Special Attack 'J' (Leash - same as Tether except heals mob to full, strips all buffs and wipes the hatelist as well) +Caryatis: Fixed TGB(if you cast a group buff with a corpse or an npc thats not a client's pet as target, it will use your group instead) + +==07/26/2011== +Congdar: redo world container check + +==07/24/2011== +Caryatis: Added support for higher level invis and see invis(eg Ethereal Invisibility & Skylight Sagacity) +Congdar: update check to world containers + +==07/23/2011== +Congdar: (Criimson)Bot dyes (and tweaks) +Congdar: (lerxst2112)Bot dyes (and tweaks) +Bad_Captain: Fixed Bot GetAC() and GetATK() to work with #ShowStats + +==07/22/2011== +Congdar: update check for Lore + +==07/17/2011== +Congdar: (pfyon, Criimson)Various bot tweaks +Caryatis: Updated My/Showstats window. + +Required SQL: +utils/sql/svn/1974_required_bot_spells_update + +==07/16/2011== +Trevius: Exported AssignToInstance(instance_id) to Perl Client. + +==07/14/2011== +Caryatis: Implemented the Extra Skill Damage itembonus(+bash, +kick dmg) + +Optional SQL: +utils/sql/svn/1972_optional_extradmg_item_cap.sql + +==07/14/2011== +Leere: Fix for reading books in SoF and later clients. + +==07/13/2011== +Derision: OP_DisciplineUpdate for SoF and Underfoot (expanded Disc struct in UF player profile, added encode for Disc update). + +==07/12/2011== +Caryatis: Added Perl Object: $mob->SetBodyType(bodytype). +Caryatis: Fixed issue with swarm(and wake the dead) pets where they would not despawn if they spawned as the target died. +Caryatis: Changed when mobs enrage to 9%(from 14%) to match live, with a rule to customize the value if wanted. +Caryatis: Added rule to allow matching of new live rules for enrage(it was removed from all non player-pet npcs). + +Optional SQL: +utils/sql/svn/1968_optional_enrage_rules.sql + +==07/11/2011== +erde: FreeBSD compile fix for Rev1963 (no strncpy in FreeBSD). + +==07/10/2011== +Derision: Underfoot - Support for /emote +Derision: Underfoot - Support for Guild Creation via new window in Underfoot. +Derision: SoD+ - Changing group leader should update Group Leadership AA to that of the new leader. +Caryatis: Live like focus effect behaviour added(Improved Damage, Healing and Reduce Manacost focus effects will now be random if the spell contains both a min(effect_base_value) and max value(effect_limit_value) + +Optional SQL: + +utils/sql/svn/1962_optional_guild_creation_window_rules.sql +utils/sql/svn/1963_optional_rule_live_like_focuses.sql + +==07/09/2011== +Derision: Underfoot - Instrument modifiers / AAs should now improve runspeed. + +==07/06/2011== +Trevius: Fix for EVENT_ITEM_CLICK_CAST bugging Titanium clients. + +==07/05/2011== +Trevius: Fix for EVENT_ITEM_CLICK_CAST for SoF+ clients. +Trevius: (Akkadius) Added new rule Console:SessionTimeOut for adjusting console session timeout. + +Optional SQL: utils/sql/svn/1960_optional_console_timeout_rule.sql + +==07/02/2011== +JJ: Split wander type 4 to allow depop with/without spawn timer. Type 4 is with, type 6 is without. + +==06/26/2011== +JJ: Added functions to count all corpses, get corpse ids, and allow checking of items on player corpses + +==06/25/2011== +JJ: (Leere) Fix for bards in reagent code + +==06/24/2011== +KLS: Dest zone would buffer overflow if it was >= 16 characters, fixed. +KLS: Comment clean up + added EntityList::FindDoor($doorid) to perlXS +KLS: Fixed the PerlXS export for GetDoorByID and GetDoorByDBID +Sorvani: Added door object perl functions GetKeyItem(), SetKeyItem([value]), GetNoKeyring(), SetNoKeyring([value]). Example Usage: my$doorobj = $entity_list->FindDoor(6); $doorbj->SetKeyItem(10615); $doorbj->SetNoKeyring(1); +JJ: (Akkadius) Exported some task functions to perl. + +==06/23/2011== +JJ: (Sorvani) Stop forcing client to tutorial when button not selected. + +==06/22/2011== +Trevius: Added new Perl Mob quest object RemoveNimbusEffect(effectid) for removing permanent nimbus particle effects. +JJ: EVENT_CLICKDOOR now passes the zone version to perl. + +==06/20/2011== +JJ: Added message to client when using #instance destroy command. +JJ: Altered doors index to include version to help bring door ids below 256 in zones with multiple versions. Required SQL provided. + +Required SQL: utils/sql/svn/1946_doors.sql + +==06/19/2011== +JJ: (Leere) Fixed reagent consumption oddities + +==06/18/2011== +JJ: Depop with spawntimer reset option now works properly. Default use is changed to false. +KLS: Added some minor stuff +KLS: Added animation field to spawn2 that will control what the spawned npc will do at their guard point. 0 = eaStanding, etc etc +KLS: Tweaked books in SoF+ as they weren't displaying right; it may still not be perfect though I haven't tested it with every book in the game. Required SQL Provided. + +==06/17/2011== +KLS: Lowered resolution on QuestTimer process timer to actually allow you to use sub 1 second times. +KLS: Added quest::settimerMS(timer, duration) to allow timers not in integer second intervals. +JJ: Moved ENTERZONE event for items to execute before SCALE_CALC. + +==06/15/2011== +Secrets: Modified the pound command #reloadstatic to respawn doors after it despawns them. +Secrets: Made the default door dest zone be "NONE" for quest::createdoor's constructor +Secrets: Added potential fix for UCS assertion error under windows after being up for so long. + +==06/14/2011== +Secrets: Opcode to remove all doors. +Secrets: Perl door support. +Secrets: Perl objects added for Doors since first PerlDoors commit: SetSize, GetSize, SetIncline, GetIncline, GetOpenType, SetOpenType +Secrets: Added quest::createdoor(model, x, y, z, h, [opentype], [size]) . This spawns a door similar to quest::creategroundobjectfrommodel, and can be manipulated in the same way as other doors. To save it, get the door object using $entity_list->GetDoorByID(id), as it returns an entity ID (and preliminary DBID/DoorID is stored on the object itself) upon creation of the door. otherwise the door is temporary, and does not save the stored DBID. $doorobj->CreateDatabaseEntry() saves it and its temporary DBID + DoorID. +Secrets: Added door object perl functions GetLockpick() and SetLockpick([value]). + +==06/10/2011== +JJ: Added more tables to remove entries from when deleting character. + +==06/06/2011== +Secrets: Adjusted the default value for Character:SkillCapMaxLevel. It now defaults to 75 because PEQ only has skillcaps up to 75, and if the level is higher than 75 it will always return 0, breaking skills. (layer 8 issue fix) +Secrets: Made stat cap adjustable via rules, Character:StatCap. The client won't be in sync but that's what #mystats is for. 0 = feature disabled, defaults to 0. + +==06/05/2011== +Derision: Fixed /yell + +==06/04/2011== +Secrets: Fixed pet HP resetting if the pet is above 32k when zoning. +Secrets: Fixed player HP rollover bug (~495k hitpoints, rarely encountered) with AAs by temporarily casting it as a float. + NOTE: the client only displays 10 million hitpoints in titanium. Keep that in mind when designing items. Only issue is displaying them, unlike the rollover bug you won't die and it will show on show/mystats. +Secrets: Fixed damage on weapons above 255. They only display on SoF+ clients, and Titanium will simply show a cosmetic issue (an incorrect value). You can set an item to any value up to the max for an uint32 and it will work. +Secrets: Added rules for the low-level damage cap. There is a rule for the pre-10 cap, and one for the pre-20 cap. Defaults to 20 and 40 respectively. + +REQUIRED: You will have to recompile sharedmem with this update. Items will fail to load otherwise. + +==05/31/2011== +Trevius: Added EVENT_CLICK_OBJECT to player quest events. It exports variable $objectid which holds the entity id of the clicked object. + +==05/29/2011== +Secrets: (Akkadius) Added quest::createguild(name, leader) for creating a brand new guild. + +==05/28/2011== +Trevius: Added quest::getguildnamebyid(guild_id) for getting a guild name from the guild ID. + +==05/25/2011== +JJ: Implemented Arcane Tongues research AA. Chance for research success increases for each rank (10, 25, 50). +JJ: Implemented New Tanaan Crafting Mastery tradeskill AA. Each rank allows an additional tradeskill above 200. +Note: For servers with players who have tradeskills already above the limit without previously purchasing these AAs will freeze the chance to increase until they purchase the proper amount of NTCM AAs. + +==05/24/2011== +KLS: Changed Mob::NPCSpecialAttacks(atk, perm) to Mob::NPCSpecialAttacks(atk, perm, [reset = 1], [remove = 0]). +This should allow one to add and remove flags individually without having to reset everything each time. +ex: +$npc->NPCSpecialAttacks(RQ, 0); //would enable the npc to rampage and quad. +$npc->NPCSpecialAttacks(S, 0, 0); //Would enable the NPC to summon as well as rampage and quad by telling it to set S but don't reset the earlier flags. + +ex: +$npc->NPCSpecialAttacks(RQS, 0); //would enable the npc to rampage, quad and summon. +$npc->NPCSpecialAttacks(S, 0, 0, 1); //Would enable the NPC rampage and quad by telling it to set S but don't reset the earlier flags and also remove the flag being set. + +==05/23/2011== +JJ: (Akkadius) Fixed camera shake usage output. + +==05/22/2011== +KLS and Co: +-All liquid should count for skill ups. +-Finished unified quest interface... this is fairly large and will probably have a few problems here and there; report them to me and ill fix them asap. +The goal behind the system is to allow more than one scripting system to work at a time (though with limited interaction due to pre-existing implementation limitation). +This was a feature requested by the Dalaya community as they plan to merge back to the eqemu codebase and clients but are stuck with an old parser and thousands of files +that can't be realistically rewritten in a short time frame. + +==05/20/2011== +JJ: Doubled buffer for tradeskill search when using experiment mode. Outputs message in error log when matches exceed buffer. + +==05/17/2011== +JJ: Changed tradeskills to distinguish world containers in multiple recipe matches. Split invalid container vs. non-unique recipe error log entries. Allowed server to select first match for duplicate recipe contents. + +==05/16/2011== +JJ: (Zothen) UCS message logic fix. +JJ: (Zothen) Learning basics clean name fix. + +==05/09/2011== +KLS: (lerxst2112) *nix compile fix. +KLS: Added #picklock #sensetrap and #disarmtrap for LDoN traps since they still don't work quite right on the newer clients because we don't implement destructable objects yet. + +==05/08/2011== +Congdar: (Timothy_nonax) Add Bot Command ( #bot pull ) + +==05/03/2011== +Trevius: (Vaion) Fix for focus effect decay over max level. +Trevius: (Zothen) Crash fix for UCS. +Trevius: Rule added for Skill Caps Max Level to resolve issues with servers that have Chars over level 75 after Rev1875. + +Optional SQL: utils/sql/svn/1889_optional_skill_cap_rule.sql + +==05/02/2011== +Secrets: Added OP_CameraEffect for Titanium. +Secrets: Added commands: #reloadallrules, #reloadrulesworld, and #camerashake. These default to +Secrets: Added optional "global" flag as item 5 in the $mob->CameraEffect() quest object. This does #camerashake, but in quest form. +Secrets: #reloadallrules reloads rules in every single zone plus world. #reloadrulesworld reloads the rules in world only. +Secrets: #camerashake shakes the camera in every zone with required args intensity and duration. + +==04/24/2011== +Trevius: Added new Perl Mob quest objects GetItemHPBonuses() and GetSpellHPBonuses(). + +==04/17/2011== +Secrets: Converted PVP flags to be a variable, as there are other PVP rulesets embedded in the client. These will need code support to work properly. (IsAttackAllowed specifically.) +Secrets: Enabled Firiona Vie ruleset as an option. This disables OOC, makes languages hard to understand between races (somehow), and disables no-drop. If set to 2, this rule will only affect GMs, allowing players to be traded no-drop items from GMs. Player-to-Player trades are still considered active hacks, and are still detected. +Secrets: Made a rule to toggle the GM Petition Window in titanium. You will need the guide petition files for that client, and it still needs code support to work. (find these yourself.) +Secrets: Added expansion settings to the rule_values table instead of the variables. The PVP settings will also be here now, too. (See Optional SQL with this update.) +Secrets: Fixed an issue pointed out by Trevius in the previous commit. + +Optional SQL: utils/sql/svn/1885_optional_rules_fv_pvp_expansions.sql + + +==04/13/2011== +BadCaptain: Bot spell update- Group Heals, debuff spells, spell recast timers, healing AI tweaked, bot bards can now have more than one song active +Optional SQL: utils/sql/svn/1884_optional_bot_spells_update.sql + +==04/12/2011== +Trevius: The mana regen portion of bard songs is no longer affected by instrument mods. +Trevius: (scruffy) Identified opcodes for /zone and petition queue on Titanium. + +==04/06/2011== +Derision: Underfoot: Corrected OP_CompletedTasks. + +==03/30/2011== +Trevius: Implemented spell formulas 1001 to 1998. They subtract/add values by increasing amounts each tick based on the formula - 1000 value. + +==03/23/2011== +JJ: (Danyelle) Face fix for Iksar Beastlord pet. + +==03/21/2011== +JJ: (Jaekob/Danyelle) Texture fix for Iksar Beastlord pet. + +==03/11/2011== +Trevius: Skill caps now use the MaxLevel rule for deciding max level of skill caps instead of being hard capped at level 75. +Trevius: All spells can be blocked in a zone by setting a spell id of 0 in the blocked_spells table. +Trevius: If a zone has all spells blocked by setting spell id 0 in blocked_spells, any additional spells added for that zone will become exceptions and be allowed. +Trevius: Reverted Sorvani's swarm pet change from Rev1872 due to some bugs it caused for normal pets. + +==02/24/2011== +Trevius: Power Source slot can now be augmented. +Trevius: Corrected spell formula 122 (Splurt) and implemented the reverse splurt (Ex. Corath Venom) where damage reduces each tic. + +==02/22/2011== +gaeorn: (sorvani) fix to make swarm pets not be targetable as NPCs + +==02/19/2011== +Trevius: Added variable export $slotid to EVENT_ITEM_CLICK and EVENT_ITEM_CLICK_CAST, which exports the slot id of the item that triggered the event. + +==02/18/2011== +Trevius: Fixed a potential crash in TrySpellOnKill(). +Derision: Underfoot: Bard instrument modifiers now show (as focus effects) in the item stats window. +Derision: Allowed fishing in 'VWater' (Region type 7) on the basis this is the only type of water in Sleeper and you can apparently fish there. + +==02/17/2011== +Derision: Fixed bug in GetZoneForage where generally only the first three zone specific items could actually be foraged. +gaeorn: (sorvani) fix for bard pacify songs + +==02/13/2011== +Trevius: Finalized implementation of Item Faction Mods. The bonuses now remove if the item is unequiped. +Trevius: Added Rule Character::ItemCastsUseFocus - If true, this allows item clickies to use focuses that have limited max levels on them +Derision: Handle Spell duration formula 15 (for Distillate of Skinspikes). + +Optional SQL: +utils/sql/svn/1859_optional_item_casts_use_focus_rule.sql + +==02/11/2011== +Trevius: Implemented item faction modifier stats (i.e. factionmod1, factionamt1). +Trevius: Added Power Source slot to item stat (charm) scaling. +Derision: SoD+: Fixed bug when looting items from last two inventory slots (commonly seen when using Loot All). +gaeorn: only send hp/mana/endurance updates to group members when in raid instead of entire raid + +==02/10/2011== +JJ: (c0ncrete) Fixed shared bank database query to match schema. + +==02/08/2011== +Trevius: Replaced most uses of strncpy with strn0cpy for consistency and stability. +Trevius: Increased zone name loading to 32 characters for doors. + +Required SQL: +utils/sql/svn/1847_required_doors_dest_zone_size_32.sql + +==02/07/2011== +Derision: SoD/UF: /makeleader and changing Group Leader via Roles works. +Derision: If the leader of a group of exactly 3 members quits, leadership should now be transferred to another member. + +==01/30/2011== +Caryatis: Stability fixes for Mystats window. + +==01/26/2011== +Derision: Support for door opentype 57 (instant intra-zone portals which don't require the client to click, such as in the bazaar). + +Note: For opentype 57, the client automatically sends the click when the player moves onto the portal. + +Optional SQL, to set the bazaar portal discs to type 57 (they are type 58 in the PEQ database, but collects show they should be type 57): + +update doors set opentype = 57 where zone = 'bazaar' and name = 'PORTAL_DISC'; + +==01/24/2011== +Trevius: Exported GetFreeSpellBookSlot(start_slot=0) and GetSpellBookSlotBySpellID(spell_id) to Perl Client. +Trevius: Fix for potential crash with EVENT_ITEM_CLICK. +Trevius: (Secrets) Identified OP_SpellEffect for Titanium. +Congdar: bad_captain: more Bot Updates / Fixes V 2.0 + +==01/22/2011== +Congdar: fix stack buffer overflow + +==01/20/2011== +Congdar: bad_captain: Bot Updates / Fixes V 2.0 + +==01/18/2011== +Trevius: Changed Perl NPC AddItem(itemid, charges, slot = 0) to AddItem(itemid, charges = 0, equipitem = true) since slot was unused anyway. +Trevius: Added equipitem option to quest::addloot(item_id, charges = 0, equipitem = true). + +==01/16/2011== +Caryatis: Fix for Phantom line of discs not working. +Caryatis: Fix for buffdurationformula 5(0 value results in a 3 tick spell, was set to 1 tick). +Caryatis: Fix for discs(and AAs) setting a reuse timer if used while not able to(ie stunned) +Caryatis: Implemented SE_VoiceGraft +Caryatis: Added scaling for pet focus items based on the pet power in the spell file. + +==01/15/2011== +Caryatis: Fix for fear effects not fading when the buff does, also added check so that a negative recast is not set with SE_ReduceReuse + +==01/14/2011== +Trevius: (l0stmancd) Fix for invalid slot move warnings on SoF+ clients. +Trevius: (l0stmancd) Fix for potential item loss issue while using an inventory augmentation sealer. +Trevius: Adjustment to the augmentation code logic to remove potential issues. +Trevius: Added updateclient option to Perl Client TakeMoneyFromPP(copper, updateclient=false). +Derision: UF: Translated command code for /pet hold +Caryatis: Implemented SE_ReduceReuseTimer(epic 1.5/2.0 focuses, sof type3), SE_BlockNextSpellFocus(chance to block next spell matching focus) +Caryatis: Implemented SE_SetBodyType, SE_SpellOnDeath, SE_BlockSpellEffect, SE_Leap, SE_ImmuneFleeing, SE_AddMeleeProc +Caryatis: Delay Death AA is now functional(optional sql). + +Optional SQL: +utils/sql/svn/1823_optional_delay_death.sql + +==01/13/2011== +JJ: (Leere) Exported quest::LearnRecipe(recipe_id) to PERL + +==01/12/2011== +gaeorn: possible fix for some short duration buffs (bard songs) fading one tick early +Caryatis: Changed SE_NoCombatSkills to SE_CombatSkills(0 = spells only, 1 = discs only) +Caryatis: Implemented SE_SpellDurationIncByTic(formerly SE_FocusCombatDurationMod), it adds tics to spells/discs +Caryatis: Added support for Special Attack 'Y'(Ranged Attack), so when an NPC can't reach or summon his target, he will use range if able to +Caryatis: Implemented SE_Forceful_Rejuv(refreshes spell gems), SE_HealFromMana, SE_ManaDrainWithDmg, SE_EndDrainWithDmg +Caryatis: Implemented SE_LimitHPPercent, SE_LimitManaPercent, SE_LimitEndPercent(limit you to a certain % while you have the buff) +Caryatis: Implemented SE_SwarmPetDuration(swarm pet focus) +Caryatis: Res Timer changes have been reverted temporarily. + +Optional SQL: +utils/sql/svn/1817_optional_npc_archery_bonus_rule.sql + +==01/11/2011== +gaeorn: fixed lull max level check to account for no max level +Caryatis: Change to Doppelganger NPCID due to PEQ's next rev already using the IDs. +Trevius: Added quest::stopalltimers() to allow stopping all current timers on an NPC at one time. +Trevius: Added option to stop all timers when using #reloadquest by adding any argument after the command (ie. "#reloadquest 1"). Also added new #rq alias for the command. + +Required SQL: +utils/sql/svn/1813_required_doppelganger_npcid_change.sql + +==01/09/2011== +Caryatis: Viral spells are now supported(field191 determines how many targets it will spread to and field 192 determines how often(in seconds)). +Caryatis: Lull spells will now only work on the levels they were intended to. +Caryatis: Corpses now have a res timer(like live). You may res a corpse as many times as you want however you will only recieve exp for the first. + +Optional SQL: +utils/sql/svn/1809_optional_rules.sql + +==01/08/2011== +Caryatis: Implemented Target's Target(target type 46). Places effect on your target's target(ie cast with mob as target but spell lands on mob's target) + +==01/05/2011== +Trevius: Changed xpreward field in the tasks table to be signed so it can display negative values for the level based experience rewards. +Trevius: Tasks now show item links in Task Rewards correctly for all clients. +Trevius: Tasks now reward any set Experience and/or Coin even if the rewardmethod is set to 2 (perl quest). +Caryatis: Mobs now summon starting at 97%, not 94%(like live). +Caryatis: Charmed pets can no longer summon mobs from out of their LoS while charmed. +Caryatis: All fixed duration charm spells now have fixed duration instead of only 2 that were supported. +Caryatis: SpinStun effects now work on mobs up to the max level that the spell denotes(previously limited to 55 regardless of spell info) +Caryatis: SpinStun durations have been changed to be pulled from the effect_value, instead of buff duration(which stopped alot from working), also spins mobs now +Caryatis: Implemented SE_Fearless(Valiant Companion), SE_AntiGate(Translocational Anchor), SE_DispelBeneficial(various spells and clicks) +Caryatis: Implemented SE_IncreaseBlockChance, SE_CurrentEnduranceOnce, SE_CurrentManaOnce, SE_AEMelee(duration rampage) +Caryatis: Persistent Casting AA is now functional(required sql) +Caryatis: Rampage(-) and Destructive Force(+) have changed, the duration on both has been altered based on the live spell info(required sql) +Caryatis: Flurry will no longer hit the highest on hatelist, it will hit the mobs current target like live(fixes rooted flurrying mobs). +Caryatis: Updated spdat.h with current status of all spell effects +Caryatis: Reorganized spell_effects.cpp to remove alot of the clutter of effects handled elsewhere + +Required SQL: +utils/sql/svn/1803_required_tasks_xpreward_signed.sql +utils/sql/svn/1804_required_ae_melee_updates.sql + +==01/04/2011== +Trevius: (l0stmancd) BagType 53 can now be used as an Augmentation Sealer from inventory. +Caryatis: Implemented SE_Doppelganger(Doppelganger AA is now functional) + +Required SQL: +utils/sql/svn/1802_required_doppelganger.sql + +==01/03/2011== +Caryatis: Implemented SE_GravityEffect(424) +Caryatis: Added rule for endurance to be included in rest regeneration + +Optional SQL: +utils/sql/svn/1799_optional_rest_regen_endurance_rule.sql + +==01/01/2011== +Derision: UF: Tweaked Arrow_Struct and implemented new OPCode for changing player size. + +==12/31/2010== +Derision: UF: Fix for extended duration buffs not reporting correctly after first tic. + +==12/30/2010== +Derision: Added a new table aa_required_level_cost to allow specifying a required level and cost for each rank of an AA. +Derision: Implemented spell formulas required by AA versions of Harm Touch/Lay on Hands. +Caryatis: ShowStatWindow was changed to ShowWindow and had some features added +Caryatis: title_type = 0 for custom titles, 1 for first and last name and 2 for guild titles +Caryatis: Implemented a limit on how low a weapon speed can be when procs are calculated to reflect the innate swing speed limit + +Required SQL: + +utils/sql/svn/1790_required_aa_required_level_cost.sql + +If there is no entry in this new table for a given rank of an AA, then the level (class_type) and cost_inc +from altadv_vars will be used as normal. + +==12/28/2010== +Derision: /corpsedrag & /corpsedrop. Optional SQL: utils/sql/svn/1784_optional_corpsedrag_rules.sql + +==12/25/2010== +Derision: Underfoot: Correct OP_SwapSpell +Derision: SoD/Underfoot: List of traders in /bazaar dropdown now works. +Derision: Implemented Advanced Tracking AA. + +==12/24/2010== +Trevius: (Orkim) Zone Short Name restriction fixes. + +==12/21/2010== +Derision: HoT: OP_SendFindableNPCs +gaeorn: bugfix for quest::movegrp to work with raids. no really, its fixed this time + +==12/15/2010== +Trevius: Added an extra check for buffs while zoning to make sure it is a valid spell being applied. +Derision: Underfoot - Fix for /dismount not dispelling horse and Group Leadership fixes (opcode changes). +Congdar: fix for bot trading exploits + +==12/13/2010== +gaeorn: fixed underfoot to properly handle new packet struct for LoadSpellSet_Struct +Derision: Underfoot - UCS should now retain /announce setting properly. + +==12/11/2010== +gaeorn: bugfix for quest::movegrp to work with raids +Derision: UCS Support for Underfoot and later clients +Congdar: bot item trading fixes for lore, dual wield, augmenting, inspecting + +==12/10/2010== +gaeorn: fixed quest::movegrp to work with raids +gaeorn: fixed casting group spells in a raid +gaeorn: (vanicae) code support for tactical mastery warrior aa +gaeorn: (vanicae) code support for touch of the divine cleric aa +gaeorn: (vanicae) item loot messages when in a raid +gaeorn: (Wolftousen) fixed spawn variance + +==12/09/2010== +Derision: Updated HoT opcodes following patch. + +==12/07/2010== +Trevius: Resolved a potential zone crash with memorizing spells. +Trevius: Added $item1_charges, and $item1_attuned exports for all 4 turn in slots item1-item4 for EVENT_ITEM. +Trevius: Fixed GetMinDMG() so it should work now. + +==12/03/2010== +Caryatis: Fix for SpellResistChance effect. + +Required SQL: +utils/sql/svn/1755_required_sql_fear_resist_aas.sql + +==12/01/2010== +Caryatis: Fix for Reflect Spells and Healing Adept AAs +Caryatis: Showstats has been updated. + +Required SQL: +utils/sql/svn/1754_required_sql_healing_adept_aa_fix.sql + +Optional SQL: +utils/sql/svn/1753_optional_haste_cap_rule.sql + +==11/30/2010== +Trevius: SoF+ - Stacked Potions will no longer show a factor of how many you have remaining in the potion belt. +Trevius: Increased the max spell ID allowed in the spell book from 20000 to 50000. +Derision: Underfoot. Corrected PP for LDoN points, Tribute and Leadership AA. Corrected offset comments/unknown names. + +==11/27/2010== +Derision: Renamed Live patch to Underfoot. +Derision: Added basic support for current live patch (HoT). +Caryatis: Reverted Bot changes due to crash issues +Caryatis: Reverted physical resist changes until we can find a more live-like solution +Caryatis: Gender Illusions are now functional +Caryatis: Implemented SE_Twinproc and Reflect spells + +Note: + +HoT support is limited to being able to enter a zone with spawns and items. +Combat, spells, moving items around, basically anything other than zoning, spawns, initial inventory load and movement will NOT work. +Note ZoneUnavailable is not implemented, so let at least one dynamic zone finish booting before attempting to enter. +This support is mainly to allow people to admire HoT zones, full playable HoT support is not planned at this time. + +Optional SQL for zone and zonepoint table (thanks to robregen for the collects): + +utils/sql/svn/1747_optional_HoT_zone_and_zonepoints.sql + +For spawns etc, go to the PEQ logs repo for data collected by robregen. + +Optional SQL: + +utils/sql/svn/1750_optional_sql_reflect_rule.sql + +==11/24/2010== +Trevius: The Rule, NPC:ReturnNonQuestNoDropItems, will now also return items that have been Attuned to a character as well as normal NO DROP items if set to true. +Trevius: Fixed an issue with tradeskill auto-combines. +Caryatis: Fix for SpellResistChance, Hit-limited focus effects(eg Gift of Mana) & SpellDamageShield(no longer will trigger on beneficial spells). +Caryatis: Showstats window will not show mana fields if the class does not use mana(to preserve the already shaky alignment). +Caryatis: (bad_captain) Removed the level bonus granted to standing mana regen, it was not like live and diminished the need to sit or use a horse considerably. +Caryatis: (bad_captain) Bots have been updated, see forums for complete details(new command: #bot showstats). +Caryatis: (Secrets) Haste values can exceed 127%(ie Can o' Whoop Ass). + +Optional SQL: +utils/sql/svn/1746_optional_sql_bot_manaregen + +==11/22/2010== +JJ: Updated waypoint handling to properly account for aggro and quest engagements. NPCs will no longer skip waypoints when distracted from their grid path when resuming wandering. + +==11/21/2010== +Caryatis: Implemented SE_SkillDamageAmount & SE_Amnesia(silence vs disciplines), SE_CripplingBlow +Caryatis: Updated Silence so that is no longer prevents the use of disciplines(like live) + +==11/20/2010== +Caryatis: Fix for discs being resisted too much, beneficial spells and resurrection effects will no longer be resistable. +Caryatis: Fix for CriticalHitChance being calculated incorrectly, leading to massively increased crit rates. +Caryatis: Fix for Healrate effect +Caryatis: Implemented ManaAbsorbPercentDamage, ReduceSkillTimer, HpToMana and LimitSpellGroup effects. +Caryatis: Updated DamageModifier effect to be more robust. + +Required SQL: +utils/sql/svn/1737_required_sql_rule_and_aa_update + +Optional SQL: +utils/sql/svn/1736_optional_sql_feral_swipe + +==11/14/2010== +Derision: Underfoot: Buff removal should now work correctly. +Derision: Underfoot: Fixed Pet Buff window. +Trevius: Fixed quest::setnextinchpevent(). +Trevius: Added Player EVENT_GROUP_CHANGE that exports variables $grouped and $raided when changes in group or raid status are made +Trevius: Added Perl Client MarkCompassLoc(x, y, z) and ClearCompassMark(). +Trevius: Minor rewrite to EVENT_HP again to use less variables. + +==11/13/2010== +Derision: Corrected OP_Track for Underfoot/June 8 2010 Client. +Derision: Corrected FindPersonRequest_Struct for Underfoot/June 8 2010 Client. +Derision: Buying AAs seems to work better now for Underfoot/June 8 2010 Client. +Derision: Most Pet commands should now work for Underfoot/June 8 2010 Client. +Caryatis: Corruption resist is now fully functional +Caryatis: Showstats has been updated. +Caryatis: Implemented SpellOnKill and CriticalDamageMob effects. +Caryatis: Fix for CriticalSpellChance + +Required SQL: +utils/sql/svn/1723_required_sql_corruption.sql + +Optional SQL: +utils/sql/svn/1723_optional_sql_new_stats_window_rule.sql + +==11/12/2010 +Caryatis: Implemented CriticalSpellChance, SpellCritChance, SpellcritDmgIncrease, CriticalHealChance, CriticalHealOverTime, CriticalDoTChance, SpellResistChance. +Caryatis: Migrated some AAs to the bonus system +Caryatis: Fading Memories is fixed(uses mana, cant use without mana, makes you invis and can use while singing.) +Caryatis: Support for new types of AAs: Expendable, Quest, Racial/Bloodlines. +Caryatis: New client object: IncrementAA(skill_id) and GetAALevel(skill_id) + +Required SQL: +utils/sql/svn/1720_required_sql_AA_effects_update.sql + +Optional SQL: +utils/sql/svn/1720_optional_sql_AAs.sql + +==11/10/2010== +Caryatis: Added support for HealAmt, SpellDmg, Clairvoyance, DS Mitigation, SE_SpellDamageShield, SE_SpellDamage +Caryatis: Heroics fully supported now(points in brackets are how many Heroics to achieve a gain) +Caryatis: hInt = For int-based casters, increases mana pool, mana regen(25), and the maximum amount of mana regen a character can have(25). +Caryatis: hWis = For wis-based casters, increases mana pool, mana regen(25), and the maximum amount of mana regen a character can have(25). +Caryatis: hStr = Increases endurance pool, endurance regen(25), and the maximum amount of endurance regen a character can have(25). Also increases damage done by melee attacks(10) and improves the bonus granted to armor class while using a shield(2). +Caryatis: hSta = Increases hit point pool, hit point regen(25), and the maximum amount of hit point regen a character can have(25). Also increases endurance pool, endurance regen(25), and the maximum amount of endurance regen a character can have(25). +Caryatis: hAgi = Increases endurance pool, endurance regen(25), and the maximum amount of endurance regen a character can have(25). Also increases the chance to dodge an attack(25), grants a bonus to defense skill(10). +Caryatis: hDex = Increases endurance pool, endurance regen(25), and the maximum amount of endurance regen a character can have(25). Also increases damage done by ranged attacks(1), improves chance to successfully assassinate or headshot(10), and improves the chance to riposte, block, and parry incoming attacks(25). +Caryatis: hCha = Improves reaction rolls with some NPCs(25) and increases the amount of faction you gain or lose when faction is adjusted(5). +Caryatis: AA Focus revamped to support new effects +Caryatis: Disciplines will no longer be dispelled + +Optional SQL: +utils/sql/svn/1719_optional_triggerOnCastAAs.sql + +==11/09/2010== +Caryatis: Implemented MaxHPChange, SkillDmgTaken, Endurance Pool and Stun Resist. + +Optional SQL: +utils/sql/svn/1717_optional_rule_bash_stun_chance.sql + +==11/07/2010== +Congdar: Fixed OP_InterruptCast for newer clients(SoF, SoD, Live). You'll now see Npc's and other players cast interrupted message properly. + +==11/06/2010== +Trevius: Fixed an issue with EVENT_HP that was sending $hpevent with a value of -1 in certain cases. + +==11/04/2010== +Trevius: Added new Perl Client GetAAExp() quest object. +Trevius: Corrected the level based Experience quest command and task reward functionality to not affect AA Experience. + +==10/30/2010== +KLS: Added NPC::AddAISpell() and NPC::RemoveAISpell() to perl NPC object. +KLS: Added field to limit AAs based on account creation time. +KLS: Implemented fade on meditate field for spells like the higher yaulp spells (it will fade just before a regen tic). +JJ: Cleaned up Win32 build locations, moved eqlaunch into build folder, and set project dependencies to guarantee build order in Windows. + +==10/16/2010== +Trevius: Added optional slot_id field to Client SummonItem(item_id, charges, attune, aug1, aug2, aug3, aug4, aug5, slot_id) - Default is slot 30 (cursor). + +==10/16/2010== +Trevius: Added Perl Item IsAttuned() and GetCharges() quest commands. +Trevius: Added Attune and Aug fields to Client SummonItem(item_id, charges, attune, aug1, aug2, aug3, aug4, aug5) + +==10/15/2010== +Trevius: NPCs can now load a default.pl file from the /quests/zone_short_name/ of the zone they are in. + +==10/14/2010== +Trevius: Added Race 502 Boats to be a controllable boat race. + +==10/10/2010== +Trevius: Added Perl Client AddLevelBasedExp(exp_percentage, max_level=0) command to add experience as a percentage of the client's current level up to the max level entered. +Trevius: Level based experience percentage rewards for tasks can now be set using a negative value of the max level * 100 + experience percent (IE. -510 for 10% exp up to level 5). + +==10/09/2010== +Trevius: Object model names can now use up to 32 characters. +Trevius: Zone short names can now use up to 32 characters. +Trevius: Added extra checks before AIYellForHelp call is made to prevent potential issues. +Secrets: Added Perl Client GetIP() and NPC GetMinDMG() commands. +KLS: Adjust procs: only check once per round now instead of once per hit, this will result in lower proc rates especially for dual wielding classes but this is how it should behave. Adjust your proc per minute rule up if you desire higher proc rates under this change. +KLS: Directional Cone Spells; still needs a little work as it isn't 100% accurate but it works enough that there's no point in me sitting on it. + +Required SQL: +utils/sql/svn/1696_modify_zone_and_object_tables.sql + +==10/03/2010== +Derision: Fixed bug in SoD+ tracking. +JJ: Removed redundant namespace usage. + +==10/02/2010== +Derision: Players disconnected with the Respawn Window up will now return to bind. +Derision: Players rezzed in an instance via the Respawn Window will no longer zone. +Derision: Implemented Blocked Buffs. +Derision: Fixed Blocked Buff spam every time you zone. +Derision: Implemented OP_ClearBlockedBuffs. + +==09/29/2010== +Derision: Fixed crash in combat logging when a player was DoTted, zoned and then died from the DoT. +Derision: Spells should no longer land on players with the Respawn Window up (e.g. AE DoTs, Evac spells). +Derision: The client should now ummem spells on death when the Respawn Window is in use. + +==09/26/2010== +WildcardX: Updated bots.sql to catch a couple database object changes that affected bots over the last months. + +==09/25/2010== +Derision: Only one Rez confirmation box is allowed to be up at a time (per character). +Derision: Enabled Respawn Window for SoF and later clients. Rule disabled by default. +Derision: Respawn Window only pops-up if a corpse is left (so #gm off to test it). + +Optional SQL (and to enable it): + +utils/sql/svn/1672_optional_rules_respawn_window.sql + +==09/24/2010== +Congdar: Remove obsolete bot code, fix compile warning + +==09/23/2010== +Congdar: Bots can now equip multi slot items in the desired slot. The first available empty slot will get the item. +Congdar: Bots now check Lore and Dual Wield with the new multi slot item code. + +==09/13/2010== +JJ: Fix to prevent NPCs using death animation from facing target during EVENT_SAY and EVENT_ITEM +Derision: Fix for missing tradeskill objects etc in SoF and later clients. +Derision: Reverted stun resist changes. + +==09/12/2010== +Derision: /guildstatus now works for SoD and UF. +Caryatis: Spells with a Melee Lifetap effect now cap the heal at the percentage specified in the spell data. +Caryatis: Work on stun resists. +Vanicae: Fix for potential world crash in online guild-member processing. + +==09/11/2010== +Vanicae: Fix for guild management window updates +wheeljack: Fix for missing tradeskill favorites +Vanicae: Tribute Focus, SE_DivineSave, SE_Flurry, SE_Accuracy +wheeljack: Fix for losing items in world containers +pfyon: quest::createBot() +Derision: None potions that are potion belt enabled (mod rods etc) should now work from the potion belt when they are inside a bag. +Derision: Recast timers should now be honoured for clicky items with a single charge (e.g. Modulating Rod). +gaeorn: more fixes for newer gcc/glib + +==09/08/2010== +gaeorn: fixes for new stricter version of gcc/glib + +==09/08/2010== +JJ: ChannelMessageSend in Client now cleans the "from" name to allow NPCs to be used as the message sender. + +==09/06/2010== +Trevius: Added to Perl Mob: SetSlotTint(material_slot, red_tint, green_tint, blue_tint). +Trevius: Added to Perl Mob: WearChange(material_slot, texture, color). +Trevius: Added to Perl Mob: GetArmorTint(material_slot). +Trevius: Changed EVENT_COMBAT to now export the normal client variables when combat state changes to 1. + +==08/28/2010== +Derision: Fixed potential buffer overrun in item_struct. NOTE: Linux users may have to increase shmmax. +Trevius: Added to Perl Mob: SetGlobal(varname, newvalue, options, duration, other=NULL). +Trevius: Added to Perl Mob: TarGlobal(varname, value, duration, npcid, charid, zoneid). +Trevius: Added to Perl Mob: DelGlobal(varname). + +==08/26/2010== +Trevius: Added new Perl Mob quest object GetItemStat(itemid, stat). +Trevius: Added new Perl Client quest object NPCSpawn(target_npc, option, respawntime=1200) to do create, add, update, remove, and deletes just like #npcspawn. +Trevius: Fixed a potential crash while augmenting items. +Trevius: Fixed a crash caused by using tradeskill container objects created with the CreateGroundObjectFromModel() quest command. +Trevius: Added an optional decay time setting for quest::creategroundobjectfrommodel(modelname, x, y, z, heading, [type], [decay_time]). +Trevius: Decay time is now optional for quest::creategroundobject(itemid, x, y, z, heading, [decaytime]) (default is not to decay at all). + +==08/25/2010== +JJ: Updated PP comments/unknowns to match size changes over time + +==08/24/2010== +gaeorn: mana updates for group should be sent to SoD clients when in raid now + +==08/23/2010== +Trevius: EVENT_TARGET_CHANGE now also works for player scripts. +Trevius: Added new EVENT_ITEM_CLICK for SoF+ clients. File naming format is: /quests/items/script_.pl (I.E. /quests/items/script_12345.pl) +Trevius: Effect Name overrides (SoF+) for the following item table fields: clickname, procname, wornname, focusname, or scrollname. +Trevius: Added to Perl Object: SetEntityVariable(id, var), EntityVariableExists(id), and GetEntityVariable(id). +Trevius: Added Rule UseRaceClassExpBonuses to allow choosing whether to use the Racial and Class Experience Bonuses (Default: True). + +OPTIONAL SQL: +utils/sql/svn/1625_optional_rule_class_race_exp_bonus.sql + +==08/15/2010== +Derision: Fixed crash when a mob died to a DoT in a zone where buff timers are suspended. + +==08/14/2010== +Derision: Spells/Songs with a Locate Corpse effect now work in SoD and later clients. +Derision: Spells/Songs with a Sense Undead/Summoned/Animal effect now work in SoD and later clients. +Trevius: Exported Perl Mob SetCurrentWP(waypoint), Perl NPC SetSaveWaypoint(waypoint), and Perl Client GetTotalSecondsPlayed(). +Trevius: Changed grid from int32 to sint32 since grid pausing requires negative values. + +==08/13/2010== +Derision: Elemental Draw should now work for SoD. + +==08/08/2010== +Derision: Added check for invalid neighbour nodes on loading .path files. +wheeljack: Fixed Bazaar search for items with focus effects. +wheeljack: Buff Timer Suspension. +wheeljack: Tradeskill messages for missing components. + +REQUIRED SQL: + +ALTER TABLE zone ADD suspendbuffs tinyint(1) unsigned NOT NULL DEFAULT 0; +UPDATE zone SET suspendbuffs = 1 WHERE short_name IN ('guildlobby', 'guildhall'); + +==08/01/2010== +Trevius: Exported the following to Perl Mob: GetWaypointX(), GetWaypointY(), GetWaypointZ(), GetWaypointH(), GetWaypointPause(), and GetWaypointID(). + +==07/27/2010== +Trevius: Exported SpellFinished(spell_id, spell_target = this, mana_cost = 0) to Perl Mob. + +==07/26/2010== +Trevius: (Secrets) Added quest::creategroundobjectfrommodel(modelname, x, y, z, heading, [type]) command. +Trevius: (Secrets) quest::creategroundobject(itemid, x, y, z, heading) now returns object ID. +Trevius: (Secrets) Added Object List Iteration functions GetObjectByDBID(id), GetObjectByID(id), GetObjectList(). +Trevius: (Secrets) exported Objects to Perl with the following commands: + IsGroundSpawn(), Close(), Delete(reset_state=false), StartDecay(), DeleteItem(index), IsObject(), + Save(), SetID(set_id), ClearUser(), DBID(), GetID(), GetX(), GetY(), GetZ(), GetHeading(), VarSave(), + GetType(), SetType(type), GetIcon(), SetIcon(icon), GetItemID(), SetItemID(itemid), SetLocation(x, y, z), + SetX(XPos), SetY(YPos), SetZ(ZPos), SetHeading(heading), SetModelName(name), GetModelName(), Repop(), Depop(). + +==07/25/2010== +Trevius: (Caryatis) Added the following Spell Effects: + SE_SympatheticProc, SE_ImprovedSpellEffect, SE_SpellVulnerability, SE_SpellTrigger + SE_InterruptCasting, SE_CastOnWearoff, SE_ApplyEffect, SE_BossSpellTrigger, SE_Twincast + SE_HealRate, SE_SkillDamageTaken, SE_EffectOnFade, SE_MaxHPChange, and SE_EndurancePool. + +==07/19/2010== +gaeorn: fix for foraging + +==07/17/2010== +JJ: Added events for tradeskill failure and success. +JJ: Added quest flag for tradeskills to be handled by perl. Required SQL in the utils folder. + +==07/14/2010== +Trevius: Hopefully fixed the issue for SoF+ clients alcohol consumption causing item loss. + +==07/13/2010== +Derision: Azone2 - Fixed crash processing fungalforest and possibly other Underfoot V4 .eqg zones. +Derision: Azone2 - Culled placeable shrooms to reduce fungalforest .map from ~80 to ~20MB. + +==07/11/2010== +Trevius: Live - Environmental damage is now functional on this client. + +==07/08/2010== +gaeorn: fixed confusing and likely flawed logic in GetZoneForage() + +==07/06/2010== +Trevius: Fix to prevent a potential item loss bug. +Trevius: Fix for Nimbus Particle Effects to make them appear properly. +JJ: Added $oncursor as an export to the parser. +JJ: Added ability for NPCs to use heading in waypoint table. Added option to use heading when adding waypoints in game (-h switch). NPCs will use in-stride heading if the default heading (-1) is used. Optional SQL included in the utils folder. + +==06/24/2010== +Trevius: EVENT_CLICKDOOR should now always show the correct door ID from the $doorid export. +Trevius: Doors with IDs higher than 126 should now work. +Trevius: Added perm_effect and client fields to SpellEffect(effect, [duration, finish_delay, zone_wide, unk20, perm_effect, client]). + +==06/23/2010== +KLS: Instances can use spawn conditions now, requires SQL change in required sql folder. Also updating will wipe all current spawn condition variables so you'll want to make sure they're set in the new table. (ps instance_id=0 for base zones!) +KLS: Added expedition expire warning to dynamic zones that aren't LDoNs, requires conf change for SoF/SoD. + +==06/22/2010== +gaeorn: replaced calls to rand() with calls to MakeRandomInt() + +==06/20/2010== +KLS: Added command #maxskills to max your skills instead of having to #setskill everything for testing. +KLS: Implemented partial melee and spell runes. +KLS: Right click tome learning in sof/sod. +KLS: Adjusted AC; should now be more sensitive to changes in gear as well as implemented softcaps. + +==06/19/2010== +Trevius: Stopped NPCs from sending an appearance packet for each WP they arrive at or depart from. + +==06/18/2010== +Trevius: Nimbus particle effects now show correctly from spells/clickies. +Trevius: Removed the ability to stack the same Nimbus particle effect repeatedly. +Trevius: Existing Nimbus particle effects are now sent to players as they zone in. +Trevius: EVENT_SPAWN should work better for more quest commands now. Please report any bugs/issues with this change. +Trevius: SoF/SoD/Live - Stand state appearances are now set in the spawn struct. + +REQUIRED SQL: +utils/sql/svn/1548_nimbuseffect_required.sql + +==06/16/2010== +gaeorn: fixed possible bug with unlocking cursed containers in LDoN +Trevius: SoF/SoD/Live - Corrected the item structures to allow summonedflag setting to show on items. +Trevius: SoD/Live - Identified Clairvoyance and added it to load from the DB to be shown on items. +Trevius: SoD - Possible fix for empty corpse looting bugging clients. +gaeorn: fixed another issue with the integer transformation for the RNG where it was not converting from a double float properly + +==06/15/2010== +Secrets: Characters with HPs higher than 30k will no longer be set back to 30k current health when zoning. +gaeorn: fixed integer transformation for RNG as it previously would only return the high value of the low to high range if the RNG returned exactly 0xffffffff +Trevius: The following item fields now load from the database: EliteMaterial LDoNSellBackRate ScriptFileID ExpendableArrow QuestItemFlag. +Trevius: Identified some unknown fields in the database from 13th floor findings, and removed multiple unused fields. +Trevius: The summonedflag field is copied from UNK109 field. + +REQUIRED SQL: +utils/sql/svn/1542_items_table_cleanup.sql + +==06/10/2010== +gaeorn: modified method of transforming RNG output into a range of integers. this should improve the equalization of the distribution of the returned random numbers + +==06/09/2010== +Trevius: Added new Perl Mob quest object SpellEffect(effect, [duration, finish_delay, zone_wide, unk20, unk26]) for sending a spell paticle effects without casting a spell. +gaeorn: replaced random number generator with the first one on this page: http://www3.ocn.ne.jp/~harase/megenerators.html + +==06/08/2010== +Trevius: Added new quest::resettaskactivity(task, activity) command for resetting a task activity done count to 0. + +==06/07/2010== +gaeorn: compile fixes for linux +KLS: Added EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT, EVENT_SPELL_EFFECT_BUFF_TIC_NPC in the similar style of the other spell effect events. + +==06/06/2010== +KLS: Added spell quests. Spell quests reside in {EQEMU_DIR}/quests/spells/ and are in the format of {spell_id}.pl. (ex: C:/eqemu/quests/spells/15.pl for greater heal) +KLS: Spell quests allow the overriding of specific spell effect. If a spell quest and sub exists then a spell's effect aside from buff duration and knockback is not used and instead the script is executed. +KLS: Two acceptable subs for spell quests: EVENT_SPELL_EFFECT_CLIENT and EVENT_SPELL_EFFECT_NPC and has a special export of $caster_id. + +==06/04/2010= +Trevius: Fixed a potential crash with pick pocket. + +==06/02/2010= +KLS: Revert titanium hp boost. +KLS: Random 10 grid type(1) will actually return the 10 closest in 3d space not the 10 closest on the grid list. +KLS: Added new grid type(5): Random 5 with LOS. +Trevius: Added new command #tempname , to allow temporarily renaming NPCs and Clients. Name resets on repop or zoning. + +RECOMMENDED SQL: +utils/sql/svn/1519_zone_primary_key_id.sql + +==06/01/2010= +Trevius: Added new Perl Mob quest object TempName(name) to allow changing a name temporarily. Setting no name will return the name to the original name. + +==05/31/2010= +Derision: Increased the name field in the internal Ground_Spawn struct from 16 to 20 characters to cater for ground_spawns that are longer than 15 characters. + +==05/27/2010== +Trevius: Unreverted the revert from R1502 and added a fix for the tradeskill container combine issue. +Trevius: Fixed a crash when using NukeItem on the Power Source slot for a Titanium client. + +==05/25/2010== +KLS: Many changes.. +KLS: Rewrote much of the adventure subsystem; moved from zone based calculations to world based. +KLS: Implemented adventure leaderboard (sort by # kills only) +KLS: Custom merchant (Adventure/PvP/Discord) purchases should attempt to put the item in a player's inventory before the cursor. +KLS: Corpses should move to an adventure GY if possible on the end of an adventure. +KLS: Adventures should always award points to zoning/offline players when they reconnect now. +KLS: Collect adventures will populate the zone with enough dropped items always automatically now, relying on drop rates is now out of the picture. +KLS: Mech. treasure chests will now be affectable by spells and destroyable; at least until we figure out the spawn structure on SoF and beyond to enable them correctly with rogue skills. +KLS: Addressed an issue that made chests slightly harder to open than they were intended to be. +KLS: Addressed a few issues with raids not being duplicated along with groups in the code. This should be apparent in the spell code. +KLS: Added a default ruleset to the zone table and added a version to the zone table. Version will default to 0 if non is found (base zone). +KLS: OP_MoveItem will ignore requests from a slot to the same slot. +KLS: Lowered the quick loot timer significantly, hopefully loot all works better. +KLS: Several memory leak and exploit fixes. +KLS: This could all probably use a bit more testing that I was able to give it as one person, I believe I got most if not all the crash bugs though! + +==05/22/2010== +Trevius: Exported ModifyNPCStat(identifier, newValue) to Perl NPC. +Trevius: Added a check for destroying items to ensure the source slot is the cursor. +Trevius: Moved the SwapItem() check to verify the source and destination slots are both valid before doing anything else. +Trevius: The client will now be updated to delete an item if the server does not see an item in that slot while moving items around. + +==05/20/2010== +Trevius: Added new Perl Mob quest object CameraEffect(duration, intensity, singleclient). Duration is in ms, intensity is from 0 to 10, and singleclient can be set to a client. + +==05/18/2010== +Trevius: Live - Corrected the end of the Item Structure. + +==05/17/2010== +Trevius: Live - Updated more Live Opcodes. The list should be about 95%+ accurate now. Live Client is very playable now. +Trevius: Increased the race max to 667 for #race and #fixmob race to match Live's current race count. + +==05/14/2010== +Trevius: Added new Perl Mob quest object QuestReward(client, silver, gold, platinum) that awards money to the client and makes the quest completed sound. +Trevius: Added 2 new optional fields to Perl Client quest object MaxSkill(skillid, [class, level]). +Trevius: Minor corrections for SetSkill(skill_num, value) and AddSkill(skillid, value) to set skill values to int16 instead of int8. + +==05/13/2010== +Trevius: Live - Updated Player Profile and Spell Buff Structs to allow the May 12th Live patched client to enter the game. +Trevius: Live - Updated about 50% of the opcodes so most basic stuff should be functional. +Derision: Live - Encoded OP_GroundSpawn plus updated OP_ClickObject/OP_ClickObjectAction. +Derision: Added Live to the list of clients reported by #cvs. + +==05/12/2010== +Trevius: SoD - Coin no longer adds weight for this client server-side, since the client itself no longer adds coin weight +Trevius: SoD - Heroic Stats should now add the correct amount of HP/Endurance/Mana to match the client in most cases. +Trevius: The scribespell and traindisc commands and quest commands should sort Disciplines properly once again. + +==05/11/2010== +Derision: Guild invitations to a player already in receipt of an invitation will now be rejected. +Derision: Fixed quest::pvp("off") +gaeorn: fixed a couple issues with the login server code. + +==05/10/2010== +Trevius: SoD - Heroic Stats/Resists now increase the cap server-side as they should. + +==05/09/2010== +Derision: Live - Added an encode for OP_ChannelMessage +Derision: Live - Encoded OP_GuildsList and corrected OP_GuildUpdateURLAndChannel. +Derision: Live - Door and Group OPCodes. Set OP_GroundSpawn=0x00000 until an encode can be done for it (was causing crashes, particularly in PoK). + +==05/08/2010== +Derision: Guild Bank fix. +Trevius(Derision): Added special handling for certain types of opcodes to correct an issue on the Live client. +Trevius: SoF/SoD - Type 5 clicky items can no longer be used if the character does not meet the race/class requirements of the item. +Trevius: Live - Can now log into the game with new character fine, but some existing characters cause zoning loops. +Derision: Live - Added a Decode for OP_ChannelMessage +Derision: Live - Corrected OP_Camp, OP_MoveItem and OP_ItemPacket. +Derision: Live - Corrected: OP_SetChatServer2, OP_MemorizeSpell, OP_CastSpell, OP_Death, OP_Animation, OP_SenseHeading +Derision: Live - Corrected: OP_Damage, OP_TargetCommand, OP_MobHealth, OP_DeleteSpawn, OP_AutoAttack, OP_AutoAttack2 +Derision: Live - Corrected: OP_SaveOnZoneReq, OP_Track, OP_TrackTarget (Tracking messages not yet working, probably due to Filter opcode). +Derision: Live - LogServer_Struct. +Derision: Live - Corrected OP_FormattedMessage and padded out ServerFilter_Struc to match live. OP_WhoAllResponse. + +==05/05/2010== +Trevius(Derision): Fixed a potential crash related to Saylinks and Antispam code. +Trevius: Live - Initial update to the Live patch files. Can only get to a blank character select at this point. + +==05/04/2010== +Trevius: SoF/SoD - Resolved an issue that was preventing some AAs from being able to be trained properly. +Derision: Updating a character's Alt or Banker flag no longer resends the entire guild member list. +joligario: quest::givecash cleaned up. +Congdar: SoF: Fix starting city for freeport characters. + +==05/03/2010== +Derision: The facility to flag 'Alts' in the Guild Management Window now works for those clients that support it. +Derision: The facility to specify a Guild chat channel and URL now work for those clients that support it. +Derision: SoD - Item links for Task rewards now display correctly in the task window. + +REQUIRED SQL: +utils/sql/svn/1451_guilds.sql + +==05/02/2010== +Derision: The Guild Management Window should now reflect the true online status and zone of members at all times. +Derision: Added a command, #cvs (Client Version Summary) to display connected client version counts. + +OPTIONAL SQL: +utils/sql/svn/1450_cvs.sql + +==05/01/2010== +Trevius: Added new Perl NPC quest object GetSwarmTarget() to get the ID of a swarm pet's set target. +Trevius: Added new Perl NPC quest object SetSwarmTarget(target_id) to set the ID of a swarm pet's target. +Derision: Group member locations are now always updated in entity_list.SendPositionUpdates (for SoD group member location on map). +Derision: Certain detrimental spells will still allow the player to enter a rested state. + +REQUIRED SQL: +utils/sql/svn/1446_allowrest_required.sql + +OPTIONAL SQL: (Read the comments in it before executing) +utils/sql/svn/1446_allowrest_optional.sql + + +==04/30/2010== +Trevius: Fixed a bug that would cause clients in an existing trade to get bugged if another client tried to trade with them during their existing trade. +Trevius: Added new Perl Mob quest object MakeTempPet(spell_id, [name=NULL, duration=0, target=NULL]). +Trevius: Added new Perl NPC quest object GetSwarmOwner() to get the ID of a swarm pet's owner. +Trevius: Hide will now break if you move when not sneaking, even if you have invisibility on. +Derision: Fixed bug in Guild Bank. +gaeorn: reverted my change from 4/24 as it did not work. + +==04/29/2010== +gaeorn: fixed tables to avoid login server hang if ServerTagDescription in tblWorldServerRegistration is null + +Optional SQL: utils/sql/svn/1436_login_server_table_fix.sql + +==04/28/2010== +Trevius: Attempting to trade with a player who is already in a trade will now give the "I'm busy right now" message for all clients. +Derision: Applied slot conversion so Apply Poison should now work in SoF. +gaeorn: login server now creates db entries for unregistered servers to keep same id across sessions +Derision: Fixed a cash amount display bug when the two participants in a Barter transaction are using different clients. + +==04/27/2010== +Trevius: SoD/SoF - Clicky items with charges can no longer be cast if they have 0 charges left. +Secrets: Mana can now exceed 32k without rolling over to 0. + +==04/26/2010== +Trevius: SoD/SoF - Failing a clicky item cast in certain scenarios should no longer cause the client to get bugged where the spells won't refresh. +Trevius: SoD/SoF - Cast bars from clicky items now use the cast time from the item, not from the spell. +Trevius: SoD - Potions in the potion belt can now be used even if they are inside bags. + +==04/25/2010== +Derision: Fixed azone2 to handle missing model files (no longer crashes on oldfieldofbone.eqg) + +==04/24/2010== +gaeorn: possible fix for long standing reconnect to login server problem if login server connection is down for an extended period of time. +Trevius: SoD - Added AA support for Harm Touch and Lay on Hands to replace the innate skills of the PAL/SK classes. + +Optional SQL: utils/sql/svn/1410_optional_sod_aas_ht_and_loh.sql + +==04/23/2010== +cavedude: Added Drakking to faction_list, updated wolf form and Skeleton. + +REQUIRED SQL: +ALTER TABLE `faction_list` ADD COLUMN `mod_r42` smallint(6) NOT NULL default '0' AFTER `mod_r14`; +ALTER TABLE `faction_list` ADD COLUMN `mod_r367` smallint(6) NOT NULL default '0' AFTER `mod_r330`; +ALTER TABLE `faction_list` ADD COLUMN `mod_r522` smallint(6) NOT NULL default '0' AFTER `mod_r367`; +UPDATE faction_list SET mod_r367 = mod_r60; +UPDATE faction_list SET mod_r42 = mod_r120; +ALTER TABLE `faction_list` DROP COLUMN `mod_r60`; +ALTER TABLE `faction_list` DROP COLUMN `mod_r120`; + +==04/22/2010== +Trevius: Added the following to Client Perl; GetEndurance(), GetMaxEndurance(), GetEnduranceRatio(), SetEndurance(amount). +Trevius: SoD - Size fix for player races. +Trevius: SoD - Manual consumption of food/drink works and deletes the consumed item. +Trevius: SoD - Consumption of alcohol now gives a chance for skilling up tolerance and also deletes the consumed item. + +==04/21/2010== +Trevius: SoD - The Mana and Endurance formulas are as close as I can get them for now. +Trevius: SoD - NPC default size is now 6 if the database is set to 0. +Trevius: It is no longer possible for current Endurance to be higher than max Endurance. +Trevius: Corrected a mistake pointed out by Rogean that was sending an unnecessary packet when clients were moving. + +==04/19/2010== +gaeorn: bug fix so login server does not send world local_ip if option local_network is not set in login.ini + +==04/18/2010== +Trevius: SoD - Added HP formulas for the SoD client, and a new rule to enable them for SoD clients only. + +Optional SQL: utils/sql/svn/1394_optional_rule_sod_hp_mana_end.sql + +==04/17/2010== +gaeorn: fixed login server auto-reconnect +cavedude (Leere): Tradeskill recipe learning +Congdar: fix windows compile: warning C4405: 'crc32' : identifier is reserved word +Congdar: fix windows compile: warning C4554: '>>' : check operator precedence for possible error; use parentheses to clarify precedence +Congdar: fix windows compile: warning C4800: 'char *' : forcing value to bool 'true' or 'false' (performance warning) + +Required SQL: utils/sql/svn/1392_recipe_learning.sql + +==04/16/2010== +KLS: (Leere) Melody Fix. +KLS: Other merges from spell branch, some more to come. Tell me if there are any crashes I only tested it for about 25 minutes. + +==04/15/2010== +cavedude (Leere): Increased the number of tradeskill favorites that can be stored client side to 500. +cavedude (Leere): Fixed avgcoin. +cavedude (renoofturks): Fixes to SK harm touch. +cavedude: Lowered snare movement speed when fleeing to 41% or higher to allow "Snare" to prevent movement at all levels. +gaeorn: multiple login server support in world. protocol to update login server account information from within game. "trusted" field for world accounts in loginserver to limit where account updates can come from. in eqemu_config.xml, use to specify first of multiple login servers. increment the number to specify additional login servers. NOTE: be sure to replace the entry or you will be limited to just one login server. MULTIPLE LOGIN SERVERS WILL NOT WORK WITH MINILOGIN. + +Required SQL for loginserver tables: +alter table tblWorldServerRegistration add ServerTrusted integer NOT NULL after ServerAdminID; + +==04/12/2010== +Derision: SoD - /who , /whotarget now work. +Derision: Corpses summoned in Shadowrest should no longer decay before you can finish looting them. + +==04/11/2010== +Derision: SoD - Corpses should now vanish instantly rather than decay when using /hidecorpse. +Derision: SoD - Players can now see other players buffs in their target window. +Derision: SoD - Tracking - Player filter now works. +Trevius: SoD - Added Primary/Secondary weapon textures into the spawn struct for non-playable races. + +==04/10/2010== +Trevius: SoD - Identified showhelm in the spawn struct. +Trevius: SoD - Corrected Face in the Player Profile. Frogloks and Vah Shir now show matching head and body correctly. +Trevius: SoD - Added Mana and Endurance update packets for clients. + +==04/08/2010== +Derision: SoD - Tracking Auto-refresh should no longer fail with Recovery timer not met. +Derision: SoD - Previously memmed spells are now unmemmed when loading a spell set. +Derision: SoD - Added Slot conversion to ApplyPoison_Struct (Apply poison should work as well as Titanium). +Derision: AllButSoD - Fixed bug in Barter code. + +==04/06/2010== +Derision: SoD - Players may now be tracked and fixed a bug where tracking would not work when switching tracking targets without cancelling tracking. +Derision: SoD - Initial work on /hidecorpse serverside support. All but /hidecorpse looted should work. + +==04/05/2010== +Derision: SoD - Loot All now works. + +==04/04/2010== +Derision: SoD - Group member (client or bot) mana and endurance should update correctly in the group window. +Derision: SoD - Altered SpawnStruct encode to send colors for all playable races (including corpses and NPCs). +Derision: SoD - Group Main Assist may be assigned via the Group Window. As the group leader cannot explicitly assign it to himself, use /grouproles set 2 +Leere: A moving mob that becomes mezzed will no longer appear to continue moving. +Fourier: Added location to the output of #listnpc. + +==04/03/2010== +Trevius: SoF/SoD - Total Spent AA Points now show correctly for these 2 clients. +Trevius: Froglok characters now default to size 5 instead of 7. This corrects size issues when using illusions. +Derision: SoD - Bazaar Trader Mode/Bazaar Search should now work correctly. +Derision: SoD - Found the helm field in the spawn struct. +Derision: SoD - Implemented new packets for sending the list of findable NPCs to the client. +Derision: SoD - Flymode and Titles in the spawn struct. + +==04/02/2010== +Derision: SoD - Group Leadership AA should mostly work (Main Assist is no longer a GLAA and Health Awareness seems intermittent). +joligario: Pet behavious change. +Trevius: SoF/SoD - Added clicky item checks to match previous clients. All clicky item hacker entries should now be actual hack attempts. +Trevius: Added an extra hacker check for clicking a must equip effect without having it equipped. +Trevius: SoD - Removed the opcodes for the Pet Buff Window until the structure is updated. Prevents pet classes from crashing when pets are buffed. +Derision: SoD - Pet Buff Window now works. +Derision: SoD - Fixed OnLevelMessage struct (popup windows). +Derision: SoD - Fixed Barter mode. +Congdar: (Taurinus) Drakkin bots + +==04/01/2010== +Derision: SoD - Tracking +Derision: SoD - Fixed price bug when buying from merchants. +Trevius: SoD - Spell books now display all scribed spells (was previously hiding the first 9 slots). +Trevius: SoD - Stopped the Barter error that was reporting when logging in due to an incorrect opcode. + +==03/31/2010== +Derision: SoD - Groups should be as functional as previous clients, except Group Leadership AA doesn't work. +Derision: SoD - Fixed items being incorrectly flagged as No Trade/Copied + +==03/30/2010== +Trevius: SoD - Corrected the opcode for Mob Health Updates so they update properly. +Trevius: SoD - Added a few new Opcodes and Structures for SoD packets that aren't used yet. + + +==03/28/2010== + +Derision: SoD - Fixed client animation/runspeed glitch. +Derision: All Clients - Player position updates to other players should be more accurate. +Trevius: Updated #race and #fixmob commands to allow up to race 626 which is the total for the SoD client. +Derision: SoD - Very preliminary work on groups. You can form a group, but nothing else. + +==03/27/2010== +Trevius: SoD - A couple more Opcode updates. +Trevius: SoD - Some group structures work, but grouping is still not functional. +Derision: SoD - Fixed random client crashes due to spawn packets and invisible mobs should now be invisible. +Derision: SoD - Did a basic Encode on OP_WhoAllResponse. Still needs work for various flags (LD/Trader/Buyer) etc. + + +==03/26/2010== +Trevius: SoD - Finished updated Opcodes and they should now be caught up with what we have in SoF. +Trevius: SoD - Updated Item Struct and now 1 inventory item can load correctly. +Trevius: SoD - Updated Position Update packet structures. +Trevius: SoD - Character Inventory now fully loads. +Trevius: SoD - AAs now load and appear to function properly. +Derision: SoD - Spawns work, sometimes. Client will crash randomly sometimes when receiving spawns +Derision: SoD - Added SoD.cpp to .vcproj files +Derision: SoD - Found showname field in Spawn Struct. +Derision: SoD - Identified equip_chest2 field in Spawn Struct. +Trevius: SoD - Looting now works. +Trevius: SoD - Commands that display item links now show links correctly. + +==03/25/2010== +Trevius: SoD - Updated PlayerProfile_Struct and CharacterSelectEntry_Struct +Trevius: SoD - Updated Spawn_Struct and the encode (hackish for now), but can get in game with it! + +==03/24/2010== +Trevius: SoD - Updated the NewZone struct and encode + +==03/23/2010== +Trevius: SoD - Initial addition of the Seeds of Destruction patch files. Note: The SoD Client is not functional at all yet, but work has begun! + +==03/22/2010== +Derision: Implemented the Guild Bank. + +REQUIRED SQL: utils/sql/svn/1293_guild_bank.sql + +==03/20/2010== +cavedude: Added $client->UpdateGroupAAs(), exported GetGroupPoints() and GetRaidPoints() to Perl. + +==03/19/2010== +cavedude: Added $client->GetLDoNPointsTheme() Perl function. +Trevius: Added new EVENT_TARGET_CHANGE perl event. It triggers when an NPC target changes or is removed. It exports $hastarget which is 0 if no target and 1 if there is a target. +Trevius: Exported new quest objects to Perl Mob - SetRace(race), SetGender(gender), and SetTexture(texture). +Trevius: Exported to Perl Mob - SendIllusion(race,[gender,texture,helmtexture,face,hairstyle,haircolor,beard,beardcolor,drakkin_heritage,drakkin_tattoo,drakkin_details,size]). +Trevius: Added rule: Character:DeathExpLossMaxLevel - Any level greater than this will no longer lose experience. Default is 255 (disabled). + +Optional SQL: utils/sql/svn/1290_optional_exp_loss_rule.sql + +==03/18/2010== +Trevius: Added 3 new Perl Entity commands - GetMobByID(id), GetNPCByID(id), and GetNPCByNPCTypeID(npc_id). +Congdar: Bots: Fixed picklock and summon corpse + +==03/16/2010== +Trevius: Saylinks should no longer cause zone crashes if used outside of EVENTs. They should function from anywhere now. + +==03/13/2010== +Trevius: Added new SetFlyMode(0|1|2|3) Perl Mob command. +Trevius: NPCs in flymode 1 or 2 will no longer use BestZ when pathing to waypoints. +Trevius: Added new optional single client argument to SendAppearanceEffect(effect1, effect2, effect3, effect4, effect5, client) to allow sending the effect only to specific players. + +==03/12/2010== +joligario: Fixed some spelling mistakes. + +==03/11/2010== +Congdar: bots can now equip items with augments. required sql: \utils\sql\svn\1280_bot_augs.sql +Congdar: new bot command '#bot augmentitem' Allows you to augment items for your other bot classes. Your must have the Augmentation Sealer window filled. + +==03/10/2010== +Derision: Begging should no longer bug the client. Still needs some work around chance/amount/skillups etc. + +==03/07/2010== +Derision: Fix for temporary items on merchants appearing to increase in price. +Leere: Refinement to above and fix for purchase price of stacked items != displayed price * quantity. + +==03/05/2010== +Trevius: Inspecting SoF players is now possible. +Derision: A mob corpse will now be left even if a Bot's pet does the most damage. + +==03/03/2010== +Trevius: Added functionality to the EQEmu Web Tool to show IP information based on account searches. +Derision: Set Message Type for spells wearing off, experience and leadership messages so they aren't classed as 'Other' by the client chat filters. +Derision: Removed -fpermissive from zone/makefile so invalid type conversions should now cause an error rather than a warning for Linux compiles. + +==03/01/2010== +Trevius: Added 2 new optional arguments to quest::gmsay(text, [color, send_to_world?]) - allows changing color and sending to world or just zone. +Trevius: Normal Saylinks should now work without requiring a target for proximity says. Silent saylinks still require a target. +Trevius: Added a check for valid slots when moving items from 1 slot to another. +Trevius: NPCs using 2 Hand Pierce weapon models can be set to skill 99 in the prim_melee_type field of the npc_types table to show the correct attack animations and messages. + +==02/26/2010== +Derision: Fix for crash in Mob::DetermineSpellTargets. +Congdar: fixed bot right-click inspect window title bar shows player name instead of target bot name. + +==02/25/2010== +Secrets: Added new Perl::Client command, ReadBook(Book Text, Type), to allow sending a book/scroll to clients via quests. Type 0 = scroll, type 1 = book. +Trevius: GMs with #gm on should now be able to /target NPCs across the zone again. +joligario: Fixed typo relating to Pet pursuit speed in MobAI.cpp +joligario: Manual WP Add fix. +joligario: Fixed duplicate declaration of command_wp in command.h + +==02/24/2010== +Derision: SoF: TRADER/BUYER will now be displayed as appropriate in /who for SoF clients. + +==02/23/2010== +Trevius: Added new Perl::Mob quest object SendAppearanceEffect(effect1, effect2, effect3, effect4, effect5) which can send up to 5 particle effects at once. +realityincarnate: spells requiring dragon/giant targets will now work on velious raid targets + +==02/22/2010== +Trevius: Added 2 new fields (prim_melee_type and sec_melee_type) to the npc_types table to allow setting attack animations/messages to go with weapon models (Default is 28 - Hand to Hand) +Trevius: Added Perl Quest Objects; GetPrimSkill(), GetSecSkill(), SetPrimSkill(skill_type), SetSecSkill(skill_type) for altering the above setting temporarily. + +Required SQL: utils/sql/svn/1259_npc_skill_types.sql + +==02/17/2010== +Trevius: Added new quest::untraindiscs() command to untrain all disciplines +Trevius: Added Client Perl Objects UntrainDisc(slot) and UntrainDiscAll() to untrain either just 1 discipline slot or all disciplines +Trevius: Added functionality to quest::traindisc() to allow it to use the spell_globals table and system +Trevius: Added in code to prevent a zone crash related to group/raids using group recourse spells as a temporary fix until the Spells branch goes live + +==02/15/2010== +Gaeorn: (iRFNA) Fixed flaw in IsPureNukeSpell() logic. Fixes bug where bots were healing enemies. +Derision: Added default value to suspendeduntil column in utils/sql/svn/1195_account_suspendeduntil.sql + +==02/07/2010== +Derision: Reverted cross-zone group invites from Rev1086 + +==02/06/2010== +Trevius: Added an account status setting option to the database page of the EQEmu Web Tool. + +==02/02/2010== +Richardo: Added options wep1 and wep2 to #npcedit to allow changing primary and secondary weapon models. + +==02/01/2010== +Trevius: Renamed BoolNPCSpecAtks() to HasNPCSpecialAtk() to fit better into the proper naming format and to make more sense. +Derision: Added $client quest methods to get current PVP point and crystal counts and to award PVP points and crystals. +Derision: Mapped out the part of the player profile used for recording recent PVP kills. +Derision: Removed EVENT_WAYPOINT. Use EVENT_WAYPOINT_ARRIVE or EVENT_WAYPOINT_DEPART instead. + +==01/31/2010== +Trevius: Exported new BoolNPCSpecAtks() to Perl::Mob. This can be used to check if an NPC has certain special attacks set. +Derision: Added support for purchasing from Discord Merchant (Class 59). +Derision: Added support for purchasing from Norrath's Keepers and Dark Reign Merchants (Classes 67 and 68). +Derision: Added commands #setpvppoints and #setcrystals +Derision: EVENT_POPUPRESPONSE is now passed to player.pl + +==01/30/2010== +cybernine186: Implemented the #suspend command. + +Required SQL: utils/sql/svn/1195_account_suspendeduntil.sql + +==01/28/2010== +gaeorn: added EQEmuLoginServer/EQCryptoAPI.h (contents from a EQEmu forum post by KLS) + +==01/28/2010== +Rabayn: Fixed reference to freed memory in Doors::HandleClick. +Derision: Anti Kill-Stealing fix. + +==01/27/2010== +Trevius: Suspend Minion AA now requires level 62 or greater to use it. +Secrets: Implemented quest::voicetell(PlayerName, MacroNumber, RaceNumber, GenderNumer). + +==01/26/2010== +Trevius: Exported new ProjectileAnim(mob, item_id, [IsArrow?, speed, angle, tilt, arc]) command to perl::mob. +Trevius: Added the following to perl::NPC - GetSpawnPointX(), GetSpawnPointY(), GetSpawnPointZ(), GetSpawnPointH(), GetGuardPointX(), GetGuardPointY(), and GetGuardPointZ() +Derision: Leadership abilities should now work correctly for groups with 1 Player + Bots. + +==01/25/2010== +Trevius: Exported CheckLoSToLoc(x, y, z, [mob_size]) to perl::mob. +Trevius: Exported FindGroundZ(x, y, [z_offset]) to perl::mob. + +==01/24/2010== +Trevius: Exported CheckLoS() to perl::mob. +Trevius: Added SoF Decode/Encode for OP_InspectAnswer. SoF clients can now inspect Titanium clients and everything shows up properly. SoF clients still cannot be inspected at all by either client. +Derision: Fix for armor not showing to other player's after looting your corpse. +Derision: Call of the Hero should no longer transfer aggro to the caster. +LeftRoad: Implemented /pet leader command. +Derision: Fix for item loss when going Linkdead with items in the trade window. +Derision: /getguildmotd now works. +Derision: Fixed Bazaar search filter for Endurance, Attack, HP & Mana Regen, Haste and Damage Shield. + +==01/23/2010== +Trevius: Added NPC:ReturnNonQuestNoDropItems rule to enable/disable a new feature that allows all NO DROP items to be returned if turned into a non-item-quest NPC (no EVENT_ITEM). +Trevius: Removed a possible exploit with quest turn-ins. +Derision: Added check for spell target type SummonedPet (e.g. Elemental Draw). +Derision: Implemented /searchcorpse GM command. +Derision: Reset Trade Accepted state if money is added to a trade. +Derision: Shrunk/Grown players/mobs will now appear the correct size to new players entering the zone. +Derision: OP_GuildMemberList for SoF (used to populate Guild Management Window Roster. +Derision: OP_GuildDemote for SoF. +Derision: OP_GuildPublicNote for SoF. +Derision: Corrected message produced when trying to use Suspend Minion on a charmed pet. + +Optional SQL: utils/sql/svn/1144_optional_rule_return_nodrop + +==01/22/2010== +Trevius: Adjusted quest::wearchange(slot, texture) so it will now show the weapon slot changes to players who zone in after the change has already happened. +Derision: Implemented Secondary Forte AA. + +==01/21/2010== +Trevius/Andmetal: Added new Spell Globals system. Adding spells to the new spell_globals table with a qglobal name and value there will require characters to have the matching qglobal in the quest_globals table in order to scribe spells via the quest::scribespells command. Note: Only integers can be used for values currently. +Trevius: Added EnableSpellGlobals rule to enable/disable the new Spell Globals system. +Trevius: Minor change to how NPC loot tables are handled for selecting drops. This should make actual drop rates a bit closer to how they are set in the database. +WildcardX: Implemented a new client animation rate to server-side speed ratio for bots to make bot movement as precise and natural looking as possible. It is possible all other Mob's could benefit from this same tuning if anyone wants to try it on their server. +WildcardX: (Frumph) Implemented a new set of #bot commands to customize bot appearances within the game! +WildcardX: (Frumph) Fixed so bot spell casters don't ghost while casting a spell with a long cast time and bot casters don't sit while a spell is being casted. +WildcardX: Fixed bot casters from doing rapidly and repeatidly going from sit to stand and back to sit during buff casting. + +Required SQL: utils/sql/svn/1136_spell_globals.sql + +==01/20/2010== +Trevius: Added quest::wearchange(slot, texture) to allow any slot to be changed to any texture/weapon model. This works just like the #wc , command. +blmille2: Implemented Calliav line of spells. +Derision: Reverted flawed change to client_packet.cpp. + +==01/19/2010== +WildcardX: Tweaked bot spell healing ai to improve general group healing. +WildcardX: Fixed a zone crash caused by adding an non-existent mob to a bot's hate list. +blmille2: Implemented Suspend Minion and Persistent Minion AA. +garim12: Mastery of the Past AA Ranger/Beastlord version fix. +WildcardX: Added new command, #distance to return the in game distance between you and your target. +WildcardX: Bot casters will now stay in ange to cast heals and other spells during combat. + +==01/18/2010== +WildcardX: Potential bug fix for #bot camp zone crash bug. Continued work on bot animations. (thanks much to Frumph and Secrets for their assistance) +joligario: New EVENT_WAYPOINT_ARRIVE and EVENT_WAYPOINT_DEPART. The old EVENT_WAYPOINT will be removed shortly, so be sure to replace all occurences of EVENT_WAYPOINT with EVENT_WAYPOINT_DEPART in all your current scripts. +joligario: Faction values of zero are no longer written to the faction_values table. +WildcardX: Smoothed out bot melee animations. Another attempt at isolating the #bot camp zone crash bug. + +==01/17/2010== +WildcardX: More bot ai refactoring that is focused on making the bot movements more smooth and accurate. Still a lingering issue when bots fight a mob that has a giant body type. +WildcardX: Bots in melee range of their target will now seperate themselves some so they won't appear so much on top of each other in combat. "Tank" classes will always be closer than "DPS" classes. +WildcardX: Fixed a issue with bot pet hitpoints not persisting correctly to the database and an issue that prevented the client from getting all HP updates for bots and their pets. + +==01/15/2010== +Derision: Added check in Handle_ApplyPoison to verify the specified inventory slot contains a poison in case the player moved items around after beginning to apply the poison. +Derision: Fixed a bug where Mend would not report failures for skill levels below 75. + +==01/15/2010== +WildcardX: Refactored the general bot ai logic. It appears I have bots properly showing their run animations and bots will now stand before they cast a spell. Because I touched soo much bot ai code, this rev should be evaluated fully before implementing on a public play server. I may decide to revert this rev of the bot code if it proves too unstable. + +==01/14/2010== +WildcardX: Fixed a bug in the EQEmuLoginServer that resulted in the server not processing login requests because the application's connection to its MySQL database engine was closed. I have implemented an auto reconnect feature of the MySQL client api. +WildcardX: Cleaned up a couple compiler warnings for EQEmuLoginServer. +WildcardX: Refactored some code that worked fine on a Windows OS, but was incompatible with a Linux OS. New code is compatible with both operating systems. + +==01/13/2010== +WildcardX: Implemented auto melee or archery weapon selection for the bot ranger if the bot ranger is level 61 or better and the bot owner toggled the #bot archery command. After toggeling once, the bot then decides on archery or melee until the #bot archery command is toggled again or the bot zones or camps. +WildcardX: Fixed a bug that prevented bots from being created on systems running MySQL on a linux OS. Windows MySQL servers were unaffected. +WildcardX: Enchanter bots will no longer cast any illusions as part of "buffing". In the near future, I will implement a new #bot command to cast illusions instead. +WildcardX: Fixed a bug that prevent enchanter bots from casting any damage over time or nuke spells against their target. +WildcardX: Fixed a bug that caused bots to "ghost" as they followed their client owner. + +==01/12/2010== +WildcardX: Implemented a bot spellcasting ai overhaul that improves upon the existing ai and creates a framework to expand additional ai enchancements around for bot coders. NOTE: Please evaluate this version of bots BEFORE you replace your existing server as this is still a work in progress. +WildcardX: Implemented bot wizard nuke spell selection for the new bot spellcasting ai. +WildcardX: Fixed various bugs to now allow any characters except the bot owner to give bot commands or spawn the bot. Also fixed #bot mana command to filter out non-caster bots. + +==01/10/2010== +Derision: /invite can now be used to invite a player in another zone into a regular group. + +==01/09/2010== +Derision: Changed Client::GroupInvite2 to use the Invitee name passed in the OP_GroupInvite packet rather than the player's target. + +==01/08/2010== +WildcardX: Fixed a bug that allowed a bot to be created with the same name as an existing player character. This would cause said bot to not zone with other bots the group. +WildcardX: Added new bot command, #bot mana. This will give a mana report for all spawned bots in the zone. Bot mana report spamming is now disabled. + +==01/07/2010== +WildcardX: Fixed a EQEmuLoginServer crash that can happen as the result of a null reference in the database code. +WildcardX: Fixed a bug with bot archery. The command shouldn't crash the zone and the bot should correctly display the bow only. + +==01/06/2010== +WildcardX: Fixed a bug that prevented Bot casters from casting if a mob becomes enraged. +WildcardX: Changed #bot list and #bot spawn commands to use bot name instead of the bot table id +WildcardX: Added new commands to save, load, delete and view bot groups. See the #bot botgroup help command to view them. +WildcardX: Fixed SQL syntax issues with database scripts for bots. +WildcardX: Fixed a bug that may have made it difficult to add bots to bot only groups. + + 1077_botgroups.sql + +==01/05/2010== +WildcardX: Fixed the EQEmuLoginServer project file, fixed a crash caused by referencing a NULL object and made a couple improvements to error handling. +Derision: Fix for quest::depopall related crash. +WildcardX: Improved healing for Bots inspired by contribtions from Bad_Captain! Bot healers won't let a HoT spell block them from casting a heal spell when in combat. +WildcardX: Bot pets will now seek to engage their target's from behind when their Bot master's are fighting the same mob from in front. + +==12/30/2009== +Derision: Update azone2 with EQG v4 support. + +==12/27/2009== +Derision: Fix to set required heading when zoning. + +==12/23/2009== +cavedude: (Kilelen) Added quest functions quest::checktitle(int titleset), quest::enabletitle(int titleset), and quest::removetitle(int titleset) to allow Perl to enable or remove titles for a player. + +Required SQL: utils/sql/svn/1057_titles.sql + +==11/21/2009== +KLS: Ad hoc task selectors should ignore tasks that are not enabled now. + +==11/19/2009== +Congdar: Fixed Resist Stat Caps, Removed GetMax...() methods calculating some AA's twice. + +==11/18/2009== +KLS: Added check for character deletion as pointed out by Rogean. + +==10/31/2009== +AndMetal: Changed HP regen calculations to be Live-like. Basically higher level regen will be about 50% more than it is currently & FD regen should be better than standing, but less than sitting (instead of 25% of sitting regen). +AndMetal: Races that get the HP Regen "bonus" (Iksar & Troll by default) can be customized as a bitmask via rule Character:BaseHPRegenBonusRaces(4352). +AndMetal: Added #setallskills alias for #setallskill/#setskillall. +AndMetal: Added '#rules get [rule]' for a single rule & '#rules values [catname]' for a category to allow real-time viewing of the rule values from the zone (not the DB). +AndMetal: Added client HP, Mana, & Endurance regen info to #showstats/#mystats + +==10/30/2009== +KLS: Exported DoSpecialAttackDamage() to perl::mob; + +==10/25/2009== +WildcardX: Tweaked guilds a little to accomodate integrating BOTS. +WildcardX: Bots can not join guilds in the same way as characters can. Bots can only be members, not officers. +WildcardX: Deprecated the legacy BotRaids system and all #bot raid commands. Also deprecated the legacy botgroups table. +WildcardX: Renamed the #bot group order ... series of commands into only #bot group ... + + 1040_DeprecatedBotRaidsSystems.sql + +==10/23/2009== +KLS: Added discussed work around for SoF > level 75 issue from forums. +WildcardX: Bot pets and bot pet items and bot pet buffs will now save their state to the database between zones and camps just like a character. +WildcardX: Add SQL to create unique columns in group_id and group_leaders tables in hopes of removing the last of the seemingly random issues with groups and zoning. + + 1038_grouptablesuniquecolumndefinitions.sql + 1038_botpetstatepersists.sql + +==10/21/2009== +WildcardX: Bots will now persist their buffs to the database while zoning or camped. + + 1036_botbuffs.sql + +==10/20/2009== +WildcardX: Bots can now be a group leader. A character can now make not only a group mixed with both bots and other characters, but also groups with all bots and lead by a bot the character designates. Please see #bot botgroup help for details. The legacy BotRaids logic will be removed from the server code in the next 24/48 hours. This effectivly makes bot raiding the same as before the raid system was created. Much of the existing "#bot group order" commands will also be deprecated soon. +WildcardX: Fix for a bug with #bot botgroup add. +WildcardX: Fix for a bug with bot group leader's record in group_id table not removing any older records. +WildcardX: Fix for a bug that caused a bot group leader to say it is following itself. +WildcardX: Fix for a bug that caused a zone crash when a client ungracefully disconnects from zone but zone still runs and the client had spawned bots. +KLS: Added #globalview +KLS: QGlobals will update instantly for the zone they're in. + +==10/19/2009== +WildcardX: Fixed a number of bugs with support bots zoning. Refactored the code and eliminated the botactives table. + + 1030_botzoningsupport.sql + +==10/18/2009== +WildcardX: Bots can now zone with their bot owners as long as they are in a group. Bots not in a group will camp instead of zone. + + 1027_botactives.sql + +==10/17/2009== +WildcardX: A character and a bot can now recieve an ldon adventure. + + 1022_botadventuring.sql + +==10/15/2009== +AndMetal: Added rule Combat:ProcTargetOnly(true). Allows the change from Rev 606 that limits procs to only the target to be removed. Note: AE procs can still cause stability issues if it is set to false. +AndMetal: Fixed some "no newline at end of file" warnings. +AndMetal: Corrected formatting/typo for some # commands. +AndMetal: Fixed a memory leak in Client::ZonePC(). +KLS: Changed how QGlobals are tracked on the server to reduce the amount of times we query the database. I've tested it quite a bit but if you find anything wrong with the new implementation: report it. + +==10/07/2009== +Rogean: #motd now updates clients instantly serverwide. + +==09/30/2009== +Rogean: Fixed possible crash in Mob::CheckWillAggro(); +Rogean: Temporarily Disabled Fear MaxLevel Check causing a crash. +Trevius: Added Quest Commands quest::playerfeature(feature, setting) and quest::npcfeature(feature, setting) - See Wiki for details on usage. +Trevius: Added Quest Objects GetHairColor(), GetBeardColor(), GetEyeColor1(), GetEyeColor2(), GetHairStyle(), GetLuclinFace(), GetBeard(), GetDrakkinHeritage(), GetDrakkinTattoo(), and GetDrakkinDetails(). + +==09/29/2009== +Rogean: Direct Heal and Rune Aggro are now based off the mana cost of the spell. +Rogean: HoT Aggro is now based off the per-tic heal amount, when the spell lands. +Rogean: Fixed several issues with focus effects. Beneficial Hate Reduction Focus should now work. +Rogean: Fixed AA Spell Casting Subtlety not applying to heal aggro. Also changed to correct values. + +==09/27/2009== +cavedude: (Shin Noir) Implemented Wizard AA: Teleport Bind. +cavedude: (tsowl) GMs will no longer need a key for locked portal doors. +cavedude: (tsowl) Fix for shared and normal bank dupes. +cavedude: (Nachyoz) Corrected a bug preventing $faction from reporting correct values. +cavedude: (drakelord) Runes will now block stuns, provided the damage does not exceed the rune's limit. + +==09/23/2009== +gaeorn: small fix to pathing code for 64bit +gaeorn: a few changes to makefiles to clean things up + +==09/20/2009== +Rogean: Small (Big?) Aggro change, melee swings now generate same aggro regardless of hit/miss/dodged. +Rogean: Aggro Formula changed to weapon_damage + dmg bonus + elemental dmg + (if applicable) bane dmg. + +==09/18/2009== +KLS: Fixed an error with Perl::Group::GetMember(index) added Perl::Raid::GetMember(index). +KLS: Added new npc types field: 'unique_spawn_by_name'. NPC will only attempt to spawn if there isn't already a npc with it's exact name in the zone already. +Rogean: Added World IP Logging @ Login. + + 980_account_ip.sql + +==09/16/2009== +KLS: Added support for raid all loot (type 4), raids will default to this loot type now(may have some appearance issues). +KLS: Added command #raidloot to change raid loot manually, this is needed for titanium if they want to switch from 4 to another and back to 4 since the client does not support type 4 natively. +KLS: Added private_corpse to npctypes to dictate whether a corpse will unlock to outside people or not, by default(0) it will unlock. + +==09/08/2009== +Trevius: SoF - Corrected disciplines for SoF clients so they can load up to 100 instead of being cut-off at 50 max. + +==08/28/2009== +KLS: Fix for bug with AI_Start not clearing feign death. +KLS: Worked out basics for /claim, not yet implemented but worked out all the packets needed for sof/titanium. +KLS: Finished with /claim implementation. + +==08/26/2009== +KLS: Significantly lowered karma required to talk globally if using the system. +KLS: Fixed some small potential exploits. +KLS: Extended anti-spam to /tell, will extend fully to universal chat server soon. +KLS: Started work on /report but did not finish implementation yet. +KLS: Added hotzone support. +KLS: Finished /report implementation. + +==08/23/2009== +WildcardX: *BOTS* Fixed a bug that made bots ghost when moving. +KLS: More tweaks to anti-hack systems, should trigger MQWarp less often. +KLS: Instituted a cooldown on looting items from corpses, 200ms per item looted. + +==08/17/2009== +KLS: Fix for zonesummon variables influencing next zone attempt if you inner-zone first. + +==08/13/2009== +KLS: Added method to enable / disable spawn2 entries from spawning, enabled by default obviously. +KLS: Two quest methods to modify spawn2 behavior quest::enable_spawn2(spawn2_id) and quest::disable_spawn2(spawn2_id), disable will force depop whatever mob is currently spawned in that spawn2 point if any. +KLS: quest::spawn_from_spawn2(spawn2_id), will force a spawn_2 point to spawn a npc even if disabled or it already has a npc spawned. +KLS: Exported several new C-Objects to perl: MobList, ClientList, NPCList, CorpseList, HateEntry, HateList. Examples of how to use can be found in utils/Examples/ListIteration.pl + +==08/11/2009== +WildcardX: *BOTS* Refactored some bots code and implemented a new feature. Players can now inspect bots and their equipment just like players inspect other players. Enjoy! +Trevius: (Secrets) Minor fix for the new Maxlevel Scaling for NPCs to correct a HP issue if they have visible armor/weapons or buffs. +Trevius: SoF - Updated Opcodes for Voice Macros. They now function in SoF (/vtell, /vgroup, /vraid). +WildcardX: *BOTS* Cleaned up bot inventory code a little and fixed a bug that caused some items to display in incorrect slots in the inspection window. +WildcardX: *BOTS* Bot bards are now holding concerts for those willing to listen... + +==08/10/2009== +WildcardX: Tweaked Groups a little to allow a group to consist of both Bots and Clients, doesn't matter what order you invite either nor does it matter how many of each. +WildcardX: *BOTS* A client camping out or zoning will now auto camp out all their bots and clean up any group those bots may have been in. +gaeorn: renamed zone->map to zone->zonemap to eliminate compile warning +gaeorn: added lots of type casting for 64bit compatibility +KLS: Added #revoke support to /auction and UCS chat channels. +KLS: Added rule Chat:KarmaGlobalChatLimit amount of karma needed to be able to chat in global channels below the minimum level and Chat:GlobalChatLevelLimit. By default the level is 8 and the karma amount equal to 24 hours of /played time. Meaning that unchanged someone must be >= level 8 or have more than a day of played time before they can chat in global channels. + +==08/09/2009== +WildcardX: *BOTS* Fixed the bots illusion bug that caused a bots gender and race values to be changed to that of the illusion in the bots table. +WildcardX: *BOTS* Added new bots command, "#bot giveitem". This command lets you give a NO DROP, NO TRADE item to your bot as long as its on your cursor. +WildcardX: *BOTS* Added new commands, "#bot camp" and "#bot camp all". The first orders your bots to camp out if you target them. The second orders all bots you own to camp out, no need to target each. +Trevius: Created methods for when an NPC is spawned to clean up the NPC code some. +WildcardX: *BOTS* Fixed a bug caused by using #bot giveitem that didn't update the client that the iyem that was on the cursor is now gone. +WildcardX: *BOTS* Fixed a bug that crash the client when trading with a bot. +KLS: Fix for #ban +KLS: #ban will now update the player's flag and kick them from the server. + +==08/08/2009== +WildcardX: *BOTS* More code refactoring. +WildcardX: *BOTS* Bots now appear as another client player to your client. This will lead to using your UI to perform operations like group invites/disbands and the raid UI. Perhaps even guilding your bot! (Thanks Trev for your assistance!) +WildcardX: Fixed a bug that negatively impacted BOTS when the NPC scaling logic was introduced. This caused bots to spawn as outrageously large levels, like level 185! +WildcardX: *BOTS* Bots can now be invited and disbanded from your group by simply using the group "Invite" and "Disband" buttons from the client's UI. +WildcardX: *BOTS* Tweak to the bots total play time calculation to make it more accurate. +KLS: Added #path meshtest simple to do a faster search on errant path nodes. +KLS: Modified the accurate hazard code to make automatic path maps with more accurate info that requires less manual editing afterward. +cavedude: (demonstar55) Pets will now be amiable to their owners, indifferent to all else. +cavedude: (demonstar55) Added $client->KeyRingCheck() and $client->KeyRingAdd() to allow Perl to manipulate the keyring. +cavedude: (demonstar55) Casting an invis spell on a player that already has a similar type invis spell will no longer drop the existing buff. +cavedude: (demonstar55) Corrected message string for heal spells. +cavedude: Added rule to determine at what HP a fleeing NPC will halt due to being snared. + +==08/07/2009== +WildcardX: *BOTS* More code refactoring. +WildcardX: Implemented 4 additional abstract methods in the mob object, HasRaid(), HasGroup(), GetRaid(), GetGroup(). These will be implemented as common interfaces by any class deriving from Mob. +WildcardX: *BOTS* Tweaked bot logic so group channnel doesnt get spammed with every swing. +Trevius: Minor adjustment to the flow of logic for the MaxClientsSetByStatus rule to allow it to override settings for the additional max client rules if enabled. +Trevius: Corrected the Attack Rating portion of #showstats and #mystats +Trevius: (Secrets) Added new quest::gmsay("message") command that will send a message to all GMs (GMs only) on the server. +Trevius: (Secrets/Trevius) Added 2 new fields (maxlevel & scalerate) to npc_types for allowing NPCs to spawn within a range of levels and scale stats at the rate set +WildcardX: NPC is no longer a friend class in the Mob class. Moved the class member Mob::target to Mob's private scope to ensure other classes use the Mob::GetTarget() and Mob::SetTarget() methods. +WildcardX: *BOTS* Commented out code that let bots use the groups chat channel until I can fix I bug I found that causes bots to have their targets repeatedly reset to null values and back to their attacker while in combat. + +Required SQL: utils/sql/svn/898_npc_maxlevel_scalerate.sql + +==08/06/2009== +WildcardX: *BOTS* Significantly modified the bots table. Removed several unnecessary columns and changed many interger columns to accept signed values. Added new columns to track dates and times for bots. +WildcardX: *BOTS* Slowed bot runspeed to a more reasonable default value. +WildcardX: *BOTS* Bots will now depop instead of being murdered to go away. +WildcardX: Only a Client object will try to update its group id in the database when a group member is removed from the group. +WildcardX: *BOTS* Bots will now record their total play time. +WildcardX: Modified Group::AddMember() to be more bots friendly and removed specific bot code to do the same thing. +WildcardX: *BOTS* Modified the IsBotAttackAllowed() method to prevent a bot wizard's familiar from attacking. +WildcardX: *BOTS* More code refactoring. Bots will now use group message channel to communicate with the group (idea being bots may eventually be in a different zone than the group and/or client). + +Optional SQL: *BOTS* utils/sql/svn/892_optional_bots_table_mod.sql +Optional SQL: *BOTS* utils/sql/svn/893_optional_bots_table_mod.sql + +==08/05/2009== +WildcardX: Fixed a bug that prevented COMBAT_EVENT signaling to NPC's from working correctly. +WildcardX: Bots will now call the database a lot less regarding their inventory. They can get info about their inventory from memory during runtime. +WildcardX: Bots appearance like face, hair, etc is now persisted to the database and used during runtime. +WildcardX: (Taurinus) Tweaked the way bots calculate their AA's so the code isn't as long and is easier to read. +WildcardX: A bot getting aggro will aggro its group members for them to come to the bots defense. +gaeorn: added utils/sql/svn/botsconvert.sql to convert old schema bots and inventory to new schema. if you need to convert old bots, use this instead of bots.sql. + +==08/04/2009== +WildcardX: Fixed a bug that broke cash handins with NPC's. +WildcardX: Fixed a bug that reversed a riposte attack's target. +WildcardX: Merged the "bots" branch into trunk and removed the prior "EQBOTS" system from trunk. Please carefully evaluate this version before making any decision to upgrade your servers. +Trevius: Added a Distance option for quest::follow(entity_id, distance) to allow setting the distance for the NPC to follow at. + +==08/02/2009== +realityincarnate: SoF - Added opcode values to avoid packet dumps of some of the more annoying unknowns +realityincarnate: SoF - Tradeskill success messages now display the recipe name instead of %1 +realityincarnate: Added #myskills command to bring up an SoF-style skills window showing current and capped skill levels +realityincarnate: Skills can no longer pass the cap from initial training (specifically bard meditate) + +==7/31/2009== +WildcardX: Implemented a method to calculate the reciprocal heading of a specified mob object. + +==7/30/2009== +Trevius: Added heading option and bool option for saving guard spot to quest::moveto(x,y,z,[h,saveguardspot?]). +Trevius: Added rule MaxClientsSetByStatus. If True, IP Limiting will be set to the status on the account as long as the status is > MaxClientsPerIP. + +==7/29/2009== +AndMetal: Small fix to quest::scribespells() to return the correct amount of scribed spells. +AndMetal: #setaapoints/#setaapts will now consistently add the correct amount of AA points. Also added a rule AA:ExpPerPoint(23976503). +AndMetal: Added a little bit of logging for exp (CLIENT__EXP). Disabled by default. + +Optional SQL: utils/sql/svn/853_optional_rule_aaexp.sql + +==7/27/2009== +gaeorn: changes to login server to compile under linux. added linux makefile for linux. converted utf16 sql files to normal ascii text. +KLS: Fix for issue where client would stop recieving communication when recieving more than 36 nodes in a find reply. Resolved by capping the amount of nodes find can return, more than 36 will result in unable to easily find message. + +==7/26/2009== +AndMetal: AGI Penalty for being encumbered now matches the Titanium client. The old formula was much higher than the client was reporting. +AndMetal: Added InLiquid() function to the WaterMap class. Will return true if you're in Water or Lava (or both?) +AndMetal: Converted to some more Message Strings in zone/spells.cpp. +AndMetal: Changed GetNextAvailableSpellBookSlot() & FindSpellBookSlotBySpellID() to return a result much faster. +AndMetal: The scribespells commands/quest functions should no longer overwrite existing spells & will use only unused book slots. +KLS: Added #path resort nodes to facilitate with keeping the pathing nodes in order. +KLS: Fix for crash from adding/removing nodes. +KLS: Combat range will now check the z axis as well. + +==7/25/2009== +KLS: Added a much more accurate but slower Hazard Detection algorithm for #path process that will calculate water and very spikey terrain between two points, other instances of PathManager::NoHazards untouched. +KLS: Added #path move and #path disconnect all. + +==7/24/2009== +Derision: Moved RecalcWeight to after CalcAABonuses in bonuses.cpp because valgrind complained aabonuses.packrat was used uninitialised. +Derision: Fixed memory leak in /who all processing. +KLS: Added functionality to #path to faciliate making .path files in game, including a very CPU intensive #path process filename which will try to create a connected path from unconnected laid out nodes. +KLS: /find will now do a hazards check. + +==7/22/2009== +realityincarnate: Added quest::factionvalue() command to give more logical faction values than the client uses (Scowling = 1, Ally = 9) + +==07/21/2009== +Trevius: Added a 4th type for quest::getlevel(type) that will get the characters level2 value, which is the max level the character has ever reached. +Trevius: Added a 3rd option for saylink() to allow the link to be different than the say message. Ex quest::saylink("bind my soul", 0, "bind your soul") + +==07/20/2009== +Trevius: Corrected/Updated some minor size stuff with illusions and player races. + +==07/19/2009== +Trevius: EVENT_PROXIMITY_SAY will now work whether you have a target or not. +Trevius: Added bool option to quest::saylink() to allow them to be silent. Ex. quest::saylink("silent link",1) +Trevius: Added quest::FlyMode() command to allow setting flymode on players via quest scripts. +WildcardX: Fixed the build by casting this->GetTarget() as NPC in void Client::Handle_OP_ItemLinkClick method as a parameter to a EVENT_SAY call. +WildcardX: Added the changelog file to the solution for convenience. +realityincarnate: Spells cast from items now reduce the charges before creating spell effects (stops loregroup conflict with Gloves/Hammer of Infuse) + +==07/15/2009== +Trevius: (Shendare) New object command (#object List|Add|Edit|Move|Rotate|Save|Copy|Delete|Undo) to manipulate objects in real-time from in-game in almost every possible way! + +==07/14/2009== +WildcardX: (for BOTS alpha code only) Set textures to 0 to create the correct appearance of a naked bot on spawn without any equipment. +AndMetal: Changed AA Effects to be loaded into a map instead of a static array. +AndMetal: Changed weight in the client class to uint32 from int16 since the client uses 32 bits for the weight. This should prevent it from overflowing. +AndMetal: Corrected Packrat from 10% per level to 1% per level per the Titanium client. It also now affects coin weight (also 1% per level) per the Titanium client. +AndMetal: Cleaned up a few compile warnings. +WildcardX: Advanced a couple error messages before a return statement in the EQEmuLoginServer project. + +==07/13/2009== +KLS: Reimplemented pet hate scaling. +KLS: Removed code that ports you if you're found warping. +WildcardX: (for BOTS alpha code only) Inviting a bot to group will show the bot name without any numeric characters (ie, mob::cleanname()). +WildcardX: (for BOTS alpha code only) Detecting pacified mobs via spell effect type instead of specific spell id's. + +==07/12/2009== +Trevius: Added "featuresave" option to #npcedit that will save all currently set facial features. +Trevius: Fixed crash for #randomfeatures if no target was selected before using it. +Congdar: Fix EQBOTS AISpells break. Add new rules, features too much to list. +KLS: Upped default monk melee damage, halved default monk special attack damage by a little over half. + +SQL: /utils/sql/svn/773_monk_rules.sql + +==07/11/2009== +KLS: SoF - Changed raid join packet encode slightly. +KLS: SoF - Added workaround for disconnection on death for now. +WildcardX: Fixed BOTS so the bot doesn't warp to its owner. +WildcardX: All commands for the BOTS system can be tested now except for bot raiding and bot group raiding. + +==07/10/2009== +Trevius: (Zeice) Added #mystats command as an alternative to #showstats that only shows stats for the client or it's own pet. +WildcardX: Migrated more code from EQBOTS to BOTS. The BOT code is less buggy than last iteration and now has even more active features that can be tested but there is a known issue or two I am aware of and working to correct. +KLS: Fix for fear not wearing off properly. +KLS: Small tweaks to npc spell ai. +KLS: SoF - Raids should now function properly. + +==07/09/2009== +WildcardX: Migrated more code from EQBOTS to BOTS. The BOTS code can do basic functions now but its still "buggy" compared to EQBOTS until migration/integration is complete. +KLS: Clients are no longer immune to charm. +KLS: Added several checks for IsFeared where we were previously only checking IsStunned. +KLS: EVENT_COMBAT should no longer be called when a NPC dies. +KLS: Changed way AISpells was loading, did away with arbitrary 16 spell limit on npc spell lists. + +==07/08/2009== +WildcardX: Migrated more code from EQBOTS to BOTS. +WildcardX: Included StringIDs.h to the zone project file and allowed the ";" to replace the "," characters in the WORLD project file. +Trevius: Added rule ArcheryBaseDamageBonus to allow easy adjustments to archery base damage +Trevius: SoF - Adjusted how AAs are loaded to allow all to be fully functional. This is a temp work-around until the correct way is finalized. +KLS: Lowered aggro backstabs generates by roughly 2/3 for a high level rogue, probably a bit less for a low level rogue. +KLS: Backstab will now use the backstab damage field from items instead of weapon damage. +KLS: Redid all combat skill ups, can only skill up against light blue targets or better for offensive and defensive actions, wont work against clients or NPCs who don't have aggro lists. +KLS: Mobs will prefer targets who are not feared to targets who are feared when selecting new targets. +KLS: Added Rule (Character, MaxFearDurationForPlayerCharacter). +KLS: Fear will now cause PCs to run instead of just be stunned, beware. + +==07/07/2009== +Derision: Fix for mobs getting stuck if their path between waypoints attempts to take them 'under the world'. + + +==07/06/2009== +KLS: Reworked EVENT_ATTACK to be more efficient. +KLS: Reworked EVENT_COMBAT to be both more efficient and smoother, no longer having an arbitrary delay before it fires. +KLS: Took out code in HateList::IsEmpty() that no longer makes any sense to me, it should be noticably faster. +KLS: Took out old unused quest::flagnpc stuff. +KLS: Added #netstats command, allows GM to view the network sent/recv & sent/recv per second on their target or themselves. +WildcardX: Migrated more code from EQBOTS to BOTS and reworked BOTS to derive from NPC object instead of Mob. + +==07/05/2009== +gaeorn: (drakelord) Added slow migitation. Set npc_types.slow_mitigation with the portion of slow you want mitigated: 0 = 0%, 1 = 100%, .5 = 50%, etc. +AndMetal: AAs are now calculated in CalcBonuses for clients. + AAs that have been converted: (Advanced) Innate STR / STA / AGI / DEX / INT / WIS / CHA, Innate Enlightenment, Natural Durability, Physical Enhancement, Planar Durability, Packrat. They should already be in the PEQ aa_effects table. + For new AAs, you'll want to modify the StatBonuses struct in zone/mob.h. You'll also need to add a case in ApplyAABonuses in zone/bonuses.cpp to add it to aabonuses. +AndMetal: (via Congdar) Fix for quest::traindiscs(). +AndMetal: Fixed Packrat. However, it doesn't seem to be updating correctly to the client (or we're taking too much off). +AndMetal: Added weight to #showstats, accurate to 1 decimal point. +KLS: NPCs will no longer try to base their attack speed off weapons they're using, the code wasn't working anyway. +KLS: Lowered monk kick damage and damage table bonuses but added rules to govern this damage so they can be boosted up for any servers that want. +KLS: More work on ranged damage for npcs, not yet fully implemented but quest functions can cause NPCs to ranged attack. +KLS: Added quest::IsEffectInSpell(spell_id, effect_id), quest::IsBeneficialSpell(spell_id), quest::GetSpellResistType(spell_id), quest::GetSpellTargetType(spell_id) + +Required SQL: + +utils/sql/svn/745_slow_mitigation.sql + + +==07/03/2009== +KLS: Fix for AA reuse timer text. +KLS: Added rules to replace defines: Combat:MaxRampageTargets, Combat:MaxFlurryHits +KLS: Redid inner workings of rampage a bit to be more efficient memory wise and to hold an unlimited number of rampage targets instead of capping at 20. +KLS: Made ranged combat stuff virtual so I can implement npc ranged attacks tomorrow. +KLS: Added 3 perl functions for the mob scalar type: SetEntityVariable(id_num, var), GetEntityVariable(id_num) and EntityVariableExists(id_num), taking int32 for id_num and string for var. Allows someone to store as many variables on the entity being targeted as they would like. + +==07/02/2009== +KLS: Berserker NPCs will now frenzy instead of kick. +KLS: Fix for GetHateRandom() method crash if there are 0 people on the hate list. +KLS: Repop force was broken when we went to the instancing system, it has now been fixed. +KLS: Updated #advnpcspawn to be able to edit respawn and variance timers of spawn points in game. + +==07/01/2009== +AndMetal: Added rules for Sacrifice, which also corrects the minimum level you can use it on: Spells:SacrificeMinLevel(46), Spells:SacrificeMaxLevel(69), & Spells:SacrificeItemID(9963). +AndMetal: Switched to some more Message String IDs. +AndMetal: quest::scribespells can now accept minimum level as a 2nd argument. Examples: quest::scribespells(60,60) will scribe just level 60 spells, quest::scribespells(75,71) will scribe spells from 71 through 75. +AndMetal: Ditto with #scribespells, quest::traindiscs, & #traindiscs. +AndMetal: quest::scribespells & quest::traindiscs will now return the amount of spells/disciplines that were scribed/learned (an optional way to verify success or failure). +AndMetal: #scribespells & #traindiscs will limit the max level to whatever the rule Character:MaxLevel is set to, unless you have GM Mode turned on, in which case it's limited to 255 (uint8). +AndMetal: Fixed a compile error introduced in Rev 729. +gaeorn: SE_Harmony and SE_ChangeFrenzyRad effects were reversed. This is based on info from alla regarding spells post OoW that only have SE_Harmony effect (reaction radius). +KLS: Added NPC code 'd' to make npcs be able to see through feign death attempts. (mostly implemented, still have to look into aggroing people who are feigned by proximity) +KLS: Fixed a typo in the petition code and un-reverted the petition.h file (hi, there's a reason it was changed). +KLS: Added mob perl object HealDamage(amount, caster = 0); + +Optional SQL: +utils/sql/svn/732_sacrifice_rules.sql + +==06/30/2009== +Derision: Fix for mobs in pathing enabled zones fleeing at low health at full run speed. +Derision: Made rule Map:UseClosestZ default to false. Only set true if using azone2 generated maps (specifically those for EQG zones). +realityincarnate: Starting cities are now tracked for newly created characters. +realityincarnate: Enabled the /setstartcity command for characters without a start city assigned. +realityincarnate: Added $client->SetStartZone and $client->GetStartZone perl commands. +KLS: Petitions will assign id based on character id instead of this stupid desyncing junk. +KLS: Max NPC name length increased to 50 characters up from 30. +KLS: Save on zone success will now commit immediately instead of possibly delaying. Hopefully will help with some of the situations where people zone to bad locations. +gaeorn: New rule NPC:SmartLastFightingDelayMoving. When true (default), if a mob is already wandering home during a FD pull and the puller stands up and then FD again, the mob's timer to return home will not reset, but instead will return home immediately. The timer will be reset if the mob forgets about the FD puller. +gaeorn: New rule Combat:AssistNoTargetSelf. When assisting a target without a target: true = target self (default), false = leave target as was before assist (this is the behavior on live). + +Optional SQL: + +utils/sql/svn/730_smart_delay_moving.sql +utils/sql/svn/731_rule_assist_notarget_self.sql + +==06/29/2009== +Derision: Fixed a pathing bug relating to nodes incorrectly culled from the front of a path. +KLS: Reworked bind code a bit. +KLS: Pets no longer get client war/berserker crit bonuses. + +==06/28/2009== +Derision: Began work on pathfinding for aggro/fear and Find NPC. + +The path files for the first few zones can be found at http://code.google.com/p/projecteqemu/downloads/list +Put the .path files in your Maps directory with your .map and .wtr files. + +Optional SQL: + +utils/sql/svn/721_pathing_rules.sql + +==06/27/2009== +KLS: Specialization should now never increase the amount of mana it takes to cast a spell. +KLS: Fix for mana preservation focus not functioning correctly. +KLS: Fix for duration enhancement focus effects not affecting detrimental spells. +KLS: Fix for summoning focus not reducing the cast time of nec/beast pets serverside. +KLS: Insufficient mana serverside check will now be before any modifiers instead of after, if you don't have enough mana in your mana pool to cast a spell after modifiers but do before modifiers the spell will cost your entire mana pool. +KLS: Added new npc code 'g', lets npcs resist spells by casters that are not in melee range. +WildcardX: More code migration from EQBOTS to BOTS. + +==06/26/2009== +Trevius: SoF - Tradeskill containers within a character inventory can now combine recipes. +Trevius: SoF - Items with Recast Delays no longer appear as greyed out. +KLS: More changes to archery damage. + +==06/25/2009== +Trevius: Structure work for Player Profile Recast Timers and Illusions in 6.2 Client. + +==06/24/2009== +Trevius: #wc (wearchange) as well as all of the facial feature commands will now retain armor tint when used. +Trevius: Using #wc now makes temporary changes visible by all clients. +Trevius: Decimals can now be used with the #size command instead of it only accepting whole numbers. +Trevius: Size is now identified in the illusion struct for SoF and Titanium. +KLS: Implemented quiver haste. +KLS: Change to ranger stationary archery bonus. (Re: Nerfed) +KLS: Adjusted high level ranger archery hate. +KLS: Fix for mana reduction focus effect checking base instead of base2 +KLS: Fix for hybrids not getting mana reduction effects. + +Optional SQL: /utils/sql/svn/710_tint_set_naming.sql + +==06/23/2009== +KLS: /surname changes +KLS: Implemented OP_ClearSurname +KLS: (gaeorn) Several 64-bit compile and runtime fixes +KLS: (gaeorn) NPC wander back rules. +Trevius: #texture will now allow player races to retain armor tint for both PCs and NPCs. +realityincarnate: bug fix for changing max_hp with the modifynpcstat command + +Required SQL: /utils/sql/svn/704_rules.sql + +==06/22/2009== +Trevius: (Rabayn) When creating recipes, it is no longer required that componentcount be on separate rows from the successcount or failcount row. +Trevius: (takatok) AA Shield Block added. +Trevius: (Shendare) Armor Tint on NPCs can now be set on a per-slot basis. +cavedude: Added rules to disable/change #peqzone debuffs. Cleaned up code regarding it some. +Derision: Fixed the BestZ stuff I broke in Rev686 + +WildcardX: More work migrating EQBOTS code into BOTS. + +Required SQL: /utils/sql/svn/702_aashieldblock_tint_table.sql +/utils/sql/svn/703_peqzone_rule.sql + +==06/20/2009== +WildcardX: Migrated more code for BOTS from database.cpp to bot.cpp. +AndMetal: Added rule 'Zone:PEQZoneReuseTime' to adjust the reuse time on the #peqzone command. Defaults to 300 per change in Rev 612. +AndMetal: Correction to the item link code used in quests. +AndMetal: Cleaned up zone/StringIDs.h to make it easier to read, plus added some additional String IDs. +AndMetal: Cleaned up fishing some. +AndMetal: Fishing failures should now happen immediately instead of waiting for the timer to run. + +Optional SQL: /utils/sql/svn/699_peqzone_rule.sql + +==06/19/2009== +KLS: NPCs will no longer warp to their very first waypoint if they start on a grid, make sure they're spawned near their first waypoint! +KLS: Added quest::SetRunning(value) (1 for run, 0 for walk) to let a NPC run when out of combat instead of walk. +KLS: Added quest::IsRunning() return 0 for walk, 1 for run to let quest know if the npc is walking or running. +cavedude (nilbog): Added many missing pet names. +realityincarnate: Restricted monster summoning to things more traditionally considered monsters (no boats, among others) + +==06/18/2009== +Derision: Fixed some flawed database queries and a few compile warnings. +Derision: Added command #reloadtitles to reload titles from the database. +Derision: Added second optional argument to #title and #titlesuffix to save the title to the database if 1. +Derision: Added second optional argument to $client->SetAATitle/SetTitleSuffix to save the title to the database. +realityincarnate: Monster Summoning pets should work properly now. + +==06/17/2009== +Derision: Removed redundant flymode on/off in water code (not needed since Rev161). +Trevius: New fields added to the alt_vars table to allow SoF and Titanium to both use it. +KLS: Aggro tweaks + melee spell procs wont proc off ranged attacks anymore. + +Required SQL: /utils/sql/svn/687_aa_table_changes.sql + +==06/16/2009== +Derision: Old corpses summoned by Expedient Recovery will no longer rebury after 5 seconds. +Derision: Added optional duration parameter to quest::popup. +Trevius: SoF - Adventure Merchant Selling is now functional. +Trevius: SoF - Items with click effects now show the min level required to click them. +Trevius: SoF - Items being sold now show the correct price that the merchant will pay for them. +cavedude: Changed the items-0.6.0-DR2-0.6.1-DR1-convert.sql error message to stop confusing people. +cavedude: Green con KOS NPCs will now aggro clients while sitting. +Derision: Spells Gems now stay greyed out after zoning until their recast time is met. +KLS: More aggro tweaks. +KLS: Fix for zone crash from world server. +WildcardX: Migrated more code for BOTS from database.cpp to bot.cpp. + +==06/15/2009== +Trevius: SoF - The following fields now show up on items that have values set for them, but the stats are not actually used by the server yet: + svcorruption, purity, backstabdmg, dsmitigation, heroic_str, heroic_int, heroic_wis, heroic_agi, heroic_dex, heroic_sta + heroic_cha, heroic_mr, heroic_fr, heroic_cr, heroic_dr, heroic_pr, heroic_svcorrup, healamt, and spelldmg +Trevius: SoF - Corrected Max Stat calculation for SoF clients over level 75. +Trevius: Fix for compile error; should have been fixed already *hint*. +Trevius: More tweaks to make illusions work better with features and armor/helm graphics +Trevius: Added alias #rf for the #randomfeatures command. Note that the feature changes are not permanent. +Trevius: Changed the default way of loading spells to now load from the spells_new table instead of from the spells_us.txt file. +Derision: Implemented the Title Window. +KLS: Aggro tweaks. +KLS: Fixes to compile errors/warnings. + +Required SQL: \utils\sql\svn\665_heroic_resists.sql +Required SQL: utils/sql/svn/667_titles.sql + +Recommended SQL: (This is recommended for databases that don't stay current with PEQ or are custom) +UPDATE items SET backstabdmg = damage WHERE backstabdmg = 0 AND itemtype = 2; + +Warning: If you do not have a spells_new table yet, you must create one and load spells into it or zones will crash when loading in. + To create the spells_new table, use the SQL from \utils\sql\svn\230_spells_table.sql + After creating the table, you can import or export your spells from the spells_us.txt file by using the scripts: + import_spells.pl + export_spells.pl + + The easiest way to import your spell file to the table is to move the import_spells.pl file to your main server directory + where your spells_us.txt file and your eqemu_config.xml file are. Then run the following command at a command prompt: + perl import_spells.pl + +See this post for more information on importing/exporting the spell file: +http://www.eqemulator.net/forums/showpost.php?p=160509&postcount=45 + +==06/14/2009== +Trevius: Added new command #randomfeatures which will randomize all possible features for playable races. +Derision: Code support for Steadfast Servant Veteran AA. +Derision: Added entity_list::GetRandomClient accessible from Perl. +Dersiion: Fixed infinite loop in EntityList::RadialSetLogging. +Derision: AATimers are now sent at level 1 rather than level >= 51. +KLS: Fix for name_filter checking. +KLS: Name filters will now apply to surnames and will consider any name with 3 repeating characters or more invalid. +KLS: Fix for compile error; should have been fixed already *hint*. + +==06/13/2009== +Derision: Implemented SE_PercentXPIncrease and Resist caps. +Trevius: Removing an Illusion should now restore all features to their original settings in most cases. +Trevius: SoF - Adjusted Power Source code some to remove a bug with using the slot. +Derision: Implemented SE_SummonAndResAllCorpses +KLS: Rebalanced some spell effects aggro values. +KLS: Hate added should now be treated correctly. +KLS: Archery should now cause aggro. +KLS: Added rule Aggro:PetAggroAmount to control how much hate a pet causes, 50% default. +KLS: Bash will now cause extra hate depending on the AC of the shield you're using if any. +KLS: Implemented resist reduction and hate reduction spell focus. +KLS: quest::updatespawntimer will now set the respawn timer in the database. +KLS: Stole Trevius' idea to let augments work as focus items. + +==06/12/2009== +Trevius: The #nukeitem command and related quest commands is now able to remove items on the cursor and within bag slots of the cursor. +Trevius: SoF - The Power Source Slot is now a usable slot. Items set to use slot 22 in the items table will be able to use this new slot. +KLS: On successful completion of an adventure players should be removed from the adventure immediately instead of after 30 minutes. + +==06/11/2009== +AndMetal: Rule values can be viewed and modified through the web interface. They're updated into the database only, so they won't go into effect until you restart the server. + +==06/10/2009== +realityincarnate: Added /makeleader command functionality +realityincarnate: Non-leaders can no longer kick members from the group +KLS: Added $zoneweather perl export + +==06/09/2009== +Trevius: Added rule Combat:ArcheryStationaryPenalty to allow adjustment of the Archery damage penalty for moving or rooted targets. +KLS: Backstab tweak again. +KLS: Added Perl commands: GetItemAt(slot), GetAugmentAt(slot, aug_slot) both returning type ItemInst usable from the Client object. +KLS: Added Perl command: IsType() for use on the ItemInst object. + +Optional SQL: \utils\sql\svn\646_archery_penalty_rule.sql + +==06/08/2009== +KLS(realityincarnate): Charms: Added charm item scaling with scriping support and initial work on evolving items. +KLS(realityincarnate): Added perl method $client->GetModCharacterFactionLevel(FactionID) +KLS(realityincarnate): Added perl method $group->GetMember(index) +KLS: Added perl method $client->GetLDoNWins() +KLS: Added perl method $client->GetLDoNLosses() +KLS: Added perl method $client->GetLDoNWinsTheme(theme 1-5) +KLS: Added perl method $client->GetLDoNLossesTheme(theme 1-5) +KLS: Added support for item clicklevel and item clicklevel2 +Trevius: Increased spellbook from 400 to 480 in the Player Profile for use with SoF (60 page spell book) +Trevius: SoF - Added new functions to SoF.cpp for converting Slot IDs between Titanium and SoF +Trevius: SoF - Corrected the location of the spellbook field in the Player Profile to fix an issue with loading spells +WildcardX: Check in of the start of what will become the new BOTS subsystem/framework. This is far from done so just continue to use the existing EQBOTS code you have been using. +KLS: Change to dangerous item inst aug creation, hopefully addresses segfault on 64 bit linux. +KLS: Hopefully fix for pets not giving adventure credit to players in ldon instances. + +==06/07/2009== +NOTE (WildcardX): This version of the server code is a little dangerous for BOT enabled servers. I do not recommend using this revision for BOT enabled servers. Most of the existing BOT code is going to be re-implemented as a "BOT" object in the server code so until I complete this architecture change the BOT code may become somewhat unstable. I apologize for the inconvience but this is necessary to allow the server code for the BOTs to be more flexible and extensible than it is now. + +Derision: Check for duplicate lore items when fishing. +Derision: Fixed a memory leak in fishing/foraging code. +WildcardX: Traded static integer values for the appropriate enumeration that defines group action types. +WildcardX: A Player grouping with one or more BOTS will now correctly be set as the group leader. +WildcardX: You can now target your BOT to disband it from the group just as you would a player. This does not call the logic that #bot group remove does, though. Keep using the BOT command for now. +WildcardX: A group of one Player and a BOT can now request a LDoN Adventure, but doing so will crash the zone. +KLS: Change to BS formula: moved backstab bonus to damage table. Still might be off but I have another change in mind if so. +KLS: Fix for bug in client calcbonuses. + +==06/06/2009== +Trevius: SoF - Corrected Char select Primary/Secondary slot display order. +Trevius: SoF - Added gender to the conversion for bodytypes 66 and 67, which should resolve any remaining issues with it. +Derision: New Universal Chat Server combining both mail and chat server functionality. + Required to support mail, chat and friends/ignore for SoF clients. + See http://www.eqemulator.net/wiki/wikka.php?wakka=UCS +WildcardX: Corrected the project file for eqlaunch. This should allow eqlaunch to execute correctly for Windows XP and Windows 2003 Server. +WildcardX: Corrected database table definition that created a bug which prevented a BOT from being created. + +REQUIRED SQL: + +CREATE TABLE `friends` ( + `charid` int(10) unsigned NOT NULL, + `type` tinyint(1) unsigned NOT NULL default '1' COMMENT '1 = Friend, 0 = Ignore', + `name` varchar(64) NOT NULL, + PRIMARY KEY (`charid`,`type`,`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +634_TrapTemplateDefaultValue.sql + +==06/05/2009== +Derision: SoF - Adventure OPCode updates + +==06/04/2009== +Trevius: SoF - Added conversion for bodytypes 66 and 67 so they are now untargetable, invisible and have no showing name as they should be. +Congdar: Bots - Implemented raid maintarget, updates to magepet and pvp. Added Sinister Strikes & Strikethrough AAs and several other updates. +KLS: Pets will no longer gain aggro from their owners before their owners load in. Fixes both a dupe and crash issue. +KLS: Players will no longer take damage or die before they finish loading into the game. +KLS: Fixed a crash in /open. + +==06/03/2009== +KLS: Used Cbodmer old ldon trap code as a base for ldon trap implementation. About 50% of the code for this system was his so I figured he deserves credit too. +Derision: Added messages when Leadership Exp is toggled on/off. +Derision: Fixed bug where group retained Leadership AA benefits of previous leader. + +Required sql: 621_LDoNTraps.sql +Updated titanium .conf file for ldon opcodes. Will look at other clients later. + +==06/02/2009== +Trevius: Updated the new Adventure Opcodes for SoF + +==06/02/2009== +KLS: Forgot to update .conf files with the merge. Please update yours if haven't. +KLS: Added settable flavor text to adventure recruiter entries. + +Required sql: 619_Adventure_Recruiter_Flavor.sql + +==06/01/2009== +KLS: (takatok) Fix for agil being factored twice in to-hit calc. +KLS: (tsowl) Reimplemented corpse reloading timer fix. Should work with burried corpses now. (Sorry it took so long to get back to). +KLS: Finished implementing Assassinate and Rescue adventure type +KLS: AC in calc will now not cap at 0. What this means: A level 65 hitting a level 1 will now hit for a majority of attacks at or near maximum damage, it should affect any other situations. +KLS: Spells shouldn't cause interrupts any longer. +KLS: LDoN theme "ALL" items should show up properly now. +KLS: Added sanity check on adventure merchant for both lore items and trying to purchase items of one theme that has a min point requirement while not meeting that requirement. +KLS: Rule Adventure:ItemIDToEnablePorts will now actually cause the door to ignore this rule if it is set to 0. +KLS: Changed the evacuate work around to use zone 1 and zone 2 instead of plane zones that probably have requirements set on them. + +==05/31/2009== +realityincarnate: Added EVENT_CAST to player quests +realityincarnate: AA $quest functions: $client->GetAAPoints(), SetAAPoints(value), AddAAPoints(number), RefundAA(), GetSpentAA() +KLS: Procs will now only occur on a player's targeted mob. +realityincarnate: fixed a calculation error with spent AA's +KLS: Merged instance branch to trunk. + +==05/30/2009== +KLS: Redid procs a bit, spell procs should work more consistently and ppm formula should be less broken and will also take into account haste now. +Highly suggested you change your Combat:AvgProcsPerMinute rule to 2.0 if you want it to be live like. +Derision: Group Leadership AA + +REQUIRED SQL: + +ALTER TABLE `group_leaders` ADD `assist` VARCHAR( 64 ) NOT NULL , +ADD `marknpc` VARCHAR( 64 ) NOT NULL , +ADD `leadershipaa` TINYBLOB NOT NULL ; + +OPTIONAL SQL: + +INSERT INTO `rule_values` VALUES (1,'Character:KillsPerRaidLeadershipAA','50',''); +INSERT INTO `rule_values` VALUES (1,'Character:KillsPerGroupLeadershipAA','50',''); + +==05/29/2009== +Derision: Lay Hands/Harm Touch hot buttons now remain depressed across zoning until reuse timer is up. + +==05/28/2009== +Congdar: Bots - updates to bot pvp, raidexp and magepet + +==05/27/2009== +Trevius: SoF - Added new opcode and structure to support the Respawn Window. It isn't fully functional yet, but most of the code is there now. +Congdar: Bots - Fixed botraid experience distribution. + +==05/26/2009== +WildcardX: Fixed a bug that displayed the name of a target being summoned in an incorrect format. +Angelox: Bots- Weapons that proc'd rune-types were proc'ing for the whole group. 3 fighters with PGTs made you pretty much bullet-proof. + +==05/25/2009== +cavedude: (gaeorn) Added a possible fix for some NPCs not always spawning. +Congdar: Bots - Added PvP for bots. +WildcardX: (erde) Added include for limits.h. + +==05/24/2009== +KLS: Fixed dodge/parry/riposte, lowered their effectiveness from skill points slightly. +Congdar: Bots - Added new command #bot magepet [earth|water|air|fire|monster] - Select the pet type you want your Mage bot to use. + +==05/23/2009== +KLS: Spells with 0 spell mod will now act as if their mod was 100% or normal instead of 0% of normal. +KLS: (Derision) Implemented discipline reuse timer's client side effects +Congdar: New feature to choose if a key should go on the keyring or not. + required sql 564_nokeyring.sql + +==05/21/2009== +Trevius: Added new quest command quest::saylink() to create itemlinks in quest text that can be clicked for a response + +Recommended SQL: .\utils\sql\svn\553_saylink_table.sql + +==05/20/2009== +Angelox: Bots- Bots can't stack invis-undead, invis-live, or any other invis type spell. +KLS: Fix for NPCs jumping in place when they have the fixpathingZ rules for movement enabled. +KLS: Traps will no longer attempt to clean up their npcs, every time a trap is destroyed so are the NPCs so it's redundant and risky. +Congdar: Bots - Added Magician AA Elemental Durability, Fixed #bot corpse summon, AI Tweak + +==05/19/2009== +Trevius: Identified hairstyle, haircolor, beard, and beardcolor in the Titanium Illusion struct so the related commands now work in Titanium. +KLS: PvP info will only be set if the server type is set to 1 (pvp) +KLS: Adjusted chances on dodge/parry/riposte modifiers +KLS: Made experience con scaling into a rule. +KLS: Added Rules: (Character, UseXPConScaling), (Character, LightBlueModifier), (Character, BlueModifier), (Character, WhiteModifier), (Character, YellowModifier), (Character, RedModifier) +Derision: Bandolier bug fix. +Derision: Equipped items that should confer an extra potion belt slot now do so in Titanium and the 6.2 client (was already working in SoF). +Angelox: Bots- (Congdar) fixed Bot illusion / change form spells as to who is affected. +Angelox: Bots- added check to Bot pacify for casting from a distance. + + +==05/18/2009== +Trevius: New Commands added: #face, #helm, #hair, #haircolor, #beard, #beardcolor, #heritage, #tattoo, #details +Trevius: Adjusted the #fixmob command to use all features. The new format is "#fixmob featurename prev/next" +Trevius: Corrected the R524 optional SQL file + +==05/16/2009== +Trevius: (Shendare) Added armor tinting functionality for NPCs by setting it in the npc_types table +Trevius: Added base support for illusions to use all facial features, but functionality still needs to be added. +Derision: Reverted (Minimum 3% chance to proc from weapon proc buffs) +KLS: Reduced chance to hit for min damage with AC. +KLS: Added AC scaling by level, will put it in a rule soon. +KLS: Enabled both petition window 'p' and pvp window 'ctrl p' on titanium/sof; neither are functional but they're enabled now and we can work on that later. +Congdar: Bots - fix compile error and update to current combat code +Congdar: Bots - fix pet exploit +KLS: Added backward compatibility to older corpses. + +Required SQL: .\utils\sql\svn\527_npc_armor_tint.sql + +==05/15/2009== +Trevius: (Trevius/Shendare) Swapped Beardcolor and Beard in the spawn struct for Titanium and Titanium Facial Features should be finalized now. +Trevius: SoF - Drakkin Specific Features (Heritage, Tattoo and Details) should now be fully functional for NPCs and PCs +Derision: Minimum 3% chance to proc from weapon proc buffs (e.g. vampiric embrace, call of sky etc.) +Trevius: Added encode for Illusion packets in Titanium so they see Drakkin as human after clicking off an illusion +Trevius: Optional SQL for adding a notes field to the rule_values table with notes from ruletypes.h + +Required SQL: .\utils\sql\svn\518_drakkin_npc_type_features.sql +Optional SQL: .\utils\sql\svn\524_rule_values_notes.sql + +==05/14/2009== +Trevius: More work on Facial Features for Characters. Trying to get them functioning properly without hacks before adding SoF Features. +Trevius: SoF - Character Facial Features other than Drakkin specific ones all now work 100% in Character Select and in game. + +==05/13/2009== +Trevius: SoF - (Shendare) Corrected the FaceChange Structure and added an encode to make it work +Trevius: SoF - Identified the Drakkin related fields of the Character Select Structure +KLS: Fix for elemental dmg not calculating correctly. + +==05/12/2009== +Trevius: SoF - (Shendare) Added a temp commented out test section of code to spawn struct encode +Trevius: SoF - (Shendare) Identified hairstyle, haircolor, flymode, drakkinheritage and drakkintattoo in the spawn struct +Trevius: SoF - (Shendare) Changed facial feature code for getting features slightly +Trevius: SoF - Identified beardcolor, invis, and drakkinspikes in the spawn struct +Trevius: SoF - Updated the OP_CastSpell to convert bag slot from Titanium to SoF for potion use +KLS: Reverted player corpse changes from 5/10 as they caused shadow rest to stop functioning. +KLS: Fixed the /bug structure and updated the table to be more useful. + +Required SQL: .\utils\sql\svn\503_bugs.sql + +==05/11/2009== +demonstar55: Added a function to allow Perl to check augments within items. +cavedude: Increased bind wound skill up speed some. +KLS: Fix for potentially dangerous typo in spawn conditions code. +KLS: Removed some non-functioning but still taking up database resources database code. +KLS: Change to a spawn2 table query to reduce disk writes. + +Suggested SQL: ALTER TABLE `spawn2` DROP INDEX `ZoneGroup`, ADD INDEX `ZoneGroup` (`zone`); + +==05/10/2009== +KLS: Fix for a add/remove raid looter across zones error. +KLS: Changed order of split raid exp, ensures amount given to a player is always at least 1. +KLS: (tsowl) Various Fixes + +==05/09/2009== +realityincarnate: Modified quest::say to allow npcs to speak in other languages, added $langid quest variable +realityincarnate: Made low language skill/drunkenness a bit less understandable +realityincarnate: Duplicate lore items from foraging/ground spawns are now handled correctly +realityincarnate: Lore items in the shared bank no longer trigger conflicts with items in individual inventories +KLS: Fix for Group:: and Raid:: splitexp bonus calculations happening once for every person in the group / raid, resulting in very high numbers for group and non-positive numbers for raid. +Trevius: SoF - Corrected a minor issues with new mounts where they weren't setting helm texture properly. + +==05/08/2009== +Derision: Fixed a bug where a pet set to guard would appear not to stop on returning to it's guard spot after being ordered to attack. +Derision: Fixed a bug where a mob aggroed would sometimes appear to run past it's target. + +==05/07/2009== +Trevius: (Erde) The Web Tool now shows the name of the process running each Dynamic zone (example: dynamic_01) + +==05/06/2009== +Angelox: Bots: Added command '#bot shrinkme' requires Shaman or Beastlord (defaults to Shaman). +Derision: Added redux_aa2, redux_rate2 fields to aa_actions. +Derision: Improved Hasty Exit should now reduce the reuse time of Escape. +Derision: The reuse timer in the AA window now shows the reuse time reduced by applicable AAs. + (although if you purchase an AA that reduces the reuse time, you must camp before the reduced reuse time will be updated). + +REQUIRED SQL: + +ALTER TABLE `aa_actions` ADD `redux_aa2` MEDIUMINT( 8 ) UNSIGNED NOT NULL DEFAULT '0', +ADD `redux_rate2` TINYINT( 4 ) NOT NULL DEFAULT '0'; +UPDATE `aa_actions` SET `redux_aa2` = '886', +`redux_rate2` = '10' WHERE `aa_actions`.`aaid` =243 AND `aa_actions`.`rank` =0 LIMIT 1 ; + +==05/05/2009== +Trevius: SoF - Melee stun/bash no longer causes you to spin +Trevius: SoF - Spell Fizzles and Interrupts no longer cause abnormally long refresh times +realityincarnate: Fixed lore check bug with items in lore group -1 +KLS: DoTs should no longer break roots early. +KLS: Added Special Attack hit magical 'm' and special attack hit bane 'b', will allow the mobs affected to hit things that require magical or bane weapons to hit. + +==05/04/2009== +KLS: Code block for pets getting sent into attack did not verify that other was not null, causing rare crashes the old way and a pretty common crash with it's new positioning. This has been corrected. +Derision: Updated spell_type in altadv_vars table to reflect the shared reuse timer ID that it really is. +Derision: AA reuse timers should now display correctly with hotkeys remaining depressed when appropriate. +KLS: Code block for pets getting sent into attack did not verify that other was not null, causing rare crashes the old way and a pretty common crash with it's new positioning. This has been corrected. +KLS: Added area rampage 'r' npc special attak code. +Congdar: Bots - Added Rogue Triple Backstab AA, Enhanced #bot list, Fixed beneficial weapon procs to not effect the entire group. +realityincarnate: Fix for SoF attunable items +realityincarnate: Added checks for multiple items in the same lore group +Congdar: Bots - Fix Cleric resurrect spell selection, Added zone profiles, Fixed crash with bot follow + +REQUIRED SQL: utils/sql/svn/463_altadv_vars.sql + +==05/03/2009== +KLS: Some optimizations for the aggro code. Added a timer for aggro checking will do it twice a second by default: lower AItarget_check_duration in features.h if aggro feels too sluggish for your tastes and recompile. +Derision: Increased reuse_time in AA_DBAction to a uint32 to support reuse timers greater than 18 hours. +Derision: Code support for Chaotic Jester Veteran AA. +Congdar: Bots - Improve Cleric healing ai, fix ae break, update attack code. +Congdar: Bots - Fix sql error for bot table +KLS: (Secrets) min/max damage updated to int32 +KLS: Fixed a bug with accuracy tweaks and finished them up. The system should work fairly well now. +KLS: Removal of several rules associated with the dual accuracy system and the old system for defensive and offensive accuracy bonus. +KLS: Some warning fixes on MSVS. +KLS: Fixed pets not getting sent into battle on attack. +KLS: (theblaz) Pets who were previously guarding after a pet attack command will return to their guard spot; though it appears a stop moving packet is missing on the pet guard spot waypoint. +KLS: Rampage will no longer hit the main tank unless the main tank is the only target in range. +KLS: Swarm pets will once again disappear when their target dies. +KLS: Added zone zone profiling stuff. + +==05/02/2009== +realityincarnate: Augment Item Info. +realityincarnate: Show helm option will now save on camping/zoning. +Derision: Corrected a few memory leaks. +Derision: 'Live'/Anniversary patches no longer registered. +Derision: Fix for random client crashes when combining items in world containers (e.g. augmenting items). +BWStripes: Fix for quest::echo. +erde: Added long zone name support to the web interface. +Cripp: Fix for the web interface for those using Perl 5.10. +Congdar: Bots - randomized face/hair etc. so they don't all look the same. Fixed Bard AE songs. + +==4/30/2009== +Congdar: Bots - bots can now use bows, new command '#bot archery'. Added Ranger archery AA's. Reduced chat mana spam. Tweaked spell ai. Fixed memory leak. Updated '#bot corpse summon'. +Derision: SoF - AAs affecting stats now show the correct stats in the client. +Derision: Tweaked base resists to match the client. +gatorman: Fix for QuickSummoning AA (to include Call of the Hero) +Congdar: Bots - root/snare spell ai fix + +==4/29/2009== +Trevius: SoF - Added rule for setting a startzone for SoF Clients separate from where Titanium Clients are set to start. Set as Zone ID number. + +optional sql: utils\sql\svn\447_sof_startzone_rule.sql + +==4/27/2009== +Derision: SoF: Reading books/notes/scrolls now works. + +==4/25/2009== +Derision: SoF: Ranged attack animations. +Derision: When shooting a bow, there is no longer a superfluous 1HS animation. + +==4/23/2009== +Trevius: (realityincarnate) Added new quest command quest::varlink(item_id) for putting item links into variables. +Derision: Fix for Tradeskill combines where a LORE ingredient is returned. +Derision: Fix for pet names containing spaces losing the space after zoning/camping. +Derision: Fixed bug where Return Home sent players bound in Grobb to Qeynos/Unknown Zone. + +==4/22/2009== +Derision: When Tribute is manually deactivated, it should stay deactivated. +Derision: Tribute will not continually try and reactivate when you run out of tribute points. + +==4/20/2009== +Derision: Added EVENT_PROXIMITY_SAY + +==4/19/2009== +Wolftousen: Player Flurry rate was slightly increased +Wolftousen: Player Flurry messages will now show up as red text, but use the same filter as NPC flurry messages +Wolftousen: Rage Volley no longer requires you to have an thrown weapon in your ranged slot +Wolftousen: Rage Volley now uses the proper damage calculation and is not based on the item you have in the ranged slot +Wolftousen: Rave Volley can no longer be dodged/blocked/parried/reposted. +Wolftousen: Procs from Buffs have been tweaked to go off more often Wolftousen: Players will now receive the "proper" bonus HP for stamina above 255. +Wolftousen: Knight class Tactical Mastery AA was implemented and should now give the strike through message +renoofturks: Created rule Aggro:StunAggroMod to dial in on aggro of stun based attacks. +cavedude: Reverse DS and some DS will now cause aggro on intial cast. +realityincarnate: Controllable boats should now work. Please see: http://eqemulator.net/forums/showthread.php?p=167892#post167892 for additonal information. +realityincarnate: Safe fall skill up is now checked on failure. + +Optional SQL: + +INSERT INTO rule_values VALUES(1,'Aggro:StunAggroMod',750); + +==4/18/2009== +Derision: Changed Account Session Limiting to kick off the old connection and allow the new one. +drakelord: Rest State HP and MP Bonus +Derision: SoF - Combat/Rest state indicator now works. +Derision: No longer need to zone for /corpse to work. +Derision: Fixed Bazaar bug. + +Optional SQL: + +INSERT INTO rule_values VALUES(1,'Character:RestRegenPercent',0); +INSERT INTO rule_values VALUES(1,'Character:RestRegenTimeToActivate',30); + +==4/13/2009== +Congdar: Bots - Fix release vs. debug compile for focus effects, updated bots to new attack code. + +==4/13/2009== +Trevius: SoF - Corrected corpse looting slots so that the correct item is looted when there are multiple on a corpse +Trevius: SoF - Item Links from corpse looting messages now display properly for both client versions +Derision: Personal Tribute should now have an effect serverside, rather than just showing increased stats in the client. +Derision: Deactivating Tribute should no longer crash the client. +Derision: SoF - Personal Tribute should now work. + +==4/12/2009== +Trevius: SoF - Commands that return itemlinks will now show the full name instead of cutting off the first 5 characters +Trevius/Derision: SoF - Fix for subitem serialization. +Derision: SoF - Corrected money update opcodes. +Derision: SoF - Bazaar Trader mode. + +==4/11/2009== +Trevius: SoF - Stopped the bogus "Deleting Item" messages from being sent to SoF Clients +cavedude: (Congdar): You can now safely delete a key item after its been added to your keyring. +cavedude: (realityincarnate): Added quest::MerchantSetItem and quest::MerchantCountItem to add temp items to a merchantlist. See Wiki for usage. +cavedude: Added rule World:MinGMAntiHackStatus to manage the min status checked against the GM AntiHack list. +Derision: Sof: Added support for regular /who + +==4/10/2009== +Derision: Tradeskill recipes involving weapons as an ingredient should no longer dupe the weapon. +Derision: Expendable items with charges are now deleted when the last charge is used. +Derision: Tradeskill skillups now based on unmodified skill level. +Derision: (realityincarnate): Prevent bard skillups for instruments they aren't trained in. +Derision: (realityincarnate): Implemented SE_Hunger (e.g. for Song of Sustenance). +Derision: (realityincarnate): Undying armor by setting RGB to 255 255 255 fixed. +Derision: (realityincarnate): Title suffix quest command, Syntax: $client->SetTitleSuffix(title) +Derision: (erde): cleanipc fix for GCC 4.3.2 +Derision: (BWstripes): Beastlord pets are now named SoAndSo's Warder. + +==4/09/2009== +Derision: Fix for Alchemy, Poison Making and Tinkering. + +==4/08/2009== +Derision: Added $client->GetClientVersion(). Returns: 1 (6.2 Client), 2 (Titanium), 3 (SoF) + +==4/07/2009== +Derision: Fixed crash in chatserver. +Derision: Fixed possible crash in QuestManager::attacknpc + +==4/06/2009== +Derision: (Image): Check packet length is at least 4 bytes in EQStream::ProcessPacket +Derision: (Image): EQStreamFactory::ReaderLoop - Assure that the incoming buffer contains an opcode to analyze. +Derision: (Image): Sanity checks on OP_SessionResponse/OP_SessionStatRequest size in EQStream::ProcessPacket + +==4/05/2009== +Derision: GM Training: SoF - Points now update correctly in the Training Window. +Derision: GM Training: Points now update correctly in the Training Window for languages. +Derision: GM Training: The correct amount of money is deducted from the player profile. +Derision: SoF - Mapped findable field in Spawn struct and OP_FindPersonRequest/Reply. +Derision: SoF - Duelling now works. +KLS: Compile option to ignore login fatal errors in world. + +==4/04/2009== +Derision: Tracking: Now works in SoF and as a side effect, Tracking 'sort by distance' works properly in 6.2 and Titanium clients. + +==4/03/2009== +Congdar: Bots - Added Healing and Lifetap AA's and Focus Effect support +Derision: SoF - Fix for incorrect animation (NPCs frozen/walking while standing still). + +==3/29/2009== +Derision: SoF - OP_PetBuffWindow, OP_Charm, OP_Stun + +==3/28/2009== +Derision: SoF - Tasks now work. +Derision: SoF - OP_Sacrifice, OP_Sound, OP_OnLevelMessage, OP_PopupResponse +Derision: Item Links in Task Descriptions are now formatted correctly based on the client version. + +==3/25/2009== +Derision: 'Hack' to display Froglok/Drakkin corpses correctly. +Derision: SoF - OP_Translocate & OP_LevelAppearance + +==3/23/2009== +Trevius: SoF - Corrected Right Click item effects so they no longer fizzle +Trevius: SoF - Right Clickable item effects with set recast delays now observe that delay properly +Trevius: SoF - Item Effect Charges now get used and updated properly +Trevius: SoF - Food and Drink are now consumed at the proper rates + +==3/21/2009== +Derision: Implemented 'Your total time entitled on this account' in /played. + +==3/20/2009== +Trevius: SoF - Fixed a fairly major crashed caused when attempting to use the 8 new bank slots, or shared bank +Trevius: SoF - Banking is now fully functional including the 8 new bank slots, shared bank and bag slots in the bank +Trevius: SoF - Bandolier and Potion Belt are now both functional +Trevius: SoF - Filled in multiple missing opcodes + +==3/19/2009== +Trevius: SoF - Grouping should now be fully functional +Trevius: SoF - (Xinu) Added many missing opcodes +Angelox: Bots- More mobs for the "#bot track rare" filter. +Derision: Fixed memory leak in Chatserver /announce processing. + +==3/15/2009== +Trevius: SoF - Tradeskill Objects/Containers and Augment Pools should now be fully functional +Trevius: SoF - PC to PC/NPC trading should now be fully functional without bugs +Trevius: SoF - Auto-Consume and Right Click consume of food/drink is functional +Trevius: SoF - Right Click Effects on Items now works in most cases, but needs coding to clean it up a bit +Trevius: SoF - Spell Interrupts are now functioning properly + +==3/15/2009== +cavedude: (realityincarnate) Languages can now be taught to other players. Also implemented drunk speak. +cavedude: (realityincarnate) Bonuses will now properly apply when switching items with bandolier. +cavedude: (realityincarnate) Corrected some instances of Bard songs that were incorrectly requiring an instrument. +cavedude: (Wolftousen) Fixes for Rage Volley, Blur of Axes, and Dead Aim. +Derision: Spells that damage the caster will now do full damage. +Congdar: Spells that summon items with charges will now summon said item fully charged. +Angelox: Bots:Added a few more mobs to the "#bot track rare" filter. +Congdar: Bots - Added commands #bot saveraid, #bot spawnraid, #bot groupraid + required sql: utils\sql\svn\386_bot_save_raid.sql + +==3/14/2009== +Trevius: SoF - Tradeskill/Augment Combines in tradeskill containers/objects now works, but currently bugs players when they close the window. +Derision: Fixed bug when activating a bandolier set without a required item in your inventory. +Trevius: SoF - Identified more fields in the Spawn Struct; Face, LFG, Beard, ShowHelm and more + +==3/13/2009== +Trevius: SoF - Added a few more encodes/decodes to resolve more slot change issues related to the addition of the Power Source Slot +Trevius: SoF - Ammo now depletes properly when used. Probably some other minor issues resolved by this change as well. + +==3/12/2009== +Trevius: SoF - AAs can now be trained and will update without needing to zone. Point Spent is still wrong. +Trevius: SoF - Char Select now shows Primary and Secondary items properly. + +==3/10/2009== +Trevius: SoF - PC to PC/NPC Trading now works, though canceling a trade will bug the character and require them to zone or relog +Trevius: SoF - Players can now sell to merchants +Trevius: SoF - Corrected a bug introduced in Revision 376 that was causing single stackable items to not be stackable + +==3/09/2009== +Trevius: SoF - Buffs and debuffs now last for the correct duration. + +==3/08/2009== +Trevius: SoF - Corrected Item Struct to fix the issue with non-stackable items showing up as being stackable +Trevius: SoF - Identified an unknown field in Item Serialization as being item charges and set it accordingly so that items should now show accurate current charges. +Trevius/Xinu: SoF - Adjusted a few opcodes for more accuracy - No visible change yet + +==3/07/2009== +Trevius: SoF - AAs now show up in the AA window, but do not give bonuses yet. +Trevius: SoF - Spent AA points are partially showing up, but still need work. +Trevius: SoF - Updated some grouping structs, but grouping still isn't working just yet +Trevius: SoF - Buffs can now be clicked off, but the duration still isn't working for them +cavedude: Added new AA, group, and raid EXP multiplier rules. Removed the associated variables. +cavedude: Removed the USE_RACE_CLASS_XP_MODS define as it was outdated and horribly broken. +cavedude: Added group XP bonus. The larger the group, the higher the XP gain. +cavedude: Added XP bonus for Warrior, Rogue, and Halfling. +cavedude: Corrected ZEM for AAs. +cavedude: (Thanks to demonstar55) Pet Affinity will no longer effect charmed pets. +cavedude: (realityincarnate) Bard songs that require instruments will now require them. + +Please note: XP gain has pretty much been overhauled. You may need tweak the multiplier rules for your server. + +Optional SQL: + +REPLACE INTO `rule_values` VALUES ('1', 'Character:ExpMultiplier', '0.5'); +INSERT INTO `rule_values` VALUES ('1', 'Character:AAExpMultiplier', '0.5'); +INSERT INTO `rule_values` VALUES ('1', 'Character:GroupExpMultiplier', '0.5'); +INSERT INTO `rule_values` VALUES ('1', 'Character:RaidExpMultiplier', '0.2'); + +==3/03/2009== +Angelox: Fixed most (if not all) npc pet ghosting at zone safepoints. + +==3/02/2009== +cavedude: (realityincarnate) Implemented Bard song Magical Monologue +cavedude: (Theeper) Attuneable items will now save over zoning/camping and will no longer auto-loot from corpses. +cavedude: Implemented quest::buryplayercorpse, similar syntax as quest::getplayerburriedcorpsecount. This will bury and depop the oldest of a specified player's unburried coprses. It will bury all corpses, but it only depops corpses in the same zone, or in zones that aren't currently open. Hopefully, somebody else out there can finish this one up so corpse summoners can be implemented. + +==2/28/2009== +WildcardX: Begun work on LDoN expansion. Adventure Recruiter window is now operational. + +==2/27/2009== +Trevius: SoF - Corrected the NewZone struct a bit, which fixed Fall Damage and Environment Damage +WildcardX: Implemented the rogue skill, Apply Poison. Includes support for the 6.0 client, Titanium and SoF. + +==2/26/2009== +Trevius: Added #define options in /zone/attack.cpp to allow an easy switch to use the old hit change code until the new code is finalized. +Trevius: Added new rule MinRangedAttackDist for setting the Minimum Ranged Attack Distance +Trevius: SoF - Updated the SoF opcodes, one of which corrects a false Hacker message and log entry + +Optional SQL: +364_ranged_dist_rule.sql + +==2/25/2009== +KLS: Fix for backwards rule in anti-spam. + +==2/24/2009== +KLS: Added initial anti-spam features to global chat operations at popular request. +KLS: Added option to turn off IP verification in chat and mail servers (for people who use internal networks and find it troublesome). +cavedude: Added peqzone column to zone to allow or deny access to zones using #peqzone. +cavedude: #peqzone will give you the approximate time remaining before reusing. +cavedude: Altered merchant price structure to better match Live. Created rules to govern its behaviour. See ruletypes.h for basic explanations! +cavedude: Added command #givemoney. +Trevius: SoF - Corrected WearChange Struct so primary and secondary items now show properly after zoning and #wc works as well. + +Required SQL: +ALTER TABLE `account` ADD `karma` INT(5) UNSIGNED DEFAULT '0' NOT NULL AFTER `revoked`; +ALTER table zone ADD column `peqzone` tinyint(4) NOT NULL default '1'; + +Optional SQL: 360_peqzone.sql + +==2/23/2009== +Trevius: (Xinu) SoF - Many more Opcodes filled in from the client +Trevius: SoF - Doors are now able to open and clickable portals now function as well +Trevius: SoF - Armor tint from dyes is now showing properly +Congdar: Bots: '#bot group remove' now removes any of your spawned bots, if it's a group leader then that whole group + +KLS: Changes to combat: +-AC has a slightly higher chance to mitigate into a miss than before +-AC is no more effective per point but the bonus ac every character and npc gets in the calculation has been effectively increased. +-The accuracy falloff from level is now more variable based on defender's level, this should address the issue where at higher levels you can grind npcs that are still blue or light blue at little risk. +-The base chance to hit has been increased. +-Added both hit and avoidance mods for class types that are adjustable by rules. +-Archery now has an accuracy penalty that is rule adjustable, with ranger buffs the penalty should make it so archery is slightly more accurate than melee at default. +-Skill is now a flat 0.33% chance to miss for every point under your max skill you are. +-The benefit from agility to avoidance has been decreased by roughly 33%. +-Maximum chance to hit is now 95%, down from 99%. +KLS: Bug fix in skill up with the skill up mod rule. + +Recommended SQL and Optional SQL: +356_combat.sql + +==2/21/2009== +cavedude00: Removed Combat:ChanceToHitDivideBy rule. Default value is now Live Like. + Required SQL: delete from rule_values where rule_name = 'Combat:ChanceToHitDivideBy'; +Congdar: Bots: Revert svn328 + Required SQl: delete from rule_values where rule_name = 'EQOffline:SpawnBotCount'; + delete from rule_values where rule_name = 'EQOffline:CreateBotCount'; + delete from rule_values where rule_name = 'EQOffline:BotQuest'; +Congdar: Bots: UN-Revert svn328 + bug fixes + (if you updated to svn354) + Required SQl: 328_bot_management.sql + Optional SQL: 328_optional_bot_management.sql + +==02/19/2009== +Trevius: (Xinu) SoF - Many Opcodes filled in from the client +Trevius: SoF - Aligned the Player Profile a bit more. Should be pretty accurate now +Trevius: SoF - Added Drakkin race again for use of equipment. Should work correctly this time. + +==02/19/2009== +KLS: SoF - Fix for ammo +Angelox: Bots: Fixed '#bot track' command +Angelox: Bots: Fixed broken entry in BotAI.cpp for '#bot track rare' command +Angelox: Bots: 'Fixed #bot gate airplane' typo +Congdar: Fix for Flash of Steel AA + +==02/18/2009== +Trevius: SoF - Combat Damage message now show up in the chat window +Trevius: SoF - GM Skill Trainers will now actually train skills +Angelox: Bots: Added emerald, skyfire, hateplane, skyplane to command #bot gate. +Trevius: SoF - Backed out item stat fix for SoF due to it causing the problem to happen in Titanium + +==02/17/2009== +Trevius: SoF - Added Drakkin race to the source so $race and probably other race related stuff can use it. +Trevius: SoF - Added multiple opcodes +Trevius: SoF - Cleaned up the patch_SoF.conf file some +Derision: SoF - Enter Tutorial/Return Home buttons work - Enter Tutorial directly on char creation does not. +Derision: SoF - OP_ZonePlayerToBind (no more disconnection on death). +Derision: SoF - Target nearest NPC (F8) now works. +Derision: SoF - /who all now works. +cavedude00: SellRate column in items will now help determine an item's sell price. +cybernine186: Optional system to ensure GMs are logging on from a known IP. +Trevious: SoF - Drakkin now gain stats from items and weapons now work for combat +Trevious: SoF - Drakkin now start with Common Tongue, Dragon and Elder Dragon Languages maxed + +Required SQL: utils/sql/svn/340_gm_ips.sql + +==02/16/2009== +Trevius: SoF - The Item Struct should be aligned almost perfectly now. +Trevius: SoF - Merchant Lists, Buying and Selling are now all functional +Trevius: SoF - Looting Items and Currency from Corpses is now functional +Trevius: SoF - Item Links are now functional +Trevius: SoF - Combat animation is now functional +Trevius: SoF - More opcodes added/updated +Trevius: SoF - Titanium users will now see Drakkins (and any race over 473) as a normal Human race + +==02/15/2009== +KLS: SoF - Item movement and bag support. +AndMetal: Augments are now visible when linking items. +Trevius: SoF - Adjusted the new Item Structure to align more fields +Trevius: SoF - Added OP_Consume and OP_LootRequest opcodes +cavedude00: (AndMetal) AAs now use skill_id instead of index for prereqs. +cavedude00: (demonstar55) Implemented Improved Instrument Mastery, Improved Singing Mastery, and Echo of Taelosia AAs. +cavedude00: Created Combat:ChanceToHitDivideBy rule and increased default value to 1250. + Required and Optional SQL: utils/sql/svn/326_aas.sql +KLS: SoF - Fix for item slots in bags. +KLS: SoF - Added work around for client inventory, it should no longer crash and items shouldn't fail to load. *fingers crossed* +KLS: SoF - Implemented summon item packet. +Congdar: Bots - Expaneded BotCount Management + required sql: utils/sql/svn/328_bot_management.sql + optional sql: utils/sql/svn/328_optional_bot_management.sql + + +==02/14/2009== +Derision: SoF - New struct for OP_ExpansionInfo +Derision: SoF - Character Creation now works. +Derision: SoF - support now compiled in by default. +Derision: Small changes to compile SoF under Windows +KLS: SoF - initial inventory load. +KLS: Change to streams, streams will now initialize to an unestablished state instead of closed. Prevents a race condition that would cause the client to hang on identification. +Trevius: SoF - Identified most or all needed fields in the illusion struct for future use. +WildcardX: Made Perl v5.10 the default for WIN32 compiles. +WildcardX: (Angelox) Added many header declarations to make gcc v4.3 happy. + +==02/13/2009== +WildcardX: Compatibility for Perl 5.10. WIN32 compiles will still need to rename perl58.lib to perl510.lib. + +==02/12/2009== +Trevius: Identified the Size, Eye Color and Texture fields in the SoF spawn structure. +Trevius: Added multiple Opcodes for SoF + +==02/12/2009== +cavedude00: Added #findzone (#fz) command to aid in getting a zone's short_name or id. +cavedude00: #peqzone now accepts both short_name and zoneidnumber. + +==02/09/2009== +Angelox: Bots:Added command '#bot corpse summon' for Necromancer. + +==02/08/2009== +Trevius: Corrected Z Coordinate on clientupdates in SoF. All Client and NPC positition structures should now be accurate +Trevius: Corrected the Object structure in SoF. Objects should now all load in the correct location and size. +Trevius: Added a few new Opcodes for SoF. +Trevius: Corrected the Opcode for OP_RequestClientZoneChange - Zoning is now fully functional including zone commands + +==02/07/2009== +cavedude00: Added faction modifiers for Berserker and Froglok. +Derision: Fixed item load failure when using MySQL 5.1. + +Required SQL: +ALTER TABLE `faction_list` ADD COLUMN `mod_c16` smallint(6) NOT NULL default '0' AFTER `mod_c15`; +ALTER TABLE `faction_list` ADD COLUMN `mod_r330` smallint(6) NOT NULL default '0' AFTER `mod_r161`; + +==02/05/2009== +Trevius: NPC coning is now working in SoF. +Trevuis: NPCs and Players can now be targeted in SoF. +Trevius: Corrected the illusion packet structure in SoF so helm change will work with the #fixmob command +Trevius: Adjusted the #fixmob and #race commands to allow up to 586 races (the max for SoF) +Trevius: Client Self position updates now report correct X, Y, and Heading in SoF, but still need Z corrected. +Trevius: All other position updates (NPC or other clients) now seems to be accurate. +Trevius: Names are now viewable over NPC and Player's heads +Angelox: Bots: charm level problem fixed. + +==02/04/2009== +Derision: Fixed ammo consumption. + +==02/02/2009== +Derision: Fix for temp merchantlists where a merchant has a single item with slotid=1 + +==02/01/2009== +Angelox: Bots:(leslamarch) corrected the'#bot gate [combines]' typo. +Angelox: Bots: Added '#bot dire charm'. +Angelox: Bots: Both 'charm' and 'dire charm' will now choose from three charmer classes. +Angelox: Bots: Added '#bot pet remove' (can't charm a mob while sporting a pet). +Angelox: Bots: Fixed exploit where player could remove charmed mobs at will. +Derision: Merchant lists with gaps should now not cause problems wih temporary merchant lists. +Derision: Logging to the eventlog table for trades with NPCs is now controlled by two rules rather than the loglevel variable. + +Required SQL: +ALTER TABLE `eventlog` ADD `event_nid` INT NOT NULL DEFAULT '0'; + +Optional SQL: +INSERT INTO `rule_values` VALUES (0, 'EventLog:RecordSellToMerchant', 'false'); +INSERT INTO `rule_values` VALUES (0, 'EventLog:RecordBuyFromMerchant', 'false'); + +==01/31/2009== +Trevius: Adjusted the NewZone Structure for SoF - Players can now move and spawns appear in place +Trevius: Adjusted the WearChange Structure for SoF - Armor shows, but need Tint to work still +Trevius: Adjusted the Object Structure for SoF - Objects now load, but need more work +Trevius: Added many new opcodes for SoF +Trevius: Adjusted when SendExpZonein is sent for SoF only +Trevius: Added new Opcode List file for SoF development purposes only + +Note: You must move the patch_SoF.conf file from /utils into your server directory for the opcode updates + +==01/31/2009== +Derision: Tweaks to temp merchant list window updates. +cavedude00: Renamed the AugSlotUnk items columns to AugSlotVisible. + +Required SQL: utils/sql/svn/292_augslots.sql + +==01/29/2009== +KLS: VC71 solution files. + +==01/28/2009== +Angelox: Bots: Added a directory with the BOT Makefiles for Windows and Linux. + +==01/27/2009== +Derision: Bazaar bug fix. +Angelox: Bots: Added command '#bot runeme' (Enchanter Rune spells) + +Optional SQL: utils/sql/svn/285_optional_bot_spell_update.sql (removes auto-runes) + +==01/24/2009== +Trevius: Initial Addition of Secrets of Faydwer Patch files. Must uncomment the #define in common/patches/patches.cpp to use SoF for now. + +==01/22/2009== +Derision: Fixed a buffer overflow problem. + +==01/19/2009== +cavedude00: Increased itemid limit to 120,000. +cavedude00: Meditate will now skill up at a more Live Like speed. + +==01/18/2009== +Derision: Fixed a cause of zone crashes. +Congdar: Worn items with charges will no longer delete when the charges are used up. + +==01/17/2009== +Derision: Fixed several cases of integer overflow relating to moving large amounts of money around. +Angelox: Bots: Added command '#bot gate', for Druid Circles or Wizard Portals (Druid Primary). +Angelox: Bots: Changed BotCount to RULE_INT. + +==01/16/2009== +Derision: Implemented voice macros (CTRL-V). Update your opcodes. +Derision: Food and drink should no longer be auto-consumed in the Bazaar +Derision: Added rule Spells:TranslocateTimeLimit. If >0, translocate must be accepted within this number of seconds. + +Optional SQL: (Required to enable voice macro use on your server + +REPLACE INTO `rule_values` VALUES (0, 'Chat:EnableVoiceMacros', 'true'); + +==01/14/2009== +Derision: Fixed makefile references for rulesys.o +Derision: Mapped field in LogServer_Struct to allow voice macro window to be opened (needs work on Opcodes to make it functional). + +==01/12/2009== +Derision: Removed code that stole all your money if you logged on with more than 1 million of any denomination of coin on your person. + +==01/11/2009== +Angelox: Bots: Added rule 'EQOffline:BotCount' defaults to 5, for desired amount of bots in the group - values are 0-5 (0 means bots are disabled, max limit is 5). +Derision: Mail/Chatchannels: Added sanity check on packet size in EQPacket::ChatDecode. +Derision: Mail/Chatchannels: Increased stream timeout from 45 to 135 seconds. +Derision: Mail: Use correct opcodes for sending Headers. **utils/mail_opcodes.conf updated** +Derision: Chatchannels: Recognise and ignore AFK command. +Derision: Chatchannels: Added uptime command (usage ;uptime or /chat uptime). +Congdar: Bots: Added Lore item check to command #bot inventory remove + +==01/08/2009== +Congdar: Bots: Fix compile issue under linux for Visible Gear update. + +Angelox: Bots: Updated required SQL, utils/sql/svn/250_bot_spell_update.sql (some spells were still being auto-casted) +Derision: Chatserver: Fix for crash when multiple connections are made in quick succession. + +==01/07/2009== +Angelox: Bots: Added command '#bot charm' (requires enchanter in group). Charm does not work while Chanter has pet so I added the grouped Chanters pet to '#bot group remove' (remove the pet before #bot charm). +Angelox: Bots: '#bot sow wolf' should not affect pets anymore. +Derision: Chatserver: Added some extra syntax error checking to prevent crashes. +Angelox: Bots: Added Wizard class and level check to the '#bot evac' command +Angelox: Bots: Added '#bot invis see' for see invisible +Required SQL: +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=80; +Congdar: Fix NPC::RemoveItem(uint16 item_id) to uint32 to work with items that have id's larger than 65535. '#npcloot remove [itemid]' will now successfully remove items with larger id's from npc loot. +Congdar: Bots: Visible gear will now show correctly when trading/equipping bots. + +==01/05/2009 +Angelox: Bots: Re-did command '#bot cure'- one of three Curer class (Cleric is primary), will ask what you want if you use '#bot cure' alone. + +==01/04/2009== +Angelox: Bots: Added command '#bot endureb' (enduring breath). +Angelox: Bots: Hopefully fixed bug where other sowers replaced primary sower (Druid). + +==01/03/2009== +Angelox: Bots: remade/improved commands '#bot levitate'and '#bot invis' + +==01/02/2009== +Congdar: Bots: update command #bot inventory list Now shows gear as item links. Removed (Bot) as last name during bot creation. +Optional SQL: update npc_types set lastname='' where isbot=1; + +==01/01/2009== +Angelox: Bots: Added command '#bot sow' (Druid has options) +Angelox: Bots: Added command '#bot levitate'. +Angelox: Bots: Added command '#bot invis' - (has options). + +Required SQL: utils/sql/svn/250_bot_spell_update.sql + +Angelox: Bots: Added command '#bot resist' (has options, don't use optional sql if you don't want). + +Optional SQL: utils/sql/svn/250_optional_bot_spell_update.sql + +Derision: Added chatserver for Chat Channel support. mail_opcodes.conf updated in utils. +Derision: See: http://www.eqemulator.net/wiki/wikka.php?wakka=ChatChannels + +REQUIRED SQL: + +CREATE TABLE `chatchannels` ( + `name` varchar(64) NOT NULL, + `owner` varchar(64) NOT NULL, + `password` varchar(64) NOT NULL, + `minstatus` int(5) NOT NULL default '0', + PRIMARY KEY (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +also see utils/sql/svn/249_chatchannels.sql for suggested system channels. + +==12/31/2008== +Congdar: Bots: Added rule EQOffline:BotManaRegen default 1.0 is fastest. 3.0 is closer to player regen rates +Congdar: Bots: Added rule EQOffline:BotFinishBuffing default true. Allows out of combat buffing to complete even if the bot is out of mana. +Congdar: Bots: Fixed bug where bot ownership could change to somebody else. +Congdar: Bots: Added caster AA's for critical damage + +==12/27/2008== +Trevius: Changed QueryLoot so that commands that use it (#npcstats & #npcloot show) will now show Item Links instead of just Item Names. +Trevius: Added Item Links to #peekinv +Trevius: Added new console command "LSReconnect" to allow manually restarting the auto Login Server connect loops if they stopped for any reason +Trevius: Added a Reconnect option in the Web Tool to allow auto Login Server connect to restart if it has stopped and is not connected already. +Derision: New program, mailserver, to implement in-game mail. See: http://www.eqemulator.net/wiki/wikka.php?wakka=MailServer, + or mailserver/readme.txt + +Required SQL: + +CREATE TABLE `mail` ( + `msgid` int(10) unsigned NOT NULL auto_increment, + `charid` int(10) unsigned NOT NULL, + `timestamp` int(11) NOT NULL default '0', + `from` varchar(100) NOT NULL, + `subject` varchar(200) NOT NULL, + `body` text NOT NULL, + `to` text NOT NULL, + `status` tinyint(4) NOT NULL, + PRIMARY KEY (`msgid`), + KEY `charid` (`charid`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +ALTER TABLE `character_` ADD `mailkey` CHAR( 16 ) NOT NULL ; + +==12/22/2008== +Trevius:(Denivia) Pets that are on Hold will now follow their master +Trevius:(Denivia) Pets will no longer try to attack corpses + +==12/20/2008== +KLS: Removed old unused Guildwars, RaidAddicts and GroupLinking code. + +==12/19/2008== +KLS: Updated how spawn timers work, spawn timers will now be based off absolute time like regular timers which will allow them to tic down even when the server is offline. +KLS: Quest::clearspawntimers() has been disabled for now, will be reactivated in next update on spawn timers. +KLS: Likewise #repop force has been temp. disabled as well pending updated code. +=Update= +KLS: Enabled Quest::clearspawntimers() and #repop force once again. +KLS: Added quest::updatespawntimer(id, duration) to update the respawn time of a dead mob, this only applies if the mob is dead and will only apply once: ie. once the mob repops and dies again it will use it's normal respawn time unless this is called again. Not completely tested. +KLS: creategroundobject constructor should create objects with max charges now. Untested. + +Required SQL: +DROP TABLE IF EXISTS `respawn_times`; +CREATE TABLE `respawn_times` ( + `id` int(11) NOT NULL default '0', + `start` int(11) NOT NULL default '0', + `duration` int(11) NOT NULL default '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +ALTER TABLE `spawn2` DROP `timeleft`; + +==12/15/2008== +KLS: Added 2nd pass to heal aggro to correctly divide aggro between those recieved not just give all recieved full aggro. + +==12/13/2008== +Derision: Fixed a couple of causes of combat related zone crashes. + +==12/12/2008== +Derision: Name of the seller now reported correctly in the /buyer window. + +==12/09/2008== +AndMetal: Added Client::MakeItemLink function, which allows for augments in Item Links. Can be used for Chat & Task windows. See zone/inventory.cpp for detailed notes on usage in the source. +AndMetal: Updated quest::itemlink & loot drops to use MakeItemLink. +AndMetal: (trevius) Fixed a small typo in utils/export_spells.pl. + +==12/08/2008== +Trevius: Changed #itemsearch output to now place itemlinks for all items it outputs from the search +Trevius: Increased the max results of #itemsearch from 20 up to 50 +Trevius: Added alias #fi as an abbreviation of #finditem, to shorten the command +Trevius: Changed the default required status for accounts to access item search commands to min status of 10 (previously 0) + +==12/01/2008== +Trevius: Moved mounts (Horses/Drogmors) into their own table instead of them being hard coded in the source. + +Required SQL: utils/sql/svn/235_horses_table.sql + +==11/26/2008== +cavedude00: Traps will no longer be trackable. + +==11/24/2008== +AndMetal: Spells can now be loaded from the database, and is now enabled by default. Use utils/import_spells.pl to import, utils/export_spells.pl to export. Use -h on either for usage. +Trevius: Food and Drink will now actually give stats. + +Required SQL: utils/sql/svn/229_spells_table.sql + +==11/22/2008 +Trevius/AndMetal: Corrected the output of #showstats to provide accurate Attack Rating calculations. +Derision: Initialise animation to zero in the Mob constructor to prevent players sometimes appearing to run off when they first spawn. +Derision: /buyer /barter fix. +Trevius: Added Account Limiting to allow limiting how many characters can be logged in per account at once - See Optional SQL. + +==11/20/2008 +Derision: Implemented /buyer (cash compensation only) and /barter. Update your patch_Titanium.conf from the utils directory. + +REQUIRED SQL: + +DROP TABLE IF EXISTS `buyer`; +CREATE TABLE `buyer` ( + `charid` int(11) NOT NULL, + `buyslot` int(11) NOT NULL, + `itemid` int(11) NOT NULL, + `itemname` varchar(65) NOT NULL, + `quantity` int(11) NOT NULL, + `price` int(11) NOT NULL, + PRIMARY KEY (`charid`,`buyslot`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +ALTER TABLE `trader_audit` ADD `trantype` TINYINT NOT NULL DEFAULT '0'; + + +==11/20/2008 +cavedude00: (aza77) Added quest::collectitems. +cavedude00: (Rocker8956) Zones with IDs higher than 255 can now be instanced. + +==11/19/2008 +seveianrex: (Varkalas) WAR Sturdiness AA Fix +AndMetal: zone-*.log files on Linux will allow read access to group members. You may need to erase any existing logs to see the change. +AndMetal: Added some logging for spell crits (SPELLS__CRITS) + +==11/18/2008 +Derision: Bazaar: Added support for changing prices without ending Trader mode. + +==11/16/2008 +Derision: Added command 'undyeme' to restore all a player's armor slots to their natural undyed state. +Trevius: (Denivia) Changed Expansive Mind AA so that it raises the Worn Mana Regen Cap (as it should) instead of just increasing mana regen. +Derision: Bazaar bug fix relating to items with -1 max charges. + +Optional SQL: + +INSERT INTO commands (command, access, description) VALUES ('undyeme', 0, 'Remove dye from all of your armor slots'); + +==11/15/2008 +seveianrex: Implemented AA use of SE_Accuracy (Ranger GoD AA "Precision of the Pathfinder" will have an effect now) +seveianrex: Implemented Enchanter GoD AA "Mesmerization Mastery" +seveianrex: Adjusted Monk Kick Mastery damage modifiers slightly. +Derision: Using Find Trader in the Bazaar will teleport you to the Trader. +Derision: Added Rule Bazaar:EnableWarpToTrader, default true. +Derision: Added Rule World:TutorialZoneID, default 189 (tutorialb). +Derision: MinPrice/MaxPrice in the Bazaar search window are now interpreted correctly as values in Platinum. +Derision: Bazaar bug fix. + +==11/14/2008 +seveianrex: Adjusted Coat of Thistles AA modifier. +seveianrex: Implemented Pet Crits for NEC/MAG/BST (Deaths Fury, Elemental Fury, Warders Fury GoD era AAs) +Derision: Bazaar Trader mode (not Barter). Please test it well. +Derision: Updated OP_ShopDelItem for Titanium. + +REQUIRED SQL: + +DROP TABLE IF EXISTS `trader`; +CREATE TABLE `trader` ( + `char_id` int(10) unsigned NOT NULL default '0', + `item_id` int(10) unsigned NOT NULL default '0', + `serialnumber` int(10) unsigned NOT NULL default '0', + `charges` int(11) NOT NULL default '0', + `item_cost` int(10) unsigned NOT NULL default '0', + `slot_id` tinyint(3) unsigned NOT NULL default '0', + PRIMARY KEY (`char_id`,`slot_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `trader_audit`; +CREATE TABLE `trader_audit` ( + `time` datetime NOT NULL, + `seller` varchar(64) NOT NULL, + `buyer` varchar(64) NOT NULL, + `itemname` varchar(64) NOT NULL, + `quantity` int(11) NOT NULL, + `totalcost` int(11) NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +UPDATE doors set opentype=155 where opentype=154 and zone='bazaar'; + +==11/12/2008 +Congdar: Bots: add command #bot lore - Casts Identify on the item on your mouse pointer +seveianrex: Tweaked the Slay Undead damage to once again. Damage should be in-line with live numbers now. +Angelox: (Kobaz): Fix for 'PIC register bx clobbered in asm' error +Angelox: Improved filtering on [bot track rare] option for Ranger +Angelox: Added option [bot track near] for Ranger + +==11/11/2008 +Congdar: Bots: fix ghosting when runnning up to targets + +==11/10/2008 +Trevius/Derision: Fix for GM Training Point exploit when de-leveling and leveling up again + +==11/10/2008 +Derision: Implemented Looking For Group/Looking For Players Window. patch_Titanium_conf has been updated. +Derision: Visual Studio users should add world/lfplist.h and world/lfplist.cpp to their project. + +REQUIRED SQL: + +ALTER TABLE `character_` ADD `lfp` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `character_` ADD `lfg` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '0'; + +==11/08/2008 +Congdar: Bots: add command #bot resurrectme +Congdar: Bots: modify heal ai, more mana calcs +Congdar: Added Rule to allow melee to bind anywhere casters can bind, default is false +optional sql: + insert into rule_values values (1, 'Character:BindAnywhere', true); +seveianrex: Figured out what was crashing ranged procs. Is fixed and fully implemented now. Rangers rejoice.. +seveianrex: Modified the way #melody works to improve the delay between songs being casted. Bards rejoice.. + +==11/07/2008 +seveianrex: GoD Rogue AA's: Seized Opportunity, Triple Backstab implemented +seveianrex: (LeftRoad) Provide a damage message to client when mob is killed by a spell +Congdar: Bots: reduce spam, fix crash when fighting Dain, change mana/hp from npc to client like calcs +Congdar: make heading calculation more efficient, change sint8 to float for float calculations and heading variable +seveianrex: Added #melody as a supplement for /melody until the OPcode can be located. Don't forget to: + +INSERT INTO commands (command, access, description) VALUES ('melody', 0, 'A supplement for /melody until the OP code is found.') + +seveianrex: Added support for defensive and ranged spell-effect based procs. Having some zone-crash issues with the ranged proc code for some reason, so it's commented out until I can fix it. + +==11/05/2008 +Derision: Dyed armor color should now show up correctly to other players. +Derision: Implemented the Potion Belt. + +==11/03/2008 +seveianrex: Added some scaling to the way NPC hitboxes are calculated. This corrects some of the issues with mobs like Wurms/Dragons who you could hit from miles away. +seveianrex: NPCs who are 5% HP or lower, are fleeing, and have a decent snare on will no longer cover so much distance. +seveianrex: Implemented Mob::GetSnaredAmount() function, utilized in the above. Returns the ABS() value of the snare. + +==11/01/2008 +seveianrex: GoD AA Implementations: [CLR] Touch of the Divine, [BRD] Internal Metronome, [SHD] Improved Consumption of the Soul +Derision: Allow backtick through the CleanName filter. +Derision: Pickpocket should no longer occasionally bug the UI. +Derision: Pickpocketing money should no longer show twice the amount taken in the client. +Derision: Looting stackable PVPItem now works. +Derision: Client now updates correctly when a charge on an item with a right click effect is used. Update your patch_Titanium.conf + +==10/31/2008 +seveianrex: Monk updates: Strikethrough AA, Mend Worsen Frequency Tweak, Kick Mastery AA Fix +seveianrex: Implemented Pet Flurries for MAG/BST/NEC +Derision: Extended quest::popup to take optional PopupID and buttontype (OK or YES/NO) fields. + +==10/30/2008 +KLS: Spell crits should work reasonably well once again. +Derision: Fixed inability to jump introduced in Rev161. + +==10/29/2008 +Derision: Mobs in water should no longer sink to the bottom. +cavedude00: Added rule to enable DeathExpLossMultiplier, or to use server code default. + +Optional SQL: +Insert into rule_values values (0, 'Character:UseDeathExpLossMult', 'true'); + +==10/27/2008 +seveianrex: Added server-side code for WAR sturdiness AA +Angelox: (Cbodmer) Experience loss based on cbodmers formula with added rule to regulate loss amount +Trevius: (Denivia) Added Critical Spell Damage for Lifetaps with the proper AAs + +Optional SQL (options are 0-10, defaults to 3): +Insert into rule_values values (0, 'Character:DeathExpLossMultiplier', 3); + +==10/26/2008 +Derision: Implemented Bandolier. Test thoroughly before trusting it with your Epic. +Derision: Minor change to enable the spell Tiny Companion to work. + +==10/25/2008 +seveianrex: Slay Undead tweak + message gender fix, Tradeskill message gender fix +Trevius: Updated SQL file for R142 to correct some altadv_vars AA table issues with AA requirements + +==10/24/2008 +seveianrex: Implemented various GoD AAs: Coat of Thistles, Rapid Strikes, Elemental Durability, Subtlety2, Strengthened Strike, Vicious Smash +seveianrex: Implemented GoD Monk AA "Kick Mastery" +Trevius: Adjusted the new Frenzy AAs code to fix a crash +Trevius: Removed the Shield Block for Paladin/SK AA until code is revised and working +Trevius: (Denivia) Added Advanced Theft of Life and Soul Thief for Necros and SKs +Trevius: (Denivia) Added Death Peace AA +Trevius: (Denivia) Added Blur of Axes and Vicious Strike AAs for Berserkers +Trevius: (Denivia) Added Shield Block AA for Paladin/SK +Derision: 'Killing' LDoN chest type objects(e.g. Vermin Nests) will now update kill activities. +KLS: Revert Lag fixes that were causing movement and facing abnormalities. +KLS: Fix to a few problems with quest::creategroundobject that was causing it to not work in some situations. + +==10/23/2008 +Congdar: Clone NoDrop removal code to NoRent, Lore, NoTrade. Optionally enabled in the db Variables table DisasbleNoDrop=1 DisableNoRent=true DisableLore=1 DisableNoTrade=1 +Congdar: Bot code cleanup, method call reduction +Congdar: Bot DoubleAttack method is more like clients +Congdar: Remove AFK leveling with bots +Angelox: Added command '#Bot evac' for Druid bots +Angelox: Added command '#Bot target calm' for Enchanter or Cleric bots +Trevius: Removed the * 10 multiplier from the SE_ProcChance since it isn't needed +KLS: Zone appearance should update for players zoning. +KLS: Added command #modifynpcstat +KLS: Added quest::modifynpcstat(identifier, value) +KLS: Fix potential crash in MobProcess +KLS: Added decay option to CreateGroundObject +KLS: Added spelltype mez(2048) and spelltype in combat buff(1024). +KLS: Setting level for npcs will now update their spawn level too. +KLS: Fix potential memory leak in item link code. +KLS: Changed resists a bit. +KLS: Added rule: Spells:ResistPerLevelDiff(85) 8.5 resist points per level diff +KLS: Added rule: Combat:BaseHitChance(54.0) +KLS: Added rule: Combat:HitPerLevelDiff(145) 45% per level diff +KLS: Added rule: Combat:AgiHitFactor(0.015) +Derision: Added 'Repeatable' column to to tasks table. Default 1 (true). + +Required SQL: + +ALTER TABLE `tasks` ADD `repeatable` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '1'; + +==10/22/2008 +AndMetal: Fix for item links to prevent occasional zone crashes +AndMetal: Critical DOTs should now work correctly +AndMetal: Fixed sql/svn/125_aggrozone.sql to work with current command table schema +AndMetal: (Theeper) Shared Bank platinum. Added rule Character:SharedBankPlat(false) to enable since it is possible to dupe plat with multiple characters logged in on the same account at the same time +AndMetal: Fixed a few random compile warnings +Angelox: Bot Tracking improves as levels increase. +Angelox: Druid Bot is now next best tracker +AndMetal: Support for +Stat Cap (Wunshi's Focusing, etc) +AndMetal: New rules for tweaking spell crits: Spells:BaseCritChance(0), Spells:BaseCritRatio(0), Spells:WizCritLevel(12), Spells:WizCritChance(7), Spells:WizCritRatio(15) +AndMetal: Loot messages will now have item links instead of just the item name + +==10/21/2008 +KLS: Some changes to make compiling with profiler enabled more error free. +KLS: Added player quest event EVENT_TASK_STAGE_COMPLETE exports $task_id and $activity_id. +KLS: Added player quest event EVENT_PICK_UP exports $picked_up_id for when a player picks up a ground spawn or dropped item. +KLS: Added quest::CreateGroundObject(itemid, x, y, z, heading) which lets the quest script create ground objects in the zone like if they had been dropped. +KLS: Objects should decay after they're loaded back into the world. Might want to backup your objects table till I can confirm this works 100%. +KLS: Lowered ground object decay time from 30 min to 5 min. + +==10/20/2008 +AndMetal: (erde) Moved SVN SQL updates to utils/sql/svn. File name will start with the revision they were introduced & optional updates have optional somewhere at the beginning of the name +Derision/Trevius: Implemented sub EVENT_AGGRO_SAY +AndMetal: Implemented Reverse Damage Shield +AndMetal: Fixed +Damage Shield on items so that it doesn't work without having a Damage Shield spell first +AndMetal: (seveianrex) Hate w[h]iped on CoH +AndMetal: (via Yeahlight) New command: #aggrozone. Requires 100 status by default + +==10/19/2008 +Angelox: Added a start to Bot tracking - Thanks Derision for all the help and know-how. +Derision: Altered damage shield message processing to be more like (the same?) as live. +Derision: If it exists, damage shield types are read from a new table (damageshieldtypes) +Derision: If no entry exists in the table, a default based on resist type is used. + +Optional SQL: See utils/sql/damageshieldtypes.sql + +==10/18/2008 +KLS: Reworked classic style traps. +KLS: Debuff style traps should function perfectly, the level field in the DB gives what level the caster is for resists in this case. +KLS: Added respawn and respawn variance to traps. +KLS: Increased range on sense traps. +KLS: Sense trap will now point the player in the direction of the trap they are sensing. +KLS: Disarm trap will no longer use sense trap's timer. +KLS: Disarm trap will no longer fail to disarm traps on success. +KLS: Increased range on disarm trap slightly. +KLS: Change to MakeRandomInt() and MakeRandomFloat() +KLS: Int generation be nearly 100% or more faster in most cases and float generation should be slightly faster in most cases +cavedude00: (seveianrex) Slay Undead Fix +cavedude00: (seveianrex) Levitate effect will no longer be removed in cases where you have two stacked lev spells and the first wears off. +cavedude00: (seveianrex) Hate list will now be cleared following CoH +cavedude00: (seveianrex) Group members will now see tradeskill emotes. + +Required SQL: +ALTER TABLE `traps` DROP `spawnchance`; +ALTER TABLE `traps` ADD `respawn_time` INT(11) UNSIGNED DEFAULT '60' NOT NULL AFTER `skill`; +ALTER TABLE `traps` ADD `level` MEDIUMINT(4) UNSIGNED DEFAULT '1' NOT NULL AFTER `skill`; +ALTER TABLE `traps` ADD `respawn_var` INT(11) UNSIGNED DEFAULT '0' NOT NULL AFTER `respawn_time`; + +==10/17/2008 +KLS: Tweaks to /pet attack +KLS: Renamed WhipeHateList to WipeHateList so I never have to view that spelling monster ever again. +cavedude00: (Rocker8956) Added shutdowndelay to zone query. + +==10/16/2008 +Derision: Cosmetic change to foraging to correctly identify food/drink items. +cavedude00: Added rules to determine if /ooc and /auction should be server wide (true) or zone wide (false - Live Like). +cavedude00: #peqzone can no longer be used if invulnerable. +KLS: More changes to proc code. Added an option of calc. proc rate based on speed of the weapon +KLS: Added Rules: Combat:AdjustProcPerMinute(true), Combat:AvgProcsPerMinute(18.0), Combat:ProcPerMinDexContrib(0.075), Combat:BaseProcChance(0.035), Combat:ProcDexDivideBy(11000) +KLS: Tweak to monster AI to hopefully save computation. + +Optional SQL: +insert into rule_values values (0, 'Chat:ServerWideOOC', 'true'); +insert into rule_values values (0, 'Chat:ServerWideAuction', 'true'); + +==10/15/2008 +Derision: Added OP_LevelAppearance to utils/patch_Titanium.conf and it is now sent to nearby clients (eye candy when you level) +Derision: (seveianrex) Alcohol Drinking / Skillup Fix +KLS: Change to proc code to correct some oddities with % chance to proc. +KLS: Changed how avoidance bonuses are calculated to be like other melee bonuses. +KLS: (seveianrex) Basic Pet Focus Implementation. + +==10/14/2008 +Trevius: Charm Spells set to max level of 0 now have unlimited max level as they should +Derision: Player pets should no longer be fearable if npcspecialattks contains 'D'. +cavedude00: (Rocker8956) Changes to quest::getlevel and quest::setinstflag/setinstflagmanually to reduce DB access +cavedude00: (Rocker8956) Added quest::getinstflag +KLS: Fixed crash in shutdown delay, please test things for crashes before adding them, this was pretty obvious. +Derision: Removed requirement to be grouped for Translocate spells. + +==10/13/2008 +Derision: Looting your corpse while liched/wolf form etc should now auto-equip items + +==10/12/2008 +Derision: No more binding wounds while feigning death. +Derision: (Rocker8956) Movegrp quest command fix. +Derision: (LeftRoad) TEMPORARY(NORENT) items in a character inventory will be retained if you log back into that character within 30 minutes. +KLS: Implemented Raid::TeleportGroup() and Raid::TeleportRaid(), untested but should work. +KLS: (seveianrex) Critical DoT and Sinister Strikes AA. + +==10/11/2008 +Derision: Resurrection fix. +KLS: Rebalanced skill ups. + +==10/10/2008 +AndMetal: Added functions to calculate AA bonuses just like item & spell bonuses (not turned on yet) +Condgar: Fix for Bots wont Attack +Congdar: Fix low level bots hitting too hard +Derision: Prevent two instances of possible zone crashes due to null pointers. +Trevius: (Striat) Added quest commands for zone and world emotes - quest::ze() & quest::we() +Derision: Recalculate Pet bonuses on zoning before setting current HP/Mana. + +==10/09/2008 +Congdar: Replace Client IsEngaged() checks in Bot Source with bot aggro check +Trevius: (AndMetal) Added quest Object SetOOCRegen() to adjust NPC Out of Combat Regen on the fly. +Trevius: Relocated the Empty Corpse check code so that the Decay Time rule for it will now work. +Trevius: Removed the Level 70 cap on the #traindisc command. +Trevius: Added optional rule to allow Max Level Attainable via experience to be set independant of other Max Level rule checks. +Derision: Reworked code to stop mobs fleeing if they have buddies to account for recent hate_list changes. + +Optional SQL: +Insert into rule_values values (0, 'Character:MaxExpLevel', 0); + +==10/08/2008 +Congdar: Fix orphaned bots when camping/zoning +Congdar: Link bot assist with client auto attack +Derision: Added #titlesuffix command to add a title after a player's name. + +==10/07/2008 +Congdar: Allow bot pets to be the main tank in a bot raid +Congdar: Remove old Sense Heading skill check +AndMetal: Web interface will now list open petitions. +Derision: #title should now prefix the target player's name, e.g. #title Lord_Protector (underscores are replaced with spaces, max 31 chars) + +==10/06/2008 +Angelox: Fix Bot names in group window (now is 'Mybot' instead of 'Mybot000') +cavedude00: (erde) Fix for world and zone crashing when compiled on VS 2008 in release mode. +cavedude00: (Rocker8956) Zone shutdown timer rule. +cavedude00: Added cleanipc to makefile. + +Required SQL: +ALTER TABLE `zone` ADD column `shutdowndelay` bigint (16) unsigned NOT NULL default '5000'; +INSERT INTO rule_values VALUES (1,'Zone:AutoShutdownDelay', 5000); + +==10/05/2008 +Derision: Total time playing in /played now maintained across sessions. +Derision: Corrected OP_DuelResponse/OP_DuelResponse2/OP_Shielding opcodes in utils/patch_Titanium.conf + +==10/04/2008 +Congdar: Double Attack redo, tested working +Congdar: Fix Bot follow bug when a bot gets killed +Derision: Added minlevel and maxlevel fields to tasks table and new quest function istaskappropriate(task). +Derision: All tasks in a TaskSet with a taskid of 0 as one of it's entries are now available to a player, subject to level restrictions. +Derision: (Spoon/Andmetal) Rez spells with effectdescnum != 82 or 39067 (6.2 spells_us) will give 100% Mana/HP restoration and no rez effects. +Derision: Corrected message when interrupting a spell with Shift-S. +Derision: Fix to display Frogloks correctly in /who all and /who all froglok (half of the change came from a post by Theeper on the forums). + +Required SQL: +ALTER TABLE `tasks` ADD `minlevel` TINYINT UNSIGNED NOT NULL DEFAULT '0', +ADD `maxlevel` TINYINT UNSIGNED NOT NULL DEFAULT '0'; + +==10/03/2008 +Congdar: Add EQOffline Bot Source +Derision: Removed 'stepped' column from task table. See http://eqemulator.net/forums/showthread.php?p=157613 +Derision: Added display of Task Description to #task show to aid in debugging a reported problem. +Derision: Lowered default required status for #task command from 250 to 150 for debugging purposes. +KLS: Removed some lingering printf from the raid stuff. +KLS: Auto attack code changed to not use target, instead we save the first target and now always use that. +KLS: Hate list will no longer work for non ai controlloed player characters. +KLS: Fix to crash in monk special attack function. + +Required SQL: ALTER TABLE `tasks` DROP `stepped` ; + +==10/02/2008 +KLS: (Congdar) Update to Tech. of Master Wu AA. +KLS: Revert of double attack change, people reporting non warriors were no longer double attacking properly. +KLS: Compile Warning in Database::GetZoneName() +KLS: Change to IsPlayerIllusionSpell() to make it more consistant, illusion spells can be more than 49 so we work off behavior and effect now. +KLS: Added AA__Message logs to project illusion code to try to track down an error. + +==10/01/2008 +Derision: (erde) VS2008 Compile Fix (#if (_MSC_VER < 1500) #define vsnprintf _vsnprintf #endif +Derision: Fix to stop zone crashing when looting PVPItem and PVPReward is set to 3. +cavedude00: Group members will no longer recieve a split when a player corpse is looted. + +==9/30/2008 +Derision: Task activities with an activitytype of <= 0 in the activities table will be sent with an activitytype of 9 to the client. +Derision: The reward field in the task table will now be displayed even if rewardid=0 +cavedude00: (Rocker8956) Added quest function to get average group/raid level. +cavedude00: (Rocker8956) Changes to setinstflagmanually to allow deletion of instance flags and to allow manual flagging of raids, groups, and individuals. + +==9/29/2008 +cavedude00: Added rule World:ClearTempMerchantlist to control whether world clears temp merchant items when started or not. +AndMetal: (Congdar) New Double Attack logic +Angelox: Keyring code fix, so clicky portals that require keys will work too. +AndMetal: Added AAs: Discordant Defiance, Blacksmithing Mastery, Baking Mastery, Brewing Mastery, Fletching Mastery, Pottery Mastery, Tailoring Mastery, Packrat, Expansive Mind, Veteran's Wrath, Advanced Fury of Magic Mastery, Destructive Fury +AndMetal: Ayonae's Tutelage should now calculate bonus for Singing + +Optional SQL: +insert into rule_values values (0,'World:ClearTempMerchantlist','true'); + +==9/28/2008 +AndMetal: Fixed exploit for pets ignoring Fear by using /pet commands +AndMetal: #wp add will now use the highest value in the grid_entries table + 1 if you don't use a Waypoint # or use 0 + +==09/26/2008 +cavedude00: (AndMetal) Deathblow AA +cavedude00: (AndMetal) Swift Journey AA +cavedude00: (AndMetal) Convalescence and Healthy Aura AAs +cavedude00: (AndMetal) Slippery Attacks AA +cavedude00: (trevius/Derision) Additional IP limiting rules +cavedude00: (Congdar) Further PC main and second hand attack fixes +cavedude00: (Derision) Fix for percent heals +cavedude00: (Theeper) Fix for quest:itemlink() +cavedude00: (Rocker8956) More work on zone instancing + +Optional SQL: +Insert into rule_values values (0, 'World:AddMaxClientsPerIP', -1 ); +Insert into rule_values values (0, 'World:AddMaxClientsStatus', -1 ); + +==09/25/2008 +cavedude00: (trevius) IP Limiting Minor Fix +cavedude00: (trevius) New Quest Command quest::clearspawntimers() +cavedude00: (trevius) New Quest Command quest::traindiscs() +cavedude00: (trevius) Enraged, Flurry and Rampage Spamming Fix +cavedude00: (seveianrex) Implemented Project Illusion AA +cavedude00: (seveianrex) Corrected AA ding message +cavedude00: (Congdar) PC main and second hand attack fixes +cavedude00: (Cantus) 2H weapon damage bonus +cavedude00: (Derision) /who all friend +cavedude00: (Derision) All merchants will now show player sold items correctly +cavedude00: (Derision) Enter tutorial and Return Home buttons during char creation/select +cavedude00: (Derision) Implemented Task system +cavedude00: (Derision) Implemented Translocate and Sacrifice Confirmation Boxes +cavedude00: (Derision) Client popup window +cavedude00: (James76) Implemented keyring +cavedude00: (AndMetal) Corrected Technique of Master Wu AA +cavedude00: (AndMetal) Corrected some proc spells not working after zoning +cavedude00: (Hugghiebear) Charm will now fade if invis is cast +cavedude00: (AiliaMorisato) New command #advnpcspawn and related functions +cavedude00: (Rocker8956) Preliminary zone instancing +cavedude00: (KLS) Implemented raid system +cavedude00: Fixed autosplit (Thanks WildcardX!) +cavedude00: New command #peqzone +cavedude00: Several minor forage/fishing fixes + +Required SQL is in utils/sql/09252008.sql +Make sure you update your .conf files, also found in utils! + +==09/01/2008 +KLS: (derision) Crash Fix. +KLS: (haecz) Non-Melee damage filter. +KLS: (LordKahel) Lore Slotted Augment. +KLS: (Spider 661 via trevius) Chaotic Potential AA +KLS: (Derision) OP_Sound update for titanium. +KLS: (ndnet via trevius) MQ Illegal Item Equip Detection +KLS: Modified faction modifiers for buying/selling. +KLS: (AndMetal) Spell blocking implementation +KLS: (Derision) Resurrection confirmation +KLS: (Cripp) Delete stale corpse backup typos +KLS: (AndMetal) CHA stacking buff bug resolved. + +Required SQL: +CREATE TABLE `blocked_spells` ( + `id` int(11) NOT NULL auto_increment, + `spellid` mediumint(8) unsigned NOT NULL default '0', + `type` tinyint(4) NOT NULL default '0', + `zoneid` int(4) NOT NULL default '0', + `x` float NOT NULL default '0', + `y` float NOT NULL default '0', + `z` float NOT NULL default '0', + `x_diff` float NOT NULL default '0', + `y_diff` float NOT NULL default '0', + `z_diff` float NOT NULL default '0', + `message` varchar(255) NOT NULL default 'You cannot cast that spell here', + `description` varchar(255) default NULL, + PRIMARY KEY (`id`) +) + +==08/15/2008 +KLS: Fix for groups clearing on new group create. +KLS: Fix for null row access in Database::GetLeaderName() +KLS: CountDispellableBuffs now will validate buffs spells. + +==08/14/2008 +KLS: Very basic work on raids. +KLS: Moved group id calls to their own database as to not be blocked by long character_ selects and such. + +Required SQL (Your groups will break completely without this): +CREATE TABLE `group_id` ( + `groupid` int(4) NOT NULL, + `charid` int(4) NOT NULL, + `name` varchar(64) NOT NULL, + PRIMARY KEY (`groupid`, `charid`) +) +ENGINE = InnoDB; + +==08/13/2008 +KLS: Added dispel field for npc spells as 512. +KLS: NPCs should be smarter about when they choose to cast certain spells; namely dots, lifetaps and dispels. +KLS: NPCs should continue to cast nuke spells for a longer period of time. +KLS: NPCs will no longer have a 2-3 sec forced delay between spell casts; it is now whatever the recovery time of the spell is just like players, beware. +KLS: (trevius) Change to npcAI recast delay cap 1000->10000 seconds. +KLS: Increased amount of mana npcs gain from int and wis. +KLS: New rule: NPC:CorpseUnlockTimer (150000) 2.5 min default on corpses being unlocked after loot instead of decay time / 2 +KLS: New rule: NPC:EmptyNPCCorpseDecayTimeMS (0) empty npc corpses will decay after this long + 1000MS on the ground. +KLS: More changes to groups: text should now be correct in *almost* all situations. +KLS: Groups should stay synced in almost all situations and leader disbanding should be smoother. +KLS: Groups will clear their info on the aquisition of a reused id. +KLS: .conf file from earlier /rewind change should be correct now make sure to update it if you want it to function. + +Optional SQL: +UPDATE npc_spells_entries SET type='512' WHERE spellid='48'; +UPDATE npc_spells_entries SET type='512' WHERE spellid='49'; +UPDATE npc_spells_entries SET type='512' WHERE spellid='1526'; +UPDATE npc_spells_entries SET type='512' WHERE spellid='1697'; + +==08/11/2008 +KLS: Group leader info should transfer to a new zone when the leader transfers to that zone. +KLS: Will now force a group update to players not in zone, text not correct. +KLS: Added #refreshgroup command - will refresh group visually from DB +KLS: Deleting stale corpses will now bury corpses instead of make them decay if shadowrest is enabled. +KLS: Small change to temp merchant lists with regards to charges. +KLS: Initial implementation of Technique of Master Wu +KLS: Spell's fizzles will now base their mana cost on the correct amount. +KLS: Tweak to spell casting expertise and mastery of the past +KLS: Hopefully fix to AEUndead spells hitting more than undead + +Required SQL (Your groups will probably break completely without this): +CREATE TABLE `group_leaders` ( + `gid` int(4) NOT NULL, + `leadername` varchar(64) NOT NULL, + PRIMARY KEY (`gid`) +) +ENGINE = InnoDB; + +==08/09/2008 +KLS: Minor tweaks to heal aggro. + +==08/07/2008 +KLS: Bald char fix revert, will try something else. +KLS: Wake The Dead initial implementation. +KLS: Hopefully better bald character fix base on kraeger's findings. + +==08/04/2008 +KLS: (Derision) Pet Buff Window Implemented. +KLS: Charmed pets should now function with the pet window, as well as appear correctly in group. +KLS: Added rule Character:SkillUpModifier (100) to govern how fasts skill ups happen on a server. 100% = normal chance, 200% = double normal chance. +KLS: (Derision) Implemented quest journal npc say. +KLS: (Derision) Fix for npc not always facing attacker. +KLS: (Derision) Stop Pets Fleeing +KLS: (CodeMephit) Hunger and thirst for new characters. +KLS: Upped food/water eat/drink gains by a lot. +KLS: (greggg230) Aggro LOS fix. +KLS: Fix for bald characters hopefully. +KLS: (kraeger) RNG name fix. +KLS: Big bug fixes with healing aggro. +KLS: Charm spells will stop making a cha check after a certain resist adj threshold +KLS: Adjusted melee hit chances to be more forgiving. +KLS: Changed how +hit mods are applied +KLS: Roughly implemented Skill Attack spell effect. + +Required: +patch_6.2.conf and patch_Titanium.conf files have changed be sure to update them. + +==07/21/2008 +KLS: (irv0) Fix for out of order ack not being sent in some situations. +KLS: (Derision) Pet bar OOC update fix. +KLS: Should have made client pets unable to give experience, untested but should work. +KLS: Healing aggro should function better for people not on the hate list. +KLS: Some work on public tradeskill objects saving their content between uses. + +==06/22/2008 +KLS: Changed world/clientlist.cpp's line endings back to unix style line endings +KLS: Fixed up ipban based on updated code from TheLieka. + +==06/21/2008 +KLS: Belated updates to azone including (derision)EQG fixes and some changes to make it easier to compile under windows. +KLS: Fixed inconsistant line endings in ruletypes.h; there are probably more line ending changes I didn't catch, please try to keep your line endings consistant with what's in the repo. + +==06/19/2008 +Scorpious2k (Knightly): Correction of divine intervention text +Scorpious2k (LordKahel): Support for defensive Instinct and Reflexive Mastery AA + +==06/18/2008 +Scorpious2k (Derision): Fix for flee runspeed - linear flee speed reduction as HP drops +Scorpious2k (Derision): Rule to prevent mobs from fleeing if they are being helped by other NPCs +Scorpious2k (haecz): Distance check for corpse dragging +Scorpious2k (haecz): Distance check for taunt +Scorpious2k (greggg230): Merchant price faction/charisma fix +Scorpious2k (greggg230): Faction will now show on /con for agnostic players +Scorpious2k (BatCountry): Correction of a zone crash caused by reloading rules +Scorpious2k (Congdar): Eliminated array index error/zone crash in spells + +==06/17/2008 +Scorpious2k (TheLieka): Ban by IP +Scorpious2k (cavedude/TheLieka): Ability to limit melee guys from being bound in certain zones. This changes the canbind + column of the zone table. Value 0 means noone can bind, value 1 means only casters can bind, value 2 means + anyone can bind in the zone (ie cities). + +Required SQL: +CREATE TABLE `Banned_IPs` ( + `ip_address` VARCHAR(32) NOT NULL, + PRIMARY KEY (`ip_address`) +) +ENGINE = InnoDB; + +Optional SQL: +Insert into rule_values values (0, 'World:UseBannedIPsTable', 0); +Update zone set canbind = 2 where zoneidnumber in (1,2,3,8,9,10,19,23,24,29,40,41,42,45,49,52,54,55,60,61,62,67,75,82,83,106,155); + +==06/14/2008 +Scorpious2k(Trevius): Door names can now go beyond the 16 char limit to allow doors and other objects from later expansions + to be used. The new max is 32 characters. +Scorpious2k(Derision): New fear adjustment to cause mobs to flee at the correct rates instead of running very fast at certain + percentages of health. +Scorpious2k(TheLieka): Limit the number of connections for an IP address There are 2 new rule values. + World:MaxClientsPerIP = Maximum number of simultaneous EQ Client connections allowed per IP address. + Set the rule value to -1 to disable this feature. + World:ExemptMaxClientsStatus = Minimum Account status to exempt the MaxClientsPerIP rule. This is + helpful for the inevitable random family of 16 that live together, and all want to play on your server, + as well as GMs, Devs, and QA staff. Again, set the rule value to -1 to disable this feature. +Scorpious2k(TheLieka): Added /rewind command +Scorpious2k(Striat): Quest Commands for Temp Race, Texture, Size and Gender Changes + +Required sql: +ALTER TABLE `doors` MODIFY COLUMN `name` VARCHAR(32) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL; + +==05/30/2008 +Scorpious2k: (Derision/Wiz) Added code to implement fear. +KLS: (Leika) Implemented detection of various MQ activities. +KLS: EVENT_CAST_ON should now export $spell_id properly. +KLS: Further expanded on fear implementation including addition of rules to govern it's behavior. +KLS: Fear on clients still uses stun but the stun should match up with the buff duration in a more accurate manner. + +Hackers SQL table: +DROP TABLE IF EXISTS `hackers`; +CREATE TABLE `hackers` ( + `id` int(4) NOT NULL auto_increment, + `account` text NOT NULL, + `name` text NOT NULL, + `hacked` text NOT NULL, + `zone` text, + `date` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) AUTO_INCREMENT=8; + +==04/26/2008 +KLS: Should have fixed discipline and combat ability timer overlap. +KLS: Added a rule for partial hits on fear, seperate from normal resist partial hits. +KLS: Fixed some quirks in the aggro system. +KLS: (Bulle)Added quest event EVENT_KILLED_MERIT will trigger if you got credit for doing the most damage to a lootable npc. +KLS: (Bulle)Added quest event EVENT_CAST_ON will trigger if a player casts on a npc. +KLS: Fixed crash in server-side check for removing detrimental effects. +KLS: (Magoth78), (Angelox)Added zone checks for levitating and being outdoors. +KLS: (haecz) Fix to dupe corpse money on a zone crash. +KLS: Changed root to resist per tic instead of partial hit. + +Required SQL: +alter table `zone` add column `canlevitate` tinyint (4) DEFAULT '1' NOT NULL after `cancombat` +alter table `zone` add column `castoutdoor` tinyint (4) DEFAULT '1' NOT NULL after `canlevitate`; + +==04/22/2008 +Rogean: Server-side check for removing detrimental spells. + +==04/13/2008 +Rogean: Fixed a hack to sell no-drop items to merchants. (Reported by KingMort) +Rogean: Fixed #hideme, it won't show you zone in and then disappear anymore. (Reported by KingMort) +Rogean: Fixed /summon. +Rogean: Changes to /who all and GM's: + * GM * Tags will no longer show up unless your #gm on + You will not show up to players of lower status if your /anon and #gm on, if your #gm off and /anon you will show up as + a normal player, regardless of statuses. + +==04/09/2008 +KLS: (AiliaMorisato)Fixed death packet bindzoneid and attack_skill fields being switched. +KLS: Added rule based caps for all PoP item abilities. +KLS: Agility will now affect a defenders chance to be missed slightly. +KLS: Added accuracy rating (10 AR. = 1% chance to hit) and (LordKahel)ATK to npcs. +KLS: Changed how mitigation is calculated slightly. +KLS: Enc animations will now respond to /pet get lost no matter what level of the animation empathy aa they have. +KLS: Hopefully fixed soul abrasion and consumption of the soul aas... again. +KLS: Added quest::forcedooropen(doorid), quest::forcedoorclose(doorid), and quest::isdooropen(doorid). +KLS: Changed archery calculations to be more similar to normal weapon damage calculations. +KLS: Taunt will now always add hate if the user is already on the top of the hate list. +KLS: Fixed EVENT_ZONE player event. + +Character:ItemDamageShieldCap (30) +Character:ItemAccuracyCap (150) +Character:ItemAvoidanceCap (100) +Character:ItemCombatEffectsCap (100) +Character:ItemShieldingCap (35) +Character:ItemSpellShieldingCap (35) +Character:ItemDoTShieldingCap (35) +Character:ItemStunResistCap (35) +Character:ItemStrikethroughCap (35) + +Required SQL: +ALTER TABLE npc_types ADD ATK MEDIUMINT NOT NULL DEFAULT '0'; +ALTER TABLE npc_types ADD Accuracy MEDIUMINT NOT NULL DEFAULT '0'; + +==04/01/2008 +Rogean: Fixed a merchant purchase packet exploit. + +==02/28/2008 +WildcardX: Tweaked the code for the Divine Intervention spell line code and the Unfailing Divinity AA ability. + +==02/27/2008 +WildcardX: Implemented HeadShot AA Ability. +WildcardX: Archery and throwing attacks will not cause you to suffer injury from your target's damage shield. +WildcardX: Implemented Spell: Death Pact. +WildcardX: Implemented Spell: Divine Intervention. +WildcardX: Implemented Unfailing Divinity AA Ability. + +==02/25/2008 +WildcardX: Enchanters can now control their pets if they purchased the Animation Empathy AA. +WildcardX: Mobs now have a chance to resist fear spell line each tic. +WildcardX: Mobs now have a chance to resist charm spell line each tic. +WildcardX: Implemented Total Domination AA. +WildcardX: Reworked new charisma test. This test now evaluates factors like MR, CHA, mob and caster levels. It yields better live-like results. +WildcardX: Enchanter pets now get the /pet report and /pet health commands by default. + +==02/23/2008 +WildcardX: Only characters with Pet Affinity AA can have group buffs casted on their pets. +WildcardX: Fixed a bug that would not allow a rune buff to be used in some situations. + +==02/22/2008 +WildcardX: Characters can now have only one caster specialization skill above 50. If more than one specialization skill is detected above 50, then all specialization skills are reset to 1. +WildcardX: Reworked mana reduction calculations. +WildcardX: Implemented spell casting specilization checks. + +==02/21/2008 +WildcardX: Removed the possible fix for the Call of Hero spell as it didnt actually fix the issue after extensive testing. +WildcardX: Added new command #scribespell. This will scribe a specified spell into the target's spell book. +WildcardX: Added new command #unscribespell. This will unscribe a specified spell from the target's spell book. + +==02/20/2008 +KLS: Zone crash fix caused by calling the TryWeaponProc() method when dead. +WildcardX: Changed the quest function depopzone() to accept a parameter to specify if the spawn timers should resume or become disabled. 0 = Disable, 1 = Enable. +WildcardX: Added the quest function repopzone(). This function will cause a zone to repop it's spawns normally. +WildcardX: Possible fix for a zoning bug caused by the Call Of Hero spell. +WildcardX: Harmony/Pacify line of spells will now cause aggro when resisted. +WildcardX: Added a check against CHA to avoid aggro from a resisted Harmony/Pacify spell. + +==02/19/2008 +WildcardX: Found a small efficiency for the code that determines if an item is equipable. +WildcardX: Re-worked the rune spell buff code to make it more efficient and reduce CPU utilization. +WildcardX: Added the quest function depopall(int npctype_id). This will remove all mobs from the zone with the specified npctype_id. +WildcardX: Added the quest function depopzone(). This will remove all mobs from the zone and NOT cause a repop. + +==02/10/2008 +WildcardX: Mesmerize line of spells will now cause aggro when casted on a mesmerize immune mob. +WildcardX: Fixed enchanter spell Theft of Thought. This spell will now work as described. +WildcardX: The pacify/harmony line of spells will no longer require a line of sight check to complete a cast. + +==01/28/2008 +WildcardX: Regenerated perl_mob.cpp due to deprecating the following methods: GetFamiliar(), SetFamiliar(), GetRune(), SetRune(), GetMagicRune(), SetMagicRune(). +WildcardX: Enchanters, this is your patch! Characters will now benefit from all rune and spell rune spell effects all spell buffs will provide, consistent with spell rules. +WildcardX: Rune and spell rune spell buffs will now persist zoning and camping. +WildcardX: Fixed a zone crash caused by TryWeaponProc() method. + +==01/27/2008 +WildcardX: Subtle changes to zoning code to allow both the 6.2 and Titanium client to perform all zoning operations similiar to live. +WildcardX: Fixed #zone and #goto commands for both the 6.2 and Titanium client. + +==01/25/2008 +WildcardX: Regenerated perl_client.cpp and perl_groups.cpp due to parameter changes for Group::TeleportGroup and Client::MovePC methods. Changes to some quest files may be necessary. +WildcardX: void MovePC(int32 zoneID, float x, float y, float z, float heading) +WildcardX: void TeleportGroup(Mob* sender, int32 zoneID, float x, float y, float z, float heading) +WildcardX: Pets will now report their buff list along with their health. +WildcardX: Fixed a bug that resulted in a zone crash if spell error logging is turned on. +WildcardX: (cavedude) Corrected an item id and some small typos in fishing. +KLS: Fixed a potential crash in Mob::CheckWillAggro() +KLS: Tweaked melee accuracy. +KLS: Ironed out some quirks between min hit and the AC code that was keeping it from mitigating some hits into misses. +KLS: (Knightly)#time should no longer be off by an hour, $zonehour, $zonemin and $zonetime are now exported to perl +KLS: Added player quests, player quests are quests designed to be attached to player processes such as zoning and clicking objects. +KLS: Player quests are loaded as either 'quests/zonename/player.pl' or as a template in 'quests/template/player.pl' +KLS: Player quests will export qglobals the client should be able to see and the following events are available for player quests: +EVENT_TIMER with var $timer +EVENT_CLICKDOOR with var $doorid +EVENT_LOOT with vars $looted_id and $looted_charges +EVENT_ZONE with var $target_zone_id +EVENT_ENTERZONE with no special var +EVENT_LEVEL_UP with no special var + + +==01/24/2008 +WildcardX: Removed a 64 character cap length on merchant names sent during a merchant greeting. +WildcardX: Fixed a bug that allowed players looting their corpse at just the right time, to duplicate their items. +WildcardX: Fixed a bug I caused in group portals when I corrected the Succor/Evac line. +WildcardX: Druids and Wizards rejoice! Succor/Evac will now perform a real zone and clear your aggro! +WildcardX: GM's can now summon players from one zone to another. +WildcardX: Cleaned up some of the code that handles zoning. +WildcardX: (cavedude) Reworked LeaveCorpses and LeaveNakedCorpses rules to allow for more options. +WildcardX: (cavedude) Created Character:DeathItemLossLevel rule to define when a player will leave items on a corpse. + +Required SQL: +insert into rule_values values(0, 'Character:DeathItemLossLevel', 10); + +==01/22/2008 +KLS: Clients will be immune to proximity aggro until they are finished loading now. +KLS: Slightly reduced the melee accuracy of clients. +KLS: Melee mitigation should now properly enforce minimum damage. +KLS: Increased the level based damage bonus for high level characters in melee combat. +KLS: Removed tic by tic healing aggro. +KLS: Fixed several potential crashes in various parts of the code. +KLS: (Knightly)Merchants will now use their clean names when conversing with players. +KLS: Fixed some improper casting in the GMSummon code. +KLS: Figured in a work around for corpses between the server and client becoming unsynced for /corpse +KLS: AA Consumption of the soul should now function correctly. +KLS: AA Soul Abrasion should now function correctly + +==01/19/2008 +KLS: Slightly reduced the effectivness of the Flurry AA +KLS: Speed of the Knight AA implemented +KLS: Evade will now reduce hate by a static amount, the static amount of hate increased slightly. +KLS: Healing aggro should now not aggro pets instead of their owners, +KLS: Removed the clause that made it so healing aggro was only effective if the healer had 100 hate or more on the target, effectivly making healers below level 30 immune to healer aggro. +KLS: HideReuseTime on the server reduced by 1 second, should reduce the number of times a player sees the 'Reuse time not met' error message. +KLS: Pets should now default to taunt ON instead of taunt OFF +KLS: Fixed a typo in ZoneDatabase::UpdateSpawn2TimeLeft() + +KLS: LDoN treasure should now really check only class and not body type. +KLS: Added new rule NPC:BuffFriends, if false npcs will only heal and not buff their friends, defaults to false. +KLS: Changed Mob::CheckHitChance() should be more consistant throughout the levels; puts more value into skill levels. +KLS: Changed Mob:AvoidDamage() to use a single roll system, as well as applied dex to parry, block and riposte chance and agil to dodge. +KLS: Brought the melee attack formula more in line with what it should be. +KLS: Added a moderate melee damage bonus to monks, and a small one to all classes at 50, 55 and 60. +KLS: Fixed an issue with strikethrough where it could go off even if you had no chance to strikethrough. +KLS: NPCs will now load their skills from the max skills of their level via the skill caps database instead of being randomly generated. +KLS: Increased the damage of backstab from str and dex and the general damage of several monk abilities. +KLS: Mob::GetProcID() should now try to fix SK spells automatically. +KLS: Aggro changes: Aggro has been changed to be based on a more live-like system, it is now based on potential damage instead of actual inflicted damage, hate is now unaffected by melee mitigation and resists. +KLS: Implemented jolt like effects straight into the aggro system, such effects should be calculated into a spells aggro regardless of resistance now. +KLS: Added an optional smart aggro list controlled by rule Aggro:SmartAggroList; true will enable it. This aggro list attempts to choose targets in a much smarter fashion, prefering players to pets, sitting and critically injured players to normal players, and players in melee range to players not. + +Added several rules to adjust the aggro system with: +Aggro:SmartAggroList ( true ) +Aggro:SittingAggroMod ( 35 ) +Aggro:MeleeRangeAggroMod ( 20 ) +Aggro:CurrentTargetAggroMod ( 0 ) +Aggro:CriticallyWoundedAggroMod ( 100 ) +Aggro:SlowAggroMod ( 450 ) +Aggro:IncapacitateAggroMod ( 500 ) +Aggro:MovementImpairAggroMod ( 175 ) +Changed Spells:SpellAggroModifier to Aggro:SpellAggroMod ( 100 ) +Changed Spells:BardSpellAggroMod to Aggro:SongAggroMod ( 33 ) +Changed Spells:PetSpellAggroMod to Aggro:PetSpellAggroMod ( 10 ) + +==01/16/2008 +KLS: Updated AA_Data.sql with various small fixes. +KLS: Changed HasPet() to check for the existance of the pet as mob as well as the petid, should sync up with GetPet() nicely now. + +==01/15/2008 +KLS: LDoN /open will only check for class now, client does not enforce bodytype so neither will the server. +KLS: Numhits in disciplines should work correctly now +KLS: Changed many instances where HasPet() was being checked to verify that we have a valid pet pointer from GetPet(), GetPet() will be used instead, (it is possible to have a valid pet ID and an invalid GetPet() pointer) +KLS: Bind wound will no longer check for existance of skill, everyone gets this skill and it was breaking bind wound for people with 0 skill. +KLS: Fixed an issue that was keeping berserkers and rangers from triple attacking. +KLS: PickPocket should now correctly skill up on it's own through normal use. +KLS: GetMinLevel(int16 spell_id) will now return 0 if the level for the class was 255 instead of 255, many buff formulas do not work well with 255 which can cause some issues when a class can use a clicky item with a spell they cannot scribe normally. +KLS: GetProcID() should no longer have hard coded values.. whatever these represented are no longer valid in the current spell data. + +==01/14/2008 +WildcardX: Replaced the corpse consent system with one that allows cross zone player consents. + +==01/13/2008 +FatherNitwit: (Derision) Added tool (awater) to extract BSP tree with region type info from .s3d files into .wtr files. +FatherNitwit: (Derision) Added support for zone to load .wtr files. +FatherNitwit: (Derision) Employ water file to prevent under water mobs from sinking. +FatherNitwit: (Derision) Employ water file to enforce fishing near water. +New Rules (see ruletypes.h for more info): +Watermap:CheckWaypointsInWaterWhenLoading (Default: false) +Watermap:CheckForWaterAtWaypoints (Default: false) +Watermap:CheckForWaterWhenMoving (Default: false) +Watermap:CheckForWaterOnSendTo (Default: false) +Watermap:CheckForWaterWhenFishing (Default: false) +Watermap:FishingRodLength (Default: 30) +Watermap:FishingLineLength (Default: 40) + +==01/12/2008 +WildcardX: (cavedude) Fixed beastlord pet sizes. + +==01/09/2008 +KLS: Starting items will now be saved if they are placed in slots other than the primary 8, this includes inside bags and on the character's inventory and bank slots +KLS: Kick at level 55 or higher now has a chance to act as a spell interrupt as bash does. +KLS: (TheLieka) Stun Immunity for Frontal Stuns on Ogres +KLS: (TheLieka) Added out of combat regen to NPCs based on rule NPC:OOCRegen +KLS: NPCs with quests using the Perl Quest Parser should no longer stop if they do not have an EVENT_SAY sub +KLS: /autofire cleaned up some, fixed many situations where it shouldn't fire and it now will auto. use throwing weapons as well as bows +KLS: Ranged and Throwing attacks will break invis. correctly. +KLS: Added the following rules: +Character:HealOnLevel (Default: false) +Character:FeignKillsPet (Default: false) +Character:ItemManaRegenCap (Default: 15) +Character:ItemHealthRegenCap (Default: 15) +Combat:UseIntervalAC (Default: false) +Combat:PetAttackMagicLevel (Default: 30) +NPC:SayPauseTimeInSec (Default: 5) +NPC:OOCRegen (Default: 0) + +==12/06/2007 +KLS: Fixed logic on duration 7 formula.. again. + +==12/02/2007 +KLS: Reworked buff duration formula 7 calculations. +KLS: (Cripp)Added (Missing?) pathing z rules. + +==11/30/2007 +FatherNitwit: Reworked pathing z code to be rules based instead of built time options. + +==11/29/2007 +FatherNitwit: (Derision) Fix BestZ pathing cleanup code top stop hopping. +FatherNitwit: (Derision) New fix-z-on-load feature. + +==11/28/2007 +Rogean: Fixed another no-drop trade hack. + +==11/26/2007 +KLS: Dynamic zones will now not attempt to boot up more than one instance of the zone if two or more clients request it in quick succession. +KLS: Fixed: qglobals will now not be exported for npcs that do not have the qglobal flag set in the perl parser. +KLS: Added a new npc special attack 'H' for immune to aggro, mobs with this set should not aggro no matter what a person does to them. +KLS: Fixed ATK loading in item bonuses +KLS: AEDuration calculation will now take target type into account correctly +KLS: Player pets should now persist through a teleport spell +KLS: Players will now be able to target themselves for group spells if they are not in a group +KLS: Fixed some buff duration oddities. +KLS: Level should have somewhat less of an impact for players in the resist code. + +==11/14/2007 +KLS: Swarm pets should be forced to correctly depop after their owner disappears. +KLS: NPCs should be able to use swarm pets correctly now. +KLS: #traindisc should no longer learn disciplines over already known disciplines. +KLS: Added a few more known spell effects to spdat.h, not implemented any yet +KLS: Rez experience should now only goto regular experience, not AA experience. + +==11/07/2007 +KLS: Reworked swarm pets a bit +KLS: Swarm pets should no longer crash zones(I hope) +KLS: Swarm pets will now gain all their information from the normal pet tables and the spell data, as a result the aa_swarmpets table is now obsolete. + +==11/06/2007 +KLS: Reverted client timeouts. +KLS: Fixed NPC HP and Mana Regen not loading from DB for NPCs + +==11/05/2007 +WildcardX: Fixed a bug that prevented player corpse summoning spells from working on Linux platforms. + +==11/04/2007 +WildcardX: The spell "Reanimation" will no longer restore any player experience. This is a 0% experience resurrection. +WildcardX: Corpses moved by a consented player will now remember where it was moved to, even after a zone restart. +WildcardX: Necromancers and Shadowknights can now summon corpses belonging to other players. +WildcardX: Corrected a bug that would have allowed Necromancers and Shadownights to summon corpses belonging to non-grouped party members. + +==11/03/2007 +WildcardX: (Cavedude) Implemented perl wrappers for new quest methods supporting the Shadowrest zone implementation. +WildcardX: Fixed a bug that prevented a player from experiencing resurrection effects in designated no combat zones. +WildcardX: Fixed a bug in graveyard system that could leave a player corpse in the database, but without a location. +WildcardX: Fixed a bug that could prevent the corpse decay timer from expiring because the graveyard timer would reset before. +WildcardX: Implemented support for the Shadowrest zone. This is configured to be disabled by default. +WildcardX: Implemented the command #summonburriedplayercorpse. +WildcardX: Implemented the command #getplayerburriedcorpsecount. +WildcardX: Implemented the quest function bool summonburriedplayercorpse(int32 char_id, float dest_x, float dest_y, float dest_z, float dest_heading). +WildcardX: Implemented the quest function int32 getplayerburriedcorpsecount(int32 char_id). + +Required SQL: +alter table player_corpses add column IsBurried TINYINT(3) NOT NULL default 0; +alter table player_corpses add column WasAtGraveyard TINYINT(3) NOT NULL default 0; +insert into rule_values values(0, 'Zone:EnableShadowrest', 0); + +==11/01/2007 +WildcardX: (TheLieka) Bug fix to discontinue player invisibility when the player loots. +WildcardX: (TheLieka) Bug fix to remove a type from the #npcedit loottable command. +WildcardX: Implemented corpse graveyard support for all zones. Once a zone is configured for a graveyard, a zone restart is necessary. +WildcardX: Zone shutdown and restart will result in all player corpses within the zone to be subject to the graveyard corpse timer. +WildcardX: Implemented the command #setgraveyard [zone name]. This command will create a graveyard for the designated zone based on your target's LOC. +WildcardX: Implemented the command #deletegraveyard [zone name]. This command will remove the graveyard for the designated zone (does not delete any corpses!). + +Required SQL: +alter table zone add column graveyard_id float NOT NULL default '0' after safe_z; + +DROP TABLE IF EXISTS `graveyard`; +CREATE TABLE `graveyard` ( + `id` int(11) NOT NULL auto_increment, + `zone_id` int(4) 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`), + UNIQUE KEY `zone_id` (`zone_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +insert into rule_values values(0, 'Zone:GraveyardTimeMS', 1200000); + +==10/19/2007 +WildcardX: (Bleh) Enhancement to avoid a zone crash by avoiding a null pointer. +WildcardX: (UrbeIT) Bug fix to allow pet spells to effect players in PVP. +WildcardX: (UrbeIT) Bug fix to allow players to remain invisible while in PVP. +WildcardX: (Foin) Bug fix to display the correct pet for Ogre and Iksar beastlords. +WildcardX: (slowglass) Bug fix to properly update LDoN points. +WildcardX: (cbodmer) Bug fix to allow the bank change button to properly work. +WildcardX: (zydria) Bug fix to allow players to meditate while mounted. +WildcardX: (Cavedude) Enhancement to allow berserkers and rangers the chance to triple attack. +WildcardX: (Cavedude) Enhancement to quest::movepc method to accept a heading value. + +==10/17/2007 +KLS: Quick fix to quest::movegrp + +==10/11/2007 +KLS: Familiars should fall under the pet system instead of their own system now, familiars should act in a much more behaved fashion when it comes to functionality shared with normal pets. +KLS: Added a network timeout for clients in zone, clients should linkdead from zone when their connection is suddenly severed. +KLS: (AiliaMorisato) Added some checks for item bonuses, bonuses should not calculate if the item is not equipable. +KLS: NPCs should now accept signals while engaged in combat. + +==10/10/2007 +FatherNitwit: Quick fix for Divine Aura on pets exploit (KingMort) + +==10/09/2007 +KLS: Tweaked my AC system a bit, should get less default defense and the system should be much more lienent overall, +goal is to allow people to see more of a benefit from having AC. + +==09/24/2007 +FatherNitwit: (sfisque) Quick adjustment to item fishing probability. + +==09/07/2007 +KLS: Fixed melee mitigation and tweaks and a small fix to AC. +KLS: Small changes to the order in the getweapondamage code to make it a little more efficient +KLS: Added a field that allows you to override a factions innate desire to assist their own faction in battle to the npc_faction table + +Required SQL: +alter table npc_faction add column ignore_primary_assist tinyint(3) not null default 0; + +==09/02/2007 +KLS: Fixed an issue with items adding stats to NPCs exponentially. +KLS: Added a rule (Boolean) NPC:UseItemBonusesForNonPets, if set to true (default) item bonuses will be applied to all npcs, but only to pets if it is false. +KLS: Lockpicking should work once again +KLS: Should hopefully no longer be possible to use teleport doors when they are in the closed position. +KLS: Added basic AC mitigation, not on by default uncomment #define USE_INT_AC in features.h to use it. + +==08/31/2007 +KLS: (gernblan) Added spawn group info to #npcstats +KLS: (TheLieka) Fix for zone exp modifiers not being used +KLS: (inkubus) Lull and harmony spells should land on enemy targets and not cause any aggro, still some work to be done on the AE portion. +KLS: (cbodmer) Pet aggro changed, owner will no longer gain more aggro from proximity than the pet +KLS: (cbodmer) Support for LDoN style boxes (class 62 + body type 33) implemented +KLS: The error in acmod() should no longer complain as much. +Required: Opcode files have changed, be sure to update to the latest .conf files. + +==08/30/2007 +KLS: Reworked Mob::GetWeaponDamage(), it will now return damage done by any item and NULL and will return a +value zero or less if we can't hit a mob with said item +KLS: Applied new GetWeaponDamage() to Attack and Special Attack code +KLS: Seperated Finishing Blow code to it's own function +KLS: Spell procs and innate procs should now function once again +KLS: NPC Weapons have more of an impact on NPC attack code now +KLS: Some NPC equipped items should correctly add bonuses to NPCs now +KLS: NPCs should no longer be able to dual wield with two-handed weapons +KLS: NPC weapons should properly show up if the item model is particularly high +KLS: Changed NPC_DW_CHANCE in features.h to 100% as default as opposed to the previous 5% +KLS: Archery will be affected by various crit AA effects and spell bonuses +KLS: Applied haste and slow modifiers to basic NPC special attacks such as kick and bash. + +==08/26/2007 +KLS: Fixed a misplaced field in the titanium spawn structure that was causing some spawn display issues on titanium. + +==08/16/2007 +KLS: Some cleaning up on autofire code +KLS: Reintroduction of item animation for archery + +==08/15/2007 +KLS: Fixed an /autofire crash +KLS: Added some conditions to RangedAttack() for now. Should keep from using ranged attacks when we shouldn't be allowed to attack. + +==08/14/2007 +KLS: Added /autofire +KLS: Fixed up SetAttackTimer() calculations a bit, especially when it comes to ranged attacks. Hopefully fewer instances where ranged attacks will be wasted because of the attack timer. +KLS: Ranged attack crits will now work no matter your chance to hit, also changed the appearances a tiny bit and added a min range check for /autofire. +KLS: Removed ReadBook packet size checking for now.. it was checking a wrong size and canceling out legitimate book requests +Make sure to update your .conf files to get /autoattack to work. + +==08/12/2007 +KLS: A little bit more tinkering with how potions and stacks work. + +==08/11/2007 +KLS: Fixed stackable potions in the item packet, they should now appear to have 1 charge instead of 0. +KLS: Added support for stackable items above 20. +KLS: Fixed forage, it will no longer overwrite slot 30 to create it's item, instead it will push the item onto the cursor like it should. + +==08/10/2007 +KLS: Fixed a stacking issue and made res effects not count to normal stacking rules. +KLS: Implemented a few more passive AA effects. +KLS: Added CanThisClassBlock() for the avoid code +KLS: Addressed some inconsistancies in the CanThisClass line of functions, GM classes should be able to do everything their base class can now. + +==08/01/2007 +FatherNitwit: (cavedude) Fix Iksar BL pet appearance. + +==07/30/2007 +Rogean: Fixed an item dupe exploit. + +==07/28/2007 +Rogean: Put in checks for No Drop Trading Hacks + +==07/26/2007 +KLS: Updated AA_Data.sql, still work to be done but should now be more complete than older AA sources. + +==07/22/2007 +FatherNitwit: Fixed another memory leak in world and zone. +KLS: Fixed up some message types so they match the client correctly. + +==07/21/2007 +Rogean: Fixed LDoN Merchant Item Inspect + +==07/20/2007 +FatherNitwit: (Striat) Fix for typos in perlparser.cpp to fix quest::me and quest::echo. +FatherNitwit: (Striat) Fix argument type in quest::setguild. +FatherNitwit: Fixed quest::settime +Rogean: Fixed up strikethrough a little +Rogean: Removed ability to proc on a riposte attack +Rogean: Implimented HoTT (Health of Target's Target) Functionality. This will only work for people with HoTT Built into their interface. + To get UI Files for a Target Window, see http://www.eqemulator.net/forums/showthread.php?p=135291 +FatherNitwit: Fix a few memory leaks. +Changes to opcode files: +patch_Titanium.conf: OP_TargetHoTT=0x6a12 +patch_6.2.conf: OP_TargetHoTT=0x3ef6 + +==07/16/2007 +FatherNitwit: (Glather) Fix for mobs showing weapons +FatherNitwit: (Cbodmer) Notify owner when buffs wear off pets. + +==6/06/2007 +KLS: (Darkonig) Change to the way tradeskill containers are handled. +KLS: (Darkonig) Change to item->isStackable() implementation, should let certain items that the client lets stack also stack server-side. + +==6/01/2007 +KLS: Addressed a memory leak in spell code and a memory leak in NPC destruction code. + +==4/13/2007 +KLS: Fixed an issue with buff duration inc. that was causing buffs to lose their effects (I hope) + +==4/08/2007 +KLS: Added support for the skill Frenzy. +KLS: Various tweaks and changes to some passive AA abilities. +KLS: Added #traindisc to train disciplines on the target player, #scribespells should no longer memorize disciplines to the spellbook. +KLS: Fixed an issue with spells that require components that use item ids higher than 32k +KLS: Fixed an issue with AAs and focus effects that increase buff duration not appearing correctly for clients until they zone. + +==4/02/2007 +KLS: Implemented number6 inspired item cooldown timers. +KLS: Added rules: NPC:MinorNPCCorpseDecayTimeMS & NPC:MajorNPCCorpseDecayTimeMS to set the decay time in millisecond of mobs below 55 and greater or equal to 55 respectively +KLS: Addressed an issue that caused some beneficial spells to generate incorrect aggro amounts. +KLS: Implemented some more passive PoP AA skills. +KLS: Fixed an problem with avoidance AAs adding chance to be hit instead of subtracting it. +KLS: Small change to the way pets and familiars initially aggro, familiars should never aggro now. +KLS: Fixed an issue where the combat code was checking for warrior triple attack but the skill check would always fail. +KLS: Critical hits should now be filterable as critical hits. +KLS: Redid hp and mana calculations based on magelo formulas, should be much more accurate, especially when using AA that increase hp. +KLS: Made mend skill checks more lienent, shouldn't ever fail after 200 skill, shouldn't ever hurt you after 150 +KLS: Initial implementation of hp balance spell effect. +KLS: Focus effects that limit max level should now correctly reduce the effect by a percentage if the spell is over the level cap + +==3/26/2007 +FatherNitwit: Optimized the merchantlist_temp query on boot. + +==3/14/2007 +KLS: Put in FNW's requested changes to tradeskill combines from the other day that I almost forgot about +KLS: Tradeskill combines should check for container type now. + +==3/11/2007 +KLS: Attack() will not set our target unless we have no target now. +KLS: Tweaked some invis and hide stuff to hopefully catch more situations where it should break correctly. +KLS: Fixed the spell critical hit ratio to be accurate. +KLS: Spell Casting Deftness and Quick Buff should now work with spells that have duration formulas. +KLS: NPCs will no longer be restricted by the number of targets on their AE spells. +KLS: Spells base_1 will load as a 32 bit signed int instead of a 16 bit signed int, should correct some oddities with certain spells. For example certain summon spells using large item ids. +KLS: Fixed a tradeskill exploit with tradeskill containers and experimentation. + +==3/10/2007 +Rogean: Fixed #si and minstatus + +==3/3/2007 +FatherNitwit: Finally rewrote 'make depend' to properly track dependencies in linux builds + +==2/22/2007 +KLS: Some more changes to AA system: +KLS: altadv_vars.cost_inc will now be unsigned to allow your skills to cost less per level +KLS: Client side aa effects are now loaded from the database once again +KLS: Updated AA_data.sql to include AAs up to planes of power. +KLS: Added #refundaa as a way for server GMs to refund a players alt ability points and give them back their points. +KLS: Implemented limited numbered hits on buffs. +KLS: Root and charm will now act as partial capable spells. +KLS: Vampires will act as undead, which is consistant with what the client thinks is undead. + +Required SQL: +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) unsigned NOT NULL default '0', + base2 mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (id), + UNIQUE KEY NewIndex (aaid,slot) +) TYPE=MyISAM; + +Optional SQL: +Resource AA_Data.sql + +==2/18/2007 +WildcardX: Player casted area of effect detrimental spells will not longer have an affect on players not engaged in PvP. +WildcardX: Fixed spawnlimit setting to control the maximum times a given npc_type can spawn in the same zone. +WildcardX: (Bleh) Fixed player emote animations. +WildcardX: Implemented a server-side check of a Client object's target to avoid it being null. +WildcardX: Fixed a zone crash that could occurr during a monk special attack or a rogue's backstab attack. +WildcardX: Area of effect beneficial spells will no longer affect non-player characters. + +==2/16/2007 +KLS: Updating Changelog AA work to be more clear as well as the required SQL. + +Required SQL: +alter table altadv_vars add column cost_inc tinyint(4) not null default 0; + +Optionally: +Source in AA_data.sql + +==2/15/2007 +WildcardX: Commented out the code supporting the deprecated OP_CloseContainer opcode. +WildcardX: Fixed world containers so when one player has closed a world container, another player can open it without the need for the first player to leave the zone. +KLS: Initial release of ongoing AA work +KLS: Most(Not all) Alternate Advancement abilities up to SoL Class working correctly with correct costs and client side effects +KLS: Fixed many many AA related bugs +KLS: Fixed Activatable AA buttons on titanium +KLS: Added #altactivate command to facilitate the new AA work +KLS: Added Character:ConsumptionMultiplier rule. +KLS: Server wont send a eat/drink message unless the player forces the client to eat/drink +KLS: Fixed an issue with TGB working in a backwards manner. +KLS: Fixed an issue with AE bard spells hitting the bard instead of the targets around him. +KLS: Fixed an issue with cancel magic spells that made them act as completely beneficial spells. +KLS: Added many missing spell effects into spdat.h +KLS: Fixed a beneficial aggro issue that was resulting in certain clicky items giving significant amounts of aggro. +KLS: MOBs with pets or familiars already out will not attempt to cast pet spells. +KLS: Curse, Poison, and Disease counters will go past 256 and all be active but will reset on zoning +KLS: Curse counters will work now. +KLS: Intimidation should work under the new skill cap rules. +KLS: Killing something will no longer clear your server side target unless you killed your target. +KLS: Fixed a int to float conversion issue in CalcRecommendedLevelBonus() (as per slowglass' post) +KLS: Fixed a crash issue related to resists, should help those crashing after zoning with a spell that has a heartbeat resist such as root. +KLS: Removed an unused spell function that didn't make much sense. + +==2/14/2007 +WildcardX: Implemented a cap of 4 mobs affected by targeted area of effect spells. This is consistent with EQ live. +WildcardX: Implemented the succor line of spells. +WildcardX: Specific mobs can now be toggled as findable or not findable by the track skill. Mobs which are not findable will not appear on any track lists. + +Required SQL: +alter table npc_types add column trackable tinyint(4) not null default 1; + +==2/13/2007 +WildcardX: Fixed bug that broke #zone command. + +==2/11/2007 +WildcardX: Further enhancements to zone weather system to reduce the amount of work necessary to perform weather and to make it more logical. +WildcardX: Added a new opcode for both 6.2 and Titanium clients, OP_ZonePlayerToBind=0x385e. Please be sure to update your .conf files. +WildcardX: Implemented the beginnings of what will become a new "system" to deal with zoning. +WildcardX: Titanium clients will now zone correctly upon death and will not go link dead and get sent to character select screen. +WildcardX: Both 6.2 and Titanium clients will now return character view to first person from corpse hover after completing zone to bind point after death. +WildcardX: Group member camping will now auto-disband upon an actual logout. +WildcardX: Disbanding a link dead group member will no longer crash the zone. +WildcardX: Link dead group members will now automatically remove themselves from group. +WildcardX: Many zoning situations will now use "heading". This was largely being ignored before. However, there are still a couple more situations where this needs to be implemented. I will continue implementing "heading" as i continue to work on the zoning code. +WildcardX: (Cavedude) Implemented yet another container type to perform even more quest combines. +WildcardX: Added additional logging to zone "debug" and "error" to support zoning and intra-zone movement. +WildcardX: Group members disbanding in another zone will still fail to appear as removed from group from other group members, but disbanding this group member when he/she rejoins group in zone will no longer crash the zone and you will be able to re-add this former group member without having to destroy the whole group. + +==2/10/2007 +FatherNitwit: Reworked NPC Idle spell casting to avoid extra LOS checks (based on KLS's observations) +FatherNitwit: Reworked NPC spell casting timers to be more consistent. + +==2/9/2007 +Doodman: Fixed AdventurePoints_Update_Struct to be the correct struct. Same structure for both 6.2 and Ti. Changed core structure. + +==2/1/2007 +WildcardX: (Bleh) Fixed quest globals. +WildcardX: Fixed the common error message MakeNameUnique() has been recording in logs about being unable to make a unique name for mobs greater than 100 in a zone. + +Required SQL: +ALTER TABLE quest_globals CHANGE expdate expdate INT; + +==1/29/2007 +WildcardX: Cleaned up zone weather code for better efficiency. Weather checks will be more frequent when their is snow or rain and less frequent when the sun is out. +WildcardX: Made weather log messages more consistent and informative. +WildcardX: Druids are now replace bards has the second best tracker in the game. This is consistent with live. +WildcardX: Fixed tracking skill. Tracking will now return a full list of every mob within the tracker's skill range, not just the first 100 mobs. +WildcardX: Clients will now see their coins update from quests or player trades. No zoning is required anymore. +WildcardX: The server is now using the OP_MoneyUpdate opcode, instead of OP_MoneyOnCorpse opcode for coin updates. +WildcardX: Clients entering a zone will now receive a weather packet. This will ensure all clients in the same zone will experience the same weather. + +==1/23/2007 +KLS: Fixed the change log so the current entries are correct +KLS: Fixed a small but significant crash. + +==1/15/2007 +KLS: Fixed an issue with storing timers. +KLS: Added in hide and basic support of hide improving AAs later +KLS: Some more changes to how procs work, perma procs will not be influenced by mob stats, should make them easier to balance. +KLS: Some internal changes to critical hits with some future spell effects in mind shouldn't change functionality. +KLS: WildCardX's key changes, keys should now work even if they're not on your cursor and no matter where in your inventory it's on you, including your bank +KLS: Pickpocket should work considerably better. +KLS: Removed some obsolete discipline code that I ran across. +KLS: Character:ExpMultiplier rule should work correctly now. +KLS: Fixed an issue with the client position update code that should see the server sending fewer redundant client position update packets. +Required SQL: +ALTER TABLE `npc_types` ADD `see_hide` tinyint(4) NOT NULL default '0'; +ALTER TABLE `npc_types` ADD `see_improved_hide` tinyint(4) NOT NULL default '0'; +Optional SQL: +UPDATE `npc_types` SET `see_hide` = 1 WHERE see_invis = '1'; +UPDATE `npc_types` SET `see_hide` = 1 WHERE see_invis_undead = '1'; + +==1/07/2007 +KLS: action_struct unknown06 renamed to instrument_mod to reflect it's behavior +KLS: WildCardX inspired weapon proc rate changes, proc rates should now load as a signed int instead of an unsigned int and should correctly calculate. +KLS: Some changes to the expired function of ptimers, it should return if the timer is expired whether or not the timer is enabled. This avoids certain situations where timers are disabled and then run down without ever being reenabled and essentially being locked down forever unless the database admin resets it manually. +KLS: Also fixed a small ptimer expire bug where if the timer just reached zero it would return expired until it was one second past zero. +KLS: Evade will no longer set the rogue's hate to 0, but instead will lower the hate by some amount based on level and the current hate of the monster. May need tweaking as don't know exact values. +KLS: Combat Range has been redone a bit so it no longer mixes ints and floats and also correctly factors in the size of the enemy you're fighting. Should prevent exploits where you can hit something rooted/permarooted but they cannot hit you back. +KLS: Rampage and enrage have been reworked and should work correctly, rampage will now carry a rampage list of 20 by default that does not get wiped until the monster stops fighting. +KLS: Fixed up spell fields and spell loading to make more sense, identified some fields. It should load in order now. +KLS: Sneak should now break when you deal / are dealt damage. +KLS: Hide should succeed a bit more and use the correct messages: "You have hidden your self from view", "You have failed to hide your self from view" +KLS: CalcAC() and CalcATK() now use signed values instead of unsigned. +KLS: When a mob returns to their guard point they should now correctly assume their guard heading. +KLS: Succor should fully wipe you from hate lists now, as should zoning. +KLS: Spin effects are now correctly capped at a max of level 55, all targets above this cannot be affected. +KLS: SE_LimitEffect should now correctly deal with any effect type the spell has and not just the ones hard coded into the server. +KLS: (Cripp)Fix for zone heading + +==12/24/2006 +KLS: (WildcardX) Fix for generic tradeskill container combines +KLS: (WildcardX) Fix for quest::depop and multipul mobs in the same zone that share a NPCType ID +KLS: (WildcardX) Zones can now be flagged to disallow offensive actions. +KLS: (WildcardX) Fix for zone weather, should no longer rain in places with no set weather, such as dungeons. +KLS: Interrupts should only be caused by melee, not spell damage now +KLS: Roots should only break on spell damage now. +KLS: DoT Focus effects should work correctly +KLS: Spell Formula 122 (Splurt) should now function correctly. +KLS: Berserkers should find themselves with the ability to double attack. +KLS: Curse, poison and disease counters should no longer be factored into stacking, should keep certain dots and certain slows from overwriting and blocking each other. +KLS: Complete Heal buffs will no longer stack with or overwrite other complete heal buffs. +Required SQL: +ALTER TABLE `zone` ADD `cancombat` tinyint(4) NOT NULL default '1'; +UPDATE `zone` SET `cancombat` = 0 WHERE short_name = 'nexus'; +UPDATE `zone` SET `cancombat` = 0 WHERE short_name = 'poknowledge'; +UPDATE `zone` SET `cancombat` = 0 WHERE short_name = 'potranquility'; + +==12/19/2006 +KLS: Fixed up Illusions a bit +KLS: Fixed a bug where the base2[0] field of a spell was not getting loaded correctly +KLS: Implemented Sacrifice Spells +KLS: Implemented Call of the Hero Spells +KLS: Implemented Silence spells +KLS: Implemented Invis Vs Animal Spells +KLS: Implemented Illusion Copy Spells +KLS: Implemented Dispel Detrimental Spells +KLS: Dispel Magic spells will skip spells with poison or disease counters. +KLS: Spells that imbue items(summon items with reagents, sacrifice) will not be affected by reagen conservation focus. +KLS: If a player is at max level the experience he has will cap at the max experience to gain the next level instead of being able to gain experience past his visible experience bar. +KLS: The max level you can group with and gain experience is now YourLevel*1.5 instead of YourLevel+8. +KLS: You'll only get an experience loss message if you actually lose exp now, no more You lost experience messages when you take a 0% rez. + +==12/15/2006 +FatherNitwit: Fix for possible crash when NPCs are immune to damage. +KLS: Small fix for newly created player corpses, hopefully they will no longer act like they are npc corpses. +KLS: Changed how recourse works a bit, it is now done after the resist check. +KLS: Group spells should be able to land on the caster's pet now. +KLS: Fixed crazy Bash/Kick damage. + +==12/09/2006 +KLS: Stun immune should apply to spin effects such as those found in One Hundred Blows. +KLS: Some cleanups in special attack and resist code. +KLS: Added a define SKILL_MAX_LEVEL in features.h that lets you define how high level you can get skills from the database, defaults to 75. +KLS: Damage shields wont proc on yourself any longer to avoid some wierdness. They only ever procced on yourself under special conditions so it shouldn't be noticable. + +==12/03/2006 +FatherNitwit: Fixed a few issues related to quest global expiration. + +==12/01/2006 +KLS: NPC ghosting fix with rule support. +FatherNitwit: Fixing up spell set loading crash. +KLS: More skill fixes +KLS: Moved the old class_skills system over to the new skills system. + +==11/28/2006 +FatherNitwit: (bleh) Added #hatelist command. +FatherNitwit: (bleh) Work on windows build issues. +FatherNitwit: Optimized merchant list query during zone boot. +FatherNitwit: Fix a few race related issues in eqbuilder. + +==11/26/2006 +FatherNitwit: Fixed berserker skill cap issue that KLS found. + +==11/25/2006 +KLS: The way resists are calculate have been changed and are now somewhat tweakable with rules, and partial hits should work correctly. +KLS: Ripostes have been seperated from the attack code and will now apply to all attacks. +KLS: Skills and Spells that directly modify melee damage should now work and be applied to all attacks. +KLS: Some fixes and optimizations in the attack code. +KLS: Familiars will no longer aggro like regular pets. +KLS: Damage Shield Bonuses on items should no longer be backwards. +KLS: Heals will report the amount of damage healed to their targets and casters. +KLS: Pets will now spawn at %t`s pet instead of %t's pet, the client filters out ' in certain things like combat and considering. +KLS: Stun Immunity will now only apply to the stun portion of the spell instead of the whole spell. +KLS: Instrument Mods should send correctly on the reapplication of bard pulses. +KLS: Spells should no longer check instant heals/damage portions of the spell for stacking purposes, several spells that should have stacked before but didn't now should properly. + +=11/24/2006 +FatherNitwit: (number6) Fix for loading saved spell sets. +FatherNitwit: Added #giveitem as inspired from the forums. +FatherNitwit: Changed default status required for #summonitem to 200. + +==11/20/2006 +KLS: Skill system tweaks and bug fixes. +KLS: Bard songs will now skill up while they are being sung instead of just when first cast. + +=11/19/2006 +FatherNitwit: Completely redid skills in the code to support >252 +FatherNitwit: Skill caps are now database driven (shared mem) +Create this table, and source in SkillCaps.sql +CREATE TABLE skill_caps ( + skillID TINYINT UNSIGNED NOT NULL, + class TINYINT UNSIGNED NOT NULL, + level TINYINT UNSIGNED NOT NULL, + cap MEDIUMINT UNSIGNED NOT NULL, + PRIMARY KEY(skillID,class,level) +); + +==11/07/2006 +KLS: Changed how critical hits work, as a result things that could not crit before like special attacks, now can. +KLS: Cleave like effects should be fixed to do an increase of your already standing chance to critical instead of a flat increase. +KLS: Reworked much of the special attack code. +KLS: Applied haste to combat skill timers, they should give trouble much less often. +KLS: Monk skill damage will be more random but the max damage they can do has not changed. +KLS: Skills should be avoided less often, they were being avoided twice in the code instead of once. +KLS: Implemented basic strikethrough. + +==11/06/2006 +FatherNitwit: (Cripp) periodic auto save. +FatherNitwit: LostZaphod's possible win32 azone fix. (untested) +FatherNitwit: (cbodmer) Regen multiplier rules. + +==11/04/2006 +Rogean: Haste Fixes: Fixed Haste/Slow as well as Bard Songs (Including Overcap). + +==11/03/2006 +KLS: Added Magoth's stun opcodes for both Titanium and 0.6.2 clients +KLS: Changed Object Clear opcode for Titanium + +==10/30/2006 +FatherNitwit: (KLS) Fixes for max endurance calc on zone in +FatherNitwit: (KLS) Fix for rulesystem category issue. +FatherNitwit: Added rule support to world (no runtime manipulation yet) +FatherNitwit: Load the 'default' ruleset from the DB if no variable is set. +KLS: Fix for client corpses not appearing to clients who witnessed their death until they zone. +KLS: Updated detrimental and healing spell aggro. +KLS: Hate mods from spells should now work. (Enchanter Visage/Shadowknight Voice line of spells) +KLS: Found and implemented the Hate Added in detrimental spells. (Shadowknight Terror line of spells) +KLS: Change to let special messages use their correct types. +KLS: Group fix for leader being unable to invite a 3rd party member until he/she zones. +KLS: GroupInvite2 opcodes found for both Titanium and 0.6.2, make sure to get the updated .conf files. +KLS: Glow messages should only go off for instant spells. +KLS: How haste calculations have been calculated has been reworked somewhat, haste shouldn't break any more, nor should all hastes stack. + +==10/28/2006 +FatherNitwit: Fix for NPC weapon appearance due to loot items. +aza77: Fixed SendAppearancePacket +aza77: Fixed a PvP bug that caused the zone to crash on death + +==10/26/2006 +FatherNitwit: WildcardX's name cleanup. +FatherNitwit: Tweak for 64 bit pointer support in the item code of the struct strategy. +FatherNitwit: More const cleanup in npc.h + +==10/22/2006 +FatherNitwit: added EVENT_COMBAT triggered when an NPC engages any form of +FatherNitwit: combat and when combat is over. $combat_state signals which. +FatherNitwit: Hopefully fixed d_meele_texture +FatherNitwit: Cleaned up a lot of const related issues (attrs, AAs, and more) +FatherNitwit: Moved a lot of NPC specific stuff from Mob into NPC +FatherNitwit: Reworked special attacks a bit (monk, kick, backstab): +FatherNitwit: they now use the standard combat hit chance forumals based on offense/defense instead of always hitting +FatherNitwit: they are now subject to damage avoidance (parry, riposte, etc.) like normal attacks + +==10/21/2006 +FatherNitwit: Hopefully fixed spawn timer variance. + +==10/18/2006 +FatherNitwit: Maybe fixed issue with losing items when zoning in mysql 5. +FatherNitwit: Fixed issue with color saving properly. + +==10/15/2006 +FatherNitwit: (KLS) Implemented Endurance +FatherNitwit: (KLS) Implemented support for seperate discipline timers +FatherNitwit: (KLS) Fix for haste & slow issue on NPCs +FatherNitwit: (KLS) Fixed faction not changing issue (caused by min/max sticking fix) +FatherNitwit: (KLS) Changed con level code to be more accurate +FatherNitwit: Made /pet attack range a rule (Pets:AttackCommandRange) default to 150. +FatherNitwit: (KLS) Changed how consider and consider corpse is handled a bit, consider corpse should be more informative +FatherNitwit: (KLS) Fixed bug with charged/stacked items on corpses. +FatherNitwit: (KLS) Support for leaving naked corpses +FatherNitwit: Added rules: Character:DeathExpLossLevel, Character:CorpseDecayTimeMS +FatherNitwit: Character:LeaveNakedCorpses +FatherNitwit: Replaced db variable 'leavecorpses' with rule Character:LeaveCorpses +FatherNitwit: (KLS) hopefully fixing monk kill crash. +FatherNitwit: (KLS) made it so you can snare things while they're rooted +FatherNitwit: (KLS) made it so beneficial songs will stack with beneficial spells +FatherNitwit: (KLS) made it so a beneficial effect and a detrimental of an effect don't conflict +FatherNitwit: (KLS) let dots of the same resist type stack so long as they aren't the same spell +FatherNitwit: cleanup windows logging a bit +FatherNitwit: (eq4me) GCC 4+ build fixes + +==10/05/2006 +FatherNitwit: (KLS) Fix for explicit spell stacking rule problem. + +==10/02/2006 +FatherNitwit: (InsaneWallaby) Tradeskill skill gain fix. +FatherNitwit: (WildcardX) Improved spawnstatus command. +FatherNitwit: (number6) Work around for lay hands/harm touch recovery time hang. +FatherNitwit: Possible fix for faction sticking at min/max. + +==09/21/2006 +FatherNitwit: Revert part of bard mod fix, and fix it right. + +==09/20/2006 +FatherNitwit: Prevent stats from going negative. +FatherNitwit: Try to prevent skills from wrapping above 252 (still no support for higher) +FatherNitwit: Minor shop close tweak. +FatherNitwit: (Zuesrooster) Potential bard modifier fixes. + +=09/07/2006 +FatherNitwit: (eq4me) Reworked tradeskill success and gain rates +FatherNitwit: Potentially addressed trade coin display issue. +FatherNitwit: Undid clickie spell component change until dicussion finishes + +=09/07/2006 +FatherNitwit: (Aramid) Fixed ability to bind. +FatherNitwit: (Somebody, forget who) Fixed issue with reagents being consumed by clickies. +FatherNitwit: (eq4me) Fixed tradeskill stat bonus calculation for success rate. +FatherNitwit: Maybe fixed up zone solution file to only build perl versions, for debug and release. + +==08/20/2006 +FatherNitwit: Zero out buff counters when we occupy a new slot. + +==08/16/2006 +FatherNitwit: Glorandwarf's updated AA.h + +==08/14/2006 +FatherNitwit: More work on titanium AAs by Glorandwarf. +FatherNitwit: (Zuesrooster) Fix for pet self buffing. + +==08/10/2006 +FatherNitwit: Changed logic in quest parser to potentially avoid windows crash (Zuesrooster) +FatherNitwit: (Zuesrooster) fixed aggro quest related crash. +FatherNitwit: Untested update of OP_AugmentItem (John Adams) +FatherNitwit: A little bit further on AAs (Glorandwarf) + +==07/27/2006 +FatherNitwit: Added door info dump on click (DOORS__INFO) + +==07/23/2006 +FatherNitwit: Enforce some zone requirements on zone in too. +FatherNitwit: Fixed minor tradeskill crash. + +==07/23/2006 +FatherNitwit: (EverHood) Archery/self PvP mitigation changes +FatherNitwit: (EverHood) Regen tweaks +FatherNitwit: (EverHood) Fix DoT damage and messages +FatherNitwit: Killed erronious double-effects on regen/HoT buffs +FatherNitwit: Fixed replace-container client side appearance issue. + +==07/17/2006 +FatherNitwit: Added new rules subsystem to allow game rules to be changed + at runtime. more about this will come as time goes on. +FatherNitwit: Added #rules command to manage rules data from in game. +FatherNitwit: Renamed old #rules to #serverrules +FatherNitwit: Moved max level into the rules system (Character:MaxLevel) +FatherNitwit: Added GM-trained tradeskill caps to rule system (Skills:MaxTrainTradeskills) +FatherNitwit: Fixed intimidation skill cap +FatherNitwit: Fixed adventure opcodes for adventure merchants on Ti. +FatherNitwit: Fix interrupt cast opcode for Ti. +FatherNitwit: Tweak to missing-spell-creagent case, might fix it? +Requred SQL: +CREATE TABLE rule_sets ( + ruleset_id TINYINT UNSIGNED NOT NULL auto_increment, + name VARCHAR(255) NOT NULL, + PRIMARY KEY(ruleset_id) +); +INSERT INTO rule_sets VALUES(0, "default"); +UPDATE rule_sets SET ruleset_id=0; +CREATE TABLE rule_values ( + ruleset_id TINYINT UNSIGNED NOT NULL, + rule_name VARCHAR(64) NOT NULL, + rule_value VARCHAR(10) NOT NULL, + INDEX(ruleset_id), + PRIMARY KEY(ruleset_id,rule_name) +); + +==07/16/2006 +FatherNitwit: (EverHood) Implemented Eye of Zomm and Bind Sight +FatherNitwit: Reworked Eye of Zomm code + +==07/16/2006 +aza77: Fixed the lift issue + keyitems at doors +aza77: Fixed + modified traps and added a new type of trap +ALTER TABLE `traps` ADD `message` VARCHAR( 200 ) NOT NULL AFTER `effectvalue2` ; + +==07/13/2006 +FatherNitwit: Fixed zone crash when removing a guild member. +aza77: Added in game guild creation variables.GuildCreation + commands #guildcreate #guildapprove #guildlist +FatherNitwit: Fixed feign aggro crash. + +==07/12/2006 +aza77: Added triggered only doors. + +==07/10/2006 +FatherNitwit: Adding EverHood's lull fixes. + +==07/09/2006 +FatherNitwit: Wizardanim has dicovered the opcode to partiall fix AAs for Ti!! +FatherNitwit: EverHood's Mob ghost running on (feign)death fix. +FatherNitwit: Redid walkspeed code to be caclualted from runspeed (EverHood inspired) +FatherNitwit: Fixed up run AA speed mods. +FatherNitwit: Added EVENT_NPC_SLAY when NPCs kill another NPC. (fanman55 inspired) +FatherNitwit: Fixed up a long running memory issue in quest::ChooseRandom +ALTER TABLE `npc_types` DROP walkspeed; + +==07/05/2006 +aza77: Added variables.Rules + commands #rules #acceptrules +ALTER TABLE `account` ADD `rulesflag` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '0'; + +==07/04/2006 +FatherNitwit: Maybe fixing feign memory. +FatherNitwit: EverHood's temp pets and group recourse fixes. +FatherNitwit: EverHood's NoLongerEngaged AI fix. +FatherNitwit: bufgix in log.ini reading. + +==06/29/2006 +aza77: Get the SQL right: +ALTER TABLE `npc_types` CHANGE `hp_regen_rate` `hp_regen_rate` INT( 11 ) NOT +NULL DEFAULT '0'; +ALTER TABLE `npc_types` CHANGE `mana_regen_rate` `mana_regen_rate` INT( 11 ) NOT +NULL DEFAULT '0'; + +==06/28/2006 +FatherNitwit: Added EverHood's feign memory fixes +FatherNitwit: Reverted regen of 0 to mean "auto config", and negative to mean "no regen" +aza77: Changed hp_regen + mana_regen such that negative values indicate no regen +ALTER TABLE `npc_types` CHANGE `hp_regen_rate` `hp_regen_rate` INT( 11 ) NOT +NULL DEFAULT '0'; +ALTER TABLE `npc_types` CHANGE `mana_regen_rate` `mana_regen_rate` INT( 11 ) NOT +NULL DEFAULT '0'; +UPDATE `npc_types` SET `hp_regen_rate` = '0' WHERE `hp_regen_rate` = '-1'; +UPDATE `npc_types` SET `mana_regen_rate` = '0' WHERE `mana_regen_rate` = '-1'; + +==06/27/2006 +aza77: Added quest::setnextinchpevent + $inchpevent +aza77: Added quest::sethp + +==06/25/2006 +aza77: Fixed PVP appearance issues +FatherNitwit: Fixed the #logsql command + +==06/20/2006 +FatherNitwit: tweaked some ability timers and fixed up ranger crit damage + +==06/18/2006 +FatherNitwit: necro DOT feign death fix (unicorn97211) +FatherNitwit: Disable kill-pet-on-feigndeath by default (features.h FEIGN_KILLS_PET) +MySQL 5 compatibility increases (WildcardX): +alter table account DROP packencrypt; +alter table character_ change extprofile extprofile blob NULL; +alter table player_corpses change data data blob NULL; +alter table player_corpses_backup change data data blob NULL; + + +==06/15/2006 +aza: Added MySQL5 support (FOR CUSTOM COMPILE ONLY) +ALTER TABLE spawn2 CHANGE `condition` `_condition` MEDIUMINT(8) UNSIGNED NOT +NULL DEFAULT '0'; + +==06/07/2006 +FatherNitwit: Fixed SetAATitle (#title), and experted it to perl. +FatherNitwit: Fixed quest money reward printing. (WildcardX) + +==06/03/2006 +FatherNitwit: Fixed a client-side inventory appearance issue, may help with trade bugs. + +==05/31/2006 +FatherNitwit: (aza) Added quest::unscribespells +FatherNitwit: fixed ranger critical hit chance. +FatherNitwit: Implemented conditions on windows. +FatherNitwit: maybe made NPCs stop buffing pets. +FatherNitwit: Fixed npc spawn limits. +FatherNitwit: Fixed feign death missing opcode. +FatherNitwit: Hopefully fixed an issue causing server to slow down terribly after being up for a day+. +FatherNitwit: Fixed crash on zone/world shutdown. +ALTER TABLE spawnentry DROP spawn_limit; +ALTER TABLE spawngroup ADD spawn_limit tinyint(4) NOT NULL default '0'; +ALTER TABLE altadv_vars ADD class_type int unsigned NOT NULL DEFAULT 0; + +==05/06/2006 +FatherNitwit: (aza) Enabled newer froglok classes. +FatherNitwit: (aza) Enabled several berserker combat skills. +FatherNitwit: Fixed possibly infinite recursion in map code. +FatherNitwit: Holding EQStreams open until they send all their data. +FatherNitwit: Trying to fix death disconnect (and failing) +FatherNitwit: Maybe fixed tinkering searching. + +==04/24/2006 +FatherNitwit: Added new HTTP functionality for moving chars between accounts. + +==04/21/2006 +FatherNitwit: Fixed bane damage loading problem. + +==04/20/2006 +FatherNitwit: Hopefully fixed the unidentified stream crash and some memory leaks. +FatherNitwit: Fixed weapon affinity huge proc bonuses +FatherNitwit: Fixed spell-granted procs not getting removed. +FatherNitwit: Implemented the 'procrate' field on items. + +==04/13/2006 +FatherNitwit: Fixed the issue of charmed mobs getting assists from their old friends. +FatherNitwit: Changed version number to 0.7.0 + +==04/13/2006 +FatherNitwit: fixed a crash related to client destructors and tradeskills +FatherNitwit: Adding the incstats canges from aza on the forums. + +==04/01/2006 +FatherNitwit: FINALLY found and fixed the netcod crash I introduced a while back. +FatherNitwit: Fixed bard group buff tics. +FatherNitwit: Fixed the global guild chat going to everybody problem. + +==03/27/2006 +Doodman: Added queuing for future packets, fixing lag reported by Richardo + +==03/25/2006 +FatherNitwit: Fixed character deletion to remove guild member entries. (Aeris1) + +==03/24/2006 +Doodman: Added live struct strategy. Can get to char select + +==03/22/2006 +Doodman: Fixed Ti items +Doodman: Added some field name mappings to load_13thfloor_items script. +UPDATE items SET stackable=1 WHERE itemtype IN (14, 15, 17, 18, 19, 27, 37, 38, 55, 56); + +==03/22/2006 +FatherNitwit: Refactored the netcode to move the opcode manager back into the stream. + +==03/21/2006 +Doodman: Merge up from Source branch +Doodman: Fixed CreateItem + +==03/20/2006 +FatherNitwit: Fixed invisibility vs. undead to only apply to undead mobs. +FatherNitwit: Added `slot` column to starting items to support much more robust configurations. +FatherNitwit: Updated char backups. (noted by typhoon) +Required SQL: +ALTER TABLE starting_items ADD slot MEDIUMINT NOT NULL DEFAULT -1; +ALTER TABLE character_backup ADD class TINYINT NOT NULL DEFAULT 0; +ALTER TABLE character_backup ADD level MEDIUMINT UNSIGNED NOT NULL DEFAULT 0; +ALTER TABLE character_backup DROP guild; +ALTER TABLE character_backup DROP guildrank; +ALTER TABLE character_backup DROP publicnote; + +==03/19/2006 +FatherNitwit: A bunch of minor changes, should help some bugs in 0.6.6. +FatherNitwit: Wrote basic guild management pages, much more needed. + +==03/18/2006 +FatherNitwit: Queue fix in netcode, should help crashing problem. +Doodman: Redid world port allocation not start looking for a port at the begining each time +Doodman: Made zone send it's port to world on connect, in case it is reconnecting to a restarted world +FatherNitwit: Changed version number to 0.6.6 +FatherNitwit: Un-broke some outbound TCP connection stuff I broke to allow reconnecting. +FatherNitwit: Completely rewrote guilds in both world and zone. +FatherNitwit: Removed guilds from shared memory. +FatherNitwit: Removed the seperation between EQ ID and DB ID. +FatherNitwit: Fixed countless rediculous things about guilds. +FatherNitwit: Any change in guild state should update in real time now. +FatherNitwit: Guild leadership may need re-establishing since it changed from account to char based. +FatherNitwit: Guild structure in database has changed significantly. +Note: custom rank stuff is too much work for me to preserve in the DB, since I doubt + anybody uses it, so if you have them, you can figure it out yourself or remake them. +Required SQL: +ALTER TABLE character_ ADD class TINYINT NOT NULL DEFAULT 0; +ALTER TABLE character_ ADD level MEDIUMINT UNSIGNED NOT NULL DEFAULT 0; + +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, guild2) +); + +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) +); + +ALTER TABLE guilds DROP eqid; +ALTER TABLE guilds DROP rank0title; +ALTER TABLE guilds DROP rank1title; +ALTER TABLE guilds DROP rank1; +ALTER TABLE guilds DROP rank2title; +ALTER TABLE guilds DROP rank2; +ALTER TABLE guilds DROP rank3title; +ALTER TABLE guilds DROP rank3; +ALTER TABLE guilds DROP rank4title; +ALTER TABLE guilds DROP rank4; +ALTER TABLE guilds DROP rank5title; +ALTER TABLE guilds DROP rank5; +ALTER TABLE guilds ADD tribute INT UNSIGNED NOT NULL; +ALTER TABLE guilds ADD motd_setter varchar(64) NOT NULL DEFAULT ''; + +INSERT INTO guild_members (char_id,guild_id,rank,public_note) +SELECT id,guild,guildrank,publicnote FROM character_ +WHERE guild > 0 AND guild < 1500; + +ALTER TABLE character_ DROP guild; +ALTER TABLE character_ DROP guildrank; +ALTER TABLE character_ DROP publicnote; + + +==03/11/2006 +FatherNitwit: Redid netcode outbound queue to properly handle sequence wrapping. + +==03/10/2006 +Doodman: Worked up serialization for Ti and Live (/snicker) +Doodman: Converted the item table to match live (13th-floor) changes below (apply both) +Doodman: Worked items to be compatible for both (kept loreflag and loregroup) +6.2 -> Ti: +ALTER TABLE items CHANGE loreflag loregroup int not null; +ALTER TABLE items CHANGE UNK117 UNK123 int not null; +ALTER TABLE items CHANGE UNK150 UNK157 int not null; +ALTER TABLE items CHANGE UNK145 UNK152 int not null; +ALTER TABLE items CHANGE UNK140 UNK147 int not null; +ALTER TABLE items CHANGE UNK135 UNK142 int not null; +ALTER TABLE items CHANGE UNK130 UNK137 int not null; +ALTER TABLE items CHANGE UNK124 UNK130 int not null; +ALTER TABLE items CHANGE UNK121 UNK127 int not null; +ALTER TABLE items CHANGE UNK118 UNK124 int not null; + +ALTER TABLE items ADD augslot1unk int not null AFTER augslot1type; +ALTER TABLE items ADD augslot2unk int not null AFTER augslot2type; +ALTER TABLE items ADD augslot3unk int not null AFTER augslot3type; +ALTER TABLE items ADD augslot4unk int not null AFTER augslot4type; +ALTER TABLE items ADD augslot5unk int not null AFTER augslot5type; +ALTER TABLE items ADD enduranceregen int not null AFTER manaregen; +ALTER TABLE items ADD notransfer int not null AFTER stacksize; +ALTER TABLE items ADD UNK133 int not null AFTER notransfer; + +Ti -> Live: +ALTER TABLE items ADD UNK134 varchar(255) not null AFTER UNK133; + +ALTER TABLE items CHANGE UNK130 potionbeltslots int not null; +ALTER TABLE items CHANGE UNK133 stackable int not null; + +==03/08/2006 +FatherNitwit: (BatCountry) Fixed throwing item crash. +FatherNitwit: (Hvitrev) Implemented Primsatic and Chromatic resists. + +==03/07/2006 +FatherNitwit: Fixed up adding a launcher to not falsely report an error. +Doodman: Fixed sending old style info from world->ls (fixing minilogin) +Doodman: Fixed TCPConnection cleanup on socket closed by the remote + +==03/06/2006 +Doodman: Fixed deadlock when zones are shutting down. +Doodman: Fixed MakeUpperString and MakeLowerString to function correctly on a NULL string +Doodman: Made HTTPSocket inherit from TCPConnection instead of using +Doodman: Fixed HTTP POST processing (needs a good workout still) + +==03/05/2006 +FatherNitwit: Developed stream proxy, stream identifier, and struct strategy to support dynamic structures. +FatherNitwit: Developed initial patch support for 0.6.2 and Titanium. + +==03/04/2006 +FatherNitwit: Fixed some broken logging in world. +FatherNitwit: Fixed the potential crash when zone connects to world. + +==03/04/2006 +FatherNitwit: Significantly refactored the old TCPConnection/TCPServer code. +FatherNitwit: Rewrote almost the entire web server to get rid of most of the socket lib. +FatherNitwit: Removed everything but the HTTP framework from the socket lib. +FatherNitwit: Rewrite better have fixed the windows crash. + + +==02/28/2006 +Changed version number to 0.6.5-DR1 +Doodman: Put in new XML configuration object and made zone/world use them +Doodman: Added auto ip configuration to world/LS +Doodman: Added auto ip/port configuration to zone +Doodman: Reworked logging in world to use the new logsys +Doodman: Made TCPConnection detect a closed socket like it should +FatherNitwit: Significantly refactored world for better code organization. +FatherNitwit: Significantly refactored the database object for better organization. +FatherNitwit: Fixed several valgrind errors. +FatherNitwit: Developed a custom webserver for world. +Doodman: Developed direct database support from perl. +FatherNitwit: Wrote many objects and pages for world. +Doodman: Wrote many objects and pages for world. +Doodman: Moved addon.ini (commands) into the database with world pages. +FatherNitwit: Developed a launcher for zone. +FatherNitwit: Developed world HTTP pages to manage launcher. +FatherNitwit: Fixed several issues with stats loading from the database. +FatherNitwit: Fixed high stat loading from the database. +FatherNitwit: Added `#grid max` command to help with grid creation. +FatherNitwit: Significant adjustments to combat, should bring things closer to live. Make sure your mobs have stats in the DB! +FatherNitwit: Fixed procs for NPCs with proc-only spell sets. + +CREATE TABLE launcher ( + name VARCHAR(64) NOT NULL, + dynamics TINYINT UNSIGNED NOT NULL, + PRIMARY KEY(name) +); + +CREATE TABLE launcher_zones ( + launcher VARCHAR(64) NOT NULL, + zone VARCHAR(16) NOT NULL, + port MEDIUMINT NOT NULL DEFAULT '0', + PRIMARY KEY(launcher,zone) +); + +CREATE TABLE commands ( + command varchar(20) NOT NULL default '', + access tinyint(3) unsigned NOT NULL default '0', + PRIMARY KEY (command) +); + + +==01/20/06 +FatherNitwit: Reworked melee and ranged attack code significantly. +FatherNitwit: Properly support recomended level on weapon DMG now. +FatherNitwit: Re-implemented racial/body bane damage and elemental damage. +FatherNitwit: Added "Immune to Non-Magical Melee" flag to NPCs (W) +FatherNitwit: Added "Immune to non-Bane Melee" flag to NPCs (O) +FatherNitwit: Added canbind flag to zone table to completely prevent binding in certain zones. +Required SQL: +ALTER TABLE zone ADD canbind TINYINT NOT NULL DEFAULT '1'; + +==01/14/06 +FatherNitwit: Fixed ranged misses not generating hate. +FatherNitwit: Find is as working as good as it ever was (straight line) + +==01/13/06 +FatherNitwit: Made some changes to buff stacking rules reguarding DoTs and beneficial overwrite. + +==01/12/06 +FatherNitwit: Fixed the "Buffs do not apply effects in client after zoning" issue. + +==01/09/06 +FatherNitwit: Made NPCs actually load: resists, attack_speed, findable +FatherNitwit: npc_types cleanup day (they dont work or are not used): +ALTER TABLE npc_types ADD attack_speed float NOT NULL default '0'; +ALTER TABLE npc_types DROP ipc; +ALTER TABLE npc_types DROP banish; +ALTER TABLE npc_types DROP social; + +==01/08/06 +FatherNitwit: Fixed more windows compile errors on .net 2003+... + +==01/07/06 +FatherNitwit: Fixed VS.net projects and compiling. (.net 2002) + +==01/04/06 +FatherNitwit: Fixed the quad special attack to not require triple in order to work. + +==01/03/06 +FatherNitwit: Added new debug logging facility. Will be implemented further in the time to come +FatherNitwit: Added command #mlog to manipulate the new logging facility. +FatherNitwit: Load log settings from log.ini if present. +FatherNitwit: Made GM's fade detrimental buffs when they die. +FatherNitwit: Reworked much of the spell casting subsystem. +FatherNitwit: Reworked bards, they should work OK now! +FatherNitwit: Debug Logging written for: spell casting, spawns, spawn conditions +FatherNitwit: Fixed block training for monks and beastlords +FatherNitwit: Fixed damage shields +FatherNitwit: Fixed a bunch of spell stacking issues +FatherNitwit: Properly enforced range checking on all spells. +FatherNitwit: Hopefully fixed long recast delay enforcement. +FatherNitwit: Removed rule that disallowed beneficial spells cast by NPCs on PCs +FatherNitwit: Hopefully fixed pottery firing. +FatherNitwit: Fixed NPC buffing faction checks. +FatherNitwit: Added LOS checks for detrimental AOE spells. +FatherNitwit: Changed version number to 0.6.4DR1 + +==12/30/05 +FatherNitwit: Fixed event_waypoint in quests +FatherNitwit: Refactored a lot of NPC pathing code (no functional changes) +FatherNitwit: Added zone flagging concepts/enforcement +FatherNitwit: Added commands: #flags, #flagedit +FatherNitwit: Added quest functions: set_zone_flag, has_zone_flag, clear_zone_flag + +Required SQL: +CREATE TABLE zone_flags ( + charID int NOT NULL, + zoneID int NOT NULL, + PRIMARY KEY(charID,zoneID) +); +ALTER TABLE zone ADD flag_needed VARCHAR(128) NOT NULL DEFAULT ''; + +==12/23/05 +FatherNitwit: Added #reloadstatic to reload doors, objects, zone points, etc... (zone in/out required) +FatherNitwit: Fixed mobs to buff their friends, not their enemies. +FatherNitwit: Allow support for quests on charmed NPCs. +FatherNitwit: Changed quest timers to be tied to the specific NPC, instead of the name being zone global. +FatherNitwit: Fixed quest global expiration times... durations should be accurate now. +FatherNitwit: Re-enabled Y (year) expiration times, and added a 'F' time to say it dosent expire. + +==12/18/05 +FatherNitwit: Removed doors from shared memory. +FatherNitwit: Hopefully fixed pottery firing in kilns. + +==12/15/05 +FatherNitwit: Fixed Levitate. + +==12/11/05 +FatherNitwit: Removed some worthless fields. +FatherNitwit: Rewrote all the pet creation code to be based on npc_types +FatherNitwit: Completely changed the pets table. +You prolly want to run: +DROP TABLE pets; +You must load up the SQL in Sql-Files/pets.sql +the new pets sql assumes NPC IDs 500-700 are free, so edit them as needed. +ALTER TABLE npc_types DROP fixedz; + +==12/09/2005 +FatherNitwit: Fixed the issue where maxed out factions cannot be raised/lowered +FatherNitwit: Changed faction hits to properly ignore illusions. +FatherNitwit: Fixed an issue with converted chars and corrupt AAs. (not fully resolved) +FatherNitwit: Fixed the issue with mobs not pathing after respawning. + +==12/01/2005 +FatherNitwit: added USE_RACE_CLASS_XP_MODS (disabled by default) to features.h +FatherNitwit: These fields are not used: +ALTER TABLE zone_points DROP keep_x; +ALTER TABLE zone_points DROP keep_y; + +==11/28/2005 (LiveCompat) +Doodman: Found additional world opcodes +Doodman: Updated char select struct + +==11/24/2005 +FatherNitwit: just some SQL updates you should run to make the recent aggro changes less annoying. +UPDATE npc_types SET _INT=80 WHERE _INT=75; +UPDATE npc_types SET _INT=60 WHERE _INT=80 AND ( bodytype=3 OR bodytype=8); +ALTER TABLE npc_types CHANGE _INT _INT mediumint(8) unsigned NOT NULL default '80'; + +==11/17/2005 +FatherNitwit: Fixed a crash in the map code +FatherNitwit: Tweaked mob usage of d_meele_texture* + +==11/11/2005 +FatherNitwit: Changed most double prescision operations to single prescision. + +==11/10/2005 +FatherNitwit: Fixed waypoint ID limit (was 65535). +FatherNitwit: Implemented the #wpinfo command + +==11/05/2005 +FatherNitwit: Fixed the broken server side filters (reworked entirely). +FatherNitwit: Refactored damage code to be more consistent between PCs and NPCs. +FatherNitwit: Changed spell damage mechanism slightly to better utilize runes. +FatherNitwit: Tweaked a bit of illusion code to be more versitile. + +==11/03/2005 +FatherNitwit: Fixed AA storage in the player profile. + +==10/28/2005 +FatherNitwit: Fixed a lot of GCC 4.0 and 64 bit problems. Should build under both better now. + +==10/24/2005 +FatherNitwit: Fixed mob int loading. + +==10/23/2005 +FatherNitwit: Fixed Client->NPC->Client riposte. + +==09/28/2005 +FatherNitwit: added quest::attacknpc and quest::attacknpctype +FatherNitwit: Cleaned up usage of Appearance in the code +FatherNitwit: Fixed faction ally checking for npc->npc faction +FatherNitwit: Removed some stupid rules reguarding npc aggro +FatherNitwit: Fixed some faction issues with aggro, guards should work reliably now. + +==09/25/2005 +FatherNitwit: Fixed incorrect usage of EXPMod variable. + +==09/24/2005 +FatherNitwit: Hopefully fixed bard instruments. +FatherNitwit: Fixed crash when loading DoD+ spells_us.txt file +FatherNitwit: Rewrote a ton of the perl parser internals to suck less. +FatherNitwit: Reworked a bit of netcode to minimize chances of #opcode crashing. +FatherNitwit: Actually loaded luclin attributes from DB. +FatherNitwit: Added npc stats to npc_types and use them. +ALTER TABLE npc_types ADD STR MEDIUMINT UNSIGNED NOT NULL DEFAULT '75'; +ALTER TABLE npc_types ADD STA MEDIUMINT UNSIGNED NOT NULL DEFAULT '75'; +ALTER TABLE npc_types ADD DEX MEDIUMINT UNSIGNED NOT NULL DEFAULT '75'; +ALTER TABLE npc_types ADD AGI MEDIUMINT UNSIGNED NOT NULL DEFAULT '75'; +ALTER TABLE npc_types ADD _INT MEDIUMINT UNSIGNED NOT NULL DEFAULT '75'; +ALTER TABLE npc_types ADD WIS MEDIUMINT UNSIGNED NOT NULL DEFAULT '75'; +ALTER TABLE npc_types ADD CHA MEDIUMINT UNSIGNED NOT NULL DEFAULT '75'; + +==09/23/2005 +FatherNitwit: Inverted XY coordinates on in zone objects and ground spawns. +FatherNitwit: Maybe fixed quests setting a waypoint in EVENT_WAYPOINT +ALTER TABLE ground_spawns ADD temp_x float NOT NULL; +UPDATE ground_spawns SET temp_x=min_x; +UPDATE ground_spawns SET min_x=min_y; +UPDATE ground_spawns SET min_y=temp_x; +UPDATE ground_spawns SET temp_x=max_x; +UPDATE ground_spawns SET max_x=max_y; +UPDATE ground_spawns SET max_y=temp_x; +ALTER TABLE ground_spawns DROP temp_x; +ALTER TABLE object ADD temp_x float NOT NULL; +UPDATE object SET temp_x=xpos; +UPDATE object SET xpos=ypos; +UPDATE object SET ypos=temp_x; +ALTER TABLE object DROP temp_x; + + +==09/22/2005 +FatherNitwit: Reworked quest signaling implementation to be less dumb. Also exported SignalNPC to perl. +FatherNitwit: Implemented a missing bard AE DOT spell type. + +==09/21/2005 +FatherNitwit: Fixing a few issues with windows compiles and corpses. + +==09/19/2005 +FatherNitwit: Added missing packet for player death to create a corpse. + +==09/16/2005 +FatherNitwit: Fixed loot error case client hang, reported by Yablargo + +==09/15/2005 +FatherNitwit: Fixed coin trading bug reported by Windcatcher + +==09/12/2005 +FatherNitwit: Fixed adventure merchants + +==09/11/2005 +FatherNitwit: Made temp merchant lists clear on world boot. +FatherNitwit: Fixed some shared memory issues on windows. +FatherNitwit: exported some more mob methods to perl. + +==08/26/2005 +Doodman: Found most of the missing guild opcodes, except the manage ones. +Doodman: Moved guildlist to shared mem (ick) since zone now sends it too. +Doodman: Need to implement a way to recover the functionality of dynamicly + adding/removing guilds with the shared mem solution + +==08/23/2005 +FatherNitwit: Added range check for all combat abilities (reported by LoOsEr) + +==08/21/2005 +FatherNitwit: Fixed gaining AA exp (buying still broken) +FatherNitwit: Fixed weapon procs +FatherNitwit: Added looting messages (links to come later) +FatherNitwit: Fixed instant spells acting like DoTs + +--- Version Changed to 0.6.2DR1 --- + +==08/05/2005 +FatherNitwit: Adding database support for both eye colors and beard type. +Required SQL: +ALTER TABLE npc_types ADD luclin_eyecolor2 int(10) unsigned NOT NULL default '1' AFTER luclin_eyecolor; +ALTER TABLE npc_types ADD luclin_beard int(10) unsigned NOT NULL default '0' AFTER luclin_beardcolor; + +==08/03/2005 +FatherNitwit: Work on eqextractor to support the newest patch. +FatherNitwit: Worked on spawn struct to identify the remaining missing fields. + +==08/02/2005 +FatherNitwit: Added initial fix for the shared bank dupe. + +==07/28/2005 +FatherNitwit: Fixed $wp in EVENT_WAYPOINT +FatherNitwit: added #aggro to query aggro checking info for mobs. +FatherNitwit: fixed a couple strange things in the aggro code. + +==07/07/2005 +FatherNitwit: Untested fix for zone point wildcards in GetClosestZone* + +==05/09/2005 +FatherNitwit: Fix some stuff related to zoning process. +update zone set min_status=0 where min_status is null; +update zone set min_level=0 where min_level is null; +alter table zone change min_status min_status tinyint(3) unsigned default 0 NOT NULL; +alter table zone change min_level min_level tinyint(3) unsigned default 0 NOT NULL; +alter table zone change long_name long_name text NOT NULL DEFAULT ''; + +==05/05/2005 +FatherNitwit: Theoretically fixed a no-drop trading bug. + +... Lots of work on 5/12/05 patch ... + +==05/05/2005 +FatherNitwit: A bunch of work on ranged attacks and attack timers. +FatherNitwit: Fixed issue with the first time a new char gains exp. +FatherNitwit: Fixed delete character at char select. +FatherNitwit: Fixed fizzle messages. +FatherNitwit: Fixed /goto and other cross-zone player teleporting. +FatherNitwit: Initial work on making arrows show up when shot. + +==04/23/2005 +LethalEncounter: Fixed compile errors on Windows +LethalEncounter: Fixed guild crash bug while logging in +LethalEncounter: Fixed most of the guild commands + +==04/13/2005 +Cofruben: Fixed possible crashes using #npscpawn command. +Cofruben: Added a few more cheat locators. + +==04/10/2005 +FatherNitwit: Reworked zone in code to make it more closely match live's ordering. +FatherNitwit: Fixed up stream factory problem with dynamic zones. +FatherNitwit: Added detailed connecting debugging. +FatherNitwit: Work on custom titles. + +==04/08/2005 +FatherNitwit: Completely rewrote zoning code to make sense. +FatherNitwit: Fixed some discipline related effects +FatherNitwit: Fixed archery hit chance cap (was 47%) +FatherNitwit: Initial title system implementation +FatherNitwit: Gave berserkers some of their special abilities +FatherNitwit: Initial work on raid system, lots to do still. +FatherNitwit: Found leadership AAs in the player profile and update packet +FatherNitwit: Enforce attack timer constraints on ranged weapons +FatherNitwit: (seq) Located crystal counters in the player profile +FatherNitwit: (image) Many of the following changes were written or inspired by GuildWars code +FatherNitwit: Enforce reuse timers for combat abilities & instill doubt +FatherNitwit: Enforce class and range checks on service providing NPCs +FatherNitwit: Enforce item slots containing proper items +FatherNitwit: Enforce range checking on ranged weapons better +FatherNitwit: Made /hideme persistent across zoning +FatherNitwit: Finally fixed no drop items in tradeskill containers for real this time I hope. +Required SQL (added to upgrade.sql): +ALTER TABLE account ADD hideme TINYINT NOT NULL DEFAULT 0; + +==04/04/2005 +FatherNitwit: Fixed guild member list struct, identified more fields of it (showeq) +FatherNitwit: (bUri) Fixed attack code with non-weapons and h2h weapons. +FatherNitwit: Split zone fog types into seperate DB fields. +FatherNitwit: Differentiated between guild and player tributes. +Required SQL: utils/0.6.1-upgrade.sql (will be updated until release) + +==01/15/2005 +Doodman: Fixed charges/quantity +Doodman: Fixed IsStackable() +Doodman: Fixed some empty Handle() functons. +Doodman: Added instance level nodrop. +Required SQL: +alter table inventory add instnodrop tinyint(1) unsigned default 0 not null; + +==01/13/2005 +Doodman: Merged source (FNW changes) in to LiveCompat +Doodman: SendItemPacket's are now sent deflated (speeds up merchants) +Doodman: Combining is now turned on in makefile.perl for zone +Doodman: Fixed world to notice zone drops immediately +Doodman: Guild_MOTD and bulk door packets are now sent deflated +Doodman: New item structure is in: + Items are preserialized in the database (utils/serialize_items.pl) + Item fields names in item struct exactly match the dn + Item fields now match the dump from (utils/load_13thfloor_items.pl) + eqitems.13th-floor.org + Item table convert script: utils/items-0.6.0-DR2-0.6.1-DR1-convert.sql + +==01/10/2005 +FatherNitwit: Disable sleep() in perl, it is bad news. +FatherNitwit: Fixed guild MOTD at login (hopefully). +FatherNitwit: Fixed minor security problem in SQL logging. +FatherNitwit: Added error reporting to waypoint editing commands. +FatherNitwit: Reworked client packet handling to use opcode dispatch + routines, which is paving the way for dynamic opcodes. + +==01/07/2005 +FatherNitwit: Fixed rogue-like skill training (sneak, etc..) +FatherNitwit: Added spawn_limit field to spawnentry and npc_types + which represent the maximum number of that npc_type which can spawn + from that spawn group/in that zone overall. 0 == no limit. +FatherNitwit: Tweaks to the packet update manager +FatherNitwit: More SQL logging (spawn editing) +FatherNitwit: Fixed Monk bind wound over lvl 50 to 70%. +FatherNitwit: Fixed Monk attacks without weapons +FatherNitwit: Fixed intimidation, success formula needs work. +FatherNitwit: Fixed disciplines again. +FatherNitwit: Added quest::isdisctome to make tome handins work better. +FatherNitwit: Fixed levitate from other people's eyes. +FatherNitwit: Fixed a few minor crash bugs. +FatherNitwit: (Cisyouc) Fixed merchant purchases not costing anything. +FatherNitwit: Implemented the snare fix from the forums (edited spell calc formulas) +Required Queries: +ALTER TABLE spawnentry ADD spawn_limit TINYINT NOT NULL DEFAULT '0'; +ALTER TABLE npc_types ADD spawn_limit TINYINT NOT NULL DEFAULT '0'; + +==01/04/2004 +Doodman: opcode/struct changes to allw zoning in on live. + +==12/29/2204 +Doodman: Fixed leak in Separator + +==12/21/2004 +FatherNitwit: fixed item handing to non-quest NPCs +FatherNitwit: delete nodrop or norent items left in world containers +FatherNitwit: changed quest::spawn commands to return the NPC ID of the spawned mob (in XS) +FatherNitwit: Cleaned up perl variable setting a lot +FatherNitwit: Tribute masters should work completely now. +FatherNitwit: Fixed login problem wher chars get into zone and move, but nothing else works. +FatherNitwit: fixed #level and other leveling commands to give proper ammount of training points. +FatherNitwit: Added new extended player profile for emu custom data storage +FatherNitwit: Implemented elemental damage on weapons +FatherNitwit: Cleaned up memory allocation a bunch +FatherNitwit: Made merchant lists load async, to speed dynamic boots +FatherNitwit: Fixed repop to not kill player pets. +FatherNitwit: Made pets cross teleport doors with their master. +FatherNitwit: Tweaked teleport doors, they should always work now. +FatherNitwit: Added optional command logging +FatherNitwit: Moved a lot of hard coded access levels for commands to features.h +FatherNitwit: Added optional status requirement to zone to specific coords +FatherNitwit: Fixed several minor crashes. +FatherNitwit: Added the unfearable flag (D in special attacks) to mobs. +FatherNitwit: Fixed mana burn and life burn (from Branks) +FatherNitwit: Added optional packet manager for mob movement packets (PACKET_UPDATE_MANAGER) +FatherNitwit: Configure it in updatemgr.cpp (level_distances2,level_timers) +FatherNitwit: Added reference counting to applayer packet (for broadcasts) +FatherNitwit: Improved packet profiler a bunch, added #packetprofile command +FatherNitwit: Cleaned up eq_packet_structs.h a bit +FatherNitwit: Required DB update: +ALTER TABLE character_ ADD extprofile BLOB NOT NULL; + +==11/20/2004 +Doodman: Fixed merchant selling bug on merchant slot + +==11/18/2004 +Doodman: Fixed the initialization of augs on NPC loot that was causing a zone crash when looting NPC's + +==11/16/2004 +Doodman: Implemented Augmentation inserting, removal and distilling. +Doodman: Augment saves in inventory, shared bank, player_corpse and object_contents. +Doodman: Fixed world to detect and cleanup a zone disconnect immediately instead of when it tries to write to it. +Doodman: Reimplmented cursor queue using an actually queue. Should be unlimited depth now. +Doodman: Updated items for new fields and new names. + +==11/10/2004 +LethalEncounter: Fixed a couple of corpse related bugs. +LethalEncounter: Added in various functions for the new login system. + +==11/09/2004 +FatherNitwit: Fixed minor crash with new grid delay thing. + +==11/08/2004 +LethalEncounter: Fixed loot bug with stacked items. +LethalEncounter: Added a 10 second timer to grid assignments to speed up zone in time. +FatherNitwit: Fixed invalid bind zone crash. +FatherNitwit: Fixed taunt crash. +FatherNitwit: Initial tribute system. Dosent give benefits, or save state yet. +FatherNitwit: Proximity quests: added quest::set_proximity, quest::clear_proximity +FatherNitwit: Proximity quests: added events: EVENT_ENTER, EVENT_EXIT +FatherNitwit: Added event queue for perl commands which cause perl events, like spawn and attack +FatherNitwit: Added the ability to define in game commands in perl (EMBPERL_COMMANDS) +FatherNitwit: Hopefully fixed multiple-death XP loss bug. +FatherNitwit: Hopefully fixed mob chain-casting buffs when target is immune +FatherNitwit: Reworked quest timers to not crash if the NPC depops/dies. +FatherNitwit: Added simple code profiling capabilities (-DEQPROFILE + features.h) +FatherNitwit: Rewrote command list to be map based instead of flat array +FatherNitwit: Rewrote NPC assist to use AIYellForHelp at regular intervals instead of each scan tic +FatherNitwit: Rewrote NPC assist code to use npc faction values to determine assist +FatherNitwit: Rewrote NPC aggro code to be a ton more effecient (REVERSE_AGGRO) +FatherNitwit: Rewrote NPC spell casting code to be a ton more effecient +FatherNitwit: Run this query (value of 1 == NPC might aggro other NPCs): +ALTER TABLE npc_types ADD npc_aggro TINYINT NOT NULL DEFAULT '0'; + +==11/07/2004 +LethalEncounter: Added in three variables per feature request. + +==11/06/2004 +LethalEncounter: Fixed Merchant bug that wouldnt let you buy anything. +LethalEncounter: Fixed stack bug that wouldnt automatically stack any items you bought. + +==11/05/2004 +LethalEncounter: Client Freeze/Black Screen of Death bugs have been eliminated. +LethalEncounter: Added a simple count to our linked list so we can process a few things faster and more efficiently. +LethalEncounter: Fixed a small compile bug in regards to corpses. + +==11/04/2004 +FatherNitwit: Fixed exploit with split that kathgar found. + +==11/01/2004 +LethalEncounter: Inventory items on zone in are now sent in one packet like live instead of individual packets. This will save on bandwidth and processing time. +Cofruben: Added support for /makeleader command. +FatherNitwit: Increased max buffs from 15 to 20 +FatherNitwit: Fixed group zoning +FatherNitwit: Fixed loading map files on UNIX with capitol letters +FatherNitwit: Fixed a couple crashed related to old AA stuff +FatherNitwit: Cannot charm a corpse or charm something when you have a pet +FatherNitwit: A little work on zoneing with a chanrmed pet, not perfect yet +FatherNitwit: Fixed throwing weapons like shurikens +FatherNitwit: Broke all the old LDoN points stuff because it moved in the Player profile +FatherNitwit: Moved all quest command's logic into the QuestManager class +FatherNitwit: Rewrote perl to use XS instead of command queue (EMBPERL_XS) +FatherNitwit: Added routines to capture perl errors into proper log IO (EMBPERL_IO_CAPTURE) +FatherNitwit: Added a HUGE set of routines for quest objects, with new variables + $client, $npc, and $entity_list... almost the entire class is exported. (EMBPERL_XS_CLASSES) +FatherNitwit: Added capability to send log messages to in-game clients (CLIENT_LOGS) +FatherNitwit: Added new log facility for quest messages named Quest +FatherNitwit: Added new commands #logs and #nologs to request/stop the server sending you its log messages +FatherNitwit: Fixed NPC -> NPC aggro, need to run these queries onan older DB: +ALTER TABLE npc_faction_entries ADD npc_value TINYINT UNSIGNED DEFAULT '0' NOT NULL; +UPDATE npc_faction_entries SET npc_value=1 WHERE value<0; + +==10/31/2004 +LethalEncounter: Aside from a few tweeks, merchants are finished. They will now keep your items after you sell them like live. +LethalEncounter: Fixed bug that caused falling damage to be calculated twice. +LethalEncounter: Changed Sense Heading to start at 200 for new characters. +LethalEncounter: Black screen bug should be fixed now. +LethalEncounter: Fixed bash/slam. + +==10/30/2004 +LethalEncounter: Fixed a few issues with item charges. +LethalEncounter: Fixed illusions. +LethalEncounter: Rewrote the merchant code. Its now much more efficient and instead of 81 database queries per shop request, a typical request doesnt pull anything from the db. It loads the information at load time. +LethalEncounter: Added in the beginning parts of temporary merchant items. (you can sell items to merchants and purchase them back) Download the merchantlist_temp table from: http://www.eqemulator.net/sql/merchantlist_temp.sql + +==10/27/2004 +Cofruben: Added LDoN adventure system.Please,source the new sql file(thanks to skorch for testing). +LethalEncounter: Mob deaths should be sent to all players now. +LethalEncounter: ServerType 1 will make you pvp on zone in now. Still trying figure out why the client doesnt send any pvp special combat abilities unless your dueling. + +==10/26/2004 +Scorpious2k: Fixed guild doors + +==10/25/2004 +FatherNitwit: Fixed zone crash related to character_ query. +FatherNitwit: Fixed book reading to give proper book window. +FatherNitwit: At least partialy fixed pet dual weild/double attack problem. +FatherNitwit: Tweaking on selling stackable items. +FatherNitwit: Fixed archery ammo issue. +LethalEncounter: Added ability to /consent (and /deny) other players so they can drag your corpse. +LethalEncounter: Fixed bug where other people were able to loot your corpse. + +==10/24/2004 +LethalEncounter: Fixed bug where you lost air supply on zoning in underwater. +LethalEncounter: Fixed GM damage bug. + +==10/23/2004 +FatherNitwit: Enable named quests by default (in features.h) +FatherNitwit: Fixed decaying world containers problem +FatherNitwit: Fixed undead nuke spells +FatherNitwit: Minor line of sight optimization +FatherNitwit: Minor tradeskill change to avoid a crash +FatherNitwit: Changed the way fear duration is calculated, hopefully it is better. +FatherNitwit: Implemented beta fear pathing code (disabled by default in features.h) +LethalEncounter: Reworked AA system and fixed some of the bugs with it. +LethalEncounter: Dyes should work correctly now. +LethalEncounter: Fixed Bazaar trader bug. +LethalEncounter: Zones now display the proper fog when you update your zone table with the latest. +LethalEncounter: Fixed zone crash and merchant item charge bug. + +==10/22/2004 +LethalEncounter: Fixed item deletion bug not updating client. + +==10/21/2004 +LethalEncounter: Fixed various AA related bugs. + +==10/20/2004 +LethalEncounter: Reworked zoning in, solved a couple of issues and should speed the process up a tad. + +==10/19/2004 +FatherNitwit: Fixed Disciplines and Abilities (lay hands & harm touch) +FatherNitwit: Added new server variable: DisableNoDrop (set to 1 takes nodrop off items when loaded) +FatherNitwit: Fixed NPC buffing through walls +FatherNitwit: Fixed symbol spell forumulas +LethalEncounter: Fixed equip bug when using two handed weapons. +LethalEncounter: Fixed resurrect bug that let you regain xp more than once. +LethalEncounter: Fixed bug that was making players corpses disappear on death when they shouldnt have. + +==10/18/2004 +LethalEncounter: Fixed pet only spells. +LethalEncounter: Fixed bug that wouldnt let you loot a corpse if someone got an error looting it. (such as a dupe lore error) +LethalEncounter: Fixed mana regen bug. + + +==10/17/2004 +LethalEncounter: Added some debug code to make it easier for people that cant get minilogin working. +LethalEncounter: You will now eat/drink like eqlive if you arent a GM. +LethalEncounter: Fixed infinite recursion bug I made a few days ago :/ + +==10/16/2004 +LethalEncounter: Fixed mana bug that was causing people not to regen their full mana. +LethalEncounter: HP Adjustments, will now send your hp update every tic and only the mob you have targeted (not including group hp updates) to reduce bandwidth. +LethalEncounter: Fixed a crash bug on character creation. Thanks to blahblah for the help :P +LethalEncounter: Fixed item charge bug, need to update your database. + +==10/15/2004 +LethalEncounter: Changes necessary for the new MiniLogin :) +LethalEncounter: Z loc fix, it was placing you ten times the z loc it was supposed to :P + +==10/14/2004 +Rogean: omg we are so teh 0.6.0DR1 now~ + +FatherNitWit: +A HUGE thanks goes out to wiz for letting me have his source +to facilitate this merge. + +WR Merges: +- An innumerable number of small bug fixes and tweaks all over +- A TON of AAs and related effects +- FISHING!!!!!!!!! +- Many timers enforced by server, not just client. +- Trap support. +- Sense trap and disarm trap +- Group linking +- Zone-crossing pets (lose their buffs though, for now) +- Voice graft +- poison and disease counters (in progress) +- hunger and thirst (in progress) +- stat food +- NPC door opening! +- Optimized idle zones to not eat the CPU +- Items tints display properly +- shielding other players (in progress) +- pick pocket +- Optional EXP scaling based on CON level +- Instill Doubt +- Made auto-putting items into bags respect size constraints. +- heal/buff aggro. +- reclaim pet gives you mana back. +- enforce undead and summoned targets spells +- reworked bard songs a bit +- implemented camping properly +- made alcohol tolerance gain skill (dunno if it does anything yet) +- better checking of lore items during trades and merchant buying +- prevent interaction with merchants on basis of bad faction +- pets should actually taunt now. +- rouge pets should backstab now. +- optional: group buffs hit group pets now. +- fixed memory blur chances +- several minor group tweaks, should make groups more stable +- improved duel messages +- optional random luclin attributes for NPCs with boring faces +- AGI adjustments based on encumbrance +- AC bonuses for iskars and monks + + +FatherNitwit Improvements: +- Made most member timers instances, not pointers. +- Rewrote focus effects system, spells that give focus effect should work too. +- Reagent reduction focus implemeneted +- Rewrote activateable AAs and swarm pets based on Brank's work +- AA spell effects are now in the DB, easily editable +- Added a bunch of simple GoD AAs +- Consolidated many #define options into features.h +- Fixed crash in world when starting zone dosent exist +- Fixed dual weild with hand-to-hand +- Extended fishing to allow mobs to spawn when items are fished up. +- Made item skill modifiers work. +- Identified meaning of old Skill field of common items. +- made bind wounds actually consume bandaids. +- Made archery and throwing consume ammo +- Archery weapons proc now +- Fixed bard item bonuses to apply to all a bard's spell effects. +- rewrote perl HasQuestFile function to behave properly. +- modified perl quest system to fit better into the virtual parser scheme. +- enforce spell/skill components being on the player, not in bank. +- fixed container loading from DB. +- implemented item skill bonuses +- prevent merchant purchases when inventory full. +- safe fall implemented, but the formula is wrong +- initial implementation of client rampaging +- eliminated a lot of sqrt calls +- added an optional packet profiler to EQ network to count opcodes +- fixed NPC-casted targeted AE spells affecting the caster +- fixed monk special attack damage to be skill based. +- most NPC classes do their special attacks now. +- fixed multi-dose potions (not 100% sure they were broken) +- implemented Percental Heal spells +- put caps on haste, HP, and mana regeneration items +- Changed HP regen to work without client's regen packet, which it dosent send anymore. +- Updated natural HP regen to be closer to live. +- corrected AC calculations +- Implemented new discipline interface. +- Added new perl function (traindisc(itemid)) to facilitate training disciplines +- changed discipline reuse timer to a ptimer, since it is long. +- implemented a ton of new spell effects to support disciplines. +- implemented PoP item attributes like shielding, accuracy, etc... +- rewrote much of group system to better support multi-zone groups. +- groups given world-wide IDs instead of zone local. +- group disbanding when split between zones works. +- Fixed warrior/monk triple attack +- Fixed NPC AE spells to respect faction +- Tweaked NPC non-caster mobs being able to cast 'proc' spells. +- Fixed base resistance calculations to match the client. +- Cleaned up NPC spell interruption messages when they were not needed. +- DOTs no longer make you stand every tic. +- Fixed mob immunities special flags +- Added new mob immunities: snare, magic, meele +- Immunity to stuns now includes spin effects as well +- Trading with NPCs should correctly handle no drop items. +- fixed AA EXP setting +- allow starting items to include books and empty bags +- Fixed starting zones problem on char creation. +- Allow bards to move and use clicky items. +- Make NPC casters send stop moving packet when they cast. +- Fixed archery ammo searching to fall over to inventory. +- mobs now dual-weild based on a chance instead of garunteed +- groups do not grant EXP if the mob is green to any group members +- some NPC combat tweaks to balance it over the levels better. Needs through testing. +- EXP is now awarded to the top damaging player/group instead of killer +- fixed donal's complete heal +- fixed AC calculations for high-agi players +- Changed mobs with primary faction = 0 to be indifferent. +- If a player logs/zones in to a Z below the world, they are placed at a valid Z if possible. +- labeled new item field as attuneable, updated DB code for it + +==10/10/2004 +LethalEncounter: Fixed some issues with hp and mana. +LethalEncounter: Fixed bug with zoning z coord. + +==10/09/2004 +LethalEncounter: Fixed spell scribing and meming. +LethalEncounter: Merged in Xabob's expansion fixes (except regen which FNW is working on). +LethalEncounter: Merged in RangerDown's fixed version of #spawnfix. +LethalEncounter: Fix for #zcolor that was brought up by rmanders on the bug forum. +LethalEncounter: Character conversions from 5.7 to 5.9 should be fixed now. +LethalEncounter: Implicit length fix and mana struct fixed. + +==10/05/2004 +FatherNitwit: Fix name generator opcode +FatherNitwit: Added loot table drop chance fix +FatherNitwit: Cleaned up some map code and other thing + +==10/02/2004 +LethalEncounter: Updated AAs so they are sent based on class now. + +==10/02/2004 +LethalEncounter: Updated to version 5.9-DR2 (EQLive Compatible). + +==10/02/2004 +LethalEncounter: Updated all the structs/opcodes necessary to login using the latest client. + +==10/02/2004 +LethalEncounter: Misc AA fixes. + +==09/29/2004 +LethalEncounter: Fixed AAs, you can now buy them correctly. Big thanks to Xabob for PacketCollecting the information for me!! + +==09/26/2004 +LethalEncounter: Fixed the displaying of AAs, moving them into two tables. Be SURE to source aa.sql for these updates. + (Will fix the buying aspect soon) + +==09/25/2004 +Doodman: Fixed linux kernel 2.6.x shared memory bug. IPC_NOWAIT is not allowd on shared memory segment operations. + +==09/20/2004 +Scorpious2k: Allow multiple Perl plug-ins + +==09/17/2004 +Doodman: (un)fixed world to hand the LS->world hand off like it did in the past + +==/09/12/2004 +FatherNitwit: Added my LOS code and map converter. Thanks to wiz for help with testing. + +==/09/09/2004 +FatherNitwit: Adding basics of spell specialization. + +==/09/07/2004 +FatherNitwit: Fixed #heal on pets. + +==/09/05/2004 +FatherNitwit: Fixed minor flaw in player profile with skills. +FatherNitwit: Merged My Tradeskill Rewrite +LethalEncounter: Moved zone configurations from the old flat file method to the database. +BE SURE TO SOURCE ZONECFG.SQL!!! + +==/09/04/2004 +FatherNitwit: Merged in ProjectEQ's 2grid system +FatherNitwit: Fixed new style hate/regen/mana regen on items +FatherNitwit: Collected PoP item features from equiped, not used yet +LethalEncounter: Fixed /bug +LethalEncounter: Fixed merchant messages/issues + +==/09/03/2004 +FatherNitwit: Fixed almost ALL compile warnings on UNIX +FatherNitwit: Added my named quest files patch as optional #ifdef +FatherNitwit: Added persistent timers (client->p_timers) +FatherNitwit: Added 508->588 byte .cfg file converter + +==/08/31/2004 +FatherNitwit: Fixed MOB point-blank AE spells to cast (from PEQ folks) +FatherNitwit: Fixed Spawned MOB's being permarooted. + +==/08/29/2004 +LethalEncounter/Cofruben: LOY Guild Management tool updated. +Cofruben: Added new perl function: quest::addloot(itemid,charges). + +==/08/28/2004 +Cofruben: Implemented inspect code. + +==/08/26/2004 +Wiz: Fixed a bug that was displaying unknown spells on login. +Wiz: Fixed a bug that was causing hp to go wonky when you join a group. +Wiz: Fixed a bug that was preventing you from attacking when invulnerable. +Wiz: Fixed a bug that would cause invulnerability spells to stop working if you zoned. + +==/08/25/2004 +FatherNitwit: Redid split, implemented autosplit. + +==/08/23/2004 +Draupner: Fixed spell haste focuses + +==08/21/2004 +LethalEncounter: Finished updating few structs, can now login with latest client. Merchants, combat, loot, etc work fine. + +==08/20/2004 +LethalEncounter: Fixed player profile. + +==08/12/2004 +Cofruben:Added basics of begging. +Cofruben:Added group splitting. + +==08/10/2004 +Wiz: Severely cleaned up position update code and made it more efficent. Should drastically reduce update packets generated by other clients. +Wiz: Improved the LOS calculation. It should use less CPU and work better now. + +==08/02/2004 +Scorpious2k: Changed Quest command settimer to reuse timers with same name instead of creating new + +==07/26/2004 +Cofruben: Added some perl functions,including: ding,addldonpoint,surname,permaclass/race/gender,scribespells. + +==07/20/2004 +Draupner: Added #npcedit + +==07/19/2004 +Doodman(WizeOne): Added beacon.o and embxs.o to makefile.perl +Doodman: Fixed #serverinfo for linux. + +==07/12/2004 +Doodman(RangerDown): Added "You cannot attack while invulnerable" message. + +==07/10/2004 +Scorpious2k: Fixed AA related exploit +Scorpious2k: Fixed AA problem where % remained if player lost levels to below 51 + +==06/27/2004 +Scorpious2k: Added quest command spawn2 - same as spawn except also requires heading +Scorpious2k: Modified Quest Globals. It now always creates a unique var if char specific (not ALL players) +Scorpious2k: Enabled traders +Sandy: Changed mobs so they must have SPECATK_SUMMON to summon players + +==06/23/2004 +Rogean: Mobs no longer Dual Wield with Shields + +==06/22/2004 +Sandy: Event Aggro added +Sandy: Permaroot added +Sandy: x,y,z variables for quests corrected + +==06/17/2004 +Dspirit: More patch 6/16 opcodes corrected + +==06/16/2004 +Dspirit: Patch 6/16 opcodes corrected + +==06/14/2004 +Dspirit: More opcodes corrected + +==06/13/2004 +Dspirit: Multiple opcodes corrected + +==06/12/2004 +LethalEncounter: #zone fixed. +Rogean: Fixed inspecting items in adventure window. +Rogean/Scorpious2k: AA Fixes Implimented + +==06/11/2004 +Doodman: Fixed OP_HPUpdate and SendHPUpate. Client now gets proper HP updates. + +==06/11/2004 +Scorpious2k/Image: #reloadqst now works for perl (Reloads the entire zones quest files). + +==05/29/2004 +Scorpious2k: Changed opcode to fix trade windows >> THANKS Dspirit + +==05/29/2004 +Scorpious2k: Merged in Sandy's EVENT_HP code +Scorpious2k: Merged in Bleh's Perl/XS enhancement +Scorpious2k: Added quest commands for mob movement stop(),start(),pause(),resume() and moveto() + +==05/28/2004 +Doodman/Bleh: Move NPCTypes out of shared mem in to zone specific STL maps. +Doodman/Bleh: Removed NPCTypes.o from makefile in EMuShareMem +Doodman/Bleh: Fixed null pointer dereference in selling + +==5/15/2004 +Doodman: Upped MMF_MAX_NPCTYPE_ID to 400k to support tcsmyworlds db +Doodman: Fixed EQNetwork.cpp to be C++ standard compliant, -not- MS compliant + +==5/11/2004 +Image: Better netcode, rawr. +Scorpious2k: Added AC to mobs - be sure to check MobACupdate.sql +Scorpious2k: Changed AC so it can now be controlled with serverOp variables ACreduction, ACrandom & ACfail +Scorpious2k: Added check so items with req level can't be auto equipped if player not >= req level +Scorpious2k: Added generic (theme = 0) LDoN point processing + +==5/6/2004 +Image: Fixed tradeskill places like smiths + +==4/28/2004 +Image: EQNetwork changes to get packets to send in order +Image: Fixed zones getting bugged (unable to attack, see others, etc.) + +==4/23/2004 +LethalEncounter: Emu works with the latest patch now. Sony did some crazy crap and added 4608 bytes to player profile :/ + +==4/20/2004 +Image: Lifetap now works on PVP +Image: Fixed the stacks of snare/root/speed, snare breaks speed spells (SOW,JBoots,etc.), root breaks snare, root breaks speed spells also. + +==4/17/2004 +LethalEncounter: Fixed merchants that were broken in the last patch. +Image: Inventory item combining added to decrease the traffic sent on zoning. + +==4/16/2004 +LethalEncounter: Fixed item struct that was changed in the last patch, items now work again. + +==4/15/2004 +LethalEncounter: Fixed Player Profile that was changed in the last patch, you can now login correctly. + +==4/08/2004 +Scorpious2k & Tark: Fixed incompatability problem with Perl and combined packets + +==4/02/2004 +Image: Improvements made on the combination code. + +==3/29/2004 +Image: Combined packets now work to full extent. Set app priority to 6 if you don't want it combined, all packets should no longer be deflated,encrypted unless priority 6. +Scorpious2k: Fixed mob movement + +==3/28/2004 +Scorpious2k: Added quest command signal() and EVENT_SIGNAL +Scorpious2k: Made mobs face player for EVENT_SAY and EVENT_ITEM +Scorpious2k: Added smogo's $hasitem for perl quests +Scorpious2k: Added selfcast() command to perl quests + +==3/23/2004 +Scorpious2k: Added AC. Probably needs major adjusting of values to match live, but its a start. + +==3/22/2004 +image: Group inviting is fixed. + +==3/21/2004 +solar: corrected an issue with channeling skill/regaining concentration +solar: changed spell resist so that only detrimental spells are resist checked. + will have to flag beneficial flags that need resist checking later. + +==3/20/2004 +solar: spell resists implemented +solar: added/fixed up some spell effects. notable ones are the stackable + hastes like melody of ervaj +solar: added new version of Wiz's CheckLos routine +solar: fixed a problem with detrimental buffs (root, mez) not working in pvp + +==3/15/2004 +solar: door updates, find sql file in release dir - doorupdate-03-15-04.sql + +==3/13/2004 +solar: fixed zone to zone movement occasionally not working + +==3/10/2004 +solar: spells that are group only should only work on the group now. + +==3/07/2004 +solar: moving coin around should work properly now + +==3/06/2004 +solar: looted stackable items should distribute properly now. + +==3/05/2004 +solar: player armor will appear the proper color now +solar: LoY Dye works. You need a vial of prismatic dye for each slot you + want to change the color of. +solar: looting differentiates between left and right clicks. left clicking + will put the item on your cursor as expected, and right clicking will + auto equip it. +solar: containers can now be auto equip looted + +==3/02/2004 +solar: implemented AE location spells. ex: rain of lava, sentinel. small + issue with this still; the caster continues to execute the casting + animation for the duration of the spell. + +==3/01/2004 +solar: started implementing AE rain type spells, still some work left to do. + There are 2 new files for a Beacon class that you have to add to + the build. + +==2/29/2004 +solar: revised and corrected a ton of spell effect stuff +solar: corrected an hp regen issue, but need to get the proper calculations + for regen rates. until corrected, clients will see their hp regen + and then immediately change to what the server thinks it should be +solar: probably broke something + +==2/28/2004 +solar: spells will now consume reagents properly +solar: lifts work again +solar: corrected a mistake that caused spells to not finish scribing properly +solar: fixed some memory leak issues regarding hp updates + +==2/25/2004 +Scorpious2k: Added vesuvias changes for 5.5 - luclin appearence should work now + +==2/22/2004 +solar: spells should be in working order again, more work to be done on this. + +==2/20/2004 +solar: merged vesuvias' appearance (face, beard etc) and zone change + changes from the old code. + +==2/19/2004 +kathgar: Fixed, SE_Fear, SE_SpinStun, SE_Charm for fixed duration spells and not double checking breaking + Range check on Archery + Skill checks on Hide and Sneak + Fixed exp deaths in PvP by DoTs cast by clients, spells cast by clients no longer in zone, and client pets + Added member bool client to Buff_Struct to aid in above fix + Fixed solar's changelog entry where he spelled my name wrong + No EQLive fixes in this change +solar: fixed a bug that was causing people to be set as gm when they shouldn't + +==2/15/2004 +solar: characters being created are checked for validity now. thanks to + kathgar for the tables. + +==2/14/2004 +Zaphod: Added Door ZonePoint information. + +==2/13/2004 +solar: Doors should work now +solar: adventure merchant stuff fully working (check me) +solar: petition interface stuff believed to be working but not fully tested +LethalEncounter: (entered by solar) fixed item opcode, items fully working + +==2/12/2004 +solar: Opcodes updated from Zaphod's list, also a few others. All the ops + that are tabbed in are unconfirmed, and I marked unused ones also. + +==2/10/2004 +Image: NewSource directory created for 0.5.5 (EQLive Compatability Developement) +Image: Few opcodes updated, also implicitlen updated. + +==2/8/2004 +solar: fixed a world crash regarding deleting an empty character slot + +==2/7/2004 +solar: AE spells working properly now +solar: wrote all new buff duration formulas +solar: buffs being applied will properly displace multiple buffs if needed +solar: bard spells should keep casting while sitting now +solar: mobs should now have a proper casting animation when they cast + +==2/6/2004 +solar: some buff stacking changes, misc spell stuff + +==2/5/2004 +solar: more spell work, bards should be in a working state, but not done. + +==2/3/2004 +Image: Divine Aura now works cross zone. +Image: Invisibility now works cross zone. +Image: Added a zone(""); command to quest (Zones them to the specified short zone name) +Image: Added selfcast("spellid"); command to quest (Makes client cast spells on themself, good for self only spells) +Image: Tons of my personal GW stuff changed, you can't see bwuhahahhah +Image: Fixed platinum duping +Image: Levitate now works cross zone (others see you levitate and not warping) + +==2/2/2004 +Scorpious2k: added quest global variables (requires DB change see quest_globals.txt in EQEmu Release)) +Scorpious2k: fixed faction command in parser + +==2/2/2004 +solar: worked on buff stacking and some misc spell stuff. + +==2/1/2004 +solar: group spell fixes +solar: implemented /targetgroupbuff + +==1/31/2004 +solar: revised the StringID based messaging. + +==1/30/2004 +solar: Merged in Wiz's CheckLos() mob method +solar: Spell casting fixes. +solar: Channeling skill should work very similar to live now. If you end + your casting where you started you have a chance to finish casting. +solar: Line of sight is checked for spells. + +==1/29/2004 +LethalEncounter: Fixed an issue with HT and LoH not repopping, apparently fatigue in player profile was moved into the timer hours slot. +solar: Updated death. You shouldn't receive an extra message about being + punched when you die to a spell anymore. + +==1/28/2004 +solar: fixed an item dupe bug. + +==1/26/2004 +solar: various fixes to spells, more coming. + +==1/25/2004 +LethalEncounter: Fixed some issues with the guilds. +LethalEncounter: No Rent items will now be deleted if your offline for more than 30 minutes. + +==1/24/2004 +LethalEncounter: The guild management tool has been updated and works again. +LethalEncounter: Guilds will now work almost 100% like live, only a few of the #guild commands are left over, mainly for gm management. +kathgar: Added #revoke and #oocmute +LethalEncounter: Fixed some resist issues with some spells. +LethalEncounter: Fixed item charges from merchants. +LethalEncounter: Fixed expansion issues like not being able to use Adventure merchants. +solar: bodytypes should be working correctly from the db now. Also added it + to #spawn and #npcstats. Pets are all summoned, except necro which + are summoned and undead. Apply the bodytype_update.sql in release + to flag your mobs. +solar: /surname command corrects capitalization and rejects non alpha now. +solar: #name fixed - renames player target +solar: made some fixes to kathgar's revoke patch - it now works as intended. + +==1/23/2004 +solar: LD clients should now be kicked when the LD timer is up, even if engaged. +solar: Buffs should be working correctly after zoning now. +solar: #gassign fixes + +==1/22/2004 +solar: Corpses are now sent to clients zoning in. + +==1/21/2004 +Trumpcard: Merged in mongrels fix for undead/invis. Use update sql provided in release to update. +solar: Buff fading should work for slots besides the first one now. This will + fix the 'mez bug' that was due to the icon not being stripped. + +==1/20/2004= +solar: sense heading skill will now improve as you move around + +==1/19/2004= +Scorpious2k: fixed faction command for quests +LethalEncounter: Fixed a problem with queued cursor items. +solar: fixes to #gassign + +==1/18/2004= +LethalEncounter: Added item and spell bonuses to the mana regen formulas, they will now help. +LethalEncounter: Fixed a bug that was killing players even when they had hitpoints remaining. +LethalEncounter: Added server side filters +LethalEncounter: Added a new command (#setstat) that allows you to set a characters stats. +LethalEncounter: Added a new quest command that allows you to modify a characters stats. +LethalEncounter: Added the ability for a player to have more than one item on their cursor at once. They will be queued like on live. +Scorpious2k: added random name generator for char create +Scorpious2k: changed order of parameters in flag console command to allow for spaces in name +kathgar: Added FLAG_COMBINED and FLAG_IMPLICIT support, define COMBINED to try it +solar: fixed #mana +solar: fixed mana disappearing when you're interrupted mid cast +solar: applied kathgar's PetCommand crash fix +solar: fixed a pvp damage crash +solar: fixed channeling skill rolling over back to 1 +solar: bumped PVP spell damage to 2/3 from 1/2 of normal + +==1/17/2004== +Image: Memleaks for LDTimer, stamina_timer, CreateHPPacket have been fixed. +Image: World server now boots people with status < 0. +solar: Reworked melee hit/miss calculations to work better + +==1/16/2004== +solar: Mez spells break properly on players now +solar: added a #stun command, takes a duration from 1 to 65535 +solar: Fixed mana disappearing when a spell like cannibalize or necro + subversion is cast. +solar: Fixed incorrect conditional that was causing ALL damage done TO a + client to be halved. Now only client to client damage is halved. +solar: Fixed a bug with the spell cast timer bar disappearing prematurely + and the spell gems readying themselves while a spell was still being + cast. +solar: Spells should interrupt properly when you duck, or are otherwise + interrupted while casting. + +==1/15/2004== +solar: HP should regen properly now + +==1/14/2004== +solar: Divine Aura spells should now work + +==1/10/2004== +LethalEncounter: Added ability for groups to zone. +LethalEncounter: Added the ability to use snares and roots in pvp. +LethalEncounter: Fixed mob movement to be more like live and to work correctly. +LethalEncounter: Added the tracking skill. +LethalEncounter: You will now get hit for max damage when you are sitting and an npc attacks. +LethalEncounter: Fixed loot messages. +LethalEncounter: Fixed loot so right click autoequips correctly now. + +==1/4/04== +MYRA: changed to new opcode for follow +MYRA: changed to new opcode for taunt +MYRA: use new (5.x) Status labels in who for telnet connection +MYRA: Added code to depop at end of grid for wander type 4 +MYRA: Added wander type 4 (single run) +MYRA: fixed eval in ExportVar per Eglin +MYRA: corrected spelling for var $uguildrank for event_timer (was $uguildrang) +MYRA: added vars $status & $cumflag per Eglin +MYRA: added vars $mobid & $mlevel per Eglin +MYRA: added missing commands + itemlink to perl +MYRA: added EVAL & KEEPERR to eval per Eglin's recommendation +MYRA: restore missing commands for qst type files & add itemlink +MYRA: fixed comma bug for me command +MYRA: fixed comma bug for echo command +MYRA: fixed comma bug for say command +MYRA: fixed comma bug for emote command +MYRA: fixed comma bug for shout command +MYRA: added itemlink(ItemNumber) command + + +==1/2/04== +LethalEncounter: Added further support for eqstr_us.txt, this should cut down on lag. +Image: Fixed client lockup related to spells. aka 'spell sticking'. + +==1/1/04== +LethalEncounter: Updated spell structure to new spells_us.txt format. NOTE: You must have the latest eqlive spells_us.txt in your EQEmu directory. + +==12/21/03 through 1/1/04== +Image/LethalEncounter/kathgar/devn00b/Shawn319: + +Updated/added several opcodes to match eqlive. + +Fixed spell interrupt casting. + +Fixed NPC and player HP updates; should be pretty accurate now and cause less packet loss. + +Fixed major crash bug with player death. + +Fixed crash bugs related to Throwing weapons, Archery, Critical hits, Kick, Assassination and Beastlord pets. + +Added more spells to the 'restricted list' of #cast. + +Fixed flaw in #flag code. Only status 200+ should be able to set an accounts flag now. + +Fixed crash bug with #time. + +Pets now face the same heading as their owner. + +Players no longer land critcal hits below 1 (no more 0 or negative hits). + +Server no longer counts item stats in the ammo slot (as it should). + +Fixed bug with eventlog that would cause certain entries to return a MySQL syntax error. + + +==12/21/03== +LethalEncounter: Ive been busy the last few days updating all the opcodes and structs for the recent patch. Nearly everything works again please post any remaining unknown opcodes to the development forum. + +==12/16/03== +Image: Fixed #zsave and added #zunderworld so you can set the zcoord for underworld. +==12/15/03== +Image: Took out an HP update in NPC::Damage() reduces HP updates big time and kills network traffic. +Image: All HP Updates are done only to targets now, TODO: Queue group clients with HP updates (once they work again) +==12/14/03== +LethalEncounter: Fixed a bug in tradeskills that was clearing the container before it was telling the client to. +LethalEncounter: Fixed the opcode for mend. +LethalEncounter: Fixed the guild trainer messages. +LethalEncounter: Fixed char create so it creates the character with a new aa struct. +LethalEncounter: Important fix for mob movement via waypoints and such. Mobs now move nearly exactly like live and it uses 20 times less bandwidth than before. + +==12/13/03== +LethalEncounter: Fixed Environmental Damage +LethalEncounter: Added support for a new database variable called startzone. + To use this, add a record called startzone to the variables table and make the value the zone shortname you want players to start in. This will force all players to start in that zone. +LethalEncounter: Fixed a bug that Shawn found where /zone wouldnt work correctly in zones with similiar names. + +==12/12/03== +Shawn319: (Bud9weiser) Fixed start_zones bug. Players should now start in correct location as long as db is up to date. +LethalEncounter: Item tint fix. +LethalEncounter: Tradeskill items being deleted without correct recipe fix. +LethalEncounter: Problem with the ' character inserting into the bug table fixed. + +==12/11/03== +LethalEncounter: Added Archery in for pvp. +LethalEncounter: Fixed a throw bug. +LethalEncounter: Fixed char select colors not matching your colors in game. + +==12/10/03== +LethalEncounter: Fixed the weapon delay bug. Special thanks to haecz for bring this to our attention. +LethalEncounter: Fixed dup money bug. Special thanks to afrospy also for bring this to our attention. +LethalEncounter: Fixed Bazaar Traders, they should work correctly. + +==12/09/03== +LethalEncounter: Fixes for item tints and dyes. +LethalEncounter: Fixed NPC textures finally. + +==12/08/03== +LethalEncounter: Fixed Horses/Drogmors. + +==12/07/03== +LethalEncounter: Fixed a bug with pets causing lag. +LethalEncounter: Fixed a crash bug/1017 bug related to login in, people on slow/lagged connections should be able to login fairly +consistently now. + +==12/02/03== +LethalEncounter: Trumpcard and I completely rewrote the linked lists to improve lag, they are about 4 times more efficient now. +LethalEncounter: Fixed a small bug with zone points. BE SURE YOUR USING THE MOST UP-TO-DATE TABLE!! + +==11/30/03== +LethalEncounter: Fixed various loot bugs, including the duplicate lore item, blah blah blah bug.. +LethalEncounter: Fixed #zonestatus and /servers to be on multiple lines +LethalEncounter: Fixed /who all guild tags to display the right guild +LethalEncounter: Added the ability to send multi-lined emotes with the
newline. +LethalEncounter: Fixed the bug that gave the message: "Multiplier == 0 in Client::CalcBaseHP" + +==11/29/03== +Image: Fixed last names for clients (When a new client zoned in, didn't send lastname). + +==11/28/03== +LethalEncounter: Added a default instance for pets that arent created via legit spells. +LethalEncounter: Fixed the spell scribe issues. + +==11/25/03== +LethalEncounter: Added a new table called ground_spawns. This will allow you to spawn items randomly throughout the zone. Use this for only random locations, use the object table for static ones. +LethalEncounter: Added Timers for ground spawns so they will respawn based on the time you set. + +==11/24/03== +Image: Fixed zoning and deaths so you no longer drop from the server. +Image: Properly enabled LDoN on EQEmu. +Image: Basic Adventure Merchant code implemented, throwing it to the STL guys to properly optimize. +LethalEncounter: Fixed Tradeskill objects. If the objects show up as bags, thats a db issue + +==11/23/03== +Trumpcard: Several zone performance enhancements, as well as changes to decrease combat lag. +LethalEncounter: Reworked pets, they should now work properly. + +==11/21/03== +LethalEncounter: Merchants wont trade with people while they are fighting now. +LethalEncounter: Players can no longer attack while invulnerable. +LethalEncounter: Fixed an AA bug that gave the player 199 aa points. + +==11/18/03== +LethalEncounter: Lotsa cool fixes like crash fixes and guild fixes and cool stuff like that + +==11/17/03== +LethalEncounter: Finally got /who all done and *hopefully* free of bugs. +LethalEncounter: Fixed a crash bug with guilds and added some of the Guildmanagement features. + +==11/16/03== +Trumpcard: Several deallocate and mem leak errors corrected. +solar: Fixed bug with mobs assisting entities they shouldn't. Often this + show itself with other mobs helping the player when the player + attacked one. +LethalEncounter: Added in the GuildManagement tool. You will need 2 new columns in your character_ table for this. +LethalEncounter: Fixed a couple crash bugs with guilds and spells. + +==11/15/03== +solar: Fixed a bug in commands where if you didn't define an accesslevel for a command + in the addon.ini it would default to accesslevel 0 instead of the hardcoded default. +solar: Modified #help command to search for a partial command specified as argument. +solar: HP wasn't being updated to client properly and would fall out of sync + +==11/14/03== +LethalEncounter: Fixed a bug in doors that would cause triggered doors to go into an endless loop and crash. + +==11/13/03== +kathgar: Fixed a crash when calling ZSList::FindByZoneID() when sending an invalid zone number. + Stack was corrupt in the backtrace, so I am not sure what called it in this way. +LethalEncounter: Fixed animation bug with attack. +LethalEncounter: Fixed Tradeskills (again) +LethalEncounter: Character Select will now show your equipment (Existing characters will need to swap the items) +LethalEncounter: NPCs will now equip weapons and items from the db. +LethalEncounter: You can now give weapons and items to NPCs to equip. + +==11/12/03== +LethalEncounter: Fixed skills, we had the struct one int32 off. +LethalEncounter: Fixed GM Trainers, they should now work as intended. + +==11/11/03== +Doodman: Fixed zone crash in CheckCloseArrgo() during assist checking based on +flawed if logic. + +==11/11/03== +LethalEncounter: Fixed Stamina issues. + +==11/10/03== +LethalEncounter: Fixed Quests. +LethalEncounter: A quest fix that makes them use far less memory, up to 25 MB per computer + +==11/09/03== +LethalEncounter: Fixed zoning, zone_points should now work correctly, but make sure you have the latest ones in zone_points! +LethalEncounter: Fixed problem with spells not scribing. +LethalEncounter: Fixed zoning bug where you zoned back into the same zone, but it looked like you were in the new zone. + +==11/06/03== +Image: Guildwars code and fixed a bug in MobAI for NPCs assisting clients, clients do not use +IsEngaged(), so it always returned false, now it uses AutoAttackEnabled() in its place for clients. +Image: Reversed the AICheckCloseSpells if statements, now sanity checks before distance check, uses less CPU usage. + +==11/05/03== +LethalEncounter: Updated all of the opcodes that were changed in the patch today. +LethalEncounter: Refined AA's some, added table to hold the timers for AA's so users can exploit them. Look in db.sql for the table. + + +==11/04/03== +LethalEncounter: Added in the basic structure for AA's and got some of them working. + +==11/03/03== +LethalEncounter: Fixed factions. +LethalEncounter: Fixed issues with mobs of different factions or no faction assisting. + +==10/29/03 to 11/02/03== +LethalEncounter: Fixed X, Y coords. +LethalEncounter: Added in groups. +LethalEncounter: Added in trades. +LethalEncounter: Fixed various animations. +LethalEncounter: Fixed client movement. +LethalEncounter: Fixed NPC movement. +LethalEncounter: Fixed forage. +LethalEncounter: Fixed Attack. +LethalEncounter: Fixed GM summon. +LethalEncounter: Fixed GM goto. +LethalEncounter: Fixed spells. +LethalEncounter: Fixed lifts. +LethalEncounter: Fixed automatic weather changes. Change the default in the zone table to suit your needs. 1: normal 2: Rainy most of the time 3: Snowing most of the time +LethalEncounter: Fixed lotsa other important stuff that I cant remember :) + +==10/29/03== +devn00b: Yet again removed Faulty pet code. LEAVE THEM ALONE + +==10/17/03== +Image: Zone points code put in, need some testing done on it please! + +==10/15/03== +Image: More optimization to the aggro code and an infinite for loop fixed. + +==10/14/03== +kathgar: Removed deprecated guildwars code + Added #ifdefs for IPC code, no more IsInteractive checks wasting cycles + Fixed a memleak in NPC with attacked_timer + +merth: Updated zone objects: + * Uses new item architecture + * Fully supports imported data from packet collector + * Objects dropped to ground persist zone sessions + +==10/13/03== +Image: Optimizations to Aggro code, ~10% more efficient according to Trumpcard. + +==10/11/03== +Trumpcard: Lots of performance improvements to combat code/AI code. Streamlined several calls, and found and removed unneeded calls + in MobAI that were causing serious bottlenecks. Went through and caught a few memory leaks, and converted + many deletes to safe deletes. + +==10/10/03== +LethalEncounter: Somewhere around this date I added in bazaar traders and cool features like that. + +==10/8/03== +Trumpcard: Starting items are now in. LE also fixed the combat damage struct and mob hp bar updates. + +==10/8/03== +scruffy: Inverted the XY coordinates system to work like the EQLive system. Run invertxy.sql to update existing databases. + +==10/6/03== +kathgar: Added opcodes for things such as the LFG tool from SEQ, replaced some instances of static opcode use + +==10/5/03== +devn00b: Removed Faulty pet code, replaced with working code. + +==09/24/03== +Image: Added the functions for the GuildWars point system. + +==8/10/03== +devn00b: Moved pets from hardcoded to DB based. need to source pets.sql + +============ diff --git a/cmake/FindMySQL.cmake b/cmake/FindMySQL.cmake new file mode 100644 index 000000000..b4b65900c --- /dev/null +++ b/cmake/FindMySQL.cmake @@ -0,0 +1,85 @@ +# - Find mysqlclient +# +# -*- cmake -*- +# +# Find the native MySQL includes and library +# +# MySQL_INCLUDE_DIR - where to find mysql.h, etc. +# MySQL_LIBRARIES - List of libraries when using MySQL. +# MySQL_FOUND - True if MySQL found. +# The following can be used as a hint as to where to search: +# MYSQL_ROOT + +IF (MySQL_INCLUDE_DIR AND MySQL_LIBRARIES) + # Already in cache, be silent + SET(MySQL_FIND_QUIETLY TRUE) +ENDIF (MySQL_INCLUDE_DIR AND MySQL_LIBRARIES) + +# Include dir +IF(MYSQL_ROOT) + FIND_PATH(MySQL_INCLUDE_DIR + NAMES mysql.h + PATHS ${MYSQL_ROOT}/include + PATH_SUFFIXES mysql + ) +ELSE(MYSQL_ROOT) + FIND_PATH(MySQL_INCLUDE_DIR + NAMES mysql.h + PATH_SUFFIXES mysql + ) +ENDIF(MYSQL_ROOT) + +# Library +SET(MySQL_NAMES mysqlclient_r mysqlclient) +IF(MYSQL_ROOT) + FIND_LIBRARY(MySQL_LIBRARY_DEBUG + NAMES ${MySQL_NAMES} + PATHS ${MYSQL_ROOT}/lib/debug /usr/lib /usr/local/lib /usr/lib64 /usr/local/lib64 + PATH_SUFFIXES mysql + ) + + FIND_LIBRARY(MySQL_LIBRARY_RELEASE + NAMES ${MySQL_NAMES} + PATHS ${MYSQL_ROOT}/lib /usr/lib /usr/local/lib /usr/lib64 /usr/local/lib64 + PATH_SUFFIXES mysql + ) +ELSE(MYSQL_ROOT) + FIND_LIBRARY(MySQL_LIBRARY_DEBUG + NAMES ${MySQL_NAMES} + PATHS /usr/lib /usr/local/lib /usr/lib64 /usr/local/lib64 + PATH_SUFFIXES mysql + ) + + FIND_LIBRARY(MySQL_LIBRARY_RELEASE + NAMES ${MySQL_NAMES} + PATHS /usr/lib /usr/local/lib /usr/lib64 /usr/local/lib64 + PATH_SUFFIXES mysql + ) +ENDIF(MYSQL_ROOT) + +IF (MySQL_INCLUDE_DIR AND MySQL_LIBRARY_DEBUG AND MySQL_LIBRARY_RELEASE) + SET(MySQL_FOUND TRUE) + SET( MySQL_LIBRARIES ${MySQL_LIBRARY_DEBUG} ${MySQL_LIBRARY_RELEASE} ) +ELSE (MySQL_INCLUDE_DIR AND MySQL_LIBRARY_DEBUG AND MySQL_LIBRARY_RELEASE) + SET(MySQL_FOUND FALSE) + SET( MySQL_LIBRARIES ) +ENDIF (MySQL_INCLUDE_DIR AND MySQL_LIBRARY_DEBUG AND MySQL_LIBRARY_RELEASE) + + +# handle the QUIETLY and REQUIRED arguments and set MySQL_FOUND to TRUE if +# all listed variables are TRUE +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(MySQL DEFAULT_MSG MySQL_LIBRARY_DEBUG MySQL_LIBRARY_RELEASE MySQL_INCLUDE_DIR) + +IF(MySQL_FOUND) + SET( MySQL_LIBRARIES ${MySQL_LIBRARY_DEBUG} ${MySQL_LIBRARY_RELEASE} ) +ELSE(MySQL_FOUND) + SET( MySQL_LIBRARIES ) +ENDIF(MySQL_FOUND) + +MARK_AS_ADVANCED( + MySQL_LIBRARY_DEBUG + MySQL_LIBRARY_RELEASE + MySQL_INCLUDE_DIR + ) + \ No newline at end of file diff --git a/common/BasePacket.cpp b/common/BasePacket.cpp new file mode 100644 index 000000000..2717ee354 --- /dev/null +++ b/common/BasePacket.cpp @@ -0,0 +1,146 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "debug.h" +#include "BasePacket.h" +#include "misc.h" +#include "packet_dump.h" + + + +BasePacket::BasePacket(const unsigned char *buf, uint32 len) +{ + this->pBuffer=NULL; + this->size=0; + this->_wpos = 0; + this->_rpos = 0; + if (len>0) { + this->size=len; + pBuffer= new unsigned char[len]; + if (buf) { + memcpy(this->pBuffer,buf,len); + } else { + memset(this->pBuffer,0,len); + } + } +} + +BasePacket::~BasePacket() +{ + if (pBuffer) + delete[] pBuffer; + pBuffer=NULL; +} + + +void BasePacket::build_raw_header_dump(char *buffer, uint16 seq) const +{ + if (timestamp.tv_sec) { + char temp[20]; + strftime(temp,20,"%F %T",localtime((const time_t *)×tamp.tv_sec)); + buffer += sprintf(buffer, "%s.%06lu ",temp,timestamp.tv_usec); + } + if (src_ip) { + string sIP,dIP;; + sIP=long2ip(src_ip); + dIP=long2ip(dst_ip); + buffer += sprintf(buffer, "[%s:%d->%s:%d]\n",sIP.c_str(),src_port,dIP.c_str(),dst_port); + } + if (seq != 0xffff) + buffer += sprintf(buffer, "[Seq=%u] ",seq); +} + +void BasePacket::DumpRawHeader(uint16 seq, FILE *to) const +{ + char buff[128]; + build_raw_header_dump(buff, seq); + fprintf(to, "%s", buff); +} + +void BasePacket::build_header_dump(char *buffer) const +{ + sprintf(buffer, "[packet]\n"); +} + +void BasePacket::DumpRawHeaderNoTime(uint16 seq, FILE *to) const +{ + if (src_ip) { + string sIP,dIP;; + sIP=long2ip(src_ip); + dIP=long2ip(dst_ip); + fprintf(to, "[%s:%d->%s:%d] ",sIP.c_str(),src_port,dIP.c_str(),dst_port); + } + if (seq != 0xffff) + fprintf(to, "[Seq=%u] ",seq); +} + +void BasePacket::DumpRaw(FILE *to) const +{ + DumpRawHeader(); + if (pBuffer && size) + dump_message_column(pBuffer, size, " ", to); + fprintf(to, "\n"); +} + +void BasePacket::ReadString(char *str, uint32 Offset, uint32 MaxLength) const +{ + uint32 i = 0, j = Offset; + + do + { + str[i++] = pBuffer[j++]; + } + while((j < size) && (i < MaxLength) && (str[i - 1] != 0)); + + str[i - 1] = '\0'; +} + +void DumpPacketHex(const BasePacket* app) +{ + DumpPacketHex(app->pBuffer, app->size); +} + +void DumpPacketAscii(const BasePacket* app) +{ + DumpPacketAscii(app->pBuffer, app->size); +} + +void DumpPacketBin(const BasePacket* app) { + DumpPacketBin(app->pBuffer, app->size); +} + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/BasePacket.h b/common/BasePacket.h new file mode 100644 index 000000000..612689600 --- /dev/null +++ b/common/BasePacket.h @@ -0,0 +1,93 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 BASEPACKET_H_ +#define BASEPACKET_H_ + +#include "types.h" +#include +#include +#include + +#ifdef WIN32 + #include + #include + #include +#else + #include + #include +#endif + +class BasePacket { +public: + unsigned char *pBuffer; + uint32 size, _wpos, _rpos; + uint32 src_ip,dst_ip; + uint16 src_port,dst_port; + uint32 priority; + timeval timestamp; + + virtual void build_raw_header_dump(char *buffer, uint16 seq=0xffff) const; + virtual void build_header_dump(char *buffer) const; + virtual void DumpRawHeader(uint16 seq=0xffff, FILE *to = stdout) const; + virtual void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const; + void DumpRaw(FILE *to = stdout) const; + + void setSrcInfo(uint32 sip, uint16 sport) { src_ip=sip; src_port=sport; } + void setDstInfo(uint32 dip, uint16 dport) { dst_ip=dip; dst_port=dport; } + void setTimeInfo(uint32 ts_sec, uint32 ts_usec) { timestamp.tv_sec=ts_sec; timestamp.tv_usec=ts_usec; } + void copyInfo(const BasePacket *p) { src_ip=p->src_ip; src_port=p->src_port; dst_ip=p->dst_ip; dst_port=p->dst_port; timestamp.tv_sec=p->timestamp.tv_sec; timestamp.tv_usec=p->timestamp.tv_usec; } + + inline bool operator<(const BasePacket &rhs) { + return (timestamp.tv_sec < rhs.timestamp.tv_sec || (timestamp.tv_sec==rhs.timestamp.tv_sec && timestamp.tv_usec < rhs.timestamp.tv_usec)); + } + + void WriteUInt8(uint8 value) { *(uint8 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint8); } + void WriteUInt32(uint32 value) { *(uint32 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint32); } + void WriteUInt64(uint64 value) { *(uint64 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint64); } + void WriteUInt16(uint32 value) { *(uint16 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint16); } + void WriteSInt32(int32 value) { *(int32 *)(pBuffer + _wpos) = value; _wpos += sizeof(int32); } + void WriteFloat(float value) { *(float *)(pBuffer + _wpos) = value; _wpos += sizeof(float); } + void WriteDouble(double value) { *(double *)(pBuffer + _wpos) = value; _wpos += sizeof(double); } + void WriteString(const char * str) { uint32 len = static_cast(strlen(str)) + 1; memcpy(pBuffer + _wpos, str, len); _wpos += len; } + + uint8 ReadUInt8() { uint8 value = *(uint8 *)(pBuffer + _rpos); _rpos += sizeof(uint8); return value; } + uint8 ReadUInt8(uint32 Offset) const { uint8 value = *(uint8 *)(pBuffer + Offset); return value; } + uint32 ReadUInt32() { uint32 value = *(uint32 *)(pBuffer + _rpos); _rpos += sizeof(uint32); return value; } + uint32 ReadUInt32(uint32 Offset) const { uint32 value = *(uint32 *)(pBuffer + Offset); return value; } + void ReadString(char *str) { uint32 len = static_cast(strlen((char *)(pBuffer + _rpos))) + 1; memcpy(str, pBuffer + _rpos, len); _rpos += len; } + void ReadString(char *str, uint32 Offset, uint32 MaxLength) const; + + uint32 GetWritePosition() { return _wpos; } + uint32 GetReadPosition() { return _rpos; } + void SetWritePosition(uint32 Newwpos) { _wpos = Newwpos; } + void SetReadPosition(uint32 Newrpos) { _rpos = Newrpos; } + +protected: + virtual ~BasePacket(); + BasePacket() { pBuffer=NULL; size=0; _wpos = 0; _rpos = 0; } + BasePacket(const unsigned char *buf, const uint32 len); +}; + +extern void DumpPacketHex(const BasePacket* app); +extern void DumpPacketAscii(const BasePacket* app); +extern void DumpPacketBin(const BasePacket* app); + +#endif /*BASEPACKET_H_*/ + + + diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 000000000..89e78d893 --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,313 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +SET(common_sources + BasePacket.cpp + classes.cpp + Condition.cpp + crash.cpp + CRC16.cpp + crc32.cpp + database.cpp + dbasync.cpp + dbcore.cpp + DBMemLeak.cpp + debug.cpp + emu_opcodes.cpp + EMuShareMem.cpp + EmuTCPConnection.cpp + EmuTCPServer.cpp + EQDB.cpp + EQDBRes.cpp + EQEmuConfig.cpp + EQEMuError.cpp + EQPacket.cpp + EQStream.cpp + EQStreamFactory.cpp + EQStreamIdent.cpp + EQStreamProxy.cpp + eqtime.cpp + extprofile.cpp + guild_base.cpp + guilds.cpp + Item.cpp + logsys.cpp + logsys_eqemu.cpp + md5.cpp + misc.cpp + MiscFunctions.cpp + moremath.cpp + Mutex.cpp + opcode_map.cpp + opcodemgr.cpp + packet_dump.cpp + packet_dump_file.cpp + packet_functions.cpp + perl_EQDB.cpp + perl_EQDBRes.cpp + ProcLauncher.cpp + ptimer.cpp + races.cpp + rdtsc.cpp + rulesys.cpp + serverinfo.cpp + shareddb.cpp + SharedLibrary.cpp + StructStrategy.cpp + TCPConnection.cpp + TCPServer.cpp + timeoutmgr.cpp + timer.cpp + unix.cpp + worldconn.cpp + XMLParser.cpp + platform.cpp + patches/Client62.cpp + patches/patches.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 + SocketLib/HttpdForm.cpp + SocketLib/HttpdSocket.cpp + SocketLib/HTTPSocket.cpp + SocketLib/MemFile.cpp + SocketLib/Mime.cpp + SocketLib/Parse.cpp + SocketLib/socket_include.cpp + SocketLib/Utility.cpp + StackWalker/StackWalker.cpp + tinyxml/tinystr.cpp + tinyxml/tinyxml.cpp + tinyxml/tinyxmlerror.cpp + tinyxml/tinyxmlparser.cpp +) + +SET(common_headers + BasePacket.h + bodytypes.h + breakdowns.h + classes.h + common_profile.h + Condition.h + crash.h + CRC16.h + crc32.h + database.h + dbasync.h + dbcore.h + DBMemLeak.h + debug.h + deity.h + emu_opcodes.h + emu_oplist.h + EMuShareMem.h + EmuTCPConnection.h + EmuTCPServer.h + eq_constants.h + eq_opcodes.h + eq_packet_structs.h + EQDB.h + EQDBRes.h + EQEmuConfig.h + EQEmuConfig_elements.h + EQEMuError.h + EQPacket.h + EQStream.h + EQStreamFactory.h + EQStreamIdent.h + EQStreamIntf.h + EQStreamLocator.h + EQStreamProxy.h + EQStreamType.h + eqtime.h + errmsg.h + extprofile.h + guild_base.h + guilds.h + Item.h + item_fieldlist.h + item_struct.h + languages.h + linked_list.h + logsys.h + logtypes.h + mail_oplist.h + md5.h + misc.h + MiscFunctions.h + moremath.h + Mutex.h + op_codes.h + opcode_dispatch.h + opcodemgr.h + packet_dump.h + packet_dump_file.h + packet_functions.h + ProcLauncher.h + profiler.h + ptimer.h + queue.h + races.h + rdtsc.h + rulesys.h + ruletypes.h + seperator.h + serverinfo.h + servertalk.h + shareddb.h + SharedLibrary.h + skills.h + StructStrategy.h + TCPBasicServer.h + TCPConnection.h + TCPServer.h + timeoutmgr.h + timer.h + types.h + unix.h + useperl.h + version.h + worldconn.h + XMLParser.h + ZoneNumbers.h + platform.h + patches/Client62.h + patches/Client62_itemfields.h + patches/Client62_ops.h + patches/Client62_structs.h + patches/patches.h + patches/SoD.h + patches/SoD_itemfields.h + patches/SoD_ops.h + patches/SoD_structs.h + patches/SoF.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_itemfields.h + patches/RoF_ops.h + patches/RoF_structs.h + patches/Titanium.h + patches/Titanium_itemfields.h + patches/Titanium_ops.h + patches/Titanium_structs.h + patches/Underfoot.h + patches/Underfoot_itemfields.h + patches/Underfoot_ops.h + patches/Underfoot_structs.h + SocketLib/Base64.h + SocketLib/File.h + SocketLib/HttpdCookies.h + SocketLib/HttpdForm.h + SocketLib/HttpdSocket.h + SocketLib/HTTPSocket.h + SocketLib/IFile.h + SocketLib/MemFile.h + SocketLib/Mime.h + SocketLib/Parse.h + SocketLib/socket_include.h + SocketLib/Utility.h + StackWalker/StackWalker.h + tinyxml/tinystr.h + tinyxml/tinyxml.h +) + +SOURCE_GROUP(Patches FILES + patches/Client62.h + patches/Client62_itemfields.h + patches/Client62_ops.h + patches/Client62_structs.h + patches/patches.h + patches/SoD.h + patches/SoD_itemfields.h + patches/SoD_ops.h + patches/SoD_structs.h + patches/SoF.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_itemfields.h + patches/RoF_ops.h + patches/RoF_structs.h + patches/Titanium.h + patches/Titanium_itemfields.h + patches/Titanium_ops.h + patches/Titanium_structs.h + patches/Underfoot.h + patches/Underfoot_itemfields.h + patches/Underfoot_ops.h + patches/Underfoot_structs.h + patches/Client62.cpp + patches/patches.cpp + patches/SoD.cpp + patches/SoF.cpp + patches/RoF.cpp + patches/Titanium.cpp + patches/Underfoot.cpp +) + +SOURCE_GROUP(SocketLib FILES + SocketLib/Base64.h + SocketLib/File.h + SocketLib/HttpdCookies.h + SocketLib/HttpdForm.h + SocketLib/HttpdSocket.h + SocketLib/HTTPSocket.h + SocketLib/IFile.h + SocketLib/MemFile.h + SocketLib/Mime.h + SocketLib/Parse.h + SocketLib/socket_include.h + SocketLib/Utility.h + SocketLib/Base64.cpp + SocketLib/File.cpp + SocketLib/HttpdCookies.cpp + SocketLib/HttpdForm.cpp + SocketLib/HttpdSocket.cpp + SocketLib/HTTPSocket.cpp + SocketLib/MemFile.cpp + SocketLib/Mime.cpp + SocketLib/Parse.cpp + SocketLib/socket_include.cpp + SocketLib/Utility.cpp +) + +SOURCE_GROUP(StackWalker FILES + StackWalker/StackWalker.h + StackWalker/StackWalker.cpp +) + +SOURCE_GROUP(TinyXML FILES + tinyxml/tinystr.h + tinyxml/tinyxml.h + tinyxml/tinystr.cpp + tinyxml/tinyxml.cpp + tinyxml/tinyxmlerror.cpp + tinyxml/tinyxmlparser.cpp +) + +INCLUDE_DIRECTORIES(Patches SocketLib StackWalker TinyXML) + +ADD_LIBRARY(Common ${common_sources} ${common_headers}) + + +IF(UNIX) + ADD_DEFINITIONS(-fPIC) + 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 ../Bin) diff --git a/common/CRC16.cpp b/common/CRC16.cpp new file mode 100644 index 000000000..6a6f44078 --- /dev/null +++ b/common/CRC16.cpp @@ -0,0 +1,346 @@ +#include + +unsigned long IntArray[]={ +0x00000000, +0x77073096, +0xEE0E612C, +0x990951BA, +0x076DC419, +0x706AF48F, +0xE963A535, +0x9E6495A3, +0x0EDB8832, +0x79DCB8A4, +0xE0D5E91E, +0x97D2D988, +0x09B64C2B, +0x7EB17CBD, +0xE7B82D07, +0x90BF1D91, +0x1DB71064, +0x6AB020F2, +0xF3B97148, +0x84BE41DE, +0x1ADAD47D, +0x6DDDE4EB, +0xF4D4B551, +0x83D385C7, +0x136C9856, +0x646BA8C0, +0xFD62F97A, +0x8A65C9EC, +0x14015C4F, +0x63066CD9, +0xFA0F3D63, +0x8D080DF5, +0x3B6E20C8, +0x4C69105E, +0xD56041E4, +0xA2677172, +0x3C03E4D1, +0x4B04D447, +0xD20D85FD, +0xA50AB56B, +0x35B5A8FA, +0x42B2986C, +0xDBBBC9D6, +0xACBCF940, +0x32D86CE3, +0x45DF5C75, +0xDCD60DCF, +0xABD13D59, +0x26D930AC, +0x51DE003A, +0xC8D75180, +0xBFD06116, +0x21B4F4B5, +0x56B3C423, +0xCFBA9599, +0xB8BDA50F, +0x2802B89E, +0x5F058808, +0xC60CD9B2, +0xB10BE924, +0x2F6F7C87, +0x58684C11, +0xC1611DAB, +0xB6662D3D, +0x76DC4190, +0x01DB7106, +0x98D220BC, +0xEFD5102A, +0x71B18589, +0x06B6B51F, +0x9FBFE4A5, +0xE8B8D433, +0x7807C9A2, +0x0F00F934, +0x9609A88E, +0xE10E9818, +0x7F6A0DBB, +0x086D3D2D, +0x91646C97, +0xE6635C01, +0x6B6B51F4, +0x1C6C6162, +0x856530D8, +0xF262004E, +0x6C0695ED, +0x1B01A57B, +0x8208F4C1, +0xF50FC457, +0x65B0D9C6, +0x12B7E950, +0x8BBEB8EA, +0xFCB9887C, +0x62DD1DDF, +0x15DA2D49, +0x8CD37CF3, +0xFBD44C65, +0x4DB26158, +0x3AB551CE, +0xA3BC0074, +0xD4BB30E2, +0x4ADFA541, +0x3DD895D7, +0xA4D1C46D, +0xD3D6F4FB, +0x4369E96A, +0x346ED9FC, +0xAD678846, +0xDA60B8D0, +0x44042D73, +0x33031DE5, +0xAA0A4C5F, +0xDD0D7CC9, +0x5005713C, +0x270241AA, +0xBE0B1010, +0xC90C2086, +0x5768B525, +0x206F85B3, +0xB966D409, +0xCE61E49F, +0x5EDEF90E, +0x29D9C998, +0xB0D09822, +0xC7D7A8B4, +0x59B33D17, +0x2EB40D81, +0xB7BD5C3B, +0xC0BA6CAD, +0xEDB88320, +0x9ABFB3B6, +0x03B6E20C, +0x74B1D29A, +0xEAD54739, +0x9DD277AF, +0x04DB2615, +0x73DC1683, +0xE3630B12, +0x94643B84, +0x0D6D6A3E, +0x7A6A5AA8, +0xE40ECF0B, +0x9309FF9D, +0x0A00AE27, +0x7D079EB1, +0xF00F9344, +0x8708A3D2, +0x1E01F268, +0x6906C2FE, +0xF762575D, +0x806567CB, +0x196C3671, +0x6E6B06E7, +0xFED41B76, +0x89D32BE0, +0x10DA7A5A, +0x67DD4ACC, +0xF9B9DF6F, +0x8EBEEFF9, +0x17B7BE43, +0x60B08ED5, +0xD6D6A3E8, +0xA1D1937E, +0x38D8C2C4, +0x4FDFF252, +0xD1BB67F1, +0xA6BC5767, +0x3FB506DD, +0x48B2364B, +0xD80D2BDA, +0xAF0A1B4C, +0x36034AF6, +0x41047A60, +0xDF60EFC3, +0xA867DF55, +0x316E8EEF, +0x4669BE79, +0xCB61B38C, +0xBC66831A, +0x256FD2A0, +0x5268E236, +0xCC0C7795, +0xBB0B4703, +0x220216B9, +0x5505262F, +0xC5BA3BBE, +0xB2BD0B28, +0x2BB45A92, +0x5CB36A04, +0xC2D7FFA7, +0xB5D0CF31, +0x2CD99E8B, +0x5BDEAE1D, +0x9B64C2B0, +0xEC63F226, +0x756AA39C, +0x026D930A, +0x9C0906A9, +0xEB0E363F, +0x72076785, +0x05005713, +0x95BF4A82, +0xE2B87A14, +0x7BB12BAE, +0x0CB61B38, +0x92D28E9B, +0xE5D5BE0D, +0x7CDCEFB7, +0x0BDBDF21, +0x86D3D2D4, +0xF1D4E242, +0x68DDB3F8, +0x1FDA836E, +0x81BE16CD, +0xF6B9265B, +0x6FB077E1, +0x18B74777, +0x88085AE6, +0xFF0F6A70, +0x66063BCA, +0x11010B5C, +0x8F659EFF, +0xF862AE69, +0x616BFFD3, +0x166CCF45, +0xA00AE278, +0xD70DD2EE, +0x4E048354, +0x3903B3C2, +0xA7672661, +0xD06016F7, +0x4969474D, +0x3E6E77DB, +0xAED16A4A, +0xD9D65ADC, +0x40DF0B66, +0x37D83BF0, +0xA9BCAE53, +0xDEBB9EC5, +0x47B2CF7F, +0x30B5FFE9, +0xBDBDF21C, +0xCABAC28A, +0x53B39330, +0x24B4A3A6, +0xBAD03605, +0xCDD70693, +0x54DE5729, +0x23D967BF, +0xB3667A2E, +0xC4614AB8, +0x5D681B02, +0x2A6F2B94, +0xB40BBE37, +0xC30C8EA1, +0x5A05DF1B, +0x2D02EF8D, +}; + +unsigned long CRC16(const unsigned char *buf, int size, int key) +{ + + //printf("CRC16() key=%d\n",key); +/* +sub_0_10020760 proc near ; CODE XREF: sub_0_10008620+AEp + ; sub_0_10022A90+14Fp ... + +arg_0 = dword ptr 4 +arg_4 = dword ptr 8 +arg_8 = dword ptr 0Ch +*/ + + //int *pecx = buf; + unsigned long ecx = key; //mov ecx, [esp+arg_8] + unsigned long eax = ecx; //mov eax, ecx + unsigned long edi; +/* int ecx = key; //mov ecx, [esp+arg_8] + int eax = ecx; //mov eax, ecx + int edi; +*/ + eax = ~ eax; //not eax + eax&=0xFF; //and eax, 0FFh + eax=IntArray[eax]; //mov eax, dword_0_10115D38[eax*4] IntArray + eax ^= 0x00FFFFFF; //xor eax, 0FFFFFFh + int edx = ecx; //mov edx, ecx + edx = edx >> 8; //sar edx, 8 + edx = edx ^ eax; //xor edx, eax + eax = eax >> 8; //sar eax, 8 + edx &= 0xFF; //and edx, 0FFh + eax &= 0x00FFFFFF; //and eax, 0FFFFFFh + //push esi + eax ^= IntArray[edx]; //xor eax, dword_0_10115D38[edx*4] + edx = ecx; //mov edx, ecx + edx = edx >> 0x10; //sar edx, 10h + edx ^= eax; //xor edx, eax + eax = eax >> 8; //sar eax, 8 + edx &= 0xFF; //and edx, 0FFh + int esi = IntArray[edx]; //mov esi, dword_0_10115D38[edx*4] + edx = size; //mov edx, [esp+4+arg_4] + eax &= 0x00FFFFFF; //and eax, 0FFFFFFh + eax ^= esi; //xor eax, esi + ecx = ecx >> 0x18; //sar ecx, 18h + ecx ^= eax; //xor ecx, eax + ecx &= 0xFF; //and ecx, 0FFh + esi = IntArray[ecx]; //mov esi, dword_0_10115D38[ecx*4] + /*ecx = (int) buf; not used */ //mov ecx, [esp+4+arg_0] + eax = eax >> 8; //sar eax, 8 + eax &= 0x00FFFFFF; //and eax, 0FFFFFFh + eax ^= esi; //xor eax, esi + /* int* esi = ecx+edx //??*///lea esi, [ecx+edx] + for(int x = 0; x < size; x++) + { //eax is the crc, ecx is the current part of the buffer + int edx = 0; //xor edx, edx + edx = buf[x] & 0x00FF; //mov dl, [ecx] + + /*if(pos > size) //cmp ecx, esi + return ~eax; //jnb short loc_0_10020803 + */ + //push edi + +//loc_0_100207E0: ; CODE XREF: sub_0_10020760+A0j +//LOOP + edx ^= eax; //xor edx, eax + eax = eax >> 8; //sar eax, 8 + edx &= 0xFF; //and edx, 0FFh + edi = IntArray[edx]; //mov edi, dword_0_10115D38[edx*4] + eax &= 0x00FFFFFF; //and eax, 0FFFFFFh + eax ^= edi; //xor eax, edi + //inc ecx + //cmp ecx, esi + // jb short loc_0_100207E0 + //pop edi + } + + return ~eax; +} + +/*loc_0_10020803: ; CODE XREF: sub_0_10020760+7Dj + not eax + pop esi + retn +sub_0_10020760 endp + +*/ diff --git a/common/CRC16.h b/common/CRC16.h new file mode 100644 index 000000000..df1768f00 --- /dev/null +++ b/common/CRC16.h @@ -0,0 +1,6 @@ +#ifndef _CRC16_H +#define _CRC16_H + +unsigned long CRC16(const unsigned char *buf, int size, int key); + +#endif diff --git a/common/Condition.cpp b/common/Condition.cpp new file mode 100644 index 000000000..48fb83ce5 --- /dev/null +++ b/common/Condition.cpp @@ -0,0 +1,153 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "debug.h" +#include "Condition.h" + +#ifdef _WINDOWS +#else +#include +#include +#include +#endif + +#ifdef _WINDOWS + + +Condition::Condition() +{ + m_events[SignalEvent] = CreateEvent (NULL, // security + FALSE, // is auto-reset event? + FALSE, // is signaled initially? + NULL); // name + m_events[BroadcastEvent] = CreateEvent (NULL, // security + TRUE, // is auto-reset event? + FALSE, // is signaled initially? + NULL); // name + m_waiters = 0; + InitializeCriticalSection(&CSMutex); +} + +Condition::~Condition() +{ + DeleteCriticalSection(&CSMutex); + CloseHandle(m_events[SignalEvent]); + CloseHandle(m_events[BroadcastEvent]); +} + +void Condition::Signal() +{ + EnterCriticalSection(&CSMutex); + if(m_waiters > 0) + SetEvent(m_events[SignalEvent]); + LeaveCriticalSection(&CSMutex); +} + +void Condition::SignalAll() +{ + EnterCriticalSection(&CSMutex); + if(m_waiters > 0) + SetEvent(m_events[BroadcastEvent]); + LeaveCriticalSection(&CSMutex); +} + +void Condition::Wait() +{ + EnterCriticalSection(&CSMutex); + + m_waiters++; + + + LeaveCriticalSection(&CSMutex); + int result = WaitForMultipleObjects (_eventCount, m_events, FALSE, INFINITE); + EnterCriticalSection(&CSMutex); + + m_waiters--; + + //see if we are the last person waiting on the condition, and there was a broadcast + //if so, we need to reset the broadcast event. + if(m_waiters == 0 && result == (WAIT_OBJECT_0+BroadcastEvent)) + ResetEvent(m_events[BroadcastEvent]); + + LeaveCriticalSection(&CSMutex); +} + + +#else //!WIN32 + +Condition::Condition() +{ + pthread_cond_init(&cond,NULL); + pthread_mutex_init(&mutex,NULL); +} + +void Condition::Signal() +{ + pthread_mutex_lock(&mutex); + pthread_cond_signal(&cond); + pthread_mutex_unlock(&mutex); +} + +void Condition::SignalAll() +{ + pthread_mutex_lock(&mutex); + pthread_cond_broadcast(&cond); + pthread_mutex_unlock(&mutex); +} + +void Condition::Wait() +{ + pthread_mutex_lock(&mutex); + pthread_cond_wait(&cond,&mutex); + pthread_mutex_unlock(&mutex); +} + +/* +I commented this specifically because I think it might be very +difficult to write a windows counterpart to it, so I would like +to discourage its use until we can confirm that it can be reasonably +implemented on windows. + +bool Condition::TimedWait(unsigned long usec) +{ +struct timeval now; +struct timespec timeout; +int retcode=0; + pthread_mutex_lock(&mutex); + gettimeofday(&now,NULL); + now.tv_usec+=usec; + timeout.tv_sec = now.tv_sec + (now.tv_usec/1000000); + timeout.tv_nsec = (now.tv_usec%1000000) *1000; + //cout << "now=" << now.tv_sec << "."< +#endif + +//Sombody, someday needs to figure out how to implement a condition +//system on windows... + + +class Condition { + private: +#ifdef WIN32 + enum { + SignalEvent = 0, + BroadcastEvent, + _eventCount + }; + + HANDLE m_events[_eventCount]; + uint32 m_waiters; + CRITICAL_SECTION CSMutex; +#else + pthread_cond_t cond; + pthread_mutex_t mutex; +#endif + public: + Condition(); + void Signal(); + void SignalAll(); + void Wait(); +// bool TimedWait(unsigned long usec); + ~Condition(); +}; + +#endif + diff --git a/common/DBMemLeak.cpp b/common/DBMemLeak.cpp new file mode 100644 index 000000000..2b457b1ad --- /dev/null +++ b/common/DBMemLeak.cpp @@ -0,0 +1,62 @@ +#ifdef _EQDEBUG +#include "../common/debug.h" +#include +#include +#include +#include "../common/Mutex.h" +#include "DBMemLeak.h" + +#include + +#ifdef _WINDOWS +#define snprintf _snprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +DBMemLeak dbmemleak; +LinkedList* list = 0; +Mutex MDBMemLeak; + +DBMemLeak::DBMemLeak() { + list = new LinkedList; +} + +DBMemLeak::~DBMemLeak() { + LinkedListIterator iterator(*list); + iterator.Reset(); + while (iterator.MoreElements()) { + char tmp[200]; + snprintf(tmp, sizeof(tmp) - 3, "DB Mem Leak: Block=%6d, Query=%s", iterator.GetData()->memblock, iterator.GetData()->query); + snprintf(tmp, sizeof(tmp), "%s\n", tmp); + OutputDebugString(tmp); + iterator.Advance(); + } + safe_delete(list); +} + +void DBMemLeak::Alloc(const void* result, const char* query) { + LockMutex lock(&MDBMemLeak); + long requestNumber; + uint8* tmp2 = new uint8; + _CrtIsMemoryBlock( tmp2, 1, &requestNumber, 0, 0 ); + safe_delete(tmp2); + DBMemLeakStruct* tmp = (DBMemLeakStruct*) new uchar[sizeof(DBMemLeakStruct) + strlen(query) + 1]; + tmp->result = result; + tmp->memblock = requestNumber; + strcpy(tmp->query, query); + list->Append(tmp); +} + +void DBMemLeak::Free(const void* result) { + LockMutex lock(&MDBMemLeak); + LinkedListIterator iterator(*list); + iterator.Reset(); + while (iterator.MoreElements()) { + if (result == iterator.GetData()->result) + iterator.RemoveCurrent(); + else + iterator.Advance(); + } +} +#endif diff --git a/common/DBMemLeak.h b/common/DBMemLeak.h new file mode 100644 index 000000000..48f3f7374 --- /dev/null +++ b/common/DBMemLeak.h @@ -0,0 +1,25 @@ +#ifdef _EQDEBUG +#ifndef DBMemLeak_H +#define DBMemLeak_H +#include "../common/types.h" +#include "../common/linked_list.h" + +#define mysql_free_result(r) { DBMemLeak::Free(r); mysql_free_result(r); } + +struct DBMemLeakStruct { + const void* result; + uint32 memblock; + char query[0]; +}; + +class DBMemLeak { +public: + DBMemLeak(); + ~DBMemLeak(); + + static void Alloc(const void* result, const char* query); + static void Free(const void* result); +}; + +#endif +#endif diff --git a/common/EMuShareMem.cpp b/common/EMuShareMem.cpp new file mode 100644 index 000000000..f1a5d8dff --- /dev/null +++ b/common/EMuShareMem.cpp @@ -0,0 +1,300 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 +using namespace std; +#include "../common/types.h" +#include "EMuShareMem.h" + +#ifdef _WINDOWS + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + + #define EmuLibName "EMuShareMem" +#else + #define EmuLibName "libEMuShareMem.so" +#endif + +LoadEMuShareMemDLL EMuShareMemDLL; + +#ifndef WIN32 +uint32 LoadEMuShareMemDLL::refCount = 0; +#endif + +LoadEMuShareMemDLL::LoadEMuShareMemDLL() { + ClearFunc(); +#ifndef WIN32 + refCountU(); +#endif +} + +LoadEMuShareMemDLL::~LoadEMuShareMemDLL() { +#ifndef WIN32 + if (refCountD() <= 0) { +#endif + Unload(); +#ifndef WIN32 + } +#endif +} + +bool LoadEMuShareMemDLL::Load() { + if(loaded) + return(true); + + if(!SharedLibrary::Load(EmuLibName)) + return(false); + + if (Loaded()) { + Items.GetItem = (DLLFUNC_GetItem) GetSym("GetItem"); + Items.IterateItems = (DLLFUNC_IterateItems) GetSym("IterateItems"); + Items.cbAddItem = (DLLFUNC_AddItem) GetSym("AddItem"); + Items.DLLLoadItems = (DLLFUNC_DLLLoadItems) GetSym("DLLLoadItems"); + Doors.GetDoor = (DLLFUNC_GetDoor) GetSym("GetDoor"); + Doors.cbAddDoor = (DLLFUNC_AddDoor) GetSym("AddDoor"); + Doors.DLLLoadDoors = (DLLFUNC_DLLLoadDoors) GetSym("DLLLoadDoors"); + Spells.DLLLoadSPDat = (DLLFUNC_DLLLoadSPDat) GetSym("DLLLoadSPDat"); + NPCFactionList.DLLLoadNPCFactionLists = (DLLFUNC_DLLLoadNPCFactionLists) GetSym("DLLLoadNPCFactionLists"); + NPCFactionList.GetNPCFactionList = (DLLFUNC_GetNPCFactionList) GetSym("GetNPCFactionList"); + NPCFactionList.cbAddNPCFactionList = (DLLFUNC_AddNPCFactionList) GetSym("AddNPCFactionList"); + NPCFactionList.cbSetFaction = (DLLFUNC_SetFaction) GetSym("SetNPCFaction"); + Loot.DLLLoadLoot = (DLLFUNC_DLLLoadLoot) GetSym("DLLLoadLoot"); + Loot.cbAddLootTable = (DLLFUNC_AddLootTable) GetSym("AddLootTable"); + Loot.cbAddLootDrop = (DLLFUNC_AddLootDrop) GetSym("AddLootDrop"); + Loot.GetLootTable = (DLLFUNC_GetLootTable) GetSym("GetLootTable"); + Loot.GetLootDrop = (DLLFUNC_GetLootDrop) GetSym("GetLootDrop"); + Opcodes.GetEQOpcode = (DLLFUNC_GetEQOpcode) GetSym("GetEQOpcode"); + Opcodes.GetEmuOpcode = (DLLFUNC_GetEmuOpcode) GetSym("GetEmuOpcode"); + Opcodes.SetOpcodePair = (DLLFUNC_SetOpcodePair) GetSym("SetOpcodePair"); + Opcodes.DLLLoadOpcodes = (DLLFUNC_DLLLoadOpcodes) GetSym("DLLLoadOpcodes"); + Opcodes.ClearEQOpcodes = (DLLFUNC_ClearEQOpcodes) GetSym("ClearEQOpcodes"); + SkillCaps.LoadSkillCaps = (DLLFUNC_DLLLoadSkillCaps) GetSym("LoadSkillCaps"); + SkillCaps.GetSkillCap = (DLLFUNC_GetSkillCap) GetSym("GetSkillCap"); + SkillCaps.SetSkillCap = (DLLFUNC_SetSkillCap) GetSym("SetSkillCap"); + SkillCaps.ClearSkillCaps = (DLLFUNC_ClearSkillCaps) GetSym("ClearSkillCaps"); + SkillCaps.GetTrainLevel = (DLLFUNC_GetTrainLevel) GetSym("GetTrainLevel"); + if(Items.GetItem == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Items.GetItem"); + return(false); + } + + if(Items.IterateItems == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Items.IterateItems"); + return(false); + } + + if(Items.cbAddItem == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Items.cbAddItem"); + return(false); + } + + if(Items.DLLLoadItems == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Items.DLLLoadItems"); + return(false); + } + + if(Doors.GetDoor == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Doors.GetDoor"); + return(false); + } + + if(Doors.cbAddDoor == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Doors.cbAddDoor"); + return(false); + } + + if(Doors.DLLLoadDoors == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Doors.DLLLoadDoors"); + return(false); + } + + if(Spells.DLLLoadSPDat == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Spells.DLLLoadSPDat"); + return(false); + } + + if(NPCFactionList.DLLLoadNPCFactionLists == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach NPCFactionList.DLLLoadNPCFactionLists"); + return(false); + } + + if(NPCFactionList.GetNPCFactionList == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach NPCFactionList.GetNPCFactionList"); + return(false); + } + + if(NPCFactionList.cbAddNPCFactionList == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach NPCFactionList.cbAddNPCFactionList"); + return(false); + } + + if(NPCFactionList.cbSetFaction == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach NPCFactionList.cbSetFaction"); + return(false); + } + + if(Loot.DLLLoadLoot == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Loot.DLLLoadLoot"); + return(false); + } + + if(Loot.cbAddLootTable == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Loot.cbAddLootTable"); + return(false); + } + + if(Loot.cbAddLootDrop == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Loot.cbAddLootDrop"); + return(false); + } + + if(Loot.GetLootTable == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Loot.GetLootTable"); + return(false); + } + + if(Loot.GetLootDrop == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Loot.GetLootDrop"); + return(false); + } + + if(Opcodes.GetEQOpcode == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Opcodes.GetEQOpcode"); + return(false); + } + + if(Opcodes.GetEmuOpcode == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Opcodes.GetEmuOpcode"); + return(false); + } + + if(Opcodes.SetOpcodePair == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Opcodes.SetOpcodePair"); + return(false); + } + + if(Opcodes.DLLLoadOpcodes == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Opcodes.DLLLoadOpcodes"); + return(false); + } + + if(Opcodes.ClearEQOpcodes == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach Opcodes.ClearEQOpcodes"); + return(false); + } + + if(SkillCaps.LoadSkillCaps == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach SkillCaps.LoadSkillCaps"); + return(false); + } + + if(SkillCaps.GetSkillCap == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach SkillCaps.GetSkillCap"); + return(false); + } + + if(SkillCaps.SetSkillCap == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach SkillCaps.SetSkillCap"); + return(false); + } + + if(SkillCaps.ClearSkillCaps == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach SkillCaps.ClearSkillCaps"); + return(false); + } + + if(SkillCaps.GetTrainLevel == NULL) { + Unload(); + LogFile->write(EQEMuLog::Error, "LoadEMuShareMemDLL::Load() failed to attach SkillCaps.GetTrainLevel"); + return(false); + } + + LogFile->write(EQEMuLog::Status, "%s loaded", EmuLibName); + loaded = true; + return true; + } + else { + LogFile->write(EQEMuLog::Error, "%s was not loaded, but did not report an error.", EmuLibName); + } + return false; +} + +void LoadEMuShareMemDLL::Unload() { + ClearFunc(); + SharedLibrary::Unload(); +} + +void LoadEMuShareMemDLL::ClearFunc() { + Items.GetItem = 0; + Items.IterateItems = 0; + Items.cbAddItem = 0; + Items.DLLLoadItems = 0; + Doors.GetDoor = 0; + Doors.cbAddDoor = 0; + Doors.DLLLoadDoors = 0; + NPCFactionList.DLLLoadNPCFactionLists = 0; + NPCFactionList.GetNPCFactionList = 0; + NPCFactionList.cbAddNPCFactionList = 0; + NPCFactionList.cbSetFaction = 0; + Loot.DLLLoadLoot = 0; + Loot.cbAddLootTable = 0; + Loot.cbAddLootDrop = 0; + Loot.GetLootTable = 0; + Loot.GetLootDrop = 0; + Opcodes.GetEQOpcode = NULL; + Opcodes.GetEmuOpcode = NULL; + Opcodes.SetOpcodePair = NULL; + Opcodes.DLLLoadOpcodes = NULL; + Opcodes.ClearEQOpcodes = NULL; + SkillCaps.LoadSkillCaps = NULL; + SkillCaps.GetSkillCap = NULL; + SkillCaps.SetSkillCap = NULL; + SkillCaps.ClearSkillCaps = NULL; + SkillCaps.GetTrainLevel = NULL; + loaded = false; +} diff --git a/common/EMuShareMem.h b/common/EMuShareMem.h new file mode 100644 index 000000000..14ae2a8de --- /dev/null +++ b/common/EMuShareMem.h @@ -0,0 +1,187 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 EMuShareMem_H +#define EMuShareMem_H +#ifdef _WINDOWS +#include +#else +#include "../common/unix.h" +#endif +#include "../common/eq_packet_structs.h" +#include "../zone/zonedump.h" +#include "../zone/loottable.h" +#include "SharedLibrary.h" + +//////////// +// Items // +/////////// +typedef bool(*CALLBACK_DBLoadItems)(int32, uint32); + +typedef bool(*DLLFUNC_DLLLoadItems)(const CALLBACK_DBLoadItems, uint32, int32*, uint32*); +typedef const Item_Struct*(*DLLFUNC_GetItem)(uint32); +typedef const Item_Struct*(*DLLFUNC_IterateItems)(uint32*); +typedef bool(*DLLFUNC_AddItem)(uint32, const Item_Struct*); + +struct ItemsDLLFunc_Struct { + DLLFUNC_DLLLoadItems DLLLoadItems; + DLLFUNC_GetItem GetItem; + DLLFUNC_IterateItems IterateItems; + DLLFUNC_AddItem cbAddItem; +}; +/* +typedef bool(*CALLBACK_DBLoadNPCTypes)(int32, uint32); + +typedef bool(*DLLFUNC_DLLLoadNPCTypes)(const CALLBACK_DBLoadNPCTypes, uint32, int32*, uint32*); +typedef const NPCType*(*DLLFUNC_GetNPCType)(uint32); +typedef bool(*DLLFUNC_AddNPCType)(uint32, const NPCType*); +struct NPCTypesDLLFunc_Struct { + DLLFUNC_DLLLoadNPCTypes DLLLoadNPCTypes; + DLLFUNC_GetNPCType GetNPCType; + DLLFUNC_AddNPCType cbAddNPCType; +}; +*/ + + +//////////// +// Doors /// +//////////// +typedef bool(*CALLBACK_DBLoadDoors)(int32, uint32); + +typedef bool(*DLLFUNC_DLLLoadDoors)(const CALLBACK_DBLoadDoors, uint32, int32*, uint32*); +typedef const Door*(*DLLFUNC_GetDoor)(uint32); +typedef bool(*DLLFUNC_AddDoor)(uint32, const Door*); +struct DoorsDLLFunc_Struct { + DLLFUNC_DLLLoadDoors DLLLoadDoors; + DLLFUNC_GetDoor GetDoor; + DLLFUNC_AddDoor cbAddDoor; +}; + +//////////// +// Spells // +//////////// +typedef bool(*CALLBACK_FileLoadSPDat)(void*, int32); + +typedef bool(*DLLFUNC_DLLLoadSPDat)(const CALLBACK_FileLoadSPDat, const void**, int32*, uint32); +struct SpellsDLLFunc_Struct { + DLLFUNC_DLLLoadSPDat DLLLoadSPDat; +}; + +////////////// +// Factions // +////////////// + +typedef bool(*CALLBACK_DBLoadNPCFactionLists)(int32, uint32); + +typedef bool(*DLLFUNC_DLLLoadNPCFactionLists)(const CALLBACK_DBLoadNPCFactionLists, uint32, int32*, uint32*, uint8); +typedef const NPCFactionList*(*DLLFUNC_GetNPCFactionList)(uint32); +typedef bool(*DLLFUNC_AddNPCFactionList)(uint32, const NPCFactionList*); +typedef bool(*DLLFUNC_SetFaction)(uint32, uint32*, int32*, int8*, uint8*); +struct NPCFactionListDLLFunc_Struct { + DLLFUNC_DLLLoadNPCFactionLists DLLLoadNPCFactionLists; + DLLFUNC_GetNPCFactionList GetNPCFactionList; + DLLFUNC_AddNPCFactionList cbAddNPCFactionList; + DLLFUNC_SetFaction cbSetFaction; +}; + +//////////// +// Loot // +/////////// + +typedef bool(*CALLBACK_DBLoadLoot)(); + +typedef bool(*DLLFUNC_DLLLoadLoot)(const CALLBACK_DBLoadLoot, uint32, uint32, uint32, uint32, uint32, uint32, uint32, uint32, uint32, uint32); +typedef bool(*DLLFUNC_AddLootTable)(uint32, const LootTable_Struct*); +typedef bool(*DLLFUNC_AddLootDrop)(uint32, const LootDrop_Struct*); +typedef const LootTable_Struct*(*DLLFUNC_GetLootTable)(uint32); +typedef const LootDrop_Struct*(*DLLFUNC_GetLootDrop)(uint32); +struct LootDLLFunc_Struct { + DLLFUNC_DLLLoadLoot DLLLoadLoot; + DLLFUNC_AddLootTable cbAddLootTable; + DLLFUNC_AddLootDrop cbAddLootDrop; + DLLFUNC_GetLootTable GetLootTable; + DLLFUNC_GetLootDrop GetLootDrop; +}; + +///////////// +// Opcodes // +///////////// + +typedef bool(*CALLBACK_DBLoadOpcodes)(const char *filename); + +typedef bool(*DLLFUNC_DLLLoadOpcodes)(const CALLBACK_DBLoadOpcodes, uint32 opsize, uint32 eq_count, uint32 emu_count, const char *filename); +typedef uint16 (*DLLFUNC_GetEQOpcode)(uint16 emu_op); +typedef uint16 (*DLLFUNC_GetEmuOpcode)(uint16 eq_op); +typedef void (*DLLFUNC_ClearEQOpcodes)(); +typedef bool(*DLLFUNC_SetOpcodePair)(uint16 emu_op, uint16 eq_op); +struct OpcodeDLLFunc_Struct { + DLLFUNC_DLLLoadOpcodes DLLLoadOpcodes; + DLLFUNC_GetEQOpcode GetEQOpcode; + DLLFUNC_GetEmuOpcode GetEmuOpcode; + DLLFUNC_SetOpcodePair SetOpcodePair; + DLLFUNC_ClearEQOpcodes ClearEQOpcodes; +}; + +//////////////// +// Skill Caps // +//////////////// + +typedef bool(*CALLBACK_DBLoadSkillCaps)(); + +typedef bool(*DLLFUNC_DLLLoadSkillCaps)(const CALLBACK_DBLoadSkillCaps, uint32 opsize, uint8 ClassCount, uint8 SkillCount, uint8 LevelCount); +typedef uint16 (*DLLFUNC_GetSkillCap)(uint8 Class_, uint8 Skill, uint8 Level); +typedef void (*DLLFUNC_ClearSkillCaps)(); +typedef bool(*DLLFUNC_SetSkillCap)(uint8 Class_, uint8 Skill, uint8 Level, uint16 cap); +typedef uint8 (*DLLFUNC_GetTrainLevel)(uint8 Class_, uint8 Skill, uint8 Level); +struct SkillCapDLLFunc_Struct { + DLLFUNC_DLLLoadSkillCaps LoadSkillCaps; + DLLFUNC_GetSkillCap GetSkillCap; + DLLFUNC_SetSkillCap SetSkillCap; + DLLFUNC_ClearSkillCaps ClearSkillCaps; + DLLFUNC_GetTrainLevel GetTrainLevel; +}; + + +class LoadEMuShareMemDLL : public SharedLibrary { +public: + LoadEMuShareMemDLL(); + ~LoadEMuShareMemDLL(); + + bool Load(); + void Unload(); + + ItemsDLLFunc_Struct Items; + //NPCTypesDLLFunc_Struct NPCTypes; + DoorsDLLFunc_Struct Doors; + SpellsDLLFunc_Struct Spells; + NPCFactionListDLLFunc_Struct NPCFactionList; + LootDLLFunc_Struct Loot; + OpcodeDLLFunc_Struct Opcodes; + SkillCapDLLFunc_Struct SkillCaps; +private: + void ClearFunc(); + + bool loaded; + +#ifdef _WINDOWS +#else + static uint32 refCount; + static uint32 refCountU() { return ++refCount; }; + static uint32 refCountD() { return --refCount; }; +#endif +}; +#endif diff --git a/common/EQDB.cpp b/common/EQDB.cpp new file mode 100644 index 000000000..c12d04b2e --- /dev/null +++ b/common/EQDB.cpp @@ -0,0 +1,78 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "debug.h" +#include "EQDB.h" +#include "database.h" +#include +#include + +EQDB EQDB::s_EQDB; + +EQDB::EQDB() { +} + +unsigned int EQDB::field_count() { + return mysql_field_count(mysql_ref); +} + +unsigned long EQDB::affected_rows() { + return mysql_affected_rows(mysql_ref); +} + +unsigned long EQDB::insert_id() { + return mysql_insert_id(mysql_ref); +} + +unsigned int EQDB::get_errno() { + return mysql_errno(mysql_ref); +} + +Const_char * EQDB::error() { + return mysql_error(mysql_ref); +} + +EQDBRes * EQDB::query(Const_char *q) { + if (mysql_real_query(mysql_ref,q,strlen(q))==0) { + if (mysql_field_count(mysql_ref)) { + MYSQL_RES *r=mysql_store_result(mysql_ref); + return new EQDBRes(r); + } else { + //no result, give them back a 'true but empty' result set + return(new EQDBRes(NULL)); + } + } + + return NULL; +} + +//NOT THREAD SAFE! +Const_char *EQDB::escape_string(Const_char *from) { + int len = strlen(from); + char *res = new char[len*2+1]; + + mysql_real_escape_string(mysql_ref,res,from,len); + + res[len*2] = '\0'; + m_escapeBuffer = res; + delete[] res; + return(m_escapeBuffer.c_str()); +} + + + + diff --git a/common/EQDB.h b/common/EQDB.h new file mode 100644 index 000000000..5bac35073 --- /dev/null +++ b/common/EQDB.h @@ -0,0 +1,54 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 EQDB_H_ +#define EQDB_H_ + +#include +#include +#include +#include "types.h" +#include "EQDBRes.h" +#include + +//this is the main object exported to perl. +class EQDB { + EQDB(); +public: + static EQDB *Singleton() { return(&s_EQDB); } + + static void SetMySQL(MYSQL *m) { s_EQDB.mysql_ref=m; } + +//BEGIN PERL EXPORT + //NOTE: you must have a space after the * of a return value + + unsigned int field_count(); + unsigned long affected_rows(); + unsigned long insert_id(); + unsigned int get_errno(); + Const_char * error(); + EQDBRes * query(Const_char *q); + Const_char * escape_string(Const_char *from); //NOT THREAD SAFE! (m_escapeBuffer) +//END PERL EXPORT + +private: + string m_escapeBuffer; + static EQDB s_EQDB; + MYSQL *mysql_ref; +}; + +#endif /*EQDB_H_*/ diff --git a/common/EQDBRes.cpp b/common/EQDBRes.cpp new file mode 100644 index 000000000..0a41f7467 --- /dev/null +++ b/common/EQDBRes.cpp @@ -0,0 +1,51 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "debug.h" +#include "EQDBRes.h" +#include + +vector EQDBRes::fetch_row_array() { + vector array; + if(res == NULL) + return(array); + + int count=mysql_num_fields(res); + MYSQL_ROW row=mysql_fetch_row(res); + for (int i=0;i EQDBRes::fetch_row_hash() { + map rowhash; + if(res == NULL) + return(rowhash); + + MYSQL_FIELD *fields; + MYSQL_ROW row; + unsigned long num_fields,i; + + if (res && (num_fields=mysql_num_fields(res)) && (row = mysql_fetch_row(res))!=NULL && (fields = mysql_fetch_fields(res))!=NULL) { + for(i=0;i +#include +#include +#include "types.h" +#include "database.h" +#include + +//this is the main object exported to perl. +class EQDBRes { +public: + EQDBRes(MYSQL_RES *r) { res=r; } + ~EQDBRes() { finish(); } + +//BEGIN PERL EXPORT + unsigned long num_rows() { return (res) ? mysql_num_rows(res) : 0; } + unsigned long num_fields() { return (res) ? mysql_num_fields(res) : 0; } + void DESTROY() { } + void finish() { if (res) mysql_free_result(res); res=NULL; }; + vector fetch_row_array(); + map fetch_row_hash(); + unsigned long * fetch_lengths() { return (res) ? mysql_fetch_lengths(res) : 0; } +//END PERL EXPORT + +private: + MYSQL_RES *res; +}; + +#endif /*EQDBRes_H_*/ diff --git a/common/EQEMuError.cpp b/common/EQEMuError.cpp new file mode 100644 index 000000000..353c5a342 --- /dev/null +++ b/common/EQEMuError.cpp @@ -0,0 +1,135 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 +*/ +#ifdef _WINDOWS +#include +#endif +#include "EQEMuError.h" +#include "linked_list.h" +#include "Mutex.h" +#include "MiscFunctions.h" +#include +#include +#ifdef _WINDOWS + #include +#endif + +void UpdateWindowTitle(char* iNewTitle = 0); +void CatchSignal(int sig_num); + +const char* EQEMuErrorText[EQEMuError_MaxErrorID] = { "ErrorID# 0, No Error", + "MySQL Error #1405 or #2001 means your mysql server rejected the username and password you presented it.", + "MySQL Error #2003 means you were unable to connect to the mysql server.", + "MySQL Error #2005 means you there are too many connections on the mysql server. The server is overloaded.", + "MySQL Error #2007 means you the server is out of memory. The server is overloaded.", + }; + +LinkedList* EQEMuErrorList; +Mutex* MEQEMuErrorList; +AutoDelete< LinkedList > ADEQEMuErrorList(&EQEMuErrorList); +AutoDelete ADMEQEMuErrorList(&MEQEMuErrorList); + +const char* GetErrorText(uint32 iError) { + if (iError >= EQEMuError_MaxErrorID) + return "ErrorID# out of range"; + else + return EQEMuErrorText[iError]; +} + +void AddEQEMuError(eEQEMuError iError, bool iExitNow) { + if (!iError) + return; + if (!EQEMuErrorList) { + EQEMuErrorList = new LinkedList; + MEQEMuErrorList = new Mutex; + } + LockMutex lock(MEQEMuErrorList); + + LinkedListIterator iterator(*EQEMuErrorList); + iterator.Reset(); + while (iterator.MoreElements()) { + if (iterator.GetData()[0] == 1) { +//Umm... this gets a big WTF... +// if (*((uint32*) iterator.GetData()[1]) == iError) +//not sure whats going on, using a character as a pointer.... + if (*((eEQEMuError*) &(iterator.GetData()[1])) == iError) + return; + } + iterator.Advance(); + } + + char* tmp = new char[6]; + tmp[0] = 1; + tmp[5] = 0; + *((uint32*) &tmp[1]) = iError; + EQEMuErrorList->Append(tmp); + + if (iExitNow) + CatchSignal(2); +} + +void AddEQEMuError(char* iError, bool iExitNow) { + if (!iError) + return; + if (!EQEMuErrorList) { + EQEMuErrorList = new LinkedList; + MEQEMuErrorList = new Mutex; + } + LockMutex lock(MEQEMuErrorList); + char* tmp = strcpy(new char[strlen(iError) + 1], iError); + EQEMuErrorList->Append(tmp); + + if (iExitNow) + CatchSignal(2); +} + +uint32 CheckEQEMuError() { + if (!EQEMuErrorList) + return 0; + uint32 ret = 0; + char* tmp = 0; + bool HeaderPrinted = false; + LockMutex lock(MEQEMuErrorList); + + while ((tmp = EQEMuErrorList->Pop() )) { + if (!HeaderPrinted) { + fprintf(stdout, "===============================\nRuntime errors:\n\n"); + HeaderPrinted = true; + } + if (tmp[0] == 1) { + fprintf(stdout, "%s\n", GetErrorText(*((uint32*) &tmp[1]))); + fprintf(stdout, "For more information on this error, visit http://www.eqemu.net/eqemuerror.php?id=%u\n\n", *((uint32*) &tmp[1])); + } + else { + fprintf(stdout, "%s\n\n", tmp); + } + safe_delete(tmp); + ret++; + } + return ret; +} + +void CheckEQEMuErrorAndPause() { +#ifdef _WINDOWS + if (CheckEQEMuError()) { + fprintf(stdout, "Hit any key to exit\n"); + UpdateWindowTitle("Error"); + getch(); + } +#endif +} + diff --git a/common/EQEMuError.h b/common/EQEMuError.h new file mode 100644 index 000000000..8ecb53aa6 --- /dev/null +++ b/common/EQEMuError.h @@ -0,0 +1,36 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 EQEMuError_H +#define EQEMuError_H + +#include "../common/types.h" + +enum eEQEMuError { EQEMuError_NoError, + EQEMuError_Mysql_1405, + EQEMuError_Mysql_2003, + EQEMuError_Mysql_2005, + EQEMuError_Mysql_2007, + EQEMuError_MaxErrorID }; + +void AddEQEMuError(eEQEMuError iError, bool iExitNow = false); +void AddEQEMuError(char* iError, bool iExitNow = false); +uint32 CheckEQEMuError(); +void CheckEQEMuErrorAndPause(); + +#endif + diff --git a/common/EQEmuConfig.cpp b/common/EQEmuConfig.cpp new file mode 100644 index 000000000..2bb8aca28 --- /dev/null +++ b/common/EQEmuConfig.cpp @@ -0,0 +1,472 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "EQEmuConfig.h" +#include "MiscFunctions.h" +#include +#include + +string EQEmuConfig::ConfigFile = "eqemu_config.xml"; +EQEmuConfig *EQEmuConfig::_config = NULL; + +void EQEmuConfig::do_world(TiXmlElement *ele) { + const char *text; + TiXmlElement * sub_ele;; + + text= ParseTextBlock(ele,"shortname"); + if (text) + ShortName=text; + + text = ParseTextBlock(ele,"longname"); + if (text) + LongName=text; + + text = ParseTextBlock(ele,"address",true); + if (text) + WorldAddress=text; + + text = ParseTextBlock(ele,"localaddress",true); + if (text) + LocalAddress=text; + + text = ParseTextBlock(ele,"maxclients",true); + if (text) + MaxClients=atoi(text); + + // Get the element + text = ParseTextBlock(ele,"key",true); + if (text) + SharedKey=text; + + // Get the element + sub_ele = ele->FirstChildElement("loginserver"); + if (sub_ele) { + text=ParseTextBlock(sub_ele,"host",true); + if (text) + LoginHost=text; + + text=ParseTextBlock(sub_ele,"port",true); + if (text) + LoginPort=atoi(text); + + text=ParseTextBlock(sub_ele,"account",true); + if (text) + LoginAccount=text; + + text=ParseTextBlock(sub_ele,"password",true); + if (text) + LoginPassword=text; + } else { + char str[32]; + do { + sprintf(str, "loginserver%i", ++LoginCount); + sub_ele = ele->FirstChildElement(str); + if (sub_ele) { + LoginConfig* loginconfig = new LoginConfig; + text=ParseTextBlock(sub_ele,"host",true); + if (text) + loginconfig->LoginHost=text; + + text=ParseTextBlock(sub_ele,"port",true); + if (text) + loginconfig->LoginPort=atoi(text); + + text=ParseTextBlock(sub_ele,"account",true); + if (text) + loginconfig->LoginAccount=text; + + text=ParseTextBlock(sub_ele,"password",true); + if (text) + loginconfig->LoginPassword=text; + loginlist.Insert(loginconfig); + } + } while(sub_ele); + } + + // Check for locked + sub_ele = ele->FirstChildElement("locked"); + if (sub_ele != NULL) + Locked=true; + + // Get the element + sub_ele = ele->FirstChildElement("tcp"); + if(sub_ele != NULL) { + + text = sub_ele->Attribute("ip"); + if (text) + WorldIP=text; + + text = sub_ele->Attribute("port"); + if (text) + WorldTCPPort=atoi(text); + + text = sub_ele->Attribute("telnet"); + if (text && !strcasecmp(text,"enabled")) + TelnetEnabled=true; + + } + + // Get the element + sub_ele = ele->FirstChildElement("http"); + if(sub_ele != NULL) { + +// text = sub_ele->Attribute("ip"); +// if (text) +// WorldIP=text; + + text = sub_ele->Attribute("mimefile"); + if (text) + WorldHTTPMimeFile=text; + + text = sub_ele->Attribute("port"); + if (text) + WorldHTTPPort=atoi(text); + + text = sub_ele->Attribute("enabled"); + if (text && !strcasecmp(text,"true")) + WorldHTTPEnabled=true; + + } +} + +void EQEmuConfig::do_chatserver(TiXmlElement *ele) { + const char *text; + + text=ParseTextBlock(ele,"host",true); + if (text) + ChatHost=text; + + text=ParseTextBlock(ele,"port",true); + if (text) + ChatPort=atoi(text); +} + +void EQEmuConfig::do_mailserver(TiXmlElement *ele) { + const char *text; + + text=ParseTextBlock(ele,"host",true); + if (text) + MailHost=text; + + text=ParseTextBlock(ele,"port",true); + if (text) + MailPort=atoi(text); +} + +void EQEmuConfig::do_database(TiXmlElement *ele) { + const char *text; + + text=ParseTextBlock(ele,"host",true); + if (text) + DatabaseHost=text; + + text=ParseTextBlock(ele,"port",true); + if (text) + DatabasePort=atoi(text); + + text=ParseTextBlock(ele,"username",true); + if (text) + DatabaseUsername=text; + + text=ParseTextBlock(ele,"password",true); + if (text) + DatabasePassword=text; + + text=ParseTextBlock(ele,"db",true); + if (text) + DatabaseDB=text; +} + + +void EQEmuConfig::do_qsdatabase(TiXmlElement *ele) { + const char *text; + + text=ParseTextBlock(ele,"host",true); + if (text) + QSDatabaseHost=text; + + text=ParseTextBlock(ele,"port",true); + if (text) + QSDatabasePort=atoi(text); + + text=ParseTextBlock(ele,"username",true); + if (text) + QSDatabaseUsername=text; + + text=ParseTextBlock(ele,"password",true); + if (text) + QSDatabasePassword=text; + + text=ParseTextBlock(ele,"db",true); + if (text) + QSDatabaseDB=text; +} + +void EQEmuConfig::do_zones(TiXmlElement *ele) { + const char *text; + TiXmlElement *sub_ele; +// TiXmlNode *node,*sub_node; + + text=ParseTextBlock(ele,"defaultstatus",true); + if (text) + DefaultStatus=atoi(text); + + // Get the element + sub_ele = ele->FirstChildElement("ports"); + if(sub_ele != NULL) { + + text = sub_ele->Attribute("low"); + if (text) + ZonePortLow=atoi(text);; + + text = sub_ele->Attribute("high"); + if (text) + ZonePortHigh=atoi(text); + } +} + +void EQEmuConfig::do_files(TiXmlElement *ele) { + const char *text; + + text=ParseTextBlock(ele,"spells",true); + if (text) + SpellsFile=text; + + text=ParseTextBlock(ele,"opcodes",true); + if (text) + OpCodesFile=text; + + text=ParseTextBlock(ele,"logsettings",true); + if (text) + LogSettingsFile=text; + + text=ParseTextBlock(ele,"eqtime",true); + if (text) + EQTimeFile=text; +} + +void EQEmuConfig::do_directories(TiXmlElement *ele) { + const char *text; + + text=ParseTextBlock(ele,"maps",true); + if (text) + MapDir=text; + + text=ParseTextBlock(ele,"quests",true); + if (text) + QuestDir=text; + + text=ParseTextBlock(ele,"plugins",true); + if (text) + PluginDir=text; + +} + +void EQEmuConfig::do_launcher(TiXmlElement *ele) { + const char *text; + TiXmlElement *sub_ele; + + text=ParseTextBlock(ele,"logprefix",true); + if (text) + LogPrefix = text; + + text=ParseTextBlock(ele,"logsuffix",true); + if (text) + LogSuffix = text; + + // Get the element + text = ParseTextBlock(ele,"exe",true); + if (text) + ZoneExe = text; + + // Get the element + sub_ele = ele->FirstChildElement("timers"); + if(sub_ele != NULL) { + text = sub_ele->Attribute("restart"); + if (text) + RestartWait = atoi(text); + + text = sub_ele->Attribute("reterminate"); + if (text) + TerminateWait = atoi(text); + + text = sub_ele->Attribute("initial"); + if (text) + InitialBootWait = atoi(text); + + text = sub_ele->Attribute("interval"); + if (text) + ZoneBootInterval = atoi(text); + } +} + +string EQEmuConfig::GetByName(const string &var_name) const { + if(var_name == "ShortName") + return(ShortName); + if(var_name == "LongName") + return(LongName); + if(var_name == "WorldAddress") + return(WorldAddress); + if(var_name == "LoginHost") + return(LoginHost); + if(var_name == "LoginAccount") + return(LoginAccount); + if(var_name == "LoginPassword") + return(LoginPassword); + if(var_name == "LoginPort") + return(itoa(LoginPort)); + if(var_name == "Locked") + return(Locked?"true":"false"); + if(var_name == "WorldTCPPort") + return(itoa(WorldTCPPort)); + if(var_name == "WorldIP") + return(WorldIP); + if(var_name == "TelnetEnabled") + return(TelnetEnabled?"true":"false"); + if(var_name == "WorldHTTPPort") + return(itoa(WorldHTTPPort)); + if(var_name == "WorldHTTPMimeFile") + return(WorldHTTPMimeFile); + if(var_name == "WorldHTTPEnabled") + return(WorldHTTPEnabled?"true":"false"); + if(var_name == "ChatHost") + return(ChatHost); + if(var_name == "ChatPort") + return(itoa(ChatPort)); + if(var_name == "MailHost") + return(MailHost); + if(var_name == "MailPort") + return(itoa(MailPort)); + if(var_name == "DatabaseHost") + return(DatabaseHost); + if(var_name == "DatabaseUsername") + return(DatabaseUsername); + if(var_name == "DatabasePassword") + return(DatabasePassword); + if(var_name == "DatabaseDB") + return(DatabaseDB); + if(var_name == "DatabasePort") + return(itoa(DatabasePort)); + if(var_name == "QSDatabaseHost") + return(QSDatabaseHost); + if(var_name == "QSDatabaseUsername") + return(QSDatabaseUsername); + if(var_name == "QSDatabasePassword") + return(QSDatabasePassword); + if(var_name == "QSDatabaseDB") + return(QSDatabaseDB); + if(var_name == "QSDatabasePort") + return(itoa(QSDatabasePort)); + if(var_name == "SpellsFile") + return(SpellsFile); + if(var_name == "OpCodesFile") + return(OpCodesFile); + if(var_name == "EQTimeFile") + return(EQTimeFile); + if(var_name == "LogSettingsFile") + return(LogSettingsFile); + if(var_name == "MapDir") + return(MapDir); + if(var_name == "QuestDir") + return(QuestDir); + if(var_name == "PluginDir") + return(PluginDir); + if(var_name == "LogPrefix") + return(LogPrefix); + if(var_name == "LogSuffix") + return(LogSuffix); + if(var_name == "ZoneExe") + return(ZoneExe); + if(var_name == "ZonePortLow") + return(itoa(ZonePortLow)); + if(var_name == "ZonePortHigh") + return(itoa(ZonePortHigh)); + if(var_name == "DefaultStatus") + return(itoa(DefaultStatus)); +// if(var_name == "DynamicCount") +// return(itoa(DynamicCount)); + return(""); +} + +void EQEmuConfig::Dump() const +{ + cout << "ShortName = " << ShortName << endl; + cout << "LongName = " << LongName << endl; + cout << "WorldAddress = " << WorldAddress << endl; + cout << "LoginHost = " << LoginHost << endl; + cout << "LoginAccount = " << LoginAccount << endl; + cout << "LoginPassword = " << LoginPassword << endl; + cout << "LoginPort = " << LoginPort << endl; + cout << "Locked = " << Locked << endl; + cout << "WorldTCPPort = " << WorldTCPPort << endl; + cout << "WorldIP = " << WorldIP << endl; + cout << "TelnetEnabled = " << TelnetEnabled << endl; + cout << "WorldHTTPPort = " << WorldHTTPPort << endl; + cout << "WorldHTTPMimeFile = " << WorldHTTPMimeFile << endl; + cout << "WorldHTTPEnabled = " << WorldHTTPEnabled << endl; + cout << "ChatHost = " << ChatHost << endl; + cout << "ChatPort = " << ChatPort << endl; + cout << "MailHost = " << MailHost << endl; + cout << "MailPort = " << MailPort << endl; + cout << "DatabaseHost = " << DatabaseHost << endl; + cout << "DatabaseUsername = " << DatabaseUsername << endl; + cout << "DatabasePassword = " << DatabasePassword << endl; + cout << "DatabaseDB = " << DatabaseDB << endl; + cout << "DatabasePort = " << DatabasePort << endl; + cout << "QSDatabaseHost = " << QSDatabaseHost << endl; + cout << "QSDatabaseUsername = " << QSDatabaseUsername << endl; + cout << "QSDatabasePassword = " << QSDatabasePassword << endl; + cout << "QSDatabaseDB = " << QSDatabaseDB << endl; + cout << "QSDatabasePort = " << QSDatabasePort << endl; + cout << "SpellsFile = " << SpellsFile << endl; + cout << "OpCodesFile = " << OpCodesFile << endl; + cout << "EQTimeFile = " << EQTimeFile << endl; + cout << "LogSettingsFile = " << LogSettingsFile << endl; + cout << "MapDir = " << MapDir << endl; + cout << "QuestDir = " << QuestDir << endl; + cout << "PluginDir = " << PluginDir << endl; + cout << "ZonePortLow = " << ZonePortLow << endl; + cout << "ZonePortHigh = " << ZonePortHigh << endl; + cout << "DefaultStatus = " << (int)DefaultStatus << endl; +// cout << "DynamicCount = " << DynamicCount << endl; +} + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/EQEmuConfig.h b/common/EQEmuConfig.h new file mode 100644 index 000000000..41856fa92 --- /dev/null +++ b/common/EQEmuConfig.h @@ -0,0 +1,227 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 __EQEmuConfig_H +#define __EQEmuConfig_H + +#include "XMLParser.h" +#include "linked_list.h" + +struct LoginConfig { + string LoginHost; + string LoginAccount; + string LoginPassword; + uint16 LoginPort; +}; + +class EQEmuConfig : public XMLParser { +public: + virtual string GetByName(const string &var_name) const; + + // From + string ShortName; + string LongName; + string WorldAddress; + string LocalAddress; + string LoginHost; + string LoginAccount; + string LoginPassword; + uint16 LoginPort; + uint32 LoginCount; + LinkedList loginlist; + bool Locked; + uint16 WorldTCPPort; + string WorldIP; + bool TelnetEnabled; + int32 MaxClients; + bool WorldHTTPEnabled; + uint16 WorldHTTPPort; + string WorldHTTPMimeFile; + string SharedKey; + + // From + string ChatHost; + uint16 ChatPort; + + // From + string MailHost; + uint16 MailPort; + + // From + string DatabaseHost; + string DatabaseUsername; + string DatabasePassword; + string DatabaseDB; + uint16 DatabasePort; + + // From // QueryServ + string QSDatabaseHost; + string QSDatabaseUsername; + string QSDatabasePassword; + string QSDatabaseDB; + uint16 QSDatabasePort; + + // From + string SpellsFile; + string OpCodesFile; + string EQTimeFile; + string LogSettingsFile; + + // From + string MapDir; + string QuestDir; + string PluginDir; + + // From + string LogPrefix; + string LogSuffix; + string ZoneExe; + uint32 RestartWait; + uint32 TerminateWait; + uint32 InitialBootWait; + uint32 ZoneBootInterval; + + // From + uint16 ZonePortLow; + uint16 ZonePortHigh; + uint8 DefaultStatus; + +// uint16 DynamicCount; + +// map StaticZones; + +protected: + + static EQEmuConfig *_config; + + static string ConfigFile; + +#define ELEMENT(name) \ + void do_##name(TiXmlElement *ele); + #include "EQEmuConfig_elements.h" + + + EQEmuConfig() { + // import the needed handler prototypes +#define ELEMENT(name) \ + Handlers[#name]=(ElementHandler)&EQEmuConfig::do_##name; + #include "EQEmuConfig_elements.h" + + // Set sane defaults + + // Login server + LoginHost="eqemulator.net"; + LoginPort=5998; + + // World + Locked=false; + WorldTCPPort=9000; + TelnetEnabled=false; + WorldHTTPEnabled=false; + WorldHTTPPort=9080; + WorldHTTPMimeFile="mime.types"; + SharedKey = ""; //blank disables authentication + + // Mail + ChatHost="eqchat.eqemulator.net"; + ChatPort=7778; + + // Mail + MailHost="eqmail.eqemulator.net"; + MailPort=7779; + + // Mysql + DatabaseHost="localhost"; + DatabasePort=3306; + DatabaseUsername="eq"; + DatabasePassword="eq"; + DatabaseDB="eq"; + + // QueryServ Database + QSDatabaseHost="localhost"; + QSDatabasePort=3306; + QSDatabaseUsername="eq"; + QSDatabasePassword="eq"; + QSDatabaseDB="eq"; + + // Files + SpellsFile="spells_us.txt"; + OpCodesFile="opcodes.conf"; + EQTimeFile="eqtime.cfg"; + LogSettingsFile="log.ini"; + + // Dirs + MapDir="Maps"; + QuestDir="quests"; + PluginDir="plugins"; + + // Launcher + LogPrefix = "logs/zone-"; + LogSuffix = ".log"; + RestartWait = 10000; //milliseconds + TerminateWait = 10000; //milliseconds + InitialBootWait = 20000; //milliseconds + ZoneBootInterval = 2000; //milliseconds +#ifdef WIN32 + ZoneExe = "zone.exe"; +#else + ZoneExe = "./zone"; +#endif + + // Zones + ZonePortLow=7000; + ZonePortHigh=7999; + DefaultStatus=0; + + // For where zones need to connect to. + WorldIP="127.0.0.1"; + + // Dynamics to start + //DynamicCount=5; + + MaxClients=-1; + + LoginCount=0; + + } + virtual ~EQEmuConfig() {} + +public: + + // Produce a const singleton + static const EQEmuConfig *get() { + if (_config == NULL) + LoadConfig(); + return(_config); + } + + // Allow the use to set the conf file to be used. + static void SetConfigFile(string file) { EQEmuConfig::ConfigFile = file; } + + // Load the config + static bool LoadConfig() { + if (_config != NULL) + delete _config; + _config=new EQEmuConfig; + + return _config->ParseFile(EQEmuConfig::ConfigFile.c_str(),"server"); + } + + void Dump() const; +}; + +#endif diff --git a/common/EQEmuConfig_elements.h b/common/EQEmuConfig_elements.h new file mode 100644 index 000000000..9dae490ae --- /dev/null +++ b/common/EQEmuConfig_elements.h @@ -0,0 +1,11 @@ +ELEMENT(world) +ELEMENT(chatserver) +ELEMENT(mailserver) +ELEMENT(zones) +ELEMENT(database) +ELEMENT(qsdatabase) +ELEMENT(files) +ELEMENT(directories) +ELEMENT(launcher) + +#undef ELEMENT diff --git a/common/EQNetwork.cpp b/common/EQNetwork.cpp new file mode 100644 index 000000000..1dfa9ddb2 --- /dev/null +++ b/common/EQNetwork.cpp @@ -0,0 +1,414 @@ +/* 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" + +using namespace std; + +#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; + } + } + + map ::iterator connection; + for (connection = connection_list.begin( ); connection != connection_list.end( );) + { + if(!connection->second) + { + map ::iterator tmp=connection; + connection++; + connection_list.erase(tmp); + continue; + } + EQStream* eqs_data = connection->second; + if (eqs_data->IsFree() && (!eqs_data->CheckNetActive())) { + map ::iterator tmp=connection; + connection++; + safe_delete(eqs_data); + connection_list.erase(tmp); + } + 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; + 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 new file mode 100644 index 000000000..71e288ab7 --- /dev/null +++ b/common/EQNetwork.h @@ -0,0 +1,121 @@ +/* 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 +using namespace std; + +#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; + + map connection_list; + queue NewQueue; +}; + +#endif diff --git a/common/EQPacket.cpp b/common/EQPacket.cpp new file mode 100644 index 000000000..20f80b3af --- /dev/null +++ b/common/EQPacket.cpp @@ -0,0 +1,511 @@ +/* + Copyright (C) 2005 Michael S. Finger + + 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 "debug.h" +#include +#include +#include +#include "EQPacket.h" +#include "misc.h" +#include "op_codes.h" +#include "CRC16.h" +#include "platform.h" +#ifndef STATIC_OPCODE +#include "opcodemgr.h" +#endif +#include "packet_dump.h" +#include "packet_functions.h" +#include +#include + +using namespace std; + +EQPacket::EQPacket(EmuOpcode op, const unsigned char *buf, uint32 len) +: BasePacket(buf, len), + emu_opcode(op) +{ +} + +void EQPacket::build_raw_header_dump(char *buffer, uint16 seq) const { + BasePacket::build_raw_header_dump(buffer, seq); + buffer += strlen(buffer); + + buffer += sprintf(buffer, "[EmuOpCode 0x%04x Size=%u]\n", emu_opcode, size); +} + +void EQPacket::DumpRawHeader(uint16 seq, FILE *to) const +{ + char buff[196]; + build_raw_header_dump(buff, seq); + fprintf(to, "%s", buff); +} + +void EQPacket::build_header_dump(char *buffer) const { + sprintf(buffer, "[EmuOpCode 0x%04x Size=%u]", emu_opcode, size); +} + +void EQPacket::DumpRawHeaderNoTime(uint16 seq, FILE *to) const +{ + if (src_ip) { + string sIP,dIP;; + sIP=long2ip(src_ip); + dIP=long2ip(dst_ip); + fprintf(to, "[%s:%d->%s:%d] ",sIP.c_str(),src_port,dIP.c_str(),dst_port); + } + if (seq != 0xffff) + fprintf(to, "[Seq=%u] ",seq); + + fprintf(to, "[EmuOpCode 0x%04x Size=%lu]\n",emu_opcode,(unsigned long)size); +} + +void EQProtocolPacket::build_raw_header_dump(char *buffer, uint16 seq) const +{ + BasePacket::build_raw_header_dump(buffer, seq); + buffer += strlen(buffer); + + buffer += sprintf(buffer, "[ProtoOpCode 0x%04x Size=%u]\n",opcode,size); +} + +void EQProtocolPacket::DumpRawHeader(uint16 seq, FILE *to) const +{ + char buff[196]; + build_raw_header_dump(buff, seq); + fprintf(to, "%s", buff); +} + +void EQProtocolPacket::build_header_dump(char *buffer) const +{ + sprintf(buffer, "[ProtoOpCode 0x%04x Size=%u]",opcode,size); +} + +void EQProtocolPacket::DumpRawHeaderNoTime(uint16 seq, FILE *to) const +{ + if (src_ip) { + string sIP,dIP;; + sIP=long2ip(src_ip); + dIP=long2ip(dst_ip); + fprintf(to, "[%s:%d->%s:%d] ",sIP.c_str(),src_port,dIP.c_str(),dst_port); + } + if (seq != 0xffff) + fprintf(to, "[Seq=%u] ",seq); + + fprintf(to, "[ProtoOpCode 0x%04x Size=%lu]\n",opcode,(unsigned long)size); +} + +void EQApplicationPacket::build_raw_header_dump(char *buffer, uint16 seq) const +{ + BasePacket::build_raw_header_dump(buffer, seq); + buffer += strlen(buffer); + +#ifdef STATIC_OPCODE + buffer += sprintf(buffer, "[OpCode 0x%04x Size=%u]\n", emu_opcode,size); +#else + buffer += sprintf(buffer, "[OpCode %s Size=%u]\n",OpcodeManager::EmuToName(emu_opcode),size); +#endif +} + +void EQApplicationPacket::DumpRawHeader(uint16 seq, FILE *to) const +{ + char buff[196]; + build_raw_header_dump(buff, seq); + fprintf(to, "%s", buff); +} + +void EQApplicationPacket::build_header_dump(char *buffer) const +{ +#ifdef STATIC_OPCODE + sprintf(buffer, "[OpCode 0x%04x Size=%u]\n", emu_opcode,size); +#else + sprintf(buffer, "[OpCode %s Size=%u]",OpcodeManager::EmuToName(emu_opcode),size); +#endif +} + +void EQApplicationPacket::DumpRawHeaderNoTime(uint16 seq, FILE *to) const +{ + if (src_ip) { + string sIP,dIP;; + sIP=long2ip(src_ip); + dIP=long2ip(dst_ip); + fprintf(to, "[%s:%d->%s:%d] ",sIP.c_str(),src_port,dIP.c_str(),dst_port); + } + if (seq != 0xffff) + fprintf(to, "[Seq=%u] ",seq); + +#ifdef STATIC_OPCODE + fprintf(to, "[OpCode 0x%04x Size=%u]\n", emu_opcode,size); +#else + fprintf(to, "[OpCode %s Size=%lu]\n",OpcodeManager::EmuToName(emu_opcode),(unsigned long)size); +#endif +} + +void EQRawApplicationPacket::build_raw_header_dump(char *buffer, uint16 seq) const +{ + BasePacket::build_raw_header_dump(buffer, seq); + buffer += strlen(buffer); + +#ifdef STATIC_OPCODE + buffer += sprintf(buffer, "[OpCode 0x%04x (0x%04x) Size=%u]\n", emu_opcode, opcode,size); +#else + buffer += sprintf(buffer, "[OpCode %s (0x%04x) Size=%u]\n", OpcodeManager::EmuToName(emu_opcode), opcode,size); +#endif +} + +void EQRawApplicationPacket::DumpRawHeader(uint16 seq, FILE *to) const +{ + char buff[196]; + build_raw_header_dump(buff, seq); + fprintf(to, "%s", buff); +} + +void EQRawApplicationPacket::build_header_dump(char *buffer) const +{ +#ifdef STATIC_OPCODE + sprintf(buffer, "[OpCode 0x%04x (0x%04x) Size=%u]\n", emu_opcode, opcode,size); +#else + sprintf(buffer, "[OpCode %s (0x%04x) Size=%u]", OpcodeManager::EmuToName(emu_opcode), opcode,size); +#endif +} + +void EQRawApplicationPacket::DumpRawHeaderNoTime(uint16 seq, FILE *to) const +{ + if (src_ip) { + string sIP,dIP;; + sIP=long2ip(src_ip); + dIP=long2ip(dst_ip); + fprintf(to, "[%s:%d->%s:%d] ",sIP.c_str(),src_port,dIP.c_str(),dst_port); + } + if (seq != 0xffff) + fprintf(to, "[Seq=%u] ",seq); + +#ifdef STATIC_OPCODE + fprintf(to, "[OpCode 0x%04x (0x%04x) Size=%u]\n", emu_opcode, opcode,size); +#else + fprintf(to, "[OpCode %s (0x%04x) Size=%lu]\n", OpcodeManager::EmuToName(emu_opcode), opcode,(unsigned long)size); +#endif +} + +uint32 EQProtocolPacket::serialize(unsigned char *dest) const +{ + if (opcode>0xff) { + *(uint16 *)dest=opcode; + } else { + *(dest)=0; + *(dest+1)=opcode; + } + memcpy(dest+2,pBuffer,size); + + return size+2; +} + +uint32 EQApplicationPacket::serialize(uint16 opcode, unsigned char *dest) const +{ + uint8 OpCodeBytes = app_opcode_size; + + if (app_opcode_size==1) + *(unsigned char *)dest = opcode; + else + { + // Application opcodes with a low order byte of 0x00 require an extra 0x00 byte inserting prior to the opcode. + if((opcode & 0x00ff) == 0) + { + *(uint8 *)dest = 0; + *(uint16 *)(dest + 1) = opcode; + ++OpCodeBytes; + } + else + *(uint16 *)dest = opcode; + } + memcpy(dest+OpCodeBytes,pBuffer,size); + + return size+OpCodeBytes; +} + +/*EQProtocolPacket::EQProtocolPacket(uint16 op, const unsigned char *buf, uint32 len) +: BasePacket(buf, len), + opcode(op) +{ + +uint32 offset; + opcode=ntohs(*(const uint16 *)buf); + offset=2; + + if (len-offset) { + pBuffer= new unsigned char[len-offset]; + memcpy(pBuffer,buf+offset,len-offset); + size=len-offset; + } else { + pBuffer=NULL; + size=0; + } + OpMgr=&RawOpcodeManager; +}*/ + +bool EQProtocolPacket::combine(const EQProtocolPacket *rhs) +{ +bool result=false; + if (opcode==OP_Combined && size+rhs->size+5<256) { + unsigned char *tmpbuffer=new unsigned char [size+rhs->size+3]; + memcpy(tmpbuffer,pBuffer,size); + uint32 offset=size; + tmpbuffer[offset++]=rhs->Size(); + offset+=rhs->serialize(tmpbuffer+offset); + size=offset; + delete[] pBuffer; + pBuffer=tmpbuffer; + result=true; + } else if (size+rhs->size+7<256) { + unsigned char *tmpbuffer=new unsigned char [size+rhs->size+6]; + uint32 offset=0; + tmpbuffer[offset++]=Size(); + offset+=serialize(tmpbuffer+offset); + tmpbuffer[offset++]=rhs->Size(); + offset+=rhs->serialize(tmpbuffer+offset); + size=offset; + delete[] pBuffer; + pBuffer=tmpbuffer; + opcode=OP_Combined; + result=true; + } + + return result; + +} + +/* +this is the code to do app-layer combining, instead of protocol layer. +this was taken out due to complex interactions with the opcode manager, +and will require a bit more thinking (likely moving into EQStream) to +get running again... but might be a good thing some day. + +bool EQApplicationPacket::combine(const EQApplicationPacket *rhs) +{ +uint32 newsize=0, offset=0; +unsigned char *tmpbuffer=NULL; + + if (opcode!=OP_AppCombined) { + newsize=app_opcode_size+size+(size>254?3:1)+app_opcode_size+rhs->size+(rhs->size>254?3:1); + tmpbuffer=new unsigned char [newsize]; + offset=0; + if (size>254) { + tmpbuffer[offset++]=0xff; + *(uint16 *)(tmpbuffer+offset)=htons(size); + offset+=1; + } else { + tmpbuffer[offset++]=size; + } + offset+=serialize(tmpbuffer+offset); + } else { + newsize=size+app_opcode_size+rhs->size+(rhs->size>254?3:1); + tmpbuffer=new unsigned char [newsize]; + memcpy(tmpbuffer,pBuffer,size); + offset=size; + } + + if (rhs->size>254) { + tmpbuffer[offset++]=0xff; + *(uint16 *)(tmpbuffer+offset)=htons(rhs->size); + offset+=1; + } else { + tmpbuffer[offset++]=rhs->size; + } + offset+=rhs->serialize(tmpbuffer+offset); + + size=offset; + opcode=OP_AppCombined; + + delete[] pBuffer; + pBuffer=tmpbuffer; + + return true; +} +*/ + +bool EQProtocolPacket::ValidateCRC(const unsigned char *buffer, int length, uint32 Key) +{ +bool valid=false; + // OP_SessionRequest, OP_SessionResponse, OP_OutOfSession are not CRC'd + if (buffer[0]==0x00 && (buffer[1]==OP_SessionRequest || buffer[1]==OP_SessionResponse || buffer[1]==OP_OutOfSession)) { + valid=true; + } else { + uint16 comp_crc=CRC16(buffer,length-2,Key); + uint16 packet_crc=ntohs(*(const uint16 *)(buffer+length-2)); +#ifdef EQN_DEBUG + if (packet_crc && comp_crc != packet_crc) { + cout << "CRC mismatch: comp=" << hex << comp_crc << ", packet=" << packet_crc << dec << endl; + } +#endif + valid = (!packet_crc || comp_crc == packet_crc); + } + return valid; +} + +uint32 EQProtocolPacket::Decompress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize) +{ +uint32 newlen=0; +uint32 flag_offset=0; + newbuf[0]=buffer[0]; + if (buffer[0]==0x00) { + flag_offset=2; + newbuf[1]=buffer[1]; + } else + flag_offset=1; + + if (length>2 && buffer[flag_offset]==0x5a) { + newlen=InflatePacket(buffer+flag_offset+1,length-(flag_offset+1)-2,newbuf+flag_offset,newbufsize-flag_offset)+2; + newbuf[newlen++]=buffer[length-2]; + newbuf[newlen++]=buffer[length-1]; + } else if (length>2 && buffer[flag_offset]==0xa5) { + memcpy(newbuf+flag_offset,buffer+flag_offset+1,length-(flag_offset+1)); + newlen=length-1; + } else { + memcpy(newbuf,buffer,length); + newlen=length; + } + + return newlen; +} + +uint32 EQProtocolPacket::Compress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize) { +uint32 flag_offset=1,newlength; + //dump_message_column(buffer,length,"Before: "); + newbuf[0]=buffer[0]; + if (buffer[0]==0) { + flag_offset=2; + newbuf[1]=buffer[1]; + } + if (length>30) { + newlength=DeflatePacket(buffer+flag_offset,length-flag_offset,newbuf+flag_offset+1,newbufsize); + *(newbuf+flag_offset)=0x5a; + newlength+=flag_offset+1; + } else { + memmove(newbuf+flag_offset+1,buffer+flag_offset,length-flag_offset); + *(newbuf+flag_offset)=0xa5; + newlength=length+1; + } + //dump_message_column(newbuf,length,"After: "); + + return newlength; +} + +void EQProtocolPacket::ChatDecode(unsigned char *buffer, int size, int DecodeKey) +{ + if ((size >= 2) && buffer[1]!=0x01 && buffer[0]!=0x02 && buffer[0]!=0x1d) { + int Key=DecodeKey; + unsigned char *test=(unsigned char *)malloc(size); + buffer+=2; + size-=2; + + int i; + for (i = 0 ; i+4 <= size ; i+=4) + { + int pt = (*(int*)&buffer[i])^(Key); + Key = (*(int*)&buffer[i]); + *(int*)&test[i]=pt; + } + unsigned char KC=Key&0xFF; + for ( ; i < size ; i++) + { + test[i]=buffer[i]^KC; + } + memcpy(buffer,test,size); + free(test); + } +} + +void EQProtocolPacket::ChatEncode(unsigned char *buffer, int size, int EncodeKey) +{ + if (buffer[1]!=0x01 && buffer[0]!=0x02 && buffer[0]!=0x1d) { + int Key=EncodeKey; + char *test=(char*)malloc(size); + int i; + buffer+=2; + size-=2; + for ( i = 0 ; i+4 <= size ; i+=4) + { + int pt = (*(int*)&buffer[i])^(Key); + Key = pt; + *(int*)&test[i]=pt; + } + unsigned char KC=Key&0xFF; + for ( ; i < size ; i++) + { + test[i]=buffer[i]^KC; + } + memcpy(buffer,test,size); + free(test); + } +} + +EQApplicationPacket *EQApplicationPacket::Copy() const { + return(new EQApplicationPacket(*this)); +} + +EQRawApplicationPacket *EQProtocolPacket::MakeAppPacket() const { + EQRawApplicationPacket *res = new EQRawApplicationPacket(opcode, pBuffer, size); + res->copyInfo(this); + return(res); +} + +EQRawApplicationPacket::EQRawApplicationPacket(uint16 opcode, const unsigned char *buf, const uint32 len) +: EQApplicationPacket(OP_Unknown, buf, len), + opcode(opcode) +{ +} +EQRawApplicationPacket::EQRawApplicationPacket(const unsigned char *buf, const uint32 len) +: EQApplicationPacket(OP_Unknown, buf+sizeof(uint16), len-sizeof(uint16)) +{ + if(GetExecutablePlatform() != ExePlatformUCS) { + opcode = *((const uint16 *) buf); + if(opcode == 0x0000) + { + if(len >= 3) + { + opcode = *((const uint16 *) (buf + 1)); + const unsigned char *packet_start = (buf + 3); + const int32 packet_length = len - 3; + safe_delete_array(pBuffer); + if(len >= 0) + { + size = packet_length; + pBuffer = new unsigned char[size]; + memcpy(pBuffer, packet_start, size); + } + else + { + size = 0; + } + } + else + { + safe_delete_array(pBuffer); + size = 0; + } + } + } else { + opcode = *((const uint8 *) buf); + } +} + +void DumpPacket(const EQApplicationPacket* app, bool iShowInfo) { + if (iShowInfo) { + cout << "Dumping Applayer: 0x" << hex << setfill('0') << setw(4) << app->GetOpcode() << dec; + cout << " size:" << app->size << endl; + } + DumpPacketHex(app->pBuffer, app->size); +// DumpPacketAscii(app->pBuffer, app->size); +} + diff --git a/common/EQPacket.h b/common/EQPacket.h new file mode 100644 index 000000000..675a35d1f --- /dev/null +++ b/common/EQPacket.h @@ -0,0 +1,155 @@ +/* + Copyright (C) 2005 Michael S. Finger + + 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 _EQPACKET_H +#define _EQPACKET_H + +#include "BasePacket.h" +#include "EQStreamType.h" +#include "op_codes.h" +#include "platform.h" + +#ifdef STATIC_OPCODE + typedef unsigned short EmuOpcode; + static const EmuOpcode OP_Unknown = 0; +#else +#include "emu_opcodes.h" +#endif + +using namespace std; + +class EQStream; +class EQStreamPair; + +class EQPacket : public BasePacket { + friend class EQStream; +public: + virtual ~EQPacket() {} + + uint32 Size() const { return size+2; } + + virtual void build_raw_header_dump(char *buffer, uint16 seq=0xffff) const; + virtual void build_header_dump(char *buffer) const; + virtual void DumpRawHeader(uint16 seq=0xffff, FILE *to = stdout) const; + virtual void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const; + + void SetOpcode(EmuOpcode op) { emu_opcode = op; } + const EmuOpcode GetOpcode() const { return(emu_opcode); } +// const char *GetOpcodeName() const; + +protected: + //this is just a cache so we dont look it up several times on Get() + //and it is mutable so we can store the cached copy even on a const object + EmuOpcode emu_opcode; + + EQPacket(EmuOpcode opcode, const unsigned char *buf, const uint32 len); +// EQPacket(const EQPacket &p) { } + EQPacket() { emu_opcode=OP_Unknown; pBuffer=NULL; size=0; } + +}; + +class EQRawApplicationPacket; + +class EQProtocolPacket : public BasePacket { + friend class EQStream; + friend class EQStreamPair; +public: + EQProtocolPacket(uint16 op, const unsigned char *buf, uint32 len) : BasePacket(buf,len), opcode(op) { acked = false; } +// EQProtocolPacket(const unsigned char *buf, uint32 len); + bool combine(const EQProtocolPacket *rhs); + uint32 serialize (unsigned char *dest) const; + EQProtocolPacket *Copy() { return new EQProtocolPacket(opcode,pBuffer,size); } + EQRawApplicationPacket *MakeAppPacket() const; + + bool acked; + + virtual void build_raw_header_dump(char *buffer, uint16 seq=0xffff) const; + virtual void build_header_dump(char *buffer) const; + virtual void DumpRawHeader(uint16 seq=0xffff, FILE *to = stdout) const; + virtual void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const; + +protected: + + static bool ValidateCRC(const unsigned char *buffer, int length, uint32 Key); + static uint32 Decompress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize); + static uint32 Compress(const unsigned char *buffer, const uint32 length, unsigned char *newbuf, uint32 newbufsize); + static void ChatDecode(unsigned char *buffer, int size, int DecodeKey); + static void ChatEncode(unsigned char *buffer, int size, int EncodeKey); + + uint16 GetRawOpcode() const { return(opcode); } + + uint32 Size() const { return size+2; } + + //the actual raw EQ opcode + uint16 opcode; +}; + +class EQApplicationPacket : public EQPacket { +// friend class EQProtocolPacket; + friend class EQStream; +public: + EQApplicationPacket() : EQPacket(OP_Unknown,NULL,0) + { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } + EQApplicationPacket(const EmuOpcode op) : EQPacket(op,NULL,0) + { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } + EQApplicationPacket(const EmuOpcode op, const uint32 len) : EQPacket(op,NULL,len) + { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } + EQApplicationPacket(const EmuOpcode op, const unsigned char *buf, const uint32 len) : EQPacket(op,buf,len) + { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } + bool combine(const EQApplicationPacket *rhs); + uint32 serialize (uint16 opcode, unsigned char *dest) const; + uint32 Size() const { return size+app_opcode_size; } + + virtual EQApplicationPacket *Copy() const; + + virtual void build_raw_header_dump(char *buffer, uint16 seq=0xffff) const; + virtual void build_header_dump(char *buffer) const; + virtual void DumpRawHeader(uint16 seq=0xffff, FILE *to = stdout) const; + virtual void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const; + +protected: + + uint8 app_opcode_size; +private: + + EQApplicationPacket(const EQApplicationPacket &p) : EQPacket(p.emu_opcode, p.pBuffer, p.size) { app_opcode_size = p.app_opcode_size; } + +}; + +class EQRawApplicationPacket : public EQApplicationPacket { + friend class EQStream; +public: + EQRawApplicationPacket(uint16 opcode, const unsigned char *buf, const uint32 len); + uint16 GetRawOpcode() const { return(opcode); } + + virtual void build_raw_header_dump(char *buffer, uint16 seq=0xffff) const; + virtual void build_header_dump(char *buffer) const; + virtual void DumpRawHeader(uint16 seq=0xffff, FILE *to = stdout) const; + virtual void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const; + +protected: + + //the actual raw EQ opcode + uint16 opcode; + + EQRawApplicationPacket(const unsigned char *buf, const uint32 len); +}; + +extern void DumpPacket(const EQApplicationPacket* app, bool iShowInfo = false); + + +#endif diff --git a/common/EQStream.cpp b/common/EQStream.cpp new file mode 100644 index 000000000..4d0bb7928 --- /dev/null +++ b/common/EQStream.cpp @@ -0,0 +1,1433 @@ +/* + Copyright (C) 2005 Michael S. Finger + + 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 "debug.h" +#include +#include +#include +#include +#include +#include +#ifdef _WINDOWS + #include +#else + #include + #include + #include + #include + #include + #include + #include +#endif +#include "EQPacket.h" +#include "EQStream.h" +//#include "EQStreamFactory.h" +#include "misc.h" +#include "Mutex.h" +#include "op_codes.h" +#include "CRC16.h" + +#if defined(ZONE) || defined(WORLD) + #define RETRANSMITS +#endif +#ifdef RETRANSMITS + #include "rulesys.h" +#endif + +//for logsys +#define _L "%s:%d: " +#define __L , long2ip(remote_ip).c_str(), ntohs(remote_port) + +uint16 EQStream::MaxWindowSize=2048; + +void EQStream::init() { + active_users = 0; + Session=0; + Key=0; + MaxLen=0; + NextInSeq=0; + NextOutSeq=0; + NextAckToSend=-1; + LastAckSent=-1; + MaxSends=5; + LastPacket=0; + oversize_buffer=NULL; + oversize_length=0; + oversize_offset=0; + RateThreshold=RATEBASE/250; + DecayRate=DECAYBASE/250; + BytesWritten=0; + SequencedBase = 0; + NextSequencedSend = 0; +#ifdef RETRANSMITS + retransmittimer = Timer::GetCurrentTime(); + retransmittimeout = 500 * RuleR(EQStream, RetransmitTimeoutMult); //use 500ms as base before we have connection stats +#endif + OpMgr = NULL; +if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + _log(NET__ERROR, _L "init Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); +} +if(NextSequencedSend > SequencedQueue.size()) { + _log(NET__ERROR, _L "init Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); +} +} + +EQRawApplicationPacket *EQStream::MakeApplicationPacket(EQProtocolPacket *p) +{ + EQRawApplicationPacket *ap=NULL; + _log(NET__APP_CREATE, _L "Creating new application packet, length %d" __L, p->size); + _raw(NET__APP_CREATE_HEX, 0xFFFF, p); + ap = p->MakeAppPacket(); + return ap; +} + +EQRawApplicationPacket *EQStream::MakeApplicationPacket(const unsigned char *buf, uint32 len) +{ + EQRawApplicationPacket *ap=NULL; + _log(NET__APP_CREATE, _L "Creating new application packet, length %d" __L, len); + _hex(NET__APP_CREATE_HEX, buf, len); + ap = new EQRawApplicationPacket(buf, len); + return ap; +} + +EQProtocolPacket *EQStream::MakeProtocolPacket(const unsigned char *buf, uint32 len) { + uint16 proto_opcode = ntohs(*(const uint16 *)buf); + + //advance over opcode. + buf += 2; + len -= 2; + + return(new EQProtocolPacket(proto_opcode, buf, len)); +} + +void EQStream::ProcessPacket(EQProtocolPacket *p) +{ +uint32 processed=0,subpacket_length=0; + if (p == NULL) + return; + // Raw Application packet + if (p->opcode > 0xff) { + p->opcode = htons(p->opcode); //byte order is backwards in the protocol packet + EQRawApplicationPacket *ap=MakeApplicationPacket(p); + if (ap) + InboundQueuePush(ap); + return; + } + + if (!Session && p->opcode!=OP_SessionRequest && p->opcode!=OP_SessionResponse) { + _log(NET__DEBUG, _L "Session not initialized, packet ignored" __L); + _raw(NET__DEBUG, 0xFFFF, p); + return; + } + + switch (p->opcode) { + case OP_Combined: { + processed=0; + while(processed < p->size) { + subpacket_length=*(p->pBuffer+processed); + EQProtocolPacket *subp=MakeProtocolPacket(p->pBuffer+processed+1,subpacket_length); + _log(NET__NET_CREATE, _L "Extracting combined packet of length %d" __L, subpacket_length); + _raw(NET__NET_CREATE_HEX, 0xFFFF, subp); + subp->copyInfo(p); + ProcessPacket(subp); + delete subp; + processed+=subpacket_length+1; + } + } + break; + + case OP_AppCombined: { + processed=0; + while(processedsize) { + EQRawApplicationPacket *ap=NULL; + if ((subpacket_length=(unsigned char)*(p->pBuffer+processed))!=0xff) { + _log(NET__NET_CREATE, _L "Extracting combined app packet of length %d, short len" __L, subpacket_length); + ap=MakeApplicationPacket(p->pBuffer+processed+1,subpacket_length); + processed+=subpacket_length+1; + } else { + subpacket_length=ntohs(*(uint16 *)(p->pBuffer+processed+1)); + _log(NET__NET_CREATE, _L "Extracting combined app packet of length %d, short len" __L, subpacket_length); + ap=MakeApplicationPacket(p->pBuffer+processed+3,subpacket_length); + processed+=subpacket_length+3; + } + if (ap) { + ap->copyInfo(p); + InboundQueuePush(ap); + } + } + } + break; + + case OP_Packet: { + uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); + SeqOrder check=CompareSequence(NextInSeq,seq); + if (check == SeqFuture) { + _log(NET__DEBUG, _L "Future OP_Packet: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); + _raw(NET__DEBUG, seq, p); + + PacketQueue[seq]=p->Copy(); + _log(NET__APP_TRACE, _L "OP_Packet Queue size=%d" __L, PacketQueue.size()); + + //SendOutOfOrderAck(seq); + + } else if (check == SeqPast) { + _log(NET__DEBUG, _L "Duplicate OP_Packet: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); + _raw(NET__DEBUG, seq, p); + SendOutOfOrderAck(seq); //we already got this packet but it was out of order + } else { + // In case we did queue one before as well. + EQProtocolPacket *qp=RemoveQueue(seq); + if (qp) { + _log(NET__NET_TRACE, "OP_Packet: Removing older queued packet with sequence %d", seq); + delete qp; + } + + SetNextAckToSend(seq); + NextInSeq++; + // Check for an embedded OP_AppCombinded (protocol level 0x19) + if (*(p->pBuffer+2)==0x00 && *(p->pBuffer+3)==0x19) { + EQProtocolPacket *subp=MakeProtocolPacket(p->pBuffer+2,p->size-2); + _log(NET__NET_CREATE, _L "seq %d, Extracting combined packet of length %d" __L, seq, subp->size); + _raw(NET__NET_CREATE_HEX, seq, subp); + subp->copyInfo(p); + ProcessPacket(subp); + delete subp; + } else { + EQRawApplicationPacket *ap=MakeApplicationPacket(p->pBuffer+2,p->size-2); + if (ap) { + ap->copyInfo(p); + InboundQueuePush(ap); + } + } + } + } + break; + + case OP_Fragment: { + uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); + SeqOrder check=CompareSequence(NextInSeq,seq); + if (check == SeqFuture) { + _log(NET__DEBUG, _L "Future OP_Fragment: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); + _raw(NET__DEBUG, seq, p); + + PacketQueue[seq]=p->Copy(); + _log(NET__APP_TRACE, _L "OP_Fragment Queue size=%d" __L, PacketQueue.size()); + + //SendOutOfOrderAck(seq); + + } else if (check == SeqPast) { + _log(NET__DEBUG, _L "Duplicate OP_Fragment: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); + _raw(NET__DEBUG, seq, p); + SendOutOfOrderAck(seq); + } else { + // In case we did queue one before as well. + EQProtocolPacket *qp=RemoveQueue(seq); + if (qp) { + _log(NET__NET_TRACE, "OP_Fragment: Removing older queued packet with sequence %d", seq); + delete qp; + } + SetNextAckToSend(seq); + NextInSeq++; + if (oversize_buffer) { + memcpy(oversize_buffer+oversize_offset,p->pBuffer+2,p->size-2); + oversize_offset+=p->size-2; + _log(NET__NET_TRACE, _L "Fragment of oversized of length %d, seq %d: now at %d/%d" __L, p->size-2, seq, oversize_offset, oversize_length); + if (oversize_offset==oversize_length) { + if (*(p->pBuffer+2)==0x00 && *(p->pBuffer+3)==0x19) { + EQProtocolPacket *subp=MakeProtocolPacket(oversize_buffer,oversize_offset); + _log(NET__NET_CREATE, _L "seq %d, Extracting combined oversize packet of length %d" __L, seq, subp->size); + //_raw(NET__NET_CREATE_HEX, subp); + subp->copyInfo(p); + ProcessPacket(subp); + delete subp; + } else { + EQRawApplicationPacket *ap=MakeApplicationPacket(oversize_buffer,oversize_offset); + _log(NET__NET_CREATE, _L "seq %d, completed combined oversize packet of length %d" __L, seq, ap->size); + if (ap) { + ap->copyInfo(p); + InboundQueuePush(ap); + } + } + delete[] oversize_buffer; + oversize_buffer=NULL; + oversize_offset=0; + } + } else { + oversize_length=ntohl(*(uint32 *)(p->pBuffer+2)); + oversize_buffer=new unsigned char[oversize_length]; + memcpy(oversize_buffer,p->pBuffer+6,p->size-6); + oversize_offset=p->size-6; + _log(NET__NET_TRACE, _L "First fragment of oversized of seq %d: now at %d/%d" __L, seq, oversize_offset, oversize_length); + } + } + } + break; + case OP_KeepAlive: { +#ifndef COLLECTOR + NonSequencedPush(new EQProtocolPacket(p->opcode,p->pBuffer,p->size)); + _log(NET__NET_TRACE, _L "Received and queued reply to keep alive" __L); +#endif + } + break; + case OP_Ack: { +#ifndef COLLECTOR + uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); + AckPackets(seq); +#ifdef RETRANSMITS + retransmittimer = Timer::GetCurrentTime(); +#endif +#endif + } + break; + case OP_SessionRequest: { + if(p->Size() < sizeof(SessionRequest)) + { + _log(NET__ERROR, _L "Received OP_SessionRequest that was of malformed size" __L); + break; + } +#ifndef COLLECTOR + if (GetState()==ESTABLISHED) { + _log(NET__ERROR, _L "Received OP_SessionRequest in ESTABLISHED state (%d)" __L, GetState()); + + /*RemoveData(); + init(); + State=UNESTABLISHED;*/ + _SendDisconnect(); + SetState(CLOSED); + break; + } +#endif + //cout << "Got OP_SessionRequest" << endl; + init(); + OutboundQueueClear(); + SessionRequest *Request=(SessionRequest *)p->pBuffer; + Session=ntohl(Request->Session); + SetMaxLen(ntohl(Request->MaxLength)); + _log(NET__NET_TRACE, _L "Received OP_SessionRequest: session %lu, maxlen %d" __L, (unsigned long)Session, MaxLen); + SetState(ESTABLISHED); +#ifndef COLLECTOR + Key=0x11223344; + SendSessionResponse(); +#endif + } + break; + case OP_SessionResponse: { + if(p->Size() < sizeof(SessionResponse)) + { + _log(NET__ERROR, _L "Received OP_SessionResponse that was of malformed size" __L); + break; + } + + init(); + OutboundQueueClear(); + SessionResponse *Response=(SessionResponse *)p->pBuffer; + SetMaxLen(ntohl(Response->MaxLength)); + Key=ntohl(Response->Key); + NextInSeq=0; + SetState(ESTABLISHED); + if (!Session) + Session=ntohl(Response->Session); + compressed=(Response->Format&FLAG_COMPRESSED); + encoded=(Response->Format&FLAG_ENCODED); + + _log(NET__NET_TRACE, _L "Received OP_SessionResponse: session %lu, maxlen %d, key %lu, compressed? %s, encoded? %s" __L, (unsigned long)Session, MaxLen, (unsigned long)Key, compressed?"yes":"no", encoded?"yes":"no"); + + // Kinda kludgy, but trie for now + if (StreamType==UnknownStream) { + if (compressed) { + if (remote_port==9000 || (remote_port==0 && p->src_port==9000)) { + SetStreamType(WorldStream); + } else { + SetStreamType(ZoneStream); + } + } else if (encoded) { + SetStreamType(ChatOrMailStream); + } else { + SetStreamType(LoginStream); + } + } + } + break; + case OP_SessionDisconnect: { + //NextInSeq=0; + EQStreamState state = GetState(); + if(state == ESTABLISHED) { + //client initiated disconnect? + _log(NET__NET_TRACE, _L "Received unsolicited OP_SessionDisconnect. Treating like a client-initiated disconnect." __L); + _SendDisconnect(); + SetState(CLOSED); + } else if(state == CLOSING) { + //we were waiting for this anyways, ignore pending messages, send the reply and be closed. + _log(NET__NET_TRACE, _L "Received OP_SessionDisconnect when we have a pending close, they beat us to it. Were happy though." __L); + _SendDisconnect(); + SetState(CLOSED); + } else { + //we are expecting this (or have already gotten it, but dont care either way) + _log(NET__NET_TRACE, _L "Received expected OP_SessionDisconnect. Moving to closed state." __L); + SetState(CLOSED); + } + } + break; + case OP_OutOfOrderAck: { +#ifndef COLLECTOR + uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); + MOutboundQueue.lock(); + +if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + _log(NET__ERROR, _L "Pre-OOA Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); +} +if(NextSequencedSend > SequencedQueue.size()) { + _log(NET__ERROR, _L "Pre-OOA Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); +} + //if the packet they got out of order is between our last acked packet and the last sent packet, then its valid. + if (CompareSequence(SequencedBase,seq) != SeqPast && CompareSequence(NextOutSeq,seq) == SeqPast) { + _log(NET__NET_TRACE, _L "Received OP_OutOfOrderAck for sequence %d, starting retransmit at the start of our unacked buffer (seq %d, was %d)." __L, + seq, SequencedBase, SequencedBase+NextSequencedSend); +#ifdef RETRANSMITS + if (!RuleB(EQStream, RetransmitAckedPackets)) { +#endif + uint16 sqsize = SequencedQueue.size(); + uint16 index = seq - SequencedBase; + _log(NET__NET_TRACE, _L " OP_OutOfOrderAck marking packet acked in queue (queue index = %d, queue size = %d)." __L, index, sqsize); + if (index < sqsize) { + deque::iterator sitr; + sitr = SequencedQueue.begin(); + sitr += index; + (*sitr)->acked = true; + } +#ifdef RETRANSMITS + } + if (RuleR(EQStream, RetransmitTimeoutMult)) { // only choose new behavior if multiplier is set + retransmittimer = Timer::GetCurrentTime(); + } +#endif + NextSequencedSend = 0; + } else { + _log(NET__NET_TRACE, _L "Received OP_OutOfOrderAck for out-of-window %d. Window (%d->%d)." __L, seq, SequencedBase, NextOutSeq); + } +if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + _log(NET__ERROR, _L "Post-OOA Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); +} +if(NextSequencedSend > SequencedQueue.size()) { + _log(NET__ERROR, _L "Post-OOA Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); +} + MOutboundQueue.unlock(); +#endif + } + break; + case OP_SessionStatRequest: { + if(p->Size() < sizeof(SessionStats)) + { + _log(NET__ERROR, _L "Received OP_SessionStatRequest that was of malformed size" __L); + break; + } +#ifndef COLLECTOR + SessionStats *Stats=(SessionStats *)p->pBuffer; + _log(NET__NET_TRACE, _L "Received Stats: %lu packets received, %lu packets sent, Deltas: local %lu, (%lu <- %lu -> %lu) remote %lu" __L, + (unsigned long)ntohl(Stats->packets_recieved), (unsigned long)ntohl(Stats->packets_sent), (unsigned long)ntohl(Stats->last_local_delta), + (unsigned long)ntohl(Stats->low_delta), (unsigned long)ntohl(Stats->average_delta), + (unsigned long)ntohl(Stats->high_delta), (unsigned long)ntohl(Stats->last_remote_delta)); + uint64 x=Stats->packets_recieved; + Stats->packets_recieved=Stats->packets_sent; + Stats->packets_sent=x; + NonSequencedPush(new EQProtocolPacket(OP_SessionStatResponse,p->pBuffer,p->size)); + AdjustRates(ntohl(Stats->average_delta)); +#ifdef RETRANSMITS + if (RuleR(EQStream, RetransmitTimeoutMult) && ntohl(Stats->average_delta)) { + //recalculate retransmittimeout using the larger of the last rtt or average rtt, which is multiplied by the rule value + if((ntohl(Stats->last_local_delta) + ntohl(Stats->last_remote_delta)) > (ntohl(Stats->average_delta) * 2)) { + retransmittimeout = (ntohl(Stats->last_local_delta) + ntohl(Stats->last_remote_delta)) * RuleR(EQStream, RetransmitTimeoutMult); + } else { + retransmittimeout = ntohl(Stats->average_delta) * 2 * RuleR(EQStream, RetransmitTimeoutMult); + } + if(retransmittimeout > RuleI(EQStream, RetransmitTimeoutMax)) + retransmittimeout = RuleI(EQStream, RetransmitTimeoutMax); + _log(NET__NET_TRACE, _L "Retransmit timeout recalculated to %dms" __L, retransmittimeout); + } +#endif +#endif + } + break; + case OP_SessionStatResponse: { + _log(NET__NET_TRACE, _L "Received OP_SessionStatResponse. Ignoring." __L); + } + break; + case OP_OutOfSession: { + _log(NET__NET_TRACE, _L "Received OP_OutOfSession. Ignoring." __L); + } + break; + default: + EQRawApplicationPacket *ap = MakeApplicationPacket(p); + if (ap) + InboundQueuePush(ap); + break; + } +} + +void EQStream::QueuePacket(const EQApplicationPacket *p, bool ack_req) +{ + if(p == NULL) + return; + + EQApplicationPacket *newp = p->Copy(); + + if (newp != NULL) + FastQueuePacket(&newp, ack_req); +} + +void EQStream::FastQueuePacket(EQApplicationPacket **p, bool ack_req) +{ + EQApplicationPacket *pack=*p; + *p = NULL; //clear caller's pointer.. effectively takes ownership + + if(pack == NULL) + return; + + if(OpMgr == NULL || *OpMgr == NULL) { + _log(NET__DEBUG, _L "Packet enqueued into a stream with no opcode manager, dropping." __L); + delete pack; + return; + } + + uint16 opcode = (*OpMgr)->EmuToEQ(pack->emu_opcode); + + //make sure this packet is compatible with this stream +/* if(StreamType == UnknownStream || StreamType == ChatOrMailStream) { + _log(NET__DEBUG, _L "Stream type undetermined (%s), packet ignored" __L, StreamTypeString(StreamType)); + return; + } + if(pack->GetPacketType() != StreamType) { + _log(NET__ERROR, _L "Trying to queue a packet of type %s into a stream of type %s, dropping it." __L, StreamTypeString(pack->GetPacketType()), StreamTypeString(StreamType)); + return; + }*/ + + _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); + + if (!ack_req) { + NonSequencedPush(new EQProtocolPacket(opcode, pack->pBuffer, pack->size)); + delete pack; + } else { + SendPacket(opcode, pack); + } +} + +void EQStream::SendPacket(uint16 opcode, EQApplicationPacket *p) +{ +uint32 chunksize,used; +uint32 length; + + // Convert the EQApplicationPacket to 1 or more EQProtocolPackets + if (p->size>(MaxLen-8)) { // proto-op(2), seq(2), app-op(2) ... data ... crc(2) + _log(NET__FRAGMENT, _L "Making oversized packet, len %d" __L, p->size); + + unsigned char *tmpbuff=new unsigned char[p->size+3]; + length=p->serialize(opcode, tmpbuff); + + EQProtocolPacket *out=new EQProtocolPacket(OP_Fragment,NULL,MaxLen-4); + *(uint32 *)(out->pBuffer+2)=htonl(p->Size()); + used=MaxLen-10; + memcpy(out->pBuffer+6,tmpbuff,used); + _log(NET__FRAGMENT, _L "First fragment: used %d/%d. Put size %d in the packet" __L, used, p->size, p->Size()); + SequencedPush(out); + + + while (usedpBuffer+2,tmpbuff+used,chunksize); + out->size=chunksize+2; + SequencedPush(out); + used+=chunksize; + _log(NET__FRAGMENT, _L "Subsequent fragment: len %d, used %d/%d." __L, chunksize, used, p->size); + } + delete p; + delete[] tmpbuff; + } else { + + unsigned char *tmpbuff=new unsigned char[p->Size()+3]; + length=p->serialize(opcode, tmpbuff+2) + 2; + + EQProtocolPacket *out=new EQProtocolPacket(OP_Packet,tmpbuff,length); + + delete[] tmpbuff; + SequencedPush(out); + delete p; + } +} + +void EQStream::SequencedPush(EQProtocolPacket *p) +{ +#ifdef COLLECTOR + delete p; +#else + MOutboundQueue.lock(); +if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + _log(NET__ERROR, _L "Pre-Push Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); +} +if(NextSequencedSend > SequencedQueue.size()) { + _log(NET__ERROR, _L "Pre-Push Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); +} + + _log(NET__APP_TRACE, _L "Pushing sequenced packet %d of length %d. Base Seq is %d." __L, NextOutSeq, p->size, SequencedBase); + *(uint16 *)(p->pBuffer)=htons(NextOutSeq); + SequencedQueue.push_back(p); + NextOutSeq++; + +if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + _log(NET__ERROR, _L "Push Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); +} +if(NextSequencedSend > SequencedQueue.size()) { + _log(NET__ERROR, _L "Push Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); +} + MOutboundQueue.unlock(); +#endif +} + +void EQStream::NonSequencedPush(EQProtocolPacket *p) +{ +#ifdef COLLECTOR + delete p; +#else + MOutboundQueue.lock(); + _log(NET__APP_TRACE, _L "Pushing non-sequenced packet of length %d" __L, p->size); + NonSequencedQueue.push(p); + MOutboundQueue.unlock(); +#endif +} + +void EQStream::SendAck(uint16 seq) +{ +uint16 Seq=htons(seq); + _log(NET__NET_ACKS, _L "Sending ack with sequence %d" __L, seq); + SetLastAckSent(seq); + NonSequencedPush(new EQProtocolPacket(OP_Ack,(unsigned char *)&Seq,sizeof(uint16))); +} + +void EQStream::SendOutOfOrderAck(uint16 seq) +{ + _log(NET__APP_TRACE, _L "Sending out of order ack with sequence %d" __L, seq); +uint16 Seq=htons(seq); + NonSequencedPush(new EQProtocolPacket(OP_OutOfOrderAck,(unsigned char *)&Seq,sizeof(uint16))); +} + +void EQStream::Write(int eq_fd) +{ +queue ReadyToSend; +bool SeqEmpty=false,NonSeqEmpty=false; +deque::iterator sitr; + + // Check our rate to make sure we can send more + MRate.lock(); + int32 threshold=RateThreshold; + MRate.unlock(); + if (BytesWritten > threshold) { + //cout << "Over threshold: " << BytesWritten << " > " << threshold << endl; + return; + } + + // If we got more packets to we need to ack, send an ack on the highest one + MAcks.lock(); + if (CompareSequence(LastAckSent, NextAckToSend) == SeqFuture) + SendAck(NextAckToSend); + MAcks.unlock(); + + // Lock the outbound queues while we process + MOutboundQueue.lock(); + + // Place to hold the base packet t combine into + EQProtocolPacket *p=NULL; + +#ifdef RETRANSMITS + // if we have a timeout defined and we have not received an ack recently enough, retransmit from beginning of queue + if (RuleR(EQStream, RetransmitTimeoutMult) && !SequencedQueue.empty() && NextSequencedSend && (GetState()==ESTABLISHED) && ((retransmittimer+retransmittimeout) < Timer::GetCurrentTime())) { + _log(NET__NET_TRACE, _L "Timeout since last ack received, starting retransmit at the start of our unacked buffer (seq %d, was %d)." __L, SequencedBase, SequencedBase+NextSequencedSend); + NextSequencedSend = 0; + retransmittimer = Timer::GetCurrentTime(); // don't want to endlessly retransmit the first packet + } +#endif + + // Find the next sequenced packet to send from the "queue" + sitr = SequencedQueue.begin(); + if (sitr!=SequencedQueue.end()) + sitr += NextSequencedSend; + + // Loop until both are empty or MaxSends is reached + while(!SeqEmpty || !NonSeqEmpty) { + + // See if there are more non-sequenced packets left + if (!NonSequencedQueue.empty()) { + if (!p) { + // If we don't have a packet to try to combine into, use this one as the base + // And remove it form the queue + p = NonSequencedQueue.front(); + _log(NET__NET_COMBINE, _L "Starting combined packet with non-seq packet of len %d" __L, p->size); + NonSequencedQueue.pop(); + } else if (!p->combine(NonSequencedQueue.front())) { + // Tryint to combine this packet with the base didn't work (too big maybe) + // So just send the base packet (we'll try this packet again later) + _log(NET__NET_COMBINE, _L "Combined packet full at len %d, next non-seq packet is len %d" __L, p->size, (NonSequencedQueue.front())->size); + ReadyToSend.push(p); + BytesWritten+=p->size; + p=NULL; + + if (BytesWritten > threshold) { + // Sent enough this round, lets stop to be fair + _log(NET__RATES, _L "Exceeded write threshold in nonseq (%d > %d)" __L, BytesWritten, threshold); + break; + } + } else { + // Combine worked, so just remove this packet and it's spot in the queue + _log(NET__NET_COMBINE, _L "Combined non-seq packet of len %d, yeilding %d combined." __L, (NonSequencedQueue.front())->size, p->size); + delete NonSequencedQueue.front(); + NonSequencedQueue.pop(); + } + } else { + // No more non-sequenced packets + NonSeqEmpty=true; + } + + if (sitr!=SequencedQueue.end()) { +//_log(NET__NET_COMBINE, _L "Send Seq with %d seq packets starting at seq %d, next send %d, and %d non-seq packets." __L, +// SequencedQueue.size(), SequencedBase, NextSequencedSend, NonSequencedQueue.size()); +if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + _log(NET__ERROR, _L "Pre-Send Seq NSS=%d Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, NextSequencedSend, SequencedBase, SequencedQueue.size(), NextOutSeq); +} +if(NextSequencedSend > SequencedQueue.size()) { + _log(NET__ERROR, _L "Pre-Send Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); +} + uint16 seq_send = SequencedBase + NextSequencedSend; //just for logging... +if(SequencedQueue.empty()) { +_log(NET__ERROR, _L "Tried to write a packet with an empty queue (%d is past next out %d)" __L, seq_send, NextOutSeq); +SeqEmpty=true; +continue; +} +/*if(CompareSequence(NextOutSeq, seq_send) == SeqFuture) { +_log(NET__ERROR, _L "Tried to write a packet beyond the end of the queue! (%d is past next out %d)" __L, seq_send, NextOutSeq); +sitr=SequencedQueue.end(); +continue; +}*/ +#ifdef RETRANSMITS + if (!RuleB(EQStream, RetransmitAckedPackets) && (*sitr)->acked) { + _log(NET__NET_TRACE, _L "Not retransmitting seq packet %d because already marked as acked" __L, seq_send); + sitr++; + NextSequencedSend++; + } else if (!p) { +#else + if (!p) { +#endif + // If we don't have a packet to try to combine into, use this one as the base + // Copy it first as it will still live until it is acked + p=(*sitr)->Copy(); + _log(NET__NET_COMBINE, _L "Starting combined packet with seq packet %d of len %d" __L, seq_send, p->size); + sitr++; + NextSequencedSend++; + } else if (!p->combine(*sitr)) { + // Trying to combine this packet with the base didn't work (too big maybe) + // So just send the base packet (we'll try this packet again later) + _log(NET__NET_COMBINE, _L "Combined packet full at len %d, next seq packet %d is len %d" __L, p->size, seq_send, (*sitr)->size); + ReadyToSend.push(p); + BytesWritten+=p->size; + p=NULL; + + if (BytesWritten > threshold) { + // Sent enough this round, lets stop to be fair + _log(NET__RATES, _L "Exceeded write threshold in seq (%d > %d)" __L, BytesWritten, threshold); + break; + } + } else { + // Combine worked + _log(NET__NET_COMBINE, _L "Combined seq packet %d of len %d, yeilding %d combined." __L, seq_send, (*sitr)->size, p->size); + sitr++; + NextSequencedSend++; + } +if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + _log(NET__ERROR, _L "Post send Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); +} +if(NextSequencedSend > SequencedQueue.size()) { + _log(NET__ERROR, _L "Post send Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); +} + } else { + // No more sequenced packets + SeqEmpty=true; + } + } + // Unlock the queue + MOutboundQueue.unlock(); + + // We have a packet still, must have run out of both seq and non-seq, so send it + if (p) { + _log(NET__NET_COMBINE, _L "Final combined packet not full, len %d" __L, p->size); + ReadyToSend.push(p); + BytesWritten+=p->size; + } + + // Send all the packets we "made" + while(!ReadyToSend.empty()) { + p = ReadyToSend.front(); + WritePacket(eq_fd,p); + delete p; + ReadyToSend.pop(); + } + + //see if we need to send our disconnect and finish our close + if(SeqEmpty && NonSeqEmpty) { + //no more data to send + if(CheckState(CLOSING)) { + _log(NET__DEBUG, _L "All outgoing data flushed, closing stream." __L ); + //we are waiting for the queues to empty, now we can do our disconnect. + //this packet will not actually go out until the next call to Write(). + _SendDisconnect(); + SetState(DISCONNECTING); + } + } +} + +void EQStream::WritePacket(int eq_fd, EQProtocolPacket *p) +{ +uint32 length; +sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr=remote_ip; + address.sin_port=remote_port; +#ifdef NOWAY + uint32 ip=address.sin_addr.s_addr; + cout << "Sending to: " + << (int)*(unsigned char *)&ip + << "." << (int)*((unsigned char *)&ip+1) + << "." << (int)*((unsigned char *)&ip+2) + << "." << (int)*((unsigned char *)&ip+3) + << "," << (int)ntohs(address.sin_port) << "(" << p->size << ")" << endl; + + p->DumpRaw(); + cout << "-------------" << endl; +#endif + length=p->serialize(buffer); + if (p->opcode!=OP_SessionRequest && p->opcode!=OP_SessionResponse) { + if (compressed) { + uint32 newlen=EQProtocolPacket::Compress(buffer,length, _tempBuffer, 2048); + memcpy(buffer,_tempBuffer,newlen); + length=newlen; + } + if (encoded) { + EQProtocolPacket::ChatEncode(buffer,length,Key); + } + + *(uint16 *)(buffer+length)=htons(CRC16(buffer,length,Key)); + length+=2; + } + //dump_message_column(buffer,length,"Writer: "); + sendto(eq_fd,(char *)buffer,length,0,(sockaddr *)&address,sizeof(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=NULL; +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)); + //cout << timestamp() << "Data from: " << temp << " OpCode 0x" << hex << setw(2) << setfill('0') << (int)p->opcode << dec << endl; + //dump_message(p->pBuffer,p->size,timestamp()); + + } + return p; +}*/ + +void EQStream::SendSessionResponse() +{ +EQProtocolPacket *out=new EQProtocolPacket(OP_SessionResponse,NULL,sizeof(SessionResponse)); + SessionResponse *Response=(SessionResponse *)out->pBuffer; + Response->Session=htonl(Session); + Response->MaxLength=htonl(MaxLen); + Response->UnknownA=2; + Response->Format=0; + if (compressed) + Response->Format|=FLAG_COMPRESSED; + if (encoded) + Response->Format|=FLAG_ENCODED; + Response->Key=htonl(Key); + + out->size=sizeof(SessionResponse); + + _log(NET__NET_TRACE, _L "Sending OP_SessionResponse: session %lu, maxlen=%d, key=0x%x, compressed? %s, encoded? %s" __L, + (unsigned long)Session, MaxLen, Key, compressed?"yes":"no", encoded?"yes":"no"); + + NonSequencedPush(out); +} + +void EQStream::SendSessionRequest() +{ +EQProtocolPacket *out=new EQProtocolPacket(OP_SessionRequest,NULL,sizeof(SessionRequest)); + SessionRequest *Request=(SessionRequest *)out->pBuffer; + memset(Request,0,sizeof(SessionRequest)); + Request->Session=htonl(time(NULL)); + Request->MaxLength=htonl(512); + + _log(NET__NET_TRACE, _L "Sending OP_SessionRequest: session %lu, maxlen=%d" __L, (unsigned long)ntohl(Request->Session), ntohl(Request->MaxLength)); + + NonSequencedPush(out); +} + +void EQStream::_SendDisconnect() +{ + if(GetState() == CLOSED) + return; + + EQProtocolPacket *out=new EQProtocolPacket(OP_SessionDisconnect,NULL,sizeof(uint32)); + *(uint32 *)out->pBuffer=htonl(Session); + NonSequencedPush(out); + + _log(NET__NET_TRACE, _L "Sending OP_SessionDisconnect: session %lu" __L, (unsigned long)Session); +} + +void EQStream::InboundQueuePush(EQRawApplicationPacket *p) +{ + MInboundQueue.lock(); + InboundQueue.push_back(p); + MInboundQueue.unlock(); +} + +EQApplicationPacket *EQStream::PopPacket() +{ +EQRawApplicationPacket *p=NULL; + + MInboundQueue.lock(); + if (InboundQueue.size()) { + vector::iterator itr=InboundQueue.begin(); + p=*itr; + InboundQueue.erase(itr); + } + MInboundQueue.unlock(); + + //resolve the opcode if we can. + if(p) { + if(OpMgr != NULL && *OpMgr != NULL) { + EmuOpcode emu_op = (*OpMgr)->EQToEmu(p->opcode); +#if EQDEBUG >= 4 + if(emu_op == OP_Unknown) { + LogFile->write(EQEMuLog::Debug, "Unable to convert EQ opcode 0x%.4x to an Application opcode.", p->opcode); + } +#endif + p->SetOpcode(emu_op); + } + } + + return p; +} + +EQRawApplicationPacket *EQStream::PopRawPacket() +{ +EQRawApplicationPacket *p=NULL; + + MInboundQueue.lock(); + if (InboundQueue.size()) { + vector::iterator itr=InboundQueue.begin(); + p=*itr; + InboundQueue.erase(itr); + } + MInboundQueue.unlock(); + + //resolve the opcode if we can. + if(p) { + if(OpMgr != NULL && *OpMgr != NULL) { + EmuOpcode emu_op = (*OpMgr)->EQToEmu(p->opcode); +#if EQDEBUG >= 4 + if(emu_op == OP_Unknown) { + LogFile->write(EQEMuLog::Debug, "Unable to convert EQ opcode 0x%.4x to an Application opcode.", p->opcode); + } +#endif + p->SetOpcode(emu_op); + } + } + + return p; +} + +EQRawApplicationPacket *EQStream::PeekPacket() +{ +EQRawApplicationPacket *p=NULL; + + MInboundQueue.lock(); + if (InboundQueue.size()) { + vector::iterator itr=InboundQueue.begin(); + p=*itr; + } + MInboundQueue.unlock(); + + return p; +} + +void EQStream::InboundQueueClear() +{ +EQApplicationPacket *p=NULL; + + _log(NET__APP_TRACE, _L "Clearing inbound queue" __L); + + MInboundQueue.lock(); + if (!InboundQueue.empty()) { + vector::iterator itr; + for(itr=InboundQueue.begin();itr!=InboundQueue.end();itr++) { + p=*itr; + delete p; + } + InboundQueue.clear(); + } + MInboundQueue.unlock(); +} + +bool EQStream::HasOutgoingData() +{ +bool flag; + + //once closed, we have nothing more to say + if(CheckClosed()) + return(false); + + MOutboundQueue.lock(); + flag=(!NonSequencedQueue.empty()); + if (!flag) { + //not only wait until we send it all, but wait until they ack everything. + flag = !SequencedQueue.empty(); + } + MOutboundQueue.unlock(); + + if (!flag) { + MAcks.lock(); + flag= (NextAckToSend>LastAckSent); + MAcks.unlock(); + } + + return flag; +} + +void EQStream::OutboundQueueClear() +{ +EQProtocolPacket *p=NULL; + + _log(NET__APP_TRACE, _L "Clearing outbound queue" __L); + + MOutboundQueue.lock(); + while(!NonSequencedQueue.empty()) { + delete NonSequencedQueue.front(); + NonSequencedQueue.pop(); + } + if(!SequencedQueue.empty()) { + deque::iterator itr; + for(itr=SequencedQueue.begin();itr!=SequencedQueue.end();itr++) { + p=*itr; + delete p; + } + 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() +{ +EQProtocolPacket *p=NULL; + + _log(NET__APP_TRACE, _L "Clearing future packet queue" __L); + + if(!PacketQueue.empty()) { + map::iterator itr; + for(itr=PacketQueue.begin();itr!=PacketQueue.end();itr++) { + p=itr->second; + delete p; + } + PacketQueue.clear(); + } +} + +void EQStream::Process(const unsigned char *buffer, const uint32 length) +{ +static unsigned char newbuffer[2048]; +uint32 newlength=0; + if (EQProtocolPacket::ValidateCRC(buffer,length,Key)) { + if (compressed) { + newlength=EQProtocolPacket::Decompress(buffer,length,newbuffer,2048); + } else { + memcpy(newbuffer,buffer,length); + newlength=length; + if (encoded) + EQProtocolPacket::ChatDecode(newbuffer,newlength-2,Key); + } + if (buffer[1]!=0x01 && buffer[1]!=0x02 && buffer[1]!=0x1d) + newlength-=2; + EQProtocolPacket *p = MakeProtocolPacket(newbuffer,newlength); + ProcessPacket(p); + delete p; + ProcessQueue(); + } else { + _log(NET__DEBUG, _L "Incoming packet failed checksum" __L); + _hex(NET__NET_CREATE_HEX, buffer, length); + } +} + +long EQStream::GetNextAckToSend() +{ + MAcks.lock(); + long l=NextAckToSend; + MAcks.unlock(); + + return l; +} + +long EQStream::GetLastAckSent() +{ + MAcks.lock(); + long l=LastAckSent; + MAcks.unlock(); + + return l; +} + +void EQStream::AckPackets(uint16 seq) +{ +deque::iterator itr, tmp; + + MOutboundQueue.lock(); +//do a bit of sanity checking. +if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + _log(NET__ERROR, _L "Pre-Ack Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); +} +if(NextSequencedSend > SequencedQueue.size()) { + _log(NET__ERROR, _L "Pre-Ack Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); +} + + SeqOrder ord = CompareSequence(SequencedBase, seq); + if(ord == SeqInOrder) { + //they are not acking anything new... + _log(NET__NET_ACKS, _L "Received an ack with no window advancement (seq %d)." __L, seq); + } else if(ord == SeqPast) { + //they are nacking blocks going back before our buffer, wtf? + _log(NET__NET_ACKS, _L "Received an ack with backward window advancement (they gave %d, our window starts at %d). This is bad." __L, seq, SequencedBase); + } else { + _log(NET__NET_ACKS, _L "Received an ack up through sequence %d. Our base is %d." __L, seq, SequencedBase); + + + //this is a good ack, we get to ack some blocks. + seq++; //we stop at the block right after their ack, counting on the wrap of both numbers. + while(SequencedBase != seq) { +if(SequencedQueue.empty()) { +_log(NET__ERROR, _L "OUT OF PACKETS acked packet with sequence %lu. Next send is %d before this." __L, (unsigned long)SequencedBase, NextSequencedSend); + SequencedBase = NextOutSeq; + NextSequencedSend = 0; + break; +} + _log(NET__NET_ACKS, _L "Removing acked packet with sequence %lu. Next send is %d before this." __L, (unsigned long)SequencedBase, NextSequencedSend); + //clean out the acked packet + delete SequencedQueue.front(); + SequencedQueue.pop_front(); + //adjust our "next" pointer + if(NextSequencedSend > 0) + NextSequencedSend--; + //advance the base sequence number to the seq of the block after the one we just got rid of. + SequencedBase++; + } +if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { + _log(NET__ERROR, _L "Post-Ack on %d Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, seq, SequencedBase, SequencedQueue.size(), NextOutSeq); +} +if(NextSequencedSend > SequencedQueue.size()) { + _log(NET__ERROR, _L "Post-Ack Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); +} + } + + MOutboundQueue.unlock(); +} + +void EQStream::SetNextAckToSend(uint32 seq) +{ + MAcks.lock(); + _log(NET__NET_ACKS, _L "Set Next Ack To Send to %lu" __L, (unsigned long)seq); + NextAckToSend=seq; + MAcks.unlock(); +} + +void EQStream::SetLastAckSent(uint32 seq) +{ + MAcks.lock(); + _log(NET__NET_ACKS, _L "Set Last Ack Sent to %lu" __L, (unsigned long)seq); + LastAckSent=seq; + MAcks.unlock(); +} + +void EQStream::ProcessQueue() +{ + if(PacketQueue.empty()) { + return; + } + + EQProtocolPacket *qp=NULL; + while((qp=RemoveQueue(NextInSeq))!=NULL) { + _log(NET__DEBUG, _L "Processing Queued Packet: Seq=%d" __L, NextInSeq); + ProcessPacket(qp); + delete qp; + _log(NET__APP_TRACE, _L "OP_Packet Queue size=%d" __L, PacketQueue.size()); + } +} + +EQProtocolPacket *EQStream::RemoveQueue(uint16 seq) +{ +map::iterator itr; +EQProtocolPacket *qp=NULL; + if ((itr=PacketQueue.find(seq))!=PacketQueue.end()) { + qp=itr->second; + PacketQueue.erase(itr); + _log(NET__APP_TRACE, _L "OP_Packet Queue size=%d" __L, PacketQueue.size()); + } + return qp; +} + +void EQStream::SetStreamType(EQStreamType type) +{ + _log(NET__NET_TRACE, _L "Changing stream type from %s to %s" __L, StreamTypeString(StreamType), StreamTypeString(type)); + StreamType=type; + switch (StreamType) { + case LoginStream: + app_opcode_size=1; + compressed=false; + encoded=false; + _log(NET__NET_TRACE, _L "Login stream has app opcode size %d, is not compressed or encoded." __L, app_opcode_size); + break; + case ChatOrMailStream: + case ChatStream: + case MailStream: + app_opcode_size=1; + compressed=false; + encoded=true; + _log(NET__NET_TRACE, _L "Chat/Mail stream has app opcode size %d, is not compressed, and is encoded." __L, app_opcode_size); + break; + case ZoneStream: + case WorldStream: + default: + app_opcode_size=2; + compressed=true; + encoded=false; + _log(NET__NET_TRACE, _L "World/Zone stream has app opcode size %d, is compressed, and is not encoded." __L, app_opcode_size); + break; + } +} + +const char *EQStream::StreamTypeString(EQStreamType t) +{ + switch (t) { + case LoginStream: + return "Login"; + break; + case WorldStream: + return "World"; + break; + case ZoneStream: + return "Zone"; + break; + case ChatOrMailStream: + return "Chat/Mail"; + break; + case ChatStream: + return "Chat"; + break; + case MailStream: + return "Mail"; + break; + case UnknownStream: + return "Unknown"; + break; + } + return "UnknownType"; +} + +//returns SeqFuture if `seq` is later than `expected_seq` +EQStream::SeqOrder EQStream::CompareSequence(uint16 expected_seq , uint16 seq) +{ + if (expected_seq==seq) { + // Curent + return SeqInOrder; + } else if ((seq > expected_seq && (uint32)seq < ((uint32)expected_seq + EQStream::MaxWindowSize)) || seq < (expected_seq - EQStream::MaxWindowSize)) { + // Future + return SeqFuture; + } else { + // Past + return SeqPast; + } +} + +void EQStream::SetState(EQStreamState state) { + MState.lock(); + _log(NET__NET_TRACE, _L "Changing state from %d to %d" __L, State, state); + State=state; + MState.unlock(); +} + + +void EQStream::CheckTimeout(uint32 now, uint32 timeout) { + + bool outgoing_data = HasOutgoingData(); //up here to avoid recursive locking + + EQStreamState orig_state = GetState(); + if (orig_state == CLOSING && !outgoing_data) { + _log(NET__NET_TRACE, _L "Out of data in closing state, disconnecting." __L); + _SendDisconnect(); + SetState(DISCONNECTING); + } else if (LastPacket && (now-LastPacket) > timeout) { + switch(orig_state) { + case CLOSING: + //if we time out in the closing state, they are not acking us, just give up + _log(NET__DEBUG, _L "Timeout expired in closing state. Moving to closed state." __L); + _SendDisconnect(); + SetState(CLOSED); + break; + case DISCONNECTING: + //we timed out waiting for them to send us the disconnect reply, just give up. + _log(NET__DEBUG, _L "Timeout expired in disconnecting state. Moving to closed state." __L); + SetState(CLOSED); + break; + case CLOSED: + _log(NET__DEBUG, _L "Timeout expired in closed state??" __L); + break; + case ESTABLISHED: + //we timed out during normal operation. Try to be nice about it. + //we will almost certainly time out again waiting for the disconnect reply, but oh well. + _log(NET__DEBUG, _L "Timeout expired in established state. Closing connection." __L); + _SendDisconnect(); + SetState(DISCONNECTING); + } + } +} + +void EQStream::Decay() +{ + MRate.lock(); + uint32 rate=DecayRate; + MRate.unlock(); + if (BytesWritten>0) { + BytesWritten-=rate; + if (BytesWritten<0) + BytesWritten=0; + } +} + +void EQStream::AdjustRates(uint32 average_delta) +{ +#ifdef RETRANSMITS + if (average_delta && (average_delta <= RuleI(EQStream, AverageDeltaMax))) { +#else + if (average_delta) { +#endif + MRate.lock(); + RateThreshold=RATEBASE/average_delta; + DecayRate=DECAYBASE/average_delta; + _log(NET__RATES, _L "Adjusting data rate to thresh %d, decay %d based on avg delta %d" __L, RateThreshold, DecayRate, average_delta); + MRate.unlock(); +#ifdef RETRANSMITS + } else { + _log(NET__RATES, _L "Not adjusting data rate because avg delta over max (%d > %d)" __L, average_delta, RuleI(EQStream, AverageDeltaMax)); +#endif + } +} + +void EQStream::Close() { + if(HasOutgoingData()) { + //there is pending data, wait for it to go out. + _log(NET__DEBUG, _L "Stream requested to Close(), but there is pending data, waiting for it." __L); + SetState(CLOSING); + } else { + //otherwise, we are done, we can drop immediately. + _SendDisconnect(); + _log(NET__DEBUG, _L "Stream closing immediate due to Close()" __L); + SetState(DISCONNECTING); + } +} + + +//this could be expanded to check more than the fitst opcode if +//we needed more complex matching +EQStream::MatchState EQStream::CheckSignature(const Signature *sig) { + EQRawApplicationPacket *p = NULL; + MatchState res = MatchNotReady; + + MInboundQueue.lock(); + if (!InboundQueue.empty()) { + //this is already getting hackish... + p = InboundQueue.front(); + if(sig->ignore_eq_opcode != 0 && p->opcode == sig->ignore_eq_opcode) { + if(InboundQueue.size() > 1) { + p = InboundQueue[1]; + } else { + p = NULL; + } + } + if(p == NULL) { + //first opcode is ignored, and nothing else remains... keep waiting + } else if(p->opcode == sig->first_eq_opcode) { + //opcode matches, check length.. + if(p->size == sig->first_length) { + _log(NET__IDENT_TRACE, "%s:%d: First opcode matched 0x%x and length matched %d", long2ip(GetRemoteIP()).c_str(), ntohs(GetRemotePort()), sig->first_eq_opcode, p->size); + res = MatchSuccessful; + } else if(sig->first_length == 0) { + _log(NET__IDENT_TRACE, "%s:%d: First opcode matched 0x%x and length (%d) is ignored", long2ip(GetRemoteIP()).c_str(), ntohs(GetRemotePort()), sig->first_eq_opcode, p->size); + res = MatchSuccessful; + } else { + //opcode matched but length did not. + _log(NET__IDENT_TRACE, "%s:%d: First opcode matched 0x%x, but length %d did not match expected %d", long2ip(GetRemoteIP()).c_str(), ntohs(GetRemotePort()), sig->first_eq_opcode, p->size, sig->first_length); + res = MatchFailed; + } + } else { + //first opcode did not match.. + _log(NET__IDENT_TRACE, "%s:%d: First opcode 0x%x did not match expected 0x%x", long2ip(GetRemoteIP()).c_str(), ntohs(GetRemotePort()), p->opcode, sig->first_eq_opcode); + res = MatchFailed; + } + } + MInboundQueue.unlock(); + + return(res); +} + + + + diff --git a/common/EQStream.h b/common/EQStream.h new file mode 100644 index 000000000..b2466d2ac --- /dev/null +++ b/common/EQStream.h @@ -0,0 +1,281 @@ +#ifndef _EQSTREAM_H +#define _EQSTREAM_H + +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif +#include "EQStreamType.h" +#include "EQPacket.h" +#include "EQStreamIntf.h" +#include "Mutex.h" +#include "../common/opcodemgr.h" +#include "../common/misc.h" +#include "../common/Condition.h" +#include "../common/timer.h" + +using namespace std; + +#define FLAG_COMPRESSED 0x01 +#define FLAG_ENCODED 0x04 + +#define RATEBASE 1048576 // 1 MB +#define DECAYBASE 78642 // RATEBASE/10 + +#pragma pack(1) +struct SessionRequest { + uint32 UnknownA; + uint32 Session; + uint32 MaxLength; +}; + +struct SessionResponse { + uint32 Session; + uint32 Key; + uint8 UnknownA; + uint8 Format; + uint8 UnknownB; + uint32 MaxLength; + uint32 UnknownD; +}; + +//Deltas are in ms, representing round trip times +struct SessionStats { +/*000*/ uint16 RequestID; +/*002*/ uint32 last_local_delta; +/*006*/ uint32 average_delta; +/*010*/ uint32 low_delta; +/*014*/ uint32 high_delta; +/*018*/ uint32 last_remote_delta; +/*022*/ uint64 packets_sent; +/*030*/ uint64 packets_recieved; +/*038*/ +}; + +#pragma pack() + +class OpcodeManager; +//extern OpcodeManager *EQNetworkOpcodeManager; + +//class EQStreamFactory; +class EQStreamPair; +class EQRawApplicationPacket; + +class EQStream : public EQStreamInterface { + friend class EQStreamPair; //for collector. + protected: + typedef enum { + SeqPast, + SeqInOrder, + SeqFuture + } SeqOrder; + + uint32 remote_ip; + uint16 remote_port; + uint8 buffer[8192]; + unsigned char *oversize_buffer; + uint32 oversize_offset,oversize_length; + uint8 app_opcode_size; + EQStreamType StreamType; + bool compressed,encoded; + uint32 retransmittimer; + uint32 retransmittimeout; + + //uint32 buffer_len; + + uint32 Session, Key; + uint16 NextInSeq; + uint32 MaxLen; + uint16 MaxSends; + + uint8 active_users; //how many things are actively using this + Mutex MInUse; + + EQStreamState State; + Mutex MState; + + uint32 LastPacket; + Mutex MVarlock; + + // Ack sequence tracking. + long NextAckToSend; + long LastAckSent; + long GetNextAckToSend(); + long GetLastAckSent(); + void AckPackets(uint16 seq); + void SetNextAckToSend(uint32); + void SetLastAckSent(uint32); + + Mutex MAcks; + + // Packets waiting to be sent (all protected by MOutboundQueue) + queue NonSequencedQueue; + deque SequencedQueue; + uint16 NextOutSeq; + uint16 SequencedBase; //the sequence number of SequencedQueue[0] + long NextSequencedSend; //index into SequencedQueue + Mutex MOutboundQueue; + + //a buffer we use for compression/decompression + unsigned char _tempBuffer[2048]; + + // Packets waiting to be processed + vector InboundQueue; + map PacketQueue; //not mutex protected, only accessed by caller of Process() + Mutex MInboundQueue; + + static uint16 MaxWindowSize; + + int32 BytesWritten; + + Mutex MRate; + int32 RateThreshold; + int32 DecayRate; + + + OpcodeManager **OpMgr; + +// EQStreamFactory *const Factory; + + EQRawApplicationPacket *MakeApplicationPacket(EQProtocolPacket *p); + EQRawApplicationPacket *MakeApplicationPacket(const unsigned char *buf, uint32 len); + EQProtocolPacket *MakeProtocolPacket(const unsigned char *buf, uint32 len); + void SendPacket(uint16 opcode, EQApplicationPacket *p); + + void SetState(EQStreamState state); + + void SendSessionResponse(); + void SendSessionRequest(); + void SendAck(uint16 seq); + void SendOutOfOrderAck(uint16 seq); + void QueuePacket(EQProtocolPacket *p); + void SendPacket(EQProtocolPacket *p); + void NonSequencedPush(EQProtocolPacket *p); + void SequencedPush(EQProtocolPacket *p); + void WritePacket(int fd,EQProtocolPacket *p); + + + uint32 GetKey() { return Key; } + void SetKey(uint32 k) { Key=k; } + void SetSession(uint32 s) { Session=s; } + + void ProcessPacket(EQProtocolPacket *p); +// virtual void DispatchPacket(EQApplicationPacket *p) { p->DumpRaw(); } + + + bool Stale(uint32 now, uint32 timeout=30) { return (LastPacket && (now-LastPacket) > timeout); } + + void InboundQueuePush(EQRawApplicationPacket *p); + EQRawApplicationPacket *PeekPacket(); //for collector. + EQRawApplicationPacket *PopRawPacket(); //for collector. + + void InboundQueueClear(); + void OutboundQueueClear(); + void PacketQueueClear(); + + void ProcessQueue(); + EQProtocolPacket *RemoveQueue(uint16 seq); + + void _SendDisconnect(); + + void init(); + public: + EQStream() { init(); remote_ip = 0; remote_port = 0; State=UNESTABLISHED; StreamType=UnknownStream; compressed=true; encoded=false; app_opcode_size=2; bytes_sent=0; bytes_recv=0; create_time=Timer::GetTimeSeconds(); } + EQStream(sockaddr_in addr) { init(); remote_ip=addr.sin_addr.s_addr; remote_port=addr.sin_port; State=UNESTABLISHED; StreamType=UnknownStream; compressed=true; encoded=false; app_opcode_size=2; bytes_sent=0; bytes_recv=0; create_time=Timer::GetTimeSeconds(); } + virtual ~EQStream() { RemoveData(); SetState(CLOSED); } +// inline void SetFactory(EQStreamFactory *f) { Factory=f; } + void SetMaxLen(uint32 length) { MaxLen=length; } + + //interface used by application (EQStreamInterface) + virtual void QueuePacket(const EQApplicationPacket *p, bool ack_req=true); + virtual void FastQueuePacket(EQApplicationPacket **p, bool ack_req=true); + virtual EQApplicationPacket *PopPacket(); + virtual void Close(); + virtual uint32 GetRemoteIP() const { return remote_ip; } + virtual uint16 GetRemotePort() const { return remote_port; } + virtual void ReleaseFromUse() { MInUse.lock(); if(active_users > 0) active_users--; MInUse.unlock(); } + virtual void RemoveData() { InboundQueueClear(); OutboundQueueClear(); PacketQueueClear(); /*if (CombinedAppPacket) delete CombinedAppPacket;*/ } + virtual bool CheckState(EQStreamState state) { return GetState() == state; } + virtual std::string Describe() const { return("Direct EQStream"); } + + void SetOpcodeManager(OpcodeManager **opm) { OpMgr = opm; } + + void CheckTimeout(uint32 now, uint32 timeout=30); + bool HasOutgoingData(); + void Process(const unsigned char *data, const uint32 length); + void SetLastPacketTime(uint32 t) {LastPacket=t;} + void Write(int eq_fd); + + // + inline bool IsInUse() { bool flag; MInUse.lock(); flag=(active_users>0); MInUse.unlock(); return flag; } + inline void PutInUse() { MInUse.lock(); active_users++; MInUse.unlock(); } + + inline EQStreamState GetState() { EQStreamState s; MState.lock(); s=State; MState.unlock(); return s; } + +// static EQProtocolPacket *Read(int eq_fd, sockaddr_in *from); + static SeqOrder CompareSequence(uint16 expected_seq , uint16 seq); + +// void Close() { SendDisconnect(); } + bool CheckActive() { return GetState()==ESTABLISHED; } + bool CheckClosed() { return GetState()==CLOSED; } + void SetOpcodeSize(uint8 s) { app_opcode_size = s; } + void SetStreamType(EQStreamType t); + inline const EQStreamType GetStreamType() const { return StreamType; } + static const char *StreamTypeString(EQStreamType t); + + void Decay(); + void AdjustRates(uint32 average_delta); + + uint32 bytes_sent; + uint32 bytes_recv; + uint32 create_time; + + void AddBytesSent(uint32 bytes) + { + bytes_sent += bytes; + } + + void AddBytesRecv(uint32 bytes) + { + bytes_recv += bytes; + } + + virtual const uint32 GetBytesSent() const { return bytes_sent; } + virtual const uint32 GetBytesRecieved() const { return bytes_recv; } + virtual const uint32 GetBytesSentPerSecond() const + { + if((Timer::GetTimeSeconds() - create_time) == 0) + return 0; + return bytes_sent / (Timer::GetTimeSeconds() - create_time); + } + + virtual const uint32 GetBytesRecvPerSecond() const + { + if((Timer::GetTimeSeconds() - create_time) == 0) + return 0; + return bytes_recv / (Timer::GetTimeSeconds() - create_time); + } + + //used for dynamic stream identification + class Signature { + public: + //this object could get more complicated if needed... + uint16 ignore_eq_opcode; //0=dont ignore + uint16 first_eq_opcode; + uint32 first_length; //0=dont check length + }; + typedef enum { + MatchNotReady, + MatchSuccessful, + MatchFailed + } MatchState; + MatchState CheckSignature(const Signature *sig); + +}; + + +#endif diff --git a/common/EQStreamFactory.cpp b/common/EQStreamFactory.cpp new file mode 100644 index 000000000..850b686cd --- /dev/null +++ b/common/EQStreamFactory.cpp @@ -0,0 +1,347 @@ +#include "debug.h" +#include "EQStreamFactory.h" +#ifdef _WINDOWS + #include + #include + #include + #include +#else + #include + #include + #include + #include + #include + #include +#endif +#include +#include +#include "op_codes.h" +#include "EQStream.h" +#include "logsys.h" + +using namespace std; + +ThreadReturnType EQStreamFactoryReaderLoop(void *eqfs) +{ +EQStreamFactory *fs=(EQStreamFactory *)eqfs; + +#ifndef WIN32 + _log(COMMON__THREADS, "Starting EQStreamFactoryReaderLoop with thread ID %d", pthread_self()); +#endif + + fs->ReaderLoop(); + +#ifndef WIN32 + _log(COMMON__THREADS, "Ending EQStreamFactoryReaderLoop with thread ID %d", pthread_self()); +#endif + + THREAD_RETURN(NULL); +} + +ThreadReturnType EQStreamFactoryWriterLoop(void *eqfs) +{ + EQStreamFactory *fs=(EQStreamFactory *)eqfs; + +#ifndef WIN32 + _log(COMMON__THREADS, "Starting EQStreamFactoryWriterLoop with thread ID %d", pthread_self()); +#endif + + fs->WriterLoop(); + +#ifndef WIN32 + _log(COMMON__THREADS, "Ending EQStreamFactoryWriterLoop with thread ID %d", pthread_self()); +#endif + + THREAD_RETURN(NULL); +} + +EQStreamFactory::EQStreamFactory(EQStreamType type, int port, uint32 timeout) + : Timeoutable(5000), stream_timeout(timeout) +{ + StreamType=type; + Port=port; + sock=-1; +} + +void EQStreamFactory::Close() +{ + Stop(); + +#ifdef _WINDOWS + closesocket(sock); +#else + close(sock); +#endif + sock=-1; +} + +bool EQStreamFactory::Open() +{ +struct sockaddr_in address; +#ifndef WIN32 + pthread_t t1,t2; +#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(Port); + 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; + } + + if (bind(sock, (struct sockaddr *) &address, sizeof(address)) < 0) { + close(sock); + sock=-1; + return false; + } + #ifdef _WINDOWS + unsigned long nonblock = 1; + ioctlsocket(sock, FIONBIO, &nonblock); + #else + fcntl(sock, F_SETFL, O_NONBLOCK); + #endif + //moved these because on windows the output was delayed and causing the console window to look bad + //cout << "Starting factory Reader" << endl; + //cout << "Starting factory Writer" << endl; + #ifdef _WINDOWS + _beginthread(EQStreamFactoryReaderLoop,0, this); + _beginthread(EQStreamFactoryWriterLoop,0, this); + #else + pthread_create(&t1,NULL,EQStreamFactoryReaderLoop,this); + pthread_create(&t2,NULL,EQStreamFactoryWriterLoop,this); + #endif + return true; +} + +EQStream *EQStreamFactory::Pop() +{ +EQStream *s=NULL; + //cout << "Pop():Locking MNewStreams" << endl; + MNewStreams.lock(); + if (NewStreams.size()) { + s=NewStreams.front(); + NewStreams.pop(); + s->PutInUse(); + } + MNewStreams.unlock(); + //cout << "Pop(): Unlocking MNewStreams" << endl; + + return s; +} + +void EQStreamFactory::Push(EQStream *s) +{ + //cout << "Push():Locking MNewStreams" << endl; + MNewStreams.lock(); + NewStreams.push(s); + MNewStreams.unlock(); + //cout << "Push(): Unlocking MNewStreams" << endl; +} + +void EQStreamFactory::ReaderLoop() +{ +fd_set readset; +map::iterator stream_itr; +int num; +int length; +unsigned char buffer[2048]; +sockaddr_in from; +int socklen=sizeof(sockaddr_in); +timeval sleep_time; +//time_t now; + + ReaderRunning=true; + while(sock!=-1) { + MReaderRunning.lock(); + if (!ReaderRunning) + break; + MReaderRunning.unlock(); + + FD_ZERO(&readset); + FD_SET(sock,&readset); + + sleep_time.tv_sec=30; + sleep_time.tv_usec=0; + if ((num=select(sock+1,&readset,NULL,NULL,&sleep_time))<0) { + // What do we wanna do? + continue; + } else if (num==0) + continue; + + if(sock == -1) + break; //somebody closed us while we were sleeping. + + if (FD_ISSET(sock,&readset)) { +#ifdef _WINDOWS + if ((length=recvfrom(sock,(char*)buffer,sizeof(buffer),0,(struct sockaddr*)&from,(int *)&socklen)) < 2) +#else + if ((length=recvfrom(sock,buffer,2048,0,(struct sockaddr *)&from,(socklen_t *)&socklen)) < 2) +#endif + { + // What do we wanna do? + } else { + char temp[25]; + sprintf(temp,"%u.%d",ntohl(from.sin_addr.s_addr),ntohs(from.sin_port)); + MStreams.lock(); + if ((stream_itr=Streams.find(temp))==Streams.end()) { + if (buffer[1]==OP_SessionRequest) { + EQStream *s = new EQStream(from); + s->SetStreamType(StreamType); + Streams[temp]=s; + WriterWork.Signal(); + Push(s); + s->AddBytesRecv(length); + s->Process(buffer,length); + s->SetLastPacketTime(Timer::GetCurrentTime()); + } + MStreams.unlock(); + } else { + EQStream *curstream = stream_itr->second; + //dont bother processing incoming packets for closed connections + if(curstream->CheckClosed()) + curstream = NULL; + else + curstream->PutInUse(); + MStreams.unlock(); //the in use flag prevents the stream from being deleted while we are using it. + + if(curstream) { + curstream->AddBytesRecv(length); + curstream->Process(buffer,length); + curstream->SetLastPacketTime(Timer::GetCurrentTime()); + curstream->ReleaseFromUse(); + } + } + } + } + } +} + +void EQStreamFactory::CheckTimeout() +{ + //lock streams the entire time were checking timeouts, it should be fast. + MStreams.lock(); + + unsigned long now=Timer::GetCurrentTime(); + map::iterator stream_itr; + + for(stream_itr=Streams.begin();stream_itr!=Streams.end();) { + EQStream *s = stream_itr->second; + + s->CheckTimeout(now, stream_timeout); + + EQStreamState state = s->GetState(); + + //not part of the else so we check it right away on state change + if (state==CLOSED) { + if (s->IsInUse()) { + //give it a little time for everybody to finish with it + } else { + //everybody is done, we can delete it now + //cout << "Removing connection" << endl; + map::iterator temp=stream_itr; + stream_itr++; + //let whoever has the stream outside delete it + delete temp->second; + Streams.erase(temp); + continue; + } + } + + stream_itr++; + } + MStreams.unlock(); +} + +void EQStreamFactory::WriterLoop() +{ +map::iterator stream_itr; +bool havework=true; +vector wants_write; +vector::iterator cur,end; +bool decay=false; +uint32 stream_count; + +Timer DecayTimer(20); + + WriterRunning=true; + DecayTimer.Enable(); + while(sock!=-1) { + //if (!havework) { + //WriterWork.Wait(); + //} + MWriterRunning.lock(); + if (!WriterRunning) + break; + MWriterRunning.unlock(); + + havework = false; + wants_write.clear(); + + decay=DecayTimer.Check(); + + //copy streams into a seperate list so we dont have to keep + //MStreams locked while we are writting + MStreams.lock(); + for(stream_itr=Streams.begin();stream_itr!=Streams.end();stream_itr++) { + // If it's time to decay the bytes sent, then let's do it before we try to write + if (decay) + stream_itr->second->Decay(); + + //bullshit checking, to see if this is really happening, GDB seems to think so... + if(stream_itr->second == NULL) { + fprintf(stderr, "ERROR: NULL Stream encountered in EQStreamFactory::WriterLoop for: %s", stream_itr->first.c_str()); + continue; + } + + if (stream_itr->second->HasOutgoingData()) { + havework=true; + stream_itr->second->PutInUse(); + wants_write.push_back(stream_itr->second); + } + } + MStreams.unlock(); + + //do the actual writes + cur = wants_write.begin(); + end = wants_write.end(); + for(; cur != end; cur++) { + (*cur)->Write(sock); + (*cur)->ReleaseFromUse(); + } + + + Sleep(10); + + MStreams.lock(); + stream_count=Streams.size(); + MStreams.unlock(); + if (!stream_count) { + //cout << "No streams, waiting on condition" << endl; + WriterWork.Wait(); + //cout << "Awake from condition, must have a stream now" << endl; + } + } +} + + + + + + + + + + + + + + + + + + diff --git a/common/EQStreamFactory.h b/common/EQStreamFactory.h new file mode 100644 index 000000000..316a175b0 --- /dev/null +++ b/common/EQStreamFactory.h @@ -0,0 +1,58 @@ +#ifndef _EQSTREAMFACTORY_H + +#define _EQSTREAMFACTORY_H + +#include +#include +#include "../common/EQStream.h" +#include "../common/Condition.h" +#include "../common/timeoutmgr.h" +#include "../common/opcodemgr.h" +#include "../common/timer.h" + +class EQStreamFactory : private Timeoutable { + private: + int sock; + int Port; + + bool ReaderRunning; + Mutex MReaderRunning; + bool WriterRunning; + Mutex MWriterRunning; + + Condition WriterWork; + + EQStreamType StreamType; + + queue NewStreams; + Mutex MNewStreams; + + map Streams; + Mutex MStreams; + + virtual void CheckTimeout(); + + Timer *DecayTimer; + + uint32 stream_timeout; + + public: + EQStreamFactory(EQStreamType type, uint32 timeout = 135000) : Timeoutable(5000), stream_timeout(timeout) { ReaderRunning=false; WriterRunning=false; StreamType=type; sock=-1; } + EQStreamFactory(EQStreamType type, int port, uint32 timeout = 135000); + + EQStream *Pop(); + void Push(EQStream *s); + + bool Open(); + bool Open(unsigned long port) { Port=port; return Open(); } + bool IsOpen() { return sock!=-1; } + void Close(); + void ReaderLoop(); + void WriterLoop(); + void Stop() { StopReader(); StopWriter(); } + void StopReader() { MReaderRunning.lock(); ReaderRunning=false; MReaderRunning.unlock(); } + void StopWriter() { MWriterRunning.lock(); WriterRunning=false; MWriterRunning.unlock(); WriterWork.Signal(); } + void SignalWriter() { WriterWork.Signal(); } +}; + +#endif diff --git a/common/EQStreamIdent.cpp b/common/EQStreamIdent.cpp new file mode 100644 index 000000000..2ecbd3988 --- /dev/null +++ b/common/EQStreamIdent.cpp @@ -0,0 +1,199 @@ + +#include "debug.h" +#include "EQStreamIdent.h" +#include "EQStreamProxy.h" +#include "logsys.h" + +using namespace std; + +EQStreamIdentifier::~EQStreamIdentifier() { + while(!m_identified.empty()) { + m_identified.front()->ReleaseFromUse(); + m_identified.pop(); + } + vector::iterator cur, end; + cur = m_streams.begin(); + end = m_streams.end(); + for(; cur != end; cur++) { + Record *r = *cur; + r->stream->ReleaseFromUse(); + delete r; + } + vector::iterator curp, endp; + curp = m_patches.begin(); + endp = m_patches.end(); + for(; curp != endp; curp++) { + delete *curp; + } +} + +void EQStreamIdentifier::RegisterPatch(const EQStream::Signature &sig, const char *name, OpcodeManager ** opcodes, const StructStrategy *structs) { + Patch *p = new Patch; + p->signature = sig; + p->name = name; + p->opcodes = opcodes; + p->structs = structs; + m_patches.push_back(p); +} + +void EQStreamIdentifier::Process() { + vector::iterator cur; + vector::iterator curp, endp; + + //foreach pending stream. + cur = m_streams.begin(); + while(cur != m_streams.end()) { + Record *r = *cur; + + //first see if this stream has expired + if(r->expire.Check(false)) { + //this stream has failed to match any pattern in our timeframe. + _log(NET__IDENTIFY, "Unable to identify stream from %s:%d before timeout.", long2ip(r->stream->GetRemoteIP()).c_str(), ntohs(r->stream->GetRemotePort())); + r->stream->ReleaseFromUse(); + delete r; + cur = m_streams.erase(cur); + continue; + } + + //then make sure the stream is still active + //if stream hasn't finished initializing then continue; + if(r->stream->GetState() == UNESTABLISHED) + { + continue; + } + if(r->stream->GetState() != ESTABLISHED) { + //the stream closed before it was identified. + _log(NET__IDENTIFY, "Unable to identify stream from %s:%d before it closed.", long2ip(r->stream->GetRemoteIP()).c_str(), ntohs(r->stream->GetRemotePort())); + switch(r->stream->GetState()) + { + case ESTABLISHED: + _log(NET__IDENTIFY, "Stream state was Established"); + break; + case CLOSING: + _log(NET__IDENTIFY, "Stream state was Closing"); + break; + case DISCONNECTING: + _log(NET__IDENTIFY, "Stream state was Disconnecting"); + break; + case CLOSED: + _log(NET__IDENTIFY, "Stream state was Closed"); + break; + default: + _log(NET__IDENTIFY, "Stream state was Unestablished or unknown"); + break; + } + r->stream->ReleaseFromUse(); + delete r; + cur = m_streams.erase(cur); + continue; + } + + //not expired, check against all patch signatures + + bool found_one = false; //"we found a matching patch for this stream" + bool all_ready = true; //"all signatures were ready to check the stream" + + //foreach possbile patch... + curp = m_patches.begin(); + endp = m_patches.end(); + for(; !found_one && curp != endp; curp++) { + Patch *p = *curp; + + //ask the stream to see if it matches the supplied signature + EQStream::MatchState res = r->stream->CheckSignature(&p->signature); + switch(res) { + case EQStream::MatchNotReady: + //the stream has not received enough packets to compare with this signature +// _log(NET__IDENT_TRACE, "%s:%d: Tried patch %s, but stream is not ready for it.", long2ip(r->stream->GetRemoteIP()).c_str(), ntohs(r->stream->GetRemotePort()), p->name.c_str()); + all_ready = false; + break; + case EQStream::MatchSuccessful: { + //yay, a match. + + _log(NET__IDENTIFY, "Identified stream %s:%d with signature %s", long2ip(r->stream->GetRemoteIP()).c_str(), ntohs(r->stream->GetRemotePort()), p->name.c_str()); + + //might want to do something less-specific here... some day.. + EQStreamInterface *s = new EQStreamProxy(r->stream, p->structs, p->opcodes); + m_identified.push(s); + + found_one = true; + break; + } + case EQStream::MatchFailed: + //do nothing... + _log(NET__IDENT_TRACE, "%s:%d: Tried patch %s, and it did not match.", long2ip(r->stream->GetRemoteIP()).c_str(), ntohs(r->stream->GetRemotePort()), p->name.c_str()); + break; + } + } + + //if we checked all patches and did not find a match. + if(all_ready && !found_one) { + //the stream cannot be identified. + _log(NET__IDENTIFY, "Unable to identify stream from %s:%d, no match found.", long2ip(r->stream->GetRemoteIP()).c_str(), ntohs(r->stream->GetRemotePort())); + r->stream->ReleaseFromUse(); + } + + //if we found a match, or were not able to identify it + if(found_one || all_ready) { + //cannot print ip/port here. r->stream is invalid. + delete r; + cur = m_streams.erase(cur); + } else { + cur++; + } + } //end foreach stream +} + +void EQStreamIdentifier::AddStream(EQStream *&eqs) { + m_streams.push_back(new Record(eqs)); + eqs = NULL; +} + +EQStreamInterface *EQStreamIdentifier::PopIdentified() { + if(m_identified.empty()) + return(NULL); + EQStreamInterface *res = m_identified.front(); + m_identified.pop(); + return(res); +} + +EQStreamIdentifier::Record::Record(EQStream *s) +: stream(s), + expire(STREAM_IDENT_WAIT_MS) +{ +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/EQStreamIdent.h b/common/EQStreamIdent.h new file mode 100644 index 000000000..361c6237f --- /dev/null +++ b/common/EQStreamIdent.h @@ -0,0 +1,50 @@ +#ifndef EQSTREAMIDENT_H_ +#define EQSTREAMIDENT_H_ + +#include "EQStream.h" +#include "timer.h" +#include +#include +#include + +#define STREAM_IDENT_WAIT_MS 10000 + +class OpcodeManager; +class StructStrategy; + +class EQStreamIdentifier { +public: + ~EQStreamIdentifier(); + + //registration interface. + void RegisterPatch(const EQStream::Signature &sig, const char *name, OpcodeManager ** opcodes, const StructStrategy *structs); + + //main processing interface + void Process(); + void AddStream(EQStream *& eqs); + EQStreamInterface *PopIdentified(); + +protected: + + //registered patches.. + class Patch { + public: + std::string name; + EQStream::Signature signature; + OpcodeManager ** opcodes; + const StructStrategy *structs; + }; + std::vector m_patches; //we own these objects. + + //pending streams.. + class Record { + public: + Record(EQStream *s); + EQStream *stream; //we own this + Timer expire; + }; + std::vector m_streams; //we own these objects, and the streams contained in them. + std::queue m_identified; //we own these objects +}; + +#endif /*EQSTREAMIDENT_H_*/ diff --git a/common/EQStreamIntf.h b/common/EQStreamIntf.h new file mode 100644 index 000000000..22818c0a1 --- /dev/null +++ b/common/EQStreamIntf.h @@ -0,0 +1,39 @@ +#ifndef EQSTREAMINTF_H_ +#define EQSTREAMINTF_H_ + +//this is the only part of an EQStream that is seen by the application. + +#include + +typedef enum { + ESTABLISHED, + CLOSING, //waiting for pending data to flush. + DISCONNECTING, //have sent disconnect, waiting for their disconnect reply. + CLOSED, //received a disconnect from remote side. + UNESTABLISHED +} EQStreamState; + +class EQApplicationPacket; + +class EQStreamInterface { +public: + virtual ~EQStreamInterface() {} + + virtual void QueuePacket(const EQApplicationPacket *p, bool ack_req=true) = 0; + virtual void FastQueuePacket(EQApplicationPacket **p, bool ack_req=true) = 0; + virtual EQApplicationPacket *PopPacket() = 0; + virtual void Close() = 0; + virtual void ReleaseFromUse() = 0; + virtual void RemoveData() = 0; + virtual uint32 GetRemoteIP() const = 0; + virtual uint16 GetRemotePort() const = 0; + virtual bool CheckState(EQStreamState state) = 0; + virtual std::string Describe() const = 0; + + virtual const uint32 GetBytesSent() const { return 0; } + virtual const uint32 GetBytesRecieved() const { return 0; } + virtual const uint32 GetBytesSentPerSecond() const { return 0; } + virtual const uint32 GetBytesRecvPerSecond() const { return 0; } +}; + +#endif /*EQSTREAMINTF_H_*/ diff --git a/common/EQStreamLocator.h b/common/EQStreamLocator.h new file mode 100644 index 000000000..acd5bf8e8 --- /dev/null +++ b/common/EQStreamLocator.h @@ -0,0 +1,175 @@ +/* + Copyright (C) 2005 EQEmulator Team + + 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 _EQSTREAM_LOCATOR_H +#define _EQSTREAM_LOCATOR_H + +/* +This did not turn out nearly as nice as I hoped. +*/ + +#include +#include +using namespace std; + +class EQStreamInfo { +public: + EQStreamInfo() {} + EQStreamInfo(uint32 isrc_ip, uint32 idst_ip, uint16 isrc_port, uint16 idst_port) { + src_ip = isrc_ip; + dst_ip = idst_ip; + src_port = isrc_port; + dst_port = idst_port; + } + void invert(EQStreamInfo &r) const { + r.src_ip = dst_ip; + r.dst_ip = src_ip; + r.src_port = dst_port; + r.dst_port = src_port; + } + uint32 src_ip; + uint32 dst_ip; + uint16 src_port; + uint16 dst_port; +}; + +inline bool operator<(const EQStreamInfo &l, const EQStreamInfo &r) { +/*printf("Less than called with:\n"); +printf("0x%.8x:%d -> 0x%.8x:%d < \n", l.src_ip, l.src_port, l.dst_ip, l.dst_port); +printf("0x%.8x:%d -> 0x%.8x:%d ", r.src_ip, r.src_port, r.dst_ip, r.dst_port); + +bool res; +if(l.src_ip != r.src_ip) + res = (l.src_ip < r.src_ip); +else if(l.dst_ip != r.dst_ip) + res = (l.dst_ip < r.dst_ip); +else if(l.src_port != r.src_port) + res = (l.src_port < r.src_port); +else +res = (l.dst_port < r.dst_port); +if(res) + printf(": True\n"); +else + printf(": False\n");*/ + + + if(l.src_ip != r.src_ip) + return(l.src_ip < r.src_ip); + if(l.dst_ip != r.dst_ip) + return(l.dst_ip < r.dst_ip); + if(l.src_port != r.src_port) + return(l.src_port < r.src_port); + return(l.dst_port < r.dst_port); + +/* //so, this turned out uglier than I had hoped + if(l.src_ip < r.src_ip) + return(true); + if(l.src_ip > r.src_ip) + return(false); + if(l.dst_ip < r.dst_ip) + return(true); + if(l.dst_ip > r.dst_ip) + return(false); + if(l.src_port < r.src_port) + return(true); + if(l.src_port > r.src_port) + return(false); + return(l.dst_port < r.dst_port);*/ +} + +inline bool operator==(const EQStreamInfo &l, const EQStreamInfo &r) { +// if(l.src_ip == r.dest_ip) { +// //maybe swapped +// return(l.src_port == r.dst_port && l.dst_ip == r.src_ip && l.dst_port == r.src_port); +// } + return(l.src_ip == r.src_ip && l.src_port == r.src_port && l.dst_ip == r.dst_ip && l.dst_port == r.dst_port); +} + +//Forces the pointer T thing so we can return NULL +template +class EQStreamLocator { +protected: + typedef typename map::iterator iterator; +public: + + void Clear() { + streams.clear(); + } + + void AddStream(const EQStreamInfo &i, T *o) { + //do we care to check if it exists? + + //add this stream, and its inverse + streams[i] = o; + EQStreamInfo inv; + i.invert(inv); + streams[inv] = o; + } + + //deletes this stream, and its inverse + void RemoveStream(const EQStreamInfo &i) { + iterator res; + res = streams.find(i); + if(res != streams.end()) + streams.erase(res); + + EQStreamInfo inv; + i.invert(inv); + res = streams.find(inv); + if(res != streams.end()) + streams.erase(res); + } + + //removes every occurance of this stream from the list + void RemoveStream(T *it) { + iterator cur, end; + cur = streams.begin(); + end = streams.end(); + for(; cur != end; cur++) { + if(cur->second == it) { + streams.erase(cur); + //lazy recursive delete for now, since we have to redo + //our iterators anyways + RemoveStream(it); + return; + } + } + } + + T *GetStream(const EQStreamInfo &i) { + iterator res; + res = streams.find(i); + //possibly optimization would be to store streams.end(), since it + //may not be a constant time operation in theory, and update our + //stored copy only on insert or delete + if(res == streams.end()) + return(NULL); + return(res->second); + } + + //allow people to iterate over the const struct +// typedef map::const_iterator iterator; +// inline iterator begin() const { return(streams.begin()); } +// inline iterator end() const { return(streams.end()); } + +protected: + map streams; +}; + + + +#endif diff --git a/common/EQStreamProxy.cpp b/common/EQStreamProxy.cpp new file mode 100644 index 000000000..7a1053d47 --- /dev/null +++ b/common/EQStreamProxy.cpp @@ -0,0 +1,101 @@ + +#include "debug.h" +#include "EQStreamProxy.h" +#include "EQStream.h" +#include "StructStrategy.h" + + +EQStreamProxy::EQStreamProxy(EQStream *&stream, const StructStrategy *structs, OpcodeManager **opcodes) +: m_stream(stream), + m_structs(structs), + m_opcodes(opcodes) +{ + stream = NULL; //take the stream. + m_stream->SetOpcodeManager(m_opcodes); +} + +EQStreamProxy::~EQStreamProxy() { + //delete m_stream; //released by the stream factory. +} + +std::string EQStreamProxy::Describe() const { + return(m_structs->Describe()); +} + +void EQStreamProxy::QueuePacket(const EQApplicationPacket *p, bool ack_req) { + if(p == NULL) + return; + + EQApplicationPacket *newp = p->Copy(); + FastQueuePacket(&newp, ack_req); +} + +void EQStreamProxy::FastQueuePacket(EQApplicationPacket **p, bool ack_req) { + if(p == NULL || *p == NULL) + return; + m_structs->Encode(p, m_stream, ack_req); +} + +EQApplicationPacket *EQStreamProxy::PopPacket() { + EQApplicationPacket *pack = m_stream->PopPacket(); + if(pack == NULL) + return(NULL); + + //pass this packet through the struct strategy. + m_structs->Decode(pack); + return(pack); +} + +void EQStreamProxy::Close() { + m_stream->Close(); +} + +uint32 EQStreamProxy::GetRemoteIP() const { + return(m_stream->GetRemoteIP()); +} + +uint16 EQStreamProxy::GetRemotePort() const { + return(m_stream->GetRemotePort()); +} + +const uint32 EQStreamProxy::GetBytesSent() const +{ + return(m_stream->GetBytesSent()); +} + +const uint32 EQStreamProxy::GetBytesRecieved() const +{ + return(m_stream->GetBytesRecieved()); +} + +const uint32 EQStreamProxy::GetBytesSentPerSecond() const +{ + return(m_stream->GetBytesSentPerSecond()); +} + +const uint32 EQStreamProxy::GetBytesRecvPerSecond() const +{ + return(m_stream->GetBytesRecvPerSecond()); +} + +void EQStreamProxy::ReleaseFromUse() { + m_stream->ReleaseFromUse(); + + //this is so ugly, but I cant think of a better way to deal with + //it right now... + if(!m_stream->IsInUse()) { + delete this; + } +} + +void EQStreamProxy::RemoveData() { + m_stream->RemoveData(); +} + +bool EQStreamProxy::CheckState(EQStreamState state) { + if(m_stream) + return(m_stream->CheckState(state)); + + return false; +} + diff --git a/common/EQStreamProxy.h b/common/EQStreamProxy.h new file mode 100644 index 000000000..56c847f2c --- /dev/null +++ b/common/EQStreamProxy.h @@ -0,0 +1,54 @@ +#ifndef EQSTREAMPROXY_H_ +#define EQSTREAMPROXY_H_ + + +#include "types.h" +#include "EQStreamIntf.h" + +class EQStream; +class StructStrategy; +class OpcodeManager; +class EQApplicationPacket; + +class EQStreamProxy : public EQStreamInterface { +public: + //takes ownership of the stream. + EQStreamProxy(EQStream *&stream, const StructStrategy *structs, OpcodeManager **opcodes); + virtual ~EQStreamProxy(); + + //EQStreamInterface: + virtual void QueuePacket(const EQApplicationPacket *p, bool ack_req=true); + virtual void FastQueuePacket(EQApplicationPacket **p, bool ack_req=true); + virtual EQApplicationPacket *PopPacket(); + virtual void Close(); + virtual uint32 GetRemoteIP() const; + virtual uint16 GetRemotePort() const; + virtual void ReleaseFromUse(); + virtual void RemoveData(); + virtual bool CheckState(EQStreamState state); + virtual std::string Describe() const; + + virtual const uint32 GetBytesSent() const; + virtual const uint32 GetBytesRecieved() const; + virtual const uint32 GetBytesSentPerSecond() const; + virtual const uint32 GetBytesRecvPerSecond() const; + +protected: + EQStream *const m_stream; //we own this stream object. + const StructStrategy *const m_structs; //we do not own this object. + //this is a pointer to a pointer to make it less likely that a packet will + //reference an invalid opcode manager when they are being reloaded. + OpcodeManager **const m_opcodes; //we do not own this object. +}; + + + + + + + + + +#endif /*EQSTREAMPROXY_H_*/ + + diff --git a/common/EQStreamType.h b/common/EQStreamType.h new file mode 100644 index 000000000..631963323 --- /dev/null +++ b/common/EQStreamType.h @@ -0,0 +1,15 @@ +#ifndef _EQSTREAMTYPE_H +#define _EQSTREAMTYPE_H + +typedef enum { + UnknownStream=0, + LoginStream, + WorldStream, + ZoneStream, + ChatOrMailStream, + ChatStream, + MailStream +} EQStreamType; + + +#endif diff --git a/common/EmuTCPConnection.cpp b/common/EmuTCPConnection.cpp new file mode 100644 index 000000000..674b30710 --- /dev/null +++ b/common/EmuTCPConnection.cpp @@ -0,0 +1,857 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 +*/ + +/* + * + * + * + * + * There are really two or three different objects shoe-hored into this + * connection object. Sombody really needs to factor out the relay link + * crap into its own subclass of this object, it will clean things up + * tremendously. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ + +#include "../common/debug.h" + +#include +using namespace std; +#include +#include +#include +using namespace std; + +#include "EmuTCPConnection.h" +#include "EmuTCPServer.h" +#include "../common/servertalk.h" +#include "../common/packet_dump.h" + +#ifdef FREEBSD //Timothy Whitman - January 7, 2003 + #define MSG_NOSIGNAL 0 +#endif + +#define TCPN_DEBUG 0 +#define TCPN_DEBUG_Console 0 +#define TCPN_DEBUG_Memory 0 +#define TCPN_LOG_PACKETS 0 +#define TCPN_LOG_RAW_DATA_OUT 0 +#define TCPN_LOG_RAW_DATA_IN 0 + + +//server side case +EmuTCPConnection::EmuTCPConnection(uint32 ID, EmuTCPServer* iServer, SOCKET in_socket, uint32 irIP, uint16 irPort, bool iOldFormat) +: TCPConnection(ID, in_socket, irIP, irPort), + keepalive_timer(SERVER_TIMEOUT), + timeout_timer(SERVER_TIMEOUT * 2) +{ + id = 0; + Server = NULL; + pOldFormat = iOldFormat; + #ifdef MINILOGIN + TCPMode = modePacket; + PacketMode = packetModeLogin; + #else + if (pOldFormat) + TCPMode = modePacket; + else + TCPMode = modeConsole; + PacketMode = packetModeZone; + #endif + RelayLink = 0; + RelayServer = false; + RelayCount = 0; + RemoteID = 0; + +} + +//client outgoing connection case (and client side relay) +EmuTCPConnection::EmuTCPConnection(bool iOldFormat, EmuTCPServer* iRelayServer, eTCPMode iMode) +: TCPConnection(), + keepalive_timer(SERVER_TIMEOUT), + timeout_timer(SERVER_TIMEOUT * 2) +{ + Server = iRelayServer; + if (Server) + RelayServer = true; + else + RelayServer = false; + RelayLink = 0; + RelayCount = 0; + RemoteID = 0; + pOldFormat = iOldFormat; + TCPMode = iMode; + PacketMode = packetModeZone; +#if TCPN_DEBUG_Memory >= 7 + cout << "Constructor #1 on outgoing TCP# " << GetID() << endl; +#endif +} + +//server side relay case +EmuTCPConnection::EmuTCPConnection(uint32 ID, EmuTCPServer* iServer, EmuTCPConnection* iRelayLink, uint32 iRemoteID, uint32 irIP, uint16 irPort) +: TCPConnection(ID, 0, irIP, irPort), + keepalive_timer(SERVER_TIMEOUT), + timeout_timer(SERVER_TIMEOUT * 2) +{ + Server = iServer; + RelayLink = iRelayLink; + RelayServer = true; + RelayCount = 0; + RemoteID = iRemoteID; + if (!RemoteID) + ThrowError("Error: TCPConnection: RemoteID == 0 on RelayLink constructor"); + pOldFormat = false; + ConnectionType = Incomming; + TCPMode = modePacket; + PacketMode = packetModeZone; +#if TCPN_DEBUG_Memory >= 7 + cout << "Constructor #3 on outgoing TCP# " << GetID() << endl; +#endif +} + +EmuTCPConnection::~EmuTCPConnection() { + //the queues free their content right now I believe. +} + + +EmuTCPNetPacket_Struct* EmuTCPConnection::MakePacket(ServerPacket* pack, uint32 iDestination) { + int32 size = sizeof(EmuTCPNetPacket_Struct) + pack->size; + if (pack->compressed) { + size += 4; + } + if (iDestination) { + size += 4; + } + EmuTCPNetPacket_Struct* tnps = (EmuTCPNetPacket_Struct*) new uchar[size]; + tnps->size = size; + tnps->opcode = pack->opcode; + *((uint8*) &tnps->flags) = 0; + uchar* buffer = tnps->buffer; + if (pack->compressed) { + tnps->flags.compressed = 1; + *((int32*) buffer) = pack->InflatedSize; + buffer += 4; + } + if (iDestination) { + tnps->flags.destination = 1; + *((int32*) buffer) = iDestination; + buffer += 4; + } + memcpy(buffer, pack->pBuffer, pack->size); + return tnps; +} + +SPackSendQueue* EmuTCPConnection::MakeOldPacket(ServerPacket* pack) { + SPackSendQueue* spsq = (SPackSendQueue*) new uchar[sizeof(SPackSendQueue) + pack->size + 4]; + if (pack->pBuffer != 0 && pack->size != 0) + memcpy((char *) &spsq->buffer[4], (char *) pack->pBuffer, pack->size); + memcpy((char *) &spsq->buffer[0], (char *) &pack->opcode, 2); + spsq->size = pack->size+4; + memcpy((char *) &spsq->buffer[2], (char *) &spsq->size, 2); + return spsq; +} + +bool EmuTCPConnection::SendPacket(ServerPacket* pack, uint32 iDestination) { + if (!Connected()) + return false; + eTCPMode tmp = GetMode(); + if (tmp != modePacket && tmp != modeTransition) + return false; + LockMutex lock(&MState); + if (RemoteID) + return RelayLink->SendPacket(pack, RemoteID); + else if (pOldFormat) { + #if TCPN_LOG_PACKETS >= 1 + if (pack && pack->opcode != 0) { + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Logging outgoing TCP OldPacket. OPCode: 0x" << hex << setw(4) << setfill('0') << pack->opcode << dec << ", size: " << setw(5) << setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << endl; + #if TCPN_LOG_PACKETS == 2 + if (pack->size >= 32) + DumpPacket(pack->pBuffer, 32); + else + DumpPacket(pack); + #endif + #if TCPN_LOG_PACKETS >= 3 + DumpPacket(pack); + #endif + } + #endif + SPackSendQueue* spsq = MakeOldPacket(pack); + ServerSendQueuePushEnd(spsq->buffer, spsq->size); + safe_delete_array(spsq); + } + else { + EmuTCPNetPacket_Struct* tnps = MakePacket(pack, iDestination); + if (tmp == modeTransition) { + InModeQueuePush(tnps); + } + else { + #if TCPN_LOG_PACKETS >= 1 + if (pack && pack->opcode != 0) { + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Logging outgoing TCP packet. OPCode: 0x" << hex << setw(4) << setfill('0') << pack->opcode << dec << ", size: " << setw(5) << setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << endl; + #if TCPN_LOG_PACKETS == 2 + if (pack->size >= 32) + DumpPacket(pack->pBuffer, 32); + else + DumpPacket(pack); + #endif + #if TCPN_LOG_PACKETS >= 3 + DumpPacket(pack); + #endif + } + #endif + ServerSendQueuePushEnd((uchar**) &tnps, tnps->size); + } + } + return true; +} + +bool EmuTCPConnection::SendPacket(EmuTCPNetPacket_Struct* tnps) { + if (RemoteID) + return false; + if (!Connected()) + return false; + if (GetMode() != modePacket) + return false; + + LockMutex lock(&MState); + eTCPMode tmp = GetMode(); + if (tmp == modeTransition) { + EmuTCPNetPacket_Struct* tnps2 = (EmuTCPNetPacket_Struct*) new uchar[tnps->size]; + memcpy(tnps2, tnps, tnps->size); + InModeQueuePush(tnps2); + return true; + } + #if TCPN_LOG_PACKETS >= 1 + if (tnps && tnps->opcode != 0) { + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Logging outgoing TCP NetPacket. OPCode: 0x" << hex << setw(4) << setfill('0') << tnps->opcode << dec << ", size: " << setw(5) << setfill(' ') << tnps->size << " " << inet_ntoa(in) << ":" << GetrPort(); + if (pOldFormat) + cout << " (OldFormat)"; + cout << endl; + #if TCPN_LOG_PACKETS == 2 + if (tnps->size >= 32) + DumpPacket((uchar*) tnps, 32); + else + DumpPacket((uchar*) tnps, tnps->size); + #endif + #if TCPN_LOG_PACKETS >= 3 + DumpPacket((uchar*) tnps, tnps->size); + #endif + } + #endif + ServerSendQueuePushEnd((const uchar*) tnps, tnps->size); + return true; +} + +ServerPacket* EmuTCPConnection::PopPacket() { + ServerPacket* ret; + if (!MOutQueueLock.trylock()) + return NULL; + ret = OutQueue.pop(); + MOutQueueLock.unlock(); + return ret; +} + +void EmuTCPConnection::InModeQueuePush(EmuTCPNetPacket_Struct* tnps) { + MSendQueue.lock(); + InModeQueue.push(tnps); + MSendQueue.unlock(); +} + +void EmuTCPConnection::OutQueuePush(ServerPacket* pack) { + MOutQueueLock.lock(); + OutQueue.push(pack); + MOutQueueLock.unlock(); +} + + +bool EmuTCPConnection::LineOutQueuePush(char* line) { + #if defined(GOTFRAGS) && 0 + if (strcmp(line, "**CRASHME**") == 0) { + int i = 0; + cout << (5 / i) << endl; + } + #endif + if(line[0] == '*') { + if (strcmp(line, "**PACKETMODE**") == 0) { + MSendQueue.lock(); + safe_delete_array(sendbuf); + if (TCPMode == modeConsole) + Send((const uchar*) "\0**PACKETMODE**\r", 16); + TCPMode = modePacket; + PacketMode = packetModeLogin; + EmuTCPNetPacket_Struct* tnps = 0; + while ((tnps = InModeQueue.pop())) { + SendPacket(tnps); + safe_delete_array(tnps); + } + MSendQueue.unlock(); + safe_delete_array(line); + return(true); + } + if (strcmp(line, "**PACKETMODEZONE**") == 0) { + MSendQueue.lock(); + safe_delete_array(sendbuf); + if (TCPMode == modeConsole) + Send((const uchar*) "\0**PACKETMODEZONE**\r", 20); + TCPMode = modePacket; + PacketMode = packetModeZone; + EmuTCPNetPacket_Struct* tnps = 0; + while ((tnps = InModeQueue.pop())) { + SendPacket(tnps); + safe_delete_array(tnps); + } + MSendQueue.unlock(); + safe_delete_array(line); + return(true); + } + if (strcmp(line, "**PACKETMODELAUNCHER**") == 0) { + MSendQueue.lock(); + safe_delete_array(sendbuf); + if (TCPMode == modeConsole) + Send((const uchar*) "\0**PACKETMODELAUNCHER**\r", 24); + TCPMode = modePacket; + PacketMode = packetModeLauncher; + EmuTCPNetPacket_Struct* tnps = 0; + while ((tnps = InModeQueue.pop())) { + SendPacket(tnps); + safe_delete_array(tnps); + } + MSendQueue.unlock(); + safe_delete_array(line); + return(true); + } + if (strcmp(line, "**PACKETMODEUCS**") == 0) { + MSendQueue.lock(); + safe_delete_array(sendbuf); + if (TCPMode == modeConsole) + Send((const uchar*) "\0**PACKETMODEUCS**\r", 19); + TCPMode = modePacket; + PacketMode = packetModeUCS; + EmuTCPNetPacket_Struct* tnps = 0; + while ((tnps = InModeQueue.pop())) { + SendPacket(tnps); + safe_delete_array(tnps); + } + MSendQueue.unlock(); + safe_delete_array(line); + return(true); + } + if (strcmp(line, "**PACKETMODEQS**") == 0) { + MSendQueue.lock(); + safe_delete_array(sendbuf); + if (TCPMode == modeConsole) + Send((const uchar*) "\0**PACKETMODEQS**\r", 18); + TCPMode = modePacket; + PacketMode = packetModeQueryServ; + EmuTCPNetPacket_Struct* tnps = 0; + while ((tnps = InModeQueue.pop())) { + SendPacket(tnps); + safe_delete_array(tnps); + } + MSendQueue.unlock(); + safe_delete_array(line); + return(true); + } + + } + + return(TCPConnection::LineOutQueuePush(line)); +} + +void EmuTCPConnection::Disconnect(bool iSendRelayDisconnect) { + TCPConnection::Disconnect(); + + if (RelayLink) { + RelayLink->RemoveRelay(this, iSendRelayDisconnect); + RelayLink = 0; + } +} + +bool EmuTCPConnection::ConnectIP(uint32 irIP, uint16 irPort, char* errbuf) { + if(!TCPConnection::ConnectIP(irIP, irPort, errbuf)) + return(false); + + MSendQueue.lock(); + #ifdef MINILOGIN + TCPMode = modePacket; + #else + if (pOldFormat) { + TCPMode = modePacket; + } + else if (TCPMode == modePacket || TCPMode == modeTransition) { + TCPMode = modeTransition; + if(PacketMode == packetModeLauncher) { + safe_delete_array(sendbuf); + sendbuf_size = 24; + sendbuf_used = sendbuf_size; + sendbuf = new uchar[sendbuf_size]; + memcpy(sendbuf, "\0**PACKETMODELAUNCHER**\r", sendbuf_size); + } else if(PacketMode == packetModeLogin) { + safe_delete_array(sendbuf); + sendbuf_size = 16; + sendbuf_used = sendbuf_size; + sendbuf = new uchar[sendbuf_size]; + memcpy(sendbuf, "\0**PACKETMODE**\r", sendbuf_size); + } else if(PacketMode == packetModeUCS) { + safe_delete_array(sendbuf); + sendbuf_size = 19; + sendbuf_used = sendbuf_size; + sendbuf = new uchar[sendbuf_size]; + memcpy(sendbuf, "\0**PACKETMODEUCS**\r", sendbuf_size); + } + else if(PacketMode == packetModeQueryServ) { + safe_delete_array(sendbuf); + sendbuf_size = 18; + sendbuf_used = sendbuf_size; + sendbuf = new uchar[sendbuf_size]; + memcpy(sendbuf, "\0**PACKETMODEQS**\r", sendbuf_size); + } else { + //default: packetModeZone + safe_delete_array(sendbuf); + sendbuf_size = 20; + sendbuf_used = sendbuf_size; + sendbuf = new uchar[sendbuf_size]; + memcpy(sendbuf, "\0**PACKETMODEZONE**\r", sendbuf_size); + } + } + #endif + MSendQueue.unlock(); + + return(true); +} + +void EmuTCPConnection::ClearBuffers() { + TCPConnection::ClearBuffers(); + + LockMutex lock2(&MOutQueueLock); + ServerPacket* pack = 0; + while ((pack = OutQueue.pop())) + safe_delete(pack); + + EmuTCPNetPacket_Struct* tnps = 0; + while ((tnps = InModeQueue.pop())) + safe_delete(tnps); + + keepalive_timer.Start(); + timeout_timer.Start(); +} + + +void EmuTCPConnection::SendNetErrorPacket(const char* reason) { + #if TCPC_DEBUG >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + cout "NetError: '"; + if (reason) + cout << reason; + cout << "': " << inet_ntoa(in) << ":" << GetPort() << endl; + #endif + ServerPacket* pack = new ServerPacket(0); + pack->size = 1; + if (reason) + pack->size += strlen(reason) + 1; + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer, 0, pack->size); + pack->pBuffer[0] = 255; + strcpy((char*) &pack->pBuffer[1], reason); + SendPacket(pack); + safe_delete(pack); +} + +void EmuTCPConnection::RemoveRelay(EmuTCPConnection* relay, bool iSendRelayDisconnect) { + if (iSendRelayDisconnect) { + ServerPacket* pack = new ServerPacket(0, 5); + pack->pBuffer[0] = 3; + *((uint32*) &pack->pBuffer[1]) = relay->GetRemoteID(); + SendPacket(pack); + safe_delete(pack); + } + RelayCount--; +} + + + +bool EmuTCPConnection::ProcessReceivedData(char* errbuf) { + if (errbuf) + errbuf[0] = 0; + timeout_timer.Start(); + if (!recvbuf) + return true; + if (TCPMode == modePacket) { + if (pOldFormat) + return ProcessReceivedDataAsOldPackets(errbuf); + else + return ProcessReceivedDataAsPackets(errbuf); + } + //else, use the base class's text processing. + bool ret = TCPConnection::ProcessReceivedData(errbuf); + //see if we made the transition to packet mode... + if(ret && TCPMode == modePacket) { + return ProcessReceivedDataAsPackets(errbuf); + } + return(ret); +} + + + +bool EmuTCPConnection::ProcessReceivedDataAsPackets(char* errbuf) { + if (errbuf) + errbuf[0] = 0; + int32 base = 0; + int32 size = 7; + uchar* buffer; + ServerPacket* pack = 0; + while ((recvbuf_used - base) >= size) { + EmuTCPNetPacket_Struct* tnps = (EmuTCPNetPacket_Struct*) &recvbuf[base]; + buffer = tnps->buffer; + size = tnps->size; + if (size >= MaxTCPReceiveBuffferSize) { +#if TCPN_DEBUG_Memory >= 1 + cout << "TCPConnection[" << GetID() << "]::ProcessReceivedDataAsPackets(): size[" << size << "] >= MaxTCPReceiveBuffferSize" << endl; + DumpPacket(&recvbuf[base], 16); +#endif + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "EmuTCPConnection::ProcessReceivedDataAsPackets(): size >= MaxTCPReceiveBuffferSize"); + return false; + } + if ((recvbuf_used - base) >= size) { + // ok, we got enough data to make this packet! + pack = new ServerPacket; + pack->size = size - sizeof(EmuTCPNetPacket_Struct); + // read headers + pack->opcode = tnps->opcode; + if (tnps->flags.compressed) { + pack->compressed = true; + pack->InflatedSize = *((int32*)buffer); + pack->size -= 4; + buffer += 4; + } + if (tnps->flags.destination) { + pack->destination = *((int32*)buffer); + pack->size -= 4; + buffer += 4; + } + // end read headers + if (pack->size > 0) { + if (tnps->flags.compressed) { + // Lets decompress the packet here + pack->compressed = false; + pack->pBuffer = new uchar[pack->InflatedSize]; + pack->size = InflatePacket(buffer, pack->size, pack->pBuffer, pack->InflatedSize); + } + else { + pack->pBuffer = new uchar[pack->size]; + memcpy(pack->pBuffer, buffer, pack->size); + } + } + if (pack->opcode == 0) { + if (pack->size) { + #if TCPN_DEBUG >= 2 + cout << "Received TCP Network layer packet" << endl; + #endif + ProcessNetworkLayerPacket(pack); + } + #if TCPN_DEBUG >= 5 + else { + cout << "Received TCP keepalive packet. (opcode=0)" << endl; + } + #endif + // keepalive, no need to process + safe_delete(pack); + } + else { + #if TCPN_LOG_PACKETS >= 1 + if (pack && pack->opcode != 0) { + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Logging incoming TCP packet. OPCode: 0x" << hex << setw(4) << setfill('0') << pack->opcode << dec << ", size: " << setw(5) << setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << endl; + #if TCPN_LOG_PACKETS == 2 + if (pack->size >= 32) + DumpPacket(pack->pBuffer, 32); + else + DumpPacket(pack); + #endif + #if TCPN_LOG_PACKETS >= 3 + DumpPacket(pack); + #endif + } + #endif + if (RelayServer && Server && pack->destination) { + EmuTCPConnection* con = Server->FindConnection(pack->destination); + if (!con) { + #if TCPN_DEBUG >= 1 + cout << "Error relaying packet: con = 0" << endl; + #endif + safe_delete(pack); + } + else + con->OutQueuePush(pack); + } + else + OutQueuePush(pack); + } + base += size; + size = 7; + } + } + if (base != 0) { + if (base >= recvbuf_used) { + safe_delete_array(recvbuf); + } else { + uchar* tmpbuf = new uchar[recvbuf_size - base]; + memcpy(tmpbuf, &recvbuf[base], recvbuf_used - base); + safe_delete_array(recvbuf); + recvbuf = tmpbuf; + recvbuf_used -= base; + recvbuf_size -= base; + } + } + return true; +} + +bool EmuTCPConnection::ProcessReceivedDataAsOldPackets(char* errbuf) { + int32 base = 0; + int32 size = 4; + uchar* buffer; + ServerPacket* pack = 0; + while ((recvbuf_used - base) >= size) { + buffer = &recvbuf[base]; + memcpy(&size, &buffer[2], 2); + if (size >= MaxTCPReceiveBuffferSize) { +#if TCPN_DEBUG_Memory >= 1 + cout << "TCPConnection[" << GetID() << "]::ProcessReceivedDataAsPackets(): size[" << size << "] >= MaxTCPReceiveBuffferSize" << endl; +#endif + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "EmuTCPConnection::ProcessReceivedDataAsPackets(): size >= MaxTCPReceiveBuffferSize"); + return false; + } + if ((recvbuf_used - base) >= size) { + // ok, we got enough data to make this packet! + pack = new ServerPacket; + memcpy(&pack->opcode, &buffer[0], 2); + pack->size = size - 4; +/* if () { // TODO: Checksum or size check or something similar + // Datastream corruption, get the hell outta here! + delete pack; + return false; + }*/ + if (pack->size > 0) { + pack->pBuffer = new uchar[pack->size]; + memcpy(pack->pBuffer, &buffer[4], pack->size); + } + if (pack->opcode == 0) { + // keepalive, no need to process + safe_delete(pack); + } + else { + #if TCPN_LOG_PACKETS >= 1 + if (pack && pack->opcode != 0) { + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Logging incoming TCP OldPacket. OPCode: 0x" << hex << setw(4) << setfill('0') << pack->opcode << dec << ", size: " << setw(5) << setfill(' ') << pack->size << " " << inet_ntoa(in) << ":" << GetrPort() << endl; + #if TCPN_LOG_PACKETS == 2 + if (pack->size >= 32) + DumpPacket(pack->pBuffer, 32); + else + DumpPacket(pack); + #endif + #if TCPN_LOG_PACKETS >= 3 + DumpPacket(pack); + #endif + } + #endif + OutQueuePush(pack); + } + base += size; + size = 4; + } + } + if (base != 0) { + if (base >= recvbuf_used) { + safe_delete_array(recvbuf); + } + else { + uchar* tmpbuf = new uchar[recvbuf_size - base]; + memcpy(tmpbuf, &recvbuf[base], recvbuf_used - base); + safe_delete_array(recvbuf); + recvbuf = tmpbuf; + recvbuf_used -= base; + recvbuf_size -= base; + } + } + return true; +} + +void EmuTCPConnection::ProcessNetworkLayerPacket(ServerPacket* pack) { + uint8 opcode = pack->pBuffer[0]; + uint8* data = &pack->pBuffer[1]; + switch (opcode) { + case 0: { + break; + } + case 1: { // Switch to RelayServer mode + if (pack->size != 1) { + SendNetErrorPacket("New RelayClient: wrong size, expected 1"); + break; + } + if (RelayServer) { + SendNetErrorPacket("Switch to RelayServer mode when already in RelayServer mode"); + break; + } + if (RemoteID) { + SendNetErrorPacket("Switch to RelayServer mode by a Relay Client"); + break; + } + if (ConnectionType != Incomming) { + SendNetErrorPacket("Switch to RelayServer mode on outgoing connection"); + break; + } + #if TCPC_DEBUG >= 3 + struct in_addr in; + in.s_addr = GetrIP(); + cout << "Switching to RelayServer mode: " << inet_ntoa(in) << ":" << GetPort() << endl; + #endif + RelayServer = true; + break; + } + case 2: { // New Relay Client + if (!RelayServer) { + SendNetErrorPacket("New RelayClient when not in RelayServer mode"); + break; + } + if (pack->size != 11) { + SendNetErrorPacket("New RelayClient: wrong size, expected 11"); + break; + } + if (ConnectionType != Incomming) { + SendNetErrorPacket("New RelayClient: illegal on outgoing connection"); + break; + } + EmuTCPConnection* con = new EmuTCPConnection(Server->GetNextID(), Server, this, *((uint32*) data), *((uint32*) &data[4]), *((uint16*) &data[8])); + Server->AddConnection(con); + RelayCount++; + break; + } + case 3: { // Delete Relay Client + if (!RelayServer) { + SendNetErrorPacket("Delete RelayClient when not in RelayServer mode"); + break; + } + if (pack->size != 5) { + SendNetErrorPacket("Delete RelayClient: wrong size, expected 5"); + break; + } + EmuTCPConnection* con = Server->FindConnection(*((uint32*)data)); + if (con) { + if (ConnectionType == Incomming) { + if (con->GetRelayLink() != this) { + SendNetErrorPacket("Delete RelayClient: RelayLink != this"); + break; + } + } + con->Disconnect(false); + } + break; + } + case 255: { + #if TCPC_DEBUG >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + cout "Received NetError: '"; + if (pack->size > 1) + cout << (char*) data; + cout << "': " << inet_ntoa(in) << ":" << GetPort() << endl; + #endif + break; + } + } +} + +bool EmuTCPConnection::SendData(bool &sent_something, char* errbuf) { + sent_something = false; + if(!TCPConnection::SendData(sent_something, errbuf)) + return(false); + + if(sent_something) + keepalive_timer.Start(); + else if (TCPMode == modePacket && keepalive_timer.Check()) { + ServerPacket* pack = new ServerPacket(0, 0); + SendPacket(pack); + safe_delete(pack); + #if TCPN_DEBUG >= 5 + cout << "Sending TCP keepalive packet. (timeout=" << timeout_timer.GetRemainingTime() << " remaining)" << endl; + #endif + } + + return(true); +} + +bool EmuTCPConnection::RecvData(char* errbuf) { + if(!TCPConnection::RecvData(errbuf)) { + if (OutQueue.count()) + return(true); + else + return(false); + } + + if ((TCPMode == modePacket || TCPMode == modeTransition) && timeout_timer.Check()) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Connection timeout"); + return false; + } + + return(true); +} + + + + + + + + + + + + diff --git a/common/EmuTCPConnection.h b/common/EmuTCPConnection.h new file mode 100644 index 000000000..8d567154d --- /dev/null +++ b/common/EmuTCPConnection.h @@ -0,0 +1,103 @@ +#ifndef EmuTCPCONNECTION_H_ +#define EmuTCPCONNECTION_H_ + +#include "TCPConnection.h" +#include "timer.h" + +//moved out of TCPConnection:: to be more exportable +#pragma pack(1) + struct EmuTCPNetPacket_Struct { + uint32 size; + struct { + uint8 + compressed : 1, + destination : 1, + flag3 : 1, + flag4 : 1, + flag5 : 1, + flag6 : 1, + flag7 : 1, + flag8 : 1; + } flags; + uint16 opcode; + uchar buffer[0]; + }; +#pragma pack() + +struct SPackSendQueue; +class EmuTCPServer; + +class EmuTCPConnection : public TCPConnection { +public: + enum eTCPMode { modeConsole, modeTransition, modePacket }; + enum ePacketMode { packetModeZone, packetModeLauncher, packetModeLogin, packetModeUCS, packetModeQueryServ }; + + EmuTCPConnection(uint32 ID, EmuTCPServer* iServer, SOCKET iSock, uint32 irIP, uint16 irPort, bool iOldFormat = false); + EmuTCPConnection(bool iOldFormat = false, EmuTCPServer* iRelayServer = 0, eTCPMode iMode = modePacket); // for outgoing connections + EmuTCPConnection(uint32 ID, EmuTCPServer* iServer, EmuTCPConnection* iRelayLink, uint32 iRemoteID, uint32 irIP, uint16 irPort); // for relay connections + virtual ~EmuTCPConnection(); + + virtual bool ConnectIP(uint32 irIP, uint16 irPort, char* errbuf = 0); + virtual void Disconnect(bool iSendRelayDisconnect = true); + + static EmuTCPNetPacket_Struct* MakePacket(ServerPacket* pack, uint32 iDestination = 0); + static SPackSendQueue* MakeOldPacket(ServerPacket* pack); + + virtual bool SendPacket(ServerPacket* pack, uint32 iDestination = 0); + virtual bool SendPacket(EmuTCPNetPacket_Struct* tnps); + ServerPacket* PopPacket(); // OutQueuePop() + void SetPacketMode(ePacketMode mode) { PacketMode = mode; } + + eTCPMode GetMode() const { return TCPMode; } + ePacketMode GetPacketMode() const { return(PacketMode); } + + //relay crap: + inline bool IsRelayServer() const { return RelayServer; } + inline TCPConnection* GetRelayLink() const { return RelayLink; } + inline uint32 GetRemoteID() const { return RemoteID; } + +protected: + void OutQueuePush(ServerPacket* pack); + void RemoveRelay(EmuTCPConnection* relay, bool iSendRelayDisconnect); + + void SendNetErrorPacket(const char* reason = 0); + + virtual bool SendData(bool &sent_something, char* errbuf = 0); + virtual bool RecvData(char* errbuf = 0); + + virtual bool ProcessReceivedData(char* errbuf = 0); + bool ProcessReceivedDataAsPackets(char* errbuf = 0); + bool ProcessReceivedDataAsOldPackets(char* errbuf = 0); + void ProcessNetworkLayerPacket(ServerPacket* pack); + + virtual bool LineOutQueuePush(char* line); + virtual void ClearBuffers(); + + EmuTCPServer* Server; + + eTCPMode TCPMode; + ePacketMode PacketMode; + bool pOldFormat; + + Timer keepalive_timer; + Timer timeout_timer; + + //relay crap: + EmuTCPConnection* RelayLink; + int32 RelayCount; + bool RelayServer; + uint32 RemoteID; + + //input queue... + void InModeQueuePush(EmuTCPNetPacket_Struct* tnps); + MyQueue InModeQueue; + + //output queue... + MyQueue OutQueue; + Mutex MOutQueueLock; +}; + +#endif /*EmuTCPCONNECTION_H_*/ + + + diff --git a/common/EmuTCPServer.cpp b/common/EmuTCPServer.cpp new file mode 100644 index 000000000..63cfeef1a --- /dev/null +++ b/common/EmuTCPServer.cpp @@ -0,0 +1,96 @@ + + + + +#include "debug.h" +#include "EmuTCPServer.h" +#include "EmuTCPConnection.h" + +EmuTCPServer::EmuTCPServer(uint16 iPort, bool iOldFormat) +: TCPServer(iPort), + pOldFormat(iOldFormat) +{ +} + +EmuTCPServer::~EmuTCPServer() { + MInQueue.lock(); + while(!m_InQueue.empty()) { + delete m_InQueue.front(); + m_InQueue.pop(); + } + MInQueue.unlock(); +} + +void EmuTCPServer::Process() { + CheckInQueue(); + TCPServer::Process(); +} + +void EmuTCPServer::CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort) +{ + EmuTCPConnection *conn = new EmuTCPConnection(ID, this, in_socket, irIP, irPort, pOldFormat); + AddConnection(conn); +} + + +void EmuTCPServer::SendPacket(ServerPacket* pack) { + EmuTCPNetPacket_Struct* tnps = EmuTCPConnection::MakePacket(pack); + SendPacket(&tnps); +} + +void EmuTCPServer::SendPacket(EmuTCPNetPacket_Struct** tnps) { + MInQueue.lock(); + m_InQueue.push(*tnps); + MInQueue.unlock(); + tnps = NULL; +} + +void EmuTCPServer::CheckInQueue() { + EmuTCPNetPacket_Struct* tnps = 0; + + while (( tnps = InQueuePop() )) { + vitr cur, end; + cur = m_list.begin(); + end = m_list.end(); + for(; cur != end; cur++) { + if ((*cur)->GetMode() != EmuTCPConnection::modeConsole && (*cur)->GetRemoteID() == 0) + (*cur)->SendPacket(tnps); + } + safe_delete(tnps); + } +} + +EmuTCPNetPacket_Struct* EmuTCPServer::InQueuePop() { + EmuTCPNetPacket_Struct* ret = NULL; + MInQueue.lock(); + if(!m_InQueue.empty()) { + ret = m_InQueue.front(); + m_InQueue.pop(); + } + MInQueue.unlock(); + return ret; +} + + +EmuTCPConnection *EmuTCPServer::FindConnection(uint32 iID) { + vitr cur, end; + cur = m_list.begin(); + end = m_list.end(); + for(; cur != end; cur++) { + if ((*cur)->GetID() == iID) + return *cur; + } + return(NULL); +} + + + + + + + + + + + + diff --git a/common/EmuTCPServer.h b/common/EmuTCPServer.h new file mode 100644 index 000000000..fcb49d274 --- /dev/null +++ b/common/EmuTCPServer.h @@ -0,0 +1,38 @@ +#ifndef EmuTCPSERVER_H_ +#define EmuTCPSERVER_H_ + +#include "TCPServer.h" + +class EmuTCPConnection; +struct EmuTCPNetPacket_Struct; +class ServerPacket; + +class EmuTCPServer : public TCPServer { +public: + EmuTCPServer(uint16 iPort = 0, bool iOldFormat = false); + virtual ~EmuTCPServer(); + + //packet broadcast routines. + void SendPacket(ServerPacket* pack); + void SendPacket(EmuTCPNetPacket_Struct** tnps); + + //special crap for relay management + EmuTCPConnection *FindConnection(uint32 iID); + + //exposed for some crap we pull. Do not call from outside this object. + TCPServer::AddConnection; + +protected: + virtual void Process(); + + virtual void CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort); + + bool pOldFormat; + + //broadcast packet queue.. + void CheckInQueue(); + Mutex MInQueue; + EmuTCPNetPacket_Struct* InQueuePop(); //returns ownership + std::queue m_InQueue; +}; +#endif /*EmuTCPSERVER_H_*/ diff --git a/common/Item.cpp b/common/Item.cpp new file mode 100644 index 000000000..db9317f76 --- /dev/null +++ b/common/Item.cpp @@ -0,0 +1,1951 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 +*/ + +#ifdef _WINDOWS + // VS6 doesn't like the length of STL generated names: disabling + #pragma warning(disable:4786) + // Quagmire: Dont know why the one in debug.h doesnt work, but it doesnt. +#endif +#include "../common/debug.h" +/*#ifdef _CRTDBG_MAP_ALLOC + #undef new + #define new new(_NORMAL_BLOCK, __FILE__, __LINE__) +#endif +*/ +#include +#include +#include +#include "Item.h" +#include "database.h" +#include "misc.h" +#include "races.h" +#include "shareddb.h" +#include "classes.h" +using namespace std; + +int32 NextItemInstSerialNumber = 1; + +static inline int32 GetNextItemInstSerialNumber() { + + // The Bazaar relies on each item a client has up for Trade having a unique + // identifier. This 'SerialNumber' is sent in Serialized item packets and + // is used in Bazaar packets to identify the item a player is buying or inspecting. + // + // E.g. A trader may have 3 Five dose cloudy potions, each with a different number of remaining charges + // up for sale with different prices. + // + // NextItemInstSerialNumber is the next one to hand out. + // + // It is very unlikely to reach 2,147,483,647. Maybe we should call abort(), rather than wrapping back to 1. + if(NextItemInstSerialNumber >= INT_MAX) + NextItemInstSerialNumber = 1; + else + NextItemInstSerialNumber++; + + return NextItemInstSerialNumber; +} + +ItemInst::ItemInst(const Item_Struct* item, int16 charges) { + m_use_type = ItemUseNormal; + m_item = item; + m_charges = charges; + m_price = 0; + m_instnodrop = false; + m_merchantslot = 0; + if(m_item &&m_item->ItemClass == ItemClassCommon) + m_color = m_item->Color; + else + m_color = 0; + m_merchantcount = 1; + m_SerialNumber = GetNextItemInstSerialNumber(); +} + +ItemInst::ItemInst(SharedDatabase *db, uint32 item_id, int16 charges) { + m_use_type = ItemUseNormal; + m_item = db->GetItem(item_id); + m_charges = charges; + m_price = 0; + m_merchantslot = 0; + m_instnodrop=false; + if(m_item && m_item->ItemClass == ItemClassCommon) + m_color = m_item->Color; + else + m_color = 0; + m_merchantcount = 1; + m_SerialNumber = GetNextItemInstSerialNumber(); +} + +ItemInstQueue::~ItemInstQueue() { + iter_queue cur,end; + cur = m_list.begin(); + end = m_list.end(); + for(; cur != end; cur++) { + ItemInst *tmp = * cur; + safe_delete(tmp); + } + m_list.clear(); +} + +Inventory::~Inventory() { + map::iterator cur,end; + + + cur = m_worn.begin(); + end = m_worn.end(); + for(; cur != end; cur++) { + ItemInst *tmp = cur->second; + safe_delete(tmp); + } + m_worn.clear(); + + cur = m_inv.begin(); + end = m_inv.end(); + for(; cur != end; cur++) { + ItemInst *tmp = cur->second; + safe_delete(tmp); + } + m_inv.clear(); + + cur = m_bank.begin(); + end = m_bank.end(); + for(; cur != end; cur++) { + ItemInst *tmp = cur->second; + safe_delete(tmp); + } + m_bank.clear(); + + cur = m_shbank.begin(); + end = m_shbank.end(); + for(; cur != end; cur++) { + ItemInst *tmp = cur->second; + safe_delete(tmp); + } + m_shbank.clear(); + + cur = m_trade.begin(); + end = m_trade.end(); + for(; cur != end; cur++) { + ItemInst *tmp = cur->second; + safe_delete(tmp); + } + m_trade.clear(); +} + +// Make a copy of an ItemInst object +ItemInst::ItemInst(const ItemInst& copy) +{ + m_use_type=copy.m_use_type; + m_item=copy.m_item; + m_charges=copy.m_charges; + m_price=copy.m_price; + m_color=copy.m_color; + m_merchantslot=copy.m_merchantslot; + m_currentslot=copy.m_currentslot; + m_instnodrop=copy.m_instnodrop; + m_merchantcount=copy.m_merchantcount; + // Copy container contents + iter_contents it; + for (it=copy.m_contents.begin(); it!=copy.m_contents.end(); it++) { + ItemInst* inst_old = it->second; + ItemInst* inst_new = NULL; + + if (inst_old) { + inst_new = inst_old->Clone(); + } + + if (inst_new != NULL) { + m_contents[it->first] = inst_new; + } + } + std::map::const_iterator iter; + for (iter = copy.m_custom_data.begin(); iter != copy.m_custom_data.end(); iter++) { + m_custom_data[iter->first] = iter->second; + } + m_SerialNumber = copy.m_SerialNumber; + m_custom_data = copy.m_custom_data; +} + +// Clean up container contents +ItemInst::~ItemInst() +{ + Clear(); +} + +// Clone a type of ItemInst object +// c++ doesn't allow a polymorphic copy constructor, +// so we have to resort to a polymorphic Clone() +ItemInst* ItemInst::Clone() const +{ + // Pseudo-polymorphic copy constructor + return new ItemInst(*this); +} + +// Query item type +bool ItemInst::IsType(ItemClass item_class) const +{ + // Check usage type + if ((m_use_type == ItemUseWorldContainer) && (item_class == ItemClassContainer)) + + return true; + if (!m_item) + return false; + + return (m_item->ItemClass == item_class); +} + +// Is item stackable? +bool ItemInst::IsStackable() const +{ + return m_item->Stackable; +} + +// Can item be equipped? + +bool ItemInst::IsEquipable(uint16 race, uint16 class_) const +{ + if (!m_item || (m_item->Slots == 0)) + return false; + + return m_item->IsEquipable(race, class_); +} + +// Can equip at this slot? +bool ItemInst::IsEquipable(int16 slot_id) const +{ + if (!m_item) + return false; + + if(slot_id == 9999) { + slot_id = 22; + uint32 slot_mask = (1 << slot_id); + if (slot_mask & m_item->Slots) + return true; + } + + if (slot_id < 22) { + uint32 slot_mask = (1 << slot_id); + if (slot_mask & m_item->Slots) + return true; + } + + return false; +} + +int8 ItemInst::AvailableAugmentSlot(int32 augtype) const +{ + if (m_item->ItemClass != ItemClassCommon || !m_item) + return -1; + + int i; + for (i=0;i<5;i++) { + if (!GetItem(i)) { + if (augtype==-1 || (m_item->AugSlotType[i] && ((1<<(m_item->AugSlotType[i]-1)) & augtype))) + break; + } + + } + + return (i<5) ? i : -1; +} + +bool ItemInst::AvailableWearSlot(uint32 aug_wear_slots) const +{ + if (m_item->ItemClass != ItemClassCommon || !m_item) + return false; + + int i; + for(i=0; i<23; i++) { + if(m_item->Slots & (1<ItemClass == ItemClassCommon) { + return GetItemID(slot); + } + + return id; +} + +uint32 ItemInst::GetItemID(uint8 slot) const +{ +const ItemInst *item; +uint32 id=0; + if ((item=GetItem(slot))!=NULL) + id= item->GetItem()->ID; + + return id; +} + + +// Has attack/delay? +bool ItemInst::IsWeapon() const +{ + if (!m_item || m_item->ItemClass != ItemClassCommon) + return false; + if(m_item->ItemType==ItemTypeArrow && m_item->Damage != 0) + return true; + else + return ((m_item->Damage != 0) && (m_item->Delay != 0)); +} + +bool ItemInst::IsAmmo() const { + + if(!m_item) return false; + + if((m_item->ItemType == ItemTypeArrow) || + (m_item->ItemType == ItemTypeThrowing) || + (m_item->ItemType == ItemTypeThrowingv2)) + return true; + + return false; + +} + +// Retrieve augment inside item +ItemInst* ItemInst::GetAugment(uint8 slot) const +{ + if (m_item->ItemClass == ItemClassCommon) + return GetItem(slot); + + return NULL; +} + +// Remove augment from item and destroy it +void ItemInst::DeleteAugment(uint8 index) +{ + if (m_item->ItemClass == ItemClassCommon) + DeleteItem(index); +} + +// Remove augment from item and return it +ItemInst* ItemInst::RemoveAugment(uint8 index) +{ + if (m_item->ItemClass == ItemClassCommon) + return PopItem(index); + + return NULL; +} + +// Add an augment to the item +void ItemInst::PutAugment(uint8 slot, const ItemInst& augment) +{ + if (m_item->ItemClass == ItemClassCommon) + PutItem(slot,augment); +} + +void ItemInst::PutAugment(SharedDatabase *db, uint8 slot, uint32 item_id) +{ + if (item_id != 0) { + const ItemInst* aug = db->CreateItem(item_id); + if(aug) + { + PutAugment(slot,*aug); + safe_delete(aug); + } + } +} + +// Retrieve item inside container +ItemInst* ItemInst::GetItem(uint8 index) const +{ + iter_contents it = m_contents.find(index); + if (it != m_contents.end()) { + ItemInst* inst = it->second; + return inst; + } + + return NULL; +} + +void ItemInst::PutItem(uint8 index, const ItemInst& inst) +{ + // Clean up item already in slot (if exists) + DeleteItem(index); + + + // Delegate to internal method + _PutItem(index, inst.Clone()); +} + +// Remove item inside container +void ItemInst::DeleteItem(uint8 index) +{ + ItemInst* inst = PopItem(index); + safe_delete(inst); +} + +// Remove all items from container +void ItemInst::Clear() +{ + // Destroy container contents + iter_contents cur, end; + cur = m_contents.begin(); + end = m_contents.end(); + for (; cur != end; cur++) { + ItemInst* inst = cur->second; + safe_delete(inst); + } + m_contents.clear(); +} + +// Remove all items from container +void ItemInst::ClearByFlags(byFlagSetting is_nodrop, byFlagSetting is_norent) +{ + // Destroy container contents + iter_contents cur, end, del; + cur = m_contents.begin(); + end = m_contents.end(); + for (; cur != end;) { + ItemInst* inst = cur->second; + const Item_Struct* item = inst->GetItem(); + del = cur; + cur++; + + switch(is_nodrop) { + case byFlagSet: + if (item->NoDrop == 0) { + safe_delete(inst); + m_contents.erase(del->first); + continue; + } + case byFlagNotSet: + if (item->NoDrop != 0) { + safe_delete(inst); + m_contents.erase(del->first); + continue; + } + default: + break; + } + + switch(is_norent) { + case byFlagSet: + if (item->NoRent == 0) { + safe_delete(inst); + m_contents.erase(del->first); + continue; + } + case byFlagNotSet: + if (item->NoRent != 0) { + safe_delete(inst); + m_contents.erase(del->first); + continue; + } + default: + break; + } + } +} + +// Remove item from container without memory delete +// Hands over memory ownership to client of this function call +ItemInst* ItemInst::PopItem(uint8 index) +{ + iter_contents it = m_contents.find(index); + if (it != m_contents.end()) { + ItemInst* inst = it->second; + m_contents.erase(index); + return inst; + } + + // Return pointer that needs to be deleted (or otherwise managed) + return NULL; +} + +// Put item onto back of queue +void ItemInstQueue::push(ItemInst* inst) +{ + m_list.push_back(inst); +} + +// Put item onto front of queue +void ItemInstQueue::push_front(ItemInst* inst) +{ + m_list.push_front(inst); +} + +// Remove item from front of queue +ItemInst* ItemInstQueue::pop() +{ + if (m_list.size() == 0) + return NULL; + + ItemInst* inst = m_list.front(); + m_list.pop_front(); + return inst; +} + +// Look at item at front of queue +ItemInst* ItemInstQueue::peek_front() const +{ + return (m_list.size()==0) ? NULL : m_list.front(); +} + +// Retrieve item at specified slot; returns false if item not found +ItemInst* Inventory::GetItem(int16 slot_id) const +{ + _CP(Inventory_GetItem); + ItemInst* result = NULL; + + // Cursor + if (slot_id == SLOT_CURSOR) { + // Cursor slot + result = m_cursor.peek_front(); + } + + // Non bag slots + else if (slot_id>=3000 && slot_id<=3007) { + // Trade slots + result = _GetItem(m_trade, slot_id); + } + else if (slot_id>=2500 && slot_id<=2501) { + // Shared Bank slots + result = _GetItem(m_shbank, slot_id); + } + else if (slot_id>=2000 && slot_id<=2023) { + // Bank slots + result = _GetItem(m_bank, slot_id); + } + else if ((slot_id>=22 && slot_id<=29)) { + // Personal inventory slots + result = _GetItem(m_inv, slot_id); + } + else if ((slot_id>=0 && slot_id<=21) || (slot_id >= 400 && slot_id<=404) || (slot_id == 9999)) { + // Equippable slots (on body) + result = _GetItem(m_worn, slot_id); + } + + // Inner bag slots + else if (slot_id>=3031 && slot_id<=3110) { + // Trade bag slots + ItemInst* inst = _GetItem(m_trade, Inventory::CalcSlotId(slot_id)); + if (inst && inst->IsType(ItemClassContainer)) { + result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); + } + } + else if (slot_id>=2531 && slot_id<=2550) { + // Shared Bank bag slots + ItemInst* inst = _GetItem(m_shbank, Inventory::CalcSlotId(slot_id)); + if (inst && inst->IsType(ItemClassContainer)) { + result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); + } + } + else if (slot_id>=2031 && slot_id<=2270) { + // Bank bag slots + ItemInst* inst = _GetItem(m_bank, Inventory::CalcSlotId(slot_id)); + if (inst && inst->IsType(ItemClassContainer)) { + result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); + } + } + else if (slot_id>=331 && slot_id<=340) { + // Cursor bag slots + ItemInst* inst = m_cursor.peek_front(); + if (inst && inst->IsType(ItemClassContainer)) { + result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); + } + } + else if (slot_id>=251 && slot_id<=330) { + // Personal inventory bag slots + ItemInst* inst = _GetItem(m_inv, Inventory::CalcSlotId(slot_id)); + if (inst && inst->IsType(ItemClassContainer)) { + result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); + } + } + + return result; +} + +std::string ItemInst::GetCustomDataString() const { + std::string ret_val; + map::const_iterator iter = m_custom_data.begin(); + while(iter != m_custom_data.end()) { + if(ret_val.length() > 0) { + ret_val += "^"; + } + ret_val += iter->first; + ret_val += "^"; + ret_val += iter->second; + iter++; + + if(ret_val.length() > 0) { + ret_val += "^"; + } + } + return ret_val; +} + +void ItemInst::SetCustomData(std::string identifier, std::string value) { + DeleteCustomData(identifier); + m_custom_data[identifier] = value; +} + +void ItemInst::SetCustomData(std::string identifier, int value) { + DeleteCustomData(identifier); + std::stringstream ss; + ss << value; + m_custom_data[identifier] = ss.str(); +} + +void ItemInst::SetCustomData(std::string identifier, float value) { + DeleteCustomData(identifier); + std::stringstream ss; + ss << value; + m_custom_data[identifier] = ss.str(); +} + +void ItemInst::SetCustomData(std::string identifier, bool value) { + DeleteCustomData(identifier); + std::stringstream ss; + ss << value; + m_custom_data[identifier] = ss.str(); +} + +void ItemInst::DeleteCustomData(std::string identifier) { + map::iterator iter = m_custom_data.find(identifier); + if(iter != m_custom_data.end()) { + m_custom_data.erase(iter); + } +} + +std::string ItemInst::GetCustomData(std::string identifier) { + map::const_iterator iter = m_custom_data.find(identifier); + if(iter != m_custom_data.end()) { + return iter->second; + } + + return ""; +} + +// Retrieve item at specified position within bag +ItemInst* Inventory::GetItem(int16 slot_id, uint8 bagidx) const +{ + return GetItem(Inventory::CalcSlotId(slot_id, bagidx)); +} + +int16 Inventory::PushCursor(const ItemInst& inst) +{ + m_cursor.push(inst.Clone()); + return SLOT_CURSOR; +} + +// Put an item snto specified slot +int16 Inventory::PutItem(int16 slot_id, const ItemInst& inst) +{ + // Clean up item already in slot (if exists) + DeleteItem(slot_id); + + if (!inst) { + // User is effectively deleting the item + // in the slot, why hold a null ptr in map<>? + return slot_id; + } + + // Delegate to internal method + return _PutItem(slot_id, inst.Clone()); +} + +// Swap items in inventory +bool Inventory::SwapItem(int16 slot_a, int16 slot_b) +{ + // Temp holding areas for a and b + ItemInst* inst_a = GetItem(slot_a); + ItemInst* inst_b = GetItem(slot_b); + + if(inst_a) { if(!inst_a->IsSlotAllowed(slot_b)) { return false; } } + if(inst_b) { if(!inst_b->IsSlotAllowed(slot_a)) { return false; } } + + _PutItem(slot_a, inst_b); // Copy b->a + _PutItem(slot_b, inst_a); // Copy a->b + + return true; +} + +// Checks that user has at least 'quantity' number of items in a given inventory slot +// Returns first slot it was found in, or SLOT_INVALID if not found + +//This function has a flaw in that it only returns the last stack that it looked at +//when quantity is greater than 1 and not all of quantity can be found in 1 stack. + +int16 Inventory::HasItem(uint32 item_id, uint8 quantity, uint8 where) +{ + _CP(Inventory_HasItem); + int16 slot_id = SLOT_INVALID; + + //Altered by Father Nitwit to support a specification of + //where to search, with a default value to maintain compatibility + + // Check each inventory bucket + if(where & invWhereWorn) { + slot_id = _HasItem(m_worn, item_id, quantity); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWherePersonal) { + slot_id = _HasItem(m_inv, item_id, quantity); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWhereBank) { + slot_id = _HasItem(m_bank, item_id, quantity); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWhereSharedBank) { + slot_id = _HasItem(m_shbank, item_id, quantity); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWhereTrading) { + slot_id = _HasItem(m_trade, item_id, quantity); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWhereCursor) { + // Check cursor queue + slot_id = _HasItem(m_cursor, item_id, quantity); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + return slot_id; +} + +//this function has the same quantity flaw mentioned above in HasItem() + +int16 Inventory::HasItemByUse(uint8 use, uint8 quantity, uint8 where) +{ + int16 slot_id = SLOT_INVALID; + + // Check each inventory bucket + if(where & invWhereWorn) { + slot_id = _HasItemByUse(m_worn, use, quantity); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWherePersonal) { + slot_id = _HasItemByUse(m_inv, use, quantity); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWhereBank) { + slot_id = _HasItemByUse(m_bank, use, quantity); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWhereSharedBank) { + slot_id = _HasItemByUse(m_shbank, use, quantity); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWhereTrading) { + slot_id = _HasItemByUse(m_trade, use, quantity); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWhereCursor) { + // Check cursor queue + slot_id = _HasItemByUse(m_cursor, use, quantity); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + return slot_id; +} + +int16 Inventory::HasItemByLoreGroup(uint32 loregroup, uint8 where) +{ + int16 slot_id = SLOT_INVALID; + + // Check each inventory bucket + if(where & invWhereWorn) { + slot_id = _HasItemByLoreGroup(m_worn, loregroup); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWherePersonal) { + slot_id = _HasItemByLoreGroup(m_inv, loregroup); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWhereBank) { + slot_id = _HasItemByLoreGroup(m_bank, loregroup); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWhereSharedBank) { + slot_id = _HasItemByLoreGroup(m_shbank, loregroup); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWhereTrading) { + slot_id = _HasItemByLoreGroup(m_trade, loregroup); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + if(where & invWhereCursor) { + // Check cursor queue + slot_id = _HasItemByLoreGroup(m_cursor, loregroup); + if (slot_id != SLOT_INVALID) + return slot_id; + } + + return slot_id; +} + +bool Inventory::HasSpaceForItem(const Item_Struct *ItemToTry, int16 Quantity) { + + if(ItemToTry->Stackable) { + + for(int16 i = 22; i <= 29; i++) { + + ItemInst* InvItem = GetItem(i); + + if(InvItem && (InvItem->GetItem()->ID == ItemToTry->ID) && (InvItem->GetCharges() < InvItem->GetItem()->StackSize)) { + + int ChargeSlotsLeft = InvItem->GetItem()->StackSize - InvItem->GetCharges(); + + if(Quantity <= ChargeSlotsLeft) + return true; + + Quantity -= ChargeSlotsLeft; + + } + if (InvItem && InvItem->IsType(ItemClassContainer)) { + + int16 BaseSlotID = Inventory::CalcSlotId(i, 0); + uint8 BagSize=InvItem->GetItem()->BagSlots; + for (uint8 BagSlot = 0; BagSlot < BagSize; BagSlot++) { + + InvItem = GetItem(BaseSlotID + BagSlot); + + if(InvItem && (InvItem->GetItem()->ID == ItemToTry->ID) && + (InvItem->GetCharges() < InvItem->GetItem()->StackSize)) { + + int ChargeSlotsLeft = InvItem->GetItem()->StackSize - InvItem->GetCharges(); + + if(Quantity <= ChargeSlotsLeft) + return true; + + Quantity -= ChargeSlotsLeft; + } + } + } + } + } + + for (int16 i = 22; i <= 29; i++) { + + ItemInst* InvItem = GetItem(i); + + if (!InvItem) { + + if(!ItemToTry->Stackable) { + + if(Quantity == 1) + return true; + else + Quantity--; + } + else { + if(Quantity <= ItemToTry->StackSize) + return true; + else + Quantity -= ItemToTry->StackSize; + } + + } + else if(InvItem->IsType(ItemClassContainer) && CanItemFitInContainer(ItemToTry, InvItem->GetItem())) { + + int16 BaseSlotID = Inventory::CalcSlotId(i, 0); + + uint8 BagSize=InvItem->GetItem()->BagSlots; + + for (uint8 BagSlot=0; BagSlotStackable) { + + if(Quantity == 1) + return true; + else + Quantity--; + } + else { + if(Quantity <= ItemToTry->StackSize) + return true; + else + Quantity -= ItemToTry->StackSize; + } + } + } + } + } + + return false; + +} + +// Remove item from inventory (with memory delete) +bool Inventory::DeleteItem(int16 slot_id, uint8 quantity) +{ + // Pop item out of inventory map (or queue) + ItemInst* item_to_delete = PopItem(slot_id); + + // Determine if object should be fully deleted, or + // just a quantity of charges of the item can be deleted + if (item_to_delete && (quantity > 0)) { + + item_to_delete->SetCharges(item_to_delete->GetCharges() - quantity); + + // If there are no charges left on the item, + if(item_to_delete->GetCharges() <= 0) { + // If the item is stackable (e.g arrows), or + // the item is not stackable, and is not a charged item, or is expendable, delete it + if(item_to_delete->IsStackable() || + (!item_to_delete->IsStackable() && + ((item_to_delete->GetItem()->MaxCharges == 0) || item_to_delete->IsExpendable()))) { + // Item can now be destroyed + safe_delete(item_to_delete); + return true; + } + } + + // Charges still exist, or it is a charged item that is not expendable. Put back into inventory + _PutItem(slot_id, item_to_delete); + return false; + } + + safe_delete(item_to_delete); + + return true; + +} + +// Checks All items in a bag for No Drop +bool Inventory::CheckNoDrop(int16 slot_id) { + ItemInst* inst = GetItem(slot_id); + if (!inst) return false; + if (!inst->GetItem()->NoDrop) return true; + if (inst->GetItem()->ItemClass == 1) { + for (uint16 i=0; i<10; i++) { + ItemInst* bagitem = GetItem(Inventory::CalcSlotId(slot_id, i)); + if (bagitem && !bagitem->GetItem()->NoDrop) return true; + } + } + return false; +} + +// Remove item from bucket without memory delete +// Returns item pointer if full delete was successful +ItemInst* Inventory::PopItem(int16 slot_id) +{ + ItemInst* p = NULL; + + if (slot_id==SLOT_CURSOR) { // Cursor + p = m_cursor.pop(); + } + else if ((slot_id>=0 && slot_id<=21) || (slot_id >= 400 && slot_id<=404) || (slot_id == 9999)) { // Worn slots + p = m_worn[slot_id]; + m_worn.erase(slot_id); + } + else if ((slot_id>=22 && slot_id<=29)) { + p = m_inv[slot_id]; + m_inv.erase(slot_id); + } + else if (slot_id>=2000 && slot_id<=2023) { // Bank slots + p = m_bank[slot_id]; + m_bank.erase(slot_id); + } + else if (slot_id>=2500 && slot_id<=2501) { // Shared bank slots + p = m_shbank[slot_id]; + m_shbank.erase(slot_id); + } + else if (slot_id>=3000 && slot_id<=3007) { // Trade window slots + p = m_trade[slot_id]; + m_trade.erase(slot_id); + } + else { + // Is slot inside bag? + ItemInst* baginst = GetItem(Inventory::CalcSlotId(slot_id)); + if (baginst != NULL && baginst->IsType(ItemClassContainer)) { + p = baginst->PopItem(Inventory::CalcBagIdx(slot_id)); + } + } + + // Return pointer that needs to be deleted (or otherwise managed) + return p; +} + +// Locate an available inventory slot +// Returns slot_id when there's one available, else SLOT_INVALID +int16 Inventory::FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size, bool is_arrow) +{ + // Check basic inventory + for (int16 i=22; i<=29; i++) { + if (!GetItem(i)) + // Found available slot in personal inventory + return i; + } + + if (!for_bag) { + for (int16 i=22; i<=29; i++) { + const ItemInst* inst = GetItem(i); + if (inst && inst->IsType(ItemClassContainer) + && inst->GetItem()->BagSize >= min_size) + { + if(inst->GetItem()->BagType == bagTypeQuiver && inst->GetItem()->ItemType != ItemTypeArrow) + { + continue; + } + + int16 base_slot_id = Inventory::CalcSlotId(i, 0); + + uint8 slots=inst->GetItem()->BagSlots; + uint8 j; + for (j=0; jsecond; + it->first; + if(!inst || !inst->GetItem()) + continue; + + printf("Slot %d: %s (%d)\n", it->first, it->second->GetItem()->Name, (inst->GetCharges()<=0) ? 1 : inst->GetCharges()); + + // Go through bag, if bag + if (inst && inst->IsType(ItemClassContainer)) { + for (itb=inst->_begin(); itb!=inst->_end(); itb++) { + ItemInst* baginst = itb->second; + if(!baginst || !baginst->GetItem()) + continue; + printf(" Slot %d: %s (%d)\n", Inventory::CalcSlotId(it->first, itb->first), + baginst->GetItem()->Name, (baginst->GetCharges()<=0) ? 1 : baginst->GetCharges()); + } + } + } + + printf("Inventory items:\n"); + for (it=m_inv.begin(); it!=m_inv.end(); it++) { + inst = it->second; + it->first; + if(!inst || !inst->GetItem()) + continue; + + printf("Slot %d: %s (%d)\n", it->first, it->second->GetItem()->Name, (inst->GetCharges()<=0) ? 1 : inst->GetCharges()); + + // Go through bag, if bag + if (inst && inst->IsType(ItemClassContainer)) { + for (itb=inst->_begin(); itb!=inst->_end(); itb++) { + ItemInst* baginst = itb->second; + if(!baginst || !baginst->GetItem()) + continue; + printf(" Slot %d: %s (%d)\n", Inventory::CalcSlotId(it->first, itb->first), + baginst->GetItem()->Name, (baginst->GetCharges()<=0) ? 1 : baginst->GetCharges()); + + } + } + } + + printf("Bank items:\n"); + for (it=m_bank.begin(); it!=m_bank.end(); it++) { + inst = it->second; + it->first; + if(!inst || !inst->GetItem()) + continue; + + printf("Slot %d: %s (%d)\n", it->first, it->second->GetItem()->Name, (inst->GetCharges()<=0) ? 1 : inst->GetCharges()); + + // Go through bag, if bag + if (inst && inst->IsType(ItemClassContainer)) { + + for (itb=inst->_begin(); itb!=inst->_end(); itb++) { + ItemInst* baginst = itb->second; + if(!baginst || !baginst->GetItem()) + continue; + printf(" Slot %d: %s (%d)\n", Inventory::CalcSlotId(it->first, itb->first), + baginst->GetItem()->Name, (baginst->GetCharges()<=0) ? 1 : baginst->GetCharges()); + + } + } + } + + printf("Shared Bank items:\n"); + for (it=m_shbank.begin(); it!=m_shbank.end(); it++) { + inst = it->second; + it->first; + if(!inst || !inst->GetItem()) + continue; + + printf("Slot %d: %s (%d)\n", it->first, it->second->GetItem()->Name, (inst->GetCharges()<=0) ? 1 : inst->GetCharges()); + + // Go through bag, if bag + if (inst && inst->IsType(ItemClassContainer)) { + + for (itb=inst->_begin(); itb!=inst->_end(); itb++) { + ItemInst* baginst = itb->second; + if(!baginst || !baginst->GetItem()) + continue; + printf(" Slot %d: %s (%d)\n", Inventory::CalcSlotId(it->first, itb->first), + baginst->GetItem()->Name, (baginst->GetCharges()<=0) ? 1 : baginst->GetCharges()); + + } + } + } + + printf("\n"); + fflush(stdout); +} + +// Internal Method: Retrieves item within an inventory bucket +ItemInst* Inventory::_GetItem(const map& bucket, int16 slot_id) const +{ + iter_inst it = bucket.find(slot_id); + if (it != bucket.end()) { + return it->second; + } + + // Not found! + return NULL; +} + +// Internal Method: "put" item into bucket, without regard for what is currently in bucket +// Assumes item has already been allocated +int16 Inventory::_PutItem(int16 slot_id, ItemInst* inst) +{ + // If putting a NULL into slot, we need to remove slot without memory delete + if (inst == NULL) { + //Why do we not delete the poped item here???? + PopItem(slot_id); + return slot_id; + } + + int16 result = SLOT_INVALID; + + if (slot_id==SLOT_CURSOR) { // Cursor + // Replace current item on cursor, if exists + m_cursor.pop(); // no memory delete, clients of this function know what they are doing + m_cursor.push_front(inst); + result = slot_id; + } + else if ((slot_id>=0 && slot_id<=21) || (slot_id >= 400 && slot_id<=404) || (slot_id == 9999)) { // Worn slots + m_worn[slot_id] = inst; + result = slot_id; + } + else if ((slot_id>=22 && slot_id<=29)) { + m_inv[slot_id] = inst; + result = slot_id; + } + else if (slot_id>=2000 && slot_id<=2023) { // Bank slots + m_bank[slot_id] = inst; + result = slot_id; + } + else if (slot_id>=2500 && slot_id<=2501) { // Shared bank slots + m_shbank[slot_id] = inst; + result = slot_id; + } + else if (slot_id>=3000 && slot_id<=3007) { // Trade window slots + m_trade[slot_id] = inst; + result = slot_id; + } + else { + // Slot must be within a bag + ItemInst* baginst = GetItem(Inventory::CalcSlotId(slot_id)); // Get parent bag + if (baginst && baginst->IsType(ItemClassContainer)) { + baginst->_PutItem(Inventory::CalcBagIdx(slot_id), inst); + result = slot_id; + } + } + + if (result == SLOT_INVALID) { + LogFile->write(EQEMuLog::Error, "Inventory::_PutItem: Invalid slot_id specified (%i)", slot_id); + safe_delete(inst); // Slot not found, clean up + } + + return result; +} + +// Internal Method: Checks an inventory bucket for a particular item +int16 Inventory::_HasItem(map& bucket, uint32 item_id, uint8 quantity) +{ + iter_inst it; + iter_contents itb; + ItemInst* inst = NULL; + uint8 quantity_found = 0; + + // Check item: After failed checks, check bag contents (if bag) + for (it=bucket.begin(); it!=bucket.end(); it++) { + inst = it->second; + if (inst) { + if (inst->GetID() == item_id) { + quantity_found += (inst->GetCharges()<=0) ? 1 : inst->GetCharges(); + if (quantity_found >= quantity) + return it->first; + } + + for(int i = 0; i < MAX_AUGMENT_SLOTS; i++) { + if (inst->GetAugmentItemID(i) == item_id && quantity <= 1) + return SLOT_AUGMENT; // Only one augment per slot. + } + } + // Go through bag, if bag + if (inst && inst->IsType(ItemClassContainer)) { + + for (itb=inst->_begin(); itb!=inst->_end(); itb++) { + ItemInst* baginst = itb->second; + if (baginst->GetID() == item_id) { + quantity_found += (baginst->GetCharges()<=0) ? 1 : baginst->GetCharges(); + if (quantity_found >= quantity) + return Inventory::CalcSlotId(it->first, itb->first); + } + for(int i = 0; i < MAX_AUGMENT_SLOTS; i++) { + if (baginst->GetAugmentItemID(i) == item_id && quantity <= 1) + return SLOT_AUGMENT; // Only one augment per slot. + } + } + } + } + + // Not found + return SLOT_INVALID; +} + +// Internal Method: Checks an inventory queue type bucket for a particular item +int16 Inventory::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity) +{ + iter_queue it; + iter_contents itb; + uint8 quantity_found = 0; + + // Read-only iteration of queue + for (it=iqueue.begin(); it!=iqueue.end(); it++) { + ItemInst* inst = *it; + if (inst) + { + if (inst->GetID() == item_id) { + quantity_found += (inst->GetCharges()<=0) ? 1 : inst->GetCharges(); + if (quantity_found >= quantity) + return SLOT_CURSOR; + } + for(int i = 0; i < MAX_AUGMENT_SLOTS; i++) { + if (inst->GetAugmentItemID(i) == item_id && quantity <= 1) + return SLOT_AUGMENT; // Only one augment per slot. + } + } + // Go through bag, if bag + if (inst && inst->IsType(ItemClassContainer)) { + + for (itb=inst->_begin(); itb!=inst->_end(); itb++) { + ItemInst* baginst = itb->second; + if (baginst->GetID() == item_id) { + quantity_found += (baginst->GetCharges()<=0) ? 1 : baginst->GetCharges(); + if (quantity_found >= quantity) + return Inventory::CalcSlotId(SLOT_CURSOR, itb->first); + } + for(int i = 0; i < MAX_AUGMENT_SLOTS; i++) { + if (baginst->GetAugmentItemID(i) == item_id && quantity <= 1) + return SLOT_AUGMENT; // Only one augment per slot. + } + + } + } + } + + // Not found + return SLOT_INVALID; +} + +// Internal Method: Checks an inventory bucket for a particular item +int16 Inventory::_HasItemByUse(map& bucket, uint8 use, uint8 quantity) +{ + iter_inst it; + iter_contents itb; + ItemInst* inst = NULL; + uint8 quantity_found = 0; + + // Check item: After failed checks, check bag contents (if bag) + for (it=bucket.begin(); it!=bucket.end(); it++) { + inst = it->second; + if (inst && inst->IsType(ItemClassCommon) && inst->GetItem()->ItemType == use) { + quantity_found += (inst->GetCharges()<=0) ? 1 : inst->GetCharges(); + if (quantity_found >= quantity) + return it->first; + } + + // Go through bag, if bag + if (inst && inst->IsType(ItemClassContainer)) { + + for (itb=inst->_begin(); itb!=inst->_end(); itb++) { + ItemInst* baginst = itb->second; + if (baginst && baginst->IsType(ItemClassCommon) && baginst->GetItem()->ItemType == use) { + quantity_found += (baginst->GetCharges()<=0) ? 1 : baginst->GetCharges(); + if (quantity_found >= quantity) + return Inventory::CalcSlotId(it->first, itb->first); + } + } + } + } + + // Not found + return SLOT_INVALID; +} + +// Internal Method: Checks an inventory queue type bucket for a particular item +int16 Inventory::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity) +{ + iter_queue it; + iter_contents itb; + uint8 quantity_found = 0; + + // Read-only iteration of queue + for (it=iqueue.begin(); it!=iqueue.end(); it++) { + ItemInst* inst = *it; + if (inst && inst->IsType(ItemClassCommon) && inst->GetItem()->ItemType == use) { + quantity_found += (inst->GetCharges()<=0) ? 1 : inst->GetCharges(); + if (quantity_found >= quantity) + return SLOT_CURSOR; + } + + // Go through bag, if bag + if (inst && inst->IsType(ItemClassContainer)) { + + for (itb=inst->_begin(); itb!=inst->_end(); itb++) { + ItemInst* baginst = itb->second; + if (baginst && baginst->IsType(ItemClassCommon) && baginst->GetItem()->ItemType == use) { + quantity_found += (baginst->GetCharges()<=0) ? 1 : baginst->GetCharges(); + if (quantity_found >= quantity) + return Inventory::CalcSlotId(SLOT_CURSOR, itb->first); + } + } + } + } + + // Not found + return SLOT_INVALID; +} + +int16 Inventory::_HasItemByLoreGroup(map& bucket, uint32 loregroup) +{ + iter_inst it; + iter_contents itb; + ItemInst* inst = NULL; + + // Check item: After failed checks, check bag contents (if bag) + for (it=bucket.begin(); it!=bucket.end(); it++) { + inst = it->second; + if (inst) { + if (inst->GetItem()->LoreGroup == loregroup) + return it->first; + + ItemInst* Aug; + for(int i = 0; i < MAX_AUGMENT_SLOTS; i++) { + Aug = inst->GetAugment(i); + if (Aug && Aug->GetItem()->LoreGroup == loregroup) + return SLOT_AUGMENT; // Only one augment per slot. + } + } + // Go through bag, if bag + if (inst && inst->IsType(ItemClassContainer)) { + + for (itb=inst->_begin(); itb!=inst->_end(); itb++) { + ItemInst* baginst = itb->second; + if (baginst && baginst->IsType(ItemClassCommon)&& baginst->GetItem()->LoreGroup == loregroup) + return Inventory::CalcSlotId(it->first, itb->first); + + ItemInst* Aug2; + for(int i = 0; i < MAX_AUGMENT_SLOTS; i++) { + Aug2 = baginst->GetAugment(i); + if (Aug2 && Aug2->GetItem()->LoreGroup == loregroup) + return SLOT_AUGMENT; // Only one augment per slot. + } + } + } + } + + // Not found + return SLOT_INVALID; +} + +// Internal Method: Checks an inventory queue type bucket for a particular item +int16 Inventory::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup) +{ + iter_queue it; + iter_contents itb; + + // Read-only iteration of queue + for (it=iqueue.begin(); it!=iqueue.end(); it++) { + ItemInst* inst = *it; + if (inst) + { + if (inst->GetItem()->LoreGroup == loregroup) + return SLOT_CURSOR; + + ItemInst* Aug; + for(int i = 0; i < MAX_AUGMENT_SLOTS; i++) { + Aug = inst->GetAugment(i); + if (Aug && Aug->GetItem()->LoreGroup == loregroup) + return SLOT_AUGMENT; // Only one augment per slot. + } + } + // Go through bag, if bag + if (inst && inst->IsType(ItemClassContainer)) { + + for (itb=inst->_begin(); itb!=inst->_end(); itb++) { + ItemInst* baginst = itb->second; + if (baginst && baginst->IsType(ItemClassCommon)&& baginst->GetItem()->LoreGroup == loregroup) + return Inventory::CalcSlotId(SLOT_CURSOR, itb->first); + + + ItemInst* Aug2; + for(int i = 0; i < MAX_AUGMENT_SLOTS; i++) { + Aug2 = baginst->GetAugment(i); + if (Aug2 && Aug2->GetItem()->LoreGroup == loregroup) + return SLOT_AUGMENT; // Only one augment per slot. + } + + } + } + } + + // Not found + return SLOT_INVALID; +} + +bool ItemInst::IsSlotAllowed(int16 slot_id) const { + // 'SupportsContainers' and 'slot_id > 21' previously saw the reassigned PowerSource slot (9999 to 22) as valid -U + if(!m_item) { return false; } + else if(Inventory::SupportsContainers(slot_id)) { return true; } + else if(m_item->Slots & (1 << slot_id)) { return true; } + else if(slot_id == 9999 && (m_item->Slots & (1 << 22))) { return true; } + else if(slot_id != 9999 && slot_id > 21) { return true; } + else { return false; } +} + +uint8 ItemInst::FirstOpenSlot() const +{ + uint8 slots=m_item->BagSlots,i; + for(i=0;iItemClass != ItemClassContainer) { return item_count; } + + for(int idx = 0; idx < m_item->BagSlots; idx++) { if(GetItem(idx)) { item_count++; } } + + return item_count; +} + +bool ItemInst::IsNoneEmptyContainer() +{ + if(m_item->ItemClass != ItemClassContainer) + return false; + + for(int i = 0; i < m_item->BagSlots; ++i) + if(GetItem(i)) + return true; + + return false; +} + +bool ItemInst::IsAugmented() +{ + for(int i = 0; i < MAX_AUGMENT_SLOTS; ++i) + if (GetAugmentItemID(i)) + return true; + + return false; +} + +// Calculate slot_id for an item within a bag +int16 Inventory::CalcSlotId(int16 bagslot_id, uint8 bagidx) +{ + if (!Inventory::SupportsContainers(bagslot_id)) { + return SLOT_INVALID; + } + + int16 slot_id = SLOT_INVALID; + + if (bagslot_id==SLOT_CURSOR || bagslot_id==8000) // Cursor + slot_id = IDX_CURSOR_BAG + bagidx; + else if (bagslot_id>=22 && bagslot_id<=29) // Inventory slots + slot_id = IDX_INV_BAG + (bagslot_id-22)*MAX_ITEMS_PER_BAG + bagidx; + else if (bagslot_id>=2000 && bagslot_id<=2023) // Bank slots + slot_id = IDX_BANK_BAG + (bagslot_id-2000)*MAX_ITEMS_PER_BAG + bagidx; + else if (bagslot_id>=2500 && bagslot_id<=2501) // Shared bank slots + slot_id = IDX_SHBANK_BAG + (bagslot_id-2500)*MAX_ITEMS_PER_BAG + bagidx; + else if (bagslot_id>=3000 && bagslot_id<=3007) // Trade window slots + slot_id = IDX_TRADE_BAG + (bagslot_id-3000)*MAX_ITEMS_PER_BAG + bagidx; + + return slot_id; +} + +// Opposite of above: Get parent bag slot_id from a slot inside of bag +int16 Inventory::CalcSlotId(int16 slot_id) +{ + int16 parent_slot_id = SLOT_INVALID; + + if (slot_id>=251 && slot_id<=330) + parent_slot_id = IDX_INV + (slot_id-251) / MAX_ITEMS_PER_BAG; + else if (slot_id>=331 && slot_id<=340) + parent_slot_id = SLOT_CURSOR; + else if (slot_id>=2000 && slot_id<=2023) + parent_slot_id = IDX_BANK + (slot_id-2000) / MAX_ITEMS_PER_BAG; + else if (slot_id>=2031 && slot_id<=2270) + parent_slot_id = IDX_BANK + (slot_id-2031) / MAX_ITEMS_PER_BAG; + else if (slot_id>=2531 && slot_id<=2550) + parent_slot_id = IDX_SHBANK + (slot_id-2531) / MAX_ITEMS_PER_BAG; + else if (slot_id>=3100 && slot_id<=3179) + parent_slot_id = IDX_TRADE + (slot_id-3100) / MAX_ITEMS_PER_BAG; + + return parent_slot_id; +} + +uint8 Inventory::CalcBagIdx(int16 slot_id) +{ + uint8 index = 0; + + if (slot_id>=251 && slot_id<=330) + index = (slot_id-251) % MAX_ITEMS_PER_BAG; + else if (slot_id>=331 && slot_id<=340) + index = (slot_id-331) % MAX_ITEMS_PER_BAG; + else if (slot_id>=2000 && slot_id<=2023) + index = (slot_id-2000) % MAX_ITEMS_PER_BAG; + else if (slot_id>=2031 && slot_id<=2270) + index = (slot_id-2031) % MAX_ITEMS_PER_BAG; + else if (slot_id>=2531 && slot_id<=2550) + index = (slot_id-2531) % MAX_ITEMS_PER_BAG; + else if (slot_id>=3100 && slot_id<=3179) + index = (slot_id-3100) % MAX_ITEMS_PER_BAG; + else if (slot_id>=4000 && slot_id<=4009) + index = (slot_id-4000) % MAX_ITEMS_PER_BAG; + + return index; +} + +int16 Inventory::CalcSlotFromMaterial(uint8 material) +{ + switch(material) + { + case MATERIAL_HEAD: + return SLOT_HEAD; + case MATERIAL_CHEST: + return SLOT_CHEST; + case MATERIAL_ARMS: + return SLOT_ARMS; + case MATERIAL_BRACER: + return SLOT_BRACER01; // there's 2 bracers, only one bracer material + case MATERIAL_HANDS: + return SLOT_HANDS; + case MATERIAL_LEGS: + return SLOT_LEGS; + case MATERIAL_FEET: + return SLOT_FEET; + case MATERIAL_PRIMARY: + return SLOT_PRIMARY; + case MATERIAL_SECONDARY: + return SLOT_SECONDARY; + default: + return -1; + } +} + +uint8 Inventory::CalcMaterialFromSlot(int16 equipslot) +{ + switch(equipslot) + { + case SLOT_HEAD: + return MATERIAL_HEAD; + case SLOT_CHEST: + return MATERIAL_CHEST; + case SLOT_ARMS: + return MATERIAL_ARMS; + case SLOT_BRACER01: + case SLOT_BRACER02: + return MATERIAL_BRACER; + case SLOT_HANDS: + return MATERIAL_HANDS; + case SLOT_LEGS: + return MATERIAL_LEGS; + case SLOT_FEET: + return MATERIAL_FEET; + case SLOT_PRIMARY: + return MATERIAL_PRIMARY; + case SLOT_SECONDARY: + return MATERIAL_SECONDARY; + default: + return 0xFF; + } +} + + +// Test whether a given slot can support a container item +bool Inventory::SupportsContainers(int16 slot_id) +{ + if ((slot_id>=22 && slot_id<=30) || // Personal inventory slots + (slot_id>=2000 && slot_id<=2023) || // Bank slots + (slot_id>=2500 && slot_id<=2501) || // Shared bank slots + (slot_id==SLOT_CURSOR) || // Cursor + (slot_id>=3000 && slot_id<=3007)) // Trade window + return true; + return false; +} + + +bool Inventory::CanItemFitInContainer(const Item_Struct *ItemToTry, const Item_Struct *Container) { + + if(!ItemToTry || !Container) return false; + + if(ItemToTry->Size > Container->BagSize) return false; + + if((Container->BagType == bagTypeQuiver) && (ItemToTry->ItemType != ItemTypeArrow)) return false; + + if((Container->BagType == bagTypeBandolier) && (ItemToTry->ItemType != ItemTypeThrowingv2)) return false; + + return true; +} + +// Methods for EvoItemInst, the extended ItemInst for evolving/scaling items +// Copy constructors +EvoItemInst::EvoItemInst(const EvoItemInst ©) { + m_use_type=copy.m_use_type; + m_item=copy.m_item; + m_charges=copy.m_charges; + m_price=copy.m_price; + m_color=copy.m_color; + m_merchantslot=copy.m_merchantslot; + m_currentslot=copy.m_currentslot; + m_instnodrop=copy.m_instnodrop; + m_merchantcount=copy.m_merchantcount; + // Copy container contents + iter_contents it; + for (it=copy.m_contents.begin(); it!=copy.m_contents.end(); it++) { + ItemInst* inst_old = it->second; + ItemInst* inst_new = NULL; + + if (inst_old) { + inst_new = inst_old->Clone(); + } + + if (inst_new != NULL) { + m_contents[it->first] = inst_new; + } + } + std::map::const_iterator iter; + for (iter = copy.m_custom_data.begin(); iter != copy.m_custom_data.end(); iter++) { + m_custom_data[iter->first] = iter->second; + } + m_SerialNumber = copy.m_SerialNumber; + m_exp = copy.m_exp; + m_evolveLvl = copy.m_evolveLvl; + m_activated = copy.m_activated; + m_evolveInfo = NULL; + if (copy.m_scaledItem) + m_scaledItem = new Item_Struct(*copy.m_scaledItem); + else + m_scaledItem = NULL; +} + +EvoItemInst::EvoItemInst(const ItemInst &basecopy) { + EvoItemInst* copy = (EvoItemInst*)&basecopy; + + m_use_type=copy->m_use_type; + m_item=copy->m_item; + m_charges=copy->m_charges; + m_price=copy->m_price; + m_color=copy->m_color; + m_merchantslot=copy->m_merchantslot; + m_currentslot=copy->m_currentslot; + m_instnodrop=copy->m_instnodrop; + m_merchantcount=copy->m_merchantcount; + // Copy container contents + iter_contents it; + for (it=copy->m_contents.begin(); it!=copy->m_contents.end(); it++) { + ItemInst* inst_old = it->second; + ItemInst* inst_new = NULL; + + if (inst_old) { + inst_new = inst_old->Clone(); + } + + if (inst_new != NULL) { + m_contents[it->first] = inst_new; + } + } + + std::map::const_iterator iter; + for (iter = copy->m_custom_data.begin(); iter != copy->m_custom_data.end(); iter++) { + m_custom_data[iter->first] = iter->second; + } + m_SerialNumber = copy->m_SerialNumber; + m_exp = 0; + m_evolveLvl = 0; + m_activated = false; + m_evolveInfo = NULL; + m_scaledItem = NULL; +} + +EvoItemInst::EvoItemInst(const Item_Struct* item, int16 charges) { + m_use_type = ItemUseNormal; + m_item = item; + m_charges = charges; + m_price = 0; + m_instnodrop = false; + m_merchantslot = 0; + if(m_item &&m_item->ItemClass == ItemClassCommon) + m_color = m_item->Color; + else + m_color = 0; + m_merchantcount = 1; + m_SerialNumber = GetNextItemInstSerialNumber(); + m_exp = 0; + m_evolveLvl = 0; + m_activated = false; + m_evolveInfo = NULL; + m_scaledItem = NULL; +} + +EvoItemInst::~EvoItemInst() { + safe_delete(m_scaledItem); +} + +EvoItemInst* EvoItemInst::Clone() const { + return new EvoItemInst(*this); +} + +const Item_Struct* EvoItemInst::GetItem() const { + if(!m_scaledItem) + return m_item; + else + return m_scaledItem; +} + +const Item_Struct* EvoItemInst::GetUnscaledItem() const { + return m_item; +} + +void EvoItemInst::Initialize(SharedDatabase *db) { + // if there's no actual item, don't do anything + if(!m_item) return; + + // initialize scaling items + if (m_item->CharmFileID != 0) { + m_evolveLvl = -1; + this->ScaleItem(); + } + + // initialize evolving items + else if ((db) && m_item->LoreGroup >= 1000 && m_item->LoreGroup != -1) { + // not complete yet + } +} + +void EvoItemInst::ScaleItem() { + // free memory from any previously scaled item data + safe_delete(m_scaledItem); + + m_scaledItem = new Item_Struct(*m_item); + float Mult = (float)(GetExp())/10000; // scaling is determined by exp, with 10,000 being full stats + + m_scaledItem->AStr = (int8)((float)m_item->AStr*Mult); + m_scaledItem->ASta = (int8)((float)m_item->ASta*Mult); + m_scaledItem->AAgi = (int8)((float)m_item->AAgi*Mult); + m_scaledItem->ADex = (int8)((float)m_item->ADex*Mult); + m_scaledItem->AInt = (int8)((float)m_item->AInt*Mult); + m_scaledItem->AWis = (int8)((float)m_item->AWis*Mult); + m_scaledItem->ACha = (int8)((float)m_item->ACha*Mult); + + m_scaledItem->MR = (int8)((float)m_item->MR*Mult); + m_scaledItem->PR = (int8)((float)m_item->PR*Mult); + m_scaledItem->DR = (int8)((float)m_item->DR*Mult); + m_scaledItem->CR = (int8)((float)m_item->CR*Mult); + m_scaledItem->FR = (int8)((float)m_item->FR*Mult); + + m_scaledItem->HP = (int32)((float)m_item->HP*Mult); + m_scaledItem->Mana = (int32)((float)m_item->Mana*Mult); + m_scaledItem->AC = (int32)((float)m_item->AC*Mult); + + m_scaledItem->SkillModValue = (int32)((float)m_item->SkillModValue*Mult); + m_scaledItem->BaneDmgAmt = (int8)((float)m_item->BaneDmgAmt*Mult); + m_scaledItem->BardValue = (int32)((float)m_item->BardValue*Mult); + m_scaledItem->ElemDmgAmt = (uint8)((float)m_item->ElemDmgAmt*Mult); + m_scaledItem->Damage = (uint32)((float)m_item->Damage*Mult); + + m_scaledItem->CombatEffects = (int8)((float)m_item->CombatEffects*Mult); + m_scaledItem->Shielding = (int8)((float)m_item->Shielding*Mult); + m_scaledItem->StunResist = (int8)((float)m_item->StunResist*Mult); + m_scaledItem->StrikeThrough = (int8)((float)m_item->StrikeThrough*Mult); + m_scaledItem->ExtraDmgAmt = (uint32)((float)m_item->ExtraDmgAmt*Mult); + m_scaledItem->SpellShield = (int8)((float)m_item->SpellShield*Mult); + m_scaledItem->Avoidance = (int8)((float)m_item->Avoidance*Mult); + m_scaledItem->Accuracy = (int8)((float)m_item->Accuracy*Mult); + + m_scaledItem->FactionAmt1 = (int32)((float)m_item->FactionAmt1*Mult); + m_scaledItem->FactionAmt2 = (int32)((float)m_item->FactionAmt2*Mult); + m_scaledItem->FactionAmt3 = (int32)((float)m_item->FactionAmt3*Mult); + m_scaledItem->FactionAmt4 = (int32)((float)m_item->FactionAmt4*Mult); + + m_scaledItem->Endur = (uint32)((float)m_item->Endur*Mult); + m_scaledItem->DotShielding = (uint32)((float)m_item->DotShielding*Mult); + m_scaledItem->Attack = (uint32)((float)m_item->Attack*Mult); + m_scaledItem->Regen = (uint32)((float)m_item->Regen*Mult); + m_scaledItem->ManaRegen = (uint32)((float)m_item->ManaRegen*Mult); + m_scaledItem->EnduranceRegen = (uint32)((float)m_item->EnduranceRegen*Mult); + m_scaledItem->Haste = (uint32)((float)m_item->Haste*Mult); + m_scaledItem->DamageShield = (uint32)((float)m_item->DamageShield*Mult); + + + m_scaledItem->CharmFileID = 0; // this stops the client from trying to scale the item itself. +} + +bool EvoItemInst::EvolveOnAllKills() const { + return (m_evolveInfo && m_evolveInfo->AllKills); +} + +int8 EvoItemInst::GetMaxEvolveLvl() const { + if(m_evolveInfo) + return m_evolveInfo->MaxLvl; + else + return 0; +} + +uint32 EvoItemInst::GetKillsNeeded(uint8 currentlevel) { + uint32 kills = -1; // default to -1 (max uint32 value) because this value is usually divided by, so we don't want to ever return zero. + if (m_evolveInfo) + if (currentlevel != m_evolveInfo->MaxLvl) + kills = m_evolveInfo->LvlKills[currentlevel-1]; + + if (kills == 0) + kills = -1; + + return kills; +} + +EvolveInfo::EvolveInfo() { + // nothing here yet +} + +EvolveInfo::EvolveInfo(uint32 first, uint8 max, bool allkills, uint32 L2, uint32 L3, uint32 L4, uint32 L5, uint32 L6, uint32 L7, uint32 L8, uint32 L9, uint32 L10) { + FirstItem = first; + MaxLvl = max; + AllKills = allkills; + LvlKills[0] = L2; + LvlKills[1] = L3; + LvlKills[2] = L4; + LvlKills[3] = L5; + LvlKills[4] = L6; + LvlKills[5] = L7; + LvlKills[6] = L8; + LvlKills[7] = L9; + LvlKills[8] = L10; +} + +bool Item_Struct::IsEquipable(uint16 Race, uint16 Class_) const +{ + bool IsRace = false; + bool IsClass = false; + + uint32 Classes_ = Classes; + + uint32 Races_ = Races; + + uint32 Race_ = GetArrayRace(Race); + + for (int CurrentClass = 1; CurrentClass <= PLAYER_CLASS_COUNT; ++CurrentClass) + { + if (Classes_ % 2 == 1) + { + if (CurrentClass == Class_) + { + IsClass = true; + break; + } + } + Classes_ >>= 1; + } + + Race_ = (Race_ == 18 ? 16 : Race_); + + for (unsigned int CurrentRace = 1; CurrentRace <= PLAYER_RACE_COUNT; ++CurrentRace) + { + if (Races_ % 2 == 1) + { + if (CurrentRace == Race_) + { + IsRace = true; + break; + } + } + Races_ >>= 1; + } + return (IsRace && IsClass); +} diff --git a/common/Item.h b/common/Item.h new file mode 100644 index 000000000..2f0da8312 --- /dev/null +++ b/common/Item.h @@ -0,0 +1,456 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 +*/ + +// @merth notes: +// These classes could be optimized with database reads/writes by storing +// a status flag indicating how object needs to interact with database + +#ifndef __ITEM_H +#define __ITEM_H + +class ItemInst; // Item belonging to a client (contains info on item, dye, augments, charges, etc) +class ItemInstQueue; // Queue of ItemInst objects (i.e., cursor) +class Inventory; // Character inventory +class ItemParse; // Parses item packets +class EvoItemInst; // Extended item inst, for use with scaling/evolving items +class EvolveInfo; // Stores information about an evolving item family + +#include +#include +#include +#include +using namespace std; +#include "../common/eq_packet_structs.h" +#include "../common/eq_constants.h" +#include "../common/item_struct.h" + +// Helper typedefs +typedef list::const_iterator iter_queue; +typedef map::const_iterator iter_inst; +typedef map::const_iterator iter_contents; + +namespace ItemField { + enum { + source=0, +#define F(x) x, +#include "item_fieldlist.h" +#undef F + updated + }; +}; + +// Indexing positions to the beginning slot_id's for a bucket of slots +#define IDX_EQUIP 0 +#define IDX_CURSOR_BAG 331 +#define IDX_INV 22 +#define IDX_INV_BAG 251 +#define IDX_TRIBUTE 400 +#define IDX_BANK 2000 +#define IDX_BANK_BAG 2031 +#define IDX_SHBANK 2500 +#define IDX_SHBANK_BAG 2531 +#define IDX_TRADE 3000 +#define IDX_TRADE_BAG 3031 +#define IDX_TRADESKILL 4000 +#define MAX_ITEMS_PER_BAG 10 + +// Specifies usage type for item inside ItemInst +enum ItemUseType +{ + ItemUseNormal, + ItemUseWorldContainer +}; + +typedef enum { + byFlagIgnore, //do not consider this flag + byFlagSet, //apply action if the flag is set + byFlagNotSet //apply action if the flag is NOT set +} byFlagSetting; + + +//FatherNitwit: location bits for searching specific +//places with HasItem() and HasItemByUse() +enum { + invWhereWorn = 0x01, + invWherePersonal = 0x02, //in the character's inventory + invWhereBank = 0x04, + invWhereSharedBank = 0x08, + invWhereTrading = 0x10, + invWhereCursor = 0x20 +}; + + +// ######################################## +// Class: Queue +// Queue that allows a read-only iterator +class ItemInstQueue +{ +public: + ~ItemInstQueue(); + ///////////////////////// + // Public Methods + ///////////////////////// + + inline iter_queue begin() { return m_list.begin(); } + inline iter_queue end() { return m_list.end(); } + + void push(ItemInst* inst); + void push_front(ItemInst* inst); + ItemInst* pop(); + ItemInst* peek_front() const; + inline int size() { return static_cast(m_list.size()); } + +protected: + ///////////////////////// + // Protected Members + ///////////////////////// + + list m_list; + +}; + +// ######################################## +// Class: Inventory +// Character inventory +class Inventory +{ + friend class ItemInst; +public: + /////////////////////////////// + // Public Methods + /////////////////////////////// + + virtual ~Inventory(); + + // Retrieve a writeable item at specified slot + ItemInst* GetItem(int16 slot_id) const; + ItemInst* GetItem(int16 slot_id, uint8 bagidx) const; + + 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); } + + // Retrieve a read-only item from inventory + inline const ItemInst* operator[](int16 slot_id) const { return GetItem(slot_id); } + + // Add item to inventory + int16 PutItem(int16 slot_id, const ItemInst& inst); + + // Add item to cursor queue + int16 PushCursor(const ItemInst& inst); + + // Swap items in inventory + bool SwapItem(int16 slot_a, int16 slot_b); + + // Remove item from inventory + bool DeleteItem(int16 slot_id, uint8 quantity=0); + + // Checks All items in a bag for No Drop + bool CheckNoDrop(int16 slot_id); + + // Remove item from inventory (and take control of memory) + ItemInst* PopItem(int16 slot_id); + + // Check whether item exists in inventory + // where argument specifies OR'd list of invWhere constants to look + int16 HasItem(uint32 item_id, uint8 quantity=0, uint8 where=0xFF); + + // Check whether there is space for the specified number of the specified item. + bool HasSpaceForItem(const Item_Struct *ItemToTry, int16 Quantity); + + // Check whether item exists in inventory + // where argument specifies OR'd list of invWhere constants to look + int16 HasItemByUse(uint8 use, uint8 quantity=0, uint8 where=0xFF); + + // Check whether item exists in inventory + // where argument specifies OR'd list of invWhere constants to look + int16 HasItemByLoreGroup(uint32 loregroup, uint8 where=0xFF); + + // Locate an available inventory slot + int16 FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size = 0, bool is_arrow = false); + + // Calculate slot_id for an item within a bag + static int16 CalcSlotId(int16 slot_id); // Calc parent bag's slot_id + static int16 CalcSlotId(int16 bagslot_id, uint8 bagidx); // Calc slot_id for item inside bag + static uint8 CalcBagIdx(int16 slot_id); // Calc bagidx for slot_id + static int16 CalcSlotFromMaterial(uint8 material); + static uint8 CalcMaterialFromSlot(int16 equipslot); + + static bool CanItemFitInContainer(const Item_Struct *ItemToTry, const Item_Struct *Container); + + // Test whether a given slot can support a container item + static bool SupportsContainers(int16 slot_id); + + void dumpInventory(); + + void SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, std::string value); + void SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, int value); + void SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, float value); + void SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, bool value); + std::string GetCustomItemData(int16 slot_id, std::string identifier); +protected: + /////////////////////////////// + // Protected Methods + /////////////////////////////// + + // Retrieves item within an inventory bucket + ItemInst* _GetItem(const map& bucket, int16 slot_id) const; + + // Private "put" item into bucket, without regard for what is currently in bucket + int16 _PutItem(int16 slot_id, ItemInst* inst); + + // Checks an inventory bucket for a particular item + int16 _HasItem(map& bucket, uint32 item_id, uint8 quantity); + int16 _HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity); + int16 _HasItemByUse(map& bucket, uint8 use, uint8 quantity); + int16 _HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity); + int16 _HasItemByLoreGroup(map& bucket, uint32 loregroup); + int16 _HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup); + + + // Player inventory + map m_worn; // Items worn by character + map m_inv; // Items in character personal inventory + map m_bank; // Items in character bank + map m_shbank; // Items in character shared bank + map m_trade; // Items in a trade session + ItemInstQueue m_cursor; // Items on cursor: FIFO +}; + +class SharedDatabase; + +// ######################################## +// Class: ItemInst +// Base class for an instance of an item +// An item instance encapsulates item data + data specific +// to an item instance (includes dye, augments, charges, etc) +class ItemInst +{ +public: + ///////////////////////// + // Methods + ///////////////////////// + + // Constructors/Destructor + ItemInst(const Item_Struct* item = NULL, int16 charges = 0); + + ItemInst(SharedDatabase *db, uint32 item_id, int16 charges = 0); + + ItemInst(ItemUseType use_type) { + m_use_type = use_type; + m_item = NULL; + m_charges = 0; + m_price = 0; + m_instnodrop = false; + m_merchantslot = 0; + m_color = 0; + } + + ItemInst(const ItemInst& copy); + + virtual ~ItemInst(); + + // Query item type + virtual bool IsType(ItemClass item_class) const; + + // Can item be stacked? + virtual bool IsStackable() const; + + // Can item be equipped by/at? + virtual bool IsEquipable(uint16 race, uint16 class_) const; + virtual bool IsEquipable(int16 slot_id) const; + + // + // Augements + // + 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; + inline int32 GetAugmentType() const { return m_item->AugType; } + + inline bool IsExpendable() const { return ((m_item->Click.Type == ET_Expendable ) || (m_item->ItemType == ItemTypePotion)); } + + // + // Contents + // + ItemInst* GetItem(uint8 slot) const; + virtual uint32 GetItemID(uint8 slot) const; + inline const ItemInst* operator[](uint8 slot) const { return GetItem(slot); } + void PutItem(uint8 slot, const ItemInst& inst); + void PutItem(SharedDatabase *db, uint8 slot, uint32 item_id); + void DeleteItem(uint8 slot); + ItemInst* PopItem(uint8 index); + void Clear(); + void ClearByFlags(byFlagSetting is_nodrop, byFlagSetting is_norent); + uint8 FirstOpenSlot() const; + uint8 GetTotalItemCount() const; + bool IsNoneEmptyContainer(); + map* GetContents() { return &m_contents; } + + // + // Augments + // + ItemInst* GetAugment(uint8 slot) const; + virtual uint32 GetAugmentItemID(uint8 slot) const; + void PutAugment(uint8 slot, const ItemInst& inst); + void PutAugment(SharedDatabase *db, uint8 slot, uint32 item_id); + void DeleteAugment(uint8 slot); + ItemInst* RemoveAugment(uint8 index); + bool IsAugmented(); + + // Has attack/delay? + virtual bool IsWeapon() const; + virtual bool IsAmmo() const; + + // Accessors + const uint32 GetID() const { return m_item->ID; } + const uint32 GetItemScriptID() const { return m_item->ScriptFileID; } + virtual const Item_Struct* GetItem() const { return m_item; } + void SetItem(const Item_Struct* item) { m_item = item; } + + int16 GetCharges() const { return m_charges; } + void SetCharges(int16 charges) { m_charges = charges; } + + uint32 GetPrice() const { return m_price; } + void SetPrice(uint32 price) { m_price = price; } + + void SetColor(uint32 color) { m_color = color; } + uint32 GetColor() const { return m_color; } + + uint32 GetMerchantSlot() const { return m_merchantslot; } + void SetMerchantSlot(uint32 slot) { m_merchantslot = slot; } + + int32 GetMerchantCount() const { return m_merchantcount; } + void SetMerchantCount(int32 count) { m_merchantcount = count; } + + int16 GetCurrentSlot() const { return m_currentslot; } + void SetCurrentSlot(int16 curr_slot) { m_currentslot = curr_slot; } + + + + + // Is this item already attuned? + bool IsInstNoDrop() const { return m_instnodrop; } + void SetInstNoDrop(bool flag) { m_instnodrop=flag; } + + std::string GetCustomDataString() const; + void SetCustomData(std::string identifier, std::string value); + void SetCustomData(std::string identifier, int value); + void SetCustomData(std::string identifier, float value); + void SetCustomData(std::string identifier, bool value); + std::string GetCustomData(std::string identifier); + void DeleteCustomData(std::string identifier); + + // Allows treatment of this object as though it were a pointer to m_item + operator bool() const { return (m_item != NULL); } + + // Compare inner Item_Struct of two ItemInst objects + bool operator==(const ItemInst& right) const { return (this->m_item == right.m_item); } + bool operator!=(const ItemInst& right) const { return (this->m_item != right.m_item); } + + // Clone current item + virtual ItemInst* Clone() const; + + bool IsSlotAllowed(int16 slot_id) const; + + virtual bool IsScaling() const { return false; } + virtual bool IsEvolving() const { return false; } + + string Serialize(int16 slot_id) const { InternalSerializedItem_Struct s; s.slot_id=slot_id; s.inst=(const void *)this; string ser; ser.assign((char *)&s,sizeof(InternalSerializedItem_Struct)); return ser; } + inline int32 GetSerialNumber() const { return m_SerialNumber; } + inline void SetSerialNumber(int32 id) { m_SerialNumber = id; } + +protected: + ////////////////////////// + // Protected Members + ////////////////////////// + iter_contents _begin() { return m_contents.begin(); } + iter_contents _end() { return m_contents.end(); } + + friend class Inventory; + + + void _PutItem(uint8 index, ItemInst* inst) { m_contents[index] = inst; } + + ItemUseType m_use_type; // Usage type for item + const Item_Struct* m_item; // Ptr to item data + int16 m_charges; // # of charges for chargeable items + uint32 m_price; // Bazaar /trader price + uint32 m_color; + uint32 m_merchantslot; + int16 m_currentslot; + bool m_instnodrop; + int32 m_merchantcount; //number avaliable on the merchant, -1=unlimited + int32 m_SerialNumber; // Unique identifier for this instance of an item. Needed for Bazaar. + // + // Items inside of this item (augs or contents); + map m_contents; // Zero-based index: min=0, max=9 + map m_custom_data; +}; + +class EvoItemInst: public ItemInst { +public: + // constructor and destructor + EvoItemInst(const EvoItemInst& copy); + EvoItemInst(const ItemInst& copy); + EvoItemInst(const Item_Struct* item = NULL, int16 charges = 0); + ~EvoItemInst(); + + // accessors... a lot of these are for evolving items (not complete yet) + bool IsScaling() const { return (m_evolveLvl == -1); } + bool IsEvolving() const { return (m_evolveLvl >= 1); } + uint32 GetExp() const { return m_exp; } + void SetExp(uint32 exp) { m_exp = exp; } + void AddExp(uint32 exp) { m_exp += exp; } + bool IsActivated() { return m_activated; } + void SetActivated(bool activated) { m_activated = activated; } + int8 GetEvolveLvl() const { return m_evolveLvl; } + + EvoItemInst* Clone() const; + const Item_Struct* GetItem() const; + const Item_Struct* GetUnscaledItem() const; + void Initialize(SharedDatabase *db = NULL); + void ScaleItem(); + bool EvolveOnAllKills() const; + int8 GetMaxEvolveLvl() const; + uint32 GetKillsNeeded(uint8 currentlevel); + + +private: + uint32 m_exp; + int8 m_evolveLvl; + bool m_activated; + Item_Struct* m_scaledItem; + const EvolveInfo* m_evolveInfo; +}; + +class EvolveInfo { +public: + friend class EvoItemInst; + //temporary + uint16 LvlKills[9]; + uint32 FirstItem; + uint8 MaxLvl; + bool AllKills; + + EvolveInfo(); + EvolveInfo(uint32 first, uint8 max, bool allkills, uint32 L2, uint32 L3, uint32 L4, uint32 L5, uint32 L6, uint32 L7, uint32 L8, uint32 L9, uint32 L10); + ~EvolveInfo(); +}; + +#endif // #define __ITEM_H diff --git a/common/MaxSkill.cpp b/common/MaxSkill.cpp new file mode 100644 index 000000000..5558827c2 --- /dev/null +++ b/common/MaxSkill.cpp @@ -0,0 +1,1901 @@ +#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/MiscFunctions.cpp b/common/MiscFunctions.cpp new file mode 100644 index 000000000..eaafe8ad8 --- /dev/null +++ b/common/MiscFunctions.cpp @@ -0,0 +1,650 @@ +/* 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 "../common/debug.h" +#include "MiscFunctions.h" +#include +#include +#include +#ifndef WIN32 +#include +#include +#endif +#include +#include +#ifdef _WINDOWS + #include +#endif +#include "../common/timer.h" +#include "../common/seperator.h" + +using namespace std; + +#ifdef _WINDOWS + #include + + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#else + #include + #include + #include + #include + #include +#ifdef FREEBSD //Timothy Whitman - January 7, 2003 + #include + #include + #endif + #include + #include + #include + #include +#endif + +#ifndef va_copy + #define va_copy(d,s) ((d) = (s)) +#endif + +static bool WELLRNG_init = false; +static int state_i = 0; +static unsigned int STATE[R]; +static unsigned int z0, z1, z2; +unsigned int (*WELLRNG19937)(void); +static unsigned int case_1 (void); +static unsigned int case_2 (void); +static unsigned int case_3 (void); +static unsigned int case_4 (void); +static unsigned int case_5 (void); +static unsigned int case_6 (void); +uint32 rnd_hash(time_t t, clock_t c); +void oneseed(const uint32 seed); + +void CoutTimestamp(bool ms) { + time_t rawtime; + struct tm* gmt_t; + time(&rawtime); + gmt_t = gmtime(&rawtime); + + struct timeval read_time; + gettimeofday(&read_time,0); + + cout << (gmt_t->tm_year + 1900) << "/" << setw(2) << setfill('0') << (gmt_t->tm_mon + 1) << "/" << setw(2) << setfill('0') << gmt_t->tm_mday << " " << setw(2) << setfill('0') << gmt_t->tm_hour << ":" << setw(2) << setfill('0') << gmt_t->tm_min << ":" << setw(2) << setfill('0') << gmt_t->tm_sec; + if (ms) + cout << "." << setw(3) << setfill('0') << (read_time.tv_usec / 1000); + cout << " GMT"; +} + +// normal strncpy doesnt put a null term on copied strings, this one does +// ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp +char* strn0cpy(char* dest, const char* source, uint32 size) { + if (!dest) + return 0; + if (size == 0 || source == 0) { + dest[0] = 0; + return dest; + } + strncpy(dest, source, size); + dest[size - 1] = 0; + return dest; +} + +// String N w/null Copy Truncated? +// return value =true if entire string(source) fit, false if it was truncated +bool strn0cpyt(char* dest, const char* source, uint32 size) { + if (!dest) + return 0; + if (size == 0 || source == 0) { + dest[0] = 0; + return false; + } + strncpy(dest, source, size); + dest[size - 1] = 0; + return (bool) (source[strlen(dest)] == 0); +} + +const char *MakeUpperString(const char *source) { + static char str[128]; + if (!source) + return NULL; + MakeUpperString(source, str); + return str; +} + +void MakeUpperString(const char *source, char *target) { + if (!source || !target) { + *target=0; + return; + } + while (*source) + { + *target = toupper(*source); + target++;source++; + } + *target = 0; +} + +const char *MakeLowerString(const char *source) { + static char str[128]; + if (!source) + return NULL; + MakeLowerString(source, str); + return str; +} + +void MakeLowerString(const char *source, char *target) { + if (!source || !target) { + *target=0; + return; + } + while (*source) + { + *target = tolower(*source); + target++;source++; + } + *target = 0; +} + +int MakeAnyLenString(char** ret, const char* format, ...) { + int buf_len = 128; + int chars = -1; + va_list argptr, tmpargptr; + va_start(argptr, format); + while (chars == -1 || chars >= buf_len) { + safe_delete_array(*ret); + if (chars == -1) + buf_len *= 2; + else + buf_len = chars + 1; + *ret = new char[buf_len]; + va_copy(tmpargptr, argptr); + chars = vsnprintf(*ret, buf_len, format, tmpargptr); + } + va_end(argptr); + return chars; +} + +uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...) { + if (*bufsize == 0) + *bufsize = 256; + if (*ret == 0) + *strlen = 0; + int chars = -1; + char* oldret = 0; + va_list argptr, tmpargptr; + va_start(argptr, format); + while (chars == -1 || chars >= (int32)(*bufsize-*strlen)) { + if (chars == -1) + *bufsize += 256; + else + *bufsize += chars + 25; + oldret = *ret; + *ret = new char[*bufsize]; + if (oldret) { + if (*strlen) + memcpy(*ret, oldret, *strlen); + safe_delete_array(oldret); + } + va_copy(tmpargptr, argptr); + chars = vsnprintf(&(*ret)[*strlen], (*bufsize-*strlen), format, tmpargptr); + } + va_end(argptr); + *strlen += chars; + return *strlen; +} + +uint32 hextoi(char* num) { + int len = strlen(num); + if (len < 3) + return 0; + + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + return 0; + + uint32 ret = 0; + int mul = 1; + for (int i=len-1; i>=2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') + ret += ((num[i] - 'A') + 10) * mul; + else if (num[i] >= 'a' && num[i] <= 'f') + ret += ((num[i] - 'a') + 10) * mul; + else if (num[i] >= '0' && num[i] <= '9') + ret += (num[i] - '0') * mul; + else + return 0; + mul *= 16; + } + return ret; +} + +uint64 hextoi64(char* num) { + int len = strlen(num); + if (len < 3) + return 0; + + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + return 0; + + uint64 ret = 0; + int mul = 1; + for (int i=len-1; i>=2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') + ret += ((num[i] - 'A') + 10) * mul; + else if (num[i] >= 'a' && num[i] <= 'f') + ret += ((num[i] - 'a') + 10) * mul; + else if (num[i] >= '0' && num[i] <= '9') + ret += (num[i] - '0') * mul; + else + return 0; + mul *= 16; + } + return ret; +} + +bool atobool(char* iBool) { + if (!strcasecmp(iBool, "true")) + return true; + if (!strcasecmp(iBool, "false")) + return false; + if (!strcasecmp(iBool, "yes")) + return true; + if (!strcasecmp(iBool, "no")) + return false; + if (!strcasecmp(iBool, "on")) + return true; + if (!strcasecmp(iBool, "off")) + return false; + if (!strcasecmp(iBool, "enable")) + return true; + if (!strcasecmp(iBool, "disable")) + return false; + if (!strcasecmp(iBool, "enabled")) + return true; + if (!strcasecmp(iBool, "disabled")) + return false; + if (!strcasecmp(iBool, "y")) + return true; + if (!strcasecmp(iBool, "n")) + return false; + if (atoi(iBool)) + return true; + return false; +} + +int32 filesize(FILE* fp) { +#ifdef _WINDOWS + return _filelength(_fileno(fp)); +#else + struct stat file_stat; + fstat(fileno(fp), &file_stat); + return (int32) file_stat.st_size; +/* uint32 tmp = 0; + while (!feof(fp)) { + fseek(fp, tmp++, SEEK_SET); + } + return tmp;*/ +#endif +} + +uint32 ResolveIP(const char* hostname, char* errbuf) { +#ifdef _WINDOWS + static InitWinsock ws; +#endif + if (errbuf) + errbuf[0] = 0; + if (hostname == 0) { + if (errbuf) + snprintf(errbuf, ERRBUF_SIZE, "ResolveIP(): hostname == 0"); + return 0; + } + struct sockaddr_in server_sin; +#ifdef _WINDOWS + PHOSTENT phostent = NULL; +#else + struct hostent *phostent = NULL; +#endif + server_sin.sin_family = AF_INET; + if ((phostent = gethostbyname(hostname)) == NULL) { +#ifdef _WINDOWS + if (errbuf) + snprintf(errbuf, ERRBUF_SIZE, "Unable to get the host name. Error: %i", WSAGetLastError()); +#else + if (errbuf) + snprintf(errbuf, ERRBUF_SIZE, "Unable to get the host name. Error: %s", strerror(errno)); +#endif + return 0; + } +#ifdef _WINDOWS + memcpy ((char FAR *)&(server_sin.sin_addr), phostent->h_addr, phostent->h_length); +#else + memcpy ((char*)&(server_sin.sin_addr), phostent->h_addr, phostent->h_length); +#endif + return server_sin.sin_addr.s_addr; +} + +bool ParseAddress(const char* iAddress, uint32* oIP, uint16* oPort, char* errbuf) { + Seperator sep(iAddress, ':', 2, 250, false, 0, 0); + if (sep.argnum == 1 && sep.IsNumber(1)) { + *oIP = ResolveIP(sep.arg[0], errbuf); + if (*oIP == 0) + return false; + if (oPort) + *oPort = atoi(sep.arg[1]); + return true; + } + return false; +} + +#ifdef _WINDOWS +InitWinsock::InitWinsock() { + WORD version = MAKEWORD (1,1); + WSADATA wsadata; + WSAStartup (version, &wsadata); +} + +InitWinsock::~InitWinsock() { + WSACleanup(); +} + +#endif + + +const char * itoa(int num) { + static char temp[_ITOA_BUFLEN]; + memset(temp,0,_ITOA_BUFLEN); + snprintf(temp,_ITOA_BUFLEN,"%d",num); + return temp; +} + +#ifndef WIN32 +const char * itoa(int num, char* a,int b) { + static char temp[_ITOA_BUFLEN]; + memset(temp,0,_ITOA_BUFLEN); + snprintf(temp,_ITOA_BUFLEN,"%d",num); + return temp; + return temp; +} +#endif + +/* + * generate a random integer in the range low-high this + * should be used instead of the rand()%limit method + */ +int MakeRandomInt(int low, int high) +{ + _CP(MakeRandomInt); + if(low >= high) + return(low); + + //return (rand()%(high-low+1) + (low)); + if(!WELLRNG_init) { + WELLRNG_init = true; + oneseed( rnd_hash( time(NULL), clock() ) ); + WELLRNG19937 = case_1; + } + unsigned int randomnum = ((WELLRNG19937)()); + if(randomnum == 0xffffffffUL) + return high; + return int ((randomnum / (double)0xffffffffUL) * (high - low + 1) + low); +} + +double MakeRandomFloat(double low, double high) +{ + _CP(MakeRandomFloat); + if(low >= high) + return(low); + + //return (rand() / (double)RAND_MAX * (high - low) + low); + if(!WELLRNG_init) { + WELLRNG_init = true; + oneseed( rnd_hash( time(NULL), clock() ) ); + WELLRNG19937 = case_1; + } + return ((WELLRNG19937)() / (double)0xffffffffUL * (high - low) + low); +} + +uint32 rnd_hash( time_t t, clock_t c ) +{ + // Get a uint32 from t and c + // Better than uint32(x) in case x is floating point in [0,1] + // Based on code by Lawrence Kirby (fred@genesis.demon.co.uk) + + static uint32 differ = 0; // guarantee time-based seeds will change + + uint32 h1 = 0; + unsigned char *p = (unsigned char *) &t; + for( size_t i = 0; i < sizeof(t); ++i ) + { + h1 *= 255 + 2U; + h1 += p[i]; + } + uint32 h2 = 0; + p = (unsigned char *) &c; + for( size_t j = 0; j < sizeof(c); ++j ) + { + h2 *= 255 + 2U; + h2 += p[j]; + } + return ( h1 + differ++ ) ^ h2; +} + +void oneseed( const uint32 seed ) +{ + // Initialize generator state with seed + // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. + // In previous versions, most significant bits (MSBs) of the seed affect + // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. + register int j = 0; + STATE[j] = seed & 0xffffffffUL; + for (j = 1; j < R; j++) + { + STATE[j] = ( 1812433253UL * ( STATE[j-1] ^ (STATE[j-1] >> 30) ) + j ) & 0xffffffffUL; + } +} + +// WELL RNG code + +/* ***************************************************************************** */ +/* Copyright: Francois Panneton and Pierre L'Ecuyer, University of Montreal */ +/* Makoto Matsumoto, Hiroshima University */ +/* Notice: This code can be used freely for personal, academic, */ +/* or non-commercial purposes. For commercial purposes, */ +/* please contact P. L'Ecuyer at: lecuyer@iro.UMontreal.ca */ +/* A modified "maximally equidistributed" implementation */ +/* by Shin Harase, Hiroshima University. */ +/* ***************************************************************************** */ + +unsigned int case_1 (void){ + // state_i == 0 + z0 = (VRm1Under & MASKL) | (VRm2Under & MASKU); + z1 = MAT0NEG (-25, V0) ^ MAT0POS (27, VM1); + z2 = MAT3POS (9, VM2) ^ MAT0POS (1, VM3); + newV1 = z1 ^ z2; + newV0Under = MAT1 (z0) ^ MAT0NEG (-9, z1) ^ MAT0NEG (-21, z2) ^ MAT0POS (21, newV1); + state_i = R - 1; + WELLRNG19937 = case_3; + return (STATE[state_i] ^ (newVM2Over & BITMASK)); +} + +static unsigned int case_2 (void){ + // state_i == 1 + z0 = (VRm1 & MASKL) | (VRm2Under & MASKU); + z1 = MAT0NEG (-25, V0) ^ MAT0POS (27, VM1); + z2 = MAT3POS (9, VM2) ^ MAT0POS (1, VM3); + newV1 = z1 ^ z2; + newV0 = MAT1 (z0) ^ MAT0NEG (-9, z1) ^ MAT0NEG (-21, z2) ^ MAT0POS (21, newV1); + state_i = 0; + WELLRNG19937 = case_1; + return (STATE[state_i] ^ (newVM2 & BITMASK)); +} + +static unsigned int case_3 (void){ + // state_i+M1 >= R + z0 = (VRm1 & MASKL) | (VRm2 & MASKU); + z1 = MAT0NEG (-25, V0) ^ MAT0POS (27, VM1Over); + z2 = MAT3POS (9, VM2Over) ^ MAT0POS (1, VM3Over); + newV1 = z1 ^ z2; + newV0 = MAT1 (z0) ^ MAT0NEG (-9, z1) ^ MAT0NEG (-21, z2) ^ MAT0POS (21, newV1); + state_i--; + if (state_i + M1 < R) + WELLRNG19937 = case_5; + return (STATE[state_i] ^ (newVM2Over & BITMASK)); +} + +static unsigned int case_4 (void){ + // state_i+M3 >= R + z0 = (VRm1 & MASKL) | (VRm2 & MASKU); + z1 = MAT0NEG (-25, V0) ^ MAT0POS (27, VM1); + z2 = MAT3POS (9, VM2) ^ MAT0POS (1, VM3Over); + newV1 = z1 ^ z2; + newV0 = MAT1 (z0) ^ MAT0NEG (-9, z1) ^ MAT0NEG (-21, z2) ^ MAT0POS (21, newV1); + state_i--; + if (state_i + M3 < R) + WELLRNG19937 = case_6; + return (STATE[state_i] ^ (newVM2 & BITMASK)); +} + +static unsigned int case_5 (void){ + // state_i+M2 >= R + z0 = (VRm1 & MASKL) | (VRm2 & MASKU); + z1 = MAT0NEG (-25, V0) ^ MAT0POS (27, VM1); + z2 = MAT3POS (9, VM2Over) ^ MAT0POS (1, VM3Over); + newV1 = z1 ^ z2; + newV0 = MAT1 (z0) ^ MAT0NEG (-9, z1) ^ MAT0NEG (-21, z2) ^ MAT0POS (21, newV1); + state_i--; + if (state_i + M2 < R) + WELLRNG19937 = case_4; + return (STATE[state_i] ^ (newVM2Over & BITMASK)); +} + +static unsigned int case_6 (void){ + // 2 <= state_i <= (R - M3 - 1) + z0 = (VRm1 & MASKL) | (VRm2 & MASKU); + z1 = MAT0NEG (-25, V0) ^ MAT0POS (27, VM1); + z2 = MAT3POS (9, VM2) ^ MAT0POS (1, VM3); + newV1 = z1 ^ z2; + newV0 = MAT1 (z0) ^ MAT0NEG (-9, z1) ^ MAT0NEG (-21, z2) ^ MAT0POS (21, newV1); + state_i--; + if (state_i == 1) + WELLRNG19937 = case_2; + return (STATE[state_i] ^ (newVM2 & BITMASK)); +} + +// end WELL RNG code + +// solar: removes the crap and turns the underscores into spaces. +char *CleanMobName(const char *in, char *out) +{ + unsigned i, j; + + for(i = j = 0; i < strlen(in); i++) + { + // convert _ to space.. any other conversions like this? I *think* this + // is the only non alpha char that's not stripped but converted. + if(in[i] == '_') + { + out[j++] = ' '; + } + else + { + if(isalpha(in[i]) || (in[i] == '`')) // numbers, #, or any other crap just gets skipped + out[j++] = in[i]; + } + } + out[j] = 0; // terimnate the string before returning it + return out; +} + +const char *ConvertArray(int input, char *returnchar) +{ + sprintf(returnchar, "%i" ,input); + return returnchar; +} + +const char *ConvertArrayF(float input, char *returnchar) +{ + sprintf(returnchar, "%0.2f", input); + return returnchar; +} + +float EQ13toFloat(int d) +{ + return ( float(d)/float(1<<2)); +} + +float NewEQ13toFloat(int d) +{ + return ( float(d)/float(1<<6)); +} + +float EQ19toFloat(int d) +{ + return ( float(d)/float(1<<3)); +} + +int FloatToEQ13(float d) +{ + return int(d*float(1<<2)); +} + +int NewFloatToEQ13(float d) +{ + return int(d*float(1<<6)); +} + +int FloatToEQ19(float d) +{ + return int(d*float(1<<3)); +} + +/* + Heading of 0 points in the pure positive Y direction + +*/ +int FloatToEQH(float d) +{ + return(int((360.0f - d) * float(1<<11)) / 360); +} + +float EQHtoFloat(int d) +{ + return(360.0f - float((d * 360) >> 11)); +} + +void RemoveApostrophes(std::string &s) +{ + for(unsigned int i = 0; i < s.length(); ++i) + if(s[i] == '\'') + s[i] = '_'; +} + +char *RemoveApostrophes(const char *s) +{ + char *NewString = new char[strlen(s) + 1]; + + strcpy(NewString, s); + + for(unsigned int i = 0 ; i < strlen(NewString); ++i) + if(NewString[i] == '\'') + NewString[i] = '_'; + + return NewString; +} + diff --git a/common/MiscFunctions.h b/common/MiscFunctions.h new file mode 100644 index 000000000..9dbed2478 --- /dev/null +++ b/common/MiscFunctions.h @@ -0,0 +1,171 @@ +/* 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 MISCFUNCTIONS_H +#define MISCFUNCTIONS_H + +#include "types.h" +#include +#include +#include +#include + + +#ifndef ERRBUF_SIZE +#define ERRBUF_SIZE 1024 +#endif + +// These are helper macros for dealing with packets of variable length, typically those that contain +// variable length strings where it is not convenient to use a fixed length struct. +// +#define VARSTRUCT_DECODE_TYPE(Type, Buffer) *(Type *)Buffer; Buffer += sizeof(Type); +#define VARSTRUCT_DECODE_STRING(String, Buffer) strcpy(String, Buffer); Buffer += strlen(String)+1; +#define VARSTRUCT_ENCODE_STRING(Buffer, String) { sprintf(Buffer, String); Buffer += strlen(String) + 1; } +#define VARSTRUCT_ENCODE_INTSTRING(Buffer, Number) { sprintf(Buffer, "%i", Number); Buffer += strlen(Buffer) + 1; } +#define VARSTRUCT_ENCODE_TYPE(Type, Buffer, Value) { *(Type *)Buffer = Value; Buffer += sizeof(Type); } +#define VARSTRUCT_SKIP_TYPE(Type, Buffer) Buffer += sizeof(Type); + +#define VERIFY_PACKET_LENGTH(OPCode, Packet, StructName) \ + if(Packet->size != sizeof(StructName)) \ + { \ + LogFile->write(EQEMuLog::Debug, "Size mismatch in " #OPCode " expected %i got %i", sizeof(StructName), Packet->size); \ + DumpPacket(Packet); \ + return; \ + } + +// Definitions for WELLRNG +// +#define W 32 +#define R 624 +#define DISCARD 31 +#define MASKU (0xffffffffU>>(W-DISCARD)) +#define MASKL (~MASKU) +#define M1 70 +#define M2 179 +#define M3 449 + +#define MAT0POS(t,v) (v^(v>>t)) +#define MAT0NEG(t,v) (v^(v<<(-(t)))) +#define MAT1(v) v +#define MAT3POS(t,v) (v>>t) + +#define V0 STATE[state_i] +#define VM1Over STATE[state_i+M1-R] +#define VM1 STATE[state_i+M1] +#define VM2Over STATE[state_i+M2-R] +#define VM2 STATE[state_i+M2] +#define VM3Over STATE[state_i+M3-R] +#define VM3 STATE[state_i+M3] +#define VRm1 STATE[state_i-1] +#define VRm1Under STATE[state_i+R-1] +#define VRm2 STATE[state_i-2] +#define VRm2Under STATE[state_i+R-2] + +#define newV0 STATE[state_i-1] +#define newV0Under STATE[state_i-1+R] +#define newV1 STATE[state_i] +#define newVRm1 STATE[state_i-2] +#define newVRm1Under STATE[state_i-2+R] + +#define newVM2Over STATE[state_i+M2-R+1] +#define newVM2 STATE[state_i+M2+1] + +#define BITMASK 0x41180000 + +////////////////////////////////////////////////////////////////////// +// +// MakeUpperString +// i : source - allocated null-terminated string +// return: pointer to static buffer with the target string +const char *MakeUpperString(const char *source); +const char *MakeLowerString(const char *source); +////////////////////////////////////////////////////////////////////// +// +// MakeUpperString +// i : source - allocated null-terminated string +// io: target - allocated buffer, at least of size strlen(source)+1 +void MakeUpperString(const char *source, char *target); +void MakeLowerString(const char *source, char *target); + + +int MakeAnyLenString(char** ret, const char* format, ...); +uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...); +uint32 hextoi(char* num); +uint64 hextoi64(char* num); +bool atobool(char* iBool); +int32 filesize(FILE* fp); +uint32 ResolveIP(const char* hostname, char* errbuf = 0); +bool ParseAddress(const char* iAddress, uint32* oIP, uint16* oPort, char* errbuf = 0); +void CoutTimestamp(bool ms = true); +char* strn0cpy(char* dest, const char* source, uint32 size); + // return value =true if entire string(source) fit, false if it was truncated +bool strn0cpyt(char* dest, const char* source, uint32 size); +int MakeRandomInt(int low, int high); +double MakeRandomFloat(double low, double high); +char *CleanMobName(const char *in, char *out); +const char *ConvertArray(int input, char *returnchar); +const char *ConvertArrayF(float input, char *returnchar); +float EQ13toFloat(int d); +float NewEQ13toFloat(int d); +float EQ19toFloat(int d); +float EQHtoFloat(int d); +int FloatToEQ13(float d); +int NewFloatToEQ13(float d); +int FloatToEQ19(float d); +int FloatToEQH(float d); +void RemoveApostrophes(std::string &s); +char *RemoveApostrophes(const char *s); + + + +#define _ITOA_BUFLEN 25 +const char *itoa(int num); //not thread safe +#ifndef _WINDOWS +const char *itoa(int num, char* a,int b); +#endif + +class InitWinsock { +public: + InitWinsock(); + ~InitWinsock(); +}; + +template class AutoDelete { +public: + AutoDelete(T** iVar, T* iSetTo = 0) { + init(iVar, iSetTo); + } + AutoDelete() { pVar = NULL; } + void init(T** iVar, T* iSetTo = 0) + { + pVar = iVar; + if (iSetTo) + *pVar = iSetTo; + } + ~AutoDelete() { + if(pVar != NULL) + safe_delete(*pVar); + } + void ReallyClearIt() { + pVar = NULL; + } +private: + T** pVar; +}; + +#endif + diff --git a/common/Mutex.cpp b/common/Mutex.cpp new file mode 100644 index 000000000..caf53b644 --- /dev/null +++ b/common/Mutex.cpp @@ -0,0 +1,285 @@ +/* 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 "../common/debug.h" +#include "../common/Mutex.h" + +#include +using namespace std; + +#define DEBUG_MUTEX_CLASS 0 +#if DEBUG_MUTEX_CLASS >= 1 + +#endif + +#ifdef _WINDOWS +bool IsTryLockSupported(); +bool TrylockSupported = IsTryLockSupported(); + +bool IsTryLockSupported() { + OSVERSIONINFOEX osvi; + BOOL bOsVersionInfoEx; + + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) ) + { + // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO. + osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); + if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) { +#if DEBUG_MUTEX_CLASS >= 1 + cout << "Mutex::trylock() NOT supported" << endl; +#endif + return false; + } + } + + // Tests for Windows NT product family. + if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion >= 4) { +#if DEBUG_MUTEX_CLASS >= 1 + cout << "Mutex::trylock() SUPPORTED" << endl; +#endif + return true; + } + else { +#if DEBUG_MUTEX_CLASS >= 1 + cout << "Mutex::trylock() NOT supported" << endl; +#endif + return false; + } +} +#endif + +Mutex::Mutex() { +#if DEBUG_MUTEX_CLASS >= 7 + cout << "Constructing Mutex" << endl; +#endif +#ifdef _WINDOWS + InitializeCriticalSection(&CSMutex); +#else + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); +#if defined(__CYGWIN__) || defined(__APPLE__) + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); +#else + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE_NP); +#endif + pthread_mutex_init(&CSMutex, &attr); + pthread_mutexattr_destroy(&attr); +#endif +} + +Mutex::~Mutex() { +#if DEBUG_MUTEX_CLASS >= 7 + cout << "Deconstructing Mutex" << endl; +#endif +#ifdef _WINDOWS + DeleteCriticalSection(&CSMutex); +#else +// pthread_mutex_destroy(&CSMutex); +#endif +} + +void Mutex::lock() { + _CP(Mutex_lock); +#if DEBUG_MUTEX_CLASS >= 9 + cout << "Locking Mutex" << endl; +#endif +#if DEBUG_MUTEX_CLASS >= 5 + if (!trylock()) { + cout << "Locking Mutex: Having to wait" << endl; + #ifdef _WINDOWS + EnterCriticalSection(&CSMutex); + #else + pthread_mutex_lock(&CSMutex); + #endif + } +#else + #ifdef _WINDOWS + EnterCriticalSection(&CSMutex); + #else + pthread_mutex_lock(&CSMutex); + #endif +#endif +} + +bool Mutex::trylock() { +#if DEBUG_MUTEX_CLASS >= 9 + cout << "TryLocking Mutex" << endl; +#endif +#ifdef _WINDOWS + #if(_WIN32_WINNT >= 0x0400) + if (TrylockSupported) + return TryEnterCriticalSection(&CSMutex); + else { + EnterCriticalSection(&CSMutex); + return true; + } + #else + EnterCriticalSection(&CSMutex); + return true; + #endif +#else + return (pthread_mutex_trylock(&CSMutex) == 0); +#endif +} + +void Mutex::unlock() { +#if DEBUG_MUTEX_CLASS >= 9 + cout << "Unlocking Mutex" << endl; +#endif +#ifdef _WINDOWS + LeaveCriticalSection(&CSMutex); +#else + pthread_mutex_unlock(&CSMutex); +#endif +} + + +LockMutex::LockMutex(Mutex* in_mut, bool iLock) { + mut = in_mut; + locked = iLock; + if (locked) { + mut->lock(); + } +} + +LockMutex::~LockMutex() { + if (locked) { + mut->unlock(); + } +} + +void LockMutex::unlock() { + if (locked) + mut->unlock(); + locked = false; +} + +void LockMutex::lock() { + if (!locked) + mut->lock(); + locked = true; +} + + +MRMutex::MRMutex() { + rl = 0; + wr = 0; + rl = 0; +} + +MRMutex::~MRMutex() { +#ifdef _EQDEBUG + if (wl || rl) { + cout << "MRMutex::~MRMutex: poor cleanup detected: rl=" << rl << ", wl=" << wl << endl; + } +#endif +} + +void MRMutex::ReadLock() { + while (!TryReadLock()) { + Sleep(1); + } +} + +bool MRMutex::TryReadLock() { + MCounters.lock(); + if (!wr && !wl) { + rl++; + MCounters.unlock(); + return true; + } + else { + MCounters.unlock(); + return false; + } +} + +void MRMutex::UnReadLock() { + MCounters.lock(); + rl--; +#ifdef _EQDEBUG + if (rl < 0) { + ThrowError("rl < 0 in MRMutex::UnReadLock()"); + } +#endif + MCounters.unlock(); +} + +void MRMutex::WriteLock() { + MCounters.lock(); + if (!rl && !wl) { + wl++; + MCounters.unlock(); + return; + } + else { + wr++; + MCounters.unlock(); + while (1) { + Sleep(1); + MCounters.lock(); + if (!rl && !wl) { + wr--; + MCounters.unlock(); + return; + } + MCounters.lock(); + } + } +} + +bool MRMutex::TryWriteLock() { + MCounters.lock(); + if (!rl && !wl) { + wl++; + MCounters.unlock(); + return true; + } + else { + MCounters.unlock(); + return false; + } +} + +void MRMutex::UnWriteLock() { + MCounters.lock(); + wl--; +#ifdef _EQDEBUG + if (wl < 0) { + ThrowError("wl < 0 in MRMutex::UnWriteLock()"); + } +#endif + MCounters.unlock(); +} + +int32 MRMutex::ReadLockCount() { + MCounters.lock(); + int32 ret = rl; + MCounters.unlock(); + return ret; +} + +int32 MRMutex::WriteLockCount() { + MCounters.lock(); + int32 ret = wl; + MCounters.unlock(); + return ret; +} + diff --git a/common/Mutex.h b/common/Mutex.h new file mode 100644 index 000000000..32c29ece8 --- /dev/null +++ b/common/Mutex.h @@ -0,0 +1,83 @@ +/* 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 MYMUTEX_H +#define MYMUTEX_H +#ifdef _WINDOWS + #include + #include +#else + #include + #include "../common/unix.h" +#endif +#include "../common/types.h" + +class Mutex { +public: + Mutex(); + ~Mutex(); + + void lock(); + void unlock(); + bool trylock(); +protected: +private: +#if defined WIN32 || defined WIN64 + CRITICAL_SECTION CSMutex; +#else + pthread_mutex_t CSMutex; +#endif +}; + +class LockMutex { +public: + LockMutex(Mutex* in_mut, bool iLock = true); + ~LockMutex(); + void unlock(); + void lock(); +private: + bool locked; + Mutex* mut; +}; + + +// Somewhat untested... +// Multi-read, single write mutex -Quagmire +class MRMutex { +public: + MRMutex(); + ~MRMutex(); + + void ReadLock(); + bool TryReadLock(); + void UnReadLock(); + + void WriteLock(); + bool TryWriteLock(); + void UnWriteLock(); + + int32 ReadLockCount(); + int32 WriteLockCount(); +private: + int32 rl; // read locks in effect + int32 wr; // write lock requests pending + int32 wl; // write locks in effect (should never be more than 1) + Mutex MCounters; +}; + +#endif + diff --git a/common/ProcLauncher.cpp b/common/ProcLauncher.cpp new file mode 100644 index 000000000..e46589032 --- /dev/null +++ b/common/ProcLauncher.cpp @@ -0,0 +1,368 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "debug.h" +#include "ProcLauncher.h" +#ifdef _WINDOWS +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +using namespace std; + +ProcLauncher ProcLauncher::s_launcher; + +#ifdef _WINDOWS +const ProcLauncher::ProcRef ProcLauncher::ProcError = 0xFFFFFFFF; +#else +const ProcLauncher::ProcRef ProcLauncher::ProcError = -1; +#endif + +ProcLauncher::ProcLauncher() +{ +#ifndef WIN32 + if(signal(SIGCHLD, ProcLauncher::HandleSigChild) == SIG_ERR) + fprintf(stderr, "Unable to register child signal handler. Thats bad."); + m_signalCount = 0; +#endif +} + + +void ProcLauncher::Process() { +#ifdef _WINDOWS + map::iterator cur, end, tmp; + cur = m_running.begin(); + end = m_running.end(); + while(cur != end) { + DWORD res; + if(GetExitCodeProcess(cur->second->proc_info.hProcess, &res)) { + //got exit code, see if its still running... + if(res == STILL_ACTIVE) { + cur++; + continue; + } + //else, it died, handle properly + } else { + //not sure the right thing to do here... why would this fail? + //GetLastError(); + TerminateProcess(cur->second->proc_info.hProcess, 1); + } + + //if we get here, the current process died. + tmp = cur; + tmp++; + ProcessTerminated(cur); + cur = tmp; + } +#else //!WIN32 + while(m_signalCount > 0) { + m_signalCount--; + int status; + ProcRef died = waitpid(-1, &status, WNOHANG); + if(died == -1) { + //error waiting... shouldent really happen... + + } else if(died == 0) { + //nothing pending... + break; + } else { + //one died... + map::iterator ref; + ref = m_running.find(died); + if(ref == m_running.end()) { + //unable to find this process in our list... + } else { + //found... hooray + ProcessTerminated(ref); + } + } + } +#endif //!WIN32 + +} + +void ProcLauncher::ProcessTerminated(std::map::iterator &it) { + + if(it->second->handler != NULL) + it->second->handler->OnTerminate(it->first, it->second); + +#ifdef _WINDOWS + CloseHandle(it->second->proc_info.hProcess); +#else //!WIN32 +#endif //!WIN32 + delete it->second; + m_running.erase(it); +} + +ProcLauncher::ProcRef ProcLauncher::Launch(Spec *&to_launch) { + //consume the pointer + Spec *it = to_launch; + to_launch = NULL; + +#ifdef _WINDOWS + STARTUPINFO siStartInfo; + BOOL bFuncRetn = FALSE; + + // Set up members of the PROCESS_INFORMATION structure. + + ZeroMemory( &it->proc_info, sizeof(PROCESS_INFORMATION) ); + + // Set up members of the STARTUPINFO structure. + + ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) ); + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.dwFlags = 0; + + //handle output redirection. + HANDLE logOut = NULL; + BOOL inherit_handles = FALSE; + if(it->logFile.length() > 0) { + inherit_handles = TRUE; + // Set up our log file to redirect output into. + SECURITY_ATTRIBUTES saAttr; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; //we want this handle to be inherited by the child. + saAttr.lpSecurityDescriptor = NULL; + logOut = CreateFile( + it->logFile.c_str(), //lpFileName + FILE_WRITE_DATA, //dwDesiredAccess + FILE_SHARE_READ, //dwShareMode + &saAttr, //lpSecurityAttributes + CREATE_ALWAYS, //dwCreationDisposition + FILE_FLAG_NO_BUFFERING, //dwFlagsAndAttributes + NULL ); //hTemplateFile + + //configure the startup info to redirect output appropriately. + siStartInfo.hStdError = logOut; + siStartInfo.hStdOutput = logOut; + siStartInfo.hStdInput = NULL; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + } + + siStartInfo.dwFlags |= CREATE_NEW_CONSOLE; + + // Create the child process. + + //glue together all the nice command line arguments + string args(it->program); + vector::iterator cur, end; + cur = it->args.begin(); + end = it->args.end(); + for(; cur != end; cur++) { + args += " "; + args += *cur; + } + + bFuncRetn = CreateProcess(it->program.c_str(), + const_cast(args.c_str()), // command line + NULL, // process security attributes + NULL, // primary thread security attributes + inherit_handles, // handles are not inherited + 0, // creation flags (CREATE_NEW_PROCESS_GROUP maybe) + NULL, // use parent's environment + NULL, // use parent's current directory + &siStartInfo, // STARTUPINFO pointer + &it->proc_info); // receives PROCESS_INFORMATION + + if (bFuncRetn == 0) { + safe_delete(it); + //GetLastError() + return(ProcError); + } + + + //keep process handle open to get exit code + CloseHandle(it->proc_info.hThread); //we dont need their thread handle + if(logOut != NULL) + CloseHandle(logOut); //we dont want their output handle either. + + ProcRef res = it->proc_info.dwProcessId; + + //record this entry.. + m_running[res] = it; + + return(res); + +#else //!WIN32 + + //build argv + char **argv = new char *[it->args.size()+2]; + unsigned int r; + argv[0] = const_cast(it->program.c_str()); + for(r = 1; r <= it->args.size(); r++) { + argv[r] = const_cast(it->args[r-1].c_str()); + } + argv[r] = NULL; + + ProcRef res = fork(); //cant use vfork since we are opening the log file. + if(res == -1) { + //error forking... errno + safe_delete(it); + safe_delete_array(argv); + return(ProcError); + } + + if(res == 0) { + //child... exec this bitch + + //handle output redirection if requested. + if(it->logFile.length() > 0) { + //we will put their output directly into a file. + int outfd = creat(it->logFile.c_str(), S_IRUSR | S_IWUSR | S_IRGRP); // S_I + R/W/X + USR/GRP/OTH + if(outfd == -1) { + fprintf(stderr, "Unable to open log file %s: %s.\n", it->logFile.c_str(), strerror(errno)); + close(STDOUT_FILENO); + close(STDERR_FILENO); + close(STDIN_FILENO); + } else { + close(STDOUT_FILENO); + if(dup2(outfd, STDOUT_FILENO) == -1) { + fprintf(stderr, "Unable to duplicate FD %d to %d. Log file will be empty: %s\n", outfd, STDOUT_FILENO, strerror(errno)); + const char *err = "Unable to redirect stdout into this file. That sucks."; + write(outfd, err, strlen(err)); + } + close(STDERR_FILENO); + if(dup2(outfd, STDERR_FILENO) == -1) { + //can no longer print to screen.. + const char *err = "Unable to redirect stderr into this file. You might miss some error info in this log."; + write(outfd, err, strlen(err)); + } + close(STDIN_FILENO); + + close(outfd); //dont need this one, we have two more copies... + } + } + + //call it... + execv(argv[0], argv); + _exit(1); + } + safe_delete_array(argv); + + //record this entry.. + m_running[res] = it; + + return(res); +#endif //!WIN32 +} + + +//if graceful is true, we try to be nice about it if possible +bool ProcLauncher::Terminate(const ProcRef &proc, bool graceful) { + //we are only willing to kill things we started... + std::map::iterator res = m_running.find(proc); + if(res == m_running.end()) + return(false); + + //we do not remove it from the list until we have been notified + //that they have been terminated. + +#ifdef _WINDOWS + if(!TerminateProcess(res->second->proc_info.hProcess, 0)) { + return(false); + } +#else //!WIN32 + int sig; + if(graceful) + sig = SIGTERM; + else + sig = SIGKILL; + if(kill(proc, sig) == -1) { + return(false); + } +#endif //!WIN32 + return(true); +} + +void ProcLauncher::TerminateAll(bool final) { + if(!final) { + //send a nice terminate to each process, with intention of waiting for them + std::map::iterator cur, end; + cur = m_running.begin(); + end = m_running.end(); + for(; cur != end; cur++) { + Terminate(cur->first, true); + } + } else { + //kill each process and remove it from the list + std::map running(m_running); + m_running.clear(); + + std::map::iterator cur, end; + cur = running.begin(); + end = running.end(); + for(; cur != end; cur++) { + Terminate(cur->first, true); + safe_delete(cur->second); + } + } +} + + +#ifndef WIN32 +void ProcLauncher::HandleSigChild(int signum) { + if(signum == SIGCHLD) { + ProcLauncher::get()->m_signalCount++; + } +} +#endif + + + +ProcLauncher::Spec::Spec() { + handler = NULL; +} + +ProcLauncher::Spec::Spec(const Spec &other) { + program = other.program; + args = other.args; + handler = other.handler; + logFile = other.logFile; +} + +ProcLauncher::Spec &ProcLauncher::Spec::operator=(const Spec &other) { + program = other.program; + args = other.args; + handler = other.handler; + logFile = other.logFile; + return(*this); +} + + + + + + + + + + + + + + + diff --git a/common/ProcLauncher.h b/common/ProcLauncher.h new file mode 100644 index 000000000..1c851de4f --- /dev/null +++ b/common/ProcLauncher.h @@ -0,0 +1,124 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 PROCLAUNCHER_H_ +#define PROCLAUNCHER_H_ + +#include "debug.h" + +#include +#include +#include + +//I forced this object to become a singleton because it registers its +//signal handler for UNIX +class ProcLauncher { + ProcLauncher(); +public: + //Singleton method + static ProcLauncher *get() { return(&s_launcher); } + static void ProcessInThisThread(); + +#ifdef WIN32 + typedef DWORD ProcRef; + static const ProcRef ProcError; +#else + typedef pid_t ProcRef; + static const ProcRef ProcError; +#endif + class EventHandler; + class Spec { + friend class ProcLauncher; //for visual c++ + public: + Spec(); + Spec(const Spec &other); + Spec &operator=(const Spec &other); + + std::string program; + std::vector args; + //std::map environment; + EventHandler *handler; //optional, we do not own this pointer + std::string logFile; //empty = do not redirect output. + protected: + //None of these fields get copied around +#ifdef WIN32 + PROCESS_INFORMATION proc_info; +#endif + }; + class EventHandler { + public: + virtual ~EventHandler() {} + virtual void OnTerminate(const ProcRef &ref, const Spec *spec) = 0; + }; + + /* + * The main launch method, call to start a new background process. + */ + ProcRef Launch(Spec *&to_launch); //takes ownership of the pointer + + /* + * The terminate method + */ + bool Terminate(const ProcRef &proc, bool graceful = true); + void TerminateAll(bool final = true); + + /* + * The main processing method. Call regularly to check for terminated + * background processes. + */ + void Process(); + +protected: +// std::vector m_specs; + std::map m_running; //we own the pointers in this map + + void ProcessTerminated(std::map::iterator &it); + +private: + static ProcLauncher s_launcher; +#ifndef WIN32 + uint32 m_signalCount; + static void HandleSigChild(int signum); +#endif +}; + + + + + + + + + +#endif /*PROCLAUNCHER_H_*/ + + + + + + + + + + + + + + + + + diff --git a/common/SharedLibrary.cpp b/common/SharedLibrary.cpp new file mode 100644 index 000000000..21e91b9d3 --- /dev/null +++ b/common/SharedLibrary.cpp @@ -0,0 +1,117 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "SharedLibrary.h" +#include + +#ifdef _WINDOWS + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + + #define EmuLibName "EMuShareMem" +#else + #define EmuLibName "libEMuShareMem.so" + + #include "../common/unix.h" + #include + #define GetProcAddress(a,b) dlsym(a,b) + #define LoadLibrary(a) dlopen(a, RTLD_NOW) + #define FreeLibrary(a) dlclose(a) + #define GetLastError() dlerror() +#endif + +SharedLibrary::SharedLibrary() { + hDLL = NULL; +} + +SharedLibrary::~SharedLibrary() { + Unload(); +} + +bool SharedLibrary::Load(const char *name) +{ +#ifdef _WINDOWS + SetLastError(0); +#endif + + hDLL = LoadLibrary(name); + + if(!hDLL) { + const char *load_error = GetError(); + fprintf(stderr, "[Error] Load Shared Library '%s' failed. Error=%s\n", name, load_error?load_error:"Null Return, no error"); + return false; + } +#ifdef _WINDOWS + else { SetLastError(0); } // Clear the win9x error +#endif + + return(true); +} + +void SharedLibrary::Unload() { + if (hDLL != NULL) { + FreeLibrary(hDLL); +#ifndef WIN32 + const char* error; + if ((error = GetError()) != NULL) + fprintf(stderr, "FreeLibrary() error = %s", error); +#endif + hDLL = NULL; + } +} + +void *SharedLibrary::GetSym(const char *name) { + if (!Loaded()) + return(NULL); + + void *r = GetProcAddress(hDLL, name); + + if(GetError() != NULL) + r = NULL; + + return(r); +} + +bool SharedLibrary::GetSym(const char *name, void **sym) +{ + bool result=false; + if (Loaded()) { + *sym = GetProcAddress(hDLL, name); + result= (GetError() == NULL); + } + + return result; +} + +const char *SharedLibrary::GetError() +{ +#ifdef _WINDOWS + //not thread safe, dont care. + static char ErrBuf[128]; + unsigned long err = GetLastError(); + if(err == 0) + return(NULL); + sprintf(ErrBuf, "Error #%lu", (unsigned long)err); + return(ErrBuf); +#else + return GetLastError(); +#endif +} diff --git a/common/SharedLibrary.h b/common/SharedLibrary.h new file mode 100644 index 000000000..db620c755 --- /dev/null +++ b/common/SharedLibrary.h @@ -0,0 +1,49 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 _SHAREDLIBRARY_H +#define _SHAREDLIBRARY_H + +#ifdef _WINDOWS +#include +#endif + +class SharedLibrary { +public: + SharedLibrary(); + virtual ~SharedLibrary(); + + //two call styles for GetSym, one returns bool, other NULL for fail + bool GetSym(const char *name, void **sym); + void *GetSym(const char *name); + + const char *GetError(); + + virtual bool Load(const char *file); + virtual void Unload(); + + inline bool Loaded() { return (hDLL != 0); } + +protected: +#ifdef _WINDOWS + HINSTANCE hDLL; +#else + void* hDLL; +#endif +}; + +#endif diff --git a/common/SocketLib/Base64.cpp b/common/SocketLib/Base64.cpp new file mode 100644 index 000000000..eb6a2fd0a --- /dev/null +++ b/common/SocketLib/Base64.cpp @@ -0,0 +1,266 @@ +/** \file Base64.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004,2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 "Base64.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +const char *Base64::bstr = + "ABCDEFGHIJKLMNOPQ" + "RSTUVWXYZabcdefgh" + "ijklmnopqrstuvwxy" + "z0123456789+/"; + +const char Base64::rstr[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, + 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0}; + + +void Base64::encode(FILE *fil, std::string& output, bool add_crlf) +{ + size_t remain; + size_t i = 0; + size_t o = 0; + char input[4]; + + output = ""; + remain = fread(input,1,3,fil); + while (remain > 0) + { + if (add_crlf && o && o % 76 == 0) + output += "\n"; + switch (remain) + { + case 1: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) ]; + output += "=="; + break; + case 2: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) ]; + output += "="; + break; + default: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) + ((input[i + 2] >> 6) & 0x03) ]; + output += bstr[ (input[i + 2] & 0x3f) ]; + } + o += 4; + // + remain = fread(input,1,3,fil); + } +} + + +void Base64::encode(const std::string& str_in, std::string& str_out, bool add_crlf) +{ + encode(str_in.c_str(), str_in.size(), str_out, add_crlf); +} + + +void Base64::encode(const char* input,size_t l,std::string& output, bool add_crlf) +{ + size_t i = 0; + size_t o = 0; + + output = ""; + while (i < l) + { + size_t remain = l - i; + if (add_crlf && o && o % 76 == 0) + output += "\n"; + switch (remain) + { + case 1: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) ]; + output += "=="; + break; + case 2: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) ]; + output += "="; + break; + default: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) + ((input[i + 2] >> 6) & 0x03) ]; + output += bstr[ (input[i + 2] & 0x3f) ]; + } + o += 4; + i += 3; + } +} + + +void Base64::encode(unsigned char* input,size_t l,std::string& output,bool add_crlf) +{ + size_t i = 0; + size_t o = 0; + + output = ""; + while (i < l) + { + size_t remain = l - i; + if (add_crlf && o && o % 76 == 0) + output += "\n"; + switch (remain) + { + case 1: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) ]; + output += "=="; + break; + case 2: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) ]; + output += "="; + break; + default: + output += bstr[ ((input[i] >> 2) & 0x3f) ]; + output += bstr[ ((input[i] << 4) & 0x30) + ((input[i + 1] >> 4) & 0x0f) ]; + output += bstr[ ((input[i + 1] << 2) & 0x3c) + ((input[i + 2] >> 6) & 0x03) ]; + output += bstr[ (input[i + 2] & 0x3f) ]; + } + o += 4; + i += 3; + } +} + + +void Base64::decode(const std::string& input,std::string& output) +{ + size_t i = 0; + size_t l = input.size(); + + output = ""; + while (i < l) + { + while (i < l && (input[i] == 13 || input[i] == 10)) + i++; + if (i < l) + { + char b1 = (char)((rstr[(int)input[i]] << 2 & 0xfc) + + (rstr[(int)input[i + 1]] >> 4 & 0x03)); + output += b1; + if (input[i + 2] != '=') + { + char b2 = (char)((rstr[(int)input[i + 1]] << 4 & 0xf0) + + (rstr[(int)input[i + 2]] >> 2 & 0x0f)); + output += b2; + } + if (input[i + 3] != '=') + { + char b3 = (char)((rstr[(int)input[i + 2]] << 6 & 0xc0) + + rstr[(int)input[i + 3]]); + output += b3; + } + i += 4; + } + } +} + + +void Base64::decode(const std::string& input, unsigned char *output, size_t& sz) +{ + size_t i = 0; + size_t l = input.size(); + size_t j = 0; + + while (i < l) + { + while (i < l && (input[i] == 13 || input[i] == 10)) + i++; + if (i < l) + { + unsigned char b1 = (unsigned char)((rstr[(int)input[i]] << 2 & 0xfc) + + (rstr[(int)input[i + 1]] >> 4 & 0x03)); + if (output) + { + output[j] = b1; + } + j++; + if (input[i + 2] != '=') + { + unsigned char b2 = (unsigned char)((rstr[(int)input[i + 1]] << 4 & 0xf0) + + (rstr[(int)input[i + 2]] >> 2 & 0x0f)); + if (output) + { + output[j] = b2; + } + j++; + } + if (input[i + 3] != '=') + { + unsigned char b3 = (unsigned char)((rstr[(int)input[i + 2]] << 6 & 0xc0) + + rstr[(int)input[i + 3]]); + if (output) + { + output[j] = b3; + } + j++; + } + i += 4; + } + } + sz = j; +} + + +size_t Base64::decode_length(const std::string& str64) +{ + if (!str64.size() || str64.size() % 4) + return 0; + size_t l = 3 * (str64.size() / 4 - 1) + 1; + if (str64[str64.size() - 2] != '=') + l++; + if (str64[str64.size() - 1] != '=') + l++; + return l; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/common/SocketLib/Base64.h b/common/SocketLib/Base64.h new file mode 100644 index 000000000..95608e30a --- /dev/null +++ b/common/SocketLib/Base64.h @@ -0,0 +1,67 @@ +/** \file Base64.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004,2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 _BASE64_H +#define _BASE64_H + +#include +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** \defgroup util Utilities */ + +/** Base64 encode/decode. + \ingroup util */ +class Base64 { +public: + + static void encode(FILE *, std::string& , bool add_crlf = true); + static void encode(const std::string&, std::string& , bool add_crlf = true); + static void encode(const char *, size_t, std::string& , bool add_crlf = true); + static void encode(unsigned char *, size_t, std::string& , bool add_crlf = true); + + static void decode(const std::string&, std::string& ); + static void decode(const std::string& in, unsigned char *out, size_t&); + + static size_t decode_length(const std::string& ); + +private: +static const char *bstr; +static const char rstr[128]; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _BASE64_H diff --git a/common/SocketLib/File.cpp b/common/SocketLib/File.cpp new file mode 100644 index 000000000..50a7d9a34 --- /dev/null +++ b/common/SocketLib/File.cpp @@ -0,0 +1,126 @@ +/** \file File.cpp + ** \date 2005-04-25 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004,2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +#include "File.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +File::File() +:m_fil(NULL) +{ +} + + +File::~File() +{ +} + + +bool File::fopen(const std::string& path, const std::string& mode) +{ + m_path = path; + m_mode = mode; + m_fil = ::fopen(path.c_str(), mode.c_str()); + return m_fil ? true : false; +} + + +void File::fclose() +{ + if (m_fil) + ::fclose(m_fil); +} + + + +size_t File::fread(char *ptr, size_t size, size_t nmemb) +{ + return m_fil ? ::fread(ptr, size, nmemb, m_fil) : 0; +} + + +size_t File::fwrite(const char *ptr, size_t size, size_t nmemb) +{ + return m_fil ? ::fwrite(ptr, size, nmemb, m_fil) : 0; +} + + + +char *File::fgets(char *s, int size) +{ + return m_fil ? ::fgets(s, size, m_fil) : NULL; +} + + +void File::fprintf(char *format, ...) +{ + va_list ap; + va_start(ap, format); + vfprintf(m_fil, format, ap); + va_end(ap); +} + + +off_t File::size() +{ + struct stat st; + if (stat(m_path.c_str(), &st) == -1) + { + return 0; + } + return st.st_size; +} + + +bool File::eof() +{ + if (m_fil) + { + if (feof(m_fil)) + return true; + } + return false; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/common/SocketLib/File.h b/common/SocketLib/File.h new file mode 100644 index 000000000..a575d0973 --- /dev/null +++ b/common/SocketLib/File.h @@ -0,0 +1,76 @@ +/** \file File.h + ** \date 2005-04-25 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004,2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 _FILE_H +#define _FILE_H + +#include "IFile.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/** IFile implementation of a disk file. + \ingroup file */ +class File : public IFile +{ +public: + File(); + ~File(); + + bool fopen(const std::string&, const std::string&); + void fclose(); + + size_t fread(char *, size_t, size_t); + size_t fwrite(const char *, size_t, size_t); + + char *fgets(char *, int); + void fprintf(char *format, ...); + + off_t size(); + bool eof(); + +private: + File(const File& ) {} // copy constructor + File& operator=(const File& ) { return *this; } // assignment operator + + std::string m_path; + std::string m_mode; + FILE *m_fil; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _FILE_H diff --git a/common/SocketLib/HTTPSocket.cpp b/common/SocketLib/HTTPSocket.cpp new file mode 100644 index 000000000..9d0eaa5be --- /dev/null +++ b/common/SocketLib/HTTPSocket.cpp @@ -0,0 +1,366 @@ +/* EQEMu: Everquest Server Emulator + * + * This code originated from `C++ Sockets Library` referenced below. + * Taken and stripped/modified to remove dependancies on parts of + * the library which we are not using, and to suit other needs. + * 2006 - EQEMu Development Team (http://eqemulator.net) + * + * + */ + +/** \file HTTPSocket.cpp + ** \date 2004-04-06 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004,2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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. +*/ +#ifdef _WIN32 +#pragma warning(disable:4786) +#endif +#include "../debug.h" +#include +#include +#include +#include "Parse.h" +#include "HTTPSocket.h" +#include "../TCPConnection.h" +#include +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + + + +HTTPSocket::HTTPSocket(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort) +:TCPConnection(ID,in_socket,irIP,irPort) +,m_first(true) +,m_header(true) +,m_http_version("HTTP/1.0") +,m_request(false) +,m_response(false) +{ +} + + +HTTPSocket::~HTTPSocket() +{ +} + +/* + * eqemu stuff + */ + +bool HTTPSocket::ProcessReceivedData(char *errbuf) +{ + if (errbuf) + errbuf[0] = 0; + if (!recvbuf) + return true; + + char *buff=(char *)recvbuf; + unsigned long bufflen=recvbuf_used; + + while(1) { + if (m_header) { + char *ptr=(char *)memchr(buff,'\n',bufflen); + if (!ptr) + break; + int length=(ptr-buff)+1; + std::string line; + line.append(buff,length-2); + OnLine(line); + + buff+=length; + bufflen-=length; + } else { + OnData(buff,bufflen); + buff+=bufflen; + bufflen=0; + break; + } + } + + if (bufflen) { + memmove(recvbuf,buff,bufflen); + recvbuf_used=bufflen; + } else { + safe_delete_array(recvbuf); + } +} + +bool HTTPSocket::SendString(const char *str) { + return(TCPConnection::Send((const uchar *) str, strlen(str))); +} + +bool HTTPSocket::SendBuf(const char *dat, unsigned int len) { + return(TCPConnection::Send((const uchar *) dat, len)); +} + +/* + * /eqemu stuff + */ + +void HTTPSocket::OnLine(const std::string& line) +{ + if (m_first) + { + Parse pa(line); + std::string str = pa.getword(); + if (str.substr(0,4) == "HTTP") // response + { + m_http_version = str; + m_status = pa.getword(); + m_status_text = pa.getrest(); + m_response = true; + } + else // request + { + m_method = str; + m_url = pa.getword(); + size_t spl = m_url.find("?"); + if (spl != std::string::npos) + { + m_uri = m_url.substr(0,spl); + m_query_string = m_url.substr(spl + 1); + } + else + { + m_uri = m_url; + } + m_http_version = pa.getword(); + m_request = true; + } + m_first = false; + OnFirst(); + return; + } + if (!line.size()) + { +// SetLineProtocol(false); + m_header = false; + OnHeaderComplete(); + return; + } + Parse pa(line,":"); + std::string key = pa.getword(); + std::string value = pa.getrest(); + OnHeader(key,value); + /* If remote end tells us to keep connection alive, and we're operating + in http/1.1 mode (not http/1.0 mode), then we mark the socket to be + retained. */ +/* if (!strcasecmp(key.c_str(), "connection") && + !strcasecmp(value.c_str(), "keep-alive") ) + { + SetRetain(); + }*/ +} + + +void HTTPSocket::SendResponse() +{ + std::string msg; + msg = m_http_version + " " + m_status + " " + m_status_text + "\r\n"; + for (string_m::iterator it = m_response_header.begin(); it != m_response_header.end(); it++) + { + std::string key = (*it).first; + std::string val = (*it).second; + msg += key + ": " + val + "\r\n"; + } + msg += "\r\n"; + SendString( msg.c_str() ); +} + + +void HTTPSocket::AddResponseHeader(const std::string& header, const char *format, ...) +{ + static char slask[5000]; + va_list ap; + + va_start(ap, format); +#ifdef _WIN32 + vsprintf(slask, format, ap); +#else + vsnprintf(slask, 5000, format, ap); +#endif + va_end(ap); + + m_response_header[header] = slask; +} + + +void HTTPSocket::SendRequest() +{ + std::string msg; + msg = m_method + " " + m_url + " " + m_http_version + "\r\n"; + for (string_m::iterator it = m_response_header.begin(); it != m_response_header.end(); it++) + { + std::string key = (*it).first; + std::string val = (*it).second; + msg += key + ": " + val + "\r\n"; + } + msg += "\r\n"; + SendString( msg.c_str() ); +} + + +std::string HTTPSocket::MyUseragent() +{ + std::string version = "C++Sockets/"; +#ifdef _VERSION + version += _VERSION; +#endif + return version; +} + + +void HTTPSocket::Reset() +{ + m_first = true; + m_header = true; + m_request = false; + m_response = false; +// SetLineProtocol(true); + while (m_response_header.size()) + { + string_m::iterator it = m_response_header.begin(); + m_response_header.erase(it); + } + +} + + +const std::string& HTTPSocket::GetMethod() +{ + return m_method; +} + + +void HTTPSocket::SetMethod(const std::string& x) +{ + m_method = x; +} + + +const std::string& HTTPSocket::GetUrl() +{ + return m_url; +} + + +void HTTPSocket::SetUrl(const std::string& x) +{ + m_url = x; +} + + +const std::string& HTTPSocket::GetUri() +{ + return m_uri; +} + + +const std::string& HTTPSocket::GetQueryString() +{ + return m_query_string; +} + + +const std::string& HTTPSocket::GetHttpVersion() +{ + return m_http_version; +} + + +const std::string& HTTPSocket::GetStatus() +{ + return m_status; +} + + +const std::string& HTTPSocket::GetStatusText() +{ + return m_status_text; +} + + +bool HTTPSocket::IsRequest() +{ + return m_request; +} + + +bool HTTPSocket::IsResponse() +{ + return m_response; +} + + +void HTTPSocket::SetHttpVersion(const std::string& x) +{ + m_http_version = x; +} + + +void HTTPSocket::SetStatus(const std::string& num, const std::string& text) { + m_status = num; + m_status_text = text; +} + +void HTTPSocket::SetStatus(const std::string& x) +{ + m_status = x; +} + + +void HTTPSocket::SetStatusText(const std::string& x) +{ + m_status_text = x; +} + + +void HTTPSocket::AddResponseHeader(const std::string& x,const std::string& y) +{ + m_response_header[x] = y; +} + + +void HTTPSocket::SetUri(const std::string& x) +{ + m_uri = x; +} + +void HTTPSocket::SendResponse(const std::string& status_num, const std::string& status_text) { + SetStatus(status_num, status_text); + SendResponse(); +} + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/common/SocketLib/HTTPSocket.h b/common/SocketLib/HTTPSocket.h new file mode 100644 index 000000000..96f4cf389 --- /dev/null +++ b/common/SocketLib/HTTPSocket.h @@ -0,0 +1,137 @@ +/* EQEMu: Everquest Server Emulator + * + * This code originated from `C++ Sockets Library` referenced below. + * Taken and stripped/modified to remove dependancies on parts of + * the library which we are not using, and to suit other needs. + * 2006 - EQEMu Development Team (http://eqemulator.net) + * + * + */ + +/** \file HTTPSocket.h Class HTTPSocket definition. + ** \date 2004-04-06 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004,2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 _HTTPSOCKET_H +#define _HTTPSOCKET_H + +#include +#include +#include "../TCPConnection.h" + + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** \defgroup http HTTP Sockets */ +/** HTTP request/response base class. + \ingroup http */ +class HTTPSocket : public TCPConnection +{ + /** map to hold http header values. */ + typedef std::map string_m; +public: + HTTPSocket(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort); + virtual ~HTTPSocket(); + + void OnLine(const std::string& line); + + /** Callback executes when first line has been received. + GetMethod, GetUrl/GetUri, and GetHttpVersion are valid when this callback is executed. */ + virtual void OnFirst() = 0; + /** For each header line this callback is executed. + \param key Http header name + \param value Http header value */ + virtual void OnHeader(const std::string& key,const std::string& value) = 0; + /** Callback fires when all http headers have been received. */ + virtual void OnHeaderComplete() = 0; + /** Chunk of http body data recevied. */ + virtual void OnData(const char *,size_t) = 0; + + const std::string& GetMethod(); + void SetMethod(const std::string& x); + const std::string& GetUrl(); + void SetUrl(const std::string& x); + const std::string& GetUri(); + void SetUri(const std::string& x); + const std::string& GetQueryString(); + const std::string& GetHttpVersion(); + const std::string& GetStatus(); + const std::string& GetStatusText(); + bool IsRequest(); + bool IsResponse(); + + void SetHttpVersion(const std::string& x); + void SetStatus(const std::string& x); + void SetStatus(const std::string& num, const std::string& text); + void SetStatusText(const std::string& x); + void AddResponseHeader(const std::string& x,const std::string& y); + void AddResponseHeader(const std::string& x,const char *format, ...); + void SendResponse(); + void SendResponse(const std::string& status_num, const std::string& status_text); + void SendRequest(); + + /** Implement this to return your own User-agent string. */ + virtual std::string MyUseragent(); + +protected: + /** Reset state of socket to sucessfully implement keep-alive. */ + virtual void Reset(); + + //stubs for crap which used to be in our parent class (TcpSocket) + bool SendString(const char *str); + bool SendBuf(const char *dat, unsigned int len); + + virtual bool ProcessReceivedData(char* errbuf = 0); + +private: +// HTTPSocket& operator=(const HTTPSocket& ) { return *this; } + bool m_first; + bool m_header; + std::string m_line; + std::string m_method; + std::string m_url; + std::string m_uri; + std::string m_query_string; + std::string m_http_version; + std::string m_status; + std::string m_status_text; + bool m_request; + bool m_response; + string_m m_response_header; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _HTTPSOCKET_H diff --git a/common/SocketLib/HttpdCookies.cpp b/common/SocketLib/HttpdCookies.cpp new file mode 100644 index 000000000..0c0486688 --- /dev/null +++ b/common/SocketLib/HttpdCookies.cpp @@ -0,0 +1,250 @@ +/** \file HttpdCookies.cpp +*/ +/* +Copyright (C) 2003-2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 "../debug.h" +#ifdef _WIN32 +#pragma warning(disable:4786) +#endif +#include "Parse.h" +#include "Utility.h" +#include "HTTPSocket.h" +#include "HttpdCookies.h" +#include "../types.h" +#include +#include +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +HttpdCookies::HttpdCookies() +{ +} + +HttpdCookies::HttpdCookies(const std::string& s) +{ + Parse *pa = new Parse(s,";"); + + std::string slask = pa -> getword(); + while (slask.size()) + { + Parse *pa2 = new Parse(slask,"="); + std::string name = pa2 -> getword(); + std::string value = pa2 -> getword(); + delete pa2; + COOKIE *c = new COOKIE(name,value); + m_cookies.push_back(c); + // + slask = pa -> getword(); + } + delete pa; +} + +HttpdCookies::~HttpdCookies() +{ + for (cookie_v::iterator it = m_cookies.begin(); it != m_cookies.end(); it++) + { + COOKIE *c = *it; + delete c; + } +} + +bool HttpdCookies::getvalue(const std::string& name,std::string& buffer) //char *buffer,size_t length) +{ + for (cookie_v::iterator it = m_cookies.begin(); it != m_cookies.end(); it++) + { + COOKIE *c = *it; + if (!strcasecmp(c -> name.c_str(),name.c_str())) + { + buffer = c -> value; + return true; + } + } + buffer = ""; + return false; +} + +void HttpdCookies::replacevalue(const std::string& name,const std::string& value) +{ + COOKIE *c = NULL; + + for (cookie_v::iterator it = m_cookies.begin(); it != m_cookies.end(); it++) + { + c = *it; + if (!strcasecmp(c -> name.c_str(),name.c_str())) + break; + c = NULL; + } + + if (c) + { + c -> value = value; + } + else + { + c = new COOKIE(name,value); + m_cookies.push_back(c); + } +} + +void HttpdCookies::replacevalue(const std::string& name,long l) +{ + replacevalue(name, Utility::l2string(l)); +} + +void HttpdCookies::replacevalue(const std::string& name,int i) +{ + replacevalue(name, Utility::l2string(i)); +} + +size_t HttpdCookies::getlength(const std::string& name) +{ + COOKIE *c = NULL; + + for (cookie_v::iterator it = m_cookies.begin(); it != m_cookies.end(); it++) + { + c = *it; + if (!strcasecmp(c -> name.c_str(),name.c_str())) + break; + c = NULL; + } + return c ? c -> value.size() : 0; +} + +void HttpdCookies::setcookie(HTTPSocket *sock, const std::string& domain, const std::string& path, const std::string& name, const std::string& value) +{ + char *str = new char[name.size() + value.size() + domain.size() + path.size() + 100]; + + // set-cookie response + if (domain.size()) + { + sprintf(str, "%s=%s; domain=%s; path=%s; expires=%s", + name.c_str(), value.c_str(), + domain.c_str(), + path.c_str(), + expiredatetime().c_str()); + } + else + { + sprintf(str, "%s=%s; path=%s; expires=%s", + name.c_str(), value.c_str(), + path.c_str(), + expiredatetime().c_str()); + } + sock -> AddResponseHeader("Set-cookie", str); + delete[] str; + + replacevalue(name, value); +} + +void HttpdCookies::setcookie(HTTPSocket *sock, const std::string& domain, const std::string& path, const std::string& name, long value) +{ + char *str = new char[name.size() + domain.size() + path.size() + 100]; + char dt[80]; + + // set-cookie response + if (domain.size()) + { + sprintf(str, "%s=%ld; domain=%s; path=%s; expires=%s", + name.c_str(), value, + domain.c_str(), + path.c_str(), + expiredatetime().c_str()); + } + else + { + sprintf(str, "%s=%ld; path=%s; expires=%s", + name.c_str(), value, + path.c_str(), + expiredatetime().c_str()); + } + sock -> AddResponseHeader("Set-cookie", str); + delete[] str; + + sprintf(dt, "%ld", value); + replacevalue(name, dt); +} + +void HttpdCookies::setcookie(HTTPSocket *sock, const std::string& domain, const std::string& path, const std::string& name, int value) +{ + char *str = new char[name.size() + domain.size() + path.size() + 100]; + char dt[80]; + + // set-cookie response + if (domain.size()) + { + sprintf(str, "%s=%d; domain=%s; path=%s; expires=%s", + name.c_str(), value, + domain.c_str(), + path.c_str(), + expiredatetime().c_str()); + } + else + { + sprintf(str, "%s=%d; path=%s; expires=%s", + name.c_str(), value, + path.c_str(), + expiredatetime().c_str()); + } + sock -> AddResponseHeader("Set-cookie", str); + delete[] str; + + sprintf(dt, "%d", value); + replacevalue(name, dt); +} + + +const std::string& HttpdCookies::expiredatetime() +{ + time_t t = time(NULL); + struct tm * tp = gmtime(&t); + const char *days[7] = {"Sunday", "Monday", + "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; + const char *months[12] = {"Jan", "Feb", "Mar", "Apr", "May", + "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + char dt[100]; + + sprintf(dt, "%s, %02d-%s-%04d %02d:%02d:%02d GMT", + days[tp -> tm_wday], + tp -> tm_mday, + months[tp -> tm_mon], + tp -> tm_year + 1910, + tp -> tm_hour, + tp -> tm_min, + tp -> tm_sec); + m_date = dt; + return m_date; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/common/SocketLib/HttpdCookies.h b/common/SocketLib/HttpdCookies.h new file mode 100644 index 000000000..7dea224b6 --- /dev/null +++ b/common/SocketLib/HttpdCookies.h @@ -0,0 +1,91 @@ +/** \file HttpdCookies.h +*/ +/* +Copyright (C) 2003-2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 _COOKIES_H +#define _COOKIES_H + +#include +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +//! Store the cookies name/value pairs. + + + +//! Retrieve and manage cookies during a cgi call. +class HTTPSocket; + +/** HTTP Cookie parse/container class. +\sa HttpdSocket +\sa HttpdForm +\ingroup webserver */ +class HttpdCookies +{ + /** Name/value pair store struct. + \ingroup webserver */ + struct COOKIE + { + COOKIE(const std::string& n,const std::string& v) : name(n),value(v) {} + std::string name; + std::string value; + }; + /** list of key/value structs. */ + typedef std::list cookie_v; +public: + HttpdCookies(); + HttpdCookies(const std::string& query_string); + ~HttpdCookies(); + +// int getvalue(const std::string& ,char *,size_t); // (name, buffer, length) + bool getvalue(const std::string&,std::string&); + void replacevalue(const std::string& ,const std::string& ); + void replacevalue(const std::string& ,long); + void replacevalue(const std::string& ,int); + size_t getlength(const std::string& ); + void setcookie(HTTPSocket *,const std::string& d,const std::string& p,const std::string& c,const std::string& v); + void setcookie(HTTPSocket *,const std::string& d,const std::string& p,const std::string& c,long v); + void setcookie(HTTPSocket *,const std::string& d,const std::string& p,const std::string& c,int v); + const std::string& expiredatetime(); + + cookie_v& GetHttpdCookies() { return m_cookies; } + +private: + cookie_v m_cookies; + std::string m_date; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _COOKIES_H diff --git a/common/SocketLib/HttpdForm.cpp b/common/SocketLib/HttpdForm.cpp new file mode 100644 index 000000000..d0eece3f1 --- /dev/null +++ b/common/SocketLib/HttpdForm.cpp @@ -0,0 +1,621 @@ +/** \file HttpdForm.cpp - read stdin, parse cgi input + ** + ** Written: 1999-Feb-10 grymse@alhem.net + **/ + +/* +Copyright (C) 1999-2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 +#ifdef _WIN32 +#pragma warning(disable:4786) +#include +#endif +#include "socket_include.h" +#include "Parse.h" +#include "IFile.h" +#include "HttpdForm.h" +#include +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +HttpdForm::HttpdForm(IFile *infil) : raw(false) +{ + CGI *cgi = NULL; + char *c_t = getenv("CONTENT_TYPE"); + char *c_l = getenv("CONTENT_LENGTH"); + size_t extra = 2; + char name[200]; + + m_current = m_cgi.end(); + *name = 0; + + if (c_t && !strncmp(c_t, "multipart/form-data",19)) + { + Parse pa(c_t,";="); + char *tempcmp = NULL; + size_t tc = 0; + size_t l = 0; + std::string str = pa.getword(); + m_strBoundary = ""; + while (str.size()) + { + if (!strcmp(str.c_str(),"boundary")) + { + m_strBoundary = pa.getword(); + l = m_strBoundary.size(); + tempcmp = new char[l + extra]; + } + // + str = pa.getword(); + } + if (m_strBoundary.size()) + { + std::string content_type; + std::string current_name; + std::string current_filename; + char slask[200]; + infil -> fgets(slask, 200); + while (!infil -> eof()) + { + while (strlen(slask) && (slask[strlen(slask) - 1] == 13 || slask[strlen(slask) - 1] == 10)) + { + slask[strlen(slask) - 1] = 0; + } + content_type = ""; + current_name = ""; + current_filename = ""; + if ((strstr(slask,m_strBoundary.c_str()) || strstr(m_strBoundary.c_str(),slask)) && strcmp(slask, m_strBoundary.c_str())) + { + m_strBoundary = slask; + l = m_strBoundary.size(); + delete[] tempcmp; + tempcmp = new char[l + extra]; + } + if (!strcmp(slask, m_strBoundary.c_str())) + { + // Get headers until empty line + infil -> fgets(slask, 200); + while (strlen(slask) && (slask[strlen(slask) - 1] == 13 || slask[strlen(slask) - 1] == 10)) + { + slask[strlen(slask) - 1] = 0; + } + while (!infil -> eof() && *slask) + { + Parse pa(slask,";"); + std::string h = pa.getword(); + if (!strcasecmp(h.c_str(),"Content-type:")) + { + content_type = pa.getword(); + } + else + if (!strcasecmp(h.c_str(),"Content-Disposition:")) + { + h = pa.getword(); + if (!strcmp(h.c_str(),"form-data")) + { + pa.EnableQuote(true); + h = pa.getword(); + while (h.size()) + { + Parse pa2(slask,"="); + std::string name = pa2.getword(); + std::string h = pa2.getrest(); + if (!strcmp(name.c_str(),"name")) + { + if (h.size() && h[0] == '"') + { + current_name = h.substr(1, h.size() - 2); + } + else + { + current_name = h; + } + } + else + if (!strcmp(name.c_str(),"filename")) + { + if (h.size() && h[0] == '"') + { + current_filename = h.substr(1, h.size() - 2); + } + else + { + current_filename = h; + } + size_t x = 0; + for (size_t i = 0; i < current_filename.size(); i++) + { + if (current_filename[i] == '/' || current_filename[i] == '\\') + x = i + 1; + } + if (x) + { + current_filename = current_filename.substr(x); + } + } + h = pa.getword(); + } + } + } + // get next header value + infil -> fgets(slask, 200); + while (strlen(slask) && (slask[strlen(slask) - 1] == 13 || slask[strlen(slask) - 1] == 10)) + { + slask[strlen(slask) - 1] = 0; + } + } + // Read content, save...? + if (!current_filename.size()) // not a file + { + std::string val; + infil -> fgets(slask,1000); + while (!infil -> eof() && strncmp(slask,m_strBoundary.c_str(),m_strBoundary.size() )) + { + val += slask; + infil -> fgets(slask,1000); + } + // remove trailing cr/linefeed + while (val.size() && (val[val.size() - 1] == 13 || val[val.size() - 1] == 10)) + { + val = val.substr(0,val.size() - 1); + } + cgi = new CGI(current_name, val); + m_cgi.push_back(cgi); + } + else // current_filename.size() > 0 + { + // read until m_strBoundary... + FILE *fil; + int out = 0; + char c; + char fn[1000]; // where post'd file will be saved +#ifdef _WIN32 + { + char tmp_path[1000]; + ::GetTempPath(1000, tmp_path); + if (tmp_path[strlen(tmp_path) - 1] != '\\') + { + strcat(tmp_path, "\\"); + } + sprintf(fn,"%s%s",tmp_path,current_filename.c_str()); + } +#else + sprintf(fn,"/tmp/%s",current_filename.c_str()); +#endif + if ((fil = fopen(fn, "wb")) != NULL) + { + infil -> fread(&c,1,1); + while (!infil -> eof()) + { + if (out) + { + fwrite(&tempcmp[tc],1,1,fil); + } + tempcmp[tc] = c; + tc++; + if (tc >= l + extra) + { + tc = 0; + out = 1; + } + if (tc) + { + if (!strncmp(tempcmp + tc + extra, m_strBoundary.c_str(), l - tc) && + !strncmp(tempcmp, m_strBoundary.c_str() + l - tc, tc)) + { + break; + } + } + else + { + if (!strncmp(tempcmp + extra, m_strBoundary.c_str(), l)) + { + break; + } + } + infil -> fread(&c,1,1); + } + fclose(fil); + + cgi = new CGI(current_name,fn,fn); + m_cgi.push_back(cgi); + + strcpy(slask, m_strBoundary.c_str()); + infil -> fgets(slask + strlen(slask), 200); // next line + } + else + { + // couldn't open file + break; + } + } + } + else + { + // Probably '--' + break; + } + } // while (!infil -> eof()) + } // if (m_strBoundary) + if (tempcmp) + { + delete[] tempcmp; + } + } + else + { + int i = 0; + int cl = c_l ? atoi(c_l) : -1; + char c,chigh,clow; + char *slask = new char[8888]; + bool got_name = false; + m_current = m_cgi.end(); + + *name = 0; + + infil -> fread(&c,1,1); + cl--; + while (cl >= 0) + { + switch (c) + { + case '=': /* end of name */ + slask[i] = 0; + i = 0; + strcpy(name,slask); + got_name = true; + break; + case '&': /* end of value */ + slask[i] = 0; + i = 0; + if(got_name) { + got_name = false; + cgi = new CGI(name,slask); + m_cgi.push_back(cgi); + } else { + cgi = new CGI(slask,""); + m_cgi.push_back(cgi); + } + break; + case '+': /* space */ + slask[i++] = ' '; + break; + case '%': /* hex value */ + infil -> fread(&chigh,1,1); + cl--; + chigh -= 48; + chigh &= 0xff - 32; + if (chigh > 9) + chigh -= 7; + infil -> fread(&clow,1,1); + cl--; + clow -= 48; + clow &= 0xff - 32; + if (clow > 9) + clow -= 7; + slask[i++] = (char)(chigh * 16 + clow); + break; + default: /* just another char */ + slask[i++] = c; + break; + } + if(infil -> eof()) + break; + // + if (cl > 0) + { + infil -> fread(&c,1,1); + } + cl--; + } + slask[i] = 0; + i = 0; + if(got_name) { + cgi = new CGI(name,slask); + m_cgi.push_back(cgi); + } else { + cgi = new CGI(slask,""); + m_cgi.push_back(cgi); + } + delete[] slask; + } +} + + +// HttpdForm(buffer,l) -- request_method GET + +HttpdForm::HttpdForm(const std::string& buffer,size_t l) : raw(false) +{ + CGI *cgi = NULL; + char slask[8888]; + char name[200]; + int i = 0; + char c,chigh,clow; + bool got_name = false; + size_t ptr = 0; + + m_current = m_cgi.end(); + + *name = 0; + + ptr = 0; + while (ptr < l) + { + c = buffer[ptr++]; + switch (c) + { + case '=': /* end of name */ + slask[i] = 0; + i = 0; + got_name = true; + strcpy(name,slask); + break; + case '&': /* end of value */ + slask[i] = 0; + i = 0; + if(got_name) { + got_name = false; + cgi = new CGI(name,slask); + m_cgi.push_back(cgi); + } else { + cgi = new CGI(slask, ""); + m_cgi.push_back(cgi); + } + break; + case '+': /* space */ + slask[i++] = ' '; + break; + case '%': /* hex value */ + chigh = buffer[ptr++]; + chigh -= 48; + chigh &= 0xff - 32; + if (chigh > 9) + chigh -= 7; + clow = buffer[ptr++]; + clow -= 48; + clow &= 0xff - 32; + if (clow > 9) + clow -= 7; + slask[i++] = (char)(chigh * 16 + clow); + break; + default: /* just another char */ + slask[i++] = c; + break; + } + } + slask[i] = 0; + i = 0; + if(got_name) { + cgi = new CGI(name,slask); + m_cgi.push_back(cgi); + } else { + cgi = new CGI(slask, ""); + m_cgi.push_back(cgi); + } +} + + +HttpdForm::~HttpdForm() +{ + CGI *cgi = NULL; //,*tmp; + + for (cgi_v::iterator it = m_cgi.begin(); it != m_cgi.end(); it++) + { + cgi = *it; + delete cgi; + } +} + + +void HttpdForm::EnableRaw(bool b) +{ + raw = b; +} + + +void HttpdForm::strcpyval(std::string& v,const char *value) //,size_t len) +{ + v = ""; + for (size_t i = 0; i < strlen(value); i++) + { + if (value[i] == '<') + { + v += "<"; + } + else + if (value[i] == '>') + { + v += ">"; + } + else + if (value[i] == '&') + { + v += "&"; + } + else + { + v += value[i]; + } + } +} + + +bool HttpdForm::getfirst(std::string& n) //char *n,size_t len) +{ + m_current = m_cgi.begin(); + return getnext(n); +} + + +bool HttpdForm::getnext(std::string& n) //char *n,size_t len) +{ + if (m_current != m_cgi.end() ) + { + CGI *current = *m_current; + n = current -> name; + m_current++; + return true; + } + else + { + n = ""; + } + return false; +} + + +bool HttpdForm::getfirst(std::string& n,std::string& v) //char *n,size_t len,char *v,size_t vlen) +{ + m_current = m_cgi.begin(); + return getnext(n,v); +} + + +bool HttpdForm::getnext(std::string& n,std::string& v) //char *n,size_t len,char *v,size_t vlen) +{ + if (m_current != m_cgi.end() ) + { + CGI *current = *m_current; + n = current -> name; + if (raw) + { + v = current -> value; + } + else + { + strcpyval(v,current -> value.c_str()); + } + m_current++; + return true; + } + else + { + n = ""; + } + return false; +} + + +int HttpdForm::getvalue(const std::string& n,std::string& v) //char *v,size_t len) +{ + CGI *cgi = NULL; + int r = 0; + + for (cgi_v::iterator it = m_cgi.begin(); it != m_cgi.end(); it++) + { + cgi = *it; + if (cgi -> name == n) + break; + cgi = NULL; + } + if (cgi) + { + if (raw) + { + v = cgi -> value; + } + else + { + strcpyval(v,cgi -> value.c_str()); + } + r++; + } + else + { + v = ""; + } + + return r; +} + + +std::string HttpdForm::getvalue(const std::string& n) +{ + for (cgi_v::iterator it = m_cgi.begin(); it != m_cgi.end(); it++) + { + CGI *cgi = *it; + if (cgi -> name == n) + { + return cgi -> value; + } + } + return ""; +} + + +size_t HttpdForm::getlength(const std::string& n) +{ + CGI *cgi = NULL; + size_t l; + + for (cgi_v::iterator it = m_cgi.begin(); it != m_cgi.end(); it++) + { + cgi = *it; + if (cgi -> name == n) + break; + cgi = NULL; + } + l = cgi ? cgi -> value.size() : 0; + if (cgi && !raw) + { + for (size_t i = 0; i < cgi -> value.size(); i++) + { + switch (cgi -> value[i]) + { + case '<': // < + case '>': // > + l += 4; + break; + case '&': // & + l += 5; + break; + } + } + } + return l; +} + + +HttpdForm::cgi_v& HttpdForm::getbase() +{ + return m_cgi; +} + + +const std::string& HttpdForm::GetBoundary() +{ + return m_strBoundary; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/common/SocketLib/HttpdForm.h b/common/SocketLib/HttpdForm.h new file mode 100644 index 000000000..a8209d98b --- /dev/null +++ b/common/SocketLib/HttpdForm.h @@ -0,0 +1,115 @@ +/** \file HttpdForm.h - read stdin, parse cgi input + ** + ** Written: 1999-Feb-10 grymse@alhem.net + **/ + +/* +Copyright (C) 1999-2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 _FORM_H +#define _FORM_H + +#include +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class IFile; + +/** Parse/store a http query_string/form-data body. + \ingroup webserver */ +class HttpdForm +{ + /** + * Store the name/value pairs from a GET/POST operation. + * "name" does not have to be unique. + \ingroup webserver + */ + struct CGI + { + CGI(const std::string& n,const std::string& v) : name(n),value(v) {} + CGI(const std::string& n,const std::string& v,const std::string& p) : name(n),value(v),path(p) {} + std::string name; + std::string value; + std::string path; + }; + /** list of key/value pairs. */ + typedef std::list cgi_v; + +public: + /** + * Default constructor (used in POST operations). + * Input is read from stdin. Number of characters to read + * can be found in the environment variable CONTENT_LENGTH. + */ + HttpdForm(IFile *); + /** + * Another constructor (used in GET operations). + * Input is read from the environment variable QUERY_STRING. + * @param query_string The httpd server provided QUERY_STRING + * @param length Query string length. + */ + HttpdForm(const std::string& query_string,size_t length); + ~HttpdForm(); + + void EnableRaw(bool); + + void strcpyval(std::string&,const char *); //,size_t); + + /* get names */ + bool getfirst(std::string& n); //char *,size_t); + bool getnext(std::string& n); //char *,size_t); + + /* get names and values */ + bool getfirst(std::string& n,std::string& v); //char *,size_t,char *,size_t); + bool getnext(std::string& n,std::string& v); //char *,size_t,char *,size_t); + + /* get value */ + int getvalue(const std::string& ,std::string& ); //char *,size_t); + std::string getvalue(const std::string& ); + size_t getlength(const std::string& ); + cgi_v& getbase(); + + const std::string& GetBoundary(); + +private: + HttpdForm(const HttpdForm& ) {} + HttpdForm& operator=(const HttpdForm& ) { return *this; } + cgi_v m_cgi; + cgi_v::iterator m_current; + std::string m_strBoundary; + bool raw; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _FORM_H diff --git a/common/SocketLib/HttpdSocket.cpp b/common/SocketLib/HttpdSocket.cpp new file mode 100644 index 000000000..1f2dc894f --- /dev/null +++ b/common/SocketLib/HttpdSocket.cpp @@ -0,0 +1,355 @@ +/** \file HttpdSocket.cpp +*/ +/* +Copyright (C) 2001-2004,2005 Anders Hedstrom (grymse@alhem.net) + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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. +*/ +#ifdef _WIN32 +#pragma warning(disable:4786) +#endif +#include "../debug.h" +#include "Utility.h" +#include "HttpdCookies.h" +#include "HttpdForm.h" +#include "MemFile.h" +#include "HttpdSocket.h" +#include "../types.h" +#include +#include +#include +#include + +#define DEB(x) +/* +#define DEB(x) { \ + FILE *fil = fopen("httpdlog","at"); \ + if (!fil) \ + fil = fopen("httpdlog","wt"); \ + if (fil) { x; fclose(fil); } \ +} +*/ + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + + +// statics +int HttpdSocket::m_request_count = 0; +std::string HttpdSocket::m_start = ""; + + +HttpdSocket::HttpdSocket(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort) +: HTTPSocket(ID,in_socket,irIP,irPort) +,m_content_length(0) +,m_file(NULL) +,m_received(0) +,m_request_id(++m_request_count) +,m_cookies(NULL) +,m_form(NULL) +{ + m_http_date = datetime2httpdate(GetDate()); + if (!m_start.size()) + m_start = m_http_date; +} + + +HttpdSocket::~HttpdSocket() +{ + if (m_file) + { + delete m_file; + } + if (m_cookies) + delete m_cookies; + if (m_form) + delete m_form; +} + + +void HttpdSocket::OnFirst() +{ +// printf("Request: %s %s %s\n",GetMethod().c_str(),GetUrl().c_str(),GetHttpVersion().c_str()); +} + + +void HttpdSocket::OnHeader(const std::string& key,const std::string& value) +{ + if (!strcasecmp(key.c_str(),"content-length")) + { + m_content_length = atoi(value.c_str()); + m_content_length_str = value; + } + else + if (!strcasecmp(key.c_str(),"cookie")) + { + m_http_cookie = value; + } + else + if (!strcasecmp(key.c_str(),"content-type")) + { + m_content_type = value; + } + else + if (!strcasecmp(key.c_str(),"if-modified-since")) + { + m_if_modified_since = value; + } +} + + +void HttpdSocket::OnHeaderComplete() +{ + m_cookies = new HttpdCookies(m_http_cookie); + +#if (defined(SOLARIS8) || defined(SOLARIS)) + { + char slask[1000]; + if (GetMethod() == "GET") + { + sprintf(slask,"QUERY_STRING=%s", GetQueryString().c_str()); + putenv(slask); + } + sprintf(slask,"REQUEST_METHOD=%s", GetMethod().c_str()); + putenv(slask); + sprintf(slask,"HTTP_COOKIE=%s", m_http_cookie.c_str()); + putenv(slask); + sprintf(slask,"CONTENT_TYPE=%s", m_content_type.c_str()); + putenv(slask); + sprintf(slask,"CONTENT_LENGTH=%s", m_content_length_str.c_str()); + putenv(slask); + } +#elif defined _WIN32 + { + char slask[1000]; + if (GetMethod() == "GET") + { + sprintf(slask,"QUERY_STRING=%s", GetQueryString().c_str()); + _putenv(slask); + } + sprintf(slask,"REQUEST_METHOD=%s", GetMethod().c_str()); + _putenv(slask); + sprintf(slask,"HTTP_COOKIE=%s", m_http_cookie.c_str()); + _putenv(slask); + sprintf(slask,"CONTENT_TYPE=%s", m_content_type.c_str()); + _putenv(slask); + sprintf(slask,"CONTENT_LENGTH=%s", m_content_length_str.c_str()); + _putenv(slask); + } +#else + if (GetMethod() == "GET") + { + setenv("QUERY_STRING", GetQueryString().c_str(), 1); + } + setenv("REQUEST_METHOD", GetMethod().c_str(), 1); + setenv("HTTP_COOKIE", m_http_cookie.c_str(), 1); + setenv("CONTENT_TYPE", m_content_type.c_str(), 1); + setenv("CONTENT_LENGTH", m_content_length_str.c_str(), 1); +#endif + + if (GetMethod() == "POST") + { + m_file = new MemFile; + } + else + if (GetMethod() == "GET") + { + m_form = new HttpdForm(GetQueryString(), GetQueryString().size() ); + AddResponseHeader("Date", datetime2httpdate(GetDate()) ); + Exec(); + Reset(); // prepare for next request + } + else + { + AddResponseHeader("Date", GetHttpDate()); + AddResponseHeader("Connection", "close"); + SetStatus("405"); + SetStatusText("Method not allowed"); + SendResponse(); + } +} + + +void HttpdSocket::OnData(const char *p,size_t l) +{ +//printf("Got %d bytes: %.*s\n", l, l, p); + if (m_file) + { + m_file -> fwrite(p,1,l); + } + m_received += l; + if (m_received >= m_content_length && m_content_length) + { + // all done + if (m_file && !m_form) + { + m_form = new HttpdForm(m_file); + AddResponseHeader("Date", datetime2httpdate(GetDate()) ); + Exec(); + Reset(); // prepare for next request + } + } +} + + +void HttpdSocket::Send64(const std::string& str64, const std::string& type) +{ + Base64 bb; + + if (!strcasecmp(m_start.c_str(), m_if_modified_since.c_str())) + { + SetStatus("304"); + SetStatusText("Not Modified"); + SendResponse(); + } + else + { + size_t len = bb.decode_length(str64); + unsigned char *buf = new unsigned char[len]; + + SetStatus("200"); + SetStatusText("OK"); + + AddResponseHeader("Content-length", Utility::l2string( (long)len) ); + AddResponseHeader("Content-type", type ); + AddResponseHeader("Last-modified", m_start); + SendResponse(); + + bb.decode(str64, buf, len); + SendBuf( (char *)buf, len); + delete[] buf; + } +} + + +std::string HttpdSocket::datetime2httpdate(const std::string& dt) +{ + struct tm tp; + time_t t; + const char *days[] = { "Sun","Mon","Tue","Wed","Thu","Fri","Sat" }; + const char *months[] = { "Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec" }; + int i; + char s[40]; + +/* 1997-12-16 09:50:40 */ + + if (dt.size() == 19) + { + tp.tm_year = atoi(dt.substr(0,4).c_str()) - 1900; + i = atoi(dt.substr(5,2).c_str()) - 1; + tp.tm_mon = i >= 0 ? i : 0; + tp.tm_mday = atoi(dt.substr(8,2).c_str()); + tp.tm_hour = atoi(dt.substr(11,2).c_str()); + tp.tm_min = atoi(dt.substr(14,2).c_str()); + tp.tm_sec = atoi(dt.substr(17,2).c_str()); + tp.tm_wday = 0; + tp.tm_yday = 0; + tp.tm_isdst = 0; + t = mktime(&tp); + /*if (t == -1) + { + Handler().LogError(this, "datetime2httpdate", 0, "mktime() failed"); + }*/ + + sprintf(s,"%s, %02d %s %d %02d:%02d:%02d GMT", + days[tp.tm_wday], + tp.tm_mday, + months[tp.tm_mon], + tp.tm_year + 1900, + tp.tm_hour,tp.tm_min,tp.tm_sec); + } + else + { + *s = 0; + } + return s; +} + + +std::string HttpdSocket::GetDate() +{ + time_t t = time(NULL); + struct tm* tp = localtime(&t); + char slask[40]; + if (tp) + { + sprintf(slask,"%d-%02d-%02d %02d:%02d:%02d", + tp -> tm_year + 1900, + tp -> tm_mon + 1, + tp -> tm_mday, + tp -> tm_hour,tp -> tm_min,tp -> tm_sec); + } + else + { + *slask = 0; + } + return slask; +} + + +void HttpdSocket::Reset() +{ + HTTPSocket::Reset(); + m_content_length = 0; + if (m_file) + { + delete m_file; + m_file = NULL; + } + m_received = 0; + m_request_id = ++m_request_count; + if (m_cookies) + delete m_cookies; + m_cookies = NULL; + if (m_form) + delete m_form; + m_form = NULL; +} + + +const std::string& HttpdSocket::GetHttpDate() +{ + return m_http_date; +} + + +HttpdCookies *HttpdSocket::GetCookies() +{ + return m_cookies; +} + + +HttpdForm *HttpdSocket::GetHttpForm() +{ + return m_form; +} + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/common/SocketLib/HttpdSocket.h b/common/SocketLib/HttpdSocket.h new file mode 100644 index 000000000..33d0f4a1f --- /dev/null +++ b/common/SocketLib/HttpdSocket.h @@ -0,0 +1,99 @@ +/** \file HttpdSocket.h +*/ +/* +Copyright (C) 2001-2004,2005 Anders Hedstrom (grymse@alhem.net) + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 _HTTPDSOCKET_H +#define _HTTPDSOCKET_H + +#include "HTTPSocket.h" + +class TCPConnection; + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +class HttpdCookies; +class HttpdForm; +class IFile; + +/** \defgroup webserver Webserver framework */ +/** Web server socket framework. + \ingroup webserver */ +class HttpdSocket : public HTTPSocket +{ +public: + HttpdSocket(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort); + ~HttpdSocket(); + + void OnFirst(); + void OnHeader(const std::string& key,const std::string& value); + void OnHeaderComplete(); + void OnData(const char *,size_t); + + /** This method needs to be implemented with logic to produce + a response to an incoming request. */ + virtual void Exec() = 0; + /** Get current date in http rfc format. */ + const std::string& GetHttpDate(); + /** Get pointer to cookie class. */ + HttpdCookies *GetCookies(); + /** Get pointer to query string/form data class. */ + HttpdForm *GetHttpForm(); + +protected: + /** Decode and send a base64-encoded string. + \param str64 Base64-encoded string + \param type Mime type of content (content-type header) */ + void Send64(const std::string& str64, const std::string& type); + std::string datetime2httpdate(const std::string& dt); + std::string GetDate(); + void Reset(); + // headers + std::string m_http_cookie; + std::string m_content_type; + std::string m_content_length_str; + std::string m_if_modified_since; + +private: +static int m_request_count; +static std::string m_start; + size_t m_content_length; + IFile *m_file; + size_t m_received; + int m_request_id; + std::string m_http_date; + HttpdCookies *m_cookies; + HttpdForm *m_form; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _HTTPDSOCKET_H diff --git a/common/SocketLib/IFile.h b/common/SocketLib/IFile.h new file mode 100644 index 000000000..3eb2c59ba --- /dev/null +++ b/common/SocketLib/IFile.h @@ -0,0 +1,65 @@ +/** \file IFile.h + ** \date 2005-04-25 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004,2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 _IFILE_H +#define _IFILE_H + +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** \defgroup file File handling */ +/** Pure virtual file I/O interface. + \ingroup file */ +class IFile +{ +public: + virtual ~IFile() {} + + virtual bool fopen(const std::string&, const std::string&) = 0; + virtual void fclose() = 0; + + virtual size_t fread(char *, size_t, size_t) = 0; + virtual size_t fwrite(const char *, size_t, size_t) = 0; + + virtual char *fgets(char *, int) = 0; + virtual void fprintf(char *format, ...) = 0; + + virtual off_t size() = 0; + virtual bool eof() = 0; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _IFILE_H diff --git a/common/SocketLib/MemFile.cpp b/common/SocketLib/MemFile.cpp new file mode 100644 index 000000000..d52174798 --- /dev/null +++ b/common/SocketLib/MemFile.cpp @@ -0,0 +1,214 @@ +/** \file MemFile.cpp + ** \date 2005-04-25 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004,2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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. +*/ +#ifdef _WIN32 +#pragma warning(disable:4786) +#endif +#include +#include + +#include "MemFile.h" +#include +#include + +#ifdef _DEBUG +#define DEB(x) x +#else +#define DEB(x) +#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +std::map MemFile::m_files; + + +MemFile::MemFile() +:m_temporary(true) +,m_base(new block_t) +,m_current_read(m_base) +,m_current_write(m_base) +,m_read_ptr(0) +,m_write_ptr(0) +{ +} + + +MemFile::MemFile(const std::string& path) +:m_path(path) +,m_temporary(false) +,m_base(m_files[path]) +,m_current_read(NULL) +,m_current_write(NULL) +,m_read_ptr(0) +,m_write_ptr(0) +{ + if (!m_base) + { + m_base = new block_t; + m_files[path] = m_base; + } + m_current_read = m_base; + m_current_write = m_base; +} + + +MemFile::~MemFile() +{ + while (m_base && m_temporary) + { + block_t *p = m_base; + m_base = p -> next; + delete p; + } +} + + +bool MemFile::fopen(const std::string& path, const std::string& mode) +{ + return true; +} + + +void MemFile::fclose() +{ +} + + + +size_t MemFile::fread(char *ptr, size_t size, size_t nmemb) +{ + size_t p = m_read_ptr % BLOCKSIZE; + size_t sz = size * nmemb; + if (p + sz < BLOCKSIZE) + { +//printf("Read @ %d(%d). %d bytes. (%c)\n", m_read_ptr, p, sz, *(m_current_read -> data + p)); + memcpy(ptr, m_current_read -> data + p, sz); + m_read_ptr += sz; + } + else + { + size_t sz1 = BLOCKSIZE - p; + size_t sz2 = size - sz1; + memcpy(ptr, m_current_read -> data + p, sz1); + m_read_ptr += sz1; + if (m_current_read -> next) + { + m_current_read = m_current_read -> next; + memcpy(ptr + sz1, m_current_read -> data, sz2); + m_read_ptr += sz2; + } + else + { +DEB(printf("Read beyond available data\n");) + return sz1; + } + } + return sz; +} + + +size_t MemFile::fwrite(const char *ptr, size_t size, size_t nmemb) +{ + size_t p = m_write_ptr % BLOCKSIZE; + size_t sz = size * nmemb; + if (p + sz < BLOCKSIZE) + { +//printf("Write @ %d(%d). %d bytes.\n", m_write_ptr, p, sz); + memcpy(m_current_write -> data + p, ptr, sz); + m_write_ptr += sz; + } + else + { + size_t sz1 = BLOCKSIZE - p; + size_t sz2 = size - sz1; + memcpy(m_current_write -> data + p, ptr, sz1); + block_t *next = new block_t; + m_current_write -> next = next; + m_current_write = next; + memcpy(m_current_write -> data, ptr + sz1, sz2); + m_write_ptr += sz; + } + return sz; +} + + + +char *MemFile::fgets(char *s, int size) +{ + int n = 0; + while (n < size - 1 && !eof()) + { + char c; + fread(&c, 1, 1); + if (c == 10) + { + s[n] = 0; + return s; + } + s[n++] = c; + } + s[n] = 0; + return s; +} + + +void MemFile::fprintf(char *format, ...) +{ + va_list ap; + char tmp[BLOCKSIZE]; + va_start(ap, format); +#ifdef _WIN32 + vsprintf(tmp, format, ap); +#else + vsnprintf(tmp, BLOCKSIZE - 1, format, ap); +#endif + va_end(ap); + fwrite(tmp, 1, strlen(tmp)); +} + + +off_t MemFile::size() +{ + return (off_t)m_write_ptr; +} + + +bool MemFile::eof() +{ + return (m_read_ptr < m_write_ptr) ? false : true; +} + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/common/SocketLib/MemFile.h b/common/SocketLib/MemFile.h new file mode 100644 index 000000000..bd82abe01 --- /dev/null +++ b/common/SocketLib/MemFile.h @@ -0,0 +1,93 @@ +/** \file MemFile.h + ** \date 2005-04-25 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004,2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 _MEMFILE_H +#define _MEMFILE_H + +#include +#include "IFile.h" + +#define BLOCKSIZE 32768 + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/** Implements a memory file. + \ingroup file */ +class MemFile : public IFile +{ +public: + /** File block structure. + \ingroup file */ + struct block_t { + block_t() : next(NULL) {} + struct block_t *next; + char data[BLOCKSIZE]; + }; +public: + MemFile(); + MemFile(const std::string& path); + ~MemFile(); + + bool fopen(const std::string& path, const std::string& mode); + void fclose(); + + size_t fread(char *ptr, size_t size, size_t nmemb); + size_t fwrite(const char *ptr, size_t size, size_t nmemb); + + char *fgets(char *s, int size); + void fprintf(char *format, ...); + + off_t size(); + bool eof(); + +private: + MemFile(const MemFile& ) {} // copy constructor + MemFile& operator=(const MemFile& ) { return *this; } // assignment operator + +static std::map m_files; + std::string m_path; + bool m_temporary; + block_t *m_base; + block_t *m_current_read; + block_t *m_current_write; + size_t m_read_ptr; + size_t m_write_ptr; +}; + + + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _MEMFILE_H diff --git a/common/SocketLib/Mime.cpp b/common/SocketLib/Mime.cpp new file mode 100644 index 000000000..04944bb5b --- /dev/null +++ b/common/SocketLib/Mime.cpp @@ -0,0 +1,92 @@ +/** + ** File ......... Mime.cpp + ** Published .... 2004-07-13 + ** Author ....... grymse@alhem.net +**/ +/* +Copyright (C) 2004 Anders Hedstrom + +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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 + +#include "Parse.h" +#include "Mime.h" +#include + + + +Mime::Mime() { +} + +Mime::Mime(const std::string& filename) { + LoadMimeFile(filename); +} + +bool Mime::LoadMimeFile(const std::string& filename) { + FILE *fil; + if ((fil = fopen(filename.c_str(),"rt")) != NULL) { + char * slask = new char[1000]; + fgets(slask,1000,fil); + while (!feof(fil)) + { + while (strlen(slask) && (slask[strlen(slask) - 1] == 13 || slask[strlen(slask) - 1] == 10)) + { + slask[strlen(slask) - 1] = 0; + } + Parse pa(slask); + std::string mime_type = pa.getword(); + std::string ext = pa.getword(); + while (ext.size()) + { + m_mime[ext] = mime_type; + ext = pa.getword(); + } + // + fgets(slask,1000,fil); + } + delete[] slask; + fclose(fil); + return(true); + } + return(false); +} + + +Mime::~Mime() +{ +} + +void Mime::Clear() { + m_mime.clear(); +} + +std::string Mime::GetMimeFromFilename(const std::string &filename) const { + std::string::size_type pos = filename.find_last_of('.'); + if(pos == std::string::npos) + return(std::string("text/plain")); + return(GetMimeFromExtension(filename.substr(pos+1))); +} + +std::string Mime::GetMimeFromExtension(const std::string& ext) const { + mime_m::const_iterator res; + res = m_mime.find(ext); + if(res == m_mime.end()) + return(std::string("text/plain")); + + return res->second; +} + + diff --git a/common/SocketLib/Mime.h b/common/SocketLib/Mime.h new file mode 100644 index 000000000..337f9397a --- /dev/null +++ b/common/SocketLib/Mime.h @@ -0,0 +1,55 @@ +/** + ** File ......... Mime.h + ** Published .... 2004-07-13 + ** Author ....... grymse@alhem.net +**/ +/* +Copyright (C) 2004 Anders Hedstrom + +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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 _MIME_H +#define _MIME_H + +#include +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +class Mime { + typedef std::map mime_m; +public: + Mime(); + Mime(const std::string& mime_file); + ~Mime(); + + void Clear(); + bool LoadMimeFile(const std::string& mime_file); + + std::string GetMimeFromFilename(const std::string &filename) const; + std::string GetMimeFromExtension(const std::string &ext) const; + +private: + mime_m m_mime; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _MIME_H diff --git a/common/SocketLib/Parse.cpp b/common/SocketLib/Parse.cpp new file mode 100644 index 000000000..946c83e68 --- /dev/null +++ b/common/SocketLib/Parse.cpp @@ -0,0 +1,327 @@ +/** \file Parse.cpp - parse a string + ** + ** Written: 1999-Feb-10 grymse@alhem.net + **/ + +/* +Copyright (C) 1999-2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 +#include +#include + +#include "Parse.h" + +#ifdef _DEBUG +#define DEB(x) +#else +#define DEB(x) +#endif + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/* implementation of class Parse */ + +Parse::Parse() +:pa_the_str("") +,pa_splits("") +,pa_ord("") +,pa_the_ptr(0) +,pa_breakchar(0) +,pa_enable(0) +,pa_disable(0) +,pa_nospace(0) +,pa_quote(false) +{ +} + +Parse::Parse(const std::string&s) +:pa_the_str(s) +,pa_splits("") +,pa_ord("") +,pa_the_ptr(0) +,pa_breakchar(0) +,pa_enable(0) +,pa_disable(0) +,pa_nospace(0) +,pa_quote(false) +{ +} + +Parse::Parse(const std::string&s,const std::string&sp) +:pa_the_str(s) +,pa_splits(sp) +,pa_ord("") +,pa_the_ptr(0) +,pa_breakchar(0) +,pa_enable(0) +,pa_disable(0) +,pa_nospace(0) +,pa_quote(false) +{ +} + +Parse::Parse(const std::string&s,const std::string&sp,short nospace) +:pa_the_str(s) +,pa_splits(sp) +,pa_ord("") +,pa_the_ptr(0) +,pa_breakchar(0) +,pa_enable(0) +,pa_disable(0) +,pa_nospace(1) +,pa_quote(false) +{ +} + + +Parse::~Parse() +{ +} + +#define C ((pa_the_ptr + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +/***************************************************/ +/* interface of class Parse */ + +/** Splits a string whatever way you want. + \ingroup util */ +class Parse +{ +public: + Parse(); + Parse(const std::string&); + Parse(const std::string&,const std::string&); + Parse(const std::string&,const std::string&,short); + ~Parse(); + short issplit(char); + void getsplit(void); + void getsplit(std::string&); + std::string getword(void); + void getword(std::string&); + void getword(std::string&,std::string&,int); + std::string getrest(); + void getrest(std::string&); + long getvalue(void); + void setbreak(char); + int getwordlen(void); + int getrestlen(void); + void enablebreak(char c) { + pa_enable = c; + } + void disablebreak(char c) { + pa_disable = c; + } + void getline(void); + void getline(std::string&); + size_t getptr(void) { return pa_the_ptr; } + void EnableQuote(bool b) { pa_quote = b; } + +private: + std::string pa_the_str; + std::string pa_splits; + std::string pa_ord; + size_t pa_the_ptr; + char pa_breakchar; + char pa_enable; + char pa_disable; + short pa_nospace; + bool pa_quote; +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _PARSE_H diff --git a/common/SocketLib/README.eqemu b/common/SocketLib/README.eqemu new file mode 100644 index 000000000..4d14b5332 --- /dev/null +++ b/common/SocketLib/README.eqemu @@ -0,0 +1,5 @@ +EQEmu took this code from `C++ Sockets Library` +http://www.alhem.net/Sockets/ +and integrated it into our world server. We did not care for the actual +socket code (didnt work on windows) so we scrapped all of it, and just +used the HTTP framework code. diff --git a/common/SocketLib/README.macosx b/common/SocketLib/README.macosx new file mode 100644 index 000000000..ee23d974a --- /dev/null +++ b/common/SocketLib/README.macosx @@ -0,0 +1,5 @@ +Find uuid.h here + http://www.die.net/doc/linux/include/uuid/uuid.h +or here + http://www.thedna.net/uuid.h + diff --git a/common/SocketLib/Utility.cpp b/common/SocketLib/Utility.cpp new file mode 100644 index 000000000..48c20d953 --- /dev/null +++ b/common/SocketLib/Utility.cpp @@ -0,0 +1,169 @@ +/** \file Utility.cpp + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004,2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 "Utility.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + + +std::string Utility::base64(const std::string& str_in) +{ + std::string str; + Base64 m_b; + m_b.encode(str_in, str, false); // , false == do not add cr/lf + return str; +} + + +std::string Utility::base64d(const std::string& str_in) +{ + std::string str; + Base64 m_b; + m_b.decode(str_in, str); + return str; +} + + +std::string Utility::l2string(long l) +{ + std::string str; + char tmp[100]; + sprintf(tmp,"%ld",l); + str = tmp; + return str; +} + + +std::string Utility::bigint2string(uint64_t l) +{ + std::string str; + uint64_t tmp = l; + while (tmp) + { + uint64_t a = tmp % 10; + str = (char)(a + 48) + str; + tmp /= 10; + } + if (!str.size()) + { + str = "0"; + } + return str; +} + + +uint64_t Utility::atoi64(const std::string& str) +{ + uint64_t l = 0; + for (size_t i = 0; i < str.size(); i++) + { + l = l * 10 + str[i] - 48; + } + return l; +} + + +unsigned int Utility::hex2unsigned(const std::string& str) +{ + unsigned int r = 0; + for (size_t i = 0; i < str.size(); i++) + { + r = r * 16 + str[i] - 48 - ((str[i] >= 'A') ? 7 : 0) - ((str[i] >= 'a') ? 32 : 0); + } + return r; +} + + +/* +* Encode string per RFC1738 URL encoding rules +* tnx rstaveley +*/ +std::string Utility::rfc1738_encode(const std::string& src) +{ +static char hex[] = "0123456789ABCDEF"; + std::string dst; + for (size_t i = 0; i < src.size(); i++) + { + if (isalnum(src[i])) + { + dst += src[i]; + } + else + if (src[i] == ' ') + { + dst += '+'; + } + else + { + dst += '%'; + dst += hex[src[i] / 16]; + dst += hex[src[i] % 16]; + } + } + return dst; +} // rfc1738_encode + + +/* +* Decode string per RFC1738 URL encoding rules +* tnx rstaveley +*/ +std::string Utility::rfc1738_decode(const std::string& src) +{ + std::string dst; + for (size_t i = 0; i < src.size(); i++) + { + if (src[i] == '%' && isxdigit(src[i + 1]) && isxdigit(src[i + 2])) + { + char c1 = src[++i]; + char c2 = src[++i]; + c1 = c1 - 48 - ((c1 >= 'A') ? 7 : 0) - ((c1 >= 'a') ? 32 : 0); + c2 = c2 - 48 - ((c2 >= 'A') ? 7 : 0) - ((c2 >= 'a') ? 32 : 0); + dst += (char)(c1 * 16 + c2); + } + else + if (src[i] == '+') + { + dst += ' '; + } + else + { + dst += src[i]; + } + } + return dst; +} // rfc1738_decode + + +#ifdef SOCKETS_NAMESPACE +} +#endif + diff --git a/common/SocketLib/Utility.h b/common/SocketLib/Utility.h new file mode 100644 index 000000000..1100a99e2 --- /dev/null +++ b/common/SocketLib/Utility.h @@ -0,0 +1,70 @@ +/** \file Utility.h + ** \date 2004-02-13 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004,2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 _UTILITY_H +#define _UTILITY_H + +#include +#ifdef _WIN32 +typedef unsigned __int64 uint64_t; +#else +#include +#ifdef SOLARIS +# include +#else +# include +#endif +#endif +#include "Base64.h" + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +/** Conversion utilities. + \ingroup util */ +class Utility +{ +public: + static std::string base64(const std::string& str_in); + static std::string base64d(const std::string& str_in); + static std::string l2string(long l); + static std::string bigint2string(uint64_t l); + static uint64_t atoi64(const std::string& str); + static unsigned int hex2unsigned(const std::string& str); + static std::string rfc1738_encode(const std::string& src); + static std::string rfc1738_decode(const std::string& src); +}; + + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // _UTILITY_H diff --git a/common/SocketLib/gpl.txt b/common/SocketLib/gpl.txt new file mode 100644 index 000000000..5b6e7c66c --- /dev/null +++ b/common/SocketLib/gpl.txt @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/common/SocketLib/socket_include.cpp b/common/SocketLib/socket_include.cpp new file mode 100644 index 000000000..353206dcc --- /dev/null +++ b/common/SocketLib/socket_include.cpp @@ -0,0 +1,87 @@ +/** \file socket_include.cpp + ** \date 2004-11-28 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004,2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 + +// only to be included in win32 projects +const char *StrError(int x) +{ +static char tmp[100]; + switch (x) + { + case 10004: return "Interrupted function call."; + case 10013: return "Permission denied."; + case 10014: return "Bad address."; + case 10022: return "Invalid argument."; + case 10024: return "Too many open files."; + case 10035: return "Resource temporarily unavailable."; + case 10036: return "Operation now in progress."; + case 10037: return "Operation already in progress."; + case 10038: return "Socket operation on nonsocket."; + case 10039: return "Destination address required."; + case 10040: return "Message too long."; + case 10041: return "Protocol wrong type for socket."; + case 10042: return "Bad protocol option."; + case 10043: return "Protocol not supported."; + case 10044: return "Socket type not supported."; + case 10045: return "Operation not supported."; + case 10046: return "Protocol family not supported."; + case 10047: return "Address family not supported by protocol family."; + case 10048: return "Address already in use."; + case 10049: return "Cannot assign requested address."; + case 10050: return "Network is down."; + case 10051: return "Network is unreachable."; + case 10052: return "Network dropped connection on reset."; + case 10053: return "Software caused connection abort."; + case 10054: return "Connection reset by peer."; + case 10055: return "No buffer space available."; + case 10056: return "Socket is already connected."; + case 10057: return "Socket is not connected."; + case 10058: return "Cannot send after socket shutdown."; + case 10060: return "Connection timed out."; + case 10061: return "Connection refused."; + case 10064: return "Host is down."; + case 10065: return "No route to host."; + case 10067: return "Too many processes."; + case 10091: return "Network subsystem is unavailable."; + case 10092: return "Winsock.dll version out of range."; + case 10093: return "Successful WSAStartup not yet performed."; + case 10101: return "Graceful shutdown in progress."; + case 10109: return "Class type not found."; + case 11001: return "Host not found."; + case 11002: return "Nonauthoritative host not found."; + case 11003: return "This is a nonrecoverable error."; + case 11004: return "Valid name, no data record of requested type."; + + default: + break; + } + sprintf(tmp, "Winsock error code: %d", x); + return tmp; +} diff --git a/common/SocketLib/socket_include.h b/common/SocketLib/socket_include.h new file mode 100644 index 000000000..20bc39370 --- /dev/null +++ b/common/SocketLib/socket_include.h @@ -0,0 +1,218 @@ +/** \file socket_include.h + ** \date 2005-04-12 + ** \author grymse@alhem.net +**/ +/* +Copyright (C) 2004,2005 Anders Hedstrom + +This library is made available under the terms of the GNU GPL. + +If you would like to use this library in a closed-source application, +a separate license agreement is available. For information about +the closed-source license agreement for the C++ sockets library, +please visit http://www.alhem.net/Sockets/license.html and/or +email license@alhem.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; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; 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 _SOCKET_INCLUDE_H +#define _SOCKET_INCLUDE_H + +#if (defined(__unix__) || defined(unix)) && !defined(USG) +#include +#endif + + +#ifndef _WIN32 +// ---------------------------------------- +// common unix includes / defines +#include +#include +#include +#include +#include +#include +#include + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +#define Errno errno +#define StrError strerror + +// WIN32 adapt +#define closesocket close +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +typedef int SOCKET; + +#ifndef INADDR_NONE +#define INADDR_NONE ((unsigned long) -1) +#endif // INADDR_NONE + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#endif // !_WIN32 + + +// ---------------------------------------- +// Generic +#ifndef SOL_IP +#define SOL_IP IPPROTO_IP +#endif + + +// ---------------------------------------- +// OS specific adaptions + +#ifdef SOLARIS +// ---------------------------------------- +// Solaris +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +typedef unsigned short port_t; +#ifdef SOCKETS_NAMESPACE +} +#endif + +#define s6_addr16 _S6_un._S6_u8 +#define MSG_NOSIGNAL 0 + +#elif defined __FreeBSD__ +// ---------------------------------------- +// FreeBSD +# if __FreeBSD_version >= 400014 +# define s6_addr16 __u6_addr.__u6_addr16 +# if !defined(MSG_NOSIGNAL) +# define MSG_NOSIGNAL 0 +# endif +# include +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +typedef in_addr_t ipaddr_t; +typedef in_port_t port_t; +#ifdef SOCKETS_NAMESPACE +} +#endif + +# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +# else +# error FreeBSD versions prior to 400014 does not support ipv6 +# endif + +#elif defined MACOSX +// ---------------------------------------- +// Mac OS X +#include +#include +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +typedef unsigned long ipaddr_t; +#ifdef SOCKETS_NAMESPACE +} +#endif + +#define s6_addr16 __u6_addr.__u6_addr16 +#define MSG_NOSIGNAL 0 // oops - thanks Derek +#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP + +#elif defined _WIN32 +// ---------------------------------------- +// Win32 +#pragma comment(lib, "wsock32.lib") +#define strcasecmp _stricmp + +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +typedef unsigned long ipaddr_t; +typedef unsigned short port_t; +typedef int socklen_t; +#ifdef SOCKETS_NAMESPACE +} +#endif + +#define MSG_NOSIGNAL 0 +#define SHUT_RDWR 2 + +// 1.8.6: define FD_SETSIZE to something bigger than 64 if there are a lot of +// simultaneous connections (must be done before including winsock.h) +//#define FD_SETSIZE 1024 +#include + +#define Errno WSAGetLastError() +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +const char *StrError(int x); + +// class WSAInitializer is a part of the Socket class (on win32) +// as a static instance - so whenever an application uses a Socket, +// winsock is initialized +class WSAInitializer // Winsock Initializer +{ +public: + WSAInitializer() { + if (WSAStartup(0x101,&m_wsadata)) + { + exit(-1); + } + } + ~WSAInitializer() { + WSACleanup(); + } +private: + WSADATA m_wsadata; +}; + +#ifdef SOCKETS_NAMESPACE +} +#endif + +#else +// ---------------------------------------- +// LINUX +#ifdef SOCKETS_NAMESPACE +namespace SOCKETS_NAMESPACE { +#endif + +typedef unsigned long ipaddr_t; +typedef unsigned short port_t; +#ifdef SOCKETS_NAMESPACE +} +#endif + + +#endif + +#ifdef _THREADSAFE_SOCKETS +#include "Mutex.h" +#include "Lock.h" +#endif + +#endif // _SOCKET_INCLUDE_H diff --git a/common/StackWalker/StackWalker.cpp b/common/StackWalker/StackWalker.cpp new file mode 100644 index 000000000..1a6659c97 --- /dev/null +++ b/common/StackWalker/StackWalker.cpp @@ -0,0 +1,1222 @@ +/********************************************************************** + * + * StackWalker.cpp + * + * + * History: + * 2005-07-27 v1 - First public release on http://www.codeproject.com/ + * http://www.codeproject.com/threads/StackWalker.asp + * 2005-07-28 v2 - Changed the params of the constructor and ShowCallstack + * (to simplify the usage) + * 2005-08-01 v3 - Changed to use 'CONTEXT_FULL' instead of CONTEXT_ALL + * (should also be enough) + * - Changed to compile correctly with the PSDK of VC7.0 + * (GetFileVersionInfoSizeA and GetFileVersionInfoA is wrongly defined: + * it uses LPSTR instead of LPCSTR as first paremeter) + * - Added declarations to support VC5/6 without using 'dbghelp.h' + * - Added a 'pUserData' member to the ShowCallstack function and the + * PReadProcessMemoryRoutine declaration (to pass some user-defined data, + * which can be used in the readMemoryFunction-callback) + * 2005-08-02 v4 - OnSymInit now also outputs the OS-Version by default + * - Added example for doing an exception-callstack-walking in main.cpp + * (thanks to owillebo: http://www.codeproject.com/script/profile/whos_who.asp?id=536268) + * 2005-08-05 v5 - Removed most Lint (http://www.gimpel.com/) errors... thanks to Okko Willeboordse! + * + **********************************************************************/ +#ifdef _WINDOWS +#include +#include +#include +#pragma comment(lib, "version.lib") // for "VerQueryValue" + +#include "StackWalker.h" + + +// If VC7 and later, then use the shipped 'dbghelp.h'-file +#if _MSC_VER >= 1300 +#include +#else +// inline the important dbghelp.h-declarations... +typedef enum { + SymNone = 0, + SymCoff, + SymCv, + SymPdb, + SymExport, + SymDeferred, + SymSym, + SymDia, + SymVirtual, + NumSymTypes +} SYM_TYPE; +typedef struct _IMAGEHLP_LINE64 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_LINE64) + PVOID Key; // internal + DWORD LineNumber; // line number in file + PCHAR FileName; // full filename + DWORD64 Address; // first instruction of line +} IMAGEHLP_LINE64, *PIMAGEHLP_LINE64; +typedef struct _IMAGEHLP_MODULE64 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + CHAR LoadedImageName[256]; // symbol file name +} IMAGEHLP_MODULE64, *PIMAGEHLP_MODULE64; +typedef struct _IMAGEHLP_SYMBOL64 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_SYMBOL64) + DWORD64 Address; // virtual address including dll base address + DWORD Size; // estimated size of symbol, can be zero + DWORD Flags; // info about the symbols, see the SYMF defines + DWORD MaxNameLength; // maximum size of symbol name in 'Name' + CHAR Name[1]; // symbol name (null terminated string) +} IMAGEHLP_SYMBOL64, *PIMAGEHLP_SYMBOL64; +typedef enum { + AddrMode1616, + AddrMode1632, + AddrModeReal, + AddrModeFlat +} ADDRESS_MODE; +typedef struct _tagADDRESS64 { + DWORD64 Offset; + WORD Segment; + ADDRESS_MODE Mode; +} ADDRESS64, *LPADDRESS64; +typedef struct _KDHELP64 { + DWORD64 Thread; + DWORD ThCallbackStack; + DWORD ThCallbackBStore; + DWORD NextCallback; + DWORD FramePointer; + DWORD64 KiCallUserMode; + DWORD64 KeUserCallbackDispatcher; + DWORD64 SystemRangeStart; + DWORD64 Reserved[8]; +} KDHELP64, *PKDHELP64; +typedef struct _tagSTACKFRAME64 { + ADDRESS64 AddrPC; // program counter + ADDRESS64 AddrReturn; // return address + ADDRESS64 AddrFrame; // frame pointer + ADDRESS64 AddrStack; // stack pointer + ADDRESS64 AddrBStore; // backing store pointer + PVOID FuncTableEntry; // pointer to pdata/fpo or NULL + DWORD64 Params[4]; // possible arguments to the function + BOOL Far; // WOW far call + BOOL Virtual; // is this a virtual frame? + DWORD64 Reserved[3]; + KDHELP64 KdHelp; +} STACKFRAME64, *LPSTACKFRAME64; +typedef +BOOL +(__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64)( + HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead + ); +typedef +PVOID +(__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64)( + HANDLE hProcess, + DWORD64 AddrBase + ); +typedef +DWORD64 +(__stdcall *PGET_MODULE_BASE_ROUTINE64)( + HANDLE hProcess, + DWORD64 Address + ); +typedef +DWORD64 +(__stdcall *PTRANSLATE_ADDRESS_ROUTINE64)( + HANDLE hProcess, + HANDLE hThread, + LPADDRESS64 lpaddr + ); +#define SYMOPT_CASE_INSENSITIVE 0x00000001 +#define SYMOPT_UNDNAME 0x00000002 +#define SYMOPT_DEFERRED_LOADS 0x00000004 +#define SYMOPT_NO_CPP 0x00000008 +#define SYMOPT_LOAD_LINES 0x00000010 +#define SYMOPT_OMAP_FIND_NEAREST 0x00000020 +#define SYMOPT_LOAD_ANYTHING 0x00000040 +#define SYMOPT_IGNORE_CVREC 0x00000080 +#define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100 +#define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200 +#define SYMOPT_EXACT_SYMBOLS 0x00000400 +#define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800 +#define SYMOPT_IGNORE_NT_SYMPATH 0x00001000 +#define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000 +#define SYMOPT_PUBLICS_ONLY 0x00004000 +#define SYMOPT_NO_PUBLICS 0x00008000 +#define SYMOPT_AUTO_PUBLICS 0x00010000 +#define SYMOPT_NO_IMAGE_SEARCH 0x00020000 +#define SYMOPT_SECURE 0x00040000 +#define SYMOPT_DEBUG 0x80000000 +#define UNDNAME_COMPLETE (0x0000) // Enable full undecoration +#define UNDNAME_NAME_ONLY (0x1000) // Crack only the name for primary declaration; +#endif // _MSC_VER < 1300 + +// Some missing defines (for VC5/6): +#ifndef INVALID_FILE_ATTRIBUTES +#define INVALID_FILE_ATTRIBUTES ((DWORD)-1) +#endif + + +// secure-CRT_functions are only available starting with VC8 +#if _MSC_VER < 1400 +#define strcpy_s strcpy +#define strcat_s(dst, len, src) strcat(dst, src) +#define _snprintf_s _snprintf +#define _tcscat_s _tcscat +#endif + +// Normally it should be enough to use 'CONTEXT_FULL' (better would be 'CONTEXT_ALL') +#define USED_CONTEXT_FLAGS CONTEXT_FULL + + +class StackWalkerInternal +{ +public: + StackWalkerInternal(StackWalker *parent, HANDLE hProcess) + { + m_parent = parent; + m_hDbhHelp = NULL; + pSC = NULL; + m_hProcess = hProcess; + m_szSymPath = NULL; + pSFTA = NULL; + pSGLFA = NULL; + pSGMB = NULL; + pSGMI = NULL; + pSGO = NULL; + pSGSFA = NULL; + pSI = NULL; + pSLM = NULL; + pSSO = NULL; + pSW = NULL; + pUDSN = NULL; + pSGSP = NULL; + } + ~StackWalkerInternal() + { + if (pSC != NULL) + pSC(m_hProcess); // SymCleanup + if (m_hDbhHelp != NULL) + FreeLibrary(m_hDbhHelp); + m_hDbhHelp = NULL; + m_parent = NULL; + if(m_szSymPath != NULL) + free(m_szSymPath); + m_szSymPath = NULL; + } + BOOL Init(LPCSTR szSymPath) + { + if (m_parent == NULL) + return FALSE; + // Dynamically load the Entry-Points for dbghelp.dll: + // First try to load the newsest one from + TCHAR szTemp[4096]; + // But before wqe do this, we first check if the ".local" file exists + if (GetModuleFileName(NULL, szTemp, 4096) > 0) + { + _tcscat_s(szTemp, _T(".local")); + if (GetFileAttributes(szTemp) == INVALID_FILE_ATTRIBUTES) + { + // ".local" file does not exist, so we can try to load the dbghelp.dll from the "Debugging Tools for Windows" + if (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows\\dbghelp.dll")); + // now check if the file exists: + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } + // Still not found? Then try to load the 64-Bit version: + if ( (m_hDbhHelp == NULL) && (GetEnvironmentVariable(_T("ProgramFiles"), szTemp, 4096) > 0) ) + { + _tcscat_s(szTemp, _T("\\Debugging Tools for Windows 64-Bit\\dbghelp.dll")); + if (GetFileAttributes(szTemp) != INVALID_FILE_ATTRIBUTES) + { + m_hDbhHelp = LoadLibrary(szTemp); + } + } + } + } + if (m_hDbhHelp == NULL) // if not already loaded, try to load a default-one + m_hDbhHelp = LoadLibrary( _T("dbghelp.dll") ); + if (m_hDbhHelp == NULL) + return FALSE; + pSI = (tSI) GetProcAddress(m_hDbhHelp, "SymInitialize" ); + pSC = (tSC) GetProcAddress(m_hDbhHelp, "SymCleanup" ); + + pSW = (tSW) GetProcAddress(m_hDbhHelp, "StackWalk64" ); + pSGO = (tSGO) GetProcAddress(m_hDbhHelp, "SymGetOptions" ); + pSSO = (tSSO) GetProcAddress(m_hDbhHelp, "SymSetOptions" ); + + pSFTA = (tSFTA) GetProcAddress(m_hDbhHelp, "SymFunctionTableAccess64" ); + pSGLFA = (tSGLFA) GetProcAddress(m_hDbhHelp, "SymGetLineFromAddr64" ); + pSGMB = (tSGMB) GetProcAddress(m_hDbhHelp, "SymGetModuleBase64" ); + pSGMI = (tSGMI) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" ); + //pSGMI_V3 = (tSGMI_V3) GetProcAddress(m_hDbhHelp, "SymGetModuleInfo64" ); + pSGSFA = (tSGSFA) GetProcAddress(m_hDbhHelp, "SymGetSymFromAddr64" ); + pUDSN = (tUDSN) GetProcAddress(m_hDbhHelp, "UnDecorateSymbolName" ); + pSLM = (tSLM) GetProcAddress(m_hDbhHelp, "SymLoadModule64" ); + pSGSP =(tSGSP) GetProcAddress(m_hDbhHelp, "SymGetSearchPath" ); + + if ( pSC == NULL || pSFTA == NULL || pSGMB == NULL || pSGMI == NULL || + pSGO == NULL || pSGSFA == NULL || pSI == NULL || pSSO == NULL || + pSW == NULL || pUDSN == NULL || pSLM == NULL ) + { + FreeLibrary(m_hDbhHelp); + m_hDbhHelp = NULL; + pSC = NULL; + return FALSE; + } + + // SymInitialize + if (szSymPath != NULL) + m_szSymPath = _strdup(szSymPath); + if (this->pSI(m_hProcess, m_szSymPath, FALSE) == FALSE) + this->m_parent->OnDbgHelpErr("SymInitialize", GetLastError(), 0); + + DWORD symOptions = this->pSGO(); // SymGetOptions + symOptions |= SYMOPT_LOAD_LINES; + symOptions |= SYMOPT_FAIL_CRITICAL_ERRORS; + //symOptions |= SYMOPT_NO_PROMPTS; + // SymSetOptions + symOptions = this->pSSO(symOptions); + + char buf[StackWalker::STACKWALK_MAX_NAMELEN] = {0}; + if (this->pSGSP != NULL) + { + if (this->pSGSP(m_hProcess, buf, StackWalker::STACKWALK_MAX_NAMELEN) == FALSE) + this->m_parent->OnDbgHelpErr("SymGetSearchPath", GetLastError(), 0); + } + char szUserName[1024] = {0}; + DWORD dwSize = 1024; + GetUserNameA(szUserName, &dwSize); + this->m_parent->OnSymInit(buf, symOptions, szUserName); + + return TRUE; + } + + StackWalker *m_parent; + + HMODULE m_hDbhHelp; + HANDLE m_hProcess; + LPSTR m_szSymPath; + +/*typedef struct IMAGEHLP_MODULE64_V3 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + // new elements: 07-Jun-2002 + CHAR LoadedImageName[256]; // symbol file name + CHAR LoadedPdbName[256]; // pdb file name + DWORD CVSig; // Signature of the CV record in the debug directories + CHAR CVData[MAX_PATH * 3]; // Contents of the CV record + DWORD PdbSig; // Signature of PDB + GUID PdbSig70; // Signature of PDB (VC 7 and up) + DWORD PdbAge; // DBI age of pdb + BOOL PdbUnmatched; // loaded an unmatched pdb + BOOL DbgUnmatched; // loaded an unmatched dbg + BOOL LineNumbers; // we have line number information + BOOL GlobalSymbols; // we have internal symbol information + BOOL TypeInfo; // we have type information + // new elements: 17-Dec-2003 + BOOL SourceIndexed; // pdb supports source server + BOOL Publics; // contains public symbols +}; +*/ +typedef struct IMAGEHLP_MODULE64_V2 { + DWORD SizeOfStruct; // set to sizeof(IMAGEHLP_MODULE64) + DWORD64 BaseOfImage; // base load address of module + DWORD ImageSize; // virtual size of the loaded module + DWORD TimeDateStamp; // date/time stamp from pe header + DWORD CheckSum; // checksum from the pe header + DWORD NumSyms; // number of symbols in the symbol table + SYM_TYPE SymType; // type of symbols loaded + CHAR ModuleName[32]; // module name + CHAR ImageName[256]; // image name + CHAR LoadedImageName[256]; // symbol file name +}; + + + // SymCleanup() + typedef BOOL (__stdcall *tSC)( IN HANDLE hProcess ); + tSC pSC; + + // SymFunctionTableAccess64() + typedef PVOID (__stdcall *tSFTA)( HANDLE hProcess, DWORD64 AddrBase ); + tSFTA pSFTA; + + // SymGetLineFromAddr64() + typedef BOOL (__stdcall *tSGLFA)( IN HANDLE hProcess, IN DWORD64 dwAddr, + OUT PDWORD pdwDisplacement, OUT PIMAGEHLP_LINE64 Line ); + tSGLFA pSGLFA; + + // SymGetModuleBase64() + typedef DWORD64 (__stdcall *tSGMB)( IN HANDLE hProcess, IN DWORD64 dwAddr ); + tSGMB pSGMB; + + // SymGetModuleInfo64() + typedef BOOL (__stdcall *tSGMI)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V2 *ModuleInfo ); + tSGMI pSGMI; + +// // SymGetModuleInfo64() +// typedef BOOL (__stdcall *tSGMI_V3)( IN HANDLE hProcess, IN DWORD64 dwAddr, OUT IMAGEHLP_MODULE64_V3 *ModuleInfo ); +// tSGMI_V3 pSGMI_V3; + + // SymGetOptions() + typedef DWORD (__stdcall *tSGO)( VOID ); + tSGO pSGO; + + // SymGetSymFromAddr64() + typedef BOOL (__stdcall *tSGSFA)( IN HANDLE hProcess, IN DWORD64 dwAddr, + OUT PDWORD64 pdwDisplacement, OUT PIMAGEHLP_SYMBOL64 Symbol ); + tSGSFA pSGSFA; + + // SymInitialize() + typedef BOOL (__stdcall *tSI)( IN HANDLE hProcess, IN PSTR UserSearchPath, IN BOOL fInvadeProcess ); + tSI pSI; + + // SymLoadModule64() + typedef DWORD64 (__stdcall *tSLM)( IN HANDLE hProcess, IN HANDLE hFile, + IN PSTR ImageName, IN PSTR ModuleName, IN DWORD64 BaseOfDll, IN DWORD SizeOfDll ); + tSLM pSLM; + + // SymSetOptions() + typedef DWORD (__stdcall *tSSO)( IN DWORD SymOptions ); + tSSO pSSO; + + // StackWalk64() + typedef BOOL (__stdcall *tSW)( + DWORD MachineType, + HANDLE hProcess, + HANDLE hThread, + LPSTACKFRAME64 StackFrame, + PVOID ContextRecord, + PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, + PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, + PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, + PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress ); + tSW pSW; + + // UnDecorateSymbolName() + typedef DWORD (__stdcall WINAPI *tUDSN)( PCSTR DecoratedName, PSTR UnDecoratedName, + DWORD UndecoratedLength, DWORD Flags ); + tUDSN pUDSN; + + typedef BOOL (__stdcall WINAPI *tSGSP)(HANDLE hProcess, PSTR SearchPath, DWORD SearchPathLength); + tSGSP pSGSP; + + +private: + // **************************************** ToolHelp32 ************************ + #define MAX_MODULE_NAME32 255 + #define TH32CS_SNAPMODULE 0x00000008 + #pragma pack( push, 8 ) + typedef struct tagMODULEENTRY32 + { + DWORD dwSize; + DWORD th32ModuleID; // This module + DWORD th32ProcessID; // owning process + DWORD GlblcntUsage; // Global usage count on the module + DWORD ProccntUsage; // Module usage count in th32ProcessID's context + BYTE * modBaseAddr; // Base address of module in th32ProcessID's context + DWORD modBaseSize; // Size in bytes of module starting at modBaseAddr + HMODULE hModule; // The hModule of this module in th32ProcessID's context + char szModule[MAX_MODULE_NAME32 + 1]; + char szExePath[MAX_PATH]; + } MODULEENTRY32; + typedef MODULEENTRY32 * PMODULEENTRY32; + typedef MODULEENTRY32 * LPMODULEENTRY32; + #pragma pack( pop ) + + BOOL GetModuleListTH32(HANDLE hProcess, DWORD pid) + { + // CreateToolhelp32Snapshot() + typedef HANDLE (__stdcall *tCT32S)(DWORD dwFlags, DWORD th32ProcessID); + // Module32First() + typedef BOOL (__stdcall *tM32F)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + // Module32Next() + typedef BOOL (__stdcall *tM32N)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); + + // try both dlls... + const TCHAR *dllname[] = { _T("kernel32.dll"), _T("tlhelp32.dll") }; + HINSTANCE hToolhelp = NULL; + tCT32S pCT32S = NULL; + tM32F pM32F = NULL; + tM32N pM32N = NULL; + + HANDLE hSnap; + MODULEENTRY32 me; + me.dwSize = sizeof(me); + BOOL keepGoing; + size_t i; + + for (i = 0; i<(sizeof(dllname) / sizeof(dllname[0])); i++ ) + { + hToolhelp = LoadLibrary( dllname[i] ); + if (hToolhelp == NULL) + continue; + pCT32S = (tCT32S) GetProcAddress(hToolhelp, "CreateToolhelp32Snapshot"); + pM32F = (tM32F) GetProcAddress(hToolhelp, "Module32First"); + pM32N = (tM32N) GetProcAddress(hToolhelp, "Module32Next"); + if ( (pCT32S != NULL) && (pM32F != NULL) && (pM32N != NULL) ) + break; // found the functions! + FreeLibrary(hToolhelp); + hToolhelp = NULL; + } + + if (hToolhelp == NULL) + return FALSE; + + hSnap = pCT32S( TH32CS_SNAPMODULE, pid ); + if (hSnap == (HANDLE) -1) + return FALSE; + + keepGoing = !!pM32F( hSnap, &me ); + int cnt = 0; + while (keepGoing) + { + this->LoadModule(hProcess, me.szExePath, me.szModule, (DWORD64) me.modBaseAddr, me.modBaseSize); + cnt++; + keepGoing = !!pM32N( hSnap, &me ); + } + CloseHandle(hSnap); + FreeLibrary(hToolhelp); + if (cnt <= 0) + return FALSE; + return TRUE; + } // GetModuleListTH32 + + // **************************************** PSAPI ************************ + typedef struct _MODULEINFO { + LPVOID lpBaseOfDll; + DWORD SizeOfImage; + LPVOID EntryPoint; + } MODULEINFO, *LPMODULEINFO; + + BOOL GetModuleListPSAPI(HANDLE hProcess) + { + // EnumProcessModules() + typedef BOOL (__stdcall *tEPM)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded ); + // GetModuleFileNameEx() + typedef DWORD (__stdcall *tGMFNE)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize ); + // GetModuleBaseName() + typedef DWORD (__stdcall *tGMBN)(HANDLE hProcess, HMODULE hModule, LPSTR lpFilename, DWORD nSize ); + // GetModuleInformation() + typedef BOOL (__stdcall *tGMI)(HANDLE hProcess, HMODULE hModule, LPMODULEINFO pmi, DWORD nSize ); + + HINSTANCE hPsapi; + tEPM pEPM; + tGMFNE pGMFNE; + tGMBN pGMBN; + tGMI pGMI; + + DWORD i; + //ModuleEntry e; + DWORD cbNeeded; + MODULEINFO mi; + HMODULE *hMods = 0; + char *tt = NULL; + char *tt2 = NULL; + const SIZE_T TTBUFLEN = 8096; + int cnt = 0; + + hPsapi = LoadLibrary( _T("psapi.dll") ); + if (hPsapi == NULL) + return FALSE; + + pEPM = (tEPM) GetProcAddress( hPsapi, "EnumProcessModules" ); + pGMFNE = (tGMFNE) GetProcAddress( hPsapi, "GetModuleFileNameExA" ); + pGMBN = (tGMFNE) GetProcAddress( hPsapi, "GetModuleBaseNameA" ); + pGMI = (tGMI) GetProcAddress( hPsapi, "GetModuleInformation" ); + if ( (pEPM == NULL) || (pGMFNE == NULL) || (pGMBN == NULL) || (pGMI == NULL) ) + { + // we couldn?t find all functions + FreeLibrary(hPsapi); + return FALSE; + } + + hMods = (HMODULE*) malloc(sizeof(HMODULE) * (TTBUFLEN / sizeof HMODULE)); + tt = (char*) malloc(sizeof(char) * TTBUFLEN); + tt2 = (char*) malloc(sizeof(char) * TTBUFLEN); + if ( (hMods == NULL) || (tt == NULL) || (tt2 == NULL) ) + goto cleanup; + + if ( ! pEPM( hProcess, hMods, TTBUFLEN, &cbNeeded ) ) + { + //_ftprintf(fLogFile, _T("%lu: EPM failed, GetLastError = %lu\n"), g_dwShowCount, gle ); + goto cleanup; + } + + if ( cbNeeded > TTBUFLEN ) + { + //_ftprintf(fLogFile, _T("%lu: More than %lu module handles. Huh?\n"), g_dwShowCount, lenof( hMods ) ); + goto cleanup; + } + + for ( i = 0; i < cbNeeded / sizeof hMods[0]; i++ ) + { + // base address, size + pGMI(hProcess, hMods[i], &mi, sizeof mi ); + // image file name + tt[0] = 0; + pGMFNE(hProcess, hMods[i], tt, TTBUFLEN ); + // module name + tt2[0] = 0; + pGMBN(hProcess, hMods[i], tt2, TTBUFLEN ); + + DWORD dwRes = this->LoadModule(hProcess, tt, tt2, (DWORD64) mi.lpBaseOfDll, mi.SizeOfImage); + if (dwRes != ERROR_SUCCESS) + this->m_parent->OnDbgHelpErr("LoadModule", dwRes, 0); + cnt++; + } + + cleanup: + if (hPsapi != NULL) FreeLibrary(hPsapi); + if (tt2 != NULL) free(tt2); + if (tt != NULL) free(tt); + if (hMods != NULL) free(hMods); + + return cnt != 0; + } // GetModuleListPSAPI + + DWORD LoadModule(HANDLE hProcess, LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size) + { + CHAR *szImg = _strdup(img); + CHAR *szMod = _strdup(mod); + DWORD result = ERROR_SUCCESS; + if ( (szImg == NULL) || (szMod == NULL) ) + result = ERROR_NOT_ENOUGH_MEMORY; + else + { + if (pSLM(hProcess, 0, szImg, szMod, baseAddr, size) == 0) + result = GetLastError(); + } + ULONGLONG fileVersion = 0; + if ( (m_parent != NULL) && (szImg != NULL) ) + { + // try to retrive the file-version: + if ( (this->m_parent->m_options & StackWalker::RetrieveFileVersion) != 0) + { + VS_FIXEDFILEINFO *fInfo = NULL; + DWORD dwHandle; + DWORD dwSize = GetFileVersionInfoSizeA(szImg, &dwHandle); + if (dwSize > 0) + { + LPVOID vData = malloc(dwSize); + if (vData != NULL) + { + if (GetFileVersionInfoA(szImg, dwHandle, dwSize, vData) != 0) + { + UINT len; + TCHAR szSubBlock[] = _T("\\"); + if (VerQueryValue(vData, szSubBlock, (LPVOID*) &fInfo, &len) == 0) + fInfo = NULL; + else + { + fileVersion = ((ULONGLONG)fInfo->dwFileVersionLS) + ((ULONGLONG)fInfo->dwFileVersionMS << 32); + } + } + free(vData); + } + } + } + + // Retrive some additional-infos about the module + IMAGEHLP_MODULE64_V2 Module; + const char *szSymType = "-unknown-"; + if (this->GetModuleInfo(hProcess, baseAddr, &Module) != FALSE) + { + switch(Module.SymType) + { + case SymNone: + szSymType = "-nosymbols-"; + break; + case SymCoff: + szSymType = "COFF"; + break; + case SymCv: + szSymType = "CV"; + break; + case SymPdb: + szSymType = "PDB"; + break; + case SymExport: + szSymType = "-exported-"; + break; + case SymDeferred: + szSymType = "-deferred-"; + break; + case SymSym: + szSymType = "SYM"; + break; + case 8: //SymVirtual: + szSymType = "Virtual"; + break; + case 9: // SymDia: + szSymType = "DIA"; + break; + } + } + this->m_parent->OnLoadModule(img, mod, baseAddr, size, result, szSymType, Module.LoadedImageName, fileVersion); + } + if (szImg != NULL) free(szImg); + if (szMod != NULL) free(szMod); + return result; + } +public: + BOOL LoadModules(HANDLE hProcess, DWORD dwProcessId) + { + // first try toolhelp32 + if (GetModuleListTH32(hProcess, dwProcessId)) + return true; + // then try psapi + return GetModuleListPSAPI(hProcess); + } + + + BOOL GetModuleInfo(HANDLE hProcess, DWORD64 baseAddr, IMAGEHLP_MODULE64_V2 *pModuleInfo) + { + if(this->pSGMI == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + // First try to use the larger ModuleInfo-Structure +// memset(pModuleInfo, 0, sizeof(IMAGEHLP_MODULE64_V3)); +// pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V3); +// if (this->pSGMI_V3 != NULL) +// { +// if (this->pSGMI_V3(hProcess, baseAddr, pModuleInfo) != FALSE) +// return TRUE; +// // check if the parameter was wrong (size is bad...) +// if (GetLastError() != ERROR_INVALID_PARAMETER) +// return FALSE; +// } + // could not retrive the bigger structure, try with the smaller one (as defined in VC7.1)... + pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); + void *pData = malloc(4096); // reserve enough memory, so the bug in v6.3.5.1 does not lead to memory-overwrites... + if (pData == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + memcpy(pData, pModuleInfo, sizeof(IMAGEHLP_MODULE64_V2)); + if (this->pSGMI(hProcess, baseAddr, (IMAGEHLP_MODULE64_V2*) pData) != FALSE) + { + // only copy as much memory as is reserved... + memcpy(pModuleInfo, pData, sizeof(IMAGEHLP_MODULE64_V2)); + pModuleInfo->SizeOfStruct = sizeof(IMAGEHLP_MODULE64_V2); + free(pData); + return TRUE; + } + free(pData); + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } +}; + +// ############################################################# +StackWalker::StackWalker(DWORD dwProcessId, HANDLE hProcess) +{ + this->m_options = OptionsAll; + this->m_modulesLoaded = FALSE; + this->m_hProcess = hProcess; + this->m_sw = new StackWalkerInternal(this, this->m_hProcess); + this->m_dwProcessId = dwProcessId; + this->m_szSymPath = NULL; +} +StackWalker::StackWalker(int options, LPCSTR szSymPath, DWORD dwProcessId, HANDLE hProcess) +{ + this->m_options = options; + this->m_modulesLoaded = FALSE; + this->m_hProcess = hProcess; + this->m_sw = new StackWalkerInternal(this, this->m_hProcess); + this->m_dwProcessId = dwProcessId; + if (szSymPath != NULL) + { + this->m_szSymPath = _strdup(szSymPath); + this->m_options |= SymBuildPath; + } + else + this->m_szSymPath = NULL; +} + +StackWalker::~StackWalker() +{ + if (m_szSymPath != NULL) + free(m_szSymPath); + m_szSymPath = NULL; + if (this->m_sw != NULL) + delete this->m_sw; + this->m_sw = NULL; +} + +BOOL StackWalker::LoadModules() +{ + if (this->m_sw == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + if (m_modulesLoaded != FALSE) + return TRUE; + + // Build the sym-path: + char *szSymPath = NULL; + if ( (this->m_options & SymBuildPath) != 0) + { + const size_t nSymPathLen = 4096; + szSymPath = (char*) malloc(nSymPathLen); + if (szSymPath == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + szSymPath[0] = 0; + // Now first add the (optional) provided sympath: + if (this->m_szSymPath != NULL) + { + strcat_s(szSymPath, nSymPathLen, this->m_szSymPath); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + strcat_s(szSymPath, nSymPathLen, ".;"); + + const size_t nTempLen = 1024; + char szTemp[nTempLen]; + // Now add the current directory: + if (GetCurrentDirectoryA(nTempLen, szTemp) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + // Now add the path for the main-module: + if (GetModuleFileNameA(NULL, szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + for (char *p = (szTemp+strlen(szTemp)-1); p >= szTemp; --p) + { + // locate the rightmost path separator + if ( (*p == '\\') || (*p == '/') || (*p == ':') ) + { + *p = 0; + break; + } + } // for (search for path separator...) + if (strlen(szTemp) > 0) + { + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + } + if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + if (GetEnvironmentVariableA("SYSTEMROOT", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + // also add the "system32"-directory: + strcat_s(szTemp, nTempLen, "\\system32"); + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, ";"); + } + + if ( (this->m_options & SymBuildPath) != 0) + { + if (GetEnvironmentVariableA("SYSTEMDRIVE", szTemp, nTempLen) > 0) + { + szTemp[nTempLen-1] = 0; + strcat_s(szSymPath, nSymPathLen, "SRV*"); + strcat_s(szSymPath, nSymPathLen, szTemp); + strcat_s(szSymPath, nSymPathLen, "\\websymbols"); + strcat_s(szSymPath, nSymPathLen, "*http://msdl.microsoft.com/download/symbols;"); + } + else + strcat_s(szSymPath, nSymPathLen, "SRV*c:\\websymbols*http://msdl.microsoft.com/download/symbols;"); + } + } + + // First Init the whole stuff... + BOOL bRet = this->m_sw->Init(szSymPath); + if (szSymPath != NULL) free(szSymPath); szSymPath = NULL; + if (bRet == FALSE) + { + this->OnDbgHelpErr("Error while initializing dbghelp.dll", 0, 0); + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + + bRet = this->m_sw->LoadModules(this->m_hProcess, this->m_dwProcessId); + if (bRet != FALSE) + m_modulesLoaded = TRUE; + return bRet; +} + + +// The following is used to pass the "userData"-Pointer to the user-provided readMemoryFunction +// This has to be done due to a problem with the "hProcess"-parameter in x64... +// Because this class is in no case multi-threading-enabled (because of the limitations +// of dbghelp.dll) it is "safe" to use a static-variable +static StackWalker::PReadProcessMemoryRoutine s_readMemoryFunction = NULL; +static LPVOID s_readMemoryFunction_UserData = NULL; + +BOOL StackWalker::ShowCallstack(HANDLE hThread, const CONTEXT *context, PReadProcessMemoryRoutine readMemoryFunction, LPVOID pUserData) +{ + CONTEXT c;; + CallstackEntry csEntry; + IMAGEHLP_SYMBOL64 *pSym = NULL; + StackWalkerInternal::IMAGEHLP_MODULE64_V2 Module; + IMAGEHLP_LINE64 Line; + int frameNum; + + if (m_modulesLoaded == FALSE) + this->LoadModules(); // ignore the result... + + if (this->m_sw->m_hDbhHelp == NULL) + { + SetLastError(ERROR_DLL_INIT_FAILED); + return FALSE; + } + + s_readMemoryFunction = readMemoryFunction; + s_readMemoryFunction_UserData = pUserData; + + if (context == NULL) + { + // If no context is provided, capture the context + if (hThread == GetCurrentThread()) + { + GET_CURRENT_CONTEXT(c, USED_CONTEXT_FLAGS); + } + else + { + SuspendThread(hThread); + memset(&c, 0, sizeof(CONTEXT)); + c.ContextFlags = USED_CONTEXT_FLAGS; + if (GetThreadContext(hThread, &c) == FALSE) + { + ResumeThread(hThread); + return FALSE; + } + } + } + else + c = *context; + + // init STACKFRAME for first call + STACKFRAME64 s; // in/out stackframe + memset(&s, 0, sizeof(s)); + DWORD imageType; +#ifdef _M_IX86 + // normally, call ImageNtHeader() and use machine info from PE header + imageType = IMAGE_FILE_MACHINE_I386; + s.AddrPC.Offset = c.Eip; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.Ebp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrStack.Offset = c.Esp; + s.AddrStack.Mode = AddrModeFlat; +#elif _M_X64 + imageType = IMAGE_FILE_MACHINE_AMD64; + s.AddrPC.Offset = c.Rip; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.Rsp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrStack.Offset = c.Rsp; + s.AddrStack.Mode = AddrModeFlat; +#elif _M_IA64 + imageType = IMAGE_FILE_MACHINE_IA64; + s.AddrPC.Offset = c.StIIP; + s.AddrPC.Mode = AddrModeFlat; + s.AddrFrame.Offset = c.IntSp; + s.AddrFrame.Mode = AddrModeFlat; + s.AddrBStore.Offset = c.RsBSP; + s.AddrBStore.Mode = AddrModeFlat; + s.AddrStack.Offset = c.IntSp; + s.AddrStack.Mode = AddrModeFlat; +#else +#error "Platform not supported!" +#endif + + pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + if (!pSym) goto cleanup; // not enough memory... + memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STACKWALK_MAX_NAMELEN); + pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64); + pSym->MaxNameLength = STACKWALK_MAX_NAMELEN; + + memset(&Line, 0, sizeof(Line)); + Line.SizeOfStruct = sizeof(Line); + + memset(&Module, 0, sizeof(Module)); + Module.SizeOfStruct = sizeof(Module); + + for (frameNum = 0; ; ++frameNum ) + { + // get next stack frame (StackWalk64(), SymFunctionTableAccess64(), SymGetModuleBase64()) + // if this returns ERROR_INVALID_ADDRESS (487) or ERROR_NOACCESS (998), you can + // assume that either you are done, or that the stack is so hosed that the next + // deeper frame could not be found. + // CONTEXT need not to be suplied if imageTyp is IMAGE_FILE_MACHINE_I386! + if ( ! this->m_sw->pSW(imageType, this->m_hProcess, hThread, &s, &c, myReadProcMem, this->m_sw->pSFTA, this->m_sw->pSGMB, NULL) ) + { + this->OnDbgHelpErr("StackWalk64", GetLastError(), s.AddrPC.Offset); + break; + } + + csEntry.offset = s.AddrPC.Offset; + csEntry.name[0] = 0; + csEntry.undName[0] = 0; + csEntry.undFullName[0] = 0; + csEntry.offsetFromSmybol = 0; + csEntry.offsetFromLine = 0; + csEntry.lineFileName[0] = 0; + csEntry.lineNumber = 0; + csEntry.loadedImageName[0] = 0; + csEntry.moduleName[0] = 0; + if (s.AddrPC.Offset == s.AddrReturn.Offset) + { + this->OnDbgHelpErr("StackWalk64-Endless-Callstack!", 0, s.AddrPC.Offset); + break; + } + if (s.AddrPC.Offset != 0) + { + // we seem to have a valid PC + // show procedure info (SymGetSymFromAddr64()) + if (this->m_sw->pSGSFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromSmybol), pSym) != FALSE) + { + // TODO: Mache dies sicher...! + strcpy_s(csEntry.name, pSym->Name); + // UnDecorateSymbolName() + this->m_sw->pUDSN( pSym->Name, csEntry.undName, STACKWALK_MAX_NAMELEN, UNDNAME_NAME_ONLY ); + this->m_sw->pUDSN( pSym->Name, csEntry.undFullName, STACKWALK_MAX_NAMELEN, UNDNAME_COMPLETE ); + } + else + { + this->OnDbgHelpErr("SymGetSymFromAddr64", GetLastError(), s.AddrPC.Offset); + } + + // show line number info, NT5.0-method (SymGetLineFromAddr64()) + if (this->m_sw->pSGLFA != NULL ) + { // yes, we have SymGetLineFromAddr64() + if (this->m_sw->pSGLFA(this->m_hProcess, s.AddrPC.Offset, &(csEntry.offsetFromLine), &Line) != FALSE) + { + csEntry.lineNumber = Line.LineNumber; + // TODO: Mache dies sicher...! + strcpy_s(csEntry.lineFileName, Line.FileName); + } + else + { + this->OnDbgHelpErr("SymGetLineFromAddr64", GetLastError(), s.AddrPC.Offset); + } + } // yes, we have SymGetLineFromAddr64() + + // show module info (SymGetModuleInfo64()) + if (this->m_sw->GetModuleInfo(this->m_hProcess, s.AddrPC.Offset, &Module ) != FALSE) + { // got module info OK + switch ( Module.SymType ) + { + case SymNone: + csEntry.symTypeString = "-nosymbols-"; + break; + case SymCoff: + csEntry.symTypeString = "COFF"; + break; + case SymCv: + csEntry.symTypeString = "CV"; + break; + case SymPdb: + csEntry.symTypeString = "PDB"; + break; + case SymExport: + csEntry.symTypeString = "-exported-"; + break; + case SymDeferred: + csEntry.symTypeString = "-deferred-"; + break; + case SymSym: + csEntry.symTypeString = "SYM"; + break; +#if API_VERSION_NUMBER >= 9 + case SymDia: + csEntry.symTypeString = "DIA"; + break; +#endif + case 8: //SymVirtual: + csEntry.symTypeString = "Virtual"; + break; + default: + //_snprintf( ty, sizeof ty, "symtype=%ld", (long) Module.SymType ); + csEntry.symTypeString = NULL; + break; + } + + // TODO: Mache dies sicher...! + strcpy_s(csEntry.moduleName, Module.ModuleName); + csEntry.baseOfImage = Module.BaseOfImage; + strcpy_s(csEntry.loadedImageName, Module.LoadedImageName); + } // got module info OK + else + { + this->OnDbgHelpErr("SymGetModuleInfo64", GetLastError(), s.AddrPC.Offset); + } + } // we seem to have a valid PC + + CallstackEntryType et = nextEntry; + if (frameNum == 0) + et = firstEntry; + this->OnCallstackEntry(et, csEntry); + + if (s.AddrReturn.Offset == 0) + { + this->OnCallstackEntry(lastEntry, csEntry); + SetLastError(ERROR_SUCCESS); + break; + } + } // for ( frameNum ) + + cleanup: + if (pSym) free( pSym ); + + if (context == NULL) + ResumeThread(hThread); + + return TRUE; +} + +BOOL __stdcall StackWalker::myReadProcMem( + HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead + ) +{ + if (s_readMemoryFunction == NULL) + { + SIZE_T st; + BOOL bRet = ReadProcessMemory(hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, &st); + *lpNumberOfBytesRead = (DWORD) st; + //printf("ReadMemory: hProcess: %p, baseAddr: %p, buffer: %p, size: %d, read: %d, result: %d\n", hProcess, (LPVOID) qwBaseAddress, lpBuffer, nSize, (DWORD) st, (DWORD) bRet); + return bRet; + } + else + { + return s_readMemoryFunction(hProcess, qwBaseAddress, lpBuffer, nSize, lpNumberOfBytesRead, s_readMemoryFunction_UserData); + } +} + +void StackWalker::OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + if (fileVersion == 0) + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s'\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName); + else + { + DWORD v4 = (DWORD) fileVersion & 0xFFFF; + DWORD v3 = (DWORD) (fileVersion>>16) & 0xFFFF; + DWORD v2 = (DWORD) (fileVersion>>32) & 0xFFFF; + DWORD v1 = (DWORD) (fileVersion>>48) & 0xFFFF; + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s:%s (%p), size: %d (result: %d), SymType: '%s', PDB: '%s', fileVersion: %d.%d.%d.%d\n", img, mod, (LPVOID) baseAddr, size, result, symType, pdbName, v1, v2, v3, v4); + } + OnOutput(buffer); +} + +void StackWalker::OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + if ( (eType != lastEntry) && (entry.offset != 0) ) + { + if (entry.name[0] == 0) + strcpy_s(entry.name, "(function-name not available)"); + if (entry.undName[0] != 0) + strcpy_s(entry.name, entry.undName); + if (entry.undFullName[0] != 0) + strcpy_s(entry.name, entry.undFullName); + if (entry.lineFileName[0] == 0) + { + strcpy_s(entry.lineFileName, "(filename not available)"); + if (entry.moduleName[0] == 0) + strcpy_s(entry.moduleName, "(module-name not available)"); + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%p (%s): %s: %s\n", (LPVOID) entry.offset, entry.moduleName, entry.lineFileName, entry.name); + } + else + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "%s (%d): %s\n", entry.lineFileName, entry.lineNumber, entry.name); + OnOutput(buffer); + } +} + +void StackWalker::OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "ERROR: %s, GetLastError: %d (Address: %p)\n", szFuncName, gle, (LPVOID) addr); + OnOutput(buffer); +} + +void StackWalker::OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName) +{ + CHAR buffer[STACKWALK_MAX_NAMELEN]; + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "SymInit: Symbol-SearchPath: '%s', symOptions: %d, UserName: '%s'\n", szSearchPath, symOptions, szUserName); + OnOutput(buffer); + // Also display the OS-version +#if _MSC_VER <= 1200 + OSVERSIONINFOA ver; + ZeroMemory(&ver, sizeof(OSVERSIONINFOA)); + ver.dwOSVersionInfoSize = sizeof(ver); + if (GetVersionExA(&ver) != FALSE) + { + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s)\n", + ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, + ver.szCSDVersion); + OnOutput(buffer); + } +#else + OSVERSIONINFOEXA ver; + ZeroMemory(&ver, sizeof(OSVERSIONINFOEXA)); + ver.dwOSVersionInfoSize = sizeof(ver); + if (GetVersionExA( (OSVERSIONINFOA*) &ver) != FALSE) + { + _snprintf_s(buffer, STACKWALK_MAX_NAMELEN, "OS-Version: %d.%d.%d (%s) 0x%x-0x%x\n", + ver.dwMajorVersion, ver.dwMinorVersion, ver.dwBuildNumber, + ver.szCSDVersion, ver.wSuiteMask, ver.wProductType); + OnOutput(buffer); + } +#endif +} + +void StackWalker::OnOutput(LPCSTR buffer) +{ + OutputDebugStringA(buffer); +} +#endif diff --git a/common/StackWalker/StackWalker.h b/common/StackWalker/StackWalker.h new file mode 100644 index 000000000..808a18810 --- /dev/null +++ b/common/StackWalker/StackWalker.h @@ -0,0 +1,192 @@ +/********************************************************************** + * + * StackWalker.h + * + * + * History: + * 2005-07-27 v1 - First public release on http://www.codeproject.com/ + * (for additional changes see History in 'StackWalker.cpp'! + * 2013-01-26 - Modified by KimLS(KLS) for EQEmu's purposes + * + **********************************************************************/ +#ifdef _WINDOWS +// #pragma once is supported starting with _MCS_VER 1000, +// so we need not to check the version (because we only support _MSC_VER >= 1100)! +#pragma once + +#include + +// special defines for VC5/6 (if no actual PSDK is installed): +#if _MSC_VER < 1300 +typedef unsigned __int64 DWORD64, *PDWORD64; +#if defined(_WIN64) +typedef unsigned __int64 SIZE_T, *PSIZE_T; +#else +typedef unsigned long SIZE_T, *PSIZE_T; +#endif +#endif // _MSC_VER < 1300 + +class StackWalkerInternal; // forward +class StackWalker +{ +public: + typedef enum StackWalkOptions + { + // No addition info will be retrived + // (only the address is available) + RetrieveNone = 0, + + // Try to get the symbol-name + RetrieveSymbol = 1, + + // Try to get the line for this symbol + RetrieveLine = 2, + + // Try to retrieve the module-infos + RetrieveModuleInfo = 4, + + // Also retrieve the version for the DLL/EXE + RetrieveFileVersion = 8, + + // Contains all the abouve + RetrieveVerbose = 0xF, + + // Generate a "good" symbol-search-path + SymBuildPath = 0x10, + + // Also use the public Microsoft-Symbol-Server + SymUseSymSrv = 0x20, + + // Contains all the abouve "Sym"-options + SymAll = 0x30, + + // Contains all options (default) + OptionsAll = 0x3F + } StackWalkOptions; + + StackWalker( + int options = OptionsAll, // 'int' is by design, to combine the enum-flags + LPCSTR szSymPath = NULL, + DWORD dwProcessId = GetCurrentProcessId(), + HANDLE hProcess = GetCurrentProcess() + ); + StackWalker(DWORD dwProcessId, HANDLE hProcess); + virtual ~StackWalker(); + + typedef BOOL (__stdcall *PReadProcessMemoryRoutine)( + HANDLE hProcess, + DWORD64 qwBaseAddress, + PVOID lpBuffer, + DWORD nSize, + LPDWORD lpNumberOfBytesRead, + LPVOID pUserData // optional data, which was passed in "ShowCallstack" + ); + + BOOL LoadModules(); + + BOOL ShowCallstack( + HANDLE hThread = GetCurrentThread(), + const CONTEXT *context = NULL, + PReadProcessMemoryRoutine readMemoryFunction = NULL, + LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback + ); + +#if _MSC_VER >= 1300 +// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" +// in older compilers in order to use it... starting with VC7 we can declare it as "protected" +protected: +#endif + enum { STACKWALK_MAX_NAMELEN = 1024 }; // max name length for found symbols + +protected: + // Entry for each Callstack-Entry + typedef struct CallstackEntry + { + DWORD64 offset; // if 0, we have no valid entry + CHAR name[STACKWALK_MAX_NAMELEN]; + CHAR undName[STACKWALK_MAX_NAMELEN]; + CHAR undFullName[STACKWALK_MAX_NAMELEN]; + DWORD64 offsetFromSmybol; + DWORD offsetFromLine; + DWORD lineNumber; + CHAR lineFileName[STACKWALK_MAX_NAMELEN]; + DWORD symType; + LPCSTR symTypeString; + CHAR moduleName[STACKWALK_MAX_NAMELEN]; + DWORD64 baseOfImage; + CHAR loadedImageName[STACKWALK_MAX_NAMELEN]; + } CallstackEntry; + + typedef enum CallstackEntryType {firstEntry, nextEntry, lastEntry}; + + virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName); + virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion); + virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); + virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr); + virtual void OnOutput(LPCSTR szText); + + StackWalkerInternal *m_sw; + HANDLE m_hProcess; + DWORD m_dwProcessId; + BOOL m_modulesLoaded; + LPSTR m_szSymPath; + + int m_options; + + static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead); + + friend StackWalkerInternal; +}; + + +// The "ugly" assembler-implementation is needed for systems before XP +// If you have a new PSDK and you only compile for XP and later, then you can use +// the "RtlCaptureContext" +// Currently there is no define which determines the PSDK-Version... +// So we just use the compiler-version (and assumes that the PSDK is +// the one which was installed by the VS-IDE) + +// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later... +// But I currently use it in x64/IA64 environments... +//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400) + +#if defined(_M_IX86) +#ifdef CURRENT_THREAD_VIA_EXCEPTION +// TODO: The following is not a "good" implementation, +// because the callstack is only valid in the "__except" block... +#define GET_CURRENT_CONTEXT(c, contextFlags) \ + do { \ + memset(&c, 0, sizeof(CONTEXT)); \ + EXCEPTION_POINTERS *pExp = NULL; \ + __try { \ + throw 0; \ + } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \ + if (pExp != NULL) \ + memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + } while(0); +#else +// The following should be enough for walking the callstack... +#define GET_CURRENT_CONTEXT(c, contextFlags) \ + do { \ + memset(&c, 0, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + __asm call x \ + __asm x: pop eax \ + __asm mov c.Eip, eax \ + __asm mov c.Ebp, ebp \ + __asm mov c.Esp, esp \ + } while(0); +#endif + +#else + +// The following is defined for x86 (XP and higher), x64 and IA64: +#define GET_CURRENT_CONTEXT(c, contextFlags) \ + do { \ + memset(&c, 0, sizeof(CONTEXT)); \ + c.ContextFlags = contextFlags; \ + RtlCaptureContext(&c); \ +} while(0); +#endif +#endif diff --git a/common/StructStrategy.cpp b/common/StructStrategy.cpp new file mode 100644 index 000000000..e18e77c13 --- /dev/null +++ b/common/StructStrategy.cpp @@ -0,0 +1,99 @@ + +#include "debug.h" +#include "StructStrategy.h" +#include "logsys.h" +#include "EQStream.h" +#include + + +//note: all encoders and decoders must be valid functions. +//so if you specify set_defaults=false +StructStrategy::StructStrategy() { + int r; + for(r = 0; r < _maxEmuOpcode; r++) { + encoders[r] = PassEncoder; + decoders[r] = PassDecoder; + } +} + +void StructStrategy::Encode(EQApplicationPacket **p, EQStream *dest, bool ack_req) const { + EmuOpcode op = (*p)->GetOpcode(); + Encoder proc = encoders[op]; + proc(p, dest, ack_req); +} + +void StructStrategy::Decode(EQApplicationPacket *p) const { + EmuOpcode op = p->GetOpcode(); + Decoder proc = decoders[op]; + proc(p); +} + + +void StructStrategy::ErrorEncoder(EQApplicationPacket **in_p, EQStream *dest, bool ack_req) { + EQApplicationPacket *p = *in_p; + *in_p = NULL; + + _log(NET__STRUCTS, "Error encoding opcode %s: no encoder provided. Dropping.", OpcodeManager::EmuToName(p->GetOpcode())); + + delete p; +} + +void StructStrategy::ErrorDecoder(EQApplicationPacket *p) { + _log(NET__STRUCTS, "Error decoding opcode %s: no decoder provided. Invalidating.", OpcodeManager::EmuToName(p->GetOpcode())); + p->SetOpcode(OP_Unknown); +} + +void StructStrategy::PassEncoder(EQApplicationPacket **p, EQStream *dest, bool ack_req) { + dest->FastQueuePacket(p, ack_req); +} + +void StructStrategy::PassDecoder(EQApplicationPacket *p) { + //do nothing since we decode in place +} + + + + +//effectively a singleton, but I decided to do it this way for no apparent reason. +namespace StructStrategyFactory { + + static map strategies; + + void RegisterPatch(EmuOpcode first_opcode, const StructStrategy *structs) { + strategies[first_opcode] = structs; + } + + const StructStrategy *FindPatch(EmuOpcode first_opcode) { + map::const_iterator res; + res = strategies.find(first_opcode); + if(res == strategies.end()) + return(NULL); + return(res->second); + } + +}; + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/StructStrategy.h b/common/StructStrategy.h new file mode 100644 index 000000000..46ae9ffbb --- /dev/null +++ b/common/StructStrategy.h @@ -0,0 +1,73 @@ +#ifndef STRUCTSTRATEGY_H_ +#define STRUCTSTRATEGY_H_ + +class EQApplicationPacket; +class EQStream; +#include "emu_opcodes.h" + +#include + +class StructStrategy { +public: + //the encoder takes ownership of the supplied packet, and may enqueue multiple resulting packets into the stream + typedef void (*Encoder)(EQApplicationPacket **p, EQStream *dest, bool ack_req); + //the decoder may only edit the supplied packet, producing a single packet for eqemu to consume. + typedef void (*Decoder)(EQApplicationPacket *p); + + StructStrategy(); + virtual ~StructStrategy() {} + + //this method takes an eqemu struct, and enqueues the produced structs into the stream. + void Encode(EQApplicationPacket **p, EQStream *dest, bool ack_req) const; + //this method takes an EQ wire struct, and converts it into an eqemu struct + void Decode(EQApplicationPacket *p) const; + + virtual std::string Describe() const = 0; + +protected: + //some common coders: + //Print an error saying unknown struct/opcode and drop it + static void ErrorEncoder(EQApplicationPacket **p, EQStream *dest, bool ack_req); + static void ErrorDecoder(EQApplicationPacket *p); + //pass the packet through without modification (emu == EQ) (default) + static void PassEncoder(EQApplicationPacket **p, EQStream *dest, bool ack_req); + static void PassDecoder(EQApplicationPacket *p); + + Encoder encoders[_maxEmuOpcode]; + Decoder decoders[_maxEmuOpcode]; +}; + +//effectively a singleton, but I decided to do it this way for no apparent reason. +namespace StructStrategyFactory { + void RegisterPatch(EmuOpcode first_opcode, const StructStrategy *structs); + + //does NOT return ownership of the strategy. + const StructStrategy *FindPatch(EmuOpcode first_opcode); +}; + + +#endif /*STRUCTSTRATEGY_H_*/ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/TCPBasicServer.h b/common/TCPBasicServer.h new file mode 100644 index 000000000..3a01051d3 --- /dev/null +++ b/common/TCPBasicServer.h @@ -0,0 +1,17 @@ +#ifndef TCPBASICSERVER_H_ +#define TCPBASICSERVER_H_ + +#include "TCPServer.h" +#include "TCPConnection.h" + +class TCPBasicServer : public TCPServer { +public: + inline TCPBasicServer(uint16 iPort = 0) : TCPServer(iPort) { } + inline virtual void CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort) { + TCPConnection *conn = new TCPConnection(ID, in_socket, irIP, irPort); + AddConnection(conn); + } +}; + +#endif /*TCPBASICSERVER_H_*/ + diff --git a/common/TCPConnection.cpp b/common/TCPConnection.cpp new file mode 100644 index 000000000..63a351131 --- /dev/null +++ b/common/TCPConnection.cpp @@ -0,0 +1,959 @@ +/* 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 "../common/debug.h" + +#include +using namespace std; +#include +#include +#include +using namespace std; + +#include "TCPConnection.h" +#include "../common/servertalk.h" +#include "../common/timer.h" +#include "../common/packet_dump.h" + +#ifdef FREEBSD //Timothy Whitman - January 7, 2003 + #define MSG_NOSIGNAL 0 +#endif + +#ifdef _WINDOWS +InitWinsock winsock; +#endif + +#define LOOP_GRANULARITY 3 //# of ms between checking our socket/queues + +#define TCPN_DEBUG 0 +#define TCPN_DEBUG_Console 0 +#define TCPN_DEBUG_Memory 0 +#define TCPN_LOG_RAW_DATA_OUT 0 //1 = info, 2 = length limited dump, 3 = full dump +#define TCPN_LOG_RAW_DATA_IN 0 //1 = info, 2 = length limited dump, 3 = full dump + +//client version +TCPConnection::TCPConnection() +: ConnectionType(Outgoing), + connection_socket(0), + id(0), + rIP(0), + rPort(0) +{ + pState = TCPS_Ready; + pFree = false; + pEcho = false; + recvbuf = NULL; + sendbuf = NULL; + pRunLoop = false; + charAsyncConnect = 0; + pAsyncConnect = false; + m_previousLineEnd = false; +#if TCPN_DEBUG_Memory >= 7 + cout << "Constructor #2 on outgoing TCP# " << GetID() << endl; +#endif +} + +//server version +TCPConnection::TCPConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort) +: ConnectionType(Incomming), + connection_socket(in_socket), + id(ID), + rIP(irIP), + rPort(irPort) +{ + pState = TCPS_Connected; + pFree = false; + pEcho = false; + recvbuf = NULL; + sendbuf = NULL; + pRunLoop = false; + charAsyncConnect = 0; + pAsyncConnect = false; + m_previousLineEnd = false; +#if TCPN_DEBUG_Memory >= 7 + cout << "Constructor #2 on incoming TCP# " << GetID() << endl; +#endif +} + +TCPConnection::~TCPConnection() { + FinishDisconnect(); + ClearBuffers(); + if (ConnectionType == Outgoing) { + MRunLoop.lock(); + pRunLoop = false; + MRunLoop.unlock(); + MLoopRunning.lock(); + MLoopRunning.unlock(); +#if TCPN_DEBUG_Memory >= 6 + cout << "Deconstructor on outgoing TCP# " << GetID() << endl; +#endif + } +#if TCPN_DEBUG_Memory >= 5 + else { + cout << "Deconstructor on incomming TCP# " << GetID() << endl; + } +#endif + safe_delete_array(recvbuf); + safe_delete_array(sendbuf); + safe_delete_array(charAsyncConnect); +} + +void TCPConnection::SetState(State_t in_state) { + MState.lock(); + pState = in_state; + MState.unlock(); +} + +TCPConnection::State_t TCPConnection::GetState() const { + State_t ret; + MState.lock(); + ret = pState; + MState.unlock(); + return ret; +} + +bool TCPConnection::GetSockName(char *host, uint16 *port) +{ + bool result=false; + LockMutex lock(&MState); + if (!Connected()) + return false; + + struct sockaddr_in local; + +#ifdef _WINDOWS + int addrlen; +#else +#ifdef FREEBSD + socklen_t addrlen; +#else + size_t addrlen; +#endif +#endif + addrlen=sizeof(struct sockaddr_in); +#ifdef _WINDOWS + if (!getsockname(connection_socket,(struct sockaddr *)&local,&addrlen)) { +#else + if (!getsockname(connection_socket,(struct sockaddr *)&local,(socklen_t *)&addrlen)) { +#endif + unsigned long ip=local.sin_addr.s_addr; + sprintf(host,"%d.%d.%d.%d", + *(unsigned char *)&ip, + *((unsigned char *)&ip+1), + *((unsigned char *)&ip+2), + *((unsigned char *)&ip+3)); + *port=ntohs(local.sin_port); + + result=true; + } + + return result; +} + +void TCPConnection::Free() { + if (ConnectionType == Outgoing) { + ThrowError("TCPConnection::Free() called on an Outgoing connection"); + } +#if TCPN_DEBUG_Memory >= 5 + cout << "Free on TCP# " << GetID() << endl; +#endif + Disconnect(); + pFree = true; +} + +bool TCPConnection::Send(const uchar* data, int32 size) { + if (!Connected()) + return false; + if (!size) + return true; + ServerSendQueuePushEnd(data, size); + return true; +} + +void TCPConnection::ServerSendQueuePushEnd(const uchar* data, int32 size) { + MSendQueue.lock(); + if (sendbuf == NULL) { + sendbuf = new uchar[size]; + sendbuf_size = size; + sendbuf_used = 0; + } + else if (size > (sendbuf_size - sendbuf_used)) { + sendbuf_size += size + 1024; + uchar* tmp = new uchar[sendbuf_size]; + memcpy(tmp, sendbuf, sendbuf_used); + safe_delete_array(sendbuf); + sendbuf = tmp; + } + memcpy(&sendbuf[sendbuf_used], data, size); + sendbuf_used += size; + MSendQueue.unlock(); +} + +void TCPConnection::ServerSendQueuePushEnd(uchar** data, int32 size) { + MSendQueue.lock(); + if (sendbuf == 0) { + sendbuf = *data; + sendbuf_size = size; + sendbuf_used = size; + MSendQueue.unlock(); + *data = 0; + return; + } + if (size > (sendbuf_size - sendbuf_used)) { + sendbuf_size += size; + uchar* tmp = new uchar[sendbuf_size]; + memcpy(tmp, sendbuf, sendbuf_used); + safe_delete_array(sendbuf); + sendbuf = tmp; + } + memcpy(&sendbuf[sendbuf_used], *data, size); + sendbuf_used += size; + MSendQueue.unlock(); + safe_delete_array(*data); +} + +void TCPConnection::ServerSendQueuePushFront(uchar* data, int32 size) { + MSendQueue.lock(); + if (sendbuf == 0) { + sendbuf = new uchar[size]; + sendbuf_size = size; + sendbuf_used = 0; + } + else if (size > (sendbuf_size - sendbuf_used)) { + sendbuf_size += size; + uchar* tmp = new uchar[sendbuf_size]; + memcpy(&tmp[size], sendbuf, sendbuf_used); + safe_delete_array(sendbuf); + sendbuf = tmp; + } + memcpy(sendbuf, data, size); + sendbuf_used += size; + MSendQueue.unlock(); +} + +bool TCPConnection::ServerSendQueuePop(uchar** data, int32* size) { + bool ret; + if (!MSendQueue.trylock()) + return false; + if (sendbuf) { + *data = sendbuf; + *size = sendbuf_used; + sendbuf = 0; + ret = true; + } + else { + ret = false; + } + MSendQueue.unlock(); + return ret; +} + +bool TCPConnection::ServerSendQueuePopForce(uchar** data, int32* size) { + bool ret; + MSendQueue.lock(); + if (sendbuf) { + *data = sendbuf; + *size = sendbuf_used; + sendbuf = 0; + ret = true; + } + else { + ret = false; + } + MSendQueue.unlock(); + return ret; +} + +char* TCPConnection::PopLine() { + char* ret; + if (!MLineOutQueue.trylock()) + return 0; + ret = (char*) LineOutQueue.pop(); + MLineOutQueue.unlock(); + return ret; +} + +bool TCPConnection::LineOutQueuePush(char* line) { + MLineOutQueue.lock(); + LineOutQueue.push(line); + MLineOutQueue.unlock(); + return(false); +} + + +void TCPConnection::FinishDisconnect() { + MState.lock(); + if (connection_socket != INVALID_SOCKET && connection_socket != 0) { + if (pState == TCPS_Connected || pState == TCPS_Disconnecting || pState == TCPS_Disconnected) { + bool sent_something = false; + SendData(sent_something); + } + pState = TCPS_Closing; + shutdown(connection_socket, 0x01); + shutdown(connection_socket, 0x00); +#ifdef _WINDOWS + closesocket(connection_socket); +#else + close(connection_socket); +#endif + connection_socket = 0; + rIP = 0; + rPort = 0; + ClearBuffers(); + } + pState = TCPS_Disconnected; + MState.unlock(); +} + +void TCPConnection::Disconnect() { + MState.lock(); + if(pState == TCPS_Connected || pState == TCPS_Connecting) { + pState = TCPS_Disconnecting; + } + MState.unlock(); +} + +bool TCPConnection::GetAsyncConnect() { + bool ret; + MAsyncConnect.lock(); + ret = pAsyncConnect; + MAsyncConnect.unlock(); + return ret; +} + +bool TCPConnection::SetAsyncConnect(bool iValue) { + bool ret; + MAsyncConnect.lock(); + ret = pAsyncConnect; + pAsyncConnect = iValue; + MAsyncConnect.unlock(); + return ret; +} + +bool TCPConnection::ConnectReady() const { + State_t s = GetState(); + if (s != TCPS_Ready && s != TCPS_Disconnected) + return(false); + return(ConnectionType == Outgoing); +} + +void TCPConnection::AsyncConnect(const char* irAddress, uint16 irPort) { + safe_delete_array(charAsyncConnect); + charAsyncConnect = new char[strlen(irAddress) + 1]; + strcpy(charAsyncConnect, irAddress); + AsyncConnect((uint32) 0, irPort); +} + +void TCPConnection::AsyncConnect(uint32 irIP, uint16 irPort) { + if (ConnectionType != Outgoing) { + // If this code runs, we got serious problems + // Crash and burn. + ThrowError("TCPConnection::AsyncConnect() call on a Incomming connection object!"); + return; + } + if(!ConnectReady()) { +#if TCPN_DEBUG > 0 + printf("Trying to do async connect in invalid state %s\n", GetState()); +#endif + return; + } + MAsyncConnect.lock(); + if (pAsyncConnect) { + MAsyncConnect.unlock(); +#if TCPN_DEBUG > 0 + printf("Trying to do async connect when already doing one.\n"); +#endif + return; + } +#if TCPN_DEBUG > 0 + printf("Start async connect.\n"); +#endif + pAsyncConnect = true; + if(irIP != 0) + safe_delete_array(charAsyncConnect); + rIP = irIP; + rPort = irPort; + MAsyncConnect.unlock(); + if (!pRunLoop) { + pRunLoop = true; +#ifdef _WINDOWS + _beginthread(TCPConnectionLoop, 0, this); +#else + pthread_t thread; + pthread_create(&thread, NULL, TCPConnectionLoop, this); +#endif + } + return; +} + +bool TCPConnection::Connect(const char* irAddress, uint16 irPort, char* errbuf) { + if (errbuf) + errbuf[0] = 0; + uint32 tmpIP = ResolveIP(irAddress); + if (!tmpIP) { + if (errbuf) { +#ifdef _WINDOWS + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Couldnt resolve hostname. Error: %i", WSAGetLastError()); +#else + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Couldnt resolve hostname. Error #%i: %s", errno, strerror(errno)); +#endif + } + return false; + } + return ConnectIP(tmpIP, irPort, errbuf); +} + +bool TCPConnection::ConnectIP(uint32 in_ip, uint16 in_port, char* errbuf) { + if (errbuf) + errbuf[0] = 0; + if (ConnectionType != Outgoing) { + // If this code runs, we got serious problems + // Crash and burn. + ThrowError("TCPConnection::Connect() call on a Incomming connection object!"); + return false; + } + MState.lock(); + if (ConnectReady()) { + pState = TCPS_Connecting; + } else { + MState.unlock(); + SetAsyncConnect(false); + return false; + } + MState.unlock(); + if (!pRunLoop) { + pRunLoop = true; +#ifdef _WINDOWS + _beginthread(TCPConnectionLoop, 0, this); +#else + pthread_t thread; + pthread_create(&thread, NULL, TCPConnectionLoop, this); +#endif + } + + connection_socket = INVALID_SOCKET; + struct sockaddr_in server_sin; +// struct in_addr in; + + if ((connection_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET || connection_socket == 0) { +#ifdef _WINDOWS + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Allocating socket failed. Error: %i", WSAGetLastError()); +#else + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): Allocating socket failed. Error: %s", strerror(errno)); +#endif + SetState(TCPS_Ready); + SetAsyncConnect(false); + return false; + } + server_sin.sin_family = AF_INET; + server_sin.sin_addr.s_addr = in_ip; + server_sin.sin_port = htons(in_port); + + // Establish a connection to the server socket. +#ifdef _WINDOWS + if (connect(connection_socket, (PSOCKADDR) &server_sin, sizeof (server_sin)) == SOCKET_ERROR) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): connect() failed. Error: %i", WSAGetLastError()); + closesocket(connection_socket); + connection_socket = 0; + SetState(TCPS_Ready); + SetAsyncConnect(false); + return false; + } +#else + if (connect(connection_socket, (struct sockaddr *) &server_sin, sizeof (server_sin)) == SOCKET_ERROR) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::Connect(): connect() failed. Error: %s", strerror(errno)); + close(connection_socket); + connection_socket = 0; + SetState(TCPS_Ready); + SetAsyncConnect(false); + return false; + } +#endif + int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k + setsockopt(connection_socket, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize)); +#ifdef _WINDOWS + unsigned long nonblocking = 1; + ioctlsocket(connection_socket, FIONBIO, &nonblocking); +#else + fcntl(connection_socket, F_SETFL, O_NONBLOCK); +#endif + + SetEcho(false); + ClearBuffers(); + + rIP = in_ip; + rPort = in_port; + SetState(TCPS_Connected); + SetAsyncConnect(false); + return true; +} + +void TCPConnection::ClearBuffers() { + LockMutex lock1(&MSendQueue); + LockMutex lock3(&MRunLoop); + LockMutex lock4(&MState); + safe_delete_array(recvbuf); + safe_delete_array(sendbuf); + + char* line = 0; + while ((line = LineOutQueue.pop())) + safe_delete_array(line); +} + +bool TCPConnection::CheckNetActive() { + MState.lock(); + if (pState == TCPS_Connected || pState == TCPS_Disconnecting) { + MState.unlock(); + return true; + } + MState.unlock(); + return false; +} + +/* This is always called from an IO thread. Either the server socket's thread, or a + * special thread we create when we make an outbound connection. */ +bool TCPConnection::Process() { + char errbuf[TCPConnection_ErrorBufferSize]; + switch(GetState()) { + case TCPS_Ready: + case TCPS_Connecting: + if (ConnectionType == Outgoing) { + if (GetAsyncConnect()) { + if (charAsyncConnect) + rIP = ResolveIP(charAsyncConnect); + ConnectIP(rIP, rPort); + } + } + return(true); + + case TCPS_Connected: + // only receive data in the connected state, no others... + if (!RecvData(errbuf)) { + struct in_addr in; + in.s_addr = GetrIP(); + //cout << inet_ntoa(in) << ":" << GetrPort() << ": " << errbuf << endl; + return false; + } + /* we break to do the send */ + break; + + case TCPS_Disconnecting: { + //waiting for any sending data to go out... + MSendQueue.lock(); + if(sendbuf) { + if(sendbuf_used > 0) { + //something left to send, keep processing... + MSendQueue.unlock(); + break; + } + //else, send buffer is empty. + safe_delete_array(sendbuf); + } //else, no send buffer, we are done. + MSendQueue.unlock(); + } + /* Fallthrough */ + + case TCPS_Disconnected: + FinishDisconnect(); + MRunLoop.lock(); + pRunLoop = false; + MRunLoop.unlock(); +// SetState(TCPS_Ready); //reset the state in case they want to use it again... + return(false); + + case TCPS_Closing: + //I dont understand this state... + + case TCPS_Error: + MRunLoop.lock(); + pRunLoop = false; + MRunLoop.unlock(); + return(false); + } + + /* we get here in connected or disconnecting with more data to send */ + + bool sent_something = false; + if (!SendData(sent_something, errbuf)) { + struct in_addr in; + in.s_addr = GetrIP(); + cout << inet_ntoa(in) << ":" << GetrPort() << ": " << errbuf << endl; + return false; + } + + return true; +} + +bool TCPConnection::RecvData(char* errbuf) { + if (errbuf) + errbuf[0] = 0; + if (!Connected()) { + return false; + } + + int status = 0; + if (recvbuf == 0) { + recvbuf = new uchar[5120]; + recvbuf_size = 5120; + recvbuf_used = 0; + recvbuf_echo = 0; + } + else if ((recvbuf_size - recvbuf_used) < 2048) { + uchar* tmpbuf = new uchar[recvbuf_size + 5120]; + memcpy(tmpbuf, recvbuf, recvbuf_used); + recvbuf_size += 5120; + safe_delete_array(recvbuf); + recvbuf = tmpbuf; + if (recvbuf_size >= MaxTCPReceiveBuffferSize) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): recvbuf_size >= MaxTCPReceiveBuffferSize"); + return false; + } + } + + status = recv(connection_socket, (char *) &recvbuf[recvbuf_used], (recvbuf_size - recvbuf_used), 0); + + if (status >= 1) { +#if TCPN_LOG_RAW_DATA_IN >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Read " << status << " bytes from network. (recvbuf_used = " << recvbuf_used << ") " << inet_ntoa(in) << ":" << GetrPort(); + cout << endl; + #if TCPN_LOG_RAW_DATA_IN == 2 + int32 tmp = status; + if (tmp > 32) + tmp = 32; + DumpPacket(&recvbuf[recvbuf_used], status); + #elif TCPN_LOG_RAW_DATA_IN >= 3 + DumpPacket(&recvbuf[recvbuf_used], status); + #endif +#endif + recvbuf_used += status; + if (!ProcessReceivedData(errbuf)) + return false; + } + else if (status == SOCKET_ERROR) { +#ifdef _WINDOWS + if (!(WSAGetLastError() == WSAEWOULDBLOCK)) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Error: %i", WSAGetLastError()); + return false; + } +#else + if (!(errno == EWOULDBLOCK)) { + if (errbuf) + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Error: %s", strerror(errno)); + return false; + } +#endif + } else if (status == 0) { + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::RecvData(): Connection closed"); + return false; + } + + return true; +} + + +bool TCPConnection::GetEcho() { + bool ret; + ret = pEcho; + return ret; +} + +void TCPConnection::SetEcho(bool iValue) { + pEcho = iValue; +} + +bool TCPConnection::ProcessReceivedData(char* errbuf) { + if (errbuf) + errbuf[0] = 0; + if (!recvbuf) + return true; +#if TCPN_DEBUG_Console >= 4 + if (recvbuf_used) { + cout << "Starting Processing: recvbuf=" << recvbuf_used << endl; + DumpPacket(recvbuf, recvbuf_used); + } +#endif + for (int i=0; i < recvbuf_used; i++) { + if (GetEcho() && i >= recvbuf_echo) { + Send(&recvbuf[i], 1); + recvbuf_echo = i + 1; + } + switch(recvbuf[i]) { + case 0: { // 0 is the code for clear buffer + if (i==0) { + recvbuf_used--; + recvbuf_echo--; + memmove(recvbuf, &recvbuf[1], recvbuf_used); + i = -1; + } else { + if (i == recvbuf_used) { + safe_delete_array(recvbuf); + i = -1; + } + else { + uchar* tmpdel = recvbuf; + recvbuf = new uchar[recvbuf_size]; + memcpy(recvbuf, &tmpdel[i+1], recvbuf_used-i); + recvbuf_used -= i + 1; + recvbuf_echo -= i + 1; + safe_delete_array(tmpdel); + i = -1; + } + } +#if TCPN_DEBUG_Console >= 5 + cout << "Removed 0x00" << endl; + if (recvbuf_used) { + cout << "recvbuf left: " << recvbuf_used << endl; + DumpPacket(recvbuf, recvbuf_used); + } + else + cout << "recbuf left: None" << endl; +#endif + m_previousLineEnd = false; + break; + } + case 10: + case 13: // newline marker + { + char *line = NULL; + if (i==0) { // empty line + if(!m_previousLineEnd) { + //char right before this was NOT a CR, report the empty line. + line = new char[1]; + line[0] = '\0'; + m_previousLineEnd = true; + } else { + m_previousLineEnd = false; + } + recvbuf_used--; + recvbuf_echo--; + memcpy(recvbuf, &recvbuf[1], recvbuf_used); + i = -1; + } else { + line = new char[i+1]; + memset(line, 0, i+1); + memcpy(line, recvbuf, i); +#if TCPN_DEBUG_Console >= 3 + cout << "Line Out: " << endl; + DumpPacket((uchar*) line, i); +#endif + //line[i] = 0; + uchar* tmpdel = recvbuf; + recvbuf = new uchar[recvbuf_size]; + recvbuf_used -= i+1; + recvbuf_echo -= i+1; + memcpy(recvbuf, &tmpdel[i+1], recvbuf_used); +#if TCPN_DEBUG_Console >= 5 + cout << "i+1=" << i+1 << endl; + if (recvbuf_used) { + cout << "recvbuf left: " << recvbuf_used << endl; + DumpPacket(recvbuf, recvbuf_used); + } + else + cout << "recbuf left: None" << endl; +#endif + safe_delete_array(tmpdel); + i = -1; + m_previousLineEnd = true; + } + + + if(line != NULL) { + bool finish_proc = false; + finish_proc = LineOutQueuePush(line); + if(finish_proc) + return(true); //break early as requested by LineOutQueuePush + } + + break; + } + case 8: // backspace + { + if (i==0) { // nothin to backspace + recvbuf_used--; + recvbuf_echo--; + memmove(recvbuf, &recvbuf[1], recvbuf_used); + i = -1; + } else { + uchar* tmpdel = recvbuf; + recvbuf = new uchar[recvbuf_size]; + memcpy(recvbuf, tmpdel, i-1); + memcpy(&recvbuf[i-1], &tmpdel[i+1], recvbuf_used-i); + recvbuf_used -= 2; + recvbuf_echo -= 2; + safe_delete_array(tmpdel); + i -= 2; + } + break; + m_previousLineEnd = false; + } + default: + m_previousLineEnd = false; + } + } + if (recvbuf_used < 0) + safe_delete_array(recvbuf); + return true; +} + +bool TCPConnection::SendData(bool &sent_something, char* errbuf) { + if (errbuf) + errbuf[0] = 0; + /************ Get first send packet on queue and send it! ************/ + uchar* data = 0; + int32 size = 0; + int status = 0; + if (ServerSendQueuePop(&data, &size)) { +#ifdef _WINDOWS + status = send(connection_socket, (const char *) data, size, 0); +#else + status = send(connection_socket, data, size, MSG_NOSIGNAL); + if(errno==EPIPE) status = SOCKET_ERROR; +#endif + if (status >= 1) { +#if TCPN_LOG_RAW_DATA_OUT >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Wrote " << status << " bytes to network. " << inet_ntoa(in) << ":" << GetrPort(); + cout << endl; + #if TCPN_LOG_RAW_DATA_OUT == 2 + int32 tmp = status; + if (tmp > 32) + tmp = 32; + DumpPacket(data, status); + #elif TCPN_LOG_RAW_DATA_OUT >= 3 + DumpPacket(data, status); + #endif +#endif + sent_something = true; + if (status < (signed)size) { +#if TCPN_LOG_RAW_DATA_OUT >= 1 + struct in_addr in; + in.s_addr = GetrIP(); + CoutTimestamp(true); + cout << ": Pushed " << (size - status) << " bytes back onto the send queue. " << inet_ntoa(in) << ":" << GetrPort(); + cout << endl; +#endif + // If there's network congestion, the number of bytes sent can be less than + // what we tried to give it... Push the extra back on the queue for later + ServerSendQueuePushFront(&data[status], size - status); + } + else if (status > (signed)size) { + ThrowError("TCPConnection::SendData(): WTF! status > size"); + return false; + } + // else if (status == size) {} + } + else { + ServerSendQueuePushFront(data, size); + } + + safe_delete_array(data); + if (status == SOCKET_ERROR) { +#ifdef _WINDOWS + if (WSAGetLastError() != WSAEWOULDBLOCK) +#else + if (errno != EWOULDBLOCK) +#endif + { + if (errbuf) { +#ifdef _WINDOWS + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::SendData(): send(): Errorcode: %i", WSAGetLastError()); +#else + snprintf(errbuf, TCPConnection_ErrorBufferSize, "TCPConnection::SendData(): send(): Errorcode: %s", strerror(errno)); +#endif + } + + //if we get an error while disconnecting, just jump to disconnected + MState.lock(); + if(pState == TCPS_Disconnecting) + pState = TCPS_Disconnected; + MState.unlock(); + + return false; + } + } + } + return true; +} + +ThreadReturnType TCPConnection::TCPConnectionLoop(void* tmp) { +#ifdef _WINDOWS + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); +#endif + if (tmp == 0) { + ThrowError("TCPConnectionLoop(): tmp = 0!"); + THREAD_RETURN(NULL); + } + TCPConnection* tcpc = (TCPConnection*) tmp; +#ifndef WIN32 + _log(COMMON__THREADS, "Starting TCPConnectionLoop with thread ID %d", pthread_self()); +#endif + tcpc->MLoopRunning.lock(); + while (tcpc->RunLoop()) { + Sleep(LOOP_GRANULARITY); + if (!tcpc->ConnectReady()) { + _CP(TCPConnectionLoop); + if (!tcpc->Process()) { + //the processing loop has detecting an error.. + //we want to drop the link immediately, so we clear buffers too. + tcpc->ClearBuffers(); + tcpc->Disconnect(); + } + Sleep(1); + } + else if (tcpc->GetAsyncConnect()) { + _CP(TCPConnectionLoop); + if (tcpc->charAsyncConnect) + tcpc->Connect(tcpc->charAsyncConnect, tcpc->GetrPort()); + else + tcpc->ConnectIP(tcpc->GetrIP(), tcpc->GetrPort()); + tcpc->SetAsyncConnect(false); + } + else + Sleep(10); //nothing to do. + } + tcpc->MLoopRunning.unlock(); + +#ifndef WIN32 + _log(COMMON__THREADS, "Ending TCPConnectionLoop with thread ID %d", pthread_self()); +#endif + + THREAD_RETURN(NULL); +} + +bool TCPConnection::RunLoop() { + bool ret; + MRunLoop.lock(); + ret = pRunLoop; + MRunLoop.unlock(); + return ret; +} + + + + + diff --git a/common/TCPConnection.h b/common/TCPConnection.h new file mode 100644 index 000000000..d2f942c5e --- /dev/null +++ b/common/TCPConnection.h @@ -0,0 +1,181 @@ +/* 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 TCP_CONNECTION_H +#define TCP_CONNECTION_H +/* + Parent classes for interserver TCP Communication. + -Quagmire +*/ + +#ifdef _WINDOWS + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include + #define INVALID_SOCKET -1 + #define SOCKET_ERROR -1 + #include "unix.h" + +#endif + +#include "types.h" +#include "Mutex.h" +#include "queue.h" +#include "MiscFunctions.h" + +class BaseTCPServer; +class ServerPacket; + +#define TCPConnection_ErrorBufferSize 1024 +#define MaxTCPReceiveBuffferSize 524288 + + +#ifndef DEF_eConnectionType +#define DEF_eConnectionType +enum eConnectionType {Incomming, Outgoing}; +#endif + + +class TCPConnection { +protected: + typedef enum { + TCPS_Ready = 0, + TCPS_Connecting = 1, + TCPS_Connected = 100, + TCPS_Disconnecting = 200, //I do not know the difference between Disconnecting and Closing + TCPS_Disconnected = 201, + TCPS_Closing = 250, + TCPS_Error = 255 + } State_t; + +public: + //socket created by a server (incoming) + TCPConnection(uint32 ID, SOCKET iSock, uint32 irIP, uint16 irPort); + //socket created to connect to a server (outgoing) + TCPConnection(); // for outgoing connections + + virtual ~TCPConnection(); + + // Functions for outgoing connections + bool Connect(const char* irAddress, uint16 irPort, char* errbuf = 0); + virtual bool ConnectIP(uint32 irIP, uint16 irPort, char* errbuf = 0); + void AsyncConnect(const char* irAddress, uint16 irPort); + void AsyncConnect(uint32 irIP, uint16 irPort); + virtual void Disconnect(); + + bool Send(const uchar* data, int32 size); + + char* PopLine(); //returns ownership of allocated byte array + inline uint32 GetrIP() const { return rIP; } + inline uint16 GetrPort() const { return rPort; } + virtual State_t GetState() const; + inline bool Connected() const { return (GetState() == TCPS_Connected); } + bool ConnectReady() const; + void Free(); // Inform TCPServer that this connection object is no longer referanced + + inline uint32 GetID() const { return id; } + + bool GetEcho(); + void SetEcho(bool iValue); + bool GetSockName(char *host, uint16 *port); + + //should only be used by TCPServer: + bool CheckNetActive(); + inline bool IsFree() const { return pFree; } + virtual bool Process(); + +protected: + friend class BaseTCPServer; + void SetState(State_t iState); + + static ThreadReturnType TCPConnectionLoop(void* tmp); +// SOCKET sock; + bool RunLoop(); + Mutex MLoopRunning; + Mutex MAsyncConnect; + bool GetAsyncConnect(); + bool SetAsyncConnect(bool iValue); + char* charAsyncConnect; + bool pAsyncConnect; //this flag should really be turned into a state instead. + + virtual bool ProcessReceivedData(char* errbuf = 0); + virtual bool SendData(bool &sent_something, char* errbuf = 0); + virtual bool RecvData(char* errbuf = 0); + + virtual void ClearBuffers(); + + + bool m_previousLineEnd; + + eConnectionType ConnectionType; + Mutex MRunLoop; + bool pRunLoop; + + SOCKET connection_socket; + uint32 id; + uint32 rIP; + uint16 rPort; // host byte order + bool pFree; + + mutable Mutex MState; + State_t pState; + + //text based line out queue. + Mutex MLineOutQueue; + virtual bool LineOutQueuePush(char* line); //this is really kinda a hack for the transition to packet mode. Returns true to stop processing the output. + MyQueue LineOutQueue; + + uchar* recvbuf; + int32 recvbuf_size; + int32 recvbuf_used; + + int32 recvbuf_echo; + volatile bool pEcho; + + Mutex MSendQueue; + uchar* sendbuf; + int32 sendbuf_size; + int32 sendbuf_used; + bool ServerSendQueuePop(uchar** data, int32* size); + bool ServerSendQueuePopForce(uchar** data, int32* size); //does a lock() instead of a trylock() + void ServerSendQueuePushEnd(const uchar* data, int32 size); + void ServerSendQueuePushEnd(uchar** data, int32 size); + void ServerSendQueuePushFront(uchar* data, int32 size); + +private: + void FinishDisconnect(); +}; + + +#endif + + + diff --git a/common/TCPServer.cpp b/common/TCPServer.cpp new file mode 100644 index 000000000..6bd714d44 --- /dev/null +++ b/common/TCPServer.cpp @@ -0,0 +1,234 @@ + + +#include "debug.h" +#include "TCPServer.h" +#include +#include +#include + +#ifdef _WINDOWS + #include +#else + #include + #include + #include + #include + #include + #define INVALID_SOCKET -1 + #define SOCKET_ERROR -1 +#endif + + +#define SERVER_LOOP_GRANULARITY 3 //# of ms between checking our socket/queues + + + + +BaseTCPServer::BaseTCPServer(uint16 in_port) { + NextID = 1; + pPort = in_port; + sock = 0; + pRunLoop = true; +#ifdef _WINDOWS + _beginthread(BaseTCPServer::TCPServerLoop, 0, this); +#else + pthread_t thread; + pthread_create(&thread, NULL, &BaseTCPServer::TCPServerLoop, this); +#endif +} + +BaseTCPServer::~BaseTCPServer() { + StopLoopAndWait(); +} + +void BaseTCPServer::StopLoopAndWait() { + MRunLoop.lock(); + if(pRunLoop) { + pRunLoop = false; + MRunLoop.unlock(); + //wait for loop to stop. + MLoopRunning.lock(); + MLoopRunning.unlock(); + } else { + MRunLoop.unlock(); + } +} + +bool BaseTCPServer::RunLoop() { + bool ret; + MRunLoop.lock(); + ret = pRunLoop; + MRunLoop.unlock(); + return ret; +} + +ThreadReturnType BaseTCPServer::TCPServerLoop(void* tmp) { +#ifdef _WINDOWS + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); +#endif + if (tmp == 0) { +// ThrowError("BaseTCPServerLoop(): tmp = 0!"); + THREAD_RETURN(NULL); + } + BaseTCPServer* tcps = (BaseTCPServer*) tmp; + +#ifndef WIN32 + _log(COMMON__THREADS, "Starting TCPServerLoop with thread ID %d", pthread_self()); +#endif + + tcps->MLoopRunning.lock(); + while (tcps->RunLoop()) { + _CP(BaseTCPServerLoop); + Sleep(SERVER_LOOP_GRANULARITY); + tcps->Process(); + } + tcps->MLoopRunning.unlock(); + +#ifndef WIN32 + _log(COMMON__THREADS, "Ending TCPServerLoop with thread ID %d", pthread_self()); +#endif + + THREAD_RETURN(NULL); +} + +void BaseTCPServer::Process() { + ListenNewConnections(); +} + +void BaseTCPServer::ListenNewConnections() { + SOCKET tmpsock; + struct sockaddr_in from; + struct in_addr in; + unsigned int fromlen; + unsigned short port; + + from.sin_family = AF_INET; + fromlen = sizeof(from); + LockMutex lock(&MSock); + if (!sock) + return; + + // Check for pending connects +#ifdef _WINDOWS + unsigned long nonblocking = 1; + while ((tmpsock = accept(sock, (struct sockaddr*) &from, (int *) &fromlen)) != INVALID_SOCKET) { + ioctlsocket (tmpsock, FIONBIO, &nonblocking); +#else +#ifdef __CYGWIN__ + while ((tmpsock = accept(sock, (struct sockaddr *) &from, (int *) &fromlen)) != INVALID_SOCKET) { +#else + while ((tmpsock = accept(sock, (struct sockaddr*) &from, &fromlen)) != INVALID_SOCKET) { +#endif + fcntl(tmpsock, F_SETFL, O_NONBLOCK); +#endif + int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k + setsockopt(tmpsock, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize)); + port = from.sin_port; + in.s_addr = from.sin_addr.s_addr; + + // New TCP connection, this must consume the socket. + CreateNewConnection(GetNextID(), tmpsock, in.s_addr, ntohs(from.sin_port)); + } +} + +bool BaseTCPServer::Open(uint16 in_port, char* errbuf) { + if (errbuf) + errbuf[0] = 0; + LockMutex lock(&MSock); + if (sock != 0) { + if (errbuf) + snprintf(errbuf, TCPServer_ErrorBufferSize, "Listening socket already open"); + return false; + } + if (in_port != 0) { + pPort = in_port; + } + +#ifdef _WINDOWS + SOCKADDR_IN address; + unsigned long nonblocking = 1; +#else + struct sockaddr_in address; +#endif + int reuse_addr = 1; + +// 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 TCP port for new TCP connections + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == INVALID_SOCKET) { + if (errbuf) + snprintf(errbuf, TCPServer_ErrorBufferSize, "socket(): INVALID_SOCKET"); + return false; + } + +// Quag: dont think following is good stuff for TCP, good for UDP +// Mis: SO_REUSEADDR shouldn't be a problem for tcp--allows you to restart +// without waiting for conns in TIME_WAIT to die + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse_addr, sizeof(reuse_addr)); + + + if (bind(sock, (struct sockaddr *) &address, sizeof(address)) < 0) { +#ifdef _WINDOWS + closesocket(sock); +#else + close(sock); +#endif + sock = 0; + if (errbuf) + sprintf(errbuf, "bind(): <0"); + return false; + } + + int bufsize = 64 * 1024; // 64kbyte recieve buffer, up from default of 8k + setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize)); +#ifdef _WINDOWS + ioctlsocket (sock, FIONBIO, &nonblocking); +#else + fcntl(sock, F_SETFL, O_NONBLOCK); +#endif + + if (listen(sock, SOMAXCONN) == SOCKET_ERROR) { +#ifdef _WINDOWS + closesocket(sock); + if (errbuf) + snprintf(errbuf, TCPServer_ErrorBufferSize, "listen() failed, Error: %d", WSAGetLastError()); +#else + close(sock); + if (errbuf) + snprintf(errbuf, TCPServer_ErrorBufferSize, "listen() failed, Error: %s", strerror(errno)); +#endif + sock = 0; + return false; + } + + return true; +} + +void BaseTCPServer::Close() { + StopLoopAndWait(); + + LockMutex lock(&MSock); + if (sock) { +#ifdef _WINDOWS + closesocket(sock); +#else + close(sock); +#endif + } + sock = 0; +} + +bool BaseTCPServer::IsOpen() { + MSock.lock(); + bool ret = (bool) (sock != 0); + MSock.unlock(); + return ret; +} + + diff --git a/common/TCPServer.h b/common/TCPServer.h new file mode 100644 index 000000000..73b6dde6e --- /dev/null +++ b/common/TCPServer.h @@ -0,0 +1,135 @@ +#ifndef TCPSERVER_H_ +#define TCPSERVER_H_ + +#include "types.h" + +#include +#include + +#define TCPServer_ErrorBufferSize 1024 + +//this is the non-connection type specific server. +class BaseTCPServer { +public: + BaseTCPServer(uint16 iPort = 0); + virtual ~BaseTCPServer(); + + bool Open(uint16 iPort = 0, char* errbuf = 0); // opens the port + void Close(); // closes the port + bool IsOpen(); + inline uint16 GetPort() { return pPort; } + inline uint32 GetNextID() { return NextID++; } + +protected: + static ThreadReturnType TCPServerLoop(void* tmp); + + //factory method: + virtual void CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort) = 0; + + + virtual void Process(); + bool RunLoop(); + Mutex MLoopRunning; + + void StopLoopAndWait(); + + void ListenNewConnections(); + + uint32 NextID; + + Mutex MRunLoop; + bool pRunLoop; + + Mutex MSock; + SOCKET sock; + uint16 pPort; + +}; + +template +class TCPServer : public BaseTCPServer { +protected: + typedef typename std::vector vstore; + typedef typename std::vector::iterator vitr; +public: + TCPServer(uint16 iPort = 0) + : BaseTCPServer(iPort) { + } + + virtual ~TCPServer() { + StopLoopAndWait(); + + //im not sure what the right thing to do here is... + //we are freeing a connection which somebody likely has a pointer to.. + //but, we really shouldent ever get called anyhow.. + vitr cur, end; + cur = m_list.begin(); + end = m_list.end(); + for(; cur != end; cur++) { + delete *cur; + } + } + + T * NewQueuePop() { + T * ret = NULL; + MNewQueue.lock(); + if(!m_NewQueue.empty()) { + ret = m_NewQueue.front(); + m_NewQueue.pop(); + } + MNewQueue.unlock(); + return ret; + } + +protected: + virtual void Process() { + BaseTCPServer::Process(); + + vitr cur; + cur = m_list.begin(); + while(cur != m_list.end()) { + T *data = *cur; + if (data->IsFree() && (!data->CheckNetActive())) { + #if EQN_DEBUG >= 4 + cout << "TCPConnection Connection deleted." << endl; + #endif + delete data; + cur = m_list.erase(cur); + } else { + if (!data->Process()) + data->Disconnect(); + cur++; + } + } + } + + void AddConnection(T *con) { + m_list.push_back(con); + MNewQueue.lock(); + m_NewQueue.push(con); + MNewQueue.unlock(); + } + + //queue of new connections, for the app to pull from + Mutex MNewQueue; + std::queue m_NewQueue; + + vstore m_list; +}; + + +#endif /*TCPSERVER_H_*/ + + + + + + + + + + + + + + diff --git a/common/XMLParser.cpp b/common/XMLParser.cpp new file mode 100644 index 000000000..21d02a8e1 --- /dev/null +++ b/common/XMLParser.cpp @@ -0,0 +1,101 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "debug.h" +#include "XMLParser.h" + +XMLParser::XMLParser() { + ParseOkay = false; +} + +bool XMLParser::ParseFile(const char *file, const char *root_ele) { + map::iterator handler; + TiXmlDocument doc( file ); + if(!doc.LoadFile()) { + printf("Unable to load '%s': %s\n", file, doc.ErrorDesc()); + return(false); + } + + TiXmlElement *root = doc.FirstChildElement( root_ele ); + if(root == NULL) { + printf("Unable to find root '%s' in %s\n",root_ele, file); + return(false); + } + + ParseOkay=true; + + TiXmlNode *main_element = NULL; + while( (main_element = root->IterateChildren( main_element )) ) { + if(main_element->Type() != TiXmlNode::ELEMENT) + continue; //skip crap we dont care about + TiXmlElement *ele = (TiXmlElement *) main_element; + + handler=Handlers.find(ele->Value()); + if (handler!=Handlers.end() && handler->second) { + ElementHandler h=handler->second; + + /* + * + * This is kinda a sketchy operation here, since all of these + * element handler methods will be functions in child classes. + * This essentially causes us to do an un-checkable (and hence + * un-handle-properly-able) cast down to the child class. This + * WILL BREAK if any children classes do multiple inheritance. + * + * + */ + + (this->*h)(ele); + } else { + //unhandled element.... do nothing for now + } + + } + + return(ParseOkay); +} + +const char *XMLParser::ParseTextBlock(TiXmlNode *within, const char *name, bool optional) { + TiXmlElement * txt = within->FirstChildElement(name); + if(txt == NULL) { + if(!optional) { + printf("Unable to find a '%s' element on %s element at line %d\n", name, within->Value(), within->Row()); + ParseOkay=false; + } + return(NULL); + } + TiXmlNode *contents = txt->FirstChild(); + if(contents == NULL || contents->Type() != TiXmlNode::TEXT) { + if(!optional) + printf("Node '%s' was expected to be a text element in %s element at line %d\n", name, txt->Value(), txt->Row()); + return(NULL); + } + return(contents->Value()); +} + +const char *XMLParser::GetText(TiXmlNode *within, bool optional) { + TiXmlNode *contents = within->FirstChild(); + if(contents == NULL || contents->Type() != TiXmlNode::TEXT) { + if(!optional) { + printf("Node was expected to be a text element in %s element at line %d\n", within->Value(), within->Row()); + ParseOkay=false; + } + return(NULL); + } + return(contents->Value()); +} + diff --git a/common/XMLParser.h b/common/XMLParser.h new file mode 100644 index 000000000..37e1bb915 --- /dev/null +++ b/common/XMLParser.h @@ -0,0 +1,59 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 XMLParser_H +#define XMLParser_H + +#include "debug.h" +#include "tinyxml/tinyxml.h" +#include "../common/types.h" + +#include +#include +using namespace std; + + + +/* + * See note in XMLParser::ParseFile() before inheriting this class. + */ +class XMLParser { +public: + typedef void (XMLParser::*ElementHandler)(TiXmlElement *ele); + + XMLParser(); + virtual ~XMLParser() {} + + bool ParseFile(const char *file, const char *root_ele); + bool ParseStatus() const { return ParseOkay; } + +protected: + const char *ParseTextBlock(TiXmlNode *within, const char *name, bool optional = false); + const char *GetText(TiXmlNode *within, bool optional = false); + + map Handlers; + + bool ParseOkay; + +}; + + +#endif + + + + diff --git a/common/ZoneNumbers.h b/common/ZoneNumbers.h new file mode 100644 index 000000000..466ffc493 --- /dev/null +++ b/common/ZoneNumbers.h @@ -0,0 +1,490 @@ +#include "../common/types.h" + +inline const char* StaticGetZoneName(uint32 zoneID) { + // @merth: I did the following query to retrieve these (following by a simple find/replace) + // select concat('case ', zoneidnumber), concat(short_name, '";') from zone order by zoneidnumber; + switch (zoneID) { + case 1: return "qeynos"; + case 2: return "qeynos2"; + case 3: return "qrg"; + case 4: return "qeytoqrg"; + case 5: return "highpass"; + case 6: return "highkeep"; + case 8: return "freportn"; + case 9: return "freportw"; + case 10: return "freporte"; + case 11: return "runnyeye"; + case 12: return "qey2hh1"; + case 13: return "northkarana"; + case 14: return "southkarana"; + case 15: return "eastkarana"; + case 16: return "beholder"; + case 17: return "blackburrow"; + case 18: return "paw"; + case 19: return "rivervale"; + case 20: return "kithicor"; + case 21: return "commons"; + case 22: return "ecommons"; + case 23: return "erudnint"; + case 24: return "erudnext"; + case 25: return "nektulos"; + case 26: return "cshome"; + case 27: return "lavastorm"; + case 28: return "nektropos"; + case 29: return "halas"; + case 30: return "everfrost"; + case 31: return "soldunga"; + case 32: return "soldungb"; + case 33: return "misty"; + case 34: return "nro"; + case 35: return "sro"; + case 36: return "befallen"; + case 37: return "oasis"; + case 38: return "tox"; + case 39: return "hole"; + case 40: return "neriaka"; + case 41: return "neriakb"; + case 42: return "neriakc"; + case 43: return "neriakd"; + case 44: return "najena"; + case 45: return "qcat"; + case 46: return "innothule"; + case 47: return "feerrott"; + case 48: return "cazicthule"; + case 49: return "oggok"; + case 50: return "rathemtn"; + case 51: return "lakerathe"; + case 52: return "grobb"; + case 53: return "aviak"; + case 54: return "gfaydark"; + case 55: return "akanon"; + case 56: return "steamfont"; + case 57: return "lfaydark"; + case 58: return "crushbone"; + case 59: return "mistmoore"; + case 60: return "kaladima"; + case 61: return "felwithea"; + case 62: return "felwitheb"; + case 63: return "unrest"; + case 64: return "kedge"; + case 65: return "guktop"; + case 66: return "gukbottom"; + case 67: return "kaladimb"; + case 68: return "butcher"; + case 69: return "oot"; + case 70: return "cauldron"; + case 71: return "airplane"; + case 72: return "fearplane"; + case 73: return "permafrost"; + case 74: return "kerraridge"; + case 75: return "paineel"; + case 76: return "hateplane"; + case 77: return "arena"; + case 78: return "fieldofbone"; + case 79: return "warslikswood"; + case 80: return "soltemple"; + case 81: return "droga"; + case 82: return "cabwest"; + case 83: return "swampofnohope"; + case 84: return "firiona"; + case 85: return "lakeofillomen"; + case 86: return "dreadlands"; + case 87: return "burningwood"; + case 88: return "kaesora"; + case 89: return "sebilis"; + case 90: return "citymist"; + case 91: return "skyfire"; + case 92: return "frontiermtns"; + case 93: return "overthere"; + case 94: return "emeraldjungle"; + case 95: return "trakanon"; + case 96: return "timorous"; + case 97: return "kurn"; + case 98: return "erudsxing"; + case 100: return "stonebrunt"; + case 101: return "warrens"; + case 102: return "karnor"; + case 103: return "chardok"; + case 104: return "dalnir"; + case 105: return "charasis"; + case 106: return "cabeast"; + case 107: return "nurga"; + case 108: return "veeshan"; + case 109: return "veksar"; + case 110: return "iceclad"; + case 111: return "frozenshadow"; + case 112: return "velketor"; + case 113: return "kael"; + case 114: return "skyshrine"; + case 115: return "thurgadina"; + case 116: return "eastwastes"; + case 117: return "cobaltscar"; + case 118: return "greatdivide"; + case 119: return "wakening"; + case 120: return "westwastes"; + case 121: return "crystal"; + case 123: return "necropolis"; + case 124: return "templeveeshan"; + case 125: return "sirens"; + case 126: return "mischiefplane"; + case 127: return "growthplane"; + case 128: return "sleeper"; + case 129: return "thurgadinb"; + case 130: return "erudsxing2"; + case 150: return "shadowhaven"; + case 151: return "bazaar"; + case 152: return "nexus"; + case 153: return "echo"; + case 154: return "acrylia"; + case 155: return "sharvahl"; + case 156: return "paludal"; + case 157: return "fungusgrove"; + case 158: return "vexthal"; + case 159: return "sseru"; + case 160: return "katta"; + case 161: return "netherbian"; + case 162: return "ssratemple"; + case 163: return "griegsend"; + case 164: return "thedeep"; + case 165: return "shadeweaver"; + case 166: return "hollowshade"; + case 167: return "grimling"; + case 168: return "mseru"; + case 169: return "letalis"; + case 170: return "twilight"; + case 171: return "thegrey"; + case 172: return "tenebrous"; + case 173: return "maiden"; + case 174: return "dawnshroud"; + case 175: return "scarlet"; + case 176: return "umbral"; + case 179: return "akheva"; + case 180: return "arena2"; + case 181: return "jaggedpine"; + case 182: return "nedaria"; + case 183: return "tutorial"; + case 184: return "load"; + case 185: return "load2"; + case 186: return "hateplaneb"; + case 187: return "shadowrest"; + case 188: return "tutoriala"; + case 189: return "tutorialb"; + case 190: return "clz"; + case 200: return "codecay"; + case 201: return "pojustice"; + case 202: return "poknowledge"; + case 203: return "potranquility"; + case 204: return "ponightmare"; + case 205: return "podisease"; + case 206: return "poinnovation"; + case 207: return "potorment"; + case 208: return "povalor"; + case 209: return "bothunder"; + case 210: return "postorms"; + case 211: return "hohonora"; + case 212: return "solrotower"; + case 213: return "powar"; + case 214: return "potactics"; + case 215: return "poair"; + case 216: return "powater"; + case 217: return "pofire"; + case 218: return "poeartha"; + case 219: return "potimea"; + case 220: return "hohonorb"; + case 221: return "nightmareb"; + case 222: return "poearthb"; + case 223: return "potimeb"; + case 224: return "gunthak"; + case 225: return "dulak"; + case 226: return "torgiran"; + case 227: return "nadox"; + case 228: return "hatesfury"; + case 229: return "guka"; + case 230: return "ruja"; + case 231: return "taka"; + case 232: return "mira"; + case 233: return "mmca"; + case 234: return "gukb"; + case 235: return "rujb"; + case 236: return "takb"; + case 237: return "mirb"; + case 238: return "mmcb"; + case 239: return "gukc"; + case 240: return "rujc"; + case 241: return "takc"; + case 242: return "mirc"; + case 243: return "mmcc"; + case 244: return "gukd"; + case 245: return "rujd"; + case 246: return "takd"; + case 247: return "mird"; + case 248: return "mmcd"; + case 249: return "guke"; + case 250: return "ruje"; + case 251: return "take"; + case 252: return "mire"; + case 253: return "mmce"; + case 254: return "gukf"; + case 255: return "rujf"; + case 256: return "takf"; + case 257: return "mirf"; + case 258: return "mmcf"; + case 259: return "gukg"; + case 260: return "rujg"; + case 261: return "takg"; + case 262: return "mirg"; + case 263: return "mmcg"; + case 264: return "gukh"; + case 265: return "rujh"; + case 266: return "takh"; + case 267: return "mirh"; + case 268: return "mmch"; + case 269: return "ruji"; + case 270: return "taki"; + case 271: return "miri"; + case 272: return "mmci"; + case 273: return "rujj"; + case 274: return "takj"; + case 275: return "mirj"; + case 276: return "mmcj"; + case 277: return "chardokb"; + case 278: return "soldungc"; + case 279: return "abysmal"; + case 280: return "natimbi"; + case 281: return "qinimi"; + case 282: return "riwwi"; + case 283: return "barindu"; + case 284: return "ferubi"; + case 285: return "snpool"; + case 286: return "snlair"; + case 287: return "snplant"; + case 288: return "sncrematory"; + case 289: return "tipt"; + case 290: return "vxed"; + case 291: return "yxtta"; + case 292: return "uqua"; + case 293: return "kodtaz"; + case 294: return "ikkinz"; + case 295: return "qvic"; + case 296: return "inktuta"; + case 297: return "txevu"; + case 298: return "tacvi"; + case 299: return "qvicb"; + case 300: return "wallofslaughter"; + case 301: return "bloodfields"; + case 302: return "draniksscar"; + case 303: return "causeway"; + case 304: return "chambersa"; + case 305: return "chambersb"; + case 306: return "chambersc"; + case 307: return "chambersd"; + case 308: return "chamberse"; + case 309: return "chambersf"; + case 316: return "provinggrounds"; + case 317: return "anguish"; + case 318: return "dranikhollowsa"; + case 319: return "dranikhollowsb"; + case 320: return "dranikhollowsc"; + case 328: return "dranikcatacombsa"; + case 329: return "dranikcatacombsb"; + case 330: return "dranikcatacombsc"; + case 331: return "draniksewersa"; + case 332: return "draniksewersb"; + case 333: return "draniksewersc"; + case 334: return "riftseekers"; + case 335: return "harbingers"; + case 336: return "dranik"; + case 337: return "broodlands"; + case 338: return "stillmoona"; + case 339: return "stillmoonb"; + case 340: return "thundercrest"; + case 341: return "delvea"; + case 342: return "delveb"; + case 343: return "thenest"; + case 344: return "guildlobby"; + case 345: return "guildhall"; + case 346: return "barter"; + case 347: return "illsalin"; + case 348: return "illsalina"; + case 349: return "illsalinb"; + case 350: return "illsalinc"; + case 351: return "dreadspire"; + case 354: return "drachnidhive"; + case 355: return "drachnidhivea"; + case 356: return "drachnidhiveb"; + case 357: return "drachnidhivec"; + case 358: return "westkorlach"; + case 359: return "westkorlacha"; + case 360: return "westkorlachb"; + case 361: return "westkorlachc"; + case 362: return "eastkorlach"; + case 363: return "eastkorlacha"; + case 364: return "shadowspine"; + case 365: return "corathus"; + case 366: return "corathusa"; + case 367: return "corathusb"; + case 368: return "nektulosa"; + case 369: return "arcstone"; + case 370: return "relic"; + case 371: return "skylance"; + case 372: return "devastation"; + case 373: return "devastationa"; + case 374: return "rage"; + case 375: return "ragea"; + case 376: return "takishruins"; + case 377: return "takishruinsa"; + case 378: return "elddar"; + case 379: return "elddara"; + case 380: return "theater"; + case 381: return "theatera"; + case 382: return "freeporteast"; + case 383: return "freeportwest"; + case 384: return "freeportsewers"; + case 385: return "freeportacademy"; + case 386: return "freeporttemple"; + case 387: return "freeportmilitia"; + case 388: return "freeportarena"; + case 389: return "freeportcityhall"; + case 390: return "freeporttheater"; + case 391: return "freeporthall"; + case 392: return "northro"; + case 393: return "southro"; + case 394: return "crescent"; + case 395: return "moors"; + case 396: return "stonehive"; + case 397: return "mesa"; + case 398: return "roost"; + case 399: return "steppes"; + case 400: return "icefall"; + case 401: return "valdeholm"; + case 402: return "frostcrypt"; + case 403: return "sunderock"; + case 404: return "vergalid"; + case 405: return "direwind"; + case 406: return "ashengate"; + case 407: return "highpasshold"; + case 408: return "commonlands"; + case 409: return "oceanoftears"; + case 410: return "kithforest"; + case 411: return "befallenb"; + case 412: return "highpasskeep"; + case 413: return "innothuleb"; + case 414: return "toxxulia"; + case 415: return "mistythicket"; + case 416: return "kattacastrum"; + case 417: return "thalassius"; + case 418: return "atiiki"; + case 419: return "zhisza"; + case 420: return "silyssar"; + case 421: return "solteris"; + case 422: return "barren"; + case 423: return "buriedsea"; + case 424: return "jardelshook"; + case 425: return "monkeyrock"; + case 426: return "suncrest"; + case 427: return "deadbone"; + case 428: return "blacksail"; + case 429: return "maidensgrave"; + case 430: return "redfeather"; + case 431: return "shipmvp"; + case 432: return "shipmvu"; + case 433: return "shippvu"; + case 434: return "shipuvu"; + case 435: return "shipmvm"; + case 436: return "mechanotus"; + case 437: return "mansion"; + case 438: return "steamfactory"; + case 439: return "shipworkshop"; + case 440: return "gyrospireb"; + case 441: return "gyrospirez"; + case 442: return "dragonscale"; + case 443: return "lopingplains"; + case 444: return "hillsofshade"; + case 445: return "bloodmoon"; + case 446: return "crystallos"; + case 447: return "guardian"; + case 448: return "steamfontmts"; + case 449: return "cryptofshade"; + case 451: return "dragonscaleb"; + case 452: return "oldfieldofbone"; + case 453: return "oldkaesoraa"; + case 454: return "oldkaesorab"; + case 455: return "oldkurn"; + case 456: return "oldkithicor"; + case 457: return "oldcommons"; + case 458: return "oldhighpass"; + case 459: return "thevoida"; + case 460: return "thevoidb"; + case 461: return "thevoidc"; + case 462: return "thevoidd"; + case 463: return "thevoide"; + case 464: return "thevoidf"; + case 465: return "thevoidg"; + case 466: return "oceangreenhills"; + case 467: return "oceangreenvillage"; + case 468: return "oldblackburrow"; + case 469: return "bertoxtemple"; + case 470: return "discord"; + case 471: return "discordtower"; + case 472: return "oldbloodfield"; + case 473: return "precipiceofwar"; + case 474: return "olddranik"; + case 475: return "toskirakk"; + case 476: return "korascian"; + case 477: return "rathechamber"; + case 480: return "brellsrest"; + case 481: return "fungalforest"; + case 482: return "underquarry"; + case 483: return "coolingchamber"; + case 484: return "shiningcity"; + case 485: return "arthicrex"; + case 486: return "foundation"; + case 487: return "lichencreep"; + case 488: return "pellucid"; + case 489: return "stonesnake"; + case 490: return "brellstemple"; + case 491: return "convorteum"; + case 492: return "brellsarena"; + case 493: return "weddingchapel"; + case 494: return "weddingchapeldark"; + case 495: return "dragoncrypt"; + case 700: return "feerrott2"; + case 701: return "thulehouse1"; + case 702: return "thulehouse2"; + case 703: return "housegarden"; + case 704: return "thulelibrary"; + case 705: return "well"; + case 706: return "fallen"; + case 707: return "morellcastle"; + case 708: return "somnium"; + case 709: return "alkabormare"; + case 710: return "miragulmare"; + case 711: return "thuledream"; + case 712: return "neighborhood"; + case 724: return "argath"; + case 725: return "arelis"; + case 726: return "sarithcity"; + case 727: return "rubak"; + case 728: return "beastdomain"; + case 729: return "resplendent"; + case 730: return "pillarsalra"; + case 731: return "windsong"; + case 732: return "cityofbronze"; + case 733: return "sepulcher"; + case 734: return "eastsepulcher"; + case 735: return "westsepulcher"; + case 752: return "shardslanding"; + case 753: return "xorbb"; + case 754: return "kaelshard"; + case 755: return "eastwastesshard"; + case 756: return "crystalshard"; + case 757: return "breedinggrounds"; + case 758: return "eviltree"; + case 759: return "grelleth"; + case 760: return "chapterhouse"; + case 996: return "arttest"; + case 998: return "fhalls"; + case 999: return "apprentice"; + } + return "UNKNWN"; +} diff --git a/common/bodytypes.h b/common/bodytypes.h new file mode 100644 index 000000000..f3e49a0b4 --- /dev/null +++ b/common/bodytypes.h @@ -0,0 +1,64 @@ +/* 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 BODYTYPES_H +#define BODYTYPES_H + +typedef enum { + BT_Humanoid = 1, + BT_Lycanthrope = 2, + BT_Undead = 3, + BT_Giant = 4, + BT_Construct = 5, + BT_Extraplanar = 6, + BT_Magical = 7, //this name might be a bit off, + BT_SummonedUndead = 8, + BT_RaidGiant = 9, + // ... + BT_NoTarget = 11, //no name, can't target this bodytype + BT_Vampire = 12, + BT_Atenha_Ra = 13, + BT_Greater_Akheva = 14, + BT_Khati_Sha = 15, + BT_Seru = 16, //not confirmed.... + BT_Zek = 19, + BT_Luggald = 20, + BT_Animal = 21, + BT_Insect = 22, + BT_Monster = 23, + BT_Summoned = 24, //Elemental? + BT_Plant = 25, + BT_Dragon = 26, + BT_Summoned2 = 27, + BT_Summoned3 = 28, + // 29 + BT_VeliousDragon = 30, //might not be a tight set + // ... + BT_Dragon3 = 32, + BT_Boxes = 33, + BT_Muramite = 34, //tribal dudes + // ... + BT_NoTarget2 = 60, + // ... + BT_SwarmPet = 63, //is this valid, or made up? + // ... + BT_InvisMan = 66, //no name, seen on 'InvisMan', can be /targeted + BT_Special = 67 +} bodyType; +/* bodytypes above 64 make the mob not show up */ + +#endif diff --git a/common/breakdowns.h b/common/breakdowns.h new file mode 100644 index 000000000..dccf6e2f6 --- /dev/null +++ b/common/breakdowns.h @@ -0,0 +1,134 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 BREAKDOWNS_H_ +#define BREAKDOWNS_H_ + +#include "types.h" + + +#pragma pack(1) +struct uint16_breakdown { + union { + uint16 all; + struct { + uint8 b1; + uint8 b2; + } bytes; + }; + inline uint16& operator=(const uint16& val) { return (all=val); } + inline uint16* operator&() { return &all; } + inline operator uint16&() { return all; } + inline uint8& b1() { return bytes.b1; } + inline uint8& b2() { return bytes.b2; } +}; + +struct uint32_breakdown { + union { + uint32 all; + struct { + uint16 w1; + uint16 w2; + } words; + struct { + uint8 b1; + union { + struct { + uint8 b2; + uint8 b3; + } middle; + uint16 w2_3; // word bytes 2 to 3 + }; + uint8 b4; + } bytes; + }; + inline uint32& operator=(const uint32& val) { return (all=val); } + inline uint32* operator&() { return &all; } + inline operator uint32&() { return all; } + + inline uint16& w1() { return words.w1; } + inline uint16& w2() { return words.w2; } + inline uint16& w2_3() { return bytes.w2_3; } + inline uint8& b1() { return bytes.b1; } + inline uint8& b2() { return bytes.middle.b2; } + inline uint8& b3() { return bytes.middle.b3; } + inline uint8& b4() { return bytes.b4; } +}; +/* +struct uint64_breakdown { + union { + uint64 all; + struct { + uint16 w1; // 1 2 + uint16 w2; // 3 4 + uint16 w3; // 5 6 + uint16 w4; // 7 8 + }; + struct { + uint32 dw1; // 1 4 + uint32 dw2; // 5 6 + }; + struct { + uint8 b1; + union { + struct { + uint16 w2_3; + uint16 w4_5; + uint16 w6_7; + }; + uint32 dw2_5; + struct { + uint8 b2; + union { + uint32 dw3_6; + struct { + uint8 b3; + union { + uint32 dw4_7; + struct { + uint8 b4; + uint8 b5; + uint8 b6; + uint8 b7; + }; + }; + }; + }; + }; + }; + }; + }; + inline uint64* operator&() { return &all; } + inline operator uint64&() { return all; } +}; +*/ +#pragma pack() + + + + + + + + + + + + + + +#endif /*BREAKDOWNS_H_*/ diff --git a/common/classes.cpp b/common/classes.cpp new file mode 100644 index 000000000..a632c8889 --- /dev/null +++ b/common/classes.cpp @@ -0,0 +1,339 @@ +/* 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 "../common/debug.h" +#include "../common/classes.h" + +const char* GetEQClassName(uint8 class_, uint8 level) { + switch(class_) { + case WARRIOR: + if (level >= 70) + return "Vanquisher"; + else if (level >= 65) + return "Overlord"; //Baron-Sprite: LEAVE MY CLASSES ALONE. + else if (level >= 60) + return "Warlord"; + else if (level >= 55) + return "Myrmidon"; + else if (level >= 51) + return "Champion"; + else + return "Warrior"; + case CLERIC: + if (level >= 70) + return "Prelate"; + else if (level >= 65) + return "Archon"; + else if (level >= 60) + return "High Priest"; + else if (level >= 55) + return "Templar"; + else if (level >= 51) + return "Vicar"; + else + return "Cleric"; + case PALADIN: + if (level >= 70) + return "Lord"; + else if (level >= 65) + return "Lord Protector"; + else if (level >= 60) + return "Crusader"; + else if (level >= 55) + return "Knight"; + else if (level >= 51) + return "Cavalier"; + else + return "Paladin"; + case RANGER: + if (level >= 70) + return "Plainswalker"; + else if (level >= 65) + return "Forest Stalker"; + else if (level >= 60) + return "Warder"; + else if (level >= 55) + return "Outrider"; + else if (level >= 51) + return "Pathfinder"; + else + return "Ranger"; + case SHADOWKNIGHT: + if (level >= 70) + return "Scourge Knight"; + else if (level >= 65) + return "Dread Lord"; + else if (level >= 60) + return "Grave Lord"; + else if (level >= 55) + return "Revenant"; + else if (level >= 51) + return "Reaver"; + else + return "Shadowknight"; + case DRUID: + if (level >= 70) + return "Natureguard"; + else if (level >= 65) + return "Storm Warden"; + else if (level >= 60) + return "Hierophant"; + else if (level >= 55) + return "Preserver"; + else if (level >= 51) + return "Wanderer"; + else + return "Druid"; + case MONK: + if (level >= 70) + return "Stone Fist"; + else if (level >= 65) + return "Transcendent"; + else if (level >= 60) + return "Grandmaster"; + else if (level >= 55) + return "Master"; + else if (level >= 51) + return "Disciple"; + else + return "Monk"; + case BARD: + if (level >= 70) + return "Performer"; + else if (level >= 65) + return "Maestro"; + else if (level >= 60) + return "Virtuoso"; + else if (level >= 55) + return "Troubadour"; + else if (level >= 51) + return "Minstrel"; + else + return "Bard"; + case ROGUE: + if (level >= 70) + return "Nemesis"; + else if (level >= 65) + return "Deceiver"; + else if (level >= 60) + return "Assassin"; + else if (level >= 55) + return "Blackguard"; + else if (level >= 51) + return "Rake"; + else + return "Rogue"; + case SHAMAN: + if (level >= 70) + return "Soothsayer"; + else if (level >= 65) + return "Prophet"; + else if (level >= 60) + return "Oracle"; + else if (level >= 55) + return "Luminary"; + else if (level >= 51) + return "Mystic"; + else + return "Shaman"; + case NECROMANCER: + if (level >= 70) + return "Wraith"; + else if (level >= 65) + return "Arch Lich"; + else if (level >= 60) + return "Warlock"; + else if (level >= 55) + return "Defiler"; + else if (level >= 51) + return "Heretic"; + else + return "Necromancer"; + case WIZARD: + if (level >= 70) + return "Grand Arcanist"; + else if (level >= 65) + return "Arcanist"; + else if (level >= 60) + return "Sorcerer"; + else if (level >= 55) + return "Evoker"; + else if (level >= 51) + return "Channeler"; + else + return "Wizard"; + case MAGICIAN: + if (level >= 70) + return "Arch Magus"; + else if (level >= 65) + return "Arch Convoker"; + else if (level >= 60) + return "Arch Mage"; + else if (level >= 55) + return "Conjurer"; + if (level >= 51) + return "Elementalist"; + else + return "Magician"; + case ENCHANTER: + if (level >= 70) + return "Bedazzler"; + else if (level >= 65) + return "Coercer"; + else if (level >= 60) + return "Phantasmist"; + else if (level >= 55) + return "Beguiler"; + else if (level >= 51) + return "Illusionist"; + else + return "Enchanter"; + case BEASTLORD: + if (level >= 70) + return "Wildblood"; + else if (level >= 65) + return "Feral Lord"; + else if (level >= 60) + return "Savage Lord"; + else if (level >= 55) + return "Animist"; + else if (level >= 51) + return "Primalist"; + else + return "Beastlord"; + case BERSERKER: + if (level >= 70) + return "Ravager"; + else if (level >= 65) + return "Fury"; + else if (level >= 60) + return "Rager"; + else if (level >= 55) + return "Vehement"; + else if (level >= 51) + return "Brawler"; + else + return "Berserker"; + case BANKER: + if (level >= 70) + return "Master Banker"; + else if (level >= 65) + return "Elder Banker"; + else if (level >= 60) + return "Oldest Banker"; + else if (level >= 55) + return "Older Banker"; + else if (level >= 51) + return "Old Banker"; + else + return "Banker"; + case WARRIORGM: + return "Warrior Guildmaster"; + case CLERICGM: + return "Cleric Guildmaster"; + case PALADINGM: + return "Paladin Guildmaster"; + case RANGERGM: + return "Ranger Guildmaster"; + case SHADOWKNIGHTGM: + return "Shadowknight Guildmaster"; + case DRUIDGM: + return "Druid Guildmaster"; + case MONKGM: + return "Monk Guildmaster"; + case BARDGM: + return "Bard Guildmaster"; + case ROGUEGM: + return "Rogue Guildmaster"; + case SHAMANGM: + return "Shaman Guildmaster"; + case NECROMANCERGM: + return "Necromancer Guildmaster"; + case WIZARDGM: + return "Wizard Guildmaster"; + case MAGICIANGM: + return "Magician Guildmaster"; + case ENCHANTERGM: + return "Enchanter Guildmaster"; + case BEASTLORDGM: + return "Beastlord Guildmaster"; + case BERSERKERGM: + return "Berserker Guildmaster"; + case MERCHANT: + return "Merchant"; + case ADVENTURERECRUITER: + return "Adventure Recruiter"; + case ADVENTUREMERCHANT: + return "Adventure Merchant"; + case CORPSE_CLASS: + return "Corpse Class"; + case TRIBUTE_MASTER: + return "Tribute Master"; + case GUILD_TRIBUTE_MASTER: + return "Guild Tribute Master"; + default: + return "Unknown"; + } +} + +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 new file mode 100644 index 000000000..a6072e1ba --- /dev/null +++ b/common/classes.h @@ -0,0 +1,92 @@ +/* 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 CLASSES_CH +#define CLASSES_CH +#include "../common/types.h" + +#define Array_Class_UNKNOWN 0 +#define WARRIOR 1 +#define CLERIC 2 +#define PALADIN 3 +#define RANGER 4 +#define SHADOWKNIGHT 5 +#define DRUID 6 +#define MONK 7 +#define BARD 8 +#define ROGUE 9 +#define SHAMAN 10 +#define NECROMANCER 11 +#define WIZARD 12 +#define MAGICIAN 13 +#define ENCHANTER 14 +#define BEASTLORD 15 +#define BERSERKER 16 +#define PLAYER_CLASS_COUNT 16 // used for array defines, must be the count of playable classes +#define WARRIORGM 20 +#define CLERICGM 21 +#define PALADINGM 22 +#define RANGERGM 23 +#define SHADOWKNIGHTGM 24 +#define DRUIDGM 25 +#define MONKGM 26 +#define BARDGM 27 +#define ROGUEGM 28 +#define SHAMANGM 29 +#define NECROMANCERGM 30 +#define WIZARDGM 31 +#define MAGICIANGM 32 +#define ENCHANTERGM 33 +#define BEASTLORDGM 34 +#define BERSERKERGM 35 +#define BANKER 40 +#define MERCHANT 41 +#define DISCORD_MERCHANT 59 +#define ADVENTURERECRUITER 60 +#define ADVENTUREMERCHANT 61 +#define LDON_TREASURE 62 //objects you can use /open on first seen in LDONs +#define CORPSE_CLASS 62 //only seen on Danvi's Corpse in Akheva so far.. +#define TRIBUTE_MASTER 63 +#define GUILD_TRIBUTE_MASTER 64 //not sure +#define NORRATHS_KEEPERS_MERCHANT 67 +#define DARK_REIGN_MERCHANT 68 +#define FELLOWSHIP_MASTER 69 +#define ALT_CURRENCY_MERCHANT 70 +#define MERCERNARY_MASTER 71 +#define warrior_1 1 +#define monk_1 64 +#define paladin_1 4 +#define shadow_1 16 +#define bard_1 128 +#define cleric_1 2 +#define necromancer_1 1024 +#define ranger_1 8 +#define druid_1 32 +#define mage_1 4096 +#define wizard_1 2048 +#define enchanter_1 8192 +#define rogue_1 256 +#define shaman_1 512 +#define beastlord_1 16384 +#define berserker_1 32768 +#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/common_profile.h b/common/common_profile.h new file mode 100644 index 000000000..6d65795c9 --- /dev/null +++ b/common/common_profile.h @@ -0,0 +1,103 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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_PROFILE_H +#define COMMON_PROFILE_H + +#ifdef ZONE +#include "../zone/features.h" + +#ifndef EQPROFILE +#ifdef COMMON_PROFILE +#undef COMMON_PROFILE +#endif +#endif + +#ifdef COMMON_PROFILE + +#include "../common/profiler.h" + +class CommonProfiler : public GeneralProfiler { +public: + enum { + Database_SaveInventory = 0, + Database_StoreCharacter, + Database_GetCharacterInfoForLogin, + Database_GetCharacterInfoForLogin_result, + Database_GetPlayerProfile, + Database_GetInventory, + Database_GetInventory_name, + Database_SetPlayerProfile, + Database_DBLoadItems, + Database_GetWaypoints, + Database_DBLoadNPCFactionLists, + + DBcore_RunQuery, + + DBAsync_ProcessWork, + DBAsync_DispatchWork, + DBAsyncLoop_loop, + + EQStreamServer_Process, + + EQStream_Process, + + EQStreamServerLoop, + EQStreamInLoop, + EQStreamOutLoop, + TCPServerLoop, + TCPConnectionLoop, + + Inventory_GetItem, + Inventory_HasItem, + + BaseTCPServerLoop, + + MakeRandomInt, + MakeRandomFloat, + + Mutex_lock, + Timer_Check, + + WorldConnection_Process, + + MaxCommonProfilerId + }; + + inline CommonProfiler() : GeneralProfiler(MaxCommonProfilerId) { } + +}; + +extern CommonProfiler _cp; + + +#define _CP(name) _GP(_cp, CommonProfiler, name) + +#else + //no zone profiling, dummy functions +#define _CP(name) ; + +#endif //COMMON_PROFILE + +#else //else !ZONE + +#define _CP(name) ; + +#endif //!ZONE + +#endif + diff --git a/common/crash.cpp b/common/crash.cpp new file mode 100644 index 000000000..44c05f50f --- /dev/null +++ b/common/crash.cpp @@ -0,0 +1,116 @@ +#include "debug.h" +#include "crash.h" + +#if defined(_WINDOWS) && defined(CRASH_LOGGING) +#include "StackWalker.h" + +class EQEmuStackWalker : public StackWalker +{ +public: + EQEmuStackWalker() : StackWalker() { } + EQEmuStackWalker(DWORD dwProcessId, HANDLE hProcess) : StackWalker(dwProcessId, hProcess) { } + virtual void OnOutput(LPCSTR szText) { + char buffer[4096]; + for(int i = 0; i < 4096; ++i) { + if(szText[i] == 0) { + buffer[i] = '\0'; + break; + } + + if(szText[i] == '\n' || szText[i] == '\r') { + buffer[i] = ' '; + } else { + buffer[i] = szText[i]; + } + } + + LogFile->write(EQEMuLog::Crash, buffer); + StackWalker::OnOutput(szText); + } +}; + +LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS *ExceptionInfo) +{ + switch(ExceptionInfo->ExceptionRecord->ExceptionCode) + { + case EXCEPTION_ACCESS_VIOLATION: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_ACCESS_VIOLATION"); + break; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_ARRAY_BOUNDS_EXCEEDED"); + break; + case EXCEPTION_BREAKPOINT: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_BREAKPOINT"); + break; + case EXCEPTION_DATATYPE_MISALIGNMENT: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_DATATYPE_MISALIGNMENT"); + break; + case EXCEPTION_FLT_DENORMAL_OPERAND: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_FLT_DENORMAL_OPERAND"); + break; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_FLT_DIVIDE_BY_ZERO"); + break; + case EXCEPTION_FLT_INEXACT_RESULT: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_FLT_INEXACT_RESULT"); + break; + case EXCEPTION_FLT_INVALID_OPERATION: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_FLT_INVALID_OPERATION"); + break; + case EXCEPTION_FLT_OVERFLOW: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_FLT_OVERFLOW"); + break; + case EXCEPTION_FLT_STACK_CHECK: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_FLT_STACK_CHECK"); + break; + case EXCEPTION_FLT_UNDERFLOW: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_FLT_UNDERFLOW"); + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_ILLEGAL_INSTRUCTION"); + break; + case EXCEPTION_IN_PAGE_ERROR: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_IN_PAGE_ERROR"); + break; + case EXCEPTION_INT_DIVIDE_BY_ZERO: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_INT_DIVIDE_BY_ZERO"); + break; + case EXCEPTION_INT_OVERFLOW: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_INT_OVERFLOW"); + break; + case EXCEPTION_INVALID_DISPOSITION: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_INVALID_DISPOSITION"); + break; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_NONCONTINUABLE_EXCEPTION"); + break; + case EXCEPTION_PRIV_INSTRUCTION: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_PRIV_INSTRUCTION"); + break; + case EXCEPTION_SINGLE_STEP: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_SINGLE_STEP"); + break; + case EXCEPTION_STACK_OVERFLOW: + LogFile->write(EQEMuLog::Crash, "EXCEPTION_STACK_OVERFLOW"); + break; + default: + LogFile->write(EQEMuLog::Crash, "Unknown Exception"); + break; + } + + if(EXCEPTION_STACK_OVERFLOW != ExceptionInfo->ExceptionRecord->ExceptionCode) + { + EQEmuStackWalker sw; sw.ShowCallstack(GetCurrentThread(), ExceptionInfo->ContextRecord); + } + + return EXCEPTION_EXECUTE_HANDLER; +} + +void set_exception_handler() { + SetUnhandledExceptionFilter(windows_exception_handler); +} +#else +// crash is off or an unhandled platform +void set_exception_handler() { +} +#endif diff --git a/common/crash.h b/common/crash.h new file mode 100644 index 000000000..ca53d620e --- /dev/null +++ b/common/crash.h @@ -0,0 +1,6 @@ +#ifndef __EQEMU_CRASH_H +#define __EQEMU_CRASH_H + +void set_exception_handler(); + +#endif diff --git a/common/crc32.cpp b/common/crc32.cpp new file mode 100644 index 000000000..d5dd48e78 --- /dev/null +++ b/common/crc32.cpp @@ -0,0 +1,112 @@ +#include "crc32.h" +#include +#include + +uint32 CRC32Table[256] = +{ + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, + 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, + 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, + 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, + 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, + 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, + 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, + 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, + 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, + 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, + 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, + 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, + 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, + 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, + 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, + 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, + 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, + 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, + 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, + 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, + 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, + 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, + 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, + 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, + 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, + 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, + 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, + 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, + 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, + 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, + 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, + 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, + 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D +}; + +uint32 CRC32::Generate(const uint8* buf, uint32 bufsize) { + return Finish(Update(buf, bufsize)); +} + +uint32 CRC32::GenerateNoFlip(const uint8* buf, uint32 bufsize) { + return Update(buf, bufsize); +} + +void CRC32::SetEQChecksum(uchar* in_data, uint32 in_length, uint32 start_at) +{ + unsigned long data; + unsigned long check = 0xffffffff; + + assert(in_length >= start_at && in_data); + + for(uint32 i=start_at; i> 8; + data = CRC32Table[data]; + check = check ^ data; + } + + memcpy(in_data, (char*)&check, 4); +} + +uint32 CRC32::Update(const uint8* buf, uint32 bufsize, uint32 crc32var) { + for(uint32 i=0; i < bufsize; i++) + Calc(buf[i], crc32var); + return crc32var; +} + +inline void CRC32::Calc(const uint8 byte, uint32& crc32var) { + crc32var = ((crc32var) >> 8) ^ CRC32Table[(byte) ^ ((crc32var) & 0x000000FF)]; +} diff --git a/common/crc32.h b/common/crc32.h new file mode 100644 index 000000000..4abea79bd --- /dev/null +++ b/common/crc32.h @@ -0,0 +1,20 @@ +#ifndef CRC32_H +#define CRC32_H +#include "types.h" + +class CRC32 { +public: + // one buffer CRC32 + static uint32 Generate(const uint8* buf, uint32 bufsize); + static uint32 GenerateNoFlip(const uint8* buf, uint32 bufsize); // Same as Generate(), but without the ~ + static void SetEQChecksum(uchar* in_data, uint32 in_length, uint32 start_at=4); + + // Multiple buffer CRC32 + static uint32 Update(const uint8* buf, uint32 bufsize, uint32 crc32 = 0xFFFFFFFF); + static inline uint32 Finish(uint32 crc32) { return ~crc32; } + static inline void Finish(uint32* crc32) { *crc32 = ~(*crc32); } + +private: + static inline void Calc(const uint8 byte, uint32& crc32); +}; +#endif diff --git a/common/database.cpp b/common/database.cpp new file mode 100644 index 000000000..bfad8287a --- /dev/null +++ b/common/database.cpp @@ -0,0 +1,3182 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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/rulesys.h" +#include +using namespace std; +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Disgrace: for windows compile +#ifdef _WINDOWS +#include +#define snprintf _snprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#else +#include "unix.h" +#include +#include +#endif + +#include "database.h" +#include "eq_packet_structs.h" +#include "guilds.h" +#include "MiscFunctions.h" +#include "extprofile.h" +extern Client client; + +/* +This is the amount of time in seconds the client has to enter the zone +server after the world server, or inbetween zones when that is finished +*/ + +/* +Establish a connection to a mysql database with the supplied parameters + + Added a very simple .ini file parser - Bounce + + Modify to use for win32 & linux - misanthropicfiend +*/ +Database::Database () +{ + DBInitVars(); +} + +/* +Establish a connection to a mysql database with the supplied parameters +*/ + +Database::Database(const char* host, const char* user, const char* passwd, const char* database, uint32 port) +{ + DBInitVars(); + Connect(host, user, passwd, database, 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)) + { + LogFile->write(EQEMuLog::Error, "Failed to connect to database: Error: %s", errbuf); + HandleMysqlError(errnum); + + return false; + } + else + { + LogFile->write(EQEMuLog::Status, "Using database '%s' at %s:%d",database,host,port); + return true; + } +} + +void Database::DBInitVars() { + + max_zonename = 0; + zonename_array = 0; + 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 +*/ +Database::~Database() +{ + unsigned int x; + if (zonename_array) { + for (x=0; x<=max_zonename; x++) { + if (zonename_array[x]) + safe_delete_array(zonename_array[x]); + } + safe_delete_array(zonename_array); + } + if (varcache_array) { + for (x=0; x= 50 || strlen(password) >= 50) + return(0); + + char tmpUN[100]; + char tmpPW[100]; + DoEscapeString(tmpUN, name, strlen(name)); + DoEscapeString(tmpPW, password, strlen(password)); + + if (RunQuery(query, MakeAnyLenString(&query, + "SELECT id, status FROM account WHERE name='%s' AND password is not null " + "and length(password) > 0 and (password='%s' or password=MD5('%s'))", + tmpUN, tmpPW, tmpPW), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) + { + row = mysql_fetch_row(result); + uint32 id = atoi(row[0]); + if (oStatus) + *oStatus = atoi(row[1]); + mysql_free_result(result); + return id; + } + else + { + mysql_free_result(result); + return 0; + } + mysql_free_result(result); + } + else + { + cerr << "Error in CheckLogin query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + return 0; +} + + +//Lieka: Get Banned IP Address List - Only return false if the incoming connection's IP address is not present in the banned_ips table. +bool Database::CheckBannedIPs(const char* loginIP) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + //cout << "Checking against Banned IPs table."<< endl; //Lieka: Debugging + if (RunQuery(query, MakeAnyLenString(&query, "SELECT ip_address FROM Banned_IPs WHERE ip_address='%s'", loginIP), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) != 0) + { + //cout << loginIP << " was present in the banned IPs table" << endl; //Lieka: Debugging + mysql_free_result(result); + return true; + } + else + { + //cout << loginIP << " was not present in the banned IPs table." << endl; //Lieka: Debugging + mysql_free_result(result); + return false; + } + mysql_free_result(result); + } + else + { + cerr << "Error in CheckBannedIPs query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return true; + } + return true; +} + +bool Database::AddBannedIP(char* bannedIP, const char* notes) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT into Banned_IPs SET ip_address='%s', notes='%s'", bannedIP, notes), errbuf)) { + cerr << "Error in ReserveName query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + safe_delete_array(query); + return true; +} + //End Lieka Edit + + bool Database::CheckGMIPs(const char* ip_address, uint32 account_id) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + if (RunQuery(query, MakeAnyLenString(&query, "SELECT * FROM `gm_ips` WHERE `ip_address` = '%s' AND `account_id` = %i", ip_address, account_id), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + mysql_free_result(result); + return true; + } else { + mysql_free_result(result); + return false; + } + mysql_free_result(result); + + } else { + safe_delete_array(query); + return false; + } + + return false; +} + +bool Database::AddGMIP(char* ip_address, char* name) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT into `gm_ips` SET `ip_address` = '%s', `name` = '%s'", ip_address, name), errbuf)) { + safe_delete_array(query); + return false; + } + safe_delete_array(query); + return true; +} + +void Database::LoginIP(uint32 AccountID, const char* LoginIP) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO account_ip SET accid=%i, ip='%s' ON DUPLICATE KEY UPDATE count=count+1, lastused=now()", AccountID, LoginIP), errbuf)) { + cerr << "Error in Log IP query '" << query << "' " << errbuf << endl; + } + safe_delete_array(query); +} + +int16 Database::CheckStatus(uint32 account_id) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT `status`, UNIX_TIMESTAMP(`suspendeduntil`) as `suspendeduntil`, UNIX_TIMESTAMP() as `current`" + " FROM `account` WHERE `id` = %i", account_id), errbuf, &result)) + { + safe_delete_array(query); + + if (mysql_num_rows(result) == 1) + { + row = mysql_fetch_row(result); + + int16 status = atoi(row[0]); + + int32 suspendeduntil = atoi(row[1]); + + int32 current = atoi(row[2]); + + mysql_free_result(result); + + if(suspendeduntil > current) + return -1; + + return status; + } + else + { + mysql_free_result(result); + return 0; + } + mysql_free_result(result); + } + else + { + cerr << "Error in CheckStatus query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + return 0; +} + +uint32 Database::CreateAccount(const char* name, const char* password, int16 status, uint32 lsaccount_id) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 querylen; + uint32 last_insert_id; + + if (password) + querylen = MakeAnyLenString(&query, "INSERT INTO account SET name='%s', password='%s', status=%i, lsaccount_id=%i, time_creation=UNIX_TIMESTAMP();",name,password,status, lsaccount_id); + else + querylen = MakeAnyLenString(&query, "INSERT INTO account SET name='%s', status=%i, lsaccount_id=%i, time_creation=UNIX_TIMESTAMP();",name, status, lsaccount_id); + + cerr << "Account Attempting to be created:" << name << " " << (int16) status << endl; + if (!RunQuery(query, querylen, errbuf, 0, 0, &last_insert_id)) { + cerr << "Error in CreateAccount query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return 0; + } + safe_delete_array(query); + + if (last_insert_id == 0) { + cerr << "Error in CreateAccount query '" << query << "' " << errbuf << endl; + return 0; + } + + return last_insert_id; +} + +bool Database::DeleteAccount(const char* name) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + + cerr << "Account Attempting to be deleted:" << name << endl; + if (RunQuery(query, MakeAnyLenString(&query, "DELETE FROM account WHERE name='%s';",name), errbuf, 0, &affected_rows)) { + safe_delete_array(query); + if (affected_rows == 1) { + return true; + } + } + else { + + cerr << "Error in DeleteAccount query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } + + return false; +} + +bool Database::SetLocalPassword(uint32 accid, const char* password) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET password=MD5('%s') where id=%i;", password, accid), errbuf)) { + cerr << "Error in SetLocalPassword query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + safe_delete_array(query); + return true; +} + +bool Database::SetAccountStatus(const char* name, int16 status) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + + cout << "Account being GM Flagged:" << name << ", Level: " << (int16) status << endl; + if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET status=%i WHERE name='%s';", status, name), errbuf, 0, &affected_rows)) { + safe_delete_array(query); + return false; + } + safe_delete_array(query); + + if (affected_rows == 0) { + cout << "Account: " << name << " does not exist, therefore it cannot be flagged\n"; + return false; + } + + return true; +} + +bool Database::ReserveName(uint32 account_id, char* name) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT into character_ SET account_id=%i, name='%s', profile=NULL", account_id, name), errbuf)) { + cerr << "Error in ReserveName query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + safe_delete_array(query); + return true; +} + +/* +Delete the character with the name "name" +returns false on failure, true otherwise +*/ +bool Database::DeleteCharacter(char *name) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query=0; + MYSQL_RES *result; + MYSQL_ROW row; + int charid, matches; + uint32 affected_rows; + + if(!name || !strlen(name)) + { + printf("DeleteCharacter: request to delete without a name (empty char slot)\n"); + return false; + } + +// get id from character_ before deleting record so we can clean up inventory and qglobal + +#if DEBUG >= 5 + printf("DeleteCharacter: Attempting to delete '%s'\n", name); +#endif + RunQuery(query, MakeAnyLenString(&query, "SELECT id from character_ WHERE name='%s'", name), errbuf, &result); + if (query) + { + safe_delete_array(query); + query = NULL; + } + matches = mysql_num_rows(result); + if(matches == 1) + { + row = mysql_fetch_row(result); + charid = atoi(row[0]); +#if DEBUG >= 5 + printf("DeleteCharacter: found '%s' with char id: %d\n", name, charid); +#endif + } + else + { + printf("DeleteCharacter: error: got %d rows matching '%s'\n", matches, name); + if(result) + { + mysql_free_result(result); + result = NULL; + } + return false; + } + + if(result) + { + mysql_free_result(result); + result = NULL; + } + + + +#if DEBUG >= 5 + printf("DeleteCharacter: deleting '%s' (id %d): ", name, charid); + printf(" quest_globals"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE from quest_globals WHERE charid='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" character_tasks"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE from character_tasks WHERE charid='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" character_activities"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE from character_activities WHERE charid='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" character_enabledtasks"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE from character_enabledtasks WHERE charid='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" completed_tasks"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE from completed_tasks WHERE charid='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" friends"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE from friends WHERE charid='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" mail"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE from mail WHERE charid='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" ptimers"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE from timers WHERE char_id='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" inventory"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE from inventory WHERE charid='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" guild_members"); +#endif +#ifdef BOTS + RunQuery(query, MakeAnyLenString(&query, "DELETE FROM guild_members WHERE char_id='%d' AND GetMobTypeById(%i) = 'C'", charid), errbuf, NULL, &affected_rows); +#else + RunQuery(query, MakeAnyLenString(&query, "DELETE FROM guild_members WHERE char_id='%d'", charid), errbuf, NULL, &affected_rows); +#endif + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" recipes"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE FROM char_recipe_list WHERE char_id='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" adventure_stats"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE FROM adventure_stats WHERE player_id='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" zone_flags"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE FROM zone_flags WHERE charID='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" titles"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE FROM titles WHERE char_id='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" titlesets"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE FROM player_titlesets WHERE char_id='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" keyring"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE FROM keyring WHERE char_id='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" factions"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE FROM faction_values WHERE char_id='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" instances"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE FROM instance_lockout_player WHERE charid='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf(" _character"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE from character_ WHERE id='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + if(affected_rows != 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", affected_rows); + return false; + } + +#if DEBUG >= 5 + printf(" alternate currency"); +#endif + RunQuery(query, MakeAnyLenString(&query, "DELETE FROM character_alt_currency WHERE char_id='%d'", charid), errbuf, NULL, &affected_rows); + if(query) + { + safe_delete_array(query); + query = NULL; + } + +#if DEBUG >= 5 + printf("\n"); +#endif + printf("DeleteCharacter: successfully deleted '%s' (id %d)\n", name, charid); + + 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) +{ + _CP(Database_StoreCharacter); + char errbuf[MYSQL_ERRMSG_SIZE]; + char query[256+sizeof(PlayerProfile_Struct)*2+sizeof(ExtendedProfile_Struct)*2+5]; + char* end = query; + uint32 affected_rows = 0; + int i; + uint32 charid = 0; + char* charidquery = 0; + char* invquery = 0; + MYSQL_RES *result; + MYSQL_ROW row = 0; + char zone[50]; + float x, y, z; + +// memset(&playeraa, 0, sizeof(playeraa)); + + // get the char id (used in inventory inserts below) + if(!RunQuery + ( + charidquery, + MakeAnyLenString + ( + &charidquery, + "SELECT id FROM character_ where name='%s'", + pp->name + ), + errbuf, + &result + )) { + safe_delete_array(charidquery); + LogFile->write(EQEMuLog::Error, "Error in char store id query: %s: %s", charidquery, errbuf); + return(false); + } + safe_delete_array(charidquery); + + if(mysql_num_rows(result) == 1) + { + row = mysql_fetch_row(result); + if(row[0]) + charid = atoi(row[0]); + } + + if(!charid) + { + LogFile->write(EQEMuLog::Error, "StoreCharacter: no character id"); + return false; + } + + const char *zname = GetZoneName(pp->zone_id); + if(zname == NULL) { + //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); + + RunQuery(query, (uint32) (end - query), errbuf, 0, &affected_rows); + + if(!affected_rows) + { + LogFile->write(EQEMuLog::Error, "StoreCharacter query '%s' %s", query, errbuf); + return false; + } + + affected_rows = 0; + + + // Doodman: Is this even used? + // now the inventory + + for (i=0; i<=2270;) + { + const ItemInst* newinv = inv->GetItem((int16)i); + if (newinv) + { + MakeAnyLenString + ( + &invquery, + "INSERT INTO inventory SET " + "charid=%0u, slotid=%0d, itemid=%0u, charges=%0d, color=%0u", + charid, i, newinv->GetItem()->ID, + newinv->GetCharges(), newinv->GetColor() + ); + + RunQuery(invquery, strlen(invquery), errbuf, 0, &affected_rows); + if(!affected_rows) + { + LogFile->write(EQEMuLog::Error, "StoreCharacter inventory failed. Query '%s' %s", invquery, errbuf); + } +#if EQDEBUG >= 9 + else + { + LogFile->write(EQEMuLog::Debug, "StoreCharacter inventory succeeded. Query '%s' %s", invquery, errbuf); + } +#endif + safe_delete_array(invquery); + } + + if(i==30){ //end of standard inventory/cursor, jump to internals of bags/cursor + i = 251; + continue; + } else if(i==340){ //end of internals of bags/cursor, jump to bank slots + i = 2000; + continue; + } else if(i==2023){ //end of bank slots, jump to internals of bank bags + i = 2031; + 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); +} + +/* +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) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT account_id, id FROM character_ WHERE name='%s'", charname), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) + { + row = mysql_fetch_row(result); + uint32 tmp = atoi(row[0]); // copy to temp var because gotta free the result before exitting this function + if (oCharID) + *oCharID = atoi(row[1]); + mysql_free_result(result); + return tmp; + } + mysql_free_result(result); + } + else { + cerr << "Error in GetAccountIDByChar query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } + + return 0; +} + +// Retrieve account_id for a given char_id +uint32 Database::GetAccountIDByChar(uint32 char_id) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint32 ret = 0; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT account_id FROM character_ WHERE id=%i", char_id), errbuf, &result)) { + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + ret = atoi(row[0]); // copy to temp var because gotta free the result before exitting this function + } + mysql_free_result(result); + } + else { + LogFile->write(EQEMuLog::Error, "Error in GetAccountIDByChar query '%s': %s", query, errbuf); + } + + safe_delete_array(query); + return ret; +} + +uint32 Database::GetAccountIDByName(const char* accname, int16* status, uint32* lsid) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + + for (unsigned int i=0; i 'z') && + (accname[i] < 'A' || accname[i] > 'Z') && + (accname[i] < '0' || accname[i] > '9')) + return 0; + } + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, status, lsaccount_id FROM account WHERE name='%s'", accname), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + uint32 tmp = atoi(row[0]); // copy to temp var because gotta free the result before exitting this function + if (status) + *status = atoi(row[1]); + if (lsid) { + if (row[2]) + *lsid = atoi(row[2]); + else + *lsid = 0; + } + mysql_free_result(result); + return tmp; + } + mysql_free_result(result); + } + else { + cerr << "Error in GetAccountIDByAcc query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } + + return 0; +} + +void Database::GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT name, lsaccount_id FROM account WHERE id='%i'", accountid), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + + strcpy(name, row[0]); + if (row[1] && oLSAccountID) { + *oLSAccountID = atoi(row[1]); + } + } + + mysql_free_result(result); + } + else { + safe_delete_array(query); + cerr << "Error in GetAccountName query '" << query << "' " << errbuf << endl; + } +} + +void Database::GetCharName(uint32 char_id, char* name) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT name FROM character_ WHERE id='%i'", char_id), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + + strcpy(name, row[0]); + } + + mysql_free_result(result); + } + else { + safe_delete_array(query); + cerr << "Error in GetCharName query '" << query << "' " << errbuf << endl; + } + +} + +bool Database::LoadVariables() { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + + if (RunQuery(query, LoadVariables_MQ(&query), errbuf, &result)) { + safe_delete_array(query); + bool ret = LoadVariables_result(result); + mysql_free_result(result); + return ret; + } + else { + cerr << "Error in LoadVariables query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } + return false; +} + +uint32 Database::LoadVariables_MQ(char** query) { +// the read of this single variable should be atomic... this was causing strange problems +// LockMutex lock(&Mvarcache); + return MakeAnyLenString(query, "SELECT varname, value, unix_timestamp() FROM variables where unix_timestamp(ts) >= %d", varcache_lastupdate); +} + +bool Database::LoadVariables_result(MYSQL_RES* result) { + uint32 i; + MYSQL_ROW row; + LockMutex lock(&Mvarcache); + if (mysql_num_rows(result) > 0) { + if (!varcache_array) { + varcache_max = mysql_num_rows(result); + varcache_array = new VarCache_Struct*[varcache_max]; + for (i=0; ivarname, row[0]) == 0) { + delete varcache_array[i]; + varcache_array[i] = (VarCache_Struct*) new uint8[sizeof(VarCache_Struct) + strlen(row[1]) + 1]; + strn0cpy(varcache_array[i]->varname, row[0], sizeof(varcache_array[i]->varname)); + strcpy(varcache_array[i]->value, row[1]); + break; + } + } + else { + varcache_array[i] = (VarCache_Struct*) new uint8[sizeof(VarCache_Struct) + strlen(row[1]) + 1]; + strcpy(varcache_array[i]->varname, row[0]); + strcpy(varcache_array[i]->value, row[1]); + break; + } + } + } + uint32 max_used = 0; + for (i=0; i max_used) + max_used = i; + } + } + max_used++; + varcache_max = max_used; + } + return true; +} + +// Gets variable from 'variables' table +bool Database::GetVariable(const char* varname, char* varvalue, uint16 varvalue_len) { + varvalue[0] = '\0'; + + LockMutex lock(&Mvarcache); + if (strlen(varname) <= 1) + return false; + for (uint32 i=0; ivarname, varname) == 0) { + snprintf(varvalue, varvalue_len, "%s", varcache_array[i]->value); + varvalue[varvalue_len-1] = 0; + return true; + } + } + else + return false; + } + return false; +} + +bool Database::SetVariable(const char* varname_in, const char* varvalue_in) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + + char *varname,*varvalue; + + varname=(char *)malloc(strlen(varname_in)*2+1); + varvalue=(char *)malloc(strlen(varvalue_in)*2+1); + DoEscapeString(varname, varname_in, strlen(varname_in)); + DoEscapeString(varvalue, varvalue_in, strlen(varvalue_in)); + + if (RunQuery(query, MakeAnyLenString(&query, "Update variables set value='%s' WHERE varname like '%s'", varvalue, varname), errbuf, 0, &affected_rows)) { + safe_delete_array(query); + if (affected_rows == 1) { + LoadVariables(); // refresh cache + free(varname); + free(varvalue); + return true; + } + else { + if (RunQuery(query, MakeAnyLenString(&query, "Insert Into variables (varname, value) values ('%s', '%s')", varname, varvalue), errbuf, 0, &affected_rows)) { + safe_delete_array(query); + if (affected_rows == 1) { + LoadVariables(); // refresh cache + free(varname); + free(varvalue); + return true; + } + } + } + } + else { + cerr << "Error in SetVariable query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } + free(varname); + free(varvalue); + return false; +} + +uint32 Database::GetMiniLoginAccount(char* ip){ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint32 retid = 0; + if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM account WHERE minilogin_ip='%s'", ip), errbuf, &result)) { + safe_delete_array(query); + if ((row = mysql_fetch_row(result))) + retid = atoi(row[0]); + mysql_free_result(result); + } + else + { + cerr << "Error in GetMiniLoginAccount query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } + return retid; +} + +// Pyro: Get zone starting points from DB +bool Database::GetSafePoints(const char* short_name, uint32 version, float* safe_x, float* safe_y, float* safe_z, int16* minstatus, uint8* minlevel, char *flag_needed) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + // int buf_len = 256; + // int chars = -1; + MYSQL_RES *result; + MYSQL_ROW row; + + if (RunQuery(query, MakeAnyLenString(&query, + "SELECT safe_x, safe_y, safe_z, min_status, min_level, " + " flag_needed FROM zone " + " WHERE short_name='%s' AND (version=%i OR version=0) ORDER BY version DESC", short_name, version), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) > 0) { + row = mysql_fetch_row(result); + if (safe_x != 0) + *safe_x = atof(row[0]); + if (safe_y != 0) + *safe_y = atof(row[1]); + if (safe_z != 0) + *safe_z = atof(row[2]); + if (minstatus != 0) + *minstatus = atoi(row[3]); + if (minlevel != 0) + *minlevel = atoi(row[4]); + if (flag_needed != NULL) + strcpy(flag_needed, row[5]); + mysql_free_result(result); + return true; + } + + mysql_free_result(result); + } + else + { + cerr << "Error in GetSafePoint query '" << query << "' " << errbuf << endl; + cerr << "If it errors, run the following querys:\n"; + cerr << "ALTER TABLE `zone` CHANGE `minium_level` `min_level` TINYINT(3) UNSIGNED DEFAULT \"0\" NOT NULL;\n"; + cerr << "ALTER TABLE `zone` CHANGE `minium_status` `min_status` TINYINT(3) UNSIGNED DEFAULT \"0\" NOT NULL;\n"; + cerr << "ALTER TABLE `zone` ADD flag_needed VARCHAR(128) NOT NULL DEFAULT '';\n"; + + safe_delete_array(query); + } + return false; +} + + +bool Database::GetZoneLongName(const char* short_name, char** long_name, char* file_name, float* safe_x, float* safe_y, float* safe_z, uint32* graveyard_id, uint32* maxclients) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT long_name, file_name, safe_x, safe_y, safe_z, graveyard_id, maxclients FROM zone WHERE short_name='%s' AND version=0", short_name), errbuf, &result)) + { + safe_delete_array(query); + if (mysql_num_rows(result) > 0) { + row = mysql_fetch_row(result); + if (long_name != 0) { + *long_name = strcpy(new char[strlen(row[0])+1], row[0]); + } + if (file_name != 0) { + if (row[1] == 0) + strcpy(file_name, short_name); + else + strcpy(file_name, row[1]); + } + if (safe_x != 0) + *safe_x = atof(row[2]); + if (safe_y != 0) + *safe_y = atof(row[3]); + if (safe_z != 0) + *safe_z = atof(row[4]); + if (graveyard_id != 0) + *graveyard_id = atoi(row[5]); + if (maxclients) + *maxclients = atoi(row[6]); + mysql_free_result(result); + return true; + } + mysql_free_result(result); + } + else + { + cerr << "Error in GetZoneLongName query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + return false; +} +uint32 Database::GetZoneGraveyardID(uint32 zone_id, uint32 version) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint32 GraveyardID = 0; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT graveyard_id FROM zone WHERE zoneidnumber='%u' AND (version=%i OR version=0) ORDER BY version DESC", zone_id, version), errbuf, &result)) + { + if (mysql_num_rows(result) > 0) { + row = mysql_fetch_row(result); + GraveyardID = atoi(row[0]); + } + mysql_free_result(result); + safe_delete_array(query); + return GraveyardID; + } + else + { + cerr << "Error in GetZoneGraveyardID query '" << query << "' " << errbuf << endl; + } + safe_delete_array(query); + return GraveyardID; +} + +bool Database::GetZoneGraveyard(const uint32 graveyard_id, uint32* graveyard_zoneid, float* graveyard_x, float* graveyard_y, float* graveyard_z, float* graveyard_heading) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT zone_id, x, y, z, heading FROM graveyard WHERE id=%i", graveyard_id), errbuf, &result)) + { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + if(graveyard_zoneid != 0) + *graveyard_zoneid = atoi(row[0]); + if(graveyard_x != 0) + *graveyard_x = atof(row[1]); + if(graveyard_y != 0) + *graveyard_y = atof(row[2]); + if(graveyard_z != 0) + *graveyard_z = atof(row[3]); + if(graveyard_heading != 0) + *graveyard_heading = atof(row[4]); + mysql_free_result(result); + return true; + } + mysql_free_result(result); + } + else + { + cerr << "Error in GetZoneGraveyard query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + return false; +} + +bool Database::LoadZoneNames() { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + query = new char[256]; + strcpy(query, "SELECT MAX(zoneidnumber) FROM zone"); + + if (RunQuery(query, strlen(query), errbuf, &result)) { + safe_delete_array(query); + row = mysql_fetch_row(result); + if (row && row[0]) + { + max_zonename = atoi(row[0]); + zonename_array = new char*[max_zonename+1]; + for(unsigned int i=0; i 0) + { + row = mysql_fetch_row(result); + peqzone = atoi(row[0]); + } + safe_delete_array(query); + mysql_free_result(result); + return peqzone; + } + else + { + cerr << "Error in GetPEQZone query '" << query << "' " << errbuf << endl; + } + safe_delete_array(query); + return peqzone; +} + +bool Database::CheckNameFilter(const char* name, bool surname) +{ + std::string str_name = name; + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if(surname) + { + // the minimum 4 is enforced by the client too + if(!name || strlen(name) < 3) + { + return false; + } + } + else + { + // the minimum 4 is enforced by the client too + if(!name || strlen(name) < 4 || strlen(name) > 64) + { + return false; + } + } + + for (int i = 0; i < str_name.size(); i++) + { + if(!isalpha(str_name[i])) + { + return false; + } + } + + for(int x = 0; x < str_name.size(); ++x) + { + str_name[x] = tolower(str_name[x]); + } + + char c = '\0'; + uint8 num_c = 0; + for(int x = 0; x < str_name.size(); ++x) + { + if(str_name[x] == c) + { + num_c++; + } + else + { + num_c = 1; + c = str_name[x]; + } + if(num_c > 2) + { + return false; + } + } + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT name FROM name_filter"), errbuf, &result)) { + safe_delete_array(query); + while(row = mysql_fetch_row(result)) + { + std::string current_row = row[0]; + for(int x = 0; x < current_row.size(); ++x) + { + current_row[x] = tolower(current_row[x]); + } + + if(str_name.find(current_row) != std::string::npos) + { + return false; + } + } + + mysql_free_result(result); + return true; + } + else + { + cerr << "Error in CheckNameFilter query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } + + return true; +} + +bool Database::AddToNameFilter(const char* name) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO name_filter (name) values ('%s')", name), errbuf, 0, &affected_rows)) { + cerr << "Error in AddToNameFilter query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + safe_delete_array(query); + + if (affected_rows == 0) { + return false; + } + + return true; +} + +uint32 Database::GetAccountIDFromLSID(uint32 iLSID, char* oAccountName, int16* oStatus) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, name, status FROM account WHERE lsaccount_id=%i", iLSID), errbuf, &result)) + { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + uint32 account_id = atoi(row[0]); + if (oAccountName) + strcpy(oAccountName, row[1]); + if (oStatus) + *oStatus = atoi(row[2]); + mysql_free_result(result); + return account_id; + } + else + { + mysql_free_result(result); + return 0; + } + mysql_free_result(result); + } + else { + cerr << "Error in GetAccountIDFromLSID query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return 0; + } + + return 0; +} + +void Database::GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT name, status FROM account WHERE id=%i", id), errbuf, &result)) + { + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + if (oAccountName) + strcpy(oAccountName, row[0]); + if (oStatus) + *oStatus = atoi(row[1]); + } + mysql_free_result(result); + } + else + cerr << "Error in GetAccountFromID query '" << query << "' " << errbuf << endl; + safe_delete_array(query); +} + +void Database::ClearMerchantTemp(){ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if (!RunQuery(query, MakeAnyLenString(&query, "delete from merchantlist_temp"), errbuf)) { + cerr << "Error in ClearMerchantTemp query '" << query << "' " << errbuf << endl; + } + safe_delete_array(query); +} + +bool Database::UpdateName(const char* oldname, const char* newname) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + + cout << "Renaming " << oldname << " to " << newname << "..." << endl; + if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET name='%s' WHERE name='%s';", newname, oldname), errbuf, 0, &affected_rows)) { + safe_delete_array(query); + return false; + } + safe_delete_array(query); + + if (affected_rows == 0) + { + return false; + } + + return true; +} + +// If the name is used or an error occurs, it returns false, otherwise it returns true +bool Database::CheckUsedName(const char* name) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + //if (strlen(name) > 15) + // return false; + if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM character_ where name='%s'", name), errbuf, &result)) { + cerr << "Error in CheckUsedName query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + else { // It was a valid Query, so lets do our counts! + safe_delete_array(query); + uint32 tmp = mysql_num_rows(result); + mysql_free_result(result); + if (tmp > 0) // There is a Name! No change (Return False) + return false; + else // Everything is okay, so we go and do this. + return true; + } +} + +uint8 Database::GetServerType() +{ + 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='ServerType'"), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) + { + row = mysql_fetch_row(result); + uint8 ServerType = atoi(row[0]); + mysql_free_result(result); + return ServerType; + } + else + { + mysql_free_result(result); + return 0; + } + mysql_free_result(result); + } + else + + { + + + cerr << "Error in GetServerType query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + return 0; + +} + +bool Database::MoveCharacterToZone(const char* charname, const char* zonename,uint32 zoneid) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + + if(zonename == NULL || strlen(zonename) == 0) + return(false); + + if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET zonename = '%s',zoneid=%i,x=-1, y=-1, z=-1 WHERE name='%s'", zonename,zoneid, charname), errbuf, 0,&affected_rows)) { + cerr << "Error in MoveCharacterToZone(name) query '" << query << "' " << errbuf << endl; + return false; + } + safe_delete_array(query); + + if (affected_rows == 0) + return false; + + return true; +} + +bool Database::MoveCharacterToZone(const char* charname, const char* zonename) { + return MoveCharacterToZone(charname, zonename, GetZoneID(zonename)); +} + +bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET zonename = '%s', zoneid=%i, x=-1, y=-1, z=-1 WHERE id=%i", iZonename, GetZoneID(iZonename), iCharID), errbuf, 0,&affected_rows)) { + cerr << "Error in MoveCharacterToZone(id) query '" << query << "' " << errbuf << endl; + return false; + } + safe_delete_array(query); + + if (affected_rows == 0) + return false; + + return true; +} + +uint8 Database::CopyCharacter(const char* oldname, const char* newname, uint32 acctid) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + PlayerProfile_Struct* pp; + ExtendedProfile_Struct* ext; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT profile, guild, guildrank, extprofile FROM character_ WHERE name='%s'", oldname), errbuf, &result)) { + safe_delete_array(query); + + row = mysql_fetch_row(result); + + pp = (PlayerProfile_Struct*)row[0]; + strcpy(pp->name, newname); + + ext = (ExtendedProfile_Struct*)row[3]; + + mysql_free_result(result); + } + + else { + cerr << "Error in CopyCharacter read query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return 0; + } + + uint32 affected_rows = 0; + 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); + + if (!RunQuery(query2, (uint32) (end - query2), errbuf, 0, &affected_rows)) { + cerr << "Error in CopyCharacter query '" << query << "' " << errbuf << endl; + return 0; + } + + // @merth: Need to copy inventory as well (and shared bank?) + if (affected_rows == 0) { + return 0; + } + + return 1; +} + +bool Database::SetHackerFlag(const char* accountname, const char* charactername, const char* hacked) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO hackers(account,name,hacked) values('%s','%s','%s')", accountname, charactername, hacked), errbuf, 0,&affected_rows)) { + cerr << "Error in SetHackerFlag query '" << query << "' " << errbuf << endl; + return false; + } + safe_delete_array(query); + + if (affected_rows == 0) + { + return false; + } + + return true; +} + +bool Database::SetMQDetectionFlag(const char* accountname, const char* charactername, const char* hacked, const char* zone) { //Utilize the "hacker" table, but also give zone information. + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + + if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO hackers(account,name,hacked,zone) values('%s','%s','%s','%s')", accountname, charactername, hacked, zone), errbuf, 0,&affected_rows)) { + cerr << "Error in SetMQDetectionFlag query '" << query << "' " << errbuf << endl; + return false; + } + + safe_delete_array(query); + + if (affected_rows == 0) + { + return false; + } + + return true; +} + +uint8 Database::GetRaceSkill(uint8 skillid, uint8 in_race) +{ + uint16 race_cap = 0; + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + //Check for a racial cap! + if (RunQuery(query, MakeAnyLenString(&query, "SELECT skillcap from race_skillcaps where skill = %i && race = %i", skillid, in_race), errbuf, &result, &affected_rows)) + { + if (affected_rows != 0) + { + row = mysql_fetch_row(result); + race_cap = atoi(row[0]); + } + delete[] query; + mysql_free_result(result); + } + + return race_cap; +} + +uint8 Database::GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 in_level) +{ + uint8 skill_level = 0, skill_formula = 0; + uint16 base_cap = 0, skill_cap = 0, skill_cap2 = 0, skill_cap3 = 0; + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + MYSQL_RES *result; + MYSQL_ROW row; + //Fetch the data from DB. + if (RunQuery(query, MakeAnyLenString(&query, "SELECT level, formula, pre50cap, post50cap, post60cap from skillcaps where skill = %i && class = %i", skillid, in_class), errbuf, &result, &affected_rows)) + { + if (affected_rows != 0) + { + row = mysql_fetch_row(result); + skill_level = atoi(row[0]); + skill_formula = atoi(row[1]); + skill_cap = atoi(row[2]); + if (atoi(row[3]) > skill_cap) + skill_cap2 = (atoi(row[3])-skill_cap)/10; //Split the post-50 skill cap into difference between pre-50 cap and post-50 cap / 10 to determine amount of points per level. + skill_cap3 = atoi(row[4]); + } + delete[] query; + mysql_free_result(result); + } + + int race_skill = GetRaceSkill(skillid,in_race); + + if (race_skill > 0 && (race_skill > skill_cap || skill_cap == 0 || in_level < skill_level)) + return race_skill; + + if (skill_cap == 0) //Can't train this skill at all. + return 255; //Untrainable + + if (in_level < skill_level) + return 254; //Untrained + + //Determine pre-51 level-based cap + if (skill_formula > 0) + base_cap = in_level*skill_formula+skill_formula; + if (base_cap > skill_cap || skill_formula == 0) + base_cap = skill_cap; + //If post 50, add post 50 cap to base cap. + if (in_level > 50 && skill_cap2 > 0) + base_cap += skill_cap2*(in_level-50); + //No cap should ever go above its post50cap + if (skill_cap3 > 0 && base_cap > skill_cap3) + base_cap = skill_cap3; + //Base cap is now the max value at the person's level, return it! + return base_cap; +} + +uint32 Database::GetCharacterInfo(const char* iName, uint32* oAccID, uint32* oZoneID, uint32* oInstanceID, float* oX, float* oY, float* oZ) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, account_id, zonename, instanceid, x, y, z FROM character_ WHERE name='%s'", iName), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + 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]); + mysql_free_result(result); + return charid; + } + mysql_free_result(result); + } + else { + cerr << "Error in GetCharacterInfo query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } + return 0; +} + +bool Database::UpdateLiveChar(char* charname,uint32 lsaccount_id) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET charname='%s' WHERE id=%i;",charname, lsaccount_id), errbuf)) { + cerr << "Error in UpdateLiveChar query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + safe_delete_array(query); + return true; +} + +bool Database::GetLiveChar(uint32 account_id, char* cname) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + if (RunQuery(query, MakeAnyLenString(&query, "SELECT charname FROM account WHERE id=%i", account_id), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + strcpy(cname,row[0]); + mysql_free_result(result); + return true; + } + mysql_free_result(result); + } + else { + cerr << "Error in GetLiveChar query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } + + return false; +} + +void Database::SetLFP(uint32 CharID, bool LFP) { + + char ErrBuf[MYSQL_ERRMSG_SIZE]; + char *Query = 0; + + if (!RunQuery(Query, MakeAnyLenString(&Query, "update character_ set lfp=%i where id=%i",LFP, CharID), ErrBuf)) + LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, ErrBuf); + + safe_delete_array(Query); + +} + +void Database::SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon) { + + char ErrBuf[MYSQL_ERRMSG_SIZE]; + char *Query = 0; + + if (!RunQuery(Query, MakeAnyLenString(&Query, "update character_ set lfp=%i, lfg=%i, firstlogon=%i where id=%i",LFP, LFG, firstlogon, CharID), ErrBuf)) + LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, ErrBuf); + + safe_delete_array(Query); + +} + +void Database::SetLFG(uint32 CharID, bool LFG) { + + char ErrBuf[MYSQL_ERRMSG_SIZE]; + char *Query = 0; + + if (!RunQuery(Query, MakeAnyLenString(&Query, "update character_ set lfg=%i where id=%i",LFG, CharID), ErrBuf)) + LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, ErrBuf); + + safe_delete_array(Query); + +} + +void Database::SetFirstLogon(uint32 CharID, uint8 firstlogon) { + + char ErrBuf[MYSQL_ERRMSG_SIZE]; + char *Query = 0; + + if (!RunQuery(Query, MakeAnyLenString(&Query, "update character_ set firstlogon=%i where id=%i",firstlogon, CharID), ErrBuf)) + LogFile->write(EQEMuLog::Error, "Error updating firstlogon for character %i : %s", CharID, ErrBuf); + + safe_delete_array(Query); + +} + +void Database::AddReport(std::string who, std::string against, std::string lines) +{ + char ErrBuf[MYSQL_ERRMSG_SIZE]; + char *Query = 0; + char *escape_str = new char[lines.size()*2+1]; + DoEscapeString(escape_str, lines.c_str(), lines.size()); + + if (!RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO reports (name, reported, reported_text) VALUES('%s', '%s', '%s')", who.c_str(), against.c_str(), escape_str), ErrBuf)) + LogFile->write(EQEMuLog::Error, "Error adding a report for %s: %s", who.c_str(), ErrBuf); + + safe_delete_array(Query); + safe_delete_array(escape_str); +} + +void Database::SetGroupID(const char* name,uint32 id, uint32 charid, uint32 ismerc){ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + if(id == 0){ //removing you from table + if (!RunQuery(query, MakeAnyLenString(&query, "delete from group_id where charid=%i and name='%s' and ismerc=%i",charid, name, ismerc), errbuf)) + LogFile->write(EQEMuLog::Error, "Error deleting character from group id: %s", errbuf); + } + else{ + if (!RunQuery(query, MakeAnyLenString(&query, "replace into group_id set charid=%i, groupid=%i, name='%s', ismerc='%i'",charid, id, name, ismerc), errbuf)) + LogFile->write(EQEMuLog::Error, "Error adding character to group id: %s", errbuf); + } + safe_delete_array(query); +} + +void Database::ClearGroup(uint32 gid) { + ClearGroupLeader(gid); + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + if(gid == 0) { //clear all groups + //if (!RunQuery(query, MakeAnyLenString(&query, "update group_id set groupid=0 where groupid!=0"), errbuf)) + if (!RunQuery(query, MakeAnyLenString(&query, "delete from group_id"), errbuf)) + printf("Unable to clear groups: %s\n",errbuf); + } else { //clear a specific group + //if (!RunQuery(query, MakeAnyLenString(&query, "update group_id set groupid=0 where groupid = %lu", gid), errbuf)) + if (!RunQuery(query, MakeAnyLenString(&query, "delete from group_id where groupid = %lu", (unsigned long)gid), errbuf)) + printf("Unable to clear groups: %s\n",errbuf); + } + safe_delete_array(query); +} + +uint32 Database::GetGroupID(const char* name){ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint32 groupid=0; + if (RunQuery(query, MakeAnyLenString(&query, "SELECT groupid from group_id where name='%s'", name), errbuf, &result)) { + if((row = mysql_fetch_row(result))) + { + if(row[0]) + groupid=atoi(row[0]); + } + else + LogFile->write(EQEMuLog::Debug, "Character not in a group: %s", name); + mysql_free_result(result); + } + else + LogFile->write(EQEMuLog::Error, "Error getting group id: %s", errbuf); + safe_delete_array(query); + return groupid; +} + +char* Database::GetGroupLeaderForLogin(const char* name,char* leaderbuf){ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + PlayerProfile_Struct pp; + if (RunQuery(query, MakeAnyLenString(&query, "SELECT profile from character_ where name='%s'", name), 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)); + strcpy(leaderbuf,pp.groupMembers[0]); + } + mysql_free_result(result); + } + else{ + printf("Unable to get leader name: %s\n",errbuf); + } + safe_delete_array(query); + return leaderbuf; +} + +void Database::SetGroupLeaderName(uint32 gid, const char* name) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if (!RunQuery(query, MakeAnyLenString(&query, "Replace into group_leaders set gid=%lu, leadername='%s'",(unsigned long)gid,name), errbuf)) + printf("Unable to set group leader: %s\n",errbuf); + + safe_delete_array(query); +} + +char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank, char* assist, char* puller, char *marknpc, GroupLeadershipAA_Struct* GLAA){ + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + MYSQL_RES* result; + MYSQL_ROW row; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT leadername, maintank, assist, puller, marknpc, leadershipaa FROM group_leaders WHERE gid=%lu",(unsigned long)gid), + errbuf, &result)) { + + safe_delete_array(query); + + row = mysql_fetch_row(result); + unsigned long* Lengths = mysql_fetch_lengths(result); + + if(row != NULL){ + + if(leaderbuf) + strcpy(leaderbuf, row[0]); + + if(maintank) + strcpy(maintank, row[1]); + + if(assist) + strcpy(assist, row[2]); + + if(puller) + strcpy(puller, row[3]); + + if(marknpc) + strcpy(marknpc, row[4]); + + if(GLAA && (Lengths[5] == sizeof(GroupLeadershipAA_Struct))) + memcpy(GLAA, row[5], sizeof(GroupLeadershipAA_Struct)); + + mysql_free_result(result); + return leaderbuf; + } + } + else + safe_delete_array(query); + + if(leaderbuf) + strcpy(leaderbuf, "UNKNOWN"); + + if(maintank) + maintank[0] = 0; + + if(assist) + assist[0] = 0; + + if(puller) + puller[0] = 0; + + if(marknpc) + marknpc[0] = 0; + + return leaderbuf; +} + +void Database::ClearGroupLeader(uint32 gid){ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + if(gid == 0) { //clear all group leaders + if (!RunQuery(query, MakeAnyLenString(&query, "DELETE from group_leaders"), errbuf)) + printf("Unable to clear group leaders: %s\n",errbuf); + } else { //clear a specific group leader + if (!RunQuery(query, MakeAnyLenString(&query, "DELETE from group_leaders where gid = %lu", (unsigned long)gid), errbuf)) + printf("Unable to clear group leader: %s\n",errbuf); + } + safe_delete_array(query); +} + +bool FetchRowMap(MYSQL_RES *result, map &rowmap) +{ +MYSQL_FIELD *fields; +MYSQL_ROW row; +unsigned long num_fields,i; +bool retval=false; + rowmap.clear(); + if (result && (num_fields=mysql_num_fields(result)) && (row = mysql_fetch_row(result))!=NULL && (fields = mysql_fetch_fields(result))!=NULL) { + retval=true; + for(i=0;i= %i ORDER BY id", count), errbuf, &result)) + { + safe_delete_array(query); + if (mysql_num_rows(result) != 0) + { + while(row = mysql_fetch_row(result)) + { + if(count < atoi(row[0])) + { + instance_id = count; + mysql_free_result(result); + return true; + } + else if(count > max) + { + instance_id = 0; + mysql_free_result(result); + return false; + } + else + { + count++; + } + } + } + else + { + mysql_free_result(result); + } + } + else + { + safe_delete_array(query); + } + instance_id = count; + return true; +} + +//perhaps purge any expireds too +bool Database::CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if(RunQuery(query, MakeAnyLenString(&query, "INSERT INTO instance_lockout (id, zone, version, start_time, duration)" + " values(%lu, %lu, %lu, UNIX_TIMESTAMP(), %lu)", (unsigned long)instance_id, (unsigned long)zone_id, (unsigned long)version, (unsigned long)duration), errbuf)) + { + safe_delete_array(query); + return true; + } + else + { + safe_delete_array(query); + return false; + } +} + +void Database::PurgeExpiredInstances() +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + uint16 id = 0; + if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM instance_lockout where " + "(start_time+duration) <= UNIX_TIMESTAMP() and never_expires = 0"), errbuf, &result)) + { + safe_delete_array(query); + if (mysql_num_rows(result) > 0) + { + row = mysql_fetch_row(result); + while(row != NULL) + { + id = atoi(row[0]); + DeleteInstance(id); + row = mysql_fetch_row(result); + } + } + mysql_free_result(result); + } + else + { + safe_delete_array(query); + } +} + +bool Database::AddClientToInstance(uint16 instance_id, uint32 char_id) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if(RunQuery(query, MakeAnyLenString(&query, "INSERT INTO instance_lockout_player(id, charid) " + "values(%lu, %lu)", (unsigned long)instance_id, (unsigned long)char_id), errbuf)) + { + safe_delete_array(query); + return true; + } + else + { + safe_delete_array(query); + return false; + } +} + +bool Database::RemoveClientFromInstance(uint16 instance_id, uint32 char_id) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if(RunQuery(query, MakeAnyLenString(&query, "DELETE FROM instance_lockout_player WHERE id=%lu AND charid=%lu", + (unsigned long)instance_id, (unsigned long)char_id), errbuf)) + { + safe_delete_array(query); + return true; + } + else + { + safe_delete_array(query); + return false; + } +} + +bool Database::RemoveClientsFromInstance(uint16 instance_id) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if(RunQuery(query, MakeAnyLenString(&query, "DELETE FROM instance_lockout_player WHERE id=%lu", + (unsigned long)instance_id), errbuf)) + { + safe_delete_array(query); + return true; + } + else + { + safe_delete_array(query); + return false; + } +} + +bool Database::CheckInstanceExists(uint16 instance_id) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT * FROM instance_lockout where id=%u", instance_id), + errbuf, &result)) + { + safe_delete_array(query); + if (mysql_num_rows(result) != 0) + { + mysql_free_result(result); + return true; + } + mysql_free_result(result); + return false; + } + else + { + safe_delete_array(query); + return false; + } + return false; +} + +void Database::BuryCorpsesInInstance(uint16 instance_id) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + + if(RunQuery(query, MakeAnyLenString(&query, "UPDATE player_corpses SET IsBurried=1, instanceid=0 WHERE instanceid=%u", + instance_id), errbuf, &result)) + { + mysql_free_result(result); + } + safe_delete_array(query); +} + +uint16 Database::GetInstanceVersion(uint16 instance_id) +{ + if(instance_id < 1) + return 0; + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint32 ret; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT version FROM instance_lockout where id=%u", instance_id), + errbuf, &result)) + { + safe_delete_array(query); + if (mysql_num_rows(result) != 0) + { + row = mysql_fetch_row(result); + ret = atoi(row[0]); + mysql_free_result(result); + return ret; + } + else + { + mysql_free_result(result); + return 0; + } + } + else + { + safe_delete_array(query); + return 0; + } + return 0; +} + +uint16 Database::GetInstanceID(const char* zone, uint32 charid, int16 version) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint16 ret; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT instance_lockout.id FROM instance_lockout, instance_lockout_player " + "WHERE instance_lockout.zone=%u AND instance_lockout.version=%u AND instance_lockout.id=instance_lockout_player.id AND " + "instance_lockout_player.charid=%u LIMIT 1;", GetZoneID(zone), version, charid, charid), errbuf, &result)) + { + safe_delete_array(query); + if (mysql_num_rows(result) != 0) + { + row = mysql_fetch_row(result); + ret = atoi(row[0]); + mysql_free_result(result); + return ret; + } + else + { + mysql_free_result(result); + return 0; + } + } + else + { + safe_delete_array(query); + return 0; + } + return 0; +} + +uint16 Database::GetInstanceID(uint32 zone, uint32 charid, int16 version) +{ + if(!zone) + return 0; + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint16 ret; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT instance_lockout.id FROM instance_lockout, instance_lockout_player " + "WHERE instance_lockout.zone=%u AND instance_lockout.version=%u AND instance_lockout.id=instance_lockout_player.id AND " + "instance_lockout_player.charid=%u LIMIT 1;", zone, version, charid), errbuf, &result)) + { + safe_delete_array(query); + if (mysql_num_rows(result) != 0) + { + row = mysql_fetch_row(result); + ret = atoi(row[0]); + mysql_free_result(result); + return ret; + } + else + { + mysql_free_result(result); + return 0; + } + } + else + { + safe_delete_array(query); + return 0; + } + return 0; +} + +void Database::AssignGroupToInstance(uint32 gid, uint32 instance_id) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint32 zone_id = ZoneIDFromInstanceID(instance_id); + uint16 version = VersionFromInstanceID(instance_id); + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT charid FROM group_id WHERE groupid=%u", gid), + errbuf, &result)) + { + safe_delete_array(query); + while((row = mysql_fetch_row(result)) != NULL) + { + uint32 charid = atoi(row[0]); + if(GetInstanceID(zone_id, charid, version) == 0) + { + AddClientToInstance(instance_id, charid); + } + } + mysql_free_result(result); + } + else + { + safe_delete_array(query); + } +} + +void Database::AssignRaidToInstance(uint32 rid, uint32 instance_id) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint32 zone_id = ZoneIDFromInstanceID(instance_id); + uint16 version = VersionFromInstanceID(instance_id); + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT charid FROM raid_members WHERE raidid=%u", rid), + errbuf, &result)) + { + safe_delete_array(query); + while((row = mysql_fetch_row(result)) != NULL) + { + uint32 charid = atoi(row[0]); + if(GetInstanceID(zone_id, charid, version) == 0) + { + AddClientToInstance(instance_id, charid); + } + } + mysql_free_result(result); + } + else + { + safe_delete_array(query); + } +} + +void Database::FlagInstanceByGroupLeader(uint32 zone, int16 version, uint32 charid, uint32 gid) +{ + uint16 id = GetInstanceID(zone, charid, version); + if(id != 0) + return; + + char ln[128]; + memset(ln, 0, 128); + strcpy(ln, GetGroupLeadershipInfo(gid, ln)); + uint32 l_charid = GetCharacterID((const char*)ln); + uint16 l_id = GetInstanceID(zone, l_charid, version); + + if(l_id == 0) + return; + + AddClientToInstance(l_id, charid); +} + +void Database::FlagInstanceByRaidLeader(uint32 zone, int16 version, uint32 charid, uint32 rid) +{ + uint16 id = GetInstanceID(zone, charid, version); + if(id != 0) + return; + + uint32 l_charid = GetCharacterID(GetRaidLeaderName(rid)); + uint16 l_id = GetInstanceID(zone, l_charid, version); + + if(l_id == 0) + return; + + AddClientToInstance(l_id, charid); +} + +void Database::SetInstanceDuration(uint16 instance_id, uint32 new_duration) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if(RunQuery(query, MakeAnyLenString(&query, "UPDATE `instance_lockout` SET start_time=UNIX_TIMESTAMP(), " + "duration=%u WHERE id=%u", new_duration, instance_id), errbuf)) + { + safe_delete_array(query); + } + else + { + //error + safe_delete_array(query); + } +} + +bool Database::GlobalInstance(uint16 instance_id) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + bool ret; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT is_global from instance_lockout where id=%u LIMIT 1", instance_id), errbuf, &result)) + { + safe_delete_array(query); + row = mysql_fetch_row(result); + if(row) + { + ret = (atoi(row[0]) == 1) ? true : false; + } + else + { + mysql_free_result(result); + return false; + } + } + else + { + safe_delete_array(query); + return false; + } + return ret; +} + +void Database::UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected = 0; + + std::string field; + + if(win) + { + switch(theme) + { + case 1: + { + field = "guk_wins"; + break; + } + case 2: + { + field = "mir_wins"; + break; + } + case 3: + { + field = "mmc_wins"; + break; + } + case 4: + { + field = "ruj_wins"; + break; + } + case 5: + { + field = "tak_wins"; + break; + } + default: + { + return; + } + } + } + else + { + switch(theme) + { + case 1: + { + field = "guk_losses"; + break; + } + case 2: + { + field = "mir_losses"; + break; + } + case 3: + { + field = "mmc_losses"; + break; + } + case 4: + { + field = "ruj_losses"; + break; + } + case 5: + { + field = "tak_losses"; + break; + } + default: + { + return; + } + } + } + + if(RunQuery(query, MakeAnyLenString(&query, "UPDATE `adventure_stats` SET %s=%s+1 WHERE player_id=%u", + field.c_str(), field.c_str(), char_id), errbuf, NULL, &affected)) + { + safe_delete_array(query); + } + else + { + //error + safe_delete_array(query); + } + + if(affected == 0) + { + if(RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `adventure_stats` SET %s=1, player_id=%u", + field.c_str(), char_id), errbuf)) + { + safe_delete_array(query); + } + else + { + //error + safe_delete_array(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) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT `guk_wins`, `mir_wins`, `mmc_wins`, `ruj_wins`, `tak_wins`, " + "`guk_losses`, `mir_losses`, `mmc_losses`, `ruj_losses`, `tak_losses` FROM `adventure_stats` WHERE player_id=%u", + char_id), errbuf, &result)) + { + safe_delete_array(query); + while((row = mysql_fetch_row(result)) != NULL) + { + 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]); + } + mysql_free_result(result); + return true; + } + else + { + safe_delete_array(query); + return false; + } +} + +uint32 Database::GetGuildDBIDByCharID(uint32 char_id) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + int retVal = 0; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT guild_id FROM guild_members WHERE char_id='%i'", char_id), errbuf, &result)) { + if (mysql_num_rows(result) == 1) { + MYSQL_ROW row = mysql_fetch_row(result); + retVal = atoi(row[0]); + } + mysql_free_result(result); + } + else { + cerr << "Error in GetAccountIDByChar query '" << query << "' " << errbuf << endl; + } + safe_delete_array(query); + return retVal; +} diff --git a/common/database.h b/common/database.h new file mode 100644 index 000000000..a08b16b04 --- /dev/null +++ b/common/database.h @@ -0,0 +1,273 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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_DATABASE_H +#define EQEMU_DATABASE_H + +#define AUTHENTICATION_TIMEOUT 60 +#define INVALID_ID 0xFFFFFFFF + +#include "debug.h" +#include "types.h" +#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 +using namespace std; + +//atoi is not uint32 or uint32 safe!!!! +#define atoul(str) strtoul(str, NULL, 10) + +//class Spawn; +class Corpse; +class Spawn2; +class NPC; +class SpawnGroupList; +class Petition; +class Client; +struct Combine_Struct; +//struct Faction; +//struct FactionMods; +//struct FactionValue; +struct ZonePoint; +struct NPCType; +class Inventory; +class ItemInst; + +struct EventLogDetails_Struct { + uint32 id; + char accountname[64]; + uint32 account_id; + int16 status; + char charactername[64]; + char targetname[64]; + char timestamp[64]; + char descriptiontype[64]; + char details[128]; +}; + +struct CharacterEventLog_Struct { +uint32 count; +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 value[0]; +}; + +struct PlayerProfile_Struct; +struct GuildRankLevel_Struct; +struct GuildRanks_Struct; +struct ExtendedProfile_Struct; +struct GuildMember_Struct; +class PTimerList; + +class Database : public DBcore { +public: + Database(); + Database(const char* host, const char* user, const char* passwd, const char* database,uint32 port); + bool Connect(const char* host, const char* user, const char* passwd, const char* database,uint32 port); + ~Database(); + + +// void ExtraOptions(); + + + /* + * General Character Related Stuff + */ + 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); + bool UpdateName(const char* oldname, const char* newname); + bool SetHackerFlag(const char* accountname, const char* charactername, const char* hacked); + bool SetMQDetectionFlag(const char* accountname, const char* charactername, const char* hacked, const char* zone); + 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 DeleteCharacter(char* name); + uint8 CopyCharacter(const char* oldname, const char* newname, uint32 acctid); + + /* + * General Information Getting Queries + */ + bool CheckNameFilter(const char* name, bool surname = false); + bool CheckUsedName(const char* name); + uint32 GetAccountIDByChar(const char* charname, uint32* oCharID = 0); + uint32 GetAccountIDByChar(uint32 char_id); + uint32 GetAccountIDByName(const char* accname, int16* status = 0, uint32* lsid = 0); + uint32 GetGuildDBIDByCharID(uint32 char_id); + void GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID = 0); + void GetCharName(uint32 char_id, char* name); + uint32 GetCharacterInfo(const char* iName, uint32* oAccID = 0, uint32* oZoneID = 0, uint32* oInstanceID = 0,float* oX = 0, float* oY = 0, float* oZ = 0); + uint32 GetCharacterID(const char *name); + bool CheckBannedIPs(const char* loginIP); //Lieka Edit: Check incomming connection against banned IP table. + bool AddBannedIP(char* bannedIP, const char* notes); //Lieka Edit: Add IP address to the Banned_IPs table. + bool CheckGMIPs(const char* loginIP, uint32 account_id); + bool AddGMIP(char* ip_address, char* name); + void LoginIP(uint32 AccountID, const char* LoginIP); + + /* + * Instancing Stuff + */ + bool VerifyZoneInstance(uint32 zone_id, uint16 instance_id); + bool VerifyInstanceAlive(uint16 instance_id, uint32 char_id); + bool CharacterInInstanceGroup(uint16 instance_id, uint32 char_id); + void DeleteInstance(uint16 instance_id); + bool CheckInstanceExpired(uint16 instance_id); + uint32 ZoneIDFromInstanceID(uint16 instance_id); + uint32 VersionFromInstanceID(uint16 instance_id); + uint32 GetTimeRemainingInstance(uint16 instance_id, bool &is_perma); + bool GetUnusedInstanceID(uint16 &instance_id); + bool CreateInstance(uint16 instance_id, uint32 zone_id, uint32 version, uint32 duration); + void PurgeExpiredInstances(); + bool AddClientToInstance(uint16 instance_id, uint32 char_id); + bool RemoveClientFromInstance(uint16 instance_id, uint32 char_id); + bool RemoveClientsFromInstance(uint16 instance_id); + bool CheckInstanceExists(uint16 instance_id); + void BuryCorpsesInInstance(uint16 instance_id); + uint16 GetInstanceVersion(uint16 instance_id); + uint16 GetInstanceID(const char* zone, uint32 charid, int16 version); + uint16 GetInstanceID(uint32 zone, uint32 charid, int16 version); + void AssignGroupToInstance(uint32 gid, uint32 instance_id); + void AssignRaidToInstance(uint32 rid, uint32 instance_id); + void FlagInstanceByGroupLeader(uint32 zone, int16 version, uint32 charid, uint32 gid); + void FlagInstanceByRaidLeader(uint32 zone, int16 version, uint32 charid, uint32 rid); + void SetInstanceDuration(uint16 instance_id, uint32 new_duration); + bool GlobalInstance(uint16 instance_id); + + /* + * 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); + + /* + * Account Related + */ + uint32 GetMiniLoginAccount(char* ip); + void GetAccountFromID(uint32 id, char* oAccountName, int16* oStatus); + uint32 CheckLogin(const char* name, const char* password, int16* oStatus = 0); + int16 CheckStatus(uint32 account_id); + uint32 CreateAccount(const char* name, const char* password, int16 status, uint32 lsaccount_id = 0); + bool DeleteAccount(const char* name); + bool SetAccountStatus(const char* name, int16 status); + bool SetLocalPassword(uint32 accid, const char* password); + uint32 GetAccountIDFromLSID(uint32 iLSID, char* oAccountName = 0, int16* oStatus = 0); + bool UpdateLiveChar(char* charname,uint32 lsaccount_id); + bool GetLiveChar(uint32 account_id, char* cname); + uint8 GetAgreementFlag(uint32 acctid); + void SetAgreementFlag(uint32 acctid); + + /* + * Groups + */ + uint32 GetGroupID(const char* name); + void SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ismerc = false); + void ClearGroup(uint32 gid = 0); + char* GetGroupLeaderForLogin(const char* name,char* leaderbuf); + + void SetGroupLeaderName(uint32 gid, const char* name); + char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = NULL, char* assist = NULL, char* puller = NULL, char *marknpc = NULL, + GroupLeadershipAA_Struct* GLAA = NULL); + void ClearGroupLeader(uint32 gid = 0); + + /* + * Raids + */ + void ClearRaid(uint32 rid = 0); + void ClearRaidDetails(uint32 rid = 0); + uint32 GetRaidID(const char* name); + const char *GetRaidLeaderName(uint32 rid); + + /* + * Database Varaibles + */ + bool GetVariable(const char* varname, char* varvalue, uint16 varvalue_len); + bool SetVariable(const char* varname, const char* varvalue); + bool LoadVariables(); + uint32 LoadVariables_MQ(char** query); + bool LoadVariables_result(MYSQL_RES* result); + + /* + * General Queries + */ + bool LoadZoneNames(); + bool GetZoneLongName(const char* short_name, char** long_name, char* file_name = 0, float* safe_x = 0, float* safe_y = 0, float* safe_z = 0, uint32* graveyard_id = 0, uint32* maxclients = 0); + bool GetZoneGraveyard(const uint32 graveyard_id, uint32* graveyard_zoneid = 0, float* graveyard_x = 0, float* graveyard_y = 0, float* graveyard_z = 0, float* graveyard_heading = 0); + uint32 GetZoneGraveyardID(uint32 zone_id, uint32 version); + uint32 GetZoneID(const char* zonename); + uint8 GetPEQZone(uint32 zoneID, uint32 version); + const char* GetZoneName(uint32 zoneID, bool ErrorUnknown = false); + uint8 GetServerType(); + bool GetSafePoints(const char* short_name, uint32 version, float* safe_x = 0, float* safe_y = 0, float* safe_z = 0, int16* minstatus = 0, uint8* minlevel = 0, char *flag_needed = NULL); + bool GetSafePoints(uint32 zoneID, uint32 version, float* safe_x = 0, float* safe_y = 0, float* safe_z = 0, int16* minstatus = 0, uint8* minlevel = 0, char *flag_needed = NULL) { return GetSafePoints(GetZoneName(zoneID), version, safe_x, safe_y, safe_z, minstatus, minlevel, flag_needed); } + uint8 GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 in_level); + uint8 GetRaceSkill(uint8 skillid, uint8 in_race); + bool LoadPTimers(uint32 charid, PTimerList &into); + void ClearPTimers(uint32 charid); + void ClearMerchantTemp(); + void SetLFP(uint32 CharID, bool LFP); + void SetLFG(uint32 CharID, bool LFG); + void SetFirstLogon(uint32 CharID, uint8 firstlogon); + 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); + //bool RunQuery(const char* query, uint32 querylen, char* errbuf = 0, MYSQL_RES** result = 0, uint32* affected_rows = 0, uint32* errnum = 0, bool retry = true); + +private: + void DBInitVars(); + + uint32 max_zonename; + char** zonename_array; + + Mutex Mvarcache; + uint32 varcache_max; + VarCache_Struct** varcache_array; + uint32 varcache_lastupdate; +}; + +bool FetchRowMap(MYSQL_RES *result, map &rowmap); +#endif diff --git a/common/dbasync.cpp b/common/dbasync.cpp new file mode 100644 index 000000000..88afa3cd9 --- /dev/null +++ b/common/dbasync.cpp @@ -0,0 +1,677 @@ +#include "debug.h" +#ifdef _WINDOWS + #include + #include + #include +#endif +#include +using namespace std; +#include "dbasync.h" +#include "database.h" +#include +#include +#include +#include "dbcore.h" +#include "common_profile.h" +#include +#include "../common/MiscFunctions.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 + cout << "Error: DBAsyncCB_LoadVariables failed: !GetAnswer: '" << errbuf << "'" << 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 + { + _CP(DBAsyncLoop_loop); + dba->Process(); + } +// Sleep(ASYNC_LOOP_GRANULARITY); + } + dba->MLoopRunning.unlock(); + +#ifndef WIN32 + _log(COMMON__THREADS, "Ending DBAsyncLoop with thread ID %d", pthread_self()); +#endif + + THREAD_RETURN(NULL); +} + +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, NULL, 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 + cout << "Adding AsyncWork #" << (*iWork)->GetWorkID() << endl; + cout << "ExecuteAfter = " << (*iWork)->pExecuteAfter << " (" << Timer::GetCurrentTime() << " + " << iDelay << ")" << 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 + cout << "DBAsync::CancelWork: " << iWorkID << 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 + cout << "Poping AsyncWork #" << ret->GetWorkID() << endl; + cout << ret->pExecuteAfter << " <= " << Timer::GetCurrentTime() << 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) { + cout << "Error: Unexpected DBAsyncWork->Status in DBAsync::Process #1" << 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) { + cout << "Error: Unexpected DBAsyncWork->Status in DBAsync::Process #2" << 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 + cout << "DBAsync::CommitWrites() called." << 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) { + cout << "Error: Unexpected DBAsyncWork->Status in DBAsync::CommitWrites #1" << endl; + } + safe_delete(tmpWork); + } + else { + DispatchWork(tmpWork); + } + } + else { + if (tmpStatus != Canceled) { + cout << "Error: Unexpected DBAsyncWork->Status in DBAsync::CommitWrites #2" << endl; + } + safe_delete(tmpWork); + } + } +} + +void DBAsync::ProcessWork(DBAsyncWork* iWork, bool iSleep) { + _CP(DBAsync_ProcessWork); + DBAsyncQuery* CurrentQuery; +#if DEBUG_MYSQL_QUERIES >= 2 + cout << "Processing AsyncWork #" << iWork->GetWorkID() << endl; +#endif + while ((CurrentQuery = iWork->PopQuery())) { + CurrentQuery->Process(pDBC); + iWork->PushAnswer(CurrentQuery); + if (iSleep) + Sleep(1); + } +} + +void DBAsync::DispatchWork(DBAsyncWork* iWork) { + _CP(DBAsync_DispatchWork); + //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 new file mode 100644 index 000000000..d6db825df --- /dev/null +++ b/common/dbasync.h @@ -0,0 +1,176 @@ +#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 new file mode 100644 index 000000000..1753ad608 --- /dev/null +++ b/common/dbcore.cpp @@ -0,0 +1,216 @@ +#include "../common/debug.h" + +#ifdef _WINDOWS +#include +#endif + +#include +using namespace std; +#include +#include +#include +#include "dbcore.h" +#include +#include "../common/MiscFunctions.h" +#include + +#ifdef _WINDOWS + #define snprintf _snprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + #include +#else + #include "unix.h" + #include +#endif + +#ifdef _EQDEBUG + #define DEBUG_MYSQL_QUERIES 0 +#else + #define DEBUG_MYSQL_QUERIES 0 +#endif + +DBcore::DBcore() { + mysql_init(&mysql); + pHost = 0; + pUser = 0; + pPassword = 0; + pDatabase = 0; + pCompress = false; + pSSL = false; +} + +DBcore::~DBcore() { + mysql_close(&mysql); + safe_delete_array(pHost); + safe_delete_array(pUser); + safe_delete_array(pPassword); + safe_delete_array(pDatabase); +} + +// Sends the MySQL server a keepalive +void DBcore::ping() { + if (!MDatabase.trylock()) { + // well, if's it's locked, someone's using it. If someone's using it, it doesnt need a keepalive + return; + } + mysql_ping(&mysql); + MDatabase.unlock(); +} + +bool DBcore::RunQuery(const char* query, uint32 querylen, char* errbuf, MYSQL_RES** result, uint32* affected_rows, uint32* last_insert_id, uint32* errnum, bool retry) { + _CP(DBcore_RunQuery); + if (errnum) + *errnum = 0; + if (errbuf) + errbuf[0] = 0; + bool ret = false; + LockMutex lock(&MDatabase); + if (pStatus != Connected) + Open(); +#if DEBUG_MYSQL_QUERIES >= 1 + char tmp[120]; + strn0cpy(tmp, query, sizeof(tmp)); + cout << "QUERY: " << tmp << endl; +#endif + 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) { + cout << "Database Error: Lost connection, attempting to recover...." << endl; + ret = RunQuery(query, querylen, errbuf, result, affected_rows, last_insert_id, errnum, false); + if (ret) + cout << "Reconnection to database successful." << 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)); + cout << "DB Query Error #" << mysql_errno(&mysql) << ": " << mysql_error(&mysql) << 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 + cout << "DB Query Error #" << mysql_errno(&mysql) << ": " << mysql_error(&mysql) << 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 = mysql_insert_id(&mysql); + if (result) { + if (*result) { + ret = true; + } + else { +#ifdef _EQDEBUG + cout << "DB Query Error: No Result" << 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) { + cout << "query successful"; + if (result && (*result)) + cout << ", " << (int) mysql_num_rows(*result) << " rows returned"; + if (affected_rows) + cout << ", " << (*affected_rows) << " rows affected"; + cout<< endl; + } + else { + cout << "QUERY: query FAILED" << endl; + } +#endif + return ret; +} + +uint32 DBcore::DoEscapeString(char* tobuf, const char* frombuf, uint32 fromlen) { +// No good reason to lock the DB, we only need it in the first place to check char encoding. +// LockMutex lock(&MDatabase); + return mysql_real_escape_string(&mysql, tobuf, frombuf, fromlen); +} + +bool DBcore::Open(const char* iHost, const char* iUser, const char* iPassword, const char* iDatabase,uint32 iPort, uint32* errnum, char* errbuf, bool iCompress, bool iSSL) { + LockMutex lock(&MDatabase); + safe_delete(pHost); + safe_delete(pUser); + safe_delete(pPassword); + safe_delete(pDatabase); + pHost = strcpy(new char[strlen(iHost) + 1], iHost); + pUser = strcpy(new char[strlen(iUser) + 1], iUser); + pPassword = strcpy(new char[strlen(iPassword) + 1], iPassword); + pDatabase = strcpy(new char[strlen(iDatabase) + 1], iDatabase); + pCompress = iCompress; + pPort = iPort; + pSSL = iSSL; + return Open(errnum, errbuf); +} + +bool DBcore::Open(uint32* errnum, char* errbuf) { + if (errbuf) + errbuf[0] = 0; + LockMutex lock(&MDatabase); + if (GetStatus() == Connected) + return true; + if (GetStatus() == Error) + mysql_close(&mysql); + mysql_init(&mysql); // Initialize structure again + if (!pHost) + return false; + /* + Added CLIENT_FOUND_ROWS flag to the connect + otherwise DB update calls would say 0 rows affected when the value already equalled + what the function was tring to set it to, therefore the function would think it failed + */ + uint32 flags = CLIENT_FOUND_ROWS; + if (pCompress) + flags |= CLIENT_COMPRESS; + if (pSSL) + flags |= CLIENT_SSL; + if (mysql_real_connect(&mysql, pHost, pUser, pPassword, pDatabase, pPort, 0, flags)) { + pStatus = Connected; + return true; + } + else { + if (errnum) + *errnum = mysql_errno(&mysql); + if (errbuf) + snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); + pStatus = Error; + return false; + } +} + + + + + + diff --git a/common/dbcore.h b/common/dbcore.h new file mode 100644 index 000000000..2e2290f95 --- /dev/null +++ b/common/dbcore.h @@ -0,0 +1,51 @@ +#ifndef DBCORE_H +#define DBCORE_H + +#ifdef _WINDOWS + #include + #include + //#include +#endif +#include +#include "../common/DBMemLeak.h" +#include "../common/types.h" +#include "../common/Mutex.h" +#include "../common/linked_list.h" +#include "../common/queue.h" +#include "../common/timer.h" +#include "../common/Condition.h" + +class DBcore { +public: + enum eStatus { Closed, Connected, Error }; + + 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); + uint32 DoEscapeString(char* tobuf, const char* frombuf, uint32 fromlen); + void ping(); + MYSQL* getMySQL(){ return &mysql; } + +protected: + bool Open(const char* iHost, const char* iUser, const char* iPassword, const char* iDatabase, uint32 iPort, uint32* errnum = 0, char* errbuf = 0, bool iCompress = false, bool iSSL = false); +private: + bool Open(uint32* errnum = 0, char* errbuf = 0); + + MYSQL mysql; + Mutex MDatabase; + eStatus pStatus; + + char* pHost; + char* pUser; + char* pPassword; + char* pDatabase; + bool pCompress; + uint32 pPort; + bool pSSL; + +}; + + +#endif + diff --git a/common/dbmemshare.cpp b/common/dbmemshare.cpp new file mode 100644 index 000000000..c8e917791 --- /dev/null +++ b/common/dbmemshare.cpp @@ -0,0 +1,128 @@ + + +// Doors +#ifdef SHAREMEM +int32 Database::GetDoorsCount(uint32* oMaxID) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + query = new char[256]; + strcpy(query, "SELECT MAX(id), count(*) FROM doors"); + if (RunQuery(query, strlen(query), errbuf, &result)) { + safe_delete(query); + row = mysql_fetch_row(result); + if (row && row[1]) { + int32 ret = atoi(row[1]); + if (oMaxID) { + if (row[0]) + *oMaxID = atoi(row[0]); + else + *oMaxID = 0; + } + mysql_free_result(result); + return ret; + } + } + else { + cerr << "Error in GetDoorsCount query '" << query << "' " << errbuf << endl; + delete[] query; + return -1; + } + + return -1; +} + +extern "C" bool extDBLoadDoors(uint32 iDoorCount, uint32 iMaxDoorID) { return database.DBLoadDoors(iDoorCount, iMaxDoorID); } +const Door* Database::GetDoor(uint8 door_id, const char* zone_name) { + for(uint32 i=0; idoor_id == door_id && strcasecmp(door->zone_name, zone_name) == 0) + return door; + } + return 0; +} + +const Door* Database::GetDoorDBID(uint32 db_id) { + return EMuShareMemDLL.Doors.GetDoor(db_id); +} + +bool Database::LoadDoors() { + if (!EMuShareMemDLL.Load()) + return false; + int32 tmp_max_door_type = -1; + uint32 tmp = 0; + tmp_max_door_type = GetDoorsCount(&tmp); + if (tmp_max_door_type < 0) { + cout << "Error: Database::LoadDoors-ShareMem: GetDoorsCount() returned < 0" << endl; + return false; + } + max_door_type = tmp_max_door_type; + bool ret = EMuShareMemDLL.Doors.DLLLoadDoors(&extDBLoadDoors, sizeof(Door), max_door_type, tmp); + return ret; +} + +bool Database::DBLoadDoors(uint32 iDoorCount, uint32 iMaxDoorID) { + cout << "Loading Doors from database..." << endl; + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + query = new char[256]; + strcpy(query, "SELECT MAX(id), Count(*) FROM doors"); + if (RunQuery(query, strlen(query), errbuf, &result)) + { + safe_delete(query); + row = mysql_fetch_row(result); + if (row && row[0]) { + if (atoi(row[0]) > iMaxDoorID) { + cout << "Error: Insufficient shared memory to load doors." << endl; + cout << "Max(id): " << atoi(row[0]) << ", iMaxDoorID: " << iMaxDoorID << endl; + cout << "Fix this by increasing the MMF_MAX_Door_ID define statement" << endl; + return false; + } + if (atoi(row[1]) != iDoorCount) { + cout << "Error: Insufficient shared memory to load doors." << endl; + cout << "Count(*): " << atoi(row[1]) << ", iDoorCount: " << iDoorCount << endl; + return false; + } + max_door_type = atoi(row[0]); + mysql_free_result(result); + Door tmpDoor; + MakeAnyLenString(&query, "SELECT id,doorid,zone,name,pos_x,pos_y,pos_z,heading,opentype,guild,lockpick,keyitem,triggerdoor,triggertype from doors");//WHERE zone='%s'", zone_name + if (RunQuery(query, strlen(query), errbuf, &result)) + { + safe_delete(query); + while((row = mysql_fetch_row(result))) { + memset(&tmpDoor, 0, sizeof(Door)); + tmpDoor.db_id = atoi(row[0]); + tmpDoor.door_id = atoi(row[1]); + strn0cpy(tmpDoor.zone_name,row[2],32); + strn0cpy(tmpDoor.door_name,row[3],32); + tmpDoor.pos_x = (float)atof(row[4]); + tmpDoor.pos_y = (float)atof(row[5]); + tmpDoor.pos_z = (float)atof(row[6]); + tmpDoor.heading = atoi(row[7]); + tmpDoor.opentype = atoi(row[8]); + tmpDoor.guild_id = atoi(row[9]); + tmpDoor.lockpick = atoi(row[10]); + tmpDoor.keyitem = atoi(row[11]); + tmpDoor.trigger_door = atoi(row[12]); + tmpDoor.trigger_type = atoi(row[13]); + EMuShareMemDLL.Doors.cbAddDoor(tmpDoor.db_id, &tmpDoor); + Sleep(0); + } + mysql_free_result(result); + } + else + { + cerr << "Error in DBLoadDoors query '" << query << "' " << errbuf << endl; + delete[] query; + return false; + } + } + } + return true; +} +#endif + diff --git a/common/debug.cpp b/common/debug.cpp new file mode 100644 index 000000000..1da90a430 --- /dev/null +++ b/common/debug.cpp @@ -0,0 +1,439 @@ +#include "debug.h" + +#include +using namespace std; +#include +#include +#ifdef _WINDOWS + #include + + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#else + #include + #include + #include +#endif +#include "../common/MiscFunctions.h" +#include "../common/platform.h" + +#ifndef va_copy + #define va_copy(d,s) ((d) = (s)) +#endif + +static volatile bool logFileValid = false; +static EQEMuLog realLogFile; +EQEMuLog *LogFile = &realLogFile; + +static const char* FileNames[EQEMuLog::MaxLogID] = { "logs/eqemu", "logs/eqemu", "logs/eqemu_error", "logs/eqemu_debug", "logs/eqemu_quest", "logs/eqemu_commands", "logs/crash" }; +static const char* LogNames[EQEMuLog::MaxLogID] = { "Status", "Normal", "Error", "Debug", "Quest", "Command", "Crash" }; + +EQEMuLog::EQEMuLog() { +// MOpen = new Mutex; +// MLog = new Mutex*[MaxLogID]; +// fp = new FILE*[MaxLogID]; +// pLogStatus = new uint8[MaxLogID]; + for (int i=0; i= 2 + pLogStatus[i] = 1 | 2; +#else + pLogStatus[i] = 0; +#endif + logCallbackFmt[i] = NULL; + logCallbackBuf[i] = NULL; + logCallbackPva[i] = NULL; + } +// TODO: Make this read from an ini or something, everyone has different opinions on what it should be +#if EQDEBUG < 2 + pLogStatus[Status] = 2; + pLogStatus[Error] = 2; + pLogStatus[Quest] = 2; + pLogStatus[Commands] = 1; +#endif + logFileValid = true; +} + +EQEMuLog::~EQEMuLog() { + logFileValid = false; + for (int i=0; i= MaxLogID) { + return false; + } + LockMutex lock(&MOpen); + if (pLogStatus[id] & 4) { + return false; + } + if (fp[id]) { + //cerr<<"Warning: LogFile already open"<= MaxLogID) { + return false; + } + bool dofile = false; + if (pLogStatus[id] & 1) { + dofile = open(id); + } + if (!(dofile || pLogStatus[id] & 2)) + return false; + LockMutex lock(&MLog[id]); + if (!logFileValid) + return false; //check again for threading race reasons (to avoid two mutexes) + + time_t aclock; + struct tm *newtime; + + time( &aclock ); /* Get time in seconds */ + newtime = localtime( &aclock ); /* Convert time to struct */ + + if (dofile) +#ifndef NO_PIDLOG + fprintf(fp[id], "[%02d.%02d. - %02d:%02d:%02d] ", newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); +#else + fprintf(fp[id], "%04i [%02d.%02d. - %02d:%02d:%02d] ", getpid(), newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); +#endif + + va_list argptr, tmpargptr; + va_start(argptr, fmt); + if (dofile) { + va_copy(tmpargptr, argptr); + vfprintf( fp[id], fmt, tmpargptr ); + } + if(logCallbackFmt[id]) { + msgCallbackFmt p = logCallbackFmt[id]; + va_copy(tmpargptr, argptr); + p(id, fmt, tmpargptr ); + } + if (pLogStatus[id] & 2) { + if (pLogStatus[id] & 8) { + fprintf(stderr, "[%s] ", LogNames[id]); + vfprintf( stderr, fmt, argptr ); + } + else { + fprintf(stdout, "[%s] ", LogNames[id]); + vfprintf( stdout, fmt, argptr ); + } + } + va_end(argptr); + if (dofile) + fprintf(fp[id], "\n"); + if (pLogStatus[id] & 2) { + if (pLogStatus[id] & 8) { + fprintf(stderr, "\n"); + fflush(stderr); + } else { + fprintf(stdout, "\n"); + fflush(stdout); + } + } + if(dofile) + fflush(fp[id]); + return true; +} + +//write with Prefix and a VA_list +bool EQEMuLog::writePVA(LogIDs id, const char *prefix, const char *fmt, va_list argptr) { + if (!logFileValid) { + return false; + } + if (id >= MaxLogID) { + return false; + } + bool dofile = false; + if (pLogStatus[id] & 1) { + dofile = open(id); + } + if (!(dofile || pLogStatus[id] & 2)) { + return false; + } + LockMutex lock(&MLog[id]); + if (!logFileValid) + return false; //check again for threading race reasons (to avoid two mutexes) + + time_t aclock; + struct tm *newtime; + + time( &aclock ); /* Get time in seconds */ + newtime = localtime( &aclock ); /* Convert time to struct */ + + va_list tmpargptr; + + if (dofile) { +#ifndef NO_PIDLOG + fprintf(fp[id], "[%02d.%02d. - %02d:%02d:%02d] %s", newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec, prefix); +#else + fprintf(fp[id], "%04i [%02d.%02d. - %02d:%02d:%02d] %s", getpid(), newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec, prefix); +#endif + va_copy(tmpargptr, argptr); + vfprintf( fp[id], fmt, tmpargptr ); + } + if(logCallbackPva[id]) { + msgCallbackPva p = logCallbackPva[id]; + va_copy(tmpargptr, argptr); + p(id, prefix, fmt, tmpargptr ); + } + if (pLogStatus[id] & 2) { + if (pLogStatus[id] & 8) { + fprintf(stderr, "[%s] %s", LogNames[id], prefix); + vfprintf( stderr, fmt, argptr ); + } + else { + fprintf(stdout, "[%s] %s", LogNames[id], prefix); + vfprintf( stdout, fmt, argptr ); + } + } + va_end(argptr); + if (dofile) + fprintf(fp[id], "\n"); + if (pLogStatus[id] & 2) { + if (pLogStatus[id] & 8) + fprintf(stderr, "\n"); + else + fprintf(stdout, "\n"); + } + if(dofile) + fflush(fp[id]); + return true; +} + +bool EQEMuLog::writebuf(LogIDs id, const char *buf, uint8 size, uint32 count) { + if (!logFileValid) { + return false; + } + if (id >= MaxLogID) { + return false; + } + bool dofile = false; + if (pLogStatus[id] & 1) { + dofile = open(id); + } + if (!(dofile || pLogStatus[id] & 2)) + return false; + LockMutex lock(&MLog[id]); + if (!logFileValid) + return false; //check again for threading race reasons (to avoid two mutexes) + + time_t aclock; + struct tm *newtime; + + time( &aclock ); /* Get time in seconds */ + newtime = localtime( &aclock ); /* Convert time to struct */ + + if (dofile) +#ifndef NO_PIDLOG + fprintf(fp[id], "[%02d.%02d. - %02d:%02d:%02d] ", newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); +#else + fprintf(fp[id], "%04i [%02d.%02d. - %02d:%02d:%02d] ", getpid(), newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec); +#endif + + if (dofile) { + fwrite(buf, size, count, fp[id]); + fprintf(fp[id], "\n"); + } + if(logCallbackBuf[id]) { + msgCallbackBuf p = logCallbackBuf[id]; + p(id, buf, size, count); + } + if (pLogStatus[id] & 2) { + if (pLogStatus[id] & 8) { + fprintf(stderr, "[%s] ", LogNames[id]); + fwrite(buf, size, count, stderr); + fprintf(stderr, "\n"); + } else { + fprintf(stdout, "[%s] ", LogNames[id]); + fwrite(buf, size, count, stdout); + fprintf(stdout, "\n"); + } + } + if(dofile) + fflush(fp[id]); + return true; +} + +bool EQEMuLog::writeNTS(LogIDs id, bool dofile, const char *fmt, ...) { + va_list argptr, tmpargptr; + va_start(argptr, fmt); + if (dofile) { + va_copy(tmpargptr, argptr); + vfprintf( fp[id], fmt, tmpargptr ); + } + if (pLogStatus[id] & 2) { + if (pLogStatus[id] & 8) + vfprintf( stderr, fmt, argptr ); + else + vfprintf( stdout, fmt, argptr ); + } + va_end(argptr); + return true; +}; + +bool EQEMuLog::Dump(LogIDs id, uint8* data, uint32 size, uint32 cols, uint32 skip) { + if (!logFileValid) { +#if EQDEBUG >= 10 + cerr << "Error: Dump() from null pointer"<= MaxLogID) + return false; + bool dofile = false; + if (pLogStatus[id] & 1) { + dofile = open(id); + } + if (!(dofile || pLogStatus[id] & 2)) + return false; + LockMutex lock(&MLog[id]); + if (!logFileValid) + return false; //check again for threading race reasons (to avoid two mutexes) + + write(id, "Dumping Packet: %i", size); + // Output as HEX + int j = 0; char* ascii = new char[cols+1]; memset(ascii, 0, cols+1); + uint32 i; + for(i=skip; i= 32 && data[i] < 127) + ascii[j++] = data[i]; + else + ascii[j++] = '.'; + } + uint32 k = ((i-skip)-1)%cols; + if (k < 8) + writeNTS(id, dofile, " "); + for (uint32 h = k+1; h < cols; h++) { + writeNTS(id, dofile, " "); + } + writeNTS(id, dofile, " | %s\n", ascii); + if (dofile) + fflush(fp[id]); + safe_delete_array(ascii); + return true; +} + +void EQEMuLog::SetCallback(LogIDs id, msgCallbackFmt proc) { + if (!logFileValid) + return; + if (id >= MaxLogID) { + return; + } + logCallbackFmt[id] = proc; +} + +void EQEMuLog::SetCallback(LogIDs id, msgCallbackBuf proc) { + if (!logFileValid) + return; + if (id >= MaxLogID) { + return; + } + logCallbackBuf[id] = proc; +} + +void EQEMuLog::SetCallback(LogIDs id, msgCallbackPva proc) { + if (!logFileValid) + return; + if (id >= MaxLogID) { + return; + } + logCallbackPva[id] = proc; +} + +void EQEMuLog::SetAllCallbacks(msgCallbackFmt proc) { + if (!logFileValid) + return; + int r; + for(r = Status; r < MaxLogID; r++) { + SetCallback((LogIDs)r, proc); + } +} + +void EQEMuLog::SetAllCallbacks(msgCallbackBuf proc) { + if (!logFileValid) + return; + int r; + for(r = Status; r < MaxLogID; r++) { + SetCallback((LogIDs)r, proc); + } +} + +void EQEMuLog::SetAllCallbacks(msgCallbackPva proc) { + if (!logFileValid) + return; + int r; + for(r = Status; r < MaxLogID; r++) { + SetCallback((LogIDs)r, proc); + } +} + + + diff --git a/common/debug.h b/common/debug.h new file mode 100644 index 000000000..ca5173897 --- /dev/null +++ b/common/debug.h @@ -0,0 +1,173 @@ +/* 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 +*/ + +// Debug Levels +#ifndef EQDEBUG +#define EQDEBUG 1 +#else +////// File/Console options +// 0 <= Quiet mode Errors to file Status and Normal ignored +// 1 >= Status and Normal to console, Errors to file +// 2 >= Status, Normal, and Error to console and logfile +// 3 >= Lite debug +// 4 >= Medium debug +// 5 >= Debug release (Anything higher is not recommended for regular use) +// 6 == (Reserved for special builds) Login opcode debug All packets dumped +// 7 == (Reserved for special builds) Chat Opcode debug All packets dumped +// 8 == (Reserved for special builds) World opcode debug All packets dumped +// 9 == (Reserved for special builds) Zone Opcode debug All packets dumped +// 10 >= More than you ever wanted to know +// +///// +// Add more below to reserve for file's functions ect. +///// +// Any setup code based on defines should go here +// +#endif + + +#if defined(_DEBUG) && defined(WIN32) + #ifndef _CRTDBG_MAP_ALLOC + #include + #include + #if (_MSC_VER < 1300) + #include + #include + #define _CRTDBG_MAP_ALLOC + #define new new(_NORMAL_BLOCK, __FILE__, __LINE__) + #define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) + #endif + #endif +#endif + +#ifndef ThrowError + void CatchSignal(int); + #if defined(CATCH_CRASH) || defined(_EQDEBUG) + #define ThrowError(errstr) { cout << "Fatal error: " << errstr << " (" << __FILE__ << ", line " << __LINE__ << ")" << endl; LogFile->write(EQEMuLog::Error, "Thown Error: %s (%s:%i)", errstr, __FILE__, __LINE__); throw errstr; } + #else + #define ThrowError(errstr) { cout << "Fatal error: " << errstr << " (" << __FILE__ << ", line " << __LINE__ << ")" << endl; LogFile->write(EQEMuLog::Error, "Thown Error: %s (%s:%i)", errstr, __FILE__, __LINE__); CatchSignal(0); } + #endif +#endif + +#ifdef _WINDOWS + // VS6 doesn't like the length of STL generated names: disabling + #pragma warning(disable:4786) + #pragma warning(disable:4996) +#endif + +#ifndef EQDEBUG_H +#define EQDEBUG_H + +#ifndef _WINDOWS + #define DebugBreak() if(0) {} +#endif + +#define _WINSOCKAPI_ //stupid windows, trying to fix the winsock2 vs. winsock issues +#if defined(WIN32) && ( defined(PACKETCOLLECTOR) || defined(COLLECTOR) ) + // Packet Collector on win32 requires winsock.h due to latest pcap.h + // winsock.h must come before windows.h + #include +#endif + +#ifdef _WINDOWS + #include + #include +#endif + +#include "logsys.h" +#include "common_profile.h" +#ifdef ZONE +#include "../zone/zone_profile.h" +#endif + +#include "../common/Mutex.h" +#include +#include + + +class EQEMuLog { +public: + EQEMuLog(); + ~EQEMuLog(); + + enum LogIDs { + Status = 0, //this must stay the first entry in this list + Normal, + Error, + Debug, + Quest, + Commands, + Crash, + MaxLogID + }; + + //these are callbacks called for each + typedef void (* msgCallbackBuf)(LogIDs id, const char *buf, uint8 size, uint32 count); + typedef void (* msgCallbackFmt)(LogIDs id, const char *fmt, va_list ap); + typedef void (* msgCallbackPva)(LogIDs id, const char *prefix, const char *fmt, va_list ap); + + void SetAllCallbacks(msgCallbackFmt proc); + void SetAllCallbacks(msgCallbackBuf proc); + void SetAllCallbacks(msgCallbackPva proc); + void SetCallback(LogIDs id, msgCallbackFmt proc); + void SetCallback(LogIDs id, msgCallbackBuf proc); + void SetCallback(LogIDs id, msgCallbackPva proc); + + bool writebuf(LogIDs id, const char *buf, uint8 size, uint32 count); + bool write(LogIDs id, const char *fmt, ...); + bool writePVA(LogIDs id, const char *prefix, const char *fmt, va_list args); + bool Dump(LogIDs id, uint8* data, uint32 size, uint32 cols=16, uint32 skip=0); +private: + bool open(LogIDs id); + bool writeNTS(LogIDs id, bool dofile, const char *fmt, ...); // no error checking, assumes is open, no locking, no timestamp, no newline + + Mutex MOpen; + Mutex MLog[MaxLogID]; + FILE* fp[MaxLogID]; +/* LogStatus: bitwise variable + 1 = output to file + 2 = output to stdout + 4 = fopen error, dont retry + 8 = use stderr instead (2 must be set) +*/ + uint8 pLogStatus[MaxLogID]; + + msgCallbackFmt logCallbackFmt[MaxLogID]; + msgCallbackBuf logCallbackBuf[MaxLogID]; + msgCallbackPva logCallbackPva[MaxLogID]; +}; + +extern EQEMuLog* LogFile; + +#ifdef _EQDEBUG +class PerformanceMonitor { +public: + PerformanceMonitor(int64* ip) { + p = ip; + QueryPerformanceCounter(&tmp); + } + ~PerformanceMonitor() { + LARGE_INTEGER tmp2; + QueryPerformanceCounter(&tmp2); + *p += tmp2.QuadPart - tmp.QuadPart; + } + LARGE_INTEGER tmp; + int64* p; +}; +#endif +#endif diff --git a/common/deity.h b/common/deity.h new file mode 100644 index 000000000..beff69134 --- /dev/null +++ b/common/deity.h @@ -0,0 +1,65 @@ +/* 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 DEITY_H +#define DEITY_H + +/* +** Diety List +*/ +#define DEITY_AGNOSTIC 396 //drop the high bit for 140 +#define DEITY_BRELL 202 +#define DEITY_CAZIC 203 +#define DEITY_EROLLSI 204 +#define DEITY_BRISTLE 205 +#define DEITY_INNY 206 +#define DEITY_KARANA 207 +#define DEITY_MITH 208 +#define DEITY_PREXUS 209 +#define DEITY_QUELLIOUS 210 +#define DEITY_RALLOS 211 +#define DEITY_SOLUSEK 213 +#define DEITY_TRIBUNAL 214 +#define DEITY_TUNARE 215 + +//Guessed: +#define DEITY_BERTOX 201 +#define DEITY_RODCET 212 +#define DEITY_VEESHAN 216 + +/* on items: + *All 0 +AGNOSTIC 1 +BERTOX 2 +BRELL 4 +CAZIC 8 +EROLLSI 16 +BRISTLE 32 +INNY 64 +KARANA 128 +MITH 256 +PREXUS 512 +QUELLIOUS 1024 +RALLOS 2048 +RODCET 4096 +SOLUSEK 8192 +TRIBUNAL 16384 +TUNARE 32768 +VEESHAN 65536 +*/ + +#endif diff --git a/common/emu_opcodes.cpp b/common/emu_opcodes.cpp new file mode 100644 index 000000000..6075e7d86 --- /dev/null +++ b/common/emu_opcodes.cpp @@ -0,0 +1,38 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2005 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 04111-1307 USA +*/ + +#include "debug.h" +#include "emu_opcodes.h" + +const char *OpcodeNames[_maxEmuOpcode+1] = { + "OP_Unknown", + +//a preprocessor hack so we dont have to maintain two lists +#define N(x) #x + #include "emu_oplist.h" + #include "mail_oplist.h" +#undef N + + "" +}; + + + + + + diff --git a/common/emu_opcodes.h b/common/emu_opcodes.h new file mode 100644 index 000000000..4899131d2 --- /dev/null +++ b/common/emu_opcodes.h @@ -0,0 +1,53 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2005 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 04111-1307 USA +*/ +#ifndef EMU_OPCODES_H +#define EMU_OPCODES_H + +//this is the highest opcode possibly used in the regular EQ protocol +#define MAX_EQ_OPCODE 0xFFFF + + +/* + + +the list of opcodes is in emu_oplist.h + +we somewhat rely on the fact that we have more than 255 opcodes, +so we know the enum type for the opcode defines must be at least +16 bits, so we can use the protocol flags on them. + +*/ + +typedef enum { //EQEmu internal opcodes list + OP_Unknown=0, + +//a preprocessor hack so we dont have to maintain two lists +#define N(x) x + #include "emu_oplist.h" + #include "mail_oplist.h" +#undef N + + _maxEmuOpcode +} EmuOpcode; + +extern const char *OpcodeNames[_maxEmuOpcode+1]; + +#endif + + + diff --git a/common/emu_oplist.h b/common/emu_oplist.h new file mode 100644 index 000000000..4b7357efe --- /dev/null +++ b/common/emu_oplist.h @@ -0,0 +1,540 @@ +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), +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_AdventureData), +N(OP_AdventureFinish), +N(OP_LeaveAdventure), +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_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_GroupDisband), +N(OP_GroupInvite2), +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_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_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_LFGGetMatchesRequest), +N(OP_LFGGetMatchesResponse), +N(OP_LFGResponse), +N(OP_LFPCommand), +N(OP_LFPGetMatchesRequest), +N(OP_LFPGetMatchesResponse), +N(OP_LeadershipExpToggle), +N(OP_LeadershipExpUpdate), +N(OP_LoadSpellSet), +N(OP_LockoutTimerInfo), +N(OP_MendHPUpdate), +N(OP_MobHealth), +N(OP_MoveLogDisregard), +N(OP_MoveLogRequest), +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_PlayEverquestRequest), +N(OP_ChatMessage), +N(OP_LoginAccepted), +N(OP_ServerListResponse), +N(OP_Poll), +N(OP_PlayEverquestResponse), +N(OP_EnterChat), +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_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_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_RemoveAllDoors), +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_SendMaxCharacters), +N(OP_SendMembership), +N(OP_SendMembershipDetails), +N(OP_LFGuild), +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), diff --git a/common/eq_constants.h b/common/eq_constants.h new file mode 100644 index 000000000..ec83c249e --- /dev/null +++ b/common/eq_constants.h @@ -0,0 +1,696 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 EQ_CONSTANTS_H +#define EQ_CONSTANTS_H + +#define BIT_Client62 1 +#define BIT_Titanium 2 +#define BIT_SoF 4 +#define BIT_SoD 8 +#define BIT_Underfoot 16 +#define BIT_RoF 32 +#define BIT_TitaniumAndEarlier 3 +#define BIT_SoFAndLater 0xFFFFFFFC +#define BIT_SoDAndLater 0xFFFFFFF8 +#define BIT_UnderfootAndLater 0xFFFFFFF0 +#define BIT_RoFAndLater 0xFFFFFFE0 +#define BIT_AllClients 0xFFFFFFFF + +#include "skills.h" + +/* +** Item attributes +** +*/ +enum ItemAttrib +{ + ItemAttribLore = (1 << 0), + ItemAttribArtifact = (1 << 1), + ItemAttribSummoned = (1 << 2), + ItemAttribMagic = (1 << 3), + ItemAttribAugment = (1 << 4), + ItemAttribPendingLore = (1 << 5), + ItemAttribNone = 0, + ItemAttribUnknown = 0xFFFFFFFF +}; + +/* +** Item types +** +*/ +enum ItemClass +{ + ItemClassCommon = 0, + ItemClassContainer = 1, + ItemClassBook = 2 +}; + +/* +** Item uses +** +*/ +enum ItemTypes +{ + ItemType1HS = 0, + ItemType2HS = 1, + ItemTypePierce = 2, + ItemType1HB = 3, + ItemType2HB = 4, + ItemTypeBow = 5, + //6 + ItemTypeThrowing = 7, + ItemTypeShield = 8, + //9 + ItemTypeArmor = 10, + ItemTypeUnknon = 11, //A lot of random crap has this item use. + ItemTypeLockPick = 12, + ItemTypeFood = 14, + ItemTypeDrink = 15, + ItemTypeLightSource = 16, + ItemTypeStackable = 17, //Not all stackable items are this use... + ItemTypeBandage = 18, + ItemTypeThrowingv2 = 19, + ItemTypeSpell = 20, //spells and tomes + ItemTypePotion = 21, + ItemTypeWindInstr = 23, + ItemTypeStringInstr = 24, + ItemTypeBrassInstr = 25, + ItemTypeDrumInstr = 26, + ItemTypeArrow = 27, + ItemTypeJewlery = 29, + ItemTypeSkull = 30, + ItemTypeTome = 31, + ItemTypeNote = 32, + ItemTypeKey = 33, + ItemTypeCoin = 34, + ItemType2HPierce = 35, + ItemTypeFishingPole = 36, + ItemTypeFishingBait = 37, + ItemTypeAlcohol = 38, + ItemTypeCompass = 40, + ItemTypePoison = 42, //might be wrong, but includes poisons + ItemTypeHand2Hand = 45, + ItemUseSinging = 50, + ItemUseAllInstruments = 51, + ItemTypeCharm = 52, + ItemTypeAugment = 54, + ItemTypeAugmentSolvent = 55, + ItemTypeAugmentDistill = 56 +}; + +/* + Bag types +*/ +enum { + bagTypeSmallBag = 0, + bagTypeLargeBag = 1, + bagTypeQuiver = 2, + bagTypeBeltPouch = 3, + bagTypeBandolier = 8 + //... there are 50 types +}; + + +/* +** Item Effect Types +** +*/ +enum { + ET_CombatProc = 0, + ET_ClickEffect = 1, + ET_WornEffect = 2, + ET_Expendable = 3, + ET_EquipClick = 4, + ET_ClickEffect2 = 5, //name unknown + ET_Focus = 6, + ET_Scroll = 7 +}; + +//SpawnAppearance types: +#define AT_Die 0 // this causes the client to keel over and zone to bind point +#define AT_WhoLevel 1 // the level that shows up on /who +#define AT_Invis 3 // 0 = visible, 1 = invisible +#define AT_PVP 4 // 0 = blue, 1 = pvp (red) +#define AT_Light 5 // light type emitted by player (lightstone, shiny shield) +#define AT_Anim 14 // 100=standing, 110=sitting, 111=ducking, 115=feigned, 105=looting +#define AT_Sneak 15 // 0 = normal, 1 = sneaking +#define AT_SpawnID 16 // server to client, sets player spawn id +#define AT_HP 17 // Client->Server, my HP has changed (like regen tic) +#define AT_Linkdead 18 // 0 = normal, 1 = linkdead +#define AT_Levitate 19 // 0=off, 1=flymode, 2=levitate +#define AT_GM 20 // 0 = normal, 1 = GM - all odd numbers seem to make it GM +#define AT_Anon 21 // 0 = normal, 1 = anon, 2 = roleplay +#define AT_GuildID 22 +#define AT_GuildRank 23 // 0=member, 1=officer, 2=leader +#define AT_AFK 24 // 0 = normal, 1 = afk +#define AT_Split 28 // 0 = normal, 1 = autosplit on +#define AT_Size 29 // spawn's size +#define AT_NPCName 31 // change PC's name's color to NPC color 0 = normal, 1 = npc name +#define AT_ShowHelm 43 // 0 = do not show helmet graphic, 1 = show graphic +#define AT_DamageState 44 // The damage state of a destructible object (0 through 4) +//#define AT_Trader 300 // Bazzar Trader Mode + +// solar: animations for AT_Anim +#define ANIM_FREEZE 102 +#define ANIM_STAND 0x64 +#define ANIM_SIT 0x6e +#define ANIM_CROUCH 0x6f +#define ANIM_DEATH 0x73 +#define ANIM_LOOT 0x69 + +typedef enum { + eaStanding = 0, + eaSitting, //1 + eaCrouching, //2 + eaDead, //3 + eaLooting, //4 + _eaMaxAppearance +} EmuAppearance; + +/* +** Diety List +*/ +#define DEITY_UNKNOWN 0 +#define DEITY_AGNOSTIC 396 +#define DEITY_BRELL 202 +#define DEITY_CAZIC 203 +#define DEITY_EROL 204 +#define DEITY_BRISTLE 205 +#define DEITY_INNY 206 +#define DEITY_KARANA 207 +#define DEITY_MITH 208 +#define DEITY_PREXUS 209 +#define DEITY_QUELLIOUS 210 +#define DEITY_RALLOS 211 +#define DEITY_SOLUSEK 213 +#define DEITY_TRIBUNAL 214 +#define DEITY_TUNARE 215 + +//Guessed: +#define DEITY_BERT 201 +#define DEITY_RODCET 212 +#define DEITY_VEESHAN 216 + +// msg_type's for custom usercolors +#define MT_Say 256 +#define MT_Tell 257 +#define MT_Group 258 +#define MT_Guild 259 +#define MT_OOC 260 +#define MT_Auction 261 +#define MT_Shout 262 +#define MT_Emote 263 +#define MT_Spells 264 +#define MT_YouHitOther 265 +#define MT_OtherHitsYou 266 +#define MT_YouMissOther 267 +#define MT_OtherMissesYou 268 +#define MT_Broadcasts 269 +#define MT_Skills 270 +#define MT_Disciplines 271 +#define MT_Unused1 272 +#define MT_DefaultText 273 +#define MT_Unused2 274 +#define MT_MerchantOffer 275 +#define MT_MerchantBuySell 276 +#define MT_YourDeath 277 +#define MT_OtherDeath 278 +#define MT_OtherHits 279 +#define MT_OtherMisses 280 +#define MT_Who 281 +#define MT_YellForHelp 282 +#define MT_NonMelee 283 +#define MT_WornOff 284 +#define MT_MoneySplit 285 +#define MT_LootMessages 286 +#define MT_DiceRoll 287 +#define MT_OtherSpells 288 +#define MT_SpellFailure 289 +#define MT_Chat 290 +#define MT_Channel1 291 +#define MT_Channel2 292 +#define MT_Channel3 293 +#define MT_Channel4 294 +#define MT_Channel5 295 +#define MT_Channel6 296 +#define MT_Channel7 297 +#define MT_Channel8 298 +#define MT_Channel9 299 +#define MT_Channel10 300 +#define MT_CritMelee 301 +#define MT_SpellCrits 302 +#define MT_TooFarAway 303 +#define MT_NPCRampage 304 +#define MT_NPCFlurry 305 +#define MT_NPCEnrage 306 +#define MT_SayEcho 307 +#define MT_TellEcho 308 +#define MT_GroupEcho 309 +#define MT_GuildEcho 310 +#define MT_OOCEcho 311 +#define MT_AuctionEcho 312 +#define MT_ShoutECho 313 +#define MT_EmoteEcho 314 +#define MT_Chat1Echo 315 +#define MT_Chat2Echo 316 +#define MT_Chat3Echo 317 +#define MT_Chat4Echo 318 +#define MT_Chat5Echo 319 +#define MT_Chat6Echo 320 +#define MT_Chat7Echo 321 +#define MT_Chat8Echo 322 +#define MT_Chat9Echo 323 +#define MT_Chat10Echo 324 +#define MT_DoTDamage 325 +#define MT_ItemLink 326 +#define MT_RaidSay 327 +#define MT_MyPet 328 +#define MT_DS 329 +#define MT_Leadership 330 +#define MT_PetFlurry 331 +#define MT_PetCrit 332 +#define MT_FocusEffect 333 +#define MT_Experience 334 +#define MT_System 335 +#define MT_PetSpell 336 +#define MT_PetResponse 337 +#define MT_ItemSpeech 338 +#define MT_StrikeThrough 339 +#define MT_Stun 340 + +//from showeq +enum ChatColor +{ + CC_Default = 0, + CC_DarkGrey = 1, + CC_DarkGreen = 2, + CC_DarkBlue = 3, + CC_Purple = 5, + CC_LightGrey = 6, + CC_User_Say = 256, + CC_User_Tell = 257, + CC_User_Group = 258, + CC_User_Guild = 259, + CC_User_OOC = 260, + CC_User_Auction = 261, + CC_User_Shout = 262, + CC_User_Emote = 263, + CC_User_Spells = 264, + CC_User_YouHitOther = 265, + CC_User_OtherHitYou = 266, + CC_User_YouMissOther = 267, + CC_User_OtherMissYou = 268, + CC_User_Duels = 269, + CC_User_Skills = 270, + CC_User_Disciplines = 271, + CC_User_Default = 273, + CC_User_MerchantOffer = 275, + CC_User_MerchantExchange = 276, + CC_User_YourDeath = 277, + CC_User_OtherDeath = 278, + CC_User_OtherHitOther = 279, + CC_User_OtherMissOther = 280, + CC_User_Who = 281, + CC_User_Yell = 282, + CC_User_NonMelee = 283, + CC_User_SpellWornOff = 284, + CC_User_MoneySplit = 285, + CC_User_Loot = 286, + CC_User_Random = 287, + CC_User_OtherSpells = 288, + CC_User_SpellFailure = 289, + CC_User_ChatChannel = 290, + CC_User_Chat1 = 291, + CC_User_Chat2 = 292, + CC_User_Chat3 = 293, + CC_User_Chat4 = 294, + CC_User_Chat5 = 295, + CC_User_Chat6 = 296, + CC_User_Chat7 = 297, + CC_User_Chat8 = 298, + CC_User_Chat9 = 299, + CC_User_Chat10 = 300, + CC_User_MeleeCrit = 301, + CC_User_SpellCrit = 302, + CC_User_TooFarAway = 303, + CC_User_NPCRampage = 304, + CC_User_NPCFurry = 305, + CC_User_NPCEnrage = 306, + CC_User_EchoSay = 307, + CC_User_EchoTell = 308, + CC_User_EchoGroup = 309, + CC_User_EchoGuild = 310, + CC_User_EchoOOC = 311, + CC_User_EchoAuction = 312, + CC_User_EchoShout = 313, + CC_User_EchoEmote = 314, + CC_User_EchoChat1 = 315, + CC_User_EchoChat2 = 316, + CC_User_EchoChat3 = 317, + CC_User_EchoChat4 = 318, + CC_User_EchoChat5 = 319, + CC_User_EchoChat6 = 320, + CC_User_EchoChat7 = 321, + CC_User_EchoChat8 = 322, + CC_User_EchoChat9 = 323, + CC_User_EchoChat10 = 324, + CC_User_UnusedAtThisTime = 325, + CC_User_ItemTags = 326, + CC_User_RaidSay = 327, + CC_User_MyPet = 328, + CC_User_DamageShield = 329, +}; + +//ZoneChange_Struct->success values +#define ZONE_ERROR_NOMSG 0 +#define ZONE_ERROR_NOTREADY -1 +#define ZONE_ERROR_VALIDPC -2 +#define ZONE_ERROR_STORYZONE -3 +#define ZONE_ERROR_NOEXPANSION -6 +#define ZONE_ERROR_NOEXPERIENCE -7 + + +typedef enum { + FilterNone = 0, + FilterGuildChat = 1, //0=hide, 1=show + FilterSocials = 2, //0=hide, 1=show + FilterGroupChat = 3, //0=hide, 1=show + FilterShouts = 4, //0=hide, 1=show + FilterAuctions = 5, //0=hide, 1=show + FilterOOC = 6, //0=hide, 1=show + FilterBadWords = 7, //0=hide, 1=show + FilterPCSpells = 8, //0=show, 1=hide, 2=group only + FilterNPCSpells = 9, //0=show, 1=hide + FilterBardSongs = 10, //0=show, 1=mine only, 2=group only, 3=hide + FilterSpellCrits = 11, //0=show, 1=mine only, 2=hide + FilterMeleeCrits = 12, //0=show, 1=hide + FilterSpellDamage = 13, //0=show, 1=mine only, 2=hide + FilterMyMisses = 14, //0=hide, 1=show + FilterOthersMiss = 15, //0=hide, 1=show + FilterOthersHit = 16, //0=hide, 1=show + FilterMissedMe = 17, //0=hide, 1=show + FilterDamageShields = 18, //0=show, 1=hide + FilterDOT = 19, //0=show, 1=hide + FilterPetHits = 20, //0=show, 1=hide + FilterPetMisses = 21, //0=show, 1=hide + FilterFocusEffects = 22, //0=show, 1=hide + FilterPetSpells = 23, //0=show, 1=hide + FilterHealOverTime = 24, //0=show, 1=hide + FilterUnknown25 = 25, + FilterUnknown26 = 26, + FilterUnknown27 = 27, + FilterUnknown28 = 28, + _FilterCount +} eqFilterType; + +typedef enum { + FilterHide, + FilterShow, + FilterShowGroupOnly, + FilterShowSelfOnly +} eqFilterMode; + +//im lazy today, dont wanna find/replace these +#define FILTER_DAMAGESHIELD FilterDamageShields +#define FILTER_NPCSPELLS FilterNPCSpells +#define FILTER_PCSPELLS FilterPCSpells +#define FILTER_BARDSONGS FilterBardSongs +#define FILTER_GUILDSAY FilterGuildChat +#define FILTER_SOCIALS FilterSocials +#define FILTER_GROUP FilterGroupChat +#define FILTER_SHOUT FilterShouts +#define FILTER_AUCTION FilterAuctions +#define FILTER_OOC FilterAuctions +#define FILTER_MYMISSES FilterMyMisses +#define FILTER_OTHERMISSES FilterOthersMiss +#define FILTER_OTHERHITS FilterOthersHit +#define FILTER_ATKMISSESME FilterMissedMe +#define FILTER_CRITSPELLS FilterSpellCrits +#define FILTER_CRITMELEE FilterMeleeCrits +#define FILTER_SPELLDAMAGE FilterSpellDamage +#define FILTER_DOTDAMAGE FilterDOT +#define FILTER_MYPETHITS FilterPetHits +#define FILTER_MYPETMISSES FilterPetMisses + + + +#define STAT_STR 0 +#define STAT_STA 1 +#define STAT_AGI 2 +#define STAT_DEX 3 +#define STAT_INT 4 +#define STAT_WIS 5 +#define STAT_CHA 6 +#define STAT_MAGIC 7 +#define STAT_COLD 8 +#define STAT_FIRE 9 +#define STAT_POISON 10 +#define STAT_DISEASE 11 +#define STAT_MANA 12 +#define STAT_HP 13 +#define STAT_AC 14 +#define STAT_ENDURANCE 15 +#define STAT_ATTACK 16 +#define STAT_HP_REGEN 17 +#define STAT_MANA_REGEN 18 +#define STAT_HASTE 19 +#define STAT_DAMAGE_SHIELD 20 + + /** + * Recast timer types. Used as an off set to charProfileStruct timers. + */ + enum RecastTypes + { + RecastTimer0 = 0, + RecastTimer1, + WeaponHealClickTimer, // 2 + MuramiteBaneNukeClickTimer, // 3 + RecastTimer4, + DispellClickTimer, // 5 (also click heal orbs?) + EpicTimer, // 6 + OoWBPClickTimer, // 7 + VishQuestClassItemTimer, // 8 + HealPotionTimer, // 9 + RecastTimer10, + RecastTimer11, + RecastTimer12, + RecastTimer13, + RecastTimer14, + RecastTimer15, + RecastTimer16, + RecastTimer17, + RecastTimer18, + ModRodTimer // 19 + }; + + enum GroupUpdateAction +{ + GUA_Joined = 0, + GUA_Left = 1, + GUA_LastLeft = 6, + GUA_FullGroupInfo = 7, + GUA_MakeLeader = 8, + GUA_Started = 9 +}; + +//0x1c is something... +static const uint8 FallingDamageType = 0xFC; +static const uint8 SpellDamageType = 0xe7; +static const uint8 DamageTypeUnknown = 0xFF; + +//indexed by 'SkillType' +static const uint8 SkillDamageTypes[HIGHEST_SKILL+1] = { + /* _1H_BLUNT */ 0, + /* _1H_SLASHING */ 1, + /* _2H_BLUNT */ 0, + /* _2H_SLASHING */ 1, + /* ABJURE */ SpellDamageType, + /* ALTERATION */ SpellDamageType, + /* APPLY_POISON */ DamageTypeUnknown, + /* ARCHERY */ 7, + /* BACKSTAB */ 8, + /* BIND_WOUND */ DamageTypeUnknown, + /* BASH */ 10, + /* BLOCKSKILL */ DamageTypeUnknown, + /* BRASS_INSTRUMENTS */ SpellDamageType, + /* CHANNELING */ DamageTypeUnknown, + /* CONJURATION */ SpellDamageType, + /* DEFENSE */ DamageTypeUnknown, + /* DISARM */ DamageTypeUnknown, + /* DISARM_TRAPS */ DamageTypeUnknown, + /* DIVINATION */ SpellDamageType, + /* DODGE */ DamageTypeUnknown, + /* DOUBLE_ATTACK */ DamageTypeUnknown, + /* DRAGON_PUNCH */ 21, + /* DUAL_WIELD */ DamageTypeUnknown, + /* EAGLE_STRIKE */ 23, + /* EVOCATION */ SpellDamageType, + /* FEIGN_DEATH */ 4, + /* FLYING_KICK */ 30, + /* FORAGE */ DamageTypeUnknown, + /* HAND_TO_HAND */ 4, + /* HIDE */ DamageTypeUnknown, + /* KICK */ 30, + /* MEDITATE */ DamageTypeUnknown, + /* MEND */ DamageTypeUnknown, + /* OFFENSE */ DamageTypeUnknown, + /* PARRY */ DamageTypeUnknown, + /* PICK_LOCK */ DamageTypeUnknown, + /* PIERCING */ 36, + /* RIPOSTE */ DamageTypeUnknown, + /* ROUND_KICK */ 30, + /* SAFE_FALL */ DamageTypeUnknown, + /* SENSE_HEADING */ DamageTypeUnknown, + /* SINGING */ SpellDamageType, + /* SNEAK */ DamageTypeUnknown, + /* SPECIALIZE_ABJURE */ DamageTypeUnknown, + /* SPECIALIZE_ALTERATION */ DamageTypeUnknown, + /* SPECIALIZE_CONJURATION */ DamageTypeUnknown, + /* SPECIALIZE_DIVINATION */ DamageTypeUnknown, + /* SPECIALIZE_EVOCATION */ DamageTypeUnknown, + /* PICK_POCKETS */ DamageTypeUnknown, + /* STRINGED_INSTRUMENTS */ SpellDamageType, + /* SWIMMING */ DamageTypeUnknown, + /* THROWING */ 51, + /* TIGER_CLAW */ 23, + /* TRACKING */ DamageTypeUnknown, + /* WIND_INSTRUMENTS */ SpellDamageType, + /* FISHING */ DamageTypeUnknown, + /* MAKE_POISON */ DamageTypeUnknown, + /* TINKERING */ DamageTypeUnknown, + /* RESEARCH */ DamageTypeUnknown, + /* ALCHEMY */ DamageTypeUnknown, + /* BAKING */ DamageTypeUnknown, + /* TAILORING */ DamageTypeUnknown, + /* SENSE_TRAPS */ DamageTypeUnknown, + /* BLACKSMITHING */ DamageTypeUnknown, + /* FLETCHING */ DamageTypeUnknown, + /* BREWING */ DamageTypeUnknown, + /* ALCOHOL_TOLERANCE */ DamageTypeUnknown, + /* BEGGING */ DamageTypeUnknown, + /* JEWELRY_MAKING */ DamageTypeUnknown, + /* POTTERY */ DamageTypeUnknown, + /* PERCUSSION_INSTRUMENTS */ SpellDamageType, + /* INTIMIDATION */ DamageTypeUnknown, + /* BERSERKING */ DamageTypeUnknown, + /* TAUNT */ DamageTypeUnknown, + /* FRENZY */ 74 +}; + +// Indexing positions into item material arrays +#define MATERIAL_HEAD 0 +#define MATERIAL_CHEST 1 +#define MATERIAL_ARMS 2 +#define MATERIAL_BRACER 3 +#define MATERIAL_HANDS 4 +#define MATERIAL_LEGS 5 +#define MATERIAL_FEET 6 +#define MATERIAL_PRIMARY 7 +#define MATERIAL_SECONDARY 8 +#define MAX_MATERIALS 9 //number of equipables + +// Used for worn NPC inventory tracking. NPCs don't use +// augments, so only the basic slots need to be kept track of. +#define MAX_WORN_INVENTORY 22 + + +/* +** Inventory Slot Equipment Enum +** Mostly used for third-party tools to reference inventory slots +** +** NOTE: Numbering for personal inventory goes top to bottom, then left to right +** It's the opposite for inside bags: left to right, then top to bottom +** Example: +** inventory: containers: +** 1 6 1 2 +** 2 7 3 4 +** 3 8 5 6 +** 4 9 7 8 +** 5 10 9 10 +** +*/ +enum InventorySlot +{ + //////////////////////// + // Equip slots + //////////////////////// + + SLOT_CHARM = 0, + SLOT_EAR01 = 1, + SLOT_HEAD = 2, + SLOT_FACE = 3, + SLOT_EAR02 = 4, + SLOT_NECK = 5, + SLOT_SHOULDER = 6, + SLOT_ARMS = 7, + SLOT_BACK = 8, + SLOT_BRACER01 = 9, + SLOT_BRACER02 = 10, + SLOT_RANGE = 11, + SLOT_HANDS = 12, + SLOT_PRIMARY = 13, + SLOT_SECONDARY = 14, + SLOT_RING01 = 15, + SLOT_RING02 = 16, + SLOT_CHEST = 17, + SLOT_LEGS = 18, + SLOT_FEET = 19, + SLOT_WAIST = 20, + SLOT_AMMO = 21, + + //////////////////////// + // All other slots + //////////////////////// + SLOT_PERSONAL_BEGIN = 22, + SLOT_PERSONAL_END = 29, + + SLOT_CURSOR = 30, + + SLOT_CURSOR_END = (int16)0xFFFE, // Last item on cursor queue + // Cursor bag slots are 331->340 (10 slots) + + // Personal Inventory Slots + // Slots 1 through 8 are slots 22->29 + // Inventory bag slots are 251->330 (10 slots per bag) + + // Tribute slots are 400-404? (upper bound unknown) + // storing these in worn item's map + + // Bank slots + // Bank slots 1 through 16 are slots 2000->2015 + // Bank bag slots are 2031->2190 + + // Shared bank slots + // Shared bank slots 1 through 2 are slots 2500->2501 + // Shared bank bag slots are 2531->2550 + + // Trade session slots + // Trade slots 1 through 8 are slots 3000->3007 + // Trade bag slots are technically 0->79 when passed to client, + // but in our code, we treat them as slots 3100->3179 + + // Slot used in OP_TradeSkillCombine for world tradeskill containers + SLOT_TRADESKILL = 1000, + SLOT_AUGMENT = 1001, + // SLOT_POWER_SOURCE = 9999, + // Value recognized by client for destroying an item + SLOT_INVALID = (int16)0xFFFF +}; + + +#endif diff --git a/common/eq_opcodes.h b/common/eq_opcodes.h new file mode 100644 index 000000000..000db47f1 --- /dev/null +++ b/common/eq_opcodes.h @@ -0,0 +1,522 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2003 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 04111-1307 USA +*/ +#ifndef EQ_OPCODES_H +#define EQ_OPCODES_H + + +//THIS FILE IS NOT USED ANYMORE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +/* +* +* +* +* +// solar: updated 2/12/04 +// +// Invalid opcodes have been \t'd out, confirmed have no \t +// all the ops have the first nibble as 0, so anything that's not 0x0??? +// is just an invalid opcode. if an opcode is known to be wrong set it to +// this to avoid conflicts with real ops +// +// codes preceded by '//' are ones that are known to be right but not used +// by any code yet +// +// codes preceded by '// not used' are ones that are probably wrong and not +// used anywhere +// + + +//im feeling lazy today and I dont wanna find-replace this include +#include "emu_opcodes.h" + + + +////////////////////////////////////// +// Zone.exe opcodes: +////////////////////////////////////// +//0x029f gives you money +//280 makes you stop receiving and sending chat messages +//260 send spell information +//45 logout +//72 player is in zone blah at dfdsfdsfg +//80 The door says +//89 Your expansion settings are: +//104 scribes a spell to your spellbook +//108,555 kicks player to server select +//123 money split from groups +//11 sense trap response +//296 name approval question +//338 failed to create new player guild +//401 pk question, 0x0191 response +//411 sacrifice question, 0x019b response +//193,213,243,244,431 chat messages +//425,499,505,506,525,526,596 crash +//451 translocate question, 0x01c3 response +//474,537 you escape from combat hiding yourself from view +//492 stat buffs +//496 your petition text is: +//581 you receive money msg with money +//562 you have control of yourself again +//613 CTD +//686 removes the unencumbered +#define LiveOP_Heartbeat 0x0176 // client sends this periodically +#define LiveOP_ReloadUI 0x02d7 +#define LiveOP_IncreaseStats 0x01eb +#define LiveOP_ApproveZone 0x0134 +#define LiveOP_Dye 0x01d5 + +// not used #define LiveOP_ExpansionSetting 0x0203 +// not used #define LiveOP_GainMoney 0x0209 +// not used #define LiveOP_Sacrifice 0x019b +// not used #define LiveOP_BecomePK 0x0191 +#define LiveOP_Stamina 0x0168 +#define LiveOP_ControlBoat 0x014d +#define LiveOP_MobUpdate 0x003e +#define LiveOP_ClientUpdate 0x0027 +#define LiveOP_ChannelMessage 0x0024 +#define LiveOP_SimpleMessage 0x01d7 +#define LiveOP_FormattedMessage 0x01d8 +//#define LiveOP_RaidInvite 0x01e4 // wrong. from seq - not used by eqemu +// #define LiveOP_RaidJoin 0x01e5 // from seq - not used by eqemu + // not used #define LiveOP_ApplyPoison 0x00b7 +#define LiveOP_TGB 0x01c6 // /targetgroupbuff +// #define LiveOP_CharInfo 0x0012 // /charinfo +// #define LiveOP_Movelog 0x0290 // /movelog +// #define LiveOP_Beta 0x02cb // /beta +#define LiveOP_TestBuff 0x0285 // /testbuffme +// #define LiveOP_Key 0x01e2 // /keys +#define LiveOP_Bind_Wound 0x012d +#define LiveOP_Charm 0x01ab +#define LiveOP_Begging 0x014c +#define LiveOP_MoveCoin 0x0152 +#define LiveOP_SpawnDoor 0x0292 +#define LiveOP_Sneak 0x009d // Clicked sneak - Doodman 10/10/2003 +#define LiveOP_ExpUpdate 0x0079 +#define LiveOP_DumpName 0x027d //no idea what this is: just tired of looking at it as unknown; updated by Shawn319 +// #define LiveOP_UpdateAA 0x0222 +#define LiveOP_RespondAA 0x01ea // AA table +#define LiveOP_SendAAStats 0x01c9 +#define LiveOP_SendAATable 0x0366 +#define LiveOP_AAAction 0x01e9 // Used for changing percent, buying? and activating skills +#define LiveOP_BoardBoat 0x00bb +#define LiveOP_LeaveBoat 0x00bc + +#define LiveOP_AdventureInfoRequest 0x02b8 //Cofruben:received when client click on the recruiter. +#define LiveOP_AdventureInfo 0x02b9 //Cofruben:sent when client right click on the recruiter. +#define LiveOP_AdventureRequest 0x02a6 //Cofruben: received when client press request button.1 normal,2 hard +#define LiveOP_AdventureDetails 0x02a8 //Cofruben: sent when client press request button. +#define LiveOP_LDoNButton 0x02a9 //Cofruben: Received button.(uint8)00 decline,01 accept. +#define LiveOP_AdventureData 0x02ba //Cofruben: Sent when client press accept button. + +#define LiveOP_AdventureFinish 0x02c9 //Cofruben:Used when you win/lose a dungeon +#define LiveOP_LeaveAdventure 0x02c6 //Cofruben:received when client press leave adventure button +#define LiveOP_AdventureUpdate 0x02ce + + +#define LiveOP_SendExpZonein 0x002b // 0 length packets +#define LiveOP_ZoneInSendName 0x01e4 + //Guild Opcodes +#define LiveOP_GuildLeader 0x01bf // /guildleader, was 00a7 +#define LiveOP_GuildPeace 0x009a // /guildpeace +#define LiveOP_GuildRemove 0x0132 // /guildremove +#define LiveOP_GuildMemberList 0x0059 //fixed by Shawn319 (dec 18th patch) +#define LiveOP_GuildMemberUpdate 0x026e +#define LiveOP_GuildInvite 0x0130 // /guildinvite +#define LiveOP_GuildMOTD 0x01c0 // /guildmotd, was 01bf + // not used #define LiveOP_GuildManagement 0x005e // LoY guild mgm't tool +#define LiveOP_GuildPublicNote 0x003c +#define LiveOP_GetGuildMOTD 0x027e // /getguildmotd +#define LiveOP_GuildDemote 0x0277 +#define LiveOP_GuildInviteAccept 0x0131 +#define LiveOP_GuildWar 0x00a4 // /guildwar + #define LiveOP_GuildUpdate 0x0b41 +#define LiveOP_GuildDelete 0x0133 +#define LiveOP_GuildManageRemove 0x0233 +#define LiveOP_GuildManageAdd 0x022d +#define LiveOP_GuildManageStatus 0x0039 + //Bazaar +#define LiveOP_Trader 0x01e8 // /trader +#define LiveOP_Bazaar 0x01e7 // /bazaar search + +#define LiveOP_BecomeTrader 0x01c4 +#define LiveOP_BazaarInspect 0x01f4 +#define LiveOP_TraderItemUpdate 0x006e +#define LiveOP_TraderDelItem 0x017c +#define LiveOP_TraderShop 0x01eb // right-click on a trader in bazaar + +#define LiveOP_TraderBuy 0x01ca // buy from a trader in bazaar + +#define LiveOP_PetCommands 0x01ac +#define LiveOP_TradeSkillCombine 0x0042 +#define LiveOP_AugmentItem 0x02e5 +#define LiveOP_ItemName 0x0367 + + + +//Shops +#define LiveOP_ShopItem 0x02cd // Send merchant item data to client (header = 0x64) +#define LiveOP_ShopPlayerBuy 0x0065 + // not used #define LiveOP_ShopTakeMoney 0x0066 +#define LiveOP_ShopPlayerSell 0x006a +#define LiveOP_ShopDelItem 0x006d +#define LiveOP_ShopEndConfirm 0x0f6d +#define LiveOP_ShopRequest 0x00f7 // right-click on merchant +#define LiveOP_ShopEnd 0x006c // Finished shopping at merchant + +#define LiveOP_AdventureMerchantRequest 0x02d1 +#define LiveOP_AdventureMerchantResponse 0x02d2 +#define LiveOP_AdventureMerchantPurchase 0x02d3 +#define LiveOP_AdventurePointsUpdate 0x02e3 + +// #define LiveOP_LFPCommand 0x0272 // Looking for player +// #define LiveOP_LFPGetMatchesRequest 0x0273 +// #define LiveOP_LFPGetMatchesResponse 0x0275 + +// #define LiveOP_LFGGetMatchesRequest 0x0271 +// #define LiveOP_LFGGetMatchesResponse 0x0274 +#define LiveOP_LFGCommand 0x0270 // When client issues /LFG command +// #define LiveOP_LFGResponse 0x01b1 +#define LiveOP_LFGAppearance 0x01d0 // Some other char in zone turns LFG on/off + +#define LiveOP_MoneyUpdate 0x01b5 + +#define LiveOP_GroupDelete 0x0721 +#define LiveOP_GroupAcknowledge 0x0272 + +#define LiveOP_GroupUpdate 0x024a +#define LiveOP_GroupInvite 0x025f +#define LiveOP_GroupDisband 0x00ff +#define LiveOP_GroupInvite2 0x00d5 +#define LiveOP_GroupFollow 0x025e +#define LiveOP_GroupFollow2 0x00d7 + +#define LiveOP_GroupCancelInvite 0x00d6 + +#define LiveOP_Split 0x0156 // /split +#define LiveOP_Jump 0x00d8 // not used atm but will be when stamina is fixed +#define LiveOP_ConsiderCorpse 0x01d6 +#define LiveOP_SkillUpdate 0x0064 + +#define LiveOP_GMEndTrainingResponse 0x0178 +#define LiveOP_GMEndTraining 0x013c +#define LiveOP_GMTrainSkill 0x0175 +#define LiveOP_GMTraining 0x013b + +#define LiveOP_ConsumeAmmo 0x017b +#define LiveOP_CombatAbility 0x0171 + +#define LiveOP_TrackUnknown 0x009c +#define LiveOP_TrackTarget 0x0234 +#define LiveOP_Track 0x0286 +#define LiveOP_ReadBook 0x0297 + +#define LiveOP_ItemLinkClick 0x001f +#define LiveOP_ItemLinkResponse 0x01f4 +#define LiveOP_ItemLinkText 0x01d9 + +#define LiveOP_RezzRequest 0x0a41 +#define LiveOP_RezzAnswer 0x00e5 +#define LiveOP_RezzComplete 0x019b + +#define LiveOP_MoveDoor 0x0128 +#define LiveOP_ClickDoor 0x0127 // Click door +#define LiveOP_SendZonepoints 0x0247 // Coords in a zone that will port you to another zone +#define LiveOP_SetRunMode 0x008c // Client hit the "run" button (or control+r) +#define LiveOP_InspectRequest 0x0248 +#define LiveOP_InspectAnswer 0x0249 +#define LiveOP_SenseTraps 0x0187 // Clicked sense traps - @Doodman 10/10/2003 +#define LiveOP_DisarmTraps 0x018e // Clicked disarm traps - @Doodman 10/10/2003 +#define LiveOP_Assist 0x01bc +#define LiveOP_PickPocket 0x0240 + +#define LiveOP_LootRequest 0x0119 +#define LiveOP_EndLootRequest 0x011a +#define LiveOP_MoneyOnCorpse 0x011b +#define LiveOP_LootComplete 0x0179 +#define LiveOP_LootItem 0x013f + // solar: there was an LiveOP_PlaceItem synonym for this +#define LiveOP_MoveItem 0x0151 // Client moving an item from one slot to another (user action) + +#define LiveOP_WhoAllRequest 0x0056 +#define LiveOP_WhoAllResponse 0x0229 +#define LiveOP_Consume 0x0167 +#define LiveOP_AutoAttack 0x0172 +#define LiveOP_AutoAttack2 0x0186 +#define LiveOP_TargetMouse 0x0173 // mouse targetting a person (also: Pressing F* key to target) +#define LiveOP_TargetCommand 0x01ba // /target user +#define LiveOP_TargetReject 0x01d8 // When /target fails (// solar: untested) +#define LiveOP_Hide 0x009e +#define LiveOP_Forage 0x012e // Clicked forage - @Doodman 10/10/2003 +#define LiveOP_Fishing 0x0077 +// #define LiveOP_Adventure 0x02d0 // /adventure +// #define LiveOP_Feedback 0x0161 // /feedback +#define LiveOP_Bug 0x0246 // /bug +#define LiveOP_Emote 0x00f2 // /me goes blah +#define LiveOP_EmoteAnim 0x0140 // solar: untested +#define LiveOP_Consider 0x015c +#define LiveOP_FaceChange 0x01cb // /face + // not used #define LiveOP_Report 0x01e0 +#define LiveOP_RandomReq 0x0197 +#define LiveOP_RandomReply 0x0087 +#define LiveOP_Camp 0x01c3 +#define LiveOP_YellForHelp 0x0192 +#define LiveOP_SafePoint 0x00ef + +#define LiveOP_Buff 0x0157 +#define LiveOP_BuffFadeMsg 0x00c0 +#define LiveOP_MultiLineMsg 0x0440 // is this still good for anything? +#define LiveOP_SpecialMesg 0x021c // Communicate textual info to client +#define LiveOP_Consent 0x0013 // /consent +#define LiveOP_ConsentResponse 0x029d +#define LiveOP_Deny 0x02d4 +#define LiveOP_Stun 0x016c +#define LiveOP_BeginCast 0x0021 +#define LiveOP_CastSpell 0x00be +#define LiveOP_InterruptCast 0x01a8 +#define LiveOP_Death 0x0105 +#define LiveOP_FeignDeath 0x023f +#define LiveOP_Illusion 0x012b +#define LiveOP_LevelUpdate 0x0078 +#define LiveOP_LevelAppearance 0x0371 + // not used #define LiveOP_LocateCorpse 0x00d1 //Sent when a client casts Locate Corpse spells? + +#define LiveOP_MemorizeSpell 0x00c2 // Memming a spell from book to spell slot +#define LiveOP_HPUpdate 0x0244 // Update HP % of a PC or NPC +#define LiveOP_SendHPTarget 0x022e +#define LiveOP_Mend 0x007d + #define LiveOP_MendHPUpdate 0x009b +#define LiveOP_Taunt 0x0160 + +// #define LiveOP_Summoncorpse 0x02b5 // /summoncorpse +// #define LiveOP_GMSearchCorpse 0x0097 // GM /searchcorpse - Search all zones for named corpse +// #define LiveOP_SearchCorpse LiveOP_GMSearchCorpse // /searchcorpse +#define LiveOP_GMDelCorpse 0x0199 // /delcorpse +#define LiveOP_GMFind 0x0047 // GM /find - ? +// not used #define LiveOP_FindResponse 0x02cc +#define LiveOP_GMServers 0x0020 // GM /servers - ? +#define LiveOP_GMGoto 0x010b // GM /goto - Transport to another loc +#define LiveOP_GMSummon 0x028c // GM /summon - Summon PC to self +#define LiveOP_GMKick 0x010a // GM /kick - Boot player +#define LiveOP_GMKill 0x0109 // GM /kill - Insta kill mob/pc +#define LiveOP_GMNameChange 0x0b40 // /name +#define LiveOP_GMLastName 0x00a3 // GM /lastname - Change user lastname +#define LiveOP_GMToggle 0x01b3 // GM /toggle - Toggle ability to receive tells from other PC's +#define LiveOP_GMEmoteZone 0x028f // GM /emotezone - Send zonewide emote +#define LiveOP_GMBecomeNPC 0x0074 // GM /becomenpc - Become an NPC +// (TODO: Use opcode 0x012d, which is also sent with LiveOP_GMBecomeNPC to create correct npc +// #define LiveOP_GMApproval 0x01b0 // GM /approval - Name approval duty? +// not used #define LiveOP_NameApproval 0x011f //Name approval +#define LiveOP_GMHideMe 0x00de // GM /hideme - Remove self from spawn lists and make invis +// #define LiveOP_GMInquire 0x00da // GM /inquire - Search soulmark data +// #define LiveOP_GMSoulmark 0x00dc // GM /praise /warn - Add soulmark comment to user file +#define LiveOP_GMZoneRequest 0x0184 // GM /zone - Transport to another zone +#define LiveOP_GMZoneRequest2 0x0239 // GM /zone 2 + +#define LiveOP_Petition 0x0068 +#define LiveOP_PetitionRefresh 0x0085 +#define LiveOP_PDeletePetition 0x01ee +#define LiveOP_PetitionBug 0x0092 // 0094 feedback 0095 guide +// #define LiveOP_PViewPetition 0x01ef +#define LiveOP_PetitionUpdate 0x0069 // Updates the Petitions in the Que +#define LiveOP_PetitionCheckout 0x0076 // Petition Checkout +#define LiveOP_PetitionCheckout2 0x0056 //Also sent when a player checks out a petition Possibly requesting who all +#define LiveOP_PetitionDelete 0x0091 // Client Petition Delete Request +#define LiveOP_PetitionResolve 0x02b4 // Client Petition Resolve Request +#define LiveOP_PetitionCheckIn 0x007e // Petition Checkin +#define LiveOP_PetitionUnCheckout 0x0090 + +#define LiveOP_PetitionQue 0x01ec //GM looking at petitions + + +#define LiveOP_SetServerFilter 0x01bb + // not used #define LiveOP_SetServerFilterAck 0x0341 + +#define LiveOP_NewSpawn 0x0218 // New NPC or PC entering zone +#define LiveOP_Animation 0x0140 +// #define LiveOP_MobHealth 0x022e // health sent when a player clicks on the mob +#define LiveOP_ZoneChange 0x0142 // Client requesting transfer to a different zone +#define LiveOP_DeleteSpawn 0x00f3 // Remove a spawn from the current zone + // not used #define LiveOP_ConfirmDelete 0x0178 //Client sends this to server to confirm op_deletespawn + // not used #define LiveOP_NewCorpse 0x00da +#define LiveOP_CrashDump 0x0265 + // not used #define LiveOP_CastOn 0x0119 +#define LiveOP_EnvDamage 0x00e8 + +#define LiveOP_Action 0x0101 +#define LiveOP_Damage 0x00e2 // seq calls this Action2 +#define LiveOP_ManaChange 0x00bf +#define LiveOP_ClientError 0x027c +// #define LiveOP_LoadSpellSet 0x02a4 +#define LiveOP_Save 0x00fb // Client asking server to save user state +#define LiveOP_LocInfo 0x0316 + +#define LiveOP_Surname 0x0188 +#define LiveOP_SwapSpell 0x018f // Swapping spell positions within book +#define LiveOP_DeleteSpell 0x01db +#define LiveOP_CloseContainer 0x029f //Client closing world container (i.e., forge) +#define LiveOP_ClickObjectAck 0x029f //Client closing world container (i.e., forge) +#define LiveOP_CreateObject 0x00fa //Zone objects (pok books, objects on ground, etc) +#define LiveOP_ClickObject 0x00f9 //Client clicking on object +#define LiveOP_ClearObject 0x01c1 +#define LiveOP_ZoneUnavail 0x0265 +// #define LiveOP_FlashMessage 0x02cd //wierd opcode that flashes message on screen +#define LiveOP_ItemPacket 0x02e0 // Variety of ways for sending out item data + //0x0283 hmm +#define LiveOP_TradeRequest 0x029a // Client request trade session +#define LiveOP_TradeRequestAck 0x0037 // Trade request recipient is acknowledging they are able to trade +#define LiveOP_TradeAcceptClick 0x002d + // not used #define LiveOP_ItemToTrade 0x0031 +#define LiveOP_TradeMoneyUpdate 0x0162 +#define LiveOP_TradeCoins 0x0036 +#define LiveOP_CancelTrade 0x002e +#define LiveOP_FinishTrade 0x002f +// #define LiveOP_Translocate 0x01c5 + // not used #define LiveOP_WebUpdate 0x01f2 +#define LiveOP_SaveOnZoneReq 0x00a1 +#define LiveOP_Logout 0x0185 // Last opcode seny by server when you zone or camp +#define LiveOP_RequestDuel 0x0298 //Shawn319: Fixed 1/3/04 + // LiveOP_DeclineDuel 0x029c +#define LiveOP_DuelResponse 0x0a5d +#define LiveOP_DuelResponse2 0x016e +#define LiveOP_InstillDoubt 0x007c + +#define LiveOP_SafeFallSuccess 0x00ac +#define LiveOP_DisciplineUpdate 0x02fb + +//Tribute Master + +//send initial tribute update right before inventory +//send the LiveOP_TributeInfo's in response to LiveOP_ReqClientSpawn +#define LiveOP_TributeUpdate 0x02f2 +#define LiveOP_TributeItem 0x02f3 +#define LiveOP_TributePointUpdate 0x02f4 +#define LiveOP_SendTributes 0x02f5 +#define LiveOP_TributeInfo 0x02f6 +#define LiveOP_SelectTribute 0x02f7 +#define LiveOP_TributeTimer 0x02f8 +#define LiveOP_StartTribute 0x02f9 +#define LiveOP_TributeNPC 0x02fa //no idea what this is for +#define LiveOP_TributeMoney 0x02fe +#define LiveOP_TributeToggle 0x0364 + +//New Tradeskill Interface +#define LiveOP_RecipesFavorite 0x0322 +#define LiveOP_RecipesSearch 0x01f9 +#define LiveOP_RecipeReply 0x01fa +#define LiveOP_RecipeDetails 0x01fb +#define LiveOP_RecipeAutoCombine 0x01fc + +//for the 'find' command +#define LiveOP_FindPersonRequest 0x02db +#define LiveOP_FindPersonReply 0x02dc + +#define LiveOP_Shielding 0x01dd //this is prolly wrong + + ////////////////////////////////////// + // Zone.exe opcodes for login sequence: + ////////////////////////////////////// +#define LiveOP_SetDataRate 0x0198 // Client sending datarate.txt value +#define LiveOP_ZoneEntry 0x023b // Info about char entering zone.. +#define LiveOP_PlayerProfile 0x006b // Basic player info (no inventory) +#define LiveOP_CharInventory 0x0291 // Full inventory of player +#define LiveOP_ZoneSpawns 0x0170 // All spawns in current zone +#define LiveOP_TimeOfDay 0x0026 // Notify client of current time +#define LiveOP_Weather 0x015b // Weather update +#define LiveOP_ReqNewZone 0x00ec // Client requesting NewZone_Struct +#define LiveOP_NewZone 0x00eb // Info about zone being loaded (exp multiplier, etc) +#define LiveOP_ReqClientSpawn 0x00fd // Client requesting spawn data +#define LiveOP_SpawnAppearance 0x012F // Sets spawnid/animation/equipment +#define LiveOP_ClientReady 0x0086 // Client fully connected, finished loading +#define LiveOP_ZoneComplete LiveOP_ClientReady //Client sends upon successful zone in + +////////////////////////////////////// +// World.exe opcodes: +////////////////////////////////////// +#define LiveOP_LoginComplete 0x02db +#define LiveOP_ApproveWorld 0x0195 +#define LiveOP_LogServer 0x035f +#define LiveOP_MOTD 0x01b2 // Server message of the day +#define LiveOP_SendLoginInfo 0x0251 +#define LiveOP_DeleteCharacter 0x00ea // Delete character @ char select +#define LiveOP_SendCharInfo 0x0102 // Send all chars visible @ char select +#define LiveOP_ExpansionInfo 0x00e1 // Which expansions user has +#define LiveOP_CharacterCreate 0x0104 // Create character @ char select +#define LiveOP_RandomNameGenerator 0x02ab // Returns a random name +#define LiveOP_GuildsList 0x005d // Server sending client list of guilds +#define LiveOP_ApproveName 0x0125 // Approving new character name @ char creation +#define LiveOP_EnterWorld 0x0261 // Server approval for client to enter world +#define LiveOP_World_Client_CRC1 0x015a // Contains a snippet of spell data +#define LiveOP_World_Client_CRC2 0x015e // Second client verification packet +#define LiveOP_SetChatServer 0x0269 // Chatserver? IP,Port,servername.Charname,password(?) +#define LiveOP_ZoneServerInfo 0x0264 // Zone server? IP,0's,uint16? +// not used #define LiveOP_UserCompInfo 0x02a5 // User submitting computer information + +////////////////////////////////////// +// Zone.exe/World.exe shared opcodes: +////////////////////////////////////// +#define LiveOP_AckPacket 0x0017 // Appears to be generic ack at the presentation level +#define LiveOP_WearChange 0x012c // Client texture/color update + +//////////////////////////// +// OLD opcodes: +//////////////////////////// + +// not used #define LiveOP_SummonedItem 0x0841 +// not used #define LiveOP_HarmTouch 0x0E41 +// not used #define LiveOP_Drink 0x0641 +// not used #define LiveOP_Medding 0x0841 +// not used #define LiveOP_SenseHeading 0x00c3 // Clicked sense heading button - solar: not used anymore + + #define PET_BACKOFF 1 + #define PET_GETLOST 2 + #define PET_HEALTHREPORT 4 + #define PET_GUARDHERE 5 + #define PET_GUARDME 6 + #define PET_ATTACK 7 + #define PET_FOLLOWME 8 + #define PET_SITDOWN 9 + #define PET_STANDUP 10 + #define PET_TAUNT 11 + #define PET_HOLD 12 + #define PET_NOTAUNT 14 + #define PET_LEADER 16 + #define PET_SLUMBER 17 + + +// Agz: The following is from the old source I used as base +/ENUMERATED PACKET OPCODE + #define ALL_FINISH 0x0005 + #define LS_REQUEST_VERSION 0x0059 + #define LS_SEND_VERSION 0x0059 + #define LS_SEND_LOGIN_INFO 0x0001 + #define LS_SEND_SESSION_ID 0x0004 + #define LS_REQUEST_UPDATE 0x0052 + #define LS_SEND_UPDATE 0x0052 + + #define LS_REQUEST_SERVERLIST 0x0046 + #define LS_SEND_SERVERLIST 0x0046 + + #define LS_REQUEST_SERVERSTATUS 0x0048 + #define LS_SEND_SERVERSTATUS 0x004A + #define LS_GET_WORLDID 0x0047 + #define LS_SEND_WORLDID 0x0047 + #define WS_SEND_LOGIN_INFO 0x5818 + #define WS_SEND_LOGIN_APPROVED 0x0710 + #define WS_SEND_LOGIN_APPROVED2 0x0180 + #define WS_SEND_CHAR_INFO 0x4720*/ +#endif diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h new file mode 100644 index 000000000..770cbd52e --- /dev/null +++ b/common/eq_packet_structs.h @@ -0,0 +1,5045 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 EQ_PACKET_STRUCTS_H +#define EQ_PACKET_STRUCTS_H + +#include "types.h" +#include +#include +#include +#include "../common/version.h" +//#include "../common/item_struct.h" + +static const uint32 BUFF_COUNT = 25; +static const uint32 BLOCKED_BUFF_COUNT = 20; + +#include "eq_constants.h" + +/* +** Compiler override to ensure +** byte aligned structures +*/ +#pragma pack(1) + +struct LoginInfo_Struct { +/*000*/ char login_info[64]; +/*064*/ uint8 unknown064[124]; +/*188*/ uint8 zoning; // 01 if zoning, 00 if not +/*189*/ uint8 unknown189[275]; +/*488*/ +}; + +struct EnterWorld_Struct { +/*000*/ char name[64]; +/*064*/ uint32 tutorial; // 01 on "Enter Tutorial", 00 if not +/*068*/ uint32 return_home; // 01 on "Return Home", 00 if not +}; + +struct ExpansionInfo_Struct { +/*0000*/ uint32 Expansions; +}; + +/* Name Approval Struct */ +/* Len: */ +/* Opcode: 0x8B20*/ +struct NameApproval +{ + char name[64]; + uint32 race; + uint32 class_; + uint32 deity; +}; + +/* +** Entity identification struct +** Size: 4 bytes +** OPCodes: OP_DeleteSpawn, OP_Assist +*/ +struct EntityId_Struct +{ +/*00*/ uint32 entity_id; +/*04*/ +}; + +struct Duel_Struct +{ + uint32 duel_initiator; + uint32 duel_target; +}; + +struct DuelResponse_Struct +{ + uint32 target_id; + uint32 entity_id; + uint32 unknown; +}; + +//adventure stuff +enum AdventureObjective +{ + Adventure_Random = 0, + Adventure_Assassinate = 1, + Adventure_Kill = 2, + Adventure_Collect = 3, + Adventure_Rescue = 4 +}; + +typedef enum +{ + LDoNTypeMechanical = 1, + LDoNTypeMagical = 2, + LDoNTypeCursed = 3, +} LDoNChestTypes; + +struct LDoNTrapTemplate +{ + uint32 id; + LDoNChestTypes type; + uint32 spell_id; + uint16 skill; + uint8 locked; +}; + +/////////////////////////////////////////////////////////////////////////////// + + +/* +** Color_Struct +** Size: 4 bytes +** Used for convenience +** Merth: Gave struct a name so gcc 2.96 would compile +** +*/ +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; + }; +}; + +/* +** Character Selection Struct +** Length: 1704 Bytes +** +*/ +struct CharacterSelect_Struct { +/*0000*/ uint32 race[10]; // Characters Race +/*0040*/ Color_Struct cs_colors[10][9]; // Characters Equipment Colors +/*0400*/ uint8 beardcolor[10]; // Characters beard Color +/*0410*/ uint8 hairstyle[10]; // Characters hair style +/*0420*/ uint32 equip[10][9]; // 0=helm, 1=chest, 2=arm, 3=bracer, 4=hand, 5=leg, 6=boot, 7=melee1, 8=melee2 (Might not be) +/*0780*/ uint32 secondary[10]; // Characters secondary IDFile number +/*0820*/ uint32 drakkin_heritage[10]; // added for SoF +/*0860*/ uint32 drakkin_tattoo[10]; // added for SoF +/*0900*/ uint32 drakkin_details[10]; // added for SoF +/*0940*/ uint32 deity[10]; // Characters Deity +/*0980*/ uint8 gohome[10]; // 1=Go Home available, 0=not +/*0990*/ uint8 tutorial[10]; // 1=Tutorial available, 0=not +/*1000*/ uint8 beard[10]; // Characters Beard Type +/*1010*/ uint8 unknown902[10]; // 10x ff +/*1020*/ uint32 primary[10]; // Characters primary IDFile number +/*1060*/ uint8 haircolor[10]; // Characters Hair Color +/*1070*/ uint8 unknown0962[2]; // 2x 00 +/*1072*/ uint32 zone[10]; // Characters Current Zone +/*1112*/ uint8 class_[10]; // Characters Classes +/*1022*/ uint8 face[10]; // Characters Face Type +/*1032*/ char name[10][64]; // Characters Names +/*1672*/ uint8 gender[10]; // Characters Gender +/*1682*/ uint8 eyecolor1[10]; // Characters Eye Color +/*1692*/ uint8 eyecolor2[10]; // Characters Eye 2 Color +/*1702*/ uint8 level[10]; // Characters Levels +/*1712*/ +}; + +/* +** Generic Spawn Struct +** Length: 257 Bytes +** Fields from old struct not yet found: +** float size; +** float walkspeed; // probably one of the ff 33 33 33 3f +** float runspeed; // probably one of the ff 33 33 33 3f +** uint8 traptype; // 65 is disarmable trap, 66 and 67 are invis triggers/traps +** uint8 npc_armor_graphic; // 0xFF=Player, 0=none, 1=leather, 2=chain, 3=steelplate +** uint8 npc_helm_graphic; // 0xFF=Player, 0=none, 1=leather, 2=chain, 3=steelplate +** +*/ + +/* +** Generic Spawn Struct +** Length: 383 Octets +** Used in: +** spawnZoneStruct +** dbSpawnStruct +** petStruct +** newSpawnStruct +*/ +/* +showeq -> eqemu +sed -e 's/_t//g' -e 's/seto_0xFF/set_to_0xFF/g' +*/ +struct Spawn_Struct { +/*0000*/ uint8 unknown0000; +/*0001*/ uint8 gm; // 0=no, 1=gm +/*0002*/ uint8 unknown0003; +/*0003*/ uint8 aaitle; // 0=none, 1=general, 2=archtype, 3=class +/*0004*/ uint8 unknown0004; +/*0005*/ uint8 anon; // 0=normal, 1=anon, 2=roleplay +/*0006*/ uint8 face; // Face id for players +/*0007*/ char name[64]; // Player's Name +/*0071*/ uint16 deity; // Player's Deity +/*0073*/ uint16 unknown0073; +/*0075*/ float size; // Model size +/*0079*/ uint32 unknown0079; +/*0083*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse,a +/*0084*/ uint8 invis; // Invis (0=not, 1=invis) +/*0085*/ uint8 haircolor; // Hair color +/*0086*/ uint8 curHp; // Current hp %%% wrong +/*0087*/ uint8 max_hp; // (name prolly wrong)takes on the value 100 for players, 100 or 110 for NPCs and 120 for PC corpses... +/*0088*/ uint8 findable; // 0=can't be found, 1=can be found +/*0089*/ uint8 unknown0089[5]; +/*0094*/ signed deltaHeading:10;// change in heading + signed x:19; // x coord + signed padding0054:3; // ***Placeholder +/*0098*/ signed y:19; // y coord + signed animation:10; // animation + signed padding0058:3; // ***Placeholder +/*0102*/ signed z:19; // z coord + signed deltaY:13; // change in y +/*0106*/ signed deltaX:13; // change in x + unsigned heading:12; // heading + signed padding0066:7; // ***Placeholder +/*0110*/ signed deltaZ:13; // change in z + signed padding0070:19; // ***Placeholder +/*0114*/ uint8 eyecolor1; // Player's left eye color +/*0115*/ uint8 unknown0115[11]; // Was [24] +/*0126*/ uint8 StandState; // stand state for SoF+ 0x64 for normal animation +/*0127*/ uint32 drakkin_heritage; // Added for SoF +/*0131*/ uint32 drakkin_tattoo; // Added for SoF +/*0135*/ uint32 drakkin_details; // Added for SoF +/*0139*/ uint8 showhelm; // 0=no, 1=yes +/*0140*/ uint8 unknown0140[4]; +/*0144*/ uint8 is_npc; // 0=no, 1=yes +/*0145*/ uint8 hairstyle; // Hair style +/*0146*/ uint8 beard; // Beard style (not totally, sure but maybe!) +/*0147*/ uint8 unknown0147[4]; +/*0151*/ uint8 level; // Spawn Level +/*0152*/ uint8 unknown0259[4]; // ***Placeholder +/*0156*/ uint8 beardcolor; // Beard color +/*0157*/ char suffix[32]; // Player's suffix (of Veeshan, etc.) +/*0189*/ uint32 petOwnerId; // If this is a pet, the spawn id of owner +/*0193*/ uint8 guildrank; // 0=normal, 1=officer, 2=leader +/*0194*/ uint8 unknown0194[3]; +/*0197*/ union + { + struct + { + /*0197*/ uint32 equip_helmet; // Equipment: Helmet Visual + /*0201*/ uint32 equip_chest; // Equipment: Chest Visual + /*0205*/ uint32 equip_arms; // Equipment: Arms Visual + /*0209*/ uint32 equip_bracers; // Equipment: Bracers Visual + /*0213*/ uint32 equip_hands; // Equipment: Hands Visual + /*0217*/ uint32 equip_legs; // Equipment: Legs Visual + /*0221*/ uint32 equip_feet; // Equipment: Feet Visual + /*0225*/ uint32 equip_primary; // Equipment: Primary Visual + /*0229*/ uint32 equip_secondary; // Equipment: Secondary Visual + } equip; + /*0197*/ uint32 equipment[MAX_MATERIALS]; // Array elements correspond to struct equipment above + }; +/*0233*/ float runspeed; // Speed when running +/*0036*/ uint8 afk; // 0=no, 1=afk +/*0238*/ uint32 guildID; // Current guild +/*0242*/ char title[32]; // Title +/*0274*/ uint8 unknown0274; +/*0275*/ uint8 set_to_0xFF[8]; // ***Placeholder (all ff) +/*0283*/ uint8 helm; // Helm texture +/*0284*/ uint32 race; // Spawn race +/*0288*/ uint32 unknown0288; +/*0292*/ char lastName[32]; // Player's Lastname +/*0324*/ float walkspeed; // Speed when walking +/*0328*/ uint8 unknown0328; +/*0329*/ uint8 is_pet; // 0=no, 1=yes +/*0330*/ uint8 light; // Spawn's lightsource %%% wrong +/*0331*/ uint8 class_; // Player's class +/*0332*/ uint8 eyecolor2; // Left eye color +/*0333*/ uint8 flymode; +/*0334*/ uint8 gender; // Gender (0=male, 1=female) +/*0335*/ uint8 bodytype; // Bodytype +/*0336*/ uint8 unknown0336[3]; +union +{ +/*0339*/ uint8 equip_chest2; // Second place in packet for chest texture (usually 0xFF in live packets) + // Not sure why there are 2 of them, but it effects chest texture! +/*0339*/ uint8 mount_color; // drogmor: 0=white, 1=black, 2=green, 3=red + // horse: 0=brown, 1=white, 2=black, 3=tan +}; +/*0340*/ uint32 spawnId; // Spawn Id +/*0344*/ uint8 unknown0344[3]; +/*0347*/ uint8 IsMercenary; +/*0348*/ union + { + struct + { + /*0348*/ Color_Struct color_helmet; // Color of helmet item + /*0352*/ Color_Struct color_chest; // Color of chest item + /*0356*/ Color_Struct color_arms; // Color of arms item + /*0360*/ Color_Struct color_bracers; // Color of bracers item + /*0364*/ Color_Struct color_hands; // Color of hands item + /*0368*/ Color_Struct color_legs; // Color of legs item + /*0372*/ Color_Struct color_feet; // Color of feet item + /*0376*/ Color_Struct color_primary; // Color of primary item + /*0380*/ Color_Struct color_secondary; // Color of secondary item + } equipment_colors; + /*0348*/ Color_Struct colors[MAX_MATERIALS]; // Array elements correspond to struct equipment_colors above + }; +/*0384*/ uint8 lfg; // 0=off, 1=lfg on +/*0385*/ + + bool DestructibleObject; // Only used to flag as a destrible object + char DestructibleModel[64]; // Model of the Destructible Object - Required - Seen "DEST_TNT_G" + char DestructibleName2[64]; // Secondary name - Not Required - Seen "a_tent" + char DestructibleString[64]; // Unknown - Not Required - Seen "ZoneActor_01186" + uint32 DestructibleAppearance; // Damage Appearance + uint32 DestructibleUnk1; + uint32 DestructibleID1; + uint32 DestructibleID2; + uint32 DestructibleID3; + uint32 DestructibleID4; + uint32 DestructibleUnk2; + uint32 DestructibleUnk3; + uint32 DestructibleUnk4; + uint32 DestructibleUnk5; + uint32 DestructibleUnk6; + uint32 DestructibleUnk7; + uint8 DestructibleUnk8; + uint32 DestructibleUnk9; + +}; + +/* +** New Spawn +** Length: 176 Bytes +** OpCode: 4921 +*/ +struct NewSpawn_Struct +{ + struct Spawn_Struct spawn; // Spawn Information +}; + +struct ClientZoneEntry_Struct { +/*0000*/ uint32 unknown00; +/*0004*/ char char_name[64]; // Character Name +}; + +/* +** Server Zone Entry Struct +** OPCodes: OP_ServerZoneEntry +** +*/ +struct ServerZoneEntry_Struct +{ + struct NewSpawn_Struct player; +}; + +struct NewZone_Struct { +/*0000*/ char char_name[64]; // Character Name +/*0064*/ char zone_short_name[32]; // Zone Short Name +/*0096*/ char zone_long_name[278]; // Zone Long Name +/*0374*/ uint8 ztype; // Zone type (usually FF) +/*0375*/ uint8 fog_red[4]; // Zone fog (red) +/*0379*/ uint8 fog_green[4]; // Zone fog (green) +/*0383*/ uint8 fog_blue[4]; // Zone fog (blue) +/*0387*/ uint8 unknown323; +/*0388*/ float fog_minclip[4]; +/*0404*/ float fog_maxclip[4]; +/*0420*/ float gravity; +/*0424*/ uint8 time_type; +/*0425*/ uint8 unknown360[49]; +/*0474*/ uint8 sky; // Sky Type +/*0475*/ uint8 unknown331[13]; // ***Placeholder +/*0488*/ float zone_exp_multiplier; // Experience Multiplier +/*0492*/ float safe_y; // Zone Safe Y +/*0496*/ float safe_x; // Zone Safe X +/*0500*/ float safe_z; // Zone Safe Z +/*0504*/ float max_z; // Guessed +/*0508*/ float underworld; // Underworld, min z (Not Sure?) +/*0512*/ float minclip; // Minimum View Distance +/*0516*/ float maxclip; // Maximum View DIstance +/*0520*/ uint8 unknown_end[84]; // ***Placeholder +/*0604*/ char zone_short_name2[68]; +/*0672*/ char unknown672[12]; +/*0684*/ uint16 zone_id; +/*0686*/ uint16 zone_instance; +/*0688*/ uint32 unknown688; +/*0692*/ uint8 unknown692[8]; +/*0700*/ float fog_density; +/*0704*/ uint32 SuspendBuffs; +/*0704*/ +}; + +/* +** Memorize Spell Struct +** Length: 12 Bytes +** +*/ +struct MemorizeSpell_Struct { +uint32 slot; // Spot in the spell book/memorized slot +uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) +uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming +uint32 unknown12; +}; + +/* +** Make Charmed Pet +** Length: 12 Bytes +** +*/ +struct Charm_Struct { +/*00*/ uint32 owner_id; +/*04*/ uint32 pet_id; +/*08*/ uint32 command; // 1: make pet, 0: release pet +/*12*/ +}; + +struct InterruptCast_Struct +{ + uint32 spawnid; + uint32 messageid; + char message[0]; +}; + +struct DeleteSpell_Struct +{ +/*000*/int16 spell_slot; +/*002*/uint8 unknowndss002[2]; +/*004*/uint8 success; +/*005*/uint8 unknowndss006[3]; +/*008*/ +}; + +struct ManaChange_Struct +{ + uint32 new_mana; // New Mana AMount + uint32 stamina; + uint32 spell_id; + uint32 unknown12; +}; + +struct SwapSpell_Struct +{ + uint32 from_slot; + uint32 to_slot; + + +}; + +struct BeginCast_Struct +{ + // len = 8 +/*000*/ uint16 caster_id; +/*002*/ uint16 spell_id; +/*004*/ uint32 cast_time; // in miliseconds +}; + +struct CastSpell_Struct +{ + uint32 slot; + uint32 spell_id; + uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast + uint32 target_id; + uint8 cs_unknown[4]; +}; + +struct SpellEffect_Struct +{ +/*000*/ uint32 EffectID; +/*004*/ uint32 EntityID; +/*008*/ uint32 EntityID2; // EntityID again +/*012*/ uint32 Duration; // In Milliseconds +/*016*/ uint32 FinishDelay; // In Milliseconds - delay for final part of spell effect +/*020*/ uint32 Unknown020; // Seen 3000 +/*024*/ uint8 Unknown024; // Seen 1 for SoD +/*025*/ uint8 Unknown025; // Seen 1 for Live +/*026*/ uint16 Unknown026; // Seen 1157 and 1177 - varies per char +/*028*/ +}; + + +/* +** SpawnAppearance_Struct +** Changes client appearance for all other clients in zone +** Size: 8 bytes +** Used in: OP_SpawnAppearance +** +*/ +struct SpawnAppearance_Struct +{ +/*0000*/ uint16 spawn_id; // ID of the spawn +/*0002*/ uint16 type; // Values associated with the type +/*0004*/ uint32 parameter; // Type of data sent +/*0008*/ +}; + + +// solar: this is used inside profile +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 SpellBuffFade_Struct { +/*000*/ uint32 entityid; +/*004*/ uint8 slot; +/*005*/ uint8 level; +/*006*/ uint8 effect; +/*007*/ uint8 unknown7; +/*008*/ uint32 spellid; +/*012*/ uint32 duration; +/*016*/ uint32 unknown016; +/*020*/ uint32 unknown020; //prolly global player ID +/*024*/ uint32 slotid; +/*028*/ uint32 bufffade; +/*032*/ +}; + +// Underfoot & later struct. +struct BuffRemoveRequest_Struct +{ +/*00*/ uint32 SlotID; +/*04*/ uint32 EntityID; +/*08*/ + }; + +struct PetBuff_Struct { +/*000*/ uint32 petid; +/*004*/ uint32 spellid[BUFF_COUNT]; +/*104*/ uint32 unknown700; +/*108*/ uint32 unknown701; +/*112*/ uint32 unknown702; +/*116*/ uint32 unknown703; +/*120*/ uint32 unknown704; +/*124*/ uint32 ticsremaining[BUFF_COUNT]; +/*224*/ uchar unknown705[20]; +/*244*/ uint32 buffcount; +}; + +struct BlockedBuffs_Struct +{ +/*00*/ int32 SpellID[BLOCKED_BUFF_COUNT]; +/*80*/ uint32 Count; +/*84*/ uint8 Pet; +/*85*/ uint8 Initialise; +/*86*/ uint16 Flags; +}; + +struct RemoveNimbusEffect_Struct +{ +/*00*/ uint32 spawnid; // Spawn ID +/*04*/ int32 nimbus_effect; // Nimbus Effect Number +/*08*/ +}; + +struct ItemNamePacket_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 unkown004; +/*008*/ char name[64]; +/*072*/ +}; + +// Length: 10 +struct ItemProperties_Struct { + +uint8 unknown01[2]; +uint8 charges; +uint8 unknown02[13]; +}; + +struct GMTrainee_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ uint32 skills[73]; + /*300*/ uint8 unknown300[148]; + /*448*/ +}; + +struct GMTrainEnd_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ +}; + +struct GMSkillChange_Struct { +/*000*/ uint16 npcid; +/*002*/ uint8 unknown1[2]; // something like PC_ID, but not really. stays the same thru the session though +/*002*/ uint16 skillbank; // 0 if normal skills, 1 if languages +/*002*/ uint8 unknown2[2]; +/*008*/ uint16 skill_id; +/*010*/ uint8 unknown3[2]; +}; + +struct GMTrainSkillConfirm_Struct { // SoF only +/*000*/ uint32 SkillID; +/*004*/ uint32 Cost; +/*008*/ uint8 NewSkill; // Set to 1 for 'You have learned the basics' message. +/*009*/ char TrainerName[64]; +/*073*/ +}; + +struct ConsentResponse_Struct { + char grantname[64]; + char ownername[64]; + uint8 permission; + char zonename[32]; +}; + +/* +** Name Generator Struct +** Length: 72 bytes +** OpCode: 0x0290 +*/ +struct NameGeneration_Struct +{ +/*0000*/ uint32 race; +/*0004*/ uint32 gender; +/*0008*/ char name[64]; +/*0072*/ +}; + +/* +** Character Creation struct +** Length: 140 Bytes +** OpCode: 0x0113 +*/ +struct CharCreate_Struct +{ +/*0000*/ uint32 class_; +/*0004*/ uint32 haircolor; // Might be hairstyle +/*0008*/ uint32 beardcolor; // Might be beard +/*0012*/ uint32 beard; // Might be beardcolor +/*0016*/ uint32 gender; +/*0020*/ uint32 race; +/*0024*/ uint32 start_zone; + // 0 = odus + // 1 = qeynos + // 2 = halas + // 3 = rivervale + // 4 = freeport + // 5 = neriak + // 6 = gukta/grobb + // 7 = ogguk + // 8 = kaladim + // 9 = gfay + // 10 = felwithe + // 11 = akanon + // 12 = cabalis + // 13 = shar vahl +/*0028*/ uint32 hairstyle; // Might be haircolor +/*0032*/ uint32 deity; +/*0036*/ uint32 STR; +/*0040*/ uint32 STA; +/*0044*/ uint32 AGI; +/*0048*/ uint32 DEX; +/*0052*/ uint32 WIS; +/*0056*/ uint32 INT; +/*0060*/ uint32 CHA; +/*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 drakkin_heritage; // added for SoF +/*0080*/ uint32 drakkin_tattoo; // added for SoF +/*0084*/ uint32 drakkin_details; // added for SoF +/*0088*/ +}; + +/* + *Used in PlayerProfile + */ +struct AA_Array +{ + uint32 AA; + uint32 value; +}; + + +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; + +static const uint32 MAX_PP_DISCIPLINES = 100; +static const uint32 MAX_DISCIPLINE_TIMERS = 20; + +struct Disciplines_Struct { + uint32 values[MAX_PP_DISCIPLINES]; +}; + +static const uint32 TRIBUTE_SLOT_START = 400; +static const uint32 MAX_PLAYER_TRIBUTES = 5; +static const uint32 MAX_PLAYER_BANDOLIER = 4; +static const uint32 MAX_PLAYER_BANDOLIER_ITEMS = 4; +static const uint32 MAX_POTIONS_IN_BELT = 4; +static const uint32 TRIBUTE_NONE = 0xFFFFFFFF; +struct Tribute_Struct { + uint32 tribute; + uint32 tier; +}; + +//len = 72 +struct BandolierItem_Struct { + uint32 item_id; + uint32 icon; + char item_name[64]; +}; + +//len = 320 +enum { //bandolier item positions + bandolierMainHand = 0, + bandolierOffHand, + bandolierRange, + bandolierAmmo +}; +struct Bandolier_Struct { + char name[32]; + BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; +}; +struct PotionBelt_Struct { + BandolierItem_Struct items[MAX_POTIONS_IN_BELT]; +}; + +struct MovePotionToBelt_Struct { + uint32 Action; + uint32 SlotNumber; + uint32 ItemID; +}; + +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]; +}; +struct RaidLeadershipAA_Struct { + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; +}; + + /** +* A bind point. +* Size: 20 Octets +*/ +struct BindStruct { + /*000*/ uint32 zoneId; + /*004*/ float x; + /*008*/ float y; + /*012*/ float z; + /*016*/ float heading; + /*020*/ +}; + +struct SuspendedMinion_Struct +{ + /*000*/ uint16 SpellID; + /*002*/ uint32 HP; + /*006*/ uint32 Mana; + /*010*/ SpellBuff_Struct Buffs[BUFF_COUNT]; + /*510*/ uint32 Items[MAX_MATERIALS]; + /*546*/ char Name[64]; + /*610*/ +}; + + +/* +** Player Profile +** +** 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 = 75; +static const uint32 MAX_PP_AA_ARRAY = 240; +static const uint32 MAX_GROUP_MEMBERS = 6; +static const uint32 MAX_RECAST_TYPES = 20; + +/* +showeq -> eqemu +sed -e 's/_t//g' -e 's/MAX_AA/MAX_PP_AA_ARRAY/g' \ + -e 's/MAX_SPELL_SLOTS/MAX_PP_MEMSPELL/g' \ + -e 's/MAX_KNOWN_SKILLS/MAX_PP_SKILL/g' \ + -e 's/MAXRIBUTES/MAX_PLAYER_TRIBUTES/g' \ + -e 's/MAX_BUFFS/BUFF_COUNT/g' \ + -e 's/MAX_KNOWN_LANGS/MAX_PP_LANGUAGE/g' \ + -e 's/MAX_RECASTYPES/MAX_RECAST_TYPES/g' \ + -e 's/spellBuff/SpellBuff_Struct/g' \ + -e 's/lastName/last_name/g' \ + -e 's/guildID/guildid/g' \ + -e 's/itemint/item_tint/g' \ + -e 's/MANA/mana/g' \ + -e 's/curHp/cur_hp/g' \ + -e 's/sSpellBook/spell_book/g' \ + -e 's/sMemSpells/mem_spells/g' \ + -e 's/uint32[ \t]*disciplines\[MAX_DISCIPLINES\]/Disciplines_Struct disciplines/g' \ + -e 's/aa_unspent/aapoints/g' \ + -e 's/aa_spent/aapoints_spent/g' \ + -e 's/InlineItem[ \t]*potionBelt\[MAX_POTIONS_IN_BELT\]/PotionBelt_Struct potionbelt/g' \ + -e 's/ldon_guk_points/ldon_points_guk/g' \ + -e 's/ldon_mir_points/ldon_points_mir/g' \ + -e 's/ldon_mmc_points/ldon_points_mmc/g' \ + -e 's/ldon_ruj_points/ldon_points_ruj/g' \ + -e 's/ldonak_points/ldon_points_tak/g' \ + -e 's/ldon_avail_points/ldon_points_available/g' \ + -e 's/tributeTime/tribute_time_remaining/g' \ + -e 's/careerTribute/career_tribute_points/g' \ + -e 's/currentTribute/tribute_points/g' \ + -e 's/tributeActive/tribute_active/g' \ + -e 's/TributeStruct/Tribute_Struct/g' \ + -e 's/expGroupLeadAA/group_leadership_exp/g' \ + -e 's/expRaidLeadAA/raid_leadership_exp/g' \ + -e 's/groupLeadAAUnspent/group_leadership_points/g' \ + -e 's/raidLeadAAUnspent/raid_leadership_points/g' \ + -e 's/uint32[ \t]*leadershipAAs\[MAX_LEAD_AA\]/LeadershipAA_Struct leader_abilities/g' \ + -e 's/BandolierStruct/Bandolier_Struct/g' \ + -e 's/MAX_BANDOLIERS/MAX_PLAYER_BANDOLIER/g' \ + -e 's/birthdayTime/birthday/g' \ + -e 's/lastSaveTime/lastlogin/g' \ + -e 's/zoneId/zone_id/g' \ + -e 's/hunger/hunger_level/g' \ + -e 's/thirst/thirst_level/g' \ + -e 's/guildstatus/guildrank/g' \ + -e 's/airRemaining/air_remaining/g' \ + */ + +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*/ 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_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[MAX_MATERIALS]; // Item texture/material of worn/held items +/*0348*/ uint8 unknown0348[44]; +/*0392*/ Color_Struct item_tint[MAX_MATERIALS]; +/*0428*/ 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_SPELLBOOK]; +/*4504*/ uint8 unknown4540[128]; // Was [428] all 0xff +/*4632*/ uint32 mem_spells[MAX_PP_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]; // @bp unknown skills? +/*4796*/ uint32 skills[MAX_PP_SKILL]; +/*5096*/ uint8 unknown5132[284]; // @bp unknown skills? +/*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*/ 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*/ Tribute_Struct tributes[MAX_PLAYER_TRIBUTES]; +/*7264*/ 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*/ 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*/ PVPStatsEntry_Struct PVPLastKill; +/*8304*/ PVPStatsEntry_Struct PVPLastDeath; +/*8392*/ uint32 PVPNumberOfKillsInLast24Hours; +/*8396*/ PVPStatsEntry_Struct PVPRecentKills[50]; +/*12796*/ uint32 aapoints_spent; +/*12800*/ uint32 expAA; +/*12804*/ uint32 aapoints; //avaliable, unspent +/*12808*/ uint8 unknown12844[36]; +/*12844*/ Bandolier_Struct bandoliers[MAX_PLAYER_BANDOLIER]; +/*14124*/ uint8 unknown14160[4506]; +/*18630*/ SuspendedMinion_Struct SuspendedMinion; // No longer in use +/*19240*/ uint32 timeentitledonaccount; +/*19244*/ 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*/ +}; + + + + +/* +** Client Target Struct +** Length: 2 Bytes +** OpCode: 6221 +*/ +struct ClientTarget_Struct { +/*000*/ uint32 new_target; // Target ID +}; + +/* +** Target Rejection Struct +** Length: 12 Bytes +** OpCode: OP_TargetReject +*/ +struct TargetReject_Struct { +/*00*/ uint8 unknown00[12]; +}; + +struct PetCommand_Struct { +/*000*/ uint32 command; +/*004*/ uint32 unknown; +}; + +/* +** Delete Spawn +** Length: 4 Bytes +** OpCode: OP_DeleteSpawn +*/ +struct DeleteSpawn_Struct +{ +/*00*/ uint32 spawn_id; // Spawn ID to delete +/*04*/ uint8 Decay; // 0 = vanish immediately, 1 = 'Decay' sparklies for corpses. +}; + +/* +** Channel Message received or sent +** Length: 144 Bytes + Variable Length + 1 +** OpCode: OP_ChannelMessage +** +*/ +struct ChannelMessage_Struct +{ +/*000*/ char targetname[64]; // Tell recipient +/*064*/ char sender[64]; // The senders name (len might be wrong) +/*128*/ uint32 language; // Language +/*132*/ uint32 chan_num; // Channel +/*136*/ uint32 cm_unknown4[2]; // ***Placeholder +/*144*/ uint32 skill_in_language; // The players skill in this language? might be wrong +/*148*/ char message[0]; // Variable length message +}; + +/* +** Special Message +** Length: 4 Bytes + Variable Text Length + 1 +** OpCode: OP_SpecialMesg +** +*/ +/* + Theres something wrong with this... example live packet: +Server->Client: [ Opcode: OP_SpecialMesg (0x0fab) Size: 244 ] + 0: 01 02 00 0A 00 00 00 09 - 05 00 00 42 61 72 73 74 | ...........Barst + 16: 72 65 20 53 6F 6E 67 77 - 65 61 76 65 72 00 7C F9 | re Songweaver.|. + 32: FF FF 84 FF FF FF 03 00 - 00 00 47 72 65 65 74 69 | ..........Greeti + +*/ +struct SpecialMesg_Struct +{ +/*00*/ char header[3]; // 04 04 00 <-- for #emote style msg +/*03*/ uint32 msg_type; // Color of text (see MT_*** below) +/*07*/ uint32 target_spawn_id; // Who is it being said to? +/*11*/ char sayer[1]; // Who is the source of the info +/*12*/ uint8 unknown12[12]; +/*24*/ char message[1]; // What is being said? +}; + +/* +** When somebody changes what they're wearing +** or give a pet a weapon (model changes) +** Length: 19 Bytes +*/ +struct WearChange_Struct{ +/*000*/ uint16 spawn_id; +/*002*/ uint32 material; +/*006*/ uint32 unknown06; +/*010*/ uint32 elite_material; // 1 for Drakkin Elite Material +/*014*/ uint32 hero_forge_model; // New to VoA +/*018*/ uint32 unknown18; // New to RoF +/*022*/ Color_Struct color; +/*026*/ uint8 wear_slot_id; +/*027*/ +}; + +/* +** Type: Bind Wound Structure +** Length: 8 Bytes +*/ +//Fixed for 7-14-04 patch +struct BindWound_Struct +{ +/*002*/ uint16 to; // TargetID +/*004*/ uint16 unknown2; // ***Placeholder +/*006*/ uint16 type; +/*008*/ uint16 unknown6; +}; + + +/* +** Type: Zone Change Request (before hand) +** Length: 88 bytes +** OpCode: a320 +*/ + +struct ZoneChange_Struct { +/*000*/ char char_name[64]; // Character Name +/*064*/ uint16 zoneID; +/*066*/ uint16 instanceID; +/*068*/ float y; +/*072*/ float x; +/*076*/ float z; +/*080*/ uint32 zone_reason; //0x0A == death, I think +/*084*/ int32 success; // =0 client->server, =1 server->client, -X=specific error +/*088*/ +}; + +// Whatever you send to the client in RequestClientZoneChange_Struct.type, the client will send back +// to the server in ZoneChange_Struct.zone_reason. My guess is this is a memo field of sorts. +// WildcardX 27 January 2008 + +struct RequestClientZoneChange_Struct { +/*00*/ uint16 zone_id; +/*02*/ uint16 instance_id; +/*04*/ float y; +/*08*/ float x; +/*12*/ float z; +/*16*/ float heading; +/*20*/ uint32 type; //unknown... values +}; + +struct Animation_Struct { +/*00*/ uint16 spawnid; +/*02*/ uint8 action; +/*03*/ uint8 value; +/*04*/ +}; + +// solar: this is what causes the caster to animate and the target to +// get the particle effects around them when a spell is cast +// also causes a buff icon +struct Action_Struct +{ + /* 00 */ uint16 target; // id of target + /* 02 */ uint16 source; // id of caster + /* 04 */ uint16 level; // level of caster + /* 06 */ uint16 instrument_mod; + /* 08 */ uint32 bard_focus_id; + /* 12 */ uint16 unknown16; +// some kind of sequence that's the same in both actions +// as well as the combat damage, to tie em together? + /* 14 */ uint32 sequence; + /* 18 */ uint32 unknown18; + /* 22 */ uint8 type; // 231 (0xE7) for spells + /* 23 */ uint32 unknown23; + /* 27 */ uint16 spell; // spell id being cast + /* 29 */ uint8 unknown29; +// this field seems to be some sort of success flag, if it's 4 + /* 30 */ uint8 buff_unknown; // if this is 4, a buff icon is made + /* 31 */ +}; + +// solar: this is what prints the You have been struck. and the regular +// melee messages like You try to pierce, etc. It's basically the melee +// and spell damage message +struct CombatDamage_Struct +{ +/* 00 */ uint16 target; +/* 02 */ uint16 source; +/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells +/* 05 */ uint16 spellid; +/* 07 */ uint32 damage; +/* 11 */ uint32 unknown11; +/* 15 */ uint32 sequence; // see above notes in Action_Struct +/* 19 */ uint32 unknown19; +/* 23 */ +}; + +/* +** Consider Struct +*/ +struct Consider_Struct{ +/*000*/ uint32 playerid; // PlayerID +/*004*/ uint32 targetid; // TargetID +/*008*/ uint32 faction; // Faction +/*0012*/ uint32 level; // Level +/*016*/ int32 cur_hp; // Current Hitpoints +/*020*/ int32 max_hp; // Maximum Hitpoints +/*024*/ uint8 pvpcon; // Pvp con flag 0/1 +/*025*/ uint8 unknown3[3]; +}; + +/* +** Spawn Death Blow +** Length: 32 Bytes +** OpCode: 0114 +*/ +struct Death_Struct +{ +/*000*/ uint32 spawn_id; +/*004*/ uint32 killer_id; +/*008*/ uint32 corpseid; // was corpseid +/*012*/ uint32 bindzoneid; +/*016*/ uint32 spell_id; +/*020*/ uint32 attack_skill; +/*024*/ uint32 damage; +/*028*/ uint32 unknown028; +}; + +struct BecomeCorpse_Struct { + uint32 spawn_id; + float y; + float x; + float z; +}; + +/* +** Spawn position update +** Struct sent from server->client to update position of +** another spawn's position update in zone (whether NPC or PC) +** +*/ +struct PlayerPositionUpdateServer_Struct +{ +/*0000*/ uint16 spawn_id; +/*0002*/ int32 delta_heading:10, // change in heading + x_pos:19, // x coord + padding0002:3; // ***Placeholder +/*0006*/ int32 y_pos:19, // y coord + animation:10, // animation + padding0006:3; // ***Placeholder +/*0010*/ int32 z_pos:19, // z coord + delta_y:13; // change in y +/*0014*/ int32 delta_x:13, // change in x + heading:12, // heading + padding0014:7; // ***Placeholder +/*0018*/ int32 delta_z:13, // change in z + padding0018:19; // ***Placeholder +/*0022*/ +}; + +/* +** Player position update +** Struct sent from client->server to update +** player position on server +** +*/ +struct PlayerPositionUpdateClient_Struct +{ +/*0000*/ uint16 spawn_id; +/*0022*/ uint16 sequence; //increments one each packet +/*0004*/ float y_pos; // y coord +/*0008*/ float delta_z; // Change in z +/*0016*/ float delta_x; // Change in x +/*0012*/ float delta_y; // Change in y +/*0020*/ int32 animation:10, // animation + delta_heading:10, // change in heading + padding0020:12; // ***Placeholder (mostly 1) +/*0024*/ float x_pos; // x coord +/*0028*/ float z_pos; // z coord +/*0034*/ uint16 heading:12, // Directional heading + padding0004:4; // ***Placeholder +/*0032*/ uint8 unknown0006[2]; // ***Placeholder +/*0036*/ +}; + +struct SpawnPositionUpdate_Struct +{ +/*0000*/ uint16 spawn_id; +/*0002*/ uint64 y_pos:19, z_pos:19, x_pos:19, padding002:7; +/*0010*/ unsigned heading:12; + signed padding010:4; +/*0012*/ +}; + +/* +** Spawn HP Update +** Length: 10 Bytes +** OpCode: OP_HPUpdate +*/ +struct SpawnHPUpdate_Struct +{ +/*00*/ uint32 cur_hp; // Id of spawn to update +/*04*/ int32 max_hp; // Maximum hp of spawn +/*08*/ int16 spawn_id; // Current hp of spawn +/*10*/ +}; + +struct ManaUpdate_Struct +{ +/*00*/ uint32 cur_mana; +/*04*/ uint32 max_mana; +/*08*/ uint16 spawn_id; +/*10*/ +}; + +struct EnduranceUpdate_Struct +{ +/*00*/ uint32 cur_end; +/*04*/ uint32 max_end; +/*08*/ uint16 spawn_id; +/*10*/ +}; + +struct SpawnHPUpdate_Struct2 +{ +/*00*/ int16 spawn_id; +/*02*/ uint8 hp; //HP Percentage +/*03*/ +}; + +struct MobManaUpdate_Struct +{ +/*00*/ uint16 spawn_id; +/*02*/ uint8 mana; //Mana Percentage +/*03*/ +}; + +struct MobEnduranceUpdate_Struct +{ +/*00*/ uint16 spawn_id; +/*02*/ uint8 endurance; //Endurance Percentage +/*03*/ +}; + +// Is this even used? +struct MobHealth +{ + /*0000*/ uint8 hp; //health percent + /*0001*/ uint16 id; //mobs id +}; + +/* +** Stamina +** Length: 8 Bytes +** OpCode: 5721 +*/ +struct Stamina_Struct { +/*00*/ uint32 food; // (low more hungry 127-0) +/*02*/ uint32 water; // (low more thirsty 127-0) +}; + +/* +** Level Update +** Length: 12 Bytes +*/ +struct LevelUpdate_Struct +{ +/*00*/ uint32 level; // New level +/*04*/ uint32 level_old; // Old level +/*08*/ uint32 exp; // Current Experience +}; + +/* +** Experience Update +** Length: 14 Bytes +** OpCode: 9921 +*/ +struct ExpUpdate_Struct +{ +/*0000*/ uint32 exp; // Current experience ratio from 0 to 330 +/*0004*/ uint32 aaxp; // @BP ?? +}; + +/* +** Item Packet Struct - Works on a variety of opcodes +** Packet Types: See ItemPacketType enum +** +*/ +enum ItemPacketType +{ + ItemPacketViewLink = 0x00, + ItemPacketTradeView = 0x65, + ItemPacketLoot = 0x66, + ItemPacketTrade = 0x67, + ItemPacketCharInventory = 0x69, + ItemPacketSummonItem = 0x6A, + ItemPacketTributeItem = 0x6C, + ItemPacketMerchant = 0x64, + ItemPacketWorldContainer = 0x6B, + ItemPacketCharmUpdate = 0x6E +}; +struct ItemPacket_Struct +{ +/*00*/ ItemPacketType PacketType; +/*04*/ char SerializedItem[1]; +/*xx*/ +}; + +struct BulkItemPacket_Struct +{ +/*00*/ char SerializedItem[0]; +/*xx*/ +}; + +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]; +}; + +struct DeleteItem_Struct { +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +}; + +struct MoveItem_Struct +{ +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +}; + +// +// from_slot/to_slot +// -1 - destroy +// 0 - cursor +// 1 - inventory +// 2 - bank +// 3 - trade +// 4 - shared bank +// +// cointype +// 0 - copeer +// 1 - silver +// 2 - gold +// 3 - platinum +// +static const uint32 COINTYPE_PP = 3; +static const uint32 COINTYPE_GP = 2; +static const uint32 COINTYPE_SP = 1; +static const uint32 COINTYPE_CP = 0; + +struct MoveCoin_Struct +{ + int32 from_slot; + int32 to_slot; + uint32 cointype1; + uint32 cointype2; + int32 amount; +}; +struct TradeCoin_Struct{ + uint32 trader; + uint8 slot; + uint16 unknown5; + uint8 unknown7; + uint32 amount; +}; +struct TradeMoneyUpdate_Struct{ + uint32 trader; + uint32 type; + uint32 amount; +}; +/* +** Surname struct +** Size: 100 bytes +*/ +struct Surname_Struct +{ +/*0000*/ char name[64]; +/*0064*/ uint32 unknown0064; +/*0068*/ char lastname[32]; +/*0100*/ +}; + +struct GuildsListEntry_Struct { + char name[64]; +}; + +static const uint32 MAX_NUMBER_GUILDS = 1500; +struct GuildsList_Struct { + uint8 head[64]; // First on guild list seems to be empty... + GuildsListEntry_Struct Guilds[MAX_NUMBER_GUILDS]; +}; + +struct GuildUpdate_Struct { + uint32 guildID; + GuildsListEntry_Struct entry; +}; + +/* +** Money Loot +** Length: 22 Bytes +** OpCode: 5020 +*/ +struct moneyOnCorpseStruct { +/*0000*/ uint8 response; // 0 = someone else is, 1 = OK, 2 = not at this time +/*0001*/ uint8 unknown1; // = 0x5a +/*0002*/ uint8 unknown2; // = 0x40 +/*0003*/ uint8 unknown3; // = 0 +/*0004*/ uint32 platinum; // Platinum Pieces +/*0008*/ uint32 gold; // Gold Pieces + +/*0012*/ uint32 silver; // Silver Pieces +/*0016*/ uint32 copper; // Copper Pieces +}; + +//opcode = 0x5220 +// size 292 + + +struct LootingItem_Struct { +/*000*/ uint32 lootee; +/*002*/ uint32 looter; +/*004*/ uint16 slot_id; +/*006*/ uint8 unknown3[2]; +/*008*/ uint32 auto_loot; +}; + +struct GuildManageStatus_Struct{ + uint32 guild_id; + uint32 oldrank; + uint32 newrank; + char name[64]; +}; +// Guild invite, remove +struct GuildJoin_Struct{ +/*000*/ uint32 guild_id; +/*004*/ uint32 unknown04; +/*008*/ uint32 level; +/*012*/ uint32 class_; +/*016*/ uint32 rank;//0 member, 1 officer, 2 leader +/*020*/ uint32 zoneid; +/*024*/ uint32 unknown24; +/*028*/ char name[64]; +/*092*/ +}; +struct GuildInviteAccept_Struct { + char inviter[64]; + char newmember[64]; + uint32 response; + uint32 guildeqid; +}; +struct GuildManageRemove_Struct { + uint32 guildeqid; + char member[64]; +}; +struct GuildCommand_Struct { + char othername[64]; + char myname[64]; + uint16 guildeqid; + uint8 unknown[2]; // for guildinvite all 0's, for remove 0=0x56, 2=0x02 + uint32 officer; +}; + +struct OnLevelMessage_Struct +{ + char Title[128]; + char Text[4096]; + uint32 Buttons; + uint32 Duration; + uint32 PopupID; + uint32 NegativeID; + char ButtonName0[25]; + char ButtonName1[25]; +}; + +struct PopupResponse_Struct { +/*0000*/ uint32 unknown0000; +/*0004*/ uint32 popupid; +}; + +struct GuildManageBanker_Struct { + uint32 unknown0; + char myname[64]; + char member[64]; + uint32 enabled; +}; + +// Server -> Client +// Update a guild members rank and banker status +struct GuildSetRank_Struct +{ +/*00*/ uint32 Unknown00; +/*04*/ uint32 Unknown04; +/*08*/ uint32 Rank; +/*12*/ char MemberName[64]; +/*76*/ uint32 Banker; +/*80*/ +}; + +// Opcode OP_GMZoneRequest +// Size = 88 bytes +struct GMZoneRequest_Struct { +/*0000*/ char charname[64]; +/*0064*/ uint32 zone_id; +/*0068*/ float x; +/*0072*/ float y; +/*0076*/ float z; +/*0080*/ char unknown0080[4]; +/*0084*/ uint32 success; // 0 if command failed, 1 if succeeded? +/*0088*/ +// /*072*/ int8 success; // =0 client->server, =1 server->client, -X=specific error +// /*073*/ uint8 unknown0073[3]; // =0 ok, =ffffff error +}; + +struct GMSummon_Struct { +/* 0*/ char charname[64]; +/* 30*/ char gmname[64]; +/* 60*/ uint32 success; +/* 61*/ uint32 zoneID; +/*92*/ float y; +/*96*/ float x; +/*100*/ float z; +/*104*/ uint32 unknown2; // E0 E0 56 00 +}; + +struct GMGoto_Struct { // x,y is swapped as compared to summon and makes sense as own packet +/* 0*/ char charname[64]; + +/* 64*/ char gmname[64]; +/* 128*/ uint32 success; +/* 132*/ uint32 zoneID; + +/*136*/ int32 y; +/*140*/ int32 x; +/*144*/ int32 z; +/*148*/ uint32 unknown2; // E0 E0 56 00 +}; + +struct GMLastName_Struct { + char name[64]; + char gmname[64]; + char lastname[64]; + uint16 unknown[4]; // 0x00, 0x00 + // 0x01, 0x00 = Update the clients +}; + +//Combat Abilities +struct CombatAbility_Struct { + uint32 m_target; //the ID of the target mob + uint32 m_atk; + uint32 m_skill; +}; + +//Instill Doubt +struct Instill_Doubt_Struct { + uint8 i_id; + uint8 ia_unknown; + uint8 ib_unknown; + uint8 ic_unknown; + uint8 i_atk; + + uint8 id_unknown; + uint8 ie_unknown; + uint8 if_unknown; + uint8 i_type; + uint8 ig_unknown; + uint8 ih_unknown; + uint8 ii_unknown; +}; + +struct GiveItem_Struct { + uint16 to_entity; + int16 to_equipSlot; + uint16 from_entity; + int16 from_equipSlot; +}; + +struct RandomReq_Struct { + uint32 low; + uint32 high; +}; + +/* solar: 9/23/03 reply to /random command; struct from Zaphod */ +struct RandomReply_Struct { +/* 00 */ uint32 low; +/* 04 */ uint32 high; +/* 08 */ uint32 result; +/* 12 */ char name[64]; +/* 76 */ +}; + +struct LFG_Struct { +/* +Wrong size on OP_LFG. Got: 80, Expected: 68 + 0: 00 00 00 00 01 00 00 00 - 00 00 00 00 64 00 00 00 | ............d... + 16: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 48: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 64: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ +Wrong size on OP_LFG. Got: 80, Expected: 68 + 0: 00 00 00 00 01 00 00 00 - 3F 00 00 00 41 00 00 00 | ........?...A... + 16: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 48: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 64: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ +Wrong size on OP_LFG. Got: 80, Expected: 68 + 0: 00 00 00 00 01 00 00 00 - 3F 00 00 00 41 00 00 00 | ........?...A... + 16: 46 72 75 62 20 66 72 75 - 62 20 66 72 75 62 00 00 | Frub frub frub.. + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 48: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 64: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ +*/ +/*000*/ uint32 unknown000; +/*004*/ uint8 value; // 0x00 = off 0x01 = on +/*005*/ uint8 MatchFilter; +/*006*/ uint16 Unknown006; +/*008*/ uint32 FromLevel; +/*012*/ uint32 ToLevel; +/*016*/ char Comments[64]; +}; + +struct LFGGetMatchesRequest_Struct { +/*000*/ uint32 Unknown000; +/*004*/ uint32 FromLevel; +/*008*/ uint32 ToLevel; +/*012*/ uint32 Classes; +}; + +enum { LFPOff = 0, + LFPOn = 1, + LFPMemberUpdate = 255 // Internal use, not sent by client +}; + +struct LFP_Struct { +/*000*/ uint32 Unknown000; +/*004*/ uint8 Action; +/*005*/ uint8 MatchFilter; +/*006*/ uint16 Unknown006; +/*008*/ uint32 FromLevel; +/*012*/ uint32 ToLevel; +/*016*/ uint32 Classes; +/*020*/ char Comments[64]; +}; + +struct LFPGetMatchesRequest_Struct { +/*000*/ uint32 Unknown000; +/*004*/ uint32 FromLevel; +/*008*/ uint32 ToLevel; +}; + +/* +** LFG_Appearance_Struct +** Packet sent to clients to notify when someone in zone toggles LFG flag +** Size: 8 bytes +** Used in: OP_LFGAppearance +** +*/ +struct LFG_Appearance_Struct +{ +/*0000*/ uint32 spawn_id; // ID of the client +/*0004*/ uint8 lfg; // 1=LFG, 0=Not LFG +/*0005*/ char unknown0005[3]; // +/*0008*/ +}; + + +// EverQuest Time Information: +// 72 minutes per EQ Day +// 3 minutes per EQ Hour +// 6 seconds per EQ Tick (2 minutes EQ Time) +// 3 seconds per EQ Minute + +struct TimeOfDay_Struct { + uint8 hour; + uint8 minute; + uint8 day; + uint8 month; + uint32 year; +}; + +// Darvik: shopkeeper structs +struct Merchant_Click_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; +/*008*/ uint32 command; //1=open, 0=cancel/close +/*012*/ float rate; //cost multiplier, dosent work anymore +}; +/* +Unknowns: +0 is e7 from 01 to // MAYBE SLOT IN PURCHASE +1 is 03 +2 is 00 +3 is 00 +4 is ?? +5 is ?? +6 is 00 from a0 to +7 is 00 from 3f to */ +/* +0 is F6 to 01 +1 is CE CE +4A 4A +00 00 +00 E0 +00 CB +00 90 +00 3F +*/ + + + +struct Merchant_Sell_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; // Player's entity id +/*008*/ uint32 itemslot; + uint32 unknown12; +/*016*/ uint8 quantity; // Already sold +/*017*/ uint8 Unknown016[3]; +/*020*/ uint32 price; +}; +struct Merchant_Purchase_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 itemslot; // Player's entity id +/*008*/ uint32 quantity; +/*012*/ uint32 price; +}; +struct Merchant_DelItem_Struct{ +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; // Player's entity id +/*008*/ uint32 itemslot; +/*012*/ uint32 unknown012; +}; +struct Adventure_Purchase_Struct { +/*000*/ uint32 Type; // 1 = LDoN, 2 = Discord, 4 = Norrath's Keepers, 5 = Dark Reign +/*000*/ uint32 npcid; +/*004*/ uint32 itemid; +/*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 unknown004[36]; +/*040*/ uint8 unknown040[16]; +/*056*/ uint32 ldon_guk_points; // Earned Deepest Guk points +/*060*/ uint8 unknown060[16]; +/*076*/ uint32 ldon_mirugal_points; // Earned Mirugal' Menagerie points +/*080*/ uint8 unknown080[16]; +/*096*/ uint32 ldon_mistmoore_points; // Earned Mismoore Catacombs Points +/*100*/ uint8 unknown100[16]; +/*116*/ uint32 ldon_rujarkian_points; // Earned Rujarkian Hills points +/*120*/ uint8 unknown120[16]; +/*136*/ uint32 ldon_takish_points; // Earned Takish points +/*140*/ +}; + +struct AdventureFinish_Struct{ +/*000*/ uint32 win_lose;//Cofruben: 00 is a lose,01 is win. +/*004*/ uint32 points; +/*008*/ +}; + +struct Weblink_Struct{ +/*000*/ char weblink[1]; +/*004*/ +}; + +//OP_AdventureRequest +struct AdventureRequest_Struct{ +/*000*/ uint32 risk;//1 normal,2 hard. +/*004*/ uint32 entity_id; +/*008*/ uint32 type; +/*012*/ +}; +struct AdventureRequestResponse_Struct{ +/*0000*/ uint32 unknown000; +/*0004*/ char text[2048]; +/*2052*/ uint32 timetoenter; +/*2056*/ uint32 timeleft; +/*2060*/ uint32 risk; +/*2064*/ float x; +/*2068*/ float y; +/*2072*/ float z; +/*2076*/ uint32 showcompass; +/*2080*/ uint32 unknown2080; +/*2084*/ +}; + +struct AdventureCountUpdate_Struct +{ +/*000*/ uint32 current; +/*004*/ uint32 total; +/*008*/ +}; + +struct AdventureStatsColumn_Struct +{ +/*000*/ uint32 total; +/*004*/ uint32 guk; +/*008*/ uint32 mir; +/*012*/ uint32 mmc; +/*016*/ uint32 ruj; +/*020*/ uint32 tak; +/*024*/ +}; + +struct AdventureStats_Struct +{ +/*000*/ AdventureStatsColumn_Struct success; +/*024*/ AdventureStatsColumn_Struct failure; +/*048*/ AdventureStatsColumn_Struct rank; +/*072*/ AdventureStatsColumn_Struct rank2; +/*096*/ +}; + +//this is mostly right but something is off that causes the client to crash sometimes +//I don't really care enough about the feature to work on it anymore though. +struct AdventureLeaderboardEntry_Struct +{ +/*000*/ char name[64]; +/*064*/ uint32 success; +/*068*/ uint32 failure; +/*072*/ +}; + +struct AdventureLeaderboardRequest_Struct +{ +/*000*/ uint32 type; +/*004*/ uint32 theme; +/*008*/ uint32 risk; +/*012*/ +}; + +struct AdventureLeaderboard_Struct +{ +/*000*/ uint32 unknown000; +/*004*/ uint32 unknown004; +/*008*/ uint32 success; +/*012*/ uint32 failure; +/*016*/ uint32 our_rank; +/*020*/ AdventureLeaderboardEntry_Struct entries[100]; +}; + +/*struct Item_Shop_Struct { + uint16 merchantid; + uint8 itemtype; + Item_Struct item; + uint8 iss_unknown001[6]; +};*/ + +struct Illusion_Struct { //size: 256 - SoF +/*000*/ uint32 spawnid; +/*004*/ char charname[64]; // +/*068*/ uint16 race; // +/*070*/ char unknown006[2]; +/*072*/ uint8 gender; +/*073*/ uint8 texture; +/*074*/ uint8 unknown008; // +/*075*/ uint8 unknown009; // +/*076*/ uint8 helmtexture; // +/*077*/ uint8 unknown010; // +/*078*/ uint8 unknown011; // +/*079*/ uint8 unknown012; // +/*080*/ uint32 face; // +/*084*/ uint8 hairstyle; // +/*085*/ uint8 haircolor; // +/*086*/ uint8 beard; // +/*087*/ uint8 beardcolor; // +/*088*/ float size; // +/*092*/ uint32 drakkin_heritage; // +/*096*/ uint32 drakkin_tattoo; // +/*100*/ uint32 drakkin_details; // +/*104*/ uint32 armor_tint[MAX_MATERIALS]; // +/*140*/ uint8 eyecolor1; // Field Not Identified in any Illusion Struct +/*141*/ uint8 eyecolor2; // Field Not Identified in any Illusion Struct +/*142*/ uint8 unknown138[114]; // +/*256*/ +}; + +struct Illusion_Struct_Old { +/*000*/ uint32 spawnid; + char charname[64]; +/**/ uint16 race; +/**/ char unknown006[2]; +/**/ uint8 gender; +/**/ uint8 texture; +/**/ uint8 helmtexture; +/**/ uint8 unknown011; +/**/ uint32 face; +/**/ char unknown020[88]; +/**/ +}; + +// OP_Sound - Size: 68 +struct QuestReward_Struct +{ +/*000*/ uint32 from_mob; // ID of mob awarding the client +/*004*/ uint32 unknown004; +/*008*/ uint32 unknown008; +/*012*/ uint32 unknown012; +/*016*/ uint32 unknown016; +/*020*/ uint32 unknown020; +/*024*/ uint32 silver; // Gives silver to the client +/*028*/ uint32 gold; // Gives gold to the client +/*032*/ uint32 platinum; // Gives platinum to the client +/*036*/ uint32 unknown036; +/*040*/ uint32 unknown040; +/*044*/ uint32 unknown044; +/*048*/ uint32 unknown048; +/*052*/ uint32 unknown052; +/*056*/ uint32 unknown056; +/*060*/ uint32 unknown060; +/*064*/ uint32 unknown064; +/*068*/ +}; + +// Size: 8 +struct Camera_Struct +{ + uint32 duration; // Duration in ms + uint32 intensity; // Between 1023410176 and 1090519040 +}; + +struct ZonePoint_Entry { +/*0000*/ uint32 iterator; +/*0004*/ float y; +/*0008*/ float x; +/*0012*/ float z; +/*0016*/ float heading; +/*0020*/ uint16 zoneid; +/*0022*/ uint16 zoneinstance; // LDoN instance +}; + +struct ZonePoints { +/*0000*/ uint32 count; +/*0004*/ struct ZonePoint_Entry zpe[0]; // Always add one extra to the end after all zonepoints +}; + +struct SkillUpdate_Struct { +/*00*/ uint32 skillId; +/*04*/ uint32 value; +/*08*/ +}; + +struct ZoneUnavail_Struct { + //This actually varies, but... + char zonename[16]; + int16 unknown[4]; +}; + +enum { //Group action fields + groupActJoin = 0, + groupActLeave = 1, + groupActDisband = 6, + groupActUpdate = 7, + groupActMakeLeader = 8, + groupActInviteInitial = 9, + groupActAAUpdate = 10 +}; + +struct GroupGeneric_Struct { + char name1[64]; + char name2[64]; +}; + +struct GroupCancel_Struct { + char name1[64]; + char name2[64]; + uint8 toggle; +}; + +struct GroupUpdate_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +}; + +struct GroupUpdate2_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +/*0452*/ GroupLeadershipAA_Struct leader_aas; +/*0580*/ uint8 unknown580[196]; +/*0766*/ uint32 NPCMarkerID; // EntityID of player delegated MarkNPC ability +/*0780*/ uint8 unknown780[56]; +/*0836*/ +}; +struct GroupJoin_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[64]; +/*0132*/ GroupLeadershipAA_Struct leader_aas; +/*0196*/ uint8 unknown196[196]; +/*0392*/ uint32 NPCMarkerID; // EntityID of player delegated MarkNPC ability +/*0396*/ uint8 unknown396[56]; +/*0452*/ +}; + +// SoD+ Struct +struct GroupLeadershipAAUpdate_Struct +{ +/*000*/ uint32 Unknown000; // GroupID or Leader EntityID ? +/*004*/ GroupLeadershipAA_Struct LeaderAAs; +/*068*/ uint32 Unknown068[49]; // Was 63 +/*264*/ uint32 NPCMarkerID; +/*268*/ uint32 Unknown268[13]; +/*320*/ +}; + +struct GroupFollow_Struct { // SoF Follow Struct +/*0000*/ char name1[64]; // inviter +/*0064*/ char name2[64]; // invitee +/*0128*/ uint32 unknown0128; +/*0132*/ +}; + +struct GroupLeaderChange_Struct +{ +/*000*/ char Unknown000[64]; +/*064*/ char LeaderName[64]; +/*128*/ char Unknown128[20]; +}; + +struct FaceChange_Struct { +/*000*/ uint8 haircolor; +/*001*/ uint8 beardcolor; +/*002*/ uint8 eyecolor1; // the eyecolors always seem to be the same, maybe left and right eye? +/*003*/ uint8 eyecolor2; +/*004*/ uint8 hairstyle; +/*005*/ uint8 beard; +/*006*/ uint8 face; +/*007*/ uint32 drakkin_heritage; +/*011*/ uint32 drakkin_tattoo; +/*015*/ uint32 drakkin_details; +//there are only 10 faces for barbs changing woad just +//increase the face value by ten so if there were 8 woad +//designs then there would be 80 barb faces +}; + +/* +** Trade request from one client to another +** Used to initiate a trade +** Size: 8 bytes +** Used in: OP_TradeRequest +*/ +struct TradeRequest_Struct { +/*00*/ uint32 to_mob_id; +/*04*/ uint32 from_mob_id; +/*08*/ +}; + +struct TradeAccept_Struct { +/*00*/ uint32 from_mob_id; +/*04*/ uint32 unknown4; //seems to be garbage +/*08*/ +}; + +/* +** Cancel Trade struct +** Sent when a player cancels a trade +** Size: 8 bytes +** Used In: OP_CancelTrade +** +*/ +struct CancelTrade_Struct { +/*00*/ uint32 fromid; +/*04*/ uint32 action; +/*08*/ +}; + +struct TradeBusy_Struct { +/*00*/ uint32 to_mob_id; +/*04*/ uint32 from_mob_id; +/*08*/ uint8 type; // Seen 01 +/*09*/ uint8 unknown09; // Seen EF (239) +/*10*/ uint8 unknown10; // Seen FF (255) +/*11*/ uint8 unknown11; // Seen FF (255) +/*12*/ +}; + +struct PetitionUpdate_Struct { + uint32 petnumber; // Petition Number + uint32 color; // 0x00 = green, 0x01 = yellow, 0x02 = red + uint32 status; + time_t senttime; // 4 has to be 0x1F + char accountid[32]; + char gmsenttoo[64]; + int32 quetotal; + char charname[64]; +}; + +struct Petition_Struct { + uint32 petnumber; + uint32 urgency; + char accountid[32]; + char lastgm[32]; + uint32 zone; + //char zone[32]; + char charname[64]; + uint32 charlevel; + uint32 charclass; + uint32 charrace; + uint32 unknown; + //time_t senttime; // Time? + uint32 checkouts; + uint32 unavail; + //uint8 unknown5[4]; + time_t senttime; + uint32 unknown2; + char petitiontext[1024]; + char gmtext[1024]; +}; + + +struct Who_All_Struct { // 76 length total +/*000*/ char whom[64]; +/*064*/ uint32 wrace; // FF FF = no race + +/*068*/ uint32 wclass; // FF FF = no class +/*072*/ uint32 lvllow; // FF FF = no numbers +/*076*/ uint32 lvlhigh; // FF FF = no numbers +/*080*/ uint32 gmlookup; // FF FF = not doing /who all gm +/*084*/ uint32 guildid; +/*088*/ uint8 unknown076[64]; +/*152*/ uint32 type; // New for SoF. 0 = /who 3 = /who all +/*156*/ +}; + +struct Stun_Struct { // 4 bytes total + uint32 duration; // Duration of stun +}; + +struct AugmentItem_Struct { +/*00*/ int16 container_slot; +/*02*/ char unknown02[2]; +/*04*/ int32 augment_slot; +/*08*/ +}; + +// OP_Emote +struct Emote_Struct { +/*0000*/ uint32 unknown01; +/*0004*/ char message[1024]; +/*1028*/ +}; + +// Inspect +struct Inspect_Struct { + uint16 TargetID; + uint16 PlayerID; +}; + +//OP_InspectAnswer - Size: 1860 +struct InspectResponse_Struct { +/*000*/ uint32 TargetID; +/*004*/ uint32 playerid; +/*008*/ char itemnames[23][64]; +/*1480*/uint32 itemicons[23]; +/*1572*/char text[288]; // Max number of chars in Inspect Window appears to be 254 // Msg struct property is 256 (254 + '\0' is my guess) -U +/*1860*/ +}; + +//OP_InspectMessageUpdate - Size: 256 (SoF+ clients after self-inspect window is closed) -U +struct InspectMessage_Struct { +/*000*/ char text[256]; +/*256*/ +}; + +//OP_SetDataRate +struct SetDataRate_Struct { + float newdatarate; +}; + +//OP_SetServerFilter +struct SetServerFilter_Struct { + uint32 filters[29]; //see enum eqFilterType +}; + +//Op_SetServerFilterAck +struct SetServerFilterAck_Struct { + uint8 blank[8]; +}; +struct IncreaseStat_Struct{ + /*0000*/ uint8 unknown0; + /*0001*/ uint8 str; + /*0002*/ uint8 sta; + /*0003*/ uint8 agi; + /*0004*/ uint8 dex; + /*0005*/ uint8 int_; + /*0006*/ uint8 wis; + /*0007*/ uint8 cha; + /*0008*/ uint8 fire; + /*0009*/ uint8 cold; + /*0010*/ uint8 magic; + /*0011*/ uint8 poison; + /*0012*/ uint8 disease; + /*0013*/ char unknown13[116]; + /*0129*/ uint8 str2; + /*0130*/ uint8 sta2; + /*0131*/ uint8 agi2; + /*0132*/ uint8 dex2; + /*0133*/ uint8 int_2; + /*0134*/ uint8 wis2; + /*0135*/ uint8 cha2; + /*0136*/ uint8 fire2; + /*0137*/ uint8 cold2; + /*0138*/ uint8 magic2; + /*0139*/ uint8 poison2; + /*0140*/ uint8 disease2; +}; + +struct GMName_Struct { + char oldname[64]; + char gmname[64]; + char newname[64]; + uint8 badname; + uint8 unknown[3]; +}; + +struct GMDelCorpse_Struct { + char corpsename[64]; + char gmname[64]; + uint8 unknown; +}; + +struct GMKick_Struct { + char name[64]; + char gmname[64]; + uint8 unknown; +}; + + +struct GMKill_Struct { + char name[64]; + char gmname[64]; + uint8 unknown; +}; + + +struct GMEmoteZone_Struct { + char text[512]; +}; + +// This is where the Text is sent to the client. +// Use ` as a newline character in the text. +// Variable length. +struct BookText_Struct { + uint8 window; // where to display the text (0xFF means new window) + uint8 type; //type: 0=scroll, 1=book, 2=item info.. prolly others. + uint32 invslot; // Only used in SoF and later clients. + char booktext[1]; // Variable Length +}; +// This is the request to read a book. +// This is just a "text file" on the server +// or in our case, the 'name' column in our books table. +struct BookRequest_Struct { + uint8 window; // where to display the text (0xFF means new window) + uint8 type; //type: 0=scroll, 1=book, 2=item info.. prolly others. + uint32 invslot; // Only used in Sof and later clients; + char txtfile[20]; +}; + +/* +** Object/Ground Spawn struct +** Used for Forges, Ovens, ground spawns, items dropped to ground, etc +** Size: 92 bytes +** OpCodes: OP_CreateObject +** Last Updated: Oct-17-2003 +** +*/ +struct Object_Struct { +/*00*/ uint32 linked_list_addr[2];// They are, get this, prev and next, ala linked list +/*08*/ uint16 unknown008[2]; // +/*12*/ uint32 drop_id; // Unique object id for zone +/*16*/ uint16 zone_id; // Redudant, but: Zone the object appears in +/*18*/ uint16 zone_instance; // +/*20*/ uint32 unknown020; // +/*24*/ uint32 unknown024; // +/*28*/ float heading; // heading +/*32*/ float z; // z coord +/*36*/ float x; // x coord +/*40*/ float y; // y coord +/*44*/ char object_name[32]; // Name of object, usually something like IT63_ACTORDEF +/*76*/ uint32 unknown076; // +/*80*/ uint32 object_type; // Type of object, not directly translated to OP_OpenObject +/*84*/ uint32 unknown084; //set to 0xFF +/*88*/ uint32 spawn_id; // Spawn Id of client interacting with object +/*92*/ +}; +// 01 = generic drop, 02 = armor, 19 = weapon +//[13:40] and 0xff seems to be indicative of the tradeskill/openable items that end up returning the old style item type in the OP_OpenObject + +/* +** Click Object Struct +** Client clicking on zone object (forge, groundspawn, etc) +** Size: 8 bytes +** Last Updated: Oct-17-2003 +** +*/ +struct ClickObject_Struct { +/*00*/ uint32 drop_id; +/*04*/ uint32 player_id; +/*08*/ +}; + +struct Shielding_Struct { + uint32 target_id; +}; + +/* +** Click Object Action Struct +** Response to client clicking on a World Container (ie, forge) +* also sent by the client when they close the container. +** +*/ +struct ClickObjectAction_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 type; // See object.h, "Object Types" +/*16*/ uint32 unknown16; // set to 0xA +/*20*/ uint32 icon; // Icon to display for tradeskill containers +/*24*/ uint32 unknown24; // +/*28*/ char object_name[64]; // Object name to display +/*92*/ +}; + +/* +** This is different now, mostly unknown +** +*/ +struct CloseContainer_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 unknown12[12]; +}; + +/* +** Generic Door Struct +** Length: 52 Octets +** Used in: +** cDoorSpawnsStruct(f721) +** +*/ +struct Door_Struct +{ +/*0000*/ char name[32]; // Filename of Door // Was 10char long before... added the 6 in the next unknown to it: Daeken M. BlackBlade //changed both to 32: Trevius +/*0032*/ float yPos; // y loc +/*0036*/ float xPos; // x loc +/*0040*/ float zPos; // z loc +/*0044*/ float heading; +/*0048*/ uint32 incline; // rotates the whole door +/*0052*/ uint16 size; // 100 is normal, smaller number = smaller model +/*0054*/ uint8 unknown0038[6]; +/*0060*/ uint8 doorId; // door's id # +/*0061*/ uint8 opentype; +/* + * Open types: + * 66 = PORT1414 (Qeynos) + * 55 = BBBOARD (Qeynos) + * 100 = QEYLAMP (Qeynos) + * 56 = CHEST1 (Qeynos) + * 5 = DOOR1 (Qeynos) + */ +/*0062*/ uint8 state_at_spawn; +/*0063*/ uint8 invert_state; // if this is 1, the door is normally open +/*0064*/ uint32 door_param; +/*0068*/ uint8 unknown0052[12]; // mostly 0s, the last 3 bytes are something tho +/*0080*/ +}; + + + +struct DoorSpawns_Struct { + struct Door_Struct doors[0]; +}; + +/* + OP Code: Op_ClickDoor + Size: 16 +*/ +struct ClickDoor_Struct { +/*000*/ uint8 doorid; +/*001*/ uint8 unknown001; // This may be some type of action setting +/*002*/ uint8 unknown002; // This is sometimes set after a lever is closed +/*003*/ uint8 unknown003; // Seen 0 +/*004*/ uint8 picklockskill; +/*005*/ uint8 unknown005[3]; +/*008*/ uint32 item_id; +/*012*/ uint16 player_id; +/*014*/ uint8 unknown014[2]; +/*016*/ +}; + +struct MoveDoor_Struct { + uint8 doorid; + uint8 action; +}; + + +struct BecomeNPC_Struct { + uint32 id; + int32 maxlevel; +}; + +struct Underworld_Struct { + float speed; + float y; + float x; + float z; +}; + +struct Resurrect_Struct { +/*000*/ uint32 unknown000; +/*004*/ uint16 zone_id; +/*006*/ uint16 instance_id; +/*008*/ float y; +/*012*/ float x; +/*016*/ float z; +/*020*/ uint32 unknown020; +/*024*/ char your_name[64]; +/*088*/ uint32 unknown088; +/*092*/ char rezzer_name[64]; +/*156*/ uint32 spellid; +/*160*/ char corpse_name[64]; +/*224*/ uint32 action; +/* 228 */ +}; + +struct Translocate_Struct { +/*000*/ uint32 ZoneID; +/*004*/ uint32 SpellID; +/*008*/ uint32 unknown008; //Heading ? +/*012*/ char Caster[64]; +/*076*/ float y; +/*080*/ float x; +/*084*/ float z; +/*088*/ uint32 Complete; +}; + +struct Sacrifice_Struct { +/*000*/ uint32 CasterID; +/*004*/ uint32 TargetID; +/*008*/ uint32 Confirm; +}; + +struct SetRunMode_Struct { + uint8 mode; + uint8 unknown[3]; +}; + +//EnvDamage is EnvDamage2 without a few bytes at the end. + +struct EnvDamage2_Struct { +/*0000*/ uint32 id; +/*0004*/ uint16 unknown4; +/*0006*/ uint32 damage; +/*0010*/ uint8 unknown10[12]; +/*0022*/ uint8 dmgtype; //FA = Lava; FC = Falling +/*0023*/ uint8 unknown2[4]; +/*0027*/ uint16 constant; //Always FFFF +/*0029*/ uint16 unknown29; +}; + +//Bazaar Stuff =D +// + +enum { + BazaarTrader_StartTraderMode = 1, + BazaarTrader_EndTraderMode = 2, + BazaarTrader_UpdatePrice = 3, + BazaarTrader_EndTransaction = 4, + BazaarSearchResults = 7, + BazaarWelcome = 9, + BazaarBuyItem = 10, + BazaarTrader_ShowItems = 11, + BazaarSearchDone = 12, + BazaarTrader_CustomerBrowsing = 13, + BazaarInspectItem = 18, + BazaarSearchDone2 = 19, + BazaarTrader_StartTraderMode2 = 22 +}; + +enum { + BazaarPriceChange_Fail = 0, + BazaarPriceChange_UpdatePrice = 1, + BazaarPriceChange_RemoveItem = 2, + BazaarPriceChange_AddItem = 3 +}; + +struct BazaarWindowStart_Struct { + uint8 Action; + uint8 Unknown001; + uint16 Unknown002; +}; + + +struct BazaarWelcome_Struct { + BazaarWindowStart_Struct Beginning; + uint32 Traders; + uint32 Items; + uint8 Unknown012[8]; +}; + +struct BazaarSearch_Struct { + BazaarWindowStart_Struct Beginning; + uint32 TraderID; + uint32 Class_; + uint32 Race; + uint32 ItemStat; + uint32 Slot; + uint32 Type; + char Name[64]; + uint32 MinPrice; + uint32 MaxPrice; + uint32 Minlevel; + uint32 MaxLlevel; +}; +struct BazaarInspect_Struct{ + uint32 ItemID; + uint32 Unknown004; + char Name[64]; +}; + +struct NewBazaarInspect_Struct { +/*000*/ BazaarWindowStart_Struct Beginning; +/*004*/ char Name[64]; +/*068*/ uint32 Unknown068; +/*072*/ uint32 Unknown072; +/*076*/ uint32 Unknown076; +/*080*/ int32 SerialNumber; +/*084*/ uint32 Unknown084; +}; + +struct BazaarReturnDone_Struct{ + uint32 Type; + uint32 TraderID; + uint32 Unknown008; + uint32 Unknown012; + uint32 Unknown016; +}; +struct BazaarSearchResults_Struct { +/*000*/ BazaarWindowStart_Struct Beginning; +/*004*/ uint32 NumItems; +/*008*/ uint32 SerialNumber; +/*012*/ uint32 SellerID; +/*016*/ uint32 Cost; +/*020*/ uint32 ItemStat; +/*024*/ char ItemName[64]; +/*088*/ + // New fields for SoD+, stripped off when encoding for older clients. + char SellerName[64]; + uint32 ItemID; +}; + +// Barter/Buyer +// +// +enum { + Barter_BuyerSearch = 0, + Barter_SellerSearch = 1, + Barter_BuyerModeOn = 2, + Barter_BuyerModeOff = 3, + Barter_BuyerItemUpdate = 5, + Barter_BuyerItemRemove = 6, + Barter_SellItem = 7, + Barter_SellerTransactionComplete = 8, + Barter_BuyerTransactionComplete = 9, + Barter_BuyerInspectBegin = 10, + Barter_BuyerInspectEnd = 11, + Barter_BuyerAppearance = 12, + Barter_BuyerInspectWindow = 13, + Barter_BarterItemInspect = 14, + Barter_SellerBrowsing = 15, + Barter_BuyerSearchResults = 16, + Barter_Welcome = 17, + Barter_WelcomeMessageUpdate = 19, + Barter_BuyerItemInspect = 21, + Barter_Unknown23 = 23 +}; + +struct BuyerWelcomeMessageUpdate_Struct { +/*000*/ uint32 Action; +/*004*/ char WelcomeMessage[256]; +}; + +struct BuyerItemSearch_Struct { +/*000*/ uint32 Unknown000; +/*004*/ char SearchString[64]; +}; + +struct BuyerItemSearchResultEntry_Struct { +/*000*/ char ItemName[64]; +/*064*/ uint32 ItemID; +/*068*/ uint32 Unknown068; +/*072*/ uint32 Unknown072; +}; + +#define MAX_BUYER_ITEMSEARCH_RESULTS 200 + +struct BuyerItemSearchResults_Struct { + uint32 Action; + uint32 ResultCount; + BuyerItemSearchResultEntry_Struct Results[MAX_BUYER_ITEMSEARCH_RESULTS]; +}; + +struct BarterSearchRequest_Struct { + uint32 Action; + char SearchString[64]; + uint32 SearchID; +}; + +struct BuyerItemSearchLinkRequest_Struct { +/*000*/ uint32 Action; // 0x00000015 +/*004*/ uint32 ItemID; +/*008*/ uint32 Unknown008; +/*012*/ uint32 Unknown012; +}; + +struct BarterItemSearchLinkRequest_Struct { +/*000*/ uint32 Action; // 0x0000000E +/*004*/ uint32 SearcherID; +/*008*/ uint32 Unknown008; +/*012*/ uint32 Unknown012; +/*016*/ uint32 ItemID; +/*020*/ uint32 Unknown020; +}; + +struct BuyerInspectRequest_Struct { + uint32 Action; + uint32 BuyerID; + uint32 Approval; +}; + +struct BuyerBrowsing_Struct { + uint32 Action; + char PlayerName[64]; +}; + +struct BuyerRemoveItem_Struct { + uint32 Action; + uint32 BuySlot; +}; + +struct ServerSideFilters_Struct { +uint8 clientattackfilters; // 0) No, 1) All (players) but self, 2) All (players) but group +uint8 npcattackfilters; // 0) No, 1) Ignore NPC misses (all), 2) Ignore NPC Misses + Attacks (all but self), 3) Ignores NPC Misses + Attacks (all but group) +uint8 clientcastfilters; // 0) No, 1) Ignore PC Casts (all), 2) Ignore PC Casts (not directed towards self) +uint8 npccastfilters; // 0) No, 1) Ignore NPC Casts (all), 2) Ignore NPC Casts (not directed towards self) +}; + +/* +** Client requesting item statistics +** Size: 48 bytes +** Used In: OP_ItemLinkClick +** Last Updated: 2/15/2009 +** +*/ +struct ItemViewRequest_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 augments[5]; +/*024*/ uint32 link_hash; +/*028*/ uint32 unknown028; +/*032*/ char unknown032[12]; //probably includes loregroup & evolving info. see Client::MakeItemLink() in zone/inventory.cpp:469 +/*044*/ uint16 icon; +/*046*/ char unknown046[2]; +}; + +struct LDONItemViewRequest_Struct { + uint32 item_id; + uint8 unknown004[4]; + char item_name[64]; +}; + +/* + * Client to server packet + */ +struct PickPocket_Struct { +// Size 18 + uint32 to; + uint32 from; + uint16 myskill; + uint8 type; // -1 you are being picked, 0 failed , 1 = plat, 2 = gold, 3 = silver, 4 = copper, 5 = item + uint8 unknown1; // 0 for response, unknown for input + uint32 coin; + uint8 lastsix[2]; +}; +/* + * Server to client packet + */ + +enum { + PickPocketFailed = 0, + PickPocketPlatinum = 1, + PickPocketGold = 2, + PickPocketSilver = 3, + PickPocketCopper = 4, + PickPocketItem = 5 +}; + + +struct sPickPocket_Struct { + // Size 28 = coin/fail + uint32 to; + uint32 from; + uint32 myskill; + uint32 type; + uint32 coin; + char itemname[64]; +}; + +struct LogServer_Struct { +// Op_Code OP_LOGSERVER +/*000*/ uint32 unknown000; +/*004*/ uint8 enable_pvp; +/*005*/ uint8 unknown005; +/*006*/ uint8 unknown006; +/*007*/ uint8 unknown007; +/*008*/ uint8 enable_FV; +/*009*/ uint8 unknown009; +/*010*/ uint8 unknown010; +/*011*/ uint8 unknown011; +/*012*/ uint32 unknown012; // htonl(1) on live +/*016*/ uint32 unknown016; // htonl(1) on live +/*020*/ uint8 unknown020[12]; +/*032*/ char worldshortname[32]; +/*064*/ uint8 unknown064[32]; +/*096*/ char unknown096[16]; // 'pacman' on live +/*112*/ char unknown112[16]; // '64.37,148,36' on live +/*126*/ uint8 unknown128[48]; +/*176*/ uint32 unknown176; // htonl(0x00002695) +/*180*/ char unknown180[80]; // 'eqdataexceptions@mail.station.sony.com' on live +/*260*/ uint8 enable_petition_wnd; +/*261*/ uint8 enablevoicemacros; +/*262*/ uint8 enablemail; +/*263*/ uint8 disable_tutorial_go_home; +/*264*/ +}; + +struct ApproveWorld_Struct { +// Size 544 +// Op_Code OP_ApproveWorld + uint8 unknown544[544]; +}; + +struct ClientError_Struct +{ +/*00001*/ char type; +/*00001*/ char unknown0001[69]; +/*00069*/ char character_name[64]; +/*00134*/ char unknown134[192]; +/*00133*/ char message[31994]; +/*32136*/ +}; + +struct Track_Struct { + uint16 entityid; + uint16 padding002; + float distance; + // Fields for SoD and later + uint8 level; + uint8 NPC; + uint8 GroupMember; + char name[64]; +}; + +struct Tracking_Struct { + Track_Struct Entrys[0]; +}; + +struct TrackTarget_Struct +{ + uint32 EntityID; +}; + +/* +** ZoneServerInfo_Struct +** Zone server information +** Size: 130 bytes +** Used In: OP_ZoneServerInfo +** +*/ +struct ZoneServerInfo_Struct +{ +/*0000*/ char ip[128]; +/*0128*/ uint16 port; +}; + +struct WhoAllPlayer{ + uint32 formatstring; + uint32 pidstring; + char* name; + uint32 rankstring; + char* guild; + uint32 unknown80[2]; + uint32 zonestring; + uint32 zone; + uint32 class_; + uint32 level; + uint32 race; + char* account; + uint32 unknown100; +}; + +// The following four structs are the WhoAllPlayer struct above broken down +// for use in World ClientList::SendFriendsWho to accomodate the user of variable +// length strings within the struct above. + +struct WhoAllPlayerPart1 { + uint32 FormatMSGID; + uint32 PIDMSGID; + char Name[1];; +}; + +struct WhoAllPlayerPart2 { + uint32 RankMSGID; + char Guild[1]; +}; + +struct WhoAllPlayerPart3 { + uint32 Unknown80[2]; + uint32 ZoneMSGID; + uint32 Zone; + uint32 Class_; + uint32 Level; + uint32 Race; + char Account[1]; +}; + +struct WhoAllPlayerPart4 { + uint32 Unknown100; +}; + +struct WhoAllReturnStruct { +/*000*/ uint32 id; +/*004*/ uint32 playerineqstring; +/*008*/ char line[27]; +/*035*/ uint8 unknown35; //0A +/*036*/ uint32 unknown36;//0s +/*040*/ uint32 playersinzonestring; +/*044*/ uint32 unknown44[2]; //0s +/*052*/ uint32 unknown52;//1 +/*056*/ uint32 unknown56;//1 +/*060*/ uint32 playercount;//1 + struct WhoAllPlayer player[0]; +}; + +struct Trader_Struct { +/*000*/ uint32 Code; +/*004*/ uint32 Unknown004; +/*008*/ uint64 Items[80]; +/*648*/ uint32 ItemCost[80]; +}; + +struct ClickTrader_Struct { +/*000*/ uint32 Code; +/*004*/ uint32 Unknown004; +/*008*/ int64 SerialNumber[80]; +/*648*/ uint32 ItemCost[80]; +}; + +struct GetItems_Struct{ + uint32 Items[80]; + int32 SerialNumber[80]; + int32 Charges[80]; +}; + +struct BecomeTrader_Struct +{ +/*000*/ uint32 ID; +/*004*/ uint32 Code; +/*008*/ char Name[64]; +/*072*/ uint32 Unknown072; // Observed 0x33,0x91 etc on zone-in, 0x00 when sent for a new trader after zone-in +/*076*/ +}; + +struct TraderStatus_Struct{ + uint32 Code; + uint32 Uknown04; + uint32 Uknown08; +}; + +struct Trader_ShowItems_Struct{ +/*000*/ uint32 Code; +/*004*/ uint32 TraderID; +/*008*/ uint32 Unknown08[3]; +}; + +struct TraderBuy_Struct{ +/*000*/ uint32 Action; +/*004*/ uint32 TraderID; +/*008*/ uint32 ItemID; +/*012*/ uint32 AlreadySold; +/*016*/ uint32 Price; +/*020*/ uint32 Quantity; +/*024*/ char ItemName[64]; +}; + +struct TraderItemUpdate_Struct{ + uint32 Unknown000; + uint32 TraderID; + uint8 FromSlot; + int ToSlot; //7? + uint16 Charges; +}; + +struct TraderPriceUpdate_Struct { +/*000*/ uint32 Action; +/*004*/ uint32 SubAction; +/*008*/ int32 SerialNumber; +/*012*/ uint32 Unknown012; +/*016*/ uint32 NewPrice; +/*020*/ uint32 Unknown016; +}; + +struct MoneyUpdate_Struct{ + int32 platinum; + int32 gold; + int32 silver; + int32 copper; +}; + +struct TraderDelItem_Struct{ + uint32 Unknown000; + uint32 TraderID; + uint32 ItemID; + uint32 Unknown012; +}; + +struct TraderClick_Struct{ +/*000*/ uint32 TraderID; +/*004*/ uint32 Unknown004; +/*008*/ uint32 Unknown008; +/*012*/ uint32 Approval; +}; + +struct FormattedMessage_Struct{ + uint32 unknown0; + uint32 string_id; + uint32 type; + char message[0]; +}; +struct SimpleMessage_Struct{ + uint32 string_id; + uint32 color; + uint32 unknown8; +}; + +struct GuildMemberUpdate_Struct { +/*00*/ uint32 GuildID; +/*04*/ char MemberName[64]; +/*68*/ uint16 ZoneID; +/*70*/ uint16 InstanceID; //speculated +/*72*/ uint32 LastSeen; //unix timestamp +/*76*/ +}; + +struct GuildMemberLevelUpdate_Struct { +/*00*/ uint32 guild_id; +/*04*/ char member_name[64]; +/*68*/ uint32 level; //not sure +}; + +struct Internal_GuildMemberEntry_Struct { +// char name[64]; //variable length + uint32 level; //network byte order + uint32 banker; //1=yes, 0=no, network byte order + uint32 class_; //network byte order + uint32 rank; //network byte order + uint32 time_last_on; //network byte order + uint32 tribute_enable; //network byte order + uint32 total_tribute; //total guild tribute donated, network byte order + uint32 last_tribute; //unix timestamp +// char public_note[1]; //variable length. + uint16 zoneinstance; //network byte order + uint16 zone_id; //network byte order +}; + +struct Internal_GuildMembers_Struct { //just for display purposes, this is not actually used in the message encoding. + char player_name[64]; //variable length. + uint32 count; //network byte order + uint32 name_length; //total length of all of the char names, excluding terminators + uint32 note_length; //total length of all the public notes, excluding terminators + Internal_GuildMemberEntry_Struct member[0]; + /* + * followed by a set of `count` null terminated name strings + * and then a set of `count` null terminated public note strings + */ +}; + +struct GuildMOTD_Struct{ +/*0000*/ uint32 unknown0; +/*0004*/ char name[64]; +/*0068*/ char setby_name[64]; +/*0132*/ uint32 unknown132; +/*0136*/ char motd[512]; +}; + +struct GuildUpdate_PublicNote{ + uint32 unknown0; + char name[64]; + char target[64]; + char note[1]; //variable length. +}; + +struct GuildUpdateURLAndChannel_Struct +{ +/*0000*/ uint32 Action; // 0 = Update URL, 1 = Update Channel +/*0004*/ char Unknown0004[76]; +/*0080*/ char Text[512]; // URL can be up to 512, SoD client caps the Channel at 128; +/*0592*/ char Unknown0592[3584]; +/*4176*/ +}; + +struct GuildStatus_Struct +{ +/*000*/ char Name[64]; +/*064*/ uint8 Unknown064[72]; +}; + +struct GuildDemoteStruct{ + char name[64]; + char target[64]; +}; + +struct GuildRemoveStruct{ + char target[64]; + char name[64]; + uint32 unknown128; + uint32 leaderstatus; //? +}; + +struct GuildMakeLeader{ + char name[64]; + char target[64]; +}; + +struct BugStruct{ +/*0000*/ char chartype[64]; +/*0064*/ char name[96]; +/*0160*/ char ui[128]; +/*0288*/ float x; +/*0292*/ float y; +/*0296*/ float z; +/*0300*/ float heading; +/*0304*/ uint32 unknown304; +/*0308*/ char unknown308[160]; +/*0468*/ char target_name[64]; +/*0532*/ uint32 type; +/*0536*/ char unknown536[2052]; +/*2584*/ char bug[2048]; +/*4632*/ char unknown4632[6]; +/*4638*/ char system_info[4094]; +}; +struct Make_Pet_Struct { //Simple struct for getting pet info + uint8 level; + uint8 class_; + uint16 race; + uint8 texture; + uint8 pettype; + float size; + uint8 type; + uint32 min_dmg; + uint32 max_dmg; +}; +struct Ground_Spawn{ + float max_x; + float max_y; + float min_x; + float min_y; + float max_z; + float heading; + char name[20]; + uint32 item; + uint32 max_allowed; + uint32 respawntimer; +}; +struct Ground_Spawns { + struct Ground_Spawn spawn[50]; //Assigned max number to allow +}; +struct PetitionBug_Struct{ + uint32 petition_number; + uint32 unknown4; + char accountname[64]; + uint32 zoneid; + char name[64]; + uint32 level; + uint32 class_; + uint32 race; + uint32 unknown152[3]; + uint32 time; + uint32 unknown168; + char text[1028]; +}; + +struct DyeStruct +{ + union + { + struct + { + struct Color_Struct head; + struct Color_Struct chest; + struct Color_Struct arms; + struct Color_Struct wrists; + struct Color_Struct hands; + struct Color_Struct legs; + struct Color_Struct feet; + struct Color_Struct primary; // you can't actually dye this + struct Color_Struct secondary; // or this + } + dyes; + struct Color_Struct dye[MAX_MATERIALS]; + }; +}; + +struct ApproveZone_Struct { + char name[64]; + uint32 zoneid; + uint32 approve; +}; +struct ZoneInSendName_Struct { + uint32 unknown0; + char name[64]; + char name2[64]; + uint32 unknown132; +}; +struct ZoneInSendName_Struct2 { + uint32 unknown0; + char name[64]; + uint32 unknown68[145]; +}; + +static const uint32 MAX_TRIBUTE_TIERS = 10; + +struct StartTribute_Struct { + uint32 client_id; + uint32 tribute_master_id; + uint32 response; +}; + +struct TributeLevel_Struct { + uint32 level; //backwards byte order! + uint32 tribute_item_id; //backwards byte order! + uint32 cost; //backwards byte order! +}; + +struct TributeAbility_Struct { + uint32 tribute_id; //backwards byte order! + uint32 tier_count; //backwards byte order! + TributeLevel_Struct tiers[MAX_TRIBUTE_TIERS]; + char name[0]; +}; + +struct GuildTributeAbility_Struct { + uint32 guild_id; + TributeAbility_Struct ability; +}; + +struct SelectTributeReq_Struct { + uint32 client_id; //? maybe action ID? + uint32 tribute_id; + uint32 unknown8; //seen E3 00 00 00 +}; + +struct SelectTributeReply_Struct { + uint32 client_id; //echoed from request. + uint32 tribute_id; + char desc[0]; +}; + +struct TributeInfo_Struct { + uint32 active; //0 == inactive, 1 == active + uint32 tributes[MAX_PLAYER_TRIBUTES]; //-1 == NONE + uint32 tiers[MAX_PLAYER_TRIBUTES]; //all 00's + uint32 tribute_master_id; +}; + +struct TributeItem_Struct { + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; +}; + +struct TributePoint_Struct { + int32 tribute_points; + uint32 unknown04; + int32 career_tribute_points; + uint32 unknown12; +}; + +struct TributeMoney_Struct { + uint32 platinum; + uint32 tribute_master_id; + int32 tribute_points; +}; + + +struct Split_Struct +{ + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; +}; + + +/* +** New Combine Struct +** Client requesting to perform a tradeskill combine +** Size: 4 bytes +** Used In: OP_TradeSkillCombine +** Last Updated: Oct-15-2003 +** +*/ +struct NewCombine_Struct { +/*00*/ int16 container_slot; +/*02*/ char unknown02[2]; +/*04*/ +}; + + +//client requesting favorite recipies +struct TradeskillFavorites_Struct { + uint32 object_type; + uint32 some_id; + uint32 favorite_recipes[500]; +}; + +//search request +struct RecipesSearch_Struct { + uint32 object_type; //same as in favorites + uint32 some_id; //same as in favorites + uint32 mintrivial; + uint32 maxtrivial; + char query[56]; + uint32 unknown4; //is set to 00 03 00 00 + uint32 unknown5; //is set to 4C DD 12 00 +/*80*/ +}; + +//one sent for each item, from server in reply to favorites or search +struct RecipeReply_Struct { + uint32 object_type; + uint32 some_id; //same as in favorites + uint32 component_count; + uint32 recipe_id; + uint32 trivial; + char recipe_name[64]; +/*84*/ +}; + +//received and sent back as an ACK with different reply_code +struct RecipeAutoCombine_Struct { + uint32 object_type; + uint32 some_id; + uint32 unknown1; //echoed in reply + uint32 recipe_id; + uint32 reply_code; // 93 64 e1 00 (junk) in request + // 00 00 00 00 in successful reply + // f5 ff ff ff in 'you dont have all the stuff' reply +}; + +struct LevelAppearance_Struct { //Sends a little graphic on level up + uint32 spawn_id; + uint32 parm1; + uint32 value1a; + uint32 value1b; + uint32 parm2; + uint32 value2a; + uint32 value2b; + uint32 parm3; + uint32 value3a; + uint32 value3b; + uint32 parm4; + uint32 value4a; + uint32 value4b; + uint32 parm5; + uint32 value5a; + uint32 value5b; +/*64*/ +}; +struct MerchantList{ + uint32 id; + uint32 slot; + uint32 item; + int16 faction_required; + int8 level_required; + uint16 alt_currency_cost; +}; +struct TempMerchantList{ + uint32 npcid; + uint32 slot; + uint32 item; + uint32 charges; //charges/quantity + uint32 origslot; +}; + +struct AltCurrencyDefinition_Struct { + uint32 id; + uint32 item_id; +}; + +struct NPC_Emote_Struct { + uint32 emoteid; + uint8 event_; + uint8 type; + char text[515]; +}; + +struct FindPerson_Point { + float y; + float x; + float z; +}; + +struct FindPersonRequest_Struct { + uint32 npc_id; + FindPerson_Point client_pos; +}; + +//variable length packet of points +struct FindPersonResult_Struct { + FindPerson_Point dest; + FindPerson_Point path[0]; //last element must be the same as dest +}; + +struct MobRename_Struct { +/*000*/ char old_name[64]; +/*064*/ char old_name_again[64]; //not sure what the difference is +/*128*/ char new_name[64]; +/*192*/ uint32 unknown192; //set to 0 +/*196*/ uint32 unknown196; //set to 1 +/*200*/ +}; + +struct PlayMP3_Struct { + char filename[128]; +}; + +//this is for custom title display in the skill window +struct TitleEntry_Struct { + uint32 title_id; + char title[1]; + char suffix[1]; +}; + +struct Titles_Struct { + uint32 title_count; + TitleEntry_Struct titles[0]; +}; + +//this is for title selection by the client +struct TitleListEntry_Struct { + uint32 unknown0; //title ID + char prefix[1]; //variable length, null terminated + char postfix[1]; //variable length, null terminated +}; + +struct TitleList_Struct { + uint32 title_count; + TitleListEntry_Struct titles[0]; //list of title structs + //uint32 unknown_ending; seen 0x7265, 0 +}; + +struct SetTitle_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + uint32 title_id; +}; + +struct SetTitleReply_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + char title[32]; + uint32 entity_id; +}; + +struct TaskMemberList_Struct { +/*00*/ uint32 gopher_id; +/*04*/ uint32 unknown04; +/*08*/ uint32 member_count; //1 less than the number of members +/*12*/ char list_pointer[0]; +/* list is of the form: + char member_name[1] //null terminated string + uint8 task_leader //boolean flag +*/ +}; + +#if 0 + +// Old struct not used by Task System implementation but left for reference +struct TaskDescription_Struct { +/*000*/ uint32 activity_count; //not right. +/*004*/ uint32 taskid; +/*008*/ uint8 unk; +/*009*/ uint32 id3; +/*013*/ uint32 unknown13; +/*017*/ char name[1]; //variable length, 0 terminated +/*018*/ uint32 unknown18; +/*022*/ uint32 unknown22; +/*026*/ char desc[1]; //variable length, 0 terminated +/*027*/ uint32 reward_count; //not sure +/*031*/ uint32 unknown31; +/*035*/ uint32 unknown35; +/*039*/ uint16 unknown39; +/*041*/ char reward_link[1]; //variable length, 0 terminated +/*042*/ uint32 unknown43; //maybe crystal count? +}; + +// Old structs not used by Task System implentation but left for reference +struct TaskActivity_Struct { +/*000*/ uint32 activity_count; //not right +/*004*/ uint32 id3; +/*008*/ uint32 taskid; +/*012*/ uint32 activity_id; +/*016*/ uint32 unknown016; +/*020*/ uint32 activity_type; +/*024*/ uint32 unknown024; +/*028*/ uint32 unknown28; +/*032*/ char mob_name[1]; //variable length, 0 terminated +/*033*/ char item_name[1]; //variable length, 0 terminated +/*034*/ uint32 goal_count; +/*038*/ uint32 unknown38; //0xFFFFFFFF +/*042*/ uint32 unknown42; //0xFFFFFFFF +/*046*/ uint32 unknown46; //saw 0x151,0x156 +/*050*/ uint32 unknown50; //saw 0x404,0 +/*054*/ char activity_name[1]; //variable length, 0 terminated... commonly empty +/*055*/ uint32 done_count; +/*059*/ uint32 unknown59; //=1 except on unknown and terminal activities? +/*063*/ +}; + +struct TaskSelectorWindowHeader_Struct { +/*000*/ uint32 TaskCount; // Guessed - Seen 1 +/*000*/ uint32 Unknown2; // Seen 2 +/*000*/ uint32 TaskGiver; // Guessed - Seen 1517 +/*000*/ TaskSelectorData_Struct Tasks[1]; +}; + +struct TaskSelectorData_Struct { +/*000*/ uint32 TaskID; // Seen 208 +/*000*/ float Unknown5; // Seen 1.0 +/*000*/ uint32 Unknown6; // Seen 0 +/*000*/ uint32 Unknown7; // Seen 0 +/*000*/ char TaskName[1]; // Null Terminated +/*000*/ char TaskDescription[1]; // Null Terminated +/*000*/ uint8 Unknown10; // Possibly another Null Terminated String? Seen 0 +/*000*/ uint32 ActivityCount; // Seen 5 +/*000*/ TaskSelectorActivities_Struct Activities[1]; +}; + +struct TaskSelectorActivities_Struct { +/*000*/ uint32 ActivityNumber; // Seen 0 to 4 +/*000*/ uint32 ActivityType; // Guessed - 1 = Give? 2 = Kill? 3 = Loot? 5 = Speak? +/*000*/ uint32 Unknown14; // Seen 0 +/*000*/ char Text1; // Seen 0 +/*000*/ uint32 Text2Len; // Size of the following string +/*000*/ char Text2; // If the previous field is 0, this field is not send (item name?) +/*000*/ uint32 GoalCount; // Seen 1 +/*000*/ uint32 NumString1Len; // Seen 2 +/*000*/ char NumString1; // Seen "-1" +/*000*/ uint32 NumString2Len; // Seen 2 +/*000*/ char NumString2; // Seen "-1" +/*000*/ char ZoneIDString; // Seen "188" tutoriala +/*000*/ char Text3[1]; // Null Terminated +/*000*/ char NumString3; // Null Terminated - Seen "1546" +}; + +struct TaskHistoryEntry_Struct { + uint32 task_id; + char name[1]; + uint32 completed_time; +}; +struct TaskHistory_Struct { + uint32 completed_count; + TaskHistoryEntry_Struct entries[0]; +}; +#endif + +struct AcceptNewTask_Struct { + uint32 unknown00; + uint32 task_id; //set to 0 for 'decline' + uint32 task_master_id; //entity ID +}; + +//was all 0's from client, server replied with same op, all 0's +struct CancelTask_Struct { + uint32 SequenceNumber; + uint32 unknown4; // Only seen 0x00000002 +}; + +#if 0 +// old struct, not used by Task System implementation but left for reference. +struct AvaliableTask_Struct { + uint32 task_index; //no idea, seen 0x1 + uint32 task_master_id; //entity ID + uint32 task_id; + uint32 unknown012; + uint32 activity_count; //not sure, seen 2 + char desc[1]; //variable length, 0 terminated + uint32 reward_platinum;//not sure on these + uint32 reward_gold; + uint32 reward_silver; + uint32 reward_copper; + char some_name[1]; //variable length, 0 terminated + uint8 unknown1; + uint32 unknown2; //0xFFFFFFFF + uint32 unknown3; //0xFFFFFFFF + uint32 unknown4; //seen 0x16 + uint8 unknown5; +}; +#endif + + +// Many of the Task System packets contain variable length strings, as well as variable numbers +// of records, hence splitting them into multiple structs (header, middle, trailer) etc. +// +struct AvailableTaskHeader_Struct { + uint32 TaskCount; + uint32 unknown1; + uint32 TaskGiver; +}; + +struct AvailableTaskData1_Struct { + uint32 TaskID; + uint32 TimeLimit; + uint32 unknown2; +}; + +struct AvailableTaskData2_Struct { + uint32 unknown1,unknown2,unknown3,unknown4; +}; + +struct AvailableTaskTrailer_Struct { + uint32 ItemCount; + uint32 unknown1, unknown2; + uint32 StartZone; +}; + +struct TaskDescriptionHeader_Struct { + uint32 SequenceNumber; // The order the tasks appear in the journal. 0 for first task, 1 for second, etc. + uint32 TaskID; + uint32 unknown2; + uint32 unknown3; + uint8 unknown4; +}; + +struct TaskDescriptionData1_Struct { + uint32 Duration; + uint32 unknown2; + uint32 StartTime; +}; + +struct TaskDescriptionData2_Struct { + uint32 RewardCount; // ?? + uint32 unknown1; + uint32 unknown2; + uint16 unknown3; + //uint8 unknown4; +}; + +struct TaskDescriptionTrailer_Struct { + //uint16 unknown1; // 0x0012 + uint32 Points; +}; + +struct TaskActivityHeader_Struct { + uint32 TaskSequenceNumber; + uint32 unknown2; // Seen 0x00000002 + uint32 TaskID; + uint32 ActivityID; + uint32 unknown3; + uint32 ActivityType; + uint32 Optional; + uint32 unknown5; +}; + +struct TaskActivityData1_Struct { + uint32 GoalCount; + uint32 unknown1; // 0xffffffff + uint32 unknown2; // 0xffffffff + uint32 ZoneID; // seen 0x36 + uint32 unknown3; +}; + +struct TaskActivityTrailer_Struct { + uint32 DoneCount; + uint32 unknown1; // Seen 1 +}; + +// The Short_Struct is sent for tasks that are hidden and act as a placeholder +struct TaskActivityShort_Struct { + uint32 TaskSequenceNumber; + uint32 unknown2; // Seen 0x00000002 + uint32 TaskID; + uint32 ActivityID; + uint32 unknown3; + uint32 ActivityType; // 0xffffffff for the short packet + uint32 unknown4; +}; + +struct TaskActivityComplete_Struct { + uint32 TaskIndex; + uint32 unknown2; // 0x00000002 + uint32 unknown3; + uint32 ActivityID; + uint32 unknown4; // 0x00000001 + uint32 unknown5; // 0x00000001 +}; + +#if 0 +// This is a dupe of the CancelTask struct +struct TaskComplete_Struct { + uint32 unknown00; // 0x00000000 + uint32 unknown04; // 0x00000002 +}; +#endif + +struct TaskHistoryRequest_Struct { + uint32 TaskIndex; // This is the sequence the task was sent in the Completed Tasks packet. +}; + +struct TaskHistoryReplyHeader_Struct { + uint32 TaskID; + uint32 ActivityCount; +}; + +struct TaskHistoryReplyData1_Struct { + uint32 ActivityType; +}; + +struct TaskHistoryReplyData2_Struct { + uint32 GoalCount; + uint32 unknown04; // 0xffffffff + uint32 unknown08; // 0xffffffff + uint32 ZoneID; + uint32 unknown16; +}; + +enum { VoiceMacroTell = 1, VoiceMacroGroup = 2, VoiceMacroRaid = 3 }; + +struct VoiceMacroIn_Struct { +/*000*/ char Unknown000[64]; +/*064*/ uint32 Type; // 1 = Tell, 2 = Group, 3 = Raid +/*068*/ char Target[64]; +/*132*/ uint32 Unknown132; // Seems to be 0x0000000c always +/*136*/ uint32 MacroNumber; +}; + +struct VoiceMacroOut_Struct { +/*000*/ char From[64]; +/*064*/ uint32 Type; // 1 = Tell, 2 = Group, 3 = Raid +/*068*/ uint32 Unknown068; +/*072*/ uint32 Voice; +/*076*/ uint32 MacroNumber; +/*080*/ char Unknown080[60]; +}; + +struct BankerChange_Struct { + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; + uint32 platinum_bank; + uint32 gold_bank; + uint32 silver_bank; + uint32 copper_bank; +}; + +struct LeadershipExpUpdate_Struct { + /*0000*/ uint32 unknown0000; // All zeroes? + /*0004*/ uint32 group_leadership_exp; // Group leadership exp value + /*0008*/ uint32 group_leadership_points; // Unspent group points + /*0012*/ uint32 unknown0012; // Type? + /*0016*/ uint32 unknown0016; // All zeroes? + /*0020*/ uint32 raid_leadership_exp; // Raid leadership exp value + /*0024*/ uint32 raid_leadership_points; // Unspent raid points + /*0028*/ uint32 unknown0028; +}; + +struct UpdateLeadershipAA_Struct { +/*00*/ uint32 ability_id; +/*04*/ uint32 new_rank; +/*08*/ uint32 pointsleft; +/*12*/ +}; + +enum +{ + GroupLeadershipAbility_MarkNPC = 0 +}; + +struct DoGroupLeadershipAbility_Struct +{ +/*000*/ uint32 Ability; +/*000*/ uint32 Parameter; +}; + +struct DelegateAbility_Struct +{ +/*000*/ uint32 DelegateAbility; +/*004*/ uint32 MemberNumber; +/*008*/ uint32 Action; +/*012*/ uint32 Unknown012; +/*016*/ uint32 Unknown016; +/*020*/ uint32 EntityID; +/*024*/ uint32 Unknown024; +/*028*/ char Name[64]; +}; + +struct GroupUpdateLeaderAA_Struct +{ +/*000*/ char Unknown000[64]; +/*064*/ GroupLeadershipAA_Struct LeaderAAs; +/*128*/ char unknown128[128]; +}; + +struct MarkNPC_Struct +{ +/*00*/ uint32 TargetID; // Target EntityID +/*04*/ uint32 Number; // Number to mark them with (1, 2 or 3) + // The following field is for SoD+ +/*08**/ char Name[64]; +}; + +struct RaidGeneral_Struct { +/*00*/ uint32 action; //=10 +/*04*/ char player_name[64]; //should both be the player's name +/*64*/ char leader_name[64]; +/*132*/ uint32 parameter; +}; + +struct RaidAddMember_Struct { +/*000*/ RaidGeneral_Struct raidGen; //param = (group num-1); 0xFFFFFFFF = no group +/*136*/ uint8 _class; +/*137*/ uint8 level; +/*138*/ uint8 isGroupLeader; +/*139*/ uint8 flags[5]; //no idea if these are needed... +}; + + +struct RaidAdd_Struct { +/*000*/ uint32 action; //=0 +/*004*/ char player_name[64]; //should both be the player's name +/*068*/ char leader_name[64]; +/*132*/ uint8 _class; +/*133*/ uint8 level; +/*134*/ uint8 has_group; +/*135*/ uint8 unknown135; //seems to be 0x42 or 0 +}; + +struct RaidCreate_Struct { +/*00*/ uint32 action; //=8 +/*04*/ char leader_name[64]; +/*68*/ uint32 leader_id; +}; + +struct RaidMemberInfo_Struct { +/*00*/ uint8 group_number; +/*01*/ char member_name[1]; //dyanmic length, null terminated '\0' +/*00*/ uint8 unknown00; +/*01*/ uint8 _class; +/*02*/ uint8 level; +/*03*/ uint8 is_raid_leader; +/*04*/ uint8 is_group_leader; +/*05*/ uint8 main_tank; //not sure +/*06*/ uint8 unknown06[5]; //prolly more flags +}; + +struct RaidDetails_Struct { +/*000*/ uint32 action; //=6,20 +/*004*/ char leader_name[64]; +/*068*/ uint32 unknown68[4]; +/*084*/ LeadershipAA_Struct abilities; //ranks in backwards byte order +/*128*/ uint8 unknown128[142]; +/*354*/ uint32 leader_id; +}; + +struct RaidMembers_Struct { +/*000*/ RaidDetails_Struct details; +/*358*/ uint32 member_count; //including leader +/*362*/ RaidMemberInfo_Struct members[1]; +/*...*/ RaidMemberInfo_Struct empty; //seem to have an extra member with a 0 length name on the end +}; + +struct DynamicWall_Struct { +/*00*/ char name[32]; +/*32*/ float y; +/*36*/ float x; +/*40*/ float z; +/*44*/ uint32 something; +/*48*/ uint32 unknown48; //0 +/*52*/ uint32 one_hundred; //0x64 +/*56*/ uint32 unknown56; //0 +/*60*/ uint32 something2; +/*64*/ int32 unknown64; //-1 +/*68*/ uint32 unknown68; //0 +/*72*/ uint32 unknown72; //0 +/*76*/ uint32 unknown76; //0x100 +/*80*/ +}; + +enum { //bandolier actions + BandolierCreate = 0, + BandolierRemove = 1, + BandolierSet = 2 +}; + +struct BandolierCreate_Struct { +/*00*/ uint32 action; //0 for create +/*04*/ uint8 number; +/*05*/ char name[32]; +/*37*/ uint16 unknown37; //seen 0x93FD +/*39*/ uint8 unknown39; //0 +}; + +struct BandolierDelete_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ uint8 unknown05[35]; +}; + +struct BandolierSet_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ uint8 unknown05[35]; +}; + +struct Arrow_Struct { +/*000*/ uint32 type; //unsure on name, seems to be 0x1, dosent matter +/*005*/ uint8 unknown004[12]; +/*016*/ float src_y; +/*020*/ float src_x; +/*024*/ float src_z; +/*028*/ uint8 unknown028[12]; +/*040*/ float velocity; //4 is normal, 20 is quite fast +/*044*/ float launch_angle; //0-450ish, not sure the units, 140ish is straight +/*048*/ float tilt; //on the order of 125 +/*052*/ uint8 unknown052[8]; +/*060*/ float arc; +/*064*/ uint8 unknown064[12]; +/*076*/ uint32 source_id; +/*080*/ uint32 target_id; //entity ID +/*084*/ uint32 item_id; //1 to about 150ish +/*088*/ uint32 unknown088; //seen 125, dosent seem to change anything.. +/*092*/ uint32 unknown092; //seen 16, dosent seem to change anything +/*096*/ uint8 unknown096[2]; +/*098*/ uint8 skill; +/*099*/ uint8 item_type; +/*100*/ uint8 unknown100; +/*101*/ char model_name[16]; +/*117*/ uint8 unknown117[19]; +}; + +//made a bunch of trivial structs for stuff for opcode finder to use +struct Consent_Struct { + char name[1]; //always at least a null +}; + +enum { LDoNMerchant = 1, DiscordMerchant = 2, NorrathsKeepersMerchant = 4, DarkReignMerchant = 5 }; + +struct AdventureMerchant_Struct { + uint32 Type; // 1 = LDoN, 2 = Discord, 4 = Norrath's Keepers, 5 = Dark Reign + uint32 entity_id; +}; + +struct Save_Struct { + uint8 unknown00[192]; +}; + +struct GMToggle_Struct { + uint8 unknown0[64]; + uint32 toggle; +}; + +struct GroupInvite_Struct { + char invitee_name[64]; + char inviter_name[64]; +// uint8 unknown128[65]; +}; + +struct BuffFadeMsg_Struct { + uint32 color; + char msg[1]; +}; + +struct UseAA_Struct { + uint32 begin; + uint32 ability; + uint32 end; +}; + +struct AA_Ability { +/*00*/ uint32 skill_id; +/*04*/ uint32 base1; +/*08*/ uint32 base2; +/*12*/ uint32 slot; +}; + +struct SendAA_Struct { +/* EMU additions for internal use */ + char name[128]; + int16 cost_inc; + uint32 sof_current_level; + uint32 sof_next_id; + uint8 level_inc; + +/*0000*/ uint32 id; +/*0004*/ uint32 unknown004; +/*0008*/ uint32 hotkey_sid; +/*0012*/ uint32 hotkey_sid2; +/*0016*/ uint32 title_sid; +/*0020*/ uint32 desc_sid; +/*0024*/ uint32 class_type; +/*0028*/ uint32 cost; +/*0032*/ uint32 seq; +/*0036*/ uint32 current_level; //1s, MQ2 calls this AARankRequired +/*0040*/ uint32 prereq_skill; //is < 0, abs() is category # +/*0044*/ uint32 prereq_minpoints; //min points in the prereq +/*0048*/ uint32 type; +/*0052*/ uint32 spellid; +/*0056*/ uint32 spell_type; +/*0060*/ uint32 spell_refresh; +/*0064*/ uint16 classes; +/*0066*/ uint16 berserker; //seems to be 1 if its a berserker ability +/*0068*/ uint32 max_level; +/*0072*/ uint32 last_id; +/*0076*/ uint32 next_id; +/*0080*/ uint32 cost2; +/*0084*/ uint32 unknown80[2]; //0s +// Begin SoF Specific/Adjusted AA Fields +/*0088*/ uint32 aa_expansion; +/*0092*/ uint32 special_category; +/*0096*/ uint32 sof_type; +/*0100*/ uint32 sof_cost_inc; +/*0104*/ uint32 sof_max_level; +/*0108*/ uint32 sof_next_skill; +/*0112*/ uint32 clientver; +/*0016*/ uint32 account_time_required; +/*0120*/ uint32 total_abilities; +/*0124*/ AA_Ability abilities[0]; +}; + +struct AA_Action { +/*00*/ uint32 action; +/*04*/ uint32 ability; +/*08*/ uint32 unknown08; +/*12*/ uint32 exp_value; +}; + + +struct AA_Skills { //this should be removed and changed to AA_Array +/*00*/ uint32 aa_skill; // Total AAs Spent +/*04*/ uint32 aa_value; +/*08*/ uint32 unknown08; +/*12*/ +}; + +struct AAExpUpdate_Struct { +/*00*/ uint32 unknown00; //seems to be a value from AA_Action.ability +/*04*/ uint32 aapoints_unspent; +/*08*/ uint8 aaxp_percent; //% of exp that goes to AAs +/*09*/ uint8 unknown09[3]; //live dosent always zero these, so they arnt part of aaxp_percent +}; + + +struct AltAdvStats_Struct { +/*000*/ uint32 experience; +/*004*/ uint16 unspent; +/*006*/ uint16 unknown006; +/*008*/ uint8 percentage; +/*009*/ uint8 unknown009[3]; +}; + +struct PlayerAA_Struct { // Is this still used? + AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct AATable_Struct { +/*00*/ int32 aa_spent; // Total AAs Spent +/*04*/ AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct Weather_Struct { + uint32 val1; //generall 0x000000FF + uint32 type; //0x31=rain, 0x02=snow(i think), 0 = normal + uint32 mode; +}; + +struct ZoneInUnknown_Struct { + uint32 val1; + uint32 val2; + uint32 val3; +}; + +struct MobHealth_Struct { + uint16 entity_id; + uint8 hp; +}; + +struct AnnoyingZoneUnknown_Struct { + uint32 entity_id; + uint32 value; //always 4 +}; + +struct LoadSpellSet_Struct { + uint32 spell[MAX_PP_MEMSPELL]; // 0xFFFFFFFF if no action, slot number if to unmem starting at 0 + uint32 unknown; //there seems to be an extra field in this packet... +}; + +// This is the structure for OP_ZonePlayerToBind opcode. Discovered on Feb 9 2007 by FNW from packet logs for titanium client +// This field "zone_name" is text the Titanium client will display on player death +// it appears to be a variable length, null-terminated string +// In logs it has "Bind Location" text which shows up on Titanium client as .... +// "Return to Bind Location, please wait..." +// This can be used to send zone name instead.. On 6.2 client, this is ignored. +struct ZonePlayerToBind_Struct { +/*000*/ uint16 bind_zone_id; +/*002*/ uint16 bind_instance_id; +/*004*/ float x; +/*008*/ float y; +/*012*/ float z; +/*016*/ float heading; +/*020*/ char zone_name[1]; +}; + +typedef struct { +/*000*/ uint32 bind_number; // Number of this bind in the iteration +/*004*/ uint32 bind_zone_id; // ID of the zone for this bind point or resurect point +/*008*/ float x; // X loc for this bind point +/*012*/ float y; // Y loc for this bind point +/*016*/ float z; // Z loc for this bind point +/*020*/ float heading; // Heading for this bind point +/*024*/ char bind_zone_name[1]; // Or "Bind Location" or "Resurrect" +/*000*/ uint8 validity; // 0 = valid choice, 1 = not a valid choice at this time (resurrection) +} RespawnOptions_Struct; + +struct RespawnWindow_Struct { +/*000*/ uint32 unknown000; // Seen 0 +/*004*/ uint32 time_remaining; // Total time before respawn in milliseconds +/*008*/ uint32 unknown008; // Seen 0 +/*012*/ uint32 total_binds; // Total Bind Point Options? - Seen 2 +/*016*/ RespawnOptions_Struct bind_points; +// First bind point is "Bind Location" and the last one is "Ressurect" +}; + +/** + * Shroud spawn. For others shrouding, this has their spawnId and + * spawnStruct. + * + * Length: 586 + * OpCode: OP_Shroud + */ +struct spawnShroudOther +{ +/*0000*/ uint32 spawnId; // Spawn Id of the shrouded player +/*0004*/ Spawn_Struct spawn; // Updated spawn struct for the player +/*0586*/ +}; + +struct ApplyPoison_Struct { + uint32 inventorySlot; + uint32 success; +}; + +struct ItemVerifyRequest_Struct { +/*000*/ int32 slot; // Slot being Right Clicked +/*004*/ uint32 target; // Target Entity ID +/*008*/ +}; + +struct ItemVerifyReply_Struct { +/*000*/ int32 slot; // Slot being Right Clicked +/*004*/ uint32 spell; // Spell ID to cast if different than item effect +/*008*/ uint32 target; // Target Entity ID +/*012*/ +}; + +/** + * Shroud yourself. For yourself shrouding, this has your spawnId, spawnStruct, + * bits of your charProfileStruct (no checksum, then charProfile up till + * but not including name), and an itemPlayerPacket for only items on the player + * and not the bank. + * + * Length: Variable + * OpCode: OP_Shroud + */ +#if 0 +struct spawnShroudSelf +{ +/*00000*/ uint32 spawnId; // Spawn Id of you +/*00004*/ Spawn_Struct spawn; // Updated spawnStruct for you +//this is a sub-struct of PlayerProfile, which we havent broken out yet. +/*00586*/ playerProfileStruct profile; // Character profile for shrouded char +/*13522*/ uint8 items; // Items on the player +/*xxxxx*/ +}; +#endif + + +typedef struct { + char Name[64]; + uint16 Class; + uint16 Level; + uint16 Zone; + uint16 GuildID; +} GroupLFPMemberEntry; + +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 +}; + +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 +/*072*/ +}; + +struct ClearObject_Struct +{ +/*000*/ uint8 Clear; // If this is not set to non-zero there is a random chance of a client crash. +/*001*/ uint8 Unknown001[7]; +}; + +struct PVPStats_Struct +{ +/*0000*/ uint32 Kills; +/*0004*/ uint32 Deaths; +/*0008*/ uint32 PVPPointsAvailable; +/*0012*/ uint32 TotalPVPPoints; +/*0016*/ uint32 BestKillStreak; +/*0020*/ uint32 WorstDeathStreak; +/*0024*/ uint32 CurrentKillStreak; +/*0028*/ uint32 Infamy; +/*0032*/ uint32 Vitality; +/*0036*/ PVPStatsEntry_Struct LastDeath; +/*0124*/ PVPStatsEntry_Struct LastKill; +/*0212*/ PVPStatsEntry_Struct KillsLast24Hours[50]; +/*4612*/ +}; + +typedef struct +{ +/*000*/ char Name[64]; +/*064*/ uint32 Kills; +/*068*/ uint32 Deaths; +/*072*/ uint32 TotalPoints; +/*076*/ uint32 Infamy; +/*080*/ +} PVPLeaderBoardEntry_Struct; + +enum { PVPSortByKills = 0, PVPSortByPoints, PVPSortByInfamy }; + +struct PVPLeaderBoardRequest_Struct +{ +/*00*/ uint32 SortType; +/*04*/ +}; + +struct PVPLeaderBoard_Struct +{ +/*0000*/ uint32 Unknown0000; +/*0004*/ uint32 MyKills; +/*0008*/ uint32 MyTotalPoints; +/*0012*/ uint32 MyRank; +/*0016*/ uint32 MyDeaths; +/*0020*/ uint32 MyInfamy; +/*0024*/ PVPLeaderBoardEntry_Struct Entries[100]; +/*8024*/ +}; + +struct PVPLeaderBoardDetailsRequest_Struct +{ +/*00*/ uint32 Unknown00; +/*04*/ char Name[64]; +/*68*/ +}; + +struct PVPLeaderBoardDetailsReply_Struct +{ +/*000*/ char Name[64]; +/*064*/ uint8 Unknown064[64]; +/*128*/ uint32 Level; +/*132*/ uint32 Race; +/*136*/ uint32 Class; +/*140*/ uint32 GuildID; +/*144*/ uint32 TotalAA; +/*148*/ uint32 Unknown148; +/*152*/ uint32 Kills; +/*156*/ uint32 Deaths; +/*160*/ uint32 Infamy; +/*164*/ uint32 Points; +/*168*/ +}; + +struct DisciplineTimer_Struct +{ +/*00*/ uint32 TimerID; +/*04*/ uint32 Duration; +/*08*/ uint32 Unknown08; +}; + +struct InternalVeteranRewardItem +{ +/*000*/ uint32 item_id; +/*004*/ uint32 charges; +/*008*/ char item_name[64]; +}; + +struct InternalVeteranReward +{ +/*000*/ uint32 claim_id; +/*004*/ uint32 number_available; +/*008*/ uint32 claim_count; +/*012*/ InternalVeteranRewardItem items[8]; +}; + +struct VeteranClaimReply +{ +/*000*/ char name[64]; +/*064*/ uint32 claim_id; +/*068*/ uint32 reject_field; +/*072*/ uint32 unknown072; +}; + +struct VeteranClaimRequest +{ +/*000*/ char name_data[64]; //name + other data +/*064*/ uint32 claim_id; +/*068*/ uint32 unknown068; +}; + +struct GMSearchCorpse_Struct +{ +/*000*/ char Unknown000[64]; +/*064*/ char Name[64]; +/*128*/ uint32 Unknown128; +}; + +struct CrystalCountUpdate_Struct +{ +/*000*/ uint32 CurrentRadiantCrystals; +/*004*/ uint32 CurrentEbonCrystals; +/*008*/ uint32 CareerRadiantCrystals; +/*012*/ uint32 CareerEbonCrystals; +}; + +struct BeggingResponse_Struct +{ +/*00*/ uint32 Unknown00; +/*04*/ uint32 Unknown04; +/*08*/ uint32 Unknown08; +/*12*/ uint32 Result; // 0 = Fail, 1 = Plat, 2 = Gold, 3 = Silver, 4 = Copper +/*16*/ uint32 Amount; +}; + +struct GuildBankAck_Struct +{ +/*00*/ uint32 Action; // 10 +/*04*/ uint32 Unknown04; +}; + +struct GuildBankDepositAck_Struct +{ +/*00*/ uint32 Action; // 10 +/*04*/ uint32 Unknown04; +/*08*/ uint32 Fail; //1 = Fail, 0 = Success +}; + +struct GuildBankPromote_Struct +{ +/*00*/ uint32 Action; // 3 +/*04*/ uint32 Unknown04; +/*08*/ uint32 Slot; +/*12*/ uint32 Slot2; // Always appears to be the same as Slot for Action code 3 +}; + +struct GuildBankPermissions_Struct +{ +/*00*/ uint32 Action; // 6 +/*04*/ uint32 Unknown04; +/*08*/ uint16 SlotID; +/*10*/ uint16 Unknown10; // Saw 1, probably indicating it is the main area rather than deposits +/*12*/ uint32 ItemID; +/*16*/ uint32 Permissions; +/*20*/ char MemberName[64]; +}; + +struct GuildBankViewItem_Struct +{ +/*00*/ uint32 Action; +/*04*/ uint32 Unknown04; +/*08*/ uint16 SlotID; // 0 = Deposit area, 1 = Main area +/*10*/ uint16 Area; +/*12*/ uint32 Unknown12; +/*16*/ uint32 Unknown16; +}; + +struct GuildBankWithdrawItem_Struct +{ +/*00*/ uint32 Action; +/*04*/ uint32 Unknown04; +/*08*/ uint16 SlotID; +/*10*/ uint16 Area; +/*12*/ uint32 Unknown12; +/*16*/ uint32 Quantity; +/*20*/ +}; + +struct GuildBankItemUpdate_Struct +{ + void Init(uint32 inAction, uint32 inUnknown004, uint16 inSlotID, uint16 inArea, uint16 inUnknown012, uint32 inItemID, uint32 inIcon, uint32 inQuantity, + uint32 inPermissions, uint32 inAllowMerge, bool inUseable) + { + Action = inAction; + Unknown004 = inUnknown004; + SlotID = inSlotID; + Area = inArea; + Unknown012 = inUnknown012; + ItemID = inItemID; + Icon = inIcon; + Quantity = inQuantity; + Permissions = inPermissions; + AllowMerge = inAllowMerge; + Useable = inUseable; + ItemName[0] = '\0'; + Donator[0] = '\0'; + WhoFor[0] = '\0'; + }; + +/*000*/ uint32 Action; +/*004*/ uint32 Unknown004; +/*008*/ uint16 SlotID; +/*010*/ uint16 Area; +/*012*/ uint32 Unknown012; +/*016*/ uint32 ItemID; +/*020*/ uint32 Icon; +/*024*/ uint32 Quantity; +/*028*/ uint32 Permissions; +/*032*/ uint8 AllowMerge; +/*033*/ uint8 Useable; // Used in conjunction with the Public-if-useable permission. +/*034*/ char ItemName[64]; +/*098*/ char Donator[64]; +/*162*/ char WhoFor[64]; +/*226*/ uint16 Unknown226; +}; + +struct GuildBankClear_Struct +{ +/*00*/ uint32 Action; +/*04*/ uint32 Unknown04; +/*08*/ uint32 DepositAreaCount; +/*12*/ uint32 MainAreaCount; +}; + +struct FindableNPC_Struct +{ +/*000*/ uint32 Action; // 0 = Add, 1 = Remove +/*004*/ uint32 EntityID; +/*008*/ char Name[64]; +/*072*/ char LastName[32]; +/*104*/ uint32 Race; +/*108*/ uint8 Class; +/*109*/ uint8 Unknown109; // Observed 0x16 +/*110*/ uint8 Unknown110; // Observed 0x06 +/*111*/ uint8 Unknown111; // Observed 0x24 +/*112*/ +}; + +struct GroupRole_Struct +{ +/*000*/ char Name1[64]; +/*064*/ char Name2[64]; +/*128*/ uint32 Unknown128; +/*132*/ uint32 Unknown132; +/*136*/ uint32 Unknown136; +/*140*/ uint32 RoleNumber; +/*144*/ uint8 Toggle; +/*145*/ uint8 Unknown145[3]; +/*148*/ +}; + +struct HideCorpse_Struct +{ +/*00*/ uint32 Action; +/*04*/ uint32 Unknown04; +/*08*/ +}; + +struct BuffIconEntry_Struct +{ + uint32 buff_slot; + uint32 spell_id; + uint32 tics_remaining; +}; + +struct BuffIcon_Struct +{ + uint32 entity_id; + uint16 count; + BuffIconEntry_Struct entries[0]; +}; + +struct ExpeditionInfo_Struct +{ +/*000*/ uint32 max_players; +/*004*/ char expedition_name[128]; +/*132*/ char leader_name[64]; +}; + +struct ExpeditionJoinPrompt_Struct +{ +/*000*/ char player_name[64]; +/*064*/ char expedition_name[64]; +}; + +struct ExpeditionExpireWarning +{ +/*008*/ uint32 minutes_remaining; +}; + +struct ExpeditionCompassEntry_Struct +{ +/*000*/ uint32 enabled; //guess +/*004*/ float y; +/*008*/ float x; +/*012*/ float z; +}; + +struct ExpeditionCompass_Struct +{ +/*000*/ uint32 count; +/*004*/ ExpeditionCompassEntry_Struct entries[0]; +}; + +struct ExpeditionMemberEntry_Struct +{ + char name[64]; + char status; +}; + +struct ExpeditionMemberList_Struct +{ +/*000*/ uint32 count; +/*004*/ ExpeditionMemberEntry_Struct entries[0]; +}; + +struct ExpeditionLockoutEntry_Struct +{ +/*000*/ uint32 time_left; +/*004*/ char expedition[128]; +/*132*/ char expedition_event[128]; +}; + +struct ExpeditionLockoutList_Struct +{ +/*000*/ uint32 count; +/*004*/ ExpeditionLockoutEntry_Struct entries[0]; +}; + +struct ExpeditionLeaderSet_Struct +{ +/*000*/ char leader_name[64]; +}; + +struct CorpseDrag_Struct +{ +/*000*/ char CorpseName[64]; +/*064*/ char DraggerName[64]; +/*128*/ uint8 Unknown128[24]; +/*152*/ +}; + +struct ChangeSize_Struct +{ +/*00*/ uint32 EntityID; +/*04*/ float Size; +/*08*/ uint32 Unknown08; // Observed 0 +/*12*/ float Unknown12; // Observed 1.0f +/*16*/ +}; + +// New OpCode/Struct for SoD+ +struct GroupMakeLeader_Struct +{ +/*000*/ uint32 Unknown000; +/*004*/ char CurrentLeader[64]; +/*068*/ char NewLeader[64]; +/*132*/ char Unknown072[324]; +/*456*/ +}; + +//One packet i didn't include here is the alt currency merchant window. +//it works much like the adventure merchant window +//it is formated like: dbstringid|1|dbstringid|count +//ex for a blank crowns window you would send: +//999999|1|999999|0 +//any items come after in much the same way adventure merchant items do except there is no theme included +#define ALT_CURRENCY_OP_POPULATE 8 +#define ALT_CURRENCY_OP_UPDATE 7 + +//Server -> Client +//Populates the initial Alternate Currency Window +struct AltCurrencyPopulateEntry_Struct +{ +/*000*/ uint32 currency_number; //corresponds to a dbstr id as well, the string matches what shows up in the "alternate currency" tab. +/*004*/ uint32 unknown00; //always 1 +/*008*/ uint32 currency_number2; //always same as currency number +/*012*/ uint32 item_id; //appears to be the item id +/*016*/ uint32 item_icon; //actual icon +/*020*/ uint32 stack_size; //most are set to 1000, the stack size for the item; should match db i think or there will be problems. +}; + +struct AltCurrencyPopulate_Struct { +/*000*/ uint32 opcode; //8 for populate +/*004*/ uint32 count; //number of entries +/*008*/ AltCurrencyPopulateEntry_Struct entries[0]; +}; + +//Server -> Client +//Updates the value of a specific Alternate Currency +struct AltCurrencyUpdate_Struct { +/*000*/ uint32 opcode; //7 for update +/*004*/ char name[64]; //name of client (who knows why just do it) +/*068*/ uint32 currency_number; //matches currency_number from populate entry +/*072*/ uint32 unknown072; //always 1 +/*076*/ uint32 amount; //new amount +/*080*/ uint32 unknown080; //seen 0 +/*084*/ uint32 unknown084; //seen 0 +}; + +//Client -> Server +//When an item is selected while the alt currency merchant window is open +struct AltCurrencySelectItem_Struct { +/*000*/ uint32 merchant_entity_id; +/*004*/ uint32 slot_id; +/*008*/ uint32 unknown008; +/*012*/ uint32 unknown012; +/*016*/ uint32 unknown016; +/*020*/ uint32 unknown020; +/*024*/ uint32 unknown024; +/*028*/ uint32 unknown028; +/*032*/ uint32 unknown032; +/*036*/ uint32 unknown036; +/*040*/ uint32 unknown040; +/*044*/ uint32 unknown044; +/*048*/ uint32 unknown048; +/*052*/ uint32 unknown052; +/*056*/ uint32 unknown056; +/*060*/ uint32 unknown060; +/*064*/ uint32 unknown064; +/*068*/ uint32 unknown068; +/*072*/ uint32 unknown072; +/*076*/ uint32 unknown076; +}; + +//Server -> Client +//As setup it makes it so that item can't be sold to the merchant. +//eg: "I will give you no doubloons for a cloth cap." +//Probably also sends amounts somewhere +struct AltCurrencySelectItemReply_Struct { +/*000*/ uint32 unknown000; +/*004*/ uint8 unknown004; //0xff +/*005*/ uint8 unknown005; //0xff +/*006*/ uint8 unknown006; //0xff +/*007*/ uint8 unknown007; //0xff +/*008*/ char item_name[64]; +/*072*/ uint32 unknown074; +/*076*/ uint32 cost; +/*080*/ uint32 unknown080; +/*084*/ uint32 unknown084; +}; + +//Client -> Server +//Requests purchase of a specific item from the vendor +struct AltCurrencyPurchaseItem_Struct { +/*000*/ uint32 merchant_entity_id; +/*004*/ uint32 item_id; +/*008*/ uint32 unknown008; //1 +}; + +//Client -> Server +//Reclaims / Create currency button pushed. +struct AltCurrencyReclaim_Struct { +/*000*/ uint32 currency_id; +/*004*/ uint32 unknown004; +/*008*/ uint32 count; +/*012*/ uint32 reclaim_flag; //1 = this is reclaim +}; + +struct AltCurrencySellItem_Struct { +/*000*/ uint32 merchant_entity_id; +/*004*/ uint32 slot_id; +/*008*/ uint32 charges; +/*012*/ uint32 cost; +}; + +struct Untargetable_Struct { +/*000*/ uint32 id; +/*004*/ uint32 targetable_flag; //0 = not targetable, 1 or higher = targetable +/*008*/ +}; + +struct CrystalReclaim_Struct { +/**/ uint32 type; +/**/ uint32 amount; +}; + +struct LFGuild_SearchPlayer_Struct +{ +/*00*/ uint32 Command; +/*04*/ uint32 Unknown04; +/*08*/ uint32 FromLevel; +/*12*/ uint32 ToLevel; +/*16*/ uint32 MinAA; +/*20*/ uint32 TimeZone; +/*24*/ uint32 Classes; +}; + +struct LFGuild_SearchGuild_Struct +{ +/*00*/ uint32 Command; +/*04*/ uint32 Unknown04; +/*08*/ uint32 Level; +/*12*/ uint32 AAPoints; +/*16*/ uint32 TimeZone; +/*20*/ uint32 Class; +/*24*/ +}; + +struct LFGuild_PlayerToggle_Struct +{ +/*000*/ uint32 Command; +/*004*/ uint8 Unknown004[68]; +/*072*/ char Comment[256]; +/*328*/ uint8 Unknown328[268]; +/*596*/ uint32 TimeZone; +/*600*/ uint8 Toggle; +/*601*/ uint8 Unknown601[7]; +/*608*/ uint32 TimePosted; +/*612*/ uint8 Unknown612[12]; +/*624*/ +}; + +struct LFGuild_GuildToggle_Struct +{ +/*000*/ uint32 Command; +/*004*/ uint8 Unknown004[8]; +/*012*/ char Comment[256]; +/*268*/ uint8 Unknown268[256]; +/*524*/ uint32 FromLevel; +/*528*/ uint32 ToLevel; +/*532*/ uint32 Classes; +/*536*/ uint32 AACount; +/*540*/ uint32 TimeZone; +/*544*/ uint8 Toggle; +/*545*/ uint8 Unknown545[3]; +/*548*/ uint32 TimePosted; +/*552*/ char Name[64]; +/*616*/ +}; + +struct MaxCharacters_Struct +{ +/*000*/ uint32 max_chars; // Seen 4 on Silver Account (4 characters max) +/*004*/ uint32 unknown004; // Seen 0 +/*008*/ uint32 unknown008; // Seen 0 +}; + +struct Membership_Struct +{ +/*000*/ uint32 membership; // Seen 2 on Gold Account +/*004*/ uint32 races; // Seen ff ff 01 00 +/*008*/ uint32 classes; // Seen ff ff 01 01 +/*012*/ uint32 entrysize; // Seen 15 00 00 00 +/*016*/ int32 entries[21]; //Varies. Seen ff ff ff ff, and 01 00 00 00 +/*104*/ uint32 exit_url_length; // Length of the exit_url string (0 for none) +/*108*/ // char exit_url[0]; // URL that will open when EQ is exited +}; +// Used by MercenaryListEntry_Struct +struct MercenaryStance_Struct { +/*0000*/ uint32 StanceIndex; // Index of this stance (sometimes reverse reverse order - 3, 2, 1, 0 for 4 stances etc) +/*0004*/ uint32 Stance; // From dbstr_us.txt - 1^24^Passive^0, 2^24^Balanced^0, etc (1 to 9 as of April 2012) +}; + +struct Membership_Entry_Struct +{ +/*000*/ uint32 purchase_id; // Seen 1, then increments 90287 to 90300 +/*004*/ uint32 bitwise_entry; // Seen 16 to 65536 - Skips 4096 +/*008*/ +}; + +struct Membership_Setting_Struct +{ +/*000*/ uint32 setting_index; // 0, 1, or 2 +/*004*/ uint32 setting_id; // 0 to 21 +/*008*/ int32 setting_value; // All can be 0, 1, or -1 +/*012*/ +}; + +struct Membership_Details_Struct +{ +/*0000*/ uint32 membership_setting_count; // Seen 66 +/*0016*/ Membership_Setting_Struct settings[66]; +/*0012*/ uint32 race_entry_count; // Seen 15 +/*1044*/ Membership_Entry_Struct membership_races[15]; +/*0012*/ uint32 class_entry_count; // Seen 15 +/*1044*/ Membership_Entry_Struct membership_classes[15]; +/*1044*/ uint32 exit_url_length; // Length of the exit_url string (0 for none) +/*1048*/ //char exit_url[42]; // Upgrade to Silver or Gold Membership URL +/*1048*/ uint32 exit_url_length2; // Length of the exit_url2 string (0 for none) +/*0000*/ //char exit_url2[49]; // Upgrade to Gold Membership URL +}; + +struct ItemPreview_Struct +{ +/*000*/ uint32 itemid; +/*004*/ uint32 unknown004[3]; +/*016*/ uint32 slot; +/*020*/ uint32 unknown020; +/*024*/ uint16 slot2; +/*026*/ uint8 unknown026[54]; +}; + +//Not an EQ packet, just a single int for the mercenary merchant structure. +struct MercenaryGrade_Struct { +uint32 GradeCountEntry; +}; + +// Used by MercenaryMerchantList_Struct +struct MercenaryListEntry_Struct { +/*0000*/ uint32 MercID; // ID unique to each type of mercenary (probably a DB id) +/*0004*/ uint32 MercType; // From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) +/*0008*/ uint32 MercSubType; // From dbstr_us.txt - 330020105^23^Race: Guktan
Type: Healer
Confidence: High
Proficiency: Apprentice, Tier V... +/*0012*/ uint32 PurchaseCost; // Purchase Cost (in gold) +/*0016*/ uint32 UpkeepCost; // Upkeep Cost (in gold) +/*0020*/ uint32 Status; // Required Account Status (Free = 0, Silver = 1, Gold = 2) at merchants - Seen 0 (suspended) or 1 (unsuspended) on hired mercs ? +/*0024*/ uint32 AltCurrencyCost; // Alternate Currency Purchase Cost? (all seen costs show N/A Bayle Mark) - Seen 0 +/*0028*/ uint32 AltCurrencyUpkeep; // Alternate Currency Upkeep Cost? (all seen costs show 1 Bayle Mark) - Seen 1 +/*0032*/ uint32 AltCurrencyType; // Alternate Currency Type? - 19^17^Bayle Mark^0 - Seen 19 +/*0036*/ uint8 MercUnk01; // Unknown (always see 0) +/*0037*/ int32 TimeLeft; // Unknown (always see -1 at merchant) - Seen 900000 (15 minutes in ms for newly hired merc) +/*0041*/ uint32 MerchantSlot; // Merchant Slot? Increments, but not always by 1 - May be for Merc Window Options (Seen 5, 36, 1 for active mercs)? +/*0045*/ uint32 MercUnk02; // Unknown (normally see 1, but sometimes 2 or 0) +/*0049*/ uint32 StanceCount; // Iterations of MercenaryStance_Struct - Normally 2 to 4 seen +/*0053*/ int32 MercUnk03; // Unknown (always 0 at merchant) - Seen on active merc: 93 a4 03 77, b8 ed 2f 26, 88 d5 8b c3, and 93 a4 ad 77 +/*0057*/ uint8 MercUnk04; // Seen 1 +/*0058*/ char MercName[1]; // Null Terminated Mercenary Name (00 at merchants) +/*0000*/ MercenaryStance_Struct* Stances; // Count Varies, but hard set to 5 max for now - From dbstr_us.txt - 1^24^Passive^0, 2^24^Balanced^0, etc (1 to 9 as of April 2012) +}; + +// [OPCode: 0x27ac OP_MercenaryDataResponse] On Live as of April 2 2012 [Server->Client] +// Opcode should be renamed to something like OP_MercenaryMerchantShopResponse since the Data Response packet is different +// Sent by the server when browsing the Mercenary Merchant +struct MercenaryMerchantList_Struct { +/*0000*/ uint32 MercTypeCount; // Number of Merc Types to follow +/*0004*/ MercenaryGrade_Struct* MercGrades; // Count varies, but hard set to 3 max for now - From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) +/*0016*/ uint32 MercCount; // Number of MercenaryInfo_Struct to follow +/*0020*/ MercenaryListEntry_Struct* Mercs; // Data for individual mercenaries in the Merchant List +}; + +// [OPCode: 0x4dd9 OP_MercenaryDataRequest] On Live as of April 2 2012 [Client->Server] +// Opcode should be renamed to something like OP_MercenaryMerchantShopRequest since the Data Request packet is different +// Right clicking merchant - shop request +struct MercenaryMerchantShopRequest_Struct { +/*0000*/ uint32 MercMerchantID; // Entity ID of the Mercenary Merchant +/*0004*/ +}; + +// Used by MercenaryDataUpdate_Struct +struct MercenaryData_Struct { +/*0000*/ uint32 MercID; // ID unique to each type of mercenary (probably a DB id) - (if 1, do not send MercenaryData_Struct - No merc hired) +/*0004*/ uint32 MercType; // From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) +/*0008*/ uint32 MercSubType; // From dbstr_us.txt - 330020105^23^Race: Guktan
Type: Healer
Confidence: High
Proficiency: Apprentice, Tier V... +/*0012*/ uint32 PurchaseCost; // Purchase Cost (in gold) +/*0016*/ uint32 UpkeepCost; // Upkeep Cost (in gold) +/*0020*/ uint32 Status; // Required Account Status (Free = 0, Silver = 1, Gold = 2) at merchants - Seen 0 (suspended) or 1 (unsuspended) on hired mercs ? +/*0024*/ uint32 AltCurrencyCost; // Alternate Currency Purchase Cost? (all seen costs show N/A Bayle Mark) - Seen 0 +/*0028*/ uint32 AltCurrencyUpkeep; // Alternate Currency Upkeep Cost? (all seen costs show 1 Bayle Mark) - Seen 1 +/*0032*/ uint32 AltCurrencyType; // Alternate Currency Type? - 19^17^Bayle Mark^0 - Seen 19 +/*0036*/ uint8 MercUnk01; // Unknown (always see 0) +/*0037*/ int32 TimeLeft; // Unknown (always see -1 at merchant) - Seen 900000 (15 minutes in ms for newly hired merc) +/*0041*/ uint32 MerchantSlot; // Merchant Slot? Increments, but not always by 1 - May be for Merc Window Options (Seen 5, 36, 1 for active mercs)? +/*0045*/ uint32 MercUnk02; // Unknown (normally see 1, but sometimes 2 or 0) +/*0049*/ uint32 StanceCount; // Iterations of MercenaryStance_Struct - Normally 2 to 4 seen +/*0053*/ int32 MercUnk03; // Unknown (always 0 at merchant) - Seen on active merc: 93 a4 03 77, b8 ed 2f 26, 88 d5 8b c3, and 93 a4 ad 77 +/*0057*/ uint8 MercUnk04; // Seen 1 +/*0058*/ char MercName[64]; // Null Terminated Mercenary Name (00 at merchants) +/*0000*/ MercenaryStance_Struct* Stances; // Count Varies, but hard set to 2 for now - From dbstr_us.txt - 1^24^Passive^0, 2^24^Balanced^0, etc (1 to 9 as of April 2012) +/*0000*/ uint32 MercUnk05; // Seen 1 - Extra Merc Data field that differs from MercenaryListEntry_Struct +// MercUnk05 may be a field that is at the end of the packet only, even if multiple mercs are listed (haven't seen examples of multiple mercs owned at once) +}; + +// [OPCode: 0x6537] On Live as of April 2 2012 [Server->Client] +// Should be named OP_MercenaryDataResponse, but the current opcode using that name should be renamed first +// Size varies if mercenary is hired or if browsing Mercenary Merchant +// This may also be the response for Client->Server 0x0327 (size 0) packet On Live as of April 2 2012 +struct MercenaryDataUpdate_Struct { +/*0000*/ int32 MercStatus; // Seen 0 with merc and -1 with no merc hired +/*0004*/ uint32 MercCount; // Seen 1 with 1 merc hired and 0 with no merc hired +/*0008*/ MercenaryData_Struct* MercData; // Data for individual mercenaries in the Merchant List +}; + +// [OPCode: 0x6537] On Live as of April 2 2012 [Server->Client] +// Size 12 and sent on Zone-In if no mercenary is currently hired and when merc is dismissed +// (Same packet as MercAssign_Struct?) +struct NoMercenaryHired_Struct { +/*0000*/ int32 MercStatus; // Seen -1 with no merc hired +/*0004*/ uint32 MercCount; // Seen 0 with no merc hired +/*0008*/ uint32 MercID; // Seen 1 when no merc is hired - ID unique to each type of mercenary +/*0012*/ +}; + +// OP_MercenaryAssign (Same packet as NoMercenaryHired_Struct?) +struct MercenaryAssign_Struct { +/*0000*/ uint32 MercEntityID; // Seen 0 (no merc spawned) or 615843841 and 22779137 +/*0004*/ uint32 MercUnk01; // +/*0008*/ uint32 MercUnk02; // +/*0012*/ +}; + +// [OPCode: 0x495d OP_MercenaryTimer] On Live as of April 2 2012 [Server->Client] [Size: 20] +// Sent on Zone-In, or after Dismissing, Suspending, or Unsuspending Mercs +struct MercenaryStatus_Struct { +/*0000*/ uint32 MercEntityID; // Seen 0 (no merc spawned) or 615843841 and 22779137 +/*0004*/ uint32 UpdateInterval; // Seen 900000 - Matches from 0x6537 packet (15 minutes in ms?) +/*0008*/ uint32 MercUnk01; // Seen 180000 - 3 minutes in milleseconds? Maybe next update interval? +/*0012*/ uint32 MercState; // Seen 5 (normal) or 1 (suspended) +/*0016*/ uint32 SuspendedTime; // Seen 0 (not suspended) or c9 c2 64 4f (suspended on Sat Mar 17 11:58:49 2012) - Unix Timestamp +/*0020*/ +}; + +// [OPCode: 0x4c6c] On Live as of April 2 2012 [Client->Server] [Size: 8] +// Sent from the client when using the Mercenary Window +struct MercenaryCommand_Struct { +/*0000*/ uint32 MercCommand; // Seen 0 (zone in with no merc or suspended), 1 (dismiss merc), 5 (normal state), 36 (zone in with merc) +/*0004*/ int32 Option; // Seen -1 (zone in with no merc), 0 (setting to passive stance), 1 (normal or setting to balanced stance) +/*0008*/ +}; + +// [OPCode: 0x1a79] On Live as of April 2 2012 [Client->Server] [Size: 1] +// Requesting to suspend or unsuspend merc +struct SuspendMercenary_Struct { +/*0000*/ uint8 SuspendMerc; // Seen 30 (48) for suspending or unsuspending +/*0001*/ +}; + +// [OPCode: 0x2528] On Live as of April 2 2012 [Server->Client] [Size: 4] +// Response to suspend merc with timestamp +struct SuspendMercenaryResponse_Struct { +/*0000*/ uint32 SuspendTime; // Unix Timestamp - Seen a9 11 78 4f +/*0004*/ +}; + +// [OPCode: 0x5e78 (OP_MercenaryHire?)] On Live as of April 2 2012 +// Sent by client when requesting to view Mercenary info or Hire a Mercenary +struct MercenaryMerchantRequest_Struct { +/*0000*/ uint32 MercID; // Seen 399 and 400 for merc ID +/*0004*/ uint32 MercUnk01; // Seen 1 +/*0008*/ uint32 MercMerchantID; // Entity ID for Mercenary Merchant +/*0012*/ uint32 MercUnk02; // Seen 65302016 (00 6e e4 03) - (probably actually individual uint8 fields), but seen as DWORD in Seeds client. +/*0016*/ +}; + +// [OPCode: 0x5e78 (OP_MercenaryHire?)] On Live as of April 2 2012 +/* +Valid response IDs: + +0 - Hire me! (Assign Merc after sending this.) +1 - Insufficient money message. +2 - Mercenary-To-Hire does not exist in the server's DB. +3 - Mercenary failed to spawn. (this actually tells us the mercenary should spawn BEFORE recieving this packet.) +4 - Mercenaries not allowed in raids. +5 - You already have a mercenary request pending +6 - You must dismiss the mercenary before hiring a new one. +7 - You must dismiss your suspended one before hiring a new one. +8 - Group is full. +9 - Error creating mercenary +10 - Replacing mercenary(?!) +11 - Your mercenary has quit! You ran out of money to pay for your mercenary! +12 - Your mercenary waived an upkeep cost of %d plat, and %d gold and your mercenary upkeep cost timer has been reset to %s. <-- these values are for GM resets of mercenaries and are generated from the client's +mercenary info. NOT from the packet. +13 - Your mercenary is about to be quit due to insufficient funds! <--- Sent before the mercenary quits, unsure of time sent before. +14 - There is no mercenary liason nearby! <-- hacking attempt check if no mercenary merchant is in the zone! +15 - You are too far away from the liason! <-- Liason exists as type in the zone, but client is too far away. (position update happened) +16 - You do not meet the requirements for that mercenary! <-- For example, if a mercenary is 'unlocked' in some way, send this if they do not have the mercenary unlock. +*/ +// Sent by Server in response to requesting to view Mercenary info or Hire a Mercenary +struct MercenaryMerchantResponse_Struct { +/*0000*/ uint32 ResponseType; +/*0004*/ +}; + +// Restore structure packing to default +#pragma pack() + +#endif diff --git a/common/eqtime.cpp b/common/eqtime.cpp new file mode 100644 index 000000000..248948aa6 --- /dev/null +++ b/common/eqtime.cpp @@ -0,0 +1,280 @@ +/* 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 +#include "../common/debug.h" +#include "../common/eqtime.h" +#include "../common/eq_packet_structs.h" +#include +#include +using namespace std; + /*#ifdef _CRTDBG_MAP_ALLOC + #undef new + #endif*/ + /*#ifdef _CRTDBG_MAP_ALLOC + #define new new(_NORMAL_BLOCK, __FILE__, __LINE__) + #endif*/ + +#define EQT_VERSION 1000 + +//Constructor +//Input: Starting EQ Time, Starting Real Time. +EQTime::EQTime(TimeOfDay_Struct start_eq, time_t start_real) +{ + eqTime.start_eqtime=start_eq; + eqTime.start_realtime=start_real; + timezone=0; +} + +EQTime::EQTime() +{ + timezone=0; + memset(&eqTime, 0, sizeof(eqTime)); + //Defaults for time + TimeOfDay_Struct start; + start.day=1; + start.hour=9; + start.minute=0; + start.month=1; + start.year=3100; + //Set default time zone + timezone=0; + //Start EQTimer + setEQTimeOfDay(start, time(0)); +} + +EQTime::~EQTime() +{ +} + +//getEQTimeOfDay - Reads timeConvert and writes the result to eqTimeOfDay +//This function was written by the ShowEQ Project. +//Input: Current Time (as a time_t), a pointer to the TimeOfDay_Struct that will be written to. +//Output: 0=Error, 1=Sucess + +int EQTime::getEQTimeOfDay( time_t timeConvert, struct TimeOfDay_Struct *eqTimeOfDay ) +{ + /* check to see if we have a reference time to go by. */ + if( eqTime.start_realtime == 0 ) + return 0; + + unsigned long diff = timeConvert - eqTime.start_realtime; + + /* There are 3 seconds per 1 EQ Minute */ + diff /= 3; + + /* Start off timezone offset */ + + int32 ntz = timezone; + + /* The minutes range from 0 - 59 */ + diff += eqTime.start_eqtime.minute + (ntz%60); + eqTimeOfDay->minute = diff % 60; + diff /= 60; + ntz /= 60; + + // The hours range from 1-24 + // 1 = 1am + // 2 = 2am + // ... + // 23 = 11 pm + // 24 = 12 am + // + // Modify it so that it works from + // 0-23 for our calculations + diff += ( eqTime.start_eqtime.hour - 1) + (ntz%24); + eqTimeOfDay->hour = (diff%24) + 1; + diff /= 24; + ntz /= 24; + + // The days range from 1-28 + // Modify it so that it works from + // 0-27 for our calculations + diff += ( eqTime.start_eqtime.day - 1 ) + (ntz%28); + eqTimeOfDay->day = (diff%28) + 1; + diff /= 28; + ntz /= 28; + + // The months range from 1-12 + // Modify it so that it works from + // 0-11 for our calculations + diff += ( eqTime.start_eqtime.month - 1 ) + (ntz%12); + eqTimeOfDay->month = (diff%12) + 1; + diff /= 12; + ntz /= 12; + + eqTimeOfDay->year = eqTime.start_eqtime.year + diff + ntz; + + return 1; +} + +//setEQTimeOfDay +int EQTime::setEQTimeOfDay(TimeOfDay_Struct start_eq, time_t start_real) +{ + if(start_real==0) + return 0; + eqTime.start_eqtime=start_eq; + eqTime.start_realtime=start_real; + return 1; +} + +//saveFile and loadFile need to use long for the save datatype... +//For some reason, ifstream/ofstream have problems with EQEmu datatypes in files. +bool EQTime::saveFile(const char *filename) +{ + ofstream of; + of.open(filename); + if(!of) + { + LogFile->write(EQEMuLog::Error, "EQTime::saveFile failed: Unable to open file '%s'", filename); + return false; + } + //Enable for debugging + //cout << "SAVE: day=" << (long)eqTime.start_eqtime.day << ";hour=" << (long)eqTime.start_eqtime.hour << ";min=" << (long)eqTime.start_eqtime.minute << ";mon=" << (long)eqTime.start_eqtime.month << ";yr=" << eqTime.start_eqtime.year << ";timet=" << eqTime.start_realtime << endl; + of << EQT_VERSION << endl; + of << (long)eqTime.start_eqtime.day << endl; + of << (long)eqTime.start_eqtime.hour << endl; + of << (long)eqTime.start_eqtime.minute << endl; + of << (long)eqTime.start_eqtime.month << endl; + of << eqTime.start_eqtime.year << endl; + of << eqTime.start_realtime << endl; + of.close(); + return true; +} + +bool EQTime::loadFile(const char *filename) +{ + int version=0; + long in_data=0; + ifstream in; + in.open(filename); + if(!in) + { + LogFile->write(EQEMuLog::Error, "Could not load EQTime file %s", filename); + return false; + } + in >> version; + in.ignore(80, '\n'); + if(version != EQT_VERSION) + { + LogFile->write(EQEMuLog::Error, "'%s' is NOT a valid EQTime file. File version is %i, EQTime version is %i", filename, version, EQT_VERSION); + return false; + } + //in >> eqTime.start_eqtime.day; + in >> in_data; + in.ignore(80, '\n'); + eqTime.start_eqtime.day = in_data; + //in >> eqTime.start_eqtime.hour; + in >> in_data; + eqTime.start_eqtime.hour = in_data; + in.ignore(80, '\n'); + //in >> eqTime.start_eqtime.minute; + in >> in_data; + in.ignore(80, '\n'); + eqTime.start_eqtime.minute = in_data; + //in >> eqTime.start_eqtime.month; + in >> in_data; + in.ignore(80, '\n'); + eqTime.start_eqtime.month = in_data; + in >> eqTime.start_eqtime.year; + in.ignore(80, '\n'); + in >> eqTime.start_realtime; + //Enable for debugging... + //cout << "LOAD: day=" << (long)eqTime.start_eqtime.day << ";hour=" << (long)eqTime.start_eqtime.hour << ";min=" << (long)eqTime.start_eqtime.minute << ";mon=" << (long)eqTime.start_eqtime.month << ";yr=" << eqTime.start_eqtime.year << ";timet=" << eqTime.start_realtime << endl; + in.close(); + return true; +} + + +bool EQTime::IsTimeBefore(TimeOfDay_Struct *base, TimeOfDay_Struct *test) { + if(base->year > test->year) + return(true); + if(base->year < test->year) + return(false); + //same years + if(base->month > test->month) + return(true); + if(base->month < test->month) + return(false); + //same month + if(base->day > test->day) + return(true); + if(base->day < test->day) + return(false); + //same day + if(base->hour > test->hour) + return(true); + if(base->hour < test->hour) + return(false); + //same hour... + return(base->minute > test->minute); +} + + +void EQTime::AddMinutes(uint32 minutes, TimeOfDay_Struct *to) { + uint32 cur; + + //minutes start at 0, everything else starts at 1 + cur = to->minute; + cur += minutes; + if(cur < 60) { + to->minute = cur; + return; + } + to->minute = cur % 60; + //carry hours + cur /= 60; + cur += to->hour; + if(cur <= 24) { + to->hour = cur; + return; + } + to->hour = ((cur-1) % 24) + 1; + //carry days + cur = (cur-1) / 24; + cur += to->day; + if(cur <= 28) { + to->day = cur; + return; + } + to->day = ((cur-1) % 28) + 1; + //carry months + cur = (cur-1) / 28; + cur += to->month; + if(cur <= 12) { + to->month = cur; + return; + } + to->month = ((cur-1) % 12) + 1; + //carry years + to->year += (cur-1) / 12; +} + +void EQTime::ToString(TimeOfDay_Struct *t, string &str) { + char buf[128]; + snprintf(buf, 128, "%.2d/%.2d/%.4d %.2d:%.2d", + t->month, t->day, t->year, t->hour, t->minute); + buf[127] = '\0'; + str = buf; +} + + + + + + diff --git a/common/eqtime.h b/common/eqtime.h new file mode 100644 index 000000000..d0dcbcfa3 --- /dev/null +++ b/common/eqtime.h @@ -0,0 +1,57 @@ +#ifndef EQTIME_H +#define EQTIME_H + +#include "../common/eq_packet_structs.h" +#include + +using namespace std; + +//Struct +struct eqTimeOfDay +{ + TimeOfDay_Struct start_eqtime; + time_t start_realtime; +}; + +//Class Def +class EQTime +{ +public: + //Constructor/destructor + EQTime(TimeOfDay_Struct start_eq, time_t start_real); + EQTime(); + ~EQTime(); + + //Get functions + int getEQTimeOfDay( TimeOfDay_Struct *eqTimeOfDay ) { return(getEQTimeOfDay(time(NULL), eqTimeOfDay)); } + int getEQTimeOfDay( time_t timeConvert, TimeOfDay_Struct *eqTimeOfDay ); + TimeOfDay_Struct getStartEQTime() { return eqTime.start_eqtime; } + time_t getStartRealTime() { return eqTime.start_realtime; } + uint32 getEQTimeZone() { return timezone; } + uint32 getEQTimeZoneHr() { return timezone/60; } + uint32 getEQTimeZoneMin() { return timezone%60; } + + //Set functions + int setEQTimeOfDay(TimeOfDay_Struct start_eq, time_t start_real); + void setEQTimeZone(int32 in_timezone) { timezone=in_timezone; } + + //Time math/logic functions + static bool IsTimeBefore(TimeOfDay_Struct *base, TimeOfDay_Struct *test); //is test before base + static void AddMinutes(uint32 minutes, TimeOfDay_Struct *to); + + static void ToString(TimeOfDay_Struct *t, string &str); + + //Database functions + //bool loadDB(Database q); + //bool setDB(Database q); + bool loadFile(const char *filename); + bool saveFile(const char *filename); + +private: + //This is our reference clock. + eqTimeOfDay eqTime; + //This is our tz offset + int32 timezone; +}; + +#endif diff --git a/common/errmsg.h b/common/errmsg.h new file mode 100644 index 000000000..8087c5269 --- /dev/null +++ b/common/errmsg.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA */ + +/* Error messages for mysql clients */ +/* error messages for the demon is in share/language/errmsg.sys */ + +#ifdef __cplusplus +extern "C" { +#endif +void init_client_errs(void); +extern const char *client_errors[]; /* Error messages */ +#ifdef __cplusplus +} +#endif + +#define CR_MIN_ERROR 2000 /* For easier client code */ +#define CR_MAX_ERROR 2999 +#if defined(OS2) && defined( MYSQL_SERVER) +#define CER(X) client_errors[(X)-CR_MIN_ERROR] +#else +#define ER(X) client_errors[(X)-CR_MIN_ERROR] +#endif +#define CLIENT_ERRMAP 2 /* Errormap used by my_error() */ + +#define CR_UNKNOWN_ERROR 2000 +#define CR_SOCKET_CREATE_ERROR 2001 +#define CR_CONNECTION_ERROR 2002 +#define CR_CONN_HOST_ERROR 2003 +#define CR_IPSOCK_ERROR 2004 +#define CR_UNKNOWN_HOST 2005 +#define CR_SERVER_GONE_ERROR 2006 +#define CR_VERSION_ERROR 2007 +#define CR_OUT_OF_MEMORY 2008 +#define CR_WRONG_HOST_INFO 2009 +#define CR_LOCALHOST_CONNECTION 2010 +#define CR_TCP_CONNECTION 2011 +#define CR_SERVER_HANDSHAKE_ERR 2012 +#define CR_SERVER_LOST 2013 +#define CR_COMMANDS_OUT_OF_SYNC 2014 +#define CR_NAMEDPIPE_CONNECTION 2015 +#define CR_NAMEDPIPEWAIT_ERROR 2016 +#define CR_NAMEDPIPEOPEN_ERROR 2017 +#define CR_NAMEDPIPESETSTATE_ERROR 2018 +#define CR_CANT_READ_CHARSET 2019 +#define CR_NET_PACKET_TOO_LARGE 2020 diff --git a/common/extprofile.cpp b/common/extprofile.cpp new file mode 100644 index 000000000..0445eccdd --- /dev/null +++ b/common/extprofile.cpp @@ -0,0 +1,57 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "debug.h" +#include "extprofile.h" + +//Set defaults in the extended profile... +void InitExtendedProfile(ExtendedProfile_Struct *p) { + memset(p, 0, sizeof(ExtendedProfile_Struct)); + //set any special values here... +} + +bool SetExtendedProfile(ExtendedProfile_Struct *to, char *old, unsigned int len) { + if(len == 0 || old == NULL) { + //handle old chars without an extended profile... + InitExtendedProfile(to); + return(true); + } + if(len == sizeof(ExtendedProfile_Struct)) { + memcpy(to, old, sizeof(ExtendedProfile_Struct)); + return(true); + } + + //convert an old block of memory of size old to the new struct + //and store the new results in 'to' + + //generic converter that will work as long as the structre + //only grows, and nothign gets re-arranged + if(len < sizeof(ExtendedProfile_Struct)) { + InitExtendedProfile(to); + memcpy(to, old, len); + return(true); + } + + return(false); +} + + + + + + diff --git a/common/extprofile.h b/common/extprofile.h new file mode 100644 index 000000000..adbf74bc1 --- /dev/null +++ b/common/extprofile.h @@ -0,0 +1,66 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 EXTENDED_PROFILE_H +#define EXTENDED_PROFILE_H + +#include "eq_packet_structs.h" +#include "Item.h" + + +#pragma pack(1) + +/* + This is a place to store data that dosent have a home in the + official player profile. + + Try not to expand or re-arrange thing in this struct + to make conversion between sizes easier. + if something gets removed from here, just turn it into an unused + item, so conversion is not needed. Then reuse old unused values if + possible later instead of adding more, so long as old values left + over would not be extrememly unpleasent side effects. +*/ +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[MAX_MATERIALS]; + char merc_name[64]; + + 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; +}; + +#pragma pack() + +void InitExtendedProfile(ExtendedProfile_Struct *p); +bool SetExtendedProfile(ExtendedProfile_Struct *to, char *old, unsigned int len); + + +#endif diff --git a/common/guild_base.cpp b/common/guild_base.cpp new file mode 100644 index 000000000..a163c6631 --- /dev/null +++ b/common/guild_base.cpp @@ -0,0 +1,1567 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "debug.h" +#include "guild_base.h" +#include "database.h" +#include "logsys.h" +#include "MiscFunctions.h" +#include +#include + +//until we move MAX_NUMBER_GUILDS +#include "eq_packet_structs.h" + +const char *const BaseGuildManager::GuildActionNames[_MaxGuildAction] = +{ "HearGuildChat", "SpeakGuildChat", "Invite", "Remove", "Promote", "Demote", "Set_MOTD", "War/Peace" }; + +BaseGuildManager::BaseGuildManager() +: m_db(NULL) +{ +} + +BaseGuildManager::~BaseGuildManager() { + ClearGuilds(); +} + +bool BaseGuildManager::LoadGuilds() { + + ClearGuilds(); + + if(m_db == NULL) { + _log(GUILDS__DB, "Requested to load guilds when we have no database object."); + return(false); + } + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + 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); + + //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); + } + safe_delete_array(query); + while ((row = mysql_fetch_row(result))) { + 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; + } + + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) { + _log(GUILDS__ERROR, "Found rank %d for non-existent guild %d, skipping.", rankn, guild_id); + continue; + } + + RankInfo &rank = res->second->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; + } + mysql_free_result(result); + + return(true); +} + +bool BaseGuildManager::RefreshGuild(uint32 guild_id) { + if(m_db == NULL) { + _log(GUILDS__DB, "Requested to refresh guild %d when we have no database object.", guild_id); + return(false); + } + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + 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); + } + 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 { + _log(GUILDS__ERROR, "Unable to find guild %d in the database.", guild_id); + 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); + } + safe_delete_array(query); + + while((row = mysql_fetch_row(result))) { + 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; + } + mysql_free_result(result); + + _log(GUILDS__DB, "Successfully refreshed guild %d from the database.", guild_id); + + 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) +{ + map::iterator res; + + //remove any old entry. + res = m_guilds.find(guild_id); + if(res != m_guilds.end()) { + delete res->second; + m_guilds.erase(res); + } + + //make the new entry and store it into the map. + GuildInfo *info = new GuildInfo; + info->name = guild_name; + info->leader_char_id = leader_char_id; + info->minstatus = minstatus; + info->motd = guild_motd; + info->motd_setter = motd_setter; + info->url = URL; + info->channel = Channel; + m_guilds[guild_id] = info; + + //now setup default ranks (everything defaults to false) + info->ranks[0].name = "Member"; + info->ranks[0].permissions[GUILD_HEAR] = true; + info->ranks[0].permissions[GUILD_SPEAK] = true; + info->ranks[1].name = "Officer"; + info->ranks[1].permissions[GUILD_HEAR] = true; + info->ranks[1].permissions[GUILD_SPEAK] = true; + info->ranks[1].permissions[GUILD_INVITE] = true; + info->ranks[1].permissions[GUILD_REMOVE] = true; + info->ranks[1].permissions[GUILD_MOTD] = true; + info->ranks[2].name = "Leader"; + info->ranks[2].permissions[GUILD_HEAR] = true; + info->ranks[2].permissions[GUILD_SPEAK] = true; + info->ranks[2].permissions[GUILD_INVITE] = true; + info->ranks[2].permissions[GUILD_REMOVE] = true; + info->ranks[2].permissions[GUILD_PROMOTE] = true; + info->ranks[2].permissions[GUILD_DEMOTE] = true; + info->ranks[2].permissions[GUILD_MOTD] = true; + info->ranks[2].permissions[GUILD_WARPEACE] = true; + + return(info); +} + +bool BaseGuildManager::_StoreGuildDB(uint32 guild_id) { + if(m_db == NULL) { + _log(GUILDS__DB, "Requested to store guild %d when we have no database object.", guild_id); + return(false); + } + + map::const_iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) { + _log(GUILDS__DB, "Requested to store non-existent guild %d", guild_id); + return(false); + } + GuildInfo *info = res->second; + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + //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); + + //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); + + //escape our strings. + char *name_esc = new char[info->name.length()*2+1]; + char *motd_esc = new char[info->motd.length()*2+1]; + char *motd_set_esc = new char[info->motd_setter.length()*2+1]; + m_db->DoEscapeString(name_esc, info->name.c_str(), info->name.length()); + m_db->DoEscapeString(motd_esc, info->motd.c_str(), info->motd.length()); + 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)) + { + _log(GUILDS__ERROR, "Error inserting new guild record when storing %d. Giving up. '%s': %s", guild_id, query, errbuf); + safe_delete_array(query); + safe_delete_array(name_esc); + safe_delete_array(motd_esc); + safe_delete_array(motd_set_esc); + return(false); + } + safe_delete_array(query); + safe_delete_array(name_esc); + safe_delete_array(motd_esc); + safe_delete_array(motd_set_esc); + + //now insert the new ranks + uint8 rank; + for(rank = 0; rank <= GUILD_MAX_RANK; rank++) { + const RankInfo &r = 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()); + + 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)" + " 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)) + { + _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); + safe_delete_array(title_esc); + return(false); + } + safe_delete_array(query); + safe_delete_array(title_esc); + } + + _log(GUILDS__DB, "Stored guild %d in the database", guild_id); + + return(true); +} + +uint32 BaseGuildManager::_GetFreeGuildID() { + if(m_db == NULL) { + _log(GUILDS__DB, "Requested find a free guild ID when we have no database object."); + return(GUILD_NONE); + } + + char errbuf[MYSQL_ERRMSG_SIZE]; + char query[100]; + MYSQL_RES *result; + + //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. + + uint16 x; + for (x = 1; x < MAX_NUMBER_GUILDS; x++) { + snprintf(query, 100, "SELECT id FROM guilds where id=%i;", x); + + 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); + } + else { + LogFile->write(EQEMuLog::Error, "Error in _GetFreeGuildID query '%s': %s", query, errbuf); + } + } + + _log(GUILDS__ERROR, "Unable to find a free guild ID when requested."); + return GUILD_NONE; +} + + + +uint32 BaseGuildManager::CreateGuild(const char* name, uint32 leader_char_id) { + uint32 gid = DBCreateGuild(name, leader_char_id); + if(gid == GUILD_NONE) + return(GUILD_NONE); + + SendGuildRefresh(gid, true, false, false, false); + SendCharRefresh(GUILD_NONE, gid, leader_char_id); + + return(gid); +} + +bool BaseGuildManager::DeleteGuild(uint32 guild_id) { + if(!DBDeleteGuild(guild_id)) + return(false); + + SendGuildDelete(guild_id); + + return(true); +} + +bool BaseGuildManager::RenameGuild(uint32 guild_id, const char* name) { + if(!DBRenameGuild(guild_id, name)) + return(false); + + SendGuildRefresh(guild_id, true, false, false, false); + + return(true); +} + +bool BaseGuildManager::SetGuildLeader(uint32 guild_id, uint32 leader_char_id) { + //get old leader first. + map::const_iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) + return(false); + GuildInfo *info = res->second; + uint32 old_leader = info->leader_char_id; + + if(!DBSetGuildLeader(guild_id, leader_char_id)) + return(false); + + SendGuildRefresh(guild_id, false, false, false, false); + SendCharRefresh(GUILD_NONE, guild_id, old_leader); + SendCharRefresh(GUILD_NONE, guild_id, leader_char_id); + + return(true); +} + +bool BaseGuildManager::SetGuildMOTD(uint32 guild_id, const char* motd, const char *setter) { + if(!DBSetGuildMOTD(guild_id, motd, setter)) + return(false); + + SendGuildRefresh(guild_id, false, true, false, false); + + return(true); +} + +bool BaseGuildManager::SetGuildURL(uint32 GuildID, const char* URL) +{ + if(!DBSetGuildURL(GuildID, URL)) + return(false); + + SendGuildRefresh(GuildID, false, true, false, false); + + return(true); +} + +bool BaseGuildManager::SetGuildChannel(uint32 GuildID, const char* Channel) +{ + if(!DBSetGuildChannel(GuildID, Channel)) + return(false); + + SendGuildRefresh(GuildID, false, true, false, false); + + return(true); +} + +bool BaseGuildManager::SetGuild(uint32 charid, uint32 guild_id, uint8 rank) { + if(rank > GUILD_MAX_RANK && guild_id != GUILD_NONE) + return(false); + + //lookup their old guild, if they had one. + uint32 old_guild = GUILD_NONE; + CharGuildInfo gci; + if(GetCharInfo(charid, gci)) { + old_guild = gci.guild_id; + } + + if(!DBSetGuild(charid, guild_id, rank)) + return(false); + + SendCharRefresh(old_guild, guild_id, charid); + + return(true); +} + +//changes rank, but not guild. +bool BaseGuildManager::SetGuildRank(uint32 charid, uint8 rank) { + if(rank > GUILD_MAX_RANK) + return(false); + + if(!DBSetGuildRank(charid, rank)) + return(false); + + SendCharRefresh(GUILD_NONE, 0, charid); + + return(true); +} + + +bool BaseGuildManager::SetBankerFlag(uint32 charid, bool is_banker) { + if(!DBSetBankerFlag(charid, is_banker)) + return(false); + + SendRankUpdate(charid); + + return(true); +} + +bool BaseGuildManager::SetAltFlag(uint32 charid, bool is_alt) +{ + if(!DBSetAltFlag(charid, is_alt)) + return(false); + + SendRankUpdate(charid); + + return(true); +} + +bool BaseGuildManager::SetTributeFlag(uint32 charid, bool enabled) { + if(!DBSetTributeFlag(charid, enabled)) + return(false); + + SendCharRefresh(GUILD_NONE, 0, charid); + + return(true); +} + +bool BaseGuildManager::SetPublicNote(uint32 charid, const char *note) { + if(!DBSetPublicNote(charid, note)) + return(false); + + SendCharRefresh(GUILD_NONE, 0, charid); + + return(true); +} + +uint32 BaseGuildManager::DBCreateGuild(const char* name, uint32 leader) { + //first try to find a free ID. + uint32 new_id = _GetFreeGuildID(); + if(new_id == GUILD_NONE) + return(GUILD_NONE); + + //now make the guild record in our local manager. + //this also sets up the default ranks for us. + _CreateGuild(new_id, name, leader, 0, "", "", "", ""); + + //now store the resulting guild setup into the DB. + if(!_StoreGuildDB(new_id)) { + _log(GUILDS__ERROR, "Error storing new guild. It may have been partially created which may need manual removal."); + return(GUILD_NONE); + } + + _log(GUILDS__DB, "Created guild %d in the database.", new_id); + + return(new_id); +} + +bool BaseGuildManager::DBDeleteGuild(uint32 guild_id) { + + //remove the local entry + map::iterator res; + res = m_guilds.find(guild_id); + if(res != m_guilds.end()) { + delete res->second; + m_guilds.erase(res); + } + + if(m_db == NULL) { + _log(GUILDS__DB, "Requested to delete guild %d when we have no database object.", 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"); + + //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"); + + //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"); + + // Delete the guild bank + _RunQuery(query, MakeAnyLenString(&query, + "DELETE FROM guild_bank WHERE guildid=%lu", (unsigned long)guild_id), "deleting guild bank"); + + _log(GUILDS__DB, "Deleted guild %d from the database.", guild_id); + + return(true); +} + +bool BaseGuildManager::DBRenameGuild(uint32 guild_id, const char* name) { + if(m_db == NULL) { + _log(GUILDS__DB, "Requested to rename guild %d when we have no database object.", guild_id); + return(false); + } + + map::const_iterator res; + res = m_guilds.find(guild_id); + 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(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)) + { + _log(GUILDS__ERROR, "Error renaming guild %d '%s': %s", guild_id, query, errbuf); + safe_delete_array(query); + safe_delete_array(esc); + 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); +} + +bool BaseGuildManager::DBSetGuildLeader(uint32 guild_id, uint32 leader) { + if(m_db == NULL) { + _log(GUILDS__DB, "Requested to set the leader for guild %d when we have no database object.", guild_id); + return(false); + } + + map::const_iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) + 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)) + { + _log(GUILDS__ERROR, "Error changing leader on guild %d '%s': %s", guild_id, query, errbuf); + safe_delete_array(query); + return(false); + } + safe_delete_array(query); + + //set the old leader to officer + if(!DBSetGuildRank(info->leader_char_id, GUILD_OFFICER)) + return(false); + //set the new leader to leader + if(!DBSetGuildRank(leader, GUILD_LEADER)) + 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); +} + +bool BaseGuildManager::DBSetGuildMOTD(uint32 guild_id, const char* motd, const char *setter) { + if(m_db == NULL) { + _log(GUILDS__DB, "Requested to set the MOTD for guild %d when we have no database object.", guild_id); + return(false); + } + + map::const_iterator res; + res = m_guilds.find(guild_id); + 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(motd); + uint32 len2 = strlen(setter); + char *esc = new char[len*2+1]; + char *esc_set = new char[len2*2+1]; + m_db->DoEscapeString(esc, motd, len); + 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)) + { + _log(GUILDS__ERROR, "Error setting MOTD for guild %d '%s': %s", guild_id, query, errbuf); + safe_delete_array(query); + safe_delete_array(esc); + safe_delete_array(esc_set); + return(false); + } + safe_delete_array(query); + safe_delete_array(esc); + safe_delete_array(esc_set); + + _log(GUILDS__DB, "Set MOTD for guild %d in the database", guild_id); + + info->motd = motd; //update our local record. + info->motd_setter = setter; //update our local record. + + return(true); +} + +bool BaseGuildManager::DBSetGuildURL(uint32 GuildID, const char* URL) +{ + if(m_db == NULL) + return(false); + + map::const_iterator res; + + 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(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)) + { + _log(GUILDS__ERROR, "Error setting URL for guild %d '%s': %s", GuildID, query, errbuf); + safe_delete_array(query); + 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); +} + +bool BaseGuildManager::DBSetGuildChannel(uint32 GuildID, const char* Channel) +{ + if(m_db == NULL) + return(false); + + map::const_iterator res; + + 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)) + { + _log(GUILDS__ERROR, "Error setting Channel for guild %d '%s': %s", GuildID, query, errbuf); + safe_delete_array(query); + 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); + + info->channel = Channel; //update our local record. + + return(true); +} + +bool BaseGuildManager::DBSetGuild(uint32 charid, uint32 guild_id, uint8 rank) { + if(m_db == NULL) { + _log(GUILDS__DB, "Requested to set char to guild %d when we have no database object.", guild_id); + return(false); + } + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + 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); + } + } else { + if (!m_db->RunQuery(query, MakeAnyLenString(&query, + "DELETE FROM guild_members WHERE char_id=%d", + charid), errbuf)) + { + _log(GUILDS__ERROR, "Error removing char %d from guild '%s': %s", charid, guild_id, query, errbuf); + safe_delete_array(query); + 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); +} + +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")); +} + +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")); +} + +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)) + { + _log(GUILDS__ERROR, "Error retrieving banker flag '%s': %s", query, errbuf); + + safe_delete_array(query); + + return false; + } + + safe_delete_array(query); + + if(mysql_num_rows(result) != 1) + return false; + + row = mysql_fetch_row(result); + + bool IsBanker = atoi(row[0]); + + mysql_free_result(result); + + return IsBanker; +} + +bool BaseGuildManager::DBSetAltFlag(uint32 charid, bool is_alt) +{ + char *query = 0; + + 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")); +} + +bool BaseGuildManager::GetAltFlag(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 `alt` from `guild_members` where char_id=%i LIMIT 1", CharID), errbuf, &result)) + { + _log(GUILDS__ERROR, "Error retrieving alt flag '%s': %s", query, errbuf); + + safe_delete_array(query); + + return false; + } + + safe_delete_array(query); + + if(mysql_num_rows(result) != 1) + return false; + + row = mysql_fetch_row(result); + + 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")); +} + +bool BaseGuildManager::DBSetPublicNote(uint32 charid, const char* note) { + if(m_db == NULL) + 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); + safe_delete_array(esc); + + _log(GUILDS__DB, "Set public not for char %d", charid); + + return(true); +} + +bool BaseGuildManager::_RunQuery(char *&query, int len, const char *errmsg) { + if(m_db == NULL) + return(false); + + char errbuf[MYSQL_ERRMSG_SIZE]; + + if (!m_db->RunQuery(query, len, errbuf)) + { + _log(GUILDS__ERROR, "Error %s: '%s': %s", errmsg, query, errbuf); + safe_delete_array(query); + return(false); + } + safe_delete_array(query); + + return(true); +} + +//factored out so I dont have to copy this crap. +#ifdef BOTS +#define GuildMemberBaseQuery \ +"SELECT c.id,c.name,c.class,c.level,c.timelaston,c.zoneid," \ +" g.guild_id,g.rank,g.tribute_enable,g.total_tribute,g.last_tribute," \ +" g.banker,g.public_note,g.alt" \ +" 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," \ +" 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 " +#endif +static void ProcessGuildMember(MYSQL_ROW &row, CharGuildInfo &into) { + //fields from `characer_` + into.char_id = atoi(row[0]); + into.char_name = row[1]; + into.class_ = atoi(row[2]); + into.level = atoi(row[3]); + into.time_last_on = atoul(row[4]); + into.zone_id = atoi(row[5]); + + //fields from `guild_members`, leave at defaults if missing + into.guild_id = row[6] ? atoi(row[6]) : GUILD_NONE; + into.rank = row[7] ? atoi(row[7]) : (GUILD_MAX_RANK+1); + into.tribute_enable = row[8] ? (row[8][0] == '0'?false:true) : false; + into.total_tribute = row[9] ? atoi(row[9]) : 0; + into.last_tribute = row[10]? atoul(row[10]) : 0; //timestamp + into.banker = row[11]? (row[11][0] == '0'?false:true) : false; + into.public_note = row[12]? row[12] : ""; + into.alt = row[13]? (row[13][0] == '0'?false:true) : false; + + //a little sanity checking/cleanup + if(into.guild_id == 0) + into.guild_id = GUILD_NONE; + if(into.rank > GUILD_MAX_RANK) + into.rank = GUILD_RANK_NONE; +} + + +bool BaseGuildManager::GetEntireGuild(uint32 guild_id, vector &members) { + members.clear(); + + if(m_db == NULL) + 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, + 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); + } + safe_delete_array(query); + + while ((row = mysql_fetch_row(result))) { + 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); +} + +bool BaseGuildManager::GetCharInfo(const char *char_name, CharGuildInfo &into) { + if(m_db == NULL) { + _log(GUILDS__DB, "Requested char info on %s when we have no database object.", char_name); + 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); + } + 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); + + return(ret); + + +} + +bool BaseGuildManager::GetCharInfo(uint32 char_id, CharGuildInfo &into) { + if(m_db == NULL) { + _log(GUILDS__DB, "Requested char info on %d when we have no database object.", char_id); + 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, +#ifdef BOTS + GuildMemberBaseQuery " WHERE c.id=%d AND c.mobtype = 'C'", char_id +#else + 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); + } + 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); + + return(ret); + +} + +//returns ownership of the buffer. +uint8 *BaseGuildManager::MakeGuildList(const char *head_name, uint32 &length) const { + //dynamic structs will make this a lot less painful. + + length = sizeof(GuildsList_Struct); + uint8 *buffer = new uint8[length]; + + //a bit little better than memsetting the whole thing... + uint32 r,pos; + for(r = 0, pos = 0; r <= MAX_NUMBER_GUILDS; r++, pos += 64) { + //strcpy((char *) buffer+pos, "BAD GUILD"); + // These 'BAD GUILD' entries were showing in the drop-downs for selecting guilds in the LFP window, + // so just fill unused entries with an empty string instead. + buffer[pos] = '\0'; + } + + strn0cpy((char *) buffer, head_name, 64); + + map::const_iterator cur, end; + cur = m_guilds.begin(); + end = m_guilds.end(); + for(; cur != end; cur++) { + pos = 64 + (64 * cur->first); + strn0cpy((char *) buffer + pos, cur->second->name.c_str(), 64); + } + return(buffer); +} + +const char *BaseGuildManager::GetRankName(uint32 guild_id, uint8 rank) const { + if(rank > GUILD_MAX_RANK) + return("Invalid Rank"); + map::const_iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) + return("Invalid Guild Rank"); + return(res->second->ranks[rank].name.c_str()); +} + +const char *BaseGuildManager::GetGuildName(uint32 guild_id) const { + if(guild_id == GUILD_NONE) + return(""); + map::const_iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) + return("Invalid Guild"); + return(res->second->name.c_str()); +} + +bool BaseGuildManager::GetGuildNameByID(uint32 guild_id, std::string &into) const { + map::const_iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) + return(false); + into = res->second->name; + return(true); +} + +uint32 BaseGuildManager::GetGuildIDByName(const char *GuildName) +{ + map::iterator Iterator; + + for(Iterator = m_guilds.begin(); Iterator != m_guilds.end(); ++Iterator) + { + if(!strcasecmp((*Iterator).second->name.c_str(), GuildName)) + return (*Iterator).first; + } + + return GUILD_NONE; +} + +bool BaseGuildManager::GetGuildMOTD(uint32 guild_id, char *motd_buffer, char *setter_buffer) const { + map::const_iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) + return(false); + strn0cpy(motd_buffer, res->second->motd.c_str(), 512); + strn0cpy(setter_buffer, res->second->motd_setter.c_str(), 64); + return(true); +} + +bool BaseGuildManager::GetGuildURL(uint32 GuildID, char *URLBuffer) const +{ + map::const_iterator res; + res = m_guilds.find(GuildID); + if(res == m_guilds.end()) + return(false); + strn0cpy(URLBuffer, res->second->url.c_str(), 512); + + return(true); +} + +bool BaseGuildManager::GetGuildChannel(uint32 GuildID, char *ChannelBuffer) const +{ + map::const_iterator res; + res = m_guilds.find(GuildID); + if(res == m_guilds.end()) + return(false); + strn0cpy(ChannelBuffer, res->second->channel.c_str(), 128); + return(true); +} + +bool BaseGuildManager::GuildExists(uint32 guild_id) const { + if(guild_id == GUILD_NONE) + return(false); + return(m_guilds.find(guild_id) != m_guilds.end()); +} + +bool BaseGuildManager::IsGuildLeader(uint32 guild_id, uint32 char_id) const { + if(guild_id == GUILD_NONE) { + _log(GUILDS__PERMISSIONS, "Check leader for char %d: not a guild.", char_id); + return(false); + } + map::const_iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) { + _log(GUILDS__PERMISSIONS, "Check leader for char %d: invalid guild.", char_id); + return(false); //invalid guild + } + _log(GUILDS__PERMISSIONS, "Check leader for guild %d, char %d: leader id=%d", guild_id, char_id, res->second->leader_char_id); + return(char_id == res->second->leader_char_id); +} + +uint32 BaseGuildManager::FindGuildByLeader(uint32 leader) const { + map::const_iterator cur, end; + cur = m_guilds.begin(); + end = m_guilds.end(); + for(; cur != end; cur++) { + if(cur->second->leader_char_id == leader) + return(cur->first); + } + return(GUILD_NONE); +} + +//returns the rank to be sent to the client for display purposes, given their eqemu rank. +uint8 BaseGuildManager::GetDisplayedRank(uint32 guild_id, uint8 rank, uint32 char_id) const { + map::const_iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) + return(3); //invalid guild rank + if (res->second->ranks[rank].permissions[GUILD_WARPEACE] || res->second->leader_char_id == char_id) + return(2); //leader rank + else if (res->second->ranks[rank].permissions[GUILD_INVITE] || res->second->ranks[rank].permissions[GUILD_REMOVE] || res->second->ranks[rank].permissions[GUILD_MOTD]) + return(1); //officer rank + return(0); //member rank +} + +bool BaseGuildManager::CheckGMStatus(uint32 guild_id, uint8 status) const { + if(status >= 250) { + _log(GUILDS__PERMISSIONS, "Check permission on guild %d with user status %d > 250, granted.", guild_id, status); + return(true); //250+ as allowed anything + } + + map::const_iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) { + _log(GUILDS__PERMISSIONS, "Check permission on guild %d with user status %d, no such guild, denied.", guild_id, status); + return(false); //invalid guild + } + + bool granted = (res->second->minstatus <= status); + + _log(GUILDS__PERMISSIONS, "Check permission on guild %s (%d) with user status %d. Min status %d: %s", + res->second->name.c_str(), guild_id, status, res->second->minstatus, granted?"granted":"denied"); + + return(granted); +} + +bool BaseGuildManager::CheckPermission(uint32 guild_id, uint8 rank, GuildAction act) const { + if(rank > GUILD_MAX_RANK) { + _log(GUILDS__PERMISSIONS, "Check permission on guild %d and rank %d for action %s (%d): Invalid rank, denied.", + guild_id, rank, GuildActionNames[act], act); + return(false); //invalid rank + } + map::const_iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) { + _log(GUILDS__PERMISSIONS, "Check permission on guild %d and rank %d for action %s (%d): Invalid guild, denied.", + guild_id, rank, GuildActionNames[act], act); + return(false); //invalid guild + } + + bool granted = res->second->ranks[rank].permissions[act]; + + _log(GUILDS__PERMISSIONS, "Check permission on guild %s (%d) and rank %s (%d) for action %s (%d): %s", + res->second->name.c_str(), guild_id, + res->second->ranks[rank].name.c_str(), rank, + GuildActionNames[act], act, + granted?"granted":"denied"); + + return(granted); +} + +bool BaseGuildManager::LocalDeleteGuild(uint32 guild_id) { + map::iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) + return(false); //invalid guild + m_guilds.erase(res); + return(true); +} + +void BaseGuildManager::ClearGuilds() { + map::iterator cur, end; + cur = m_guilds.begin(); + end = m_guilds.end(); + for(; cur != end; cur++) { + delete cur->second; + } + m_guilds.clear(); +} + +BaseGuildManager::RankInfo::RankInfo() { + uint8 r; + for(r = 0; r < _MaxGuildAction; r++) + permissions[r] = false; +} + +BaseGuildManager::GuildInfo::GuildInfo() { + leader_char_id = 0; + minstatus = 0; +} + +uint32 BaseGuildManager::DoesAccountContainAGuildLeader(uint32 AccountID) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + 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)) + { + _log(GUILDS__ERROR, "Error executing query '%s': %s", query, errbuf); + safe_delete_array(query); + 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; +} + + + + + + + +*/ + + + + + + diff --git a/common/guild_base.h b/common/guild_base.h new file mode 100644 index 000000000..0b3e2269e --- /dev/null +++ b/common/guild_base.h @@ -0,0 +1,150 @@ +#ifndef GUILD_BASE_H_ +#define GUILD_BASE_H_ + +#include "guilds.h" +#include +#include +#include + +class Database; + +class CharGuildInfo { +public: + //fields from `characer_` + uint32 char_id; + std::string char_name; + uint8 class_; + uint16 level; + uint32 time_last_on; + uint32 zone_id; + + //fields from `guild_members` + uint32 guild_id; + uint8 rank; + bool tribute_enable; + uint32 total_tribute; + uint32 last_tribute; //timestamp + bool banker; + bool alt; + std::string public_note; +}; + +//this object holds guild functionality shared between world and zone. +class BaseGuildManager { +public: + BaseGuildManager(); + virtual ~BaseGuildManager(); + + //this must be called before doing anything else with this object + void SetDatabase(Database *db) { m_db = db; } + + bool LoadGuilds(); + bool RefreshGuild(uint32 guild_id); + + //guild edit actions. + uint32 CreateGuild(const char* name, uint32 leader_char_id); + bool DeleteGuild(uint32 guild_id); + bool RenameGuild(uint32 guild_id, const char* name); + bool SetGuildMOTD(uint32 guild_id, const char* motd, const char *setter); + bool SetGuildURL(uint32 GuildID, const char* URL); + bool SetGuildChannel(uint32 GuildID, const char* Channel); + + //character edit actions + bool SetGuildLeader(uint32 guild_id, uint32 leader_char_id); + bool SetGuild(uint32 charid, uint32 guild_id, uint8 rank); + bool SetGuildRank(uint32 charid, uint8 rank); + bool SetBankerFlag(uint32 charid, bool is_banker); + bool GetAltFlag(uint32 CharID); + bool SetAltFlag(uint32 charid, bool is_alt); + bool GetBankerFlag(uint32 CharID); + bool SetTributeFlag(uint32 charid, bool enabled); + bool SetPublicNote(uint32 charid, const char *note); + + //queries + bool GetCharInfo(const char *char_name, CharGuildInfo &into); + bool GetCharInfo(uint32 char_id, CharGuildInfo &into); + bool GetEntireGuild(uint32 guild_id, std::vector &members); //caller is responsible for deleting each pointer in the resulting vector. + bool GuildExists(uint32 guild_id) const; + bool GetGuildMOTD(uint32 guild_id, char *motd_buffer, char *setter_buffer) const; + bool GetGuildURL(uint32 GuildID, char *URLBuffer) const; + bool GetGuildChannel(uint32 GuildID, char *ChannelBuffer) const; + const char *GetRankName(uint32 guild_id, uint8 rank) const; + const char *GetGuildName(uint32 guild_id) const; + bool GetGuildNameByID(uint32 guild_id, std::string &into) const; + uint32 GetGuildIDByName(const char *GuildName); + bool IsGuildLeader(uint32 guild_id, uint32 char_id) const; + uint8 GetDisplayedRank(uint32 guild_id, uint8 rank, uint32 char_id) const; + bool CheckGMStatus(uint32 guild_id, uint8 status) const; + bool CheckPermission(uint32 guild_id, uint8 rank, GuildAction act) const; +// uint32 Getguild_id(uint32 eqid); + uint32 FindGuildByLeader(uint32 leader) const; +// void GetGuildMembers(uint32 guild_id,GuildMember_Struct* gms); + uint32 NumberInGuild(uint32 guild_id); +// bool GetGuildRanks(uint32 guildeqid, GuildRanks_Struct* gr); +// bool EditGuild(uint32 guild_id, uint8 ranknum, GuildRankLevel_Struct* grl); + + uint8 *MakeGuildList(const char *head_name, uint32 &length) const; //make a guild list packet, returns ownership of the buffer. + + static const char *const GuildActionNames[_MaxGuildAction]; + uint32 DoesAccountContainAGuildLeader(uint32 AccountID); + +protected: + //the methods which must be defined by base classes. + virtual void SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation) = 0; + virtual void SendCharRefresh(uint32 old_guild_id, uint32 guild_id, uint32 charid) = 0; + virtual void SendRankUpdate(uint32 CharID) = 0; + virtual void SendGuildDelete(uint32 guild_id) = 0; + + uint32 DBCreateGuild(const char* name, uint32 leader_char_id); + bool DBDeleteGuild(uint32 guild_id); + bool DBRenameGuild(uint32 guild_id, const char* name); + bool DBSetGuildLeader(uint32 guild_id, uint32 leader_char_id); + bool DBSetGuildMOTD(uint32 guild_id, const char* motd, const char *setter); + bool DBSetGuildURL(uint32 GuildID, const char* URL); + bool DBSetGuildChannel(uint32 GuildID, const char* Channel); + bool DBSetGuild(uint32 charid, uint32 guild_id, uint8 rank); + bool DBSetGuildRank(uint32 charid, uint8 rank); + bool DBSetBankerFlag(uint32 charid, bool is_banker); + 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); +// void DBSetPublicNote(uint32 guild_id,char* charname, char* note); + + bool LocalDeleteGuild(uint32 guild_id); + + class RankInfo { + public: + RankInfo(); + std::string name; + bool permissions[_MaxGuildAction]; + }; + class GuildInfo { + public: + GuildInfo(); + std::string name; + std::string motd; + std::string motd_setter; + std::string url; + std::string channel; + + uint32 leader_char_id; + uint8 minstatus; + //tribute is not in here on purpose, since it is only valid in world! + RankInfo ranks[GUILD_MAX_RANK+1]; + }; + + std::map m_guilds; //we own the pointers in this map + void ClearGuilds(); //clears internal structure + + Database *m_db; //we do not own this + + bool _StoreGuildDB(uint32 guild_id); + GuildInfo *_CreateGuild(uint32 guild_id, const char *guild_name, uint32 account_id, uint8 minstatus, const char *guild_motd, const char *motd_setter, const char *Channel, const char *URL); + uint32 _GetFreeGuildID(); +}; + + +#endif /*GUILD_BASE_H_*/ + + diff --git a/common/guilds.cpp b/common/guilds.cpp new file mode 100644 index 000000000..83dd147d2 --- /dev/null +++ b/common/guilds.cpp @@ -0,0 +1,341 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 "MiscFunctions.h" +#include "guilds.h" +#include "database.h" +#include "eq_packet_structs.h" + + +#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; +} +*/ + diff --git a/common/guilds.h b/common/guilds.h new file mode 100644 index 000000000..663698a70 --- /dev/null +++ b/common/guilds.h @@ -0,0 +1,46 @@ +/* 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 GUILD_H +#define GUILD_H + +#include "types.h" + +#define GUILD_NONE 0xFFFFFFFF // user has no guild + +#define GUILD_MAX_RANK 8 // 0-2 - some places in the code assume a single digit, dont go above 9 + +//defines for standard ranks +#define GUILD_MEMBER 0 +#define GUILD_OFFICER 1 +#define GUILD_LEADER 2 +#define GUILD_RANK_NONE (GUILD_MAX_RANK+1) + +typedef enum { + GUILD_HEAR = 0, + GUILD_SPEAK = 1, + GUILD_INVITE = 2, + GUILD_REMOVE = 3, + GUILD_PROMOTE = 4, + GUILD_DEMOTE = 5, + GUILD_MOTD = 6, + GUILD_WARPEACE = 7, + _MaxGuildAction +} GuildAction; + +#endif diff --git a/common/item_fieldlist.h b/common/item_fieldlist.h new file mode 100644 index 000000000..c4d54d70f --- /dev/null +++ b/common/item_fieldlist.h @@ -0,0 +1,197 @@ +/* + + +These fields must be in the order of how they are serialized! + + + +*/ + +F(itemclass) +F(name) +F(lore) +F(idfile) +F(id) +F(weight) +F(norent) +F(nodrop) +F(size) +F(slots) +F(price) +F(icon) +F(UNK012) +F(UNK013) +F(benefitflag) +F(tradeskills) +F(cr) +F(dr) +F(pr) +F(mr) +F(fr) +F(astr) +F(asta) +F(aagi) +F(adex) +F(acha) +F(aint) +F(awis) +F(hp) +F(mana) +F(ac) +F(deity) +F(skillmodvalue) +F(UNK033) +F(skillmodtype) +F(banedmgrace) +F(banedmgamt) +F(banedmgbody) +F(magic) +F(casttime_) +F(reqlevel) +F(bardtype) +F(bardvalue) +F(light) +F(delay) +F(reclevel) +F(recskill) +F(elemdmgtype) +F(elemdmgamt) +F(range) +F(damage) +F(color) +F(classes) +F(races) +F(UNK054) +F(maxcharges) +F(itemtype) +F(material) +F(sellrate) +F(UNK059) +F(casttime) +F(elitematerial) +F(procrate) +F(combateffects) +F(shielding) +F(stunresist) +F(strikethrough) +F(extradmgskill) +F(extradmgamt) +F(spellshield) +F(avoidance) +F(accuracy) +F(charmfileid) +F(factionmod1) +F(factionmod2) +F(factionmod3) +F(factionmod4) +F(factionamt1) +F(factionamt2) +F(factionamt3) +F(factionamt4) +F(charmfile) +F(augtype) +F(augslot1type) +F(augslot1visible) +F(augslot2type) +F(augslot2visible) +F(augslot3type) +F(augslot3visible) +F(augslot4type) +F(augslot4visible) +F(augslot5type) +F(augslot5visible) +F(ldontheme) +F(ldonprice) +F(ldonsold) +F(bagtype) +F(bagslots) +F(bagsize) +F(bagwr) +F(book) +F(booktype) +F(filename) +F(banedmgraceamt) +F(augrestrict) +F(loregroup) +F(pendingloreflag) +F(artifactflag) +F(summonedflag) +F(favor) +F(fvnodrop) +F(endur) +F(dotshielding) +F(attack) +F(regen) +F(manaregen) +F(enduranceregen) +F(haste) +F(damageshield) +F(recastdelay) +F(recasttype) +F(guildfavor) +F(augdistiller) +F(UNK123) +F(UNK124) +F(attuneable) +F(nopet) +F(UNK127) +F(pointtype) +F(potionbelt) +F(potionbeltslots) +F(stacksize) +F(notransfer) +F(stackable) +F(UNK134) +F(clickeffect) +F(clicktype) +F(clicklevel) +F(clicklevel2) +F(proceffect) +F(proctype) +F(proclevel) +F(proclevel2) +F(worneffect) +F(worntype) +F(wornlevel) +F(wornlevel2) +F(focuseffect) +F(focustype) +F(focuslevel) +F(focuslevel2) +F(scrolleffect) +F(scrolltype) +F(scrolllevel) +F(scrolllevel2) +F(bardeffect) +F(bardeffecttype) +F(bardlevel2) +F(bardlevel) +F(questitemflag) +F(svcorruption) +F(purity) +F(backstabdmg) +F(dsmitigation) +F(heroic_str) +F(heroic_int) +F(heroic_wis) +F(heroic_agi) +F(heroic_dex) +F(heroic_sta) +F(heroic_cha) +F(heroic_mr) +F(heroic_fr) +F(heroic_cr) +F(heroic_dr) +F(heroic_pr) +F(heroic_svcorrup) +F(healamt) +F(spelldmg) +F(ldonsellbackrate) +F(scriptfileid) +F(expendablearrow) +F(clairvoyance) +F(clickname) +F(procname) +F(wornname) +F(focusname) +F(scrollname) diff --git a/common/item_struct.h b/common/item_struct.h new file mode 100644 index 000000000..d1dd5b1b0 --- /dev/null +++ b/common/item_struct.h @@ -0,0 +1,247 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2003 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 04111-1307 USA +*/ + +#ifndef ITEM_STRUCT_H +#define ITEM_STRUCT_H + +/* + * Note: (Doodman) + * This structure has field names that match the DB name exactly. + * Please take care as to not mess this up as it should make + * everyones life (i.e. mine) much easier. And the DB names + * match the field name from the 13th floor (SEQ) item collectors, + * so please maintain that as well. + * + * Note #2: (Doodman) + * UnkXXX fields are left in here for completeness but commented + * out since they are really unknown and since the items are now + * preserialized they should not be needed. Conversly if they + * -are- needed, then they shouldn't be unkown. + * + * Note #3: (Doodman) + * Please take care when adding new found data fields to add them + * to the appropriate structure. Item_Struct has elements that are + * global to all types of items only. + * + * Note #4: (Doodman) + * Made ya look! Ha! + */ + +#include "eq_constants.h" + +/* +** Child struct of Item_Struct: +** Effect data: Click, Proc, Focus, Worn, Scroll +** +*/ +struct ItemEffect_Struct { + int16 Effect; + uint8 Type; + uint8 Level; + uint8 Level2; + //MaxCharges + //CastTime + //RecastDelay + //RecastType + //ProcRate +}; + +class ItemInst; + +struct InternalSerializedItem_Struct { + int16 slot_id; + const void * inst; +}; + +#define MAX_AUGMENT_SLOTS 5 + +struct Item_Struct { + bool IsEquipable(uint16 Race, uint16 Class) const; + // Non packet based fields + uint8 MinStatus; + + // Packet based fields + uint8 ItemClass; // Item Type: 0=common, 1=container, 2=book + char Name[64]; // Name + char Lore[80]; // Lore Name: *=lore, &=summoned, #=artifact, ~=pending lore + char IDFile[30]; // Visible model + uint32 ID; // Unique ID (also PK for DB) + uint8 Weight; // Item weight * 10 + uint8 NoRent; // No Rent: 0=norent, 255=not norent + uint8 NoDrop; // No Drop: 0=nodrop, 255=not nodrop + uint8 Size; // Size: 0=tiny, 1=small, 2=medium, 3=large, 4=giant + uint32 Slots; // Bitfield for which slots this item can be used in + uint32 Price; // Item cost (?) + uint32 Icon; // Icon Number + uint32 LoreGroup; // Later items use LoreGroup instead of LoreFlag. we might want to see about changing this to int32 since it is commonly -1 and is constantly being cast from signed (-1) to unsigned (4294967295) + bool LoreFlag; // This will be true if LoreGroup is non-zero + bool PendingLoreFlag; + bool ArtifactFlag; + bool SummonedFlag; + uint8 FVNoDrop; // Firiona Vie nodrop flag + uint32 Favor; // Individual favor + uint32 GuildFavor; // Guild favor + uint32 PointType; + + //uint32 Unk117; + //uint32 Unk118; + //uint32 Unk121; + //uint32 Unk124; + + uint8 BagType; // 0:Small Bag, 1:Large Bag, 2:Quiver, 3:Belt Pouch ... there are 50 types + uint8 BagSlots; // Number of slots: can only be 2, 4, 6, 8, or 10 + uint8 BagSize; // 0:TINY, 1:SMALL, 2:MEDIUM, 3:LARGE, 4:GIANT + uint8 BagWR; // 0->100 + + bool BenefitFlag; + bool Tradeskills; // Is this a tradeskill item? + int8 CR; // Save vs Cold + int8 DR; // Save vs Disease + int8 PR; // Save vs Poison + int8 MR; // Save vs Magic + int8 FR; // Save vs Fire + int8 AStr; // Strength + int8 ASta; // Stamina + int8 AAgi; // Agility + int8 ADex; // Dexterity + int8 ACha; // Charisma + int8 AInt; // Intelligence + int8 AWis; // Wisdom + int32 HP; // HP + int32 Mana; // Mana + int32 AC; // AC + uint32 Deity; // Bitmask of Deities that can equip this item + //uint32 Unk033 + int32 SkillModValue; // % Mod to skill specified in SkillModType + uint32 SkillModType; // Type of skill for SkillModValue to apply to + uint32 BaneDmgRace; // Bane Damage Race + int8 BaneDmgAmt; // Bane Damage Body Amount + uint32 BaneDmgBody; // Bane Damage Body + bool Magic; // True=Magic Item, False=not + int32 CastTime_; + uint8 ReqLevel; // Required Level to use item + uint32 BardType; // Bard Skill Type + int32 BardValue; // Bard Skill Amount + int8 Light; // Light + uint8 Delay; // Delay * 10 + uint8 RecLevel; // Recommended level to use item + uint8 RecSkill; // Recommended skill to use item (refers to primary skill of item) + uint8 ElemDmgType; // Elemental Damage Type (1=magic, 2=fire) + uint8 ElemDmgAmt; // Elemental Damage + uint8 Range; // Range of item + uint32 Damage; // Delay between item usage (in 0.1 sec increments) + uint32 Color; // RR GG BB 00 <-- as it appears in pc + uint32 Classes; // Bitfield of classes that can equip item (1 << class#) + uint32 Races; // Bitfield of races that can equip item (1 << race#) + //uint32 Unk054; + int16 MaxCharges; // Maximum charges items can hold: -1 if not a chargeable item + uint8 ItemType; // Item Type/Skill (itemClass* from above) + uint8 Material; // Item material type + float SellRate; // Sell rate + //uint32 Unk059; + union { + uint32 Fulfilment; // Food fulfilment (How long it lasts) + int16 CastTime; // Cast Time for clicky effects, in milliseconds + }; + uint32 EliteMaterial; + int32 ProcRate; + int8 CombatEffects; // PoP: Combat Effects + + int8 Shielding; // PoP: Shielding % + int8 StunResist; // PoP: Stun Resist % + int8 StrikeThrough; // PoP: Strike Through % + uint32 ExtraDmgSkill; + uint32 ExtraDmgAmt; + int8 SpellShield; // PoP: Spell Shield % + int8 Avoidance; // PoP: Avoidance + + int8 Accuracy; // PoP: Accuracy + + uint32 CharmFileID; + int32 FactionMod1; // Faction Mod 1 + int32 FactionMod2; // Faction Mod 2 + int32 FactionMod3; // Faction Mod 3 + int32 FactionMod4; // Faction Mod 4 + int32 FactionAmt1; // Faction Amt 1 + int32 FactionAmt2; // Faction Amt 2 + int32 FactionAmt3; // Faction Amt 3 + int32 FactionAmt4; // Faction Amt 4 + char CharmFile[32]; // ? + uint32 AugType; + uint8 AugSlotType[MAX_AUGMENT_SLOTS]; // LDoN: Augment Slot 1-5 Type + uint8 AugSlotVisible[MAX_AUGMENT_SLOTS]; // LDoN: Augment Slot 1-5 Visible + uint8 AugSlotUnk2[MAX_AUGMENT_SLOTS]; // LDoN: Augment Slot 1-5 Unknown + uint32 LDoNTheme; + uint32 LDoNPrice; + uint32 LDoNSold; + uint32 BaneDmgRaceAmt; + uint32 AugRestrict; + uint32 Endur; + uint32 DotShielding; + uint32 Attack; + uint32 Regen; + uint32 ManaRegen; + uint32 EnduranceRegen; + uint32 Haste; + uint32 DamageShield; + uint32 RecastDelay; + uint32 RecastType; + uint32 AugDistiller; + bool Attuneable; + bool NoPet; + bool PotionBelt; + bool Stackable; + bool NoTransfer; + bool QuestItemFlag; + int16 StackSize; + uint8 PotionBeltSlots; + ItemEffect_Struct Click, Proc, Worn, Focus, Scroll, Bard; + + uint8 Book; // 0=Not book, 1=Book + uint32 BookType; + char Filename[33]; // Filename for book data + // Begin SoF Fields + int32 SVCorruption; + uint32 Purity; + uint32 BackstabDmg; + uint32 DSMitigation; + int32 HeroicStr; + int32 HeroicInt; + int32 HeroicWis; + int32 HeroicAgi; + int32 HeroicDex; + int32 HeroicSta; + int32 HeroicCha; + int32 HeroicMR; + int32 HeroicFR; + int32 HeroicCR; + int32 HeroicDR; + int32 HeroicPR; + int32 HeroicSVCorrup; + int32 HealAmt; + int32 SpellDmg; + uint32 LDoNSellBackRate; + uint32 ScriptFileID; + uint16 ExpendableArrow; + uint32 Clairvoyance; + char ClickName[65]; + char ProcName[65]; + char WornName[65]; + char FocusName[65]; + char ScrollName[65]; + +}; + +#endif diff --git a/common/languages.h b/common/languages.h new file mode 100644 index 000000000..39aee2717 --- /dev/null +++ b/common/languages.h @@ -0,0 +1,52 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2002 EQEMu Development Team (http://eqemulator.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 LANGUAGES_H +#define LANGUAGES_H +#include "../common/types.h" + +#define LANG_COMMON_TONGUE 0 +#define LANG_BARBARIAN 1 +#define LANG_ERUDIAN 2 +#define LANG_ELVISH 3 +#define LANG_DARK_ELVISH 4 +#define LANG_DWARVISH 5 +#define LANG_TROLL 6 +#define LANG_OGRE 7 +#define LANG_GNOMISH 8 +#define LANG_HALFLING 9 +#define LANG_THIEVES_CANT 10 +#define LANG_OLD_ERUDIAN 11 +#define LANG_ELDER_ELVISH 12 +#define LANG_FROGLOK 13 +#define LANG_GOBLIN 14 +#define LANG_GNOLL 15 +#define LANG_COMBINE_TONGUE 16 +#define LANG_ELDER_TEIRDAL 17 +#define LANG_LIZARDMAN 18 +#define LANG_ORCISH 19 +#define LANG_FAERIE 20 +#define LANG_DRAGON 21 +#define LANG_ELDER_DRAGON 22 +#define LANG_DARK_SPEECH 23 +#define LANG_VAH_SHIR 24 +#define LANG_ALARAN 25 +#define LANG_HADAL 26 +#define LANG_UNKNOWN 27 + +#endif + diff --git a/common/linked_list.h b/common/linked_list.h new file mode 100644 index 000000000..491ec5804 --- /dev/null +++ b/common/linked_list.h @@ -0,0 +1,444 @@ +/* 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 LINKEDLIST_H +#define LINKEDLIST_H + +#include "types.h" + +enum direction{FORWARD,BACKWARD}; + +template class LinkedListIterator; + +template +class ListElement +{ +private: + + TYPE data; + ListElement* next; + ListElement* prev; +public: + ListElement (); + ListElement (const TYPE&); + ListElement (const ListElement&); + + ~ListElement (); + + ListElement& operator= (const ListElement&); + + ListElement* GetLast () + { + ListElement* tmp = this; + while (tmp->GetNext()) { + tmp = tmp->GetNext(); + } + return tmp; + } + ListElement* GetNext () const { return next ; } + ListElement* GetPrev () const { return prev ; } + + inline TYPE& GetData () { return data ; } + inline const TYPE& GetData () const { return data ; } + + void SetData ( const TYPE& d ) { data = d ; } // Quagmire - this may look like a mem leak, but dont change it, this behavior is expected where it's called + void SetLastNext ( ListElement* p ) + { + GetLast()->SetNext(p); + } + void SetNext (ListElement* n) { next = n ; } + void SetPrev (ListElement* p) { prev = p ; } + + void ReplaceData(const TYPE&); +}; + +template +class LinkedList +{ +private: + uint32 count; + ListElement* first; + bool list_destructor_invoked; + +public: + + LinkedList(); + ~LinkedList(); + bool dont_delete; + LinkedList& operator= (const LinkedList&); + + void Append (const TYPE&); + void Insert (const TYPE&); + TYPE Pop(); + TYPE PeekTop(); + void Clear(); + void LCount() { count--; } + void ResetCount() { count=0; } + uint32 Count() { return count; } + ListElement* GetFirst() { return first; } + + friend class LinkedListIterator; +}; + +template +class LinkedListIterator +{ +private: + LinkedList& list; + ListElement* current_element; + direction dir; + +public: + LinkedListIterator(LinkedList& l,direction d = FORWARD) : list(l), dir(d) {}; + + void Advance(); + const TYPE& GetData(); + bool IsFirst() + { + if (current_element->GetPrev() == 0) + return true; + else + return false; + } + bool IsLast() + { + if (current_element->GetNext() == 0) + return true; + else + return false; + } + bool MoreElements(); + void MoveFirst(); + void MoveLast(); + void RemoveCurrent(bool DeleteData = true); + void Replace(const TYPE& new_data); + void Reset(); + void SetDir(direction); +}; + +template +void LinkedListIterator::Advance() +{ + if (current_element == 0) + { + return; + } + if (dir == FORWARD) + { + current_element = current_element->GetNext(); + } + else + { + current_element = current_element->GetPrev(); + } + + if (list.list_destructor_invoked) + { + while(current_element && current_element->GetData() == 0) + { +// if (current_element == 0) +// { +// return; +// } + if (dir == FORWARD) + { + current_element = current_element->GetNext(); + } + else + { + current_element = current_element->GetPrev(); + } + } + } +} + +template +bool LinkedListIterator::MoreElements() +{ + if (current_element == 0) + return false; + return true; +} + +template +const TYPE& LinkedListIterator::GetData() +{ + return current_element->GetData(); +} + +template +void LinkedListIterator::MoveFirst() +{ + ListElement* prev = current_element->GetPrev(); + ListElement* next = current_element->GetNext(); + + if (prev == 0) + { + return; + } + +// if (prev != 0) +// { + prev->SetNext(next); +// } + if (next != 0) + { + next->SetPrev(prev); + } + current_element->SetPrev(0); + current_element->SetNext(list.first); + list.first->SetPrev(current_element); + list.first = current_element; +} + + +template +void LinkedListIterator::MoveLast() +{ + ListElement* prev = current_element->GetPrev(); + ListElement* next = current_element->GetNext(); + + if (next == 0) + { + return; + } + + if (prev != 0) + { + prev->SetNext(next); + } + else + { + list.first = next; + } +// if (next != 0) +// { + next->SetPrev(prev); +// } + current_element->SetNext(0); + current_element->SetPrev(next->GetLast()); + next->GetLast()->SetNext(current_element); +} + +template +void LinkedListIterator::RemoveCurrent(bool DeleteData) +{ + ListElement* save; + + if (list.first == current_element) + { + list.first = current_element->GetNext(); + } + + if (current_element->GetPrev() != 0) + { + current_element->GetPrev()->SetNext(current_element->GetNext()); + } + if (current_element->GetNext() != 0) + { + current_element->GetNext()->SetPrev(current_element->GetPrev()); + } + if (dir == FORWARD) + { + save = current_element->GetNext(); + } + else + { + save = current_element->GetPrev(); + } + current_element->SetNext(0); + current_element->SetPrev(0); + if (!DeleteData) + current_element->SetData(0); + safe_delete(current_element); + current_element = save; + list.LCount(); +} + +template +void LinkedListIterator::Replace(const TYPE& new_data) +{ + current_element->ReplaceData(new_data); +} + +template +void LinkedListIterator::Reset() +{ + if (!(&list)) + { + current_element=0; + return; + } + + if (dir == FORWARD) + { + current_element = list.first; + } + else + { + if (list.first == 0) + { + current_element = 0; + } + else + { + current_element = list.first->GetLast(); + } + } + + if (list.list_destructor_invoked) + { + while(current_element && current_element->GetData() == 0) + { +// if (current_element == 0) +// { +// return; +// } + if (dir == FORWARD) + { + current_element = current_element->GetNext(); + } + else + { + current_element = current_element->GetPrev(); + } + } + } +} + +template +void LinkedListIterator::SetDir(direction d) +{ + dir = d; +} + +template +ListElement::ListElement(const TYPE& d) +{ + data = d; + next = 0; + prev = 0; +} + +template +ListElement::~ListElement() +{ +// cout << "ListElement::~ListElement()" << endl; + + if (data != 0) + safe_delete(data); + data = 0; + if (next != 0) + { + safe_delete(next); + next = 0; + } +} + +template +void ListElement::ReplaceData(const TYPE& new_data) +{ + if (data != 0) + safe_delete(data); + data = new_data; +} + +template +LinkedList::LinkedList() +{ + list_destructor_invoked = false; + first = 0; + count = 0; + dont_delete = false; +} + +template +LinkedList::~LinkedList() +{ + list_destructor_invoked = true; + if(!dont_delete) + Clear(); +} + +template +void LinkedList::Clear() { + while (first) { + ListElement* tmp = first; + first = tmp->GetNext(); + tmp->SetNext(0); + safe_delete(tmp); + } + ResetCount(); +} + +template +void LinkedList::Append(const TYPE& data) +{ + ListElement* new_element = new ListElement(data); + + if (first == 0) + { + first = new_element; + } + else + { + new_element->SetPrev(first->GetLast()); + first->SetLastNext(new_element); + } + count++; +} + +template +void LinkedList::Insert(const TYPE& data) +{ + ListElement* new_element = new ListElement(data); + + new_element->SetNext(first); + if (first != 0) + { + first->SetPrev(new_element); + } + first = new_element; + count++; +} + +template +TYPE LinkedList::Pop() { + TYPE ret = 0; + if (first) { + ListElement* tmpdel = first; + first = tmpdel->GetNext(); + if (first) + first->SetPrev(0); + ret = tmpdel->GetData(); + tmpdel->SetData(0); + tmpdel->SetNext(0); + safe_delete(tmpdel); + count--; + } + return ret; +} + +template +TYPE LinkedList::PeekTop() { + if (first) + return first->GetData(); + return 0; +} + +#endif + diff --git a/common/logsys.cpp b/common/logsys.cpp new file mode 100644 index 000000000..ee5c8c06c --- /dev/null +++ b/common/logsys.cpp @@ -0,0 +1,160 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "logsys.h" +#include "debug.h" +#include +#include +#include +#include "misc.h" +#include "EQPacket.h" + + +#define LOG_CATEGORY(category) #category , +const char *log_category_names[NUMBER_OF_LOG_CATEGORIES] = { + #include "logtypes.h" +}; + + +//this array is private to this file, only a const version of it is exposed +#define LOG_TYPE(category, type, enabled) { enabled, LOG_ ##category, #category "__" #type }, +static LogTypeStatus real_log_type_info[NUMBER_OF_LOG_TYPES+1] = +{ + #include "logtypes.h" + { false, NUMBER_OF_LOG_CATEGORIES, "BAD TYPE" } /* dummy trailing record */ +}; +const LogTypeStatus *log_type_info = real_log_type_info; + + + +void log_hex(LogType type, const void *data, unsigned long length, unsigned char padding) { + if(!is_log_enabled(type)) + return; + char buffer[80]; + uint32 offset; + for(offset=0;offsetbuild_header_dump(buffer); + log_message(type,"%s", buffer); + log_hex(type,(const char *)p->pBuffer,p->size); +} + +void log_raw_packet(LogType type, uint16 seq, const BasePacket *p) { + if(!is_log_enabled(type)) + return; + char buffer[196]; + p->build_raw_header_dump(buffer, seq); + log_message(type,buffer); + log_hex(type,(const char *)p->pBuffer,p->size); +} + + +void log_enable(LogType t) { + real_log_type_info[t].enabled = true; +} + +void log_disable(LogType t) { + real_log_type_info[t].enabled = false; +} + +void log_toggle(LogType t) { + real_log_type_info[t].enabled = !real_log_type_info[t].enabled; +} + + +bool load_log_settings(const char *filename) { + //this is a terrible algorithm, but im lazy today + FILE *f = fopen(filename, "r"); + if(f == NULL) + return(false); + char linebuf[512], type_name[256], value[256]; + while(!feof(f)) { + if(fgets(linebuf, 512, f) == NULL) + continue; +#ifdef _WINDOWS + if (sscanf(linebuf, "%[^=]=%[^\n]\n", type_name, value) != 2) + continue; +#else + if (sscanf(linebuf, "%[^=]=%[^\r\n]\n", type_name, value) != 2) + continue; +#endif + + if(type_name[0] == '\0' || type_name[0] == '#') + continue; + + //first make sure we understand the value + bool enabled; + if(!strcasecmp(value, "on") || !strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "enabled") || !strcmp(value, "1")) + enabled = true; + else if(!strcasecmp(value, "off") || !strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "disabled") || !strcmp(value, "0")) + enabled = false; + else { + printf("Unable to parse value '%s' from %s. Skipping line.", value, filename); + continue; + } + + int r; + //first see if it is a category name + for(r = 0; r < NUMBER_OF_LOG_CATEGORIES; r++) { + if(!strcasecmp(log_category_names[r], type_name)) + break; + } + if(r != NUMBER_OF_LOG_CATEGORIES) { + //matched a category. + int k; + for(k = 0; k < NUMBER_OF_LOG_TYPES; k++) { + if(log_type_info[k].category != r) + continue; //does not match this category. + if(enabled) + log_enable(LogType(k)); + else + log_disable(LogType(k)); + } + continue; + } + + for(r = 0; r < NUMBER_OF_LOG_TYPES; r++) { + if(!strcasecmp(log_type_info[r].name, type_name)) + break; + } + if(r == NUMBER_OF_LOG_TYPES) { + printf("Unable to locate log type %s from file %s. Skipping line.", type_name, filename); + continue; + } + + //got it all figured out, do something now... + if(enabled) + log_enable(LogType(r)); + else + log_disable(LogType(r)); + } + fclose(f); + return(true); +} + + + + diff --git a/common/logsys.h b/common/logsys.h new file mode 100644 index 000000000..a8c27fdf9 --- /dev/null +++ b/common/logsys.h @@ -0,0 +1,187 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 LOGSYS_H_ +#define LOGSYS_H_ + +/* + * + * Usage: + * + * These are the main functions provided by logsys: + * - _log(TYPE, fmt, ...) - Log a message in any context + * - mlog(TYPE, fmt, ...) - Zone only. Log a message from a Mob:: context, prefixing it with the mob's name. + * - clog(TYPE, fmt, ...) - World only. Log a message from a Client:: context, prefixing it with the client's account name. + * - zlog(TYPE, fmt, ...) - World only. Log a message from a ZoneServer:: context, prefixing it with the zones id/name or ip/port. + * - _hex(TYPE, data, length) - Log hex dump in any context. + * - mhex(TYPE, data, length) - Zone only. Log a hex dump from a Mob:: context, prefixing it with the mob's name + * - _pkt(TYPE, BasePacket *) - Log a packet hex dump with header in any context. + * - mhex(TYPE, data, length) - Zone only. Log a packet hex dump from a Mob:: context, prefixing it with the mob's name + * Types are defined in logtypes.h + * + * + * + * + * this is very C-ish, not C++ish, but thats how I felt like writting it + * + * + * + */ + +#include +#include "types.h" + +#define LOG_CATEGORY(category) LOG_ ##category , +typedef enum { + #include "logtypes.h" + NUMBER_OF_LOG_CATEGORIES +} LogCategory; + +#define LOG_TYPE(category, type, enabled) category##__##type , +typedef enum { + #include "logtypes.h" + NUMBER_OF_LOG_TYPES +} LogType; + +extern const char *log_category_names[NUMBER_OF_LOG_CATEGORIES]; + +typedef struct { + bool enabled; + LogCategory category; + const char *name; +} LogTypeStatus; + +//expose a read-only pointer +extern const LogTypeStatus *log_type_info; + +// For log_packet, et all. +class BasePacket; + +extern void log_message(LogType type, const char *fmt, ...); +extern void log_messageVA(LogType type, const char *fmt, va_list args); +extern void log_hex(LogType type, const void *data, unsigned long length, unsigned char padding=4); +extern void log_packet(LogType type, const BasePacket *p); +extern void log_raw_packet(LogType type, uint16 seq, const BasePacket *p); + +#ifdef DISABLE_LOGSYS + //completely disabled, this is the best I can come up with since we have no variadic macros + inline void _log(LogType, const char *, ...) {}//i feel dirty for putting this ifdef here, but I dont wanna have to include a header in all zone files to get it + inline void mlog(LogType, const char *, ...) {} + inline void clog(LogType, const char *, ...) {} + inline void zlog(LogType, const char *, ...) {} +#else //!DISABLE_LOGSYS + + //we have variadic macros, hooray! + //the do-while construct is needed to allow a ; at the end of log(); lines when used + //in conditional statements without {}'s + #define _log( type, format, ...) \ + do { \ + if(log_type_info[ type ].enabled) { \ + log_message(type, format, ##__VA_ARGS__); \ + } \ + } while(false) + #ifdef ZONE + class Mob; + extern void log_message_mob(LogType type, Mob *who, const char *fmt, ...); + #define mlog( type, format, ...) \ + if(IsLoggingEnabled()) \ + do { \ + if(log_type_info[ type ].enabled) { \ + log_message_mob(type, this, format, ##__VA_ARGS__); \ + } \ + } while(false) + #endif + #ifdef WORLD + class Client; + extern void log_message_client(LogType type, Client *who, const char *fmt, ...); + #define clog( type, format, ...) \ + do { \ + if(log_type_info[ type ].enabled) { \ + log_message_client(type, this, format, ##__VA_ARGS__); \ + } \ + } while(false) + + class ZoneServer; + extern void log_message_zone(LogType type, ZoneServer *who, const char *fmt, ...); + #define zlog( type, format, ...) \ + do { \ + if(log_type_info[ type ].enabled) { \ + log_message_zone(type, this, format, ##__VA_ARGS__); \ + } \ + } while(false) + #endif +#endif //!DISABLE_LOGSYS + + +/* these are macros which do not use ..., and work for anybody */ + #define _hex( type, data, len) \ + do { \ + if(log_type_info[ type ].enabled) { \ + log_hex(type, (const char *)data, len); \ + } \ + } while(false) + #define _pkt( type, packet) \ + do { \ + if(log_type_info[ type ].enabled) { \ + log_packet(type, packet); \ + } \ + } while(false) + #define _raw( type, seq, packet) \ + do { \ + if(log_type_info[ type ].enabled) { \ + log_raw_packet(type, seq, packet); \ + } \ + } while(false) +#ifdef ZONE + 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(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(log_type_info[ type ].enabled) { \ + log_packet_mob(type, this, packet); \ + } \ + } while(false) +#endif + +extern void log_enable(LogType t); +extern void log_disable(LogType t); +extern void log_toggle(LogType t); + +#define is_log_enabled( type ) \ + log_type_info[ type ].enabled + +extern bool load_log_settings(const char *filename); + + + + +#endif /*LOGSYS_H_*/ + + + + + + diff --git a/common/logsys_eqemu.cpp b/common/logsys_eqemu.cpp new file mode 100644 index 000000000..56f4f2c5b --- /dev/null +++ b/common/logsys_eqemu.cpp @@ -0,0 +1,43 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "debug.h" +#include "logsys.h" +#include +#include +#include + +void log_message(LogType type, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + log_messageVA(type, fmt, args); + va_end(args); +} + +void log_messageVA(LogType type, const char *fmt, va_list args) { + char prefix_buffer[256]; + snprintf(prefix_buffer, 255, "[%s] ", log_type_info[type].name); + prefix_buffer[255] = '\0'; + + LogFile->writePVA(EQEMuLog::Debug, prefix_buffer, fmt, args); +} + + + + + diff --git a/common/logtypes.h b/common/logtypes.h new file mode 100644 index 000000000..156b22f18 --- /dev/null +++ b/common/logtypes.h @@ -0,0 +1,265 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 LOG_CATEGORY +#define LOG_CATEGORY(name) +#endif +#ifndef LOG_TYPE +#define LOG_TYPE(cat, type, default_value) +#endif +#ifndef ENABLED +#define ENABLED true +#endif +#ifndef DISABLED +#define DISABLED false +#endif + + + + +LOG_CATEGORY( CHAT ) +LOG_TYPE( CHAT, SAY, DISABLED ) +LOG_TYPE( CHAT, EMOTE, DISABLED ) +LOG_TYPE( CHAT, OOC, DISABLED ) +LOG_TYPE( CHAT, GROUP, DISABLED ) +LOG_TYPE( CHAT, GUILD, DISABLED ) + +LOG_CATEGORY( MAIL ) +LOG_TYPE( MAIL, INIT, ENABLED ) +LOG_TYPE( MAIL, ERROR, ENABLED ) +LOG_TYPE( MAIL, CLIENT, DISABLED ) +LOG_TYPE( MAIL, TRACE, DISABLED ) +LOG_TYPE( MAIL, PACKETS, DISABLED) + +LOG_CATEGORY( CHANNELS ) +LOG_TYPE( CHANNELS, INIT, ENABLED ) +LOG_TYPE( CHANNELS, ERROR, ENABLED ) +LOG_TYPE( CHANNELS, CLIENT, DISABLED ) +LOG_TYPE( CHANNELS, TRACE, DISABLED ) +LOG_TYPE( CHANNELS, PACKETS, DISABLED) + +LOG_CATEGORY( UCS ) +LOG_TYPE( UCS, INIT, ENABLED ) +LOG_TYPE( UCS, ERROR, ENABLED ) +LOG_TYPE( UCS, CLIENT, DISABLED ) +LOG_TYPE( UCS, TRACE, DISABLED ) +LOG_TYPE( UCS, PACKETS, DISABLED) + +LOG_CATEGORY( QUERYSERV ) +LOG_TYPE( QUERYSERV, INIT, ENABLED ) +LOG_TYPE( QUERYSERV, ERROR, ENABLED ) +LOG_TYPE( QUERYSERV, CLIENT, DISABLED ) +LOG_TYPE( QUERYSERV, TRACE, DISABLED ) +LOG_TYPE( QUERYSERV, PACKETS, DISABLED) + +LOG_CATEGORY( SPAWNS ) +LOG_TYPE( SPAWNS, MAIN, DISABLED ) +LOG_TYPE( SPAWNS, CONDITIONS, DISABLED ) +LOG_TYPE( SPAWNS, LIMITS, DISABLED ) + +LOG_CATEGORY( AI ) +LOG_TYPE( AI, ERROR, ENABLED ) +LOG_TYPE( AI, WAYPOINTS, DISABLED ) +LOG_TYPE( AI, BUFFS, DISABLED ) +LOG_TYPE( AI, SPELLS, DISABLED ) + +LOG_CATEGORY( PATHING) +LOG_TYPE( PATHING, DEBUG, DISABLED ) + +LOG_CATEGORY( QUESTS ) +LOG_TYPE( QUESTS, PATHING, DISABLED ) + +LOG_CATEGORY( SPELLS ) +LOG_TYPE( SPELLS, LOAD, DISABLED ) +LOG_TYPE( SPELLS, LOAD_ERR, DISABLED ) +LOG_TYPE( SPELLS, CASTING_ERR, DISABLED ) +LOG_TYPE( SPELLS, CASTING, DISABLED ) +LOG_TYPE( SPELLS, EFFECT_VALUES, DISABLED ) +LOG_TYPE( SPELLS, RESISTS, DISABLED ) +LOG_TYPE( SPELLS, STACKING, DISABLED ) +LOG_TYPE( SPELLS, BARDS, DISABLED ) +LOG_TYPE( SPELLS, BUFFS, DISABLED ) +LOG_TYPE( SPELLS, PROCS, DISABLED ) +LOG_TYPE( SPELLS, MODIFIERS, DISABLED ) +LOG_TYPE( SPELLS, CRITS, DISABLED ) +LOG_TYPE( SPELLS, REZ, DISABLED ) + +LOG_CATEGORY( FACTION ) + +LOG_CATEGORY( ZONE ) +LOG_TYPE( ZONE, GROUND_SPAWNS, DISABLED ) +LOG_TYPE( ZONE, INIT, ENABLED ) +LOG_TYPE( ZONE, INIT_ERR, ENABLED ) +LOG_TYPE( ZONE, WORLD, ENABLED ) +LOG_TYPE( ZONE, WORLD_ERR, ENABLED ) +LOG_TYPE( ZONE, WORLD_TRACE, DISABLED ) + +LOG_CATEGORY( TASKS ) +LOG_TYPE( TASKS, GLOBALLOAD, DISABLED ) +LOG_TYPE( TASKS, CLIENTLOAD, DISABLED ) +LOG_TYPE( TASKS, UPDATE, DISABLED ) +LOG_TYPE( TASKS, CLIENTSAVE, DISABLED ) +LOG_TYPE( TASKS, PACKETS, DISABLED ) +LOG_TYPE( TASKS, PROXIMITY, DISABLED ) + + +LOG_CATEGORY( TRADING ) +LOG_TYPE( TRADING, ERROR, ENABLED ) +LOG_TYPE( TRADING, CLIENT, DISABLED ) +LOG_TYPE( TRADING, NPC, DISABLED ) +LOG_TYPE( TRADING, HOLDER, DISABLED ) +LOG_TYPE( TRADING, BARTER, DISABLED ) +LOG_TYPE( TRADING, PACKETS, DISABLED ) + +LOG_CATEGORY( INVENTORY ) +LOG_TYPE( INVENTORY, ERROR, ENABLED ) +LOG_TYPE( INVENTORY, SLOTS, ENABLED ) +LOG_TYPE( INVENTORY, BANDOLIER, ENABLED ) + +LOG_CATEGORY( TRADESKILLS ) +LOG_TYPE( TRADESKILLS, IN, DISABLED ) +LOG_TYPE( TRADESKILLS, OUT, DISABLED ) +LOG_TYPE( TRADESKILLS, SQL, DISABLED ) +LOG_TYPE( TRADESKILLS, TRACE, DISABLED ) + +LOG_CATEGORY( TRIBUTE ) +LOG_TYPE( TRIBUTE, ERROR, DISABLED ) +LOG_TYPE( TRIBUTE, IN, DISABLED ) +LOG_TYPE( TRIBUTE, OUT, DISABLED ) + +LOG_CATEGORY( AA ) +LOG_TYPE( AA, ERROR, ENABLED ) +LOG_TYPE( AA, MESSAGE, DISABLED ) +LOG_TYPE( AA, IN, DISABLED ) +LOG_TYPE( AA, OUT, DISABLED ) +LOG_TYPE( AA, BONUSES, DISABLED ) + + +LOG_CATEGORY( DOORS ) +LOG_TYPE( DOORS, INFO, DISABLED ) + +LOG_CATEGORY( PETS ) +LOG_TYPE( PETS, AGGRO, DISABLED ) + +LOG_CATEGORY( COMBAT ) +LOG_TYPE( COMBAT, ATTACKS, DISABLED ) +LOG_TYPE( COMBAT, TOHIT, DISABLED ) +LOG_TYPE( COMBAT, MISSES, DISABLED ) +LOG_TYPE( COMBAT, DAMAGE, DISABLED ) +LOG_TYPE( COMBAT, HITS, DISABLED ) +LOG_TYPE( COMBAT, RANGED, DISABLED ) +LOG_TYPE( COMBAT, SPECIAL_ATTACKS, DISABLED ) +LOG_TYPE( COMBAT, PROCS, DISABLED ) + +LOG_CATEGORY( GUILDS ) +LOG_TYPE( GUILDS, ERROR, ENABLED ) +LOG_TYPE( GUILDS, ACTIONS, ENABLED ) +LOG_TYPE( GUILDS, DB, DISABLED ) +LOG_TYPE( GUILDS, PERMISSIONS, DISABLED ) +LOG_TYPE( GUILDS, REFRESH, DISABLED ) //inter-zone refresh comm +LOG_TYPE( GUILDS, IN_PACKETS, DISABLED ) +LOG_TYPE( GUILDS, OUT_PACKETS, DISABLED ) +LOG_TYPE( GUILDS, IN_PACKET_TRACE, DISABLED ) //hex dumps +LOG_TYPE( GUILDS, OUT_PACKET_TRACE, DISABLED ) //hex dumps +LOG_TYPE( GUILDS, BANK_ERROR, ENABLED ) + +LOG_CATEGORY( CLIENT ) +LOG_TYPE( CLIENT, ERROR, ENABLED ) +LOG_TYPE( CLIENT, DUELING, DISABLED ) +LOG_TYPE( CLIENT, SPELLS, DISABLED ) +LOG_TYPE( CLIENT, NET_ERR, ENABLED ) +LOG_TYPE( CLIENT, NET_IN_TRACE, DISABLED ) +LOG_TYPE( CLIENT, EXP, DISABLED ) + +LOG_CATEGORY( SKILLS ) +LOG_TYPE( SKILLS, GAIN, DISABLED ) + +LOG_CATEGORY( RULES ) +LOG_TYPE( RULES, ERROR, DISABLED ) +LOG_TYPE( RULES, CHANGE, DISABLED ) + +LOG_CATEGORY( NET ) +LOG_TYPE( NET, WORLD, ENABLED ) +LOG_TYPE( NET, OPCODES, ENABLED ) +LOG_TYPE( NET, IDENTIFY, ENABLED ) +LOG_TYPE( NET, IDENT_TRACE, ENABLED ) +LOG_TYPE( NET, STRUCTS, ENABLED ) +LOG_TYPE( NET, STRUCT_HEX, ENABLED ) +LOG_TYPE( NET, ERROR, ENABLED ) +LOG_TYPE( NET, DEBUG, DISABLED ) +LOG_TYPE( NET, APP_TRACE, DISABLED ) +LOG_TYPE( NET, APP_CREATE, DISABLED ) +LOG_TYPE( NET, APP_CREATE_HEX, DISABLED ) +LOG_TYPE( NET, NET_TRACE, DISABLED ) +LOG_TYPE( NET, NET_COMBINE, DISABLED ) +LOG_TYPE( NET, FRAGMENT, DISABLED ) +LOG_TYPE( NET, FRAGMENT_HEX, DISABLED ) +LOG_TYPE( NET, NET_CREATE, DISABLED ) +LOG_TYPE( NET, NET_CREATE_HEX, DISABLED ) +LOG_TYPE( NET, NET_ACKS, DISABLED ) +LOG_TYPE( NET, RATES, DISABLED ) + +LOG_CATEGORY( DATABASE ) + +LOG_CATEGORY( COMMON ) +LOG_TYPE( COMMON, ERROR, ENABLED ) +LOG_TYPE( COMMON, THREADS, ENABLED ) + +LOG_CATEGORY( LAUNCHER ) +LOG_TYPE( LAUNCHER, ERROR, ENABLED ) +LOG_TYPE( LAUNCHER, INIT, ENABLED ) +LOG_TYPE( LAUNCHER, STATUS, ENABLED ) +LOG_TYPE( LAUNCHER, NET, ENABLED ) +LOG_TYPE( LAUNCHER, WORLD, ENABLED ) + +LOG_CATEGORY( WORLD ) +LOG_TYPE( WORLD, CONFIG, ENABLED ) +LOG_TYPE( WORLD, INIT, ENABLED ) +LOG_TYPE( WORLD, INIT_ERR, ENABLED ) +LOG_TYPE( WORLD, CLIENT, ENABLED ) +LOG_TYPE( WORLD, ZONE, ENABLED ) +LOG_TYPE( WORLD, LS, ENABLED ) +LOG_TYPE( WORLD, CLIENT_ERR, ENABLED ) +LOG_TYPE( WORLD, ZONE_ERR, ENABLED ) +LOG_TYPE( WORLD, LS_ERR, ENABLED ) +LOG_TYPE( WORLD, SHUTDOWN, ENABLED ) +LOG_TYPE( WORLD, CLIENTLIST, DISABLED ) +LOG_TYPE( WORLD, CLIENTLIST_ERR, ENABLED ) +LOG_TYPE( WORLD, ZONELIST, ENABLED ) +LOG_TYPE( WORLD, ZONELIST_ERR, ENABLED ) +LOG_TYPE( WORLD, CLIENT_TRACE, DISABLED ) +LOG_TYPE( WORLD, ZONE_TRACE, DISABLED ) +LOG_TYPE( WORLD, LS_TRACE, DISABLED ) +LOG_TYPE( WORLD, CONSOLE, ENABLED ) +LOG_TYPE( WORLD, HTTP, ENABLED ) +LOG_TYPE( WORLD, HTTP_ERR, ENABLED ) +LOG_TYPE( WORLD, PERL, ENABLED ) +LOG_TYPE( WORLD, PERL_ERR, ENABLED ) +LOG_TYPE( WORLD, EQW, ENABLED ) +LOG_TYPE( WORLD, LAUNCH, ENABLED ) +LOG_TYPE( WORLD, LAUNCH_ERR, ENABLED ) +LOG_TYPE( WORLD, LAUNCH_TRACE, ENABLED ) + +#undef LOG_TYPE +#undef LOG_CATEGORY + + + + diff --git a/common/mail_oplist.h b/common/mail_oplist.h new file mode 100644 index 000000000..f10e62be0 --- /dev/null +++ b/common/mail_oplist.h @@ -0,0 +1,13 @@ +//Mail and Chat Channels +N(OP_MailLogin), +N(OP_Mail), +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), diff --git a/common/md5.cpp b/common/md5.cpp new file mode 100644 index 000000000..f44b590bb --- /dev/null +++ b/common/md5.cpp @@ -0,0 +1,271 @@ +/* md5.c -- An implementation of Ron Rivest's MD5 message-digest algorithm. +* Written by Colin Plumb in 1993, no copyright is claimed. This code is in the +* public domain; do with it what you wish. Equivalent code is available from +* RSA Data Security, Inc. This code does not oblige you to include legal +* boilerplate in the documentation. To compute the message digest of a string +* of bytes, declare an MD5Context structure, pass it to MD5Init, call +* MD5Update as needed on buffers full of bytes, and then call MD5Final, which +* will fill a supplied 16-byte array with the digest. +*/ +#include /* for memcpy() */ +#include "../common/md5.h" +#include "../common/MiscFunctions.h" +#include "../common/seperator.h" + +MD5::MD5() { + memset(pMD5, 0, 16); +} + +MD5::MD5(const uchar* buf, uint32 len) { + Generate(buf, len, pMD5); +} + +MD5::MD5(const char* buf, uint32 len) { + Generate((const uchar*) buf, len, pMD5); +} + +MD5::MD5(const uint8 buf[16]) { + Set(buf); +} + +MD5::MD5(const char* iMD5String) { + Set(iMD5String); +} + +void MD5::Generate(const char* iString) { + Generate((const uchar*) iString, strlen(iString)); +} + +void MD5::Generate(const uint8* buf, uint32 len) { + Generate(buf, len, pMD5); +} + +bool MD5::Set(const uint8 buf[16]) { + memcpy(pMD5, buf, 16); + return true; +} + +bool MD5::Set(const char* iMD5String) { + char tmp[5] = { '0', 'x', 0, 0, 0 }; + for (int i=0; i<16; i++) { + tmp[2] = iMD5String[i*2]; + tmp[3] = iMD5String[(i*2) + 1]; + if (!Seperator::IsHexNumber(tmp)) + return false; + pMD5[i] = hextoi(tmp); + } + return true; +} + +MD5::operator const char* () { + snprintf(pMD5String, sizeof(pMD5String), "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", pMD5[0], pMD5[1], pMD5[2], pMD5[3], pMD5[4], pMD5[5], pMD5[6], pMD5[7], pMD5[8], pMD5[9], pMD5[10], pMD5[11], pMD5[12], pMD5[13], pMD5[14], pMD5[15]); + return pMD5String; +} + +bool MD5::operator== (const MD5& iMD5) { + if (memcmp(pMD5, iMD5.pMD5, 16) == 0) + return true; + else + return false; +} + +bool MD5::operator== (const uint8* iMD5) { + if (memcmp(pMD5, iMD5, 16) == 0) + return true; + else + return false; +} + +bool MD5::operator== (const char* iMD5String) { + char tmp[5] = { '0', 'x', 0, 0, 0 }; + for (int i=0; i<16; i++) { + tmp[2] = iMD5String[i*2]; + tmp[3] = iMD5String[(i*2) + 1]; + if (pMD5[i] != hextoi(tmp)) + return false; + } + return true; +} + +MD5& MD5::operator= (const MD5& iMD5) { + memcpy(pMD5, iMD5.pMD5, 16); + return *this; +} + +MD5* MD5::operator= (const MD5* iMD5) { + memcpy(pMD5, iMD5->pMD5, 16); + return this; +} + +/* Byte-swap an array of words to little-endian. (Byte-sex independent) */ +void MD5::byteSwap(uint32 *buf, uint32 words) { + uint8 *p = (uint8 *)buf; + do { + *buf++ = (uint32)((uint32)p[3]<<8 | p[2]) << 16 | + ((uint32)p[1]<<8 | p[0]); + p += 4; + } while (--words); +} + +void MD5::Generate(const uint8* buf, uint32 len, uint8 digest[16]) { + MD5Context ctx; + Init(&ctx); + Update(&ctx, buf, len); + Final(digest, &ctx); +} + +/* Start MD5 accumulation. */ +void MD5::Init(struct MD5Context *ctx) { + ctx->hash[0] = 0x67452301; + ctx->hash[1] = 0xefcdab89; + ctx->hash[2] = 0x98badcfe; + ctx->hash[3] = 0x10325476; + ctx->bytes[1] = ctx->bytes[0] = 0; +} + +/* Update ctx to reflect the addition of another buffer full of bytes. */ +void MD5::Update(struct MD5Context *ctx, uint8 const *buf, uint32 len) { + uint32 t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) /* Update 64-bit byte count */ + ctx->bytes[1]++; /* Carry from low to high */ + + + + t = 64 - (t & 0x3f); /* Bytes available in ctx->input (>= 1) */ + if (t > len) { + memcpy((uint8*)ctx->input+64-t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((uint8*)ctx->input+64-t, buf, t); + byteSwap(ctx->input, 16); + Transform(ctx->hash, ctx->input); + buf += t; + len -= t; + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->input, buf, 64); + byteSwap(ctx->input, 16); + Transform(ctx->hash, ctx->input); + buf += 64; + len -= 64; + } + /* Buffer any remaining bytes of data */ + memcpy(ctx->input, buf, len); +} + +/* Final wrapup - pad to 64-byte boundary with the bit pattern +* 1 0* (64-bit count of bits processed, LSB-first) */ +void MD5::Final(uint8 digest[16], MD5Context *ctx) { + int count = ctx->bytes[0] & 0x3F; /* Bytes mod 64 */ + uint8 *p = (uint8*)ctx->input + count; + /* Set the first byte of padding to 0x80. There is always room. */ + *p++ = 0x80; + /* Bytes of zero padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count+8); + byteSwap(ctx->input, 16); + Transform(ctx->hash, ctx->input); + p = (uint8*)ctx->input; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->input, 14); + /* Append 8 bytes of length in *bits* and transform */ + ctx->input[14] = ctx->bytes[0] << 3; + + ctx->input[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + Transform(ctx->hash, ctx->input); + byteSwap(ctx->hash, 4); + memcpy(digest, ctx->hash, 16); + memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ +} + +/* The four core functions */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) (w += f(x,y,z)+in, w = (w<>(32-s)) + x) + + + +/* The heart of the MD5 algorithm. */ +void MD5::Transform(uint32 hash[4], const uint32 input[16]) { + register uint32 a = hash[0], b = hash[1], c = hash[2], d = hash[3]; + + MD5STEP(F1, a, b, c, d, input[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, input[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, input[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, input[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, input[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, input[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, input[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, input[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, input[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, input[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, input[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, input[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, input[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, input[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, input[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, input[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, input[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, input[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, input[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, input[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, input[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, input[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, input[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, input[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, input[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, input[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, input[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, input[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, input[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, input[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, input[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, input[12]+0x8d2a4c8a, 20); + + + + + MD5STEP(F3, a, b, c, d, input[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, input[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, input[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, input[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, input[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, input[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, input[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, input[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, input[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, input[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, input[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, input[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, input[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, input[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, input[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, input[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, input[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, input[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, input[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, input[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, input[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, input[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, input[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, input[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, input[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, input[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, input[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, input[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, input[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, input[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, input[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, input[ 9]+0xeb86d391, 21); + + hash[0] += a; hash[1] += b; hash[2] += c; hash[3] += d; +} diff --git a/common/md5.h b/common/md5.h new file mode 100644 index 000000000..958443c96 --- /dev/null +++ b/common/md5.h @@ -0,0 +1,45 @@ +#ifndef MD5_H +#define MD5_H +#include "../common/types.h" + + +class MD5 { +public: + struct MD5Context { + uint32 hash[4]; + uint32 bytes[2]; + uint32 input[16]; + }; + static void Generate(const uint8* buf, uint32 len, uint8 digest[16]); + + static void Init(struct MD5Context *context); + static void Update(struct MD5Context *context, const uint8 *buf, uint32 len); + static void Final(uint8 digest[16], struct MD5Context *context); + + MD5(); + MD5(const uchar* buf, uint32 len); + MD5(const char* buf, uint32 len); + MD5(const uint8 buf[16]); + MD5(const char* iMD5String); + + void Generate(const char* iString); + void Generate(const uint8* buf, uint32 len); + bool Set(const uint8 buf[16]); + bool Set(const char* iMD5String); + + bool operator== (const MD5& iMD5); + bool operator== (const uint8 iMD5[16]); + bool operator== (const char* iMD5String); + + MD5& operator= (const MD5& iMD5); + MD5* operator= (const MD5* iMD5); + MD5* operator= (const uint8* iMD5); + operator const char* (); +protected: + uint8 pMD5[16]; +private: + static void byteSwap(uint32 *buf, uint32 words); + static void Transform(uint32 hash[4], const uint32 input[16]); + char pMD5String[33]; +}; +#endif diff --git a/common/misc.cpp b/common/misc.cpp new file mode 100644 index 000000000..9fe01154a --- /dev/null +++ b/common/misc.cpp @@ -0,0 +1,574 @@ +#ifdef _WINDOWS + // VS6 doesn't like the length of STL generated names: disabling + #pragma warning(disable:4786) +#endif +#include "debug.h" +#include +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#endif +#include +#include "misc.h" +#include "types.h" +#include +#include + +using namespace std; + +#define ENC(c) (((c) & 0x3f) + ' ') +#define DEC(c) (((c) - ' ') & 0x3f) + +map DBFieldNames; + +#ifndef WIN32 +#if defined(FREEBSD) || defined(__CYGWIN__) +int print_stacktrace() +{ + printf("Insert stack trace here...\n"); + return(0); +} +#else //!WIN32 && !FREEBSD == linux +#include +int print_stacktrace() +{ + void *ba[20]; + int n = backtrace (ba, 20); + if (n != 0) + { + char **names = backtrace_symbols (ba, n); + if (names != NULL) + { + int i; + cerr << "called from " << (char*)names[0] << endl; + for (i = 1; i < n; ++i) + cerr << " " << (char*)names[i] << endl; + free (names); + } + } + return(0); +} +#endif //!FREEBSD +#endif //!WIN32 + +void Unprotect(string &s, char what) +{ + if (s.length()) { + for(string::size_type i=0;i fields_list + each fields_list is a map of field index -> value +*/ +bool ItemParse(const char *data, int length, map > &items, int id_pos, int name_pos, int max_field, int level) +{ +int i; +char *end,*ptr; +map field; +static char *buffer=NULL; +static int buffsize=0; +static char *temp=NULL; + if (!buffsize || buffsize<(length+1)) { + buffer=(char *)realloc(buffer,length+1); + temp=(char *)realloc(temp,length+1); + buffsize=length+1; + } + memcpy(buffer,data,length); + buffer[length]=0; + + ptr=buffer; + + for(i=0;i & tokens, char delim) +{ +int i,len; +string::size_type end; +//char temp[1024]; +string x; + tokens.clear(); + i=0; + while(s.length()) { + if (s[0]==delim) { + s.erase(0,1); + tokens[i++]=""; + } else { + end=0; + while((end=s.find(delim,end+1))!=string::npos && s[end-1]=='\\'); + if (end!=string::npos) { + x=s; + x.erase(end,string::npos); + s.erase(0,end+1); + Unprotect(x,'|'); + tokens[i++]=x; + } else { + Unprotect(s,'|'); + tokens[i++]=s; + break; + } + } + len=0; + } + return i; +} + +void LoadItemDBFieldNames() { + DBFieldNames[0]="N/A"; // Charges + DBFieldNames[1]="unknown002"; // ? + DBFieldNames[2]="N/A"; // Current Equip Slot + DBFieldNames[3]="unknown004"; + DBFieldNames[4]="unknown005"; // ? + DBFieldNames[5]="itemclass"; // "Item Type (0=common, 1=container, 2=book)" + DBFieldNames[6]="name"; // Name + DBFieldNames[7]="lore"; // "Lore Name (*=lore, &=summoned, #=artifact)" + DBFieldNames[8]="idfile"; // IDFile + DBFieldNames[9]="id"; // ItemNumber + DBFieldNames[10]="weight"; // Weight + DBFieldNames[11]="norent"; // "NoRent (0=norent, 255=not norent)" + DBFieldNames[12]="nodrop"; // "NoDrop (0=nodrop, 255=not nodrop)" + DBFieldNames[13]="size"; // "Size (0=tiny, 1=small, 2=medium, 3=large, 4=giant)" + DBFieldNames[14]="slots"; // EquipSlots + DBFieldNames[15]="cost"; // Cost + DBFieldNames[16]="icon"; // IconNumber + DBFieldNames[17]="unknown018"; + DBFieldNames[18]="unknown019"; + DBFieldNames[19]="unknown020"; // ? + DBFieldNames[20]="tradeskills"; // "Tradeskill Item (1=is a tradeskill item, 0=not)" + DBFieldNames[21]="cr"; // SvCold + DBFieldNames[22]="dr"; // SvDisease + DBFieldNames[23]="pr"; // SvPoison + DBFieldNames[24]="mr"; // SvMagic + DBFieldNames[25]="fr"; // SvFire + DBFieldNames[26]="astr"; // STR + DBFieldNames[27]="asta"; // STA + DBFieldNames[28]="aagi"; // AGI + DBFieldNames[29]="adex"; // DEX + DBFieldNames[30]="acha"; // CHA + DBFieldNames[31]="aint"; // INT + DBFieldNames[32]="awis"; // WIS + DBFieldNames[33]="hp"; // HP + DBFieldNames[34]="mana"; // Mana + DBFieldNames[35]="ac"; // AC + DBFieldNames[36]="deity"; // Deity + DBFieldNames[37]="skillmodvalue"; // Skill Mod Value + DBFieldNames[38]="skillmodtype"; // Skill Mod Type + DBFieldNames[39]="banedmgrace"; // Bane Dmg Race + DBFieldNames[40]="banedmgamt"; // Band Dmg + DBFieldNames[41]="banedmgbody"; // Band Dmg Body + DBFieldNames[42]="magic"; // "Magic (0=not magic, 1=magic)" + DBFieldNames[43]="casttime2"; // Casttime appears twice + DBFieldNames[44]="hasteproclvl"; // "Level (Haste value, rather)" + DBFieldNames[45]="reqlevel"; // Required Level + DBFieldNames[46]="bardtype"; // Bard Type + DBFieldNames[47]="bardvalue"; // Bard Type Amount + DBFieldNames[48]="light"; // Light + DBFieldNames[49]="delay"; // Attack Delay + DBFieldNames[50]="reclevel"; // Recommended Level + DBFieldNames[51]="recskill"; // Recommended Skill + DBFieldNames[52]="elemdmgamt"; // "Elemental Dmg Type (1=magic, 2=fire, 3=cold, 4=poison, 5=disease)" + DBFieldNames[53]="elemdmgtype"; // Elemental Dmg + DBFieldNames[54]="effecttype"; // "Effect Type (0=combat, 1=clicky, 2=Worn, 3=Expendable charges, 4=Must Equip Clicky, 5=clicky)" + DBFieldNames[55]="range"; // Range + DBFieldNames[56]="damage"; // Damage + DBFieldNames[57]="color"; // Color + DBFieldNames[58]="classes"; // Classes + DBFieldNames[59]="races"; // Races + DBFieldNames[60]="unknown061"; + DBFieldNames[61]="spellid"; // SpellId + DBFieldNames[62]="maxcharges"; // MaxCharges + DBFieldNames[63]="itemtype"; // "Skill (ItemType: 1hs, etc)" + DBFieldNames[64]="material"; // Material + DBFieldNames[65]="sellrate"; // ** Sell Rate + DBFieldNames[66]="unknown067"; + DBFieldNames[67]="casttime"; // CastTime (milliseconds) + DBFieldNames[68]="unknown069"; + DBFieldNames[69]="unknown070"; // ? + DBFieldNames[70]="focusid"; // Focus Effect Spell Id + DBFieldNames[71]="combateffects"; // CombatEffects + DBFieldNames[72]="shielding"; // Shielding + DBFieldNames[73]="stunresist"; // StunResist + DBFieldNames[74]="strikethrough"; // StrikeThrough + DBFieldNames[75]="unknown076"; + DBFieldNames[76]="unknown077"; // ? + DBFieldNames[77]="spellshield"; // Spell Shield + DBFieldNames[78]="avoidance"; // Avoidance + DBFieldNames[79]="accuracy"; // Accuracy + DBFieldNames[80]="factionmod1"; // Faction Mod Index 1 + DBFieldNames[81]="factionmod2"; // Faction Mod Index 2 + DBFieldNames[82]="factionmod3"; // Faction Mod Index 3 + DBFieldNames[83]="factionmod4"; // Faction Mod Index 4 + DBFieldNames[84]="factionamt1"; // Faction Mod Value 1 + DBFieldNames[85]="factionamt2"; // Faction Mod Value 2 + DBFieldNames[86]="factionamt3"; // Faction Mod Value 3 + DBFieldNames[87]="factionamt4"; // Faction Mod Value 4 + DBFieldNames[88]="unknown089"; + DBFieldNames[89]="charmfile"; // ** Charm File + DBFieldNames[90]="unknown091"; + DBFieldNames[91]="augslot1type"; // Slot1Type + DBFieldNames[92]="augslot2type"; // Slot2Type + DBFieldNames[93]="augslot3type"; // Slot3Type + DBFieldNames[94]="augslot4type"; // Slot4Type + DBFieldNames[95]="augslot5type"; // Slot5Type + DBFieldNames[96]="ldonpointtheme"; + DBFieldNames[97]="ldonpointcost"; // ? + DBFieldNames[98]="unknown099"; + DBFieldNames[99]="bagtype"; // bag type + DBFieldNames[100]="bagslots"; // bag slots + DBFieldNames[101]="bagsize"; // bag size capacity + DBFieldNames[102]="bagwr"; // bag weight reduction + DBFieldNames[103]="booktype"; // "book type (0=rolled up note, 1=book)" + DBFieldNames[104]="unknown105"; + DBFieldNames[105]="filename"; // Book Filename + DBFieldNames[106]="unknown107"; + DBFieldNames[107]="unknown108"; + DBFieldNames[108]="loreflag"; + DBFieldNames[109]="unknown111"; + DBFieldNames[110]="unknown112"; + DBFieldNames[111]="unknown113"; + DBFieldNames[112]="unknown114"; + DBFieldNames[113]="unknown115"; // ? (end quote) +} + +void encode_length(unsigned long length, char *out) +{ +char buf[4]; + memcpy(buf,&length,sizeof(unsigned long)); + encode_chunk(buf,3,out); +} + +unsigned long encode(char *in, unsigned long length, char *out) +{ +unsigned long used=0,len=0; + while(used> 2); + *(out+1)=ENC((in[0] << 4)|(((len<2 ? 0 : in[1]) >> 4) & 0xF)); + *(out+2)=ENC(((len<2 ? 0 : in[1]) << 2)|(((len<3 ? 0 : in[2]) >> 6) & 0x3)); + *(out+3)=ENC((len<3 ? 0 : in[2])); +} + +void decode_chunk(char *in, char *out) +{ + *out = DEC(*in) << 2 | DEC(in[1]) >> 4; + *(out+1) = DEC(in[1]) << 4 | DEC(in[2]) >> 2; + *(out+2) = DEC(in[2]) << 6 | DEC(in[3]); +} + +void dump_message_column(unsigned char *buffer, unsigned long length, string leader, FILE *to) +{ +unsigned long i,j; +unsigned long rows,offset=0; + rows=(length/16)+1; + for(i=0;i +#include +#include + +using namespace std; + +#define ITEMFIELDCOUNT 116 + +void Unprotect(string &s, char what); + +void Protect(string &s, char what); + +bool ItemParse(const char *data, int length, map > &items, int id_pos, int name_pos, int max_field, int level=0); + +int Tokenize(string s, map & tokens, char delim='|'); + +void LoadItemDBFieldNames(); + +void encode_length(unsigned long length, char *out); +unsigned long decode_length(char *in); +unsigned long encode(char *in, unsigned long length, char *out); +void decode(char *in, char *out); +void encode_chunk(char *in, int len, char *out); +void decode_chunk(char *in, char *out); + +#ifndef WIN32 +int print_stacktrace(); +#endif + +void dump_message_column(unsigned char *buffer, unsigned long length, string leader="", FILE *to = stdout); +string string_from_time(string pattern, time_t now=0); +string timestamp(time_t now=0); +string long2ip(unsigned long ip); +string pop_arg(string &s, string seps, bool obey_quotes); +int EQsprintf(char *buffer, const char *pattern, const char *arg1, const char *arg2, const char *arg3, const char *arg4, const char *arg5, const char *arg6, const char *arg7, const char *arg8, const char *arg9); +string generate_key(int length); +void build_hex_line(const char *buffer, unsigned long length, unsigned long offset, char *out_buffer, unsigned char padding=4); +void print_hex(const char *buffer, unsigned long length); + +#endif diff --git a/common/moremath.cpp b/common/moremath.cpp new file mode 100644 index 000000000..a3140a2a8 --- /dev/null +++ b/common/moremath.cpp @@ -0,0 +1,45 @@ +/* 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 "../common/debug.h" + +// Quagmire - i was really surprised, but i couldnt find the equivilent standard library function +signed char sign(signed int tmp) { + if (tmp > 0) + return 1; + else if (tmp < 0) + return -1; + else + return 0; +} + +signed char sign(double tmp) { + if (tmp > 0) + return 1; + else if (tmp < 0) + return -1; + else + return 0; +} + +uint32 pow32(uint32 base, uint32 exponet) { + uint32 ret = 1; + for (uint32 i=1; i<=exponet; i++) + ret *= base; + return ret; +} + diff --git a/common/moremath.h b/common/moremath.h new file mode 100644 index 000000000..1f64d0462 --- /dev/null +++ b/common/moremath.h @@ -0,0 +1,26 @@ +/* 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 MOREMATH_H +#define MOREMATH_H + +signed char sign(signed int tmp); +signed char sign(double tmp); +uint32 pow32(uint32 base, uint32 exponet); + +#endif + diff --git a/common/op_codes.h b/common/op_codes.h new file mode 100644 index 000000000..4142c61f3 --- /dev/null +++ b/common/op_codes.h @@ -0,0 +1,19 @@ +#ifndef _OP_CODES_H + +#define _OP_CODES_H + +static const char OP_SessionRequest = 0x01; +static const char OP_SessionResponse = 0x02; +static const char OP_Combined = 0x03; +static const char OP_SessionDisconnect = 0x05; +static const char OP_KeepAlive = 0x06; +static const char OP_SessionStatRequest = 0x07; +static const char OP_SessionStatResponse= 0x08; +static const char OP_Packet = 0x09; +static const char OP_Fragment = 0x0d; +static const char OP_OutOfOrderAck = 0x11; +static const char OP_Ack = 0x15; +static const char OP_AppCombined = 0x19; +static const char OP_OutOfSession = 0x1d; + +#endif diff --git a/common/opcode_dispatch.h b/common/opcode_dispatch.h new file mode 100644 index 000000000..a57408b66 --- /dev/null +++ b/common/opcode_dispatch.h @@ -0,0 +1,636 @@ + +/* + +missing: +OP_TGB +OP_GuildMemberList + +*/ + + +//opcode_dispatch.h + +/* + +There are currently eight (times two) dispatch modes possible, the different +modes for a given state differ only in the arguments which +are passed to the dispatch function. + +The variable length versions ensure that the packet is +AT LEAST as long as the supplied structure. + +The variable length versions should only be used when the packet +is truely variable length, and usage of the raw versions should +be avoided as much as reasonable. There is almost no valid reason +to use the raw version. Use the zero length version when the packet +should be zero length. + +the 'struct' passed can also be a raw type like 'float' or 'uint32' +in the case that the packet only contains that single variable. + +Connecting state: +IN_C(opcode, struct) -> Typed dispatch in connecting mode + Dispatch Prototype: + void Client::Handle_Connect_opcode(struct *packet); +IN_Cv(opcode, struct) -> Typed dispatch in connecting mode, variable length + Dispatch Prototype: + void Client::Handle_Connect_opcode(struct *packet, uint32 length); +IN_Cz(opcode) -> Zero length dispatch in connecting mode + Dispatch Prototype: + void Client::Handle_Connect_opcode(); +IN_Cr(opcode) -> Raw dispatch in connecting mode + Dispatch Prototype: + void Client::Handle_Connect_opcode(const EQApplicationPacket *app); + +Connected state: +IN(opcode, struct) -> Typed dispatch in connected mode + Dispatch Prototype: + void Client::Handle_opcode(struct *packet); +INv(opcode, struct) -> Typed dispatch in connected mode, variable length + Dispatch Prototype: + void Client::Handle_opcode(struct *packet, uint32 length); +INz(opcode) -> Zero length dispatch in connected mode + Dispatch Prototype: + void Client::Handle_opcode(); +INr(opcode) -> Raw dispatch in connected mode + Dispatch Prototype: + void Client::Handle_opcode(const EQApplicationPacket *app); + +All of the above functions apply to OUT mode as well. + +lines prefixed with //alt: provide information about alternate +structures for an opcode, mainly used by opcode finder + +*/ + + +//IN_C(OP_SetDataRate, float); +IN_C(OP_ZoneEntry, ClientZoneEntry_Struct); +IN_C(OP_SetServerFilter, SetServerFilter_Struct); +IN_Cz(OP_SendAATable); +IN_Cz(OP_SendTributes); +IN_Cz(OP_SendGuildTributes); +IN_Cz(OP_SendAAStats); +IN_Cz(OP_ReqClientSpawn); +IN_Cz(OP_ReqNewZone); +IN_Cz(OP_SendExpZonein); +//IN_Cr(OP_ZoneComplete); + +//these three should stay raw, since we ignore their contents and +//they overlap with the connected opcodes +IN_Cr(OP_SpawnAppearance); +IN_Cr(OP_WearChange); +IN_Cr(OP_ClientUpdate); + +IN_Cz(OP_ClientReady); +IN_C(OP_ClientError, ClientError_Struct); +IN_C(OP_ApproveZone, ApproveZone_Struct); +IN_Cr(OP_TGB); + +IN(OP_AckPacket, uint32); +IN(OP_ClientUpdate, PlayerPositionUpdateClient_Struct); +IN(OP_AutoAttack, uint32); +IN(OP_AutoAttack2, uint32); +INv(OP_Consent, Consent_Struct); +INv(OP_ConsentDeny, Consent_Struct); +IN(OP_TargetMouse, ClientTarget_Struct); +IN(OP_TargetCommand, ClientTarget_Struct); +IN(OP_Shielding, Shielding_Struct); +INr(OP_Jump); //? +INr(OP_AdventureInfoRequest); +INr(OP_AdventureRequest); +IN(OP_LDoNButton, bool); //not sure on length +INr(OP_LeaveAdventure); +IN(OP_Consume, Consume_Struct); +IN(OP_AdventureMerchantRequest, AdventureMerchant_Struct); +IN(OP_AdventureMerchantPurchase, Adventure_Purchase_Struct); +IN(OP_ConsiderCorpse, Consider_Struct); +IN(OP_Consider, Consider_Struct); +INr(OP_Begging); //? +INr(OP_TestBuff); +IN(OP_Surname, Surname_Struct); +INr(OP_YellForHelp); +IN(OP_Assist, EntityId_Struct); //generic +IN(OP_GMTraining, GMTrainee_Struct); +IN(OP_GMEndTraining, GMTrainEnd_Struct); +IN(OP_GMTrainSkill, GMSkillChange_Struct); +IN(OP_RequestDuel, Duel_Struct); +IN(OP_DuelResponse, DuelResponse_Struct); +IN(OP_DuelResponse2, Duel_Struct); +IN(OP_SpawnAppearance, SpawnAppearance_Struct); +IN(OP_BazaarInspect, BazaarInspect_Struct); +IN(OP_Death, Death_Struct); +IN(OP_MoveCoin, MoveCoin_Struct); +IN(OP_ItemLinkClick, ItemViewRequest_Struct); +IN(OP_MoveItem, MoveItem_Struct); +INz(OP_Camp); //? +INz(OP_Logout); +INz(OP_SenseHeading); //? +INz(OP_FeignDeath); //? +INz(OP_Sneak); //? +INz(OP_Hide); //? +INv(OP_ChannelMessage, ChannelMessage_Struct); +IN(OP_WearChange, WearChange_Struct); +IN(OP_DeleteSpawn, EntityId_Struct); //client->server follows OP_SaveOnZoneReq +IN(OP_SaveOnZoneReq, Save_Struct); //follows OP_ZoneChange +IN(OP_Save, Save_Struct); +IN(OP_WhoAllRequest, Who_All_Struct); +IN(OP_GMZoneRequest, GMZoneRequest_Struct); +IN(OP_GMZoneRequest2, uint32); +IN(OP_EndLootRequest, EntityId_Struct); //follows OP_LootRequest +IN(OP_LootRequest, EntityId_Struct); //entity must be a corpse +IN(OP_Dye, DyeStruct); +INr(OP_ConfirmDelete); //? +IN(OP_LootItem, LootingItem_Struct); +INr(OP_GuildDelete); //? +IN(OP_GuildPublicNote, GuildUpdate_PublicNote); +INz(OP_GetGuildsList); //? +IN(OP_SetGuildMOTD, GuildMOTD_Struct); +INz(OP_GuildPeace); //? +INz(OP_GuildWar); //? +IN(OP_GuildLeader, GuildMakeLeader); +IN(OP_GuildDemote, GuildDemoteStruct); +IN(OP_GuildInvite, GuildCommand_Struct); +IN(OP_GuildRemove, GuildCommand_Struct); +IN(OP_GuildInviteAccept, GuildInviteAccept_Struct); +IN(OP_ManaChange, ManaChange_Struct); //possibly zero len too +//alt:INz(OP_ManaChange); +IN(OP_MemorizeSpell, MemorizeSpell_Struct); +IN(OP_SwapSpell, SwapSpell_Struct); +IN(OP_CastSpell, CastSpell_Struct); +IN(OP_DeleteItem, DeleteItem_Struct); +IN(OP_CombatAbility, CombatAbility_Struct); +IN(OP_Taunt, ClientTarget_Struct); +INz(OP_InstillDoubt); +IN(OP_RezzAnswer, Resurrect_Struct); +IN(OP_GMSummon, GMSummon_Struct); +IN(OP_TradeBusy, TradeBusy_Struct); +IN(OP_TradeRequest, TradeRequest_Struct); +IN(OP_TradeRequestAck, TradeRequest_Struct); //follows OP_TradeRequest +IN(OP_CancelTrade, CancelTrade_Struct); +IN(OP_TradeAcceptClick, TradeAccept_Struct); +IN(OP_BoardBoat, EntityId_Struct); //not really the struct, just 4 bytes +INz(OP_LeaveBoat); //? +IN(OP_RandomReq, RandomReq_Struct); +IN(OP_Buff, SpellBuffFade_Struct); +IN(OP_GMHideMe, SpawnAppearance_Struct); +IN(OP_GMNameChange, GMName_Struct); +IN(OP_GMKill, GMKill_Struct); +IN(OP_GMLastName, GMLastName_Struct); +IN(OP_GMToggle, GMToggle_Struct); +IN(OP_LFGCommand, LFG_Struct); +IN(OP_GMGoto, GMSummon_Struct); +IN(OP_TraderShop, TraderClick_Struct); +IN(OP_ShopRequest, Merchant_Click_Struct); +IN(OP_Bazaar, BazaarSearch_Struct); +//alt:IN(OP_Bazaar, BazaarWelcome_Struct); //alternate structure for OP_Bazaar +IN(OP_ShopPlayerBuy, Merchant_Sell_Struct); +IN(OP_ShopPlayerSell, Merchant_Purchase_Struct); +INr(OP_ShopEnd); //? +IN(OP_CloseContainer, ClickObjectAction_Struct); +IN(OP_ClickObjectAction, ClickObjectAction_Struct); +IN(OP_ClickObject, ClickObject_Struct); +IN(OP_RecipesFavorite, TradeskillFavorites_Struct); +IN(OP_RecipesSearch, RecipesSearch_Struct); +IN(OP_RecipeDetails, uint32); +//there is also a complicated OP_RecipeDetails reply struct OUT +IN(OP_RecipeAutoCombine, RecipeAutoCombine_Struct); +IN(OP_TradeSkillCombine, NewCombine_Struct); +IN(OP_ItemName, ItemNamePacket_Struct); +IN(OP_AugmentItem, AugmentItem_Struct); +IN(OP_ClickDoor, ClickDoor_Struct); +INr(OP_CreateObject); //? +IN(OP_FaceChange, FaceChange_Struct); +IN(OP_GroupInvite, GroupInvite_Struct); +IN(OP_GroupInvite2, GroupInvite_Struct); //will generally follow OP_GroupInvite for next invite +IN(OP_GroupFollow, GroupGeneric_Struct); //will follow invite +IN(OP_GroupFollow2, GroupGeneric_Struct); //will follow invite2 +INr(OP_GroupAcknowledge); //? +IN(OP_GroupCancelInvite, GroupGeneric_Struct); //follows invite or invite2 +IN(OP_GroupDisband, GroupGeneric_Struct); +INr(OP_GroupDelete); //? +IN(OP_GMEmoteZone, GMEmoteZone_Struct); +IN(OP_InspectRequest, Inspect_Struct); +IN(OP_InspectAnswer, Inspect_Struct); //follows request +IN(OP_DeleteSpell, DeleteSpell_Struct); +IN(OP_PetitionBug, PetitionBug_Struct); +IN(OP_Bug, BugStruct); + +//all these petition opcodes need checking +//converted most of them to raw so they wouldent cause problems +INr(OP_Petition); //used to be just a string +IN(OP_PetitionCheckIn, Petition_Struct); +IN(OP_PetitionResolve, PetitionUpdate_Struct); +IN(OP_PetitionDelete, PetitionUpdate_Struct); +INr(OP_PetitionUnCheckout); //prolly 4 bytes in length +INr(OP_PetitionQue); +INr(OP_PDeletePetition); //used to be just a string +INr(OP_PetitionCheckout); //prolly 4 bytes in length +INr(OP_PetitionRefresh); + +IN(OP_PetCommands, PetCommand_Struct); +IN(OP_ReadBook, BookRequest_Struct); +IN(OP_Emote, Emote_Struct); +#ifdef DISJOINT_STATES +IN(OP_SetServerFilter, SetServerFilter_Struct); +#endif +IN(OP_GMDelCorpse, GMDelCorpse_Struct); +IN(OP_GMKick, GMKick_Struct); +INr(OP_GMServers); //? +IN(OP_Illusion, Illusion_Struct); +IN(OP_GMBecomeNPC, BecomeNPC_Struct); +INz(OP_Fishing); //? +INz(OP_Forage); //? +INz(OP_Mend); //? +IN(OP_EnvDamage, EnvDamage2_Struct); +IN(OP_Damage, CombatDamage_Struct); +IN(OP_AAAction, AA_Action); +IN(OP_TraderBuy, TraderBuy_Struct); +IN(OP_Trader, Trader_ShowItems_Struct); +IN(OP_GMFind, GMSummon_Struct); +IN(OP_PickPocket, PickPocket_Struct); +IN(OP_Bind_Wound, BindWound_Struct); +INr(OP_TrackTarget); +INr(OP_Track); +INz(OP_TrackUnknown); //follows OP_Track +#ifdef DISJOINT_STATES +IN(OP_ClientError, ClientError_Struct); +#endif +INr(OP_ReloadUI); //? +INr(OP_TGB); //4 bytes, value is 0, 1, or 2 +IN(OP_Split, Split_Struct); +INz(OP_SenseTraps); //? +INz(OP_DisarmTraps); //? +IN(OP_OpenTributeMaster, StartTribute_Struct); +IN(OP_OpenGuildTributeMaster, StartTribute_Struct); +IN(OP_TributeItem, TributeItem_Struct); +IN(OP_TributeMoney, TributeMoney_Struct); +IN(OP_SelectTribute, SelectTributeReq_Struct); +IN(OP_TributeUpdate, TributeInfo_Struct); +IN(OP_TributeToggle, uint32); //value is 0 or 1 +IN(OP_TributeNPC, uint32); //contains tribute master entity ID +INr(OP_CrashDump); +INr(OP_ControlBoat); +INr(OP_DumpName); +INr(OP_SetRunMode); +INr(OP_SafeFallSuccess); +INr(OP_Heartbeat); +INr(OP_SafePoint); +INr(OP_Ignore); +IN(OP_FindPersonRequest, FindPersonRequest_Struct); +IN(OP_LeadershipExpToggle, uint8); +IN(OP_PurchaseLeadershipAA, uint32); //value is < _maxLeaderAA +INr(OP_ClearTitle); //follows OP_SendTitleList +INr(OP_BankerChange); +IN(OP_SetTitle, SetTitle_Struct); +INz(OP_RequestTitles); +IN(OP_ItemVerifyRequest, ItemVerifyRequest_Struct); + + +/* + for now we are listing outgoing packets which are NOT + also incoming packets +*/ +OUTz(OP_ClearObject); +OUTz(OP_FinishTrade); //follows OP_TradeAcceptClick +OUTz(OP_GMEndTrainingResponse); //follows OP_GMTraining +OUTz(OP_LootComplete); //follows OP_LootItem +OUTz(OP_WorldObjectsSent); +OUTz(OP_FinishWindow); +OUTz(OP_FinishWindow2); +//OUTz(OP_TradeSkillCombine); + +//OUTv(OP_AdventureDetails, strlen(AF.text)+1); +//OUTv(OP_AdventureInfo, strlen(buffer1)+1); +//OUTv(OP_AdventureInfo, strlen(p)+1); +//OUTv(OP_AdventureMerchantResponse, strlen(msg)+2); +OUTv(OP_ItemPacket, ItemPacket_Struct); +OUTv(OP_BuffFadeMsg, BuffFadeMsg_Struct); +OUTv(OP_FormattedMessage, FormattedMessage_Struct); +OUTv(OP_GuildMemberList, uint32); //variable length, but nasty +OUTv(OP_InterruptCast, InterruptCast_Struct); +OUTv(OP_ItemLinkResponse, ItemPacket_Struct); +OUTv(OP_ZoneSpawns, Spawn_Struct); +OUTv(OP_CompletedTasks, TaskHistory_Struct); +OUTv(OP_CharInventory, ItemPacket_Struct); +OUTv(OP_CustomTitles, Titles_Struct); +OUTv(OP_SpawnDoor, Door_Struct); +OUTv(OP_SendZonepoints, ZonePoints); +OUTv(OP_TributeInfo, TributeAbility_Struct); +OUTv(OP_GuildTributeInfo, GuildTributeAbility_Struct); +OUTv(OP_SendTitleList, TitleList_Struct); +//these arnt used anymore +//OUTv(OP_ItemLinkText, strlen(itemlink)+14+strlen(charname)); +//OUTv(OP_ItemLinkText, strlen(name2)+68); + +OUT(OP_SendMaxCharacters, MaxCharacters_Struct); +OUT(OP_AAExpUpdate, AAExpUpdate_Struct); +OUT(OP_Action, Action_Struct); +OUT(OP_AdventureData, AdventureRequestResponse_Struct); +OUT(OP_AdventureFinish, AdventureFinish_Struct); +OUT(OP_AdventurePointsUpdate, AdventurePoints_Update_Struct); +OUT(OP_Animation, Animation_Struct); +OUT(OP_AnnoyingZoneUnknown, AnnoyingZoneUnknown_Struct); +OUT(OP_BankerChange, BankerChange_Struct); +OUT(OP_BecomeTrader, BecomeTrader_Struct); +OUT(OP_BeginCast, BeginCast_Struct); +OUT(OP_Charm, Charm_Struct); +OUT(OP_CameraEffect, Camera_Struct); +OUT(OP_ClickObjectAction, ClickObjectAction_Struct); +OUT(OP_ConsentResponse, ConsentResponse_Struct); +//OUT(OP_ConsumeAmmo, MoveItem_Struct); +OUT(OP_EnduranceUpdate, EnduranceUpdate_Struct); +OUT(OP_ExpUpdate, ExpUpdate_Struct); +OUT(OP_GroundSpawn, Object_Struct); +OUT(OP_GroupUpdate, GroupJoin_Struct); //takes on 3 sizes... +//alt:OUT(OP_GroupUpdate, GroupUpdate2_Struct); +//alt:OUT(OP_GroupUpdate, GroupUpdate_Struct); +OUT(OP_GuildMOTD, GuildMOTD_Struct); +OUT(OP_GuildManageAdd, GuildJoin_Struct); +OUT(OP_GuildManageRemove, GuildManageRemove_Struct); +OUT(OP_GuildManageStatus, GuildManageStatus_Struct); +OUT(OP_GuildMemberUpdate, GuildMemberUpdate_Struct); +OUT(OP_HPUpdate, SpawnHPUpdate_Struct); +OUT(OP_IncreaseStats, IncreaseStat_Struct); +OUT(OP_ItemVerifyReply, ItemVerifyReply_Struct); +OUT(OP_LFGAppearance, LFG_Appearance_Struct); +OUT(OP_LeadershipExpUpdate, LeadershipExpUpdate_Struct); +OUT(OP_LevelAppearance, LevelAppearance_Struct); +OUT(OP_LevelUpdate, LevelUpdate_Struct); +OUT(OP_ManaUpdate, ManaUpdate_Struct); +OUT(OP_MobEnduranceUpdate, MobEnduranceUpdate_Struct); +OUT(OP_MobHealth, MobHealth_Struct); +OUT(OP_MobManaUpdate, MobManaUpdate_Struct); +OUT(OP_MobRename, MobRename_Struct); +OUT(OP_MoneyOnCorpse, moneyOnCorpseStruct); //follows OP_LootRequest +OUT(OP_MoneyUpdate, MoneyUpdate_Struct); +OUT(OP_MoveDoor, MoveDoor_Struct); +OUT(OP_NewSpawn, NewSpawn_Struct); +OUT(OP_NewZone, NewZone_Struct); +OUT(OP_PetitionCheckout, Petition_Struct); +OUT(OP_PetitionUpdate, PetitionUpdate_Struct); +OUT(OP_PlayerProfile, PlayerProfile_Struct); +OUT(OP_RaidUpdate, ZoneInSendName_Struct); +//alt:OUTv(OP_RaidUpdate, RaidMembers_Struct); +OUT(OP_RandomReply, RandomReply_Struct); +OUT(OP_RecipeReply, RecipeReply_Struct); +OUT(OP_RequestClientZoneChange, RequestClientZoneChange_Struct); +OUT(OP_RespondAA, AATable_Struct); +OUT(OP_RezzRequest, Resurrect_Struct); +OUT(OP_SetTitleReply, SetTitleReply_Struct); +OUT(OP_ShopDelItem, Merchant_DelItem_Struct); +OUT(OP_SimpleMessage, SimpleMessage_Struct); +OUT(OP_SkillUpdate, SkillUpdate_Struct); +OUT(OP_SomeItemPacketMaybe, Arrow_Struct); +OUT(OP_SpellEffect, SpellEffect_Struct); +OUT(OP_Stamina, Stamina_Struct); +OUT(OP_Stun, Stun_Struct); +OUT(OP_TargetReject, TargetReject_Struct); +OUT(OP_TimeOfDay, TimeOfDay_Struct); +OUT(OP_Track, Track_Struct); +OUT(OP_TradeCoins, TradeCoin_Struct); +OUT(OP_TradeMoneyUpdate, TradeMoneyUpdate_Struct); +OUT(OP_TraderDelItem, TraderDelItem_Struct); +OUT(OP_TraderItemUpdate, TraderItemUpdate_Struct); +OUT(OP_TributeTimer, uint32); +OUT(OP_UpdateLeadershipAA, UpdateLeadershipAA_Struct); +OUT(OP_Weather, Weather_Struct); +OUT(OP_ZoneChange, ZoneChange_Struct); +OUT(OP_ZoneInUnknown, ZoneInUnknown_Struct); + +//this is the set of opcodes which are allready listed +//in the IN section above, but are also sent OUT +#ifdef DISJOINT_DIRECTIONS +OUTz(OP_ClientReady); //follows OP_SetServerFilter +OUTz(OP_Dye); +OUTz(OP_GMKick); +OUTz(OP_SendAAStats); //follows OP_ReqNewZone +OUTz(OP_SendExpZonein); //follows OP_SendZonepoints + +OUTv(OP_ReadBook, BookText_Struct); +OUTv(OP_SendAATable, SendAA_Struct); + +OUT(OP_AAAction, UseAA_Struct); +OUT(OP_Bazaar, BazaarReturnDone_Struct); +//alt:OUT(OP_Bazaar, BazaarWelcome_Struct); +OUT(OP_Buff, SpellBuffFade_Struct); +OUT(OP_ClickObject, ClickObject_Struct); +OUT(OP_ClientUpdate, PlayerPositionUpdateServer_Struct); +OUT(OP_SpawnPositionUpdate, SpawnPositionUpdate_Struct); +OUT(OP_Consider, Consider_Struct); +OUT(OP_Damage, CombatDamage_Struct); +OUT(OP_Death, Death_Struct); +OUT(OP_DeleteSpawn, EntityId_Struct); +OUT(OP_DeleteSpell, DeleteSpell_Struct); +OUT(OP_EmoteAnim, EmoteAnim_Struct); +OUT(OP_GMFind, GMSummon_Struct); +OUT(OP_GMKick, GMKick_Struct); +OUT(OP_GMKill, GMKill_Struct); +OUT(OP_GMLastName, GMLastName_Struct); +OUT(OP_GMNameChange, GMName_Struct); +OUT(OP_GMSummon, GMSummon_Struct); +OUT(OP_GMZoneRequest, GMZoneRequest_Struct); +OUT(OP_Illusion, Illusion_Struct); +OUT(OP_ItemName, ItemNamePacket_Struct); +OUT(OP_ManaChange, ManaChange_Struct); +//alt:OUTz(OP_ManaChange); //takes on at least two lengths +OUT(OP_MemorizeSpell, MemorizeSpell_Struct); +OUT(OP_MoveItem, MoveItem_Struct); +OUT(OP_PickPocket, sPickPocket_Struct); +OUT(OP_RecipeAutoCombine, RecipeAutoCombine_Struct); +OUT(OP_RequestDuel, Duel_Struct); +OUT(OP_ShopPlayerBuy, Merchant_Sell_Struct); +OUT(OP_ShopPlayerSell, Merchant_Purchase_Struct); +OUT(OP_ShopRequest, Merchant_Click_Struct); +OUT(OP_SpawnAppearance, SpawnAppearance_Struct); +OUT(OP_TradeRequestAck, TradeRequest_Struct); +OUT(OP_Trader, TraderBuy_Struct); //3 possible lengths +//alt:OUT(OP_Trader, Trader_ShowItems_Struct); +//alt:OUT(OP_Trader, Trader_Struct); +OUT(OP_TraderBuy, TraderBuy_Struct); +OUT(OP_TraderShop, TraderClick_Struct); +OUT(OP_WearChange, WearChange_Struct); +OUT(OP_ZoneEntry, ServerZoneEntry_Struct); +#endif + + + + + +/* + +//... client_packet.h + +#define IN_C(op, s) \ + void Handle_Connect_##op (s *in); +#define IN_Cv(op, s) \ + void Handle_Connect_##op (s *in, uint32 length); +#define IN_Cz(op) \ + void Handle_Connect_##op (); +#define IN_Cr(op) \ + void Handle_Connect_##op (const EQApplicationPacket *app); +#define IN(op, s) \ + void Handle_##op (s *in); +#define INv(op, s) \ + void Handle_##op (s *in, uint32 length); +#define INz(op) \ + void Handle_##op (); +#define INr(op) \ + void Handle_##op (const EQApplicationPacket *app); +#define OUT_C(op, s) +#define OUT_Cv(op, s) +#define OUT_Cz(op) +#define OUT_Cr(op) +#define OUT(op, s) +#define OUTv(op, s) +#define OUTz(op) +#define OUTr(op) +#include "opcode_dispatch.h" +#undef IN_C +#undef IN_Cr +#undef IN_Cv +#undef IN_Cz +#undef IN +#undef INr +#undef INv +#undef INz +#undef OUT_C +#undef OUT_Cr +#undef OUT_Cv +#undef OUT_Cz +#undef OUT +#undef OUTr +#undef OUTv +#undef OUTz + +class OpcodeDispatcher { +public: + virtual void dispatch(Client *on, const EQApplicationPacket *app) = 0; +}; + +class RawOpcodeDispatcher : public OpcodeDispatcher { +public: + typedef void (Client::*proc)(const EQApplicationPacket *app); + RawOpcodeDispatcher(proc p) { + d = p; + } + + virtual void dispatch(Client *on, const EQApplicationPacket *app) { + (on->*d)(app); + } +protected: + proc d; +}; + +class ZeroOpcodeDispatcher : public OpcodeDispatcher { +public: + typedef void (Client::*proc)(); + ZeroOpcodeDispatcher(proc p) { + d = p; + } + + virtual void dispatch(Client *on, const EQApplicationPacket *app) { + if(app->size != 0) { + //error.. + return; + } + (on->*d)(); + } +protected: + proc d; +}; + +template +class TypedOpcodeDispatcher : public OpcodeDispatcher { +public: + typedef void (Client::*proc)(T *packet); + TypedOpcodeDispatcher(proc p, const char *sn) { + d = p; + struct_name = sn; + } + + virtual void dispatch(Client *on, const EQApplicationPacket *app) { + if(app->size != sizeof(T)) { + //error.. + return; + } + T * tmp = (T *) app->pBuffer; + (on->*d)(tmp); + } + +protected: + proc d; + const char *struct_name; +}; + +template +class TypedVarOpcodeDispatcher : public OpcodeDispatcher { +public: + typedef void (Client::*proc)(T *packet, uint32 length); + TypedVarOpcodeDispatcher(proc p, const char *sn) { + d = p; + struct_name = sn; + } + + virtual void dispatch(Client *on, const EQApplicationPacket *app) { + if(app->size < sizeof(T)) { + //error.. + return; + } + T * tmp = (T *) app->pBuffer; + (on->*d)(tmp, app->size); + } + +protected: + proc d; + const char *struct_name; +}; + + + +//... client_packet.cpp +void MapOpcodes() { + //.. +#define IN_C(op, s) \ + ConnectingOpcodes[op] = new TypedOpcodeDispatcher(&Client::Handle_Connect_##op , #s); +#define IN_Cv(op, s) \ + ConnectingOpcodes[op] = new TypedVarOpcodeDispatcher(&Client::Handle_Connect_##op , #s); +#define IN_Cz(op) \ + ConnectingOpcodes[op] = new ZeroOpcodeDispatcher(&Client::Handle_Connect_##op); +#define IN_Cr(op) \ + ConnectingOpcodes[op] = new RawOpcodeDispatcher(&Client::Handle_Connect_##op); +#define IN(op, s) \ + ConnectedOpcodes[op] = new TypedOpcodeDispatcher(&Client::Handle_##op , #s); +#define INv(op, s) \ + ConnectedOpcodes[op] = new TypedVarOpcodeDispatcher(&Client::Handle_##op , #s); +#define INz(op) \ + ConnectedOpcodes[op] = new ZeroOpcodeDispatcher(&Client::Handle_##op); +#define INr(op) \ + ConnectedOpcodes[op] = new RawOpcodeDispatcher(&Client::Handle_##op); +#define OUT_C(op, s) +#define OUT_Cv(op, s) +#define OUT_Cz(op) +#define OUT_Cr(op) +#define OUT(op, s) +#define OUTv(op, s) +#define OUTz(op) +#define OUTr(op) +#include "opcode_dispatch.h" +#undef IN_C +#undef IN_Cr +#undef IN_Cv +#undef IN_Cz +#undef IN +#undef INr +#undef INv +#undef INz +#undef OUT_C +#undef OUT_Cr +#undef OUT_Cv +#undef OUT_Cz +#undef OUT +#undef OUTr +#undef OUTv +#undef OUTz + +*/ diff --git a/common/opcode_map.cpp b/common/opcode_map.cpp new file mode 100644 index 000000000..42c2d320f --- /dev/null +++ b/common/opcode_map.cpp @@ -0,0 +1,309 @@ +#include "debug.h" +#include +#include + +using namespace std; + +map opcode_map; + +string get_opcode_name(unsigned long opcode) +{ +map::iterator itr;; + + return (itr=opcode_map.find(opcode))!=opcode_map.end() ? itr->second : "OP_Unknown"; +} +void load_opcode_names() +{ + opcode_map[0x0176]="LiveOP_Heartbeat"; + opcode_map[0x02d7]="LiveOP_ReloadUI"; + opcode_map[0x01eb]="LiveOP_IncreaseStats"; + opcode_map[0x0134]="LiveOP_ApproveZone"; + opcode_map[0x01d5]="LiveOP_Dye"; + opcode_map[0x0168]="LiveOP_Stamina"; + opcode_map[0x014d]="LiveOP_ControlBoat"; + opcode_map[0x003e]="LiveOP_MobUpdate"; + opcode_map[0x0027]="LiveOP_ClientUpdate"; + opcode_map[0x0024]="LiveOP_ChannelMessage"; + opcode_map[0x01d7]="LiveOP_SimpleMessage"; + opcode_map[0x01d8]="LiveOP_FormattedMessage"; + opcode_map[0x01c6]="LiveOP_TGB"; + opcode_map[0x0285]="LiveOP_TestBuff"; + opcode_map[0x012d]="LiveOP_Bind_Wound"; + opcode_map[0x01ab]="LiveOP_Charm"; + opcode_map[0x014c]="LiveOP_Begging"; + opcode_map[0x0152]="LiveOP_MoveCoin"; + opcode_map[0x0292]="LiveOP_SpawnDoor"; + opcode_map[0x009d]="LiveOP_Sneak"; + opcode_map[0x0079]="LiveOP_ExpUpdate"; + opcode_map[0x027d]="LiveOP_DumpName"; + opcode_map[0x01ea]="LiveOP_RespondAA"; + opcode_map[0x01c9]="LiveOP_SendAAStats"; + opcode_map[0x0366]="LiveOP_SendAATable"; + opcode_map[0x01e9]="LiveOP_AAAction"; + opcode_map[0x00bb]="LiveOP_BoardBoat"; + opcode_map[0x00bc]="LiveOP_LeaveBoat"; + opcode_map[0x02b8]="LiveOP_AdventureInfoRequest"; + opcode_map[0x02b9]="LiveOP_AdventureInfo"; + opcode_map[0x02a6]="LiveOP_AdventureRequest"; + opcode_map[0x02a8]="LiveOP_AdventureDetails"; + opcode_map[0x02a9]="LiveOP_LDoNButton"; + opcode_map[0x02ba]="LiveOP_AdventureData"; + opcode_map[0x02c9]="LiveOP_AdventureFinish"; + opcode_map[0x02c6]="LiveOP_LeaveAdventure"; + opcode_map[0x02ce]="LiveOP_AdventureUpdate"; + opcode_map[0x002b]="LiveOP_SendExpZonein"; + opcode_map[0x01e4]="LiveOP_ZoneInSendName"; + opcode_map[0x01bf]="LiveOP_GuildLeader"; + opcode_map[0x009a]="LiveOP_GuildPeace"; + opcode_map[0x0132]="LiveOP_GuildRemove"; + opcode_map[0x0059]="LiveOP_GuildMemberList"; + opcode_map[0x026e]="LiveOP_GuildMemberUpdate"; + opcode_map[0x0130]="LiveOP_GuildInvite"; + opcode_map[0x01c0]="LiveOP_GuildMOTD"; + opcode_map[0x003c]="LiveOP_GuildPublicNote"; + opcode_map[0x027e]="LiveOP_GetGuildMOTD"; + opcode_map[0x0277]="LiveOP_GuildDemote"; + opcode_map[0x0131]="LiveOP_GuildInviteAccept"; + opcode_map[0x00a4]="LiveOP_GuildWar"; + opcode_map[0x0133]="LiveOP_GuildDelete"; + opcode_map[0x0233]="LiveOP_GuildManageRemove"; + opcode_map[0x022d]="LiveOP_GuildManageAdd"; + opcode_map[0x0039]="LiveOP_GuildManageStatus"; + opcode_map[0x01e8]="LiveOP_Trader"; + opcode_map[0x01e7]="LiveOP_Bazaar"; + opcode_map[0x01c4]="LiveOP_BecomeTrader"; + opcode_map[0x01f4]="LiveOP_BazaarInspect"; + opcode_map[0x006e]="LiveOP_TraderItemUpdate"; + opcode_map[0x017c]="LiveOP_TraderDelItem"; + opcode_map[0x01eb]="LiveOP_TraderShop"; + opcode_map[0x01ca]="LiveOP_TraderBuy"; + opcode_map[0x01ac]="LiveOP_PetCommands"; + opcode_map[0x0042]="LiveOP_TradeSkillCombine"; + opcode_map[0x02e5]="LiveOP_AugmentItem"; + opcode_map[0x0367]="LiveOP_ItemName"; + opcode_map[0x02cd]="LiveOP_ShopItem"; + opcode_map[0x0065]="LiveOP_ShopPlayerBuy"; + opcode_map[0x006a]="LiveOP_ShopPlayerSell"; + opcode_map[0x006d]="LiveOP_ShopDelItem"; + opcode_map[0x0f6d]="LiveOP_ShopEndConfirm"; + opcode_map[0x00f7]="LiveOP_ShopRequest"; + opcode_map[0x006c]="LiveOP_ShopEnd"; + opcode_map[0x02d1]="LiveOP_AdventureMerchantRequest"; + opcode_map[0x02d2]="LiveOP_AdventureMerchantResponse"; + opcode_map[0x02d3]="LiveOP_AdventureMerchantPurchase"; + opcode_map[0x02e3]="LiveOP_AdventurePointsUpdate"; + opcode_map[0x0270]="LiveOP_LFGCommand"; + opcode_map[0x01d0]="LiveOP_LFGAppearance"; + opcode_map[0x01b5]="LiveOP_MoneyUpdate"; + opcode_map[0x0721]="LiveOP_GroupDelete"; + opcode_map[0x0272]="LiveOP_GroupAcknowledge"; + opcode_map[0x024a]="LiveOP_GroupUpdate"; + opcode_map[0x025f]="LiveOP_GroupInvite"; + opcode_map[0x00ff]="LiveOP_GroupDisband"; + opcode_map[0x00d5]="LiveOP_GroupInvite2"; + opcode_map[0x025e]="LiveOP_GroupFollow"; + opcode_map[0x00d7]="LiveOP_GroupFollow2"; + opcode_map[0x00d6]="LiveOP_GroupCancelInvite"; + opcode_map[0x0156]="LiveOP_Split"; + opcode_map[0x00d8]="LiveOP_Jump"; + opcode_map[0x01d6]="LiveOP_ConsiderCorpse"; + opcode_map[0x0064]="LiveOP_SkillUpdate"; + opcode_map[0x0178]="LiveOP_GMEndTrainingResponse"; + opcode_map[0x013c]="LiveOP_GMEndTraining"; + opcode_map[0x0175]="LiveOP_GMTrainSkill"; + opcode_map[0x013b]="LiveOP_GMTraining"; + opcode_map[0x017b]="LiveOP_ConsumeAmmo"; + opcode_map[0x0171]="LiveOP_CombatAbility"; + opcode_map[0x009c]="LiveOP_TrackUnknown"; + opcode_map[0x0234]="LiveOP_TrackTarget"; + opcode_map[0x0286]="LiveOP_Track"; + opcode_map[0x0297]="LiveOP_ReadBook"; + opcode_map[0x001f]="LiveOP_ItemLinkClick"; + opcode_map[0x01f4]="LiveOP_ItemLinkResponse"; + opcode_map[0x01d9]="LiveOP_ItemLinkText"; + opcode_map[0x0a41]="LiveOP_RezzRequest"; + opcode_map[0x00e5]="LiveOP_RezzAnswer"; + opcode_map[0x019b]="LiveOP_RezzComplete"; + opcode_map[0x0128]="LiveOP_MoveDoor"; + opcode_map[0x0127]="LiveOP_ClickDoor"; + opcode_map[0x0247]="LiveOP_SendZonepoints"; + opcode_map[0x008c]="LiveOP_SetRunMode"; + opcode_map[0x0248]="LiveOP_InspectRequest"; + opcode_map[0x0249]="LiveOP_InspectAnswer"; + opcode_map[0x0187]="LiveOP_SenseTraps"; + opcode_map[0x018e]="LiveOP_DisarmTraps"; + opcode_map[0x01bc]="LiveOP_Assist"; + opcode_map[0x0240]="LiveOP_PickPocket"; + opcode_map[0x0119]="LiveOP_LootRequest"; + opcode_map[0x011a]="LiveOP_EndLootRequest"; + opcode_map[0x011b]="LiveOP_MoneyOnCorpse"; + opcode_map[0x0179]="LiveOP_LootComplete"; + opcode_map[0x013f]="LiveOP_LootItem"; + opcode_map[0x0151]="LiveOP_MoveItem"; + opcode_map[0x0056]="LiveOP_WhoAllRequest"; + opcode_map[0x0229]="LiveOP_WhoAllResponse"; + opcode_map[0x0167]="LiveOP_Consume"; + opcode_map[0x0172]="LiveOP_AutoAttack"; + opcode_map[0x0186]="LiveOP_AutoAttack2"; + opcode_map[0x0173]="LiveOP_TargetMouse"; + opcode_map[0x01ba]="LiveOP_TargetCommand"; + opcode_map[0x01d8]="LiveOP_TargetReject"; + opcode_map[0x009e]="LiveOP_Hide"; + opcode_map[0x012e]="LiveOP_Forage"; + opcode_map[0x0077]="LiveOP_Fishing"; + opcode_map[0x0246]="LiveOP_Bug"; + opcode_map[0x00f2]="LiveOP_Emote"; + opcode_map[0x0140]="LiveOP_EmoteAnim"; + opcode_map[0x015c]="LiveOP_Consider"; + opcode_map[0x01cb]="LiveOP_FaceChange"; + opcode_map[0x0197]="LiveOP_RandomReq"; + opcode_map[0x0087]="LiveOP_RandomReply"; + opcode_map[0x01c3]="LiveOP_Camp"; + opcode_map[0x0192]="LiveOP_YellForHelp"; + opcode_map[0x00ef]="LiveOP_SafePoint"; + opcode_map[0x0157]="LiveOP_Buff"; + opcode_map[0x00c0]="LiveOP_BuffFadeMsg"; + opcode_map[0x0440]="LiveOP_MultiLineMsg"; + opcode_map[0x021c]="LiveOP_SpecialMesg"; + opcode_map[0x0013]="LiveOP_Consent"; + opcode_map[0x029d]="LiveOP_ConsentResponse"; + opcode_map[0x02d4]="LiveOP_Deny"; + opcode_map[0x016c]="LiveOP_Stun"; + opcode_map[0x0021]="LiveOP_BeginCast"; + opcode_map[0x00be]="LiveOP_CastSpell"; + opcode_map[0x01a8]="LiveOP_InterruptCast"; + opcode_map[0x0105]="LiveOP_Death"; + opcode_map[0x023f]="LiveOP_FeignDeath"; + opcode_map[0x012b]="LiveOP_Illusion"; + opcode_map[0x0078]="LiveOP_LevelUpdate"; + opcode_map[0x0371]="LiveOP_LevelAppearance"; + opcode_map[0x00c2]="LiveOP_MemorizeSpell"; + opcode_map[0x0244]="LiveOP_HPUpdate"; + opcode_map[0x022e]="LiveOP_SendHPTarget"; + opcode_map[0x007d]="LiveOP_Mend"; + opcode_map[0x0160]="LiveOP_Taunt"; + opcode_map[0x0199]="LiveOP_GMDelCorpse"; + opcode_map[0x0047]="LiveOP_GMFind"; + opcode_map[0x0020]="LiveOP_GMServers"; + opcode_map[0x010b]="LiveOP_GMGoto"; + opcode_map[0x028c]="LiveOP_GMSummon"; + opcode_map[0x010a]="LiveOP_GMKick"; + opcode_map[0x0109]="LiveOP_GMKill"; + opcode_map[0x0b40]="LiveOP_GMNameChange"; + opcode_map[0x00a3]="LiveOP_GMLastName"; + opcode_map[0x01b3]="LiveOP_GMToggle"; + opcode_map[0x028f]="LiveOP_GMEmoteZone"; + opcode_map[0x0074]="LiveOP_GMBecomeNPC"; + opcode_map[0x00de]="LiveOP_GMHideMe"; + opcode_map[0x0184]="LiveOP_GMZoneRequest"; + opcode_map[0x0239]="LiveOP_GMZoneRequest2"; + opcode_map[0x0068]="LiveOP_Petition"; + opcode_map[0x0085]="LiveOP_PetitionRefresh"; + opcode_map[0x01ee]="LiveOP_PDeletePetition"; + opcode_map[0x0092]="LiveOP_PetitionBug"; + opcode_map[0x0069]="LiveOP_PetitionUpdate"; + opcode_map[0x0076]="LiveOP_PetitionCheckout"; + opcode_map[0x0056]="LiveOP_PetitionCheckout2"; + opcode_map[0x0091]="LiveOP_PetitionDelete"; + opcode_map[0x02b4]="LiveOP_PetitionResolve"; + opcode_map[0x007e]="LiveOP_PetitionCheckIn"; + opcode_map[0x0090]="LiveOP_PetitionUnCheckout"; + opcode_map[0x01ec]="LiveOP_PetitionQue"; + opcode_map[0x01bb]="LiveOP_SetServerFilter"; + opcode_map[0x0218]="LiveOP_NewSpawn"; + opcode_map[0x0140]="LiveOP_Animation"; + opcode_map[0x0142]="LiveOP_ZoneChange"; + opcode_map[0x00f3]="LiveOP_DeleteSpawn"; + opcode_map[0x0265]="LiveOP_CrashDump"; + opcode_map[0x00e8]="LiveOP_EnvDamage"; + opcode_map[0x0101]="LiveOP_Action"; + opcode_map[0x00e2]="LiveOP_Damage"; + opcode_map[0x00bf]="LiveOP_ManaChange"; + opcode_map[0x027c]="LiveOP_ClientError"; + opcode_map[0x00fb]="LiveOP_Save"; + opcode_map[0x0316]="LiveOP_LocInfo"; + opcode_map[0x0188]="LiveOP_Surname"; + opcode_map[0x018f]="LiveOP_SwapSpell"; + opcode_map[0x01db]="LiveOP_DeleteSpell"; + opcode_map[0x029f]="LiveOP_CloseContainer"; + opcode_map[0x029f]="LiveOP_ClickObjectAck"; + opcode_map[0x00fa]="LiveOP_CreateObject"; + opcode_map[0x00f9]="LiveOP_ClickObject"; + opcode_map[0x01c1]="LiveOP_ClearObject"; + opcode_map[0x0265]="LiveOP_ZoneUnavail"; + opcode_map[0x02e0]="LiveOP_ItemPacket"; + opcode_map[0x029a]="LiveOP_TradeRequest"; + opcode_map[0x0037]="LiveOP_TradeRequestAck"; + opcode_map[0x002d]="LiveOP_TradeAcceptClick"; + opcode_map[0x0162]="LiveOP_TradeMoneyUpdate"; + opcode_map[0x0036]="LiveOP_TradeCoins"; + opcode_map[0x002e]="LiveOP_CancelTrade"; + opcode_map[0x002f]="LiveOP_FinishTrade"; + opcode_map[0x00a1]="LiveOP_SaveOnZoneReq"; + opcode_map[0x0185]="LiveOP_Logout"; + opcode_map[0x0298]="LiveOP_RequestDuel"; + opcode_map[0x0a5d]="LiveOP_DuelResponse"; + opcode_map[0x016e]="LiveOP_DuelResponse2"; + opcode_map[0x007c]="LiveOP_InstillDoubt"; + opcode_map[0x00ac]="LiveOP_SafeFallSuccess"; + opcode_map[0x02fb]="LiveOP_DisciplineUpdate"; + opcode_map[0x02f2]="LiveOP_TributeUpdate"; + opcode_map[0x02f3]="LiveOP_TributeItem"; + opcode_map[0x02f4]="LiveOP_TributePointUpdate"; + opcode_map[0x02f5]="LiveOP_SendTributes"; + opcode_map[0x02f6]="LiveOP_TributeInfo"; + opcode_map[0x02f7]="LiveOP_SelectTribute"; + opcode_map[0x02f8]="LiveOP_TributeTimer"; + opcode_map[0x02f9]="LiveOP_StartTribute"; + opcode_map[0x02fa]="LiveOP_TributeNPC"; + opcode_map[0x02fe]="LiveOP_TributeMoney"; + opcode_map[0x0364]="LiveOP_TributeToggle"; + opcode_map[0x0322]="LiveOP_RecipesFavorite"; + opcode_map[0x01f9]="LiveOP_RecipesSearch"; + opcode_map[0x01fa]="LiveOP_RecipeReply"; + opcode_map[0x01fb]="LiveOP_RecipeDetails"; + opcode_map[0x01fc]="LiveOP_RecipeAutoCombine"; + opcode_map[0x02db]="LiveOP_FindPersonRequest"; + opcode_map[0x02dc]="LiveOP_FindPersonReply"; + opcode_map[0x01dd]="LiveOP_Shielding"; + opcode_map[0x0198]="LiveOP_SetDataRate"; + opcode_map[0x023b]="LiveOP_ZoneEntry"; + opcode_map[0x006b]="LiveOP_PlayerProfile"; + opcode_map[0x0291]="LiveOP_CharInventory"; + opcode_map[0x0170]="LiveOP_ZoneSpawns"; + opcode_map[0x0026]="LiveOP_TimeOfDay"; + opcode_map[0x015b]="LiveOP_Weather"; + opcode_map[0x00ec]="LiveOP_ReqNewZone"; + opcode_map[0x00eb]="LiveOP_NewZone"; + opcode_map[0x00fd]="LiveOP_ReqClientSpawn"; + opcode_map[0x012F]="LiveOP_SpawnAppearance"; + opcode_map[0x0086]="LiveOP_ClientReady"; + opcode_map[0x0086]="LiveOP_ZoneComplete"; + opcode_map[0x02db]="LiveOP_LoginComplete"; + opcode_map[0x0195]="LiveOP_ApproveWorld"; + opcode_map[0x035f]="LiveOP_LogServer"; + opcode_map[0x01b2]="LiveOP_MOTD"; + opcode_map[0x0251]="LiveOP_SendLoginInfo"; + opcode_map[0x00ea]="LiveOP_DeleteCharacter"; + opcode_map[0x0102]="LiveOP_SendCharInfo"; + opcode_map[0x00e1]="LiveOP_ExpansionInfo"; + opcode_map[0x0104]="LiveOP_CharacterCreate"; + opcode_map[0x02ab]="LiveOP_RandomNameGenerator"; + opcode_map[0x005d]="LiveOP_GuildsList"; + opcode_map[0x0125]="LiveOP_ApproveName"; + opcode_map[0x0261]="LiveOP_EnterWorld"; + opcode_map[0x015a]="LiveOP_World_Client_CRC1"; + opcode_map[0x015e]="LiveOP_World_Client_CRC2"; + opcode_map[0x0269]="LiveOP_SetChatServer"; + opcode_map[0x0264]="LiveOP_ZoneServerInfo"; + opcode_map[0x0017]="LiveOP_AckPacket"; + opcode_map[0x012c]="LiveOP_WearChange"; + opcode_map[0x1FA1]="LiveOP_WorldObjectsSent"; + opcode_map[0x39C4]="LiveOP_BlockedBuffs"; + opcode_map[0x4656]="LiveOP_SpawnPositionUpdate"; + opcode_map[0x4b61]="LiveOP_ManaUpdate"; + opcode_map[0x02d6]="LiveOP_EnduranceUpdate"; + opcode_map[0x2ac1]="LiveOP_MobManaUpdate"; + opcode_map[0x6c5f]="LiveOP_MobEnduranceUpdate"; + opcode_map[0x73a8]="LiveOP_SendMaxCharacters"; +} diff --git a/common/opcodemgr.cpp b/common/opcodemgr.cpp new file mode 100644 index 000000000..bf22f5754 --- /dev/null +++ b/common/opcodemgr.cpp @@ -0,0 +1,371 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "debug.h" +#include +#include "opcodemgr.h" +#include "debug.h" +#include "emu_opcodes.h" +#include +#include + +#ifdef SHARED_OPCODES +#include "EMuShareMem.h" +extern LoadEMuShareMemDLL EMuShareMemDLL; +#endif + +#include +#include +using namespace std; + + +//#define DEBUG_TRANSLATE + + +OpcodeManager::OpcodeManager() { + loaded = false; +} + +bool OpcodeManager::LoadOpcodesFile(const char *filename, OpcodeSetStrategy *s, bool report_errors) { + FILE *opf = fopen(filename, "r"); + if(opf == NULL) { + fprintf(stderr, "Unable to open opcodes file '%s'. Thats bad.\n", filename); + return(false); + } + + map eq; + + //load the opcode file into eq, could swap in a nice XML parser here + char line[2048]; + int lineno = 0; + uint16 curop; + while(!feof(opf)) { + lineno++; + line[0] = '\0'; //for blank line at end of file + if(fgets(line, sizeof(line), opf) == NULL) + break; + + //ignore any line that dosent start with OP_ + if(line[0] != 'O' || line[1] != 'P' || line[2] != '_') + continue; + + char *num = line+3; //skip OP_ + //look for the = sign + while(*num != '=' && *num != '\0') { + num++; + } + //make sure we found = + if(*num != '=') { + if(report_errors) fprintf(stderr, "Malformed opcode line at %s:%d\n", filename, lineno); + continue; + } + *num = '\0'; //null terminate the name + num++; //num should point to the opcode + + //read the opcode + if(sscanf(num, "0x%hx", &curop) != 1) { + if(report_errors) fprintf(stderr, "Malformed opcode at %s:%d\n", filename, lineno); + continue; + } + + //we have a name and our opcode... stick it in the map + eq[line] = curop; + } + fclose(opf); + + + //do the mapping and store them in the shared memory array + bool ret = true; + EmuOpcode emu_op; + map::iterator res; + //stupid enum wont let me ++ on it... + for(emu_op = OP_Unknown; emu_op < _maxEmuOpcode; emu_op=(EmuOpcode)(emu_op+1)) { + //get the name of this emu opcode + const char *op_name = OpcodeNames[emu_op]; + if(op_name[0] == '\0') { + if(report_errors) printf("Over-ran the bounds of the opcode name array. You prolly need to recompile the opcode stuff.\n"); + if(report_errors) printf("I will let you continue, since you may have gotten all the opcodes you need.\n"); + break; + } + + //find the opcode in the file + res = eq.find(op_name); + if(res == eq.end()) { + if(report_errors) fprintf(stderr, "Opcode %s is missing from %s\n", op_name, filename); + //ret = false; + continue; //continue to give them a list of all missing opcodes + } + + //ship the mapping off to shared mem. + s->Set(emu_op, res->second); + } + + return(ret); +} + +//convenience routines +const char *OpcodeManager::EmuToName(const EmuOpcode emu_op) { + return(OpcodeNames[emu_op]); +} + +const char *OpcodeManager::EQToName(const uint16 eq_op) { + //first must resolve the eq op to an emu op + EmuOpcode emu_op = EQToEmu(eq_op); + return(OpcodeNames[emu_op]); +} + +EmuOpcode OpcodeManager::NameSearch(const char *name) { + EmuOpcode emu_op; + //stupid enum wont let me ++ on it... + for(emu_op = OP_Unknown; emu_op < _maxEmuOpcode; emu_op=(EmuOpcode)(emu_op+1)) { + //get the name of this emu opcode + const char *op_name = OpcodeNames[emu_op]; + if(!strcasecmp(op_name, name)) { + return(emu_op); + } + } + return(OP_Unknown); +} + +#ifdef SHARED_OPCODES +bool SharedOpcodeManager::LoadOpcodes(const char *filename, bool report_errors) { + if (!EMuShareMemDLL.Load()) { + printf("Unable to load EMuShareMem for opcodes.\n"); + return false; + } + MOpcodes.lock(); + + loaded = true; + + bool ret = EMuShareMemDLL.Opcodes.DLLLoadOpcodes(DLLLoadOpcodesCallback, sizeof(uint16), MAX_EQ_OPCODE, _maxEmuOpcode, filename); + + MOpcodes.unlock(); + return ret; +} + +bool SharedOpcodeManager::DLLLoadOpcodesCallback(const char *filename) { + SharedMemStrategy s; + return(LoadOpcodesFile(filename, &s, true)); +} + +bool SharedOpcodeManager::ReloadOpcodes(const char *filename, bool report_errors) { +/* if(!loaded) + return(LoadOpcodes(filename)); + + MOpcodes.lock(); + EMuShareMemDLL.Opcodes.ClearEQOpcodes(); + + SharedMemStrategy s; + bool ret = LoadOpcodesFile(filename, &s); + + MOpcodes.unlock(); + return(ret);*/ + + //shared memory is read only right now, cant reload it + return(false); +} + + +uint16 SharedOpcodeManager::EmuToEQ(const EmuOpcode emu_op) { + //opcode is checked for validity in GetEQOpcode + uint16 res; + MOpcodes.lock(); + res = EMuShareMemDLL.Opcodes.GetEQOpcode((uint16)emu_op); + MOpcodes.unlock(); +#ifdef DEBUG_TRANSLATE + fprintf(stderr, "S Translate Emu %s (%d) to EQ 0x%.4x\n", OpcodeNames[emu_op], emu_op, res); +#endif + return(res); +} + +EmuOpcode SharedOpcodeManager::EQToEmu(const uint16 eq_op) { + //opcode is checked for validity in GetEmuOpcode +//Disabled since current live EQ uses the entire uint16 bitspace for opcodes +// if(eq_op > MAX_EQ_OPCODE) +// return(OP_Unknown); + uint16 res; + MOpcodes.lock(); + res = EMuShareMemDLL.Opcodes.GetEmuOpcode(eq_op); + MOpcodes.unlock(); +#ifdef DEBUG_TRANSLATE + fprintf(stderr, "S Translate EQ 0x%.4x to Emu %s (%d)\n", eq_op, OpcodeNames[res], res); +#endif + return((EmuOpcode)res); +} + + +void SharedOpcodeManager::SharedMemStrategy::Set(EmuOpcode emu_op, uint16 eq_op) { + EMuShareMemDLL.Opcodes.SetOpcodePair((uint16)emu_op, eq_op); +} + +#endif + + +RegularOpcodeManager::RegularOpcodeManager() +: MutableOpcodeManager() +{ + emu_to_eq = NULL; + eq_to_emu = NULL; +} + +RegularOpcodeManager::~RegularOpcodeManager() { + safe_delete(emu_to_eq); + safe_delete(eq_to_emu); +} + +bool RegularOpcodeManager::LoadOpcodes(const char *filename, bool report_errors) { + NormalMemStrategy s; + s.it = this; + MOpcodes.lock(); + + loaded = true; + eq_to_emu = new EmuOpcode[MAX_EQ_OPCODE]; + emu_to_eq = new uint16[_maxEmuOpcode]; + EQOpcodeCount = MAX_EQ_OPCODE; + EmuOpcodeCount = _maxEmuOpcode; + + //dont need to set eq_to_emu cause every element should get a value + memset(eq_to_emu, 0, sizeof(EmuOpcode)*MAX_EQ_OPCODE); + memset(emu_to_eq, 0, sizeof(uint16)*_maxEmuOpcode); + + bool ret = LoadOpcodesFile(filename, &s, report_errors); + MOpcodes.unlock(); + return ret; +} + +bool RegularOpcodeManager::ReloadOpcodes(const char *filename, bool report_errors) { + if(!loaded) + return(LoadOpcodes(filename)); + + NormalMemStrategy s; + s.it = this; + MOpcodes.lock(); + + memset(eq_to_emu, 0, sizeof(uint16)*MAX_EQ_OPCODE); + + bool ret = LoadOpcodesFile(filename, &s, report_errors); + + MOpcodes.unlock(); + return(ret); +} + + +uint16 RegularOpcodeManager::EmuToEQ(const EmuOpcode emu_op) { + //opcode is checked for validity in GetEQOpcode + uint16 res; + MOpcodes.lock(); + res = emu_to_eq[emu_op]; + MOpcodes.unlock(); +#ifdef DEBUG_TRANSLATE + fprintf(stderr, "M Translate Emu %s (%d) to EQ 0x%.4x\n", OpcodeNames[emu_op], emu_op, res); +#endif + return(res); +} + +EmuOpcode RegularOpcodeManager::EQToEmu(const uint16 eq_op) { + //opcode is checked for validity in GetEmuOpcode +//Disabled since current live EQ uses the entire uint16 bitspace for opcodes +// if(eq_op > MAX_EQ_OPCODE) +// return(OP_Unknown); + EmuOpcode res; + MOpcodes.lock(); + res = eq_to_emu[eq_op]; + MOpcodes.unlock(); +#ifdef DEBUG_TRANSLATE + fprintf(stderr, "M Translate EQ 0x%.4x to Emu %s (%d)\n", eq_op, OpcodeNames[res], res); +#endif + return(res); +} + +void RegularOpcodeManager::SetOpcode(EmuOpcode emu_op, uint16 eq_op) { + + //clear out old mapping + uint16 oldop = emu_to_eq[emu_op]; + if(oldop != 0) + eq_to_emu[oldop] = OP_Unknown; + + //use our strategy, since we have it + NormalMemStrategy s; + s.it = this; + s.Set(emu_op, eq_op); +} + + +void RegularOpcodeManager::NormalMemStrategy::Set(EmuOpcode emu_op, uint16 eq_op) { + if(uint32(emu_op) >= it->EmuOpcodeCount || eq_op >= it->EQOpcodeCount) + return; + it->emu_to_eq[emu_op] = eq_op; + it->eq_to_emu[eq_op] = emu_op; +} + +NullOpcodeManager::NullOpcodeManager() +: MutableOpcodeManager() { +} + + +bool NullOpcodeManager::LoadOpcodes(const char *filename, bool report_errors) { + return(true); +} + +bool NullOpcodeManager::ReloadOpcodes(const char *filename, bool report_errors) { + return(true); +} + +uint16 NullOpcodeManager::EmuToEQ(const EmuOpcode emu_op) { + return(0); +} + +EmuOpcode NullOpcodeManager::EQToEmu(const uint16 eq_op) { + return(OP_Unknown); +} + +EmptyOpcodeManager::EmptyOpcodeManager() +: MutableOpcodeManager() { +} + + +bool EmptyOpcodeManager::LoadOpcodes(const char *filename, bool report_errors) { + return(true); +} + +bool EmptyOpcodeManager::ReloadOpcodes(const char *filename, bool report_errors) { + return(true); +} + +uint16 EmptyOpcodeManager::EmuToEQ(const EmuOpcode emu_op) { + map::iterator f; + f = emu_to_eq.find(emu_op); + return(f == emu_to_eq.end()? 0 : f->second); +} + +EmuOpcode EmptyOpcodeManager::EQToEmu(const uint16 eq_op) { + map::iterator f; + f = eq_to_emu.find(eq_op); + return(f == eq_to_emu.end()?OP_Unknown:f->second); +} + +void EmptyOpcodeManager::SetOpcode(EmuOpcode emu_op, uint16 eq_op) { + emu_to_eq[emu_op] = eq_op; + eq_to_emu[eq_op] = emu_op; +} + + + + + + + diff --git a/common/opcodemgr.h b/common/opcodemgr.h new file mode 100644 index 000000000..dba37c881 --- /dev/null +++ b/common/opcodemgr.h @@ -0,0 +1,176 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 OPCODE_MANAGER_H +#define OPCODE_MANAGER_H + +#include "types.h" +#include "Mutex.h" +#include "emu_opcodes.h" + +#include +using namespace std; + +//enable the use of shared mem opcodes for world and zone only +#ifdef ZONE +#define SHARED_OPCODES +#endif +#ifdef WORLD +#define SHARED_OPCODES +#endif + +class OpcodeManager { +public: + OpcodeManager(); + virtual ~OpcodeManager() {} + + virtual bool Mutable() { return(false); } + virtual bool LoadOpcodes(const char *filename, bool report_errors = false) = 0; + virtual bool ReloadOpcodes(const char *filename, bool report_errors = false) = 0; + + virtual uint16 EmuToEQ(const EmuOpcode emu_op) = 0; + virtual EmuOpcode EQToEmu(const uint16 eq_op) = 0; + + static const char *EmuToName(const EmuOpcode emu_op); + const char *EQToName(const uint16 emu_op); + EmuOpcode NameSearch(const char *name); + + //This has to be public for stupid visual studio + class OpcodeSetStrategy { + public: + virtual ~OpcodeSetStrategy() {} //shut up compiler! + virtual void Set(EmuOpcode emu_op, uint16 eq_op) = 0; + }; + +protected: + bool loaded; //true if all opcodes loaded + Mutex MOpcodes; //this only protects the local machine + //in a shared manager, this dosent protect others + + static bool LoadOpcodesFile(const char *filename, OpcodeSetStrategy *s, bool report_errors); +}; + +class MutableOpcodeManager : public OpcodeManager { +public: + MutableOpcodeManager() : OpcodeManager() {} + virtual bool Mutable() { return(true); } + virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op) = 0; +}; + +#ifdef SHARED_OPCODES //quick toggle since only world and zone should possibly use this +//keeps opcodes in shared memory +class SharedOpcodeManager : public OpcodeManager { +public: + virtual ~SharedOpcodeManager() {} + + virtual bool LoadOpcodes(const char *filename, bool report_errors = false); + virtual bool ReloadOpcodes(const char *filename, bool report_errors = false); + + virtual uint16 EmuToEQ(const EmuOpcode emu_op); + virtual EmuOpcode EQToEmu(const uint16 eq_op); + +protected: + class SharedMemStrategy : public OpcodeManager::OpcodeSetStrategy { + public: + virtual ~SharedMemStrategy() {} //shut up compiler! + void Set(EmuOpcode emu_op, uint16 eq_op); + }; + static bool DLLLoadOpcodesCallback(const char *filename); +}; +#endif //SHARED_OPCODES + +//keeps opcodes in regular heap memory +class RegularOpcodeManager : public MutableOpcodeManager { +public: + RegularOpcodeManager(); + virtual ~RegularOpcodeManager(); + + virtual bool Editable() { return(true); } + virtual bool LoadOpcodes(const char *filename, bool report_errors = false); + virtual bool ReloadOpcodes(const char *filename, bool report_errors = false); + + virtual uint16 EmuToEQ(const EmuOpcode emu_op); + virtual EmuOpcode EQToEmu(const uint16 eq_op); + + //implement our editing interface + virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op); + +protected: + class NormalMemStrategy : public OpcodeManager::OpcodeSetStrategy { + public: + virtual ~NormalMemStrategy() {} //shut up compiler! + RegularOpcodeManager *it; + void Set(EmuOpcode emu_op, uint16 eq_op); + }; + friend class NormalMemStrategy; + + uint16 *emu_to_eq; + EmuOpcode *eq_to_emu; + uint32 EQOpcodeCount; + uint32 EmuOpcodeCount; +}; + +//always resolves everything to 0 or OP_Unknown +class NullOpcodeManager : public MutableOpcodeManager { +public: + NullOpcodeManager(); + + virtual bool LoadOpcodes(const char *filename, bool report_errors = false); + virtual bool ReloadOpcodes(const char *filename, bool report_errors = false); + + virtual uint16 EmuToEQ(const EmuOpcode emu_op); + virtual EmuOpcode EQToEmu(const uint16 eq_op); + + //fake it, just used for testing anyways + virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op) {} +}; + +//starts as NullOpcodeManager, but remembers any mappings set +//could prolly have been implemented with an extension to regular, +//by overriding its load methods to be empty. +class EmptyOpcodeManager : public MutableOpcodeManager { +public: + EmptyOpcodeManager(); + + virtual bool LoadOpcodes(const char *filename, bool report_errors = false); + virtual bool ReloadOpcodes(const char *filename, bool report_errors = false); + + virtual uint16 EmuToEQ(const EmuOpcode emu_op); + virtual EmuOpcode EQToEmu(const uint16 eq_op); + + //fake it, just used for testing anyways + virtual void SetOpcode(EmuOpcode emu_op, uint16 eq_op); +protected: + map emu_to_eq; + map eq_to_emu; +}; + +#endif + + + + + + + + + + + + + diff --git a/common/packet_dump.cpp b/common/packet_dump.cpp new file mode 100644 index 000000000..1a1d26d3a --- /dev/null +++ b/common/packet_dump.cpp @@ -0,0 +1,195 @@ +/* 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 "../common/debug.h" +#include +#include +#include + +using namespace std; + +#include "packet_dump.h" +#include "EQPacket.h" +#include "../common/servertalk.h" + +void DumpPacketAscii(const uchar* buf, uint32 size, uint32 cols, uint32 skip) { + // Output as ASCII + for(uint32 i=skip; i 32 && buf[i] < 127) + { + cout << buf[i]; + } + else + { + cout << '.'; + } + } + cout << endl << endl; +} + +void DumpPacketHex(const uchar* buf, uint32 size, uint32 cols, uint32 skip) { + if (size == 0 || size > 39565) + return; + // Output as HEX + char output[4]; + int j = 0; + char* ascii = new char[cols+1]; + memset(ascii, 0, cols+1); + uint32 i; + for(i=skip; i= 32 && buf[i] < 127) { + ascii[j++] = buf[i]; + } + else { + ascii[j++] = '.'; + } +// cout << setfill(0) << setw(2) << hex << (int)buf[i] << " "; + } + uint32 k = ((i-skip)-1)%cols; + if (k < 8) + cout << " "; + for (uint32 h = k+1; h < cols; h++) { + cout << " "; + } + cout << " | " << ascii << endl; + safe_delete_array(ascii); +} + +void DumpPacket(const uchar* buf, uint32 size) +{ + DumpPacketHex(buf, size); +// DumpPacketAscii(buf,size); +} + +void DumpPacket(const ServerPacket* pack, bool iShowInfo) { + if (iShowInfo) { + cout << "Dumping ServerPacket: 0x" << hex << setfill('0') << setw(4) << pack->opcode << dec; + cout << " size:" << pack->size << endl; + } + DumpPacketHex(pack->pBuffer, pack->size); +} + +void DumpPacketBin(const ServerPacket* pack) { + DumpPacketBin(pack->pBuffer, pack->size); +} + +void DumpPacketBin(uint32 data) { + DumpPacketBin((uchar*)&data, sizeof(uint32)); +} + +void DumpPacketBin(uint16 data) { + DumpPacketBin((uchar*)&data, sizeof(uint16)); +} + +void DumpPacketBin(uint8 data) { + DumpPacketBin((uchar*)&data, sizeof(uint8)); +} + + +void DumpPacketBin(const void* iData, uint32 len) { + if (!len) + return; + const uint8* data = (const uint8*) iData; + uint32 k=0; + for (k=0; k 1) + cout << " " << hex << setw(2) << setfill('0') << (int) data[k-3] << dec; + if (tmp > 2) + cout << " " << hex << setw(2) << setfill('0') << (int) data[k-2] << dec; + if (tmp > 3) + cout << " " << hex << setw(2) << setfill('0') << (int) data[k-1] << dec; + cout << endl; +} diff --git a/common/packet_dump.h b/common/packet_dump.h new file mode 100644 index 000000000..c7f552eea --- /dev/null +++ b/common/packet_dump.h @@ -0,0 +1,35 @@ +/* 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 PACKET_DUMP_H +#define PACKET_DUMP_H + +#include "../common/types.h" + +class ServerPacket; + +void DumpPacketAscii(const uchar* buf, uint32 size, uint32 cols=16, uint32 skip=0); +void DumpPacketHex(const uchar* buf, uint32 size, uint32 cols=16, uint32 skip=0); +void DumpPacketBin(const void* data, uint32 len); +void DumpPacket(const uchar* buf, uint32 size); +void DumpPacket(const ServerPacket* pack, bool iShowInfo = false); +void DumpPacketBin(const ServerPacket* pack); +void DumpPacketBin(uint32 data); +void DumpPacketBin(uint16 data); +void DumpPacketBin(uint8 data); + +#endif diff --git a/common/packet_dump_file.cpp b/common/packet_dump_file.cpp new file mode 100644 index 000000000..e02de9348 --- /dev/null +++ b/common/packet_dump_file.cpp @@ -0,0 +1,212 @@ +/* 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 +#include +#include +#include "../common/debug.h" +#include +//#ifdef _CRTDBG_MAP_ALLOC +// #undef new +// #define new new(_NORMAL_BLOCK, __FILE__, __LINE__) +//#endif +#include +#include + +#ifdef _WINDOWS + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#else + #include +#endif + +#include "EQStream.h" +#include "packet_dump_file.h" + +using namespace std; + +void FileDumpPacketAscii(const char* filename, const uchar* buf, uint32 size, uint32 cols, uint32 skip) { + ofstream logfile(filename, ios::app); + // Output as ASCII + for(uint32 i=skip; i 32 && buf[i] < 127) + { + logfile << buf[i]; + } + else + { + logfile << '.'; + } + } + logfile << endl << endl; +} + +void oldFileDumpPacketHex(const char* filename, const uchar* buf, uint32 size, uint32 cols, uint32 skip) +{ + ofstream logfile(filename, ios::app); + // Output as HEX + char output[4]; + for(uint32 i=skip; i= 32 && buf[i] < 127) { + ascii[j++] = buf[i]; + } + else { + ascii[j++] = '.'; + } +// logfile << setfill(0) << setw(2) << hex << (int)buf[i] << " "; + } + uint32 k = ((i-skip)-1)%cols; + if (k < 8) + logfile << " "; + for (uint32 h = k+1; h < cols; h++) { + logfile << " "; + } + logfile << " | " << ascii << endl; + delete[] ascii; +} + +void FileDumpPacketHex(const char* filename, const EQApplicationPacket* app) +{ + FileDumpPacketHex(filename, app->pBuffer, app->size); +} + +void FileDumpPacketAscii(const char* filename, const EQApplicationPacket* app) +{ + FileDumpPacketAscii(filename, app->pBuffer, app->size); +} + +void FileDumpPacket(const char* filename, const uchar* buf, uint32 size) +{ + FilePrintLine(filename, true, "Size: %5i", size); + FileDumpPacketHex(filename, buf, size); +// FileDumpPacketAscii(filename, buf,size); +} + +void FileDumpPacket(const char* filename, const EQApplicationPacket* app) +{ + FilePrintLine(filename, true, "Size: %5i, OPCode: 0x%04x", app->size, app->GetOpcode()); + FileDumpPacketHex(filename, app->pBuffer, app->size); +// FileDumpPacketAscii(filename, app->pBuffer, app->size); +} + +/* + prints a line to the file. if text = 0, prints a blank line + if prefix_timestamp specified, prints the current date/time to the file + ": " + text +*/ +void FilePrintLine(const char* filename, bool prefix_timestamp, const char* text, ...) { + ofstream logfile(filename, ios::app); + if (prefix_timestamp) { + time_t rawtime; + struct tm* gmt_t; + time(&rawtime); + gmt_t = gmtime(&rawtime); + logfile << (gmt_t->tm_year + 1900) << "/" << setw(2) << setfill('0') << (gmt_t->tm_mon + 1) << "/" << setw(2) << setfill('0') << gmt_t->tm_mday << " " << setw(2) << setfill('0') << gmt_t->tm_hour << ":" << setw(2) << setfill('0') << gmt_t->tm_min << ":" << setw(2) << setfill('0') << gmt_t->tm_sec << " GMT"; + } + + if (text != 0) { + va_list argptr; + char buffer[256]; + va_start(argptr, text); + vsnprintf(buffer, 256, text, argptr); + va_end(argptr); + + if (prefix_timestamp) + logfile << ": "; + logfile << buffer; + } + logfile << endl; +} + +void FilePrint(const char* filename, bool newline, bool prefix_timestamp, const char* text, ...) { + ofstream logfile(filename, ios::app); + if (prefix_timestamp) { + time_t rawtime; + struct tm* gmt_t; + time(&rawtime); + gmt_t = gmtime(&rawtime); + logfile << (gmt_t->tm_year + 1900) << "/" << setw(2) << setfill('0') << (gmt_t->tm_mon + 1) << "/" << setw(2) << setfill('0') << gmt_t->tm_mday << " " << setw(2) << setfill('0') << gmt_t->tm_hour << ":" << setw(2) << setfill('0') << gmt_t->tm_min << ":" << setw(2) << setfill('0') << gmt_t->tm_sec << " GMT"; + } + + if (text != 0) { + va_list argptr; + char buffer[1000]; + va_start(argptr, text); + vsnprintf(buffer,1000, text, argptr); + va_end(argptr); + + if (prefix_timestamp) + logfile << ": "; + logfile << buffer; + } + + if (newline) + logfile << endl; +} + diff --git a/common/packet_dump_file.h b/common/packet_dump_file.h new file mode 100644 index 000000000..da3c0d418 --- /dev/null +++ b/common/packet_dump_file.h @@ -0,0 +1,37 @@ +/* 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 PACKET_DUMP_FILE_H +#define PACKET_DUMP_FILE_H + +#include +using namespace std; + +#include "../common/types.h" + +class EQApplicationPacket; + +void FileDumpPacketAscii(const char* filename, const uchar* buf, uint32 size, uint32 cols=16, uint32 skip=0); +void FileDumpPacketHex(const char* filename, const uchar* buf, uint32 size, uint32 cols=16, uint32 skip=0); +void FileDumpPacketHex(const char* filename, const EQApplicationPacket* app); +void FileDumpPacketAscii(const char* filename, const EQApplicationPacket* app); +void FileDumpPacket(const char* filename, const uchar* buf, uint32 size); +void FileDumpPacket(const char* filename, const EQApplicationPacket* app); +void FilePrintLine(const char* filename, bool prefix_timestamp = false, const char* text = 0, ...); +void FilePrint(const char* filename, bool newline = true, bool prefix_timestamp = false, const char* text = 0, ...); +#endif + diff --git a/common/packet_functions.cpp b/common/packet_functions.cpp new file mode 100644 index 000000000..b05bf226f --- /dev/null +++ b/common/packet_functions.cpp @@ -0,0 +1,334 @@ +/* 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 "../common/debug.h" +#include +#include +#include +#include +#include "packet_dump.h" +#include "packet_functions.h" + +#ifndef WIN32 + #include +#endif + +using namespace std; + +void EncryptProfilePacket(EQApplicationPacket* app) { + //EncryptProfilePacket(app->pBuffer, app->size); +} + +void EncryptProfilePacket(uchar* pBuffer, uint32 size) { + uint64* data=(uint64*)pBuffer; + uint64 crypt = 0x659365E7; + uint64 next_crypt; + uint32 len = size >> 3; + + uint64 swap = data[0]; + data[0] = data[len/2]; + data[len/2] = swap; + + for(uint32 i=0; i>0x19)|(data[i]<<0x27))+0x422437A9; + data[i] = (data[i]<<0x07)|(data[i]>>0x39); + data[i] = data[i] - crypt; + crypt = next_crypt; + } +} + +void EncryptZoneSpawnPacket(EQApplicationPacket* app) { + //EncryptZoneSpawnPacket(app->pBuffer, app->size); +} + +void EncryptZoneSpawnPacket(uchar* pBuffer, uint32 size) { + uint64* data=(uint64*)pBuffer; + uint64 crypt = 0x0000; + uint64 next_crypt; + uint32 len = size >> 3; + + uint64 swap = data[0]; + data[0] = data[len/2]; + data[len/2] = swap; + + for(uint32 i=0; i>0x23))+0x659365E7; + data[i] = (data[i]<<0x0e)|(data[i]>>0x32); + data[i] = data[i] - crypt; + crypt = next_crypt; + } +} + +#define MEMORY_DEBUG + +#ifndef MEMORY_DEBUG +#define eqemu_alloc_func Z_NULL +#define eqemu_free_func Z_NULL +#else +//These functions only exist to make my memory profiler +voidpf eqemu_alloc_func(voidpf opaque, uInt items, uInt size); +void eqemu_free_func(voidpf opaque, voidpf address); + +voidpf eqemu_alloc_func(voidpf opaque, uInt items, uInt size) { + voidpf tmp = new char[items*size]; + return(tmp); +} + +void eqemu_free_func(voidpf opaque, voidpf address) { + delete[] (char *)address; +} +#endif + + +int DeflatePacket(const unsigned char* in_data, int in_length, unsigned char* out_data, int max_out_length) { +#ifdef REUSE_ZLIB + static bool inited = false; + static z_stream zstream; + int zerror; + + if(in_data == NULL && out_data == NULL && in_length == 0 && max_out_length == 0) { + //special delete state + deflateEnd(&zstream); + return(0); + } + if(!inited) { + memset(&zstream, 0, sizeof(zstream)); + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + deflateInit(&zstream, Z_FINISH); + } + + zstream.next_in = const_cast(in_data); + zstream.avail_in = in_length; +/* zstream.zalloc = Z_NULL; + zstream.zfree = Z_NULL; + zstream.opaque = Z_NULL; + deflateInit(&zstream, Z_FINISH);*/ + zstream.next_out = out_data; + zstream.avail_out = max_out_length; + zerror = deflate(&zstream, Z_FINISH); + + deflateReset(&zstream); + + if (zerror == Z_STREAM_END) + { +// deflateEnd(&zstream); + return zstream.total_out; + } + else + { +// zerror = deflateEnd(&zstream); + return 0; + } +#else + if(in_data == NULL) { + return(0); + } + + z_stream zstream; + memset(&zstream, 0, sizeof(zstream)); + int zerror; + + zstream.next_in = const_cast(in_data); + zstream.avail_in = in_length; + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + deflateInit(&zstream, Z_FINISH); + zstream.next_out = out_data; + zstream.avail_out = max_out_length; + zerror = deflate(&zstream, Z_FINISH); + + if (zerror == Z_STREAM_END) + { + deflateEnd(&zstream); + return zstream.total_out; + } + else + { + zerror = deflateEnd(&zstream); + return 0; + } +#endif +} + +uint32 InflatePacket(const uchar* indata, uint32 indatalen, uchar* outdata, uint32 outdatalen, bool iQuiet) { +#ifdef REUSE_ZLIB + static bool inited = false; + static z_stream zstream; + int zerror; + + if(indata == NULL && outdata == NULL && indatalen == 0 && outdatalen == 0) { + //special delete state + inflateEnd(&zstream); + return(0); + } + if(!inited) { + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + inflateInit2(&zstream, 15); + } + + zstream.next_in = const_cast(indata); + zstream.avail_in = indatalen; + zstream.next_out = outdata; + zstream.avail_out = outdatalen; + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + + i = inflateInit2( &zstream, 15 ); + if (i != Z_OK) { + return 0; + } + + zerror = inflate( &zstream, Z_FINISH ); + + inflateReset(&zstream); + + if(zerror == Z_STREAM_END) { + return zstream.total_out; + } + else { + if (!iQuiet) { + cout << "Error: InflatePacket: inflate() returned " << zerror << " '"; + if (zstream.msg) + cout << zstream.msg; + cout << "'" << endl; +#ifdef EQDEBUG + DumpPacket(indata-16, indatalen+16); +#endif + } + + if (zerror == -4 && zstream.msg == 0) + { + return 0; + } + + return 0; + } +#else + if(indata == NULL) + return(0); + + z_stream zstream; + int zerror = 0; + int i; + + zstream.next_in = const_cast(indata); + zstream.avail_in = indatalen; + zstream.next_out = outdata; + zstream.avail_out = outdatalen; + zstream.zalloc = eqemu_alloc_func; + zstream.zfree = eqemu_free_func; + zstream.opaque = Z_NULL; + + i = inflateInit2( &zstream, 15 ); + if (i != Z_OK) { + return 0; + } + + zerror = inflate( &zstream, Z_FINISH ); + + if(zerror == Z_STREAM_END) { + inflateEnd( &zstream ); + return zstream.total_out; + } + else { + if (!iQuiet) { + cout << "Error: InflatePacket: inflate() returned " << zerror << " '"; + if (zstream.msg) + cout << zstream.msg; + cout << "'" << endl; +#ifdef EQDEBUG + DumpPacket(indata-16, indatalen+16); +#endif + } + + if (zerror == -4 && zstream.msg == 0) + { + return 0; + } + + zerror = inflateEnd( &zstream ); + return 0; + } +#endif +} + +uint32 roll(uint32 in, uint8 bits) { + return ((in << bits) | (in >> (32-bits))); +} + +uint64 roll(uint64 in, uint8 bits) { + return ((in << bits) | (in >> (64-bits))); +} + +uint32 rorl(uint32 in, uint8 bits) { + return ((in >> bits) | (in << (32-bits))); +} + +uint64 rorl(uint64 in, uint8 bits) { + return ((in >> bits) | (in << (64-bits))); +} + +uint32 CRCLookup(uchar idx) { + if (idx == 0) + return 0x00000000; + + if (idx == 1) + return 0x77073096; + + if (idx == 2) + return roll(CRCLookup(1), 1); + + if (idx == 4) + return 0x076DC419; + + for (uchar b=7; b>0; b--) { + uchar bv = 1 << b; + + if (!(idx ^ bv)) { + // bit is only one set + return ( roll(CRCLookup (4), b - 2) ); + } + + if (idx&bv) { + // bit is set + return( CRCLookup(bv) ^ CRCLookup(idx&(bv - 1)) ); + } + } + + //Failure + return false; +} + +uint32 GenerateCRC(uint32 b, uint32 bufsize, uchar *buf) { + uint32 CRC = (b ^ 0xFFFFFFFF); + uint32 bufremain = bufsize; + uchar* bufptr = buf; + + while (bufremain--) { + CRC = CRCLookup((uchar)(*(bufptr++)^ (CRC&0xFF))) ^ (CRC >> 8); + } + + return (htonl (CRC ^ 0xFFFFFFFF)); +} diff --git a/common/packet_functions.h b/common/packet_functions.h new file mode 100644 index 000000000..4ac7fa0ac --- /dev/null +++ b/common/packet_functions.h @@ -0,0 +1,43 @@ +/* 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 PACKET_FUNCTIONS_H +#define PACKET_FUNCTIONS_H +#include "types.h" + +class EQApplicationPacket; + +uint32 roll(uint32 in, uint8 bits); +uint64 roll(uint64 in, uint8 bits); +uint32 rorl(uint32 in, uint8 bits); +uint64 rorl(uint64 in, uint8 bits); + +void EncryptProfilePacket(EQApplicationPacket* app); +void EncryptProfilePacket(uchar* pBuffer, uint32 size); + +#define EncryptSpawnPacket EncryptZoneSpawnPacket +//void EncryptSpawnPacket(EQApplicationPacket* app); +//void EncryptSpawnPacket(uchar* pBuffer, uint32 size); + +void EncryptZoneSpawnPacket(EQApplicationPacket* app); +void EncryptZoneSpawnPacket(uchar* pBuffer, uint32 size); + +int DeflatePacket(const unsigned char* in_data, int in_length, unsigned char* out_data, int max_out_length); +uint32 InflatePacket(const uchar* indata, uint32 indatalen, uchar* outdata, uint32 outdatalen, bool iQuiet = false); +uint32 GenerateCRC(uint32 b, uint32 bufsize, uchar *buf); + +#endif diff --git a/common/packetfile.cpp b/common/packetfile.cpp new file mode 100644 index 000000000..ad4937e0d --- /dev/null +++ b/common/packetfile.cpp @@ -0,0 +1,445 @@ +#ifndef WIN32 +#include +#else +#include +#endif + +#include +#include +#include +#include "packetfile.h" +#include "../common/eq_opcodes.h" +#include "../common/eq_packet_structs.h" +#include "../common/misc.h" +#include + +using namespace std; + + +PacketFileWriter::PacketFileWriter(bool _force_flush) { + out = NULL; + force_flush = _force_flush; +} + +PacketFileWriter::~PacketFileWriter() { + CloseFile(); +} + +bool PacketFileWriter::SetPacketStamp(const char *name, uint32 stamp) { + FILE *in; + in = fopen(name, "r+b"); + if(in == NULL) { + fprintf(stderr, "Error opening packet file '%s': %s\n", name, strerror(errno)); + return(false); + } + + unsigned long magic = 0; + + if(fread(&magic, sizeof(magic), 1, in) != 1) { + fprintf(stderr, "Error reading header from packet file: %s\n", strerror(errno)); + fclose(in); + return(false); + } + + PacketFileReader *ret = NULL; + if(magic == OLD_PACKET_FILE_MAGIC) { + OldPacketFileHeader *pos = 0; + uint32 stamp_pos = (uint32) &pos->packet_file_stamp; + fseek(in, stamp_pos, SEEK_SET); + OldPacketFileHeader hdr; + hdr.packet_file_stamp = stamp; + if(fwrite(&hdr.packet_file_stamp, sizeof(hdr.packet_file_stamp), 1, in) != 1) { + fprintf(stderr, "Error writting to packet file: %s\n", strerror(errno)); + fclose(in); + return(false); + } + } else if(magic == PACKET_FILE_MAGIC) { + PacketFileHeader *pos = 0; + uint32 stamp_pos = (uint32) &pos->packet_file_stamp; + fseek(in, stamp_pos, SEEK_SET); + PacketFileHeader hdr; + hdr.packet_file_stamp = stamp; + if(fwrite(&hdr.packet_file_stamp, sizeof(hdr.packet_file_stamp), 1, in) != 1) { + fprintf(stderr, "Error writting to packet file: %s\n", strerror(errno)); + fclose(in); + return(false); + } + } else { + fprintf(stderr, "Unknown packet file type 0x%.8x\n", magic); + fclose(in); + return(false); + } + + fclose(in); + return(true); +} + +bool PacketFileWriter::OpenFile(const char *name) { + CloseFile(); + + printf("Opening packet file: %s\n", name); + + out = fopen(name, "wb"); + if(out == NULL) { + fprintf(stderr, "Error opening packet file '%s': %s\n", name, strerror(errno)); + return(false); + } + + PacketFileHeader head; + head.packet_file_magic = PACKET_FILE_MAGIC; + head.packet_file_version = PACKET_FILE_CURRENT_VERSION; + head.packet_file_stamp = time(NULL); + + if(fwrite(&head, sizeof(head), 1, out) != 1) { + fprintf(stderr, "Error writting header to packet file: %s\n", strerror(errno)); + fclose(out); + return(false); + } + + return(true); +} + +void PacketFileWriter::CloseFile() { + if(out != NULL) { + fclose(out); + out = NULL; + printf("Closed packet file.\n"); + } +} + +void PacketFileWriter::WritePacket(uint16 eq_op, uint32 packlen, const unsigned char *packet, bool to_server, const struct timeval &tv) { + if(out == NULL) + return; + + _WriteBlock(eq_op, packet, packlen, to_server, tv); + +/* + Could log only the packets we care about, but this is most of the stream, + so just log them all... + + switch(eq_op) { + case OP_NewZone: + case OP_ZoneSpawns: + case OP_NewSpawn: + case OP_MobUpdate: + case OP_ClientUpdate: + case OP_Death: + case OP_DeleteSpawn: + case OP_CastSpell: + case OP_ShopRequest: + case OP_ShopEndConfirm: + case OP_ItemPacket: + _WriteBlock(eq_op, packet, packlen); + default: + return; + } + */ +} + +bool PacketFileWriter::_WriteBlock(uint16 eq_op, const void *d, uint16 len, bool to_server, const struct timeval &tv) { + if(out == NULL) + return(false); + + PacketFileSection s; + s.opcode = eq_op; + s.len = len; + s.tv_sec = tv.tv_sec; + s.tv_msec = tv.tv_usec/1000; + + if(to_server) + SetToServer(s); + else + SetToClient(s); + + if(fwrite(&s, sizeof(s), 1, out) != 1) { + fprintf(stderr, "Error writting block header: %s\n", strerror(errno)); + return(false); + } + + if(fwrite(d, 1, len, out) != len) { + fprintf(stderr, "Error writting block body: %s\n", strerror(errno)); + return(false); + } + + if(force_flush) + fflush(out); + + return(true); +} + + + + + + +PacketFileReader *PacketFileReader::OpenPacketFile(const char *name) { + FILE *in; + in = fopen(name, "rb"); + if(in == NULL) { + fprintf(stderr, "Error opening packet file '%s': %s\n", name, strerror(errno)); + return(NULL); + } + + unsigned long magic = 0; + + if(fread(&magic, sizeof(magic), 1, in) != 1) { + fprintf(stderr, "Error reading header to packet file: %s\n", strerror(errno)); + fclose(in); + return(NULL); + } + + PacketFileReader *ret = NULL; + if(magic == OLD_PACKET_FILE_MAGIC) { + ret = new OldPacketFileReader(); + } else if(magic == PACKET_FILE_MAGIC) { + ret = new NewPacketFileReader(); + } else { + fprintf(stderr, "Unknown packet file type 0x%.8x\n", magic); + fclose(in); + return(NULL); + } + + if(!ret->OpenFile(name)) { + safe_delete(ret); + return(NULL); + } + + return(ret); +} + +PacketFileReader::PacketFileReader() { + packet_file_stamp = 0; +} + +OldPacketFileReader::OldPacketFileReader() +: PacketFileReader() +{ + in = NULL; +} + +OldPacketFileReader::~OldPacketFileReader() { + CloseFile(); +} + +bool OldPacketFileReader::OpenFile(const char *name) { + CloseFile(); + + //printf("Opening packet file: %s\n", name); + + in = fopen(name, "rb"); + if(in == NULL) { + fprintf(stderr, "Error opening packet file '%s': %s\n", name, strerror(errno)); + return(false); + } + + OldPacketFileHeader head; + + if(fread(&head, sizeof(head), 1, in) != 1) { + fprintf(stderr, "Error reading header to packet file: %s\n", strerror(errno)); + fclose(in); + return(false); + } + + if(head.packet_file_magic != OLD_PACKET_FILE_MAGIC) { + fclose(in); + if(head.packet_file_magic > (OLD_PACKET_FILE_MAGIC)) { + fprintf(stderr, "Error: this is a build file, not a packet file, its allready processed!\n"); + } else { + fprintf(stderr, "Error: this is not a packet file!\n"); + } + return(false); + } + + uint32 now = time(NULL); + if(head.packet_file_stamp > now) { + fprintf(stderr, "Error: invalid timestamp in file. Your clock or the collector's is wrong (%d sec ahead).\n", head.packet_file_stamp-now); + fclose(in); + return(false); + } + + packet_file_stamp = head.packet_file_stamp; + + return(true); +} + +void OldPacketFileReader::CloseFile() { + if(in != NULL) { + fclose(in); + in = NULL; + //printf("Closed packet file.\n"); + } +} + +bool OldPacketFileReader::ResetFile() { + if(in == NULL) + return(false); + rewind(in); + + //gotta read past the header again + OldPacketFileHeader head; + + if(fread(&head, sizeof(head), 1, in) != 1) { + return(false); + } + + return(true); +} + +bool OldPacketFileReader::ReadPacket(uint16 &eq_op, uint32 &packlen, unsigned char *packet, bool &to_server, struct timeval &tv) { + if(in == NULL) + return(false); + if(feof(in)) + return(false); + + OldPacketFileSection s; + + if(fread(&s, sizeof(s), 1, in) != 1) { + if(!feof(in)) + fprintf(stderr, "Error reading section header: %s\n", strerror(errno)); + return(false); + } + + eq_op = s.opcode; + + if(packlen < s.len) { + fprintf(stderr, "Packet buffer is too small! %d < %d, skipping\n", packlen, s.len); + fseek(in, s.len, SEEK_CUR); + return(false); + } + + if(fread(packet, 1, s.len, in) != s.len) { + if(feof(in)) + fprintf(stderr, "Error: EOF encountered when expecting packet data.\n"); + else + fprintf(stderr, "Error reading packet body: %s\n", strerror(errno)); + return(false); + } + + packlen = s.len; + to_server = false; + tv.tv_sec = 0; + tv.tv_usec = 0; + + return(true); +} + + +NewPacketFileReader::NewPacketFileReader() +: PacketFileReader() +{ + in = NULL; +} + +NewPacketFileReader::~NewPacketFileReader() { + CloseFile(); +} + +bool NewPacketFileReader::OpenFile(const char *name) { + CloseFile(); + + //printf("Opening packet file: %s\n", name); + + in = fopen(name, "rb"); + if(in == NULL) { + fprintf(stderr, "Error opening packet file '%s': %s\n", name, strerror(errno)); + return(false); + } + + PacketFileHeader head; + + if(fread(&head, sizeof(head), 1, in) != 1) { + fprintf(stderr, "Error reading header to packet file: %s\n", strerror(errno)); + fclose(in); + return(false); + } + + if(head.packet_file_magic != PACKET_FILE_MAGIC) { + fclose(in); + if(head.packet_file_magic == (PACKET_FILE_MAGIC+1)) { + fprintf(stderr, "Error: this is a build file, not a packet file, its allready processed!\n"); + } else { + fprintf(stderr, "Error: this is not a packet file!\n"); + } + return(false); + } + + uint32 now = time(NULL); + if(head.packet_file_stamp > now) { + fprintf(stderr, "Error: invalid timestamp in file. Your clock or the collector's is wrong (%d sec ahead).\n", head.packet_file_stamp-now); + fclose(in); + return(false); + } + + packet_file_stamp = head.packet_file_stamp; + + return(true); +} + +void NewPacketFileReader::CloseFile() { + if(in != NULL) { + fclose(in); + in = NULL; + //printf("Closed packet file.\n"); + } +} + +bool NewPacketFileReader::ResetFile() { + if(in == NULL) + return(false); + rewind(in); + + //gotta read past the header again + PacketFileHeader head; + + if(fread(&head, sizeof(head), 1, in) != 1) { + return(false); + } + + return(true); +} + +bool NewPacketFileReader::ReadPacket(uint16 &eq_op, uint32 &packlen, unsigned char *packet, bool &to_server, struct timeval &tv) { + if(in == NULL) + return(false); + if(feof(in)) + return(false); + + PacketFileSection s; + + if(fread(&s, sizeof(s), 1, in) != 1) { + if(!feof(in)) + fprintf(stderr, "Error reading section header: %s\n", strerror(errno)); + return(false); + } + + eq_op = s.opcode; + + if(packlen < s.len) { + fprintf(stderr, "Packet buffer is too small! %d < %d, skipping\n", packlen, s.len); + fseek(in, s.len, SEEK_CUR); + return(false); + } + + if(fread(packet, 1, s.len, in) != s.len) { + if(feof(in)) + fprintf(stderr, "Error: EOF encountered when expecting packet data.\n"); + else + fprintf(stderr, "Error reading packet body: %s\n", strerror(errno)); + return(false); + } + + packlen = s.len; + to_server = IsToServer(s); + tv.tv_sec = s.tv_sec; + tv.tv_usec = 1000*s.tv_msec; + + return(true); +} + + + + + + + + diff --git a/common/packetfile.h b/common/packetfile.h new file mode 100644 index 000000000..35585f83a --- /dev/null +++ b/common/packetfile.h @@ -0,0 +1,130 @@ +#ifndef PACKET_FILE_H +#define PACKET_FILE_H + +#include "../common/types.h" +#include +#include +//#include + +//constants used in the packet file header +#define PACKET_FILE_MAGIC 0x93a7b6f6 +#define OLD_PACKET_FILE_MAGIC 0x93a7b6f7 + +#define PACKET_FILE_CURRENT_VERSION 1 + +#pragma pack(1) +//old structs from when I forgot to put the version number in +struct OldPacketFileHeader { + uint32 packet_file_magic; + uint32 packet_file_stamp; +}; +struct OldPacketFileSection { + uint16 opcode; + uint32 len; +}; + +struct PacketFileHeader { + uint32 packet_file_magic; + uint16 packet_file_version; + uint32 packet_file_stamp; +}; + +struct PacketFileSection { + uint16 opcode; + uint8 flags; //mainly for client->server, but others could be added + uint32 tv_sec; + uint16 tv_msec; + uint32 len; +}; +#pragma pack() + +#define TO_SERVER_FLAG 0x01 +#define SetToClient(pfs) pfs.flags = pfs.flags&~TO_SERVER_FLAG +#define SetToServer(pfs) pfs.flags = pfs.flags|TO_SERVER_FLAG +#define IsToClient(pfs) (pfs.flags&TO_SERVER_FLAG == 0) +#define IsToServer(pfs) (pfs.flags&TO_SERVER_FLAG != 0) + + +class PacketFileWriter { +public: + PacketFileWriter(bool force_flush); + ~PacketFileWriter(); + + bool OpenFile(const char *name); + void CloseFile(); + + void WritePacket(uint16 eq_op, uint32 packlen, const unsigned char *packet, bool to_server, const struct timeval &tv); + + static bool SetPacketStamp(const char *file, uint32 stamp); + +protected: + bool _WriteBlock(uint16 eq_op, const void *d, uint16 len, bool to_server, const struct timeval &tv); + + //gzFile out; + FILE *out; + bool force_flush; +}; + + +class PacketFileReader { +public: + PacketFileReader(); + + virtual bool OpenFile(const char *name) = 0; + virtual void CloseFile() = 0; + virtual bool ResetFile() = 0; //aka rewind + + virtual bool ReadPacket(uint16 &eq_op, uint32 &packlen, unsigned char *packet, bool &to_server, struct timeval &tv) = 0; + + time_t GetStamp() { return(time_t(packet_file_stamp)); } + + //factory method to open the right packet file. + static PacketFileReader *OpenPacketFile(const char *name); + +protected: + + uint32 packet_file_stamp; +}; + +class OldPacketFileReader : public PacketFileReader { +public: + OldPacketFileReader(); + virtual ~OldPacketFileReader(); + + bool OpenFile(const char *name); + void CloseFile(); + bool ResetFile(); //aka rewind + + bool ReadPacket(uint16 &eq_op, uint32 &packlen, unsigned char *packet, bool &to_server, struct timeval &tv); + + time_t GetStamp() { return(time_t(packet_file_stamp)); } + +protected: + + //gzFile in; + FILE *in; +}; + +class NewPacketFileReader: public PacketFileReader { +public: + NewPacketFileReader(); + virtual ~NewPacketFileReader(); + + bool OpenFile(const char *name); + void CloseFile(); + bool ResetFile(); //aka rewind + + bool ReadPacket(uint16 &eq_op, uint32 &packlen, unsigned char *packet, bool &to_server, struct timeval &tv); + + time_t GetStamp() { return(time_t(packet_file_stamp)); } + +protected: + + //gzFile in; + FILE *in; +}; + + +#endif + + diff --git a/common/patches/Client62.cpp b/common/patches/Client62.cpp new file mode 100644 index 000000000..ed16c464c --- /dev/null +++ b/common/patches/Client62.cpp @@ -0,0 +1,1035 @@ + +#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 "../Item.h" +#include "Client62_structs.h" + +namespace Client62 { + +static const char *name = "6.2"; +static OpcodeManager *opcodes = NULL; +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 == NULL) { + //TODO: get this file name from the config file + 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; + string pname; + + //register our world signature. + pname = 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 = 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 != NULL) { + //TODO: get this file name from the config file + 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); +} + + +#include "SSDefine.h" + + +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); + 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 = NULL; + + 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 = NULL; + + //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 = NULL; + + //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 = NULL; + + //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; + 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 = NULL; + + //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 = NULL; + + 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 = NULL; + + 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 = NULL; + char *instance = NULL; + const char *protection=(const char *)"\\\\\\\\\\"; + char *sub_items[10] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + 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/Client62.h b/common/patches/Client62.h new file mode 100644 index 000000000..244b6f1ef --- /dev/null +++ b/common/patches/Client62.h @@ -0,0 +1,36 @@ +#ifndef CLIENT62_H_ +#define CLIENT62_H_ + +#include "../StructStrategy.h" +#include "../Item.h" + +class EQStreamIdentifier; + +namespace Client62 { + + //these are the only public member of this namespace. + extern void Register(EQStreamIdentifier &into); + extern void Reload(); + + + + //you should not directly access anything below.. + //I just dont feel like making a seperate header for it. + + class Strategy : public StructStrategy { + public: + Strategy(); + + protected: + + virtual std::string Describe() const; + + //magic macro to declare our opcode processors + #include "SSDeclare.h" + #include "Client62_ops.h" + + }; + +}; + +#endif /*CLIENT62_H_*/ diff --git a/common/patches/Client62_itemfields.h b/common/patches/Client62_itemfields.h new file mode 100644 index 000000000..22419208b --- /dev/null +++ b/common/patches/Client62_itemfields.h @@ -0,0 +1,166 @@ +/* + + +These fields must be in the order of how they are serialized! + + + +*/ + + +/* 000 */ //I(ItemClass) Leave this one off on purpose +/* 001 */ S(Name) +/* 002 */ S(Lore) +/* 003 */ S(IDFile) +/* 004 */ I(ID) +/* 005 */ I(Weight) +/* 006 */ I(NoRent) +/* 007 */ I(NoDrop) +/* 008 */ I(Size) +/* 009 */ I(Slots) +/* 010 */ I(Price) +/* 011 */ I(Icon) +/* 012 */ C("0") +/* 013 */ C("0") +/* 014 */ I(BenefitFlag) +/* 015 */ I(Tradeskills) +/* 016 */ I(CR) +/* 017 */ I(DR) +/* 018 */ I(PR) +/* 019 */ I(MR) +/* 020 */ I(FR) +/* 021 */ I(AStr) +/* 022 */ I(ASta) +/* 023 */ I(AAgi) +/* 024 */ I(ADex) +/* 025 */ I(ACha) +/* 026 */ I(AInt) +/* 027 */ I(AWis) +/* 028 */ I(HP) +/* 029 */ I(Mana) +/* 030 */ I(AC) +/* 031 */ I(Deity) +/* 032 */ C("0") +/* 033 */ I(SkillModValue) +/* 034 */ I(SkillModType) +/* 035 */ I(BaneDmgRace) +/* 036 */ I(BaneDmgAmt) +/* 037 */ I(BaneDmgBody) +/* 038 */ I(Magic) +/* 039 */ I(CastTime_) +/* 040 */ I(ReqLevel) +/* 041 */ I(BardType) +/* 042 */ I(BardValue) +/* 043 */ I(Light) +/* 044 */ I(Delay) +/* 045 */ I(RecLevel) +/* 046 */ I(RecSkill) +/* 047 */ I(ElemDmgType) +/* 048 */ I(ElemDmgAmt) +/* 049 */ I(Range) +/* 050 */ I(Damage) +/* 051 */ I(Color) +/* 052 */ I(Classes) +/* 053 */ I(Races) +/* 054 */ C("0") +/* 055 */ I(MaxCharges) +/* 056 */ I(ItemType) +/* 057 */ I(Material) +/* 058 */ F(SellRate) +/* 059 */ C("0") +/* 060 */ I(CastTime_) +/* 061 */ C("0") +/* 062 */ I(ProcRate) +/* 063 */ I(CombatEffects) +/* 064 */ I(Shielding) +/* 065 */ I(StunResist) +/* 066 */ I(StrikeThrough) +/* 067 */ I(ExtraDmgSkill) +/* 068 */ I(ExtraDmgAmt) +/* 069 */ I(SpellShield) +/* 070 */ I(Avoidance) +/* 071 */ I(Accuracy) +/* 072 */ I(CharmFileID) +/* 073 */ I(FactionMod1) +/* 074 */ I(FactionMod2) +/* 075 */ I(FactionMod3) +/* 076 */ I(FactionMod4) +/* 077 */ I(FactionAmt1) +/* 078 */ I(FactionAmt2) +/* 079 */ I(FactionAmt3) +/* 080 */ I(FactionAmt4) +/* 081 */ S(CharmFile) +/* 082 */ I(AugType) +/* 083 */ I(AugSlotType[0]) +/* 084 */ I(AugSlotType[1]) +/* 085 */ I(AugSlotType[2]) +/* 086 */ I(AugSlotType[3]) +/* 087 */ I(AugSlotType[4]) +/* 088 */ I(LDoNTheme) +/* 089 */ I(LDoNPrice) +/* 090 */ I(LDoNSold) +/* 091 */ I(BagType) +/* 092 */ I(BagSlots) +/* 093 */ I(BagSize) +/* 094 */ I(BagWR) +/* 095 */ I(Book) +/* 096 */ I(BookType) +/* 097 */ S(Filename) +/* 098 */ I(BaneDmgRaceAmt) +/* 099 */ I(AugRestrict) +/* 100 */ I(LoreFlag) +/* 101 */ I(PendingLoreFlag) +/* 102 */ I(ArtifactFlag) +/* 103 */ I(SummonedFlag) +/* 104 */ I(Favor) +/* 105 */ I(FVNoDrop) +/* 106 */ I(Endur) +/* 107 */ I(DotShielding) +/* 108 */ I(Attack) +/* 109 */ I(Regen) +/* 110 */ I(ManaRegen) +/* 111 */ I(Haste) +/* 112 */ I(DamageShield) +/* 113 */ I(RecastDelay) +/* 114 */ I(RecastType) +/* 115 */ I(GuildFavor) +/* 116 */ I(AugDistiller) +/* 117 */ C("0") +/* 118 */ C("0") +/* 119 */ I(Attuneable) +/* 120 */ I(NoPet) +/* 121 */ C("0") +/* 122 */ I(PointType) +/* 123 */ I(PotionBelt) +/* 124 */ I(PotionBeltSlots) +/* 125 */ I(StackSize) +/* 126 */ I(Click.Effect) +/* 127 */ I(Click.Type) +/* 128 */ I(Click.Level2) +/* 129 */ I(Click.Level) +/* 130 */ C("0") +/* 131 */ I(Proc.Effect) +/* 132 */ I(Proc.Type) +/* 133 */ I(Proc.Level2) +/* 134 */ I(Proc.Level) +/* 135 */ C("0") +/* 136 */ I(Worn.Effect) +/* 137 */ I(Worn.Type) +/* 138 */ I(Worn.Level2) +/* 139 */ I(Worn.Level) +/* 140 */ C("0") +/* 141 */ I(Focus.Effect) +/* 142 */ I(Focus.Type) +/* 143 */ I(Focus.Level2) +/* 144 */ I(Focus.Level) +/* 145 */ C("0") +/* 146 */ I(Scroll.Effect) +/* 147 */ I(Scroll.Type) +/* 148 */ I(Scroll.Level2) +/* 149 */ I(Scroll.Level) +/* 150 */ C("0") +#undef I +#undef C +#undef S +#undef F + diff --git a/common/patches/Client62_ops.h b/common/patches/Client62_ops.h new file mode 100644 index 000000000..5f1a1b4a0 --- /dev/null +++ b/common/patches/Client62_ops.h @@ -0,0 +1,36 @@ + +//list of packets we need to encode on the way out: +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_Track) +E(OP_BazaarSearch) +E(OP_RespondAA) +E(OP_DeleteSpawn) +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) +D(OP_CharacterCreate) +D(OP_ItemLinkClick) +D(OP_WhoAllRequest) +D(OP_ReadBook) +D(OP_FaceChange) +D(OP_WearChange) +#undef E +#undef D diff --git a/common/patches/Client62_structs.h b/common/patches/Client62_structs.h new file mode 100644 index 000000000..aa48fc0e6 --- /dev/null +++ b/common/patches/Client62_structs.h @@ -0,0 +1,3101 @@ +#ifndef CLIENT62_STRUCTS_H_ +#define CLIENT62_STRUCTS_H_ + +namespace Client62 { + namespace structs { + + +static const uint32 BUFF_COUNT = 25; +/* +** Compiler override to ensure +** byte aligned structures +*/ +#pragma pack(1) + +struct LoginInfo_Struct { +/*000*/ char login_info[64]; +/*064*/ uint8 unknown064[124]; +/*188*/ uint8 zoning; // 01 if zoning, 00 if not +/*189*/ uint8 unknown189[275]; +/*488*/ +}; + +struct EnterWorld_Struct { +/*000*/ char name[64]; +/*064*/ uint32 tutorial; // 01 on "Enter Tutorial", 00 if not +/*068*/ uint32 return_home; // 01 on "Return Home", 00 if not +}; + +/* Name Approval Struct */ +/* Len: */ +/* Opcode: 0x8B20*/ +struct NameApproval +{ + char name[64]; + uint32 race; + uint32 class_; + uint32 deity; +}; + +/* +** Entity identification struct +** Size: 4 bytes +** OPCodes: OP_DeleteSpawn, OP_Assist +*/ +struct EntityId_Struct +{ +/*00*/ uint32 entity_id; +/*04*/ +}; + +struct Duel_Struct +{ + uint32 duel_initiator; + uint32 duel_target; +}; + +struct DuelResponse_Struct +{ + uint32 target_id; + uint32 entity_id; + uint32 unknown; +}; +/* + Cofruben: + Adventure stuff,not a net one,just one for our use +*/ +static const uint32 ADVENTURE_COLLECT = 0; +static const uint32 ADVENTURE_MASSKILL = 1; +static const uint32 ADVENTURE_NAMED = 2; +static const uint32 ADVENTURE_RESCUE = 3; + +struct AdventureInfo { + uint32 QuestID; + uint32 NPCID; + bool in_use; + uint32 status; + bool ShowCompass; + uint32 Objetive;// can be item to collect,mobs to kill,boss to kill and someone to rescue. + uint32 ObjetiveValue;// number of items,or number of needed mob kills. + char text[512]; + uint8 type; + uint32 minutes; + uint32 points; + float x; + float y; + uint32 zoneid; + uint32 zonedungeonid; +}; +/////////////////////////////////////////////////////////////////////////////// + + +/* +** Color_Struct +** Size: 4 bytes +** Used for convenience +** Merth: Gave struct a name so gcc 2.96 would compile +** +*/ +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; + }; +}; + +/* +** Character Selection Struct +** Length: 1676 Bytes +** +*/ +struct CharacterSelect_Struct { +/*0000*/ uint32 zone[10]; // Characters Current Zone +/*0040*/ uint8 eyecolor1[10]; // Characters Eye Color +/*0050*/ uint8 eyecolor2[10]; // Characters Eye 2 Color +/*0060*/ uint8 hairstyle[10]; // Characters hair style +/*0070*/ uint8 unknown0070[2]; +/*0072*/ uint32 primary[10]; // Characters primary IDFile number +/*0112*/ uint32 race[10]; // Characters Race +/*0152*/ uint8 class_[10]; // Characters Classes +/*0162*/ char name[10][64]; // Characters Names +/*0802*/ uint8 gender[10]; // Characters Gender +/*0812*/ uint8 level[10]; // Characters Levels +/*0822*/ uint8 unknown0822[2]; +/*0824*/ uint32 secondary[10]; // Characters secondary IDFile number +/*0864*/ uint8 face[10]; // Characters Face Type +/*0874*/ uint8 beard[10]; // Characters Beard Type +/*0884*/ uint32 equip[10][9]; // 0=helm, 1=chest, 2=arm, 3=bracer, 4=hand, 5=leg, 6=boot, 7=melee1, 8=melee2 (Might not be) +/*1244*/ uint8 haircolor[10]; // Characters Hair Color +/*1254*/ uint8 gohome[10]; // 1=Go Home available, 0=not +/*1264*/ Color_Struct cs_colors[10][9]; // Characters Equipment Colors +/*1624*/ uint32 deity[10]; // Characters Deity +/*1664*/ uint8 beardcolor[10]; // Characters beard Color +/*1674*/ uint8 unknown1674[2]; +/*1676*/ +}; + +/* +** Generic Spawn Struct +** Length: 257 Bytes +** Fields from old struct not yet found: +** float size; +** float walkspeed; // probably one of the ff 33 33 33 3f +** float runspeed; // probably one of the ff 33 33 33 3f +** uint8 traptype; // 65 is disarmable trap, 66 and 67 are invis triggers/traps +** uint8 npc_armor_graphic; // 0xFF=Player, 0=none, 1=leather, 2=chain, 3=steelplate +** uint8 npc_helm_graphic; // 0xFF=Player, 0=none, 1=leather, 2=chain, 3=steelplate +** +*/ + +/* +** Generic Spawn Struct +** Length: 383 Octets +** Used in: +** spawnZoneStruct +** dbSpawnStruct +** petStruct +** newSpawnStruct +*/ +struct Spawn_Struct +{ +/*0000*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse,a +/*0001*/ uint8 set_to_0xFF[8]; // ***Placeholder (all ff) +/*0009*/ uint8 curHp; // Current hp +/*0010*/ char lastName[32]; // Player's Lastname +/*0042*/ uint8 bodytype; // Bodytype +/*0043*/ uint8 unknown0042[7]; +/*0050*/ uint32 petOwnerId; // If this is a pet, the spawn id of owner +/*0054*/ signed deltaHeading:10;// change in heading + signed x:19; // x coord + signed padding0054:3; // ***Placeholder +/*0058*/ signed y:19; // y coord + signed animation:10; // ***Placeholder (seems like speed) + signed padding0058:3; // animation +/*0062*/ signed z:19; // z coord + signed deltaY:13; // change in y +/*0066*/ signed deltaX:13; // change in x + unsigned heading:12; // heading + signed padding0066:7; // ***Placeholder +/*0070*/ signed deltaZ:13; // change in z + signed padding0070:19; // ***Placeholder +/*0074*/ uint16 deity; // Player's Deity +/*0076*/ uint8 unknown[2]; +/*0078*/ union + { + struct + { + /*0078*/ uint32 equip_helmet; // Equipment: Helmet Visual + /*0082*/ uint32 equip_chest; // Equipment: Chest Visual + /*0086*/ uint32 equip_arms; // Equipment: Arms Visual + /*0090*/ uint32 equip_bracers; // Equipment: Bracers Visual + /*0094*/ uint32 equip_hands; // Equipment: Hands Visual + /*0098*/ uint32 equip_legs; // Equipment: Legs Visual + /*0102*/ uint32 equip_feet; // Equipment: Feet Visual + /*0106*/ uint32 equip_primary; // Equipment: Primary Visual + /*0110*/ uint32 equip_secondary; // Equipment: Secondary Visual + } equip; + /*0078*/ uint32 equipment[9]; // Array elements correspond to struct equipment above + }; +/*0114*/ uint8 unknown0114[5]; +/*0119*/ uint8 afk; // 0=no, 1=afk +/*0120*/ uint32 spawnId; // Spawn Id +/*0124*/ uint8 is_pet; // 0=no, 1=yes +/*0125*/ uint8 gm; // 0=no, 1=gm +/*0126*/ uint8 unknown0126[4]; +/*0130*/ uint8 aaitle; // 0=none, 1=general, 2=archtype, 3=class +/*0131*/ uint8 unknown0132[24]; +/*0155*/ uint8 anon; // 0=normal, 1=anon, 2=roleplay +/*0156*/ uint8 unknown0156; +/*0157*/ uint8 face; // Face id for players +/*0159*/ uint8 helm; +/*0159*/ uint8 unknown0159; +/*0160*/ float runspeed; // Speed when running +/*0164*/ uint8 eyecolor1; // Player's left eye color +/*0165*/ uint8 beard; //not 100% verified, but slightly verified +/*0166*/ uint8 is_npc; // 0=no, 1=yes +/*0167*/ uint8 flymode; //seems to be 3 for 'useable' npc classes +/*0168*/ uint32 guildID; // Current guild +/*0172*/ union + { + struct + { + /*0172*/ Color_Struct color_helmet; // Color of helmet item + /*0176*/ Color_Struct color_chest; // Color of chest item + /*0180*/ Color_Struct color_arms; // Color of arms item + /*0184*/ Color_Struct color_bracers; // Color of bracers item + /*0188*/ Color_Struct color_hands; // Color of hands item + /*0192*/ Color_Struct color_legs; // Color of legs item + /*0196*/ Color_Struct color_feet; // Color of feet item + /*0200*/ Color_Struct color_primary; // Color of primary item + /*0204*/ Color_Struct color_secondary; // Color of secondary item + } equipment_colors; + /*0172*/ Color_Struct colors[9]; // Array elements correspond to struct equipment_colors above + }; +/*0208*/ uint8 hairstyle; +/*0209*/ float walkspeed; // Speed when walking +/*0213*/ uint8 unknown0213[3]; +/*0216*/ uint8 class_; // Player's class +/*0217*/ uint8 beardcolor; // Beard color +/*0218*/ float size; // Model size +/*0222*/ uint8 findable; // 0=can't be found, 1=can be found +/*0223*/ float unknown_float; +/*0227*/ char suffix[32]; // Player's suffix (of Veeshan, etc.) +/*0259*/ uint8 unknown0259[4]; //[0] is sometimes 100 +/*0263*/ uint8 guildrank; // 0=normal, 1=officer, 2=leader +/*0264*/ uint8 unknown0264[3]; +union +{ +/*0267*/ uint8 equip_chest2; // Second place in packet for chest texture (usually 0xFF in live packets) + // Not sure why there are 2 of them, but it effects chest texture! +/*0267*/ uint8 mount_color; // drogmor: 0=white, 1=black, 2=green, 3=red + // horse: 0=brown, 1=white, 2=black, 3=tan +}; +/*0268*/ uint32 race; // Spawn race +/*0272*/ uint8 invis; // Invis (0=not, 1=invis) +/*0273*/ uint8 unknown0276[5]; +/*0278*/ uint8 lfg; // 0=off, 1=lfg on +/*0279*/ uint8 level; // Spawn Level +/*0280*/ uint8 haircolor; // Hair color +/*0281*/ uint8 max_hp; //(name prolly wrong)takes on the value 100 for players, 100 or 110 for NPCs and 120 for PC corpses... +/*0282*/ uint8 light; // Spawn's lightsource +/*0283*/ uint8 gender; // Gender (0=male, 1=female) +/*0284*/ char name[64]; // Player's Name +/*0348*/ uint8 eyecolor2; // Left eye color +/*0349*/ char title[32]; // Title +/*0381*/ uint8 unknown0381[2]; +}; /*0383*/ + +/* +** New Spawn +** Length: 176 Bytes +** OpCode: 4921 +*/ +struct NewSpawn_Struct +{ + struct Spawn_Struct spawn; // Spawn Information +}; + +struct ClientZoneEntry_Struct { +/*0000*/ uint32 unknown00; +/*0004*/ char char_name[64]; // Character Name +}; + +/* +** Server Zone Entry Struct +** Length: 452 Bytes +** OPCodes: OP_ServerZoneEntry +** +*/ +struct ServerZoneEntry_Struct +{ + struct NewSpawn_Struct player; +}; + +struct NewZone_Struct { +/*0000*/ char char_name[64]; // Character Name +/*0064*/ char zone_short_name[32]; // Zone Short Name +/*0096*/ char zone_long_name[278]; // Zone Long Name +/*0374*/ uint8 ztype; // Zone type (usually FF) +/*0375*/ uint8 fog_red[4]; // Zone fog (red) +/*0379*/ uint8 fog_green[4]; // Zone fog (green) +/*0383*/ uint8 fog_blue[4]; // Zone fog (blue) +/*0387*/ uint8 unknown323; +/*0388*/ float fog_minclip[4]; +/*0404*/ float fog_maxclip[4]; +/*0420*/ float gravity; +/*0424*/ uint8 time_type; +/*0425*/ uint8 unknown360[49]; +/*0474*/ uint8 sky; // Sky Type +/*0475*/ uint8 unknown331[13]; // ***Placeholder +/*0488*/ float zone_exp_multiplier; // Experience Multiplier +/*0492*/ float safe_y; // Zone Safe Y +/*0496*/ float safe_x; // Zone Safe X +/*0500*/ float safe_z; // Zone Safe Z +/*0504*/ float max_z; // Guessed +/*0508*/ float underworld; // Underworld, min z (Not Sure?) +/*0512*/ float minclip; // Minimum View Distance +/*0516*/ float maxclip; // Maximum View DIstance +/*0520*/ uint8 unknown_end[84]; // ***Placeholder +/*0604*/ char zone_short_name2[68]; +/*0672*/ char unknown672[12]; +/*0684*/ uint16 zone_id; +/*0686*/ uint16 zone_instance; +/*0688*/ uint32 unknown688; +/*0682*/ uint8 unknown692[4]; +}; + +/* +** Memorize Spell Struct +** Length: 12 Bytes +** +*/ +struct MemorizeSpell_Struct { +uint32 slot; // Spot in the spell book/memorized slot +uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) +uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming +uint32 unknown12; +}; + +/* +** Make Charmed Pet +** Length: 12 Bytes +** +*/ +struct Charm_Struct { +/*00*/ uint32 owner_id; +/*04*/ uint32 pet_id; +/*08*/ uint32 command; // 1: make pet, 0: release pet +/*12*/ +}; + +struct InterruptCast_Struct +{ + uint32 spawnid; + uint32 messageid; + char message[0]; +}; + +struct DeleteSpell_Struct +{ +/*000*/int16 spell_slot; +/*002*/uint8 unknowndss002[2]; +/*004*/uint8 success; +/*005*/uint8 unknowndss006[3]; +/*008*/ +}; + +struct ManaChange_Struct +{ + uint32 new_mana; // New Mana AMount + uint32 stamina; + uint32 spell_id; + uint32 unknown12; +}; + +struct SwapSpell_Struct +{ + uint32 from_slot; + uint32 to_slot; + + +}; + +struct BeginCast_Struct +{ + // len = 8 +/*000*/ uint16 caster_id; +/*002*/ uint16 spell_id; +/*004*/ uint32 cast_time; // in miliseconds +}; + +struct CastSpell_Struct +{ + uint32 slot; + uint32 spell_id; + uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast + uint32 target_id; + uint8 cs_unknown[4]; +}; + +/* +** SpawnAppearance_Struct +** Changes client appearance for all other clients in zone +** Size: 8 bytes +** Used in: OP_SpawnAppearance +** +*/ +struct SpawnAppearance_Struct +{ +/*0000*/ uint16 spawn_id; // ID of the spawn +/*0002*/ uint16 type; // Values associated with the type +/*0004*/ uint32 parameter; // Type of data sent +/*0008*/ +}; + + +// solar: this is used inside profile +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; +/*014*/ uint8 Unknown012[4]; +}; + +struct SpellBuffFade_Struct { +/*000*/ uint32 entityid; +/*004*/ uint8 slot; +/*005*/ uint8 level; +/*006*/ uint8 effect; +/*007*/ uint8 unknown7; +/*008*/ uint32 spellid; +/*012*/ uint32 duration; +/*016*/ uint32 unknown016; +/*020*/ uint32 unknown020; //prolly global player ID +/*024*/ uint32 slotid; +/*028*/ uint32 bufffade; +/*032*/ +}; + +struct ItemNamePacket_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 unkown004; +/*008*/ char name[64]; +/*072*/ +}; + +// Length: 10 +struct ItemProperties_Struct { + +uint8 unknown01[2]; +uint8 charges; +uint8 unknown02[13]; +}; + +struct GMTrainee_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ uint32 skills[73]; + /*300*/ uint8 unknown300[148]; + /*448*/ +}; + +struct GMTrainEnd_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ +}; + +struct GMSkillChange_Struct { +/*000*/ uint16 npcid; +/*002*/ uint8 unknown1[2]; // something like PC_ID, but not really. stays the same thru the session though +/*002*/ uint16 skillbank; // 0 if normal skills, 1 if languages +/*002*/ uint8 unknown2[2]; +/*008*/ uint16 skill_id; +/*010*/ uint8 unknown3[2]; +}; +struct ConsentResponse_Struct { + char grantname[64]; + char ownername[64]; + uint8 permission; + char zonename[32]; +}; + +/* +** Name Generator Struct +** Length: 72 bytes +** OpCode: 0x0290 +*/ +struct NameGeneration_Struct +{ +/*0000*/ uint32 race; +/*0004*/ uint32 gender; +/*0008*/ char name[64]; +/*0072*/ +}; + +/* +** Character Creation struct +** Length: 140 Bytes +** OpCode: 0x0113 +*/ +struct CharCreate_Struct +{ + /*0000*/ uint32 class_; //guess + /*0004*/ char name[64]; + /*0068*/ uint32 beardcolor; // credit goes to vesuvias for appearance stuff + /*0072*/ uint32 beard; + /*0076*/ uint32 haircolor; + /*0080*/ int32 gender; + /*0084*/ int32 race; + /*0088*/ int32 start_zone; + /*0092*/ int32 hairstyle; + /*0096*/ uint32 deity; + ///*0072*/ int32 deity; + + + // 0 = odus + // 1 = qeynos + // 2 = halas + // 3 = rivervale + // 4 = freeport + // 5 = neriak + // 6 = gukta/grobb + // 7 = ogguk + // 8 = kaladim + // 9 = gfay + // 10 = felwithe + // 11 = akanon + // 12 = cabalis + // 13 = shar vahl + + +/*0100*/ int32 STR; +/*0104*/ int32 STA; +/*0108*/ int32 AGI; +/*0112*/ int32 DEX; +/*0116*/ int32 WIS; +/*0120*/ int32 INT; +/*0124*/ int32 CHA; +/*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; +}; + +/* + *Used in PlayerProfile + */ +struct AA_Array +{ + uint32 AA; + uint32 value; +}; + + +static const uint32 MAX_PP_DISCIPLINES = 100; + +struct Disciplines_Struct { + uint32 values[MAX_PP_DISCIPLINES]; +}; + +static const uint32 MAX_PLAYER_TRIBUTES = 5; +static const uint32 MAX_PLAYER_BANDOLIER = 4; +static const uint32 MAX_PLAYER_BANDOLIER_ITEMS = 4; +static const uint32 TRIBUTE_NONE = 0xFFFFFFFF; +struct Tribute_Struct { + uint32 tribute; + uint32 tier; +}; + +//len = 72 +struct BandolierItem_Struct { + uint32 item_id; + uint32 icon; + char item_name[64]; +}; + +//len = 320 +enum { //bandolier item positions + bandolierMainHand = 0, + bandolierOffHand, + bandolierRange, + bandolierAmmo +}; +struct Bandolier_Struct { + char name[32]; + BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; +}; +struct PotionBelt_Struct { + BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; +}; + +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]; +}; +struct RaidLeadershipAA_Struct { + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; +}; + +/* +** Player Profile +** +** Length: 4308 bytes +** OpCode: 0x006a + */ +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 = 75; +static const uint32 MAX_PP_AA_ARRAY = 240; +static const uint32 MAX_GROUP_MEMBERS = 6; +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*/ uint32 bind_zone_id; // Zone player is bound in +/*0124*/ uint32 unknown0124[4]; +/*0140*/ float bind_x[4]; // Bind loc x coord +/*0156*/ float zone_safe_x; +/*0160*/ float bind_y[4]; // Bind loc y coord +/*0176*/ float zone_safe_y; +/*0180*/ float bind_z[4]; // Bind loc z coord +/*0196*/ float zone_safe_z; +/*0200*/ float bind_heading[4]; // +/*0216*/ float zone_safe_heading; +/*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 unknown0245[7]; // +/*0252*/ uint32 intoxication; +/*0256*/ uint32 spellSlotRefresh[MAX_PP_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[9]; // Item texture/material of worn/held items +/*0348*/ uint8 unknown0256[44]; +/*0396*/ Color_Struct item_tint[9]; +/*0432*/ AA_Array aa_array[MAX_PP_AA_ARRAY]; +/*2348*/ float unknown2348; //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 unknown1496; +/*2460*/ uint32 points; // Unspent Practice points +/*2464*/ uint32 mana; // current mana +/*2468*/ uint32 cur_hp; // current hp +/*2472*/ uint32 unknown1512; // 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 unknown1545[47]; // ? +/*2552*/ uint8 languages[MAX_PP_LANGUAGE]; +/*2580*/ uint8 unknown1620[4]; +/*2584*/ uint32 spell_book[MAX_PP_SPELLBOOK]; +/*4184*/ uint8 unknown3224[448]; // all 0xff +/*4632*/ uint32 mem_spells[MAX_PP_MEMSPELL]; +/*4668*/ uint8 unknown3704[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 unknown3756[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 unknown3812[24]; // @bp unknown skills? +/*4796*/ uint32 skills[MAX_PP_SKILL]; +/*5096*/ uint8 unknown5096[284]; // @bp unknown skills? +/*5380*/ uint32 pvp2; // +/*5384*/ uint32 unknown4420; // +/*5388*/ uint32 pvptype; // +/*5392*/ uint32 unknown4428; // +/*5396*/ uint32 ability_down; // Doodman - Guessing +/*5400*/ uint8 unknown4436[8]; // +/*5408*/ uint32 autosplit; //not used right now +/*5412*/ uint8 unknown4448[8]; +/*5420*/ uint32 zone_change_count; // Number of times user has zoned in their career (guessing) +/*5424*/ uint8 unknown4460[28]; // +/*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 unknown4496[16]; // +/*5476*/ int32 hunger_level; +/*5480*/ int32 thirst_level; +/*5484*/ uint32 ability_up; +/*5488*/ char unknown4524[16]; +/*5504*/ uint16 zone_id; // Current zone of the player +/*5506*/ uint16 zoneInstance; // Instance ID +/*5508*/ SpellBuff_Struct buffs[BUFF_COUNT]; // Buffs currently on the player +/*6008*/ char groupMembers[6][64]; // +/*6392*/ char unknown6392[656]; +/*7048*/ uint32 entityid; +/*7052*/ uint32 leadAAActive; +/*7056*/ uint32 unknown7056; +/*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*/ uint8 unknown5940[112]; +/*7196*/ uint32 tribute_time_remaining; //in miliseconds +/*7200*/ uint32 unknown6048; +/*7204*/ uint32 career_tribute_points; +/*7208*/ uint32 unknown6056; +/*7212*/ uint32 tribute_points; +/*7216*/ uint32 unknown6064; +/*7220*/ uint32 tribute_active; //1=active +/*7224*/ Tribute_Struct tributes[MAX_PLAYER_TRIBUTES]; +/*7264*/ Disciplines_Struct disciplines; //fathernitwit: 10-06-04 +/*7664*/ char unknown7464[240]; +/*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*/ LeadershipAA_Struct leader_abilities; +/*8052*/ uint8 unknown8052[132]; +/*8184*/ uint32 air_remaining; +/*8188*/ uint8 unknown8188[4608]; +/*12796*/ uint32 aapoints_spent; +/*12800*/ uint32 expAA; +/*12804*/ uint32 aapoints; //avaliable, unspent +/*12808*/ uint8 unknown12808[36]; +/*12844*/ Bandolier_Struct bandoliers[MAX_PLAYER_BANDOLIER]; +/*14124*/ uint8 unknown14124[5120]; +/*19244*/ PotionBelt_Struct potionbelt; //there should be 3 more of these +/*19532*/ uint8 unknown19532[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 unknown19559[5]; // ***Placeholder (6/29/2005) +/*19564*/ uint32 unknown15964; +/*19568*/ +}; + +/* +** Client Target Struct +** Length: 2 Bytes +** OpCode: 6221 +*/ +struct ClientTarget_Struct { +/*000*/ uint32 new_target; // Target ID +}; + +/* +** Target Rejection Struct +** Length: 12 Bytes +** OpCode: OP_TargetReject +*/ +struct TargetReject_Struct { +/*00*/ uint8 unknown00[12]; +}; + +struct PetCommand_Struct { +/*000*/ uint32 command; +/*004*/ uint32 unknown; +}; + +/* +** Delete Spawn +** Length: 4 Bytes +** OpCode: OP_DeleteSpawn +*/ +struct DeleteSpawn_Struct +{ +/*00*/ uint32 spawn_id; // Spawn ID to delete +/*04*/ +}; + +/* +** Channel Message received or sent +** Length: 144 Bytes + Variable Length + 1 +** OpCode: OP_ChannelMessage +** +*/ +struct ChannelMessage_Struct +{ +/*000*/ char targetname[64]; // Tell recipient +/*064*/ char sender[64]; // The senders name (len might be wrong) +/*128*/ uint32 language; // Language +/*132*/ uint32 chan_num; // Channel +/*136*/ uint32 cm_unknown4[2]; // ***Placeholder +/*144*/ uint32 skill_in_language; // The players skill in this language? might be wrong +/*148*/ char message[0]; // Variable length message +}; + +/* +** Special Message +** Length: 4 Bytes + Variable Text Length + 1 +** OpCode: OP_SpecialMesg +** +*/ +/* + Theres something wrong with this... example live packet: +Server->Client: [ Opcode: OP_SpecialMesg (0x0fab) Size: 244 ] + 0: 01 02 00 0A 00 00 00 09 - 05 00 00 42 61 72 73 74 | ...........Barst + 16: 72 65 20 53 6F 6E 67 77 - 65 61 76 65 72 00 7C F9 | re Songweaver.|. + 32: FF FF 84 FF FF FF 03 00 - 00 00 47 72 65 65 74 69 | ..........Greeti + +*/ +struct SpecialMesg_Struct +{ +/*00*/ char header[3]; // 04 04 00 <-- for #emote style msg +/*03*/ uint32 msg_type; // Color of text (see MT_*** below) +/*07*/ uint32 target_spawn_id; // Who is it being said to? +/*11*/ char sayer[1]; // Who is the source of the info +/*12*/ uint8 unknown12[12]; +/*24*/ char message[1]; // What is being said? +}; + +/* +** When somebody changes what they're wearing +** or give a pet a weapon (model changes) +** Length: 16 Bytes +** Opcode: 9220 +*/ +struct WearChange_Struct{ +/*000*/ uint16 spawn_id; +/*002*/ uint16 material; +/*004*/ Color_Struct color; +/*009*/ uint8 wear_slot_id; +}; + +/* +** Type: Bind Wound Structure +** Length: 8 Bytes +*/ +//Fixed for 7-14-04 patch +struct BindWound_Struct +{ +/*002*/ uint16 to; // TargetID +/*004*/ uint16 unknown2; // ***Placeholder +/*006*/ uint16 type; +/*008*/ uint16 unknown6; +}; + + +/* +** Type: Zone Change Request (before hand) +** Length: 88 bytes +** OpCode: a320 +*/ + +struct ZoneChange_Struct { +/*000*/ char char_name[64]; // Character Name +/*064*/ uint16 zoneID; +/*066*/ uint16 instanceID; +/*068*/ float y; +/*072*/ float x; +/*076*/ float z; +/*080*/ uint32 zone_reason; //0x0A == death, I think +/*084*/ int32 success; // =0 client->server, =1 server->client, -X=specific error +/*088*/ +}; + +struct RequestClientZoneChange_Struct { +/*00*/ uint16 zone_id; +/*02*/ uint16 instance_id; +/*04*/ float y; +/*08*/ float x; +/*12*/ float z; +/*16*/ float heading; +/*20*/ uint32 type; //unknown... values +}; + +struct Animation_Struct { +/*00*/ uint16 spawnid; +/*02*/ uint8 action; +/*03*/ uint8 value; +/*04*/ +}; + +// solar: this is what causes the caster to animate and the target to +// get the particle effects around them when a spell is cast +// also causes a buff icon +struct Action_Struct +{ + /* 00 */ uint16 target; // id of target + /* 02 */ uint16 source; // id of caster + /* 04 */ uint16 level; // level of caster + /* 06 */ uint16 instrument_mod; + /* 08 */ uint32 unknown08; + /* 12 */ uint16 unknown16; +// some kind of sequence that's the same in both actions +// as well as the combat damage, to tie em together? + /* 14 */ uint32 sequence; + /* 18 */ uint32 unknown18; + /* 22 */ uint8 type; // 231 (0xE7) for spells + /* 23 */ uint32 unknown23; + /* 27 */ uint16 spell; // spell id being cast + /* 29 */ uint8 unknown29; +// this field seems to be some sort of success flag, if it's 4 + /* 30 */ uint8 buff_unknown; // if this is 4, a buff icon is made + /* 31 */ +}; + +// solar: this is what prints the You have been struck. and the regular +// melee messages like You try to pierce, etc. It's basically the melee +// and spell damage message +struct CombatDamage_Struct +{ +/* 00 */ uint16 target; +/* 02 */ uint16 source; +/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells +/* 05 */ uint16 spellid; +/* 07 */ uint32 damage; +/* 11 */ uint32 unknown11; +/* 15 */ uint32 sequence; // see above notes in Action_Struct +/* 19 */ uint32 unknown19; +/* 23 */ +}; + +/* +** Consider Struct +** Length: 24 Bytes +** OpCode: 3721 +*/ +struct Consider_Struct{ +/*000*/ uint32 playerid; // PlayerID +/*004*/ uint32 targetid; // TargetID +/*008*/ uint32 faction; // Faction +/*0012*/ uint32 level; // Level +/*016*/ int32 cur_hp; // Current Hitpoints +/*020*/ int32 max_hp; // Maximum Hitpoints +/*024*/ uint8 pvpcon; // Pvp con flag 0/1 +/*025*/ uint8 unknown3[3]; +}; + +/* +** Spawn Death Blow +** Length: 32 Bytes +** OpCode: 0114 +*/ +struct Death_Struct +{ +/*000*/ uint32 spawn_id; +/*004*/ uint32 killer_id; +/*008*/ uint32 corpseid; // was corpseid +/*012*/ uint32 attack_skill; // was type +/*016*/ uint32 spell_id; +/*020*/ uint32 bindzoneid; //bindzoneid? +/*024*/ uint32 damage; +/*028*/ uint32 unknown028; +}; + +struct BecomeCorpse_Struct { + uint32 spawn_id; + float y; + float x; + float z; +}; + +/* +** Spawn position update +** Struct sent from server->client to update position of +** another spawn's position update in zone (whether NPC or PC) +** +*/ +struct PlayerPositionUpdateServer_Struct +{ +/*0000*/ uint16 spawn_id; +/*0002*/ int32 delta_heading:10, // change in heading + x_pos:19, // x coord + padding0002:3; // ***Placeholder +/*0006*/ int32 y_pos:19, // y coord + animation:10, // animation + padding0006:3; // ***Placeholder +/*0010*/ int32 z_pos:19, // z coord + delta_y:13; // change in y +/*0014*/ int32 delta_x:13, // change in x + heading:12, // heading + padding0014:7; // ***Placeholder +/*0018*/ int32 delta_z:13, // change in z + padding0018:19; // ***Placeholder +/*0022*/ +}; + +/* +** Player position update +** Struct sent from client->server to update +** player position on server +** +*/ +struct PlayerPositionUpdateClient_Struct +{ +/*0000*/ uint16 spawn_id; +/*0022*/ uint16 sequence; //increments one each packet +/*0004*/ float y_pos; // y coord +/*0008*/ float delta_z; // Change in z +/*0016*/ float delta_x; // Change in x +/*0012*/ float delta_y; // Change in y +/*0020*/ int32 animation:10, // animation + delta_heading:10, // change in heading + padding0020:12; // ***Placeholder (mostly 1) +/*0024*/ float x_pos; // x coord +/*0028*/ float z_pos; // z coord +/*0034*/ uint16 heading:12, // Directional heading + padding0004:4; // ***Placeholder +/*0032*/ uint8 unknown0006[2]; // ***Placeholder +/*0036*/ +}; + +/* +** Spawn HP Update +** Length: 10 Bytes +** OpCode: OP_HPUpdate +*/ +struct SpawnHPUpdate_Struct +{ +/*00*/ uint32 cur_hp; // Id of spawn to update +/*04*/ int32 max_hp; // Maximum hp of spawn +/*08*/ int16 spawn_id; // Current hp of spawn +/*10*/ +}; +struct SpawnHPUpdate_Struct2 +{ +/*01*/ int16 spawn_id; +/*00*/ uint8 hp; +}; +/* +** Stamina +** Length: 8 Bytes +** OpCode: 5721 +*/ +struct Stamina_Struct { +/*00*/ uint32 food; // (low more hungry 127-0) +/*02*/ uint32 water; // (low more thirsty 127-0) +}; + +/* +** Level Update +** Length: 12 Bytes +*/ +struct LevelUpdate_Struct +{ +/*00*/ uint32 level; // New level +/*04*/ uint32 level_old; // Old level +/*08*/ uint32 exp; // Current Experience +}; + +/* +** Experience Update +** Length: 14 Bytes +** OpCode: 9921 +*/ +struct ExpUpdate_Struct +{ +/*0000*/ uint32 exp; // Current experience ratio from 0 to 330 +/*0004*/ uint32 aaxp; // @BP ?? +}; + +/* +** Item Packet Struct - Works on a variety of opcodes +** Packet Types: See ItemPacketType enum +** +*/ +enum ItemPacketType +{ + ItemPacketViewLink = 0x00, + ItemPacketTradeView = 0x65, + ItemPacketLoot = 0x66, + ItemPacketTrade = 0x67, + ItemPacketCharInventory = 0x69, + ItemPacketSummonItem = 0x6A, + ItemPacketTributeItem = 0x6C, + ItemPacketMerchant = 0x64, + ItemPacketWorldContainer = 0x6B +}; +struct ItemPacket_Struct +{ +/*00*/ ItemPacketType PacketType; +/*04*/ char SerializedItem[1]; +/*xx*/ +}; + +struct BulkItemPacket_Struct +{ +/*00*/ char SerializedItem[0]; +/*xx*/ +}; + +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]; +}; + + +struct MoveItem_Struct +{ +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +}; + +// +// from_slot/to_slot +// -1 - destroy +// 0 - cursor +// 1 - inventory +// 2 - bank +// 3 - trade +// 4 - shared bank +// +// cointype +// 0 - copeer +// 1 - silver +// 2 - gold +// 3 - platinum +// +static const uint32 COINTYPE_PP = 3; +static const uint32 COINTYPE_GP = 2; +static const uint32 COINTYPE_SP = 1; +static const uint32 COINTYPE_CP = 0; + +struct MoveCoin_Struct +{ + int32 from_slot; + int32 to_slot; + int32 cointype1; + int32 cointype2; + int32 amount; +}; +struct TradeCoin_Struct{ + uint32 trader; + uint8 slot; + uint16 unknown5; + uint8 unknown7; + uint32 amount; +}; +struct TradeMoneyUpdate_Struct{ + uint32 trader; + uint32 type; + uint32 amount; +}; +/* +** Surname struct +** Size: 100 bytes +*/ +struct Surname_Struct +{ +/*0000*/ char name[64]; +/*0064*/ uint32 unknown0064; +/*0068*/ char lastname[32]; +/*0100*/ +}; + +struct GuildsListEntry_Struct { + char name[64]; +}; + +static const uint32 MAX_NUMBER_GUILDS = 1500; +struct GuildsList_Struct { + uint8 head[64]; // First on guild list seems to be empty... + GuildsListEntry_Struct Guilds[MAX_NUMBER_GUILDS]; +}; + +struct GuildUpdate_Struct { + uint32 guildID; + GuildsListEntry_Struct entry; +}; + +/* +** Money Loot +** Length: 22 Bytes +** OpCode: 5020 +*/ +struct moneyOnCorpseStruct { +/*0000*/ uint8 response; // 0 = someone else is, 1 = OK, 2 = not at this time +/*0001*/ uint8 unknown1; // = 0x5a +/*0002*/ uint8 unknown2; // = 0x40 +/*0003*/ uint8 unknown3; // = 0 +/*0004*/ uint32 platinum; // Platinum Pieces +/*0008*/ uint32 gold; // Gold Pieces + +/*0012*/ uint32 silver; // Silver Pieces +/*0016*/ uint32 copper; // Copper Pieces +}; + +//opcode = 0x5220 +// size 292 + + +struct LootingItem_Struct { +/*000*/ uint32 lootee; +/*002*/ uint32 looter; +/*004*/ uint16 slot_id; +/*006*/ uint8 unknown3[2]; +/*008*/ uint32 auto_loot; +}; + +struct GuildManageStatus_Struct{ + uint32 guildid; + uint32 oldrank; + uint32 newrank; + char name[64]; +}; +// Guild invite, remove +struct GuildJoin_Struct{ +/*000*/ uint32 guildid; +/*004*/ uint32 unknown04; +/*008*/ uint32 level; +/*012*/ uint32 class_; +/*016*/ uint32 rank;//0 member, 1 officer, 2 leader +/*020*/ uint32 zoneid; +/*024*/ uint32 unknown24; +/*028*/ char name[64]; +/*092*/ +}; +struct GuildInviteAccept_Struct { + char inviter[64]; + char newmember[64]; + uint32 response; + uint32 guildeqid; +}; +struct GuildManageRemove_Struct { + uint32 guildeqid; + char member[64]; +}; +struct GuildCommand_Struct { + char othername[64]; + char myname[64]; + uint16 guildeqid; + uint8 unknown[2]; // for guildinvite all 0's, for remove 0=0x56, 2=0x02 + uint32 officer; +}; + +// 4244 bytes. Is not really an 'OnLevelMessage', it causes a popup box to display in the client +// Text looks like HTML. +struct OnLevelMessage_Struct { +/*0000*/ char Title[128]; +/*0128*/ char Text[4096]; +/*4224*/ uint32 Buttons; +/*4228*/ uint32 Duration; +/*4232*/ uint32 PopupID; +/*4236*/ uint32 unknown4236; +/*4240*/ uint32 unknown4240; +/*4244*/ +}; + +// Opcode OP_GMZoneRequest +// Size = 88 bytes +struct GMZoneRequest_Struct { +/*0000*/ char charname[64]; +/*0064*/ uint32 zone_id; +/*0068*/ float x; +/*0072*/ float y; +/*0076*/ float z; +/*0080*/ char unknown0080[4]; +/*0084*/ uint32 success; // 0 if command failed, 1 if succeeded? +/*0088*/ +// /*072*/ int8 success; // =0 client->server, =1 server->client, -X=specific error +// /*073*/ uint8 unknown0073[3]; // =0 ok, =ffffff error +}; + +struct GMSummon_Struct { +/* 0*/ char charname[64]; +/* 30*/ char gmname[64]; +/* 60*/ uint32 success; +/* 61*/ uint32 zoneID; +/*92*/ int32 y; +/*96*/ int32 x; +/*100*/ int32 z; +/*104*/ uint32 unknown2; // E0 E0 56 00 +}; + +struct GMGoto_Struct { // x,y is swapped as compared to summon and makes sense as own packet +/* 0*/ char charname[64]; + +/* 64*/ char gmname[64]; +/* 128*/ uint32 success; +/* 132*/ uint32 zoneID; + +/*136*/ int32 y; +/*140*/ int32 x; +/*144*/ int32 z; +/*148*/ uint32 unknown2; // E0 E0 56 00 +}; + +struct GMLastName_Struct { + char name[64]; + char gmname[64]; + char lastname[64]; + uint16 unknown[4]; // 0x00, 0x00 + // 0x01, 0x00 = Update the clients +}; + +//Combat Abilities +struct CombatAbility_Struct { + uint32 m_target; //the ID of the target mob + uint32 m_atk; + 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; + uint8 ia_unknown; + uint8 ib_unknown; + uint8 ic_unknown; + uint8 i_atk; + + uint8 id_unknown; + uint8 ie_unknown; + uint8 if_unknown; + uint8 i_type; + uint8 ig_unknown; + uint8 ih_unknown; + uint8 ii_unknown; +}; + +struct GiveItem_Struct { + uint16 to_entity; + int16 to_equipSlot; + uint16 from_entity; + int16 from_equipSlot; +}; + +struct RandomReq_Struct { + uint32 low; + uint32 high; +}; + +/* solar: 9/23/03 reply to /random command; struct from Zaphod */ +struct RandomReply_Struct { +/* 00 */ uint32 low; +/* 04 */ uint32 high; +/* 08 */ uint32 result; +/* 12 */ char name[64]; +/* 76 */ +}; + +struct LFG_Struct { +/* +Wrong size on OP_LFG. Got: 80, Expected: 68 + 0: 00 00 00 00 01 00 00 00 - 00 00 00 00 64 00 00 00 | ............d... + 16: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 48: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 64: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ +Wrong size on OP_LFG. Got: 80, Expected: 68 + 0: 00 00 00 00 01 00 00 00 - 3F 00 00 00 41 00 00 00 | ........?...A... + 16: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 48: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 64: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ +Wrong size on OP_LFG. Got: 80, Expected: 68 + 0: 00 00 00 00 01 00 00 00 - 3F 00 00 00 41 00 00 00 | ........?...A... + 16: 46 72 75 62 20 66 72 75 - 62 20 66 72 75 62 00 00 | Frub frub frub.. + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 48: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 64: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ +*/ +/*000*/ uint32 unknown000; +/*004*/ uint32 value; // 0x00 = off 0x01 = on +/*008*/ uint32 unknown008; +/*012*/ uint32 unknown012; +/*016*/ char name[64]; +}; + +/* +** LFG_Appearance_Struct +** Packet sent to clients to notify when someone in zone toggles LFG flag +** Size: 8 bytes +** Used in: OP_LFGAppearance +** +*/ +struct LFG_Appearance_Struct +{ +/*0000*/ uint32 spawn_id; // ID of the client +/*0004*/ uint8 lfg; // 1=LFG, 0=Not LFG +/*0005*/ char unknown0005[3]; // +/*0008*/ +}; + + +// EverQuest Time Information: +// 72 minutes per EQ Day +// 3 minutes per EQ Hour +// 6 seconds per EQ Tick (2 minutes EQ Time) +// 3 seconds per EQ Minute + +struct TimeOfDay_Struct { + uint8 hour; + uint8 minute; + uint8 day; + uint8 month; + uint32 year; +}; + +// Darvik: shopkeeper structs +struct Merchant_Click_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; +/*008*/ uint32 command; //1=open, 0=cancel/close +/*012*/ float rate; //cost multiplier, dosent work anymore +}; +/* +Unknowns: +0 is e7 from 01 to // MAYBE SLOT IN PURCHASE +1 is 03 +2 is 00 +3 is 00 +4 is ?? +5 is ?? +6 is 00 from a0 to +7 is 00 from 3f to */ +/* +0 is F6 to 01 +1 is CE CE +4A 4A +00 00 +00 E0 +00 CB +00 90 +00 3F +*/ + + + +struct Merchant_Sell_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; // Player's entity id +/*008*/ uint32 itemslot; + uint32 unknown12; +/*016*/ uint8 quantity; // Already sold +/*017*/ uint8 Unknown016[3]; +/*020*/ uint32 price; +}; +struct Merchant_Purchase_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 itemslot; // Player's entity id +/*008*/ uint32 quantity; +/*012*/ uint32 price; +}; +struct Merchant_DelItem_Struct{ +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; // Player's entity id +/*008*/ uint32 itemslot; +}; +struct Adventure_Purchase_Struct { +/*000*/ uint32 some_flag; //set to 1 generally... +/*000*/ uint32 npcid; +/*004*/ uint32 itemid; +/*008*/ uint32 variable; +}; + +struct AdventurePoints_Update_Struct { +/*000*/ uint32 ldon_available_points; // Total available points +/*004*/ uint8 unkown_apu004[20]; +/*024*/ uint32 ldon_guk_points; // Earned Deepest Guk points +/*028*/ uint32 ldon_mirugal_points; // Earned Mirugal' Mebagerie points +/*032*/ uint32 ldon_mistmoore_points; // Earned Mismoore Catacombs Points +/*036*/ uint32 ldon_rujarkian_points; // Earned Rujarkian Hills points +/*040*/ uint32 ldon_takish_points; // Earned Takish points +/*044*/ uint8 unknown_apu042[216]; +}; + + +struct AdventureFinish_Struct{ + uint32 win_lose;//Cofruben: 00 is a lose,01 is win. + uint32 points; +}; +//OP_AdventureRequest +struct AdventureRequest_Struct{ + uint32 risk;//1 normal,2 hard. + uint32 entity_id; +}; +struct AdventureRequestResponse_Struct{ + uint32 unknown000; + char text[2048]; + uint32 timetoenter; + uint32 timeleft; + uint32 risk; + float x; + float y; + float z; + uint32 showcompass; + uint32 unknown2080; +}; + + + +/*struct Item_Shop_Struct { + uint16 merchantid; + uint8 itemtype; + Item_Struct item; + uint8 iss_unknown001[6]; +};*/ + +/* +** Illusion_Struct +** Changes client visible features +** Size: 168 bytes +** Used In: OP_Illusion, #face, Mob::SendIllusionPacket() +** Fields from the deprecated struct: +** uint8 unknown_26; //Always 26 +** uint8 haircolor; +** uint8 beardcolor; +** uint8 eyecolor1; // the eyecolors always seem to be the same, maybe left and right eye? +** uint8 eyecolor2; +** uint8 hairstyle; +** uint8 aa_title; +** uint8 luclinface; // and beard +** Updated by Father Nitwit for 7-14-04 patch +** +*/ + +struct Illusion_Struct { +/*000*/ uint32 spawnid; +/*004*/ char charname[64]; +/*068*/ uint16 race; +/*070*/ char unknown070[2]; +/*072*/ uint8 gender; +/*073*/ uint8 texture; +/*074*/ uint8 helmtexture; +/*075*/ uint8 unknown075; +/*076*/ uint32 face; +/*080*/ uint8 hairstyle; +/*081*/ uint8 haircolor; +/*082*/ uint8 beard; +/*083*/ uint8 beardcolor; +/*084*/ float size; +/*088*/ char unknown084[80]; +/*168*/ +}; + + +struct ZonePoint_Entry { +/*0000*/ uint32 iterator; +/*0004*/ float y; +/*0008*/ float x; +/*0012*/ float z; +/*0016*/ float heading; +/*0020*/ uint16 zoneid; +/*0022*/ uint16 zoneinstance; // LDoN instance +}; + +struct ZonePoints { +/*0000*/ uint32 count; +/*0004*/ struct ZonePoint_Entry zpe[0]; // Always add one extra to the end after all zonepoints +}; + +struct SkillUpdate_Struct { +/*00*/ uint32 skillId; +/*04*/ uint32 value; +/*08*/ +}; + +struct ZoneUnavail_Struct { + //This actually varies, but... + char zonename[16]; + int16 unknown[4]; +}; + +struct GroupGeneric_Struct { + char name1[64]; + char name2[64]; +}; + +struct GroupCancel_Struct { + char name1[64]; + char name2[64]; + uint8 toggle; +}; + +struct GroupUpdate_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +}; + +struct GroupUpdate2_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +/*0452*/ GroupLeadershipAA_Struct leader_aas; +/*0580*/ uint8 unknown[188]; +/*0768*/ +}; +struct GroupJoin_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[64]; +/*0132*/ uint8 unknown[84]; +}; + +struct FaceChange_Struct { +/*000*/ uint8 haircolor; +/*001*/ uint8 beardcolor; +/*002*/ uint8 eyecolor1; // the eyecolors always seem to be the same, maybe left and right eye? +/*003*/ uint8 eyecolor2; +/*004*/ uint8 hairstyle; +/*005*/ uint8 beard; // vesuvias +/*006*/ uint8 face; +//vesuvias: +//there are only 10 faces for barbs changing woad just +//increase the face value by ten so if there were 8 woad +//designs then there would be 80 barb faces +}; + +/* +** Trade request from one client to another +** Used to initiate a trade +** Size: 8 bytes +** Used in: OP_TradeRequest +*/ +struct TradeRequest_Struct { +/*00*/ uint32 to_mob_id; +/*04*/ uint32 from_mob_id; +/*08*/ +}; + +struct TradeAccept_Struct { +/*00*/ uint32 from_mob_id; +/*04*/ uint32 unknown4; //seems to be garbage +/*08*/ +}; + +/* +** Cancel Trade struct +** Sent when a player cancels a trade +** Size: 8 bytes +** Used In: OP_CancelTrade +** +*/ +struct CancelTrade_Struct { +/*00*/ uint32 fromid; +/*04*/ uint32 action; +/*08*/ +}; + +struct PetitionUpdate_Struct { + uint32 petnumber; // Petition Number + uint32 color; // 0x00 = green, 0x01 = yellow, 0x02 = red + uint32 status; + uint32 senttime; // 4 has to be 0x1F + char accountid[32]; + char gmsenttoo[64]; + int32 quetotal; + char charname[64]; +}; + +struct Petition_Struct { + uint32 petnumber; + uint32 urgency; + char accountid[32]; + char lastgm[32]; + uint32 zone; + //char zone[32]; + char charname[64]; + uint32 charlevel; + uint32 charclass; + uint32 charrace; + uint32 unknown; + //time_t senttime; // Time? + uint32 checkouts; + uint32 unavail; + //uint8 unknown5[4]; + uint32 senttime; + uint32 unknown2; + char petitiontext[1024]; + char gmtext[1024]; +}; + + +struct Who_All_Struct { // 76 length total +/*000*/ char whom[64]; +/*064*/ uint32 wrace; // FF FF = no race + +/*066*/ uint32 wclass; // FF FF = no class +/*068*/ uint32 lvllow; // FF FF = no numbers +/*070*/ uint32 lvlhigh; // FF FF = no numbers +/*072*/ uint32 gmlookup; // FF FF = not doing /who all gm +/*074*/ uint32 unknown074; +/*076*/ uint8 unknown076[64]; +/*140*/ +}; + +struct Stun_Struct { // 4 bytes total + uint32 duration; // Duration of stun +}; + +struct AugmentItem_Struct { +/*00*/ int16 container_slot; +/*02*/ char unknown02[2]; +/*04*/ int32 augment_slot; +/*08*/ +}; + +// OP_Emote +struct Emote_Struct { +/*0000*/ uint32 unknown01; +/*0004*/ char message[1024]; +/*1028*/ +}; + +// Inspect +struct Inspect_Struct { + uint16 TargetID; + uint16 PlayerID; +}; +//OP_InspectAnswer +struct InspectResponse_Struct{//Cofruben:need to send two of this for the inspect response. +/*000*/ uint32 TargetID; +/*004*/ uint32 playerid; +/*008*/ char itemnames[21][64]; +/*1352*/char unknown_zero[64];//fill with zero's. +/*1416*/uint32 itemicons[21]; +/*1500*/uint32 unknown_zero2; +/*1504*/char text[288]; +}; + +//OP_SetDataRate +struct SetDataRate_Struct { + float newdatarate; +}; + +//OP_SetServerFilter +struct SetServerFilter_Struct { + uint32 filters[25]; //see enum eqFilterType +}; + +//Op_SetServerFilterAck +struct SetServerFilterAck_Struct { + uint8 blank[8]; +}; +struct IncreaseStat_Struct{ + /*0000*/ uint8 unknown0; + /*0001*/ uint8 str; + /*0002*/ uint8 sta; + /*0003*/ uint8 agi; + /*0004*/ uint8 dex; + /*0005*/ uint8 int_; + /*0006*/ uint8 wis; + /*0007*/ uint8 cha; + /*0008*/ uint8 fire; + /*0009*/ uint8 cold; + /*0010*/ uint8 magic; + /*0011*/ uint8 poison; + /*0012*/ uint8 disease; + /*0013*/ char unknown13[116]; + /*0129*/ uint8 str2; + /*0130*/ uint8 sta2; + /*0131*/ uint8 agi2; + /*0132*/ uint8 dex2; + /*0133*/ uint8 int_2; + /*0134*/ uint8 wis2; + /*0135*/ uint8 cha2; + /*0136*/ uint8 fire2; + /*0137*/ uint8 cold2; + /*0138*/ uint8 magic2; + /*0139*/ uint8 poison2; + /*0140*/ uint8 disease2; +}; + +struct GMName_Struct { + char oldname[64]; + char gmname[64]; + char newname[64]; + uint8 badname; + uint8 unknown[3]; +}; + +struct GMDelCorpse_Struct { + char corpsename[64]; + char gmname[64]; + uint8 unknown; +}; + +struct GMKick_Struct { + char name[64]; + char gmname[64]; + uint8 unknown; +}; + + +struct GMKill_Struct { + char name[64]; + char gmname[64]; + uint8 unknown; +}; + + +struct GMEmoteZone_Struct { + char text[512]; +}; + +// This is where the Text is sent to the client. +// Use ` as a newline character in the text. +// Variable length. +struct BookText_Struct { + uint8 window; // where to display the text (0xFF means new window) + uint8 type; //type: 0=scroll, 1=book, 2=item info.. prolly others. + char booktext[1]; // Variable Length +}; +// This is the request to read a book. +// This is just a "text file" on the server +// or in our case, the 'name' column in our books table. +struct BookRequest_Struct { + uint8 window; // where to display the text (0xFF means new window) + uint8 type; //type: 0=scroll, 1=book, 2=item info.. prolly others. + char txtfile[1]; // Variable Length +}; + +/* +** Object/Ground Spawn struct +** Used for Forges, Ovens, ground spawns, items dropped to ground, etc +** Size: 92 bytes +** OpCodes: OP_CreateObject +** Last Updated: Oct-17-2003 +** +*/ +struct Object_Struct { +/*00*/ uint32 linked_list_addr[2];// They are, get this, prev and next, ala linked list +/*08*/ uint16 unknown008[2]; // +/*12*/ uint32 drop_id; // Unique object id for zone +/*16*/ uint16 zone_id; // Redudant, but: Zone the object appears in +/*18*/ uint16 zone_instance; // +/*20*/ uint32 unknown020; // +/*24*/ uint32 unknown024; // +/*28*/ float heading; // heading +/*32*/ float z; // z coord +/*36*/ float x; // x coord +/*40*/ float y; // y coord +/*44*/ char object_name[20]; // Name of object, usually something like IT63_ACTORDEF +/*64*/ float unknown064; // seems like coords, not always valid, all 0 on most world objects +/*68*/ float unknown068; // seems like coords, not always valid, all 0 on most world objects +/*72*/ float unknown072; // seems like coords, not always valid, all 0 on most world objects +/*76*/ uint32 unknown076; // +/*80*/ uint32 object_type; // Type of object, not directly translated to OP_OpenObject +/*84*/ uint32 unknown084; //set to 0xFF +/*88*/ uint32 spawn_id; // Spawn Id of client interacting with object +/*92*/ +}; +// 01 = generic drop, 02 = armor, 19 = weapon +//[13:40] and 0xff seems to be indicative of the tradeskill/openable items that end up returning the old style item type in the OP_OpenObject + +/* +** Click Object Struct +** Client clicking on zone object (forge, groundspawn, etc) +** Size: 8 bytes +** Last Updated: Oct-17-2003 +** +*/ +struct ClickObject_Struct { +/*00*/ uint32 drop_id; +/*04*/ uint32 player_id; +/*08*/ +}; + +struct Shielding_Struct { + uint32 target_id; +}; + +/* +** Click Object Acknowledgement Struct +** Response to client clicking on a World Container (ie, forge) +** +*/ +struct ClickObjectAck_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 type; // See object.h, "Object Types" +/*16*/ uint32 unknown16; // +/*20*/ uint32 icon; // Icon to display for tradeskill containers +/*24*/ uint32 unknown24; // +/*28*/ char object_name[64]; // Object name to display +/*92*/ +}; + +/* +** This is different now, mostly unknown +** +*/ +struct CloseContainer_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 unknown12[12]; +}; + +/* +** Generic Door Struct +** Length: 52 Octets +** Used in: +** cDoorSpawnsStruct(f721) +** +*/ +struct Door_Struct +{ +/*0000*/ char name[32]; // Filename of Door // Was 10char long before... added the 6 in the next unknown to it: Daeken M. BlackBlade +/*0032*/ float yPos; // y loc +/*0036*/ float xPos; // x loc +/*0040*/ float zPos; // z loc +/*0044*/ float heading; +/*0048*/ uint32 incline; // rotates the whole door +/*0052*/ uint16 size; // 100 is normal, smaller number = smaller model +/*0054*/ uint8 unknown0038[6]; +/*0060*/ uint8 doorId; // door's id # +/*0061*/ uint8 opentype; +/* + * Open types: + * 66 = PORT1414 (Qeynos) + * 55 = BBBOARD (Qeynos) + * 100 = QEYLAMP (Qeynos) + * 56 = CHEST1 (Qeynos) + * 5 = DOOR1 (Qeynos) + */ +/*0062*/ uint8 state_at_spawn; +/*0063*/ uint8 invert_state; // if this is 1, the door is normally open +/*0064*/ uint32 door_param; //this may index zone_points, representing the destination +/*0068*/ uint8 unknown0052[12]; // mostly 0s, the last 3 bytes are something tho +/*0080*/ +}; + + + +struct DoorSpawns_Struct { + struct Door_Struct doors[0]; +}; + +/* + OP Code: Op_ClickDoor + Size: 16 +*/ +struct ClickDoor_Struct { +/*000*/ uint8 doorid; +/*001*/ uint8 unknown001; // This may be some type of action setting +/*002*/ uint8 unknown002; // This is sometimes set after a lever is closed +/*003*/ uint8 unknown003; // Seen 0 +/*004*/ uint8 picklockskill; +/*005*/ uint8 unknown005[3]; +/*008*/ uint32 item_id; +/*012*/ uint16 player_id; +/*014*/ uint8 unknown014[2]; +/*016*/ +}; + +struct MoveDoor_Struct { + uint8 doorid; + uint8 action; +}; + + +struct BecomeNPC_Struct { + uint32 id; + int32 maxlevel; +}; + +struct Underworld_Struct { + float speed; + float y; + float x; + float z; +}; + +struct Resurrect_Struct { + uint32 unknown00; + uint16 zone_id; + uint16 instance_id; + float y; + float x; + float z; + char your_name[64]; + uint32 unknown88; + char rezzer_name[64]; + uint32 spellid; + char corpse_name[64]; + uint32 action; +/* 228 */ +}; + +struct SetRunMode_Struct { + uint8 mode; + uint8 unknown[3]; +}; + +//EnvDamage is EnvDamage2 without a few bytes at the end. + +struct EnvDamage2_Struct { +/*0000*/ uint32 id; +/*0004*/ uint16 unknown4; +/*0006*/ uint32 damage; +/*0010*/ uint8 unknown10[12]; +/*0022*/ uint8 dmgtype; //FA = Lava; FC = Falling +/*0023*/ uint8 unknown2[4]; +/*0027*/ uint16 constant; //Always FFFF +/*0029*/ uint16 unknown29; +}; + +//Bazaar Stuff =D + +struct BazaarWindowStart_Struct { + uint8 Action; + uint8 Unknown001; + uint16 Unknown002; +}; + + +struct BazaarWelcome_Struct { + BazaarWindowStart_Struct beginning; + uint32 traders; + uint32 items; + uint8 unknown1[8]; +}; + +struct BazaarSearch_Struct { + BazaarWindowStart_Struct beginning; + uint32 traderid; + uint32 class_; + uint32 race; + uint32 stat; + uint32 slot; + uint32 type; + char name[64]; + uint32 minprice; + uint32 maxprice; +}; +struct BazaarInspect_Struct{ + uint32 item_id; + uint32 unknown; + char name[64]; +}; +struct BazaarReturnDone_Struct{ + uint32 type; + uint32 traderid; + uint32 unknown8; + uint32 unknown12; + uint32 unknown16; +}; + +struct BazaarSearchResults_Struct { +/*000*/ BazaarWindowStart_Struct Beginning; +/*004*/ uint32 NumItems; +/*008*/ uint32 SerialNumber; +/*012*/ uint32 SellerID; +/*016*/ uint32 Cost; +/*020*/ uint32 ItemStat; +/*024*/ char ItemName[64]; +/*088*/ +}; + +struct ServerSideFilters_Struct { +uint8 clientattackfilters; // 0) No, 1) All (players) but self, 2) All (players) but group +uint8 npcattackfilters; // 0) No, 1) Ignore NPC misses (all), 2) Ignore NPC Misses + Attacks (all but self), 3) Ignores NPC Misses + Attacks (all but group) +uint8 clientcastfilters; // 0) No, 1) Ignore PC Casts (all), 2) Ignore PC Casts (not directed towards self) +uint8 npccastfilters; // 0) No, 1) Ignore NPC Casts (all), 2) Ignore NPC Casts (not directed towards self) +}; + +/* +** Client requesting item statistics +** Size: 32 bytes +** Used In: OP_ItemLinkClick +** Last Updated: 2/15/2009 +** +*/ +struct ItemViewRequest_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 augments[5]; +/*024*/ uint32 link_hash; +/*028*/ char unknown028[4]; +/*032*/ +}; + + +/* + * Client to server packet + */ +struct PickPocket_Struct { +// Size 18 + uint32 to; + uint32 from; + uint16 myskill; + uint8 type; // -1 you are being picked, 0 failed , 1 = plat, 2 = gold, 3 = silver, 4 = copper, 5 = item + uint8 unknown1; // 0 for response, unknown for input + uint32 coin; + uint8 lastsix[2]; +}; +/* + * Server to client packet + */ + +struct sPickPocket_Struct { + // Size 28 = coin/fail + uint32 to; + uint32 from; + uint32 myskill; + uint32 type; + uint32 coin; + char itemname[64]; +}; + +struct LogServer_Struct { +// Op_Code OP_LOGSERVER +/*000*/ uint32 unknown000; +/*004*/ uint32 unknown004; +/*008*/ uint32 unknown008; +/*012*/ uint32 unknown012; // htonl(1) on live +/*016*/ uint32 unknown016; // htonl(1) on live +/*020*/ uint8 unknown020[12]; +/*032*/ char worldshortname[32]; +/*064*/ uint8 unknown064[32]; +/*096*/ char unknown096[16]; // 'pacman' on live +/*112*/ char unknown112[16]; // '64.37,148,36' on live +/*126*/ uint8 unknown128[48]; +/*176*/ uint32 unknown176; // htonl(0x00002695) +/*180*/ char unknown180[80]; // 'eqdataexceptions@mail.station.sony.com' on live +/*260*/ uint8 unknown260; // 0x01 on live +/*261*/ uint8 unknown261; // 0x01 on live +/*262*/ uint8 unknown262[2]; +/*264*/ +}; + +struct ApproveWorld_Struct { +// Size 544 +// Op_Code OP_ApproveWorld + uint8 unknown544[544]; +}; + +struct ClientError_Struct +{ +/*00001*/ char type; +/*00001*/ char unknown0001[69]; +/*00069*/ char character_name[64]; +/*00134*/ char unknown134[192]; +/*00133*/ char message[31994]; +/*32136*/ +}; + +struct MobHealth +{ + /*0000*/ uint8 hp; //health percent + /*0001*/ uint16 id;//mobs id +}; + +struct Track_Struct { + uint16 entityid; + uint16 padding002; + float distance; +}; + +struct Tracking_Struct { + Track_Struct Entrys[0]; +}; + +/* +** ZoneServerInfo_Struct +** Zone server information +** Size: 130 bytes +** Used In: OP_ZoneServerInfo +** +*/ +struct ZoneServerInfo_Struct +{ +/*0000*/ char ip[128]; +/*0128*/ uint16 port; +}; + +struct WhoAllPlayer{ + uint32 formatstring; + uint32 pidstring; + char* name; + uint32 rankstring; + char* guild; + uint32 unknown80[2]; + uint32 zonestring; + uint32 zone; + uint32 class_; + uint32 level; + uint32 race; + char* account; + uint32 unknown100; +}; + +struct WhoAllReturnStruct { + uint32 id; + uint32 playerineqstring; + char line[27]; + uint8 unknown35; //0A + uint32 unknown36;//0s + uint32 playersinzonestring; + uint32 unknown44[2]; //0s + uint32 unknown52;//1 + uint32 unknown56;//1 + uint32 playercount;//1 + struct WhoAllPlayer player[0]; +}; + +struct Trader_Struct { + uint32 code; + uint32 itemid[160]; + uint32 unknown; + uint32 itemcost[80]; +}; + +struct ClickTrader_Struct { + uint32 code; + uint32 unknown[161];//damn soe this is totally pointless :/ but at least your finally using memset! Good job :) -LE + uint32 itemcost[80]; +}; + +struct GetItems_Struct{ + uint32 items[80]; +}; + +struct BecomeTrader_Struct +{ + uint32 ID; + uint32 Code; +}; + +struct Trader_ShowItems_Struct{ + uint32 code; + uint32 traderid; + uint32 unknown08[3]; +}; + +struct TraderBuy_Struct{ + uint32 unknown0; + uint32 traderid; + uint32 itemid; + uint32 unknown8; + uint32 price; + uint32 quantity; + uint32 slot_num; + char itemname[60]; +}; + +struct TraderItemUpdate_Struct{ + uint32 unknown0; + uint32 traderid; + uint8 fromslot; + uint8 toslot; //7? + uint16 charges; +}; + +struct MoneyUpdate_Struct{ + int32 platinum; + int32 gold; + int32 silver; + int32 copper; +}; + +struct TraderDelItem_Struct{ + uint32 slotid; + uint32 quantity; + uint32 unknown; +}; + +struct TraderClick_Struct{ + uint32 traderid; + uint32 unknown4[2]; + uint32 approval; +}; + +struct FormattedMessage_Struct{ + uint32 unknown0; + uint32 string_id; + uint32 type; + char message[0]; +}; +struct SimpleMessage_Struct{ + uint32 string_id; + uint32 color; + uint32 unknown8; +}; + +struct GuildMemberEntry_Struct { + char name[1]; //variable length + uint32 level; //network byte order + uint32 banker; //1=yes, 0=no, network byte order + uint32 class_; //network byte order + uint32 rank; //network byte order + uint32 time_last_on; //network byte order + uint32 tribute_enable; //network byte order + uint32 total_tribute; //total guild tribute donated, network byte order + uint32 last_tribute; //unix timestamp + char public_note[1]; //variable length. + uint16 zoneinstance; //network byte order + uint16 zone_id; //network byte order +/* 38 + strings */ +}; + +struct GuildMembers_Struct { //just for display purposes, this is not actually used in the message encoding. + char player_name[1]; //variable length. + uint32 count; //network byte order + GuildMemberEntry_Struct member[0]; +}; + +struct GuildMOTD_Struct{ +/*0000*/ uint32 unknown0; +/*0004*/ char name[64]; +/*0068*/ char setby_name[64]; +/*0132*/ uint32 unknown132; +/*0136*/ char motd[512]; +}; +struct GuildUpdate_PublicNote{ + uint32 unknown0; + char name[64]; + char target[64]; + char note[100]; //we are cutting this off at 100, actually around 252 +}; +struct GuildDemoteStruct{ + char name[64]; + char target[64]; +}; +struct GuildRemoveStruct{ + char target[64]; + char name[64]; + uint32 unknown128; + uint32 leaderstatus; //? +}; +struct GuildMakeLeader{ + char name[64]; + char target[64]; +}; +struct BugStruct{ +/*0000*/ char chartype[64]; +/*0064*/ char name[96]; +/*0160*/ char ui[128]; +/*0288*/ float x; +/*0292*/ float y; +/*0296*/ float z; +/*0300*/ float heading; +/*0304*/ uint32 unknown304; +/*0308*/ uint32 type; +/*0312*/ char unknown312[2144]; +/*2456*/ char bug[1024]; +/*3480*/ char placeholder[2]; +/*3482*/ char system_info[4098]; +}; + +struct Ground_Spawn{ + float max_x; + float max_y; + float min_x; + float min_y; + float max_z; + float heading; + char name[16]; + uint32 item; + uint32 max_allowed; + uint32 respawntimer; +}; +struct Ground_Spawns { + struct Ground_Spawn spawn[50]; //Assigned max number to allow +}; +struct PetitionBug_Struct{ + uint32 petition_number; + uint32 unknown4; + char accountname[64]; + uint32 zoneid; + char name[64]; + uint32 level; + uint32 class_; + uint32 race; + uint32 unknown152[3]; + uint32 time; + uint32 unknown168; + char text[1028]; +}; + +struct DyeStruct +{ + union + { + struct + { + struct Color_Struct head; + struct Color_Struct chest; + struct Color_Struct arms; + struct Color_Struct wrists; + struct Color_Struct hands; + struct Color_Struct legs; + struct Color_Struct feet; + struct Color_Struct primary; // you can't actually dye this + struct Color_Struct secondary; // or this + } + dyes; + struct Color_Struct dye[9]; + }; +}; + +struct ApproveZone_Struct { + char name[64]; + uint32 zoneid; + uint32 approve; +}; +struct ZoneInSendName_Struct { + uint32 unknown0; + char name[64]; + char name2[64]; + uint32 unknown132; +}; +struct ZoneInSendName_Struct2 { + uint32 unknown0; + char name[64]; + uint32 unknown68[145]; +}; + +static const uint32 MAX_TRIBUTE_TIERS = 10; + +struct StartTribute_Struct { + uint32 client_id; + uint32 tribute_master_id; + uint32 response; +}; + +struct TributeLevel_Struct { + uint32 level; //backwards byte order! + uint32 tribute_item_id; //backwards byte order! + uint32 cost; //backwards byte order! +}; + +struct TributeAbility_Struct { + uint32 tribute_id; //backwards byte order! + uint32 tier_count; //backwards byte order! + TributeLevel_Struct tiers[MAX_TRIBUTE_TIERS]; + char name[0]; +}; + +struct GuildTributeAbility_Struct { + uint32 guild_id; + TributeAbility_Struct ability; +}; + +struct SelectTributeReq_Struct { + uint32 client_id; //? maybe action ID? + uint32 tribute_id; + uint32 unknown8; //seen E3 00 00 00 +}; + +struct SelectTributeReply_Struct { + uint32 client_id; //echoed from request. + uint32 tribute_id; + char desc[0]; +}; + +struct TributeInfo_Struct { + uint32 active; //0 == inactive, 1 == active + uint32 tributes[MAX_PLAYER_TRIBUTES]; //-1 == NONE + uint32 tiers[MAX_PLAYER_TRIBUTES]; //all 00's + uint32 tribute_master_id; +}; + +struct TributeItem_Struct { + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; +}; + +struct TributePoint_Struct { + int32 tribute_points; + uint32 unknown04; + int32 career_tribute_points; + uint32 unknown12; +}; + +struct TributeMoney_Struct { + uint32 platinum; + uint32 tribute_master_id; + int32 tribute_points; +}; + + +struct Split_Struct +{ + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; +}; + + +/* +** New Combine Struct +** Client requesting to perform a tradeskill combine +** Size: 4 bytes +** Used In: OP_TradeSkillCombine +** Last Updated: Oct-15-2003 +** +*/ +struct NewCombine_Struct { +/*00*/ int16 container_slot; +/*02*/ char unknown02[2]; +/*04*/ +}; + + +//client requesting favorite recipies +struct TradeskillFavorites_Struct { + uint32 object_type; + uint32 some_id; + uint32 favorite_recipes[500]; +}; + +//search request +struct RecipesSearch_Struct { + uint32 object_type; //same as in favorites + uint32 some_id; //same as in favorites + uint32 mintrivial; + uint32 maxtrivial; + char query[56]; + uint32 unknown4; //is set to 00 03 00 00 + uint32 unknown5; //is set to 4C DD 12 00 +/*80*/ +}; + +//one sent for each item, from server in reply to favorites or search +struct RecipeReply_Struct { + uint32 object_type; + uint32 some_id; //same as in favorites + uint32 component_count; + uint32 recipe_id; + uint32 trivial; + char recipe_name[64]; +/*84*/ +}; + +//received and sent back as an ACK with different reply_code +struct RecipeAutoCombine_Struct { + uint32 object_type; + uint32 some_id; + uint32 unknown1; //echoed in reply + uint32 recipe_id; + uint32 reply_code; // 93 64 e1 00 (junk) in request + // 00 00 00 00 in successful reply + // f5 ff ff ff in 'you dont have all the stuff' reply +}; + +struct LevelAppearance_Struct { //Sends a little graphic on level up + uint32 spawn_id; + uint32 parm1; + uint32 value1a; + uint32 value1b; + uint32 parm2; + uint32 value2a; + uint32 value2b; + uint32 parm3; + uint32 value3a; + uint32 value3b; + uint32 parm4; + uint32 value4a; + uint32 value4b; + uint32 parm5; + uint32 value5a; + uint32 value5b; +/*64*/ +}; +struct MerchantList{ + uint32 id; + uint32 slot; + uint32 item; +}; +struct TempMerchantList{ + uint32 npcid; + uint32 slot; + uint32 item; + uint32 charges; //charges/quantity + uint32 origslot; +}; + + +struct FindPerson_Point { + float y; + float x; + float z; +}; + +struct FindPersonRequest_Struct { + uint32 npc_id; + FindPerson_Point client_pos; +}; + +//variable length packet of points +struct FindPersonResult_Struct { + FindPerson_Point dest; + FindPerson_Point path[0]; //last element must be the same as dest +}; + +struct MobRename_Struct { +/*000*/ char old_name[64]; +/*064*/ char old_name_again[64]; //not sure what the difference is +/*128*/ char new_name[64]; +/*192*/ uint32 unknown192; //set to 0 +/*196*/ uint32 unknown196; //set to 1 +/*200*/ +}; + +struct PlayMP3_Struct { + char filename[128]; +}; + +//this is for custom title display in the skill window +struct TitleEntry_Struct { + uint32 skill_id; + uint32 skill_value; + char title[1]; +}; + +struct Titles_Struct { + uint32 title_count; + TitleEntry_Struct titles[0]; +}; + +//this is for title selection by the client +struct TitleListEntry_Struct { + uint32 unknown0; //title ID + char prefix[1]; //variable length, null terminated + char postfix[1]; //variable length, null terminated +}; + +struct TitleList_Struct { + uint32 title_count; + TitleListEntry_Struct titles[0]; //list of title structs + //uint32 unknown_ending; seen 0x7265, 0 +}; + +struct SetTitle_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + uint32 title_id; +}; + +struct SetTitleReply_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + char title[32]; + uint32 entity_id; +}; + +struct TaskDescription_Struct { +/*000*/ uint32 activity_count; //not right. +/*004*/ uint32 taskid; +/*008*/ uint8 unk; +/*009*/ uint32 id3; +/*013*/ uint32 unknown13; +/*017*/ char name[1]; //variable length, 0 terminated +/*018*/ uint32 unknown18; +/*022*/ uint32 unknown22; +/*026*/ char desc[1]; //variable length, 0 terminated +/*027*/ uint32 reward_count; //not sure +/*031*/ uint32 unknown31; +/*035*/ uint32 unknown35; +/*039*/ uint16 unknown39; +/*041*/ char reward_link[1]; //variable length, 0 terminated +/*042*/ uint32 unknown43; //maybe crystal count? +}; + +struct TaskMemberList_Struct { +/*00*/ uint32 gopher_id; +/*04*/ uint32 unknown04; +/*08*/ uint32 member_count; //1 less than the number of members +/*12*/ char list_pointer[0]; +/* list is of the form: + char member_name[1] //null terminated string + uint8 task_leader //boolean flag +*/ +}; + + +struct TaskActivity_Struct { +/*000*/ uint32 activity_count; //not right +/*004*/ uint32 id3; +/*008*/ uint32 taskid; +/*012*/ uint32 activity_id; +/*016*/ uint32 unknown016; +/*020*/ uint32 activity_type; +/*024*/ uint32 unknown024; +/*028*/ uint32 unknown28; +/*032*/ char mob_name[1]; //variable length, 0 terminated +/*033*/ char item_name[1]; //variable length, 0 terminated +/*034*/ uint32 goal_count; +/*038*/ uint32 unknown38; //0xFFFFFFFF +/*042*/ uint32 unknown42; //0xFFFFFFFF +/*046*/ uint32 unknown46; //saw 0x151,0x156 +/*050*/ uint32 unknown50; //saw 0x404,0 +/*054*/ char activity_name[1]; //variable length, 0 terminated... commonly empty +/*055*/ uint32 done_count; +/*059*/ uint32 unknown59; //=1 except on unknown and terminal activities? +/*063*/ +}; + +struct TaskHistoryEntry_Struct { + uint32 task_id; + char name[1]; + uint32 completed_time; +}; +struct TaskHistory_Struct { + uint32 completed_count; + TaskHistoryEntry_Struct entries[0]; +}; + +struct AcceptNewTask_Struct { + uint32 task_id; //set to 0 for 'decline' + uint32 task_master_id; //entity ID +}; + +//was all 0's from client, server replied with same op, all 0's +struct CancelTask_Struct { + uint32 unknown0; + uint32 unknown4; +}; + +struct AvaliableTask_Struct { + uint32 task_index; //no idea, seen 0x1 + uint32 task_master_id; //entity ID + uint32 task_id; + uint32 unknown012; + uint32 activity_count; //not sure, seen 2 + char desc[1]; //variable length, 0 terminated + uint32 reward_platinum;//not sure on these + uint32 reward_gold; + uint32 reward_silver; + uint32 reward_copper; + char some_name[1]; //variable length, 0 terminated + uint8 unknown1; + uint32 unknown2; //0xFFFFFFFF + uint32 unknown3; //0xFFFFFFFF + uint32 unknown4; //seen 0x16 + uint8 unknown5; +}; + + +struct BankerChange_Struct { + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; + uint32 platinum_bank; + uint32 gold_bank; + uint32 silver_bank; + uint32 copper_bank; +}; + +struct LeadershipExpUpdate_Struct { +/*00*/ uint32 group_leadership_exp; +/*04*/ uint32 group_leadership_points; +/*08*/ uint32 raid_leadership_exp; +/*12*/ uint32 raid_leadership_points; +}; + +struct UpdateLeadershipAA_Struct { +/*00*/ uint32 ability_id; +/*04*/ uint32 new_rank; +/*08*/ uint32 pointsleft; +/*12*/ +}; + + +struct RaidGeneral_Struct { +/*00*/ uint32 action; //=10 +/*04*/ char player_name[64]; //should both be the player's name +/*04*/ char leader_name[64]; +/*132*/ uint32 parameter; +}; + +struct RaidAdd_Struct { +/*000*/ uint32 action; //=0 +/*004*/ char player_name[64]; //should both be the player's name +/*068*/ char leader_name[64]; +/*132*/ uint8 _class; +/*133*/ uint8 level; +/*134*/ uint8 has_group; +/*135*/ uint8 unknown135; //seems to be 0x42 or 0 +}; + +struct RaidCreate_Struct { +/*00*/ uint32 action; //=8 +/*04*/ char leader_name[64]; +/*68*/ uint32 leader_id; +}; + +struct RaidMemberInfo_Struct { +/*00*/ uint8 group_number; +/*01*/ char member_name[1]; //dyanmic length, null terminated '\0' +/*00*/ uint8 unknown00; +/*01*/ uint8 _class; +/*02*/ uint8 level; +/*03*/ uint8 is_raid_leader; +/*04*/ uint8 is_group_leader; +/*05*/ uint8 main_tank; //not sure +/*06*/ uint8 unknown06[5]; //prolly more flags +}; + +struct RaidDetails_Struct { +/*000*/ uint32 action; //=6,20 +/*004*/ char leader_name[64]; +/*068*/ uint32 unknown68[4]; +/*084*/ LeadershipAA_Struct abilities; //ranks in backwards byte order +/*128*/ uint8 unknown128[142]; +/*354*/ uint32 leader_id; +}; + +struct RaidMembers_Struct { +/*000*/ RaidDetails_Struct details; +/*358*/ uint32 member_count; //including leader +/*362*/ RaidMemberInfo_Struct members[1]; +/*...*/ RaidMemberInfo_Struct empty; //seem to have an extra member with a 0 length name on the end +}; + +struct DynamicWall_Struct { +/*00*/ char name[32]; +/*32*/ float y; +/*36*/ float x; +/*40*/ float z; +/*44*/ uint32 something; +/*48*/ uint32 unknown48; //0 +/*52*/ uint32 one_hundred; //0x64 +/*56*/ uint32 unknown56; //0 +/*60*/ uint32 something2; +/*64*/ int32 unknown64; //-1 +/*68*/ uint32 unknown68; //0 +/*72*/ uint32 unknown72; //0 +/*76*/ uint32 unknown76; //0x100 +/*80*/ +}; + +enum { //bandolier actions + BandolierCreate = 0, + BandolierRemove = 1, + BandolierSet = 2 +}; + +struct BandolierCreate_Struct { +/*00*/ uint32 action; //0 for create +/*04*/ uint8 number; +/*05*/ char name[32]; +/*37*/ uint16 unknown37; //seen 0x93FD +/*39*/ uint8 unknown39; //0 +}; + +struct BandolierDelete_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ uint8 unknown05[35]; +}; + +struct BandolierSet_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ uint8 unknown05[35]; +}; + +struct Arrow_Struct { +/*000*/ uint32 type; //unsure on name, seems to be 0x1, dosent matter +/*005*/ uint8 unknown004[12]; +/*016*/ float src_y; +/*020*/ float src_x; +/*024*/ float src_z; +/*028*/ uint8 unknown028[12]; +/*040*/ float velocity; //4 is normal, 20 is quite fast +/*044*/ float launch_angle; //0-450ish, not sure the units, 140ish is straight +/*048*/ float tilt; //on the order of 125 +/*052*/ uint8 unknown052[8]; +/*060*/ float arc; +/*064*/ uint8 unknown064[12]; +/*076*/ uint32 source_id; +/*080*/ uint32 target_id; //entity ID +/*084*/ uint32 item_id; //1 to about 150ish +/*088*/ uint32 unknown088; //seen 125, dosent seem to change anything.. +/*092*/ uint32 unknown092; //seen 16, dosent seem to change anything +/*096*/ uint8 unknown096[5]; +/*101*/ char model_name[16]; +/*117*/ uint8 unknown117[19]; +}; + +//made a bunch of trivial structs for stuff for opcode finder to use +struct Consent_Struct { + char name[1]; //always at least a null +}; + +struct AdventureMerchant_Struct { + uint32 unknown_flag; //seems to be 1 + uint32 entity_id; +}; + +struct Save_Struct { + uint8 unknown00[192]; +}; + +struct GMToggle_Struct { + uint8 unknown0[64]; + uint32 toggle; +}; + +struct GroupInvite_Struct { + char invitee_name[64]; + char inviter_name[64]; +// uint8 unknown128[65]; +}; + +struct BuffFadeMsg_Struct { + uint32 color; + char msg[1]; +}; + +struct UseAA_Struct { + uint32 begin; + uint32 ability; + uint32 end; +}; + +struct AA_Ability { +/*00*/ uint32 skill_id; +/*04*/ uint32 base1; +/*08*/ uint32 base2; +/*12*/ uint32 slot; +}; + +struct SendAA_Struct { +/*0000*/ uint32 id; +/*0004*/ uint32 hotkey_sid; +/*0008*/ uint32 hotkey_sid2; +/*0012*/ uint32 title_sid; +/*0016*/ uint32 desc_sid; +/*0020*/ uint32 class_type; +/*0024*/ uint32 cost; +/*0028*/ uint32 seq; +/*0032*/ uint32 current_level; //1s, MQ2 calls this AARankRequired +/*0036*/ uint32 prereq_skill; //is < 0, abs() is category # +/*0040*/ uint32 prereq_minpoints; //min points in the prereq +/*0044*/ uint32 type; +/*0048*/ uint32 spellid; +/*0052*/ uint32 spell_type; +/*0056*/ uint32 spell_refresh; +/*0060*/ uint16 classes; +/*0062*/ uint16 berserker; //seems to be 1 if its a berserker ability +/*0064*/ uint32 max_level; +/*0068*/ uint32 last_id; +/*0072*/ uint32 next_id; +/*0076*/ uint32 cost2; +/*0080*/ uint32 unknown80[2]; //0s +/*0084*/ uint32 total_abilities; +/*0088*/ AA_Ability abilities[0]; +}; + +struct AA_List { + SendAA_Struct* aa[0]; +}; + +struct AA_Action { +/*00*/ uint32 action; +/*04*/ uint32 ability; +/*08*/ uint32 unknown08; +/*12*/ uint32 exp_value; +}; + +struct AA_Skills { //this should be removed and changed to AA_Array +/*00*/ uint32 aa_skill; +/*04*/ uint32 aa_value; +}; + +struct AAExpUpdate_Struct { +/*00*/ uint32 unknown00; //seems to be a value from AA_Action.ability +/*04*/ uint32 aapoints_unspent; +/*08*/ uint8 aaxp_percent; //% of exp that goes to AAs +/*09*/ uint8 unknown09[3]; //live dosent always zero these, so they arnt part of aaxp_percent +}; + + +struct AltAdvStats_Struct { +/*000*/ uint32 experience; +/*004*/ uint16 unspent; +/*006*/ uint16 unknown006; +/*008*/ uint8 percentage; +/*009*/ uint8 unknown009[3]; +}; + +struct PlayerAA_Struct { + AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct AATable_Struct { + AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct Weather_Struct { + uint32 val1; //generall 0x000000FF + uint32 type; //0x31=rain, 0x02=snow(i think), 0 = normal + uint32 mode; +}; + +struct ZoneInUnknown_Struct { + uint32 val1; + uint32 val2; + uint32 val3; +}; + +struct MobHealth_Struct { + uint16 entity_id; + uint8 hp; +}; + +struct AnnoyingZoneUnknown_Struct { + uint32 entity_id; + uint32 value; //always 4 +}; + +struct GuildMemberUpdate_Struct { +/*00*/ uint32 guild_id; //not sure +/*04*/ char member_name[64]; +/*68*/ uint16 zone_id; +/*70*/ uint16 instance_id; +/*72*/ uint32 unknown072; +}; + + + + + + + }; //end namespace structs +}; //end namespace Client62 + +#endif /*CLIENT62_STRUCTS_H_*/ + + + + + + + + + + diff --git a/common/patches/RoF.cpp b/common/patches/RoF.cpp new file mode 100644 index 000000000..abc5be853 --- /dev/null +++ b/common/patches/RoF.cpp @@ -0,0 +1,5314 @@ + +#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 "../Item.h" +#include "RoF_structs.h" +#include "../rulesys.h" + +#include +#include + +namespace RoF { + +static const char *name = "RoF"; +static OpcodeManager *opcodes = NULL; +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 == NULL) { + //TODO: get this file name from the config file + 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; + string pname; + + //register our world signature. + pname = 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 = 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 != NULL) { + //TODO: get this file name from the config file + 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); +} + + + +#include "SSDefine.h" + +// Converts Titanium Slot IDs to RoF Slot IDs for use in Encodes +static inline structs::ItemSlotStruct TitaniumToRoFSlot(uint32 TitaniumSlot) +{ + structs::ItemSlotStruct RoFSlot; + RoFSlot.SlotType = 0xffff; + RoFSlot.Unknown02 = 0; + RoFSlot.MainSlot = 0xffff; + RoFSlot.SubSlot = 0xffff; + RoFSlot.AugSlot = 0xffff; + RoFSlot.Unknown01 = 0; + uint32 TempSlot = 0; + + if (TitaniumSlot < 56 || TitaniumSlot == 9999) // Main Inventory and Cursor + { + RoFSlot.SlotType = 0; + RoFSlot.MainSlot = TitaniumSlot; + if (TitaniumSlot == 9999) + { + RoFSlot.MainSlot = 21; + } + else if (TitaniumSlot >= 30) // Cursor and Extended Corpse Inventory + { + RoFSlot.MainSlot += 3; + } + else if (TitaniumSlot > 20) + { + RoFSlot.MainSlot += 1; + } + + } + /*else if (TitaniumSlot < 51) // Cursor Buffer + { + RoFSlot.SlotType = 5; + RoFSlot.MainSlot = TitaniumSlot - 31; + }*/ + else if (TitaniumSlot > 250 && TitaniumSlot < 341) + { + RoFSlot.SlotType = 0; + TempSlot = TitaniumSlot - 1; + RoFSlot.MainSlot = int(TempSlot / 10) - 2; + RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 2) * 10); + if (RoFSlot.MainSlot > 30) + { + RoFSlot.MainSlot = 33; + } + } + else if (TitaniumSlot > 399 && TitaniumSlot < 405) // Tribute + { + RoFSlot.SlotType = 6; + RoFSlot.MainSlot = TitaniumSlot - 400; + } + else if (TitaniumSlot > 1999 && TitaniumSlot < 2271) + { + RoFSlot.SlotType = 1; + TempSlot = TitaniumSlot - 2000; + RoFSlot.MainSlot = TempSlot; + if (TempSlot > 30) + { + RoFSlot.MainSlot = int(TempSlot / 10) - 3; + RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 3) * 10); + } + } + else if (TitaniumSlot > 2499 && TitaniumSlot < 2551) + { + RoFSlot.SlotType = 2; + TempSlot = TitaniumSlot - 2500; + RoFSlot.MainSlot = TempSlot; + if (TempSlot > 30) + { + RoFSlot.MainSlot = int(TempSlot / 10) - 3; + RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 3) * 10); + } + } + else if (TitaniumSlot > 2999 && TitaniumSlot < 3180) + { + RoFSlot.SlotType = 3; + TempSlot = TitaniumSlot - 3000; + RoFSlot.MainSlot = TempSlot; + if (TempSlot > 99) + { + if (TempSlot > 100) + { + RoFSlot.MainSlot = int((TempSlot - 100) / 10); + } + else + { + RoFSlot.MainSlot = 0; + } + RoFSlot.SubSlot = TempSlot - (100 + RoFSlot.MainSlot); + } + } + else if (TitaniumSlot > 3999 && TitaniumSlot < 4009) + { + RoFSlot.SlotType = 4; + TempSlot = TitaniumSlot - 4000; + RoFSlot.MainSlot = TempSlot; + } + + _log(NET__ERROR, "Convert Titanium Slot %i to RoF Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i", TitaniumSlot, RoFSlot.SlotType, RoFSlot.Unknown02, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01); + + return RoFSlot; +} + +static inline uint32 RoFToTitaniumSlot(structs::ItemSlotStruct RoFSlot) +{ + uint32 TitaniumSlot = 0xffffffff; + uint32 TempSlot = 0; + + if (RoFSlot.SlotType == 0 && RoFSlot.MainSlot < 57) // Worn/Personal Inventory and Cursor (Originally 51) + { + if (RoFSlot.MainSlot == 21) // Power Source + { + TempSlot = 9999; + } + else if (RoFSlot.MainSlot >= 33) // Cursor and Extended Corpse Inventory + { + TempSlot = RoFSlot.MainSlot - 3; + } + /*else if (RoFSlot.MainSlot == 31 || RoFSlot.MainSlot == 32) { // 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 >= 22) // Ammo and Main Inventory + { + TempSlot = RoFSlot.MainSlot - 1; + } + else // Worn Slots + { + TempSlot = RoFSlot.MainSlot; + } + + if (RoFSlot.SubSlot >= 0) // Bag Slots + { + TempSlot = ((TempSlot + 3) * 10) + RoFSlot.SubSlot + 1; + } + + TitaniumSlot = TempSlot; + } + else if (RoFSlot.SlotType == 1) // Bank Slots + { + TempSlot = 2000; + if (RoFSlot.SubSlot >= 0) + { + TempSlot += ((RoFSlot.MainSlot + 3) * 10) + RoFSlot.SubSlot + 1; + } + else + { + TempSlot += RoFSlot.MainSlot; + } + TitaniumSlot = TempSlot; + } + else if (RoFSlot.SlotType == 2) // Shared Bank Slots + { + TempSlot = 2500; + if (RoFSlot.SubSlot >= 0) + { + TempSlot += ((RoFSlot.MainSlot + 3) * 10) + RoFSlot.SubSlot + 1; + } + else + { + TempSlot += RoFSlot.MainSlot; + } + TitaniumSlot = TempSlot; + } + else if (RoFSlot.SlotType == 3) // Trade Slots + { + TempSlot = 3000; + if (RoFSlot.SubSlot >= 0) + { + TempSlot += 100 + (RoFSlot.MainSlot * 10) + RoFSlot.SubSlot; + } + else + { + TempSlot += RoFSlot.MainSlot; + } + TitaniumSlot = TempSlot; + } + else if (RoFSlot.SlotType == 4) // Tradeskill Container Slots + { + TempSlot = 4000; + if (RoFSlot.MainSlot >= 0) + { + TempSlot += RoFSlot.MainSlot; + } + TitaniumSlot = TempSlot; + } + /*else if (RoFSlot.SlotType == 5) // Cursor Buffer + { + TempSlot = 31; + if (RoFSlot.MainSlot >= 0) + { + TempSlot += RoFSlot.MainSlot; + } + TitaniumSlot = TempSlot; + }*/ + _log(NET__ERROR, "Convert RoF Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i to Titanium Slot %i", RoFSlot.SlotType, RoFSlot.Unknown02, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01, TitaniumSlot); + + return TitaniumSlot; +} + +static inline uint32 MainInvRoFToTitaniumSlot(structs::MainInvItemSlotStruct RoFSlot) +{ + uint32 TitaniumSlot = 0xffffffff; + uint32 TempSlot = 0; + + if (RoFSlot.MainSlot < 57) // Worn/Personal Inventory and Cursor (Originally 33) + { + if (RoFSlot.MainSlot == 21) + { + TempSlot = 9999; + } + else if (RoFSlot.MainSlot >= 33) // Cursor and Extended Corpse Inventory + { + TempSlot = RoFSlot.MainSlot - 3; + } + /*else if (RoFSlot.MainSlot == 31 || RoFSlot.MainSlot == 32) { // 9th and 10th RoF inventory slots + // Need to figure out what to do when we get these + + // Same as above + }*/ + else if (RoFSlot.MainSlot >= 22) // Main Inventory and Ammo Slots + { + TempSlot = RoFSlot.MainSlot - 1; + } + else + { + TempSlot = RoFSlot.MainSlot; + } + + if (RoFSlot.SubSlot >= 0) // Bag Slots + { + TempSlot = ((TempSlot + 3) * 10) + RoFSlot.SubSlot + 1; + } + + TitaniumSlot = TempSlot; + } + + _log(NET__ERROR, "Convert RoF Slots: Main %i, Sub %i, Aug %i, Unk1 %i to Titanium Slot %i", RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01, TitaniumSlot); + + return TitaniumSlot; +} + +// Converts Titanium Slot IDs to RoF Slot IDs for use in Encodes +static inline structs::MainInvItemSlotStruct MainInvTitaniumToRoFSlot(uint32 TitaniumSlot) +{ + structs::MainInvItemSlotStruct RoFSlot; + RoFSlot.MainSlot = 0xffff; + RoFSlot.SubSlot = 0xffff; + RoFSlot.AugSlot = 0xffff; + RoFSlot.Unknown01 = 0; + uint32 TempSlot = 0; + + if (TitaniumSlot < 56 || TitaniumSlot == 9999) // (Originally 52) + { + RoFSlot.MainSlot = TitaniumSlot; + if (TitaniumSlot == 9999) + { + RoFSlot.MainSlot = 21; + } + else if (TitaniumSlot > 29) // Cursor and Extended Corpse Inventory + { + RoFSlot.MainSlot += 3; + } + else if(TitaniumSlot > 20) // Ammo and Personl Inventory + { + RoFSlot.MainSlot += 1; + } + /*else if (TitaniumSlot > 29) // Cursor + { + RoFSlot.MainSlot = 33; + if (TitaniumSlot > 30) + { + RoFSlot.SubSlot = (TitaniumSlot + 3) - 33; + } + }*/ + } + else if (TitaniumSlot > 250 && TitaniumSlot < 341) + { + TempSlot = TitaniumSlot - 1; + RoFSlot.MainSlot = int(TempSlot / 10) - 2; + RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 2) * 10); + } + + _log(NET__ERROR, "Convert Titanium Slot %i to RoF Slots: Main %i, Sub %i, Aug %i, Unk1 %i", TitaniumSlot, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01); + + return RoFSlot; +} + +ENCODE(OP_TaskHistoryReply) +{ + EQApplicationPacket *in = *p; + *p = NULL; + + // 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 = NULL; + + 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(NULL) - 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 = NULL; + + 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 < MAX_MATERIALS; 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 = NULL; + + 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 < MAX_PP_SKILL; r++) + { + outapp->WriteUInt32(emu->skills[r]); + } + + // 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 < MAX_PLAYER_BANDOLIER; r++) + { + outapp->WriteString(emu->bandoliers[r].name); + + for(uint32 j = 0; j < MAX_PLAYER_BANDOLIER_ITEMS; ++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 - MAX_PLAYER_BANDOLIER; r++) + { + outapp->WriteString(""); + + for(uint32 j = 0; j < MAX_PLAYER_BANDOLIER_ITEMS; ++j) + { + outapp->WriteString(""); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + } + } + + + outapp->WriteUInt32(structs::MAX_POTIONS_IN_BELT); + + for(uint32 r = 0; r < MAX_POTIONS_IN_BELT; 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 - MAX_POTIONS_IN_BELT; 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(MAX_PLAYER_TRIBUTES); + + for(uint32 r = 0; r < MAX_PLAYER_TRIBUTES; 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 = 16; r < 48; r++) { + eq->unknown521[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 = NULL; + + 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 = NULL; + + 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 = NULL; + + 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 = NULL; + + //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[MATERIAL_PRIMARY]); + 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[MATERIAL_SECONDARY]); + 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 = NULL; + + //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].GradeCountEntry); + } + + 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 = NULL; + + //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 = NULL; + + 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 = NULL; + + 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 = NULL; + + //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 = NULL; + + 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 = NULL; + 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 = MainInvTitaniumToRoFSlot(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 = MainInvTitaniumToRoFSlot(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 = TitaniumToRoFSlot(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 = TitaniumToRoFSlot(emu->from_slot); + eq->to_slot = TitaniumToRoFSlot(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 = TitaniumToRoFSlot(emu->from_slot); + eq->to_slot = TitaniumToRoFSlot(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 = TitaniumToRoFSlot(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 = TitaniumToRoFSlot(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 + memcpy(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); + ss.clear(); + + 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 = MainInvTitaniumToRoFSlot(emu->slot); + OUT(charges); + OUT(sell_price); + + FINISH_ENCODE(); +} + +ENCODE(OP_RaidUpdate) +{ + EQApplicationPacket *inapp = *p; + *p = NULL; + 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)); + + 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[] __emu_buffer; +} + +ENCODE(OP_WhoAllResponse) +{ + EQApplicationPacket *in = *p; + *p = NULL; + + 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 = NULL; + + 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 = NULL; + + 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 = NULL; + + 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 = TitaniumToRoFSlot(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 = TitaniumToRoFSlot(emu->slot_id); + OUT(charges); + OUT(cost); + FINISH_ENCODE(); +} + +ENCODE(OP_AltCurrency) +{ + EQApplicationPacket *in = *p; + *p = NULL; + + 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(); +} + +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 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 = RoFToTitaniumSlot(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 = RoFToTitaniumSlot(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 = MainInvRoFToTitaniumSlot(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 = MainInvRoFToTitaniumSlot(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 = RoFToTitaniumSlot(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 = RoFToTitaniumSlot(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 = RoFToTitaniumSlot(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 = RoFToTitaniumSlot(eq->from_slot); + emu->to_slot = RoFToTitaniumSlot(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 = RoFToTitaniumSlot(eq->from_slot); + emu->to_slot = RoFToTitaniumSlot(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 = MainInvRoFToTitaniumSlot(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 = RoFToTitaniumSlot(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 = RoFToTitaniumSlot(eq->container_slot); + if (slot_id == 4000) { + slot_id = 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 = RoFToTitaniumSlot(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 = RoFToTitaniumSlot(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(); +} + +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); + ss.clear(); + + 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 = TitaniumToRoFSlot(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; + 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.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.augrestrict = item->AugRestrict; + isbs.augdistiller = 0; + + 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]; + } + + // 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 = 0; x < 10; ++x) { + + SubSerializations[x] = NULL; + + const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); + + if(subitem) { + + int SubSlotNumber; + + iqbs.subitem_count++; + + if(slot_id_in >= 22 && slot_id_in < 30) + SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); + else if(slot_id_in >= 2000 && slot_id_in <= 2023) + SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); + else if(slot_id_in >= 2500 && slot_id_in <= 2501) + SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); + else + SubSlotNumber = slot_id_in; // ??????? + + SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); + } + } + + ss.write((const char*)&iqbs, sizeof(RoF::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; +} + +} //end namespace RoF diff --git a/common/patches/RoF.h b/common/patches/RoF.h new file mode 100644 index 000000000..877f61973 --- /dev/null +++ b/common/patches/RoF.h @@ -0,0 +1,36 @@ +#ifndef RoF_H_ +#define RoF_H_ + +#include "../StructStrategy.h" + +class EQStreamIdentifier; + +namespace RoF { + + //these are the only public member of this namespace. + extern void Register(EQStreamIdentifier &into); + extern void Reload(); + + + + //you should not directly access anything below.. + //I just dont feel like making a seperate header for it. + + class Strategy : public StructStrategy { + public: + Strategy(); + + protected: + + virtual std::string Describe() const; + + //magic macro to declare our opcode processors + #include "SSDeclare.h" + #include "RoF_ops.h" + }; + +}; + + + +#endif /*RoF_H_*/ diff --git a/common/patches/RoF_itemfields.h b/common/patches/RoF_itemfields.h new file mode 100644 index 000000000..ccba333b5 --- /dev/null +++ b/common/patches/RoF_itemfields.h @@ -0,0 +1,439 @@ +/* + + +These fields must be in the order of how they are serialized! + + + +*/ +#define NEW_TRY +#ifdef NEW_TRY +//* 000 */ I(ItemClass) // Leave this one off on purpose +/* 001 */ S(Name) +/* 002 */ S(Lore) +//* 003 */ C("")//lorefile - Newly Added - Field is Null +/* 004 */ S(IDFile) +/* 005 */ I(ID) +/* 006 */ I(Weight) +/* 007 */ I(NoRent) +/* 008 */ I(NoDrop) +/* 009 */ I(Size) +/* 010 */ I(Slots) +/* 011 */ I(Price) +/* 012 */ I(Icon) +/* 013 */ C("0")//UNK013 +/* 014 */ C("0")//UNK014 +/* 015 */ I(BenefitFlag) +/* 016 */ I(Tradeskills) +/* 017 */ I(CR) +/* 018 */ I(DR) +/* 019 */ I(PR) +/* 020 */ I(MR) +/* 021 */ I(FR) +/* 022 */ C("0")//svcorruption - Newly Added +/* 023 */ I(AStr) +/* 024 */ I(ASta) +/* 025 */ I(AAgi) +/* 026 */ I(ADex) +/* 027 */ I(ACha) +/* 028 */ I(AInt) +/* 029 */ I(AWis) +/* 030 */ I(HP) +/* 031 */ I(Mana) +/* 032 */ I(Endur) //endur - Relocated +/* 033 */ I(AC) +/* 034 */ I(Classes)//classes - Relocated +/* 035 */ I(Races)//races - Relocated +/* 036 */ I(Deity) +/* 037 */ I(SkillModValue) +/* 038 */ C("0")//UNK038 - Default is 0 +/* 039 */ I(SkillModType) +/* 040 */ I(BaneDmgRace) +/* 041 */ I(BaneDmgBody)//banedmgbody - Relocated +/* 042 */ I(BaneDmgRaceAmt)//banedmgraceamt - Relocated +/* 043 */ I(BaneDmgAmt)//banedmgamt - Relocated +/* 044 */ I(Magic) +/* 045 */ I(CastTime_) +/* 046 */ I(ReqLevel) +/* 047 */ I(RecLevel)//reclevel - Relocated +/* 048 */ I(RecSkill)//recskill - Relocated +/* 049 */ I(BardType) +/* 050 */ I(BardValue) +/* 051 */ I(Light) +/* 052 */ I(Delay) +/* 053 */ I(ElemDmgType) +/* 054 */ I(ElemDmgAmt) +/* 055 */ I(Range) +/* 056 */ I(Damage) +/* 057 */ I(Color) +/* 058 */ I(ItemType) +/* 059 */ I(Material) +/* 060 */ C("0")//UNK060 - Default is 0 +/* 061 */ C("0")//UNK061 - Default is 0 +/* 062 */ F(SellRate) +/* 063 */ I(CombatEffects) +/* 064 */ I(Shielding) +/* 065 */ I(StunResist) +/* 066 */ I(StrikeThrough) +/* 067 */ I(ExtraDmgSkill) +/* 068 */ I(ExtraDmgAmt) +/* 069 */ I(SpellShield) +/* 070 */ I(Avoidance) +/* 071 */ I(Accuracy) +/* 072 */ I(CharmFileID) +/* 073 */ I(FactionMod1)//Swapped these so Faction Amt comes after each Faction Mod +/* 074 */ I(FactionAmt1)//Swapped these so Faction Amt comes after each Faction Mod +/* 075 */ I(FactionMod2)//Swapped these so Faction Amt comes after each Faction Mod +/* 076 */ I(FactionAmt2)//Swapped these so Faction Amt comes after each Faction Mod +/* 077 */ I(FactionMod3)//Swapped these so Faction Amt comes after each Faction Mod +/* 078 */ I(FactionAmt3)//Swapped these so Faction Amt comes after each Faction Mod +/* 079 */ I(FactionMod4)//Swapped these so Faction Amt comes after each Faction Mod +/* 080 */ I(FactionAmt4)//Swapped these so Faction Amt comes after each Faction Mod +/* 081 */ S(CharmFile) +/* 082 */ I(AugType) +/* 083 */ I(AugRestrict)//augrestrict - Relocated +/* 084 */ I(AugDistiller)//augdistiller - Relocated +/* 085 */ I(AugSlotType[0]) +/* 086 */ I(AugSlotVisible[0])//augslot1visible - Default 1 +/* 087 */ C("0")//augslot1unk2 - Newly Added - Default 0 +/* 088 */ I(AugSlotType[1]) +/* 089 */ I(AugSlotVisible[1]) +/* 090 */ C("0")//augslot2unk2 - Newly Added +/* 091 */ I(AugSlotType[2]) +/* 092 */ I(AugSlotVisible[2]) +/* 093 */ C("0")//augslot3unk2 - Newly Added +/* 094 */ I(AugSlotType[3]) +/* 095 */ I(AugSlotVisible[3]) +/* 096 */ C("0")//augslot4unk2 - Newly Added +/* 097 */ I(AugSlotType[4]) +/* 098 */ I(AugSlotVisible[4]) +/* 099 */ C("0")//augslot5unk2 - Newly Added +/* 100 */ I(PointType)//pointtype - Relocated +/* 101 */ I(LDoNTheme) +/* 102 */ I(LDoNPrice) +/* 103 */ C("70")//UNK098 - Newly Added - Default 70, but some are set to 0 +/* 104 */ I(LDoNSold) +/* 105 */ I(BagType) +/* 106 */ I(BagSlots) +/* 107 */ I(BagSize) +/* 108 */ I(BagWR) +/* 109 */ I(Book) +/* 110 */ I(BookType) +/* 111 */ S(Filename) +/* 112 */ I(LoreGroup) +/* 113 */ I(ArtifactFlag) +/* 114 */ C("0")//I(PendingLoreFlag)?//UNK109 - Default 0, but a few are 1 +/* 115 */ I(Favor) +/* 116 */ I(GuildFavor)//guildfavor - Relocated +/* 117 */ I(FVNoDrop) +/* 118 */ I(DotShielding) +/* 119 */ I(Attack) +/* 120 */ I(Regen) +/* 121 */ I(ManaRegen) +/* 122 */ I(EnduranceRegen) +/* 123 */ I(Haste) +/* 124 */ I(DamageShield) +/* 125 */ C("-1") //UNK120 - Default is -1 +/* 126 */ C("0") //UNK121 - Default is 0 +/* 127 */ I(Attuneable) +/* 128 */ I(NoPet) +/* 129 */ C("0") //UNK124 - Default 0, but a few are 1 +/* 130 */ I(PotionBelt) +/* 131 */ C("0") //potionbeltslots - Default 0, but a few are 1 +/* 132 */ I(StackSize) +/* 133 */ I(NoTransfer) +/* 134 */ I(Stackable)//UNK129 - Default is 0, but some are much higher +/* 135 */ I(QuestItemFlag)//questitemflag - Default is 0 (off), flag on = 1 +/* 136 */ C("0")//UNK131 - Default is 0, but there is an item set to 1 +/* 137 */ C("0")//UNK132 - Default is 0? 0000000000000000000? +/* 138 */ I(Click.Effect) +/* 139 */ I(Click.Type) +/* 140 */ I(Click.Level2) +/* 141 */ I(Click.Level) +/* 142 */ I(MaxCharges)//maxcharges - Relocated +/* 143 */ I(CastTime_)//casttime - Relocated - Note Duplicate Entries for CastTime_ and none for CastTime +/* 144 */ I(RecastDelay)//recastdelay - Relocated +/* 145 */ I(RecastType)//recasttype - Relocated +/* 146 */ C("0")//clickunk5 - Newly Added - Default is 0 +/* 147 */ C("")//clickname - Newly Added - Default is Null +/* 148 */ C("-1")//clickunk7 - Newly Added - Default is -1, but some set to 0 and some much higher +/* 149 */ I(Proc.Effect) +/* 150 */ I(Proc.Type) +/* 151 */ I(Proc.Level2) +/* 152 */ I(Proc.Level) +/* 153 */ C("0")//procunk1 - Newly Added - Default is 0, but some set to -1 and 1 +/* 154 */ C("0")//procunk2 - Newly Added - Default is 0 +/* 155 */ C("0")//procunk3 - Newly Added - Default is 0 +/* 156 */ C("0")//procunk4 - Newly Added - Default is 0 +/* 157 */ I(ProcRate)//procrate - Relocated +/* 158 */ C("")//procname - Newly Added - Default is Null +/* 159 */ C("-1")//procunk7 - Newly Added - Default is -1, but some set to 0 +/* 160 */ I(Worn.Effect) +/* 161 */ I(Worn.Type) +/* 162 */ I(Worn.Level2) +/* 163 */ I(Worn.Level) +/* 164 */ C("0")//wornunk1 - Newly Added - Default is 0 +/* 165 */ C("0")//wornunk2 - Newly Added - Default is 0 +/* 166 */ C("0")//wornunk3 - Newly Added - Default is 0 +/* 167 */ C("0")//wornunk4 - Newly Added - Default is 0 +/* 168 */ C("0")//wornunk5 - Newly Added - Default is 0 +/* 169 */ C("")//wornname - Newly Added - Default is Null +/* 170 */ C("-1")//wornunk7 - Newly Added - Default is -1, but some set to 0 +/* 171 */ I(Focus.Effect) +/* 172 */ I(Focus.Type) +/* 173 */ I(Focus.Level2) +/* 174 */ I(Focus.Level) +/* 175 */ C("0")//focusunk1 - Newly Added - Default is 0 +/* 176 */ C("0")//focusunk2 - Newly Added - Default is 0 +/* 177 */ C("0")//focusunk3 - Newly Added - Default is 0 +/* 178 */ C("0")//focusunk4 - Newly Added - Default is 0 +/* 179 */ C("0")//focusunk5 - Newly Added - Default is 0 +/* 180 */ C("")//focusname - Newly Added - Default is Null +/* 181 */ C("-1")//focusunk7 - Newly Added - Default is -1, but some set to 0 +/* 182 */ I(Scroll.Effect) +/* 183 */ I(Scroll.Type) +/* 184 */ I(Scroll.Level2) +/* 185 */ I(Scroll.Level) +/* 186 */ C("0")//scrollunk1 - Renumber this*** +/* 187 */ C("0")//scrollunk2 - Newly Added - Default is 0 +/* 188 */ C("0")//scrollunk3 - Newly Added - Default is 0 +/* 189 */ C("0")//scrollunk4 - Newly Added - Default is 0 +/* 190 */ C("0")//scrollunk5 - Newly Added - Default is 0 +/* 191 */ C("")//scrollname - Newly Added - Default is Null +/* 192 */ C("-1")//scrollunk7 - Newly Added - Default is -1, but some set to 0 +/* 193 */ C("0")//UNK193 - Default is 0 +/* 194 */ C("0")//purity - Newly Added - Default is 0, but some go up to 75 +/* 195 */ C("0")//dsmitigation - Newly Added - Default is 0, but some are up to 2 +/* 196 */ C("0")//heroic_str - Newly Added - Default is 0 +/* 197 */ C("0")//heroic_int - Newly Added - Default is 0 +/* 198 */ C("0")//heroic_wis - Newly Added - Default is 0 +/* 199 */ C("0")//heroic_agi - Newly Added - Default is 0 +/* 200 */ C("0")//heroic_dex - Newly Added - Default is 0 +/* 201 */ C("0")//heroic_sta - Newly Added - Default is 0 +/* 202 */ C("0")//heroic_cha - Newly Added - Default is 0 +/* 203 */ C("0")//HeroicSvPoison - Newly Added - Default is 0 +/* 204 */ C("0")//HeroicSvMagic - Newly Added - Default is 0 +/* 205 */ C("0")//HeroicSvFire - Newly Added - Default is 0 +/* 206 */ C("0")//HeroicSvDisease - Newly Added - Default is 0 +/* 207 */ C("0")//HeroicSvCold - Newly Added - Default is 0 +/* 208 */ C("0")//HeroicSvCorruption - Newly Added - Default is 0 +/* 209 */ C("0")//healamt - Newly Added - Default is 0, but some are up to 9 +/* 210 */ C("0")//spelldmg - Newly Added - Default is 0, but some are up to 9 +/* 211 */ C("0")//clairvoyance - Newly Added - Default is 0, but some are up to 10 +/* 212 */ C("0")//backstabdmg - Newly Added - Default is 0, but some are up to 65 +//* 213 */ C("0")//evolvinglevel - Newly Added - Default is 0, but some are up to 7 +//* 214 */ C("0")//MaxPower - Newly Added +//* 215 */ C("0")//Power - Newly Added + +//This doesn't appear to be used /* 102 */ S(verified)//verified +//This doesn't appear to be used /* 102 */ S(serialized)//created +//Unsure where this goes right now (or if it is even used) /* 108 */ I(SummonedFlag) + +#else +/* 000 */ //I(ItemClass) Leave this one off on purpose +/* 001 */ S(Name) +/* 002 */ S(Lore) +/* 003 */ C("") //LoreFile? +/* 003 */ S(IDFile) +/* 004 */ I(ID) +/* 005 */ I(Weight) +/* 006 */ I(NoRent) +/* 007 */ I(NoDrop) +/* 008 */ I(Size) +/* 009 */ I(Slots) +/* 010 */ I(Price) +/* 011 */ I(Icon) +/* 013 */ C("0") +/* 014 */ C("0") +/* 014 */ I(BenefitFlag) +/* 015 */ I(Tradeskills) +/* 016 */ I(CR) +/* 017 */ I(DR) +/* 018 */ I(PR) +/* 019 */ I(MR) +/* 020 */ I(FR) + C("0") //svcorruption +/* 021 */ I(AStr) +/* 022 */ I(ASta) +/* 023 */ I(AAgi) +/* 024 */ I(ADex) +/* 025 */ I(ACha) +/* 026 */ I(AInt) +/* 027 */ I(AWis) +/* 028 */ I(HP) +/* 029 */ I(Mana) + I(Endur) +/* 030 */ I(AC) +/* 052 */ I(Classes) +/* 053 */ I(Races) +/* 031 */ I(Deity) +/* 032 */ I(SkillModValue) +/* 033 */ C("0") +/* 034 */ I(SkillModType) +/* 035 */ I(BaneDmgRace) +/* 037 */ I(BaneDmgBody) +/* 036 */ I(BaneDmgRaceAmt) +/* 036 */ I(BaneDmgAmt) +/* 038 */ I(Magic) +/* 039 */ I(CastTime_) +/* 040 */ I(ReqLevel) +/* 045 */ I(RecLevel) +/* 046 */ I(RecSkill) +/* 041 */ I(BardType) +/* 042 */ I(BardValue) +/* 043 */ I(Light) +/* 044 */ I(Delay) +/* 047 */ I(ElemDmgType) +/* 048 */ I(ElemDmgAmt) +/* 049 */ I(Range) +/* 050 */ I(Damage) +/* 051 */ I(Color) +/* 056 */ I(ItemType) +/* 057 */ I(Material) +/* 060 */ C("0") +/* 061 */ C("0") +/* 058 */ F(SellRate) +/* 063 */ I(CombatEffects) +/* 064 */ I(Shielding) +/* 065 */ I(StunResist) +/* 059 */ //C("0") +/* 061 */ //C("0") +/* 066 */ I(StrikeThrough) +/* 067 */ I(ExtraDmgSkill) +/* 068 */ I(ExtraDmgAmt) +/* 069 */ I(SpellShield) +/* 070 */ I(Avoidance) +/* 071 */ I(Accuracy) +/* 072 */ I(CharmFileID) +/* 073 */ I(FactionMod1) +/* 077 */ I(FactionAmt1) +/* 074 */ I(FactionMod2) +/* 078 */ I(FactionAmt2) +/* 075 */ I(FactionMod3) +/* 079 */ I(FactionAmt3) +/* 076 */ I(FactionMod4) +/* 080 */ I(FactionAmt4) +/* 081 */ S(CharmFile) +/* 082 */ I(AugType) +/* 082 */ I(AugRestrict) +/* 082 */ I(AugDistiller) +/* 083 */ I(AugSlotType[0]) +/* 084 */ I(AugSlotVisible[0]) +/* 084 */ I(AugSlotUnk2[0]) +/* 085 */ I(AugSlotType[1]) +/* 086 */ I(AugSlotVisible[1]) +/* 086 */ I(AugSlotUnk2[1]) +/* 087 */ I(AugSlotType[2]) +/* 088 */ I(AugSlotVisible[2]) +/* 088 */ I(AugSlotUnk2[2]) +/* 089 */ I(AugSlotType[3]) +/* 090 */ I(AugSlotVisible[3]) +/* 090 */ I(AugSlotUnk2[3]) +/* 091 */ I(AugSlotType[4]) +/* 092 */ I(AugSlotVisible[4]) +/* 092 */ I(AugSlotUnk2[4]) +/* 093 */ I(PointType) +/* 093 */ I(LDoNTheme) +/* 094 */ I(LDoNPrice) +/* 094 */ C("0") +/* 095 */ I(LDoNSold) +/* 096 */ I(BagType) +/* 097 */ I(BagSlots) +/* 098 */ I(BagSize) +/* 099 */ I(BagWR) +/* 100 */ I(Book) +/* 101 */ I(BookType) +/* 102 */ S(Filename) +/* 105 */ I(LoreGroup) +/* 106 */ //I(PendingLoreFlag) +/* 107 */ I(ArtifactFlag) +/* 094 */ C("0") +/* 108 */ //I(SummonedFlag) +/* 109 */ I(Favor) +/* 121 */ I(GuildFavor) +/* 110 */ I(FVNoDrop) +/* 112 */ I(DotShielding) +/* 113 */ I(Attack) +/* 114 */ I(Regen) +/* 115 */ I(ManaRegen) +/* 116 */ I(EnduranceRegen) +/* 117 */ I(Haste) +/* 118 */ I(DamageShield) +/* 120 */ C("0") +/* 121 */ C("0") +/* 125 */ I(Attuneable) +/* 126 */ I(NoPet) +/* 124 */ C("0") +/* 129 */ I(PotionBelt) +/* 130 */ I(PotionBeltSlots) +/* 131 */ I(StackSize) +/* 132 */ I(NoTransfer) +/* 129 */ C("0") +/* 132 */ I(QuestItemFlag) +/* 131 */ C("0") +/* 132 */ C("00000000000000000000000000000000000000") +/* 134 */ I(Click.Effect) +/* 135 */ I(Click.Type) +/* 136 */ I(Click.Level2) +/* 137 */ I(Click.Level) +/* 055 */ I(MaxCharges) +/* 060 */ I(CastTime) +/* 119 */ I(RecastDelay) +/* 120 */ I(RecastType) +/* 138 */ C("0") //clickunk5 (prolly ProcRate) +/* 138 */ C("") //clickunk6 +/* 138 */ C("-1") //clickunk7 +/* 139 */ I(Proc.Effect) +/* 140 */ I(Proc.Type) +/* 141 */ I(Proc.Level2) +/* 142 */ I(Proc.Level) +/* 143 */ C("0") //procunk1 (prolly MaxCharges) +/* 143 */ C("0") //procunk2 (prolly CastTime) +/* 143 */ C("0") //procunk3 (prolly RecastDelay) +/* 143 */ C("0") //procunk4 (prolly RecastType) +/* 062 */ I(ProcRate) +/* 143 */ C("") //procunk6 +/* 143 */ C("-1") //procunk7 +/* 144 */ I(Worn.Effect) +/* 145 */ I(Worn.Type) +/* 146 */ I(Worn.Level2) +/* 147 */ I(Worn.Level) +/* 143 */ C("0") //wornunk1 (prolly MaxCharges) +/* 143 */ C("0") //wornunk2 (prolly CastTime) +/* 143 */ C("0") //wornunk3 (prolly RecastDelay) +/* 143 */ C("0") //wornunk4 (prolly RecastType) +/* 143 */ C("0") //wornunk5 (prolly ProcRate) +/* 143 */ C("") //wornunk6 +/* 143 */ C("-1") //wornunk7 +/* 149 */ I(Focus.Effect) +/* 150 */ I(Focus.Type) +/* 151 */ I(Focus.Level2) +/* 152 */ I(Focus.Level) +/* 143 */ C("0") //focusunk1 (prolly MaxCharges) +/* 143 */ C("0") //focusunk2 (prolly CastTime) +/* 143 */ C("0") //focusunk3 (prolly RecastDelay) +/* 143 */ C("0") //focusunk4 (prolly RecastType) +/* 143 */ C("0") //focusunk5 (prolly ProcRate) +/* 143 */ C("") //focusunk6 +/* 143 */ C("-1") //focusunk7 +/* 154 */ I(Scroll.Effect) +/* 155 */ I(Scroll.Type) +/* 156 */ I(Scroll.Level2) +/* 157 */ I(Scroll.Level) +/* 143 */ C("0") //scrollunk1 (prolly MaxCharges) +/* 143 */ C("0") //scrollunk2 (prolly CastTime) +/* 143 */ C("0") //scrollunk3 (prolly RecastDelay) +/* 143 */ C("0") //scrollunk4 (prolly RecastType) +/* 143 */ C("0") //scrollunk5 (prolly ProcRate) +/* 143 */ C("") //scrollunk6 +/* 143 */ C("-1") //scrollunk7 +/* 193 */ C("0") //Power Source Capacity +/* 194 */ C("0") //purity + +#endif + +#undef I +#undef C +#undef S +#undef F + diff --git a/common/patches/RoF_ops.h b/common/patches/RoF_ops.h new file mode 100644 index 000000000..159607a72 --- /dev/null +++ b/common/patches/RoF_ops.h @@ -0,0 +1,160 @@ + +//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) +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_ApplyPoison) +E(OP_ChannelMessage) +E(OP_GuildsList) +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_GMLastName) +E(OP_BeginCast) +//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) +D(OP_Buff) +D(OP_ShopPlayerSell) +D(OP_Consume) +D(OP_CastSpell) +D(OP_Save) +D(OP_ItemVerifyRequest) +D(OP_GroupInvite) +D(OP_GroupInvite2) +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_GuildDemote) +D(OP_GuildRemove) +D(OP_GuildStatus) +D(OP_Trader) +D(OP_GMLastName) +#undef E +#undef D diff --git a/common/patches/RoF_structs.h b/common/patches/RoF_structs.h new file mode 100644 index 000000000..df428ffdf --- /dev/null +++ b/common/patches/RoF_structs.h @@ -0,0 +1,4795 @@ +#ifndef RoF_STRUCTS_H_ +#define RoF_STRUCTS_H_ + +namespace RoF { + namespace structs { + +/* +** Compiler override to ensure +** byte aligned structures +*/ +#pragma pack(1) + +struct LoginInfo_Struct { +/*000*/ char login_info[64]; +/*064*/ uint8 unknown064[124]; +/*188*/ uint8 zoning; // 01 if zoning, 00 if not +/*189*/ uint8 unknown189[275]; +/*488*/ +}; + +struct EnterWorld_Struct { +/*000*/ char name[64]; +/*064*/ uint32 tutorial; // 01 on "Enter Tutorial", 00 if not +/*068*/ uint32 return_home; // 01 on "Return Home", 00 if not +}; + +//New For SoF +struct WorldObjectsSent_Struct { +}; + +// New for RoF - Size: 12 +struct ItemSlotStruct { +/*000*/ int16 SlotType; // Worn and Normal inventory = 0, Bank = 1, Shared Bank = 2, Delete Item = -1 +/*002*/ int16 Unknown02; +/*004*/ int16 MainSlot; +/*006*/ int16 SubSlot; +/*008*/ int16 AugSlot; // Guessing - Seen 0xffff +/*010*/ int16 Unknown01; // Normally 0 - Seen 13262 when deleting an item, but didn't match item ID +/*012*/ +}; + +// New for RoF - Used for Merchant_Purchase_Struct +// Can't sellfrom other than main inventory so Slot Type is not needed. +struct MainInvItemSlotStruct { +/*000*/ int16 MainSlot; +/*002*/ int16 SubSlot; +/*004*/ int16 AugSlot; +/*006*/ int16 Unknown01; +/*008*/ +}; + +/* Name Approval Struct */ +/* Len: */ +/* Opcode: 0x8B20*/ +struct NameApproval +{ + char name[64]; + uint32 race; + uint32 class_; + uint32 deity; +}; + +/* +** Entity identification struct +** Size: 4 bytes +** OPCodes: OP_DeleteSpawn, OP_Assist +*/ +struct EntityId_Struct +{ +/*00*/ uint32 entity_id; +/*04*/ +}; + +struct Duel_Struct +{ + uint32 duel_initiator; + uint32 duel_target; +}; + +struct DuelResponse_Struct +{ + uint32 target_id; + uint32 entity_id; + uint32 unknown; +}; + +//Adventure stuff,not a net one,just one for our use +static const uint32 ADVENTURE_COLLECT = 0; +static const uint32 ADVENTURE_MASSKILL = 1; +static const uint32 ADVENTURE_NAMED = 2; +static const uint32 ADVENTURE_RESCUE = 3; + +static const uint32 BUFF_COUNT = 42; // was 25 +static const uint32 BLOCKED_BUFF_COUNT = 30; // was 20 + +static const uint32 MAX_PLAYER_TRIBUTES = 5; +static const uint32 MAX_TRIBUTE_TIERS = 10; +static const uint32 TRIBUTE_NONE = 0xFFFFFFFF; + +static const uint32 MAX_PLAYER_BANDOLIER = 20; +static const uint32 MAX_PLAYER_BANDOLIER_ITEMS = 4; + +static const uint32 MAX_POTIONS_IN_BELT = 5; + +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); + +static const uint32 MAX_NUMBER_GUILDS = 1500; + +// Used primarily in the Player Profile: +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 = 100; // was 75 +static const uint32 MAX_PP_AA_ARRAY = 300; +static const uint32 MAX_PP_DISCIPLINES = 200; // was 100 +static const uint32 MAX_GROUP_MEMBERS = 6; +static const uint32 MAX_RECAST_TYPES = 20; + +struct AdventureInfo { + uint32 QuestID; + uint32 NPCID; + bool in_use; + uint32 status; + bool ShowCompass; + uint32 Objetive;// can be item to collect,mobs to kill,boss to kill and someone to rescue. + uint32 ObjetiveValue;// number of items,or number of needed mob kills. + char text[512]; + uint8 type; + uint32 minutes; + uint32 points; + float x; + float y; + uint32 zoneid; + uint32 zonedungeonid; +}; +/////////////////////////////////////////////////////////////////////////////// + + +/* +** Color_Struct +** Size: 4 bytes +** Used for convenience +** Merth: Gave struct a name so gcc 2.96 would compile +** +*/ +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 CharSelectEquip { + //totally guessed; + uint32 equip0; + uint32 equip1; + uint32 equip2; + uint32 itemid; + uint32 equip3; + Color_Struct color; +}; + +struct CharacterSelectEntry_Struct { +/*0000*/ char name[1]; // Name null terminated +/*0000*/ uint8 class_; +/*0000*/ uint32 race; +/*0000*/ uint8 level; +/*0000*/ uint8 class_2; +/*0000*/ uint32 race2; +/*0000*/ uint16 zone; +/*0000*/ uint16 instance; +/*0000*/ uint8 gender; +/*0000*/ uint8 face; +/*0000*/ CharSelectEquip equip[9]; +/*0000*/ uint8 u15; // Seen FF +/*0000*/ uint8 u19; // Seen FF +/*0000*/ uint32 drakkin_tattoo; +/*0000*/ uint32 drakkin_details; +/*0000*/ uint32 deity; +/*0000*/ uint32 primary; +/*0000*/ uint32 secondary; +/*0000*/ uint8 haircolor; +/*0000*/ uint8 beardcolor; +/*0000*/ uint8 eyecolor1; +/*0000*/ uint8 eyecolor2; +/*0000*/ uint8 hairstyle; +/*0000*/ uint8 beard; +/*0000*/ uint8 char_enabled; +/*0000*/ uint8 tutorial; // Seen 1 for new char or 0 for existing +/*0000*/ uint32 drakkin_heritage; +/*0000*/ uint8 unknown1; // Seen 0 +/*0000*/ uint8 gohome; // Seen 0 for new char and 1 for existing +/*0000*/ uint32 LastLogin; +/*0000*/ uint8 unknown2; // Seen 0 +}; + +/* +** Character Selection Struct +** +*/ +struct CharacterSelect_Struct { +/*000*/ uint32 char_count; //number of chars in this packet +/*004*/ CharacterSelectEntry_Struct entries[0]; +}; + +struct Membership_Entry_Struct +{ +/*000*/ uint32 purchase_id; // Seen 1, then increments 90287 to 90300 +/*004*/ uint32 bitwise_entry; // Seen 16 to 65536 - Skips 4096 +/*008*/ +}; + +struct Membership_Setting_Struct +{ +/*000*/ uint32 setting_index; // 0, 1, or 2 +/*004*/ uint32 setting_id; // 0 to 21 +/*008*/ int32 setting_value; // All can be 0, 1, or -1 +/*012*/ +}; + +struct Membership_Details_Struct +{ +/*0000*/ uint32 membership_setting_count; // Seen 66 +/*0016*/ Membership_Setting_Struct settings[66]; +/*0012*/ uint32 race_entry_count; // Seen 15 +/*1044*/ Membership_Entry_Struct membership_races[15]; +/*0012*/ uint32 class_entry_count; // Seen 15 +/*1044*/ Membership_Entry_Struct membership_classes[15]; +/*1044*/ uint32 exit_url_length; // Length of the exit_url string (0 for none) +/*1048*/ //char exit_url[42]; // Upgrade to Silver or Gold Membership URL +/*1048*/ uint32 exit_url_length2; // Length of the exit_url2 string (0 for none) +/*0000*/ //char exit_url2[49]; // Upgrade to Gold Membership URL +}; + +struct Membership_Struct +{ +/*000*/ uint32 membership; // Seen 2 on Gold Account +/*004*/ uint32 races; // Seen ff ff 01 00 +/*008*/ uint32 classes; // Seen ff ff 01 01 +/*012*/ uint32 entrysize; // Seen 22 +/*016*/ int32 entries[22]; // Most -1, 1, and 0 for Gold Status +/*104*/ +}; + + +/* +* Visible equiptment. +* Size: 20 Octets +*/ +struct EquipStruct { +/*00*/ uint32 equip0; +/*04*/ uint32 equip1; +/*08*/ uint32 equip2; +/*12*/ uint32 itemId; +/*16*/ uint32 equip3; // Same as equip0? +/*20*/ +}; + + +/* +** Generic Spawn Struct +** Length: 897 Octets +** Used in: +** spawnZoneStruct +** dbSpawnStruct +** petStruct +** newSpawnStruct +*/ +/* +showeq -> eqemu +sed -e 's/_t//g' -e 's/seto_0xFF/set_to_0xFF/g' +*/ + +struct Spawn_Struct_Bitfields +{ +/*00*/ unsigned gender:2; // Gender (0=male, 1=female, 2=monster) +/*02*/ unsigned ispet:1; // Guessed based on observing live spawns +/*03*/ unsigned afk:1; // 0=no, 1=afk +/*04*/ unsigned anon:2; // 0=normal, 1=anon, 2=roleplay +/*06*/ unsigned gm:1; +/*06*/ unsigned sneak:1; +/*08*/ unsigned lfg:1; +/*09*/ unsigned unknown09:1; +/*10*/ unsigned invis:1; // May have invis & sneak the wrong way around ... not sure how to tell which is which +/*11*/ unsigned invis1:1; // GM Invis? Can only be seen with #gm on - same for the below +/*12*/ unsigned invis2:1; // This one also make the NPC/PC invis +/*13*/ unsigned invis3:1; // This one also make the NPC/PC invis +/*14*/ unsigned invis4:1; // This one also make the NPC/PC invis +/*15*/ unsigned invis6:1; // This one also make the NPC/PC invis +/*16*/ unsigned invis7:1; // This one also make the NPC/PC invis +/*17*/ unsigned invis8:1; // This one also make the NPC/PC invis +/*18*/ unsigned invis9:1; // This one also make the NPC/PC invis +/*19*/ unsigned invis10:1; // This one also make the NPC/PC invis +/*20*/ unsigned invis11:1; // This one also make the NPC/PC invis +/*21*/ unsigned invis12:1; // This one also make the NPC/PC invis +/*22*/ unsigned linkdead:1; // 1 Toggles LD on or off after name. Correct for RoF +/*23*/ unsigned showhelm:1; +/*24*/ unsigned unknown24:1; // Prefixes name with ! +/*25*/ unsigned trader:1; +/*26*/ unsigned unknown26:1; +/*27*/ unsigned targetable:1; +/*28*/ unsigned targetable_with_hotkey:1; +/*29*/ unsigned showname:1; +/*30*/ unsigned unknown30:1; +/*30*/ unsigned untargetable:1; // Untargetable with mouse + /* + // Unknown in RoF + unsigned betabuffed:1; + unsigned buyer:1; + unsigned buyer:1; + */ +}; + +struct Spawn_Struct_Position +{ +/*000*/ signed padding0000:12; + signed y:19; + signed padding0001:1; + +/*004*/ signed deltaX:13; // change in x + signed deltaHeading:10;// change in heading + signed padding0008:9; + +/*008*/ signed deltaY:13; + signed z:19; + +/*012*/ signed x:19; + signed animation:10; // animation + signed padding0016:3; + +/*004*/ signed heading:12; + signed deltaZ:13; // change in z + signed padding0020:7; +}; + +/* +struct Spawn_Struct_Position +{ + signed padding0000:12; // ***Placeholder + signed deltaX:13; // change in x + signed padding0005:7; // ***Placeholder + signed deltaHeading:10;// change in heading + signed deltaY:13; // change in y + signed padding0006:9; // ***Placeholder + signed y:19; // y coord + signed animation:13; // animation + unsigned heading:12; // heading + signed x:19; // x coord + signed padding0014:1; // ***Placeholder + signed z:19; // z coord + signed deltaZ:13; // change in z +}; */ + +struct Spawn_Struct +{ +// Note this struct is not used as such, it is here for reference. As the struct is variable sized, the packets +// are constructed in Live.cpp +// +/*0000*/ char name[1]; //name[64]; +/*0000*/ //uint8 nullterm1; // hack to null terminate name +/*0064*/ uint32 spawnId; +/*0068*/ uint8 level; +/*0069*/ float unknown1; +/*0073*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse + Spawn_Struct_Bitfields Bitfields; +/*0000*/ uint8 otherData; // & 4 - has title, & 8 - has suffix, & 1 - it's a chest or untargetable +/*0000*/ float unknown3; // seen -1 +/*0000*/ float unknown4; +/*0000*/ float size; +/*0000*/ uint8 face; +/*0000*/ float walkspeed; +/*0000*/ float runspeed; +/*0000*/ uint32 race; +/*0000*/ uint8 showname; // for body types - was charProperties +/*0000*/ uint32 bodytype; +/*0000*/ //uint32 bodytype2; // this is only present if charProperties==2 + // are there more than two possible properties? +/*0000*/ uint8 curHp; +/*0000*/ uint8 haircolor; +/*0000*/ uint8 beardcolor; +/*0000*/ uint8 eyecolor1; +/*0000*/ uint8 eyecolor2; +/*0000*/ uint8 hairstyle; +/*0000*/ uint8 beard; +/*0000*/ uint32 drakkin_heritage; +/*0000*/ uint32 drakkin_tattoo; +/*0000*/ uint32 drakkin_details; +/*0000*/ uint8 statue; // was holding +/*0000*/ uint32 deity; +/*0000*/ uint32 guildID; +/*0000*/ uint32 guildrank; // 0=member, 1=officer, 2=leader, -1=not guilded +/*0000*/ uint8 class_; +/*0000*/ uint8 pvp; // 0 = normal name color, 2 = PVP name color +/*0000*/ uint8 StandState; // stand state - 0x64 for normal animation +/*0000*/ uint8 light; +/*0000*/ uint8 flymode; +/*0000*/ uint8 equip_chest2; +/*0000*/ uint8 unknown9; +/*0000*/ uint8 unknown10; +/*0000*/ uint8 helm; +/*0000*/ char lastName[1]; +/*0000*/ //uint8 lastNameNull; //hack! +/*0000*/ uint32 aatitle; // 0=none, 1=general, 2=archtype, 3=class was AARank +/*0000*/ uint8 unknown12; +/*0000*/ uint32 petOwnerId; +/*0000*/ uint8 unknown13; +/*0000*/ uint32 unknown14; // Stance 64 = normal 4 = aggressive 40 = stun/mezzed +/*0000*/ uint32 unknown15; +/*0000*/ uint32 unknown16; +/*0000*/ uint32 unknown17; +/*0000*/ //uint8 unknownRoF3; +/*0000*/ uint32 unknown18; +/*0000*/ uint32 unknown19; + Spawn_Struct_Position Position; +/*0000*/ union + { + struct + { + /*0000*/ Color_Struct color_helmet; // Color of helmet item + /*0000*/ Color_Struct color_chest; // Color of chest item + /*0000*/ Color_Struct color_arms; // Color of arms item + /*0000*/ Color_Struct color_bracers; // Color of bracers item + /*0000*/ Color_Struct color_hands; // Color of hands item + /*0000*/ Color_Struct color_legs; // Color of legs item + /*0000*/ Color_Struct color_feet; // Color of feet item + /*0000*/ Color_Struct color_primary; // Color of primary item + /*0000*/ Color_Struct color_secondary; // Color of secondary item + } equipment_colors; + /*0000*/ Color_Struct colors[9]; // Array elements correspond to struct equipment_colors above + }; + +// skip these bytes if not a valid player race +/*0000*/ union + { + struct + { + /*0000*/ EquipStruct equip_helmet; // Equiptment: Helmet visual + /*0000*/ EquipStruct equip_chest; // Equiptment: Chest visual + /*0000*/ EquipStruct equip_arms; // Equiptment: Arms visual + /*0000*/ EquipStruct equip_bracers; // Equiptment: Wrist visual + /*0000*/ EquipStruct equip_hands; // Equiptment: Hands visual + /*0000*/ EquipStruct equip_legs; // Equiptment: Legs visual + /*0000*/ EquipStruct equip_feet; // Equiptment: Boots visual + /*0000*/ EquipStruct equip_primary; // Equiptment: Main visual + /*0000*/ EquipStruct equip_secondary; // Equiptment: Off visual + } equip; + /*0000*/ EquipStruct equipment[9]; + }; + +/*0000*/ //char title[0]; // only read if(hasTitleOrSuffix & 4) +/*0000*/ //char suffix[0]; // only read if(hasTitleOrSuffix & 8) + char unknown20[8]; + uint8 IsMercenary; // If NPC == 1 and this == 1, then the NPC name is Orange. +/*0000*/ char unknown21[55]; +}; + + +/* +** Generic Spawn Struct +** Fields from old struct not yet found: +** uint8 traptype; // 65 is disarmable trap, 66 and 67 are invis triggers/traps +** uint8 is_pet; // 0=no, 1=yes +** uint8 afk; // 0=no, 1=afk +** uint8 is_npc; // 0=no, 1=yes +** uint8 max_hp; // (name prolly wrong)takes on the value 100 for players, 100 or 110 for NPCs and 120 for PC corpses... +** uint8 guildrank; // 0=normal, 1=officer, 2=leader +** uint8 eyecolor2; //not sure, may be face +** uint8 aaitle; // 0=none, 1=general, 2=archtype, 3=class +*/ + +/* +** New Spawn +** Length: 176 Bytes +** OpCode: 4921 +*/ +struct NewSpawn_Struct +{ + struct Spawn_Struct spawn; // Spawn Information +}; + + +/* +** Client Zone Entry struct +** Length: 68 Octets +** OpCode: ZoneEntryCode (when direction == client) +*/ +struct ClientZoneEntry_Struct { +/*00*/ uint32 unknown00; // ***Placeholder +/*04*/ char char_name[64]; // Player firstname [32] +/*68*/ uint32 unknown68; +/*72*/ uint32 unknown72; +}; + + +/* +** Server Zone Entry Struct +** Length: 452 Bytes +** OPCodes: OP_ServerZoneEntry +** +*/ +struct ServerZoneEntry_Struct //Adjusted from SEQ Everquest.h Struct +{ + struct NewSpawn_Struct player; +}; + + +//New Zone Struct - Size: 948 +struct NewZone_Struct { +/*0000*/ char char_name[64]; // Character Name +/*0064*/ char zone_short_name[32]; // Zone Short Name +/*0096*/ char unknown0096[96]; +/*0192*/ char zone_long_name[278]; // Zone Long Name +/*0470*/ uint8 ztype; // Zone type (usually FF) +/*0471*/ uint8 fog_red[4]; // Zone fog (red) +/*0475*/ uint8 fog_green[4]; // Zone fog (green) +/*0479*/ uint8 fog_blue[4]; // Zone fog (blue) +/*0483*/ uint8 unknown323; +/*0484*/ float fog_minclip[4]; +/*0500*/ float fog_maxclip[4]; +/*0516*/ float gravity; +/*0520*/ uint8 time_type; +/*0521*/ uint8 unknown521[49]; +/*0570*/ uint8 sky; // Sky Type +/*0571*/ uint8 unknown571[13]; // ***Placeholder +/*0584*/ float zone_exp_multiplier; // Experience Multiplier +/*0588*/ float safe_y; // Zone Safe Y +/*0592*/ float safe_x; // Zone Safe X +/*0596*/ float safe_z; // Zone Safe Z +/*0600*/ float min_z; // Guessed - NEW - Seen 0 +/*0604*/ float max_z; // Guessed +/*0608*/ float underworld; // Underworld, min z (Not Sure?) +/*0612*/ float minclip; // Minimum View Distance +/*0616*/ float maxclip; // Maximum View DIstance +/*0620*/ uint8 unknown620[84]; // ***Placeholder +/*0704*/ char zone_short_name2[96]; //zone file name? excludes instance number which can be in previous version. +/*0800*/ int32 unknown800; //seen -1 +/*0804*/ char unknown804[40]; // +/*0844*/ int32 unknown844; //seen 600 +/*0848*/ int32 unknown848; +/*0852*/ uint16 zone_id; +/*0854*/ uint16 zone_instance; +/*0856*/ char unknown856[20]; +/*0876*/ uint32 SuspendBuffs; +/*0880*/ uint32 unknown880; // Seen 50 +/*0884*/ uint32 unknown884; // Seen 10 +/*0888*/ uint8 unknown888; // Seen 1 +/*0889*/ uint8 unknown889; // Seen 0 (POK) or 1 (rujj) +/*0890*/ uint8 unknown890; // Seen 1 +/*0891*/ uint8 unknown891; // Seen 0 +/*0892*/ uint8 unknown892; // Seen 0 +/*0893*/ uint8 unknown893; // Seen 0 - 00 +/*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off +/*0895*/ uint8 unknown895; // Seen 0 - 00 +/*0896*/ uint32 unknown896; // Seen 180 +/*0900*/ uint32 unknown900; // Seen 180 +/*0904*/ uint32 unknown904; // Seen 180 +/*0908*/ uint32 unknown908; // Seen 2 +/*0912*/ uint32 unknown912; // Seen 2 +/*0916*/ float FogDensity; // Most zones have this set to 0.33 Blightfire had 0.16 +/*0920*/ uint32 unknown920; // Seen 0 +/*0924*/ uint32 unknown924; // Seen 0 +/*0928*/ uint32 unknown928; // Seen 0 +/*0932*/ int32 unknown932; // Seen -1 +/*0936*/ int32 unknown936; // Seen -1 +/*0940*/ uint32 unknown940; // Seen 0 +/*0944*/ float unknown944; // Seen 1.0 +/*0948*/ +}; + + +/* +** Memorize Spell Struct +** Length: 16 Bytes +** +*/ +struct MemorizeSpell_Struct { +uint32 slot; // Spot in the spell book/memorized slot +uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) +uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming +uint32 unknown12; +}; + +/* +** Make Charmed Pet +** Length: 12 Bytes +** +*/ +struct Charm_Struct { +/*00*/ uint32 owner_id; +/*04*/ uint32 pet_id; +/*08*/ uint32 command; // 1: make pet, 0: release pet +/*12*/ +}; + +struct InterruptCast_Struct +{ + uint32 spawnid; + uint32 messageid; + //char message[0]; +}; + +struct DeleteSpell_Struct +{ +/*000*/int16 spell_slot; +/*002*/uint8 unknowndss002[2]; +/*004*/uint8 success; +/*005*/uint8 unknowndss006[3]; +/*008*/ +}; + +struct ManaChange_Struct +{ + uint32 new_mana; // New Mana AMount + uint32 stamina; + uint32 spell_id; + uint32 unknown12; + uint32 unknown16; +}; + +struct SwapSpell_Struct +{ + uint32 from_slot; + uint32 to_slot; +}; + +struct BeginCast_Struct +{ +/*000*/ uint32 spell_id; +/*004*/ uint16 caster_id; +/*006*/ uint32 cast_time; // in miliseconds +/*010*/ +}; + +struct CastSpell_Struct +{ +/*00*/ uint32 slot; +/*04*/ uint32 spell_id; +/*08*/ ItemSlotStruct inventoryslot; // slot for clicky item, Seen unknown of 131 = normal cast +/*20*/ uint32 target_id; +/*24*/ uint32 cs_unknown[5]; +/*44*/ +}; + +/* +** SpawnAppearance_Struct +** Changes client appearance for all other clients in zone +** Size: 8 bytes +** Used in: OP_SpawnAppearance +** +*/ +struct SpawnAppearance_Struct +{ +/*0000*/ uint16 spawn_id; // ID of the spawn +/*0002*/ uint16 type; // Values associated with the type +/*0004*/ uint32 parameter; // Type of data sent +/*0008*/ +}; + +struct SpellBuff_Struct +{ +/*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*/ +}; + +struct SpellBuff_Struct_Old +{ +/*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*/ float unknown004; // Seen 1 for no buff +/*008*/ uint32 spellid; +/*012*/ uint32 duration; +/*016*/ uint32 unknown016; +/*020*/ uint32 player_id; // 'global' ID of the caster, for wearoff messages +/*024*/ uint32 counters; +/*028*/ uint8 unknown0028[60]; +/*088*/ +}; + +// Not functional yet, but this is what the packet looks like on Live +struct SpellBuffFade_Struct_Live { +/*000*/ uint32 entityid; // Player id who cast the buff +/*004*/ uint8 unknown004; +/*005*/ uint8 level; +/*006*/ uint8 effect; +/*007*/ uint8 unknown007; +/*008*/ float unknown008; +/*012*/ uint32 spellid; +/*016*/ uint32 duration; +/*020*/ uint32 playerId; // Global player ID? +/*024*/ uint8 unknown0028[68]; +/*092*/ uint32 slotid; +/*096*/ uint32 bufffade; +/*100*/ +}; + +struct SpellBuffFade_Struct { +/*000*/ uint32 entityid; +/*004*/ uint8 slot; +/*005*/ uint8 level; +/*006*/ uint8 effect; +/*007*/ uint8 unknown7; +/*008*/ uint32 spellid; +/*012*/ uint32 duration; +/*016*/ uint32 unknown016; +/*020*/ uint32 unknown020; // Global player ID? +/*024*/ uint32 playerId; // Player id who cast the buff +/*028*/ uint32 slotid; +/*032*/ uint32 bufffade; +/*036*/ +}; + +struct BuffRemoveRequest_Struct +{ +/*00*/ uint32 SlotID; +/*04*/ uint32 EntityID; +/*08*/ +}; + +struct GMTrainee_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ uint32 skills[73]; + /*300*/ uint8 unknown300[148]; + /*448*/ +}; + +struct GMTrainEnd_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ +}; + +struct GMSkillChange_Struct { +/*000*/ uint16 npcid; +/*002*/ uint8 unknown1[2]; // something like PC_ID, but not really. stays the same thru the session though +/*002*/ uint16 skillbank; // 0 if normal skills, 1 if languages +/*002*/ uint8 unknown2[2]; +/*008*/ uint16 skill_id; +/*010*/ uint8 unknown3[2]; +/*012*/ +}; + +struct GMTrainSkillConfirm_Struct { // SoF+ only +/*000*/ uint32 SkillID; +/*004*/ uint32 Cost; +/*008*/ uint8 NewSkill; // Set to 1 for 'You have learned the basics' message. +/*009*/ char TrainerName[64]; +/*073*/ uint8 Unknown073[3]; +/*076*/ +}; + +struct ConsentResponse_Struct { + char grantname[64]; + char ownername[64]; + uint8 permission; + char zonename[64]; +}; + +/* +** Name Generator Struct +** Length: 72 bytes +** OpCode: 0x0290 +*/ +struct NameGeneration_Struct +{ +/*0000*/ uint32 race; +/*0004*/ uint32 gender; +/*0008*/ char name[64]; +/*0072*/ +}; + +/* +** Character Creation struct +** Length: 96 Bytes +*/ +struct CharCreate_Struct +{ +/*0000*/ uint32 gender; +/*0004*/ uint32 race; +/*0008*/ uint32 class_; +/*0012*/ uint32 deity; +/*0016*/ uint32 start_zone; +/*0020*/ uint32 haircolor; +/*0024*/ uint32 beard; +/*0028*/ uint32 beardcolor; +/*0032*/ uint32 hairstyle; +/*0036*/ uint32 face; +/*0040*/ uint32 eyecolor1; +/*0044*/ uint32 eyecolor2; +/*0048*/ uint32 drakkin_heritage; +/*0052*/ uint32 drakkin_tattoo; +/*0056*/ uint32 drakkin_details; +/*0060*/ uint32 STR; +/*0064*/ uint32 STA; +/*0068*/ uint32 AGI; +/*0073*/ uint32 DEX; +/*0076*/ uint32 WIS; +/*0080*/ uint32 INT; +/*0084*/ uint32 CHA; +/*0088*/ uint32 tutorial; +/*0092*/ uint32 unknown0092; +/*0096*/ +}; + +/* +** Character Creation struct +** Length: 0 Bytes +** OpCode: 0x +*/ +struct CharCreate_Struct_Temp //Size is now 0 +{ +}; + +/* + *Used in PlayerProfile + */ +struct AA_Array +{ + uint32 AA; + uint32 value; + uint32 unknown08; // Looks like AA_Array is now 12 bytes in Live +}; + +struct Disciplines_Struct { + uint32 values[MAX_PP_DISCIPLINES]; +}; + + + +struct Tribute_Struct { + uint32 tribute; + uint32 tier; +}; + +struct BandolierItem_Struct { + char item_name[1]; // Variable Length + uint32 item_id; + uint32 icon; +}; + +//len = 72 +struct BandolierItem_Struct_Old { + uint32 item_id; + uint32 icon; + char item_name[64]; +}; + +//len = 320 +enum { //bandolier item positions + bandolierMainHand = 0, + bandolierOffHand, + bandolierRange, + bandolierAmmo +}; +struct Bandolier_Struct { + char name[1]; // Variable Length + BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; +}; + +struct Bandolier_Struct_Old { + char name[32]; + BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; +}; + +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]; +}; +struct RaidLeadershipAA_Struct { + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; +}; + + /** +* A bind point. +* Size: 20 Octets +*/ +struct BindStruct { + /*000*/ uint32 zoneId; + /*004*/ float x; + /*008*/ float y; + /*012*/ float z; + /*016*/ float heading; + /*020*/ +}; + + +// Player Profile - Variable Length as of Oct 12 2012 patch +struct PlayerProfile_Struct +{ +/*00000*/ uint32 checksum; // +/*00004*/ uint32 checksum_size; // Value = ( Packet Size - 9 ) +/*00008*/ uint8 checksum_byte; // +/*00009*/ uint8 unknown_rof1[3]; // +/*00012*/ uint32 unknown_rof2; // +/*00016*/ uint8 gender; // Player Gender - 0 Male, 1 Female +/*00017*/ uint32 race; // Player race +/*00021*/ uint8 class_; // Player class +/*00022*/ uint8 level; // Level of player +/*00023*/ uint8 level1; // Level of player (again?) +/*00024*/ uint32 bind_count; // Seen 5 +/*00028*/ BindStruct binds[5]; // Bind points (primary is first) 5 fields = 100 bytes +/*00128*/ uint32 deity; // deity +/*00132*/ uint32 unknown_rof3; // Maybe Drakkin Heritage? +/*00136*/ uint32 unknown4_count; // Seen 10 +/*00140*/ uint32 unknown_rof4[10]; // Seen all 0s +/*00180*/ uint32 equip_count; // Seen 22 +union +{ + struct + { + /*00184*/ EquipStruct equip_helmet; // Equiptment: Helmet visual + /*00204*/ EquipStruct equip_chest; // Equiptment: Chest visual + /*00224*/ EquipStruct equip_arms; // Equiptment: Arms visual + /*00244*/ EquipStruct equip_bracers; // Equiptment: Wrist visual + /*00264*/ EquipStruct equip_hands; // Equiptment: Hands visual + /*00284*/ EquipStruct equip_legs; // Equiptment: Legs visual + /*00304*/ EquipStruct equip_feet; // Equiptment: Boots visual + /*00324*/ EquipStruct equip_primary; // Equiptment: Main visual + /*00344*/ EquipStruct equip_secondary; // Equiptment: Off visual + // Below slots are just guesses, but all 0s anyway... + /*00364*/ EquipStruct equip_charm; // Equiptment: Non-visual + /*00384*/ EquipStruct equip_ear1; // Equiptment: Non-visual + /*00404*/ EquipStruct equip_ear2; // Equiptment: Non-visual + /*00424*/ EquipStruct equip_face; // Equiptment: Non-visual + /*00444*/ EquipStruct equip_neck; // Equiptment: Non-visual + /*00464*/ EquipStruct equip_shoulder; // Equiptment: Non-visual + /*00484*/ EquipStruct equip_bracer2; // Equiptment: Non-visual + /*00504*/ EquipStruct equip_range; // Equiptment: Non-visual + /*00524*/ EquipStruct equip_ring1; // Equiptment: Non-visual + /*00544*/ EquipStruct equip_ring2; // Equiptment: Non-visual + /*00564*/ EquipStruct equip_waist; // Equiptment: Non-visual + /*00584*/ EquipStruct equip_powersource; // Equiptment: Non-visual + /*00604*/ EquipStruct equip_ammo; // Equiptment: Non-visual + } equip; + /*00184*/ EquipStruct equipment[22]; +}; +/*00624*/ uint32 equip2_count; // Seen 9 +/*00628*/ EquipStruct equipment2[9]; // Appears to be Visible slots, but all 0s +/*00808*/ uint32 tint_count; // Seen 9 +/*00812*/ Color_Struct item_tint[9]; // RR GG BB 00 +/*00848*/ uint32 tint_count2; // Seen 9 +/*00852*/ Color_Struct item_tint2[9]; // RR GG BB 00 +/*00888*/ uint8 haircolor; // Player hair color +/*00889*/ uint8 beardcolor; // Player beard color +/*00890*/ uint32 unknown_rof5; // +/*00894*/ uint8 eyecolor1; // Player left eye color +/*00895*/ uint8 eyecolor2; // Player right eye color +/*00896*/ uint8 hairstyle; // Player hair style +/*00897*/ uint8 beard; // Player beard type +/*00898*/ uint8 face; // Player face +/*00899*/ uint32 drakkin_heritage; // +/*00903*/ uint32 drakkin_tattoo; // +/*00907*/ uint32 drakkin_details; // +/*00911*/ uint8 unknown_rof6; // +/*00912*/ int8 unknown_rof7; // seen -1 +/*00913*/ uint8 unknown_rof8; // +/*00914*/ uint8 unknown_rof9; // +/*00915*/ uint8 unknown_rof10; // Seen 1 or 0 (on a female?) +/*00916*/ float height; // Seen 7.0 (barb), 5.0 (woodelf), 5.5 (halfelf) +/*00920*/ float unknown_rof11; // Seen 3.0 +/*00924*/ float unknown_rof12; // Seen 2.5 +/*00928*/ float unknown_rof13; // Seen 5.5 +/*00932*/ uint32 primary; // Seen 10528 +/*00936*/ uint32 secondary; // Seen 10006 +/*00940*/ uint32 points; // Unspent Practice points - RELOCATED??? +/*00944*/ uint32 mana; // Current mana +/*00948*/ uint32 cur_hp; // Current HP without +HP equipment +/*00952*/ uint32 STR; // Strength - 6e 00 00 00 - 110 +/*00956*/ uint32 STA; // Stamina - 73 00 00 00 - 115 +/*00960*/ uint32 CHA; // Charisma - 37 00 00 00 - 55 +/*00964*/ uint32 DEX; // Dexterity - 50 00 00 00 - 80 +/*00968*/ uint32 INT; // Intelligence - 3c 00 00 00 - 60 +/*00972*/ uint32 AGI; // Agility - 5f 00 00 00 - 95 +/*00976*/ uint32 WIS; // Wisdom - 46 00 00 00 - 70 +/*00980*/ uint32 unknown_rof14[7]; // Probably base resists? +/*01008*/ uint32 aa_count; // Seen 300 +/*01012*/ AA_Array aa_array[MAX_PP_AA_ARRAY]; // [300] 3600 bytes - AAs 12 bytes each +/*04612*/ uint32 skill_count; // Seen 100 +/*04616*/ uint32 skills[MAX_PP_SKILL]; // [100] 400 bytes - List of skills +/*05016*/ uint32 unknown15_count; // Seen 25 +/*05020*/ uint32 unknown_rof15[25]; // Most are 255 or 0 +/*05120*/ uint32 discipline_count; // Seen 200 +/*05124*/ Disciplines_Struct disciplines; // [200] 800 bytes Known disciplines +/*05924*/ uint32 timestamp_count; // Seen 20 +/*05928*/ uint32 timestamps[20]; // Unknown Unix Timestamps - maybe recast related? +/*06008*/ uint32 recast_count; // Seen 20 +/*06012*/ uint32 recastTimers[MAX_RECAST_TYPES];// [20] 80 bytes - Timers (UNIX Time of last use) +/*06092*/ uint32 timestamp2_count; // Seen 100 +/*06096*/ uint32 timestamps2[100]; // Unknown Unix Timestamps - maybe Skill related? +/*06496*/ uint32 spell_book_count; // Seen 720 +/*06500*/ uint32 spell_book[MAX_PP_SPELLBOOK]; // List of the Spells in spellbook 720 = 90 pages [2880 bytes] +/*09380*/ uint32 mem_spell_count; // Seen 16 +/*09384*/ int32 mem_spells[MAX_PP_MEMSPELL]; // [16] List of spells memorized - First 12 are set or -1 and last 4 are 0s +/*09448*/ uint32 unknown16_count; // Seen 13 +/*09452*/ uint32 unknown_rof16[13]; // Possibly spell or buff related +/*09504*/ uint8 unknown_rof17; // Seen 0 or 8 +/*09505*/ uint32 buff_count; // Seen 42 +/*09509*/ SpellBuff_Struct buffs[BUFF_COUNT]; // [42] 3360 bytes - Buffs currently on the player (42 Max) - (Each Size 80) +/*12869*/ uint32 platinum; // Platinum Pieces on player +/*12873*/ uint32 gold; // Gold Pieces on player +/*12877*/ uint32 silver; // Silver Pieces on player +/*12881*/ uint32 copper; // Copper Pieces on player +/*12885*/ uint32 platinum_cursor; // Platinum Pieces on cursor +/*12889*/ uint32 gold_cursor; // Gold Pieces on cursor +/*12893*/ uint32 silver_cursor; // Silver Pieces on cursor +/*12897*/ uint32 copper_cursor; // Copper Pieces on cursor +/*12901*/ uint32 intoxication; // Alcohol level (in ticks till sober?) - Position Guessed +/*12905*/ uint32 toxicity; // Potion Toxicity (15=too toxic, each potion adds 3) - Position Guessed +/*12909*/ uint32 unknown_rof19; // +/*12913*/ uint32 thirst_level; // Drink (ticks till next drink) - Position Guessed +/*12917*/ uint32 hunger_level; // Food (ticks till next eat) - Position Guessed +/*12921*/ uint32 aapoints_spent; // Number of spent AA points +/*12925*/ uint32 aapoint_count; // Seen 5 - Unsure what this is exactly +/*12929*/ uint32 aapoints_assigned; // Number of Assigned AA points - Seen 206 (total of the 4 fields below) +/*12933*/ uint32 aa_spent_general; // Seen 63 +/*12937*/ uint32 aa_spent_archetype; // Seen 40 +/*12941*/ uint32 aa_spent_class; // Seen 103 +/*12945*/ uint32 aa_spent_special; // Seen 0 +/*12949*/ uint32 aapoints; // Unspent AA points - Seen 1 +/*12953*/ uint16 unknown_rof20; // +/*12955*/ uint32 bandolier_count; // Seen 20 +/*12959*/ Bandolier_Struct bandoliers[MAX_PLAYER_BANDOLIER]; // [20] 740 bytes (Variable Name Sizes) - bandolier contents +/*13699*/ uint32 potionbelt_count; // Seen 5 +/*13703*/ PotionBelt_Struct potionbelt; // [5] 45 bytes potion belt - (Variable Name Sizes) +/*13748*/ int32 unknown_rof21; // Seen -1 +/*13752*/ int32 hp_total; // Guessed - Seen 20 on level 1 new mage +/*13756*/ int32 endurance_total; // Guessed - Seen 20 on level 1 new mage +/*13760*/ int32 mana_total; // Guessed - Only seen on casters so far - Matches above field if caster +/*13764*/ uint32 unknown_rof22[12]; // Same for all seen PPs - 48 bytes: +/* +19 00 00 00 19 00 00 00 19 00 00 00 0f 00 00 00 +0f 00 00 00 0f 00 00 00 0f 00 00 00 1f 85 eb 3e +33 33 33 3f 08 00 00 00 02 00 00 00 01 00 00 00 +*/ +/*13812*/ uint32 unknown_rof23; // Seen 5, 19, and 20 in examples +/*13816*/ uint32 unknown_rof24[4]; // +/*13832*/ uint32 unknown_rof25[2]; // Seen random numbers from 0 to 2165037 +/*13840*/ uint32 unknown_rof26; // Seen 106 +//END SUB-STRUCT used for shrouding. +/*13844*/ uint32 name_str_len; // Seen 64 +/*13848*/ char name[64]; // Name of player - 19960 for Live 1180 difference +/*13912*/ uint32 last_name_str_len; // Seen 32 +/*13916*/ char last_name[32]; // Last name of player +/*13948*/ uint32 birthday; // character birthday +/*13952*/ uint32 account_startdate; // Date the Account was started +/*13956*/ uint32 lastlogin; // character last save time +/*13960*/ uint32 timePlayedMin; // time character played +/*13964*/ uint32 timeentitledonaccount; +/*13968*/ uint32 expansions; // Bitmask for expansions ff 7f 00 00 - SoD +/*13972*/ uint32 language_count; // Seen 32 +/*13976*/ uint8 languages[MAX_PP_LANGUAGE]; // [32] 32 bytes - List of languages +/*14008*/ uint16 zone_id; // see zones.h +/*14010*/ uint16 zoneInstance; // Instance id +/*14012*/ float y; // Players y position (NOT positive about this switch) +/*14016*/ float x; // Players x position +/*14020*/ float z; // Players z position +/*14024*/ float heading; // Players heading +/*14028*/ uint32 air_remaining; // Air supply (seconds) +/*14032*/ int32 unknown_rof28; // Seen -1 +/*14036*/ uint8 unknown_rof29[10]; // +/*14046*/ uint32 unknown_rof30; // Random large number or 0 +/*14050*/ uint32 unknown_rof31; // +/*14054*/ uint32 unknown32_count; // Seen 5 +/*14058*/ uint8 unknown_rof32[29]; // +/*14087*/ uint32 unknown33_count; // Seen 32 for melee/hybrid and 21 for druid, 34 for mage +/*14091*/ uint32 unknown_rof33[64]; // Struct contains 2 ints, so double 32 count (Variable Sized) +// Position Varies after this point - Really starts varying at Bandolier names, but if no names set it starts here +/*00000*/ int32 unknown_rof34; // Seen -1 +/*00000*/ int32 unknown_rof35; // Seen -1 +/*00000*/ uint8 unknown_rof36[18]; // +/*00000*/ uint32 unknown37_count; // Seen 5 +/*00000*/ int32 unknown_rof37[10]; // Alternates -1 and 0 - Struct contains 2 ints +/*00000*/ uint32 unknown38_count; // Seen 10 +/*00000*/ int32 unknown_rof38[20]; // Alternates -1 and 0 - Struct contains 2 ints +/*00000*/ uint8 unknown_rof39[137]; // +/*00000*/ float unknown_rof40; // Seen 1.0 +/*00000*/ uint32 unknown_rof41[9]; // +/*00000*/ uint32 unknown_rof42; // Seen 0 or 1 +/*00000*/ uint32 unknown_string1_count; // Seen 64 +/*00000*/ char unknown_string1[64]; // +/*00000*/ uint8 unknown_rof43[30]; // +/*00000*/ uint32 unknown_string2_count; // Seen 64 +/*00000*/ char unknown_string2[64]; // +/*00000*/ uint32 unknown_string3_count; // Seen 64 +/*00000*/ char unknown_string3[64]; // +/*00000*/ uint32 unknown_rof44; // Seen 0 or 50 +/*00000*/ uint8 unknown_rof45[663]; // +/*00000*/ uint32 char_id; // Guessed based on char creation date and values +/*00000*/ uint8 unknown_rof46; // Seen 0 or 1 +/*00000*/ uint32 unknown_rof47; // Seen 6 +/*00000*/ uint32 unknown_rof48[13]; // Seen all 0s or some mix of ints and float? +/*00000*/ uint32 unknown_rof49; // Seen 64 +/*00000*/ uint8 unknown_rof50[256]; // Seen mostly 0s, but one example had a 2 in the middle +/*00000*/ uint32 unknown_rof51; // Seen 100 or 0 +/*00000*/ uint8 unknown_rof52[82]; // +/*00000*/ uint32 unknown_rof53; // Seen 50 + +uint8 unknown_rof54[1325]; // Unknown Section + +// Bottom of Struct: +/*00000*/ uint8 groupAutoconsent; // 0=off, 1=on +/*00000*/ uint8 raidAutoconsent; // 0=off, 1=on +/*00000*/ uint8 guildAutoconsent; // 0=off, 1=on +/*00000*/ uint8 unknown_rof55; // Seen 1 +/*00000*/ uint32 level3; // SoF looks at the level here to determine how many leadership AA you can bank. +/*00000*/ uint32 showhelm; // 0=no, 1=yes +/*00000*/ uint32 RestTimer; +/*00000*/ uint8 unknown_rof56; +/*00000*/ uint32 unknown_rof57; // Seen 49 +/*00000*/ uint8 unknown_rof58[1045]; // Seen all 0s + +/* +///////////////////// - Haven't identified the below fields in the PP yet +uint8 pvp; // 1=pvp, 0=not pvp +uint8 anon; // 2=roleplay, 1=anon, 0=not anon +uint8 gm; // 0=no, 1=yes (guessing!) +uint32 guild_id; // guildid +uint8 guildrank; // 0=member, 1=officer, 2=guildleader -1=no guild +uint32 guildbanker; +uint32 available_slots; +uint32 endurance; // Current endurance +uint32 spellSlotRefresh[MAX_PP_MEMSPELL]; // Refresh time (millis) - 4 bytes Each * 16 = 64 bytes +uint32 abilitySlotRefresh; +/////////////////////// + +uint32 platinum_bank; // Platinum Pieces in Bank +uint32 gold_bank; // Gold Pieces in Bank +uint32 silver_bank; // Silver Pieces in Bank +uint32 copper_bank; // Copper Pieces in Bank +uint32 platinum_shared; // Shared platinum pieces + +uint32 autosplit; // 0 = off, 1 = on + +char groupMembers[MAX_GROUP_MEMBERS][64];// 384 all the members in group, including self +char groupLeader[64]; // Leader of the group ? +uint32 entityid; + +uint32 leadAAActive; // 0 = leader AA off, 1 = leader AA on +int32 ldon_points_guk; // Earned GUK points +int32 ldon_points_mir; // Earned MIR points +int32 ldon_points_mmc; // Earned MMC points +int32 ldon_points_ruj; // Earned RUJ points +int32 ldon_points_tak; // Earned TAK points +int32 ldon_points_available;// Available LDON points +float tribute_time_remaining;// Time remaining on tribute (millisecs) +uint32 career_tribute_points;// Total favor points for this char +uint32 tribute_points; // Current tribute points +uint32 tribute_active; // 0 = off, 1=on +Tribute_Struct tributes[MAX_PLAYER_TRIBUTES]; // [40] Current tribute loadout +double group_leadership_exp; // Current group lead exp points +double raid_leadership_exp; // Current raid lead AA exp points +uint32 group_leadership_points; // Unspent group lead AA points +uint32 raid_leadership_points; // Unspent raid lead AA points +LeadershipAA_Struct leader_abilities; // [128]Leader AA ranks 19332 + +uint32 PVPKills; +uint32 PVPDeaths; +uint32 PVPCurrentPoints; +uint32 PVPCareerPoints; +uint32 PVPBestKillStreak; +uint32 PVPWorstDeathStreak; +uint32 PVPCurrentKillStreak; +PVPStatsEntry_Struct PVPLastKill; // size 88 +PVPStatsEntry_Struct PVPLastDeath; // size 88 +uint32 PVPNumberOfKillsInLast24Hours; +PVPStatsEntry_Struct PVPRecentKills[50]; // size 4400 - 88 each +uint32 expAA; // Exp earned in current AA point +uint32 currentRadCrystals; // Current count of radiant crystals +uint32 careerRadCrystals; // Total count of radiant crystals ever +uint32 currentEbonCrystals; // Current count of ebon crystals +uint32 careerEbonCrystals; // Total count of ebon crystals ever +*/ + +}; + +/* +** Client Target Struct +** Length: 2 Bytes +** OpCode: 6221 +*/ +struct ClientTarget_Struct { +/*000*/ uint32 new_target; // Target ID +}; + +/* +** Target Rejection Struct +** Length: 12 Bytes +** OpCode: OP_TargetReject +*/ +struct TargetReject_Struct { +/*00*/ uint8 unknown00[12]; +}; + +struct PetCommand_Struct { +/*00*/ uint32 command; +/*04*/ uint32 unknown04; +/*08*/ uint32 unknown08; +}; + +/* +** Delete Spawn +** Length: 5 Bytes +** OpCode: OP_DeleteSpawn +*/ +struct DeleteSpawn_Struct +{ +/*00*/ uint32 spawn_id; // Spawn ID to delete +/*04*/ uint8 unknown04; // Seen 1 +/*05*/ +}; + +/* +** Channel Message received or sent +** Length: 144 Bytes + Variable Length + 1 +** OpCode: OP_ChannelMessage +** +*/ +struct ChannelMessage_Struct +{ +/*000*/ char targetname[64]; // Tell recipient +/*064*/ char sender[64]; // The senders name (len might be wrong) +/*128*/ uint32 language; // Language +/*132*/ uint32 chan_num; // Channel +/*136*/ uint32 cm_unknown4[2]; // ***Placeholder +/*144*/ uint32 skill_in_language; // The players skill in this language? might be wrong +/*148*/ char message[0]; // Variable length message +}; + +/* +** Special Message +** Length: 4 Bytes + Variable Text Length + 1 +** OpCode: OP_SpecialMesg +** +*/ +/* + Theres something wrong with this... example live packet: +Server->Client: [ Opcode: OP_SpecialMesg (0x0fab) Size: 244 ] + 0: 01 02 00 0A 00 00 00 09 - 05 00 00 42 61 72 73 74 | ...........Barst + 16: 72 65 20 53 6F 6E 67 77 - 65 61 76 65 72 00 7C F9 | re Songweaver.|. + 32: FF FF 84 FF FF FF 03 00 - 00 00 47 72 65 65 74 69 | ..........Greeti + +*/ +struct SpecialMesg_Struct +{ +/*00*/ char header[3]; // 04 04 00 <-- for #emote style msg +/*03*/ uint32 msg_type; // Color of text (see MT_*** below) +/*07*/ uint32 target_spawn_id; // Who is it being said to? +/*11*/ char sayer[1]; // Who is the source of the info - Was 1 +/*12*/ uint8 unknown12[12]; +/*24*/ char message[128]; // What is being said? - was 128 +}; + +/* +** When somebody changes what they're wearing +** or give a pet a weapon (model changes) +** Length: 19 Bytes +*/ +struct WearChange_Struct{ +/*000*/ uint16 spawn_id; +/*002*/ uint32 material; +/*006*/ uint32 unknown06; +/*010*/ uint32 elite_material; // 1 for Drakkin Elite Material +/*014*/ uint32 hero_forge_model; // New to VoA +/*018*/ uint32 unknown18; // New to RoF +/*022*/ Color_Struct color; +/*026*/ uint8 wear_slot_id; +/*027*/ +}; + +/* +** Type: Bind Wound Structure +** Length: 8 Bytes +*/ +//Fixed for 7-14-04 patch +struct BindWound_Struct +{ +/*000*/ uint16 to; // TargetID +/*002*/ uint16 unknown2; // ***Placeholder +/*004*/ uint16 type; +/*006*/ uint16 unknown6; +}; + + +/* +** Type: Zone Change Request (before hand) +** Length: 88 bytes +** OpCode: a320 +*/ + +struct ZoneChange_Struct { +/*000*/ char char_name[64]; // Character Name +/*064*/ uint16 zoneID; +/*066*/ uint16 instanceID; +/*068*/ uint32 Unknown068; +/*072*/ uint32 Unknown072; +/*076*/ float y; +/*080*/ float x; +/*084*/ float z; +/*088*/ uint32 zone_reason; //0x0A == death, I think +/*092*/ int32 success; // =0 client->server, =1 server->client, -X=specific error +/*096*/ uint32 Unknown096; // Not sure the extra 4 bytes goes here or earlier in the struct. +/*100*/ +}; + +struct RequestClientZoneChange_Struct { +/*000*/ uint16 zone_id; +/*002*/ uint16 instance_id; +/*004*/ uint32 unknown004; +/*008*/ float y; +/*012*/ float x; +/*016*/ float z; +/*020*/ float heading; +/*024*/ uint32 type; //unknown... values +/*032*/ uint8 unknown032[144]; +/*172*/ uint32 unknown172; +/*176*/ +}; + +struct Animation_Struct { +/*00*/ uint16 spawnid; +/*02*/ uint8 value; +/*03*/ uint8 action; +/*04*/ +}; + +// solar: this is what causes the caster to animate and the target to +// get the particle effects around them when a spell is cast +// also causes a buff icon +struct Action_Struct +{ +/*00*/ uint16 target; // id of target +/*02*/ uint16 source; // id of caster +/*04*/ uint16 level; // level of caster - Seen 0 +/*06*/ uint32 unknown06; +/*10*/ float instrument_mod; +/*14*/ uint32 bard_focus_id; // seen 0 +/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again +/*22*/ uint32 unknown22; +/*26*/ uint8 type; +/*27*/ uint32 damage; +/*31*/ uint16 unknown31; +/*33*/ uint32 spell; // spell id being cast +/*37*/ uint8 level2; // level of caster again? Or maybe the castee +/*38*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? +/*39*/ +}; + +// Starting with 2/21/2006, OP_Actions seem to come in pairs, duplicating +// themselves, with the second one with slightly more information. Maybe this +// has to do with buff blocking?? +struct ActionAlt_Struct +{ +/*00*/ uint16 target; // id of target +/*02*/ uint16 source; // id of caster +/*04*/ uint16 level; // level of caster - Seen 0 +/*06*/ uint32 unknown06; +/*10*/ float instrument_mod; +/*14*/ uint32 bard_focus_id; // seen 0 +/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again +/*22*/ uint32 unknown22; +/*26*/ uint8 type; +/*27*/ uint32 damage; +/*31*/ uint16 unknown31; +/*33*/ uint32 spell; // spell id being cast +/*37*/ uint8 level2; // level of caster again? Or maybe the castee +/*38*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? +/*39*/ uint32 unknown39; // New field to Underfoot - Seen 14 +/*43*/ uint8 unknown43; // New field to Underfoot - Seen 0 +/*44*/ uint8 unknown44; // New field to Underfoot - Seen 17 +/*45*/ uint8 unknown45; // New field to Underfoot - Seen 0 +/*46*/ int32 unknown46; // New field to Underfoot - Seen -1 +/*50*/ uint32 unknown50; // New field to Underfoot - Seen 0 +/*54*/ uint16 unknown54; // New field to Underfoot - Seen 0 +/*56*/ +}; + +// solar: this is what prints the You have been struck. and the regular +// melee messages like You try to pierce, etc. It's basically the melee +// and spell damage message +struct CombatDamage_Struct +{ +/* 00 */ uint16 target; +/* 02 */ uint16 source; +/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells +/* 05 */ uint32 spellid; +/* 09 */ int32 damage; +/* 13 */ float unknown11; // cd cc cc 3d +/* 17 */ float sequence; // see above notes in Action_Struct +/* 21 */ uint8 unknown19[9]; // was [9] +/* 30 */ +}; + + +/* +** Consider Struct +** Length: 20 Bytes +*/ +struct Consider_Struct{ +/*000*/ uint32 playerid; // PlayerID +/*004*/ uint32 targetid; // TargetID +/*008*/ uint32 faction; // Faction +/*012*/ uint32 level; // Level +/*016*/ uint8 pvpcon; // Pvp con flag 0/1 +/*017*/ uint8 unknown017[3]; // +/*020*/ +}; + +/* +** Spawn Death Blow +** Length: 32 Bytes +** OpCode: 0114 +*/ +struct Death_Struct +{ +/*000*/ uint32 spawn_id; +/*004*/ uint32 killer_id; +/*008*/ uint32 corpseid; // was corpseid +/*012*/ uint32 attack_skill; // was type +/*016*/ uint32 spell_id; +/*020*/ uint32 bindzoneid; //bindzoneid? +/*024*/ uint32 damage; +/*028*/ uint32 unknown028; +}; + +struct BecomeCorpse_Struct { + uint32 spawn_id; + float y; + float x; + float z; +}; + +struct ZonePlayerToBind_Struct { +/*000*/ uint16 bind_zone_id; +/*002*/ uint16 bind_instance_id; +/*004*/ float x; +/*008*/ float y; +/*012*/ float z; +/*016*/ float heading; +/*020*/ char zone_name[1]; // Or "Bind Location" +/*000*/ uint8 unknown021; // Seen 1 - Maybe 0 would be to force a rezone and 1 is just respawn +/*000*/ uint32 unknown022; // Seen 32 or 59 +/*000*/ uint32 unknown023; // Seen 0 +/*000*/ uint32 unknown024; // Seen 21 or 43 +}; + +struct ZonePlayerToBindHeader_Struct +{ + /*000*/ uint16 bind_zone_id; + /*002*/ uint16 bind_instance_id; + /*004*/ float x; + /*008*/ float y; + /*012*/ float z; + /*016*/ float heading; + /*020*/ char zone_name[1]; // Or "Bind Location" +}; + +struct ZonePlayerToBindFooter_Struct +{ + /*000*/ uint8 unknown021; // Seen 1 - Maybe 0 would be to force a rezone and 1 is just respawn + /*000*/ uint32 unknown022; // Seen 32 or 59 + /*000*/ uint32 unknown023; // Seen 0 + /*000*/ uint32 unknown024; // Seen 21 or 43 +}; + +typedef struct { +/*000*/ uint32 bind_number; // Number of this bind in the iteration +/*004*/ uint32 bind_zone_id; // ID of the zone for this bind point or resurect point +/*008*/ float x; // X loc for this bind point +/*012*/ float y; // Y loc for this bind point +/*016*/ float z; // Z loc for this bind point +/*020*/ float heading; // Heading for this bind point +/*024*/ char bind_zone_name[1]; // Or "Bind Location" or "Resurrect" +/*000*/ uint8 validity; // 0 = valid choice, 1 = not a valid choice at this time (resurrection) +} RespawnOptions_Struct; + +struct RespawnWindow_Struct { +/*000*/ uint32 unknown000; // Seen 0 +/*004*/ uint32 time_remaining; // Total time before respawn in milliseconds +/*008*/ uint32 unknown008; // Seen 0 +/*012*/ uint32 total_binds; // Total Bind Point Options? - Seen 2 +/*016*/ RespawnOptions_Struct bind_points; +// First bind point is "Bind Location" and the last one is "Ressurect" +}; + + +/* +** Spawn position update - Size: 22 +** Struct sent from server->client to update position of +** another spawn's position update in zone (whether NPC or PC) +** +*/ +struct PlayerPositionUpdateServer_Struct +{ +/*0000*/ uint16 spawn_id; +/*0002*/ uint16 spawnId2; +/*0004*/ signed padding0004:12; + signed y_pos:19; // y coord + unsigned padding:1; +/*0008*/ signed delta_x:13; // change in x + signed delta_heading:10;// change in heading + signed padding0008:9; +/*0012*/ signed delta_y:13; // change in y + signed z_pos:19; // z coord +/*0016*/ signed x_pos:19; // x coord + signed animation:10; // animation + signed padding0016:3; +/*0020*/ unsigned heading:12; // heading + signed delta_z:13; // change in z + signed padding0020:7; +/*0024*/ +}; + + +/* +** Player position update - Size: 40 +** Struct sent from client->server to update +** player position on server +** +*/ +struct PlayerPositionUpdateClient_Struct +{ +/*0000*/ uint16 sequence; // increments one each packet - Verified +/*0002*/ uint16 spawn_id; // Player's spawn id +/*0004*/ uint8 unknown0004[6]; // ***Placeholder +/*0010*/ float delta_y; // Change in y +/*0014*/ float x_pos; // x coord (2nd loc value) +/*0018*/ float y_pos; // y coord (1st loc value) +/*0022*/ signed delta_heading:10; // change in heading + unsigned animation:10; // ***Placeholder + unsigned padding0024:12; // animation +/*0026*/ float delta_z; // Change in z +/*0030*/ float delta_x; // Change in x +/*0034*/ float z_pos; // z coord (3rd loc value) +/*0038*/ unsigned heading:12; // Directional heading + unsigned padding0040:10; // ***Placeholder + unsigned padding0041:10; // ***Placeholder +/*0042*/ +}; + +/* +** Spawn HP Update +** Length: 10 Bytes +** OpCode: OP_HPUpdate +*/ +struct SpawnHPUpdate_Struct +{ +/*00*/ int16 spawn_id; +/*02*/ uint32 cur_hp; +/*06*/ int32 max_hp; +/*10*/ +}; + +/* +** SendExpZonein +** Length: 152 Bytes +** OpCode: OP_SendExpZonein +*/ +struct SendExpZonein_Struct +{ +/*0000*/ uint16 spawn_id; // ID of the spawn +/*0002*/ uint16 type; // Values associated with the type +/*0004*/ uint32 parameter; // Type of data sent +/*0008*/ uint32 exp; // Current experience ratio from 0 to 330 +/*0012*/ uint32 expAA; +/*0016*/ uint8 unknown0016[4]; +/*0020*/ char name[64]; +/*0084*/ char last_name[64]; +/*00148*/ uint32 unknown132; +/*00152*/ +}; + +/* +** SendExpZonein +** Length: 0 Bytes +** OpCode: OP_SendExpZonein +*/ +//struct SendExpZonein_Struct {}; + +struct SpawnHPUpdate_Struct2 +{ +/*01*/ int16 spawn_id; +/*00*/ uint8 hp; +}; +/* +** Stamina +** Length: 8 Bytes +** OpCode: 5721 +*/ +struct Stamina_Struct { +/*00*/ uint32 food; // (low more hungry 127-0) +/*02*/ uint32 water; // (low more thirsty 127-0) +}; + +/* +** Level Update +** Length: 12 Bytes +*/ +struct LevelUpdate_Struct +{ +/*00*/ uint32 level; // New level +/*04*/ uint32 level_old; // Old level +/*08*/ uint32 exp; // Current Experience +}; + +/* +** Experience Update +** Length: 14 Bytes +** OpCode: 9921 +*/ +struct ExpUpdate_Struct +{ +/*0000*/ uint32 exp; // Current experience ratio from 0 to 330 +/*0004*/ uint32 aaxp; // @BP ?? +}; + +/* +** Item Packet Struct - Works on a variety of opcodes +** Packet Types: See ItemPacketType enum +** +*/ +enum ItemPacketType +{ + ItemPacketViewLink = 0x00, + ItemPacketTradeView = 0x65, + ItemPacketLoot = 0x66, + ItemPacketTrade = 0x67, + ItemPacketCharInventory = 0x69, + ItemPacketSummonItem = 0x6A, + ItemPacketTributeItem = 0x6C, + ItemPacketMerchant = 0x64, + ItemPacketWorldContainer = 0x6B +}; +struct ItemPacket_Struct +{ +/*00*/ ItemPacketType PacketType; +/*04*/ char SerializedItem[1]; //was 1 +/*xx*/ +}; + +struct BulkItemPacket_Struct +{ +/*00*/ char SerializedItem[0]; +/*xx*/ +}; + +struct Consume_Struct +{ +/*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 +/*024*/ +}; + +struct ItemNamePacket_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 unkown004; +/*008*/ char name[64]; +/*072*/ +}; + +// Length: 16 +struct ItemProperties_Struct_Old { + +/*000*/ uint8 unknown01[2]; +/*002*/ uint8 charges; +/*003*/ uint8 unknown02[13]; +/*016*/ +}; + +// Length: 8 +struct ItemProperties_Struct { + +/*000*/ uint8 unknown01[4]; +/*004*/ uint8 charges; +/*005*/ uint8 unknown02[3]; +/*008*/ +}; + +struct DeleteItem_Struct { +/*0000*/ ItemSlotStruct from_slot; +/*0004*/ ItemSlotStruct to_slot; +/*0008*/ uint32 number_in_stack; +/*0012*/ +}; + +struct MoveItem_Struct { +/*0000*/ ItemSlotStruct from_slot; +/*0004*/ ItemSlotStruct to_slot; +/*0008*/ uint32 number_in_stack; +/*0012*/ +}; + +// +// from_slot/to_slot +// -1 - destroy +// 0 - cursor +// 1 - inventory +// 2 - bank +// 3 - trade +// 4 - shared bank +// +// cointype +// 0 - copeer +// 1 - silver +// 2 - gold +// 3 - platinum +// +static const uint32 COINTYPE_PP = 3; +static const uint32 COINTYPE_GP = 2; +static const uint32 COINTYPE_SP = 1; +static const uint32 COINTYPE_CP = 0; + +struct MoveCoin_Struct +{ + int32 from_slot; + int32 to_slot; + int32 cointype1; + int32 cointype2; + int32 amount; +}; +struct TradeCoin_Struct{ + uint32 trader; + uint8 slot; + uint16 unknown5; + uint8 unknown7; + uint32 amount; +}; +struct TradeMoneyUpdate_Struct{ + uint32 trader; + uint32 type; + uint32 amount; +}; +/* +** Surname struct +** Size: 100 bytes +*/ +struct Surname_Struct +{ +/*0000*/ char name[64]; +/*0064*/ uint32 unknown0064; +/*0068*/ char lastname[32]; +/*0100*/ +}; + +struct GuildsListEntry_Struct { + char name[64]; +}; + +struct GuildsList_Struct { + uint8 head[64]; // First on guild list seems to be empty... + GuildsListEntry_Struct Guilds[MAX_NUMBER_GUILDS]; +}; + +struct GuildUpdate_Struct { + uint32 guildID; + GuildsListEntry_Struct entry; +}; + +/* +** Money Loot +** Length: 22 Bytes +** OpCode: 5020 +*/ +struct moneyOnCorpseStruct { +/*0000*/ uint8 response; // 0 = someone else is, 1 = OK, 2 = not at this time +/*0001*/ uint8 unknown1; // = 0x5a +/*0002*/ uint8 unknown2; // = 0x40 +/*0003*/ uint8 unknown3; // = 0 +/*0004*/ uint32 platinum; // Platinum Pieces +/*0008*/ uint32 gold; // Gold Pieces + +/*0012*/ uint32 silver; // Silver Pieces +/*0016*/ uint32 copper; // Copper Pieces +}; + +struct LootingItem_Struct { +/*000*/ uint32 lootee; +/*004*/ uint32 looter; +/*008*/ uint16 slot_id; +/*010*/ uint16 unknown10; +/*012*/ uint32 auto_loot; +/*016*/ uint32 unknown16; +/*020*/ +}; + +struct GuildManageStatus_Struct{ + uint32 guildid; + uint32 oldrank; + uint32 newrank; + char name[64]; +}; +// Guild invite, remove +struct GuildJoin_Struct{ +/*000*/ uint32 guild_id; +/*004*/ uint32 unknown04; +/*008*/ uint32 level; +/*012*/ uint32 class_; +/*016*/ uint32 rank;//0 member, 1 officer, 2 leader +/*020*/ uint32 zoneid; +/*024*/ uint32 unknown24; +/*028*/ char name[64]; +/*092*/ +}; +struct GuildInviteAccept_Struct { + char inviter[64]; + char newmember[64]; + uint32 response; + uint32 guildeqid; +}; +struct GuildManageRemove_Struct { + uint32 guildeqid; + char member[64]; +}; +struct GuildCommand_Struct { +/*000*/ char othername[64]; +/*064*/ char myname[64]; +/*128*/ uint16 guildeqid; +/*130*/ uint8 unknown[2]; // for guildinvite all 0's, for remove 0=0x56, 2=0x02 +/*132*/ uint32 officer; +/*136*/ uint32 unknown136; // New in RoF +/*140*/ +}; + +// Opcode OP_GMZoneRequest +// Size = 88 bytes +struct GMZoneRequest_Struct { +/*0000*/ char charname[64]; +/*0064*/ uint32 zone_id; +/*0068*/ float x; +/*0072*/ float y; +/*0076*/ float z; +/*0080*/ char unknown0080[4]; +/*0084*/ uint32 success; // 0 if command failed, 1 if succeeded? +/*0088*/ +// /*072*/ int8 success; // =0 client->server, =1 server->client, -X=specific error +// /*073*/ uint8 unknown0073[3]; // =0 ok, =ffffff error +}; + +struct GMSummon_Struct { +/* 0*/ char charname[64]; +/* 30*/ char gmname[64]; +/* 60*/ uint32 success; +/* 61*/ uint32 zoneID; +/*92*/ int32 y; +/*96*/ int32 x; +/*100*/ int32 z; +/*104*/ uint32 unknown2; // E0 E0 56 00 +}; + +struct GMGoto_Struct { // x,y is swapped as compared to summon and makes sense as own packet +/* 0*/ char charname[64]; + +/* 64*/ char gmname[64]; +/* 128*/ uint32 success; +/* 132*/ uint32 zoneID; + +/*136*/ int32 y; +/*140*/ int32 x; +/*144*/ int32 z; +/*148*/ uint32 unknown2; // E0 E0 56 00 +}; + +struct GMLastName_Struct { +/*000*/ char name[64]; +/*064*/ char gmname[64]; +/*128*/ char lastname[64]; +/*192*/ uint16 unknown[4]; // 0x00, 0x00, 0x01, 0x00 = Update the clients +/*200*/ uint32 unknown200[8]; +/*232*/ +}; + +struct OnLevelMessage_Struct { +/*0000*/ uint32 Title_Count; +/*0000*/ char Title[128]; +/*0000*/ uint32 Text_Count; +/*0000*/ char Text[4096]; +/*0000*/ uint32 ButtonName0_Count; +/*0000*/ char ButtonName0[25]; // If Buttons = 1, these two are the text for the left and right buttons respectively +/*0000*/ uint32 ButtonName1_Count; +/*0000*/ char ButtonName1[25]; +/*0000*/ uint8 Buttons; +/*0000*/ uint8 Unknown4275; // Something to do with audio controls +/*0000*/ uint32 Duration; +/*0000*/ uint32 PopupID; // If none zero, a response packet with 00 00 00 00 is returned on clicking the left button +/*0000*/ uint32 NegativeID; // If none zero, a response packet with 01 00 00 00 is returned on clicking the right button +/*0000*/ uint32 Unknown4288; +/*0000*/ uint8 Unknown4276; +/*0000*/ uint8 Unknown4277; +/*0000*/ +}; + +//Combat Abilities +struct CombatAbility_Struct { + uint32 m_target; //the ID of the target mob + uint32 m_atk; + uint32 m_skill; +}; + +//Instill Doubt +struct Instill_Doubt_Struct { + uint8 i_id; + uint8 ia_unknown; + uint8 ib_unknown; + uint8 ic_unknown; + uint8 i_atk; + + uint8 id_unknown; + uint8 ie_unknown; + uint8 if_unknown; + uint8 i_type; + uint8 ig_unknown; + uint8 ih_unknown; + uint8 ii_unknown; +}; + +struct GiveItem_Struct { + uint16 to_entity; + int16 to_equipSlot; + uint16 from_entity; + int16 from_equipSlot; +}; + +struct RandomReq_Struct { + uint32 low; + uint32 high; +}; + +/* solar: 9/23/03 reply to /random command; struct from Zaphod */ +struct RandomReply_Struct { +/* 00 */ uint32 low; +/* 04 */ uint32 high; +/* 08 */ uint32 result; +/* 12 */ char name[64]; +/* 76 */ +}; + +/* +** LFG_Appearance_Struct +** Packet sent to clients to notify when someone in zone toggles LFG flag +** Size: 8 bytes +** Used in: OP_LFGAppearance +** +*/ +struct LFG_Appearance_Struct +{ +/*0000*/ uint32 spawn_id; // ID of the client +/*0004*/ uint8 lfg; // 1=LFG, 0=Not LFG +/*0005*/ char unknown0005[3]; // +/*0008*/ +}; + + +// EverQuest Time Information: +// 72 minutes per EQ Day +// 3 minutes per EQ Hour +// 6 seconds per EQ Tick (2 minutes EQ Time) +// 3 seconds per EQ Minute + +struct TimeOfDay_Struct { + uint8 hour; + uint8 minute; + uint8 day; + uint8 month; + uint16 year; +/*0006*/ uint16 unknown0016; // Placeholder +/*0008*/ +}; + +// Darvik: shopkeeper structs +struct Merchant_Click_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; +/*008*/ uint32 command; // 1=open, 0=cancel/close +/*012*/ float rate; // cost multiplier, dosent work anymore +/*016*/ int32 unknown01; // Seen 3 from Server or -1 from Client +/*020*/ int32 unknown02; // Seen 2592000 from Server or -1 from Client +/*024*/ +}; +/* +Unknowns: +0 is e7 from 01 to // MAYBE SLOT IN PURCHASE +1 is 03 +2 is 00 +3 is 00 +4 is ?? +5 is ?? +6 is 00 from a0 to +7 is 00 from 3f to */ +/* +0 is F6 to 01 +1 is CE CE +4A 4A +00 00 +00 E0 +00 CB +00 90 +00 3F +*/ + +struct Merchant_Sell_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; // Player's entity id +/*008*/ uint32 itemslot; // Merchant Slot / Item Instance ID +/*012*/ uint32 unknown12; +/*016*/ uint32 quantity; // Already sold +/*020*/ uint32 unknown20; +/*024*/ uint32 price; +/*028*/ uint32 unknown28; // Normally 0, but seen 84 c5 63 00 as well +/*032*/ +}; + +struct Merchant_Purchase_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ MainInvItemSlotStruct itemslot; +/*012*/ uint32 quantity; +/*016*/ uint32 price; +/*020*/ +}; + +struct Merchant_DelItem_Struct{ +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; // Player's entity id +/*008*/ uint32 itemslot; +}; + +struct AltCurrencyDefinition_Struct { + uint32 id; + uint32 item_id; +}; + +//One packet i didn't include here is the alt currency merchant window. +//it works much like the adventure merchant window +//it is formated like: dbstringid|1|dbstringid|count +//ex for a blank crowns window you would send: +//999999|1|999999|0 +//any items come after in much the same way adventure merchant items do except there is no theme included + +//Server -> Client +//Populates the initial Alternate Currency Window +struct AltCurrencyPopulateEntry_Struct +{ +/*000*/ uint32 currency_number; // corresponds to a dbstr id as well, the string matches what shows up in the "alternate currency" tab. +/*004*/ uint32 unknown00; // always 1 +/*008*/ uint32 currency_number2; // always same as currency number +/*012*/ uint32 item_id; // appears to be the item id +/*016*/ uint32 item_icon; // actual icon +/*020*/ uint32 stack_size; // most are set to 1000, the stack size for the item; should match db i think or there will be problems. +/*024*/ uint8 display; // If set to 0, will not display by default. +/*025*/ +}; + +struct AltCurrencyPopulate_Struct { +/*000*/ uint32 opcode; // 8 for populate +/*004*/ uint32 count; // number of entries +/*008*/ AltCurrencyPopulateEntry_Struct entries[0]; +}; + +//Server -> Client +//Updates the value of a specific Alternate Currency +struct AltCurrencyUpdate_Struct { +/*000*/ uint32 opcode; //7 for update +/*004*/ char name[64]; //name of client (who knows why just do it) +/*068*/ uint32 currency_number; //matches currency_number from populate entry +/*072*/ uint32 unknown072; //always 1 +/*076*/ uint32 amount; //new amount +/*080*/ uint32 unknown080; //seen 0 +/*084*/ uint32 unknown084; //seen 0 +}; + +//Client -> Server +//When an item is selected while the alt currency merchant window is open +struct AltCurrencySelectItem_Struct { +/*000*/ uint32 merchant_entity_id; +/*004*/ //uint32 slot_id; + ItemSlotStruct slot_id; +/*008*/ uint32 unknown008; +/*012*/ uint32 unknown012; +/*016*/ uint32 unknown016; +/*020*/ uint32 unknown020; +/*024*/ uint32 unknown024; +/*028*/ uint32 unknown028; +/*032*/ uint32 unknown032; +/*036*/ uint32 unknown036; +/*040*/ uint32 unknown040; +/*044*/ uint32 unknown044; +/*048*/ uint32 unknown048; +/*052*/ uint32 unknown052; +/*056*/ uint32 unknown056; +/*060*/ uint32 unknown060; +/*064*/ uint32 unknown064; +/*068*/ uint32 unknown068; +/*072*/ uint32 unknown072; +/*076*/ uint32 unknown076; +}; + +//Server -> Client +//As setup it makes it so that item can't be sold to the merchant. +//eg: "I will give you no doubloons for a cloth cap." +//Probably also sends amounts somewhere +struct AltCurrencySelectItemReply_Struct { +/*000*/ uint32 unknown000; +/*004*/ uint8 unknown004; //0xff +/*005*/ uint8 unknown005; //0xff +/*006*/ uint8 unknown006; //0xff +/*007*/ uint8 unknown007; //0xff +/*008*/ char item_name[64]; +/*072*/ uint32 unknown074; +/*076*/ uint32 cost; +/*080*/ uint32 unknown080; +/*084*/ uint32 unknown084; +}; + +//Client -> Server +//Requests purchase of a specific item from the vendor +struct AltCurrencyPurchaseItem_Struct { +/*000*/ uint32 merchant_entity_id; +/*004*/ uint32 item_id; +/*008*/ uint32 unknown008; //1 +}; + +//Client -> Server +//Reclaims / Create currency button pushed. +struct AltCurrencyReclaim_Struct { +/*000*/ uint32 currency_id; +/*004*/ uint32 unknown004; +/*008*/ uint32 count; +/*012*/ uint32 reclaim_flag; //1 = this is reclaim +}; + +struct AltCurrencySellItem_Struct { +/*000*/ uint32 merchant_entity_id; +/*004*/ //uint32 slot_id; + ItemSlotStruct slot_id; +/*008*/ uint32 charges; +/*012*/ uint32 cost; +}; + +struct Adventure_Purchase_Struct { +/*000*/ uint32 some_flag; //set to 1 generally... +/*004*/ uint32 npcid; +/*008*/ uint32 itemid; +/*012*/ uint32 variable; +}; + +struct Adventure_Sell_Struct { +/*000*/ uint32 unknown000; //0x01 - Stack Size/Charges? +/*004*/ uint32 npcid; +/*008*/ MainInvItemSlotStruct slot; +/*016*/ uint32 charges; +/*020*/ uint32 sell_price; +/*024*/ +}; + +struct AdventurePoints_Update_Struct { +/*000*/ uint32 ldon_available_points; // Total available points +/*004*/ uint8 unkown_apu004[20]; +/*024*/ uint32 ldon_guk_points; // Earned Deepest Guk points +/*028*/ uint32 ldon_mirugal_points; // Earned Mirugal' Mebagerie points +/*032*/ uint32 ldon_mistmoore_points; // Earned Mismoore Catacombs Points +/*036*/ uint32 ldon_rujarkian_points; // Earned Rujarkian Hills points +/*040*/ uint32 ldon_takish_points; // Earned Takish points +/*044*/ uint8 unknown_apu042[216]; +}; + + +struct AdventureFinish_Struct{ + uint32 win_lose;//Cofruben: 00 is a lose,01 is win. + uint32 points; +}; +//OP_AdventureRequest +struct AdventureRequest_Struct{ + uint32 risk;//1 normal,2 hard. + uint32 entity_id; +}; +struct AdventureRequestResponse_Struct{ + uint32 unknown000; + char text[2048]; + uint32 timetoenter; + uint32 timeleft; + uint32 risk; + float x; + float y; + float z; + uint32 showcompass; + uint32 unknown2080; +}; + +//this is mostly right but something is off that causes the client to crash sometimes +//I don't really care enough about the feature to work on it anymore though. +struct AdventureLeaderboardEntry_Struct +{ +/*004*/ char name[64]; +/*008*/ uint32 success; +/*012*/ uint32 failure; +/*016*/ +}; + +struct AdventureLeaderboard_Struct +{ +/*000*/ uint32 unknown000; +/*004*/ uint32 unknown004; +/*008*/ uint32 success; +/*012*/ uint32 failure; +/*016*/ uint32 our_rank; +/*020*/ +}; + +/*struct Item_Shop_Struct { + uint16 merchantid; + uint8 itemtype; + Item_Struct item; + uint8 iss_unknown001[6]; +};*/ + +struct Illusion_Struct { // Was size: 336 +/*000*/ uint32 spawnid; +/*004*/ char charname[64]; +/*068*/ uint16 race; +/*070*/ char unknown006[2]; // Weird green name +/*072*/ uint8 gender; +/*073*/ uint8 texture; +/*074*/ uint8 unknown074; +/*075*/ uint8 unknown075; +/*076*/ uint8 helmtexture; +/*077*/ uint8 unknown077; +/*078*/ uint8 unknown078; +/*079*/ uint8 unknown079; +/*080*/ uint32 face; +/*084*/ uint8 hairstyle; // Some Races don't change Hair Style Properly in SoF +/*085*/ uint8 haircolor; +/*086*/ uint8 beard; +/*087*/ uint8 beardcolor; +/*088*/ float size; +/*092*/ uint8 unknown092[148]; +/*240*/ uint32 unknown240; // Removes armor? +/*244*/ uint32 drakkin_heritage; +/*248*/ uint32 drakkin_tattoo; +/*252*/ uint32 drakkin_details; +/*256*/ uint8 unknown256[60]; // This and below are new to RoF +/*316*/ int32 unknown316; // Seen -1 +/*320*/ uint8 unknown320[16]; +/*336*/ +}; + +struct ZonePoint_Entry { //32 octets +/*0000*/ uint32 iterator; +/*0004*/ float y; +/*0008*/ float x; +/*0012*/ float z; +/*0016*/ float heading; +/*0020*/ uint16 zoneid; +/*0022*/ uint16 zoneinstance; // LDoN instance +/*0024*/ uint32 unknown024; +/*0028*/ uint32 unknown028; +/*0032*/ +}; + +struct ZonePoints { +/*0000*/ uint32 count; +/*0004*/ struct ZonePoint_Entry zpe[0]; // Always add one extra to the end after all zonepoints +//*0xxx*/ uint8 unknown0xxx[24]; //New from SEQ +}; + +struct SkillUpdate_Struct { +/*00*/ uint32 skillId; +/*04*/ uint32 value; +/*08*/ uint8 unknown08; // Seen 1 +/*09*/ uint8 unknown09; // Seen 80 +/*10*/ uint8 unknown10; // Seen 136 +/*11*/ uint8 unknown11; // Seen 54 +/*12*/ +}; + +struct ZoneUnavail_Struct { + //This actually varies, but... + char zonename[16]; + int16 unknown[4]; +}; + +struct GroupInvite_Struct { +/*0000*/ char invitee_name[64]; +/*0064*/ char inviter_name[64]; +/*0128*/ uint32 unknown0128; +/*0132*/ uint32 unknown0132; +/*0136*/ uint32 unknown0136; +/*0140*/ uint32 unknown0140; +/*0144*/ uint32 unknown0144; +/*0148*/ +}; + +struct GroupGeneric_Struct { +/*0000*/ char name1[64]; +/*0064*/ char name2[64]; +/*0128*/ uint32 unknown0128; +/*0132*/ uint32 unknown0132; +/*0136*/ uint32 unknown0136; +/*0140*/ uint32 unknown0140; +/*0144*/ uint32 unknown0144; +/*0148*/ +}; + +struct GroupCancel_Struct { +/*000*/ char name1[64]; +/*064*/ char name2[64]; +/*128*/ uint8 unknown128[20]; +/*148*/ uint32 toggle; +/*152*/ +}; + +struct GroupUpdate_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +/*0452*/ +}; + +struct GroupUpdate2_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +/*0452*/ GroupLeadershipAA_Struct leader_aas; +/*0516*/ uint8 unknown[252]; // Titanium uses [188] here +/*0768*/ +}; + +struct GroupUpdate_Struct_Live { // New for Live +/*0000*/ uint32 groupid; // Guess - Matches unknown0136 from GroupFollow_Struct +/*0004*/ uint32 totalmembers; // Guess +/*0000*/ //uint32 leadersname[0]; // Group Leader Name Null Terminated +/*0008*/ //GroupMembers_Struct groupmembers; +}; + +struct GroupMembers_Struct { // New for Live +/*0000*/ uint32 membernumber; // Guess - number of member in the group (0 to 5?) +/*0000*/ //char membername[0]; // Member Name Null Terminated +/*0000*/ uint8 unknown001[3]; // Seen 0 +/*0000*/ uint32 memberlevel; // Guess +/*0000*/ uint8 unknown002[11]; // Seen 0 +}; + +struct GroupJoin_Struct_Live { // New for Live +/*0000*/ uint32 unknown0000; // Matches unknown0136 from GroupFollow_Struct +/*0004*/ uint32 action; +/*0008*/ uint8 unknown0008[5]; // Seen 0 +/*0013*/ //char membername[0]; // Null Terminated? +/*0000*/ uint8 unknown0013[3]; // Seen 0 +/*0000*/ uint32 unknown0016; // Matches unknown0132 from GroupFollow_Struct +/*0000*/ uint8 unknown0020[11]; // Seen 0 +}; + +struct GroupJoin_Struct { +/*000*/ char unknown000[64]; +/*064*/ char membername[64]; +/*128*/ uint8 unknown128[20]; // Leadership AA ? +/*148*/ +}; + +struct GroupFollow_Struct { // Live Follow Struct +/*0000*/ char name1[64]; // inviter +/*0064*/ char name2[64]; // invitee +/*0128*/ uint32 unknown0128; // Seen 0 +/*0132*/ uint32 unknown0132; // Group ID or member level? +/*0136*/ uint32 unknown0136; // Maybe Voice Chat Channel or Group ID? +/*0140*/ uint32 unknown0140; // Seen 0 +/*0144*/ uint32 unknown0144; // Seen 0 +/*0148*/ uint32 unknown0148; +/*0152*/ +}; + +struct LFG_Struct { +/*000*/ uint32 unknown000; +/*004*/ uint32 value; // 0x00 = off 0x01 = on +/*008*/ uint32 unknown008; +/*012*/ uint32 unknown012; +/*016*/ char name[64]; +}; + +struct FaceChange_Struct { +/*000*/ uint8 haircolor; +/*001*/ uint8 beardcolor; +/*002*/ uint8 eyecolor1; +/*003*/ uint8 eyecolor2; +/*004*/ uint8 hairstyle; +/*005*/ uint8 beard; +/*006*/ uint8 face; +/*007*/ uint8 unknown007; +/*008*/ uint32 drakkin_heritage; +/*012*/ uint32 drakkin_tattoo; +/*016*/ uint32 drakkin_details; +/*020*/ uint32 unknown020; +/*024*/ +}; +//there are only 10 faces for barbs changing woad just +//increase the face value by ten so if there were 8 woad +//designs then there would be 80 barb faces + +/* +** Trade request from one client to another +** Used to initiate a trade +** Size: 8 bytes +** Used in: OP_TradeRequest +*/ +struct TradeRequest_Struct { +/*00*/ uint32 to_mob_id; +/*04*/ uint32 from_mob_id; +/*08*/ +}; + +struct TradeAccept_Struct { +/*00*/ uint32 from_mob_id; +/*04*/ uint32 unknown4; //seems to be garbage +/*08*/ +}; + +/* +** Cancel Trade struct +** Sent when a player cancels a trade +** Size: 8 bytes +** Used In: OP_CancelTrade +** +*/ +struct CancelTrade_Struct { +/*00*/ uint32 fromid; +/*04*/ uint32 action; +/*08*/ +}; + +struct PetitionUpdate_Struct { + uint32 petnumber; // Petition Number + uint32 color; // 0x00 = green, 0x01 = yellow, 0x02 = red + uint32 status; + time_t senttime; // 4 has to be 0x1F + char accountid[32]; + char gmsenttoo[64]; + int32 quetotal; + char charname[64]; +}; + +struct Petition_Struct { + uint32 petnumber; + uint32 urgency; + char accountid[32]; + char lastgm[32]; + uint32 zone; + //char zone[32]; + char charname[64]; + uint32 charlevel; + uint32 charclass; + uint32 charrace; + uint32 unknown; + //time_t senttime; // Time? + uint32 checkouts; + uint32 unavail; + //uint8 unknown5[4]; + time_t senttime; + uint32 unknown2; + char petitiontext[1024]; + char gmtext[1024]; +}; + + +struct Who_All_Struct { // 156 length total +/*000*/ char whom[64]; +/*088*/ uint8 unknown088[64]; +/*064*/ uint32 wrace; // FF FF = no race +/*068*/ uint32 wclass; // FF FF = no class +/*072*/ uint32 lvllow; // FF FF = no numbers +/*076*/ uint32 lvlhigh; // FF FF = no numbers +/*080*/ uint32 gmlookup; // FF FF = not doing /who all gm +/*084*/ uint32 guildid; // Also used for Buyer/Trader/LFG +/*152*/ uint32 type; // 0 = /who 3 = /who all +/*156*/ +}; + +struct Stun_Struct { // 8 bytes total +/*000*/ uint32 duration; // Duration of stun +/*004*/ uint8 unknown004; // seen 0 +/*005*/ uint8 unknown005; // seen 163 +/*006*/ uint8 unknown006; // seen 67 +/*007*/ uint8 unknown007; // seen 0 +/*008*/ +}; + +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 +/*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) +/*36*/ int32 augment_action; // Guessed - 0 = augment, 1 = remove with distiller, 3 = delete aug +/*36*/ //int32 augment_slot; +/*40*/ +}; + +// OP_Emote +struct Emote_Struct { +/*0000*/ uint32 unknown01; +/*0004*/ char message[1024]; // was 1024 +/*1028*/ +}; + +// Inspect +struct Inspect_Struct { + uint32 TargetID; + uint32 PlayerID; +}; + +//OP_InspectAnswer - Size: 1860 +struct InspectResponse_Struct{ +/*000*/ uint32 TargetID; +/*004*/ uint32 playerid; +/*008*/ char itemnames[23][64]; +/*1480*/uint32 itemicons[23]; +/*1572*/char text[288]; // Max number of chars in Inspect Window appears to be 254 +/*1860*/ +}; + +//OP_SetDataRate +struct SetDataRate_Struct { + float newdatarate; +}; + +//OP_SetServerFilter +struct SetServerFilter_Struct { + uint32 filters[35]; //see enum eqFilterType [31] +}; + +//Op_SetServerFilterAck +struct SetServerFilterAck_Struct { + uint8 blank[8]; +}; +struct IncreaseStat_Struct{ + /*0000*/ uint8 unknown0; + /*0001*/ uint8 str; + /*0002*/ uint8 sta; + /*0003*/ uint8 agi; + /*0004*/ uint8 dex; + /*0005*/ uint8 int_; + /*0006*/ uint8 wis; + /*0007*/ uint8 cha; + /*0008*/ uint8 fire; + /*0009*/ uint8 cold; + /*0010*/ uint8 magic; + /*0011*/ uint8 poison; + /*0012*/ uint8 disease; + /*0013*/ char unknown13[116]; + /*0129*/ uint8 str2; + /*0130*/ uint8 sta2; + /*0131*/ uint8 agi2; + /*0132*/ uint8 dex2; + /*0133*/ uint8 int_2; + /*0134*/ uint8 wis2; + /*0135*/ uint8 cha2; + /*0136*/ uint8 fire2; + /*0137*/ uint8 cold2; + /*0138*/ uint8 magic2; + /*0139*/ uint8 poison2; + /*0140*/ uint8 disease2; +}; + +struct GMName_Struct { + char oldname[64]; + char gmname[64]; + char newname[64]; + uint8 badname; + uint8 unknown[3]; +}; + +struct GMDelCorpse_Struct { + char corpsename[64]; + char gmname[64]; + uint8 unknown; +}; + +struct GMKick_Struct { + char name[64]; + char gmname[64]; + uint8 unknown; +}; + + +struct GMKill_Struct { + char name[64]; + char gmname[64]; + uint8 unknown; +}; + + +struct GMEmoteZone_Struct { + char text[512]; +}; + +// The BookText_Struct is not used in SoF and later clients. +// The BookRequest_Struct is used instead for both request and reply. +// +struct BookText_Struct { + uint8 window; // where to display the text (0xFF means new window) + uint8 type; //type: 0=scroll, 1=book, 2=item info.. prolly + char booktext[1]; // Variable Length - was 1 +}; +// This is the request to read a book. +// This is just a "text file" on the server +// or in our case, the 'name' column in our books table. +struct BookRequest_Struct { +/*0000*/ uint32 window; // where to display the text (0xFFFFFFFF means new window). +/*0004*/ uint16 invslot; // Is the slot, but the RoF conversion causes it to fail. Turned to 0 since it isnt required anyway. +/*0008*/ uint32 unknown006; // Seen FFFFFFFF +/*0010*/ uint16 unknown008; // seen 0000 +/*0012*/ uint32 type; // 0 = Scroll, 1 = Book, 2 = Item Info. Possibly others +/*0016*/ uint32 unknown0012; +/*0020*/ uint16 unknown0016; +/*0022*/ char txtfile[8194]; +}; + +/* +** Object/Ground Spawn struct +** Used for Forges, Ovens, ground spawns, items dropped to ground, etc +** Size: Variable +** OpCodes: OP_CreateObject +** +*/ +struct Object_Struct { +/*00*/ uint32 object_count; // Iteration count in the object list +/*00*/ char object_name[1]; // Name of object, usually something like IT63_ACTORDEF +/*00*/ uint16 zone_id; // Redudant, but: Zone the object appears in +/*00*/ uint16 zone_instance; // +/*00*/ uint32 drop_id; // Unique object id for zone +/*00*/ uint32 unknown024; // 53 9e f9 7e - same for all objects in the zone? +/*00*/ float heading; // heading +/*00*/ float unknown032[2]; // 00 00 00 00 00 00 00 00 +/*00*/ float size; // Size - default 1 +/*00*/ float z; // z coord +/*00*/ float x; // x coord +/*00*/ float y; // y coord +/*00*/ int32 object_type; // Type of object, not directly translated to OP_OpenObject +}; +// object_type - 01 = generic drop, 02 = armor, 19 = weapon +// 0xff seems to be indicative of the tradeskill/openable items that end up returning the old style item type in the OP_OpenObject + +/* +** Click Object Struct +** Client clicking on zone object (forge, groundspawn, etc) +** Size: 8 bytes +** Last Updated: Oct-17-2003 +** +*/ +struct ClickObject_Struct { +/*00*/ uint32 drop_id; // Appears to use the Object Count field now +/*04*/ uint32 player_id; +/*08*/ +}; + +struct Shielding_Struct { + uint32 target_id; +}; + +/* +** Click Object Acknowledgement Struct +** Response to client clicking on a World Container (ie, forge) +** +*/ +struct ClickObjectAction_Struct { +/*00*/ //uint32 player_id; // Appears to have been removed +/*00*/ uint32 drop_id; // Appears to use the object_count field now +/*04*/ int32 unknown04; // Seen -1 +/*08*/ int32 unknown08; // Seen -1 +/*08*/ //uint32 open; // 1=opening, 0=closing - Removed? +/*12*/ uint32 type; // See object.h, "Object Types" +/*16*/ uint32 unknown16; // +/*20*/ uint32 icon; // Icon to display for tradeskill containers +/*24*/ uint32 unknown24; // +/*28*/ char object_name[64]; // Object name to display +/*92*/ +}; + +/* +** This is different now, mostly unknown +** +*/ +struct CloseContainer_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 unknown12[12]; +}; + +/* +** Generic Door Struct +** Length: 52 Octets +** Used in: +** cDoorSpawnsStruct(f721) +** +*/ +struct Door_Struct +{ +/*0000*/ char name[32]; // Filename of Door // Was 10char long before... added the 6 in the next unknown to it: Daeken M. BlackBlade +/*0032*/ float yPos; // y loc +/*0036*/ float xPos; // x loc +/*0040*/ float zPos; // z loc +/*0044*/ float heading; +/*0048*/ uint32 incline; // rotates the whole door +/*0052*/ uint32 size; // 100 is normal, smaller number = smaller model +/*0054*/ uint8 unknown0054[4]; // 00 00 00 00 +/*0060*/ uint8 doorId; // door's id # +/*0061*/ uint8 opentype; +/*0062*/ uint8 state_at_spawn; +/*0063*/ uint8 invert_state; // if this is 1, the door is normally open +/*0064*/ uint32 door_param; // normally ff ff ff ff (-1) +/*0068*/ uint32 unknown0068; // 00 00 00 00 +/*0072*/ uint32 unknown0072; // 00 00 00 00 +/*0076*/ uint8 unknown0076[4]; // New for RoF +/*0080*/ uint8 unknown0080; // seen 1 or 0 +/*0081*/ uint8 unknown0081; // seen 1 (always?) +/*0082*/ uint8 unknown0082; // seen 0 (always?) +/*0083*/ uint8 unknown0083; // seen 1 (always?) +/*0084*/ uint8 unknown0084; // seen 0 (always?) +/*0085*/ uint8 unknown0085; // seen 1 or 0 or rarely 2C or 90 or ED or 2D or A1 +/*0086*/ uint8 unknown0086; // seen 0 or rarely FF or FE or 10 or 5A or 82 +/*0087*/ uint8 unknown0087; // seen 0 or rarely 02 or 7C +/*0088*/ uint8 unknown0088[8]; // mostly 0s, the last 3 bytes are something tho +/*0096*/ +}; + +struct DoorSpawns_Struct { + struct Door_Struct doors[0]; +}; + +/* + OP Code: Op_ClickDoor + Size: 16 +*/ +struct ClickDoor_Struct { +/*000*/ uint8 doorid; +/*001*/ uint8 unknown001; // This may be some type of action setting +/*002*/ uint8 unknown002; // This is sometimes set after a lever is closed +/*003*/ uint8 unknown003; // Seen 0 +/*004*/ uint8 picklockskill; +/*005*/ uint8 unknown005[3]; +/*008*/ uint32 item_id; +/*012*/ uint16 player_id; +/*014*/ uint8 unknown014[2]; +/*016*/ +}; + +struct MoveDoor_Struct { + uint8 doorid; + uint8 action; +}; + + +struct BecomeNPC_Struct { + uint32 id; + int32 maxlevel; +}; + +struct Underworld_Struct { + float speed; + float y; + float x; + float z; +}; + +struct Resurrect_Struct { + uint32 unknown00; + uint16 zone_id; + uint16 instance_id; + float y; + float x; + float z; + char your_name[64]; + uint32 unknown88; + char rezzer_name[64]; + uint32 spellid; + char corpse_name[64]; + uint32 action; +/* 228 */ +}; + +struct SetRunMode_Struct { + uint8 mode; //01=run 00=walk + uint8 unknown[3]; +}; + +// EnvDamage is EnvDamage2 without a few bytes at the end. +// Size: 37 bytes +struct EnvDamage2_Struct { +/*0000*/ uint32 id; +/*0004*/ uint16 unknown4; +/*0006*/ uint32 damage; +/*0010*/ float unknown10; // New to Underfoot - Seen 1 +/*0014*/ uint8 unknown14[12]; +/*0026*/ uint8 dmgtype; // FA = Lava; FC = Falling +/*0027*/ uint8 unknown27[4]; +/*0031*/ uint16 unknown31; // New to Underfoot - Seen 66 +/*0033*/ uint16 constant; // Always FFFF +/*0035*/ uint16 unknown35; +/*0037*/ +}; + +//Bazaar Stuff + +enum { + BazaarTrader_StartTraderMode = 1, + BazaarTrader_EndTraderMode = 2, + BazaarTrader_UpdatePrice = 3, + BazaarTrader_EndTransaction = 4, + BazaarSearchResults = 7, + BazaarWelcome = 9, + BazaarBuyItem = 10, + BazaarTrader_ShowItems = 11, + BazaarSearchDone = 12, + BazaarTrader_CustomerBrowsing = 13, + BazaarInspectItem = 18, + BazaarSearchDone2 = 19, + BazaarTrader_StartTraderMode2 = 22 +}; + +enum { + BazaarPriceChange_Fail = 0, + BazaarPriceChange_UpdatePrice = 1, + BazaarPriceChange_RemoveItem = 2, + BazaarPriceChange_AddItem = 3 +}; + +struct BazaarWindowStart_Struct { + uint8 Action; + uint8 Unknown001; + uint16 Unknown002; +}; + + +struct BazaarWelcome_Struct { + BazaarWindowStart_Struct Beginning; + uint32 Traders; + uint32 Items; + uint8 Unknown012[8]; +}; + +struct BazaarSearch_Struct { + BazaarWindowStart_Struct Beginning; + uint32 TraderID; + uint32 Class_; + uint32 Race; + uint32 ItemStat; + uint32 Slot; + uint32 Type; + char Name[64]; + uint32 MinPrice; + uint32 MaxPrice; + uint32 Minlevel; + uint32 MaxLlevel; +}; +struct BazaarInspect_Struct{ + uint32 ItemID; + uint32 Unknown004; + char Name[64]; +}; + +struct NewBazaarInspect_Struct { +/*000*/ BazaarWindowStart_Struct Beginning; +/*004*/ char Name[64]; +/*068*/ uint32 Unknown068; +/*072*/ int32 SerialNumber; +/*076*/ uint32 Unknown076; +/*080*/ uint32 SellerID; +/*084*/ uint32 Unknown084; +}; + +struct BazaarReturnDone_Struct{ + uint32 Type; + uint32 TraderID; + uint32 Unknown008; + uint32 Unknown012; + uint32 Unknown016; +}; + +struct BazaarSearchResults_Struct { +/*000*/ BazaarWindowStart_Struct Beginning; +/*004*/ uint32 SellerID; +/*008*/ char SellerName[64]; +/*072*/ uint32 NumItems; +/*076*/ uint32 ItemID; +/*080*/ uint32 SerialNumber; +/*084*/ uint32 Unknown084; +/*088*/ char ItemName[64]; +/*152*/ uint32 Cost; +/*156*/ uint32 ItemStat; +/*160*/ +}; + +struct ServerSideFilters_Struct { +uint8 clientattackfilters; // 0) No, 1) All (players) but self, 2) All (players) but group +uint8 npcattackfilters; // 0) No, 1) Ignore NPC misses (all), 2) Ignore NPC Misses + Attacks (all but self), 3) Ignores NPC Misses + Attacks (all but group) +uint8 clientcastfilters; // 0) No, 1) Ignore PC Casts (all), 2) Ignore PC Casts (not directed towards self) +uint8 npccastfilters; // 0) No, 1) Ignore NPC Casts (all), 2) Ignore NPC Casts (not directed towards self) +}; + +/* +** Client requesting item statistics +** Size: 52 bytes +** Used In: OP_ItemLinkClick +** Last Updated: 01/03/2012 +** +*/ +struct ItemViewRequest_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 augments[6]; +/*028*/ uint32 link_hash; +/*032*/ uint32 unknown028; //seems to always be 4 on SoF client +/*036*/ char unknown032[12]; //probably includes loregroup & evolving info. see Client::MakeItemLink() in zone/inventory.cpp:469 +/*048*/ uint16 icon; +/*050*/ char unknown046[2]; +/*052*/ +}; + +/* + * Client to server packet + */ +struct PickPocket_Struct { +// Size 18 + uint32 to; + uint32 from; + uint16 myskill; + uint8 type; // -1 you are being picked, 0 failed , 1 = plat, 2 = gold, 3 = silver, 4 = copper, 5 = item + uint8 unknown1; // 0 for response, unknown for input + uint32 coin; + uint8 lastsix[2]; +}; +/* + * Server to client packet + */ + +struct sPickPocket_Struct { + // Size 28 = coin/fail + uint32 to; + uint32 from; + uint32 myskill; + uint32 type; + uint32 coin; + char itemname[64]; +}; + + +struct LogServer_Struct { +// Op_Code OP_LOGSERVER +/*000*/ uint32 unknown000; +/*004*/ uint8 enable_pvp; +/*005*/ uint8 unknown005; +/*006*/ uint8 unknown006; +/*007*/ uint8 unknown007; +/*008*/ uint8 enable_FV; +/*009*/ uint8 unknown009; +/*010*/ uint8 unknown010; +/*011*/ uint8 unknown011; +/*012*/ uint32 unknown012; // htonl(1) on live +/*016*/ uint32 unknown016; // htonl(1) on live +/*020*/ uint8 unknown020[12]; +/*032*/ uint32 unknown032; +/*036*/ char worldshortname[32]; +/*068*/ uint8 unknown068[181]; +/*249*/ uint8 unknown249[27]; +/*276*/ float unknown276[7]; +/*304*/ uint8 unknown304[256]; +/*560*/ + +/* Currently lost + uint8 enablevoicemacros; + uint8 enablemail; +*/ +}; + +struct ApproveWorld_Struct { +// Size 544 +// Op_Code OP_ApproveWorld + uint8 unknown544[544]; +}; + +struct ClientError_Struct +{ +/*00001*/ char type; +/*00001*/ char unknown0001[69]; +/*00069*/ char character_name[64]; +/*00134*/ char unknown134[192]; +/*00133*/ char message[31994]; +/*32136*/ +}; + +struct MobHealth +{ + /*0000*/ uint8 hp; //health percent + /*0001*/ uint16 id;//mobs id +}; + +struct Track_Struct { + uint16 entityid; + uint16 y; + uint16 x; + uint16 z; +}; + +struct Tracking_Struct { + Track_Struct Entrys[0]; +}; + +// Looks like new tracking structures - Opcode: 0x57a7 +struct Tracking_Struct_New { + uint16 totalcount; // Total Count of mobs within tracking range + Track_Struct Entrys[0]; +}; + +struct Track_Struct_New { + uint16 entityid; // Entity ID + uint16 unknown002; // 00 00 + uint32 unknown004; // + uint8 level; // level of mob + uint8 unknown009; // 01 maybe type of mob? player/npc? + char name[1]; // name of mob +}; + + +/* +** ZoneServerInfo_Struct +** Zone server information +** Size: 130 bytes +** Used In: OP_ZoneServerInfo +** +*/ +struct ZoneServerInfo_Struct +{ +/*0000*/ char ip[128]; +/*0128*/ uint16 port; +}; + +struct WhoAllPlayer{ + uint32 formatstring; + uint32 pidstring; + int32 unknown64; // Seen -1 + char* name; + uint32 rankstring; + char* guild; + uint32 unknown80[2]; + uint32 zonestring; + uint32 zone; + uint32 class_; + uint32 level; + uint32 race; + char* account; + uint32 unknown100; +}; + +struct WhoAllReturnStruct { + uint32 id; + uint32 playerineqstring; + char line[27]; + uint8 unknown35; // 0A + uint32 unknown36; // Seen 208243456 + uint32 playersinzonestring; + uint32 unknown52; // Same as playercount? + uint32 unknown44[2]; // 0s + uint32 unknown56; // Same as playercount? + uint32 playercount; // Player Count in the who list + struct WhoAllPlayer player[0]; +}; + +// The following four structs are the WhoAllPlayer struct above broken down +// for use in World ClientList::SendFriendsWho to accomodate the user of variable +// length strings within the struct above. + +struct WhoAllPlayerPart1 { + uint32 FormatMSGID; + uint32 Unknown04; + uint32 Unknown08; + char Name[1]; +}; + +struct WhoAllPlayerPart2 { + uint32 RankMSGID; + char Guild[1]; +}; + +struct WhoAllPlayerPart3 { + uint32 Unknown80[2]; + uint32 ZoneMSGID; + uint32 Zone; + uint32 Class_; + uint32 Level; + uint32 Race; + char Account[1]; +}; + +struct WhoAllPlayerPart4 { + uint32 Unknown100; +}; + +struct TraderItemSerial_Struct { + char SerialNumber[17]; + uint8 Unknown18; +}; + +struct Trader_Struct { +/*0000*/ uint32 Code; +/*0004*/ TraderItemSerial_Struct items[200]; +/*3604*/ uint32 ItemCost[200]; +/*4404*/ +}; + +struct ClickTrader_Struct { +/*0000*/ uint32 Code; +/*0004*/ TraderItemSerial_Struct items[200]; +/*3604*/ uint32 ItemCost[200]; +/*4404*/ +}; + +struct GetItems_Struct { + uint32 items[200]; +}; + +struct BecomeTrader_Struct { + uint32 id; + uint32 code; +}; + +struct Trader_ShowItems_Struct { +/*000*/ uint32 Code; +/*004*/ char SerialNumber[17]; +/*021*/ uint8 Unknown21; +/*022*/ uint16 TraderID; +/*026*/ uint32 Stacksize; +/*030*/ uint32 Price; +/*032*/ +}; + +struct TraderStatus_Struct { +/*000*/ uint32 Code; +/*004*/ uint32 Uknown04; +/*008*/ uint32 Uknown08; +/*012*/ +}; + +struct TraderBuy_Struct { +/*000*/ uint32 Action; +/*004*/ uint32 Unknown004; +/*008*/ uint32 Price; +/*012*/ uint32 Unknown008; // Probably high order bits of a 64 bit price. +/*016*/ uint32 TraderID; +/*020*/ char ItemName[64]; +/*084*/ uint32 Unknown076; +/*088*/ uint32 ItemID; +/*092*/ uint32 AlreadySold; +/*096*/ uint32 Quantity; +/*100*/ uint32 Unknown092; +/*104*/ +}; + +struct TraderItemUpdate_Struct{ + uint32 unknown0; + uint32 traderid; + uint8 fromslot; + uint8 toslot; //7? + uint16 charges; +}; + +struct MoneyUpdate_Struct{ + int32 platinum; + int32 gold; + int32 silver; + int32 copper; +}; + +//struct MoneyUpdate_Struct +//{ +//*0000*/ uint32 spawn_id; // ***Placeholder +//*0004*/ uint32 cointype; // Coin Type +//*0008*/ uint32 amount; // Amount +//*0012*/ +//}; + + +struct TraderDelItem_Struct{ + uint32 slotid; + uint32 quantity; + uint32 unknown; +}; + +struct TraderClick_Struct{ + uint32 traderid; + uint32 unknown4[2]; + uint32 approval; +}; + +struct FormattedMessage_Struct{ + uint32 unknown0; + uint32 string_id; + uint32 type; + char message[0]; +//*0???*/ uint8 unknown0[8]; // ***Placeholder +}; +struct SimpleMessage_Struct{ + uint32 string_id; + uint32 color; + uint32 unknown8; +}; + +// Size: 52 + strings +// Other than the strings, all of this packet is network byte order (reverse from normal) +struct GuildMemberEntry_Struct { + char name[1]; // variable length + uint32 level; + uint32 banker; // 1=yes, 0=no + uint32 class_; + uint32 rank; + uint32 time_last_on; + uint32 tribute_enable; + uint32 unknown01; // Seen 0 + uint32 total_tribute; // total guild tribute donated, network byte order + uint32 last_tribute; // unix timestamp + uint32 unknown_one; // unknown, set to 1 + char public_note[1]; // variable length. + uint16 zoneinstance; // Seen 0s or -1 in RoF + uint16 zone_id; // Seen 0s or -1 in RoF + uint32 unknown_one2; // unknown, set to 1 + uint32 unknown04; // Seen 0 +}; + +//just for display purposes, this is not actually used in the message encoding other than for size. +struct GuildMembers_Struct { + char player_name[1]; // variable length. + uint32 guildid; // Was unknown02 - network byte order + uint32 count; // network byte order + GuildMemberEntry_Struct member[0]; +}; + +struct GuildMOTD_Struct{ +/*0000*/ uint32 unknown0; +/*0004*/ char name[64]; +/*0068*/ char setby_name[64]; +/*0132*/ uint32 unknown132; +/*0136*/ char motd[0]; //was 512 +}; + +struct GuildURL_Struct{ +/*0000*/ uint32 unknown0; //index? seen server send 0 w/ the Guild URL, followed by 1 with nothing. +/*0004*/ uint32 unknown4; +/*0008*/ uint32 unknown8; //seen 7 +/*0012*/ char setby_name[64]; +/*0076*/ uint32 unknown132; //seen 0xc7de +/*0136*/ char url[4040]; +}; + +struct GuildStatus_Struct +{ +/*000*/ char Name[64]; +/*064*/ uint8 Unknown064[76]; +}; + +struct GuildMemberUpdate_Struct { +/*00*/ uint32 GuildID; +/*04*/ char MemberName[64]; +/*68*/ uint16 ZoneID; +/*70*/ uint16 InstanceID; //speculated +/*72*/ uint32 LastSeen; //unix timestamp +/*76*/ uint32 Unknown76; +/*80*/ +}; + +struct GuildMemberLevelUpdate_Struct { +/*00*/ uint32 guild_id; +/*04*/ char member_name[64]; +/*68*/ uint32 level; //not sure +}; + +struct GuildUpdate_PublicNote { + uint32 unknown0; + char name[64]; + char target[64]; + char note[100]; //we are cutting this off at 100, actually around 252 +}; + +struct GuildDemoteStruct { +/*000*/ char name[64]; +/*064*/ char target[64]; +/*128*/ uint32 rank; // New in RoF +/*132*/ +}; + +struct GuildRemoveStruct { +/*000*/ char target[64]; +/*064*/ char name[64]; +/*128*/ uint32 GuildID; // Was unknown128 +/*132*/ uint32 leaderstatus; +/*136*/ uint32 unknown136; // New in RoF +/*140*/ +}; + +struct GuildMakeLeader { + char name[64]; + char target[64]; +}; + +// Server -> Client +// Update a guild members rank and banker status +struct GuildSetRank_Struct +{ +/*00*/ uint32 GuildID; // Was Unknown00 +/*04*/ uint32 Rank; +/*08*/ char MemberName[64]; +/*72*/ uint32 Banker; +/*76*/ uint32 Unknown76; // Seen 1 - Maybe Banker? +/*80*/ +}; + +struct BugStruct{ +/*0000*/ char chartype[64]; +/*0064*/ char name[96]; +/*0160*/ char ui[128]; +/*0288*/ float x; +/*0292*/ float y; +/*0296*/ float z; +/*0300*/ float heading; +/*0304*/ uint32 unknown304; +/*0308*/ uint32 type; +/*0312*/ char unknown312[2144]; +/*2456*/ char bug[1024]; +/*3480*/ char placeholder[2]; +/*3482*/ char system_info[4098]; +}; +struct Make_Pet_Struct { //Simple struct for getting pet info + uint8 level; + uint8 class_; + uint16 race; + uint8 texture; + uint8 pettype; + float size; + uint8 type; + uint32 min_dmg; + uint32 max_dmg; +}; +struct Ground_Spawn{ + float max_x; + float max_y; + float min_x; + float min_y; + float max_z; + float heading; + char name[16]; + uint32 item; + uint32 max_allowed; + uint32 respawntimer; +}; +struct Ground_Spawns { + struct Ground_Spawn spawn[50]; //Assigned max number to allow +}; +struct PetitionBug_Struct{ + uint32 petition_number; + uint32 unknown4; + char accountname[64]; + uint32 zoneid; + char name[64]; + uint32 level; + uint32 class_; + uint32 race; + uint32 unknown152[3]; + uint32 time; + uint32 unknown168; + char text[1028]; +}; + +struct DyeStruct +{ + union + { + struct + { + struct Color_Struct head; + struct Color_Struct chest; + struct Color_Struct arms; + struct Color_Struct wrists; + struct Color_Struct hands; + struct Color_Struct legs; + struct Color_Struct feet; + struct Color_Struct primary; // you can't actually dye this + struct Color_Struct secondary; // or this + } + dyes; + struct Color_Struct dye[9]; + }; +}; + +struct ApproveZone_Struct { + char name[64]; + uint32 zoneid; + uint32 approve; +}; +struct ZoneInSendName_Struct { + uint32 unknown0; + char name[64]; + char name2[64]; + uint32 unknown132; +}; +struct ZoneInSendName_Struct2 { + uint32 unknown0; + char name[64]; + uint32 unknown68[145]; +}; + +struct StartTribute_Struct { + uint32 client_id; + uint32 tribute_master_id; + uint32 response; +}; + +struct TributeLevel_Struct { + uint32 level; //backwards byte order! + uint32 tribute_item_id; //backwards byte order! + uint32 cost; //backwards byte order! +}; + +struct TributeAbility_Struct { +/*000*/ uint32 tribute_id; //backwards byte order! +/*004*/ uint32 tier_count; //backwards byte order! +/*008*/ TributeLevel_Struct tiers[MAX_TRIBUTE_TIERS]; +/*128*/ uint32 unknown128; // New to RoF +/*132*/ char name[0]; +}; + +struct GuildTributeAbility_Struct { + uint32 guild_id; + TributeAbility_Struct ability; +}; + +struct SelectTributeReq_Struct { + uint32 client_id; //? maybe action ID? + uint32 tribute_id; + uint32 unknown8; //seen E3 00 00 00 +}; + +struct SelectTributeReply_Struct { + uint32 client_id; //echoed from request. + uint32 tribute_id; + char desc[0]; +}; + +struct TributeInfo_Struct { + uint32 active; //0 == inactive, 1 == active + uint32 tributes[MAX_PLAYER_TRIBUTES]; //-1 == NONE + uint32 tiers[MAX_PLAYER_TRIBUTES]; //all 00's + uint32 tribute_master_id; +}; + +struct TributeItem_Struct +{ +/*00*/ ItemSlotStruct slot; +/*12*/ uint32 quantity; +/*16*/ uint32 tribute_master_id; +/*20*/ int32 tribute_points; +/*24*/ +}; + +struct TributePoint_Struct { + int32 tribute_points; + uint32 unknown04; + int32 career_tribute_points; + uint32 unknown12; +}; + +struct TributeMoney_Struct { + uint32 platinum; + uint32 tribute_master_id; + int32 tribute_points; +}; + + +struct Split_Struct +{ + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; +}; + + +/* +** New Combine Struct +** Client requesting to perform a tradeskill combine +** Size: 24 bytes +** Used In: OP_TradeSkillCombine +** Last Updated: 01-05-2013 +*/ +struct NewCombine_Struct { +/*00*/ ItemSlotStruct container_slot; +/*12*/ ItemSlotStruct unknown_slot; // Slot type is 8? +/*24*/ +}; + + +//client requesting favorite recipies +struct TradeskillFavorites_Struct { + uint32 object_type; + uint32 some_id; + uint32 favorite_recipes[500]; +}; + +//search request +struct RecipesSearch_Struct { + uint32 object_type; //same as in favorites + uint32 some_id; //same as in favorites + uint32 mintrivial; + uint32 maxtrivial; + char query[56]; + uint32 unknown4; //is set to 00 03 00 00 + uint32 unknown5; //is set to 4C DD 12 00 +/*80*/ +}; + +//one sent for each item, from server in reply to favorites or search +struct RecipeReply_Struct { + uint32 object_type; + uint32 some_id; //same as in favorites + uint32 component_count; + uint32 recipe_id; + uint32 trivial; + char recipe_name[64]; +/*84*/ +}; + +//received and sent back as an ACK with different reply_code +struct RecipeAutoCombine_Struct { +/*00*/ uint32 object_type; +/*04*/ uint32 some_id; +/*08*/ ItemSlotStruct container_slot; //echoed in reply - Was uint32 unknown1 +/*20*/ ItemSlotStruct unknown_slot; //echoed in reply +/*32*/ uint32 recipe_id; +/*36*/ uint32 reply_code; +/*40*/ +}; + +struct LevelAppearance_Struct { //Sends a little graphic on level up + uint32 spawn_id; + uint32 parm1; + uint32 value1a; + uint32 value1b; + uint32 parm2; + uint32 value2a; + uint32 value2b; + uint32 parm3; + uint32 value3a; + uint32 value3b; + uint32 parm4; + uint32 value4a; + uint32 value4b; + uint32 parm5; + uint32 value5a; + uint32 value5b; +/*64*/ +}; +struct MerchantList{ + uint32 id; + uint32 slot; + uint32 item; +}; +struct TempMerchantList{ + uint32 npcid; + uint32 slot; + uint32 item; + uint32 charges; //charges/quantity + uint32 origslot; +}; + + +struct FindPerson_Point { + float y; + float x; + float z; +}; + +struct FindPersonRequest_Struct { +/*00*/ uint32 unknown00; +/*04*/ uint32 npc_id; +/*08*/ uint32 unknown08; +/*12*/ uint32 unknown12; +/*16*/ FindPerson_Point client_pos; +/*28*/ uint32 unknown28; +/*32*/ uint32 unknown32; +/*36*/ uint32 unknown36; +}; + +//variable length packet of points +struct FindPersonResult_Struct { + FindPerson_Point dest; + FindPerson_Point path[0]; //last element must be the same as dest +}; + +struct MobRename_Struct { +/*000*/ char old_name[64]; +/*064*/ char old_name_again[64]; //not sure what the difference is +/*128*/ char new_name[64]; +/*192*/ uint32 unknown192; //set to 0 +/*196*/ uint32 unknown196; //set to 1 +/*200*/ +}; + +struct PlayMP3_Struct { + char filename[128]; +}; + +//this is for custom title display in the skill window +struct TitleEntry_Struct { + uint32 skill_id; + uint32 skill_value; + char title[1]; +}; + +struct Titles_Struct { + uint32 title_count; + TitleEntry_Struct titles[0]; +}; + +//this is for title selection by the client +struct TitleListEntry_Struct { + uint32 unknown0; //title ID + char prefix[1]; //variable length, null terminated + char postfix[1]; //variable length, null terminated +}; + +struct TitleList_Struct { + uint32 title_count; + TitleListEntry_Struct titles[0]; //list of title structs + //uint32 unknown_ending; seen 0x7265, 0 +}; + +struct SetTitle_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + uint32 title_id; +}; + +struct SetTitleReply_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + char title[32]; + uint32 entity_id; +}; + + +#if 0 +// Old struct not used by Task System implementation but left for reference +struct TaskDescription_Struct { +/*000*/ uint32 activity_count; //not right. +/*004*/ uint32 taskid; +/*008*/ uint8 unk; +/*009*/ uint32 id3; +/*013*/ uint32 unknown13; +/*017*/ char name[1]; //variable length, 0 terminated +/*018*/ uint32 unknown18; +/*022*/ uint32 unknown22; +/*026*/ uint32 unknown26; +/*030*/ char desc[1]; //variable length, 0 terminated +/*031*/ uint32 reward_count; //not sure +/*035*/ uint8 unknown31; +/*036*/ uint32 unknown31; +/*040*/ uint32 unknown35; +/*044*/ uint16 unknown39; +/*046*/ char reward_link[1]; //variable length, 0 terminated +/*047*/ uint32 unknown43; //maybe crystal count? +/*051*/ +}; +#endif + +struct TaskMemberList_Struct { +/*00*/ uint32 gopher_id; +/*04*/ uint32 unknown04; +/*08*/ uint32 member_count; //1 less than the number of members +/*12*/ char list_pointer[0]; +/* list is of the form: + char member_name[1] //null terminated string + uint8 task_leader //boolean flag +*/ +}; + +#if 0 +// Struct not used by Task System implentation but left for reference (current for RoF) +struct TaskActivity_Struct { +/*000*/ uint32 TaskSequenceNumber; +/*004*/ uint32 unknown2; +/*008*/ uint32 TaskID; +/*012*/ uint32 ActivityID; +/*016*/ uint32 unknown3; +/*020*/ uint32 ActivityType; +/*024*/ uint32 Optional; +/*028*/ uint8 unknown5; +/*032*/ char Text1[1]; // Variable length - Null terminated +/*000*/ uint32 Text2Len; // Lenth of the following string +/*000*/ char Text2[1]; // Variable length - not Null terminated +/*000*/ uint32 GoalCount; +/*000*/ uint32 String1Len; // Lenth of the following string - Seen 2 +/*000*/ char String1[1]; // Numeric String - Seen "-1" - not Null terminated +/*000*/ uint32 String2Len; // Lenth of the following string - Seen 2 +/*000*/ char String2[1]; // Numeric String - Seen "-1" - not Null terminated +/*000*/ char ZoneIDString1[1]; // Numeric String - Seen "398" - Null terminated +/*000*/ uint32 unknown7; // Seen 0 +/*000*/ char Text3[1]; // Variable length - Null terminated +/*000*/ uint32 DoneCount; +/*000*/ uint8 unknown9; // Seen 1 +/*000*/ char ZoneIDString2[1]; // Numeric String - Seen "398" - Null terminated +}; + +struct TaskHistoryEntry_Struct { + uint32 task_id; + char name[1]; + uint32 completed_time; +}; +struct TaskHistory_Struct { + uint32 completed_count; + TaskHistoryEntry_Struct entries[0]; +}; +#endif + +struct AcceptNewTask_Struct { + uint32 unknown00; + uint32 task_id; //set to 0 for 'decline' + uint32 task_master_id; //entity ID +}; + +//was all 0's from client, server replied with same op, all 0's +struct CancelTask_Struct { + uint32 SequenceNumber; + uint32 unknown4; // Only seen 0x00000002 +}; + +#if 0 +// old struct, not used by Task System implementation but left for reference. +struct AvaliableTask_Struct { + uint32 task_index; //no idea, seen 0x1 + uint32 task_master_id; //entity ID + uint32 task_id; + uint32 unknown012; + uint32 activity_count; //not sure, seen 2 + char desc[1]; //variable length, 0 terminated + uint32 reward_platinum;//not sure on these + uint32 reward_gold; + uint32 reward_silver; + uint32 reward_copper; + char some_name[1]; //variable length, 0 terminated + uint8 unknown1; + uint32 unknown2; //0xFFFFFFFF + uint32 unknown3; //0xFFFFFFFF + uint32 unknown4; //seen 0x16 + uint8 unknown5; +}; +#endif + + +// Many of the Task System packets contain variable length strings, as well as variable numbers +// of records, hence splitting them into multiple structs (header, middle, trailer) etc. +// +struct AvailableTaskHeader_Struct { + uint32 TaskCount; + uint32 unknown1; + uint32 TaskGiver; +}; + +struct AvailableTaskData1_Struct { + uint32 TaskID; + uint32 unknown1; + uint32 TimeLimit; + uint32 unknown2; +}; + +struct AvailableTaskData2_Struct { + uint32 unknown1,unknown2,unknown3,unknown4; +}; + +struct AvailableTaskTrailer_Struct { + uint32 ItemCount; + uint32 unknown1, unknown2; + uint32 StartZone; +}; + +struct TaskDescriptionHeader_Struct { + uint32 SequenceNumber; // The order the tasks appear in the journal. 0 for first task, 1 for second, etc. + uint32 TaskID; + uint32 unknown2; + uint32 unknown3; + uint8 unknown4; +}; + +struct TaskDescriptionData1_Struct { + uint32 Duration; + uint32 unknown2; + uint32 StartTime; +}; + +struct TaskDescriptionData2_Struct { + uint32 RewardCount; // ?? + uint32 unknown1; + uint32 unknown2; + uint16 unknown3; + //uint8 unknown4; +}; + +struct TaskDescriptionTrailer_Struct { + //uint16 unknown1; // 0x0012 + uint32 Points; +}; + +struct TaskActivityHeader_Struct { + uint32 TaskSequenceNumber; + uint32 unknown2; // Seen 0x00000002 + uint32 TaskID; + uint32 ActivityID; + uint32 unknown3; + uint32 ActivityType; + uint32 Optional; + uint32 unknown5; +}; + +struct TaskActivityData1_Struct { + uint32 GoalCount; + uint32 unknown1; // 0xffffffff + uint32 unknown2; // 0xffffffff + uint32 ZoneID; // seen 0x36 + uint32 unknown3; +}; + +struct TaskActivityTrailer_Struct { + uint32 DoneCount; + uint32 unknown1; // Seen 1 +}; + +// The Short_Struct is sent for tasks that are hidden and act as a placeholder +struct TaskActivityShort_Struct { + uint32 TaskSequenceNumber; + uint32 unknown2; // Seen 0x00000002 + uint32 TaskID; + uint32 ActivityID; + uint32 unknown3; + uint32 ActivityType; // 0xffffffff for the short packet + uint32 unknown4; +}; + +struct TaskActivityComplete_Struct { + uint32 TaskIndex; + uint32 unknown2; // 0x00000002 + uint32 unknown3; + uint32 ActivityID; + uint32 unknown4; // 0x00000001 + uint32 unknown5; // 0x00000001 +}; + +#if 0 +// This is a dupe of the CancelTask struct +struct TaskComplete_Struct { + uint32 unknown00; // 0x00000000 + uint32 unknown04; // 0x00000002 +}; +#endif + +struct TaskHistoryRequest_Struct { + uint32 TaskIndex; // This is the sequence the task was sent in the Completed Tasks packet. +}; + +struct TaskHistoryReplyHeader_Struct { + uint32 TaskID; + uint32 ActivityCount; +}; + +struct TaskHistoryReplyData1_Struct { + uint32 ActivityType; +}; + +struct TaskHistoryReplyData2_Struct { + uint32 GoalCount; + uint32 unknown04; // 0xffffffff + uint32 unknown08; // 0xffffffff + uint32 ZoneID; + uint32 unknown16; +}; + +struct BankerChange_Struct { +/*00*/ uint32 platinum; +/*04*/ uint32 gold; +/*08*/ uint32 silver; +/*12*/ uint32 copper; +/*16*/ uint32 platinum_bank; +/*20*/ uint32 gold_bank; +/*24*/ uint32 silver_bank; +/*28*/ uint32 copper_bank; +/*32*/ +}; + +struct LeadershipExpUpdate_Struct { +/*00*/ double group_leadership_exp; +/*08*/ uint32 group_leadership_points; +/*12*/ uint32 Unknown12; +/*16*/ double raid_leadership_exp; +/*24*/ uint32 raid_leadership_points; +}; + +struct UpdateLeadershipAA_Struct { +/*00*/ uint32 ability_id; +/*04*/ uint32 new_rank; +/*08*/ uint32 unknown08; +/*12*/ +}; + +/** +* Leadership AA update +* Length: 32 Octets +* OpCode: LeadExpUpdate +*/ +struct leadExpUpdateStruct { + /*0000*/ uint32 unknown0000; // All zeroes? + /*0004*/ uint32 group_leadership_exp; // Group leadership exp value + /*0008*/ uint32 group_leadership_points; // Unspent group points + /*0012*/ uint32 unknown0012; // Type? + /*0016*/ uint32 unknown0016; // All zeroes? + /*0020*/ uint32 raid_leadership_exp; // Raid leadership exp value + /*0024*/ uint32 raid_leadership_points; // Unspent raid points + /*0028*/ uint32 unknown0028; +}; + +struct RaidGeneral_Struct { +/*00*/ uint32 action; +/*04*/ char player_name[64]; +/*68*/ uint32 unknown68; +/*72*/ char leader_name[64]; +/*136*/ uint32 parameter; +}; + +struct RaidAddMember_Struct { +/*000*/ RaidGeneral_Struct raidGen; //param = (group num-1); 0xFFFFFFFF = no group +/*136*/ uint8 _class; +/*137*/ uint8 level; +/*138*/ uint8 isGroupLeader; +/*139*/ uint8 flags[5]; //no idea if these are needed... +}; + +struct RaidAdd_Struct { +/*000*/ uint32 action; //=0 +/*004*/ char player_name[64]; //should both be the player's name +/*068*/ char leader_name[64]; +/*132*/ uint8 _class; +/*133*/ uint8 level; +/*134*/ uint8 has_group; +/*135*/ uint8 unknown135; //seems to be 0x42 or 0 +}; + +struct RaidCreate_Struct { +/*00*/ uint32 action; //=8 +/*04*/ char leader_name[64]; +/*68*/ uint32 leader_id; +}; + +struct RaidMemberInfo_Struct { +/*00*/ uint8 group_number; +/*01*/ char member_name[1]; //dyanmic length, null terminated '\0' +/*00*/ uint8 unknown00; +/*01*/ uint8 _class; +/*02*/ uint8 level; +/*03*/ uint8 is_raid_leader; +/*04*/ uint8 is_group_leader; +/*05*/ uint8 main_tank; //not sure +/*06*/ uint8 unknown06[5]; //prolly more flags +}; + +struct RaidDetails_Struct { +/*000*/ uint32 action; //=6,20 +/*004*/ char leader_name[64]; +/*068*/ uint32 unknown68[4]; +/*084*/ LeadershipAA_Struct abilities; //ranks in backwards byte order +/*128*/ uint8 unknown128[142]; +/*354*/ uint32 leader_id; +}; + +struct RaidMembers_Struct { +/*000*/ RaidDetails_Struct details; +/*358*/ uint32 member_count; //including leader +/*362*/ RaidMemberInfo_Struct members[1]; +/*...*/ RaidMemberInfo_Struct empty; //seem to have an extra member with a 0 length name on the end +}; + +struct DynamicWall_Struct { +/*00*/ char name[32]; +/*32*/ float y; +/*36*/ float x; +/*40*/ float z; +/*44*/ uint32 something; +/*48*/ uint32 unknown48; //0 +/*52*/ uint32 one_hundred; //0x64 +/*56*/ uint32 unknown56; //0 +/*60*/ uint32 something2; +/*64*/ int32 unknown64; //-1 +/*68*/ uint32 unknown68; //0 +/*72*/ uint32 unknown72; //0 +/*76*/ uint32 unknown76; //0x100 +/*80*/ +}; + +enum { //bandolier actions + BandolierCreate = 0, + BandolierRemove = 1, + BandolierSet = 2 +}; + +struct BandolierCreate_Struct { +/*00*/ uint32 action; //0 for create +/*04*/ uint8 number; +/*05*/ char name[32]; +/*37*/ uint16 unknown37; //seen 0x93FD +/*39*/ uint8 unknown39; //0 +}; + +struct BandolierDelete_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ uint8 unknown05[35]; +}; + +struct BandolierSet_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ uint8 unknown05[35]; +}; + +struct Arrow_Struct { +/*000*/ float src_y; +/*004*/ float src_x; +/*008*/ float src_z; +/*012*/ uint8 unknown012[12]; +/*024*/ float velocity; //4 is normal, 20 is quite fast +/*028*/ float launch_angle; //0-450ish, not sure the units, 140ish is straight +/*032*/ float tilt; //on the order of 125 +/*036*/ uint8 unknown036[8]; +/*044*/ float arc; +/*048*/ uint32 source_id; +/*052*/ uint32 target_id; //entity ID +/*056*/ uint32 item_id; +/*060*/ uint32 unknown060; +/*064*/ uint32 unknown064; +/*068*/ uint8 unknown068; +/*069*/ uint8 unknown069; +/*070*/ uint8 unknown070; +/*071*/ uint8 item_type; +/*072*/ uint8 skill; +/*073*/ char model_name[43]; +/*116*/ +}; + +//made a bunch of trivial structs for stuff for opcode finder to use +struct Consent_Struct { + char name[1]; //always at least a null - was 1 +}; + +struct AdventureMerchant_Struct { + uint32 unknown_flag; //seems to be 1 + uint32 entity_id; +}; + +// OP_Save - Size: 484 +struct Save_Struct { +/*000*/ uint8 unknown00[192]; +/*192*/ uint8 unknown0192[176]; +/*368*/ uint8 unknown0368[116]; +/*484*/ +}; + +struct GMToggle_Struct { + uint8 unknown0[64]; + uint32 toggle; +}; + +struct BuffFadeMsg_Struct { + uint32 color; + char msg[1]; //was 1 +/*0???*/ uint8 paddingXXX[3]; // always 0's +}; + +struct UseAA_Struct { + uint32 begin; + uint32 ability; + uint32 end; +}; + +struct AA_Ability { +/*00*/ uint32 skill_id; +/*04*/ uint32 base1; +/*08*/ uint32 base2; +/*12*/ uint32 slot; +/*16*/ +}; + +struct SendAA_Struct { +/*0000*/ uint32 id; +/*0004*/ uint8 unknown004; // uint32 unknown004; set to 1. +/*0005*/ int32 hotkey_sid; +/*0009*/ int32 hotkey_sid2; +/*0013*/ uint32 title_sid; +/*0017*/ uint32 desc_sid; +/*0021*/ uint32 class_type; +/*0025*/ uint32 cost; +/*0029*/ uint32 seq; +/*0033*/ uint32 current_level; //1s, MQ2 calls this AARankRequired +/*0037*/ uint32 unknown037; // Introduced during HoT +/*0041*/ uint32 prereq_skill; //is < 0, abs() is category # +/*0045*/ uint32 unknown045; // New Mar 21 2012 - Seen 1 +/*0049*/ uint32 prereq_minpoints; //min points in the prereq +/*0053*/ uint32 type; +/*0057*/ uint32 spellid; +/*0061*/ uint32 unknown057; // Introduced during HoT - Seen 1 - Maybe account status or enable/disable AA? +/*0065*/ uint32 spell_type; +/*0069*/ uint32 spell_refresh; +/*0073*/ uint16 classes; +/*0075*/ uint16 berserker; //seems to be 1 if its a berserker ability +/*0077*/ uint32 max_level; +/*0081*/ uint32 last_id; +/*0085*/ uint32 next_id; +/*0089*/ uint32 cost2; +/*0093*/ uint8 unknown80[7]; +/*0100*/ uint32 aa_expansion; +/*0104*/ uint32 special_category; +/*0108*/ uint32 unknown0096; +/*0112*/ uint32 total_abilities; +/*0116*/ AA_Ability abilities[0]; +}; + +struct AA_List { + SendAA_Struct* aa[0]; +}; + +struct AA_Action { +/*00*/ uint32 action; +/*04*/ uint32 ability; +/*08*/ uint32 unknown08; +/*12*/ uint32 exp_value; +/*16*/ +}; + +struct AA_Skills { //this should be removed and changed to AA_Array +/*00*/ uint32 aa_skill; // Total AAs Spent +/*04*/ uint32 aa_value; +/*08*/ uint32 unknown08; +/*12*/ +}; + +struct AAExpUpdate_Struct { +/*00*/ uint32 unknown00; //seems to be a value from AA_Action.ability +/*04*/ uint32 aapoints_unspent; +/*08*/ uint8 aaxp_percent; //% of exp that goes to AAs +/*09*/ uint8 unknown09[3]; //live doesn't always zero these, so they arnt part of aaxp_percent +/*12*/ +}; + +struct AltAdvStats_Struct { +/*000*/ uint32 experience; +/*004*/ uint16 unspent; +/*006*/ uint16 unknown006; +/*008*/ uint8 percentage; +/*009*/ uint8 unknown009[3]; +/*012*/ +}; + +struct PlayerAA_Struct { // Is this still used? + AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct AA_Values { +/*00*/ uint32 aa_skill; +/*04*/ uint32 aa_value; +/*08*/ uint32 unknown08; +/*12*/ +}; + +struct AATable_Struct { +/*00*/ uint32 aa_spent; // Total AAs Spent +/*04*/ uint32 aapoints_assigned; // Number of Assigned AA points - Seen 206 (total of the 4 fields below) +/*08*/ uint32 aa_spent_general; // Seen 63 +/*12*/ uint32 aa_spent_archetype; // Seen 40 +/*16*/ uint32 aa_spent_class; // Seen 103 +/*20*/ uint32 aa_spent_special; // Seen 0 +/*24*/ AA_Values aa_list[MAX_PP_AA_ARRAY]; +}; + +struct Weather_Struct { + uint32 val1; //generall 0x000000FF + uint32 type; //0x31=rain, 0x02=snow(i think), 0 = normal + uint32 mode; +}; + +struct ZoneInUnknown_Struct { + uint32 val1; + uint32 val2; + uint32 val3; +}; + +struct MobHealth_Struct { + uint16 entity_id; + uint8 hp; +}; + +struct AnnoyingZoneUnknown_Struct { + uint32 entity_id; + uint32 value; //always 4 +}; + +struct LoadSpellSet_Struct { + uint8 spell[12]; // 0xFFFFFFFF if no action, slot number if to unmem starting at 0 + uint32 unknown; //Seen 12 - Maybe a gem count? +}; + +struct BlockedBuffs_Struct +{ +/*000*/ int32 SpellID[BLOCKED_BUFF_COUNT]; +/*120*/ uint32 Count; +/*124*/ uint8 Pet; +/*125*/ uint8 Initialise; +/*126*/ uint16 Flags; +}; + +//Size 24 Bytes +struct WorldObfuscator_Struct { +/*000*/ uint32 var1; +/*004*/ uint32 Unknown1; +/*008*/ uint32 Unknown2; +/*012*/ uint32 Unknown3; +/*016*/ uint32 var2; +/*020*/ uint32 Unknown4; +/*024*/ +}; + +struct ExpansionInfo_Struct { +/*000*/ char Unknown000[64]; +/*064*/ uint32 Expansions; +}; + +struct ApplyPoison_Struct { + MainInvItemSlotStruct inventorySlot; + uint32 success; +}; + +struct ItemVerifyRequest_Struct { +/*000*/ ItemSlotStruct slot; +/*012*/ uint32 target; // Target Entity ID +/*016*/ +}; + +struct ItemVerifyReply_Struct { +/*000*/ ItemSlotStruct slot; +/*012*/ uint32 spell; // Spell ID to cast if different than item effect +/*016*/ uint32 target; // Target Entity ID +/*020*/ +}; + + +struct RoFSlotStruct +{ + uint8 Bank; + uint16 MainSlot; + uint16 SubSlot; +}; + +struct ItemSerializationHeader +{ +/*000*/ char unknown000[13]; // New for HoT. Looks like a string. +/*017*/ uint32 stacksize; +/*021*/ uint32 unknown004; +/*025*/ uint8 slot_type; // 0 = normal, 1 = bank, 2 = shared bank, 9 = merchant, 20 = ? +/*026*/ uint16 main_slot; +/*028*/ uint16 sub_slot; +/*030*/ uint16 unknown013; // 0xffff +/*032*/ uint32 price; +/*036*/ uint32 merchant_slot; //1 if not a merchant item +/*040*/ uint32 unknown020; //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 +/*056*/ uint32 charges; //Total Charges an item has (-1 for unlimited) +/*060*/ uint32 inst_nodrop; // 1 if the item is no drop (attuned items) +/*064*/ uint32 unknown044; // 0 +/*068*/ uint32 unknown048; // 0 +/*072*/ uint32 unknown052; // 0 +/*076*/ uint32 unknown056; // 0 +/*080*/ uint8 unknown060; // 0 +/*081*/ uint8 unknown061; // 0 - Add Evolving Item struct if this isn't set to 0? +/*082*/ uint8 unknown062; // 0 +/*083*/ uint32 unknowna1; // 0xffffffff +/*087*/ uint32 unknowna2; // 0 +/*091*/ uint8 unknown063; // 0 +/*092*/ uint32 unknowna3; // 0 +/*096*/ uint32 unknowna4; // 0xffffffff +/*100*/ uint32 unknowna5; // 0 +/*104*/ uint8 ItemClass; //0, 1, or 2 +/*105*/ +}; + +struct ItemBodyStruct +{ + uint32 id; + int32 weight; // Seen an item on Live with -0.1 weight + uint8 norent; + uint8 nodrop; + uint8 attune; + uint8 size; + uint32 slots; + uint32 price; + uint32 icon; + uint8 unknown1; + uint8 unknown2; + uint32 BenefitFlag; + uint8 tradeskills; + int8 CR; + int8 DR; + int8 PR; + int8 MR; + int8 FR; + int8 SVCorruption; + int8 AStr; + int8 ASta; + int8 AAgi; + int8 ADex; + int8 ACha; + int8 AInt; + int8 AWis; + int32 HP; + int32 Mana; + uint32 Endur; + int32 AC; + int32 regen; + int32 mana_regen; + int32 end_regen; + uint32 Classes; + uint32 Races; + uint32 Deity; + int32 SkillModValue; + int32 SkillModMax; // Max skill point modification + int32 SkillModType; + uint32 SkillModExtra; // Adds a "+value" after the mod percentage + uint32 BaneDmgRace; + uint32 BaneDmgBody; + uint32 BaneDmgRaceAmt; + int32 BaneDmgAmt; + uint8 Magic; + int32 CastTime_; + uint32 ReqLevel; + uint32 RecLevel; + uint32 RecSkill; + uint32 BardType; + int32 BardValue; + uint8 Light; + uint8 Delay; + uint8 ElemDmgType; + uint8 ElemDmgAmt; + uint8 Range; + uint32 Damage; + uint32 Color; + uint32 Prestige; // New to March 21 2012 client + uint8 ItemType; + uint32 Material; + uint32 unknown7; + uint32 EliteMaterial; + uint32 unknown_RoF3; // New to March 21 2012 client + uint32 unknown_RoF4; // New to December 10th 2012 client - NEW + float SellRate; + int32 CombatEffects; + int32 Shielding; + int32 StunResist; + int32 StrikeThrough; + int32 ExtraDmgSkill; + int32 ExtraDmgAmt; + int32 SpellShield; + int32 Avoidance; + int32 Accuracy; + uint32 CharmFileID; + uint32 FactionMod1; + int32 FactionAmt1; + uint32 FactionMod2; + int32 FactionAmt2; + uint32 FactionMod3; + int32 FactionAmt3; + uint32 FactionMod4; + int32 FactionAmt4; +}; + +struct AugSlotStruct +{ + uint32 type; + uint8 visible; + uint8 unknown; +}; + +struct ItemSecondaryBodyStruct +{ + uint32 augtype; + uint32 augrestrict; + uint32 augdistiller; // New to December 10th 2012 client - NEW + AugSlotStruct augslots[6]; + + uint32 ldonpoint_type; + uint32 ldontheme; + uint32 ldonprice; + uint32 ldonsellbackrate; + uint32 ldonsold; + + uint8 bagtype; + uint8 bagslots; + uint8 bagsize; + uint8 wreduction; + + uint8 book; + uint8 booktype; + //int32 filename; filename is either 0xffffffff/0x00000000 or the null term string ex: CREWizardNote\0 +}; + +struct ItemTertiaryBodyStruct +{ + int32 loregroup; + uint8 artifact; + uint8 summonedflag; + uint32 favor; + uint8 fvnodrop; + int32 dotshield; + int32 atk; + int32 haste; + int32 damage_shield; + uint32 guildfavor; + uint32 augdistil; + int32 unknown3; // 0xffffffff + uint32 unknown4; + uint8 no_pet; + uint8 unknown5; + + uint8 potion_belt_enabled; + uint32 potion_belt_slots; + + uint32 stacksize; + uint8 no_transfer; + uint16 expendablearrow; + + uint32 unknown8; + uint32 unknown9; + uint32 unknown10; + uint32 unknown11; + uint8 unknown12; + uint8 unknown13; + uint8 unknown14; +}; + +struct ClickEffectStruct +{ + int32 effect; + uint8 level2; + uint32 type; + uint8 level; + int32 max_charges; + int32 cast_time; + uint32 recast; + int32 recast_type; + uint32 clickunk5; + //uint8 effect_string; + //int32 clickunk7; +}; + +struct ProcEffectStruct +{ + uint32 effect; + uint8 level2; + uint32 type; + uint8 level; + uint32 unknown1; // poison? + uint32 unknown2; + uint32 unknown3; + uint32 unknown4; + uint32 procrate; + //uint8 effect_string; + //uint32 unknown5; +}; + +struct WornEffectStruct //worn, focus and scroll effect +{ + uint32 effect; + uint8 level2; + uint32 type; + uint8 level; + uint32 unknown1; + uint32 unknown2; + uint32 unknown3; + uint32 unknown4; + uint32 unknown5; + //uint8 effect_string; + //uint32 unknown6; +}; + +struct ItemQuaternaryBodyStruct +{ + uint32 scriptfileid; + uint8 quest_item; + uint32 Power; // Enables "Power" percentage field used by Power Sources + uint32 Purity; + uint8 unknown16; // RoF + uint32 BackstabDmg; + uint32 DSMitigation; + int32 HeroicStr; + int32 HeroicInt; + int32 HeroicWis; + int32 HeroicAgi; + int32 HeroicDex; + int32 HeroicSta; + int32 HeroicCha; + int32 HeroicMR; + int32 HeroicFR; + int32 HeroicCR; + int32 HeroicDR; + int32 HeroicPR; + int32 HeroicSVCorrup; + int32 HealAmt; + int32 SpellDmg; + int32 clairvoyance; + uint8 unknown18; //Power Source Capacity or evolve filename? + uint32 evolve_string; // Some String, but being evolution related is just a guess + uint8 unknown19; + uint32 unknown20; // Bard Stuff? + //uint32 unknown21; + uint8 unknown22; + uint32 unknown23; + uint32 unknown24; + uint32 unknown25; + float unknown26; + float unknown27; + uint32 unknown_RoF6; // 0 New to March 21 2012 client + uint32 unknown28; // 0xffffffff + uint16 unknown29; + uint32 unknown30; // 0xffffffff + uint16 unknown31; + uint32 unknown32; + float unknown33; + uint32 unknown34; + uint32 unknown35; + uint32 unknown36; + uint32 unknown37; + uint32 unknown_RoF7; + uint32 unknown_RoF8; + uint8 unknown38; // 0 + uint8 unknown39; // 1 + uint32 subitem_count; +}; + +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*/ +}; + +struct VeteranRewardItem +{ +/*000*/ uint32 item_id; +/*004*/ uint32 charges; +/*008*/ char item_name[64]; +}; + +struct VeteranReward +{ +/*000*/ uint32 claim_id; +/*004*/ uint32 number_available; +/*008*/ uint32 claim_count; +/*012*/ VeteranRewardItem items[8]; +}; + +struct ExpeditionEntryHeader_Struct +{ +/*000*/ uint32 unknown000; +/*000*/ uint32 number_of_entries; +}; + +struct ExpeditionJoinPrompt_Struct +{ +/*000*/ uint32 clientid; +/*004*/ uint32 unknown004; +/*008*/ char player_name[64]; +/*072*/ char expedition_name[64]; +}; + +struct ExpeditionExpireWarning +{ +/*000*/ uint32 clientid; +/*004*/ uint32 unknown004; +/*008*/ uint32 minutes_remaining; +}; + +struct ExpeditionInfo_Struct +{ +/*000*/ uint32 clientid; +/*004*/ uint32 unknown004; +/*008*/ uint32 unknown008; +/*012*/ uint32 max_players; +/*016*/ char expedition_name[128]; +/*142*/ char leader_name[64]; +}; + +struct ExpeditionCompassEntry_Struct +{ +/*000*/ float unknown000; //seen *((uint32*)) = 1584791871 +/*004*/ uint32 enabled; //guess +/*008*/ uint32 unknown008; //seen 1019 +/*012*/ float y; +/*016*/ float x; +/*020*/ float z; +}; + +struct ExpeditionCompass_Struct +{ +/*000*/ uint32 clientid; +/*004*/ uint32 count; +/*008*/ ExpeditionCompassEntry_Struct entries[0]; +}; + +struct MaxCharacters_Struct +{ +/*000*/ uint32 max_chars; // Seen 4 on Silver Account (4 chars max) +/*004*/ uint32 unknown004; // Seen 0 +/*008*/ uint32 unknown008; // Seen 0 +}; + +// Used by MercenaryListEntry_Struct +struct MercenaryStance_Struct { +/*0000*/ uint32 StanceIndex; // Index of this stance (sometimes reverse reverse order - 3, 2, 1, 0 for 4 stances etc) +/*0004*/ uint32 Stance; // From dbstr_us.txt - 1^24^Passive^0, 2^24^Balanced^0, etc +}; +// Used by MercenaryMerchantList_Struct +struct MercenaryListEntry_Struct { +/*0000*/ uint32 MercID; // ID unique to each type of mercenary (probably a DB id) +/*0004*/ uint32 MercType; // From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) +/*0008*/ uint32 MercSubType; // From dbstr_us.txt - 330020105^23^Race: Guktan
Type: Healer
Confidence: High
Proficiency: Apprentice, Tier V... +/*0012*/ uint32 PurchaseCost; // Purchase Cost (in gold) +/*0016*/ uint32 UpkeepCost; // Upkeep Cost (in gold) +/*0020*/ uint32 Status; // Required Account Status (Free = 0, Silver = 1, Gold = 2) at merchants - Seen 0 (suspended) or 1 (unsuspended) on hired mercs ? +/*0024*/ uint32 AltCurrencyCost; // Alternate Currency Purchase Cost? (all seen costs show N/A Bayle Mark) - Seen 0 +/*0028*/ uint32 AltCurrencyUpkeep; // Alternate Currency Upkeep Cost? (all seen costs show 1 Bayle Mark) - Seen 1 +/*0032*/ uint32 AltCurrencyType; // Alternate Currency Type? - 19^17^Bayle Mark^0 - Seen 19 +/*0036*/ uint8 MercUnk01; // Unknown (always see 0) +/*0037*/ int32 TimeLeft; // Unknown (always see -1 at merchant) - Seen 900000 (15 minutes in ms for newly hired merc) +/*0041*/ uint32 MerchantSlot; // Merchant Slot? Increments, but not always by 1 - May be for Merc Window Options (Seen 5, 36, 1 for active mercs)? +/*0045*/ uint32 MercUnk02; // Unknown (normally see 1, but sometimes 2 or 0) +/*0049*/ uint32 StanceCount; // Iterations of MercenaryStance_Struct - Normally 2 to 4 seen +/*0053*/ int32 MercUnk03; // Unknown (always 0 at merchant) - Seen on active merc: 93 a4 03 77, b8 ed 2f 26, 88 d5 8b c3, and 93 a4 ad 77 +/*0057*/ uint8 MercUnk04; // Seen 1 +/*0058*/ char MercName[1]; // Null Terminated Mercenary Name (00 at merchants) +/*0000*/ MercenaryStance_Struct Stances[1]; // Count Varies - From dbstr_us.txt - 1^24^Passive^0, 2^24^Balanced^0, etc +}; + +// Sent by the server when browsing the Mercenary Merchant +struct MercenaryMerchantList_Struct { +/*0000*/ uint32 MercTypeCount; // Number of Merc Types to follow +/*0004*/ uint32 MercTypes[1]; // Count varies, but hard set to 3 max for now - From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) +/*0016*/ uint32 MercCount; // Number of MercenaryInfo_Struct to follow +/*0020*/ MercenaryListEntry_Struct Mercs[0]; // Data for individual mercenaries in the Merchant List +}; + +// OP_MercenaryDataRequest +// Right clicking merchant - shop request +struct MercenaryMerchantShopRequest_Struct { +/*0000*/ uint32 MercMerchantID; // Entity ID of the Mercenary Merchant +/*0004*/ +}; + +// Used by MercenaryDataUpdate_Struct +struct MercenaryData_Struct { +/*0000*/ uint32 MercID; // ID unique to each type of mercenary (probably a DB id) - (if 1, do not send MercenaryData_Struct - No merc hired) +/*0004*/ uint32 MercType; // From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) +/*0008*/ uint32 MercSubType; // From dbstr_us.txt - 330020105^23^Race: Guktan
Type: Healer
Confidence: High
Proficiency: Apprentice, Tier V... +/*0012*/ uint32 PurchaseCost; // Purchase Cost (in gold) +/*0016*/ uint32 UpkeepCost; // Upkeep Cost (in gold) +/*0020*/ uint32 Status; // Required Account Status (Free = 0, Silver = 1, Gold = 2) at merchants - Seen 0 (suspended) or 1 (unsuspended) on hired mercs ? +/*0024*/ uint32 AltCurrencyCost; // Alternate Currency Purchase Cost? (all seen costs show N/A Bayle Mark) - Seen 0 +/*0028*/ uint32 AltCurrencyUpkeep; // Alternate Currency Upkeep Cost? (all seen costs show 1 Bayle Mark) - Seen 1 +/*0032*/ uint32 AltCurrencyType; // Alternate Currency Type? - 19^17^Bayle Mark^0 - Seen 19 +/*0036*/ uint8 MercUnk01; // Unknown (always see 0) +/*0037*/ int32 TimeLeft; // Unknown (always see -1 at merchant) - Seen 900000 (15 minutes in ms for newly hired merc) +/*0041*/ uint32 MerchantSlot; // Merchant Slot? Increments, but not always by 1 - May be for Merc Window Options (Seen 5, 36, 1 for active mercs)? +/*0045*/ uint32 MercUnk02; // Unknown (normally see 1, but sometimes 2 or 0) +/*0049*/ uint32 StanceCount; // Iterations of MercenaryStance_Struct - Normally 2 to 4 seen +/*0053*/ int32 MercUnk03; // Unknown (always 0 at merchant) - Seen on active merc: 93 a4 03 77, b8 ed 2f 26, 88 d5 8b c3, and 93 a4 ad 77 +/*0057*/ uint8 MercUnk04; // Seen 1 +/*0058*/ char MercName[1]; // Null Terminated Mercenary Name (00 at merchants) +/*0000*/ MercenaryStance_Struct Stances[1]; // Count Varies, but hard set to 2 for now - From dbstr_us.txt - 1^24^Passive^0, 2^24^Balanced^0, etc (1 to 9 as of April 2012) +/*0000*/ uint32 MercUnk05; // Seen 1 - Extra Merc Data field that differs from MercenaryListEntry_Struct +// MercUnk05 may be a field that is at the end of the packet only, even if multiple mercs are listed (haven't seen examples of multiple mercs owned at once) +}; + +// Should be named OP_MercenaryDataResponse, but the current opcode using that name should be renamed first +// Size varies if mercenary is hired or if browsing Mercenary Merchant +// This may also be the response for Client->Server 0x0327 (size 0) packet On Live as of April 2 2012 +struct MercenaryDataUpdate_Struct { +/*0000*/ int32 MercStatus; // Seen 0 with merc and -1 with no merc hired +/*0004*/ uint32 MercCount; // Seen 1 with 1 merc hired and 0 with no merc hired +/*0008*/ MercenaryData_Struct MercData[0]; // Data for individual mercenaries in the Merchant List +}; + +// Size 12 and sent on Zone-In if no mercenary is currently hired and when merc is dismissed +// (Same packet as MercAssign_Struct?) +struct NoMercenaryHired_Struct { +/*0000*/ int32 MercStatus; // Seen -1 with no merc hired +/*0004*/ uint32 MercCount; // Seen 0 with no merc hired +/*0008*/ uint32 MercID; // Seen 1 when no merc is hired - ID unique to each type of mercenary +/*0012*/ +}; + +// OP_MercenaryAssign (Same packet as NoMercenaryHired_Struct?) +// Not actually Merc related - This is actually a weapon equp packet +struct MercenaryAssign_Struct { +/*0000*/ uint32 MercEntityID; // Seen 0 (no merc spawned) or 615843841 and 22779137 +/*0004*/ uint32 MercUnk01; // +/*0008*/ uint32 MercUnk02; // +/*0012*/ +}; + +// OP_MercenaryTimer +// Sent on Zone-In, or after Dismissing, Suspending, or Unsuspending Mercs +struct MercenaryStatus_Struct { +/*0000*/ uint32 MercEntityID; // Seen 0 (no merc spawned) or 615843841 and 22779137 +/*0004*/ uint32 UpdateInterval; // Seen 900000 - Matches from 0x6537 packet (15 minutes in ms?) +/*0008*/ uint32 MercUnk01; // Seen 180000 - 3 minutes in milleseconds? Maybe next update interval? +/*0012*/ uint32 MercState; // Seen 5 (normal) or 1 (suspended) +/*0016*/ uint32 SuspendedTime; // Seen 0 (not suspended) or c9 c2 64 4f (suspended on Sat Mar 17 11:58:49 2012) - Unix Timestamp +/*0020*/ +}; + +// Sent from the client when using the Mercenary Window +struct MercenaryCommand_Struct { +/*0000*/ uint32 MercCommand; // Seen 0 (zone in with no merc or suspended), 1 (dismiss merc), 5 (normal state), 36 (zone in with merc) +/*0004*/ int32 Option; // Seen -1 (zone in with no merc), 0 (setting to passive stance), 1 (normal or setting to balanced stance) +/*0008*/ +}; + +// Requesting to suspend or unsuspend merc +struct SuspendMercenary_Struct { +/*0000*/ uint8 SuspendMerc; // Seen 30 (48) for suspending or unsuspending +/*0001*/ +}; + +// Response to suspend merc with timestamp +struct SuspendMercenaryResponse_Struct { +/*0000*/ uint32 SuspendTime; // Unix Timestamp - Seen a9 11 78 4f +/*0004*/ +}; + +// Sent by client when requesting to view Mercenary info or Hire a Mercenary +struct MercenaryMerchantRequest_Struct { +/*0000*/ uint32 MercID; // Seen 399 and 400 for merc ID +/*0004*/ uint32 MercUnk01; // Seen 1 +/*0008*/ uint32 MercMerchantID; // Entity ID for Mercenary Merchant +/*0012*/ uint32 MercUnk02; // Seen 65302016 (00 6e e4 03) - (probably actually individual uint8 fields), but seen as DWORD in Seeds client. +/*0016*/ +}; + +// Sent by Server in response to requesting to view Mercenary info or Hire a Mercenary +struct MercenaryMerchantResponse_Struct { +/*0000*/ uint32 ResponseType; +/*0004*/ +}; + + }; //end namespace structs +}; //end namespace RoF + +#endif /*RoF_STRUCTS_H_*/ diff --git a/common/patches/SSDeclare.h b/common/patches/SSDeclare.h new file mode 100644 index 000000000..b45c4d722 --- /dev/null +++ b/common/patches/SSDeclare.h @@ -0,0 +1,6 @@ + + +#define E(x) static void Encode_##x(EQApplicationPacket **p, EQStream *dest, bool ack_req); +#define D(x) static void Decode_##x(EQApplicationPacket *p); + + diff --git a/common/patches/SSDefine.h b/common/patches/SSDefine.h new file mode 100644 index 000000000..497fdc329 --- /dev/null +++ b/common/patches/SSDefine.h @@ -0,0 +1,155 @@ + +#define ENCODE(x) void Strategy::Encode_##x(EQApplicationPacket **p, EQStream *dest, bool ack_req) +#define DECODE(x) void Strategy::Decode_##x(EQApplicationPacket *__packet) + +#define StructDist(in, f1, f2) (uint32(&in->f2)-uint32(&in->f1)) +#define CopyBlock(to, field, from, field1, field2) \ + memcpy((void *) &to->field, (const void *) from->field1, StructDist(from, field1, field2)); +#define CopyLen(to, field, from, field1, len) \ + memcpy((void *) &to->field, (const void *) from->field1, len); + + +/* + * + * for encoders + * + */ +//more complex operations and variable length packets +#define FASTQUEUE(packet) dest->FastQueuePacket(&packet, ack_req); +#define TAKE(packet_name) \ + EQApplicationPacket *packet_name = *p; \ + *p = NULL; + +//simple buffer-to-buffer movement for fixed length packets +//the eq packet is mapped into `eq`, the emu packet into `emu` +#define SETUP_DIRECT_ENCODE(emu_struct, eq_struct) \ + SETUP_VAR_ENCODE(emu_struct); \ + ALLOC_VAR_ENCODE(eq_struct, sizeof(eq_struct)); + +//like a direct encode, but for variable length packets (two stage) +#define SETUP_VAR_ENCODE(emu_struct) \ + EQApplicationPacket *__packet = *p; \ + *p = NULL; \ + unsigned char *__emu_buffer = __packet->pBuffer; \ + emu_struct *emu = (emu_struct *) __emu_buffer; \ + uint32 __i = 0; \ + __i++; /* to shut up compiler */ + +#define ALLOC_VAR_ENCODE(eq_struct, len) \ + __packet->pBuffer = new unsigned char[len]; \ + __packet->size = len; \ + memset(__packet->pBuffer, 0, len); \ + eq_struct *eq = (eq_struct *) __packet->pBuffer; \ + +//a shorter assignment for direct mode +#undef OUT +#define OUT(x) eq->x = emu->x; +#define OUT_str(x) \ + strncpy(eq->x, emu->x, sizeof(eq->x)); \ + eq->x[sizeof(eq->x)-1] = '\0'; +#define OUT_array(x, n) \ + for(__i = 0; __i < n; __i++) \ + eq->x[__i] = emu->x[__i]; + +//call before any premature returns in an encoder using SETUP_DIRECT_ENCODE +#define FAIL_ENCODE() \ + delete[] __emu_buffer; \ + delete __packet; + +//call to finish an encoder using SETUP_DIRECT_ENCODE +#define FINISH_ENCODE() \ + delete[] __emu_buffer; \ + dest->FastQueuePacket(&__packet, ack_req); + +//check length of packet before decoding. Call before setup. +#define ENCODE_LENGTH_EXACT(struct_) \ + if((*p)->size != sizeof(struct_)) { \ + _log(NET__STRUCTS, "Wrong size on outbound %s (" #struct_ "): Got %d, expected %d", opcodes->EmuToName((*p)->GetOpcode()), (*p)->size, sizeof(struct_)); \ + _hex(NET__STRUCT_HEX, (*p)->pBuffer, (*p)->size); \ + delete *p; \ + *p = NULL; \ + return; \ + } +#define ENCODE_LENGTH_ATLEAST(struct_) \ + if((*p)->size < sizeof(struct_)) { \ + _log(NET__STRUCTS, "Wrong size on outbound %s (" #struct_ "): Got %d, expected at least %d", opcodes->EmuToName((*p)->GetOpcode()), (*p)->size, sizeof(struct_)); \ + _hex(NET__STRUCT_HEX, (*p)->pBuffer, (*p)->size); \ + delete *p; \ + *p = NULL; \ + return; \ + } + +//forward this opcode to another encoder +#define ENCODE_FORWARD(other_op) \ + Encode_##other_op(p, dest, ack_req); + +//destroy the packet, it is not sent to this client version +#define EAT_ENCODE(op) \ + ENCODE(op) { \ + delete *p; \ + *p = NULL; \ + } + + + +/* + * + * for decoders: + * + */ + +//simple buffer-to-buffer movement for fixed length packets +//the eq packet is mapped into `eq`, the emu packet into `emu` +#define SETUP_DIRECT_DECODE(emu_struct, eq_struct) \ + unsigned char *__eq_buffer = __packet->pBuffer; \ + __packet->size = sizeof(emu_struct); \ + __packet->pBuffer = new unsigned char[__packet->size]; \ + emu_struct *emu = (emu_struct *) __packet->pBuffer; \ + eq_struct *eq = (eq_struct *) __eq_buffer; + +#define MEMSET_IN(emu_struct) \ + memset(__packet->pBuffer, 0, sizeof(emu_struct)); + +//a shorter assignment for direct mode +#undef IN +#define IN(x) emu->x = eq->x; + +//call before any premature returns in an encoder using SETUP_DIRECT_DECODE +#define FAIL_DIRECT_DECODE() \ + delete[] __eq_buffer; \ + p->SetOpcode(OP_Unknown); + +//call to finish an encoder using SETUP_DIRECT_DECODE +#define FINISH_DIRECT_DECODE() \ + delete[] __eq_buffer; + +//check length of packet before decoding. Call before setup. +#define DECODE_LENGTH_EXACT(struct_) \ + if(__packet->size != sizeof(struct_)) { \ + __packet->SetOpcode(OP_Unknown); /* invalidate the packet */ \ + _log(NET__STRUCTS, "Wrong size on incoming %s (" #struct_ "): Got %d, expected %d", opcodes->EmuToName(__packet->GetOpcode()), __packet->size, sizeof(struct_)); \ + _hex(NET__STRUCT_HEX, __packet->pBuffer, __packet->size); \ + return; \ + } +#define DECODE_LENGTH_ATLEAST(struct_) \ + if(__packet->size < sizeof(struct_)) { \ + __packet->SetOpcode(OP_Unknown); /* invalidate the packet */ \ + _log(NET__STRUCTS, "Wrong size on incoming %s (" #struct_ "): Got %d, expected at least %d", opcodes->EmuToName(__packet->GetOpcode()), __packet->size, sizeof(struct_)); \ + _hex(NET__STRUCT_HEX, __packet->pBuffer, __packet->size); \ + return; \ + } + +//forward this opcode to another decoder +#define DECODE_FORWARD(other_op) \ + Decode_##other_op(__packet); + + + + + + + + + + + diff --git a/common/patches/SSRegister.h b/common/patches/SSRegister.h new file mode 100644 index 000000000..b9ada774c --- /dev/null +++ b/common/patches/SSRegister.h @@ -0,0 +1,3 @@ + +#define E(x) encoders[x] = Encode_##x; +#define D(x) decoders[x] = Decode_##x; diff --git a/common/patches/SoD.cpp b/common/patches/SoD.cpp new file mode 100644 index 000000000..151099a47 --- /dev/null +++ b/common/patches/SoD.cpp @@ -0,0 +1,3536 @@ + +#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 "../Item.h" +#include "SoD_structs.h" +#include "../rulesys.h" + +#include +#include + +namespace SoD { + +static const char *name = "SoD"; +static OpcodeManager *opcodes = NULL; +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 == NULL) { + //TODO: get this file name from the config file + 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; + string pname; + + //register our world signature. + pname = 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 = 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 != NULL) { + //TODO: get this file name from the config file + 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); +} + + + +#include "SSDefine.h" + + +// Converts Titanium Slot IDs to SoD Slot IDs for use in Encodes +static inline uint32 TitaniumToSoDSlot(uint32 TitaniumSlot) { + uint32 SoDSlot = 0; + + if(TitaniumSlot >= 21 && TitaniumSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots + { + SoDSlot = TitaniumSlot + 1; + } + else if(TitaniumSlot >= 251 && TitaniumSlot <= 340) // Bag Slots for Normal Inventory and Cursor + { + SoDSlot = TitaniumSlot + 11; + } + else if(TitaniumSlot >= 2031 && TitaniumSlot <= 2270) // Bank Bag Slots + { + SoDSlot = TitaniumSlot + 1; + } + else if(TitaniumSlot >= 2531 && TitaniumSlot <= 2550) // Shared Bank Bag Slots + { + SoDSlot = TitaniumSlot + 1; + } + else if(TitaniumSlot == 9999) //Unused slot ID to give a place to save Power Slot + { + SoDSlot = 21; + } + else + { + SoDSlot = TitaniumSlot; + } + + return SoDSlot; +} + +// Converts SoD Slot IDs to Titanium Slot IDs for use in Decodes +static inline uint32 SoDToTitaniumSlot(uint32 SoDSlot) { + uint32 TitaniumSlot = 0; + + if(SoDSlot >= 22 && SoDSlot <= 54) // Cursor/Ammo/Power Source and Normal Inventory Slots + { + TitaniumSlot = SoDSlot - 1; + } + else if(SoDSlot >= 262 && SoDSlot <= 351) // Bag Slots for Normal Inventory and Cursor + { + TitaniumSlot = SoDSlot - 11; + } + else if(SoDSlot >= 2032 && SoDSlot <= 2271) // Bank Bag Slots + { + TitaniumSlot = SoDSlot - 1; + } + else if(SoDSlot >= 2532 && SoDSlot <= 2551) // Shared Bank Bag Slots + { + TitaniumSlot = SoDSlot - 1; + } + else if(SoDSlot == 21) + { + TitaniumSlot = 9999; //Unused slot ID to give a place to save Power Slot + } + else + { + TitaniumSlot = SoDSlot; + } + + return TitaniumSlot; +} + + +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 = NULL; + + 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 < MAX_MATERIALS; 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); +// 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 = 16; r < 48; r++) { + eq->unknown521[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 = NULL; + + 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 = NULL; + + 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 = NULL; + + 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 = NULL; + + 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 = NULL; + + //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[MATERIAL_PRIMARY]); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MATERIAL_SECONDARY]); + 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 = NULL; + + //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].GradeCountEntry); + } + } + 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 = NULL; + + //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 = NULL; + + 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 = NULL; + + 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 = NULL; + + //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 = TitaniumToSoDSlot(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 = TitaniumToSoDSlot(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 = TitaniumToSoDSlot(emu->from_slot); + eq->to_slot = TitaniumToSoDSlot(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 = TitaniumToSoDSlot(emu->from_slot); + eq->to_slot = TitaniumToSoDSlot(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 = TitaniumToSoDSlot(emu->slot); + OUT(spell); + OUT(target); + + FINISH_ENCODE(); +} + +ENCODE(OP_Trader) { + + if((*p)->size != sizeof(TraderBuy_Struct)) { + EQApplicationPacket *in = *p; + *p = NULL; + 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 = TitaniumToSoDSlot(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 = TitaniumToSoDSlot(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); + ss.clear(); + + 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 = TitaniumToSoDSlot(emu->slot); + OUT(charges); + OUT(sell_price); + + FINISH_ENCODE(); +} + +ENCODE(OP_RaidUpdate) +{ + EQApplicationPacket *inapp = *p; + *p = NULL; + 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)); + + 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[] __emu_buffer; +} + +ENCODE(OP_WhoAllResponse) +{ + EQApplicationPacket *in = *p; + *p = NULL; + + 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 = TitaniumToSoDSlot(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 = SoDToTitaniumSlot(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 = SoDToTitaniumSlot(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 = SoDToTitaniumSlot(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 = SoDToTitaniumSlot(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 = SoDToTitaniumSlot(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 = SoDToTitaniumSlot(eq->from_slot); + emu->to_slot = SoDToTitaniumSlot(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 = SoDToTitaniumSlot(eq->from_slot); + emu->to_slot = SoDToTitaniumSlot(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 = SoDToTitaniumSlot(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 = SoDToTitaniumSlot(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 = SoDToTitaniumSlot(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 = SoDToTitaniumSlot(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 = SoDToTitaniumSlot(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); + ss.clear(); + + 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 = TitaniumToSoDSlot(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 = 0; x < 10; ++x) { + + SubSerializations[x] = NULL; + + const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); + + if(subitem) { + + int SubSlotNumber; + + iqbs.subitem_count++; + + if(slot_id_in >= 22 && slot_id_in < 30) + SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); + else if(slot_id_in >= 2000 && slot_id_in <= 2023) + SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); + else if(slot_id_in >= 2500 && slot_id_in <= 2501) + SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); + else + SubSlotNumber = slot_id_in; // ??????? + + 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 = SoDToTitaniumSlot(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 = SoDToTitaniumSlot(eq->slot_id); + IN(charges); + IN(cost); + FINISH_DIRECT_DECODE(); +} + + +} //end namespace SoD diff --git a/common/patches/SoD.h b/common/patches/SoD.h new file mode 100644 index 000000000..ffcee7b25 --- /dev/null +++ b/common/patches/SoD.h @@ -0,0 +1,36 @@ +#ifndef SoD_H_ +#define SoD_H_ + +#include "../StructStrategy.h" + +class EQStreamIdentifier; + +namespace SoD { + + //these are the only public member of this namespace. + extern void Register(EQStreamIdentifier &into); + extern void Reload(); + + + + //you should not directly access anything below.. + //I just dont feel like making a seperate header for it. + + class Strategy : public StructStrategy { + public: + Strategy(); + + protected: + + virtual std::string Describe() const; + + //magic macro to declare our opcode processors + #include "SSDeclare.h" + #include "SoD_ops.h" + }; + +}; + + + +#endif /*SoD_H_*/ diff --git a/common/patches/SoD_itemfields.h b/common/patches/SoD_itemfields.h new file mode 100644 index 000000000..ccba333b5 --- /dev/null +++ b/common/patches/SoD_itemfields.h @@ -0,0 +1,439 @@ +/* + + +These fields must be in the order of how they are serialized! + + + +*/ +#define NEW_TRY +#ifdef NEW_TRY +//* 000 */ I(ItemClass) // Leave this one off on purpose +/* 001 */ S(Name) +/* 002 */ S(Lore) +//* 003 */ C("")//lorefile - Newly Added - Field is Null +/* 004 */ S(IDFile) +/* 005 */ I(ID) +/* 006 */ I(Weight) +/* 007 */ I(NoRent) +/* 008 */ I(NoDrop) +/* 009 */ I(Size) +/* 010 */ I(Slots) +/* 011 */ I(Price) +/* 012 */ I(Icon) +/* 013 */ C("0")//UNK013 +/* 014 */ C("0")//UNK014 +/* 015 */ I(BenefitFlag) +/* 016 */ I(Tradeskills) +/* 017 */ I(CR) +/* 018 */ I(DR) +/* 019 */ I(PR) +/* 020 */ I(MR) +/* 021 */ I(FR) +/* 022 */ C("0")//svcorruption - Newly Added +/* 023 */ I(AStr) +/* 024 */ I(ASta) +/* 025 */ I(AAgi) +/* 026 */ I(ADex) +/* 027 */ I(ACha) +/* 028 */ I(AInt) +/* 029 */ I(AWis) +/* 030 */ I(HP) +/* 031 */ I(Mana) +/* 032 */ I(Endur) //endur - Relocated +/* 033 */ I(AC) +/* 034 */ I(Classes)//classes - Relocated +/* 035 */ I(Races)//races - Relocated +/* 036 */ I(Deity) +/* 037 */ I(SkillModValue) +/* 038 */ C("0")//UNK038 - Default is 0 +/* 039 */ I(SkillModType) +/* 040 */ I(BaneDmgRace) +/* 041 */ I(BaneDmgBody)//banedmgbody - Relocated +/* 042 */ I(BaneDmgRaceAmt)//banedmgraceamt - Relocated +/* 043 */ I(BaneDmgAmt)//banedmgamt - Relocated +/* 044 */ I(Magic) +/* 045 */ I(CastTime_) +/* 046 */ I(ReqLevel) +/* 047 */ I(RecLevel)//reclevel - Relocated +/* 048 */ I(RecSkill)//recskill - Relocated +/* 049 */ I(BardType) +/* 050 */ I(BardValue) +/* 051 */ I(Light) +/* 052 */ I(Delay) +/* 053 */ I(ElemDmgType) +/* 054 */ I(ElemDmgAmt) +/* 055 */ I(Range) +/* 056 */ I(Damage) +/* 057 */ I(Color) +/* 058 */ I(ItemType) +/* 059 */ I(Material) +/* 060 */ C("0")//UNK060 - Default is 0 +/* 061 */ C("0")//UNK061 - Default is 0 +/* 062 */ F(SellRate) +/* 063 */ I(CombatEffects) +/* 064 */ I(Shielding) +/* 065 */ I(StunResist) +/* 066 */ I(StrikeThrough) +/* 067 */ I(ExtraDmgSkill) +/* 068 */ I(ExtraDmgAmt) +/* 069 */ I(SpellShield) +/* 070 */ I(Avoidance) +/* 071 */ I(Accuracy) +/* 072 */ I(CharmFileID) +/* 073 */ I(FactionMod1)//Swapped these so Faction Amt comes after each Faction Mod +/* 074 */ I(FactionAmt1)//Swapped these so Faction Amt comes after each Faction Mod +/* 075 */ I(FactionMod2)//Swapped these so Faction Amt comes after each Faction Mod +/* 076 */ I(FactionAmt2)//Swapped these so Faction Amt comes after each Faction Mod +/* 077 */ I(FactionMod3)//Swapped these so Faction Amt comes after each Faction Mod +/* 078 */ I(FactionAmt3)//Swapped these so Faction Amt comes after each Faction Mod +/* 079 */ I(FactionMod4)//Swapped these so Faction Amt comes after each Faction Mod +/* 080 */ I(FactionAmt4)//Swapped these so Faction Amt comes after each Faction Mod +/* 081 */ S(CharmFile) +/* 082 */ I(AugType) +/* 083 */ I(AugRestrict)//augrestrict - Relocated +/* 084 */ I(AugDistiller)//augdistiller - Relocated +/* 085 */ I(AugSlotType[0]) +/* 086 */ I(AugSlotVisible[0])//augslot1visible - Default 1 +/* 087 */ C("0")//augslot1unk2 - Newly Added - Default 0 +/* 088 */ I(AugSlotType[1]) +/* 089 */ I(AugSlotVisible[1]) +/* 090 */ C("0")//augslot2unk2 - Newly Added +/* 091 */ I(AugSlotType[2]) +/* 092 */ I(AugSlotVisible[2]) +/* 093 */ C("0")//augslot3unk2 - Newly Added +/* 094 */ I(AugSlotType[3]) +/* 095 */ I(AugSlotVisible[3]) +/* 096 */ C("0")//augslot4unk2 - Newly Added +/* 097 */ I(AugSlotType[4]) +/* 098 */ I(AugSlotVisible[4]) +/* 099 */ C("0")//augslot5unk2 - Newly Added +/* 100 */ I(PointType)//pointtype - Relocated +/* 101 */ I(LDoNTheme) +/* 102 */ I(LDoNPrice) +/* 103 */ C("70")//UNK098 - Newly Added - Default 70, but some are set to 0 +/* 104 */ I(LDoNSold) +/* 105 */ I(BagType) +/* 106 */ I(BagSlots) +/* 107 */ I(BagSize) +/* 108 */ I(BagWR) +/* 109 */ I(Book) +/* 110 */ I(BookType) +/* 111 */ S(Filename) +/* 112 */ I(LoreGroup) +/* 113 */ I(ArtifactFlag) +/* 114 */ C("0")//I(PendingLoreFlag)?//UNK109 - Default 0, but a few are 1 +/* 115 */ I(Favor) +/* 116 */ I(GuildFavor)//guildfavor - Relocated +/* 117 */ I(FVNoDrop) +/* 118 */ I(DotShielding) +/* 119 */ I(Attack) +/* 120 */ I(Regen) +/* 121 */ I(ManaRegen) +/* 122 */ I(EnduranceRegen) +/* 123 */ I(Haste) +/* 124 */ I(DamageShield) +/* 125 */ C("-1") //UNK120 - Default is -1 +/* 126 */ C("0") //UNK121 - Default is 0 +/* 127 */ I(Attuneable) +/* 128 */ I(NoPet) +/* 129 */ C("0") //UNK124 - Default 0, but a few are 1 +/* 130 */ I(PotionBelt) +/* 131 */ C("0") //potionbeltslots - Default 0, but a few are 1 +/* 132 */ I(StackSize) +/* 133 */ I(NoTransfer) +/* 134 */ I(Stackable)//UNK129 - Default is 0, but some are much higher +/* 135 */ I(QuestItemFlag)//questitemflag - Default is 0 (off), flag on = 1 +/* 136 */ C("0")//UNK131 - Default is 0, but there is an item set to 1 +/* 137 */ C("0")//UNK132 - Default is 0? 0000000000000000000? +/* 138 */ I(Click.Effect) +/* 139 */ I(Click.Type) +/* 140 */ I(Click.Level2) +/* 141 */ I(Click.Level) +/* 142 */ I(MaxCharges)//maxcharges - Relocated +/* 143 */ I(CastTime_)//casttime - Relocated - Note Duplicate Entries for CastTime_ and none for CastTime +/* 144 */ I(RecastDelay)//recastdelay - Relocated +/* 145 */ I(RecastType)//recasttype - Relocated +/* 146 */ C("0")//clickunk5 - Newly Added - Default is 0 +/* 147 */ C("")//clickname - Newly Added - Default is Null +/* 148 */ C("-1")//clickunk7 - Newly Added - Default is -1, but some set to 0 and some much higher +/* 149 */ I(Proc.Effect) +/* 150 */ I(Proc.Type) +/* 151 */ I(Proc.Level2) +/* 152 */ I(Proc.Level) +/* 153 */ C("0")//procunk1 - Newly Added - Default is 0, but some set to -1 and 1 +/* 154 */ C("0")//procunk2 - Newly Added - Default is 0 +/* 155 */ C("0")//procunk3 - Newly Added - Default is 0 +/* 156 */ C("0")//procunk4 - Newly Added - Default is 0 +/* 157 */ I(ProcRate)//procrate - Relocated +/* 158 */ C("")//procname - Newly Added - Default is Null +/* 159 */ C("-1")//procunk7 - Newly Added - Default is -1, but some set to 0 +/* 160 */ I(Worn.Effect) +/* 161 */ I(Worn.Type) +/* 162 */ I(Worn.Level2) +/* 163 */ I(Worn.Level) +/* 164 */ C("0")//wornunk1 - Newly Added - Default is 0 +/* 165 */ C("0")//wornunk2 - Newly Added - Default is 0 +/* 166 */ C("0")//wornunk3 - Newly Added - Default is 0 +/* 167 */ C("0")//wornunk4 - Newly Added - Default is 0 +/* 168 */ C("0")//wornunk5 - Newly Added - Default is 0 +/* 169 */ C("")//wornname - Newly Added - Default is Null +/* 170 */ C("-1")//wornunk7 - Newly Added - Default is -1, but some set to 0 +/* 171 */ I(Focus.Effect) +/* 172 */ I(Focus.Type) +/* 173 */ I(Focus.Level2) +/* 174 */ I(Focus.Level) +/* 175 */ C("0")//focusunk1 - Newly Added - Default is 0 +/* 176 */ C("0")//focusunk2 - Newly Added - Default is 0 +/* 177 */ C("0")//focusunk3 - Newly Added - Default is 0 +/* 178 */ C("0")//focusunk4 - Newly Added - Default is 0 +/* 179 */ C("0")//focusunk5 - Newly Added - Default is 0 +/* 180 */ C("")//focusname - Newly Added - Default is Null +/* 181 */ C("-1")//focusunk7 - Newly Added - Default is -1, but some set to 0 +/* 182 */ I(Scroll.Effect) +/* 183 */ I(Scroll.Type) +/* 184 */ I(Scroll.Level2) +/* 185 */ I(Scroll.Level) +/* 186 */ C("0")//scrollunk1 - Renumber this*** +/* 187 */ C("0")//scrollunk2 - Newly Added - Default is 0 +/* 188 */ C("0")//scrollunk3 - Newly Added - Default is 0 +/* 189 */ C("0")//scrollunk4 - Newly Added - Default is 0 +/* 190 */ C("0")//scrollunk5 - Newly Added - Default is 0 +/* 191 */ C("")//scrollname - Newly Added - Default is Null +/* 192 */ C("-1")//scrollunk7 - Newly Added - Default is -1, but some set to 0 +/* 193 */ C("0")//UNK193 - Default is 0 +/* 194 */ C("0")//purity - Newly Added - Default is 0, but some go up to 75 +/* 195 */ C("0")//dsmitigation - Newly Added - Default is 0, but some are up to 2 +/* 196 */ C("0")//heroic_str - Newly Added - Default is 0 +/* 197 */ C("0")//heroic_int - Newly Added - Default is 0 +/* 198 */ C("0")//heroic_wis - Newly Added - Default is 0 +/* 199 */ C("0")//heroic_agi - Newly Added - Default is 0 +/* 200 */ C("0")//heroic_dex - Newly Added - Default is 0 +/* 201 */ C("0")//heroic_sta - Newly Added - Default is 0 +/* 202 */ C("0")//heroic_cha - Newly Added - Default is 0 +/* 203 */ C("0")//HeroicSvPoison - Newly Added - Default is 0 +/* 204 */ C("0")//HeroicSvMagic - Newly Added - Default is 0 +/* 205 */ C("0")//HeroicSvFire - Newly Added - Default is 0 +/* 206 */ C("0")//HeroicSvDisease - Newly Added - Default is 0 +/* 207 */ C("0")//HeroicSvCold - Newly Added - Default is 0 +/* 208 */ C("0")//HeroicSvCorruption - Newly Added - Default is 0 +/* 209 */ C("0")//healamt - Newly Added - Default is 0, but some are up to 9 +/* 210 */ C("0")//spelldmg - Newly Added - Default is 0, but some are up to 9 +/* 211 */ C("0")//clairvoyance - Newly Added - Default is 0, but some are up to 10 +/* 212 */ C("0")//backstabdmg - Newly Added - Default is 0, but some are up to 65 +//* 213 */ C("0")//evolvinglevel - Newly Added - Default is 0, but some are up to 7 +//* 214 */ C("0")//MaxPower - Newly Added +//* 215 */ C("0")//Power - Newly Added + +//This doesn't appear to be used /* 102 */ S(verified)//verified +//This doesn't appear to be used /* 102 */ S(serialized)//created +//Unsure where this goes right now (or if it is even used) /* 108 */ I(SummonedFlag) + +#else +/* 000 */ //I(ItemClass) Leave this one off on purpose +/* 001 */ S(Name) +/* 002 */ S(Lore) +/* 003 */ C("") //LoreFile? +/* 003 */ S(IDFile) +/* 004 */ I(ID) +/* 005 */ I(Weight) +/* 006 */ I(NoRent) +/* 007 */ I(NoDrop) +/* 008 */ I(Size) +/* 009 */ I(Slots) +/* 010 */ I(Price) +/* 011 */ I(Icon) +/* 013 */ C("0") +/* 014 */ C("0") +/* 014 */ I(BenefitFlag) +/* 015 */ I(Tradeskills) +/* 016 */ I(CR) +/* 017 */ I(DR) +/* 018 */ I(PR) +/* 019 */ I(MR) +/* 020 */ I(FR) + C("0") //svcorruption +/* 021 */ I(AStr) +/* 022 */ I(ASta) +/* 023 */ I(AAgi) +/* 024 */ I(ADex) +/* 025 */ I(ACha) +/* 026 */ I(AInt) +/* 027 */ I(AWis) +/* 028 */ I(HP) +/* 029 */ I(Mana) + I(Endur) +/* 030 */ I(AC) +/* 052 */ I(Classes) +/* 053 */ I(Races) +/* 031 */ I(Deity) +/* 032 */ I(SkillModValue) +/* 033 */ C("0") +/* 034 */ I(SkillModType) +/* 035 */ I(BaneDmgRace) +/* 037 */ I(BaneDmgBody) +/* 036 */ I(BaneDmgRaceAmt) +/* 036 */ I(BaneDmgAmt) +/* 038 */ I(Magic) +/* 039 */ I(CastTime_) +/* 040 */ I(ReqLevel) +/* 045 */ I(RecLevel) +/* 046 */ I(RecSkill) +/* 041 */ I(BardType) +/* 042 */ I(BardValue) +/* 043 */ I(Light) +/* 044 */ I(Delay) +/* 047 */ I(ElemDmgType) +/* 048 */ I(ElemDmgAmt) +/* 049 */ I(Range) +/* 050 */ I(Damage) +/* 051 */ I(Color) +/* 056 */ I(ItemType) +/* 057 */ I(Material) +/* 060 */ C("0") +/* 061 */ C("0") +/* 058 */ F(SellRate) +/* 063 */ I(CombatEffects) +/* 064 */ I(Shielding) +/* 065 */ I(StunResist) +/* 059 */ //C("0") +/* 061 */ //C("0") +/* 066 */ I(StrikeThrough) +/* 067 */ I(ExtraDmgSkill) +/* 068 */ I(ExtraDmgAmt) +/* 069 */ I(SpellShield) +/* 070 */ I(Avoidance) +/* 071 */ I(Accuracy) +/* 072 */ I(CharmFileID) +/* 073 */ I(FactionMod1) +/* 077 */ I(FactionAmt1) +/* 074 */ I(FactionMod2) +/* 078 */ I(FactionAmt2) +/* 075 */ I(FactionMod3) +/* 079 */ I(FactionAmt3) +/* 076 */ I(FactionMod4) +/* 080 */ I(FactionAmt4) +/* 081 */ S(CharmFile) +/* 082 */ I(AugType) +/* 082 */ I(AugRestrict) +/* 082 */ I(AugDistiller) +/* 083 */ I(AugSlotType[0]) +/* 084 */ I(AugSlotVisible[0]) +/* 084 */ I(AugSlotUnk2[0]) +/* 085 */ I(AugSlotType[1]) +/* 086 */ I(AugSlotVisible[1]) +/* 086 */ I(AugSlotUnk2[1]) +/* 087 */ I(AugSlotType[2]) +/* 088 */ I(AugSlotVisible[2]) +/* 088 */ I(AugSlotUnk2[2]) +/* 089 */ I(AugSlotType[3]) +/* 090 */ I(AugSlotVisible[3]) +/* 090 */ I(AugSlotUnk2[3]) +/* 091 */ I(AugSlotType[4]) +/* 092 */ I(AugSlotVisible[4]) +/* 092 */ I(AugSlotUnk2[4]) +/* 093 */ I(PointType) +/* 093 */ I(LDoNTheme) +/* 094 */ I(LDoNPrice) +/* 094 */ C("0") +/* 095 */ I(LDoNSold) +/* 096 */ I(BagType) +/* 097 */ I(BagSlots) +/* 098 */ I(BagSize) +/* 099 */ I(BagWR) +/* 100 */ I(Book) +/* 101 */ I(BookType) +/* 102 */ S(Filename) +/* 105 */ I(LoreGroup) +/* 106 */ //I(PendingLoreFlag) +/* 107 */ I(ArtifactFlag) +/* 094 */ C("0") +/* 108 */ //I(SummonedFlag) +/* 109 */ I(Favor) +/* 121 */ I(GuildFavor) +/* 110 */ I(FVNoDrop) +/* 112 */ I(DotShielding) +/* 113 */ I(Attack) +/* 114 */ I(Regen) +/* 115 */ I(ManaRegen) +/* 116 */ I(EnduranceRegen) +/* 117 */ I(Haste) +/* 118 */ I(DamageShield) +/* 120 */ C("0") +/* 121 */ C("0") +/* 125 */ I(Attuneable) +/* 126 */ I(NoPet) +/* 124 */ C("0") +/* 129 */ I(PotionBelt) +/* 130 */ I(PotionBeltSlots) +/* 131 */ I(StackSize) +/* 132 */ I(NoTransfer) +/* 129 */ C("0") +/* 132 */ I(QuestItemFlag) +/* 131 */ C("0") +/* 132 */ C("00000000000000000000000000000000000000") +/* 134 */ I(Click.Effect) +/* 135 */ I(Click.Type) +/* 136 */ I(Click.Level2) +/* 137 */ I(Click.Level) +/* 055 */ I(MaxCharges) +/* 060 */ I(CastTime) +/* 119 */ I(RecastDelay) +/* 120 */ I(RecastType) +/* 138 */ C("0") //clickunk5 (prolly ProcRate) +/* 138 */ C("") //clickunk6 +/* 138 */ C("-1") //clickunk7 +/* 139 */ I(Proc.Effect) +/* 140 */ I(Proc.Type) +/* 141 */ I(Proc.Level2) +/* 142 */ I(Proc.Level) +/* 143 */ C("0") //procunk1 (prolly MaxCharges) +/* 143 */ C("0") //procunk2 (prolly CastTime) +/* 143 */ C("0") //procunk3 (prolly RecastDelay) +/* 143 */ C("0") //procunk4 (prolly RecastType) +/* 062 */ I(ProcRate) +/* 143 */ C("") //procunk6 +/* 143 */ C("-1") //procunk7 +/* 144 */ I(Worn.Effect) +/* 145 */ I(Worn.Type) +/* 146 */ I(Worn.Level2) +/* 147 */ I(Worn.Level) +/* 143 */ C("0") //wornunk1 (prolly MaxCharges) +/* 143 */ C("0") //wornunk2 (prolly CastTime) +/* 143 */ C("0") //wornunk3 (prolly RecastDelay) +/* 143 */ C("0") //wornunk4 (prolly RecastType) +/* 143 */ C("0") //wornunk5 (prolly ProcRate) +/* 143 */ C("") //wornunk6 +/* 143 */ C("-1") //wornunk7 +/* 149 */ I(Focus.Effect) +/* 150 */ I(Focus.Type) +/* 151 */ I(Focus.Level2) +/* 152 */ I(Focus.Level) +/* 143 */ C("0") //focusunk1 (prolly MaxCharges) +/* 143 */ C("0") //focusunk2 (prolly CastTime) +/* 143 */ C("0") //focusunk3 (prolly RecastDelay) +/* 143 */ C("0") //focusunk4 (prolly RecastType) +/* 143 */ C("0") //focusunk5 (prolly ProcRate) +/* 143 */ C("") //focusunk6 +/* 143 */ C("-1") //focusunk7 +/* 154 */ I(Scroll.Effect) +/* 155 */ I(Scroll.Type) +/* 156 */ I(Scroll.Level2) +/* 157 */ I(Scroll.Level) +/* 143 */ C("0") //scrollunk1 (prolly MaxCharges) +/* 143 */ C("0") //scrollunk2 (prolly CastTime) +/* 143 */ C("0") //scrollunk3 (prolly RecastDelay) +/* 143 */ C("0") //scrollunk4 (prolly RecastType) +/* 143 */ C("0") //scrollunk5 (prolly ProcRate) +/* 143 */ C("") //scrollunk6 +/* 143 */ C("-1") //scrollunk7 +/* 193 */ C("0") //Power Source Capacity +/* 194 */ C("0") //purity + +#endif + +#undef I +#undef C +#undef S +#undef F + diff --git a/common/patches/SoD_ops.h b/common/patches/SoD_ops.h new file mode 100644 index 000000000..d7b87d514 --- /dev/null +++ b/common/patches/SoD_ops.h @@ -0,0 +1,117 @@ + +//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) +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_ApplyPoison) +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_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) +D(OP_Buff) +D(OP_ShopPlayerSell) +D(OP_Consume) +D(OP_CastSpell) +D(OP_Save) +D(OP_ItemVerifyRequest) +D(OP_GroupInvite) +D(OP_GroupInvite2) +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_WearChange) +D(OP_ShopPlayerBuy) +D(OP_BazaarSearch) +D(OP_LoadSpellSet) +D(OP_ApplyPoison) +D(OP_DeleteItem) +D(OP_AugmentItem) +D(OP_Bug) +D(OP_AltCurrencySellSelection) +D(OP_AltCurrencySell) +#undef E +#undef D diff --git a/common/patches/SoD_structs.h b/common/patches/SoD_structs.h new file mode 100644 index 000000000..95f4b22ef --- /dev/null +++ b/common/patches/SoD_structs.h @@ -0,0 +1,4349 @@ +#ifndef SoD_STRUCTS_H_ +#define SoD_STRUCTS_H_ + +namespace SoD { + namespace structs { + + +static const uint32 BUFF_COUNT = 25; + +/* +** Compiler override to ensure +** byte aligned structures +*/ +#pragma pack(1) + +struct LoginInfo_Struct { +/*000*/ char login_info[64]; +/*064*/ uint8 unknown064[124]; +/*188*/ uint8 zoning; // 01 if zoning, 00 if not +/*189*/ uint8 unknown189[275]; +/*488*/ +}; + +struct EnterWorld_Struct { +/*000*/ char name[64]; +/*064*/ uint32 tutorial; // 01 on "Enter Tutorial", 00 if not +/*068*/ uint32 return_home; // 01 on "Return Home", 00 if not +}; + +//New For SoF +struct WorldObjectsSent_Struct { +}; + +/* Name Approval Struct */ +/* Len: */ +/* Opcode: 0x8B20*/ +struct NameApproval +{ + char name[64]; + uint32 race; + uint32 class_; + uint32 deity; +}; + +/* +** Entity identification struct +** Size: 4 bytes +** OPCodes: OP_DeleteSpawn, OP_Assist +*/ +struct EntityId_Struct +{ +/*00*/ uint32 entity_id; +/*04*/ +}; + +struct Duel_Struct +{ + uint32 duel_initiator; + uint32 duel_target; +}; + +struct DuelResponse_Struct +{ + uint32 target_id; + uint32 entity_id; + uint32 unknown; +}; +/* + Cofruben: + Adventure stuff,not a net one,just one for our use +*/ +static const uint32 ADVENTURE_COLLECT = 0; +static const uint32 ADVENTURE_MASSKILL = 1; +static const uint32 ADVENTURE_NAMED = 2; +static const uint32 ADVENTURE_RESCUE = 3; + +struct AdventureInfo { + uint32 QuestID; + uint32 NPCID; + bool in_use; + uint32 status; + bool ShowCompass; + uint32 Objetive;// can be item to collect,mobs to kill,boss to kill and someone to rescue. + uint32 ObjetiveValue;// number of items,or number of needed mob kills. + char text[512]; + uint8 type; + uint32 minutes; + uint32 points; + float x; + float y; + uint32 zoneid; + uint32 zonedungeonid; +}; +/////////////////////////////////////////////////////////////////////////////// + + +/* +** Color_Struct +** Size: 4 bytes +** Used for convenience +** Merth: Gave struct a name so gcc 2.96 would compile +** +*/ +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 CharSelectEquip { + //totally guessed; + uint32 equip0; + uint32 equip1; + uint32 itemid; + Color_Struct color; +}; + +struct CharacterSelectEntry_Struct { +/*0000*/ uint8 level; // +/*0000*/ uint8 hairstyle; // +/*0002*/ uint8 gender; // +/*0003*/ char name[1]; //variable length, edi+0 +/*0000*/ uint8 beard; // +/*0001*/ uint8 haircolor; // +/*0000*/ uint8 face; // +/*0000*/ CharSelectEquip equip[9]; +/*0000*/ uint32 primary; // +/*0000*/ uint32 secondary; // +/*0000*/ uint8 u15; // 0xff +/*0000*/ uint32 deity; // +/*0000*/ uint16 zone; // +/*0000*/ uint16 instance; +/*0000*/ uint8 gohome; // +/*0000*/ uint8 u19; // 0xff +/*0000*/ uint32 race; // +/*0000*/ uint8 tutorial; // +/*0000*/ uint8 class_; // +/*0000*/ uint8 eyecolor1; // +/*0000*/ uint8 beardcolor; // +/*0000*/ uint8 eyecolor2; // +/*0000*/ uint32 drakkin_heritage; // Drakkin Heritage +/*0000*/ uint32 drakkin_tattoo; // Drakkin Tattoo +/*0000*/ uint32 drakkin_details; // Drakkin Details (Facial Spikes) +/*0000*/ uint8 unknown; // New field to SoD + +}; + +/* +** Character Selection Struct +** +*/ +struct CharacterSelect_Struct { +/*0000*/ uint32 char_count; //number of chars in this packet +/*0004*/ uint32 total_chars; //total number of chars allowed? +/*0008*/ CharacterSelectEntry_Struct entries[0]; +}; + +/* +* Visible equiptment. +* Size: 12 Octets +*/ +struct EquipStruct { +/*00*/ uint32 equip0; +/*04*/ uint32 equip1; +/*08*/ uint32 itemId; +/*12*/ +}; + + +/* +** Generic Spawn Struct +** Length: 897 Octets +** Used in: +** spawnZoneStruct +** dbSpawnStruct +** petStruct +** newSpawnStruct +*/ +/* +showeq -> eqemu +sed -e 's/_t//g' -e 's/seto_0xFF/set_to_0xFF/g' +*/ + +struct Spawn_Struct_Bitfields +{ + + unsigned ispet:1; // Could be 'is summoned pet' rather than just is pet. + unsigned afk:1; // 0=no, 1=afk + unsigned sneak:1; + unsigned lfg:1; + unsigned padding5:1; + unsigned invis:1; // 0 = visible, 1 = invis/sneaking + unsigned padding7:11; + unsigned gm:1; + unsigned anon:2; // 0=normal, 1=anon, 2=roleplay + unsigned gender:2; // Gender (0=male, 1=female, 2=monster) + unsigned linkdead:1; // Toggles LD on or off after name + unsigned betabuffed:1; + unsigned showhelm:1; + unsigned padding26:1; + unsigned targetable:1; // 1 = Targetable 0 = Not Targetable (is_npc?) + unsigned targetable_with_hotkey:1; // is_npc? + unsigned showname:1; + unsigned statue:1; + unsigned trader:1; + unsigned buyer:1; +}; + +struct Spawn_Struct_Position +{ +/*0000*/ signed padding0000:12; // ***Placeholder + signed deltaX:13; // change in x + signed padding0005:7; // ***Placeholder +/*0000*/ signed deltaHeading:10;// change in heading + signed deltaY:13; // change in y + signed padding0006:9; // ***Placeholder +/*0000*/ signed y:19; // y coord + signed animation:13; // animation +/*0000*/ unsigned heading:12; // heading + signed x:19; // x coord + signed padding0014:1; // ***Placeholder +/*0000*/ signed z:19; // z coord + signed deltaZ:13; // change in z +}; + +struct Spawn_Struct +{ +// Note this struct is not used as such, it is here for reference. As the struct is variable sized, the packets +// are constructed in SoD.cpp +// +/*0000*/ char name[1]; //name[64]; +/*0000*/ //uint8 nullterm1; // hack to null terminate name +/*0064*/ uint32 spawnId; +/*0068*/ uint8 level; +/*0069*/ float unknown1; +/*0073*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse + Spawn_Struct_Bitfields Bitfields; +/*0000*/ uint8 otherData; // & 4 - has title, & 8 - has suffix, & 1 - it's a chest or untargetable +/*0000*/ float unknown3; // seen -1 +/*0000*/ float unknown4; +/*0000*/ float size; +/*0000*/ uint8 face; +/*0000*/ float walkspeed; +/*0000*/ float runspeed; +/*0000*/ uint32 race; +/*0000*/ uint8 showname; // for body types - was charProperties +/*0000*/ uint32 bodytype; +/*0000*/ //uint32 bodytype2; // this is only present if charProperties==2 + // are there more than two possible properties? +/*0000*/ uint8 curHp; +/*0000*/ uint8 haircolor; +/*0000*/ uint8 beardcolor; +/*0000*/ uint8 eyecolor1; +/*0000*/ uint8 eyecolor2; +/*0000*/ uint8 hairstyle; +/*0000*/ uint8 beard; +/*0000*/ uint32 drakkin_heritage; +/*0000*/ uint32 drakkin_tattoo; +/*0000*/ uint32 drakkin_details; +/*0000*/ uint8 statue; // was holding +/*0000*/ uint32 deity; +/*0000*/ uint32 guildID; +/*0000*/ uint32 guildrank; // 0=member, 1=officer, 2=leader, -1=not guilded +/*0000*/ uint8 class_; +/*0000*/ uint8 pvp; // 0 = normal name color, 2 = PVP name color +/*0000*/ uint8 StandState; // stand state - 0x64 for normal animation +/*0000*/ uint8 light; +/*0000*/ uint8 flymode; +/*0000*/ uint8 equip_chest2; +/*0000*/ uint8 unknown9; +/*0000*/ uint8 unknown10; +/*0000*/ uint8 helm; +/*0000*/ char lastName[1]; +/*0000*/ //uint8 lastNameNull; //hack! +/*0000*/ uint32 aatitle; // 0=none, 1=general, 2=archtype, 3=class was AARank +/*0000*/ uint8 unknown12; +/*0000*/ uint32 petOwnerId; +/*0000*/ uint8 unknown13; +/*0000*/ uint32 unknown14; // Stance 64 = normal 4 = aggressive 40 = stun/mezzed +/*0000*/ uint32 unknown15; +/*0000*/ uint32 unknown16; +/*0000*/ uint32 unknown17; +/*0000*/ uint32 unknown18; +/*0000*/ uint32 unknown19; + Spawn_Struct_Position Position; +/*0000*/ union + { + struct + { + /*0000*/ Color_Struct color_helmet; // Color of helmet item + /*0000*/ Color_Struct color_chest; // Color of chest item + /*0000*/ Color_Struct color_arms; // Color of arms item + /*0000*/ Color_Struct color_bracers; // Color of bracers item + /*0000*/ Color_Struct color_hands; // Color of hands item + /*0000*/ Color_Struct color_legs; // Color of legs item + /*0000*/ Color_Struct color_feet; // Color of feet item + /*0000*/ Color_Struct color_primary; // Color of primary item + /*0000*/ Color_Struct color_secondary; // Color of secondary item + } equipment_colors; + /*0000*/ Color_Struct colors[9]; // Array elements correspond to struct equipment_colors above + }; + +// skip these bytes if not a valid player race +/*0000*/ union + { + struct + { + /*0000*/ EquipStruct equip_helmet; // Equiptment: Helmet visual + /*0000*/ EquipStruct equip_chest; // Equiptment: Chest visual + /*0000*/ EquipStruct equip_arms; // Equiptment: Arms visual + /*0000*/ EquipStruct equip_bracers; // Equiptment: Wrist visual + /*0000*/ EquipStruct equip_hands; // Equiptment: Hands visual + /*0000*/ EquipStruct equip_legs; // Equiptment: Legs visual + /*0000*/ EquipStruct equip_feet; // Equiptment: Boots visual + /*0000*/ EquipStruct equip_primary; // Equiptment: Main visual + /*0000*/ EquipStruct equip_secondary; // Equiptment: Off visual + } equip; + /*0000*/ EquipStruct equipment[9]; + }; + +/*0000*/ //char title[0]; // only read if(hasTitleOrSuffix & 4) +/*0000*/ //char suffix[0]; // only read if(hasTitleOrSuffix & 8) + char unknown20[8]; + uint8 IsMercenary; // If NPC == 1 and this == 1, then the NPC name is Orange. +/*0000*/ char unknown21[24]; // Was 33 +}; + + +/* +** Generic Spawn Struct +** Fields from old struct not yet found: +** uint8 traptype; // 65 is disarmable trap, 66 and 67 are invis triggers/traps +** uint8 is_pet; // 0=no, 1=yes +** uint8 afk; // 0=no, 1=afk +** uint8 is_npc; // 0=no, 1=yes +** uint8 max_hp; // (name prolly wrong)takes on the value 100 for players, 100 or 110 for NPCs and 120 for PC corpses... +** uint8 guildrank; // 0=normal, 1=officer, 2=leader +** uint8 eyecolor2; //not sure, may be face +** uint8 aaitle; // 0=none, 1=general, 2=archtype, 3=class +*/ + +/* +** New Spawn +** Length: 176 Bytes +** OpCode: 4921 +*/ +struct NewSpawn_Struct +{ + struct Spawn_Struct spawn; // Spawn Information +}; + + +/* +** Client Zone Entry struct +** Length: 68 Octets +** OpCode: ZoneEntryCode (when direction == client) +*/ +struct ClientZoneEntry_Struct { +/*0000*/ uint32 unknown0000; // ***Placeholder +/*0004*/ char char_name[64]; // Player firstname [32] +//*0036*/ uint8 unknown0036[28]; // ***Placeholder +//*0064*/ uint32 unknown0064; // unknown +}; + + +/* +** Server Zone Entry Struct +** Length: 452 Bytes +** OPCodes: OP_ServerZoneEntry +** +*/ +struct ServerZoneEntry_Struct //Adjusted from SEQ Everquest.h Struct +{ + struct NewSpawn_Struct player; +}; + + +//New Zone Struct - Size: 932 +struct NewZone_Struct { +/*0000*/ char char_name[64]; // Character Name +/*0064*/ char zone_short_name[32]; // Zone Short Name +/*0096*/ char unknown0096[96]; +/*0192*/ char zone_long_name[278]; // Zone Long Name +/*0470*/ uint8 ztype; // Zone type (usually FF) +/*0471*/ uint8 fog_red[4]; // Zone fog (red) +/*0475*/ uint8 fog_green[4]; // Zone fog (green) +/*0479*/ uint8 fog_blue[4]; // Zone fog (blue) +/*0483*/ uint8 unknown323; +/*0484*/ float fog_minclip[4]; +/*0500*/ float fog_maxclip[4]; +/*0516*/ float gravity; +/*0520*/ uint8 time_type; +/*0521*/ uint8 unknown521[49]; +/*0570*/ uint8 sky; // Sky Type +/*0571*/ uint8 unknown571[13]; // ***Placeholder +/*0584*/ float zone_exp_multiplier; // Experience Multiplier +/*0588*/ float safe_y; // Zone Safe Y +/*0592*/ float safe_x; // Zone Safe X +/*0596*/ float safe_z; // Zone Safe Z +/*0600*/ float min_z; // Guessed - NEW - Seen 0 +/*0604*/ float max_z; // Guessed +/*0608*/ float underworld; // Underworld, min z (Not Sure?) +/*0612*/ float minclip; // Minimum View Distance +/*0616*/ float maxclip; // Maximum View DIstance +/*0620*/ uint8 unknown620[84]; // ***Placeholder +/*0704*/ char zone_short_name2[96]; //zone file name? excludes instance number which can be in previous version. +/*0800*/ int32 unknown800; //seen -1 +/*0804*/ char unknown804[40]; // +/*0844*/ int32 unknown844; //seen 600 +/*0848*/ int32 unknown848; +/*0852*/ uint16 zone_id; +/*0854*/ uint16 zone_instance; +/*0856*/ char unknown856[20]; +/*0876*/ uint32 SuspendBuffs; +/*0880*/ uint32 unknown880; //seen 50 +/*0884*/ uint32 unknown884; //seen 10 +/*0888*/ uint8 unknown888; //seen 1 +/*0889*/ uint8 unknown889; //seen 0 (POK) or 1 (rujj) +/*0890*/ uint8 unknown890; //seen 1 +/*0891*/ uint8 unknown891; //seen 0 +/*0892*/ uint8 unknown892; //seen 0 +/*0893*/ uint8 unknown893; //seen 0 - 00 +/*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off +/*0895*/ uint8 unknown895; //seen 0 - 00 +/*0896*/ uint32 unknown896; //seen 180 +/*0900*/ uint32 unknown900; //seen 180 +/*0904*/ uint32 unknown904; //seen 180 +/*0908*/ uint32 unknown908; //seen 2 +/*0912*/ uint32 unknown912; //seen 2 +/*0916*/ float FogDensity; //Of about 10 or so zones tested, all but one have this set to 0.33 Blightfire had 0.16 +/*0920*/ uint32 unknown920; //seen 0 +/*0924*/ uint32 unknown924; //seen 0 +/*0928*/ uint32 unknown928; //seen 0 +/*0932*/ +}; + + +/* +** Memorize Spell Struct +** Length: 16 Bytes +** +*/ +struct MemorizeSpell_Struct { +uint32 slot; // Spot in the spell book/memorized slot +uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) +uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming +//uint32 unknown12; +}; + +/* +** Make Charmed Pet +** Length: 12 Bytes +** +*/ +struct Charm_Struct { +/*00*/ uint32 owner_id; +/*04*/ uint32 pet_id; +/*08*/ uint32 command; // 1: make pet, 0: release pet +/*12*/ +}; + +struct InterruptCast_Struct +{ + uint32 spawnid; + uint32 messageid; + char message[0]; +}; + +struct DeleteSpell_Struct +{ +/*000*/int16 spell_slot; +/*002*/uint8 unknowndss002[2]; +/*004*/uint8 success; +/*005*/uint8 unknowndss006[3]; +/*008*/ +}; + +struct ManaChange_Struct +{ + uint32 new_mana; // New Mana AMount + uint32 stamina; + uint32 spell_id; + uint32 unknown12; + uint32 unknown16; +}; + +struct SwapSpell_Struct +{ + uint32 from_slot; + uint32 to_slot; + + +}; + +struct BeginCast_Struct +{ + // len = 8 +/*004*/ uint16 caster_id; +/*006*/ uint16 spell_id; +/*016*/ uint32 cast_time; // in miliseconds +}; + +struct CastSpell_Struct +{ + uint32 slot; + uint32 spell_id; + uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast + uint32 target_id; + uint8 cs_unknown[4]; +}; + +/* +** SpawnAppearance_Struct +** Changes client appearance for all other clients in zone +** Size: 8 bytes +** Used in: OP_SpawnAppearance +** +*/ +struct SpawnAppearance_Struct +{ +/*0000*/ uint16 spawn_id; // ID of the spawn +/*0002*/ uint16 type; // Values associated with the type +/*0004*/ uint32 parameter; // Type of data sent +/*0008*/ +}; + + +// solar: this is used inside profile +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 unknown004; //Might need to be swapped with player_id +/*020*/ uint32 player_id; //'global' ID of the caster, for wearoff messages +/*024*/ + + +}; + + +struct SpellBuffFade_Struct { +/*000*/ uint32 entityid; +/*004*/ uint8 slot; +/*005*/ uint8 level; +/*006*/ uint8 effect; +/*007*/ uint8 unknown7; +/*008*/ uint32 spellid; +/*012*/ uint32 duration; +/*016*/ uint32 unknown016; +/*020*/ uint32 unknown020; //prolly global player ID +/*024*/ uint32 playerId; // Player id who cast the buff +/*028*/ uint32 slotid; +/*032*/ uint32 bufffade; +/*036*/ +}; + +struct GMTrainee_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ uint32 skills[73]; + /*300*/ uint8 unknown300[148]; + /*448*/ +}; + +struct GMTrainEnd_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ +}; + +struct GMSkillChange_Struct { +/*000*/ uint16 npcid; +/*002*/ uint8 unknown1[2]; // something like PC_ID, but not really. stays the same thru the session though +/*002*/ uint16 skillbank; // 0 if normal skills, 1 if languages +/*002*/ uint8 unknown2[2]; +/*008*/ uint16 skill_id; +/*010*/ uint8 unknown3[2]; +}; +struct ConsentResponse_Struct { + char grantname[64]; + char ownername[64]; + uint8 permission; + char zonename[64]; +}; + +/* +** Name Generator Struct +** Length: 72 bytes +** OpCode: 0x0290 +*/ +struct NameGeneration_Struct +{ +/*0000*/ uint32 race; +/*0004*/ uint32 gender; +/*0008*/ char name[64]; +/*0072*/ +}; + +/* +** Character Creation struct +** Length: 140 Bytes +** OpCode: 0x009b +*/ +struct CharCreate_Struct +{ +/*0000*/ uint32 class_; +/*0004*/ uint32 haircolor; +/*0008*/ uint32 beard; +/*0012*/ uint32 beardcolor; +/*0016*/ uint32 gender; +/*0020*/ uint32 race; +/*0024*/ uint32 start_zone; +/*0028*/ uint32 hairstyle; +/*0032*/ uint32 deity; +/*0036*/ uint32 STR; +/*0040*/ uint32 STA; +/*0044*/ uint32 AGI; +/*0048*/ uint32 DEX; +/*0052*/ uint32 WIS; +/*0056*/ uint32 INT; +/*0060*/ uint32 CHA; +/*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 tutorial; +/*0080*/ uint32 drakkin_heritage; +/*0084*/ uint32 drakkin_tattoo; +/*0088*/ uint32 drakkin_details; +/*0092*/ +}; + +/* +** Character Creation struct +** Length: 0 Bytes +** OpCode: 0x +*/ +struct CharCreate_Struct_Temp //Size is now 0 +{ +}; + +/* + *Used in PlayerProfile + */ +struct AA_Array +{ + uint32 AA; + uint32 value; + uint32 unknown08; // Looks like AA_Array is now 12 bytes in Live +}; + + +static const uint32 MAX_PP_DISCIPLINES = 100; + +struct Disciplines_Struct { + uint32 values[MAX_PP_DISCIPLINES]; +}; + +static const uint32 MAX_PLAYER_TRIBUTES = 5; +static const uint32 MAX_PLAYER_BANDOLIER = 20; +static const uint32 MAX_PLAYER_BANDOLIER_ITEMS = 4; +static const uint32 MAX_POTIONS_IN_BELT = 5; +static const uint32 TRIBUTE_NONE = 0xFFFFFFFF; + +struct Tribute_Struct { + uint32 tribute; + uint32 tier; +}; + +//len = 72 +struct BandolierItem_Struct { + uint32 item_id; + uint32 icon; + char item_name[64]; +}; + +//len = 320 +enum { //bandolier item positions + bandolierMainHand = 0, + bandolierOffHand, + bandolierRange, + bandolierAmmo +}; +struct Bandolier_Struct { + char name[32]; + BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; +}; +struct PotionBelt_Struct { + BandolierItem_Struct items[MAX_POTIONS_IN_BELT]; +}; + +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]; +}; +struct RaidLeadershipAA_Struct { + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; +}; + + /** +* A bind point. +* Size: 20 Octets +*/ +struct BindStruct { + /*000*/ uint32 zoneId; + /*004*/ float x; + /*008*/ float y; + /*012*/ float z; + /*016*/ float heading; + /*020*/ +}; + + +/* +** Player Profile +** +** Length: 4308 bytes +** OpCode: 0x006a + */ +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 = 75; +static const uint32 MAX_PP_AA_ARRAY = 300; //was 299 +static const uint32 MAX_GROUP_MEMBERS = 6; +static const uint32 MAX_RECAST_TYPES = 20; +/* +showeq -> eqemu +sed -e 's/_t//g' -e 's/MAX_AA/MAX_PP_AA_ARRAY/g' \ + -e 's/MAX_SPELL_SLOTS/MAX_PP_MEMSPELL/g' \ + -e 's/MAX_KNOWN_SKILLS/MAX_PP_SKILL/g' \ + -e 's/MAXRIBUTES/MAX_PLAYER_TRIBUTES/g' \ + -e 's/MAX_BUFFS/BUFF_COUNT/g' \ + -e 's/MAX_KNOWN_LANGS/MAX_PP_LANGUAGE/g' \ + -e 's/MAX_RECASTYPES/MAX_RECAST_TYPES/g' \ + -e 's/spellBuff/SpellBuff_Struct/g' \ + -e 's/lastName/last_name/g' \ + -e 's/guildID/guild_id/g' \ + -e 's/itemint/item_tint/g' \ + -e 's/MANA/mana/g' \ + -e 's/curHp/cur_hp/g' \ + -e 's/sSpellBook/spell_book/g' \ + -e 's/sMemSpells/mem_spells/g' \ + -e 's/uint32[ \t]*disciplines\[MAX_DISCIPLINES\]/Disciplines_Struct disciplines/g' \ + -e 's/aa_unspent/aapoints/g' \ + -e 's/aa_spent/aapoints_spent/g' \ + -e 's/InlineItem[ \t]*potionBelt\[MAX_POTIONS_IN_BELT\]/PotionBelt_Struct potionbelt/g' \ + -e 's/ldon_guk_points/ldon_points_guk/g' \ + -e 's/ldon_mir_points/ldon_points_mir/g' \ + -e 's/ldon_mmc_points/ldon_points_mmc/g' \ + -e 's/ldon_ruj_points/ldon_points_ruj/g' \ + -e 's/ldonak_points/ldon_points_tak/g' \ + -e 's/ldon_avail_points/ldon_points_available/g' \ + -e 's/tributeTime/tribute_time_remaining/g' \ + -e 's/careerTribute/career_tribute_points/g' \ + -e 's/currentTribute/tribute_points/g' \ + -e 's/tributeActive/tribute_active/g' \ + -e 's/TributeStruct/Tribute_Struct/g' \ + -e 's/expGroupLeadAA/group_leadership_exp/g' \ + -e 's/expRaidLeadAA/raid_leadership_exp/g' \ + -e 's/groupLeadAAUnspent/group_leadership_points/g' \ + -e 's/raidLeadAAUnspent/raid_leadership_points/g' \ + -e 's/uint32[ \t]*leadershipAAs\[MAX_LEAD_AA\]/LeadershipAA_Struct leader_abilities/g' \ + -e 's/BandolierStruct/Bandolier_Struct/g' \ + -e 's/MAX_BANDOLIERS/MAX_PLAYER_BANDOLIER/g' \ + -e 's/birthdayTime/birthday/g' \ + -e 's/lastSaveTime/lastlogin/g' \ + -e 's/zoneId/zone_id/g' \ + -e 's/hunger/hunger_level/g' \ + -e 's/thirst/thirst_level/g' \ + -e 's/guildstatus/guildrank/g' \ + -e 's/airRemaining/air_remaining/g' \ + */ + +// Live Feb 13 2009 - Size 23488 +struct PlayerProfile_Struct +{ +/*00000*/ uint32 checksum; // +//BEGIN SUB-STRUCT used for shrouding stuff... +/*00004*/ uint32 gender; // Player Gender - 0 Male, 1 Female +/*00008*/ uint32 race; // Player race +/*00012*/ uint32 class_; // Player class +/*00016*/ uint8 unknown00016[40]; // #### uint32 unknown00016; in Titanium ####uint8[40] +/*00056*/ uint8 level; // Level of player +/*00057*/ uint8 level1; // Level of player (again?) +/*00058*/ uint8 unknown00022[2]; // ***Placeholder +/*00060*/ BindStruct binds[5]; // Bind points (primary is first) +/*00160*/ uint32 deity; // deity +/*00164*/ uint32 intoxication; // Alcohol level (in ticks till sober?) +/*00168*/ uint32 spellSlotRefresh[MAX_PP_MEMSPELL]; // Refresh time (millis) - 4 Octets Each +/*00208*/ uint32 abilitySlotRefresh; +/*00212*/ uint8 haircolor; // Player hair color +/*00213*/ uint8 beardcolor; // Player beard color +/*00214*/ uint8 eyecolor1; // Player left eye color +/*00215*/ uint8 eyecolor2; // Player right eye color +/*00216*/ uint8 hairstyle; // Player hair style +/*00217*/ uint8 beard; // Player beard type +/*00218*/ uint8 unknown00178[14]; //[10]14 on Live? was 10 +/*00232*/ union + { + struct + { + /*00228*/ EquipStruct equip_helmet; // Equiptment: Helmet visual + /*00240*/ EquipStruct equip_chest; // Equiptment: Chest visual + /*00252*/ EquipStruct equip_arms; // Equiptment: Arms visual + /*00264*/ EquipStruct equip_bracers; // Equiptment: Wrist visual + /*00276*/ EquipStruct equip_hands; // Equiptment: Hands visual + /*00288*/ EquipStruct equip_legs; // Equiptment: Legs visual + /*00300*/ EquipStruct equip_feet; // Equiptment: Boots visual + /*00312*/ EquipStruct equip_primary; // Equiptment: Main visual + /*00324*/ EquipStruct equip_secondary; // Equiptment: Off visual + } equip; + /*00228*/ EquipStruct equipment[9]; //Live Shows [108] for this part + }; +/*00340*/ uint8 unknown00224[156]; // Live Shows [160] +/*00496*/ Color_Struct item_tint[9]; // RR GG BB 00 +/*00532*/ AA_Array aa_array[MAX_PP_AA_ARRAY]; // [3600] AAs 12 bytes each +/*04132*/ uint32 points; // Unspent Practice points - RELOCATED??? +/*04136*/ uint32 mana; // Current mana +/*04140*/ uint32 cur_hp; // Current HP without +HP equipment +/*04144*/ uint32 STR; // Strength - 6e 00 00 00 - 110 +/*04148*/ uint32 STA; // Stamina - 73 00 00 00 - 115 +/*04152*/ uint32 CHA; // Charisma - 37 00 00 00 - 55 +/*04156*/ uint32 DEX; // Dexterity - 50 00 00 00 - 80 +/*04160*/ uint32 INT; // Intelligence - 3c 00 00 00 - 60 +/*04164*/ uint32 AGI; // Agility - 5f 00 00 00 - 95 +/*04168*/ uint32 WIS; // Wisdom - 46 00 00 00 - 70 +/*04172*/ uint8 unknown04172[28]; // +/*04200*/ uint8 face; // Player face +/*04201*/ uint8 unknown02264[147]; // was [175] +/*04348*/ uint32 spell_book[MAX_PP_SPELLBOOK]; // List of the Spells in spellbook 480 = 60 pages [1920] +/*06268*/ uint8 unknown4184[128]; // was [164] Seen -1 +/*06396*/ uint32 mem_spells[MAX_PP_MEMSPELL]; // List of spells memorized +/*06436*/ uint8 unknown04396[28]; //#### uint8 unknown04396[32]; in Titanium ####[28] +/*06464*/ uint32 platinum; // Platinum Pieces on player +/*06468*/ uint32 gold; // Gold Pieces on player +/*06472*/ uint32 silver; // Silver Pieces on player +/*06476*/ uint32 copper; // Copper Pieces on player +/*06480*/ uint32 platinum_cursor; // Platinum Pieces on cursor +/*06484*/ uint32 gold_cursor; // Gold Pieces on cursor +/*06488*/ uint32 silver_cursor; // Silver Pieces on cursor +/*06492*/ uint32 copper_cursor; // Copper Pieces on cursor +/*06496*/ uint32 skills[MAX_PP_SKILL]; // [300] List of skills +/*06796*/ uint8 unknown04760[236]; +/*07032*/ uint32 toxicity; // Potion Toxicity (15=too toxic, each potion adds 3) +/*07036*/ uint32 thirst_level; // Drink (ticks till next drink) +/*07040*/ uint32 hunger_level; // Food (ticks till next eat) +/*07044*/ SpellBuff_Struct buffs[BUFF_COUNT]; // [600]Buffs currently on the player +/*07644*/ Disciplines_Struct disciplines; // [400] Known disciplines +/*08044*/ uint32 recastTimers[MAX_RECAST_TYPES]; // Timers (UNIX Time of last use) +/*08124*/ uint8 unknown08124[160]; // Some type of Timers +/*08284*/ uint32 endurance; // Current endurance +/*08288*/ uint32 aapoints_spent; // Number of spent AA points +/*08292*/ uint32 aapoints; // Unspent AA points +/*08296*/ uint8 unknown06160[4]; +/*08300*/ Bandolier_Struct bandoliers[MAX_PLAYER_BANDOLIER]; // [6400] bandolier contents +/*14700*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot +/*15060*/ uint8 unknown12852[8]; +/*15068*/ uint32 available_slots; +/*15072*/ uint8 unknown12864[80]; //#### uint8 uint8 unknown12864[76]; in Titanium ####[80] +//END SUB-STRUCT used for shrouding. +/*15152*/ char name[64]; // Name of player +/*15216*/ char last_name[32]; // Last name of player +/*15248*/ uint8 unknown19588[12]; //#### Not In Titanium #### new to SoF[12] +/*15260*/ uint32 guild_id; // guildid +/*15264*/ uint32 birthday; // character birthday +/*15268*/ uint32 account_startdate; // Date the Account was started - New Field for SoD*** +/*15272*/ uint32 lastlogin; // character last save time +/*15276*/ uint32 timePlayedMin; // time character played +/*15280*/ uint8 pvp; // 1=pvp, 0=not pvp +/*15281*/ uint8 anon; // 2=roleplay, 1=anon, 0=not anon +/*15282*/ uint8 gm; // 0=no, 1=yes (guessing!) +/*15283*/ uint8 guildrank; // 0=member, 1=officer, 2=guildleader -1=no guild +/*15284*/ uint32 guildbanker; +/*15288*/ uint8 unknown13054[4]; //was [8] +/*15292*/ uint32 exp; // Current Experience +/*15296*/ uint8 unknown13072[8]; +/*15304*/ uint32 timeentitledonaccount; +/*15308*/ uint8 languages[MAX_PP_LANGUAGE]; // List of languages +/*15333*/ uint8 unknown13109[7]; //#### uint8 unknown13109[4]; in Titanium ####[7] +/*15340*/ float y; // Players y position (NOT positive about this switch) +/*15344*/ float x; // Players x position +/*15348*/ float z; // Players z position +/*15352*/ float heading; // Players heading +/*15356*/ uint8 unknown13132[4]; // ***Placeholder +/*15360*/ uint32 platinum_bank; // Platinum Pieces in Bank +/*15364*/ uint32 gold_bank; // Gold Pieces in Bank +/*15368*/ uint32 silver_bank; // Silver Pieces in Bank +/*15372*/ uint32 copper_bank; // Copper Pieces in Bank +/*15376*/ uint32 platinum_shared; // Shared platinum pieces +/*15380*/ uint8 unknown13156[1036]; // was [716] +/*16416*/ uint32 expansions; // Bitmask for expansions +/*16420*/ uint8 unknown13244[12]; +/*16432*/ uint32 autosplit; // 0 = off, 1 = on +/*16436*/ uint8 unknown13260[16]; +/*16452*/ uint16 zone_id; // see zones.h +/*16454*/ uint16 zoneInstance; // Instance id +/*16456*/ char groupMembers[MAX_GROUP_MEMBERS][64];// 384 all the members in group, including self +/*16840*/ char groupLeader[64]; // Leader of the group ? +/*16904*/ uint8 unknown13728[348]; // was [788] +/*17252*/ uint32 entityid; +/*17256*/ uint32 leadAAActive; // 0 = leader AA off, 1 = leader AA on +/*17260*/ uint8 unknown14392[4]; +/*17264*/ int32 ldon_points_guk; // Earned GUK points +/*17268*/ int32 ldon_points_mir; // Earned MIR points +/*17272*/ int32 ldon_points_mmc; // Earned MMC points +/*17276*/ int32 ldon_points_ruj; // Earned RUJ points +/*17280*/ int32 ldon_points_tak; // Earned TAK points +/*17284*/ int32 ldon_points_available; // Available LDON points +/*17288*/ uint8 unknown14420[136]; //#### uint8 unknown14420[132]; in Titanium ####[136] +/*17424*/ float tribute_time_remaining; // Time remaining on tribute (millisecs) +/*17428*/ uint32 career_tribute_points; // Total favor points for this char +/*17432*/ uint32 unknown7208; // *** Placeholder +/*17436*/ uint32 tribute_points; // Current tribute points +/*17440*/ uint32 unknown7216; // *** Placeholder +/*17444*/ uint32 tribute_active; // 0 = off, 1=on +/*17448*/ Tribute_Struct tributes[MAX_PLAYER_TRIBUTES]; // [40] Current tribute loadout +/*17488*/ uint8 unknown14616[4]; +/*17492*/ double group_leadership_exp; // Current group lead exp points +/*17500*/ double raid_leadership_exp; // Current raid lead AA exp points +/*17508*/ uint32 group_leadership_points; // Unspent group lead AA points +/*17512*/ uint32 raid_leadership_points; // Unspent raid lead AA points +/*17516*/ LeadershipAA_Struct leader_abilities; // [128]Leader AA ranks +/*17644*/ uint8 unknown14772[128]; +/*17772*/ uint32 air_remaining; // Air supply (seconds) +/*17776*/ uint32 PVPKills; +/*17780*/ uint32 PVPDeaths; +/*17784*/ uint32 PVPCurrentPoints; +/*17788*/ uint32 PVPCareerPoints; +/*17792*/ uint32 PVPBestKillStreak; +/*17796*/ uint32 PVPWorstDeathStreak; +/*17800*/ uint32 PVPCurrentKillStreak; +/*17804*/ PVPStatsEntry_Struct PVPLastKill; // size 88 +/*17892*/ PVPStatsEntry_Struct PVPLastDeath; // size 88 +/*17980*/ uint32 PVPNumberOfKillsInLast24Hours; +/*17984*/ PVPStatsEntry_Struct PVPRecentKills[50]; // size 4400 - 88 each +/*22384*/ uint32 expAA; // Exp earned in current AA point +/*22388*/ uint8 unknown19516[40]; +/*22428*/ uint32 currentRadCrystals; // Current count of radiant crystals +/*22432*/ uint32 careerRadCrystals; // Total count of radiant crystals ever +/*22436*/ uint32 currentEbonCrystals; // Current count of ebon crystals +/*22440*/ uint32 careerEbonCrystals; // Total count of ebon crystals ever +/*22444*/ uint8 groupAutoconsent; // 0=off, 1=on +/*22445*/ uint8 raidAutoconsent; // 0=off, 1=on +/*22446*/ uint8 guildAutoconsent; // 0=off, 1=on +/*22447*/ uint8 unknown19575; // ***Placeholder (6/29/2005) +/*22448*/ uint32 level3; // SoF looks at the level here to determine how many leadership AA you can bank. +/*22452*/ uint32 showhelm; // 0=no, 1=yes +/*22456*/ uint32 RestTimer; +/*22460*/ uint8 unknown19584[1028]; // ***Placeholder (2/13/2007) was[1028]or[940]or[1380] - END of Struct +/*23488*/ +}; + +/** + * Shroud spawn. For others shrouding, this has their spawnId and + * spawnStruct. + * + * Length: 586 + * OpCode: OP_Shroud + */ +//struct spawnShroudOther_Struct +//{ +//*0000*/ uint32 spawnId; // Spawn Id of the shrouded player +//*0004*/ spawn_Struct spawn; // Updated spawn struct for the player +//*0586*/ +//}; + +/** + * Shroud yourself. For yourself shrouding, this has your spawnId, spawnStruct, + * bits of your charProfileStruct (no checksum, then charProfile up till + * but not including name), and an itemPlayerPacket for only items on the player + * and not the bank. + * + * Length: Variable + * OpCode: OP_Shroud + */ +//struct spawnShroudSelf_Struct +//{ +//*00000*/ uint32 spawnId; // Spawn Id of you +//*00004*/ spawn_Struct spawn; // Updated spawnStruct for you +//*00586*/ PlayerProfile_Struct profile; // Character profile for shrouded char +//*13522*/ uint8 items; // Items on the player +/*xxxxx*/ +//}; + + + +/* +** Client Target Struct +** Length: 2 Bytes +** OpCode: 6221 +*/ +struct ClientTarget_Struct { +/*000*/ uint32 new_target; // Target ID +}; + +/* +** Target Rejection Struct +** Length: 12 Bytes +** OpCode: OP_TargetReject +*/ +struct TargetReject_Struct { +/*00*/ uint8 unknown00[12]; +}; + +struct PetCommand_Struct { +/*000*/ uint32 command; +/*004*/ uint32 unknown; +}; + +/* +** Delete Spawn +** Length: 4 Bytes +** OpCode: OP_DeleteSpawn +*/ +struct DeleteSpawn_Struct +{ +/*00*/ uint32 spawn_id; // Spawn ID to delete +/*04*/ +}; + +/* +** Channel Message received or sent +** Length: 144 Bytes + Variable Length + 1 +** OpCode: OP_ChannelMessage +** +*/ +struct ChannelMessage_Struct +{ +/*000*/ char targetname[64]; // Tell recipient +/*064*/ char sender[64]; // The senders name (len might be wrong) +/*128*/ uint32 language; // Language +/*132*/ uint32 chan_num; // Channel +/*136*/ uint32 cm_unknown4[2]; // ***Placeholder +/*144*/ uint32 skill_in_language; // The players skill in this language? might be wrong +/*148*/ char message[0]; // Variable length message +}; + +/* +** Special Message +** Length: 4 Bytes + Variable Text Length + 1 +** OpCode: OP_SpecialMesg +** +*/ +/* + Theres something wrong with this... example live packet: +Server->Client: [ Opcode: OP_SpecialMesg (0x0fab) Size: 244 ] + 0: 01 02 00 0A 00 00 00 09 - 05 00 00 42 61 72 73 74 | ...........Barst + 16: 72 65 20 53 6F 6E 67 77 - 65 61 76 65 72 00 7C F9 | re Songweaver.|. + 32: FF FF 84 FF FF FF 03 00 - 00 00 47 72 65 65 74 69 | ..........Greeti + +*/ +struct SpecialMesg_Struct +{ +/*00*/ char header[3]; // 04 04 00 <-- for #emote style msg +/*03*/ uint32 msg_type; // Color of text (see MT_*** below) +/*07*/ uint32 target_spawn_id; // Who is it being said to? +/*11*/ char sayer[1]; // Who is the source of the info - Was 1 +/*12*/ uint8 unknown12[12]; +/*24*/ char message[128]; // What is being said? - was 128 +}; + +/* +** When somebody changes what they're wearing +** or give a pet a weapon (model changes) +** Length: 19 Bytes +*/ +struct WearChange_Struct{ +/*000*/ uint16 spawn_id; +/*002*/ uint32 material; +/*006*/ uint32 unknown06; +/*010*/ uint32 elite_material; // 1 for Drakkin Elite Material +/*014*/ Color_Struct color; +/*018*/ uint8 wear_slot_id; +/*019*/ +}; + +/* +** Type: Bind Wound Structure +** Length: 8 Bytes +*/ +//Fixed for 7-14-04 patch +struct BindWound_Struct +{ +/*000*/ uint16 to; // TargetID +/*002*/ uint16 unknown2; // ***Placeholder +/*004*/ uint16 type; +/*006*/ uint16 unknown6; +}; + + +/* +** Type: Zone Change Request (before hand) +** Length: 88 bytes +** OpCode: a320 +*/ + +struct ZoneChange_Struct { +/*000*/ char char_name[64]; // Character Name +/*064*/ uint16 zoneID; +/*066*/ uint16 instanceID; +/*068*/ float y; +/*072*/ float x; +/*076*/ float z; +/*080*/ uint32 zone_reason; //0x0A == death, I think +/*084*/ int32 success; // =0 client->server, =1 server->client, -X=specific error +/*088*/ +}; + +struct RequestClientZoneChange_Struct { +/*00*/ uint16 zone_id; +/*02*/ uint16 instance_id; +/*04*/ float y; +/*08*/ float x; +/*12*/ float z; +/*16*/ float heading; +/*20*/ uint32 type; //unknown... values +}; + +struct Animation_Struct { +/*00*/ uint16 spawnid; +/*02*/ uint8 action; +/*03*/ uint8 value; +/*04*/ +}; + +// solar: this is what causes the caster to animate and the target to +// get the particle effects around them when a spell is cast +// also causes a buff icon +struct Action_Struct +{ + /* 00 */ uint16 target; // id of target + /* 02 */ uint16 source; // id of caster + /* 04 */ uint16 level; // level of caster + /* 06 */ uint16 instrument_mod; // seems to be fixed to 0x0A + /* 08 */ uint32 unknown08; + /* 12 */ uint16 unknown16; +// some kind of sequence that's the same in both actions +// as well as the combat damage, to tie em together? + /* 14 */ float sequence; // was uint32 + /* 18 */ uint32 unknown18; + /* 22 */ uint8 type; // 231 (0xE7) for spells + /* 23 */ uint32 unknown23; + /* 27 */ uint16 spell; // spell id being cast + /* 29 */ uint8 level2; // level of caster again? Or maybe the castee +// this field seems to be some sort of success flag, if it's 4 + /* 30 */ uint8 buff_unknown; // if this is 4, a buff icon is made + /* 31 */ +}; + +// Starting with 2/21/2006, OP_Actions seem to come in pairs, duplicating +// themselves, with the second one with slightly more information. Maybe this +// has to do with buff blocking?? +struct ActionAlt_Struct // ActionAlt_Struct - Size: 56 bytes +{ +/*0000*/ uint16 target; // Target ID +/*0002*/ uint16 source; // SourceID +/*0004*/ uint16 level; // level of caster +/*0006*/ uint16 instrument_mod; // seems to be fixed to 0x0A +/*0008*/ uint32 unknown08; +/*0012*/ uint16 unknown16; +/*0014*/ uint32 sequence; +/*0018*/ uint32 unknown18; +/*0022*/ uint8 type; // Casts, Falls, Bashes, etc... +/*0023*/ uint32 damage; // Amount of Damage +/*0027*/ uint16 spell; // SpellID +/*0029*/ uint8 unknown29; +/*0030*/ uint8 buff_unknown; // if this is 4, a buff icon is made +/*0031*/ uint32 unknown0031; // seen 00 00 00 00 +/*0035*/ uint8 unknown0035; // seen 00 +/*0036*/ uint32 unknown0036; // seen ff ff ff ff +/*0040*/ uint32 unknown0040; // seen ff ff ff ff +/*0044*/ uint32 unknown0044; // seen ff ff ff ff +/*0048*/ uint32 unknown0048; // seen 00 00 00 00 +/*0052*/ uint32 unknown0052; // seen 00 00 00 00 +/*0056*/ +}; + +// solar: this is what prints the You have been struck. and the regular +// melee messages like You try to pierce, etc. It's basically the melee +// and spell damage message +struct CombatDamage_Struct +{ +/* 00 */ uint16 target; +/* 02 */ uint16 source; +/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells +/* 05 */ uint16 spellid; +/* 07 */ int32 damage; +/* 11 */ float unknown11; // cd cc cc 3d +/* 15 */ float sequence; // see above notes in Action_Struct +/* 19 */ uint8 unknown19[9]; // was [9] +/* 28 */ +}; + +/* +** Consider Struct +** Length: 20 Bytes +*/ +struct Consider_Struct{ +/*000*/ uint32 playerid; // PlayerID +/*004*/ uint32 targetid; // TargetID +/*008*/ uint32 faction; // Faction +/*012*/ uint32 level; // Level +/*016*/ uint8 pvpcon; // Pvp con flag 0/1 +/*017*/ uint8 unknown017[3]; // +/*020*/ +}; + +/* +** Spawn Death Blow +** Length: 32 Bytes +** OpCode: 0114 +*/ +struct Death_Struct +{ +/*000*/ uint32 spawn_id; +/*004*/ uint32 killer_id; +/*008*/ uint32 corpseid; // was corpseid +/*012*/ uint32 attack_skill; // was type +/*016*/ uint32 spell_id; +/*020*/ uint32 bindzoneid; //bindzoneid? +/*024*/ uint32 damage; +/*028*/ uint32 unknown028; +}; + +struct BecomeCorpse_Struct { + uint32 spawn_id; + float y; + float x; + float z; +}; + +struct ZonePlayerToBind_Struct { +/*000*/ uint16 bind_zone_id; +/*002*/ uint16 bind_instance_id; +/*004*/ float x; +/*008*/ float y; +/*012*/ float z; +/*016*/ float heading; +/*020*/ char zone_name[1]; // Or "Bind Location" +/*000*/ uint8 unknown021; // Seen 1 - Maybe 0 would be to force a rezone and 1 is just respawn +/*000*/ uint32 unknown022; // Seen 32 or 59 +/*000*/ uint32 unknown023; // Seen 0 +/*000*/ uint32 unknown024; // Seen 21 or 43 +}; + +struct ZonePlayerToBindHeader_Struct +{ + /*000*/ uint16 bind_zone_id; + /*002*/ uint16 bind_instance_id; + /*004*/ float x; + /*008*/ float y; + /*012*/ float z; + /*016*/ float heading; + /*020*/ char zone_name[1]; // Or "Bind Location" +}; + +struct ZonePlayerToBindFooter_Struct +{ + /*000*/ uint8 unknown021; // Seen 1 - Maybe 0 would be to force a rezone and 1 is just respawn + /*000*/ uint32 unknown022; // Seen 32 or 59 + /*000*/ uint32 unknown023; // Seen 0 + /*000*/ uint32 unknown024; // Seen 21 or 43 +}; + +typedef struct { +/*000*/ uint32 bind_number; // Number of this bind in the iteration +/*004*/ uint32 bind_zone_id; // ID of the zone for this bind point or resurect point +/*008*/ float x; // X loc for this bind point +/*012*/ float y; // Y loc for this bind point +/*016*/ float z; // Z loc for this bind point +/*020*/ float heading; // Heading for this bind point +/*024*/ char bind_zone_name[1]; // Or "Bind Location" or "Resurrect" +/*000*/ uint8 validity; // 0 = valid choice, 1 = not a valid choice at this time (resurrection) +} RespawnOptions_Struct; + +struct RespawnWindow_Struct { +/*000*/ uint32 unknown000; // Seen 0 +/*004*/ uint32 time_remaining; // Total time before respawn in milliseconds +/*008*/ uint32 unknown008; // Seen 0 +/*012*/ uint32 total_binds; // Total Bind Point Options? - Seen 2 +/*016*/ RespawnOptions_Struct bind_points; +// First bind point is "Bind Location" and the last one is "Ressurect" +}; + + +/* +** Spawn position update - Size: 22 +** Struct sent from server->client to update position of +** another spawn's position update in zone (whether NPC or PC) +** +*/ +struct PlayerPositionUpdateServer_Struct +{ +/*0000*/ uint16 spawn_id; +/*0002*/ signed padding0000:12; // ***Placeholder + signed delta_x:13; // change in x + signed padding0005:7; // ***Placeholder +/*0006*/ signed delta_heading:10;// change in heading + signed delta_y:13; // change in y + signed padding0006:9; // ***Placeholder +/*0010*/ signed y_pos:19; // y coord + signed animation:10; // animation + signed padding0010:3; // ***Placeholder +/*0014*/ unsigned heading:12; // heading + signed x_pos:19; // x coord + signed padding0014:1; // ***Placeholder +/*0018*/ signed z_pos:19; // z coord + signed delta_z:13; // change in z +/*0022*/ +}; + + +/* +** Player position update - Size: 40 +** Struct sent from client->server to update +** player position on server +** +*/ +struct PlayerPositionUpdateClient_Struct +{ +/*0000*/ uint16 spawn_id; // Player's spawn id +/*0002*/ uint16 sequence; // increments one each packet - Verified +/*0004*/ uint8 unknown0004[4]; // ***Placeholder +/*0008*/ float x_pos; // x coord (2nd loc value) +/*0012*/ float y_pos; // y coord (1st loc value) +/*0016*/ signed delta_heading:10; // change in heading + unsigned padding0036:10; // animation + unsigned padding0016:12; // ***Placeholder +/*0020*/ float delta_x; // Change in x +/*0024*/ float delta_y; // Change in y +/*0028*/ float z_pos; // z coord (3rd loc value) +/*0032*/ float delta_z; // Change in z +/*0036*/ unsigned animation:10; // ***Placeholder + unsigned heading:12; // Directional heading + unsigned padding0037:10; // ***Placeholder +/*0040*/ +}; + +/* +** Spawn HP Update +** Length: 10 Bytes +** OpCode: OP_HPUpdate +*/ +struct SpawnHPUpdate_Struct +{ +/*00*/ uint32 cur_hp; // Id of spawn to update +/*04*/ int32 max_hp; // Maximum hp of spawn +/*08*/ int16 spawn_id; // Current hp of spawn +/*10*/ +}; + +/* +** SendExpZonein +** Length: 152 Bytes +** OpCode: OP_SendExpZonein +*/ +struct SendExpZonein_Struct +{ +/*0000*/ uint16 spawn_id; // ID of the spawn +/*0002*/ uint16 type; // Values associated with the type +/*0004*/ uint32 parameter; // Type of data sent +/*0008*/ uint32 exp; // Current experience ratio from 0 to 330 +/*0012*/ uint32 expAA; +/*0016*/ uint8 unknown0016[4]; +/*0020*/ char name[64]; +/*0084*/ char last_name[64]; +/*00148*/ uint32 unknown132; +/*00152*/ +}; + +/* +** SendExpZonein +** Length: 0 Bytes +** OpCode: OP_SendExpZonein +*/ +//struct SendExpZonein_Struct {}; + +struct SpawnHPUpdate_Struct2 +{ +/*01*/ int16 spawn_id; +/*00*/ uint8 hp; +}; +/* +** Stamina +** Length: 8 Bytes +** OpCode: 5721 +*/ +struct Stamina_Struct { +/*00*/ uint32 food; // (low more hungry 127-0) +/*02*/ uint32 water; // (low more thirsty 127-0) +}; + +/* +** Level Update +** Length: 12 Bytes +*/ +struct LevelUpdate_Struct +{ +/*00*/ uint32 level; // New level +/*04*/ uint32 level_old; // Old level +/*08*/ uint32 exp; // Current Experience +}; + +/* +** Experience Update +** Length: 14 Bytes +** OpCode: 9921 +*/ +struct ExpUpdate_Struct +{ +/*0000*/ uint32 exp; // Current experience ratio from 0 to 330 +/*0004*/ uint32 aaxp; // @BP ?? +}; + +/* +** Item Packet Struct - Works on a variety of opcodes +** Packet Types: See ItemPacketType enum +** +*/ +enum ItemPacketType +{ + ItemPacketViewLink = 0x00, + ItemPacketTradeView = 0x65, + ItemPacketLoot = 0x66, + ItemPacketTrade = 0x67, + ItemPacketCharInventory = 0x69, + ItemPacketSummonItem = 0x6A, + ItemPacketTributeItem = 0x6C, + ItemPacketMerchant = 0x64, + ItemPacketWorldContainer = 0x6B +}; +struct ItemPacket_Struct +{ +/*00*/ ItemPacketType PacketType; +/*04*/ char SerializedItem[1]; //was 1 +/*xx*/ +}; + +struct BulkItemPacket_Struct +{ +/*00*/ char SerializedItem[0]; +/*xx*/ +}; + +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]; +/*0016*/ +}; + +struct ItemNamePacket_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 unkown004; +/*008*/ char name[64]; +/*072*/ +}; + +// Length: 16 +struct ItemProperties_Struct_Old { + +/*000*/ uint8 unknown01[2]; +/*002*/ uint8 charges; +/*003*/ uint8 unknown02[13]; +/*016*/ +}; + +// Length: 8 +struct ItemProperties_Struct { + +/*000*/ uint8 unknown01[4]; +/*004*/ uint8 charges; +/*005*/ uint8 unknown02[3]; +/*008*/ +}; + +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; +/*0012*/ +}; + +// +// from_slot/to_slot +// -1 - destroy +// 0 - cursor +// 1 - inventory +// 2 - bank +// 3 - trade +// 4 - shared bank +// +// cointype +// 0 - copeer +// 1 - silver +// 2 - gold +// 3 - platinum +// +static const uint32 COINTYPE_PP = 3; +static const uint32 COINTYPE_GP = 2; +static const uint32 COINTYPE_SP = 1; +static const uint32 COINTYPE_CP = 0; + +struct MoveCoin_Struct +{ + int32 from_slot; + int32 to_slot; + int32 cointype1; + int32 cointype2; + int32 amount; +}; +struct TradeCoin_Struct{ + uint32 trader; + uint8 slot; + uint16 unknown5; + uint8 unknown7; + uint32 amount; +}; +struct TradeMoneyUpdate_Struct{ + uint32 trader; + uint32 type; + uint32 amount; +}; +/* +** Surname struct +** Size: 100 bytes +*/ +struct Surname_Struct +{ +/*0000*/ char name[64]; +/*0064*/ uint32 unknown0064; +/*0068*/ char lastname[32]; +/*0100*/ +}; + +struct GuildsListEntry_Struct { + char name[64]; +}; + +static const uint32 MAX_NUMBER_GUILDS = 1500; +struct GuildsList_Struct { + uint8 head[64]; // First on guild list seems to be empty... + GuildsListEntry_Struct Guilds[MAX_NUMBER_GUILDS]; +}; + +struct GuildUpdate_Struct { + uint32 guildID; + GuildsListEntry_Struct entry; +}; + +/* +** Money Loot +** Length: 22 Bytes +** OpCode: 5020 +*/ +struct moneyOnCorpseStruct { +/*0000*/ uint8 response; // 0 = someone else is, 1 = OK, 2 = not at this time +/*0001*/ uint8 unknown1; // = 0x5a +/*0002*/ uint8 unknown2; // = 0x40 +/*0003*/ uint8 unknown3; // = 0 +/*0004*/ uint32 platinum; // Platinum Pieces +/*0008*/ uint32 gold; // Gold Pieces + +/*0012*/ uint32 silver; // Silver Pieces +/*0016*/ uint32 copper; // Copper Pieces +}; + +struct LootingItem_Struct { +/*000*/ uint32 lootee; +/*004*/ uint32 looter; +/*008*/ uint32 slot_id; +/*012*/ uint32 auto_loot; +/*016*/ uint32 unknown16; +/*020*/ +}; + +struct GuildManageStatus_Struct{ + uint32 guildid; + uint32 oldrank; + uint32 newrank; + char name[64]; +}; +// Guild invite, remove +struct GuildJoin_Struct{ +/*000*/ uint32 guild_id; +/*004*/ uint32 unknown04; +/*008*/ uint32 level; +/*012*/ uint32 class_; +/*016*/ uint32 rank;//0 member, 1 officer, 2 leader +/*020*/ uint32 zoneid; +/*024*/ uint32 unknown24; +/*028*/ char name[64]; +/*092*/ +}; +struct GuildInviteAccept_Struct { + char inviter[64]; + char newmember[64]; + uint32 response; + uint32 guildeqid; +}; +struct GuildManageRemove_Struct { + uint32 guildeqid; + char member[64]; +}; +struct GuildCommand_Struct { + char othername[64]; + char myname[64]; + uint16 guildeqid; + uint8 unknown[2]; // for guildinvite all 0's, for remove 0=0x56, 2=0x02 + uint32 officer; +}; + +// Opcode OP_GMZoneRequest +// Size = 88 bytes +struct GMZoneRequest_Struct { +/*0000*/ char charname[64]; +/*0064*/ uint32 zone_id; +/*0068*/ float x; +/*0072*/ float y; +/*0076*/ float z; +/*0080*/ char unknown0080[4]; +/*0084*/ uint32 success; // 0 if command failed, 1 if succeeded? +/*0088*/ +// /*072*/ int8 success; // =0 client->server, =1 server->client, -X=specific error +// /*073*/ uint8 unknown0073[3]; // =0 ok, =ffffff error +}; + +struct GMSummon_Struct { +/* 0*/ char charname[64]; +/* 30*/ char gmname[64]; +/* 60*/ uint32 success; +/* 61*/ uint32 zoneID; +/*92*/ int32 y; +/*96*/ int32 x; +/*100*/ int32 z; +/*104*/ uint32 unknown2; // E0 E0 56 00 +}; + +struct GMGoto_Struct { // x,y is swapped as compared to summon and makes sense as own packet +/* 0*/ char charname[64]; + +/* 64*/ char gmname[64]; +/* 128*/ uint32 success; +/* 132*/ uint32 zoneID; + +/*136*/ int32 y; +/*140*/ int32 x; +/*144*/ int32 z; +/*148*/ uint32 unknown2; // E0 E0 56 00 +}; + +struct GMLastName_Struct { + char name[64]; + char gmname[64]; + char lastname[64]; + uint16 unknown[4]; // 0x00, 0x00 + // 0x01, 0x00 = Update the clients +}; + +struct OnLevelMessage_Struct { +/*0000*/ char Title[128]; +/*0128*/ char Text[4096]; +/*4224*/ char ButtonName0[25]; // If Buttons = 1, these two are the text for the left and right buttons respectively +/*4249*/ char ButtonName1[25]; +/*4274*/ uint8 Buttons; +/*4275*/ uint8 Unknown4275; // Something to do with audio controls +/*4276*/ uint32 Duration; +/*4280*/ uint32 PopupID; // If none zero, a response packet with 00 00 00 00 is returned on clicking the left button +/*4284*/ uint32 NegativeID; // If none zero, a response packet with 01 00 00 00 is returned on clicking the right button +/*4288*/ uint32 Unknown4288; +/*4292*/ +}; + +//Combat Abilities +struct CombatAbility_Struct { + uint32 m_target; //the ID of the target mob + uint32 m_atk; + uint32 m_skill; +}; + +//Instill Doubt +struct Instill_Doubt_Struct { + uint8 i_id; + uint8 ia_unknown; + uint8 ib_unknown; + uint8 ic_unknown; + uint8 i_atk; + + uint8 id_unknown; + uint8 ie_unknown; + uint8 if_unknown; + uint8 i_type; + uint8 ig_unknown; + uint8 ih_unknown; + uint8 ii_unknown; +}; + +struct GiveItem_Struct { + uint16 to_entity; + int16 to_equipSlot; + uint16 from_entity; + int16 from_equipSlot; +}; + +struct RandomReq_Struct { + uint32 low; + uint32 high; +}; + +/* solar: 9/23/03 reply to /random command; struct from Zaphod */ +struct RandomReply_Struct { +/* 00 */ uint32 low; +/* 04 */ uint32 high; +/* 08 */ uint32 result; +/* 12 */ char name[64]; +/* 76 */ +}; + +/* +** LFG_Appearance_Struct +** Packet sent to clients to notify when someone in zone toggles LFG flag +** Size: 8 bytes +** Used in: OP_LFGAppearance +** +*/ +struct LFG_Appearance_Struct +{ +/*0000*/ uint32 spawn_id; // ID of the client +/*0004*/ uint8 lfg; // 1=LFG, 0=Not LFG +/*0005*/ char unknown0005[3]; // +/*0008*/ +}; + + +// EverQuest Time Information: +// 72 minutes per EQ Day +// 3 minutes per EQ Hour +// 6 seconds per EQ Tick (2 minutes EQ Time) +// 3 seconds per EQ Minute + +struct TimeOfDay_Struct { + uint8 hour; + uint8 minute; + uint8 day; + uint8 month; + uint16 year; +/*0006*/ uint16 unknown0016; // Placeholder +/*0008*/ +}; + +// Darvik: shopkeeper structs +struct Merchant_Click_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; +/*008*/ uint32 command; //1=open, 0=cancel/close +/*012*/ float rate; //cost multiplier, dosent work anymore +}; +/* +Unknowns: +0 is e7 from 01 to // MAYBE SLOT IN PURCHASE +1 is 03 +2 is 00 +3 is 00 +4 is ?? +5 is ?? +6 is 00 from a0 to +7 is 00 from 3f to */ +/* +0 is F6 to 01 +1 is CE CE +4A 4A +00 00 +00 E0 +00 CB +00 90 +00 3F +*/ + + + +struct Merchant_Sell_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; // Player's entity id +/*008*/ uint32 itemslot; +/*012*/ uint32 unknown12; +/*016*/ uint8 quantity; // Already sold +/*017*/ uint8 Unknown017[3]; +/*020*/ uint32 Unknown020; +/*024*/ uint32 price; +/*028*/ uint32 pricehighorderbits; // It appears the price is 64 bits in SoD+ +/*032*/ +}; + +struct Merchant_Purchase_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 itemslot; // Player's entity id +/*008*/ uint32 quantity; +/*012*/ uint32 price; +}; +struct Merchant_DelItem_Struct{ +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; // Player's entity id +/*008*/ uint32 itemslot; +}; +struct Adventure_Purchase_Struct { +/*000*/ uint32 some_flag; //set to 1 generally... +/*004*/ uint32 npcid; +/*008*/ uint32 itemid; +/*012*/ uint32 variable; +}; + +struct Adventure_Sell_Struct { +/*000*/ uint32 unknown000; //0x01 - Stack Size/Charges? +/*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]; +/*024*/ uint32 ldon_guk_points; // Earned Deepest Guk points +/*028*/ uint32 ldon_mirugal_points; // Earned Mirugal' Mebagerie points +/*032*/ uint32 ldon_mistmoore_points; // Earned Mismoore Catacombs Points +/*036*/ uint32 ldon_rujarkian_points; // Earned Rujarkian Hills points +/*040*/ uint32 ldon_takish_points; // Earned Takish points +/*044*/ uint8 unknown_apu042[216]; +}; + + +struct AdventureFinish_Struct{ + uint32 win_lose;//Cofruben: 00 is a lose,01 is win. + uint32 points; +}; +//OP_AdventureRequest +struct AdventureRequest_Struct{ + uint32 risk;//1 normal,2 hard. + uint32 entity_id; +}; +struct AdventureRequestResponse_Struct{ + uint32 unknown000; + char text[2048]; + uint32 timetoenter; + uint32 timeleft; + uint32 risk; + float x; + float y; + float z; + uint32 showcompass; + uint32 unknown2080; +}; + +//this is mostly right but something is off that causes the client to crash sometimes +//I don't really care enough about the feature to work on it anymore though. +struct AdventureLeaderboardEntry_Struct +{ +/*004*/ char name[64]; +/*008*/ uint32 success; +/*012*/ uint32 failure; +/*016*/ +}; + +struct AdventureLeaderboard_Struct +{ +/*000*/ uint32 unknown000; +/*004*/ uint32 unknown004; +/*008*/ uint32 success; +/*012*/ uint32 failure; +/*016*/ uint32 our_rank; +/*020*/ +}; + +/*struct Item_Shop_Struct { + uint16 merchantid; + uint8 itemtype; + Item_Struct item; + uint8 iss_unknown001[6]; +};*/ + +struct Illusion_Struct { //size: 256 +/*000*/ uint32 spawnid; +/*004*/ char charname[64]; // +/*068*/ uint16 race; // +/*070*/ char unknown006[2]; // Weird green name +/*072*/ uint8 gender; +/*073*/ uint8 texture; +/*074*/ uint8 unknown074; // +/*075*/ uint8 unknown075; // +/*076*/ uint8 helmtexture; // +/*077*/ uint8 unknown077; // +/*078*/ uint8 unknown078; // +/*079*/ uint8 unknown079; // +/*080*/ uint32 face; // +/*084*/ uint8 hairstyle; // Some Races don't change Hair Style Properly in SoF +/*085*/ uint8 haircolor; // +/*086*/ uint8 beard; // +/*087*/ uint8 beardcolor; // +/*088*/ float size; // +/*092*/ uint8 unknown092[148]; +/*240*/ uint32 unknown240; // Removes armor? +/*244*/ uint32 drakkin_heritage; // +/*248*/ uint32 drakkin_tattoo; // +/*252*/ uint32 drakkin_details; // +/*256*/ +}; + +struct ZonePoint_Entry { //24 octets +/*0000*/ uint32 iterator; +/*0004*/ float y; +/*0008*/ float x; +/*0012*/ float z; +/*0016*/ float heading; +/*0020*/ uint16 zoneid; +/*0022*/ uint16 zoneinstance; // LDoN instance +}; + +struct ZonePoints { +/*0000*/ uint32 count; +/*0004*/ struct ZonePoint_Entry zpe[0]; // Always add one extra to the end after all zonepoints +//*0xxx*/ uint8 unknown0xxx[24]; //New from SEQ +}; + +struct SkillUpdate_Struct { +/*00*/ uint32 skillId; +/*04*/ uint32 value; +/*08*/ +}; + +struct ZoneUnavail_Struct { + //This actually varies, but... + char zonename[16]; + int16 unknown[4]; +}; + +struct GroupInvite_Struct { +/*0000*/ char invitee_name[64]; +/*0064*/ char inviter_name[64]; +/*0128*/ uint32 unknown0128; +/*0132*/ uint32 unknown0132; +/*0136*/ uint32 unknown0136; +/*0140*/ uint32 unknown0140; +/*0144*/ uint32 unknown0144; +/*0148*/ +}; + +struct GroupGeneric_Struct { +/*0000*/ char name1[64]; +/*0064*/ char name2[64]; +/*0128*/ uint32 unknown0128; +/*0132*/ uint32 unknown0132; +/*0136*/ uint32 unknown0136; +/*0140*/ uint32 unknown0140; +/*0144*/ uint32 unknown0144; +/*0148*/ +}; + +struct GroupCancel_Struct { +/*000*/ char name1[64]; +/*064*/ char name2[64]; +/*128*/ uint8 unknown128[20]; +/*148*/ uint32 toggle; +/*152*/ +}; + +struct GroupUpdate_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +/*0452*/ +}; + +struct GroupUpdate2_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +/*0452*/ GroupLeadershipAA_Struct leader_aas; +/*0516*/ uint8 unknown[252]; // Titanium uses [188] here +/*0768*/ +}; + +struct GroupUpdate_Struct_SoD { // New for SoD +/*0000*/ uint32 groupid; // Guess - Matches unknown0136 from GroupFollow_Struct +/*0004*/ uint32 totalmembers; // Guess +/*0000*/ //uint32 leadersname[0]; // Group Leader Name Null Terminated +/*0008*/ //GroupMembers_Struct groupmembers; +}; + +struct GroupMembers_Struct { // New for SoD +/*0000*/ uint32 membernumber; // Guess - number of member in the group (0 to 5?) +/*0000*/ //char membername[0]; // Member Name Null Terminated +/*0000*/ uint8 unknown001[3]; // Seen 0 +/*0000*/ uint32 memberlevel; // Guess +/*0000*/ uint8 unknown002[11]; // Seen 0 +}; + +struct GroupJoin_Struct_SoD { // New for SoD +/*0000*/ uint32 unknown0000; // Matches unknown0136 from GroupFollow_Struct +/*0004*/ uint32 action; +/*0008*/ uint8 unknown0008[5]; // Seen 0 +/*0013*/ //char membername[0]; // Null Terminated? +/*0000*/ uint8 unknown0013[3]; // Seen 0 +/*0000*/ uint32 unknown0016; // Matches unknown0132 from GroupFollow_Struct +/*0000*/ uint8 unknown0020[11]; // Seen 0 +}; + +struct GroupJoin_Struct { +/*000*/ char unknown000[64]; +/*064*/ char membername[64]; +/*128*/ uint8 unknown128[20]; // Leadership AA ? +/*148*/ +}; + +struct GroupFollow_Struct { // SoD Follow Struct +/*0000*/ char name1[64]; // inviter +/*0064*/ char name2[64]; // invitee +/*0128*/ uint32 unknown0128; // Seen 0 +/*0132*/ uint32 unknown0132; // Group ID or member level? +/*0136*/ uint32 unknown0136; // Maybe Voice Chat Channel or Group ID? +/*0140*/ uint32 unknown0140; // Seen 0 +/*0144*/ uint32 unknown0144; // Seen 0 +/*0148*/ uint32 unknown0148; +/*0152*/ +}; + +struct LFG_Struct { +/*000*/ uint32 unknown000; +/*004*/ uint32 value; // 0x00 = off 0x01 = on +/*008*/ uint32 unknown008; +/*012*/ uint32 unknown012; +/*016*/ char name[64]; +}; + +struct FaceChange_Struct { +/*000*/ uint8 haircolor; +/*001*/ uint8 beardcolor; +/*002*/ uint8 eyecolor1; +/*003*/ uint8 eyecolor2; +/*004*/ uint8 hairstyle; +/*005*/ uint8 beard; +/*006*/ uint8 face; +/*007*/ uint8 unknown007; +/*008*/ uint32 drakkin_heritage; +/*012*/ uint32 drakkin_tattoo; +/*016*/ uint32 drakkin_details; +/*020*/ uint32 unknown020; +/*024*/ +}; +//there are only 10 faces for barbs changing woad just +//increase the face value by ten so if there were 8 woad +//designs then there would be 80 barb faces + +/* +** Trade request from one client to another +** Used to initiate a trade +** Size: 8 bytes +** Used in: OP_TradeRequest +*/ +struct TradeRequest_Struct { +/*00*/ uint32 to_mob_id; +/*04*/ uint32 from_mob_id; +/*08*/ +}; + +struct TradeAccept_Struct { +/*00*/ uint32 from_mob_id; +/*04*/ uint32 unknown4; //seems to be garbage +/*08*/ +}; + +/* +** Cancel Trade struct +** Sent when a player cancels a trade +** Size: 8 bytes +** Used In: OP_CancelTrade +** +*/ +struct CancelTrade_Struct { +/*00*/ uint32 fromid; +/*04*/ uint32 action; +/*08*/ +}; + +struct PetitionUpdate_Struct { + uint32 petnumber; // Petition Number + uint32 color; // 0x00 = green, 0x01 = yellow, 0x02 = red + uint32 status; + time_t senttime; // 4 has to be 0x1F + char accountid[32]; + char gmsenttoo[64]; + int32 quetotal; + char charname[64]; +}; + +struct Petition_Struct { + uint32 petnumber; + uint32 urgency; + char accountid[32]; + char lastgm[32]; + uint32 zone; + //char zone[32]; + char charname[64]; + uint32 charlevel; + uint32 charclass; + uint32 charrace; + uint32 unknown; + //time_t senttime; // Time? + uint32 checkouts; + uint32 unavail; + //uint8 unknown5[4]; + time_t senttime; + uint32 unknown2; + char petitiontext[1024]; + char gmtext[1024]; +}; + + +struct Who_All_Struct { // 76 length total +/*000*/ char whom[64]; +/*064*/ uint32 wrace; // FF FF = no race + +/*068*/ uint32 wclass; // FF FF = no class +/*072*/ uint32 lvllow; // FF FF = no numbers +/*076*/ uint32 lvlhigh; // FF FF = no numbers +/*080*/ uint32 gmlookup; // FF FF = not doing /who all gm +/*084*/ uint32 guildid; // Also used for Buyer/Trader/LFG +/*088*/ uint8 unknown088[64]; +/*156*/ uint32 type; // 0 = /who 3 = /who all +}; + +struct Stun_Struct { // 8 bytes total +/*000*/ uint32 duration; // Duration of stun +/*004*/ uint8 unknown004; // seen 0 +/*005*/ uint8 unknown005; // seen 163 +/*006*/ uint8 unknown006; // seen 67 +/*007*/ uint8 unknown007; // seen 0 +/*008*/ +}; + +struct AugmentItem_Struct { +/*00*/ int16 container_slot; +/*02*/ char unknown02[2]; +/*04*/ int32 augment_slot; +/*08*/ +}; + +// OP_Emote +struct Emote_Struct { +/*0000*/ uint32 unknown01; +/*0004*/ char message[1024]; // was 1024 +/*1028*/ +}; + +// Inspect +struct Inspect_Struct { + uint32 TargetID; + uint32 PlayerID; +}; + +//OP_InspectAnswer - Size: 1860 +struct InspectResponse_Struct{ +/*000*/ uint32 TargetID; +/*004*/ uint32 playerid; +/*008*/ char itemnames[23][64]; +/*1480*/uint32 itemicons[23]; +/*1572*/char text[288]; // Max number of chars in Inspect Window appears to be 254 +/*1860*/ +}; + +//OP_SetDataRate +struct SetDataRate_Struct { + float newdatarate; +}; + +//OP_SetServerFilter +struct SetServerFilter_Struct { + uint32 filters[32]; //see enum eqFilterType [31] +}; + +//Op_SetServerFilterAck +struct SetServerFilterAck_Struct { + uint8 blank[8]; +}; +struct IncreaseStat_Struct{ + /*0000*/ uint8 unknown0; + /*0001*/ uint8 str; + /*0002*/ uint8 sta; + /*0003*/ uint8 agi; + /*0004*/ uint8 dex; + /*0005*/ uint8 int_; + /*0006*/ uint8 wis; + /*0007*/ uint8 cha; + /*0008*/ uint8 fire; + /*0009*/ uint8 cold; + /*0010*/ uint8 magic; + /*0011*/ uint8 poison; + /*0012*/ uint8 disease; + /*0013*/ char unknown13[116]; + /*0129*/ uint8 str2; + /*0130*/ uint8 sta2; + /*0131*/ uint8 agi2; + /*0132*/ uint8 dex2; + /*0133*/ uint8 int_2; + /*0134*/ uint8 wis2; + /*0135*/ uint8 cha2; + /*0136*/ uint8 fire2; + /*0137*/ uint8 cold2; + /*0138*/ uint8 magic2; + /*0139*/ uint8 poison2; + /*0140*/ uint8 disease2; +}; + +struct GMName_Struct { + char oldname[64]; + char gmname[64]; + char newname[64]; + uint8 badname; + uint8 unknown[3]; +}; + +struct GMDelCorpse_Struct { + char corpsename[64]; + char gmname[64]; + uint8 unknown; +}; + +struct GMKick_Struct { + char name[64]; + char gmname[64]; + uint8 unknown; +}; + + +struct GMKill_Struct { + char name[64]; + char gmname[64]; + uint8 unknown; +}; + + +struct GMEmoteZone_Struct { + char text[512]; +}; + +// The BookText_Struct is not used in SoF and later clients. +// The BookRequest_Struct is used instead for both request and reply. +// +struct BookText_Struct { + uint8 window; // where to display the text (0xFF means new window) + uint8 type; //type: 0=scroll, 1=book, 2=item info.. prolly + char booktext[1]; // Variable Length - was 1 +}; +// This is the request to read a book. +// This is just a "text file" on the server +// or in our case, the 'name' column in our books table. +struct BookRequest_Struct { +/*0000*/ uint32 window; // where to display the text (0xFFFFFFFF means new window). +/*0004*/ uint32 invslot; // The inventory slot the book is in. Not used, but echoed in the response packet. +/*0008*/ uint32 type; // 0 = Scroll, 1 = Book, 2 = Item Info. Possibly others +/*0012*/ uint32 unknown0012; +/*0016*/ uint16 unknown0016; +/*0018*/ char txtfile[8194]; +}; + +/* +** Object/Ground Spawn struct +** Used for Forges, Ovens, ground spawns, items dropped to ground, etc +** Size: 104 bytes +** OpCodes: OP_CreateObject +** Last Updated: Oct-17-2003 +** +*/ +struct Object_Struct { +/*00*/ uint32 linked_list_addr[2];// They are, get this, prev and next, ala linked list +/*08*/ uint32 unknown008; // Something related to the linked list? +/*12*/ uint32 drop_id; // Unique object id for zone +/*16*/ uint16 zone_id; // Redudant, but: Zone the object appears in +/*18*/ uint16 zone_instance; // +/*20*/ uint32 unknown020; // 00 00 00 00 +/*24*/ uint32 unknown024; // 53 9e f9 7e - same for all objects in the zone? +/*40*/ float heading; // heading +/*32*/ uint8 unknown032[8]; // 00 00 00 00 00 00 00 00 +/*28*/ float size; // Size - default 1 +/*44*/ float z; // z coord +/*48*/ float x; // x coord +/*52*/ float y; // y coord +/*56*/ char object_name[32]; // Name of object, usually something like IT63_ACTORDEF was [20] +/*88*/ uint32 unknown088; // unique ID? Maybe for a table that includes the contents? +/*92*/ uint32 object_type; // Type of object, not directly translated to OP_OpenObject +/*96*/ uint8 unknown096[4]; // ff ff ff ff +/*100*/ uint32 spawn_id; // Spawn Id of client interacting with object +/*104*/ +}; +// 01 = generic drop, 02 = armor, 19 = weapon +//[13:40] and 0xff seems to be indicative of the tradeskill/openable items that end up returning the old style item type in the OP_OpenObject + +/* +** Click Object Struct +** Client clicking on zone object (forge, groundspawn, etc) +** Size: 8 bytes +** Last Updated: Oct-17-2003 +** +*/ +struct ClickObject_Struct { +/*00*/ uint32 drop_id; +/*04*/ uint32 player_id; +/*08*/ +}; + +struct Shielding_Struct { + uint32 target_id; +}; + +/* +** Click Object Acknowledgement Struct +** Response to client clicking on a World Container (ie, forge) +** +*/ +struct ClickObjectAction_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 type; // See object.h, "Object Types" +/*16*/ uint32 unknown16; // +/*20*/ uint32 icon; // Icon to display for tradeskill containers +/*24*/ uint32 unknown24; // +/*28*/ char object_name[64]; // Object name to display +/*92*/ +}; + +/* +** This is different now, mostly unknown +** +*/ +struct CloseContainer_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 unknown12[12]; +}; + +/* +** Generic Door Struct +** Length: 52 Octets +** Used in: +** cDoorSpawnsStruct(f721) +** +*/ +struct Door_Struct +{ +/*0000*/ char name[32]; // Filename of Door // Was 10char long before... added the 6 in the next unknown to it: Daeken M. BlackBlade +/*0032*/ float yPos; // y loc +/*0036*/ float xPos; // x loc +/*0040*/ float zPos; // z loc +/*0044*/ float heading; +/*0048*/ uint32 incline; // rotates the whole door +/*0052*/ uint32 size; // 100 is normal, smaller number = smaller model +/*0054*/ uint8 unknown0054[4]; // 00 00 00 00 +/*0060*/ uint8 doorId; // door's id # +/*0061*/ uint8 opentype; +/*0062*/ uint8 state_at_spawn; +/*0063*/ uint8 invert_state; // if this is 1, the door is normally open +/*0064*/ uint32 door_param; // normally ff ff ff ff (-1) +/*0068*/ uint32 unknown0068; // 00 00 00 00 +/*0072*/ uint32 unknown0072; // 00 00 00 00 +/*0076*/ uint8 unknown0076; // seen 1 or 0 +/*0077*/ uint8 unknown0077; // seen 1 (always?) +/*0078*/ uint8 unknown0078; // seen 0 (always?) +/*0079*/ uint8 unknown0079; // seen 1 (always?) +/*0080*/ uint8 unknown0080; // seen 0 (always?) +/*0081*/ uint8 unknown0081; // seen 1 or 0 or rarely 2C or 90 or ED or 2D or A1 +/*0082*/ uint8 unknown0082; // seen 0 or rarely FF or FE or 10 or 5A or 82 +/*0083*/ uint8 unknown0083; // seen 0 or rarely 02 or 7C +/*0084*/ uint8 unknown0084[8]; // mostly 0s, the last 3 bytes are something tho +/*0092*/ +}; + +struct DoorSpawns_Struct { + struct Door_Struct doors[0]; +}; + +/* + OP Code: Op_ClickDoor + Size: 16 +*/ +struct ClickDoor_Struct { +/*000*/ uint8 doorid; +/*001*/ uint8 unknown001; // This may be some type of action setting +/*002*/ uint8 unknown002; // This is sometimes set after a lever is closed +/*003*/ uint8 unknown003; // Seen 0 +/*004*/ uint8 picklockskill; +/*005*/ uint8 unknown005[3]; +/*008*/ uint32 item_id; +/*012*/ uint16 player_id; +/*014*/ uint8 unknown014[2]; +/*016*/ +}; + +struct MoveDoor_Struct { + uint8 doorid; + uint8 action; +}; + + +struct BecomeNPC_Struct { + uint32 id; + int32 maxlevel; +}; + +struct Underworld_Struct { + float speed; + float y; + float x; + float z; +}; + +struct Resurrect_Struct { + uint32 unknown00; + uint16 zone_id; + uint16 instance_id; + float y; + float x; + float z; + char your_name[64]; + uint32 unknown88; + char rezzer_name[64]; + uint32 spellid; + char corpse_name[64]; + uint32 action; +/* 228 */ +}; + +struct SetRunMode_Struct { + uint8 mode; //01=run 00=walk + uint8 unknown[3]; +}; + +//EnvDamage is EnvDamage2 without a few bytes at the end. + +struct EnvDamage2_Struct { +/*0000*/ uint32 id; +/*0004*/ uint16 unknown4; +/*0006*/ uint32 damage; +/*0010*/ uint8 unknown10[12]; +/*0022*/ uint8 dmgtype; //FA = Lava; FC = Falling +/*0023*/ uint8 unknown2[4]; +/*0027*/ uint16 constant; //Always FFFF +/*0029*/ uint16 unknown29; +}; + + +//Bazaar Stuff =D +// + +enum { + BazaarTrader_StartTraderMode = 1, + BazaarTrader_EndTraderMode = 2, + BazaarTrader_UpdatePrice = 3, + BazaarTrader_EndTransaction = 4, + BazaarSearchResults = 7, + BazaarWelcome = 9, + BazaarBuyItem = 10, + BazaarTrader_ShowItems = 11, + BazaarSearchDone = 12, + BazaarTrader_CustomerBrowsing = 13 +}; + +enum { + BazaarPriceChange_Fail = 0, + BazaarPriceChange_UpdatePrice = 1, + BazaarPriceChange_RemoveItem = 2, + BazaarPriceChange_AddItem = 3 +}; + +struct BazaarWindowStart_Struct { + uint8 Action; + uint8 Unknown001; + uint16 Unknown002; +}; + + +struct BazaarWelcome_Struct { + BazaarWindowStart_Struct Beginning; + uint32 Traders; + uint32 Items; + uint8 Unknown012[8]; +}; + +struct BazaarSearch_Struct { + BazaarWindowStart_Struct Beginning; + uint32 TraderID; + uint32 Class_; + uint32 Race; + uint32 ItemStat; + uint32 Slot; + uint32 Type; + char Name[64]; + uint32 MinPrice; + uint32 MaxPrice; + uint32 Minlevel; + uint32 MaxLlevel; +}; +struct BazaarInspect_Struct{ + uint32 ItemID; + uint32 Unknown004; + char Name[64]; +}; + +struct NewBazaarInspect_Struct { +/*000*/ BazaarWindowStart_Struct Beginning; +/*004*/ char Name[64]; +/*068*/ uint32 Unknown068; +/*072*/ int32 SerialNumber; +/*076*/ uint32 Unknown076; +/*080*/ uint32 SellerID; +/*084*/ uint32 Unknown084; +}; + +struct BazaarReturnDone_Struct{ + uint32 Type; + uint32 TraderID; + uint32 Unknown008; + uint32 Unknown012; + uint32 Unknown016; +}; + +struct BazaarSearchResults_Struct { +/*000*/ BazaarWindowStart_Struct Beginning; +/*004*/ uint32 SellerID; +/*008*/ char SellerName[64]; +/*072*/ uint32 NumItems; +/*076*/ uint32 ItemID; +/*080*/ uint32 SerialNumber; +/*084*/ uint32 Unknown084; +/*088*/ char ItemName[64]; +/*152*/ uint32 Cost; +/*156*/ uint32 ItemStat; +/*160*/ +}; + +struct ServerSideFilters_Struct { +uint8 clientattackfilters; // 0) No, 1) All (players) but self, 2) All (players) but group +uint8 npcattackfilters; // 0) No, 1) Ignore NPC misses (all), 2) Ignore NPC Misses + Attacks (all but self), 3) Ignores NPC Misses + Attacks (all but group) +uint8 clientcastfilters; // 0) No, 1) Ignore PC Casts (all), 2) Ignore PC Casts (not directed towards self) +uint8 npccastfilters; // 0) No, 1) Ignore NPC Casts (all), 2) Ignore NPC Casts (not directed towards self) +}; + +/* +** Client requesting item statistics +** Size: 48 bytes +** Used In: OP_ItemLinkClick +** Last Updated: 2/15/2009 +** +*/ +struct ItemViewRequest_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 augments[5]; +/*024*/ uint32 link_hash; +/*028*/ uint32 unknown028; //seems to always be 4 on SoF client +/*032*/ char unknown032[12]; //probably includes loregroup & evolving info. see Client::MakeItemLink() in zone/inventory.cpp:469 +/*044*/ uint16 icon; +/*046*/ char unknown046[2]; +}; + +/* + * Client to server packet + */ +struct PickPocket_Struct { +// Size 18 + uint32 to; + uint32 from; + uint16 myskill; + uint8 type; // -1 you are being picked, 0 failed , 1 = plat, 2 = gold, 3 = silver, 4 = copper, 5 = item + uint8 unknown1; // 0 for response, unknown for input + uint32 coin; + uint8 lastsix[2]; +}; +/* + * Server to client packet + */ + +struct sPickPocket_Struct { + // Size 28 = coin/fail + uint32 to; + uint32 from; + uint32 myskill; + uint32 type; + uint32 coin; + char itemname[64]; +}; + + +struct LogServer_Struct { +// Op_Code OP_LOGSERVER +/*000*/ uint32 unknown000; +/*004*/ uint8 enable_pvp; +/*005*/ uint8 unknown005; +/*006*/ uint8 unknown006; +/*007*/ uint8 unknown007; +/*008*/ uint8 enable_FV; +/*009*/ uint8 unknown009; +/*010*/ uint8 unknown010; +/*011*/ uint8 unknown011; +/*012*/ uint32 unknown012; // htonl(1) on live +/*016*/ uint32 unknown016; // htonl(1) on live +/*020*/ uint8 unknown020[12]; +/*032*/ char worldshortname[32]; +/*064*/ uint8 unknown064[32]; +/*096*/ char unknown096[16]; // 'pacman' on live +/*112*/ char unknown112[16]; // '64.37,148,36' on live +/*126*/ uint8 unknown128[48]; +/*176*/ uint32 unknown176; // htonl(0x00002695) +/*180*/ char unknown180[80]; // 'eqdataexceptions@mail.station.sony.com' on live +/*260*/ uint8 unknown260; // 0x01 on live +/*261*/ uint8 enablevoicemacros; +/*262*/ uint8 enablemail; +/*263*/ uint8 unknown263[16]; +/*279*/ +}; + +struct ApproveWorld_Struct { +// Size 544 +// Op_Code OP_ApproveWorld + uint8 unknown544[544]; +}; + +struct ClientError_Struct +{ +/*00001*/ char type; +/*00001*/ char unknown0001[69]; +/*00069*/ char character_name[64]; +/*00134*/ char unknown134[192]; +/*00133*/ char message[31994]; +/*32136*/ +}; + +struct MobHealth +{ + /*0000*/ uint8 hp; //health percent + /*0001*/ uint16 id;//mobs id +}; + +struct Track_Struct { + uint16 entityid; + uint16 y; + uint16 x; + uint16 z; +}; + +struct Tracking_Struct { + Track_Struct Entrys[0]; +}; + +// Looks like new tracking structures - Opcode: 0x57a7 +struct Tracking_Struct_New { + uint16 totalcount; // Total Count of mobs within tracking range + Track_Struct Entrys[0]; +}; + +struct Track_Struct_New { + uint16 entityid; // Entity ID + uint16 unknown002; // 00 00 + uint32 unknown004; // + uint8 level; // level of mob + uint8 unknown009; // 01 maybe type of mob? player/npc? + char name[1]; // name of mob +}; + + +/* +** ZoneServerInfo_Struct +** Zone server information +** Size: 130 bytes +** Used In: OP_ZoneServerInfo +** +*/ +struct ZoneServerInfo_Struct +{ +/*0000*/ char ip[128]; +/*0128*/ uint16 port; +}; + +struct WhoAllPlayer{ + uint32 formatstring; + uint32 pidstring; + char* name; + uint32 rankstring; + char* guild; + uint32 unknown80[2]; + uint32 zonestring; + uint32 zone; + uint32 class_; + uint32 level; + uint32 race; + char* account; + uint32 unknown100; +}; + +struct WhoAllReturnStruct { + uint32 id; + uint32 playerineqstring; + char line[27]; + uint8 unknown35; //0A + uint32 unknown36;//0s + uint32 playersinzonestring; + uint32 unknown44[2]; //0s + uint32 unknown52;//1 + uint32 unknown56;//1 + uint32 playercount;//1 + struct WhoAllPlayer player[0]; +}; + +// The following four structs are the WhoAllPlayer struct above broken down +// for use in World ClientList::SendFriendsWho to accomodate the user of variable +// length strings within the struct above. + +struct WhoAllPlayerPart1 { + uint32 FormatMSGID; + uint32 Unknown04; + uint32 Unknown08; + char Name[1]; +}; + +struct WhoAllPlayerPart2 { + uint32 RankMSGID; + char Guild[1]; +}; + +struct WhoAllPlayerPart3 { + uint32 Unknown80[2]; + uint32 ZoneMSGID; + uint32 Zone; + uint32 Class_; + uint32 Level; + uint32 Race; + char Account[1]; +}; + +struct WhoAllPlayerPart4 { + uint32 Unknown100; +}; + +struct Trader_Struct { + uint32 code; + uint32 itemid[160]; + uint32 unknown; + uint32 itemcost[80]; +}; + +struct ClickTrader_Struct { + uint32 code; + uint32 unknown[161];//damn soe this is totally pointless :/ but at least your finally using memset! Good job :) -LE + uint32 itemcost[80]; +}; + +struct GetItems_Struct{ + uint32 items[80]; +}; + +struct BecomeTrader_Struct{ + uint32 id; + uint32 code; +}; + +struct Trader_ShowItems_Struct{ + uint32 code; + uint32 traderid; + uint32 unknown08[3]; +}; + +struct TraderBuy_Struct { +/*000*/ uint32 Action; +/*004*/ uint32 Unknown004; +/*008*/ uint32 Price; +/*012*/ uint32 Unknown008; // Probably high order bits of a 64 bit price. +/*016*/ uint32 TraderID; +/*020*/ char ItemName[64]; +/*084*/ uint32 Unknown076; +/*088*/ uint32 ItemID; +/*092*/ uint32 AlreadySold; +/*096*/ uint32 Quantity; +/*100*/ uint32 Unknown092; +/*104*/ +}; + +struct TraderItemUpdate_Struct{ + uint32 unknown0; + uint32 traderid; + uint8 fromslot; + uint8 toslot; //7? + uint16 charges; +}; + +struct MoneyUpdate_Struct{ + int32 platinum; + int32 gold; + int32 silver; + int32 copper; +}; + +//struct MoneyUpdate_Struct +//{ +//*0000*/ uint32 spawn_id; // ***Placeholder +//*0004*/ uint32 cointype; // Coin Type +//*0008*/ uint32 amount; // Amount +//*0012*/ +//}; + + +struct TraderDelItem_Struct{ + uint32 slotid; + uint32 quantity; + uint32 unknown; +}; + +struct TraderClick_Struct{ + uint32 traderid; + uint32 unknown4[2]; + uint32 approval; +}; + +struct FormattedMessage_Struct{ + uint32 unknown0; + uint32 string_id; + uint32 type; + char message[0]; +//*0???*/ uint8 unknown0[8]; // ***Placeholder +}; +struct SimpleMessage_Struct{ + uint32 string_id; + uint32 color; + uint32 unknown8; +}; + +struct GuildMemberEntry_Struct { + char name[1]; //variable length + uint32 level; //network byte order + uint32 banker; //1=yes, 0=no, network byte order + uint32 class_; //network byte order + uint32 rank; //network byte order + uint32 time_last_on; //network byte order + uint32 tribute_enable; //network byte order + uint32 total_tribute; //total guild tribute donated, network byte order + uint32 last_tribute; //unix timestamp + uint32 unknown_one; //unknown, set to one. (network byte order) + char public_note[1]; //variable length. + uint16 zoneinstance; //network byte order + uint16 zone_id; //network byte order +/* 42 + strings */ +}; + +struct GuildMembers_Struct { //just for display purposes, this is not actually used in the message encoding. + char player_name[1]; //variable length. + uint32 count; //network byte order + GuildMemberEntry_Struct member[0]; +}; + +struct GuildMOTD_Struct{ +/*0000*/ uint32 unknown0; +/*0004*/ char name[64]; +/*0068*/ char setby_name[64]; +/*0132*/ uint32 unknown132; +/*0136*/ char motd[0]; //was 512 +}; + +struct GuildURL_Struct{ +/*0000*/ uint32 unknown0; //index? seen server send 0 w/ the Guild URL, followed by 1 with nothing. +/*0004*/ uint32 unknown4; +/*0008*/ uint32 unknown8; //seen 7 +/*0068*/ char setby_name[64]; +/*0132*/ uint32 unknown132; //seen 0x167 +/*0136*/ char url[4080]; +}; + +struct GuildMemberUpdate_Struct { +/*00*/ uint32 guild_id; +/*04*/ char member_name[64]; +/*68*/ uint16 zone_id; +/*70*/ uint16 instance_id; +/*72*/ uint32 some_timestamp; +}; + +struct GuildMemberLevelUpdate_Struct { +/*00*/ uint32 guild_id; +/*04*/ char member_name[64]; +/*68*/ uint32 level; //not sure +}; + + + +struct GuildUpdate_PublicNote{ + uint32 unknown0; + char name[64]; + char target[64]; + char note[100]; //we are cutting this off at 100, actually around 252 +}; +struct GuildDemoteStruct{ + char name[64]; + char target[64]; +}; +struct GuildRemoveStruct{ + char target[64]; + char name[64]; + uint32 unknown128; + uint32 leaderstatus; //? +}; +struct GuildMakeLeader{ + char name[64]; + char target[64]; +}; + +struct BugStruct{ +/*0000*/ uint32 type1; //seems to be just a different way of seeing type; seems to be ordered completely differently +/*0004*/ char chartype[64]; +/*0068*/ char name[96]; +/*0164*/ char ui[128]; +/*0292*/ float x; +/*0296*/ float y; +/*0300*/ float z; +/*0304*/ float heading; +/*0308*/ uint32 unknown304; +/*0312*/ char unknown308[160]; +/*0472*/ char target_name[64]; +/*0536*/ uint32 type; +/*0540*/ char unknown536[2052]; +/*2588*/ char bug[2048]; +/*4636*/ char unknown4632[6]; +/*4642*/ char system_info[4094]; +}; +struct Make_Pet_Struct { //Simple struct for getting pet info + uint8 level; + uint8 class_; + uint16 race; + uint8 texture; + uint8 pettype; + float size; + uint8 type; + uint32 min_dmg; + uint32 max_dmg; +}; +struct Ground_Spawn{ + float max_x; + float max_y; + float min_x; + float min_y; + float max_z; + float heading; + char name[16]; + uint32 item; + uint32 max_allowed; + uint32 respawntimer; +}; +struct Ground_Spawns { + struct Ground_Spawn spawn[50]; //Assigned max number to allow +}; +struct PetitionBug_Struct{ + uint32 petition_number; + uint32 unknown4; + char accountname[64]; + uint32 zoneid; + char name[64]; + uint32 level; + uint32 class_; + uint32 race; + uint32 unknown152[3]; + uint32 time; + uint32 unknown168; + char text[1028]; +}; + +struct DyeStruct +{ + union + { + struct + { + struct Color_Struct head; + struct Color_Struct chest; + struct Color_Struct arms; + struct Color_Struct wrists; + struct Color_Struct hands; + struct Color_Struct legs; + struct Color_Struct feet; + struct Color_Struct primary; // you can't actually dye this + struct Color_Struct secondary; // or this + } + dyes; + struct Color_Struct dye[9]; + }; +}; + +struct ApproveZone_Struct { + char name[64]; + uint32 zoneid; + uint32 approve; +}; +struct ZoneInSendName_Struct { + uint32 unknown0; + char name[64]; + char name2[64]; + uint32 unknown132; +}; +struct ZoneInSendName_Struct2 { + uint32 unknown0; + char name[64]; + uint32 unknown68[145]; +}; + +static const uint32 MAX_TRIBUTE_TIERS = 10; + +struct StartTribute_Struct { + uint32 client_id; + uint32 tribute_master_id; + uint32 response; +}; + +struct TributeLevel_Struct { + uint32 level; //backwards byte order! + uint32 tribute_item_id; //backwards byte order! + uint32 cost; //backwards byte order! +}; + +struct TributeAbility_Struct { + uint32 tribute_id; //backwards byte order! + uint32 tier_count; //backwards byte order! + TributeLevel_Struct tiers[MAX_TRIBUTE_TIERS]; + char name[0]; +}; + +struct GuildTributeAbility_Struct { + uint32 guild_id; + TributeAbility_Struct ability; +}; + +struct SelectTributeReq_Struct { + uint32 client_id; //? maybe action ID? + uint32 tribute_id; + uint32 unknown8; //seen E3 00 00 00 +}; + +struct SelectTributeReply_Struct { + uint32 client_id; //echoed from request. + uint32 tribute_id; + char desc[0]; +}; + +struct TributeInfo_Struct { + uint32 active; //0 == inactive, 1 == active + uint32 tributes[MAX_PLAYER_TRIBUTES]; //-1 == NONE + uint32 tiers[MAX_PLAYER_TRIBUTES]; //all 00's + uint32 tribute_master_id; +}; + +struct TributeItem_Struct { + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; +}; + +struct TributePoint_Struct { + int32 tribute_points; + uint32 unknown04; + int32 career_tribute_points; + uint32 unknown12; +}; + +struct TributeMoney_Struct { + uint32 platinum; + uint32 tribute_master_id; + int32 tribute_points; +}; + + +struct Split_Struct +{ + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; +}; + + +/* +** New Combine Struct +** Client requesting to perform a tradeskill combine +** Size: 4 bytes +** Used In: OP_TradeSkillCombine +** Last Updated: Oct-15-2003 +** +*/ +struct NewCombine_Struct { +/*00*/ int16 container_slot; +/*02*/ char unknown02[2]; +/*04*/ +}; + + +//client requesting favorite recipies +struct TradeskillFavorites_Struct { + uint32 object_type; + uint32 some_id; + uint32 favorite_recipes[500]; +}; + +//search request +struct RecipesSearch_Struct { + uint32 object_type; //same as in favorites + uint32 some_id; //same as in favorites + uint32 mintrivial; + uint32 maxtrivial; + char query[56]; + uint32 unknown4; //is set to 00 03 00 00 + uint32 unknown5; //is set to 4C DD 12 00 +/*80*/ +}; + +//one sent for each item, from server in reply to favorites or search +struct RecipeReply_Struct { + uint32 object_type; + uint32 some_id; //same as in favorites + uint32 component_count; + uint32 recipe_id; + uint32 trivial; + char recipe_name[64]; +/*84*/ +}; + +//received and sent back as an ACK with different reply_code +struct RecipeAutoCombine_Struct { + uint32 object_type; + uint32 some_id; + uint32 unknown1; //echoed in reply + uint32 recipe_id; + uint32 reply_code; // 93 64 e1 00 (junk) in request + // 00 00 00 00 in successful reply + // f5 ff ff ff in 'you dont have all the stuff' reply +}; + +struct LevelAppearance_Struct { //Sends a little graphic on level up + uint32 spawn_id; + uint32 parm1; + uint32 value1a; + uint32 value1b; + uint32 parm2; + uint32 value2a; + uint32 value2b; + uint32 parm3; + uint32 value3a; + uint32 value3b; + uint32 parm4; + uint32 value4a; + uint32 value4b; + uint32 parm5; + uint32 value5a; + uint32 value5b; +/*64*/ +}; +struct MerchantList{ + uint32 id; + uint32 slot; + uint32 item; +}; +struct TempMerchantList{ + uint32 npcid; + uint32 slot; + uint32 item; + uint32 charges; //charges/quantity + uint32 origslot; +}; + + +struct FindPerson_Point { + float y; + float x; + float z; +}; + +struct FindPersonRequest_Struct { + uint32 unknown000; + uint32 npc_id; + FindPerson_Point client_pos; +}; + +//variable length packet of points +struct FindPersonResult_Struct { + FindPerson_Point dest; + FindPerson_Point path[0]; //last element must be the same as dest +}; + +struct MobRename_Struct { +/*000*/ char old_name[64]; +/*064*/ char old_name_again[64]; //not sure what the difference is +/*128*/ char new_name[64]; +/*192*/ uint32 unknown192; //set to 0 +/*196*/ uint32 unknown196; //set to 1 +/*200*/ +}; + +struct PlayMP3_Struct { + char filename[128]; +}; + +//this is for custom title display in the skill window +struct TitleEntry_Struct { + uint32 skill_id; + uint32 skill_value; + char title[1]; +}; + +struct Titles_Struct { + uint32 title_count; + TitleEntry_Struct titles[0]; +}; + +//this is for title selection by the client +struct TitleListEntry_Struct { + uint32 unknown0; //title ID + char prefix[1]; //variable length, null terminated + char postfix[1]; //variable length, null terminated +}; + +struct TitleList_Struct { + uint32 title_count; + TitleListEntry_Struct titles[0]; //list of title structs + //uint32 unknown_ending; seen 0x7265, 0 +}; + +struct SetTitle_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + uint32 title_id; +}; + +struct SetTitleReply_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + char title[32]; + uint32 entity_id; +}; + + +#if 0 +// Old struct not used by Task System implementation but left for reference +struct TaskDescription_Struct { +/*000*/ uint32 activity_count; //not right. +/*004*/ uint32 taskid; +/*008*/ uint8 unk; +/*009*/ uint32 id3; +/*013*/ uint32 unknown13; +/*017*/ char name[1]; //variable length, 0 terminated +/*018*/ uint32 unknown18; +/*022*/ uint32 unknown22; +/*026*/ char desc[1]; //variable length, 0 terminated +/*027*/ uint32 reward_count; //not sure +/*031*/ uint32 unknown31; +/*035*/ uint32 unknown35; +/*039*/ uint16 unknown39; +/*041*/ char reward_link[1]; //variable length, 0 terminated +/*042*/ uint32 unknown43; //maybe crystal count? +}; +#endif + +struct TaskMemberList_Struct { +/*00*/ uint32 gopher_id; +/*04*/ uint32 unknown04; +/*08*/ uint32 member_count; //1 less than the number of members +/*12*/ char list_pointer[0]; +/* list is of the form: + char member_name[1] //null terminated string + uint8 task_leader //boolean flag +*/ +}; + +#if 0 +// Old structs not used by Task System implentation but left for reference +struct TaskActivity_Struct { +/*000*/ uint32 activity_count; //not right +/*004*/ uint32 id3; +/*008*/ uint32 taskid; +/*012*/ uint32 activity_id; +/*016*/ uint32 unknown016; +/*020*/ uint32 activity_type; +/*024*/ uint32 unknown024; +/*028*/ uint32 unknown28; +/*032*/ char mob_name[1]; //variable length, 0 terminated +/*033*/ char item_name[1]; //variable length, 0 terminated +/*034*/ uint32 goal_count; +/*038*/ uint32 unknown38; //0xFFFFFFFF +/*042*/ uint32 unknown42; //0xFFFFFFFF +/*046*/ uint32 unknown46; //saw 0x151,0x156 +/*050*/ uint32 unknown50; //saw 0x404,0 +/*054*/ char activity_name[1]; //variable length, 0 terminated... commonly empty +/*055*/ uint32 done_count; +/*059*/ uint32 unknown59; //=1 except on unknown and terminal activities? +/*063*/ +}; + +struct TaskHistoryEntry_Struct { + uint32 task_id; + char name[1]; + uint32 completed_time; +}; +struct TaskHistory_Struct { + uint32 completed_count; + TaskHistoryEntry_Struct entries[0]; +}; +#endif + +struct AcceptNewTask_Struct { + uint32 unknown00; + uint32 task_id; //set to 0 for 'decline' + uint32 task_master_id; //entity ID +}; + +//was all 0's from client, server replied with same op, all 0's +struct CancelTask_Struct { + uint32 SequenceNumber; + uint32 unknown4; // Only seen 0x00000002 +}; + +#if 0 +// old struct, not used by Task System implementation but left for reference. +struct AvaliableTask_Struct { + uint32 task_index; //no idea, seen 0x1 + uint32 task_master_id; //entity ID + uint32 task_id; + uint32 unknown012; + uint32 activity_count; //not sure, seen 2 + char desc[1]; //variable length, 0 terminated + uint32 reward_platinum;//not sure on these + uint32 reward_gold; + uint32 reward_silver; + uint32 reward_copper; + char some_name[1]; //variable length, 0 terminated + uint8 unknown1; + uint32 unknown2; //0xFFFFFFFF + uint32 unknown3; //0xFFFFFFFF + uint32 unknown4; //seen 0x16 + uint8 unknown5; +}; +#endif + + +// Many of the Task System packets contain variable length strings, as well as variable numbers +// of records, hence splitting them into multiple structs (header, middle, trailer) etc. +// +struct AvailableTaskHeader_Struct { + uint32 TaskCount; + uint32 unknown1; + uint32 TaskGiver; +}; + +struct AvailableTaskData1_Struct { + uint32 TaskID; + uint32 unknown1; + uint32 TimeLimit; + uint32 unknown2; +}; + +struct AvailableTaskData2_Struct { + uint32 unknown1,unknown2,unknown3,unknown4; +}; + +struct AvailableTaskTrailer_Struct { + uint32 ItemCount; + uint32 unknown1, unknown2; + uint32 StartZone; +}; + +struct TaskDescriptionHeader_Struct { + uint32 SequenceNumber; // The order the tasks appear in the journal. 0 for first task, 1 for second, etc. + uint32 TaskID; + uint32 unknown2; + uint32 unknown3; + uint8 unknown4; +}; + +struct TaskDescriptionData1_Struct { + uint32 Duration; + uint32 unknown2; + uint32 StartTime; +}; + +struct TaskDescriptionData2_Struct { + uint32 RewardCount; // ?? + uint32 unknown1; + uint32 unknown2; + uint16 unknown3; + //uint8 unknown4; +}; + +struct TaskDescriptionTrailer_Struct { + //uint16 unknown1; // 0x0012 + uint32 Points; +}; + +struct TaskActivityHeader_Struct { + uint32 TaskSequenceNumber; + uint32 unknown2; // Seen 0x00000002 + uint32 TaskID; + uint32 ActivityID; + uint32 unknown3; + uint32 ActivityType; + uint32 Optional; + uint32 unknown5; +}; + +struct TaskActivityData1_Struct { + uint32 GoalCount; + uint32 unknown1; // 0xffffffff + uint32 unknown2; // 0xffffffff + uint32 ZoneID; // seen 0x36 + uint32 unknown3; +}; + +struct TaskActivityTrailer_Struct { + uint32 DoneCount; + uint32 unknown1; // Seen 1 +}; + +// The Short_Struct is sent for tasks that are hidden and act as a placeholder +struct TaskActivityShort_Struct { + uint32 TaskSequenceNumber; + uint32 unknown2; // Seen 0x00000002 + uint32 TaskID; + uint32 ActivityID; + uint32 unknown3; + uint32 ActivityType; // 0xffffffff for the short packet + uint32 unknown4; +}; + +struct TaskActivityComplete_Struct { + uint32 TaskIndex; + uint32 unknown2; // 0x00000002 + uint32 unknown3; + uint32 ActivityID; + uint32 unknown4; // 0x00000001 + uint32 unknown5; // 0x00000001 +}; + +#if 0 +// This is a dupe of the CancelTask struct +struct TaskComplete_Struct { + uint32 unknown00; // 0x00000000 + uint32 unknown04; // 0x00000002 +}; +#endif + +struct TaskHistoryRequest_Struct { + uint32 TaskIndex; // This is the sequence the task was sent in the Completed Tasks packet. +}; + +struct TaskHistoryReplyHeader_Struct { + uint32 TaskID; + uint32 ActivityCount; +}; + +struct TaskHistoryReplyData1_Struct { + uint32 ActivityType; +}; + +struct TaskHistoryReplyData2_Struct { + uint32 GoalCount; + uint32 unknown04; // 0xffffffff + uint32 unknown08; // 0xffffffff + uint32 ZoneID; + uint32 unknown16; +}; + +struct BankerChange_Struct { + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; + uint32 platinum_bank; + uint32 gold_bank; + uint32 silver_bank; + uint32 copper_bank; +}; + +struct LeadershipExpUpdate_Struct { +/*00*/ double group_leadership_exp; +/*08*/ uint32 group_leadership_points; +/*12*/ uint32 Unknown12; +/*16*/ double raid_leadership_exp; +/*24*/ uint32 raid_leadership_points; +}; + +struct UpdateLeadershipAA_Struct { +/*00*/ uint32 ability_id; +/*04*/ uint32 new_rank; +/*08*/ uint32 unknown08; +/*12*/ +}; + +/** +* Leadership AA update +* Length: 32 Octets +* OpCode: LeadExpUpdate +*/ +struct leadExpUpdateStruct { + /*0000*/ uint32 unknown0000; // All zeroes? + /*0004*/ uint32 group_leadership_exp; // Group leadership exp value + /*0008*/ uint32 group_leadership_points; // Unspent group points + /*0012*/ uint32 unknown0012; // Type? + /*0016*/ uint32 unknown0016; // All zeroes? + /*0020*/ uint32 raid_leadership_exp; // Raid leadership exp value + /*0024*/ uint32 raid_leadership_points; // Unspent raid points + /*0028*/ uint32 unknown0028; +}; + +struct RaidGeneral_Struct { +/*00*/ uint32 action; +/*04*/ char player_name[64]; +/*68*/ uint32 unknown68; +/*72*/ char leader_name[64]; +/*136*/ uint32 parameter; +}; + +struct RaidAddMember_Struct { +/*000*/ RaidGeneral_Struct raidGen; //param = (group num-1); 0xFFFFFFFF = no group +/*136*/ uint8 _class; +/*137*/ uint8 level; +/*138*/ uint8 isGroupLeader; +/*139*/ uint8 flags[5]; //no idea if these are needed... +}; + +struct RaidAdd_Struct { +/*000*/ uint32 action; //=0 +/*004*/ char player_name[64]; //should both be the player's name +/*068*/ char leader_name[64]; +/*132*/ uint8 _class; +/*133*/ uint8 level; +/*134*/ uint8 has_group; +/*135*/ uint8 unknown135; //seems to be 0x42 or 0 +}; + +struct RaidCreate_Struct { +/*00*/ uint32 action; //=8 +/*04*/ char leader_name[64]; +/*68*/ uint32 leader_id; +}; + +struct RaidMemberInfo_Struct { +/*00*/ uint8 group_number; +/*01*/ char member_name[1]; //dyanmic length, null terminated '\0' +/*00*/ uint8 unknown00; +/*01*/ uint8 _class; +/*02*/ uint8 level; +/*03*/ uint8 is_raid_leader; +/*04*/ uint8 is_group_leader; +/*05*/ uint8 main_tank; //not sure +/*06*/ uint8 unknown06[5]; //prolly more flags +}; + +struct RaidDetails_Struct { +/*000*/ uint32 action; //=6,20 +/*004*/ char leader_name[64]; +/*068*/ uint32 unknown68[4]; +/*084*/ LeadershipAA_Struct abilities; //ranks in backwards byte order +/*128*/ uint8 unknown128[142]; +/*354*/ uint32 leader_id; +}; + +struct RaidMembers_Struct { +/*000*/ RaidDetails_Struct details; +/*358*/ uint32 member_count; //including leader +/*362*/ RaidMemberInfo_Struct members[1]; +/*...*/ RaidMemberInfo_Struct empty; //seem to have an extra member with a 0 length name on the end +}; + +struct DynamicWall_Struct { +/*00*/ char name[32]; +/*32*/ float y; +/*36*/ float x; +/*40*/ float z; +/*44*/ uint32 something; +/*48*/ uint32 unknown48; //0 +/*52*/ uint32 one_hundred; //0x64 +/*56*/ uint32 unknown56; //0 +/*60*/ uint32 something2; +/*64*/ int32 unknown64; //-1 +/*68*/ uint32 unknown68; //0 +/*72*/ uint32 unknown72; //0 +/*76*/ uint32 unknown76; //0x100 +/*80*/ +}; + +enum { //bandolier actions + BandolierCreate = 0, + BandolierRemove = 1, + BandolierSet = 2 +}; + +struct BandolierCreate_Struct { +/*00*/ uint32 action; //0 for create +/*04*/ uint8 number; +/*05*/ char name[32]; +/*37*/ uint16 unknown37; //seen 0x93FD +/*39*/ uint8 unknown39; //0 +}; + +struct BandolierDelete_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ uint8 unknown05[35]; +}; + +struct BandolierSet_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ uint8 unknown05[35]; +}; + +struct Arrow_Struct { +/*000*/ float src_y; +/*004*/ float src_x; +/*008*/ float src_z; +/*012*/ uint8 unknown012[12]; +/*024*/ float velocity; //4 is normal, 20 is quite fast +/*028*/ float launch_angle; //0-450ish, not sure the units, 140ish is straight +/*032*/ float tilt; //on the order of 125 +/*036*/ uint8 unknown036[8]; +/*044*/ float arc; +/*048*/ uint32 source_id; +/*052*/ uint32 target_id; //entity ID +/*056*/ uint32 item_id; +/*060*/ uint32 unknown060; +/*064*/ uint32 unknown064; +/*068*/ uint8 unknown068; +/*069*/ uint8 unknown069; +/*070*/ uint8 unknown070; +/*071*/ uint8 item_type; +/*072*/ uint8 skill; +/*073*/ char model_name[43]; +/*116*/ +}; + +//made a bunch of trivial structs for stuff for opcode finder to use +struct Consent_Struct { + char name[1]; //always at least a null - was 1 +}; + +struct AdventureMerchant_Struct { + uint32 unknown_flag; //seems to be 1 + uint32 entity_id; +}; + +struct Save_Struct { + uint8 unknown00[192]; + uint8 unknown0192[176]; +}; + +struct GMToggle_Struct { + uint8 unknown0[64]; + uint32 toggle; +}; + +struct BuffFadeMsg_Struct { + uint32 color; + char msg[1]; //was 1 +/*0???*/ uint8 paddingXXX[3]; // always 0's +}; + +struct UseAA_Struct { + uint32 begin; + uint32 ability; + uint32 end; +}; + +struct AA_Ability { +/*00*/ uint32 skill_id; +/*04*/ uint32 base1; +/*08*/ uint32 base2; +/*12*/ uint32 slot; +/*16*/ +}; + +struct SendAA_Struct { +/*0000*/ uint32 id; +/*0004*/ uint8 unknown004; // uint32 unknown004; set to 1. +/*0005*/ uint32 hotkey_sid; +/*0009*/ uint32 hotkey_sid2; +/*0013*/ uint32 title_sid; +/*0017*/ uint32 desc_sid; +/*0021*/ uint32 class_type; +/*0025*/ uint32 cost; +/*0029*/ uint32 seq; +/*0033*/ uint32 current_level; //1s, MQ2 calls this AARankRequired +/*0037*/ uint32 prereq_skill; //is < 0, abs() is category # +/*0041*/ uint32 prereq_minpoints; //min points in the prereq +/*0045*/ uint32 type; +/*0049*/ uint32 spellid; +/*0053*/ uint32 spell_type; +/*0057*/ uint32 spell_refresh; +/*0061*/ uint16 classes; +/*0063*/ uint16 berserker; //seems to be 1 if its a berserker ability +/*0065*/ uint32 max_level; +/*0069*/ uint32 last_id; +/*0073*/ uint32 next_id; +/*0077*/ uint32 cost2; +/*0081*/ uint8 unknown80[7]; +/*0088*/ uint32 aa_expansion; +/*0092*/ uint32 special_category; +/*0096*/ uint32 unknown0096; +/*0100*/ uint32 total_abilities; +/*0104*/ AA_Ability abilities[0]; +}; + +struct AA_List { + SendAA_Struct* aa[0]; +}; + +struct AA_Action { +/*00*/ uint32 action; +/*04*/ uint32 ability; +/*08*/ uint32 unknown08; +/*12*/ uint32 exp_value; +}; + +struct AA_Skills { //this should be removed and changed to AA_Array +/*00*/ uint32 aa_skill; // Total AAs Spent +/*04*/ uint32 aa_value; +/*08*/ uint32 unknown08; +}; + +struct AAExpUpdate_Struct { +/*00*/ uint32 unknown00; //seems to be a value from AA_Action.ability +/*04*/ uint32 aapoints_unspent; +/*08*/ uint8 aaxp_percent; //% of exp that goes to AAs +/*09*/ uint8 unknown09[3]; //live doesn't always zero these, so they arnt part of aaxp_percent +}; + + +struct AltAdvStats_Struct { +/*000*/ uint32 experience; +/*004*/ uint16 unspent; +/*006*/ uint16 unknown006; +/*008*/ uint8 percentage; +/*009*/ uint8 unknown009[3]; +}; + +struct PlayerAA_Struct { // Is this still used? + AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct AATable_Struct { +/*00*/ int32 aa_spent; // Total AAs Spent +/*04*/ AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct Weather_Struct { + uint32 val1; //generall 0x000000FF + uint32 type; //0x31=rain, 0x02=snow(i think), 0 = normal + uint32 mode; +}; + +struct ZoneInUnknown_Struct { + uint32 val1; + uint32 val2; + uint32 val3; +}; + +struct MobHealth_Struct { + uint16 entity_id; + uint8 hp; +}; + +struct AnnoyingZoneUnknown_Struct { + uint32 entity_id; + uint32 value; //always 4 +}; + +struct LoadSpellSet_Struct { + uint32 spell[MAX_PP_MEMSPELL]; // 0xFFFFFFFF if no action, slot number if to unmem starting at 0 + uint32 unknown; //there seems to be an extra field in this packet... +}; + +struct BlockedBuffs_Struct { +/*000*/ uint8 unknown000[80]; +/*080*/ uint8 unknown081; +/*081*/ uint8 unknown082; +/*082*/ uint8 unknown083; +/*083*/ uint8 unknown084; +/*084*/ uint8 unknown085; +/*085*/ uint8 unknown086; +/*086*/ uint8 unknown087; +/*087*/ uint8 unknown088; +/*088*/ +}; + +//Size 24 Bytes +struct WorldObfuscator_Struct +{ +/*000*/ uint32 var1; +/*004*/ uint32 Unknown1; +/*008*/ uint32 Unknown2; +/*012*/ uint32 Unknown3; +/*016*/ uint32 var2; +/*020*/ uint32 Unknown4; +/*024*/ +}; + +struct ExpansionInfo_Struct { +/*000*/ char Unknown000[64]; +/*064*/ uint32 Expansions; +}; + +struct ApplyPoison_Struct { + uint32 inventorySlot; + uint32 success; +}; + +struct ItemVerifyRequest_Struct { +/*000*/ int32 slot; // Slot being Right Clicked +/*004*/ uint32 target; // Target Entity ID +/*008*/ +}; + +struct ItemVerifyReply_Struct { +/*000*/ int32 slot; // Slot being Right Clicked +/*004*/ uint32 spell; // Spell ID to cast if different than item effect +/*008*/ uint32 target; // Target Entity ID +/*012*/ +}; + +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 SoD +/*063*/ uint8 ItemClass; //0, 1, or 2 +}; + +struct ItemBodyStruct +{ + uint32 id; + uint8 weight; + uint8 norent; + uint8 nodrop; + uint8 attune; + uint8 size; + uint32 slots; + uint32 price; + uint32 icon; + uint8 unknown1; + uint8 unknown2; + uint32 BenefitFlag; + uint8 tradeskills; + int8 CR; + int8 DR; + int8 PR; + int8 MR; + int8 FR; + int8 SVCorruption; + int8 AStr; + int8 ASta; + int8 AAgi; + int8 ADex; + int8 ACha; + int8 AInt; + int8 AWis; + int32 HP; + int32 Mana; + uint32 Endur; + int32 AC; + int32 regen; + int32 mana_regen; + int32 end_regen; + uint32 Classes; + uint32 Races; + uint32 Deity; + int32 SkillModValue; + uint32 unknown6; + uint32 SkillModType; + uint32 BaneDmgRace; + uint32 BaneDmgBody; + uint32 BaneDmgRaceAmt; + int32 BaneDmgAmt; + uint8 Magic; + int32 CastTime_; + uint32 ReqLevel; + uint32 RecLevel; + uint32 RecSkill; + uint32 BardType; + int32 BardValue; + uint8 Light; + uint8 Delay; + uint8 ElemDmgType; + uint8 ElemDmgAmt; + uint8 Range; + uint32 Damage; + uint32 Color; + uint8 ItemType; + uint32 Material; + uint32 unknown7; + uint32 EliteMaterial; + float SellRate; + int32 CombatEffects; + int32 Shielding; + int32 StunResist; + int32 StrikeThrough; + int32 ExtraDmgSkill; + int32 ExtraDmgAmt; + int32 SpellShield; + int32 Avoidance; + int32 Accuracy; + uint32 CharmFileID; + uint32 FactionMod1; + int32 FactionAmt1; + uint32 FactionMod2; + int32 FactionAmt2; + uint32 FactionMod3; + int32 FactionAmt3; + uint32 FactionMod4; + int32 FactionAmt4; + //int16 unknown14; + +}; + +struct AugSlotStruct +{ + uint32 type; + uint8 visible; + uint8 unknown; +}; + +struct ItemTertiaryBodyStruct +{ + int32 loregroup; + uint8 artifact; + uint8 summonedflag; + uint32 favor; + uint8 fvnodrop; + int32 dotshield; + int32 atk; + int32 haste; + int32 damage_shield; + uint32 guildfavor; + uint32 augdistil; + int32 unknown3; + uint32 unknown4; + uint8 no_pet; + uint8 unknown5; + + uint8 potion_belt_enabled; + uint32 potion_belt_slots; + + uint32 stacksize; + uint8 no_transfer; + uint16 expendablearrow; + + uint32 unknown8; + uint32 unknown9; + uint32 unknown10; + uint32 unknown11; + uint8 unknown12; + uint8 unknown13; + uint8 unknown14; +}; + +struct ClickEffectStruct +{ + int32 effect; + uint8 level2; + uint32 type; + uint8 level; + int32 max_charges; + int32 cast_time; + uint32 recast; + int32 recast_type; + uint32 clickunk5; + //uint8 effect_string; //unused + //int32 clickunk7; +}; + +struct ProcEffectStruct +{ + uint32 effect; + uint8 level2; + uint32 type; + uint8 level; + uint32 unknown1; // poison? + uint32 unknown2; + uint32 unknown3; + uint32 unknown4; + uint32 procrate; + //uint8 effect_string; + //uint32 unknown5; +}; + +struct WornEffectStruct //worn, focus and scroll effect +{ + uint32 effect; + uint8 level2; + uint32 type; + uint8 level; + uint32 unknown1; + uint32 unknown2; + uint32 unknown3; + uint32 unknown4; + uint32 unknown5; + //uint8 effect_string; + //uint32 unknown6; +}; + + +struct ItemSecondaryBodyStruct{ + uint32 augtype; + uint32 augrestrict; + AugSlotStruct augslots[5]; + + uint32 ldonpoint_type; + uint32 ldontheme; + uint32 ldonprice; + uint32 ldonsellbackrate; + uint32 ldonsold; + + uint8 bagtype; + uint8 bagslots; + uint8 bagsize; + uint8 wreduction; + + uint8 book; + uint8 booktype; + //int32 filename; filename is either 0xffffffff/0x00000000 or the null term string ex: CREWizardNote\0 +}; + +struct ItemQuaternaryBodyStruct +{ + uint32 scriptfileid; + uint8 quest_item; + uint32 unknown15; //0xffffffff - Power Source Capacity? + uint32 Purity; + uint32 BackstabDmg; + uint32 DSMitigation; + int32 HeroicStr; + int32 HeroicInt; + int32 HeroicWis; + int32 HeroicAgi; + int32 HeroicDex; + int32 HeroicSta; + int32 HeroicCha; + int32 HeroicMR; + int32 HeroicFR; + int32 HeroicCR; + int32 HeroicDR; + int32 HeroicPR; + int32 HeroicSVCorrup; + int32 HealAmt; + int32 SpellDmg; + int32 clairvoyance; + uint8 unknown18; //Power Source Capacity or evolve filename? + uint32 evolve_string; // Some String, but being evolution related is just a guess + uint32 subitem_count; +}; + +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*/ +}; + +struct VeteranRewardItem +{ +/*000*/ uint32 item_id; +/*004*/ uint32 charges; +/*008*/ char item_name[64]; +}; + +struct VeteranReward +{ +/*000*/ uint32 claim_id; +/*004*/ uint32 number_available; +/*008*/ uint32 claim_count; +/*012*/ VeteranRewardItem items[8]; +}; + +struct ExpeditionEntryHeader_Struct +{ +/*000*/ uint32 unknown000; +/*000*/ uint32 number_of_entries; +}; + +struct ExpeditionJoinPrompt_Struct +{ +/*000*/ uint32 clientid; +/*004*/ uint32 unknown004; +/*008*/ char player_name[64]; +/*072*/ char expedition_name[64]; +}; + +struct ExpeditionExpireWarning +{ +/*000*/ uint32 clientid; +/*004*/ uint32 unknown004; +/*008*/ uint32 minutes_remaining; +}; + +struct ExpeditionInfo_Struct +{ +/*000*/ uint32 clientid; +/*004*/ uint32 unknown004; +/*008*/ uint32 unknown008; +/*012*/ uint32 max_players; +/*016*/ char expedition_name[128]; +/*142*/ char leader_name[64]; +}; + +struct ExpeditionCompassEntry_Struct +{ +/*000*/ float unknown000; //seen *((uint32*)) = 1584791871 +/*004*/ uint32 enabled; //guess +/*008*/ uint32 unknown008; //seen 1019 +/*012*/ float y; +/*016*/ float x; +/*020*/ float z; +}; + +struct ExpeditionCompass_Struct +{ +/*000*/ uint32 clientid; +/*004*/ uint32 count; +/*008*/ ExpeditionCompassEntry_Struct entries[0]; +}; + +struct AltCurrencySelectItem_Struct { + uint32 merchant_entity_id; + uint32 slot_id; + uint32 unknown008; + uint32 unknown012; + uint32 unknown016; + uint32 unknown020; + uint32 unknown024; + uint32 unknown028; + uint32 unknown032; + uint32 unknown036; + uint32 unknown040; + uint32 unknown044; + uint32 unknown048; + uint32 unknown052; + uint32 unknown056; + uint32 unknown060; + uint32 unknown064; + uint32 unknown068; + uint32 unknown072; + uint32 unknown076; +}; + +struct AltCurrencySellItem_Struct { +/*000*/ uint32 merchant_entity_id; +/*004*/ uint32 slot_id; +/*006*/ uint32 charges; +/*010*/ uint32 cost; +}; + +struct MercenaryGrade_Struct { +uint32 GradeCountEntry; +}; + +// Used by MercenaryListEntry_Struct +struct MercenaryStance_Struct { +/*0000*/ uint32 StanceIndex; // Index of this stance (sometimes reverse reverse order - 3, 2, 1, 0 for 4 stances etc) +/*0004*/ uint32 Stance; // From dbstr_us.txt - 1^24^Passive^0, 2^24^Balanced^0, etc (1 to 9 as of April 2012) +}; + +// Used by MercenaryMerchantList_Struct +struct MercenaryListEntry_Struct { +/*0000*/ uint32 MercID; // ID unique to each type of mercenary (probably a DB id) +/*0004*/ uint32 MercType; // From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) +/*0008*/ uint32 MercSubType; // From dbstr_us.txt - 330020105^23^Race: Guktan
Type: Healer
Confidence: High
Proficiency: Apprentice, Tier V... +/*0012*/ uint32 PurchaseCost; // Purchase Cost (in gold) +/*0016*/ uint32 UpkeepCost; // Upkeep Cost (in gold) +/*0020*/ uint32 AltCurrencyCost; // Alternate Currency Purchase Cost? (all seen costs show N/A Bayle Mark) - Seen 0 +/*0024*/ uint32 AltCurrencyUpkeep; // Alternate Currency Upkeep Cost? (all seen costs show 1 Bayle Mark) - Seen 1 +/*0028*/ uint32 AltCurrencyType; // Alternate Currency Type? - 19^17^Bayle Mark^0 - Seen 19 +/*0032*/ uint32 StanceCount; // Iterations of MercenaryStance_Struct - Normally 2 to 4 seen +/*0036*/ int32 TimeLeft; // Unknown (always see -1 at merchant) - Seen 900000 (15 minutes in ms for newly hired merc) +/*0040*/ MercenaryStance_Struct* Stances; // Count Varies, but hard set to 2 for now - From dbstr_us.txt - 1^24^Passive^0, 2^24^Balanced^0, etc (1 to 9 as of April 2012) +}; + +// [OPCode: 0x27ac OP_MercenaryDataResponse] On Live as of April 2 2012 [Server->Client] +// Opcode should be renamed to something like OP_MercenaryMerchantShopResponse since the Data Response packet is different +// Sent by the server when browsing the Mercenary Merchant +struct MercenaryMerchantList_Struct { +/*0000*/ uint32 MercTypeCount; // Number of Merc Types to follow +/*0004*/ MercenaryGrade_Struct* MercGrades; // Count varies, but hard set to 3 max for now - From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) +/*0016*/ uint32 MercCount; // Number of MercenaryInfo_Struct to follow +/*0020*/ MercenaryListEntry_Struct* Mercs; // Data for individual mercenaries in the Merchant List +}; + +// [OPCode: 0x4dd9 OP_MercenaryDataRequest] On Live as of April 2 2012 [Client->Server] +// Opcode should be renamed to something like OP_MercenaryMerchantShopRequest since the Data Request packet is different +// Right clicking merchant - shop request +struct MercenaryMerchantShopRequest_Struct { +/*0000*/ uint32 MercMerchantID; // Entity ID of the Mercenary Merchant +/*0004*/ +}; + +// Used by MercenaryDataUpdate_Struct +struct MercenaryData_Struct { +/*0000*/ uint32 MercID; // ID unique to each type of mercenary (probably a DB id) +/*0004*/ uint32 MercType; // From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) +/*0008*/ uint32 MercSubType; // From dbstr_us.txt - 330020105^23^Race: Guktan
Type: Healer
Confidence: High
Proficiency: Apprentice, Tier V... +/*0012*/ uint32 PurchaseCost; // Purchase Cost (in gold) +/*0016*/ uint32 UpkeepCost; // Upkeep Cost (in gold) +/*0020*/ uint32 AltCurrencyCost; // Alternate Currency Purchase Cost? (all seen costs show N/A Bayle Mark) - Seen 0 +/*0024*/ uint32 AltCurrencyUpkeep; // Alternate Currency Upkeep Cost? (all seen costs show 1 Bayle Mark) - Seen 1 +/*0028*/ uint32 AltCurrencyType; // Alternate Currency Type? - 19^17^Bayle Mark^0 - Seen 19 +/*0032*/ uint32 StanceCount; // Iterations of MercenaryStance_Struct - Normally 2 to 4 seen +/*0036*/ int32 TimeLeft; // Unknown (always see -1 at merchant) - Seen 900000 (15 minutes in ms for newly hired merc) +/*0040*/ MercenaryStance_Struct Stances[1]; // Count Varies, but hard set to 2 for now - From dbstr_us.txt - 1^24^Passive^0, 2^24^Balanced^0, etc (1 to 9 as of April 2012) +/*0000*/ uint32 MercUnk05; // Seen 1 - Extra Merc Data field that differs from MercenaryListEntry_Struct +// MercUnk05 may be a field that is at the end of the packet only, even if multiple mercs are listed (haven't seen examples of multiple mercs owned at once) +}; + +// [OPCode: 0x6537] On Live as of April 2 2012 [Server->Client] +// Should be named OP_MercenaryDataResponse, but the current opcode using that name should be renamed first +// Size varies if mercenary is hired or if browsing Mercenary Merchant +// This may also be the response for Client->Server 0x0327 (size 0) packet On Live as of April 2 2012 +struct MercenaryDataUpdate_Struct { +/*0000*/ int32 MercStatus; // Seen 0 with merc and -1 with no merc hired +/*0004*/ uint32 MercCount; // Seen 1 with 1 merc hired and 0 with no merc hired +/*0008*/ MercenaryData_Struct MercData[0]; // Data for individual mercenaries in the Merchant List +}; + +// [OPCode: 0x6537] On Live as of April 2 2012 [Server->Client] +// Size 12 and sent on Zone-In if no mercenary is currently hired and when merc is dismissed +// (Same packet as MercAssign_Struct?) +struct NoMercenaryHired_Struct { +/*0000*/ int32 MercStatus; // Seen -1 with no merc hired +/*0004*/ uint32 MercCount; // Seen 0 with no merc hired +/*0008*/ uint32 MercID; // Seen 1 when no merc is hired - ID unique to each type of mercenary +/*0012*/ +}; + +// [OPCode: 0x495d OP_MercenaryTimer] On Live as of April 2 2012 [Server->Client] [Size: 20] +// Sent on Zone-In, or after Dismissing, Suspending, or Unsuspending Mercs +struct MercenaryStatus_Struct { +/*0000*/ uint32 MercEntityID; // Seen 0 (no merc spawned) or 615843841 and 22779137 +/*0004*/ uint32 UpdateInterval; // Seen 900000 - Matches from 0x6537 packet (15 minutes in ms?) +/*0008*/ uint32 MercUnk01; // Seen 180000 - 3 minutes in milleseconds? Maybe next update interval? +/*0012*/ uint32 MercState; // Seen 5 (normal) or 1 (suspended) +/*0016*/ uint32 SuspendedTime; // Seen 0 (not suspended) or c9 c2 64 4f (suspended on Sat Mar 17 11:58:49 2012) - Unix Timestamp +/*0020*/ +}; + +// [OPCode: 0x4c6c] On Live as of April 2 2012 [Client->Server] [Size: 8] +// Sent from the client when using the Mercenary Window +struct MercenaryCommand_Struct { +/*0000*/ uint32 MercCommand; // Seen 0 (zone in with no merc or suspended), 1 (dismiss merc), 5 (normal state), 36 (zone in with merc) +/*0004*/ int32 Option; // Seen -1 (zone in with no merc), 0 (setting to passive stance), 1 (normal or setting to balanced stance) +/*0008*/ +}; + +// [OPCode: 0x1a79] On Live as of April 2 2012 [Client->Server] [Size: 1] +// Requesting to suspend or unsuspend merc +struct SuspendMercenary_Struct { +/*0000*/ uint8 SuspendMerc; // Seen 30 (48) for suspending or unsuspending +/*0001*/ +}; + +// [OPCode: 0x2528] On Live as of April 2 2012 [Server->Client] [Size: 4] +// Response to suspend merc with timestamp +struct SuspendMercenaryResponse_Struct { +/*0000*/ uint32 SuspendTime; // Unix Timestamp - Seen a9 11 78 4f +/*0004*/ +}; + +// [OPCode: 0x5e78 (OP_MercenaryHire?)] On Live as of April 2 2012 +// Sent by client when requesting to view Mercenary info or Hire a Mercenary +struct MercenaryMerchantRequest_Struct { +/*0000*/ uint32 RequestType; // Seen 399 for a Hire Request and 400 for a view merc info request? (may actually be merc ID) +/*0004*/ uint32 MercUnk01; // Seen 1 +/*0008*/ uint32 MercMerchantID; // Entity ID for Mercenary Merchant +/*0012*/ uint32 MercUnk02; // Seen 65302016 (00 6e e4 03) - (probably actually individual uint8 fields) +/*0016*/ +}; + +// [OPCode: 0x5e78 (OP_MercenaryHire?)] On Live as of April 2 2012 +// Sent by Server in response to requesting to view Mercenary info or Hire a Mercenary +struct MercenaryMerchantResponse_Struct { +/*0000*/ uint32 ResponseType; // Seen 0 for hire response, 6 for info response, and 9 for denied hire request +/*0004*/ +}; + +// (Same packet as NoMercenaryHired_Struct?) +struct MercenaryAssign_Struct { +/*0000*/ uint32 MercEntityID; // Seen 0 (no merc spawned) or 615843841 and 22779137 +/*0004*/ uint32 MercUnk01; // +/*0008*/ uint32 MercUnk02; // +/*0012*/ +}; + + + }; //end namespace structs +}; //end namespace SoD + +#endif /*SoD_STRUCTS_H_*/ diff --git a/common/patches/SoF.cpp b/common/patches/SoF.cpp new file mode 100644 index 000000000..67aea22e4 --- /dev/null +++ b/common/patches/SoF.cpp @@ -0,0 +1,2841 @@ + +#include "../debug.h" +#include "SoF.h" +#include "../opcodemgr.h" +#include "../logsys.h" +#include "../EQStreamIdent.h" +#include "../crc32.h" + +#include "../eq_packet_structs.h" +#include "../MiscFunctions.h" +#include "../Item.h" +#include "SoF_structs.h" +#include "../rulesys.h" + +#include +#include + +namespace SoF { + +static const char *name = "SoF"; +static OpcodeManager *opcodes = NULL; +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 == NULL) { + //TODO: get this file name from the config file + 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; + string pname; + + //register our world signature. + pname = 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 = 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 != NULL) { + //TODO: get this file name from the config file + 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); +} + + + +#include "SSDefine.h" + + +// Converts Titanium Slot IDs to SoF Slot IDs for use in Encodes +static inline uint32 TitaniumToSoFSlot(uint32 TitaniumSlot) { + uint32 SoFSlot = 0; + + if(TitaniumSlot >= 21 && TitaniumSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots + { + SoFSlot = TitaniumSlot + 1; + } + else if(TitaniumSlot >= 251 && TitaniumSlot <= 340) // Bag Slots for Normal Inventory and Cursor + { + SoFSlot = TitaniumSlot + 11; + } + else if(TitaniumSlot >= 2031 && TitaniumSlot <= 2270) // Bank Bag Slots + { + SoFSlot = TitaniumSlot + 1; + } + else if(TitaniumSlot >= 2531 && TitaniumSlot <= 2550) // Shared Bank Bag Slots + { + SoFSlot = TitaniumSlot + 1; + } + else if(TitaniumSlot == 9999) //Unused slot ID to give a place to save Power Slot + { + SoFSlot = 21; + } + else + { + SoFSlot = TitaniumSlot; + } + + return SoFSlot; +} + +// Converts Sof Slot IDs to Titanium Slot IDs for use in Decodes +static inline uint32 SoFToTitaniumSlot(uint32 SoFSlot) { + uint32 TitaniumSlot = 0; + + if(SoFSlot >= 22 && SoFSlot <= 54) // Cursor/Ammo/Power Source and Normal Inventory Slots + { + TitaniumSlot = SoFSlot - 1; + } + else if(SoFSlot >= 262 && SoFSlot <= 351) // Bag Slots for Normal Inventory and Cursor + { + TitaniumSlot = SoFSlot - 11; + } + else if(SoFSlot >= 2032 && SoFSlot <= 2271) // Bank Bag Slots + { + TitaniumSlot = SoFSlot - 1; + } + else if(SoFSlot >= 2532 && SoFSlot <= 2551) // Shared Bank Bag Slots + { + TitaniumSlot = SoFSlot - 1; + } + else if(SoFSlot == 21) + { + TitaniumSlot = 9999; //Unused slot ID to give a place to save Power Slot + } + else + { + TitaniumSlot = SoFSlot; + } + + return TitaniumSlot; +} + + +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 = NULL; + + 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 < MAX_MATERIALS; 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); +// 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 = 16; r < 48; r++) { + eq->unknown521[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 = NULL; + + 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 = NULL; + + //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 == NULL) + { + ofs = 0; + if ((emu->lastName[2] < '0') || (emu->lastName[2] > '9')) + { + val = rnd; + } + else + { + val = atoi(&emu->lastName[2]); + } + } + else + { + sep[0] = NULL; + 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 = NULL; + + 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 = NULL; + + 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 = NULL; + + //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 = TitaniumToSoFSlot(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 = TitaniumToSoFSlot(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 = TitaniumToSoFSlot(emu->from_slot); + eq->to_slot = TitaniumToSoFSlot(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 = TitaniumToSoFSlot(emu->from_slot); + eq->to_slot = TitaniumToSoFSlot(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 = TitaniumToSoFSlot(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 = NULL; + dest->FastQueuePacket(&in, ack_req); + return; + } + + //consume the packet + EQApplicationPacket *in = *p; + *p = NULL; + + //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 = NULL; + 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 = TitaniumToSoFSlot(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 = TitaniumToSoFSlot(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); + ss.clear(); + + 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 = TitaniumToSoFSlot(emu->slot); + OUT(charges); + OUT(sell_price); + + FINISH_ENCODE(); +} + +ENCODE(OP_RaidUpdate) +{ + EQApplicationPacket *inapp = *p; + *p = NULL; + 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)); + + 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[] __emu_buffer; +} + +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 = TitaniumToSoFSlot(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 = SoFToTitaniumSlot(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 = SoFToTitaniumSlot(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 = SoFToTitaniumSlot(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 = SoFToTitaniumSlot(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 = SoFToTitaniumSlot(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 = SoFToTitaniumSlot(eq->from_slot); + emu->to_slot = SoFToTitaniumSlot(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 = SoFToTitaniumSlot(eq->from_slot); + emu->to_slot = SoFToTitaniumSlot(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 = SoFToTitaniumSlot(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 = SoFToTitaniumSlot(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 = SoFToTitaniumSlot(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 = SoFToTitaniumSlot(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 = SoFToTitaniumSlot(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); + ss.clear(); + + 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 = TitaniumToSoFSlot(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 = 0; x < 10; ++x) { + + SubSerializations[x] = NULL; + + const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); + + if(subitem) { + + int SubSlotNumber; + + iqbs.subitem_count++; + + if(slot_id_in >= 22 && slot_id_in < 30) + SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); + else if(slot_id_in >= 2000 && slot_id_in <= 2023) + SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); + else if(slot_id_in >= 2500 && slot_id_in <= 2501) + SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); + else + SubSlotNumber = slot_id_in; // ??????? + + 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 = SoFToTitaniumSlot(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 = SoFToTitaniumSlot(eq->slot_id); + IN(charges); + IN(cost); + FINISH_DIRECT_DECODE(); +} + + +} //end namespace SoF + + + + diff --git a/common/patches/SoF.h b/common/patches/SoF.h new file mode 100644 index 000000000..6b1fff745 --- /dev/null +++ b/common/patches/SoF.h @@ -0,0 +1,36 @@ +#ifndef SoF_H_ +#define SoF_H_ + +#include "../StructStrategy.h" + +class EQStreamIdentifier; + +namespace SoF { + + //these are the only public member of this namespace. + extern void Register(EQStreamIdentifier &into); + extern void Reload(); + + + + //you should not directly access anything below.. + //I just dont feel like making a seperate header for it. + + class Strategy : public StructStrategy { + public: + Strategy(); + + protected: + + virtual std::string Describe() const; + + //magic macro to declare our opcode processors + #include "SSDeclare.h" + #include "SoF_ops.h" + }; + +}; + + + +#endif /*SoF_H_*/ diff --git a/common/patches/SoF_itemfields.h b/common/patches/SoF_itemfields.h new file mode 100644 index 000000000..ccba333b5 --- /dev/null +++ b/common/patches/SoF_itemfields.h @@ -0,0 +1,439 @@ +/* + + +These fields must be in the order of how they are serialized! + + + +*/ +#define NEW_TRY +#ifdef NEW_TRY +//* 000 */ I(ItemClass) // Leave this one off on purpose +/* 001 */ S(Name) +/* 002 */ S(Lore) +//* 003 */ C("")//lorefile - Newly Added - Field is Null +/* 004 */ S(IDFile) +/* 005 */ I(ID) +/* 006 */ I(Weight) +/* 007 */ I(NoRent) +/* 008 */ I(NoDrop) +/* 009 */ I(Size) +/* 010 */ I(Slots) +/* 011 */ I(Price) +/* 012 */ I(Icon) +/* 013 */ C("0")//UNK013 +/* 014 */ C("0")//UNK014 +/* 015 */ I(BenefitFlag) +/* 016 */ I(Tradeskills) +/* 017 */ I(CR) +/* 018 */ I(DR) +/* 019 */ I(PR) +/* 020 */ I(MR) +/* 021 */ I(FR) +/* 022 */ C("0")//svcorruption - Newly Added +/* 023 */ I(AStr) +/* 024 */ I(ASta) +/* 025 */ I(AAgi) +/* 026 */ I(ADex) +/* 027 */ I(ACha) +/* 028 */ I(AInt) +/* 029 */ I(AWis) +/* 030 */ I(HP) +/* 031 */ I(Mana) +/* 032 */ I(Endur) //endur - Relocated +/* 033 */ I(AC) +/* 034 */ I(Classes)//classes - Relocated +/* 035 */ I(Races)//races - Relocated +/* 036 */ I(Deity) +/* 037 */ I(SkillModValue) +/* 038 */ C("0")//UNK038 - Default is 0 +/* 039 */ I(SkillModType) +/* 040 */ I(BaneDmgRace) +/* 041 */ I(BaneDmgBody)//banedmgbody - Relocated +/* 042 */ I(BaneDmgRaceAmt)//banedmgraceamt - Relocated +/* 043 */ I(BaneDmgAmt)//banedmgamt - Relocated +/* 044 */ I(Magic) +/* 045 */ I(CastTime_) +/* 046 */ I(ReqLevel) +/* 047 */ I(RecLevel)//reclevel - Relocated +/* 048 */ I(RecSkill)//recskill - Relocated +/* 049 */ I(BardType) +/* 050 */ I(BardValue) +/* 051 */ I(Light) +/* 052 */ I(Delay) +/* 053 */ I(ElemDmgType) +/* 054 */ I(ElemDmgAmt) +/* 055 */ I(Range) +/* 056 */ I(Damage) +/* 057 */ I(Color) +/* 058 */ I(ItemType) +/* 059 */ I(Material) +/* 060 */ C("0")//UNK060 - Default is 0 +/* 061 */ C("0")//UNK061 - Default is 0 +/* 062 */ F(SellRate) +/* 063 */ I(CombatEffects) +/* 064 */ I(Shielding) +/* 065 */ I(StunResist) +/* 066 */ I(StrikeThrough) +/* 067 */ I(ExtraDmgSkill) +/* 068 */ I(ExtraDmgAmt) +/* 069 */ I(SpellShield) +/* 070 */ I(Avoidance) +/* 071 */ I(Accuracy) +/* 072 */ I(CharmFileID) +/* 073 */ I(FactionMod1)//Swapped these so Faction Amt comes after each Faction Mod +/* 074 */ I(FactionAmt1)//Swapped these so Faction Amt comes after each Faction Mod +/* 075 */ I(FactionMod2)//Swapped these so Faction Amt comes after each Faction Mod +/* 076 */ I(FactionAmt2)//Swapped these so Faction Amt comes after each Faction Mod +/* 077 */ I(FactionMod3)//Swapped these so Faction Amt comes after each Faction Mod +/* 078 */ I(FactionAmt3)//Swapped these so Faction Amt comes after each Faction Mod +/* 079 */ I(FactionMod4)//Swapped these so Faction Amt comes after each Faction Mod +/* 080 */ I(FactionAmt4)//Swapped these so Faction Amt comes after each Faction Mod +/* 081 */ S(CharmFile) +/* 082 */ I(AugType) +/* 083 */ I(AugRestrict)//augrestrict - Relocated +/* 084 */ I(AugDistiller)//augdistiller - Relocated +/* 085 */ I(AugSlotType[0]) +/* 086 */ I(AugSlotVisible[0])//augslot1visible - Default 1 +/* 087 */ C("0")//augslot1unk2 - Newly Added - Default 0 +/* 088 */ I(AugSlotType[1]) +/* 089 */ I(AugSlotVisible[1]) +/* 090 */ C("0")//augslot2unk2 - Newly Added +/* 091 */ I(AugSlotType[2]) +/* 092 */ I(AugSlotVisible[2]) +/* 093 */ C("0")//augslot3unk2 - Newly Added +/* 094 */ I(AugSlotType[3]) +/* 095 */ I(AugSlotVisible[3]) +/* 096 */ C("0")//augslot4unk2 - Newly Added +/* 097 */ I(AugSlotType[4]) +/* 098 */ I(AugSlotVisible[4]) +/* 099 */ C("0")//augslot5unk2 - Newly Added +/* 100 */ I(PointType)//pointtype - Relocated +/* 101 */ I(LDoNTheme) +/* 102 */ I(LDoNPrice) +/* 103 */ C("70")//UNK098 - Newly Added - Default 70, but some are set to 0 +/* 104 */ I(LDoNSold) +/* 105 */ I(BagType) +/* 106 */ I(BagSlots) +/* 107 */ I(BagSize) +/* 108 */ I(BagWR) +/* 109 */ I(Book) +/* 110 */ I(BookType) +/* 111 */ S(Filename) +/* 112 */ I(LoreGroup) +/* 113 */ I(ArtifactFlag) +/* 114 */ C("0")//I(PendingLoreFlag)?//UNK109 - Default 0, but a few are 1 +/* 115 */ I(Favor) +/* 116 */ I(GuildFavor)//guildfavor - Relocated +/* 117 */ I(FVNoDrop) +/* 118 */ I(DotShielding) +/* 119 */ I(Attack) +/* 120 */ I(Regen) +/* 121 */ I(ManaRegen) +/* 122 */ I(EnduranceRegen) +/* 123 */ I(Haste) +/* 124 */ I(DamageShield) +/* 125 */ C("-1") //UNK120 - Default is -1 +/* 126 */ C("0") //UNK121 - Default is 0 +/* 127 */ I(Attuneable) +/* 128 */ I(NoPet) +/* 129 */ C("0") //UNK124 - Default 0, but a few are 1 +/* 130 */ I(PotionBelt) +/* 131 */ C("0") //potionbeltslots - Default 0, but a few are 1 +/* 132 */ I(StackSize) +/* 133 */ I(NoTransfer) +/* 134 */ I(Stackable)//UNK129 - Default is 0, but some are much higher +/* 135 */ I(QuestItemFlag)//questitemflag - Default is 0 (off), flag on = 1 +/* 136 */ C("0")//UNK131 - Default is 0, but there is an item set to 1 +/* 137 */ C("0")//UNK132 - Default is 0? 0000000000000000000? +/* 138 */ I(Click.Effect) +/* 139 */ I(Click.Type) +/* 140 */ I(Click.Level2) +/* 141 */ I(Click.Level) +/* 142 */ I(MaxCharges)//maxcharges - Relocated +/* 143 */ I(CastTime_)//casttime - Relocated - Note Duplicate Entries for CastTime_ and none for CastTime +/* 144 */ I(RecastDelay)//recastdelay - Relocated +/* 145 */ I(RecastType)//recasttype - Relocated +/* 146 */ C("0")//clickunk5 - Newly Added - Default is 0 +/* 147 */ C("")//clickname - Newly Added - Default is Null +/* 148 */ C("-1")//clickunk7 - Newly Added - Default is -1, but some set to 0 and some much higher +/* 149 */ I(Proc.Effect) +/* 150 */ I(Proc.Type) +/* 151 */ I(Proc.Level2) +/* 152 */ I(Proc.Level) +/* 153 */ C("0")//procunk1 - Newly Added - Default is 0, but some set to -1 and 1 +/* 154 */ C("0")//procunk2 - Newly Added - Default is 0 +/* 155 */ C("0")//procunk3 - Newly Added - Default is 0 +/* 156 */ C("0")//procunk4 - Newly Added - Default is 0 +/* 157 */ I(ProcRate)//procrate - Relocated +/* 158 */ C("")//procname - Newly Added - Default is Null +/* 159 */ C("-1")//procunk7 - Newly Added - Default is -1, but some set to 0 +/* 160 */ I(Worn.Effect) +/* 161 */ I(Worn.Type) +/* 162 */ I(Worn.Level2) +/* 163 */ I(Worn.Level) +/* 164 */ C("0")//wornunk1 - Newly Added - Default is 0 +/* 165 */ C("0")//wornunk2 - Newly Added - Default is 0 +/* 166 */ C("0")//wornunk3 - Newly Added - Default is 0 +/* 167 */ C("0")//wornunk4 - Newly Added - Default is 0 +/* 168 */ C("0")//wornunk5 - Newly Added - Default is 0 +/* 169 */ C("")//wornname - Newly Added - Default is Null +/* 170 */ C("-1")//wornunk7 - Newly Added - Default is -1, but some set to 0 +/* 171 */ I(Focus.Effect) +/* 172 */ I(Focus.Type) +/* 173 */ I(Focus.Level2) +/* 174 */ I(Focus.Level) +/* 175 */ C("0")//focusunk1 - Newly Added - Default is 0 +/* 176 */ C("0")//focusunk2 - Newly Added - Default is 0 +/* 177 */ C("0")//focusunk3 - Newly Added - Default is 0 +/* 178 */ C("0")//focusunk4 - Newly Added - Default is 0 +/* 179 */ C("0")//focusunk5 - Newly Added - Default is 0 +/* 180 */ C("")//focusname - Newly Added - Default is Null +/* 181 */ C("-1")//focusunk7 - Newly Added - Default is -1, but some set to 0 +/* 182 */ I(Scroll.Effect) +/* 183 */ I(Scroll.Type) +/* 184 */ I(Scroll.Level2) +/* 185 */ I(Scroll.Level) +/* 186 */ C("0")//scrollunk1 - Renumber this*** +/* 187 */ C("0")//scrollunk2 - Newly Added - Default is 0 +/* 188 */ C("0")//scrollunk3 - Newly Added - Default is 0 +/* 189 */ C("0")//scrollunk4 - Newly Added - Default is 0 +/* 190 */ C("0")//scrollunk5 - Newly Added - Default is 0 +/* 191 */ C("")//scrollname - Newly Added - Default is Null +/* 192 */ C("-1")//scrollunk7 - Newly Added - Default is -1, but some set to 0 +/* 193 */ C("0")//UNK193 - Default is 0 +/* 194 */ C("0")//purity - Newly Added - Default is 0, but some go up to 75 +/* 195 */ C("0")//dsmitigation - Newly Added - Default is 0, but some are up to 2 +/* 196 */ C("0")//heroic_str - Newly Added - Default is 0 +/* 197 */ C("0")//heroic_int - Newly Added - Default is 0 +/* 198 */ C("0")//heroic_wis - Newly Added - Default is 0 +/* 199 */ C("0")//heroic_agi - Newly Added - Default is 0 +/* 200 */ C("0")//heroic_dex - Newly Added - Default is 0 +/* 201 */ C("0")//heroic_sta - Newly Added - Default is 0 +/* 202 */ C("0")//heroic_cha - Newly Added - Default is 0 +/* 203 */ C("0")//HeroicSvPoison - Newly Added - Default is 0 +/* 204 */ C("0")//HeroicSvMagic - Newly Added - Default is 0 +/* 205 */ C("0")//HeroicSvFire - Newly Added - Default is 0 +/* 206 */ C("0")//HeroicSvDisease - Newly Added - Default is 0 +/* 207 */ C("0")//HeroicSvCold - Newly Added - Default is 0 +/* 208 */ C("0")//HeroicSvCorruption - Newly Added - Default is 0 +/* 209 */ C("0")//healamt - Newly Added - Default is 0, but some are up to 9 +/* 210 */ C("0")//spelldmg - Newly Added - Default is 0, but some are up to 9 +/* 211 */ C("0")//clairvoyance - Newly Added - Default is 0, but some are up to 10 +/* 212 */ C("0")//backstabdmg - Newly Added - Default is 0, but some are up to 65 +//* 213 */ C("0")//evolvinglevel - Newly Added - Default is 0, but some are up to 7 +//* 214 */ C("0")//MaxPower - Newly Added +//* 215 */ C("0")//Power - Newly Added + +//This doesn't appear to be used /* 102 */ S(verified)//verified +//This doesn't appear to be used /* 102 */ S(serialized)//created +//Unsure where this goes right now (or if it is even used) /* 108 */ I(SummonedFlag) + +#else +/* 000 */ //I(ItemClass) Leave this one off on purpose +/* 001 */ S(Name) +/* 002 */ S(Lore) +/* 003 */ C("") //LoreFile? +/* 003 */ S(IDFile) +/* 004 */ I(ID) +/* 005 */ I(Weight) +/* 006 */ I(NoRent) +/* 007 */ I(NoDrop) +/* 008 */ I(Size) +/* 009 */ I(Slots) +/* 010 */ I(Price) +/* 011 */ I(Icon) +/* 013 */ C("0") +/* 014 */ C("0") +/* 014 */ I(BenefitFlag) +/* 015 */ I(Tradeskills) +/* 016 */ I(CR) +/* 017 */ I(DR) +/* 018 */ I(PR) +/* 019 */ I(MR) +/* 020 */ I(FR) + C("0") //svcorruption +/* 021 */ I(AStr) +/* 022 */ I(ASta) +/* 023 */ I(AAgi) +/* 024 */ I(ADex) +/* 025 */ I(ACha) +/* 026 */ I(AInt) +/* 027 */ I(AWis) +/* 028 */ I(HP) +/* 029 */ I(Mana) + I(Endur) +/* 030 */ I(AC) +/* 052 */ I(Classes) +/* 053 */ I(Races) +/* 031 */ I(Deity) +/* 032 */ I(SkillModValue) +/* 033 */ C("0") +/* 034 */ I(SkillModType) +/* 035 */ I(BaneDmgRace) +/* 037 */ I(BaneDmgBody) +/* 036 */ I(BaneDmgRaceAmt) +/* 036 */ I(BaneDmgAmt) +/* 038 */ I(Magic) +/* 039 */ I(CastTime_) +/* 040 */ I(ReqLevel) +/* 045 */ I(RecLevel) +/* 046 */ I(RecSkill) +/* 041 */ I(BardType) +/* 042 */ I(BardValue) +/* 043 */ I(Light) +/* 044 */ I(Delay) +/* 047 */ I(ElemDmgType) +/* 048 */ I(ElemDmgAmt) +/* 049 */ I(Range) +/* 050 */ I(Damage) +/* 051 */ I(Color) +/* 056 */ I(ItemType) +/* 057 */ I(Material) +/* 060 */ C("0") +/* 061 */ C("0") +/* 058 */ F(SellRate) +/* 063 */ I(CombatEffects) +/* 064 */ I(Shielding) +/* 065 */ I(StunResist) +/* 059 */ //C("0") +/* 061 */ //C("0") +/* 066 */ I(StrikeThrough) +/* 067 */ I(ExtraDmgSkill) +/* 068 */ I(ExtraDmgAmt) +/* 069 */ I(SpellShield) +/* 070 */ I(Avoidance) +/* 071 */ I(Accuracy) +/* 072 */ I(CharmFileID) +/* 073 */ I(FactionMod1) +/* 077 */ I(FactionAmt1) +/* 074 */ I(FactionMod2) +/* 078 */ I(FactionAmt2) +/* 075 */ I(FactionMod3) +/* 079 */ I(FactionAmt3) +/* 076 */ I(FactionMod4) +/* 080 */ I(FactionAmt4) +/* 081 */ S(CharmFile) +/* 082 */ I(AugType) +/* 082 */ I(AugRestrict) +/* 082 */ I(AugDistiller) +/* 083 */ I(AugSlotType[0]) +/* 084 */ I(AugSlotVisible[0]) +/* 084 */ I(AugSlotUnk2[0]) +/* 085 */ I(AugSlotType[1]) +/* 086 */ I(AugSlotVisible[1]) +/* 086 */ I(AugSlotUnk2[1]) +/* 087 */ I(AugSlotType[2]) +/* 088 */ I(AugSlotVisible[2]) +/* 088 */ I(AugSlotUnk2[2]) +/* 089 */ I(AugSlotType[3]) +/* 090 */ I(AugSlotVisible[3]) +/* 090 */ I(AugSlotUnk2[3]) +/* 091 */ I(AugSlotType[4]) +/* 092 */ I(AugSlotVisible[4]) +/* 092 */ I(AugSlotUnk2[4]) +/* 093 */ I(PointType) +/* 093 */ I(LDoNTheme) +/* 094 */ I(LDoNPrice) +/* 094 */ C("0") +/* 095 */ I(LDoNSold) +/* 096 */ I(BagType) +/* 097 */ I(BagSlots) +/* 098 */ I(BagSize) +/* 099 */ I(BagWR) +/* 100 */ I(Book) +/* 101 */ I(BookType) +/* 102 */ S(Filename) +/* 105 */ I(LoreGroup) +/* 106 */ //I(PendingLoreFlag) +/* 107 */ I(ArtifactFlag) +/* 094 */ C("0") +/* 108 */ //I(SummonedFlag) +/* 109 */ I(Favor) +/* 121 */ I(GuildFavor) +/* 110 */ I(FVNoDrop) +/* 112 */ I(DotShielding) +/* 113 */ I(Attack) +/* 114 */ I(Regen) +/* 115 */ I(ManaRegen) +/* 116 */ I(EnduranceRegen) +/* 117 */ I(Haste) +/* 118 */ I(DamageShield) +/* 120 */ C("0") +/* 121 */ C("0") +/* 125 */ I(Attuneable) +/* 126 */ I(NoPet) +/* 124 */ C("0") +/* 129 */ I(PotionBelt) +/* 130 */ I(PotionBeltSlots) +/* 131 */ I(StackSize) +/* 132 */ I(NoTransfer) +/* 129 */ C("0") +/* 132 */ I(QuestItemFlag) +/* 131 */ C("0") +/* 132 */ C("00000000000000000000000000000000000000") +/* 134 */ I(Click.Effect) +/* 135 */ I(Click.Type) +/* 136 */ I(Click.Level2) +/* 137 */ I(Click.Level) +/* 055 */ I(MaxCharges) +/* 060 */ I(CastTime) +/* 119 */ I(RecastDelay) +/* 120 */ I(RecastType) +/* 138 */ C("0") //clickunk5 (prolly ProcRate) +/* 138 */ C("") //clickunk6 +/* 138 */ C("-1") //clickunk7 +/* 139 */ I(Proc.Effect) +/* 140 */ I(Proc.Type) +/* 141 */ I(Proc.Level2) +/* 142 */ I(Proc.Level) +/* 143 */ C("0") //procunk1 (prolly MaxCharges) +/* 143 */ C("0") //procunk2 (prolly CastTime) +/* 143 */ C("0") //procunk3 (prolly RecastDelay) +/* 143 */ C("0") //procunk4 (prolly RecastType) +/* 062 */ I(ProcRate) +/* 143 */ C("") //procunk6 +/* 143 */ C("-1") //procunk7 +/* 144 */ I(Worn.Effect) +/* 145 */ I(Worn.Type) +/* 146 */ I(Worn.Level2) +/* 147 */ I(Worn.Level) +/* 143 */ C("0") //wornunk1 (prolly MaxCharges) +/* 143 */ C("0") //wornunk2 (prolly CastTime) +/* 143 */ C("0") //wornunk3 (prolly RecastDelay) +/* 143 */ C("0") //wornunk4 (prolly RecastType) +/* 143 */ C("0") //wornunk5 (prolly ProcRate) +/* 143 */ C("") //wornunk6 +/* 143 */ C("-1") //wornunk7 +/* 149 */ I(Focus.Effect) +/* 150 */ I(Focus.Type) +/* 151 */ I(Focus.Level2) +/* 152 */ I(Focus.Level) +/* 143 */ C("0") //focusunk1 (prolly MaxCharges) +/* 143 */ C("0") //focusunk2 (prolly CastTime) +/* 143 */ C("0") //focusunk3 (prolly RecastDelay) +/* 143 */ C("0") //focusunk4 (prolly RecastType) +/* 143 */ C("0") //focusunk5 (prolly ProcRate) +/* 143 */ C("") //focusunk6 +/* 143 */ C("-1") //focusunk7 +/* 154 */ I(Scroll.Effect) +/* 155 */ I(Scroll.Type) +/* 156 */ I(Scroll.Level2) +/* 157 */ I(Scroll.Level) +/* 143 */ C("0") //scrollunk1 (prolly MaxCharges) +/* 143 */ C("0") //scrollunk2 (prolly CastTime) +/* 143 */ C("0") //scrollunk3 (prolly RecastDelay) +/* 143 */ C("0") //scrollunk4 (prolly RecastType) +/* 143 */ C("0") //scrollunk5 (prolly ProcRate) +/* 143 */ C("") //scrollunk6 +/* 143 */ C("-1") //scrollunk7 +/* 193 */ C("0") //Power Source Capacity +/* 194 */ C("0") //purity + +#endif + +#undef I +#undef C +#undef S +#undef F + diff --git a/common/patches/SoF_opcode_list.h b/common/patches/SoF_opcode_list.h new file mode 100644 index 000000000..ec5d8b11c --- /dev/null +++ b/common/patches/SoF_opcode_list.h @@ -0,0 +1,1229 @@ +// Generated by DumpOpcodeTable.idc + +// This File is not used in the build, but here as a reference for SoF. +// This should be a complete list of all opcodes for SoF. +// The list was generated from IDA using a script from the ShowEQ project. +// This file can be removed at any time. +// It is only a reference the project to upgrade the emulator to use SoF. + +0x10ff, OP_SenseTraps +0x32d3, +0x0cfb, +0x4a4b, +0x4ef3, +0x6b5e, +0x366b, +0x560d, +0x0fb9, +0x082f, +0x20cf, +0x3996, +0x4d38, AckPacket +0x26a0, +0x12ec, +0x042c, +0x6ef2, +0x1040, +0x6fe8, +0x0cea, +0x2de4, OP_ItemLinkClick +0x1f23, +0x5a50, OP_BeginCast +0x19b6, +0x6fad, +0x3c7d, OP_ChannelMessage +0x0860, +0x7274, OP_TimeOfDay +0x5cf3, OP_ClientUpdate +0x2d6f, +0x2d7e, +0x3479, +0x3703, OP_WorldObjectsSent +0x2221, +0x1d07, +0x7a43, +0x30b7, +0x3b11, +0x0a4e, +0x2b67, +0x4e9d, +0x78ab, +0x09e2, +0x08ca, +0x1255, +0x4919, +0x3810, +0x4914, +0x753b, +0x285c, +0x28a9, +0x5962, +0x7647, OP_MobUpdate +0x069c, +0x0406, +0x163c, +0x3f31, +0x68bc, +0x7548, +0x10c6, +0x24d8, OP_RequestClientZoneChange +0x7732, +0x4de4, //Last one tried so far +0x343a, +0x06e4, +0x6f16, +0x0260, +0x5f95, +0x52aa, +0x2978, +0x6457, +0x4791, +0x2d51, +0x2cf7, +0x3dc3, +0x34d4, OP_WhoAllRequest +0x0933, +0x7d8f, +0x317c, OP_GuildMemberList ??? +0x7267, +0x2a86, +0x1eb3, +0x04fb, OP_GuildsList +0x069, +0x5f3c, +0x7b9c, +0x3764, +0x5f05, +0x6e27, +0x29e0, OP_SkillUpdate +0x09c4, +0x05a1, +0x0161, +0x3a46, +0x5c96, +0x5a58, +0x0feb, OP_PlayerProfile +0x0bd9, +0x068d, +0x3dab, +0x0a94, +0x553b, +0x088d, +0x7e73, +0x501d, +0x583c, +0x42fa, +0x4a1a, +0x2c9a, +0x73ab, +0x1036, OP_LevelUpdate +0x6d0b, OP_ExpUpdate +0x681b, +0x076d, +0x6865, +0x7767, +0x288c, +0x5ecb, +0x8c6e, +0x2ada, +0x1510, +0x2659, +0x4f6d, +0x1758, +0x2854, OP_ClientReady +0x649c, OP_RandomReply +0x25bd, +0x7560, +0x2e93, +0x2716, OP_SetRunMode +0x3bec, +0x023d, +0x6102, +0x2ef9, +0x47f6, +0x6f48, +0x71a2, +0x5410, +0x3906, +0x4b52, +0x5f15, +0x5948, +0x49ea, +0x1b42, +0x3ab1, +0x6a70, +0x17c4, +0x65ff, OP_Hide +0x14d3, +0x273d, +0x1103, +0x3791, +0x0375, +0x5f91, +0x5092, +0x5f83, +0x7703, +0x64fc, +0x394f, +0x6e8b, +0x516b, +0x1584, +0x65bf, +0x376d, +0x7374, +0x03a1, +0x4000, +0x571c, +0x3450, +0x5f8c, +0x55ff, +0x0822, +0x7774, +0x05b9, +0x3deb, +0x4543, +0x54a7, +0x56a2, +0x1ee9, +0x7f5d, OP_CastSpell +0x0659, OP_ManaChange +0x3bc7, OP_BuffFadeMsg +0x3209, +0x6a93, OP_MemorizeSpell +0x1237, +0x7df3, +0x4f78, +0x26aa, +0x5ffc, +0x50c0, +0x5bf8, +0x46b3, +0x328a, +0x375d, +0x2a8e, +0x7beb, +0x3739, +0x268f, +0x267a, +0x67df, +0x1f6f, +0x07f6, OP_GroupInvite2 +0x596c, OP_GroupCancelInvite +0x59d4, OP_GroupFollow +0x7871, OP_Jump +0x0292, OP_CorpseLocResponse +0x21f5, +0x7b65, +0x3578, +0x2c86, +0x2e3c, +0x306e, +0x2008, +0x0a1b, OP_ExpansionInfo +0x0ef2, OP_Action2 +0x2e29, +0x227d, +0x7e07, +0x7688, +0x0d97, +0x20e7, +0x0ef0, +0x789f, OP_DeleteCharacter +0x5d22, OP_NewZone +0x5417, OP_ReqNewZone +0x262c, +0x5dc0, +0x7580, OP_Shroud +0x5afa, +0x2bcb, +0x5f4d, OP_Emote2 ??? +0x15da, OP_RemoveSpawn +0x7626, +0x23d7, +0x5fb7, OP_ShopRequest +0x0b2e, +0x21b3, OP_ClickObject +0x30da, OP_GroundSpawn +0x72f2, OP_BankerChange +0x66f2, +0x014c, OP_ReqClientSpawn +0x1fa1, OP_SendExpZonein +0x4b26, OP_GroupDisband +0x08fa, +0x5285, OP_Action +0x6040, OP_SendCharInfo +0x5da8, +0x009b, OP_CharacterCreate ? +0x596f, OP_Death +0x1056, +0x5428, +0x0911, +0x7b6b, +0x7a92, +0x1e2c, +0x251e, +0x7c3b, +0x2f76, +0x36bd, +0x1df4, +0x115f, +0x1b5b, +0x725f, +0x6955, +0x3f9e, +0x3db6, +0x6947, +0x3af9, +0x36e3, +0x6599, +0x51c9, OP_MoneyOnCorpse +0x4530, +0x204a, +0x177d, +0x5f10, +0x6cc8, +0x127b, +0x03e, +0x7811, +0x7431, +0x2037, OP_ApproveName +0x1967, +0x5ac1, +0x61df, +0x3530, +0x62b0, +0x7f86, OP_Illusion +0x25f0, OP_WearChange +0x351e, +0x54c5, +0x50ec, OP_SpawnAppearance +0x430f, +0x110f, +0x7149, +0x0b9a, +0x7e98, +0x46fe, +0x141b, +0x5a84, +0x5bfc, +0x7fa8, +0x69d8, +0x0362, +0x6768, +0x3e9b, +0x2fa1, +0x3760, +0x13a1, OP_Emote +0x5890, +0x201e, OP_ZoneChange +0x5c17, +0x2f33, +0x23fa, +0x31c2, +0x60bc, +0x34ca, +0x17df, +0x0a51, +0x7397, +0x7ae0, +0x4ce7, +0x4ba0, +0x2bb7, +0x0698, +0x14b3, - unknown sent on occasion when nothing is being done - Move Item Charge Failed? +0x1d9d, +0x7ae5, +0x05ea, +0x1b6f, +0x198e, +0x7bd6, OP_Buff +0x3501, +0x47ab, +0x7a9e, OP_World_Client_CRC1 +0x70a5, OP_Weather +0x32e1, OP_Consider +0x4c66, +0x3795, OP_World_Client_CRC2 +0x0580, +0x094d, +0x216c, +0x69e7, +0x2ba1, +0x6be8, +0x55e3, +0x466f, +0x729a, +0x7120, OP_Stamina +0x287e, +0x7546, OP_Logout +0x5335, +0x55bf, +0x7d90, +0x2a85, +0x5e59, +0x5af2, OP_ZoneSpawns +0x045d, +0x3427, +0x4395, OP_TargetMouse +0x1019, +0x7498, +0x79a5, OP_ConfirmDelete +0x60ce, +0x5e93, +0x48f3, +0x475f, +0x7dd4, +0x32e2, +0x5ed6, +0x2d94, +0x6db8, +0x7ba1, +0x2c96, +0x7899, +0x6b17, +0x7678, +0x3874, OP_Logout +0x6017, +0x129a, OP_LogServer +0x683e, +0x237b, +0x7239, +0x25cd, +0x081c, +0x14bf, +0x1418, OP_SwapSpell +0x2675, +0x7d93, +0x4f4a, +0x0fdf, +0x3c23, +0x7fc0, OP_ApproveWorld +0x6459, +0x4e2d, OP_RandomReq +0x1809, +0x79df, +0x55c9, +0x3eba, +0x4b0d, +0x590f, +0x3ccc, +0x343e, +0x421c, +0x6754, +0x48e0, +0x378e, +0x5cc1, +0x57e2, +0x2b02, +0x1608, +0x51f6, +0x6899, +0x411e, +0x2f32, +0x573a, +0x5297, +0x459e, +0x3de3, +0x5546, +0x0835, +0x5711, OP_MOTD +0x7ab6, +0x498f, +0x305a, +0x3fe3, +0x4bb5, +0x266b, +0x537a, +0x2da9, OP_TargetCommand +0x7038, OP_SetServerFilter +0x15a4, +0x7cc8, +0x2512, +0x273f, +0x3309, OP_GetGuildMOTD +0x0e66, +0x1f0f, +0x6807, OP_Camp +0x32b7, +0x5e90, +0x0137, OP_TGB +0x08db, +0x3c97, +0x3518, OP_AAExpUpdate +0x7569, +0x482d, +0x2128, +0x3d12, +0x64b3, +0x6be5, OP_MobRename +0x4b7f, +0x3ed0, +0x1972, +0x6af9, +0x148c, +0x3611, +0x4cbb, +0x553e, OP_SimpleMessage +0x5b9e, OP_FormattedMessage +0x1875, +0x22fa, +0x6d7e, +0x0829, +0x6034, +0x5bd9, +0x29ca, +0x58c8, +0x2f10, +0x3964, +0x0e11, +0x237e, OP_GuildMemberList +0x1d88, +0x626a, +0x14f2, OP_BazaarSearch +0x6b41, +0x0a4f, +0x4426, +0x1acf, +0x6eb9, +0x7dfc, +0x371e, +0x7f23, +0x1445, +0xe24f, +0x7015, +0x573f, +0x31e4, +0x5f7c, +0x144d, +0x1a81, +0x41a2, +0x67f5, +0x0706, +0x0427, +0x0c77, +0x085b, +0x5a64, +0x4697, +0x0c6, +0x24d4, +0x1408, +0x7abf, +0x1651, +0x4e6a, +0x57e3, +0x6f9e, +0x6fac, +0x2eb1, +0x3547, +0x1306, +0x2ff2, +0x56a9, +0x4a44, +0x57a0, +0x58b6, +0x74e5, +0x3131, +0x3c30, +0x443c, +0x36ed, +0x6c89, +0x581a, OP_NewSpawn +0x0fe9, +0x59e2, +0x44f9, +0x1126, OP_SpecialMesg +0x0af9, +0x2e60, OP_TaskActivity +0x4db6, +0x5e80, +0x7d70, +0x13a8, +0x0314, +0x3d03, +0x5b53, +0x2dff, +0x3755, +0x38b6, +0x3817, OP_WhoAllResponse +0x5c51, +0x41be, +0x2f97, +0x0e5a, +0x311a, OP_MobHealth +0x23f1, OP_InitialMobHealth +0x3198, TargetManaStatus? +0x2fd1, +0x7287, TargetEnduranceStatus? +0x1976, +0x21e8, +0x7182, +0x65d8, +0x459b, +0x7d73, +0x6833, +0x6a08, +0x737e, OP_ZoneEntry +0x6aac, +0x4922, +0x25b0, +0x1c68, OP_ZoneEntry ??? +0x768a, +0x42f6, +0x4c29, +0x4b2c, +0x62f6, OP_HPUpdate +0x3df0, +0x1cf0, +0x2992, OP_SendZonepoints +0x5a79, OP_InspectRequest OP_HPUpdate ??? +0x084f, OP_InspectAnswer +0x2e5c, OP_GroupUpdate +0x19f5, +0x389d, +0x083a, +0x66d1, +0x3236, OP_SendZonePoints ??? +0x6c3c, OP_SendLoginInfo +0x3478, OP_InspectAnswer ??? +0x580c, +0x1aee, OP_PostEnterWorld +0x221c, +0x5303, +0x0b2a, +0x6794, +0x5366, +0x1db0, +0x22be, +0x043, +0x185a, +0x1a12, +0x6321, OP_GroupInvite +0x58bd, +0x1340, OP_EnterWorld +0x289c, +0x1509, +0x18b1, OP_ZoneServerInfo +0x07a2, +0x0f07, +0x765d, +0x1939, +0x71b8, OP_SetChatServer +0x01f3, +0x2d48, +0x5ba7, +0x3224, +0x71d1, OP_GuildMemberUpdate +0x718d, +0x5d81, +0x21e0, +0x1f00, +0x601a, +0x6dca, +0x2274, +0x3936, +0x2cdf, +0x6783, +0x7e6c, +0x0e22, +0x26e0, +0x7572, +0x2e09, +0x0b50, +0x4289, +0x4322, +0x5437, +0x174c, +0x1e42, +0x3f2b, +0x07bf, +0x060a, +0x3544, +0x52b3, +0x3cbe, +0x2c85, +0x34ab, +0x3235, +0x6256, +0x57a7, +0x26cc, +0x2b82, +0x08a3, OP_CharInventory +0x2a69, +0x68c8, OP_SpawnDoor +0x1505, +0x22f0, +0x273b, +0x1e62, +0x424a, +0x3a2b, +0x68a6, +0x180d, +0x21c2, +0x2397, +0x4d30, OP_ConsentResponse +0x5f49, OP_GuildMOTD +0x67c0, +0x0fc1, +0x1a80, +0x5d30, +0x6ac8, +0x05b5, +0x5bc2, +0x4f4c, +0x337a, +0x7f4d, +0x7eac, +0x0f28, +0x149a, +0x7783, +0x6d37, +0x083f, +0x7c6e, +0x62f9, +0x6d40, +0x3f26, +0x37fa, +0x7fd9, +0x575a, +0x30f1, +0x7b8d, +0x6e84, +0x526d, +0x10b9, +0x766c, +0x077b, +0x6209, +0x6ca0, +0x73c7, +0x487f, +0x2efb, +0x240f, +0x07ec, +0x1cf7, +0x3e1e, +0x1342, +0x1dbb, +0x0593, +0x0974, +0x1807, +0x785a, +0x632f, +0x2f75, +0x53c2, +0x7d2a, +0x68fa, +0x2ca7, +0x4eb5, +0x59f9, +0x79d7, +0x536f, +0x607e, OP_DenyResponse +0x5b79, +0x7503, +0x7627, +0x01e1, +0x07f0, +0x35f8, +0x7770, +0x6bc8, +0x5232, +0x4b92, +0x5c52, +0x78cd, OP_ItemPacket +0x2a71, +0x7f62, +0x7466, +0x6583, +0x5e9e, +0x172a, +0x265a, +0x20fd, +0x071, +0x5c2f, +0x6213, +0x2ebd, OP_WorldClientReady +0x70b2, +0x2a28, +0x5a3a, +0x6555, +0x1ffe, +0x124a, +0x71fb, OP_TributeUpdate +0x16d6, +0x656c, +0x6764, OP_SendTributes +0x68c2, +0x1d8c, +0x2871, +0x5027, +0x7342, OP_TargetHoTT +0x20e8, +0x6201, +0x53c5, +0x65bc, +0x26be, +0x38f7, +0x683d, +0x44b1, +0x3db7, +0x2df7, +0x47aa, +0x05eb, +0x0912, +0x660d, +0x7b38, +0x5911, +0x426a, +0x2ce8, +0x2c3e, +0x1a75, +0x5116, +0x1793, +0x7a6f, +0x0e8e, +0x3d07, +0x4e4b, +0x6ce2, +0x60a6, +0x02ac, +0x20d6, +0x0516, +0x79d3, +0x5e14, +0x3516, +0x0332, +0x4428, +0x5153, +0x03dd, - Sent when clicking the Change button in the Bank window +0x3f91, +0x2e2c, +0x6a26, +0x1803, +0x3d7d, +0x028b, +0x0ada, OP_CompletedTasks +0x43b6, +0x5b3b, +0x680c, +0x3a69, +0x4012, +0x4349, +0x1dc3, +0x3fe7, +0x6af1, +0x02e3, +0x4788, +0x0d1c, +0x1705, +0x69cb, +0x5ea8, +0x3c73, +0x2224, +0x174a, +0x1ada, +0x2613, +0x45e2, OP_Stamina +0x5872, +0x6774, OP_TributeInfo +0x504b, +0x5665, +0x3ba7, +0x38de, +0x3f33, OP_SendGuildTributes +0x6375, +0x7d5e, +0x5647, +0x6790, +0x6fd3, +0x3223, +0x6b98, +0x48f9, +0x32cc, OP_SetChatServer2 +0x51e1, +0x77a6, +0x7338, +0x206b, +0xe59f, +0x71b9, OP_RespondAA or OP_SendAAStats +0x6f05, OP_SendAATable +0x08f8, +0x218c, +0x24a1, +0x65e4, +0x3445, +0x3754, +0x7205, +0x0b08, +0x66a8, +0x19ff, +0x3ec8, +0x1fae, +0x2c26, +0x6276, +0x30d2, +0x2486, OP_WorldComplete +0x54fe, +0x690b, +0x23f9, +0x179c, +0x7260, - unknown - Client crash if server sends this +0x183e, OP_TributeTimer +0x3e90, +0x1486, +0x45d2, OP_UpdateAA +0x2e8f, +0x7be8, +0x44e2, +0x3fd4, +0x5290, +0x4016, +0x3500, +0x51bf, +0x2d3e, +0x58f7, +0x60db, +0x16f3, +0x7e91, +0x1072, +0x0d4e, +0x1931, +0x6052, +0x33fd, +0x4184, +0x3446, +0x2b2d, +0x3563, +0x74b1, +0x5cea, +0x3207, +0x48c4, +0x43ff, +0x4ee7, +0x4792, +0x23dd, +0x44cc, +0x667e, +0x421e, +0x899b, +0x2906, +0x71b2, +0x5488, +0x1c26, +0x40d6, +0x3c5b, +0x3880, +0x35a6, +0x7065, +0x54c4, +0x2841, +0x30ac, +0x40b0, +0x3f07, +0x7a4d, +0x2148, +0x6edc, +0x17c3, +0x1c98, +0x322e, +0x5d1d, +0x516f, +0x2c8c, +0x6cab, +0x0ad6, +0x5bed, +0x5050, +0x3cde, +0x5802, +0x6a62, +0x354a, +0x0624, +0x17d2, +0x4b46, +0xb3b7, +0x3697, +0x06b7, +0x788a, +0x322d, +0x76e3, +0x3713, +0x61fb, +0x1b8c, +0x09eb, +0x4fc5, +0x64c1, +0x65e2, +0x0730, +0x465f, +0x2bf9, +0x2c66, +0x075f, +0x51ea, +0x4f5a, +0x3187, +0x6675, +0x7aeb, +0x1525, +0x7126, +0x4305, +0x2132, +0x3b00, +0x0180, +0x1f74, +0x29ad, +0x7571, +0x4ecc, +0x58fb, - Auto-AFK after 15 minutes +0x531d, +0x4858, +0x74b5, +0x66a6, +0x1a27, +0x5855, +0x75ac, +0x3035, +0x3a60, +0x5a3b, +0x3c8a, +0x192a, +0x5d0f, +0x16c1, +0x6177, OP_FloatListThing +0x4220, +0x2535, +0x567b, +0x360b, +0x745e, +0x3cca, +0x4a09, +0x5cb4, +0x6bac, +0x474a, +0x428f, OP_Rewind +0x6f58, +0x7266, +0x5d70, +0x4e65, +0x793b, +0x03ce, +0x7641, +0x0e0f, +0x4822, +0xc9f3, +0x556f, +0x58b9, +0x01ff, +0x3632, +0x6d39, +0x6232, +0x364c, +0x5d61, +0x08a8, +0x452f, +0x34ba, +0x1625, +0x436f, +0x61e2, +0x02c4, +0x1de1, +0x3bf3, +0x2173, +0x5c99, +0x1db7, +0x7ea1, +0x0c41, +0x3ab3, +0x3017, +0x79ad, +0x5fd8, +0x3d3a, +0x6373, +0x063b, OP_RespawnWindow +0x7bf6, OP_RespawnFromHover +0x6542, +0x5bda, - Sent when opening Shroud Bank window +0x6aca, +0x073f, +0x31c5, +0x5f6f, +0x1664, +0x7d81, +0x604d, +0x4060, +0x7e32, OP_CharacterCreate +0x044b, +0x6640, +0x0ad2, +0x083d, +0x2f49, +0x7a2a, +0x3fac, +0x0a27, +0x7161, +0x60a5, +0x7930, OP_WorldUnknown001 ??? OP_ObfuscatorInfo +0x3cff, +0x47f1, +0x67e9, +0x39c4, OP_BlockedBuffs +0x125e, +0x33c1, +0x4cf1, +0x4c8c, +0x62a9, +0x3e36, +0x7bd9, +0x143e, +0x5090, +0x5ec8, +0x2f8b, +0x3c32, +0x05da, +0x54b5, +0x349a, +0x7fb9, +0x2d65, +0x48f7, +0x536e, +0x7bb0, +0x4e68, +0x4c74, +0x25a2, +0x3e55, +0x6cfc, +0x0676, +0x5aaa, +0x0959, +0x13db, +0x275a, +0x2fb9, +0x7a36, +0x9ef3, +0x1ee1, +0x0cc2, unknown sent on occasion when nothing is being done +0x41ee, +0x7ab3, +0x097d, +0x2c04, +0x292c, +0x4f28, +0x61cc, +0x4547, +0x0d11, OP_NpcMoveUpdate +0x51ad, +0x17de, +0x116e, +0x67c5, +0x655c, +0x4db4, +0x0887, +0x76ea, +0x1fdd, +0x16a3, +0x33a3, +0x1621, +0x49dc, +0x45f9, +0x50e4, +0x22cf, OP_SendSpellChecksum +0x43ba, OP_SendSkillCapsChecksum +0x10e3, +0x7dd7, +0x4d21, +0x2283, +0x078e, +0x5c04, +0x936e, +0x298d, +0x63c4, +0x1c1b, +0x01cf, +0x197b, +0x5953, +0x21a5, +0x1bd0, +0x33e1, +0x1826, +0x655a, +0x5160, +0x0d59, +0x287f, +0x3c49, +0x19eb, +0x1bf4, +0x533e, +0x0a9d, +0x4c7d, +0x0fda, +0x27fa, +0x0163, +0x767a, +0x1f30, +0x2950, +0x6994, +0x0a8f, +0x493f, +0x137b, +0x204f, +0xd0ba, +0x7125, +0x2866, +0x19a3, +0x0ddf, +0x727d, +0x751c, +0x3e85, - Sent when Guild Management window is opened +0x066a, +0x3a8a, +0x522d, +0x28ec, +0x5ea7, +0x16bf, +0x0d0f, +0x0f05, +0x585f, +0x54cf, +0x4378, +0x433f, +0x3994, +0x2ac3, +0x7b20, +0x7d00, +0x4268, +0x6549, +0x66c8, OP_InventoryWindow +0x1996, +0x3a3b, +0x504f, +0x7c7b, +0x6970, +0x5814, OP_ExpUpdate ??? Unsure +0x0dd0, +0x4114, +0x5d8e, +0x0a42, +0x4eee, +0x6d00, +0x2936, +0x6143, +0x2342, +0x4e45, +0x2be9, +0x21e4, +0x5270, +0x0bc3, +0x7a3f, +0x73fe, +0x2dba, +0x070c, +0x5871, +0x3966, +0x0f8d, +0x78b5, +0x3653, +0x1e69, +0x1ec6, +0x6181, +0xFFFFFFFF, + +// 1217 (0x4c1) opcodes counted + diff --git a/common/patches/SoF_ops.h b/common/patches/SoF_ops.h new file mode 100644 index 000000000..ac8dd2008 --- /dev/null +++ b/common/patches/SoF_ops.h @@ -0,0 +1,100 @@ + +//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) +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_ApplyPoison) +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_OnLevelMessage) +E(OP_AltCurrencySell) +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) +D(OP_Buff) +D(OP_ShopPlayerSell) +D(OP_Consume) +D(OP_CastSpell) +D(OP_Save) +D(OP_ItemVerifyRequest) +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_WearChange) +D(OP_ApplyPoison) +D(OP_DeleteItem) +D(OP_AugmentItem) +D(OP_AltCurrencySellSelection) +D(OP_AltCurrencySell) +#undef E +#undef D diff --git a/common/patches/SoF_structs.h b/common/patches/SoF_structs.h new file mode 100644 index 000000000..3305719cb --- /dev/null +++ b/common/patches/SoF_structs.h @@ -0,0 +1,4066 @@ +#ifndef SoF_STRUCTS_H_ +#define SoF_STRUCTS_H_ + +namespace SoF { + namespace structs { + + +static const uint32 BUFF_COUNT = 25; + +/* +** Compiler override to ensure +** byte aligned structures +*/ +#pragma pack(1) + +struct LoginInfo_Struct { +/*000*/ char login_info[64]; +/*064*/ uint8 unknown064[124]; +/*188*/ uint8 zoning; // 01 if zoning, 00 if not +/*189*/ uint8 unknown189[275]; +/*488*/ +}; + +struct EnterWorld_Struct { +/*000*/ char name[64]; +/*064*/ uint32 tutorial; // 01 on "Enter Tutorial", 00 if not +/*068*/ uint32 return_home; // 01 on "Return Home", 00 if not +}; + +//New For SoF +struct WorldObjectsSent_Struct { +}; + +/* Name Approval Struct */ +/* Len: */ +/* Opcode: 0x8B20*/ +struct NameApproval +{ + char name[64]; + uint32 race; + uint32 class_; + uint32 deity; +}; + +/* +** Entity identification struct +** Size: 4 bytes +** OPCodes: OP_DeleteSpawn, OP_Assist +*/ +struct EntityId_Struct +{ +/*00*/ uint32 entity_id; +/*04*/ +}; + +struct Duel_Struct +{ + uint32 duel_initiator; + uint32 duel_target; +}; + +struct DuelResponse_Struct +{ + uint32 target_id; + uint32 entity_id; + uint32 unknown; +}; +/* + Cofruben: + Adventure stuff,not a net one,just one for our use +*/ +static const uint32 ADVENTURE_COLLECT = 0; +static const uint32 ADVENTURE_MASSKILL = 1; +static const uint32 ADVENTURE_NAMED = 2; +static const uint32 ADVENTURE_RESCUE = 3; + +struct AdventureInfo { + uint32 QuestID; + uint32 NPCID; + bool in_use; + uint32 status; + bool ShowCompass; + uint32 Objetive;// can be item to collect,mobs to kill,boss to kill and someone to rescue. + uint32 ObjetiveValue;// number of items,or number of needed mob kills. + char text[512]; + uint8 type; + uint32 minutes; + uint32 points; + float x; + float y; + uint32 zoneid; + uint32 zonedungeonid; +}; +/////////////////////////////////////////////////////////////////////////////// + + +/* +** Color_Struct +** Size: 4 bytes +** Used for convenience +** Merth: Gave struct a name so gcc 2.96 would compile +** +*/ +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 CharSelectEquip { + //totally guessed; + uint32 equip0; + uint32 equip1; + uint32 itemid; + Color_Struct color; +}; + +struct CharacterSelectEntry_Struct { +/*0000*/ uint8 level; // +/*0000*/ uint8 hairstyle; // +/*0002*/ uint8 gender; // +/*0003*/ char name[1]; //variable length, edi+0 +/*0000*/ uint8 beard; // +/*0001*/ uint8 haircolor; // +/*0000*/ uint8 face; // +/*0000*/ CharSelectEquip equip[9]; +/*0000*/ uint32 primary; // +/*0000*/ uint32 secondary; // +/*0000*/ uint8 u15; // 0xff +/*0000*/ uint32 deity; // +/*0000*/ uint16 zone; // +/*0000*/ uint16 instance; +/*0000*/ uint8 gohome; // +/*0000*/ uint8 u19; // 0xff +/*0000*/ uint32 race; // +/*0000*/ uint8 tutorial; // +/*0000*/ uint8 class_; // +/*0000*/ uint8 eyecolor1; // +/*0000*/ uint8 beardcolor; // +/*0000*/ uint8 eyecolor2; // +/*0000*/ uint32 drakkin_heritage; // Drakkin Heritage +/*0000*/ uint32 drakkin_tattoo; // Drakkin Tattoo +/*0000*/ uint32 drakkin_details; // Drakkin Details (Facial Spikes) +}; + +/* +** Character Selection Struct +** +*/ +struct CharacterSelect_Struct { +/*0000*/ uint32 char_count; //number of chars in this packet +/*0004*/ uint32 total_chars; //total number of chars allowed? +/*0008*/ CharacterSelectEntry_Struct entries[0]; +}; + +/* +* Visible equiptment. +* Size: 12 Octets +*/ +struct EquipStruct { +/*00*/ uint32 equip0; +/*04*/ uint32 equip1; +/*08*/ uint32 itemId; +/*12*/ +}; + + +/* +** Generic Spawn Struct +** Length: 897 Octets +** Used in: +** spawnZoneStruct +** dbSpawnStruct +** petStruct +** newSpawnStruct +*/ +/* +showeq -> eqemu +sed -e 's/_t//g' -e 's/seto_0xFF/set_to_0xFF/g' +*/ +struct Spawn_Struct { +/*0000*/ uint8 showname; //New Field - Toggles Name Display on or off - 0 = off, 1 = on +/*0001*/ uint8 unknown0001[4]; // +/*0005*/ uint8 linkdead; //New Field - Toggles LD on or off after name - 0 = off, 1 = on +/*0006*/ uint8 statue; //New Field - Freezes NPC into a statue pose +/*0007*/ uint8 showhelm; // +/*0008*/ uint8 unknown0008; // +/*0009*/ uint16 deity; // Player's Deity +/*0011*/ uint8 unknown0011[3]; // +/*0014*/ uint32 drakkin_heritage; // Heritage Color on Drakkin 0 - 6 +/*0018*/ uint8 unknown0018[4]; // +/*0022*/ uint8 gender; // Gender (0=male, 1=female, 2=monster) +/*0023*/ uint8 unknown0023[4]; // +/*0027*/ union + { + struct + { + /*0027*/ EquipStruct equip_helmet; // Equiptment: Helmet visual + /*0039*/ EquipStruct equip_chest; // Equiptment: Chest visual + /*0051*/ EquipStruct equip_arms; // Equiptment: Arms visual + /*0063*/ EquipStruct equip_bracers; // Equiptment: Wrist visual + /*0075*/ EquipStruct equip_hands; // Equiptment: Hands visual + /*0087*/ EquipStruct equip_legs; // Equiptment: Legs visual + /*0099*/ EquipStruct equip_feet; // Equiptment: Boots visual + /*0111*/ EquipStruct equip_primary; // Equiptment: Main visual + /*0123*/ EquipStruct equip_secondary; // Equiptment: Off visual + } equip; + /*0027*/ EquipStruct equipment[9]; + }; + +/*0135*/ uint8 StandState; // Seems to be required to be set to 0x64 for normal animation. +/*0136*/ uint8 unknown0136; +/*0137*/ uint32 guildID; // Current guild +/*0141*/ uint32 spelleffect; // Displays a spell effect on spawn +/*0145*/ uint32 spelleffect2; // Appears to be a duplicate of spelleffect +/*0149*/ uint32 spelleffect3; // Appears to be a duplicate of spelleffect +/*0153*/ uint32 spelleffect4; // Appears to be a duplicate of spelleffect +/*0157*/ uint32 spelleffect5; // Appears to be a duplicate of spelleffect +/*0161*/ uint32 spelleffect6; // Appears to be a duplicate of spelleffect +/*0165*/ uint8 class_; // Player's class +/*0166*/ uint8 unknown0166[8]; // +/*0174*/ uint8 flymode; // 0 = flymode off, 1 = flymode on +/*0175*/ uint8 unknown0175[192]; +/*0367*/ uint8 gm; +/*0368*/ uint8 helm; +/*0369*/ uint8 drakkin_tattoo; // Tattoos on Drakkin 0 - 7 +/*0370*/ uint8 unknown0370[3]; +/*0373*/ uint8 beardcolor; // Sets Beard Color +/*0374*/ uint8 unknown0374[128]; +/*0502*/ float runspeed; // Speed when walking +/*0506*/ uint8 light; // Spawn's lightsource +/*0507*/ uint8 unknown0507[4]; +/*0511*/ uint8 level; // Spawn Level +/*0512*/ uint8 unknown0512[16]; +/*0528*/ uint8 lfg; +/*0529*/ uint8 unknown0529[4]; +/*0533*/ uint8 hairstyle; // Sets the style of hair +/*0534*/ uint8 haircolor; // Sets Hair Color +/*0535*/ uint32 race; // Spawn race +/*0539*/ uint8 unknown0539[41]; +/*0580*/ char suffix[32]; // Player's suffix (of Veeshan, etc.) +/*0612*/ uint8 findable; +/*0613*/ uint8 bodytype; // Sets the bodytype of NPCs +/*0614*/ uint8 unknown0614[11]; +/*0625*/ uint8 bodytype2; //New Field - Seems to do the same thing as bodytype +/*0626*/ uint8 unknown0626[28]; +union +{ +/*0654*/ uint8 equip_chest2; // This is Texture for NPCs +/*0654*/ uint8 mount_color; // This should be merged into 1 field, "texture" +}; +/*0655*/ uint8 curHp; // Current hp +/*0656*/ uint8 invis; // 0 = visible, 1 = invis/sneaking +/*0657*/ uint8 unknown0657; +/*0658*/ char lastName[32]; // Player's Lastname +/*0690*/ uint8 unknown0690; +/*0691*/ uint8 eyecolor1; +/*0692*/ char title[32]; // Title +/*0724*/ uint8 beard; +/*0725*/ uint8 targetable; // 1 = Targetable 0 = Not Targetable (is_npc?) +/*0726*/ uint8 unknown0726[4]; +/*0730*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse +/*0731*/ uint8 unknown0731[11]; +/*0742*/ uint8 targetable_with_hotkey; +/*0743*/ signed padding00:12; // ***Placeholder + signed x:19; // x coord + signed padding01:1; // ***Placeholder +/*0747*/ signed deltaX:13; // change in x + signed deltaY:13; // change in y + signed padding02:6; // ***Placeholder +/*0751*/ signed z:19; // z coord + signed deltaHeading:10; // change in heading + signed padding03:3; // ***Placeholder +/*0755*/ signed y:19; // y coord + signed deltaZ:13; // change in z +/*0759*/ signed animation:10; // animation + unsigned heading:12; // heading + signed padding04:10; // ***Placeholder +/*0763*/ uint32 spawnId; // Spawn Id +/*0767*/ uint8 unknown0767[4]; +/*0771*/ uint32 nonvisible; //Non Visible NPC that can only be targeted with /target +/*0775*/ char name[64]; // Player's Name +/*0839*/ uint32 petOwnerId; // If this is a pet, the spawn id of owner +/*0843*/ uint8 pvp; // 0 = normal name color, 2 = PVP name color +/*0844*/ union + { + struct + { + /*0844*/ Color_Struct color_helmet; // Color of helmet item + /*0848*/ Color_Struct color_chest; // Color of chest item + /*0852*/ Color_Struct color_arms; // Color of arms item + /*0856*/ Color_Struct color_bracers; // Color of bracers item + /*0860*/ Color_Struct color_hands; // Color of hands item + /*0864*/ Color_Struct color_legs; // Color of legs item + /*0868*/ Color_Struct color_feet; // Color of feet item + /*0872*/ Color_Struct color_primary; // Color of primary item + /*0876*/ Color_Struct color_secondary; // Color of secondary item + } equipment_colors; + /*0844*/ Color_Struct colors[9]; // Array elements correspond to struct equipment_colors above + }; +/*0880*/ uint8 anon; // 0=normal, 1=anon, 2=roleplay +/*0881*/ uint8 face; +/*0882*/ uint8 drakkin_details; // Face Details (Spikes) on Drakkin 0 - 7 +/*0883*/ uint8 unknown0883[4]; +/*0887*/ float size; +/*0891*/ float walkspeed; // Speed when running +/*0895*/ uint8 unknown0895[2]; +/*0897*/ +}; + + +/* +** Generic Spawn Struct +** Fields from old struct not yet found: +** uint8 traptype; // 65 is disarmable trap, 66 and 67 are invis triggers/traps +** uint8 is_pet; // 0=no, 1=yes +** uint8 afk; // 0=no, 1=afk +** uint8 is_npc; // 0=no, 1=yes +** uint8 max_hp; // (name prolly wrong)takes on the value 100 for players, 100 or 110 for NPCs and 120 for PC corpses... +** uint8 guildrank; // 0=normal, 1=officer, 2=leader +** uint8 eyecolor2; //not sure, may be face +** uint8 aaitle; // 0=none, 1=general, 2=archtype, 3=class +*/ + +/* +** New Spawn +** Length: 176 Bytes +** OpCode: 4921 +*/ +struct NewSpawn_Struct +{ + struct Spawn_Struct spawn; // Spawn Information +}; + + +/* +** Client Zone Entry struct +** Length: 68 Octets +** OpCode: ZoneEntryCode (when direction == client) +*/ +struct ClientZoneEntry_Struct { +/*0000*/ uint32 unknown0000; // ***Placeholder +/*0004*/ char char_name[64]; // Player firstname [32] +//*0036*/ uint8 unknown0036[28]; // ***Placeholder +//*0064*/ uint32 unknown0064; // unknown +}; + + +/* +** Server Zone Entry Struct +** Length: 452 Bytes +** OPCodes: OP_ServerZoneEntry +** +*/ +struct ServerZoneEntry_Struct //Adjusted from SEQ Everquest.h Struct +{ + struct NewSpawn_Struct player; +}; + + +//New Zone Struct - Size: 912 +struct NewZone_Struct { +/*0000*/ char char_name[64]; // Character Name +/*0064*/ char zone_short_name[32]; // Zone Short Name +/*0096*/ char unknown0096[96]; +/*0192*/ char zone_long_name[278]; // Zone Long Name +/*0470*/ uint8 ztype; // Zone type (usually FF) +/*0471*/ uint8 fog_red[4]; // Zone fog (red) +/*0475*/ uint8 fog_green[4]; // Zone fog (green) +/*0479*/ uint8 fog_blue[4]; // Zone fog (blue) +/*0483*/ uint8 unknown323; +/*0484*/ float fog_minclip[4]; +/*0500*/ float fog_maxclip[4]; +/*0516*/ float gravity; +/*0520*/ uint8 time_type; +/*0521*/ uint8 unknown521[49]; +/*0570*/ uint8 sky; // Sky Type +/*0571*/ uint8 unknown571[13]; // ***Placeholder +/*0584*/ float zone_exp_multiplier; // Experience Multiplier +/*0588*/ float safe_y; // Zone Safe Y +/*0592*/ float safe_x; // Zone Safe X +/*0596*/ float safe_z; // Zone Safe Z +/*0600*/ float max_z; // Guessed +/*0604*/ float underworld; // Underworld, min z (Not Sure?) +/*0608*/ float minclip; // Minimum View Distance +/*0612*/ float maxclip; // Maximum View DIstance +/*0616*/ uint8 unknown_end[84]; // ***Placeholder +/*0700*/ char zone_short_name2[96]; //zone file name? excludes instance number which can be in previous version. +/*0796*/ int32 unknown796; //seen -1 +/*0800*/ char unknown800[40]; // +/*0840*/ int32 unknown840; //seen 600 +/*0844*/ int32 unknown844; +/*0848*/ uint16 zone_id; +/*0850*/ uint16 zone_instance; +/*0852*/ char unknown852[20]; +/*0872*/ uint32 SuspendBuffs; +/*0876*/ uint32 unknown876; //seen 50 +/*0880*/ uint32 unknown880; //seen 10 +/*0884*/ uint8 unknown884; //seen 1 +/*0885*/ uint8 unknown885; //seen 0 (POK) or 1 (rujj) +/*0886*/ uint8 unknown886; //seen 1 +/*0887*/ uint8 unknown887; //seen 0 +/*0888*/ uint8 unknown888; //seen 0 +/*0893*/ uint8 unknown889; //seen 0 - 00 +/*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off +/*0895*/ uint8 unknown891; //seen 0 - 00 +/*0892*/ uint32 unknown892; //seen 180 +/*0896*/ uint32 unknown896; //seen 180 +/*0900*/ uint32 unknown900; //seen 180 +/*0904*/ uint32 unknown904; //seen 2 +/*0908*/ uint32 unknown908; //seen 2 +/*0912*/ +}; + + +/* +** Memorize Spell Struct +** Length: 16 Bytes +** +*/ +struct MemorizeSpell_Struct { +uint32 slot; // Spot in the spell book/memorized slot +uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) +uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming +//uint32 unknown12; +}; + +/* +** Make Charmed Pet +** Length: 12 Bytes +** +*/ +struct Charm_Struct { +/*00*/ uint32 owner_id; +/*04*/ uint32 pet_id; +/*08*/ uint32 command; // 1: make pet, 0: release pet +/*12*/ +}; + +struct InterruptCast_Struct +{ + uint32 spawnid; + uint32 messageid; + char message[0]; +}; + +struct DeleteSpell_Struct +{ +/*000*/int16 spell_slot; +/*002*/uint8 unknowndss002[2]; +/*004*/uint8 success; +/*005*/uint8 unknowndss006[3]; +/*008*/ +}; + +struct ManaChange_Struct +{ + uint32 new_mana; // New Mana AMount + uint32 stamina; + uint32 spell_id; + uint32 unknown12; + uint32 unknown16; +}; + +struct SwapSpell_Struct +{ + uint32 from_slot; + uint32 to_slot; + + +}; + +struct BeginCast_Struct +{ + // len = 8 +/*004*/ uint16 caster_id; +/*006*/ uint16 spell_id; +/*016*/ uint32 cast_time; // in miliseconds +}; + +struct CastSpell_Struct +{ + uint32 slot; + uint32 spell_id; + uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast + uint32 target_id; + uint8 cs_unknown[4]; +}; + +/* +** SpawnAppearance_Struct +** Changes client appearance for all other clients in zone +** Size: 8 bytes +** Used in: OP_SpawnAppearance +** +*/ +struct SpawnAppearance_Struct +{ +/*0000*/ uint16 spawn_id; // ID of the spawn +/*0002*/ uint16 type; // Values associated with the type +/*0004*/ uint32 parameter; // Type of data sent +/*0008*/ +}; + + +// solar: this is used inside profile +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 unknown004; //Might need to be swapped with player_id +/*020*/ uint32 player_id; //'global' ID of the caster, for wearoff messages +/*024*/ + + +}; + + +struct SpellBuffFade_Struct { +/*000*/ uint32 entityid; +/*004*/ uint8 slot; +/*005*/ uint8 level; +/*006*/ uint8 effect; +/*007*/ uint8 unknown7; +/*008*/ uint32 spellid; +/*012*/ uint32 duration; +/*016*/ uint32 unknown016; +/*020*/ uint32 unknown020; //prolly global player ID +/*024*/ uint32 playerId; // Player id who cast the buff +/*028*/ uint32 slotid; +/*032*/ uint32 bufffade; +/*036*/ +}; + +struct GMTrainee_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ uint32 skills[73]; + /*300*/ uint8 unknown300[148]; + /*448*/ +}; + +struct GMTrainEnd_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ +}; + +struct GMSkillChange_Struct { +/*000*/ uint16 npcid; +/*002*/ uint8 unknown1[2]; // something like PC_ID, but not really. stays the same thru the session though +/*002*/ uint16 skillbank; // 0 if normal skills, 1 if languages +/*002*/ uint8 unknown2[2]; +/*008*/ uint16 skill_id; +/*010*/ uint8 unknown3[2]; +}; +struct ConsentResponse_Struct { + char grantname[64]; + char ownername[64]; + uint8 permission; + char zonename[64]; +}; + +/* +** Name Generator Struct +** Length: 72 bytes +** OpCode: 0x0290 +*/ +struct NameGeneration_Struct +{ +/*0000*/ uint32 race; +/*0004*/ uint32 gender; +/*0008*/ char name[64]; +/*0072*/ +}; + +/* +** Character Creation struct +** Length: 140 Bytes +** OpCode: 0x009b +*/ +struct CharCreate_Struct +{ +/*0000*/ uint32 class_; +/*0004*/ uint32 haircolor; +/*0008*/ uint32 beard; +/*0012*/ uint32 beardcolor; +/*0016*/ uint32 gender; +/*0020*/ uint32 race; +/*0024*/ uint32 start_zone; +/*0028*/ uint32 hairstyle; +/*0032*/ uint32 deity; +/*0036*/ uint32 STR; +/*0040*/ uint32 STA; +/*0044*/ uint32 AGI; +/*0048*/ uint32 DEX; +/*0052*/ uint32 WIS; +/*0056*/ uint32 INT; +/*0060*/ uint32 CHA; +/*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 tutorial; +/*0080*/ uint32 drakkin_heritage; +/*0084*/ uint32 drakkin_tattoo; +/*0088*/ uint32 drakkin_details; +/*0092*/ +}; + +/* +** Character Creation struct +** Length: 0 Bytes +** OpCode: 0x +*/ +struct CharCreate_Struct_Temp //Size is now 0 +{ +}; + +/* + *Used in PlayerProfile + */ +struct AA_Array +{ + uint32 AA; + uint32 value; + uint32 unknown08; // Looks like AA_Array is now 12 bytes in Live +}; + + +static const uint32 MAX_PP_DISCIPLINES = 100; + +struct Disciplines_Struct { + uint32 values[MAX_PP_DISCIPLINES]; +}; + +static const uint32 MAX_PLAYER_TRIBUTES = 5; +static const uint32 MAX_PLAYER_BANDOLIER = 20; +static const uint32 MAX_PLAYER_BANDOLIER_ITEMS = 4; +static const uint32 MAX_POTIONS_IN_BELT = 5; +static const uint32 TRIBUTE_NONE = 0xFFFFFFFF; + +struct Tribute_Struct { + uint32 tribute; + uint32 tier; +}; + +//len = 72 +struct BandolierItem_Struct { + uint32 item_id; + uint32 icon; + char item_name[64]; +}; + +//len = 320 +enum { //bandolier item positions + bandolierMainHand = 0, + bandolierOffHand, + bandolierRange, + bandolierAmmo +}; +struct Bandolier_Struct { + char name[32]; + BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; +}; +struct PotionBelt_Struct { + BandolierItem_Struct items[MAX_POTIONS_IN_BELT]; +}; + +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]; +}; +struct RaidLeadershipAA_Struct { + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; +}; + + /** +* A bind point. +* Size: 20 Octets +*/ +struct BindStruct { + /*000*/ uint32 zoneId; + /*004*/ float x; + /*008*/ float y; + /*012*/ float z; + /*016*/ float heading; + /*020*/ +}; + + +/* +** Player Profile +** +** Length: 4308 bytes +** OpCode: 0x006a + */ +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 = 75; +static const uint32 MAX_PP_AA_ARRAY = 300; //was 299 +static const uint32 MAX_GROUP_MEMBERS = 6; +static const uint32 MAX_RECAST_TYPES = 20; +/* +showeq -> eqemu +sed -e 's/_t//g' -e 's/MAX_AA/MAX_PP_AA_ARRAY/g' \ + -e 's/MAX_SPELL_SLOTS/MAX_PP_MEMSPELL/g' \ + -e 's/MAX_KNOWN_SKILLS/MAX_PP_SKILL/g' \ + -e 's/MAXRIBUTES/MAX_PLAYER_TRIBUTES/g' \ + -e 's/MAX_BUFFS/BUFF_COUNT/g' \ + -e 's/MAX_KNOWN_LANGS/MAX_PP_LANGUAGE/g' \ + -e 's/MAX_RECASTYPES/MAX_RECAST_TYPES/g' \ + -e 's/spellBuff/SpellBuff_Struct/g' \ + -e 's/lastName/last_name/g' \ + -e 's/guildID/guild_id/g' \ + -e 's/itemint/item_tint/g' \ + -e 's/MANA/mana/g' \ + -e 's/curHp/cur_hp/g' \ + -e 's/sSpellBook/spell_book/g' \ + -e 's/sMemSpells/mem_spells/g' \ + -e 's/uint32[ \t]*disciplines\[MAX_DISCIPLINES\]/Disciplines_Struct disciplines/g' \ + -e 's/aa_unspent/aapoints/g' \ + -e 's/aa_spent/aapoints_spent/g' \ + -e 's/InlineItem[ \t]*potionBelt\[MAX_POTIONS_IN_BELT\]/PotionBelt_Struct potionbelt/g' \ + -e 's/ldon_guk_points/ldon_points_guk/g' \ + -e 's/ldon_mir_points/ldon_points_mir/g' \ + -e 's/ldon_mmc_points/ldon_points_mmc/g' \ + -e 's/ldon_ruj_points/ldon_points_ruj/g' \ + -e 's/ldonak_points/ldon_points_tak/g' \ + -e 's/ldon_avail_points/ldon_points_available/g' \ + -e 's/tributeTime/tribute_time_remaining/g' \ + -e 's/careerTribute/career_tribute_points/g' \ + -e 's/currentTribute/tribute_points/g' \ + -e 's/tributeActive/tribute_active/g' \ + -e 's/TributeStruct/Tribute_Struct/g' \ + -e 's/expGroupLeadAA/group_leadership_exp/g' \ + -e 's/expRaidLeadAA/raid_leadership_exp/g' \ + -e 's/groupLeadAAUnspent/group_leadership_points/g' \ + -e 's/raidLeadAAUnspent/raid_leadership_points/g' \ + -e 's/uint32[ \t]*leadershipAAs\[MAX_LEAD_AA\]/LeadershipAA_Struct leader_abilities/g' \ + -e 's/BandolierStruct/Bandolier_Struct/g' \ + -e 's/MAX_BANDOLIERS/MAX_PLAYER_BANDOLIER/g' \ + -e 's/birthdayTime/birthday/g' \ + -e 's/lastSaveTime/lastlogin/g' \ + -e 's/zoneId/zone_id/g' \ + -e 's/hunger/hunger_level/g' \ + -e 's/thirst/thirst_level/g' \ + -e 's/guildstatus/guildrank/g' \ + -e 's/airRemaining/air_remaining/g' \ + */ + + +struct PlayerProfile_Struct //23576 Octets +{ +/*00000*/ uint32 checksum; // +//BEGIN SUB-STRUCT used for shrouding stuff... +/*00004*/ uint32 gender; // Player Gender - 0 Male, 1 Female +/*00008*/ uint32 race; // Player race +/*00012*/ uint32 class_; // Player class +/*00016*/ uint8 unknown00016[40]; // #### uint32 unknown00016; in Titanium ####uint8[40] +/*00056*/ uint8 level; // Level of player +/*00057*/ uint8 level1; // Level of player (again?) +/*00058*/ uint8 unknown00022[2]; // ***Placeholder +/*00060*/ BindStruct binds[5]; // Bind points (primary is first) +/*00160*/ uint32 deity; // deity +/*00164*/ uint32 intoxication; // Alcohol level (in ticks till sober?) +/*00168*/ uint32 spellSlotRefresh[MAX_PP_MEMSPELL]; // Refresh time (millis) - 4 Octets Each +/*00208*/ uint32 abilitySlotRefresh; +/*00212*/ uint8 haircolor; // Player hair color +/*00213*/ uint8 beardcolor; // Player beard color +/*00214*/ uint8 eyecolor1; // Player left eye color +/*00215*/ uint8 eyecolor2; // Player right eye color +/*00216*/ uint8 hairstyle; // Player hair style +/*00217*/ uint8 beard; // Player beard type +/*00218*/ uint8 unknown00178[10]; //[10]14 on Live? +/*00228*/ union + { + struct + { + /*00228*/ EquipStruct equip_helmet; // Equiptment: Helmet visual + /*00240*/ EquipStruct equip_chest; // Equiptment: Chest visual + /*00252*/ EquipStruct equip_arms; // Equiptment: Arms visual + /*00264*/ EquipStruct equip_bracers; // Equiptment: Wrist visual + /*00276*/ EquipStruct equip_hands; // Equiptment: Hands visual + /*00288*/ EquipStruct equip_legs; // Equiptment: Legs visual + /*00300*/ EquipStruct equip_feet; // Equiptment: Boots visual + /*00312*/ EquipStruct equip_primary; // Equiptment: Main visual + /*00324*/ EquipStruct equip_secondary; // Equiptment: Off visual + } equip; + /*00228*/ EquipStruct equipment[9]; //Live Shows [108] for this part + }; +/*00336*/ uint8 unknown00224[156]; // Live Shows [160] +/*00496*/ Color_Struct item_tint[9]; // RR GG BB 00 +/*00544*/ AA_Array aa_array[MAX_PP_AA_ARRAY]; // [3600] AAs 12 bytes each +/*04132*/ uint32 points; // Unspent Practice points - RELOCATED??? +/*04136*/ uint32 mana; // Current mana +/*04140*/ uint32 cur_hp; // Current HP without +HP equipment +/*04144*/ uint32 STR; // Strength - 6e 00 00 00 - 110 +/*04148*/ uint32 STA; // Stamina - 73 00 00 00 - 115 +/*04152*/ uint32 CHA; // Charisma - 37 00 00 00 - 55 +/*04156*/ uint32 DEX; // Dexterity - 50 00 00 00 - 80 +/*04160*/ uint32 INT; // Intelligence - 3c 00 00 00 - 60 +/*04164*/ uint32 AGI; // Agility - 5f 00 00 00 - 95 +/*04168*/ uint32 WIS; // Wisdom - 46 00 00 00 - 70 +/*04172*/ uint8 face; // Player face +/*04173*/ uint8 unknown02264[147]; // was [139] +/*04312*/ uint32 spell_book[MAX_PP_SPELLBOOK]; // List of the Spells in spellbook 480 = 60 pages +/*06232*/ uint8 unknown4184[128]; // was [136] +/*06396*/ uint32 mem_spells[MAX_PP_MEMSPELL]; // List of spells memorized +/*06436*/ uint8 unknown04396[28]; //#### uint8 unknown04396[32]; in Titanium ####[28] +/*06464*/ uint32 platinum; // Platinum Pieces on player +/*06468*/ uint32 gold; // Gold Pieces on player +/*06472*/ uint32 silver; // Silver Pieces on player +/*06476*/ uint32 copper; // Copper Pieces on player +/*06480*/ uint32 platinum_cursor; // Platinum Pieces on cursor +/*06484*/ uint32 gold_cursor; // Gold Pieces on cursor +/*06488*/ uint32 silver_cursor; // Silver Pieces on cursor +/*06492*/ uint32 copper_cursor; // Copper Pieces on cursor +/*06496*/ uint32 skills[MAX_PP_SKILL]; // [300] List of skills +/*06796*/ uint8 unknown04760[236]; +/*07032*/ uint32 toxicity; // Potion Toxicity (15=too toxic, each potion adds 3) +/*07036*/ uint32 thirst_level; // Drink (ticks till next drink) +/*07040*/ uint32 hunger_level; // Food (ticks till next eat) +/*07044*/ SpellBuff_Struct buffs[BUFF_COUNT]; // [600]Buffs currently on the player +/*07644*/ Disciplines_Struct disciplines; // [400] Known disciplines +/*08044*/ uint32 recastTimers[MAX_RECAST_TYPES]; // Timers (UNIX Time of last use) +/*08124*/ uint8 unknown08124[160]; // Some type of Timers +/*08284*/ uint32 endurance; // Current endurance +/*08288*/ uint32 aapoints_spent; // Number of spent AA points +/*08292*/ uint32 aapoints; // Unspent AA points +/*08296*/ uint8 unknown06160[4]; +/*08300*/ Bandolier_Struct bandoliers[MAX_PLAYER_BANDOLIER]; // [6400] bandolier contents +/*14700*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot +/*15060*/ uint8 unknown12852[8]; +/*15068*/ uint32 available_slots; +/*15072*/ uint8 unknown12864[80]; //#### uint8 uint8 unknown12864[76]; in Titanium ####[80] +//END SUB-STRUCT used for shrouding. +/*15120*/ char name[64]; // Name of player +/*15184*/ char last_name[32]; // Last name of player +/*15216*/ uint8 unknown19588[12]; //#### Not In Titanium #### new to SoF[12] +/*15228*/ uint32 guild_id; // guildid +/*15232*/ uint32 birthday; // character birthday +/*15236*/ uint32 lastlogin; // character last save time +/*15240*/ uint32 timePlayedMin; // time character played +//*21020*/ uint8 unknown1959[4]; // was uint32 unknown19588; - Not in SoF??? +/*15244*/ uint8 pvp; // 1=pvp, 0=not pvp +/*15245*/ uint8 anon; // 2=roleplay, 1=anon, 0=not anon +/*15246*/ uint8 gm; // 0=no, 1=yes (guessing!) +/*15247*/ uint8 guildrank; // 0=member, 1=officer, 2=guildleader +/*15248*/ uint32 guildbanker; +/*15252*/ uint8 unknown13054[8]; //[12] +/*15260*/ uint32 exp; // Current Experience +/*15264*/ uint8 unknown13072[8]; +/*15272*/ uint32 timeentitledonaccount; +/*15276*/ uint8 languages[MAX_PP_LANGUAGE]; // List of languages +/*15301*/ uint8 unknown13109[7]; //#### uint8 unknown13109[4]; in Titanium ####[7] +/*15308*/ float y; // Players y position (NOT positive about this switch) +/*15312*/ float x; // Players x position +/*15316*/ float z; // Players z position +/*15320*/ float heading; // Players heading +/*15324*/ uint8 unknown13132[4]; // ***Placeholder +/*15328*/ uint32 platinum_bank; // Platinum Pieces in Bank +/*15332*/ uint32 gold_bank; // Gold Pieces in Bank +/*15336*/ uint32 silver_bank; // Silver Pieces in Bank +/*15340*/ uint32 copper_bank; // Copper Pieces in Bank +/*15344*/ uint32 platinum_shared; // Shared platinum pieces +/*15348*/ uint8 unknown13156[716]; //#### uint8 unknown13156[84]; in Titanium ####[716] +/*16064*/ uint32 expansions; // Bitmask for expansions +/*16068*/ uint8 unknown13244[12]; +/*16080*/ uint32 autosplit; // 0 = off, 1 = on +/*16084*/ uint8 unknown13260[16]; +/*16100*/ uint16 zone_id; // see zones.h +/*16102*/ uint16 zoneInstance; // Instance id +/*16104*/ char groupMembers[MAX_GROUP_MEMBERS][64];// 384 all the members in group, including self +/*16488*/ char groupLeader[64]; // Leader of the group ? +/*16552*/ uint8 unknown13728[788]; //#### uint8 unknown13728[660]; in Titanium ####[792] +/*17340*/ uint32 entityid; +/*17344*/ uint32 leadAAActive; // 0 = leader AA off, 1 = leader AA on +/*17348*/ uint8 unknown14392[4]; +/*17352*/ int32 ldon_points_guk; // Earned GUK points +/*17356*/ int32 ldon_points_mir; // Earned MIR points +/*17360*/ int32 ldon_points_mmc; // Earned MMC points +/*17364*/ int32 ldon_points_ruj; // Earned RUJ points +/*17368*/ int32 ldon_points_tak; // Earned TAK points +/*17372*/ int32 ldon_points_available; // Available LDON points +/*17376*/ uint8 unknown14420[136]; //#### uint8 unknown14420[132]; in Titanium ####[136] +/*17512*/ float tribute_time_remaining; // Time remaining on tribute (millisecs) +/*17516*/ uint32 career_tribute_points; // Total favor points for this char +/*17520*/ uint32 unknown7208; // *** Placeholder +/*17524*/ uint32 tribute_points; // Current tribute points +/*17528*/ uint32 unknown7216; // *** Placeholder +/*17532*/ uint32 tribute_active; // 0 = off, 1=on +/*17536*/ Tribute_Struct tributes[MAX_PLAYER_TRIBUTES]; // [40] Current tribute loadout +/*17576*/ uint8 unknown14616[4]; +/*17580*/ double group_leadership_exp; // Current group lead exp points +/*17588*/ double raid_leadership_exp; // Current raid lead AA exp points +/*17596*/ uint32 group_leadership_points; // Unspent group lead AA points +/*17600*/ uint32 raid_leadership_points; // Unspent raid lead AA points +/*17604*/ LeadershipAA_Struct leader_abilities; // [128]Leader AA ranks +/*17732*/ uint8 unknown14772[128]; +/*17860*/ uint32 air_remaining; // Air supply (seconds) +/*17864*/ uint32 PVPKills; +/*17868*/ uint32 PVPDeaths; +/*17872*/ uint32 PVPCurrentPoints; +/*17876*/ uint32 PVPCareerPoints; +/*17880*/ uint32 PVPBestKillStreak; +/*17884*/ uint32 PVPWorstDeathStreak; +/*17888*/ uint32 PVPCurrentKillStreak; +/*17892*/ PVPStatsEntry_Struct PVPLastKill; +/*17980*/ PVPStatsEntry_Struct PVPLastDeath; +/*18068*/ uint32 PVPNumberOfKillsInLast24Hours; +/*18072*/ PVPStatsEntry_Struct PVPRecentKills[50]; +/*22472*/ uint32 expAA; // Exp earned in current AA point +/*22476*/ uint8 unknown19516[40]; +/*22516*/ uint32 currentRadCrystals; // Current count of radiant crystals +/*22520*/ uint32 careerRadCrystals; // Total count of radiant crystals ever +/*22524*/ uint32 currentEbonCrystals; // Current count of ebon crystals +/*22528*/ uint32 careerEbonCrystals; // Total count of ebon crystals ever +/*22532*/ uint8 groupAutoconsent; // 0=off, 1=on +/*22533*/ uint8 raidAutoconsent; // 0=off, 1=on +/*22534*/ uint8 guildAutoconsent; // 0=off, 1=on +/*22535*/ uint8 unknown19575; // ***Placeholder (6/29/2005) +/*22536*/ uint32 level3; // SoF looks at the level here to determine how many leadership AA you can bank. +/*22540*/ uint32 showhelm; // 0=no, 1=yes +/*22544*/ uint32 RestTimer; +/*22544*/ uint8 unknown19584[1028]; // ***Placeholder (2/13/2007)[1032] - END of Struct +/*23576*/ +}; + +/** + * Shroud spawn. For others shrouding, this has their spawnId and + * spawnStruct. + * + * Length: 586 + * OpCode: OP_Shroud + */ +//struct spawnShroudOther_Struct +//{ +//*0000*/ uint32 spawnId; // Spawn Id of the shrouded player +//*0004*/ spawn_Struct spawn; // Updated spawn struct for the player +//*0586*/ +//}; + +/** + * Shroud yourself. For yourself shrouding, this has your spawnId, spawnStruct, + * bits of your charProfileStruct (no checksum, then charProfile up till + * but not including name), and an itemPlayerPacket for only items on the player + * and not the bank. + * + * Length: Variable + * OpCode: OP_Shroud + */ +//struct spawnShroudSelf_Struct +//{ +//*00000*/ uint32 spawnId; // Spawn Id of you +//*00004*/ spawn_Struct spawn; // Updated spawnStruct for you +//*00586*/ PlayerProfile_Struct profile; // Character profile for shrouded char +//*13522*/ uint8 items; // Items on the player +/*xxxxx*/ +//}; + + + +/* +** Client Target Struct +** Length: 2 Bytes +** OpCode: 6221 +*/ +struct ClientTarget_Struct { +/*000*/ uint32 new_target; // Target ID +}; + +/* +** Target Rejection Struct +** Length: 12 Bytes +** OpCode: OP_TargetReject +*/ +struct TargetReject_Struct { +/*00*/ uint8 unknown00[12]; +}; + +struct PetCommand_Struct { +/*000*/ uint32 command; +/*004*/ uint32 unknown; +}; + +/* +** Delete Spawn +** Length: 4 Bytes +** OpCode: OP_DeleteSpawn +*/ +struct DeleteSpawn_Struct +{ +/*00*/ uint32 spawn_id; // Spawn ID to delete +/*04*/ +}; + +/* +** Channel Message received or sent +** Length: 144 Bytes + Variable Length + 1 +** OpCode: OP_ChannelMessage +** +*/ +struct ChannelMessage_Struct +{ +/*000*/ char targetname[64]; // Tell recipient +/*064*/ char sender[64]; // The senders name (len might be wrong) +/*128*/ uint32 language; // Language +/*132*/ uint32 chan_num; // Channel +/*136*/ uint32 cm_unknown4[2]; // ***Placeholder +/*144*/ uint32 skill_in_language; // The players skill in this language? might be wrong +/*148*/ char message[0]; // Variable length message +}; + +/* +** Special Message +** Length: 4 Bytes + Variable Text Length + 1 +** OpCode: OP_SpecialMesg +** +*/ +/* + Theres something wrong with this... example live packet: +Server->Client: [ Opcode: OP_SpecialMesg (0x0fab) Size: 244 ] + 0: 01 02 00 0A 00 00 00 09 - 05 00 00 42 61 72 73 74 | ...........Barst + 16: 72 65 20 53 6F 6E 67 77 - 65 61 76 65 72 00 7C F9 | re Songweaver.|. + 32: FF FF 84 FF FF FF 03 00 - 00 00 47 72 65 65 74 69 | ..........Greeti + +*/ +struct SpecialMesg_Struct +{ +/*00*/ char header[3]; // 04 04 00 <-- for #emote style msg +/*03*/ uint32 msg_type; // Color of text (see MT_*** below) +/*07*/ uint32 target_spawn_id; // Who is it being said to? +/*11*/ char sayer[1]; // Who is the source of the info - Was 1 +/*12*/ uint8 unknown12[12]; +/*24*/ char message[128]; // What is being said? - was 128 +}; + +/* +** When somebody changes what they're wearing +** or give a pet a weapon (model changes) +** Length: 19 Bytes +*/ +struct WearChange_Struct{ +/*000*/ uint16 spawn_id; +/*002*/ uint32 material; +/*006*/ uint32 unknown06; +/*010*/ uint32 elite_material; // 1 for Drakkin Elite Material +/*014*/ Color_Struct color; +/*018*/ uint8 wear_slot_id; +/*019*/ +}; + +/* +** Type: Bind Wound Structure +** Length: 8 Bytes +*/ +//Fixed for 7-14-04 patch +struct BindWound_Struct +{ +/*000*/ uint16 to; // TargetID +/*002*/ uint16 unknown2; // ***Placeholder +/*004*/ uint16 type; +/*006*/ uint16 unknown6; +}; + + +/* +** Type: Zone Change Request (before hand) +** Length: 88 bytes +** OpCode: a320 +*/ + +struct ZoneChange_Struct { +/*000*/ char char_name[64]; // Character Name +/*064*/ uint16 zoneID; +/*066*/ uint16 instanceID; +/*068*/ float y; +/*072*/ float x; +/*076*/ float z; +/*080*/ uint32 zone_reason; //0x0A == death, I think +/*084*/ int32 success; // =0 client->server, =1 server->client, -X=specific error +/*088*/ +}; + +struct RequestClientZoneChange_Struct { +/*00*/ uint16 zone_id; +/*02*/ uint16 instance_id; +/*04*/ float y; +/*08*/ float x; +/*12*/ float z; +/*16*/ float heading; +/*20*/ uint32 type; //unknown... values +}; + +struct Animation_Struct { +/*00*/ uint16 spawnid; +/*02*/ uint8 action; +/*03*/ uint8 value; +/*04*/ +}; + +// solar: this is what causes the caster to animate and the target to +// get the particle effects around them when a spell is cast +// also causes a buff icon +struct Action_Struct +{ + /* 00 */ uint16 target; // id of target + /* 02 */ uint16 source; // id of caster + /* 04 */ uint16 level; // level of caster + /* 06 */ uint16 instrument_mod; // seems to be fixed to 0x0A + /* 08 */ uint32 unknown08; + /* 12 */ uint16 unknown16; +// some kind of sequence that's the same in both actions +// as well as the combat damage, to tie em together? + /* 14 */ float sequence; // was uint32 + /* 18 */ uint32 unknown18; + /* 22 */ uint8 type; // 231 (0xE7) for spells + /* 23 */ uint32 unknown23; + /* 27 */ uint16 spell; // spell id being cast + /* 29 */ uint8 level2; // level of caster again? Or maybe the castee +// this field seems to be some sort of success flag, if it's 4 + /* 30 */ uint8 buff_unknown; // if this is 4, a buff icon is made + /* 31 */ +}; + +// Starting with 2/21/2006, OP_Actions seem to come in pairs, duplicating +// themselves, with the second one with slightly more information. Maybe this +// has to do with buff blocking?? +struct ActionAlt_Struct // ActionAlt_Struct - Size: 56 bytes +{ +/*0000*/ uint16 target; // Target ID +/*0002*/ uint16 source; // SourceID +/*0004*/ uint16 level; // level of caster +/*0006*/ uint16 instrument_mod; // seems to be fixed to 0x0A +/*0008*/ uint32 unknown08; +/*0012*/ uint16 unknown16; +/*0014*/ uint32 sequence; +/*0018*/ uint32 unknown18; +/*0022*/ uint8 type; // Casts, Falls, Bashes, etc... +/*0023*/ uint32 damage; // Amount of Damage +/*0027*/ uint16 spell; // SpellID +/*0029*/ uint8 unknown29; +/*0030*/ uint8 buff_unknown; // if this is 4, a buff icon is made +/*0031*/ uint32 unknown0031; // seen 00 00 00 00 +/*0035*/ uint8 unknown0035; // seen 00 +/*0036*/ uint32 unknown0036; // seen ff ff ff ff +/*0040*/ uint32 unknown0040; // seen ff ff ff ff +/*0044*/ uint32 unknown0044; // seen ff ff ff ff +/*0048*/ uint32 unknown0048; // seen 00 00 00 00 +/*0052*/ uint32 unknown0052; // seen 00 00 00 00 +/*0056*/ +}; + +// solar: this is what prints the You have been struck. and the regular +// melee messages like You try to pierce, etc. It's basically the melee +// and spell damage message +struct CombatDamage_Struct +{ +/* 00 */ uint16 target; +/* 02 */ uint16 source; +/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells +/* 05 */ uint16 spellid; +/* 07 */ int32 damage; +/* 11 */ float unknown11; // cd cc cc 3d +/* 15 */ float sequence; // see above notes in Action_Struct +/* 19 */ uint8 unknown19[9]; // was [9] +/* 28 */ +}; + +/* +** Consider Struct +** Length: 20 Bytes +*/ +struct Consider_Struct{ +/*000*/ uint32 playerid; // PlayerID +/*004*/ uint32 targetid; // TargetID +/*008*/ uint32 faction; // Faction +/*012*/ uint32 level; // Level +/*016*/ uint8 pvpcon; // Pvp con flag 0/1 +/*017*/ uint8 unknown017[3]; // +/*020*/ +}; + +/* +** Spawn Death Blow +** Length: 32 Bytes +** OpCode: 0114 +*/ +struct Death_Struct +{ +/*000*/ uint32 spawn_id; +/*004*/ uint32 killer_id; +/*008*/ uint32 corpseid; // was corpseid +/*012*/ uint32 attack_skill; // was type +/*016*/ uint32 spell_id; +/*020*/ uint32 bindzoneid; //bindzoneid? +/*024*/ uint32 damage; +/*028*/ uint32 unknown028; +}; + +struct BecomeCorpse_Struct { + uint32 spawn_id; + float y; + float x; + float z; +}; + +struct ZonePlayerToBind_Struct { +/*000*/ uint16 bind_zone_id; +/*002*/ uint16 bind_instance_id; +/*004*/ float x; +/*008*/ float y; +/*012*/ float z; +/*016*/ float heading; +/*020*/ char zone_name[1]; // Or "Bind Location" +/*000*/ uint8 unknown021; // Seen 1 - Maybe 0 would be to force a rezone and 1 is just respawn +/*000*/ uint32 unknown022; // Seen 32 or 59 +/*000*/ uint32 unknown023; // Seen 0 +/*000*/ uint32 unknown024; // Seen 21 or 43 +}; + +struct ZonePlayerToBindHeader_Struct +{ + /*000*/ uint16 bind_zone_id; + /*002*/ uint16 bind_instance_id; + /*004*/ float x; + /*008*/ float y; + /*012*/ float z; + /*016*/ float heading; + /*020*/ char zone_name[1]; // Or "Bind Location" +}; + +struct ZonePlayerToBindFooter_Struct +{ + /*000*/ uint8 unknown021; // Seen 1 - Maybe 0 would be to force a rezone and 1 is just respawn + /*000*/ uint32 unknown022; // Seen 32 or 59 + /*000*/ uint32 unknown023; // Seen 0 + /*000*/ uint32 unknown024; // Seen 21 or 43 +}; + +typedef struct { +/*000*/ uint32 bind_number; // Number of this bind in the iteration +/*004*/ uint32 bind_zone_id; // ID of the zone for this bind point or resurect point +/*008*/ float x; // X loc for this bind point +/*012*/ float y; // Y loc for this bind point +/*016*/ float z; // Z loc for this bind point +/*020*/ float heading; // Heading for this bind point +/*024*/ char bind_zone_name[1]; // Or "Bind Location" or "Resurrect" +/*000*/ uint8 validity; // 0 = valid choice, 1 = not a valid choice at this time (resurrection) +} RespawnOptions_Struct; + +struct RespawnWindow_Struct { +/*000*/ uint32 unknown000; // Seen 0 +/*004*/ uint32 time_remaining; // Total time before respawn in milliseconds +/*008*/ uint32 unknown008; // Seen 0 +/*012*/ uint32 total_binds; // Total Bind Point Options? - Seen 2 +/*016*/ RespawnOptions_Struct bind_points; +// First bind point is "Bind Location" and the last one is "Ressurect" +}; + + +/* +** Spawn position update - Size: 26 +** Struct sent from server->client to update position of +** another spawn's position update in zone (whether NPC or PC) +** +*/ + +struct PlayerPositionUpdateServer_Struct +{ +/*0000*/ uint16 spawn_id; // Entity ID of the Spawn/Player +/*0002*/ signed padding0000:12; // ***Placeholder + signed x_pos:19; // x coord + signed padding0290:1; // ***Placeholder +/*0006*/ signed delta_x:13; // change in x + signed delta_y:13; // change in y + signed padding0294:6; // ***Placeholder +/*0010*/ signed z_pos:19; // z coord + signed delta_heading:10; // change in heading + signed padding0298:3; // ***Placeholder +/*0014*/ signed y_pos:19; // y coord + signed delta_z:13; // change in z +/*0022*/ signed animation:10; // animation + unsigned heading:12; // heading + signed padding0302:10; // ***Placeholder +/*0026*/ +}; + +/* +** Player position update - Size: 40 +** Struct sent from client->server to update +** player position on server +** +*/ +struct PlayerPositionUpdateClient_Struct +{ +/*0000*/ uint16 spawn_id; // Player's Entity ID - Verified +/*0002*/ uint16 sequence; //increments one each packet - Verified +/*0004*/ uint8 unknown0004[4]; // ***Placeholder +/*0008*/ float delta_z; // Change in z +/*0012*/ float x_pos; // x coord - Verified +/*0016*/ signed delta_heading:10; // Change in heading + signed animation:10; // Animation + unsigned padding0028:12; // Seems to always be 0 +/*0020*/ float y_pos; // y coord - Verified +/*0024*/ float delta_x; // Change in x +/*0028*/ unsigned heading:12; // Directional heading - Verified + unsigned padding0032:20; // ***Placeholder - Some Static Number +/*0032*/ float delta_y; // Change in y +/*0036*/ float z_pos; // z coord - Verified +/*0040*/ +}; + +/* +** Spawn HP Update +** Length: 10 Bytes +** OpCode: OP_HPUpdate +*/ +struct SpawnHPUpdate_Struct +{ +/*00*/ uint32 cur_hp; // Id of spawn to update +/*04*/ int32 max_hp; // Maximum hp of spawn +/*08*/ int16 spawn_id; // Current hp of spawn +/*10*/ +}; + +/* +** SendExpZonein +** Length: 152 Bytes +** OpCode: OP_SendExpZonein +*/ +struct SendExpZonein_Struct +{ +/*0000*/ uint16 spawn_id; // ID of the spawn +/*0002*/ uint16 type; // Values associated with the type +/*0004*/ uint32 parameter; // Type of data sent +/*0008*/ uint32 exp; // Current experience ratio from 0 to 330 +/*0012*/ uint32 expAA; +/*0016*/ uint8 unknown0016[4]; +/*0020*/ char name[64]; +/*0084*/ char last_name[64]; +/*00148*/ uint32 unknown132; +/*00152*/ +}; + +/* +** SendExpZonein +** Length: 0 Bytes +** OpCode: OP_SendExpZonein +*/ +//struct SendExpZonein_Struct {}; + +struct SpawnHPUpdate_Struct2 +{ +/*01*/ int16 spawn_id; +/*00*/ uint8 hp; +}; +/* +** Stamina +** Length: 8 Bytes +** OpCode: 5721 +*/ +struct Stamina_Struct { +/*00*/ uint32 food; // (low more hungry 127-0) +/*02*/ uint32 water; // (low more thirsty 127-0) +}; + +/* +** Level Update +** Length: 12 Bytes +*/ +struct LevelUpdate_Struct +{ +/*00*/ uint32 level; // New level +/*04*/ uint32 level_old; // Old level +/*08*/ uint32 exp; // Current Experience +}; + +/* +** Experience Update +** Length: 14 Bytes +** OpCode: 9921 +*/ +struct ExpUpdate_Struct +{ +/*0000*/ uint32 exp; // Current experience ratio from 0 to 330 +/*0004*/ uint32 aaxp; // @BP ?? +}; + +/* +** Item Packet Struct - Works on a variety of opcodes +** Packet Types: See ItemPacketType enum +** +*/ +enum ItemPacketType +{ + ItemPacketViewLink = 0x00, + ItemPacketTradeView = 0x65, + ItemPacketLoot = 0x66, + ItemPacketTrade = 0x67, + ItemPacketCharInventory = 0x69, + ItemPacketSummonItem = 0x6A, + ItemPacketTributeItem = 0x6C, + ItemPacketMerchant = 0x64, + ItemPacketWorldContainer = 0x6B +}; +struct ItemPacket_Struct +{ +/*00*/ ItemPacketType PacketType; +/*04*/ char SerializedItem[1]; //was 1 +/*xx*/ +}; + +struct BulkItemPacket_Struct +{ +/*00*/ char SerializedItem[0]; +/*xx*/ +}; + +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]; +/*0016*/ +}; + +struct ItemNamePacket_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 unkown004; +/*008*/ char name[64]; +/*072*/ +}; + +// Length: 16 +struct ItemProperties_Struct_Old { + +/*000*/ uint8 unknown01[2]; +/*002*/ uint8 charges; +/*003*/ uint8 unknown02[13]; +/*016*/ +}; + +// Length: 8 +struct ItemProperties_Struct { + +/*000*/ uint8 unknown01[4]; +/*004*/ uint8 charges; +/*005*/ uint8 unknown02[3]; +/*008*/ +}; + +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; +/*0012*/ +}; + +// +// from_slot/to_slot +// -1 - destroy +// 0 - cursor +// 1 - inventory +// 2 - bank +// 3 - trade +// 4 - shared bank +// +// cointype +// 0 - copeer +// 1 - silver +// 2 - gold +// 3 - platinum +// +static const uint32 COINTYPE_PP = 3; +static const uint32 COINTYPE_GP = 2; +static const uint32 COINTYPE_SP = 1; +static const uint32 COINTYPE_CP = 0; + +struct MoveCoin_Struct +{ + int32 from_slot; + int32 to_slot; + int32 cointype1; + int32 cointype2; + int32 amount; +}; +struct TradeCoin_Struct{ + uint32 trader; + uint8 slot; + uint16 unknown5; + uint8 unknown7; + uint32 amount; +}; +struct TradeMoneyUpdate_Struct{ + uint32 trader; + uint32 type; + uint32 amount; +}; +/* +** Surname struct +** Size: 100 bytes +*/ +struct Surname_Struct +{ +/*0000*/ char name[64]; +/*0064*/ uint32 unknown0064; +/*0068*/ char lastname[32]; +/*0100*/ +}; + +struct GuildsListEntry_Struct { + char name[64]; +}; + +static const uint32 MAX_NUMBER_GUILDS = 1500; +struct GuildsList_Struct { + uint8 head[64]; // First on guild list seems to be empty... + GuildsListEntry_Struct Guilds[MAX_NUMBER_GUILDS]; +}; + +struct GuildUpdate_Struct { + uint32 guildID; + GuildsListEntry_Struct entry; +}; + +/* +** Money Loot +** Length: 22 Bytes +** OpCode: 5020 +*/ +struct moneyOnCorpseStruct { +/*0000*/ uint8 response; // 0 = someone else is, 1 = OK, 2 = not at this time +/*0001*/ uint8 unknown1; // = 0x5a +/*0002*/ uint8 unknown2; // = 0x40 +/*0003*/ uint8 unknown3; // = 0 +/*0004*/ uint32 platinum; // Platinum Pieces +/*0008*/ uint32 gold; // Gold Pieces + +/*0012*/ uint32 silver; // Silver Pieces +/*0016*/ uint32 copper; // Copper Pieces +}; + +//opcode = 0x5220 +// size 292 + + +struct LootingItem_Struct { +/*000*/ uint32 lootee; +/*002*/ uint32 looter; +/*004*/ uint16 slot_id; +/*006*/ uint8 unknown3[2]; +/*008*/ uint32 auto_loot; +}; + +struct GuildManageStatus_Struct{ + uint32 guildid; + uint32 oldrank; + uint32 newrank; + char name[64]; +}; +// Guild invite, remove +struct GuildJoin_Struct{ +/*000*/ uint32 guild_id; +/*004*/ uint32 unknown04; +/*008*/ uint32 level; +/*012*/ uint32 class_; +/*016*/ uint32 rank;//0 member, 1 officer, 2 leader +/*020*/ uint32 zoneid; +/*024*/ uint32 unknown24; +/*028*/ char name[64]; +/*092*/ +}; +struct GuildInviteAccept_Struct { + char inviter[64]; + char newmember[64]; + uint32 response; + uint32 guildeqid; +}; +struct GuildManageRemove_Struct { + uint32 guildeqid; + char member[64]; +}; +struct GuildCommand_Struct { + char othername[64]; + char myname[64]; + uint16 guildeqid; + uint8 unknown[2]; // for guildinvite all 0's, for remove 0=0x56, 2=0x02 + uint32 officer; +}; + +// 4244 bytes. Is not really an 'OnLevelMessage', it causes a popup box to display in the client +// Text looks like HTML. +struct OnLevelMessage_Struct { +/*0000*/ char Title[128]; +/*0128*/ char Text[4096]; +/*4224*/ uint32 Buttons; +/*4228*/ uint32 Duration; +/*4232*/ uint32 PopupID; +/*4236*/ uint32 unknown4236; +/*4240*/ uint32 unknown4240; +/*4244*/ +}; + +// Opcode OP_GMZoneRequest +// Size = 88 bytes +struct GMZoneRequest_Struct { +/*0000*/ char charname[64]; +/*0064*/ uint32 zone_id; +/*0068*/ float x; +/*0072*/ float y; +/*0076*/ float z; +/*0080*/ char unknown0080[4]; +/*0084*/ uint32 success; // 0 if command failed, 1 if succeeded? +/*0088*/ +// /*072*/ int8 success; // =0 client->server, =1 server->client, -X=specific error +// /*073*/ uint8 unknown0073[3]; // =0 ok, =ffffff error +}; + +struct GMSummon_Struct { +/* 0*/ char charname[64]; +/* 30*/ char gmname[64]; +/* 60*/ uint32 success; +/* 61*/ uint32 zoneID; +/*92*/ int32 y; +/*96*/ int32 x; +/*100*/ int32 z; +/*104*/ uint32 unknown2; // E0 E0 56 00 +}; + +struct GMGoto_Struct { // x,y is swapped as compared to summon and makes sense as own packet +/* 0*/ char charname[64]; + +/* 64*/ char gmname[64]; +/* 128*/ uint32 success; +/* 132*/ uint32 zoneID; + +/*136*/ int32 y; +/*140*/ int32 x; +/*144*/ int32 z; +/*148*/ uint32 unknown2; // E0 E0 56 00 +}; + +struct GMLastName_Struct { + char name[64]; + char gmname[64]; + char lastname[64]; + uint16 unknown[4]; // 0x00, 0x00 + // 0x01, 0x00 = Update the clients +}; + +//Combat Abilities +struct CombatAbility_Struct { + uint32 m_target; //the ID of the target mob + uint32 m_atk; + uint32 m_skill; +}; + +//Instill Doubt +struct Instill_Doubt_Struct { + uint8 i_id; + uint8 ia_unknown; + uint8 ib_unknown; + uint8 ic_unknown; + uint8 i_atk; + + uint8 id_unknown; + uint8 ie_unknown; + uint8 if_unknown; + uint8 i_type; + uint8 ig_unknown; + uint8 ih_unknown; + uint8 ii_unknown; +}; + +struct GiveItem_Struct { + uint16 to_entity; + int16 to_equipSlot; + uint16 from_entity; + int16 from_equipSlot; +}; + +struct RandomReq_Struct { + uint32 low; + uint32 high; +}; + +/* solar: 9/23/03 reply to /random command; struct from Zaphod */ +struct RandomReply_Struct { +/* 00 */ uint32 low; +/* 04 */ uint32 high; +/* 08 */ uint32 result; +/* 12 */ char name[64]; +/* 76 */ +}; + +/* +** LFG_Appearance_Struct +** Packet sent to clients to notify when someone in zone toggles LFG flag +** Size: 8 bytes +** Used in: OP_LFGAppearance +** +*/ +struct LFG_Appearance_Struct +{ +/*0000*/ uint32 spawn_id; // ID of the client +/*0004*/ uint8 lfg; // 1=LFG, 0=Not LFG +/*0005*/ char unknown0005[3]; // +/*0008*/ +}; + + +// EverQuest Time Information: +// 72 minutes per EQ Day +// 3 minutes per EQ Hour +// 6 seconds per EQ Tick (2 minutes EQ Time) +// 3 seconds per EQ Minute + +struct TimeOfDay_Struct { + uint8 hour; + uint8 minute; + uint8 day; + uint8 month; + uint16 year; +/*0006*/ uint16 unknown0016; // Placeholder +/*0008*/ +}; + +// Darvik: shopkeeper structs +struct Merchant_Click_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; +/*008*/ uint32 command; //1=open, 0=cancel/close +/*012*/ float rate; //cost multiplier, dosent work anymore +}; +/* +Unknowns: +0 is e7 from 01 to // MAYBE SLOT IN PURCHASE +1 is 03 +2 is 00 +3 is 00 +4 is ?? +5 is ?? +6 is 00 from a0 to +7 is 00 from 3f to */ +/* +0 is F6 to 01 +1 is CE CE +4A 4A +00 00 +00 E0 +00 CB +00 90 +00 3F +*/ + + + +struct Merchant_Sell_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; // Player's entity id +/*008*/ uint32 itemslot; + uint32 unknown12; +/*016*/ uint8 quantity; // Already sold +/*017*/ uint8 Unknown016[3]; +/*020*/ uint32 price; +}; +struct Merchant_Purchase_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 itemslot; // Player's entity id +/*008*/ uint32 quantity; +/*012*/ uint32 price; +}; +struct Merchant_DelItem_Struct{ +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; // Player's entity id +/*008*/ uint32 itemslot; +}; +struct Adventure_Purchase_Struct { +/*000*/ uint32 some_flag; //set to 1 generally... +/*004*/ uint32 npcid; +/*008*/ uint32 itemid; +/*012*/ uint32 variable; +}; + +struct Adventure_Sell_Struct { +/*000*/ uint32 unknown000; //0x01 - Stack Size/Charges? +/*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]; +/*024*/ uint32 ldon_guk_points; // Earned Deepest Guk points +/*028*/ uint32 ldon_mirugal_points; // Earned Mirugal' Mebagerie points +/*032*/ uint32 ldon_mistmoore_points; // Earned Mismoore Catacombs Points +/*036*/ uint32 ldon_rujarkian_points; // Earned Rujarkian Hills points +/*040*/ uint32 ldon_takish_points; // Earned Takish points +/*044*/ uint8 unknown_apu042[216]; +}; + + +struct AdventureFinish_Struct{ + uint32 win_lose;//Cofruben: 00 is a lose,01 is win. + uint32 points; +}; +//OP_AdventureRequest +struct AdventureRequest_Struct{ + uint32 risk;//1 normal,2 hard. + uint32 entity_id; +}; +struct AdventureRequestResponse_Struct{ + uint32 unknown000; + char text[2048]; + uint32 timetoenter; + uint32 timeleft; + uint32 risk; + float x; + float y; + float z; + uint32 showcompass; + uint32 unknown2080; +}; + +//this is mostly right but something is off that causes the client to crash sometimes +//I don't really care enough about the feature to work on it anymore though. +struct AdventureLeaderboardEntry_Struct +{ +/*004*/ char name[64]; +/*008*/ uint32 success; +/*012*/ uint32 failure; +/*016*/ +}; + +struct AdventureLeaderboard_Struct +{ +/*000*/ uint32 unknown000; +/*004*/ uint32 unknown004; +/*008*/ uint32 success; +/*012*/ uint32 failure; +/*016*/ uint32 our_rank; +/*020*/ +}; + +/*struct Item_Shop_Struct { + uint16 merchantid; + uint8 itemtype; + Item_Struct item; + uint8 iss_unknown001[6]; +};*/ + +struct Illusion_Struct { //size: 256 +/*000*/ uint32 spawnid; +/*004*/ char charname[64]; // +/*068*/ uint16 race; // +/*070*/ char unknown006[2]; // Weird green name +/*072*/ uint8 gender; +/*073*/ uint8 texture; +/*074*/ uint8 unknown074; // +/*075*/ uint8 unknown075; // +/*076*/ uint8 helmtexture; // +/*077*/ uint8 unknown077; // +/*078*/ uint8 unknown078; // +/*079*/ uint8 unknown079; // +/*080*/ uint32 face; // +/*084*/ uint8 hairstyle; // Some Races don't change Hair Style Properly in SoF +/*085*/ uint8 haircolor; // +/*086*/ uint8 beard; // +/*087*/ uint8 beardcolor; // +/*088*/ float size; // +/*092*/ uint8 unknown092[148]; +/*240*/ uint32 unknown240; // Removes armor? +/*244*/ uint32 drakkin_heritage; // +/*248*/ uint32 drakkin_tattoo; // +/*252*/ uint32 drakkin_details; // +/*256*/ +}; + +struct ZonePoint_Entry { //24 octets +/*0000*/ uint32 iterator; +/*0004*/ float y; +/*0008*/ float x; +/*0012*/ float z; +/*0016*/ float heading; +/*0020*/ uint16 zoneid; +/*0022*/ uint16 zoneinstance; // LDoN instance +}; + +struct ZonePoints { +/*0000*/ uint32 count; +/*0004*/ struct ZonePoint_Entry zpe[0]; // Always add one extra to the end after all zonepoints +//*0xxx*/ uint8 unknown0xxx[24]; //New from SEQ +}; + +struct SkillUpdate_Struct { +/*00*/ uint32 skillId; +/*04*/ uint32 value; +/*08*/ +}; + +struct ZoneUnavail_Struct { + //This actually varies, but... + char zonename[16]; + int16 unknown[4]; +}; + +struct GroupInvite_Struct { +/*0000*/ char invitee_name[64]; +/*0064*/ char inviter_name[64]; +/*0128*/ +}; + +struct GroupGeneric_Struct { +/*0000*/ char name1[64]; +/*0064*/ char name2[64]; +/*0128*/ +}; + +struct GroupCancel_Struct { +/*0000*/ char name1[64]; +/*0064*/ char name2[64]; +/*0128*/ uint8 toggle; +/*0129*/ +}; + +struct GroupUpdate_Struct { // From Titanium Structs +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +/*0452*/ +}; + +struct GroupUpdate2_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +/*0452*/ GroupLeadershipAA_Struct leader_aas; +/*0516*/ uint8 unknown[252]; // Titanium uses [188] here +/*0768*/ +}; + +struct GroupJoin_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[64]; +/*0132*/ uint8 unknown[84]; +/*0216*/ +}; + +struct GroupFollow_Struct { // SoF Follow Struct +/*0000*/ char name1[64]; // inviter +/*0064*/ char name2[64]; // invitee +/*0128*/ uint32 unknown0128; +/*0132*/ +}; + +struct LFG_Struct { +/*000*/ uint32 unknown000; +/*004*/ uint32 value; // 0x00 = off 0x01 = on +/*008*/ uint32 unknown008; +/*012*/ uint32 unknown012; +/*016*/ char name[64]; +}; + +struct FaceChange_Struct { +/*000*/ uint8 haircolor; +/*001*/ uint8 beardcolor; +/*002*/ uint8 eyecolor1; +/*003*/ uint8 eyecolor2; +/*004*/ uint8 hairstyle; +/*005*/ uint8 beard; +/*006*/ uint8 face; +/*007*/ uint8 unknown007; +/*008*/ uint32 drakkin_heritage; +/*012*/ uint32 drakkin_tattoo; +/*016*/ uint32 drakkin_details; +/*020*/ uint32 unknown020; +/*024*/ +}; +//there are only 10 faces for barbs changing woad just +//increase the face value by ten so if there were 8 woad +//designs then there would be 80 barb faces + +/* +** Trade request from one client to another +** Used to initiate a trade +** Size: 8 bytes +** Used in: OP_TradeRequest +*/ +struct TradeRequest_Struct { +/*00*/ uint32 to_mob_id; +/*04*/ uint32 from_mob_id; +/*08*/ +}; + +struct TradeAccept_Struct { +/*00*/ uint32 from_mob_id; +/*04*/ uint32 unknown4; //seems to be garbage +/*08*/ +}; + +/* +** Cancel Trade struct +** Sent when a player cancels a trade +** Size: 8 bytes +** Used In: OP_CancelTrade +** +*/ +struct CancelTrade_Struct { +/*00*/ uint32 fromid; +/*04*/ uint32 action; +/*08*/ +}; + +struct PetitionUpdate_Struct { + uint32 petnumber; // Petition Number + uint32 color; // 0x00 = green, 0x01 = yellow, 0x02 = red + uint32 status; + time_t senttime; // 4 has to be 0x1F + char accountid[32]; + char gmsenttoo[64]; + int32 quetotal; + char charname[64]; +}; + +struct Petition_Struct { + uint32 petnumber; + uint32 urgency; + char accountid[32]; + char lastgm[32]; + uint32 zone; + //char zone[32]; + char charname[64]; + uint32 charlevel; + uint32 charclass; + uint32 charrace; + uint32 unknown; + //time_t senttime; // Time? + uint32 checkouts; + uint32 unavail; + //uint8 unknown5[4]; + time_t senttime; + uint32 unknown2; + char petitiontext[1024]; + char gmtext[1024]; +}; + + +struct Who_All_Struct { // 76 length total +/*000*/ char whom[64]; +/*064*/ uint32 wrace; // FF FF = no race + +/*068*/ uint32 wclass; // FF FF = no class +/*072*/ uint32 lvllow; // FF FF = no numbers +/*076*/ uint32 lvlhigh; // FF FF = no numbers +/*080*/ uint32 gmlookup; // FF FF = not doing /who all gm +/*084*/ uint32 guildid; // Also used for Buyer/Trader/LFG +/*088*/ uint8 unknown088[64]; +/*156*/ uint32 type; // 0 = /who 3 = /who all +}; + +struct Stun_Struct { // 8 bytes total +/*000*/ uint32 duration; // Duration of stun +/*004*/ uint8 unknown004; // seen 0 +/*005*/ uint8 unknown005; // seen 163 +/*006*/ uint8 unknown006; // seen 67 +/*007*/ uint8 unknown007; // seen 0 +/*008*/ +}; + +struct AugmentItem_Struct { +/*00*/ int16 container_slot; +/*02*/ char unknown02[2]; +/*04*/ int32 augment_slot; +/*08*/ +}; + +// OP_Emote +struct Emote_Struct { +/*0000*/ uint32 unknown01; +/*0004*/ char message[1024]; // was 1024 +/*1028*/ +}; + +// Inspect +struct Inspect_Struct { + uint32 TargetID; + uint32 PlayerID; +}; + +//OP_InspectAnswer - Size: 1860 +struct InspectResponse_Struct{ +/*000*/ uint32 TargetID; +/*004*/ uint32 playerid; +/*008*/ char itemnames[23][64]; +/*1480*/uint32 itemicons[23]; +/*1572*/char text[288]; // Max number of chars in Inspect Window appears to be 254 +/*1860*/ +}; + +//OP_SetDataRate +struct SetDataRate_Struct { + float newdatarate; +}; + +//OP_SetServerFilter +struct SetServerFilter_Struct { + uint32 filters[32]; //see enum eqFilterType [31] +}; + +//Op_SetServerFilterAck +struct SetServerFilterAck_Struct { + uint8 blank[8]; +}; +struct IncreaseStat_Struct{ + /*0000*/ uint8 unknown0; + /*0001*/ uint8 str; + /*0002*/ uint8 sta; + /*0003*/ uint8 agi; + /*0004*/ uint8 dex; + /*0005*/ uint8 int_; + /*0006*/ uint8 wis; + /*0007*/ uint8 cha; + /*0008*/ uint8 fire; + /*0009*/ uint8 cold; + /*0010*/ uint8 magic; + /*0011*/ uint8 poison; + /*0012*/ uint8 disease; + /*0013*/ char unknown13[116]; + /*0129*/ uint8 str2; + /*0130*/ uint8 sta2; + /*0131*/ uint8 agi2; + /*0132*/ uint8 dex2; + /*0133*/ uint8 int_2; + /*0134*/ uint8 wis2; + /*0135*/ uint8 cha2; + /*0136*/ uint8 fire2; + /*0137*/ uint8 cold2; + /*0138*/ uint8 magic2; + /*0139*/ uint8 poison2; + /*0140*/ uint8 disease2; +}; + +struct GMName_Struct { + char oldname[64]; + char gmname[64]; + char newname[64]; + uint8 badname; + uint8 unknown[3]; +}; + +struct GMDelCorpse_Struct { + char corpsename[64]; + char gmname[64]; + uint8 unknown; +}; + +struct GMKick_Struct { + char name[64]; + char gmname[64]; + uint8 unknown; +}; + + +struct GMKill_Struct { + char name[64]; + char gmname[64]; + uint8 unknown; +}; + + +struct GMEmoteZone_Struct { + char text[512]; +}; + +// The BookText_Struct is not used in SoF and later clients. +// The BookRequest_Struct is used instead for both request and reply. +// +struct BookText_Struct { + uint8 window; // where to display the text (0xFF means new window) + uint8 type; //type: 0=scroll, 1=book, 2=item info.. prolly + char booktext[1]; // Variable Length - was 1 +}; +// This is the request to read a book. +// This is just a "text file" on the server +// or in our case, the 'name' column in our books table. +struct BookRequest_Struct { +/*0000*/ uint32 window; // where to display the text (0xFFFFFFFF means new window). +/*0004*/ uint32 invslot; // The inventory slot the book is in. Not used, but echoed in the response packet. +/*0008*/ uint32 type; // 0 = Scroll, 1 = Book, 2 = Item Info. Possibly others +/*0012*/ uint32 unknown0012; +/*0016*/ uint16 unknown0016; +/*0018*/ char txtfile[8194]; +}; + +/* +** Object/Ground Spawn struct +** Used for Forges, Ovens, ground spawns, items dropped to ground, etc +** Size: 104 bytes +** OpCodes: OP_CreateObject +** Last Updated: Oct-17-2003 +** +*/ +struct Object_Struct { +/*00*/ uint32 linked_list_addr[2];// They are, get this, prev and next, ala linked list +/*08*/ uint32 unknown008; // Something related to the linked list? +/*12*/ uint32 drop_id; // Unique object id for zone +/*16*/ uint16 zone_id; // Redudant, but: Zone the object appears in +/*18*/ uint16 zone_instance; // +/*20*/ uint32 unknown020; // 00 00 00 00 +/*24*/ uint32 unknown024; // 53 9e f9 7e - same for all objects in the zone? +/*40*/ float heading; // heading +/*32*/ uint8 unknown032[8]; // 00 00 00 00 00 00 00 00 +/*28*/ float size; // Size - default 1 +/*44*/ float z; // z coord +/*48*/ float x; // x coord +/*52*/ float y; // y coord +/*56*/ char object_name[32]; // Name of object, usually something like IT63_ACTORDEF was [20] +/*88*/ uint32 unknown088; // unique ID? Maybe for a table that includes the contents? +/*92*/ uint32 object_type; // Type of object, not directly translated to OP_OpenObject +/*96*/ uint8 unknown096[4]; // ff ff ff ff +/*100*/ uint32 spawn_id; // Spawn Id of client interacting with object +/*104*/ +}; +// 01 = generic drop, 02 = armor, 19 = weapon +//[13:40] and 0xff seems to be indicative of the tradeskill/openable items that end up returning the old style item type in the OP_OpenObject + +/* +** Click Object Struct +** Client clicking on zone object (forge, groundspawn, etc) +** Size: 8 bytes +** Last Updated: Oct-17-2003 +** +*/ +struct ClickObject_Struct { +/*00*/ uint32 drop_id; +/*04*/ uint32 player_id; +/*08*/ +}; + +struct Shielding_Struct { + uint32 target_id; +}; + +/* +** Click Object Acknowledgement Struct +** Response to client clicking on a World Container (ie, forge) +** +*/ +struct ClickObjectAction_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 type; // See object.h, "Object Types" +/*16*/ uint32 unknown16; // +/*20*/ uint32 icon; // Icon to display for tradeskill containers +/*24*/ uint32 unknown24; // +/*28*/ char object_name[64]; // Object name to display +/*92*/ +}; + +/* +** This is different now, mostly unknown +** +*/ +struct CloseContainer_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 unknown12[12]; +}; + +/* +** Generic Door Struct +** Length: 52 Octets +** Used in: +** cDoorSpawnsStruct(f721) +** +*/ +struct Door_Struct +{ +/*0000*/ char name[32]; // Filename of Door // Was 10char long before... added the 6 in the next unknown to it: Daeken M. BlackBlade +/*0032*/ float yPos; // y loc +/*0036*/ float xPos; // x loc +/*0040*/ float zPos; // z loc +/*0044*/ float heading; +/*0048*/ uint32 incline; // rotates the whole door +/*0052*/ uint32 size; // 100 is normal, smaller number = smaller model +/*0054*/ uint8 unknown0054[4]; // 00 00 00 00 +/*0060*/ uint8 doorId; // door's id # +/*0061*/ uint8 opentype; +/*0062*/ uint8 state_at_spawn; +/*0063*/ uint8 invert_state; // if this is 1, the door is normally open +/*0064*/ uint32 door_param; // normally ff ff ff ff (-1) +/*0068*/ uint32 unknown0068; // 00 00 00 00 +/*0072*/ uint32 unknown0072; // 00 00 00 00 +/*0076*/ uint8 unknown0076; // seen 1 or 0 +/*0077*/ uint8 unknown0077; // seen 1 (always?) +/*0078*/ uint8 unknown0078; // seen 0 (always?) +/*0079*/ uint8 unknown0079; // seen 1 (always?) +/*0080*/ uint8 unknown0080; // seen 0 (always?) +/*0081*/ uint8 unknown0081; // seen 1 or 0 or rarely 2C or 90 or ED or 2D or A1 +/*0082*/ uint8 unknown0082; // seen 0 or rarely FF or FE or 10 or 5A or 82 +/*0083*/ uint8 unknown0083; // seen 0 or rarely 02 or 7C +/*0084*/ uint8 unknown0084[8]; // mostly 0s, the last 3 bytes are something tho +/*0092*/ +}; + +struct DoorSpawns_Struct { + struct Door_Struct doors[0]; +}; + +/* + OP Code: Op_ClickDoor + Size: 16 +*/ +struct ClickDoor_Struct { +/*000*/ uint8 doorid; +/*001*/ uint8 unknown001; // This may be some type of action setting +/*002*/ uint8 unknown002; // This is sometimes set after a lever is closed +/*003*/ uint8 unknown003; // Seen 0 +/*004*/ uint8 picklockskill; +/*005*/ uint8 unknown005[3]; +/*008*/ uint32 item_id; +/*012*/ uint16 player_id; +/*014*/ uint8 unknown014[2]; +/*016*/ +}; + +struct MoveDoor_Struct { + uint8 doorid; + uint8 action; +}; + + +struct BecomeNPC_Struct { + uint32 id; + int32 maxlevel; +}; + +struct Underworld_Struct { + float speed; + float y; + float x; + float z; +}; + +struct Resurrect_Struct { + uint32 unknown00; + uint16 zone_id; + uint16 instance_id; + float y; + float x; + float z; + char your_name[64]; + uint32 unknown88; + char rezzer_name[64]; + uint32 spellid; + char corpse_name[64]; + uint32 action; +/* 228 */ +}; + +struct SetRunMode_Struct { + uint8 mode; //01=run 00=walk + uint8 unknown[3]; +}; + +//EnvDamage is EnvDamage2 without a few bytes at the end. + +struct EnvDamage2_Struct { +/*0000*/ uint32 id; +/*0004*/ uint16 unknown4; +/*0006*/ uint32 damage; +/*0010*/ uint8 unknown10[12]; +/*0022*/ uint8 dmgtype; //FA = Lava; FC = Falling +/*0023*/ uint8 unknown2[4]; +/*0027*/ uint16 constant; //Always FFFF +/*0029*/ uint16 unknown29; +}; + +//Bazaar Stuff =D + +struct BazaarWindowStart_Struct { + uint8 Action; + uint8 Unknown001; + uint16 Unknown002; +}; + +struct BazaarWelcome_Struct { + BazaarWindowStart_Struct beginning; + uint32 traders; + uint32 items; + uint8 unknown1[8]; +}; + +struct BazaarSearch_Struct { + BazaarWindowStart_Struct beginning; + uint32 traderid; + uint32 class_; + uint32 race; + uint32 stat; + uint32 slot; + uint32 type; + char name[64]; + uint32 minprice; + uint32 maxprice; + uint32 minlevel; + uint32 maxlevel; +}; + +struct BazaarInspect_Struct{ + uint32 item_id; + uint32 unknown; + char name[64]; +}; +struct BazaarReturnDone_Struct{ + uint32 type; + uint32 traderid; + uint32 unknown8; + uint32 unknown12; + uint32 unknown16; +}; +struct BazaarSearchResults_Struct { + BazaarWindowStart_Struct Beginning; + uint32 SellerID; + uint32 NumItems; // Don't know. Don't know the significance of this field. + uint32 SerialNumber; + uint32 Unknown016; + uint32 Unknown020; // Something to do with stats as well + char ItemName[64]; + uint32 Cost; + uint32 ItemStat; +}; + +struct ServerSideFilters_Struct { +uint8 clientattackfilters; // 0) No, 1) All (players) but self, 2) All (players) but group +uint8 npcattackfilters; // 0) No, 1) Ignore NPC misses (all), 2) Ignore NPC Misses + Attacks (all but self), 3) Ignores NPC Misses + Attacks (all but group) +uint8 clientcastfilters; // 0) No, 1) Ignore PC Casts (all), 2) Ignore PC Casts (not directed towards self) +uint8 npccastfilters; // 0) No, 1) Ignore NPC Casts (all), 2) Ignore NPC Casts (not directed towards self) +}; + +/* +** Client requesting item statistics +** Size: 48 bytes +** Used In: OP_ItemLinkClick +** Last Updated: 2/15/2009 +** +*/ +struct ItemViewRequest_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 augments[5]; +/*024*/ uint32 link_hash; +/*028*/ uint32 unknown028; //seems to always be 4 on SoF client +/*032*/ char unknown032[12]; //probably includes loregroup & evolving info. see Client::MakeItemLink() in zone/inventory.cpp:469 +/*044*/ uint16 icon; +/*046*/ char unknown046[2]; +}; + +/* + * Client to server packet + */ +struct PickPocket_Struct { +// Size 18 + uint32 to; + uint32 from; + uint16 myskill; + uint8 type; // -1 you are being picked, 0 failed , 1 = plat, 2 = gold, 3 = silver, 4 = copper, 5 = item + uint8 unknown1; // 0 for response, unknown for input + uint32 coin; + uint8 lastsix[2]; +}; +/* + * Server to client packet + */ + +struct sPickPocket_Struct { + // Size 28 = coin/fail + uint32 to; + uint32 from; + uint32 myskill; + uint32 type; + uint32 coin; + char itemname[64]; +}; + + +struct LogServer_Struct { +// Op_Code OP_LOGSERVER +/*000*/ uint32 unknown000; +/*004*/ uint8 enable_pvp; +/*005*/ uint8 unknown005; +/*006*/ uint8 unknown006; +/*007*/ uint8 unknown007; +/*008*/ uint8 enable_FV; +/*009*/ uint8 unknown009; +/*010*/ uint8 unknown010; +/*011*/ uint8 unknown011; +/*012*/ uint32 unknown012; // htonl(1) on live +/*016*/ uint32 unknown016; // htonl(1) on live +/*020*/ uint8 unknown020[12]; +/*032*/ char worldshortname[32]; +/*064*/ uint8 unknown064[32]; +/*096*/ char unknown096[16]; // 'pacman' on live +/*112*/ char unknown112[16]; // '64.37,148,36' on live +/*126*/ uint8 unknown128[48]; +/*176*/ uint32 unknown176; // htonl(0x00002695) +/*180*/ char unknown180[80]; // 'eqdataexceptions@mail.station.sony.com' on live +/*260*/ uint8 unknown260; // 0x01 on live +/*261*/ uint8 enablevoicemacros; +/*262*/ uint8 enablemail; +/*263*/ uint8 unknown263[16]; +/*279*/ +}; + +struct ApproveWorld_Struct { +// Size 544 +// Op_Code OP_ApproveWorld + uint8 unknown544[544]; +}; + +struct ClientError_Struct +{ +/*00001*/ char type; +/*00001*/ char unknown0001[69]; +/*00069*/ char character_name[64]; +/*00134*/ char unknown134[192]; +/*00133*/ char message[31994]; +/*32136*/ +}; + +struct MobHealth +{ + /*0000*/ uint8 hp; //health percent + /*0001*/ uint16 id;//mobs id +}; + +struct Track_Struct { + uint16 entityid; + uint16 padding002; + float distance; +}; + +struct Tracking_Struct { + Track_Struct Entrys[0]; +}; + +// Looks like new tracking structures - Opcode: 0x57a7 +struct Tracking_Struct_New { + uint16 totalcount; // Total Count of mobs within tracking range + Track_Struct Entrys[0]; +}; + +struct Track_Struct_New { + uint16 entityid; // Entity ID + uint16 unknown002; // 00 00 + uint32 unknown004; // + uint8 level; // level of mob + uint8 unknown009; // 01 maybe type of mob? player/npc? + char name[1]; // name of mob +}; + + +/* +** ZoneServerInfo_Struct +** Zone server information +** Size: 130 bytes +** Used In: OP_ZoneServerInfo +** +*/ +struct ZoneServerInfo_Struct +{ +/*0000*/ char ip[128]; +/*0128*/ uint16 port; +}; + +struct WhoAllPlayer{ + uint32 formatstring; + uint32 pidstring; + char* name; + uint32 rankstring; + char* guild; + uint32 unknown80[2]; + uint32 zonestring; + uint32 zone; + uint32 class_; + uint32 level; + uint32 race; + char* account; + uint32 unknown100; +}; + +struct WhoAllReturnStruct { + uint32 id; + uint32 playerineqstring; + char line[27]; + uint8 unknown35; //0A + uint32 unknown36;//0s + uint32 playersinzonestring; + uint32 unknown44[2]; //0s + uint32 unknown52;//1 + uint32 unknown56;//1 + uint32 playercount;//1 + struct WhoAllPlayer player[0]; +}; + +struct Trader_Struct { + uint32 code; + uint32 itemid[160]; + uint32 unknown; + uint32 itemcost[80]; +}; + +struct ClickTrader_Struct { + uint32 code; + uint32 unknown[161];//damn soe this is totally pointless :/ but at least your finally using memset! Good job :) -LE + uint32 itemcost[80]; +}; + +struct GetItems_Struct{ + uint32 items[80]; +}; + +struct BecomeTrader_Struct{ + uint32 ID; + uint32 Code; +}; + +struct Trader_ShowItems_Struct{ + uint32 code; + uint32 traderid; + uint32 unknown08[3]; +}; + +struct TraderBuy_Struct { +/*000*/ uint32 Action; +/*004*/ uint32 Price; +/*008*/ uint32 TraderID; +/*012*/ char ItemName[64]; +/*076*/ uint32 Unknown076; +/*080*/ uint32 ItemID; +/*084*/ uint32 AlreadySold; +/*088*/ uint32 Quantity; +/*092*/ uint32 Unknown092; +}; + +struct TraderItemUpdate_Struct{ + uint32 unknown0; + uint32 traderid; + uint8 fromslot; + uint8 toslot; //7? + uint16 charges; +}; + +struct MoneyUpdate_Struct{ + int32 platinum; + int32 gold; + int32 silver; + int32 copper; +}; + +//struct MoneyUpdate_Struct +//{ +//*0000*/ uint32 spawn_id; // ***Placeholder +//*0004*/ uint32 cointype; // Coin Type +//*0008*/ uint32 amount; // Amount +//*0012*/ +//}; + + +struct TraderDelItem_Struct{ + uint32 slotid; + uint32 quantity; + uint32 unknown; +}; + +struct TraderClick_Struct{ + uint32 traderid; + uint32 unknown4[2]; + uint32 approval; +}; + +struct FormattedMessage_Struct{ + uint32 unknown0; + uint32 string_id; + uint32 type; + char message[0]; +//*0???*/ uint8 unknown0[8]; // ***Placeholder +}; +struct SimpleMessage_Struct{ + uint32 string_id; + uint32 color; + uint32 unknown8; +}; + +struct GuildMemberEntry_Struct { + char name[1]; //variable length + uint32 level; //network byte order + uint32 banker; //1=yes, 0=no, network byte order + uint32 class_; //network byte order + uint32 rank; //network byte order + uint32 time_last_on; //network byte order + uint32 tribute_enable; //network byte order + uint32 total_tribute; //total guild tribute donated, network byte order + uint32 last_tribute; //unix timestamp + uint32 unknown_one; //unknown, set to one. (network byte order) + char public_note[1]; //variable length. + uint16 zoneinstance; //network byte order + uint16 zone_id; //network byte order +/* 42 + strings */ +}; + +struct GuildMembers_Struct { //just for display purposes, this is not actually used in the message encoding. + char player_name[1]; //variable length. + uint32 count; //network byte order + GuildMemberEntry_Struct member[0]; +}; + +struct GuildMOTD_Struct{ +/*0000*/ uint32 unknown0; +/*0004*/ char name[64]; +/*0068*/ char setby_name[64]; +/*0132*/ uint32 unknown132; +/*0136*/ char motd[0]; //was 512 +}; + +struct GuildURL_Struct{ +/*0000*/ uint32 unknown0; //index? seen server send 0 w/ the Guild URL, followed by 1 with nothing. +/*0004*/ uint32 unknown4; +/*0008*/ uint32 unknown8; //seen 7 +/*0068*/ char setby_name[64]; +/*0132*/ uint32 unknown132; //seen 0x167 +/*0136*/ char url[4080]; +}; + +struct GuildMemberUpdate_Struct { +/*00*/ uint32 guild_id; +/*04*/ char member_name[64]; +/*68*/ uint16 zone_id; +/*70*/ uint16 instance_id; +/*72*/ uint32 some_timestamp; +}; + +struct GuildMemberLevelUpdate_Struct { +/*00*/ uint32 guild_id; +/*04*/ char member_name[64]; +/*68*/ uint32 level; //not sure +}; + + + +struct GuildUpdate_PublicNote{ + uint32 unknown0; + char name[64]; + char target[64]; + char note[100]; //we are cutting this off at 100, actually around 252 +}; +struct GuildDemoteStruct{ + char name[64]; + char target[64]; +}; +struct GuildRemoveStruct{ + char target[64]; + char name[64]; + uint32 unknown128; + uint32 leaderstatus; //? +}; +struct GuildMakeLeader{ + char name[64]; + char target[64]; +}; + + + +struct BugStruct{ +/*0000*/ char chartype[64]; +/*0064*/ char name[96]; +/*0160*/ char ui[128]; +/*0288*/ float x; +/*0292*/ float y; +/*0296*/ float z; +/*0300*/ float heading; +/*0304*/ uint32 unknown304; +/*0308*/ uint32 type; +/*0312*/ char unknown312[2144]; +/*2456*/ char bug[1024]; +/*3480*/ char placeholder[2]; +/*3482*/ char system_info[4098]; +}; +struct Make_Pet_Struct { //Simple struct for getting pet info + uint8 level; + uint8 class_; + uint16 race; + uint8 texture; + uint8 pettype; + float size; + uint8 type; + uint32 min_dmg; + uint32 max_dmg; +}; +struct Ground_Spawn{ + float max_x; + float max_y; + float min_x; + float min_y; + float max_z; + float heading; + char name[16]; + uint32 item; + uint32 max_allowed; + uint32 respawntimer; +}; +struct Ground_Spawns { + struct Ground_Spawn spawn[50]; //Assigned max number to allow +}; +struct PetitionBug_Struct{ + uint32 petition_number; + uint32 unknown4; + char accountname[64]; + uint32 zoneid; + char name[64]; + uint32 level; + uint32 class_; + uint32 race; + uint32 unknown152[3]; + uint32 time; + uint32 unknown168; + char text[1028]; +}; + +struct DyeStruct +{ + union + { + struct + { + struct Color_Struct head; + struct Color_Struct chest; + struct Color_Struct arms; + struct Color_Struct wrists; + struct Color_Struct hands; + struct Color_Struct legs; + struct Color_Struct feet; + struct Color_Struct primary; // you can't actually dye this + struct Color_Struct secondary; // or this + } + dyes; + struct Color_Struct dye[9]; + }; +}; + +struct ApproveZone_Struct { + char name[64]; + uint32 zoneid; + uint32 approve; +}; +struct ZoneInSendName_Struct { + uint32 unknown0; + char name[64]; + char name2[64]; + uint32 unknown132; +}; +struct ZoneInSendName_Struct2 { + uint32 unknown0; + char name[64]; + uint32 unknown68[145]; +}; + +static const uint32 MAX_TRIBUTE_TIERS = 10; + +struct StartTribute_Struct { + uint32 client_id; + uint32 tribute_master_id; + uint32 response; +}; + +struct TributeLevel_Struct { + uint32 level; //backwards byte order! + uint32 tribute_item_id; //backwards byte order! + uint32 cost; //backwards byte order! +}; + +struct TributeAbility_Struct { + uint32 tribute_id; //backwards byte order! + uint32 tier_count; //backwards byte order! + TributeLevel_Struct tiers[MAX_TRIBUTE_TIERS]; + char name[0]; +}; + +struct GuildTributeAbility_Struct { + uint32 guild_id; + TributeAbility_Struct ability; +}; + +struct SelectTributeReq_Struct { + uint32 client_id; //? maybe action ID? + uint32 tribute_id; + uint32 unknown8; //seen E3 00 00 00 +}; + +struct SelectTributeReply_Struct { + uint32 client_id; //echoed from request. + uint32 tribute_id; + char desc[0]; +}; + +struct TributeInfo_Struct { + uint32 active; //0 == inactive, 1 == active + uint32 tributes[MAX_PLAYER_TRIBUTES]; //-1 == NONE + uint32 tiers[MAX_PLAYER_TRIBUTES]; //all 00's + uint32 tribute_master_id; +}; + +struct TributeItem_Struct { + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; +}; + +struct TributePoint_Struct { + int32 tribute_points; + uint32 unknown04; + int32 career_tribute_points; + uint32 unknown12; +}; + +struct TributeMoney_Struct { + uint32 platinum; + uint32 tribute_master_id; + int32 tribute_points; +}; + + +struct Split_Struct +{ + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; +}; + + +/* +** New Combine Struct +** Client requesting to perform a tradeskill combine +** Size: 4 bytes +** Used In: OP_TradeSkillCombine +** Last Updated: Oct-15-2003 +** +*/ +struct NewCombine_Struct { +/*00*/ int16 container_slot; +/*02*/ char unknown02[2]; +/*04*/ +}; + + +//client requesting favorite recipies +struct TradeskillFavorites_Struct { + uint32 object_type; + uint32 some_id; + uint32 favorite_recipes[500]; +}; + +//search request +struct RecipesSearch_Struct { + uint32 object_type; //same as in favorites + uint32 some_id; //same as in favorites + uint32 mintrivial; + uint32 maxtrivial; + char query[56]; + uint32 unknown4; //is set to 00 03 00 00 + uint32 unknown5; //is set to 4C DD 12 00 +/*80*/ +}; + +//one sent for each item, from server in reply to favorites or search +struct RecipeReply_Struct { + uint32 object_type; + uint32 some_id; //same as in favorites + uint32 component_count; + uint32 recipe_id; + uint32 trivial; + char recipe_name[64]; +/*84*/ +}; + +//received and sent back as an ACK with different reply_code +struct RecipeAutoCombine_Struct { + uint32 object_type; + uint32 some_id; + uint32 unknown1; //echoed in reply + uint32 recipe_id; + uint32 reply_code; // 93 64 e1 00 (junk) in request + // 00 00 00 00 in successful reply + // f5 ff ff ff in 'you dont have all the stuff' reply +}; + +struct LevelAppearance_Struct { //Sends a little graphic on level up + uint32 spawn_id; + uint32 parm1; + uint32 value1a; + uint32 value1b; + uint32 parm2; + uint32 value2a; + uint32 value2b; + uint32 parm3; + uint32 value3a; + uint32 value3b; + uint32 parm4; + uint32 value4a; + uint32 value4b; + uint32 parm5; + uint32 value5a; + uint32 value5b; +/*64*/ +}; +struct MerchantList{ + uint32 id; + uint32 slot; + uint32 item; +}; +struct TempMerchantList{ + uint32 npcid; + uint32 slot; + uint32 item; + uint32 charges; //charges/quantity + uint32 origslot; +}; + + +struct FindPerson_Point { + float y; + float x; + float z; +}; + +struct FindPersonRequest_Struct { + uint32 unknown000; + uint32 npc_id; + FindPerson_Point client_pos; +}; + +//variable length packet of points +struct FindPersonResult_Struct { + FindPerson_Point dest; + FindPerson_Point path[0]; //last element must be the same as dest +}; + +struct MobRename_Struct { +/*000*/ char old_name[64]; +/*064*/ char old_name_again[64]; //not sure what the difference is +/*128*/ char new_name[64]; +/*192*/ uint32 unknown192; //set to 0 +/*196*/ uint32 unknown196; //set to 1 +/*200*/ +}; + +struct PlayMP3_Struct { + char filename[128]; +}; + +//this is for custom title display in the skill window +struct TitleEntry_Struct { + uint32 skill_id; + uint32 skill_value; + char title[1]; +}; + +struct Titles_Struct { + uint32 title_count; + TitleEntry_Struct titles[0]; +}; + +//this is for title selection by the client +struct TitleListEntry_Struct { + uint32 unknown0; //title ID + char prefix[1]; //variable length, null terminated + char postfix[1]; //variable length, null terminated +}; + +struct TitleList_Struct { + uint32 title_count; + TitleListEntry_Struct titles[0]; //list of title structs + //uint32 unknown_ending; seen 0x7265, 0 +}; + +struct SetTitle_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + uint32 title_id; +}; + +struct SetTitleReply_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + char title[32]; + uint32 entity_id; +}; + + +#if 0 +// Old struct not used by Task System implementation but left for reference +struct TaskDescription_Struct { +/*000*/ uint32 activity_count; //not right. +/*004*/ uint32 taskid; +/*008*/ uint8 unk; +/*009*/ uint32 id3; +/*013*/ uint32 unknown13; +/*017*/ char name[1]; //variable length, 0 terminated +/*018*/ uint32 unknown18; +/*022*/ uint32 unknown22; +/*026*/ char desc[1]; //variable length, 0 terminated +/*027*/ uint32 reward_count; //not sure +/*031*/ uint32 unknown31; +/*035*/ uint32 unknown35; +/*039*/ uint16 unknown39; +/*041*/ char reward_link[1]; //variable length, 0 terminated +/*042*/ uint32 unknown43; //maybe crystal count? +}; +#endif + +struct TaskMemberList_Struct { +/*00*/ uint32 gopher_id; +/*04*/ uint32 unknown04; +/*08*/ uint32 member_count; //1 less than the number of members +/*12*/ char list_pointer[0]; +/* list is of the form: + char member_name[1] //null terminated string + uint8 task_leader //boolean flag +*/ +}; + +#if 0 +// Old structs not used by Task System implentation but left for reference +struct TaskActivity_Struct { +/*000*/ uint32 activity_count; //not right +/*004*/ uint32 id3; +/*008*/ uint32 taskid; +/*012*/ uint32 activity_id; +/*016*/ uint32 unknown016; +/*020*/ uint32 activity_type; +/*024*/ uint32 unknown024; +/*028*/ uint32 unknown28; +/*032*/ char mob_name[1]; //variable length, 0 terminated +/*033*/ char item_name[1]; //variable length, 0 terminated +/*034*/ uint32 goal_count; +/*038*/ uint32 unknown38; //0xFFFFFFFF +/*042*/ uint32 unknown42; //0xFFFFFFFF +/*046*/ uint32 unknown46; //saw 0x151,0x156 +/*050*/ uint32 unknown50; //saw 0x404,0 +/*054*/ char activity_name[1]; //variable length, 0 terminated... commonly empty +/*055*/ uint32 done_count; +/*059*/ uint32 unknown59; //=1 except on unknown and terminal activities? +/*063*/ +}; + +struct TaskHistoryEntry_Struct { + uint32 task_id; + char name[1]; + uint32 completed_time; +}; +struct TaskHistory_Struct { + uint32 completed_count; + TaskHistoryEntry_Struct entries[0]; +}; +#endif + +struct AcceptNewTask_Struct { + uint32 unknown00; + uint32 task_id; //set to 0 for 'decline' + uint32 task_master_id; //entity ID +}; + +//was all 0's from client, server replied with same op, all 0's +struct CancelTask_Struct { + uint32 SequenceNumber; + uint32 unknown4; // Only seen 0x00000002 +}; + +#if 0 +// old struct, not used by Task System implementation but left for reference. +struct AvaliableTask_Struct { + uint32 task_index; //no idea, seen 0x1 + uint32 task_master_id; //entity ID + uint32 task_id; + uint32 unknown012; + uint32 activity_count; //not sure, seen 2 + char desc[1]; //variable length, 0 terminated + uint32 reward_platinum;//not sure on these + uint32 reward_gold; + uint32 reward_silver; + uint32 reward_copper; + char some_name[1]; //variable length, 0 terminated + uint8 unknown1; + uint32 unknown2; //0xFFFFFFFF + uint32 unknown3; //0xFFFFFFFF + uint32 unknown4; //seen 0x16 + uint8 unknown5; +}; +#endif + + +// Many of the Task System packets contain variable length strings, as well as variable numbers +// of records, hence splitting them into multiple structs (header, middle, trailer) etc. +// +struct AvailableTaskHeader_Struct { + uint32 TaskCount; + uint32 unknown1; + uint32 TaskGiver; +}; + +struct AvailableTaskData1_Struct { + uint32 TaskID; + uint32 unknown1; + uint32 TimeLimit; + uint32 unknown2; +}; + +struct AvailableTaskData2_Struct { + uint32 unknown1,unknown2,unknown3,unknown4; +}; + +struct AvailableTaskTrailer_Struct { + uint32 ItemCount; + uint32 unknown1, unknown2; + uint32 StartZone; +}; + +struct TaskDescriptionHeader_Struct { + uint32 SequenceNumber; // The order the tasks appear in the journal. 0 for first task, 1 for second, etc. + uint32 TaskID; + uint32 unknown2; + uint32 unknown3; + uint8 unknown4; +}; + +struct TaskDescriptionData1_Struct { + uint32 Duration; + uint32 unknown2; + uint32 StartTime; +}; + +struct TaskDescriptionData2_Struct { + uint32 RewardCount; // ?? + uint32 unknown1; + uint32 unknown2; + uint16 unknown3; + //uint8 unknown4; +}; + +struct TaskDescriptionTrailer_Struct { + //uint16 unknown1; // 0x0012 + uint32 Points; +}; + +struct TaskActivityHeader_Struct { + uint32 TaskSequenceNumber; + uint32 unknown2; // Seen 0x00000002 + uint32 TaskID; + uint32 ActivityID; + uint32 unknown3; + uint32 ActivityType; + uint32 Optional; + uint32 unknown5; +}; + +struct TaskActivityData1_Struct { + uint32 GoalCount; + uint32 unknown1; // 0xffffffff + uint32 unknown2; // 0xffffffff + uint32 ZoneID; // seen 0x36 + uint32 unknown3; +}; + +struct TaskActivityTrailer_Struct { + uint32 DoneCount; + uint32 unknown1; // Seen 1 +}; + +// The Short_Struct is sent for tasks that are hidden and act as a placeholder +struct TaskActivityShort_Struct { + uint32 TaskSequenceNumber; + uint32 unknown2; // Seen 0x00000002 + uint32 TaskID; + uint32 ActivityID; + uint32 unknown3; + uint32 ActivityType; // 0xffffffff for the short packet + uint32 unknown4; +}; + +struct TaskActivityComplete_Struct { + uint32 TaskIndex; + uint32 unknown2; // 0x00000002 + uint32 unknown3; + uint32 ActivityID; + uint32 unknown4; // 0x00000001 + uint32 unknown5; // 0x00000001 +}; + +#if 0 +// This is a dupe of the CancelTask struct +struct TaskComplete_Struct { + uint32 unknown00; // 0x00000000 + uint32 unknown04; // 0x00000002 +}; +#endif + +struct TaskHistoryRequest_Struct { + uint32 TaskIndex; // This is the sequence the task was sent in the Completed Tasks packet. +}; + +struct TaskHistoryReplyHeader_Struct { + uint32 TaskID; + uint32 ActivityCount; +}; + +struct TaskHistoryReplyData1_Struct { + uint32 ActivityType; +}; + +struct TaskHistoryReplyData2_Struct { + uint32 GoalCount; + uint32 unknown04; // 0xffffffff + uint32 unknown08; // 0xffffffff + uint32 ZoneID; + uint32 unknown16; +}; + +struct BankerChange_Struct { + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; + uint32 platinum_bank; + uint32 gold_bank; + uint32 silver_bank; + uint32 copper_bank; +}; + +struct LeadershipExpUpdate_Struct { +/*00*/ double group_leadership_exp; +/*08*/ uint32 group_leadership_points; +/*12*/ uint32 Unknown12; +/*16*/ double raid_leadership_exp; +/*24*/ uint32 raid_leadership_points; +}; + +struct UpdateLeadershipAA_Struct { +/*00*/ uint32 ability_id; +/*04*/ uint32 new_rank; +/*08*/ uint32 unknown08; +/*12*/ +}; + +/** +* Leadership AA update +* Length: 32 Octets +* OpCode: LeadExpUpdate +*/ +struct leadExpUpdateStruct { + /*0000*/ uint32 unknown0000; // All zeroes? + /*0004*/ uint32 group_leadership_exp; // Group leadership exp value + /*0008*/ uint32 group_leadership_points; // Unspent group points + /*0012*/ uint32 unknown0012; // Type? + /*0016*/ uint32 unknown0016; // All zeroes? + /*0020*/ uint32 raid_leadership_exp; // Raid leadership exp value + /*0024*/ uint32 raid_leadership_points; // Unspent raid points + /*0028*/ uint32 unknown0028; +}; + +struct RaidGeneral_Struct { +/*00*/ uint32 action; +/*04*/ char player_name[64]; +/*68*/ uint32 unknown68; +/*72*/ char leader_name[64]; +/*136*/ uint32 parameter; +}; + +struct RaidAddMember_Struct { +/*000*/ RaidGeneral_Struct raidGen; //param = (group num-1); 0xFFFFFFFF = no group +/*136*/ uint8 _class; +/*137*/ uint8 level; +/*138*/ uint8 isGroupLeader; +/*139*/ uint8 flags[5]; //no idea if these are needed... +}; + +struct RaidAdd_Struct { +/*000*/ uint32 action; //=0 +/*004*/ char player_name[64]; //should both be the player's name +/*068*/ char leader_name[64]; +/*132*/ uint8 _class; +/*133*/ uint8 level; +/*134*/ uint8 has_group; +/*135*/ uint8 unknown135; //seems to be 0x42 or 0 +}; + +struct RaidCreate_Struct { +/*00*/ uint32 action; //=8 +/*04*/ char leader_name[64]; +/*68*/ uint32 leader_id; +}; + +struct RaidMemberInfo_Struct { +/*00*/ uint8 group_number; +/*01*/ char member_name[1]; //dyanmic length, null terminated '\0' +/*00*/ uint8 unknown00; +/*01*/ uint8 _class; +/*02*/ uint8 level; +/*03*/ uint8 is_raid_leader; +/*04*/ uint8 is_group_leader; +/*05*/ uint8 main_tank; //not sure +/*06*/ uint8 unknown06[5]; //prolly more flags +}; + +struct RaidDetails_Struct { +/*000*/ uint32 action; //=6,20 +/*004*/ char leader_name[64]; +/*068*/ uint32 unknown68[4]; +/*084*/ LeadershipAA_Struct abilities; //ranks in backwards byte order +/*128*/ uint8 unknown128[142]; +/*354*/ uint32 leader_id; +}; + +struct RaidMembers_Struct { +/*000*/ RaidDetails_Struct details; +/*358*/ uint32 member_count; //including leader +/*362*/ RaidMemberInfo_Struct members[1]; +/*...*/ RaidMemberInfo_Struct empty; //seem to have an extra member with a 0 length name on the end +}; + +struct DynamicWall_Struct { +/*00*/ char name[32]; +/*32*/ float y; +/*36*/ float x; +/*40*/ float z; +/*44*/ uint32 something; +/*48*/ uint32 unknown48; //0 +/*52*/ uint32 one_hundred; //0x64 +/*56*/ uint32 unknown56; //0 +/*60*/ uint32 something2; +/*64*/ int32 unknown64; //-1 +/*68*/ uint32 unknown68; //0 +/*72*/ uint32 unknown72; //0 +/*76*/ uint32 unknown76; //0x100 +/*80*/ +}; + +enum { //bandolier actions + BandolierCreate = 0, + BandolierRemove = 1, + BandolierSet = 2 +}; + +struct BandolierCreate_Struct { +/*00*/ uint32 action; //0 for create +/*04*/ uint8 number; +/*05*/ char name[32]; +/*37*/ uint16 unknown37; //seen 0x93FD +/*39*/ uint8 unknown39; //0 +}; + +struct BandolierDelete_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ uint8 unknown05[35]; +}; + +struct BandolierSet_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ uint8 unknown05[35]; +}; + +struct Arrow_Struct { +/*000*/ float src_y; +/*004*/ float src_x; +/*008*/ float src_z; +/*012*/ uint8 unknown012[12]; +/*024*/ float velocity; //4 is normal, 20 is quite fast +/*028*/ float launch_angle; //0-450ish, not sure the units, 140ish is straight +/*032*/ float tilt; //on the order of 125 +/*036*/ uint8 unknown036[8]; +/*044*/ float arc; +/*048*/ uint32 source_id; +/*052*/ uint32 target_id; //entity ID +/*056*/ uint32 item_id; +/*060*/ uint32 unknown060; +/*064*/ uint32 unknown064; +/*068*/ uint8 unknown068; +/*069*/ uint8 unknown069; +/*070*/ uint8 unknown070; +/*071*/ uint8 item_type; +/*072*/ uint8 skill; +/*073*/ char model_name[43]; +/*116*/ +}; + +//made a bunch of trivial structs for stuff for opcode finder to use +struct Consent_Struct { + char name[1]; //always at least a null - was 1 +}; + +struct AdventureMerchant_Struct { + uint32 unknown_flag; //seems to be 1 + uint32 entity_id; +}; + +struct Save_Struct { + uint8 unknown00[192]; + uint8 unknown0192[176]; +}; + +struct GMToggle_Struct { + uint8 unknown0[64]; + uint32 toggle; +}; + +struct BuffFadeMsg_Struct { + uint32 color; + char msg[1]; //was 1 +/*0???*/ uint8 paddingXXX[3]; // always 0's +}; + +struct UseAA_Struct { + uint32 begin; + uint32 ability; + uint32 end; +}; + +struct AA_Ability { +/*00*/ uint32 skill_id; +/*04*/ uint32 base1; +/*08*/ uint32 base2; +/*12*/ uint32 slot; +/*16*/ +}; + +struct SendAA_Struct { +/*0000*/ uint32 id; +/*0004*/ uint8 unknown004; // uint32 unknown004; set to 1. +/*0005*/ uint32 hotkey_sid; +/*0009*/ uint32 hotkey_sid2; +/*0013*/ uint32 title_sid; +/*0017*/ uint32 desc_sid; +/*0021*/ uint32 class_type; +/*0025*/ uint32 cost; +/*0029*/ uint32 seq; +/*0033*/ uint32 current_level; //1s, MQ2 calls this AARankRequired +/*0037*/ uint32 prereq_skill; //is < 0, abs() is category # +/*0041*/ uint32 prereq_minpoints; //min points in the prereq +/*0045*/ uint32 type; +/*0049*/ uint32 spellid; +/*0053*/ uint32 spell_type; +/*0057*/ uint32 spell_refresh; +/*0061*/ uint16 classes; +/*0063*/ uint16 berserker; //seems to be 1 if its a berserker ability +/*0065*/ uint32 max_level; +/*0069*/ uint32 last_id; +/*0073*/ uint32 next_id; +/*0077*/ uint32 cost2; +/*0081*/ uint8 unknown80[7]; +/*0088*/ uint32 aa_expansion; +/*0092*/ uint32 special_category; +/*0096*/ uint16 unknown0096; +/*0098*/ uint32 total_abilities; +/*0102*/ AA_Ability abilities[0]; +}; + +struct AA_List { + SendAA_Struct* aa[0]; +}; + +struct AA_Action { +/*00*/ uint32 action; +/*04*/ uint32 ability; +/*08*/ uint32 unknown08; +/*12*/ uint32 exp_value; +}; + +struct AA_Skills { //this should be removed and changed to AA_Array +/*00*/ uint32 aa_skill; // Total AAs Spent +/*04*/ uint32 aa_value; +/*08*/ uint32 unknown08; +}; + +struct AAExpUpdate_Struct { +/*00*/ uint32 unknown00; //seems to be a value from AA_Action.ability +/*04*/ uint32 aapoints_unspent; +/*08*/ uint8 aaxp_percent; //% of exp that goes to AAs +/*09*/ uint8 unknown09[3]; //live doesn't always zero these, so they arnt part of aaxp_percent +}; + + +struct AltAdvStats_Struct { +/*000*/ uint32 experience; +/*004*/ uint16 unspent; +/*006*/ uint16 unknown006; +/*008*/ uint8 percentage; +/*009*/ uint8 unknown009[3]; +}; + +struct PlayerAA_Struct { + AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct AATable_Struct { +/*00*/ int32 aa_spent; // Total AAs Spent +/*04*/ AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct Weather_Struct { + uint32 val1; //generall 0x000000FF + uint32 type; //0x31=rain, 0x02=snow(i think), 0 = normal + uint32 mode; +}; + +struct ZoneInUnknown_Struct { + uint32 val1; + uint32 val2; + uint32 val3; +}; + +struct MobHealth_Struct { + uint16 entity_id; + uint8 hp; +}; + +struct AnnoyingZoneUnknown_Struct { + uint32 entity_id; + uint32 value; //always 4 +}; + +struct BlockedBuffs_Struct { +/*000*/ uint8 unknown000[80]; +/*080*/ uint8 unknown081; +/*081*/ uint8 unknown082; +/*082*/ uint8 unknown083; +/*083*/ uint8 unknown084; +/*084*/ uint8 unknown085; +/*085*/ uint8 unknown086; +/*086*/ uint8 unknown087; +/*087*/ uint8 unknown088; +/*088*/ +}; + +//Size 24 Bytes +struct WorldObfuscator_Struct +{ +/*000*/ uint32 var1; +/*004*/ uint32 Unknown1; +/*008*/ uint32 Unknown2; +/*012*/ uint32 Unknown3; +/*016*/ uint32 var2; +/*020*/ uint32 Unknown4; +/*024*/ +}; + +struct ExpansionInfo_Struct { +/*000*/ char Unknown000[64]; +/*064*/ uint32 Expansions; +}; + +struct ApplyPoison_Struct { + uint32 inventorySlot; + uint32 success; +}; + +struct ItemVerifyRequest_Struct { +/*000*/ int32 slot; // Slot being Right Clicked +/*004*/ uint32 target; // Target Entity ID +/*008*/ +}; + +struct ItemVerifyReply_Struct { +/*000*/ int32 slot; // Slot being Right Clicked +/*004*/ uint32 spell; // Spell ID to cast if different than item effect +/*008*/ uint32 target; // Target Entity ID +/*012*/ +}; + +struct ItemSerializationHeader +{ + uint32 stacksize; + uint32 unknown004; + uint32 slot; + uint32 price; + uint32 merchant_slot; //1 if not a merchant item + uint32 unknown020; //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 + uint32 charges; //Total Charges an item has (-1 for unlimited) + uint32 inst_nodrop; // 1 if the item is no drop (attuned items) + uint32 unknown044; //0 + uint32 unknown048; //0 + uint32 unknown052; //0 + uint32 unknown056; //0 + uint8 unknown060; //0 + uint8 unknown061; //0 - Add Evolving Item struct if this isn't set to 0? + uint8 ItemClass; //0, 1, or 2 +}; + +struct ItemBodyStruct +{ + uint32 id; + uint8 weight; + uint8 norent; + uint8 nodrop; + uint8 attune; + uint8 size; + uint32 slots; + uint32 price; + uint32 icon; + uint8 unknown1; + uint8 unknown2; + uint32 BenefitFlag; + uint8 tradeskills; + int8 CR; + int8 DR; + int8 PR; + int8 MR; + int8 FR; + int8 SVCorruption; + int8 AStr; + int8 ASta; + int8 AAgi; + int8 ADex; + int8 ACha; + int8 AInt; + int8 AWis; + int32 HP; + int32 Mana; + uint32 Endur; + int32 AC; + int32 regen; + int32 mana_regen; + int32 end_regen; + uint32 Classes; + uint32 Races; + uint32 Deity; + int32 SkillModValue; + uint32 unknown6; + uint32 SkillModType; + uint32 BaneDmgRace; + uint32 BaneDmgBody; + uint32 BaneDmgRaceAmt; + int32 BaneDmgAmt; + uint8 Magic; + int32 CastTime_; + uint32 ReqLevel; + uint32 RecLevel; + uint32 RecSkill; + uint32 BardType; + int32 BardValue; + uint8 Light; + uint8 Delay; + uint8 ElemDmgType; + uint8 ElemDmgAmt; + uint8 Range; + uint32 Damage; + uint32 Color; + uint8 ItemType; + uint32 Material; + uint32 unknown7; + uint32 EliteMaterial; + float SellRate; + int32 CombatEffects; + int32 Shielding; + int32 StunResist; + int32 StrikeThrough; + int32 ExtraDmgSkill; + int32 ExtraDmgAmt; + int32 SpellShield; + int32 Avoidance; + int32 Accuracy; + uint32 CharmFileID; + uint32 FactionMod1; + int32 FactionAmt1; + uint32 FactionMod2; + int32 FactionAmt2; + uint32 FactionMod3; + int32 FactionAmt3; + uint32 FactionMod4; + int32 FactionAmt4; + +}; + +struct AugSlotStruct +{ + uint32 type; + uint8 visible; + uint8 unknown; +}; + +struct ItemTertiaryBodyStruct +{ + int32 loregroup; + uint8 artifact; + uint8 summonedflag; + uint32 favor; + uint8 fvnodrop; + int32 dotshield; + int32 atk; + int32 haste; + int32 damage_shield; + uint32 guildfavor; + uint32 augdistil; + int32 unknown3; + uint32 unknown4; + uint8 no_pet; + uint8 unknown5; + + uint8 potion_belt_enabled; + uint32 potion_belt_slots; + + uint32 stacksize; + uint8 no_transfer; + uint16 expendablearrow; + + uint32 unknown8; + uint32 unknown9; + uint32 unknown10; + uint32 unknown11; + uint8 unknown12; + uint8 unknown13; + uint8 unknown14; +}; + +struct ClickEffectStruct +{ + int32 effect; + uint8 level2; + uint32 type; + uint8 level; + int32 max_charges; + int32 cast_time; + uint32 recast; + int32 recast_type; + uint32 clickunk5; + //uint8 effect_string; //unused + //int32 clickunk7; +}; + +struct ProcEffectStruct +{ + uint32 effect; + uint8 level2; + uint32 type; + uint8 level; + uint32 unknown1; // poison? + uint32 unknown2; + uint32 unknown3; + uint32 unknown4; + uint32 procrate; + //uint8 effect_string; + //uint32 unknown5; +}; + +struct WornEffectStruct //worn, focus and scroll effect +{ + uint32 effect; + uint8 level2; + uint32 type; + uint8 level; + uint32 unknown1; + uint32 unknown2; + uint32 unknown3; + uint32 unknown4; + uint32 unknown5; + //uint8 effect_string; + //uint32 unknown6; +}; + + +struct ItemSecondaryBodyStruct{ + uint32 augtype; + uint32 augrestrict; + AugSlotStruct augslots[5]; + + uint32 ldonpoint_type; + uint32 ldontheme; + uint32 ldonprice; + uint32 ldonsellbackrate; + uint32 ldonsold; + + uint8 bagtype; + uint8 bagslots; + uint8 bagsize; + uint8 wreduction; + + uint8 book; + uint8 booktype; + //int32 filename; filename is either 0xffffffff/0x00000000 or the null term string ex: CREWizardNote\0 +}; + +struct ItemQuaternaryBodyStruct +{ + uint32 scriptfileid; + uint8 quest_item; + uint32 unknown15; //0xffffffff - Power Source Capacity? + uint32 Purity; + uint32 BackstabDmg; + uint32 DSMitigation; + int32 HeroicStr; + int32 HeroicInt; + int32 HeroicWis; + int32 HeroicAgi; + int32 HeroicDex; + int32 HeroicSta; + int32 HeroicCha; + int32 HeroicMR; + int32 HeroicFR; + int32 HeroicCR; + int32 HeroicDR; + int32 HeroicPR; + int32 HeroicSVCorrup; + int32 HealAmt; + int32 SpellDmg; + uint32 evolve_string; // Some String, but being evolution related is just a guess + uint32 subitem_count; +}; + +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*/ +}; + +struct VeteranRewardItem +{ +/*000*/ uint32 item_id; +/*004*/ uint32 charges; +/*008*/ char item_name[64]; +}; + +struct VeteranReward +{ +/*000*/ uint32 claim_id; +/*004*/ uint32 number_available; +/*008*/ uint32 claim_count; +/*012*/ VeteranRewardItem items[8]; +}; + +struct ExpeditionExpireWarning +{ +/*000*/ uint32 unknown000; +/*004*/ uint32 minutes_remaining; +}; + +struct ExpeditionInfo_Struct +{ +/*000*/ uint32 clientid; +/*004*/ uint32 enabled_max; +/*008*/ uint32 max_players; +/*012*/ char expedition_name[128]; +/*142*/ char leader_name[64]; +}; + +struct ExpeditionCompassEntry_Struct +{ +/*000*/ float unknown000; //seen *((uint32*)) = 1584791871 +/*004*/ uint32 enabled; //guess +/*008*/ uint32 unknown008; //seen 1019 +/*012*/ float y; +/*016*/ float x; +/*020*/ float z; +}; + +struct ExpeditionCompass_Struct +{ +/*000*/ uint32 clientid; +/*004*/ uint32 count; +/*008*/ ExpeditionCompassEntry_Struct entries[0]; +}; + +struct ExpeditionJoinPrompt_Struct +{ +/*000*/ uint32 clientid; +/*004*/ char player_name[64]; +/*068*/ char expedition_name[64]; +}; + +struct AltCurrencySelectItem_Struct { + uint32 merchant_entity_id; + uint32 slot_id; + uint32 unknown008; + uint32 unknown012; + uint32 unknown016; + uint32 unknown020; + uint32 unknown024; + uint32 unknown028; + uint32 unknown032; + uint32 unknown036; + uint32 unknown040; + uint32 unknown044; + uint32 unknown048; + uint32 unknown052; + uint32 unknown056; + uint32 unknown060; + uint32 unknown064; + uint32 unknown068; + uint32 unknown072; + uint32 unknown076; +}; + +struct AltCurrencySellItem_Struct { +/*000*/ uint32 merchant_entity_id; +/*004*/ uint32 slot_id; +/*006*/ uint32 charges; +/*010*/ uint32 cost; +}; + + + }; //end namespace structs +}; //end namespace SoF + +#endif /*SoF_STRUCTS_H_*/ + + + + + + + + + + diff --git a/common/patches/Titanium.cpp b/common/patches/Titanium.cpp new file mode 100644 index 000000000..b9db73cd1 --- /dev/null +++ b/common/patches/Titanium.cpp @@ -0,0 +1,1450 @@ + +#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 "../MiscFunctions.h" +#include "../Item.h" +#include "Titanium_structs.h" +#include + +namespace Titanium { + +static const char *name = "Titanium"; +static OpcodeManager *opcodes = NULL; +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 == NULL) { + //TODO: get this file name from the config file + 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; + string pname; + + //register our world signature. + pname = 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 = 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 != NULL) { + //TODO: get this file name from the config file + 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); +} + + + +#include "SSDefine.h" + +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); +// 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 = NULL; + + 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 = NULL; + + //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 = NULL; + + //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 = NULL; + + //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; + 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 = NULL; + dest->FastQueuePacket(&in, ack_req); + return; + } + + //consume the packet + EQApplicationPacket *in = *p; + *p = NULL; + + //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 = NULL; + 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 = NULL; + + //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 = NULL; + + 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)); + + 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[] __emu_buffer; +} + +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 = NULL; + + 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 = NULL; + char *instance = NULL; + const char *protection=(const char *)"\\\\\\\\\\"; + char *sub_items[10] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + 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/Titanium.h b/common/patches/Titanium.h new file mode 100644 index 000000000..185e55252 --- /dev/null +++ b/common/patches/Titanium.h @@ -0,0 +1,36 @@ +#ifndef Titanium_H_ +#define Titanium_H_ + +#include "../StructStrategy.h" + +class EQStreamIdentifier; + +namespace Titanium { + + //these are the only public member of this namespace. + extern void Register(EQStreamIdentifier &into); + extern void Reload(); + + + + //you should not directly access anything below.. + //I just dont feel like making a seperate header for it. + + class Strategy : public StructStrategy { + public: + Strategy(); + + protected: + + virtual std::string Describe() const; + + //magic macro to declare our opcode processors + #include "SSDeclare.h" + #include "Titanium_ops.h" + }; + +}; + + + +#endif /*Titanium_H_*/ diff --git a/common/patches/Titanium_itemfields.h b/common/patches/Titanium_itemfields.h new file mode 100644 index 000000000..8030de887 --- /dev/null +++ b/common/patches/Titanium_itemfields.h @@ -0,0 +1,174 @@ +/* + + +These fields must be in the order of how they are serialized! + + + +*/ + + +/* 000 */ //I(ItemClass) Leave this one off on purpose +/* 001 */ S(Name) +/* 002 */ S(Lore) +/* 003 */ S(IDFile) +/* 004 */ I(ID) +/* 005 */ I(Weight) +/* 006 */ I(NoRent) +/* 007 */ I(NoDrop) +/* 008 */ I(Size) +/* 009 */ I(Slots) +/* 010 */ I(Price) +/* 011 */ I(Icon) +/* 012 */ C("0") +/* 013 */ C("0") +/* 014 */ I(BenefitFlag) +/* 015 */ I(Tradeskills) +/* 016 */ I(CR) +/* 017 */ I(DR) +/* 018 */ I(PR) +/* 019 */ I(MR) +/* 020 */ I(FR) +/* 021 */ I(AStr) +/* 022 */ I(ASta) +/* 023 */ I(AAgi) +/* 024 */ I(ADex) +/* 025 */ I(ACha) +/* 026 */ I(AInt) +/* 027 */ I(AWis) +/* 028 */ I(HP) +/* 029 */ I(Mana) +/* 030 */ I(AC) +/* 031 */ I(Deity) +/* 032 */ I(SkillModValue) +/* 033 */ C("0") +/* 034 */ I(SkillModType) +/* 035 */ I(BaneDmgRace) +/* 036 */ I(BaneDmgAmt) +/* 037 */ I(BaneDmgBody) +/* 038 */ I(Magic) +/* 039 */ I(CastTime_) +/* 040 */ I(ReqLevel) +/* 041 */ I(BardType) +/* 042 */ I(BardValue) +/* 043 */ I(Light) +/* 044 */ I(Delay) +/* 045 */ I(RecLevel) +/* 046 */ I(RecSkill) +/* 047 */ I(ElemDmgType) +/* 048 */ I(ElemDmgAmt) +/* 049 */ I(Range) +/* 050 */ I(Damage) +/* 051 */ I(Color) +/* 052 */ I(Classes) +/* 053 */ I(Races) +/* 054 */ C("0") +/* 055 */ I(MaxCharges) +/* 056 */ I(ItemType) +/* 057 */ I(Material) +/* 058 */ F(SellRate) +/* 059 */ C("0") +/* 060 */ I(CastTime_) +/* 061 */ C("0") +/* 062 */ I(ProcRate) +/* 063 */ I(CombatEffects) +/* 064 */ I(Shielding) +/* 065 */ I(StunResist) +/* 066 */ I(StrikeThrough) +/* 067 */ I(ExtraDmgSkill) +/* 068 */ I(ExtraDmgAmt) +/* 069 */ I(SpellShield) +/* 070 */ I(Avoidance) +/* 071 */ I(Accuracy) +/* 072 */ I(CharmFileID) +/* 073 */ I(FactionMod1) +/* 074 */ I(FactionMod2) +/* 075 */ I(FactionMod3) +/* 076 */ I(FactionMod4) +/* 077 */ I(FactionAmt1) +/* 078 */ I(FactionAmt2) +/* 079 */ I(FactionAmt3) +/* 080 */ I(FactionAmt4) +/* 081 */ S(CharmFile) +/* 082 */ I(AugType) +/* 083 */ I(AugSlotType[0]) +/* 084 */ I(AugSlotVisible[0]) +/* 085 */ I(AugSlotType[1]) +/* 086 */ I(AugSlotVisible[1]) +/* 087 */ I(AugSlotType[2]) +/* 088 */ I(AugSlotVisible[2]) +/* 089 */ I(AugSlotType[3]) +/* 090 */ I(AugSlotVisible[3]) +/* 091 */ I(AugSlotType[4]) +/* 092 */ I(AugSlotVisible[4]) +/* 093 */ I(LDoNTheme) +/* 094 */ I(LDoNPrice) +/* 095 */ I(LDoNSold) +/* 096 */ I(BagType) +/* 097 */ I(BagSlots) +/* 098 */ I(BagSize) +/* 099 */ I(BagWR) +/* 100 */ I(Book) +/* 101 */ I(BookType) +/* 102 */ S(Filename) +/* 103 */ I(BaneDmgRaceAmt) +/* 104 */ I(AugRestrict) +/* 105 */ I(LoreGroup) +/* 106 */ I(PendingLoreFlag) +/* 107 */ I(ArtifactFlag) +/* 108 */ I(SummonedFlag) +/* 109 */ I(Favor) +/* 110 */ I(FVNoDrop) +/* 111 */ I(Endur) +/* 112 */ I(DotShielding) +/* 113 */ I(Attack) +/* 114 */ I(Regen) +/* 115 */ I(ManaRegen) +/* 116 */ I(EnduranceRegen) +/* 117 */ I(Haste) +/* 118 */ I(DamageShield) +/* 119 */ I(RecastDelay) +/* 120 */ I(RecastType) +/* 121 */ I(GuildFavor) +/* 122 */ I(AugDistiller) +/* 123 */ C("0") +/* 124 */ C("0") +/* 125 */ I(Attuneable) +/* 126 */ I(NoPet) +/* 127 */ C("0") +/* 128 */ I(PointType) +/* 129 */ I(PotionBelt) +/* 130 */ I(PotionBeltSlots) +/* 131 */ I(StackSize) +/* 132 */ I(NoTransfer) +/* 133 */ I(Stackable) +/* 134 */ I(Click.Effect) +/* 135 */ I(Click.Type) +/* 136 */ I(Click.Level2) +/* 137 */ I(Click.Level) +/* 138 */ C("0") +/* 139 */ I(Proc.Effect) +/* 140 */ I(Proc.Type) +/* 141 */ I(Proc.Level2) +/* 142 */ I(Proc.Level) +/* 143 */ C("0") +/* 144 */ I(Worn.Effect) +/* 145 */ I(Worn.Type) +/* 146 */ I(Worn.Level2) +/* 147 */ I(Worn.Level) +/* 148 */ C("0") +/* 149 */ I(Focus.Effect) +/* 150 */ I(Focus.Type) +/* 151 */ I(Focus.Level2) +/* 152 */ I(Focus.Level) +/* 153 */ C("0") +/* 154 */ I(Scroll.Effect) +/* 155 */ I(Scroll.Type) +/* 156 */ I(Scroll.Level2) +/* 157 */ I(Scroll.Level) +/* 158 */ C("0") +#undef I +#undef C +#undef S +#undef F + diff --git a/common/patches/Titanium_ops.h b/common/patches/Titanium_ops.h new file mode 100644 index 000000000..ead6bcecc --- /dev/null +++ b/common/patches/Titanium_ops.h @@ -0,0 +1,53 @@ + +//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) +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_DeleteSpawn) +E(OP_WearChange) +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_LFGuild) +//list of packets we need to decode on the way in: +D(OP_SetServerFilter) +D(OP_CharacterCreate) +D(OP_ItemLinkClick) +D(OP_TraderBuy) +D(OP_WhoAllRequest) +D(OP_ReadBook) +D(OP_FaceChange) +D(OP_InspectRequest) +D(OP_InspectAnswer) +D(OP_WearChange) +D(OP_LFGuild) +#undef E +#undef D diff --git a/common/patches/Titanium_structs.h b/common/patches/Titanium_structs.h new file mode 100644 index 000000000..d1aef83df --- /dev/null +++ b/common/patches/Titanium_structs.h @@ -0,0 +1,3280 @@ +#ifndef Titanium_STRUCTS_H_ +#define Titanium_STRUCTS_H_ + +namespace Titanium { + namespace structs { + + +static const uint32 BUFF_COUNT = 25; + +/* +** Compiler override to ensure +** byte aligned structures +*/ +#pragma pack(1) + +struct LoginInfo_Struct { +/*000*/ char login_info[64]; +/*064*/ uint8 unknown064[124]; +/*188*/ uint8 zoning; // 01 if zoning, 00 if not +/*189*/ uint8 unknown189[275]; +/*488*/ +}; + +struct EnterWorld_Struct { +/*000*/ char name[64]; +/*064*/ uint32 tutorial; // 01 on "Enter Tutorial", 00 if not +/*068*/ uint32 return_home; // 01 on "Return Home", 00 if not +}; + +/* Name Approval Struct */ +/* Len: */ +/* Opcode: 0x8B20*/ +struct NameApproval +{ + char name[64]; + uint32 race; + uint32 class_; + uint32 deity; +}; + +/* +** Entity identification struct +** Size: 4 bytes +** OPCodes: OP_DeleteSpawn, OP_Assist +*/ +struct EntityId_Struct +{ +/*00*/ uint32 entity_id; +/*04*/ +}; + +struct Duel_Struct +{ + uint32 duel_initiator; + uint32 duel_target; +}; + +struct DuelResponse_Struct +{ + uint32 target_id; + uint32 entity_id; + uint32 unknown; +}; +/* + Cofruben: + Adventure stuff,not a net one,just one for our use +*/ +static const uint32 ADVENTURE_COLLECT = 0; +static const uint32 ADVENTURE_MASSKILL = 1; +static const uint32 ADVENTURE_NAMED = 2; +static const uint32 ADVENTURE_RESCUE = 3; + +struct AdventureInfo { + uint32 QuestID; + uint32 NPCID; + bool in_use; + uint32 status; + bool ShowCompass; + uint32 Objetive;// can be item to collect,mobs to kill,boss to kill and someone to rescue. + uint32 ObjetiveValue;// number of items,or number of needed mob kills. + char text[512]; + uint8 type; + uint32 minutes; + uint32 points; + float x; + float y; + uint32 zoneid; + uint32 zonedungeonid; +}; +/////////////////////////////////////////////////////////////////////////////// + + +/* +** Color_Struct +** Size: 4 bytes +** Used for convenience +** Merth: Gave struct a name so gcc 2.96 would compile +** +*/ +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; + }; +}; + +/* +** Character Selection Struct +** Length: 1704 Bytes +** +*/ +struct CharacterSelect_Struct { +/*0000*/ uint32 race[10]; // Characters Race +/*0040*/ Color_Struct cs_colors[10][9]; // Characters Equipment Colors +/*0400*/ uint8 beardcolor[10]; // Characters beard Color +/*0410*/ uint8 hairstyle[10]; // Characters hair style +/*0420*/ uint32 equip[10][9]; // 0=helm, 1=chest, 2=arm, 3=bracer, 4=hand, 5=leg, 6=boot, 7=melee1, 8=melee2 (Might not be) +/*0780*/ uint32 secondary[10]; // Characters secondary IDFile number +/*0820*/ uint8 unknown820[10]; // 10x ff +/*0830*/ uint8 unknown830[2]; // 2x 00 +/*0832*/ uint32 deity[10]; // Characters Deity +/*0872*/ uint8 gohome[10]; // 1=Go Home available, 0=not +/*0882*/ uint8 tutorial[10]; // 1=Tutorial available, 0=not +/*0892*/ uint8 beard[10]; // Characters Beard Type +/*0902*/ uint8 unknown902[10]; // 10x ff +/*0912*/ uint32 primary[10]; // Characters primary IDFile number +/*0952*/ uint8 haircolor[10]; // Characters Hair Color +/*0962*/ uint8 unknown0962[2]; // 2x 00 +/*0964*/ uint32 zone[10]; // Characters Current Zone +/*1004*/ uint8 class_[10]; // Characters Classes +/*1014*/ uint8 face[10]; // Characters Face Type +/*1024*/ char name[10][64]; // Characters Names +/*1664*/ uint8 gender[10]; // Characters Gender +/*1674*/ uint8 eyecolor1[10]; // Characters Eye Color +/*1684*/ uint8 eyecolor2[10]; // Characters Eye 2 Color +/*1694*/ uint8 level[10]; // Characters Levels +/*1704*/ +}; + +/* +** Generic Spawn Struct +** Length: 257 Bytes +** Fields from old struct not yet found: +** float size; +** float walkspeed; // probably one of the ff 33 33 33 3f +** float runspeed; // probably one of the ff 33 33 33 3f +** uint8 traptype; // 65 is disarmable trap, 66 and 67 are invis triggers/traps +** uint8 npc_armor_graphic; // 0xFF=Player, 0=none, 1=leather, 2=chain, 3=steelplate +** uint8 npc_helm_graphic; // 0xFF=Player, 0=none, 1=leather, 2=chain, 3=steelplate +** +*/ + +/* +** Generic Spawn Struct +** Length: 383 Octets +** Used in: +** spawnZoneStruct +** dbSpawnStruct +** petStruct +** newSpawnStruct +*/ +/* +showeq -> eqemu +sed -e 's/_t//g' -e 's/seto_0xFF/set_to_0xFF/g' +*/ +struct Spawn_Struct { +/*0000*/ uint8 unknown0000; +/*0001*/ uint8 gm; // 0=no, 1=gm +/*0002*/ uint8 unknown0003; +/*0003*/ uint8 aaitle; // 0=none, 1=general, 2=archtype, 3=class +/*0004*/ uint8 unknown0004; +/*0005*/ uint8 anon; // 0=normal, 1=anon, 2=roleplay +/*0006*/ uint8 face; // Face id for players +/*0007*/ char name[64]; // Player's Name +/*0071*/ uint16 deity; // Player's Deity +/*0073*/ uint16 unknown0073; +/*0075*/ float size; // Model size +/*0079*/ uint32 unknown0079; +/*0083*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse,a +/*0084*/ uint8 invis; // Invis (0=not, 1=invis) +/*0085*/ uint8 haircolor; // Hair color +/*0086*/ uint8 curHp; // Current hp %%% wrong +/*0087*/ uint8 max_hp; // (name prolly wrong)takes on the value 100 for players, 100 or 110 for NPCs and 120 for PC corpses... +/*0088*/ uint8 findable; // 0=can't be found, 1=can be found +/*0089*/ uint8 unknown0089[5]; +/*0094*/ signed deltaHeading:10;// change in heading + signed x:19; // x coord + signed padding0054:3; // ***Placeholder +/*0098*/ signed y:19; // y coord + signed animation:10; // animation + signed padding0058:3; // ***Placeholder +/*0102*/ signed z:19; // z coord + signed deltaY:13; // change in y +/*0106*/ signed deltaX:13; // change in x + unsigned heading:12; // heading + signed padding0066:7; // ***Placeholder +/*0110*/ signed deltaZ:13; // change in z + signed padding0070:19; // ***Placeholder +/*0114*/ uint8 eyecolor1; // Player's left eye color +/*0115*/ uint8 unknown0115[24]; +/*0139*/ uint8 showhelm; // 0=no, 1=yes +/*0140*/ uint8 unknown0140[4]; +/*0144*/ uint8 is_npc; // 0=no, 1=yes +/*0145*/ uint8 hairstyle; // Hair style +/*0146*/ uint8 beardcolor; // Beard color +/*0147*/ uint8 unknown0147[4]; +/*0151*/ uint8 level; // Spawn Level +/*0152*/ uint8 unknown0259[4]; // ***Placeholder +/*0156*/ uint8 beard; // Beard style +/*0157*/ char suffix[32]; // Player's suffix (of Veeshan, etc.) +/*0189*/ uint32 petOwnerId; // If this is a pet, the spawn id of owner +/*0193*/ uint8 guildrank; // 0=normal, 1=officer, 2=leader +/*0194*/ uint8 unknown0194[3]; +/*0197*/ union + { + struct + { + /*0197*/ uint32 equip_helmet; // Equipment: Helmet Visual + /*0201*/ uint32 equip_chest; // Equipment: Chest Visual + /*0205*/ uint32 equip_arms; // Equipment: Arms Visual + /*0209*/ uint32 equip_bracers; // Equipment: Bracers Visual + /*0213*/ uint32 equip_hands; // Equipment: Hands Visual + /*0217*/ uint32 equip_legs; // Equipment: Legs Visual + /*0221*/ uint32 equip_feet; // Equipment: Feet Visual + /*0225*/ uint32 equip_primary; // Equipment: Primary Visual + /*0229*/ uint32 equip_secondary; // Equipment: Secondary Visual + } equip; + /*0197*/ uint32 equipment[9]; // Array elements correspond to struct equipment above + }; +/*0233*/ float runspeed; // Speed when running +/*0036*/ uint8 afk; // 0=no, 1=afk +/*0238*/ uint32 guildID; // Current guild +/*0242*/ char title[32]; // Title +/*0274*/ uint8 unknown0274; +/*0275*/ uint8 helm; // Helm texture +/*0276*/ uint8 set_to_0xFF[8]; // ***Placeholder (all ff) +/*0284*/ uint32 race; // Spawn race +/*0288*/ uint32 unknown0288; +/*0292*/ char lastName[32]; // Player's Lastname +/*0324*/ float walkspeed; // Speed when walking +/*0328*/ uint8 unknown0328; +/*0329*/ uint8 is_pet; // 0=no, 1=yes +/*0330*/ uint8 light; // Spawn's lightsource %%% wrong +/*0331*/ uint8 class_; // Player's class +/*0332*/ uint8 eyecolor2; // Left eye color +/*0333*/ uint8 flymode; +/*0334*/ uint8 gender; // Gender (0=male, 1=female) +/*0335*/ uint8 bodytype; // Bodytype +/*0336*/ uint8 unknown0336[3]; +union +{ +/*0339*/ uint8 equip_chest2; // Second place in packet for chest texture (usually 0xFF in live packets) + // Not sure why there are 2 of them, but it effects chest texture! +/*0339*/ uint8 mount_color; // drogmor: 0=white, 1=black, 2=green, 3=red + // horse: 0=brown, 1=white, 2=black, 3=tan +}; +/*0340*/ uint32 spawnId; // Spawn Id +/*0344*/ uint8 unknown0344[4]; +/*0348*/ union + { + struct + { + /*0348*/ Color_Struct color_helmet; // Color of helmet item + /*0352*/ Color_Struct color_chest; // Color of chest item + /*0356*/ Color_Struct color_arms; // Color of arms item + /*0360*/ Color_Struct color_bracers; // Color of bracers item + /*0364*/ Color_Struct color_hands; // Color of hands item + /*0368*/ Color_Struct color_legs; // Color of legs item + /*0372*/ Color_Struct color_feet; // Color of feet item + /*0376*/ Color_Struct color_primary; // Color of primary item + /*0380*/ Color_Struct color_secondary; // Color of secondary item + } equipment_colors; + /*0348*/ Color_Struct colors[9]; // Array elements correspond to struct equipment_colors above + }; +/*0384*/ uint8 lfg; // 0=off, 1=lfg on +/*0385*/ + +}; + +/* +** New Spawn +** Length: 176 Bytes +** OpCode: 4921 +*/ +struct NewSpawn_Struct +{ + struct Spawn_Struct spawn; // Spawn Information +}; + +struct ClientZoneEntry_Struct { +/*0000*/ uint32 unknown00; +/*0004*/ char char_name[64]; // Character Name +}; + +/* +** Server Zone Entry Struct +** Length: 452 Bytes +** OPCodes: OP_ServerZoneEntry +** +*/ +struct ServerZoneEntry_Struct +{ + struct NewSpawn_Struct player; +}; + +struct NewZone_Struct { +/*0000*/ char char_name[64]; // Character Name +/*0064*/ char zone_short_name[32]; // Zone Short Name +/*0096*/ char zone_long_name[278]; // Zone Long Name +/*0374*/ uint8 ztype; // Zone type (usually FF) +/*0375*/ uint8 fog_red[4]; // Zone fog (red) +/*0379*/ uint8 fog_green[4]; // Zone fog (green) +/*0383*/ uint8 fog_blue[4]; // Zone fog (blue) +/*0387*/ uint8 unknown323; +/*0388*/ float fog_minclip[4]; +/*0404*/ float fog_maxclip[4]; +/*0420*/ float gravity; +/*0424*/ uint8 time_type; +/*0425*/ uint8 unknown360[49]; +/*0474*/ uint8 sky; // Sky Type +/*0475*/ uint8 unknown331[13]; // ***Placeholder +/*0488*/ float zone_exp_multiplier; // Experience Multiplier +/*0492*/ float safe_y; // Zone Safe Y +/*0496*/ float safe_x; // Zone Safe X +/*0500*/ float safe_z; // Zone Safe Z +/*0504*/ float max_z; // Guessed +/*0508*/ float underworld; // Underworld, min z (Not Sure?) +/*0512*/ float minclip; // Minimum View Distance +/*0516*/ float maxclip; // Maximum View DIstance +/*0520*/ uint8 unknown_end[84]; // ***Placeholder +/*0604*/ char zone_short_name2[68]; +/*0672*/ char unknown672[12]; +/*0684*/ uint16 zone_id; +/*0686*/ uint16 zone_instance; +/*0688*/ uint32 unknown688; +/*0692*/ uint8 unknown692[8]; +/*0700*/ +}; + +/* +** Memorize Spell Struct +** Length: 12 Bytes +** +*/ +struct MemorizeSpell_Struct { +uint32 slot; // Spot in the spell book/memorized slot +uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) +uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming +uint32 unknown12; +}; + +/* +** Make Charmed Pet +** Length: 12 Bytes +** +*/ +struct Charm_Struct { +/*00*/ uint32 owner_id; +/*04*/ uint32 pet_id; +/*08*/ uint32 command; // 1: make pet, 0: release pet +/*12*/ +}; + +struct InterruptCast_Struct +{ + uint32 spawnid; + uint32 messageid; + char message[0]; +}; + +struct DeleteSpell_Struct +{ +/*000*/int16 spell_slot; +/*002*/uint8 unknowndss002[2]; +/*004*/uint8 success; +/*005*/uint8 unknowndss006[3]; +/*008*/ +}; + +struct ManaChange_Struct +{ + uint32 new_mana; // New Mana AMount + uint32 stamina; + uint32 spell_id; + uint32 unknown12; +}; + +struct SwapSpell_Struct +{ + uint32 from_slot; + uint32 to_slot; + + +}; + +struct BeginCast_Struct +{ + // len = 8 +/*000*/ uint16 caster_id; +/*002*/ uint16 spell_id; +/*004*/ uint32 cast_time; // in miliseconds +}; + +struct CastSpell_Struct +{ + uint32 slot; + uint32 spell_id; + uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast + uint32 target_id; + uint8 cs_unknown[4]; +}; + +/* +** SpawnAppearance_Struct +** Changes client appearance for all other clients in zone +** Size: 8 bytes +** Used in: OP_SpawnAppearance +** +*/ +struct SpawnAppearance_Struct +{ +/*0000*/ uint16 spawn_id; // ID of the spawn +/*0002*/ uint16 type; // Values associated with the type +/*0004*/ uint32 parameter; // Type of data sent +/*0008*/ +}; + + +// solar: this is used inside profile +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 +}; + +struct SpellBuffFade_Struct { +/*000*/ uint32 entityid; +/*004*/ uint8 slot; +/*005*/ uint8 level; +/*006*/ uint8 effect; +/*007*/ uint8 unknown7; +/*008*/ uint32 spellid; +/*012*/ uint32 duration; +/*016*/ uint32 unknown016; +/*020*/ uint32 unknown020; //prolly global player ID +/*024*/ uint32 slotid; +/*028*/ uint32 bufffade; +/*032*/ +}; + +struct ItemNamePacket_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 unkown004; +/*008*/ char name[64]; +/*072*/ +}; + +// Length: 10 +struct ItemProperties_Struct { + +uint8 unknown01[2]; +uint8 charges; +uint8 unknown02[13]; +}; + +struct GMTrainee_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ uint32 skills[73]; + /*300*/ uint8 unknown300[148]; + /*448*/ +}; + +struct GMTrainEnd_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ +}; + +struct GMSkillChange_Struct { +/*000*/ uint16 npcid; +/*002*/ uint8 unknown1[2]; // something like PC_ID, but not really. stays the same thru the session though +/*002*/ uint16 skillbank; // 0 if normal skills, 1 if languages +/*002*/ uint8 unknown2[2]; +/*008*/ uint16 skill_id; +/*010*/ uint8 unknown3[2]; +}; +struct ConsentResponse_Struct { + char grantname[64]; + char ownername[64]; + uint8 permission; + char zonename[32]; +}; + +/* +** Name Generator Struct +** Length: 72 bytes +** OpCode: 0x0290 +*/ +struct NameGeneration_Struct +{ +/*0000*/ uint32 race; +/*0004*/ uint32 gender; +/*0008*/ char name[64]; +/*0072*/ +}; + +/* +** Character Creation struct +** Length: 140 Bytes +** OpCode: 0x0113 +*/ +struct CharCreate_Struct +{ +/*0000*/ uint32 class_; +/*0004*/ uint32 haircolor; // Might be hairstyle +/*0008*/ uint32 beardcolor; // Might be beard +/*0012*/ uint32 beard; // Might be beardcolor +/*0016*/ uint32 gender; +/*0020*/ uint32 race; +/*0024*/ uint32 start_zone; + // 0 = odus + // 1 = qeynos + // 2 = halas + // 3 = rivervale + // 4 = freeport + // 5 = neriak + // 6 = gukta/grobb + // 7 = ogguk + // 8 = kaladim + // 9 = gfay + // 10 = felwithe + // 11 = akanon + // 12 = cabalis + // 13 = shar vahl +/*0028*/ uint32 hairstyle; // Might be haircolor +/*0032*/ uint32 deity; +/*0036*/ uint32 STR; +/*0040*/ uint32 STA; +/*0044*/ uint32 AGI; +/*0048*/ uint32 DEX; +/*0052*/ uint32 WIS; +/*0056*/ uint32 INT; +/*0060*/ uint32 CHA; +/*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 +/*0080*/ +}; + +/* + *Used in PlayerProfile + */ +struct AA_Array +{ + uint32 AA; + uint32 value; +}; + + +static const uint32 MAX_PP_DISCIPLINES = 100; + +struct Disciplines_Struct { + uint32 values[MAX_PP_DISCIPLINES]; +}; + +static const uint32 MAX_PLAYER_TRIBUTES = 5; +static const uint32 MAX_PLAYER_BANDOLIER = 4; +static const uint32 MAX_PLAYER_BANDOLIER_ITEMS = 4; +static const uint32 TRIBUTE_NONE = 0xFFFFFFFF; +struct Tribute_Struct { + uint32 tribute; + uint32 tier; +}; + +//len = 72 +struct BandolierItem_Struct { + uint32 item_id; + uint32 icon; + char item_name[64]; +}; + +//len = 320 +enum { //bandolier item positions + bandolierMainHand = 0, + bandolierOffHand, + bandolierRange, + bandolierAmmo +}; +struct Bandolier_Struct { + char name[32]; + BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; +}; +struct PotionBelt_Struct { + BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; +}; + +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]; +}; +struct RaidLeadershipAA_Struct { + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; +}; + + /** +* A bind point. +* Size: 20 Octets +*/ +struct BindStruct { + /*000*/ uint32 zoneId; + /*004*/ float x; + /*008*/ float y; + /*012*/ float z; + /*016*/ float heading; + /*020*/ +}; + + +/* +** Player Profile +** +** Length: 4308 bytes +** OpCode: 0x006a + */ +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 = 75; +static const uint32 MAX_PP_AA_ARRAY = 240; +static const uint32 MAX_GROUP_MEMBERS = 6; +static const uint32 MAX_RECAST_TYPES = 20; +/* +showeq -> eqemu +sed -e 's/_t//g' -e 's/MAX_AA/MAX_PP_AA_ARRAY/g' \ + -e 's/MAX_SPELL_SLOTS/MAX_PP_MEMSPELL/g' \ + -e 's/MAX_KNOWN_SKILLS/MAX_PP_SKILL/g' \ + -e 's/MAXRIBUTES/MAX_PLAYER_TRIBUTES/g' \ + -e 's/MAX_BUFFS/BUFF_COUNT/g' \ + -e 's/MAX_KNOWN_LANGS/MAX_PP_LANGUAGE/g' \ + -e 's/MAX_RECASTYPES/MAX_RECAST_TYPES/g' \ + -e 's/spellBuff/SpellBuff_Struct/g' \ + -e 's/lastName/last_name/g' \ + -e 's/guildID/guild_id/g' \ + -e 's/itemint/item_tint/g' \ + -e 's/MANA/mana/g' \ + -e 's/curHp/cur_hp/g' \ + -e 's/sSpellBook/spell_book/g' \ + -e 's/sMemSpells/mem_spells/g' \ + -e 's/uint32[ \t]*disciplines\[MAX_DISCIPLINES\]/Disciplines_Struct disciplines/g' \ + -e 's/aa_unspent/aapoints/g' \ + -e 's/aa_spent/aapoints_spent/g' \ + -e 's/InlineItem[ \t]*potionBelt\[MAX_POTIONS_IN_BELT\]/PotionBelt_Struct potionbelt/g' \ + -e 's/ldon_guk_points/ldon_points_guk/g' \ + -e 's/ldon_mir_points/ldon_points_mir/g' \ + -e 's/ldon_mmc_points/ldon_points_mmc/g' \ + -e 's/ldon_ruj_points/ldon_points_ruj/g' \ + -e 's/ldonak_points/ldon_points_tak/g' \ + -e 's/ldon_avail_points/ldon_points_available/g' \ + -e 's/tributeTime/tribute_time_remaining/g' \ + -e 's/careerTribute/career_tribute_points/g' \ + -e 's/currentTribute/tribute_points/g' \ + -e 's/tributeActive/tribute_active/g' \ + -e 's/TributeStruct/Tribute_Struct/g' \ + -e 's/expGroupLeadAA/group_leadership_exp/g' \ + -e 's/expRaidLeadAA/raid_leadership_exp/g' \ + -e 's/groupLeadAAUnspent/group_leadership_points/g' \ + -e 's/raidLeadAAUnspent/raid_leadership_points/g' \ + -e 's/uint32[ \t]*leadershipAAs\[MAX_LEAD_AA\]/LeadershipAA_Struct leader_abilities/g' \ + -e 's/BandolierStruct/Bandolier_Struct/g' \ + -e 's/MAX_BANDOLIERS/MAX_PLAYER_BANDOLIER/g' \ + -e 's/birthdayTime/birthday/g' \ + -e 's/lastSaveTime/lastlogin/g' \ + -e 's/zoneId/zone_id/g' \ + -e 's/hunger/hunger_level/g' \ + -e 's/thirst/thirst_level/g' \ + -e 's/guildstatus/guildrank/g' \ + -e 's/airRemaining/air_remaining/g' \ + + + + */ +struct PlayerProfile_Struct +{ +/*00000*/ uint32 checksum; // +/*00004*/ uint32 gender; // Player Gender - 0 Male, 1 Female +/*00008*/ uint32 race; // Player race +/*00012*/ uint32 class_; // Player class +/*00016*/ uint32 unknown00016; // ***Placeholder +/*00020*/ uint8 level; // Level of player +/*00021*/ uint8 level1; // Level of player (again?) +/*00022*/ uint8 unknown00022[2]; // ***Placeholder +/*00024*/ BindStruct binds[5]; // Bind points (primary is first) +/*00124*/ uint32 deity; // deity +/*00128*/ uint32 intoxication; // Alcohol level (in ticks till sober?) +/*00132*/ uint32 spellSlotRefresh[MAX_PP_MEMSPELL]; // Refresh time (millis) +/*00168*/ uint32 abilitySlotRefresh; +/*00172*/ uint8 haircolor; // Player hair color +/*00173*/ uint8 beardcolor; // Player beard color +/*00174*/ uint8 eyecolor1; // Player left eye color +/*00175*/ uint8 eyecolor2; // Player right eye color +/*00176*/ uint8 hairstyle; // Player hair style +/*00177*/ uint8 beard; // Player beard type +/*00178*/ uint8 unknown00178[10]; +/*00188*/ uint32 item_material[9]; // Item texture/material of worn items +/*00224*/ uint8 unknown00224[44]; +/*00268*/ Color_Struct item_tint[9]; // RR GG BB 00 +/*00304*/ AA_Array aa_array[MAX_PP_AA_ARRAY]; // AAs +/*02224*/ uint32 points; // Unspent Practice points +/*02228*/ uint32 mana; // Current mana +/*02232*/ uint32 cur_hp; // Current HP without +HP equipment +/*02236*/ uint32 STR; // Strength +/*02240*/ uint32 STA; // Stamina +/*02244*/ uint32 CHA; // Charisma +/*02248*/ uint32 DEX; // Dexterity +/*02252*/ uint32 INT; // Intelligence +/*02256*/ uint32 AGI; // Agility +/*02260*/ uint32 WIS; // Wisdom +/*02264*/ uint8 face; // Player face +/*02265*/ uint8 unknown02264[47]; +/*02312*/ uint32 spell_book[MAX_PP_SPELLBOOK]; // List of the Spells in spellbook +/*03912*/ uint8 unknown4184[448]; // all 0xff after last spell +/*04360*/ uint32 mem_spells[MAX_PP_MEMSPELL]; // List of spells memorized +/*04396*/ uint8 unknown04396[32]; +/*04428*/ uint32 platinum; // Platinum Pieces on player +/*04432*/ uint32 gold; // Gold Pieces on player +/*04436*/ uint32 silver; // Silver Pieces on player +/*04440*/ uint32 copper; // Copper Pieces on player +/*04444*/ uint32 platinum_cursor; // Platinum Pieces on cursor +/*04448*/ uint32 gold_cursor; // Gold Pieces on cursor +/*04452*/ uint32 silver_cursor; // Silver Pieces on cursor +/*04456*/ uint32 copper_cursor; // Copper Pieces on cursor +/*04460*/ uint32 skills[MAX_PP_SKILL]; // List of skills +/*04760*/ uint8 unknown04760[236]; +/*04996*/ uint32 toxicity; // Potion Toxicity (15=too toxic, each potion adds 3) +/*05000*/ uint32 thirst_level; // Drink (ticks till next drink) +/*05004*/ uint32 hunger_level; // Food (ticks till next eat) +/*05008*/ SpellBuff_Struct buffs[BUFF_COUNT]; // Buffs currently on the player +/*05508*/ Disciplines_Struct disciplines; // Known disciplines +/*05908*/ uint32 recastTimers[MAX_RECAST_TYPES]; // Timers (GMT of last use) +/*05988*/ uint8 unknown05008[160]; +/*06148*/ uint32 endurance; // Current endurance +/*06152*/ uint32 aapoints_spent; // Number of spent AA points +/*06156*/ uint32 aapoints; // Unspent AA points +/*06160*/ uint8 unknown06160[4]; +/*06164*/ Bandolier_Struct bandoliers[MAX_PLAYER_BANDOLIER]; // bandolier contents +/*07444*/ uint8 unknown07444[5120]; +/*12564*/ PotionBelt_Struct potionbelt; // potion belt +/*12852*/ uint8 unknown12852[8]; +/*12860*/ uint32 available_slots; +/*12864*/ uint8 unknown12864[76]; +/*12940*/ char name[64]; // Name of player +/*13004*/ char last_name[32]; // Last name of player +/*13036*/ uint32 guild_id; // guildid +/*13040*/ uint32 birthday; // character birthday +/*13044*/ uint32 lastlogin; // character last save time +/*13048*/ uint32 timePlayedMin; // time character played +/*13052*/ uint8 pvp; // 1=pvp, 0=not pvp +/*13053*/ uint8 anon; // 2=roleplay, 1=anon, 0=not anon +/*13054*/ uint8 gm; // 0=no, 1=yes (guessing!) +/*13055*/ uint8 guildrank; // 0=member, 1=officer, 2=guildleader +/*13056*/ uint32 guildbanker; +/*13060*/ uint8 unknown13054[8]; +/*13068*/ uint32 exp; // Current Experience +/*13072*/ uint8 unknown13072[8]; +/*13080*/ uint32 timeentitledonaccount; // In days, displayed in /played +/*13084*/ uint8 languages[MAX_PP_LANGUAGE]; // List of languages +/*13109*/ uint8 unknown13109[4]; // All 0x00 (language buffer?) +/*13116*/ float x; // Players x position +/*13120*/ float y; // Players y position +/*13124*/ float z; // Players z position +/*13128*/ float heading; // Players heading +/*13132*/ uint8 unknown13132[4]; // ***Placeholder +/*13136*/ uint32 platinum_bank; // Platinum Pieces in Bank +/*13140*/ uint32 gold_bank; // Gold Pieces in Bank +/*13144*/ uint32 silver_bank; // Silver Pieces in Bank +/*13148*/ uint32 copper_bank; // Copper Pieces in Bank +/*13152*/ uint32 platinum_shared; // Shared platinum pieces +/*13156*/ uint8 unknown13156[84]; +/*13240*/ uint32 expansions; // Bitmask for expansions +/*13244*/ uint8 unknown13244[12]; +/*13256*/ uint32 autosplit; // 0 = off, 1 = on +/*13260*/ uint8 unknown13260[16]; +/*13276*/ uint16 zone_id; // see zones.h +/*13278*/ uint16 zoneInstance; // Instance id +/*13280*/ char groupMembers[MAX_GROUP_MEMBERS][64];// all the members in group, including self +/*13664*/ char groupLeader[64]; // Leader of the group ? +/*13728*/ uint8 unknown13728[656]; +/*14384*/ uint32 entityid; +/*14388*/ uint32 leadAAActive; // 0 = leader AA off, 1 = leader AA on +/*14392*/ uint8 unknown14392[4]; +/*14396*/ int32 ldon_points_guk; // Earned GUK points +/*14400*/ int32 ldon_points_mir; // Earned MIR points +/*14404*/ int32 ldon_points_mmc; // Earned MMC points +/*14408*/ int32 ldon_points_ruj; // Earned RUJ points +/*14412*/ int32 ldon_points_tak; // Earned TAK points +/*14416*/ int32 ldon_points_available; // Available LDON points +/*14420*/ uint8 unknown14420[132]; +/*14552*/ uint32 tribute_time_remaining; // Time remaining on tribute (millisecs) +/*14556*/ uint32 career_tribute_points; // Total favor points for this char +/*14560*/ uint32 unknown7208; // *** Placeholder +/*14564*/ uint32 tribute_points; // Current tribute points +/*14568*/ uint32 unknown7216; // *** Placeholder +/*14572*/ uint32 tribute_active; // 0 = off, 1=on +/*14576*/ Tribute_Struct tributes[MAX_PLAYER_TRIBUTES]; // Current tribute loadout +/*14616*/ uint32 unknown14616; +/*14620*/ double group_leadership_exp; +/*14628*/ double raid_leadership_exp; +/*14640*/ uint32 group_leadership_points; // Unspent group lead AA points +/*14644*/ uint32 raid_leadership_points; // Unspent raid lead AA points +/*14644*/ LeadershipAA_Struct leader_abilities; // Leader AA ranks +/*14772*/ uint8 unknown14772[128]; +/*14900*/ uint32 air_remaining; // Air supply (seconds) +/*14904*/ uint32 PVPKills; +/*14908*/ uint32 PVPDeaths; +/*14912*/ uint32 PVPCurrentPoints; +/*14916*/ uint32 PVPCareerPoints; +/*14920*/ uint32 PVPBestKillStreak; +/*14924*/ uint32 PVPWorstDeathStreak; +/*14928*/ uint32 PVPCurrentKillStreak; +/*14932*/ PVPStatsEntry_Struct PVPLastKill; +/*15020*/ PVPStatsEntry_Struct PVPLastDeath; +/*15108*/ uint32 PVPNumberOfKillsInLast24Hours; +/*15112*/ PVPStatsEntry_Struct PVPRecentKills[50]; +/*19512*/ uint32 expAA; // Exp earned in current AA point +/*19516*/ uint8 unknown19516[40]; +/*19556*/ uint32 currentRadCrystals; // Current count of radiant crystals +/*19560*/ uint32 careerRadCrystals; // Total count of radiant crystals ever +/*19564*/ uint32 currentEbonCrystals; // Current count of ebon crystals +/*19568*/ uint32 careerEbonCrystals; // Total count of ebon crystals ever +/*19572*/ uint8 groupAutoconsent; // 0=off, 1=on +/*19573*/ uint8 raidAutoconsent; // 0=off, 1=on +/*19574*/ uint8 guildAutoconsent; // 0=off, 1=on +/*19575*/ uint8 unknown19575; // ***Placeholder (6/29/2005) +/*19576*/ uint32 level3; // Titanium looks here to determine the max leadership points you can bank. +/*19580*/ uint32 showhelm; // 0=no, 1=yes +/*19584*/ uint8 unknown19584[4]; // ***Placeholder (10/27/2005) +/*19588*/ uint32 unknown19588; // *** Placeholder +/*19584*/ +}; + + + + + +/* +** Client Target Struct +** Length: 2 Bytes +** OpCode: 6221 +*/ +struct ClientTarget_Struct { +/*000*/ uint32 new_target; // Target ID +}; + +/* +** Target Rejection Struct +** Length: 12 Bytes +** OpCode: OP_TargetReject +*/ +struct TargetReject_Struct { +/*00*/ uint8 unknown00[12]; +}; + +struct PetCommand_Struct { +/*000*/ uint32 command; +/*004*/ uint32 unknown; +}; + +/* +** Delete Spawn +** Length: 4 Bytes +** OpCode: OP_DeleteSpawn +*/ +struct DeleteSpawn_Struct +{ +/*00*/ uint32 spawn_id; // Spawn ID to delete +/*04*/ +}; + +/* +** Channel Message received or sent +** Length: 144 Bytes + Variable Length + 1 +** OpCode: OP_ChannelMessage +** +*/ +struct ChannelMessage_Struct +{ +/*000*/ char targetname[64]; // Tell recipient +/*064*/ char sender[64]; // The senders name (len might be wrong) +/*128*/ uint32 language; // Language +/*132*/ uint32 chan_num; // Channel +/*136*/ uint32 cm_unknown4[2]; // ***Placeholder +/*144*/ uint32 skill_in_language; // The players skill in this language? might be wrong +/*148*/ char message[0]; // Variable length message +}; + +/* +** Special Message +** Length: 4 Bytes + Variable Text Length + 1 +** OpCode: OP_SpecialMesg +** +*/ +/* + Theres something wrong with this... example live packet: +Server->Client: [ Opcode: OP_SpecialMesg (0x0fab) Size: 244 ] + 0: 01 02 00 0A 00 00 00 09 - 05 00 00 42 61 72 73 74 | ...........Barst + 16: 72 65 20 53 6F 6E 67 77 - 65 61 76 65 72 00 7C F9 | re Songweaver.|. + 32: FF FF 84 FF FF FF 03 00 - 00 00 47 72 65 65 74 69 | ..........Greeti + +*/ +struct SpecialMesg_Struct +{ +/*00*/ char header[3]; // 04 04 00 <-- for #emote style msg +/*03*/ uint32 msg_type; // Color of text (see MT_*** below) +/*07*/ uint32 target_spawn_id; // Who is it being said to? +/*11*/ char sayer[1]; // Who is the source of the info +/*12*/ uint8 unknown12[12]; +/*24*/ char message[1]; // What is being said? +}; + +/* +** When somebody changes what they're wearing +** or give a pet a weapon (model changes) +** Length: 16 Bytes +** Opcode: 9220 +*/ +struct WearChange_Struct{ +/*000*/ uint16 spawn_id; +/*002*/ uint16 material; +/*004*/ Color_Struct color; +/*009*/ uint8 wear_slot_id; +}; + +/* +** Type: Bind Wound Structure +** Length: 8 Bytes +*/ +//Fixed for 7-14-04 patch +struct BindWound_Struct +{ +/*002*/ uint16 to; // TargetID +/*004*/ uint16 unknown2; // ***Placeholder +/*006*/ uint16 type; +/*008*/ uint16 unknown6; +}; + + +/* +** Type: Zone Change Request (before hand) +** Length: 88 bytes +** OpCode: a320 +*/ + +struct ZoneChange_Struct { +/*000*/ char char_name[64]; // Character Name +/*064*/ uint16 zoneID; +/*066*/ uint16 instanceID; +/*068*/ float y; +/*072*/ float x; +/*076*/ float z; +/*080*/ uint32 zone_reason; //0x0A == death, I think +/*084*/ int32 success; // =0 client->server, =1 server->client, -X=specific error +/*088*/ +}; + +struct RequestClientZoneChange_Struct { +/*00*/ uint16 zone_id; +/*02*/ uint16 instance_id; +/*04*/ float y; +/*08*/ float x; +/*12*/ float z; +/*16*/ float heading; +/*20*/ uint32 type; //unknown... values +}; + +struct Animation_Struct { +/*00*/ uint16 spawnid; +/*02*/ uint8 action; +/*03*/ uint8 value; +/*04*/ +}; + +// solar: this is what causes the caster to animate and the target to +// get the particle effects around them when a spell is cast +// also causes a buff icon +struct Action_Struct +{ + /* 00 */ uint16 target; // id of target + /* 02 */ uint16 source; // id of caster + /* 04 */ uint16 level; // level of caster + /* 06 */ uint16 instrument_mod; + /* 08 */ uint32 unknown08; + /* 12 */ uint16 unknown16; +// some kind of sequence that's the same in both actions +// as well as the combat damage, to tie em together? + /* 14 */ uint32 sequence; + /* 18 */ uint32 unknown18; + /* 22 */ uint8 type; // 231 (0xE7) for spells + /* 23 */ uint32 unknown23; + /* 27 */ uint16 spell; // spell id being cast + /* 29 */ uint8 unknown29; +// this field seems to be some sort of success flag, if it's 4 + /* 30 */ uint8 buff_unknown; // if this is 4, a buff icon is made + /* 31 */ +}; + +// solar: this is what prints the You have been struck. and the regular +// melee messages like You try to pierce, etc. It's basically the melee +// and spell damage message +struct CombatDamage_Struct +{ +/* 00 */ uint16 target; +/* 02 */ uint16 source; +/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells +/* 05 */ uint16 spellid; +/* 07 */ uint32 damage; +/* 11 */ uint32 unknown11; +/* 15 */ uint32 sequence; // see above notes in Action_Struct +/* 19 */ uint32 unknown19; +/* 23 */ +}; + +/* +** Consider Struct +** Length: 24 Bytes +** OpCode: 3721 +*/ +struct Consider_Struct{ +/*000*/ uint32 playerid; // PlayerID +/*004*/ uint32 targetid; // TargetID +/*008*/ uint32 faction; // Faction +/*0012*/ uint32 level; // Level +/*016*/ int32 cur_hp; // Current Hitpoints +/*020*/ int32 max_hp; // Maximum Hitpoints +/*024*/ uint8 pvpcon; // Pvp con flag 0/1 +/*025*/ uint8 unknown3[3]; +}; + +/* +** Spawn Death Blow +** Length: 32 Bytes +** OpCode: 0114 +*/ +struct Death_Struct +{ +/*000*/ uint32 spawn_id; +/*004*/ uint32 killer_id; +/*008*/ uint32 corpseid; // was corpseid +/*012*/ uint32 attack_skill; // was type +/*016*/ uint32 spell_id; +/*020*/ uint32 bindzoneid; //bindzoneid? +/*024*/ uint32 damage; +/*028*/ uint32 unknown028; +}; + +struct BecomeCorpse_Struct { + uint32 spawn_id; + float y; + float x; + float z; +}; + +/* +** Spawn position update +** Struct sent from server->client to update position of +** another spawn's position update in zone (whether NPC or PC) +** +*/ +struct PlayerPositionUpdateServer_Struct +{ +/*0000*/ uint16 spawn_id; +/*0002*/ int32 delta_heading:10, // change in heading + x_pos:19, // x coord + padding0002:3; // ***Placeholder +/*0006*/ int32 y_pos:19, // y coord + animation:10, // animation + padding0006:3; // ***Placeholder +/*0010*/ int32 z_pos:19, // z coord + delta_y:13; // change in y +/*0014*/ int32 delta_x:13, // change in x + heading:12, // heading + padding0014:7; // ***Placeholder +/*0018*/ int32 delta_z:13, // change in z + padding0018:19; // ***Placeholder +/*0022*/ +}; + +/* +** Player position update +** Struct sent from client->server to update +** player position on server +** +*/ +struct PlayerPositionUpdateClient_Struct +{ +/*0000*/ uint16 spawn_id; +/*0022*/ uint16 sequence; //increments one each packet +/*0004*/ float y_pos; // y coord +/*0008*/ float delta_z; // Change in z +/*0016*/ float delta_x; // Change in x +/*0012*/ float delta_y; // Change in y +/*0020*/ int32 animation:10, // animation + delta_heading:10, // change in heading + padding0020:12; // ***Placeholder (mostly 1) +/*0024*/ float x_pos; // x coord +/*0028*/ float z_pos; // z coord +/*0034*/ uint16 heading:12, // Directional heading + padding0004:4; // ***Placeholder +/*0032*/ uint8 unknown0006[2]; // ***Placeholder +/*0036*/ +}; + +/* +** Spawn HP Update +** Length: 10 Bytes +** OpCode: OP_HPUpdate +*/ +struct SpawnHPUpdate_Struct +{ +/*00*/ uint32 cur_hp; // Id of spawn to update +/*04*/ int32 max_hp; // Maximum hp of spawn +/*08*/ int16 spawn_id; // Current hp of spawn +/*10*/ +}; +struct SpawnHPUpdate_Struct2 +{ +/*01*/ int16 spawn_id; +/*00*/ uint8 hp; +}; +/* +** Stamina +** Length: 8 Bytes +** OpCode: 5721 +*/ +struct Stamina_Struct { +/*00*/ uint32 food; // (low more hungry 127-0) +/*02*/ uint32 water; // (low more thirsty 127-0) +}; + +/* +** Level Update +** Length: 12 Bytes +*/ +struct LevelUpdate_Struct +{ +/*00*/ uint32 level; // New level +/*04*/ uint32 level_old; // Old level +/*08*/ uint32 exp; // Current Experience +}; + +/* +** Experience Update +** Length: 14 Bytes +** OpCode: 9921 +*/ +struct ExpUpdate_Struct +{ +/*0000*/ uint32 exp; // Current experience ratio from 0 to 330 +/*0004*/ uint32 aaxp; // @BP ?? +}; + +/* +** Item Packet Struct - Works on a variety of opcodes +** Packet Types: See ItemPacketType enum +** +*/ +enum ItemPacketType +{ + ItemPacketViewLink = 0x00, + ItemPacketTradeView = 0x65, + ItemPacketLoot = 0x66, + ItemPacketTrade = 0x67, + ItemPacketCharInventory = 0x69, + ItemPacketSummonItem = 0x6A, + ItemPacketTributeItem = 0x6C, + ItemPacketMerchant = 0x64, + ItemPacketWorldContainer = 0x6B +}; +struct ItemPacket_Struct +{ +/*00*/ ItemPacketType PacketType; +/*04*/ char SerializedItem[1]; +/*xx*/ +}; + +struct BulkItemPacket_Struct +{ +/*00*/ char SerializedItem[0]; +/*xx*/ +}; + +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]; +}; + + +struct MoveItem_Struct +{ +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +}; + +// +// from_slot/to_slot +// -1 - destroy +// 0 - cursor +// 1 - inventory +// 2 - bank +// 3 - trade +// 4 - shared bank +// +// cointype +// 0 - copeer +// 1 - silver +// 2 - gold +// 3 - platinum +// +static const uint32 COINTYPE_PP = 3; +static const uint32 COINTYPE_GP = 2; +static const uint32 COINTYPE_SP = 1; +static const uint32 COINTYPE_CP = 0; + +struct MoveCoin_Struct +{ + int32 from_slot; + int32 to_slot; + int32 cointype1; + int32 cointype2; + int32 amount; +}; +struct TradeCoin_Struct{ + uint32 trader; + uint8 slot; + uint16 unknown5; + uint8 unknown7; + uint32 amount; +}; +struct TradeMoneyUpdate_Struct{ + uint32 trader; + uint32 type; + uint32 amount; +}; +/* +** Surname struct +** Size: 100 bytes +*/ +struct Surname_Struct +{ +/*0000*/ char name[64]; +/*0064*/ uint32 unknown0064; +/*0068*/ char lastname[32]; +/*0100*/ +}; + +struct GuildsListEntry_Struct { + char name[64]; +}; + +static const uint32 MAX_NUMBER_GUILDS = 1500; +struct GuildsList_Struct { + uint8 head[64]; // First on guild list seems to be empty... + GuildsListEntry_Struct Guilds[MAX_NUMBER_GUILDS]; +}; + +struct GuildUpdate_Struct { + uint32 guildID; + GuildsListEntry_Struct entry; +}; + +/* +** Money Loot +** Length: 22 Bytes +** OpCode: 5020 +*/ +struct moneyOnCorpseStruct { +/*0000*/ uint8 response; // 0 = someone else is, 1 = OK, 2 = not at this time +/*0001*/ uint8 unknown1; // = 0x5a +/*0002*/ uint8 unknown2; // = 0x40 +/*0003*/ uint8 unknown3; // = 0 +/*0004*/ uint32 platinum; // Platinum Pieces +/*0008*/ uint32 gold; // Gold Pieces + +/*0012*/ uint32 silver; // Silver Pieces +/*0016*/ uint32 copper; // Copper Pieces +}; + +//opcode = 0x5220 +// size 292 + + +struct LootingItem_Struct { +/*000*/ uint32 lootee; +/*002*/ uint32 looter; +/*004*/ uint16 slot_id; +/*006*/ uint8 unknown3[2]; +/*008*/ uint32 auto_loot; +}; + +struct GuildManageStatus_Struct{ + uint32 guildid; + uint32 oldrank; + uint32 newrank; + char name[64]; +}; +// Guild invite, remove +struct GuildJoin_Struct{ +/*000*/ uint32 guildid; +/*004*/ uint32 unknown04; +/*008*/ uint32 level; +/*012*/ uint32 class_; +/*016*/ uint32 rank;//0 member, 1 officer, 2 leader +/*020*/ uint32 zoneid; +/*024*/ uint32 unknown24; +/*028*/ char name[64]; +/*092*/ +}; +struct GuildInviteAccept_Struct { + char inviter[64]; + char newmember[64]; + uint32 response; + uint32 guildeqid; +}; +struct GuildManageRemove_Struct { + uint32 guildeqid; + char member[64]; +}; +struct GuildCommand_Struct { + char othername[64]; + char myname[64]; + uint16 guildeqid; + uint8 unknown[2]; // for guildinvite all 0's, for remove 0=0x56, 2=0x02 + uint32 officer; +}; + +// 4244 bytes. Is not really an 'OnLevelMessage', it causes a popup box to display in the client +// Text looks like HTML. +struct OnLevelMessage_Struct { +/*0000*/ char Title[128]; +/*0128*/ char Text[4096]; +/*4224*/ uint32 Buttons; +/*4228*/ uint32 Duration; +/*4232*/ uint32 PopupID; +/*4236*/ uint32 unknown4236; +/*4240*/ uint32 unknown4240; +/*4244*/ +}; + +// Opcode OP_GMZoneRequest +// Size = 88 bytes +struct GMZoneRequest_Struct { +/*0000*/ char charname[64]; +/*0064*/ uint32 zone_id; +/*0068*/ float x; +/*0072*/ float y; +/*0076*/ float z; +/*0080*/ char unknown0080[4]; +/*0084*/ uint32 success; // 0 if command failed, 1 if succeeded? +/*0088*/ +// /*072*/ int8 success; // =0 client->server, =1 server->client, -X=specific error +// /*073*/ uint8 unknown0073[3]; // =0 ok, =ffffff error +}; + +struct GMSummon_Struct { +/* 0*/ char charname[64]; +/* 30*/ char gmname[64]; +/* 60*/ uint32 success; +/* 61*/ uint32 zoneID; +/*92*/ int32 y; +/*96*/ int32 x; +/*100*/ int32 z; +/*104*/ uint32 unknown2; // E0 E0 56 00 +}; + +struct GMGoto_Struct { // x,y is swapped as compared to summon and makes sense as own packet +/* 0*/ char charname[64]; + +/* 64*/ char gmname[64]; +/* 128*/ uint32 success; +/* 132*/ uint32 zoneID; + +/*136*/ int32 y; +/*140*/ int32 x; +/*144*/ int32 z; +/*148*/ uint32 unknown2; // E0 E0 56 00 +}; + +struct GMLastName_Struct { + char name[64]; + char gmname[64]; + char lastname[64]; + uint16 unknown[4]; // 0x00, 0x00 + // 0x01, 0x00 = Update the clients +}; + +//Combat Abilities +struct CombatAbility_Struct { + uint32 m_target; //the ID of the target mob + uint32 m_atk; + 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; + uint8 ia_unknown; + uint8 ib_unknown; + uint8 ic_unknown; + uint8 i_atk; + + uint8 id_unknown; + uint8 ie_unknown; + uint8 if_unknown; + uint8 i_type; + uint8 ig_unknown; + uint8 ih_unknown; + uint8 ii_unknown; +}; + +struct GiveItem_Struct { + uint16 to_entity; + int16 to_equipSlot; + uint16 from_entity; + int16 from_equipSlot; +}; + +struct RandomReq_Struct { + uint32 low; + uint32 high; +}; + +/* solar: 9/23/03 reply to /random command; struct from Zaphod */ +struct RandomReply_Struct { +/* 00 */ uint32 low; +/* 04 */ uint32 high; +/* 08 */ uint32 result; +/* 12 */ char name[64]; +/* 76 */ +}; + +struct LFG_Struct { +/* +Wrong size on OP_LFG. Got: 80, Expected: 68 + 0: 00 00 00 00 01 00 00 00 - 00 00 00 00 64 00 00 00 | ............d... + 16: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 48: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 64: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ +Wrong size on OP_LFG. Got: 80, Expected: 68 + 0: 00 00 00 00 01 00 00 00 - 3F 00 00 00 41 00 00 00 | ........?...A... + 16: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 48: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 64: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ +Wrong size on OP_LFG. Got: 80, Expected: 68 + 0: 00 00 00 00 01 00 00 00 - 3F 00 00 00 41 00 00 00 | ........?...A... + 16: 46 72 75 62 20 66 72 75 - 62 20 66 72 75 62 00 00 | Frub frub frub.. + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 48: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 64: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ +*/ +/*000*/ uint32 unknown000; +/*004*/ uint32 value; // 0x00 = off 0x01 = on +/*008*/ uint32 unknown008; +/*012*/ uint32 unknown012; +/*016*/ char name[64]; +}; + +/* +** LFG_Appearance_Struct +** Packet sent to clients to notify when someone in zone toggles LFG flag +** Size: 8 bytes +** Used in: OP_LFGAppearance +** +*/ +struct LFG_Appearance_Struct +{ +/*0000*/ uint32 spawn_id; // ID of the client +/*0004*/ uint8 lfg; // 1=LFG, 0=Not LFG +/*0005*/ char unknown0005[3]; // +/*0008*/ +}; + + +// EverQuest Time Information: +// 72 minutes per EQ Day +// 3 minutes per EQ Hour +// 6 seconds per EQ Tick (2 minutes EQ Time) +// 3 seconds per EQ Minute + +struct TimeOfDay_Struct { + uint8 hour; + uint8 minute; + uint8 day; + uint8 month; + uint32 year; +}; + +// Darvik: shopkeeper structs +struct Merchant_Click_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; +/*008*/ uint32 command; //1=open, 0=cancel/close +/*012*/ float rate; //cost multiplier, dosent work anymore +}; +/* +Unknowns: +0 is e7 from 01 to // MAYBE SLOT IN PURCHASE +1 is 03 +2 is 00 +3 is 00 +4 is ?? +5 is ?? +6 is 00 from a0 to +7 is 00 from 3f to */ +/* +0 is F6 to 01 +1 is CE CE +4A 4A +00 00 +00 E0 +00 CB +00 90 +00 3F +*/ + + + +struct Merchant_Sell_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; // Player's entity id +/*008*/ uint32 itemslot; + uint32 unknown12; +/*016*/ uint8 quantity; // Already sold +/*017*/ uint8 Unknown016[3]; +/*020*/ uint32 price; +}; +struct Merchant_Purchase_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 itemslot; // Player's entity id +/*008*/ uint32 quantity; +/*012*/ uint32 price; +}; +struct Merchant_DelItem_Struct{ +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; // Player's entity id +/*008*/ uint32 itemslot; +}; +struct Adventure_Purchase_Struct { +/*000*/ uint32 some_flag; //set to 1 generally... +/*000*/ uint32 npcid; +/*004*/ uint32 itemid; +/*008*/ uint32 variable; +}; + +struct AdventurePoints_Update_Struct { +/*000*/ uint32 ldon_available_points; // Total available points +/*004*/ uint8 unkown_apu004[20]; +/*024*/ uint32 ldon_guk_points; // Earned Deepest Guk points +/*028*/ uint32 ldon_mirugal_points; // Earned Mirugal' Mebagerie points +/*032*/ uint32 ldon_mistmoore_points; // Earned Mismoore Catacombs Points +/*036*/ uint32 ldon_rujarkian_points; // Earned Rujarkian Hills points +/*040*/ uint32 ldon_takish_points; // Earned Takish points +/*044*/ uint8 unknown_apu042[216]; +}; + + +struct AdventureFinish_Struct{ + uint32 win_lose;//Cofruben: 00 is a lose,01 is win. + uint32 points; +}; +//OP_AdventureRequest +struct AdventureRequest_Struct{ + uint32 risk;//1 normal,2 hard. + uint32 entity_id; +}; +struct AdventureRequestResponse_Struct{ + uint32 unknown000; + char text[2048]; + uint32 timetoenter; + uint32 timeleft; + uint32 risk; + float x; + float y; + float z; + uint32 showcompass; + uint32 unknown2080; +}; + + + +/*struct Item_Shop_Struct { + uint16 merchantid; + uint8 itemtype; + Item_Struct item; + uint8 iss_unknown001[6]; +};*/ + +struct Illusion_Struct { +/*000*/ uint32 spawnid; +/*004*/ char charname[64]; +/*068*/ uint16 race; +/*070*/ char unknown070[2]; +/*072*/ uint8 gender; +/*073*/ uint8 texture; +/*074*/ uint8 helmtexture; +/*075*/ uint8 unknown075; +/*076*/ uint32 face; +/*080*/ uint8 hairstyle; +/*081*/ uint8 haircolor; +/*082*/ uint8 beard; +/*083*/ uint8 beardcolor; +/*084*/ float size; +/*088*/ char unknown084[80]; +/*168*/ +}; + +struct ZonePoint_Entry { +/*0000*/ uint32 iterator; +/*0004*/ float y; +/*0008*/ float x; +/*0012*/ float z; +/*0016*/ float heading; +/*0020*/ uint16 zoneid; +/*0022*/ uint16 zoneinstance; // LDoN instance +}; + +struct ZonePoints { +/*0000*/ uint32 count; +/*0004*/ struct ZonePoint_Entry zpe[0]; // Always add one extra to the end after all zonepoints +}; + +struct SkillUpdate_Struct { +/*00*/ uint32 skillId; +/*04*/ uint32 value; +/*08*/ +}; + +struct ZoneUnavail_Struct { + //This actually varies, but... + char zonename[16]; + int16 unknown[4]; +}; + +struct GroupGeneric_Struct { + char name1[64]; + char name2[64]; +}; + +struct GroupCancel_Struct { + char name1[64]; + char name2[64]; + uint8 toggle; +}; + +struct GroupUpdate_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +}; + +struct GroupUpdate2_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +/*0452*/ GroupLeadershipAA_Struct leader_aas; +/*0580*/ uint8 unknown[188]; +/*0768*/ +}; +struct GroupJoin_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[64]; +/*0132*/ uint8 unknown[84]; +}; + +struct FaceChange_Struct { +/*000*/ uint8 haircolor; +/*001*/ uint8 beardcolor; +/*002*/ uint8 eyecolor1; // the eyecolors always seem to be the same, maybe left and right eye? +/*003*/ uint8 eyecolor2; +/*004*/ uint8 hairstyle; +/*005*/ uint8 beard; // vesuvias +/*006*/ uint8 face; +//vesuvias: +//there are only 10 faces for barbs changing woad just +//increase the face value by ten so if there were 8 woad +//designs then there would be 80 barb faces +}; + +/* +** Trade request from one client to another +** Used to initiate a trade +** Size: 8 bytes +** Used in: OP_TradeRequest +*/ +struct TradeRequest_Struct { +/*00*/ uint32 to_mob_id; +/*04*/ uint32 from_mob_id; +/*08*/ +}; + +struct TradeAccept_Struct { +/*00*/ uint32 from_mob_id; +/*04*/ uint32 unknown4; //seems to be garbage +/*08*/ +}; + +/* +** Cancel Trade struct +** Sent when a player cancels a trade +** Size: 8 bytes +** Used In: OP_CancelTrade +** +*/ +struct CancelTrade_Struct { +/*00*/ uint32 fromid; +/*04*/ uint32 action; +/*08*/ +}; + +struct PetitionUpdate_Struct { + uint32 petnumber; // Petition Number + uint32 color; // 0x00 = green, 0x01 = yellow, 0x02 = red + uint32 status; + time_t senttime; // 4 has to be 0x1F + char accountid[32]; + char gmsenttoo[64]; + int32 quetotal; + char charname[64]; +}; + +struct Petition_Struct { + uint32 petnumber; + uint32 urgency; + char accountid[32]; + char lastgm[32]; + uint32 zone; + //char zone[32]; + char charname[64]; + uint32 charlevel; + uint32 charclass; + uint32 charrace; + uint32 unknown; + //time_t senttime; // Time? + uint32 checkouts; + uint32 unavail; + //uint8 unknown5[4]; + time_t senttime; + uint32 unknown2; + char petitiontext[1024]; + char gmtext[1024]; +}; + + +struct Who_All_Struct { // 76 length total +/*000*/ char whom[64]; +/*064*/ uint32 wrace; // FF FF = no race + +/*066*/ uint32 wclass; // FF FF = no class +/*068*/ uint32 lvllow; // FF FF = no numbers +/*070*/ uint32 lvlhigh; // FF FF = no numbers +/*072*/ uint32 gmlookup; // FF FF = not doing /who all gm +/*074*/ uint32 unknown074; +/*076*/ uint8 unknown076[64]; +/*140*/ +}; + +struct Stun_Struct { // 4 bytes total + uint32 duration; // Duration of stun +}; + +struct AugmentItem_Struct { +/*00*/ int16 container_slot; +/*02*/ char unknown02[2]; +/*04*/ int32 augment_slot; +/*08*/ +}; + +// OP_Emote +struct Emote_Struct { +/*0000*/ uint32 unknown01; +/*0004*/ char message[1024]; +/*1028*/ +}; + +// Inspect +struct Inspect_Struct { + uint32 TargetID; + uint32 PlayerID; +}; +//OP_InspectAnswer +struct InspectResponse_Struct{//Cofruben:need to send two of this for the inspect response. +/*000*/ uint32 TargetID; +/*004*/ uint32 playerid; +/*008*/ char itemnames[22][64]; +/*1416*/uint32 itemicons[22]; +/*1504*/char text[288]; +/*1792*/ +}; + +//OP_SetDataRate +struct SetDataRate_Struct { + float newdatarate; +}; + +//OP_SetServerFilter +struct SetServerFilter_Struct { + uint32 filters[29]; //see enum eqFilterType +}; + +//Op_SetServerFilterAck +struct SetServerFilterAck_Struct { + uint8 blank[8]; +}; +struct IncreaseStat_Struct{ + /*0000*/ uint8 unknown0; + /*0001*/ uint8 str; + /*0002*/ uint8 sta; + /*0003*/ uint8 agi; + /*0004*/ uint8 dex; + /*0005*/ uint8 int_; + /*0006*/ uint8 wis; + /*0007*/ uint8 cha; + /*0008*/ uint8 fire; + /*0009*/ uint8 cold; + /*0010*/ uint8 magic; + /*0011*/ uint8 poison; + /*0012*/ uint8 disease; + /*0013*/ char unknown13[116]; + /*0129*/ uint8 str2; + /*0130*/ uint8 sta2; + /*0131*/ uint8 agi2; + /*0132*/ uint8 dex2; + /*0133*/ uint8 int_2; + /*0134*/ uint8 wis2; + /*0135*/ uint8 cha2; + /*0136*/ uint8 fire2; + /*0137*/ uint8 cold2; + /*0138*/ uint8 magic2; + /*0139*/ uint8 poison2; + /*0140*/ uint8 disease2; +}; + +struct GMName_Struct { + char oldname[64]; + char gmname[64]; + char newname[64]; + uint8 badname; + uint8 unknown[3]; +}; + +struct GMDelCorpse_Struct { + char corpsename[64]; + char gmname[64]; + uint8 unknown; +}; + +struct GMKick_Struct { + char name[64]; + char gmname[64]; + uint8 unknown; +}; + + +struct GMKill_Struct { + char name[64]; + char gmname[64]; + uint8 unknown; +}; + + +struct GMEmoteZone_Struct { + char text[512]; +}; + +// This is where the Text is sent to the client. +// Use ` as a newline character in the text. +// Variable length. +struct BookText_Struct { + uint8 window; // where to display the text (0xFF means new window) + uint8 type; //type: 0=scroll, 1=book, 2=item info.. prolly others. + char booktext[1]; // Variable Length +}; +// This is the request to read a book. +// This is just a "text file" on the server +// or in our case, the 'name' column in our books table. +struct BookRequest_Struct { + uint8 window; // where to display the text (0xFF means new window) + uint8 type; //type: 0=scroll, 1=book, 2=item info.. prolly others. + char txtfile[1]; // Variable length +}; + +/* +** Object/Ground Spawn struct +** Used for Forges, Ovens, ground spawns, items dropped to ground, etc +** Size: 92 bytes +** OpCodes: OP_CreateObject +** Last Updated: Oct-17-2003 +** +*/ +struct Object_Struct { +/*00*/ uint32 linked_list_addr[2];// They are, get this, prev and next, ala linked list +/*08*/ uint16 unknown008[2]; // +/*12*/ uint32 drop_id; // Unique object id for zone +/*16*/ uint16 zone_id; // Redudant, but: Zone the object appears in +/*18*/ uint16 zone_instance; // +/*20*/ uint32 unknown020; // +/*24*/ uint32 unknown024; // +/*28*/ float heading; // heading +/*32*/ float z; // z coord +/*36*/ float x; // x coord +/*40*/ float y; // y coord +/*44*/ char object_name[32]; // Name of object, usually something like IT63_ACTORDEF +/*76*/ uint32 unknown076; // +// ShowEQ shows an extra field in here... +/*80*/ uint32 object_type; // Type of object, not directly translated to OP_OpenObject +/*84*/ uint32 unknown084; //set to 0xFF +/*88*/ uint32 spawn_id; // Spawn Id of client interacting with object +/*92*/ +}; +// 01 = generic drop, 02 = armor, 19 = weapon +//[13:40] and 0xff seems to be indicative of the tradeskill/openable items that end up returning the old style item type in the OP_OpenObject + +/* +** Click Object Struct +** Client clicking on zone object (forge, groundspawn, etc) +** Size: 8 bytes +** Last Updated: Oct-17-2003 +** +*/ +struct ClickObject_Struct { +/*00*/ uint32 drop_id; +/*04*/ uint32 player_id; +/*08*/ +}; + +struct Shielding_Struct { + uint32 target_id; +}; + +/* +** Click Object Acknowledgement Struct +** Response to client clicking on a World Container (ie, forge) +** +*/ +struct ClickObjectAck_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 type; // See object.h, "Object Types" +/*16*/ uint32 unknown16; // +/*20*/ uint32 icon; // Icon to display for tradeskill containers +/*24*/ uint32 unknown24; // +/*28*/ char object_name[64]; // Object name to display +/*92*/ +}; + +/* +** This is different now, mostly unknown +** +*/ +struct CloseContainer_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 unknown12[12]; +}; + +/* +** Generic Door Struct +** Length: 52 Octets +** Used in: +** cDoorSpawnsStruct(f721) +** +*/ +struct Door_Struct +{ +/*0000*/ char name[32]; // Filename of Door // Was 10char long before... added the 6 in the next unknown to it: Daeken M. BlackBlade +/*0032*/ float yPos; // y loc +/*0036*/ float xPos; // x loc +/*0040*/ float zPos; // z loc +/*0044*/ float heading; +/*0048*/ uint32 incline; // rotates the whole door +/*0052*/ uint16 size; // 100 is normal, smaller number = smaller model +/*0054*/ uint8 unknown0038[6]; +/*0060*/ uint8 doorId; // door's id # +/*0061*/ uint8 opentype; +/* + * Open types: + * 66 = PORT1414 (Qeynos) + * 55 = BBBOARD (Qeynos) + * 100 = QEYLAMP (Qeynos) + * 56 = CHEST1 (Qeynos) + * 5 = DOOR1 (Qeynos) + */ +/*0062*/ uint8 state_at_spawn; +/*0063*/ uint8 invert_state; // if this is 1, the door is normally open +/*0064*/ uint32 door_param; +/*0068*/ uint8 unknown0052[12]; // mostly 0s, the last 3 bytes are something tho +/*0080*/ +}; + + + +struct DoorSpawns_Struct { + struct Door_Struct doors[0]; +}; + +/* + OP Code: Op_ClickDoor + Size: 16 +*/ +struct ClickDoor_Struct { +/*000*/ uint8 doorid; +/*001*/ uint8 unknown001; // This may be some type of action setting +/*002*/ uint8 unknown002; // This is sometimes set after a lever is closed +/*003*/ uint8 unknown003; // Seen 0 +/*004*/ uint8 picklockskill; +/*005*/ uint8 unknown005[3]; +/*008*/ uint32 item_id; +/*012*/ uint16 player_id; +/*014*/ uint8 unknown014[2]; +/*016*/ +}; + +struct MoveDoor_Struct { + uint8 doorid; + uint8 action; +}; + + +struct BecomeNPC_Struct { + uint32 id; + int32 maxlevel; +}; + +struct Underworld_Struct { + float speed; + float y; + float x; + float z; +}; + +struct Resurrect_Struct { + uint32 unknown00; + uint16 zone_id; + uint16 instance_id; + float y; + float x; + float z; + char your_name[64]; + uint32 unknown88; + char rezzer_name[64]; + uint32 spellid; + char corpse_name[64]; + uint32 action; +/* 228 */ +}; + +struct SetRunMode_Struct { + uint8 mode; + uint8 unknown[3]; +}; + +//EnvDamage is EnvDamage2 without a few bytes at the end. + +struct EnvDamage2_Struct { +/*0000*/ uint32 id; +/*0004*/ uint16 unknown4; +/*0006*/ uint32 damage; +/*0010*/ uint8 unknown10[12]; +/*0022*/ uint8 dmgtype; //FA = Lava; FC = Falling +/*0023*/ uint8 unknown2[4]; +/*0027*/ uint16 constant; //Always FFFF +/*0029*/ uint16 unknown29; +}; + +//Bazaar Stuff =D + +struct BazaarWindowStart_Struct { + uint8 Action; + uint8 Unknown001; + uint16 Unknown002; +}; + + +struct BazaarWelcome_Struct { + BazaarWindowStart_Struct beginning; + uint32 traders; + uint32 items; + uint8 unknown1[8]; +}; + +struct BazaarSearch_Struct { + BazaarWindowStart_Struct beginning; + uint32 traderid; + uint32 class_; + uint32 race; + uint32 stat; + uint32 slot; + uint32 type; + char name[64]; + uint32 minprice; + uint32 maxprice; + uint32 minlevel; + uint32 maxlevel; +}; +struct BazaarInspect_Struct{ + uint32 item_id; + uint32 unknown; + char name[64]; +}; +struct BazaarReturnDone_Struct{ + uint32 type; + uint32 traderid; + uint32 unknown8; + uint32 unknown12; + uint32 unknown16; +}; +struct BazaarSearchResults_Struct { + BazaarWindowStart_Struct Beginning; + uint32 SellerID; + uint32 NumItems; // Don't know. Don't know the significance of this field. + uint32 SerialNumber; + uint32 Unknown016; + uint32 Unknown020; // Something to do with stats as well + char ItemName[64]; + uint32 Cost; + uint32 ItemStat; +}; + +struct ServerSideFilters_Struct { +uint8 clientattackfilters; // 0) No, 1) All (players) but self, 2) All (players) but group +uint8 npcattackfilters; // 0) No, 1) Ignore NPC misses (all), 2) Ignore NPC Misses + Attacks (all but self), 3) Ignores NPC Misses + Attacks (all but group) +uint8 clientcastfilters; // 0) No, 1) Ignore PC Casts (all), 2) Ignore PC Casts (not directed towards self) +uint8 npccastfilters; // 0) No, 1) Ignore NPC Casts (all), 2) Ignore NPC Casts (not directed towards self) +}; + +/* +** Client requesting item statistics +** Size: 44 bytes +** Used In: OP_ItemLinkClick +** Last Updated: 2/15/2009 +** +*/ +struct ItemViewRequest_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 augments[5]; +/*024*/ uint32 link_hash; +/*028*/ char unknown028[16]; +}; + +/* + * Client to server packet + */ +struct PickPocket_Struct { +// Size 18 + uint32 to; + uint32 from; + uint16 myskill; + uint8 type; // -1 you are being picked, 0 failed , 1 = plat, 2 = gold, 3 = silver, 4 = copper, 5 = item + uint8 unknown1; // 0 for response, unknown for input + uint32 coin; + uint8 lastsix[2]; +}; +/* + * Server to client packet + */ + +struct sPickPocket_Struct { + // Size 28 = coin/fail + uint32 to; + uint32 from; + uint32 myskill; + uint32 type; + uint32 coin; + char itemname[64]; +}; + +struct LogServer_Struct { +// Op_Code OP_LOGSERVER +/*000*/ uint32 unknown000; +/*004*/ uint32 unknown004; +/*008*/ uint32 unknown008; +/*012*/ uint32 unknown012; // htonl(1) on live +/*016*/ uint32 unknown016; // htonl(1) on live +/*020*/ uint8 unknown020[12]; +/*032*/ char worldshortname[32]; +/*064*/ uint8 unknown064[32]; +/*096*/ char unknown096[16]; // 'pacman' on live +/*112*/ char unknown112[16]; // '64.37,148,36' on live +/*126*/ uint8 unknown128[48]; +/*176*/ uint32 unknown176; // htonl(0x00002695) +/*180*/ char unknown180[80]; // 'eqdataexceptions@mail.station.sony.com' on live +/*260*/ uint8 unknown260; // 0x01 on live +/*261*/ uint8 unknown261; // 0x01 on live +/*262*/ uint8 unknown262[2]; +/*264*/ +}; + +struct ApproveWorld_Struct { +// Size 544 +// Op_Code OP_ApproveWorld + uint8 unknown544[544]; +}; + +struct ClientError_Struct +{ +/*00001*/ char type; +/*00001*/ char unknown0001[69]; +/*00069*/ char character_name[64]; +/*00134*/ char unknown134[192]; +/*00133*/ char message[31994]; +/*32136*/ +}; + +struct MobHealth +{ + /*0000*/ uint8 hp; //health percent + /*0001*/ uint16 id;//mobs id +}; + +struct Track_Struct { + uint16 entityid; + uint16 padding002; + float distance; +}; + +struct Tracking_Struct { + Track_Struct Entrys[0]; +}; + +/* +** ZoneServerInfo_Struct +** Zone server information +** Size: 130 bytes +** Used In: OP_ZoneServerInfo +** +*/ +struct ZoneServerInfo_Struct +{ +/*0000*/ char ip[128]; +/*0128*/ uint16 port; +}; + +struct WhoAllPlayer{ + uint32 formatstring; + uint32 pidstring; + char* name; + uint32 rankstring; + char* guild; + uint32 unknown80[2]; + uint32 zonestring; + uint32 zone; + uint32 class_; + uint32 level; + uint32 race; + char* account; + uint32 unknown100; +}; + +struct WhoAllReturnStruct { + uint32 id; + uint32 playerineqstring; + char line[27]; + uint8 unknown35; //0A + uint32 unknown36;//0s + uint32 playersinzonestring; + uint32 unknown44[2]; //0s + uint32 unknown52;//1 + uint32 unknown56;//1 + uint32 playercount;//1 + struct WhoAllPlayer player[0]; +}; + +struct Trader_Struct { + uint32 code; + uint32 itemid[160]; + uint32 unknown; + uint32 itemcost[80]; +}; + +struct ClickTrader_Struct { + uint32 code; + uint32 unknown[161];//damn soe this is totally pointless :/ but at least your finally using memset! Good job :) -LE + uint32 itemcost[80]; +}; + +struct GetItems_Struct{ + uint32 items[80]; +}; + +struct BecomeTrader_Struct{ + uint32 ID; + uint32 Code; +}; + +struct Trader_ShowItems_Struct{ + uint32 code; + uint32 traderid; + uint32 unknown08[3]; +}; + +struct TraderBuy_Struct { +/*000*/ uint32 Action; +/*004*/ uint32 Price; +/*008*/ uint32 TraderID; +/*012*/ char ItemName[64]; +/*076*/ uint32 Unknown076; +/*080*/ uint32 ItemID; +/*084*/ uint32 AlreadySold; +/*088*/ uint32 Quantity; +/*092*/ uint32 Unknown092; +}; + + +struct TraderItemUpdate_Struct{ + uint32 unknown0; + uint32 traderid; + uint8 fromslot; + uint8 toslot; //7? + uint16 charges; +}; + +struct MoneyUpdate_Struct{ + int32 platinum; + int32 gold; + int32 silver; + int32 copper; +}; + +struct TraderDelItem_Struct{ + uint32 slotid; + uint32 quantity; + uint32 unknown; +}; + +struct TraderClick_Struct{ + uint32 traderid; + uint32 unknown4[2]; + uint32 approval; +}; + +struct FormattedMessage_Struct{ + uint32 unknown0; + uint32 string_id; + uint32 type; + char message[0]; +}; +struct SimpleMessage_Struct{ + uint32 string_id; + uint32 color; + uint32 unknown8; +}; + +struct GuildMemberEntry_Struct { + char name[1]; //variable length + uint32 level; //network byte order + uint32 banker; //1=yes, 0=no, network byte order + uint32 class_; //network byte order + uint32 rank; //network byte order + uint32 time_last_on; //network byte order + uint32 tribute_enable; //network byte order + uint32 total_tribute; //total guild tribute donated, network byte order + uint32 last_tribute; //unix timestamp + uint32 unknown_one; //unknown, set to one. (network byte order) + char public_note[1]; //variable length. + uint16 zoneinstance; //network byte order + uint16 zone_id; //network byte order +/* 42 + strings */ +}; + +struct GuildMembers_Struct { //just for display purposes, this is not actually used in the message encoding. + char player_name[1]; //variable length. + uint32 count; //network byte order + GuildMemberEntry_Struct member[0]; +}; + +struct GuildMOTD_Struct{ +/*0000*/ uint32 unknown0; +/*0004*/ char name[64]; +/*0068*/ char setby_name[64]; +/*0132*/ uint32 unknown132; +/*0136*/ char motd[512]; +}; +struct GuildUpdate_PublicNote{ + uint32 unknown0; + char name[64]; + char target[64]; + char note[100]; //we are cutting this off at 100, actually around 252 +}; +struct GuildDemoteStruct{ + char name[64]; + char target[64]; +}; +struct GuildRemoveStruct{ + char target[64]; + char name[64]; + uint32 unknown128; + uint32 leaderstatus; //? +}; +struct GuildMakeLeader{ + char name[64]; + char target[64]; +}; +struct BugStruct{ +/*0000*/ char chartype[64]; +/*0064*/ char name[96]; +/*0160*/ char ui[128]; +/*0288*/ float x; +/*0292*/ float y; +/*0296*/ float z; +/*0300*/ float heading; +/*0304*/ uint32 unknown304; +/*0308*/ uint32 type; +/*0312*/ char unknown312[2144]; +/*2456*/ char bug[1024]; +/*3480*/ char placeholder[2]; +/*3482*/ char system_info[4098]; +}; +struct Make_Pet_Struct { //Simple struct for getting pet info + uint8 level; + uint8 class_; + uint16 race; + uint8 texture; + uint8 pettype; + float size; + uint8 type; + uint32 min_dmg; + uint32 max_dmg; +}; +struct Ground_Spawn{ + float max_x; + float max_y; + float min_x; + float min_y; + float max_z; + float heading; + char name[16]; + uint32 item; + uint32 max_allowed; + uint32 respawntimer; +}; +struct Ground_Spawns { + struct Ground_Spawn spawn[50]; //Assigned max number to allow +}; +struct PetitionBug_Struct{ + uint32 petition_number; + uint32 unknown4; + char accountname[64]; + uint32 zoneid; + char name[64]; + uint32 level; + uint32 class_; + uint32 race; + uint32 unknown152[3]; + uint32 time; + uint32 unknown168; + char text[1028]; +}; + +struct DyeStruct +{ + union + { + struct + { + struct Color_Struct head; + struct Color_Struct chest; + struct Color_Struct arms; + struct Color_Struct wrists; + struct Color_Struct hands; + struct Color_Struct legs; + struct Color_Struct feet; + struct Color_Struct primary; // you can't actually dye this + struct Color_Struct secondary; // or this + } + dyes; + struct Color_Struct dye[9]; + }; +}; + +struct ApproveZone_Struct { + char name[64]; + uint32 zoneid; + uint32 approve; +}; +struct ZoneInSendName_Struct { + uint32 unknown0; + char name[64]; + char name2[64]; + uint32 unknown132; +}; +struct ZoneInSendName_Struct2 { + uint32 unknown0; + char name[64]; + uint32 unknown68[145]; +}; + +static const uint32 MAX_TRIBUTE_TIERS = 10; + +struct StartTribute_Struct { + uint32 client_id; + uint32 tribute_master_id; + uint32 response; +}; + +struct TributeLevel_Struct { + uint32 level; //backwards byte order! + uint32 tribute_item_id; //backwards byte order! + uint32 cost; //backwards byte order! +}; + +struct TributeAbility_Struct { + uint32 tribute_id; //backwards byte order! + uint32 tier_count; //backwards byte order! + TributeLevel_Struct tiers[MAX_TRIBUTE_TIERS]; + char name[0]; +}; + +struct GuildTributeAbility_Struct { + uint32 guild_id; + TributeAbility_Struct ability; +}; + +struct SelectTributeReq_Struct { + uint32 client_id; //? maybe action ID? + uint32 tribute_id; + uint32 unknown8; //seen E3 00 00 00 +}; + +struct SelectTributeReply_Struct { + uint32 client_id; //echoed from request. + uint32 tribute_id; + char desc[0]; +}; + +struct TributeInfo_Struct { + uint32 active; //0 == inactive, 1 == active + uint32 tributes[MAX_PLAYER_TRIBUTES]; //-1 == NONE + uint32 tiers[MAX_PLAYER_TRIBUTES]; //all 00's + uint32 tribute_master_id; +}; + +struct TributeItem_Struct { + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; +}; + +struct TributePoint_Struct { + int32 tribute_points; + uint32 unknown04; + int32 career_tribute_points; + uint32 unknown12; +}; + +struct TributeMoney_Struct { + uint32 platinum; + uint32 tribute_master_id; + int32 tribute_points; +}; + + +struct Split_Struct +{ + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; +}; + + +/* +** New Combine Struct +** Client requesting to perform a tradeskill combine +** Size: 4 bytes +** Used In: OP_TradeSkillCombine +** Last Updated: Oct-15-2003 +** +*/ +struct NewCombine_Struct { +/*00*/ int16 container_slot; +/*02*/ char unknown02[2]; +/*04*/ +}; + + +//client requesting favorite recipies +struct TradeskillFavorites_Struct { + uint32 object_type; + uint32 some_id; + uint32 favorite_recipes[500]; +}; + +//search request +struct RecipesSearch_Struct { + uint32 object_type; //same as in favorites + uint32 some_id; //same as in favorites + uint32 mintrivial; + uint32 maxtrivial; + char query[56]; + uint32 unknown4; //is set to 00 03 00 00 + uint32 unknown5; //is set to 4C DD 12 00 +/*80*/ +}; + +//one sent for each item, from server in reply to favorites or search +struct RecipeReply_Struct { + uint32 object_type; + uint32 some_id; //same as in favorites + uint32 component_count; + uint32 recipe_id; + uint32 trivial; + char recipe_name[64]; +/*84*/ +}; + +//received and sent back as an ACK with different reply_code +struct RecipeAutoCombine_Struct { + uint32 object_type; + uint32 some_id; + uint32 unknown1; //echoed in reply + uint32 recipe_id; + uint32 reply_code; // 93 64 e1 00 (junk) in request + // 00 00 00 00 in successful reply + // f5 ff ff ff in 'you dont have all the stuff' reply +}; + +struct LevelAppearance_Struct { //Sends a little graphic on level up + uint32 spawn_id; + uint32 parm1; + uint32 value1a; + uint32 value1b; + uint32 parm2; + uint32 value2a; + uint32 value2b; + uint32 parm3; + uint32 value3a; + uint32 value3b; + uint32 parm4; + uint32 value4a; + uint32 value4b; + uint32 parm5; + uint32 value5a; + uint32 value5b; +/*64*/ +}; +struct MerchantList{ + uint32 id; + uint32 slot; + uint32 item; +}; +struct TempMerchantList{ + uint32 npcid; + uint32 slot; + uint32 item; + uint32 charges; //charges/quantity + uint32 origslot; +}; + + +struct FindPerson_Point { + float y; + float x; + float z; +}; + +struct FindPersonRequest_Struct { + uint32 npc_id; + FindPerson_Point client_pos; +}; + +//variable length packet of points +struct FindPersonResult_Struct { + FindPerson_Point dest; + FindPerson_Point path[0]; //last element must be the same as dest +}; + +struct MobRename_Struct { +/*000*/ char old_name[64]; +/*064*/ char old_name_again[64]; //not sure what the difference is +/*128*/ char new_name[64]; +/*192*/ uint32 unknown192; //set to 0 +/*196*/ uint32 unknown196; //set to 1 +/*200*/ +}; + +struct PlayMP3_Struct { + char filename[128]; +}; + +//this is for custom title display in the skill window +struct TitleEntry_Struct { + uint32 skill_id; + uint32 skill_value; + char title[1]; +}; + +struct Titles_Struct { + uint32 title_count; + TitleEntry_Struct titles[0]; +}; + +//this is for title selection by the client +struct TitleListEntry_Struct { + uint32 unknown0; //title ID + char prefix[1]; //variable length, null terminated + char postfix[1]; //variable length, null terminated +}; + +struct TitleList_Struct { + uint32 title_count; + TitleListEntry_Struct titles[0]; //list of title structs + //uint32 unknown_ending; seen 0x7265, 0 +}; + +struct SetTitle_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + uint32 title_id; +}; + +struct SetTitleReply_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + char title[32]; + uint32 entity_id; +}; + +struct TaskDescription_Struct { +/*000*/ uint32 activity_count; //not right. +/*004*/ uint32 taskid; +/*008*/ uint8 unk; +/*009*/ uint32 id3; +/*013*/ uint32 unknown13; +/*017*/ char name[1]; //variable length, 0 terminated +/*018*/ uint32 unknown18; +/*022*/ uint32 unknown22; +/*026*/ char desc[1]; //variable length, 0 terminated +/*027*/ uint32 reward_count; //not sure +/*031*/ uint32 unknown31; +/*035*/ uint32 unknown35; +/*039*/ uint16 unknown39; +/*041*/ char reward_link[1]; //variable length, 0 terminated +/*042*/ uint32 unknown43; //maybe crystal count? +}; + +struct TaskMemberList_Struct { +/*00*/ uint32 gopher_id; +/*04*/ uint32 unknown04; +/*08*/ uint32 member_count; //1 less than the number of members +/*12*/ char list_pointer[0]; +/* list is of the form: + char member_name[1] //null terminated string + uint8 task_leader //boolean flag +*/ +}; + + +struct TaskActivity_Struct { +/*000*/ uint32 activity_count; //not right +/*004*/ uint32 id3; +/*008*/ uint32 taskid; +/*012*/ uint32 activity_id; +/*016*/ uint32 unknown016; +/*020*/ uint32 activity_type; +/*024*/ uint32 unknown024; +/*028*/ uint32 unknown28; +/*032*/ char mob_name[1]; //variable length, 0 terminated +/*033*/ char item_name[1]; //variable length, 0 terminated +/*034*/ uint32 goal_count; +/*038*/ uint32 unknown38; //0xFFFFFFFF +/*042*/ uint32 unknown42; //0xFFFFFFFF +/*046*/ uint32 unknown46; //saw 0x151,0x156 +/*050*/ uint32 unknown50; //saw 0x404,0 +/*054*/ char activity_name[1]; //variable length, 0 terminated... commonly empty +/*055*/ uint32 done_count; +/*059*/ uint32 unknown59; //=1 except on unknown and terminal activities? +/*063*/ +}; + +struct TaskHistoryEntry_Struct { + uint32 task_id; + char name[1]; + uint32 completed_time; +}; +struct TaskHistory_Struct { + uint32 completed_count; + TaskHistoryEntry_Struct entries[0]; +}; + +struct AcceptNewTask_Struct { + uint32 task_id; //set to 0 for 'decline' + uint32 task_master_id; //entity ID +}; + +//was all 0's from client, server replied with same op, all 0's +struct CancelTask_Struct { + uint32 unknown0; + uint32 unknown4; +}; + +struct AvaliableTask_Struct { + uint32 task_index; //no idea, seen 0x1 + uint32 task_master_id; //entity ID + uint32 task_id; + uint32 unknown012; + uint32 activity_count; //not sure, seen 2 + char desc[1]; //variable length, 0 terminated + uint32 reward_platinum;//not sure on these + uint32 reward_gold; + uint32 reward_silver; + uint32 reward_copper; + char some_name[1]; //variable length, 0 terminated + uint8 unknown1; + uint32 unknown2; //0xFFFFFFFF + uint32 unknown3; //0xFFFFFFFF + uint32 unknown4; //seen 0x16 + uint8 unknown5; +}; + + +struct BankerChange_Struct { + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; + uint32 platinum_bank; + uint32 gold_bank; + uint32 silver_bank; + uint32 copper_bank; +}; + +struct LeadershipExpUpdate_Struct { +/*00*/ double group_leadership_exp; +/*08*/ uint32 group_leadership_points; +/*12*/ uint32 Unknown12; +/*16*/ double raid_leadership_exp; +/*24*/ uint32 raid_leadership_points; +}; + +struct UpdateLeadershipAA_Struct { +/*00*/ uint32 ability_id; +/*04*/ uint32 new_rank; +/*08*/ uint32 pointsleft; +/*12*/ +}; + +/** +* Leadership AA update +* Length: 32 Octets +* OpCode: LeadExpUpdate +*/ +struct leadExpUpdateStruct { + /*0000*/ uint32 unknown0000; // All zeroes? + /*0004*/ uint32 group_leadership_exp; // Group leadership exp value + /*0008*/ uint32 group_leadership_points; // Unspent group points + /*0012*/ uint32 unknown0012; // Type? + /*0016*/ uint32 unknown0016; // All zeroes? + /*0020*/ uint32 raid_leadership_exp; // Raid leadership exp value + /*0024*/ uint32 raid_leadership_points; // Unspent raid points + /*0028*/ uint32 unknown0028; +}; + + + +struct RaidGeneral_Struct { +/*00*/ uint32 action; //=10 +/*04*/ char player_name[64]; //should both be the player's name +/*04*/ char leader_name[64]; +/*132*/ uint32 parameter; +}; + +struct RaidAdd_Struct { +/*000*/ uint32 action; //=0 +/*004*/ char player_name[64]; //should both be the player's name +/*068*/ char leader_name[64]; +/*132*/ uint8 _class; +/*133*/ uint8 level; +/*134*/ uint8 has_group; +/*135*/ uint8 unknown135; //seems to be 0x42 or 0 +}; + +struct RaidCreate_Struct { +/*00*/ uint32 action; //=8 +/*04*/ char leader_name[64]; +/*68*/ uint32 leader_id; +}; + +struct RaidMemberInfo_Struct { +/*00*/ uint8 group_number; +/*01*/ char member_name[1]; //dyanmic length, null terminated '\0' +/*00*/ uint8 unknown00; +/*01*/ uint8 _class; +/*02*/ uint8 level; +/*03*/ uint8 is_raid_leader; +/*04*/ uint8 is_group_leader; +/*05*/ uint8 main_tank; //not sure +/*06*/ uint8 unknown06[5]; //prolly more flags +}; + +struct RaidDetails_Struct { +/*000*/ uint32 action; //=6,20 +/*004*/ char leader_name[64]; +/*068*/ uint32 unknown68[4]; +/*084*/ LeadershipAA_Struct abilities; //ranks in backwards byte order +/*128*/ uint8 unknown128[142]; +/*354*/ uint32 leader_id; +}; + +struct RaidMembers_Struct { +/*000*/ RaidDetails_Struct details; +/*358*/ uint32 member_count; //including leader +/*362*/ RaidMemberInfo_Struct members[1]; +/*...*/ RaidMemberInfo_Struct empty; //seem to have an extra member with a 0 length name on the end +}; + +struct DynamicWall_Struct { +/*00*/ char name[32]; +/*32*/ float y; +/*36*/ float x; +/*40*/ float z; +/*44*/ uint32 something; +/*48*/ uint32 unknown48; //0 +/*52*/ uint32 one_hundred; //0x64 +/*56*/ uint32 unknown56; //0 +/*60*/ uint32 something2; +/*64*/ int32 unknown64; //-1 +/*68*/ uint32 unknown68; //0 +/*72*/ uint32 unknown72; //0 +/*76*/ uint32 unknown76; //0x100 +/*80*/ +}; + +enum { //bandolier actions + BandolierCreate = 0, + BandolierRemove = 1, + BandolierSet = 2 +}; + +struct BandolierCreate_Struct { +/*00*/ uint32 action; //0 for create +/*04*/ uint8 number; +/*05*/ char name[32]; +/*37*/ uint16 unknown37; //seen 0x93FD +/*39*/ uint8 unknown39; //0 +}; + +struct BandolierDelete_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ uint8 unknown05[35]; +}; + +struct BandolierSet_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ uint8 unknown05[35]; +}; + +struct Arrow_Struct { +/*000*/ uint32 type; //unsure on name, seems to be 0x1, dosent matter +/*005*/ uint8 unknown004[12]; +/*016*/ float src_y; +/*020*/ float src_x; +/*024*/ float src_z; +/*028*/ uint8 unknown028[12]; +/*040*/ float velocity; //4 is normal, 20 is quite fast +/*044*/ float launch_angle; //0-450ish, not sure the units, 140ish is straight +/*048*/ float tilt; //on the order of 125 +/*052*/ uint8 unknown052[8]; +/*060*/ float arc; +/*064*/ uint8 unknown064[12]; +/*076*/ uint32 source_id; +/*080*/ uint32 target_id; //entity ID +/*084*/ uint32 item_id; //1 to about 150ish +/*088*/ uint32 unknown088; //seen 125, dosent seem to change anything.. +/*092*/ uint32 unknown092; //seen 16, dosent seem to change anything +/*096*/ uint8 unknown096[5]; +/*101*/ char model_name[16]; +/*117*/ uint8 unknown117[19]; +}; + +//made a bunch of trivial structs for stuff for opcode finder to use +struct Consent_Struct { + char name[1]; //always at least a null +}; + +struct AdventureMerchant_Struct { + uint32 unknown_flag; //seems to be 1 + uint32 entity_id; +}; + +struct Save_Struct { + uint8 unknown00[192]; +}; + +struct GMToggle_Struct { + uint8 unknown0[64]; + uint32 toggle; +}; + +struct GroupInvite_Struct { + char invitee_name[64]; + char inviter_name[64]; +// uint8 unknown128[65]; +}; + +struct BuffFadeMsg_Struct { + uint32 color; + char msg[1]; +}; + +struct UseAA_Struct { + uint32 begin; + uint32 ability; + uint32 end; +}; + +struct AA_Ability { +/*00*/ uint32 skill_id; +/*04*/ uint32 base1; +/*08*/ uint32 base2; +/*12*/ uint32 slot; +}; + +struct SendAA_Struct { +/*0000*/ uint32 id; +/*0004*/ uint32 unknown004; //set to 1. +/*0008*/ uint32 hotkey_sid; +/*0012*/ uint32 hotkey_sid2; +/*0016*/ uint32 title_sid; +/*0020*/ uint32 desc_sid; +/*0024*/ uint32 class_type; +/*0028*/ uint32 cost; +/*0032*/ uint32 seq; +/*0036*/ uint32 current_level; //1s, MQ2 calls this AARankRequired +/*0040*/ uint32 prereq_skill; //is < 0, abs() is category # +/*0044*/ uint32 prereq_minpoints; //min points in the prereq +/*0048*/ uint32 type; +/*0052*/ uint32 spellid; +/*0056*/ uint32 spell_type; +/*0060*/ uint32 spell_refresh; +/*0064*/ uint16 classes; +/*0066*/ uint16 berserker; //seems to be 1 if its a berserker ability +/*0068*/ uint32 max_level; +/*0072*/ uint32 last_id; +/*0076*/ uint32 next_id; +/*0080*/ uint32 cost2; +/*0084*/ uint32 unknown80[2]; //0s +/*0088*/ uint32 total_abilities; +/*0092*/ AA_Ability abilities[0]; +}; + +struct AA_List { + SendAA_Struct* aa[0]; +}; + +struct AA_Action { +/*00*/ uint32 action; +/*04*/ uint32 ability; +/*08*/ uint32 unknown08; +/*12*/ uint32 exp_value; +}; + +struct AA_Skills { //this should be removed and changed to AA_Array +/*00*/ uint32 aa_skill; +/*04*/ uint32 aa_value; +}; + +struct AAExpUpdate_Struct { +/*00*/ uint32 unknown00; //seems to be a value from AA_Action.ability +/*04*/ uint32 aapoints_unspent; +/*08*/ uint8 aaxp_percent; //% of exp that goes to AAs +/*09*/ uint8 unknown09[3]; //live dosent always zero these, so they arnt part of aaxp_percent +}; + + +struct AltAdvStats_Struct { +/*000*/ uint32 experience; +/*004*/ uint16 unspent; +/*006*/ uint16 unknown006; +/*008*/ uint8 percentage; +/*009*/ uint8 unknown009[3]; +}; + +struct PlayerAA_Struct { + AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct AATable_Struct { + AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct Weather_Struct { + uint32 val1; //generall 0x000000FF + uint32 type; //0x31=rain, 0x02=snow(i think), 0 = normal + uint32 mode; +}; + +struct ZoneInUnknown_Struct { + uint32 val1; + uint32 val2; + uint32 val3; +}; + +struct MobHealth_Struct { + uint16 entity_id; + uint8 hp; +}; + +struct AnnoyingZoneUnknown_Struct { + uint32 entity_id; + uint32 value; //always 4 +}; + +struct GuildMemberUpdate_Struct { +/*000*/ uint32 guild_id; //not sure +/*004*/ char member_name[64]; +/*068*/ uint16 zone_id; +/*070*/ uint16 instance_id; +/*072*/ uint32 unknown072; +}; + +struct VeteranRewardItem +{ +/*000*/ uint32 item_id; +/*004*/ char item_name[256]; +}; + +struct VeteranReward +{ +/*000*/ uint32 claim_id; +/*004*/ VeteranRewardItem item; +}; + +struct ExpeditionExpireWarning +{ +/*000*/ uint32 unknown000; +/*004*/ uint32 minutes_remaining; +}; + +struct ExpeditionInfo_Struct +{ +/*000*/ uint32 clientid; +/*004*/ uint32 enabled_max; +/*008*/ uint32 max_players; +/*012*/ char expedition_name[128]; +/*142*/ char leader_name[64]; +}; + +struct ExpeditionCompassEntry_Struct +{ +/*000*/ float unknown000; //seen *((uint32*)) = 1584791871 +/*004*/ uint32 enabled; //guess +/*008*/ uint32 unknown008; //seen 1019 +/*012*/ float y; +/*016*/ float x; +/*020*/ float z; +}; + +struct ExpeditionCompass_Struct +{ +/*000*/ uint32 clientid; +/*004*/ uint32 count; +/*008*/ ExpeditionCompassEntry_Struct entries[0]; +}; + +struct ExpeditionJoinPrompt_Struct +{ +/*000*/ uint32 clientid; +/*004*/ char player_name[64]; +/*068*/ char expedition_name[64]; +}; + +struct LFGuild_SearchPlayer_Struct +{ +/*00*/ uint32 Command; +/*04*/ uint32 Unknown04; +/*08*/ uint32 FromLevel; +/*12*/ uint32 ToLevel; +/*16*/ uint32 MinAA; +/*20*/ uint32 TimeZone; +/*24*/ uint32 Classes; +}; + +struct LFGuild_SearchGuild_Struct +{ +/*00*/ uint32 Command; +/*04*/ uint32 Unknown04; +/*08*/ uint32 Level; +/*12*/ uint32 AAPoints; +/*16*/ uint32 TimeZone; +/*20*/ uint32 Class; +/*24*/ +}; + +struct LFGuild_PlayerToggle_Struct +{ +/*000*/ uint32 Command; +/*004*/ uint8 Unknown004[68]; +/*072*/ char Comment[256]; +/*328*/ uint8 Unknown328[268]; +/*596*/ uint32 TimeZone; +/*600*/ uint8 Toggle; +/*601*/ uint8 Unknown601[7]; +/*608*/ uint32 Expires; +/*612*/ +}; + +struct LFGuild_GuildToggle_Struct +{ +/*000*/ uint32 Command; +/*004*/ uint8 Unknown004[8]; +/*012*/ char Comment[256]; +/*268*/ uint8 Unknown268[256]; +/*524*/ uint32 FromLevel; +/*528*/ uint32 ToLevel; +/*532*/ uint32 Classes; +/*536*/ uint32 AACount; +/*540*/ uint32 TimeZone; +/*544*/ uint8 Toggle; +/*545*/ uint8 Unknown545[3]; +/*548*/ uint32 Expires; +/*552*/ char Name[64]; +/*616*/ +}; + + }; //end namespace structs +}; //end namespace Titanium + + + +#endif /*Titanium_STRUCTS_H_*/ + + + + + + + + + + diff --git a/common/patches/Underfoot.cpp b/common/patches/Underfoot.cpp new file mode 100644 index 000000000..ed4371f7e --- /dev/null +++ b/common/patches/Underfoot.cpp @@ -0,0 +1,3945 @@ + +#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 "../Item.h" +#include "Underfoot_structs.h" +#include "../rulesys.h" + +#include +#include + +namespace Underfoot +{ + +static const char *name = "Underfoot"; +static OpcodeManager *opcodes = NULL; +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 == NULL) { + //TODO: get this file name from the config file + 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; + string pname; + + //register our world signature. + pname = 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 = 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 != NULL) { + //TODO: get this file name from the config file + 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); +} + + + +#include "SSDefine.h" + + +// Converts Titanium Slot IDs to Underfoot Slot IDs for use in Encodes +static inline uint32 TitaniumToUnderfootSlot(uint32 TitaniumSlot) { + uint32 UnderfootSlot = 0; + + if(TitaniumSlot >= 21 && TitaniumSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots + { + UnderfootSlot = TitaniumSlot + 1; + } + else if(TitaniumSlot >= 251 && TitaniumSlot <= 340) // Bag Slots for Normal Inventory and Cursor + { + UnderfootSlot = TitaniumSlot + 11; + } + else if(TitaniumSlot >= 2031 && TitaniumSlot <= 2270) // Bank Bag Slots + { + UnderfootSlot = TitaniumSlot + 1; + } + else if(TitaniumSlot >= 2531 && TitaniumSlot <= 2550) // Shared Bank Bag Slots + { + UnderfootSlot = TitaniumSlot + 1; + } + else if(TitaniumSlot == 9999) //Unused slot ID to give a place to save Power Slot + { + UnderfootSlot = 21; + } + else + { + UnderfootSlot = TitaniumSlot; + } + + return UnderfootSlot; +} + +// Converts Underfoot Slot IDs to Titanium Slot IDs for use in Decodes +static inline uint32 UnderfootToTitaniumSlot(uint32 UnderfootSlot) { + uint32 TitaniumSlot = 0; + + if(UnderfootSlot >= 22 && UnderfootSlot <= 54) // Cursor/Ammo/Power Source and Normal Inventory Slots + { + TitaniumSlot = UnderfootSlot - 1; + } + else if(UnderfootSlot >= 262 && UnderfootSlot <= 351) // Bag Slots for Normal Inventory and Cursor + { + TitaniumSlot = UnderfootSlot - 11; + } + else if(UnderfootSlot >= 2032 && UnderfootSlot <= 2271) // Bank Bag Slots + { + TitaniumSlot = UnderfootSlot - 1; + } + else if(UnderfootSlot >= 2532 && UnderfootSlot <= 2551) // Shared Bank Bag Slots + { + TitaniumSlot = UnderfootSlot - 1; + } + else if(UnderfootSlot == 21) + { + TitaniumSlot = 9999; //Unused slot ID to give a place to save Power Slot + } + else + { + TitaniumSlot = UnderfootSlot; + } + + return TitaniumSlot; +} + + +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 = NULL; + + 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 < MAX_MATERIALS; 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); +// 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 = 16; r < 48; r++) { + eq->unknown521[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 = NULL; + + 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 = NULL; + + 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 = NULL; + + 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 = NULL; + + 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 = NULL; + + //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[MATERIAL_PRIMARY]); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MATERIAL_SECONDARY]); + 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 = NULL; + + //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].GradeCountEntry); + } + + 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 = NULL; + + //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 = NULL; + + 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 = NULL; + + 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 = NULL; + + //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 = NULL; + + 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 = TitaniumToUnderfootSlot(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 = TitaniumToUnderfootSlot(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 = TitaniumToUnderfootSlot(emu->from_slot); + eq->to_slot = TitaniumToUnderfootSlot(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 = TitaniumToUnderfootSlot(emu->from_slot); + eq->to_slot = TitaniumToUnderfootSlot(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 = TitaniumToUnderfootSlot(emu->slot); + OUT(spell); + OUT(target); + + FINISH_ENCODE(); +} + +ENCODE(OP_Trader) { + + if((*p)->size != sizeof(TraderBuy_Struct)) { + EQApplicationPacket *in = *p; + *p = NULL; + 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 = TitaniumToUnderfootSlot(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 = TitaniumToUnderfootSlot(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); + ss.clear(); + + 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 = TitaniumToUnderfootSlot(emu->slot); + OUT(charges); + OUT(sell_price); + + FINISH_ENCODE(); +} + +ENCODE(OP_RaidUpdate) +{ + EQApplicationPacket *inapp = *p; + *p = NULL; + 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)); + + 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[] __emu_buffer; +} + +ENCODE(OP_WhoAllResponse) +{ + EQApplicationPacket *in = *p; + *p = NULL; + + 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 = NULL; + + 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 = NULL; + + 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 = NULL; + + 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 = TitaniumToUnderfootSlot(emu->slot_id); + OUT(charges); + OUT(cost); + FINISH_ENCODE(); +} + +ENCODE(OP_AltCurrency) +{ + EQApplicationPacket *in = *p; + *p = NULL; + + 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 = UnderfootToTitaniumSlot(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 = UnderfootToTitaniumSlot(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 = UnderfootToTitaniumSlot(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 = UnderfootToTitaniumSlot(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 = UnderfootToTitaniumSlot(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 = UnderfootToTitaniumSlot(eq->from_slot); + emu->to_slot = UnderfootToTitaniumSlot(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 = UnderfootToTitaniumSlot(eq->from_slot); + emu->to_slot = UnderfootToTitaniumSlot(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 = UnderfootToTitaniumSlot(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 = UnderfootToTitaniumSlot(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 = UnderfootToTitaniumSlot(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 = UnderfootToTitaniumSlot(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 = UnderfootToTitaniumSlot(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 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); + ss.clear(); + + 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 = TitaniumToUnderfootSlot(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 = 0; x < 10; ++x) { + + SubSerializations[x] = NULL; + + const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); + + if(subitem) { + + int SubSlotNumber; + + iqbs.subitem_count++; + + if(slot_id_in >= 22 && slot_id_in < 30) + SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); + else if(slot_id_in >= 2000 && slot_id_in <= 2023) + SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); + else if(slot_id_in >= 2500 && slot_id_in <= 2501) + SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); + else + SubSlotNumber = slot_id_in; // ??????? + + 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 = UnderfootToTitaniumSlot(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 = UnderfootToTitaniumSlot(eq->slot_id); + IN(charges); + IN(cost); + FINISH_DIRECT_DECODE(); +} + +} //end namespace Underfoot diff --git a/common/patches/Underfoot.h b/common/patches/Underfoot.h new file mode 100644 index 000000000..52436385d --- /dev/null +++ b/common/patches/Underfoot.h @@ -0,0 +1,36 @@ +#ifndef Underfoot_H_ +#define Underfoot_H_ + +#include "../StructStrategy.h" + +class EQStreamIdentifier; + +namespace Underfoot { + + //these are the only public member of this namespace. + extern void Register(EQStreamIdentifier &into); + extern void Reload(); + + + + //you should not directly access anything below.. + //I just dont feel like making a seperate header for it. + + class Strategy : public StructStrategy { + public: + Strategy(); + + protected: + + virtual std::string Describe() const; + + //magic macro to declare our opcode processors + #include "SSDeclare.h" + #include "Underfoot_ops.h" + }; + +}; + + + +#endif /*Underfoot_H_*/ diff --git a/common/patches/Underfoot_itemfields.h b/common/patches/Underfoot_itemfields.h new file mode 100644 index 000000000..ccba333b5 --- /dev/null +++ b/common/patches/Underfoot_itemfields.h @@ -0,0 +1,439 @@ +/* + + +These fields must be in the order of how they are serialized! + + + +*/ +#define NEW_TRY +#ifdef NEW_TRY +//* 000 */ I(ItemClass) // Leave this one off on purpose +/* 001 */ S(Name) +/* 002 */ S(Lore) +//* 003 */ C("")//lorefile - Newly Added - Field is Null +/* 004 */ S(IDFile) +/* 005 */ I(ID) +/* 006 */ I(Weight) +/* 007 */ I(NoRent) +/* 008 */ I(NoDrop) +/* 009 */ I(Size) +/* 010 */ I(Slots) +/* 011 */ I(Price) +/* 012 */ I(Icon) +/* 013 */ C("0")//UNK013 +/* 014 */ C("0")//UNK014 +/* 015 */ I(BenefitFlag) +/* 016 */ I(Tradeskills) +/* 017 */ I(CR) +/* 018 */ I(DR) +/* 019 */ I(PR) +/* 020 */ I(MR) +/* 021 */ I(FR) +/* 022 */ C("0")//svcorruption - Newly Added +/* 023 */ I(AStr) +/* 024 */ I(ASta) +/* 025 */ I(AAgi) +/* 026 */ I(ADex) +/* 027 */ I(ACha) +/* 028 */ I(AInt) +/* 029 */ I(AWis) +/* 030 */ I(HP) +/* 031 */ I(Mana) +/* 032 */ I(Endur) //endur - Relocated +/* 033 */ I(AC) +/* 034 */ I(Classes)//classes - Relocated +/* 035 */ I(Races)//races - Relocated +/* 036 */ I(Deity) +/* 037 */ I(SkillModValue) +/* 038 */ C("0")//UNK038 - Default is 0 +/* 039 */ I(SkillModType) +/* 040 */ I(BaneDmgRace) +/* 041 */ I(BaneDmgBody)//banedmgbody - Relocated +/* 042 */ I(BaneDmgRaceAmt)//banedmgraceamt - Relocated +/* 043 */ I(BaneDmgAmt)//banedmgamt - Relocated +/* 044 */ I(Magic) +/* 045 */ I(CastTime_) +/* 046 */ I(ReqLevel) +/* 047 */ I(RecLevel)//reclevel - Relocated +/* 048 */ I(RecSkill)//recskill - Relocated +/* 049 */ I(BardType) +/* 050 */ I(BardValue) +/* 051 */ I(Light) +/* 052 */ I(Delay) +/* 053 */ I(ElemDmgType) +/* 054 */ I(ElemDmgAmt) +/* 055 */ I(Range) +/* 056 */ I(Damage) +/* 057 */ I(Color) +/* 058 */ I(ItemType) +/* 059 */ I(Material) +/* 060 */ C("0")//UNK060 - Default is 0 +/* 061 */ C("0")//UNK061 - Default is 0 +/* 062 */ F(SellRate) +/* 063 */ I(CombatEffects) +/* 064 */ I(Shielding) +/* 065 */ I(StunResist) +/* 066 */ I(StrikeThrough) +/* 067 */ I(ExtraDmgSkill) +/* 068 */ I(ExtraDmgAmt) +/* 069 */ I(SpellShield) +/* 070 */ I(Avoidance) +/* 071 */ I(Accuracy) +/* 072 */ I(CharmFileID) +/* 073 */ I(FactionMod1)//Swapped these so Faction Amt comes after each Faction Mod +/* 074 */ I(FactionAmt1)//Swapped these so Faction Amt comes after each Faction Mod +/* 075 */ I(FactionMod2)//Swapped these so Faction Amt comes after each Faction Mod +/* 076 */ I(FactionAmt2)//Swapped these so Faction Amt comes after each Faction Mod +/* 077 */ I(FactionMod3)//Swapped these so Faction Amt comes after each Faction Mod +/* 078 */ I(FactionAmt3)//Swapped these so Faction Amt comes after each Faction Mod +/* 079 */ I(FactionMod4)//Swapped these so Faction Amt comes after each Faction Mod +/* 080 */ I(FactionAmt4)//Swapped these so Faction Amt comes after each Faction Mod +/* 081 */ S(CharmFile) +/* 082 */ I(AugType) +/* 083 */ I(AugRestrict)//augrestrict - Relocated +/* 084 */ I(AugDistiller)//augdistiller - Relocated +/* 085 */ I(AugSlotType[0]) +/* 086 */ I(AugSlotVisible[0])//augslot1visible - Default 1 +/* 087 */ C("0")//augslot1unk2 - Newly Added - Default 0 +/* 088 */ I(AugSlotType[1]) +/* 089 */ I(AugSlotVisible[1]) +/* 090 */ C("0")//augslot2unk2 - Newly Added +/* 091 */ I(AugSlotType[2]) +/* 092 */ I(AugSlotVisible[2]) +/* 093 */ C("0")//augslot3unk2 - Newly Added +/* 094 */ I(AugSlotType[3]) +/* 095 */ I(AugSlotVisible[3]) +/* 096 */ C("0")//augslot4unk2 - Newly Added +/* 097 */ I(AugSlotType[4]) +/* 098 */ I(AugSlotVisible[4]) +/* 099 */ C("0")//augslot5unk2 - Newly Added +/* 100 */ I(PointType)//pointtype - Relocated +/* 101 */ I(LDoNTheme) +/* 102 */ I(LDoNPrice) +/* 103 */ C("70")//UNK098 - Newly Added - Default 70, but some are set to 0 +/* 104 */ I(LDoNSold) +/* 105 */ I(BagType) +/* 106 */ I(BagSlots) +/* 107 */ I(BagSize) +/* 108 */ I(BagWR) +/* 109 */ I(Book) +/* 110 */ I(BookType) +/* 111 */ S(Filename) +/* 112 */ I(LoreGroup) +/* 113 */ I(ArtifactFlag) +/* 114 */ C("0")//I(PendingLoreFlag)?//UNK109 - Default 0, but a few are 1 +/* 115 */ I(Favor) +/* 116 */ I(GuildFavor)//guildfavor - Relocated +/* 117 */ I(FVNoDrop) +/* 118 */ I(DotShielding) +/* 119 */ I(Attack) +/* 120 */ I(Regen) +/* 121 */ I(ManaRegen) +/* 122 */ I(EnduranceRegen) +/* 123 */ I(Haste) +/* 124 */ I(DamageShield) +/* 125 */ C("-1") //UNK120 - Default is -1 +/* 126 */ C("0") //UNK121 - Default is 0 +/* 127 */ I(Attuneable) +/* 128 */ I(NoPet) +/* 129 */ C("0") //UNK124 - Default 0, but a few are 1 +/* 130 */ I(PotionBelt) +/* 131 */ C("0") //potionbeltslots - Default 0, but a few are 1 +/* 132 */ I(StackSize) +/* 133 */ I(NoTransfer) +/* 134 */ I(Stackable)//UNK129 - Default is 0, but some are much higher +/* 135 */ I(QuestItemFlag)//questitemflag - Default is 0 (off), flag on = 1 +/* 136 */ C("0")//UNK131 - Default is 0, but there is an item set to 1 +/* 137 */ C("0")//UNK132 - Default is 0? 0000000000000000000? +/* 138 */ I(Click.Effect) +/* 139 */ I(Click.Type) +/* 140 */ I(Click.Level2) +/* 141 */ I(Click.Level) +/* 142 */ I(MaxCharges)//maxcharges - Relocated +/* 143 */ I(CastTime_)//casttime - Relocated - Note Duplicate Entries for CastTime_ and none for CastTime +/* 144 */ I(RecastDelay)//recastdelay - Relocated +/* 145 */ I(RecastType)//recasttype - Relocated +/* 146 */ C("0")//clickunk5 - Newly Added - Default is 0 +/* 147 */ C("")//clickname - Newly Added - Default is Null +/* 148 */ C("-1")//clickunk7 - Newly Added - Default is -1, but some set to 0 and some much higher +/* 149 */ I(Proc.Effect) +/* 150 */ I(Proc.Type) +/* 151 */ I(Proc.Level2) +/* 152 */ I(Proc.Level) +/* 153 */ C("0")//procunk1 - Newly Added - Default is 0, but some set to -1 and 1 +/* 154 */ C("0")//procunk2 - Newly Added - Default is 0 +/* 155 */ C("0")//procunk3 - Newly Added - Default is 0 +/* 156 */ C("0")//procunk4 - Newly Added - Default is 0 +/* 157 */ I(ProcRate)//procrate - Relocated +/* 158 */ C("")//procname - Newly Added - Default is Null +/* 159 */ C("-1")//procunk7 - Newly Added - Default is -1, but some set to 0 +/* 160 */ I(Worn.Effect) +/* 161 */ I(Worn.Type) +/* 162 */ I(Worn.Level2) +/* 163 */ I(Worn.Level) +/* 164 */ C("0")//wornunk1 - Newly Added - Default is 0 +/* 165 */ C("0")//wornunk2 - Newly Added - Default is 0 +/* 166 */ C("0")//wornunk3 - Newly Added - Default is 0 +/* 167 */ C("0")//wornunk4 - Newly Added - Default is 0 +/* 168 */ C("0")//wornunk5 - Newly Added - Default is 0 +/* 169 */ C("")//wornname - Newly Added - Default is Null +/* 170 */ C("-1")//wornunk7 - Newly Added - Default is -1, but some set to 0 +/* 171 */ I(Focus.Effect) +/* 172 */ I(Focus.Type) +/* 173 */ I(Focus.Level2) +/* 174 */ I(Focus.Level) +/* 175 */ C("0")//focusunk1 - Newly Added - Default is 0 +/* 176 */ C("0")//focusunk2 - Newly Added - Default is 0 +/* 177 */ C("0")//focusunk3 - Newly Added - Default is 0 +/* 178 */ C("0")//focusunk4 - Newly Added - Default is 0 +/* 179 */ C("0")//focusunk5 - Newly Added - Default is 0 +/* 180 */ C("")//focusname - Newly Added - Default is Null +/* 181 */ C("-1")//focusunk7 - Newly Added - Default is -1, but some set to 0 +/* 182 */ I(Scroll.Effect) +/* 183 */ I(Scroll.Type) +/* 184 */ I(Scroll.Level2) +/* 185 */ I(Scroll.Level) +/* 186 */ C("0")//scrollunk1 - Renumber this*** +/* 187 */ C("0")//scrollunk2 - Newly Added - Default is 0 +/* 188 */ C("0")//scrollunk3 - Newly Added - Default is 0 +/* 189 */ C("0")//scrollunk4 - Newly Added - Default is 0 +/* 190 */ C("0")//scrollunk5 - Newly Added - Default is 0 +/* 191 */ C("")//scrollname - Newly Added - Default is Null +/* 192 */ C("-1")//scrollunk7 - Newly Added - Default is -1, but some set to 0 +/* 193 */ C("0")//UNK193 - Default is 0 +/* 194 */ C("0")//purity - Newly Added - Default is 0, but some go up to 75 +/* 195 */ C("0")//dsmitigation - Newly Added - Default is 0, but some are up to 2 +/* 196 */ C("0")//heroic_str - Newly Added - Default is 0 +/* 197 */ C("0")//heroic_int - Newly Added - Default is 0 +/* 198 */ C("0")//heroic_wis - Newly Added - Default is 0 +/* 199 */ C("0")//heroic_agi - Newly Added - Default is 0 +/* 200 */ C("0")//heroic_dex - Newly Added - Default is 0 +/* 201 */ C("0")//heroic_sta - Newly Added - Default is 0 +/* 202 */ C("0")//heroic_cha - Newly Added - Default is 0 +/* 203 */ C("0")//HeroicSvPoison - Newly Added - Default is 0 +/* 204 */ C("0")//HeroicSvMagic - Newly Added - Default is 0 +/* 205 */ C("0")//HeroicSvFire - Newly Added - Default is 0 +/* 206 */ C("0")//HeroicSvDisease - Newly Added - Default is 0 +/* 207 */ C("0")//HeroicSvCold - Newly Added - Default is 0 +/* 208 */ C("0")//HeroicSvCorruption - Newly Added - Default is 0 +/* 209 */ C("0")//healamt - Newly Added - Default is 0, but some are up to 9 +/* 210 */ C("0")//spelldmg - Newly Added - Default is 0, but some are up to 9 +/* 211 */ C("0")//clairvoyance - Newly Added - Default is 0, but some are up to 10 +/* 212 */ C("0")//backstabdmg - Newly Added - Default is 0, but some are up to 65 +//* 213 */ C("0")//evolvinglevel - Newly Added - Default is 0, but some are up to 7 +//* 214 */ C("0")//MaxPower - Newly Added +//* 215 */ C("0")//Power - Newly Added + +//This doesn't appear to be used /* 102 */ S(verified)//verified +//This doesn't appear to be used /* 102 */ S(serialized)//created +//Unsure where this goes right now (or if it is even used) /* 108 */ I(SummonedFlag) + +#else +/* 000 */ //I(ItemClass) Leave this one off on purpose +/* 001 */ S(Name) +/* 002 */ S(Lore) +/* 003 */ C("") //LoreFile? +/* 003 */ S(IDFile) +/* 004 */ I(ID) +/* 005 */ I(Weight) +/* 006 */ I(NoRent) +/* 007 */ I(NoDrop) +/* 008 */ I(Size) +/* 009 */ I(Slots) +/* 010 */ I(Price) +/* 011 */ I(Icon) +/* 013 */ C("0") +/* 014 */ C("0") +/* 014 */ I(BenefitFlag) +/* 015 */ I(Tradeskills) +/* 016 */ I(CR) +/* 017 */ I(DR) +/* 018 */ I(PR) +/* 019 */ I(MR) +/* 020 */ I(FR) + C("0") //svcorruption +/* 021 */ I(AStr) +/* 022 */ I(ASta) +/* 023 */ I(AAgi) +/* 024 */ I(ADex) +/* 025 */ I(ACha) +/* 026 */ I(AInt) +/* 027 */ I(AWis) +/* 028 */ I(HP) +/* 029 */ I(Mana) + I(Endur) +/* 030 */ I(AC) +/* 052 */ I(Classes) +/* 053 */ I(Races) +/* 031 */ I(Deity) +/* 032 */ I(SkillModValue) +/* 033 */ C("0") +/* 034 */ I(SkillModType) +/* 035 */ I(BaneDmgRace) +/* 037 */ I(BaneDmgBody) +/* 036 */ I(BaneDmgRaceAmt) +/* 036 */ I(BaneDmgAmt) +/* 038 */ I(Magic) +/* 039 */ I(CastTime_) +/* 040 */ I(ReqLevel) +/* 045 */ I(RecLevel) +/* 046 */ I(RecSkill) +/* 041 */ I(BardType) +/* 042 */ I(BardValue) +/* 043 */ I(Light) +/* 044 */ I(Delay) +/* 047 */ I(ElemDmgType) +/* 048 */ I(ElemDmgAmt) +/* 049 */ I(Range) +/* 050 */ I(Damage) +/* 051 */ I(Color) +/* 056 */ I(ItemType) +/* 057 */ I(Material) +/* 060 */ C("0") +/* 061 */ C("0") +/* 058 */ F(SellRate) +/* 063 */ I(CombatEffects) +/* 064 */ I(Shielding) +/* 065 */ I(StunResist) +/* 059 */ //C("0") +/* 061 */ //C("0") +/* 066 */ I(StrikeThrough) +/* 067 */ I(ExtraDmgSkill) +/* 068 */ I(ExtraDmgAmt) +/* 069 */ I(SpellShield) +/* 070 */ I(Avoidance) +/* 071 */ I(Accuracy) +/* 072 */ I(CharmFileID) +/* 073 */ I(FactionMod1) +/* 077 */ I(FactionAmt1) +/* 074 */ I(FactionMod2) +/* 078 */ I(FactionAmt2) +/* 075 */ I(FactionMod3) +/* 079 */ I(FactionAmt3) +/* 076 */ I(FactionMod4) +/* 080 */ I(FactionAmt4) +/* 081 */ S(CharmFile) +/* 082 */ I(AugType) +/* 082 */ I(AugRestrict) +/* 082 */ I(AugDistiller) +/* 083 */ I(AugSlotType[0]) +/* 084 */ I(AugSlotVisible[0]) +/* 084 */ I(AugSlotUnk2[0]) +/* 085 */ I(AugSlotType[1]) +/* 086 */ I(AugSlotVisible[1]) +/* 086 */ I(AugSlotUnk2[1]) +/* 087 */ I(AugSlotType[2]) +/* 088 */ I(AugSlotVisible[2]) +/* 088 */ I(AugSlotUnk2[2]) +/* 089 */ I(AugSlotType[3]) +/* 090 */ I(AugSlotVisible[3]) +/* 090 */ I(AugSlotUnk2[3]) +/* 091 */ I(AugSlotType[4]) +/* 092 */ I(AugSlotVisible[4]) +/* 092 */ I(AugSlotUnk2[4]) +/* 093 */ I(PointType) +/* 093 */ I(LDoNTheme) +/* 094 */ I(LDoNPrice) +/* 094 */ C("0") +/* 095 */ I(LDoNSold) +/* 096 */ I(BagType) +/* 097 */ I(BagSlots) +/* 098 */ I(BagSize) +/* 099 */ I(BagWR) +/* 100 */ I(Book) +/* 101 */ I(BookType) +/* 102 */ S(Filename) +/* 105 */ I(LoreGroup) +/* 106 */ //I(PendingLoreFlag) +/* 107 */ I(ArtifactFlag) +/* 094 */ C("0") +/* 108 */ //I(SummonedFlag) +/* 109 */ I(Favor) +/* 121 */ I(GuildFavor) +/* 110 */ I(FVNoDrop) +/* 112 */ I(DotShielding) +/* 113 */ I(Attack) +/* 114 */ I(Regen) +/* 115 */ I(ManaRegen) +/* 116 */ I(EnduranceRegen) +/* 117 */ I(Haste) +/* 118 */ I(DamageShield) +/* 120 */ C("0") +/* 121 */ C("0") +/* 125 */ I(Attuneable) +/* 126 */ I(NoPet) +/* 124 */ C("0") +/* 129 */ I(PotionBelt) +/* 130 */ I(PotionBeltSlots) +/* 131 */ I(StackSize) +/* 132 */ I(NoTransfer) +/* 129 */ C("0") +/* 132 */ I(QuestItemFlag) +/* 131 */ C("0") +/* 132 */ C("00000000000000000000000000000000000000") +/* 134 */ I(Click.Effect) +/* 135 */ I(Click.Type) +/* 136 */ I(Click.Level2) +/* 137 */ I(Click.Level) +/* 055 */ I(MaxCharges) +/* 060 */ I(CastTime) +/* 119 */ I(RecastDelay) +/* 120 */ I(RecastType) +/* 138 */ C("0") //clickunk5 (prolly ProcRate) +/* 138 */ C("") //clickunk6 +/* 138 */ C("-1") //clickunk7 +/* 139 */ I(Proc.Effect) +/* 140 */ I(Proc.Type) +/* 141 */ I(Proc.Level2) +/* 142 */ I(Proc.Level) +/* 143 */ C("0") //procunk1 (prolly MaxCharges) +/* 143 */ C("0") //procunk2 (prolly CastTime) +/* 143 */ C("0") //procunk3 (prolly RecastDelay) +/* 143 */ C("0") //procunk4 (prolly RecastType) +/* 062 */ I(ProcRate) +/* 143 */ C("") //procunk6 +/* 143 */ C("-1") //procunk7 +/* 144 */ I(Worn.Effect) +/* 145 */ I(Worn.Type) +/* 146 */ I(Worn.Level2) +/* 147 */ I(Worn.Level) +/* 143 */ C("0") //wornunk1 (prolly MaxCharges) +/* 143 */ C("0") //wornunk2 (prolly CastTime) +/* 143 */ C("0") //wornunk3 (prolly RecastDelay) +/* 143 */ C("0") //wornunk4 (prolly RecastType) +/* 143 */ C("0") //wornunk5 (prolly ProcRate) +/* 143 */ C("") //wornunk6 +/* 143 */ C("-1") //wornunk7 +/* 149 */ I(Focus.Effect) +/* 150 */ I(Focus.Type) +/* 151 */ I(Focus.Level2) +/* 152 */ I(Focus.Level) +/* 143 */ C("0") //focusunk1 (prolly MaxCharges) +/* 143 */ C("0") //focusunk2 (prolly CastTime) +/* 143 */ C("0") //focusunk3 (prolly RecastDelay) +/* 143 */ C("0") //focusunk4 (prolly RecastType) +/* 143 */ C("0") //focusunk5 (prolly ProcRate) +/* 143 */ C("") //focusunk6 +/* 143 */ C("-1") //focusunk7 +/* 154 */ I(Scroll.Effect) +/* 155 */ I(Scroll.Type) +/* 156 */ I(Scroll.Level2) +/* 157 */ I(Scroll.Level) +/* 143 */ C("0") //scrollunk1 (prolly MaxCharges) +/* 143 */ C("0") //scrollunk2 (prolly CastTime) +/* 143 */ C("0") //scrollunk3 (prolly RecastDelay) +/* 143 */ C("0") //scrollunk4 (prolly RecastType) +/* 143 */ C("0") //scrollunk5 (prolly ProcRate) +/* 143 */ C("") //scrollunk6 +/* 143 */ C("-1") //scrollunk7 +/* 193 */ C("0") //Power Source Capacity +/* 194 */ C("0") //purity + +#endif + +#undef I +#undef C +#undef S +#undef F + diff --git a/common/patches/Underfoot_ops.h b/common/patches/Underfoot_ops.h new file mode 100644 index 000000000..7aa9caf50 --- /dev/null +++ b/common/patches/Underfoot_ops.h @@ -0,0 +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) +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_ApplyPoison) +E(OP_ChannelMessage) +E(OP_GuildsList) +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_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) +D(OP_Buff) +D(OP_ShopPlayerSell) +D(OP_Consume) +D(OP_CastSpell) +D(OP_Save) +D(OP_ItemVerifyRequest) +D(OP_GroupInvite) +D(OP_GroupInvite2) +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_WearChange) +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) +#undef E +#undef D diff --git a/common/patches/Underfoot_structs.h b/common/patches/Underfoot_structs.h new file mode 100644 index 000000000..61cd189c7 --- /dev/null +++ b/common/patches/Underfoot_structs.h @@ -0,0 +1,4423 @@ +#ifndef Underfoot_STRUCTS_H_ +#define Underfoot_STRUCTS_H_ + +namespace Underfoot { + namespace structs { + + +static const uint32 BUFF_COUNT = 25; + +/* +** Compiler override to ensure +** byte aligned structures +*/ +#pragma pack(1) + +struct LoginInfo_Struct { +/*000*/ char login_info[64]; +/*064*/ uint8 unknown064[124]; +/*188*/ uint8 zoning; // 01 if zoning, 00 if not +/*189*/ uint8 unknown189[275]; +/*488*/ +}; + +struct EnterWorld_Struct { +/*000*/ char name[64]; +/*064*/ uint32 tutorial; // 01 on "Enter Tutorial", 00 if not +/*068*/ uint32 return_home; // 01 on "Return Home", 00 if not +}; + +//New For SoF +struct WorldObjectsSent_Struct { +}; + +/* Name Approval Struct */ +/* Len: */ +/* Opcode: 0x8B20*/ +struct NameApproval +{ + char name[64]; + uint32 race; + uint32 class_; + uint32 deity; +}; + +/* +** Entity identification struct +** Size: 4 bytes +** OPCodes: OP_DeleteSpawn, OP_Assist +*/ +struct EntityId_Struct +{ +/*00*/ uint32 entity_id; +/*04*/ +}; + +struct Duel_Struct +{ + uint32 duel_initiator; + uint32 duel_target; +}; + +struct DuelResponse_Struct +{ + uint32 target_id; + uint32 entity_id; + uint32 unknown; +}; +/* + Cofruben: + Adventure stuff,not a net one,just one for our use +*/ +static const uint32 ADVENTURE_COLLECT = 0; +static const uint32 ADVENTURE_MASSKILL = 1; +static const uint32 ADVENTURE_NAMED = 2; +static const uint32 ADVENTURE_RESCUE = 3; + +struct AdventureInfo { + uint32 QuestID; + uint32 NPCID; + bool in_use; + uint32 status; + bool ShowCompass; + uint32 Objetive;// can be item to collect,mobs to kill,boss to kill and someone to rescue. + uint32 ObjetiveValue;// number of items,or number of needed mob kills. + char text[512]; + uint8 type; + uint32 minutes; + uint32 points; + float x; + float y; + uint32 zoneid; + uint32 zonedungeonid; +}; +/////////////////////////////////////////////////////////////////////////////// + + +/* +** Color_Struct +** Size: 4 bytes +** Used for convenience +** Merth: Gave struct a name so gcc 2.96 would compile +** +*/ +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 CharSelectEquip { + //totally guessed; + uint32 equip0; + uint32 equip1; + uint32 itemid; + Color_Struct color; +}; + +struct CharacterSelectEntry_Struct { +/*0000*/ uint8 level; // +/*0000*/ uint8 hairstyle; // +/*0002*/ uint8 gender; // +/*0003*/ char name[1]; //variable length, edi+0 +/*0000*/ uint8 beard; // +/*0001*/ uint8 haircolor; // +/*0000*/ uint8 face; // +/*0000*/ CharSelectEquip equip[9]; +/*0000*/ uint32 primary; // +/*0000*/ uint32 secondary; // +/*0000*/ uint8 u15; // 0xff +/*0000*/ uint32 deity; // +/*0000*/ uint16 zone; // +/*0000*/ uint16 instance; +/*0000*/ uint8 gohome; // +/*0000*/ uint8 u19; // 0xff +/*0000*/ uint32 race; // +/*0000*/ uint8 tutorial; // +/*0000*/ uint8 class_; // +/*0000*/ uint8 eyecolor1; // +/*0000*/ uint8 beardcolor; // +/*0000*/ uint8 eyecolor2; // +/*0000*/ uint32 drakkin_heritage; // Drakkin Heritage +/*0000*/ uint32 drakkin_tattoo; // Drakkin Tattoo +/*0000*/ uint32 drakkin_details; // Drakkin Details (Facial Spikes) +/*0000*/ uint8 unknown; // New field to Underfoot + +}; + +/* +** Character Selection Struct +** +*/ +struct CharacterSelect_Struct { +/*0000*/ uint32 char_count; //number of chars in this packet +/*0004*/ uint32 total_chars; //total number of chars allowed? +/*0008*/ CharacterSelectEntry_Struct entries[0]; +}; + +/* +* Visible equiptment. +* Size: 12 Octets +*/ +struct EquipStruct { +/*00*/ uint32 equip0; +/*04*/ uint32 equip1; +/*08*/ uint32 itemId; +/*12*/ +}; + + +/* +** Generic Spawn Struct +** Length: 897 Octets +** Used in: +** spawnZoneStruct +** dbSpawnStruct +** petStruct +** newSpawnStruct +*/ +/* +showeq -> eqemu +sed -e 's/_t//g' -e 's/seto_0xFF/set_to_0xFF/g' +*/ + +struct Spawn_Struct_Bitfields +{ + + unsigned ispet:1; // Could be 'is summoned pet' rather than just is pet. + unsigned afk:1; // 0=no, 1=afk + unsigned sneak:1; + unsigned lfg:1; + unsigned padding5:1; + unsigned invis:1; // 0 = visible, 1 = invis/sneaking + unsigned padding7:11; + unsigned gm:1; + unsigned anon:2; // 0=normal, 1=anon, 2=roleplay + unsigned gender:2; // Gender (0=male, 1=female, 2=monster) + unsigned linkdead:1; // Toggles LD on or off after name + unsigned betabuffed:1; + unsigned showhelm:1; + unsigned padding26:1; + unsigned targetable:1; // 1 = Targetable 0 = Not Targetable (is_npc?) + unsigned targetable_with_hotkey:1; // is_npc? + unsigned showname:1; + unsigned statue:1; + unsigned trader:1; + unsigned buyer:1; +}; + +struct Spawn_Struct_Position +{ +/*0000*/ signed padding0000:12; // ***Placeholder + signed deltaX:13; // change in x + signed padding0005:7; // ***Placeholder +/*0000*/ signed deltaHeading:10;// change in heading + signed deltaY:13; // change in y + signed padding0006:9; // ***Placeholder +/*0000*/ signed y:19; // y coord + signed animation:13; // animation +/*0000*/ unsigned heading:12; // heading + signed x:19; // x coord + signed padding0014:1; // ***Placeholder +/*0000*/ signed z:19; // z coord + signed deltaZ:13; // change in z +}; + +struct Spawn_Struct +{ +// Note this struct is not used as such, it is here for reference. As the struct is variable sized, the packets +// are constructed in Underfoot.cpp +// +/*0000*/ char name[1]; //name[64]; +/*0000*/ //uint8 nullterm1; // hack to null terminate name +/*0064*/ uint32 spawnId; +/*0068*/ uint8 level; +/*0069*/ float unknown1; +/*0073*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse + Spawn_Struct_Bitfields Bitfields; +/*0000*/ uint8 otherData; // & 4 - has title, & 8 - has suffix, & 1 - it's a chest or untargetable +/*0000*/ float unknown3; // seen -1 +/*0000*/ float unknown4; +/*0000*/ float size; +/*0000*/ uint8 face; +/*0000*/ float walkspeed; +/*0000*/ float runspeed; +/*0000*/ uint32 race; +/*0000*/ uint8 showname; // for body types - was charProperties +/*0000*/ uint32 bodytype; +/*0000*/ //uint32 bodytype2; // this is only present if charProperties==2 + // are there more than two possible properties? +/*0000*/ uint8 curHp; +/*0000*/ uint8 haircolor; +/*0000*/ uint8 beardcolor; +/*0000*/ uint8 eyecolor1; +/*0000*/ uint8 eyecolor2; +/*0000*/ uint8 hairstyle; +/*0000*/ uint8 beard; +/*0000*/ uint32 drakkin_heritage; +/*0000*/ uint32 drakkin_tattoo; +/*0000*/ uint32 drakkin_details; +/*0000*/ uint8 statue; // was holding +/*0000*/ uint32 deity; +/*0000*/ uint32 guildID; +/*0000*/ uint32 guildrank; // 0=member, 1=officer, 2=leader, -1=not guilded +/*0000*/ uint8 class_; +/*0000*/ uint8 pvp; // 0 = normal name color, 2 = PVP name color +/*0000*/ uint8 StandState; // stand state - 0x64 for normal animation +/*0000*/ uint8 light; +/*0000*/ uint8 flymode; +/*0000*/ uint8 equip_chest2; +/*0000*/ uint8 unknown9; +/*0000*/ uint8 unknown10; +/*0000*/ uint8 helm; +/*0000*/ char lastName[1]; +/*0000*/ //uint8 lastNameNull; //hack! +/*0000*/ uint32 aatitle; // 0=none, 1=general, 2=archtype, 3=class was AARank +/*0000*/ uint8 unknown12; +/*0000*/ uint32 petOwnerId; +/*0000*/ uint8 unknown13; +/*0000*/ uint32 unknown14; // Stance 64 = normal 4 = aggressive 40 = stun/mezzed +/*0000*/ uint32 unknown15; +/*0000*/ uint32 unknown16; +/*0000*/ uint32 unknown17; +/*0000*/ uint32 unknown18; +/*0000*/ uint32 unknown19; + Spawn_Struct_Position Position; +/*0000*/ union + { + struct + { + /*0000*/ Color_Struct color_helmet; // Color of helmet item + /*0000*/ Color_Struct color_chest; // Color of chest item + /*0000*/ Color_Struct color_arms; // Color of arms item + /*0000*/ Color_Struct color_bracers; // Color of bracers item + /*0000*/ Color_Struct color_hands; // Color of hands item + /*0000*/ Color_Struct color_legs; // Color of legs item + /*0000*/ Color_Struct color_feet; // Color of feet item + /*0000*/ Color_Struct color_primary; // Color of primary item + /*0000*/ Color_Struct color_secondary; // Color of secondary item + } equipment_colors; + /*0000*/ Color_Struct colors[9]; // Array elements correspond to struct equipment_colors above + }; + +// skip these bytes if not a valid player race +/*0000*/ union + { + struct + { + /*0000*/ EquipStruct equip_helmet; // Equiptment: Helmet visual + /*0000*/ EquipStruct equip_chest; // Equiptment: Chest visual + /*0000*/ EquipStruct equip_arms; // Equiptment: Arms visual + /*0000*/ EquipStruct equip_bracers; // Equiptment: Wrist visual + /*0000*/ EquipStruct equip_hands; // Equiptment: Hands visual + /*0000*/ EquipStruct equip_legs; // Equiptment: Legs visual + /*0000*/ EquipStruct equip_feet; // Equiptment: Boots visual + /*0000*/ EquipStruct equip_primary; // Equiptment: Main visual + /*0000*/ EquipStruct equip_secondary; // Equiptment: Off visual + } equip; + /*0000*/ EquipStruct equipment[9]; + }; + +/*0000*/ //char title[0]; // only read if(hasTitleOrSuffix & 4) +/*0000*/ //char suffix[0]; // only read if(hasTitleOrSuffix & 8) + char unknown20[8]; + uint8 IsMercenary; // If NPC == 1 and this == 1, then the NPC name is Orange. +/*0000*/ char unknown21[28]; +}; + + +/* +** Generic Spawn Struct +** Fields from old struct not yet found: +** uint8 traptype; // 65 is disarmable trap, 66 and 67 are invis triggers/traps +** uint8 is_pet; // 0=no, 1=yes +** uint8 afk; // 0=no, 1=afk +** uint8 is_npc; // 0=no, 1=yes +** uint8 max_hp; // (name prolly wrong)takes on the value 100 for players, 100 or 110 for NPCs and 120 for PC corpses... +** uint8 guildrank; // 0=normal, 1=officer, 2=leader +** uint8 eyecolor2; //not sure, may be face +** uint8 aaitle; // 0=none, 1=general, 2=archtype, 3=class +*/ + +/* +** New Spawn +** Length: 176 Bytes +** OpCode: 4921 +*/ +struct NewSpawn_Struct +{ + struct Spawn_Struct spawn; // Spawn Information +}; + + +/* +** Client Zone Entry struct +** Length: 68 Octets +** OpCode: ZoneEntryCode (when direction == client) +*/ +struct ClientZoneEntry_Struct { +/*0000*/ uint32 unknown0000; // ***Placeholder +/*0004*/ char char_name[64]; // Player firstname [32] +//*0036*/ uint8 unknown0036[28]; // ***Placeholder +//*0064*/ uint32 unknown0064; // unknown +}; + + +/* +** Server Zone Entry Struct +** Length: 452 Bytes +** OPCodes: OP_ServerZoneEntry +** +*/ +struct ServerZoneEntry_Struct //Adjusted from SEQ Everquest.h Struct +{ + struct NewSpawn_Struct player; +}; + + +//New Zone Struct - Size: 932 +struct NewZone_Struct { +/*0000*/ char char_name[64]; // Character Name +/*0064*/ char zone_short_name[32]; // Zone Short Name +/*0096*/ char unknown0096[96]; +/*0192*/ char zone_long_name[278]; // Zone Long Name +/*0470*/ uint8 ztype; // Zone type (usually FF) +/*0471*/ uint8 fog_red[4]; // Zone fog (red) +/*0475*/ uint8 fog_green[4]; // Zone fog (green) +/*0479*/ uint8 fog_blue[4]; // Zone fog (blue) +/*0483*/ uint8 unknown323; +/*0484*/ float fog_minclip[4]; +/*0500*/ float fog_maxclip[4]; +/*0516*/ float gravity; +/*0520*/ uint8 time_type; +/*0521*/ uint8 unknown521[49]; +/*0570*/ uint8 sky; // Sky Type +/*0571*/ uint8 unknown571[13]; // ***Placeholder +/*0584*/ float zone_exp_multiplier; // Experience Multiplier +/*0588*/ float safe_y; // Zone Safe Y +/*0592*/ float safe_x; // Zone Safe X +/*0596*/ float safe_z; // Zone Safe Z +/*0600*/ float min_z; // Guessed - NEW - Seen 0 +/*0604*/ float max_z; // Guessed +/*0608*/ float underworld; // Underworld, min z (Not Sure?) +/*0612*/ float minclip; // Minimum View Distance +/*0616*/ float maxclip; // Maximum View DIstance +/*0620*/ uint8 unknown620[84]; // ***Placeholder +/*0704*/ char zone_short_name2[96]; //zone file name? excludes instance number which can be in previous version. +/*0800*/ int32 unknown800; //seen -1 +/*0804*/ char unknown804[40]; // +/*0844*/ int32 unknown844; //seen 600 +/*0848*/ int32 unknown848; +/*0852*/ uint16 zone_id; +/*0854*/ uint16 zone_instance; +/*0856*/ char unknown856[20]; +/*0876*/ uint32 SuspendBuffs; +/*0880*/ uint32 unknown880; //seen 50 +/*0884*/ uint32 unknown884; //seen 10 +/*0888*/ uint8 unknown888; //seen 1 +/*0889*/ uint8 unknown889; //seen 0 (POK) or 1 (rujj) +/*0890*/ uint8 unknown890; //seen 1 +/*0891*/ uint8 unknown891; //seen 0 +/*0892*/ uint8 unknown892; //seen 0 +/*0893*/ uint8 unknown893; //seen 0 - 00 +/*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off +/*0895*/ uint8 unknown895; //seen 0 - 00 +/*0896*/ uint32 unknown896; //seen 180 +/*0900*/ uint32 unknown900; //seen 180 +/*0904*/ uint32 unknown904; //seen 180 +/*0908*/ uint32 unknown908; //seen 2 +/*0912*/ uint32 unknown912; //seen 2 +/*0916*/ float FogDensity; //Of about 10 or so zones tested, all but one have this set to 0.33 Blightfire had 0.16 +/*0920*/ uint32 unknown920; //seen 0 +/*0924*/ uint32 unknown924; //seen 0 +/*0928*/ uint32 unknown928; //seen 0 +/*0932*/ uint8 unknown932[12]; +}; + + +/* +** Memorize Spell Struct +** Length: 16 Bytes +** +*/ +struct MemorizeSpell_Struct { +uint32 slot; // Spot in the spell book/memorized slot +uint32 spell_id; // Spell id (200 or c8 is minor healing, etc) +uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming +//uint32 unknown12; +}; + +/* +** Make Charmed Pet +** Length: 12 Bytes +** +*/ +struct Charm_Struct { +/*00*/ uint32 owner_id; +/*04*/ uint32 pet_id; +/*08*/ uint32 command; // 1: make pet, 0: release pet +/*12*/ +}; + +struct InterruptCast_Struct +{ + uint32 spawnid; + uint32 messageid; + char message[0]; +}; + +struct DeleteSpell_Struct +{ +/*000*/int16 spell_slot; +/*002*/uint8 unknowndss002[2]; +/*004*/uint8 success; +/*005*/uint8 unknowndss006[3]; +/*008*/ +}; + +struct ManaChange_Struct +{ + uint32 new_mana; // New Mana AMount + uint32 stamina; + uint32 spell_id; + uint32 unknown12; + uint32 unknown16; +}; + +struct SwapSpell_Struct +{ + uint32 from_slot; + uint32 to_slot; + + +}; + +struct BeginCast_Struct +{ + // len = 8 +/*004*/ uint16 caster_id; +/*006*/ uint16 spell_id; +/*016*/ uint32 cast_time; // in miliseconds +}; + +struct CastSpell_Struct +{ + uint32 slot; + uint32 spell_id; + uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast + uint32 target_id; + uint32 cs_unknown[5]; +}; + +/* +** SpawnAppearance_Struct +** Changes client appearance for all other clients in zone +** Size: 8 bytes +** Used in: OP_SpawnAppearance +** +*/ +struct SpawnAppearance_Struct +{ +/*0000*/ uint16 spawn_id; // ID of the spawn +/*0002*/ uint16 type; // Values associated with the type +/*0004*/ uint32 parameter; // Type of data sent +/*0008*/ +}; + +// Size 76 (was 24) +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 unknown004; // Seen 1 for no buff +/*008*/ uint32 spellid; +/*012*/ uint32 duration; +/*016*/ uint32 unknown016; +/*020*/ uint32 player_id; // 'global' ID of the caster, for wearoff messages +/*024*/ uint32 counters; +/*028*/ uint8 unknown0028[48]; +/*076*/ +}; + +// Not functional yet, but this is what the packet looks like on Underfoot +struct SpellBuffFade_Struct_Underfoot { +/*000*/ uint32 entityid; // Player id who cast the buff +/*004*/ uint8 slot; +/*005*/ uint8 level; +/*006*/ uint8 effect; +/*007*/ uint8 unknown7; +/*008*/ float unknown008; +/*012*/ uint32 spellid; +/*016*/ uint32 duration; +/*020*/ uint32 unknown016; +/*024*/ uint32 playerId; // Global player ID? +/*028*/ uint32 unknown020; +/*032*/ uint8 unknown0028[48]; +/*080*/ uint32 slotid; +/*084*/ uint32 bufffade; +/*088*/ +}; + +struct SpellBuffFade_Struct { +/*000*/ uint32 entityid; +/*004*/ uint8 slot; +/*005*/ uint8 level; +/*006*/ uint8 effect; +/*007*/ uint8 unknown7; +/*008*/ uint32 spellid; +/*012*/ uint32 duration; +/*016*/ uint32 unknown016; +/*020*/ uint32 unknown020; // Global player ID? +/*024*/ uint32 playerId; // Player id who cast the buff +/*028*/ uint32 slotid; +/*032*/ uint32 bufffade; +/*036*/ +}; + +struct BuffRemoveRequest_Struct +{ +/*00*/ uint32 SlotID; +/*04*/ uint32 EntityID; +/*08*/ + }; + +struct GMTrainee_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ uint32 skills[73]; + /*300*/ uint8 unknown300[148]; + /*448*/ +}; + +struct GMTrainEnd_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ +}; + +struct GMSkillChange_Struct { +/*000*/ uint16 npcid; +/*002*/ uint8 unknown1[2]; // something like PC_ID, but not really. stays the same thru the session though +/*002*/ uint16 skillbank; // 0 if normal skills, 1 if languages +/*002*/ uint8 unknown2[2]; +/*008*/ uint16 skill_id; +/*010*/ uint8 unknown3[2]; +}; +struct ConsentResponse_Struct { + char grantname[64]; + char ownername[64]; + uint8 permission; + char zonename[64]; +}; + +/* +** Name Generator Struct +** Length: 72 bytes +** OpCode: 0x0290 +*/ +struct NameGeneration_Struct +{ +/*0000*/ uint32 race; +/*0004*/ uint32 gender; +/*0008*/ char name[64]; +/*0072*/ +}; + +/* +** Character Creation struct +** Length: 140 Bytes +** OpCode: 0x009b +*/ +struct CharCreate_Struct +{ +/*0000*/ uint32 class_; +/*0004*/ uint32 haircolor; +/*0008*/ uint32 beard; +/*0012*/ uint32 beardcolor; +/*0016*/ uint32 gender; +/*0020*/ uint32 race; +/*0024*/ uint32 start_zone; +/*0028*/ uint32 hairstyle; +/*0032*/ uint32 deity; +/*0036*/ uint32 STR; +/*0040*/ uint32 STA; +/*0044*/ uint32 AGI; +/*0048*/ uint32 DEX; +/*0052*/ uint32 WIS; +/*0056*/ uint32 INT; +/*0060*/ uint32 CHA; +/*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 tutorial; +/*0080*/ uint32 drakkin_heritage; +/*0084*/ uint32 drakkin_tattoo; +/*0088*/ uint32 drakkin_details; +/*0092*/ +}; + +/* +** Character Creation struct +** Length: 0 Bytes +** OpCode: 0x +*/ +struct CharCreate_Struct_Temp //Size is now 0 +{ +}; + +/* + *Used in PlayerProfile + */ +struct AA_Array +{ + uint32 AA; + uint32 value; + uint32 unknown08; // Looks like AA_Array is now 12 bytes in Underfoot +}; + + +static const uint32 MAX_PP_DISCIPLINES = 200; + +struct Disciplines_Struct { + uint32 values[MAX_PP_DISCIPLINES]; +}; + +static const uint32 MAX_PLAYER_TRIBUTES = 5; +static const uint32 MAX_PLAYER_BANDOLIER = 20; +static const uint32 MAX_PLAYER_BANDOLIER_ITEMS = 4; +static const uint32 MAX_POTIONS_IN_BELT = 5; +static const uint32 TRIBUTE_NONE = 0xFFFFFFFF; + +struct Tribute_Struct { + uint32 tribute; + uint32 tier; +}; + +//len = 72 +struct BandolierItem_Struct { + uint32 item_id; + uint32 icon; + char item_name[64]; +}; + +//len = 320 +enum { //bandolier item positions + bandolierMainHand = 0, + bandolierOffHand, + bandolierRange, + bandolierAmmo +}; +struct Bandolier_Struct { + char name[32]; + BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; +}; +struct PotionBelt_Struct { + BandolierItem_Struct items[MAX_POTIONS_IN_BELT]; +}; + +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]; +}; +struct RaidLeadershipAA_Struct { + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; +}; + + /** +* A bind point. +* Size: 20 Octets +*/ +struct BindStruct { + /*000*/ uint32 zoneId; + /*004*/ float x; + /*008*/ float y; + /*012*/ float z; + /*016*/ float heading; + /*020*/ +}; + + +/* +** Player Profile +** +** Length: 4308 bytes +** 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_MEMSPELL = 10; //was 9 now 10 on Underfoot +static const uint32 MAX_PP_SKILL = 75; +static const uint32 MAX_PP_AA_ARRAY = 300; //was 299 +static const uint32 MAX_GROUP_MEMBERS = 6; +static const uint32 MAX_RECAST_TYPES = 20; +/* +showeq -> eqemu +sed -e 's/_t//g' -e 's/MAX_AA/MAX_PP_AA_ARRAY/g' \ + -e 's/MAX_SPELL_SLOTS/MAX_PP_MEMSPELL/g' \ + -e 's/MAX_KNOWN_SKILLS/MAX_PP_SKILL/g' \ + -e 's/MAXRIBUTES/MAX_PLAYER_TRIBUTES/g' \ + -e 's/MAX_BUFFS/BUFF_COUNT/g' \ + -e 's/MAX_KNOWN_LANGS/MAX_PP_LANGUAGE/g' \ + -e 's/MAX_RECASTYPES/MAX_RECAST_TYPES/g' \ + -e 's/spellBuff/SpellBuff_Struct/g' \ + -e 's/lastName/last_name/g' \ + -e 's/guildID/guild_id/g' \ + -e 's/itemint/item_tint/g' \ + -e 's/MANA/mana/g' \ + -e 's/curHp/cur_hp/g' \ + -e 's/sSpellBook/spell_book/g' \ + -e 's/sMemSpells/mem_spells/g' \ + -e 's/uint32[ \t]*disciplines\[MAX_DISCIPLINES\]/Disciplines_Struct disciplines/g' \ + -e 's/aa_unspent/aapoints/g' \ + -e 's/aa_spent/aapoints_spent/g' \ + -e 's/InlineItem[ \t]*potionBelt\[MAX_POTIONS_IN_BELT\]/PotionBelt_Struct potionbelt/g' \ + -e 's/ldon_guk_points/ldon_points_guk/g' \ + -e 's/ldon_mir_points/ldon_points_mir/g' \ + -e 's/ldon_mmc_points/ldon_points_mmc/g' \ + -e 's/ldon_ruj_points/ldon_points_ruj/g' \ + -e 's/ldonak_points/ldon_points_tak/g' \ + -e 's/ldon_avail_points/ldon_points_available/g' \ + -e 's/tributeTime/tribute_time_remaining/g' \ + -e 's/careerTribute/career_tribute_points/g' \ + -e 's/currentTribute/tribute_points/g' \ + -e 's/tributeActive/tribute_active/g' \ + -e 's/TributeStruct/Tribute_Struct/g' \ + -e 's/expGroupLeadAA/group_leadership_exp/g' \ + -e 's/expRaidLeadAA/raid_leadership_exp/g' \ + -e 's/groupLeadAAUnspent/group_leadership_points/g' \ + -e 's/raidLeadAAUnspent/raid_leadership_points/g' \ + -e 's/uint32[ \t]*leadershipAAs\[MAX_LEAD_AA\]/LeadershipAA_Struct leader_abilities/g' \ + -e 's/BandolierStruct/Bandolier_Struct/g' \ + -e 's/MAX_BANDOLIERS/MAX_PLAYER_BANDOLIER/g' \ + -e 's/birthdayTime/birthday/g' \ + -e 's/lastSaveTime/lastlogin/g' \ + -e 's/zoneId/zone_id/g' \ + -e 's/hunger/hunger_level/g' \ + -e 's/thirst/thirst_level/g' \ + -e 's/guildstatus/guildrank/g' \ + -e 's/airRemaining/air_remaining/g' \ + */ + +// Underfoot May 5 2010 - Size 25312 + 1320 +// Underfoot May 12 2010 - Size 26632 +struct PlayerProfile_Struct +{ +/*00000*/ uint32 checksum; // +//BEGIN SUB-STRUCT used for shrouding stuff... +/*00004*/ uint32 gender; // Player Gender - 0 Male, 1 Female +/*00008*/ uint32 race; // Player race +/*00012*/ uint32 class_; // Player class +/*00016*/ uint8 unknown00016[40]; // #### uint32 unknown00016; in Titanium ####uint8[40] +/*00056*/ uint8 level; // Level of player +/*00057*/ uint8 level1; // Level of player (again?) +/*00058*/ uint8 unknown00058[2]; // ***Placeholder +/*00060*/ BindStruct binds[5]; // Bind points (primary is first) +/*00160*/ uint32 deity; // deity +/*00164*/ uint32 intoxication; // Alcohol level (in ticks till sober?) +/*00168*/ uint32 spellSlotRefresh[MAX_PP_MEMSPELL]; // Refresh time (millis) - 4 Octets Each +/*00208*/ uint8 unknown00208[14]; // Seen 00 00 00 00 00 00 00 00 00 00 00 00 02 01 +/*00222*/ uint32 abilitySlotRefresh; +/*00226*/ uint8 haircolor; // Player hair color +/*00227*/ uint8 beardcolor; // Player beard color +/*00228*/ uint8 eyecolor1; // Player left eye color +/*00229*/ uint8 eyecolor2; // Player right eye color +/*00230*/ uint8 hairstyle; // Player hair style +/*00231*/ uint8 beard; // Player beard type +/*00232*/ uint8 unknown00232[4]; // was 14 +/*00236*/ union + { + struct + { + /*00236*/ EquipStruct equip_helmet; // Equiptment: Helmet visual + /*00248*/ EquipStruct equip_chest; // Equiptment: Chest visual + /*00260*/ EquipStruct equip_arms; // Equiptment: Arms visual + /*00272*/ EquipStruct equip_bracers; // Equiptment: Wrist visual + /*00284*/ EquipStruct equip_hands; // Equiptment: Hands visual + /*00296*/ EquipStruct equip_legs; // Equiptment: Legs visual + /*00308*/ EquipStruct equip_feet; // Equiptment: Boots visual + /*00320*/ EquipStruct equip_primary; // Equiptment: Main visual + /*00332*/ EquipStruct equip_secondary; // Equiptment: Off visual + } equip; + /*00236*/ EquipStruct equipment[9]; //Underfoot Shows [108] for this part + }; +/*00344*/ uint8 unknown00344[168]; // Underfoot Shows [160] +/*00512*/ Color_Struct item_tint[9]; // RR GG BB 00 +/*00548*/ AA_Array aa_array[MAX_PP_AA_ARRAY]; // [3600] AAs 12 bytes each +/*04148*/ uint32 points; // Unspent Practice points - RELOCATED??? +/*04152*/ uint32 mana; // Current mana +/*04156*/ uint32 cur_hp; // Current HP without +HP equipment +/*04160*/ uint32 STR; // Strength - 6e 00 00 00 - 110 +/*04164*/ uint32 STA; // Stamina - 73 00 00 00 - 115 +/*04168*/ uint32 CHA; // Charisma - 37 00 00 00 - 55 +/*04172*/ uint32 DEX; // Dexterity - 50 00 00 00 - 80 +/*04176*/ uint32 INT; // Intelligence - 3c 00 00 00 - 60 +/*04180*/ uint32 AGI; // Agility - 5f 00 00 00 - 95 +/*04184*/ uint32 WIS; // Wisdom - 46 00 00 00 - 70 +/*04188*/ uint8 unknown04188[28]; // +/*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 +/*07316*/ uint32 gold; // Gold Pieces on player +/*07320*/ uint32 silver; // Silver Pieces on player +/*07324*/ uint32 copper; // Copper Pieces on player +/*07328*/ uint32 platinum_cursor; // Platinum Pieces on cursor +/*07332*/ uint32 gold_cursor; // Gold Pieces on cursor +/*07336*/ uint32 silver_cursor; // Silver Pieces on cursor +/*07340*/ uint32 copper_cursor; // Copper Pieces on cursor +/*07344*/ uint32 skills[MAX_PP_SKILL]; // [300] List of skills +/*07644*/ uint8 unknown07644[236]; +/*07880*/ uint32 toxicity; // Potion Toxicity (15=too toxic, each potion adds 3) +/*07884*/ uint32 thirst_level; // Drink (ticks till next drink) +/*07888*/ uint32 hunger_level; // Food (ticks till next eat) +/*07892*/ SpellBuff_Struct buffs[BUFF_COUNT]; // [1900] Buffs currently on the player (30 Max) - (Each Size 76) +/*09792*/ uint8 unknown09792[380]; // BUFF_COUNT has been left at 25. These are the extra 5 buffs in Underfoot +/*10172*/ Disciplines_Struct disciplines; // [400] Known disciplines +/*10972*/ uint32 recastTimers[MAX_RECAST_TYPES]; // Timers (UNIX Time of last use) +/*11052*/ uint8 unknown11052[160]; // Some type of Timers +/*11212*/ uint32 endurance; // Current endurance +/*11216*/ uint8 unknown11216[20]; // ? +/*11236*/ uint32 aapoints_spent; // Number of spent AA points +/*11240*/ uint32 aapoints; // Unspent AA points +/*11244*/ uint8 unknown11244[4]; +/*11248*/ Bandolier_Struct bandoliers[MAX_PLAYER_BANDOLIER]; // [6400] bandolier contents +/*17648*/ PotionBelt_Struct potionbelt; // [360] potion belt 72 extra octets by adding 1 more belt slot +/*18008*/ uint8 unknown18008[8]; +/*18016*/ uint32 available_slots; +/*18020*/ uint8 unknown18020[80]; // +//END SUB-STRUCT used for shrouding. +/*18100*/ char name[64]; // Name of player +/*18164*/ char last_name[32]; // Last name of player +/*18196*/ uint8 unknown18196[8]; //#### Not In Titanium #### new to SoF[12] +/*18204*/ uint32 guild_id; // guildid +/*18208*/ uint32 birthday; // character birthday +/*18212*/ uint32 lastlogin; // character last save time +/*18216*/ uint32 account_startdate; // Date the Account was started - New Field for Underfoot*** +/*18220*/ uint32 timePlayedMin; // time character played +/*18224*/ uint8 pvp; // 1=pvp, 0=not pvp +/*18225*/ uint8 anon; // 2=roleplay, 1=anon, 0=not anon +/*18226*/ uint8 gm; // 0=no, 1=yes (guessing!) +/*18227*/ uint8 guildrank; // 0=member, 1=officer, 2=guildleader -1=no guild +/*18228*/ uint32 guildbanker; +/*18232*/ uint8 unknown18232[4]; //was [8] +/*18236*/ uint32 exp; // Current Experience +/*18240*/ uint8 unknown18240[8]; +/*18248*/ uint32 timeentitledonaccount; +/*18252*/ uint8 languages[MAX_PP_LANGUAGE]; // List of languages +/*18277*/ uint8 unknown18277[7]; //#### uint8 unknown13109[4]; in Titanium ####[7] +/*18284*/ float y; // Players y position (NOT positive about this switch) +/*18288*/ float x; // Players x position +/*18292*/ float z; // Players z position +/*18296*/ float heading; // Players heading +/*18300*/ uint8 unknown18300[4]; // ***Placeholder +/*18304*/ uint32 platinum_bank; // Platinum Pieces in Bank +/*18308*/ uint32 gold_bank; // Gold Pieces in Bank +/*18312*/ uint32 silver_bank; // Silver Pieces in Bank +/*18316*/ uint32 copper_bank; // Copper Pieces in Bank +/*18320*/ uint32 platinum_shared; // Shared platinum pieces +/*18324*/ uint8 unknown18324[1036]; // was [716] +/*19360*/ uint32 expansions; // Bitmask for expansions ff 7f 00 00 - SoD +/*19364*/ uint8 unknown19364[12]; +/*19376*/ uint32 autosplit; // 0 = off, 1 = on +/*19380*/ uint8 unknown19380[16]; +/*19396*/ uint16 zone_id; // see zones.h +/*19398*/ uint16 zoneInstance; // Instance id +/*19400*/ char groupMembers[MAX_GROUP_MEMBERS][64];// 384 all the members in group, including self +/*19784*/ char groupLeader[64]; // Leader of the group ? +/*19848*/ uint8 unknown19848[540]; // was [348] +/*20388*/ uint32 entityid; +/*20392*/ uint32 leadAAActive; // 0 = leader AA off, 1 = leader AA on +/*20396*/ uint8 unknown20396[4]; +/*20400*/ int32 ldon_points_guk; // Earned GUK points +/*20404*/ int32 ldon_points_mir; // Earned MIR points +/*20408*/ int32 ldon_points_mmc; // Earned MMC points +/*20412*/ int32 ldon_points_ruj; // Earned RUJ points +/*20416*/ int32 ldon_points_tak; // Earned TAK points +/*20420*/ int32 ldon_points_available; // Available LDON points +/*20424*/ uint32 unknown20424[7]; +/*20452*/ uint32 unknown20452; +/*20456*/ uint32 unknown20456; +/*20460*/ uint8 unknown20460[4]; +/*20464*/ uint32 unknown20464[6]; +/*20488*/ uint8 unknown20488[72]; // was [136] +/*20560*/ float tribute_time_remaining; // Time remaining on tribute (millisecs) +/*20564*/ uint32 career_tribute_points; // Total favor points for this char +/*20568*/ uint32 unknown20546; // *** Placeholder +/*20572*/ uint32 tribute_points; // Current tribute points +/*20576*/ uint32 unknown20572; // *** Placeholder +/*20580*/ uint32 tribute_active; // 0 = off, 1=on +/*20584*/ Tribute_Struct tributes[MAX_PLAYER_TRIBUTES]; // [40] Current tribute loadout +/*20624*/ uint8 unknown20620[4]; +/*20628*/ double group_leadership_exp; // Current group lead exp points +/*20636*/ double raid_leadership_exp; // Current raid lead AA exp points +/*20644*/ uint32 group_leadership_points; // Unspent group lead AA points +/*20648*/ uint32 raid_leadership_points; // Unspent raid lead AA points +/*20652*/ LeadershipAA_Struct leader_abilities; // [128]Leader AA ranks 19332 +/*20780*/ uint8 unknown20776[128]; // was [128] +/*20908*/ uint32 air_remaining; // Air supply (seconds) +/*20912*/ uint32 PVPKills; +/*20916*/ uint32 PVPDeaths; +/*20920*/ uint32 PVPCurrentPoints; +/*20924*/ uint32 PVPCareerPoints; +/*20928*/ uint32 PVPBestKillStreak; +/*20932*/ uint32 PVPWorstDeathStreak; +/*20936*/ uint32 PVPCurrentKillStreak; +/*20940*/ PVPStatsEntry_Struct PVPLastKill; // size 88 +/*21028*/ PVPStatsEntry_Struct PVPLastDeath; // size 88 +/*21116*/ uint32 PVPNumberOfKillsInLast24Hours; +/*21120*/ PVPStatsEntry_Struct PVPRecentKills[50]; // size 4400 - 88 each +/*25520*/ uint32 expAA; // Exp earned in current AA point +/*25524*/ uint8 unknown25524[40]; +/*25564*/ uint32 currentRadCrystals; // Current count of radiant crystals +/*25568*/ uint32 careerRadCrystals; // Total count of radiant crystals ever +/*25572*/ uint32 currentEbonCrystals; // Current count of ebon crystals +/*25576*/ uint32 careerEbonCrystals; // Total count of ebon crystals ever +/*25580*/ uint8 groupAutoconsent; // 0=off, 1=on +/*25581*/ uint8 raidAutoconsent; // 0=off, 1=on +/*25582*/ uint8 guildAutoconsent; // 0=off, 1=on +/*25583*/ uint8 unknown25583; // ***Placeholder (6/29/2005) +/*25584*/ uint32 level3; // SoF looks at the level here to determine how many leadership AA you can bank. +/*25588*/ uint32 showhelm; // 0=no, 1=yes +/*25592*/ uint32 RestTimer; +/*25596*/ uint8 unknown25596[1036]; // ***Placeholder (2/13/2007) was[1028]or[940]or[1380] - END of Struct +/*26632*/ +}; + +/** + * Shroud spawn. For others shrouding, this has their spawnId and + * spawnStruct. + * + * Length: 586 + * OpCode: OP_Shroud + */ +//struct spawnShroudOther_Struct +//{ +//*0000*/ uint32 spawnId; // Spawn Id of the shrouded player +//*0004*/ spawn_Struct spawn; // Updated spawn struct for the player +//*0586*/ +//}; + +/** + * Shroud yourself. For yourself shrouding, this has your spawnId, spawnStruct, + * bits of your charProfileStruct (no checksum, then charProfile up till + * but not including name), and an itemPlayerPacket for only items on the player + * and not the bank. + * + * Length: Variable + * OpCode: OP_Shroud + */ +//struct spawnShroudSelf_Struct +//{ +//*00000*/ uint32 spawnId; // Spawn Id of you +//*00004*/ spawn_Struct spawn; // Updated spawnStruct for you +//*00586*/ PlayerProfile_Struct profile; // Character profile for shrouded char +//*13522*/ uint8 items; // Items on the player +/*xxxxx*/ +//}; + + + +/* +** Client Target Struct +** Length: 2 Bytes +** OpCode: 6221 +*/ +struct ClientTarget_Struct { +/*000*/ uint32 new_target; // Target ID +}; + +/* +** Target Rejection Struct +** Length: 12 Bytes +** OpCode: OP_TargetReject +*/ +struct TargetReject_Struct { +/*00*/ uint8 unknown00[12]; +}; + +struct PetCommand_Struct { +/*000*/ uint32 command; +/*004*/ uint32 unknown; +}; + +/* +** Delete Spawn +** Length: 4 Bytes +** OpCode: OP_DeleteSpawn +*/ +struct DeleteSpawn_Struct +{ +/*00*/ uint32 spawn_id; // Spawn ID to delete +/*04*/ +}; + +/* +** Channel Message received or sent +** Length: 144 Bytes + Variable Length + 1 +** OpCode: OP_ChannelMessage +** +*/ +struct ChannelMessage_Struct +{ +/*000*/ char targetname[64]; // Tell recipient +/*064*/ char sender[64]; // The senders name (len might be wrong) +/*128*/ uint32 language; // Language +/*132*/ uint32 chan_num; // Channel +/*136*/ uint32 cm_unknown4[2]; // ***Placeholder +/*144*/ uint32 skill_in_language; // The players skill in this language? might be wrong +/*148*/ char message[0]; // Variable length message +}; + +/* +** Special Message +** Length: 4 Bytes + Variable Text Length + 1 +** OpCode: OP_SpecialMesg +** +*/ +/* + Theres something wrong with this... example live packet: +Server->Client: [ Opcode: OP_SpecialMesg (0x0fab) Size: 244 ] + 0: 01 02 00 0A 00 00 00 09 - 05 00 00 42 61 72 73 74 | ...........Barst + 16: 72 65 20 53 6F 6E 67 77 - 65 61 76 65 72 00 7C F9 | re Songweaver.|. + 32: FF FF 84 FF FF FF 03 00 - 00 00 47 72 65 65 74 69 | ..........Greeti + +*/ +struct SpecialMesg_Struct +{ +/*00*/ char header[3]; // 04 04 00 <-- for #emote style msg +/*03*/ uint32 msg_type; // Color of text (see MT_*** below) +/*07*/ uint32 target_spawn_id; // Who is it being said to? +/*11*/ char sayer[1]; // Who is the source of the info - Was 1 +/*12*/ uint8 unknown12[12]; +/*24*/ char message[128]; // What is being said? - was 128 +}; + +/* +** When somebody changes what they're wearing +** or give a pet a weapon (model changes) +** Length: 19 Bytes +*/ +struct WearChange_Struct{ +/*000*/ uint16 spawn_id; +/*002*/ uint32 material; +/*006*/ uint32 unknown06; +/*010*/ uint32 elite_material; // 1 for Drakkin Elite Material +/*014*/ Color_Struct color; +/*018*/ uint8 wear_slot_id; +/*019*/ +}; + +/* +** Type: Bind Wound Structure +** Length: 8 Bytes +*/ +//Fixed for 7-14-04 patch +struct BindWound_Struct +{ +/*000*/ uint16 to; // TargetID +/*002*/ uint16 unknown2; // ***Placeholder +/*004*/ uint16 type; +/*006*/ uint16 unknown6; +}; + + +/* +** Type: Zone Change Request (before hand) +** Length: 88 bytes +** OpCode: a320 +*/ + +struct ZoneChange_Struct { +/*000*/ char char_name[64]; // Character Name +/*064*/ uint16 zoneID; +/*066*/ uint16 instanceID; +/*068*/ float y; +/*072*/ float x; +/*076*/ float z; +/*080*/ uint32 zone_reason; //0x0A == death, I think +/*084*/ int32 success; // =0 client->server, =1 server->client, -X=specific error +/*088*/ +}; + +struct RequestClientZoneChange_Struct { +/*00*/ uint16 zone_id; +/*02*/ uint16 instance_id; +/*04*/ float y; +/*08*/ float x; +/*12*/ float z; +/*16*/ float heading; +/*20*/ uint32 type; //unknown... values +}; + +struct Animation_Struct { +/*00*/ uint16 spawnid; +/*02*/ uint8 action; +/*03*/ uint8 value; +/*04*/ +}; + +// solar: this is what causes the caster to animate and the target to +// get the particle effects around them when a spell is cast +// also causes a buff icon +struct Action_Struct +{ +/*00*/ uint16 target; // id of target +/*02*/ uint16 source; // id of caster +/*04*/ uint16 level; // level of caster - Seen 0 +/*06*/ uint32 unknown06; +/*10*/ uint16 instrument_focus; +/*12*/ uint16 unknown12; // seems to always be set to something and it doesn't change between casts except in special cases like changing instrument mods +/*14*/ uint32 unknown14; // seen 0 +/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again +/*22*/ uint32 unknown22; +/*26*/ uint8 type; +/*27*/ uint32 damage; +/*31*/ uint16 unknown31; +/*33*/ uint16 spell; // spell id being cast +/*35*/ uint8 level2; // level of caster again? Or maybe the castee +/*36*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? +}; + + + +// Starting with 2/21/2006, OP_Actions seem to come in pairs, duplicating +// themselves, with the second one with slightly more information. Maybe this +// has to do with buff blocking?? +struct ActionAlt_Struct +{ +/*00*/ uint16 target; // id of target +/*02*/ uint16 source; // id of caster +/*04*/ uint16 level; // level of caster - Seen 0 +/*06*/ uint32 unknown06; +/*10*/ float instrument_mod; +/*14*/ uint32 unknown14; // seen 0 +/*18*/ float knockback_angle; //seems to go from 0-512 then it rolls over again +/*22*/ uint32 unknown22; +/*26*/ uint8 type; +/*27*/ uint32 damage; +/*31*/ uint16 unknown31; +/*33*/ uint16 spell; // spell id being cast +/*35*/ uint8 level2; // level of caster again? Or maybe the castee +/*36*/ uint8 effect_flag; // if this is 4, the effect is valid: or if two are sent at the same time? +/*37*/ uint32 unknown37; // New field to Underfoot - Seen 14 +/*41*/ uint8 unknown41; // New field to Underfoot - Seen 0 +/*42*/ uint8 unknown42; // New field to Underfoot - Seen 0 +/*43*/ uint8 unknown43; // New field to Underfoot - Seen 0 +/*44*/ uint32 unknown44; // New field to Underfoot - Seen 23 +/*48*/ uint32 unknown48; // New field to Underfoot - Seen -1 +/*52*/ uint32 unknown52; // New field to Underfoot - Seen -1 +/*56*/ uint32 unknown56; // New field to Underfoot - Seen 0 +/*60*/ uint32 unknown60; // New field to Underfoot - Seen 0 +/*64*/ +}; + +// solar: this is what prints the You have been struck. and the regular +// melee messages like You try to pierce, etc. It's basically the melee +// and spell damage message +struct CombatDamage_Struct +{ +/* 00 */ uint16 target; +/* 02 */ uint16 source; +/* 04 */ uint8 type; //slashing, etc. 231 (0xE7) for spells +/* 05 */ uint16 spellid; +/* 07 */ int32 damage; +/* 11 */ float unknown11; // cd cc cc 3d +/* 15 */ float sequence; // see above notes in Action_Struct +/* 19 */ uint8 unknown19[9]; // was [9] +/* 28 */ +}; + + +/* +** Consider Struct +** Length: 20 Bytes +*/ +struct Consider_Struct{ +/*000*/ uint32 playerid; // PlayerID +/*004*/ uint32 targetid; // TargetID +/*008*/ uint32 faction; // Faction +/*012*/ uint32 level; // Level +/*016*/ uint8 pvpcon; // Pvp con flag 0/1 +/*017*/ uint8 unknown017[3]; // +/*020*/ +}; + +/* +** Spawn Death Blow +** Length: 32 Bytes +** OpCode: 0114 +*/ +struct Death_Struct +{ +/*000*/ uint32 spawn_id; +/*004*/ uint32 killer_id; +/*008*/ uint32 corpseid; // was corpseid +/*012*/ uint32 attack_skill; // was type +/*016*/ uint32 spell_id; +/*020*/ uint32 bindzoneid; //bindzoneid? +/*024*/ uint32 damage; +/*028*/ uint32 unknown028; +}; + +struct BecomeCorpse_Struct { + uint32 spawn_id; + float y; + float x; + float z; +}; + +struct ZonePlayerToBind_Struct { +/*000*/ uint16 bind_zone_id; +/*002*/ uint16 bind_instance_id; +/*004*/ float x; +/*008*/ float y; +/*012*/ float z; +/*016*/ float heading; +/*020*/ char zone_name[1]; // Or "Bind Location" +/*000*/ uint8 unknown021; // Seen 1 - Maybe 0 would be to force a rezone and 1 is just respawn +/*000*/ uint32 unknown022; // Seen 32 or 59 +/*000*/ uint32 unknown023; // Seen 0 +/*000*/ uint32 unknown024; // Seen 21 or 43 +}; + +struct ZonePlayerToBindHeader_Struct +{ + /*000*/ uint16 bind_zone_id; + /*002*/ uint16 bind_instance_id; + /*004*/ float x; + /*008*/ float y; + /*012*/ float z; + /*016*/ float heading; + /*020*/ char zone_name[1]; // Or "Bind Location" +}; + +struct ZonePlayerToBindFooter_Struct +{ + /*000*/ uint8 unknown021; // Seen 1 - Maybe 0 would be to force a rezone and 1 is just respawn + /*000*/ uint32 unknown022; // Seen 32 or 59 + /*000*/ uint32 unknown023; // Seen 0 + /*000*/ uint32 unknown024; // Seen 21 or 43 +}; + +typedef struct { +/*000*/ uint32 bind_number; // Number of this bind in the iteration +/*004*/ uint32 bind_zone_id; // ID of the zone for this bind point or resurect point +/*008*/ float x; // X loc for this bind point +/*012*/ float y; // Y loc for this bind point +/*016*/ float z; // Z loc for this bind point +/*020*/ float heading; // Heading for this bind point +/*024*/ char bind_zone_name[1]; // Or "Bind Location" or "Resurrect" +/*000*/ uint8 validity; // 0 = valid choice, 1 = not a valid choice at this time (resurrection) +} RespawnOptions_Struct; + +struct RespawnWindow_Struct { +/*000*/ uint32 unknown000; // Seen 0 +/*004*/ uint32 time_remaining; // Total time before respawn in milliseconds +/*008*/ uint32 unknown008; // Seen 0 +/*012*/ uint32 total_binds; // Total Bind Point Options? - Seen 2 +/*016*/ RespawnOptions_Struct bind_points; +// First bind point is "Bind Location" and the last one is "Ressurect" +}; + + +/* +** Spawn position update - Size: 22 +** Struct sent from server->client to update position of +** another spawn's position update in zone (whether NPC or PC) +** +*/ +struct PlayerPositionUpdateServer_Struct +{ +/*0000*/ uint16 spawn_id; +/*0002*/ signed padding0000:12; // ***Placeholder + signed delta_x:13; // change in x + signed padding0005:7; // ***Placeholder +/*0006*/ signed delta_heading:10;// change in heading + signed delta_y:13; // change in y + signed padding0006:9; // ***Placeholder +/*0010*/ signed y_pos:19; // y coord + signed animation:10; // animation + signed padding0010:3; // ***Placeholder +/*0014*/ unsigned heading:12; // heading + signed x_pos:19; // x coord + signed padding0014:1; // ***Placeholder +/*0018*/ signed z_pos:19; // z coord + signed delta_z:13; // change in z +/*0022*/ +}; + + +/* +** Player position update - Size: 40 +** Struct sent from client->server to update +** player position on server +** +*/ +struct PlayerPositionUpdateClient_Struct +{ +/*0000*/ uint16 spawn_id; // Player's spawn id +/*0002*/ uint16 sequence; // increments one each packet - Verified +/*0004*/ uint8 unknown0004[4]; // ***Placeholder +/*0008*/ float x_pos; // x coord (2nd loc value) +/*0012*/ float y_pos; // y coord (1st loc value) +/*0016*/ signed delta_heading:10; // change in heading + unsigned padding0036:10; // animation + unsigned padding0016:12; // ***Placeholder +/*0020*/ float delta_x; // Change in x +/*0024*/ float delta_y; // Change in y +/*0028*/ float z_pos; // z coord (3rd loc value) +/*0032*/ float delta_z; // Change in z +/*0036*/ unsigned animation:10; // ***Placeholder + unsigned heading:12; // Directional heading + unsigned padding0037:10; // ***Placeholder +/*0040*/ +}; + +/* +** Spawn HP Update +** Length: 10 Bytes +** OpCode: OP_HPUpdate +*/ +struct SpawnHPUpdate_Struct +{ +/*00*/ uint32 cur_hp; // Id of spawn to update +/*04*/ int32 max_hp; // Maximum hp of spawn +/*08*/ int16 spawn_id; // Current hp of spawn +/*10*/ +}; + +/* +** SendExpZonein +** Length: 152 Bytes +** OpCode: OP_SendExpZonein +*/ +struct SendExpZonein_Struct +{ +/*0000*/ uint16 spawn_id; // ID of the spawn +/*0002*/ uint16 type; // Values associated with the type +/*0004*/ uint32 parameter; // Type of data sent +/*0008*/ uint32 exp; // Current experience ratio from 0 to 330 +/*0012*/ uint32 expAA; +/*0016*/ uint8 unknown0016[4]; +/*0020*/ char name[64]; +/*0084*/ char last_name[64]; +/*00148*/ uint32 unknown132; +/*00152*/ +}; + +/* +** SendExpZonein +** Length: 0 Bytes +** OpCode: OP_SendExpZonein +*/ +//struct SendExpZonein_Struct {}; + +struct SpawnHPUpdate_Struct2 +{ +/*01*/ int16 spawn_id; +/*00*/ uint8 hp; +}; +/* +** Stamina +** Length: 8 Bytes +** OpCode: 5721 +*/ +struct Stamina_Struct { +/*00*/ uint32 food; // (low more hungry 127-0) +/*02*/ uint32 water; // (low more thirsty 127-0) +}; + +/* +** Level Update +** Length: 12 Bytes +*/ +struct LevelUpdate_Struct +{ +/*00*/ uint32 level; // New level +/*04*/ uint32 level_old; // Old level +/*08*/ uint32 exp; // Current Experience +}; + +/* +** Experience Update +** Length: 14 Bytes +** OpCode: 9921 +*/ +struct ExpUpdate_Struct +{ +/*0000*/ uint32 exp; // Current experience ratio from 0 to 330 +/*0004*/ uint32 aaxp; // @BP ?? +}; + +/* +** Item Packet Struct - Works on a variety of opcodes +** Packet Types: See ItemPacketType enum +** +*/ +enum ItemPacketType +{ + ItemPacketViewLink = 0x00, + ItemPacketTradeView = 0x65, + ItemPacketLoot = 0x66, + ItemPacketTrade = 0x67, + ItemPacketCharInventory = 0x69, + ItemPacketSummonItem = 0x6A, + ItemPacketTributeItem = 0x6C, + ItemPacketMerchant = 0x64, + ItemPacketWorldContainer = 0x6B +}; +struct ItemPacket_Struct +{ +/*00*/ ItemPacketType PacketType; +/*04*/ char SerializedItem[1]; //was 1 +/*xx*/ +}; + +struct BulkItemPacket_Struct +{ +/*00*/ char SerializedItem[0]; +/*xx*/ +}; + +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]; +/*0016*/ +}; + +struct ItemNamePacket_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 unkown004; +/*008*/ char name[64]; +/*072*/ +}; + +// Length: 16 +struct ItemProperties_Struct_Old { + +/*000*/ uint8 unknown01[2]; +/*002*/ uint8 charges; +/*003*/ uint8 unknown02[13]; +/*016*/ +}; + +// Length: 8 +struct ItemProperties_Struct { + +/*000*/ uint8 unknown01[4]; +/*004*/ uint8 charges; +/*005*/ uint8 unknown02[3]; +/*008*/ +}; + +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; +/*0012*/ +}; + +// +// from_slot/to_slot +// -1 - destroy +// 0 - cursor +// 1 - inventory +// 2 - bank +// 3 - trade +// 4 - shared bank +// +// cointype +// 0 - copeer +// 1 - silver +// 2 - gold +// 3 - platinum +// +static const uint32 COINTYPE_PP = 3; +static const uint32 COINTYPE_GP = 2; +static const uint32 COINTYPE_SP = 1; +static const uint32 COINTYPE_CP = 0; + +struct MoveCoin_Struct +{ + int32 from_slot; + int32 to_slot; + int32 cointype1; + int32 cointype2; + int32 amount; +}; +struct TradeCoin_Struct{ + uint32 trader; + uint8 slot; + uint16 unknown5; + uint8 unknown7; + uint32 amount; +}; +struct TradeMoneyUpdate_Struct{ + uint32 trader; + uint32 type; + uint32 amount; +}; +/* +** Surname struct +** Size: 100 bytes +*/ +struct Surname_Struct +{ +/*0000*/ char name[64]; +/*0064*/ uint32 unknown0064; +/*0068*/ char lastname[32]; +/*0100*/ +}; + +struct GuildsListEntry_Struct { + char name[64]; +}; + +static const uint32 MAX_NUMBER_GUILDS = 1500; +struct GuildsList_Struct { + uint8 head[64]; // First on guild list seems to be empty... + GuildsListEntry_Struct Guilds[MAX_NUMBER_GUILDS]; +}; + +struct GuildUpdate_Struct { + uint32 guildID; + GuildsListEntry_Struct entry; +}; + +/* +** Money Loot +** Length: 22 Bytes +** OpCode: 5020 +*/ +struct moneyOnCorpseStruct { +/*0000*/ uint8 response; // 0 = someone else is, 1 = OK, 2 = not at this time +/*0001*/ uint8 unknown1; // = 0x5a +/*0002*/ uint8 unknown2; // = 0x40 +/*0003*/ uint8 unknown3; // = 0 +/*0004*/ uint32 platinum; // Platinum Pieces +/*0008*/ uint32 gold; // Gold Pieces + +/*0012*/ uint32 silver; // Silver Pieces +/*0016*/ uint32 copper; // Copper Pieces +}; + +struct LootingItem_Struct { +/*000*/ uint32 lootee; +/*004*/ uint32 looter; +/*008*/ uint32 slot_id; +/*012*/ uint32 auto_loot; +/*016*/ uint32 unknown16; +/*020*/ +}; + +struct GuildManageStatus_Struct{ + uint32 guildid; + uint32 oldrank; + uint32 newrank; + char name[64]; +}; +// Guild invite, remove +struct GuildJoin_Struct{ +/*000*/ uint32 guild_id; +/*004*/ uint32 unknown04; +/*008*/ uint32 level; +/*012*/ uint32 class_; +/*016*/ uint32 rank;//0 member, 1 officer, 2 leader +/*020*/ uint32 zoneid; +/*024*/ uint32 unknown24; +/*028*/ char name[64]; +/*092*/ +}; +struct GuildInviteAccept_Struct { + char inviter[64]; + char newmember[64]; + uint32 response; + uint32 guildeqid; +}; +struct GuildManageRemove_Struct { + uint32 guildeqid; + char member[64]; +}; +struct GuildCommand_Struct { + char othername[64]; + char myname[64]; + uint16 guildeqid; + uint8 unknown[2]; // for guildinvite all 0's, for remove 0=0x56, 2=0x02 + uint32 officer; +}; + +// Opcode OP_GMZoneRequest +// Size = 88 bytes +struct GMZoneRequest_Struct { +/*0000*/ char charname[64]; +/*0064*/ uint32 zone_id; +/*0068*/ float x; +/*0072*/ float y; +/*0076*/ float z; +/*0080*/ char unknown0080[4]; +/*0084*/ uint32 success; // 0 if command failed, 1 if succeeded? +/*0088*/ +// /*072*/ int8 success; // =0 client->server, =1 server->client, -X=specific error +// /*073*/ uint8 unknown0073[3]; // =0 ok, =ffffff error +}; + +struct GMSummon_Struct { +/* 0*/ char charname[64]; +/* 30*/ char gmname[64]; +/* 60*/ uint32 success; +/* 61*/ uint32 zoneID; +/*92*/ int32 y; +/*96*/ int32 x; +/*100*/ int32 z; +/*104*/ uint32 unknown2; // E0 E0 56 00 +}; + +struct GMGoto_Struct { // x,y is swapped as compared to summon and makes sense as own packet +/* 0*/ char charname[64]; + +/* 64*/ char gmname[64]; +/* 128*/ uint32 success; +/* 132*/ uint32 zoneID; + +/*136*/ int32 y; +/*140*/ int32 x; +/*144*/ int32 z; +/*148*/ uint32 unknown2; // E0 E0 56 00 +}; + +struct GMLastName_Struct { + char name[64]; + char gmname[64]; + char lastname[64]; + uint16 unknown[4]; // 0x00, 0x00 + // 0x01, 0x00 = Update the clients +}; + +struct OnLevelMessage_Struct { +/*0000*/ char Title[128]; +/*0128*/ char Text[4096]; +/*4224*/ char ButtonName0[25]; // If Buttons = 1, these two are the text for the left and right buttons respectively +/*4249*/ char ButtonName1[25]; +/*4274*/ uint8 Buttons; +/*4275*/ uint8 Unknown4275; // Something to do with audio controls +/*4276*/ uint32 Duration; +/*4280*/ uint32 PopupID; // If none zero, a response packet with 00 00 00 00 is returned on clicking the left button +/*4284*/ uint32 NegativeID; // If none zero, a response packet with 01 00 00 00 is returned on clicking the right button +/*4288*/ uint32 Unknown4288; +/*4292*/ +}; + +//Combat Abilities +struct CombatAbility_Struct { + uint32 m_target; //the ID of the target mob + uint32 m_atk; + uint32 m_skill; +}; + +//Instill Doubt +struct Instill_Doubt_Struct { + uint8 i_id; + uint8 ia_unknown; + uint8 ib_unknown; + uint8 ic_unknown; + uint8 i_atk; + + uint8 id_unknown; + uint8 ie_unknown; + uint8 if_unknown; + uint8 i_type; + uint8 ig_unknown; + uint8 ih_unknown; + uint8 ii_unknown; +}; + +struct GiveItem_Struct { + uint16 to_entity; + int16 to_equipSlot; + uint16 from_entity; + int16 from_equipSlot; +}; + +struct RandomReq_Struct { + uint32 low; + uint32 high; +}; + +/* solar: 9/23/03 reply to /random command; struct from Zaphod */ +struct RandomReply_Struct { +/* 00 */ uint32 low; +/* 04 */ uint32 high; +/* 08 */ uint32 result; +/* 12 */ char name[64]; +/* 76 */ +}; + +/* +** LFG_Appearance_Struct +** Packet sent to clients to notify when someone in zone toggles LFG flag +** Size: 8 bytes +** Used in: OP_LFGAppearance +** +*/ +struct LFG_Appearance_Struct +{ +/*0000*/ uint32 spawn_id; // ID of the client +/*0004*/ uint8 lfg; // 1=LFG, 0=Not LFG +/*0005*/ char unknown0005[3]; // +/*0008*/ +}; + + +// EverQuest Time Information: +// 72 minutes per EQ Day +// 3 minutes per EQ Hour +// 6 seconds per EQ Tick (2 minutes EQ Time) +// 3 seconds per EQ Minute + +struct TimeOfDay_Struct { + uint8 hour; + uint8 minute; + uint8 day; + uint8 month; + uint16 year; +/*0006*/ uint16 unknown0016; // Placeholder +/*0008*/ +}; + +// Darvik: shopkeeper structs +struct Merchant_Click_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; +/*008*/ uint32 command; //1=open, 0=cancel/close +/*012*/ float rate; //cost multiplier, dosent work anymore +}; +/* +Unknowns: +0 is e7 from 01 to // MAYBE SLOT IN PURCHASE +1 is 03 +2 is 00 +3 is 00 +4 is ?? +5 is ?? +6 is 00 from a0 to +7 is 00 from 3f to */ +/* +0 is F6 to 01 +1 is CE CE +4A 4A +00 00 +00 E0 +00 CB +00 90 +00 3F +*/ + + + +struct Merchant_Sell_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; // Player's entity id +/*008*/ uint32 itemslot; +/*012*/ uint32 unknown12; +/*016*/ uint8 quantity; // Already sold +/*017*/ uint8 Unknown017[3]; +/*020*/ uint32 Unknown020; +/*024*/ uint32 price; +/*028*/ uint32 pricehighorderbits; // It appears the price is 64 bits in Underfoot+ +/*032*/ +}; + +struct Merchant_Purchase_Struct { +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 itemslot; // Player's entity id +/*008*/ uint32 quantity; +/*012*/ uint32 price; +}; +struct Merchant_DelItem_Struct{ +/*000*/ uint32 npcid; // Merchant NPC's entity id +/*004*/ uint32 playerid; // Player's entity id +/*008*/ uint32 itemslot; +}; +struct Adventure_Purchase_Struct { +/*000*/ uint32 some_flag; //set to 1 generally... +/*004*/ uint32 npcid; +/*008*/ uint32 itemid; +/*012*/ uint32 variable; +}; + +struct Adventure_Sell_Struct { +/*000*/ uint32 unknown000; //0x01 - Stack Size/Charges? +/*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]; +/*024*/ uint32 ldon_guk_points; // Earned Deepest Guk points +/*028*/ uint32 ldon_mirugal_points; // Earned Mirugal' Mebagerie points +/*032*/ uint32 ldon_mistmoore_points; // Earned Mismoore Catacombs Points +/*036*/ uint32 ldon_rujarkian_points; // Earned Rujarkian Hills points +/*040*/ uint32 ldon_takish_points; // Earned Takish points +/*044*/ uint8 unknown_apu042[216]; +}; + + +struct AdventureFinish_Struct{ + uint32 win_lose;//Cofruben: 00 is a lose,01 is win. + uint32 points; +}; +//OP_AdventureRequest +struct AdventureRequest_Struct{ + uint32 risk;//1 normal,2 hard. + uint32 entity_id; +}; +struct AdventureRequestResponse_Struct{ + uint32 unknown000; + char text[2048]; + uint32 timetoenter; + uint32 timeleft; + uint32 risk; + float x; + float y; + float z; + uint32 showcompass; + uint32 unknown2080; +}; + +//this is mostly right but something is off that causes the client to crash sometimes +//I don't really care enough about the feature to work on it anymore though. +struct AdventureLeaderboardEntry_Struct +{ +/*004*/ char name[64]; +/*008*/ uint32 success; +/*012*/ uint32 failure; +/*016*/ +}; + +struct AdventureLeaderboard_Struct +{ +/*000*/ uint32 unknown000; +/*004*/ uint32 unknown004; +/*008*/ uint32 success; +/*012*/ uint32 failure; +/*016*/ uint32 our_rank; +/*020*/ +}; + +/*struct Item_Shop_Struct { + uint16 merchantid; + uint8 itemtype; + Item_Struct item; + uint8 iss_unknown001[6]; +};*/ + +struct Illusion_Struct { //size: 256 +/*000*/ uint32 spawnid; +/*004*/ char charname[64]; // +/*068*/ uint16 race; // +/*070*/ char unknown006[2]; // Weird green name +/*072*/ uint8 gender; +/*073*/ uint8 texture; +/*074*/ uint8 unknown074; // +/*075*/ uint8 unknown075; // +/*076*/ uint8 helmtexture; // +/*077*/ uint8 unknown077; // +/*078*/ uint8 unknown078; // +/*079*/ uint8 unknown079; // +/*080*/ uint32 face; // +/*084*/ uint8 hairstyle; // Some Races don't change Hair Style Properly in SoF +/*085*/ uint8 haircolor; // +/*086*/ uint8 beard; // +/*087*/ uint8 beardcolor; // +/*088*/ float size; // +/*092*/ uint8 unknown092[148]; +/*240*/ uint32 unknown240; // Removes armor? +/*244*/ uint32 drakkin_heritage; // +/*248*/ uint32 drakkin_tattoo; // +/*252*/ uint32 drakkin_details; // +/*256*/ +}; + +struct ZonePoint_Entry { //28 octets +/*0000*/ uint32 iterator; +/*0004*/ float y; +/*0008*/ float x; +/*0012*/ float z; +/*0016*/ float heading; +/*0020*/ uint16 zoneid; +/*0022*/ uint16 zoneinstance; // LDoN instance +/*0024*/ uint32 unknown024; +/*0028*/ +}; + +struct ZonePoints { +/*0000*/ uint32 count; +/*0004*/ struct ZonePoint_Entry zpe[0]; // Always add one extra to the end after all zonepoints +//*0xxx*/ uint8 unknown0xxx[24]; //New from SEQ +}; + +struct SkillUpdate_Struct { +/*00*/ uint32 skillId; +/*04*/ uint32 value; +/*08*/ +}; + +struct ZoneUnavail_Struct { + //This actually varies, but... + char zonename[16]; + int16 unknown[4]; +}; + +struct GroupInvite_Struct { +/*0000*/ char invitee_name[64]; +/*0064*/ char inviter_name[64]; +/*0128*/ uint32 unknown0128; +/*0132*/ uint32 unknown0132; +/*0136*/ uint32 unknown0136; +/*0140*/ uint32 unknown0140; +/*0144*/ uint32 unknown0144; +/*0148*/ +}; + +struct GroupGeneric_Struct { +/*0000*/ char name1[64]; +/*0064*/ char name2[64]; +/*0128*/ uint32 unknown0128; +/*0132*/ uint32 unknown0132; +/*0136*/ uint32 unknown0136; +/*0140*/ uint32 unknown0140; +/*0144*/ uint32 unknown0144; +/*0148*/ +}; + +struct GroupCancel_Struct { +/*000*/ char name1[64]; +/*064*/ char name2[64]; +/*128*/ uint8 unknown128[20]; +/*148*/ uint32 toggle; +/*152*/ +}; + +struct GroupUpdate_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +/*0452*/ +}; + +struct GroupUpdate2_Struct { +/*0000*/ uint32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +/*0452*/ GroupLeadershipAA_Struct leader_aas; +/*0516*/ uint8 unknown[252]; // Titanium uses [188] here +/*0768*/ +}; + +struct GroupUpdate_Struct_Underfoot { // New for Underfoot +/*0000*/ uint32 groupid; // Guess - Matches unknown0136 from GroupFollow_Struct +/*0004*/ uint32 totalmembers; // Guess +/*0000*/ //uint32 leadersname[0]; // Group Leader Name Null Terminated +/*0008*/ //GroupMembers_Struct groupmembers; +}; + +struct GroupMembers_Struct { // New for Underfoot +/*0000*/ uint32 membernumber; // Guess - number of member in the group (0 to 5?) +/*0000*/ //char membername[0]; // Member Name Null Terminated +/*0000*/ uint8 unknown001[3]; // Seen 0 +/*0000*/ uint32 memberlevel; // Guess +/*0000*/ uint8 unknown002[11]; // Seen 0 +}; + +struct GroupJoin_Struct_Underfoot { // New for Underfoot +/*0000*/ uint32 unknown0000; // Matches unknown0136 from GroupFollow_Struct +/*0004*/ uint32 action; +/*0008*/ uint8 unknown0008[5]; // Seen 0 +/*0013*/ //char membername[0]; // Null Terminated? +/*0000*/ uint8 unknown0013[3]; // Seen 0 +/*0000*/ uint32 unknown0016; // Matches unknown0132 from GroupFollow_Struct +/*0000*/ uint8 unknown0020[11]; // Seen 0 +}; + +struct GroupJoin_Struct { +/*000*/ char unknown000[64]; +/*064*/ char membername[64]; +/*128*/ uint8 unknown128[20]; // Leadership AA ? +/*148*/ +}; + +struct GroupFollow_Struct { // Underfoot Follow Struct +/*0000*/ char name1[64]; // inviter +/*0064*/ char name2[64]; // invitee +/*0128*/ uint32 unknown0128; // Seen 0 +/*0132*/ uint32 unknown0132; // Group ID or member level? +/*0136*/ uint32 unknown0136; // Maybe Voice Chat Channel or Group ID? +/*0140*/ uint32 unknown0140; // Seen 0 +/*0144*/ uint32 unknown0144; // Seen 0 +/*0148*/ uint32 unknown0148; +/*0152*/ +}; + +struct LFG_Struct { +/*000*/ uint32 unknown000; +/*004*/ uint32 value; // 0x00 = off 0x01 = on +/*008*/ uint32 unknown008; +/*012*/ uint32 unknown012; +/*016*/ char name[64]; +}; + +struct FaceChange_Struct { +/*000*/ uint8 haircolor; +/*001*/ uint8 beardcolor; +/*002*/ uint8 eyecolor1; +/*003*/ uint8 eyecolor2; +/*004*/ uint8 hairstyle; +/*005*/ uint8 beard; +/*006*/ uint8 face; +/*007*/ uint8 unknown007; +/*008*/ uint32 drakkin_heritage; +/*012*/ uint32 drakkin_tattoo; +/*016*/ uint32 drakkin_details; +/*020*/ uint32 unknown020; +/*024*/ +}; +//there are only 10 faces for barbs changing woad just +//increase the face value by ten so if there were 8 woad +//designs then there would be 80 barb faces + +/* +** Trade request from one client to another +** Used to initiate a trade +** Size: 8 bytes +** Used in: OP_TradeRequest +*/ +struct TradeRequest_Struct { +/*00*/ uint32 to_mob_id; +/*04*/ uint32 from_mob_id; +/*08*/ +}; + +struct TradeAccept_Struct { +/*00*/ uint32 from_mob_id; +/*04*/ uint32 unknown4; //seems to be garbage +/*08*/ +}; + +/* +** Cancel Trade struct +** Sent when a player cancels a trade +** Size: 8 bytes +** Used In: OP_CancelTrade +** +*/ +struct CancelTrade_Struct { +/*00*/ uint32 fromid; +/*04*/ uint32 action; +/*08*/ +}; + +struct PetitionUpdate_Struct { + uint32 petnumber; // Petition Number + uint32 color; // 0x00 = green, 0x01 = yellow, 0x02 = red + uint32 status; + time_t senttime; // 4 has to be 0x1F + char accountid[32]; + char gmsenttoo[64]; + int32 quetotal; + char charname[64]; +}; + +struct Petition_Struct { + uint32 petnumber; + uint32 urgency; + char accountid[32]; + char lastgm[32]; + uint32 zone; + //char zone[32]; + char charname[64]; + uint32 charlevel; + uint32 charclass; + uint32 charrace; + uint32 unknown; + //time_t senttime; // Time? + uint32 checkouts; + uint32 unavail; + //uint8 unknown5[4]; + time_t senttime; + uint32 unknown2; + char petitiontext[1024]; + char gmtext[1024]; +}; + + +struct Who_All_Struct { // 76 length total +/*000*/ char whom[64]; +/*064*/ uint32 wrace; // FF FF = no race + +/*068*/ uint32 wclass; // FF FF = no class +/*072*/ uint32 lvllow; // FF FF = no numbers +/*076*/ uint32 lvlhigh; // FF FF = no numbers +/*080*/ uint32 gmlookup; // FF FF = not doing /who all gm +/*084*/ uint32 guildid; // Also used for Buyer/Trader/LFG +/*088*/ uint8 unknown088[64]; +/*156*/ uint32 type; // 0 = /who 3 = /who all +}; + +struct Stun_Struct { // 8 bytes total +/*000*/ uint32 duration; // Duration of stun +/*004*/ uint8 unknown004; // seen 0 +/*005*/ uint8 unknown005; // seen 163 +/*006*/ uint8 unknown006; // seen 67 +/*007*/ uint8 unknown007; // seen 0 +/*008*/ +}; + +struct AugmentItem_Struct { +/*00*/ int16 container_slot; +/*02*/ char unknown02[2]; +/*04*/ int32 augment_slot; +/*08*/ +}; + +// OP_Emote +struct Emote_Struct { +/*0000*/ uint32 unknown01; +/*0004*/ char message[1024]; // was 1024 +/*1028*/ +}; + +// Inspect +struct Inspect_Struct { + uint32 TargetID; + uint32 PlayerID; +}; + +//OP_InspectAnswer - Size: 1860 +struct InspectResponse_Struct{ +/*000*/ uint32 TargetID; +/*004*/ uint32 playerid; +/*008*/ char itemnames[23][64]; +/*1480*/uint32 itemicons[23]; +/*1572*/char text[288]; // Max number of chars in Inspect Window appears to be 254 +/*1860*/ +}; + +//OP_SetDataRate +struct SetDataRate_Struct { + float newdatarate; +}; + +//OP_SetServerFilter +struct SetServerFilter_Struct { + uint32 filters[34]; //see enum eqFilterType [31] +}; + +//Op_SetServerFilterAck +struct SetServerFilterAck_Struct { + uint8 blank[8]; +}; +struct IncreaseStat_Struct{ + /*0000*/ uint8 unknown0; + /*0001*/ uint8 str; + /*0002*/ uint8 sta; + /*0003*/ uint8 agi; + /*0004*/ uint8 dex; + /*0005*/ uint8 int_; + /*0006*/ uint8 wis; + /*0007*/ uint8 cha; + /*0008*/ uint8 fire; + /*0009*/ uint8 cold; + /*0010*/ uint8 magic; + /*0011*/ uint8 poison; + /*0012*/ uint8 disease; + /*0013*/ char unknown13[116]; + /*0129*/ uint8 str2; + /*0130*/ uint8 sta2; + /*0131*/ uint8 agi2; + /*0132*/ uint8 dex2; + /*0133*/ uint8 int_2; + /*0134*/ uint8 wis2; + /*0135*/ uint8 cha2; + /*0136*/ uint8 fire2; + /*0137*/ uint8 cold2; + /*0138*/ uint8 magic2; + /*0139*/ uint8 poison2; + /*0140*/ uint8 disease2; +}; + +struct GMName_Struct { + char oldname[64]; + char gmname[64]; + char newname[64]; + uint8 badname; + uint8 unknown[3]; +}; + +struct GMDelCorpse_Struct { + char corpsename[64]; + char gmname[64]; + uint8 unknown; +}; + +struct GMKick_Struct { + char name[64]; + char gmname[64]; + uint8 unknown; +}; + + +struct GMKill_Struct { + char name[64]; + char gmname[64]; + uint8 unknown; +}; + + +struct GMEmoteZone_Struct { + char text[512]; +}; + +// The BookText_Struct is not used in SoF and later clients. +// The BookRequest_Struct is used instead for both request and reply. +// +struct BookText_Struct { + uint8 window; // where to display the text (0xFF means new window) + uint8 type; //type: 0=scroll, 1=book, 2=item info.. prolly + char booktext[1]; // Variable Length - was 1 +}; +// This is the request to read a book. +// This is just a "text file" on the server +// or in our case, the 'name' column in our books table. +struct BookRequest_Struct { +/*0000*/ uint32 window; // where to display the text (0xFFFFFFFF means new window). +/*0004*/ uint32 invslot; // The inventory slot the book is in. Not used, but echoed in the response packet. +/*0008*/ uint32 type; // 0 = Scroll, 1 = Book, 2 = Item Info. Possibly others +/*0012*/ uint32 unknown0012; +/*0016*/ uint16 unknown0016; +/*0018*/ char txtfile[8194]; +}; + +/* +** Object/Ground Spawn struct +** Used for Forges, Ovens, ground spawns, items dropped to ground, etc +** Size: 104 bytes +** OpCodes: OP_CreateObject +** Last Updated: Oct-17-2003 +** +*/ +struct Object_Struct { +/*00*/ uint32 linked_list_addr[2];// They are, get this, prev and next, ala linked list +/*08*/ uint32 unknown008; // Something related to the linked list? +/*12*/ uint32 drop_id; // Unique object id for zone +/*16*/ uint16 zone_id; // Redudant, but: Zone the object appears in +/*18*/ uint16 zone_instance; // +/*20*/ uint32 unknown020; // 00 00 00 00 +/*24*/ uint32 unknown024; // 53 9e f9 7e - same for all objects in the zone? +/*40*/ float heading; // heading +/*32*/ uint8 unknown032[8]; // 00 00 00 00 00 00 00 00 +/*28*/ float size; // Size - default 1 +/*44*/ float z; // z coord +/*48*/ float x; // x coord +/*52*/ float y; // y coord +/*56*/ char object_name[32]; // Name of object, usually something like IT63_ACTORDEF was [20] +/*88*/ uint32 unknown088; // unique ID? Maybe for a table that includes the contents? +/*92*/ uint32 object_type; // Type of object, not directly translated to OP_OpenObject +/*96*/ uint8 unknown096[4]; // ff ff ff ff +/*100*/ uint32 spawn_id; // Spawn Id of client interacting with object +/*104*/ +}; +// 01 = generic drop, 02 = armor, 19 = weapon +//[13:40] and 0xff seems to be indicative of the tradeskill/openable items that end up returning the old style item type in the OP_OpenObject + +/* +** Click Object Struct +** Client clicking on zone object (forge, groundspawn, etc) +** Size: 8 bytes +** Last Updated: Oct-17-2003 +** +*/ +struct ClickObject_Struct { +/*00*/ uint32 drop_id; +/*04*/ uint32 player_id; +/*08*/ +}; + +struct Shielding_Struct { + uint32 target_id; +}; + +/* +** Click Object Acknowledgement Struct +** Response to client clicking on a World Container (ie, forge) +** +*/ +struct ClickObjectAction_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 type; // See object.h, "Object Types" +/*16*/ uint32 unknown16; // +/*20*/ uint32 icon; // Icon to display for tradeskill containers +/*24*/ uint32 unknown24; // +/*28*/ char object_name[64]; // Object name to display +/*92*/ +}; + +/* +** This is different now, mostly unknown +** +*/ +struct CloseContainer_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 unknown12[12]; +}; + +/* +** Generic Door Struct +** Length: 52 Octets +** Used in: +** cDoorSpawnsStruct(f721) +** +*/ +struct Door_Struct +{ +/*0000*/ char name[32]; // Filename of Door // Was 10char long before... added the 6 in the next unknown to it: Daeken M. BlackBlade +/*0032*/ float yPos; // y loc +/*0036*/ float xPos; // x loc +/*0040*/ float zPos; // z loc +/*0044*/ float heading; +/*0048*/ uint32 incline; // rotates the whole door +/*0052*/ uint32 size; // 100 is normal, smaller number = smaller model +/*0054*/ uint8 unknown0054[4]; // 00 00 00 00 +/*0060*/ uint8 doorId; // door's id # +/*0061*/ uint8 opentype; +/*0062*/ uint8 state_at_spawn; +/*0063*/ uint8 invert_state; // if this is 1, the door is normally open +/*0064*/ uint32 door_param; // normally ff ff ff ff (-1) +/*0068*/ uint32 unknown0068; // 00 00 00 00 +/*0072*/ uint32 unknown0072; // 00 00 00 00 +/*0076*/ uint8 unknown0076; // seen 1 or 0 +/*0077*/ uint8 unknown0077; // seen 1 (always?) +/*0078*/ uint8 unknown0078; // seen 0 (always?) +/*0079*/ uint8 unknown0079; // seen 1 (always?) +/*0080*/ uint8 unknown0080; // seen 0 (always?) +/*0081*/ uint8 unknown0081; // seen 1 or 0 or rarely 2C or 90 or ED or 2D or A1 +/*0082*/ uint8 unknown0082; // seen 0 or rarely FF or FE or 10 or 5A or 82 +/*0083*/ uint8 unknown0083; // seen 0 or rarely 02 or 7C +/*0084*/ uint8 unknown0084[8]; // mostly 0s, the last 3 bytes are something tho +/*0092*/ +}; + +struct DoorSpawns_Struct { + struct Door_Struct doors[0]; +}; + +/* + OP Code: Op_ClickDoor + Size: 16 +*/ +struct ClickDoor_Struct { +/*000*/ uint8 doorid; +/*001*/ uint8 unknown001; // This may be some type of action setting +/*002*/ uint8 unknown002; // This is sometimes set after a lever is closed +/*003*/ uint8 unknown003; // Seen 0 +/*004*/ uint8 picklockskill; +/*005*/ uint8 unknown005[3]; +/*008*/ uint32 item_id; +/*012*/ uint16 player_id; +/*014*/ uint8 unknown014[2]; +/*016*/ +}; + +struct MoveDoor_Struct { + uint8 doorid; + uint8 action; +}; + + +struct BecomeNPC_Struct { + uint32 id; + int32 maxlevel; +}; + +struct Underworld_Struct { + float speed; + float y; + float x; + float z; +}; + +struct Resurrect_Struct { + uint32 unknown00; + uint16 zone_id; + uint16 instance_id; + float y; + float x; + float z; + char your_name[64]; + uint32 unknown88; + char rezzer_name[64]; + uint32 spellid; + char corpse_name[64]; + uint32 action; +/* 228 */ +}; + +struct SetRunMode_Struct { + uint8 mode; //01=run 00=walk + uint8 unknown[3]; +}; + +// EnvDamage is EnvDamage2 without a few bytes at the end. +// Size: 37 bytes +struct EnvDamage2_Struct { +/*0000*/ uint32 id; +/*0004*/ uint16 unknown4; +/*0006*/ uint32 damage; +/*0010*/ float unknown10; // New to Underfoot - Seen 1 +/*0014*/ uint8 unknown14[12]; +/*0026*/ uint8 dmgtype; // FA = Lava; FC = Falling +/*0027*/ uint8 unknown27[4]; +/*0031*/ uint16 unknown31; // New to Underfoot - Seen 66 +/*0033*/ uint16 constant; // Always FFFF +/*0035*/ uint16 unknown35; +/*0037*/ +}; + +//Bazaar Stuff + +enum { + BazaarTrader_StartTraderMode = 1, + BazaarTrader_EndTraderMode = 2, + BazaarTrader_UpdatePrice = 3, + BazaarTrader_EndTransaction = 4, + BazaarSearchResults = 7, + BazaarWelcome = 9, + BazaarBuyItem = 10, + BazaarTrader_ShowItems = 11, + BazaarSearchDone = 12, + BazaarTrader_CustomerBrowsing = 13 +}; + +enum { + BazaarPriceChange_Fail = 0, + BazaarPriceChange_UpdatePrice = 1, + BazaarPriceChange_RemoveItem = 2, + BazaarPriceChange_AddItem = 3 +}; + +struct BazaarWindowStart_Struct { + uint8 Action; + uint8 Unknown001; + uint16 Unknown002; +}; + + +struct BazaarWelcome_Struct { + BazaarWindowStart_Struct Beginning; + uint32 Traders; + uint32 Items; + uint8 Unknown012[8]; +}; + +struct BazaarSearch_Struct { + BazaarWindowStart_Struct Beginning; + uint32 TraderID; + uint32 Class_; + uint32 Race; + uint32 ItemStat; + uint32 Slot; + uint32 Type; + char Name[64]; + uint32 MinPrice; + uint32 MaxPrice; + uint32 Minlevel; + uint32 MaxLlevel; +}; +struct BazaarInspect_Struct{ + uint32 ItemID; + uint32 Unknown004; + char Name[64]; +}; + +struct NewBazaarInspect_Struct { +/*000*/ BazaarWindowStart_Struct Beginning; +/*004*/ char Name[64]; +/*068*/ uint32 Unknown068; +/*072*/ int32 SerialNumber; +/*076*/ uint32 Unknown076; +/*080*/ uint32 SellerID; +/*084*/ uint32 Unknown084; +}; + +struct BazaarReturnDone_Struct{ + uint32 Type; + uint32 TraderID; + uint32 Unknown008; + uint32 Unknown012; + uint32 Unknown016; +}; + +struct BazaarSearchResults_Struct { +/*000*/ BazaarWindowStart_Struct Beginning; +/*004*/ uint32 SellerID; +/*008*/ char SellerName[64]; +/*072*/ uint32 NumItems; +/*076*/ uint32 ItemID; +/*080*/ uint32 SerialNumber; +/*084*/ uint32 Unknown084; +/*088*/ char ItemName[64]; +/*152*/ uint32 Cost; +/*156*/ uint32 ItemStat; +/*160*/ +}; + +struct ServerSideFilters_Struct { +uint8 clientattackfilters; // 0) No, 1) All (players) but self, 2) All (players) but group +uint8 npcattackfilters; // 0) No, 1) Ignore NPC misses (all), 2) Ignore NPC Misses + Attacks (all but self), 3) Ignores NPC Misses + Attacks (all but group) +uint8 clientcastfilters; // 0) No, 1) Ignore PC Casts (all), 2) Ignore PC Casts (not directed towards self) +uint8 npccastfilters; // 0) No, 1) Ignore NPC Casts (all), 2) Ignore NPC Casts (not directed towards self) +}; + +/* +** Client requesting item statistics +** Size: 48 bytes +** Used In: OP_ItemLinkClick +** Last Updated: 2/15/2009 +** +*/ +struct ItemViewRequest_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 augments[5]; +/*024*/ uint32 link_hash; +/*028*/ uint32 unknown028; //seems to always be 4 on SoF client +/*032*/ char unknown032[12]; //probably includes loregroup & evolving info. see Client::MakeItemLink() in zone/inventory.cpp:469 +/*044*/ uint16 icon; +/*046*/ char unknown046[2]; +}; + +/* + * Client to server packet + */ +struct PickPocket_Struct { +// Size 18 + uint32 to; + uint32 from; + uint16 myskill; + uint8 type; // -1 you are being picked, 0 failed , 1 = plat, 2 = gold, 3 = silver, 4 = copper, 5 = item + uint8 unknown1; // 0 for response, unknown for input + uint32 coin; + uint8 lastsix[2]; +}; +/* + * Server to client packet + */ + +struct sPickPocket_Struct { + // Size 28 = coin/fail + uint32 to; + uint32 from; + uint32 myskill; + uint32 type; + uint32 coin; + char itemname[64]; +}; + + +struct LogServer_Struct { +// Op_Code OP_LOGSERVER +/*000*/ uint32 unknown000; +/*004*/ uint8 enable_pvp; +/*005*/ uint8 unknown005; +/*006*/ uint8 unknown006; +/*007*/ uint8 unknown007; +/*008*/ uint8 enable_FV; +/*009*/ uint8 unknown009; +/*010*/ uint8 unknown010; +/*011*/ uint8 unknown011; +/*012*/ uint32 unknown012; // htonl(1) on live +/*016*/ uint32 unknown016; // htonl(1) on live +/*020*/ uint8 unknown020[12]; +/*032*/ uint32 unknown032; +/*036*/ char worldshortname[32]; +/*068*/ uint8 unknown064[32]; +/*100*/ char unknown096[16]; // 'pacman' on live +/*116*/ char unknown112[16]; // '64.37,148,36' on live +/*132*/ uint8 unknown128[48]; +/*180*/ uint32 unknown176; // htonl(0x00002695) +/*184*/ char unknown180[80]; // 'eqdataexceptions@mail.station.sony.com' on live +/*264*/ uint8 unknown260; // 0x01 on live +/*265*/ uint8 enablevoicemacros; +/*266*/ uint8 enablemail; +/*267*/ uint8 unknown263[41]; +/*308*/ +}; + +struct ApproveWorld_Struct { +// Size 544 +// Op_Code OP_ApproveWorld + uint8 unknown544[544]; +}; + +struct ClientError_Struct +{ +/*00001*/ char type; +/*00001*/ char unknown0001[69]; +/*00069*/ char character_name[64]; +/*00134*/ char unknown134[192]; +/*00133*/ char message[31994]; +/*32136*/ +}; + +struct MobHealth +{ + /*0000*/ uint8 hp; //health percent + /*0001*/ uint16 id;//mobs id +}; + +struct Track_Struct { + uint16 entityid; + uint16 y; + uint16 x; + uint16 z; +}; + +struct Tracking_Struct { + Track_Struct Entrys[0]; +}; + +// Looks like new tracking structures - Opcode: 0x57a7 +struct Tracking_Struct_New { + uint16 totalcount; // Total Count of mobs within tracking range + Track_Struct Entrys[0]; +}; + +struct Track_Struct_New { + uint16 entityid; // Entity ID + uint16 unknown002; // 00 00 + uint32 unknown004; // + uint8 level; // level of mob + uint8 unknown009; // 01 maybe type of mob? player/npc? + char name[1]; // name of mob +}; + + +/* +** ZoneServerInfo_Struct +** Zone server information +** Size: 130 bytes +** Used In: OP_ZoneServerInfo +** +*/ +struct ZoneServerInfo_Struct +{ +/*0000*/ char ip[128]; +/*0128*/ uint16 port; +}; + +struct WhoAllPlayer{ + uint32 formatstring; + uint32 pidstring; + char* name; + uint32 rankstring; + char* guild; + uint32 unknown80[2]; + uint32 zonestring; + uint32 zone; + uint32 class_; + uint32 level; + uint32 race; + char* account; + uint32 unknown100; +}; + +struct WhoAllReturnStruct { + uint32 id; + uint32 playerineqstring; + char line[27]; + uint8 unknown35; //0A + uint32 unknown36;//0s + uint32 playersinzonestring; + uint32 unknown44[2]; //0s + uint32 unknown52;//1 + uint32 unknown56;//1 + uint32 playercount;//1 + struct WhoAllPlayer player[0]; +}; + +// The following four structs are the WhoAllPlayer struct above broken down +// for use in World ClientList::SendFriendsWho to accomodate the user of variable +// length strings within the struct above. + +struct WhoAllPlayerPart1 { + uint32 FormatMSGID; + uint32 Unknown04; + uint32 Unknown08; + char Name[1]; +}; + +struct WhoAllPlayerPart2 { + uint32 RankMSGID; + char Guild[1]; +}; + +struct WhoAllPlayerPart3 { + uint32 Unknown80[2]; + uint32 ZoneMSGID; + uint32 Zone; + uint32 Class_; + uint32 Level; + uint32 Race; + char Account[1]; +}; + +struct WhoAllPlayerPart4 { + uint32 Unknown100; +}; + +struct Trader_Struct { + uint32 code; + uint32 itemid[160]; + uint32 unknown; + uint32 itemcost[80]; +}; + +struct ClickTrader_Struct { + uint32 code; + uint32 unknown[161];//damn soe this is totally pointless :/ but at least your finally using memset! Good job :) -LE + uint32 itemcost[80]; +}; + +struct GetItems_Struct{ + uint32 items[80]; +}; + +struct BecomeTrader_Struct{ + uint32 id; + uint32 code; +}; + +struct Trader_ShowItems_Struct{ + uint32 code; + uint32 traderid; + uint32 unknown08[3]; +}; + +struct TraderBuy_Struct { +/*000*/ uint32 Action; +/*004*/ uint32 Unknown004; +/*008*/ uint32 Price; +/*012*/ uint32 Unknown008; // Probably high order bits of a 64 bit price. +/*016*/ uint32 TraderID; +/*020*/ char ItemName[64]; +/*084*/ uint32 Unknown076; +/*088*/ uint32 ItemID; +/*092*/ uint32 AlreadySold; +/*096*/ uint32 Quantity; +/*100*/ uint32 Unknown092; +/*104*/ +}; + +struct TraderItemUpdate_Struct{ + uint32 unknown0; + uint32 traderid; + uint8 fromslot; + uint8 toslot; //7? + uint16 charges; +}; + +struct MoneyUpdate_Struct{ + int32 platinum; + int32 gold; + int32 silver; + int32 copper; +}; + +//struct MoneyUpdate_Struct +//{ +//*0000*/ uint32 spawn_id; // ***Placeholder +//*0004*/ uint32 cointype; // Coin Type +//*0008*/ uint32 amount; // Amount +//*0012*/ +//}; + + +struct TraderDelItem_Struct{ + uint32 slotid; + uint32 quantity; + uint32 unknown; +}; + +struct TraderClick_Struct{ + uint32 traderid; + uint32 unknown4[2]; + uint32 approval; +}; + +struct FormattedMessage_Struct{ + uint32 unknown0; + uint32 string_id; + uint32 type; + char message[0]; +//*0???*/ uint8 unknown0[8]; // ***Placeholder +}; +struct SimpleMessage_Struct{ + uint32 string_id; + uint32 color; + uint32 unknown8; +}; + +struct GuildMemberEntry_Struct { + char name[1]; //variable length + uint32 level; //network byte order + uint32 banker; //1=yes, 0=no, network byte order + uint32 class_; //network byte order + uint32 rank; //network byte order + uint32 time_last_on; //network byte order + uint32 tribute_enable; //network byte order + uint32 total_tribute; //total guild tribute donated, network byte order + uint32 last_tribute; //unix timestamp + uint32 unknown_one; //unknown, set to one. (network byte order) + char public_note[1]; //variable length. + uint16 zoneinstance; //network byte order + uint16 zone_id; //network byte order +/* 42 + strings */ +}; + +struct GuildMembers_Struct { //just for display purposes, this is not actually used in the message encoding. + char player_name[1]; //variable length. + uint32 count; //network byte order + GuildMemberEntry_Struct member[0]; +}; + +struct GuildMOTD_Struct{ +/*0000*/ uint32 unknown0; +/*0004*/ char name[64]; +/*0068*/ char setby_name[64]; +/*0132*/ uint32 unknown132; +/*0136*/ char motd[0]; //was 512 +}; + +struct GuildURL_Struct{ +/*0000*/ uint32 unknown0; //index? seen server send 0 w/ the Guild URL, followed by 1 with nothing. +/*0004*/ uint32 unknown4; +/*0008*/ uint32 unknown8; //seen 7 +/*0068*/ char setby_name[64]; +/*0132*/ uint32 unknown132; //seen 0x167 +/*0136*/ char url[4080]; +}; + +struct GuildMemberUpdate_Struct { +/*00*/ uint32 guild_id; +/*04*/ char member_name[64]; +/*68*/ uint16 zone_id; +/*70*/ uint16 instance_id; +/*72*/ uint32 some_timestamp; +}; + +struct GuildMemberLevelUpdate_Struct { +/*00*/ uint32 guild_id; +/*04*/ char member_name[64]; +/*68*/ uint32 level; //not sure +}; + + + +struct GuildUpdate_PublicNote{ + uint32 unknown0; + char name[64]; + char target[64]; + char note[100]; //we are cutting this off at 100, actually around 252 +}; +struct GuildDemoteStruct{ + char name[64]; + char target[64]; +}; +struct GuildRemoveStruct{ + char target[64]; + char name[64]; + uint32 unknown128; + uint32 leaderstatus; //? +}; +struct GuildMakeLeader{ + char name[64]; + char target[64]; +}; + + + +struct BugStruct{ +/*0000*/ char chartype[64]; +/*0064*/ char name[96]; +/*0160*/ char ui[128]; +/*0288*/ float x; +/*0292*/ float y; +/*0296*/ float z; +/*0300*/ float heading; +/*0304*/ uint32 unknown304; +/*0308*/ uint32 type; +/*0312*/ char unknown312[2144]; +/*2456*/ char bug[1024]; +/*3480*/ char placeholder[2]; +/*3482*/ char system_info[4098]; +}; +struct Make_Pet_Struct { //Simple struct for getting pet info + uint8 level; + uint8 class_; + uint16 race; + uint8 texture; + uint8 pettype; + float size; + uint8 type; + uint32 min_dmg; + uint32 max_dmg; +}; +struct Ground_Spawn{ + float max_x; + float max_y; + float min_x; + float min_y; + float max_z; + float heading; + char name[16]; + uint32 item; + uint32 max_allowed; + uint32 respawntimer; +}; +struct Ground_Spawns { + struct Ground_Spawn spawn[50]; //Assigned max number to allow +}; +struct PetitionBug_Struct{ + uint32 petition_number; + uint32 unknown4; + char accountname[64]; + uint32 zoneid; + char name[64]; + uint32 level; + uint32 class_; + uint32 race; + uint32 unknown152[3]; + uint32 time; + uint32 unknown168; + char text[1028]; +}; + +struct DyeStruct +{ + union + { + struct + { + struct Color_Struct head; + struct Color_Struct chest; + struct Color_Struct arms; + struct Color_Struct wrists; + struct Color_Struct hands; + struct Color_Struct legs; + struct Color_Struct feet; + struct Color_Struct primary; // you can't actually dye this + struct Color_Struct secondary; // or this + } + dyes; + struct Color_Struct dye[9]; + }; +}; + +struct ApproveZone_Struct { + char name[64]; + uint32 zoneid; + uint32 approve; +}; +struct ZoneInSendName_Struct { + uint32 unknown0; + char name[64]; + char name2[64]; + uint32 unknown132; +}; +struct ZoneInSendName_Struct2 { + uint32 unknown0; + char name[64]; + uint32 unknown68[145]; +}; + +static const uint32 MAX_TRIBUTE_TIERS = 10; + +struct StartTribute_Struct { + uint32 client_id; + uint32 tribute_master_id; + uint32 response; +}; + +struct TributeLevel_Struct { + uint32 level; //backwards byte order! + uint32 tribute_item_id; //backwards byte order! + uint32 cost; //backwards byte order! +}; + +struct TributeAbility_Struct { + uint32 tribute_id; //backwards byte order! + uint32 tier_count; //backwards byte order! + TributeLevel_Struct tiers[MAX_TRIBUTE_TIERS]; + char name[0]; +}; + +struct GuildTributeAbility_Struct { + uint32 guild_id; + TributeAbility_Struct ability; +}; + +struct SelectTributeReq_Struct { + uint32 client_id; //? maybe action ID? + uint32 tribute_id; + uint32 unknown8; //seen E3 00 00 00 +}; + +struct SelectTributeReply_Struct { + uint32 client_id; //echoed from request. + uint32 tribute_id; + char desc[0]; +}; + +struct TributeInfo_Struct { + uint32 active; //0 == inactive, 1 == active + uint32 tributes[MAX_PLAYER_TRIBUTES]; //-1 == NONE + uint32 tiers[MAX_PLAYER_TRIBUTES]; //all 00's + uint32 tribute_master_id; +}; + +struct TributeItem_Struct { + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; +}; + +struct TributePoint_Struct { + int32 tribute_points; + uint32 unknown04; + int32 career_tribute_points; + uint32 unknown12; +}; + +struct TributeMoney_Struct { + uint32 platinum; + uint32 tribute_master_id; + int32 tribute_points; +}; + + +struct Split_Struct +{ + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; +}; + + +/* +** New Combine Struct +** Client requesting to perform a tradeskill combine +** Size: 4 bytes +** Used In: OP_TradeSkillCombine +** Last Updated: Oct-15-2003 +** +*/ +struct NewCombine_Struct { +/*00*/ int16 container_slot; +/*02*/ char unknown02[2]; +/*04*/ +}; + + +//client requesting favorite recipies +struct TradeskillFavorites_Struct { + uint32 object_type; + uint32 some_id; + uint32 favorite_recipes[500]; +}; + +//search request +struct RecipesSearch_Struct { + uint32 object_type; //same as in favorites + uint32 some_id; //same as in favorites + uint32 mintrivial; + uint32 maxtrivial; + char query[56]; + uint32 unknown4; //is set to 00 03 00 00 + uint32 unknown5; //is set to 4C DD 12 00 +/*80*/ +}; + +//one sent for each item, from server in reply to favorites or search +struct RecipeReply_Struct { + uint32 object_type; + uint32 some_id; //same as in favorites + uint32 component_count; + uint32 recipe_id; + uint32 trivial; + char recipe_name[64]; +/*84*/ +}; + +//received and sent back as an ACK with different reply_code +struct RecipeAutoCombine_Struct { + uint32 object_type; + uint32 some_id; + uint32 unknown1; //echoed in reply + uint32 recipe_id; + uint32 reply_code; // 93 64 e1 00 (junk) in request + // 00 00 00 00 in successful reply + // f5 ff ff ff in 'you dont have all the stuff' reply +}; + +struct LevelAppearance_Struct { //Sends a little graphic on level up + uint32 spawn_id; + uint32 parm1; + uint32 value1a; + uint32 value1b; + uint32 parm2; + uint32 value2a; + uint32 value2b; + uint32 parm3; + uint32 value3a; + uint32 value3b; + uint32 parm4; + uint32 value4a; + uint32 value4b; + uint32 parm5; + uint32 value5a; + uint32 value5b; +/*64*/ +}; +struct MerchantList{ + uint32 id; + uint32 slot; + uint32 item; +}; +struct TempMerchantList{ + uint32 npcid; + uint32 slot; + uint32 item; + uint32 charges; //charges/quantity + uint32 origslot; +}; + + +struct FindPerson_Point { + float y; + float x; + float z; +}; + +struct FindPersonRequest_Struct +{ +/*000*/ uint32 unknown000; +/*004*/ uint32 npc_id; +/*008*/ FindPerson_Point client_pos; +/*020*/ uint32 unknown020; +/*024*/ +}; + +//variable length packet of points +struct FindPersonResult_Struct { + FindPerson_Point dest; + FindPerson_Point path[0]; //last element must be the same as dest +}; + +struct MobRename_Struct { +/*000*/ char old_name[64]; +/*064*/ char old_name_again[64]; //not sure what the difference is +/*128*/ char new_name[64]; +/*192*/ uint32 unknown192; //set to 0 +/*196*/ uint32 unknown196; //set to 1 +/*200*/ +}; + +struct PlayMP3_Struct { + char filename[128]; +}; + +//this is for custom title display in the skill window +struct TitleEntry_Struct { + uint32 skill_id; + uint32 skill_value; + char title[1]; +}; + +struct Titles_Struct { + uint32 title_count; + TitleEntry_Struct titles[0]; +}; + +//this is for title selection by the client +struct TitleListEntry_Struct { + uint32 unknown0; //title ID + char prefix[1]; //variable length, null terminated + char postfix[1]; //variable length, null terminated +}; + +struct TitleList_Struct { + uint32 title_count; + TitleListEntry_Struct titles[0]; //list of title structs + //uint32 unknown_ending; seen 0x7265, 0 +}; + +struct SetTitle_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + uint32 title_id; +}; + +struct SetTitleReply_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + char title[32]; + uint32 entity_id; +}; + + +#if 0 +// Old struct not used by Task System implementation but left for reference +struct TaskDescription_Struct { +/*000*/ uint32 activity_count; //not right. +/*004*/ uint32 taskid; +/*008*/ uint8 unk; +/*009*/ uint32 id3; +/*013*/ uint32 unknown13; +/*017*/ char name[1]; //variable length, 0 terminated +/*018*/ uint32 unknown18; +/*022*/ uint32 unknown22; +/*026*/ char desc[1]; //variable length, 0 terminated +/*027*/ uint32 reward_count; //not sure +/*031*/ uint32 unknown31; +/*035*/ uint32 unknown35; +/*039*/ uint16 unknown39; +/*041*/ char reward_link[1]; //variable length, 0 terminated +/*042*/ uint32 unknown43; //maybe crystal count? +}; +#endif + +struct TaskMemberList_Struct { +/*00*/ uint32 gopher_id; +/*04*/ uint32 unknown04; +/*08*/ uint32 member_count; //1 less than the number of members +/*12*/ char list_pointer[0]; +/* list is of the form: + char member_name[1] //null terminated string + uint8 task_leader //boolean flag +*/ +}; + +#if 0 +// Old structs not used by Task System implentation but left for reference +struct TaskActivity_Struct { +/*000*/ uint32 activity_count; //not right +/*004*/ uint32 id3; +/*008*/ uint32 taskid; +/*012*/ uint32 activity_id; +/*016*/ uint32 unknown016; +/*020*/ uint32 activity_type; +/*024*/ uint32 unknown024; +/*028*/ uint32 unknown28; +/*032*/ char mob_name[1]; //variable length, 0 terminated +/*033*/ char item_name[1]; //variable length, 0 terminated +/*034*/ uint32 goal_count; +/*038*/ uint32 unknown38; //0xFFFFFFFF +/*042*/ uint32 unknown42; //0xFFFFFFFF +/*046*/ uint32 unknown46; //saw 0x151,0x156 +/*050*/ uint32 unknown50; //saw 0x404,0 +/*054*/ char activity_name[1]; //variable length, 0 terminated... commonly empty +/*055*/ uint32 done_count; +/*059*/ uint32 unknown59; //=1 except on unknown and terminal activities? +/*063*/ +}; + +struct TaskHistoryEntry_Struct { + uint32 task_id; + char name[1]; + uint32 completed_time; +}; +struct TaskHistory_Struct { + uint32 completed_count; + TaskHistoryEntry_Struct entries[0]; +}; +#endif + +struct AcceptNewTask_Struct { + uint32 unknown00; + uint32 task_id; //set to 0 for 'decline' + uint32 task_master_id; //entity ID +}; + +//was all 0's from client, server replied with same op, all 0's +struct CancelTask_Struct { + uint32 SequenceNumber; + uint32 unknown4; // Only seen 0x00000002 +}; + +#if 0 +// old struct, not used by Task System implementation but left for reference. +struct AvaliableTask_Struct { + uint32 task_index; //no idea, seen 0x1 + uint32 task_master_id; //entity ID + uint32 task_id; + uint32 unknown012; + uint32 activity_count; //not sure, seen 2 + char desc[1]; //variable length, 0 terminated + uint32 reward_platinum;//not sure on these + uint32 reward_gold; + uint32 reward_silver; + uint32 reward_copper; + char some_name[1]; //variable length, 0 terminated + uint8 unknown1; + uint32 unknown2; //0xFFFFFFFF + uint32 unknown3; //0xFFFFFFFF + uint32 unknown4; //seen 0x16 + uint8 unknown5; +}; +#endif + + +// Many of the Task System packets contain variable length strings, as well as variable numbers +// of records, hence splitting them into multiple structs (header, middle, trailer) etc. +// +struct AvailableTaskHeader_Struct { + uint32 TaskCount; + uint32 unknown1; + uint32 TaskGiver; +}; + +struct AvailableTaskData1_Struct { + uint32 TaskID; + uint32 unknown1; + uint32 TimeLimit; + uint32 unknown2; +}; + +struct AvailableTaskData2_Struct { + uint32 unknown1,unknown2,unknown3,unknown4; +}; + +struct AvailableTaskTrailer_Struct { + uint32 ItemCount; + uint32 unknown1, unknown2; + uint32 StartZone; +}; + +struct TaskDescriptionHeader_Struct { + uint32 SequenceNumber; // The order the tasks appear in the journal. 0 for first task, 1 for second, etc. + uint32 TaskID; + uint32 unknown2; + uint32 unknown3; + uint8 unknown4; +}; + +struct TaskDescriptionData1_Struct { + uint32 Duration; + uint32 unknown2; + uint32 StartTime; +}; + +struct TaskDescriptionData2_Struct { + uint32 RewardCount; // ?? + uint32 unknown1; + uint32 unknown2; + uint16 unknown3; + //uint8 unknown4; +}; + +struct TaskDescriptionTrailer_Struct { + //uint16 unknown1; // 0x0012 + uint32 Points; +}; + +struct TaskActivityHeader_Struct { + uint32 TaskSequenceNumber; + uint32 unknown2; // Seen 0x00000002 + uint32 TaskID; + uint32 ActivityID; + uint32 unknown3; + uint32 ActivityType; + uint32 Optional; + uint32 unknown5; +}; + +struct TaskActivityData1_Struct { + uint32 GoalCount; + uint32 unknown1; // 0xffffffff + uint32 unknown2; // 0xffffffff + uint32 ZoneID; // seen 0x36 + uint32 unknown3; +}; + +struct TaskActivityTrailer_Struct { + uint32 DoneCount; + uint32 unknown1; // Seen 1 +}; + +// The Short_Struct is sent for tasks that are hidden and act as a placeholder +struct TaskActivityShort_Struct { + uint32 TaskSequenceNumber; + uint32 unknown2; // Seen 0x00000002 + uint32 TaskID; + uint32 ActivityID; + uint32 unknown3; + uint32 ActivityType; // 0xffffffff for the short packet + uint32 unknown4; +}; + +struct TaskActivityComplete_Struct { + uint32 TaskIndex; + uint32 unknown2; // 0x00000002 + uint32 unknown3; + uint32 ActivityID; + uint32 unknown4; // 0x00000001 + uint32 unknown5; // 0x00000001 +}; + +#if 0 +// This is a dupe of the CancelTask struct +struct TaskComplete_Struct { + uint32 unknown00; // 0x00000000 + uint32 unknown04; // 0x00000002 +}; +#endif + +struct TaskHistoryRequest_Struct { + uint32 TaskIndex; // This is the sequence the task was sent in the Completed Tasks packet. +}; + +struct TaskHistoryReplyHeader_Struct { + uint32 TaskID; + uint32 ActivityCount; +}; + +struct TaskHistoryReplyData1_Struct { + uint32 ActivityType; +}; + +struct TaskHistoryReplyData2_Struct { + uint32 GoalCount; + uint32 unknown04; // 0xffffffff + uint32 unknown08; // 0xffffffff + uint32 ZoneID; + uint32 unknown16; +}; + +struct BankerChange_Struct { + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; + uint32 platinum_bank; + uint32 gold_bank; + uint32 silver_bank; + uint32 copper_bank; +}; + +struct LeadershipExpUpdate_Struct { +/*00*/ double group_leadership_exp; +/*08*/ uint32 group_leadership_points; +/*12*/ uint32 Unknown12; +/*16*/ double raid_leadership_exp; +/*24*/ uint32 raid_leadership_points; +}; + +struct UpdateLeadershipAA_Struct { +/*00*/ uint32 ability_id; +/*04*/ uint32 new_rank; +/*08*/ uint32 unknown08; +/*12*/ +}; + +/** +* Leadership AA update +* Length: 32 Octets +* OpCode: LeadExpUpdate +*/ +struct leadExpUpdateStruct { + /*0000*/ uint32 unknown0000; // All zeroes? + /*0004*/ uint32 group_leadership_exp; // Group leadership exp value + /*0008*/ uint32 group_leadership_points; // Unspent group points + /*0012*/ uint32 unknown0012; // Type? + /*0016*/ uint32 unknown0016; // All zeroes? + /*0020*/ uint32 raid_leadership_exp; // Raid leadership exp value + /*0024*/ uint32 raid_leadership_points; // Unspent raid points + /*0028*/ uint32 unknown0028; +}; + +struct RaidGeneral_Struct { +/*00*/ uint32 action; +/*04*/ char player_name[64]; +/*68*/ uint32 unknown68; +/*72*/ char leader_name[64]; +/*136*/ uint32 parameter; +}; + +struct RaidAddMember_Struct { +/*000*/ RaidGeneral_Struct raidGen; //param = (group num-1); 0xFFFFFFFF = no group +/*136*/ uint8 _class; +/*137*/ uint8 level; +/*138*/ uint8 isGroupLeader; +/*139*/ uint8 flags[5]; //no idea if these are needed... +}; + +struct RaidAdd_Struct { +/*000*/ uint32 action; //=0 +/*004*/ char player_name[64]; //should both be the player's name +/*068*/ char leader_name[64]; +/*132*/ uint8 _class; +/*133*/ uint8 level; +/*134*/ uint8 has_group; +/*135*/ uint8 unknown135; //seems to be 0x42 or 0 +}; + +struct RaidCreate_Struct { +/*00*/ uint32 action; //=8 +/*04*/ char leader_name[64]; +/*68*/ uint32 leader_id; +}; + +struct RaidMemberInfo_Struct { +/*00*/ uint8 group_number; +/*01*/ char member_name[1]; //dyanmic length, null terminated '\0' +/*00*/ uint8 unknown00; +/*01*/ uint8 _class; +/*02*/ uint8 level; +/*03*/ uint8 is_raid_leader; +/*04*/ uint8 is_group_leader; +/*05*/ uint8 main_tank; //not sure +/*06*/ uint8 unknown06[5]; //prolly more flags +}; + +struct RaidDetails_Struct { +/*000*/ uint32 action; //=6,20 +/*004*/ char leader_name[64]; +/*068*/ uint32 unknown68[4]; +/*084*/ LeadershipAA_Struct abilities; //ranks in backwards byte order +/*128*/ uint8 unknown128[142]; +/*354*/ uint32 leader_id; +}; + +struct RaidMembers_Struct { +/*000*/ RaidDetails_Struct details; +/*358*/ uint32 member_count; //including leader +/*362*/ RaidMemberInfo_Struct members[1]; +/*...*/ RaidMemberInfo_Struct empty; //seem to have an extra member with a 0 length name on the end +}; + +struct DynamicWall_Struct { +/*00*/ char name[32]; +/*32*/ float y; +/*36*/ float x; +/*40*/ float z; +/*44*/ uint32 something; +/*48*/ uint32 unknown48; //0 +/*52*/ uint32 one_hundred; //0x64 +/*56*/ uint32 unknown56; //0 +/*60*/ uint32 something2; +/*64*/ int32 unknown64; //-1 +/*68*/ uint32 unknown68; //0 +/*72*/ uint32 unknown72; //0 +/*76*/ uint32 unknown76; //0x100 +/*80*/ +}; + +enum { //bandolier actions + BandolierCreate = 0, + BandolierRemove = 1, + BandolierSet = 2 +}; + +struct BandolierCreate_Struct { +/*00*/ uint32 action; //0 for create +/*04*/ uint8 number; +/*05*/ char name[32]; +/*37*/ uint16 unknown37; //seen 0x93FD +/*39*/ uint8 unknown39; //0 +}; + +struct BandolierDelete_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ uint8 unknown05[35]; +}; + +struct BandolierSet_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ uint8 unknown05[35]; +}; + +// Not 100% sure on this struct. Live as of 1/1/11 is different than UF. Seems to work 'OK' +struct Arrow_Struct +{ +/*000*/ float src_y; +/*004*/ float src_x; +/*008*/ float src_z; +/*012*/ uint8 unknown012[12]; +/*024*/ float velocity; //4 is normal, 20 is quite fast +/*028*/ float launch_angle; //0-450ish, not sure the units, 140ish is straight +/*032*/ float tilt; //on the order of 125 +/*036*/ uint8 unknown036[8]; +/*044*/ float arc; +/*048*/ uint32 source_id; +/*052*/ uint32 target_id; //entity ID +/*056*/ uint32 item_id; +/*060*/ uint8 unknown060[10]; +/*070*/ uint8 unknown070; +/*071*/ uint8 item_type; +/*072*/ uint8 skill; +/*073*/ uint8 unknown073[13]; +/*086*/ char model_name[30]; +/*116*/ +}; + +//made a bunch of trivial structs for stuff for opcode finder to use +struct Consent_Struct { + char name[1]; //always at least a null - was 1 +}; + +struct AdventureMerchant_Struct { + uint32 unknown_flag; //seems to be 1 + uint32 entity_id; +}; + +struct Save_Struct { + uint8 unknown00[192]; + uint8 unknown0192[176]; +}; + +struct GMToggle_Struct { + uint8 unknown0[64]; + uint32 toggle; +}; + +struct BuffFadeMsg_Struct { + uint32 color; + char msg[1]; //was 1 +/*0???*/ uint8 paddingXXX[3]; // always 0's +}; + +struct UseAA_Struct { + uint32 begin; + uint32 ability; + uint32 end; +}; + +struct AA_Ability { +/*00*/ uint32 skill_id; +/*04*/ uint32 base1; +/*08*/ uint32 base2; +/*12*/ uint32 slot; +/*16*/ +}; + +struct SendAA_Struct { +/*0000*/ uint32 id; +/*0004*/ uint8 unknown004; // uint32 unknown004; set to 1. +/*0005*/ uint32 hotkey_sid; +/*0009*/ uint32 hotkey_sid2; +/*0013*/ uint32 title_sid; +/*0017*/ uint32 desc_sid; +/*0021*/ uint32 class_type; +/*0025*/ uint32 cost; +/*0029*/ uint32 seq; +/*0033*/ uint32 current_level; //1s, MQ2 calls this AARankRequired +/*0037*/ uint32 prereq_skill; //is < 0, abs() is category # +/*0041*/ uint32 prereq_minpoints; //min points in the prereq +/*0045*/ uint32 type; +/*0049*/ uint32 spellid; +/*0053*/ uint32 spell_type; +/*0057*/ uint32 spell_refresh; +/*0061*/ uint16 classes; +/*0063*/ uint16 berserker; //seems to be 1 if its a berserker ability +/*0065*/ uint32 max_level; +/*0069*/ uint32 last_id; +/*0073*/ uint32 next_id; +/*0077*/ uint32 cost2; +/*0081*/ uint8 unknown80[7]; +/*0088*/ uint32 aa_expansion; +/*0092*/ uint32 special_category; +/*0096*/ uint32 unknown0096; +/*0100*/ uint32 total_abilities; +/*0104*/ AA_Ability abilities[0]; +}; + +struct AA_List { + SendAA_Struct* aa[0]; +}; + +struct AA_Action { +/*00*/ uint32 action; +/*04*/ uint32 ability; +/*08*/ uint32 unknown08; +/*12*/ uint32 exp_value; +}; + +struct AA_Skills { //this should be removed and changed to AA_Array +/*00*/ uint32 aa_skill; // Total AAs Spent +/*04*/ uint32 aa_value; +/*08*/ uint32 unknown08; +}; + +struct AAExpUpdate_Struct { +/*00*/ uint32 unknown00; //seems to be a value from AA_Action.ability +/*04*/ uint32 aapoints_unspent; +/*08*/ uint8 aaxp_percent; //% of exp that goes to AAs +/*09*/ uint8 unknown09[3]; //live doesn't always zero these, so they arnt part of aaxp_percent +}; + + +struct AltAdvStats_Struct { +/*000*/ uint32 experience; +/*004*/ uint16 unspent; +/*006*/ uint16 unknown006; +/*008*/ uint8 percentage; +/*009*/ uint8 unknown009[3]; +}; + +struct PlayerAA_Struct { // Is this still used? + AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct AATable_Struct { +/*00*/ int32 aa_spent; // Total AAs Spent +/*04*/ int32 aa_assigned; // Assigned: field in the AA window. +/*08*/ int32 aa_spent3; // Unknown. Same as aa_spent in observed packets. +/*12*/ int32 unknown012; +/*16*/ int32 unknown016; +/*20*/ int32 unknown020; +/*24*/ AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct Weather_Struct { + uint32 val1; //generall 0x000000FF + uint32 type; //0x31=rain, 0x02=snow(i think), 0 = normal + uint32 mode; +}; + +struct ZoneInUnknown_Struct { + uint32 val1; + uint32 val2; + uint32 val3; +}; + +struct MobHealth_Struct { + uint16 entity_id; + uint8 hp; +}; + +struct AnnoyingZoneUnknown_Struct { + uint32 entity_id; + uint32 value; //always 4 +}; + +struct LoadSpellSet_Struct { + uint8 spell[MAX_PP_MEMSPELL]; // 0 if no action + uint16 unknown2; // is this two more spell gems? + uint32 unknown; // there seems to be an extra field in this packet... +}; + +struct BlockedBuffs_Struct { +/*000*/ uint8 unknown000[80]; +/*080*/ uint8 unknown081; +/*081*/ uint8 unknown082; +/*082*/ uint8 unknown083; +/*083*/ uint8 unknown084; +/*084*/ uint8 unknown085; +/*085*/ uint8 unknown086; +/*086*/ uint8 unknown087; +/*087*/ uint8 unknown088; +/*088*/ +}; + +//Size 24 Bytes +struct WorldObfuscator_Struct +{ +/*000*/ uint32 var1; +/*004*/ uint32 Unknown1; +/*008*/ uint32 Unknown2; +/*012*/ uint32 Unknown3; +/*016*/ uint32 var2; +/*020*/ uint32 Unknown4; +/*024*/ +}; + +struct ExpansionInfo_Struct { +/*000*/ char Unknown000[64]; +/*064*/ uint32 Expansions; +}; + +struct ApplyPoison_Struct { + uint32 inventorySlot; + uint32 success; +}; + +struct ItemVerifyRequest_Struct { +/*000*/ int32 slot; // Slot being Right Clicked +/*004*/ uint32 target; // Target Entity ID +/*008*/ +}; + +struct ItemVerifyReply_Struct { +/*000*/ int32 slot; // Slot being Right Clicked +/*004*/ uint32 spell; // Spell ID to cast if different than item effect +/*008*/ uint32 target; // Target Entity ID +/*012*/ +}; + +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 +}; + +struct ItemBodyStruct +{ + uint32 id; + uint8 weight; + uint8 norent; + uint8 nodrop; + uint8 attune; + uint8 size; + uint32 slots; + uint32 price; + uint32 icon; + uint8 unknown1; + uint8 unknown2; + uint32 BenefitFlag; + uint8 tradeskills; + int8 CR; + int8 DR; + int8 PR; + int8 MR; + int8 FR; + int8 SVCorruption; + int8 AStr; + int8 ASta; + int8 AAgi; + int8 ADex; + int8 ACha; + int8 AInt; + int8 AWis; + int32 HP; + int32 Mana; + uint32 Endur; + int32 AC; + int32 regen; + int32 mana_regen; + int32 end_regen; + uint32 Classes; + uint32 Races; + uint32 Deity; + int32 SkillModValue; + uint32 unknown5; + uint32 SkillModType; + uint32 BaneDmgRace; + uint32 BaneDmgBody; + uint32 BaneDmgRaceAmt; + int32 BaneDmgAmt; + uint8 Magic; + int32 CastTime_; + uint32 ReqLevel; + uint32 RecLevel; + uint32 RecSkill; + uint32 BardType; + int32 BardValue; + uint8 Light; + uint8 Delay; + uint8 ElemDmgType; + uint8 ElemDmgAmt; + uint8 Range; + uint32 Damage; + uint32 Color; + uint8 ItemType; + uint32 Material; + uint32 unknown7; + uint32 EliteMaterial; + float SellRate; + int32 CombatEffects; + int32 Shielding; + int32 StunResist; + int32 StrikeThrough; + int32 ExtraDmgSkill; + int32 ExtraDmgAmt; + int32 SpellShield; + int32 Avoidance; + int32 Accuracy; + uint32 CharmFileID; //not sure the point of this; disables all stats on item if > 0 + uint32 FactionMod1; + int32 FactionAmt1; + uint32 FactionMod2; + int32 FactionAmt2; + uint32 FactionMod3; + int32 FactionAmt3; + uint32 FactionMod4; + int32 FactionAmt4; +}; + +struct AugSlotStruct +{ + uint32 type; + uint8 visible; + uint8 unknown; +}; + +struct ItemTertiaryBodyStruct +{ + int32 loregroup; + uint8 artifact; + uint8 summonedflag; + uint32 favor; + uint8 fvnodrop; + int32 dotshield; + int32 atk; + int32 haste; + int32 damage_shield; + uint32 guildfavor; + uint32 augdistil; + int32 unknown3; + uint32 unknown4; + uint8 no_pet; + uint8 unknown5; + + uint8 potion_belt_enabled; + uint32 potion_belt_slots; + + uint32 stacksize; + uint8 no_transfer; + uint16 expendablearrow; + + uint32 unknown8; + uint32 unknown9; + uint32 unknown10; + uint32 unknown11; + uint8 unknown12; + uint8 unknown13; + uint8 unknown14; +}; + +struct ClickEffectStruct +{ + int32 effect; + uint8 level2; + uint32 type; + uint8 level; + int32 max_charges; + int32 cast_time; + uint32 recast; + int32 recast_type; + uint32 clickunk5; + //uint8 effect_string; //unused + //int32 clickunk7; +}; + +struct ProcEffectStruct +{ + uint32 effect; + uint8 level2; + uint32 type; + uint8 level; + uint32 unknown1; // poison? + uint32 unknown2; + uint32 unknown3; + uint32 unknown4; + uint32 procrate; + //uint8 effect_string; + //uint32 unknown5; +}; + +struct WornEffectStruct //worn, focus and scroll effect +{ + uint32 effect; + uint8 level2; + uint32 type; + uint8 level; + uint32 unknown1; + uint32 unknown2; + uint32 unknown3; + uint32 unknown4; + uint32 unknown5; + //uint8 effect_string; + //uint32 unknown6; +}; + + +struct ItemSecondaryBodyStruct{ + uint32 augtype; + uint32 augrestrict; + AugSlotStruct augslots[5]; + + uint32 ldonpoint_type; + uint32 ldontheme; + uint32 ldonprice; + uint32 ldonsellbackrate; + uint32 ldonsold; + + uint8 bagtype; + uint8 bagslots; + uint8 bagsize; + uint8 wreduction; + + uint8 book; + uint8 booktype; + //int32 filename; filename is either 0xffffffff/0x00000000 or the null term string ex: CREWizardNote\0 +}; + +struct ItemQuaternaryBodyStruct +{ + + uint32 scriptfileid; + uint8 quest_item; + uint32 unknown15; //0xffffffff - Power Source Capacity? + uint32 Purity; + uint32 BackstabDmg; + uint32 DSMitigation; + int32 HeroicStr; + int32 HeroicInt; + int32 HeroicWis; + int32 HeroicAgi; + int32 HeroicDex; + int32 HeroicSta; + int32 HeroicCha; + int32 HeroicMR; + int32 HeroicFR; + int32 HeroicCR; + int32 HeroicDR; + int32 HeroicPR; + int32 HeroicSVCorrup; + int32 HealAmt; + int32 SpellDmg; + int32 clairvoyance; + uint8 unknown18; //Power Source Capacity or evolve filename? + uint32 evolve_string; // Some String, but being evolution related is just a guess + uint8 unknown19; + uint32 unknown20; // Bard Stuff? + uint32 unknown21; + uint32 unknown22; + uint32 subitem_count; +}; + +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*/ +}; + +struct VeteranRewardItem +{ +/*000*/ uint32 item_id; +/*004*/ uint32 charges; +/*008*/ char item_name[64]; +}; + +struct VeteranReward +{ +/*000*/ uint32 claim_id; +/*004*/ uint32 number_available; +/*008*/ uint32 claim_count; +/*012*/ VeteranRewardItem items[8]; +}; + +struct ExpeditionEntryHeader_Struct +{ +/*000*/ uint32 unknown000; +/*000*/ uint32 number_of_entries; +}; + +struct ExpeditionJoinPrompt_Struct +{ +/*000*/ uint32 clientid; +/*004*/ uint32 unknown004; +/*008*/ char player_name[64]; +/*072*/ char expedition_name[64]; +}; + +struct ExpeditionExpireWarning +{ +/*000*/ uint32 clientid; +/*004*/ uint32 unknown004; +/*008*/ uint32 minutes_remaining; +}; + +struct ExpeditionInfo_Struct +{ +/*000*/ uint32 clientid; +/*004*/ uint32 unknown004; +/*008*/ uint32 unknown008; +/*012*/ uint32 max_players; +/*016*/ char expedition_name[128]; +/*142*/ char leader_name[64]; +}; + +struct ExpeditionCompassEntry_Struct +{ +/*000*/ float unknown000; //seen *((uint32*)) = 1584791871 +/*004*/ uint32 enabled; //guess +/*008*/ uint32 unknown008; //seen 1019 +/*012*/ float y; +/*016*/ float x; +/*020*/ float z; +}; + +struct ExpeditionCompass_Struct +{ +/*000*/ uint32 clientid; +/*004*/ uint32 count; +/*008*/ ExpeditionCompassEntry_Struct entries[0]; +}; + +struct AltCurrencySelectItem_Struct { + uint32 merchant_entity_id; + uint32 slot_id; + uint32 unknown008; + uint32 unknown012; + uint32 unknown016; + uint32 unknown020; + uint32 unknown024; + uint32 unknown028; + uint32 unknown032; + uint32 unknown036; + uint32 unknown040; + uint32 unknown044; + uint32 unknown048; + uint32 unknown052; + uint32 unknown056; + uint32 unknown060; + uint32 unknown064; + uint32 unknown068; + uint32 unknown072; + uint32 unknown076; +}; + +struct AltCurrencySellItem_Struct { +/*000*/ uint32 merchant_entity_id; +/*004*/ uint32 slot_id; +/*006*/ uint32 charges; +/*010*/ uint32 cost; +}; + +struct AltCurrencyPopulateEntry_Struct +{ +/*000*/ uint32 currency_number; //corresponds to a dbstr id as well, the string matches what shows up in the "alternate currency" tab. +/*004*/ uint32 unknown00; //always 1 +/*008*/ uint32 currency_number2; //always same as currency number +/*012*/ uint32 item_id; //appears to be the item id +/*016*/ uint32 item_icon; //actual icon +/*020*/ uint32 stack_size; //most are set to 1000, the stack size for the item; should match db i think or there will be problems. +/*024*/ uint8 unknown024; //seen 0 and 1 +}; + +struct AltCurrencyPopulate_Struct { +/*000*/ uint32 opcode; //8 for populate +/*004*/ uint32 count; //number of entries +/*008*/ AltCurrencyPopulateEntry_Struct entries[0]; +}; + +// Used by MercenaryListEntry_Struct +struct MercenaryStance_Struct { +/*0000*/ uint32 StanceIndex; // Index of this stance (sometimes reverse reverse order - 3, 2, 1, 0 for 4 stances etc) +/*0004*/ uint32 Stance; // From dbstr_us.txt - 1^24^Passive^0, 2^24^Balanced^0, etc (1 to 9 as of April 2012) +}; + +// Used by MercenaryMerchantList_Struct +struct MercenaryListEntry_Struct { +/*0000*/ uint32 MercID; // ID unique to each type of mercenary (probably a DB id) +/*0004*/ uint32 MercType; // From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) +/*0008*/ uint32 MercSubType; // From dbstr_us.txt - 330020105^23^Race: Guktan
Type: Healer
Confidence: High
Proficiency: Apprentice, Tier V... +/*0012*/ uint32 PurchaseCost; // Purchase Cost (in gold) +/*0016*/ uint32 UpkeepCost; // Upkeep Cost (in gold) +/*0020*/ uint32 AltCurrencyCost; // Alternate Currency Purchase Cost? (all seen costs show N/A Bayle Mark) - Seen 0 +/*0024*/ uint32 AltCurrencyUpkeep; // Alternate Currency Upkeep Cost? (all seen costs show 1 Bayle Mark) - Seen 1 +/*0028*/ uint32 AltCurrencyType; // Alternate Currency Type? - 19^17^Bayle Mark^0 - Seen 19 +/*0032*/ uint8 MercUnk01; // Unknown (always see 0) +/*0033*/ int32 TimeLeft; // Unknown (always see -1 at merchant) - Seen 900000 (15 minutes in ms for newly hired merc) +/*0037*/ uint32 MerchantSlot; // Merchant Slot? Increments, but not always by 1 - May be for Merc Window Options (Seen 5, 36, 1 for active mercs)? +/*0041*/ uint32 MercUnk02; // Unknown (normally see 1, but sometimes 2 or 0) +/*0045*/ uint32 StanceCount; // Iterations of MercenaryStance_Struct - Normally 2 to 4 seen +/*0049*/ int32 MercUnk03; // Unknown (always 0 at merchant) - Seen on active merc: 93 a4 03 77, b8 ed 2f 26, 88 d5 8b c3, and 93 a4 ad 77 +/*0053*/ uint8 MercUnk04; // Seen 1 +/*0054*/ MercenaryStance_Struct Stances[1]; // Count Varies, but hard set to 2 for now - From dbstr_us.txt - 1^24^Passive^0, 2^24^Balanced^0, etc (1 to 9 as of April 2012) +}; + +// [OPCode: 0x27ac OP_MercenaryDataResponse] On Live as of April 2 2012 [Server->Client] +// Opcode should be renamed to something like OP_MercenaryMerchantShopResponse since the Data Response packet is different +// Sent by the server when browsing the Mercenary Merchant +struct MercenaryMerchantList_Struct { +/*0000*/ uint32 MercTypeCount; // Number of Merc Types to follow +/*0004*/ uint32 MercTypes[1]; // Count varies, but hard set to 3 for now - From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) +/*0016*/ uint32 MercCount; // Number of MercenaryInfo_Struct to follow +/*0020*/ MercenaryListEntry_Struct Mercs[0]; // Data for individual mercenaries in the Merchant List +}; + +// [OPCode: 0x4dd9 OP_MercenaryDataRequest] On Live as of April 2 2012 [Client->Server] +// Opcode should be renamed to something like OP_MercenaryMerchantShopRequest since the Data Request packet is different +// Right clicking merchant - shop request +struct MercenaryMerchantShopRequest_Struct { +/*0000*/ uint32 MercMerchantID; // Entity ID of the Mercenary Merchant +/*0004*/ +}; + +// Used by MercenaryDataUpdate_Struct +struct MercenaryData_Struct { +/*0000*/ uint32 MercID; // ID unique to each type of mercenary (probably a DB id) +/*0004*/ uint32 MercType; // From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) +/*0008*/ uint32 MercSubType; // From dbstr_us.txt - 330020105^23^Race: Guktan
Type: Healer
Confidence: High
Proficiency: Apprentice, Tier V... +/*0012*/ uint32 PurchaseCost; // Purchase Cost (in gold) +/*0016*/ uint32 UpkeepCost; // Upkeep Cost (in gold) +/*0020*/ uint32 AltCurrencyCost; // Alternate Currency Purchase Cost? (all seen costs show N/A Bayle Mark) - Seen 0 +/*0024*/ uint32 AltCurrencyUpkeep; // Alternate Currency Upkeep Cost? (all seen costs show 1 Bayle Mark) - Seen 1 +/*0028*/ uint32 AltCurrencyType; // Alternate Currency Type? - 19^17^Bayle Mark^0 - Seen 19 +/*0032*/ uint8 MercUnk01; // Unknown (always see 0) +/*0033*/ int32 TimeLeft; // Unknown (always see -1 at merchant) - Seen 900000 (15 minutes in ms for newly hired merc) +/*0037*/ uint32 MerchantSlot; // Merchant Slot? Increments, but not always by 1 - May be for Merc Window Options (Seen 5, 36, 1 for active mercs)? +/*0041*/ uint32 MercUnk02; // Unknown (normally see 1, but sometimes 2 or 0) +/*0045*/ uint32 StanceCount; // Iterations of MercenaryStance_Struct - Normally 2 to 4 seen +/*0049*/ int32 MercUnk03; // Unknown (always 0 at merchant) - Seen on active merc: 93 a4 03 77, b8 ed 2f 26, 88 d5 8b c3, and 93 a4 ad 77 +/*0053*/ uint8 MercUnk04; // Seen 1 +/*0054*/ MercenaryStance_Struct Stances[1]; // Count Varies, but hard set to 2 for now - From dbstr_us.txt - 1^24^Passive^0, 2^24^Balanced^0, etc (1 to 9 as of April 2012) +/*0000*/ uint32 MercUnk05; // Seen 1 - Extra Merc Data field that differs from MercenaryListEntry_Struct +// MercUnk05 may be a field that is at the end of the packet only, even if multiple mercs are listed (haven't seen examples of multiple mercs owned at once) +}; + +// [OPCode: 0x6537] On Live as of April 2 2012 [Server->Client] +// Should be named OP_MercenaryDataResponse, but the current opcode using that name should be renamed first +// Size varies if mercenary is hired or if browsing Mercenary Merchant +// This may also be the response for Client->Server 0x0327 (size 0) packet On Live as of April 2 2012 +struct MercenaryDataUpdate_Struct { +/*0000*/ int32 MercStatus; // Seen 0 with merc and -1 with no merc hired +/*0004*/ uint32 MercCount; // Seen 1 with 1 merc hired and 0 with no merc hired +/*0008*/ MercenaryData_Struct MercData[0]; // Data for individual mercenaries in the Merchant List +}; + +// [OPCode: 0x6537] On Live as of April 2 2012 [Server->Client] +// Size 12 and sent on Zone-In if no mercenary is currently hired and when merc is dismissed +// (Same packet as MercAssign_Struct?) +struct NoMercenaryHired_Struct { +/*0000*/ int32 MercStatus; // Seen -1 with no merc hired +/*0004*/ uint32 MercCount; // Seen 0 with no merc hired +/*0008*/ uint32 MercID; // Seen 1 when no merc is hired - ID unique to each type of mercenary +/*0012*/ +}; + +// [OPCode: 0x495d OP_MercenaryTimer] On Live as of April 2 2012 [Server->Client] [Size: 20] +// Sent on Zone-In, or after Dismissing, Suspending, or Unsuspending Mercs +struct MercenaryStatus_Struct { +/*0000*/ uint32 MercEntityID; // Seen 0 (no merc spawned) or 615843841 and 22779137 +/*0004*/ uint32 UpdateInterval; // Seen 900000 - Matches from 0x6537 packet (15 minutes in ms?) +/*0008*/ uint32 MercUnk01; // Seen 180000 - 3 minutes in milleseconds? Maybe next update interval? +/*0012*/ uint32 MercState; // Seen 5 (normal) or 1 (suspended) +/*0016*/ uint32 SuspendedTime; // Seen 0 (not suspended) or c9 c2 64 4f (suspended on Sat Mar 17 11:58:49 2012) - Unix Timestamp +/*0020*/ +}; + +// [OPCode: 0x4c6c] On Live as of April 2 2012 [Client->Server] [Size: 8] +// Sent from the client when using the Mercenary Window +struct MercenaryCommand_Struct { +/*0000*/ uint32 MercCommand; // Seen 0 (zone in with no merc or suspended), 1 (dismiss merc), 5 (normal state), 36 (zone in with merc) +/*0004*/ int32 Option; // Seen -1 (zone in with no merc), 0 (setting to passive stance), 1 (normal or setting to balanced stance) +/*0008*/ +}; + +// [OPCode: 0x1a79] On Live as of April 2 2012 [Client->Server] [Size: 1] +// Requesting to suspend or unsuspend merc +struct SuspendMercenary_Struct { +/*0000*/ uint8 SuspendMerc; // Seen 30 (48) for suspending or unsuspending +/*0001*/ +}; + +// [OPCode: 0x2528] On Live as of April 2 2012 [Server->Client] [Size: 4] +// Response to suspend merc with timestamp +struct SuspendMercenaryResponse_Struct { +/*0000*/ uint32 SuspendTime; // Unix Timestamp - Seen a9 11 78 4f +/*0004*/ +}; + +// [OPCode: 0x5e78 (OP_MercenaryHire?)] On Live as of April 2 2012 +// Sent by client when requesting to view Mercenary info or Hire a Mercenary +struct MercenaryMerchantRequest_Struct { +/*0000*/ uint32 RequestType; // Seen 399 for a Hire Request and 400 for a view merc info request? (may actually be merc ID) +/*0004*/ uint32 MercUnk01; // Seen 1 +/*0008*/ uint32 MercMerchantID; // Entity ID for Mercenary Merchant +/*0012*/ uint32 MercUnk02; // Seen 65302016 (00 6e e4 03) - (probably actually individual uint8 fields) +/*0016*/ +}; + +// [OPCode: 0x5e78 (OP_MercenaryHire?)] On Live as of April 2 2012 +// Sent by Server in response to requesting to view Mercenary info or Hire a Mercenary +struct MercenaryMerchantResponse_Struct { +/*0000*/ uint32 ResponseType; // Seen 0 for hire response, 6 for info response, and 9 for denied hire request +/*0004*/ +}; + +// (Same packet as NoMercenaryHired_Struct?) +struct MercenaryAssign_Struct { +/*0000*/ uint32 MercEntityID; // Seen 0 (no merc spawned) or 615843841 and 22779137 +/*0004*/ uint32 MercUnk01; // +/*0008*/ uint32 MercUnk02; // +/*0012*/ +}; + + }; //end namespace structs +}; //end namespace Underfoot + +#endif /*Underfoot_STRUCTS_H_*/ diff --git a/common/patches/makepatch b/common/patches/makepatch new file mode 100644 index 000000000..c270e6f26 --- /dev/null +++ b/common/patches/makepatch @@ -0,0 +1,12 @@ +#!/bin/sh + +if [ -z "$1" ]; then + echo "Usage: $0 [patch name]" + exit 1 +fi + +for ext in .cpp .h _ops.h _structs.h +do + cp template$ext $1$ext + perl -pi -e "s/TEMPLATE/$1/g" $1$ext +done diff --git a/common/patches/patches.cpp b/common/patches/patches.cpp new file mode 100644 index 000000000..3261b3153 --- /dev/null +++ b/common/patches/patches.cpp @@ -0,0 +1,28 @@ + +#include "../debug.h" +#include "patches.h" + +#include "Client62.h" +#include "Titanium.h" +#include "Underfoot.h" +#include "SoF.h" +#include "SoD.h" +#include "RoF.h" + +void RegisterAllPatches(EQStreamIdentifier &into) { + Client62::Register(into); + Titanium::Register(into); + SoF::Register(into); + SoD::Register(into); + Underfoot::Register(into); + RoF::Register(into); +} + +void ReloadAllPatches() { + Client62::Reload(); + Titanium::Reload(); + SoF::Reload(); + SoD::Reload(); + Underfoot::Reload(); + RoF::Reload(); +} diff --git a/common/patches/patches.h b/common/patches/patches.h new file mode 100644 index 000000000..8383a17fa --- /dev/null +++ b/common/patches/patches.h @@ -0,0 +1,15 @@ +#ifndef PATCHES_H_ +#define PATCHES_H_ + +/*enum { + Patch_062, + Patch_Titanium, + Patch_Live +};*/ + +class EQStreamIdentifier; + +void RegisterAllPatches(EQStreamIdentifier &into); +void ReloadAllPatches(); + +#endif /*PATCHES_H_*/ diff --git a/common/patches/template.cpp b/common/patches/template.cpp new file mode 100644 index 000000000..017cfb6d3 --- /dev/null +++ b/common/patches/template.cpp @@ -0,0 +1,173 @@ + +#include "TEMPLATE.h" +#include "../opcodemgr.h" +#include "../logsys.h" +#include "../EQStreamIdent.h" + +#include "../eq_packet_structs.h" +#include "TEMPLATE_structs.h" + +namespace TEMPLATE { + +static const char *name = "CHANGEME"; +static OpcodeManager *opcodes = NULL; +static Strategy struct_strategy; + +void Register(EQStreamIdentifier &into) { + //create our opcode manager if we havent already + if(opcodes == NULL) { + 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; + + //register our world signature. + signature.first_length = sizeof(structs::LoginInfo_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); + into.RegisterPatch(signature, name, &opcodes, &struct_strategy); + + //register our zone signature. + signature.first_length = sizeof(structs::ClientZoneEntry_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); + into.RegisterPatch(signature, name, &opcodes, &struct_strategy); +} + +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 != NULL) { + //TODO: get this file name from the config file + 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 "TEMPLATE_ops.h" +} + +std::string Strategy::Describe() const { + std::string r; + r += "Patch "; + r += name; + return(r); +} + + +#include "SSDefine.h" + + +/*ENCODE(OP_PlayerProfile) { + SETUP_DIRECT(PlayerProfile_Struct, structs::PlayerProfile_Struct); + + + + FINISH_DIRECT(); +} + +ENCODE(OP_NewZone) { + SETUP_DIRECT(PlayerProfile_Struct, structs::PlayerProfile_Struct); + + + + FINISH_DIRECT(); +}*/ + +ENCODE(OP_SendAATable) { + SETUP_DIRECT_ENCODE(SendAA_Struct, structs::SendAA_Struct); + OUT(id); + OUT(hotkey_sid); + OUT(hotkey_sid2); + OUT(title_sid); + OUT(desc_sid); + 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].increase_amt); + OUT(abilities[r].unknown08); + OUT(abilities[r].last_level); + } + FINISH_DIRECT_ENCODE(); +} + +DECODE(OP_SetServerFilter) { + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + int r; + for(r = 0; r < 25; r++) { + IN(filters[r]); + } + emu->filters[25] = 1; + FINISH_DIRECT_DECODE(); +} + + + + + + + + + + + + + + + + + + + + + +} //end namespace TEMPLATE + + + + + + diff --git a/common/patches/template.h b/common/patches/template.h new file mode 100644 index 000000000..3e379eefe --- /dev/null +++ b/common/patches/template.h @@ -0,0 +1,37 @@ +#ifndef TEMPLATE_H_ +#define TEMPLATE_H_ + +#include "../StructStrategy.h" + +class EQStreamIdentifier; + +namespace TEMPLATE { + + //these are the only public member of this namespace. + extern void Register(EQStreamIdentifier &into); + extern void Reload(); + + + + //you should not directly access anything below.. + //I just dont feel like making a seperate header for it. + + class Strategy : public StructStrategy { + public: + Strategy(); + + protected: + + virtual std::string Describe() const; + + //magic macro to declare our opcodes + #include "SSDeclare.h" + #include "TEMPLATE_ops.h" + + }; + +}; + + + +#endif /*TEMPLATE_H_*/ diff --git a/common/patches/template_ops.h b/common/patches/template_ops.h new file mode 100644 index 000000000..905520155 --- /dev/null +++ b/common/patches/template_ops.h @@ -0,0 +1,10 @@ + +//list of packets we need to encode on the way out: +E(OP_SendAATable) + +//list of packets we need to decode on the way in: +D(OP_SetServerFilter) + + +#undef E +#undef D diff --git a/common/patches/template_structs.h b/common/patches/template_structs.h new file mode 100644 index 000000000..78d085f91 --- /dev/null +++ b/common/patches/template_structs.h @@ -0,0 +1,28 @@ +#ifndef TEMPLATE_STRUCTS_H_ +#define TEMPLATE_STRUCTS_H_ + +namespace TEMPLATE { + namespace structs { + + +//paste contents of eq_packet_structs.h here... + + + + }; //end namespace structs +}; //end namespace TEMPLATE + + + + +#endif /*TEMPLATE_STRUCTS_H_*/ + + + + + + + + + + diff --git a/common/perl_EQDB.cpp b/common/perl_EQDB.cpp new file mode 100644 index 000000000..0c653d666 --- /dev/null +++ b/common/perl_EQDB.cpp @@ -0,0 +1,256 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of tmp. Do not edit this file, edit tmp instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 +*/ + +typedef const char Const_char; + +#ifdef EMBPERL +#include "../common/debug.h" +#include "../common/useperl.h" +#include "EQDB.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +XS(XS_EQDB_field_count); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQDB_field_count) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQDB::field_count(THIS)"); + { + EQDB * THIS; + unsigned int RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EQDB")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQDB *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQDB"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->field_count(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQDB_affected_rows); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQDB_affected_rows) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQDB::affected_rows(THIS)"); + { + EQDB * THIS; + unsigned long RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EQDB")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQDB *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQDB"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->affected_rows(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQDB_insert_id); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQDB_insert_id) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQDB::insert_id(THIS)"); + { + EQDB * THIS; + unsigned long RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EQDB")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQDB *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQDB"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->insert_id(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQDB_get_errno); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQDB_get_errno) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQDB::get_errno(THIS)"); + { + EQDB * THIS; + unsigned int RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EQDB")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQDB *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQDB"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->get_errno(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQDB_error); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQDB_error) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQDB::error(THIS)"); + { + EQDB * THIS; + Const_char * RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EQDB")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQDB *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQDB"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->error(); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_EQDB_query); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQDB_query) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQDB::query(THIS, q)"); + { + EQDB * THIS; + EQDBRes * RETVAL; + Const_char * q = (Const_char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EQDB")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQDB *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQDB"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->query(q); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "EQDBRes", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQDB_escape_string); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQDB_escape_string) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQDB::escape_string(THIS, from)"); + { + EQDB * THIS; + Const_char * RETVAL; + dXSTARG; + Const_char * from = (Const_char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EQDB")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQDB *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQDB"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->escape_string(from); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_EQDB); /* prototype to pass -Wmissing-prototypes */ +XS(boot_EQDB) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "field_count"), XS_EQDB_field_count, file, "$"); + newXSproto(strcpy(buf, "affected_rows"), XS_EQDB_affected_rows, file, "$"); + newXSproto(strcpy(buf, "insert_id"), XS_EQDB_insert_id, file, "$"); + newXSproto(strcpy(buf, "get_errno"), XS_EQDB_get_errno, file, "$"); + newXSproto(strcpy(buf, "error"), XS_EQDB_error, file, "$"); + newXSproto(strcpy(buf, "query"), XS_EQDB_query, file, "$$"); + newXSproto(strcpy(buf, "escape_string"), XS_EQDB_escape_string, file, "$$"); + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES + diff --git a/common/perl_EQDBRes.cpp b/common/perl_EQDBRes.cpp new file mode 100644 index 000000000..bb09284a8 --- /dev/null +++ b/common/perl_EQDBRes.cpp @@ -0,0 +1,295 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of tmp. Do not edit this file, edit tmp instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 +*/ + +typedef const char Const_char; + +#ifdef EMBPERL +#include "../common/debug.h" +#include "../common/useperl.h" +#include "EQDBRes.h" + + +XS(XS_EQDBRes_num_rows); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQDBRes_num_rows) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQDBRes::num_rows(THIS)"); + { + EQDBRes * THIS; + unsigned long RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EQDBRes")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQDBRes *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQDBRes"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->num_rows(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQDBRes_num_fields); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQDBRes_num_fields) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQDBRes::num_fields(THIS)"); + { + EQDBRes * THIS; + unsigned long RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EQDBRes")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQDBRes *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQDBRes"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->num_fields(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQDBRes_DESTROY); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQDBRes_DESTROY) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQDBRes::DESTROY(THIS)"); + { + EQDBRes * THIS; + + if (SvROK(ST(0))) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQDBRes *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not a reference"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + delete THIS; + } + XSRETURN_EMPTY; +} + +XS(XS_EQDBRes_finish); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQDBRes_finish) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQDBRes::finish(THIS)"); + { + EQDBRes * THIS; + + if (sv_derived_from(ST(0), "EQDBRes")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQDBRes *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQDBRes"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->finish(); + } + XSRETURN_EMPTY; +} + +XS(XS_EQDBRes_fetch_row_array); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQDBRes_fetch_row_array) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQDBRes::fetch_row_array(THIS)"); + { + EQDBRes * THIS; + vector RETVAL; + + if (sv_derived_from(ST(0), "EQDBRes")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQDBRes *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQDBRes"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->fetch_row_array(); + ST(0) = sv_newmortal(); + { + U32 ix_RETVAL; + /* pop crap off the stack we dont really want */ + POPs; + POPs; + /* grow the stack to the number of elements being returned */ + EXTEND(SP, RETVAL.size()); + for (ix_RETVAL = 0; ix_RETVAL < RETVAL.size(); ix_RETVAL++) { + const string &it = RETVAL[ix_RETVAL]; + ST(ix_RETVAL) = sv_newmortal(); + sv_setpvn(ST(ix_RETVAL), it.c_str(), it.length()); + } + /* hackish, but im over it. The normal xsubpp return will be right below this */ + XSRETURN(RETVAL.size()); + } + } + XSRETURN(1); +} + +XS(XS_EQDBRes_fetch_row_hash); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQDBRes_fetch_row_hash) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQDBRes::fetch_row_hash(THIS)"); + { + EQDBRes * THIS; + map RETVAL; + + if (sv_derived_from(ST(0), "EQDBRes")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQDBRes *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQDBRes"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->fetch_row_hash(); + ST(0) = sv_newmortal(); + if (RETVAL.begin()!=RETVAL.end()) + { + //NOTE: we are leaking the original ST(0) right now + HV *hv = newHV(); + sv_2mortal((SV*)hv); + ST(0) = newRV((SV*)hv); + + map::const_iterator cur, end; + cur = RETVAL.begin(); + end = RETVAL.end(); + for(; cur != end; cur++) { + /* get the element from the hash, creating if needed (will be needed) */ + SV**ele = hv_fetch(hv, cur->first.c_str(), cur->first.length(), TRUE); + if(ele == NULL) { + Perl_croak(aTHX_ "Unable to create a hash element for RETVAL"); + break; + } + /* put our string in the SV associated with this element in the hash */ + sv_setpvn(*ele, cur->second.c_str(), cur->second.length()); + } + } + + + + + + + + + + + + + + + + + + } + XSRETURN(1); +} + +XS(XS_EQDBRes_fetch_lengths); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQDBRes_fetch_lengths) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQDBRes::fetch_lengths(THIS)"); + { + EQDBRes * THIS; + unsigned long * RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EQDBRes")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQDBRes *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQDBRes"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->fetch_lengths(); + XSprePUSH; PUSHp((char *)RETVAL, sizeof(*RETVAL)); + } + XSRETURN(1); +} + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_EQDBRes); /* prototype to pass -Wmissing-prototypes */ +XS(boot_EQDBRes) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "num_rows"), XS_EQDBRes_num_rows, file, "$"); + newXSproto(strcpy(buf, "num_fields"), XS_EQDBRes_num_fields, file, "$"); + newXSproto(strcpy(buf, "DESTROY"), XS_EQDBRes_DESTROY, file, "$"); + newXSproto(strcpy(buf, "finish"), XS_EQDBRes_finish, file, "$"); + newXSproto(strcpy(buf, "fetch_row_array"), XS_EQDBRes_fetch_row_array, file, "$"); + newXSproto(strcpy(buf, "fetch_row_hash"), XS_EQDBRes_fetch_row_hash, file, "$"); + newXSproto(strcpy(buf, "fetch_lengths"), XS_EQDBRes_fetch_lengths, file, "$"); + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES + diff --git a/common/platform.cpp b/common/platform.cpp new file mode 100644 index 000000000..6ec98b364 --- /dev/null +++ b/common/platform.cpp @@ -0,0 +1,11 @@ +#include "platform.h" + +EQEmuExePlatform exe_platform = ExePlatformNone; + +void RegisterExecutablePlatform(EQEmuExePlatform p) { + exe_platform = p; +} + +const EQEmuExePlatform& GetExecutablePlatform() { + return exe_platform; +} diff --git a/common/platform.h b/common/platform.h new file mode 100644 index 000000000..09eeeb7b2 --- /dev/null +++ b/common/platform.h @@ -0,0 +1,18 @@ +#ifndef EQEMU_PLATFORM_H +#define EQEMU_PLATFORM_H + +enum EQEmuExePlatform +{ + ExePlatformNone = 0, + ExePlatformZone, + ExePlatformWorld, + ExePlatformLogin, + ExePlatformQueryServ, + ExePlatformUCS, + ExePlatformLaunch, +}; + +void RegisterExecutablePlatform(EQEmuExePlatform p); +const EQEmuExePlatform& GetExecutablePlatform(); + +#endif diff --git a/common/profiler.h b/common/profiler.h new file mode 100644 index 000000000..425fb9f2b --- /dev/null +++ b/common/profiler.h @@ -0,0 +1,92 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 PROFILER_H +#define PROFILER_H + +#ifdef EQPROFILE + +#include "../common/rdtsc.h" +#include "../common/types.h" + +class ScopedProfiler; + +class GeneralProfiler { + friend class ScopedProfiler; +public: + inline GeneralProfiler(unsigned int _count) { + count = _count; + timers = new RDTSC_Collector[count]; + } + inline virtual ~GeneralProfiler() { + safe_delete_array(timers); + } + + inline double getTotalDuration(unsigned int id) { + return(idreset(); + } + + RDTSC_Collector *timers; + unsigned int count; +}; + +class ScopedProfiler { +public: + inline ScopedProfiler(RDTSC_Collector *c) { + _it = c; + c->start(); + } + inline ~ScopedProfiler() { + _it->stop(); + } +protected: + RDTSC_Collector *_it; +}; + + +#define _GP(obj, pkg, name) ScopedProfiler __eqemu_profiler(&obj.timers[pkg::name]) + +#else // else !EQPROFILE + //no profiling, dummy functions +#define _GP(obj, pkg, name) ; + +#endif + +#endif diff --git a/common/ptimer.cpp b/common/ptimer.cpp new file mode 100644 index 000000000..1b2ac4593 --- /dev/null +++ b/common/ptimer.cpp @@ -0,0 +1,504 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 "debug.h" + +#include "timer.h" +#include "ptimer.h" +#include "database.h" +#include "MiscFunctions.h" +#include +#include +#include + +#ifdef _WINDOWS + #include + #include + int gettimeofday (timeval *tp, ...); +#else + #include +#endif + +#if EQDEBUG > 10 + #define DEBUG_PTIMERS +#endif + + +/* +Persistent timers, By Father Nitwit + +The idea of persistent timers is timers which follow a player +through zoning. You use operations on them much like regular +timers: Start, Check, Clear but they also provide methods +to store them in the DB: Load and Store. + +All durations are in seconds. + +Each persistent timer is attached to a character, and given +a specific type. A given character can only have one timer +of each type. While the type is just an arbitrary number, +please record what you are using it for in the enum for +pTimerType at the top of ptimer.h, and give it a UNIQUE number. + +There should be little need to directly access ptimers. Each +client has a facility called p_timers which should handle +most of what you need. The idea is that instead of making +your own PersistentTimer, you use the methods on p_timers: +Start, Check, Clear, GetRemainingTime to access them. You +starting a timer which does not exist will create it. If +you need to do more than that with your timer, you should +still use p_timers, just use the Get() method to get direct +access to the PersistentTimer. All timers in the p_timers +list will automatically be loaded and stored to the database. +You should never need to call any Load or Store methods. + +To get to p_timers when you are not in the Client:: scope, +use client->GetPTimers(). to access timers. + +To use ptimers, you need to create the table below in your DB: + +Schema: + +CREATE TABLE timers ( + char_id INT(11) NOT NULL, + type MEDIUMINT UNSIGNED NOT NULL, + start INT UNSIGNED NOT NULL, + duration INT UNSIGNED NOT NULL, + enable TINYINT NOT NULL, + PRIMARY KEY(char_id, type) +); + + +*/ + + +//#define DEBUG_PTIMERS + + +PersistentTimer *PersistentTimer::LoadTimer(Database *db, uint32 char_id, pTimerType type) { + PersistentTimer *p; + p = new PersistentTimer(char_id, type, 0); + if(p->Load(db)) + return(p); + delete p; + return(NULL); +} + +PersistentTimer::PersistentTimer(uint32 char_id, pTimerType type, uint32 in_timer_time) { + _char_id = char_id; + _type = type; + + timer_time = in_timer_time; + start_time = get_current_time(); + if (timer_time == 0) { + enabled = false; + } else { + enabled = true; + } +#ifdef DEBUG_PTIMERS + printf("New timer: char %lu of type %u at %lu for %lu seconds.\n", (unsigned long)_char_id, _type, (unsigned long)start_time, (unsigned long)timer_time); +#endif +} + +PersistentTimer::PersistentTimer(uint32 char_id, pTimerType type, uint32 in_start_time, uint32 in_timer_time, bool in_enable) { + _char_id = char_id; + _type = type; + + timer_time = in_timer_time; + start_time = in_start_time; + enabled = in_enable; +#ifdef DEBUG_PTIMERS + printf("New stored timer: char %lu of type %u at %lu for %lu seconds.\n", (unsigned long)_char_id, _type, (unsigned long)start_time, (unsigned long)timer_time); +#endif +} + +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); +#if EQDEBUG > 5 + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Load, error: %s", errbuf); +#endif + 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], NULL, 10); + timer_time = strtoul(row[1], NULL, 10); + enabled = (row[2][0] == '1'); + + res = true; + } + mysql_free_result(result); + + return(res); +} + +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); + + +#ifdef DEBUG_PTIMERS + printf("Storing timer: char %lu of type %u: '%s'\n", (unsigned long)_char_id, _type, query); +#endif + + if (!db->RunQuery(query, qlen, errbuf)) { + safe_delete_array(query); +#if EQDEBUG > 5 + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Store, error: %s", errbuf); +#endif + return(false); + } + safe_delete_array(query); + + 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); + +#ifdef DEBUG_PTIMERS + printf("Clearing timer: char %lu of type %u: '%s'\n", (unsigned long)_char_id, _type, query); +#endif + + if (!db->RunQuery(query, qlen, errbuf)) { + safe_delete_array(query); +#if EQDEBUG > 5 + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Clear, error: %s", errbuf); +#endif + return(false); + } + safe_delete_array(query); + + return(true); + +} + +/* This function checks if the timer triggered */ +bool PersistentTimer::Expired(Database *db, bool iReset) { + if (this == NULL) { + LogFile->write(EQEMuLog::Error, "Null timer during ->Check()!?\n"); + return(true); + } + uint32 current_time = get_current_time(); + if (current_time-start_time >= timer_time) { + if (enabled && iReset) { + start_time = current_time; // Reset timer + } else if(enabled) { + Clear(db); //remove it from DB too + } + return(true); + } + + return(false); +} + +/* This function set the timer and restart it */ +void PersistentTimer::Start(uint32 set_timer_time) { + start_time = get_current_time(); + enabled = true; + if (set_timer_time != 0) { + timer_time = set_timer_time; + } +#ifdef DEBUG_PTIMERS + printf("Starting timer: char %lu of type %u at %lu for %lu seconds.\n", (unsigned long)_char_id, _type, (unsigned long)start_time, (unsigned long)timer_time); +#endif +} + +// This timer updates the timer without restarting it +void PersistentTimer::SetTimer(uint32 set_timer_time) { + // If we were disabled before => restart the timer + timer_time = set_timer_time; + if (!enabled) { + start_time = get_current_time(); + enabled = true; + } +#ifdef DEBUG_PTIMERS + printf("Setting timer: char %lu of type %u at %lu for %lu seconds.\n", (unsigned long)_char_id, _type, (unsigned long)start_time, (unsigned long)timer_time); +#endif +} + +uint32 PersistentTimer::GetRemainingTime() { + if (enabled) { + uint32 current_time = get_current_time(); + if (current_time-start_time > timer_time) + return 0; + else + return (start_time + timer_time) - current_time; + } + else { + return 0xFFFFFFFF; + } +} + + +uint32 PersistentTimer::get_current_time() { + timeval tv; + gettimeofday(&tv, NULL); + return(tv.tv_sec); +} + +PTimerList::PTimerList(uint32 char_id) { + _char_id = char_id; +} + +PTimerList::~PTimerList() { + map::iterator s; + s = _list.begin(); + while(s != _list.end()) { + if(s->second != NULL) + delete s->second; + s++; + } +} + + +bool PTimerList::Load(Database *db) { + map::iterator s; + s = _list.begin(); + while(s != _list.end()) { + if(s->second != NULL) + delete s->second; + s++; + } + _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); +#if EQDEBUG > 5 + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Load, error: %s", errbuf); +#endif + 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)) ) { + type = atoi(row[0]); + start_time = strtoul(row[1], NULL, 10); + timer_time = strtoul(row[2], NULL, 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(false)) + _list[type] = cur; + else + delete cur; + } + mysql_free_result(result); + + return(true); +} + +bool PTimerList::Store(Database *db) { +#ifdef DEBUG_PTIMERS + printf("Storing all timers for char %lu\n", (unsigned long)_char_id); +#endif + + map::iterator s; + s = _list.begin(); + bool res = true; + while(s != _list.end()) { + if(s->second != NULL) { +#ifdef DEBUG_PTIMERS + printf("Storing timer %u for char %lu\n", s->first, (unsigned long)_char_id); +#endif + if(!s->second->Store(db)) + res = false; + } + s++; + } + return(res); +} + +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); + +#ifdef DEBUG_PTIMERS + printf("Storing all timers for char %lu: '%s'\n", (unsigned long)_char_id, query); +#endif + + if (!db->RunQuery(query, qlen, errbuf)) { + safe_delete_array(query); +#if EQDEBUG > 5 + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Clear, error: %s", errbuf); +#endif + return(false); + } + safe_delete_array(query); + + return(true); +} + +void PTimerList::Start(pTimerType type, uint32 duration) { + if(_list.count(type) == 1 && _list[type] != NULL) { + _list[type]->Start(duration); + } else { + _list[type] = new PersistentTimer(_char_id, type, duration); + } +} + +void PTimerList::Clear(Database *db, pTimerType type) { + if(_list.count(type) == 1) { + if(_list[type] != NULL) { + _list[type]->Clear(db); + delete _list[type]; + } + _list.erase(type); + } +} + +bool PTimerList::Expired(Database *db, pTimerType type, bool reset) { + if(_list.count(type) != 1) + return(true); + if(_list[type] == NULL) + return(true); + return(_list[type]->Expired(db, reset)); +} + +bool PTimerList::Enabled(pTimerType type) { + if(_list.count(type) != 1) + return(false); + if(_list[type] == NULL) + return(false); + return(_list[type]->Enabled()); +} + +void PTimerList::Enable(pTimerType type) { + if(_list.count(type) == 1 && _list[type] != NULL) + _list[type]->Enable(); +} + +void PTimerList::Disable(pTimerType type) { + if(_list.count(type) == 1 && _list[type] != NULL) + _list[type]->Disable(); +} + +uint32 PTimerList::GetRemainingTime(pTimerType type) { + if(_list.count(type) != 1) + return(0); + if(_list[type] == NULL) + return(0); + return(_list[type]->GetRemainingTime()); +} + +PersistentTimer *PTimerList::Get(pTimerType type) { + if(_list.count(type) != 1) + return(NULL); + return(_list[type]); +} + +void PTimerList::ToVector(vector< pair > &out) { + + pair p; + + map::iterator s; + s = _list.begin(); + while(s != _list.end()) { + if(s->second != NULL) { + p.first = s->first; + p.second = s->second; + out.push_back(p); + } + s++; + } +} + +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); + +#ifdef DEBUG_PTIMERS + printf("Clearing timer (offline): char %lu of type %u: '%s'\n", (unsigned long)char_id, type, query); +#endif + + if (!db->RunQuery(query, qlen, errbuf)) { + safe_delete_array(query); +#if EQDEBUG > 5 + LogFile->write(EQEMuLog::Error, "Error in PTimerList::ClearOffline, error: %s", errbuf); +#endif + return(false); + } + safe_delete_array(query); + + return(true); + + +} diff --git a/common/ptimer.h b/common/ptimer.h new file mode 100644 index 000000000..919f0cfd4 --- /dev/null +++ b/common/ptimer.h @@ -0,0 +1,145 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 PTIMER_H +#define PTIMER_H + +#include "types.h" +#include +#include +using namespace std; + +enum { //values for pTimerType + pTimerStartAdventureTimer = 1, + pTimerSurnameChange = 2, + pTimerFeignDeath = 3, + pTimerSneak = 4, + pTimerHide = 5, + pTimerTaunt = 6, + pTimerInstillDoubt = 7, + pTimerFishing = 8, + pTimerForaging = 9, + pTimerMend = 10, + pTimerTracking = 11, + pTimerSenseTraps = 12, + pTimerDisarmTraps = 13, + pTimerDisciplineReuseStart = 14, + pTimerDisciplineReuseEnd = 24, + pTimerCombatAbility = 25, + pTimerBeggingPickPocket = 26, + + pTimerLayHands = 87, //these IDs are used by client too + pTimerHarmTouch = 89, //so dont change them + + pTimerItemStart = 100, + pTimerItemEnd = 200, //I don't think any items would use one this high but hey, incase. + pTimerPeqzoneReuse = 900, + pTimerMercReuse = 901, + pTimerMercSuspend = 902, + pTimerAAStart = 1000, //AA re-use timers + pTimerAAEnd = 2999, + pTimerAAEffectStart = 3001, //AA effect timers + pTimerAAEffectEnd = 4999, + + pTimerSpellStart = 5000 //Do not put any timer IDs above this one + //if needed, increase its starting ID +}; + +class Database; + +typedef uint16 pTimerType; + +class PersistentTimer { +public: + static PersistentTimer *LoadTimer(Database *db, uint32 char_id, pTimerType type); + + PersistentTimer(uint32 char_id, pTimerType type, uint32 duration); + PersistentTimer(uint32 char_id, pTimerType type, uint32 start_time, uint32 duration, bool enable); + + bool Expired(Database *db, bool iReset = true); + void Start(uint32 set_timer_time=0); + + void SetTimer(uint32 set_timer_time=0); + uint32 GetRemainingTime(); + inline void Enable() { enabled = true; } + inline void Disable() { enabled = false; } + inline const uint32 GetTimerTime() const { return timer_time; } + inline const uint32 GetStartTime() const { return start_time; } + inline const pTimerType GetType() const { return _type; } + + inline bool Enabled() { return enabled; } + + bool Load(Database *db); + bool Store(Database *db); + bool Clear(Database *db); + +protected: + uint32 get_current_time(); + + uint32 start_time; + uint32 timer_time; + bool enabled; + + uint32 _char_id; + pTimerType _type; +}; + +//a list of persistent timers for a specific character +class PTimerList { +public: + PTimerList(uint32 char_id = 0); + + ~PTimerList(); + + bool Load(Database *db); + bool Store(Database *db); + bool Clear(Database *db); + + void Start(pTimerType type, uint32 duration); + bool Expired(Database *db, pTimerType type, bool reset = true); + void Clear(Database *db, pTimerType type); + void Enable(pTimerType type); + bool Enabled(pTimerType type); + void Disable(pTimerType type); + uint32 GetRemainingTime(pTimerType type); + PersistentTimer *Get(pTimerType type); + + inline void SetCharID(uint32 char_id) { _char_id = char_id; } + + void ToVector(vector< pair > &out); + + //Clear a timer for a char not logged in + //this is not defined on a char which is logged in! + static bool ClearOffline(Database *db, uint32 char_id, pTimerType type); + + typedef map::iterator iterator; + iterator begin() { return(_list.begin()); } + iterator end() { return(_list.end()); } + + + void AddTimer(pTimerType type, uint32 start_time, uint32 duration, bool enable); +protected: + uint32 _char_id; + + map _list; +}; + +//code prettying macros +#define AA_Choose3(val, v1, v2, v3) (val==1?v1:(val==2?v2:v3)) +#define AA_Choose5(val, v1, v2, v3, v4, v5) (val==1?v1:(val==2?v2:(val==3?v3:(val==4?v4:v5)))) + +#endif diff --git a/common/queue.h b/common/queue.h new file mode 100644 index 000000000..c79e81e19 --- /dev/null +++ b/common/queue.h @@ -0,0 +1,126 @@ +/* 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 QUEUE_H +#define QUEUE_H + +template +class MyQueue; + +template +class MyQueueNode +{ +public: + MyQueueNode(T* data) + { + next = 0; + this->data = data; + } + + friend class MyQueue; + +private: + T* data; + MyQueueNode* next; +}; + +template +class MyQueue +{ +public: + MyQueue() + { + head = tail = 0; + } + ~MyQueue() { + clear(); + } + + void push(T* data) + { + if (head == 0) + { + tail = head = new MyQueueNode(data); + } + else + { + tail->next = new MyQueueNode(data); + tail = tail->next; + } + } + + T* pop() + { + if (head == 0) + { + return 0; + } + + T* data = head->data; + MyQueueNode* next_node = head->next; + delete head; + head = next_node; + + return data; + } + + T* top() + { + if (head == 0) + { + return 0; + } + + return head->data; + } + + bool empty() + { + if (head == 0) + { + return true; + } + + return false; + } + + void clear() + { + T* d = 0; + while((d = pop())) { + delete d; + } + return; + } + + int count() + { + int count = 0; + MyQueueNode* d = head; + while(d != 0) { + count++; + d = d->next; + } + return(count); + } + +private: + MyQueueNode* head; + MyQueueNode* tail; +}; + +#endif diff --git a/common/queues.h b/common/queues.h new file mode 100644 index 000000000..66125e650 --- /dev/null +++ b/common/queues.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA */ + +/* + Code for generell handling of priority Queues. + Implemention of queues from "Algoritms in C" by Robert Sedgewick. + Copyright Monty Program KB. + By monty. +*/ + +#ifndef _queues_h +#define _queues_h +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct st_queue { + byte **root; + void *first_cmp_arg; + uint elements; + uint max_elements; + uint offset_to_key; /* compare is done on element+offset */ + int max_at_top; /* Set if queue_top gives max */ + int (*compare)(void *, byte *,byte *); +} QUEUE; + +#define queue_top(queue) ((queue)->root[1]) +#define queue_element(queue,index) ((queue)->root[index+1]) +#define queue_end(queue) ((queue)->root[(queue)->elements]) +#define queue_replaced(queue) _downheap(queue,1) + +int init_queue(QUEUE *queue,uint max_elements,uint offset_to_key, + pbool max_at_top, int (*compare)(void *,byte *, byte *), + void *first_cmp_arg); +int reinit_queue(QUEUE *queue,uint max_elements,uint offset_to_key, + pbool max_at_top, int (*compare)(void *,byte *, byte *), + void *first_cmp_arg); +void delete_queue(QUEUE *queue); +void queue_insert(QUEUE *queue,byte *element); +byte *queue_remove(QUEUE *queue,uint idx); +void _downheap(QUEUE *queue,uint idx); +#define is_queue_inited(queue) ((queue)->root != 0) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/common/races.cpp b/common/races.cpp new file mode 100644 index 000000000..a041845a3 --- /dev/null +++ b/common/races.cpp @@ -0,0 +1,113 @@ +/* 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 "../common/debug.h" +#include "../common/eq_packet_structs.h" +#include "../common/races.h" + +const char* GetRaceName(uint16 race) { + switch(race) { + case HUMAN: + return "Human"; + case BARBARIAN: + return "Barbarian"; + case ERUDITE: + return "Erudite"; + case WOOD_ELF: + return "Wood Elf"; + case HIGH_ELF: + return "High Elf"; + case DARK_ELF: + return "Dark Elf"; + case HALF_ELF: + return "Half Elf"; + case DWARF: + return "Dwarf"; + case TROLL: + return "Troll"; + case OGRE: + return "Ogre"; + case HALFLING: + return "Halfling"; + case GNOME: + return "Gnome"; + case IKSAR: + return "Iksar"; + case WEREWOLF: + return "Werewolf"; + case SKELETON: + return "Skeleton"; + case ELEMENTAL: + return "Elemental"; + case EYE_OF_ZOMM: + return "Eye of Zomm"; + case WOLF_ELEMENTAL: + return "Wolf Elemental"; + case IKSAR_SKELETON: + return "Iksar Skeleton"; + case VAHSHIR: + return "Vah Shir"; + case FROGLOK: + case FROGLOK2: + return "Froglok"; + case DRAKKIN: + return "Drakkin"; + default: + return "Unknown"; + } +} + +uint32 GetArrayRace(uint16 race) { + switch(race) { + case HUMAN: + return Array_Race_HUMAN; + case BARBARIAN: + return Array_Race_BARBARIAN; + case ERUDITE: + return Array_Race_ERUDITE; + case WOOD_ELF: + return Array_Race_WOOD_ELF; + case HIGH_ELF: + return Array_Race_HIGH_ELF; + case DARK_ELF: + return Array_Race_DARK_ELF; + case HALF_ELF: + return Array_Race_HALF_ELF; + case DWARF: + return Array_Race_DWARF; + case TROLL: + return Array_Race_TROLL; + case OGRE: + return Array_Race_OGRE; + case HALFLING: + return Array_Race_HALFLING; + case GNOME: + return Array_Race_GNOME; + case IKSAR: + return Array_Race_IKSAR; + case VAHSHIR: + return Array_Race_VAHSHIR; + case FROGLOK: + case FROGLOK2: + return Array_Race_FROGLOK; + case DRAKKIN: + return Array_Race_DRAKKIN; + default: + return Array_Race_UNKNOWN; + } +} + diff --git a/common/races.h b/common/races.h new file mode 100644 index 000000000..452da9461 --- /dev/null +++ b/common/races.h @@ -0,0 +1,516 @@ +/* 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 RACES_H +#define RACES_H +#include "../common/types.h" +#include + +//theres a big list straight from the client below. + +#define HUMAN 1 +#define BARBARIAN 2 +#define ERUDITE 3 +#define WOOD_ELF 4 +#define HIGH_ELF 5 +#define DARK_ELF 6 +#define HALF_ELF 7 +#define DWARF 8 +#define TROLL 9 +#define OGRE 10 +#define HALFLING 11 +#define GNOME 12 +#define WEREWOLF 14 +#define WOLF 42 +#define BEAR 43 +#define SKELETON 60 +#define TIGER 63 +#define ELEMENTAL 75 +#define ALLIGATOR 91 +#define EYE_OF_ZOMM 108 +#define WOLF_ELEMENTAL 120 +#define INVISIBLE_MAN 127 +#define IKSAR 128 +#define VAHSHIR 130 +#define CONTROLLED_BOAT 141 +#define IKSAR_SKELETON 161 +#define FROGLOK 330 +#define FROGLOK2 74 // Not sure why /who all reports race as 74 for frogloks +#define DRAKKIN 522 // 32768 +#define EMU_RACE_NPC 131069 // was 65533 +#define EMU_RACE_PET 131070 // was 65534 +#define EMU_RACE_UNKNOWN 131071 // was 65535 + + +#define human_1 1 +#define barbarian_1 2 +#define erudite_1 4 +#define woodelf_1 8 +#define highelf_1 16 +#define darkelf_1 32 +#define halfelf_1 64 +#define dwarf_1 128 +#define troll_1 256 +#define ogre_1 512 +#define halfling_1 1024 +#define gnome_1 2048 +#define iksar_1 4096 +#define vahshir_1 8192 +#define rall_1 16384 //froglok? +#define drakkin_1 32768 + +const char* GetRaceName(uint16 race); + +uint32 GetArrayRace(uint16 race); +inline uint32 GetRaceBitmask(uint16 race) { return uint32(pow(2.0f, float(GetArrayRace(race) - 1))); } + +#define Array_Race_UNKNOWN 0 +#define Array_Race_HUMAN 1 +#define Array_Race_BARBARIAN 2 +#define Array_Race_ERUDITE 3 +#define Array_Race_WOOD_ELF 4 +#define Array_Race_HIGH_ELF 5 +#define Array_Race_DARK_ELF 6 +#define Array_Race_HALF_ELF 7 +#define Array_Race_DWARF 8 +#define Array_Race_TROLL 9 +#define Array_Race_OGRE 10 +#define Array_Race_HALFLING 11 +#define Array_Race_GNOME 12 +#define Array_Race_IKSAR 13 +#define Array_Race_VAHSHIR 14 +#define Array_Race_FROGLOK 15 +#define Array_Race_DRAKKIN 16 +#define Array_Race_NPC 17 +#define Array_Race_PET 18 +#define Count_Array_Race 19 // used for array defines, must be the max + 1 +#define PLAYER_RACE_COUNT 16 // The count of all player races + +/* + +//pulled from the client by ksmith: +$races_table = array( + 1 => "Human", + 2 => "Barbarian", + 3 => "Erudite", + 4 => "Wood Elf", + 5 => "High Elf", + 6 => "Dark Elf", + 7 => "Half Elf", + 8 => "Dwarf", + 9 => "Troll", + 10 => "Ogre", + 11 => "Halfling", + 12 => "Gnome", + 13 => "Aviak", + 14 => "Were Wolf", + 15 => "Brownie", + 16 => "Centaur", + 17 => "Golem", + 18 => "Giant / Cyclops", + 19 => "Trakenon", + 20 => "Doppleganger", + 21 => "Evil Eye", + 22 => "Beetle", + 23 => "Kerra", + 24 => "Fish", + 25 => "Fairy", + 26 => "Old Froglok", + 27 => "Old Froglok Ghoul", + 28 => "Fungusman", + 29 => "Gargoyle", + 30 => "Gasbag", + 31 => "Gelatinous Cube", + 32 => "Ghost", + 33 => "Ghoul", + 34 => "Giant Bat", + 35 => "Giant Eel", + 36 => "Giant Rat", + 37 => "Giant Snake", + 38 => "Giant Spider", + 39 => "Gnoll", + 40 => "Goblin", + 41 => "Gorilla", + 42 => "Wolf", + 43 => "Bear", + 44 => "Freeport Guards", + 45 => "Demi Lich", + 46 => "Imp", + 47 => "Griffin", + 48 => "Kobold", + 49 => "Lava Dragon", + 50 => "Lion", + 51 => "Lizard Man", + 52 => "Mimic", + 53 => "Minotaur", + 54 => "Orc", + 55 => "Human Beggar", + 56 => "Pixie", + 57 => "Dracnid", + 58 => "Solusek Ro", + 59 => "Bloodgills", + 60 => "Skeleton", + 61 => "Shark", + 62 => "Tunare", + 63 => "Tiger", + 64 => "Treant", + 65 => "Vampire", + 66 => "Rallos Zek", + 67 => "Highpass Citizen", + 68 => "Tentacle", + 69 => "Will 'O Wisp", + 70 => "Zombie", + 71 => "Qeynos Citizen", + 72 => "Ship", + 73 => "Launch", + 74 => "Piranha", + 75 => "Elemental", + 76 => "Puma", + 77 => "Neriak Citizen", + 78 => "Erudite Citizen", + 79 => "Bixie", + 80 => "Reanimated Hand", + 81 => "Rivervale Citizen", + 82 => "Scarecrow", + 83 => "Skunk", + 84 => "Snake Elemental", + 85 => "Spectre", + 86 => "Sphinx", + 87 => "Armadillo", + 88 => "Clockwork Gnome", + 89 => "Drake", + 90 => "Halas Citizen", + 91 => "Alligator", + 92 => "Grobb Citizen", + 93 => "Oggok Citizen", + 94 => "Kaladim Citizen", + 95 => "Cazic Thule", + 96 => "Cockatrice", + 97 => "Daisy Man", + 98 => "Elf Vampire", + 99 => "Denizen", + 100 => "Dervish", + 101 => "Efreeti", + 102 => "Old Froglok Tadpole", + 103 => "Kedge", + 104 => "Leech", + 105 => "Swordfish", + 106 => "Felguard", + 107 => "Mammoth", + 108 => "Eye of Zomm", + 109 => "Wasp", + 110 => "Mermaid", + 111 => "Harpie", + 112 => "Fayguard", + 113 => "Drixie", + 114 => "Ghost Ship", + 115 => "Clam", + 116 => "Sea Horse", + 117 => "Ghost Dwarf", + 118 => "Erudite Ghost", + 119 => "Sabertooth Cat", + 120 => "Wolf Elemental", + 121 => "Gorgon", + 122 => "Dragon Skeleton", + 123 => "Innoruuk", + 124 => "Unicorn", + 125 => "Pegasus", + 126 => "Djinn", + 127 => "Invisible Man", + 128 => "Iksar", + 129 => "Scorpion", + 130 => "Vah Shir", + 131 => "Sarnak", + 132 => "Draglock", + 133 => "Lycanthrope", + 134 => "Mosquito", + 135 => "Rhino", + 136 => "Xalgoz", + 137 => "Kunark Goblin", + 138 => "Yeti", + 139 => "Iksar Citizen", + 140 => "Forest Giant", + 141 => "Boat", + 142 => "UNKNOWN RACE", + 143 => "UNKNOWN RACE", + 144 => "Burynai", + 145 => "Goo", + 146 => "Spectral Sarnak", + 147 => "Spectral Iksar", + 148 => "Kunark Fish", + 149 => "Iksar Scorpion", + 150 => "Erollisi", + 151 => "Tribunal", + 152 => "Bertoxxulous", + 153 => "Bristlebane", + 154 => "Fay Drake", + 155 => "Sarnak Skeleton", + 156 => "Ratman", + 157 => "Wyvern", + 158 => "Wurm", + 159 => "Devourer", + 160 => "Iksar Golem", + 161 => "Iksar Skeleton", + 162 => "Man Eating Plant", + 163 => "Raptor", + 164 => "Sarnak Golem", + 165 => "Water Dragon", + 166 => "Iksar Hand", + 167 => "Succulent", + 168 => "Flying Monkey", + 169 => "Brontotherium", + 170 => "Snow Dervish", + 171 => "Dire Wolf", + 172 => "Manticore", + 173 => "Totem", + 174 => "Cold Spectre", + 175 => "Enchanted Armor", + 176 => "Snow Bunny", + 177 => "Walrus", + 178 => "Rock-gem Men", + 179 => "UNKNOWN RACE", + 180 => "UNKNOWN RACE", + 181 => "Yak Man", + 182 => "Faun", + 183 => "Coldain", + 184 => "Velious Dragons", + 185 => "Hag", + 186 => "Hippogriff", + 187 => "Siren", + 188 => "Frost Giant", + 189 => "Storm Giant", + 190 => "Ottermen", + 191 => "Walrus Man", + 192 => "Clockwork Dragon", + 193 => "Abhorent", + 194 => "Sea Turtle", + 195 => "Black and White Dragons", + 196 => "Ghost Dragon", + 197 => "Ronnie Test", + 198 => "Prismatic Dragon", + 199 => "ShikNar", + 200 => "Rockhopper", + 201 => "Underbulk", + 202 => "Grimling", + 203 => "Vacuum Worm", + 204 => "Evan Test", + 205 => "Kahli Shah", + 206 => "Owlbear", + 207 => "Rhino Beetle", + 208 => "Vampyre", + 209 => "Earth Elemental", + 210 => "Air Elemental", + 211 => "Water Elemental", + 212 => "Fire Elemental", + 213 => "Wetfang Minnow", + 214 => "Thought Horror", + 215 => "Tegi", + 216 => "Horse", + 217 => "Shissar", + 218 => "Fungal Fiend", + 219 => "Vampire Volatalis", + 220 => "StoneGrabber", + 221 => "Scarlet Cheetah", + 222 => "Zelniak", + 223 => "Lightcrawler", + 224 => "Shade", + 225 => "Sunflower", + 226 => "Sun Revenant", + 227 => "Shrieker", + 228 => "Galorian", + 229 => "Netherbian", + 230 => "Akheva", + 231 => "Spire Spirit", + 232 => "Sonic Wolf", + 233 => "Ground Shaker", + 234 => "Vah Shir Skeleton", + 235 => "Mutant Humanoid", + 236 => "Seru", + 237 => "Recuso", + 238 => "Vah Shir King", + 239 => "Vah Shir Guard", + 240 => "Teleport Man", + 241 => "Lujein", + 242 => "Naiad", + 243 => "Nymph", + 244 => "Ent", + 245 => "Fly Man", + 246 => "Tarew Marr", + 247 => "Sol Ro", + 248 => "Clockwork Golem", + 249 => "Clockwork Brain", + 250 => "Spectral Banshee", + 251 => "Guard of Justice", + 252 => "UNKNOWN RACE", + 253 => "Disease Boss", + 254 => "Sol Ro Guard", + 255 => "New Bertox", + 256 => "New Tribunal", + 257 => "Terris Thule", + 258 => "Vegerog", + 259 => "Crocodile", + 260 => "Bat", + 261 => "Slarghilug", + 262 => "Tranquilion", + 263 => "Tin Soldier", + 264 => "Nightmare Wraith", + 265 => "Malarian", + 266 => "Knight of Pestilence", + 267 => "Lepertoloth", + 268 => "Bubonian Boss", + 269 => "Bubonian Underling", + 270 => "Pusling", + 271 => "Water Mephit", + 272 => "Stormrider", + 273 => "Junk Beast", + 274 => "Broken Clockwork", + 275 => "Giant Clockwork", + 276 => "Clockwork Beetle", + 277 => "Nightmare Goblin", + 278 => "Karana", + 279 => "Blood Raven", + 280 => "Nightmare Gargoyle", + 281 => "Mouths of Insanity", + 282 => "Skeletal Horse", + 283 => "Saryn", + 284 => "Fennin Ro", + 285 => "Tormentor", + 286 => "Necro Priest", + 287 => "Nightmare", + 288 => "New Rallos Zek", + 289 => "Vallon Zek", + 290 => "Tallon Zek", + 291 => "Air Mephit", + 292 => "Earth Mephit", + 293 => "Fire Mephit", + 294 => "Nightmare Mephit", + 295 => "Zebuxoruk", + 296 => "Mithaniel Marr", + 297 => "Undead Knight", + 298 => "The Rathe", + 299 => "Xegony", + 300 => "Fiend", + 301 => "Test Object", + 302 => "Crab", + 303 => "Phoenix", + 304 => "PoP Dragon", + 305 => "PoP Bear", + 306 => "Storm Taarid", + 307 => "Storm Satuur", + 308 => "Storm Kuraaln", + 309 => "Storm Volaas", + 310 => "Storm Mana", + 311 => "Storm Fire", + 312 => "Storm Celestial", + 313 => "War Wraith", + 314 => "Wrulon", + 315 => "Kraken", + 316 => "Poison Frog", + 317 => "Queztocoatal", + 318 => "Valorian", + 319 => "War Boar", + 320 => "PoP Efreeti", + 321 => "War Boar Unarmored", + 322 => "Black Knight", + 323 => "Animated Armor", + 324 => "Undead Footman", + 325 => "Rallos Zek Minion", + 326 => "Arachnid", + 327 => "Crystal Spider", + 328 => "Zeb Cage", + 329 => "BoT Portal", + 330 => "Froglok", + 331 => "Troll Buccaneer", + 332 => "Troll Freebooter", + 333 => "Troll Sea Rover", + 334 => "Spectre Pirate Boss", + 335 => "Pirate Boss", + 336 => "Pirate Dark Shaman", + 337 => "Pirate Officer", + 338 => "Gnome Pirate", + 339 => "Dark Elf Pirate", + 340 => "Ogre Pirate", + 341 => "Human Pirate", + 342 => "Erudite Pirate", + 343 => "Poison Dart Frog", + 344 => "Troll Zombie", + 345 => "Luggald Land", + 346 => "Luggald Armored", + 347 => "Luggald Robed", + 348 => "Froglok Mount", + 349 => "Froglok Skeleton", + 350 => "Undead Froglok", + 351 => "Chosen Warrior", + 352 => "Chosen Wizard", + 353 => "Veksar", + 354 => "Greater Veksar", + 355 => "Veksar Boss", + 356 => "Chokadai", + 357 => "Undead Chokadai", + 358 => "Undead Veksar", + 359 => "Vampire Lesser", + 360 => "Vampire Elite", + 361 => "Rujakian Orc", + 362 => "Bone Golem", + 363 => "Synarcana", + 364 => "Sand Elf", + 365 => "Vampire Master", + 366 => "Rujakian Orc Elite", + 367 => "Skeleton New", + 368 => "Mummy New", + 369 => "Goblin New", + 370 => "Insect", + 371 => "Froglok Ghost", + 372 => "Dervish New", + 373 => "Shadow Creatue", + 374 => "Golem New", + 375 => "Evil Eye New", + 376 => "Box", + 377 => "Barrel", + 378 => "Chest", + 379 => "Vase", + 380 => "Table", + 381 => "Weapons Rack", + 382 => "Coffin", + 383 => "Bones", + 384 => "Jokester", + 385 => "Talosian Nihil", + 386 => "Talosian Exile", + 387 => "Talosian Golem", + 388 => "Talosian Wolf", + 389 => "Talosian Amphibian", + 390 => "Talosian Mountain Beast", + 391 => "Talosian Trilobyte", + 392 => "Invader War Hound", + 393 => "Invader Elite Centaur", + 394 => "Invader Lamia", + 395 => "Invader Cyclops", + 396 => "Kyv", + 397 => "Invader Soldier", + 398 => "Invader Brute", + 399 => "Invader Force Commander", + 400 => "Invader Lieutenant Boss", + 401 => "Invader War Beast", + 402 => "Invader Soldier Elite", + 403 => "UNKNOWN RACE", + 404 => "Discord Ship", + + +*/ + + +#endif diff --git a/common/rdtsc.cpp b/common/rdtsc.cpp new file mode 100644 index 000000000..f4d0bd6cc --- /dev/null +++ b/common/rdtsc.cpp @@ -0,0 +1,176 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "debug.h" +#include "rdtsc.h" +#include "types.h" +#include + +#ifdef _WINDOWS + #include + #include "../common/timer.h" +#else + #include + #include +#endif + +#ifdef i386 + #define USE_RDTSC +#else + #ifndef WIN32 + #warning RDTSC_Timer cannot use rdtsc on a non-intel platform, using gettimeofday + #endif +#endif + +bool RDTSC_Timer::_inited = false; +int64 RDTSC_Timer::_ticsperms = 0; + +RDTSC_Timer::RDTSC_Timer() { + if(!_inited) { + //find our clock rate + RDTSC_Timer::init(); + } + _start = 0; + _end = 0; +} + +RDTSC_Timer::RDTSC_Timer(bool start_it) { + if(!_inited) { + //find our clock rate + RDTSC_Timer::init(); + } + if(start_it) + start(); + else { + _start = 0; + _end = 0; + } +} + +int64 RDTSC_Timer::rdtsc() { + int64 res; +#ifdef USE_RDTSC +#ifndef WIN64 +#ifdef WIN32 + //untested! + unsigned long highw, loww; + __asm { + push eax + push edx + rdtsc + mov highw, eax + mov loww, edx + pop edx + pop eax + } + res = ((int64)highw)<<32 | loww; +#else + //gnu version + __asm__ __volatile__ ("rdtsc" : "=A" (res)); +#endif +#else + //fall back to get time of day + timeval t; + gettimeofday(&t, NULL); + res = ((int64)t.tv_sec) * 1000 + t.tv_usec; +#endif +#endif + return(res); +} + +void RDTSC_Timer::init() { +#ifdef USE_RDTSC + int64 before, after, sum; + + int r; + sum = 0; + // run an average to increase accuracy of clock rate + for(r = 0; r < CALIBRATE_LOOPS; r++) { + before = rdtsc(); + + //sleep a know duration to figure out clock rate +#ifdef _WINDOWS + Sleep(SLEEP_TIME); +#else + usleep(SLEEP_TIME * 1000); //ms * 1000 +#endif + + after = rdtsc(); + + sum += after - before; + } + + //ticks per sleep / ms per sleep + _ticsperms = (sum / CALIBRATE_LOOPS) / SLEEP_TIME; + +#else + //if using gettimeofday, this is fixed at 1000 + _ticsperms = 1000; +#endif +// printf("Tics per milisecond: %llu \n", _ticsperms); + + _inited = true; //only want to do this once +} + +//start the timer +void RDTSC_Timer::start() { + _start = rdtsc(); + _end = 0; +} + +//stop the timer +void RDTSC_Timer::stop() { + _end = rdtsc(); +} + +//calculate the elapsed duration +double RDTSC_Timer::getDuration() { + return(((double)(getTicks())) / double(_ticsperms)); +} + +RDTSC_Collector::RDTSC_Collector() : RDTSC_Timer() { + reset(); +} + +RDTSC_Collector::RDTSC_Collector(bool start_it) : RDTSC_Timer(start_it) { + reset(); +} + +void RDTSC_Collector::stop() { + RDTSC_Timer::stop(); + _sum += RDTSC_Timer::getTicks(); + _count++; +} + +//calculate the elapsed duration +double RDTSC_Collector::getTotalDuration() { + return(((double)(getTotalTicks())) / double(_ticsperms)); +} + +double RDTSC_Collector::getAverage() { + return(((double)(getTotalTicks())) / double(_ticsperms * _count)); +} + +void RDTSC_Collector::reset() { + _sum = 0; + _count = 0; +} + + + + + diff --git a/common/rdtsc.h b/common/rdtsc.h new file mode 100644 index 000000000..b1babfd2b --- /dev/null +++ b/common/rdtsc.h @@ -0,0 +1,87 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 RDTSC_H +#define RDTSC_H + + +#define CALIBRATE_LOOPS 3 +#define SLEEP_TIME 10 //in ms + +/* + + This class implementes the highest possibly prescision timer + which is avaliable on the current archetecture. + + On intel, this uses the rdtsc instruction to get the actual + clock cycle count, and elsewhere it falls back to gettimeofday + + All calculations are carried out in 64 bit integers. +*/ + +#include "types.h" + +class RDTSC_Timer { +public: + RDTSC_Timer(); + RDTSC_Timer(bool start_it); + + void start(); //start the timer + virtual void stop(); //stop the timer + double getDuration(); //returns the number of miliseconds elapsed + + //access functions + int64 getTicks() { return(_end - _start); } + static int64 ticksPerMS() { return(_ticsperms); } + +protected: + static int64 rdtsc(); + + int64 _start; + int64 _end; + +protected: + static void init(); + static bool _inited; + static int64 _ticsperms; +}; + +//this is a timer which can be started and stoped many times. +//each time it contributes its counter to a sum, whic is used +//to find net duration. +class RDTSC_Collector : public RDTSC_Timer { +public: + RDTSC_Collector(); + RDTSC_Collector(bool start_it); + + void reset(); + + void stop(); //stop the timer + + double getTotalDuration(); //returns the number of miliseconds elapsed + double getAverage(); + + int64 getTotalTicks() { return(_sum); } + int64 getCount() { return(_count); } + +protected: + int64 _sum; + int64 _count; +}; + + +#endif diff --git a/common/rulesys.cpp b/common/rulesys.cpp new file mode 100644 index 000000000..32b8fd8ec --- /dev/null +++ b/common/rulesys.cpp @@ -0,0 +1,492 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "rulesys.h" +#include "logsys.h" +#include "database.h" +#include "MiscFunctions.h" +#include +#include + +/* + + FatherNitwit: Added new rules subsystem to allow game rules to be changed + at runtime. more about this will come as time goes on. +FatherNitwit: Added #rules command to manage rules data from in game. +FatherNitwit: Renamed old #rules to #serverrules +FatherNitwit: Moved max level into the rules system (Character:MaxLevel) +Requred SQL: + + + +CREATE TABLE rule_sets ( + ruleset_id TINYINT UNSIGNED NOT NULL auto_increment, + name VARCHAR(255) NOT NULL, + PRIMARY KEY(ruleset_id) +); +INSERT INTO rule_sets VALUES(0, "default"); +UPDATE rule_sets SET ruleset_id=0; + +CREATE TABLE rule_values ( + ruleset_id TINYINT UNSIGNED NOT NULL, + rule_name VARCHAR(64) NOT NULL, + rule_value VARCHAR(10) NOT NULL, + INDEX(ruleset_id), + PRIMARY KEY(ruleset_id,rule_name) +); + + + +Commands: +#rules: + - current -> lists current set name + - switch (set name) -> change set in the DB, but dont reload + - load (set name) -> load set into this zone without changing the world + - wload (set name) -> tell world and all zones to load this rule set + - store [set name] -> store the current rules in this zone to the set (or + active if not specified) + - reset -> reset all rule values to their defaults. + - list [catname] + - set (cat) (rule) (value) + - values [catname] -> show the values of all rules in the specified category/ + + +*/ + +const char *RuleManager::s_categoryNames[_CatCount+1] = { + #define RULE_CATEGORY(catname) \ + #catname , + #include "ruletypes.h" + "InvalidCategory" +}; + +const RuleManager::RuleInfo RuleManager::s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount+1] = { + /* this is done in three steps so we can reliably get to them by index*/ + #define RULE_INT(cat, rule, default_value) \ + { #cat ":" #rule, Category__##cat, IntRule, Int__##rule }, + #include "ruletypes.h" + #define RULE_REAL(cat, rule, default_value) \ + { #cat ":" #rule, Category__##cat, RealRule, Real__##rule }, + #include "ruletypes.h" + #define RULE_BOOL(cat, rule, default_value) \ + { #cat ":" #rule, Category__##cat, BoolRule, Bool__##rule }, + #include "ruletypes.h" + { "Invalid Rule", _CatCount, IntRule } +}; + +RuleManager::RuleManager() +: m_activeRuleset(0), + m_activeName("default") +{ + ResetRules(); +} + +RuleManager::CategoryType RuleManager::FindCategory(const char *catname) { + int r; + for(r = 0; r < _CatCount; r++) { + if(strcasecmp(catname, s_categoryNames[r]) == 0) + return((CategoryType) r); + } + return(InvalidCategory); +} + +bool RuleManager::ListRules(const char *catname, std::vector &into) { + CategoryType cat = InvalidCategory; + if(catname != NULL) { + cat = FindCategory(catname); + if(cat == InvalidCategory) { + _log(RULES__ERROR, "Unable to find category '%s'", catname); + return(false); + } + } + int r; + int rcount = CountRules(); + for(r = 0; r < rcount; r++) { + const RuleInfo &rule = s_RuleInfo[r]; + if(catname == NULL || cat == rule.category) { + into.push_back(rule.name); + } + } + return(true); +} + +bool RuleManager::ListCategories(std::vector &into) { + int r; + for(r = 0; r < _CatCount; r++) { + into.push_back(s_categoryNames[r]); + } + return(true); +} + + +bool RuleManager::GetRule(const char *rule_name, std::string &ret_val) { + RuleType type; + uint16 index; + if (!_FindRule(rule_name, type, index)) + return false; + + char tmp[255] = ""; + switch(type) { + case IntRule: + sprintf(tmp, "%i", m_RuleIntValues[index]); + break; + case RealRule: + sprintf(tmp, "%f", m_RuleRealValues[index]); + break; + case BoolRule: + std::string tmp_val = m_RuleBoolValues[index] ? "true" : "false"; + sprintf(tmp, "%s", tmp_val.c_str()); + break; + } + + ret_val = tmp; + + return true; +} + +bool RuleManager::SetRule(const char *rule_name, const char *rule_value, Database *db, bool db_save) { + if(rule_name == NULL || rule_value == NULL) + return(false); + + RuleType type; + uint16 index; + if(!_FindRule(rule_name, type, index)) + return(false); + + switch(type) { + case IntRule: + m_RuleIntValues [index] = atoi(rule_value); + _log(RULES__CHANGE, "Set rule %s to value %d", rule_name, m_RuleIntValues[index]); + break; + case RealRule: + m_RuleRealValues[index] = atof(rule_value); + _log(RULES__CHANGE, "Set rule %s to value %.13f", rule_name, m_RuleRealValues[index]); + break; + case BoolRule: + uint32 val = 0; + if(!strcasecmp(rule_value, "on") || !strcasecmp(rule_value, "true") || !strcasecmp(rule_value, "yes") || !strcasecmp(rule_value, "enabled") || !strcmp(rule_value, "1")) + val = 1; + m_RuleBoolValues[index] = val; + _log(RULES__CHANGE, "Set rule %s to value %s", rule_name, m_RuleBoolValues[index] == 1 ?"true":"false"); + break; + } + + if(db_save) + _SaveRule(db, type, index); + + return(true); +} + +void RuleManager::ResetRules() { + _log(RULES__CHANGE, "Resetting running rules to default values"); + #define RULE_INT(cat, rule, default_value) \ + m_RuleIntValues[ Int__##rule ] = default_value; + #define RULE_REAL(cat, rule, default_value) \ + m_RuleRealValues[ Real__##rule ] = default_value; + #define RULE_BOOL(cat, rule, default_value) \ + m_RuleBoolValues[ Bool__##rule ] = default_value; + #include "ruletypes.h" +} + +bool RuleManager::_FindRule(const char *rule_name, RuleType &type_into, uint16 &index_into) { + if(rule_name == NULL) + return(false); + + int r; + int rcount = CountRules(); + for(r = 0; r < rcount; r++) { + const RuleInfo &rule = s_RuleInfo[r]; + if(strcmp(rule_name, rule.name) == 0) { + type_into = rule.type; + index_into = rule.rule_index; + return(true); + } + } + _log(RULES__ERROR, "Unable to find rule '%s'", rule_name); + return(false); +} + +//assumes index is valid! +const char *RuleManager::_GetRuleName(RuleType type, uint16 index) { + switch(type) { + case IntRule: + return(s_RuleInfo[index].name); + case RealRule: + return(s_RuleInfo[index+_IntRuleCount].name); + case BoolRule: + return(s_RuleInfo[index+_IntRuleCount+_RealRuleCount].name); + } + //should never happen + return("InvalidRule??"); +} + +void RuleManager::SaveRules(Database *db, const char *ruleset) { + + if(ruleset != NULL) { + //saving to a specific name + if(m_activeName != ruleset) { + //a new name... + + m_activeRuleset = _FindOrCreateRuleset(db, ruleset); + if(m_activeRuleset == -1) { + _log(RULES__ERROR, "Unable to find or create rule set %s", ruleset); + return; + } + m_activeName = ruleset; + } + _log(RULES__CHANGE, "Saving running rules into rule set %s (%d)", ruleset, m_activeRuleset); + } else { + _log(RULES__CHANGE, "Saving running rules into running rule set %s", m_activeName.c_str(), m_activeRuleset); + } + + int r; + for(r = 0; r < _IntRuleCount; r++) { + _SaveRule(db, IntRule, r); + } + for(r = 0; r < _RealRuleCount; r++) { + _SaveRule(db, RealRule, r); + } + for(r = 0; r < _BoolRuleCount; r++) { + _SaveRule(db, BoolRule, r); + } +} + + + +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) { + _log(RULES__ERROR, "Failed to find ruleset '%s' for load operation. Canceling.", ruleset); + return(false); + } + + _log(RULES__CHANGE, "Loading rule set '%s' (%d)", ruleset, rsid); + + 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)) + { + safe_delete_array(query); + while((row = mysql_fetch_row(result))) { + if(!SetRule(row[0], row[1], 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); + } + + return(true); +} + +void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) { + char vstr[100]; + + switch(type) { + case IntRule: + sprintf(vstr, "%d", m_RuleIntValues[index]); + break; + case RealRule: + sprintf(vstr, "%.13f", m_RuleRealValues[index]); + break; + case BoolRule: + sprintf(vstr, "%s", m_RuleBoolValues[index]?"true":"false"); + 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); +} + + +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; + + 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); + + return(res); +} + +int RuleManager::_FindOrCreateRuleset(Database *db, const char *ruleset) { + int res; + + res = GetRulesetID(db, ruleset); + if(res >= 0) + 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,NULL,NULL,&new_id)) + { + _log(RULES__ERROR, "Fauled to create rule set in the database: %s: %s", query,errbuf); + res = -1; + } else { + res = new_id; + } + safe_delete_array(query); + + return(res); +} + +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)) + { + 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); + } + safe_delete_array(query); + + return(res); +} + +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)) + { + 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); + } + return(true); +} + +int32 RuleManager::GetIntRule(RuleManager::IntType t) const +{ + return(m_RuleIntValues[t]); +} + +float RuleManager::GetRealRule(RuleManager::RealType t) const +{ + return(m_RuleRealValues[t]); +} + +bool RuleManager::GetBoolRule(RuleManager::BoolType t) const +{ + return (m_RuleBoolValues[t] == 1); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/rulesys.h b/common/rulesys.h new file mode 100644 index 000000000..126f7f7ad --- /dev/null +++ b/common/rulesys.h @@ -0,0 +1,152 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 RULESYS_H_ +#define RULESYS_H_ + +/* +* Access to the rules system in normal code is done with three calls: +* - RuleI(category, rule) -> fetch an integer rule's value +* - RuleR(category, rule) -> fetch a real (float) rule's value +* - RuleB(category, rule) -> fetch a boolean/flag rule's value +* +*/ + +//note, these macros assume there is always a RuleManager *rules in scope, +//which makes it a global for now, but with instancing we will do exactly +//what we do with the zone global and just make it a member of core classes +#define RuleI(cat, rule) \ + rules->GetIntRule( RuleManager::Int__##rule ) +#define RuleR(cat, rule) \ + rules->GetRealRule( RuleManager::Real__##rule ) +#define RuleB(cat, rule) \ + rules->GetBoolRule( RuleManager::Bool__##rule ) + + +#include +#include +#include + +#include "types.h" + +class Database; + +class RuleManager { +public: + //generate our rule enums: + typedef enum { + #define RULE_INT(cat, rule, default_value) \ + Int__##rule, + #include "ruletypes.h" + _IntRuleCount + } IntType; + + typedef enum { + #define RULE_REAL(cat, rule, default_value) \ + Real__##rule, + #include "ruletypes.h" + _RealRuleCount + } RealType; + + typedef enum { + #define RULE_BOOL(cat, rule, default_value) \ + Bool__##rule, + #include "ruletypes.h" + _BoolRuleCount + } BoolType; + + typedef enum { + #define RULE_CATEGORY(catname) \ + Category__##catname, + #include "ruletypes.h" + _CatCount + } CategoryType; + + static const IntType InvalidInt = _IntRuleCount; + static const RealType InvalidReal = _RealRuleCount; + static const BoolType InvalidBool = _BoolRuleCount; + static const CategoryType InvalidCategory = _CatCount; + + static const uint32 _RulesCount = _IntRuleCount+_RealRuleCount+_BoolRuleCount; + + RuleManager(); + + //fetch routines, you should generally use the Rule* macros instead of this + int32 GetIntRule (IntType t) const; + float GetRealRule(RealType t) const; + bool GetBoolRule(BoolType t) const; + + //management routines + static const char *GetRuleName(IntType t) { return(s_RuleInfo[t].name); } + static const char *GetRuleName(RealType t) { return(s_RuleInfo[t+_IntRuleCount].name); } + static const char *GetRuleName(BoolType t) { return(s_RuleInfo[t+_IntRuleCount+_RealRuleCount].name); } + static uint32 CountRules() { return(_RulesCount); } + static CategoryType FindCategory(const char *catname); + bool ListRules(const char *catname, std::vector &into); + bool ListCategories(std::vector &into); + bool GetRule(const char *rule_name, std::string &ret_val); + bool SetRule(const char *rule_name, const char *rule_value, Database *db = NULL, bool db_save = false); + + int GetActiveRulesetID() const { return(m_activeRuleset); } + const char *GetActiveRuleset() const { return(m_activeName.c_str()); } + static int GetRulesetID(Database *db, const char *rulesetname); + static std::string GetRulesetName(Database *db, int id); + static bool ListRulesets(Database *db, std::map &into); + + void ResetRules(); + bool LoadRules(Database *db, const char *ruleset = NULL); + void SaveRules(Database *db, const char *ruleset = NULL); + +protected: + + int m_activeRuleset; + std::string m_activeName; +#ifdef WIN64 + uint32 m_RuleIntValues [_IntRuleCount ]; +#else + int m_RuleIntValues [_IntRuleCount ]; +#endif + float m_RuleRealValues[_RealRuleCount]; + uint32 m_RuleBoolValues[_BoolRuleCount]; + + typedef enum { + IntRule, + RealRule, + BoolRule + } RuleType; + + static bool _FindRule(const char *rule_name, RuleType &type_into, uint16 &index_into); + static const char *_GetRuleName(RuleType type, uint16 index); + static int _FindOrCreateRuleset(Database *db, const char *ruleset); + void _SaveRule(Database *db, RuleType type, uint16 index); + + static const char *s_categoryNames[]; + typedef struct { + const char *name; + CategoryType category; + RuleType type; + uint16 rule_index; //index into its 'type' array + } RuleInfo; + static const RuleInfo s_RuleInfo[]; + +}; + +//this is not the right way to do this, but I +//dont feel like solving it correctly right now: +extern RuleManager *rules; + +#endif /*RULESYS_H_*/ diff --git a/common/ruletypes.h b/common/ruletypes.h new file mode 100644 index 000000000..04b5c0cc7 --- /dev/null +++ b/common/ruletypes.h @@ -0,0 +1,518 @@ + + + +#ifndef RULE_CATEGORY +#define RULE_CATEGORY(name) +#endif +#ifndef RULE_INT +#define RULE_INT(cat, rule, default_value) +#endif +#ifndef RULE_REAL +#define RULE_REAL(cat, rule, default_value) +#endif +#ifndef RULE_BOOL +#define RULE_BOOL(cat, rule, default_value) +#endif +#ifndef RULE_CATEGORY_END +#define RULE_CATEGORY_END() +#endif + + + + +RULE_CATEGORY( Character ) +RULE_INT ( Character, MaxLevel, 65 ) +RULE_BOOL ( Character, PerCharacterQglobalMaxLevel, false) // This will check for qglobal 'CharMaxLevel' character qglobal (Type 5), if player tries to level beyond that point, it will not go beyond that level +RULE_INT ( Character, MaxExpLevel, 0 ) //Sets the Max Level attainable via Experience +RULE_INT ( Character, DeathExpLossLevel, 10 ) // Any level greater than this will lose exp on death +RULE_INT ( Character, DeathExpLossMaxLevel, 255 ) // Any level greater than this will no longer lose exp on death +RULE_INT ( Character, DeathItemLossLevel, 10 ) +RULE_INT ( Character, DeathExpLossMultiplier, 3) //Adjust how much exp is lost +RULE_BOOL( Character, UseDeathExpLossMult, false ) //Adjust to use the above multiplier or to use code default. +RULE_INT ( Character, CorpseDecayTimeMS, 10800000 ) +RULE_INT ( Character, CorpseResTimeMS, 10800000 ) // time before cant res corpse(3 hours) +RULE_BOOL( Character, LeaveCorpses, true ) +RULE_BOOL( Character, LeaveNakedCorpses, false ) +RULE_INT ( Character, MaxDraggedCorpses, 2 ) +RULE_REAL( Character, DragCorpseDistance, 400) // If the corpse is <= this distance from the player, it won't move +RULE_REAL( Character, ExpMultiplier, 0.5 ) +RULE_REAL( Character, AAExpMultiplier, 0.5 ) +RULE_REAL( Character, GroupExpMultiplier, 0.5 ) +RULE_REAL( Character, RaidExpMultiplier, 0.2 ) +RULE_BOOL( Character, UseXPConScaling, true ) +RULE_INT ( Character, LightBlueModifier, 40 ) +RULE_INT ( Character, BlueModifier, 90 ) +RULE_INT ( Character, WhiteModifier, 100 ) +RULE_INT ( Character, YellowModifier, 125 ) +RULE_INT ( Character, RedModifier, 150 ) +RULE_INT ( Character, AutosaveIntervalS, 300 ) //0=disabled +RULE_INT ( Character, HPRegenMultiplier, 100) +RULE_INT ( Character, ManaRegenMultiplier, 100) +RULE_INT ( Character, EnduranceRegenMultiplier, 100) +RULE_INT ( Character, ConsumptionMultiplier, 100) //item's hunger restored = this value * item's food level, 100 = normal, 50 = people eat 2x as fast, 200 = people eat 2x as slow +RULE_BOOL( Character, HealOnLevel, false) +RULE_BOOL( Character, FeignKillsPet, false) +RULE_INT ( Character, ItemManaRegenCap, 15) +RULE_INT ( Character, ItemHealthRegenCap, 35) +RULE_INT ( Character, ItemDamageShieldCap, 30) +RULE_INT ( Character, ItemAccuracyCap, 150) +RULE_INT ( Character, ItemAvoidanceCap, 100) +RULE_INT ( Character, ItemCombatEffectsCap, 100) +RULE_INT ( Character, ItemShieldingCap, 35) +RULE_INT ( Character, ItemSpellShieldingCap, 35) +RULE_INT ( Character, ItemDoTShieldingCap, 35) +RULE_INT ( Character, ItemStunResistCap, 35) +RULE_INT ( Character, ItemStrikethroughCap, 35) +RULE_INT ( Character, ItemATKCap, 250) +RULE_INT ( Character, ItemHealAmtCap, 250) +RULE_INT ( Character, ItemSpellDmgCap, 250) +RULE_INT ( Character, ItemClairvoyanceCap, 250) +RULE_INT ( Character, ItemDSMitigationCap, 50) +RULE_INT ( Character, ItemEnduranceRegenCap, 15) +RULE_INT ( Character, ItemExtraDmgCap, 150) // Cap for bonuses to melee skills like Bash, Frenzy, etc +RULE_INT ( Character, HasteCap, 100) // Haste cap for non-v3(overhaste) haste. +RULE_INT ( Character, SkillUpModifier, 100) //skill ups are at 100% +RULE_BOOL ( Character, SharedBankPlat, false) //off by default to prevent duping for now +RULE_BOOL ( Character, BindAnywhere, false) +RULE_INT ( Character, RestRegenPercent, 0) // Set to >0 to enable rest state bonus HP and mana regen. +RULE_INT ( Character, RestRegenTimeToActivate, 30) // Time in seconds for rest state regen to kick in. +RULE_BOOL ( Character, RestRegenEndurance, false) // Whether rest regen will work for endurance or not. +RULE_INT ( Character, KillsPerGroupLeadershipAA, 250) // Number of dark blues or above per Group Leadership AA +RULE_INT ( Character, KillsPerRaidLeadershipAA, 250) // Number of dark blues or above per Raid Leadership AA +RULE_INT ( Character, MaxFearDurationForPlayerCharacter, 4) //4 tics, each tic calculates every 6 seconds. +RULE_INT ( Character, MaxCharmDurationForPlayerCharacter, 15) +RULE_INT ( Character, BaseHPRegenBonusRaces, 4352) //a bitmask of race(s) that receive the regen bonus. Iksar (4096) & Troll (256) = 4352. see common/races.h for the bitmask values +RULE_BOOL ( Character, SoDClientUseSoDHPManaEnd, false) // Setting this to true will allow SoD clients to use the SoD HP/Mana/End formulas and previous clients will use the old formulas +RULE_BOOL ( Character, UseRaceClassExpBonuses, true) // Setting this to true will enable Class and Racial experience rate bonuses +RULE_BOOL ( Character, RespawnFromHover, false) // Use Respawn window, or not. +RULE_INT ( Character, RespawnFromHoverTimer, 300) // Respawn Window countdown timer, in SECONDS +RULE_BOOL ( Character, UseNewStatsWindow, true) // New stats window shows everything +RULE_BOOL ( Character, ItemCastsUseFocus, false) // If true, this allows item clickies to use focuses that have limited max levels on them +RULE_INT ( Character, MinStatusForNoDropExemptions, 80) // This allows status x and higher to trade no drop items. +RULE_INT ( Character, SkillCapMaxLevel, 75 ) // Sets the Max Level used for Skill Caps (from skill_caps table). -1 makes it use MaxLevel rule value. It is set to 75 because PEQ only has skillcaps up to that level, and grabbing the players' skill past 75 will return 0, breaking all skills past that level. This helps servers with obsurd level caps (75+ level cap) function without any modifications. +RULE_INT ( Character, StatCap, 0 ) +RULE_BOOL ( Character, CheckCursorEmptyWhenLooting, true ) // If true, a player cannot loot a corpse (player or NPC) with an item on their cursor +RULE_BOOL ( Character, MaintainIntoxicationAcrossZones, true ) // If true, alcohol effects are maintained across zoning and logging out/in. +RULE_BOOL ( Character, EnableDiscoveredItems, true ) // If enabled, it enables EVENT_DISCOVER_ITEM and also saves character names and timestamps for the first time an item is discovered. +RULE_BOOL ( Character, EnableXTargetting, true) // Enable Extended Targetting Window, for users with UF and later clients. + +RULE_CATEGORY_END() + +RULE_CATEGORY( Mercs ) +RULE_INT (Mercs, SuspendIntervalMS, 10000) +RULE_INT (Mercs, UpkeepIntervalMS, 180000) +RULE_INT (Mercs, SuspendIntervalS, 10) +RULE_INT (Mercs, UpkeepIntervalS, 180) +RULE_BOOL ( Mercs, AllowMercs, false ) +RULE_INT (Mercs, AggroRadius, 100) // Determines the distance from which a merc will aggro group member's target(also used to determine the distance at which a healer merc will begin healing a group member) +RULE_INT (Mercs, AggroRadiusPuller, 25) // Determines the distance from which a merc will aggro group member's target, if they have the group role of puller (also used to determine the distance at which a healer merc will begin healing a group member, if they have the group role of puller) +RULE_INT (Mercs, ScaleRate, 100) +RULE_CATEGORY_END() + +RULE_CATEGORY( Guild ) +RULE_INT ( Guild, MaxMembers, 2048 ) +RULE_BOOL ( Guild, PlayerCreationAllowed, false) // Allow players to create a guild using the window in Underfoot+ +RULE_INT ( Guild, PlayerCreationLimit, 1) // Only allow use of the UF+ window if the account has < than this number of guild leaders on it +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 ) +RULE_INT ( Skills, MaxTrainTradeskills, 21 ) +RULE_BOOL ( Skills, UseLimitTradeskillSearchSkillDiff, true ) +RULE_INT ( Skills, MaxTradeskillSearchSkillDiff, 50 ) +RULE_INT ( Skills, MaxTrainSpecializations, 50 ) // Max level a GM trainer will train casting specializations +RULE_CATEGORY_END() + +RULE_CATEGORY( Pets ) +RULE_REAL( Pets, AttackCommandRange, 150 ) +RULE_BOOL( Pets, UnTargetableSwarmPet, false ) +RULE_CATEGORY_END() + +RULE_CATEGORY( GM ) +RULE_INT ( GM, MinStatusToZoneAnywhere, 250 ) +RULE_CATEGORY_END() + +RULE_CATEGORY( World ) +RULE_INT ( World, ZoneAutobootTimeoutMS, 60000 ) +RULE_INT ( World, ClientKeepaliveTimeoutMS, 65000 ) +RULE_BOOL ( World, UseBannedIPsTable, false ) // Toggle whether or not to check incoming client connections against the Banned_IPs table. Set this value to false to disable this feature. +RULE_BOOL ( World, EnableTutorialButton, true) +RULE_BOOL ( World, EnableReturnHomeButton, true) +RULE_INT ( World, MaxLevelForTutorial, 10) +RULE_INT ( World, TutorialZoneID, 189) +RULE_INT ( World, GuildBankZoneID, 345) +RULE_INT ( World, MinOfflineTimeToReturnHome, 21600) // 21600 seconds is 6 Hours +RULE_INT ( World, MaxClientsPerIP, -1 ) // Maximum number of clients allowed to connect per IP address if account status is < AddMaxClientsStatus. Default value: -1 (feature disabled) +RULE_INT ( World, ExemptMaxClientsStatus, -1 ) // Exempt accounts from the MaxClientsPerIP and AddMaxClientsStatus rules, if their status is >= this value. Default value: -1 (feature disabled) +RULE_INT ( World, AddMaxClientsPerIP, -1 ) // Maximum number of clients allowed to connect per IP address if account status is < ExemptMaxClientsStatus. Default value: -1 (feature disabled) +RULE_INT ( World, AddMaxClientsStatus, -1 ) // Accounts with status >= this rule will be allowed to use the amount of accounts defined in the AddMaxClientsPerIP. Default value: -1 (feature disabled) +RULE_BOOL ( World, MaxClientsSetByStatus, false) // If True, IP Limiting will be set to the status on the account as long as the status is > MaxClientsPerIP +RULE_BOOL ( World, ClearTempMerchantlist, true) // Clears temp merchant items when world boots. +RULE_BOOL ( World, DeleteStaleCorpeBackups, true) // Deletes stale corpse backups older than 2 weeks. +RULE_INT ( World, AccountSessionLimit, -1 ) //Max number of characters allowed on at once from a single account (-1 is disabled) +RULE_INT ( World, ExemptAccountLimitStatus, -1 ) //Min status required to be exempt from multi-session per account limiting (-1 is disabled) +RULE_BOOL ( World, GMAccountIPList, false) // Check ip list against GM Accounts, AntiHack GM Accounts. +RULE_INT ( World, MinGMAntiHackStatus, 1 ) //Minimum GM status to check against AntiHack list +RULE_INT ( World, SoFStartZoneID, -1 ) //Sets the Starting Zone for SoF Clients separate from Titanium Clients (-1 is disabled) +RULE_INT ( World, ExpansionSettings, 16383) // Sets the expansion settings for the server, This is sent on login to world and affects client expansion settings. Defaults to all expansions enabled up to TSS. +RULE_INT ( World, PVPSettings, 0) // Sets the PVP settings for the server, 1 = Rallos Zek RuleSet, 2 = Tallon/Vallon Zek Ruleset, 4 = Sullon Zek Ruleset, 6 = Discord Ruleset, anything above 6 is the Discord Ruleset without the no-drop restrictions removed. TODO: Edit IsAttackAllowed in Zone to accomodate for these rules. +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_CATEGORY_END() + +RULE_CATEGORY( Zone ) +RULE_INT ( Zone, NPCPositonUpdateTicCount, 32 ) //ms between intervals of sending a position update to the entire zone. +RULE_INT ( Zone, ClientLinkdeadMS, 180000) //the time a client remains link dead on the server after a sudden disconnection +RULE_INT ( Zone, GraveyardTimeMS, 1200000) //ms time until a player corpse is moved to a zone's graveyard, if one is specified for the zone +RULE_BOOL ( Zone, EnableShadowrest, 1 ) // enables or disables the shadowrest zone feature for player corpses. Default is turned on. +RULE_BOOL ( Zone, UsePlayerCorpseBackups, true) // Keeps backups of player corpses. +RULE_INT ( Zone, MQWarpExemptStatus, -1 ) // Required status level to exempt the MQWarpDetector. Set to -1 to disable this feature. +RULE_INT ( Zone, MQZoneExemptStatus, -1 ) // Required status level to exempt the MQZoneDetector. Set to -1 to disable this feature. +RULE_INT ( Zone, MQGateExemptStatus, -1 ) // Required status level to exempt the MQGateDetector. Set to -1 to disable this feature. +RULE_INT ( Zone, MQGhostExemptStatus, -1 ) // Required status level to exempt the MGhostDetector. Set to -1 to disable this feature. +RULE_BOOL ( Zone, EnableMQWarpDetector, true ) // Enable the MQWarp Detector. Set to False to disable this feature. +RULE_BOOL ( Zone, EnableMQZoneDetector, true ) // Enable the MQZone Detector. Set to False to disable this feature. +RULE_BOOL ( Zone, EnableMQGateDetector, true ) // Enable the MQGate Detector. Set to False to disable this feature. +RULE_BOOL ( Zone, EnableMQGhostDetector, true ) // Enable the MQGhost Detector. Set to False to disable this feature. +RULE_REAL ( Zone, MQWarpDetectionDistanceFactor, 9.0) //clients move at 4.4 about if in a straight line but with movement and to acct for lag we raise it a bit +RULE_BOOL ( Zone, MarkMQWarpLT, false ) +RULE_INT ( Zone, AutoShutdownDelay, 5000 ) //How long a dynamic zone stays loaded while empty +RULE_INT ( Zone, PEQZoneReuseTime, 900 ) //How long, in seconds, until you can reuse the #peqzone command. +RULE_INT ( Zone, PEQZoneDebuff1, 4454 ) //First debuff casted by #peqzone Default is Cursed Keeper's Blight. +RULE_INT ( Zone, PEQZoneDebuff2, 2209 ) //Second debuff casted by #peqzone Default is Tendrils of Apathy. +RULE_BOOL ( Zone, UsePEQZoneDebuffs, true ) //Will determine if #peqzone will debuff players or not when used. +RULE_REAL ( Zone, HotZoneBonus, 0.75 ) +RULE_INT ( Zone, ReservedInstances, 30 ) //Will reserve this many instance ids for globals... probably not a good idea to change this while a server is running. +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_CATEGORY_END() + +RULE_CATEGORY( Map ) +//enable these to help prevent mob hopping when they are pathing +RULE_BOOL ( Map, FixPathingZWhenLoading, true ) //increases zone boot times a bit to reduce hopping. +RULE_BOOL ( Map, FixPathingZAtWaypoints, false ) //alternative to `WhenLoading`, accomplishes the same thing but does it at each waypoint instead of once at boot time. +RULE_BOOL ( Map, FixPathingZWhenMoving, false ) //very CPU intensive, but helps hopping with widely spaced waypoints. +RULE_BOOL ( Map, FixPathingZOnSendTo, false ) //try to repair Z coords in the SendTo routine as well. +RULE_REAL ( Map, FixPathingZMaxDeltaMoving, 20 ) //at runtime while pathing: max change in Z to allow the BestZ code to apply. +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() + +RULE_CATEGORY( Pathing ) +// Some of these rules may benefit by being made into columns in the zone table, +// for instance, in dungeons, the min LOS distances could be substantially lowered. +RULE_BOOL ( Pathing, Aggro, true ) // Enable pathing for aggroed mobs. +RULE_BOOL ( Pathing, AggroReturnToGrid, true ) // Enable pathing for aggroed roaming mobs returning to their previous waypoint. +RULE_BOOL ( Pathing, Guard, true ) // Enable pathing for mobs moving to their guard point. +RULE_BOOL ( Pathing, Find, true ) // Enable pathing for FindPerson requests from the client. +RULE_BOOL ( Pathing, Fear, true ) // Enable pathing for fear +RULE_REAL ( Pathing, ZDiffThreshold, 10) // If a mob las LOS to it's target, it will run to it if the Z difference is < this. +RULE_INT ( Pathing, LOSCheckFrequency, 1000) // A mob will check for LOS to it's target this often (milliseconds). +RULE_INT ( Pathing, RouteUpdateFrequencyShort, 1000) // How often a new route will be calculated if the target has moved. +RULE_INT ( Pathing, RouteUpdateFrequencyLong, 5000) // How often a new route will be calculated if the target has moved. +// When a path has a path node route and it's target changes position, if it has RouteUpdateFrequencyNodeCount or less nodes to go on it's +// current path, it will recalculate it's path based on the RouteUpdateFrequencyShort timer, otherwise it will use the +// RouteUpdateFrequencyLong timer. +RULE_INT ( Pathing, RouteUpdateFrequencyNodeCount, 5) +RULE_REAL ( Pathing, MinDistanceForLOSCheckShort, 40000) // (NoRoot). While following a path, only check for LOS to target within this distance. +RULE_REAL ( Pathing, MinDistanceForLOSCheckLong, 1000000) // (NoRoot). Min distance when initially attempting to acquire the target. +RULE_INT ( Pathing, MinNodesLeftForLOSCheck, 4) // Only check for LOS when we are down to this many path nodes left to run. +// This next rule was put in for situations where the mob and it's target may be on different sides of a 'hazard', e.g. a pit +// If the mob has LOS to it's target, even though there is a hazard in it's way, it may break off from the node path and run at +// the target, only to later detect the hazard and re-acquire a node path. Depending upon the placement of the path nodes, this +// can lead to the mob looping. The rule is intended to allow the mob to at least get closer to it's target each time before +// checking LOS and trying to head straight for it. +RULE_INT ( Pathing, MinNodesTraversedForLOSCheck, 3) // Only check for LOS after we have traversed this many path nodes. +RULE_INT ( Pathing, CullNodesFromStart, 1) // Checks LOS from Start point to second node for this many nodes and removes first node if there is LOS +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 ) +// enable these to use the water detection code. Requires Water Maps generated by awater utility +RULE_BOOL ( Watermap, CheckWaypointsInWaterWhenLoading, false ) // Does not apply BestZ as waypoints are loaded if they are in water +RULE_BOOL ( Watermap, CheckForWaterAtWaypoints, false) // Check if a mob has moved into/out of water when at waypoints and sets flymode +RULE_BOOL ( Watermap, CheckForWaterWhenMoving, false) // Checks if a mob has moved into/out of water each time it's loc is recalculated +RULE_BOOL ( Watermap, CheckForWaterOnSendTo, false) // Checks if a mob has moved into/out of water on SendTo +RULE_BOOL ( Watermap, CheckForWaterWhenFishing, false) // Only lets a player fish near water (if a water map exists for the zone) +RULE_REAL ( Watermap, FishingRodLength, 30) // How far in front of player water must be for fishing to work +RULE_REAL ( Watermap, FishingLineLength, 40) // If water is more than this far below the player, it is considered too far to fish +RULE_CATEGORY_END() + +RULE_CATEGORY( Spells ) +RULE_INT ( Spells, AutoResistDiff, 15) +RULE_REAL ( Spells, ResistChance, 2.0) //chance to resist given no resists and same level +RULE_REAL ( Spells, ResistMod, 0.40) //multiplier, chance to resist = this * ResistAmount +RULE_REAL ( Spells, PartialHitChance, 0.7) //The chance when a spell is resisted that it will partial hit. +RULE_REAL ( Spells, PartialHitChanceFear, 0.25) //The chance when a fear spell is resisted that it will partial hit. +RULE_INT ( Spells, BaseCritChance, 0) //base % chance that everyone has to crit a spell +RULE_INT ( Spells, BaseCritRatio, 100) //base % bonus to damage on a successful spell crit. 100 = 2x damage +RULE_INT ( Spells, WizCritLevel, 12) //level wizards first get spell crits +RULE_INT ( Spells, WizCritChance, 7) //wiz's crit chance, on top of BaseCritChance +RULE_INT ( Spells, WizCritRatio, 0) //wiz's crit bonus, on top of BaseCritRatio (should be 0 for Live-like) +RULE_INT ( Spells, ResistPerLevelDiff, 85) //8.5 resist per level difference. +RULE_INT ( Spells, TranslocateTimeLimit, 0) // If not zero, time in seconds to accept a Translocate. +RULE_INT ( Spells, SacrificeMinLevel, 46) //first level Sacrifice will work on +RULE_INT ( Spells, SacrificeMaxLevel, 69) //last level Sacrifice will work on +RULE_INT ( Spells, SacrificeItemID, 9963) //Item ID of the item Sacrifice will return (defaults to an EE) +RULE_BOOL ( Spells, EnableSpellGlobals, false) // If Enabled, spells check the spell_globals table and compare character data from the quest globals before allowing that spell to scribe with scribespells +RULE_INT ( Spells, MaxBuffSlotsNPC, 25) +RULE_INT ( Spells, MaxSongSlotsNPC, 10) +RULE_INT ( Spells, MaxDiscSlotsNPC, 1) +RULE_INT ( Spells, MaxTotalSlotsNPC, 36) +RULE_INT ( Spells, MaxTotalSlotsPET, 25) // do not set this higher than 25 until the player profile is removed from the blob +RULE_BOOL (Spells, EnableBlockedBuffs, true) +RULE_INT ( Spells, ReflectType, 1) //0 = disabled, 1 = single target player spells only, 2 = all player spells, 3 = all single target spells, 4 = all spells +RULE_INT ( Spells, VirusSpreadDistance, 30) // The distance a viral spell will jump to its next victim +RULE_BOOL( Spells, LiveLikeFocusEffects, true) // Determines whether specific healing, dmg and mana reduction focuses are randomized +RULE_INT ( Spells, BaseImmunityLevel, 55) // The level that targets start to be immune to stun, fear and mez spells with a max level of 0. +RULE_BOOL ( Spells, NPCIgnoreBaseImmunity, true) // Whether or not NPCs get to ignore the BaseImmunityLevel for their spells. +RULE_REAL ( Spells, AvgSpellProcsPerMinute, 6.0) //Adjust rate for sympathetic spell procs +RULE_INT ( Spells, ResistFalloff, 67) //Max that level that will adjust our resist chance based on level modifiers +RULE_INT ( Spells, CharismaEffectiveness, 10) // Deterimes how much resist modification charisma applies to charm/pacify checks. Default 10 CHA = -1 resist mod. +RULE_INT ( Spells, CharmBreakCheckChance, 25) //Determines chance for a charm break check to occur each buff tick. +RULE_INT ( Spells, MaxCastTimeReduction, 50) //Max percent your spell cast time can be reduced by spell haste +RULE_INT ( Spells, RootBreakFromSpells, 20) //Chance for root to break when cast on. +RULE_INT ( Spells, DeathSaveCharismaMod, 3) //Determines how much charisma effects chance of death save firing. +RULE_INT ( Spells, DivineInterventionHeal, 8000) //Divine intervention heal amount. +RULE_BOOL( Spells, AdditiveBonusValues, false) //Allow certain bonuses to be calculated by adding together the value from each item, instead of taking the highest value. (ie Add together all Cleave Effects) +RULE_CATEGORY_END() + +RULE_CATEGORY( Combat ) +RULE_INT ( Combat, MeleeBaseCritChance, 0 ) //The base crit chance for non warriors, NOTE: This will apply to NPCs as well +RULE_INT ( Combat, WarBerBaseCritChance, 3 ) //The base crit chance for warriors and berserkers, only applies to clients +RULE_INT ( Combat, BerserkBaseCritChance, 6 ) //The bonus base crit chance you get when you're berserk +RULE_INT ( Combat, NPCBashKickLevel, 6 ) //The level that npcs can KICK/BASH +RULE_INT ( Combat, NPCBashKickStunChance, 15 ) //Percent chance that a bash/kick will stun +RULE_INT ( Combat, ClientBaseCritChance, 0 ) //The base crit chance for all clients, this will stack with warrior's/zerker's crit chance. +RULE_BOOL ( Combat, UseIntervalAC, true) +RULE_INT ( Combat, PetAttackMagicLevel, 30) +RULE_BOOL ( Combat, EnableFearPathing, true) +RULE_INT ( Combat, FleeHPRatio, 25) +RULE_INT ( Combat, FleeSnareHPRatio, 11) // HP at which snare will halt movement of a fleeing NPC. +RULE_BOOL ( Combat, FleeIfNotAlone, false) // If false, mobs won't flee if other mobs are in combat with it. +RULE_BOOL ( Combat, AdjustProcPerMinute, true) +RULE_REAL ( Combat, AvgProcsPerMinute, 2.0) +RULE_REAL ( Combat, ProcPerMinDexContrib, 0.075) +RULE_REAL ( Combat, BaseProcChance, 0.035) +RULE_REAL ( Combat, ProcDexDivideBy, 11000) +RULE_REAL ( Combat, BaseHitChance, 69.0) +RULE_REAL ( Combat, NPCBonusHitChance, 26.0) +RULE_REAL ( Combat, HitFalloffMinor, 5.0) //hit will fall off up to 5% over the initial level range +RULE_REAL ( Combat, HitFalloffModerate, 7.0) //hit will fall off up to 7% over the three levels after the initial level range +RULE_REAL ( Combat, HitFalloffMajor, 50.0) //hit will fall off sharply if we're outside the minor and moderate range +RULE_REAL ( Combat, HitBonusPerLevel, 1.2) //You gain this % of hit for every level you are above your target +RULE_REAL ( Combat, WeaponSkillFalloff, 0.33) //For every weapon skill point that's not maxed you lose this % of hit +RULE_REAL ( Combat, ArcheryHitPenalty, 0.25) //Archery has a hit penalty to try to help balance it with the plethora of long term +hit modifiers for it +RULE_REAL ( Combat, AgiHitFactor, 0.01) +RULE_INT ( Combat, MinRangedAttackDist, 25) //Minimum Distance to use Ranged Attacks +RULE_BOOL ( Combat, ArcheryBonusRequiresStationary, true) //does the 2x archery bonus chance require a stationary npc +RULE_REAL ( Combat, ArcheryBaseDamageBonus, 1) // % Modifier to Base Archery Damage (.5 = 50% base damage, 1 = 100%, 2 = 200%) +RULE_REAL ( Combat, ArcheryNPCMultiplier, 1.0) // this is multiplied by the regular dmg to get the archery dmg +RULE_BOOL ( Combat, AssistNoTargetSelf, true) //when assisting a target that does not have a target: true = target self, false = leave target as was before assist (false = live like) +RULE_INT ( Combat, MaxRampageTargets, 3) //max number of people hit with rampage +RULE_INT ( Combat, MaxFlurryHits, 2) //max number of extra hits from flurry +RULE_INT ( Combat, MonkDamageTableBonus, 5) //% bonus monks get to their damage table calcs +RULE_INT ( Combat, FlyingKickBonus, 25) //% Modifier that this skill gets to str and skill bonuses +RULE_INT ( Combat, DragonPunchBonus, 20) //% Modifier that this skill gets to str and skill bonuses +RULE_INT ( Combat, EagleStrikeBonus, 15) //% Modifier that this skill gets to str and skill bonuses +RULE_INT ( Combat, TigerClawBonus, 10) //% Modifier that this skill gets to str and skill bonuses +RULE_INT ( Combat, RoundKickBonus, 5) //% Modifier that this skill gets to str and skill bonuses +RULE_INT ( Combat, FrenzyBonus, 0) //% Modifier to damage +RULE_BOOL ( Combat, ProcTargetOnly, true) //true = procs will only affect our target, false = procs will affect all of our targets +RULE_REAL ( Combat, NPCACFactor, 2.25) +RULE_INT ( Combat, ClothACSoftcap, 75) +RULE_INT ( Combat, LeatherACSoftcap, 100) +RULE_INT ( Combat, MonkACSoftcap, 120) +RULE_INT ( Combat, ChainACSoftcap, 200) +RULE_INT ( Combat, PlateACSoftcap, 300) +RULE_REAL ( Combat, AAMitigationACFactor, 3.0) +RULE_REAL ( Combat, WarriorACSoftcapReturn, 0.45) +RULE_REAL ( Combat, KnightACSoftcapReturn, 0.33) +RULE_REAL ( Combat, LowPlateChainACSoftcapReturn, 0.23) +RULE_REAL ( Combat, LowChainLeatherACSoftcapReturn, 0.17) +RULE_REAL ( Combat, CasterACSoftcapReturn, 0.06) +RULE_REAL ( Combat, MiscACSoftcapReturn, 0.3) +RULE_REAL ( Combat, ACthac0Factor, 0.55) +RULE_REAL ( Combat, ACthac20Factor, 0.55) +RULE_INT ( Combat, HitCapPre20, 40) // live has it capped at 40 for whatever dumb reason... this is mainly for custom servers +RULE_INT ( Combat, HitCapPre10, 20) // live has it capped at 20, see above :p +RULE_INT ( Combat, MinHastedDelay, 400) // how fast we can get with haste. +RULE_REAL ( Combat, AvgDefProcsPerMinute, 2.0) +RULE_REAL ( Combat, DefProcPerMinAgiContrib, 0.075) //How much agility contributes to defensive proc rate +RULE_INT ( Combat, SpecialAttackACBonus, 15) //Percent amount of damage per AC gained for certain special attacks (damage = AC*SpecialAttackACBonus/100). +RULE_INT ( Combat, NPCFlurryChance, 20) // Chance for NPC to flurry. +RULE_BOOL (Combat,TauntOverLevel, 1) //Allows you to taunt NPC's over warriors level. +RULE_REAL (Combat,TauntSkillFalloff, 0.33)//For every taunt skill point that's not maxed you lose this % chance to taunt. +RULE_BOOL (Combat,EXPFromDmgShield, false) //Determine if damage from a damage shield counts for EXP gain. +RULE_CATEGORY_END() + +RULE_CATEGORY( NPC ) +RULE_INT ( NPC, MinorNPCCorpseDecayTimeMS, 450000 ) //level<55 +RULE_INT ( NPC, MajorNPCCorpseDecayTimeMS, 1500000 ) //level>=55 +RULE_INT ( NPC, CorpseUnlockTimer, 150000 ) +RULE_INT ( NPC, EmptyNPCCorpseDecayTimeMS, 0 ) +RULE_BOOL (NPC, UseItemBonusesForNonPets, true) +RULE_INT ( NPC, SayPauseTimeInSec, 5) +RULE_INT ( NPC, OOCRegen, 0) +RULE_BOOL ( NPC, BuffFriends, false ) +RULE_BOOL ( NPC, EnableNPCQuestJournal, false) +RULE_INT ( NPC, LastFightingDelayMovingMin, 10000) +RULE_INT ( NPC, LastFightingDelayMovingMax, 20000) +RULE_BOOL ( NPC, SmartLastFightingDelayMoving, true) +RULE_BOOL ( NPC, ReturnNonQuestNoDropItems, false) // Returns NO DROP items on NPCs that don't have an EVENT_ITEM 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, UseMultiQuest, false) // If true, NPC will remember items handed to them for classic multiquest support. +RULE_CATEGORY_END() + +RULE_CATEGORY ( Aggro ) +RULE_BOOL ( Aggro, SmartAggroList, true ) +RULE_INT ( Aggro, SittingAggroMod, 35 ) //35% +RULE_INT ( Aggro, MeleeRangeAggroMod, 10 ) //10% +RULE_INT ( Aggro, CurrentTargetAggroMod, 0 ) //0% -- will prefer our current target to any other; makes it harder for our npcs to switch targets. +RULE_INT ( Aggro, CriticallyWoundedAggroMod, 100 ) //100% +RULE_INT ( Aggro, SpellAggroMod, 100 ) +RULE_INT ( Aggro, SongAggroMod, 33 ) +RULE_INT ( Aggro, PetSpellAggroMod, 10 ) +RULE_REAL ( Aggro, TunnelVisionAggroMod, 0.75 ) //people not currently the top hate generate this much hate on a Tunnel Vision mob +RULE_INT ( Aggro, MaxStunProcAggro, 400 ) // Set to -1 for no limit. Maxmimum amount of aggro that a stun based proc will add. +RULE_CATEGORY_END() + +RULE_CATEGORY ( TaskSystem) +RULE_BOOL ( TaskSystem, EnableTaskSystem, true) // Globally enable or disable the Task system +RULE_INT ( TaskSystem, PeriodicCheckTimer, 5) // Seconds between checks for failed tasks. Also used by the 'Touch' activity +RULE_BOOL ( TaskSystem, RecordCompletedTasks, true) +RULE_BOOL ( TaskSystem, RecordCompletedOptionalActivities, false) +RULE_BOOL ( TaskSystem, KeepOneRecordPerCompletedTask, true) +RULE_BOOL ( TaskSystem, EnableTaskProximity, true) +RULE_CATEGORY_END() + +#ifdef BOTS +RULE_CATEGORY ( Bots ) +RULE_REAL ( Bots, BotManaRegen, 2.0 ) // Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players. +RULE_BOOL ( Bots, BotFinishBuffing, false ) // Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat. +RULE_INT ( Bots, CreateBotCount, 150 ) // Number of bots that each account can create +RULE_INT ( Bots, SpawnBotCount, 71 ) // Number of bots a character can have spawned at one time, You + 71 bots is a 12 group raid +RULE_BOOL ( Bots, BotQuest, false ) // Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl +RULE_BOOL ( 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. +RULE_BOOL ( Bots, BotSpellQuest, false ) // Anita Thrall's (Anita_Thrall.pl) Bot Spell Scriber quests. +RULE_INT ( Bots, BotAAExpansion, 8 ) // Bots get AAs through this expansion +RULE_BOOL ( Bots, BotGroupXP, false ) // Determines whether client gets xp for bots outside their group. +RULE_CATEGORY_END() +#endif + +RULE_CATEGORY ( Chat ) +RULE_BOOL ( Chat, ServerWideOOC, true) +RULE_BOOL ( Chat, ServerWideAuction, true) +RULE_BOOL ( Chat, EnableVoiceMacros, true) +RULE_BOOL ( Chat, EnableMailKeyIPVerification, true) +RULE_BOOL ( Chat, EnableAntiSpam, true) +RULE_BOOL ( Chat, FlowCommandstoPerl_EVENT_SAY, false) // Allows you to parse #commands into EVENT_SAY (Useful in global_player.pl) that aren't found in the source - should probably be individual scripts per command sometime +RULE_INT ( Chat, MinStatusToBypassAntiSpam, 100) +RULE_INT ( Chat, MinimumMessagesPerInterval, 4) +RULE_INT ( Chat, MaximumMessagesPerInterval, 12) +RULE_INT ( Chat, MaxMessagesBeforeKick, 20) +RULE_INT ( Chat, IntervalDurationMS, 60000) +RULE_INT ( Chat, KarmaUpdateIntervalMS, 1200000) +RULE_INT ( Chat, KarmaGlobalChatLimit, 72) //amount of karma you need to be able to talk in ooc/auction/chat below the level limit +RULE_INT ( Chat, GlobalChatLevelLimit, 8) //level limit you need to of reached to talk in ooc/auction/chat if your karma is too low. +RULE_CATEGORY_END() + +RULE_CATEGORY ( Merchant ) +RULE_BOOL ( Merchant, UsePriceMod, true) // Use faction/charisma price modifiers. +RULE_REAL ( Merchant, SellCostMod, 1.05) // Modifier for NPC sell price. +RULE_REAL ( Merchant, BuyCostMod, 0.95) // Modifier for NPC buy price. +RULE_INT ( Merchant, PriceBonusPct, 4) // Determines maximum price bonus from having good faction/CHA. Value is a percent. +RULE_INT ( Merchant, PricePenaltyPct, 4) // Determines maximum price penalty from having bad faction/CHA. Value is a percent. +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_CATEGORY_END() + +RULE_CATEGORY ( Bazaar ) +RULE_BOOL ( Bazaar, AuditTrail, false) +RULE_INT ( Bazaar, MaxSearchResults, 50) +RULE_BOOL ( Bazaar, EnableWarpToTrader, true) +RULE_INT ( Bazaar, MaxBarterSearchResults, 200) // The max results returned in the /barter search +RULE_CATEGORY_END() + +RULE_CATEGORY ( Mail ) +RULE_BOOL ( Mail, EnableMailSystem, true) // If false, client won't bring up the Mail window. +RULE_INT ( Mail, ExpireTrash, 0) // Time in seconds. 0 will delete all messages in the trash when the mailserver starts +RULE_INT ( Mail, ExpireRead, 31536000 ) // 1 Year. Set to -1 for never +RULE_INT ( Mail, ExpireUnread, 31536000 ) // 1 Year. Set to -1 for never +RULE_CATEGORY_END() + +RULE_CATEGORY ( Channels ) +RULE_INT ( Channels, RequiredStatusAdmin, 251) // Required status to administer chat channels +RULE_INT ( Channels, RequiredStatusListAll, 251) // Required status to list all chat channels +RULE_INT ( Channels, DeleteTimer, 1440) // Empty password protected channels will be deleted after this many minutes +RULE_CATEGORY_END() + +RULE_CATEGORY ( EventLog ) +RULE_BOOL ( EventLog, RecordSellToMerchant, false ) // Record sales from a player to an NPC merchant in eventlog table +RULE_BOOL ( EventLog, RecordBuyFromMerchant, false ) // Record purchases by a player from an NPC merchant in eventlog table +RULE_CATEGORY_END() + +RULE_CATEGORY ( Adventure ) +RULE_INT ( Adventure, MinNumberForGroup, 2 ) +RULE_INT ( Adventure, MaxNumberForGroup, 6 ) +RULE_INT ( Adventure, MinNumberForRaid, 18 ) +RULE_INT ( Adventure, MaxNumberForRaid, 36 ) +RULE_INT ( Adventure, MaxLevelRange, 9 ) +RULE_INT ( Adventure, NumberKillsForBossSpawn, 45) +RULE_REAL ( Adventure, DistanceForRescueAccept, 10000.0) +RULE_REAL ( Adventure, DistanceForRescueComplete, 2500.0) +RULE_INT ( Adventure, ItemIDToEnablePorts, 41000 ) //0 to disable, otherwise using a LDoN portal will require the user to have this item. +RULE_INT ( Adventure, LDoNTrapDistanceUse, 625 ) +RULE_REAL ( Adventure, LDoNBaseTrapDifficulty, 15.0 ) +RULE_REAL ( Adventure, LDoNCriticalFailTrapThreshold, 10.0 ) +RULE_INT ( Adventure, LDoNAdventureExpireTime, 1800) //30 minutes to expire +RULE_CATEGORY_END() + +RULE_CATEGORY ( AA ) +RULE_INT ( AA, ExpPerPoint, 23976503) //Amount of exp per AA. Is the same as the amount of exp to go from level 51 to level 52. +RULE_BOOL ( AA, Stacking, true) //Allow AA that belong to the same group to stack on SOF+ clients. +RULE_CATEGORY_END() + +RULE_CATEGORY( Console ) +RULE_INT ( Console, SessionTimeOut, 600000 ) // Amount of time in ms for the console session to time out +RULE_CATEGORY_END() + +RULE_CATEGORY( EQStream ) +RULE_INT ( EQStream, RetransmitTimeoutMax, 5000 ) // maximum retransmit timeout before retransmitting unacked packets +RULE_INT ( EQStream, AverageDeltaMax, 2500 ) // maximum average rtt where we will still recalculate transmit rates +RULE_REAL ( EQStream, RetransmitTimeoutMult, 3.0 ) // multiplier applied to rtt stats to generate a retransmit timeout value +RULE_BOOL ( EQStream, RetransmitAckedPackets, true ) // should we restransmit packets that were already acked? +RULE_CATEGORY_END() + +RULE_CATEGORY( QueryServ ) +RULE_BOOL( QueryServ, PlayerChatLogging, 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, PlayerLogPCCoordinates, false) // Logs Player Coordinates with certain events +RULE_CATEGORY_END() + +#undef RULE_CATEGORY +#undef RULE_INT +#undef RULE_REAL +#undef RULE_BOOL +#undef RULE_CATEGORY_END + diff --git a/common/seperator-2.h b/common/seperator-2.h new file mode 100644 index 000000000..b681c0a8a --- /dev/null +++ b/common/seperator-2.h @@ -0,0 +1,233 @@ +/* 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 +*/ +// This class will split up a string smartly at the div character (default is space) +// Seperator.arg[i] is a copy of the string chopped at the divs +// Seperator.argplus[i] is a pointer to the original string so it doesnt end at the div + +// Written by Quagmire +#ifndef SEPERATOR2_H +#define SEPERATOR2_H +#define arglen 400 +#define argnum 100 // Not including 0 +#define arglenz 100 +#define argnumz 10 // Not including 0 +#define arghlenz 400 +#define arghnumz 2048 // Not including 0 +#define arghlenza 100 +#define arghnumza 10 // Not including 0 + +class Seperator2 +{ +public: + Seperator2(char* messagez, char divz = '|') { + int iz; + for (iz=0; iz <= argnumz; iz++) { + memset(argz[iz], 0, sizeof(argz[iz])); + argplusz[iz] = argz[iz]; + } + + int lenz = strlen(messagez); + int az = 0, sz = 0, lz = 0; + bool inargz = (!(messagez[0] == divz)); + argplusz[0] = messagez; + for (iz=0; iz <= lenz; iz++) { + if (inargz) { + if (messagez[iz] == divz) { + lz = iz-sz; + if (lz >= (arglenz-1)) + lz = (arglenz-1); + memcpy(argz[az], argplusz[az], lz); + memset(&argz[az][lz], 0, 1); + az++; + inargz = false; + } + } + else { + sz = iz; + argplusz[az] = &messagez[iz]; + if (!(messagez[iz] == divz)) { + inargz = true; + } + } + if (az > argnumz) + break; + } + if (inargz) + memcpy(argz[az], argplusz[az], (iz-sz) - 1); + } + ~Seperator2() {} + char argz[argnumz+1][arglenz]; + char* argplusz[argnumz+1]; + bool IsNumberz(int numz) { + bool SeenDecz = false; + int lenz = strlen(argz[numz]); + if (lenz == 0) { + return false; + } + int iz; + for (iz = 0; iz < lenz; iz++) { + if (argz[numz][iz] < '0' || argz[numz][iz] > '9') { + if (argz[numz][iz] == '.' && !SeenDecz) { + SeenDecz = true; + } + else if (iz == 0 && (argz[numz][iz] == '-' || argz[numz][iz] == '+') && !argz[numz][iz+1] == 0) { + // this is ok, do nothin + } + else { + return false; + } + } + } + return true; + } +}; + +class Seperator3 +{ +public: + Seperator3(char* messagez, char divz = 10) { + int iz; + for (iz=0; iz <= arghnumz; iz++) { + memset(arghz[iz], 0, sizeof(arghz[iz])); + arghplusz[iz] = arghz[iz]; + } + + int lenz = strlen(messagez); + int az = 0, sz = 0, lz = 0; + bool inarghz = (!(messagez[0] == divz)); + arghplusz[0] = messagez; + for (iz=0; iz <= lenz; iz++) { + if (inarghz) { + if (messagez[iz] == divz) { + lz = iz-sz; + if (lz >= (arghlenz-1)) + lz = (arghlenz-1); + memcpy(arghz[az], arghplusz[az], lz); + memset(&arghz[az][lz], 0, 1); + az++; + inarghz = false; + } + } + else { + sz = iz; + arghplusz[az] = &messagez[iz]; + if (!(messagez[iz] == divz)) { + inarghz = true; + } + } + if (az > arghnumz) + break; + } + if (inarghz) + memcpy(arghz[az], arghplusz[az], (iz-sz) - 1); + } + ~Seperator3() {} + char arghz[arghnumz+1][arghlenz]; + char* arghplusz[arghnumz+1]; + bool IsNumberz(int numz) { + bool SeenDecz = false; + int lenz = strlen(arghz[numz]); + if (lenz == 0) { + return false; + } + int iz; + for (iz = 0; iz < lenz; iz++) { + if (arghz[numz][iz] < '0' || arghz[numz][iz] > '9') { + if (arghz[numz][iz] == '.' && !SeenDecz) { + SeenDecz = true; + } + else if (iz == 0 && (arghz[numz][iz] == '-' || arghz[numz][iz] == '+') && !arghz[numz][iz+1] == 0) { + // this is ok, do nothin + } + else { + return false; + } + } + } + return true; + } +}; + +class Seperator4 +{ +public: + Seperator4(char* messageza, char divza = ':') { + int iza; + for (iza=0; iza <= arghnumza; iza++) { + memset(arghza[iza], 0, sizeof(arghza[iza])); + arghplusza[iza] = arghza[iza]; + } + + int lenza = strlen(messageza); + int aza = 0, sza = 0, lza = 0; + bool inarghza = (!(messageza[0] == divza)); + arghplusza[0] = messageza; + for (iza=0; iza <= lenza; iza++) { + if (inarghza) { + if (messageza[iza] == divza) { + lza = iza-sza; + if (lza >= (arghlenza-1)) + lza = (arghlenza-1); + memcpy(arghza[aza], arghplusza[aza], lza); + memset(&arghza[aza][lza], 0, 1); + aza++; + inarghza = false; + } + } + else { + sza = iza; + arghplusza[aza] = &messageza[iza]; + if (!(messageza[iza] == divza)) { + inarghza = true; + } + } + if (aza > arghnumza) + break; + } + if (inarghza) + memcpy(arghza[aza], arghplusza[aza], (iza-sza) - 1); + } + ~Seperator4() {} + char arghza[arghnumza+1][arghlenza]; + char* arghplusza[arghnumza+1]; + bool IsNumberza(int numza) { + bool SeenDecza = false; + int lenza = strlen(arghza[numza]); + if (lenza == 0) { + return false; + } + int iza; + for (iza = 0; iza < lenza; iza++) { + if (arghza[numza][iza] < '0' || arghza[numza][iza] > '9') { + if (arghza[numza][iza] == '.' && !SeenDecza) { + SeenDecza = true; + } + else if (iza == 0 && (arghza[numza][iza] == '-' || arghza[numza][iza] == '+') && !arghza[numza][iza+1] == 0) { + // this is ok, do nothin + } + else { + return false; + } + } + } + return true; + } +}; + + +#endif diff --git a/common/seperator.h b/common/seperator.h new file mode 100644 index 000000000..5957528cb --- /dev/null +++ b/common/seperator.h @@ -0,0 +1,156 @@ +/* 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 +*/ +// This class will split up a string smartly at the div character (default is space and tab) +// Seperator.arg[i] is a copy of the string chopped at the divs +// Seperator.argplus[i] is a pointer to the original string so it doesnt end at the div + +// Written by Quagmire +#ifndef SEPERATOR_H +#define SEPERATOR_H + +#include +#include + +class Seperator +{ +public: + Seperator(const char* message_in, char div = ' ', uint16 in_maxargnum = 10, uint16 arglen = 100, bool iObeyQuotes = false, char div2 = '\t', char div3 = 0, bool iSkipEmpty = true) { + int i; + argnum = 0; + int len = static_cast(strlen(message_in)); + + if(arglen > len) + arglen = len+1; + + //msg = strdup(message); + msg = new char[len+1]; + strcpy(msg, message_in); + const char *message = msg; + this->maxargnum = in_maxargnum; + argplus = new const char *[maxargnum+1]; + arg = new char *[maxargnum+1]; + for (i=0; i<=maxargnum; i++) { + argplus[i]=arg[i] = new char[arglen+1]; + memset(arg[i], 0, arglen+1); + } + + int s = 0, l = 0; + bool inarg = (!iSkipEmpty || !(message[0] == div || message[0] == div2 || message[0] == div3)); + bool inquote = (iObeyQuotes && (message[0] == '\"' || message[0] == '\'')); + argplus[0] = message; + if (len == 0) + return; + + for (i=0; i= arglen) + l = arglen; + if (l) + memcpy(arg[argnum], argplus[argnum], l); + arg[argnum][l] = 0; + argnum++; + if (iSkipEmpty) + inarg = false; + else { + s=i+1; + argplus[argnum] = &message[s]; + } + } + } + else if (iObeyQuotes && (message[i] == '\"' || message[i] == '\'')) { + inquote = true; + } + else { + s = i; + argplus[argnum] = &message[s]; + if (!(message[i] == div || message[i] == div2 || message[i] == div3)) { + inarg = true; + } + } + if (argnum > maxargnum) + break; + } + if (inarg && argnum <= maxargnum) { + l = i-s; + if (l >= arglen) + l = arglen; + if (l) + memcpy(arg[argnum], argplus[argnum], l); + } + } + ~Seperator() { + for (int i=0; i<=maxargnum; i++) + safe_delete_array(arg[i]); + safe_delete_array(arg); + safe_delete_array(argplus); + safe_delete_array(msg); + } + uint16 argnum; + char** arg; + const char** argplus; + char * msg; + bool IsNumber(int num) const { + return IsNumber(arg[num]); + } + bool IsHexNumber(int num) const { + return IsHexNumber(arg[num]); + } + static bool IsNumber(const char* check) { + bool SeenDec = false; + int len = static_cast(strlen(check)); + if (len == 0) { + return false; + } + int i; + for (i = 0; i < len; i++) { + if (check[i] < '0' || check[i] > '9') { + if (check[i] == '.' && !SeenDec) { + SeenDec = true; + } + else if (i == 0 && (check[i] == '-' || check[i] == '+') && !check[i+1] == 0) { + // this is ok, do nothin + } + else { + return false; + } + } + } + return true; + } + static bool IsHexNumber(char* check) { + int len = static_cast(strlen(check)); + if (len < 3) + return false; + if (check[0] != '0' || (check[1] != 'x' && check[1] != 'X')) + return false; + for (int i=2; i '9') && (check[i] < 'A' || check[i] > 'F') && (check[i] < 'a' || check[i] > 'f')) + return false; + } + return true; + } + inline uint16 GetMaxArgNum() const { return maxargnum; } +private: + uint16 maxargnum; +}; + +#endif diff --git a/common/serverinfo.cpp b/common/serverinfo.cpp new file mode 100644 index 000000000..410da4c26 --- /dev/null +++ b/common/serverinfo.cpp @@ -0,0 +1,123 @@ +/* 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 +*/ +// Serverinfo.cpp - Server information gathering functions, used in #serverinfo - Windows specific +// I'm not sure quite how to get this exact information in *nix, hopefully someone can fill that in +// -T7g +// Implement preliminary support for *nix variants +// misanthropicfiend + +#ifdef _WINDOWS +#include + +char Ver_name[100]; +DWORD Ver_build, Ver_min, Ver_maj, Ver_pid; + +int GetOS() { + + strcpy(Ver_name, "Unknown operating system"); + + OSVERSIONINFO Ver_os; + Ver_os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + + if(!(GetVersionEx(&Ver_os))) return 1; + + Ver_build = Ver_os.dwBuildNumber & 0xFFFF; + Ver_min = Ver_os.dwMinorVersion; + Ver_maj = Ver_os.dwMajorVersion; + Ver_pid = Ver_os.dwPlatformId; + + if ((Ver_pid == 1) && (Ver_maj == 4)) + { + if ((Ver_min < 10) && (Ver_build == 950)) + { + strcpy(Ver_name, "Microsoft Windows 95"); + } + else if ((Ver_min < 10) && + ((Ver_build > 950) && (Ver_build <= 1080))) + { + strcpy(Ver_name, "Microsoft Windows 95 SP1"); + } + else if ((Ver_min < 10) && (Ver_build > 1080)) + { + strcpy(Ver_name, "Microsoft Windows 95 OSR2"); + } + else if ((Ver_min == 10) && (Ver_build == 1998)) + { + strcpy(Ver_name, "Microsoft Windows 98"); + } + else if ((Ver_min == 10) && + ((Ver_build > 1998) && (Ver_build < 2183))) + { + strcpy(Ver_name, "Microsoft Windows 98, Service Pack 1"); + } + else if ((Ver_min == 10) && (Ver_build >= 2183)) + { + strcpy(Ver_name, "Microsoft Windows 98 Second Edition"); + } + else if (Ver_min == 90) + { + strcpy(Ver_name, "Microsoft Windows ME"); + } + } + else if (Ver_pid == 2) + { + if ((Ver_maj == 3) && (Ver_min == 51)) + { + strcpy(Ver_name, "Microsoft Windows NT 3.51"); + } + else if ((Ver_maj == 4) && (Ver_min == 0)) + { + strcpy(Ver_name, "Microsoft Windows NT 4"); + } + else if ((Ver_maj == 5) && (Ver_min == 0)) + { + strcpy(Ver_name, "Microsoft Windows 2000"); + } + else if ((Ver_maj == 5) && (Ver_min == 1)) + { + strcpy(Ver_name, "Microsoft Windows XP"); + } + else if ((Ver_maj == 5) && (Ver_min == 2)) + { + strcpy(Ver_name, "Microsoft Windows 2003"); + } + } + + return 0; +} + +#else + +#include +#include +#include + +char* GetOS(char* os_string) { + utsname info; + + if(uname(&info)==0) { + snprintf(os_string, 99, "%s %s %s %s %s", info.sysname, info.nodename, info.release, info.version, info.machine); + } else { + strncpy(os_string, "Error determining OS & version!", 25); + } + + return os_string; + +} + +#endif diff --git a/common/serverinfo.h b/common/serverinfo.h new file mode 100644 index 000000000..dcba2a525 --- /dev/null +++ b/common/serverinfo.h @@ -0,0 +1,29 @@ +/* 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 SERVERINFO_H +#define SERVERINFO_H + +#ifdef _WINDOWS + extern char Ver_name[36]; + extern DWORD Ver_build, Ver_min, Ver_maj, Ver_pid; + int GetOS(); +#else + char* GetOS(char* os_string); +#endif + +#endif diff --git a/common/servertalk.h b/common/servertalk.h new file mode 100644 index 000000000..97221b107 --- /dev/null +++ b/common/servertalk.h @@ -0,0 +1,1236 @@ +#ifndef EQ_SOPCODES_H +#define EQ_SOPCODES_H + +#define EQEMU_PROTOCOL_VERSION "0.3.10" + +#include "../common/types.h" +#include "../common/packet_functions.h" +#include "../common/eq_packet_structs.h" + +#define SERVER_TIMEOUT 45000 // how often keepalive gets sent +#define INTERSERVER_TIMER 10000 +#define LoginServer_StatusUpdateInterval 15000 +#define LoginServer_AuthStale 60000 +#define AUTHCHANGE_TIMEOUT 900 // in seconds + +#define ServerOP_KeepAlive 0x0001 // packet to test if port is still open +#define ServerOP_ChannelMessage 0x0002 // broadcast/guildsay +#define ServerOP_SetZone 0x0003 // client -> server zoneinfo +#define ServerOP_ShutdownAll 0x0004 // exit(0); +#define ServerOP_ZoneShutdown 0x0005 // unload all data, goto sleep mode +#define ServerOP_ZoneBootup 0x0006 // come out of sleep mode and load zone specified +#define ServerOP_ZoneStatus 0x0007 // Shows status of all zones +#define ServerOP_SetConnectInfo 0x0008 // Tells server address and port # +#define ServerOP_EmoteMessage 0x0009 // Worldfarts +#define ServerOP_ClientList 0x000A // Update worldserver's client list, for #whos +#define ServerOP_Who 0x000B // #who +#define ServerOP_ZonePlayer 0x000C // #zone, or #summon +#define ServerOP_KickPlayer 0x000D // #kick + +#define ServerOP_RefreshGuild 0x000E // Notice to all zoneservers to refresh their guild cache for ID# in packet (ServerGuildRefresh_Struct) +#define ServerOP_VoiceMacro 0x000F +//#define ServerOP_GuildInvite 0x0010 +#define ServerOP_DeleteGuild 0x0011 // ServerGuildID_Struct +#define ServerOP_GuildRankUpdate 0x0012 +#define ServerOP_GuildCharRefresh 0x0013 +#define ServerOP_GuildMemberUpdate 0x0014 +#define ServerOP_RequestOnlineGuildMembers 0x0015 +#define ServerOP_OnlineGuildMembersResponse 0x0016 +#define ServerOP_LFGuildUpdate 0x0017 + +#define ServerOP_FlagUpdate 0x0018 // GM Flag updated for character, refresh the memory cache +#define ServerOP_GMGoto 0x0019 +#define ServerOP_MultiLineMsg 0x001A +#define ServerOP_Lock 0x001B // For #lock/#unlock inside server +#define ServerOP_Motd 0x001C // For changing MoTD inside server. +#define ServerOP_Uptime 0x001D +#define ServerOP_Petition 0x001E +#define ServerOP_KillPlayer 0x001F +#define ServerOP_UpdateGM 0x0020 +#define ServerOP_RezzPlayer 0x0021 +#define ServerOP_ZoneReboot 0x0022 +#define ServerOP_ZoneToZoneRequest 0x0023 +#define ServerOP_AcceptWorldEntrance 0x0024 +#define ServerOP_ZAAuth 0x0025 +#define ServerOP_ZAAuthFailed 0x0026 +#define ServerOP_ZoneIncClient 0x0027 // Incomming client +#define ServerOP_ClientListKA 0x0028 +#define ServerOP_ChangeWID 0x0029 +#define ServerOP_IPLookup 0x002A +#define ServerOP_LockZone 0x002B +#define ServerOP_ItemStatus 0x002C +#define ServerOP_OOCMute 0x002D +#define ServerOP_Revoke 0x002E +//#define 0x002F +#define ServerOP_GroupIDReq 0x0030 +#define ServerOP_GroupIDReply 0x0031 +#define ServerOP_GroupLeave 0x0032 // for disbanding out of zone folks +#define ServerOP_RezzPlayerAccept 0x0033 +#define ServerOP_SpawnCondition 0x0034 +#define ServerOP_SpawnEvent 0x0035 +#define ServerOP_SetLaunchName 0x0036 +#define ServerOP_RezzPlayerReject 0x0037 +#define ServerOP_SpawnPlayerCorpse 0x0038 +#define ServerOP_Consent 0x0039 +#define ServerOP_Consent_Response 0x003a +#define ServerOP_ForceGroupUpdate 0x003b +#define ServerOP_OOZGroupMessage 0x003c +#define ServerOP_DisbandGroup 0x003d //for disbanding a whole group cross zone +#define ServerOP_GroupJoin 0x003e //for joining ooz folks +#define ServerOP_UpdateSpawn 0x003f +#define ServerOP_SpawnStatusChange 0x0040 +#define ServerOP_ReloadTasks 0x0060 +#define ServerOP_DepopAllPlayersCorpses 0x0061 +#define ServerOP_ReloadTitles 0x0062 +#define ServerOP_QGlobalUpdate 0x0063 +#define ServerOP_QGlobalDelete 0x0064 +#define ServerOP_DepopPlayerCorpse 0x0065 + +#define ServerOP_RaidAdd 0x0100 //in use +#define ServerOP_RaidRemove 0x0101 //in use +#define ServerOP_RaidDisband 0x0102 //in use +#define ServerOP_RaidLockFlag 0x0103 //in use +#define ServerOP_RaidGroupLeader 0x0104 //in use +#define ServerOP_RaidLeader 0x0105 //in use +#define ServerOP_RaidGroupSay 0x0106 //in use +#define ServerOP_RaidSay 0x0107 //in use +#define ServerOP_DetailsChange 0x0108 //in use + +#define ServerOP_UpdateGroup 0x010A //in use +#define ServerOP_RaidGroupDisband 0x010B //in use +#define ServerOP_RaidChangeGroup 0x010C //in use +#define ServerOP_RaidGroupAdd 0x010D +#define ServerOP_RaidGroupRemove 0x010E +#define ServerOP_GroupInvite 0x010F +#define ServerOP_GroupFollow 0x0110 +#define ServerOP_GroupFollowAck 0x0111 +#define ServerOP_GroupCancelInvite 0x0112 + +#define ServerOP_InstanceUpdateTime 0x014F +#define ServerOP_AdventureRequest 0x0150 +#define ServerOP_AdventureRequestAccept 0x0151 +#define ServerOP_AdventureRequestDeny 0x0152 +#define ServerOP_AdventureRequestCreate 0x0153 +#define ServerOP_AdventureData 0x0154 +#define ServerOP_AdventureDataClear 0x0155 +#define ServerOP_AdventureCreateDeny 0x0156 +#define ServerOP_AdventureDataRequest 0x0157 +#define ServerOP_AdventureClickDoor 0x0158 +#define ServerOP_AdventureClickDoorReply 0x0159 +#define ServerOP_AdventureClickDoorError 0x015a +#define ServerOP_AdventureLeave 0x015b +#define ServerOP_AdventureLeaveReply 0x015c +#define ServerOP_AdventureLeaveDeny 0x015d +#define ServerOP_AdventureCountUpdate 0x015e +#define ServerOP_AdventureZoneData 0x015f +#define ServerOP_AdventureAssaCountUpdate 0x0160 +#define ServerOP_AdventureFinish 0x0161 +#define ServerOP_AdventureLeaderboard 0x0162 + +#define ServerOP_WhoAll 0x0210 +#define ServerOP_FriendsWho 0x0211 +#define ServerOP_LFGMatches 0x0212 +#define ServerOP_LFPUpdate 0x0213 +#define ServerOP_LFPMatches 0x0214 +#define ServerOP_ClientVersionSummary 0x0215 +#define ServerOP_LSInfo 0x1000 +#define ServerOP_LSStatus 0x1001 +#define ServerOP_LSClientAuth 0x1002 +#define ServerOP_LSFatalError 0x1003 +#define ServerOP_SystemwideMessage 0x1005 +#define ServerOP_ListWorlds 0x1006 +#define ServerOP_PeerConnect 0x1007 +#define ServerOP_NewLSInfo 0x1008 +#define ServerOP_LSRemoteAddr 0x1009 +#define ServerOP_LSAccountUpdate 0x100A + +#define ServerOP_EncapPacket 0x2007 // Packet within a packet +#define ServerOP_WorldListUpdate 0x2008 +#define ServerOP_WorldListRemove 0x2009 +#define ServerOP_TriggerWorldListRefresh 0x200A +#define ServerOP_WhoAllReply 0x2010 +#define ServerOP_SetWorldTime 0x200B +#define ServerOP_GetWorldTime 0x200C +#define ServerOP_SyncWorldTime 0x200E + +#define ServerOP_LSZoneInfo 0x3001 +#define ServerOP_LSZoneStart 0x3002 +#define ServerOP_LSZoneBoot 0x3003 +#define ServerOP_LSZoneShutdown 0x3004 +#define ServerOP_LSZoneSleep 0x3005 +#define ServerOP_LSPlayerLeftWorld 0x3006 +#define ServerOP_LSPlayerJoinWorld 0x3007 +#define ServerOP_LSPlayerZoneChange 0x3008 + +#define ServerOP_UsertoWorldReq 0xAB00 +#define ServerOP_UsertoWorldResp 0xAB01 + +#define ServerOP_LauncherConnectInfo 0x3000 +#define ServerOP_LauncherZoneRequest 0x3001 +#define ServerOP_LauncherZoneStatus 0x3002 +#define ServerOP_DoZoneCommand 0x3003 + +#define ServerOP_UCSMessage 0x4000 +#define ServerOP_UCSMailMessage 0x4001 +#define ServerOP_ReloadRules 0x4002 +#define ServerOP_ReloadRulesWorld 0x4003 +#define ServerOP_CameraShake 0x4004 +#define ServerOP_QueryServGeneric 0x4005 +#define ServerOP_CZSignalClient 0x4006 +#define ServerOP_CZSignalClientByName 0x4007 +#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 + +enum { QSG_LFGuild = 0 }; +enum { QSG_LFGuild_PlayerMatches = 0, QSG_LFGuild_UpdatePlayerInfo, QSG_LFGuild_RequestPlayerInfo, QSG_LFGuild_UpdateGuildInfo, QSG_LFGuild_GuildMatches, + QSG_LFGuild_RequestGuildInfo }; + +#define ServerOP_Speech 0x4513 + +/************ PACKET RELATED STRUCT ************/ +class ServerPacket +{ +public: + ~ServerPacket() { safe_delete_array(pBuffer); } + ServerPacket(uint16 in_opcode = 0, uint32 in_size = 0) { + this->compressed = false; + size = in_size; + opcode = in_opcode; + if (size == 0) { + pBuffer = 0; + } + else { + pBuffer = new uchar[size]; + memset(pBuffer, 0, size); + } + _wpos = 0; + _rpos = 0; + } + ServerPacket* Copy() { + if (this == 0) { + return 0; + } + ServerPacket* ret = new ServerPacket(this->opcode, this->size); + if (this->size) + memcpy(ret->pBuffer, this->pBuffer, this->size); + ret->compressed = this->compressed; + ret->InflatedSize = this->InflatedSize; + return ret; + } + bool Deflate() { + if (compressed) + return false; + if ((!this->pBuffer) || (!this->size)) + return false; + uchar* tmp = new uchar[this->size + 128]; + uint32 tmpsize = DeflatePacket(this->pBuffer, this->size, tmp, this->size + 128); + if (!tmpsize) { + safe_delete_array(tmp); + return false; + } + this->compressed = true; + this->InflatedSize = this->size; + this->size = tmpsize; + uchar* tmpdel = this->pBuffer; + this->pBuffer = tmp; + safe_delete_array(tmpdel); + return true; + } + bool Inflate() { + if (!compressed) + return false; + if ((!this->pBuffer) || (!this->size)) + return false; + uchar* tmp = new uchar[InflatedSize]; + uint32 tmpsize = InflatePacket(this->pBuffer, this->size, tmp, InflatedSize); + if (!tmpsize) { + safe_delete_array(tmp); + return false; + } + compressed = false; + this->size = tmpsize; + uchar* tmpdel = this->pBuffer; + this->pBuffer = tmp; + safe_delete_array(tmpdel); + return true; + } + + void WriteUInt8(uint8 value) { *(uint8 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint8); } + void WriteUInt32(uint32 value) { *(uint32 *)(pBuffer + _wpos) = value; _wpos += sizeof(uint32); } + void WriteString(const char * str) { uint32 len = static_cast(strlen(str)) + 1; memcpy(pBuffer + _wpos, str, len); _wpos += len; } + + uint8 ReadUInt8() { uint8 value = *(uint8 *)(pBuffer + _rpos); _rpos += sizeof(uint8); return value; } + uint32 ReadUInt32() { uint32 value = *(uint32 *)(pBuffer + _rpos); _rpos += sizeof(uint32); return value; } + void ReadString(char *str) { uint32 len = static_cast(strlen((char *)(pBuffer + _rpos))) + 1; memcpy(str, pBuffer + _rpos, len); _rpos += len; } + + uint32 GetWritePosition() { return _wpos; } + uint32 GetReadPosition() { return _rpos; } + void SetWritePosition(uint32 Newwpos) { _wpos = Newwpos; } + void WriteSkipBytes(uint32 count) { _wpos += count; } + void ReadSkipBytes(uint32 count) { _rpos += count; } + void SetReadPosition(uint32 Newrpos) { _rpos = Newrpos; } + + uint32 size; + uint16 opcode; + uchar* pBuffer; + uint32 _wpos; + uint32 _rpos; + bool compressed; + uint32 InflatedSize; + uint32 destination; +}; + +#pragma pack(1) + +struct SPackSendQueue { + uint16 size; + uchar buffer[0]; +}; + +struct ServerZoneStateChange_struct { + uint32 ZoneServerID; + char adminname[64]; + uint32 zoneid; + uint16 instanceid; + bool makestatic; +}; + +struct ServerZoneIncommingClient_Struct { + uint32 zoneid; // in case the zone shut down, boot it back up + uint16 instanceid; // instance id if it exists for booting up + uint32 ip; // client's IP address + uint32 wid; // client's WorldID# + uint32 accid; + int16 admin; + uint32 charid; + bool tellsoff; + char charname[64]; + char lskey[30]; +}; + +struct ServerChangeWID_Struct { + uint32 charid; + uint32 newwid; +}; +struct SendGroup_Struct{ + uint8 grouptotal; + uint32 zoneid; + char leader[64]; + char thismember[64]; + char members[5][64]; +}; + +struct ServerGroupFollow_Struct { + uint32 CharacterID; + GroupGeneric_Struct gf; +}; + +struct ServerGroupFollowAck_Struct { + char Name[64]; +}; + + +struct ServerChannelMessage_Struct { + char deliverto[64]; + char to[64]; + char from[64]; + uint8 fromadmin; + bool noreply; + uint16 chan_num; + uint32 guilddbid; + uint16 language; + char message[0]; +}; + +struct ServerEmoteMessage_Struct { + char to[64]; + uint32 guilddbid; + int16 minstatus; + uint32 type; + char message[0]; +}; + +struct ServerVoiceMacro_Struct { + char From[64]; + union { + char To[64]; + uint32 GroupID; + uint32 RaidID; + }; + uint32 Type; + uint32 Voice; + uint32 MacroNumber; +}; + +struct ServerClientList_Struct { + uint8 remove; + uint32 wid; + uint32 IP; + uint32 zone; + uint16 instance_id; + int16 Admin; + uint32 charid; + char name[64]; + uint32 AccountID; + char AccountName[30]; + uint32 LSAccountID; + char lskey[30]; + uint16 race; + uint8 class_; + uint8 level; + uint8 anon; + bool tellsoff; + uint32 guild_id; + bool LFG; + uint8 gm; + uint8 ClientVersion; + uint8 LFGFromLevel; + uint8 LFGToLevel; + bool LFGMatchFilter; + char LFGComments[64]; +}; + +struct ServerClientListKeepAlive_Struct { + uint32 numupdates; + uint32 wid[0]; +}; + +struct ServerZonePlayer_Struct { + char adminname[64]; + int16 adminrank; + uint8 ignorerestrictions; + char name[64]; + char zone[25]; + uint32 instance_id; + float x_pos; + float y_pos; + float z_pos; +}; + +struct RezzPlayer_Struct { + uint32 dbid; + uint32 exp; + uint16 rezzopcode; + //char packet[160]; + Resurrect_Struct rez; +}; + +struct ServerZoneReboot_Struct { +// char ip1[250]; + char ip2[250]; + uint16 port; + uint32 zoneid; +}; + +struct SetZone_Struct { + uint16 instanceid; + uint32 zoneid; + bool staticzone; +}; + +struct ServerKickPlayer_Struct { + char adminname[64]; + int16 adminrank; + char name[64]; + uint32 AccountID; +}; + +struct ServerLSInfo_Struct { + char name[201]; // name the worldserver wants + char address[250]; // DNS address of the server + char account[31]; // account name for the worldserver + char password[31]; // password for the name + char protocolversion[25]; // Major protocol version number + char serverversion[64]; // minor server software version number + uint8 servertype; // 0=world, 1=chat, 2=login, 3=MeshLogin +}; + +struct ServerNewLSInfo_Struct { + char name[201]; // name the worldserver wants + char shortname[50]; // shortname the worldserver wants + char remote_address[125]; // DNS address of the server + char local_address[125]; // DNS address of the server + char account[31]; // account name for the worldserver + char password[31]; // password for the name + char protocolversion[25]; // Major protocol version number + char serverversion[64]; // minor server software version number + uint8 servertype; // 0=world, 1=chat, 2=login, 3=MeshLogin +}; + +struct ServerLSAccountUpdate_Struct { // for updating info on login server + char worldaccount[31]; // account name for the worldserver + char worldpassword[31]; // password for the name + uint32 useraccountid; // player account ID + char useraccount[31]; // player account name + char userpassword[51]; // player account password + char useremail[101]; // player account email address +}; + +struct ServerLSStatus_Struct { + int32 status; + int32 num_players; + int32 num_zones; +}; +struct ZoneInfo_Struct { + uint32 zone; + uint16 count; + uint32 zone_wid; +}; +struct ZoneBoot_Struct { + uint32 zone; + uint32 instance; + char compile_time[25]; + uint32 zone_wid; +}; +struct ZoneShutdown_Struct { + uint32 zone; + uint32 instance; + uint32 zone_wid; +}; +struct ServerLSZoneSleep_Struct { + uint32 zone; + uint32 instance; + uint32 zone_wid; +}; + +struct ServerLSPlayerJoinWorld_Struct { + uint32 lsaccount_id; + char key[30]; +}; + +struct ServerLSPlayerLeftWorld_Struct { + uint32 lsaccount_id; + char key[30]; +}; + +struct ServerLSPlayerZoneChange_Struct { + uint32 lsaccount_id; + uint32 from; // 0 = world + uint32 to; // 0 = world +}; +struct ServerLSClientAuth { + uint32 lsaccount_id; // ID# in login server's db + char name[30]; // username in login server's db + char key[30]; // the Key the client will present + uint8 lsadmin; // login server admin level + int16 worldadmin; // login's suggested worldadmin level setting for this user, up to the world if they want to obey it + uint32 ip; + uint8 local; // 1 if the client is from the local network +}; + +struct ServerSystemwideMessage { + uint32 lsaccount_id; + char key[30]; // sessionID key for verification + uint32 type; + char message[0]; +}; + +struct ServerLSPeerConnect { + uint32 ip; + uint16 port; +}; + +struct ServerConnectInfo { + char address[250]; + uint16 port; +}; + +struct ServerGMGoto_Struct { + char myname[64]; + char gotoname[64]; + int16 admin; +}; + +struct ServerMultiLineMsg_Struct { + char to[64]; + char message[0]; +}; + +struct ServerLock_Struct { + char myname[64]; // User that did it + uint8 mode; // 0 = Unlocked ; 1 = Locked +}; + +struct ServerMotd_Struct { + char myname[64]; // User that set the motd + char motd[512]; // the new MoTD +}; + +struct ServerUptime_Struct { + uint32 zoneserverid; // 0 for world + char adminname[64]; +}; + +struct ServerPetitionUpdate_Struct { + uint32 petid; // Petition Number + uint8 status; // 0x00 = ReRead DB -- 0x01 = Checkout -- More? Dunno... lol +}; + +struct ServerWhoAll_Struct { + int16 admin; + uint32 fromid; + char from[64]; + char whom[64]; + uint16 wrace; // FF FF = no race + uint16 wclass; // FF FF = no class + uint16 lvllow; // FF FF = no numbers + uint16 lvlhigh; // FF FF = no numbers + uint16 gmlookup; // FF FF = not doing /who all gm +}; + +struct ServerFriendsWho_Struct { + uint32 FromID; + char FromName[64]; + char FriendsString[1]; +}; + +struct ServerKillPlayer_Struct { + char gmname[64]; + char target[64]; + int16 admin; +}; + +struct ServerUpdateGM_Struct { + char gmname[64]; + bool gmstatus; +}; + +struct ServerEncapPacket_Struct { + uint32 ToID; // ID number of the LWorld on the other server + uint16 opcode; + uint16 size; + uchar data[0]; +}; + +struct ZoneToZone_Struct { + char name[64]; + uint32 guild_id; + uint32 requested_zone_id; + uint32 requested_instance_id; + uint32 current_zone_id; + uint32 current_instance_id; + int8 response; + int16 admin; + uint8 ignorerestrictions; +}; + +struct WorldToZone_Struct { + uint32 account_id; + int8 response; +}; +struct WorldShutDown_Struct { + uint32 time; + uint32 interval; +}; +struct ServerSyncWorldList_Struct { + uint32 RemoteID; + uint32 ip; + int32 status; + char name[201]; + char address[250]; + char account[31]; + uint32 accountid; + uint8 authlevel; + uint8 servertype; // 0=world, 1=chat, 2=login + uint32 adminid; + uint8 greenname; + uint8 showdown; + int32 num_players; + int32 num_zones; + bool placeholder; +}; + +struct UsertoWorldRequest_Struct { + uint32 lsaccountid; + uint32 worldid; + uint32 FromID; + uint32 ToID; +}; + +struct UsertoWorldResponse_Struct { + uint32 lsaccountid; + uint32 worldid; + int8 response; // -3) World Full, -2) Banned, -1) Suspended, 0) Denied, 1) Allowed + uint32 FromID; + uint32 ToID; +}; + +// generic struct to be used for alot of simple zone->world questions +struct ServerGenericWorldQuery_Struct { + char from[64]; // charname the query is from + int16 admin; // char's admin level + char query[0]; // text of the query +}; + +struct ServerLockZone_Struct { + uint8 op; + char adminname[64]; + uint16 zoneID; +}; + +struct RevokeStruct { + char adminname[64]; + char name[64]; + bool toggle; //0 off 1 on +}; + +struct ServerGroupIDReply_Struct { + uint32 start; //a range of group IDs to use. + uint32 end; +}; + +struct ServerGroupLeave_Struct { + uint32 zoneid; + uint16 instance_id; + uint32 gid; + char member_name[64]; //kick this member from the group +}; + +struct ServerGroupJoin_Struct { + uint32 zoneid; + uint16 instance_id; + uint32 gid; + char member_name[64]; //this person is joining the group +}; + +struct ServerForceGroupUpdate_Struct { + uint32 origZoneID; + uint16 instance_id; + uint32 gid; +}; + +struct ServerGroupChannelMessage_Struct { + uint32 zoneid; + uint16 instanceid; + uint32 groupid; + char from[64]; + char message[0]; +}; + +struct ServerDisbandGroup_Struct { + uint32 zoneid; + uint16 instance_id; + uint32 groupid; +}; + +struct SimpleName_Struct{ + char name[64]; +}; + +struct ServerSpawnCondition_Struct { + uint32 zoneID; + uint32 instanceID; + uint16 condition_id; + int16 value; +}; + +struct ServerSpawnEvent_Struct { + uint32 zoneID; + uint32 event_id; +}; + +//zone->world +struct LaunchName_Struct { + char launcher_name[32]; + char zone_name[16]; +}; + +struct LauncherConnectInfo { + char name[64]; +}; + +typedef enum { + ZR_Start, + ZR_Restart, + ZR_Stop +} ZoneRequestCommands; +struct LauncherZoneRequest { + uint8 command; + char short_name[33]; +}; + +struct LauncherZoneStatus { + char short_name[33]; + uint32 start_count; + uint8 running; +}; + + +struct ServerGuildID_Struct { + uint32 guild_id; +}; + +struct ServerGuildRefresh_Struct { + uint32 guild_id; + uint8 name_change; + uint8 motd_change; + uint8 rank_change; + uint8 relation_change; +}; + +struct ServerGuildCharRefresh_Struct { + uint32 guild_id; + uint32 old_guild_id; + uint32 char_id; +}; + +struct ServerGuildRankUpdate_Struct +{ + uint32 GuildID; + char MemberName[64]; + uint32 Rank; + uint32 Banker; +}; + +struct ServerGuildMemberUpdate_Struct { + uint32 GuildID; + char MemberName[64]; + uint32 ZoneID; + uint32 LastSeen; +}; + +struct SpawnPlayerCorpse_Struct { + uint32 player_corpse_id; + uint32 zone_id; +}; + +struct ServerOP_Consent_Struct { + char grantname[64]; + char ownername[64]; + uint8 permission; + uint32 zone_id; + uint16 instance_id; + uint32 message_string_id; +}; + +struct ReloadTasks_Struct { + uint32 Command; + uint32 Parameter; +}; + +struct ServerDepopAllPlayersCorpses_Struct +{ + uint32 CharacterID; + uint32 ZoneID; + uint16 InstanceID; +}; + +struct ServerDepopPlayerCorpse_Struct +{ + uint32 DBID; + uint32 ZoneID; + uint16 InstanceID; +}; + +struct ServerRaidGeneralAction_Struct { + uint32 zoneid; + uint16 instance_id; + uint32 rid; + uint32 gid; + char playername[64]; +}; + +struct ServerRaidGroupAction_Struct { //add / remove depends on opcode. + char membername[64]; //member who's adding / leaving + uint32 gid; //group id to send to. + uint32 rid; //raid id to send to. +}; + +struct ServerRaidMessage_Struct { + uint32 rid; + uint32 gid; + char from[64]; + char message[0]; +}; + +struct ServerLFGMatchesRequest_Struct { + uint32 FromID; + uint8 QuerierLevel; + char FromName[64]; + uint8 FromLevel; + uint8 ToLevel; + uint32 Classes; +}; + +struct ServerLFGMatchesResponse_Struct { + char Name[64]; + uint8 Level; + uint8 Class_; + uint16 Zone; + uint16 GuildID; + uint16 Anon; + char Comments[64]; +}; + +struct ServerLFPUpdate_Struct { + uint32 LeaderID; + uint8 Action; + uint8 MatchFilter; + uint32 FromLevel; + uint32 ToLevel; + uint32 Classes; + char Comments[64]; + GroupLFPMemberEntry Members[MAX_GROUP_MEMBERS]; +}; + +struct ServerLFPMatchesResponse_Struct { + uint32 FromLevel; + uint32 ToLevel; + uint32 Classes; + GroupLFPMemberEntry Members[MAX_GROUP_MEMBERS]; + char Comments[64]; +}; + +struct ServerLFPMatchesRequest_Struct { + uint32 FromID; + uint8 FromLevel; + uint8 ToLevel; + uint8 QuerierClass; + uint8 QuerierLevel; + char FromName[64]; +}; + +struct UpdateSpawnTimer_Struct { + uint32 id; + uint32 duration; +}; + +struct ServerInstanceUpdateTime_Struct +{ + uint16 instance_id; + uint32 new_duration; +}; + +struct ServerSpawnStatusChange_Struct +{ + uint32 id; + bool new_status; +}; + +struct ServerQGlobalUpdate_Struct +{ + uint32 id; + char name[64]; + char value[128]; + uint32 npc_id; + uint32 char_id; + uint32 zone_id; + uint32 expdate; + uint32 from_zone_id; + uint32 from_instance_id; +}; + +struct ServerQGlobalDelete_Struct +{ + char name[64]; + uint32 npc_id; + uint32 char_id; + uint32 zone_id; + uint32 from_zone_id; + uint32 from_instance_id; +}; + +struct ServerRequestOnlineGuildMembers_Struct +{ + uint32 FromID; + uint32 GuildID; +}; + +struct ServerRequestClientVersionSummary_Struct +{ + char Name[64]; +}; + +struct ServerAdventureRequest_Struct +{ + char leader[64]; + uint32 template_id; + uint8 type; + uint8 risk; + uint8 member_count; +}; + +struct ServerAdventureRequestDeny_Struct +{ + char leader[64]; + char reason[512]; +}; + +struct ServerAdventureRequestAccept_Struct +{ + char leader[64]; + char text[512]; + uint32 theme; + uint32 id; + uint32 member_count; +}; + +struct ServerAdventureRequestCreate_Struct +{ + char leader[64]; + uint32 theme; + uint32 id; + uint32 member_count; +}; + +struct ServerSendAdventureData_Struct +{ + char player[64]; + char text[512]; + uint32 time_left; + uint32 time_to_enter; + uint32 risk; + float x; + float y; + int count; + int total; + uint32 zone_in_id; + uint32 zone_in_object; + uint16 instance_id; + uint32 finished_adventures; +}; + +struct ServerFinishedAdventures_Struct +{ + uint32 zone_in_id; + uint32 zone_in_object; +}; + +struct ServerPlayerClickedAdventureDoor_Struct +{ + char player[64]; + int id; + int zone_id; +}; + +struct ServerPlayerClickedAdventureDoorReply_Struct +{ + char player[64]; + int zone_id; + int instance_id; + float x; + float y; + float z; + float h; +}; + +struct ServerAdventureCount_Struct +{ + uint16 instance_id; +}; + +struct ServerAdventureCountUpdate_Struct +{ + char player[64]; + int count; + int total; +}; + +struct ServerZoneAdventureDataReply_Struct +{ + uint16 instance_id; + int count; + int total; + int type; + int data_id; + int assa_count; + int assa_x; + int assa_y; + int assa_z; + int assa_h; + int dest_x; + int dest_y; + int dest_z; + int dest_h; +}; + +struct ServerAdventureFinish_Struct +{ + char player[64]; + bool win; + int points; + int theme; +}; + +struct ServerLeaderboardRequest_Struct +{ + char player[64]; + uint8 type; +}; + +struct ServerCameraShake_Struct +{ + uint32 duration; // milliseconds + uint32 intensity; // number from 1-10 +}; + +struct ServerMailMessageHeader_Struct { + char from[64]; + char to[64]; + char subject[128]; + char message[0]; +}; + +struct Server_Speech_Struct { + char to[64]; + char from[64]; + uint32 guilddbid; + int16 minstatus; + uint32 type; + char message[0]; +}; + +struct CZClientSignal_Struct { + int charid; + uint32 data; +}; + +struct CZClientSignalByName_Struct { + char Name[64]; + uint32 data; +}; + +struct QSTradeItems_Struct { + uint32 from_id; + uint16 from_slot; + uint32 to_id; + uint16 to_slot; + uint32 item_id; + uint16 charges; + uint32 aug_1; + uint32 aug_2; + uint32 aug_3; + uint32 aug_4; + uint32 aug_5; +}; + +struct QSPlayerLogTrade_Struct { + uint32 char1_id; + MoneyUpdate_Struct char1_money; + uint16 char1_count; + uint32 char2_id; + MoneyUpdate_Struct char2_money; + uint16 char2_count; + QSTradeItems_Struct items[0]; +}; + +struct QSHandinItems_Struct { + char action_type[6]; // handin, return or reward + uint16 char_slot; + uint32 item_id; + uint16 charges; + uint32 aug_1; + uint32 aug_2; + uint32 aug_3; + uint32 aug_4; + uint32 aug_5; +}; + +struct QSPlayerLogHandin_Struct { + uint32 quest_id; + uint32 char_id; + MoneyUpdate_Struct char_money; + uint16 char_count; + uint32 npc_id; + MoneyUpdate_Struct npc_money; + uint16 npc_count; + QSHandinItems_Struct items[0]; +}; + +struct QSPlayerLogNPCKillSub_Struct{ + uint32 NPCID; + uint32 ZoneID; + uint32 Type; +}; + +struct QSPlayerLogNPCKillsPlayers_Struct{ + uint32 char_id; +}; + +struct QSPlayerLogNPCKill_Struct{ + QSPlayerLogNPCKillSub_Struct s1; + QSPlayerLogNPCKillsPlayers_Struct Chars[0]; +}; + +struct QSDeleteItems_Struct { + uint16 char_slot; + uint32 item_id; + uint16 charges; + uint32 aug_1; + uint32 aug_2; + uint32 aug_3; + uint32 aug_4; + uint32 aug_5; +}; + +struct QSPlayerLogDelete_Struct { + uint32 char_id; + uint16 stack_size; // '0' indicates full stack or non-stackable item move + uint16 char_count; + QSDeleteItems_Struct items[0]; +}; + +struct QSMoveItems_Struct { + uint16 from_slot; + uint16 to_slot; + uint32 item_id; + uint16 charges; + uint32 aug_1; + uint32 aug_2; + uint32 aug_3; + uint32 aug_4; + uint32 aug_5; +}; + +struct QSPlayerLogMove_Struct { + uint32 char_id; + uint16 from_slot; + uint16 to_slot; + uint16 stack_size; // '0' indicates full stack or non-stackable item move + uint16 char_count; + bool postaction; + QSMoveItems_Struct items[0]; +}; + +struct QSTransactionItems_Struct { + uint16 char_slot; + uint32 item_id; + uint16 charges; + uint32 aug_1; + uint32 aug_2; + uint32 aug_3; + uint32 aug_4; + uint32 aug_5; +}; + +struct QSMerchantLogTransaction_Struct { + uint32 zone_id; + uint32 merchant_id; + MoneyUpdate_Struct merchant_money; + uint16 merchant_count; + uint32 char_id; + MoneyUpdate_Struct char_money; + uint16 char_count; + QSTransactionItems_Struct items[0]; +}; + +struct CZMessagePlayer_Struct { + uint32 Type; + char CharName[64]; + char Message[512]; +}; + +struct ReloadWorld_Struct{ + uint32 Option; +}; + +#pragma pack() + +#endif diff --git a/common/shareddb.cpp b/common/shareddb.cpp new file mode 100644 index 000000000..7cb361c46 --- /dev/null +++ b/common/shareddb.cpp @@ -0,0 +1,1708 @@ + +#include "shareddb.h" +#include "../common/Item.h" +#include "../common/EMuShareMem.h" +#include "../common/classes.h" +#include "../common/rulesys.h" +#include "../common/seperator.h" +#include "MiscFunctions.h" +#include "eq_packet_structs.h" +#include "guilds.h" +#include "extprofile.h" +#include +#include +#include + +using namespace std; + + +extern LoadEMuShareMemDLL EMuShareMemDLL; + +//hackish mechanism to support callbacks from sharedmem +SharedDatabase *SharedDatabase::s_usedb = NULL; + +SharedDatabase::SharedDatabase() +: Database() +{ + SDBInitVars(); + s_usedb = this; +} + +SharedDatabase::SharedDatabase(const char* host, const char* user, const char* passwd, const char* database, uint32 port) +: Database(host, user, passwd, database, port) +{ + SDBInitVars(); + s_usedb = this; +} + +void SharedDatabase::SDBInitVars() { + max_item = 0; + max_npc_type = 0; + + loottable_max = 0; + lootdrop_max = 0; + max_door_type = 0; + npcfactionlist_max = 0; +} + +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)) { + cerr << "Error in SetGMSpeed query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + 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 + { + + cerr << "Error in GetGMSpeed query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + return 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)) { + cerr << "Error in SetGMSpeed query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + 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); + } + + safe_delete_array(query); + + return EntitledTime; +} + +bool SharedDatabase::SaveCursor(uint32 char_id, list::const_iterator &start, 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=30 or (slotid>=331 and slotid<=340))", char_id), errbuf))) { + for(it=start,i=8000;it!=end;it++,i++) { + ItemInst *inst=*it; + if (!(ret=SaveInventory(char_id,inst,(i==8000) ? 30 : i))) + break; + } + } else { + cout << "Clearing cursor failed: " << errbuf << endl; + } + safe_delete_array(query); + + return ret; +} + +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); + //returning true is less harmful in the face of a query error + 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]); + + uint16 expect_charges = 0; + if(inst->GetCharges() >= 0) + expect_charges = inst->GetCharges(); + else + expect_charges = 0x7FFF; + + if(id == inst->GetItem()->ID && charges == expect_charges) + found = true; + } + mysql_free_result(result); + return(found); +} + +bool SharedDatabase::SaveInventory(uint32 char_id, const ItemInst* inst, int16 slot_id) { + _CP(Database_SaveInventory); + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + bool ret = false; + uint32 augslot[5] = { 0, 0, 0, 0, 0 }; + + //never save tribute slots: + if(slot_id >= 400 && slot_id <= 404) + return(true); + + if (inst && inst->IsType(ItemClassCommon)) { + for(int i=0;i<5;i++) { + ItemInst *auginst=inst->GetItem(i); + augslot[i]=(auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; + } + } + + if (slot_id>=2500 && slot_id<=2600) { // 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); + + 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, 0); + 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, 0); + ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid=%i AND slotid>=%i AND slotid<%i", + char_id, base_slot_id, (base_slot_id+10)), errbuf); + } + + // @merth: need to delete augments here + } + else { + uint16 charges = 0; + if(inst->GetCharges() >= 0) + charges = inst->GetCharges(); + else + charges = 0x7FFF; + // Update/Insert item + uint32 len_query = MakeAnyLenString(&query, + "REPLACE INTO inventory " + " (charid,slotid,itemid,charges,instnodrop,custom_data,color," + " augslot1,augslot2,augslot3,augslot4,augslot5)" + " VALUES(%lu,%lu,%lu,%lu,%lu,'%s',%lu," + " %lu,%lu,%lu,%lu,%lu)", + (unsigned long)char_id, (unsigned long)slot_id, (unsigned long)inst->GetItem()->ID, (unsigned long)charges, + (unsigned long)(inst->IsInstNoDrop() ? 1:0),inst->GetCustomDataString().c_str(),(unsigned long)inst->GetColor(), + (unsigned long)augslot[0],(unsigned long)augslot[1],(unsigned long)augslot[2],(unsigned long)augslot[3],(unsigned long)augslot[4] ); + + ret = RunQuery(query, len_query, errbuf); + } + } + + if (!ret) + LogFile->write(EQEMuLog::Error, "SaveInventory query '%s': %s", query, errbuf); + safe_delete_array(query); + + // Save bag contents, if slot supports bag contents + if (inst && inst->IsType(ItemClassContainer) && Inventory::SupportsContainers(slot_id)) { + for (uint8 idx=0; idx<10; 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 + { + + cerr << "Error in GetSharedPlatinum query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + return 0; +} + +bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET sharedplat = sharedplat + %i WHERE id = %i", amount_to_add, account_id), errbuf)) { + cerr << "Error in SetSharedPlatinum query '" << query << "' " << errbuf << 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) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + 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); + + while((row = mysql_fetch_row(result))) { + int itemid = atoi(row[0]); + int charges = atoi(row[1]); + int slot = atoi(row[2]); + myitem = GetItem(itemid); + if(!myitem) + continue; + ItemInst* myinst = CreateBaseItem(myitem, charges); + if(slot < 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; + + 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 (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[5]; + 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); + + if (item) { + int16 put_slot_id = SLOT_INVALID; + + ItemInst* inst = CreateBaseItem(item, charges); + if (item->ItemClass == ItemClassCommon) { + for(int i=0;i<5;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; + + 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 == SLOT_INVALID) { + 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,NULL,slot_id); + } + } + else { + 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); + } + } + + mysql_free_result(result); + ret = true; + } + else { + LogFile->write(EQEMuLog::Error, "Database::GetSharedBank(uint32 account_id): %s", errbuf); + } + + safe_delete_array(query); + return ret; +} + + +// Overloaded: Retrieve character inventory based on character id +bool SharedDatabase::GetInventory(uint32 char_id, Inventory* inv) { + _CP(Database_GetInventory); + 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)) { + + 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[5]; + 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; + + const Item_Struct* item = GetItem(item_id); + + if (item) { + int16 put_slot_id = SLOT_INVALID; + + ItemInst* inst = CreateBaseItem(item, charges); + + if(row[10]) { + std::string data_str(row[10]); + std::string id; + std::string value; + bool use_id = true; + + for(int i = 0; i < data_str.length(); ++i) { + if(data_str[i] == '^') { + if(!use_id) { + inst->SetCustomData(id, value); + id.clear(); + value.clear(); + } + use_id = !use_id; + } + else { + char v = data_str[i]; + if(use_id) { + id.push_back(v); + } else { + value.push_back(v); + } + } + } + } + + if (instnodrop || (slot_id >= 0 && slot_id <= 21 && 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=0;i<5;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 == SLOT_INVALID) { + LogFile->write(EQEMuLog::Error, + "Warning: Invalid slot_id for item in inventory: charid=%i, item_id=%i, slot_id=%i", + char_id, item_id, slot_id); + } + } + else { + LogFile->write(EQEMuLog::Error, + "Warning: charid %i has an invalid item_id %i in inventory slot %i", + char_id, item_id, slot_id); + } + } + mysql_free_result(result); + + // 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"); + } + + safe_delete_array(query); + return ret; +} + +// Overloaded: Retrieve character inventory based on account_id and character name +bool SharedDatabase::GetInventory(uint32 account_id, char* name, Inventory* inv) { + _CP(Database_GetInventory_name); + 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[5]; + 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 = SLOT_INVALID; + if(!item) + continue; + + ItemInst* inst = CreateBaseItem(item, charges); + inst->SetInstNoDrop(instnodrop); + + if(row[10]) { + std::string data_str(row[10]); + std::string id; + std::string value; + bool use_id = true; + + for(int i = 0; i < data_str.length(); ++i) { + if(data_str[i] == '^') { + if(!use_id) { + inst->SetCustomData(id, value); + id.clear(); + value.clear(); + } + use_id = !use_id; + } + else { + char v = data_str[i]; + if(use_id) { + id.push_back(v); + } else { + value.push_back(v); + } + } + } + } + + if (color > 0) + inst->SetColor(color); + inst->SetCharges(charges); + + if (item->ItemClass == ItemClassCommon) { + for(int i=0;i<5;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 == SLOT_INVALID) { + LogFile->write(EQEMuLog::Error, + "Warning: Invalid slot_id for item in inventory: name=%s, acctid=%i, item_id=%i, slot_id=%i", + name, account_id, item_id, slot_id); + } + } + mysql_free_result(result); + + // Retrieve shared inventory + ret = GetSharedBank(account_id, inv, false); + } + else { + LogFile->write(EQEMuLog::Error, "GetInventory query '%s' %s", query, errbuf); + 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; +} + + +int32 SharedDatabase::GetItemsCount(uint32* oMaxID) { + char errbuf[MYSQL_ERRMSG_SIZE]; + MYSQL_RES *result; + MYSQL_ROW row; + int32 ret = -1; + + char query[] = "SELECT MAX(id),count(*) FROM items"; + if (RunQuery(query, strlen(query), errbuf, &result)) { + row = mysql_fetch_row(result); + if (row != NULL && row[1] != 0) { + ret = atoi(row[1]); + if (oMaxID) { + if (row[0]) + *oMaxID = atoi(row[0]); + else + *oMaxID = 0; + } + } + mysql_free_result(result); + } + else { + cerr << "Error in GetItemsCount query '" << query << "' " << errbuf << endl; + } + + return ret; +} + + +int32 SharedDatabase::GetNPCTypesCount(uint32* oMaxID) { + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + query = new char[256]; + strcpy(query, "SELECT MAX(id), count(*) FROM npc_types"); + if (RunQuery(query, strlen(query), errbuf, &result)) { + safe_delete_array(query); + row = mysql_fetch_row(result); + if (row != NULL && 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 { + cerr << "Error in GetNPCTypesCount query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + + return -1; + } + + return -1; +} + + +bool SharedDatabase::extDBLoadItems(int32 iItemCount, uint32 iMaxItemID) { + return s_usedb->DBLoadItems(iItemCount, iMaxItemID); +} + +bool SharedDatabase::LoadItems() { + if (!EMuShareMemDLL.Load()) + return false; + int32 tmp = 0; + tmp = GetItemsCount(&max_item); + if (tmp == -1) { + cout << "Error: SharedDatabase::LoadItems() (sharemem): GetItemsCount() returned -1" << endl; + return false; + } + bool ret = EMuShareMemDLL.Items.DLLLoadItems(&extDBLoadItems, sizeof(Item_Struct), &tmp, &max_item); + return ret; +} + +// Load all database items into cache +bool SharedDatabase::DBLoadItems(int32 iItemCount, uint32 iMaxItemID) { + _CP(Database_DBLoadItems); + char errbuf[MYSQL_ERRMSG_SIZE]; + MYSQL_RES *result; + MYSQL_ROW row; + bool ret = false; + + LogFile->write(EQEMuLog::Status, "Loading items from database: count=%i", iItemCount); + + // Make sure enough memory was alloc'd in cache + int32 item_count = GetItemsCount(&max_item); + if (item_count != iItemCount) { + LogFile->write(EQEMuLog::Error, "Insufficient shared memory to load items (actual=%i, allocated=%i)", item_count, iItemCount); + return ret; + } + else if (max_item != iMaxItemID) { + LogFile->write(EQEMuLog::Error, "Insufficient shared memory to load items (max item=%i, allocated=%i). Increase MMF_EQMAX_ITEMS define", max_item, iMaxItemID); + return ret; + } + + bool disableNoRent = false; + char ndbuffer[4]; + if(GetVariable("disablenorent", ndbuffer, 4)) { + if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') { + disableNoRent = true; + } + } + bool disableNoDrop = false; + if(GetVariable("disablenodrop", ndbuffer, 4)) { + if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') { + disableNoDrop = true; + } + } + bool disableLoreGroup = false; + if(GetVariable("disablelore", ndbuffer, 4)) { + if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') { + disableLoreGroup = true; + } + } + bool disableNoTransfer = false; + if(GetVariable("disablenotransfer", ndbuffer, 4)) { + if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') { + disableNoTransfer = true; + } + } + + // Retrieve all items from database + char 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))) { +#if EQDEBUG >= 6 + LogFile->write(EQEMuLog::Status, "Loading %s:%i", row[ItemField::name], row[ItemField::id]); +#endif + memset(&item, 0, sizeof(Item_Struct)); + + 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.Unk012 = (int32)atoul(row[ItemField::UNK012]); + //item.Unk013 = (uint32)atoul(row[ItemField::UNK013]); + item.BenefitFlag = (uint32)atoul(row[ItemField::benefitflag]); + 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.Unk123 = (uint32)atoul(row[ItemField::UNK123]); + //item.Unk124 = (uint32)atoul(row[ItemField::UNK124]); + item.Attuneable = (atoi(row[ItemField::attuneable])==0) ? false : true; + item.NoPet = (atoi(row[ItemField::nopet])==0) ? false : true; + //item.Unk127 = (uint32)atoul(row[ItemField::UNK127]); + 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.Unk134 = (uint32)atoul(row[ItemField::UNK134]); + 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]); + + if (!EMuShareMemDLL.Items.cbAddItem(item.ID, &item)) { + LogFile->write(EQEMuLog::Error, "Database::DBLoadItems: Failure reported from EMuShareMemDLL.Items.cbAddItem(%i)", item.ID); + break; + } + } + + mysql_free_result(result); + ret = true; + } + else { + LogFile->write(EQEMuLog::Error, "DBLoadItems query '%s', %s", query, errbuf); + LogFile->write(EQEMuLog::Error, "If you got boat loads of errors, make sure you sourced all sql updates!\n"); + } + return ret; +} + + +const Item_Struct* SharedDatabase::GetItem(uint32 id) { + return EMuShareMemDLL.Items.GetItem(id); +} + +const Item_Struct* SharedDatabase::IterateItems(uint32* NextIndex) { + return EMuShareMemDLL.Items.IterateItems(NextIndex); +} + + +string SharedDatabase::GetBook(const char *txtfile) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + char txtfile2[20]; + string txtout; + strcpy(txtfile2,txtfile); + if (!RunQuery(query, MakeAnyLenString(&query, "SELECT txtfile FROM books where name='%s'", txtfile2), errbuf, &result)) { + cerr << "Error in GetBook query '" << query << "' " << errbuf << endl; + if (query != 0) + safe_delete_array(query); + 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; + } + } +} + + +bool SharedDatabase::extDBLoadNPCFactionLists(int32 iNPCFactionListCount, uint32 iMaxNPCFactionListID) { + return s_usedb->DBLoadNPCFactionLists(iNPCFactionListCount, iMaxNPCFactionListID); +} + +const NPCFactionList* SharedDatabase::GetNPCFactionEntry(uint32 id) { + return EMuShareMemDLL.NPCFactionList.GetNPCFactionList(id); +} + +bool SharedDatabase::LoadNPCFactionLists() { + if (!EMuShareMemDLL.Load()) + return false; + int32 tmp = -1; + uint32 tmp_npcfactionlist_max; + tmp = GetNPCFactionListsCount(&tmp_npcfactionlist_max); + if (tmp < 0) { + cout << "Error: SharedDatabase::LoadNPCFactionLists-ShareMem: GetNPCFactionListsCount() returned < 0" << endl; + return false; + } + npcfactionlist_max = tmp_npcfactionlist_max; + bool ret = EMuShareMemDLL.NPCFactionList.DLLLoadNPCFactionLists(&extDBLoadNPCFactionLists, sizeof(NPCFactionList), &tmp, &npcfactionlist_max, MAX_NPC_FACTIONS); + return ret; +} + +bool SharedDatabase::DBLoadNPCFactionLists(int32 iNPCFactionListCount, uint32 iMaxNPCFactionListID) { + _CP(Database_DBLoadNPCFactionLists); + LogFile->write(EQEMuLog::Status, "Loading NPC Faction Lists from database..."); + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + query = new char[256]; + strcpy(query, "SELECT MAX(id), Count(*) FROM npc_faction"); + if (RunQuery(query, strlen(query), errbuf, &result)) { + safe_delete_array(query); + row = mysql_fetch_row(result); + if (row && row[0]) { + if ((uint32)atoi(row[0]) > iMaxNPCFactionListID) { + cout << "Error: Insufficient shared memory to load NPC Faction Lists." << endl; + cout << "Max(id): " << atoi(row[0]) << ", iMaxNPCFactionListID: " << iMaxNPCFactionListID << endl; + cout << "Fix this by increasing the MMF_MAX_NPCFactionList_ID define statement" << endl; + mysql_free_result(result); + return false; + } + if (atoi(row[1]) != iNPCFactionListCount) { + cout << "Error: number of NPCFactionLists in memshare doesnt match database." << endl; + cout << "Count(*): " << atoi(row[1]) << ", iNPCFactionListCount: " << iNPCFactionListCount << endl; + mysql_free_result(result); + return false; + } + npcfactionlist_max = atoi(row[0]); + mysql_free_result(result); + NPCFactionList tmpnfl; + if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, primaryfaction, ignore_primary_assist from npc_faction"), errbuf, &result)) { + safe_delete_array(query); + while((row = mysql_fetch_row(result))) { + memset(&tmpnfl, 0, sizeof(NPCFactionList)); + tmpnfl.id = atoi(row[0]); + tmpnfl.primaryfaction = atoi(row[1]); + //if we have ignore_primary_assist set to non-zero then we will not assist our own faction + //else we will assist (this is the default) + tmpnfl.assistprimaryfaction = (atoi(row[2]) == 0) ? true : false; + if (!EMuShareMemDLL.NPCFactionList.cbAddNPCFactionList(tmpnfl.id, &tmpnfl)) { + mysql_free_result(result); + cout << "Error: SharedDatabase::DBLoadNPCFactionLists: !EMuShareMemDLL.NPCFactionList.cbAddNPCFactionList" << endl; + return false; + } + + Sleep(0); + } + mysql_free_result(result); + } + else { + cerr << "Error in DBLoadNPCFactionLists query2 '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + if (RunQuery(query, MakeAnyLenString(&query, "SELECT npc_faction_id, faction_id, value, npc_value, temp FROM npc_faction_entries order by npc_faction_id"), errbuf, &result)) { + safe_delete_array(query); + int8 i = 0; + uint32 curflid = 0; + uint32 tmpflid = 0; + uint32 tmpfactionid[MAX_NPC_FACTIONS]; + int32 tmpfactionvalue[MAX_NPC_FACTIONS]; + int8 tmpfactionnpcvalue[MAX_NPC_FACTIONS]; + uint8 tmpfactiontemp[MAX_NPC_FACTIONS]; + + memset(tmpfactionid, 0, sizeof(tmpfactionid)); + memset(tmpfactionvalue, 0, sizeof(tmpfactionvalue)); + memset(tmpfactionnpcvalue, 0, sizeof(tmpfactionnpcvalue)); + memset(tmpfactiontemp, 0, sizeof(tmpfactiontemp)); + + while((row = mysql_fetch_row(result))) { + tmpflid = atoi(row[0]); + if (curflid != tmpflid && curflid != 0) { + if (!EMuShareMemDLL.NPCFactionList.cbSetFaction(curflid, tmpfactionid, tmpfactionvalue, tmpfactionnpcvalue, tmpfactiontemp)) { + mysql_free_result(result); + cout << "Error: SharedDatabase::DBLoadNPCFactionLists: !EMuShareMemDLL.NPCFactionList.cbSetFaction" << endl; + return false; + } + memset(tmpfactionid, 0, sizeof(tmpfactionid)); + memset(tmpfactionvalue, 0, sizeof(tmpfactionvalue)); + memset(tmpfactionnpcvalue, 0, sizeof(tmpfactionnpcvalue)); + memset(tmpfactiontemp, 0, sizeof(tmpfactiontemp)); + i = 0; + } + curflid = tmpflid; + tmpfactionid[i] = atoi(row[1]); + tmpfactionvalue[i] = atoi(row[2]); + tmpfactionnpcvalue[i] = atoi(row[3]); + tmpfactiontemp[i] = atoi(row[4]); + i++; + if (i >= MAX_NPC_FACTIONS) { + cerr << "Error in DBLoadNPCFactionLists: More than MAX_NPC_FACTIONS factions returned, flid=" << tmpflid << endl; + break; + } + Sleep(0); + } + if (tmpflid) { + EMuShareMemDLL.NPCFactionList.cbSetFaction(curflid, tmpfactionid, tmpfactionvalue, tmpfactionnpcvalue, tmpfactiontemp); + } + + mysql_free_result(result); + } + else { + cerr << "Error in DBLoadNPCFactionLists query3 '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + } + else { + mysql_free_result(result); + //return false; + } + } + else { + cerr << "Error in DBLoadNPCFactionLists query1 '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + 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) { + _CP(Database_GetPlayerProfile); + 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) { + _CP(Database_SetPlayerProfile); + 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) +{ + const Item_Struct* item = NULL; + ItemInst* inst = NULL; + item = GetItem(item_id); + if (item) { + inst = CreateBaseItem(item, charges); + inst->PutAugment(this, 0, aug1); + inst->PutAugment(this, 1, aug2); + inst->PutAugment(this, 2, aug3); + inst->PutAugment(this, 3, aug4); + inst->PutAugment(this, 4, aug5); + } + + return inst; +} + + +// Create appropriate ItemInst class +ItemInst* SharedDatabase::CreateItem(const Item_Struct* item, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5) +{ + ItemInst* inst = NULL; + if (item) { + inst = CreateBaseItem(item, charges); + inst->PutAugment(this, 0, aug1); + inst->PutAugment(this, 1, aug2); + inst->PutAugment(this, 2, aug3); + inst->PutAugment(this, 3, aug4); + inst->PutAugment(this, 4, aug5); + } + + return inst; +} + +ItemInst* SharedDatabase::CreateBaseItem(const Item_Struct* item, int16 charges) { + ItemInst* inst = NULL; + if (item) { + // if maxcharges is -1 that means it is an unlimited use item. + // set it to 1 charge so that it is usable on creation + if (charges == 0 && item->MaxCharges == -1) + charges = 1; + + if(item->CharmFileID != 0 || (item->LoreGroup >= 1000 && item->LoreGroup != -1)) { + inst = new EvoItemInst(item, charges); + ((EvoItemInst*)inst)->Initialize(this); + } + else + inst = new ItemInst(item, charges); + } + return inst; +} + +int32 SharedDatabase::DeleteStalePlayerCorpses() { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + + if(RuleB(Zone, EnableShadowrest)) + { + if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE player_corpses SET IsBurried = 1 WHERE IsBurried=0 and " + "(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > %d and not timeofdeath=0", + (RuleI(Character, CorpseDecayTimeMS) / 1000)), errbuf, 0, &affected_rows)) + { + safe_delete_array(query); + 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; + } + } + + 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); + return -1; + } + safe_delete_array(query); + + return affected_rows; +} + +int32 SharedDatabase::GetNPCFactionListsCount(uint32* oMaxID) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + query = new char[256]; + strcpy(query, "SELECT MAX(id), count(*) FROM npc_faction"); + if (RunQuery(query, strlen(query), errbuf, &result)) { + safe_delete_array(query); + row = mysql_fetch_row(result); + if (row != NULL && 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; + } + } + else { + cerr << "Error in GetNPCFactionListsCount query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return -1; + } + + return -1; +} + +bool SharedDatabase::GetCommandSettings(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 { + cerr << "Error in GetCommands query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + return false; +} + +bool SharedDatabase::extDBLoadSkillCaps() { + return s_usedb->DBLoadSkillCaps(); +} + +bool SharedDatabase::LoadSkillCaps() { + if (!EMuShareMemDLL.Load()) + return false; + + uint8 class_count = PLAYER_CLASS_COUNT; + uint8 skill_count = HIGHEST_SKILL+1; + uint8 level_count = HARD_LEVEL_CAP+1; + + return EMuShareMemDLL.SkillCaps.LoadSkillCaps(&extDBLoadSkillCaps, + sizeof(uint16), class_count, skill_count, level_count); +} + +bool SharedDatabase::DBLoadSkillCaps() { + LogFile->write(EQEMuLog::Status, "Loading skill caps from database..."); + + uint8 class_count = PLAYER_CLASS_COUNT; + uint8 skill_count = HIGHEST_SKILL+1; + uint8 level_count = HARD_LEVEL_CAP+1; + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + if (RunQuery(query, MakeAnyLenString(&query, + "SELECT skillID,class,level,cap FROM skill_caps ORDER BY skillID,class,level"), + errbuf, &result)) { + safe_delete_array(query); + + while ((row = mysql_fetch_row(result))) { + uint8 skillID = atoi(row[0]); + uint8 class_ = atoi(row[1])-1; //classes are base 1... + uint8 level = atoi(row[2]); + uint16 cap = atoi(row[3]); + if(skillID >= skill_count || class_ >= class_count || level >= level_count) + continue; + EMuShareMemDLL.SkillCaps.SetSkillCap(class_, skillID, level, cap); + } + mysql_free_result(result); + } + else { + cerr << "Error in DBLoadSkillCaps (memshare) #2 query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + return true; +} + +uint16 SharedDatabase::GetSkillCap(uint8 Class_, SkillType Skill, uint8 Level) { + if(Class_ == 0) + return(0); + int SkillMaxLevel = RuleI(Character, SkillCapMaxLevel); + if (SkillMaxLevel < 1) { + SkillMaxLevel = RuleI(Character, MaxLevel); + } + if(Level > SkillMaxLevel){ + return EMuShareMemDLL.SkillCaps.GetSkillCap(Class_-1, Skill, SkillMaxLevel); + } + else{ + return EMuShareMemDLL.SkillCaps.GetSkillCap(Class_-1, Skill, Level); + } +} + +uint8 SharedDatabase::GetTrainLevel(uint8 Class_, SkillType Skill, uint8 Level) { + if(Class_ == 0) + return(0); + + uint8 ret = 0; + int SkillMaxLevel = RuleI(Character, SkillCapMaxLevel); + if (SkillMaxLevel < 1) { + SkillMaxLevel = RuleI(Character, MaxLevel); + } + if(Level > SkillMaxLevel) { + ret = EMuShareMemDLL.SkillCaps.GetTrainLevel(Class_-1, Skill, SkillMaxLevel); + } + else + { + ret = EMuShareMemDLL.SkillCaps.GetTrainLevel(Class_-1, Skill, Level); + } + if(ret > GetSkillCap(Class_, Skill, Level)) + ret = GetSkillCap(Class_, Skill, Level); + + return ret; +} + +void SharedDatabase::DBLoadDamageShieldTypes(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 DBLoadDamageShieldTypes: %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); + } + else { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); + safe_delete_array(query); + } +} + +const EvolveInfo* SharedDatabase::GetEvolveInfo(uint32 loregroup) { + return NULL; // nothing here for now... database and/or sharemem pulls later +} + +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 { + cerr << "Error in GetPlayerInspectMessage query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } +} + +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)) { + cerr << "Error in SetPlayerInspectMessage query '" << query << "' " << errbuf << endl; + } + + safe_delete_array(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 { + cerr << "Error in GetBotInspectMessage query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } +} + +void SharedDatabase::SetBotInspectMessage(uint32 botid, const InspectMessage_Struct* message) { + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE bots SET BotInspectMessage='%s' WHERE BotID=%i", message->text, botid), errbuf)) { + cerr << "Error in SetBotInspectMessage query '" << query << "' " << errbuf << endl; + } + + safe_delete_array(query); +} diff --git a/common/shareddb.h b/common/shareddb.h new file mode 100644 index 000000000..3d38fbcec --- /dev/null +++ b/common/shareddb.h @@ -0,0 +1,154 @@ +#ifndef SHAREDDB_H_ +#define SHAREDDB_H_ + +#define MAX_ITEM_ID 200000 + +#include "database.h" +#include "skills.h" +#include "../zone/spdat.h" +#include "Item.h" + +#include + + +struct Item_Struct; +struct NPCFactionList; +struct Faction; +struct LootTable_Struct; +struct LootDrop_Struct; + +/* + * This object is inherited by world and zone's DB object, + * and is mainly here to facilitate shared memory, and other + * things which only world and zone need. + */ +class SharedDatabase : public Database { +public: + SharedDatabase(); + SharedDatabase(const char* host, const char* user, const char* passwd, const char* database,uint32 port); + virtual ~SharedDatabase(); + + /* + * General Character Related Stuff + */ + 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 GetBotInspectMessage(uint32 botid, InspectMessage_Struct* message); + void SetBotInspectMessage(uint32 botid, const InspectMessage_Struct* message); + + uint32 GetTotalTimeEntitledOnAccount(uint32 AccountID); + + /* + * Character Inventory + */ + 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 VerifyInventory(uint32 account_id, int16 slot_id, const ItemInst* inst); + bool GetSharedBank(uint32 id, Inventory* inv, bool is_charid); + int32 GetSharedPlatinum(uint32 account_id); + bool SetSharedPlatinum(uint32 account_id, int32 amount_to_add); + bool GetInventory(uint32 char_id, Inventory* inv); + bool GetInventory(uint32 account_id, char* name, Inventory* inv); + bool SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, uint32 si_race, uint32 si_class, uint32 si_deity, uint32 si_current_zone, char* si_name, int admin); + + + string GetBook(const char *txtfile); + + /* + * Item Methods + */ + ItemInst* CreateItem(uint32 item_id, int16 charges=0, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0); + ItemInst* CreateItem(const Item_Struct* item, int16 charges=0, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0); + ItemInst* CreateBaseItem(const Item_Struct* item, int16 charges=0); + + /* + * Shared Memory crap + */ + inline const uint32 GetMaxItem() { return max_item; } + inline const uint32 GetMaxLootTableID() { return loottable_max; } + inline const uint32 GetMaxLootDropID() { return lootdrop_max; } + inline const uint32 GetMaxNPCType() { return max_npc_type; } + inline const uint32 GetMaxNPCFactionList() { return npcfactionlist_max; } + const Item_Struct* GetItem(uint32 id); + const EvolveInfo* GetEvolveInfo(uint32 loregroup); + const NPCFactionList* GetNPCFactionEntry(uint32 id); + uint16 GetSkillCap(uint8 Class_, SkillType Skill, uint8 Level); + uint8 GetTrainLevel(uint8 Class_, SkillType Skill, uint8 Level); +// const Door* GetDoor(uint8 door_id, const char* zone_name); +// const Door* GetDoorDBID(uint32 db_id); + const LootTable_Struct* GetLootTable(uint32 loottable_id); + const LootDrop_Struct* GetLootDrop(uint32 lootdrop_id); + bool LoadItems(); + bool LoadLoot(); + bool LoadNPCFactionLists(); + bool LoadSkillCaps(); + bool GetCommandSettings(map &commands); + const Item_Struct* IterateItems(uint32* NextIndex); + bool DBLoadItems(int32 iItemCount, uint32 iMaxItemID); + bool DBLoadNPCTypes(int32 iNPCTypeCount, uint32 iMaxNPCTypeID); + bool DBLoadNPCFactionLists(int32 iNPCFactionListCount, uint32 iMaxNPCFactionListID); + bool DBLoadLoot(); + bool DBLoadSkillCaps(); + void DBLoadDamageShieldTypes(SPDat_Spell_Struct* sp, int32 iMaxSpellID); + + +protected: + void SDBInitVars(); + + /* + * Private shared mem stuff + */ + int32 GetItemsCount(uint32* oMaxID = 0); + int32 GetNPCTypesCount(uint32* oMaxID = 0); + int32 GetNPCFactionListsCount(uint32* oMaxID = 0); + static bool extDBLoadItems(int32 iItemCount, uint32 iMaxItemID); + static bool extDBLoadNPCFactionLists(int32 iNPCFactionListCount, uint32 iMaxNPCFactionListID); + static bool extDBLoadLoot(); + static bool extDBLoadSkillCaps(); + + + uint32 max_item; + uint32 max_npc_type; + uint32 max_door_type; + uint32 npcfactionlist_max; + uint32 loottable_max; + uint32 lootdrop_max; + + uint32 npc_spells_maxid; + +private: + static SharedDatabase *s_usedb; +}; + + + + + +#endif /*SHAREDDB_H_*/ + + + + + + + + + + + + + + + + + + + diff --git a/common/skills.h b/common/skills.h new file mode 100644 index 000000000..43b8227bc --- /dev/null +++ b/common/skills.h @@ -0,0 +1,104 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2002 EQEMu Development Team (http://eqemulator.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 SKILLS_H +#define SKILLS_H + +// Correct Skill Numbers as of 4-14-2002 +typedef enum { + _1H_BLUNT = 0, + _1H_SLASHING = 1, + _2H_BLUNT = 2, + _2H_SLASHING = 3, + ABJURE = 4, + ALTERATION = 5, + APPLY_POISON = 6, + ARCHERY = 7, + BACKSTAB = 8, + BIND_WOUND = 9, + BASH = 10, + BLOCKSKILL = 11, + BRASS_INSTRUMENTS = 12, + CHANNELING = 13, + CONJURATION = 14, + DEFENSE = 15, + DISARM = 16, + DISARM_TRAPS = 17, + DIVINATION = 18, + DODGE = 19, + DOUBLE_ATTACK = 20, + DRAGON_PUNCH = 21 , //aka Tail Rake + DUAL_WIELD = 22, + EAGLE_STRIKE = 23, + EVOCATION = 24, + FEIGN_DEATH = 25, + FLYING_KICK = 26, + FORAGE = 27, + HAND_TO_HAND = 28, + HIDE = 29, + KICK = 30, + MEDITATE = 31, + MEND = 32, + OFFENSE = 33, + PARRY = 34, + PICK_LOCK = 35, + PIERCING = 36, + RIPOSTE = 37, + ROUND_KICK = 38, + SAFE_FALL = 39, + SENSE_HEADING = 40, + SINGING = 41, + SNEAK = 42, + SPECIALIZE_ABJURE = 43, + SPECIALIZE_ALTERATION = 44, + SPECIALIZE_CONJURATION = 45, + SPECIALIZE_DIVINATION = 46, + SPECIALIZE_EVOCATION = 47, + PICK_POCKETS = 48, + STRINGED_INSTRUMENTS = 49, + SWIMMING = 50, + THROWING = 51, + TIGER_CLAW = 52, + TRACKING = 53, + WIND_INSTRUMENTS = 54, + FISHING = 55, + MAKE_POISON = 56, + TINKERING = 57, + RESEARCH = 58, + ALCHEMY = 59, + BAKING = 60, + TAILORING = 61, + SENSE_TRAPS = 62, + BLACKSMITHING = 63, + FLETCHING = 64, + BREWING = 65, + ALCOHOL_TOLERANCE = 66, + BEGGING = 67, + JEWELRY_MAKING = 68, + POTTERY = 69, + PERCUSSION_INSTRUMENTS = 70, + INTIMIDATION = 71, + BERSERKING = 72, + TAUNT = 73, + FRENZY = 74, + GENERIC_TRADESKILL = 75 +} SkillType; + +#define HIGHEST_SKILL FRENZY + +#endif + diff --git a/common/timeoutmgr.cpp b/common/timeoutmgr.cpp new file mode 100644 index 000000000..a29041823 --- /dev/null +++ b/common/timeoutmgr.cpp @@ -0,0 +1,82 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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" + +//#define TIMEOUT_DEBUG + +#include "timeoutmgr.h" + +Timeoutable::Timeoutable(uint32 check_frequency) + : next_check(check_frequency) +{ + timeout_manager.AddMember(this); +} + +Timeoutable::~Timeoutable() { + timeout_manager.DeleteMember(this); +} + + +TimeoutManager::TimeoutManager() { +} + +void TimeoutManager::CheckTimeouts() { + vector::iterator cur,end; + cur = members.begin(); + end = members.end(); + for(; cur != end; cur++) { + Timeoutable *it = *cur; + if(it->next_check.Check()) { +#ifdef TIMEOUT_DEBUG + LogFile->write(EQEMuLog::Debug, "Checking timeout on 0x%x\n", it); +#endif + it->CheckTimeout(); + } + } +} + +//methods called by Timeoutable objects: +void TimeoutManager::AddMember(Timeoutable *who) { + if(who == NULL) + return; + + DeleteMember(who); //just in case... prolly not needed. + members.push_back(who); +#ifdef TIMEOUT_DEBUG + LogFile->write(EQEMuLog::Debug, "Adding timeoutable 0x%x\n", who); +#endif +} + +void TimeoutManager::DeleteMember(Timeoutable *who) { +#ifdef TIMEOUT_DEBUG + LogFile->write(EQEMuLog::Debug, "Removing timeoutable 0x%x\n", who); +#endif + vector::iterator cur,end; + cur = members.begin(); + end = members.end(); + for(; cur != end; cur++) { + if(*cur == who) { + members.erase(cur); + return; + } + } +} + + + + diff --git a/common/timeoutmgr.h b/common/timeoutmgr.h new file mode 100644 index 000000000..ac968f5b3 --- /dev/null +++ b/common/timeoutmgr.h @@ -0,0 +1,67 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 TIMEOUT_MANAGER_H +#define TIMEOUT_MANAGER_H + +//ms between checking all timeouts +//timeouts are generally somewhat large, so its safe to use a +//value on the order of seconds here. +#define TIMEOUT_GRANULARITY 1000 + +#include "types.h" +#include "timer.h" + +#include +using namespace std; + +//timeoutable objects automatically register themselves +//with the global TimeoutManager object +class TimeoutManager; +class Timeoutable { + friend class TimeoutManager; +public: + //this frequency should generally be a multiple of TIMEOUT_GRANULARITY + Timeoutable(uint32 check_frequency); + virtual ~Timeoutable(); + + virtual void CheckTimeout() = 0; + +private: + //accessed directly by TimeoutManager + Timer next_check; +}; + +class TimeoutManager { + friend class Timeoutable; +public: + TimeoutManager(); + + void CheckTimeouts(); + +protected: + + //methods called by Timeoutable objects: + void AddMember(Timeoutable *who); + void DeleteMember(Timeoutable *who); + + vector members; +}; + +extern TimeoutManager timeout_manager; + +#endif diff --git a/common/timer.cpp b/common/timer.cpp new file mode 100644 index 000000000..c75f44b51 --- /dev/null +++ b/common/timer.cpp @@ -0,0 +1,194 @@ +/* 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 "../common/debug.h" +// Disgrace: for windows compile +#ifndef WIN32 + #include +#else + #include +#endif + +#include +using namespace std; + +#include "timer.h" + +uint32 current_time = 0; +uint32 last_time = 0; + +Timer::Timer(uint32 in_timer_time, bool iUseAcurateTiming) { + timer_time = in_timer_time; + start_time = current_time; + set_at_trigger = timer_time; + pUseAcurateTiming = iUseAcurateTiming; + if (timer_time == 0) { + enabled = false; + } + else { + enabled = true; + } +} + +Timer::Timer(uint32 start, uint32 timer, bool iUseAcurateTiming = false) { + timer_time = timer; + start_time = start; + set_at_trigger = timer_time; + pUseAcurateTiming = iUseAcurateTiming; + if (timer_time == 0) { + enabled = false; + } + else { + enabled = true; + } +} + +/* Reimplemented for MSVC - Bounce */ +#ifdef _WINDOWS +int gettimeofday (timeval *tp, ...) +{ + timeb tb; + + ftime (&tb); + + tp->tv_sec = tb.time; + tp->tv_usec = tb.millitm * 1000; + + return 0; +} +#endif + +/* This function checks if the timer triggered */ +bool Timer::Check(bool iReset) +{ + _CP(Timer_Check); + if (this==0) { + cerr << "Null timer during ->Check()!?\n"; + return true; + } +// if (!current_time || !start_time || !timer_time) {cerr << "Timer::Check on a timer that does not have a vital member defined."; +// return true;} + if (enabled && current_time-start_time > timer_time) { + if (iReset) { + if (pUseAcurateTiming) + start_time += timer_time; + else + start_time = current_time; // Reset timer + timer_time = set_at_trigger; + } + return true; + } + + return false; +} + +/* This function disables the timer */ +void Timer::Disable() { + enabled = false; +} + +void Timer::Enable() { + enabled = true; +} + +/* This function set the timer and restart it */ +void Timer::Start(uint32 set_timer_time, bool ChangeResetTimer) { + start_time = current_time; + enabled = true; + if (set_timer_time != 0) + { + timer_time = set_timer_time; + if (ChangeResetTimer) + set_at_trigger = set_timer_time; + } +} + +/* This timer updates the timer without restarting it */ +void Timer::SetTimer(uint32 set_timer_time) { + /* If we were disabled before => restart the timer */ + if (!enabled) { + start_time = current_time; + enabled = true; + } + if (set_timer_time != 0) { + timer_time = set_timer_time; + set_at_trigger = set_timer_time; + } +} + +uint32 Timer::GetRemainingTime() { + if (enabled) { + if (current_time-start_time > timer_time) + return 0; + else + return (start_time + timer_time) - current_time; + } + else { + return 0xFFFFFFFF; + } +} + +void Timer::SetAtTrigger(uint32 in_set_at_trigger, bool iEnableIfDisabled) { + set_at_trigger = in_set_at_trigger; + if (!Enabled() && iEnableIfDisabled) { + Enable(); + } +} + +void Timer::Trigger() +{ + enabled = true; + + timer_time = set_at_trigger; + start_time = current_time-timer_time-1; +} + +const uint32 Timer::GetCurrentTime() +{ + return current_time; +} + +//just to keep all time related crap in one place... not really related to timers. +const uint32 Timer::GetTimeSeconds() { + struct timeval read_time; + + gettimeofday(&read_time,0); + return(read_time.tv_sec); +} + +const uint32 Timer::SetCurrentTime() +{ + struct timeval read_time; + uint32 this_time; + + gettimeofday(&read_time,0); + this_time = read_time.tv_sec * 1000 + read_time.tv_usec / 1000; + + if (last_time == 0) + { + current_time = 0; + } + else + { + current_time += this_time - last_time; + } + + last_time = this_time; + +// cerr << "Current time:" << current_time << endl; + return current_time; +} diff --git a/common/timer.h b/common/timer.h new file mode 100644 index 000000000..b9f489d31 --- /dev/null +++ b/common/timer.h @@ -0,0 +1,70 @@ +/* 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 TIMER_H +#define TIMER_H + +#include "types.h" + +// Disgrace: for windows compile +#ifdef _WINDOWS + #include "debug.h" + int gettimeofday (timeval *tp, ...); +#endif + +class Timer +{ +public: + Timer(uint32 timer_time, bool iUseAcurateTiming = false); + Timer(uint32 start, uint32 timer, bool iUseAcurateTiming); + ~Timer() { } + + bool Check(bool iReset = true); + void Enable(); + void Disable(); + void Start(uint32 set_timer_time=0, bool ChangeResetTimer = true); + void SetTimer(uint32 set_timer_time=0); + uint32 GetRemainingTime(); + inline const uint32& GetTimerTime() { return timer_time; } + inline const uint32& GetSetAtTrigger() { return set_at_trigger; } + void Trigger(); + void SetAtTrigger(uint32 set_at_trigger, bool iEnableIfDisabled = false); + + inline bool Enabled() { return enabled; } + inline uint32 GetStartTime() { return(start_time); } + inline uint32 GetDuration() { return(timer_time); } + + static const uint32 SetCurrentTime(); + static const uint32 GetCurrentTime(); + static const uint32 GetTimeSeconds(); + +private: + uint32 start_time; + uint32 timer_time; + bool enabled; + uint32 set_at_trigger; + + // Tells the timer to be more acurate about happening every X ms. + // Instead of Check() setting the start_time = now, + // it it sets it to start_time += timer_time + bool pUseAcurateTiming; + +// static uint32 current_time; +// static uint32 last_time; +}; + +#endif diff --git a/common/tinyxml/tinystr.cpp b/common/tinyxml/tinystr.cpp new file mode 100644 index 000000000..413f5c344 --- /dev/null +++ b/common/tinyxml/tinystr.cpp @@ -0,0 +1,318 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml.h" + +#ifndef TIXML_USE_STL + + +#include +#include +#include + +#include "tinystr.h" + +// TiXmlString constructor, based on a C string +TiXmlString::TiXmlString (const char* instring) +{ + size_t newlen; + char * newstring; + + if (!instring) + { + allocated = 0; + cstring = NULL; + current_length = 0; + return; + } + newlen = strlen (instring) + 1; + newstring = new char [newlen]; + memcpy (newstring, instring, newlen); + // strcpy (newstring, instring); + allocated = newlen; + cstring = newstring; + current_length = newlen - 1; +} + +// TiXmlString copy constructor +TiXmlString::TiXmlString (const TiXmlString& copy) +{ + size_t newlen; + char * newstring; + + // Prevent copy to self! + if ( © == this ) + return; + + if (! copy . allocated) + { + allocated = 0; + cstring = NULL; + current_length = 0; + return; + } + newlen = copy . length () + 1; + newstring = new char [newlen]; + // strcpy (newstring, copy . cstring); + memcpy (newstring, copy . cstring, newlen); + allocated = newlen; + cstring = newstring; + current_length = newlen - 1; +} + +// TiXmlString = operator. Safe when assign own content +void TiXmlString ::operator = (const char * content) +{ + size_t newlen; + char * newstring; + + if (! content) + { + empty_it (); + return; + } + newlen = strlen (content) + 1; + newstring = new char [newlen]; + // strcpy (newstring, content); + memcpy (newstring, content, newlen); + empty_it (); + allocated = newlen; + cstring = newstring; + current_length = newlen - 1; +} + +// = operator. Safe when assign own content +void TiXmlString ::operator = (const TiXmlString & copy) +{ + size_t newlen; + char * newstring; + + if (! copy . length ()) + { + empty_it (); + return; + } + newlen = copy . length () + 1; + newstring = new char [newlen]; + // strcpy (newstring, copy . c_str ()); + memcpy (newstring, copy . c_str (), newlen); + empty_it (); + allocated = newlen; + cstring = newstring; + current_length = newlen - 1; +} + + +// append a const char * to an existing TiXmlString +void TiXmlString::append( const char* str, size_t len ) +{ + char * new_string; + size_t new_alloc, new_size, size_suffix; + + // don't use strlen - it can overrun the len passed in! + const char* p = str; + size_suffix = 0; + + while ( *p && size_suffix < (unsigned)len ) + { + ++p; + ++size_suffix; + } + if ( !size_suffix) + return; + + new_size = length () + size_suffix + 1; + // check if we need to expand + if (new_size > allocated) + { + // compute new size + new_alloc = assign_new_size (new_size); + + // allocate new buffer + new_string = new char [new_alloc]; + new_string [0] = 0; + + // copy the previous allocated buffer into this one + if (allocated && cstring) + // strcpy (new_string, cstring); + memcpy (new_string, cstring, length ()); + + // append the suffix. It does exist, otherwize we wouldn't be expanding + // strncat (new_string, str, len); + memcpy (new_string + length (), + str, + size_suffix); + + // return previsously allocated buffer if any + if (allocated && cstring) + delete [] cstring; + + // update member variables + cstring = new_string; + allocated = new_alloc; + } + else + { + // we know we can safely append the new string + // strncat (cstring, str, len); + memcpy (cstring + length (), + str, + size_suffix); + } + current_length = new_size - 1; + cstring [current_length] = 0; +} + + +// append a const char * to an existing TiXmlString +void TiXmlString::append( const char * suffix ) +{ + char * new_string; + size_t new_alloc, new_size; + + new_size = length () + strlen (suffix) + 1; + // check if we need to expand + if (new_size > allocated) + { + // compute new size + new_alloc = assign_new_size (new_size); + + // allocate new buffer + new_string = new char [new_alloc]; + new_string [0] = 0; + + // copy the previous allocated buffer into this one + if (allocated && cstring) + memcpy (new_string, cstring, 1 + length ()); + // strcpy (new_string, cstring); + + // append the suffix. It does exist, otherwize we wouldn't be expanding + // strcat (new_string, suffix); + memcpy (new_string + length (), + suffix, + strlen (suffix) + 1); + + // return previsously allocated buffer if any + if (allocated && cstring) + delete [] cstring; + + // update member variables + cstring = new_string; + allocated = new_alloc; + } + else + { + // we know we can safely append the new string + // strcat (cstring, suffix); + memcpy (cstring + length (), + suffix, + strlen (suffix) + 1); + } + current_length = new_size - 1; +} + +// Check for TiXmlString equuivalence +//bool TiXmlString::operator == (const TiXmlString & compare) const +//{ +// return (! strcmp (c_str (), compare . c_str ())); +//} + +//unsigned TiXmlString::length () const +//{ +// if (allocated) +// // return strlen (cstring); +// return current_length; +// return 0; +//} + + +unsigned TiXmlString::find (char tofind, unsigned offset) const +{ + char * lookup; + + if (offset >= length ()) + return (unsigned) notfound; + for (lookup = cstring + offset; * lookup; lookup++) + if (* lookup == tofind) + return (unsigned)(lookup - cstring); + return (unsigned) notfound; +} + + +bool TiXmlString::operator == (const TiXmlString & compare) const +{ + if ( allocated && compare.allocated ) + { + assert( cstring ); + assert( compare.cstring ); + return ( strcmp( cstring, compare.cstring ) == 0 ); + } + else if ( length() == 0 && compare.length() == 0 ) + { + return true; + } + return false; +} + + +bool TiXmlString::operator == (const char* compare) const +{ + if ( allocated && compare && *compare ) + { + assert( cstring ); + return ( strcmp( cstring, compare ) == 0 ); + } + else if ( length() == 0 && (!compare || !*compare ) ) // this is a little dubious, but try to duplicate behavior in other operator== + { + return true; + } + return false; +} + + +bool TiXmlString::operator < (const TiXmlString & compare) const +{ + if ( allocated && compare.allocated ) + { + assert( cstring ); + assert( compare.cstring ); + return ( strcmp( cstring, compare.cstring ) > 0 ); + } + return false; +} + + +bool TiXmlString::operator > (const TiXmlString & compare) const +{ + if ( allocated && compare.allocated ) + { + assert( cstring ); + assert( compare.cstring ); + return ( strcmp( cstring, compare.cstring ) < 0 ); + } + return false; +} + + +#endif // TIXML_USE_STL diff --git a/common/tinyxml/tinystr.h b/common/tinyxml/tinystr.h new file mode 100644 index 000000000..39605020e --- /dev/null +++ b/common/tinyxml/tinystr.h @@ -0,0 +1,250 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml.h" + + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#ifdef _MSC_VER +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include + +/* + TiXmlString is an emulation of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // TiXmlString constructor, based on a string, mark explicit to force + // us to find unnecessary casting. + explicit TiXmlString (const char * instring); + + // TiXmlString empty constructor + TiXmlString () + { + allocated = 0; + cstring = NULL; + current_length = 0; + } + + // TiXmlString copy constructor + explicit TiXmlString (const TiXmlString& copy); + + // TiXmlString destructor + ~ TiXmlString () + { + empty_it (); + } + + // Convert a TiXmlString into a classical char * + const char * c_str () const + { + if (allocated) + return cstring; + return ""; + } + + // Return the length of a TiXmlString + size_t length () const + { + return ( allocated ) ? current_length : 0; + } + + // TiXmlString = operator + void operator = (const char * content); + + // = operator + void operator = (const TiXmlString & copy); + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + append (suffix); + return *this; + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + append (single); + return *this; + } + + // += operator. Maps to append + TiXmlString& operator += (TiXmlString & suffix) + { + append (suffix); + return *this; + } + bool operator == (const TiXmlString & compare) const; + bool operator == (const char* compare) const; + bool operator < (const TiXmlString & compare) const; + bool operator > (const TiXmlString & compare) const; + + // Checks if a TiXmlString is empty + bool empty () const + { + return length () ? false : true; + } + + // single char extraction + const char& at (unsigned index) const + { + assert( index < length ()); + return cstring [index]; + } + + // find a char in a string. Return TiXmlString::notfound if not found + unsigned find (char lookup) const + { + return find (lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::notfound if not found + unsigned find (char tofind, unsigned offset) const; + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function clears the content of the TiXmlString if any exists. + */ + void reserve (unsigned size) + { + empty_it (); + if (size) + { + allocated = size; + cstring = new char [size]; + cstring [0] = 0; + current_length = 0; + } + } + + // [] operator + char& operator [] (unsigned index) const + { + assert( index < length ()); + return cstring [index]; + } + + // Error value for find primitive + enum { notfound = 0xffffffff, + npos = notfound }; + + void append (const char *str, size_t len ); + + protected : + + // The base string + char * cstring; + // Number of chars allocated + size_t allocated; + // Current string size + size_t current_length; + + // New size computation. It is simplistic right now : it returns twice the amount + // we need + size_t assign_new_size (size_t minimum_to_allocate) + { + return minimum_to_allocate * 2; + } + + // Internal function that clears the content of a TiXmlString + void empty_it () + { + if (cstring) + delete [] cstring; + cstring = NULL; + allocated = 0; + current_length = 0; + } + + void append (const char *suffix ); + + // append function for another TiXmlString + void append (const TiXmlString & suffix) + { + append (suffix . c_str ()); + } + + // append for a single char. + void append (char single) + { + if ( cstring && current_length < (allocated-1) ) + { + cstring[ current_length ] = single; + ++current_length; + cstring[ current_length ] = 0; + } + else + { + char smallstr [2]; + smallstr [0] = single; + smallstr [1] = 0; + append (smallstr); + } + } + +} ; + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + TiXmlOutStream () : TiXmlString () {} + + // TiXmlOutStream << operator. Maps to TiXmlString::append + TiXmlOutStream & operator << (const char * in) + { + append (in); + return (* this); + } + + // TiXmlOutStream << operator. Maps to TiXmlString::append + TiXmlOutStream & operator << (const TiXmlString & in) + { + append (in . c_str ()); + return (* this); + } +} ; + +#ifdef _MSC_VER +#pragma warning( default : 4530 ) +#pragma warning( default : 4786 ) +#endif + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/common/tinyxml/tinyxml.cpp b/common/tinyxml/tinyxml.cpp new file mode 100644 index 000000000..c652cc1c9 --- /dev/null +++ b/common/tinyxml/tinyxml.cpp @@ -0,0 +1,1592 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include +#include "tinyxml.h" + +#ifdef TIXML_USE_STL +#include +#endif + + +bool TiXmlBase::condenseWhiteSpace = true; + +void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_OSTREAM* stream ) +{ + TIXML_STRING buffer; + PutString( str, &buffer ); + (*stream) << buffer; +} + +void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +// <-- Strange class for a bug fix. Search for STL_STRING_BUG +TiXmlBase::StringToBuffer::StringToBuffer( const TIXML_STRING& str ) +{ + buffer = new char[ str.length()+1 ]; + if ( buffer ) + { + strcpy( buffer, str.c_str() ); + } +} + + +TiXmlBase::StringToBuffer::~StringToBuffer() +{ + delete [] buffer; +} +// End strange bug fix. --> + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) + return 0; + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) + return 0; + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( replaceThis->parent != this ) + return 0; + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( node->SValue() == _value ) + return node; + } + return 0; +} + + +TiXmlNode* TiXmlNode::FirstChild( const char * _value ) +{ + TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( node->SValue() == _value ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( node->SValue() == _value ) + return node; + } + return 0; +} + +TiXmlNode* TiXmlNode::LastChild( const char * _value ) +{ + TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( node->SValue() == _value ) + return node; + } + return 0; +} + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + +TiXmlNode* TiXmlNode::IterateChildren( TiXmlNode* previous ) +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + +TiXmlNode* TiXmlNode::IterateChildren( const char * val, TiXmlNode* previous ) +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( node->SValue() == _value ) + return node; + } + return 0; +} + +TiXmlNode* TiXmlNode::NextSibling( const char * _value ) +{ + TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( node->SValue() == _value ) + return node; + } + return 0; +} + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( node->SValue() == _value ) + return node; + } + return 0; +} + +TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) +{ + TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( node->SValue() == _value ) + return node; + } + return 0; +} + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +TiXmlElement* TiXmlNode::FirstChildElement() +{ + TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) +{ + TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +TiXmlElement* TiXmlNode::NextSiblingElement() +{ + TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) +{ + TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + +TiXmlDocument* TiXmlNode::GetDocument() +{ + TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +void TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char * TiXmlElement::Attribute( const char * name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + + if ( node ) + return node->Value(); + + return 0; +} + + +const char * TiXmlElement::Attribute( const char * name, int* i ) const +{ + const char * s = Attribute( name ); + if ( i ) + { + if ( s ) + *i = atoi( s ); + else + *i = 0; + } + return s; +} + + +const char * TiXmlElement::Attribute( const char * name, double* d ) const +{ + const char * s = Attribute( name ); + if ( d ) + { + if ( s ) + *d = atof( s ); + else + *d = 0; + } + return s; +} + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + return node->QueryIntValue( ival ); +} + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + return node->QueryDoubleValue( dval ); +} + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + char buf[64]; + sprintf( buf, "%d", val ); + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + char buf[256]; + sprintf( buf, "%f", val ); + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetAttribute( const char * name, const char * _value ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + for ( i=0; iNext() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a node + // 2) An element with only a text child is printed as text + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i", value.c_str() ); + } +} + +void TiXmlElement::StreamOut( TIXML_OSTREAM * stream ) const +{ + (*stream) << "<" << value; + + const TiXmlAttribute* attrib; + for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) + { + (*stream) << " "; + attrib->StreamOut( stream ); + } + + // If this node has children, give it a closing tag. Else + // make it an empty tag. + TiXmlNode* node; + if ( firstChild ) + { + (*stream) << ">"; + + for ( node = firstChild; node; node=node->NextSibling() ) + { + node->StreamOut( stream ); + } + (*stream) << ""; + } + else + { + (*stream) << " />"; + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + // See STL_STRING_BUG below. + StringToBuffer buf( value ); + + if ( buf.buffer && LoadFile( buf.buffer, encoding ) ) + return true; + + return false; +} + + +bool TiXmlDocument::SaveFile() const +{ + // See STL_STRING_BUG below. + StringToBuffer buf( value ); + + if ( buf.buffer && SaveFile( buf.buffer ) ) + return true; + + return false; +} + +bool TiXmlDocument::LoadFile( const char* filename, TiXmlEncoding encoding ) +{ + // Delete the existing data: + Clear(); + location.Clear(); + + // There was a really terrifying little bug here. The code: + // value = filename + // in the STL case, cause the assignment method of the std::string to + // be called. What is strange, is that the std::string had the same + // address as it's c_str() method, and so bad things happen. Looks + // like a bug in the Microsoft STL implementation. + // See STL_STRING_BUG above. + // Fixed with the StringToBuffer class. + value = filename; + + FILE* file = fopen( value.c_str (), "r" ); + + if ( file ) + { + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length == 0 ) + { + fclose( file ); + return false; + } + + // If we have a file, assume it is all one big XML file, and read it in. + // The document parser may decide the document ends sooner than the entire file, however. + TIXML_STRING data; + data.reserve( length ); + + const int BUF_SIZE = 2048; + char buf[BUF_SIZE]; + + while( fgets( buf, BUF_SIZE, file ) ) + { + data += buf; + } + fclose( file ); + + Parse( data.c_str(), 0, encoding ); + + if ( Error() ) + return false; + else + return true; + } + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; +} + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = fopen( filename, "w" ); + if ( fp ) + { + Print( fp, 0 ); + fclose( fp ); + return true; + } + return false; +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorDesc = errorDesc.c_str (); + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + const TiXmlNode* node; + for ( node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + +void TiXmlDocument::StreamOut( TIXML_OSTREAM * out ) const +{ + const TiXmlNode* node; + for ( node=FirstChild(); node; node=node->NextSibling() ) + { + node->StreamOut( out ); + + // Special rule for streams: stop after the root element. + // The stream in code will only read one element, so don't + // write more than one. + if ( node->ToElement() ) + break; + } +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/ ) const +{ + TIXML_STRING n, v; + + PutString( name, &n ); + PutString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + else + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); +} + + +void TiXmlAttribute::StreamOut( TIXML_OSTREAM * stream ) const +{ + if (value.find( '\"' ) != TIXML_STRING::npos) + { + PutString( name, stream ); + (*stream) << "=" << "'"; + PutString( value, stream ); + (*stream) << "'"; + } + else + { + PutString( name, stream ); + (*stream) << "=" << "\""; + PutString( value, stream ); + (*stream) << "\""; + } +} + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( sscanf( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( sscanf( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + sprintf (buf, "%d", _value); + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + sprintf (buf, "%lf", _value); + SetValue (buf); +} + +const int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +const double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + +void TiXmlComment::StreamOut( TIXML_OSTREAM * stream ) const +{ + (*stream) << ""; +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int /*depth*/ ) const +{ + TIXML_STRING buffer; + PutString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); +} + + +void TiXmlText::StreamOut( TIXML_OSTREAM * stream ) const +{ + PutString( value, stream ); +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/ ) const +{ + fprintf (cfile, ""); +} + +void TiXmlDeclaration::StreamOut( TIXML_OSTREAM * stream ) const +{ + (*stream) << ""; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlUnknown::StreamOut( TIXML_OSTREAM * stream ) const +{ + (*stream) << "<" << value << ">"; // Don't use entities here! It is unknown. +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + +const TiXmlAttribute* TiXmlAttributeSet::Find( const char * name ) const +{ + const TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +TiXmlAttribute* TiXmlAttributeSet::Find( const char * name ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +#ifdef TIXML_USE_STL +TIXML_ISTREAM & operator >> (TIXML_ISTREAM & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +TIXML_OSTREAM & operator<< (TIXML_OSTREAM & out, const TiXmlNode & base) +{ + base.StreamOut (& out); + return out; +} + + +#ifdef TIXML_USE_STL +std::string & operator<< (std::string& out, const TiXmlNode& base ) +{ + std::ostringstream os_stream( std::ostringstream::out ); + base.StreamOut( &os_stream ); + + out.append( os_stream.str() ); + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && iNextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && iNextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && iNextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && iNextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} diff --git a/common/tinyxml/tinyxml.h b/common/tinyxml/tinyxml.h new file mode 100644 index 000000000..916dea954 --- /dev/null +++ b/common/tinyxml/tinyxml.h @@ -0,0 +1,1426 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#if defined( DEBUG ) && defined( _MSC_VER ) +#include +#define TIXML_LOG OutputDebugString +#else +#define TIXML_LOG printf +#endif + +#ifdef TIXML_USE_STL + #include + #include + #define TIXML_STRING std::string + #define TIXML_ISTREAM std::istream + #define TIXML_OSTREAM std::ostream +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString + #define TIXML_OSTREAM TiXmlOutStream +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 3; +const int TIXML_PATCH_VERSION = 4; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream. + This is a formatted print, and will insert tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + values is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } + void* GetUserData() { return userData; } + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_OUT_OF_MEMORY, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + // See STL_STRING_BUG + // Utility class to overcome a bug. + class StringToBuffer + { + public: + StringToBuffer( const TIXML_STRING& str ); + ~StringToBuffer(); + char* buffer; + }; + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + + virtual void StreamOut (TIXML_OSTREAM *) const = 0; + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( TIXML_ISTREAM * in, TIXML_STRING * tag ); + static bool StreamTo( TIXML_ISTREAM * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + strncpy( _value, p, *length ); + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Puts a string to a stream, expanding entities as it goes. + // Note this should not contian the '<', '>', etc, or they will be transformed into entities! + static void PutString( const TIXML_STRING& str, TIXML_OSTREAM* out ); + + static void PutString( const TIXML_STRING& str, TIXML_STRING* out ); + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to Engilish words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #else + // Used internally, not part of the public API. + friend TIXML_OSTREAM& operator<< (TIXML_OSTREAM& out, const TiXmlNode& base); + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + DOCUMENT, + ELEMENT, + COMMENT, + UNKNOWN, + TEXT, + DECLARATION, + TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char * Value() const { return value.c_str (); } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) + { + StringToBuffer buf( _value ); + SetValue( buf.buffer ? buf.buffer : "" ); + } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * value ); ///< The first child of this node with the matching 'value'. Will be null if none found. + + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * value ); + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( TiXmlNode* previous ); + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * value, TiXmlNode* previous ); + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char * ); + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char * ); + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement(); + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char * ); + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement(); + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * value ) const; + TiXmlElement* FirstChildElement( const char * value ); + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: DOCUMENT, ELEMENT, COMMENT, + UNKNOWN, TEXT, and DECLARATION. + */ + virtual int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument(); + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + const TiXmlDocument* ToDocument() const { return ( this && type == DOCUMENT ) ? (const TiXmlDocument*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + const TiXmlElement* ToElement() const { return ( this && type == ELEMENT ) ? (const TiXmlElement*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + const TiXmlComment* ToComment() const { return ( this && type == COMMENT ) ? (const TiXmlComment*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + const TiXmlUnknown* ToUnknown() const { return ( this && type == UNKNOWN ) ? (const TiXmlUnknown*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + const TiXmlText* ToText() const { return ( this && type == TEXT ) ? (const TiXmlText*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + const TiXmlDeclaration* ToDeclaration() const { return ( this && type == DECLARATION ) ? (const TiXmlDeclaration*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + + TiXmlDocument* ToDocument() { return ( this && type == DOCUMENT ) ? (TiXmlDocument*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlElement* ToElement() { return ( this && type == ELEMENT ) ? (TiXmlElement*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlComment* ToComment() { return ( this && type == COMMENT ) ? (TiXmlComment*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlUnknown* ToUnknown() { return ( this && type == UNKNOWN ) ? (TiXmlUnknown*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlText* ToText() { return ( this && type == TEXT ) ? (TiXmlText*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlDeclaration* ToDeclaration() { return ( this && type == DECLARATION ) ? (TiXmlDeclaration*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( TIXML_ISTREAM* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + // Internal Value function returning a TIXML_STRING + const TIXML_STRING& SValue() const { return value ; } + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str (); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str (); } ///< Return the value of this attribute. + const int IntValue() const; ///< Return the value of this attribute, converted to an integer. + const double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int value ); ///< Set the value from an integer. + void SetDoubleValue( double value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) + { + StringToBuffer buf( _name ); + SetName ( buf.buffer ? buf.buffer : "error" ); + } + /// STL std::string form. + void SetValue( const std::string& _value ) + { + StringToBuffer buf( _value ); + SetValue( buf.buffer ? buf.buffer : "error" ); + } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next(); + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous(); + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual void StreamOut( TIXML_OSTREAM * out ) const; + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + const TiXmlAttribute* Find( const char * name ) const; + TiXmlAttribute* Find( const char * name ); + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + void operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, float* value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + *value = (float)d; + return result; + } + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * value ); + + #ifdef TIXML_USE_STL + const char* Attribute( const std::string& name ) const { return Attribute( name.c_str() ); } + const char* Attribute( const std::string& name, int* i ) const { return Attribute( name.c_str(), i ); } + const char* Attribute( const std::string& name, double* d ) const { return Attribute( name.c_str(), d ); } + int QueryIntAttribute( const std::string& name, int* value ) const { return QueryIntAttribute( name.c_str(), value ); } + int QueryDoubleAttribute( const std::string& name, double* value ) const { return QueryDoubleAttribute( name.c_str(), value ); } + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ) + { + StringToBuffer n( name ); + StringToBuffer v( _value ); + if ( n.buffer && v.buffer ) + SetAttribute (n.buffer, v.buffer ); + } + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ) + { + StringToBuffer n( name ); + if ( n.buffer ) + SetAttribute (n.buffer, _value); + } + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); + #endif + virtual void StreamOut( TIXML_OSTREAM * out ) const; + + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {} + TiXmlComment( const TiXmlComment& ); + void operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + /// Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); + #endif + virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. Contained in an element. +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /// Constructor. + TiXmlText (const char * initValue) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); } + void operator=( const TiXmlText& base ) { base.CopyTo( this ); } + + /// Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + virtual void StreamOut ( TIXML_OSTREAM * out ) const; + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); + #endif + +private: +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + void operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + /// Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); + #endif + virtual void StreamOut ( TIXML_OSTREAM * out) const; + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); } + void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + /// Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); + #endif + virtual void StreamOut ( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + void operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { + StringToBuffer f( filename ); + return ( f.buffer && LoadFile( f.buffer, encoding )); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { + StringToBuffer f( filename ); + return ( f.buffer && SaveFile( f.buffer )); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + const int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() { return errorLocation.row+1; } + int ErrorCol() { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Dump the document to standard out. */ + void Print() const { Print( stdout, 0 ); } + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +protected : + virtual void StreamOut ( TIXML_OSTREAM * out) const; + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).Element(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).Element(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).Element(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* node ) { this->node = node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /// Return the handle as a TiXmlNode. This may return null. + TiXmlNode* Node() const { return node; } + /// Return the handle as a TiXmlElement. This may return null. + TiXmlElement* Element() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /// Return the handle as a TiXmlText. This may return null. + TiXmlText* Text() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /// Return the handle as a TiXmlUnknown. This may return null; + TiXmlUnknown* Unknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + +private: + TiXmlNode* node; +}; + +#ifdef _MSC_VER +#pragma warning( default : 4530 ) +#pragma warning( default : 4786 ) +#endif + +#endif + diff --git a/common/tinyxml/tinyxmlerror.cpp b/common/tinyxml/tinyxmlerror.cpp new file mode 100644 index 000000000..b04add7f9 --- /dev/null +++ b/common/tinyxml/tinyxmlerror.cpp @@ -0,0 +1,51 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml.h" + +// The goal of the seperate error file is to make the first +// step towards localization. tinyxml (currently) only supports +// latin-1, but at least the error messages could now be translated. +// +// It also cleans up the code a bit. +// + +const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] = +{ + "No error", + "Error", + "Failed to open file", + "Memory allocation failed.", + "Error parsing Element.", + "Failed to read Element name", + "Error reading Element value.", + "Error reading Attributes.", + "Error: empty tag.", + "Error reading end tag.", + "Error parsing Unknown.", + "Error parsing Comment.", + "Error parsing Declaration.", + "Error document empty.", + "Error null (0) or unexpected EOF found in input stream.", +}; diff --git a/common/tinyxml/tinyxmlparser.cpp b/common/tinyxml/tinyxmlparser.cpp new file mode 100644 index 000000000..3e6b174f7 --- /dev/null +++ b/common/tinyxml/tinyxmlparser.cpp @@ -0,0 +1,1508 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml.h" +#include +#include + +//#define DEBUG_PARSER + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( TIXML_ISTREAM * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( TIXML_ISTREAM * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + (*name) += *p; + ++p; + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; iappend( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + return p + strlen( endTag ); +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + //TiXmlParsingData data( pError, prevData ); + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + TiXmlDocument* doc = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: "; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + p = ReadText( p, &value, false, endTag, false, encoding ); + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + + int tabsize = 4; + if ( document ) + tabsize = document->TabSize(); + +// TiXmlParsingData data( p, prevData ); + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + + if ( *p == '\'' ) + { + ++p; + end = "\'"; + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == '"' ) + { + ++p; + end = "\""; + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace + && *p != '/' && *p != '>' ) // tag end + { + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( c == '<' ) + return; + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; +// TiXmlParsingData data( p, prevData ); + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p ) + return p-1; // don't truncate the '<' + return 0; +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } +// TiXmlParsingData data( p, prevData ); + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i +typedef uint8_t byte; +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; +#else +typedef unsigned char byte; +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; + +#ifdef _WINDOWS + #if defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 64 + typedef unsigned __int64 uint64; + typedef unsigned __int64 uint64; + typedef signed __int64 int64; + #else + #error __int64 not supported + #endif +#else +typedef unsigned long long uint64; +typedef unsigned long long uint64; +typedef signed long long int64; +#endif +#endif + +#ifdef _WINDOWS + #pragma warning( disable : 4200 ) +#endif + +#ifndef __cplusplus +typedef enum { true, false } bool; +#endif + +typedef unsigned long ulong; +typedef unsigned short ushort; +typedef unsigned char uchar; +typedef const char Const_char; //for perl XS + +#ifdef _WINDOWS + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + typedef void ThreadReturnType; + #define THREAD_RETURN(x) _endthread(); return; +#else + typedef void* ThreadReturnType; + #define THREAD_RETURN(x) return(x); +#endif + +#define safe_delete(d) if(d) { delete d; d=0; } +#define safe_delete_array(d) if(d) { delete[] d; d=0; } +#define L32(i) ((uint32) i) +#define H32(i) ((uint32) (i >> 32)) +#define L16(i) ((uint16) i) + +#ifndef WIN32 +// More WIN32 compatability + typedef unsigned long DWORD; + typedef unsigned char BYTE; + typedef char CHAR; + typedef unsigned short WORD; + typedef float FLOAT; + typedef FLOAT *PFLOAT; + typedef BYTE *PBYTE,*LPBYTE; + typedef int *PINT,*LPINT; + typedef WORD *PWORD,*LPWORD; + typedef long *LPLONG, LONG; + typedef DWORD *PDWORD,*LPDWORD; + typedef int INT; + typedef unsigned int UINT,*PUINT,*LPUINT; +#endif + + +#ifdef _WINDOWS +#define DLLFUNC extern "C" __declspec(dllexport) +#else +#define DLLFUNC extern "C" +#endif + + + +#endif diff --git a/common/unix.cpp b/common/unix.cpp new file mode 100644 index 000000000..953eeff99 --- /dev/null +++ b/common/unix.cpp @@ -0,0 +1,49 @@ +/* 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 _WINDOWS + +#include "unix.h" +#include +#include + +void Sleep(unsigned int x) { + if (x > 0) + usleep(x*1000); +} + +char* strupr(char* tmp) { + int l = strlen(tmp); + for (int x = 0; x < l; x++) { + tmp[x] = toupper(tmp[x]); + } + return tmp; +} + +char* strlwr(char* tmp) { + int l = strlen(tmp); + for (int x = 0; x < l; x++) { + tmp[x] = tolower(tmp[x]); + } + return tmp; +} + +#else + int joe = 1; +#endif /* !WIN32 */ + + diff --git a/common/unix.h b/common/unix.h new file mode 100644 index 000000000..dd974ba79 --- /dev/null +++ b/common/unix.h @@ -0,0 +1,34 @@ +/* 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 _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; + +void Sleep(unsigned int x); +#ifndef __CYGWIN__ +char* strupr(char* tmp); +char* strlwr(char* tmp); +#endif +#endif +#endif diff --git a/common/useperl.h b/common/useperl.h new file mode 100644 index 000000000..b20863c42 --- /dev/null +++ b/common/useperl.h @@ -0,0 +1,50 @@ +#ifndef EMU_PERL_H_ +#define EMU_PERL_H_ + + +//headers from the Perl distribution +#include +#define WIN32IO_IS_STDIO + +#ifndef WIN32 +extern "C" { //the perl headers dont do this for us... +#endif +#include +#include +#ifndef WIN32 +}; +#endif + +#ifdef WIN32 +#ifndef snprintf +#define snprintf _snprintf +#endif +#endif + +//perl defines these macros and dosent clean them up, lazy bastards. -- I hate them too! +#ifdef Copy +#undef Copy +#endif + +#ifdef list +#undef list +#endif + +#ifdef write +#undef write +#endif + +#ifdef bool +#undef bool +#endif + +#ifdef Zero +#undef Zero +#endif + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +#endif /*EMU_PERL_H_*/ diff --git a/common/version.h b/common/version.h new file mode 100644 index 000000000..0ac48f56d --- /dev/null +++ b/common/version.h @@ -0,0 +1,36 @@ +/* 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 VERSION_H +#define VERSION_H + +#define CURRENT_VERSION "0.8.0" +#define CURRENT_CHAT_VERSION "EQEmulator " CURRENT_VERSION +#define CURRENT_ZONE_VERSION "EQEmulator " CURRENT_VERSION +#define CURRENT_WORLD_VERSION "EQEmulator " CURRENT_VERSION +#define COMPILE_DATE __DATE__ +#define COMPILE_TIME __TIME__ +#define SVN_REVISION "$Rev: 986 $" +#ifndef WIN32 + #define LAST_MODIFIED __TIME__ +#else + #define LAST_MODIFIED __TIMESTAMP__ +#endif + +#endif + diff --git a/common/win_getopt.cpp b/common/win_getopt.cpp new file mode 100644 index 000000000..59e6b7079 --- /dev/null +++ b/common/win_getopt.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* based on @(#)getopt.c 8.1 (Berkeley) 6/4/93 */ + +#ifndef __STDC__ +#define const +#endif +#include +#include +#include +#include "win_getopt.h" + +/* + * get option letter from argument vector + */ +int opterr = 1, /* if error message should be printed */ + optind = 1, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +char *optarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +int getopt(int nargc, char * const *nargv, const char *ostr) +{ + static char *place = EMSG; /* option letter processing */ + register char *oli; /* option letter list index */ + char *p; + + if (optreset || !*place) { /* update scanning pointer */ + optreset = 0; + if (optind >= nargc || *(place = nargv[optind]) != '-') { + place = EMSG; + return(-1); + } + if (place[1] && *++place == '-') { /* found "--" */ + ++optind; + place = EMSG; + return(-1); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, optopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means EOF. + */ + if (optopt == (int)'-') + return(-1); + if (!*place) + ++optind; + if (opterr && *ostr != ':') { + if (!(p = strrchr(*nargv, '/'))) + p = *nargv; + else + ++p; + (void)fprintf(stderr, "%s: illegal option -- %c\n", + p, optopt); + } + return(BADCH); + } + if (*++oli != ':') { /* don't need argument */ + optarg = NULL; + if (!*place) + ++optind; + } + else { /* need an argument */ + if (*place) /* no white space */ + optarg = place; + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + if (!(p = strrchr(*nargv, '/'))) + p = *nargv; + else + ++p; + if (*ostr == ':') + return(BADARG); + if (opterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + p, optopt); + return(BADCH); + } + else /* white space */ + optarg = nargv[optind]; + place = EMSG; + ++optind; + } + return(optopt); /* dump back option letter */ +} + diff --git a/common/win_getopt.h b/common/win_getopt.h new file mode 100644 index 000000000..737e476a7 --- /dev/null +++ b/common/win_getopt.h @@ -0,0 +1,24 @@ +#ifndef WIN_GETOPT_H +#define WIN_GETOPT_H + +#ifdef WIN32 + +#ifdef __cplusplus +extern "C" { +#endif + +extern int opterr, /* if error message should be printed */ + optind, /* index into parent argv vector */ + optopt, /* character checked for validity */ + optreset; /* reset getopt */ +extern char *optarg; /* argument associated with option */ + +extern int getopt(int nargc, char * const *nargv, const char *ostr); + + +#ifdef __cplusplus +}; +#endif + +#endif +#endif diff --git a/common/worldconn.cpp b/common/worldconn.cpp new file mode 100644 index 000000000..ef3922bf7 --- /dev/null +++ b/common/worldconn.cpp @@ -0,0 +1,101 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 +#include +#include +#include +using namespace std; +#include +#include +#include + +#include "worldconn.h" +#include "EQEmuConfig.h" +#include "md5.h" +#include "database.h" +#include "servertalk.h" + +WorldConnection::WorldConnection(EmuTCPConnection::ePacketMode mode, const char *password) +: m_password(password) +{ + tcpc.SetPacketMode(mode); + pTryReconnect = true; + pConnected = false; +} + +WorldConnection::~WorldConnection() { +} + +bool WorldConnection::SendPacket(ServerPacket* pack) { + if (!Connected()) + return false; + return tcpc.SendPacket(pack); +} + +void WorldConnection::OnConnected() { + const EQEmuConfig *Config=EQEmuConfig::get(); + _log(NET__WORLD, "Connected to World: %s:%d", Config->WorldIP.c_str(), Config->WorldTCPPort); + + ServerPacket* pack = new ServerPacket(ServerOP_ZAAuth, 16); + MD5::Generate((const uchar*) m_password.c_str(), m_password.length(), pack->pBuffer); + SendPacket(pack); + safe_delete(pack); +} + +void WorldConnection::Process() { + _CP(WorldConnection_Process); + + //persistent connection.... + if (!Connected()) { + pConnected = tcpc.Connected(); + if (pConnected) { + OnConnected(); + } + else + return; + } + +} + +void WorldConnection::AsyncConnect() { + const EQEmuConfig *Config=EQEmuConfig::get(); + tcpc.AsyncConnect(Config->WorldIP.c_str(), Config->WorldTCPPort); +} + +bool WorldConnection::Connect() { + const EQEmuConfig *Config=EQEmuConfig::get(); + char errbuf[TCPConnection_ErrorBufferSize]; + if (tcpc.Connect(Config->WorldIP.c_str(), Config->WorldTCPPort, errbuf)) { + return true; + } else { + _log(NET__WORLD, "WorldConnection connect: Connecting to the server %s:%d failed: %s", Config->WorldIP.c_str(), Config->WorldTCPPort, errbuf); + } + return false; +} + +void WorldConnection::Disconnect() { + tcpc.Disconnect(); +} + + + + + + diff --git a/common/worldconn.h b/common/worldconn.h new file mode 100644 index 000000000..3821d3c9a --- /dev/null +++ b/common/worldconn.h @@ -0,0 +1,59 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 WORLDCONNECTION_H +#define WORLDCONNECTION_H + +#include "../common/EmuTCPConnection.h" +#include + +class ServerPacket; + +/* + * This object is an arbitrary connection to world. + */ +class WorldConnection { +public: + WorldConnection(EmuTCPConnection::ePacketMode mode, const char *password = ""); + virtual ~WorldConnection(); + + virtual void Process(); + bool SendPacket(ServerPacket* pack); + + uint32 GetIP() const { return tcpc.GetrIP(); } + uint16 GetPort() const { return tcpc.GetrPort(); } + bool Connected() const { return (pConnected && tcpc.Connected()); } + + void SetPassword(const char *password) { m_password = password; } + bool Connect(); + void AsyncConnect(); + void Disconnect(); + inline bool TryReconnect() const { return pTryReconnect; } + +protected: + virtual void OnConnected(); + + std::string m_password; + EmuTCPConnection tcpc; + bool pTryReconnect; + bool pConnected; +}; + + +#endif + diff --git a/dependencies/.gitignore b/dependencies/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/eqlaunch/CMakeLists.txt b/eqlaunch/CMakeLists.txt new file mode 100644 index 000000000..8483b9bb0 --- /dev/null +++ b/eqlaunch/CMakeLists.txt @@ -0,0 +1,37 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +SET(eqlaunch_sources + eqlaunch.cpp + worldserver.cpp + ZoneLaunch.cpp +) + +SET(eqlaunch_headers + worldserver.h + ZoneLaunch.h +) + +ADD_EXECUTABLE(eqlaunch ${eqlaunch_sources} ${eqlaunch_headers}) + +TARGET_LINK_LIBRARIES(eqlaunch Common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE}) + +IF(MSVC) + + SET_TARGET_PROPERTIES(eqlaunch PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF") + TARGET_LINK_LIBRARIES(eqlaunch "Ws2_32.lib") +ENDIF(MSVC) + +IF(MINGW) + TARGET_LINK_LIBRARIES(eqlaunch "WS2_32") +ENDIF(MINGW) + +IF(UNIX) + TARGET_LINK_LIBRARIES(eqlaunch "dl") + TARGET_LINK_LIBRARIES(eqlaunch "z") + TARGET_LINK_LIBRARIES(eqlaunch "m") + TARGET_LINK_LIBRARIES(eqlaunch "rt") + TARGET_LINK_LIBRARIES(eqlaunch "pthread") + ADD_DEFINITIONS(-fPIC) +ENDIF(UNIX) + +SET(EXECUTABLE_OUTPUT_PATH ../Bin) diff --git a/eqlaunch/ZoneLaunch.cpp b/eqlaunch/ZoneLaunch.cpp new file mode 100644 index 000000000..6b038d8bf --- /dev/null +++ b/eqlaunch/ZoneLaunch.cpp @@ -0,0 +1,287 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "ZoneLaunch.h" +#include "worldserver.h" +#include "../common/EQEmuConfig.h" + +//static const uint32 ZONE_RESTART_DELAY = 10000; +//static const uint32 ZONE_TERMINATE_WAIT = 10000; + +int ZoneLaunch::s_running = 0; //the number of zones running under this launcher +Timer ZoneLaunch::s_startTimer(1); //I do not trust this things state after static initialization + +void ZoneLaunch::InitStartTimer() { + s_startTimer.Start(1); + s_startTimer.Trigger(); +} + +ZoneLaunch::ZoneLaunch(WorldServer *world, const char *launcher_name, + const char *zone_name, const EQEmuConfig *config) +: m_state(StateStartPending), + m_world(world), + m_zone(zone_name), + m_launcherName(launcher_name), + m_config(config), + m_timer(config->RestartWait), + m_ref(ProcLauncher::ProcError), + m_startCount(0), + m_killFails(0) +{ + //trigger the startup timer initially so it boots the first time. + m_timer.Trigger(); +} + +ZoneLaunch::~ZoneLaunch() { + if(IsRunning()) + s_running--; +} + +void ZoneLaunch::SendStatus() const { + m_world->SendStatus(m_zone.c_str(), m_startCount, IsRunning()); +} + +void ZoneLaunch::Start() { + ProcLauncher::Spec *spec = new ProcLauncher::Spec(); + spec->program = m_config->ZoneExe; +// if(m_zone.substr(0,7) == "dynamic") +// spec->args.push_back("."); +// else + spec->args.push_back(m_zone); + spec->args.push_back(m_launcherName); + spec->handler = this; + spec->logFile = m_config->LogPrefix + m_zone + m_config->LogSuffix; + + //spec is consumed, even on failure + m_ref = ProcLauncher::get()->Launch(spec); + if(m_ref == ProcLauncher::ProcError) { + _log(LAUNCHER__ERROR, "Failure to launch '%s %s %s'. ", m_config->ZoneExe.c_str(), m_zone.c_str(), m_launcherName); + m_timer.Start(m_config->RestartWait); + return; + } + + m_startCount++; + m_state = StateStarted; + s_running++; + m_killFails = 0; + + SendStatus(); + + _log(LAUNCHER__STATUS, "Zone %s has been started.", m_zone.c_str()); +} + +void ZoneLaunch::Restart() { + switch(m_state) { + case StateRestartPending: + _log(LAUNCHER__STATUS, "Restart of zone %s requested when a restart is already pending.", m_zone.c_str()); + break; + case StateStartPending: + //we havent started yet, do nothing + _log(LAUNCHER__STATUS, "Restart of %s before it has started. Ignoring.", m_zone.c_str()); + break; + case StateStarted: + //process is running along, kill it off.. + if(m_ref == ProcLauncher::ProcError) + break; //we have no proc ref... cannot stop.. + if(!ProcLauncher::get()->Terminate(m_ref, true)) { + //failed to terminate the process, its not likely that it will work if we try again, so give up. + _log(LAUNCHER__ERROR, "Failed to terminate zone %s. Giving up and moving to stopped.", m_zone.c_str()); + m_state = StateStopped; + break; + } + _log(LAUNCHER__STATUS, "Termination signal sent to zone %s.", m_zone.c_str()); + m_timer.Start(m_config->TerminateWait); + m_state = StateRestartPending; + break; + case StateStopPending: + _log(LAUNCHER__STATUS, "Restart of zone %s requested when a stop is pending. Ignoring.", m_zone.c_str()); + break; + case StateStopped: + //process is already stopped... nothing to do.. + _log(LAUNCHER__STATUS, "Restart requested when zone %s is already stopped.", m_zone.c_str()); + break; + } +} + +void ZoneLaunch::Stop(bool graceful) { + switch(m_state) { + case StateStartPending: + //we havent started yet, transition directly to stopped. + _log(LAUNCHER__STATUS, "Stopping zone %s before it has started.", m_zone.c_str()); + m_state = StateStopped; + break; + case StateStarted: + case StateRestartPending: + case StateStopPending: + if(m_ref == ProcLauncher::ProcError) + break; //we have no proc ref... cannot stop.. + if(!ProcLauncher::get()->Terminate(m_ref, graceful)) { + //failed to terminate the process, its not likely that it will work if we try again, so give up. + _log(LAUNCHER__ERROR, "Failed to terminate zone %s. Giving up and moving to stopped.", m_zone.c_str()); + m_state = StateStopped; + break; + } + _log(LAUNCHER__STATUS, "Termination signal sent to zone %s.", m_zone.c_str()); + m_timer.Start(m_config->TerminateWait); + m_state = StateStopPending; + break; + case StateStopped: + //process is already stopped... nothing to do.. + _log(LAUNCHER__STATUS, "Stop requested when zone %s is already stopped.", m_zone.c_str()); + break; + } +} + +bool ZoneLaunch::Process() { + switch(m_state) { + case StateStartPending: + if(m_timer.Check(false)) { + //our internal timer says its time to start. Check with the shared timer. + if(!s_startTimer.Check(false)) { + //we have to wait on the shared timer now.. + break; + } + + //ok, both timers say we can start. + //disable our internal timer, will get started again if it is needed. + m_timer.Disable(); + + //actually start up the program + _log(LAUNCHER__STATUS, "Starting zone %s", m_zone.c_str()); + Start(); + + //now update the shared timer to reflect the proper start interval. + if(s_running == 1) { + //we are the first zone started. wait that interval. + _log(LAUNCHER__STATUS, "Waiting %d milliseconds before booting the second zone.", m_config->InitialBootWait); + s_startTimer.Start(m_config->InitialBootWait); + } else { + //just some follow on zone, use that interval. + _log(LAUNCHER__STATUS, "Waiting %d milliseconds before booting the next zone.", m_config->ZoneBootInterval); + s_startTimer.Start(m_config->ZoneBootInterval); + } + + } //else, timer still ticking, keep waiting + break; + case StateStarted: + //happy state, do nothing.. + break; + case StateRestartPending: + //waiting for notification that our child has died.. + if(m_timer.Check()) { + //we have timed out, try to kill the child again + _log(LAUNCHER__STATUS, "Zone %s refused to die, killing again.", m_zone.c_str()); + Restart(); + } + break; + case StateStopPending: + //waiting for notification that our child has died.. + if(m_timer.Check()) { + //we have timed out, try to kill the child again + m_killFails++; + if(m_killFails > 5) { //should get this number from somewhere.. + _log(LAUNCHER__STATUS, "Zone %s refused to die, giving up and acting like its dead.", m_zone.c_str()); + m_state = StateStopped; + s_running--; + SendStatus(); + } else { + _log(LAUNCHER__STATUS, "Zone %s refused to die, killing again.", m_zone.c_str()); + Stop(false); + } + } + break; + case StateStopped: + //signal our caller to remove us + return(false); + break; + } + return(true); +} + +//called when the process actually dies off... +void ZoneLaunch::OnTerminate(const ProcLauncher::ProcRef &ref, const ProcLauncher::Spec *spec) { + s_running--; + + switch(m_state) { + case StateStartPending: + _log(LAUNCHER__STATUS, "Zone %s has gone down before we started it..?? Restart timer started.", m_zone.c_str()); + m_state = StateStartPending; + m_timer.Start(m_config->RestartWait); + break; + case StateStarted: + //something happened to our happy process... + _log(LAUNCHER__STATUS, "Zone %s has gone down. Restart timer started.", m_zone.c_str()); + m_state = StateStartPending; + m_timer.Start(m_config->RestartWait); + break; + case StateRestartPending: + //it finally died, start it on up again + _log(LAUNCHER__STATUS, "Zone %s has terminated. Transitioning to starting state.", m_zone.c_str()); + m_state = StateStartPending; + break; + case StateStopPending: + //it finally died, transition to close. + _log(LAUNCHER__STATUS, "Zone %s has terminated. Transitioning to stopped state.", m_zone.c_str()); + m_state = StateStopped; + break; + case StateStopped: + //we already thought it was stopped... dont care... + _log(LAUNCHER__STATUS, "Notified of zone %s terminating when we thought it was stopped.", m_zone.c_str()); + break; + } + + SendStatus(); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eqlaunch/ZoneLaunch.h b/eqlaunch/ZoneLaunch.h new file mode 100644 index 000000000..9c68e7e5f --- /dev/null +++ b/eqlaunch/ZoneLaunch.h @@ -0,0 +1,87 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 ZONELAUNCH_H_ +#define ZONELAUNCH_H_ + +#include "../common/ProcLauncher.h" +#include "../common/timer.h" +#include + +class WorldServer; +class EQEmuConfig; + +class ZoneLaunch : protected ProcLauncher::EventHandler { +public: + ZoneLaunch(WorldServer *world, const char *launcher_name, + const char *zone_name, const EQEmuConfig *config); + virtual ~ZoneLaunch(); + + void Stop(bool graceful = true); + void Restart(); + + bool Process(); + + void SendStatus() const; + + const char *GetZone() const { return(m_zone.c_str()); } + uint32 GetStartCount() const { return(m_startCount); } + + //should only be called during process init to setup the start timer. + static void InitStartTimer(); + +protected: + bool IsRunning() const { return(m_state == StateStarted || m_state == StateStopPending || m_state == StateRestartPending); } + + void Start(); + + void OnTerminate(const ProcLauncher::ProcRef &ref, const ProcLauncher::Spec *spec); + + enum { + StateStartPending, + StateStarted, + StateRestartPending, + StateStopPending, + StateStopped + } m_state; + + WorldServer *const m_world; + const std::string m_zone; + const char *const m_launcherName; + const EQEmuConfig *const m_config; + + Timer m_timer; + ProcLauncher::ProcRef m_ref; + uint32 m_startCount; + + uint32 m_killFails; + +private: + static int s_running; + static Timer s_startTimer; +}; + + + + + + + + + + +#endif /*ZONELAUNCH_H_*/ diff --git a/eqlaunch/eqlaunch.cpp b/eqlaunch/eqlaunch.cpp new file mode 100644 index 000000000..052d849fe --- /dev/null +++ b/eqlaunch/eqlaunch.cpp @@ -0,0 +1,207 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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/ProcLauncher.h" +#include "../common/EQEmuConfig.h" +#include "../common/servertalk.h" +#include "../common/platform.h" +#include "../common/crash.h" +#include "worldserver.h" +#include "ZoneLaunch.h" +#include +#include +#include +#include +#include + +using namespace std; + +bool RunLoops = false; + +void CatchSignal(int sig_num); + +int main(int argc, char *argv[]) { + RegisterExecutablePlatform(ExePlatformLaunch); + set_exception_handler(); + + string launcher_name; + if(argc == 2) { + launcher_name = argv[1]; + } + if(launcher_name.length() < 1) { + _log(LAUNCHER__ERROR, "You must specfify a launcher name as the first argument to this program."); + return(1); + } + + _log(LAUNCHER__INIT, "Loading server configuration.."); + if (!EQEmuConfig::LoadConfig()) { + _log(LAUNCHER__ERROR, "Loading server configuration failed."); + return(1); + } + const EQEmuConfig *Config = EQEmuConfig::get(); + + /* + * Setup nice signal handlers + */ + if (signal(SIGINT, CatchSignal) == SIG_ERR) { + _log(LAUNCHER__ERROR, "Could not set signal handler"); + return 1; + } + if (signal(SIGTERM, CatchSignal) == SIG_ERR) { + _log(LAUNCHER__ERROR, "Could not set signal handler"); + return 1; + } + #ifndef WIN32 + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { + _log(LAUNCHER__ERROR, "Could not set signal handler"); + return 1; + } + + /* + * Add '.' to LD_LIBRARY_PATH + */ + //the storage passed to putenv must remain valid... crazy unix people + const char *pv = getenv("LD_LIBRARY_PATH"); + if(pv == NULL) { + putenv(strdup("LD_LIBRARY_PATH=.")); + } else { + char *v = (char *) malloc(strlen(pv) + 19); + sprintf(v, "LD_LIBRARY_PATH=.:%s", pv); + putenv(v); + } + #endif + + map zones; + WorldServer world(zones, launcher_name.c_str(), Config); + if (!world.Connect()) { + _log(LAUNCHER__ERROR, "worldserver.Connect() FAILED! Will retry."); + } + + map::iterator zone, zend; + set to_remove; + + Timer InterserverTimer(INTERSERVER_TIMER); // does auto-reconnect + + _log(LAUNCHER__INIT, "Starting main loop..."); + +// zones["test"] = new ZoneLaunch(&world, "./zone", "dynamic_1"); + + ProcLauncher *launch = ProcLauncher::get(); + RunLoops = true; + while(RunLoops) { + //Advance the timer to our current point in time + Timer::SetCurrentTime(); + + /* + * Process the world connection + */ + world.Process(); + + /* + * Let the process manager look for dead children + */ + launch->Process(); + + /* + * Give all zones a chance to process. + */ + zone = zones.begin(); + zend = zones.end(); + for(; zone != zend; zone++) { + if(!zone->second->Process()) + to_remove.insert(zone->first); + } + + /* + * Kill off any zones which have stopped + */ + while(!to_remove.empty()) { + string rem = *to_remove.begin(); + to_remove.erase(rem); + zone = zones.find(rem); + if(zone == zones.end()) { + //wtf... + continue; + } + delete zone->second; + zones.erase(rem); + } + + + if (InterserverTimer.Check()) { + if (world.TryReconnect() && (!world.Connected())) + world.AsyncConnect(); + } + + /* + * Take a nice nap until next cycle + */ + if(zones.empty()) + Sleep(5000); + else + Sleep(2000); + } + + //try to be semi-nice about this... without waiting too long + zone = zones.begin(); + zend = zones.end(); + for(; zone != zend; zone++) { + zone->second->Stop(); + } + Sleep(1); + launch->Process(); + launch->TerminateAll(false); + Sleep(1); + launch->Process(); + //kill anybody left + launch->TerminateAll(true); + for(; zone != zend; zone++) { + delete zone->second; + } + + return(0); +} + + +void CatchSignal(int sig_num) { + _log(LAUNCHER__STATUS, "Caught signal %d", sig_num); + RunLoops = false; +} + + + + + + + + + + + + + + + + + + + + + + diff --git a/eqlaunch/worldserver.cpp b/eqlaunch/worldserver.cpp new file mode 100644 index 000000000..a3071f3df --- /dev/null +++ b/eqlaunch/worldserver.cpp @@ -0,0 +1,155 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "worldserver.h" +#include "../common/servertalk.h" +#include "ZoneLaunch.h" +#include "../common/EQEmuConfig.h" + + +WorldServer::WorldServer(map &zones, const char *name, const EQEmuConfig *config) +: WorldConnection(EmuTCPConnection::packetModeLauncher, config->SharedKey.c_str()), + m_name(name), + m_config(config), + m_zones(zones) +{ +} + +WorldServer::~WorldServer() { +} + +void WorldServer::OnConnected() { + WorldConnection::OnConnected(); + + ServerPacket* pack = new ServerPacket(ServerOP_LauncherConnectInfo, sizeof(LauncherConnectInfo)); + LauncherConnectInfo* sci = (LauncherConnectInfo*) pack->pBuffer; + strn0cpy(sci->name, m_name, sizeof(sci->name)); +// sci->port = net.GetZonePort(); +// strcpy(sci->address, net.GetZoneAddress()); + SendPacket(pack); + safe_delete(pack); + + //send status for all zones... + std::map::iterator cur, end; + cur = m_zones.begin(); + end = m_zones.end(); + for(; cur != end; cur++) { + cur->second->SendStatus(); + } +} + +void WorldServer::Process() { + + WorldConnection::Process(); + + if (!Connected()) + return; + + ServerPacket *pack = 0; + while((pack = tcpc.PopPacket())) { + switch(pack->opcode) { + case 0: { + break; + } + case ServerOP_EmoteMessage: + case ServerOP_KeepAlive: { + // ignore this + break; + } + case ServerOP_ZAAuthFailed: { + _log(LAUNCHER__ERROR, "World server responded 'Not Authorized', disabling reconnect"); + pTryReconnect = false; + Disconnect(); + break; + } + case ServerOP_LauncherZoneRequest: { + if(pack->size != sizeof(LauncherZoneRequest)) { + _log(LAUNCHER__NET, "Invalid size of LauncherZoneRequest: %d", pack->size); + break; + } + const LauncherZoneRequest *lzr = (const LauncherZoneRequest *) pack->pBuffer; + + + switch(ZoneRequestCommands(lzr->command)) { + case ZR_Start: { + if(m_zones.find(lzr->short_name) != m_zones.end()) { + _log(LAUNCHER__ERROR, "World told us to start zone %s, but it is already running.", lzr->short_name); + } else { + _log(LAUNCHER__WORLD, "World told us to start zone %s.", lzr->short_name); + ZoneLaunch *l = new ZoneLaunch(this, m_name, lzr->short_name, m_config); + m_zones[lzr->short_name] = l; + } + break; + } + case ZR_Restart: { + map::iterator res = m_zones.find(lzr->short_name); + if(res == m_zones.end()) { + _log(LAUNCHER__ERROR, "World told us to restart zone %s, but it is not running.", lzr->short_name); + } else { + _log(LAUNCHER__WORLD, "World told us to restart zone %s.", lzr->short_name); + res->second->Restart(); + } + break; + } + case ZR_Stop: { + map::iterator res = m_zones.find(lzr->short_name); + if(res == m_zones.end()) { + _log(LAUNCHER__ERROR, "World told us to stop zone %s, but it is not running.", lzr->short_name); + } else { + _log(LAUNCHER__WORLD, "World told us to stop zone %s.", lzr->short_name); + res->second->Stop(); + } + break; + } + } + break; + } + case ServerOP_GroupIDReply: { + //ignore this, world is still being dumb + break; + } + + default: { + _log(LAUNCHER__NET, "Unknown opcode 0x%x from World of len %d", pack->opcode, pack->size); + //DumpPacket(pack->pBuffer, pack->size); + break; + } + } + safe_delete(pack); + } +} + + + +void WorldServer::SendStatus(const char *short_name, uint32 start_count, bool running) { + ServerPacket* pack = new ServerPacket(ServerOP_LauncherZoneStatus, sizeof(LauncherZoneStatus)); + LauncherZoneStatus* it =(LauncherZoneStatus*) pack->pBuffer; + + strn0cpy(it->short_name, short_name, 32); + it->start_count = start_count; + it->running = running?1:0; + + SendPacket(pack); + safe_delete(pack); +} + + + + + + diff --git a/eqlaunch/worldserver.h b/eqlaunch/worldserver.h new file mode 100644 index 000000000..b6d6314ad --- /dev/null +++ b/eqlaunch/worldserver.h @@ -0,0 +1,46 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 WORLDSERVER_H +#define WORLDSERVER_H + +#include "../common/worldconn.h" +#include +#include +#include + +class ZoneLaunch; +class EQEmuConfig; + +class WorldServer : public WorldConnection { +public: + WorldServer(std::map &zones, const char *name, const EQEmuConfig *config); + virtual ~WorldServer(); + + virtual void Process(); + + void SendStatus(const char *short_name, uint32 start_count, bool running); + +private: + virtual void OnConnected(); + + const char *const m_name; + const EQEmuConfig *const m_config; + std::map &m_zones; +}; +#endif + diff --git a/loginserver/CMakeLists.txt b/loginserver/CMakeLists.txt new file mode 100644 index 000000000..5ded6a0c7 --- /dev/null +++ b/loginserver/CMakeLists.txt @@ -0,0 +1,66 @@ +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 +) + +IF(MSVC OR MINGW) + 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 +) + +IF(UNIX) + SET(EQEMU_UNIX_ENC_LIBRARY_LOC "${CMAKE_SOURCE_DIR}/dependencies" CACHE PATH "Location of EQEmuAuthCrypto and cryptopp") + LINK_DIRECTORIES(${EQEMU_UNIX_ENC_LIBRARY_LOC}) +ENDIF(UNIX) + +ADD_EXECUTABLE(loginserver ${eqlogin_sources} ${eqlogin_headers}) + +TARGET_LINK_LIBRARIES(loginserver Common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE}) + +IF(MSVC) + + SET_TARGET_PROPERTIES(loginserver PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF") + TARGET_LINK_LIBRARIES(loginserver "Ws2_32.lib") +ENDIF(MSVC) + +IF(MINGW) + TARGET_LINK_LIBRARIES(loginserver "WS2_32") +ENDIF(MINGW) + +IF(UNIX) + TARGET_LINK_LIBRARIES(loginserver "dl") + TARGET_LINK_LIBRARIES(loginserver "z") + TARGET_LINK_LIBRARIES(loginserver "m") + TARGET_LINK_LIBRARIES(loginserver "rt") + TARGET_LINK_LIBRARIES(loginserver "pthread") + TARGET_LINK_LIBRARIES(loginserver "EQEmuAuthCrypto") + TARGET_LINK_LIBRARIES(loginserver "cryptopp") + ADD_DEFINITIONS(-fPIC) +ENDIF(UNIX) + +SET(EXECUTABLE_OUTPUT_PATH ../Bin) diff --git a/loginserver/Client.cpp b/loginserver/Client.cpp new file mode 100644 index 000000000..92351d159 --- /dev/null +++ b/loginserver/Client.cpp @@ -0,0 +1,396 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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 "Client.h" +#include "ErrorLog.h" +#include "LoginServer.h" +#include "LoginStructures.h" +#include "../common/MiscFunctions.h" + +extern ErrorLog *server_log; +extern LoginServer server; + +Client::Client(EQStream *c, ClientVersion v) +{ + connection = c; + version = v; + status = cs_not_sent_session_ready; + account_id = 0; + play_server_id = 0; + play_sequence_id = 0; +} + +bool Client::Process() +{ + EQApplicationPacket *app = connection->PopPacket(); + while(app) + { + if(server.options.IsTraceOn()) + { + server_log->Log(log_network, "Application packet recieved from client (size %u)", app->Size()); + } + + if(server.options.IsDumpInPacketsOn()) + { + DumpPacket(app); + } + + switch(app->GetOpcode()) + { + case OP_SessionReady: + { + if(server.options.IsTraceOn()) + { + server_log->Log(log_network, "Session ready recieved from client."); + } + Handle_SessionReady((const char*)app->pBuffer, app->Size()); + break; + } + case OP_Login: + { + if(app->Size() < 20) + { + server_log->Log(log_network_error, "Login recieved but it is too small, discarding."); + break; + } + + if(server.options.IsTraceOn()) + { + server_log->Log(log_network, "Login recieved from client."); + } + + Handle_Login((const char*)app->pBuffer, app->Size()); + break; + } + case OP_ServerListRequest: + { + if(server.options.IsTraceOn()) + { + server_log->Log(log_network, "Server list request recieved from client."); + } + + SendServerListPacket(); + break; + } + case OP_PlayEverquestRequest: + { + if(app->Size() < sizeof(PlayEverquestRequest_Struct)) + { + server_log->Log(log_network_error, "Play recieved but it is too small, discarding."); + break; + } + + Handle_Play((const char*)app->pBuffer); + break; + } + default: + { + char dump[64]; + app->build_header_dump(dump); + server_log->Log(log_network_error, "Recieved unhandled application packet from the client: %s.", dump); + } + } + + delete app; + app = connection->PopPacket(); + } + + return true; +} + +void Client::Handle_SessionReady(const char* data, unsigned int size) +{ + if(status != cs_not_sent_session_ready) + { + server_log->Log(log_network_error, "Session ready recieved again after already being recieved."); + return; + } + + if(size < sizeof(unsigned int)) + { + server_log->Log(log_network_error, "Session ready was too small."); + return; + } + + unsigned int mode = *((unsigned int*)data); + if(mode == (unsigned int)lm_from_world) + { + server_log->Log(log_network, "Session ready indicated logged in from world(unsupported feature), disconnecting."); + connection->Close(); + return; + } + + status = cs_waiting_for_login; + + /** + * The packets are mostly the same but slightly different between the two versions. + */ + if(version == cv_sod) + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChatMessage, 17); + outapp->pBuffer[0] = 0x02; + outapp->pBuffer[10] = 0x01; + outapp->pBuffer[11] = 0x65; + + if(server.options.IsDumpOutPacketsOn()) + { + DumpPacket(outapp); + } + + connection->QueuePacket(outapp); + delete outapp; + } + else + { + const char *msg = "ChatMessage"; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChatMessage, 16 + strlen(msg)); + outapp->pBuffer[0] = 0x02; + outapp->pBuffer[10] = 0x01; + outapp->pBuffer[11] = 0x65; + strcpy((char*)(outapp->pBuffer + 15), msg); + + if(server.options.IsDumpOutPacketsOn()) + { + DumpPacket(outapp); + } + + connection->QueuePacket(outapp); + delete outapp; + } +} + +void Client::Handle_Login(const char* data, unsigned int size) +{ + if(status != cs_waiting_for_login) + { + server_log->Log(log_network_error, "Login recieved after already having logged in."); + return; + } + + if((size - 12) % 8 != 0) + { + server_log->Log(log_network_error, "Login recieved packet of size: %u, this would cause a block corruption, discarding.", size); + return; + } + + status = cs_logged_in; + + string e_user; + string e_hash; + char *e_buffer = NULL; + unsigned int d_account_id = 0; + string d_pass_hash; + +#ifdef WIN32 + e_buffer = server.eq_crypto->DecryptUsernamePassword(data, size, server.options.GetEncryptionMode()); + + int buffer_len = strlen(e_buffer); + e_hash.assign(e_buffer, buffer_len); + e_user.assign((e_buffer + buffer_len + 1), strlen(e_buffer + buffer_len + 1)); + + if(server.options.IsTraceOn()) + { + server_log->Log(log_client, "User: %s", e_user.c_str()); + server_log->Log(log_client, "Hash: %s", e_hash.c_str()); + } + + server.eq_crypto->DeleteHeap(e_buffer); +#else + e_buffer = DecryptUsernamePassword(data, size, server.options.GetEncryptionMode()); + + int buffer_len = strlen(e_buffer); + e_hash.assign(e_buffer, buffer_len); + e_user.assign((e_buffer + buffer_len + 1), strlen(e_buffer + buffer_len + 1)); + + if(server.options.IsTraceOn()) + { + server_log->Log(log_client, "User: %s", e_user.c_str()); + server_log->Log(log_client, "Hash: %s", e_hash.c_str()); + } + + _HeapDeleteCharBuffer(e_buffer); +#endif + + bool result; + if(server.db->GetLoginDataFromAccountName(e_user, d_pass_hash, d_account_id) == false) + { + server_log->Log(log_client_error, "Error logging in, user %s does not exist in the database.", e_user.c_str()); + result = false; + } + else + { + if(d_pass_hash.compare(e_hash) == 0) + { + result = true; + } + else + { + result = false; + } + } + + if(result) + { + server.CM->RemoveExistingClient(d_account_id); + in_addr in; + in.s_addr = connection->GetRemoteIP(); + server.db->UpdateLSAccountData(d_account_id, string(inet_ntoa(in))); + GenerateKey(); + account_id = d_account_id; + account_name = e_user; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LoginAccepted, 10 + 80); + const LoginLoginRequest_Struct* llrs = (const LoginLoginRequest_Struct *)data; + LoginLoginAccepted_Struct* llas = (LoginLoginAccepted_Struct *)outapp->pBuffer; + llas->unknown1 = llrs->unknown1; + llas->unknown2 = llrs->unknown2; + llas->unknown3 = llrs->unknown3; + llas->unknown4 = llrs->unknown4; + llas->unknown5 = llrs->unknown5; + + Login_ReplyBlock_Struct * lrbs = new Login_ReplyBlock_Struct; + memset(lrbs, 0, sizeof(Login_ReplyBlock_Struct)); + + lrbs->failed_attempts = 0; + lrbs->message = 0x01; + lrbs->lsid = d_account_id; + lrbs->unknown3[3] = 0x03; + lrbs->unknown4[3] = 0x02; + lrbs->unknown5[0] = 0xe7; + lrbs->unknown5[1] = 0x03; + lrbs->unknown6[0] = 0xff; + lrbs->unknown6[1] = 0xff; + lrbs->unknown6[2] = 0xff; + lrbs->unknown6[3] = 0xff; + lrbs->unknown7[0] = 0xa0; + lrbs->unknown7[1] = 0x05; + lrbs->unknown8[3] = 0x02; + lrbs->unknown9[0] = 0xff; + lrbs->unknown9[1] = 0x03; + lrbs->unknown11[0] = 0x63; + lrbs->unknown12[0] = 0x01; + memcpy(lrbs->key, key.c_str(), key.size()); + +#ifdef WIN32 + unsigned int e_size; + char *encrypted_buffer = server.eq_crypto->Encrypt((const char*)lrbs, 75, e_size); + memcpy(llas->encrypt, encrypted_buffer, 80); + server.eq_crypto->DeleteHeap(encrypted_buffer); +#else + unsigned int e_size; + char *encrypted_buffer = Encrypt((const char*)lrbs, 75, e_size); + memcpy(llas->encrypt, encrypted_buffer, 80); + _HeapDeleteCharBuffer(encrypted_buffer); +#endif + + if(server.options.IsDumpOutPacketsOn()) + { + DumpPacket(outapp); + } + + connection->QueuePacket(outapp); + delete outapp; + } + else + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LoginAccepted, sizeof(LoginLoginFailed_Struct)); + const LoginLoginRequest_Struct* llrs = (const LoginLoginRequest_Struct *)data; + LoginLoginFailed_Struct* llas = (LoginLoginFailed_Struct *)outapp->pBuffer; + llas->unknown1 = llrs->unknown1; + llas->unknown2 = llrs->unknown2; + llas->unknown3 = llrs->unknown3; + llas->unknown4 = llrs->unknown4; + llas->unknown5 = llrs->unknown5; + memcpy(llas->unknown6, FailedLoginResponseData, sizeof(FailedLoginResponseData)); + + if(server.options.IsDumpOutPacketsOn()) + { + DumpPacket(outapp); + } + + connection->QueuePacket(outapp); + delete outapp; + } +} + +void Client::Handle_Play(const char* data) +{ + if(status != cs_logged_in) + { + server_log->Log(log_client_error, "Client sent a play request when they either were not logged in, discarding."); + return; + } + + const PlayEverquestRequest_Struct *play = (const PlayEverquestRequest_Struct*)data; + unsigned int server_id_in = (unsigned int)play->ServerNumber; + unsigned int sequence_in = (unsigned int)play->Sequence; + + if(server.options.IsTraceOn()) + { + server_log->Log(log_network, "Play recieved from client, server number %u sequence %u.", server_id_in, sequence_in); + } + + this->play_server_id = (unsigned int)play->ServerNumber; + play_sequence_id = sequence_in; + play_server_id = server_id_in; + server.SM->SendUserToWorldRequest(server_id_in, account_id); +} + +void Client::SendServerListPacket() +{ + EQApplicationPacket *outapp = server.SM->CreateServerListPacket(this); + + if(server.options.IsDumpOutPacketsOn()) + { + DumpPacket(outapp); + } + + connection->QueuePacket(outapp); + delete outapp; +} + +void Client::SendPlayResponse(EQApplicationPacket *outapp) +{ + if(server.options.IsTraceOn()) + { + server_log->Log(log_network_trace, "Sending play response for %s.", GetAccountName().c_str()); + server_log->LogPacket(log_network_trace, (const char*)outapp->pBuffer, outapp->size); + } + connection->QueuePacket(outapp); + status = cs_logged_in; +} + +void Client::GenerateKey() +{ + key.clear(); + int count = 0; + while(count < 10) + { + static const char key_selection[] = + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9' + }; + + key.append((const char*)&key_selection[MakeRandomInt(0, 35)], 1); + count++; + } +} + diff --git a/loginserver/Client.h b/loginserver/Client.h new file mode 100644 index 000000000..7ca7cf9a2 --- /dev/null +++ b/loginserver/Client.h @@ -0,0 +1,145 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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_CLIENT_H +#define EQEMU_CLIENT_H + +#include "../common/debug.h" +#include "../common/opcodemgr.h" +#include "../common/EQStreamType.h" +#include "../common/EQStreamFactory.h" +#ifndef WIN32 +#include "EQCryptoAPI.h" +#endif +#include + +using namespace std; + +enum ClientVersion +{ + cv_titanium, + cv_sod +}; + +enum ClientStatus +{ + cs_not_sent_session_ready, + cs_waiting_for_login, + cs_logged_in +}; + +enum LoginMode +{ + lm_initial = 2, + lm_from_world = 3 +}; + +/** + * Client class, controls a single client and it's + * connection to the login server. + */ +class Client +{ +public: + /** + * Constructor, sets our connection to c and version to v + */ + Client(EQStream *c, ClientVersion v); + + /** + * Destructor. + */ + ~Client() { } + + /** + * Processes the client's connection and does various actions. + */ + bool Process(); + + /** + * Sends our reply to session ready packet. + */ + void Handle_SessionReady(const char* data, unsigned int size); + + /** + * Verifies login and send a reply. + */ + void Handle_Login(const char* data, unsigned int size); + + /** + * Sends a packet to the requested server to see if the client is allowed or not. + */ + void Handle_Play(const char* data); + + /** + * Sends a server list packet to the client. + */ + void SendServerListPacket(); + + /** + * Sends the input packet to the client and clears our play response states. + */ + void SendPlayResponse(EQApplicationPacket *outapp); + + /** + * Generates a random login key for the client during login. + */ + void GenerateKey(); + + /** + * Gets the account id of this client. + */ + unsigned int GetAccountID() const { return account_id; } + + /** + * Gets the account name of this client. + */ + string GetAccountName() const { return account_name; } + + /** + * Gets the key generated at login for this client. + */ + string GetKey() const { return key; } + + /** + * Gets the server selected to be played on for this client. + */ + unsigned int GetPlayServerID() const { return play_server_id; } + + /** + * Gets the play sequence state for this client. + */ + unsigned int GetPlaySequence() const { return play_sequence_id; } + + /** + * Gets the connection for this client. + */ + EQStream *GetConnection() { return connection; } +private: + EQStream *connection; + ClientVersion version; + ClientStatus status; + + string account_name; + unsigned int account_id; + unsigned int play_server_id; + unsigned int play_sequence_id; + string key; +}; + +#endif + diff --git a/loginserver/ClientManager.cpp b/loginserver/ClientManager.cpp new file mode 100644 index 000000000..2083925d0 --- /dev/null +++ b/loginserver/ClientManager.cpp @@ -0,0 +1,207 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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 "ClientManager.h" +#include "ErrorLog.h" +#include "LoginServer.h" + +extern ErrorLog *server_log; +extern LoginServer server; +extern bool run_server; + +ClientManager::ClientManager() +{ + int titanium_port = atoi(server.config->GetVariable("Titanium", "port").c_str()); + titanium_stream = new EQStreamFactory(LoginStream, titanium_port); + titanium_ops = new RegularOpcodeManager; + if(!titanium_ops->LoadOpcodes(server.config->GetVariable("Titanium", "opcodes").c_str())) + { + server_log->Log(log_error, "ClientManager fatal error: couldn't load opcodes for Titanium file %s.", + server.config->GetVariable("Titanium", "opcodes").c_str()); + run_server = false; + } + + if(titanium_stream->Open()) + { + server_log->Log(log_network, "ClientManager listening on Titanium stream."); + } + else + { + server_log->Log(log_error, "ClientManager fatal error: couldn't open Titanium stream."); + run_server = false; + } + + int sod_port = atoi(server.config->GetVariable("SoD", "port").c_str()); + sod_stream = new EQStreamFactory(LoginStream, sod_port); + sod_ops = new RegularOpcodeManager; + if(!sod_ops->LoadOpcodes(server.config->GetVariable("SoD", "opcodes").c_str())) + { + server_log->Log(log_error, "ClientManager fatal error: couldn't load opcodes for SoD file %s.", + server.config->GetVariable("SoD", "opcodes").c_str()); + run_server = false; + } + + if(sod_stream->Open()) + { + server_log->Log(log_network, "ClientManager listening on SoD stream."); + } + else + { + server_log->Log(log_error, "ClientManager fatal error: couldn't open SoD stream."); + run_server = false; + } +} + +ClientManager::~ClientManager() +{ + if(titanium_stream) + { + titanium_stream->Close(); + delete titanium_stream; + } + + if(titanium_ops) + { + delete titanium_ops; + } + + if(sod_stream) + { + sod_stream->Close(); + delete sod_stream; + } + + if(sod_ops) + { + delete sod_ops; + } +} + +void ClientManager::Process() +{ + ProcessDisconnect(); + EQStream *cur = titanium_stream->Pop(); + while(cur) + { + struct in_addr in; + in.s_addr = cur->GetRemoteIP(); + server_log->Log(log_network, "New Titanium client connection from %s:%d", inet_ntoa(in), ntohs(cur->GetRemotePort())); + + cur->SetOpcodeManager(&titanium_ops); + Client *c = new Client(cur, cv_titanium); + clients.push_back(c); + cur = titanium_stream->Pop(); + } + + cur = sod_stream->Pop(); + while(cur) + { + struct in_addr in; + in.s_addr = cur->GetRemoteIP(); + server_log->Log(log_network, "New SoD client connection from %s:%d", inet_ntoa(in), ntohs(cur->GetRemotePort())); + + cur->SetOpcodeManager(&sod_ops); + Client *c = new Client(cur, cv_sod); + clients.push_back(c); + cur = sod_stream->Pop(); + } + + list::iterator iter = clients.begin(); + while(iter != clients.end()) + { + if((*iter)->Process() == false) + { + server_log->Log(log_client, "Client had a fatal error and had to be removed from the login."); + delete (*iter); + iter = clients.erase(iter); + } + else + { + iter++; + } + } +} + +void ClientManager::ProcessDisconnect() +{ + list::iterator iter = clients.begin(); + while(iter != clients.end()) + { + EQStream *c = (*iter)->GetConnection(); + if(c->CheckClosed()) + { + server_log->Log(log_network, "Client disconnected from the server, removing client."); + delete (*iter); + iter = clients.erase(iter); + } + else + { + iter++; + } + } +} + +void ClientManager::UpdateServerList() +{ + list::iterator iter = clients.begin(); + while(iter != clients.end()) + { + (*iter)->SendServerListPacket(); + iter++; + } +} + +void ClientManager::RemoveExistingClient(unsigned int account_id) +{ + list::iterator iter = clients.begin(); + while(iter != clients.end()) + { + if((*iter)->GetAccountID() == account_id) + { + server_log->Log(log_network, "Client attempting to log in and existing client already logged in, removing existing client."); + delete (*iter); + iter = clients.erase(iter); + } + else + { + iter++; + } + } +} + +Client *ClientManager::GetClient(unsigned int account_id) +{ + Client *cur = NULL; + int count = 0; + list::iterator iter = clients.begin(); + while(iter != clients.end()) + { + if((*iter)->GetAccountID() == account_id) + { + cur = (*iter); + count++; + } + iter++; + } + + if(count > 1) + { + server_log->Log(log_client_error, "More than one client with a given account_id existed in the client list."); + } + return cur; +} + diff --git a/loginserver/ClientManager.h b/loginserver/ClientManager.h new file mode 100644 index 000000000..4b279c116 --- /dev/null +++ b/loginserver/ClientManager.h @@ -0,0 +1,80 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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_CLIENTMANAGER_H +#define EQEMU_CLIENTMANAGER_H + +#include "../common/debug.h" +#include "../common/opcodemgr.h" +#include "../common/EQStreamType.h" +#include "../common/EQStreamFactory.h" +#include "Client.h" +#include + +using namespace std; + +/** + * Client manager class, holds all the client objects and does basic processing. + */ +class ClientManager +{ +public: + /** + * Constructor, sets up the stream factories and opcode managers. + */ + ClientManager(); + + /** + * Destructor, shuts down the streams and opcode managers. + */ + ~ClientManager(); + + /** + * Processes every client in the internal list, removes them if necessary. + */ + void Process(); + + /** + * Sends a new server list to every client. + */ + void UpdateServerList(); + + /** + * Removes a client with a certain account id. + */ + void RemoveExistingClient(unsigned int account_id); + + /** + * Gets a client (if exists) by their account id. + */ + Client *GetClient(unsigned int account_id); +private: + + /** + * Processes disconnected clients, removes them if necessary. + */ + void ProcessDisconnect(); + + list clients; + OpcodeManager *titanium_ops; + EQStreamFactory *titanium_stream; + OpcodeManager *sod_ops; + EQStreamFactory *sod_stream; +}; + +#endif + diff --git a/loginserver/Config.cpp b/loginserver/Config.cpp new file mode 100644 index 000000000..7841e441e --- /dev/null +++ b/loginserver/Config.cpp @@ -0,0 +1,215 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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 "Config.h" +#include "ErrorLog.h" + +extern ErrorLog *server_log; +/** + * Retrieves the variable we want from our title or theme + * First gets the map from the title + * Then gets the argument from the map we got from title + */ +string Config::GetVariable(string title, string parameter) +{ + map >::iterator iter = vars.find(title); + if(iter != vars.end()) + { + map::iterator arg_iter = iter->second.find(parameter); + if(arg_iter != iter->second.end()) + { + return arg_iter->second; + } + } + + return string(""); +} + +/** + * Opens a file and passes it to the tokenizer + * Then it parses the tokens returned and puts them into titles and variables. + */ +void Config::Parse(const char *file_name) +{ + if(file_name == NULL) + { + server_log->Log(log_error, "Config::Parse(), file_name passed was null."); + return; + } + + vars.clear(); + FILE *input = fopen(file_name, "r"); + if(input) + { + list tokens; + Tokenize(input, tokens); + + char mode = 0; + string title, param, arg; + list::iterator iter = tokens.begin(); + while(iter != tokens.end()) + { + if((*iter).compare("[") == 0) + { + title.clear(); + bool first = true; + iter++; + if(iter == tokens.end()) + { + server_log->Log(log_error, "Config::Parse(), EOF before title done parsing."); + fclose(input); + vars.clear(); + return; + } + + while((*iter).compare("]") != 0 && iter != tokens.end()) + { + if(!first) + { + title += " "; + } + else + { + first = false; + } + + title += (*iter); + iter++; + } + iter++; + } + + if(mode == 0) + { + param = (*iter); + mode++; + } + else if(mode == 1) + { + mode++; + if((*iter).compare("=") != 0) + { + server_log->Log(log_error, "Config::Parse(), invalid parse token where = should be."); + fclose(input); + vars.clear(); + return; + } + } + else + { + arg = (*iter); + mode = 0; + map >::iterator map_iter = vars.find(title); + if(map_iter != vars.end()) + { + map_iter->second[param] = arg; + vars[title] = map_iter->second; + } + else + { + map var_map; + var_map[param] = arg; + vars[title] = var_map; + } + } + iter++; + } + fclose(input); + } + else + { + server_log->Log(log_error, "Config::Parse(), file was unable to be opened for parsing."); + } +} + +/** + * Pretty basic lexical analyzer + * Breaks up the input character stream into tokens and puts them into the list provided. + * Ignores # as a line comment + */ +void Config::Tokenize(FILE *input, list &tokens) +{ + char c = fgetc(input); + string lexeme; + + while(c != EOF) + { + if(isspace(c)) + { + if(lexeme.size() > 0) + { + tokens.push_back(lexeme); + lexeme.clear(); + } + c = fgetc(input); + continue; + } + + if(isalnum(c)) + { + lexeme.append((const char *)&c, 1); + c = fgetc(input); + continue; + } + + switch(c) + { + case '#': + { + if(lexeme.size() > 0) + { + tokens.push_back(lexeme); + lexeme.clear(); + } + + while(c != '\n' && c != EOF) + { + c = fgetc(input); + } + break; + } + case '[': + case ']': + case '=': + { + if(lexeme.size() > 0) + { + tokens.push_back(lexeme); + lexeme.clear(); + } + + lexeme.append((const char *)&c, 1); + tokens.push_back(lexeme); + lexeme.clear(); + break; + } + default: + { + lexeme.append((const char *)&c, 1); + } + } + + c = fgetc(input); + } + + if(lexeme.size() > 0) + { + tokens.push_back(lexeme); + } +} + diff --git a/loginserver/Config.h b/loginserver/Config.h new file mode 100644 index 000000000..37ce9902a --- /dev/null +++ b/loginserver/Config.h @@ -0,0 +1,63 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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_CONFIG_H +#define EQEMU_CONFIG_H + +#include +#include +#include +#include + +using namespace std; + +/** + * Keeps track of all the configuration for the application with a small parser. + * Note: This is not a thread safe class, but only parse writes to variables in the class. + * Thus making it mostly safe so long as you're careful with where you call Parse() + */ +class Config +{ +public: + Config() { } + ~Config() { } + + /** + * Parses the selected file for variables, will clear current variables if selected. + */ + virtual void Parse(const char *file_name); + + /** + * Gets a variable if it exists. + */ + string GetVariable(string title, string parameter); + +protected: + map > vars; + +private: + /** + * Breaks our input up into tokens for Parse(). + * This is private because it's not intended to be overloaded by a derived class which + * may get it's input from other places than a C file pointer. (a http get request for example). + * The programmer of a derived class would be expected to make their own Tokenize function for their own Parse(). + */ + void Tokenize(FILE* input, list &tokens); +}; + +#endif + diff --git a/loginserver/Database.h b/loginserver/Database.h new file mode 100644 index 000000000..08bc3b70d --- /dev/null +++ b/loginserver/Database.h @@ -0,0 +1,81 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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_DATABASE_H +#define EQEMU_DATABASE_H + +#include + +using namespace std; + +#define EQEMU_MYSQL_ENABLED +//#define EQEMU_POSTGRESQL_ENABLED + +/** + * Base database class, intended to be extended. + */ +class Database +{ +public: + Database() : user(""), pass(""), host(""), port(""), name("") { } + virtual ~Database() { } + + /** + * Returns true if the database successfully connected. + */ + virtual bool IsConnected() { return false; } + + /** + * Retrieves the login data (password hash and account id) from the account name provided + * Needed for client login procedure. + * Returns true if the record was found, false otherwise. + */ + virtual bool GetLoginDataFromAccountName(string name, string &password, unsigned int &id) { return false; } + + /** + * Retrieves the world registration from the long and short names provided. + * Needed for world login procedure. + * Returns true if the record was found, false otherwise. + */ + virtual bool GetWorldRegistration(string long_name, string short_name, unsigned int &id, string &desc, unsigned int &list_id, + unsigned int &trusted, string &list_desc, string &account, string &password) { return false; } + + /** + * Updates the ip address of the client with account id = id + */ + virtual void UpdateLSAccountData(unsigned int id, string ip_address) { } + + /** + * Updates or creates the login server account with info from world server + */ + virtual void UpdateLSAccountInfo(unsigned int id, string name, string password, string email) { } + + /** + * Updates the ip address of the world with account id = id + */ + virtual void UpdateWorldRegistration(unsigned int id, string long_name, string ip_address) { } + + /** + * Creates new world registration for unregistered servers and returns new id + */ + virtual bool CreateWorldRegistration(string long_name, string short_name, unsigned int &id) { return false; } +protected: + string user, pass, host, port, name; +}; + +#endif + diff --git a/loginserver/DatabaseMySQL.cpp b/loginserver/DatabaseMySQL.cpp new file mode 100644 index 000000000..5409405e6 --- /dev/null +++ b/loginserver/DatabaseMySQL.cpp @@ -0,0 +1,290 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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 "Database.h" + +#ifdef EQEMU_MYSQL_ENABLED +#include "DatabaseMySQL.h" +#include "ErrorLog.h" +#include "LoginServer.h" + +extern ErrorLog *server_log; +extern LoginServer server; + +#pragma comment(lib, "mysqlclient.lib") + +DatabaseMySQL::DatabaseMySQL(string user, string pass, string host, string port, string name) +{ + this->user = user; + this->pass = pass; + this->host = host; + this->name = name; + + db = mysql_init(NULL); + if(db) + { + my_bool r = 1; + mysql_options(db, MYSQL_OPT_RECONNECT, &r); + if(!mysql_real_connect(db, host.c_str(), user.c_str(), pass.c_str(), name.c_str(), atoi(port.c_str()), NULL, 0)) + { + mysql_close(db); + server_log->Log(log_database, "Failed to connect to MySQL database."); + } + } + else + { + server_log->Log(log_database, "Failed to create db object in MySQL database."); + } +} + +DatabaseMySQL::~DatabaseMySQL() +{ + if(db) + { + mysql_close(db); + } +} + +bool DatabaseMySQL::GetLoginDataFromAccountName(string name, string &password, unsigned int &id) +{ + if(!db) + { + return false; + } + + MYSQL_RES *res; + MYSQL_ROW row; + stringstream query(stringstream::in | stringstream::out); + query << "SELECT LoginServerID, AccountPassword FROM " << server.options.GetAccountTable() << " WHERE AccountName = '"; + query << name; + query << "'"; + + if(mysql_query(db, query.str().c_str()) != 0) + { + server_log->Log(log_database, "Mysql query failed: %s", query.str().c_str()); + return false; + } + + res = mysql_use_result(db); + + if(res) + { + while((row = mysql_fetch_row(res)) != NULL) + { + id = atoi(row[0]); + password = row[1]; + mysql_free_result(res); + return true; + } + } + + server_log->Log(log_database, "Mysql query returned no result: %s", query.str().c_str()); + return false; +} + +bool DatabaseMySQL::GetWorldRegistration(string long_name, string short_name, unsigned int &id, string &desc, unsigned int &list_id, + unsigned int &trusted, string &list_desc, string &account, string &password) +{ + if(!db) + { + return false; + } + + MYSQL_RES *res; + MYSQL_ROW row; + char escaped_short_name[101]; + unsigned long length; + length = mysql_real_escape_string(db, escaped_short_name, short_name.substr(0, 100).c_str(), short_name.substr(0, 100).length()); + escaped_short_name[length+1] = 0; + stringstream query(stringstream::in | stringstream::out); + query << "SELECT WSR.ServerID, WSR.ServerTagDescription, WSR.ServerTrusted, SLT.ServerListTypeID, "; + query << "SLT.ServerListTypeDescription, WSR.ServerAdminID FROM " << server.options.GetWorldRegistrationTable(); + query << " AS WSR JOIN " << server.options.GetWorldServerTypeTable() << " AS SLT ON WSR.ServerListTypeID = SLT.ServerListTypeID"; + query << " WHERE WSR.ServerShortName = '"; + query << escaped_short_name; + query << "'"; + + if(mysql_query(db, query.str().c_str()) != 0) + { + server_log->Log(log_database, "Mysql query failed: %s", query.str().c_str()); + return false; + } + + res = mysql_use_result(db); + if(res) + { + if((row = mysql_fetch_row(res)) != NULL) + { + id = atoi(row[0]); + desc = row[1]; + trusted = atoi(row[2]); + list_id = atoi(row[3]); + list_desc = row[4]; + int db_account_id = atoi(row[5]); + mysql_free_result(res); + + if(db_account_id > 0) + { + stringstream query(stringstream::in | stringstream::out); + query << "SELECT AccountName, AccountPassword FROM " << server.options.GetWorldAdminRegistrationTable(); + query << " WHERE ServerAdminID = " << db_account_id; + + if(mysql_query(db, query.str().c_str()) != 0) + { + server_log->Log(log_database, "Mysql query failed: %s", query.str().c_str()); + return false; + } + + res = mysql_use_result(db); + if(res) + { + if((row = mysql_fetch_row(res)) != NULL) + { + account = row[0]; + password = row[1]; + mysql_free_result(res); + return true; + } + } + + server_log->Log(log_database, "Mysql query returned no result: %s", query.str().c_str()); + return false; + } + return true; + } + } + + server_log->Log(log_database, "Mysql query returned no result: %s", query.str().c_str()); + return false; +} + +void DatabaseMySQL::UpdateLSAccountData(unsigned int id, string ip_address) +{ + if(!db) + { + return; + } + + stringstream query(stringstream::in | stringstream::out); + query << "UPDATE " << server.options.GetAccountTable() << " SET LastIPAddress = '"; + query << ip_address; + query << "', LastLoginDate = now() where LoginServerID = "; + query << id; + + if(mysql_query(db, query.str().c_str()) != 0) + { + server_log->Log(log_database, "Mysql query failed: %s", query.str().c_str()); + } +} + +void DatabaseMySQL::UpdateLSAccountInfo(unsigned int id, string name, string password, string email) +{ + if(!db) + { + return; + } + + stringstream query(stringstream::in | stringstream::out); + query << "REPLACE " << server.options.GetAccountTable() << " SET LoginServerID = "; + query << id << ", AccountName = '" << name << "', AccountPassword = sha('"; + query << password << "'), AccountCreateDate = now(), AccountEmail = '" << email; + query << "', LastIPAddress = '0.0.0.0', LastLoginDate = now()"; + + if(mysql_query(db, query.str().c_str()) != 0) + { + server_log->Log(log_database, "Mysql query failed: %s", query.str().c_str()); + } +} + +void DatabaseMySQL::UpdateWorldRegistration(unsigned int id, string long_name, string ip_address) +{ + if(!db) + { + return; + } + + char escaped_long_name[101]; + unsigned long length; + length = mysql_real_escape_string(db, escaped_long_name, long_name.substr(0, 100).c_str(), long_name.substr(0, 100).length()); + escaped_long_name[length+1] = 0; + stringstream query(stringstream::in | stringstream::out); + query << "UPDATE " << server.options.GetWorldRegistrationTable() << " SET ServerLastLoginDate = now(), ServerLastIPAddr = '"; + query << ip_address; + query << "', ServerLongName = '"; + query << escaped_long_name; + query << "' WHERE ServerID = "; + query << id; + + if(mysql_query(db, query.str().c_str()) != 0) + { + server_log->Log(log_database, "Mysql query failed: %s", query.str().c_str()); + } +} + +bool DatabaseMySQL::CreateWorldRegistration(string long_name, string short_name, unsigned int &id) +{ + if(!db) + { + return false; + } + + MYSQL_RES *res; + MYSQL_ROW row; + char escaped_long_name[201]; + char escaped_short_name[101]; + unsigned long length; + length = mysql_real_escape_string(db, escaped_long_name, long_name.substr(0, 100).c_str(), long_name.substr(0, 100).length()); + escaped_long_name[length+1] = 0; + length = mysql_real_escape_string(db, escaped_short_name, short_name.substr(0, 100).c_str(), short_name.substr(0, 100).length()); + escaped_short_name[length+1] = 0; + stringstream query(stringstream::in | stringstream::out); + query << "SELECT max(ServerID) FROM " << server.options.GetWorldRegistrationTable(); + + if(mysql_query(db, query.str().c_str()) != 0) + { + server_log->Log(log_database, "Mysql query failed: %s", query.str().c_str()); + return false; + } + + res = mysql_use_result(db); + if(res) + { + if((row = mysql_fetch_row(res)) != NULL) + { + id = atoi(row[0]) + 1; + mysql_free_result(res); + + stringstream query(stringstream::in | stringstream::out); + query << "INSERT INTO " << server.options.GetWorldRegistrationTable() << " SET ServerID = " << id; + query << ", ServerLongName = '" << escaped_long_name << "', ServerShortName = '" << escaped_short_name; + query << "', ServerListTypeID = 3, ServerAdminID = 0, ServerTrusted = 0, ServerTagDescription = ''"; + + if(mysql_query(db, query.str().c_str()) != 0) + { + server_log->Log(log_database, "Mysql query failed: %s", query.str().c_str()); + return false; + } + return true; + } + } + server_log->Log(log_database, "World registration did not exist in the database for %s %s", long_name.c_str(), short_name.c_str()); + return false; +} + +#endif + diff --git a/loginserver/DatabaseMySQL.h b/loginserver/DatabaseMySQL.h new file mode 100644 index 000000000..6bb0c6682 --- /dev/null +++ b/loginserver/DatabaseMySQL.h @@ -0,0 +1,98 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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_DATABASEMYSQL_H +#define EQEMU_DATABASEMYSQL_H + +#include "Database.h" +#ifdef EQEMU_MYSQL_ENABLED + +#include +#include +#include +#include + +using namespace std; + +/** + * Mysql Database class + */ +class DatabaseMySQL : public Database +{ +public: + /** + * Constructor, sets our database to null. + */ + DatabaseMySQL() { db = NULL; } + + /** + * Constructor, tries to set our database to connect to the supplied options. + */ + DatabaseMySQL(string user, string pass, string host, string port, string name); + + /** + * Destructor, frees our database if needed. + */ + virtual ~DatabaseMySQL(); + + /** + * @return Returns true if the database successfully connected. + */ + virtual bool IsConnected() { return (db != NULL); } + + /** + * Retrieves the login data (password hash and account id) from the account name provided + * Needed for client login procedure. + * Returns true if the record was found, false otherwise. + */ + virtual bool GetLoginDataFromAccountName(string name, string &password, unsigned int &id); + + /** + * Retrieves the world registration from the long and short names provided. + * Needed for world login procedure. + * Returns true if the record was found, false otherwise. + */ + virtual bool GetWorldRegistration(string long_name, string short_name, unsigned int &id, string &desc, unsigned int &list_id, + unsigned int &trusted, string &list_desc, string &account, string &password); + + /** + * Updates the ip address of the client with account id = id + */ + virtual void UpdateLSAccountData(unsigned int id, string ip_address); + + /** + * Updates or creates the login server account with info from world server + */ + virtual void UpdateLSAccountInfo(unsigned int id, string name, string password, string email); + + /** + * Updates the ip address of the world with account id = id + */ + virtual void UpdateWorldRegistration(unsigned int id, string long_name, string ip_address); + + /** + * Creates new world registration for unregistered servers and returns new id + */ + virtual bool CreateWorldRegistration(string long_name, string short_name, unsigned int &id); +protected: + string user, pass, host, port, name; + MYSQL *db; +}; + +#endif +#endif + diff --git a/loginserver/DatabasePostgreSQL.cpp b/loginserver/DatabasePostgreSQL.cpp new file mode 100644 index 000000000..a74787d73 --- /dev/null +++ b/loginserver/DatabasePostgreSQL.cpp @@ -0,0 +1,234 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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 "Database.h" + +#ifdef EQEMU_POSTGRESQL_ENABLED +#include "DatabasePostgreSQL.h" +#include "ErrorLog.h" +#include "LoginServer.h" + +extern ErrorLog *server_log; +extern LoginServer server; + +#pragma comment(lib, "libpq.lib") + +DatabasePostgreSQL::DatabasePostgreSQL(string user, string pass, string host, string port, string name) +{ + db = NULL; + db = PQsetdbLogin(host.c_str(), port.c_str(), NULL, NULL, name.c_str(), user.c_str(), pass.c_str()); + if(!db) + { + server_log->Log(log_database, "Failed to connect to PostgreSQL Database."); + } + + if(PQstatus(db) != CONNECTION_OK) + { + server_log->Log(log_database, "Failed to connect to PostgreSQL Database."); + PQfinish(db); + db = NULL; + } +} + +DatabasePostgreSQL::~DatabasePostgreSQL() +{ + if(db) + { + PQfinish(db); + } +} + +bool DatabasePostgreSQL::GetLoginDataFromAccountName(string name, string &password, unsigned int &id) +{ + if(!db) + { + return false; + } + + /** + * PostgreSQL doesn't have automatic reconnection option like mysql + * but it's easy to check and reconnect + */ + if(PQstatus(db) != CONNECTION_OK) + { + PQreset(db); + if(PQstatus(db) != CONNECTION_OK) + { + return false; + } + } + + stringstream query(stringstream::in | stringstream::out); + query << "SELECT LoginServerID, AccountPassword FROM " << server.options.GetAccountTable() << " WHERE AccountName = '"; + query << name; + query << "'"; + + PGresult *res = PQexec(db, query.str().c_str()); + + char *error = PQresultErrorMessage(res); + if(strlen(error) > 0) + { + server_log->Log(log_database, "Database error in DatabasePostgreSQL::GetLoginDataFromAccountName(): %s", error); + PQclear(res); + return false; + } + + if(PQntuples(res) > 0) + { + id = atoi(PQgetvalue(res, 0, 0)); + password = PQgetvalue(res, 0, 1); + PQclear(res); + return true; + } + + PQclear(res); + return false; +} + +bool DatabasePostgreSQL::GetWorldRegistration(string long_name, string short_name, unsigned int &id, string &desc, unsigned int &list_id, + unsigned int &trusted, string &list_desc, string &account, string &password) +{ + if(!db) + { + return false; + } + + /** + * PostgreSQL doesn't have automatic reconnection option like mysql + * but it's easy to check and reconnect + */ + if(PQstatus(db) != CONNECTION_OK) + { + PQreset(db); + if(PQstatus(db) != CONNECTION_OK) + { + return false; + } + } + + stringstream query(stringstream::in | stringstream::out); + query << "SELECT WSR.ServerID, WSR.ServerTagDescription, WSR.ServerTrusted, SLT.ServerListTypeID, "; + query << "SLT.ServerListTypeDescription, SAR.AccountName, SAR.AccountPassword FROM " << server.options.GetWorldRegistrationTable(); + query << " AS WSR JOIN " << server.options.GetWorldServerTypeTable() << " AS SLT ON WSR.ServerListTypeID = SLT.ServerListTypeID JOIN "; + query << server.options.GetWorldAdminRegistrationTable() << " AS SAR ON WSR.ServerAdminID = SAR.ServerAdminID WHERE WSR.ServerShortName"; + query << " = '"; + query << short_name; + query << "'"; + + PGresult *res = PQexec(db, query.str().c_str()); + + char *error = PQresultErrorMessage(res); + if(strlen(error) > 0) + { + server_log->Log(log_database, "Database error in DatabasePostgreSQL::GetWorldRegistration(): %s", error); + PQclear(res); + return false; + } + + if(PQntuples(res) > 0) + { + id = atoi(PQgetvalue(res, 0, 0)); + desc = PQgetvalue(res, 0, 1); + trusted = atoi(PQgetvalue(res, 0, 2)); + list_id = atoi(PQgetvalue(res, 0, 3)); + list_desc = PQgetvalue(res, 0, 4); + account = PQgetvalue(res, 0, 5); + password = PQgetvalue(res, 0, 6); + + PQclear(res); + return true; + } + + PQclear(res); + return false; +} + +void DatabasePostgreSQL::UpdateLSAccountData(unsigned int id, string ip_address) +{ + if(!db) + { + return; + } + + /** + * PostgreSQL doesn't have automatic reconnection option like mysql + * but it's easy to check and reconnect + */ + if(PQstatus(db) != CONNECTION_OK) + { + PQreset(db); + if(PQstatus(db) != CONNECTION_OK) + { + return; + } + } + + stringstream query(stringstream::in | stringstream::out); + query << "UPDATE " << server.options.GetAccountTable() << " SET LastIPAddress = '"; + query << ip_address; + query << "', LastLoginDate = current_date where LoginServerID = "; + query << id; + PGresult *res = PQexec(db, query.str().c_str()); + + char *error = PQresultErrorMessage(res); + if(strlen(error) > 0) + { + server_log->Log(log_database, "Database error in DatabasePostgreSQL::GetLoginDataFromAccountName(): %s", error); + } + PQclear(res); +} + +void DatabasePostgreSQL::UpdateWorldRegistration(unsigned int id, string long_name, string ip_address) +{ + if(!db) + { + return; + } + + /** + * PostgreSQL doesn't have automatic reconnection option like mysql + * but it's easy to check and reconnect + */ + if(PQstatus(db) != CONNECTION_OK) + { + PQreset(db); + if(PQstatus(db) != CONNECTION_OK) + { + return; + } + } + + stringstream query(stringstream::in | stringstream::out); + query << "UPDATE " << server.options.GetWorldRegistrationTable() << " SET ServerLastLoginDate = current_date, ServerLastIPAddr = '"; + query << ip_address; + query << "', ServerLongName = '"; + query << long_name; + query << "' where ServerID = "; + query << id; + PGresult *res = PQexec(db, query.str().c_str()); + + char *error = PQresultErrorMessage(res); + if(strlen(error) > 0) + { + server_log->Log(log_database, "Database error in DatabasePostgreSQL::GetLoginDataFromAccountName(): %s", error); + } + PQclear(res); +} + +#endif + diff --git a/loginserver/DatabasePostgreSQL.h b/loginserver/DatabasePostgreSQL.h new file mode 100644 index 000000000..1b7638d34 --- /dev/null +++ b/loginserver/DatabasePostgreSQL.h @@ -0,0 +1,93 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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_DATABASEPOSTGRESQL_H +#define EQEMU_DATABASEPOSTGRESQL_H + +#include "Database.h" +#ifdef EQEMU_POSTGRESQL_ENABLED + +#include +#include +#include +#include + +using namespace std; + +/** + * PostgreSQL Database class + */ +class DatabasePostgreSQL : public Database +{ +public: + /** + * Constructor, sets our database to null. + */ + DatabasePostgreSQL() { db = NULL; } + + /** + * Constructor, tries to set our database to connect to the supplied options. + */ + DatabasePostgreSQL(string user, string pass, string host, string port, string name); + + /** + * Destructor, frees our database if needed. + */ + virtual ~DatabasePostgreSQL(); + + /** + * Returns true if the database successfully connected. + */ + virtual bool IsConnected() { return (db != NULL); } + + /** + * Retrieves the login data (password hash and account id) from the account name provided + * Needed for client login procedure. + * Returns true if the record was found, false otherwise. + */ + virtual bool GetLoginDataFromAccountName(string name, string &password, unsigned int &id); + + /** + * Retrieves the world registration from the long and short names provided. + * Needed for world login procedure. + * Returns true if the record was found, false otherwise. + */ + virtual bool GetWorldRegistration(string long_name, string short_name, unsigned int &id, string &desc, unsigned int &list_id, + unsigned int &trusted, string &list_desc, string &account, string &password); + + /** + * Updates the ip address of the client with account id = id + */ + virtual void UpdateLSAccountData(unsigned int id, string ip_address); + + /** + * Updates the ip address of the world with account id = id + */ + virtual void UpdateWorldRegistration(unsigned int id, string long_name, string ip_address); + + /** + * Creates new world registration for unregistered servers and returns new id + */ + virtual bool CreateWorldRegistration(string long_name, string short_name, unsigned int &id); +protected: + string user, pass, host, port, name; + PGconn *db; +}; + +#endif +#endif + diff --git a/loginserver/EQCryptoAPI.h b/loginserver/EQCryptoAPI.h new file mode 100644 index 000000000..b0a890193 --- /dev/null +++ b/loginserver/EQCryptoAPI.h @@ -0,0 +1,25 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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 EQEMUCAPI__H +#define EQEMUCAPI__H + +char* DecryptUsernamePassword(const char* encryptedBuffer, unsigned int bufferSize, int mode); +char* Encrypt(const char* buffer, unsigned int bufferSize, unsigned int &outSize); +void _HeapDeleteCharBuffer(char *buffer); + +#endif diff --git a/loginserver/Encryption.cpp b/loginserver/Encryption.cpp new file mode 100644 index 000000000..0a7986fc0 --- /dev/null +++ b/loginserver/Encryption.cpp @@ -0,0 +1,144 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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 "Encryption.h" +#include "ErrorLog.h" + +extern ErrorLog *server_log; + +bool Encryption::LoadCrypto(string name) +{ + if(!Load(name.c_str())) + { + server_log->Log(log_error, "Failed to load %s from the operating system.", name.c_str()); + return false; + } + else + { + encrypt_func = (DLLFUNC_Encrypt)GetSym("Encrypt"); + if(encrypt_func == NULL) + { + server_log->Log(log_error, "Failed to attach Encrypt."); + Unload(); + return false; + } + decrypt_func = (DLLFUNC_DecryptUsernamePassword)GetSym("DecryptUsernamePassword"); + if(decrypt_func == NULL) + { + server_log->Log(log_error, "Failed to attach DecryptUsernamePassword."); + Unload(); + return false; + } + delete_func = (DLLFUNC_HeapDelete)GetSym("_HeapDeleteCharBuffer"); + if(delete_func == NULL) + { + server_log->Log(log_error, "Failed to attach _HeapDeleteCharBuffer."); + Unload(); + return false; + } + } + return true; +} + +char *Encryption::DecryptUsernamePassword(const char* encrypted_buffer, unsigned int buffer_size, int mode) +{ + if(decrypt_func) + { + return decrypt_func(encrypted_buffer, buffer_size, mode); + } + return NULL; +} + +char *Encryption::Encrypt(const char* buffer, unsigned int buffer_size, unsigned int &out_size) +{ + if(encrypt_func) + { + return encrypt_func(buffer, buffer_size, out_size); + } + return NULL; +} + +void Encryption::DeleteHeap(char *buffer) +{ + if(delete_func) + { + delete_func(buffer); + } +} + +bool Encryption::Load(const char *name) +{ + SetLastError(0); +#ifdef UNICODE + int name_length = strlen(name); + int wide_length = MultiByteToWideChar(CP_ACP, 0, name, name_length+1, 0, 0); + WCHAR *wide_string = new WCHAR[wide_length]; + MultiByteToWideChar(CP_ACP, 0, name, name_length+1, wide_string, wide_length); + + h_dll = LoadLibrary(wide_string); + delete[] wide_string; +#else + h_dll = LoadLibrary(name); +#endif + + if(h_dll == NULL) + { + return false; + } + else + { + SetLastError(0); + } + + return true; +} + +void Encryption::Unload() +{ + if(h_dll) + { + FreeLibrary(h_dll); + h_dll = NULL; + } +} + +bool Encryption::GetSym(const char *name, void **sym) +{ + if(Loaded()) + { + *sym = GetProcAddress(h_dll, name); + return(*sym != NULL); + } + else + { + return false; + } +} + +void *Encryption::GetSym(const char *name) +{ + if(Loaded()) + { + return GetProcAddress(h_dll, name); + } + else + { + return NULL; + } +} + diff --git a/loginserver/Encryption.h b/loginserver/Encryption.h new file mode 100644 index 000000000..b3fe3a506 --- /dev/null +++ b/loginserver/Encryption.h @@ -0,0 +1,106 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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_ENCRYPTION_H +#define EQEMU_ENCRYPTION_H + +#ifdef WIN32 +#include +#include + +using namespace std; + +typedef char*(*DLLFUNC_DecryptUsernamePassword)(const char*, unsigned int, int); +typedef char*(*DLLFUNC_Encrypt)(const char*, unsigned int, unsigned int&); +typedef void(*DLLFUNC_HeapDelete)(char*); + +/** + * Basic windows encryption plugin. + * Handles the managment of the plugin. + */ +class Encryption +{ +public: + /** + * Constructor, sets all member pointers to NULL. + */ + Encryption() : h_dll(NULL), encrypt_func(NULL), decrypt_func(NULL), delete_func(NULL) { }; + + /** + * Destructor, if it's loaded it unloads this library. + */ + ~Encryption() { if(Loaded()) { Unload(); } } + + /** + * Returns true if the dll is loaded, otherwise false. + */ + inline bool Loaded() { return (h_dll != NULL); } + + /** + * Loads the plugin. + * True if there are no errors, false if there was an error. + */ + bool LoadCrypto(string name); + + /** + * Wrapper around the plugin's decrypt function. + */ + char* DecryptUsernamePassword(const char* encryptedBuffer, unsigned int bufferSize, int mode); + + /** + * Wrapper around the plugin's encrypt function. + */ + char* Encrypt(const char* buffer, unsigned int bufferSize, unsigned int &outSize); + + /** + * Wrapper around the plugin's delete function. + */ + void DeleteHeap(char* buffer); + +private: + /** + * Loads the named dll into memory. + * Returns true if there were no errors, otherwise return false. + */ + bool Load(const char *name); + + /** + * Frees the dll from memory if it's loaded. + */ + void Unload(); + + /** + * Similar in function to *sym = GetProcAddress(h_dll, name). + * Returns true if there were no errors, false otherwise. + */ + bool GetSym(const char *name, void **sym); + + /** + * Similar in function to return GetProcAddress(h_dll, name). + * Returns a pointer to the function if it is found, null on an error. + */ + void *GetSym(const char *name); + + HINSTANCE h_dll; + DLLFUNC_Encrypt encrypt_func; + DLLFUNC_DecryptUsernamePassword decrypt_func; + DLLFUNC_HeapDelete delete_func; +}; + +#endif +#endif + diff --git a/loginserver/ErrorLog.cpp b/loginserver/ErrorLog.cpp new file mode 100644 index 000000000..9ec6db87a --- /dev/null +++ b/loginserver/ErrorLog.cpp @@ -0,0 +1,209 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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 +#include "ErrorLog.h" + +const char *eqLogTypes[_log_largest_type] = +{ + "Debug", + "Error", + "Database", + "Network", + "Network Trace", + "Network Error", + "World", + "World Error", + "Client", + "Client Error" +}; + +ErrorLog::ErrorLog(const char* file_name) +{ + log_mutex = new Mutex(); + error_log = fopen(file_name, "w"); +} + +ErrorLog::~ErrorLog() +{ + log_mutex->lock(); + if(error_log) + { + fclose(error_log); + } + log_mutex->unlock(); + delete log_mutex; +} + +void ErrorLog::Log(eqLogType type, const char *message, ...) +{ + if(type >= _log_largest_type) + { + return; + } + + va_list argptr; + char *buffer = new char[4096]; + va_start(argptr, message); + vsnprintf(buffer, 4096, message, argptr); + va_end(argptr); + + time_t m_clock; + struct tm *m_time; + time(&m_clock); + m_time = localtime(&m_clock); + + log_mutex->lock(); + printf("[%s] [%02d.%02d.%02d - %02d:%02d:%02d] %s\n", + eqLogTypes[type], + m_time->tm_mon+1, + m_time->tm_mday, + m_time->tm_year%100, + m_time->tm_hour, + m_time->tm_min, + m_time->tm_sec, + buffer); + + if(error_log) + { + fprintf(error_log, "[%s] [%02d.%02d.%02d - %02d:%02d:%02d] %s\n", + eqLogTypes[type], + m_time->tm_mon+1, + m_time->tm_mday, + m_time->tm_year%100, + m_time->tm_hour, + m_time->tm_min, + m_time->tm_sec, + buffer); + fflush(error_log); + } + + log_mutex->unlock(); + delete[] buffer; +} + +void ErrorLog::LogPacket(eqLogType type, const char *data, size_t size) +{ + if(type >= _log_largest_type) + { + return; + } + + log_mutex->lock(); + time_t m_clock; + struct tm *m_time; + time(&m_clock); + m_time = localtime(&m_clock); + + log_mutex->lock(); + printf("[%s] [%02d.%02d.%02d - %02d:%02d:%02d] dumping packet of size %u:\n", + eqLogTypes[type], + m_time->tm_mon+1, + m_time->tm_mday, + m_time->tm_year%100, + m_time->tm_hour, + m_time->tm_min, + m_time->tm_sec, + (unsigned int)size); + + if(error_log) + { + fprintf(error_log, "[%s] [%02d.%02d.%02d - %02d:%02d:%02d] dumping packet of size %u\n", + eqLogTypes[type], + m_time->tm_mon+1, + m_time->tm_mday, + m_time->tm_year%100, + m_time->tm_hour, + m_time->tm_min, + m_time->tm_sec, + (unsigned int)size); + } + + char ascii[17]; //16 columns + 1 null term + memset(ascii, 0, 17); + + size_t j = 0; + size_t i = 0; + for(; i < size; ++i) + { + if(i % 16 == 0) + { + if(i != 0) + { + printf(" | %s\n", ascii); + if(error_log) + { + fprintf(error_log, " | %s\n", ascii); + } + } + printf("%.4u: ", (unsigned int)i); + memset(ascii, 0, 17); + j = 0; + } + else if(i % 8 == 0) + { + printf("- "); + if(error_log) + { + fprintf(error_log, "- "); + } + } + + printf("%02X ", (unsigned int)data[i]); + if(error_log) + { + fprintf(error_log, "%02X ", (unsigned int)data[i]); + } + + if(data[i] >= 32 && data[i] < 127) + { + ascii[j++] = data[i]; + } + else + { + ascii[j++] = '.'; + } + } + + size_t k = (i - 1) % 16; + if(k < 8) + { + printf(" "); + if(error_log) + { + fprintf(error_log, " "); + } + } + + for(size_t h = k + 1; h < 16; ++h) + { + printf(" "); + if(error_log) + { + fprintf(error_log, " "); + } + } + + printf(" | %s\n", ascii); + if(error_log) + { + fprintf(error_log, " | %s\n", ascii); + fflush(error_log); + } + + log_mutex->unlock(); +} diff --git a/loginserver/ErrorLog.h b/loginserver/ErrorLog.h new file mode 100644 index 000000000..13f2a2ab0 --- /dev/null +++ b/loginserver/ErrorLog.h @@ -0,0 +1,81 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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_ERROR_LOG_H +#define EQEMU_ERROR_LOG_H + +#include +#include +#include +#include + +#include "../common/Mutex.h" + +using namespace std; + +/** + * Dictates the log type specified in ErrorLog for Log(...) + */ +enum eqLogType +{ + log_debug, + log_error, + log_database, + log_network, + log_network_trace, + log_network_error, + log_world, + log_world_error, + log_client, + log_client_error, + _log_largest_type +}; + +/** + * Basic error logging class. + * Thread safe logging class that records time and date to both a file and to console(if exists). + */ +class ErrorLog +{ +public: + /** + * Constructor: opens the log file for writing and creates our mutex for writing to the log. + */ + ErrorLog(const char* file_name); + + /** + * Closes the file and destroys the mutex. + */ + ~ErrorLog(); + + /** + * Writes to the log system a variable message. + */ + void Log(eqLogType type, const char *message, ...); + + /** + * Writes to the log system a packet. + */ + void LogPacket(eqLogType type, const char *data, size_t size); + +protected: + Mutex *log_mutex; + FILE* error_log; +}; + +#endif + diff --git a/loginserver/LoginServer.h b/loginserver/LoginServer.h new file mode 100644 index 000000000..c4075ea62 --- /dev/null +++ b/loginserver/LoginServer.h @@ -0,0 +1,60 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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_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" + +/** + * Login server struct, contains every variable for the server that needs to exist + * outside the scope of main(). + */ +struct LoginServer +{ +public: + /** + * I don't really like how this looks with all the ifdefs... + * but it's the most trivial way to do this. + */ +#ifdef WIN32 + LoginServer() : config(NULL), db(NULL), eq_crypto(NULL), SM(NULL) { } +#else + LoginServer() : config(NULL), db(NULL) { } +#endif + + Config *config; + Database *db; + Options options; + ServerManager *SM; + ClientManager *CM; + +#ifdef WIN32 + Encryption *eq_crypto; +#endif +}; + +#endif + diff --git a/loginserver/LoginStructures.h b/loginserver/LoginStructures.h new file mode 100644 index 000000000..cee9a7665 --- /dev/null +++ b/loginserver/LoginStructures.h @@ -0,0 +1,115 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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_LOGINSTRUCTURES_H +#define EQEMU_LOGINSTRUCTURES_H + +#pragma pack(1) + +struct LoginChatMessage_Struct { + short Unknown0; + uint32 Unknown1; + uint32 Unknown2; + uint32 Unknown3; + uint8 Unknown4; + char ChatMessage[1]; +}; + +struct LoginLoginRequest_Struct { + short unknown1; + short unknown2; + short unknown3; + short unknown4; + short unknown5; + char unknown6[16]; +}; + +struct LoginLoginAccepted_Struct { + short unknown1; + short unknown2; + short unknown3; + short unknown4; + short unknown5; + char encrypt[80]; +}; + +struct Login_ReplyBlock_Struct +{ + char message; //0x01 + char unknown2[7]; //0x00 + uint32 lsid; + char key[11]; //10 char + null term; + uint32 failed_attempts; + char unknown3[4]; //0x00, 0x00, 0x00, 0x03 + char unknown4[4]; //0x00, 0x00, 0x00, 0x02 + char unknown5[4]; //0xe7, 0x03, 0x00, 0x00 + char unknown6[4]; //0xff, 0xff, 0xff, 0xff + char unknown7[4]; //0xa0, 0x05, 0x00, 0x00 + char unknown8[4]; //0x00, 0x00, 0x00, 0x02 + char unknown9[4]; //0xff, 0x03, 0x00, 0x00 + char unknown10[4]; //0x00, 0x00, 0x00, 0x00 + char unknown11[4]; //0x63, 0x00, 0x00, 0x00 + char unknown12[4]; //0x01, 0x00, 0x00, 0x00 + char unknown13[4]; //0x00, 0x00, 0x00, 0x00 + char unknown14[4]; //0x00, 0x00, 0x00, 0x00 +}; + +struct LoginLoginFailed_Struct { + short unknown1; + short unknown2; + short unknown3; + short unknown4; + short unknown5; + char unknown6[74]; +}; + +struct ServerListHeader_Struct { + + uint32 Unknown1; + uint32 Unknown2; + uint32 Unknown3; + uint32 Unknown4; + uint32 NumberOfServers; +}; + +struct PlayEverquestRequest_Struct +{ + uint16 Sequence; + uint32 Unknown1; + uint32 Unknown2; + uint32 ServerNumber; +}; + +struct PlayEverquestResponse_Struct { + uint8 Sequence; + uint8 Unknown1[9]; + uint8 Allowed; + uint16 Message; + uint8 Unknown2[3]; + uint32 ServerNumber; +}; + +static const unsigned char FailedLoginResponseData[] = { + 0xf6, 0x85, 0x9c, 0x23, 0x57, 0x7e, 0x3e, 0x55, 0xb3, 0x4c, 0xf8, 0xc8, 0xcb, 0x77, 0xd5, 0x16, + 0x09, 0x7a, 0x63, 0xdc, 0x57, 0x7e, 0x3e, 0x55, 0xb3, 0x4c, 0xf8, 0xc8, 0xcb, 0x77, 0xd5, 0x16, + 0x09, 0x7a, 0x63, 0xdc, 0x57, 0x7e, 0x3e, 0x55, 0xb3 }; + + +#pragma pack() + +#endif + diff --git a/loginserver/Main.cpp b/loginserver/Main.cpp new file mode 100644 index 000000000..6a77be9b4 --- /dev/null +++ b/loginserver/Main.cpp @@ -0,0 +1,275 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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/types.h" +#include "../common/opcodemgr.h" +#include "../common/EQStreamFactory.h" +#include "../common/timer.h" +#include "../common/platform.h" +#include "../common/crash.h" +#include "LoginServer.h" +#include +#include +#include +#include + +TimeoutManager timeout_manager; +LoginServer server; +ErrorLog *server_log; +bool run_server = true; +using namespace std; + +void CatchSignal(int sig_num) +{ +} + +int main() +{ + RegisterExecutablePlatform(ExePlatformLogin); + set_exception_handler(); + + //Create our error log, is of format login_.log + time_t current_time = time(NULL); + stringstream log_name(stringstream::in | stringstream::out); +#ifdef WIN32 + log_name << ".\\logs\\login_" << (unsigned int)current_time << ".log"; +#else + log_name << "./logs/login_" << (unsigned int)current_time << ".log"; +#endif + server_log = new ErrorLog(log_name.str().c_str()); + server_log->Log(log_debug, "Logging System Init."); + + //Create our subsystem and parse the ini file. + server.config = new Config(); + server_log->Log(log_debug, "Config System Init."); + server.config->Parse("login.ini"); + + //Parse unregistered allowed option. + if(server.config->GetVariable("options", "unregistered_allowed").compare("FALSE") == 0) + { + server.options.AllowUnregistered(false); + } + + //Parse trace option. + if(server.config->GetVariable("options", "trace").compare("TRUE") == 0) + { + server.options.Trace(true); + } + + //Parse trace option. + if(server.config->GetVariable("options", "world_trace").compare("TRUE") == 0) + { + server.options.WorldTrace(true); + } + + //Parse packet inc dump option. + if(server.config->GetVariable("options", "dump_packets_in").compare("TRUE") == 0) + { + server.options.DumpInPackets(true); + } + + //Parse packet out dump option. + if(server.config->GetVariable("options", "dump_packets_out").compare("TRUE") == 0) + { + server.options.DumpOutPackets(true); + } + + //Parse encryption mode option. + string mode = server.config->GetVariable("security", "mode"); + if(mode.size() > 0) + { + server.options.EncryptionMode(atoi(mode.c_str())); + } + + //Parse local network option. + string ln = server.config->GetVariable("options", "local_network"); + if(ln.size() > 0) + { + server.options.LocalNetwork(ln); + } + + //Parse reject duplicate servers option. + if(server.config->GetVariable("options", "reject_duplicate_servers").compare("TRUE") == 0) + { + server.options.RejectDuplicateServers(true); + } + + //Parse account table option. + ln = server.config->GetVariable("schema", "account_table"); + if(ln.size() > 0) + { + server.options.AccountTable(ln); + } + + //Parse world account table option. + ln = server.config->GetVariable("schema", "world_registration_table"); + if(ln.size() > 0) + { + server.options.WorldRegistrationTable(ln); + } + + //Parse admin world account table option. + ln = server.config->GetVariable("schema", "world_admin_registration_table"); + if(ln.size() > 0) + { + server.options.WorldAdminRegistrationTable(ln); + } + + //Parse world type table option. + ln = server.config->GetVariable("schema", "world_server_type_table"); + if(ln.size() > 0) + { + server.options.WorldServerTypeTable(ln); + } + + //Create our DB from options. + if(server.config->GetVariable("database", "subsystem").compare("MySQL") == 0) + { +#ifdef EQEMU_MYSQL_ENABLED + server_log->Log(log_debug, "MySQL Database Init."); + server.db = (Database*)new DatabaseMySQL( + server.config->GetVariable("database", "user"), + server.config->GetVariable("database", "password"), + server.config->GetVariable("database", "host"), + server.config->GetVariable("database", "port"), + server.config->GetVariable("database", "db")); +#endif + } + else if(server.config->GetVariable("database", "subsystem").compare("PostgreSQL") == 0) + { +#ifdef EQEMU_POSTGRESQL_ENABLED + server_log->Log(log_debug, "PostgreSQL Database Init."); + server.db = (Database*)new DatabasePostgreSQL( + server.config->GetVariable("database", "user"), + server.config->GetVariable("database", "password"), + server.config->GetVariable("database", "host"), + server.config->GetVariable("database", "port"), + server.config->GetVariable("database", "db")); +#endif + } + + //Make sure our database got created okay, otherwise cleanup and exit. + if(!server.db) + { + server_log->Log(log_error, "Database Initialization Failure."); + server_log->Log(log_debug, "Config System Shutdown."); + delete server.config; + server_log->Log(log_debug, "Log System Shutdown."); + delete server_log; + return 1; + } + +#if WIN32 + //initialize our encryption. + server_log->Log(log_debug, "Encryption Initialize."); + server.eq_crypto = new Encryption(); + if(server.eq_crypto->LoadCrypto(server.config->GetVariable("security", "plugin"))) + { + server_log->Log(log_debug, "Encryption Loaded Successfully."); + } + else + { + //We can't run without encryption, cleanup and exit. + server_log->Log(log_error, "Encryption Failed to Load."); + server_log->Log(log_debug, "Database System Shutdown."); + delete server.db; + server_log->Log(log_debug, "Config System Shutdown."); + delete server.config; + server_log->Log(log_debug, "Log System Shutdown."); + delete server_log; + return 1; + } +#endif + + //create our server manager. + server_log->Log(log_debug, "Server Manager Initialize."); + server.SM = new ServerManager(); + if(!server.SM) + { + //We can't run without a server manager, cleanup and exit. + server_log->Log(log_error, "Server Manager Failed to Start."); +#ifdef WIN32 + server_log->Log(log_debug, "Encryption System Shutdown."); + delete server.eq_crypto; +#endif + server_log->Log(log_debug, "Database System Shutdown."); + delete server.db; + server_log->Log(log_debug, "Config System Shutdown."); + delete server.config; + server_log->Log(log_debug, "Log System Shutdown."); + delete server_log; + return 1; + } + + //create our client manager. + server_log->Log(log_debug, "Client Manager Initialize."); + server.CM = new ClientManager(); + if(!server.CM) + { + //We can't run without a client manager, cleanup and exit. + server_log->Log(log_error, "Client Manager Failed to Start."); + server_log->Log(log_debug, "Server Manager Shutdown."); + delete server.SM; +#ifdef WIN32 + server_log->Log(log_debug, "Encryption System Shutdown."); + delete server.eq_crypto; +#endif + server_log->Log(log_debug, "Database System Shutdown."); + delete server.db; + server_log->Log(log_debug, "Config System Shutdown."); + delete server.config; + server_log->Log(log_debug, "Log System Shutdown."); + delete server_log; + return 1; + } + +#ifdef WIN32 +#ifdef UNICODE + SetConsoleTitle(L"EQEmu Login Server"); +#else + SetConsoleTitle("EQEmu Login Server"); +#endif +#endif + + server_log->Log(log_debug, "Server Started."); + while(run_server) + { + Timer::SetCurrentTime(); + server.CM->Process(); + server.SM->Process(); + Sleep(100); + } + + server_log->Log(log_debug, "Server Shutdown."); + server_log->Log(log_debug, "Client Manager Shutdown."); + delete server.CM; + server_log->Log(log_debug, "Server Manager Shutdown."); + delete server.SM; +#ifdef WIN32 + server_log->Log(log_debug, "Encryption System Shutdown."); + delete server.eq_crypto; +#endif + server_log->Log(log_debug, "Database System Shutdown."); + delete server.db; + server_log->Log(log_debug, "Config System Shutdown."); + delete server.config; + server_log->Log(log_debug, "Log System Shutdown."); + delete server_log; + return 0; +} + diff --git a/loginserver/Options.h b/loginserver/Options.h new file mode 100644 index 000000000..4f04457e3 --- /dev/null +++ b/loginserver/Options.h @@ -0,0 +1,176 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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_OPTIONS_H +#define EQEMU_OPTIONS_H + +/** + * Collects options on one object, because having a bunch of global variables floating around is + * really ugly and just a little dangerous. + */ +class Options +{ +public: + /** + * Constructor, sets the default options. + */ + Options() : + allow_unregistered(true), + trace(false), + dump_in_packets(false), + dump_out_packets(false), + encryption_mode(5), + local_network("127.0.0.1"), + reject_duplicate_servers(false) { } + + /** + * Sets allow_unregistered. + */ + inline void AllowUnregistered(bool b) { allow_unregistered = b; } + + /** + * Returns the value of allow_unregistered. + */ + inline bool IsUnregisteredAllowed() const { return allow_unregistered; } + + /** + * Sets trace. + */ + inline void Trace(bool b) { trace = b; } + + /** + * Returns the value of trace. + */ + inline bool IsTraceOn() const { return trace; } + + /** + * Sets trace. + */ + inline void WorldTrace(bool b) { world_trace = b; } + + /** + * Returns the value of trace. + */ + inline bool IsWorldTraceOn() const { return world_trace; } + + /** + * Sets dump_in_packets. + */ + inline void DumpInPackets(bool b) { dump_in_packets = b; } + + /** + * Returns the value of dump_in_packets. + */ + inline bool IsDumpInPacketsOn() const { return dump_in_packets; } + + /** + * Sets dump_out_packets. + */ + inline void DumpOutPackets(bool b) { dump_out_packets = b; } + + /** + * Returns the value of dump_out_packets. + */ + inline bool IsDumpOutPacketsOn() const { return dump_out_packets; } + + /** + * Sets encryption_mode. + */ + inline void EncryptionMode(int m) { encryption_mode = m; } + + /** + * Returns the value of encryption_mode. + */ + inline int GetEncryptionMode() const { return encryption_mode; } + + /** + * Sets local_network. + */ + inline void LocalNetwork(string n) { local_network = n; } + + /** + * Return the value of local_network. + */ + inline string GetLocalNetwork() const { return local_network; } + + /** + * Sets account table. + */ + inline void AccountTable(string t) { account_table = t; } + + /** + * Return the value of local_network. + */ + inline string GetAccountTable() const { return account_table; } + + /** + * Sets world account table. + */ + inline void WorldRegistrationTable(string t) { world_registration_table = t; } + + /** + * Return the value of world account table. + */ + inline string GetWorldRegistrationTable() const { return world_registration_table; } + + /** + * Sets world admin account table. + */ + inline void WorldAdminRegistrationTable(string t) { world_admin_registration_table = t; } + + /** + * Return the value of world admin account table. + */ + inline string GetWorldAdminRegistrationTable() const { return world_admin_registration_table; } + + /** + * Sets world server type table. + */ + inline void WorldServerTypeTable(string t) { world_server_type_table = t; } + + /** + * Return the value of world admin account table. + */ + inline string GetWorldServerTypeTable() const { return world_server_type_table; } + + /** + * Sets whether we are rejecting duplicate servers or not. + */ + inline void RejectDuplicateServers(bool b) { reject_duplicate_servers = b; } + + /** + * Returns whether we are rejecting duplicate servers or not. + */ + inline bool IsRejectingDuplicateServers() { return reject_duplicate_servers; } + +private: + bool allow_unregistered; + bool trace; + bool world_trace; + bool dump_in_packets; + bool dump_out_packets; + bool reject_duplicate_servers; + int encryption_mode; + string local_network; + string account_table; + string world_registration_table; + string world_admin_registration_table; + string world_server_type_table; +}; + +#endif + diff --git a/loginserver/ServerManager.cpp b/loginserver/ServerManager.cpp new file mode 100644 index 000000000..f703ce925 --- /dev/null +++ b/loginserver/ServerManager.cpp @@ -0,0 +1,344 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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 "ServerManager.h" +#include "LoginServer.h" +#include "ErrorLog.h" +#include "LoginStructures.h" +#include + +extern ErrorLog *server_log; +extern LoginServer server; +extern bool run_server; + +ServerManager::ServerManager() +{ + char error_buffer[TCPConnection_ErrorBufferSize]; + + int listen_port = atoi(server.config->GetVariable("options", "listen_port").c_str()); + tcps = new EmuTCPServer(listen_port, true); + if(tcps->Open(listen_port, error_buffer)) + { + server_log->Log(log_network, "ServerManager listening on port %u", listen_port); + } + else + { + server_log->Log(log_error, "ServerManager fatal error opening port on %u: %s", listen_port, error_buffer); + run_server = false; + } +} + +ServerManager::~ServerManager() +{ + if(tcps) + { + tcps->Close(); + delete tcps; + } +} + +void ServerManager::Process() +{ + ProcessDisconnect(); + EmuTCPConnection *tcp_c = NULL; + while(tcp_c = tcps->NewQueuePop()) + { + in_addr tmp; + tmp.s_addr = tcp_c->GetrIP(); + server_log->Log(log_network, "New world server connection from %s:%d", inet_ntoa(tmp), tcp_c->GetrPort()); + + WorldServer *cur = GetServerByAddress(tcp_c->GetrIP()); + if(cur) + { + server_log->Log(log_network, "World server already existed for %s, removing existing connection and updating current.", inet_ntoa(tmp)); + cur->GetConnection()->Free(); + cur->SetConnection(tcp_c); + cur->Reset(); + } + else + { + WorldServer *w = new WorldServer(tcp_c); + world_servers.push_back(w); + } + } + + list::iterator iter = world_servers.begin(); + while(iter != world_servers.end()) + { + if((*iter)->Process() == false) + { + server_log->Log(log_world, "World server %s had a fatal error and had to be removed from the login.", (*iter)->GetLongName().c_str()); + delete (*iter); + iter = world_servers.erase(iter); + } + else + { + iter++; + } + } +} + +void ServerManager::ProcessDisconnect() +{ + list::iterator iter = world_servers.begin(); + while(iter != world_servers.end()) + { + EmuTCPConnection *c = (*iter)->GetConnection(); + if(!c->Connected()) + { + in_addr tmp; + tmp.s_addr = c->GetrIP(); + server_log->Log(log_network, "World server disconnected from the server, removing server and freeing connection."); + c->Free(); + delete (*iter); + iter = world_servers.erase(iter); + } + else + { + iter++; + } + } +} + +WorldServer* ServerManager::GetServerByAddress(unsigned int address) +{ + list::iterator iter = world_servers.begin(); + while(iter != world_servers.end()) + { + if((*iter)->GetConnection()->GetrIP() == address) + { + return (*iter); + } + iter++; + } + + return NULL; +} + +EQApplicationPacket *ServerManager::CreateServerListPacket(Client *c) +{ + unsigned int packet_size = sizeof(ServerListHeader_Struct); + unsigned int server_count = 0; + in_addr in; + in.s_addr = c->GetConnection()->GetRemoteIP(); + string client_ip = inet_ntoa(in); + + list::iterator iter = world_servers.begin(); + while(iter != world_servers.end()) + { + if((*iter)->IsAuthorized() == false) + { + iter++; + continue; + } + + in.s_addr = (*iter)->GetConnection()->GetrIP(); + string world_ip = inet_ntoa(in); + + if(world_ip.compare(client_ip) == 0) + { + packet_size += (*iter)->GetLongName().size() + (*iter)->GetLocalIP().size() + 24; + } + else if(client_ip.find(server.options.GetLocalNetwork()) != string::npos) + { + packet_size += (*iter)->GetLongName().size() + (*iter)->GetLocalIP().size() + 24; + } + else + { + packet_size += (*iter)->GetLongName().size() + (*iter)->GetRemoteIP().size() + 24; + } + + server_count++; + iter++; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ServerListResponse, packet_size); + ServerListHeader_Struct *sl = (ServerListHeader_Struct*)outapp->pBuffer; + sl->Unknown1 = 0x00000004; + sl->Unknown2 = 0x00000000; + sl->Unknown3 = 0x01650000; + /** + * Not sure what this is but it should be noted setting it to + * 0xFFFFFFFF crashes the client so: don't do that. + */ + sl->Unknown4 = 0x00000000; + sl->NumberOfServers = server_count; + + unsigned char *data_ptr = outapp->pBuffer; + data_ptr += sizeof(ServerListHeader_Struct); + + iter = world_servers.begin(); + while(iter != world_servers.end()) + { + if((*iter)->IsAuthorized() == false) + { + iter++; + continue; + } + + in.s_addr = (*iter)->GetConnection()->GetrIP(); + string world_ip = inet_ntoa(in); + if(world_ip.compare(client_ip) == 0) + { + memcpy(data_ptr, (*iter)->GetLocalIP().c_str(), (*iter)->GetLocalIP().size()); + data_ptr += ((*iter)->GetLocalIP().size() + 1); + } + else if(client_ip.find(server.options.GetLocalNetwork()) != string::npos) + { + memcpy(data_ptr, (*iter)->GetLocalIP().c_str(), (*iter)->GetLocalIP().size()); + data_ptr += ((*iter)->GetLocalIP().size() + 1); + } + else + { + memcpy(data_ptr, (*iter)->GetRemoteIP().c_str(), (*iter)->GetRemoteIP().size()); + data_ptr += ((*iter)->GetRemoteIP().size() + 1); + } + + switch((*iter)->GetServerListID()) + { + case 1: + { + *(unsigned int*)data_ptr = 0x00000030; + break; + } + case 2: + { + *(unsigned int*)data_ptr = 0x00000009; + break; + } + default: + { + *(unsigned int*)data_ptr = 0x00000001; + } + } + data_ptr += 4; + + *(unsigned int*)data_ptr = (*iter)->GetRuntimeID(); + data_ptr += 4; + + memcpy(data_ptr, (*iter)->GetLongName().c_str(), (*iter)->GetLongName().size()); + data_ptr += ((*iter)->GetLongName().size() + 1); + + memcpy(data_ptr, "EN", 2); + data_ptr += 3; + + memcpy(data_ptr, "US", 2); + data_ptr += 3; + + // 0 = Up, 1 = Down, 2 = Up, 3 = down, 4 = locked, 5 = locked(down) + if((*iter)->GetStatus() < 0) + { + if((*iter)->GetZonesBooted() == 0) + { + *(uint32*)data_ptr = 0x01; + } + else + { + *(uint32*)data_ptr = 0x04; + } + } + else + { + *(uint32*)data_ptr = 0x02; + } + data_ptr += 4; + + *(uint32*)data_ptr = (*iter)->GetPlayersOnline(); + data_ptr += 4; + + iter++; + } + + return outapp; +} + +void ServerManager::SendUserToWorldRequest(unsigned int server_id, unsigned int client_account_id) +{ + list::iterator iter = world_servers.begin(); + bool found = false; + while(iter != world_servers.end()) + { + if((*iter)->GetRuntimeID() == server_id) + { + ServerPacket *outapp = new ServerPacket(ServerOP_UsertoWorldReq, sizeof(UsertoWorldRequest_Struct)); + UsertoWorldRequest_Struct *utwr = (UsertoWorldRequest_Struct*)outapp->pBuffer; + utwr->worldid = server_id; + utwr->lsaccountid = client_account_id; + (*iter)->GetConnection()->SendPacket(outapp); + found = true; + + if(server.options.IsDumpInPacketsOn()) + { + DumpPacket(outapp); + } + delete outapp; + } + iter++; + } + + if(!found && server.options.IsTraceOn()) + { + server_log->Log(log_client_error, "Client requested a user to world but supplied an invalid id of %u.", server_id); + } +} + +bool ServerManager::ServerExists(string l_name, string s_name, WorldServer *ignore) +{ + list::iterator iter = world_servers.begin(); + while(iter != world_servers.end()) + { + if((*iter) == ignore) + { + iter++; + continue; + } + + if((*iter)->GetLongName().compare(l_name) == 0 && (*iter)->GetShortName().compare(s_name) == 0) + { + return true; + } + + iter++; + } + return false; +} + +void ServerManager::DestroyServerByName(string l_name, string s_name, WorldServer *ignore) +{ + list::iterator iter = world_servers.begin(); + while(iter != world_servers.end()) + { + if((*iter) == ignore) + { + iter++; + } + + if((*iter)->GetLongName().compare(l_name) == 0 && (*iter)->GetShortName().compare(s_name) == 0) + { + EmuTCPConnection *c = (*iter)->GetConnection(); + if(c->Connected()) + { + c->Disconnect(); + } + c->Free(); + delete (*iter); + iter = world_servers.erase(iter); + } + + iter++; + } +} diff --git a/loginserver/ServerManager.h b/loginserver/ServerManager.h new file mode 100644 index 000000000..bb14c2449 --- /dev/null +++ b/loginserver/ServerManager.h @@ -0,0 +1,91 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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_SERVERMANAGER_H +#define EQEMU_SERVERMANAGER_H + +#include "../common/debug.h" +#include "../common/EQStreamFactory.h" +#include "../common/EmuTCPConnection.h" +#include "../common/EmuTCPServer.h" +#include "../common/servertalk.h" +#include "../common/packet_dump.h" +#include "WorldServer.h" +#include "Client.h" +#include + +using namespace std; + +/** + * Server manager class, deals with management of the world servers. + */ +class ServerManager +{ +public: + /** + * Constructor, sets up the TCP server and starts listening. + */ + ServerManager(); + + /** + * Destructor, shuts down the TCP server. + */ + ~ServerManager(); + + /** + * Does basic processing for all the servers. + */ + void Process(); + + /** + * Sends a request to world to see if the client is banned or suspended. + */ + void SendUserToWorldRequest(unsigned int server_id, unsigned int client_account_id); + + /** + * Creates a server list packet for the client. + */ + EQApplicationPacket *CreateServerListPacket(Client *c); + + /** + * Checks to see if there is a server exists with this name, ignoring option. + */ + bool ServerExists(string l_name, string s_name, WorldServer *ignore = NULL); + + /** + * Destroys a server with this name, ignoring option. + */ + void DestroyServerByName(string l_name, string s_name, WorldServer *ignore = NULL); + +private: + /** + * Processes all the disconnected connections in Process(), not used outside. + */ + void ProcessDisconnect(); + + /** + * Retrieves a server(if exists) by ip address + * Useful utility for the reconnect process. + */ + WorldServer* GetServerByAddress(unsigned int address); + + EmuTCPServer* tcps; + list world_servers; +}; + +#endif + diff --git a/loginserver/WorldServer.cpp b/loginserver/WorldServer.cpp new file mode 100644 index 000000000..e25d821ea --- /dev/null +++ b/loginserver/WorldServer.cpp @@ -0,0 +1,541 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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 "WorldServer.h" +#include "ErrorLog.h" +#include "LoginServer.h" +#include "LoginStructures.h" + +extern ErrorLog *server_log; +extern LoginServer server; + +WorldServer::WorldServer(EmuTCPConnection *c) +{ + connection = c; + zones_booted = 0; + players_online = 0; + status = 0; + runtime_id = 0; + server_list_id = 0; + server_type = 0; + authorized = false; + trusted = false; + logged_in = false; +} + +WorldServer::~WorldServer() +{ + if(connection) + { + connection->Free(); + } +} + +void WorldServer::Reset() +{ + zones_booted = 0; + players_online = 0; + status = 0; + runtime_id; + server_list_id = 0; + server_type = 0; + authorized = false; + logged_in = false; +} + +bool WorldServer::Process() +{ + ServerPacket *app = NULL; + while(app = connection->PopPacket()) + { + if(server.options.IsWorldTraceOn()) + { + server_log->Log(log_network_trace, "Application packet recieved from server: 0x%.4X, (size %u)", app->opcode, app->size); + } + + if(server.options.IsDumpInPacketsOn()) + { + DumpPacket(app); + } + + switch(app->opcode) + { + case ServerOP_NewLSInfo: + { + if(app->size < sizeof(ServerNewLSInfo_Struct)) + { + server_log->Log(log_network_error, "Recieved application packet from server that had opcode ServerOP_NewLSInfo, " + "but was too small. Discarded to avoid buffer overrun."); + break; + } + + if(server.options.IsWorldTraceOn()) + { + server_log->Log(log_network_trace, "New Login Info Recieved."); + } + + ServerNewLSInfo_Struct *info = (ServerNewLSInfo_Struct*)app->pBuffer; + Handle_NewLSInfo(info); + break; + } + case ServerOP_LSStatus: + { + if(app->size < sizeof(ServerLSStatus_Struct)) + { + server_log->Log(log_network_error, "Recieved application packet from server that had opcode ServerOP_LSStatus, " + "but was too small. Discarded to avoid buffer overrun."); + break; + } + + if(server.options.IsWorldTraceOn()) + { + server_log->Log(log_network_trace, "World Server Status Recieved."); + } + + ServerLSStatus_Struct *ls_status = (ServerLSStatus_Struct*)app->pBuffer; + Handle_LSStatus(ls_status); + break; + } + case ServerOP_LSZoneInfo: + case ServerOP_LSZoneShutdown: + case ServerOP_LSZoneStart: + case ServerOP_LSZoneBoot: + case ServerOP_LSZoneSleep: + case ServerOP_LSPlayerLeftWorld: + case ServerOP_LSPlayerJoinWorld: + case ServerOP_LSPlayerZoneChange: + { + //Not logging these to cut down on spam until we implement them + break; + } + + case ServerOP_UsertoWorldResp: + { + if(app->size < sizeof(UsertoWorldResponse_Struct)) + { + server_log->Log(log_network_error, "Recieved application packet from server that had opcode ServerOP_UsertoWorldResp, " + "but was too small. Discarded to avoid buffer overrun."); + break; + } + + //I don't use world trace for this and here is why: + //Because this is a part of the client login procedure it makes tracking client errors + //While keeping world server spam with multiple servers connected almost impossible. + if(server.options.IsTraceOn()) + { + server_log->Log(log_network_trace, "User-To-World Response recieved."); + } + + UsertoWorldResponse_Struct *utwr = (UsertoWorldResponse_Struct*)app->pBuffer; + server_log->Log(log_client, "Trying to find client with user id of %u.", utwr->lsaccountid); + Client *c = server.CM->GetClient(utwr->lsaccountid); + if(c) + { + server_log->Log(log_client, "Found client with user id of %u and account name of %s.", utwr->lsaccountid, c->GetAccountName().c_str()); + EQApplicationPacket *outapp = new EQApplicationPacket(OP_PlayEverquestResponse, sizeof(PlayEverquestResponse_Struct)); + PlayEverquestResponse_Struct *per = (PlayEverquestResponse_Struct*)outapp->pBuffer; + per->Sequence = c->GetPlaySequence(); + per->ServerNumber = c->GetPlayServerID(); + server_log->Log(log_client, "Found sequence and play of %u %u", c->GetPlaySequence(), c->GetPlayServerID()); + server_log->LogPacket(log_network_trace, (const char*)outapp->pBuffer, outapp->size); + + if(utwr->response > 0) + { + per->Allowed = 1; + SendClientAuth(c->GetConnection()->GetRemoteIP(), c->GetAccountName(), c->GetKey(), c->GetAccountID()); + } + + switch(utwr->response) + { + case 1: + per->Message = 101; + break; + case 0: + per->Message = 326; + break; + case -1: + per->Message = 337; + break; + case -2: + per->Message = 338; + break; + case -3: + per->Message = 303; + break; + } + + if(server.options.IsTraceOn()) + { + server_log->Log(log_network_trace, "Sending play response with following data, allowed %u, sequence %u, server number %u, message %u", + per->Allowed, per->Sequence, per->ServerNumber, per->Message); + server_log->LogPacket(log_network_trace, (const char*)outapp->pBuffer, outapp->size); + } + + if(server.options.IsDumpOutPacketsOn()) + { + DumpPacket(outapp); + } + + c->SendPlayResponse(outapp); + delete outapp; + } + else + { + server_log->Log(log_client_error, "Recieved User-To-World Response for %u but could not find the client referenced!.", utwr->lsaccountid); + } + break; + } + case ServerOP_LSAccountUpdate: + { + server_log->Log(log_network_trace, "ServerOP_LSAccountUpdate packet recieved from: %s", short_name.c_str()); + ServerLSAccountUpdate_Struct *lsau = (ServerLSAccountUpdate_Struct*)app->pBuffer; + if(trusted) + { + server_log->Log(log_network_trace, "ServerOP_LSAccountUpdate update processed for: %s", lsau->useraccount); + string name; + string password; + string email; + name.assign(lsau->useraccount); + password.assign(lsau->userpassword); + email.assign(lsau->useremail); + server.db->UpdateLSAccountInfo(lsau->useraccountid, name, password, email); + } + break; + } + default: + { + server_log->Log(log_network_error, "Recieved application packet from server that had an unknown operation code 0x%.4X.", app->opcode); + } + } + + delete app; + app = NULL; + } + return true; +} + +void WorldServer::Handle_NewLSInfo(ServerNewLSInfo_Struct* i) +{ + if(logged_in) + { + server_log->Log(log_network_error, "WorldServer::Handle_NewLSInfo called but the login server was already marked as logged in, aborting."); + return; + } + + if(strlen(i->account) <= 30) + { + account_name = i->account; + } + else + { + server_log->Log(log_network_error, "Handle_NewLSInfo error, account name was too long."); + return; + } + + if(strlen(i->password) <= 30) + { + account_password = i->password; + } + else + { + server_log->Log(log_network_error, "Handle_NewLSInfo error, account password was too long."); + return; + } + + if(strlen(i->name) <= 200) + { + long_name = i->name; + } + else + { + server_log->Log(log_network_error, "Handle_NewLSInfo error, long name was too long."); + return; + } + + if(strlen(i->shortname) <= 50) + { + short_name = i->shortname; + } + else + { + server_log->Log(log_network_error, "Handle_NewLSInfo error, short name was too long."); + return; + } + + if(strlen(i->local_address) <= 125) + { + if(strlen(i->local_address) == 0) + { + server_log->Log(log_network_error, "Handle_NewLSInfo error, local address was null, defaulting to localhost"); + local_ip = "127.0.0.1"; + } + else + { + local_ip = i->local_address; + } + } + else + { + server_log->Log(log_network_error, "Handle_NewLSInfo error, local address was too long."); + return; + } + + if(strlen(i->remote_address) <= 125) + { + if(strlen(i->remote_address) == 0) + { + in_addr in; + in.s_addr = GetConnection()->GetrIP(); + remote_ip = inet_ntoa(in); + server_log->Log(log_network_error, "Handle_NewLSInfo error, remote address was null, defaulting to stream address %s.", remote_ip.c_str()); + } + else + { + remote_ip = i->remote_address; + } + } + else + { + in_addr in; + in.s_addr = GetConnection()->GetrIP(); + remote_ip = inet_ntoa(in); + server_log->Log(log_network_error, "Handle_NewLSInfo error, remote address was too long, defaulting to stream address %s.", remote_ip.c_str()); + } + + if(strlen(i->serverversion) <= 64) + { + version = i->serverversion; + } + else + { + server_log->Log(log_network_error, "Handle_NewLSInfo error, server version was too long."); + return; + } + + if(strlen(i->protocolversion) <= 25) + { + protocol = i->protocolversion; + } + else + { + server_log->Log(log_network_error, "Handle_NewLSInfo error, protocol version was too long."); + return; + } + + server_type = i->servertype; + logged_in = true; + + if(server.options.IsRejectingDuplicateServers()) + { + if(server.SM->ServerExists(long_name, short_name, this)) + { + server_log->Log(log_world_error, "World tried to login but there already exists a server that has that name."); + return; + } + } + else + { + if(server.SM->ServerExists(long_name, short_name, this)) + { + server_log->Log(log_world_error, "World tried to login but there already exists a server that has that name."); + server.SM->DestroyServerByName(long_name, short_name, this); + } + } + + if(!server.options.IsUnregisteredAllowed()) + { + if(account_name.size() > 0 && account_password.size() > 0) + { + unsigned int s_id = 0; + unsigned int s_list_type = 0; + unsigned int s_trusted = 0; + string s_desc; + string s_list_desc; + string s_acct_name; + string s_acct_pass; + if(server.db->GetWorldRegistration(long_name, short_name, s_id, s_desc, s_list_type, s_trusted, s_list_desc, s_acct_name, s_acct_pass)) + { + if(s_acct_name.size() == 0 || s_acct_pass.size() == 0) + { + server_log->Log(log_world, "Server %s(%s) successfully logged into account that had no user/password requirement.", + long_name.c_str(), short_name.c_str()); + authorized = true; + SetRuntimeID(s_id); + server_list_id = s_list_type; + desc = s_desc; + } + else if(s_acct_name.compare(account_name) == 0 && s_acct_pass.compare(account_password) == 0) + { + server_log->Log(log_world, "Server %s(%s) successfully logged in.", + long_name.c_str(), short_name.c_str()); + authorized = true; + SetRuntimeID(s_id); + server_list_id = s_list_type; + desc = s_desc; + if(s_trusted) + { + server_log->Log(log_network_trace, "ServerOP_LSAccountUpdate sent to world"); + trusted = true; + ServerPacket *outapp = new ServerPacket(ServerOP_LSAccountUpdate, 0); + connection->SendPacket(outapp); + } + } + else + { + server_log->Log(log_world, "Server %s(%s) attempted to log in but account and password did not match the entry in the database, and only" + " registered servers are allowed.", long_name.c_str(), short_name.c_str()); + return; + } + } + else + { + server_log->Log(log_world, "Server %s(%s) attempted to log in but database couldn't find an entry and only registered servers are allowed.", + long_name.c_str(), short_name.c_str()); + return; + } + } + else + { + server_log->Log(log_world, "Server %s(%s) did not attempt to log in but only registered servers are allowed.", + long_name.c_str(), short_name.c_str()); + return; + } + } + else + { + unsigned int s_id = 0; + unsigned int s_list_type = 0; + unsigned int s_trusted = 0; + string s_desc; + string s_list_desc; + string s_acct_name; + string s_acct_pass; + if(server.db->GetWorldRegistration(long_name, short_name, s_id, s_desc, s_list_type, s_trusted, s_list_desc, s_acct_name, s_acct_pass)) + { + if(account_name.size() > 0 && account_password.size() > 0) + { + if(s_acct_name.compare(account_name) == 0 && s_acct_pass.compare(account_password) == 0) + { + server_log->Log(log_world, "Server %s(%s) successfully logged in.", + long_name.c_str(), short_name.c_str()); + authorized = true; + SetRuntimeID(s_id); + server_list_id = s_list_type; + desc = s_desc; + if(s_trusted) + { + server_log->Log(log_network_trace, "ServerOP_LSAccountUpdate sent to world"); + trusted = true; + ServerPacket *outapp = new ServerPacket(ServerOP_LSAccountUpdate, 0); + connection->SendPacket(outapp); + } + } + else + { + // this is the first of two cases where we should deny access even if unregistered is allowed + server_log->Log(log_world, "Server %s(%s) attempted to log in but account and password did not match the entry in the database.", + long_name.c_str(), short_name.c_str()); + } + } + else + { + if(s_acct_name.size() > 0 || s_acct_pass.size() > 0) + { + // this is the second of two cases where we should deny access even if unregistered is allowed + server_log->Log(log_world, "Server %s(%s) did not attempt to log in but this server requires a password.", + long_name.c_str(), short_name.c_str()); + } + else + { + server_log->Log(log_world, "Server %s(%s) did not attempt to log in but unregistered servers are allowed.", + long_name.c_str(), short_name.c_str()); + authorized = true; + SetRuntimeID(s_id); + server_list_id = 3; + } + } + } + else + { + server_log->Log(log_world, "Server %s(%s) attempted to log in but database couldn't find an entry but unregistered servers are allowed.", + long_name.c_str(), short_name.c_str()); + if(server.db->CreateWorldRegistration(long_name, short_name, s_id)) + { + authorized = true; + SetRuntimeID(s_id); + server_list_id = 3; + } + } + } + + in_addr in; + in.s_addr = connection->GetrIP(); + server.db->UpdateWorldRegistration(GetRuntimeID(), long_name, string(inet_ntoa(in))); + + if(authorized) + { + server.CM->UpdateServerList(); + } +} + +void WorldServer::Handle_LSStatus(ServerLSStatus_Struct *s) +{ + players_online = s->num_players; + zones_booted = s->num_zones; + status = s->status; +} + +void WorldServer::SendClientAuth(unsigned int ip, string account, string key, unsigned int account_id) +{ + ServerPacket *outapp = new ServerPacket(ServerOP_LSClientAuth, sizeof(ServerLSClientAuth)); + ServerLSClientAuth* slsca = (ServerLSClientAuth*)outapp->pBuffer; + + slsca->lsaccount_id = account_id; + strncpy(slsca->name, account.c_str(), account.size() > 30 ? 30 : account.size()); + strncpy(slsca->key, key.c_str(), 10); + slsca->lsadmin = 0; + slsca->worldadmin = 0; + slsca->ip = ip; + + in_addr in; + in.s_addr = ip;connection->GetrIP(); + string client_address(inet_ntoa(in)); + in.s_addr = connection->GetrIP(); + string world_address(inet_ntoa(in)); + + if(client_address.compare(world_address) == 0) + { + slsca->local = 1; + } + else if(client_address.find(server.options.GetLocalNetwork()) != string::npos) + { + slsca->local = 1; + } + else + { + slsca->local = 0; + } + + connection->SendPacket(outapp); + + if(server.options.IsDumpInPacketsOn()) + { + DumpPacket(outapp); + } + delete outapp; +} + diff --git a/loginserver/WorldServer.h b/loginserver/WorldServer.h new file mode 100644 index 000000000..7139955e2 --- /dev/null +++ b/loginserver/WorldServer.h @@ -0,0 +1,162 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2010 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_WORLDSERVER_H +#define EQEMU_WORLDSERVER_H + +#include "../common/debug.h" +#include "../common/EQStreamFactory.h" +#include "../common/EmuTCPConnection.h" +#include "../common/EmuTCPServer.h" +#include "../common/servertalk.h" +#include "../common/packet_dump.h" +#include + +using namespace std; + +/** + * World server class, controls the connected server processing. + */ +class WorldServer +{ +public: + /** + * Constructor, sets our connection to c. + */ + WorldServer(EmuTCPConnection *c); + + /** + * Destructor, frees our connection if it exists. + */ + ~WorldServer(); + + /** + * Resets the basic stats of this server. + */ + void Reset(); + + /** + * Does processing of all the connections for this world. + * Returns true except for a fatal error that requires disconnection. + */ + bool Process(); + + /** + * Accesses connection, it is intentional that this is not const (trust me). + */ + EmuTCPConnection *GetConnection() { return connection; } + + /** + * Sets the connection to c. + */ + void SetConnection(EmuTCPConnection *c) { connection = c; } + + /** + * Gets the runtime id of this server. + */ + unsigned int GetRuntimeID() const { return runtime_id; } + + /** + * Sets the runtime id of this server. + */ + void SetRuntimeID(unsigned int id) { runtime_id = id; } + + /** + * Gets the long name of the server. + */ + string GetLongName() const { return long_name; } + + /** + * Gets the short name of the server. + */ + string GetShortName() const { return short_name; } + + /** + * Gets whether the server is authorized to show up on the server list or not. + */ + bool IsAuthorized() const { return authorized; } + + /** + * Gets the local ip of the server. + */ + string GetLocalIP() const { return local_ip; } + + /** + * Gets the remote ip of the server. + */ + string GetRemoteIP() const { return remote_ip; } + + /** + * Gets what kind of server this server is (legends, preferred, normal) + */ + unsigned int GetServerListID() const { return server_list_id; } + + /** + * Gets the status of the server. + */ + int GetStatus() const { return status; } + + /** + * Gets the number of zones online on the server. + */ + unsigned int GetZonesBooted() const { return zones_booted; } + + /** + * Gets the number of players on the server. + */ + unsigned int GetPlayersOnline() const { return players_online; } + + /** + * Takes the info struct we recieved from world and processes it. + */ + void Handle_NewLSInfo(ServerNewLSInfo_Struct* i); + + /** + * Takes the status struct we recieved from world and processes it. + */ + void Handle_LSStatus(ServerLSStatus_Struct *s); + + /** + * Informs world that there is a client incoming with the following data. + */ + void SendClientAuth(unsigned int ip, string account, string key, unsigned int account_id); + +private: + + EmuTCPConnection *connection; + unsigned int zones_booted; + unsigned int players_online; + int status; + unsigned int runtime_id; + unsigned int server_list_id; + unsigned int server_type; + string desc; + string long_name; + string short_name; + string account_name; + string account_password; + string remote_ip; + string local_ip; + string protocol; + string version; + bool authorized; + bool logged_in; + bool trusted; +}; + +#endif + diff --git a/loginserver/login_util/EQEmuLoginServerDBInstall.sql b/loginserver/login_util/EQEmuLoginServerDBInstall.sql new file mode 100644 index 000000000..ae8f6e5e8 --- /dev/null +++ b/loginserver/login_util/EQEmuLoginServerDBInstall.sql @@ -0,0 +1,57 @@ +DROP TABLE IF EXISTS tblLoginServerAccounts; +CREATE TABLE IF NOT EXISTS tblLoginServerAccounts ( + LoginServerID integer unsigned NOT NULL auto_increment, + AccountName varchar(30) NOT NULL, + AccountPassword varchar(50) NOT NULL, + AccountCreateDate timestamp default CURRENT_TIMESTAMP NOT NULL, + AccountEmail varchar(100) NOT NULL, + LastLoginDate datetime NOT NULL, + LastIPAddress varchar(15) NOT NULL, + PRIMARY KEY (LoginServerID, AccountName) +) ENGINE=InnoDB; + +insert into tblLoginServerAccounts (AccountName, AccountPassword, AccountEmail, LastLoginDate, LastIPAddress) values('Admin', sha('password'), 'admin@somewhere.com', now(), '127.0.0.1'); + +DROP TABLE IF EXISTS tblServerListType; +CREATE TABLE IF NOT EXISTS tblServerListType ( + ServerListTypeID integer unsigned NOT NULL, + ServerListTypeDescription varchar(20) NOT NULL, + PRIMARY KEY (ServerListTypeID) +) ENGINE=MyISAM; + +INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (1, 'Legends'); +INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (2, 'Preferred'); +INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (3, 'Standard'); + +DROP TABLE IF EXISTS tblServerAdminRegistration; +CREATE TABLE IF NOT EXISTS tblServerAdminRegistration ( + ServerAdminID integer unsigned NOT NULL auto_increment, + AccountName varchar(30) NOT NULL, + AccountPassword varchar(30) NOT NULL, + FirstName varchar(40) NOT NULL, + LastName varchar(50) NOT NULL, + Email varchar(100) NULL, + RegistrationDate datetime NOT NULL, + RegistrationIPAddr varchar(15) NOT NULL, + PRIMARY KEY (ServerAdminID, Email) +) ENGINE=MyISAM; + +INSERT INTO tblServerAdminRegistration (AccountName, AccountPassword, FirstName, LastName, Email, RegistrationDate, RegistrationIPAddr) VALUES ('Admin', 'Password', 'Tom', 'Wilson', 'Tom.Wilson@gmail.com', now(), '0.0.0.0'); + +DROP TABLE IF EXISTS tblWorldServerRegistration; +CREATE TABLE IF NOT EXISTS tblWorldServerRegistration ( + ServerID integer unsigned NOT NULL auto_increment, + ServerLongName varchar(100) NOT NULL, + ServerTagDescription varchar(50) NOT NULL DEFAULT '', + ServerShortName varchar(25) NOT NULL, + ServerListTypeID integer NOT NULL, + ServerLastLoginDate datetime NULL, + ServerLastIPAddr varchar(15) NULL, + ServerAdminID integer NOT NULL, + ServerTrusted integer NOT NULL, + Note varchar(300) NULL, + PRIMARY KEY (ServerID, ServerLongName) +) ENGINE=InnoDB; + + +INSERT INTO tblWorldServerRegistration (ServerLongName, ServerTagDescription, ServerShortName, ServerListTypeID, ServerLastLoginDate, ServerLastIPAddr, ServerAdminID, ServerTrusted, Note) VALUES ('My Test Server', 'A test server', 'MTST', 1, now(), '0.0.0.0', 1, 0, 'This is a note for the test server'); diff --git a/loginserver/login_util/EQEmuLoginServerPostgreDBInstall.sql b/loginserver/login_util/EQEmuLoginServerPostgreDBInstall.sql new file mode 100644 index 000000000..bedfbe20d --- /dev/null +++ b/loginserver/login_util/EQEmuLoginServerPostgreDBInstall.sql @@ -0,0 +1,57 @@ +DROP TABLE IF EXISTS tblLoginServerAccounts; +CREATE TABLE tblLoginServerAccounts ( + LoginServerID SERIAL, + AccountName text NOT NULL, + AccountPassword text NOT NULL, + AccountCreateDate date NOT NULL, + AccountEmail text NOT NULL, + LastLoginDate date NOT NULL, + LastIPAddress text NOT NULL, + PRIMARY KEY(LoginServerID, AccountName) +); + +insert into tblLoginServerAccounts (AccountName, AccountPassword, AccountEmail, AccountCreateDate, LastLoginDate, LastIPAddress) values('Admin', '5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8', 'admin@somewhere.com', current_date, current_date, '127.0.0.1'); + +DROP TABLE IF EXISTS tblServerListType; +CREATE TABLE tblServerListType ( + ServerListTypeID integer NOT NULL, + CHECK (ServerListTypeID >= 0), + ServerListTypeDescription text NOT NULL, + PRIMARY KEY (ServerListTypeID) +); + +INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (1, 'Legends'); +INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (2, 'Preferred'); +INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (3, 'Standard'); + +DROP TABLE IF EXISTS tblServerAdminRegistration; +CREATE TABLE tblServerAdminRegistration ( + ServerAdminID SERIAL, + AccountName text NOT NULL, + AccountPassword text NOT NULL, + FirstName text NOT NULL, + LastName text NOT NULL, + Email text NOT NULL, + RegistrationDate date NOT NULL, + RegistrationIPAddr text NOT NULL, + PRIMARY KEY (ServerAdminID, Email) +); + +INSERT INTO tblServerAdminRegistration (AccountName, AccountPassword, FirstName, LastName, Email, RegistrationDate, RegistrationIPAddr) VALUES ('Admin', 'Password', 'Tom', 'Wilson', 'Tom.Wilson@gmail.com', current_date, '0.0.0.0'); + +DROP TABLE IF EXISTS tblWorldServerRegistration; +CREATE TABLE tblWorldServerRegistration ( + ServerID SERIAL, + ServerLongName text NOT NULL, + ServerTagDescription text NOT NULL, + ServerShortName text NOT NULL, + ServerListTypeID integer NOT NULL, + ServerLastLoginDate date NULL, + ServerLastIPAddr text NOT NULL, + ServerAdminID integer NOT NULL, + ServerTrusted integer NOT NULL, + Note text NOT NULL, + PRIMARY KEY (ServerID, ServerLongName) +); + +INSERT INTO tblWorldServerRegistration (ServerLongName, ServerTagDescription, ServerShortName, ServerListTypeID, ServerLastLoginDate, ServerLastIPAddr, ServerAdminID, ServerTrusted, Note) VALUES ('My Test Server', 'A test server', 'MTST', 1, current_date, '0.0.0.0', 1, 0, 'This is a note for the test server'); \ No newline at end of file diff --git a/loginserver/login_util/login.ini b/loginserver/login_util/login.ini new file mode 100644 index 000000000..bde7616f1 --- /dev/null +++ b/loginserver/login_util/login.ini @@ -0,0 +1,35 @@ +[database] +host = localhost +port = 3306 +db = eqemu +user = user +password = password +subsystem = MySQL + +[options] +unregistered_allowed = TRUE +reject_duplicate_servers = FALSE +trace = TRUE +world_trace = FALSE +dump_packets_in = FALSE +dump_packets_out = FALSE +listen_port = 5998 +local_network = 192.168.1. + +[security] +plugin = EQEmuAuthCrypto +mode = 5 + +[Titanium] +port = 5998 +opcodes = login_opcodes.conf + +[SoD] +port = 5999 +opcodes = login_opcodes_sod.conf + +[schema] +account_table = tblLoginServerAccounts +world_registration_table = tblWorldServerRegistration +world_admin_registration_table = tblServerAdminRegistration +world_server_type_table = tblServerListType \ No newline at end of file diff --git a/loginserver/login_util/login_opcodes.conf b/loginserver/login_util/login_opcodes.conf new file mode 100644 index 000000000..74518b7f6 --- /dev/null +++ b/loginserver/login_util/login_opcodes.conf @@ -0,0 +1,12 @@ +#EQEmu Public Login Server OPCodes +OP_SessionReady=0x0001 +OP_Login=0x0002 +OP_ServerListRequest=0x0004 +OP_PlayEverquestRequest=0x000d +OP_PlayEverquestResponse=0x0021 +OP_ChatMessage=0x0016 +OP_LoginAccepted=0x0017 +OP_ServerListResponse=0x0018 +OP_Poll=0x0029 +OP_EnterChat=0x000f +OP_PollResponse=0x0011 diff --git a/loginserver/login_util/login_opcodes_sod.conf b/loginserver/login_util/login_opcodes_sod.conf new file mode 100644 index 000000000..fc0d8bb94 --- /dev/null +++ b/loginserver/login_util/login_opcodes_sod.conf @@ -0,0 +1,12 @@ +#EQEmu Public Login Server OPCodes +OP_SessionReady=0x0001 +OP_Login=0x0002 +OP_ServerListRequest=0x0004 +OP_PlayEverquestRequest=0x000d +OP_PlayEverquestResponse=0x0022 +OP_ChatMessage=0x0017 +OP_LoginAccepted=0x0018 +OP_ServerListResponse=0x0019 +OP_Poll=0x0029 +OP_EnterChat=0x000f +OP_PollResponse=0x0011 diff --git a/loginserver/login_util/tblLoginServerAccounts.sql b/loginserver/login_util/tblLoginServerAccounts.sql new file mode 100644 index 000000000..65143b38b --- /dev/null +++ b/loginserver/login_util/tblLoginServerAccounts.sql @@ -0,0 +1,11 @@ +DROP TABLE IF EXISTS tblLoginServerAccounts; +CREATE TABLE IF NOT EXISTS tblLoginServerAccounts ( + LoginServerID integer unsigned NOT NULL auto_increment, + AccountName varchar(30) NOT NULL, + AccountPassword varchar(50) NOT NULL, + AccountCreateDate timestamp default CURRENT_TIMESTAMP NOT NULL, + AccountEmail varchar(100) NOT NULL, + LastLoginDate datetime NOT NULL, + LastIPAddress varchar(15) NOT NULL, + PRIMARY KEY (LoginServerID, AccountName) +) ENGINE=InnoDB; diff --git a/loginserver/login_util/tblServerAdminRegistration.sql b/loginserver/login_util/tblServerAdminRegistration.sql new file mode 100644 index 000000000..8e91a5a77 --- /dev/null +++ b/loginserver/login_util/tblServerAdminRegistration.sql @@ -0,0 +1,12 @@ +DROP TABLE IF EXISTS tblServerAdminRegistration; +CREATE TABLE IF NOT EXISTS tblServerAdminRegistration ( + ServerAdminID integer unsigned NOT NULL auto_increment, + AccountName varchar(30) NOT NULL, + AccountPassword varchar(30) NOT NULL, + FirstName varchar(40) NOT NULL, + LastName varchar(50) NOT NULL, + Email varchar(100) NULL, + RegistrationDate datetime NOT NULL, + RegistrationIPAddr varchar(15) NOT NULL, + PRIMARY KEY (ServerAdminID, Email) +) ENGINE=MyISAM; \ No newline at end of file diff --git a/loginserver/login_util/tblServerListType.sql b/loginserver/login_util/tblServerListType.sql new file mode 100644 index 000000000..fafaffe45 --- /dev/null +++ b/loginserver/login_util/tblServerListType.sql @@ -0,0 +1,10 @@ +DROP TABLE IF EXISTS tblServerListType; +CREATE TABLE IF NOT EXISTS tblServerListType ( + ServerListTypeID integer unsigned NOT NULL, + ServerListTypeDescription varchar(20) NOT NULL, + PRIMARY KEY (ServerListTypeID) +) ENGINE=MyISAM; + +INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (1, 'Legends'); +INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (2, 'Preferred'); +INSERT INTO tblServerListType (ServerListTypeID, ServerListTypeDescription) VALUES (3, 'Standard'); \ No newline at end of file diff --git a/loginserver/login_util/tblWorldServerRegistration.sql b/loginserver/login_util/tblWorldServerRegistration.sql new file mode 100644 index 000000000..74818fe0b --- /dev/null +++ b/loginserver/login_util/tblWorldServerRegistration.sql @@ -0,0 +1,13 @@ +DROP TABLE IF EXISTS tblWorldServerRegistration; +CREATE TABLE IF NOT EXISTS tblWorldServerRegistration ( + ServerID integer unsigned NOT NULL auto_increment, + ServerLongName varchar(100) NOT NULL, + ServerTagDescription varchar(50) NOT NULL DEFAULT '', + ServerShortName varchar(25) NOT NULL, + ServerListTypeID integer NOT NULL, + ServerLastLoginDate datetime NULL, + ServerLastIPAddr varchar(15) NULL, + ServerAdminID integer NOT NULL, + Note varchar(300) NULL, + PRIMARY KEY (ServerID, ServerLongName) +) ENGINE=InnoDB; diff --git a/queryserv/CMakeLists.txt b/queryserv/CMakeLists.txt new file mode 100644 index 000000000..96cdaf69c --- /dev/null +++ b/queryserv/CMakeLists.txt @@ -0,0 +1,43 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +SET(qserv_sources + database.cpp + lfguild.cpp + queryserv.cpp + queryservconfig.cpp + worldserver.cpp +) + +SET(qserv_headers + database.h + lfguild.h + queryservconfig.h + worldserver.h +) + +ADD_EXECUTABLE(queryserv ${qserv_sources} ${qserv_headers}) + +ADD_DEFINITIONS(-DQSERV) + +TARGET_LINK_LIBRARIES(queryserv Common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE}) + +IF(MSVC) + + SET_TARGET_PROPERTIES(queryserv PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF") + TARGET_LINK_LIBRARIES(queryserv "Ws2_32.lib") +ENDIF(MSVC) + +IF(MINGW) + TARGET_LINK_LIBRARIES(queryserv "WS2_32") +ENDIF(MINGW) + +IF(UNIX) + TARGET_LINK_LIBRARIES(queryserv "dl") + TARGET_LINK_LIBRARIES(queryserv "z") + TARGET_LINK_LIBRARIES(queryserv "m") + TARGET_LINK_LIBRARIES(queryserv "rt") + TARGET_LINK_LIBRARIES(queryserv "pthread") + ADD_DEFINITIONS(-fPIC) +ENDIF(UNIX) + +SET(EXECUTABLE_OUTPUT_PATH ../Bin) diff --git a/queryserv/database.cpp b/queryserv/database.cpp new file mode 100644 index 000000000..4e5f5a95c --- /dev/null +++ b/queryserv/database.cpp @@ -0,0 +1,323 @@ +/* + EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2008 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 +using namespace std; +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Disgrace: for windows compile +#ifdef _WINDOWS +#include +#define snprintf _snprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#else +#include "../common/unix.h" +#include +#endif + +#include "database.h" +#include "../common/eq_packet_structs.h" +#include "../common/MiscFunctions.h" +#include "../common/servertalk.h" + +Database::Database () +{ + DBInitVars(); +} + +/* +Establish a connection to a mysql database with the supplied parameters +*/ + +Database::Database(const char* host, const char* user, const char* passwd, const char* database, uint32 port) +{ + DBInitVars(); + Connect(host, user, passwd, database, 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)) + { + LogFile->write(EQEMuLog::Error, "Failed to connect to database: Error: %s", errbuf); + HandleMysqlError(errnum); + + return false; + } + else + { + LogFile->write(EQEMuLog::Status, "Using database '%s' at %s:%d",database,host,port); + return true; + } +} + +void Database::DBInitVars() { + +} + + + +void Database::HandleMysqlError(uint32 errnum) { +} + +/* + +Close the connection to the database +*/ +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)); + + 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); + } + + safe_delete_array(query); + safe_delete_array(S1); + safe_delete_array(S2); + safe_delete_array(S3); +} + +void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 Items) { + + 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); + } + + 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); + } + } + } +} + +void Database::LogPlayerHandin(QSPlayerLogHandin_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); + } + + 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); + } + } + } +} + +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); + } + + 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); + } + } + } +} + +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); + } + + 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); + } + } + } +} + +void Database::LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 Items) { + + 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); + } + + 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); + } + } + } +} + +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 new file mode 100644 index 000000000..0aebb6ae2 --- /dev/null +++ b/queryserv/database.h @@ -0,0 +1,63 @@ +/* + EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2008 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 CHATSERVER_DATABASE_H +#define CHATSERVER_DATABASE_H + +#define AUTHENTICATION_TIMEOUT 60 +#define INVALID_ID 0xFFFFFFFF + +#include "../common/debug.h" +#include "../common/types.h" +#include "../common/dbcore.h" +#include "../common/linked_list.h" +#include "../common/servertalk.h" +#include +#include +#include +using namespace std; + +//atoi is not uint32 or uint32 safe!!!! +#define atoul(str) strtoul(str, NULL, 10) + +class Database : public DBcore { +public: + Database(); + Database(const char* host, const char* user, const char* passwd, const char* database,uint32 port); + bool Connect(const char* host, const char* user, const char* passwd, const char* database,uint32 port); + ~Database(); + + bool 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 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); +protected: + void HandleMysqlError(uint32 errnum); +private: + void DBInitVars(); + +}; + +#endif diff --git a/queryserv/lfguild.cpp b/queryserv/lfguild.cpp new file mode 100644 index 000000000..9f33d039f --- /dev/null +++ b/queryserv/lfguild.cpp @@ -0,0 +1,435 @@ +#include +#include "lfguild.h" +#include "database.h" +#include "worldserver.h" +#include "../common/MiscFunctions.h" +#include "../common/packet_dump.h" +#include "../common/rulesys.h" + +extern WorldServer *worldserver; +extern Database database; + +PlayerLookingForGuild::PlayerLookingForGuild(char *Name, char *Comments, uint32 Level, uint32 Class, uint32 AACount, uint32 Timezone, uint32 TimePosted) +{ + this->Name = Name; + this->Comments = Comments; + this->Level = Level; + this->Class = Class; + this->AACount = AACount; + this->TimeZone = Timezone; + this->TimePosted = TimePosted; +} + +GuildLookingForPlayers::GuildLookingForPlayers(char *Name, char *Comments, uint32 FromLevel, uint32 ToLevel, uint32 Classes, uint32 AACount, uint32 Timezone, uint32 TimePosted) +{ + this->Name = Name; + this->Comments = Comments; + this->FromLevel = FromLevel; + this->ToLevel = ToLevel; + this->Classes = Classes; + this->AACount = AACount; + this->TimeZone = Timezone; + this->TimePosted = TimePosted; +} + +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); + + return false; + } + + safe_delete_array(query); + + while((row = mysql_fetch_row(result))) { + + 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); + } + 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); + + return true; +} + +void LFGuildManager::HandlePacket(ServerPacket *pack) +{ + char From[64]; + + pack->SetReadPosition(0); + uint32 FromZoneID = pack->ReadUInt32(); + uint32 FromInstanceID = pack->ReadUInt32(); + pack->ReadString(From); + pack->ReadSkipBytes(4); + uint32 SubType = pack->ReadUInt32(); + + switch(SubType) + { + case QSG_LFGuild_PlayerMatches: + { + uint32 FromLevel = pack->ReadUInt32(); + uint32 ToLevel = pack->ReadUInt32(); + uint32 MinAA = pack->ReadUInt32(); + uint32 TimeZone = pack->ReadUInt32(); + uint32 Classes = pack->ReadUInt32(); + + SendPlayerMatches(FromZoneID, FromInstanceID, From, FromLevel, ToLevel, MinAA, TimeZone, Classes); + break; + } + + case QSG_LFGuild_UpdatePlayerInfo: + { + char Comments[257]; + uint32 Class = pack->ReadUInt32(); + uint32 Level = pack->ReadUInt32(); + uint32 AAPoints = pack->ReadUInt32(); + pack->ReadString(Comments); + uint32 Toggle = pack->ReadUInt32(); + uint32 TimeZone = pack->ReadUInt32(); + + TogglePlayer(FromZoneID, FromInstanceID, From, Class, Level, AAPoints, Comments, Toggle, TimeZone); + + break; + } + case QSG_LFGuild_RequestPlayerInfo: + { + SendPlayerStatus(FromZoneID, FromInstanceID, From); + break; + } + case QSG_LFGuild_UpdateGuildInfo: + { + char GuildName[33], Comments[257]; + + pack->ReadString(GuildName); + pack->ReadString(Comments); + + uint32 FromLevel = pack->ReadUInt32(); + uint32 ToLevel = pack->ReadUInt32(); + uint32 Classes = pack->ReadUInt32(); + uint32 AACount = pack->ReadUInt32(); + uint32 Toggle = pack->ReadUInt32(); + uint32 TimeZone = pack->ReadUInt32(); + + ToggleGuild(FromZoneID, FromInstanceID, From, GuildName, Comments, FromLevel, ToLevel, Classes, AACount, Toggle, TimeZone); + + + break; + } + case QSG_LFGuild_GuildMatches: + { + uint32 Level = pack->ReadUInt32(); + uint32 AAPoints = pack->ReadUInt32(); + uint32 TimeZone = pack->ReadUInt32(); + uint32 Class = pack->ReadUInt32(); + + SendGuildMatches(FromZoneID, FromInstanceID, From, Level, AAPoints, TimeZone, Class); + break; + } + case QSG_LFGuild_RequestGuildInfo: + { + char GuildName[33]; + pack->ReadString(GuildName); + + SendGuildStatus(FromZoneID, FromInstanceID, From, GuildName); + break; + } + + default: + break; + } + + +} + +void LFGuildManager::SendPlayerMatches(uint32 FromZoneID, uint32 FromInstanceID, char *From, uint32 FromLevel, uint32 ToLevel, uint32 MinAA, uint32 TimeZone, uint32 Classes) +{ + std::list::iterator it; + std::list Matches; + + uint32 PacketSize = strlen(From) + 21, NumberOfMatches = 0; + + for(it = Players.begin(); it != Players.end(); ++it) + { + uint32 bitmask = 1 << (*it).Class ; + + if(((*it).Level >= FromLevel) && ((*it).Level <= ToLevel) && ((*it).AACount >= MinAA) && (bitmask & Classes) && ((TimeZone == 0xFFFFFFFF) || (TimeZone == (*it).TimeZone))) + { + ++NumberOfMatches; + Matches.push_back(*it); + PacketSize += (*it).Name.length() + (*it).Comments.length() + 18; + } + + } + + ServerPacket *pack = new ServerPacket(ServerOP_QueryServGeneric, PacketSize); + + pack->WriteUInt32(FromZoneID); + pack->WriteUInt32(FromInstanceID); + pack->WriteString(From); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_PlayerMatches); + pack->WriteUInt32(NumberOfMatches); + + for(it = Matches.begin(); it != Matches.end(); ++it) + { + pack->WriteString((*it).Name.c_str()); + pack->WriteString((*it).Comments.c_str()); + pack->WriteUInt32((*it).Level); + pack->WriteUInt32((*it).Class); + pack->WriteUInt32((*it).AACount); + pack->WriteUInt32((*it).TimeZone); + + } + worldserver->SendPacket(pack); + safe_delete(pack); +} + +void LFGuildManager::SendGuildMatches(uint32 FromZoneID, uint32 FromInstanceID, char *From, uint32 Level, uint32 AAPoints, uint32 TimeZone, uint32 Class) +{ + std::list::iterator it; + std::list Matches; + + uint32 bitmask = 1 << Class ; + + uint32 PacketSize = strlen(From) + 21, NumberOfMatches = 0; + + for(it = Guilds.begin(); it != Guilds.end(); ++it) + { + if((Level >= (*it).FromLevel) && (Level <= (*it).ToLevel) && (AAPoints >= (*it).AACount) && (bitmask & (*it).Classes) && (((*it).TimeZone == 0xFFFFFFFF) || (TimeZone == 0xFFFFFFFF) || (TimeZone == (*it).TimeZone))) + { + ++NumberOfMatches; + Matches.push_back(*it); + PacketSize += (*it).Name.length() + (*it).Comments.length() + 6; + } + + } + + ServerPacket *pack = new ServerPacket(ServerOP_QueryServGeneric, PacketSize); + + pack->WriteUInt32(FromZoneID); + pack->WriteUInt32(FromInstanceID); + pack->WriteString(From); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_GuildMatches); + pack->WriteUInt32(NumberOfMatches); + + for(it = Matches.begin(); it != Matches.end(); ++it) + { + pack->WriteString((*it).Name.c_str()); + pack->WriteUInt32((*it).TimeZone); + pack->WriteString((*it).Comments.c_str()); + } + worldserver->SendPacket(pack); + safe_delete(pack); +} + +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)) + { + 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); + + uint32 Now = time(NULL); + + 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); + + } + + ServerPacket *pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(From) + strlen(Comments) + 30); + + pack->WriteUInt32(FromZoneID); + pack->WriteUInt32(FromInstanceID); + pack->WriteString(From); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_RequestPlayerInfo); + pack->WriteString(Comments); + pack->WriteUInt32(TimeZone); + pack->WriteUInt32(Now); + pack->WriteUInt32(Toggle); + + 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) + { + 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); + + uint32 Now = time(NULL); + + if(Toggle == 1) + { + 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); + + } + ServerPacket *pack = new ServerPacket(ServerOP_LFGuildUpdate, strlen(GuildName) + strlen(Comments) + 30); + + pack->WriteString(GuildName); + pack->WriteString(Comments); + pack->WriteUInt32(FromLevel); + pack->WriteUInt32(ToLevel); + pack->WriteUInt32(Classes); + pack->WriteUInt32(AACount); + pack->WriteUInt32(TimeZone); + pack->WriteUInt32(Now); + pack->WriteUInt32(Toggle); + + worldserver->SendPacket(pack); + safe_delete(pack); +} + +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) + { + if((*it).TimePosted + 604800 <= (uint32)time(NULL)) + { + 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); + + safe_delete_array(query); + + it = Players.erase(it); + } + } + + for(it2 = Guilds.begin(); it2 != Guilds.end(); ++it2) + { + if((*it2).TimePosted + 2592000 <= time(NULL)) + { + 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); + + safe_delete_array(query); + + it2 = Guilds.erase(it2); + } + } +} + +void LFGuildManager::SendPlayerStatus(uint32 FromZoneID, uint32 FromInstanceID, char *From) +{ + + std::list::iterator it; + + for(it = Players.begin(); it != Players.end(); ++it) + { + if(!strcasecmp((*it).Name.c_str(), From)) + { + ServerPacket *pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(From) + (*it).Comments.length() + 30); + + pack->WriteUInt32(FromZoneID); + pack->WriteUInt32(FromInstanceID); + pack->WriteString(From); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_RequestPlayerInfo); + pack->WriteString((*it).Comments.c_str()); + pack->WriteUInt32((*it).TimeZone); + pack->WriteUInt32((*it).TimePosted); + pack->WriteUInt32(1); + + worldserver->SendPacket(pack); + safe_delete(pack); + break; + } + } +} + +void LFGuildManager::SendGuildStatus(uint32 FromZoneID, uint32 FromInstanceID, char *From, char *GuildName) +{ + std::list::iterator it; + + for(it = Guilds.begin(); it != Guilds.end(); ++it) + { + if(!strcasecmp((*it).Name.c_str(), GuildName)) + { + ServerPacket *pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(From) + (*it).Comments.length() + 42); + + pack->WriteUInt32(FromZoneID); + pack->WriteUInt32(FromInstanceID); + pack->WriteString(From); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_RequestGuildInfo); + pack->WriteString((*it).Comments.c_str()); + pack->WriteUInt32((*it).FromLevel); + pack->WriteUInt32((*it).ToLevel); + pack->WriteUInt32((*it).Classes); + pack->WriteUInt32((*it).AACount); + pack->WriteUInt32((*it).TimeZone); + pack->WriteUInt32((*it).TimePosted); + + worldserver->SendPacket(pack); + safe_delete(pack); + break; + } + } +} diff --git a/queryserv/lfguild.h b/queryserv/lfguild.h new file mode 100644 index 000000000..c9c59ded3 --- /dev/null +++ b/queryserv/lfguild.h @@ -0,0 +1,57 @@ +#ifndef LFGUILD_H +#define LFGUILD_H + +#include +#include +#include "../common/types.h" +#include "../common/servertalk.h" + +class PlayerLookingForGuild +{ + +public: + PlayerLookingForGuild(char *Name, char *Comments, uint32 Level, uint32 Class, uint32 AACount, uint32 Timezone, uint32 TimePosted); + + std::string Name; + std::string Comments; + uint32 Level; + uint32 Class; + uint32 AACount; + uint32 TimeZone; + uint32 TimePosted; +}; + +class GuildLookingForPlayers +{ + +public: + GuildLookingForPlayers(char *Name, char *Comments, uint32 FromLevel, uint32 ToLevel, uint32 Classes, uint32 AACount, uint32 Timezone, uint32 TimePosted); + + std::string Name; + std::string Comments; + uint32 FromLevel; + uint32 ToLevel; + uint32 Classes; + uint32 AACount; + uint32 TimeZone; + uint32 TimePosted; +}; + +class LFGuildManager +{ + +public: + bool LoadDatabase(); + void HandlePacket(ServerPacket *pack); + void ExpireEntries(); +private: + void SendPlayerMatches(uint32 FromZoneID, uint32 FromInstanceID, char *From, uint32 FromLevel, uint32 ToLevel, uint32 MinAA, uint32 TimeZone, uint32 Classes); + void SendGuildMatches(uint32 FromZoneID, uint32 FromInstanceID, char *From, uint32 Level, uint32 AAPoints, uint32 TimeZone, uint32 Class); + void TogglePlayer(uint32 FromZoneID, uint32 FromInstanceID, char *From, uint32 Class, uint32 Level, uint32 AAPoints, char *Comments, uint32 Toggle, uint32 TimeZone); + void ToggleGuild(uint32 FromZoneID, uint32 FromInstanceID, char *From, char* GuildName, char *Comments, uint32 FromLevel, uint32 ToLevel, uint32 Classes, uint32 AACount, uint32 Toggle, uint32 TimeZone); + void SendPlayerStatus(uint32 FromZoneID, uint32 FromInstanceID, char *From); + void SendGuildStatus(uint32 FromZoneID, uint32 FromInstanceID, char *From, char *GuildName); + std::list Players; + std::list Guilds; +}; +#endif /* LFGUILD_H */ diff --git a/queryserv/queryserv.cpp b/queryserv/queryserv.cpp new file mode 100644 index 000000000..eaa33b33a --- /dev/null +++ b/queryserv/queryserv.cpp @@ -0,0 +1,161 @@ +/* + EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2008 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/opcodemgr.h" +#include "../common/EQStreamFactory.h" +#include "../common/rulesys.h" +#include "../common/servertalk.h" +#include "../common/platform.h" +#include "../common/crash.h" +#include "database.h" +#include "queryservconfig.h" +#include "worldserver.h" +#include "lfguild.h" +#include +#include + +volatile bool RunLoops = true; + +uint32 MailMessagesSent = 0; +uint32 ChatMessagesSent = 0; + +TimeoutManager timeout_manager; + +Database database; +LFGuildManager lfguildmanager; +string WorldShortName; + +// RuleManager *rules = new RuleManager(); + +const queryservconfig *Config; + +WorldServer *worldserver = 0; + + +void CatchSignal(int sig_num) { + + RunLoops = false; + + if(worldserver) + worldserver->Disconnect(); +} + +int main() { + RegisterExecutablePlatform(ExePlatformQueryServ); + set_exception_handler(); + + Timer LFGuildExpireTimer(60000); + + Timer InterserverTimer(INTERSERVER_TIMER); // does auto-reconnect + + _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; + + _log(QUERYSERV__INIT, "Connecting to MySQL..."); + + if (!database.Connect( + Config->QSDatabaseHost.c_str(), + Config->QSDatabaseUsername.c_str(), + Config->QSDatabasePassword.c_str(), + Config->QSDatabaseDB.c_str(), + Config->QSDatabasePort)) { + _log(WORLD__INIT_ERR, "Cannot continue without a database connection."); + return(1); + } + + char tmp[64]; + + // Disable the Rule system, since we could be split brained from the main database + // if (database.GetVariable("RuleSet", tmp, sizeof(tmp)-1)) { + // _log(WORLD__INIT, "Loading rule set '%s'", tmp); + // if(!rules->LoadRules(&database, tmp)) { + // _log(QUERYSERV__ERROR, "Failed to load ruleset '%s', falling back to defaults.", tmp); + // } + // } else { + // if(!rules->LoadRules(&database, "default")) { + // _log(QUERYSERV__INIT, "No rule set configured, using default rules"); + // } else { + // _log(QUERYSERV__INIT, "Loaded default rule set 'default'", tmp); + // } + // } + + if (signal(SIGINT, CatchSignal) == SIG_ERR) { + _log(QUERYSERV__ERROR, "Could not set signal handler"); + return 0; + } + if (signal(SIGTERM, CatchSignal) == SIG_ERR) { + _log(QUERYSERV__ERROR, "Could not set signal handler"); + return 0; + } + + worldserver = new WorldServer; + + worldserver->Connect(); + + lfguildmanager.LoadDatabase(); + + while(RunLoops) { + + Timer::SetCurrentTime(); + + if(LFGuildExpireTimer.Check()) + lfguildmanager.ExpireEntries(); + + if (InterserverTimer.Check()) { + if (worldserver->TryReconnect() && (!worldserver->Connected())) + worldserver->AsyncConnect(); + } + worldserver->Process(); + + timeout_manager.CheckTimeouts(); + + Sleep(100); + } +} + +void UpdateWindowTitle(char* iNewTitle) { +#ifdef _WINDOWS + char tmp[500]; + if (iNewTitle) { + snprintf(tmp, sizeof(tmp), "QueryServ: %s", iNewTitle); + } + else { + snprintf(tmp, sizeof(tmp), "QueryServ"); + } + SetConsoleTitle(tmp); +#endif +} diff --git a/queryserv/queryservconfig.cpp b/queryserv/queryservconfig.cpp new file mode 100644 index 000000000..9720554ba --- /dev/null +++ b/queryserv/queryservconfig.cpp @@ -0,0 +1,29 @@ +/* + EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2008 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 "queryservconfig.h" + +queryservconfig *queryservconfig::_chat_config = NULL; + +string queryservconfig::GetByName(const string &var_name) const { + return(EQEmuConfig::GetByName(var_name)); +} diff --git a/queryserv/queryservconfig.h b/queryserv/queryservconfig.h new file mode 100644 index 000000000..d0c6533bc --- /dev/null +++ b/queryserv/queryservconfig.h @@ -0,0 +1,56 @@ +/* + EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2008 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 __queryservconfig_H +#define __queryservconfig_H + +#include "../common/EQEmuConfig.h" + +class queryservconfig : public EQEmuConfig { +public: + virtual string GetByName(const string &var_name) const; + +private: + + static queryservconfig *_chat_config; + +public: + + // Produce a const singleton + static const queryservconfig *get() { + if (_chat_config == NULL) + LoadConfig(); + return(_chat_config); + } + + // Load the config + static bool LoadConfig() { + if (_chat_config != NULL) + delete _chat_config; + _chat_config=new queryservconfig; + _config=_chat_config; + + return _config->ParseFile(EQEmuConfig::ConfigFile.c_str(),"server"); + } + +}; + +#endif diff --git a/queryserv/worldserver.cpp b/queryserv/worldserver.cpp new file mode 100644 index 000000000..3efd1bbbb --- /dev/null +++ b/queryserv/worldserver.cpp @@ -0,0 +1,179 @@ +/* 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 "../common/debug.h" +#include +using namespace std; +#include +#include +#include +#include +#include +#include + +#include "../common/servertalk.h" +#include "worldserver.h" +#include "queryservconfig.h" +#include "database.h" +#include "lfguild.h" +#include "../common/packet_functions.h" +#include "../common/md5.h" +#include "../common/packet_dump.h" + +extern WorldServer worldserver; +extern const queryservconfig *Config; +extern Database database; +extern LFGuildManager lfguildmanager; + +WorldServer::WorldServer() +: WorldConnection(EmuTCPConnection::packetModeQueryServ, Config->SharedKey.c_str()) +{ + pTryReconnect = true; +} + +WorldServer::~WorldServer() +{ +} + +void WorldServer::OnConnected() +{ + _log(QUERYSERV__INIT, "Connected to World."); + WorldConnection::OnConnected(); +} + +void WorldServer::Process() +{ + WorldConnection::Process(); + + if (!Connected()) + return; + + ServerPacket *pack = 0; + + while((pack = tcpc.PopPacket())) + { + _log(QUERYSERV__TRACE, "Received Opcode: %4X", pack->opcode); + + switch(pack->opcode) + { + case 0: { + break; + } + case ServerOP_KeepAlive: + { + break; + } + case ServerOP_Speech: + { + Server_Speech_Struct *SSS = (Server_Speech_Struct*)pack->pBuffer; + + string tmp1 = SSS->from; + string tmp2 = SSS->to; + + database.AddSpeech(tmp1.c_str(), tmp2.c_str(), SSS->message, SSS->minstatus, SSS->guilddbid, SSS->type); + break; + } + case ServerOP_QSPlayerLogTrades: + { + QSPlayerLogTrade_Struct *QS = (QSPlayerLogTrade_Struct*)pack->pBuffer; + uint32 Items = QS->char1_count + QS->char2_count; + database.LogPlayerTrade(QS, Items); + break; + } + case ServerOP_QSPlayerLogHandins: + { + QSPlayerLogHandin_Struct *QS = (QSPlayerLogHandin_Struct*)pack->pBuffer; + uint32 Items = QS->char_count + QS->npc_count; + database.LogPlayerHandin(QS, Items); + break; + } + 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: + { + QSPlayerLogDelete_Struct *QS = (QSPlayerLogDelete_Struct*)pack->pBuffer; + uint32 Items = QS->char_count; + database.LogPlayerDelete(QS, Items); + break; + } + case ServerOP_QSPlayerLogMoves: + { + QSPlayerLogMove_Struct *QS = (QSPlayerLogMove_Struct*)pack->pBuffer; + uint32 Items = QS->char_count; + database.LogPlayerMove(QS, Items); + break; + } + case ServerOP_QSMerchantLogTransactions: + { + QSMerchantLogTransaction_Struct *QS = (QSMerchantLogTransaction_Struct*)pack->pBuffer; + uint32 Items = QS->char_count + QS->merchant_count; + database.LogMerchantTransaction(QS, Items); + 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. + char From[64]; + pack->SetReadPosition(8); + pack->ReadString(From); + uint32 Type = pack->ReadUInt32(); + + switch(Type) + { + case QSG_LFGuild: + { + lfguildmanager.HandlePacket(pack); + break; + } + + default: + _log(QUERYSERV__ERROR, "Received unhandled ServerOP_QueryServGeneric", Type); + break; + } + + break; + } + + + } + } + + safe_delete(pack); + return; +} + diff --git a/queryserv/worldserver.h b/queryserv/worldserver.h new file mode 100644 index 000000000..e579b467d --- /dev/null +++ b/queryserv/worldserver.h @@ -0,0 +1,35 @@ +/* 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 WORLDSERVER_H +#define WORLDSERVER_H + +#include "../common/worldconn.h" +#include "../common/eq_packet_structs.h" + +class WorldServer : public WorldConnection +{ +public: + WorldServer(); + virtual ~WorldServer(); + virtual void Process(); + +private: + virtual void OnConnected(); +}; +#endif + diff --git a/ucs/CMakeLists.txt b/ucs/CMakeLists.txt new file mode 100644 index 000000000..6f0d023a0 --- /dev/null +++ b/ucs/CMakeLists.txt @@ -0,0 +1,45 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +SET(ucs_sources + chatchannel.cpp + clientlist.cpp + database.cpp + ucs.cpp + ucsconfig.cpp + worldserver.cpp +) + +SET(ucs_headers + chatchannel.h + clientlist.h + database.h + ucsconfig.h + worldserver.h +) + +ADD_EXECUTABLE(ucs ${ucs_sources} ${ucs_headers}) + +ADD_DEFINITIONS(-DUCS) + +TARGET_LINK_LIBRARIES(ucs Common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE}) + +IF(MSVC) + + SET_TARGET_PROPERTIES(ucs PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF") + TARGET_LINK_LIBRARIES(ucs "Ws2_32.lib") +ENDIF(MSVC) + +IF(MINGW) + TARGET_LINK_LIBRARIES(ucs "WS2_32") +ENDIF(MINGW) + +IF(UNIX) + TARGET_LINK_LIBRARIES(ucs "dl") + TARGET_LINK_LIBRARIES(ucs "z") + TARGET_LINK_LIBRARIES(ucs "m") + TARGET_LINK_LIBRARIES(ucs "rt") + TARGET_LINK_LIBRARIES(ucs "pthread") + ADD_DEFINITIONS(-fPIC) +ENDIF(UNIX) + +SET(EXECUTABLE_OUTPUT_PATH ../Bin) diff --git a/ucs/chatchannel.cpp b/ucs/chatchannel.cpp new file mode 100644 index 000000000..163a6301e --- /dev/null +++ b/ucs/chatchannel.cpp @@ -0,0 +1,706 @@ +/* + EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2008 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 "chatchannel.h" +#include "clientlist.h" +#include "database.h" +#include "../common/MiscFunctions.h" +#include + +extern Database database; +extern uint32 ChatMessagesSent; + +ChatChannel::ChatChannel(string inName, string inOwner, string inPassword, bool inPermanent, int inMinimumStatus) : + DeleteTimer(0) { + + Name = inName; + + Owner = inOwner; + + Password = inPassword; + + Permanent = inPermanent; + + MinimumStatus = inMinimumStatus; + + Moderated = false; + + _log(UCS__TRACE, "New ChatChannel created: Name: [%s], Owner: [%s], Password: [%s], MinStatus: %i", + Name.c_str(), Owner.c_str(), Password.c_str(), MinimumStatus); + +} + +ChatChannel::~ChatChannel() { + + LinkedListIterator iterator(ClientsInChannel); + + iterator.Reset(); + + while(iterator.MoreElements()) + iterator.RemoveCurrent(false); +} + +ChatChannel* ChatChannelList::CreateChannel(string Name, string Owner, string Password, bool Permanent, int MinimumStatus) { + + ChatChannel *NewChannel = new ChatChannel(CapitaliseName(Name), Owner, Password, Permanent, MinimumStatus); + + ChatChannels.Insert(NewChannel); + + return NewChannel; +} + +ChatChannel* ChatChannelList::FindChannel(string Name) { + + string NormalisedName = CapitaliseName(Name); + + LinkedListIterator iterator(ChatChannels); + + iterator.Reset(); + + while(iterator.MoreElements()) { + + ChatChannel *CurrentChannel = iterator.GetData(); + + if(CurrentChannel && (CurrentChannel->Name == NormalisedName)) + return iterator.GetData(); + + iterator.Advance(); + } + + return NULL; +} + +void ChatChannelList::SendAllChannels(Client *c) { + + if(!c) return; + + if(!c->CanListAllChannels()) { + c->GeneralChannelMessage("You do not have permission to list all the channels."); + return; + } + + c->GeneralChannelMessage("All current channels:"); + + int ChannelsInLine = 0; + + LinkedListIterator iterator(ChatChannels); + + iterator.Reset(); + + string Message; + + char CountString[10]; + + while(iterator.MoreElements()) { + + ChatChannel *CurrentChannel = iterator.GetData(); + + if(!CurrentChannel || (CurrentChannel->GetMinStatus() > c->GetAccountStatus())) { + + iterator.Advance(); + + continue; + } + + if(ChannelsInLine > 0) + Message += ", "; + + sprintf(CountString, "(%i)", CurrentChannel->MemberCount(c->GetAccountStatus())); + + Message += CurrentChannel->GetName(); + + Message += CountString; + + ChannelsInLine++; + + if(ChannelsInLine == 6) { + + c->GeneralChannelMessage(Message); + + ChannelsInLine = 0; + + Message.clear(); + } + + iterator.Advance(); + } + + if(ChannelsInLine > 0) + c->GeneralChannelMessage(Message); + +} + +void ChatChannelList::RemoveChannel(ChatChannel *Channel) { + + _log(UCS__TRACE, "RemoveChannel(%s)", Channel->GetName().c_str()); + + LinkedListIterator iterator(ChatChannels); + + iterator.Reset(); + + while(iterator.MoreElements()) { + + if(iterator.GetData() == Channel) { + + iterator.RemoveCurrent(); + + return; + } + + iterator.Advance(); + } +} + +void ChatChannelList::RemoveAllChannels() { + + _log(UCS__TRACE, "RemoveAllChannels"); + + LinkedListIterator iterator(ChatChannels); + + iterator.Reset(); + + while(iterator.MoreElements()) + iterator.RemoveCurrent(); +} + +int ChatChannel::MemberCount(int Status) { + + int Count = 0; + + LinkedListIterator iterator(ClientsInChannel); + + iterator.Reset(); + + while(iterator.MoreElements()) { + + Client *ChannelClient = iterator.GetData(); + + if(ChannelClient && (!ChannelClient->GetHideMe() || (ChannelClient->GetAccountStatus() < Status))) + Count++; + + iterator.Advance(); + } + + return Count; +} + +void ChatChannel::SetPassword(string inPassword) { + + Password = inPassword; + + if(Permanent) + { + RemoveApostrophes(Password); + database.SetChannelPassword(Name, Password); + } +} + +void ChatChannel::SetOwner(string inOwner) { + + Owner = inOwner; + + if(Permanent) + database.SetChannelOwner(Name, Owner); +} + +void ChatChannel::AddClient(Client *c) { + + if(!c) return; + + DeleteTimer.Disable(); + + if(IsClientInChannel(c)) { + + _log(UCS__ERROR, "Client %s already in channel %s", c->GetName().c_str(), GetName().c_str()); + + return; + } + + bool HideMe = c->GetHideMe(); + + int AccountStatus = c->GetAccountStatus(); + + _log(UCS__TRACE, "Adding %s to channel %s", c->GetName().c_str(), Name.c_str()); + + LinkedListIterator iterator(ClientsInChannel); + + iterator.Reset(); + + while(iterator.MoreElements()) { + + Client *CurrentClient = iterator.GetData(); + + if(CurrentClient && CurrentClient->IsAnnounceOn()) + if(!HideMe || (CurrentClient->GetAccountStatus() > AccountStatus)) + CurrentClient->AnnounceJoin(this, c); + + iterator.Advance(); + } + + ClientsInChannel.Insert(c); + +} + +bool ChatChannel::RemoveClient(Client *c) { + + if(!c) return false; + + _log(UCS__TRACE, "RemoveClient %s from channel %s", c->GetName().c_str(), GetName().c_str()); + + bool HideMe = c->GetHideMe(); + + int AccountStatus = c->GetAccountStatus(); + + int PlayersInChannel = 0; + + LinkedListIterator iterator(ClientsInChannel); + + iterator.Reset(); + + while(iterator.MoreElements()) { + + Client *CurrentClient = iterator.GetData(); + + if(CurrentClient == c) { + iterator.RemoveCurrent(false); + } + else if(CurrentClient) { + + PlayersInChannel++; + + if(CurrentClient->IsAnnounceOn()) + if(!HideMe || (CurrentClient->GetAccountStatus() > AccountStatus)) + CurrentClient->AnnounceLeave(this, c); + + iterator.Advance(); + } + + } + + if((PlayersInChannel == 0) && !Permanent) { + + if((Password.length() == 0) || (RuleI(Channels, DeleteTimer) == 0)) + return false; + + _log(UCS__TRACE, "Starting delete timer for empty password protected channel %s", Name.c_str()); + + DeleteTimer.Start(RuleI(Channels, DeleteTimer) * 60000); + } + + return true; +} + +void ChatChannel::SendOPList(Client *c) { + + if(!c) return; + + c->GeneralChannelMessage("Channel " + Name + " op-list: (Owner=" + Owner + ")"); + + list::iterator Iterator; + + for(Iterator = Moderators.begin(); Iterator != Moderators.end(); Iterator++) + c->GeneralChannelMessage((*Iterator)); + +} + +void ChatChannel::SendChannelMembers(Client *c) { + + if(!c) return; + + char CountString[10]; + + sprintf(CountString, "(%i)", MemberCount(c->GetAccountStatus())); + + string Message = "Channel " + GetName(); + + Message += CountString; + + Message += " members:"; + + c->GeneralChannelMessage(Message); + + int AccountStatus = c->GetAccountStatus(); + + Message.clear(); + + int MembersInLine = 0; + + LinkedListIterator iterator(ClientsInChannel); + + iterator.Reset(); + + while(iterator.MoreElements()) { + + Client *ChannelClient = iterator.GetData(); + + // Don't list hidden characters with status higher or equal than the character requesting the list. + // + if(!ChannelClient || (ChannelClient->GetHideMe() && (ChannelClient->GetAccountStatus() >= AccountStatus))) { + iterator.Advance(); + continue; + } + + if(MembersInLine > 0) + Message += ", "; + + Message += ChannelClient->GetName(); + + MembersInLine++; + + if(MembersInLine == 6) { + + c->GeneralChannelMessage(Message); + + MembersInLine = 0; + + Message.clear(); + } + + iterator.Advance(); + } + + if(MembersInLine > 0) + c->GeneralChannelMessage(Message); + +} + +void ChatChannel::SendMessageToChannel(string Message, Client* Sender) { + + if(!Sender) return; + + ChatMessagesSent++; + + LinkedListIterator iterator(ClientsInChannel); + + iterator.Reset(); + + while(iterator.MoreElements()) { + + Client *ChannelClient = iterator.GetData(); + + if(ChannelClient) + { + _log(UCS__TRACE, "Sending message to %s from %s", + ChannelClient->GetName().c_str(), Sender->GetName().c_str()); + ChannelClient->SendChannelMessage(Name, Message, Sender); + } + + iterator.Advance(); + } +} + +void ChatChannel::SetModerated(bool inModerated) { + + Moderated = inModerated; + + LinkedListIterator iterator(ClientsInChannel); + + iterator.Reset(); + + while(iterator.MoreElements()) { + + Client *ChannelClient = iterator.GetData(); + + if(ChannelClient) { + + if(Moderated) + ChannelClient->GeneralChannelMessage("Channel " + Name + " is now moderated."); + else + ChannelClient->GeneralChannelMessage("Channel " + Name + " is no longer moderated."); + } + + iterator.Advance(); + } + +} +bool ChatChannel::IsClientInChannel(Client *c) { + + if(!c) return false; + + LinkedListIterator iterator(ClientsInChannel); + + iterator.Reset(); + + while(iterator.MoreElements()) { + + if(iterator.GetData() == c) + return true; + + iterator.Advance(); + } + + return false; +} + +ChatChannel *ChatChannelList::AddClientToChannel(string ChannelName, Client *c) { + + if(!c) return NULL; + + if((ChannelName.length() > 0) && (isdigit(ChannelName[0]))) { + + c->GeneralChannelMessage("The channel name can not begin with a number."); + + return NULL; + } + + string NormalisedName, Password; + + string::size_type Colon = ChannelName.find_first_of(":"); + + if(Colon == string::npos) + NormalisedName = CapitaliseName(ChannelName); + else { + NormalisedName = CapitaliseName(ChannelName.substr(0, Colon)); + + Password = ChannelName.substr(Colon + 1); + } + + if((NormalisedName.length() > 64) || (Password.length() > 64)) { + + c->GeneralChannelMessage("The channel name or password cannot exceed 64 characters."); + + return NULL; + } + + _log(UCS__TRACE, "AddClient to channel [%s] with password [%s]", NormalisedName.c_str(), Password.c_str()); + + ChatChannel *RequiredChannel = FindChannel(NormalisedName); + + if(!RequiredChannel) + RequiredChannel = CreateChannel(NormalisedName, c->GetName(), Password, false, 0); + + if(RequiredChannel->GetMinStatus() > c->GetAccountStatus()) { + + string Message = "You do not have the required account status to join channel " + NormalisedName; + + c->GeneralChannelMessage(Message); + + return NULL; + } + + if(RequiredChannel->IsClientInChannel(c)) + return NULL; + + if(RequiredChannel->IsInvitee(c->GetName())) { + + RequiredChannel->AddClient(c); + + RequiredChannel->RemoveInvitee(c->GetName()); + + return RequiredChannel; + } + + if(RequiredChannel->CheckPassword(Password) || RequiredChannel->IsOwner(c->GetName()) || RequiredChannel->IsModerator(c->GetName()) || + c->IsChannelAdmin()) { + + RequiredChannel->AddClient(c); + + return RequiredChannel; + } + + c->GeneralChannelMessage("Incorrect password for channel " + (NormalisedName)); + + return NULL; +} + +ChatChannel *ChatChannelList::RemoveClientFromChannel(string inChannelName, Client *c) { + + if(!c) return NULL; + + string ChannelName = inChannelName; + + if((inChannelName.length() > 0) && isdigit(ChannelName[0])) + ChannelName = c->ChannelSlotName(atoi(inChannelName.c_str())); + + ChatChannel *RequiredChannel = FindChannel(ChannelName); + + if(!RequiredChannel) + return NULL; + + // RemoveClient will return false if there is no-one left in the channel, and the channel is not permanent and has + // no password. + // + if(!RequiredChannel->RemoveClient(c)) + RemoveChannel(RequiredChannel); + + return RequiredChannel; +} + +void ChatChannelList::Process() { + + LinkedListIterator iterator(ChatChannels); + + iterator.Reset(); + + while(iterator.MoreElements()) { + + ChatChannel *CurrentChannel = iterator.GetData(); + + if(CurrentChannel && CurrentChannel->ReadyToDelete()) { + + _log(UCS__TRACE, "Empty temporary password protected channel %s being destroyed.", + CurrentChannel->GetName().c_str()); + + RemoveChannel(CurrentChannel); + } + + iterator.Advance(); + + } +} + +void ChatChannel::AddInvitee(string Invitee) { + + if(!IsInvitee(Invitee)) { + + Invitees.push_back(Invitee); + + _log(UCS__TRACE, "Added %s as invitee to channel %s", Invitee.c_str(), Name.c_str()); + } + +} + +void ChatChannel::RemoveInvitee(string Invitee) { + + list::iterator Iterator; + + for(Iterator = Invitees.begin(); Iterator != Invitees.end(); Iterator++) { + + if((*Iterator) == Invitee) { + + Invitees.erase(Iterator); + + _log(UCS__TRACE, "Removed %s as invitee to channel %s", Invitee.c_str(), Name.c_str()); + + return; + } + } +} + +bool ChatChannel::IsInvitee(string Invitee) { + + list::iterator Iterator; + + for(Iterator = Invitees.begin(); Iterator != Invitees.end(); Iterator++) { + + if((*Iterator) == Invitee) + return true; + } + + return false; +} + +void ChatChannel::AddModerator(string Moderator) { + + if(!IsModerator(Moderator)) { + + Moderators.push_back(Moderator); + + _log(UCS__TRACE, "Added %s as moderator to channel %s", Moderator.c_str(), Name.c_str()); + } + +} + +void ChatChannel::RemoveModerator(string Moderator) { + + list::iterator Iterator; + + for(Iterator = Moderators.begin(); Iterator != Moderators.end(); Iterator++) { + + if((*Iterator) == Moderator) { + + Moderators.erase(Iterator); + + _log(UCS__TRACE, "Removed %s as moderator to channel %s", Moderator.c_str(), Name.c_str()); + + return; + } + } +} + +bool ChatChannel::IsModerator(string Moderator) { + + list::iterator Iterator; + + for(Iterator = Moderators.begin(); Iterator != Moderators.end(); Iterator++) { + + if((*Iterator) == Moderator) + return true; + } + + return false; +} + +void ChatChannel::AddVoice(string inVoiced) { + + if(!HasVoice(inVoiced)) { + + Voiced.push_back(inVoiced); + + _log(UCS__TRACE, "Added %s as voiced to channel %s", inVoiced.c_str(), Name.c_str()); + } + +} + +void ChatChannel::RemoveVoice(string inVoiced) { + + list::iterator Iterator; + + for(Iterator = Voiced.begin(); Iterator != Voiced.end(); Iterator++) { + + if((*Iterator) == inVoiced) { + + Voiced.erase(Iterator); + + _log(UCS__TRACE, "Removed %s as voiced to channel %s", inVoiced.c_str(), Name.c_str()); + + return; + } + } +} + +bool ChatChannel::HasVoice(string inVoiced) { + + list::iterator Iterator; + + for(Iterator = Voiced.begin(); Iterator != Voiced.end(); Iterator++) { + + if((*Iterator) == inVoiced) + return true; + } + + return false; +} + +string CapitaliseName(string inString) { + + string NormalisedName = inString; + + for(unsigned int i = 0; i < NormalisedName.length(); i++) { + + if(i == 0) + NormalisedName[i] = toupper(NormalisedName[i]); + else + NormalisedName[i] = tolower(NormalisedName[i]); + } + + return NormalisedName; +} diff --git a/ucs/chatchannel.h b/ucs/chatchannel.h new file mode 100644 index 000000000..e18a831b8 --- /dev/null +++ b/ucs/chatchannel.h @@ -0,0 +1,92 @@ +#ifndef CHATCHANNEL_H +#define CHATCHANNEL_H + +//#include "clientlist.h" +#include "../common/linked_list.h" +#include "../common/timer.h" +#include +#include + +using namespace std; + +class Client; + +class ChatChannel { + +public: + + ChatChannel(string inName, string inOwner, string inPassword, bool inPermanent, int inMinimumStatus = 0); + ~ChatChannel(); + + void AddClient(Client *c); + bool RemoveClient(Client *c); + bool IsClientInChannel(Client *c); + + int MemberCount(int Status); + string GetName() { return Name; } + void SendMessageToChannel(string Message, Client* Sender); + bool CheckPassword(string inPassword) { return ((Password.length() == 0) || (Password == inPassword)); } + void SetPassword(string inPassword); + bool IsOwner(string Name) { return (Owner == Name); } + void SetOwner(string inOwner); + void SendChannelMembers(Client *c); + int GetMinStatus() { return MinimumStatus; } + bool ReadyToDelete() { return DeleteTimer.Check(); } + void SendOPList(Client *c); + void AddInvitee(string Invitee); + void RemoveInvitee(string Invitee); + bool IsInvitee(string Invitee); + void AddModerator(string Moderator); + void RemoveModerator(string Modeerator); + bool IsModerator(string Moderator); + void AddVoice(string Voiced); + void RemoveVoice(string Voiced); + bool HasVoice(string Voiced); + inline bool IsModerated() { return Moderated; } + void SetModerated(bool inModerated); + + friend class ChatChannelList; + +private: + + string Name; + string Owner; + string Password; + + bool Permanent; + bool Moderated; + + int MinimumStatus; + + Timer DeleteTimer; + + LinkedList ClientsInChannel; + + list Moderators; + list Invitees; + list Voiced; + +}; + +class ChatChannelList { + +public: + ChatChannel* CreateChannel(string Name, string Owner, string Passwordi, bool Permanent, int MinimumStatus = 0); + ChatChannel* FindChannel(string Name); + ChatChannel* AddClientToChannel(string Channel, Client *c); + ChatChannel* RemoveClientFromChannel(string Channel, Client *c); + void RemoveClientFromAllChannels(Client *c); + void RemoveChannel(ChatChannel *Channel); + void RemoveAllChannels(); + void SendAllChannels(Client *c); + void Process(); + +private: + + LinkedList ChatChannels; + +}; + +string CapitaliseName(string inString); + +#endif diff --git a/ucs/clientlist.cpp b/ucs/clientlist.cpp new file mode 100644 index 000000000..92cf7806c --- /dev/null +++ b/ucs/clientlist.cpp @@ -0,0 +1,2409 @@ +/* + EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2008 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/MiscFunctions.h" + +#include "clientlist.h" +#include "database.h" +#include "chatchannel.h" + +#include "../common/EQStreamFactory.h" +#include "../common/EmuTCPConnection.h" +#include "../common/EmuTCPServer.h" +#include +#include +#include +#include +#include + +using namespace std; + +extern Database database; +extern string WorldShortName; +extern string GetMailPrefix(); +extern ChatChannelList *ChannelList; +extern Clientlist *CL; +extern uint32 ChatMessagesSent; +extern uint32 MailMessagesSent; + +int LookupCommand(const char *ChatCommand) { + + if(!ChatCommand) return -1; + + for(int i = 0; i < CommandEndOfList; i++) { + + if(!strcasecmp(Commands[i].CommandString, ChatCommand)) + return Commands[i].CommandCode; + } + + return -1; +} + +void Client::SendUptime() { + + uint32 ms = Timer::GetCurrentTime(); + uint32 d = ms / 86400000; + ms -= d * 86400000; + uint32 h = ms / 3600000; + ms -= h * 3600000; + uint32 m = ms / 60000; + ms -= m * 60000; + uint32 s = ms / 1000; + + char *Buffer = NULL; + + MakeAnyLenString(&Buffer, "UCS has been up for %02id %02ih %02im %02is", d, h, m, s); + GeneralChannelMessage(Buffer); + safe_delete_array(Buffer); + MakeAnyLenString(&Buffer, "Chat Messages Sent: %i, Mail Messages Sent: %i", ChatMessagesSent, MailMessagesSent); + GeneralChannelMessage(Buffer); + safe_delete_array(Buffer); +} + +vector ParseRecipients(string RecipientString) { + + // This method parses the Recipient List in the mailto command, which can look like this example: + // + // "Baalinor , + // -Friends , + // Guild , SOE.EQ.BTG2.luccerathe, SOE.EQ.BTG2.codsas + // + // First, it splits it up at commas, so it looks like this: + // + // Baalinor + // -Friends + // Guild + // SOE.EQ.BTG2.luccerathe + // SOE.EQ.BTG2 + // + // Then, if an entry has a '<' in it, it extracts the text between the < and > + // If the text between the < and > has a space in it, then there are multiple addresses in there, so those are extracted. + // + // The prefix (SOE.EQ.) is discarded, the names are normalised so they begin with a single upper case character + // followed by lower case. + // + // The vector is sorted and any duplicates discarded, so the vector we return, in our example, looks like this: + // + // Baalinor + // -Playedtest + // -Dyetest + // Dsfvxcbcx + // Necronor + // Luccerathe + // Codsas + // + // The '-' prefix indicates 'Secret To' (like BCC:) + // + vector RecipientList; + + string Secret; + + string::size_type CurrentPos, Comma, FirstLT, LastGT, Space, LastPeriod; + + CurrentPos = 0; + + while(CurrentPos != string::npos) { + + Comma = RecipientString.find_first_of(",", CurrentPos); + + if(Comma == string::npos) { + + RecipientList.push_back(RecipientString.substr(CurrentPos)); + + break; + } + RecipientList.push_back(RecipientString.substr(CurrentPos, Comma - CurrentPos)); + + CurrentPos = Comma + 2; + } + + vector::iterator Iterator; + + Iterator = RecipientList.begin(); + + while(Iterator != RecipientList.end()) { + + if((*Iterator)[0] == '-') { + + Secret = "-"; + + while((*Iterator)[0] == '-') + (*Iterator) = (*Iterator).substr(1); + } + else + Secret = ""; + + FirstLT = (*Iterator).find_first_of("<"); + + if(FirstLT != string::npos) { + + LastGT = (*Iterator).find_last_of(">"); + + if(LastGT != string::npos) { + + (*Iterator) = (*Iterator).substr(FirstLT + 1, LastGT - FirstLT - 1); + + if((*Iterator).find_first_of(" ") != string::npos) { + + string Recips = (*Iterator); + + RecipientList.erase(Iterator); + + CurrentPos = 0; + + while(CurrentPos != string::npos) { + + Space = Recips.find_first_of(" ", CurrentPos); + + if(Space == string::npos) { + + RecipientList.push_back(Secret + Recips.substr(CurrentPos)); + + break; + } + RecipientList.push_back(Secret + Recips.substr(CurrentPos, + Space - CurrentPos)); + CurrentPos = Space + 1; + } + Iterator = RecipientList.begin(); + + continue; + } + } + } + + + (*Iterator) = Secret + (*Iterator); + + Iterator++; + + } + + for(Iterator = RecipientList.begin(); Iterator != RecipientList.end(); Iterator++) { + + if((*Iterator).length() > 0) { + + if((*Iterator)[0] == '-') + Secret = "-"; + else + Secret = ""; + + LastPeriod = (*Iterator).find_last_of("."); + + if(LastPeriod != string::npos) { + + (*Iterator) = (*Iterator).substr(LastPeriod + 1); + + for(unsigned int i = 0; i < (*Iterator).length(); i++) { + + if(i == 0) + (*Iterator)[i] = toupper((*Iterator)[i]); + else + (*Iterator)[i] = tolower((*Iterator)[i]); + } + + (*Iterator) = Secret + (*Iterator); + } + } + + } + + sort(RecipientList.begin(), RecipientList.end()); + + vector::iterator new_end_pos = unique(RecipientList.begin(), RecipientList.end()); + + RecipientList.erase(new_end_pos, RecipientList.end()); + + return RecipientList; + +} + +static void ProcessMailTo(Client *c, string MailMessage) { + + _log(UCS__TRACE, "MAILTO: From %s, %s", c->MailBoxName().c_str(), MailMessage.c_str()); + + vector Recipients; + + string::size_type FirstQuote = MailMessage.find_first_of("\"", 0); + + string::size_type NextQuote = MailMessage.find_first_of("\"", FirstQuote + 1); + + string RecipientsString = MailMessage.substr(FirstQuote+1, NextQuote-FirstQuote - 1); + + Recipients = ParseRecipients(RecipientsString); + + // Now extract the subject field. This is in quotes if it is more than one word + // + string Subject; + + string::size_type SubjectStart = NextQuote + 2; + + string::size_type SubjectEnd; + + if(MailMessage.substr(SubjectStart, 1) == "\"") { + + SubjectEnd = MailMessage.find_first_of("\"", SubjectStart + 1); + + Subject = MailMessage.substr(SubjectStart + 1, SubjectEnd - SubjectStart - 1); + + SubjectEnd += 2; + + } + else { + SubjectEnd = MailMessage.find_first_of(" ", SubjectStart); + + Subject = MailMessage.substr(SubjectStart, SubjectEnd - SubjectStart); + + SubjectEnd++; + + } + string Body = MailMessage.substr(SubjectEnd); + + bool Success = true; + + RecipientsString.clear(); + + int VisibleRecipients = 0; + + for(unsigned int i = 0; i 0) + + RecipientsString += ", "; + + VisibleRecipients++; + + RecipientsString = RecipientsString + GetMailPrefix() + Recipients[i]; + } + } + if(VisibleRecipients == 0) + RecipientsString = ""; + + for(unsigned int i=0; iMailBoxName(), Subject, Body, RecipientsString)) { + + _log(UCS__ERROR, "Failed in SendMail(%s, %s, %s, %s)", Recipients[i].c_str(), + c->MailBoxName().c_str(), Subject.c_str(), RecipientsString.c_str()); + + int PacketLength = 10 + Recipients[i].length() + Subject.length(); + + // Failure + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MailDeliveryStatus, PacketLength); + + char *PacketBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_STRING(PacketBuffer, "1"); + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0x20); + VARSTRUCT_ENCODE_STRING(PacketBuffer, Recipients[i].c_str()); + VARSTRUCT_ENCODE_STRING(PacketBuffer, Subject.c_str()); + VARSTRUCT_ENCODE_STRING(PacketBuffer, "0"); + VARSTRUCT_ENCODE_TYPE(uint16, PacketBuffer, 0x3237); + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0x0); + + _pkt(UCS__PACKETS, outapp); + + c->QueuePacket(outapp); + + safe_delete(outapp); + + Success = false; + } + } + + if(Success) { + // Success + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MailDeliveryStatus, 10); + + char *PacketBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_STRING(PacketBuffer, "1"); + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0); + VARSTRUCT_ENCODE_STRING(PacketBuffer, "test"); // Doesn't matter what we send in this text field. + VARSTRUCT_ENCODE_STRING(PacketBuffer, "1"); + + _pkt(UCS__PACKETS, outapp); + + c->QueuePacket(outapp); + + safe_delete(outapp); + } +} + +static void ProcessMailTo(Client *c, string from, string subject, string message) { +} + +static void ProcessSetMessageStatus(string SetMessageCommand) { + + int MessageNumber; + + int Status; + + switch(SetMessageCommand[0]) { + + case 'R': // READ + Status = 3; + break; + + case 'T': // TRASH + Status = 4; + break; + + default: // DELETE + Status = 0; + + } + string::size_type NumStart = SetMessageCommand.find_first_of("123456789"); + + while(NumStart != string::npos) { + + string::size_type NumEnd = SetMessageCommand.find_first_of(" ", NumStart); + + if(NumEnd == string::npos) { + + MessageNumber = atoi(SetMessageCommand.substr(NumStart).c_str()); + + database.SetMessageStatus(MessageNumber, Status); + + break; + } + + MessageNumber = atoi(SetMessageCommand.substr(NumStart, NumEnd-NumStart).c_str()); + + database.SetMessageStatus(MessageNumber, Status); + + NumStart = SetMessageCommand.find_first_of("123456789", NumEnd); + } +} + +static void ProcessCommandBuddy(Client *c, string Buddy) { + + _log(UCS__TRACE, "Received buddy command with parameters %s", Buddy.c_str()); + c->GeneralChannelMessage("Buddy list modified"); + + uint8 SubAction = 1; + + if(Buddy.substr(0, 1) == "-") + SubAction = 0; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_Buddy, Buddy.length() + 2); + char *PacketBuffer = (char *)outapp->pBuffer; + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, SubAction); + + if(SubAction == 1) { + VARSTRUCT_ENCODE_STRING(PacketBuffer, Buddy.c_str()); + database.AddFriendOrIgnore(c->GetCharID(), 1, Buddy); + } + else { + VARSTRUCT_ENCODE_STRING(PacketBuffer, Buddy.substr(1).c_str()); + database.RemoveFriendOrIgnore(c->GetCharID(), 1, Buddy.substr(1)); + } + + _pkt(UCS__PACKETS, outapp); + c->QueuePacket(outapp); + + safe_delete(outapp); + +} + +static void ProcessCommandIgnore(Client *c, string Ignoree) { + + _log(UCS__TRACE, "Received ignore command with parameters %s", Ignoree.c_str()); + c->GeneralChannelMessage("Ignore list modified"); + + uint8 SubAction = 0; + + if(Ignoree.substr(0, 1) == "-") { + SubAction = 1; + Ignoree = Ignoree.substr(1); + + // Strip off the SOE.EQ.. + // + string CharacterName; + + string::size_type LastPeriod = Ignoree.find_last_of("."); + + if(LastPeriod == string::npos) + CharacterName = Ignoree; + else + CharacterName = Ignoree.substr(LastPeriod + 1); + + database.RemoveFriendOrIgnore(c->GetCharID(), 0, CharacterName); + + } + else + { + database.AddFriendOrIgnore(c->GetCharID(), 0, Ignoree); + Ignoree = "SOE.EQ." + WorldShortName + "." + Ignoree; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_Ignore, Ignoree.length() + 2); + char *PacketBuffer = (char *)outapp->pBuffer; + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, SubAction); + + VARSTRUCT_ENCODE_STRING(PacketBuffer, Ignoree.c_str()); + + _pkt(UCS__PACKETS, outapp); + c->QueuePacket(outapp); + + safe_delete(outapp); + +} +Clientlist::Clientlist(int ChatPort) { + + chatsf = new EQStreamFactory(ChatStream, ChatPort, 45000); + + ChatOpMgr = new RegularOpcodeManager; + + if(!ChatOpMgr->LoadOpcodes("mail_opcodes.conf")) + exit(1); + + if (chatsf->Open()) + _log(UCS__INIT,"Client (UDP) Chat listener started on port %i.", ChatPort); + else { + _log(UCS__ERROR,"Failed to start client (UDP) listener (port %-4i)", ChatPort); + + exit(1); + } +} + +Client::Client(EQStream *eqs) { + + ClientStream = eqs; + + CurrentMailBox = 0; + + Announce = false; + + Status = 0; + + HideMe = 0; + + AccountID = 0; + + AllowInvites = true; + Revoked = false; + + for(int i = 0; i < MAX_JOINED_CHANNELS ; i++) + JoinedChannels[i] = NULL; + + TotalKarma = 0; + AttemptedMessages = 0; + ForceDisconnect = false; + + AccountGrabUpdateTimer = new Timer(60000); //check every minute + GlobalChatLimiterTimer = new Timer(RuleI(Chat, IntervalDurationMS)); + + TypeOfConnection = ConnectionTypeUnknown; + + UnderfootOrLater = false; +} + +Client::~Client() { + + CloseConnection(); + + LeaveAllChannels(false); + + if(AccountGrabUpdateTimer) + { + delete AccountGrabUpdateTimer; + AccountGrabUpdateTimer = NULL; + } + + if(GlobalChatLimiterTimer) + { + delete GlobalChatLimiterTimer; + GlobalChatLimiterTimer = NULL; + } +} + +void Client::CloseConnection() { + + ClientStream->RemoveData(); + + ClientStream->Close(); + + ClientStream->ReleaseFromUse(); +} + +void Clientlist::CheckForStaleConnections(Client *c) { + + if(!c) return; + + list::iterator Iterator; + + for(Iterator = ClientChatConnections.begin(); Iterator != ClientChatConnections.end(); Iterator++) { + + if(((*Iterator) != c) && ((c->GetName() == (*Iterator)->GetName()) + && (c->GetConnectionType() == (*Iterator)->GetConnectionType()))) { + + _log(UCS__CLIENT, "Removing old connection for %s", c->GetName().c_str()); + + struct in_addr in; + + in.s_addr = (*Iterator)->ClientStream->GetRemoteIP(); + + _log(UCS__CLIENT, "Client connection from %s:%d closed.", inet_ntoa(in), + ntohs((*Iterator)->ClientStream->GetRemotePort())); + + safe_delete((*Iterator)); + + Iterator = ClientChatConnections.erase(Iterator); + } + } +} + +void Clientlist::Process() { + + EQStream *eqs; + + while((eqs = chatsf->Pop())) { + + struct in_addr in; + + in.s_addr = eqs->GetRemoteIP(); + + _log(UCS__CLIENT, "New Client UDP connection from %s:%d", inet_ntoa(in), ntohs(eqs->GetRemotePort())); + + eqs->SetOpcodeManager(&ChatOpMgr); + + Client *c = new Client(eqs); + + ClientChatConnections.push_back(c); + } + + list::iterator Iterator; + + for(Iterator = ClientChatConnections.begin(); Iterator != ClientChatConnections.end(); Iterator++) { + + (*Iterator)->AccountUpdate(); + if((*Iterator)->ClientStream->CheckClosed()) { + + struct in_addr in; + + in.s_addr = (*Iterator)->ClientStream->GetRemoteIP(); + + _log(UCS__CLIENT, "Client connection from %s:%d closed.", inet_ntoa(in), + ntohs((*Iterator)->ClientStream->GetRemotePort())); + + safe_delete((*Iterator)); + + Iterator = ClientChatConnections.erase(Iterator); + + if(Iterator == ClientChatConnections.end()) + break; + + continue; + } + + EQApplicationPacket *app = 0; + + bool KeyValid = true; + + while( KeyValid && !(*Iterator)->GetForceDisconnect() && + (app = (EQApplicationPacket *)(*Iterator)->ClientStream->PopPacket())) { + + _pkt(UCS__PACKETS, app); + + EmuOpcode opcode = app->GetOpcode(); + + switch(opcode) { + + case OP_MailLogin: { + + char *PacketBuffer = (char *)app->pBuffer; + + char MailBox[64]; + + char Key[64]; + + char ConnectionTypeIndicator; + + VARSTRUCT_DECODE_STRING(MailBox, PacketBuffer); + + if(strlen(PacketBuffer) != 9) + { + _log(UCS__ERROR, "Mail key is the wrong size. Version of world incompatible with UCS."); + KeyValid = false; + break; + } + ConnectionTypeIndicator = VARSTRUCT_DECODE_TYPE(char, PacketBuffer); + + (*Iterator)->SetConnectionType(ConnectionTypeIndicator); + + VARSTRUCT_DECODE_STRING(Key, PacketBuffer); + + string MailBoxString = MailBox, CharacterName; + + // Strip off the SOE.EQ.. + // + string::size_type LastPeriod = MailBoxString.find_last_of("."); + + if(LastPeriod == string::npos) + CharacterName = MailBoxString; + else + CharacterName = MailBoxString.substr(LastPeriod + 1); + + _log(UCS__TRACE, "Received login for user %s with key %s", MailBox, Key); + + if(!database.VerifyMailKey(CharacterName, (*Iterator)->ClientStream->GetRemoteIP(), Key)) { + + _log(UCS__ERROR, "Chat Key for %s does not match, closing connection.", MailBox); + + KeyValid = false; + + break; + } + + (*Iterator)->SetAccountID(database.FindAccount(CharacterName.c_str(), (*Iterator))); + + database.GetAccountStatus((*Iterator)); + + if((*Iterator)->GetConnectionType() == ConnectionTypeCombined) + (*Iterator)->SendFriends(); + + (*Iterator)->SendMailBoxes(); + + CheckForStaleConnections((*Iterator)); + + break; + } + + case OP_Mail: { + + string CommandString = (const char*)app->pBuffer; + + ProcessOPMailCommand((*Iterator), CommandString); + + break; + } + + default: { + + _log(UCS__ERROR, "Unhandled chat opcode %8X", opcode); + break; + } + } + safe_delete(app); + + } + if(!KeyValid || (*Iterator)->GetForceDisconnect()) { + + struct in_addr in; + + in.s_addr = (*Iterator)->ClientStream->GetRemoteIP(); + + _log(UCS__TRACE, "Force disconnecting client: %s:%d, KeyValid=%i, GetForceDisconnect()=%i", + inet_ntoa(in), ntohs((*Iterator)->ClientStream->GetRemotePort()), + KeyValid, (*Iterator)->GetForceDisconnect()); + + (*Iterator)->ClientStream->Close(); + + safe_delete((*Iterator)); + + Iterator = ClientChatConnections.erase(Iterator); + + if(Iterator == ClientChatConnections.end()) + break; + } + + } + +} + +void Clientlist::ProcessOPMailCommand(Client *c, string CommandString) +{ + + if(CommandString.length() == 0) + return; + + if(isdigit(CommandString[0])) + { + + c->SendChannelMessageByNumber(CommandString); + + return; + } + + if(CommandString[0] == '#') { + + c->SendChannelMessage(CommandString); + + return; + } + + string Command, Parameters; + + string::size_type Space = CommandString.find_first_of(" "); + + if(Space != string::npos) { + + Command = CommandString.substr(0, Space); + + string::size_type ParametersStart = CommandString.find_first_not_of(" ", Space); + + if(ParametersStart != string::npos) + Parameters = CommandString.substr(ParametersStart); + } + else + Command = CommandString; + + int CommandCode = LookupCommand(Command.c_str()); + + switch(CommandCode) { + + case CommandJoin: + c->JoinChannels(Parameters); + break; + + case CommandLeaveAll: + c->LeaveAllChannels(); + break; + + case CommandLeave: + c->LeaveChannels(Parameters); + break; + + case CommandListAll: + ChannelList->SendAllChannels(c); + break; + + case CommandList: + c->ProcessChannelList(Parameters); + break; + + case CommandSet: + c->LeaveAllChannels(false); + c->JoinChannels(Parameters); + break; + + case CommandAnnounce: + c->ToggleAnnounce(Parameters); + break; + + case CommandSetOwner: + c->SetChannelOwner(Parameters); + break; + + case CommandOPList: + c->OPList(Parameters); + break; + + case CommandInvite: + c->ChannelInvite(Parameters); + break; + + case CommandGrant: + c->ChannelGrantModerator(Parameters); + break; + + case CommandModerate: + c->ChannelModerate(Parameters); + break; + + case CommandVoice: + c->ChannelGrantVoice(Parameters); + break; + + case CommandKick: + c->ChannelKick(Parameters); + break; + + case CommandPassword: + c->SetChannelPassword(Parameters); + break; + + case CommandToggleInvites: + c->ToggleInvites(); + break; + + case CommandAFK: + break; + + case CommandUptime: + c->SendUptime(); + break; + + case CommandGetHeaders: + database.SendHeaders(c); + break; + + case CommandGetBody: + database.SendBody(c, atoi(Parameters.c_str())); + break; + + case CommandMailTo: + ProcessMailTo(c, Parameters); + break; + + case CommandSetMessageStatus: + _log(UCS__TRACE, "Set Message Status, Params: %s", Parameters.c_str()); + ProcessSetMessageStatus(Parameters); + break; + + case CommandSelectMailBox: + { + string::size_type NumStart = Parameters.find_first_of("0123456789"); + c->ChangeMailBox(atoi(Parameters.substr(NumStart).c_str())); + break; + } + case CommandSetMailForwarding: + break; + + case CommandBuddy: + RemoveApostrophes(Parameters); + ProcessCommandBuddy(c, Parameters); + break; + + case CommandIgnorePlayer: + RemoveApostrophes(Parameters); + ProcessCommandIgnore(c, Parameters); + break; + + default: + c->SendHelp(); + _log(UCS__ERROR, "Unhandled OP_Mail command: %s", CommandString.c_str()); + } +} + +void Clientlist::CloseAllConnections() { + + + list::iterator Iterator; + + for(Iterator = ClientChatConnections.begin(); Iterator != ClientChatConnections.end(); Iterator++) { + + _log(UCS__TRACE, "Removing client %s", (*Iterator)->GetName().c_str()); + + (*Iterator)->CloseConnection(); + } +} + +void Client::AddCharacter(int CharID, const char *CharacterName, int Level) { + + if(!CharacterName) return; + _log(UCS__TRACE, "Adding character %s with ID %i for %s", CharacterName, CharID, GetName().c_str()); + CharacterEntry NewCharacter; + NewCharacter.CharID = CharID; + NewCharacter.Name = CharacterName; + NewCharacter.Level = Level; + + Characters.push_back(NewCharacter); +} + +void Client::SendMailBoxes() { + + int Count = Characters.size(); + + int PacketLength = 10; + + string s; + + for(int i = 0; i < Count; i++) { + + s += GetMailPrefix() + Characters[i].Name; + + if(i != (Count - 1)) + s = s + ","; + } + + PacketLength += s.length() + 1; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MailLogin, PacketLength); + + char *PacketBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 1); + VARSTRUCT_ENCODE_TYPE(uint32, PacketBuffer, Count); + VARSTRUCT_ENCODE_TYPE(uint32, PacketBuffer, 0); + + VARSTRUCT_ENCODE_STRING(PacketBuffer, s.c_str()); + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0); + + _pkt(UCS__PACKETS, outapp); + + QueuePacket(outapp); + + safe_delete(outapp); +} + +Client *Clientlist::FindCharacter(string CharacterName) { + + list::iterator Iterator; + + for(Iterator = ClientChatConnections.begin(); Iterator != ClientChatConnections.end(); Iterator++) { + + if((*Iterator)->GetName() == CharacterName) + return ((*Iterator)); + + } + + return NULL; +} + +void Client::AddToChannelList(ChatChannel *JoinedChannel) { + + if(!JoinedChannel) return; + + for(int i = 0; i < MAX_JOINED_CHANNELS; i++) + if(JoinedChannels[i] == NULL) { + JoinedChannels[i] = JoinedChannel; + _log(UCS__TRACE, "Added Channel %s to slot %i for %s", JoinedChannel->GetName().c_str(), i + 1, GetName().c_str()); + return; + } +} + +void Client::RemoveFromChannelList(ChatChannel *JoinedChannel) { + + for(int i = 0; i < MAX_JOINED_CHANNELS; i++) + if(JoinedChannels[i] == JoinedChannel) { + + // Shuffle all the channels down. Client likes them all nice and consecutive. + // + for(int j = i; j < (MAX_JOINED_CHANNELS - 1); j++) + JoinedChannels[j] = JoinedChannels[j + 1]; + + JoinedChannels[MAX_JOINED_CHANNELS - 1] = NULL; + + break; + } +} + +int Client::ChannelCount() { + + int NumberOfChannels = 0; + + for(int i = 0; i < MAX_JOINED_CHANNELS; i++) + if(JoinedChannels[i]) + NumberOfChannels++; + + return NumberOfChannels; + +} + +void Client::JoinChannels(string ChannelNameList) { + + for(int x = 0; x < ChannelNameList.size(); ++x) + { + if(ChannelNameList[x] == '%') + { + ChannelNameList[x] = '/'; + } + } + + _log(UCS__TRACE, "Client: %s joining channels %s", GetName().c_str(), ChannelNameList.c_str()); + + int NumberOfChannels = ChannelCount(); + + string::size_type CurrentPos = ChannelNameList.find_first_not_of(" "); + + while(CurrentPos != string::npos) { + + if(NumberOfChannels == MAX_JOINED_CHANNELS) { + + GeneralChannelMessage("You have joined the maximum number of channels. /leave one before trying to join another."); + + break; + } + + string::size_type Comma = ChannelNameList.find_first_of(", ", CurrentPos); + + if(Comma == string::npos) { + + ChatChannel* JoinedChannel = ChannelList->AddClientToChannel(ChannelNameList.substr(CurrentPos), this); + + if(JoinedChannel) + AddToChannelList(JoinedChannel); + + break; + } + + ChatChannel* JoinedChannel = ChannelList->AddClientToChannel(ChannelNameList.substr(CurrentPos, Comma-CurrentPos), this); + + if(JoinedChannel) { + + AddToChannelList(JoinedChannel); + + NumberOfChannels++; + } + + CurrentPos = ChannelNameList.find_first_not_of(", ", Comma); + } + + string JoinedChannelsList, ChannelMessage; + + ChannelMessage = "Channels: "; + + char tmp[200]; + + int ChannelCount = 0; + + for(int i = 0; i < MAX_JOINED_CHANNELS ; i++) { + + if(JoinedChannels[i] != NULL) { + + if(ChannelCount) { + + JoinedChannelsList = JoinedChannelsList + ","; + + ChannelMessage = ChannelMessage + ","; + + } + + JoinedChannelsList = JoinedChannelsList + JoinedChannels[i]->GetName(); + + sprintf(tmp, "%i=%s(%i)", i + 1, JoinedChannels[i]->GetName().c_str(), JoinedChannels[i]->MemberCount(Status)); + + ChannelMessage += tmp; + + ChannelCount++; + } + } + + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_Mail, JoinedChannelsList.length() + 1); + + char *PacketBuffer = (char *)outapp->pBuffer; + + sprintf(PacketBuffer, "%s", JoinedChannelsList.c_str()); + + _pkt(UCS__PACKETS, outapp); + + QueuePacket(outapp); + + safe_delete(outapp); + + if(ChannelCount == 0) + ChannelMessage = "You are not on any channels."; + + outapp = new EQApplicationPacket(OP_ChannelMessage, ChannelMessage.length() + 3); + + PacketBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0x00); + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0x00); + VARSTRUCT_ENCODE_STRING(PacketBuffer, ChannelMessage.c_str()); + + _pkt(UCS__PACKETS, outapp); + + QueuePacket(outapp); + + safe_delete(outapp); +} + +void Client::LeaveChannels(string ChannelNameList) { + + _log(UCS__TRACE, "Client: %s leaving channels %s", GetName().c_str(), ChannelNameList.c_str()); + + string::size_type CurrentPos = 0; + + while(CurrentPos != string::npos) { + + string::size_type Comma = ChannelNameList.find_first_of(", ", CurrentPos); + + if(Comma == string::npos) { + + ChatChannel* JoinedChannel = ChannelList->RemoveClientFromChannel(ChannelNameList.substr(CurrentPos), this); + + if(JoinedChannel) + RemoveFromChannelList(JoinedChannel); + + break; + } + + ChatChannel* JoinedChannel = ChannelList->RemoveClientFromChannel(ChannelNameList.substr(CurrentPos, Comma-CurrentPos), this); + + if(JoinedChannel) + RemoveFromChannelList(JoinedChannel); + + CurrentPos = ChannelNameList.find_first_not_of(", ", Comma); + } + + string JoinedChannelsList, ChannelMessage; + + ChannelMessage = "Channels: "; + + char tmp[200]; + + int ChannelCount = 0; + + for(int i = 0; i < MAX_JOINED_CHANNELS ; i++) { + + if(JoinedChannels[i] != NULL) { + + if(ChannelCount) { + + JoinedChannelsList = JoinedChannelsList + ","; + + ChannelMessage = ChannelMessage + ","; + } + + JoinedChannelsList = JoinedChannelsList + JoinedChannels[i]->GetName(); + + sprintf(tmp, "%i=%s(%i)", i + 1, JoinedChannels[i]->GetName().c_str(), JoinedChannels[i]->MemberCount(Status)); + + ChannelMessage += tmp; + + ChannelCount++; + } + } + + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_Mail, JoinedChannelsList.length() + 1); + + char *PacketBuffer = (char *)outapp->pBuffer; + + sprintf(PacketBuffer, "%s", JoinedChannelsList.c_str()); + + _pkt(UCS__PACKETS, outapp); + + QueuePacket(outapp); + + safe_delete(outapp); + + if(ChannelCount == 0) + ChannelMessage = "You are not on any channels."; + + outapp = new EQApplicationPacket(OP_ChannelMessage, ChannelMessage.length() + 3); + + PacketBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0x00); + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0x00); + VARSTRUCT_ENCODE_STRING(PacketBuffer, ChannelMessage.c_str()); + + _pkt(UCS__PACKETS, outapp); + + QueuePacket(outapp); + + safe_delete(outapp); +} + +void Client::LeaveAllChannels(bool SendUpdatedChannelList) { + + for(int i = 0; i < MAX_JOINED_CHANNELS; i++) { + + if(JoinedChannels[i]) { + + ChannelList->RemoveClientFromChannel(JoinedChannels[i]->GetName(), this); + + JoinedChannels[i] = NULL; + } + } + + if(SendUpdatedChannelList) + SendChannelList(); +} + + +void Client::ProcessChannelList(string Input) { + + if(Input.length() == 0) { + + SendChannelList(); + + return; + } + + string ChannelName = Input; + + if(isdigit(ChannelName[0])) + ChannelName = ChannelSlotName(atoi(ChannelName.c_str())); + + ChatChannel *RequiredChannel = ChannelList->FindChannel(ChannelName); + + if(RequiredChannel) + RequiredChannel->SendChannelMembers(this); + else + GeneralChannelMessage("Channel " + Input + " not found."); +} + + + +void Client::SendChannelList() { + + string ChannelMessage; + + ChannelMessage = "Channels: "; + + char tmp[200]; + + int ChannelCount = 0; + + for(int i = 0; i < MAX_JOINED_CHANNELS ; i++) { + + if(JoinedChannels[i] != NULL) { + + if(ChannelCount) + ChannelMessage = ChannelMessage + ","; + + sprintf(tmp, "%i=%s(%i)", i + 1, JoinedChannels[i]->GetName().c_str(), JoinedChannels[i]->MemberCount(Status)); + + ChannelMessage += tmp; + + ChannelCount++; + } + } + + if(ChannelCount == 0) + ChannelMessage = "You are not on any channels."; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChannelMessage, ChannelMessage.length() + 3); + + char *PacketBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0x00); + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0x00); + VARSTRUCT_ENCODE_STRING(PacketBuffer, ChannelMessage.c_str()); + + _pkt(UCS__PACKETS, outapp); + + QueuePacket(outapp); + + safe_delete(outapp); +} + +void Client::SendChannelMessage(string Message) +{ + + string::size_type MessageStart = Message.find_first_of(" "); + + if(MessageStart == string::npos) + return; + + string ChannelName = Message.substr(1, MessageStart-1); + + _log(UCS__TRACE, "%s tells %s, [%s]", GetName().c_str(), ChannelName.c_str(), Message.substr(MessageStart + 1).c_str()); + + ChatChannel *RequiredChannel = ChannelList->FindChannel(ChannelName); + + if(IsRevoked()) + { + GeneralChannelMessage("You are Revoked, you cannot chat in global channels."); + return; + } + + if(ChannelName.compare("Newplayers") != 0) + { + if(GetKarma() < RuleI(Chat, KarmaGlobalChatLimit)) + { + CharacterEntry *char_ent = NULL; + for(int x = 0; x < Characters.size(); ++x) + { + if(Characters[x].Name.compare(GetName()) == 0) + { + char_ent = &Characters[x]; + break; + } + } + if(char_ent) + { + if(char_ent->Level < RuleI(Chat, GlobalChatLevelLimit)) + { + GeneralChannelMessage("You are either not high enough level or high enough karma to talk in this channel right now."); + return; + } + } + } + } + + if(RequiredChannel) + if(RuleB(Chat, EnableAntiSpam)) + { + if(!RequiredChannel->IsModerated() || RequiredChannel->HasVoice(GetName()) || RequiredChannel->IsOwner(GetName()) || + RequiredChannel->IsModerator(GetName()) || IsChannelAdmin()) + { + if(GlobalChatLimiterTimer) + { + if(GlobalChatLimiterTimer->Check(false)) + { + GlobalChatLimiterTimer->Start(RuleI(Chat, IntervalDurationMS)); + AttemptedMessages = 0; + } + } + int AllowedMessages = RuleI(Chat, MinimumMessagesPerInterval) + GetKarma(); + AllowedMessages = AllowedMessages > RuleI(Chat, MaximumMessagesPerInterval) ? RuleI(Chat, MaximumMessagesPerInterval) : AllowedMessages; + + if(RuleI(Chat, MinStatusToBypassAntiSpam) <= Status) + AllowedMessages = 10000; + + AttemptedMessages++; + if(AttemptedMessages > AllowedMessages) + { + if(AttemptedMessages > RuleI(Chat, MaxMessagesBeforeKick)) + { + ForceDisconnect = true; + } + if(GlobalChatLimiterTimer) + { + char TimeLeft[256]; + sprintf(TimeLeft, "You are currently rate limited, you cannot send more messages for %i seconds.", + (GlobalChatLimiterTimer->GetRemainingTime() / 1000)); + GeneralChannelMessage(TimeLeft); + } + else + { + GeneralChannelMessage("You are currently rate limited, you cannot send more messages for up to 60 seconds."); + } + } + else + { + RequiredChannel->SendMessageToChannel(Message.substr(MessageStart+1), this); + } + } + else + GeneralChannelMessage("Channel " + ChannelName + " is moderated and you have not been granted a voice."); + } + else + { + if(!RequiredChannel->IsModerated() || RequiredChannel->HasVoice(GetName()) || RequiredChannel->IsOwner(GetName()) || + RequiredChannel->IsModerator(GetName()) || IsChannelAdmin()) + RequiredChannel->SendMessageToChannel(Message.substr(MessageStart+1), this); + else + GeneralChannelMessage("Channel " + ChannelName + " is moderated and you have not been granted a voice."); + } + +} + +void Client::SendChannelMessageByNumber(string Message) { + + string::size_type MessageStart = Message.find_first_of(" "); + + if(MessageStart == string::npos) + return; + + int ChannelNumber = atoi(Message.substr(0, MessageStart).c_str()); + + if((ChannelNumber < 1) || (ChannelNumber > MAX_JOINED_CHANNELS)) { + + GeneralChannelMessage("Invalid channel name/number specified."); + + return; + } + + ChatChannel *RequiredChannel = JoinedChannels[ChannelNumber-1]; + + if(!RequiredChannel) { + + GeneralChannelMessage("Invalid channel name/number specified."); + + return; + } + + if(IsRevoked()) + { + GeneralChannelMessage("You are Revoked, you cannot chat in global channels."); + return; + } + + if(RequiredChannel->GetName().compare("Newplayers") != 0) + { + if(GetKarma() < RuleI(Chat, KarmaGlobalChatLimit)) + { + CharacterEntry *char_ent = NULL; + for(int x = 0; x < Characters.size(); ++x) + { + if(Characters[x].Name.compare(GetName()) == 0) + { + char_ent = &Characters[x]; + break; + } + } + if(char_ent) + { + if(char_ent->Level < RuleI(Chat, GlobalChatLevelLimit)) + { + GeneralChannelMessage("You are either not high enough level or high enough karma to talk in this channel right now."); + return; + } + } + } + } + + _log(UCS__TRACE, "%s tells %s, [%s]", GetName().c_str(), RequiredChannel->GetName().c_str(), + Message.substr(MessageStart + 1).c_str()); + + if(RuleB(Chat, EnableAntiSpam)) + { + if(!RequiredChannel->IsModerated() || RequiredChannel->HasVoice(GetName()) || RequiredChannel->IsOwner(GetName()) || + RequiredChannel->IsModerator(GetName())) + { + if(GlobalChatLimiterTimer) + { + if(GlobalChatLimiterTimer->Check(false)) + { + GlobalChatLimiterTimer->Start(RuleI(Chat, IntervalDurationMS)); + AttemptedMessages = 0; + } + } + int AllowedMessages = RuleI(Chat, MinimumMessagesPerInterval) + GetKarma(); + AllowedMessages = AllowedMessages > RuleI(Chat, MaximumMessagesPerInterval) ? RuleI(Chat, MaximumMessagesPerInterval) : AllowedMessages; + if(RuleI(Chat, MinStatusToBypassAntiSpam) <= Status) + AllowedMessages = 10000; + + AttemptedMessages++; + if(AttemptedMessages > AllowedMessages) + { + if(AttemptedMessages > RuleI(Chat, MaxMessagesBeforeKick)) + { + ForceDisconnect = true; + } + if(GlobalChatLimiterTimer) + { + char TimeLeft[256]; + sprintf(TimeLeft, "You are currently rate limited, you cannot send more messages for %i seconds.", + (GlobalChatLimiterTimer->GetRemainingTime() / 1000)); + GeneralChannelMessage(TimeLeft); + } + else + { + GeneralChannelMessage("You are currently rate limited, you cannot send more messages for up to 60 seconds."); + } + } + else + { + RequiredChannel->SendMessageToChannel(Message.substr(MessageStart+1), this); + } + } + else + GeneralChannelMessage("Channel " + RequiredChannel->GetName() + " is moderated and you have not been granted a voice."); + } + else + { + if(!RequiredChannel->IsModerated() || RequiredChannel->HasVoice(GetName()) || RequiredChannel->IsOwner(GetName()) || + RequiredChannel->IsModerator(GetName())) + RequiredChannel->SendMessageToChannel(Message.substr(MessageStart+1), this); + else + GeneralChannelMessage("Channel " + RequiredChannel->GetName() + " is moderated and you have not been granted a voice."); + } + +} + +void Client::SendChannelMessage(string ChannelName, string Message, Client *Sender) { + + if(!Sender) return; + + string FQSenderName = WorldShortName + "." + Sender->GetName(); + + int PacketLength = ChannelName.length() + Message.length() + FQSenderName.length() + 3; + + if(UnderfootOrLater) + PacketLength += 8; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChannelMessage, PacketLength); + + char *PacketBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_STRING(PacketBuffer, ChannelName.c_str()); + VARSTRUCT_ENCODE_STRING(PacketBuffer, FQSenderName.c_str()); + VARSTRUCT_ENCODE_STRING(PacketBuffer, Message.c_str()); + + if(UnderfootOrLater) + VARSTRUCT_ENCODE_STRING(PacketBuffer, "SPAM:0:"); + + _pkt(UCS__PACKETS, outapp); + QueuePacket(outapp); + + safe_delete(outapp); +} + +void Client::ToggleAnnounce(string State) +{ + if(State == "") + Announce = !Announce; + else if(State == "on") + Announce = true; + else + Announce = false; + + string Message = "Announcing now "; + + if(Announce) + Message += "on"; + else + Message += "off"; + + GeneralChannelMessage(Message); +} + +void Client::AnnounceJoin(ChatChannel *Channel, Client *c) { + + if(!Channel || !c) return; + + int PacketLength = Channel->GetName().length() + c->GetName().length() + 2; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChannelAnnounceJoin, PacketLength); + + char *PacketBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_STRING(PacketBuffer, Channel->GetName().c_str()); + VARSTRUCT_ENCODE_STRING(PacketBuffer, c->GetName().c_str()); + + _pkt(UCS__PACKETS, outapp); + + QueuePacket(outapp); + + safe_delete(outapp); +} + +void Client::AnnounceLeave(ChatChannel *Channel, Client *c) { + + if(!Channel || !c) return; + + int PacketLength = Channel->GetName().length() + c->GetName().length() + 2; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChannelAnnounceLeave, PacketLength); + + char *PacketBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_STRING(PacketBuffer, Channel->GetName().c_str()); + VARSTRUCT_ENCODE_STRING(PacketBuffer, c->GetName().c_str()); + + _pkt(UCS__PACKETS, outapp); + + QueuePacket(outapp); + + safe_delete(outapp); +} + +void Client::GeneralChannelMessage(const char *Characters) { + + if(!Characters) return; + + string Message = Characters; + + GeneralChannelMessage(Message); + +} + +void Client::GeneralChannelMessage(string Message) { + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChannelMessage, Message.length() + 3); + char *PacketBuffer = (char *)outapp->pBuffer; + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0x00); + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0x00); + VARSTRUCT_ENCODE_STRING(PacketBuffer, Message.c_str()); + + _pkt(UCS__PACKETS, outapp); + QueuePacket(outapp); + + safe_delete(outapp); +} + +void Client::SetChannelPassword(string ChannelPassword) { + + string::size_type PasswordStart = ChannelPassword.find_first_not_of(" "); + + if(PasswordStart == string::npos) { + string Message = "Incorrect syntax: /chat password "; + GeneralChannelMessage(Message); + return; + } + + string::size_type Space = ChannelPassword.find_first_of(" ", PasswordStart); + + if(Space == string::npos) { + string Message = "Incorrect syntax: /chat password "; + GeneralChannelMessage(Message); + return; + } + + string Password = ChannelPassword.substr(PasswordStart, Space - PasswordStart); + + string::size_type ChannelStart = ChannelPassword.find_first_not_of(" ", Space); + + if(ChannelStart == string::npos) { + string Message = "Incorrect syntax: /chat password "; + GeneralChannelMessage(Message); + return; + } + + string ChannelName = ChannelPassword.substr(ChannelStart); + + if((ChannelName.length() > 0) && isdigit(ChannelName[0])) + ChannelName = ChannelSlotName(atoi(ChannelName.c_str())); + + string Message; + + if(!strcasecmp(Password.c_str(), "remove")) { + Password.clear(); + Message = "Password REMOVED on channel " + ChannelName; + } + else + Message = "Password change on channel " + ChannelName; + + _log(UCS__TRACE, "Set password of channel [%s] to [%s] by %s", ChannelName.c_str(), Password.c_str(), GetName().c_str()); + + ChatChannel *RequiredChannel = ChannelList->FindChannel(ChannelName); + + if(!RequiredChannel) { + string Message = "Channel not found."; + GeneralChannelMessage(Message); + return; + } + + if(!RequiredChannel->IsOwner(GetName()) && !RequiredChannel->IsModerator(GetName()) && !IsChannelAdmin()) { + string Message = "You do not own or have moderator rights on channel " + ChannelName; + GeneralChannelMessage(Message); + return; + } + + RequiredChannel->SetPassword(Password); + + GeneralChannelMessage(Message); + +} + +void Client::SetChannelOwner(string CommandString) { + + string::size_type PlayerStart = CommandString.find_first_not_of(" "); + + if(PlayerStart == string::npos) { + string Message = "Incorrect syntax: /chat setowner "; + GeneralChannelMessage(Message); + return; + } + + string::size_type Space = CommandString.find_first_of(" ", PlayerStart); + + if(Space == string::npos) { + string Message = "Incorrect syntax: /chat setowner "; + GeneralChannelMessage(Message); + return; + } + + string NewOwner = CapitaliseName(CommandString.substr(PlayerStart, Space - PlayerStart)); + + string::size_type ChannelStart = CommandString.find_first_not_of(" ", Space); + + if(ChannelStart == string::npos) { + string Message = "Incorrect syntax: /chat setowner "; + GeneralChannelMessage(Message); + return; + } + + string ChannelName = CapitaliseName(CommandString.substr(ChannelStart)); + + if((ChannelName.length() > 0) && isdigit(ChannelName[0])) + ChannelName = ChannelSlotName(atoi(ChannelName.c_str())); + + _log(UCS__TRACE, "Set owner of channel [%s] to [%s]", ChannelName.c_str(), NewOwner.c_str()); + + ChatChannel *RequiredChannel = ChannelList->FindChannel(ChannelName); + + if(!RequiredChannel) { + GeneralChannelMessage("Channel " + ChannelName + " not found."); + return; + } + + if(!RequiredChannel->IsOwner(GetName()) && !IsChannelAdmin()) { + string Message = "You do not own channel " + ChannelName; + GeneralChannelMessage(Message); + return; + } + + if(database.FindCharacter(NewOwner.c_str()) < 0) { + + GeneralChannelMessage("Player " + NewOwner + " does not exist."); + return; + } + + RequiredChannel->SetOwner(NewOwner); + + if(RequiredChannel->IsModerator(NewOwner)) + RequiredChannel->RemoveModerator(NewOwner); + + GeneralChannelMessage("Channel owner changed."); + +} + +void Client::OPList(string CommandString) { + + string::size_type ChannelStart = CommandString.find_first_not_of(" "); + + if(ChannelStart == string::npos) { + string Message = "Incorrect syntax: /chat oplist "; + GeneralChannelMessage(Message); + return; + } + + string ChannelName = CapitaliseName(CommandString.substr(ChannelStart)); + + if((ChannelName.length() > 0) && isdigit(ChannelName[0])) + ChannelName = ChannelSlotName(atoi(ChannelName.c_str())); + + ChatChannel *RequiredChannel = ChannelList->FindChannel(ChannelName); + + if(!RequiredChannel) { + GeneralChannelMessage("Channel " + ChannelName + " not found."); + return; + } + + RequiredChannel->SendOPList(this); +} + +void Client::ChannelInvite(string CommandString) { + + string::size_type PlayerStart = CommandString.find_first_not_of(" "); + + if(PlayerStart == string::npos) { + string Message = "Incorrect syntax: /chat invite "; + GeneralChannelMessage(Message); + return; + } + + string::size_type Space = CommandString.find_first_of(" ", PlayerStart); + + if(Space == string::npos) { + string Message = "Incorrect syntax: /chat invite "; + GeneralChannelMessage(Message); + return; + } + + string Invitee = CapitaliseName(CommandString.substr(PlayerStart, Space - PlayerStart)); + + string::size_type ChannelStart = CommandString.find_first_not_of(" ", Space); + + if(ChannelStart == string::npos) { + string Message = "Incorrect syntax: /chat invite "; + GeneralChannelMessage(Message); + return; + } + + string ChannelName = CapitaliseName(CommandString.substr(ChannelStart)); + + if((ChannelName.length() > 0) && isdigit(ChannelName[0])) + ChannelName = ChannelSlotName(atoi(ChannelName.c_str())); + + _log(UCS__TRACE, "[%s] invites [%s] to channel [%s]", GetName().c_str(), Invitee.c_str(), ChannelName.c_str()); + + Client *RequiredClient = CL->FindCharacter(Invitee); + + if(!RequiredClient) { + + GeneralChannelMessage(Invitee + " is not online."); + return; + } + + if(RequiredClient == this) { + + GeneralChannelMessage("You cannot invite yourself to a channel."); + return; + } + + if(!RequiredClient->InvitesAllowed()) { + + GeneralChannelMessage("That player is not currently accepting channel invitations."); + return; + } + + ChatChannel *RequiredChannel = ChannelList->FindChannel(ChannelName); + + if(!RequiredChannel) { + + GeneralChannelMessage("Channel " + ChannelName + " not found."); + return; + } + + if(!RequiredChannel->IsOwner(GetName()) && !RequiredChannel->IsModerator(GetName())) { + + string Message = "You do not own or have moderator rights to channel " + ChannelName; + + GeneralChannelMessage(Message); + return; + } + + if(RequiredChannel->IsClientInChannel(RequiredClient)) { + + GeneralChannelMessage(Invitee + " is already in that channel"); + + return; + } + + RequiredChannel->AddInvitee(Invitee); + + RequiredClient->GeneralChannelMessage(GetName() + " has invited you to join channel " + ChannelName); + + GeneralChannelMessage("Invitation sent to " + Invitee + " to join channel " + ChannelName); + +} + +void Client::ChannelModerate(string CommandString) { + + string::size_type ChannelStart = CommandString.find_first_not_of(" "); + + if(ChannelStart == string::npos) { + + string Message = "Incorrect syntax: /chat moderate "; + + GeneralChannelMessage(Message); + return; + } + + string ChannelName = CapitaliseName(CommandString.substr(ChannelStart)); + + if((ChannelName.length() > 0) && isdigit(ChannelName[0])) + ChannelName = ChannelSlotName(atoi(ChannelName.c_str())); + + ChatChannel *RequiredChannel = ChannelList->FindChannel(ChannelName); + + if(!RequiredChannel) { + + GeneralChannelMessage("Channel " + ChannelName + " not found."); + return; + } + + if(!RequiredChannel->IsOwner(GetName()) && !RequiredChannel->IsModerator(GetName()) && !IsChannelAdmin()) { + + GeneralChannelMessage("You do not own or have moderator rights to channel " + ChannelName); + return; + } + + RequiredChannel->SetModerated(!RequiredChannel->IsModerated()); + + if(!RequiredChannel->IsClientInChannel(this)) + if(RequiredChannel->IsModerated()) + GeneralChannelMessage("Channel " + ChannelName + " is now moderated."); + else + GeneralChannelMessage("Channel " + ChannelName + " is no longer moderated."); + +} + +void Client::ChannelGrantModerator(string CommandString) { + + string::size_type PlayerStart = CommandString.find_first_not_of(" "); + + if(PlayerStart == string::npos) { + + GeneralChannelMessage("Incorrect syntax: /chat grant "); + return; + } + + string::size_type Space = CommandString.find_first_of(" ", PlayerStart); + + if(Space == string::npos) { + + GeneralChannelMessage("Incorrect syntax: /chat grant "); + return; + } + + string Moderator = CapitaliseName(CommandString.substr(PlayerStart, Space - PlayerStart)); + + string::size_type ChannelStart = CommandString.find_first_not_of(" ", Space); + + if(ChannelStart == string::npos) { + + GeneralChannelMessage("Incorrect syntax: /chat grant "); + return; + } + + string ChannelName = CapitaliseName(CommandString.substr(ChannelStart)); + + if((ChannelName.length() > 0) && isdigit(ChannelName[0])) + ChannelName = ChannelSlotName(atoi(ChannelName.c_str())); + + _log(UCS__TRACE, "[%s] gives [%s] moderator rights to channel [%s]", GetName().c_str(), Moderator.c_str(), ChannelName.c_str()); + + Client *RequiredClient = CL->FindCharacter(Moderator); + + if(!RequiredClient && (database.FindCharacter(Moderator.c_str()) < 0)) { + + GeneralChannelMessage("Player " + Moderator + " does not exist."); + return; + } + + if(RequiredClient == this) { + + GeneralChannelMessage("You cannot grant yourself moderator rights to a channel."); + return; + } + + ChatChannel *RequiredChannel = ChannelList->FindChannel(ChannelName); + + if(!RequiredChannel) { + + GeneralChannelMessage("Channel " + ChannelName + " not found."); + return; + } + + if(!RequiredChannel->IsOwner(GetName()) && !IsChannelAdmin()) { + + GeneralChannelMessage("You do not own channel " + ChannelName); + return; + } + + if(RequiredChannel->IsModerator(Moderator)) { + + RequiredChannel->RemoveModerator(Moderator); + + if(RequiredClient) + RequiredClient->GeneralChannelMessage(GetName() + " has removed your moderator rights to channel " + ChannelName); + + GeneralChannelMessage("Removing moderator rights from " + Moderator + " to channel " + ChannelName); + } + else { + RequiredChannel->AddModerator(Moderator); + + if(RequiredClient) + RequiredClient->GeneralChannelMessage(GetName() + " has made you a moderator of channel " + ChannelName); + + GeneralChannelMessage(Moderator + " is now a moderator on channel " + ChannelName); + } + +} + +void Client::ChannelGrantVoice(string CommandString) { + + string::size_type PlayerStart = CommandString.find_first_not_of(" "); + + if(PlayerStart == string::npos) { + + GeneralChannelMessage("Incorrect syntax: /chat voice "); + return; + } + + string::size_type Space = CommandString.find_first_of(" ", PlayerStart); + + if(Space == string::npos) { + GeneralChannelMessage("Incorrect syntax: /chat voice "); + return; + } + + string Voicee = CapitaliseName(CommandString.substr(PlayerStart, Space - PlayerStart)); + + string::size_type ChannelStart = CommandString.find_first_not_of(" ", Space); + + if(ChannelStart == string::npos) { + GeneralChannelMessage("Incorrect syntax: /chat voice "); + return; + } + + string ChannelName = CapitaliseName(CommandString.substr(ChannelStart)); + + if((ChannelName.length() > 0) && isdigit(ChannelName[0])) + ChannelName = ChannelSlotName(atoi(ChannelName.c_str())); + + _log(UCS__TRACE, "[%s] gives [%s] voice to channel [%s]", GetName().c_str(), Voicee.c_str(), ChannelName.c_str()); + + Client *RequiredClient = CL->FindCharacter(Voicee); + + if(!RequiredClient && (database.FindCharacter(Voicee.c_str()) < 0)) { + + GeneralChannelMessage("Player " + Voicee + " does not exist."); + return; + } + + if(RequiredClient == this) { + + GeneralChannelMessage("You cannot grant yourself voice to a channel."); + return; + } + + ChatChannel *RequiredChannel = ChannelList->FindChannel(ChannelName); + + if(!RequiredChannel) { + + GeneralChannelMessage("Channel " + ChannelName + " not found."); + return; + } + + if(!RequiredChannel->IsOwner(GetName()) && !RequiredChannel->IsModerator(GetName()) && !IsChannelAdmin()) { + + GeneralChannelMessage("You do not own or have moderator rights to channel " + ChannelName); + return; + } + + if(RequiredChannel->IsOwner(RequiredClient->GetName()) || RequiredChannel->IsModerator(RequiredClient->GetName())) { + + GeneralChannelMessage("The channel owner and moderators automatically have voice."); + return; + } + + if(RequiredChannel->HasVoice(Voicee)) { + + RequiredChannel->RemoveVoice(Voicee); + + if(RequiredClient) + RequiredClient->GeneralChannelMessage(GetName() + " has removed your voice rights to channel " + ChannelName); + + GeneralChannelMessage("Removing voice from " + Voicee + " in channel " + ChannelName); + } + else { + RequiredChannel->AddVoice(Voicee); + + if(RequiredClient) + RequiredClient->GeneralChannelMessage(GetName() + " has given you voice in channel " + ChannelName); + + GeneralChannelMessage(Voicee + " now has voice in channel " + ChannelName); + } + +} + +void Client::ChannelKick(string CommandString) { + + string::size_type PlayerStart = CommandString.find_first_not_of(" "); + + if(PlayerStart == string::npos) { + + GeneralChannelMessage("Incorrect syntax: /chat kick "); + return; + } + + string::size_type Space = CommandString.find_first_of(" ", PlayerStart); + + if(Space == string::npos) { + + GeneralChannelMessage("Incorrect syntax: /chat kick "); + return; + } + + string Kickee = CapitaliseName(CommandString.substr(PlayerStart, Space - PlayerStart)); + + string::size_type ChannelStart = CommandString.find_first_not_of(" ", Space); + + if(ChannelStart == string::npos) { + + GeneralChannelMessage("Incorrect syntax: /chat kick "); + return; + } + string ChannelName = CapitaliseName(CommandString.substr(ChannelStart)); + + if((ChannelName.length() > 0) && isdigit(ChannelName[0])) + ChannelName = ChannelSlotName(atoi(ChannelName.c_str())); + + _log(UCS__TRACE, "[%s] kicks [%s] from channel [%s]", GetName().c_str(), Kickee.c_str(), ChannelName.c_str()); + + Client *RequiredClient = CL->FindCharacter(Kickee); + + if(!RequiredClient) { + + GeneralChannelMessage("Player " + Kickee + " is not online."); + return; + } + + if(RequiredClient == this) { + + GeneralChannelMessage("You cannot kick yourself out of a channel."); + return; + } + + ChatChannel *RequiredChannel = ChannelList->FindChannel(ChannelName); + + if(!RequiredChannel) { + + GeneralChannelMessage("Channel " + ChannelName + " not found."); + return; + } + + if(!RequiredChannel->IsOwner(GetName()) && !RequiredChannel->IsModerator(GetName()) && !IsChannelAdmin()) { + + GeneralChannelMessage("You do not own or have moderator rights to channel " + ChannelName); + return; + } + + if(RequiredChannel->IsOwner(RequiredClient->GetName())) { + + GeneralChannelMessage("You cannot kick the owner out of the channel."); + return; + } + + if(RequiredChannel->IsModerator(Kickee) && !RequiredChannel->IsOwner(GetName())) { + + GeneralChannelMessage("Only the channel owner can kick a moderator out of the channel."); + return; + } + + if(RequiredChannel->IsModerator(Kickee)) { + + RequiredChannel->RemoveModerator(Kickee); + + RequiredClient->GeneralChannelMessage(GetName() + " has removed your moderator rights to channel " + ChannelName); + + GeneralChannelMessage("Removing moderator rights from " + Kickee + " to channel " + ChannelName); + } + + RequiredClient->GeneralChannelMessage(GetName() + " has kicked you from channel " + ChannelName); + + GeneralChannelMessage("Kicked " + Kickee + " from channel " + ChannelName); + + RequiredClient->LeaveChannels(ChannelName); +} + +void Client::ToggleInvites() { + + AllowInvites = !AllowInvites; + + if(AllowInvites) + GeneralChannelMessage("You will now receive channel invitations."); + else + GeneralChannelMessage("You will no longer receive channel invitations."); + +} + +string Client::ChannelSlotName(int SlotNumber) { + + if((SlotNumber < 1 ) || (SlotNumber > MAX_JOINED_CHANNELS)) + return ""; + + if(JoinedChannels[SlotNumber - 1] == NULL) + return ""; + + return JoinedChannels[SlotNumber - 1]->GetName(); + +} + +void Client::SendHelp() { + + GeneralChannelMessage("Chat Channel Commands:"); + + GeneralChannelMessage("/join, /leave, /leaveall, /list, /announce, /autojoin, ;set"); + GeneralChannelMessage(";oplist, ;grant, ;invite, ;kick, ;moderate, ;password, ;voice"); + GeneralChannelMessage(";setowner, ;toggleinvites"); +} + +void Client::AccountUpdate() +{ + if(AccountGrabUpdateTimer) + { + if(AccountGrabUpdateTimer->Check(false)) + { + AccountGrabUpdateTimer->Start(60000); + database.GetAccountStatus(this); + } + } +} + +void Client::SetConnectionType(char c) { + + switch(c) + { + case 'S': + { + TypeOfConnection = ConnectionTypeCombined; + _log(UCS__TRACE, "Connection type is Combined (SoF/SoD)"); + break; + } + case 'U': + { + TypeOfConnection = ConnectionTypeCombined; + UnderfootOrLater = true; + _log(UCS__TRACE, "Connection type is Combined (Underfoot+)"); + break; + } + case 'M': + { + TypeOfConnection = ConnectionTypeMail; + _log(UCS__TRACE, "Connection type is Mail (6.2 or Titanium client)"); + break; + } + case 'C': + { + TypeOfConnection = ConnectionTypeChat; + _log(UCS__TRACE, "Connection type is Chat (6.2 or Titanium client)"); + break; + } + default: + { + TypeOfConnection = ConnectionTypeUnknown; + _log(UCS__TRACE, "Connection type is unknown."); + } + } +} + +Client *Clientlist::IsCharacterOnline(string CharacterName) { + + // This method is used to determine if the character we are a sending an email to is connected to the mailserver, + // so we can send them a new email notification. + // + // The way live works is that it sends a notification if a player receives an email for their 'primary' mailbox, + // i.e. for the character they are logged in as, or for the character whose mailbox they have selected in the + // mail window. + // + list::iterator Iterator; + + for(Iterator = ClientChatConnections.begin(); Iterator != ClientChatConnections.end(); Iterator++) { + + if(!(*Iterator)->IsMailConnection()) + continue; + + int MailBoxNumber = (*Iterator)->GetMailBoxNumber(CharacterName); + + // If the mail is destined for the primary mailbox for this character, or the one they have selected + // + if((MailBoxNumber == 0) || (MailBoxNumber == (*Iterator)->GetMailBoxNumber())) + return (*Iterator); + + } + + return NULL; +} + +int Client::GetMailBoxNumber(string CharacterName) { + + for(unsigned int i = 0; i < Characters.size(); i++) + if(Characters[i].Name == CharacterName) + return i; + + return -1; +} + +void Client::SendNotification(int MailBoxNumber, string Subject, string From, int MessageID) { + + char TimeStamp[100]; + + char sMessageID[100]; + + char Sequence[100]; + + sprintf(TimeStamp, "%i", (int)time(NULL)); + + sprintf(sMessageID, "%i", MessageID); + + sprintf(Sequence, "%i", 1); + + int PacketLength = 8 + strlen(sMessageID) + strlen(TimeStamp)+ From.length() + Subject.length(); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MailNew, PacketLength); + + char *PacketBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_INTSTRING(PacketBuffer, MailBoxNumber); + VARSTRUCT_ENCODE_STRING(PacketBuffer, sMessageID); + VARSTRUCT_ENCODE_STRING(PacketBuffer, TimeStamp); + VARSTRUCT_ENCODE_STRING(PacketBuffer, "1"); + VARSTRUCT_ENCODE_STRING(PacketBuffer, From.c_str()); + VARSTRUCT_ENCODE_STRING(PacketBuffer, Subject.c_str()); + + _pkt(UCS__PACKETS, outapp); + + QueuePacket(outapp); + + safe_delete(outapp); +} + +void Client::ChangeMailBox(int NewMailBox) { + + _log(UCS__TRACE, "%s Change to mailbox %i", MailBoxName().c_str(), NewMailBox); + + SetMailBox(NewMailBox); + + _log(UCS__TRACE, "New mailbox is %s", MailBoxName().c_str()); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MailboxChange, 2); + + char *buf = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_INTSTRING(buf, NewMailBox); + + _pkt(UCS__PACKETS, outapp); + + QueuePacket(outapp); + + safe_delete(outapp); +} + +void Client::SendFriends() { + + vector Friends, Ignorees; + + database.GetFriendsAndIgnore(GetCharID(), Friends, Ignorees); + + EQApplicationPacket *outapp; + + vector::iterator Iterator; + + Iterator = Friends.begin(); + + while(Iterator != Friends.end()) { + + outapp = new EQApplicationPacket(OP_Buddy, (*Iterator).length() + 2); + + char *PacketBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 1); + + VARSTRUCT_ENCODE_STRING(PacketBuffer, (*Iterator).c_str()); + + _pkt(UCS__PACKETS, outapp); + + QueuePacket(outapp); + + safe_delete(outapp); + + Iterator++; + } + + Iterator = Ignorees.begin(); + + while(Iterator != Ignorees.end()) { + + string Ignoree = "SOE.EQ." + WorldShortName + "." + (*Iterator); + + outapp = new EQApplicationPacket(OP_Ignore, Ignoree.length() + 2); + + char *PacketBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0); + + VARSTRUCT_ENCODE_STRING(PacketBuffer, Ignoree.c_str()); + + _pkt(UCS__PACKETS, outapp); + + QueuePacket(outapp); + + safe_delete(outapp); + + Iterator++; + } +} + +string Client::MailBoxName() { + + if((Characters.size() == 0) || (CurrentMailBox > (Characters.size() - 1))) + { + _log(UCS__ERROR, "MailBoxName() called with CurrentMailBox set to %i and Characters.size() is %i", + CurrentMailBox, Characters.size()); + + return ""; + } + + _log(UCS__TRACE, "MailBoxName() called with CurrentMailBox set to %i and Characters.size() is %i", + CurrentMailBox, Characters.size()); + + return Characters[CurrentMailBox].Name; + +} + +int Client::GetCharID() { + + if(Characters.size() == 0) + return 0; + + return Characters[0].CharID; +} diff --git a/ucs/clientlist.h b/ucs/clientlist.h new file mode 100644 index 000000000..f118e939c --- /dev/null +++ b/ucs/clientlist.h @@ -0,0 +1,195 @@ +/* + EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2008 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 CHATSERVER_CLIENTLIST_H +#define CHATSERVER_CLIENTLIST_H + +#include "../common/opcodemgr.h" +#include "../common/EQStreamType.h" +#include "../common/EQStreamFactory.h" +#include "../common/rulesys.h" +#include "chatchannel.h" +#include +#include + +#define MAX_JOINED_CHANNELS 10 + +enum { CommandJoin = 0, CommandLeaveAll, CommandLeave, CommandListAll, CommandList, CommandSet, CommandAnnounce, CommandSetOwner, + CommandOPList, CommandInvite, CommandGrant, CommandModerate, CommandVoice, CommandKick, + CommandPassword, CommandToggleInvites, CommandAFK, CommandUptime, + CommandGetHeaders, CommandGetBody, CommandMailTo, CommandSetMessageStatus, CommandSelectMailBox, + CommandSetMailForwarding, CommandBuddy, CommandIgnorePlayer, + CommandEndOfList }; + +struct CommandEntry { + const char *CommandString; + int CommandCode; +}; + +typedef enum { ConnectionTypeUnknown, ConnectionTypeCombined, ConnectionTypeMail, ConnectionTypeChat } ConnectionType; + +static const CommandEntry Commands[] = { + { "join", CommandJoin }, + { "leaveall", CommandLeaveAll }, + { "leave", CommandLeave }, + { "listall", CommandListAll }, + { "list", CommandList }, + { "set", CommandSet }, + { "announce", CommandAnnounce }, + { "setowner", CommandSetOwner }, + { "oplist", CommandOPList }, + { "invite", CommandInvite }, + { "grant", CommandGrant }, + { "moderate", CommandModerate }, + { "voice", CommandVoice }, + { "kick", CommandKick }, + { "password", CommandPassword }, + { "toggleinvites", CommandToggleInvites }, + { "afk", CommandAFK }, + { "uptime", CommandUptime }, + { "getheaders", CommandGetHeaders }, + { "getbody", CommandGetBody }, + { "mailto", CommandMailTo }, + { "setmessagestatus", CommandSetMessageStatus }, + { "selectmailbox", CommandSelectMailBox }, + { "setmailforwarding", CommandSetMailForwarding }, + { "buddy", CommandBuddy }, + { "ignoreplayer", CommandIgnorePlayer }, + { "", CommandEndOfList } }; + +struct CharacterEntry { + int CharID; + int Level; + string Name; +}; + +class Client { + +public: + Client(EQStream* eqs); + ~Client(); + + EQStream *ClientStream; + void AddCharacter(int CharID, const char *CharacterName, int Level); + void ClearCharacters() { Characters.clear(); } + void SendMailBoxes(); + inline void QueuePacket(const EQApplicationPacket *p, bool ack_req=true) { ClientStream->QueuePacket(p, ack_req); } + string GetName() { if(Characters.size()) return Characters[0].Name; else return ""; } + void JoinChannels(string ChannelList); + void LeaveChannels(string ChannelList); + void LeaveAllChannels(bool SendUpdatedChannelList = true); + void AddToChannelList(ChatChannel *JoinedChannel); + void RemoveFromChannelList(ChatChannel *JoinedChannel); + void SendChannelMessage(string Message); + void SendChannelMessage(string ChannelName, string Message, Client *Sender); + void SendChannelMessageByNumber(string Message); + void SendChannelList(); + void CloseConnection(); + void ToggleAnnounce(string State); + bool IsAnnounceOn() { return (Announce == true); } + void AnnounceJoin(ChatChannel *Channel, Client *c); + void AnnounceLeave(ChatChannel *Channel, Client *c); + void GeneralChannelMessage(string Message); + void GeneralChannelMessage(const char *Characters); + void SetChannelPassword(string ChannelPassword); + void ProcessChannelList(string CommandString); + void AccountUpdate(); + int ChannelCount(); + inline void SetAccountID(int inAccountID) { AccountID = inAccountID; } + inline int GetAccountID() { return AccountID; } + inline void SetAccountStatus(int inStatus) { Status = inStatus; } + inline void SetHideMe(bool inHideMe) { HideMe = inHideMe; } + inline void SetKarma(uint32 inKarma) { TotalKarma = inKarma; } + inline int GetAccountStatus() { return Status; } + inline bool GetHideMe() { return HideMe; } + inline uint32 GetKarma() { return TotalKarma; } + void SetChannelOwner(string CommandString); + void OPList(string CommandString); + void ChannelInvite(string CommandString); + void ChannelGrantModerator(string CommandString); + void ChannelGrantVoice(string CommandString); + void ChannelKick(string CommandString); + void ChannelModerate(string CommandString); + string ChannelSlotName(int ChannelNumber); + void ToggleInvites(); + bool InvitesAllowed() { return AllowInvites; } + bool IsRevoked() { return Revoked; } + void SetRevoked(bool r) { Revoked = r; } + inline bool IsChannelAdmin() { return (Status >= RuleI(Channels, RequiredStatusAdmin)); } + inline bool CanListAllChannels() { return (Status >= RuleI(Channels, RequiredStatusListAll)); } + void SendHelp(); + inline bool GetForceDisconnect() { return ForceDisconnect; } + string MailBoxName(); + int GetMailBoxNumber() { return CurrentMailBox; } + int GetMailBoxNumber(string CharacterName); + void SetConnectionType(char c); + ConnectionType GetConnectionType() { return TypeOfConnection; } + inline bool IsMailConnection() { return (TypeOfConnection == ConnectionTypeMail) || (TypeOfConnection == ConnectionTypeCombined); } + void SendNotification(int MailBoxNumber, string From, string Subject, int MessageID); + void ChangeMailBox(int NewMailBox); + inline void SetMailBox(int NewMailBox) { CurrentMailBox = NewMailBox; } + void SendFriends(); + int GetCharID(); + void SendUptime(); + +private: + unsigned int CurrentMailBox; + vector Characters; + ChatChannel *JoinedChannels[MAX_JOINED_CHANNELS]; + bool Announce; + int AccountID; + int Status; + bool HideMe; + bool AllowInvites; + bool Revoked; + + //Anti Spam Stuff + Timer *AccountGrabUpdateTimer; + uint32 TotalKarma; + + Timer *GlobalChatLimiterTimer; //60 seconds + int AttemptedMessages; + bool ForceDisconnect; + ConnectionType TypeOfConnection; + bool UnderfootOrLater; +}; + +class Clientlist { + +public: + Clientlist(int MailPort); + void Process(); + void CloseAllConnections(); + Client *FindCharacter(string CharacterName); + void CheckForStaleConnections(Client *c); + Client *IsCharacterOnline(string CharacterName); + void ProcessOPMailCommand(Client *c, string CommandString); + +private: + + EQStreamFactory *chatsf; + + list ClientChatConnections; + + OpcodeManager *ChatOpMgr; +}; + +#endif diff --git a/ucs/database.cpp b/ucs/database.cpp new file mode 100644 index 000000000..f8900862c --- /dev/null +++ b/ucs/database.cpp @@ -0,0 +1,770 @@ +/* + EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2008 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 +using namespace std; +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Disgrace: for windows compile +#ifdef _WINDOWS +#include +#define snprintf _snprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#else +#include "../common/unix.h" +#include +#endif + +#include "database.h" +#include "../common/eq_packet_structs.h" +#include "../common/MiscFunctions.h" +#include "chatchannel.h" + +extern Clientlist *CL; +extern string GetMailPrefix(); +extern ChatChannelList *ChannelList; +extern uint32 MailMessagesSent; + +Database::Database () +{ + DBInitVars(); +} + +/* +Establish a connection to a mysql database with the supplied parameters +*/ + +Database::Database(const char* host, const char* user, const char* passwd, const char* database, uint32 port) +{ + DBInitVars(); + Connect(host, user, passwd, database, 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)) + { + LogFile->write(EQEMuLog::Error, "Failed to connect to database: Error: %s", errbuf); + HandleMysqlError(errnum); + + return false; + } + else + { + LogFile->write(EQEMuLog::Status, "Using database '%s' at %s:%d",database,host,port); + return true; + } +} + +void Database::DBInitVars() { + +} + + + +void Database::HandleMysqlError(uint32 errnum) { +} + +/* + +Close the connection to the database +*/ +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); + + return; + } + _log(UCS__TRACE, "GetAccountStatus Query: %s", query); + safe_delete_array(query); + + if(mysql_num_rows(result) != 1) + { + _log(UCS__ERROR, "Error in GetAccountStatus"); + mysql_free_result(result); + return; + } + + row = mysql_fetch_row(result); + + c->SetAccountStatus(atoi(row[0])); + c->SetHideMe(atoi(row[1]) != 0); + c->SetKarma(atoi(row[2])); + c->SetRevoked((atoi(row[3])==1?true:false)); + + _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) { + + _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); + return -1; + } + safe_delete_array(query); + + if (mysql_num_rows(result) != 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]); + + mysql_free_result(result); + _log(UCS__TRACE, "Account ID for %s is %i", CharacterName, AccountID); + + 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); + + 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; +} + +bool Database::VerifyMailKey(string CharacterName, int IPAddress, 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); + + return false; + } + + safe_delete_array(query); + + row = mysql_fetch_row(result); + + // 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]; + + if(RuleB(Chat, EnableMailKeyIPVerification) == true) + sprintf(CombinedKey, "%08X%s", IPAddress, MailKey.c_str()); + else + 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; + +} + +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); + + return -1; + } + + safe_delete_array(query); + safe_delete_array(SafeCharName); + + 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; + +} + +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; +} + +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); + + return false; + } + + safe_delete_array(query); + + while((row = mysql_fetch_row(result))) { + + string ChannelName = row[0]; + string ChannelOwner = row[1]; + string ChannelPassword = row[2]; + + ChannelList->CreateChannel(ChannelName, ChannelOwner, ChannelPassword, true, atoi(row[3])); + } + + mysql_free_result(result); + + return true; +} + +void Database::SetChannelPassword(string ChannelName, string Password) { + + _log(UCS__TRACE, "Database::SetChannelPassword(%s, %s)", ChannelName.c_str(), Password.c_str()); + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + 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(string ChannelName, string Owner) { + + _log(UCS__TRACE, "Database::SetChannelOwner(%s, %s)", ChannelName.c_str(), Owner.c_str()); + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + 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) { + + int UnknownField2 = 25015275; + int UnknownField3 = 1; + + int CharacterID = FindCharacter(c->MailBoxName().c_str()); + _log(UCS__TRACE, "Sendheaders for %s, CharID is %i", c->MailBoxName().c_str(), CharacterID); + 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`,`timestamp`,`from`,`subject`, `status` from `mail` " + "where `charid`=%i", CharacterID),errbuf,&result)){ + + safe_delete_array(query); + + return ; + } + + safe_delete_array(query); + + char Buf[100]; + + my_ulonglong NumRows = mysql_num_rows(result); + + int HeaderCountPacketLength = 0; + + 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); + + _pkt(UCS__PACKETS, outapp); + + c->QueuePacket(outapp); + + safe_delete(outapp); + + int RowNum = 0; + + while((row = mysql_fetch_row(result))) { + + + int HeaderPacketLength = 0; + + 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; + + 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]); + + _pkt(UCS__PACKETS, outapp); + + c->QueuePacket(outapp); + + safe_delete(outapp); + + RowNum++; + } + + mysql_free_result(result); + +} + +void Database::SendBody(Client *c, int MessageNumber) { + + int CharacterID = FindCharacter(c->MailBoxName().c_str()); + + _log(UCS__TRACE, "SendBody: MsgID %i, to %s, CharID is %i", MessageNumber, c->MailBoxName().c_str(), CharacterID); + + 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); + + return; + } + + row = mysql_fetch_row(result); + + _log(UCS__TRACE, "Message: %i body (%i bytes)", MessageNumber, strlen(row[1])); + + int PacketLength = 12 + strlen(row[0]) + strlen(row[1]) + strlen(row[2]); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MailSendBody,PacketLength); + + char *PacketBuffer = (char *)outapp->pBuffer; + + 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); + + mysql_free_result(result); + + _pkt(UCS__PACKETS, outapp); + + c->QueuePacket(outapp); + + safe_delete(outapp); + + +} + +bool Database::SendMail(string Recipient, string From, string Subject, string Body, string RecipientsString) { + + int CharacterID; + + string CharacterName; + + //printf("Database::SendMail(%s, %s, %s)\n", Recipient.c_str(), From.c_str(), Subject.c_str()); + + string::size_type LastPeriod = Recipient.find_last_of("."); + + if(LastPeriod == string::npos) + CharacterName = Recipient; + else + CharacterName = Recipient.substr(LastPeriod+1); + + CharacterName[0] = toupper(CharacterName[0]); + + for(unsigned int i = 1; i < CharacterName.length(); i++) + CharacterName[i] = tolower(CharacterName[i]); + + CharacterID = FindCharacter(CharacterName.c_str()); + + _log(UCS__TRACE, "SendMail: CharacterID for recipient %s is %i", CharacterName.c_str(), CharacterID); + + 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]; + + 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(NULL); // 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); + + return false; + } + + _log(UCS__TRACE, "MessageID %i generated, from %s, to %s", LastMsgID, From.c_str(), Recipient.c_str()); + + safe_delete_array(EscSubject); + safe_delete_array(EscBody); + safe_delete_array(query); + + Client *c = CL->IsCharacterOnline(CharacterName); + + if(c) { + string FQN = GetMailPrefix() + From; + + c->SendNotification(c->GetMailBoxNumber(CharacterName), Subject, FQN, LastMsgID); + } + + MailMessagesSent++; + + return true; +} + +void Database::SetMessageStatus(int MessageNumber, int Status) { + + _log(UCS__TRACE, "SetMessageStatus %i %i", MessageNumber, Status); + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + 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)) { + + _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 ; + } + safe_delete_array(query); + + row = mysql_fetch_row(result); + + _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(NULL) - 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); + } + // Expire Read + if(RuleI(Mail, ExpireRead) >= 0) { + if(RunQuery(query, MakeAnyLenString(&query, "delete from `mail` where `status`=3 and `timestamp` < %i", + time(NULL) - 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); + } + // Expire Unread + if(RuleI(Mail, ExpireUnread) >= 0) { + if(RunQuery(query, MakeAnyLenString(&query, "delete from `mail` where `status`=1 and `timestamp` < %i", + time(NULL) - 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); + } +} + +void Database::AddFriendOrIgnore(int CharID, int Type, 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); + else + _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, 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); + else + _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, vector &Friends, 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 ; + } + + safe_delete_array(query); + + while((row = mysql_fetch_row(result))) { + + 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()); + } + } + + mysql_free_result(result); + + return; +} diff --git a/ucs/database.h b/ucs/database.h new file mode 100644 index 000000000..fafb23989 --- /dev/null +++ b/ucs/database.h @@ -0,0 +1,73 @@ +/* + EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2008 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 CHATSERVER_DATABASE_H +#define CHATSERVER_DATABASE_H + +#define AUTHENTICATION_TIMEOUT 60 +#define INVALID_ID 0xFFFFFFFF + +#include "../common/debug.h" +#include "../common/types.h" +#include "../common/dbcore.h" +#include "../common/linked_list.h" +#include "clientlist.h" +#include +#include +#include +using namespace std; + +//atoi is not uint32 or uint32 safe!!!! +#define atoul(str) strtoul(str, NULL, 10) + +class Database : public DBcore { +public: + Database(); + Database(const char* host, const char* user, const char* passwd, const char* database,uint32 port); + bool Connect(const char* host, const char* user, const char* passwd, const char* database,uint32 port); + ~Database(); + + int FindAccount(const char *CharacterName, Client *c); + int FindCharacter(const char *CharacterName); + bool VerifyMailKey(string CharacterName, int IPAddress, string MailKey); + bool GetVariable(const char* varname, char* varvalue, uint16 varvalue_len); + bool LoadChatChannels(); + void GetAccountStatus(Client *c); + void SetChannelPassword(string ChannelName, string Password); + void SetChannelOwner(string ChannelName, string Owner); + void SendHeaders(Client *c); + void SendBody(Client *c, int MessageNumber); + bool SendMail(string Recipient, string From, string Subject, string Body, string RecipientsString); + void SetMessageStatus(int MessageNumber, int Status); + void ExpireMail(); + void AddFriendOrIgnore(int CharID, int Type, string Name); + void RemoveFriendOrIgnore(int CharID, int Type, string Name); + void GetFriendsAndIgnore(int CharID, vector &Friends, vector &Ignorees); + + +protected: + void HandleMysqlError(uint32 errnum); +private: + void DBInitVars(); + +}; + +#endif diff --git a/ucs/ucs.cpp b/ucs/ucs.cpp new file mode 100644 index 000000000..8a08a2fff --- /dev/null +++ b/ucs/ucs.cpp @@ -0,0 +1,192 @@ +/* + EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2008 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 "clientlist.h" +#include "../common/opcodemgr.h" +#include "../common/EQStreamFactory.h" +#include "../common/rulesys.h" +#include "../common/servertalk.h" +#include "../common/platform.h" +#include "../common/crash.h" +#include "database.h" +#include "ucsconfig.h" +#include "chatchannel.h" +#include "worldserver.h" +#include +#include + +volatile bool RunLoops = true; + +uint32 MailMessagesSent = 0; +uint32 ChatMessagesSent = 0; + +TimeoutManager timeout_manager; + +Clientlist *CL; + +ChatChannelList *ChannelList; + +Database database; + +string WorldShortName; + +RuleManager *rules = new RuleManager(); + +const ucsconfig *Config; + +WorldServer *worldserver = 0; + + +void CatchSignal(int sig_num) { + + RunLoops = false; + + if(worldserver) + worldserver->Disconnect(); +} + +string GetMailPrefix() { + + return "SOE.EQ." + WorldShortName + "."; + +} + +int main() { + RegisterExecutablePlatform(ExePlatformUCS); + set_exception_handler(); + + // Check every minute for unused channels we can delete + // + Timer ChannelListProcessTimer(60000); + + Timer InterserverTimer(INTERSERVER_TIMER); // does auto-reconnect + + _log(UCS__INIT, "Starting EQEmu Universal Chat Server."); + + if (!ucsconfig::LoadConfig()) { + + _log(UCS__INIT, "Loading server configuration failed."); + + return(1); + } + + Config = ucsconfig::get(); + + if(!load_log_settings(Config->LogSettingsFile.c_str())) + _log(UCS__INIT, "Warning: Unable to read %s", Config->LogSettingsFile.c_str()); + else + _log(UCS__INIT, "Log settings loaded from %s", Config->LogSettingsFile.c_str()); + + WorldShortName = Config->ShortName; + + _log(UCS__INIT, "Connecting to MySQL..."); + + if (!database.Connect( + Config->DatabaseHost.c_str(), + Config->DatabaseUsername.c_str(), + Config->DatabasePassword.c_str(), + Config->DatabaseDB.c_str(), + Config->DatabasePort)) { + _log(WORLD__INIT_ERR, "Cannot continue without a database connection."); + return(1); + } + + char tmp[64]; + + if (database.GetVariable("RuleSet", tmp, sizeof(tmp)-1)) { + _log(WORLD__INIT, "Loading rule set '%s'", tmp); + if(!rules->LoadRules(&database, tmp)) { + _log(UCS__ERROR, "Failed to load ruleset '%s', falling back to defaults.", tmp); + } + } else { + if(!rules->LoadRules(&database, "default")) { + _log(UCS__INIT, "No rule set configured, using default rules"); + } else { + _log(UCS__INIT, "Loaded default rule set 'default'", tmp); + } + } + + database.ExpireMail(); + + if(Config->ChatPort != Config->MailPort) + { + _log(UCS__ERROR, "MailPort and CharPort must be the same in eqemu_config.xml for UCS."); + exit(1); + } + + CL = new Clientlist(Config->ChatPort); + + ChannelList = new ChatChannelList(); + + database.LoadChatChannels(); + + if (signal(SIGINT, CatchSignal) == SIG_ERR) { + _log(UCS__ERROR, "Could not set signal handler"); + return 0; + } + if (signal(SIGTERM, CatchSignal) == SIG_ERR) { + _log(UCS__ERROR, "Could not set signal handler"); + return 0; + } + + worldserver = new WorldServer; + + worldserver->Connect(); + + while(RunLoops) { + + Timer::SetCurrentTime(); + + CL->Process(); + + if(ChannelListProcessTimer.Check()) + ChannelList->Process(); + + if (InterserverTimer.Check()) { + if (worldserver->TryReconnect() && (!worldserver->Connected())) + worldserver->AsyncConnect(); + } + worldserver->Process(); + + timeout_manager.CheckTimeouts(); + + Sleep(100); + } + + ChannelList->RemoveAllChannels(); + + CL->CloseAllConnections(); + +} + +void UpdateWindowTitle(char* iNewTitle) { +#ifdef _WINDOWS + char tmp[500]; + if (iNewTitle) { + snprintf(tmp, sizeof(tmp), "UCS: %s", iNewTitle); + } + else { + snprintf(tmp, sizeof(tmp), "UCS"); + } + SetConsoleTitle(tmp); +#endif +} diff --git a/ucs/ucsconfig.cpp b/ucs/ucsconfig.cpp new file mode 100644 index 000000000..211199a6b --- /dev/null +++ b/ucs/ucsconfig.cpp @@ -0,0 +1,29 @@ +/* + EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2008 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 "ucsconfig.h" + +ucsconfig *ucsconfig::_chat_config = NULL; + +string ucsconfig::GetByName(const string &var_name) const { + return(EQEmuConfig::GetByName(var_name)); +} diff --git a/ucs/ucsconfig.h b/ucs/ucsconfig.h new file mode 100644 index 000000000..af3799e33 --- /dev/null +++ b/ucs/ucsconfig.h @@ -0,0 +1,56 @@ +/* + EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2008 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 __ucsconfig_H +#define __ucsconfig_H + +#include "../common/EQEmuConfig.h" + +class ucsconfig : public EQEmuConfig { +public: + virtual string GetByName(const string &var_name) const; + +private: + + static ucsconfig *_chat_config; + +public: + + // Produce a const singleton + static const ucsconfig *get() { + if (_chat_config == NULL) + LoadConfig(); + return(_chat_config); + } + + // Load the config + static bool LoadConfig() { + if (_chat_config != NULL) + delete _chat_config; + _chat_config=new ucsconfig; + _config=_chat_config; + + return _config->ParseFile(EQEmuConfig::ConfigFile.c_str(),"server"); + } + +}; + +#endif diff --git a/ucs/worldserver.cpp b/ucs/worldserver.cpp new file mode 100644 index 000000000..a8c5d7c7b --- /dev/null +++ b/ucs/worldserver.cpp @@ -0,0 +1,134 @@ +/* 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 "../common/debug.h" +#include +using namespace std; +#include +#include +#include +#include +#include +#include + +#include "../common/servertalk.h" +#include "worldserver.h" +#include "clientlist.h" +#include "ucsconfig.h" +#include "database.h" +#include "../common/packet_functions.h" +#include "../common/md5.h" + +extern WorldServer worldserver; +extern Clientlist *CL; +extern const ucsconfig *Config; +extern Database database; + +void ProcessMailTo(Client *c, string from, string subject, string message); + +WorldServer::WorldServer() +: WorldConnection(EmuTCPConnection::packetModeUCS, Config->SharedKey.c_str()) +{ + pTryReconnect = true; +} + +WorldServer::~WorldServer() +{ +} + +void WorldServer::OnConnected() +{ + _log(UCS__INIT, "Connected to World."); + WorldConnection::OnConnected(); +} + +void WorldServer::Process() +{ + WorldConnection::Process(); + + if (!Connected()) + return; + + ServerPacket *pack = 0; + + while((pack = tcpc.PopPacket())) + { + _log(UCS__TRACE, "Received Opcode: %4X", pack->opcode); + + switch(pack->opcode) + { + case 0: { + break; + } + case ServerOP_KeepAlive: + { + break; + } + case ServerOP_UCSMessage: + { + char *Buffer = (char *)pack->pBuffer; + + char *From = new char[strlen(Buffer) + 1]; + + VARSTRUCT_DECODE_STRING(From, Buffer); + + string Message = Buffer; + + _log(UCS__TRACE, "Player: %s, Sent Message: %s", From, Message.c_str()); + + Client *c = CL->FindCharacter(From); + + safe_delete_array(From); + + if(Message.length() < 2) + break; + + if(!c) + { + _log(UCS__TRACE, "Client not found."); + break; + } + + if(Message[0] == ';') + { + c->SendChannelMessageByNumber(Message.substr(1, string::npos)); + } + else if(Message[0] == '[') + { + CL->ProcessOPMailCommand(c, Message.substr(1, string::npos)); + } + + break; + } + + case ServerOP_UCSMailMessage: + { + ServerMailMessageHeader_Struct *mail = (ServerMailMessageHeader_Struct*)pack->pBuffer; + database.SendMail(string("SOE.EQ.") + Config->ShortName + string(".") + string(mail->to), + string(mail->from), + mail->subject, + mail->message, + string()); + break; + } + } + } + + safe_delete(pack); + return; +} + diff --git a/ucs/worldserver.h b/ucs/worldserver.h new file mode 100644 index 000000000..e579b467d --- /dev/null +++ b/ucs/worldserver.h @@ -0,0 +1,35 @@ +/* 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 WORLDSERVER_H +#define WORLDSERVER_H + +#include "../common/worldconn.h" +#include "../common/eq_packet_structs.h" + +class WorldServer : public WorldConnection +{ +public: + WorldServer(); + virtual ~WorldServer(); + virtual void Process(); + +private: + virtual void OnConnected(); +}; +#endif + diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt new file mode 100644 index 000000000..3e6d2ee1a --- /dev/null +++ b/utils/CMakeLists.txt @@ -0,0 +1,22 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +IF(UNIX) + ADD_DEFINITIONS(-fPIC) +ENDIF(UNIX) + +IF(UNIX AND EQEMU_BUILD_CLEANIPC) + SET(cleanipc_sources + cleanipc.cpp + ) + + SET(cleanipc_headers + ) + + ADD_EXECUTABLE(cleanipc ${cleanipc_sources} ${cleanipc_headers}) + TARGET_LINK_LIBRARIES(cleanipc "rt") + SET(EXECUTABLE_OUTPUT_PATH ../Bin) +ENDIF(UNIX AND EQEMU_BUILD_CLEANIPC) + +IF(EQEMU_BUILD_AZONE) + ADD_SUBDIRECTORY(azone2) +ENDIF(EQEMU_BUILD_AZONE) \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2.sln b/utils/EQExtractor2/EQExtractor2.sln new file mode 100644 index 000000000..0503567e5 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C# Express 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EQExtractor2", "EQExtractor2\EQExtractor2.csproj", "{621904C6-9028-439C-A0E6-1F856C88D238}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {621904C6-9028-439C-A0E6-1F856C88D238}.Debug|x86.ActiveCfg = Debug|x86 + {621904C6-9028-439C-A0E6-1F856C88D238}.Debug|x86.Build.0 = Debug|x86 + {621904C6-9028-439C-A0E6-1F856C88D238}.Release|x86.ActiveCfg = Release|x86 + {621904C6-9028-439C-A0E6-1F856C88D238}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/utils/EQExtractor2/EQExtractor2/ChangeLog.txt b/utils/EQExtractor2/EQExtractor2/ChangeLog.txt new file mode 100644 index 000000000..bafe7d8d5 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/ChangeLog.txt @@ -0,0 +1,310 @@ +EQExtractor2 Changelog. All changes since the 1.0 release. + +==02/13/2013== +Derision: Added decoder to support packet dumps from current Live client. SQL generation NOT SUPPORTED YET. +Derision: Added boolean to patch decoders to indicate if they support SQL generation or not. + +==02/10/2013== +Derision: Added decoder to support packet dumps from for the current Test Server client. SQL generation not tested. + +==01/30/2013== +Derision: Added support in netcode for fragmented Client->Server packets. + +==01/28/2013== +Derision: Added decoder to support packet dumps from for the current Test Server client. SQL generation not tested. + +==01/25/2013== +Derision: Updated .conf files for RoF and current live client (Thanks Trevius). + +==01/18/2013== +Derision: Added support for January 16 2013 client (with help from posts on the SEQ boards). + +==12/18/2012== +Derision: Fixed crash when generating SQL for a spawn that is a destructible object that has no 'bodytype' field. + +==12/17/2012== +Derision: Support for December 10 2012 client. + +==08/22/2012== +Derision: Changed Base1/Base2 in AA dump to signed Int32 + +==08/17/2012== +Derision: Support for August 15 2012 client. + +==07/19/2012== +Derision: Support for July 13 2012 client. + +==06/30/2012== +Derision: Support for June 25 2012 client. + +==03/24/2012== +Derision: Corrected spawn packet parsing per post by Razzle on ShowEQ forums. + +==03/19/2012== +Derision: Support for March 15 2012 client. Credit to fransick and Newby on the ShowEQ forums for the PP and most of the opcode changes. + +==12/03/2011== +Derision: Support for November 17 2011 client. +Derision: Added checkbox on SQL generation form to include invisible NPCs (race 127), default is false. + +==08/06/2011== +Derision: Support for August 04 2011 client (currently on test). + +==05/25/2011== +Derision: Support for May 24 2011 client. + +==05/16/2011== +Derision: Fixed DumpAA option for May 12 client. There are two new uint32s in the SendAATable struct, one after Current Level and one after SpellType. + +==05/15/2011== +Derision: Support for May 12 2011 client. Based on info by posters on the ShowEQ forums. + +==03/21/2011== +Derision: Support for March 15 2011 client + +==02/10/2011== +Derision: Support for Feb 8 2011 client +Derision: Corrected Identifier for Feb 8 2011 patch +Derision: Corrected OP_SendAATable for Feb 8 2011 patch + +==01/29/2011== +Derision: Doors with opentype 57 are no longer automatically changed to type 58. +Derision: Handling of opcodes with zero low-byte (e.g. 0x4200). +Derision: Band-aid for crash when parsing OP_ZoneEntry for player with Otherdata & 0x01 set. + +==12/25/2010== +Derision: Heading appears to be multiplied by 2 in ZonePoint packets. + +==12/21/2010== +Derision: OP_SendFindableNPCS + +==12/08/2010== +Derision: Added support for client Dec 7 2010 14:04:53 + +==11/22/2010== +Derision: Netcode fix highlighted by live patch supplied by robregen. +Derision: Handled spawns with otherdata & 2 set the same as otherdata & 1 (destructable object data present), based on live patch supplied + by robregen. + +==10/23/2010== +Derision: Support for 20th October Live patch. + +==09/23/2010== +Derision: Support for 22nd September Test Server client. Don't think I have OP_ShopEnd correct. + +==09/17/2010== +Derision: Passed packet direction to Explorers. + +==09/11/2010== +Derision: Identified a few more 'Area' IDs for the new Item slot numbering system. +Derision: Explorer methods are now called with the packet payload as a ByteStream rather than byte[] + +==09/10/2010== +Derision: Addded ability to call 'Explorer' methods during Packet Dump for use while figuring out packet formats. + +==09/08/2010== +Derision: Moved item packet decode into a separate method. + +==09/03/2010== +Derision: Added support for version currently on test server (seems like a lot of opcodes have changed values). +Derision: Fixed item Quantity field for Sep 1 patch. Fixed typo in patch .conf file +Derision: Debug text box now resizes with it's parent form. + +==08/29/2010== +Derision: Added handling of unencapsulated application packets with flags which were previously being interpreted as unknown opcodes. + (Seems to only have affects position update packets). + +Derision: Construct grids from both OP_NPCMoveUpdate and OP_MobUpdate packets. Previously, if OP_NPCMoveUpdate packets were sent for a mob, + i.e. high-resolution updates, any OP_MobUpdate packets for that mob where ignored. + +Derision: Added facility in Options to specify a file to write low level netcode debug information to. +Derision: Decoded Crypto flag from OP_SessionRequest to identify and ignore compressed UCS stream. +Derision: Added error checking when opening netcode debug file. + +==08/27/2010== +Derision: Redesigned the UI. Use the menu bar, or the following shortcuts: + + Ctrl-L Load .PCAP file + Ctrl-S Generate SQL + Ctrl-P Dump Packets + Ctrl-V View Packets + Ctrl-A Dump AA + Ctrl-D Show Debug Window (hidden by default. Change in Options). + Ctrl-O Set Options (these settings persist across sessions). + +Derision: Added the ability to disable removal of redundant waypoints. +Derision: Added an option to only generate SQL for spawns whose name contains a given string. +Derision: Added optional timestamps to packet dump. Added capture date to SQL output. +Derision: Fixed crash when Debug log was closed rather than just hidden. + +==07/16/2010== +Derision: Replaced HexDump method with a considerably faster version. + +==07/14/2010== +Derision: Renamed PatchTestJune242010Decoder to PatchJuly132010Decoder. +Derision: Updated patch_May12-2010.conf with current EQEmu patch_Live.conf. Updated version number to 2.0.5 + +==06/26/2010== +Derision: Removed the need to duplicate certain methods when deriving a new decoder. +Derision: Moved patch specific variable setting (PPLength, Name etc.) into the decoder constructor + +==06/25/2010== +Derision: Added support for current Test Server version (only PP length appears to have changed, no OPCode changes). + +==06/12/2010== + +Derision: Updated version to 2.0 SVN +Derision: Fixed a bug in GenerateZoneSQL where it was using the ZoneID from the NewZone packet rather than from the PP. + +------------------------------------------------------------------------------------------------------------ +==06/11/2010== + +More refactoring. Should be possible to support .pcaps generated from mulitple client versions. +Refactoring and cleanup about as done as I can be bothered to before releasing the source :) + +Merchant items that have a quantity other than -1 (mostly player sold items) are included in the SQL, but commented out. + +==06/10/2010== + +More refactoring. + +==06/08/2010== + +More refactoring. +Began to move code that may change with EQClient patch versions out into a separate class to allow easier handling of new patches. + +==06/07/2010== + +Changed GenerateSpawnsSQL to use BitConverter instead of memory streams; +Refactored some code. + +Initial implementation of ByteStream class. + +==06/06/2010== + +For doors of opentype 57 and 58 (ports), the destination zone and coordinates are populated in the doors table, if a matching +zone point exists with the door param of that door. Doortype 57 is also changed to 58 if a matching zp is found. + +Processing a capture file which was started while already in a zone and then zoning *should* now process packets for the +second zone correctly. + +Released as version 1.9 + +==06/05/2010== + +Reset expected sequence numbers when switching streams. + + +==06/05/2010== + +Permalocked onto stream using C->S OP_ZoneEntry to reduce chance of multi-boxing messing the collect up. +Version clause is appended to query when updating existing npc_types. +Selecting the update existing npc types option will automatically uncheck all other options. +Reworked texture/helm parsing, so hopefully NPCs should look as close as possible to live now (as close as we can without having textures for each armor slot in npc_types). +Added 'experimental option to add texture colors to npc_types_tint so that different armor slots can have different colors. + +Released as version 1.8 + +------------------------------- + +==06/04/2010== + +Fixed bug introduced in v1.6 that broke merchant SQL generation. +Gender is now updated for existing npc_types. +Guard races are now included when updating existing npc_types. +Obtained the ZoneID number from the Player Profile instead of the NewZone_Struct. + +Released as version 1.7 + +------------------------------- + +==06/03/2010== + +The zone long name should no longer be truncated at 32 characters. +Apostrophes in the zone long name are escaped, i.e. Dranik's Hollows should be Dranik\'s Hollows. +The 'Update Existing NPC Types' option is now available. If selected, the following columns: + + texture, helmtexture, size, face, luclin_hairstyle, luclin_haircolor, luclin_eyecolor, luclin_eyecolor2, luclin_beardcolor, + luclin_beard, drakkin_heritage, drakkin_tattoo, drakkin_details, armortint_red, armortint_green, armortint_blue, + d_meele_texture1, d_meele_texture2, findable + + will be updated for existing rows in the npc_types table that meet the following criteria: + + Their ID falls within the range specified. + The NPC is a playable race (Race <= 12 || Race == 128 || Race == 130 || Race == 330 || Race == 522) + The NPC name is unique in the sense that only one NPC with the given first name was seen in the packet collect. + +Released as version 1.6 + + +==06/02/2010== + +Implemented in 1.5 but not mentioned in changelog: Player mounts no longer included in SQL. + +---------------------- + +==06/02/2010== + +Fixed bug which caused the first spawn to always be skipped. +Update generated for fog_density column in zone table. +Populated Drakkin specific fields in npc_types. +Did some work on updating existing npc_types but left it disabled until an issue is resolved. + +Released as version 1.5 + +==06/01/2010== + +Changed doors and object/ground spawn extractors to use Bitconverter instead of memory streams. + +==05/31/2010== + +If zone config and/or zone points are selected, the generated SQL will no longer be commented out. +--------------------------------- + +==05/31/2010== +Populated Melee texture columns. +For playable races, equip_chest2 is set based on the equipped chest item, if one is equipped. +Set armour tint columns based on the colour field for the chest slot. + +The starting IDs for database inserts are now set as variables at the top of the SQL: + Note the Door ID has changed meaning. Instead of being the value for the id column in the doors table, + it is now the base value for the doorid column. The id column will just use the next autoincrement id. + +If the version number is none-zero, The base doorid = Version * 1000 and all other insert IDs are + set to ZoneID * 1000 + (Version * 100). + +If the version number is none-zero, deletes for npc_types and the spawn tables will only be generated from + the starting InsertID to starting ID + 99. + +Moved the Version NumericUpDown control to before the InsertID textboxes as changing the version will + auto-change the InsertIDs. + +Corrected a bug where IT10805 objects were flagged as ovens instead of brew barrels. +Added checkboxes (default unchecked) for zone config (fog, safe point, etc) and zone points. + +Released as version 1.4 +------------------------------------ + +==05/30/2010== +Added ZonePoint SQL generation. +Added generation of SQL to update zone table. +Obtained ZoneID from the OP_NewZone packet instead of from a lookup table. +Set findable column in npc_types based on OP_SendFindableNPCs packets from server. +Added Version field to UI which is used to populate the version column in the doors, npc_types, spawn2, ground_spawns and object tables. +Released as version 1.1 +------------------------------------------- + +Added apostrophes around short name in zone UPDATE statement. +Added Dump AA button to dump AA info. +FogDensity is reported along with the other OP_NewZone stuff +Released as version 1.2 + +------------------------------------------- +Fixed bug where incorrect Insert IDs were being using for spawngroups +Version 1.3 +------------------------------------------- + + + + + diff --git a/utils/EQExtractor2/EQExtractor2/EQApplicationLayer.cs b/utils/EQExtractor2/EQExtractor2/EQApplicationLayer.cs new file mode 100644 index 000000000..a3c9a898f --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/EQApplicationLayer.cs @@ -0,0 +1,1730 @@ +// +// Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net). Distributed under GPL version 2. +// +// +// The code in here should all be independent of the version of the EQ client. +// +// The EQStreamProcessor class will call the relevant Patch Specific decoder to return the required data in order to build SQL +// + + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.IO; +using SharpPcap; +using zlib; +using MyUtils; +using EQExtractor2.InternalTypes; +using EQExtractor2.Patches; + +using EQPacket; + +namespace EQApplicationLayer +{ + public delegate void LogHandler(string Message); + + class EQStreamProcessor + { + // PatchSpecificDecoder is the base, Dummy' class from which the actual supported patch version decoders inherit. + // Setting PatchDecoder to this is a failsafe in case the stream isn't identified. + public PatchSpecficDecoder PatchDecoder = new PatchSpecficDecoder(); + + List ZonePointList; + + // The PacketManager decodes the raw stream into EQApplictionPackets and holds the list of decoded packets for us to process + public PacketManager Packets; + + // The PatchList is a list of each client version specific decoder + List PatchList; + + // EQStreamProcessor is the class that generates the SQL. It calls the relevant patch specific decoder to decode the packets + // and return the relevant data in a standardized internal format. + + public EQStreamProcessor() + { + ZonePointList = null; + + Packets = new PacketManager(); + + // Tell the PacketManager to call our Identify method to identify the packet stream. We will then call the different + // patch specific Identifiers to identify the patch version. + + Packets.SetVersionIdentifierMethod(Identify); + + // Put our supported patch version decoders into the list. + + PatchList = new List(); + + PatchList.Add(new PatchMay122010Decoder()); + + PatchList.Add(new PatchJuly132010Decoder()); + + PatchList.Add(new PatchTestSep012010Decoder()); + + PatchList.Add(new PatchTestSep222010Decoder()); + + PatchList.Add(new PatchOct202010Decoder()); + + PatchList.Add(new PatchDec072010Decoder()); + + PatchList.Add(new PatchFeb082011Decoder()); + + PatchList.Add(new PatchMarch152011Decoder()); + + PatchList.Add(new PatchMay122011Decoder()); + + PatchList.Add(new PatchMay242011Decoder()); + + PatchList.Add(new PatchAug042011Decoder()); + + PatchList.Add(new PatchNov172011Decoder()); + + PatchList.Add(new PatchMar152012Decoder()); + + PatchList.Add(new PatchJune252012Decoder()); + + PatchList.Add(new PatchJuly132012Decoder()); + + PatchList.Add(new PatchAugust152012Decoder()); + + PatchList.Add(new PatchDecember102012Decoder()); + + PatchList.Add(new PatchJanuary162013Decoder()); + + PatchList.Add(new PatchTestServerJanuary162013Decoder()); + + PatchList.Add(new PatchTestServerFebruary52013Decoder()); + + PatchList.Add(new PatchFebruary112013Decoder()); + + PatchList.Add(new PatchSoD()); + + } + + // This is called from the main form to tell us where the application was launched from (where to look for the .conf files) + // and also gives us the LogHandler method so we can send debug messages to the main form. + + public bool Init(string ConfDirectory, LogHandler Logger) + { + // Pass the LogHandler on down to the PacketManager + + Packets.SetLogHandler(Logger); + + string ErrorMessage = ""; + + // Here we init all the patch version specific decoders. The only reason one should fail to init is if it can't + // find it's patch_XXXX.conf. + // + // If at least one initialises successfully, then return true to the caller, otherwise return false + bool AllDecodersFailed = true; + + PatchDecoder.Init(ConfDirectory, ref ErrorMessage); + + foreach (PatchSpecficDecoder p in PatchList) + { + Logger("Initialising patch " + p.GetVersion()); + if (!p.Init(ConfDirectory, ref ErrorMessage)) + Logger(ErrorMessage); + else + AllDecodersFailed = false; + } + + return !AllDecodersFailed; + } + + public string GetDecoderVersion() + { + // We don't need to check if PatchDecoder is null, because it is always initialised to an instance of the base + // PatchSpecficDecoder class + return PatchDecoder.GetVersion(); + } + + public bool StreamRecognised() + { + // Only the base PatchSpecficDecoder class returns false + return !PatchDecoder.UnsupportedVersion(); + } + + public bool DumpPackets(string FileName, bool ShowTimeStamps) + { + return PatchDecoder.DumpPackets(FileName, ShowTimeStamps); + } + + // This method is called by the PacketManager as it processes each packet in order to determine if the client patch + // version is recognised. + // + // We call the Identify methods of each supported version decoder + // + // The decoders return No, if they cannot recognise the version from the given OpCode, size and direction, + // 'Tentative' if they recognise it, but are not 100% sure it is their version, and Yes if they are 100% + // sure the version has been identified. + // + public IdentificationStatus Identify(int OpCode, int Size, PacketDirection Direction) + { + IdentificationStatus Status = IdentificationStatus.No; + + foreach (PatchSpecficDecoder p in PatchList) + { + IdentificationStatus TempStatus = p.Identify(OpCode, Size, Direction); + + if (TempStatus == IdentificationStatus.Yes) + { + // The version has been identified. Set PatchDecoder to point to this decoder + PatchDecoder = p; + + return IdentificationStatus.Yes; + } + else if (TempStatus > Status) + Status = TempStatus; + } + return Status; + } + + // This is called by the main form when all the packets have been processed. It prompts us to pass the packets down + // to the decoder for this patch version. + public void PCAPFileReadFinished() + { + PatchDecoder.GivePackets(Packets); + } + + // The following methods are called by the main form and we just pass them on down to the decoder for the particular + // patch version that the .pcap was produced with. + + public List GetPacketsOfType(string OpCodeName, PacketDirection Direction) + { + return PatchDecoder.GetPacketsOfType(OpCodeName, Direction); + } + + public void ProcessPacket(System.Net.IPAddress srcIp, System.Net.IPAddress dstIp, ushort srcPort, ushort dstPort, byte[] Payload, DateTime PacketTime) + { + Packets.ProcessPacket(srcIp, dstIp, srcPort, dstPort, Payload, PacketTime, false, false); + } + + public DateTime GetCaptureStartTime() + { + return PatchDecoder.GetCaptureStartTime(); + } + + public string GetZoneName() + { + return PatchDecoder.GetZoneName(); + } + + public string GetZoneLongName() + { + NewZoneStruct NewZone = PatchDecoder.GetZoneData(); + + return NewZone.LongName; + } + + public int VerifyPlayerProfile() + { + return PatchDecoder.VerifyPlayerProfile(); + } + + public UInt16 GetZoneNumber() + { + return PatchDecoder.GetZoneNumber(); + } + + // + // The following are all the methods that actually generate the SQL. Again, we call the patch specific decoder's methods to + // return the data we need in a version agnostic format. + // + + public delegate void SQLDestination(string Message); + + public void GenerateDoorsSQL(string ZoneName, int DoorDBID, UInt32 SpawnVersion, SQLDestination SQLOut) + { + List DoorList = PatchDecoder.GetDoors(); + + SQLOut("--"); + SQLOut("-- Doors"); + SQLOut("--"); + int UpperBound = (DoorDBID == 1 ? 998 : 999); + SQLOut("DELETE from doors where zone = '" + ZoneName + "' and doorid >= @BaseDoorID and doorid <= @BaseDoorID + " + UpperBound + " and version = " + SpawnVersion + ";"); + + foreach(Door d in DoorList) + { + if ((d.OpenType == 57) || (d.OpenType == 58)) + { + ZonePoint? zp = GetZonePointNumber(d.DoorParam); + + if (zp != null) + { + d.DestZone = ZoneNumberToName(zp.Value.TargetZoneID); + d.DestX = zp.Value.TargetX; + d.DestY = zp.Value.TargetY; + d.DestZ = zp.Value.TargetZ; + d.DestHeading = zp.Value.Heading; + + } + } + + string DoorQuery = "INSERT INTO doors(`doorid`, `zone`, `version`, `name`, `pos_y`, `pos_x`, `pos_z`, `heading`, `opentype`, `doorisopen`, `door_param`, `dest_zone`, `dest_x`, `dest_y`, `dest_z`, `dest_heading`, `invert_state`, `incline`, `size`) VALUES("; + DoorQuery += "@BaseDoorID + " + d.ID + ", '" + ZoneName + "', " + SpawnVersion + ", '" + d.Name + "', " + d.YPos + ", " + d.XPos + ", " + d.ZPos + ", " + d.Heading + ", " + d.OpenType + ", " + d.StateAtSpawn + ", " + d.DoorParam + ", '" + d.DestZone + "', " + d.DestX + ", " + d.DestY + ", " + d.DestZ + ", " + d.DestHeading + ", " + d.InvertState + ", " + d.Incline + ", " + d.Size + ");"; + + SQLOut(DoorQuery); + } + } + + public void GenerateSpawnSQL(bool GenerateSpawns, bool GenerateGrids, bool GenerateMerchants, + string ZoneName, UInt32 ZoneID, UInt32 SpawnVersion, + bool UpdateExistingNPCTypes, bool UseNPCTypesTint, string SpawnNameFilter, + bool CoalesceWaypoints, bool IncludeInvisibleMen, SQLDestination SQLOut) + { + UInt32 NPCTypeDBID = 0; + UInt32 SpawnGroupID = 0; + UInt32 SpawnEntryID = 0; + UInt32 Spawn2ID = 0; + UInt32 GridDBID = 0; + UInt32 MerchantDBID = 0; + + List ZoneSpawns = PatchDecoder.GetSpawns(); + + List FindableEntities = PatchDecoder.GetFindableSpawns(); + + if (GenerateSpawns) + { + SQLOut("--"); + SQLOut("-- Spawns"); + SQLOut("--"); + if (SpawnVersion == 0) + { + SQLOut("DELETE from npc_types where id >= @StartingNPCTypeID and id <= @StartingNPCTypeID + 999 and version = " + SpawnVersion + ";"); + + if(UseNPCTypesTint) + SQLOut("DELETE from npc_types_tint where id >= @StartingNPCTypeID and id <= @StartingNPCTypeID + 999;"); + + SQLOut("DELETE from spawngroup where id >= @StartingSpawnGroupID and id <= @StartingSpawnGroupID + 999;"); + SQLOut("DELETE from spawnentry where spawngroupID >= @StartingSpawnEntryID and spawngroupID <= @StartingSpawnEntryID + 999;"); + SQLOut("DELETE from spawn2 where id >= @StartingSpawn2ID and id <= @StartingSpawn2ID + 999 and version = " + SpawnVersion + ";"); + } + else + { + SQLOut("DELETE from npc_types where id >= @StartingNPCTypeID and id <= @StartingNPCTypeID + 99 and version = " + SpawnVersion + ";"); + SQLOut("DELETE from spawngroup where id >= @StartingSpawnGroupID and id <= @StartingSpawnGroupID + 99;"); + SQLOut("DELETE from spawnentry where spawngroupID >= @StartingSpawnEntryID and spawngroupID <= @StartingSpawnEntryID + 99;"); + SQLOut("DELETE from spawn2 where id >= @StartingSpawn2ID and id <= @StartingSpawn2ID + 99 and version = " + SpawnVersion + ";"); + } + + } + + NPCTypeList NPCTL = new NPCTypeList(); + + NPCSpawnList NPCSL = new NPCSpawnList(); + + foreach(ZoneEntryStruct Spawn in ZoneSpawns) + { + if (NPCType.IsMount(Spawn.SpawnName)) + continue; + + if (!IncludeInvisibleMen && (Spawn.Race == 127)) + continue; + + Spawn.Findable = (FindableEntities.IndexOf(Spawn.SpawnID) >= 0); + + if (Spawn.IsNPC != 1) + continue; + + if (Spawn.PetOwnerID > 0) + continue; + + if ((SpawnNameFilter.Length > 0) && (Spawn.SpawnName.IndexOf(SpawnNameFilter) == -1)) + continue; + + bool ColoursInUse = false; + + for (int ColourSlot = 0; ColourSlot < 9; ++ColourSlot) + { + if (((Spawn.SlotColour[ColourSlot] & 0x00ffffff) != 0) && UseNPCTypesTint) + ColoursInUse = true; + } + + if (Spawn.IsMercenary > 0) + continue; + + UInt32 ExistingDBID = NPCTL.FindNPCType(Spawn.SpawnName, Spawn.Level, Spawn.Gender, Spawn.Size, Spawn.Face, Spawn.WalkSpeed, Spawn.RunSpeed, Spawn.Race, + Spawn.BodyType, Spawn.HairColor, Spawn.BeardColor, Spawn.EyeColor1, Spawn.EyeColor2, Spawn.HairStyle, Spawn.Beard, + Spawn.DrakkinHeritage, Spawn.DrakkinTattoo, Spawn.DrakkinDetails, Spawn.Deity, Spawn.Class, Spawn.EquipChest2, + Spawn.Helm, Spawn.LastName); + + + if (ExistingDBID == 0) + { + NPCType NewNPCType = new NPCType(NPCTypeDBID, Spawn.SpawnName, Spawn.Level, Spawn.Gender, Spawn.Size, Spawn.Face, Spawn.WalkSpeed, Spawn.RunSpeed, Spawn.Race, + Spawn.BodyType, Spawn.HairColor, Spawn.BeardColor, Spawn.EyeColor1, Spawn.EyeColor2, Spawn.HairStyle, Spawn.Beard, + Spawn.DrakkinHeritage, Spawn.DrakkinTattoo, Spawn.DrakkinDetails, Spawn.Deity, Spawn.Class, Spawn.EquipChest2, + Spawn.Helm, Spawn.LastName, Spawn.Findable, Spawn.MeleeTexture1, Spawn.MeleeTexture2, Spawn.ArmorTintRed, Spawn.ArmorTintGreen, Spawn.ArmorTintBlue, Spawn.SlotColour); + + NPCTL.AddNPCType(NewNPCType); + + ExistingDBID = NPCTypeDBID++; + + UInt32 ArmorTintID = 0; + + if (ColoursInUse) + { + ArmorTintID = ExistingDBID; + Spawn.ArmorTintRed = 0; + Spawn.ArmorTintGreen = 0; + Spawn.ArmorTintBlue = 0; + } + + string NPCTypesQuery = "INSERT INTO npc_types(`id`, `name`, `lastname`, `level`, `gender`, `size`, `runspeed`,`race`, `class`, `bodytype`, `hp`, `texture`, `helmtexture`, `face`, `luclin_hairstyle`, `luclin_haircolor`, `luclin_eyecolor`, `luclin_eyecolor2`,`luclin_beard`, `luclin_beardcolor`, `findable`, `version`, `d_meele_texture1`, `d_meele_texture2`, `armortint_red`, `armortint_green`, `armortint_blue`, `drakkin_heritage`, `drakkin_tattoo`, `drakkin_details`) VALUES("; + + NPCTypesQuery += "@StartingNPCTypeID + " + ExistingDBID + ", '" + Spawn.SpawnName + "', " + "'" + Spawn.LastName + "', " + Spawn.Level + ", " + Spawn.Gender + ", " + Spawn.Size + ", "; + NPCTypesQuery += Spawn.RunSpeed + ", " + Spawn.Race + ", " + Spawn.Class + ", " + Spawn.BodyType + ", " + Spawn.Level * (10 + Spawn.Level) + ", "; + NPCTypesQuery += Spawn.EquipChest2 + ", " + Spawn.Helm + ", " + Spawn.Face + ", " + Spawn.HairStyle + ", " + Spawn.HairColor + ", " + Spawn.EyeColor1 + ", "; + NPCTypesQuery += Spawn.EyeColor2 + ", " + Spawn.Beard + ", " + Spawn.BeardColor + ", " + (Spawn.Findable ? 1 : 0) + ", " + SpawnVersion + ", "; + NPCTypesQuery += Spawn.MeleeTexture1 + ", " + Spawn.MeleeTexture2 + ", " + Spawn.ArmorTintRed + ", " + Spawn.ArmorTintGreen + ", " + Spawn.ArmorTintBlue + ", "; + NPCTypesQuery += Spawn.DrakkinHeritage + ", " + Spawn.DrakkinTattoo + ", " + Spawn.DrakkinDetails + ");"; + + if (GenerateSpawns) + SQLOut(NPCTypesQuery); + + if (GenerateSpawns && ColoursInUse && NPCType.IsPlayableRace(Spawn.Race)) + { + string TintQuery = "REPLACE INTO npc_types_tint(id, tint_set_name, "; + TintQuery += "red1h, grn1h, blu1h, "; + TintQuery += "red2c, grn2c, blu2c, "; + TintQuery += "red3a, grn3a, blu3a, "; + TintQuery += "red4b, grn4b, blu4b, "; + TintQuery += "red5g, grn5g, blu5g, "; + TintQuery += "red6l, grn6l, blu6l, "; + TintQuery += "red7f, grn7f, blu7f, "; + TintQuery += "red8x, grn8x, blu8x, "; + TintQuery += "red9x, grn9x, blu9x) values(@StartingNPCTypeID + " + ExistingDBID + ", '" + Spawn.SpawnName + "'"; + + for (int sc = 0; sc < 9; ++sc) + { + TintQuery += String.Format(", {0}, {1}, {2}", (Spawn.SlotColour[sc] >> 16) & 0xff, + (Spawn.SlotColour[sc] >> 8) & 0xff, + (Spawn.SlotColour[sc] & 0xff)); + } + + TintQuery += ");"; + SQLOut(TintQuery); + SQLOut("UPDATE npc_types SET armortint_id = @StartingNPCTypeID + " + ArmorTintID + " WHERE id = @StartingNPCTypeID + " + ExistingDBID + " LIMIT 1;"); + } + + } + + NPCSL.AddNPCSpawn(Spawn.SpawnID, Spawn2ID, ExistingDBID, Spawn.SpawnName); + + Position p = new Position(Spawn.XPos, Spawn.YPos, Spawn.ZPos, Spawn.Heading, DateTime.MinValue); + + NPCSL.AddWaypoint(Spawn.SpawnID, p, false); + + string SpawnGroupQuery = "INSERT INTO spawngroup(`id`, `name`, `spawn_limit`, `dist`, `max_x`, `min_x`, `max_y`, `min_y`, `delay`) VALUES("; + SpawnGroupQuery += "@StartingSpawnGroupID + " + SpawnGroupID + ", CONCAT('" + ZoneName + "', @StartingSpawnGroupID + " + SpawnGroupID + "), 0, 0, 0, 0, 0, 0, 0);"; + + string SpawnEntryQuery = "INSERT INTO spawnentry(`spawngroupID`, `npcID`, `chance`) VALUES("; + SpawnEntryQuery += "@StartingSpawnEntryID + " + SpawnEntryID + ", @StartingNPCTypeID + " + ExistingDBID + ", " + "100);"; + + string Spawn2EntryQuery = "INSERT INTO spawn2(`id`, `spawngroupID`, `zone`, `version`, `x`, `y`, `z`, `heading`, `respawntime`, `variance`, `pathgrid`, `_condition`, `cond_value`, `enabled`) VALUES("; + Spawn2EntryQuery += "@StartingSpawn2ID + " + Spawn2ID + ", @StartingSpawnGroupID + " + SpawnGroupID + ", '" + ZoneName + "', " + SpawnVersion + ", " + Spawn.XPos + ", " + Spawn.YPos + ", " + Spawn.ZPos + ", "; + Spawn2EntryQuery += Spawn.Heading + ", 640, 0, 0, 0, 1, 1);"; + + SpawnGroupID++; + SpawnEntryID++; + Spawn2ID++; + + if (GenerateSpawns) + { + SQLOut(SpawnGroupQuery); + SQLOut(SpawnEntryQuery); + SQLOut(Spawn2EntryQuery); + } + } + + if (UpdateExistingNPCTypes) + { + List UniqueSpawns = NPCTL.GetUniqueSpawns(); + + foreach (NPCType n in UniqueSpawns) + { + string UpdateQuery = "UPDATE npc_types set texture = {0}, helmtexture = {1}, size = {2}, face = {3}, luclin_hairstyle = {4}, "; + UpdateQuery += "luclin_haircolor = {5}, luclin_eyecolor = {6}, luclin_eyecolor2 = {7}, luclin_beardcolor = {8}, "; + UpdateQuery += "luclin_beard = {9}, drakkin_heritage = {10}, drakkin_tattoo = {11}, drakkin_details = {12}, "; + UpdateQuery += "armortint_red = {13}, armortint_green = {14}, armortint_blue = {15}, d_meele_texture1 = {16}, "; + UpdateQuery += "d_meele_texture2 = {17}, findable = {18}, gender = {20} where name = '{19}' and id >= @StartingNPCTypeID and id <= @StartingNPCTypeID + 999 and version = {21};"; + + SQLOut(String.Format(UpdateQuery, n.EquipChest2, n.Helm, n.Size, n.Face, n.HairStyle, n.HairColor, n.EyeColor1, n.EyeColor2, n.BeardColor, + n.Beard, n.DrakkinHeritage, n.DrakkinTattoo, n.DrakkinDetails, n.ArmorTintRed, n.ArmorTintGreen, + n.ArmorTintBlue, n.MeleeTexture1, n.MeleeTexture2, n.Findable, n.Name, n.Gender, SpawnVersion)); + + if(!NPCType.IsPlayableRace(n.Race)) + continue; + + bool ColoursInUse = false; + + for (int ColourSlot = 0; ColourSlot < 9; ++ColourSlot) + { + if (((n.SlotColour[ColourSlot] & 0x00ffffff) != 0) && UseNPCTypesTint) + ColoursInUse = true; + } + + if (ColoursInUse) + { + string TintQuery = "REPLACE INTO npc_types_tint(id, tint_set_name, "; + TintQuery += "red1h, grn1h, blu1h, "; + TintQuery += "red2c, grn2c, blu2c, "; + TintQuery += "red3a, grn3a, blu3a, "; + TintQuery += "red4b, grn4b, blu4b, "; + TintQuery += "red5g, grn5g, blu5g, "; + TintQuery += "red6l, grn6l, blu6l, "; + TintQuery += "red7f, grn7f, blu7f, "; + TintQuery += "red8x, grn8x, blu8x, "; + TintQuery += "red9x, grn9x, blu9x) SELECT id, '" + n.Name + "'"; + + for (int sc = 0; sc < 9; ++sc) + { + TintQuery += String.Format(", {0}, {1}, {2}", (n.SlotColour[sc] >> 16) & 0xff, + (n.SlotColour[sc] >> 8) & 0xff, + (n.SlotColour[sc] & 0xff)); + } + + TintQuery += " from npc_types where name = '" + n.Name + "' and id >= @StartingNPCTypeID and id <= @StartingNPCTypeID + 999 and version = " + SpawnVersion + " LIMIT 1;"; + SQLOut(TintQuery); + SQLOut(String.Format("UPDATE npc_types set armortint_id = (SELECT id from npc_types_tint WHERE tint_set_name = '{0}' and id >= @StartingNPCTypeID and id <= @StartingNPCTypeID + 999 and version = {1} LIMIT 1), armortint_red = 0, armortint_green = 0, armortint_blue = 0 WHERE name = '{0}' and id >= @StartingNPCTypeID and id <= @StartingNPCTypeID + 999 and version = {1} LIMIT 1;", + n.Name, SpawnVersion)); + } + } + return; + } + + if (GenerateGrids) + { + List AllMovementUpdates = PatchDecoder.GetAllMovementUpdates(); + + foreach (PositionUpdate Update in AllMovementUpdates) + NPCSL.AddWaypoint(Update.SpawnID, Update.p, Update.HighRes); + + SQLOut("--"); + SQLOut("-- Grids"); + SQLOut("--"); + + SQLOut("DELETE from grid WHERE id >= @StartingGridID AND id <= @StartingGridID + 999;"); + SQLOut("DELETE from grid_entries WHERE gridid >= @StartingGridID AND gridid <= @StartingGridID + 999;"); + foreach (NPCSpawn ns in NPCSL._NPCSpawnList) + { + if (ns.Waypoints.Count > 1) + { + bool AllWaypointsTheSame = true; + + for (int WPNumber = 0; WPNumber < ns.Waypoints.Count; ++WPNumber) + { + if (WPNumber == 0) + continue; + if ((ns.Waypoints[WPNumber].x != ns.Waypoints[WPNumber - 1].x) || + (ns.Waypoints[WPNumber].y != ns.Waypoints[WPNumber - 1].y) || + (ns.Waypoints[WPNumber].z != ns.Waypoints[WPNumber - 1].z)) + { + AllWaypointsTheSame = false; + } + } + + if (AllWaypointsTheSame) + continue; + + int WaypointsInserted = 0; + + int WPNum = 1; + + int Pause = 10; + + int FirstUsableWaypoint = 0; + + if (ns.DoesHaveHighResWaypoints()) + FirstUsableWaypoint = 1; + + for (int WPNumber = FirstUsableWaypoint; WPNumber < ns.Waypoints.Count; ++WPNumber) + { + Position p = ns.Waypoints[WPNumber]; + + if (CoalesceWaypoints) + { + if ((WPNumber > FirstUsableWaypoint) && (WPNumber < (ns.Waypoints.Count - 2))) + { + Position np = ns.Waypoints[WPNumber + 1]; + + if ((Math.Abs(p.heading - np.heading) < 1) || (Math.Abs(p.heading - np.heading) > 255)) + { + // Skipping waypoint as heading is the same as next. + continue; + } + if ((Math.Abs(p.heading - np.heading) < 5) || (Math.Abs(p.heading - np.heading) > 251)) + { + // Setting pause to 0 because headings are similar + Pause = 0; + } + else if ((p.x == np.x) && (p.y == np.y) && (p.z == np.z)) + { + // Skipping waypoint as same as next"); + continue; + } + else + Pause = 10; + } + } + + // If this is the last waypoint, and we haven't inserted any of the previous ones, then don't bother + // with this one either. + if ((WPNumber == (ns.Waypoints.Count - 1)) && (WaypointsInserted == 0)) + continue; + + SQLOut("INSERT into grid_entries (`gridid`, `zoneid`, `number`, `x`, `y`, `z`, `heading`, `pause`) VALUES(@StartingGridID + " + GridDBID + ", " + ZoneID + ", " + (WPNum++) + ", " + p.x + ", " + p.y + ", " + p.z + ", " + p.heading + ", " + Pause + ");"); + + ++WaypointsInserted; + } + if (WaypointsInserted > 1) + { + SQLOut("INSERT into grid(`id`, `zoneid`, `type`, `type2`) VALUES(@StartingGridID + " + GridDBID + ", " + ZoneID + ", 3, 2); -- " + ns.Name); + + SQLOut("UPDATE spawn2 set pathgrid = @StartingGridID + " + GridDBID + " WHERE id = @StartingSpawn2ID + " + ns.Spawn2DBID + ";"); + + if (ns.DoesHaveHighResWaypoints()) + SQLOut("UPDATE spawn2 set x = " + ns.Waypoints[1].x + ", y = " + ns.Waypoints[1].y + ", z = " + ns.Waypoints[1].z + ", heading = " + ns.Waypoints[1].heading + " WHERE id = @StartingSpawn2ID + " + ns.Spawn2DBID + ";"); + + ++GridDBID; + } + } + } + } + if(GenerateMerchants) + GenerateMerchantSQL(NPCSL, MerchantDBID, GenerateSpawns, SQLOut); + } + + public void GenerateMerchantSQL(NPCSpawnList NPCSL, UInt32 MerchantDBID, bool GenerateSpawns, SQLDestination SQLOut) + { + MerchantManager mm = PatchDecoder.GetMerchantData(NPCSL); + + if(GenerateSpawns) + SQLOut("DELETE from merchantlist where merchantid >= @StartingMerchantID and merchantid <= @StartingMerchantID + 999;"); + + SQLOut("--"); + SQLOut("-- Merchant Lists"); + SQLOut("-- "); + + foreach (Merchant m in mm.MerchantList) + { + UInt32 MerchantSpawnID = m.SpawnID; + + NPCSpawn npc = NPCSL.GetNPC(MerchantSpawnID); + + if(npc == null) + continue; + + UInt32 MerchantNPCTypeID = npc.NPCTypeID; + + SQLOut("--"); + SQLOut("-- " + npc.Name); + SQLOut("-- "); + + bool StartOfPlayerSoldItems = false; + + foreach (MerchantItem mi in m.Items) + { + string Insert = ""; + + if (mi.Quantity >= 0) + { + if (!StartOfPlayerSoldItems) + { + StartOfPlayerSoldItems = true; + SQLOut("--"); + SQLOut("-- The items below were more than likely sold to " + npc.Name + " by players. Uncomment them if you want."); + SQLOut("--"); + } + + Insert += "-- "; + } + + Insert += "INSERT into merchantlist(`merchantid`, `slot`, `item`) VALUES("; + + Insert += "@StartingMerchantID + " + MerchantDBID + ", " + mi.Slot + ", " + mi.ItemID + "); -- " + mi.Name; + + SQLOut(Insert); + } + + if(GenerateSpawns) + SQLOut("UPDATE npc_types SET merchant_id = @StartingMerchantID + " + MerchantDBID + " WHERE id = @StartingNPCTypeID + " + MerchantNPCTypeID + ";"); + + ++MerchantDBID; + } + } + + public void GenerateZonePointSQL(string ZoneName, SQLDestination SQLOut) + { + foreach (ZonePoint zp in ZonePointList) + { + string Insert = String.Format("REPLACE into zone_points(`zone`, `number`, `y`, `x`, `z`, `heading`, `target_y`, `target_x`, `target_z`, `target_heading`, `zoneinst`, `target_zone_id`, `buffer`) VALUES('{0}', {1}, 0, 0, 0, 0, {2}, {3}, {4}, {5}, {6}, {7}, 0);", ZoneName, zp.Number, zp.y, zp.x, zp.z, zp.Heading, zp.Instance, zp.ZoneID); + + SQLOut(Insert); + } + } + + public void GenerateZonePointList() + { + ZonePointList = PatchDecoder.GetZonePointList(); + } + + public ZonePoint? GetZonePointNumber(Int32 Number) + { + if (ZonePointList != null) + { + foreach (ZonePoint zp in ZonePointList) + { + if (zp.Number == Number) + return zp; + } + } + return null; + } + + public UInt32 GenerateZoneSQL(SQLDestination SQLOut) + { + UInt16 ZoneID = PatchDecoder.GetZoneNumber(); + + NewZoneStruct NewZone = PatchDecoder.GetZoneData(); + + SQLOut("--"); + SQLOut("-- Zone Config"); + SQLOut("--"); + string InsertFormat = "UPDATE zone set `short_name` = '{0}', `file_name` = '', `long_name` = '{1}', `safe_x` = {2}, `safe_y` = {3}, `safe_z` = {4}, "; + InsertFormat += "`underworld` = {6}, `minclip` = {7}, `maxclip` = {8}, `fog_minclip` = {9}, `fog_maxclip` = {10}, "; + InsertFormat += "`fog_blue` = {11}, `fog_red` = {12}, `fog_green` = {13}, `sky` = {14}, `ztype` = {15}, `time_type` = {16}, "; + InsertFormat += "`fog_red2` = {17}, `fog_green2` = {18}, `fog_blue2` = {19}, `fog_minclip2` = {20}, `fog_maxclip2` = {21}, "; + InsertFormat += "`fog_red3` = {22}, `fog_green3` = {23}, `fog_blue3` = {24}, `fog_minclip3` = {25}, `fog_maxclip3` = {26}, "; + InsertFormat += "`fog_red4` = {27}, `fog_green4` = {28}, `fog_blue4` = {29}, `fog_minclip4` = {30}, `fog_maxclip4` = {31} WHERE zoneidnumber = {5};"; + + SQLOut(String.Format(InsertFormat, NewZone.ShortName2, NewZone.LongName, NewZone.SafeX, NewZone.SafeY, NewZone.SafeZ, + ZoneID, NewZone.UnderWorld, NewZone.MinClip, NewZone.MaxClip, NewZone.FogMinClip[0], NewZone.FogMaxClip[0], + NewZone.FogBlue[0], NewZone.FogRed[0], NewZone.FogGreen[0], NewZone.Sky, NewZone.Type, NewZone.TimeType, + NewZone.FogRed[1], NewZone.FogGreen[1], NewZone.FogBlue[1], NewZone.FogMinClip[1], NewZone.FogMaxClip[1], + NewZone.FogRed[2], NewZone.FogGreen[2], NewZone.FogBlue[2], NewZone.FogMinClip[2], NewZone.FogMaxClip[2], + NewZone.FogRed[3], NewZone.FogGreen[3], NewZone.FogBlue[3], NewZone.FogMinClip[3], NewZone.FogMaxClip[3])); + + SQLOut(String.Format("UPDATE zone set fog_density = {0} WHERE zoneidnumber = {1};", NewZone.FogDensity, ZoneID)); + SQLOut("--"); + + return ZoneID; + } + + public bool DumpAAs(string FileName) + { + return PatchDecoder.DumpAAs(FileName); + } + + public void GenerateObjectSQL(bool DoGroundSpawns, bool DoObjects, UInt32 SpawnVersion, SQLDestination SQLOut) + { + List GroundSpawns = PatchDecoder.GetGroundSpawns(); + + UInt32 GroundSpawnDBID = 0; + UInt32 ObjectDBID = 0; + + SQLOut("--"); + SQLOut("-- Objects and Groundspawns"); + SQLOut("--"); + + if(DoGroundSpawns) + SQLOut("DELETE from ground_spawns where id >= @StartingGroundSpawnID and id <= @StartingGroundSpawnID + 999 and version = " + SpawnVersion + ";"); + + if(DoObjects) + SQLOut("DELETE from object where id >= @StartingObjectID and id <= @StartingObjectID + 999 and version = " + SpawnVersion + ";"); + + foreach(GroundSpawnStruct GroundSpawn in GroundSpawns) + { + String Insert; + + if (IsGroundSpawn(GroundSpawn.ObjectType) && DoGroundSpawns) + { + Insert = "INSERT into ground_spawns(`id`, `zoneid`, `version`, `max_x`, `max_y`, `max_z`, `min_x`, `min_y`, `heading`, `name`, `item`, `max_allowed`, `comment`, `respawn_timer`) VALUES("; + + Insert += "@StartingGroundSpawnID + " + (GroundSpawnDBID++) + ", " + GroundSpawn.ZoneID + ", " + SpawnVersion + ", " + GroundSpawn.x + ", " + GroundSpawn.y + ", " + GroundSpawn.z + ", " + GroundSpawn.x + ", " + GroundSpawn.y + ", " + GroundSpawn.Heading + ", '" + GroundSpawn.Name + "', 1001, 1, 'Auto generated by Collector. FIX THE ITEMID!', 300000);"; + + SQLOut(Insert); + } + else if(!IsGroundSpawn(GroundSpawn.ObjectType) && DoObjects) + { + GroundSpawn.ObjectType = ObjectNameToType(GroundSpawn.Name); + + Insert = "INSERT into object(`id`, `zoneid`, `version`, `xpos`, `ypos`, `zpos`, `heading`, `itemid`, `charges`, `objectname`, `type`, `icon`) VALUES("; + + Insert += "@StartingObjectID + " + (ObjectDBID++) + ", " + GroundSpawn.ZoneID + ", " + SpawnVersion + ", " + GroundSpawn.x + ", " + GroundSpawn.y + ", " + GroundSpawn.z + ", " + GroundSpawn.Heading + ", 0, 0, '" + GroundSpawn.Name + "', " + GroundSpawn.ObjectType + ", 0);"; + + SQLOut(Insert); + } + } + } + + static bool IsGroundSpawn(UInt32 Type) + { + if ((Type == 1) || (Type == 8)) + return true; + + return false; + } + + public static UInt32 ObjectNameToType(string Name) + { + switch (Name) + { + case "IT10511_ACTORDEF": + return 3; + case "IT10512_ACTODEF": + return 1; + case "IT10714_ACTORDEF": + return 53; + case "IT10800_ACTORDEF": + return 21; + case "IT10801_ACTORDEF": + return 22; + case "IT10802_ACTORDEF": + return 16; + case "IT10803_ACTORDEF": + return 15; + case "IT10865_ACTORDEF": + return 15; + case "IT128_ACTORDEF": + return 16; + case "IT27_ACTORDEF": + return 0; + case "IT403_ACTORDEF": + return 1; + case "IT5_ACTORDEF": + return 25; + case "IT63_ACTORDEF": + return 8; + case "IT64_ACTORDEF": + return 30; + case "IT69_ACTORDEF": + return 15; + case "IT73_ACTORDEF": + return 22; + case "IT74_ACTORDEF": + return 21; + case "IT10805_ACTORDEF": + case "IT70_ACTORDEF": + return 19; + case "IT66_ACTORDEF": + case "IT10804_ACTORDEF": + case "IT10863_ACTORDEF": + case "IT10864_ACTORDEF": + return 17; + + default: + return 255; + } + } + + public static UInt32 ZoneNameToNumber(string ZoneName) + { + switch (ZoneName) + { + case "qeynos": return 1; + case "qeynos2": return 2; + case "qrg": return 3; + case "qeytoqrg": return 4; + case "highpass": return 5; + case "highkeep": return 6; + case "freportn": return 8; + case "freportw": return 9; + case "freporte": return 10; + case "runnyeye": return 11; + case "qey2hh1": return 12; + case "northkarana": return 13; + case "southkarana": return 14; + case "eastkarana": return 15; + case "beholder": return 16; + case "blackburrow": return 17; + case "paw": return 18; + case "rivervale": return 19; + case "kithicor": return 20; + case "commons": return 21; + case "ecommons": return 22; + case "erudnint": return 23; + case "erudnext": return 24; + case "nektulos": return 25; + case "cshome": return 26; + case "lavastorm": return 27; + case "nektropos": return 28; + case "halas": return 29; + case "everfrost": return 30; + case "soldunga": return 31; + case "soldungb": return 32; + case "misty": return 33; + case "nro": return 34; + case "sro": return 35; + case "befallen": return 36; + case "oasis": return 37; + case "tox": return 38; + case "hole": return 39; + case "neriaka": return 40; + case "neriakb": return 41; + case "neriakc": return 42; + case "neriakd": return 43; + case "najena": return 44; + case "qcat": return 45; + case "innothule": return 46; + case "feerrott": return 47; + case "cazicthule": return 48; + case "oggok": return 49; + case "rathemtn": return 50; + case "lakerathe": return 51; + case "grobb": return 52; + case "aviak": return 53; + case "gfaydark": return 54; + case "akanon": return 55; + case "steamfont": return 56; + case "lfaydark": return 57; + case "crushbone": return 58; + case "mistmoore": return 59; + case "kaladima": return 60; + case "felwithea": return 61; + case "felwitheb": return 62; + case "unrest": return 63; + case "kedge": return 64; + case "guktop": return 65; + case "gukbottom": return 66; + case "kaladimb": return 67; + case "butcher": return 68; + case "oot": return 69; + case "cauldron": return 70; + case "airplane": return 71; + case "fearplane": return 72; + case "permafrost": return 73; + case "kerraridge": return 74; + case "paineel": return 75; + case "hateplane": return 76; + case "arena": return 77; + case "fieldofbone": return 78; + case "warslikswood": return 79; + case "soltemple": return 80; + case "droga": return 81; + case "cabwest": return 82; + case "swampofnohope": return 83; + case "firiona": return 84; + case "lakeofillomen": return 85; + case "dreadlands": return 86; + case "burningwood": return 87; + case "kaesora": return 88; + case "sebilis": return 89; + case "citymist": return 90; + case "skyfire": return 91; + case "frontiermtns": return 92; + case "overthere": return 93; + case "emeraldjungle": return 94; + case "trakanon": return 95; + case "timorous": return 96; + case "kurn": return 97; + case "erudsxing": return 98; + case "stonebrunt": return 100; + case "warrens": return 101; + case "karnor": return 102; + case "chardok": return 103; + case "dalnir": return 104; + case "charasis": return 105; + case "cabeast": return 106; + case "nurga": return 107; + case "veeshan": return 108; + case "veksar": return 109; + case "iceclad": return 110; + case "frozenshadow": return 111; + case "velketor": return 112; + case "kael": return 113; + case "skyshrine": return 114; + case "thurgadina": return 115; + case "eastwastes": return 116; + case "cobaltscar": return 117; + case "greatdivide": return 118; + case "wakening": return 119; + case "westwastes": return 120; + case "crystal": return 121; + case "necropolis": return 123; + case "templeveeshan": return 124; + case "sirens": return 125; + case "mischiefplane": return 126; + case "growthplane": return 127; + case "sleeper": return 128; + case "thurgadinb": return 129; + case "erudsxing2": return 130; + case "shadowhaven": return 150; + case "bazaar": return 151; + case "nexus": return 152; + case "echo": return 153; + case "acrylia": return 154; + case "sharvahl": return 155; + case "paludal": return 156; + case "fungusgrove": return 157; + case "vexthal": return 158; + case "sseru": return 159; + case "katta": return 160; + case "netherbian": return 161; + case "ssratemple": return 162; + case "griegsend": return 163; + case "thedeep": return 164; + case "shadeweaver": return 165; + case "hollowshade": return 166; + case "grimling": return 167; + case "mseru": return 168; + case "letalis": return 169; + case "twilight": return 170; + case "thegrey": return 171; + case "tenebrous": return 172; + case "maiden": return 173; + case "dawnshroud": return 174; + case "scarlet": return 175; + case "umbral": return 176; + case "akheva": return 179; + case "arena2": return 180; + case "jaggedpine": return 181; + case "nedaria": return 182; + case "tutorial": return 183; + case "load": return 184; + case "load2": return 185; + case "hateplaneb": return 186; + case "shadowrest": return 187; + case "tutoriala": return 188; + case "tutorialb": return 189; + case "clz": return 190; + case "codecay": return 200; + case "pojustice": return 201; + case "poknowledge": return 202; + case "potranquility": return 203; + case "ponightmare": return 204; + case "podisease": return 205; + case "poinnovation": return 206; + case "potorment": return 207; + case "povalor": return 208; + case "bothunder": return 209; + case "postorms": return 210; + case "hohonora": return 211; + case "solrotower": return 212; + case "powar": return 213; + case "potactics": return 214; + case "poair": return 215; + case "powater": return 216; + case "pofire": return 217; + case "poeartha": return 218; + case "potimea": return 219; + case "hohonorb": return 220; + case "nightmareb": return 221; + case "poearthb": return 222; + case "potimeb": return 223; + case "gunthak": return 224; + case "dulak": return 225; + case "torgiran": return 226; + case "nadox": return 227; + case "hatesfury": return 228; + case "guka": return 229; + case "ruja": return 230; + case "taka": return 231; + case "mira": return 232; + case "mmca": return 233; + case "gukb": return 234; + case "rujb": return 235; + case "takb": return 236; + case "mirb": return 237; + case "mmcb": return 238; + case "gukc": return 239; + case "rujc": return 240; + case "takc": return 241; + case "mirc": return 242; + case "mmcc": return 243; + case "gukd": return 244; + case "rujd": return 245; + case "takd": return 246; + case "mird": return 247; + case "mmcd": return 248; + case "guke": return 249; + case "ruje": return 250; + case "take": return 251; + case "mire": return 252; + case "mmce": return 253; + case "gukf": return 254; + case "rujf": return 255; + case "takf": return 256; + case "mirf": return 257; + case "mmcf": return 258; + case "gukg": return 259; + case "rujg": return 260; + case "takg": return 261; + case "mirg": return 262; + case "mmcg": return 263; + case "gukh": return 264; + case "rujh": return 265; + case "takh": return 266; + case "mirh": return 267; + case "mmch": return 268; + case "ruji": return 269; + case "taki": return 270; + case "miri": return 271; + case "mmci": return 272; + case "rujj": return 273; + case "takj": return 274; + case "mirj": return 275; + case "mmcj": return 276; + case "chardokb": return 277; + case "soldungc": return 278; + case "abysmal": return 279; + case "natimbi": return 280; + case "qinimi": return 281; + case "riwwi": return 282; + case "barindu": return 283; + case "ferubi": return 284; + case "snpool": return 285; + case "snlair": return 286; + case "snplant": return 287; + case "sncrematory": return 288; + case "tipt": return 289; + case "vxed": return 290; + case "yxtta": return 291; + case "uqua": return 292; + case "kodtaz": return 293; + case "ikkinz": return 294; + case "qvic": return 295; + case "inktuta": return 296; + case "txevu": return 297; + case "tacvi": return 298; + case "qvicb": return 299; + case "wallofslaughter": return 300; + case "bloodfields": return 301; + case "draniksscar": return 302; + case "causeway": return 303; + case "chambersa": return 304; + case "chambersb": return 305; + case "chambersc": return 306; + case "chambersd": return 307; + case "chamberse": return 308; + case "chambersf": return 309; + case "provinggrounds": return 316; + case "anguish": return 317; + case "dranikhollowsa": return 318; + case "dranikhollowsb": return 319; + case "dranikhollowsc": return 320; + case "dranikhollowsd": return 321; + case "dranikhollowse": return 322; + case "dranikhollowsf": return 323; + case "dranikhollowsg": return 324; + case "dranikhollowsh": return 325; + case "dranikhollowsi": return 326; + case "dranikhollowsj": return 327; + case "dranikcatacombsa": return 328; + case "dranikcatacombsb": return 329; + case "dranikcatacombsc": return 330; + case "draniksewersa": return 331; + case "draniksewersb": return 332; + case "draniksewersc": return 333; + case "riftseekers": return 334; + case "harbingers": return 335; + case "dranik": return 336; + case "broodlands": return 337; + case "stillmoona": return 338; + case "stillmoonb": return 339; + case "thundercrest": return 340; + case "delvea": return 341; + case "delveb": return 342; + case "thenest": return 343; + case "guildlobby": return 344; + case "guildhall": return 345; + case "barter": return 346; + case "illsalin": return 347; + case "illsalina": return 348; + case "illsalinb": return 349; + case "illsalinc": return 350; + case "dreadspire": return 351; + case "drachnidhive": return 354; + case "drachnidhivea": return 355; + case "drachnidhiveb": return 356; + case "drachnidhivec": return 357; + case "westkorlach": return 358; + case "westkorlacha": return 359; + case "westkorlachb": return 360; + case "westkorlachc": return 361; + case "eastkorlach": return 362; + case "eastkorlacha": return 363; + case "shadowspine": return 364; + case "corathus": return 365; + case "corathusa": return 366; + case "corathusb": return 367; + case "nektulosa": return 368; + case "arcstone": return 369; + case "relic": return 370; + case "skylance": return 371; + case "devastation": return 372; + case "devastationa": return 373; + case "rage": return 374; + case "ragea": return 375; + case "takishruins": return 376; + case "takishruinsa": return 377; + case "elddar": return 378; + case "elddara": return 379; + case "theater": return 380; + case "theatera": return 381; + case "freeporteast": return 382; + case "freeportwest": return 383; + case "freeportsewers": return 384; + case "freeportacademy": return 385; + case "freeporttemple": return 386; + case "freeportmilitia": return 387; + case "freeportarena": return 388; + case "freeportcityhall": return 389; + case "freeporttheater": return 390; + case "freeporthall": return 391; + case "northro": return 392; + case "southro": return 393; + case "crescent": return 394; + case "moors": return 395; + case "stonehive": return 396; + case "mesa": return 397; + case "roost": return 398; + case "steppes": return 399; + case "icefall": return 400; + case "valdeholm": return 401; + case "frostcrypt": return 402; + case "sunderock": return 403; + case "vergalid": return 404; + case "direwind": return 405; + case "ashengate": return 406; + case "highpasshold": return 407; + case "commonlands": return 408; + case "oceanoftears": return 409; + case "kithforest": return 410; + case "befallenb": return 411; + case "highpasskeep": return 412; + case "innothuleb": return 413; + case "toxxulia": return 414; + case "mistythicket": return 415; + case "kattacastrum": return 416; + case "thalassius": return 417; + case "atiiki": return 418; + case "zhisza": return 419; + case "silyssar": return 420; + case "solteris": return 421; + case "barren": return 422; + case "buriedsea": return 423; + case "jardelshook": return 424; + case "monkeyrock": return 425; + case "suncrest": return 426; + case "deadbone": return 427; + case "blacksail": return 428; + case "maidensgrave": return 429; + case "redfeather": return 430; + case "shipmvp": return 431; + case "shipmvu": return 432; + case "shippvu": return 433; + case "shipuvu": return 434; + case "shipmvm": return 435; + case "mechanotus": return 436; + case "mansion": return 437; + case "steamfactory": return 438; + case "shipworkshop": return 439; + case "gyrospireb": return 440; + case "gyrospirez": return 441; + case "dragonscale": return 442; + case "lopingplains": return 443; + case "hillsofshade": return 444; + case "bloodmoon": return 445; + case "crystallos": return 446; + case "guardian": return 447; + case "steamfontmts": return 448; + case "cryptofshade": return 449; + case "dragonscaleb": return 451; + case "oldfieldofbone": return 452; + case "oldkaesoraa": return 453; + case "oldkaesorab": return 454; + case "oldkurn": return 455; + case "oldkithicor": return 456; + case "oldcommons": return 457; + case "oldhighpass": return 458; + case "thevoida": return 459; + case "thevoidb": return 460; + case "thevoidc": return 461; + case "thevoidd": return 462; + case "thevoide": return 463; + case "thevoidf": return 464; + case "thevoidg": return 465; + case "oceangreenhills": return 466; + case "oceangreenvillag": return 467; + case "oldblackburrow": return 468; + case "bertoxtemple": return 469; + case "discord": return 470; + case "discordtower": return 471; + case "oldbloodfield": return 472; + case "precipiceofwar": return 473; + case "olddranik": return 474; + case "toskirakk": return 475; + case "korascian": return 476; + case "rathechamber": return 477; + case "arttest": return 996; + case "fhalls": return 998; + case "apprentice": return 999; + + } + + return 0; + } + + public static string ZoneNumberToName(UInt32 ZoneNumber) + { + switch (ZoneNumber) + { + case 1: return "qeynos"; + case 2: return "qeynos2"; + case 3: return "qrg"; + case 4: return "qeytoqrg"; + case 5: return "highpass"; + case 6: return "highkeep"; + case 8: return "freportn"; + case 9: return "freportw"; + case 10: return "freporte"; + case 11: return "runnyeye"; + case 12: return "qey2hh1"; + case 13: return "northkarana"; + case 14: return "southkarana"; + case 15: return "eastkarana"; + case 16: return "beholder"; + case 17: return "blackburrow"; + case 18: return "paw"; + case 19: return "rivervale"; + case 20: return "kithicor"; + case 21: return "commons"; + case 22: return "ecommons"; + case 23: return "erudnint"; + case 24: return "erudnext"; + case 25: return "nektulos"; + case 26: return "cshome"; + case 27: return "lavastorm"; + case 28: return "nektropos"; + case 29: return "halas"; + case 30: return "everfrost"; + case 31: return "soldunga"; + case 32: return "soldungb"; + case 33: return "misty"; + case 34: return "nro"; + case 35: return "sro"; + case 36: return "befallen"; + case 37: return "oasis"; + case 38: return "tox"; + case 39: return "hole"; + case 40: return "neriaka"; + case 41: return "neriakb"; + case 42: return "neriakc"; + case 43: return "neriakd"; + case 44: return "najena"; + case 45: return "qcat"; + case 46: return "innothule"; + case 47: return "feerrott"; + case 48: return "cazicthule"; + case 49: return "oggok"; + case 50: return "rathemtn"; + case 51: return "lakerathe"; + case 52: return "grobb"; + case 53: return "aviak"; + case 54: return "gfaydark"; + case 55: return "akanon"; + case 56: return "steamfont"; + case 57: return "lfaydark"; + case 58: return "crushbone"; + case 59: return "mistmoore"; + case 60: return "kaladima"; + case 61: return "felwithea"; + case 62: return "felwitheb"; + case 63: return "unrest"; + case 64: return "kedge"; + case 65: return "guktop"; + case 66: return "gukbottom"; + case 67: return "kaladimb"; + case 68: return "butcher"; + case 69: return "oot"; + case 70: return "cauldron"; + case 71: return "airplane"; + case 72: return "fearplane"; + case 73: return "permafrost"; + case 74: return "kerraridge"; + case 75: return "paineel"; + case 76: return "hateplane"; + case 77: return "arena"; + case 78: return "fieldofbone"; + case 79: return "warslikswood"; + case 80: return "soltemple"; + case 81: return "droga"; + case 82: return "cabwest"; + case 83: return "swampofnohope"; + case 84: return "firiona"; + case 85: return "lakeofillomen"; + case 86: return "dreadlands"; + case 87: return "burningwood"; + case 88: return "kaesora"; + case 89: return "sebilis"; + case 90: return "citymist"; + case 91: return "skyfire"; + case 92: return "frontiermtns"; + case 93: return "overthere"; + case 94: return "emeraldjungle"; + case 95: return "trakanon"; + case 96: return "timorous"; + case 97: return "kurn"; + case 98: return "erudsxing"; + case 100: return "stonebrunt"; + case 101: return "warrens"; + case 102: return "karnor"; + case 103: return "chardok"; + case 104: return "dalnir"; + case 105: return "charasis"; + case 106: return "cabeast"; + case 107: return "nurga"; + case 108: return "veeshan"; + case 109: return "veksar"; + case 110: return "iceclad"; + case 111: return "frozenshadow"; + case 112: return "velketor"; + case 113: return "kael"; + case 114: return "skyshrine"; + case 115: return "thurgadina"; + case 116: return "eastwastes"; + case 117: return "cobaltscar"; + case 118: return "greatdivide"; + case 119: return "wakening"; + case 120: return "westwastes"; + case 121: return "crystal"; + case 123: return "necropolis"; + case 124: return "templeveeshan"; + case 125: return "sirens"; + case 126: return "mischiefplane"; + case 127: return "growthplane"; + case 128: return "sleeper"; + case 129: return "thurgadinb"; + case 130: return "erudsxing2"; + case 150: return "shadowhaven"; + case 151: return "bazaar"; + case 152: return "nexus"; + case 153: return "echo"; + case 154: return "acrylia"; + case 155: return "sharvahl"; + case 156: return "paludal"; + case 157: return "fungusgrove"; + case 158: return "vexthal"; + case 159: return "sseru"; + case 160: return "katta"; + case 161: return "netherbian"; + case 162: return "ssratemple"; + case 163: return "griegsend"; + case 164: return "thedeep"; + case 165: return "shadeweaver"; + case 166: return "hollowshade"; + case 167: return "grimling"; + case 168: return "mseru"; + case 169: return "letalis"; + case 170: return "twilight"; + case 171: return "thegrey"; + case 172: return "tenebrous"; + case 173: return "maiden"; + case 174: return "dawnshroud"; + case 175: return "scarlet"; + case 176: return "umbral"; + case 179: return "akheva"; + case 180: return "arena2"; + case 181: return "jaggedpine"; + case 182: return "nedaria"; + case 183: return "tutorial"; + case 184: return "load"; + case 185: return "load2"; + case 186: return "hateplaneb"; + case 187: return "shadowrest"; + case 188: return "tutoriala"; + case 189: return "tutorialb"; + case 190: return "clz"; + case 200: return "codecay"; + case 201: return "pojustice"; + case 202: return "poknowledge"; + case 203: return "potranquility"; + case 204: return "ponightmare"; + case 205: return "podisease"; + case 206: return "poinnovation"; + case 207: return "potorment"; + case 208: return "povalor"; + case 209: return "bothunder"; + case 210: return "postorms"; + case 211: return "hohonora"; + case 212: return "solrotower"; + case 213: return "powar"; + case 214: return "potactics"; + case 215: return "poair"; + case 216: return "powater"; + case 217: return "pofire"; + case 218: return "poeartha"; + case 219: return "potimea"; + case 220: return "hohonorb"; + case 221: return "nightmareb"; + case 222: return "poearthb"; + case 223: return "potimeb"; + case 224: return "gunthak"; + case 225: return "dulak"; + case 226: return "torgiran"; + case 227: return "nadox"; + case 228: return "hatesfury"; + case 229: return "guka"; + case 230: return "ruja"; + case 231: return "taka"; + case 232: return "mira"; + case 233: return "mmca"; + case 234: return "gukb"; + case 235: return "rujb"; + case 236: return "takb"; + case 237: return "mirb"; + case 238: return "mmcb"; + case 239: return "gukc"; + case 240: return "rujc"; + case 241: return "takc"; + case 242: return "mirc"; + case 243: return "mmcc"; + case 244: return "gukd"; + case 245: return "rujd"; + case 246: return "takd"; + case 247: return "mird"; + case 248: return "mmcd"; + case 249: return "guke"; + case 250: return "ruje"; + case 251: return "take"; + case 252: return "mire"; + case 253: return "mmce"; + case 254: return "gukf"; + case 255: return "rujf"; + case 256: return "takf"; + case 257: return "mirf"; + case 258: return "mmcf"; + case 259: return "gukg"; + case 260: return "rujg"; + case 261: return "takg"; + case 262: return "mirg"; + case 263: return "mmcg"; + case 264: return "gukh"; + case 265: return "rujh"; + case 266: return "takh"; + case 267: return "mirh"; + case 268: return "mmch"; + case 269: return "ruji"; + case 270: return "taki"; + case 271: return "miri"; + case 272: return "mmci"; + case 273: return "rujj"; + case 274: return "takj"; + case 275: return "mirj"; + case 276: return "mmcj"; + case 277: return "chardokb"; + case 278: return "soldungc"; + case 279: return "abysmal"; + case 280: return "natimbi"; + case 281: return "qinimi"; + case 282: return "riwwi"; + case 283: return "barindu"; + case 284: return "ferubi"; + case 285: return "snpool"; + case 286: return "snlair"; + case 287: return "snplant"; + case 288: return "sncrematory"; + case 289: return "tipt"; + case 290: return "vxed"; + case 291: return "yxtta"; + case 292: return "uqua"; + case 293: return "kodtaz"; + case 294: return "ikkinz"; + case 295: return "qvic"; + case 296: return "inktuta"; + case 297: return "txevu"; + case 298: return "tacvi"; + case 299: return "qvicb"; + case 300: return "wallofslaughter"; + case 301: return "bloodfields"; + case 302: return "draniksscar"; + case 303: return "causeway"; + case 304: return "chambersa"; + case 305: return "chambersb"; + case 306: return "chambersc"; + case 307: return "chambersd"; + case 308: return "chamberse"; + case 309: return "chambersf"; + case 316: return "provinggrounds"; + case 317: return "anguish"; + case 318: return "dranikhollowsa"; + case 319: return "dranikhollowsb"; + case 320: return "dranikhollowsc"; + case 321: return "dranikhollowsd"; + case 322: return "dranikhollowse"; + case 323: return "dranikhollowsf"; + case 324: return "dranikhollowsg"; + case 325: return "dranikhollowsh"; + case 326: return "dranikhollowsi"; + case 327: return "dranikhollowsj"; + case 328: return "dranikcatacombsa"; + case 329: return "dranikcatacombsb"; + case 330: return "dranikcatacombsc"; + case 331: return "draniksewersa"; + case 332: return "draniksewersb"; + case 333: return "draniksewersc"; + case 334: return "riftseekers"; + case 335: return "harbingers"; + case 336: return "dranik"; + case 337: return "broodlands"; + case 338: return "stillmoona"; + case 339: return "stillmoonb"; + case 340: return "thundercrest"; + case 341: return "delvea"; + case 342: return "delveb"; + case 343: return "thenest"; + case 344: return "guildlobby"; + case 345: return "guildhall"; + case 346: return "barter"; + case 347: return "illsalin"; + case 348: return "illsalina"; + case 349: return "illsalinb"; + case 350: return "illsalinc"; + case 351: return "dreadspire"; + case 354: return "drachnidhive"; + case 355: return "drachnidhivea"; + case 356: return "drachnidhiveb"; + case 357: return "drachnidhivec"; + case 358: return "westkorlach"; + case 359: return "westkorlacha"; + case 360: return "westkorlachb"; + case 361: return "westkorlachc"; + case 362: return "eastkorlach"; + case 363: return "eastkorlacha"; + case 364: return "shadowspine"; + case 365: return "corathus"; + case 366: return "corathusa"; + case 367: return "corathusb"; + case 368: return "nektulosa"; + case 369: return "arcstone"; + case 370: return "relic"; + case 371: return "skylance"; + case 372: return "devastation"; + case 373: return "devastationa"; + case 374: return "rage"; + case 375: return "ragea"; + case 376: return "takishruins"; + case 377: return "takishruinsa"; + case 378: return "elddar"; + case 379: return "elddara"; + case 380: return "theater"; + case 381: return "theatera"; + case 382: return "freeporteast"; + case 383: return "freeportwest"; + case 384: return "freeportsewers"; + case 385: return "freeportacademy"; + case 386: return "freeporttemple"; + case 387: return "freeportmilitia"; + case 388: return "freeportarena"; + case 389: return "freeportcityhall"; + case 390: return "freeporttheater"; + case 391: return "freeporthall"; + case 392: return "northro"; + case 393: return "southro"; + case 394: return "crescent"; + case 395: return "moors"; + case 396: return "stonehive"; + case 397: return "mesa"; + case 398: return "roost"; + case 399: return "steppes"; + case 400: return "icefall"; + case 401: return "valdeholm"; + case 402: return "frostcrypt"; + case 403: return "sunderock"; + case 404: return "vergalid"; + case 405: return "direwind"; + case 406: return "ashengate"; + case 407: return "highpasshold"; + case 408: return "commonlands"; + case 409: return "oceanoftears"; + case 410: return "kithforest"; + case 411: return "befallenb"; + case 412: return "highpasskeep"; + case 413: return "innothuleb"; + case 414: return "toxxulia"; + case 415: return "mistythicket"; + case 416: return "kattacastrum"; + case 417: return "thalassius"; + case 418: return "atiiki"; + case 419: return "zhisza"; + case 420: return "silyssar"; + case 421: return "solteris"; + case 422: return "barren"; + case 423: return "buriedsea"; + case 424: return "jardelshook"; + case 425: return "monkeyrock"; + case 426: return "suncrest"; + case 427: return "deadbone"; + case 428: return "blacksail"; + case 429: return "maidensgrave"; + case 430: return "redfeather"; + case 431: return "shipmvp"; + case 432: return "shipmvu"; + case 433: return "shippvu"; + case 434: return "shipuvu"; + case 435: return "shipmvm"; + case 436: return "mechanotus"; + case 437: return "mansion"; + case 438: return "steamfactory"; + case 439: return "shipworkshop"; + case 440: return "gyrospireb"; + case 441: return "gyrospirez"; + case 442: return "dragonscale"; + case 443: return "lopingplains"; + case 444: return "hillsofshade"; + case 445: return "bloodmoon"; + case 446: return "crystallos"; + case 447: return "guardian"; + case 448: return "steamfontmts"; + case 449: return "cryptofshade"; + case 451: return "dragonscaleb"; + case 452: return "oldfieldofbone"; + case 453: return "oldkaesoraa"; + case 454: return "oldkaesorab"; + case 455: return "oldkurn"; + case 456: return "oldkithicor"; + case 457: return "oldcommons"; + case 458: return "oldhighpass"; + case 459: return "thevoida"; + case 460: return "thevoidb"; + case 461: return "thevoidc"; + case 462: return "thevoidd"; + case 463: return "thevoide"; + case 464: return "thevoidf"; + case 465: return "thevoidg"; + case 466: return "oceangreenhills"; + case 467: return "oceangreenvillag"; + case 468: return "oldblackburrow"; + case 469: return "bertoxtemple"; + case 470: return "discord"; + case 471: return "discordtower"; + case 472: return "oldbloodfield"; + case 473: return "precipiceofwar"; + case 474: return "olddranik"; + case 475: return "toskirakk"; + case 476: return "korascian"; + case 477: return "rathechamber"; + case 996: return "arttest"; + case 998: return "fhalls"; + case 999: return "apprentice"; + } + return "UNKNOWNZONE"; + } + + public bool SupportsSQLGeneration() + { + if (PatchDecoder.SupportsSQLGeneration) + return true; + + return false; + } + } +} diff --git a/utils/EQExtractor2/EQExtractor2/EQExtractor2.csproj b/utils/EQExtractor2/EQExtractor2/EQExtractor2.csproj new file mode 100644 index 000000000..8266329f1 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/EQExtractor2.csproj @@ -0,0 +1,217 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {621904C6-9028-439C-A0E6-1F856C88D238} + WinExe + Properties + EQExtractor2 + EQExtractor2 + v4.0 + + + 512 + + + x86 + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + .\PacketDotNet.dll + + + .\SharpPcap.dll + + + + + + + + + + + + + .\zlib.net.dll + + + + + Form + + + GenerateSQLForm.cs + + + Form + + + LogForm.cs + + + + + + + + + + + + + Form + + + EQExtractor2Form1.cs + + + + + + + + + + + + + + + + + + + + + + Form + + + UserOptions.cs + + + + EQExtractor2Form1.cs + + + GenerateSQLForm.cs + + + LogForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + UserOptions.cs + + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.Designer.cs b/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.Designer.cs new file mode 100644 index 000000000..2c5b005f4 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.Designer.cs @@ -0,0 +1,280 @@ +namespace EQExtractor2 +{ + partial class EQExtractor2Form1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.InputFileOpenDialog = new System.Windows.Forms.OpenFileDialog(); + this.PacketDumpFileDialog = new System.Windows.Forms.SaveFileDialog(); + this.ProgressBar = new System.Windows.Forms.ProgressBar(); + this.mainMenu1 = new System.Windows.Forms.MainMenu(this.components); + this.menuFile = new System.Windows.Forms.MenuItem(); + this.menuLoadPCAP = new System.Windows.Forms.MenuItem(); + this.menuGenerateSQL = new System.Windows.Forms.MenuItem(); + this.menuPacketDump = new System.Windows.Forms.MenuItem(); + this.menuDumpAAs = new System.Windows.Forms.MenuItem(); + this.menuViewPackets = new System.Windows.Forms.MenuItem(); + this.menuExit = new System.Windows.Forms.MenuItem(); + this.menuView = new System.Windows.Forms.MenuItem(); + this.menuViewDebugLog = new System.Windows.Forms.MenuItem(); + this.menuOptions = new System.Windows.Forms.MenuItem(); + this.label1 = new System.Windows.Forms.Label(); + this.ClientVersionLabel = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.PacketCountLabel = new System.Windows.Forms.Label(); + this.ZoneLabelText = new System.Windows.Forms.Label(); + this.ZoneLabel = new System.Windows.Forms.Label(); + this.statusStrip1 = new System.Windows.Forms.StatusStrip(); + this.StatusBar = new System.Windows.Forms.ToolStripStatusLabel(); + this.statusStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // InputFileOpenDialog + // + this.InputFileOpenDialog.Filter = "Capture Files (*.pcap)|*.pcap|All files (*.*)|*.*"; + // + // PacketDumpFileDialog + // + this.PacketDumpFileDialog.Filter = "Text Files (*.txt)|*.txt|All files (*.*)|*.*"; + this.PacketDumpFileDialog.Title = "Dump Packets To File"; + // + // ProgressBar + // + this.ProgressBar.Location = new System.Drawing.Point(2, 78); + this.ProgressBar.Name = "ProgressBar"; + this.ProgressBar.Size = new System.Drawing.Size(627, 23); + this.ProgressBar.TabIndex = 35; + this.ProgressBar.Visible = false; + // + // mainMenu1 + // + this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuFile, + this.menuView}); + // + // menuFile + // + this.menuFile.Index = 0; + this.menuFile.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuLoadPCAP, + this.menuGenerateSQL, + this.menuPacketDump, + this.menuDumpAAs, + this.menuViewPackets, + this.menuExit}); + this.menuFile.Text = "File"; + // + // menuLoadPCAP + // + this.menuLoadPCAP.Index = 0; + this.menuLoadPCAP.Shortcut = System.Windows.Forms.Shortcut.CtrlL; + this.menuLoadPCAP.Text = "Load PCAP"; + this.menuLoadPCAP.Click += new System.EventHandler(this.menuLoadPCAP_Click); + // + // menuGenerateSQL + // + this.menuGenerateSQL.Enabled = false; + this.menuGenerateSQL.Index = 1; + this.menuGenerateSQL.Shortcut = System.Windows.Forms.Shortcut.CtrlS; + this.menuGenerateSQL.Text = "Generate SQL"; + this.menuGenerateSQL.Click += new System.EventHandler(this.menuGenerateSQL_Click); + // + // menuPacketDump + // + this.menuPacketDump.Enabled = false; + this.menuPacketDump.Index = 2; + this.menuPacketDump.Shortcut = System.Windows.Forms.Shortcut.CtrlP; + this.menuPacketDump.Text = "Packet Dump"; + this.menuPacketDump.Click += new System.EventHandler(this.menuPacketDump_Click); + // + // menuDumpAAs + // + this.menuDumpAAs.Enabled = false; + this.menuDumpAAs.Index = 3; + this.menuDumpAAs.Shortcut = System.Windows.Forms.Shortcut.CtrlA; + this.menuDumpAAs.Text = "Dump AAs"; + this.menuDumpAAs.Click += new System.EventHandler(this.menuDumpAAs_Click); + // + // menuViewPackets + // + this.menuViewPackets.Enabled = false; + this.menuViewPackets.Index = 4; + this.menuViewPackets.Shortcut = System.Windows.Forms.Shortcut.CtrlV; + this.menuViewPackets.Text = "View Packets"; + this.menuViewPackets.Click += new System.EventHandler(this.menuViewPackets_Click); + // + // menuExit + // + this.menuExit.Index = 5; + this.menuExit.Text = "Exit"; + this.menuExit.Click += new System.EventHandler(this.menuExit_Click); + // + // menuView + // + this.menuView.Index = 1; + this.menuView.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.menuViewDebugLog, + this.menuOptions}); + this.menuView.Text = "View"; + this.menuView.Popup += new System.EventHandler(this.menuView_Popup); + // + // menuViewDebugLog + // + this.menuViewDebugLog.Index = 0; + this.menuViewDebugLog.Shortcut = System.Windows.Forms.Shortcut.CtrlD; + this.menuViewDebugLog.Text = "Debug Log"; + this.menuViewDebugLog.Click += new System.EventHandler(this.menuViewDebugLog_Click); + // + // menuOptions + // + this.menuOptions.Index = 1; + this.menuOptions.Shortcut = System.Windows.Forms.Shortcut.CtrlO; + this.menuOptions.Text = "Options"; + this.menuOptions.Click += new System.EventHandler(this.menuOptions_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(92, 13); + this.label1.TabIndex = 36; + this.label1.Text = "EQ Client Version:"; + // + // ClientVersionLabel + // + this.ClientVersionLabel.AutoSize = true; + this.ClientVersionLabel.Location = new System.Drawing.Point(116, 9); + this.ClientVersionLabel.Name = "ClientVersionLabel"; + this.ClientVersionLabel.Size = new System.Drawing.Size(166, 13); + this.ClientVersionLabel.TabIndex = 37; + this.ClientVersionLabel.Text = ".PCAP file not loaded. Press Ctrl-L"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(55, 37); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(49, 13); + this.label2.TabIndex = 38; + this.label2.Text = "Packets:"; + // + // PacketCountLabel + // + this.PacketCountLabel.AutoSize = true; + this.PacketCountLabel.Location = new System.Drawing.Point(116, 37); + this.PacketCountLabel.Name = "PacketCountLabel"; + this.PacketCountLabel.Size = new System.Drawing.Size(145, 13); + this.PacketCountLabel.TabIndex = 39; + this.PacketCountLabel.Text = " "; + // + // ZoneLabelText + // + this.ZoneLabelText.AutoSize = true; + this.ZoneLabelText.Location = new System.Drawing.Point(72, 62); + this.ZoneLabelText.Name = "ZoneLabelText"; + this.ZoneLabelText.Size = new System.Drawing.Size(32, 13); + this.ZoneLabelText.TabIndex = 40; + this.ZoneLabelText.Text = "Zone"; + // + // ZoneLabel + // + this.ZoneLabel.AutoSize = true; + this.ZoneLabel.Location = new System.Drawing.Point(116, 62); + this.ZoneLabel.Name = "ZoneLabel"; + this.ZoneLabel.Size = new System.Drawing.Size(145, 13); + this.ZoneLabel.TabIndex = 41; + this.ZoneLabel.Text = " "; + // + // statusStrip1 + // + this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.StatusBar}); + this.statusStrip1.Location = new System.Drawing.Point(0, 104); + this.statusStrip1.Name = "statusStrip1"; + this.statusStrip1.Size = new System.Drawing.Size(633, 22); + this.statusStrip1.TabIndex = 42; + this.statusStrip1.Text = "statusStrip1"; + // + // StatusBar + // + this.StatusBar.Name = "StatusBar"; + this.StatusBar.Size = new System.Drawing.Size(209, 17); + this.StatusBar.Text = "Ready. Press Ctrl-L to load a .PCAP file"; + // + // EQExtractor2Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(633, 126); + this.Controls.Add(this.statusStrip1); + this.Controls.Add(this.ZoneLabel); + this.Controls.Add(this.ZoneLabelText); + this.Controls.Add(this.PacketCountLabel); + this.Controls.Add(this.label2); + this.Controls.Add(this.ClientVersionLabel); + this.Controls.Add(this.label1); + this.Controls.Add(this.ProgressBar); + this.Menu = this.mainMenu1; + this.Name = "EQExtractor2Form1"; + this.Text = "EQExtractor2"; + this.Load += new System.EventHandler(this.EQExtractor2Form1_Load); + this.statusStrip1.ResumeLayout(false); + this.statusStrip1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.OpenFileDialog InputFileOpenDialog; + private System.Windows.Forms.SaveFileDialog PacketDumpFileDialog; + private System.Windows.Forms.ProgressBar ProgressBar; + private System.Windows.Forms.MainMenu mainMenu1; + private System.Windows.Forms.MenuItem menuFile; + private System.Windows.Forms.MenuItem menuLoadPCAP; + private System.Windows.Forms.MenuItem menuGenerateSQL; + private System.Windows.Forms.MenuItem menuPacketDump; + private System.Windows.Forms.MenuItem menuDumpAAs; + private System.Windows.Forms.MenuItem menuExit; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label ClientVersionLabel; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label PacketCountLabel; + private System.Windows.Forms.Label ZoneLabelText; + private System.Windows.Forms.Label ZoneLabel; + private System.Windows.Forms.MenuItem menuView; + private System.Windows.Forms.MenuItem menuViewDebugLog; + private System.Windows.Forms.StatusStrip statusStrip1; + private System.Windows.Forms.ToolStripStatusLabel StatusBar; + private System.Windows.Forms.MenuItem menuViewPackets; + private System.Windows.Forms.MenuItem menuOptions; + } +} + diff --git a/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs b/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs new file mode 100644 index 000000000..33f6d9645 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.cs @@ -0,0 +1,559 @@ +// +// Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net). Distributed under GPL version 2. +// +// + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using System.IO; +using SharpPcap; +using EQApplicationLayer; + +namespace EQExtractor2 +{ + public partial class EQExtractor2Form1 : Form + { + string Version = "EQExtractor2 Version 2.6.0 SVN"; + + static int PacketsSeen = 0; + static long BytesRead = 0; + static long CaptureFileSize = 0; + string ZoneName; + string SpawnNameFilter = ""; + bool CoalesceWaypoints = true; + + GenerateSQLForm SQLForm = new GenerateSQLForm(); + LogForm DebugLog = new LogForm(); + UserOptions Options = new UserOptions(); + + StreamWriter SQLStream; + StreamWriter PacketDebugStream; + + EQStreamProcessor StreamProcessor; + + public EQExtractor2Form1() + { + InitializeComponent(); + DisplayUsageInfo(); + + Options.PacketDumpViewerProgram.Text = Properties.Settings.Default.TextFileViewer; + Options.ShowDebugWindowOnStartup.Checked = Properties.Settings.Default.ShowDebugWindowOnStartup; + Options.ShowTimeStamps.Checked = Properties.Settings.Default.DumpTimeStamps; + + } + + public void Log(string Message) + { + DebugLog.ConsoleWindow.Items.Add(Message); + DebugLog.ConsoleWindow.SelectedIndex = DebugLog.ConsoleWindow.Items.Count - 1; + Application.DoEvents(); + } + + private void device_OnPacketArrival(object sender, SharpPcap.CaptureEventArgs e) + { + if (e.Packet.LinkLayerType == PacketDotNet.LinkLayers.Ethernet) + { + PacketDotNet.Packet packet; + + long TotalPacketSize = e.Packet.Data.Length; + BytesRead += TotalPacketSize; + ++PacketsSeen; + + if ((PacketsSeen > 0) && ((PacketsSeen % 10000) == 0)) + { + DebugLog.ConsoleWindow.SelectedIndex = DebugLog.ConsoleWindow.Items.Count - 1; + int Progress = (int)((float)BytesRead / (float)CaptureFileSize * 100); + ProgressBar.Value = Progress; + + Application.DoEvents(); + } + + try + { + packet = PacketDotNet.Packet.ParsePacket(e.Packet); + } + catch + { + return; + } + + var ethernetPacket = (PacketDotNet.EthernetPacket)packet; + + var udpPacket = PacketDotNet.UdpPacket.GetEncapsulated(packet); + + if (udpPacket != null) + { + var ipPacket = (PacketDotNet.IpPacket)udpPacket.ParentPacket; + System.Net.IPAddress srcIp = ipPacket.SourceAddress; + System.Net.IPAddress dstIp = ipPacket.DestinationAddress; + + byte[] Payload = udpPacket.PayloadData; + + Int32 l = udpPacket.Length - udpPacket.Header.GetLength(0); + + if (l > 0) + { + Array.Resize(ref Payload, l); + + StreamProcessor.ProcessPacket(srcIp, dstIp, udpPacket.SourcePort, udpPacket.DestinationPort, Payload, packet.Timeval.Date); + } + } + } + } + + public void WriteSQL(string Message) + { + SQLStream.WriteLine(Message); + } + + public void PacketDebugLogger(string Message) + { + PacketDebugStream.WriteLine(Message); + } + + private void DisableAllControls() + { + foreach (Control c in this.Controls) + { + if ((c is Button) || (c is TextBox) || (c is MaskedTextBox) || (c is CheckBox)) + c.Enabled = false; + } + + } + + private void EnableAllControls() + { + foreach (Control c in this.Controls) + c.Enabled = true; + + menuGenerateSQL.Enabled = StreamProcessor.StreamRecognised() && StreamProcessor.SupportsSQLGeneration(); + menuDumpAAs.Enabled = StreamProcessor.StreamRecognised(); + } + + private void menuLoadPCAP_Click(object sender, EventArgs e) + { + if (InputFileOpenDialog.ShowDialog() != DialogResult.OK) + return; + + menuGenerateSQL.Enabled = false; + menuPacketDump.Enabled = false; + menuViewPackets.Enabled = false; + menuDumpAAs.Enabled = false; + + SharpPcap.OfflinePcapDevice device; + + try + { + string CapFile = InputFileOpenDialog.FileName; + + device = new SharpPcap.OfflinePcapDevice(CapFile); + + device.Open(); + } + catch + { + StatusBar.Text = "Error: File does not exist or not in .pcap format."; + Log("Error: File does not exist or not in .pcap format."); + return; + } + + StreamProcessor = new EQStreamProcessor(); + + if (!StreamProcessor.Init(Application.StartupPath, this.Log)) + { + Log("Fatal error initialising Stream Processor. No decoders could be initialised (mostly likely misplaced patch_XXXX.conf files."); + StatusBar.Text = "Fatal error initialising Stream Processor. No decoders could be initialised (mostly likely misplaced patch_XXXX.conf files."; + return; + } + + if (Options.EQPacketDebugFilename.Text.Length > 0) + { + try + { + PacketDebugStream = new StreamWriter(Options.EQPacketDebugFilename.Text); + StreamProcessor.Packets.SetDebugLogHandler(PacketDebugLogger); + } + catch + { + Log("Failed to open netcode debug file for writing."); + Options.EQPacketDebugFilename.Text = ""; + StreamProcessor.Packets.SetDebugLogHandler(null); + } + } + else + StreamProcessor.Packets.SetDebugLogHandler(null); + + StatusBar.Text = "Reading packets from " + InputFileOpenDialog.FileName + ". Please wait..."; + + device.OnPacketArrival += + new PacketArrivalEventHandler(device_OnPacketArrival); + + BytesRead = 0; + PacketsSeen = 0; + + DebugLog.ConsoleWindow.Items.Add("-- Capturing from '" + InputFileOpenDialog.FileName); + ProgressBar.Value = 0; + ProgressBar.Show(); + + menuFile.Enabled = false; + + CaptureFileSize = device.FileSize; + + device.Capture(); + + device.Close(); + + Log("End of file reached. Processed " + PacketsSeen + " packets and " + BytesRead + " bytes."); + + ProgressBar.Hide(); + + if (Options.EQPacketDebugFilename.Text.Length > 0) + PacketDebugStream.Close(); + + PacketCountLabel.Text = PacketsSeen.ToString(); + if (StreamProcessor.Packets.ErrorsInStream) + Log("There were errors encountered in the packet stream. Data may be incomplete."); + + DebugLog.ConsoleWindow.SelectedIndex = DebugLog.ConsoleWindow.Items.Count - 1; + + menuFile.Enabled = true; + + StreamProcessor.PCAPFileReadFinished(); + + menuPacketDump.Enabled = true; + + menuViewPackets.Enabled = true; + + Log("Stream recognised as " + StreamProcessor.GetDecoderVersion()); + + int PPLength = StreamProcessor.VerifyPlayerProfile(); + + ClientVersionLabel.Text = StreamProcessor.GetDecoderVersion(); + + if (PPLength == 0) + { + Log("Unable to find player profile packet, or packet not of correct size."); + menuDumpAAs.Enabled = false; + menuGenerateSQL.Enabled = false; + ClientVersionLabel.ForeColor = Color.Red; + ZoneLabel.Text = ""; + PacketCountLabel.Text = ""; + StatusBar.Text = "Unrecognised EQ Client Version. Press Ctrl-P to dump, or Ctrl-V to view packets."; + return; + } + else + { + ClientVersionLabel.ForeColor = Color.Green; + Log("Found player profile packet of the expected length (" + PPLength + ")."); + + if(StreamProcessor.SupportsSQLGeneration()) + StatusBar.Text = "Client version recognised. Press Ctrl-S to Generate SQL"; + else + StatusBar.Text = "Client version recognised. *SQL GENERATION NOT SUPPORTED FOR THIS CLIENT*"; + } + + ZoneName = StreamProcessor.GetZoneName(); + + UInt32 ZoneNumber = StreamProcessor.GetZoneNumber(); + + Log("Zonename is " + StreamProcessor.GetZoneName()); + + Log("Zone number is " + ZoneNumber); + + ZoneLabel.Text = StreamProcessor.GetZoneLongName() + " [" + StreamProcessor.GetZoneName() + "] (" + ZoneNumber.ToString() + ")"; + + SQLForm.ZoneIDTextBox.Text = ZoneNumber.ToString(); + SQLForm.ZoneIDTextBox.Enabled = true; + SQLForm.DoorsTextBox.Enabled = true; + SQLForm.NPCTypesTextBox.Enabled = true; + SQLForm.SpawnEntryTextBox.Enabled = true; + SQLForm.SpawnGroupTextBox.Enabled = true; + SQLForm.Spawn2TextBox.Enabled = true; + SQLForm.GridTextBox.Enabled = true; + SQLForm.ObjectTextBox.Enabled = true; + SQLForm.GroundSpawnTextBox.Enabled = true; + SQLForm.MerchantTextBox.Enabled = true; + SQLForm.VersionSelector.Enabled = true; + menuGenerateSQL.Enabled = StreamProcessor.SupportsSQLGeneration(); + menuPacketDump.Enabled = true; + menuViewPackets.Enabled = true; + menuDumpAAs.Enabled = true; + + SQLForm.RecalculateBaseInsertIDs(); + + StreamProcessor.GenerateZonePointList(); + } + + private void menuGenerateSQL_Click(object sender, EventArgs e) + { + if (SQLForm.ShowDialog() != DialogResult.OK) + return; + + string SQLFile = SQLForm.FileName; + + try + { + SQLStream = new StreamWriter(SQLFile); + } + catch + { + Log("Unable to open file " + SQLFile + " for writing."); + StatusBar.Text = "Unable to open file " + SQLFile + " for writing."; + return; + } + + UInt32 SpawnDBID = Convert.ToUInt32(SQLForm.NPCTypesTextBox.Text); + UInt32 SpawnGroupID = Convert.ToUInt32(SQLForm.SpawnGroupTextBox.Text); + UInt32 SpawnEntryID = Convert.ToUInt32(SQLForm.SpawnEntryTextBox.Text); + UInt32 Spawn2ID = Convert.ToUInt32(SQLForm.Spawn2TextBox.Text); + UInt32 GridDBID = Convert.ToUInt32(SQLForm.GridTextBox.Text); + UInt32 MerchantDBID = Convert.ToUInt32(SQLForm.MerchantTextBox.Text); + int DoorDBID = Convert.ToInt32(SQLForm.DoorsTextBox.Text); + UInt32 GroundSpawnDBID = Convert.ToUInt32(SQLForm.GroundSpawnTextBox.Text); + UInt32 ObjectDBID = Convert.ToUInt32(SQLForm.ObjectTextBox.Text); + + UInt32 ZoneID = Convert.ToUInt32(SQLForm.ZoneIDTextBox.Text); + + SpawnNameFilter = SQLForm.SpawnNameFilter.Text; + CoalesceWaypoints = SQLForm.CoalesceWaypoints.Checked; + + WriteSQL("-- SQL created by " + Version); + WriteSQL("--"); + WriteSQL("-- Using Decoder: " + StreamProcessor.GetDecoderVersion()); + WriteSQL("--"); + WriteSQL("-- Packets captured on " + StreamProcessor.GetCaptureStartTime().ToString()); + WriteSQL("--"); + WriteSQL("-- Change these variables if required"); + WriteSQL("--"); + WriteSQL("set @StartingNPCTypeID = " + SpawnDBID + ";"); + WriteSQL("set @StartingSpawnGroupID = " + SpawnGroupID + ";"); + WriteSQL("set @StartingSpawnEntryID = " + SpawnEntryID + ";"); + WriteSQL("set @StartingSpawn2ID = " + Spawn2ID + ";"); + WriteSQL("set @StartingGridID = " + GridDBID + ";"); + WriteSQL("set @StartingMerchantID = " + MerchantDBID + ";"); + WriteSQL("set @BaseDoorID = " + DoorDBID + ";"); + WriteSQL("set @StartingGroundSpawnID = " + GroundSpawnDBID + ";"); + WriteSQL("set @StartingObjectID = " + ObjectDBID + ";"); + WriteSQL("--"); + WriteSQL("--"); + + if (SQLForm.ZoneCheckBox.Checked) + StreamProcessor.GenerateZoneSQL(this.WriteSQL); + + if (SQLForm.ZonePointCheckBox.Checked) + StreamProcessor.GenerateZonePointSQL(ZoneName, this.WriteSQL); + + UInt32 SpawnVersion = (UInt32)SQLForm.VersionSelector.Value; + + if (SQLForm.DoorCheckBox.Checked) + { + Log("Starting to generate SQL for Doors."); + StreamProcessor.GenerateDoorsSQL(ZoneName, DoorDBID, SpawnVersion, this.WriteSQL); + Log("Finished generating SQL for Doors."); + } + + Log("Starting to generate SQL for Spawns and/or Grids."); + + StreamProcessor.GenerateSpawnSQL(SQLForm.SpawnCheckBox.Checked, SQLForm.GridCheckBox.Checked, SQLForm.MerchantCheckBox.Checked, ZoneName, ZoneID, SpawnVersion, SQLForm.UpdateExistingNPCTypesCheckbox.Checked, SQLForm.NPCTypesTintCheckBox.Checked, SpawnNameFilter, CoalesceWaypoints, SQLForm.InvisibleMenCheckBox.Checked, this.WriteSQL); + + Log("Finished generating SQL for Spawns and/or Grids."); + + if (SQLForm.GroundSpawnCheckBox.Checked || SQLForm.ObjectCheckBox.Checked) + { + Log("Starting to generate SQL for Ground Spawns and/or Objects."); + + StreamProcessor.GenerateObjectSQL(SQLForm.GroundSpawnCheckBox.Checked, SQLForm.ObjectCheckBox.Checked, SpawnVersion, this.WriteSQL); + + Log("Finished generating SQL for Ground Spawns and/or Objects."); + } + + StatusBar.Text = "SQL written to " + SQLFile; + SQLStream.Close(); + } + + private void menuPacketDump_Click(object sender, EventArgs e) + { + if (PacketDumpFileDialog.ShowDialog() == DialogResult.OK) + { + StatusBar.Text = "Packet dump in progress. Please wait..."; + Log("Packets dump in progress..."); + + DisableAllControls(); + + Application.DoEvents(); + + if (StreamProcessor.DumpPackets(PacketDumpFileDialog.FileName, Properties.Settings.Default.DumpTimeStamps)) + { + StatusBar.Text = "Packets dumped successfully."; + Log("Packets dumped successfully."); + } + else + { + StatusBar.Text = "Packet dump failed."; + Log("Packet dump failed."); + } + + EnableAllControls(); + } + } + + private void menuDumpAAs_Click(object sender, EventArgs e) + { + if (PacketDumpFileDialog.ShowDialog() == DialogResult.OK) + { + Log("AA dump in progress..."); + + DisableAllControls(); + + if (StreamProcessor.DumpAAs(PacketDumpFileDialog.FileName)) + { + StatusBar.Text = "AAs dumped successfully."; + Log("AAs dumped successfully."); + } + else + { + StatusBar.Text = "AA dumped failed."; + Log("AA dump failed."); + } + EnableAllControls(); + } + } + + private void menuExit_Click(object sender, EventArgs e) + { + Close(); + } + + private void menuViewDebugLog_Click(object sender, EventArgs e) + { + menuViewDebugLog.Checked = DebugLog.Visible; + + if (!menuViewDebugLog.Checked) + { + menuViewDebugLog.Checked = true; + ShowDebugLog(); + + } + else + { + menuViewDebugLog.Checked = false; + DebugLog.Hide(); + } + } + + private void menuViewPackets_Click(object sender, EventArgs e) + { + DisableAllControls(); + + Application.DoEvents(); + + string TextFileViewer = Properties.Settings.Default.TextFileViewer; + + string TempFileName = Path.GetTempFileName(); + + if (StreamProcessor.DumpPackets(TempFileName, Properties.Settings.Default.DumpTimeStamps)) + { + try + { + System.Diagnostics.Process.Start(TextFileViewer, TempFileName); + } + catch + { + StatusBar.Text = "Unable to launch " + TextFileViewer; + } + } + else + { + StatusBar.Text = "Unexpected error while generating temporary file."; + } + EnableAllControls(); + } + + private void EQExtractor2Form1_Load(object sender, EventArgs e) + { + if (Properties.Settings.Default.ShowDebugWindowOnStartup) + { + ShowDebugLog(); + + } + } + + private void DisplayUsageInfo() + { +#if DEBUG + Version += " (Debug Build)"; +#else + Version += " (Release Build)"; +#endif + Text = Version; + + Log(Version + " Initialised."); + + Log(""); + Log("Instructions:"); + Log("Generate a .pcap file using Wireshark. To do this, park a character in the zone you want to collect in."); + Log("Camp to character select. Start Wireshark capturing. Zone your character in and just sit around for a"); + Log("while, or go and inspect merchant inventories if you want to collect those. When finished, stop the"); + Log("Wireshark capture and save it (File/Save As)."); + Log(""); + Log("Load the .pcap file into this program by pressing Ctrl-L."); + Log("To generate SQL, press Ctrl-S and select the check boxes and set the starting SQL INSERT IDs as required."); + Log("Review the generated SQL before sourcing as DELETEs are auto-generated."); + Log("Press Ctrl-V to view packets, or Ctrl-D to dump them to a text file."); + Log(""); + } + + private void menuOptions_Click(object sender, EventArgs e) + { + DialogResult d = Options.ShowDialog(); + + if (d == DialogResult.OK) + { + Properties.Settings.Default.TextFileViewer = Options.PacketDumpViewerProgram.Text; + Properties.Settings.Default.ShowDebugWindowOnStartup = Options.ShowDebugWindowOnStartup.Checked; + Properties.Settings.Default.DumpTimeStamps = Options.ShowTimeStamps.Checked; + Properties.Settings.Default.Save(); + + if (Properties.Settings.Default.ShowDebugWindowOnStartup) + { + if (!DebugLog.Visible) + ShowDebugLog(); + } + else + { + if (DebugLog.Visible) + { + DebugLog.Hide(); + menuViewDebugLog.Checked = false; + } + } + } + else + { + Options.PacketDumpViewerProgram.Text = Properties.Settings.Default.TextFileViewer; + Options.ShowDebugWindowOnStartup.Checked = Properties.Settings.Default.ShowDebugWindowOnStartup; + Options.ShowTimeStamps.Checked = Properties.Settings.Default.DumpTimeStamps; + } + } + + private void ShowDebugLog() + { + DebugLog.Left = this.Location.X; + DebugLog.Top = this.Location.Y + this.Height; + DebugLog.Show(); + menuViewDebugLog.Checked = true; + this.Focus(); + } + + private void menuView_Popup(object sender, EventArgs e) + { + menuViewDebugLog.Checked = DebugLog.Visible; + } + } +} + diff --git a/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.resx b/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.resx new file mode 100644 index 000000000..bba201935 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/EQExtractor2Form1.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 308, 17 + + + 580, 17 + + + 697, 17 + + \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/EQPacket.cs b/utils/EQExtractor2/EQExtractor2/EQPacket.cs new file mode 100644 index 000000000..775d42de9 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/EQPacket.cs @@ -0,0 +1,902 @@ +// +// Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net). Distributed under GPL version 2. +// +// +// This is the netcode for turning SOE protocol packets into EQ Application Packets +// +using System; +using System.Text; +using System.IO; +using System.Collections.Generic; +using zlib; +using MyUtils; +using EQExtractor2.Patches; +using EQExtractor2.OpCodes; +using EQApplicationLayer; + +namespace EQPacket +{ + public enum PacketDirection {ClientToServer, ServerToClient, Unknown }; + + public class EQApplicationPacket + { + public EQApplicationPacket(System.Net.IPAddress srcIp, System.Net.IPAddress dstIp, ushort srcPort, ushort dstPort, int OpCode, int BufferSize, byte[] Source, int Offset, PacketDirection Direction, DateTime PacketTime) + { + this.OpCode = OpCode; + + this.Direction = Direction; + + this.PacketTime = PacketTime; + + if (BufferSize < 0) + { + //Log("OPCode " + OpCode.ToString("x") + ", Buffer Size " + BufferSize + " Buffer Size < 0 !"); + return; + } + + Buffer = new byte[BufferSize]; + + Array.Copy(Source, Offset, Buffer, 0, BufferSize); + } + + public byte[] Buffer; + public int OpCode; + public PacketDirection Direction; + public DateTime PacketTime; + public bool Locked = false; + } + + public class PacketManager + { + bool DEBUG = false; + bool DUMPPACKETS = false; + + private void Debug(string Message) + { + if (DebugLogger != null) + DebugLogger(Message); + } + + public bool ErrorsInStream = false; + + const UInt32 OP_SessionRequest = 0x0001; + const UInt32 OP_SessionResponse = 0x0002; + const UInt32 OP_Combined = 0x0003; + const UInt32 OP_Packet = 0x0009; + const UInt32 OP_Fragment = 0x000d; + const UInt32 OP_OutOfOrderAck = 0x0011; + const UInt32 OP_Ack = 0x0015; + + private bool PermaLocked = false; + private bool Identified = false; + + private int[] FragmentSeq = {-1, -1}; + private int[] FragmentedPacketSize = {0, 0}; + private int[] FragmentedBytesCollected = {0, 0}; + private byte[][] Fragments = new byte [2][]; + + + private System.Net.IPAddress ServerIP; + private ushort ServerPort = 0; + private int ExpectedServerSEQ = 0; + + private System.Net.IPAddress ClientIP; + private ushort ClientPort = 0; + private int ExpectedClientSEQ = 0; + + private ushort CryptoFlag = 0; + + StreamIdentifier Identifier = null; + + public DateTime LastPacketTime = DateTime.Now; + + public List PacketList = new List(); + + LogHandler Logger = null; + LogHandler DebugLogger = null; + + public void SetLogHandler(LogHandler Logger) + { + this.Logger = Logger; + } + + public void SetDebugLogHandler(LogHandler Logger) + { + this.DebugLogger = Logger; + DEBUG = (Logger != null); + DUMPPACKETS = (Logger != null); + } + + void Log(string Message) + { + if(Logger != null) + Logger(Message); + } + + public delegate IdentificationStatus StreamIdentifier(int OPCode, int Size, PacketDirection Direction); + + public void SetVersionIdentifierMethod(StreamIdentifier Identifier) + { + this.Identifier = Identifier; + } + + public void AddPacket(EQApplicationPacket Packet) + { + #pragma warning disable 0162 + + if (DUMPPACKETS) + { + Debug("[OPCode: 0x" + Packet.OpCode.ToString("x") + "] [Size: " + Packet.Buffer.Length + "]"); + Debug(Utils.HexDump(Packet.Buffer)); + } + + #pragma warning restore 0162 + + Packet.Locked = PermaLocked; + + PacketList.Add(Packet); + } + + private struct CacheEntry + { + public int Seq; + public PacketDirection Direction; + public byte[] Payload; + public DateTime PacketTime; + public bool SubPacket; + + public CacheEntry(int inSeq, PacketDirection inDirection, byte[] inPayload, DateTime inPacketTime, bool inSubPacket) + { + Seq = inSeq; + Direction = inDirection; + Payload = inPayload; + PacketTime = inPacketTime; + SubPacket = inSubPacket; + } + } + + private List Cache = new List(); + + private void AddToCache(int Seq, PacketDirection Direction, byte[] Payload, DateTime PacketTime, bool SubPacket) + { + if(DEBUG) + Debug("Adding packet with Seq " + Seq + " to cache."); + + foreach(CacheEntry Existing in Cache) + { + if((Existing.Direction == Direction) && (Existing.Seq == Seq)) + return; + } + CacheEntry ce = new CacheEntry(Seq, Direction, Payload, PacketTime, SubPacket); + + Cache.Add(ce); + } + + private bool CacheEmpty() + { + return (Cache.Count == 0); + } + + public void DumpCache() + { + Log("Cache has " + Cache.Count + " elements."); + for (int i = 0; i < Cache.Count; ++i) + { + string Direction; + + if (Cache[i].Direction == PacketDirection.ClientToServer) + Direction = "Client to Server"; + else if (Cache[i].Direction == PacketDirection.ServerToClient) + Direction = "Server to Client"; + else + Direction = "Unknown"; + + Log("Cache Entry Seq " + Cache[i].Seq + ", Direction:" + Direction); + } + } + + private int FindCacheEntry(int Seq, PacketDirection Direction) + { + for(int i = 0; i < Cache.Count; ++i) + { + CacheEntry ce = Cache[i]; + + if ((ce.Seq == Seq) && (ce.Direction == Direction)) + return i; + } + return -1; + } + + private void ProcessCache() + { + int CacheElement; + + CacheElement = FindCacheEntry(GetExpectedSeq(PacketDirection.ServerToClient), PacketDirection.ServerToClient); + + while ( CacheElement >= 0) + { + if (DEBUG) + Debug("Processing packet with seq " + Cache[CacheElement].Seq + " from cache."); + + ProcessPacket(ServerIP, ClientIP, ServerPort, ClientPort, Cache[CacheElement].Payload, Cache[CacheElement].PacketTime, Cache[CacheElement].SubPacket, true); + + Cache.RemoveRange(CacheElement, 1); + + if (DEBUG) + Debug("Cache now has " + Cache.Count + " elements."); + + CacheElement = FindCacheEntry(GetExpectedSeq(PacketDirection.ServerToClient), PacketDirection.ServerToClient); + } + + CacheElement = FindCacheEntry(GetExpectedSeq(PacketDirection.ClientToServer), PacketDirection.ClientToServer); + + while (CacheElement >= 0) + { + ProcessPacket(ClientIP, ServerIP, ClientPort, ServerPort, Cache[CacheElement].Payload, Cache[CacheElement].PacketTime, Cache[CacheElement].SubPacket, true); + + Cache.RemoveRange(CacheElement, 1); + + CacheElement = FindCacheEntry(GetExpectedSeq(PacketDirection.ServerToClient), PacketDirection.ServerToClient); + } + } + + private PacketDirection GetDirection(System.Net.IPAddress srcIp, System.Net.IPAddress dstIp, ushort srcPort, ushort dstPort) + { + if ((srcIp.Equals(ServerIP)) && (srcPort == ServerPort) && (dstIp.Equals(ClientIP)) && (dstPort == ClientPort)) + return PacketDirection.ServerToClient; + + if ((srcIp.Equals(ClientIP)) && (srcPort == ClientPort) && (dstIp.Equals(ServerIP)) && (dstPort == ServerPort)) + return PacketDirection.ClientToServer; + + return PacketDirection.Unknown; + } + + private int GetExpectedSeq(PacketDirection Direction) + { + if (Direction == PacketDirection.ClientToServer) + return ExpectedClientSEQ; + else if (Direction == PacketDirection.ServerToClient) + return ExpectedServerSEQ; + + return 0; + } + + private void AdvanceSeq(PacketDirection Direction) + { + if (Direction == PacketDirection.ClientToServer) + ++ExpectedClientSEQ; + else if (Direction == PacketDirection.ServerToClient) + ++ExpectedServerSEQ; + } + + public void ProcessPacket(System.Net.IPAddress srcIp, System.Net.IPAddress dstIp, ushort srcPort, ushort dstPort, byte[] Payload, DateTime PacketTime, bool SubPacket, bool Cached) + { + byte Flags = 0x00; + + UInt32 OpCode = (UInt32)(Payload[0] * 256 + Payload[1]); + + PacketDirection Direction = GetDirection(srcIp, dstIp, srcPort, dstPort); + + if ((Direction == PacketDirection.Unknown) && (OpCode != OP_SessionRequest)) + return; + + // Check if this is a UCS connection and if so, skip packets until we see another OP_SessionRequest + + if (((CryptoFlag & 4) > 0) && (OpCode != OP_SessionRequest)) + return; + + if (Direction == PacketDirection.ClientToServer) + Debug("Client -> Server"); + else + Debug("Server -> Client"); + + Debug("Delta: " + (PacketTime - LastPacketTime)); + + TimeSpan Elapsed = PacketTime - LastPacketTime; + + //if (Elapsed.Seconds > 1) + // Debug("*** More than 1 second elapsed ***"); + + LastPacketTime = PacketTime; + + switch (OpCode) + { + case OP_SessionRequest: + { + if (PermaLocked) + break; + + ClientIP = srcIp; + + ClientPort = srcPort; + + ServerIP = dstIp; + + ServerPort = dstPort; + + Log("-- Locked onto EQ Stream. Client IP " + srcIp + ":" + srcPort + " Server IP " + dstIp + ":" + dstPort); + + ExpectedClientSEQ = 0; + + ExpectedServerSEQ = 0; + + CryptoFlag = 0; + + break; + } + + case OP_SessionResponse: + { + CryptoFlag = (ushort)(Payload[11] + (Payload[12] * 256)); + Log("Stream Crypto Flag is 0x" + CryptoFlag.ToString("x4")); + break; + } + + case OP_Combined: + { + if (DEBUG) + { + Debug("OP_Combined, Direction " + (Direction == PacketDirection.ClientToServer ? "Client->Server" : "Server->Client")); + Debug(Utils.HexDump(Payload)); + } + + byte[] Uncompressed; + + if (!SubPacket && (Payload[2] == 0x5a)) + { + Uncompressed = DecompressPacket(Payload, 3); + } + else if (!SubPacket && (Payload[2] == 0xa5)) + { + Uncompressed = new byte[Payload.Length - 3]; + + Array.Copy(Payload, 3, Uncompressed, 0, Payload.Length - 3); + + } + else + { + Uncompressed = new byte[Payload.Length - 2]; + + Array.Copy(Payload, 2, Uncompressed, 0, Payload.Length - 2); + } + + int BufferPosition = 0; + + while (BufferPosition < Uncompressed.Length - 2) + { + int SubPacketSize = Uncompressed[BufferPosition++]; + + byte[] NewPacket = new byte[SubPacketSize]; + + Array.Copy(Uncompressed, BufferPosition, NewPacket, 0, SubPacketSize); + + BufferPosition += SubPacketSize; + + ProcessPacket(srcIp, dstIp, srcPort, dstPort, NewPacket, PacketTime, true, Cached); + } + break; + } + + case OP_Packet: + { + if (DEBUG) + Debug("OP_Packet, Subpacket = "+ SubPacket); + + byte[] Uncompressed; + + if (!SubPacket && (Payload[2] == 0x5a)) + { + if (DEBUG) + Debug("Compressed"); + + Uncompressed = DecompressPacket(Payload, 3); + } + else if (!SubPacket && (Payload[2] == 0xa5)) + { + if (DEBUG) + Debug("0xa5"); + + Uncompressed = new byte[Payload.Length - 5]; + + Array.Copy(Payload, 3, Uncompressed, 0, Payload.Length - 5); + } + else + { + if (DEBUG) + Debug("Uncompressed"); + + Uncompressed = new byte[Payload.Length - 2]; + + Array.Copy(Payload, 2, Uncompressed, 0, Payload.Length - 2); + } + if (DEBUG) + { + //Debug("Raw payload is:"); + //Debug(Utils.HexDump(Payload)); + //Debug("Uncompressed data is:"); + //Debug(Utils.HexDump(Uncompressed)); + } + + int Seq = Uncompressed[0] * 256 + Uncompressed[1]; + Debug("Seq is " + Seq + " Expected " + GetExpectedSeq(Direction)); + if (Seq != GetExpectedSeq(Direction)) + { + if (Seq > GetExpectedSeq(Direction)) + { + if ((Seq - GetExpectedSeq(Direction) < 1000)) + AddToCache(Seq, Direction, Payload, PacketTime, SubPacket); + else + { + Log("Giving up on seeing expected fragment."); + + ErrorsInStream = true; + + FragmentSeq[(int)Direction] = -1; + + AdvanceSeq(Direction); + } + } + + break; + } + else + AdvanceSeq(Direction); + + + bool Multi = ((Uncompressed[2] == 0x00) && (Uncompressed[3] == 0x19)); + + if (Multi) + { + int BufferPosition = 4; + + while (BufferPosition < (Uncompressed.Length - 2)) + { + int Size = 0; + + if (Uncompressed[BufferPosition] == 0xff) + { + if (Uncompressed[BufferPosition + 1] == 0x01) + Size = 256 + Uncompressed[BufferPosition + 2]; + else + Size = Uncompressed[BufferPosition + 2]; + + BufferPosition += 3; + } + else + Size = Uncompressed[BufferPosition++]; + + int OpCodeBytes = 2; + int AppOpCode = Uncompressed[BufferPosition++]; + + if (AppOpCode == 0) + { + ++BufferPosition; + OpCodeBytes = 3; + } + + AppOpCode += (Uncompressed[BufferPosition++] * 256); + + ProcessAppPacket(srcIp, dstIp, srcPort, dstPort, AppOpCode, Size - OpCodeBytes, Uncompressed, BufferPosition, Direction, PacketTime); + + BufferPosition = BufferPosition + (Size - OpCodeBytes); + } + } + else + { + int BufferPosition = 2; + + int OpCodeBytes = 2; + int AppOpCode = Uncompressed[BufferPosition++]; + + if (AppOpCode == 0) + { + ++BufferPosition; + OpCodeBytes = 3; + } + + AppOpCode += (Uncompressed[BufferPosition++] * 256); + + ProcessAppPacket(srcIp, dstIp, srcPort, dstPort, AppOpCode, Uncompressed.Length - (2 + OpCodeBytes), Uncompressed, BufferPosition, Direction, PacketTime); + } + break; + } + + + case OP_Fragment: + { + if (DEBUG) + { + Debug("OP_Fragment"); + Debug("Raw Data"); + Debug(Utils.HexDump(Payload)); + } + + byte[] Uncompressed; + + if (!SubPacket && (Payload[2] == 0x5a)) + { + if (DEBUG) + Debug("Compressed"); + + Uncompressed = DecompressPacket(Payload, 3); + } + else if (!SubPacket && (Payload[2] == 0xa5)) + { + if (DEBUG) + Debug("0xa5"); + + Uncompressed = new byte[Payload.Length - 5]; + + Array.Copy(Payload, 3, Uncompressed, 0, Payload.Length - 5); + } + else + { + Uncompressed = new byte[Payload.Length - 2]; + + Array.Copy(Payload, 2, Uncompressed, 0, Payload.Length - 2); + } + if (DEBUG) + { + Debug("Uncompressed data."); + Debug(Utils.HexDump(Uncompressed)); + } + + if (FragmentSeq[(int)Direction] == -1) + { + if (DEBUG) + Debug("First fragment."); + + FragmentSeq[(int)Direction] = (Uncompressed[0] * 256) + Uncompressed[1]; + + if (DEBUG) + Debug("FragmentSeq is " + FragmentSeq[(int)Direction] + " Expecting " + GetExpectedSeq(Direction)); + + if (FragmentSeq[(int)Direction] != GetExpectedSeq(Direction)) + { + if (FragmentSeq[(int)Direction] > GetExpectedSeq(Direction)) + { + if((FragmentSeq[(int)Direction] - GetExpectedSeq(Direction)) < 1000) + AddToCache(FragmentSeq[(int)Direction], Direction, Payload, PacketTime, SubPacket); + else + { + Log("Giving up on seeing expected fragment."); + + ErrorsInStream = true; + + FragmentSeq[(int)Direction] = -1; + + AdvanceSeq(Direction); + } + } + FragmentSeq[(int)Direction] = -1; + + break; + } + else + AdvanceSeq(Direction); + + FragmentedPacketSize[(int)Direction] = Uncompressed[2] * 0x1000000 + Uncompressed[3] * 0x10000 + Uncompressed[4] * 0x100 + Uncompressed[5]; + + if((FragmentedPacketSize[(int)Direction] == 0) || (FragmentedPacketSize[(int)Direction] > 1000000)) + { + if (DEBUG) + Debug("Got a fragmented packet of size " + FragmentedPacketSize[(int)Direction] + ". Discarding."); + + ErrorsInStream = true; + + FragmentSeq[(int)Direction] = -1; + + break; + } + FragmentedBytesCollected[(int)Direction] = Uncompressed.Length - 6; + + if (DEBUG) + Debug("Total packet size is " + FragmentedPacketSize[(int)Direction]); + + if((Uncompressed.Length - 6) > FragmentedPacketSize[(int)Direction]) + { + Log("Mangled fragment."); + + ErrorsInStream = true; + + FragmentSeq[(int)Direction] = -1; + + break; + } + + Fragments[(int)Direction] = new byte[FragmentedPacketSize[(int)Direction]]; + + if (DEBUG) + Debug("Copying " + (Uncompressed.Length - 6) + " bytes to Fragments starting at index 0"); + + Array.Copy(Uncompressed, 6, Fragments[(int)Direction], 0, Uncompressed.Length - 6); + } + else + { + int LastSeq = FragmentSeq[(int)Direction]; + + FragmentSeq[(int)Direction] = (Uncompressed[0] * 256) + Uncompressed[1]; + + if (DEBUG) + Debug("FragmentSeq is " + FragmentSeq[(int)Direction] + ". Expecting " + GetExpectedSeq(Direction)); + + if (FragmentSeq[(int)Direction] != GetExpectedSeq(Direction)) + { + if (FragmentSeq[(int)Direction] > GetExpectedSeq(Direction)) + { + if ((FragmentSeq[(int)Direction] - GetExpectedSeq(Direction)) < 1000) + AddToCache(FragmentSeq[(int)Direction], Direction, Payload, PacketTime, SubPacket); + else + { + Log("Giving up on seeing expected fragment."); + + ErrorsInStream = true; + + AdvanceSeq(Direction); + + FragmentSeq[(int)Direction] = -1; + } + } + break; + } + else + AdvanceSeq(Direction); + + if (DEBUG) + Debug("Copying " + (Uncompressed.Length - 2) + " bytes from Uncompressed to Fragments starting at " + FragmentedBytesCollected[(int)Direction]); + + if ((Uncompressed.Length - 2) > (Fragments[(int)Direction].Length - FragmentedBytesCollected[(int)Direction])) + { + Log("Mangled fragment. Discarding."); + + ErrorsInStream = true; + + FragmentSeq[(int)Direction] = -1; + + break; + } + + Array.Copy(Uncompressed, 2, Fragments[(int)Direction], FragmentedBytesCollected[(int)Direction], Uncompressed.Length - 2); + + FragmentedBytesCollected[(int)Direction] += Uncompressed.Length - 2; + + if (FragmentedBytesCollected[(int)Direction] == FragmentedPacketSize[(int)Direction]) + { + if (DEBUG) + Debug("Got whole packet."); + + if ((Fragments[(int)Direction][0] == 0x00) && (Fragments[1][(int)Direction] == 0x019)) + { + if (DEBUG) + Debug("Multi packet."); + + int BufferPosition = 2; + + while (BufferPosition < Fragments[(int)Direction].Length) + { + int Size = 0; + + if (Fragments[(int)Direction][BufferPosition] == 0xff) + { + if (Fragments[(int)Direction][BufferPosition + 1] == 0x01) + Size = 256 + Fragments[(int)Direction][BufferPosition + 2]; + else + Size = Fragments[(int)Direction][BufferPosition + 2]; + + BufferPosition += 3; + } + else + Size = Fragments[(int)Direction][BufferPosition++]; + + int OpCodeBytes = 2; + int AppOpCode = Fragments[(int)Direction][BufferPosition++]; + + if (AppOpCode == 0) + { + ++BufferPosition; + OpCodeBytes = 3; + } + + AppOpCode += (Fragments[(int)Direction][BufferPosition++] * 256); + + ProcessAppPacket(srcIp, dstIp, srcPort, dstPort, AppOpCode, Size - OpCodeBytes, Fragments[(int)Direction], BufferPosition, Direction, PacketTime); + + BufferPosition = BufferPosition + (Size - OpCodeBytes); + } + } + else + { + int BufferPosition = 0; + int OpCodeBytes = 2; + int AppOpCode = Fragments[(int)Direction][BufferPosition++]; + + if (AppOpCode == 0) + { + ++BufferPosition; + OpCodeBytes = 3; + } + + AppOpCode += (Fragments[(int)Direction][BufferPosition++] * 256); + + byte[] NewPacket = new byte[Fragments[(int)Direction].Length - OpCodeBytes]; + + Array.Copy(Fragments[(int)Direction], BufferPosition, NewPacket, 0, Fragments[(int)Direction].Length - OpCodeBytes); + + ProcessAppPacket(srcIp, dstIp, srcPort, dstPort, AppOpCode, NewPacket.Length, NewPacket, 0, Direction, PacketTime); + } + if (DEBUG) + Debug("Reseting FragmentSeq to -1"); + + FragmentSeq[(int)Direction] = -1; + } + } + + break; + } + case OP_OutOfOrderAck: + { + int Seq = Payload[2] * 256 + Payload[3]; + + if (DEBUG) + Debug("OP_OutOfOrder " + Seq); + + break; + } + case OP_Ack: + { + int Seq; + + if(Payload.Length > 4) + Seq = Payload[3] * 256 + Payload[4]; + else + Seq = Payload[2] * 256 + Payload[3]; + + string DirectionString; + + if(Direction == PacketDirection.ClientToServer) + DirectionString = "Client to Server"; + else + DirectionString = "Server to Client"; + + if (DEBUG) + { + Debug("OP_Ack, Seq " + Seq + " " + DirectionString); + Debug(Utils.HexDump(Payload)); + } + + break; + } + default: + if (OpCode > 0xff) + { + if (DEBUG) + { + Debug("Unencapsulated EQ Application OpCode. Subpacket is " + SubPacket.ToString()); + Debug("--- Raw payload ---"); + Debug(Utils.HexDump(Payload)); + Debug("-------------------"); + } + + int AppOpCode; + byte[] NewPacket; + + if (SubPacket) + { + AppOpCode = Payload[1] * 256 + Payload[0]; + + NewPacket = new byte[Payload.Length - 2]; + + Array.Copy(Payload, 2, NewPacket, 0, Payload.Length - 2); + } + else + { + // This packet has a flag byte between the first and second bytes of the opcode, and also a CRC + + Flags = Payload[1]; + + if (Flags == 0x5a) + { + if(DEBUG) + Debug("Compressed unencapsulated packet."); + + byte[] NewPayload = new byte[Payload.Length - 4]; + Array.Copy(Payload, 2, NewPayload, 0, Payload.Length - 4); + byte[] Uncompressed = DecompressPacket(NewPayload, 0); + + if (DEBUG) + { + Debug("Uncompressed Payload:"); + Debug(Utils.HexDump(Uncompressed)); + } + // Opcode is first byte of compressed payload and first byte of uncompressed data + AppOpCode = Uncompressed[0] * 256 + Payload[0]; + NewPacket = new byte[Uncompressed.Length - 1]; + Array.Copy(Uncompressed, 1, NewPacket, 0, Uncompressed.Length - 1); + } + else + { + AppOpCode = Payload[2] * 256 + Payload[0]; + + NewPacket = new byte[Payload.Length - 5]; + + Array.Copy(Payload, 3, NewPacket, 0, Payload.Length - 5); + } + } + + ProcessAppPacket(srcIp, dstIp, srcPort, dstPort, AppOpCode, NewPacket.Length, NewPacket, 0, Direction, PacketTime); + } + else + { + if (DEBUG) + { + Debug("OP_Unknown (" + OpCode.ToString("x") + ")"); + Debug(Utils.HexDump(Payload)); + } + } + break; + } + + if (!Cached && !CacheEmpty()) + ProcessCache(); + } + + public void ProcessAppPacket(System.Net.IPAddress srcIp, System.Net.IPAddress dstIp, ushort srcPort, ushort dstPort, int InOpCode, int BufferSize, byte[] Source, int Offset, PacketDirection Direction, DateTime PacketTime) + { + EQApplicationPacket app = new EQApplicationPacket(srcIp, dstIp, srcPort, dstPort, InOpCode, BufferSize, Source, Offset, Direction, PacketTime); + + if (!Identified) + { + IdentificationStatus TempStatus = Identifier(InOpCode, BufferSize, Direction); + + if (!PermaLocked && (TempStatus > IdentificationStatus.No)) + { + Log("-- Permalocking to this stream."); + PermaLocked = true; + } + Identified = (TempStatus == IdentificationStatus.Yes); + } + AddPacket(app); + } + + public byte[] DecompressPacket(byte[] Payload, int Offset) + { + MemoryStream ms = new MemoryStream(Payload.GetUpperBound(0) - Offset); + + ms.Write(Payload, Offset, Payload.GetUpperBound(0) - Offset); + + ms.Seek(0, System.IO.SeekOrigin.Begin); + + ZInputStream zs = new ZInputStream(ms); + + int UncompressedSize; + + byte[] Uncompressed = new byte[4096]; + + try + { + UncompressedSize = zs.read(Uncompressed, 0, 4096); + } + catch + { + if(DEBUG) + Debug("DECOMPRESSION FAILURE"); + + Array.Copy(Payload, Offset - 1, Uncompressed, 0, Payload.Length - (Offset - 1)); + + UncompressedSize = Payload.Length - (Offset - 1); + } + + zs.Close(); + + zs.Dispose(); + + ms.Close(); + + ms.Dispose(); + + Array.Resize(ref Uncompressed, UncompressedSize); + + return Uncompressed; + } + } +} diff --git a/utils/EQExtractor2/EQExtractor2/GenerateSQLForm.Designer.cs b/utils/EQExtractor2/EQExtractor2/GenerateSQLForm.Designer.cs new file mode 100644 index 000000000..6805e5df2 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/GenerateSQLForm.Designer.cs @@ -0,0 +1,625 @@ +namespace EQExtractor2 +{ + partial class GenerateSQLForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.MerchantTextBox = new System.Windows.Forms.MaskedTextBox(); + this.MerchantLabel = new System.Windows.Forms.Label(); + this.GroundSpawnTextBox = new System.Windows.Forms.MaskedTextBox(); + this.GroundSpawnLabel = new System.Windows.Forms.Label(); + this.ObjectTextBox = new System.Windows.Forms.MaskedTextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.GridTextBox = new System.Windows.Forms.MaskedTextBox(); + this.GridLabel = new System.Windows.Forms.Label(); + this.Spawn2TextBox = new System.Windows.Forms.MaskedTextBox(); + this.Spawn2Label = new System.Windows.Forms.Label(); + this.SpawnEntryTextBox = new System.Windows.Forms.MaskedTextBox(); + this.SpawnEntryLabel = new System.Windows.Forms.Label(); + this.SpawnGroupTextBox = new System.Windows.Forms.MaskedTextBox(); + this.SpawnGroupLabel = new System.Windows.Forms.Label(); + this.NPCTypesTextBox = new System.Windows.Forms.MaskedTextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.DoorsTextBox = new System.Windows.Forms.MaskedTextBox(); + this.DoorsLabel = new System.Windows.Forms.Label(); + this.VersionSelector = new System.Windows.Forms.NumericUpDown(); + this.VersionLabel = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.ZoneIDTextBox = new System.Windows.Forms.MaskedTextBox(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.NPCTypesTintCheckBox = new System.Windows.Forms.CheckBox(); + this.UpdateExistingNPCTypesCheckbox = new System.Windows.Forms.CheckBox(); + this.ZonePointCheckBox = new System.Windows.Forms.CheckBox(); + this.ZoneCheckBox = new System.Windows.Forms.CheckBox(); + this.MerchantCheckBox = new System.Windows.Forms.CheckBox(); + this.GroundSpawnCheckBox = new System.Windows.Forms.CheckBox(); + this.ObjectCheckBox = new System.Windows.Forms.CheckBox(); + this.GridCheckBox = new System.Windows.Forms.CheckBox(); + this.SpawnCheckBox = new System.Windows.Forms.CheckBox(); + this.DoorCheckBox = new System.Windows.Forms.CheckBox(); + this.SQLFileDialog = new System.Windows.Forms.SaveFileDialog(); + this.GenerateSQLButton = new System.Windows.Forms.Button(); + this.SQLCancelButton = new System.Windows.Forms.Button(); + this.MiscOptions = new System.Windows.Forms.GroupBox(); + this.SpawnNameFilter = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.CoalesceWaypoints = new System.Windows.Forms.CheckBox(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.InvisibleMenCheckBox = new System.Windows.Forms.CheckBox(); + this.groupBox1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.VersionSelector)).BeginInit(); + this.groupBox2.SuspendLayout(); + this.MiscOptions.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox1 + // + this.groupBox1.Controls.Add(this.MerchantTextBox); + this.groupBox1.Controls.Add(this.MerchantLabel); + this.groupBox1.Controls.Add(this.GroundSpawnTextBox); + this.groupBox1.Controls.Add(this.GroundSpawnLabel); + this.groupBox1.Controls.Add(this.ObjectTextBox); + this.groupBox1.Controls.Add(this.label1); + this.groupBox1.Controls.Add(this.GridTextBox); + this.groupBox1.Controls.Add(this.GridLabel); + this.groupBox1.Controls.Add(this.Spawn2TextBox); + this.groupBox1.Controls.Add(this.Spawn2Label); + this.groupBox1.Controls.Add(this.SpawnEntryTextBox); + this.groupBox1.Controls.Add(this.SpawnEntryLabel); + this.groupBox1.Controls.Add(this.SpawnGroupTextBox); + this.groupBox1.Controls.Add(this.SpawnGroupLabel); + this.groupBox1.Controls.Add(this.NPCTypesTextBox); + this.groupBox1.Controls.Add(this.label3); + this.groupBox1.Controls.Add(this.DoorsTextBox); + this.groupBox1.Controls.Add(this.DoorsLabel); + this.groupBox1.Controls.Add(this.VersionSelector); + this.groupBox1.Controls.Add(this.VersionLabel); + this.groupBox1.Controls.Add(this.label4); + this.groupBox1.Controls.Add(this.ZoneIDTextBox); + this.groupBox1.Location = new System.Drawing.Point(12, 12); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(262, 345); + this.groupBox1.TabIndex = 0; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Insert IDs"; + // + // MerchantTextBox + // + this.MerchantTextBox.Enabled = false; + this.MerchantTextBox.HidePromptOnLeave = true; + this.MerchantTextBox.Location = new System.Drawing.Point(107, 302); + this.MerchantTextBox.Mask = "0000000000"; + this.MerchantTextBox.Name = "MerchantTextBox"; + this.MerchantTextBox.PromptChar = ' '; + this.MerchantTextBox.Size = new System.Drawing.Size(61, 20); + this.MerchantTextBox.TabIndex = 34; + // + // MerchantLabel + // + this.MerchantLabel.AutoSize = true; + this.MerchantLabel.Location = new System.Drawing.Point(14, 306); + this.MerchantLabel.Name = "MerchantLabel"; + this.MerchantLabel.Size = new System.Drawing.Size(76, 13); + this.MerchantLabel.TabIndex = 33; + this.MerchantLabel.Text = "Merchant Lists"; + // + // GroundSpawnTextBox + // + this.GroundSpawnTextBox.Enabled = false; + this.GroundSpawnTextBox.HidePromptOnLeave = true; + this.GroundSpawnTextBox.Location = new System.Drawing.Point(107, 274); + this.GroundSpawnTextBox.Mask = "0000000000"; + this.GroundSpawnTextBox.Name = "GroundSpawnTextBox"; + this.GroundSpawnTextBox.PromptChar = ' '; + this.GroundSpawnTextBox.Size = new System.Drawing.Size(61, 20); + this.GroundSpawnTextBox.TabIndex = 32; + // + // GroundSpawnLabel + // + this.GroundSpawnLabel.AutoSize = true; + this.GroundSpawnLabel.Location = new System.Drawing.Point(7, 278); + this.GroundSpawnLabel.Name = "GroundSpawnLabel"; + this.GroundSpawnLabel.Size = new System.Drawing.Size(83, 13); + this.GroundSpawnLabel.TabIndex = 31; + this.GroundSpawnLabel.Text = "Ground Spawns"; + // + // ObjectTextBox + // + this.ObjectTextBox.Enabled = false; + this.ObjectTextBox.HidePromptOnLeave = true; + this.ObjectTextBox.Location = new System.Drawing.Point(107, 246); + this.ObjectTextBox.Mask = "0000000000"; + this.ObjectTextBox.Name = "ObjectTextBox"; + this.ObjectTextBox.PromptChar = ' '; + this.ObjectTextBox.Size = new System.Drawing.Size(61, 20); + this.ObjectTextBox.TabIndex = 30; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(47, 250); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(43, 13); + this.label1.TabIndex = 29; + this.label1.Text = "Objects"; + // + // GridTextBox + // + this.GridTextBox.Enabled = false; + this.GridTextBox.HidePromptOnLeave = true; + this.GridTextBox.Location = new System.Drawing.Point(107, 218); + this.GridTextBox.Mask = "0000000000"; + this.GridTextBox.Name = "GridTextBox"; + this.GridTextBox.PromptChar = ' '; + this.GridTextBox.Size = new System.Drawing.Size(61, 20); + this.GridTextBox.TabIndex = 28; + // + // GridLabel + // + this.GridLabel.AutoSize = true; + this.GridLabel.Location = new System.Drawing.Point(59, 222); + this.GridLabel.Name = "GridLabel"; + this.GridLabel.Size = new System.Drawing.Size(31, 13); + this.GridLabel.TabIndex = 27; + this.GridLabel.Text = "Grids"; + // + // Spawn2TextBox + // + this.Spawn2TextBox.Enabled = false; + this.Spawn2TextBox.HidePromptOnLeave = true; + this.Spawn2TextBox.Location = new System.Drawing.Point(107, 190); + this.Spawn2TextBox.Mask = "0000000000"; + this.Spawn2TextBox.Name = "Spawn2TextBox"; + this.Spawn2TextBox.PromptChar = ' '; + this.Spawn2TextBox.Size = new System.Drawing.Size(61, 20); + this.Spawn2TextBox.TabIndex = 26; + // + // Spawn2Label + // + this.Spawn2Label.AutoSize = true; + this.Spawn2Label.Location = new System.Drawing.Point(44, 194); + this.Spawn2Label.Name = "Spawn2Label"; + this.Spawn2Label.Size = new System.Drawing.Size(46, 13); + this.Spawn2Label.TabIndex = 25; + this.Spawn2Label.Text = "Spawn2"; + // + // SpawnEntryTextBox + // + this.SpawnEntryTextBox.Enabled = false; + this.SpawnEntryTextBox.HidePromptOnLeave = true; + this.SpawnEntryTextBox.Location = new System.Drawing.Point(107, 162); + this.SpawnEntryTextBox.Mask = "0000000000"; + this.SpawnEntryTextBox.Name = "SpawnEntryTextBox"; + this.SpawnEntryTextBox.PromptChar = ' '; + this.SpawnEntryTextBox.Size = new System.Drawing.Size(61, 20); + this.SpawnEntryTextBox.TabIndex = 24; + // + // SpawnEntryLabel + // + this.SpawnEntryLabel.AutoSize = true; + this.SpawnEntryLabel.Location = new System.Drawing.Point(26, 166); + this.SpawnEntryLabel.Name = "SpawnEntryLabel"; + this.SpawnEntryLabel.Size = new System.Drawing.Size(64, 13); + this.SpawnEntryLabel.TabIndex = 23; + this.SpawnEntryLabel.Text = "SpawnEntry"; + // + // SpawnGroupTextBox + // + this.SpawnGroupTextBox.Enabled = false; + this.SpawnGroupTextBox.HidePromptOnLeave = true; + this.SpawnGroupTextBox.Location = new System.Drawing.Point(107, 134); + this.SpawnGroupTextBox.Mask = "0000000000"; + this.SpawnGroupTextBox.Name = "SpawnGroupTextBox"; + this.SpawnGroupTextBox.PromptChar = ' '; + this.SpawnGroupTextBox.Size = new System.Drawing.Size(61, 20); + this.SpawnGroupTextBox.TabIndex = 22; + // + // SpawnGroupLabel + // + this.SpawnGroupLabel.AutoSize = true; + this.SpawnGroupLabel.Location = new System.Drawing.Point(21, 138); + this.SpawnGroupLabel.Name = "SpawnGroupLabel"; + this.SpawnGroupLabel.Size = new System.Drawing.Size(69, 13); + this.SpawnGroupLabel.TabIndex = 21; + this.SpawnGroupLabel.Text = "SpawnGroup"; + // + // NPCTypesTextBox + // + this.NPCTypesTextBox.Enabled = false; + this.NPCTypesTextBox.HidePromptOnLeave = true; + this.NPCTypesTextBox.Location = new System.Drawing.Point(107, 106); + this.NPCTypesTextBox.Mask = "0000000000"; + this.NPCTypesTextBox.Name = "NPCTypesTextBox"; + this.NPCTypesTextBox.PromptChar = ' '; + this.NPCTypesTextBox.Size = new System.Drawing.Size(61, 20); + this.NPCTypesTextBox.TabIndex = 20; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(32, 110); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(58, 13); + this.label3.TabIndex = 19; + this.label3.Text = "NPCTypes"; + // + // DoorsTextBox + // + this.DoorsTextBox.Enabled = false; + this.DoorsTextBox.HidePromptOnLeave = true; + this.DoorsTextBox.Location = new System.Drawing.Point(107, 78); + this.DoorsTextBox.Mask = "0000000000"; + this.DoorsTextBox.Name = "DoorsTextBox"; + this.DoorsTextBox.PromptChar = ' '; + this.DoorsTextBox.Size = new System.Drawing.Size(61, 20); + this.DoorsTextBox.TabIndex = 18; + // + // DoorsLabel + // + this.DoorsLabel.AutoSize = true; + this.DoorsLabel.Location = new System.Drawing.Point(55, 82); + this.DoorsLabel.Name = "DoorsLabel"; + this.DoorsLabel.Size = new System.Drawing.Size(35, 13); + this.DoorsLabel.TabIndex = 17; + this.DoorsLabel.Text = "Doors"; + // + // VersionSelector + // + this.VersionSelector.Enabled = false; + this.VersionSelector.Location = new System.Drawing.Point(107, 50); + this.VersionSelector.Maximum = new decimal(new int[] { + 999, + 0, + 0, + 0}); + this.VersionSelector.Name = "VersionSelector"; + this.VersionSelector.Size = new System.Drawing.Size(39, 20); + this.VersionSelector.TabIndex = 16; + this.VersionSelector.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; + this.VersionSelector.ValueChanged += new System.EventHandler(this.VersionSelector_ValueChanged); + // + // VersionLabel + // + this.VersionLabel.AutoSize = true; + this.VersionLabel.Location = new System.Drawing.Point(48, 54); + this.VersionLabel.Name = "VersionLabel"; + this.VersionLabel.Size = new System.Drawing.Size(42, 13); + this.VersionLabel.TabIndex = 15; + this.VersionLabel.Text = "Version"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(44, 26); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(46, 13); + this.label4.TabIndex = 14; + this.label4.Text = "ZoneID:"; + // + // ZoneIDTextBox + // + this.ZoneIDTextBox.Enabled = false; + this.ZoneIDTextBox.HidePromptOnLeave = true; + this.ZoneIDTextBox.Location = new System.Drawing.Point(107, 22); + this.ZoneIDTextBox.Mask = "0000000"; + this.ZoneIDTextBox.Name = "ZoneIDTextBox"; + this.ZoneIDTextBox.PromptChar = ' '; + this.ZoneIDTextBox.Size = new System.Drawing.Size(100, 20); + this.ZoneIDTextBox.TabIndex = 13; + this.ZoneIDTextBox.Validated += new System.EventHandler(this.ZoneIDTextBox_Validated); + // + // groupBox2 + // + this.groupBox2.Controls.Add(this.InvisibleMenCheckBox); + this.groupBox2.Controls.Add(this.NPCTypesTintCheckBox); + this.groupBox2.Controls.Add(this.UpdateExistingNPCTypesCheckbox); + this.groupBox2.Controls.Add(this.ZonePointCheckBox); + this.groupBox2.Controls.Add(this.ZoneCheckBox); + this.groupBox2.Controls.Add(this.MerchantCheckBox); + this.groupBox2.Controls.Add(this.GroundSpawnCheckBox); + this.groupBox2.Controls.Add(this.ObjectCheckBox); + this.groupBox2.Controls.Add(this.GridCheckBox); + this.groupBox2.Controls.Add(this.SpawnCheckBox); + this.groupBox2.Controls.Add(this.DoorCheckBox); + this.groupBox2.Location = new System.Drawing.Point(298, 12); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(200, 345); + this.groupBox2.TabIndex = 1; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Include"; + // + // NPCTypesTintCheckBox + // + this.NPCTypesTintCheckBox.AutoSize = true; + this.NPCTypesTintCheckBox.Location = new System.Drawing.Point(24, 233); + this.NPCTypesTintCheckBox.Name = "NPCTypesTintCheckBox"; + this.NPCTypesTintCheckBox.Size = new System.Drawing.Size(117, 17); + this.NPCTypesTintCheckBox.TabIndex = 42; + this.NPCTypesTintCheckBox.Text = "Use npc_types_tint"; + this.toolTip1.SetToolTip(this.NPCTypesTintCheckBox, "Uses the npc_types_tint table instead of the tint columns in the npc_types table." + + ""); + this.NPCTypesTintCheckBox.UseVisualStyleBackColor = true; + // + // UpdateExistingNPCTypesCheckbox + // + this.UpdateExistingNPCTypesCheckbox.AutoSize = true; + this.UpdateExistingNPCTypesCheckbox.Location = new System.Drawing.Point(24, 210); + this.UpdateExistingNPCTypesCheckbox.Name = "UpdateExistingNPCTypesCheckbox"; + this.UpdateExistingNPCTypesCheckbox.Size = new System.Drawing.Size(152, 17); + this.UpdateExistingNPCTypesCheckbox.TabIndex = 41; + this.UpdateExistingNPCTypesCheckbox.Text = "Update existing NPC types"; + this.UpdateExistingNPCTypesCheckbox.UseVisualStyleBackColor = true; + this.UpdateExistingNPCTypesCheckbox.CheckedChanged += new System.EventHandler(this.UpdateExistingNPCTypesCheckbox_CheckedChanged); + // + // ZonePointCheckBox + // + this.ZonePointCheckBox.AutoSize = true; + this.ZonePointCheckBox.Checked = true; + this.ZonePointCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + this.ZonePointCheckBox.Location = new System.Drawing.Point(24, 187); + this.ZonePointCheckBox.Name = "ZonePointCheckBox"; + this.ZonePointCheckBox.Size = new System.Drawing.Size(83, 17); + this.ZonePointCheckBox.TabIndex = 40; + this.ZonePointCheckBox.Text = "Zone Points"; + this.ZonePointCheckBox.UseVisualStyleBackColor = true; + // + // ZoneCheckBox + // + this.ZoneCheckBox.AutoSize = true; + this.ZoneCheckBox.Checked = true; + this.ZoneCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + this.ZoneCheckBox.Location = new System.Drawing.Point(24, 164); + this.ZoneCheckBox.Name = "ZoneCheckBox"; + this.ZoneCheckBox.Size = new System.Drawing.Size(84, 17); + this.ZoneCheckBox.TabIndex = 39; + this.ZoneCheckBox.Text = "Zone Config"; + this.ZoneCheckBox.UseVisualStyleBackColor = true; + // + // MerchantCheckBox + // + this.MerchantCheckBox.AutoSize = true; + this.MerchantCheckBox.Checked = true; + this.MerchantCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + this.MerchantCheckBox.Location = new System.Drawing.Point(24, 141); + this.MerchantCheckBox.Name = "MerchantCheckBox"; + this.MerchantCheckBox.Size = new System.Drawing.Size(95, 17); + this.MerchantCheckBox.TabIndex = 38; + this.MerchantCheckBox.Text = "Merchant Lists"; + this.MerchantCheckBox.UseVisualStyleBackColor = true; + // + // GroundSpawnCheckBox + // + this.GroundSpawnCheckBox.AutoSize = true; + this.GroundSpawnCheckBox.Checked = true; + this.GroundSpawnCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + this.GroundSpawnCheckBox.Location = new System.Drawing.Point(24, 118); + this.GroundSpawnCheckBox.Name = "GroundSpawnCheckBox"; + this.GroundSpawnCheckBox.Size = new System.Drawing.Size(102, 17); + this.GroundSpawnCheckBox.TabIndex = 9; + this.GroundSpawnCheckBox.Text = "Ground Spawns"; + this.GroundSpawnCheckBox.UseVisualStyleBackColor = true; + // + // ObjectCheckBox + // + this.ObjectCheckBox.AutoSize = true; + this.ObjectCheckBox.Checked = true; + this.ObjectCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + this.ObjectCheckBox.Location = new System.Drawing.Point(24, 95); + this.ObjectCheckBox.Name = "ObjectCheckBox"; + this.ObjectCheckBox.Size = new System.Drawing.Size(62, 17); + this.ObjectCheckBox.TabIndex = 8; + this.ObjectCheckBox.Text = "Objects"; + this.ObjectCheckBox.UseVisualStyleBackColor = true; + // + // GridCheckBox + // + this.GridCheckBox.AutoSize = true; + this.GridCheckBox.Checked = true; + this.GridCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + this.GridCheckBox.Location = new System.Drawing.Point(24, 72); + this.GridCheckBox.Name = "GridCheckBox"; + this.GridCheckBox.Size = new System.Drawing.Size(50, 17); + this.GridCheckBox.TabIndex = 7; + this.GridCheckBox.Text = "Grids"; + this.GridCheckBox.UseVisualStyleBackColor = true; + // + // SpawnCheckBox + // + this.SpawnCheckBox.AutoSize = true; + this.SpawnCheckBox.Checked = true; + this.SpawnCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + this.SpawnCheckBox.Location = new System.Drawing.Point(24, 49); + this.SpawnCheckBox.Name = "SpawnCheckBox"; + this.SpawnCheckBox.Size = new System.Drawing.Size(64, 17); + this.SpawnCheckBox.TabIndex = 5; + this.SpawnCheckBox.Text = "Spawns"; + this.SpawnCheckBox.UseVisualStyleBackColor = true; + this.SpawnCheckBox.CheckedChanged += new System.EventHandler(this.SpawnCheckBox_CheckedChanged); + // + // DoorCheckBox + // + this.DoorCheckBox.AutoSize = true; + this.DoorCheckBox.Checked = true; + this.DoorCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; + this.DoorCheckBox.Location = new System.Drawing.Point(24, 26); + this.DoorCheckBox.Name = "DoorCheckBox"; + this.DoorCheckBox.Size = new System.Drawing.Size(54, 17); + this.DoorCheckBox.TabIndex = 4; + this.DoorCheckBox.Text = "Doors"; + this.DoorCheckBox.UseVisualStyleBackColor = true; + // + // SQLFileDialog + // + this.SQLFileDialog.Filter = "SQL Files (*.sql)|*.sql|Text Files (*.txt)|*.txt|All files (*.*)|*.*"; + this.SQLFileDialog.Title = "Generate SQL and Save As"; + // + // GenerateSQLButton + // + this.GenerateSQLButton.Location = new System.Drawing.Point(126, 474); + this.GenerateSQLButton.Name = "GenerateSQLButton"; + this.GenerateSQLButton.Size = new System.Drawing.Size(100, 27); + this.GenerateSQLButton.TabIndex = 3; + this.GenerateSQLButton.Text = "Generate SQL"; + this.GenerateSQLButton.UseVisualStyleBackColor = true; + this.GenerateSQLButton.Click += new System.EventHandler(this.GenerateSQLButton_Click); + // + // SQLCancelButton + // + this.SQLCancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.SQLCancelButton.Location = new System.Drawing.Point(305, 474); + this.SQLCancelButton.Name = "SQLCancelButton"; + this.SQLCancelButton.Size = new System.Drawing.Size(100, 27); + this.SQLCancelButton.TabIndex = 4; + this.SQLCancelButton.Text = "Cancel"; + this.SQLCancelButton.UseVisualStyleBackColor = true; + this.SQLCancelButton.Click += new System.EventHandler(this.SQLCancelButton_Click); + // + // MiscOptions + // + this.MiscOptions.Controls.Add(this.SpawnNameFilter); + this.MiscOptions.Controls.Add(this.label2); + this.MiscOptions.Controls.Add(this.CoalesceWaypoints); + this.MiscOptions.Location = new System.Drawing.Point(12, 368); + this.MiscOptions.Name = "MiscOptions"; + this.MiscOptions.Size = new System.Drawing.Size(486, 76); + this.MiscOptions.TabIndex = 2; + this.MiscOptions.TabStop = false; + this.MiscOptions.Text = "Misc. Options"; + // + // SpawnNameFilter + // + this.SpawnNameFilter.Location = new System.Drawing.Point(229, 46); + this.SpawnNameFilter.Name = "SpawnNameFilter"; + this.SpawnNameFilter.Size = new System.Drawing.Size(209, 20); + this.SpawnNameFilter.TabIndex = 5; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(14, 49); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(209, 13); + this.label2.TabIndex = 4; + this.label2.Text = "Only include spawns that contain the string"; + // + // CoalesceWaypoints + // + this.CoalesceWaypoints.AutoSize = true; + this.CoalesceWaypoints.Checked = true; + this.CoalesceWaypoints.CheckState = System.Windows.Forms.CheckState.Checked; + this.CoalesceWaypoints.Location = new System.Drawing.Point(17, 19); + this.CoalesceWaypoints.Name = "CoalesceWaypoints"; + this.CoalesceWaypoints.Size = new System.Drawing.Size(184, 17); + this.CoalesceWaypoints.TabIndex = 3; + this.CoalesceWaypoints.Text = "Automatically coalesce waypoints"; + this.toolTip1.SetToolTip(this.CoalesceWaypoints, "Remove redundant waypoints from generated SQL"); + this.CoalesceWaypoints.UseVisualStyleBackColor = true; + // + // InvisibleMenCheckBox + // + this.InvisibleMenCheckBox.AutoSize = true; + this.InvisibleMenCheckBox.Location = new System.Drawing.Point(24, 256); + this.InvisibleMenCheckBox.Name = "InvisibleMenCheckBox"; + this.InvisibleMenCheckBox.Size = new System.Drawing.Size(126, 17); + this.InvisibleMenCheckBox.TabIndex = 43; + this.InvisibleMenCheckBox.Text = "Include Invisible Men"; + this.toolTip1.SetToolTip(this.InvisibleMenCheckBox, "Includes Race 127 NPCs"); + this.InvisibleMenCheckBox.UseVisualStyleBackColor = true; + // + // GenerateSQLForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.SQLCancelButton; + this.ClientSize = new System.Drawing.Size(514, 513); + this.Controls.Add(this.MiscOptions); + this.Controls.Add(this.SQLCancelButton); + this.Controls.Add(this.GenerateSQLButton); + this.Controls.Add(this.groupBox2); + this.Controls.Add(this.groupBox1); + this.Name = "GenerateSQLForm"; + this.Text = "Generate SQL"; + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.VersionSelector)).EndInit(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.MiscOptions.ResumeLayout(false); + this.MiscOptions.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.Label VersionLabel; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label DoorsLabel; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label SpawnGroupLabel; + private System.Windows.Forms.Label SpawnEntryLabel; + private System.Windows.Forms.Label Spawn2Label; + private System.Windows.Forms.Label GridLabel; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label GroundSpawnLabel; + private System.Windows.Forms.Label MerchantLabel; + public System.Windows.Forms.NumericUpDown VersionSelector; + public System.Windows.Forms.MaskedTextBox ZoneIDTextBox; + public System.Windows.Forms.MaskedTextBox DoorsTextBox; + public System.Windows.Forms.MaskedTextBox NPCTypesTextBox; + public System.Windows.Forms.MaskedTextBox SpawnGroupTextBox; + public System.Windows.Forms.MaskedTextBox SpawnEntryTextBox; + public System.Windows.Forms.MaskedTextBox Spawn2TextBox; + public System.Windows.Forms.MaskedTextBox GridTextBox; + public System.Windows.Forms.MaskedTextBox ObjectTextBox; + public System.Windows.Forms.MaskedTextBox GroundSpawnTextBox; + public System.Windows.Forms.MaskedTextBox MerchantTextBox; + public System.Windows.Forms.CheckBox SpawnCheckBox; + public System.Windows.Forms.CheckBox DoorCheckBox; + public System.Windows.Forms.CheckBox GroundSpawnCheckBox; + public System.Windows.Forms.CheckBox ObjectCheckBox; + public System.Windows.Forms.CheckBox GridCheckBox; + public System.Windows.Forms.CheckBox ZonePointCheckBox; + public System.Windows.Forms.CheckBox ZoneCheckBox; + public System.Windows.Forms.CheckBox MerchantCheckBox; + public System.Windows.Forms.CheckBox NPCTypesTintCheckBox; + public System.Windows.Forms.CheckBox UpdateExistingNPCTypesCheckbox; + private System.Windows.Forms.SaveFileDialog SQLFileDialog; + private System.Windows.Forms.Button GenerateSQLButton; + private System.Windows.Forms.Button SQLCancelButton; + private System.Windows.Forms.GroupBox MiscOptions; + public System.Windows.Forms.CheckBox CoalesceWaypoints; + public System.Windows.Forms.TextBox SpawnNameFilter; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.ToolTip toolTip1; + public System.Windows.Forms.CheckBox InvisibleMenCheckBox; + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/GenerateSQLForm.cs b/utils/EQExtractor2/EQExtractor2/GenerateSQLForm.cs new file mode 100644 index 000000000..d937b495f --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/GenerateSQLForm.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace EQExtractor2 +{ + public partial class GenerateSQLForm : Form + { + public string FileName = ""; + + public GenerateSQLForm() + { + InitializeComponent(); + } + + public void RecalculateBaseInsertIDs() + { + UInt32 ZoneNumber = Convert.ToUInt32(ZoneIDTextBox.Text); + + NPCTypesTextBox.Text = ((ZoneNumber * 1000) + (VersionSelector.Value * 100)).ToString(); + SpawnEntryTextBox.Text = NPCTypesTextBox.Text; + SpawnGroupTextBox.Text = NPCTypesTextBox.Text; + Spawn2TextBox.Text = NPCTypesTextBox.Text; + GridTextBox.Text = NPCTypesTextBox.Text; + ObjectTextBox.Text = NPCTypesTextBox.Text; + GroundSpawnTextBox.Text = NPCTypesTextBox.Text; + MerchantTextBox.Text = NPCTypesTextBox.Text; + DoorsTextBox.Text = ((VersionSelector.Value * 1000)).ToString(); + } + + private void VersionSelector_ValueChanged(object sender, EventArgs e) + { + RecalculateBaseInsertIDs(); + } + + private void ZoneIDTextBox_Validated(object sender, EventArgs e) + { + RecalculateBaseInsertIDs(); + } + + private void SpawnCheckBox_CheckedChanged(object sender, EventArgs e) + { + if (!SpawnCheckBox.Checked) + { + GridCheckBox.Checked = false; + GridCheckBox.Enabled = false; + } + else + GridCheckBox.Enabled = true; + } + + private void UpdateExistingNPCTypesCheckbox_CheckedChanged(object sender, EventArgs e) + { + if (UpdateExistingNPCTypesCheckbox.Checked) + { + SpawnCheckBox.Checked = false; + SpawnCheckBox.Enabled = false; + GridCheckBox.Checked = false; + GridCheckBox.Enabled = false; + MerchantCheckBox.Checked = false; + MerchantCheckBox.Enabled = false; + ZoneCheckBox.Checked = false; + ZonePointCheckBox.Checked = false; + GridCheckBox.Checked = false; + DoorCheckBox.Checked = false; + ObjectCheckBox.Checked = false; + GroundSpawnCheckBox.Checked = false; + } + else + { + SpawnCheckBox.Enabled = true; + GridCheckBox.Enabled = true; + MerchantCheckBox.Enabled = true; + } + } + + private void GenerateSQLButton_Click(object sender, EventArgs e) + { + if (SQLFileDialog.ShowDialog() == DialogResult.OK) + { + FileName = SQLFileDialog.FileName; + this.DialogResult = DialogResult.OK; + //Close(); + //return; + } + + } + + private void SQLCancelButton_Click(object sender, EventArgs e) + { + this.DialogResult = DialogResult.Cancel; + } + + + } + + +} diff --git a/utils/EQExtractor2/EQExtractor2/GenerateSQLForm.resx b/utils/EQExtractor2/EQExtractor2/GenerateSQLForm.resx new file mode 100644 index 000000000..183eb2750 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/GenerateSQLForm.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 580, 17 + + + 17, 17 + + \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/InternalTypes.cs b/utils/EQExtractor2/EQExtractor2/InternalTypes.cs new file mode 100644 index 000000000..645aaea00 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/InternalTypes.cs @@ -0,0 +1,691 @@ +// +// Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net). Distributed under GPL version 2. +// +// + +using System; +using System.Collections.Generic; + +namespace EQExtractor2.InternalTypes +{ + class Constants + { + public const UInt32 MATERIAL_HEAD = 0; + public const UInt32 MATERIAL_CHEST = 1; + public const UInt32 MATERIAL_PRIMARY = 7; + public const UInt32 MATERIAL_SECONDARY = 8; + } + + class GroundSpawnStruct + { + public UInt32 DropID; + public string Name; + public UInt16 ZoneID; + public UInt16 InstanceID; + public float Heading; + public float y; + public float x; + public float z; + public UInt32 ObjectType; + } + + public class NewZoneStruct + { + public NewZoneStruct() + { + FogRed = new byte[4]; + FogGreen = new byte[4]; + FogBlue = new byte[4]; + FogMinClip = new float[4]; + FogMaxClip = new float[4]; + } + + public string ShortName; + public string LongName; + public byte Type; + public byte[] FogRed; + public byte[] FogGreen; + public byte[] FogBlue; + public float[] FogMinClip; + public float[] FogMaxClip; + public float Gravity; + public byte TimeType; + public byte Sky; + public float ZEM; + public float SafeY; + public float SafeX; + public float SafeZ; + public float MinZ; + public float MaxZ; + public float UnderWorld; + public float MinClip; + public float MaxClip; + public string ShortName2; + public UInt16 ZoneID; + public UInt16 InstanceID; + public byte FallDamage; + public float FogDensity; + } + + public class ZoneEntryStruct + { + public ZoneEntryStruct() + { + SlotColour = new UInt32[9]; + Equipment = new UInt32[9]; + } + + public string SpawnName; + public UInt32 SpawnID; + public bool Findable; + public Byte Level; + public Byte IsNPC; + public uint Showname; + public uint TargetableWithHotkey; + public uint Targetable; + public uint ShowHelm; + public uint Gender; + public byte OtherData; + public string DestructableString1; + public string DestructableString2; + public string DestructableString3; + public UInt32 DestructableUnk1; + public UInt32 DestructableUnk2; + public UInt32 DestructableID1; + public UInt32 DestructableID2; + public UInt32 DestructableID3; + public UInt32 DestructableID4; + public UInt32 DestructableUnk3; + public UInt32 DestructableUnk4; + public UInt32 DestructableUnk5; + public UInt32 DestructableUnk6; + public UInt32 DestructableUnk7; + public UInt32 DestructableUnk8; + public UInt32 DestructableUnk9; + public byte DestructableByte; + public float Size; + public byte Face; + public float WalkSpeed; + public float RunSpeed; + public UInt32 Race; + public byte PropCount; + public UInt32 BodyType; + public byte HairColor; + public byte BeardColor; + public byte EyeColor1; + public byte EyeColor2; + public byte HairStyle; + public byte Beard; + public UInt32 DrakkinHeritage; + public UInt32 DrakkinTattoo; + public UInt32 DrakkinDetails; + public UInt32 Deity; + public byte Class; + public byte EquipChest2; + public byte Helm; + public string LastName; + public UInt32 PetOwnerID; + public float YPos; + public float Heading; + public float XPos; + public float ZPos; + public UInt32[] SlotColour; + public byte ArmorTintRed; + public byte ArmorTintGreen; + public byte ArmorTintBlue; + public UInt32 MeleeTexture1; + public UInt32 MeleeTexture2; + public UInt32[] Equipment; + public string Title; + public string Suffix; + public byte IsMercenary; + public UInt32 Padding5; + public UInt32 Padding7; + public UInt32 Padding26; + + } + + public class NPCSpawn + { + public NPCSpawn(UInt32 SpawnID, UInt32 Spawn2DBID, UInt32 NPCTypeID, string Name) + { + this.SpawnID = SpawnID; + this.Spawn2DBID = Spawn2DBID; + this.NPCTypeID = NPCTypeID; + this.Name = Name; + } + + public bool DoesHaveHighResWaypoints() + { + return HasHighResWaypoints; + } + + public void SetHasHighResWaypoints() + { + HasHighResWaypoints = true; + } + + public void AddWaypoint(Position p, bool HighRes) + { + Waypoints.Add(p); + if (HighRes) + HasHighResWaypoints = true; + } + + public UInt32 SpawnID; + public UInt32 Spawn2DBID; + public UInt32 NPCTypeID; + public string Name; + bool HasHighResWaypoints = false; + public List Waypoints = new List(); + } + + public class NPCTypeList + { + List _NPCTypeList = new List(); + + public void AddNPCType(NPCType NewNPC) + { + _NPCTypeList.Add(NewNPC); + } + + public List GetUniqueSpawns() + { + foreach (NPCType n in _NPCTypeList) + { + if (NPCType.IsPlayableRace(n.Race) || NPCType.IsGuardRace(n.Race)) + n.Unique = true; + else + n.Unique = false; + } + + for (int i = 0; i < _NPCTypeList.Count; ++i) + { + for (int j = 0; j < _NPCTypeList.Count; ++j) + { + if ((i != j) && (_NPCTypeList[i].Name == _NPCTypeList[j].Name)) + { + _NPCTypeList[i].Unique = false; + } + } + } + + List Results = new List(); + + foreach (NPCType n in _NPCTypeList) + if (n.Unique) + Results.Add(n); + + return Results; + } + + + public UInt32 FindNPCType(string Name, Byte Level, uint Gender, float Size, Byte Face, float WalkSpeed, float RunSpeed, UInt32 Race, + UInt32 BodyType, Byte HairColor, Byte BeardColor, Byte EyeColor1, Byte EyeColor2, Byte HairStyle, Byte Beard, + UInt32 DrakkinHeritage, UInt32 DrakkinTattoo, UInt32 DrakkinDetails, UInt32 Deity, Byte Class, Byte EquipChest2, + Byte Helm, string LastName) + { + foreach (NPCType n in _NPCTypeList) + { + if (n.Name != Name) + continue; + + if (n.Level != Level) + continue; + + if (n.Gender != Gender) + continue; + + if (n.Size != Size) + continue; + + if (n.Face != Face) + continue; + + if (n.WalkSpeed != WalkSpeed) + continue; + + if (n.RunSpeed != RunSpeed) + continue; + + if (n.Race != Race) + continue; + + if (n.BodyType != BodyType) + continue; + + if (n.HairColor != HairColor) + continue; + + if (n.BeardColor != BeardColor) + continue; + + if (n.EyeColor1 != EyeColor1) + continue; + + if (n.EyeColor2 != EyeColor2) + continue; + + if (n.HairStyle != HairStyle) + continue; + + if (n.Beard != Beard) + continue; + + if (n.DrakkinHeritage != DrakkinHeritage) + continue; + + if (n.DrakkinTattoo != DrakkinTattoo) + continue; + + if (n.DrakkinDetails != DrakkinDetails) + continue; + + if (n.Deity != Deity) + continue; + + if (n.Class != Class) + continue; + + if (n.EquipChest2 != EquipChest2) + continue; + + if (n.Helm != Helm) + continue; + + if (n.LastName != LastName) + continue; + + //Console.WriteLine("Found NPCType already in list, returning {0}", n.DBID); + + return n.DBID; + + } + + return 0; + } + } + + public class NPCSpawnList + { + public List _NPCSpawnList = new List(); + + public NPCSpawn GetNPC(UInt32 SpawnID) + { + foreach (NPCSpawn s in _NPCSpawnList) + { + if (s.SpawnID == SpawnID) + return s; + + } + + return null; + } + + public bool DoesHaveHighResWaypoints(UInt32 SpawnID) + { + foreach (NPCSpawn s in _NPCSpawnList) + { + if (s.SpawnID == SpawnID) + { + if (s.DoesHaveHighResWaypoints()) + return true; + else + return false; + } + } + return false; + } + + public void AddNPCSpawn(UInt32 DBID, UInt32 SpawnID, UInt32 NPCTypeID, string Name) + { + NPCSpawn NewSpawn = new NPCSpawn(DBID, SpawnID, NPCTypeID, Name); + + _NPCSpawnList.Add(NewSpawn); + } + + + public void AddWaypoint(UInt32 SpawnID, Position p, bool HighRes) + { + foreach (NPCSpawn n in _NPCSpawnList) + { + if (n.SpawnID == SpawnID) + { + n.AddWaypoint(p, HighRes); + return; + } + } + } + } + + public struct ZonePoint + { + + public ZonePoint(UInt32 Number, UInt16 ZoneID, UInt16 Instance, float x, float y, float z, + float TargetX, float TargetY, float TargetZ, float Heading, UInt32 TargetZoneID) + { + this.Number = Number; + this.ZoneID = ZoneID; + this.Instance = Instance; + this.x = x; + this.y = y; + this.z = z; + this.TargetX = TargetX; + this.TargetY = TargetY; + this.TargetZ = TargetZ; + this.Heading = Heading; + this.TargetZoneID = TargetZoneID; + + } + + public UInt32 Number; + public UInt16 ZoneID; + public UInt16 Instance; + public float x; + public float y; + public float z; + public float TargetX; + public float TargetY; + public float TargetZ; + public float Heading; + public UInt32 TargetZoneID; + }; + + struct Item + { + public UInt32 StackSize; + public UInt32 Slot; + public UInt32 MerchantSlot; + public UInt32 Price; + public Int32 Quantity; + public string Name; + public string Lore; + public string IDFile; + public UInt32 ID; + }; + + struct MerchantItem + { + public MerchantItem(UInt32 ItemID, string Name, UInt32 Slot, int Quantity) + { + this.ItemID = ItemID; + this.Name = Name; + this.Slot = Slot; + this.Quantity = Quantity; + } + + public UInt32 ItemID; + public string Name; + public UInt32 Slot; + public int Quantity; + } + + struct Merchant + { + public Merchant(UInt32 SpawnID) + { + this.SpawnID = SpawnID; + this.Items = new List(); + + } + + public UInt32 SpawnID; + public List Items; + } + + public class NPCType + { + public NPCType(UInt32 DBID, string Name, Byte Level, uint Gender, float Size, Byte Face, float WalkSpeed, float RunSpeed, UInt32 Race, + UInt32 BodyType, Byte HairColor, Byte BeardColor, Byte EyeColor1, Byte EyeColor2, Byte HairStyle, Byte Beard, + UInt32 DrakkinHeritage, UInt32 DrakkinTattoo, UInt32 DrakkinDetails, UInt32 Deity, Byte Class, Byte EquipChest2, + Byte Helm, string LastName, bool Findable, UInt32 MeleeTexture1, UInt32 MeleeTexture2, + UInt32 ArmorTintRed, UInt32 ArmorTintGreen, UInt32 ArmorTintBlue, UInt32[] SlotColour) + { + this.DBID = DBID; + this.Name = Name; + this.Level = Level; + this.Gender = Gender; + this.Size = Size; + this.Face = Face; + this.WalkSpeed = WalkSpeed; + this.RunSpeed = RunSpeed; + this.Race = Race; + this.BodyType = BodyType; + this.HairColor = HairColor; + this.BeardColor = BeardColor; + this.EyeColor1 = EyeColor1; + this.EyeColor2 = EyeColor2; + this.HairStyle = HairStyle; + this.Beard = Beard; + this.DrakkinHeritage = DrakkinHeritage; + this.DrakkinTattoo = DrakkinTattoo; + this.DrakkinDetails = DrakkinDetails; + this.Deity = Deity; + this.Class = Class; + this.EquipChest2 = EquipChest2; + this.Helm = Helm; + this.LastName = LastName; + this.Findable = Findable; + this.MeleeTexture1 = MeleeTexture1; + this.MeleeTexture2 = MeleeTexture2; + this.ArmorTintRed = ArmorTintRed; + this.ArmorTintGreen = ArmorTintGreen; + this.ArmorTintBlue = ArmorTintBlue; + this.SlotColour = SlotColour; + this.Unique = true; + + + } + public static bool IsPlayableRace(UInt32 Race) + { + if (Race <= 12 || Race == 128 || Race == 130 || Race == 330 || Race == 522) + return true; + + return false; + } + + static UInt32[] GuardRaces = { 44, 67, 71, 77, 78, 81, 90, 92, 93, 94, 106, 112, 139, 183, 239 }; + + public static bool IsGuardRace(UInt32 Race) + { + if (Array.IndexOf(GuardRaces, Race) >= 0) + return true; + + return false; + } + + public static bool IsMount(string Name) + { + if (Name.IndexOf("`s_Mount") >= 0) + return true; + + return false; + } + + public UInt32 DBID; + public string Name = ""; + public Byte Level = 0; + public uint Gender = 0; + public float Size = 0; + public Byte Face = 0; + public float WalkSpeed = 0; + public float RunSpeed = 0; + public UInt32 Race = 0; + public UInt32 BodyType = 0; + public Byte HairColor = 0; + public Byte BeardColor = 0; + public Byte EyeColor1 = 0; + public Byte EyeColor2 = 0; + public Byte HairStyle = 0; + public Byte Beard = 0; + public UInt32 DrakkinHeritage = 0; + public UInt32 DrakkinTattoo = 0; + public UInt32 DrakkinDetails = 0; + public UInt32 Deity = 0; + public Byte Class = 0; + public Byte EquipChest2 = 0; + public Byte Helm = 0; + public string LastName = ""; + public bool Findable = false; + public UInt32 MeleeTexture1 = 0; + public UInt32 MeleeTexture2 = 0; + public UInt32 ArmorTintRed = 0; + public UInt32 ArmorTintGreen = 0; + public UInt32 ArmorTintBlue = 0; + public UInt32[] SlotColour; + + public bool Unique = true; + } + + public class Position + { + public Position() + { + } + + public Position(float x, float y, float z, float heading, DateTime TimeStamp) + { + this.x = x; + this.y = y; + this.z = z; + this.heading = heading; + this.TimeStamp = TimeStamp; + } + + public double Distance(Position p) + { + float XDiff = this.x - p.x; + float YDiff = this.y - p.y; + + return Math.Sqrt((XDiff * XDiff) + (YDiff * YDiff)); + } + + public float x, y, z, heading; + public DateTime TimeStamp; + } + + public class PositionUpdate + { + public PositionUpdate() + { + p = new Position(); + } + + public UInt32 SpawnID; + + public Position p; + + public bool HighRes; + } + + public class ExplorerSpawnRecord + { + public ExplorerSpawnRecord(UInt32 ID, string Name) + { + this.ID = ID; + this.Name = Name; + } + + public UInt32 ID; + public string Name; + } + + public class Door + { + public Door(string Name, float YPos, float XPos, float ZPos, float Heading, UInt32 Incline, Int32 Size, Byte ID, Byte OpenType, + Byte StateAtSpawn, Byte InvertState, Int32 DoorParam, string DestZone, float DestX, float DestY, float DestZ, + float DestHeading) + { + this.Name = Name; + this.YPos = YPos; + this.XPos = XPos; + this.ZPos = ZPos; + this.Heading = Heading; + this.Incline = Incline; + this.Size = Size; + this.ID = ID; + this.OpenType = OpenType; + this.StateAtSpawn = StateAtSpawn; + this.InvertState = InvertState; + this.DoorParam = DoorParam; + this.DestZone = DestZone; + this.DestX = DestX; + this.DestY = DestY; + this.DestZ = DestZ; + this.DestHeading = DestHeading; + } + + public string Name; + public float YPos; + public float XPos; + public float ZPos; + public float Heading; + public UInt32 Incline; + public Int32 Size; + public Byte ID; + public Byte OpenType; + public Byte StateAtSpawn; + public Byte InvertState; + public Int32 DoorParam; + public string DestZone; + public float DestX; + public float DestY; + public float DestZ; + public float DestHeading; + + } + class MerchantManager + { + public bool AddMerchant(UInt32 SpawnID) + { + foreach (Merchant m in MerchantList) + { + if (m.SpawnID == SpawnID) + return false; + } + + Merchant nm = new Merchant(SpawnID); + + MerchantList.Add(nm); + + return true; + } + + public int FindMerchant(UInt32 SpawnID) + { + for (int i = 0; i < MerchantList.Count; ++i) + { + if (MerchantList[i].SpawnID == SpawnID) + return i; + } + + return -1; + } + + public bool AddMerchantItem(UInt32 SpawnID, UInt32 ItemID, string Name, UInt32 Slot, int Quantity) + { + + int i = FindMerchant(SpawnID); + + + if (i < 0) + return false; + + for (int j = 0; j < MerchantList[i].Items.Count; ++j) + if (MerchantList[i].Items[j].ItemID == ItemID) + return false; + + MerchantItem NewItem = new MerchantItem(ItemID, Name, Slot, Quantity); + + MerchantList[i].Items.Add(NewItem); + + return true; + + } + + + public List MerchantList = new List(); + + } + +} diff --git a/utils/EQExtractor2/EQExtractor2/LogForm.Designer.cs b/utils/EQExtractor2/EQExtractor2/LogForm.Designer.cs new file mode 100644 index 000000000..b1531ee72 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/LogForm.Designer.cs @@ -0,0 +1,67 @@ +namespace EQExtractor2 +{ + partial class LogForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.ConsoleWindow = new System.Windows.Forms.ListBox(); + this.SuspendLayout(); + // + // ConsoleWindow + // + this.ConsoleWindow.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.ConsoleWindow.Font = new System.Drawing.Font("Courier New", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.ConsoleWindow.FormattingEnabled = true; + this.ConsoleWindow.HorizontalScrollbar = true; + this.ConsoleWindow.ItemHeight = 14; + this.ConsoleWindow.Location = new System.Drawing.Point(12, 12); + this.ConsoleWindow.Name = "ConsoleWindow"; + this.ConsoleWindow.Size = new System.Drawing.Size(938, 326); + this.ConsoleWindow.TabIndex = 35; + // + // LogForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(962, 355); + this.Controls.Add(this.ConsoleWindow); + this.Name = "LogForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.Manual; + this.Text = "EQExtractor2 Debug Log"; + this.Closing += new System.ComponentModel.CancelEventHandler(this.LogForm_FormClosing); + this.ResumeLayout(false); + + } + + #endregion + + public System.Windows.Forms.ListBox ConsoleWindow; + + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/LogForm.cs b/utils/EQExtractor2/EQExtractor2/LogForm.cs new file mode 100644 index 000000000..c836aa1af --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/LogForm.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace EQExtractor2 +{ + public partial class LogForm : Form + { + public LogForm() + { + InitializeComponent(); + } + + private void LogForm_FormClosing(Object sender, System.ComponentModel.CancelEventArgs e) + { + e.Cancel = true; + this.Hide(); + } + + } +} diff --git a/utils/EQExtractor2/EQExtractor2/LogForm.resx b/utils/EQExtractor2/EQExtractor2/LogForm.resx new file mode 100644 index 000000000..1af7de150 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/LogForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/OpcodeManager.cs b/utils/EQExtractor2/EQExtractor2/OpcodeManager.cs new file mode 100644 index 000000000..21b980190 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/OpcodeManager.cs @@ -0,0 +1,161 @@ +// +// Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net). Distributed under GPL version 2. +// +// + +using System; +using System.IO; +using System.Collections.Generic; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.OpCodes +{ + public class OpCode + { + public UInt32 Value; + public string Name; + public ExplorerMethod Explorer; + public UInt32 Count; + + public OpCode(string inName, UInt32 inValue) + { + Name = inName; + Value = inValue; + Explorer = null; + Count = 0; + + } + } + + public delegate void ExplorerMethod(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction); + + public class OpCodeManager + { + public List OpCodeList = new List(); + + public bool Init(string ConfFile, ref string ErrorMessage) + { + StreamReader sr; + + string PatchFile = ConfFile; + + try + { + sr = new StreamReader(PatchFile); + } + catch + { + ErrorMessage = "Unable to open opcode file " + PatchFile; + return false; + } + + string Line; + + while ((Line = sr.ReadLine()) != null) + { + if (Line.Length == 0) + continue; + if (Line[0] == '#') + continue; + + int EqualsSign = Line.IndexOf("="); + + if (EqualsSign < 0) + continue; + string OPCodeName = Line.Substring(0, EqualsSign); + string OPCodeValue = Line.Substring(EqualsSign + 1, 6); + UInt32 OPCodeNumber = 0; + try + { + OPCodeNumber = Convert.ToUInt32(OPCodeValue, 16); + } + catch + { + ErrorMessage = "Malformed OPCode value for " + OPCodeName; + OPCodeNumber = 0; + } + + if (OPCodeNumber > 0) + AddOpCode(OPCodeName, OPCodeNumber); + } + + return true; + } + + public void AddOpCode(string OpCodeName, UInt32 OpCodeValue) + { + OpCode NewOpCode = new OpCode(OpCodeName, OpCodeValue); + OpCodeList.Add(NewOpCode); + + } + + public string OpCodeToName(int OpCodeValue) + { + foreach (OpCode oc in OpCodeList) + { + if (oc.Value == OpCodeValue) + return oc.Name; + } + + return "OP_Unknown"; + } + + public OpCode GetOpCodeByNumber(int OpCodeValue) + { + foreach (OpCode oc in OpCodeList) + { + if (oc.Value == OpCodeValue) + return oc; + } + + return null; + } + + public UInt32 OpCodeNameToNumber(string Name) + { + foreach (OpCode oc in OpCodeList) + { + if (oc.Name == Name) + return oc.Value; + } + return 0; + } + + public void ResetCounts() + { + foreach (OpCode oc in OpCodeList) + oc.Count = 0; + } + + public void RegisterExplorer(string Name, ExplorerMethod Explorer) + { + foreach (OpCode oc in OpCodeList) + { + if (oc.Name == Name) + { + oc.Explorer = Explorer; + return; + } + } + } + + public void UnRegisterExplorer(string Name) + { + // Designed to be used when a decoder wants to use some of it's base classes explorers, but some need to be UnRegistered + // as they are incompatible with the current patch version. + // i.e. + // base.RegisterExplorers(); + // UnRegisterExplorer("OP_NotCompatible"); + // + foreach (OpCode oc in OpCodeList) + { + if (oc.Name == Name) + { + oc.Explorer = null; + return; + } + } + } + } +} diff --git a/utils/EQExtractor2/EQExtractor2/PacketDotNet.xml b/utils/EQExtractor2/EQExtractor2/PacketDotNet.xml new file mode 100644 index 000000000..3f74595da --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PacketDotNet.xml @@ -0,0 +1,5278 @@ + + + + /home/cmorgan/packetnet_git/PacketDotNet/bin/Release/PacketDotNet + + + + + Represents an application layer packet as described at http://en.wikipedia.org/wiki/Application_Layer + + + + + ApplicationPacket constructor + + + A + + + + Represents a Layer 2 protocol. + + + + + DataLinkPacket constructor + + + A + + + + See http://en.wikipedia.org/wiki/Ethernet#Ethernet_frame_types_and_the_EtherType_field + + + + + Construct a new ethernet packet from source and destination mac addresses + + + + + Create an EthernetPacket from a byte array + + + A + + A + + + + Create an EthernetPacket from a byte array and a Timeval + + + A + + A + + A + + + + Payload packet, overridden to set the 'Type' field based on + the type of packet being used here if the PayloadPacket is being set + + + + MAC address of the host where the packet originated from. + + + MAC address of the host where the packet originated from. + + + + Type of packet that this ethernet packet encapsulates + + + + Fetch ascii escape sequence of the color associated with this packet type. + + + + Used by the EthernetPacket constructor. Located here because the LinuxSLL constructor + also needs to perform the same operations as it contains an ethernet type + + + A + + A + + A + + A + + + Convert this ethernet packet to a readable string. + + + Generate string with contents describing this ethernet packet. + whether or not the string should contain ansi + color escape sequences. + + + + Convert a more verbose string. + + + + Generate a random EthernetPacket + TODO: could improve this routine to set a random payload as well + + + A + + + Copied from Pcap.Net @ 20091117 + + Code constants for well-defined ethernet protocols. + EtherType is a two-octet field in an Ethernet frame, as defined by the Ethernet II framing networking standard. + It is used to indicate which protocol is encapsulated in the payload. + Also contains entries taken from linux/if_ether.h and tcpdump/ethertype.h + + + + No Ethernet type + + + + + Internet Protocol, Version 4 (IPv4) + + + + + Address Resolution Protocol (ARP) + + + + + Reverse Address Resolution Protocol (RARP) + + + + + AppleTalk (Ethertalk) + + + + + AppleTalk Address Resolution Protocol (AARP) + + + + + VLAN-tagged frame (IEEE 802.1Q) + + + + + Novell IPX (alt) + + + + + Novell + + + + + Internet Protocol, Version 6 (IPv6) + + + + + MAC Control + + + + + CobraNet + + + + + MPLS unicast + + + + + MPLS multicast + + + + + PPPoE Discovery Stage + + + + + PPPoE Session Stage + + + + + EAP over LAN (IEEE 802.1X) + + + + + HyperSCSI (SCSI over Ethernet) + + + + + ATA over Ethernet + + + + + EtherCAT Protocol + + + + + Provider Bridging (IEEE 802.1ad) + + + + + AVB Transport Protocol (AVBTP) + + + + + Link Layer Discovery Protocol (LLDP) + + + + + SERCOS III + + + + + Circuit Emulation Services over Ethernet (MEF-8) + + + + + HomePlug + + + + + MAC security (IEEE 802.1AE) + + + + + Precision Time Protocol (IEEE 1588) + + + + + IEEE 802.1ag Connectivity Fault Management (CFM) Protocol / ITU-T Recommendation Y.1731 (OAM) + + + + + Fibre Channel over Ethernet + + + + + FCoE Initialization Protocol + + + + + Q-in-Q + + + + + Veritas Low Latency Transport (LLT) + + + + + Ethernet loopback packet + + + + + Ethernet echo packet + + + + + Base class for IPv4 and IPv6 packets that exports the common + functionality that both of these classes has in common + + + + + IpPacket constructor + + + A + + + + The default time to live value for Ip packets being constructed + + + + + Payload packet, overridden to set the NextHeader/Protocol based + on the type of payload packet when the payload packet is set + + + + + The destination address + + + + + The source address + + + + + The IP version + + + + + The protocol of the ip packet's payload + Named 'Protocol' in IPv4 + Named 'NextHeader' in IPv6' + + + + + The protocol of the ip packet's payload + Included along side Protocol for user convienence + + + + + The number of hops remaining before this packet is discarded + Named 'TimeToLive' in IPv4 + Named 'HopLimit' in IPv6 + + + + + The number of hops remaining for this packet + Included along side of TimeToLive for user convienence + + + + + ipv4 header length field, calculated for ipv6 packets + NOTE: This field is the number of 32bit words in the ip header, + ie. the number of bytes is 4x this value + + + + + ipv4 total number of bytes in the ipv4 header + payload, + ipv6 PayloadLength + IPv6Fields.HeaderLength + + + + + ipv6 payload length in bytes, + calculate from ipv4.TotalLength - (ipv4.HeaderLength * 4) + + + + + Adds a pseudo ip header to a given packet. Used to generate the full + byte array required to generate a udp or tcp checksum. + + + A + + A + + + + Convert an ip address from a byte[] + + + A + + A + + A + + A + + + + Called by IPv4 and IPv6 packets to parse their packet payload + + + A + + A + + A + + A + + A + + + + Returns the IpPacket inside of the Packet p or null if + there is no encapsulated packet + + + A + + A + + + + Generate a random packet of a specific ip version + + + A + + A + + + + Base class for all packet types. + Defines helper methods and accessors for the architecture that underlies how + packets interact and store their data. + + + + + Basic Packet constructor + + + A + + + + PosixTimeval of this packet, can be the packet arrival time + or the packet creation time + + + + + Returns true if we already have a contiguous byte[] in either + of these conditions: + - This packet's header byte[] and payload byte[] are the same instance + or + - This packet's header byte[] and this packet's payload packet + are the same instance and the offsets indicate that the bytes + are contiguous + + + + + The packet that is carrying this one + + + + + Returns a + + + + + Packet that this packet carries if one is present. + Note that the packet MAY have a null PayloadPacket but + a non-null PayloadData + + + + + Payload byte[] if one is present. + Note that the packet MAY have a null PayloadData but a + non-null PayloadPacket + + + + + byte[] containing this packet and its payload + NOTE: Use 'public virtual ByteArraySegment BytesHighPerformance' for highest performance + + + + + The option to return a ByteArraySegment means that this method + is higher performance as the data can start at an offset other than + the first byte. + + + + + Color used when generating the text description of a packet + + + + + Turns an array of bytes into an EthernetPacket + + The packets caught + An ethernet packet which has references to the higher protocols + + + + Parse a raw packet into its specific packets and payloads + + + A + + A + + + + Parse bytes into a packet + + + A + + A + + A + + A + + + + Used to ensure that values like checksums and lengths are + properly updated + + + + + Called to ensure that calculated values are updated before + the packet bytes are retrieved + Classes should override this method to update things like + checksums and lengths that take too much time or are too complex + to update for each packet parameter change + + + + + Returns a ansi colored string. This routine calls + the ToColoredString() of the payload packet if one + is present. + + + A + + A + + + + Returns a verbose ansi colored string. This routine calls + the ToColoredVerboseString() of the payload packet if one + is present. + + + A + + A + + + + Session layer packet + + + + + Constructor + + + A + + + + TcpPacket + See: http://en.wikipedia.org/wiki/Transmission_Control_Protocol + + + + + Create a new TCP packet from values + + + + + byte[]/int offset constructor, timeval defaults to the current time + + + A + + A + + + + byte[]/int offset/PosixTimeval constructor + + + A + + A + + A + + + + Constructor when this packet is encapsulated in another packet + + + A + + A + + A + + A + + + + 20 bytes is the smallest tcp header + + + + Fetch the port number on the source host. + + + Fetches the port number on the destination host. + + + Fetch the packet sequence number. + + + Fetch the packet acknowledgment number. + + + The size of the tcp header in 32bit words + + + + The size of the receive window, which specifies the number of + bytes (beyond the sequence number in the acknowledgment field) that + the receiver is currently willing to receive. + + + + + Tcp checksum field value of type UInt16 + + + + Check if the TCP packet is valid, checksum-wise. + + + + True if the tcp checksum is valid + + + + Check the URG flag, flag indicates if the urgent pointer is valid. + + + Check the ACK flag, flag indicates if the ack number is valid. + + + Check the PSH flag, flag indicates the receiver should pass the + data to the application as soon as possible. + + + + Check the RST flag, flag indicates the session should be reset between + the sender and the receiver. + + + + Check the SYN flag, flag indicates the sequence numbers should + be synchronized between the sender and receiver to initiate + a connection. + + + + Check the FIN flag, flag indicates the sender is finished sending. + + + + ECN flag + + + + + CWR flag + + + + Fetch ascii escape sequence of the color associated with this packet type. + + + Fetch the urgent pointer. + + + + Bytes that represent the tcp options + + + A + + + Computes the TCP checksum, optionally updating the TCP checksum header. + + The calculated TCP checksum. + + + + Update the checksum value. + + + + Convert this TCP packet to a readable string. + + + Generate string with contents describing this TCP packet. + whether or not the string should contain ansi + color escape sequences. + + + + Convert this TCP packet to a verbose. + + + + Returns the TcpPacket embedded in Packet p or null if + there is no embedded TcpPacket + + + + + Create a randomized tcp packet with the given ip version + + + A + + + + User datagram protocol + See http://en.wikipedia.org/wiki/Udp + + + + + Create from values + + + A + + A + + + + byte[]/int offset constructor, timeval defaults to the current time + + + A + + A + + + + byte[]/int offset/PosixTimeval constructor + + + A + + A + + A + + + + Constructor when this packet is encapsulated in another packet + + + A + + A + + A + + A + + + Fetch the port number on the source host. + + + Fetch the port number on the target host. + + + + Length in bytes of the header and payload, minimum size of 8, + the size of the Udp header + + + + Fetch the header checksum. + + + Check if the UDP packet is valid, checksum-wise. + + + + True if the udp checksum is valid + + + + Fetch ascii escape sequence of the color associated with this packet type. + + + + Update the Udp length + + + + + Calculates the UDP checksum, optionally updating the UDP checksum header. + + The calculated UDP checksum. + + + + Update the checksum value. + + + + Convert this UDP packet to a readable string. + + + Generate string with contents describing this UDP packet. + whether or not the string should contain ansi + color escape sequences. + + + + + Returns the UdpPacket inside of the Packet p or null if + there is no encapsulated packet + + + A + + A + + + + Generate a random packet + + + A + + + + Ethernet protocol field encoding information. + + + + Width of the ethernet type code in bytes. + + + Position of the destination MAC address within the ethernet header. + + + Position of the source MAC address within the ethernet header. + + + Position of the ethernet type field within the ethernet header. + + + Total length of an ethernet header in bytes. + + + + size of an ethernet mac address in bytes + + + + + Transport layer packet + + + + + Constructor + + + A + + + + Options for use when creating a transport layer checksum + + + + + No extra options + + + + + Attach a pseudo IP header to the transport data being checksummed + + + + + The Checksum version + + + + + Calculates the transport layer checksum, either for the + tcp or udp packet + + + + + + A + + + + Determine if the transport layer checksum is valid + + + A + + A + + + + String representation of an IP protocol value + + + + 'Human-readable' IP protocol descriptions. + + + Fetch a protocol description. + the code associated with the message. + + a message describing the significance of the IP protocol. + + + + String constants for color console output. +

+ This file contains control sequences to print color text on a text + console capable of interpreting and displaying control sequences. +

+ A capable console would be + unix bash, os/2 shell, or command.com w/ ansi.sys loaded +

+ Chris Cheetham + +
+ + + Delimits the start of an ansi color sequence, the color code goes after this + + + + + Delimits the stop of the ansi color sequence, the color code comes before this + + + + + Defines the lengths and positions of the udp fields within + a udp packet + + + + Length of a UDP port in bytes. + + + Length of the header length field in bytes. + + + Length of the checksum field in bytes. + + + Position of the source port. + + + Position of the destination port. + + + Position of the header length. + + + Position of the header checksum length. + + + Length of a UDP header in bytes. + + + + Equivalent of System.IO.BinaryReader, but with either endianness, depending on + the EndianBitConverter it is constructed with. No data is buffered in the + reader; the client may seek within the stream at will. + + + + + Equivalent of System.IO.BinaryWriter, but with either endianness, depending on + the EndianBitConverter it is constructed with. + + Converter to use when reading data + Stream to read data from + + + + Constructs a new binary reader with the given bit converter, reading + to the given stream, using the given encoding. + + Converter to use when reading data + Stream to read data from + Encoding to use when reading character data + + + + Whether or not this reader has been disposed yet. + + + + + Decoder to use for string conversions. + + + + + Buffer used for temporary storage before conversion into primitives + + + + + Buffer used for temporary storage when reading a single character + + + + + Minimum number of bytes used to encode a character + + + + + The bit converter used to read values from the stream + + + + + The encoding used to read strings + + + + + Gets the underlying stream of the EndianBinaryReader. + + + + + Closes the reader, including the underlying stream.. + + + + + Seeks within the stream. + + Offset to seek to. + Origin of seek operation. + + + + Reads a single byte from the stream. + + The byte read + + + + Reads a single signed byte from the stream. + + The byte read + + + + Reads a boolean from the stream. 1 byte is read. + + The boolean read + + + + Reads a 16-bit signed integer from the stream, using the bit converter + for this reader. 2 bytes are read. + + The 16-bit integer read + + + + Reads a 32-bit signed integer from the stream, using the bit converter + for this reader. 4 bytes are read. + + The 32-bit integer read + + + + Reads a 64-bit signed integer from the stream, using the bit converter + for this reader. 8 bytes are read. + + The 64-bit integer read + + + + Reads a 16-bit unsigned integer from the stream, using the bit converter + for this reader. 2 bytes are read. + + The 16-bit unsigned integer read + + + + Reads a 32-bit unsigned integer from the stream, using the bit converter + for this reader. 4 bytes are read. + + The 32-bit unsigned integer read + + + + Reads a 64-bit unsigned integer from the stream, using the bit converter + for this reader. 8 bytes are read. + + The 64-bit unsigned integer read + + + + Reads a single-precision floating-point value from the stream, using the bit converter + for this reader. 4 bytes are read. + + The floating point value read + + + + Reads a double-precision floating-point value from the stream, using the bit converter + for this reader. 8 bytes are read. + + The floating point value read + + + + Reads a decimal value from the stream, using the bit converter + for this reader. 16 bytes are read. + + The decimal value read + + + + Reads a single character from the stream, using the character encoding for + this reader. If no characters have been fully read by the time the stream ends, + -1 is returned. + + The character read, or -1 for end of stream. + + + + Reads the specified number of characters into the given buffer, starting at + the given index. + + The buffer to copy data into + The first index to copy data into + The number of characters to read + The number of characters actually read. This will only be less than + the requested number of characters if the end of the stream is reached. + + + + + Reads the specified number of bytes into the given buffer, starting at + the given index. + + The buffer to copy data into + The first index to copy data into + The number of bytes to read + The number of bytes actually read. This will only be less than + the requested number of bytes if the end of the stream is reached. + + + + + Reads the specified number of bytes, returning them in a new byte array. + If not enough bytes are available before the end of the stream, this + method will return what is available. + + The number of bytes to read + The bytes read + + + + Reads the specified number of bytes, returning them in a new byte array. + If not enough bytes are available before the end of the stream, this + method will throw an IOException. + + The number of bytes to read + The bytes read + + + + Reads a 7-bit encoded integer from the stream. This is stored with the least significant + information first, with 7 bits of information per byte of value, and the top + bit as a continuation flag. This method is not affected by the endianness + of the bit converter. + + The 7-bit encoded integer read from the stream. + + + + Reads a 7-bit encoded integer from the stream. This is stored with the most significant + information first, with 7 bits of information per byte of value, and the top + bit as a continuation flag. This method is not affected by the endianness + of the bit converter. + + The 7-bit encoded integer read from the stream. + + + + Reads a length-prefixed string from the stream, using the encoding for this reader. + A 7-bit encoded integer is first read, which specifies the number of bytes + to read from the stream. These bytes are then converted into a string with + the encoding for this reader. + + The string read from the stream. + + + + Checks whether or not the reader has been disposed, throwing an exception if so. + + + + + Reads the given number of bytes from the stream, throwing an exception + if they can't all be read. + + Buffer to read into + Number of bytes to read + + + + Reads the given number of bytes from the stream if possible, returning + the number of bytes actually read, which may be less than requested if + (and only if) the end of the stream is reached. + + Buffer to read into + Number of bytes to read + Number of bytes actually read + + + + Disposes of the underlying stream. + + + + + Equivalent of System.IO.BinaryWriter, but with either endianness, depending on + the EndianBitConverter it is constructed with. + + + + + Constructs a new binary writer with the given bit converter, writing + to the given stream, using UTF-8 encoding. + + Converter to use when writing data + Stream to write data to + + + + Constructs a new binary writer with the given bit converter, writing + to the given stream, using the given encoding. + + Converter to use when writing data + Stream to write data to + Encoding to use when writing character data + + + + Whether or not this writer has been disposed yet. + + + + + Buffer used for temporary storage during conversion from primitives + + + + + Buffer used for Write(char) + + + + + The bit converter used to write values to the stream + + + + + The encoding used to write strings + + + + + Gets the underlying stream of the EndianBinaryWriter. + + + + + Closes the writer, including the underlying stream. + + + + + Flushes the underlying stream. + + + + + Seeks within the stream. + + Offset to seek to. + Origin of seek operation. + + + + Writes a boolean value to the stream. 1 byte is written. + + The value to write + + + + Writes a 16-bit signed integer to the stream, using the bit converter + for this writer. 2 bytes are written. + + The value to write + + + + Writes a 32-bit signed integer to the stream, using the bit converter + for this writer. 4 bytes are written. + + The value to write + + + + Writes a 64-bit signed integer to the stream, using the bit converter + for this writer. 8 bytes are written. + + The value to write + + + + Writes a 16-bit unsigned integer to the stream, using the bit converter + for this writer. 2 bytes are written. + + The value to write + + + + Writes a 32-bit unsigned integer to the stream, using the bit converter + for this writer. 4 bytes are written. + + The value to write + + + + Writes a 64-bit unsigned integer to the stream, using the bit converter + for this writer. 8 bytes are written. + + The value to write + + + + Writes a single-precision floating-point value to the stream, using the bit converter + for this writer. 4 bytes are written. + + The value to write + + + + Writes a double-precision floating-point value to the stream, using the bit converter + for this writer. 8 bytes are written. + + The value to write + + + + Writes a decimal value to the stream, using the bit converter for this writer. + 16 bytes are written. + + The value to write + + + + Writes a signed byte to the stream. + + The value to write + + + + Writes an unsigned byte to the stream. + + The value to write + + + + Writes an array of bytes to the stream. + + The values to write + + + + Writes a portion of an array of bytes to the stream. + + An array containing the bytes to write + The index of the first byte to write within the array + The number of bytes to write + + + + Writes a single character to the stream, using the encoding for this writer. + + The value to write + + + + Writes an array of characters to the stream, using the encoding for this writer. + + An array containing the characters to write + + + + Writes a string to the stream, using the encoding for this writer. + + The value to write. Must not be null. + value is null + + + + Writes a 7-bit encoded integer from the stream. This is stored with the least significant + information first, with 7 bits of information per byte of value, and the top + bit as a continuation flag. + + The 7-bit encoded integer to write to the stream + + + + Checks whether or not the writer has been disposed, throwing an exception if so. + + + + + Writes the specified number of bytes from the start of the given byte array, + after checking whether or not the writer has been disposed. + + The array of bytes to write from + The number of bytes to write + + + + Disposes of the underlying stream. + + + + + Implementation of EndianBitConverter which converts to/from big-endian + byte arrays. + + + + + Indicates the byte order ("endianess") in which data is converted using this class. + + + + + Indicates the byte order ("endianess") in which data is converted using this class. + + + Different computer architectures store data using different byte orders. "Big-endian" + means the most significant byte is on the left end of a word. "Little-endian" means the + most significant byte is on the right end of a word. + + true if this converter is little-endian, false otherwise. + + + + Copies the specified number of bytes from value to buffer, starting at index. + + The value to copy + The number of bytes to copy + The buffer to copy the bytes into + The index to start at + + + + Returns a value built from the specified number of bytes from the given buffer, + starting at index. + + The data in byte array format + The first index to use + The number of bytes to use + The value built from the given bytes + + + + A class to allow the conversion of doubles to string representations of + their exact decimal values. The implementation aims for readability over + efficiency. + + + + + Private class used for manipulating sequences of decimal digits. + + + + + Constructs an arbitrary decimal expansion from the given long. + The long must not be negative. + + + + Digits in the decimal expansion, one byte per digit + + + + How many digits are *after* the decimal point + + + + + Multiplies the current expansion by the given amount, which should + only be 2 or 5. + + + + + Shifts the decimal point; a negative value makes + the decimal expansion bigger (as fewer digits come after the + decimal place) and a positive value makes the decimal + expansion smaller. + + + + + Removes leading/trailing zeroes from the expansion. + + + + + Converts the value to a proper decimal string representation. + + + + + Converts the given double to a string representation of its + exact decimal value. + + The double to convert. + A string representation of the double's exact decimal value. + + + + Equivalent of System.BitConverter, but with either endianness. + + + + + Union used solely for the equivalent of DoubleToInt64Bits and vice versa. + + + + + Creates an instance representing the given integer. + + The integer value of the new instance. + + + + Creates an instance representing the given floating point number. + + The floating point value of the new instance. + + + + Int32 version of the value. + + + + + Single version of the value. + + + + + Returns the value of the instance as an integer. + + + + + Returns the value of the instance as a floating point number. + + + + + Indicates the byte order ("endianess") in which data is converted using this class. + + + + + Returns a little-endian bit converter instance. The same instance is + always returned. + + + + + Returns a big-endian bit converter instance. The same instance is + always returned. + + + + + Indicates the byte order ("endianess") in which data is converted using this class. + + + Different computer architectures store data using different byte orders. "Big-endian" + means the most significant byte is on the left end of a word. "Little-endian" means the + most significant byte is on the right end of a word. + + true if this converter is little-endian, false otherwise. + + + + Converts the specified double-precision floating point number to a + 64-bit signed integer. Note: the endianness of this converter does not + affect the returned value. + + The number to convert. + A 64-bit signed integer whose value is equivalent to value. + + + + Converts the specified 64-bit signed integer to a double-precision + floating point number. Note: the endianness of this converter does not + affect the returned value. + + The number to convert. + A double-precision floating point number whose value is equivalent to value. + + + + Converts the specified single-precision floating point number to a + 32-bit signed integer. Note: the endianness of this converter does not + affect the returned value. + + The number to convert. + A 32-bit signed integer whose value is equivalent to value. + + + + Converts the specified 32-bit signed integer to a single-precision floating point + number. Note: the endianness of this converter does not + affect the returned value. + + The number to convert. + A single-precision floating point number whose value is equivalent to value. + + + + Returns a Boolean value converted from one byte at a specified position in a byte array. + + An array of bytes. + The starting position within value. + true if the byte at startIndex in value is nonzero; otherwise, false. + + + + Returns a Unicode character converted from two bytes at a specified position in a byte array. + + An array of bytes. + The starting position within value. + A character formed by two bytes beginning at startIndex. + + + + Returns a double-precision floating point number converted from eight bytes + at a specified position in a byte array. + + An array of bytes. + The starting position within value. + A double precision floating point number formed by eight bytes beginning at startIndex. + + + + Returns a single-precision floating point number converted from four bytes + at a specified position in a byte array. + + An array of bytes. + The starting position within value. + A single precision floating point number formed by four bytes beginning at startIndex. + + + + Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array. + + An array of bytes. + The starting position within value. + A 16-bit signed integer formed by two bytes beginning at startIndex. + + + + Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array. + + An array of bytes. + The starting position within value. + A 32-bit signed integer formed by four bytes beginning at startIndex. + + + + Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array. + + An array of bytes. + The starting position within value. + A 64-bit signed integer formed by eight bytes beginning at startIndex. + + + + Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array. + + An array of bytes. + The starting position within value. + A 16-bit unsigned integer formed by two bytes beginning at startIndex. + + + + Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array. + + An array of bytes. + The starting position within value. + A 32-bit unsigned integer formed by four bytes beginning at startIndex. + + + + Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array. + + An array of bytes. + The starting position within value. + A 64-bit unsigned integer formed by eight bytes beginning at startIndex. + + + + Checks the given argument for validity. + + The byte array passed in + The start index passed in + The number of bytes required + value is a null reference + + startIndex is less than zero or greater than the length of value minus bytesRequired. + + + + + Checks the arguments for validity before calling FromBytes + (which can therefore assume the arguments are valid). + + The bytes to convert after checking + The index of the first byte to convert + The number of bytes to convert + + + + + + Convert the given number of bytes from the given array, from the given start + position, into a long, using the bytes as the least significant part of the long. + By the time this is called, the arguments have been checked for validity. + + The bytes to convert + The index of the first byte to convert + The number of bytes to use in the conversion + The converted number + + + + Returns a String converted from the elements of a byte array. + + An array of bytes. + All the elements of value are converted. + + A String of hexadecimal pairs separated by hyphens, where each pair + represents the corresponding element in value; for example, "7F-2C-4A". + + + + + Returns a String converted from the elements of a byte array starting at a specified array position. + + An array of bytes. + The starting position within value. + The elements from array position startIndex to the end of the array are converted. + + A String of hexadecimal pairs separated by hyphens, where each pair + represents the corresponding element in value; for example, "7F-2C-4A". + + + + + Returns a String converted from a specified number of bytes at a specified position in a byte array. + + An array of bytes. + The starting position within value. + The number of bytes to convert. + The length elements from array position startIndex are converted. + + A String of hexadecimal pairs separated by hyphens, where each pair + represents the corresponding element in value; for example, "7F-2C-4A". + + + + + Returns a decimal value converted from sixteen bytes + at a specified position in a byte array. + + An array of bytes. + The starting position within value. + A decimal formed by sixteen bytes beginning at startIndex. + + + + Returns the specified decimal value as an array of bytes. + + The number to convert. + An array of bytes with length 16. + + + + Copies the specified decimal value into the specified byte array, + beginning at the specified index. + + A character to convert. + The byte array to copy the bytes into + The first index into the array to copy the bytes into + + + + Returns an array with the given number of bytes formed + from the least significant bytes of the specified value. + This is used to implement the other GetBytes methods. + + The value to get bytes for + The number of significant bytes to return + + + + Returns the specified Boolean value as an array of bytes. + + A Boolean value. + An array of bytes with length 1. + + + + Returns the specified Unicode character value as an array of bytes. + + A character to convert. + An array of bytes with length 2. + + + + Returns the specified double-precision floating point value as an array of bytes. + + The number to convert. + An array of bytes with length 8. + + + + Returns the specified 16-bit signed integer value as an array of bytes. + + The number to convert. + An array of bytes with length 2. + + + + Returns the specified 32-bit signed integer value as an array of bytes. + + The number to convert. + An array of bytes with length 4. + + + + Returns the specified 64-bit signed integer value as an array of bytes. + + The number to convert. + An array of bytes with length 8. + + + + Returns the specified single-precision floating point value as an array of bytes. + + The number to convert. + An array of bytes with length 4. + + + + Returns the specified 16-bit unsigned integer value as an array of bytes. + + The number to convert. + An array of bytes with length 2. + + + + Returns the specified 32-bit unsigned integer value as an array of bytes. + + The number to convert. + An array of bytes with length 4. + + + + Returns the specified 64-bit unsigned integer value as an array of bytes. + + The number to convert. + An array of bytes with length 8. + + + + Copies the given number of bytes from the least-specific + end of the specified value into the specified byte array, beginning + at the specified index. + This is used to implement the other CopyBytes methods. + + The value to copy bytes for + The number of significant bytes to copy + The byte array to copy the bytes into + The first index into the array to copy the bytes into + + + + Copies the given number of bytes from the least-specific + end of the specified value into the specified byte array, beginning + at the specified index. + This must be implemented in concrete derived classes, but the implementation + may assume that the value will fit into the buffer. + + The value to copy bytes for + The number of significant bytes to copy + The byte array to copy the bytes into + The first index into the array to copy the bytes into + + + + Copies the specified Boolean value into the specified byte array, + beginning at the specified index. + + A Boolean value. + The byte array to copy the bytes into + The first index into the array to copy the bytes into + + + + Copies the specified Unicode character value into the specified byte array, + beginning at the specified index. + + A character to convert. + The byte array to copy the bytes into + The first index into the array to copy the bytes into + + + + Copies the specified double-precision floating point value into the specified byte array, + beginning at the specified index. + + The number to convert. + The byte array to copy the bytes into + The first index into the array to copy the bytes into + + + + Copies the specified 16-bit signed integer value into the specified byte array, + beginning at the specified index. + + The number to convert. + The byte array to copy the bytes into + The first index into the array to copy the bytes into + + + + Copies the specified 32-bit signed integer value into the specified byte array, + beginning at the specified index. + + The number to convert. + The byte array to copy the bytes into + The first index into the array to copy the bytes into + + + + Copies the specified 64-bit signed integer value into the specified byte array, + beginning at the specified index. + + The number to convert. + The byte array to copy the bytes into + The first index into the array to copy the bytes into + + + + Copies the specified single-precision floating point value into the specified byte array, + beginning at the specified index. + + The number to convert. + The byte array to copy the bytes into + The first index into the array to copy the bytes into + + + + Copies the specified 16-bit unsigned integer value into the specified byte array, + beginning at the specified index. + + The number to convert. + The byte array to copy the bytes into + The first index into the array to copy the bytes into + + + + Copies the specified 32-bit unsigned integer value into the specified byte array, + beginning at the specified index. + + The number to convert. + The byte array to copy the bytes into + The first index into the array to copy the bytes into + + + + Copies the specified 64-bit unsigned integer value into the specified byte array, + beginning at the specified index. + + The number to convert. + The byte array to copy the bytes into + The first index into the array to copy the bytes into + + + + Endianness of a converter + + + + + Little endian - least significant byte first + + + + + Big endian - most significant byte first + + + + + Implementation of EndianBitConverter which converts to/from little-endian + byte arrays. + + + + + Indicates the byte order ("endianess") in which data is converted using this class. + + + + + Indicates the byte order ("endianess") in which data is converted using this class. + + + Different computer architectures store data using different byte orders. "Big-endian" + means the most significant byte is on the left end of a word. "Little-endian" means the + most significant byte is on the right end of a word. + + true if this converter is little-endian, false otherwise. + + + + Copies the specified number of bytes from value to buffer, starting at index. + + The value to copy + The number of bytes to copy + The buffer to copy the bytes into + The index to start at + + + + Returns a value built from the specified number of bytes from the given buffer, + starting at index. + + The data in byte array format + The first index to use + The number of bytes to use + The value built from the given bytes + + + + Internet packets include IPv4, IPv6, IGMP etc, see + http://en.wikipedia.org/wiki/Internet_Layer + + + + + Constructor + + + A + + + + IP protocol field encoding information. + + + + Width of the IP version and header length field in bytes. + + + Width of the Differentiated Services / Type of service field in bytes. + + + Width of the total length field in bytes. + + + Width of the ID field in bytes. + + + Width of the fragment offset bits and offset field in bytes. + + + Width of the TTL field in bytes. + + + Width of the IP protocol code in bytes. + + + Width of the IP checksum in bytes. + + + Position of the version code and header length within the IP header. + + + Position of the differentiated services value within the IP header. + + + Position of the header length within the IP header. + + + Position of the packet ID within the IP header. + + + Position of the flag bits and fragment offset within the IP header. + + + Position of the ttl within the IP header. + + + + Position of the protocol used within the IP data + + + + Position of the checksum within the IP header. + + + Position of the source IP address within the IP header. + + + Position of the destination IP address within a packet. + + + Length in bytes of an IP header, excluding options. + + + + Number of bytes in an IPv4 address + + + + + IPv4 packet + See http://en.wikipedia.org/wiki/IPv4 for into + + + + + Construct an instance by values + + + + + Parse bytes into an IP packet + + + A + + A + + + + Parse bytes into an IP packet + + + A + + A + + A + + + Type of service code constants for IP. Type of service describes + how a packet should be handled. +

+ TOS is an 8-bit record in an IP header which contains a 3-bit + precendence field, 4 TOS bit fields and a 0 bit. +

+ The following constants are bit masks which can be logically and'ed + with the 8-bit IP TOS field to determine what type of service is set. +

+ Taken from TCP/IP Illustrated V1 by Richard Stevens, p34. +

+
+ + + Number of bytes in the smallest valid ipv4 packet + + + + + Version number of the IP protocol being used + + + + Get the IP version code. + + + + Forwards compatibility IPv6.PayloadLength property + + + + + The IP header length field. At most, this can be a + four-bit value. The high order bits beyond the fourth bit + will be ignored. + + The length of the IP header in 32-bit words. + + + + + The unique ID of this IP datagram. The ID normally + increments by one each time a datagram is sent by a host. + A 16-bit unsigned integer. + + + + + Fragmentation offset + The offset specifies a number of octets (i.e., bytes). + A 13-bit unsigned integer. + + + + Fetch the IP address of the host where the packet originated from. + + + Fetch the IP address of the host where the packet is destined. + + + Fetch the header checksum. + + + Check if the IP packet is valid, checksum-wise. + + + + Check if the IP packet header is valid, checksum-wise. + + + + Fetch ascii escape sequence of the color associated with this packet type. + + + Fetch the type of service. + + + + Renamed to DifferentiatedServices in IPv6 but present here + for backwards compatibility + + + + + The entire datagram size including header and data + + + + Fetch fragment flags. + A 3-bit unsigned integer. + + + Fetch the time to live. TTL sets the upper limit on the number of + routers through which this IP datagram is allowed to pass. + Originally intended to be the number of seconds the packet lives it is now decremented + by one each time a router passes the packet on + 8-bit value + + + + Fetch the code indicating the type of protocol embedded in the IP + + + + + + Calculates the IP checksum, optionally updating the IP checksum header. + + The calculated IP checksum. + + + + + Update the checksum value + + + + + Prepend to the given byte[] origHeader the portion of the IPv6 header used for + generating an tcp checksum + http://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_checksum_using_IPv4 + http://tools.ietf.org/html/rfc793 + + + A + + A + + + Convert this IP packet to a readable string. + + + Generate string with contents describing this IP packet. + whether or not the string should contain ansi + color escape sequences. + + + + Convert this IP packet to a more verbose string. + + + + Generate a random packet + + + A + + + + A struct containing length and position information about IPv6 Fields. + + + + + Commutes the field positions. + + + + + The IP Version, Traffic Class, and Flow Label field length. These must be in one + field due to boundary crossings. + + + + + The payload length field length. + + + + + The next header field length, identifies protocol encapsulated by the packet + + + + + The hop limit field length. + + + + + Address field length + + + + + The byte position of the field line in the IPv6 header. + This is where the IP version, Traffic Class, and Flow Label fields are. + + + + + The byte position of the payload length field. + + + + + The byte position of the next header field. (Replaces the ipv4 protocol field) + + + + + The byte position of the hop limit field. + + + + + The byte position of the source address field. + + + + + The byte position of the destination address field. + + + + + The byte length of the IPv6 Header + + + + + IPv6 packet + References + ---------- + http://tools.ietf.org/html/rfc2460 + http://en.wikipedia.org/wiki/IPv6 + + + + + Create an IPv6 packet from values + + + A + + A + + + + byte[]/int offset constructor, timeval defaults to the current time + + + A + + A + + + + byte[]/int offset/PosixTimeval constructor + + + A + + A + + A + + + + Minimum number of bytes in an IPv6 header + + + + + The version of the IP protocol. The '6' in IPv6 indicates the version of the protocol + + + + + The version field of the IPv6 Packet. + + + + + The traffic class field of the IPv6 Packet. + + + + + The flow label field of the IPv6 Packet. + + + + + The payload lengeth field of the IPv6 Packet + NOTE: Differs from the IPv4 'Total length' field that includes the length of the header as + payload length is ONLY the size of the payload. + + + + + Backwards compatibility property for IPv4.HeaderLength + NOTE: This field is the number of 32bit words + + + + + Backwards compatibility property for IPv4.TotalLength + + + + + Identifies the protocol encapsulated by this packet + Replaces IPv4's 'protocol' field, has compatible values + + + + + The protocol of the packet encapsulated in this ip packet + + + + + The hop limit field of the IPv6 Packet. + NOTE: Replaces the 'time to live' field of IPv4 + 8-bit value + + + + + Helper alias for 'HopLimit' + + + + + The source address field of the IPv6 Packet. + + + + + The destination address field of the IPv6 Packet. + + + + + Converts the packet to a color string. TODO add a method for colored to string. + + + + + Prepend to the given byte[] origHeader the portion of the IPv6 header used for + generating an tcp checksum + http://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_checksum_using_IPv6 + http://tools.ietf.org/html/rfc2460#page-27 + + + A + + A + + + + Converts the packet to a string. + + + + + + Generate string with contents describing this IP packet. + whether or not the string should contain ansi + color escape sequences. + + + + Convert this IP packet to a more verbose string. + + + + Generate a random packet + + + A + + + Code constants for ip ports. + + + + Secure shell + + + + + Terminal protocol + + + + + Simple mail transport protocol + + + + + Hyper text transfer protocol + + + + + Same as Http + + + + + Secure ftp + + + + + Network time protocol + + + + + Simple network management protocol + + + + + Computes the one's sum on a byte array. + Based TCP/IP Illustrated Vol. 2(1995) by Gary R. Wright and W. Richard + Stevens. Page 236. And on http://www.cs.utk.edu/~cs594np/unp/checksum.html + + + + + Computes the one's complement sum on a byte array + + + + + Computes the one's complement sum on a byte array + + + + + Compute a ones sum of a byte array + + + A + + A + + + + 16 bit sum of all values + http://en.wikipedia.org/wiki/Signed_number_representations#Ones.27_complement + + + A + + A + + A + + A + + + IP protocol field encoding information. + + + + Length of a TCP port in bytes. + + + Length of the sequence number in bytes. + + + Length of the acknowledgment number in bytes. + + + Length of the data offset and flags field in bytes. + + + The length of the flags field + + + Length of the window size field in bytes. + + + Length of the checksum field in bytes. + + + Length of the urgent field in bytes. + + + Position of the source port field. + + + Position of the destination port field. + + + Position of the sequence number field. + + + Position of the acknowledgment number field. + + + Position of the data offset + + + Position of the flags field + + + Position of the window size field. + + + Position of the checksum field. + + + Position of the urgent pointer field. + + + Length in bytes of a TCP header. + + + IP protocol field encoding information. + + FIXME: These fields are partially broken because they assume the offset for + several fields and the offset is actually based on the accumulated offset + into the structure determined by the fields that indicate sizes + + Type code for ethernet addresses. + + + Type code for MAC addresses. + + + Operation type length in bytes. + + + + The length of the address type fields in bytes, + eg. the length of hardware type or protocol type + + + + + The length of the address length fields in bytes. + + + + Position of the hardware address type. + + + Position of the protocol address type. + + + Position of the hardware address length. + + + Position of the protocol address length. + + + Position of the operation type. + + + Position of the sender hardware address. + + + Position of the sender protocol address. + + + Position of the target hardware address. + + + Position of the target protocol address. + + + Total length in bytes of an ARP header. + + + + An ARP protocol packet. + + + + + Create an ARPPacket from values + + + A + + A + + A + + A + + A + + + + byte[]/int Offset constructor + + + A + + A + + + + byte[]/int offset/PosixTimeval constructor + + + A + + A + + A + + + + Also known as HardwareType + + + + + Also known as ProtocolType + + + + + Hardware address length field + + + + + Protocol address length field + + + + Fetch the operation code. + Usually one of ARPFields.{ARP_OP_REQ_CODE, ARP_OP_REP_CODE}. + + Sets the operation code. + Usually one of ARPFields.{ARP_OP_REQ_CODE, ARP_OP_REP_CODE}. + + + + + Upper layer protocol address of the sender, typically an IPv4 or IPv6 address + + + + + Upper layer protocol address of the target, typically an IPv4 or IPv6 address + + + + + Sender hardware address, usually an ethernet mac address + + + + + Target hardware address, usually an ethernet mac address + + + + Fetch ascii escape sequence of the color associated with this packet type. + + + Convert this ARP packet to a readable string. + + + Generate string with contents describing this ARP packet. + whether or not the string should contain ansi + color escape sequences. + + + + + Returns the encapsulated ARPPacket of the Packet p or null if + there is no encapsulated packet + + + A + + A + + + + ICMP protocol field encoding information. + See http://en.wikipedia.org/wiki/ICMPv6 + + + + Length of the ICMP message type code in bytes. + + + Length of the ICMP subcode in bytes. + + + Length of the ICMP header checksum in bytes. + + + Position of the ICMP message type. + + + Position of the ICMP message subcode. + + + Position of the ICMP header checksum. + + + Length in bytes of an ICMP header. + + + + An ICMP packet. + See http://en.wikipedia.org/wiki/ICMPv6 + + + + + byte[]/int offset constructor + + + A + + A + + + + byte[]/int Offset/PosixTimeval constructor + + + A + + A + + A + + + + The Type value + + + + Fetch the ICMP code + + + + Checksum value + + + + Fetch ascii escape sequence of the color associated with this packet type. + + + Convert this ICMP packet to a readable string. + + + Generate string with contents describing this ICMP packet. + whether or not the string should contain ansi + color escape sequences. + + + + + Returns the ICMPv6Packet inside of Packet p or null if + there is no encapsulated ICMPv6Packet + + + A + + A + + + Code constants for IGMP message types. + From RFC #2236. + + + + + An IGMP packet. + + + + + byte[]/int Offset constructor + + + A + + A + + + + byte[]/int offset/PosixTimeval constructor + + + A + + A + + A + + + + The type of IGMP message + + + + Fetch the IGMP max response time. + + + Fetch the IGMP header checksum. + + + Fetch the IGMP group address. + + + Fetch ascii escape sequence of the color associated with this packet type. + + + Convert this IGMP packet to a readable string. + + + Generate string with contents describing this IGMP packet. + whether or not the string should contain ansi + color escape sequences. + + + + + Internet Link layer packet + See http://en.wikipedia.org/wiki/Link_Layer + + + + + Constructor + + + A + + + + Look for the innermost payload. This method is useful because + while some packets are LinuxSSL->IpPacket or + EthernetPacket->IpPacket, there are some packets that are + EthernetPacket->PPPoEPacket->PPPPacket->IpPacket, and for these cases + we really want to get to the IpPacket + + + A + + + IGMP protocol field encoding information. + + + Length of the IGMP message type code in bytes. + + + Length of the IGMP max response code in bytes. + + + Length of the IGMP header checksum in bytes. + + + Length of group address in bytes. + + + Position of the IGMP message type. + + + Position of the IGMP max response code. + + + Position of the IGMP header checksum. + + + Position of the IGMP group address. + + + Length in bytes of an IGMP header. + + + + Represents a Linux cooked capture packet, the kinds of packets + received when capturing on an 'any' device + See http://github.com/mcr/libpcap/blob/master/pcap/sll.h + + + + + Create an LinuxSLLPacket from a byte array + + + A + + A + + + + Create an LinuxSLLPacket from a byte array and a Timeval + + + A + + A + + A + + + + Information about the packet direction + + + + + The + + + + + Number of bytes in the link layer address of the sender of the packet + + + + + Link layer header bytes, maximum of 8 bytes + + + + + The encapsulated protocol type + + + + + ToString implementation + + + A + + + + Colored string that represents the values in this class instance + + + A + + A + + + Convert a more verbose string. + + + + Lengths and offsets to the fields in the LinuxSLL packet + See http://github.com/mcr/libpcap/blob/master/pcap/sll.h + + + + + Length of the packet type field + + + + + Link layer address type + + + + + Link layer address length + + + + + The link layer address field length + NOTE: the actual link layer address MAY be shorter than this + + + + + Number of bytes in a SLL header + + + + + Length of the ethernet protocol field + + + + + Position of the packet type field + + + + + Position of the link layer address type field + + + + + Positino of the link layer address length field + + + + + Position of the link layer address field + + + + + Position of the ethernet protocol type field + + + + + The types of cooked packets + See http://github.com/mcr/libpcap/blob/master/pcap/sll.h + + + + + Packet was sent to us by somebody else + + + + + Packet was broadcast by somebody else + + + + + Packet was multicast, but not broadcast + + + + + Packet was sent by somebody else to somebody else + + + + + Packet was sent by us + + + + + The protocol encapsulated inside of the IP packet + + + + Dummy protocol for TCP. + + + IPv6 Hop-by-Hop options. + + + Internet Control Message Protocol. + + + Internet Group Management Protocol. + + + IPIP tunnels (older KA9Q tunnels use 94). + + + Transmission Control Protocol. + + + Exterior Gateway Protocol. + + + PUP protocol. + + + User Datagram Protocol. + + + XNS IDP protocol. + + + SO Transport Protocol Class 4. + + + IPv6 header. + + + IPv6 routing header. + + + IPv6 fragmentation header. + + + Reservation Protocol. + + + General Routing Encapsulation. + + + encapsulating security payload. + + + authentication header. + + + ICMPv6. + + + IPv6 no next header. + + + IPv6 destination options. + + + Multicast Transport Protocol. + + + Encapsulation Header. + + + Protocol Independent Multicast. + + + Compression Header Protocol. + + + Raw IP packets. + + + Unrecognized IP protocol. + WARNING: this only works because the int storage for the protocol + code has more bits than the field in the IP header where it is stored. + + + + IP protocol mask. + + + Code constants for internet protocol versions. + + + + Internet protocol version 4. + + + Internet protocol version 6. + + + Link-layer type codes. +

+ Taken from libpcap/bpf/net/bpf.h and pcap/net/bpf.h. +

+ The link-layer type is used to determine what data-structure the + IP protocol bits will be encapsulated inside of. +

+ On a 10/100mbps network, packets are encapsulated inside of ethernet. + 14-byte ethernet headers which contain MAC addresses and an ethernet type + field. +

+ On ethernet over ppp, the link-layer type is raw, and packets + are not encapsulated in any ethernet header. +

+
+ + no link-layer encapsulation + + + Ethernet (10Mb) + + + Experimental Ethernet (3Mb) + + + Amateur Radio AX.25 + + + Proteon ProNET Token Ring + + + Chaos + + + IEEE 802 Networks + + + ARCNET + + + Serial Line IP + + + Point-to-point Protocol + + + FDDI + + + LLC/SNAP encapsulated atm + + + raw IP + + + BSD Slip. + + + BSD PPP. + + + IP over ATM. + + + PPP over HDLC. + + + Cisco HDLC. + + + IEEE 802.11 wireless. + + + OpenBSD loopback. + + + Linux cooked sockets. + + + unknown link-layer type + + + + Helper class that prints out an array of hex values + + + + + Create a string that contains the hex values of byte[] Byte in + text form + + + A + + A + + A + + A + + + POSIX.4 timeval + + + + Constructor with Seconds and MicroSeconds fields + + + A + + A + + + + Construct a PosixTimeval using the current UTC time + + + + + Number of seconds in the timeval + + + + + Number of microseconds in the timeval + + + + The timeval as a DateTime in Utc + + + + Equals override + + + A + + A + + + + GetHashCode override + + + A + + + + Convert the timeval to a string like 'SECONDS.MICROSECONDSs' + + + A + + + + Operator < overload + + + A + + A + + A + + + + Operator > overload + + + A + + A + + A + + + + Operator <= + + + A + + A + + A + + + + Operator >= + + + A + + A + + A + + + + Operator == + + + A + + A + + A + + + + Operator != + + + A + + A + + A + + + + Random utility methods + + + + + Generate a random ip address + + + A + + A + + + + Differentiates between a packet class payload, a byte[] payload + or no payload + + + + + ICMPv6 types, see http://en.wikipedia.org/wiki/ICMPv6 and + http://www.iana.org/assignments/icmpv6-parameters + + + + + Raw packet as loaded from a pcap device or file + + + + + Constructor + + + A + + A + + A + + + + Link layer from which this packet was captured + + + + + The unix timeval when the packet was created + + + + Fetch data portion of the packet. + + + + ToString() override + + + A + + + + The possible ARP operation values + + + + + Arp request + + + + + Arp response + + + + + Point to Point Protocol + See http://tools.ietf.org/html/rfc2516 + + + + + Construct a new PPPoEPacket from source and destination mac addresses + + + + + Create an PPPoEPacket from a byte array + + + A + + A + + + + Create an PPPoEPacket from a byte array and a Timeval + + + A + + A + + A + + + + PPPoe version, must be 0x1 according to RFC + + + + + Type, must be 0x1 according to RFC + + + + + + + + + Session identifier for this PPPoe packet + + + + + Length of the PPPoe payload, not including the PPPoe header + + + + Fetch ascii escape sequence of the color associated with this packet type. + + + Convert this packet to a readable string. + + + Generate string with contents describing this packet. + whether or not the string should contain ansi + color escape sequences. + + + + Convert a more verbose string. + + + + Generate a random PPPoEPacket + + + A + + + + Point to Point Protocol + See http://tools.ietf.org/html/rfc2516 + + + + Size in bytes of the version/type field + + + Size in bytes of the code field + + + Size in bytes of the SessionId field + + + Size in bytes of the Length field + + + Offset from the start of the header to the version/type field + + + Offset from the start of the header to the Code field + + + Offset from the start of the header to the SessionId field + + + Offset from the start of the header to the Length field + + + + Length of the overall PPPoe header + + + + + Values for the Code field of a PPPoE packet + See http://tools.ietf.org/html/rfc2516 + + + + + The PPPoe payload must contain a PPP packet + + + + + Active Discovery Offer (PADO) packet + + + + + From RFC2516: + The Host sends the PADI packet with the DESTINATION_ADDR set to the + broadcast address. The CODE field is set to 0x09 and the SESSION_ID + MUST be set to 0x0000. + The PADI packet MUST contain exactly one TAG of TAG_TYPE Service- + Name, indicating the service the Host is requesting, and any number + of other TAG types. An entire PADI packet (including the PPPoE + header) MUST NOT exceed 1484 octets so as to leave sufficient room + for a relay agent to add a Relay-Session-Id TAG. + + + + + Indicate that the PPPoe session specified by the SessionId field of + the PPPoe packet has been terminated + + + + + PPP packet + See http://en.wikipedia.org/wiki/Point-to-Point_Protocol + + + + + Construct a new PPPPacket from source and destination mac addresses + + + + + Create an PPPPacket from a byte array + + + A + + A + + + + Create an PPPPacket from a byte array and a Timeval + + + A + + A + + A + + + + See http://www.iana.org/assignments/ppp-numbers + + + + Fetch ascii escape sequence of the color associated with this packet type. + + + Convert this packet to a readable string. + + + Generate string with contents describing this packet. + whether or not the string should contain ansi + color escape sequences. + + + + Convert a more verbose string. + + + + Generate a random PPPoEPacket + + + A + + + + The fields in a PPP packet + See http://en.wikipedia.org/wiki/Point-to-Point_Protocol + + + + + Length of the Protocol field in bytes, the field is of type + PPPProtocol + + + + + Offset from the start of the PPP packet where the Protocol field is located + + + + + The length of the header + + + + + Indicates the protocol encapsulated by the PPP packet + See http://www.iana.org/assignments/ppp-numbers + + + + Padding + + + IPv4 + + + IPv6 + + + + Code constants for ICMP message types. + From http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol#List_of_permitted_control_messages_.28incomplete_list.29 + Note that these values represent the combined + type and code fields, where the type field is the upper byte + + + + + ICMP protocol field encoding information. + See http://en.wikipedia.org/wiki/ICMPv6 + + + + Length of the ICMP message type code in bytes. + + + Length of the ICMP header checksum in bytes. + + + Length of the ICMP ID field in bytes. + + + Length of the ICMP Sequence field in bytes + + + Position of the ICMP message type/code. + + + Position of the ICMP header checksum. + + + Position of the ICMP ID field + + + Position of the Sequence field + + + Length in bytes of an ICMP header. + + + + An ICMP packet + See http://en.wikipedia.org/wiki/Internet_Control_Message_Protocol + + + + + byte[]/int offset constructor + + + A + + A + + + + byte[]/int Offset/PosixTimeval constructor + + + A + + A + + A + + + + The Type/Code enum value + + + + + Checksum value + + + + + ID field + + + + + Sequence field + + + + + Contents of the ICMP packet + + + + Fetch ascii escape sequence of the color associated with this packet type. + + + Convert this ICMP packet to a readable string. + + + Generate string with contents describing this ICMP packet. + whether or not the string should contain ansi + color escape sequences. + + + + + Returns the ICMPv4Packet inside of Packet p or null if + there is no encapsulated ICMPv4Packet + + + A + + A + + + + Container class that refers to a segment of bytes in a byte[] + + + + + Constructor from a byte array, offset into the byte array and + a length beyond that offset of the bytes this class is referencing + + + A + + A + + A + + + + Number of bytes beyond the offset into Bytes + + + + + The byte[] array + + + + + Offset into Bytes + + + + + Return true if we need to perform a copy to get + the bytes represented by this class + + + A + + + + Returns a contiguous byte[] from this container, if necessary, by copying + the bytes from the current offset into a newly allocated byte[]. + NeedsCopyForActualBytes can be used to determine if the copy is necessary + + + A + + + + Helper method that returns the segment immediately following + this instance, useful for processing where the parent + wants to pass the next segment to a sub class for processing + + + A + + + + Format the class information as a string + + + A + + + + Encapsulates and ensures that we have either a Packet OR + a ByteArraySegment but not both + + + + + Whether or not this container contains a packet, a byte[] or neither + + + + + Appends to the MemoryStream either the byte[] represented by TheByteArray, or + if ThePacket is non-null, the Packet.Bytes will be appended to the memory stream + which will append ThePacket's header and any encapsulated packets it contains + + + A + + + + A LLDP packet. + As specified in IEEE Std 802.1AB + + + See http://en.wikipedia.org/wiki/Link_Layer_Discovery_Protocol for general info + See IETF 802.1AB for the full specification + + + + + Create an empty LLDPPacket + + + + + Creates a LLDP packet from a byte[] + + + A + + A + + + + Creates a LLDP packet from a byte[] + + + A + + A + + A + + + + Contains the TLV's in the LLDPDU + + + + + Allows access of the TlvCollection by index + + The index of the item being set/retrieved in the collection + The requested TLV + + + + The current length of the LLDPDU + + + + + LLDPPacket specific implementation of BytesHighPerformance + Necessary because each TLV in the collection may have a + byte[] that is not shared by other TLVs + NOTE: There is potential for the same performance improvement that + the Packet class uses where we check to see if each TLVs uses the + same byte[] and that there are no gaps. + + + + + Enables foreach functionality for this class + + The next item in the list + + + + Parse byte[] into TLVs + + + + + + + A + + A + + A + + A + + + + Returns the LLDP inside of the Packet p or null if + there is no encapsulated packet + + + A + + A + + + + Create a randomized LLDP packet with some basic TLVs + + + A + + + + Convert this LLDP packet to a readable string. + + + A human readable string. + + + + + Convert this LLDP packet to a readable string. + + + Sets whether the output includes coloring. + + + A human readable string. + + + + + Convert this LLDP packet to a verbose readable string. + + + Sets whether the output includes coloring. + + + A verbose human readable string. + + + + + The TLV Types + + + See IETF RFC 802.1AB for more info + + + + Signifies the end of a LLDPU + + The End Of LLDPDU TLV is a 2-octet, all-zero + TLV that is used to mark the end of the TLV + sequence in LLDPDUs + + Source: IETF RFC 802.1AB + + + A Chassis Identifier + + A mandatory TLV that identifies the chassis + containing the IEEE 802 LAN station + associated with the transmitting LLDP agent + + Source: IETF RFC 802.1AB + + + A Port Identifier + + A mandatory TLV that identifies the + port component of the MSAP identifier associated + with the transmitting LLDP agent. + + Source: IETF RFC 802.1AB + + + Specifies the Time to Live + + Indicates the number of seconds that the + recipient LLDP agent is to regard the information + associated with this MSAP identifier to be valid + A value of 0 signals that this source is no longer + available and all information associated with it + should be deleted. + + Source: IETF RFC 802.1AB + + + A Description of the Port + + The port description field shall contain an + alpha-numeric string that indicates the port’s + description. + + Source: IETF RFC 802.1AB + + + The System's Assigned Name + + The System Name TLV allows network management + to advertise the system’s assigned name. + + Source: IETF RFC 802.1AB + + + A Description of the System + + The System Description TLV allows network + management to advertise the system’s description + + Source: IETF RFC 802.1AB + + + A bitmap containing the System's capabilities + + The System Capabilities TLV is an optional TLV + that identifies the primary function(s) of the + system and whether or not these primary functions + are enabled. + + Source: IETF RFC 802.1AB + + + The Management Address + + The Management Address TLV identifies an address + associated with the local LLDP agent that may be + used to reach higher layer entities to assist + discovery by network management. + + Source: IETF RFC 802.1AB + + + A vendor-specifid TLV + + This TLV category is provided to allow different + organizations, such as IEEE 802.1, IEEE 802.3, IETF, + as well as individual software and equipment vendors, + to define TLVs that advertise information to remote + entities attached to the same media. + + Source: IETF RFC 802.1AB + + + + The Chassis ID TLV subtypes + + + + A Chassis Component identifier + See IETF RFC 2737 + + + An Interface Alias identifier + See IETF RFC 2863 + + + A Port Component identifier + See IETF RFC 2737 + + + A MAC (Media Access Control) Address identifier + See IEEE Std 802 + + + A Network Address (IP Address) Identifier + See IEEE Std 802 + + + An Interface Name identifier + See IEEE Std 802 + + + A Locally Assigned identifier + + + + The Port ID TLV subtypes + + + + An Interface Alias identifier + See IETF RFC 2863 + + + A Port Component identifier + See IETF RFC 2737 + + + A MAC (Media Access Control) Address identifier + See IEEE Std 802 + + + A Network Address (IP Address) Identifier + See IEEE Std 802 + + + An Interface Name identifier + See IEEE Std 802 + + + An Agent Circiut ID identifier + See IETF RFC 3046 + + + A Locally Assigned identifier + See IETF RFC 3046 + + + + The System Capabilities options + + + + + An Other Type of System + + + + A Repeater + See IETF RFC 2108 + + + A Bridge + IETF RFC 2674 + + + A WLAN Access Point + IEEE 802.11 MIB + + + A Router + IETF RFC 1812 + + + A Telephone + IETF RFC 2011 + + + A DOCSIS Cable Device + + See IETF RFC 2669 + See IETF RFC 2670 + + + + A Station with no other capabilities + IETF RFC 2011 + + + + A Type-Length-Value object + + + + + Create a tlv + + + + + Creates a TLV + + + Bytes that comprise the TLV + + + The TLVs offset from the start of byte[] bytes + + + + + Points to the TLV data + + + + + Interface to this TLVs type and length + + + + + Length of value portion of the TLV + NOTE: Does not include the length of the Type and Length fields + + + + + Total length of the TLV, including the length of the Type and Length fields + + + + + Tlv type + + + + + Offset to the value bytes of the TLV + + + + + Return a byte[] that contains the tlv + + + + + Points to the TLV data + + + + + A Network Address + + + + + Creates a Network Address entity + + + The Network Address + + + + + Create a network address from byte data + + + A + + A + + A + + + + Length of AddressFamily field in bytes + + + + + Number of bytes in the NetworkAddress + + + + The format of the Network Address + + + The Network Address + + + + Equals override + + + A + + A + + + + GetHashCode() override + + + A + + + + ToString() override + + + A + + + + Interface Numbering Types + + Source IETF RFC 802.1AB + + + Unknown + + + Interface Index + + + System Port Number + + + + An End Of LLDPDU TLV + + + + + Parses bytes into an End Of LLDPDU TLV + + + TLV bytes + + + The End Of LLDPDU TLV's offset from the + origin of the LLDP + + + + + Creates an End Of LLDPDU TLV + + + + + Convert this TTL TLV to a string. + + + A human readable string + + + + + A Chassis ID TLV + + + + + Creates a Chassis ID TLV by parsing a byte[] + + + + + The Chassis ID TLV's offset from the + origin of the LLDP + + + + + Creates a Chassis ID TLV and sets it value + + + The ChassisID subtype + + + The subtype's value + + + + + Create a ChassisID given a mac address + + + A + + + + Create a ChassisID given an interface name + http://tools.ietf.org/search/rfc2863 page 38 + + + A + + + + Length of the sub type field in bytes + + + + + The type of the TLV subtype + + + + + The TLV subtype value + + + + + If SubType is ChassisComponent + + + + + If SubType is InterfaceName the interface name + + + + + If SubType is MACAddress the mac address + + + + + If SubType is NetworkAddress the network address + + + + + If SubType is PortComponent + + + + + If SubType is InterfaceAlias + + + + + Helper method to reduce duplication in type specific constructors + + + + + Convert this Chassis ID TLV to a string. + + + A human readable string + + + + + A Port ID TLV + + + + + Creates a Port ID TLV + + + + + The Port ID TLV's offset from the + origin of the LLDP + + + + + Creates a Port ID TLV and sets it value + + + The Port ID SubType + + + The subtype's value + + + + + Construct a PortID from a NetworkAddress + + + A + + + + The type of the TLV subtype + + + + + The TLV subtype value + + + + + Offset to the value field + + + + + Size of the value field + + + + + Helper method to reduce duplication in type specific constructors + + + + + Convert this Port ID TLV to a string. + + + A human readable string + + + + + A Time to Live TLV + + + + + Creates a TTL TLV + + + + + The TTL TLV's offset from the + origin of the LLDP + + + + + Creates a TTL TLV and sets it value + + + The length in seconds until the LLDP + is refreshed + + + + + Number of bytes in the value portion of this tlv + + + + + The number of seconds until the LLDP needs + to be refreshed + A value of 0 means that the LLDP source is + closed and should no longer be refreshed + + + + + Convert this TTL TLV to a string. + + + A human readable string + + + + + A Port Description TLV + + + + + Creates a Port Description TLV + + + + + The Port Description TLV's offset from the + origin of the LLDP + + + + + Creates a Port Description TLV and sets it value + + + A textual description of the port + + + + + A textual Description of the port + + + + + A System Name TLV + + + + + Creates a System Name TLV + + + + + The System Name TLV's offset from the + origin of the LLDP + + + + + Creates a System Name TLV and sets it value + + + A textual Name of the system + + + + + A textual Name of the system + + + + + A System Description TLV + + + + + Creates a System Description TLV + + + + + The System Description TLV's offset from the + origin of the LLDP + + + + + Creates a System Description TLV and sets it value + + + A textual Description of the system + + + + + A textual Description of the system + + + + + A System Capabilities TLV + [TLVTypeLength - 2 bytes][System Capabilities - 2 bytes][Enabled Capabilities - 2 bytes] + + + + + Creates a System Capabilities TLV + + + + + The System Capabilities TLV's offset from the + origin of the LLDP + + + + + Creates a System Capabilities TLV and sets the value + + + A bitmap containing the available System Capabilities + + + A bitmap containing the enabled System Capabilities + + + + + A bitmap containing the available System Capabilities + + + + + A bitmap containing the Enabled System Capabilities + + + + + Checks whether the system is capable of a certain function + + + The capability being checked + + + Whether or not the system is capable of the function being tested + + + + + Checks whether the specified function has been enabled on the system + + + The capability being checked + + + Whether or not the specified function is enabled + + + + + Convert this System Capabilities TLV to a string. + + + A human readable string + + + + + A Time to Live TLV + [TLV Type Length : 2][Mgmt Addr length : 1][Mgmt Addr Subtype : 1][Mgmt Addr : 1-31] + [Interface Subtype : 1][Interface number : 4][OID length : 1][OID : 0-128] + + + + + Creates a Management Address TLV + + + The LLDP Data unit being modified + + + The Management Address TLV's offset from the + origin of the LLDP + + + + + Creates a Management Address TLV and sets it value + + + The Management Address + + + The Interface Numbering Sub Type + + + The Interface Number + + + The Object Identifier + + + + + Number of bytes in the AddressLength field + + + + + Number of bytes in the interface number subtype field + + + + + Number of bytes in the interface number field + + + + + Number of bytes in the object identifier length field + + + + + Maximum number of bytes in the object identifier field + + + + + The Management Address Length + + + + + The Management Address Subtype + Forward to the MgmtAddress instance + + + + + The Management Address + + + + + Interface Number Sub Type + + + + + Interface Number + + + + + Object ID Length + + + + + Object ID + + + + + Convert this Management Address TLV to a string. + + + A human readable string + + + + + An Organization Specific TLV + [TLV Type Length : 2][Organizationally Unique Identifier OUI : 3] + [Organizationally Defined Subtype : 1][Organizationally Defined Information String : 0 - 507] + + + + + Creates an Organization Specific TLV + + + The LLDP Data unit being modified + + + The Organization Specific TLV's offset from the + origin of the LLDP + + + + + Creates an Organization Specific TLV and sets it value + + + An Organizationally Unique Identifier + + + An Organizationally Defined SubType + + + An Organizationally Defined Information String + + + + + An Organizationally Unique Identifier + + + + + An Organizationally Defined SubType + + + + + An Organizationally Defined Information String + + + + + Convert this Organization Specific TLV to a string. + + + A human readable string + + + + + Tlv type and length are 2 bytes + See http://en.wikipedia.org/wiki/Link_Layer_Discovery_Protocol#Frame_structure + + + + + Construct a TLVTypeLength for a TLV + + + A + + + + Length in bytes of the tlv type and length fields + + + + + The TLV Value's Type + + + + + The TLV Value's Length + NOTE: Value is the length of the TLV Value only, does not include the length + of the type and length fields + + + + + A unsigned short representing the concatenated Type and Length + + + + + The IANA (Internet Assigned Numbers Authority) Address Family + + Source http://www.iana.org/assignments/address-family-numbers/ + + + IP version 4 + + + IP version 6 + + + NSAP + + + HDLC + + + BBN 1822 + + + 802 (includes all 802 media plus Ethernet "canonical format") + + + E.163 + + + + Base class for several TLV types that all contain strings + + + + + Creates a String TLV + + + + + The Port Description TLV's offset from the + origin of the LLDP + + + + + Create from a type and string value + + + A + + A + + + + A textual Description of the port + + + + + Convert this Port Description TLV to a string. + + + A human readable string + + + + + Custom collection for TLV types + Special behavior includes: + - Preventing an EndOfLLDPDU tlv from being added out of place + - Checking and throwing exceptions if one-per-LLDP packet TLVs are added multiple times + + + + + Override to: + - Prevent duplicate end tlvs from being added + - Ensure that an end tlv is present + - Replace any automatically added end tlvs with the user provided tlv + + + A + + A + +
+
diff --git a/utils/EQExtractor2/EQExtractor2/PatchAug04-2011.cs b/utils/EQExtractor2/EQExtractor2/PatchAug04-2011.cs new file mode 100644 index 000000000..df72a7ed1 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchAug04-2011.cs @@ -0,0 +1,112 @@ +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchAug042011Decoder : PatchMay242011Decoder + { + public PatchAug042011Decoder() + { + Version = "EQ Client Build Date August 04 2011."; + + ExpectedPPLength = 28496; + + PPZoneIDOffset = 21164; + + PatchConfFileName = "patch_Aug04-2011.conf"; + } + + public override void RegisterExplorers() + { + //OpManager.RegisterExplorer("OP_MercenaryDataResponse", ExploreMercenaryDataResponse); + } + + public void ExploreMercenaryDataResponse(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 TypeCount = Buffer.ReadUInt32(); + + //OutputStream.WriteLine("Type Count: {0}\r\n", TypeCount); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Number of Types (Journeyman and Apprentice in this case\r\n", TypeCount); + for (int i = 0; i < TypeCount; ++i) + { + UInt32 TypeDBStringID = Buffer.ReadUInt32(); + //OutputStream.WriteLine(" Type {0} DBStringID {1}", i, TypeDBStringID); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // DBStringID for Type {1}", TypeDBStringID, i); + } + + UInt32 Count2 = Buffer.ReadUInt32(); + + //OutputStream.WriteLine(" Count 2 is {0}", Count2); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Count of Sub-types that follow", Count2); + + for (int i = 0; i < Count2; ++i) + { + int Offset = Buffer.GetPosition(); + + UInt32 Unknown1 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown1); + UInt32 DBStringID1 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // DBStringID of Type", DBStringID1); + UInt32 DBStringID2 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // DBStringID of Sub-Type", DBStringID2); + UInt32 PurchaseCost = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Purchase Cost", PurchaseCost); + UInt32 UpkeepCost = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Upkeep Cost", UpkeepCost); + UInt32 Unknown2 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown2); + UInt32 Unknown3 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown3); + UInt32 Unknown4 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown4); + + byte Unknown5 = Buffer.ReadByte(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint8, Buffer, {0}); // Unknown", Unknown5); + + UInt32 Unknown6 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown6); + UInt32 Unknown7 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown7); + UInt32 Unknown8 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown8); + + UInt32 StanceCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Number of Stances for this Merc", StanceCount); + + UInt32 Unknown10 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown10); + + byte Unknown11 = Buffer.ReadByte(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint8, Buffer, {0}); // Unknown", Unknown11); + + + //OutputStream.WriteLine(" Offset: {5} Unknown1: {0} DBStrings: {1} {2} Purchase: {3} Upkeep: {4}\r\n", Unknown1, DBStringID1, DBStringID2, + // PurchaseCost, UpkeepCost, Offset); + //OutputStream.WriteLine(" Unknowns: {0} {1} {2} {3} {4} {5} {6} {7} {8}\r\n", + // Unknown2, Unknown3, Unknown4, Unknown5, Unknown6, Unknown7, Unknown8, Unknown10, Unknown11); + + //OutputStream.WriteLine(" Stance Count: {0}", StanceCount); + + for (int j = 0; j < StanceCount; ++j) + { + UInt32 StanceNum = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Stance Number", StanceNum); + UInt32 StanceType = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Stance DBStringID (1 = Passive, 2 = Balanced etc.", StanceType); + + //OutputStream.WriteLine(" {0}: {1}", StanceNum, StanceType); + } + OutputStream.WriteLine(""); + } + + OutputStream.WriteLine("\r\nBuffer position at end is {0}", Buffer.GetPosition()); + OutputStream.WriteLine(""); + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchAugust15-2012.cs b/utils/EQExtractor2/EQExtractor2/PatchAugust15-2012.cs new file mode 100644 index 000000000..9351f0663 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchAugust15-2012.cs @@ -0,0 +1,46 @@ +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchAugust152012Decoder : PatchJuly132012Decoder + { + public PatchAugust152012Decoder() + { + Version = "EQ Client Build Date August 15 2012."; + + ExpectedPPLength = 33904; + + PPZoneIDOffset = 26572; + + PatchConfFileName = "patch_August15-2012.conf"; + } + + override public Item DecodeItemPacket(byte[] PacketBuffer) + { + ByteStream Buffer = new ByteStream(PacketBuffer); + + Item NewItem = new Item(); + + Buffer.SetPosition(30); + NewItem.MerchantSlot = Buffer.ReadByte(); + NewItem.Price = Buffer.ReadUInt32(); + Buffer.SkipBytes(5); + NewItem.Quantity = Buffer.ReadInt32(); + Buffer.SetPosition(109); + // 109 + NewItem.Name = Buffer.ReadString(true); + NewItem.Lore = Buffer.ReadString(true); + NewItem.IDFile = Buffer.ReadString(true); + Buffer.SkipBytes(1); + NewItem.ID = Buffer.ReadUInt32(); + + return NewItem; + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchDec7-2010.cs b/utils/EQExtractor2/EQExtractor2/PatchDec7-2010.cs new file mode 100644 index 000000000..97b109900 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchDec7-2010.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchDec072010Decoder : PatchOct202010Decoder + { + public PatchDec072010Decoder() + { + Version = "EQ Client Build Date December 7 2010."; + + PatchConfFileName = "patch_Dec7-2010.conf"; + + } + override public IdentificationStatus Identify(int OpCode, int Size, PacketDirection Direction) + { + if ((OpCode == OpManager.OpCodeNameToNumber("OP_ZoneEntry")) && (Direction == PacketDirection.ClientToServer)) + return IdentificationStatus.Tentative; + + if ((OpCode == OpManager.OpCodeNameToNumber("OP_SendAATable")) && (Direction == PacketDirection.ServerToClient) && + (Size == 120)) + return IdentificationStatus.Yes; + + return IdentificationStatus.No; + } + + public override void RegisterExplorers() + { + //OpManager.RegisterExplorer("OP_ClientUpdate", ExploreClientUpdate); + } + + public void ExploreClientUpdate(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt16 SpawnID = Buffer.ReadUInt16(); + Buffer.SkipBytes(6); + float x = Buffer.ReadSingle(); + float y = Buffer.ReadSingle(); + Buffer.SkipBytes(12); + float z = Buffer.ReadSingle(); + + Buffer.SkipBytes(4); + UInt32 Temp = Buffer.ReadUInt32(); + Temp = Temp & 0x3FFFFF; + Temp = Temp >> 10; + float heading = Utils.EQ19ToFloat((Int32)(Temp)); + + OutputStream.WriteLine("Loc: {0}, {1}, {2} Heading: {3}", x, y, z, heading); + + OutputStream.WriteLine(""); + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchDecember10-2012.cs b/utils/EQExtractor2/EQExtractor2/PatchDecember10-2012.cs new file mode 100644 index 000000000..4d36525f4 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchDecember10-2012.cs @@ -0,0 +1,1641 @@ +using System; +using System.IO; +using System.Diagnostics; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchDecember102012Decoder : PatchAugust152012Decoder + { + public PatchDecember102012Decoder() + { + Version = "EQ Client Build Date December 10 2012."; + + ExpectedPPLength = -1; + + PPZoneIDOffset = -1; + + PatchConfFileName = "patch_Dec10-2012.conf"; + + PacketsToMatch = new PacketToMatch[] { + new PacketToMatch { OPCodeName = "OP_AckPacket", Direction = PacketDirection.ClientToServer, RequiredSize = 4, VersionMatched = false }, + new PacketToMatch { OPCodeName = "OP_ZoneEntry", Direction = PacketDirection.ClientToServer, RequiredSize = 76, VersionMatched = false }, + new PacketToMatch { OPCodeName = "OP_PlayerProfile", Direction = PacketDirection.ServerToClient, RequiredSize = -1, VersionMatched = true }, + }; + + WaitingForPacket = 0; + } + + override public IdentificationStatus Identify(int OpCode, int Size, PacketDirection Direction) + { + if ((OpCode == OpManager.OpCodeNameToNumber(PacketsToMatch[WaitingForPacket].OPCodeName)) && + (Direction == PacketsToMatch[WaitingForPacket].Direction)) + { + if((PacketsToMatch[WaitingForPacket].RequiredSize >= 0) && (Size != PacketsToMatch[WaitingForPacket].RequiredSize)) + return IdentificationStatus.No; + + if(PacketsToMatch[WaitingForPacket].VersionMatched) + return IdentificationStatus.Yes; + + WaitingForPacket++; + + return IdentificationStatus.Tentative; + } + + return IdentificationStatus.No; + } + + override public int VerifyPlayerProfile() + { + List PlayerProfilePacket = GetPacketsOfType("OP_PlayerProfile", PacketDirection.ServerToClient); + + if (PlayerProfilePacket.Count == 0) + { + return 0; + } + else + { + // We should really verify the variable length PP here ... + + return -1; + } + + return ExpectedPPLength; + } + + override public PositionUpdate Decode_OP_MobUpdate(byte[] MobUpdatePacket) + { + PositionUpdate PosUpdate = new PositionUpdate(); + + ByteStream Buffer = new ByteStream(MobUpdatePacket); + + PosUpdate.SpawnID = Buffer.ReadUInt16(); + + Buffer.SkipBytes(2); + + UInt32 Word1 = Buffer.ReadUInt32(); + + UInt32 Word2 = Buffer.ReadUInt32(); + + UInt16 Word3 = Buffer.ReadUInt16(); + + PosUpdate.p.y = Utils.EQ19ToFloat((Int32)(Word1 & 0x7FFFF)); + + // Z is in the top 13 bits of Word1 and the bottom 6 of Word2 + + UInt32 ZPart1 = Word1 >> 19; // ZPart1 now has low order bits of Z in bottom 13 bits + UInt32 ZPart2 = Word2 & 0x3F; // ZPart2 now has high order bits of Z in bottom 6 bits + + ZPart2 = ZPart2 << 13; + + PosUpdate.p.z = Utils.EQ19ToFloat((Int32)(ZPart1 | ZPart2)); + + PosUpdate.p.x = Utils.EQ19ToFloat((Int32)(Word2 >> 13) & 0x7FFFF); + + PosUpdate.p.heading = Utils.EQ19ToFloat((Int32)((Word3 >> 4) & 0xFFF)); + + PosUpdate.HighRes = false; + + return PosUpdate; + } + + override public UInt16 GetZoneNumber() + { + + // A return value of zero from this method should be intepreted as 'Unable to identify patch version'. + + // Thanks to ShowEQ team for details on how to parse the variable length PP + + List PlayerProfilePacket = GetPacketsOfType("OP_PlayerProfile", PacketDirection.ServerToClient); + + if (PlayerProfilePacket.Count == 0) + { + return 0; + } + + ByteStream Buffer = new ByteStream(PlayerProfilePacket[0]); + + Buffer.SkipBytes(24); + + UInt32 BindCount = Buffer.ReadUInt32(); + + for (int i = 0; i < BindCount; ++i) + { + Buffer.SkipBytes(20); // sizeof(Bind Struct) + } + Buffer.SkipBytes(8); // Deity, intoxication + + UInt32 SpellRefreshCount = Buffer.ReadUInt32(); + + for (int i = 0; i < SpellRefreshCount; ++i) + { + Buffer.SkipBytes(4); + } + + UInt32 EquipmentCount = Buffer.ReadUInt32(); + + for (int i = 0; i < EquipmentCount; ++i) + { + Buffer.SkipBytes(20); + } + + UInt32 SomethingCount = Buffer.ReadUInt32(); + + for (int i = 0; i < SomethingCount; ++i) + { + Buffer.SkipBytes(20); + } + + SomethingCount = Buffer.ReadUInt32(); + + for (int i = 0; i < SomethingCount; ++i) + { + Buffer.SkipBytes(4); + } + + SomethingCount = Buffer.ReadUInt32(); + + for (int i = 0; i < SomethingCount; ++i) + { + Buffer.SkipBytes(4); + } + + Buffer.SkipBytes(52); // Per SEQ, this looks like face, haircolor, beardcolor etc. + + UInt32 Points = Buffer.ReadUInt32(); + UInt32 Mana = Buffer.ReadUInt32(); + UInt32 CurHP = Buffer.ReadUInt32(); + + Buffer.SkipBytes(28); + Buffer.SkipBytes(28); + + UInt32 AACount = Buffer.ReadUInt32(); + + for (int i = 0; i < AACount; ++i) + { + Buffer.SkipBytes(12); + } + + SomethingCount = Buffer.ReadUInt32(); + + for (int i = 0; i < SomethingCount; ++i) + { + Buffer.SkipBytes(4); + } + SomethingCount = Buffer.ReadUInt32(); + + for (int i = 0; i < SomethingCount; ++i) + { + Buffer.SkipBytes(4); + } + SomethingCount = Buffer.ReadUInt32(); + + for (int i = 0; i < SomethingCount; ++i) + { + Buffer.SkipBytes(4); + } + + SomethingCount = Buffer.ReadUInt32(); + + for (int i = 0; i < SomethingCount; ++i) + { + Buffer.SkipBytes(4); + } + + SomethingCount = Buffer.ReadUInt32(); + + for (int i = 0; i < SomethingCount; ++i) + { + Buffer.SkipBytes(4); + } + + SomethingCount = Buffer.ReadUInt32(); + + for (int i = 0; i < SomethingCount; ++i) + { + Buffer.SkipBytes(4); + } + + UInt32 SpellBookSlots = Buffer.ReadUInt32(); + + for (int i = 0; i < SpellBookSlots; ++i) + { + Buffer.SkipBytes(4); + } + + UInt32 SpellMemSlots = Buffer.ReadUInt32(); + + for (int i = 0; i < SpellMemSlots; ++i) + { + Buffer.SkipBytes(4); + } + + SomethingCount = Buffer.ReadUInt32(); + + for (int i = 0; i < SomethingCount; ++i) + { + Buffer.SkipBytes(4); + } + + Buffer.SkipBytes(1); + + UInt32 BuffCount = Buffer.ReadUInt32(); + + for (int i = 0; i < BuffCount; ++i) + { + Buffer.SkipBytes(80); + } + + UInt32 Plat = Buffer.ReadUInt32(); + UInt32 Gold = Buffer.ReadUInt32(); + UInt32 Silver = Buffer.ReadUInt32(); + UInt32 Copper = Buffer.ReadUInt32(); + + Buffer.SkipBytes(16); // Money on cursor + + Buffer.SkipBytes(20); + + UInt32 AASpent = Buffer.ReadUInt32(); + + Buffer.SkipBytes(30); + + UInt32 BandolierCount = Buffer.ReadUInt32(); + + for (int i = 0; i < BandolierCount; ++i) + { + Buffer.ReadString(false); + + Buffer.ReadString(false); + Buffer.SkipBytes(8); + + Buffer.ReadString(false); + Buffer.SkipBytes(8); + + Buffer.ReadString(false); + Buffer.SkipBytes(8); + + Buffer.ReadString(false); + Buffer.SkipBytes(8); + } + + UInt32 PotionCount = Buffer.ReadUInt32(); + + for (int i = 0; i < PotionCount; ++i) + { + Buffer.ReadString(false); + Buffer.SkipBytes(8); + } + + Buffer.SkipBytes(100); + + int CurrentPosition = Buffer.GetPosition(); + + String PlayerName = Buffer.ReadString(false); + + Buffer.SetPosition(CurrentPosition + 64); + + Buffer.SkipBytes(96); + + // This is what I am after ... + + UInt16 ZoneID = Buffer.ReadUInt16(); + + return ZoneID; + + } + + override public List GetSpawns() + { + List ZoneSpawns = new List(); + + List SpawnPackets = GetPacketsOfType("OP_ZoneEntry", PacketDirection.ServerToClient); + + foreach (byte[] SpawnPacket in SpawnPackets) + { + ZoneEntryStruct NewSpawn = new ZoneEntryStruct(); + + ByteStream Buffer = new ByteStream(SpawnPacket); + + NewSpawn.SpawnName = Buffer.ReadString(true); + + NewSpawn.SpawnName = Utils.MakeCleanName(NewSpawn.SpawnName); + + NewSpawn.SpawnID = Buffer.ReadUInt32(); + + NewSpawn.Level = Buffer.ReadByte(); + + float UnkSize = Buffer.ReadSingle(); + + NewSpawn.IsNPC = Buffer.ReadByte(); + + UInt32 Bitfield = Buffer.ReadUInt32(); + + NewSpawn.Gender = (Bitfield & 3); + + Byte OtherData = Buffer.ReadByte(); + + Buffer.SkipBytes(8); // Skip 8 unknown bytes + + NewSpawn.DestructableString1 = ""; + NewSpawn.DestructableString2 = ""; + NewSpawn.DestructableString3 = ""; + + if ((NewSpawn.IsNPC > 0) && ((OtherData & 1) > 0)) + { + // Destructable Objects + NewSpawn.DestructableString1 = Buffer.ReadString(false); + NewSpawn.DestructableString2 = Buffer.ReadString(false); + NewSpawn.DestructableString3 = Buffer.ReadString(false); + Buffer.SkipBytes(53); + } + + if ((OtherData & 4) > 0) + { + // Auras + Buffer.ReadString(false); + Buffer.ReadString(false); + Buffer.SkipBytes(54); + } + + NewSpawn.PropCount = Buffer.ReadByte(); + + if (NewSpawn.PropCount > 0) + NewSpawn.BodyType = Buffer.ReadUInt32(); + else + NewSpawn.BodyType = 0; + + + for (int j = 1; j < NewSpawn.PropCount; ++j) + Buffer.SkipBytes(4); + + Buffer.SkipBytes(1); // Skip HP % + NewSpawn.HairColor = Buffer.ReadByte(); + NewSpawn.BeardColor = Buffer.ReadByte(); + NewSpawn.EyeColor1 = Buffer.ReadByte(); + NewSpawn.EyeColor2 = Buffer.ReadByte(); + NewSpawn.HairStyle = Buffer.ReadByte(); + NewSpawn.Beard = Buffer.ReadByte(); + + NewSpawn.DrakkinHeritage = Buffer.ReadUInt32(); + NewSpawn.DrakkinTattoo = Buffer.ReadUInt32(); + NewSpawn.DrakkinDetails = Buffer.ReadUInt32(); + + NewSpawn.EquipChest2 = Buffer.ReadByte(); + + bool UseWorn = (NewSpawn.EquipChest2 == 255); + + Buffer.SkipBytes(2); // 2 Unknown bytes; + + NewSpawn.Helm = Buffer.ReadByte(); + + NewSpawn.Size = Buffer.ReadSingle(); + + NewSpawn.Face = Buffer.ReadByte(); + + NewSpawn.WalkSpeed = Buffer.ReadSingle(); + + NewSpawn.RunSpeed = Buffer.ReadSingle(); + + NewSpawn.Race = Buffer.ReadUInt32(); + + Buffer.SkipBytes(1); // Skip Holding + + NewSpawn.Deity = Buffer.ReadUInt32(); + + Buffer.SkipBytes(8); // Skip GuildID and GuildRank + + NewSpawn.Class = Buffer.ReadByte(); + + Buffer.SkipBytes(4); // Skip PVP, Standstate, Light, Flymode + + NewSpawn.LastName = Buffer.ReadString(true); + + Buffer.SkipBytes(6); + + NewSpawn.PetOwnerID = Buffer.ReadUInt32(); + + Buffer.SkipBytes(25); + + NewSpawn.MeleeTexture1 = 0; + NewSpawn.MeleeTexture2 = 0; + + if ( (NewSpawn.IsNPC == 0) || NPCType.IsPlayableRace(NewSpawn.Race)) + { + for (int ColourSlot = 0; ColourSlot < 9; ++ColourSlot) + NewSpawn.SlotColour[ColourSlot] = Buffer.ReadUInt32(); + + for (int i = 0; i < 9; ++i) + { + NewSpawn.Equipment[i] = Buffer.ReadUInt32(); + + UInt32 Equip3 = Buffer.ReadUInt32(); + + UInt32 Equip2 = Buffer.ReadUInt32(); + + UInt32 Equip1 = Buffer.ReadUInt32(); + + UInt32 Equip0 = Buffer.ReadUInt32(); + } + + if (NewSpawn.Equipment[Constants.MATERIAL_CHEST] > 0) + { + NewSpawn.EquipChest2 = (byte)NewSpawn.Equipment[Constants.MATERIAL_CHEST]; + + } + + NewSpawn.ArmorTintRed = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 16) & 0xff); + + NewSpawn.ArmorTintGreen = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 8) & 0xff); + + NewSpawn.ArmorTintBlue = (byte)(NewSpawn.SlotColour[Constants.MATERIAL_CHEST] & 0xff); + + if (NewSpawn.Equipment[Constants.MATERIAL_PRIMARY] > 0) + NewSpawn.MeleeTexture1 = NewSpawn.Equipment[Constants.MATERIAL_PRIMARY]; + + if (NewSpawn.Equipment[Constants.MATERIAL_SECONDARY] > 0) + NewSpawn.MeleeTexture2 = NewSpawn.Equipment[Constants.MATERIAL_SECONDARY]; + + if (UseWorn) + NewSpawn.Helm = (byte)NewSpawn.Equipment[Constants.MATERIAL_HEAD]; + else + NewSpawn.Helm = 0; + + } + else + { + // Non playable race + + Buffer.SkipBytes(20); + + NewSpawn.MeleeTexture1 = Buffer.ReadUInt32(); + Buffer.SkipBytes(16); + NewSpawn.MeleeTexture2 = Buffer.ReadUInt32(); + Buffer.SkipBytes(16); + } + + if (NewSpawn.EquipChest2 == 255) + NewSpawn.EquipChest2 = 0; + + if (NewSpawn.Helm == 255) + NewSpawn.Helm = 0; + + UInt32 Position1 = Buffer.ReadUInt32(); + + UInt32 Position2 = Buffer.ReadUInt32(); + + UInt32 Position3 = Buffer.ReadUInt32(); + + UInt32 Position4 = Buffer.ReadUInt32(); + + UInt32 Position5 = Buffer.ReadUInt32(); + + NewSpawn.YPos = Utils.EQ19ToFloat((Int32)(Position1 >> 12)); + + NewSpawn.ZPos = Utils.EQ19ToFloat((Int32)(Position3 >> 13) & 0x7FFFF); + + NewSpawn.XPos = Utils.EQ19ToFloat((Int32)(Position4) & 0x7FFFF); + + NewSpawn.Heading = Utils.EQ19ToFloat((Int32)(Position5) & 0x7FFFF); + + + + + if ((OtherData & 16) > 0) + { + NewSpawn.Title = Buffer.ReadString(false); + } + + if ((OtherData & 32) > 0) + { + NewSpawn.Suffix = Buffer.ReadString(false); + } + + // unknowns + Buffer.SkipBytes(8); + + NewSpawn.IsMercenary = Buffer.ReadByte(); + + Buffer.SkipBytes(54); + + Debug.Assert(Buffer.GetPosition() == Buffer.Length(), "Length mismatch while parsing zone spawns"); + + ZoneSpawns.Add(NewSpawn); + } + + return ZoneSpawns; + } + + + public override void RegisterExplorers() + { + //OpManager.RegisterExplorer("OP_PlayerProfile", ExplorePlayerProfile); + //OpManager.RegisterExplorer("OP_ZoneEntry", ExploreZoneEntry); + //OpManager.RegisterExplorer("OP_RequestClientZoneChange", ExploreRequestClientZoneChange); + //OpManager.RegisterExplorer("OP_NPCMoveUpdate", ExploreNPCMoveUpdate); + //OpManager.RegisterExplorer("OP_MobUpdate", ExploreMobUpdate); + //OpManager.RegisterExplorer("OP_ClientUpdate", ExploreClientUpdate); + //OpManager.RegisterExplorer("OP_OpenNewTasksWindow", ExploreOpenNewTasksWindow); + //OpManager.RegisterExplorer("OP_TaskDescription", ExploreTaskDescription); + //OpManager.RegisterExplorer("OP_CharInventory", ExploreInventory); + } + + public void DecodeItemPacket(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + String UnkString = Buffer.ReadString(false); + Buffer.SkipBytes(88); + String ItemName = Buffer.ReadString(false); + String ItemLore = Buffer.ReadString(false); + String ItemIDFile = Buffer.ReadString(false); + Buffer.ReadString(false); + + UInt32 ItemID = Buffer.ReadUInt32(); + OutputStream.WriteLine("ItemName: {0}, IDFile: {1}", ItemName, ItemIDFile); + + Buffer.SkipBytes(251); + + String CharmFile = Buffer.ReadString(false); + + OutputStream.WriteLine("CharmFile: {0}", CharmFile); + + Buffer.SkipBytes(74); // Secondary BS + + String FileName = Buffer.ReadString(false); + OutputStream.WriteLine("FileName: {0}", CharmFile); + + Buffer.SkipBytes(76); // Tertiary BS + + UInt32 ClickEffect = Buffer.ReadUInt32(); + Buffer.SkipBytes(26); // ClickEffect Struct + String ClickName = Buffer.ReadString(false); + OutputStream.WriteLine("ClickEffect = {0}, ClickName = {1}", ClickEffect, ClickName); + Buffer.ReadUInt32(); + + ClickEffect = Buffer.ReadUInt32(); + Buffer.SkipBytes(26); // ClickEffect Struct + ClickName = Buffer.ReadString(false); + OutputStream.WriteLine("ClickEffect = {0}, ClickName = {1}", ClickEffect, ClickName); + Buffer.ReadUInt32(); + + ClickEffect = Buffer.ReadUInt32(); + Buffer.SkipBytes(26); // ClickEffect Struct + ClickName = Buffer.ReadString(false); + OutputStream.WriteLine("ClickEffect = {0}, ClickName = {1}", ClickEffect, ClickName); + Buffer.ReadUInt32(); + + ClickEffect = Buffer.ReadUInt32(); + Buffer.SkipBytes(26); // ClickEffect Struct + ClickName = Buffer.ReadString(false); + OutputStream.WriteLine("ClickEffect = {0}, ClickName = {1}", ClickEffect, ClickName); + Buffer.ReadUInt32(); + + ClickEffect = Buffer.ReadUInt32(); + Buffer.SkipBytes(26); // ClickEffect Struct + ClickName = Buffer.ReadString(false); + OutputStream.WriteLine("ClickEffect = {0}, ClickName = {1}", ClickEffect, ClickName); + Buffer.ReadUInt32(); + + ClickEffect = Buffer.ReadUInt32(); + Buffer.SkipBytes(26); // ClickEffect Struct + ClickName = Buffer.ReadString(false); + OutputStream.WriteLine("ClickEffect = {0}, ClickName = {1}", ClickEffect, ClickName); + Buffer.ReadUInt32(); + + //Buffer.SkipBytes(167); + Buffer.SkipBytes(125); + //Byte UnkByte = Buffer.ReadByte(); + //OutputStream.WriteLine("Unk byte is {0:X}", UnkByte); + OutputStream.WriteLine("At String ? Pos is {0}", Buffer.GetPosition()); + UnkString = Buffer.ReadString(false); + OutputStream.WriteLine("Unk String is {0}", UnkString); + Buffer.SkipBytes(41); + UInt32 SubItemCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("Buffer Pos: {0}, SubItemCount = {1}", Buffer.GetPosition(), SubItemCount); + + for (int j = 0; j < SubItemCount; ++j) + { + Buffer.ReadUInt32(); + DecodeItemPacket(OutputStream, Buffer, Direction); + } + + + } + + + public void ExploreInventory(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + if (Buffer.Length() < 4) + return; + + UInt32 Count = Buffer.ReadUInt32(); + + for (int i = 0; i < Count; ++i) + { + try + { + DecodeItemPacket(OutputStream, Buffer, Direction); + } + catch + { + return; + } + } + } + + public void ExploreTaskDescription(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 Seq = Buffer.ReadUInt32(); + UInt32 TaskID = Buffer.ReadUInt32(); + UInt32 Unk1 = Buffer.ReadUInt32(); + UInt32 Unk2 = Buffer.ReadUInt32(); + byte Unk3 = Buffer.ReadByte(); + + OutputStream.WriteLine("Seq: {0}, TaskID: {1}, Unk1: {2:X}, Unk2: {3:X}, Unk3: {4:X}", Seq, TaskID, Unk1, Unk2, Unk3); + + String Title = Buffer.ReadString(false); + OutputStream.WriteLine("Title: {0}", Title); + + UInt32 Duration = Buffer.ReadUInt32(); + UInt32 Unk4 = Buffer.ReadUInt32(); + UInt32 StartTime = Buffer.ReadUInt32(); + + String Desc = Buffer.ReadString(false); + + + OutputStream.WriteLine("Duration: {0}, Unk4: {1:X}, StartTime: {2:X}", Duration, Unk4, StartTime); + + OutputStream.WriteLine("Desc: {0}", Desc); + + UInt32 RewardCount = Buffer.ReadUInt32(); + UInt32 Unk5 = Buffer.ReadUInt32(); + UInt32 Unk6 = Buffer.ReadUInt32(); + UInt16 Unk7 = Buffer.ReadUInt16(); + + OutputStream.WriteLine("RewardCount: {0}, Unk5: {1:X}, Unk6: {2:X}, Unk7: {3:X}", Duration, Unk5, Unk6, Unk7); + + string MyString = ""; + + byte b; + + while ((b = Buffer.ReadByte()) != 0) + { + if (b == 0x12) + continue; + + MyString += Convert.ToChar(b); + } + + OutputStream.WriteLine("RewardString: {0}", MyString); + + UInt32 Unk8 = Buffer.ReadUInt32(); + byte Unk9 = Buffer.ReadByte(); + + OutputStream.WriteLine("Unk8: {0:X}, Unk9: {1:X}", Unk8, Unk9); + + } + + public void ExploreOpenNewTasksWindow(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 NumTasks = Buffer.ReadUInt32(); + UInt32 Unknown = Buffer.ReadUInt32(); + UInt32 TaskGiver = Buffer.ReadUInt32(); + + OutputStream.WriteLine("Number of Tasks: {0}, Given by: {1}", NumTasks, TaskGiver); + + for (int i = 0; i < NumTasks; ++i) + { + UInt32 TaskID = Buffer.ReadUInt32(); + float Unk1 = Buffer.ReadSingle(); + UInt32 TimeLimit = Buffer.ReadUInt32(); + UInt32 Unk2 = Buffer.ReadUInt32(); + + string Title = Buffer.ReadString(false); + string Description = Buffer.ReadString(false); + string UnkString = Buffer.ReadString(false); + + OutputStream.WriteLine("TaskID: {0}, Title: {1}", TaskID, Title); + + UInt32 ActivityCount = Buffer.ReadUInt32(); + OutputStream.WriteLine("Unknown: {0}", ActivityCount); + + OutputStream.WriteLine(""); + for (int j = 0; j < ActivityCount; ++j) + { + OutputStream.WriteLine("Activity {0}", i); + OutputStream.WriteLine(""); + OutputStream.WriteLine(" Unknown: {0}", Buffer.ReadUInt32()); + OutputStream.WriteLine(" Unknown: {0}", Buffer.ReadUInt32()); + OutputStream.WriteLine(" Unknown: {0}", Buffer.ReadUInt32()); + + OutputStream.WriteLine(" String: {0}", Buffer.ReadString(false)); + + UInt32 StringLength = Buffer.ReadUInt32(); + OutputStream.WriteLine(" StringLength: {0}", StringLength); + + string MyString = ""; + + for (int k = 0; k < StringLength; ++k) + MyString += Convert.ToChar(Buffer.ReadByte()); + + OutputStream.WriteLine(" Weird String: {0}", MyString); + + + OutputStream.WriteLine(" Unknown: {0}", Buffer.ReadUInt32()); + OutputStream.WriteLine(" Unknown: {0}", Buffer.ReadUInt32()); + + OutputStream.WriteLine(" String: {0}", Buffer.ReadString(false)); + OutputStream.WriteLine(" Unknown 2 bytes: {0}", Buffer.ReadUInt16()); + + //if (i == 3) + //{ + // OutputStream.WriteLine("Offset is now: {0}", Buffer.GetPosition()); + // return; + //} + OutputStream.WriteLine(" String: {0}", Buffer.ReadString(false)); + OutputStream.WriteLine(" String: {0}", Buffer.ReadString(false)); + OutputStream.WriteLine(" String: {0}", Buffer.ReadString(false)); + } + OutputStream.WriteLine(""); + //OutputStream.WriteLine("Offset is now: {0}", Buffer.GetPosition()); + + } + } + + + public void ExploreRequestClientZoneChange(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 ZoneID = Buffer.ReadUInt32(); + UInt32 Unknown = Buffer.ReadUInt32(); + float y = Buffer.ReadSingle(); + float x = Buffer.ReadSingle(); + float z = Buffer.ReadSingle(); + float heading = Buffer.ReadSingle(); + Buffer.SkipBytes(148); + float uf = Buffer.ReadSingle(); + OutputStream.WriteLine("UF = {0}", uf); + + } + + public void ExploreClientUpdate(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + if (Direction == PacketDirection.ServerToClient) + { + UInt32 SpawnID = Buffer.ReadUInt16(); + UInt32 SpawnID2 = Buffer.ReadUInt16(); + OutputStream.WriteLine("ClientUpdate S->C SpawnID: {0}, SpawnID2: {1}", SpawnID, SpawnID2); + + UInt32 Word = Buffer.ReadUInt32(); + float Y = Utils.EQ19ToFloat((int)(Word >> 12)); + OutputStream.WriteLine("Y = {0}", Y); + + //Buffer.SkipBytes(6); + //float DeltaY = Buffer.ReadSingle(); + //float YPos = Buffer.ReadSingle(); + //float XPos = Buffer.ReadSingle(); + //float DeltaHeading = Utils.EQ19ToFloat((int)(Word & 0x3FF)); + } + + + + + } + + public void ExploreNPCMoveUpdate(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + PositionUpdate PosUpdate; + + PosUpdate = Decode_OP_NPCMoveUpdate(Buffer.Buffer); + + OutputStream.WriteLine("SpawnID: {0}, X = {1}, Y = {2}, Z = {3}, Heading = {4}", PosUpdate.SpawnID, PosUpdate.p.x, PosUpdate.p.y, PosUpdate.p.z, PosUpdate.p.heading); + } + + public void ExploreMobUpdate(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + PositionUpdate PosUpdate; + + PosUpdate = Decode_OP_MobUpdate(Buffer.Buffer); + + OutputStream.WriteLine("SpawnID: {0}, X = {1}, Y = {2}, Z = {3}, Heading = {4}", PosUpdate.SpawnID, PosUpdate.p.x, PosUpdate.p.y, PosUpdate.p.z, PosUpdate.p.heading); + } + + public void ExplorePlayerProfile(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + OutputStream.WriteLine("{0, -5}: Checksum = {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: ChecksumSize = {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown = {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown = {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine(""); + OutputStream.WriteLine("{0, -5}: Gender = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Race = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Class = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Level = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Level1 = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + + OutputStream.WriteLine(""); + UInt32 BindCount = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: BindCount = {1}", Buffer.GetPosition() - 4, BindCount); + + for (int i = 0; i < BindCount; ++i) + { + OutputStream.WriteLine("{0, -5}: Bind: {1} Zone: {2} XYZ: {3},{4},{5} Heading: {6}", + Buffer.GetPosition(), i, Buffer.ReadUInt32(), Buffer.ReadSingle(), Buffer.ReadSingle(),Buffer.ReadSingle(),Buffer.ReadSingle()); + } + + OutputStream.WriteLine(""); + OutputStream.WriteLine("{0, -5}: Deity = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Intoxication = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine(""); + + //Buffer.SkipBytes(8); // Deity, intoxication + + UInt32 UnknownCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: Unknown Count = {1}", Buffer.GetPosition() - 4, UnknownCount); + + + + for (int i = 0; i < UnknownCount; ++i) + { + OutputStream.WriteLine("{0, -5}: Unknown : {1}, Value = {2}", Buffer.GetPosition(), i, Buffer.ReadUInt32()); + //Buffer.SkipBytes(4); + } + + UInt32 EquipmentCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: EquipmentCount = {1}", Buffer.GetPosition() - 4, EquipmentCount); + + for (int i = 0; i < EquipmentCount; ++i) + { + OutputStream.Write("{0, -5}: Equip: {1} Values: ", Buffer.GetPosition(), i); + for (int j = 0; j < 5; ++j) + OutputStream.Write("{0} ", Buffer.ReadUInt32()); + + OutputStream.WriteLine(""); + //Buffer.SkipBytes(20); + } + + UInt32 EquipmentCount2 = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: EquipmentCount2 = {1}", Buffer.GetPosition() - 4, EquipmentCount2); + + for (int i = 0; i < EquipmentCount2; ++i) + { + OutputStream.Write("{0, -5}: Equip2: {1} Values: ", Buffer.GetPosition(), i); + for (int j = 0; j < 5; ++j) + OutputStream.Write("{0} ", Buffer.ReadUInt32()); + + OutputStream.WriteLine(""); + //Buffer.SkipBytes(20); + } + + + + UInt32 TintCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: TintCount = {1}", Buffer.GetPosition() - 4, TintCount); + + for (int i = 0; i < TintCount; ++i) + { + OutputStream.WriteLine("{0, -5}: TintCount : {1}, Value = {2}", Buffer.GetPosition(), i, Buffer.ReadUInt32()); + //Buffer.SkipBytes(4); + } + + UInt32 TintCount2 = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: TintCount2 = {1}", Buffer.GetPosition() - 4, TintCount2); + + for (int i = 0; i < TintCount; ++i) + { + OutputStream.WriteLine("{0, -5}: TintCount2 : {1}, Value = {2}", Buffer.GetPosition(), i, Buffer.ReadUInt32()); + //Buffer.SkipBytes(4); + } + + OutputStream.WriteLine("{0, -5}: Hair Color = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Beard Color = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Eye1 Color = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Eye2 Color = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Hairstyle = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Beard = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Face = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Drakkin Heritage = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Drakkin Tattoo = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Drakkin Details = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Height = {1}", Buffer.GetPosition(), Buffer.ReadSingle()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadSingle()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadSingle()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadSingle()); + OutputStream.WriteLine("{0, -5}: Primary = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Secondary = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + + + //Buffer.SkipBytes(52); // Per SEQ, this looks like face, haircolor, beardcolor etc. + OutputStream.WriteLine("{0, -5}: Unspent Skill Points = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Mana = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Current HP = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + //UInt32 Points = Buffer.ReadUInt32(); + //UInt32 Mana = Buffer.ReadUInt32(); + //UInt32 CurHP = Buffer.ReadUInt32(); + + //OutputStream.WriteLine("Points, Mana, CurHP = {0}, {1}, {2}", Points, Mana, CurHP); + + OutputStream.WriteLine("{0, -5}: STR = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: STA = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: CHA = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: DEX = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: INT = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: AGI = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: WIS = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + //Buffer.SkipBytes(28); + //Buffer.SkipBytes(28); + + UInt32 AACount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: AA Count = {1}", Buffer.GetPosition() - 4, AACount); + + + for (int i = 0; i < AACount; ++i) + { + OutputStream.WriteLine(" AA: {0}, Value: {1}, Unknown08: {2}", Buffer.ReadUInt32(), Buffer.ReadUInt32(), Buffer.ReadUInt32()); + //Buffer.SkipBytes(12); + } + + UInt32 SkillCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: Skill Count = {1}", Buffer.GetPosition() - 4, SkillCount); + + for (int i = 0; i < SkillCount; ++i) + { + Buffer.SkipBytes(4); + } + + UInt32 SomethingCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: Something Count = {1}", Buffer.GetPosition() - 4, SomethingCount); + + + for (int i = 0; i < SomethingCount; ++i) + { + Buffer.SkipBytes(4); + } + + UInt32 DisciplineCount = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: Discipline Count = {1}", Buffer.GetPosition() - 4, DisciplineCount); + + for (int i = 0; i < DisciplineCount; ++i) + { + Buffer.SkipBytes(4); + } + + UInt32 TimeStampCount = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: TimeStamp Count = {1}", Buffer.GetPosition() - 4, TimeStampCount); + + for (int i = 0; i < TimeStampCount; ++i) + { + Buffer.SkipBytes(4); + } + + UInt32 RecastCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: Recast Count = {1}", Buffer.GetPosition() - 4, RecastCount); + + for (int i = 0; i < RecastCount; ++i) + { + Buffer.SkipBytes(4); + } + + UInt32 TimeStamp2Count = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: TimeStamp2 Count = {1}", Buffer.GetPosition() - 4, TimeStamp2Count); + + for (int i = 0; i < TimeStamp2Count; ++i) + { + Buffer.SkipBytes(4); + } + + + UInt32 SpellBookSlots = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: SpellBookSlot Count = {1}", Buffer.GetPosition() - 4, SpellBookSlots); + + for (int i = 0; i < SpellBookSlots; ++i) + { + Buffer.SkipBytes(4); + } + + UInt32 SpellMemSlots = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: Spell Mem Count = {1}", Buffer.GetPosition() - 4, SpellMemSlots); + + for (int i = 0; i < SpellMemSlots; ++i) + { + Buffer.SkipBytes(4); + } + + SomethingCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: Unknown Count = {1}", Buffer.GetPosition() - 4, SomethingCount); + + for (int i = 0; i < SomethingCount; ++i) + { + Buffer.SkipBytes(4); + } + + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadByte()); + + UInt32 BuffCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: Buff Count = {1}", Buffer.GetPosition() - 4, BuffCount); + + for (int i = 0; i < BuffCount; ++i) + { + Buffer.ReadByte(); + float UnkFloat = Buffer.ReadSingle(); + UInt32 PlayerID = Buffer.ReadUInt32(); + Byte UnkByte = Buffer.ReadByte(); + UInt32 Counters1 = Buffer.ReadUInt32(); + UInt32 Duration = Buffer.ReadUInt32(); + Byte Level = Buffer.ReadByte(); + UInt32 SpellID = Buffer.ReadUInt32(); + UInt32 SlotID = Buffer.ReadUInt32(); + Buffer.SkipBytes(5); + UInt32 Counters2 = Buffer.ReadUInt32(); + OutputStream.WriteLine("Sl: {0}, UF: {1}, PID: {2}, UByte: {3}, Cnt1: {4}, Dur: {5}, Lvl: {6} SpellID: {7}, SlotID: {8}, Cnt2: {9}", + i, UnkFloat, PlayerID, UnkByte, Counters1, Duration, Level, SpellID, SlotID, Counters2); + Buffer.SkipBytes(44); + } + + OutputStream.WriteLine("{0, -5}: Plat = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Gold = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Silver = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Copper = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Plat Cursor = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Gold Cursor = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Silver Cursor = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Copper Cursor = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Toxicity? = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Thirst? = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Hunger? = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + //Buffer.SkipBytes(20); + + OutputStream.WriteLine("{0, -5}: AA Spent = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: AA Point Count? = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: AA Assigned = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: AA Spent General = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: AA Spent Archetype = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: AA Spent Class = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: AA Spent Special = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: AA Unspent = {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown", Buffer.GetPosition(), Buffer.ReadUInt16()); + + + //Buffer.SkipBytes(30); + + UInt32 BandolierCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: Bandolier Count = {1}", Buffer.GetPosition() - 4, BandolierCount); + + for (int i = 0; i < BandolierCount; ++i) + { + Buffer.ReadString(false); + + Buffer.ReadString(false); + Buffer.SkipBytes(8); + + Buffer.ReadString(false); + Buffer.SkipBytes(8); + + Buffer.ReadString(false); + Buffer.SkipBytes(8); + + Buffer.ReadString(false); + Buffer.SkipBytes(8); + } + + UInt32 PotionCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: Potion Count = {1}", Buffer.GetPosition() - 4, PotionCount); + + for (int i = 0; i < PotionCount; ++i) + { + Buffer.ReadString(false); + Buffer.SkipBytes(8); + } + + OutputStream.WriteLine("{0, -5}: Unknown {1}", Buffer.GetPosition(), Buffer.ReadInt32()); + OutputStream.WriteLine("{0, -5}: Item HP Total? {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Endurance Total? {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Mana Total? {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Unknown {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Expansion Count {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + UInt32 NameLength = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: Name Length: {1}", Buffer.GetPosition() - 4, NameLength); + + int CurrentPosition = Buffer.GetPosition(); + OutputStream.WriteLine("{0, -5}: Name: {1}", Buffer.GetPosition(), Buffer.ReadString(false)); + + Buffer.SetPosition(CurrentPosition + (int)NameLength); + + UInt32 LastNameLength = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: LastName Length: {1}", Buffer.GetPosition() - 4, LastNameLength); + + CurrentPosition = Buffer.GetPosition(); + OutputStream.WriteLine("{0, -5}: Last Name: {1}", Buffer.GetPosition(), Buffer.ReadString(false)); + + Buffer.SetPosition(CurrentPosition + (int)LastNameLength); + + OutputStream.WriteLine("{0, -5}: Birthday {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Account Start Date {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Last Login Date {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Time Played Minutes {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Time Entitled On Account {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Expansions {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + UInt32 LanguageCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("{0, -5}: Language Count = {1}", Buffer.GetPosition() - 4, LanguageCount); + + for (int i = 0; i < LanguageCount; ++i) + { + Buffer.SkipBytes(1); + } + + OutputStream.WriteLine("{0, -5}: Zone ID {1}", Buffer.GetPosition(), Buffer.ReadUInt16()); + OutputStream.WriteLine("{0, -5}: Zone Instance {1}", Buffer.GetPosition(), Buffer.ReadUInt16()); + OutputStream.WriteLine("{0, -5}: Y,X,Z {1},{2},{3} Heading: {4}", + Buffer.GetPosition(), Buffer.ReadSingle(), Buffer.ReadSingle(), Buffer.ReadSingle(), Buffer.ReadSingle()); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + + OutputStream.WriteLine("{0, -5}: GuildID? {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Experience {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + + OutputStream.WriteLine("{0, -5}: Bank Plat {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Bank Gold {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Bank Silver {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Bank Copper {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + UInt32 Unknown42 = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: Unknown, value 42? {1}", Buffer.GetPosition() - 4, Unknown42); + + Buffer.SkipBytes((int)(Unknown42 * 8)); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Career Tribute Favour {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Current Tribute Favour {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + + UInt32 PersonalTributeCount = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: Personal Tribute Count {1}", Buffer.GetPosition() - 4, PersonalTributeCount); + Buffer.SkipBytes((int)(PersonalTributeCount * 8)); + + UInt32 GuildTributeCount = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: Guild Tribute Count {1}", Buffer.GetPosition() - 4, GuildTributeCount); + Buffer.SkipBytes((int)(GuildTributeCount * 8)); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("Skipping 121 bytes starting at offset {0}", Buffer.GetPosition()); + Buffer.SkipBytes(121); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("Position now {0}", Buffer.GetPosition()); + + UInt32 Unknown64 = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: Unknown64 {1}", Buffer.GetPosition() - 4, Unknown64); + Buffer.SkipBytes((int)Unknown64); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + Unknown64 = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: Unknown64 {1}", Buffer.GetPosition() - 4, Unknown64); + Buffer.SkipBytes((int)Unknown64); + + Unknown64 = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: Unknown64 {1}", Buffer.GetPosition() - 4, Unknown64); + Buffer.SkipBytes((int)Unknown64); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("Skipping 320 bytes starting at offset {0}", Buffer.GetPosition()); + Buffer.SkipBytes(320); + + OutputStream.WriteLine("Skipping 343 bytes starting at offset {0}", Buffer.GetPosition()); + Buffer.SkipBytes(343); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + + UInt32 Unknown6 = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: Unknown6 {1} LDON Stuff ?", Buffer.GetPosition() - 4, Unknown6); + + for(int i = 0; i < Unknown6; ++i) + OutputStream.WriteLine("{0, -5}: Unknown LDON? {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + Unknown64 = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: Unknown64 {1}", Buffer.GetPosition() - 4, Unknown64); + Buffer.SkipBytes((int)Unknown64 * 4); + + // Air remaining ? + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + // Next 7 could be PVP stats, + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + // PVP LastKill struct ? + OutputStream.WriteLine("Skipping string + 24 bytes starting at offset {0}", Buffer.GetPosition()); + //Buffer.SkipBytes(25); + + Byte b; + do + { + b = Buffer.ReadByte(); + } while (b != 0); + + Buffer.SkipBytes(24); + + // PVP LastDeath struct ? + OutputStream.WriteLine("Skipping string + 24 bytes starting at offset {0}", Buffer.GetPosition()); + //Buffer.SkipBytes(25); + do + { + b = Buffer.ReadByte(); + } while (b != 0); + + Buffer.SkipBytes(24); + + // PVP Number of Kills in Last 24 hours ? + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + UInt32 Unknown50 = Buffer.ReadUInt32(); + OutputStream.WriteLine("{0, -5}: Unknown50 {1}", Buffer.GetPosition() - 4, Unknown50); + // PVP Recent Kills ? + OutputStream.WriteLine("Skipping 50 x (String + 24 bytes) starting at offset {0}", Buffer.GetPosition()); + //Buffer.SkipBytes(1338); + for (int i = 0; i < 50; ++i) + { + do + { + b = Buffer.ReadByte(); + } while (b != 0); + + Buffer.SkipBytes(24); + + } + + + + // + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Group autoconsent? {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Raid autoconsent? {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Guild autoconsent? {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadByte()); + + OutputStream.WriteLine("{0, -5}: Level3? {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("{0, -5}: Showhelm? {1}", Buffer.GetPosition(), Buffer.ReadByte()); + + OutputStream.WriteLine("{0, -5}: RestTimer? {1}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("Skipping 1028 bytes starting at offset {0}", Buffer.GetPosition()); + Buffer.SkipBytes(1028); + + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + OutputStream.WriteLine("{0, -5}: Unknown {1:X}", Buffer.GetPosition(), Buffer.ReadUInt32()); + + OutputStream.WriteLine("Pointer is {0} bytes from end.", Buffer.Length() - Buffer.GetPosition()); + + + + } + + public void ExploreZoneEntry(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + if (Direction != PacketDirection.ServerToClient) + return; + + string FirstName = Buffer.ReadString(false); + + OutputStream.WriteLine("Name = {0}", FirstName); + + UInt32 SpawnID = Buffer.ReadUInt32(); + + OutputStream.WriteLine("SpawnID = {0}", SpawnID); + + byte Level = Buffer.ReadByte(); + + OutputStream.WriteLine("Level = {0}", Level); + + Buffer.SkipBytes(4); + + byte IsNPC = Buffer.ReadByte(); + + OutputStream.WriteLine("IsNPC = {0}", IsNPC); + + UInt32 Bitfield = Buffer.ReadUInt32(); + OutputStream.WriteLine("Name: {0}, Bitfield: {1}", FirstName, Convert.ToString(Bitfield, 2)); + + byte OtherData = Buffer.ReadByte(); + + OutputStream.WriteLine("OtherData = {0}", OtherData); + + Buffer.SkipBytes(8); + + if ((OtherData & 1) > 0) + { + OutputStream.WriteLine("OD: {0}", Buffer.ReadString(false)); + OutputStream.WriteLine("OD: {0}", Buffer.ReadString(false)); + OutputStream.WriteLine("OD: {0}", Buffer.ReadString(false)); + Buffer.SkipBytes(53); + } + + if ((OtherData & 4) > 0) + { + OutputStream.WriteLine("Aura: {0}", Buffer.ReadString(false)); + OutputStream.WriteLine("Aura: {0}", Buffer.ReadString(false)); + Buffer.SkipBytes(54); + } + + byte Properties = Buffer.ReadByte(); + OutputStream.WriteLine("Properties = {0}, Offset now {1}", Properties, Buffer.GetPosition()); + + UInt32 BodyType = 0; + + if(Properties > 0) + BodyType = Buffer.ReadUInt32(); + + OutputStream.WriteLine("Bodytype = {0}", BodyType); + + if (Properties != 1) + OutputStream.WriteLine("XXXX Properties is {0}", Properties); + + for (int i = 1; i < Properties; ++i) + OutputStream.WriteLine(" Prop: {0}", Buffer.ReadUInt32()); + + OutputStream.WriteLine("Position is now {0}", Buffer.GetPosition()); + + byte HP = Buffer.ReadByte(); + byte HairColor = Buffer.ReadByte(); + byte BeardColor = Buffer.ReadByte(); + byte Eye1 = Buffer.ReadByte(); + byte Eye2 = Buffer.ReadByte(); + byte HairStyle = Buffer.ReadByte(); + byte BeardStyle = Buffer.ReadByte(); + OutputStream.WriteLine("Beardstyle is {0}", BeardStyle); + + Buffer.SkipBytes(12); // Drakkin stuff + byte EquipChest2 = Buffer.ReadByte(); + Buffer.SkipBytes(2); + byte Helm = Buffer.ReadByte(); + + + float Size = Buffer.ReadSingle(); + + byte Face = Buffer.ReadByte(); + + float WalkSpeed = Buffer.ReadSingle(); + + float RunSpeed = Buffer.ReadSingle(); + + UInt32 Race = Buffer.ReadUInt32(); + + OutputStream.WriteLine("Size: {0}, Face: {1}, Walkspeed: {2}, RunSpeed: {3}, Race: {4}", Size, Face, WalkSpeed, RunSpeed, Race); + + //Buffer.SkipBytes(18); + Buffer.SkipBytes(5); + UInt32 GuildID = Buffer.ReadUInt32(); + UInt32 GuildRank = Buffer.ReadUInt32(); + Buffer.SkipBytes(5); + OutputStream.WriteLine("GuildID: {0}, Guild Rank: {1}", GuildID, GuildRank); + + Buffer.ReadString(false); + + Buffer.SkipBytes(35); + + + if ((IsNPC == 0) || NPCType.IsPlayableRace(Race)) + { + for (int ColourSlot = 0; ColourSlot < 9; ++ColourSlot) + OutputStream.WriteLine("Color {0} is {1}", ColourSlot, Buffer.ReadUInt32()); + + for (int i = 0; i < 9; ++i) + { + UInt32 Equip3 = Buffer.ReadUInt32(); + + UInt32 Equipx = Buffer.ReadUInt32(); + + UInt32 Equip2 = Buffer.ReadUInt32(); + + UInt32 Equip1 = Buffer.ReadUInt32(); + + UInt32 Equip0 = Buffer.ReadUInt32(); + + OutputStream.WriteLine("Equip slot {0}: 0,1,2,x,3 is {1}, {2}, {3}, {4}, {5}", i, + Equip0, Equip1, Equip2, Equipx, Equip3); + } + + + + + + } + else + { + // Non playable race + // Melee Texture 1 is 20 bytes in + // Melee Texture 1 is 40 bytes in + // This whole segment is 28 + 24 + 8 = 60 + // Skip 20, Read m1, skip 16, read m2, skip 16 + /* + OutputStream.WriteLine("None playable race, offset now {0}", Buffer.GetPosition()); + Buffer.SkipBytes(28); + + UInt32 MeleeTexture1 = Buffer.ReadUInt32(); + Buffer.SkipBytes(12); + UInt32 MeleeTexture2 = Buffer.ReadUInt32(); + Buffer.SkipBytes(12); + */ + OutputStream.WriteLine("None playable race, offset now {0}", Buffer.GetPosition()); + Buffer.SkipBytes(20); + + UInt32 MeleeTexture1 = Buffer.ReadUInt32(); + Buffer.SkipBytes(16); + UInt32 MeleeTexture2 = Buffer.ReadUInt32(); + Buffer.SkipBytes(16); + } + + OutputStream.WriteLine("Position starts at offset {0}", Buffer.GetPosition()); + + UInt32 Position1 = Buffer.ReadUInt32(); + + UInt32 Position2 = Buffer.ReadUInt32(); + + UInt32 Position3 = Buffer.ReadUInt32(); + + UInt32 Position4 = Buffer.ReadUInt32(); + + UInt32 Position5 = Buffer.ReadUInt32(); + + float YPos = Utils.EQ19ToFloat((Int32)(Position1 >> 12)); + + float ZPos = Utils.EQ19ToFloat((Int32)(Position3 >> 13) & 0x7FFFF); + + float XPos = Utils.EQ19ToFloat((Int32)(Position4) & 0x7FFFF); + + float Heading = Utils.EQ19ToFloat((Int32)(Position5) & 0x7FFFF); + + OutputStream.WriteLine("(X,Y,Z) = {0}, {1}, {2}, Heading = {3}", XPos, YPos, ZPos, Heading); + + if((OtherData & 16) > 1) + OutputStream.WriteLine("Title: {0}", Buffer.ReadString(false)); + + if ((OtherData & 32) > 1) + OutputStream.WriteLine("Suffix: {0}", Buffer.ReadString(false)); + + Buffer.SkipBytes(8); + + byte IsMerc = Buffer.ReadByte(); + + OutputStream.WriteLine("IsMerc: {0}", IsMerc); + + Buffer.SkipBytes(54); + + OutputStream.WriteLine("Buffer Length: {0}, Current Position: {1}", Buffer.Length(), Buffer.GetPosition()); + + if (Buffer.Length() != Buffer.GetPosition()) + OutputStream.WriteLine("PARSE ERROR"); + + + + + + OutputStream.WriteLine(""); + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchFeb8-2011.cs b/utils/EQExtractor2/EQExtractor2/PatchFeb8-2011.cs new file mode 100644 index 000000000..02c1e4430 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchFeb8-2011.cs @@ -0,0 +1,63 @@ +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchFeb082011Decoder : PatchDec072010Decoder + { + public PatchFeb082011Decoder() + { + Version = "EQ Client Build Date February 8 2011."; + + PatchConfFileName = "patch_Feb8-2011.conf"; + + ExpectedPPLength = 28176; + + PPZoneIDOffset = 20844; + } + + override public IdentificationStatus Identify(int OpCode, int Size, PacketDirection Direction) + { + if ((OpCode == OpManager.OpCodeNameToNumber("OP_ZoneEntry")) && (Direction == PacketDirection.ClientToServer)) + { + IDStatus = IdentificationStatus.Tentative; + return IdentificationStatus.Tentative; + } + + if (IDStatus != IdentificationStatus.Tentative) + return IdentificationStatus.No; + + if ((OpCode == OpManager.OpCodeNameToNumber("OP_PlayerProfile")) && (Direction == PacketDirection.ServerToClient) && + (Size == ExpectedPPLength)) + return IdentificationStatus.Yes; + + return IdentificationStatus.No; + } + + override public Item DecodeItemPacket(byte[] PacketBuffer) + { + ByteStream Buffer = new ByteStream(PacketBuffer); + + Item NewItem = new Item(); + + Buffer.SetPosition(30); + NewItem.MerchantSlot = Buffer.ReadByte(); // 13 + NewItem.Price = Buffer.ReadUInt32(); // 14 + Buffer.SkipBytes(5); + NewItem.Quantity = Buffer.ReadInt32(); // 23 + Buffer.SetPosition(97); + NewItem.Name = Buffer.ReadString(true); + NewItem.Lore = Buffer.ReadString(true); + NewItem.IDFile = Buffer.ReadString(true); + Buffer.SkipBytes(1); + NewItem.ID = Buffer.ReadUInt32(); + + return NewItem; + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchFebruary11-2013.cs b/utils/EQExtractor2/EQExtractor2/PatchFebruary11-2013.cs new file mode 100644 index 000000000..b3213b6ff --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchFebruary11-2013.cs @@ -0,0 +1,23 @@ +using System; +using System.IO; +using System.Diagnostics; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchFebruary112013Decoder : PatchJanuary162013Decoder + { + public PatchFebruary112013Decoder() + { + Version = "EQ Client Build Date February 11 2013."; + + PatchConfFileName = "patch_Feb11-2013.conf"; + + SupportsSQLGeneration = false; + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchGeneric.cs b/utils/EQExtractor2/EQExtractor2/PatchGeneric.cs new file mode 100644 index 000000000..5457c0ba2 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchGeneric.cs @@ -0,0 +1,307 @@ +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + public enum IdentificationStatus { No, Tentative, Yes }; + + class PatchSpecficDecoder + { + protected class PacketToMatch + { + public String OPCodeName; + public PacketDirection Direction; + public Int32 RequiredSize; + public bool VersionMatched; + }; + + + + public PatchSpecficDecoder() + { + Version = "Unsupported Client Version"; + ExpectedPPLength = 0; + PPZoneIDOffset = 0; + PatchConfFileName = ""; + IDStatus = IdentificationStatus.No; + SupportsSQLGeneration = true; + } + + public string GetVersion() + { + return Version; + } + + virtual public bool UnsupportedVersion() + { + return ExpectedPPLength == 0; + } + + virtual public bool Init(string ConfDirectory, ref string ErrorMessage) + { + OpManager = new OpCodeManager(); + + return false; + } + + virtual public IdentificationStatus Identify(int OpCode, int Size, PacketDirection Direction) + { + return IdentificationStatus.No; + } + + virtual public List GetDoors() + { + List DoorList = new List(); + + return DoorList; + } + + virtual public UInt16 GetZoneNumber() + { + return 0; + } + + virtual public int VerifyPlayerProfile() + { + return 0; + } + + virtual public MerchantManager GetMerchantData(NPCSpawnList NPCSL) + { + return null; + } + + virtual public Item DecodeItemPacket(byte[] PacketBuffer) + { + Item NewItem = new Item(); + + return NewItem; + } + + + virtual public List GetZonePointList() + { + List ZonePointList = new List(); + + return ZonePointList; + } + + virtual public NewZoneStruct GetZoneData() + { + NewZoneStruct NewZone = new NewZoneStruct(); + + return NewZone; + } + + virtual public List GetSpawns() + { + List ZoneSpawns = new List(); + + return ZoneSpawns; + } + + virtual public List GetHighResolutionMovementUpdates() + { + List Updates = new List(); + + return Updates; + } + + virtual public List GetLowResolutionMovementUpdates() + { + List Updates = new List(); + + return Updates; + } + + virtual public List GetAllMovementUpdates() + { + List Updates = new List(); + + return Updates; + } + + virtual public PositionUpdate Decode_OP_NPCMoveUpdate(byte[] UpdatePacket) + { + PositionUpdate PosUpdate = new PositionUpdate(); + + return PosUpdate; + } + + virtual public PositionUpdate Decode_OP_MobUpdate(byte[] MobUpdatePacket) + { + PositionUpdate PosUpdate = new PositionUpdate(); + + return PosUpdate; + } + + virtual public List GetClientMovementUpdates() + { + List Updates = new List(); + + return Updates; + } + + virtual public List GetGroundSpawns() + { + List GroundSpawns = new List(); + + return GroundSpawns; + } + + virtual public List GetFindableSpawns() + { + List FindableSpawnList = new List(); + + return FindableSpawnList; + } + + virtual public string GetZoneName() + { + return ""; + } + + virtual public bool DumpAAs(string FileName) + { + return false; + } + + public void GivePackets(PacketManager pm) + { + Packets = pm; + } + + virtual public void RegisterExplorers() + { + } + + public List GetPacketsOfType(string OpCodeName, PacketDirection Direction) + { + List ReturnList = new List(); + + if (OpManager == null) + return ReturnList; + + UInt32 OpCodeNumber = OpManager.OpCodeNameToNumber(OpCodeName); + + foreach (EQApplicationPacket app in Packets.PacketList) + { + if ((app.OpCode == OpCodeNumber) && (app.Direction == Direction) && (app.Locked)) + ReturnList.Add(app.Buffer); + } + + return ReturnList; + } + + public DateTime GetCaptureStartTime() + { + if (Packets.PacketList.Count > 0) + { + return Packets.PacketList[0].PacketTime; + } + return DateTime.MinValue; + } + + public bool DumpPackets(string FileName, bool ShowTimeStamps) + { + + StreamWriter PacketDumpStream; + + try + { + PacketDumpStream = new StreamWriter(FileName); + } + catch + { + return false; + } + + string Direction = ""; + + foreach (EQApplicationPacket p in Packets.PacketList) + { + if(ShowTimeStamps) + PacketDumpStream.WriteLine(p.PacketTime.ToString()); + + if (p.Direction == PacketDirection.ServerToClient) + Direction = "[Server->Client]"; + else + Direction = "[Client->Server]"; + + OpCode oc = OpManager.GetOpCodeByNumber(p.OpCode); + + string OpCodeName = (oc != null) ? oc.Name : "OP_Unknown"; + + PacketDumpStream.WriteLine("[OPCode: 0x" + p.OpCode.ToString("x4") + "] " + OpCodeName + " " + Direction + " [Size: " + p.Buffer.Length + "]"); + PacketDumpStream.WriteLine(Utils.HexDump(p.Buffer)); + + if ((oc != null) && (oc.Explorer != null)) + oc.Explorer(PacketDumpStream, new ByteStream(p.Buffer), p.Direction); + } + + PacketDumpStream.Close(); + + return true; + } + public int PacketTypeCountByName(string OPCodeName) + { + UInt32 OpCodeNumber = OpManager.OpCodeNameToNumber(OPCodeName); + + int Count = 0; + + foreach (EQApplicationPacket app in Packets.PacketList) + { + if (app.OpCode == OpCodeNumber) + ++Count; + } + + + return Count; + } + + protected void AddExplorerSpawn(UInt32 ID, string Name) + { + ExplorerSpawnRecord e = new ExplorerSpawnRecord(ID, Name); + + ExplorerSpawns.Add(e); + } + + protected string FindExplorerSpawn(UInt32 ID) + { + foreach(ExplorerSpawnRecord s in ExplorerSpawns) + { + if (s.ID == ID) + return s.Name; + } + + return ""; + } + + protected PacketManager Packets; + + public OpCodeManager OpManager; + + protected string Version; + + protected int ExpectedPPLength; + + protected int PPZoneIDOffset; + + protected string PatchConfFileName; + + protected PacketToMatch[] PacketsToMatch; + + protected UInt32 WaitingForPacket; + + protected IdentificationStatus IDStatus; + + private List ExplorerSpawns = new List(); + + public bool SupportsSQLGeneration; + } +} diff --git a/utils/EQExtractor2/EQExtractor2/PatchJanuary16-2013.cs b/utils/EQExtractor2/EQExtractor2/PatchJanuary16-2013.cs new file mode 100644 index 000000000..f43df3233 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchJanuary16-2013.cs @@ -0,0 +1,467 @@ +using System; +using System.IO; +using System.Diagnostics; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchJanuary162013Decoder : PatchDecember102012Decoder + { + public PatchJanuary162013Decoder() + { + Version = "EQ Client Build Date January 16 2013."; + + PatchConfFileName = "patch_Jan16-2013.conf"; + } + + override public List GetSpawns() + { + List ZoneSpawns = new List(); + + List SpawnPackets = GetPacketsOfType("OP_ZoneEntry", PacketDirection.ServerToClient); + + foreach (byte[] SpawnPacket in SpawnPackets) + { + ZoneEntryStruct NewSpawn = new ZoneEntryStruct(); + + ByteStream Buffer = new ByteStream(SpawnPacket); + + NewSpawn.SpawnName = Buffer.ReadString(true); + + NewSpawn.SpawnName = Utils.MakeCleanName(NewSpawn.SpawnName); + + NewSpawn.SpawnID = Buffer.ReadUInt32(); + + NewSpawn.Level = Buffer.ReadByte(); + + float UnkSize = Buffer.ReadSingle(); + + NewSpawn.IsNPC = Buffer.ReadByte(); + + UInt32 Bitfield = Buffer.ReadUInt32(); + + NewSpawn.Gender = (Bitfield & 3); + + Byte OtherData = Buffer.ReadByte(); + + Buffer.SkipBytes(8); // Skip 8 unknown bytes + + NewSpawn.DestructableString1 = ""; + NewSpawn.DestructableString2 = ""; + NewSpawn.DestructableString3 = ""; + + if ((NewSpawn.IsNPC > 0) && ((OtherData & 1) > 0)) + { + // Destructable Objects + NewSpawn.DestructableString1 = Buffer.ReadString(false); + NewSpawn.DestructableString2 = Buffer.ReadString(false); + NewSpawn.DestructableString3 = Buffer.ReadString(false); + Buffer.SkipBytes(53); + } + + if ((OtherData & 4) > 0) + { + // Auras + Buffer.ReadString(false); + Buffer.ReadString(false); + Buffer.SkipBytes(54); + } + + NewSpawn.PropCount = Buffer.ReadByte(); + + if (NewSpawn.PropCount > 0) + NewSpawn.BodyType = Buffer.ReadUInt32(); + else + NewSpawn.BodyType = 0; + + + for (int j = 1; j < NewSpawn.PropCount; ++j) + Buffer.SkipBytes(4); + + Buffer.SkipBytes(1); // Skip HP % + NewSpawn.HairColor = Buffer.ReadByte(); + NewSpawn.BeardColor = Buffer.ReadByte(); + NewSpawn.EyeColor1 = Buffer.ReadByte(); + NewSpawn.EyeColor2 = Buffer.ReadByte(); + NewSpawn.HairStyle = Buffer.ReadByte(); + NewSpawn.Beard = Buffer.ReadByte(); + + NewSpawn.DrakkinHeritage = Buffer.ReadUInt32(); + NewSpawn.DrakkinTattoo = Buffer.ReadUInt32(); + NewSpawn.DrakkinDetails = Buffer.ReadUInt32(); + + NewSpawn.EquipChest2 = Buffer.ReadByte(); + + bool UseWorn = (NewSpawn.EquipChest2 == 255); + + Buffer.SkipBytes(2); // 2 Unknown bytes; + + NewSpawn.Helm = Buffer.ReadByte(); + + NewSpawn.Size = Buffer.ReadSingle(); + + NewSpawn.Face = Buffer.ReadByte(); + + NewSpawn.WalkSpeed = Buffer.ReadSingle(); + + NewSpawn.RunSpeed = Buffer.ReadSingle(); + + NewSpawn.Race = Buffer.ReadUInt32(); + + Buffer.SkipBytes(1); // Skip Holding + + NewSpawn.Deity = Buffer.ReadUInt32(); + + Buffer.SkipBytes(8); // Skip GuildID and GuildRank + + NewSpawn.Class = Buffer.ReadByte(); + + Buffer.SkipBytes(4); // Skip PVP, Standstate, Light, Flymode + + NewSpawn.LastName = Buffer.ReadString(true); + + Buffer.SkipBytes(6); + + NewSpawn.PetOwnerID = Buffer.ReadUInt32(); + + Buffer.SkipBytes(25); + + NewSpawn.MeleeTexture1 = 0; + NewSpawn.MeleeTexture2 = 0; + + if ((NewSpawn.IsNPC == 0) || NPCType.IsPlayableRace(NewSpawn.Race)) + { + for (int ColourSlot = 0; ColourSlot < 9; ++ColourSlot) + NewSpawn.SlotColour[ColourSlot] = Buffer.ReadUInt32(); + + for (int i = 0; i < 9; ++i) + { + NewSpawn.Equipment[i] = Buffer.ReadUInt32(); + + UInt32 Equip3 = Buffer.ReadUInt32(); + + UInt32 Equip2 = Buffer.ReadUInt32(); + + UInt32 Equip1 = Buffer.ReadUInt32(); + + UInt32 Equip0 = Buffer.ReadUInt32(); + } + + if (NewSpawn.Equipment[Constants.MATERIAL_CHEST] > 0) + { + NewSpawn.EquipChest2 = (byte)NewSpawn.Equipment[Constants.MATERIAL_CHEST]; + + } + + NewSpawn.ArmorTintRed = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 16) & 0xff); + + NewSpawn.ArmorTintGreen = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 8) & 0xff); + + NewSpawn.ArmorTintBlue = (byte)(NewSpawn.SlotColour[Constants.MATERIAL_CHEST] & 0xff); + + if (NewSpawn.Equipment[Constants.MATERIAL_PRIMARY] > 0) + NewSpawn.MeleeTexture1 = NewSpawn.Equipment[Constants.MATERIAL_PRIMARY]; + + if (NewSpawn.Equipment[Constants.MATERIAL_SECONDARY] > 0) + NewSpawn.MeleeTexture2 = NewSpawn.Equipment[Constants.MATERIAL_SECONDARY]; + + if (UseWorn) + NewSpawn.Helm = (byte)NewSpawn.Equipment[Constants.MATERIAL_HEAD]; + else + NewSpawn.Helm = 0; + + } + else + { + // Non playable race + + Buffer.SkipBytes(20); + + NewSpawn.MeleeTexture1 = Buffer.ReadUInt32(); + Buffer.SkipBytes(16); + NewSpawn.MeleeTexture2 = Buffer.ReadUInt32(); + Buffer.SkipBytes(16); + } + + if (NewSpawn.EquipChest2 == 255) + NewSpawn.EquipChest2 = 0; + + if (NewSpawn.Helm == 255) + NewSpawn.Helm = 0; + + UInt32 Position1 = Buffer.ReadUInt32(); + + UInt32 Position2 = Buffer.ReadUInt32(); + + UInt32 Position3 = Buffer.ReadUInt32(); + + UInt32 Position4 = Buffer.ReadUInt32(); + + UInt32 Position5 = Buffer.ReadUInt32(); + + NewSpawn.YPos = Utils.EQ19ToFloat((Int32)((Position4 >> 13) & 0x7FFFF)); + + NewSpawn.ZPos = Utils.EQ19ToFloat((Int32)(Position5 >> 10) & 0x7FFFF); + + NewSpawn.XPos = Utils.EQ19ToFloat((Int32)(Position2 >> 10) & 0x7FFFF); + + NewSpawn.Heading = Utils.EQ19ToFloat((Int32)(Position3) & 0xFFF); + + if ((OtherData & 16) > 0) + { + NewSpawn.Title = Buffer.ReadString(false); + } + + if ((OtherData & 32) > 0) + { + NewSpawn.Suffix = Buffer.ReadString(false); + } + + // unknowns + Buffer.SkipBytes(8); + + NewSpawn.IsMercenary = Buffer.ReadByte(); + + Buffer.SkipBytes(54); + + Debug.Assert(Buffer.GetPosition() == Buffer.Length(), "Length mismatch while parsing zone spawns"); + + ZoneSpawns.Add(NewSpawn); + } + + return ZoneSpawns; + } + + public override void RegisterExplorers() + { + //OpManager.RegisterExplorer("OP_ZoneEntry", ExploreZoneEntry); + //OpManager.RegisterExplorer("OP_NPCMoveUpdate", ExploreNPCMoveUpdate); + //OpManager.RegisterExplorer("OP_MobUpdate", ExploreMobUpdate); + } + + public void ExploreNPCMoveUpdate(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + PositionUpdate PosUpdate; + + PosUpdate = Decode_OP_NPCMoveUpdate(Buffer.Buffer); + + OutputStream.WriteLine("SpawnID: {0}, X = {1}, Y = {2}, Z = {3}, Heading = {4}", PosUpdate.SpawnID, PosUpdate.p.x, PosUpdate.p.y, PosUpdate.p.z, PosUpdate.p.heading); + } + + public void ExploreMobUpdate(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + PositionUpdate PosUpdate; + + PosUpdate = Decode_OP_MobUpdate(Buffer.Buffer); + + OutputStream.WriteLine("SpawnID: {0}, X = {1}, Y = {2}, Z = {3}, Heading = {4}", PosUpdate.SpawnID, PosUpdate.p.x, PosUpdate.p.y, PosUpdate.p.z, PosUpdate.p.heading); + } + + public void ExploreZoneEntry(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + if (Direction != PacketDirection.ServerToClient) + return; + + string FirstName = Buffer.ReadString(false); + + OutputStream.WriteLine("Name = {0}", FirstName); + + UInt32 SpawnID = Buffer.ReadUInt32(); + + OutputStream.WriteLine("SpawnID = {0}", SpawnID); + + byte Level = Buffer.ReadByte(); + + OutputStream.WriteLine("Level = {0}", Level); + + Buffer.SkipBytes(4); + + byte IsNPC = Buffer.ReadByte(); + + OutputStream.WriteLine("IsNPC = {0}", IsNPC); + + UInt32 Bitfield = Buffer.ReadUInt32(); + OutputStream.WriteLine("Name: {0}, Bitfield: {1}", FirstName, Convert.ToString(Bitfield, 2)); + + byte OtherData = Buffer.ReadByte(); + + OutputStream.WriteLine("OtherData = {0}", OtherData); + + Buffer.SkipBytes(8); + + if ((OtherData & 1) > 0) + { + OutputStream.WriteLine("OD: {0}", Buffer.ReadString(false)); + OutputStream.WriteLine("OD: {0}", Buffer.ReadString(false)); + OutputStream.WriteLine("OD: {0}", Buffer.ReadString(false)); + Buffer.SkipBytes(53); + } + + if ((OtherData & 4) > 0) + { + OutputStream.WriteLine("Aura: {0}", Buffer.ReadString(false)); + OutputStream.WriteLine("Aura: {0}", Buffer.ReadString(false)); + Buffer.SkipBytes(54); + } + + byte Properties = Buffer.ReadByte(); + OutputStream.WriteLine("Properties = {0}, Offset now {1}", Properties, Buffer.GetPosition()); + + UInt32 BodyType = 0; + + if (Properties > 0) + BodyType = Buffer.ReadUInt32(); + + OutputStream.WriteLine("Bodytype = {0}", BodyType); + + if (Properties != 1) + OutputStream.WriteLine("XXXX Properties is {0}", Properties); + + for (int i = 1; i < Properties; ++i) + OutputStream.WriteLine(" Prop: {0}", Buffer.ReadUInt32()); + + OutputStream.WriteLine("Position is now {0}", Buffer.GetPosition()); + + byte HP = Buffer.ReadByte(); + byte HairColor = Buffer.ReadByte(); + byte BeardColor = Buffer.ReadByte(); + byte Eye1 = Buffer.ReadByte(); + byte Eye2 = Buffer.ReadByte(); + byte HairStyle = Buffer.ReadByte(); + byte BeardStyle = Buffer.ReadByte(); + OutputStream.WriteLine("Beardstyle is {0}", BeardStyle); + + Buffer.SkipBytes(12); // Drakkin stuff + byte EquipChest2 = Buffer.ReadByte(); + Buffer.SkipBytes(2); + byte Helm = Buffer.ReadByte(); + + + float Size = Buffer.ReadSingle(); + + byte Face = Buffer.ReadByte(); + + float WalkSpeed = Buffer.ReadSingle(); + + float RunSpeed = Buffer.ReadSingle(); + + UInt32 Race = Buffer.ReadUInt32(); + + OutputStream.WriteLine("Size: {0}, Face: {1}, Walkspeed: {2}, RunSpeed: {3}, Race: {4}", Size, Face, WalkSpeed, RunSpeed, Race); + + Buffer.SkipBytes(18); + + Buffer.ReadString(false); + + Buffer.SkipBytes(35); + + + if ((IsNPC == 0) || NPCType.IsPlayableRace(Race)) + { + for (int ColourSlot = 0; ColourSlot < 9; ++ColourSlot) + OutputStream.WriteLine("Color {0} is {1}", ColourSlot, Buffer.ReadUInt32()); + + for (int i = 0; i < 9; ++i) + { + UInt32 Equip3 = Buffer.ReadUInt32(); + + UInt32 Equipx = Buffer.ReadUInt32(); + + UInt32 Equip2 = Buffer.ReadUInt32(); + + UInt32 Equip1 = Buffer.ReadUInt32(); + + UInt32 Equip0 = Buffer.ReadUInt32(); + + OutputStream.WriteLine("Equip slot {0}: 0,1,2,x,3 is {1}, {2}, {3}, {4}, {5}", i, + Equip0, Equip1, Equip2, Equipx, Equip3); + } + + + + + + } + else + { + // Non playable race + // Melee Texture 1 is 20 bytes in + // Melee Texture 1 is 40 bytes in + // This whole segment is 28 + 24 + 8 = 60 + // Skip 20, Read m1, skip 16, read m2, skip 16 + /* + OutputStream.WriteLine("None playable race, offset now {0}", Buffer.GetPosition()); + Buffer.SkipBytes(28); + + UInt32 MeleeTexture1 = Buffer.ReadUInt32(); + Buffer.SkipBytes(12); + UInt32 MeleeTexture2 = Buffer.ReadUInt32(); + Buffer.SkipBytes(12); + */ + OutputStream.WriteLine("None playable race, offset now {0}", Buffer.GetPosition()); + Buffer.SkipBytes(20); + + UInt32 MeleeTexture1 = Buffer.ReadUInt32(); + Buffer.SkipBytes(16); + UInt32 MeleeTexture2 = Buffer.ReadUInt32(); + Buffer.SkipBytes(16); + } + + OutputStream.WriteLine("Position starts at offset {0}", Buffer.GetPosition()); + + UInt32 Position1 = Buffer.ReadUInt32(); + + UInt32 Position2 = Buffer.ReadUInt32(); + + UInt32 Position3 = Buffer.ReadUInt32(); + + UInt32 Position4 = Buffer.ReadUInt32(); + + UInt32 Position5 = Buffer.ReadUInt32(); + + float YPos = Utils.EQ19ToFloat((Int32)((Position4 >> 13) & 0x7FFFF)); + + float ZPos = Utils.EQ19ToFloat((Int32)(Position5 >> 10) & 0x7FFFF); + + float XPos = Utils.EQ19ToFloat((Int32)(Position2 >> 10) & 0x7FFFF); + + //float Heading = Utils.EQ19ToFloat((Int32)(Position1 >> 13) & 0x3FF); + //float Heading = Utils.EQ19ToFloat((Int32)(Position2) & 0x3FF); + float Heading = Utils.EQ19ToFloat((Int32)(Position3) & 0x3FF); + + //for(int i = 0; i < 32; ++i) + // OutputStream.WriteLine("Pos3 << {0} = {1}", i, Utils.EQ19ToFloat((Int32)(Position3 >> i) & 0x3FF)); + + OutputStream.WriteLine("(X,Y,Z) = {0}, {1}, {2}, Heading = {3}", XPos, YPos, ZPos, Heading); + + if ((OtherData & 16) > 1) + OutputStream.WriteLine("Title: {0}", Buffer.ReadString(false)); + + if ((OtherData & 32) > 1) + OutputStream.WriteLine("Suffix: {0}", Buffer.ReadString(false)); + + Buffer.SkipBytes(8); + + byte IsMerc = Buffer.ReadByte(); + + OutputStream.WriteLine("IsMerc: {0}", IsMerc); + + Buffer.SkipBytes(54); + + OutputStream.WriteLine("Buffer Length: {0}, Current Position: {1}", Buffer.Length(), Buffer.GetPosition()); + + if (Buffer.Length() != Buffer.GetPosition()) + OutputStream.WriteLine("PARSE ERROR"); + + + + + + OutputStream.WriteLine(""); + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchJuly13-2010.cs b/utils/EQExtractor2/EQExtractor2/PatchJuly13-2010.cs new file mode 100644 index 000000000..dcc8eafe6 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchJuly13-2010.cs @@ -0,0 +1,143 @@ +// +// Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net). Distributed under GPL version 2. +// +// +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchJuly132010Decoder : PatchMay122010Decoder + { + public PatchJuly132010Decoder() + { + Version = "EQ Client Build Date July 13 2010. (Including Test Server June 24 to July 8 2010)."; + + ExpectedPPLength = 26640; + } + + public override void RegisterExplorers() + { + base.RegisterExplorers(); + + //OpManager.RegisterExplorer("OP_ZoneEntry", ExploreZoneEntry); + //OpManager.RegisterExplorer("OP_CastSpell", ExploreCastSpell); + //OpManager.RegisterExplorer("OP_SpawnAppearance", ExploreSpawnAppearance); + //OpManager.RegisterExplorer("OP_ItemPacket", ExploreItemPacket); + } + + public void ExploreItemPacket(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 StackSize = Buffer.ReadUInt32(); + Buffer.SkipBytes(4); + UInt32 Slot = Buffer.ReadUInt32(); + UInt32 MerchantSlot = Buffer.ReadUInt32(); + UInt32 Price = Buffer.ReadUInt32(); + Int32 Quantity = Buffer.ReadInt32(); + Buffer.SetPosition(68); + string Name = Buffer.ReadString(true); + + OutputStream.WriteLine("Item: {0} at Slot: {1}", Name, Slot); + + } + + + public void ExploreCastSpell(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 Slot = Buffer.ReadUInt32(); + UInt32 SpellID = Buffer.ReadUInt32(); + + OutputStream.WriteLine("Casting spell {0} from slot {1}", SpellID, Slot); + + OutputStream.WriteLine(""); + } + public void ExploreMercenaryDataResponse(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 TypeCount = Buffer.ReadUInt32(); + + //OutputStream.WriteLine("Type Count: {0}\r\n", TypeCount); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Number of Types (Journeyman and Apprentice in this case\r\n", TypeCount); + for (int i = 0; i < TypeCount; ++i) + { + UInt32 TypeDBStringID = Buffer.ReadUInt32(); + //OutputStream.WriteLine(" Type {0} DBStringID {1}", i, TypeDBStringID); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // DBStringID for Type {1}", TypeDBStringID, i); + } + + UInt32 Count2 = Buffer.ReadUInt32(); + + //OutputStream.WriteLine(" Count 2 is {0}", Count2); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Count of Sub-types that follow", Count2); + + for (int i = 0; i < Count2; ++i) + { + int Offset = Buffer.GetPosition(); + + UInt32 Unknown1 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown1); + UInt32 DBStringID1 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // DBStringID of Type", DBStringID1); + UInt32 DBStringID2 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // DBStringID of Sub-Type", DBStringID2); + UInt32 PurchaseCost = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Purchase Cost", PurchaseCost); + UInt32 UpkeepCost = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Upkeep Cost", UpkeepCost); + UInt32 Unknown2 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown2); + UInt32 Unknown3 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown3); + UInt32 Unknown4 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown4); + + byte Unknown5 = Buffer.ReadByte(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint8, Buffer, {0}); // Unknown", Unknown5); + + UInt32 Unknown6 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown6); + UInt32 Unknown7 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown7); + UInt32 Unknown8 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown8); + + UInt32 StanceCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Number of Stances for this Merc", StanceCount); + + UInt32 Unknown10 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown10); + + byte Unknown11 = Buffer.ReadByte(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint8, Buffer, {0}); // Unknown", Unknown11); + + + //OutputStream.WriteLine(" Offset: {5} Unknown1: {0} DBStrings: {1} {2} Purchase: {3} Upkeep: {4}\r\n", Unknown1, DBStringID1, DBStringID2, + // PurchaseCost, UpkeepCost, Offset); + //OutputStream.WriteLine(" Unknowns: {0} {1} {2} {3} {4} {5} {6} {7} {8}\r\n", + // Unknown2, Unknown3, Unknown4, Unknown5, Unknown6, Unknown7, Unknown8, Unknown10, Unknown11); + + //OutputStream.WriteLine(" Stance Count: {0}", StanceCount); + + for (int j = 0; j < StanceCount; ++j) + { + UInt32 StanceNum = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Stance Number", StanceNum); + UInt32 StanceType = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Stance DBStringID (1 = Passive, 2 = Balanced etc.", StanceType); + + //OutputStream.WriteLine(" {0}: {1}", StanceNum, StanceType); + } + OutputStream.WriteLine(""); + } + + OutputStream.WriteLine("\r\nBuffer position at end is {0}", Buffer.GetPosition()); + OutputStream.WriteLine(""); + } + + } +} diff --git a/utils/EQExtractor2/EQExtractor2/PatchJuly13-2012.cs b/utils/EQExtractor2/EQExtractor2/PatchJuly13-2012.cs new file mode 100644 index 000000000..bd037eca7 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchJuly13-2012.cs @@ -0,0 +1,24 @@ +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchJuly132012Decoder : PatchJune252012Decoder + { + public PatchJuly132012Decoder() + { + Version = "EQ Client Build Date July 13 2012."; + + ExpectedPPLength = 33784; + + PPZoneIDOffset = 26452; + + PatchConfFileName = "patch_July13-2012.conf"; + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchJune25-2012.cs b/utils/EQExtractor2/EQExtractor2/PatchJune25-2012.cs new file mode 100644 index 000000000..98f5b0e75 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchJune25-2012.cs @@ -0,0 +1,20 @@ +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchJune252012Decoder : PatchMar152012Decoder + { + public PatchJune252012Decoder() + { + Version = "EQ Client Build Date June 25 2012."; + + PatchConfFileName = "patch_June25-2012.conf"; + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchMar15-2012.cs b/utils/EQExtractor2/EQExtractor2/PatchMar15-2012.cs new file mode 100644 index 000000000..00c65a5b7 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchMar15-2012.cs @@ -0,0 +1,458 @@ +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchMar152012Decoder : PatchNov172011Decoder + { + public PatchMar152012Decoder() + { + Version = "EQ Client Build Date March 15 2012."; + + ExpectedPPLength = 29688; + + PPZoneIDOffset = 22356; + + PatchConfFileName = "patch_Mar15-2012.conf"; + } + + public override void RegisterExplorers() + { + //OpManager.RegisterExplorer("OP_ZoneEntry", ExploreZoneEntry); + } + + public void ExploreZoneEntry(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + if (Direction != PacketDirection.ServerToClient) + return; + + string SpawnName = Buffer.ReadString(true); + + UInt32 SpawnID = Buffer.ReadUInt32(); + + byte Level = Buffer.ReadByte(); + + float UnkSize = Buffer.ReadSingle(); + + byte IsNPC = Buffer.ReadByte(); + + UInt32 Bitfield = Buffer.ReadUInt32(); + /* + NewSpawn.Showname = (Bitfield >> 28) & 1; + NewSpawn.TargetableWithHotkey = (Bitfield >> 27) & 1; + NewSpawn.Targetable = (Bitfield >> 26) & 1; + + NewSpawn.ShowHelm = (Bitfield >> 24) & 1; + NewSpawn.Gender = (Bitfield >> 20) & 3; + + NewSpawn.Padding5 = (Bitfield >> 4) & 1; + NewSpawn.Padding7 = (Bitfield >> 6) & 2047; + NewSpawn.Padding26 = (Bitfield >> 25) & 1; + */ + Byte OtherData = Buffer.ReadByte(); + + Buffer.SkipBytes(8); // Skip 8 unknown bytes + + //NewSpawn.DestructableString1 = ""; + //NewSpawn.DestructableString2 = ""; + //NewSpawn.DestructableString3 = ""; + + if ((IsNPC > 0) && ((OtherData & 3) > 0)) + { + // Destructable Objects. Not handled yet + // + //SQLOut(String.Format("-- OBJECT FOUND SpawnID {0}", SpawnID.ToString("x"))); + + Buffer.ReadString(false); + + Buffer.ReadString(false); + + Buffer.ReadString(false); + + Buffer.ReadUInt32(); + + Buffer.ReadUInt32(); + + Buffer.ReadUInt32(); + + Buffer.ReadUInt32(); + + Buffer.ReadUInt32(); + + Buffer.ReadUInt32(); + + Buffer.ReadUInt32(); + + Buffer.ReadUInt32(); + + Buffer.ReadUInt32(); + + Buffer.ReadUInt32(); + + Buffer.ReadUInt32(); + + Buffer.ReadUInt32(); + + Buffer.ReadUInt32(); + + Buffer.ReadByte(); + + + } + OutputStream.WriteLine("Size starts at offset {0}", Buffer.GetPosition()); + + float Size = Buffer.ReadSingle(); + + byte Face = Buffer.ReadByte(); + + float WalkSpeed = Buffer.ReadSingle(); + + float RunSpeed = Buffer.ReadSingle(); + + UInt32 Race = Buffer.ReadUInt32(); + + byte PropCount = Buffer.ReadByte(); + + UInt32 BodyType = 0; + + if (PropCount >= 1) + { + BodyType = Buffer.ReadUInt32(); + + for (int j = 1; j < PropCount; ++j) + Buffer.SkipBytes(4); + } + + Buffer.SkipBytes(1); // Skip HP % + + byte HairColor = Buffer.ReadByte(); + byte BeardColor = Buffer.ReadByte(); + byte EyeColor1 = Buffer.ReadByte(); + byte EyeColor2 = Buffer.ReadByte(); + byte HairStyle = Buffer.ReadByte(); + byte Beard = Buffer.ReadByte(); + + UInt32 DrakkinHeritage = Buffer.ReadUInt32(); + + UInt32 DrakkinTattoo = Buffer.ReadUInt32(); + + UInt32 DrakkinDetails = Buffer.ReadUInt32(); + + Buffer.SkipBytes(1); // Skip Holding + + UInt32 Deity = Buffer.ReadUInt32(); + + Buffer.SkipBytes(8); // Skip GuildID and GuildRank + + byte Class = Buffer.ReadByte(); + + Buffer.SkipBytes(4); // Skip PVP, Standstate, Light, Flymode + + byte EquipChest2 = Buffer.ReadByte(); + + bool UseWorn = (EquipChest2 == 255); + + Buffer.SkipBytes(2); // 2 Unknown bytes; + + byte Helm = Buffer.ReadByte(); + + string LastName = Buffer.ReadString(true); + + Buffer.SkipBytes(5); // AATitle + unknown byte + + UInt32 PetOwnerID = Buffer.ReadUInt32(); + + Buffer.SkipBytes(26); // Unknown byte + 6 unknown uint32 + + OutputStream.WriteLine("Position starts at offset {0}", Buffer.GetPosition()); + + UInt32 Position1 = Buffer.ReadUInt32(); + + UInt32 Position2 = Buffer.ReadUInt32(); + + UInt32 Position3 = Buffer.ReadUInt32(); + + UInt32 Position4 = Buffer.ReadUInt32(); + + UInt32 Position5 = Buffer.ReadUInt32(); + + float YPos = Utils.EQ19ToFloat((Int32)(Position3 & 0x7FFFF)); + + float Heading = Utils.EQ19ToFloat((Int32)(Position4 & 0xFFF)); + + float XPos = Utils.EQ19ToFloat((Int32)(Position4 >> 12) & 0x7FFFF); + + float ZPos = Utils.EQ19ToFloat((Int32)(Position5 & 0x7FFFF)); + + OutputStream.WriteLine("(X,Y,Z) = {0}, {1}, {2}, Heading = {3}", XPos, YPos, ZPos, Heading); + + if (NPCType.IsPlayableRace(Race)) + { + for (int ColourSlot = 0; ColourSlot < 9; ++ColourSlot) + OutputStream.WriteLine("Color {0} is {1}", ColourSlot, Buffer.ReadUInt32()); + } + + OutputStream.WriteLine(""); + + } + + override public List GetSpawns() + { + List ZoneSpawns = new List(); + + List SpawnPackets = GetPacketsOfType("OP_ZoneEntry", PacketDirection.ServerToClient); + + foreach (byte[] SpawnPacket in SpawnPackets) + { + ZoneEntryStruct NewSpawn = new ZoneEntryStruct(); + + ByteStream Buffer = new ByteStream(SpawnPacket); + + NewSpawn.SpawnName = Buffer.ReadString(true); + + NewSpawn.SpawnName = Utils.MakeCleanName(NewSpawn.SpawnName); + + NewSpawn.SpawnID = Buffer.ReadUInt32(); + + NewSpawn.Level = Buffer.ReadByte(); + + float UnkSize = Buffer.ReadSingle(); + + NewSpawn.IsNPC = Buffer.ReadByte(); + + UInt32 Bitfield = Buffer.ReadUInt32(); + + NewSpawn.Showname = (Bitfield >> 28) & 1; + NewSpawn.TargetableWithHotkey = (Bitfield >> 27) & 1; + NewSpawn.Targetable = (Bitfield >> 26) & 1; + + NewSpawn.ShowHelm = (Bitfield >> 24) & 1; + NewSpawn.Gender = (Bitfield >> 20) & 3; + + NewSpawn.Padding5 = (Bitfield >> 4) & 1; + NewSpawn.Padding7 = (Bitfield >> 6) & 2047; + NewSpawn.Padding26 = (Bitfield >> 25) & 1; + + Byte OtherData = Buffer.ReadByte(); + + Buffer.SkipBytes(8); // Skip 8 unknown bytes + + NewSpawn.DestructableString1 = ""; + NewSpawn.DestructableString2 = ""; + NewSpawn.DestructableString3 = ""; + + if ((NewSpawn.IsNPC > 0) && ((OtherData & 3) > 0)) + { + // Destructable Objects. Not handled yet + // + //SQLOut(String.Format("-- OBJECT FOUND SpawnID {0}", SpawnID.ToString("x"))); + + NewSpawn.DestructableString1 = Buffer.ReadString(false); + + NewSpawn.DestructableString2 = Buffer.ReadString(false); + + NewSpawn.DestructableString3 = Buffer.ReadString(false); + + NewSpawn.DestructableUnk1 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk2 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID1 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID2 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID3 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID4 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk3 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk4 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk5 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk6 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk7 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk8 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk9 = Buffer.ReadUInt32(); + + NewSpawn.DestructableByte = Buffer.ReadByte(); + + //SQLOut(String.Format("-- DES: {0,8:x} {1,8:x} {2,8:d} {3,8:d} {4,8:d} {5,8:d} {6,8:x} {7,8:x} {8,8:x} {9,8:x} {10,8:x} {11,8:x} {12,8:x} {13,2:x} {14} {15} {16}", + // DestructableUnk1, DestructableUnk2, DestructableID1, DestructableID2, DestructableID3, DestructableID4, + // DestructableUnk3, DestructableUnk4, DestructableUnk5, DestructableUnk6, DestructableUnk7, DestructableUnk8, + // DestructableUnk9, DestructableByte, DestructableString1, DestructableString2, DestructableString3)); + } + + NewSpawn.Size = Buffer.ReadSingle(); + + NewSpawn.Face = Buffer.ReadByte(); + + NewSpawn.WalkSpeed = Buffer.ReadSingle(); + + NewSpawn.RunSpeed = Buffer.ReadSingle(); + + NewSpawn.Race = Buffer.ReadUInt32(); + + NewSpawn.PropCount = Buffer.ReadByte(); + + NewSpawn.BodyType = 0; + + if (NewSpawn.PropCount >= 1) + { + NewSpawn.BodyType = Buffer.ReadUInt32(); + + for (int j = 1; j < NewSpawn.PropCount; ++j) + Buffer.SkipBytes(4); + } + + Buffer.SkipBytes(1); // Skip HP % + + NewSpawn.HairColor = Buffer.ReadByte(); + NewSpawn.BeardColor = Buffer.ReadByte(); + NewSpawn.EyeColor1 = Buffer.ReadByte(); + NewSpawn.EyeColor2 = Buffer.ReadByte(); + NewSpawn.HairStyle = Buffer.ReadByte(); + NewSpawn.Beard = Buffer.ReadByte(); + + NewSpawn.DrakkinHeritage = Buffer.ReadUInt32(); + + NewSpawn.DrakkinTattoo = Buffer.ReadUInt32(); + + NewSpawn.DrakkinDetails = Buffer.ReadUInt32(); + + Buffer.SkipBytes(1); // Skip Holding + + NewSpawn.Deity = Buffer.ReadUInt32(); + + Buffer.SkipBytes(8); // Skip GuildID and GuildRank + + NewSpawn.Class = Buffer.ReadByte(); + + Buffer.SkipBytes(4); // Skip PVP, Standstate, Light, Flymode + + NewSpawn.EquipChest2 = Buffer.ReadByte(); + + bool UseWorn = (NewSpawn.EquipChest2 == 255); + + Buffer.SkipBytes(2); // 2 Unknown bytes; + + NewSpawn.Helm = Buffer.ReadByte(); + + NewSpawn.LastName = Buffer.ReadString(true); + + Buffer.SkipBytes(5); // AATitle + unknown byte + + NewSpawn.PetOwnerID = Buffer.ReadUInt32(); + + Buffer.SkipBytes(26); // Unknown byte + 6 unknown uint32 + + UInt32 Position1 = Buffer.ReadUInt32(); + + UInt32 Position2 = Buffer.ReadUInt32(); + + UInt32 Position3 = Buffer.ReadUInt32(); + + UInt32 Position4 = Buffer.ReadUInt32(); + + UInt32 Position5 = Buffer.ReadUInt32(); + + NewSpawn.YPos = Utils.EQ19ToFloat((Int32)(Position3 & 0x7FFFF)); + + NewSpawn.Heading = Utils.EQ19ToFloat((Int32)(Position4 & 0xFFF)); + + NewSpawn.XPos = Utils.EQ19ToFloat((Int32)(Position4 >> 12) & 0x7FFFF); + + NewSpawn.ZPos = Utils.EQ19ToFloat((Int32)(Position5 & 0x7FFFF)); + + NewSpawn.MeleeTexture1 = 0; + NewSpawn.MeleeTexture2 = 0; + + if (NPCType.IsPlayableRace(NewSpawn.Race)) + { + for (int ColourSlot = 0; ColourSlot < 9; ++ColourSlot) + NewSpawn.SlotColour[ColourSlot] = Buffer.ReadUInt32(); + + for (int i = 0; i < 9; ++i) + { + NewSpawn.Equipment[i] = Buffer.ReadUInt32(); + + UInt32 Equip2 = Buffer.ReadUInt32(); + + UInt32 Equip1 = Buffer.ReadUInt32(); + + UInt32 Equip0 = Buffer.ReadUInt32(); + } + + if (NewSpawn.Equipment[Constants.MATERIAL_CHEST] > 0) + { + NewSpawn.EquipChest2 = (byte)NewSpawn.Equipment[Constants.MATERIAL_CHEST]; + + } + + NewSpawn.ArmorTintRed = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 16) & 0xff); + + NewSpawn.ArmorTintGreen = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 8) & 0xff); + + NewSpawn.ArmorTintBlue = (byte)(NewSpawn.SlotColour[Constants.MATERIAL_CHEST] & 0xff); + + if (NewSpawn.Equipment[Constants.MATERIAL_PRIMARY] > 0) + NewSpawn.MeleeTexture1 = NewSpawn.Equipment[Constants.MATERIAL_PRIMARY]; + + if (NewSpawn.Equipment[Constants.MATERIAL_SECONDARY] > 0) + NewSpawn.MeleeTexture2 = NewSpawn.Equipment[Constants.MATERIAL_SECONDARY]; + + if (UseWorn) + NewSpawn.Helm = (byte)NewSpawn.Equipment[Constants.MATERIAL_HEAD]; + else + NewSpawn.Helm = 0; + + } + else + { + // Non playable race + + Buffer.SkipBytes(16); + + NewSpawn.MeleeTexture1 = Buffer.ReadUInt32(); + Buffer.SkipBytes(12); + NewSpawn.MeleeTexture2 = Buffer.ReadUInt32(); + Buffer.SkipBytes(12); + } + + if (NewSpawn.EquipChest2 == 255) + NewSpawn.EquipChest2 = 0; + + if (NewSpawn.Helm == 255) + NewSpawn.Helm = 0; + + if ((OtherData & 4) > 0) + { + NewSpawn.Title = Buffer.ReadString(false); + } + + if ((OtherData & 8) > 0) + { + NewSpawn.Suffix = Buffer.ReadString(false); + } + + // unknowns + Buffer.SkipBytes(8); + + NewSpawn.IsMercenary = Buffer.ReadByte(); + + ZoneSpawns.Add(NewSpawn); + } + + return ZoneSpawns; + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchMarch15-2011.cs b/utils/EQExtractor2/EQExtractor2/PatchMarch15-2011.cs new file mode 100644 index 000000000..2ec152125 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchMarch15-2011.cs @@ -0,0 +1,24 @@ +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchMarch152011Decoder : PatchFeb082011Decoder + { + public PatchMarch152011Decoder() + { + Version = "EQ Client Build Date March 2011."; + + PatchConfFileName = "patch_March15-2011.conf"; + + ExpectedPPLength = 28536; + + PPZoneIDOffset = 21204; + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchMay12-2010.cs b/utils/EQExtractor2/EQExtractor2/PatchMay12-2010.cs new file mode 100644 index 000000000..d31c6d172 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchMay12-2010.cs @@ -0,0 +1,1449 @@ +// +// Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net). Distributed under GPL version 2. +// +// + +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchMay122010Decoder : PatchSpecficDecoder + { + // The date specified in the version and patch conf file name should be the first build that this patch supports. If later + // patches don't break anything, then there is no need to create a new PatchSpecificDecoder just because the client build + // date has changed. + + // I expect in most cases if a new client patch is released that breaks the extractor, than a new decoder derived from the previous + // version's decoder would be created, rather than creating a brand new one based off the base class, unless all of the previous + // decoder's methods need replacing to support the new patch. + // + public PatchMay122010Decoder() + { + Version = "EQ Client Build Date May 12 2010. (Valid up to and including Build Date June 8 2010)"; + + PatchConfFileName = "patch_May12-2010.conf"; + + ExpectedPPLength = 26632; + + PPZoneIDOffset = 19396; + } + + override public bool Init(string ConfDirectory, ref string ErrorMessage) + { + OpManager = new OpCodeManager(); + + if (!OpManager.Init(ConfDirectory + "\\" + PatchConfFileName, ref ErrorMessage)) + return false; + + RegisterExplorers(); + + return true; + } + + // This method is used to identify a particular patch version + // + // The method should always return 'Tentative' for the Client->Server OP_ZoneEntry + // + // 'Yes' should only be returned when a packet uniquely identifying this particular patch version has been seen. + // + // When new patches are added, the Identify method of older patch decoders may need updating to ensure they identify a + // patch uniquely. + // + // + override public IdentificationStatus Identify(int OpCode, int Size, PacketDirection Direction) + { + if((OpCode == OpManager.OpCodeNameToNumber("OP_ZoneEntry")) && (Direction == PacketDirection.ClientToServer)) + return IdentificationStatus.Tentative; + + if ((OpCode == OpManager.OpCodeNameToNumber("OP_PlayerProfile")) && (Direction == PacketDirection.ServerToClient) && + (Size == ExpectedPPLength)) + return IdentificationStatus.Yes; + + return IdentificationStatus.No; + } + + override public int VerifyPlayerProfile() + { + List PlayerProfilePacket = GetPacketsOfType("OP_PlayerProfile", PacketDirection.ServerToClient); + + if (PlayerProfilePacket.Count == 0) + { + return 0; + } + else + { + if (PlayerProfilePacket[0].Length != ExpectedPPLength) + { + return 0; + } + } + + return ExpectedPPLength; + } + + override public UInt16 GetZoneNumber() + { + // A return value of zero from this method should be intepreted as 'Unable to identify patch version'. + + List PlayerProfilePacket = GetPacketsOfType("OP_PlayerProfile", PacketDirection.ServerToClient); + + if (PlayerProfilePacket.Count == 0) + { + return 0; + } + else + { + if (PlayerProfilePacket[0].Length != ExpectedPPLength) + { + return 0; + } + } + + return BitConverter.ToUInt16(PlayerProfilePacket[0], PPZoneIDOffset); + } + + override public List GetDoors() + { + List DoorList = new List(); + + List SpawnDoorPacket = GetPacketsOfType("OP_SpawnDoor", PacketDirection.ServerToClient); + + if ((SpawnDoorPacket.Count == 0) || (SpawnDoorPacket[0].Length == 0)) + return DoorList; + + int DoorCount = SpawnDoorPacket[0].Length / 92; + + ByteStream Buffer = new ByteStream(SpawnDoorPacket[0]); + + for (int d = 0; d < DoorCount; ++d) + { + string DoorName = Buffer.ReadFixedLengthString(32, false); + + float YPos = Buffer.ReadSingle(); + + float XPos = Buffer.ReadSingle(); + + float ZPos = Buffer.ReadSingle(); + + float Heading = Buffer.ReadSingle(); + + UInt32 Incline = Buffer.ReadUInt32(); + + Int32 Size = Buffer.ReadInt32(); + + Buffer.SkipBytes(4); // Skip Unknown + + Byte DoorID = Buffer.ReadByte(); + + Byte OpenType = Buffer.ReadByte(); + + Byte StateAtSpawn = Buffer.ReadByte(); + + Byte InvertState = Buffer.ReadByte(); + + Int32 DoorParam = Buffer.ReadInt32(); + + // Skip past the trailing unknowns in the door struct, moving to the next door in the packet. + + Buffer.SkipBytes(24); + + string DestZone = "NONE"; + + Door NewDoor = new Door(DoorName, YPos, XPos, ZPos, Heading, Incline, Size, DoorID, OpenType, StateAtSpawn, InvertState, + DoorParam, DestZone, 0, 0, 0, 0); + + DoorList.Add(NewDoor); + + } + return DoorList; + } + + override public MerchantManager GetMerchantData(NPCSpawnList NPCSL) + { + List PacketList = Packets.PacketList; + + UInt32 OP_ShopRequest = OpManager.OpCodeNameToNumber("OP_ShopRequest"); + + UInt32 OP_ShopEnd = OpManager.OpCodeNameToNumber("OP_ShopEnd"); + + UInt32 OP_ItemPacket = OpManager.OpCodeNameToNumber("OP_ItemPacket"); + + MerchantManager mm = new MerchantManager(); + + for (int i = 0; i < PacketList.Count; ++i) + { + EQApplicationPacket p = PacketList[i]; + + if ((p.Direction == PacketDirection.ServerToClient) && (p.OpCode == OP_ShopRequest)) + { + ByteStream Buffer = new ByteStream(p.Buffer); + + UInt32 MerchantSpawnID = Buffer.ReadUInt32(); + + NPCSpawn npc = NPCSL.GetNPC(MerchantSpawnID); + + UInt32 NPCTypeID; + + if (npc != null) + NPCTypeID = npc.NPCTypeID; + else + NPCTypeID = 0; + + mm.AddMerchant(MerchantSpawnID); + + for (int j = i + 1; j < PacketList.Count; ++j) + { + p = PacketList[j]; + + if ((p.OpCode == OP_ShopEnd) || (p.OpCode == OP_ShopRequest)) + break; + + if (p.OpCode == OP_ItemPacket) + { + Item NewItem = DecodeItemPacket(p.Buffer); + + mm.AddMerchantItem(MerchantSpawnID, NewItem.ID, NewItem.Name, NewItem.MerchantSlot, NewItem.Quantity); + } + } + } + } + + return mm; + } + + override public Item DecodeItemPacket(byte[] PacketBuffer) + { + ByteStream Buffer = new ByteStream(PacketBuffer); + + Item NewItem = new Item(); + + NewItem.StackSize = Buffer.ReadUInt32(); + Buffer.SkipBytes(4); + NewItem.Slot = Buffer.ReadUInt32(); + NewItem.MerchantSlot = Buffer.ReadUInt32(); + NewItem.Price = Buffer.ReadUInt32(); + NewItem.Quantity = Buffer.ReadInt32(); + Buffer.SetPosition(68); + NewItem.Name = Buffer.ReadString(true); + NewItem.Lore = Buffer.ReadString(true); + NewItem.IDFile = Buffer.ReadString(true); + NewItem.ID = Buffer.ReadUInt32(); + + return NewItem; + } + + override public List GetZonePointList() + { + List ZonePointList = new List(); + + List ZonePointPackets = GetPacketsOfType("OP_SendZonepoints", PacketDirection.ServerToClient); + + if (ZonePointPackets.Count < 1) + { + return ZonePointList; + } + + // Assume there is only 1 packet and process the first one. + + ByteStream Buffer = new ByteStream(ZonePointPackets[0]); + + UInt32 Entries = Buffer.ReadUInt32(); + + if (Entries == 0) + return ZonePointList; + + float x, y, z, Heading; + + UInt32 Number; + + UInt16 ZoneID, Instance; + + ZonePointList = new List(); + + for (int i = 0; i < Entries; ++i) + { + Number = Buffer.ReadUInt32(); + + y = Buffer.ReadSingle(); + + x = Buffer.ReadSingle(); + + z = Buffer.ReadSingle(); + + Heading = Buffer.ReadSingle(); + + if (Heading != 999) + Heading = Heading / 2; + + ZoneID = Buffer.ReadUInt16(); + + Instance = Buffer.ReadUInt16(); + + Buffer.SkipBytes(4); // Skip the last UInt32 + + ZonePoint NewZonePoint = new ZonePoint(Number, ZoneID, Instance, x, y, z, x, y, z, Heading, ZoneID); + + ZonePointList.Add(NewZonePoint); + } + + return ZonePointList; + } + + override public NewZoneStruct GetZoneData() + { + NewZoneStruct NewZone = new NewZoneStruct(); + + List ZonePackets = GetPacketsOfType("OP_NewZone", PacketDirection.ServerToClient); + + if (ZonePackets.Count < 1) + return NewZone; + + // Assume there is only 1 packet and process the first one. + + ByteStream Buffer = new ByteStream(ZonePackets[0]); + + string CharName = Buffer.ReadFixedLengthString(64, false); + + NewZone.ShortName = Buffer.ReadFixedLengthString(32, false); + + Buffer.SkipBytes(96); // Skip Unknown + + NewZone.LongName = Buffer.ReadFixedLengthString(278, true); + + NewZone.Type = Buffer.ReadByte(); + + NewZone.FogRed = Buffer.ReadBytes(4); + + NewZone.FogGreen = Buffer.ReadBytes(4); + + NewZone.FogBlue = Buffer.ReadBytes(4); + + Buffer.SkipBytes(1); // Unknown + + for (int i = 0; i < 4; ++i) + NewZone.FogMinClip[i] = Buffer.ReadSingle(); + + for (int i = 0; i < 4; ++i) + NewZone.FogMaxClip[i] = Buffer.ReadSingle(); + + NewZone.Gravity = Buffer.ReadSingle(); + + NewZone.TimeType = Buffer.ReadByte(); + + Buffer.SkipBytes(49); // Unknown + + NewZone.Sky = Buffer.ReadByte(); + + Buffer.SkipBytes(13); // Unknown + + NewZone.ZEM = Buffer.ReadSingle(); + + NewZone.SafeY = Buffer.ReadSingle(); + + NewZone.SafeX = Buffer.ReadSingle(); + + NewZone.SafeZ = Buffer.ReadSingle(); + + NewZone.MinZ = Buffer.ReadSingle(); + + NewZone.MaxZ = Buffer.ReadSingle(); + + NewZone.UnderWorld = Buffer.ReadSingle(); + + NewZone.MinClip = Buffer.ReadSingle(); + + NewZone.MaxClip = Buffer.ReadSingle(); + + Buffer.SkipBytes(84); // Unknown + + NewZone.ShortName2 = Buffer.ReadFixedLengthString(96, false); + + Buffer.SkipBytes(52); // Unknown + + NewZone.ZoneID = Buffer.ReadUInt16(); + + NewZone.InstanceID = Buffer.ReadUInt16(); + + Buffer.SkipBytes(38); // Unknown + + NewZone.FallDamage = Buffer.ReadByte(); + + Buffer.SkipBytes(21); // Unknown + + NewZone.FogDensity = Buffer.ReadSingle(); + + // Everything else after this point in the packet is unknown. + + return NewZone; + } + + override public List GetSpawns() + { + List ZoneSpawns = new List(); + + List SpawnPackets = GetPacketsOfType("OP_ZoneEntry", PacketDirection.ServerToClient); + + foreach (byte[] SpawnPacket in SpawnPackets) + { + ZoneEntryStruct NewSpawn = new ZoneEntryStruct(); + + ByteStream Buffer = new ByteStream(SpawnPacket); + + NewSpawn.SpawnName = Buffer.ReadString(true); + + NewSpawn.SpawnName = Utils.MakeCleanName(NewSpawn.SpawnName); + + NewSpawn.SpawnID = Buffer.ReadUInt32(); + + NewSpawn.Level = Buffer.ReadByte(); + + float UnkSize = Buffer.ReadSingle(); + + NewSpawn.IsNPC = Buffer.ReadByte(); + + UInt32 Bitfield = Buffer.ReadUInt32(); + + NewSpawn.Showname = (Bitfield >> 28) & 1; + NewSpawn.TargetableWithHotkey = (Bitfield >> 27) & 1; + NewSpawn.Targetable = (Bitfield >> 26) & 1; + + NewSpawn.ShowHelm = (Bitfield >> 24) & 1; + NewSpawn.Gender = (Bitfield >> 20) & 3; + + NewSpawn.Padding5 = (Bitfield >> 4) & 1; + NewSpawn.Padding7 = (Bitfield >> 6) & 2047; + NewSpawn.Padding26 = (Bitfield >> 25) & 1; + + Byte OtherData = Buffer.ReadByte(); + + Buffer.SkipBytes(8); // Skip 8 unknown bytes + + NewSpawn.DestructableString1 = ""; + NewSpawn.DestructableString2 = ""; + NewSpawn.DestructableString3 = ""; + + if ((NewSpawn.IsNPC == 1) && ((OtherData & 3) > 0)) + { + // Destructable Objects. Not handled yet + // + //SQLOut(String.Format("-- OBJECT FOUND SpawnID {0}", SpawnID.ToString("x"))); + + NewSpawn.DestructableString1 = Buffer.ReadString(false); + + NewSpawn.DestructableString2 = Buffer.ReadString(false); + + NewSpawn.DestructableString3 = Buffer.ReadString(false); + + NewSpawn.DestructableUnk1 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk2 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID1 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID2 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID3 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID4 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk3 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk4 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk5 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk6 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk7 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk8 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk9 = Buffer.ReadUInt32(); + + NewSpawn.DestructableByte = Buffer.ReadByte(); + + //SQLOut(String.Format("-- DES: {0,8:x} {1,8:x} {2,8:d} {3,8:d} {4,8:d} {5,8:d} {6,8:x} {7,8:x} {8,8:x} {9,8:x} {10,8:x} {11,8:x} {12,8:x} {13,2:x} {14} {15} {16}", + // DestructableUnk1, DestructableUnk2, DestructableID1, DestructableID2, DestructableID3, DestructableID4, + // DestructableUnk3, DestructableUnk4, DestructableUnk5, DestructableUnk6, DestructableUnk7, DestructableUnk8, + // DestructableUnk9, DestructableByte, DestructableString1, DestructableString2, DestructableString3)); + } + + NewSpawn.Size = Buffer.ReadSingle(); + + NewSpawn.Face = Buffer.ReadByte(); + + NewSpawn.WalkSpeed = Buffer.ReadSingle(); + + NewSpawn.RunSpeed = Buffer.ReadSingle(); + + NewSpawn.Race = Buffer.ReadUInt32(); + + NewSpawn.PropCount = Buffer.ReadByte(); + + NewSpawn.BodyType = 0; + + if (NewSpawn.PropCount >= 1) + { + NewSpawn.BodyType = Buffer.ReadUInt32(); + + for (int j = 1; j < NewSpawn.PropCount; ++j) + Buffer.SkipBytes(4); + } + + Buffer.SkipBytes(1); // Skip HP % + + NewSpawn.HairColor = Buffer.ReadByte(); + NewSpawn.BeardColor = Buffer.ReadByte(); + NewSpawn.EyeColor1 = Buffer.ReadByte(); + NewSpawn.EyeColor2 = Buffer.ReadByte(); + NewSpawn.HairStyle = Buffer.ReadByte(); + NewSpawn.Beard = Buffer.ReadByte(); + + NewSpawn.DrakkinHeritage = Buffer.ReadUInt32(); + + NewSpawn.DrakkinTattoo = Buffer.ReadUInt32(); + + NewSpawn.DrakkinDetails = Buffer.ReadUInt32(); + + Buffer.SkipBytes(1); // Skip Holding + + NewSpawn.Deity = Buffer.ReadUInt32(); + + Buffer.SkipBytes(8); // Skip GuildID and GuildRank + + NewSpawn.Class = Buffer.ReadByte(); + + Buffer.SkipBytes(4); // Skip PVP, Standstate, Light, Flymode + + NewSpawn.EquipChest2 = Buffer.ReadByte(); + + bool UseWorn = (NewSpawn.EquipChest2 == 255); + + Buffer.SkipBytes(2); // 2 Unknown bytes; + + NewSpawn.Helm = Buffer.ReadByte(); + + NewSpawn.LastName = Buffer.ReadString(false); + + Buffer.SkipBytes(5); // AATitle + unknown byte + + NewSpawn.PetOwnerID = Buffer.ReadUInt32(); + + Buffer.SkipBytes(25); // Unknown byte + 6 unknown uint32 + + UInt32 Position1 = Buffer.ReadUInt32(); + + UInt32 Position2 = Buffer.ReadUInt32(); + + UInt32 Position3 = Buffer.ReadUInt32(); + + UInt32 Position4 = Buffer.ReadUInt32(); + + UInt32 Position5 = Buffer.ReadUInt32(); + + NewSpawn.YPos = Utils.EQ19ToFloat((Int32)(Position3 & 0x7FFFF)); + + NewSpawn.Heading = Utils.EQ19ToFloat((Int32)(Position4 & 0xFFF)); + + NewSpawn.XPos = Utils.EQ19ToFloat((Int32)(Position4 >> 12) & 0x7FFFF); + + NewSpawn.ZPos = Utils.EQ19ToFloat((Int32)(Position5 & 0x7FFFF)); + + for (int ColourSlot = 0; ColourSlot < 9; ++ColourSlot) + NewSpawn.SlotColour[ColourSlot] = Buffer.ReadUInt32(); + + NewSpawn.MeleeTexture1 = 0; + NewSpawn.MeleeTexture2 = 0; + + if (NPCType.IsPlayableRace(NewSpawn.Race)) + { + for (int i = 0; i < 9; ++i) + { + NewSpawn.Equipment[i] = Buffer.ReadUInt32(); + + UInt32 Equip1 = Buffer.ReadUInt32(); + + UInt32 Equip0 = Buffer.ReadUInt32(); + } + + if (NewSpawn.Equipment[Constants.MATERIAL_CHEST] > 0) + { + NewSpawn.EquipChest2 = (byte)NewSpawn.Equipment[Constants.MATERIAL_CHEST]; + + } + + NewSpawn.ArmorTintRed = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 16) & 0xff); + + NewSpawn.ArmorTintGreen = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 8) & 0xff); + + NewSpawn.ArmorTintBlue = (byte)(NewSpawn.SlotColour[Constants.MATERIAL_CHEST] & 0xff); + + if (NewSpawn.Equipment[Constants.MATERIAL_PRIMARY] > 0) + NewSpawn.MeleeTexture1 = NewSpawn.Equipment[Constants.MATERIAL_PRIMARY]; + + if (NewSpawn.Equipment[Constants.MATERIAL_SECONDARY] > 0) + NewSpawn.MeleeTexture2 = NewSpawn.Equipment[Constants.MATERIAL_SECONDARY]; + + if (UseWorn) + NewSpawn.Helm = (byte)NewSpawn.Equipment[Constants.MATERIAL_HEAD]; + else + NewSpawn.Helm = 0; + + } + else + { + // Non playable race + NewSpawn.MeleeTexture1 = NewSpawn.SlotColour[3]; + NewSpawn.MeleeTexture2 = NewSpawn.SlotColour[6]; + } + + if (NewSpawn.EquipChest2 == 255) + NewSpawn.EquipChest2 = 0; + + if (NewSpawn.Helm == 255) + NewSpawn.Helm = 0; + + if ((OtherData & 4) > 0) + { + NewSpawn.Title = Buffer.ReadString(false); + } + + if ((OtherData & 8) > 0) + { + NewSpawn.Suffix = Buffer.ReadString(false); + } + + // unknowns + Buffer.SkipBytes(8); + + NewSpawn.IsMercenary = Buffer.ReadByte(); + + ZoneSpawns.Add(NewSpawn); + } + + return ZoneSpawns; + } + + override public List GetHighResolutionMovementUpdates() + { + List Updates = new List(); + + List UpdatePackets = GetPacketsOfType("OP_NPCMoveUpdate", PacketDirection.ServerToClient); + + foreach (byte[] UpdatePacket in UpdatePackets) + Updates.Add(Decode_OP_NPCMoveUpdate(UpdatePacket)); + + return Updates; + } + + override public PositionUpdate Decode_OP_NPCMoveUpdate(byte[] UpdatePacket) + { + PositionUpdate PosUpdate = new PositionUpdate(); + + BitStream bs = new BitStream(UpdatePacket, 13); + + PosUpdate.SpawnID = bs.readUInt(16); + + UInt32 VFlags = bs.readUInt(6); + + PosUpdate.p.y = (float)bs.readInt(19) / (float)(1 << 3); + + PosUpdate.p.x = (float)bs.readInt(19) / (float)(1 << 3); + + PosUpdate.p.z = (float)bs.readInt(19) / (float)(1 << 3); + + PosUpdate.p.heading = (float)bs.readInt(12) / (float)(1 << 3); + + PosUpdate.HighRes = true; + + return PosUpdate; + } + + override public List GetLowResolutionMovementUpdates() + { + List Updates = new List(); + + List UpdatePackets = GetPacketsOfType("OP_MobUpdate", PacketDirection.ServerToClient); + + foreach (byte[] MobUpdatePacket in UpdatePackets) + Updates.Add(Decode_OP_MobUpdate(MobUpdatePacket)); + + return Updates; + } + + override public PositionUpdate Decode_OP_MobUpdate(byte[] MobUpdatePacket) + { + PositionUpdate PosUpdate = new PositionUpdate(); + + ByteStream Buffer = new ByteStream(MobUpdatePacket); + + PosUpdate.SpawnID = Buffer.ReadUInt16(); + + UInt32 Word1 = Buffer.ReadUInt32(); + + UInt32 Word2 = Buffer.ReadUInt32(); + + UInt16 Word3 = Buffer.ReadUInt16(); + + PosUpdate.p.y = Utils.EQ19ToFloat((Int32)(Word1 & 0x7FFFF)); + + // Z is in the top 13 bits of Word1 and the bottom 6 of Word2 + + UInt32 ZPart1 = Word1 >> 19; // ZPart1 now has low order bits of Z in bottom 13 bits + UInt32 ZPart2 = Word2 & 0x3F; // ZPart2 now has high order bits of Z in bottom 6 bits + + ZPart2 = ZPart2 << 13; + + PosUpdate.p.z = Utils.EQ19ToFloat((Int32)(ZPart1 | ZPart2)); + + PosUpdate.p.x = Utils.EQ19ToFloat((Int32)(Word2 >> 6) & 0x7FFFF); + + PosUpdate.p.heading = Utils.EQ19ToFloat((Int32)(Word3 & 0xFFF)); + + PosUpdate.HighRes = false; + + return PosUpdate; + } + + override public List GetAllMovementUpdates() + { + PositionUpdate PosUpdate = new PositionUpdate(); + + List Updates = new List(); + + List PacketList = Packets.PacketList; + + UInt32 OP_NPCMoveUpdate = OpManager.OpCodeNameToNumber("OP_NPCMoveUpdate"); + + UInt32 OP_MobUpdate = OpManager.OpCodeNameToNumber("OP_MobUpdate"); + + for (int i = 0; i < PacketList.Count; ++i) + { + EQApplicationPacket p = PacketList[i]; + + if (p.Direction == PacketDirection.ServerToClient) + { + if (p.OpCode == OP_NPCMoveUpdate) + { + PosUpdate = Decode_OP_NPCMoveUpdate(p.Buffer); + PosUpdate.p.TimeStamp = p.PacketTime; + Updates.Add(PosUpdate); + } + else if (p.OpCode == OP_MobUpdate) + { + PosUpdate = Decode_OP_MobUpdate(p.Buffer); + PosUpdate.p.TimeStamp = p.PacketTime; + Updates.Add(PosUpdate); + } + } + } + + return Updates; + } + + override public List GetClientMovementUpdates() + { + List Updates = new List(); + + List PacketList = Packets.PacketList; + + UInt32 OP_ClientUpdate = OpManager.OpCodeNameToNumber("OP_ClientUpdate"); + + foreach (EQApplicationPacket UpdatePacket in PacketList) + { + if ((UpdatePacket.OpCode != OP_ClientUpdate) || (UpdatePacket.Direction != PacketDirection.ClientToServer)) + continue; + + ByteStream Buffer = new ByteStream(UpdatePacket.Buffer); + + PositionUpdate PosUpdate = new PositionUpdate(); + + PosUpdate.SpawnID = Buffer.ReadUInt16(); + Buffer.SkipBytes(6); + PosUpdate.p.x = Buffer.ReadSingle(); + PosUpdate.p.y = Buffer.ReadSingle(); + Buffer.SkipBytes(12); + PosUpdate.p.z = Buffer.ReadSingle(); + PosUpdate.p.TimeStamp = UpdatePacket.PacketTime; + Buffer.SkipBytes(4); + UInt32 Temp = Buffer.ReadUInt32(); + Temp = Temp & 0x3FFFFF; + Temp = Temp >> 10; + PosUpdate.p.heading = Utils.EQ19ToFloat((Int32)(Temp)); + + + Updates.Add(PosUpdate); + } + + return Updates; + } + + override public List GetGroundSpawns() + { + List GroundSpawns = new List(); + + List GroundSpawnPackets = GetPacketsOfType("OP_GroundSpawn", PacketDirection.ServerToClient); + + foreach (byte[] GroundSpawnPacket in GroundSpawnPackets) + { + GroundSpawnStruct GroundSpawn = new GroundSpawnStruct(); + + ByteStream Buffer = new ByteStream(GroundSpawnPacket); + + GroundSpawn.DropID = Buffer.ReadUInt32(); + + GroundSpawn.Name = Buffer.ReadString(false); + + GroundSpawn.ZoneID = Buffer.ReadUInt16(); + + GroundSpawn.InstanceID = Buffer.ReadUInt16(); + + Buffer.SkipBytes(8); // Two unknown uint32s + + GroundSpawn.Heading = Buffer.ReadSingle(); + + Buffer.SkipBytes(12); // First float is 255 to make some groundspawns appear, second 4 bytes unknown, last is a float + + GroundSpawn.y = Buffer.ReadSingle(); + + GroundSpawn.x = Buffer.ReadSingle(); + + GroundSpawn.z = Buffer.ReadSingle(); + + GroundSpawn.ObjectType = Buffer.ReadUInt32(); + + GroundSpawns.Add(GroundSpawn); + } + return GroundSpawns; + } + + override public List GetFindableSpawns() + { + List FindableSpawnList = new List(); + + List FindablePackets = GetPacketsOfType("OP_SendFindableNPCs", PacketDirection.ServerToClient); + + if (FindablePackets.Count < 1) + return FindableSpawnList; + + foreach (byte[] Packet in FindablePackets) + { + if (BitConverter.ToUInt32(Packet, 0) == 0) + FindableSpawnList.Add(BitConverter.ToUInt32(Packet, 4)); + } + + return FindableSpawnList; + } + + override public string GetZoneName() + { + List NewZonePacket = GetPacketsOfType("OP_NewZone", PacketDirection.ServerToClient); + + if (NewZonePacket.Count != 1) + return ""; + + return Utils.ReadNullTerminatedString(NewZonePacket[0], 704, 96, false); + } + + public override void RegisterExplorers() + { + //OpManager.RegisterExplorer("OP_NewZone", ExploreNewZonePacket); + //OpManager.RegisterExplorer("OP_ZoneEntry", ExploreZoneEntry); + //OpManager.RegisterExplorer("OP_PlayerProfile", ExplorePlayerProfile); + //OpManager.RegisterExplorer("OP_RespawnWindow", ExploreRespawnWindow); + //OpManager.RegisterExplorer("OP_ZonePlayerToBind", ExploreZonePlayerToBind); + //OpManager.RegisterExplorer("OP_RequestClientZoneChange", ExploreRequestClientZoneChange); + //OpManager.RegisterExplorer("OP_DeleteSpawn", ExploreDeleteSpawn); + //OpManager.RegisterExplorer("OP_SpawnAppearance", ExploreSpawnAppearance); + //OpManager.RegisterExplorer("OP_HPUpdate", ExploreHPUpdate); + //OpManager.RegisterExplorer("OP_Animation", ExploreAnimation); + //OpManager.RegisterExplorer("OP_CharInventory", ExploreCharInventoryPacket); + + } + + public void ExploreZoneEntry(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + if (Direction != PacketDirection.ServerToClient) + return; + + string Name = Buffer.ReadString(false); + UInt32 SpawnID = Buffer.ReadUInt32(); + byte Level = Buffer.ReadByte(); + Buffer.SkipBytes(4); + bool IsNPC = (Buffer.ReadByte() != 0); + UInt32 Bitfield = Buffer.ReadUInt32(); + + string DestructableString1; + string DestructableString2; + string DestructableString3; + UInt32 DestructableUnk1; + UInt32 DestructableUnk2; + UInt32 DestructableID1; + UInt32 DestructableID2; + UInt32 DestructableID3; + UInt32 DestructableID4; + UInt32 DestructableUnk3; + UInt32 DestructableUnk4; + UInt32 DestructableUnk5; + UInt32 DestructableUnk6; + UInt32 DestructableUnk7; + UInt32 DestructableUnk8; + UInt32 DestructableUnk9; + byte DestructableByte; + + Byte OtherData = Buffer.ReadByte(); + + Buffer.SkipBytes(8); // Skip 8 unknown bytes + + DestructableString1 = ""; + DestructableString2 = ""; + DestructableString3 = ""; + + OutputStream.WriteLine("Spawn Name: {0} ID: {1} Level: {2} {3}\r\n", Name, SpawnID, Level, IsNPC ? "NPC" : "Player"); + + if ((OtherData & 1) > 0) + { + // Destructable Objects. + + DestructableString1 = Buffer.ReadString(false); + + DestructableString2 = Buffer.ReadString(false); + + DestructableString3 = Buffer.ReadString(false); + + DestructableUnk1 = Buffer.ReadUInt32(); + + DestructableUnk2 = Buffer.ReadUInt32(); + + DestructableID1 = Buffer.ReadUInt32(); + + DestructableID2 = Buffer.ReadUInt32(); + + DestructableID3 = Buffer.ReadUInt32(); + + DestructableID4 = Buffer.ReadUInt32(); + + DestructableUnk3 = Buffer.ReadUInt32(); + + DestructableUnk4 = Buffer.ReadUInt32(); + + DestructableUnk5 = Buffer.ReadUInt32(); + + DestructableUnk6 = Buffer.ReadUInt32(); + + DestructableUnk7 = Buffer.ReadUInt32(); + + DestructableUnk8 = Buffer.ReadUInt32(); + + DestructableUnk9 = Buffer.ReadUInt32(); + + DestructableByte = Buffer.ReadByte(); + + OutputStream.WriteLine("DESTRUCTABLE OBJECT:\r\n"); + OutputStream.WriteLine(" String1: {0}", DestructableString1); + OutputStream.WriteLine(" String2: {0}", DestructableString2); + OutputStream.WriteLine(" String3: {0}\r\n", DestructableString3); + + OutputStream.WriteLine(" Unk1: {0,8:x} Unk2: {1,8:x}\r\n ID1 : {2,8:x} ID2 : {3,8:x} ID3 : {4,8:x} ID4 : {5,8:x}\r\n Unk3: {6,8:x} Unk4: {7,8:x} Unk5: {8,8:x} Unk6: {9,8:x}\r\n Unk7: {10,8:x} Unk8: {11,8:x} Unk9: {12,8:x}\r\n UnkByte: {13,2:x}", + DestructableUnk1, DestructableUnk2, DestructableID1, DestructableID2, DestructableID3, DestructableID4, + DestructableUnk3, DestructableUnk4, DestructableUnk5, DestructableUnk6, DestructableUnk7, DestructableUnk8, + DestructableUnk9, DestructableByte); + } + + Buffer.SkipBytes(17); + + byte PropCount = Buffer.ReadByte(); + + if (PropCount >= 1) + { + Buffer.SkipBytes(4); + + for (int j = 1; j < PropCount; ++j) + Buffer.SkipBytes(4); + } + + byte HP = Buffer.ReadByte(); + + OutputStream.WriteLine("HP% is {0}\r\n", HP); + + AddExplorerSpawn(SpawnID, Name); + } + + public void ExploreSpawnAppearance(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt16 SpawnID = Buffer.ReadUInt16(); + UInt16 Type = Buffer.ReadUInt16(); + UInt32 Param = Buffer.ReadUInt32(); + string SpawnName = FindExplorerSpawn(SpawnID); + + OutputStream.WriteLine("Spawn {0} {1} Appearance Change Type {2} Parameter {3}", SpawnID, SpawnName, Type, Param); + + OutputStream.WriteLine(""); + } + + public void ExploreHPUpdate(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 CurrentHP = Buffer.ReadUInt32(); + Int32 MaxHP = Buffer.ReadInt32(); + UInt16 SpawnID = Buffer.ReadUInt16(); + + string SpawnName = FindExplorerSpawn(SpawnID); + + OutputStream.WriteLine("Spawn {0} {1} Current HP: {2} Max HP: {3}", SpawnID, SpawnName, CurrentHP, MaxHP); + + OutputStream.WriteLine(""); + } + + public void ExploreAnimation(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt16 SpawnID = Buffer.ReadUInt16(); + byte Action = Buffer.ReadByte(); + byte Value = Buffer.ReadByte(); + + string SpawnName = FindExplorerSpawn(SpawnID); + + OutputStream.WriteLine("Spawn {0} {1} Action: {2} Value: {3}", SpawnID, SpawnName, Action, Value); + + OutputStream.WriteLine(""); + } + + public void ExploreNewZonePacket(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + Buffer.SetPosition(704); + + OutputStream.WriteLine("Zone name is {0}\r\n", Buffer.ReadString(false)); + } + + public void ExplorePlayerProfile(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + Buffer.SetPosition(12); + + UInt32 PlayerClass = Buffer.ReadUInt32(); + + Buffer.SetPosition(56); + + byte PlayerLevel = Buffer.ReadByte(); + + Buffer.SetPosition(18100); + + OutputStream.WriteLine("Name: {0} Class: {1} Level: {2}\r\n", Buffer.ReadString(false), PlayerClass, PlayerLevel); + } + + public void ExploreRespawnWindow(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + if (Direction == PacketDirection.ServerToClient) + { + UInt32 Unknown000 = Buffer.ReadUInt32(); + UInt32 TimeRemaining = Buffer.ReadUInt32(); + UInt32 Unknown008 = Buffer.ReadUInt32(); + UInt32 NumBinds = Buffer.ReadUInt32(); + + OutputStream.WriteLine("Unknown000: {0} Time: {1} Unknown008: {2} Num Binds: {3}\r\n", Unknown000, TimeRemaining, Unknown008, NumBinds); + + for (int i = 0; i < NumBinds; ++i) + { + UInt32 BindNumber = Buffer.ReadUInt32(); + UInt32 ZoneID = Buffer.ReadUInt32(); + float X = Buffer.ReadSingle(); + float Y = Buffer.ReadSingle(); + float Z = Buffer.ReadSingle(); + float Heading = Buffer.ReadSingle(); + string ZoneName = Buffer.ReadString(false); + byte Valid = Buffer.ReadByte(); + + OutputStream.WriteLine("Bind Number: {0} Zone ID: {1} Zone Name: {2} Valid: {3}", BindNumber, ZoneID, ZoneName, Valid); + } + } + + OutputStream.WriteLine(""); + } + + public void ExploreZonePlayerToBind(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 ZoneID = Buffer.ReadUInt32(); + + float X = Buffer.ReadSingle(); + float Y = Buffer.ReadSingle(); + float Z = Buffer.ReadSingle(); + float Heading = Buffer.ReadSingle(); + string ZoneName = Buffer.ReadString(false); + + byte Unknown021 = Buffer.ReadByte(); + UInt32 Unknown022 = Buffer.ReadUInt32(); + UInt32 Unknown023 = Buffer.ReadUInt32(); + UInt32 Unknown024 = Buffer.ReadUInt32(); + + OutputStream.WriteLine("ZoneID: {0} Loc: ({1}, {2}, {3}) ZoneName: {4}", ZoneID, X, Y, Z, ZoneName); + OutputStream.WriteLine("Unknowns: {0} {1} {2} {3}", Unknown021, Unknown022, Unknown023, Unknown024); + + + OutputStream.WriteLine(""); + } + + public void ExploreRequestClientZoneChange(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt16 ZoneID = Buffer.ReadUInt16(); + UInt16 Instance = Buffer.ReadUInt16(); + float X = Buffer.ReadSingle(); + float Y = Buffer.ReadSingle(); + float Z = Buffer.ReadSingle(); + float Heading = Buffer.ReadSingle(); + UInt32 Type = Buffer.ReadUInt16(); + + OutputStream.WriteLine("ZoneID: {0} Type: {1}", ZoneID, Type); + OutputStream.WriteLine(""); + } + + public void ExploreDeleteSpawn(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 SpawnID = Buffer.ReadUInt32(); + byte Decay = 0; // Buffer.ReadByte(); + + string SpawnName = FindExplorerSpawn(SpawnID); + + OutputStream.WriteLine("SpawnID: {0} {1} Decay: {2}\r\n", SpawnID, SpawnName, Decay); + } + + public void ExploreCharInventoryPacket(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 ItemCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("There are {0} items in the inventory.\r\n", ItemCount); + + for (int i = 0; i < ItemCount; ++i) + { + ExploreSubItem(OutputStream, ref Buffer); + } + + OutputStream.WriteLine(""); + } + + public void ExploreItemPacket(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + Buffer.SkipBytes(4); // Skip type field. + + ExploreSubItem(OutputStream, ref Buffer); + + OutputStream.WriteLine(""); + } + + void ExploreSubItem(StreamWriter OutputStream, ref ByteStream Buffer) + { + UInt32 StackSize = Buffer.ReadUInt32(); + Buffer.SkipBytes(4); + UInt32 Slot = Buffer.ReadUInt32(); + UInt32 Price = Buffer.ReadUInt32(); + UInt32 MerchantSlot = Buffer.ReadUInt32(); + Buffer.SkipBytes(16); + Buffer.SkipBytes(28); + string Name = Buffer.ReadString(true); + Buffer.ReadString(true); // Lore + Buffer.ReadString(true); // IDFile + + OutputStream.WriteLine("Item Name: {0}", Name); + + Buffer.SkipBytes(236); // ItemBodyStruct + Buffer.ReadString(true); // CharmFile + Buffer.SkipBytes(64); // ItemSecondaryBodyStruct + Buffer.ReadString(true); // Filename + + Buffer.SkipBytes(76); // ItemTertiaryBodyStruct + + //Buffer.SkipBytes(30); // Click Effect Struct + UInt32 Effect = Buffer.ReadUInt32(); + byte Level2 = Buffer.ReadByte(); + UInt32 Type = Buffer.ReadUInt32(); + byte Level = Buffer.ReadByte(); + UInt32 Unknown1 = Buffer.ReadUInt32(); + UInt32 Unknown2 = Buffer.ReadUInt32(); + UInt32 Unknown3 = Buffer.ReadUInt32(); + UInt32 Unknown4 = Buffer.ReadUInt32(); + UInt32 Unknown5 = Buffer.ReadUInt32(); + + OutputStream.WriteLine("Buffer pos is {0}" + Buffer.GetPosition()); + string ClickName = Buffer.ReadString(true); // Clickname + OutputStream.WriteLine(" Click Name: {0}", ClickName); + //Buffer.SkipBytes(4); // Clickunk7 + UInt32 Unknown7 = Buffer.ReadUInt32(); + OutputStream.WriteLine(" Effect: {0} Level2: {1} Type {2} Level {3}", Effect, Level2, Type, Level); + OutputStream.WriteLine(" Unks: {0} {1} {2} {3} {4} {5}", Unknown1, Unknown2, Unknown3, Unknown4, Unknown5, Unknown7); + + Buffer.SkipBytes(30); // Proc Effect Struct + Buffer.ReadString(true); // Clickname + Buffer.SkipBytes(4); // Unknown5 + + Buffer.SkipBytes(30); // Worn Effect Struct + Buffer.ReadString(true); // Wornname + Buffer.SkipBytes(4); // Unknown6 + + //Buffer.SkipBytes(30); // Worn Effect Struct + Effect = Buffer.ReadUInt32(); + Level2 = Buffer.ReadByte(); + Type = Buffer.ReadUInt32(); + Level = Buffer.ReadByte(); + Unknown1 = Buffer.ReadUInt32(); + Unknown2 = Buffer.ReadUInt32(); + Unknown3 = Buffer.ReadUInt32(); + Unknown4 = Buffer.ReadUInt32(); + Unknown5 = Buffer.ReadUInt32(); + string FocusName = Buffer.ReadString(true); // Focusname + OutputStream.WriteLine(" Focusname is {0}", FocusName); + UInt32 Unknown6 = Buffer.ReadUInt32(); + OutputStream.WriteLine(" Effect: {0} Level2: {1} Type {2} Level {3}", Effect, Level2, Type, Level); + OutputStream.WriteLine(" Unks: {0} {1} {2} {3} {4} {5}", Unknown1, Unknown2, Unknown3, Unknown4, Unknown5, Unknown6); + //Buffer.SkipBytes(4); // Unknown6 + + Buffer.SkipBytes(30); // Scroll Effect Struct + Buffer.ReadString(true); // Scrollname + Buffer.SkipBytes(4); // Unknown6 + + Buffer.SkipBytes(30); // Bard Effect Struct + Buffer.ReadString(true); // Wornname + Buffer.SkipBytes(4); // Unknown6 + + Buffer.SkipBytes(103); // Quaternarybodystruct - 4 + + UInt32 SubLengths = Buffer.ReadUInt32(); + + //return; + + for (int i = 0; i < SubLengths; ++i) + { + Buffer.SkipBytes(4); + ExploreSubItem(OutputStream, ref Buffer); + } + + return; + + //Buffer.SkipBytes(236); // Item Body Struct + + UInt32 ID = Buffer.ReadUInt32(); + byte Weight = Buffer.ReadByte(); + byte NoRent = Buffer.ReadByte(); + byte NoDrop = Buffer.ReadByte(); + byte Attune = Buffer.ReadByte(); + byte Size = Buffer.ReadByte(); + + OutputStream.WriteLine(" ID: {0} Weight: {1} NoRent: {2} NoDrop: {3} Attune {4} Size {5}", ID, Weight, NoRent, NoDrop, Attune, Size); + + UInt32 Slots = Buffer.ReadUInt32(); + //UInt32 Price = Buffer.ReadUInt32(); + UInt32 Icon = Buffer.ReadUInt32(); + Buffer.SkipBytes(2); + UInt32 BenefitFlags = Buffer.ReadUInt32(); + byte Tradeskills = Buffer.ReadByte(); + + OutputStream.WriteLine(" Slots: {0} Price: {1} Icon: {2} BenefitFlags {3} Tradeskills: {4}", Slots, Price, Icon, BenefitFlags, Tradeskills); + + byte CR = Buffer.ReadByte(); + byte DR = Buffer.ReadByte(); + byte PR = Buffer.ReadByte(); + byte MR = Buffer.ReadByte(); + byte FR = Buffer.ReadByte(); + byte SVC = Buffer.ReadByte(); + + OutputStream.WriteLine(" CR: {0} DR: {1} PR: {2} MR: {3} FR: {4} SVC: {5}", CR, DR, PR, MR, FR, SVC); + + byte AStr = Buffer.ReadByte(); + byte ASta = Buffer.ReadByte(); + byte AAgi = Buffer.ReadByte(); + byte ADex = Buffer.ReadByte(); + byte ACha = Buffer.ReadByte(); + byte AInt = Buffer.ReadByte(); + byte AWis = Buffer.ReadByte(); + + OutputStream.WriteLine(" AStr: {0} ASta: {1} AAgi: {2} ADex: {3} ACha: {4} AInt: {5} AWis: {6}", AStr, ASta, AAgi, ADex, ACha, AInt, AWis); + + Int32 HP = Buffer.ReadInt32(); + Int32 Mana = Buffer.ReadInt32(); + UInt32 Endurance = Buffer.ReadUInt32(); + Int32 AC = Buffer.ReadInt32(); + Int32 Regen = Buffer.ReadInt32(); + Int32 ManaRegen = Buffer.ReadInt32(); + Int32 EndRegen = Buffer.ReadInt32(); + UInt32 Classes = Buffer.ReadUInt32(); + UInt32 Races = Buffer.ReadUInt32(); + UInt32 Deity = Buffer.ReadUInt32(); + Int32 SkillModValue = Buffer.ReadInt32(); + Buffer.SkipBytes(4); + UInt32 SkillModType = Buffer.ReadUInt32(); + UInt32 BaneDamageRace = Buffer.ReadUInt32(); + UInt32 BaneDamageBody = Buffer.ReadUInt32(); + UInt32 BaneDamageRaceAmount = Buffer.ReadUInt32(); + Int32 BaneDamageAmount = Buffer.ReadInt32(); + byte Magic = Buffer.ReadByte(); + Int32 CastTime = Buffer.ReadInt32(); + UInt32 ReqLevel = Buffer.ReadUInt32(); + UInt32 RecLevel = Buffer.ReadUInt32(); + UInt32 ReqSkill = Buffer.ReadUInt32(); + UInt32 BardType = Buffer.ReadUInt32(); + Int32 BardValue = Buffer.ReadInt32(); + byte Light = Buffer.ReadByte(); + byte Delay = Buffer.ReadByte(); + byte ElemDamageAmount = Buffer.ReadByte(); + byte ElemDamageType = Buffer.ReadByte(); + byte Range = Buffer.ReadByte(); + UInt32 Damage = Buffer.ReadUInt32(); + UInt32 Color = Buffer.ReadUInt32(); + byte ItemType = Buffer.ReadByte(); + UInt32 Material = Buffer.ReadUInt32(); + Buffer.SkipBytes(4); + UInt32 EliteMaterial = Buffer.ReadUInt32(); + float SellRate = Buffer.ReadSingle(); + Int32 CombatEffects = Buffer.ReadInt32(); + Int32 Shielding = Buffer.ReadInt32(); + Int32 StunResist = Buffer.ReadInt32(); + Int32 StrikeThrough = Buffer.ReadInt32(); + Int32 ExtraDamageSkill = Buffer.ReadInt32(); + Int32 ExtraDamageAmount = Buffer.ReadInt32(); + Int32 SpellShield = Buffer.ReadInt32(); + Int32 Avoidance = Buffer.ReadInt32(); + Int32 Accuracy = Buffer.ReadInt32(); + UInt32 CharmFileID = Buffer.ReadUInt32(); + UInt32 FactionMod1 = Buffer.ReadUInt32(); + Int32 FactionAmount1 = Buffer.ReadInt32(); + UInt32 FactionMod2 = Buffer.ReadUInt32(); + Int32 FactionAmount2 = Buffer.ReadInt32(); + UInt32 FactionMod3 = Buffer.ReadUInt32(); + Int32 FactionAmount3 = Buffer.ReadInt32(); + UInt32 FactionMod4 = Buffer.ReadUInt32(); + Int32 FactionAmount4 = Buffer.ReadInt32(); + + Buffer.ReadString(true); // Charm File + Buffer.SkipBytes(64); // Item Secondary Body Struct + Buffer.ReadString(true); // Filename + Buffer.SkipBytes(76); // Item Tertiary Body Struct + Buffer.SkipBytes(30); // Click Effect Struct + Buffer.ReadString(true); // Clickname + Buffer.SkipBytes(4); // clickunk7 + Buffer.SkipBytes(30); // Proc Effect Struct + Buffer.ReadString(true); // Proc Name + Buffer.SkipBytes(4); // unknown5 + Buffer.SkipBytes(30); // Worn Effect Struct + Buffer.ReadString(true); // Worn Name + Buffer.SkipBytes(4); // unknown6 + Buffer.SkipBytes(30); // Worn Effect Struct + Buffer.ReadString(true); // Worn Name + Buffer.SkipBytes(4); // unknown6 + Buffer.SkipBytes(30); // Worn Effect Struct + Buffer.ReadString(true); // Worn Name + Buffer.SkipBytes(4); // unknown6 + Buffer.SkipBytes(30); // Worn Effect Struct + Buffer.ReadString(true); // Worn Name + Buffer.SkipBytes(4); // unknown6 + Buffer.SkipBytes(103); // Item Quaternary Body Struct - 4 (we want to read the SubLength field at the end) + + //UInt32 SubLengths = Buffer.ReadUInt32(); + + for (int i = 0; i < SubLengths; ++i) + { + Buffer.SkipBytes(4); + ExploreSubItem(OutputStream, ref Buffer); + } + } + + override public bool DumpAAs(string FileName) + { + List AAPackets = GetPacketsOfType("OP_SendAATable", PacketDirection.ServerToClient); + + + if (AAPackets.Count < 1) + return false; + + StreamWriter OutputFile; + + try + { + OutputFile = new StreamWriter(FileName); + } + catch + { + return false; + + } + + OutputFile.WriteLine("-- There are " + AAPackets.Count + " OP_SendAATable packets."); + OutputFile.WriteLine(""); + + foreach (byte[] Packet in AAPackets) + { + UInt32 AAID = BitConverter.ToUInt32(Packet, 0); + UInt32 Unknown004 = BitConverter.ToUInt32(Packet, 4); + UInt32 HotKeySID = BitConverter.ToUInt32(Packet, 5); + UInt32 HotKeySID2 = BitConverter.ToUInt32(Packet, 9); + UInt32 TitleSID = BitConverter.ToUInt32(Packet, 13); + UInt32 DescSID = BitConverter.ToUInt32(Packet, 17); + UInt32 ClassType = BitConverter.ToUInt32(Packet, 21); + UInt32 Cost = BitConverter.ToUInt32(Packet, 25); + UInt32 Seq = BitConverter.ToUInt32(Packet, 29); + UInt32 CurrentLevel = BitConverter.ToUInt32(Packet, 33); + UInt32 PrereqSkill = BitConverter.ToUInt32(Packet, 37); + UInt32 PrereqMinpoints = BitConverter.ToUInt32(Packet, 41); + UInt32 Type = BitConverter.ToUInt32(Packet, 45); + UInt32 SpellID = BitConverter.ToUInt32(Packet, 49); + UInt32 SpellType = BitConverter.ToUInt32(Packet, 53); + UInt32 SpellRefresh = BitConverter.ToUInt32(Packet, 57); + UInt16 Classes = BitConverter.ToUInt16(Packet, 61); + UInt16 Berserker = BitConverter.ToUInt16(Packet, 63); + UInt32 MaxLevel = BitConverter.ToUInt32(Packet, 65); + UInt32 LastID = BitConverter.ToUInt32(Packet, 69); + UInt32 NextID = BitConverter.ToUInt32(Packet, 73); + UInt32 Cost2 = BitConverter.ToUInt32(Packet, 77); + UInt32 AAExpansion = BitConverter.ToUInt32(Packet, 88); + UInt32 SpecialCategory = BitConverter.ToUInt32(Packet, 92); + UInt32 TotalAbilities = BitConverter.ToUInt32(Packet, 100); + + OutputFile.WriteLine(String.Format("AAID: {0}", AAID)); + OutputFile.WriteLine(" Unknown004:\t" + Unknown004); + OutputFile.WriteLine(" HotkeySID:\t" + HotKeySID); + OutputFile.WriteLine(" HotkeySID2:\t" + HotKeySID2); + OutputFile.WriteLine(" TitleSID:\t" + TitleSID); + OutputFile.WriteLine(" DescSID:\t" + DescSID); + OutputFile.WriteLine(" ClassType:\t" + ClassType); + OutputFile.WriteLine(" Cost:\t\t" + Cost); + OutputFile.WriteLine(" Seq:\t\t" + Seq); + OutputFile.WriteLine(" CurrentLevel:\t" + CurrentLevel); + OutputFile.WriteLine(" PrereqSkill:\t" + PrereqSkill); + OutputFile.WriteLine(" PrereqMinPt:\t" + PrereqMinpoints); + OutputFile.WriteLine(" Type:\t\t" + Type); + OutputFile.WriteLine(" SpellID:\t" + SpellID); + OutputFile.WriteLine(" SpellType:\t" + SpellType); + OutputFile.WriteLine(" SpellRefresh:\t" + SpellRefresh); + OutputFile.WriteLine(" Classes:\t" + Classes); + OutputFile.WriteLine(" Berserker:\t" + Berserker); + OutputFile.WriteLine(" MaxLevel:\t" + MaxLevel); + OutputFile.WriteLine(" LastID:\t" + LastID); + OutputFile.WriteLine(" NextID:\t" + NextID); + OutputFile.WriteLine(" Cost2:\t\t" + Cost2); + OutputFile.WriteLine(" AAExpansion:\t" + AAExpansion); + OutputFile.WriteLine(" SpecialCat:\t" + SpecialCategory); + OutputFile.WriteLine(""); + OutputFile.WriteLine(" TotalAbilities:\t" + TotalAbilities); + OutputFile.WriteLine(""); + + for (int i = 0; i < TotalAbilities; ++i) + { + UInt32 Ability = BitConverter.ToUInt32(Packet, 104 + (i * 16)); + Int32 Base1 = BitConverter.ToInt32(Packet, 108 + (i * 16)); + Int32 Base2 = BitConverter.ToInt32(Packet, 112 + (i * 16)); + UInt32 Slot = BitConverter.ToUInt32(Packet, 116 + (i * 16)); + + OutputFile.WriteLine(String.Format(" Ability:\t{0}\tBase1:\t{1}\tBase2:\t{2}\tSlot:\t{3}", Ability, Base1, Base2, Slot)); + + } + OutputFile.WriteLine(""); + + } + + OutputFile.Close(); + + return true; + } + } +} diff --git a/utils/EQExtractor2/EQExtractor2/PatchMay12-2011.cs b/utils/EQExtractor2/EQExtractor2/PatchMay12-2011.cs new file mode 100644 index 000000000..97f0ac2ac --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchMay12-2011.cs @@ -0,0 +1,296 @@ +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchMay122011Decoder : PatchMarch152011Decoder + { + public PatchMay122011Decoder() + { + Version = "EQ Client Build Date May 12 2011."; + + PatchConfFileName = "patch_May12-2011.conf"; + + ExpectedPPLength = 28536; + + PPZoneIDOffset = 21204; + } + + override public PositionUpdate Decode_OP_NPCMoveUpdate(byte[] UpdatePacket) + { + PositionUpdate PosUpdate = new PositionUpdate(); + + BitStream bs = new BitStream(UpdatePacket, (uint)UpdatePacket.Length); + + PosUpdate.SpawnID = bs.readUInt(16); + + UInt32 Unknown = bs.readUInt(16); + + UInt32 VFlags = bs.readUInt(6); + + PosUpdate.p.y = (float)bs.readInt(19) / (float)(1 << 3); + + PosUpdate.p.x = (float)bs.readInt(19) / (float)(1 << 3); + + PosUpdate.p.z = (float)bs.readInt(19) / (float)(1 << 3); + + PosUpdate.p.heading = (float)bs.readInt(12) / (float)(1 << 3); + + PosUpdate.HighRes = true; + + return PosUpdate; + } + + override public PositionUpdate Decode_OP_MobUpdate(byte[] MobUpdatePacket) + { + PositionUpdate PosUpdate = new PositionUpdate(); + + ByteStream Buffer = new ByteStream(MobUpdatePacket); + + PosUpdate.SpawnID = Buffer.ReadUInt16(); + + Buffer.SkipBytes(2); + + UInt32 Word1 = Buffer.ReadUInt32(); + + UInt32 Word2 = Buffer.ReadUInt32(); + + UInt16 Word3 = Buffer.ReadUInt16(); + + PosUpdate.p.y = Utils.EQ19ToFloat((Int32)(Word1 & 0x7FFFF)); + + // Z is in the top 13 bits of Word1 and the bottom 6 of Word2 + + UInt32 ZPart1 = Word1 >> 19; // ZPart1 now has low order bits of Z in bottom 13 bits + UInt32 ZPart2 = Word2 & 0x3F; // ZPart2 now has high order bits of Z in bottom 6 bits + + ZPart2 = ZPart2 << 13; + + PosUpdate.p.z = Utils.EQ19ToFloat((Int32)(ZPart1 | ZPart2)); + + PosUpdate.p.x = Utils.EQ19ToFloat((Int32)(Word2 >> 6) & 0x7FFFF); + + PosUpdate.p.heading = Utils.EQ19ToFloat((Int32)(Word3 & 0xFFF)); + + PosUpdate.HighRes = false; + + return PosUpdate; + } + + override public List GetZonePointList() + { + List ZonePointList = new List(); + + List ZonePointPackets = GetPacketsOfType("OP_SendZonepoints", PacketDirection.ServerToClient); + + if (ZonePointPackets.Count < 1) + { + return ZonePointList; + } + + // Assume there is only 1 packet and process the first one. + + ByteStream Buffer = new ByteStream(ZonePointPackets[0]); + + UInt32 Entries = Buffer.ReadUInt32(); + + if (Entries == 0) + return ZonePointList; + + float x, y, z, Heading; + + UInt32 Number; + + UInt16 ZoneID, Instance; + + ZonePointList = new List(); + + for (int i = 0; i < Entries; ++i) + { + Number = Buffer.ReadUInt32(); + + y = Buffer.ReadSingle(); + + x = Buffer.ReadSingle(); + + z = Buffer.ReadSingle(); + + Heading = Buffer.ReadSingle(); + + if (Heading != 999) + Heading = Heading / 2; + + ZoneID = Buffer.ReadUInt16(); + + Instance = Buffer.ReadUInt16(); + + Buffer.SkipBytes(8); // Skip the last UInt32 + + ZonePoint NewZonePoint = new ZonePoint(Number, ZoneID, Instance, x, y, z, x, y, z, Heading, ZoneID); + + ZonePointList.Add(NewZonePoint); + } + + return ZonePointList; + } + override public bool DumpAAs(string FileName) + { + List AAPackets = GetPacketsOfType("OP_SendAATable", PacketDirection.ServerToClient); + + + if (AAPackets.Count < 1) + return false; + + StreamWriter OutputFile; + + try + { + OutputFile = new StreamWriter(FileName); + } + catch + { + return false; + + } + + OutputFile.WriteLine("-- There are " + AAPackets.Count + " OP_SendAATable packets."); + OutputFile.WriteLine(""); + + foreach (byte[] Packet in AAPackets) + { + UInt32 AAID = BitConverter.ToUInt32(Packet, 0); + UInt32 Unknown004 = BitConverter.ToUInt32(Packet, 4); + UInt32 HotKeySID = BitConverter.ToUInt32(Packet, 5); + UInt32 HotKeySID2 = BitConverter.ToUInt32(Packet, 9); + UInt32 TitleSID = BitConverter.ToUInt32(Packet, 13); + UInt32 DescSID = BitConverter.ToUInt32(Packet, 17); + UInt32 ClassType = BitConverter.ToUInt32(Packet, 21); + UInt32 Cost = BitConverter.ToUInt32(Packet, 25); + UInt32 Seq = BitConverter.ToUInt32(Packet, 29); + UInt32 CurrentLevel = BitConverter.ToUInt32(Packet, 33); + UInt32 Unknown037 = BitConverter.ToUInt32(Packet, 37); + UInt32 PrereqSkill = BitConverter.ToUInt32(Packet, 41); + UInt32 PrereqMinpoints = BitConverter.ToUInt32(Packet, 45); + UInt32 Type = BitConverter.ToUInt32(Packet, 49); + UInt32 SpellID = BitConverter.ToUInt32(Packet, 53); + UInt32 Unknown057 = BitConverter.ToUInt32(Packet, 57); + UInt32 SpellType = BitConverter.ToUInt32(Packet, 61); + UInt32 SpellRefresh = BitConverter.ToUInt32(Packet, 65); + UInt16 Classes = BitConverter.ToUInt16(Packet, 69); + UInt16 Berserker = BitConverter.ToUInt16(Packet, 71); + UInt32 MaxLevel = BitConverter.ToUInt32(Packet, 73); + UInt32 LastID = BitConverter.ToUInt32(Packet, 77); + UInt32 NextID = BitConverter.ToUInt32(Packet, 81); + UInt32 Cost2 = BitConverter.ToUInt32(Packet, 85); + UInt32 AAExpansion = BitConverter.ToUInt32(Packet, 96); + UInt32 SpecialCategory = BitConverter.ToUInt32(Packet, 100); + UInt32 TotalAbilities = BitConverter.ToUInt32(Packet, 108); + + OutputFile.WriteLine(String.Format("AAID: {0}", AAID)); + OutputFile.WriteLine(" Unknown004:\t" + Unknown004); + OutputFile.WriteLine(" HotkeySID:\t" + HotKeySID); + OutputFile.WriteLine(" HotkeySID2:\t" + HotKeySID2); + OutputFile.WriteLine(" TitleSID:\t" + TitleSID); + OutputFile.WriteLine(" DescSID:\t" + DescSID); + OutputFile.WriteLine(" ClassType:\t" + ClassType); + OutputFile.WriteLine(" Cost:\t\t" + Cost); + OutputFile.WriteLine(" Seq:\t\t" + Seq); + OutputFile.WriteLine(" CurrentLevel:\t" + CurrentLevel); + OutputFile.WriteLine(" Unknown037:\t" + Unknown037); + OutputFile.WriteLine(" PrereqSkill:\t" + PrereqSkill); + OutputFile.WriteLine(" PrereqMinPt:\t" + PrereqMinpoints); + OutputFile.WriteLine(" Type:\t\t" + Type); + OutputFile.WriteLine(" SpellID:\t" + SpellID); + OutputFile.WriteLine(" Unknown057:\t" + Unknown057); + OutputFile.WriteLine(" SpellType:\t" + SpellType); + OutputFile.WriteLine(" SpellRefresh:\t" + SpellRefresh); + OutputFile.WriteLine(" Classes:\t" + Classes); + OutputFile.WriteLine(" Berserker:\t" + Berserker); + OutputFile.WriteLine(" MaxLevel:\t" + MaxLevel); + OutputFile.WriteLine(" LastID:\t" + LastID); + OutputFile.WriteLine(" NextID:\t" + NextID); + OutputFile.WriteLine(" Cost2:\t\t" + Cost2); + OutputFile.WriteLine(" AAExpansion:\t" + AAExpansion); + OutputFile.WriteLine(" SpecialCat:\t" + SpecialCategory); + OutputFile.WriteLine(""); + OutputFile.WriteLine(" TotalAbilities:\t" + TotalAbilities); + OutputFile.WriteLine(""); + + for (int i = 0; i < TotalAbilities; ++i) + { + UInt32 Ability = BitConverter.ToUInt32(Packet, 112 + (i * 16)); + Int32 Base1 = BitConverter.ToInt32(Packet, 116 + (i * 16)); + Int32 Base2 = BitConverter.ToInt32(Packet, 120 + (i * 16)); + UInt32 Slot = BitConverter.ToUInt32(Packet, 124 + (i * 16)); + + OutputFile.WriteLine(String.Format(" Ability:\t{0}\tBase1:\t{1}\tBase2:\t{2}\tSlot:\t{3}", Ability, Base1, Base2, Slot)); + + } + OutputFile.WriteLine(""); + + } + + OutputFile.Close(); + + return true; + } + + public override void RegisterExplorers() + { + //base.RegisterExplorers(); + + //OpManager.RegisterExplorer("OP_SpawnDoor", ExploreSpawnDoor); + + } + + public void ExploreSpawnDoor(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + uint DoorCount = Buffer.Length() / 96; + + OutputStream.WriteLine("Door Count: {0}", DoorCount); + + for (int d = 0; d < DoorCount; ++d) + { + string DoorName = Buffer.ReadFixedLengthString(32, false); + + + float YPos = Buffer.ReadSingle(); + + float XPos = Buffer.ReadSingle(); + + float ZPos = Buffer.ReadSingle(); + + float Heading = Buffer.ReadSingle(); + + UInt32 Incline = Buffer.ReadUInt32(); + + Int32 Size = Buffer.ReadInt32(); + + Buffer.SkipBytes(4); // Skip Unknown + + Byte DoorID = Buffer.ReadByte(); + + Byte OpenType = Buffer.ReadByte(); + + Byte StateAtSpawn = Buffer.ReadByte(); + + Byte InvertState = Buffer.ReadByte(); + + Int32 DoorParam = Buffer.ReadInt32(); + + OutputStream.WriteLine(" Name: {0} ID: {1} OT: {2} SAS: {3} IS: {4} DP: {5}", + DoorName, DoorID, OpenType, StateAtSpawn, InvertState, DoorParam); + + // Skip past the trailing unknowns in the door struct, moving to the next door in the packet. + + Buffer.SkipBytes(28); + } + + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchMay24-2011.cs b/utils/EQExtractor2/EQExtractor2/PatchMay24-2011.cs new file mode 100644 index 000000000..280db1333 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchMay24-2011.cs @@ -0,0 +1,22 @@ +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchMay242011Decoder : PatchMay122011Decoder + { + public PatchMay242011Decoder() + { + Version = "EQ Client Build Date May 24 2011."; + + ExpectedPPLength = 28856; + + PPZoneIDOffset = 21524; + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchNov17-2011.cs b/utils/EQExtractor2/EQExtractor2/PatchNov17-2011.cs new file mode 100644 index 000000000..01218b638 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchNov17-2011.cs @@ -0,0 +1,456 @@ +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchNov172011Decoder : PatchAug042011Decoder + { + public PatchNov172011Decoder() + { + Version = "EQ Client Build Date November 17 2011."; + + ExpectedPPLength = 29560; + + PPZoneIDOffset = 22228; + + PatchConfFileName = "patch_Nov17-2011.conf"; + } + + override public List GetSpawns() + { + List ZoneSpawns = new List(); + + List SpawnPackets = GetPacketsOfType("OP_ZoneEntry", PacketDirection.ServerToClient); + + foreach (byte[] SpawnPacket in SpawnPackets) + { + ZoneEntryStruct NewSpawn = new ZoneEntryStruct(); + + ByteStream Buffer = new ByteStream(SpawnPacket); + + NewSpawn.SpawnName = Buffer.ReadString(true); + + NewSpawn.SpawnName = Utils.MakeCleanName(NewSpawn.SpawnName); + + NewSpawn.SpawnID = Buffer.ReadUInt32(); + + NewSpawn.Level = Buffer.ReadByte(); + + float UnkSize = Buffer.ReadSingle(); + + NewSpawn.IsNPC = Buffer.ReadByte(); + + UInt32 Bitfield = Buffer.ReadUInt32(); + + NewSpawn.Showname = (Bitfield >> 28) & 1; + NewSpawn.TargetableWithHotkey = (Bitfield >> 27) & 1; + NewSpawn.Targetable = (Bitfield >> 26) & 1; + + NewSpawn.ShowHelm = (Bitfield >> 24) & 1; + NewSpawn.Gender = (Bitfield >> 20) & 3; + + NewSpawn.Padding5 = (Bitfield >> 4) & 1; + NewSpawn.Padding7 = (Bitfield >> 6) & 2047; + NewSpawn.Padding26 = (Bitfield >> 25) & 1; + + Byte OtherData = Buffer.ReadByte(); + + Buffer.SkipBytes(8); // Skip 8 unknown bytes + + NewSpawn.DestructableString1 = ""; + NewSpawn.DestructableString2 = ""; + NewSpawn.DestructableString3 = ""; + + if ((NewSpawn.IsNPC == 1) && ((OtherData & 3) > 0)) + { + // Destructable Objects. Not handled yet + // + //SQLOut(String.Format("-- OBJECT FOUND SpawnID {0}", SpawnID.ToString("x"))); + + NewSpawn.DestructableString1 = Buffer.ReadString(false); + + NewSpawn.DestructableString2 = Buffer.ReadString(false); + + NewSpawn.DestructableString3 = Buffer.ReadString(false); + + NewSpawn.DestructableUnk1 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk2 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID1 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID2 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID3 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID4 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk3 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk4 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk5 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk6 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk7 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk8 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk9 = Buffer.ReadUInt32(); + + NewSpawn.DestructableByte = Buffer.ReadByte(); + + //SQLOut(String.Format("-- DES: {0,8:x} {1,8:x} {2,8:d} {3,8:d} {4,8:d} {5,8:d} {6,8:x} {7,8:x} {8,8:x} {9,8:x} {10,8:x} {11,8:x} {12,8:x} {13,2:x} {14} {15} {16}", + // DestructableUnk1, DestructableUnk2, DestructableID1, DestructableID2, DestructableID3, DestructableID4, + // DestructableUnk3, DestructableUnk4, DestructableUnk5, DestructableUnk6, DestructableUnk7, DestructableUnk8, + // DestructableUnk9, DestructableByte, DestructableString1, DestructableString2, DestructableString3)); + } + + NewSpawn.Size = Buffer.ReadSingle(); + + NewSpawn.Face = Buffer.ReadByte(); + + NewSpawn.WalkSpeed = Buffer.ReadSingle(); + + NewSpawn.RunSpeed = Buffer.ReadSingle(); + + NewSpawn.Race = Buffer.ReadUInt32(); + + NewSpawn.PropCount = Buffer.ReadByte(); + + NewSpawn.BodyType = 0; + + if (NewSpawn.PropCount >= 1) + { + NewSpawn.BodyType = Buffer.ReadUInt32(); + + for (int j = 1; j < NewSpawn.PropCount; ++j) + Buffer.SkipBytes(4); + } + + Buffer.SkipBytes(1); // Skip HP % + + NewSpawn.HairColor = Buffer.ReadByte(); + NewSpawn.BeardColor = Buffer.ReadByte(); + NewSpawn.EyeColor1 = Buffer.ReadByte(); + NewSpawn.EyeColor2 = Buffer.ReadByte(); + NewSpawn.HairStyle = Buffer.ReadByte(); + NewSpawn.Beard = Buffer.ReadByte(); + + NewSpawn.DrakkinHeritage = Buffer.ReadUInt32(); + + NewSpawn.DrakkinTattoo = Buffer.ReadUInt32(); + + NewSpawn.DrakkinDetails = Buffer.ReadUInt32(); + + Buffer.SkipBytes(1); // Skip Holding + + NewSpawn.Deity = Buffer.ReadUInt32(); + + Buffer.SkipBytes(8); // Skip GuildID and GuildRank + + NewSpawn.Class = Buffer.ReadByte(); + + Buffer.SkipBytes(4); // Skip PVP, Standstate, Light, Flymode + + NewSpawn.EquipChest2 = Buffer.ReadByte(); + + bool UseWorn = (NewSpawn.EquipChest2 == 255); + + Buffer.SkipBytes(2); // 2 Unknown bytes; + + NewSpawn.Helm = Buffer.ReadByte(); + + NewSpawn.LastName = Buffer.ReadString(false); + + Buffer.SkipBytes(5); // AATitle + unknown byte + + NewSpawn.PetOwnerID = Buffer.ReadUInt32(); + + Buffer.SkipBytes(26); // Unknown byte + 6 unknown uint32 + + UInt32 Position1 = Buffer.ReadUInt32(); + + UInt32 Position2 = Buffer.ReadUInt32(); + + UInt32 Position3 = Buffer.ReadUInt32(); + + UInt32 Position4 = Buffer.ReadUInt32(); + + UInt32 Position5 = Buffer.ReadUInt32(); + + NewSpawn.YPos = Utils.EQ19ToFloat((Int32)(Position3 & 0x7FFFF)); + + NewSpawn.Heading = Utils.EQ19ToFloat((Int32)(Position4 & 0xFFF)); + + NewSpawn.XPos = Utils.EQ19ToFloat((Int32)(Position4 >> 12) & 0x7FFFF); + + NewSpawn.ZPos = Utils.EQ19ToFloat((Int32)(Position5 & 0x7FFFF)); + + for (int ColourSlot = 0; ColourSlot < 9; ++ColourSlot) + NewSpawn.SlotColour[ColourSlot] = Buffer.ReadUInt32(); + + NewSpawn.MeleeTexture1 = 0; + NewSpawn.MeleeTexture2 = 0; + + if (NPCType.IsPlayableRace(NewSpawn.Race)) + { + for (int i = 0; i < 9; ++i) + { + NewSpawn.Equipment[i] = Buffer.ReadUInt32(); + + UInt32 Equip1 = Buffer.ReadUInt32(); + + UInt32 Equip0 = Buffer.ReadUInt32(); + } + + if (NewSpawn.Equipment[Constants.MATERIAL_CHEST] > 0) + { + NewSpawn.EquipChest2 = (byte)NewSpawn.Equipment[Constants.MATERIAL_CHEST]; + + } + + NewSpawn.ArmorTintRed = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 16) & 0xff); + + NewSpawn.ArmorTintGreen = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 8) & 0xff); + + NewSpawn.ArmorTintBlue = (byte)(NewSpawn.SlotColour[Constants.MATERIAL_CHEST] & 0xff); + + if (NewSpawn.Equipment[Constants.MATERIAL_PRIMARY] > 0) + NewSpawn.MeleeTexture1 = NewSpawn.Equipment[Constants.MATERIAL_PRIMARY]; + + if (NewSpawn.Equipment[Constants.MATERIAL_SECONDARY] > 0) + NewSpawn.MeleeTexture2 = NewSpawn.Equipment[Constants.MATERIAL_SECONDARY]; + + if (UseWorn) + NewSpawn.Helm = (byte)NewSpawn.Equipment[Constants.MATERIAL_HEAD]; + else + NewSpawn.Helm = 0; + + } + else + { + // Non playable race + NewSpawn.MeleeTexture1 = NewSpawn.SlotColour[3]; + NewSpawn.MeleeTexture2 = NewSpawn.SlotColour[6]; + } + + if (NewSpawn.EquipChest2 == 255) + NewSpawn.EquipChest2 = 0; + + if (NewSpawn.Helm == 255) + NewSpawn.Helm = 0; + + if ((OtherData & 4) > 0) + { + NewSpawn.Title = Buffer.ReadString(false); + } + + if ((OtherData & 8) > 0) + { + NewSpawn.Suffix = Buffer.ReadString(false); + } + + // unknowns + Buffer.SkipBytes(8); + + NewSpawn.IsMercenary = Buffer.ReadByte(); + + ZoneSpawns.Add(NewSpawn); + } + + return ZoneSpawns; + } + + public override void RegisterExplorers() + { + //base.RegisterExplorers(); + + //OpManager.RegisterExplorer("OP_NPCMoveUpdate", ExploreNPCMoveUpdate); + //OpManager.RegisterExplorer("OP_MobUpdate", ExploreMobUpdate); + + } + + public void ExploreNPCMoveUpdate(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + BitStream bs = new BitStream(Buffer.Buffer, Buffer.Length()); + + UInt32 SpawnID = bs.readUInt(16); + + UInt32 Unknown = bs.readUInt(16); + + UInt32 VFlags = bs.readUInt(6); + + float y = (float)bs.readInt(19) / (float)(1 << 3); + + float x = (float)bs.readInt(19) / (float)(1 << 3); + + float z = (float)bs.readInt(19) / (float)(1 << 3); + + float heading = (float)bs.readInt(12) / (float)(1 << 3); + + OutputStream.WriteLine("Spawn: {0} Flags: {1}, XYZ: {2}, {3}, {4}, Heading: {5}", SpawnID, VFlags, x, y, z, heading); + } + + public void ExploreMobUpdate(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 SpawnID = Buffer.ReadUInt16(); + + Buffer.SkipBytes(2); + + UInt32 Word1 = Buffer.ReadUInt32(); + + UInt32 Word2 = Buffer.ReadUInt32(); + + UInt16 Word3 = Buffer.ReadUInt16(); + + float y = Utils.EQ19ToFloat((Int32)(Word1 & 0x7FFFF)); + + // Z is in the top 13 bits of Word1 and the bottom 6 of Word2 + + UInt32 ZPart1 = Word1 >> 19; // ZPart1 now has low order bits of Z in bottom 13 bits + UInt32 ZPart2 = Word2 & 0x3F; // ZPart2 now has high order bits of Z in bottom 6 bits + + ZPart2 = ZPart2 << 13; + + float z = Utils.EQ19ToFloat((Int32)(ZPart1 | ZPart2)); + + float x = Utils.EQ19ToFloat((Int32)(Word2 >> 6) & 0x7FFFF); + + float heading = Utils.EQ19ToFloat((Int32)(Word3 & 0xFFF)); + + OutputStream.WriteLine("Spawn: {0} XYZ: {1}, {2}, {3}, Heading: {4}", SpawnID, x, y, z, heading); + } + + override public bool DumpAAs(string FileName) + { + List AAPackets = GetPacketsOfType("OP_SendAATable", PacketDirection.ServerToClient); + + + if (AAPackets.Count < 1) + return false; + + StreamWriter OutputFile; + + try + { + OutputFile = new StreamWriter(FileName); + } + catch + { + return false; + + } + + OutputFile.WriteLine("-- There are " + AAPackets.Count + " OP_SendAATable packets."); + OutputFile.WriteLine(""); + + foreach (byte[] Packet in AAPackets) + { + ByteStream Buffer = new ByteStream(Packet); + + UInt32 AAID = Buffer.ReadUInt32(); + byte Unknown004 = Buffer.ReadByte(); + UInt32 HotKeySID = Buffer.ReadUInt32(); + UInt32 HotKeySID2 = Buffer.ReadUInt32(); + UInt32 TitleSID = Buffer.ReadUInt32(); + UInt32 DescSID = Buffer.ReadUInt32(); + UInt32 ClassType = Buffer.ReadUInt32(); + UInt32 Cost = Buffer.ReadUInt32(); + UInt32 Seq = Buffer.ReadUInt32(); + UInt32 CurrentLevel = Buffer.ReadUInt32(); + + UInt32 PreReqSkillCount = Buffer.ReadUInt32(); + UInt32 [] PreReqSkills = new UInt32[PreReqSkillCount]; + + for(int i = 0; i < PreReqSkillCount; ++i) + PreReqSkills[i] = Buffer.ReadUInt32(); // The SEQ value of the AA packet containing the pre-req AA + + UInt32 PreReqMinPointCount = Buffer.ReadUInt32(); + UInt32[] PreReqMinPoints = new UInt32[PreReqMinPointCount]; + + for (int i = 0; i < PreReqMinPointCount; ++i) + PreReqMinPoints[i] = Buffer.ReadUInt32(); + + UInt32 Type = Buffer.ReadUInt32(); + UInt32 SpellID = Buffer.ReadUInt32(); + UInt32 Unknown057 = Buffer.ReadUInt32(); + UInt32 SpellType = Buffer.ReadUInt32(); + UInt32 SpellRefresh = Buffer.ReadUInt32(); + UInt16 Classes = Buffer.ReadUInt16(); + UInt16 Berserker = Buffer.ReadUInt16(); + UInt32 MaxLevel = Buffer.ReadUInt32(); + UInt32 LastID = Buffer.ReadUInt32(); + UInt32 NextID = Buffer.ReadUInt32(); + UInt32 Cost2 = Buffer.ReadUInt32(); + Buffer.SkipBytes(7); + UInt32 AAExpansion = Buffer.ReadUInt32(); + UInt32 SpecialCategory = Buffer.ReadUInt32(); + Buffer.SkipBytes(4); + UInt32 TotalAbilities = Buffer.ReadUInt32(); + + OutputFile.WriteLine(String.Format("AAID: {0}", AAID)); + OutputFile.WriteLine(" Unknown004:\t" + Unknown004); + OutputFile.WriteLine(" HotkeySID:\t" + HotKeySID); + OutputFile.WriteLine(" HotkeySID2:\t" + HotKeySID2); + OutputFile.WriteLine(" TitleSID:\t" + TitleSID); + OutputFile.WriteLine(" DescSID:\t" + DescSID); + OutputFile.WriteLine(" ClassType:\t" + ClassType); + OutputFile.WriteLine(" Cost:\t\t" + Cost); + OutputFile.WriteLine(" Seq:\t\t" + Seq); + OutputFile.WriteLine(" CurrentLevel:\t" + CurrentLevel); + OutputFile.Write(" PreReqSkills (SEQs):\t"); + for (int i = 0; i < PreReqSkillCount; ++i) + OutputFile.Write("{0} ", PreReqSkills[i]); + + OutputFile.WriteLine(""); + + OutputFile.Write(" PreReqSkills MinPoints:\t"); + for (int i = 0; i < PreReqMinPointCount; ++i) + OutputFile.Write("{0} ", PreReqMinPoints[i]); + + OutputFile.WriteLine(""); + + OutputFile.WriteLine(" Type:\t\t" + Type); + OutputFile.WriteLine(" SpellID:\t" + SpellID); + OutputFile.WriteLine(" Unknown057:\t" + Unknown057); + OutputFile.WriteLine(" SpellType:\t" + SpellType); + OutputFile.WriteLine(" SpellRefresh:\t" + SpellRefresh); + OutputFile.WriteLine(" Classes:\t" + Classes); + OutputFile.WriteLine(" Berserker:\t" + Berserker); + OutputFile.WriteLine(" MaxLevel:\t" + MaxLevel); + OutputFile.WriteLine(" LastID:\t" + LastID); + OutputFile.WriteLine(" NextID:\t" + NextID); + OutputFile.WriteLine(" Cost2:\t\t" + Cost2); + OutputFile.WriteLine(" AAExpansion:\t" + AAExpansion); + OutputFile.WriteLine(" SpecialCat:\t" + SpecialCategory); + OutputFile.WriteLine(""); + OutputFile.WriteLine(" TotalAbilities:\t" + TotalAbilities); + OutputFile.WriteLine(""); + + for (int i = 0; i < TotalAbilities; ++i) + { + UInt32 Ability = Buffer.ReadUInt32(); + Int32 Base1 = Buffer.ReadInt32(); + Int32 Base2 = Buffer.ReadInt32(); + UInt32 Slot = Buffer.ReadUInt32(); + + OutputFile.WriteLine(String.Format(" Ability:\t{0}\tBase1:\t{1}\tBase2:\t{2}\tSlot:\t{3}", Ability, Base1, Base2, Slot)); + + } + OutputFile.WriteLine(""); + + } + + OutputFile.Close(); + + return true; + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchOct20-2010.cs b/utils/EQExtractor2/EQExtractor2/PatchOct20-2010.cs new file mode 100644 index 000000000..03c063a89 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchOct20-2010.cs @@ -0,0 +1,22 @@ +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchOct202010Decoder : PatchTestSep222010Decoder + { + public PatchOct202010Decoder() + { + Version = "EQ Client Build Date October 20 2010."; + + ExpectedPPLength = 27816; + + PPZoneIDOffset = 20484; + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchSoD.cs b/utils/EQExtractor2/EQExtractor2/PatchSoD.cs new file mode 100644 index 000000000..70c06c4f1 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchSoD.cs @@ -0,0 +1,991 @@ +// +// Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net). Distributed under GPL version 2. +// +// +// +// IMPORTANT NOTE: This decoder for SoD was developed purely as a cursory test of the multi-patch support and is not guaranteed +// to be 100% correct. +// +// +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchSoD : PatchSpecficDecoder + { + public PatchSoD() + { + Version = "Seeds of Destruction"; + + PatchConfFileName = "patch_SoD.conf"; + + ExpectedPPLength = 23488; + + PPZoneIDOffset = 16452; + } + + override public bool Init(string ConfDirectory, ref string ErrorMessage) + { + OpManager = new OpCodeManager(); + + if (!OpManager.Init(ConfDirectory + "\\" + PatchConfFileName, ref ErrorMessage)) + return false; + + RegisterExplorers(); + + return true; + } + + override public IdentificationStatus Identify(int OpCode, int Size, PacketDirection Direction) + { + if ((OpCode == OpManager.OpCodeNameToNumber("OP_ZoneEntry")) && (Direction == PacketDirection.ClientToServer)) + return IdentificationStatus.Tentative; + + if ((OpCode == OpManager.OpCodeNameToNumber("OP_PlayerProfile")) && (Direction == PacketDirection.ServerToClient) && + (Size == ExpectedPPLength)) + return IdentificationStatus.Yes; + + + return IdentificationStatus.No; + } + + override public int VerifyPlayerProfile() + { + List PlayerProfilePacket = GetPacketsOfType("OP_PlayerProfile", PacketDirection.ServerToClient); + + if (PlayerProfilePacket.Count == 0) + { + return 0; + } + else + { + if (PlayerProfilePacket[0].Length != ExpectedPPLength) + { + return 0; + } + } + + return ExpectedPPLength; + } + + override public UInt16 GetZoneNumber() + { + List PlayerProfilePacket = GetPacketsOfType("OP_PlayerProfile", PacketDirection.ServerToClient); + + if (PlayerProfilePacket.Count == 0) + { + return 0; + } + else + { + if (PlayerProfilePacket[0].Length != ExpectedPPLength) + { + return 0; + } + } + + return BitConverter.ToUInt16(PlayerProfilePacket[0], PPZoneIDOffset); + } + + override public List GetDoors() + { + List DoorList = new List(); + + List SpawnDoorPacket = GetPacketsOfType("OP_SpawnDoor", PacketDirection.ServerToClient); + + if ((SpawnDoorPacket.Count == 0) || (SpawnDoorPacket[0].Length == 0)) + return DoorList; + + int DoorCount = SpawnDoorPacket[0].Length / 92; + + ByteStream Buffer = new ByteStream(SpawnDoorPacket[0]); + + for (int d = 0; d < DoorCount; ++d) + { + string DoorName = Buffer.ReadFixedLengthString(32, false); + + float YPos = Buffer.ReadSingle(); + + float XPos = Buffer.ReadSingle(); + + float ZPos = Buffer.ReadSingle(); + + float Heading = Buffer.ReadSingle(); + + UInt32 Incline = Buffer.ReadUInt32(); + + Int32 Size = Buffer.ReadInt32(); + + Buffer.SkipBytes(4); // Skip Unknown + + Byte DoorID = Buffer.ReadByte(); + + Byte OpenType = Buffer.ReadByte(); + + Byte StateAtSpawn = Buffer.ReadByte(); + + Byte InvertState = Buffer.ReadByte(); + + Int32 DoorParam = Buffer.ReadInt32(); + + // Skip past the trailing unknowns in the door struct, moving to the next door in the packet. + + Buffer.SkipBytes(24); + + string DestZone = "NONE"; + + Door NewDoor = new Door(DoorName, YPos, XPos, ZPos, Heading, Incline, Size, DoorID, OpenType, StateAtSpawn, InvertState, + DoorParam, DestZone, 0, 0, 0, 0); + + DoorList.Add(NewDoor); + + } + return DoorList; + } + + override public MerchantManager GetMerchantData(NPCSpawnList NPCSL) + { + List PacketList = Packets.PacketList; + + UInt32 OP_ShopRequest = OpManager.OpCodeNameToNumber("OP_ShopRequest"); + + UInt32 OP_ShopEnd = OpManager.OpCodeNameToNumber("OP_ShopEnd"); + + UInt32 OP_ItemPacket = OpManager.OpCodeNameToNumber("OP_ItemPacket"); + + MerchantManager mm = new MerchantManager(); + + for (int i = 0; i < PacketList.Count; ++i) + { + EQApplicationPacket p = PacketList[i]; + + if ((p.Direction == PacketDirection.ServerToClient) && (p.OpCode == OP_ShopRequest)) + { + ByteStream Buffer = new ByteStream(p.Buffer); + + UInt32 MerchantSpawnID = Buffer.ReadUInt32(); + + NPCSpawn npc = NPCSL.GetNPC(MerchantSpawnID); + + UInt32 NPCTypeID; + + if (npc != null) + NPCTypeID = npc.NPCTypeID; + else + NPCTypeID = 0; + + mm.AddMerchant(MerchantSpawnID); + + for (int j = i + 1; j < PacketList.Count; ++j) + { + p = PacketList[j]; + + if (p.OpCode == OP_ShopEnd) + break; + + + if (p.OpCode == OP_ItemPacket) + { + Buffer = new ByteStream(p.Buffer); + + UInt32 StackSize = Buffer.ReadUInt32(); + + Buffer.SkipBytes(4); + + UInt32 Slot = Buffer.ReadUInt32(); + + UInt32 MerchantSlot = Buffer.ReadUInt32(); + + UInt32 Price = Buffer.ReadUInt32(); + + Int32 Quantity = Buffer.ReadInt32(); + + Buffer.SetPosition(68); // Point to item name + + string ItemName = Buffer.ReadString(true); + + string Lore = Buffer.ReadString(true); + + string IDFile = Buffer.ReadString(true); + + + UInt32 ItemID = Buffer.ReadUInt32(); + + //if (Quantity == -1) + mm.AddMerchantItem(MerchantSpawnID, ItemID, ItemName, MerchantSlot, Quantity); + } + } + } + } + + return mm; + } + + override public List GetZonePointList() + { + List ZonePointList = new List(); + + List ZonePointPackets = GetPacketsOfType("OP_SendZonepoints", PacketDirection.ServerToClient); + + if (ZonePointPackets.Count < 1) + { + ZonePointList = null; + + return ZonePointList; + } + + // Assume there is only 1 packet and process the first one. + + ByteStream Buffer = new ByteStream(ZonePointPackets[0]); + + UInt32 Entries = Buffer.ReadUInt32(); + + if (Entries == 0) + return ZonePointList; + + float x, y, z, Heading; + + UInt32 Number; + + UInt16 ZoneID, Instance; + + ZonePointList = new List(); + + for (int i = 0; i < Entries; ++i) + { + Number = Buffer.ReadUInt32(); + + y = Buffer.ReadSingle(); + + x = Buffer.ReadSingle(); + + z = Buffer.ReadSingle(); + + Heading = Buffer.ReadSingle(); + + if (Heading != 999) + Heading = Heading / 2; + + ZoneID = Buffer.ReadUInt16(); + + Instance = Buffer.ReadUInt16(); + + //Buffer.SkipBytes(4); // Skip the last UInt32 + + ZonePoint NewZonePoint = new ZonePoint(Number, ZoneID, Instance, x, y, z, x, y, z, Heading, ZoneID); + + ZonePointList.Add(NewZonePoint); + } + + return ZonePointList; + } + + override public NewZoneStruct GetZoneData() + { + NewZoneStruct NewZone = new NewZoneStruct(); + + List ZonePackets = GetPacketsOfType("OP_NewZone", PacketDirection.ServerToClient); + + if (ZonePackets.Count < 1) + return NewZone; + + // Assume there is only 1 packet and process the first one. + + ByteStream Buffer = new ByteStream(ZonePackets[0]); + + string CharName = Buffer.ReadFixedLengthString(64, false); + + NewZone.ShortName = Buffer.ReadFixedLengthString(32, false); + + Buffer.SkipBytes(96); // Skip Unknown + + NewZone.LongName = Buffer.ReadFixedLengthString(278, true); + + NewZone.Type = Buffer.ReadByte(); + + NewZone.FogRed = Buffer.ReadBytes(4); + + NewZone.FogGreen = Buffer.ReadBytes(4); + + NewZone.FogBlue = Buffer.ReadBytes(4); + + Buffer.SkipBytes(1); // Unknown + + for (int i = 0; i < 4; ++i) + NewZone.FogMinClip[i] = Buffer.ReadSingle(); + + for (int i = 0; i < 4; ++i) + NewZone.FogMaxClip[i] = Buffer.ReadSingle(); + + NewZone.Gravity = Buffer.ReadSingle(); + + NewZone.TimeType = Buffer.ReadByte(); + + Buffer.SkipBytes(49); // Unknown + + NewZone.Sky = Buffer.ReadByte(); + + Buffer.SkipBytes(13); // Unknown + + NewZone.ZEM = Buffer.ReadSingle(); + + NewZone.SafeY = Buffer.ReadSingle(); + + NewZone.SafeX = Buffer.ReadSingle(); + + NewZone.SafeZ = Buffer.ReadSingle(); + + NewZone.MinZ = Buffer.ReadSingle(); + + NewZone.MaxZ = Buffer.ReadSingle(); + + NewZone.UnderWorld = Buffer.ReadSingle(); + + NewZone.MinClip = Buffer.ReadSingle(); + + NewZone.MaxClip = Buffer.ReadSingle(); + + Buffer.SkipBytes(84); // Unknown + + NewZone.ShortName2 = Buffer.ReadFixedLengthString(96, false); + + Buffer.SkipBytes(52); // Unknown + + NewZone.ZoneID = Buffer.ReadUInt16(); + + NewZone.InstanceID = Buffer.ReadUInt16(); + + Buffer.SkipBytes(38); // Unknown + + NewZone.FallDamage = Buffer.ReadByte(); + + Buffer.SkipBytes(21); // Unknown + + NewZone.FogDensity = Buffer.ReadSingle(); + + // Everything else after this point in the packet is unknown. + + return NewZone; + } + + override public List GetSpawns() + { + List ZoneSpawns = new List(); + + List SpawnPackets = GetPacketsOfType("OP_ZoneEntry", PacketDirection.ServerToClient); + + foreach (byte[] SpawnPacket in SpawnPackets) + { + ZoneEntryStruct NewSpawn = new ZoneEntryStruct(); + + ByteStream Buffer = new ByteStream(SpawnPacket); + + NewSpawn.SpawnName = Buffer.ReadString(true); + + NewSpawn.SpawnName = Utils.MakeCleanName(NewSpawn.SpawnName); + + NewSpawn.SpawnID = Buffer.ReadUInt32(); + + NewSpawn.Level = Buffer.ReadByte(); + + float UnkSize = Buffer.ReadSingle(); + + NewSpawn.IsNPC = Buffer.ReadByte(); + + UInt32 Bitfield = Buffer.ReadUInt32(); + + NewSpawn.Showname = (Bitfield >> 28) & 1; + NewSpawn.TargetableWithHotkey = (Bitfield >> 27) & 1; + NewSpawn.Targetable = (Bitfield >> 26) & 1; + + NewSpawn.ShowHelm = (Bitfield >> 24) & 1; + NewSpawn.Gender = (Bitfield >> 20) & 3; + + Byte OtherData = Buffer.ReadByte(); + + Buffer.SkipBytes(8); // Skip 8 unknown bytes + + NewSpawn.DestructableString1 = ""; + NewSpawn.DestructableString2 = ""; + NewSpawn.DestructableString3 = ""; + + if ((OtherData & 1) > 0) + { + //SQLOut(String.Format("-- OBJECT FOUND SpawnID {0}", SpawnID.ToString("x"))); + //Console.WriteLine("Otherdata is {0} Skipping Stuff.", OtherData.ToString("x")); + NewSpawn.DestructableString1 = Buffer.ReadString(false); + + NewSpawn.DestructableString2 = Buffer.ReadString(false); + + NewSpawn.DestructableString3 = Buffer.ReadString(false); + + NewSpawn.DestructableUnk1 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk2 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID1 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID2 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID3 = Buffer.ReadUInt32(); + + NewSpawn.DestructableID4 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk3 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk4 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk5 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk6 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk7 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk8 = Buffer.ReadUInt32(); + + NewSpawn.DestructableUnk9 = Buffer.ReadUInt32(); + + NewSpawn.DestructableByte = Buffer.ReadByte(); + + //SQLOut(String.Format("-- DES: {0,8:x} {1,8:x} {2,8:d} {3,8:d} {4,8:d} {5,8:d} {6,8:x} {7,8:x} {8,8:x} {9,8:x} {10,8:x} {11,8:x} {12,8:x} {13,2:x} {14} {15} {16}", + // DestructableUnk1, DestructableUnk2, DestructableID1, DestructableID2, DestructableID3, DestructableID4, + // DestructableUnk3, DestructableUnk4, DestructableUnk5, DestructableUnk6, DestructableUnk7, DestructableUnk8, + // DestructableUnk9, DestructableByte, DestructableString1, DestructableString2, DestructableString3)); + } + + NewSpawn.Size = Buffer.ReadSingle(); + + NewSpawn.Face = Buffer.ReadByte(); + + NewSpawn.WalkSpeed = Buffer.ReadSingle(); + + NewSpawn.RunSpeed = Buffer.ReadSingle(); + + NewSpawn.Race = Buffer.ReadUInt32(); + + NewSpawn.PropCount = Buffer.ReadByte(); + + NewSpawn.BodyType = 0; + + if (NewSpawn.PropCount >= 1) + { + NewSpawn.BodyType = Buffer.ReadUInt32(); + + for (int j = 1; j < NewSpawn.PropCount; ++j) + Buffer.SkipBytes(4); + } + + Buffer.SkipBytes(1); // Skip HP % + + NewSpawn.HairColor = Buffer.ReadByte(); + NewSpawn.BeardColor = Buffer.ReadByte(); + NewSpawn.EyeColor1 = Buffer.ReadByte(); + NewSpawn.EyeColor2 = Buffer.ReadByte(); + NewSpawn.HairStyle = Buffer.ReadByte(); + NewSpawn.Beard = Buffer.ReadByte(); + + NewSpawn.DrakkinHeritage = Buffer.ReadUInt32(); + + NewSpawn.DrakkinTattoo = Buffer.ReadUInt32(); + + NewSpawn.DrakkinDetails = Buffer.ReadUInt32(); + + Buffer.SkipBytes(1); // Skip Holding + + NewSpawn.Deity = Buffer.ReadUInt32(); + + Buffer.SkipBytes(8); // Skip GuildID and GuildRank + + NewSpawn.Class = Buffer.ReadByte(); + + Buffer.SkipBytes(4); // Skip PVP, Standstate, Light, Flymode + + NewSpawn.EquipChest2 = Buffer.ReadByte(); + + bool UseWorn = (NewSpawn.EquipChest2 == 255); + + Buffer.SkipBytes(2); // 2 Unknown bytes; + + NewSpawn.Helm = Buffer.ReadByte(); + + NewSpawn.LastName = Buffer.ReadString(false); + + Buffer.SkipBytes(5); // AATitle + unknown byte + + NewSpawn.PetOwnerID = Buffer.ReadUInt32(); + + Buffer.SkipBytes(25); // Unknown byte + 6 unknown uint32 + + UInt32 Position1 = Buffer.ReadUInt32(); + + UInt32 Position2 = Buffer.ReadUInt32(); + + UInt32 Position3 = Buffer.ReadUInt32(); + + UInt32 Position4 = Buffer.ReadUInt32(); + + UInt32 Position5 = Buffer.ReadUInt32(); + + NewSpawn.YPos = Utils.EQ19ToFloat((Int32)(Position3 & 0x7FFFF)); + + NewSpawn.Heading = Utils.EQ19ToFloat((Int32)(Position4 & 0xFFF)); + + NewSpawn.XPos = Utils.EQ19ToFloat((Int32)(Position4 >> 12) & 0x7FFFF); + + NewSpawn.ZPos = Utils.EQ19ToFloat((Int32)(Position5 & 0x7FFFF)); + + for (int ColourSlot = 0; ColourSlot < 9; ++ColourSlot) + NewSpawn.SlotColour[ColourSlot] = Buffer.ReadUInt32(); + + NewSpawn.MeleeTexture1 = 0; + NewSpawn.MeleeTexture2 = 0; + + if (NPCType.IsPlayableRace(NewSpawn.Race)) + { + for (int i = 0; i < 9; ++i) + { + NewSpawn.Equipment[i] = Buffer.ReadUInt32(); + + UInt32 Equip1 = Buffer.ReadUInt32(); + + UInt32 Equip0 = Buffer.ReadUInt32(); + } + + if (NewSpawn.Equipment[Constants.MATERIAL_CHEST] > 0) + { + NewSpawn.EquipChest2 = (byte)NewSpawn.Equipment[Constants.MATERIAL_CHEST]; + + } + + NewSpawn.ArmorTintRed = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 16) & 0xff); + + NewSpawn.ArmorTintGreen = (byte)((NewSpawn.SlotColour[Constants.MATERIAL_CHEST] >> 8) & 0xff); + + NewSpawn.ArmorTintBlue = (byte)(NewSpawn.SlotColour[Constants.MATERIAL_CHEST] & 0xff); + + if (NewSpawn.Equipment[Constants.MATERIAL_PRIMARY] > 0) + NewSpawn.MeleeTexture1 = NewSpawn.Equipment[Constants.MATERIAL_PRIMARY]; + + if (NewSpawn.Equipment[Constants.MATERIAL_SECONDARY] > 0) + NewSpawn.MeleeTexture2 = NewSpawn.Equipment[Constants.MATERIAL_SECONDARY]; + + if (UseWorn) + NewSpawn.Helm = (byte)NewSpawn.Equipment[Constants.MATERIAL_HEAD]; + else + NewSpawn.Helm = 0; + + } + else + { + // Non playable race + NewSpawn.MeleeTexture1 = NewSpawn.SlotColour[3]; + NewSpawn.MeleeTexture2 = NewSpawn.SlotColour[6]; + + } + + if (NewSpawn.EquipChest2 == 255) + NewSpawn.EquipChest2 = 0; + + if (NewSpawn.Helm == 255) + NewSpawn.Helm = 0; + + if ((OtherData & 4) > 0) + { + NewSpawn.Title = Buffer.ReadString(false); + } + + if ((OtherData & 8) > 0) + { + NewSpawn.Suffix = Buffer.ReadString(false); + } + + // unknowns + Buffer.SkipBytes(8); + + NewSpawn.IsMercenary = Buffer.ReadByte(); + + ZoneSpawns.Add(NewSpawn); + } + + return ZoneSpawns; + } + + override public List GetHighResolutionMovementUpdates() + { + List Updates = new List(); + + List UpdatePackets = GetPacketsOfType("OP_NPCMoveUpdate", PacketDirection.ServerToClient); + + foreach (byte[] UpdatePacket in UpdatePackets) + { + PositionUpdate PosUpdate = new PositionUpdate(); + + BitStream bs = new BitStream(UpdatePacket, 13); + + PosUpdate.SpawnID = bs.readUInt(16); + + UInt32 VFlags = bs.readUInt(6); + + PosUpdate.p.y = (float)bs.readInt(19) / (float)(1 << 3); + + PosUpdate.p.x = (float)bs.readInt(19) / (float)(1 << 3); + + PosUpdate.p.z = (float)bs.readInt(19) / (float)(1 << 3); + + PosUpdate.p.heading = (float)bs.readInt(12) / (float)(1 << 3); + + Updates.Add(PosUpdate); + } + + return Updates; + } + + override public List GetLowResolutionMovementUpdates() + { + List Updates = new List(); + + List UpdatePackets = GetPacketsOfType("OP_MobUpdate", PacketDirection.ServerToClient); + + foreach (byte[] MobUpdatePacket in UpdatePackets) + { + PositionUpdate PosUpdate = new PositionUpdate(); + + ByteStream Buffer = new ByteStream(MobUpdatePacket); + + PosUpdate.SpawnID = Buffer.ReadUInt16(); + + UInt32 Word1 = Buffer.ReadUInt32(); + + UInt32 Word2 = Buffer.ReadUInt32(); + + UInt16 Word3 = Buffer.ReadUInt16(); + + PosUpdate.p.y = Utils.EQ19ToFloat((Int32)(Word1 & 0x7FFFF)); + + // Z is in the top 13 bits of Word1 and the bottom 6 of Word2 + + UInt32 ZPart1 = Word1 >> 19; // ZPart1 now has low order bits of Z in bottom 13 bits + UInt32 ZPart2 = Word2 & 0x3F; // ZPart2 now has low order bits of Z in bottom 6 bits + + ZPart2 = ZPart2 << 13; + + PosUpdate.p.z = Utils.EQ19ToFloat((Int32)(ZPart1 | ZPart2)); + + PosUpdate.p.x = Utils.EQ19ToFloat((Int32)(Word2 >> 6) & 0x7FFFF); + + PosUpdate.p.heading = Utils.EQ19ToFloat((Int32)(Word3 & 0xFFF)); + + Updates.Add(PosUpdate); + } + + return Updates; + } + + override public List GetGroundSpawns() + { + List GroundSpawns = new List(); + + List GroundSpawnPackets = GetPacketsOfType("OP_GroundSpawn", PacketDirection.ServerToClient); + + foreach (byte[] GroundSpawnPacket in GroundSpawnPackets) + { + GroundSpawnStruct GroundSpawn = new GroundSpawnStruct(); + + ByteStream Buffer = new ByteStream(GroundSpawnPacket); + + Buffer.SkipBytes(12); + + GroundSpawn.DropID = Buffer.ReadUInt32(); + + GroundSpawn.ZoneID = Buffer.ReadUInt16(); + + GroundSpawn.InstanceID = Buffer.ReadUInt16(); + + Buffer.SkipBytes(8); + + GroundSpawn.Heading = Buffer.ReadSingle(); + + Buffer.SkipBytes(12); + + GroundSpawn.z = Buffer.ReadSingle(); + + GroundSpawn.x = Buffer.ReadSingle(); + + GroundSpawn.y = Buffer.ReadSingle(); + + GroundSpawn.Name = Buffer.ReadFixedLengthString(16, false); + + Buffer.SkipBytes(20); + + GroundSpawn.ObjectType = Buffer.ReadUInt32(); + + GroundSpawns.Add(GroundSpawn); + } + return GroundSpawns; + } + + override public List GetFindableSpawns() + { + List FindableSpawnList = new List(); + + List FindablePackets = GetPacketsOfType("OP_SendFindableNPCs", PacketDirection.ServerToClient); + + if (FindablePackets.Count < 1) + return FindableSpawnList; + + foreach (byte[] Packet in FindablePackets) + { + if (BitConverter.ToUInt32(Packet, 0) == 0) + FindableSpawnList.Add(BitConverter.ToUInt32(Packet, 4)); + } + + return FindableSpawnList; + } + + override public string GetZoneName() + { + List NewZonePacket = GetPacketsOfType("OP_NewZone", PacketDirection.ServerToClient); + + if (NewZonePacket.Count != 1) + return ""; + + return Utils.ReadNullTerminatedString(NewZonePacket[0], 704, 96, false); + } + + public override void RegisterExplorers() + { + OpManager.RegisterExplorer("OP_ZoneEntry", ExploreZoneEntry); + //OpManager.RegisterExplorer("OP_RespawnWindow", ExploreRespawnWindow); + //OpManager.RegisterExplorer("OP_ZonePlayerToBind", ExploreZonePlayerToBind); + //OpManager.RegisterExplorer("OP_RequestClientZoneChange", ExploreRequestClientZoneChange); + //OpManager.RegisterExplorer("OP_DeleteSpawn", ExploreDeleteSpawn); + OpManager.RegisterExplorer("OP_HPUpdate", ExploreHPUpdate); + + } + + public void ExploreZoneEntry(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + if (Direction != PacketDirection.ServerToClient) + return; + + string Name = Buffer.ReadString(false); + UInt32 SpawnID = Buffer.ReadUInt32(); + byte Level = Buffer.ReadByte(); + Buffer.SkipBytes(4); + bool IsNPC = (Buffer.ReadByte() != 0); + UInt32 Bitfield = Buffer.ReadUInt32(); + + string DestructableString1; + string DestructableString2; + string DestructableString3; + UInt32 DestructableUnk1; + UInt32 DestructableUnk2; + UInt32 DestructableID1; + UInt32 DestructableID2; + UInt32 DestructableID3; + UInt32 DestructableID4; + UInt32 DestructableUnk3; + UInt32 DestructableUnk4; + UInt32 DestructableUnk5; + UInt32 DestructableUnk6; + UInt32 DestructableUnk7; + UInt32 DestructableUnk8; + UInt32 DestructableUnk9; + byte DestructableByte; + + Byte OtherData = Buffer.ReadByte(); + + Buffer.SkipBytes(8); // Skip 8 unknown bytes + + DestructableString1 = ""; + DestructableString2 = ""; + DestructableString3 = ""; + + OutputStream.WriteLine("Spawn Name: {0} ID: {1} Level: {2} {3}\r\n", Name, SpawnID, Level, IsNPC ? "NPC" : "Player"); + + if ((OtherData & 1) > 0) + { + // Destructable Objects. + + DestructableString1 = Buffer.ReadString(false); + + DestructableString2 = Buffer.ReadString(false); + + DestructableString3 = Buffer.ReadString(false); + + DestructableUnk1 = Buffer.ReadUInt32(); + + DestructableUnk2 = Buffer.ReadUInt32(); + + DestructableID1 = Buffer.ReadUInt32(); + + DestructableID2 = Buffer.ReadUInt32(); + + DestructableID3 = Buffer.ReadUInt32(); + + DestructableID4 = Buffer.ReadUInt32(); + + DestructableUnk3 = Buffer.ReadUInt32(); + + DestructableUnk4 = Buffer.ReadUInt32(); + + DestructableUnk5 = Buffer.ReadUInt32(); + + DestructableUnk6 = Buffer.ReadUInt32(); + + DestructableUnk7 = Buffer.ReadUInt32(); + + DestructableUnk8 = Buffer.ReadUInt32(); + + DestructableUnk9 = Buffer.ReadUInt32(); + + DestructableByte = Buffer.ReadByte(); + + OutputStream.WriteLine("DESTRUCTABLE OBJECT:\r\n"); + OutputStream.WriteLine(" String1: {0}", DestructableString1); + OutputStream.WriteLine(" String2: {0}", DestructableString2); + OutputStream.WriteLine(" String3: {0}\r\n", DestructableString3); + + OutputStream.WriteLine(" Unk1: {0,8:x} Unk2: {1,8:x}\r\n ID1 : {2,8:x} ID2 : {3,8:x} ID3 : {4,8:x} ID4 : {5,8:x}\r\n Unk3: {6,8:x} Unk4: {7,8:x} Unk5: {8,8:x} Unk6: {9,8:x}\r\n Unk7: {10,8:x} Unk8: {11,8:x} Unk9: {12,8:x}\r\n UnkByte: {13,2:x}", + DestructableUnk1, DestructableUnk2, DestructableID1, DestructableID2, DestructableID3, DestructableID4, + DestructableUnk3, DestructableUnk4, DestructableUnk5, DestructableUnk6, DestructableUnk7, DestructableUnk8, + DestructableUnk9, DestructableByte); + } + + Buffer.SkipBytes(17); + + byte PropCount = Buffer.ReadByte(); + + if (PropCount >= 1) + { + Buffer.SkipBytes(4); + + for (int j = 1; j < PropCount; ++j) + Buffer.SkipBytes(4); + } + + byte HP = Buffer.ReadByte(); + + OutputStream.WriteLine("HP% is {0}\r\n", HP); + + AddExplorerSpawn(SpawnID, Name); + } + + public void ExploreHPUpdate(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 CurrentHP = Buffer.ReadUInt32(); + Int32 MaxHP = Buffer.ReadInt32(); + UInt16 SpawnID = Buffer.ReadUInt16(); + + string SpawnName = FindExplorerSpawn(SpawnID); + + OutputStream.WriteLine("Spawn {0} {1} Current HP: {2} Max HP: {3}", SpawnID, SpawnName, CurrentHP, MaxHP); + + OutputStream.WriteLine(""); + } + + override public bool DumpAAs(string FileName) + { + List AAPackets = GetPacketsOfType("OP_SendAATable", PacketDirection.ServerToClient); + + + if (AAPackets.Count < 1) + return false; + + StreamWriter OutputFile; + + try + { + OutputFile = new StreamWriter(FileName); + } + catch + { + return false; + + } + + OutputFile.WriteLine("-- There are " + AAPackets.Count + " OP_SendAATable packets."); + OutputFile.WriteLine(""); + + foreach (byte[] Packet in AAPackets) + { + UInt32 AAID = BitConverter.ToUInt32(Packet, 0); + UInt32 Unknown004 = BitConverter.ToUInt32(Packet, 4); + UInt32 HotKeySID = BitConverter.ToUInt32(Packet, 5); + UInt32 HotKeySID2 = BitConverter.ToUInt32(Packet, 9); + UInt32 TitleSID = BitConverter.ToUInt32(Packet, 13); + UInt32 DescSID = BitConverter.ToUInt32(Packet, 17); + UInt32 ClassType = BitConverter.ToUInt32(Packet, 21); + UInt32 Cost = BitConverter.ToUInt32(Packet, 25); + UInt32 Seq = BitConverter.ToUInt32(Packet, 29); + UInt32 CurrentLevel = BitConverter.ToUInt32(Packet, 33); + UInt32 PrereqSkill = BitConverter.ToUInt32(Packet, 37); + UInt32 PrereqMinpoints = BitConverter.ToUInt32(Packet, 41); + UInt32 Type = BitConverter.ToUInt32(Packet, 45); + UInt32 SpellID = BitConverter.ToUInt32(Packet, 49); + UInt32 SpellType = BitConverter.ToUInt32(Packet, 53); + UInt32 SpellRefresh = BitConverter.ToUInt32(Packet, 57); + UInt16 Classes = BitConverter.ToUInt16(Packet, 61); + UInt16 Berserker = BitConverter.ToUInt16(Packet, 63); + UInt32 MaxLevel = BitConverter.ToUInt32(Packet, 65); + UInt32 LastID = BitConverter.ToUInt32(Packet, 69); + UInt32 NextID = BitConverter.ToUInt32(Packet, 73); + UInt32 Cost2 = BitConverter.ToUInt32(Packet, 77); + UInt32 AAExpansion = BitConverter.ToUInt32(Packet, 88); + UInt32 SpecialCategory = BitConverter.ToUInt32(Packet, 92); + UInt32 TotalAbilities = BitConverter.ToUInt32(Packet, 100); + + OutputFile.WriteLine(String.Format("AAID: {0}", AAID)); + OutputFile.WriteLine(" Unknown004:\t" + Unknown004); + OutputFile.WriteLine(" HotkeySID:\t" + HotKeySID); + OutputFile.WriteLine(" HotkeySID2:\t" + HotKeySID2); + OutputFile.WriteLine(" TitleSID:\t" + TitleSID); + OutputFile.WriteLine(" DescSID:\t" + DescSID); + OutputFile.WriteLine(" ClassType:\t" + ClassType); + OutputFile.WriteLine(" Cost:\t\t" + Cost); + OutputFile.WriteLine(" Seq:\t\t" + Seq); + OutputFile.WriteLine(" CurrentLevel:\t" + CurrentLevel); + OutputFile.WriteLine(" PrereqSkill:\t" + PrereqSkill); + OutputFile.WriteLine(" PrereqMinPt:\t" + PrereqMinpoints); + OutputFile.WriteLine(" Type:\t\t" + Type); + OutputFile.WriteLine(" SpellID:\t" + SpellID); + OutputFile.WriteLine(" SpellType:\t" + SpellType); + OutputFile.WriteLine(" SpellRefresh:\t" + SpellRefresh); + OutputFile.WriteLine(" Classes:\t" + Classes); + OutputFile.WriteLine(" Berserker:\t" + Berserker); + OutputFile.WriteLine(" MaxLevel:\t" + MaxLevel); + OutputFile.WriteLine(" LastID:\t" + LastID); + OutputFile.WriteLine(" NextID:\t" + NextID); + OutputFile.WriteLine(" Cost2:\t\t" + Cost2); + OutputFile.WriteLine(" AAExpansion:\t" + AAExpansion); + OutputFile.WriteLine(" SpecialCat:\t" + SpecialCategory); + OutputFile.WriteLine(""); + OutputFile.WriteLine(" TotalAbilities:\t" + TotalAbilities); + OutputFile.WriteLine(""); + + for (int i = 0; i < TotalAbilities; ++i) + { + UInt32 Ability = BitConverter.ToUInt32(Packet, 104 + (i * 16)); + Int32 Base1 = BitConverter.ToInt32(Packet, 108 + (i * 16)); + Int32 Base2 = BitConverter.ToInt32(Packet, 112 + (i * 16)); + UInt32 Slot = BitConverter.ToUInt32(Packet, 116 + (i * 16)); + + OutputFile.WriteLine(String.Format(" Ability:\t{0}\tBase1:\t{1}\tBase2:\t{2}\tSlot:\t{3}", Ability, Base1, Base2, Slot)); + + } + OutputFile.WriteLine(""); + + } + + OutputFile.Close(); + + return true; + } + } +} diff --git a/utils/EQExtractor2/EQExtractor2/PatchTestSep1-2010.cs b/utils/EQExtractor2/EQExtractor2/PatchTestSep1-2010.cs new file mode 100644 index 000000000..b2a7cd486 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchTestSep1-2010.cs @@ -0,0 +1,341 @@ +// +// Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net). Distributed under GPL version 2. +// +// +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchTestSep012010Decoder : PatchJuly132010Decoder + { + public PatchTestSep012010Decoder() + { + Version = "EQ Client Build Date Test Server September 1 2010."; + + PatchConfFileName = "patch_Sep01-2010.conf"; + } + + override public IdentificationStatus Identify(int OpCode, int Size, PacketDirection Direction) + { + if((OpCode == OpManager.OpCodeNameToNumber("OP_ZoneEntry")) && (Direction == PacketDirection.ClientToServer)) + return IdentificationStatus.Yes; + + return IdentificationStatus.No; + } + + override public Item DecodeItemPacket(byte[] PacketBuffer) + { + ByteStream Buffer = new ByteStream(PacketBuffer); + + Item NewItem = new Item(); + + NewItem.StackSize = Buffer.ReadUInt32(); // 00 + Buffer.SkipBytes(4); + NewItem.Slot = Buffer.ReadUInt32(); // 08 + Buffer.SkipBytes(1); + NewItem.MerchantSlot = Buffer.ReadByte(); // 13 + NewItem.Price = Buffer.ReadUInt32(); // 14 + Buffer.SkipBytes(5); + NewItem.Quantity = Buffer.ReadInt32(); // 23 + Buffer.SetPosition(71); + NewItem.Name = Buffer.ReadString(true); + NewItem.Lore = Buffer.ReadString(true); + NewItem.IDFile = Buffer.ReadString(true); + NewItem.ID = Buffer.ReadUInt32(); + + return NewItem; + } + + public override void RegisterExplorers() + { + base.RegisterExplorers(); + + //OpManager.RegisterExplorer("OP_CharInventory", ExploreCharInventoryPacket); + //OpManager.RegisterExplorer("OP_ItemPacket", ExploreItemPacket); + //OpManager.RegisterExplorer("OP_MercenaryPurchaseWindow", ExploreMercenaryPurchaseWindow); + } + + public void ExploreCharInventoryPacket(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 ItemCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("There are {0} items in the inventory.\r\n", ItemCount ); + + for (int i = 0; i < ItemCount; ++i) + { + ExploreSubItem(OutputStream, ref Buffer); + } + + OutputStream.WriteLine(""); + } + + public void ExploreItemPacket(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + Buffer.SkipBytes(4); // Skip type field. + + ExploreSubItem(OutputStream, ref Buffer); + + OutputStream.WriteLine(""); + } + + void ExploreSubItem(StreamWriter OutputStream, ref ByteStream Buffer) + { + Buffer.SkipBytes(8); + + byte Area = Buffer.ReadByte(); + UInt16 MainSlot = Buffer.ReadUInt16(); + Int16 SubSlot = Buffer.ReadInt16(); + Buffer.SkipBytes(54); + string Name = Buffer.ReadString(true); + + if (SubSlot >= 0) + OutputStream.Write(" "); + + string AreaName = "Unknown"; + + switch (Area) + { + case 0: + AreaName = "Personal Inventory"; + break; + case 1: + AreaName = "Bank"; + break; + case 2: + AreaName = "Shared Bank"; + break; + case 6: + AreaName = "Personal Tribute"; + break; + case 7: + AreaName = "Guild Tribute"; + break; + case 8: + AreaName = "Merchant"; + break; + } + + OutputStream.WriteLine("Area: {0} {1} Main Slot {2,2} Sub Slot {3,3} Name {4}", Area, AreaName.PadRight(20), MainSlot, SubSlot, Name); + + Buffer.ReadString(true); // Lore + Buffer.ReadString(true); // IDFile + + //Buffer.SkipBytes(236); // Item Body Struct + + UInt32 ID = Buffer.ReadUInt32(); + byte Weight = Buffer.ReadByte(); + byte NoRent = Buffer.ReadByte(); + byte NoDrop = Buffer.ReadByte(); + byte Attune = Buffer.ReadByte(); + byte Size = Buffer.ReadByte(); + + OutputStream.WriteLine(" ID: {0} Weight: {1} NoRent: {2} NoDrop: {3} Attune {4} Size {5}", ID, Weight, NoRent, NoDrop, Attune, Size); + + UInt32 Slots = Buffer.ReadUInt32(); + UInt32 Price = Buffer.ReadUInt32(); + UInt32 Icon = Buffer.ReadUInt32(); + Buffer.SkipBytes(2); + UInt32 BenefitFlags = Buffer.ReadUInt32(); + byte Tradeskills = Buffer.ReadByte(); + + OutputStream.WriteLine(" Slots: {0} Price: {1} Icon: {2} BenefitFlags {3} Tradeskills: {4}", Slots, Price, Icon, BenefitFlags, Tradeskills); + + byte CR = Buffer.ReadByte(); + byte DR = Buffer.ReadByte(); + byte PR = Buffer.ReadByte(); + byte MR = Buffer.ReadByte(); + byte FR = Buffer.ReadByte(); + byte SVC = Buffer.ReadByte(); + + OutputStream.WriteLine(" CR: {0} DR: {1} PR: {2} MR: {3} FR: {4} SVC: {5}", CR, DR, PR, MR, FR, SVC); + + byte AStr = Buffer.ReadByte(); + byte ASta = Buffer.ReadByte(); + byte AAgi = Buffer.ReadByte(); + byte ADex = Buffer.ReadByte(); + byte ACha = Buffer.ReadByte(); + byte AInt = Buffer.ReadByte(); + byte AWis = Buffer.ReadByte(); + + OutputStream.WriteLine(" AStr: {0} ASta: {1} AAgi: {2} ADex: {3} ACha: {4} AInt: {5} AWis: {6}", AStr, ASta, AAgi, ADex, ACha, AInt, AWis); + + Int32 HP = Buffer.ReadInt32(); + Int32 Mana = Buffer.ReadInt32(); + UInt32 Endurance = Buffer.ReadUInt32(); + Int32 AC = Buffer.ReadInt32(); + Int32 Regen = Buffer.ReadInt32(); + Int32 ManaRegen = Buffer.ReadInt32(); + Int32 EndRegen = Buffer.ReadInt32(); + UInt32 Classes = Buffer.ReadUInt32(); + UInt32 Races = Buffer.ReadUInt32(); + UInt32 Deity = Buffer.ReadUInt32(); + Int32 SkillModValue = Buffer.ReadInt32(); + Buffer.SkipBytes(4); + UInt32 SkillModType = Buffer.ReadUInt32(); + UInt32 BaneDamageRace = Buffer.ReadUInt32(); + UInt32 BaneDamageBody = Buffer.ReadUInt32(); + UInt32 BaneDamageRaceAmount = Buffer.ReadUInt32(); + Int32 BaneDamageAmount = Buffer.ReadInt32(); + byte Magic = Buffer.ReadByte(); + Int32 CastTime = Buffer.ReadInt32(); + UInt32 ReqLevel = Buffer.ReadUInt32(); + UInt32 RecLevel = Buffer.ReadUInt32(); + UInt32 ReqSkill = Buffer.ReadUInt32(); + UInt32 BardType = Buffer.ReadUInt32(); + Int32 BardValue = Buffer.ReadInt32(); + byte Light = Buffer.ReadByte(); + byte Delay = Buffer.ReadByte(); + byte ElemDamageAmount = Buffer.ReadByte(); + byte ElemDamageType = Buffer.ReadByte(); + byte Range = Buffer.ReadByte(); + UInt32 Damage = Buffer.ReadUInt32(); + UInt32 Color = Buffer.ReadUInt32(); + byte ItemType = Buffer.ReadByte(); + UInt32 Material = Buffer.ReadUInt32(); + Buffer.SkipBytes(4); + UInt32 EliteMaterial = Buffer.ReadUInt32(); + float SellRate = Buffer.ReadSingle(); + Int32 CombatEffects = Buffer.ReadInt32(); + Int32 Shielding = Buffer.ReadInt32(); + Int32 StunResist = Buffer.ReadInt32(); + Int32 StrikeThrough = Buffer.ReadInt32(); + Int32 ExtraDamageSkill = Buffer.ReadInt32(); + Int32 ExtraDamageAmount = Buffer.ReadInt32(); + Int32 SpellShield = Buffer.ReadInt32(); + Int32 Avoidance = Buffer.ReadInt32(); + Int32 Accuracy = Buffer.ReadInt32(); + UInt32 CharmFileID = Buffer.ReadUInt32(); + UInt32 FactionMod1 = Buffer.ReadUInt32(); + Int32 FactionAmount1 = Buffer.ReadInt32(); + UInt32 FactionMod2 = Buffer.ReadUInt32(); + Int32 FactionAmount2 = Buffer.ReadInt32(); + UInt32 FactionMod3 = Buffer.ReadUInt32(); + Int32 FactionAmount3 = Buffer.ReadInt32(); + UInt32 FactionMod4 = Buffer.ReadUInt32(); + Int32 FactionAmount4 = Buffer.ReadInt32(); + + Buffer.ReadString(true); // Charm File + Buffer.SkipBytes(64); // Item Secondary Body Struct + Buffer.ReadString(true); // Filename + Buffer.SkipBytes(76); // Item Tertiary Body Struct + Buffer.SkipBytes(30); // Click Effect Struct + Buffer.ReadString(true); // Clickname + Buffer.SkipBytes(4); // clickunk7 + Buffer.SkipBytes(30); // Proc Effect Struct + Buffer.ReadString(true); // Proc Name + Buffer.SkipBytes(4); // unknown5 + Buffer.SkipBytes(30); // Worn Effect Struct + Buffer.ReadString(true); // Worn Name + Buffer.SkipBytes(4); // unknown6 + Buffer.SkipBytes(30); // Worn Effect Struct + Buffer.ReadString(true); // Worn Name + Buffer.SkipBytes(4); // unknown6 + Buffer.SkipBytes(30); // Worn Effect Struct + Buffer.ReadString(true); // Worn Name + Buffer.SkipBytes(4); // unknown6 + Buffer.SkipBytes(30); // Worn Effect Struct + Buffer.ReadString(true); // Worn Name + Buffer.SkipBytes(4); // unknown6 + Buffer.SkipBytes(103); // Item Quaternary Body Struct - 4 (we want to read the SubLength field at the end) + + UInt32 SubLengths = Buffer.ReadUInt32(); + + for (int i = 0; i < SubLengths; ++i) + { + Buffer.SkipBytes(4); + ExploreSubItem(OutputStream, ref Buffer); + } + } + + public void ExploreMercenaryPurchaseWindow(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 TypeCount = Buffer.ReadUInt32(); + + //OutputStream.WriteLine("Type Count: {0}\r\n", TypeCount); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Number of Types (Journeyman and Apprentice in this case\r\n", TypeCount); + for (int i = 0; i < TypeCount; ++i) + { + UInt32 TypeDBStringID = Buffer.ReadUInt32(); + //OutputStream.WriteLine(" Type {0} DBStringID {1}", i, TypeDBStringID); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // DBStringID for Type {1}", TypeDBStringID, i); + } + + UInt32 Count2 = Buffer.ReadUInt32(); + + //OutputStream.WriteLine(" Count 2 is {0}", Count2); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Count of Sub-types that follow", Count2); + + for (int i = 0; i < Count2; ++i) + { + int Offset = Buffer.GetPosition(); + + UInt32 Unknown1 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown1); + UInt32 DBStringID1 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // DBStringID of Type", DBStringID1); + UInt32 DBStringID2 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // DBStringID of Sub-Type", DBStringID2); + UInt32 PurchaseCost = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Purchase Cost", PurchaseCost); + UInt32 UpkeepCost = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Upkeep Cost", UpkeepCost); + UInt32 Unknown2 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown2); + UInt32 Unknown3 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown3); + UInt32 Unknown4 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown4); + + byte Unknown5 = Buffer.ReadByte(); + //OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint8, Buffer, {0}); // Unknown", Unknown5); + + UInt32 Unknown6 = Buffer.ReadUInt32(); + //OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown6); + UInt32 Unknown7 = Buffer.ReadUInt32(); + //OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown7); + UInt32 Unknown8 = Buffer.ReadUInt32(); + //OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown8); + + UInt32 StanceCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Number of Stances for this Merc", StanceCount); + + UInt32 Unknown10 = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Unknown", Unknown10); + + byte Unknown11 = Buffer.ReadByte(); + //OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint8, Buffer, {0}); // Unknown", Unknown11); + + + //OutputStream.WriteLine(" Offset: {5} Unknown1: {0} DBStrings: {1} {2} Purchase: {3} Upkeep: {4}\r\n", Unknown1, DBStringID1, DBStringID2, + // PurchaseCost, UpkeepCost, Offset); + //OutputStream.WriteLine(" Unknowns: {0} {1} {2} {3} {4} {5} {6} {7} {8}\r\n", + // Unknown2, Unknown3, Unknown4, Unknown5, Unknown6, Unknown7, Unknown8, Unknown10, Unknown11); + + //OutputStream.WriteLine(" Stance Count: {0}", StanceCount); + + for (int j = 0; j < StanceCount; ++j) + { + UInt32 StanceNum = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Stance Number", StanceNum); + UInt32 StanceType = Buffer.ReadUInt32(); + OutputStream.WriteLine("VARSTRUCT_ENCODE_TYPE(uint32, Buffer, {0}); // Stance DBStringID (1 = Passive, 2 = Balanced etc.", StanceType); + + //OutputStream.WriteLine(" {0}: {1}", StanceNum, StanceType); + } + OutputStream.WriteLine(""); + } + + OutputStream.WriteLine("\r\nBuffer position at end is {0}", Buffer.GetPosition()); + OutputStream.WriteLine(""); + } + + + } +} diff --git a/utils/EQExtractor2/EQExtractor2/PatchTestSep22-2010.cs b/utils/EQExtractor2/EQExtractor2/PatchTestSep22-2010.cs new file mode 100644 index 000000000..6c5230501 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchTestSep22-2010.cs @@ -0,0 +1,349 @@ +using System; +using System.IO; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchTestSep222010Decoder : PatchTestSep012010Decoder + { + public PatchTestSep222010Decoder() + { + Version = "EQ Client Build Date Test Server September 22 2010."; + + PatchConfFileName = "patch_Sep22-2010.conf"; + + ExpectedPPLength = 26728; + } + + override public IdentificationStatus Identify(int OpCode, int Size, PacketDirection Direction) + { + if ((OpCode == OpManager.OpCodeNameToNumber("OP_ZoneEntry")) && (Direction == PacketDirection.ClientToServer)) + return IdentificationStatus.Tentative; + + if ((OpCode == OpManager.OpCodeNameToNumber("OP_PlayerProfile")) && (Direction == PacketDirection.ServerToClient) && + (Size == ExpectedPPLength)) + return IdentificationStatus.Yes; + + return IdentificationStatus.No; + } + + override public List GetDoors() + { + List DoorList = new List(); + + List SpawnDoorPacket = GetPacketsOfType("OP_SpawnDoor", PacketDirection.ServerToClient); + + if ((SpawnDoorPacket.Count == 0) || (SpawnDoorPacket[0].Length == 0)) + return DoorList; + + int DoorCount = SpawnDoorPacket[0].Length / 96; + + ByteStream Buffer = new ByteStream(SpawnDoorPacket[0]); + + for (int d = 0; d < DoorCount; ++d) + { + string DoorName = Buffer.ReadFixedLengthString(32, false); + + float YPos = Buffer.ReadSingle(); + + float XPos = Buffer.ReadSingle(); + + float ZPos = Buffer.ReadSingle(); + + float Heading = Buffer.ReadSingle(); + + UInt32 Incline = Buffer.ReadUInt32(); + + Int32 Size = Buffer.ReadInt32(); + + Buffer.SkipBytes(4); // Skip Unknown + + Byte DoorID = Buffer.ReadByte(); + + Byte OpenType = Buffer.ReadByte(); + + Byte StateAtSpawn = Buffer.ReadByte(); + + Byte InvertState = Buffer.ReadByte(); + + Int32 DoorParam = Buffer.ReadInt32(); + + // Skip past the trailing unknowns in the door struct, moving to the next door in the packet. + + Buffer.SkipBytes(28); + + string DestZone = "NONE"; + + Door NewDoor = new Door(DoorName, YPos, XPos, ZPos, Heading, Incline, Size, DoorID, OpenType, StateAtSpawn, InvertState, + DoorParam, DestZone, 0, 0, 0, 0); + + DoorList.Add(NewDoor); + + } + return DoorList; + } + + override public Item DecodeItemPacket(byte[] PacketBuffer) + { + ByteStream Buffer = new ByteStream(PacketBuffer); + + Item NewItem = new Item(); + + //NewItem.StackSize = Buffer.ReadUInt32(); // 00 + //Buffer.SkipBytes(4); + //NewItem.Slot = Buffer.ReadUInt32(); // 08 + //Buffer.SkipBytes(1); + Buffer.SetPosition(30); + NewItem.MerchantSlot = Buffer.ReadByte(); // 13 + NewItem.Price = Buffer.ReadUInt32(); // 14 + Buffer.SkipBytes(5); + NewItem.Quantity = Buffer.ReadInt32(); // 23 + Buffer.SetPosition(96); + NewItem.Name = Buffer.ReadString(true); + NewItem.Lore = Buffer.ReadString(true); + NewItem.IDFile = Buffer.ReadString(true); + NewItem.ID = Buffer.ReadUInt32(); + + return NewItem; + } + + public override void RegisterExplorers() + { + base.RegisterExplorers(); + + //OpManager.RegisterExplorer("OP_CharInventory", ExploreCharInventoryPacket); + //OpManager.RegisterExplorer("OP_ItemPacket", ExploreItemPacket); + } + + public void ExploreCharInventoryPacket(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + UInt32 ItemCount = Buffer.ReadUInt32(); + + OutputStream.WriteLine("There are {0} items in the inventory.\r\n", ItemCount); + + for (int i = 0; i < ItemCount; ++i) + { + ExploreSubItem(OutputStream, ref Buffer); + } + + OutputStream.WriteLine(""); + } + + public void ExploreItemPacket(StreamWriter OutputStream, ByteStream Buffer, PacketDirection Direction) + { + Buffer.SkipBytes(4); // Skip type field. + + ExploreSubItem(OutputStream, ref Buffer); + + OutputStream.WriteLine(""); + } + + void ExploreSubItem(StreamWriter OutputStream, ref ByteStream Buffer) + { + Buffer.SkipBytes(17); + Buffer.SkipBytes(8); + + byte Area = Buffer.ReadByte(); + UInt16 MainSlot = Buffer.ReadUInt16(); + Int16 SubSlot = Buffer.ReadInt16(); + Buffer.SkipBytes(62); + string Name = Buffer.ReadString(true); + + if (SubSlot >= 0) + OutputStream.Write(" "); + + string AreaName = "Unknown"; + + switch (Area) + { + case 0: + AreaName = "Personal Inventory"; + break; + case 1: + AreaName = "Bank"; + break; + case 2: + AreaName = "Shared Bank"; + break; + case 6: + AreaName = "Personal Tribute"; + break; + case 7: + AreaName = "Guild Tribute"; + break; + case 8: + AreaName = "Merchant"; + break; + } + + OutputStream.WriteLine("Area: {0} {1} Main Slot {2,2} Sub Slot {3,3} Name {4}", Area, AreaName.PadRight(20), MainSlot, SubSlot, Name); + + Buffer.ReadString(true); // Lore + Buffer.ReadString(true); // IDFile + + //Buffer.SkipBytes(236); // Item Body Struct + + UInt32 ID = Buffer.ReadUInt32(); + byte Weight = Buffer.ReadByte(); + Buffer.SkipBytes(3); + byte NoRent = Buffer.ReadByte(); + byte NoDrop = Buffer.ReadByte(); + byte Attune = Buffer.ReadByte(); + byte Size = Buffer.ReadByte(); + + OutputStream.WriteLine(" ID: {0} Weight: {1} NoRent: {2} NoDrop: {3} Attune {4} Size {5}", ID, Weight, NoRent, NoDrop, Attune, Size); + + UInt32 Slots = Buffer.ReadUInt32(); + UInt32 Price = Buffer.ReadUInt32(); + UInt32 Icon = Buffer.ReadUInt32(); + Buffer.SkipBytes(2); + UInt32 BenefitFlags = Buffer.ReadUInt32(); + byte Tradeskills = Buffer.ReadByte(); + + OutputStream.WriteLine(" Slots: {0} Price: {1} Icon: {2} BenefitFlags {3} Tradeskills: {4}", Slots, Price, Icon, BenefitFlags, Tradeskills); + + byte CR = Buffer.ReadByte(); + byte DR = Buffer.ReadByte(); + byte PR = Buffer.ReadByte(); + byte MR = Buffer.ReadByte(); + byte FR = Buffer.ReadByte(); + byte SVC = Buffer.ReadByte(); + + OutputStream.WriteLine(" CR: {0} DR: {1} PR: {2} MR: {3} FR: {4} SVC: {5}", CR, DR, PR, MR, FR, SVC); + + byte AStr = Buffer.ReadByte(); + byte ASta = Buffer.ReadByte(); + byte AAgi = Buffer.ReadByte(); + byte ADex = Buffer.ReadByte(); + byte ACha = Buffer.ReadByte(); + byte AInt = Buffer.ReadByte(); + byte AWis = Buffer.ReadByte(); + + OutputStream.WriteLine(" AStr: {0} ASta: {1} AAgi: {2} ADex: {3} ACha: {4} AInt: {5} AWis: {6}", AStr, ASta, AAgi, ADex, ACha, AInt, AWis); + + Int32 HP = Buffer.ReadInt32(); + Int32 Mana = Buffer.ReadInt32(); + UInt32 Endurance = Buffer.ReadUInt32(); + Int32 AC = Buffer.ReadInt32(); + Int32 Regen = Buffer.ReadInt32(); + Int32 ManaRegen = Buffer.ReadInt32(); + Int32 EndRegen = Buffer.ReadInt32(); + UInt32 Classes = Buffer.ReadUInt32(); + UInt32 Races = Buffer.ReadUInt32(); + UInt32 Deity = Buffer.ReadUInt32(); + Int32 SkillModValue = Buffer.ReadInt32(); + Buffer.SkipBytes(4); + UInt32 SkillModType = Buffer.ReadUInt32(); + UInt32 BaneDamageRace = Buffer.ReadUInt32(); + UInt32 BaneDamageBody = Buffer.ReadUInt32(); + UInt32 BaneDamageRaceAmount = Buffer.ReadUInt32(); + Int32 BaneDamageAmount = Buffer.ReadInt32(); + byte Magic = Buffer.ReadByte(); + Int32 CastTime = Buffer.ReadInt32(); + UInt32 ReqLevel = Buffer.ReadUInt32(); + UInt32 RecLevel = Buffer.ReadUInt32(); + UInt32 ReqSkill = Buffer.ReadUInt32(); + UInt32 BardType = Buffer.ReadUInt32(); + Int32 BardValue = Buffer.ReadInt32(); + byte Light = Buffer.ReadByte(); + byte Delay = Buffer.ReadByte(); + byte ElemDamageAmount = Buffer.ReadByte(); + byte ElemDamageType = Buffer.ReadByte(); + byte Range = Buffer.ReadByte(); + UInt32 Damage = Buffer.ReadUInt32(); + UInt32 Color = Buffer.ReadUInt32(); + byte ItemType = Buffer.ReadByte(); + UInt32 Material = Buffer.ReadUInt32(); + Buffer.SkipBytes(4); + UInt32 EliteMaterial = Buffer.ReadUInt32(); + float SellRate = Buffer.ReadSingle(); + Int32 CombatEffects = Buffer.ReadInt32(); + Int32 Shielding = Buffer.ReadInt32(); + Int32 StunResist = Buffer.ReadInt32(); + Int32 StrikeThrough = Buffer.ReadInt32(); + Int32 ExtraDamageSkill = Buffer.ReadInt32(); + Int32 ExtraDamageAmount = Buffer.ReadInt32(); + Int32 SpellShield = Buffer.ReadInt32(); + Int32 Avoidance = Buffer.ReadInt32(); + Int32 Accuracy = Buffer.ReadInt32(); + UInt32 CharmFileID = Buffer.ReadUInt32(); + UInt32 FactionMod1 = Buffer.ReadUInt32(); + Int32 FactionAmount1 = Buffer.ReadInt32(); + UInt32 FactionMod2 = Buffer.ReadUInt32(); + Int32 FactionAmount2 = Buffer.ReadInt32(); + UInt32 FactionMod3 = Buffer.ReadUInt32(); + Int32 FactionAmount3 = Buffer.ReadInt32(); + UInt32 FactionMod4 = Buffer.ReadUInt32(); + Int32 FactionAmount4 = Buffer.ReadInt32(); + + Buffer.ReadString(true); // Charm File + + //Buffer.SkipBytes(64); // Item Secondary Body Struct + + UInt32 AugType = Buffer.ReadUInt32(); + UInt32 AugRestrict = Buffer.ReadUInt32(); + + Buffer.SkipBytes(30); // Augslot stuff + + UInt32 LDONPointType = Buffer.ReadUInt32(); + UInt32 LDONTheme = Buffer.ReadUInt32(); + UInt32 LDONPrice = Buffer.ReadUInt32(); + UInt32 LDONSellBackRate = Buffer.ReadUInt32(); + UInt32 LDONSold = Buffer.ReadUInt32(); + + byte BagType = Buffer.ReadByte(); + byte BagSlots = Buffer.ReadByte(); + byte BagSize = Buffer.ReadByte(); + byte BagWeightReduction = Buffer.ReadByte(); + + byte Book = Buffer.ReadByte(); + byte BookType = Buffer.ReadByte(); + + OutputStream.WriteLine("\r\n BagType: {0} BagSlots: {1} BagSize: {2} BagWR: {3}", BagType, BagSlots, BagSize, BagWeightReduction); + + + + Buffer.ReadString(true); // Filename + Buffer.SkipBytes(76); // Item Tertiary Body Struct + Buffer.SkipBytes(30); // Click Effect Struct + Buffer.ReadString(true); // Clickname + Buffer.SkipBytes(4); // clickunk7 + Buffer.SkipBytes(30); // Proc Effect Struct + Buffer.ReadString(true); // Proc Name + Buffer.SkipBytes(4); // unknown5 + Buffer.SkipBytes(30); // Worn Effect Struct + Buffer.ReadString(true); // Worn Name + Buffer.SkipBytes(4); // unknown6 + Buffer.SkipBytes(30); // Worn Effect Struct + Buffer.ReadString(true); // Worn Name + Buffer.SkipBytes(4); // unknown6 + Buffer.SkipBytes(30); // Worn Effect Struct + Buffer.ReadString(true); // Worn Name + Buffer.SkipBytes(4); // unknown6 + Buffer.SkipBytes(30); // Worn Effect Struct + Buffer.ReadString(true); // Worn Name + Buffer.SkipBytes(4); // unknown6 + Buffer.SkipBytes(103); // Item Quaternary Body Struct - 4 (we want to read the SubLength field at the end) + + Buffer.SkipBytes(57); // HoT + + OutputStream.WriteLine(" Reading sublengths from offset {0}", Buffer.GetPosition()); + UInt32 SubLengths = Buffer.ReadUInt32(); + + + + for (int i = 0; i < SubLengths; ++i) + { + Buffer.SkipBytes(4); + ExploreSubItem(OutputStream, ref Buffer); + } + + OutputStream.WriteLine(""); + } + + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchTestServerFebruary5-2013.cs b/utils/EQExtractor2/EQExtractor2/PatchTestServerFebruary5-2013.cs new file mode 100644 index 000000000..c705bccd4 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchTestServerFebruary5-2013.cs @@ -0,0 +1,23 @@ +using System; +using System.IO; +using System.Diagnostics; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchTestServerFebruary52013Decoder : PatchJanuary162013Decoder + { + public PatchTestServerFebruary52013Decoder() + { + Version = "EQ Client Build Date Test Server February 5 2013."; + + PatchConfFileName = "patch_TestServer-Feb5-2013.conf"; + + SupportsSQLGeneration = false; + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/PatchTestServerJanuary16-2013.cs b/utils/EQExtractor2/EQExtractor2/PatchTestServerJanuary16-2013.cs new file mode 100644 index 000000000..41f6a3889 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/PatchTestServerJanuary16-2013.cs @@ -0,0 +1,23 @@ +using System; +using System.IO; +using System.Diagnostics; +using System.Collections.Generic; +using EQExtractor2.InternalTypes; +using EQExtractor2.OpCodes; +using EQPacket; +using MyUtils; + +namespace EQExtractor2.Patches +{ + class PatchTestServerJanuary162013Decoder : PatchJanuary162013Decoder + { + public PatchTestServerJanuary162013Decoder() + { + Version = "EQ Client Build Date Test Server January 16 2013."; + + PatchConfFileName = "patch_TestServer-Jan16-2013.conf"; + + SupportsSQLGeneration = false; + } + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/Program.cs b/utils/EQExtractor2/EQExtractor2/Program.cs new file mode 100644 index 000000000..ae7077d4b --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/Program.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace EQExtractor2 +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new EQExtractor2Form1()); + } + } +} diff --git a/utils/EQExtractor2/EQExtractor2/Properties/AssemblyInfo.cs b/utils/EQExtractor2/EQExtractor2/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..879069892 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("EQExtractor2")] +[assembly: AssemblyDescription("EQExtractor2")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("EQEmu Team")] +[assembly: AssemblyProduct("EQExtractor2")] +[assembly: AssemblyCopyright("Copyright © EQEmu Team 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("31d666b6-5caf-47af-b8a7-b74ce016c77d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("2.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/utils/EQExtractor2/EQExtractor2/Properties/Resources.Designer.cs b/utils/EQExtractor2/EQExtractor2/Properties/Resources.Designer.cs new file mode 100644 index 000000000..e67b74c43 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace EQExtractor2.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("EQExtractor2.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/utils/EQExtractor2/EQExtractor2/Properties/Resources.resx b/utils/EQExtractor2/EQExtractor2/Properties/Resources.resx new file mode 100644 index 000000000..af7dbebba --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/Properties/Settings.Designer.cs b/utils/EQExtractor2/EQExtractor2/Properties/Settings.Designer.cs new file mode 100644 index 000000000..27d78fb48 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/Properties/Settings.Designer.cs @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace EQExtractor2.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("notepad.exe")] + public string TextFileViewer { + get { + return ((string)(this["TextFileViewer"])); + } + set { + this["TextFileViewer"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool ShowDebugWindowOnStartup { + get { + return ((bool)(this["ShowDebugWindowOnStartup"])); + } + set { + this["ShowDebugWindowOnStartup"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool DumpTimeStamps { + get { + return ((bool)(this["DumpTimeStamps"])); + } + set { + this["DumpTimeStamps"] = value; + } + } + } +} diff --git a/utils/EQExtractor2/EQExtractor2/Properties/Settings.settings b/utils/EQExtractor2/EQExtractor2/Properties/Settings.settings new file mode 100644 index 000000000..4b0083cec --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/Properties/Settings.settings @@ -0,0 +1,15 @@ + + + + + + notepad.exe + + + False + + + False + + + \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/SharpPcap.xml b/utils/EQExtractor2/EQExtractor2/SharpPcap.xml new file mode 100644 index 000000000..eda2f585c --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/SharpPcap.xml @@ -0,0 +1,1415 @@ + + + + /home/cmorgan/sharppcap_git/SharpPcap/bin/Release/SharpPcap + + + + + Capture live packets from a network device + + + + + Constructs a new PcapDevice based on a 'pcapIf' struct + + A 'pcapIf' struct representing + the pcap device + + + + Default contructor for subclasses + + + + + Gets the pcap name of this network device + + + + + Addresses that represent this device + + + + + Gets the pcap description of this device + + + + + Interface flags, see pcap_findalldevs() man page for more info + + + + + True if device is a loopback interface, false if not + + + + + Set/Get Non-Blocking Mode. returns allways false for savefiles. + + + + + Set the kernel value buffer size in bytes + WinPcap extension + + + + + PcapDevice finalizer. Ensure PcapDevices are stopped and closed before exit. + + + + + Open the device with default values of: promiscuous_mode = false, read_timeout = 1000 + To start capturing call the 'StartCapture' function + + + + + Open the device. To start capturing call the 'StartCapture' function + + + A + + + + Open the device. To start capturing call the 'StartCapture' function + + + A + + A + + + + Sends a raw packet throgh this device + + The packet to send + + + + Sends a raw packet throgh this device + + The packet to send + The number of bytes to send + + + + Sends a raw packet throgh this device + + The packet bytes to send + + + + Sends a raw packet throgh this device + + The packet bytes to send + The number of bytes to send + + + + Sends all packets in a 'PcapSendQueue' out this pcap device + + + A + + A + + A + + + + Retrieves pcap statistics + + + A + + + + General Pcap Exception. + + + + + A wrapper class for libpcap's pcap_pkthdr structure + + + + + Constructs a new PcapHeader + + + + + Constructs a new PcapHeader + + The seconds value of the packet's timestamp + The microseconds value of the packet's timestamp + The actual length of the packet + The length of the capture + + + + The seconds value of the packet's timestamp + + + + + The microseconds value of the packet's timestamp + + + + + The actual length of the packet + + + + + The length of the capture + + + + + Return the DateTime value of this pcap header + + + + + Marshal this structure into the platform dependent version and return + and IntPtr to that memory + NOTE: IntPtr MUST BE FREED via Marshal.FreeHGlobal() + + + A + + + + Capture packets from an offline pcap file + + + + + Constructs a new offline device for reading + pcap files + + + + + + + The description of this device + + + + + The name of the capture file + + + + + Description of the device + + + + + Number of bytes in the capture file + + + + + The underlying pcap file name + + + + + Opens the device for capture + + + + + Retrieves pcap statistics + + + A + + + + Interface to the WinPcap send queue extension methods + + + + + Creates and allocates a new SendQueue + + + The maximun amount of memory (in bytes) + to allocate for the queue + + + + The current length in bytes of this queue + + + + + Add a packet to this send queue. The PcapHeader defines the packet length. + + The packet bytes to add + The pcap header of the packet + True if success, else false + + + + Add a packet to this send queue. + + The packet bytes to add + The pcap header of the packet + True if success, else false + + + + Add a packet to this send queue. + + The packet bytes to add + True if success, else false + + + + Add a packet to this send queue. + + The packet to add + True if success, else false + + + + Add a packet to this send queue. + + The packet to add + The 'seconds' part of the packet's timestamp + The 'microseconds' part of the packet's timestamp + True if success, else false + + + + Send a queue of raw packets to the network. + + + The device on which to send the queue + A + + A + + A + + + + Destroy the send queue. + + + + + Holds network statistics entry from winpcap when in statistics mode + See http://www.winpcap.org/docs/docs_41b5/html/group__wpcap__tut9.html + + + + + This holds byte received and packets received + + + + + This holds time value + + + + + Number of packets received since last sample + + + + + Number of bytes received since last sample + + + + + Constants and static helper methods + + + + Represents the infinite number for packet captures + + + + Returns the pcap version string retrieved via a call to pcap_lib_version() + + + + + Helper class/method to retrieve the version of the SharpPcap assembly + + + + + Returns the current version string of the SharpPcap library + + the current version string of the SharpPcap library + + + + Item in a list of interfaces. + + + + + Representation of an interface address. + + + + + Structure used by kernel to store a generic address + Look at the sa_family value to determine which specific structure to use + 'struct sockaddr' + + + + + Structure that holds an ipv4 address + + + + + Structure that holds an ipv4 address + 'struct sockaddr' + + + + + Structure that holds an ipv6 address + NOTE: we cast the 'struct sockaddr*' to this structure based on the sa_family type + 'struct sockaddr_in6' + + + + + Structure to represent a low level address, like a hardware address + + + + + Windows and Unix differ in their memory models and make it difficult to + support struct timeval in a single library, like this one, across + multiple platforms. + See http://en.wikipedia.org/wiki/64bit#Specific_data_models + The issue is that struct timeval { long tv_sec; long tv_usec; } + has different sizes on Linux 32 and 64bit but the same size on + Windows 32 and 64 bit + Thanks to Jon Pryor for his help in figuring out both the issue with Linux + 32/64bit and the issue between Windows and Unix + + + + + Windows version of struct timeval, the longs are 32bit even on 64-bit versions of Windows + + + + + Each packet in the dump file is prepended with this generic header. + This gets around the problem of different headers for different + packet interfaces. + + + + + Each packet in the dump file is prepended with this generic header. + This gets around the problem of different headers for different + packet interfaces. + + + + + Packet data bytes + NOTE: This struct doesn't exist in header files, it is a construct to map to an + unmanaged byte array + + + + + A BPF pseudo-assembly program for packet filtering + + + + + A queue of raw packets that will be sent to the network with pcap_sendqueue_transmit() + + + + + Define the return values from int pcap_stats() + + + + + Unix version of 'struct pcap_stat' + Uses the same trick as timeval_unix + + + + + Packets received + + + + + Packets dropped + + + + + Drops by interface (maybe not yet supported) + + + + + Windows version of 'struct pcap_stat' + + + + + Packets received + + + + + Packets dropped + + + + + Drops by interface (maybe not yet supported) + + + + + Packets that reach the application + WIN32 only, based on struct pcap_stat in pcap.h + + + + + Per http://msdn.microsoft.com/en-us/ms182161.aspx + + + + + The delegate declaration for PcapHandler requires an UnmanagedFunctionPointer attribute. + Without this it fires for one time and then throws null pointer exception + + + + Create a list of network devices that can be opened with pcap_open(). + + + Open a file to write packets. + + + + Save a packet to disk. + + + + close the files associated with p and deallocates resources. + + + + To avoid callback, this returns one packet at a time + + + + + Send a raw packet.
+ This function allows to send a raw packet to the network. + The MAC CRC doesn't need to be included, because it is transparently calculated + and added by the network interface driver. +
+ the interface that will be used to send the packet + contains the data of the packet to send (including the various protocol headers) + the dimension of the buffer pointed by data + 0 if the packet is succesfully sent, -1 otherwise. +
+ + + Compile a packet filter, converting an high level filtering expression (see Filtering expression syntax) in a program that can be interpreted by the kernel-level filtering engine. + + + + + Free up allocated memory pointed to by a bpf_program struct generated by pcap_compile() + + + + + return the error text pertaining to the last pcap library error. + + + + Returns a pointer to a string giving information about the version of the libpcap library being used; note that it contains more information than just a version number. + + + return the standard I/O stream of the 'savefile' opened by pcap_dump_open(). + + + Flushes the output buffer to the 'savefile', so that any packets + written with pcap_dump() but not yet written to the 'savefile' will be written. + -1 is returned on error, 0 on success. + + + Closes a savefile. + + + Return the link layer of an adapter. + + + + Set nonblocking mode. pcap_loop() and pcap_next() doesnt work in nonblocking mode! + + + + + Get nonblocking mode, returns allways 0 for savefiles. + + + + + Read packets until cnt packets are processed or an error occurs. + + + + + Retrieves a selectable file descriptor + + + A + + A + + + + Fills in the pcap_stat structure passed to the function + based on the pcap_t adapter + + + A + + A + + A + + + + Returns the file descriptor number from which captured packets are read, + if a network device was opened with pcap_create() and pcap_activate() or + with pcap_open_live(), or -1, if a ``savefile'' was opened with + pcap_open_offline() + Libpcap specific method + + + A + + A + + + + Set the working mode of the interface p to mode. + Valid values for mode are MODE_CAPT (default capture mode) + and MODE_STAT (statistical mode). See the tutorial + "\ref wpcap_tut9" for details about statistical mode. + WinPcap specific method + + + + + WinPcap specific method for setting the kernel buffer size + associated with this adapter. The old buffer is discarded + when the buffer size is changed. + See http://www.winpcap.org/docs/docs_40_2/html/group__wpcapfunc.html + + + A + + A + + A + + + + Allocate a send queue. + + The size of the queue + A pointer to the allocated buffer + + + + Destroy a send queue. + + A pointer to the queue start address + + + + Add a packet to a send queue. + + A pointer to a queue + The pcap header of the packet to send + The packet data + + + + Send a queue of raw packets to the network. + + + + + + determines if the send operation must be synchronized: + if it is non-zero, the packets are sent respecting the timestamps, + otherwise they are sent as fast as possible + The amount of bytes actually sent. + If it is smaller than the size parameter, an error occurred + during the send. The error can be caused by a driver/adapter + problem or by an inconsistent/bogus send queue. + + + + Base class for all pcap devices + + + + + If Environment.OSVersion.Platform is unix and MonoUnixFound is true + then we can support proper termination of the capture loop + + + A + + + + Low level interface object that contains device specific information + + + + + Handle to an open dump file, not equal to IntPtr.Zero if a dump file is open + + + + + Handle to a pcap adapter, not equal to IntPtr.Zero if an adapter is open + + + + + Number of packets that this adapter should capture + + + + + Fires whenever a new packet is processed, either when the packet arrives + from the network device or when the packet is read from the on-disk file.
+ For network captured packets this event is invoked only when working in "PcapMode.Capture" mode. +
+
+ + + Fires whenever a new pcap statistics is available for this Pcap Device.
+ For network captured packets this event is invoked only when working in "PcapMode.Statistics" mode. +
+
+ + + Fired when the capture process of this pcap device is stopped + + + + + Return a value indicating if the capturing process of this adapter is started + + + + + Maximum time within which the capture thread must join the main thread (on + ) or else the thread is aborted and an exception thrown. + + + + + Low level pcap device values + + + + + Return a value indicating if this adapter is opened + + + + + Gets a value indicating wether pcap dump file is already associated with this device + + + + + Gets the name of the device + + + + + Description of the device + + + + + Return the pcap link layer value of an adapter. + + + + + WinPcap specific property + + + + + The underlying pcap device handle + + + + + The last pcap error associated with this pcap device + + + + + Setup the reflection type and methodinfo for invocation of + Mono.Unix.Native.Syscall.poll() to avoid timeouts when + stopping the capture thread + + + + + Starts the capturing process + + + + + Stops the capture process + Throws an exception if the stop capture timeout is exceeded and the + capture thread was aborted + + + + + Synchronously capture packets on this device. Method blocks forever. + + + + + Synchronously captures packets on this network device. This method will block + until capturing is finished. + + The number of packets to be captured. + -1 means capture indefiniately + + + + The capture thread + + + + + Retrieve the last error string for a given pcap_t* device + + + A + + A + + + + Open the device with class specific options + + + + + Closes this adapter + + + + + Retrieves pcap statistics + + + A + + + + Notify the OnPacketArrival delegates about a newly captured packet + + + A + + + + Notify the delegates that are subscribed to the capture stopped event + + + A + + + + Gets the next packet captured on this device + + The next packet captured on this device + + + + Gets the next packet captured on this device + + A packet reference + A reference to a packet object + + + + Pcap_loop callback method. + + + + + Convert an unmanaged packet into a managed PacketDotNet.RawPacket + + + A + + A + + A + + + + Opens a file for packet writings + + + + + + + Closes the opened dump file + + + + + Flushes all write buffers of the opened dump file + + + + + Writes a packet to the pcap dump file associated with this device. + + + + + Writes a packet to the pcap dump file associated with this device. + + The packet to write + + + + Writes a packet to the pcap dump file associated with this device. + + The packet to write + + + + Compile a kernel level filtering expression, and associate the filter + with this device. For more info on filter expression syntax, see: + http://www.winpcap.org/docs/docs31/html/group__language.html + + The filter expression to compile + + + or unmanaged memory will be leaked + + + Free memory allocated in CompileFilter() + + + A + + + + Returns true if the filter expression was able to be compiled into a + program without errors + + + + + Helper method for ensuring we are running in winpcap. Throws + a PcapWinPcapRequiredException() if not on a windows platform + + + + + Helper method for checking that the adapter is open, throws an + exception with a string of ExceptionString if the device isn't open + + + A + + + + Override the default ToString() implementation + + + A + + + + Managed representation of the unmanaged pcap_addr structure + + + + + The address value of this PcapAddress, null if none is present + + + + + Netmask of this PcapAddress, null if none is present + + + + + Broadcast address of this PcapAddress, null if none is present + + + + + Destination address, null if the interface isn't a point-to-point interface + + + + + ToString override + + + A + + + + Container class that represents either an ip address or a mac address + An analog to the 'sockaddr_' series of structures + + + + + Create a Sockaddr from a PhysicalAddress which is presumed to + be a hardware address + + + A + + + + Types of addresses a Sockaddr can represent + + + + + Address represents an ipv4 or ipv6 address + + + + + Address represents a physical hardware address eg. a ethernet mac address + + + + + Unknown address type + + + + + Address type represented by this Sockaddr + + + + + If type == AF_INET_AF_INET6 + + + + + If type == HARDWARE + + + + + Address family + + + + + ToString override + + + A + + + + managed version of struct pcap_if + NOTE: we can't use pcap_if directly because the class contains + a pointer to pcap_if that will be freed when the + device memory is freed, so instead convert the unmanaged structure + to a managed one to avoid this issue + + + + + Name of the interface. Used internally when passed to pcap_open_live() + + + + + Human readable interface name derived from System.Net.NetworkInformation.NetworkInterface.Name + + + + + Text description of the interface as given by pcap/winpcap + + + + + Gateway address of this device + NOTE: May only be available on Windows + + + + + Addresses associated with this device + + + + + Pcap interface flags + + + + + MacAddress of the interface + + + + + ToString override + + + A + + + + A PcapDevice or dumpfile is not ready for capture operations. + + + + + Adapter statistics, received, dropped packet counts etc + + + + + Retrieve pcap statistics from the adapter + + + pcap_t* for the adapter + A + + + + Number of packets received + + + + + Number of packets dropped + + + + + Number of interface dropped packets + + + + + ToString override + + + A + + + + thrown when pcap_stats() reports an error + + + + + string constructor + + + A + + + + Exception thrown when a WinPcap extension method is called from + a non-Windows platform + + + + + string constructor + + + A + + + + Thrown when a method not supported on an offline device is called + + + + + string constructor + + + A + + + + The mode used when opening a device + + + + + Promiscuous mode. + Instructs the OS that we want to receive all packets, even those not + intended for the adapter. On non-switched networks this can result in + a large amount of addtional traffic. + NOTE: Devices in this mode CAN be detected via the network + + + + + Not promiscuous mode + + + + + Capture event arguments + + + + + Constructor + + + A + + A + + + + Packet that was captured + + + + + Device this EventArgs was generated for + + + + + Event that contains statistics mode data + NOTE: WinPcap only + + + + + Constructor for a statistics mode event + + + A + + A + + + + Statistics data for this event + + + + + The types of transmit modes allowed by the WinPcap specific send queue + implementation + + + + + Packets are sent as fast as possible + + + + + Packets are synchronized in the kernel with a high precision timestamp + + + + + The working mode of a Pcap device + + + + + Set a Pcap device to capture packets, Capture mode + + + + + Set a Pcap device to report statistics. +
+ Statistics mode is only supported in WinPcap +
+
+ + + List of available Pcap Interfaces. + + + + + Represents a strongly typed, read-only list of PcapDevices. + + + + The name or description of the pcap interface to get. + + + + Method to retrieve this classes singleton instance + + + + + Caution: Use the singlton instance unless you know why you need to call this. + One use is for multiple filters on the same physical device. To apply multiple + filters open the same physical device multiple times, one for each + filter by calling this routine and picking the same device out of each list. + + + A + + + + Retrieve a list of the current PcapDevices + + + A + + + + Refresh the device list + + + + + Resolves MAC addresses from IP addresses using the Address Resolution Protocol (ARP) + + + + + Constructs a new ARP Resolver + + + + + Constructs a new ARP Resolver + + The name of the network device on which this resolver sends its ARP packets + + + + The source MAC address to be used for ARP requests. + If null, the local device MAC address is used + + + + + The source IP address to be used for ARP requests. + If null, the local device IP address is used + + + + + The default device name on which to send ARP requests + + + + + Resolves the MAC address of the specified IP address. The 'DeviceName' propery must be set + prior to using this method. + + The IP address to resolve + The MAC address that matches to the given IP address + + + + Resolves the MAC address of the specified IP address + + The IP address to resolve + The local network device name on which to send the ARP request + The MAC address that matches to the given IP address + + + + Status types when capture is stopped + + + + + Capture completed without errors + + + + + Error while capturing + + + + A delegate for Packet Arrival events + + + + A delegate for delivering network statistics when using winpcap in + statistics mode + + + + + A delegate for notifying of a capture stopped event + + +
+
diff --git a/utils/EQExtractor2/EQExtractor2/UserOptions.Designer.cs b/utils/EQExtractor2/EQExtractor2/UserOptions.Designer.cs new file mode 100644 index 000000000..372cb8fcd --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/UserOptions.Designer.cs @@ -0,0 +1,153 @@ +namespace EQExtractor2 +{ + partial class UserOptions + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.label1 = new System.Windows.Forms.Label(); + this.PacketDumpViewerProgram = new System.Windows.Forms.TextBox(); + this.ShowDebugWindowOnStartup = new System.Windows.Forms.CheckBox(); + this.ShowTimeStamps = new System.Windows.Forms.CheckBox(); + this.OptionsOKButton = new System.Windows.Forms.Button(); + this.OptionsCancelButton = new System.Windows.Forms.Button(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.EQPacketDebugFilename = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 9); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(107, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Packet Dump Viewer"; + // + // PacketDumpViewerProgram + // + this.PacketDumpViewerProgram.Location = new System.Drawing.Point(125, 6); + this.PacketDumpViewerProgram.Name = "PacketDumpViewerProgram"; + this.PacketDumpViewerProgram.Size = new System.Drawing.Size(390, 20); + this.PacketDumpViewerProgram.TabIndex = 1; + this.toolTip1.SetToolTip(this.PacketDumpViewerProgram, "If not using notepad/wordpad include the full path, e.g. C:\\Program Files (x86)\\N" + + "otepad++\\notepad++.exe"); + // + // ShowDebugWindowOnStartup + // + this.ShowDebugWindowOnStartup.AutoSize = true; + this.ShowDebugWindowOnStartup.Location = new System.Drawing.Point(15, 61); + this.ShowDebugWindowOnStartup.Name = "ShowDebugWindowOnStartup"; + this.ShowDebugWindowOnStartup.Size = new System.Drawing.Size(184, 17); + this.ShowDebugWindowOnStartup.TabIndex = 2; + this.ShowDebugWindowOnStartup.Text = "Show Debug Window On Startup"; + this.ShowDebugWindowOnStartup.UseVisualStyleBackColor = true; + // + // ShowTimeStamps + // + this.ShowTimeStamps.AutoSize = true; + this.ShowTimeStamps.Location = new System.Drawing.Point(15, 84); + this.ShowTimeStamps.Name = "ShowTimeStamps"; + this.ShowTimeStamps.Size = new System.Drawing.Size(205, 17); + this.ShowTimeStamps.TabIndex = 3; + this.ShowTimeStamps.Text = "Include Time Stamps In Packet Dump"; + this.ShowTimeStamps.UseVisualStyleBackColor = true; + // + // OptionsOKButton + // + this.OptionsOKButton.Location = new System.Drawing.Point(125, 122); + this.OptionsOKButton.Name = "OptionsOKButton"; + this.OptionsOKButton.Size = new System.Drawing.Size(75, 23); + this.OptionsOKButton.TabIndex = 4; + this.OptionsOKButton.Text = "OK"; + this.OptionsOKButton.UseVisualStyleBackColor = true; + this.OptionsOKButton.Click += new System.EventHandler(this.OptionsOKButton_Click); + // + // OptionsCancelButton + // + this.OptionsCancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.OptionsCancelButton.Location = new System.Drawing.Point(332, 122); + this.OptionsCancelButton.Name = "OptionsCancelButton"; + this.OptionsCancelButton.Size = new System.Drawing.Size(75, 23); + this.OptionsCancelButton.TabIndex = 5; + this.OptionsCancelButton.Text = "Cancel"; + this.OptionsCancelButton.UseVisualStyleBackColor = true; + this.OptionsCancelButton.Click += new System.EventHandler(this.OptionsCancelButton_Click); + // + // EQPacketDebugFilename + // + this.EQPacketDebugFilename.Location = new System.Drawing.Point(125, 32); + this.EQPacketDebugFilename.Name = "EQPacketDebugFilename"; + this.EQPacketDebugFilename.Size = new System.Drawing.Size(390, 20); + this.EQPacketDebugFilename.TabIndex = 7; + this.toolTip1.SetToolTip(this.EQPacketDebugFilename, "Output file for low level netcode debugging"); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(12, 35); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(83, 13); + this.label2.TabIndex = 6; + this.label2.Text = "Netcode Debug"; + this.toolTip1.SetToolTip(this.label2, "Output file for low level netcode debugging"); + // + // UserOptions + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.OptionsCancelButton; + this.ClientSize = new System.Drawing.Size(527, 166); + this.Controls.Add(this.EQPacketDebugFilename); + this.Controls.Add(this.label2); + this.Controls.Add(this.OptionsCancelButton); + this.Controls.Add(this.OptionsOKButton); + this.Controls.Add(this.ShowTimeStamps); + this.Controls.Add(this.ShowDebugWindowOnStartup); + this.Controls.Add(this.PacketDumpViewerProgram); + this.Controls.Add(this.label1); + this.Name = "UserOptions"; + this.Text = "Options"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button OptionsOKButton; + private System.Windows.Forms.Button OptionsCancelButton; + public System.Windows.Forms.TextBox PacketDumpViewerProgram; + public System.Windows.Forms.CheckBox ShowDebugWindowOnStartup; + public System.Windows.Forms.CheckBox ShowTimeStamps; + private System.Windows.Forms.ToolTip toolTip1; + public System.Windows.Forms.TextBox EQPacketDebugFilename; + private System.Windows.Forms.Label label2; + } +} \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/UserOptions.cs b/utils/EQExtractor2/EQExtractor2/UserOptions.cs new file mode 100644 index 000000000..74877708f --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/UserOptions.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace EQExtractor2 +{ + public partial class UserOptions : Form + { + public UserOptions() + { + InitializeComponent(); + } + + private void OptionsOKButton_Click(object sender, EventArgs e) + { + this.DialogResult = DialogResult.OK; + } + + private void OptionsCancelButton_Click(object sender, EventArgs e) + { + this.DialogResult = DialogResult.Cancel; + } + } +} diff --git a/utils/EQExtractor2/EQExtractor2/UserOptions.resx b/utils/EQExtractor2/EQExtractor2/UserOptions.resx new file mode 100644 index 000000000..df8339b68 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/UserOptions.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/Utils.cs b/utils/EQExtractor2/EQExtractor2/Utils.cs new file mode 100644 index 000000000..3aeb28028 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/Utils.cs @@ -0,0 +1,314 @@ +// +// The BitStream class is a direct C# translation of the class of the same name in the ShowEQ source, so the following +// Copyright notice applies to that: +// +// ShowEQ Distributed under GPL +// http://www.sourceforge.net/projects/seq +// +// Copyright 2004 Zaphod (dohpaz@users.sourceforge.net). +// +// ----------------------------------------------------------- +// +// All other code: +// +// Copyright (C) 2001-2010 EQEMu Development Team (http://eqemulator.net). Distributed under GPL version 2. +// +// + + +using System; +using System.Text; + +namespace MyUtils +{ + public class ByteStream + { + public ByteStream(byte[] NewBuffer) + { + Buffer = NewBuffer; + + BufferPointer = 0; + } + + public uint Length() + { + return (uint)Buffer.Length; + } + + public UInt32 ReadUInt32() + { + UInt32 Value = BitConverter.ToUInt32(Buffer, BufferPointer); + + BufferPointer += 4; + + return Value; + } + + public Int32 ReadInt32() + { + Int32 Value = BitConverter.ToInt32(Buffer, BufferPointer); + + BufferPointer += 4; + + return Value; + } + + public float ReadSingle() + { + float Value = BitConverter.ToSingle(Buffer, BufferPointer); + + BufferPointer += 4; + + return Value; + } + public UInt16 ReadUInt16() + { + UInt16 Value = BitConverter.ToUInt16(Buffer, BufferPointer); + + BufferPointer += 2; + + return Value; + } + + public Int16 ReadInt16() + { + Int16 Value = BitConverter.ToInt16(Buffer, BufferPointer); + + BufferPointer += 2; + + return Value; + } + public byte ReadByte() + { + return Buffer[BufferPointer++]; + } + + public byte[] ReadBytes(int Count) + { + byte[] Slice = new byte[Count]; + + Array.Copy(Buffer, BufferPointer, Slice, 0, Count); + + BufferPointer += Count; + + return Slice; + } + + public void SkipBytes(int Count) + { + BufferPointer += Count; + } + + public string ReadString(bool Escape) + { + string Result = ""; + + while(Buffer[BufferPointer] != 0) + { + if (Escape && ((char)Buffer[BufferPointer] == '\'')) + Result += '\\'; + + Result += (char)Buffer[BufferPointer++]; + } + + ++BufferPointer; + + return Result; + } + + public string ReadFixedLengthString(int Length, bool Escape) + { + int StartingPosition = BufferPointer; + + string Result = ""; + + while (Buffer[BufferPointer] != 0) + { + if (Escape && ((char)Buffer[BufferPointer] == '\'')) + Result += '\\'; + + Result += (char)Buffer[BufferPointer++]; + } + + BufferPointer = StartingPosition + Length; + + return Result; + } + + public void SetPosition(int NewPosition) + { + BufferPointer = NewPosition; + } + + public int GetPosition() + { + return BufferPointer; + } + + public byte[] Buffer; + + int BufferPointer; + } + + class BitStream + { + public BitStream(byte[] Data, UInt32 Length) + { + m_data = Data; + TotalBits = Length * 8; + Reset(); + } + public void Reset() + { + CurrentBit = 0; + } + public UInt32 readUInt(int bitCount) + { + // Make sure we have the bits first. + if (CurrentBit + bitCount > TotalBits) + { + return 0; + } + + UInt32 currentByte = (CurrentBit >> 3); + UInt32 Out = 0; + + // Partial bytes in the lead and end. Full bytes in the middle. + Int32 leadPartialBitCount = 8 - ((Int32)CurrentBit % 8); + UInt32 middleByteCount; + Int32 tailPartialBitCount; + + if (leadPartialBitCount == 8) + { + // Lead partial is a byte. So just put it in the middle. + leadPartialBitCount = 0; + } + + if (leadPartialBitCount > bitCount) + { + // All the bits we need are in the lead partial. Note that when + // the lead partial is byte aligned, this won't process it. Instead + // it will be handled by the tailPartialBitCount. + Out = m_data[currentByte] & (((UInt32)1 << leadPartialBitCount) - 1); + CurrentBit += (UInt32)bitCount; + + return Out >> (leadPartialBitCount - bitCount); + } + else + { + // Spanning multiple bytes. leadPartialBitCount is correct. + // Calculate middle and tail. + middleByteCount = ((UInt32)bitCount - (UInt32)leadPartialBitCount) / 8; + tailPartialBitCount = + bitCount - ((Int32)leadPartialBitCount + (Int32)middleByteCount*8); + } + + if (leadPartialBitCount > 0) + { + // Pull in partial from the lead byte + Out |= m_data[currentByte] & (UInt32)((1 << leadPartialBitCount) - 1); + currentByte++; + } + + // Middle + for (int i=0; i 0) + { + Out = (UInt32)((Out << tailPartialBitCount) | + (m_data[currentByte] >> (8 - tailPartialBitCount))); + } + + // Update the current bit + CurrentBit += (UInt32)bitCount; + + return Out; + } + + public Int32 readInt(Int32 bitCount) + { + // Sign + UInt32 sign = readUInt(1); + Int32 retval = (Int32)readUInt(bitCount - 1); + + return retval * ((sign == 1) ? -1 : 1); + } + + + private byte[] m_data; + private UInt32 TotalBits; + private UInt32 CurrentBit; + + } + class Utils + { + public static string HexDump(byte[] bytes) + { + StringBuilder Dump = new StringBuilder(); + + string Hex = "", Ascii = ""; + + int Offset = 0, i = 0, Length = bytes.Length; + + while (i < Length) + { + Hex += bytes[i].ToString("x2") + " "; + + Ascii += (((bytes[i] >= 32) && (bytes[i] <= 126)) ? ((char)bytes[i]).ToString() : "."); + + if (((++i % 16) == 0) || (i == Length)) + { + Dump.AppendFormat("{0:000} | {1} | {2}\r\n", Offset, Hex.PadRight(48), Ascii); + + Hex = Ascii = ""; + + Offset = i; + } + } + return Dump.ToString(); + } + + public static string ReadNullTerminatedString(byte[] Buffer, int Offset, int MaxSize, bool Escape) + { + string Result = ""; + + for (int i = 0; i < MaxSize && Buffer[Offset + i] != 0; ++i) + { + if(Escape && ((char)Buffer[Offset + i] == '\'')) + Result += '\\'; + + Result += (char)Buffer[Offset + i]; + } + + return Result; + } + + public static string MakeCleanName(string Name) + { + StringBuilder CleanName = new StringBuilder(); + + foreach(char c in Name) + if(!Char.IsDigit(c)) + CleanName.Append(c); + + return CleanName.ToString(); + } + + public static float EQ19ToFloat(Int32 EQ19Value) + { + if ((EQ19Value & 0x40000) > 0) + EQ19Value = -(0x7FFFF - EQ19Value + 1); + + return (float)EQ19Value / (float)(1 << 3); + } + + + } + + +} diff --git a/utils/EQExtractor2/EQExtractor2/app.config b/utils/EQExtractor2/EQExtractor2/app.config new file mode 100644 index 000000000..7d3bcf9ad --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/app.config @@ -0,0 +1,21 @@ + + + + +
+ + + + + + notepad.exe + + + False + + + False + + + + diff --git a/utils/EQExtractor2/EQExtractor2/patch_Aug04-2011.conf b/utils/EQExtractor2/EQExtractor2/patch_Aug04-2011.conf new file mode 100644 index 000000000..cdfc5079a --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_Aug04-2011.conf @@ -0,0 +1,616 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x0000a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x5d32 # +OP_ApproveWorld=0x1f87 # +OP_LogServer=0x44ae # +OP_SendCharInfo=0x1513 # +OP_ExpansionInfo=0x1771 # +OP_GuildsList=0x5b0b # +OP_EnterWorld=0x1190 # +OP_PostEnterWorld=0x4d5c # +OP_World_Client_CRC1=0x4552 # +OP_World_Client_CRC2=0x3e50 # +OP_SendSpellChecksum=0x0000 # +OP_SendSkillCapsChecksum=0x0000 # + +# Character Select Related: +OP_DeleteCharacter=0x0000 # +OP_CharacterCreateRequest=0x0000 # +OP_CharacterCreate=0x0000 # +OP_RandomNameGenerator=0x0000 # +OP_ApproveName=0x0000 # + +OP_MOTD=0x097f # +OP_SetChatServer=0x0360 # +OP_SetChatServer2=0x4399 # +OP_ZoneServerInfo=0x710e # +OP_WorldComplete=0x3aef # +OP_WorldUnknown001=0x0000 # +OP_FloatListThing=0x33f2 # + +# Reasons for Disconnect: +OP_ZoneUnavail=0x66e4 # This is not the right opcode. Produces a 'Your character is inaccessible' message. +OP_WorldClientReady=0x3f24 # +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # +OP_ZoneEntry=0x02d6 # +OP_ReqNewZone=0x1c36 # +OP_NewZone=0x0254 # +OP_ZoneSpawns=0x0000 # +OP_PlayerProfile=0x6022 # +OP_TimeOfDay=0x6015 # +OP_LevelUpdate=0x0000 # +OP_Stamina=0x0000 # +OP_RequestClientZoneChange=0x18ea # +OP_ZoneChange=0x5bd9 # +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x1a58 # +OP_TributeUpdate=0x4895 # +OP_TributeTimer=0x0000 # +OP_TaskDescription=0x0000 # +OP_TaskActivity=0x0000 # +OP_CompletedTasks=0x0000 # +OP_Weather=0x4658 # +OP_SendAATable=0x1d99 # +OP_UpdateAA=0x0000 # +OP_RespondAA=0x0000 # +OP_ReqClientSpawn=0x47e7 # +OP_SpawnDoor=0x6cfe # +OP_GroundSpawn=0x442a # +OP_SendZonepoints=0x5851 # +OP_SendAAStats=0x0000 # +OP_WorldObjectsSent=0x7b73 # +OP_BlockedBuffs=0x664a # +OP_SendExpZonein=0x54e8 # +OP_SendTributes=0x0000 # +OP_TributeInfo=0x0000 # +OP_SendGuildTributes=0x0000 # +OP_AAExpUpdate=0x0000 # +OP_ExpUpdate=0x0000 # +OP_HPUpdate=0x6967 # Was 0x6967 - Causing crash ? +OP_ManaChange=0x0000 # +OP_TGB=0x0000 # +OP_SpecialMesg=0x156c # +OP_GuildMemberList=0x51bc # +OP_GuildMOTD=0x0000 # +OP_CharInventory=0x307d # +OP_WearChange=0x0000 # +OP_ClientUpdate=0x7062 # +OP_ClientReady=0x6cdc # +OP_SetServerFilter=0x0000 # + +# Guild Opcodes +OP_GetGuildMOTD=0x0000 # +OP_GetGuildMOTDReply=0x0000 # +OP_GuildMemberUpdate=0x0000 # +OP_GuildInvite=0x0000 # +OP_GuildRemove=0x0000 # +OP_GuildPeace=0x0000 # +OP_SetGuildMOTD=0x0000 # +OP_GuildList=0x5b0b # +OP_GuildWar=0x0000 # +OP_GuildLeader=0x0000 # +OP_GuildDelete=0x0000 # +OP_GuildInviteAccept=0x0000 # +OP_GuildDemote=0x0000 # +OP_GuildPublicNote=0x0000 # +OP_GuildManageBanker=0x0000 # +OP_GuildBank=0x0000 # +OP_SetGuildRank=0x0000 # +OP_GuildUpdateURLAndChannel=0x0000 # +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x0000 # +OP_GMBecomeNPC=0x0000 # +OP_GMZoneRequest=0x6f79 # +OP_GMZoneRequest2=0x02d6 # +OP_GMGoto=0x0000 # +OP_GMSearchCorpse=0x0000 # +OP_GMHideMe=0x0000 # +OP_GMDelCorpse=0x0000 # +OP_GMApproval=0x0000 # +OP_GMToggle=0x0000 # +OP_GMSummon=0x0000 # +OP_GMEmoteZone=0x0000 # +OP_GMEmoteWorld=0x0000 # +OP_GMFind=0x0000 # +OP_GMKick=0x0000 # +OP_GMKill=0x0000 # +OP_GMNameChange=0x0000 # +OP_GMLastName=0x0000 # + +OP_InspectAnswer=0x0000 # +OP_BeginCast=0x0d5a # +OP_BuffFadeMsg=0x50c2 # +OP_ConsentResponse=0x0000 # +OP_MemorizeSpell=0x0000 # +OP_SwapSpell=0x0000 # +OP_CastSpell=0x7286 # +OP_Consider=0x0000 # +OP_FormattedMessage=0x32c6 # +OP_SimpleMessage=0x0000 # C 0x0000 +OP_Buff=0x0000 # +OP_Illusion=0x0000 # +OP_MoneyOnCorpse=0x0000 # +OP_RandomReply=0x0000 # +OP_DenyResponse=0x0000 # +OP_SkillUpdate=0x0000 # +OP_GMTrainSkillConfirm=0x0000 # +OP_RandomReq=0x0000 # +OP_Death=0x49b6 # +OP_Bind_Wound=0x0000 # +OP_GMTraining=0x0000 # +OP_GMEndTraining=0x0000 # +OP_GMTrainSkill=0x0000 # +OP_Animation=0x3ea4 # Was 0x0000 +OP_Begging=0x0000 # +OP_Consent=0x0000 # +OP_ConsentDeny=0x0000 # +OP_AutoFire=0x0000 # +OP_PetCommands=0x0000 # +OP_DeleteSpell=0x0000 # +OP_Surname=0x0000 # +OP_ClearSurname=0x0000 # +OP_FaceChange=0x0000 # +OP_SenseHeading=0x0000 # +OP_Action=0x4200 # 0x4200 +OP_ConsiderCorpse=0x0000 # +OP_HideCorpse=0x0000 # +OP_Bug=0x0000 # +OP_Feedback=0x0000 # +OP_Report=0x0000 # +OP_Damage=0x7519 # +OP_ChannelMessage=0x2e79 # +OP_Assist=0x0000 # +OP_AssistGroup=0x0000 # +OP_MoveCoin=0x0000 # +OP_ZonePlayerToBind=0x0000 # +OP_KeyRing=0x0000 # +OP_WhoAllRequest=0x0000 # +OP_WhoAllResponse=0x0000 # +OP_FriendsWho=0x0000 # +OP_ConfirmDelete=0x0000 # +OP_Logout=0x517b # +OP_Rewind=0x0000 # +OP_TargetCommand=0x0000 # +OP_InspectRequest=0x0000 # +OP_Hide=0x0000 # +OP_Jump=0x0000 # +OP_Camp=0x0a1d # Was 0x1436 +OP_Emote=0x0000 # +OP_SetRunMode=0x0000 # +OP_BankerChange=0x0000 # +OP_TargetMouse=0x5f5e # +OP_MobHealth=0x0000 # +OP_InitialMobHealth=0x0000 # +OP_TargetHoTT=0x0000 # +OP_TargetBuffs=0x0000 # +OP_BuffCreate=0x0000 +OP_DeleteSpawn=0x725a # +OP_AutoAttack=0x1df9 # +OP_AutoAttack2=0x0000 # +OP_Consume=0x0000 # +OP_MoveItem=0x0000 # +OP_DeleteItem=0x0000 # +OP_DeleteCharge=0x0000 # +OP_ItemPacket=0x7c87 # +OP_ItemLinkResponse=0x0000 # +OP_ItemLinkClick=0x0000 # +OP_NewSpawn=0x0000 # +OP_Track=0x0000 # +OP_TrackTarget=0x0000 # +OP_TrackUnknown=0x0000 # +OP_ClickDoor=0x3154 # +OP_MoveDoor=0x470e # +OP_EnvDamage=0x0000 # +OP_BoardBoat=0x0000 # +OP_Forage=0x0000 # +OP_LeaveBoat=0x0000 # +OP_ControlBoat=0x0000 # +OP_SafeFallSuccess=0x0000 # +OP_RezzComplete=0x0000 # +OP_RezzRequest=0x0000 # +OP_RezzAnswer=0x0000 # +OP_Shielding=0x0000 # +OP_RequestDuel=0x0000 # +OP_MobRename=0x0000 # +OP_AugmentItem=0x0000 # +OP_WeaponEquip1=0x0000 # +OP_WeaponEquip2=0x0000 # +OP_WeaponUnequip2=0x0000 # +OP_ApplyPoison=0x0000 # +OP_Save=0x1436 # +OP_TestBuff=0x0000 # +OP_CustomTitles=0x0000 # +OP_Split=0x0000 # +OP_YellForHelp=0x0000 # +OP_LoadSpellSet=0x0000 # +OP_Bandolier=0x0000 # +OP_PotionBelt=0x0000 # +OP_DuelResponse=0x0000 # +OP_DuelResponse2=0x0000 # +OP_SaveOnZoneReq=0x2913 # +OP_ReadBook=0x0000 # +OP_Dye=0x0000 # +OP_InterruptCast=0x0000 # +OP_AAAction=0x0000 # +OP_LeadershipExpToggle=0x0000 # +OP_LeadershipExpUpdate=0x0000 # +OP_PurchaseLeadershipAA=0x0000 # +OP_UpdateLeadershipAA=0x0000 # +OP_MarkNPC=0x0000 # +OP_ClearNPCMarks=0x0000 # +OP_DoGroupLeadershipAbility=0x0000 # +OP_GroupLeadershipAAUpdate=0x0000 # +OP_DelegateAbility=0x0000 # +OP_SetGroupTarget=0x0000 # +OP_Charm=0x0000 # +OP_Stun=0x0000 # +OP_SendFindableNPCs=0x0786 # +OP_FindPersonRequest=0x0000 # +OP_FindPersonReply=0x0000 # +OP_Sound=0x0000 # +OP_PetBuffWindow=0x0000 # +OP_LevelAppearance=0x0000 # +OP_Translocate=0x0000 # +OP_Sacrifice=0x0000 # +OP_PopupResponse=0x0000 # +OP_OnLevelMessage=0x0000 # +OP_AugmentInfo=0x0000 # +OP_Petition=0x0000 # +OP_SomeItemPacketMaybe=0x4200 # +OP_SomeItemPacketMaybe=0x0000 # +OP_PVPStats=0x0000 # +OP_PVPLeaderBoardRequest=0x0000 # +OP_PVPLeaderBoardReply=0x0000 # +OP_PVPLeaderBoardDetailsRequest=0x0000 # +OP_PVPLeaderBoardDetailsReply=0x0000 # +OP_RestState=0x0000 # +OP_RespawnWindow=0x0000 # +OP_DisciplineTimer=0x0000 # +OP_LDoNButton=0x0000 # +OP_SetStartCity=0x0000 # +OP_VoiceMacroIn=0x0000 # +OP_VoiceMacroOut=0x0000 # +OP_ItemViewUnknown=0x0000 # +OP_VetRewardsAvaliable=0x0000 # +OP_VetClaimRequest=0x0000 # +OP_VetClaimReply=0x0000 # +OP_CrystalCountUpdate=0x0000 # +OP_DisciplineUpdate=0x0000 # +OP_BecomeCorpse=0x0000 # +OP_Action2=0x0000 # +OP_MobUpdate=0x4656 # +OP_NPCMoveUpdate=0x38e0 # +OP_CameraEffect=0x0000 # +OP_SpellEffect=0x0000 # + +OP_DzQuit=0x0000 +OP_DzListTimers=0x0000 +OP_DzAddPlayer=0x0000 +OP_DzRemovePlayer=0x0000 +OP_DzSwapPlayer=0x0000 +OP_DzMakeLeader=0x0000 +OP_DzPlayerList=0x0000 +OP_DzJoinExpeditionConfirm=0x0000 +OP_DzJoinExpeditionReply=0x0000 +OP_DzExpeditionInfo=0x0000 +OP_DzMemberStatus=0x0000 +OP_DzLeaderStatus=0x0000 +OP_DzExpeditionEndsWarning=0x0000 +OP_DzExpeditionList=0x0000 +OP_DzMemberList=0x0000 +OP_DzCompass=0x0000 +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # +OP_ManaUpdate=0x0ff4 # +OP_EnduranceUpdate=0x0000 # +OP_MobManaUpdate=0x1912 # +OP_MobEnduranceUpdate=0x0000 # + +# Looting +OP_LootRequest=0x0000 # +OP_EndLootRequest=0x0000 # +OP_LootItem=0x0000 # +OP_LootComplete=0x0000 # + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x0000 # +OP_TraderDelItem=0x0000 # +OP_BecomeTrader=0x0000 # +OP_TraderShop=0x0000 # +OP_Trader=0x0000 # +OP_TraderBuy=0x0000 # +OP_Barter=0x0000 # +OP_ShopItem=0x0000 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # +OP_TraderItemUpdate=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x0000 # +OP_TradeAcceptClick=0x0000 # +OP_TradeRequestAck=0x0000 # +OP_TradeCoins=0x0000 # +OP_FinishTrade=0x0000 # +OP_CancelTrade=0x0000 # +OP_TradeMoneyUpdate=0x0000 # +OP_MoneyUpdate=0x0000 # +OP_TradeBusy=0x0000 # + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x0000 # +OP_FinishWindow2=0x0000 # + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x0000 # +OP_ItemVerifyReply=0x0000 # + +# merchant crap +OP_ShopPlayerSell=0x0000 # +OP_ShopRequest=0x58c5 # +OP_ShopEnd=0x3753 # +OP_ShopEndConfirm=0x0000 # +OP_ShopPlayerBuy=0x0000 # +OP_ShopDelItem=0x0000 # + +# tradeskill stuff: +OP_ClickObject=0x0000 # +OP_ClickObjectAction=0x0000 # +OP_ClearObject=0x0000 # +OP_RecipeDetails=0x0000 # +OP_RecipesFavorite=0x0000 # +OP_RecipesSearch=0x0000 # +OP_RecipeReply=0x0000 # +OP_RecipeAutoCombine=0x0000 # +OP_TradeSkillCombine=0x0000 # + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x0000 # +OP_OpenTributeMaster=0x0000 # +OP_SelectTribute=0x0000 # +OP_TributeItem=0x0000 # +OP_TributeMoney=0x0000 # +OP_TributeToggle=0x0000 # +OP_TributePointUpdate=0x0000 # +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x0000 # +OP_AdventureFinish=0x0000 # +OP_AdventureInfoRequest=0x0000 # +OP_AdventureInfo=0x0000 # +OP_AdventureRequest=0x0000 # +OP_AdventureDetails=0x0000 # +OP_AdventureData=0x0000 # +OP_AdventureUpdate=0x0000 # +OP_AdventureMerchantRequest=0x0000 # +OP_AdventureMerchantResponse=0x0000 # +OP_AdventureMerchantPurchase=0x0000 # +OP_AdventureMerchantSell=0x0000 # +OP_AdventurePointsUpdate=0x0000 # +OP_AdventureStatsRequest=0x0000 # +OP_AdventureStatsReply=0x0000 # +OP_AdventureLeaderboardRequest=0x0000 # +OP_AdventureLeaderboardReply=0x0000 # + +# Group Opcodes +OP_GroupDisband=0x0000 # +OP_GroupInvite=0x0000 # +OP_GroupFollow=0x0000 # +OP_GroupUpdate=0x0000 # +OP_GroupUpdateB=0x0000 # +OP_GroupCancelInvite=0x0000 # +OP_GroupAcknowledge=0x0000 # +OP_GroupDelete=0x0000 # +OP_CancelInvite=0x0000 # +OP_GroupFollow2=0x0000 # +OP_GroupInvite2=0x0000 # +OP_GroupDisbandYou=0x0000 # +OP_GroupDisbandOther=0x0000 # +OP_GroupLeaderChange=0x0000 # +OP_GroupRoles=0x0000 # + +# LFG/LFP Opcodes +OP_LFGCommand=0x0000 # +OP_LFGGetMatchesRequest=0x0000 # +OP_LFGGetMatchesResponse=0x0000 # +OP_LFPGetMatchesRequest=0x0000 # +OP_LFPGetMatchesResponse=0x0000 # +OP_LFPCommand=0x0000 # +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x0000 # +OP_RaidUpdate=0x0000 # +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x0000 # +OP_CombatAbility=0x0000 # +OP_SenseTraps=0x0000 # +OP_PickPocket=0x0000 # +OP_DisarmTraps=0x0000 # +OP_Disarm=0x0000 # +OP_Sneak=0x0000 # +OP_Fishing=0x0000 # +OP_InstillDoubt=0x0000 # +OP_FeignDeath=0x0000 # +OP_Mend=0x0000 # +OP_LDoNOpen=0x0000 # + +# Task packets +OP_TaskActivityComplete=0x0000 # +OP_TaskMemberList=0x0000 # +OP_OpenNewTasksWindow=0x0000 # +OP_AvaliableTask=0x0000 # +OP_AcceptNewTask=0x0000 # +OP_TaskHistoryRequest=0x0000 # +OP_TaskHistoryReply=0x0000 # +OP_CancelTask=0x0000 # +OP_DeclineAllTasks=0x0000 # + +# Title opcodes +OP_NewTitlesAvailable=0x0000 # +OP_RequestTitles=0x0000 # +OP_SendTitleList=0x0000 # +OP_SetTitle=0x0000 # +OP_SetTitleReply=0x0000 # + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_IncreaseStats=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x0000 # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x0000 # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0000 # +OP_Login=0x0000 # +OP_ServerListRequest=0x0000 # +OP_PlayEverquestRequest=0x0000 # +OP_PlayEverquestResponse=0x0000 # +OP_ChatMessage=0x0000 # +OP_LoginAccepted=0x0000 # +OP_ServerListResponse=0x0000 # +OP_Poll=0x0000 # +OP_EnterChat=0x0000 # +OP_PollResponse=0x0000 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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_MercenaryDataRequest=0x367f +OP_MercenaryDataResponse=0x6a9b +OP_MercenaryHire=0x3671 +OP_MercenaryAssign=0x4be6 +OP_MercenaryTimer=0x6364 +OP_MercenaryUnknown1=0x2ef8 diff --git a/utils/EQExtractor2/EQExtractor2/patch_August15-2012.conf b/utils/EQExtractor2/EQExtractor2/patch_August15-2012.conf new file mode 100644 index 000000000..91dce281a --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_August15-2012.conf @@ -0,0 +1,633 @@ +# OPCodes not used by EQExtractor2 are most likely not correct. +# +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x0000 # +OP_ApproveWorld=0x0000 # +OP_LogServer=0x0000 # +OP_SendCharInfo=0x0000 # +OP_ExpansionInfo=0x0000 # +OP_GuildsList=0x0000 # +OP_EnterWorld=0x0000 # +OP_PostEnterWorld=0x0000 # +OP_World_Client_CRC1=0x0000 # +OP_World_Client_CRC2=0x0000 # +OP_SendSpellChecksum=0x0000 # +OP_SendSkillCapsChecksum=0x0000 # + +# Character Select Related: +OP_DeleteCharacter=0x0000 # +OP_CharacterCreateRequest=0x0000 # +OP_CharacterCreate=0x0000 # +OP_RandomNameGenerator=0x0000 # +OP_ApproveName=0x0000 # + +OP_MOTD=0x0000 # +OP_SetChatServer=0x0000 # +OP_SetChatServer2=0x0000 # +OP_ZoneServerInfo=0x0000 # +OP_WorldComplete=0x0000 # +OP_WorldUnknown001=0x0000 # +OP_FloatListThing=0x0000 # + +# Reasons for Disconnect: +OP_ZoneUnavail=0x0000 # +OP_WorldClientReady=0x0000 # Testing VoA 0x3f24 +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # +OP_ZoneEntry=0x4938 # +OP_ReqNewZone=0x0000 # +OP_NewZone=0x3401 # +OP_ZoneSpawns=0x0000 # +OP_PlayerProfile=0x078e # +OP_TimeOfDay=0x0000 # +OP_LevelUpdate=0x0000 # +OP_Stamina=0x0000 # +OP_RequestClientZoneChange=0x0000 # +OP_ZoneChange=0x0000 # + +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x0000 # +OP_ChangeSize=0x0000 # +OP_TributeUpdate=0x0000 # +OP_TributeTimer=0x0000 # Testing VoA 0x1525 +OP_TaskDescription=0x0000 # +OP_TaskActivity=0x0000 # +OP_CompletedTasks=0x0000 # +OP_Weather=0x0000 # +OP_SendAATable=0x40e8 # Testing VoA 0x6a7e +OP_UpdateAA=0x0000 # Testing VoA 0x5363 +OP_RespondAA=0x0000 # Testing VoA 0x0643 or maybe 0x7bf6 +OP_ReqClientSpawn=0x0000 # +OP_SpawnDoor=0x0e71 # +OP_GroundSpawn=0x6618 # +OP_SendZonepoints=0x0c54 # +OP_SendAAStats=0x0000 # +OP_WorldObjectsSent=0x0000 # +OP_BlockedBuffs=0x0000 # +OP_RemoveBlockedBuffs=0x0000 # +OP_ClearBlockedBuffs=0x0000 # +OP_SendExpZonein=0x0000 # +OP_SendTributes=0x0000 # +OP_TributeInfo=0x0000 # +OP_SendGuildTributes=0x0000 # +OP_AAExpUpdate=0x0000 # +OP_ExpUpdate=0x0000 # Testing VoA 0x0555 +OP_HPUpdate=0x0000 # +OP_ManaChange=0x0000 # +OP_TGB=0x0000 # +OP_SpecialMesg=0x0000 # +OP_GuildMemberList=0x0000 # +OP_GuildMOTD=0x0000 # Testing VoA 0x0a1d +OP_CharInventory=0x0000 # Testing VoA 0x6cfe +OP_WearChange=0x0000 # +OP_ClientUpdate=0x0000 # +OP_ClientReady=0x0000 # +OP_SetServerFilter=0x0000 # + +# Guild Opcodes +OP_GetGuildMOTD=0x0000 # +OP_GetGuildMOTDReply=0x0000 # +OP_GuildMemberUpdate=0x0000 # +OP_GuildInvite=0x0000 # +OP_GuildRemove=0x0000 # +OP_GuildPeace=0x0000 # +OP_SetGuildMOTD=0x0000 # +OP_GuildList=0x0000 # +OP_GuildWar=0x0000 # +OP_GuildLeader=0x0000 # +OP_GuildDelete=0x0000 # +OP_GuildInviteAccept=0x0000 # +OP_GuildDemote=0x0000 # +OP_GuildPublicNote=0x0000 # +OP_GuildManageBanker=0x0000 # +OP_GuildBank=0x0000 # +OP_SetGuildRank=0x0000 # +OP_GuildUpdateURLAndChannel=0x0000 # +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +OP_GuildStatus=0x0000 # +OP_GuildCreate=0x0000 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x0000 # +OP_GMBecomeNPC=0x0000 # +OP_GMZoneRequest=0x0000 # +OP_GMZoneRequest2=0x0000 # +OP_GMGoto=0x0000 # +OP_GMSearchCorpse=0x0000 # +OP_GMHideMe=0x0000 # +OP_GMDelCorpse=0x0000 # +OP_GMApproval=0x0000 # +OP_GMToggle=0x0000 # +OP_GMSummon=0x0000 # +OP_GMEmoteZone=0x0000 # +OP_GMEmoteWorld=0x0000 # +OP_GMFind=0x0000 # +OP_GMKick=0x0000 # +OP_GMKill=0x0000 # +OP_GMNameChange=0x0000 # +OP_GMLastName=0x0000 # + +OP_InspectAnswer=0x0000 # +OP_BeginCast=0x0000 # +OP_BuffFadeMsg=0x0000 # +OP_ConsentResponse=0x0000 # +OP_MemorizeSpell=0x0000 # +OP_SwapSpell=0x0000 # +OP_CastSpell=0x0000 # +OP_Consider=0x0000 # +OP_FormattedMessage=0x0000 # +OP_SimpleMessage=0x0000 # +OP_Buff=0x0000 # +OP_Illusion=0x0000 # +OP_MoneyOnCorpse=0x0000 # +OP_RandomReply=0x0000 # +OP_DenyResponse=0x0000 # +OP_SkillUpdate=0x0000 # +OP_GMTrainSkillConfirm=0x0000 # +OP_RandomReq=0x0000 # +OP_Death=0x0000 # +OP_Bind_Wound=0x0000 # +OP_GMTraining=0x0000 # +OP_GMEndTraining=0x0000 # +OP_GMTrainSkill=0x0000 # +OP_Animation=0x0000 # +OP_Begging=0x0000 # +OP_Consent=0x0000 # +OP_ConsentDeny=0x0000 # +OP_AutoFire=0x0000 # +OP_PetCommands=0x0000 # +OP_DeleteSpell=0x0000 # +OP_Surname=0x0000 # +OP_ClearSurname=0x0000 # +OP_FaceChange=0x0000 # +OP_SenseHeading=0x0000 # +OP_Action=0x0000 # +OP_ConsiderCorpse=0x0000 # +OP_HideCorpse=0x0000 # +OP_CorpseDrag=0x0000 # +OP_CorpseDrop=0x0000 # +OP_Bug=0x0000 # +OP_Feedback=0x0000 # +OP_Report=0x0000 # +OP_Damage=0x0000 # +OP_ChannelMessage=0x0000 # +OP_Assist=0x0000 # +OP_AssistGroup=0x0000 # +OP_MoveCoin=0x0000 # +OP_ZonePlayerToBind=0x0000 # +OP_KeyRing=0x0000 # +OP_WhoAllRequest=0x0000 # +OP_WhoAllResponse=0x0000 # +OP_FriendsWho=0x0000 # +OP_ConfirmDelete=0x0000 # +OP_Logout=0x0000 # +OP_Rewind=0x0000 # +OP_TargetCommand=0x0000 # +OP_InspectRequest=0x0000 # +OP_Hide=0x0000 # +OP_Jump=0x0000 # +OP_Camp=0x0000 # +OP_Emote=0x0000 # +OP_SetRunMode=0x0000 # +OP_BankerChange=0x0000 # +OP_TargetMouse=0x0000 # +OP_MobHealth=0x0000 # +OP_InitialMobHealth=0x0000 # +OP_TargetHoTT=0x0000 # +OP_TargetBuffs=0x0000 # +OP_BuffCreate=0x0000 # +OP_BuffRemoveRequest=0x0000 +OP_DeleteSpawn=0x0000 # +OP_AutoAttack=0x0000 # +OP_AutoAttack2=0x0000 # +OP_Consume=0x0000 # +OP_MoveItem=0x0000 # +OP_DeleteItem=0x0000 # +OP_DeleteCharge=0x0000 # +OP_ItemPacket=0x38b0 # +OP_ItemLinkResponse=0x0000 # +OP_ItemLinkClick=0x0000 # +OP_NewSpawn=0x0000 # +OP_Track=0x0000 # +OP_TrackTarget=0x0000 # +OP_TrackUnknown=0x0000 # +OP_ClickDoor=0x0000 # +OP_MoveDoor=0x0000 # +OP_RemoveAllDoors=0x0000 # +OP_EnvDamage=0x0000 # +OP_BoardBoat=0x0000 # +OP_Forage=0x0000 # +OP_LeaveBoat=0x0000 # +OP_ControlBoat=0x0000 # +OP_SafeFallSuccess=0x0000 # +OP_RezzComplete=0x0000 # +OP_RezzRequest=0x0000 # +OP_RezzAnswer=0x0000 # +OP_Shielding=0x0000 # +OP_RequestDuel=0x0000 # +OP_MobRename=0x0000 # +OP_AugmentItem=0x0000 # +OP_WeaponEquip1=0x0000 # +OP_WeaponEquip2=0x0000 # +OP_WeaponUnequip2=0x0000 # +OP_ApplyPoison=0x0000 # +OP_Save=0x0000 # +OP_TestBuff=0x0000 # +OP_CustomTitles=0x0000 # +OP_Split=0x0000 # +OP_YellForHelp=0x0000 # +OP_LoadSpellSet=0x0000 # +OP_Bandolier=0x0000 # +OP_PotionBelt=0x0000 # +OP_DuelResponse=0x0000 # +OP_DuelResponse2=0x0000 # +OP_SaveOnZoneReq=0x0000 # +OP_ReadBook=0x0000 # +OP_Dye=0x0000 # +OP_InterruptCast=0x0000 # +OP_AAAction=0x0000 # +OP_LeadershipExpToggle=0x0000 # +OP_LeadershipExpUpdate=0x0000 # +OP_PurchaseLeadershipAA=0x0000 # +OP_UpdateLeadershipAA=0x0000 # +OP_MarkNPC=0x0000 # +OP_ClearNPCMarks=0x0000 # +OP_DoGroupLeadershipAbility=0x0000 # +OP_GroupLeadershipAAUpdate=0x0000 # +OP_DelegateAbility=0x0000 # +OP_SetGroupTarget=0x0000 # +OP_Charm=0x0000 # +OP_Stun=0x0000 # +OP_SendFindableNPCs=0x6c36 # +OP_FindPersonRequest=0x0000 # +OP_FindPersonReply=0x0000 # +OP_Sound=0x0000 # +OP_PetBuffWindow=0x0000 # +OP_LevelAppearance=0x0000 # +OP_Translocate=0x0000 # +OP_Sacrifice=0x0000 # +OP_PopupResponse=0x0000 # +OP_OnLevelMessage=0x0000 # +OP_AugmentInfo=0x0000 # +OP_Petition=0x0000 # +OP_SomeItemPacketMaybe=0x0000 # +OP_PVPStats=0x0000 # +OP_PVPLeaderBoardRequest=0x0000 # +OP_PVPLeaderBoardReply=0x0000 # +OP_PVPLeaderBoardDetailsRequest=0x0000 # +OP_PVPLeaderBoardDetailsReply=0x0000 # +OP_RestState=0x0000 # +OP_RespawnWindow=0x0000 # +OP_DisciplineTimer=0x0000 # +OP_LDoNButton=0x0000 # +OP_SetStartCity=0x0000 # +OP_VoiceMacroIn=0x0000 # +OP_VoiceMacroOut=0x0000 # +OP_ItemViewUnknown=0x0000 # +OP_VetRewardsAvaliable=0x0000 # +OP_VetClaimRequest=0x0000 # +OP_VetClaimReply=0x0000 # +OP_CrystalCountUpdate=0x0000 # +OP_DisciplineUpdate=0x0000 # +OP_BecomeCorpse=0x0000 # +OP_Action2=0x0000 # +OP_MobUpdate=0x68da # +OP_NPCMoveUpdate=0x226b # +OP_CameraEffect=0x0000 # +OP_SpellEffect=0x0000 # +OP_RemoveNimbusEffect=0x0000 # +OP_AltCurrency=0x0000 +OP_AltCurrencyMerchantRequest=0x0000 +OP_AltCurrencyMerchantReply=0x0000 +OP_AltCurrencyPurchase=0x0000 +OP_AltCurrencySell=0x0000 +OP_AltCurrencySellSelection=0x0000 +OP_AltCurrencyReclaim=0x0000 +OP_CrystalReclaim=0x0000 +OP_CrystalCreate=0x0000 +OP_Untargetable=0x0000 +OP_IncreaseStats=0x0000 + +OP_DzQuit=0x0000 +OP_DzListTimers=0x0000 +OP_DzAddPlayer=0x0000 +OP_DzRemovePlayer=0x0000 +OP_DzSwapPlayer=0x0000 +OP_DzMakeLeader=0x0000 +OP_DzPlayerList=0x0000 +OP_DzJoinExpeditionConfirm=0x0000 +OP_DzJoinExpeditionReply=0x0000 +OP_DzExpeditionInfo=0x0000 +OP_DzMemberStatus=0x0000 +OP_DzLeaderStatus=0x0000 +OP_DzExpeditionEndsWarning=0x0000 +OP_DzExpeditionList=0x0000 +OP_DzMemberList=0x0000 +OP_DzCompass=0x0000 +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # +OP_ManaUpdate=0x0000 # +OP_EnduranceUpdate=0x0000 # +OP_MobManaUpdate=0x0000 # +OP_MobEnduranceUpdate=0x0000 # + +# Looting +OP_LootRequest=0x0000 # +OP_EndLootRequest=0x0000 # +OP_LootItem=0x0000 # +OP_LootComplete=0x0000 # + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x0000 # +OP_TraderDelItem=0x0000 # +OP_BecomeTrader=0x0000 # +OP_TraderShop=0x0000 # +OP_Trader=0x0000 # +OP_TraderBuy=0x0000 # +OP_Barter=0x0000 # +OP_ShopItem=0x0000 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # +OP_TraderItemUpdate=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x0000 # +OP_TradeAcceptClick=0x0000 # +OP_TradeRequestAck=0x0000 # +OP_TradeCoins=0x0000 # +OP_FinishTrade=0x0000 # +OP_CancelTrade=0x0000 # +OP_TradeMoneyUpdate=0x0000 # +OP_MoneyUpdate=0x0000 # +OP_TradeBusy=0x0000 # + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x0000 # +OP_FinishWindow2=0x0000 # + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x0000 # +OP_ItemVerifyReply=0x0000 # + +# merchant crap +OP_ShopPlayerSell=0x0000 # +OP_ShopRequest=0x5f0d # +OP_ShopEnd=0x537c # +OP_ShopEndConfirm=0x0000 # +OP_ShopPlayerBuy=0x0000 # +OP_ShopDelItem=0x0000 # + +# tradeskill stuff: +OP_ClickObject=0x0000 # +OP_ClickObjectAction=0x0000 # +OP_ClearObject=0x0000 # +OP_RecipeDetails=0x0000 # +OP_RecipesFavorite=0x0000 # +OP_RecipesSearch=0x0000 # +OP_RecipeReply=0x0000 # +OP_RecipeAutoCombine=0x0000 # +OP_TradeSkillCombine=0x0000 # + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x0000 # +OP_OpenTributeMaster=0x0000 # +OP_SelectTribute=0x0000 # +OP_TributeItem=0x0000 # +OP_TributeMoney=0x0000 # +OP_TributeToggle=0x0000 # +OP_TributePointUpdate=0x0000 # +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x0000 # +OP_AdventureFinish=0x0000 # +OP_AdventureInfoRequest=0x0000 # +OP_AdventureInfo=0x0000 # +OP_AdventureRequest=0x0000 # +OP_AdventureDetails=0x0000 # +OP_AdventureData=0x0000 # +OP_AdventureUpdate=0x0000 # +OP_AdventureMerchantRequest=0x0000 # +OP_AdventureMerchantResponse=0x0000 # +OP_AdventureMerchantPurchase=0x0000 # +OP_AdventureMerchantSell=0x0000 # +OP_AdventurePointsUpdate=0x0000 # +OP_AdventureStatsRequest=0x0000 # +OP_AdventureStatsReply=0x0000 # +OP_AdventureLeaderboardRequest=0x0000 # +OP_AdventureLeaderboardReply=0x0000 # + +# Group Opcodes +OP_GroupDisband=0x0000 # +OP_GroupInvite=0x0000 # +OP_GroupFollow=0x0000 # +OP_GroupUpdate=0x0000 # +OP_GroupUpdateB=0x0000 # +OP_GroupCancelInvite=0x0000 # +OP_GroupAcknowledge=0x0000 # +OP_GroupDelete=0x0000 # +OP_CancelInvite=0x0000 # +OP_GroupFollow2=0x0000 # +OP_GroupInvite2=0x0000 # +OP_GroupDisbandYou=0x0000 # +OP_GroupDisbandOther=0x0000 # +OP_GroupLeaderChange=0x0000 # +OP_GroupRoles=0x0000 # +OP_GroupMakeLeader=0x0000 + +# LFG/LFP Opcodes +OP_LFGCommand=0x0000 # +OP_LFGGetMatchesRequest=0x0000 # +OP_LFGGetMatchesResponse=0x0000 # +OP_LFPGetMatchesRequest=0x0000 # +OP_LFPGetMatchesResponse=0x0000 # +OP_LFPCommand=0x0000 # +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x0000 # +OP_RaidUpdate=0x0000 # Testing VoA 0x0c08 +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x0000 # +OP_CombatAbility=0x0000 # +OP_SenseTraps=0x0000 # +OP_PickPocket=0x0000 # +OP_DisarmTraps=0x0000 # +OP_Disarm=0x0000 # +OP_Sneak=0x0000 # +OP_Fishing=0x0000 # +OP_InstillDoubt=0x0000 # +OP_FeignDeath=0x0000 # +OP_Mend=0x0000 # +OP_LDoNOpen=0x0000 # + +# Task packets +OP_TaskActivityComplete=0x0000 # +OP_TaskMemberList=0x0000 # +OP_OpenNewTasksWindow=0x0000 # +OP_AvaliableTask=0x0000 # +OP_AcceptNewTask=0x0000 # +OP_TaskHistoryRequest=0x0000 # +OP_TaskHistoryReply=0x0000 # +OP_CancelTask=0x0000 # +OP_DeclineAllTasks=0x0000 # + +# Title opcodes +OP_NewTitlesAvailable=0x0000 # +OP_RequestTitles=0x0000 # +OP_SendTitleList=0x0000 # +OP_SetTitle=0x0000 # +OP_SetTitleReply=0x0000 # + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x0000 # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x0000 # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0000 # +OP_Login=0x0000 # +OP_ServerListRequest=0x0000 # +OP_PlayEverquestRequest=0x0000 # +OP_PlayEverquestResponse=0x0000 # +OP_ChatMessage=0x0000 # +OP_LoginAccepted=0x0000 # +OP_ServerListResponse=0x0000 # +OP_Poll=0x0000 # +OP_EnterChat=0x0000 # +OP_PollResponse=0x0000 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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 # diff --git a/utils/EQExtractor2/EQExtractor2/patch_Dec10-2012.conf b/utils/EQExtractor2/EQExtractor2/patch_Dec10-2012.conf new file mode 100644 index 000000000..c7d325fee --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_Dec10-2012.conf @@ -0,0 +1,651 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 used for unknown explorer + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x11b6 +OP_ApproveWorld=0x7e47 +OP_LogServer=0x19ab +OP_SendCharInfo=0x06a2 +OP_ExpansionInfo=0x711a +OP_GuildsList=0x2d38 +OP_EnterWorld=0x57c3 +OP_PostEnterWorld=0x0c3d +OP_World_Client_CRC1=0x0044 +OP_World_Client_CRC2=0x26df +OP_SendSpellChecksum=0x0000 +OP_SendSkillCapsChecksum=0x0000 + +# Character Select Related: +OP_SendMaxCharacters=0x5349 +OP_SendMembership=0x03b2 +OP_SendMembershipDetails=0x0b1f +OP_CharacterCreateRequest=0x006b +OP_CharacterCreate=0x0afc +OP_DeleteCharacter=0x0ab4 +OP_RandomNameGenerator=0x4453 +OP_ApproveName=0x213a +OP_MOTD=0x1ae3 +OP_SetChatServer=0x2a81 +OP_SetChatServer2=0x4099 +OP_ZoneServerInfo=0x49d9 +OP_WorldComplete=0x12d4 +OP_WorldUnknown001=0x6be3 +OP_FloatListThing=0x7a19 + +# Reasons for Disconnect: +OP_ZoneUnavail=0x6281 +OP_WorldClientReady=0x43b0 +OP_CharacterStillInZone=0x0000 +OP_WorldChecksumFailure=0x0000 +OP_WorldLoginFailed=0x0000 +OP_WorldLogout=0x0000 +OP_WorldLevelTooHigh=0x0000 +OP_CharInacessable=0x0000 +OP_UserCompInfo=0x0000 +OP_SendExeChecksum=0x0000 +OP_SendBaseDataChecksum=0x0000 + +# Zone in opcodes +OP_AckPacket=0x47a6 +OP_ZoneEntry=0x1665 +OP_ReqNewZone=0x3dec +OP_NewZone=0x7fff +OP_ZoneSpawns=0x0980 +OP_PlayerProfile=0x46bb +OP_TimeOfDay=0x3be2 +OP_LevelUpdate=0x7ce0 +OP_Stamina=0x1949 +OP_RequestClientZoneChange=0x647a +OP_ZoneChange=0x1eb4 +OP_LockoutTimerInfo=0x0000 +OP_ZoneServerReady=0x0000 +OP_ZoneInUnknown=0x0000 +OP_LogoutReply=0x0000 +OP_PreLogoutReply=0x0000 + +# Required to fully log in +OP_SpawnAppearance=0x7360 +OP_ChangeSize=0x355c +OP_TributeUpdate=0x2691 +OP_TributeTimer=0x3294 +OP_SendTributes=0x0a1c +OP_SendGuildTributes=0x1781 +OP_TributeInfo=0x5dd2 +OP_Weather=0x0bd7 +OP_ReqClientSpawn=0x5a4f +OP_SpawnDoor=0x7b6c +OP_GroundSpawn=0x4286 +OP_SendZonepoints=0x7922 +OP_BlockedBuffs=0x5a71 +OP_RemoveBlockedBuffs=0x78ba +OP_ClearBlockedBuffs=0x5d3c +OP_WorldObjectsSent=0x7fa8 +OP_SendExpZonein=0x25ab +OP_SendAATable=0x7791 +OP_RespondAA=0x379d +OP_UpdateAA=0x504f +OP_SendAAStats=0x3d1c +OP_AAExpUpdate=0x25c5 +OP_ExpUpdate=0x47e3 +OP_HPUpdate=0x07b8 +OP_ManaChange=0x1e3b +OP_TGB=0x10a6 +OP_SpecialMesg=0x362c +OP_GuildMemberList=0x4053 +OP_GuildMOTD=0x0561 +OP_CharInventory=0x786e +OP_WearChange=0x1ad3 +OP_ClientUpdate=0x455d +OP_ClientReady=0x3f83 # 0x422d +OP_SetServerFilter=0x5b27 + +# Guild Opcodes - Disabled until crashes are resolved in RoF +OP_GetGuildMOTD=0x7ba3 # Was 0x35dc +OP_GetGuildMOTDReply=0x42a2 # Was 0x4586 +OP_GuildMemberUpdate=0x0048 # Was 0x5643 +OP_GuildInvite=0x5f8e +OP_GuildRemove=0x296d +OP_GuildPeace=0x17eb +OP_SetGuildMOTD=0x44d0 +OP_GuildList=0x2d38 +OP_GuildWar=0x395a +OP_GuildLeader=0x7c6f +OP_GuildDelete=0x241b +OP_GuildInviteAccept=0x78a5 +OP_GuildDemote=0x3100 +OP_GuildPublicNote=0x3c2c +OP_GuildManageBanker=0x096d # Was 0x0737 +OP_GuildBank=0x2ab0 # Was 0x10c3 +OP_SetGuildRank=0x3599 +OP_GuildUpdateURLAndChannel=0x7851 +OP_GuildStatus=0x25a5 +OP_GuildCreate=0xd2d1 # or maybe 0x086e +OP_GuildMemberLevelUpdate=0x0000 # Unused? +OP_ZoneGuildList=0x0000 # Unused? +OP_GetGuildsList=0x0000 # Unused? +OP_LFGuild=0x0000 +OP_GuildManageRemove=0x0000 +OP_GuildManageAdd=0x0000 +OP_GuildManageStatus=0x0000 + +# GM/Guide Opcodes +OP_GMServers=0x2612 +OP_GMBecomeNPC=0x5201 +OP_GMZoneRequest=0x69e5 +OP_GMZoneRequest2=0x65d6 +OP_GMGoto=0x0345 +OP_GMSearchCorpse=0x5bed +OP_GMHideMe=0x4c65 +OP_GMDelCorpse=0x6034 +OP_GMApproval=0x0e37 +OP_GMToggle=0x1276 +OP_GMSummon=0x0655 # Was 0x684f +OP_GMEmoteZone=0x1b39 # Was 0x0655 +OP_GMEmoteWorld=0x1f8c # Was 0x1935 +OP_GMFind=0x6160 +OP_GMKick=0x5646 +OP_GMKill=0x507e +OP_GMNameChange=0x4434 # Was 0x51f7 +OP_GMLastName=0x3077 # Was 0x4dd7 + +# Misc Opcodes +OP_InspectRequest=0x23f1 +OP_InspectAnswer=0x5794 +OP_InspectMessageUpdate=0x3064 +OP_BeginCast=0x17ff +OP_BuffFadeMsg=0x41cb +OP_ConsentResponse=0x183d +OP_MemorizeSpell=0x2fac +OP_SwapSpell=0x4736 +OP_CastSpell=0x1cb5 +OP_Consider=0x4d8d +OP_FormattedMessage=0x6afe +OP_SimpleMessage=0x02a5 +OP_Buff=0x08ed +OP_Illusion=0x6c43 +OP_MoneyOnCorpse=0x1837 +OP_RandomReply=0x6525 +OP_DenyResponse=0x344a +OP_SkillUpdate=0x52c6 +OP_GMTrainSkillConfirm=0x35cc # 0x3960 +OP_RandomReq=0x59db +OP_Death=0x3a65 +OP_GMTraining=0x6e8f +OP_GMEndTraining=0x7ffe +OP_GMTrainSkill=0x444c +OP_Animation=0x5e0c +OP_Begging=0x54ac +OP_Consent=0x400e +OP_ConsentDeny=0x34c1 +OP_AutoFire=0x314e +OP_PetCommands=0x0093 +OP_DeleteSpell=0x05dc +OP_Surname=0x1a87 +OP_ClearSurname=0x17b6 +OP_FaceChange=0x5b06 +OP_SenseHeading=0x46c8 +OP_Action=0x0ea7 +OP_ConsiderCorpse=0x06c5 +OP_HideCorpse=0x3c4b +OP_CorpseDrag=0x0be7 +OP_CorpseDrop=0xdf77 +OP_Bug=0x0c07 +OP_Feedback=0x45ca +OP_Report=0x6d94 +OP_Damage=0x5428 +OP_ChannelMessage=0x33bc +OP_Assist=0x4cd1 +OP_AssistGroup=0x63df +OP_MoveCoin=0x765a +OP_ZonePlayerToBind=0x7f99 +OP_KeyRing=0x04ba +OP_WhoAllRequest=0x78eb +OP_WhoAllResponse=0x3393 +OP_FriendsWho=0x3dac +OP_ConfirmDelete=0x78e0 +OP_Logout=0x64bd +OP_Rewind=0x5521 +OP_TargetCommand=0x000a +OP_Hide=0x2da1 +OP_Jump=0x1d47 +OP_Camp=0x41cd +OP_Emote=0x37fd +OP_SetRunMode=0x2ce6 +OP_BankerChange=0x4e20 +OP_TargetMouse=0x0e25 +OP_MobHealth=0x218d +OP_InitialMobHealth=0x0000 # Unused? +OP_TargetHoTT=0x3af5 +OP_XTargetResponse=0x7f64 +OP_XTargetRequest=0x6753 +OP_XTargetAutoAddHaters=0x5f51 +OP_TargetBuffs=0x1c71 +OP_BuffCreate=0x71f5 +OP_BuffRemoveRequest=0x7efd +OP_DeleteSpawn=0x3b06 +OP_AutoAttack=0x0d14 +OP_AutoAttack2=0x3912 +OP_Consume=0x4692 +OP_MoveItem=0x62a2 +OP_DeleteItem=0x3eb5 +OP_DeleteCharge=0x2d5b +OP_ItemPacket=0x5e0e +OP_ItemLinkResponse=0x2fdb +OP_ItemLinkClick=0x0353 +OP_ItemPreview=0x05cf +OP_NewSpawn=0x3f5b +OP_Track=0x7d43 +OP_TrackTarget=0x4c42 +OP_TrackUnknown=0x2395 +OP_ClickDoor=0x7cc0 +OP_MoveDoor=0x5611 +OP_RemoveAllDoors=0x39c9 +OP_EnvDamage=0x52e9 +OP_BoardBoat=0x2731 +OP_Forage=0x416d +OP_LeaveBoat=0x6cc1 +OP_ControlBoat=0x4aa0 +OP_SafeFallSuccess=0x6d27 +OP_RezzComplete=0x3297 +OP_RezzRequest=0x521b +OP_RezzAnswer=0x004e +OP_Shielding=0x17d9 +OP_RequestDuel=0x1ea9 +OP_MobRename=0x5040 +OP_AugmentItem=0x1627 # Was 0x37cb +OP_WeaponEquip1=0x35c3 +OP_WeaponEquip2=0x012f # Was 0x6022 +OP_WeaponUnequip2=0x1076 # Was 0x0110 +OP_ApplyPoison=0x1499 +OP_Save=0x2e6f +OP_TestBuff=0x046e # Was 0x3772 +OP_CustomTitles=0x471a +OP_Split=0x269e +OP_YellForHelp=0x0017 +OP_LoadSpellSet=0x38b4 +OP_Bandolier=0x2b6f +OP_PotionBelt=0x2d1b # Was 0x4d3b +OP_DuelResponse=0x0dee +OP_DuelResponse2=0x5e04 +OP_SaveOnZoneReq=0x36b1 +OP_ReadBook=0x383c +OP_Dye=0x62d8 +OP_InterruptCast=0x7470 +OP_AAAction=0x719a +OP_LeadershipExpToggle=0x3ea6 +OP_LeadershipExpUpdate=0x6922 +OP_PurchaseLeadershipAA=0x1962 +OP_UpdateLeadershipAA=0x56aa +OP_MarkNPC=0x2d9f +OP_ClearNPCMarks=0x0d2d +OP_DelegateAbility=0x7820 +OP_SetGroupTarget=0x118a +OP_Charm=0x7118 +OP_Stun=0x53c0 +OP_SendFindableNPCs=0x34c3 +OP_FindPersonRequest=0x2f3b +OP_FindPersonReply=0x44f7 +OP_Sound=0x3cec +OP_PetBuffWindow=0x7197 +OP_LevelAppearance=0x7c4d +OP_Translocate=0x6f01 +OP_Sacrifice=0x76ab +OP_PopupResponse=0x2fa1 +OP_OnLevelMessage=0x09cd +OP_AugmentInfo=0x7812 +OP_Petition=0x2885 +OP_SomeItemPacketMaybe=0x11a0 +OP_PVPStats=0x3034 # Unsure +OP_PVPLeaderBoardRequest=0x1bce +OP_PVPLeaderBoardReply=0x3e78 +OP_PVPLeaderBoardDetailsRequest=0x46f4 +OP_PVPLeaderBoardDetailsReply=0x6a60 +OP_RestState=0x3ad5 +OP_RespawnWindow=0x7767 +OP_LDoNButton=0x596e +OP_SetStartCity=0x7936 # Was 0x2d1b +OP_VoiceMacroIn=0x202e +OP_VoiceMacroOut=0x3920 +OP_ItemViewUnknown=0x0b64 +OP_VetRewardsAvaliable=0x05d9 +OP_VetClaimRequest=0xcdde +OP_VetClaimReply=0x361b +OP_DisciplineUpdate=0x2483 # Was 0x2f05 +OP_DisciplineTimer=0x4933 # Was 0x5e3f +OP_BecomeCorpse=0x0000 # Unused? +OP_Action2=0x0000 # Unused? +OP_MobUpdate=0x6b5a +OP_NPCMoveUpdate=0x5bd9 +OP_CameraEffect=0x5712 +OP_SpellEffect=0x72b6 +OP_RemoveNimbusEffect=0x3ba7 +OP_AltCurrency=0x8fcb +OP_AltCurrencyMerchantRequest=0x7e3e +OP_AltCurrencyMerchantReply=0x0b60 +OP_AltCurrencyPurchase=0x74a8 +OP_AltCurrencySell=0x3167 +OP_AltCurrencySellSelection=0x3c47 +OP_AltCurrencyReclaim=0x46ed +OP_CrystalCountUpdate=0x5a82 # Was 0x3f60 +OP_CrystalCreate=0x7616 # Was 0x5a82 +OP_CrystalReclaim=0x73db # Was 0x7616 +OP_Untargetable=0x7717 +OP_IncreaseStats=0x0711 +OP_Weblink=0x7cce +#OP_OpenInventory=0x0000 # Likely does not exist in RoF -U +OP_OpenContainer=0x654f + +OP_DzQuit=0x5fc8 +OP_DzListTimers=0x67b9 +OP_DzAddPlayer=0x5ca2 +OP_DzRemovePlayer=0x0dc1 +OP_DzSwapPlayer=0x4995 +OP_DzMakeLeader=0x17b2 +OP_DzPlayerList=0x1aff +OP_DzJoinExpeditionConfirm=0x30df +OP_DzJoinExpeditionReply=0x15d4 +OP_DzExpeditionInfo=0x3861 +OP_DzExpeditionList=0x0b3b +OP_DzMemberStatus=0x26c2 +OP_DzLeaderStatus=0x4021 +OP_DzExpeditionEndsWarning=0x32eb +OP_DzMemberList=0x348f +OP_DzCompass=0x0e01 # Was 0x4f09 +OP_DzChooseZone=0x0000 # Maybe 0x29d6 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # Actually OP_MobUpdate ? +OP_ManaUpdate=0x6675 +OP_EnduranceUpdate=0x71fb +OP_MobManaUpdate=0x48b5 +OP_MobEnduranceUpdate=0x7cb5 + +# Mercenary Opcodes +OP_MercenaryDataUpdateRequest=0x6774 +OP_MercenaryDataUpdate=0x5a7c +OP_MercenaryDataRequest=0x612e +OP_MercenaryDataResponse=0x0768 +OP_MercenaryHire=0x18ba +OP_MercenaryDismiss=0x7ff4 +OP_MercenaryTimerRequest=0x4326 +OP_MercenaryUnknown1=0x7db4 +OP_MercenaryCommand=0x7b21 +OP_MercenarySuspendRequest=0x61b3 +OP_MercenarySuspendResponse=0x5975 + +# Looting +OP_LootRequest=0x43a7 +OP_EndLootRequest=0x3d8c +OP_LootItem=0x515b +OP_LootComplete=0x256d + +# bazaar trader stuff: +OP_BazaarSearch=0x2ad2 +OP_TraderDelItem=0x63c8 +OP_BecomeTrader=0x74bc +OP_TraderShop=0x0a5e +OP_Trader=0x6790 +OP_TraderBuy=0x0000 +OP_Barter=0x6854 +OP_ShopItem=0x0000 +OP_BazaarInspect=0x0000 +OP_Bazaar=0x0000 +OP_TraderItemUpdate=0x0000 + +# pc/npc trading +OP_TradeRequest=0x5f9e +OP_TradeAcceptClick=0x13db +OP_TradeRequestAck=0x6852 +OP_TradeCoins=0x584d +OP_FinishTrade=0x3fbf +OP_CancelTrade=0x0234 +OP_TradeMoneyUpdate=0x65f6 +OP_MoneyUpdate=0x6e42 +OP_TradeBusy=0x7c6d + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x1939 +OP_FinishWindow2=0x68df + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x2c0c +OP_ItemVerifyReply=0x40de + +# merchant stuff +OP_ShopPlayerSell=0x6b4d +OP_ShopRequest=0x16ba +OP_ShopEnd=0x0d07 +OP_ShopEndConfirm=0x1860 +OP_ShopPlayerBuy=0x3bab +OP_ShopDelItem=0x509c + +# tradeskill stuff: +OP_ClickObject=0x7992 +OP_ClickObjectAction=0x5507 +OP_ClearObject=0x2ead +OP_RecipeDetails=0x7ad0 +OP_RecipesFavorite=0x6960 +OP_RecipesSearch=0x30f7 +OP_RecipeReply=0x544e +OP_RecipeAutoCombine=0x46a4 +OP_TradeSkillCombine=0x7447 + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x107b +OP_OpenTributeMaster=0x6fed # Was 0x40f5 +OP_SelectTribute=0x5513 +OP_TributeItem=0x105b +OP_TributeMoney=0x4b6d # Was 0x6fed +OP_TributeToggle=0x1f50 +OP_TributePointUpdate=0x01ec +OP_TributeNPC=0x0000 +OP_GuildTributeInfo=0x0000 +OP_OpenTributeReply=0x0000 +OP_GuildTributeStatus=0x0000 + +# Adventure packets: +OP_LeaveAdventure=0x18b3 +OP_AdventureFinish=0x3d8b +OP_AdventureInfoRequest=0x38db +OP_AdventureInfo=0x1036 +OP_AdventureRequest=0x325c +OP_AdventureDetails=0x4975 +OP_AdventureData=0x729b +OP_AdventureUpdate=0x2df4 +OP_AdventureMerchantRequest=0x2aa0 # Was 654d +OP_AdventureMerchantResponse=0x5aed # Was 7949 +OP_AdventureMerchantPurchase=0x725e # Was 155a +OP_AdventureMerchantSell=0x7882 # Was 389c +OP_AdventurePointsUpdate=0x37cb # Was 7589 +OP_AdventureStatsRequest=0x68c2 +OP_AdventureStatsReply=0x62fa +OP_AdventureLeaderboardRequest=0x577a +OP_AdventureLeaderboardReply=0x2533 + +# Group Opcodes +OP_GroupDisband=0x1ed0 +OP_GroupInvite=0x1602 +OP_GroupFollow=0x2789 +OP_GroupUpdate=0x0ba4 +OP_GroupUpdateB=0x5a07 +OP_GroupCancelInvite=0x2b26 +OP_GroupAcknowledge=0x5fae +OP_GroupDelete=0x33ca +OP_CancelInvite=0x2b26 +OP_GroupFollow2=0x4964 +OP_GroupInvite2=0x6e80 +OP_GroupDisbandYou=0x623d +OP_GroupDisbandOther=0x74fa +OP_GroupLeaderChange=0x46fc +OP_GroupRoles=0x047c +OP_GroupMakeLeader=0x4129 +OP_DoGroupLeadershipAbility=0x17d7 +OP_GroupLeadershipAAUpdate=0x6567 + +# LFG/LFP Opcodes +OP_LFGCommand=0x4463 +OP_LFGGetMatchesRequest=0x1373 +OP_LFGGetMatchesResponse=0x31d8 +OP_LFPGetMatchesRequest=0x7fd9 +OP_LFPGetMatchesResponse=0x4111 +OP_LFPCommand=0x6e8b +OP_LFGAppearance=0x0000 +OP_LFGResponse=0x0000 + +# Raid Opcodes +OP_RaidInvite=0x1bd1 +OP_RaidUpdate=0x548e +OP_RaidJoin=0x0000 + +# Button-push commands +OP_Taunt=0x0af6 +OP_CombatAbility=0x6e4c +OP_SenseTraps=0x7462 # Was 0x2ee0 +OP_PickPocket=0x047b +OP_DisarmTraps=0x0000 +OP_Disarm=0x70a9 +OP_Sneak=0x62f4 +OP_Fishing=0x53c2 +OP_InstillDoubt=0x63a5 +OP_FeignDeath=0x677d +OP_Mend=0x324f +OP_Bind_Wound=0x585d +OP_LDoNOpen=0x3567 + +# Task packets +OP_TaskDescription=0x2294 +OP_TaskActivity=0x7181 +OP_CompletedTasks=0x9495 +OP_TaskActivityComplete=0x71cd +OP_AcceptNewTask=0x394d +OP_CancelTask=0x0c7f +OP_TaskMemberList=0x748e # Was 0x1656 +OP_OpenNewTasksWindow=0x436c # Was 0x11de +OP_AvaliableTask=0x2bf8 # Was 0x2377 +OP_TaskHistoryRequest=0x6cf6 +OP_TaskHistoryReply=0x25eb +OP_DeclineAllTasks=0x0000 + +# Title opcodes +OP_NewTitlesAvailable=0x45d1 +OP_RequestTitles=0x1b5a +OP_SendTitleList=0x0d64 +OP_SetTitle=0x0bfc +OP_SetTitleReply=0x7e20 + +# mail opcodes +OP_Command=0x0000 +OP_MailboxHeader=0x0000 +OP_MailHeader=0x0000 +OP_MailBody=0x0000 +OP_NewMail=0x0000 +OP_SentConfirm=0x0000 + +########### Below this point should not be needed ########### + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 +OP_LocInfo=0x0000 +OP_ReloadUI=0x0000 +OP_ItemName=0x0000 +OP_ItemLinkText=0x0000 +OP_MultiLineMsg=0x0000 +OP_MendHPUpdate=0x0000 +OP_TargetReject=0x0000 +OP_SafePoint=0x0000 +OP_ApproveZone=0x0000 +OP_ZoneComplete=0x0000 +OP_ClientError=0x0000 +OP_DumpName=0x0000 +OP_Heartbeat=0x0000 +OP_CrashDump=0x0000 +OP_LoginComplete=0x0000 + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 +OP_PlayMP3=0x0000 +OP_ReclaimCrystals=0x0000 +OP_DynamicWall=0x0000 +OP_OpenDiscordMerchant=0x0000 +OP_DiscordMerchantInventory=0x0000 +OP_GiveMoney=0x0000 +OP_RequestKnowledgeBase=0x0000 +OP_KnowledgeBase=0x0000 +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 +OP_Some6ByteHPUpdate=0x0000 seems to happen when you target group members +OP_QueryResponseThing=0x0000 + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x46d3 # OP_SendSpellChecksum +#OP_LoginUnknown2=0x040b # OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 search term for petition +OP_PetitionSearchResults=0x0000 (list of?) matches from search +OP_PetitionSearchText=0x0000 text results of search + +OP_PetitionUpdate=0x0000 +OP_PetitionCheckout=0x0000 +OP_PetitionCheckIn=0x0000 +OP_PetitionQue=0x0000 +OP_PetitionUnCheckout=0x0000 +OP_PetitionDelete=0x0000 +OP_DeletePetition=0x0000 +OP_PetitionResolve=0x0000 +OP_PDeletePetition=0x0000 +OP_PetitionBug=0x0000 +OP_PetitionRefresh=0x0000 +OP_PetitionCheckout2=0x0000 +OP_PetitionViewPetition=0x0000 + +# Login opcodes +OP_SessionReady=0x0000 +OP_Login=0x0000 +OP_ServerListRequest=0x0000 +OP_PlayEverquestRequest=0x0000 +OP_PlayEverquestResponse=0x0000 +OP_ChatMessage=0x0000 +OP_LoginAccepted=0x0000 +OP_ServerListResponse=0x0000 +OP_Poll=0x0000 +OP_EnterChat=0x0000 +OP_PollResponse=0x0000 + +# raw opcodes +OP_RAWSessionRequest=0x0000 +OP_RAWSessionResponse=0x0000 +OP_RAWCombined=0x0000 +OP_RAWSessionDisconnect=0x0000 +OP_RAWKeepAlive=0x0000 +OP_RAWSessionStatRequest=0x0000 +OP_RAWSessionStatResponse=0x0000 +OP_RAWPacket=0x0000 +OP_RAWFragment=0x0000 +OP_RAWOutOfOrderAck=0x0000 +OP_RAWAck=0x0000 +OP_RAWAppCombined=0x0000 +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 diff --git a/utils/EQExtractor2/EQExtractor2/patch_Dec7-2010.conf b/utils/EQExtractor2/EQExtractor2/patch_Dec7-2010.conf new file mode 100644 index 000000000..513f5017a --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_Dec7-2010.conf @@ -0,0 +1,611 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x0000a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x6893 # was 0x2683 +OP_ApproveWorld=0x115a # was 0x28a7 +OP_LogServer=0x701f # was 0x224f +OP_SendCharInfo=0x1b85 # was 0x2c27 +OP_ExpansionInfo=0x1771 # Same +OP_GuildsList=0x5b0b # Same +OP_EnterWorld=0x3288 # was 0x4f60 +OP_PostEnterWorld=0x2f2c # was 0x570c +OP_World_Client_CRC1=0x0000 # C +OP_World_Client_CRC2=0x0000 # C +OP_SendSpellChecksum=0x0000 # C +OP_SendSkillCapsChecksum=0x0000 # C + +# Character Select Related: +OP_DeleteCharacter=0x0000 # C +OP_CharacterCreateRequest=0x0000 # C +OP_CharacterCreate=0x0000 # C +OP_RandomNameGenerator=0x0000 # C 0x0000 +OP_ApproveName=0x0000 # C + +OP_MOTD=0x7312 # C 0x0000 +OP_SetChatServer=0x698a # +OP_SetChatServer2=0x0000 # Not sent any more ? +OP_ZoneServerInfo=0x0479 # was 0x03cc +OP_WorldComplete=0x0d98 # +OP_WorldUnknown001=0x0000 # C 0x0000 +OP_FloatListThing=0x0000 # V + +# Reasons for Disconnect: +OP_ZoneUnavail=0x0000 # C 0x0000 +OP_WorldClientReady=0x399b # C 0x0000 +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # was 0x3594 +OP_ZoneEntry=0x002b # was 0x538f +OP_ReqNewZone=0x5ca5 # was 0x5ca5 +OP_NewZone=0x0254 # was 0x0254 +OP_ZoneSpawns=0x0000 # ? +OP_PlayerProfile=0x6022 # was 0x6022 +OP_TimeOfDay=0x6015 # was 0x6015 +OP_LevelUpdate=0x0000 # V +OP_Stamina=0x0000 # V +OP_RequestClientZoneChange=0x3fd2 # was 0x4178 +OP_ZoneChange=0x0b93 # was 0x4a61 +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x7b64 # V +OP_TributeUpdate=0x4895 # +OP_TributeTimer=0x0000 # C +OP_TaskDescription=0x0000 # C +OP_TaskActivity=0x0000 # C +OP_CompletedTasks=0x0000 # C 0x0000 +OP_Weather=0x7ce4 # was 0x0ed5 +OP_SendAATable=0x466c # +OP_UpdateAA=0x0000 # V +OP_RespondAA=0x0000 # C 0x0000 +OP_ReqClientSpawn=0x54e8 # was 0x6618 +OP_SpawnDoor=0x6cfe # +OP_GroundSpawn=0x5f0d # was 0x5f0d +OP_SendZonepoints=0x5f51 # was 0x0ff4 +OP_SendAAStats=0x0000 # C +OP_WorldObjectsSent=0x7b73 # was 0x7b73 +OP_BlockedBuffs=0x664a # was 0x4027 +OP_SendExpZonein=0x2c27 # was 0x1436 +OP_SendTributes=0x0000 # V +OP_TributeInfo=0x0000 # V +OP_SendGuildTributes=0x0000 # C 0x0000 +OP_AAExpUpdate=0x0000 # V +OP_ExpUpdate=0x0000 # V +OP_HPUpdate=0x0000 # V +OP_ManaChange=0x0000 # C +OP_TGB=0x0000 # C +OP_SpecialMesg=0x3bf5 # was 0x14ff +OP_GuildMemberList=0x0000 # C +OP_GuildMOTD=0x0000 # V +OP_CharInventory=0x307d # +OP_WearChange=0x0000 # V +OP_ClientUpdate=0x7062 # was 0x7062 +OP_ClientReady=0x6cdc # was 0x6cdc +OP_SetServerFilter=0x0000 # V + +# Guild Opcodes +OP_GetGuildMOTD=0x0000 # C +OP_GetGuildMOTDReply=0x0000 # C +OP_GuildMemberUpdate=0x0000 # C +OP_GuildInvite=0x0000 # C +OP_GuildRemove=0x0000 # C +OP_GuildPeace=0x0000 # C +OP_SetGuildMOTD=0x0000 # C +OP_GuildList=0x0000 # C +OP_GuildWar=0x0000 # C +OP_GuildLeader=0x0000 # C +OP_GuildDelete=0x0000 # C +OP_GuildInviteAccept=0x0000 # C +OP_GuildDemote=0x0000 # C +OP_GuildPublicNote=0x0000 # C +OP_GuildManageBanker=0x0000 # C +OP_GuildBank=0x0000 # C +OP_SetGuildRank=0x0000 # C +OP_GuildUpdateURLAndChannel=0x0000 # C +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x0000 # C +OP_GMBecomeNPC=0x0000 # C +OP_GMZoneRequest=0x6f79 # was 0x18ea +OP_GMZoneRequest2=0x02d6 # was 0x3ad9 +OP_GMGoto=0x0000 # C +OP_GMSearchCorpse=0x0000 # C +OP_GMHideMe=0x0000 # C +OP_GMDelCorpse=0x0000 # C +OP_GMApproval=0x0000 # C +OP_GMToggle=0x0000 # C 0x0000 +OP_GMSummon=0x0000 # C +OP_GMEmoteZone=0x0000 # C +OP_GMEmoteWorld=0x0000 # C +OP_GMFind=0x0000 # C +OP_GMKick=0x0000 # C +OP_GMKill=0x0000 # C +OP_GMNameChange=0x0000 # C +OP_GMLastName=0x0000 # C + +OP_InspectAnswer=0x0000 # C +OP_BeginCast=0x0000 # C +OP_BuffFadeMsg=0x50c2 # C +OP_ConsentResponse=0x0000 # C +OP_MemorizeSpell=0x0000 # C +OP_SwapSpell=0x0000 # C +OP_CastSpell=0x7286 # C +OP_Consider=0x0000 # C +OP_FormattedMessage=0x0000 # C +OP_SimpleMessage=0x0000 # C 0x0000 +OP_Buff=0x0000 # C +OP_Illusion=0x0000 # C +OP_MoneyOnCorpse=0x0000 # C +OP_RandomReply=0x0000 # C +OP_DenyResponse=0x0000 # C +OP_SkillUpdate=0x0000 # C +OP_GMTrainSkillConfirm=0x0000 # C +OP_RandomReq=0x0000 # C +OP_Death=0x0000 # C +OP_Bind_Wound=0x0000 # C +OP_GMTraining=0x0000 # C +OP_GMEndTraining=0x0000 # C +OP_GMTrainSkill=0x0000 # C +OP_Animation=0x0000 # Was 0x0000 +OP_Begging=0x0000 # C +OP_Consent=0x0000 # C +OP_ConsentDeny=0x0000 # C +OP_AutoFire=0x0000 # C +OP_PetCommands=0x0000 # C +OP_DeleteSpell=0x0000 # C +OP_Surname=0x0000 # C +OP_ClearSurname=0x0000 # C +OP_FaceChange=0x0000 # C +OP_SenseHeading=0x0000 # C +OP_Action=0x1513 # C +OP_ConsiderCorpse=0x0000 # C +OP_HideCorpse=0x0000 # C 0x0000 +OP_Bug=0x0000 # C +OP_Feedback=0x0000 # C +OP_Report=0x0000 # C +OP_Damage=0x7519 # C or OP_Action2? +OP_ChannelMessage=0x2e79 # was 0x2e79 +OP_Assist=0x0000 # C +OP_AssistGroup=0x0000 # C +OP_MoveCoin=0x0000 # C +OP_ZonePlayerToBind=0x0000 # C +OP_KeyRing=0x0000 # C +OP_WhoAllRequest=0x0000 # C +OP_WhoAllResponse=0x0000 # C +OP_FriendsWho=0x0000 # C +OP_ConfirmDelete=0x0000 # V +OP_Logout=0x44ae # was 0x64ec +OP_Rewind=0x0000 # C +OP_TargetCommand=0x0000 # C Was 0x0000 +OP_InspectRequest=0x0000 # C +OP_Hide=0x0000 # C +OP_Jump=0x0000 # C +OP_Camp=0x42ef # C +OP_Emote=0x0000 # C +OP_SetRunMode=0x0000 # C +OP_BankerChange=0x0000 # C +OP_TargetMouse=0x36f8 # C 0x0000 +OP_MobHealth=0x0000 # C +OP_InitialMobHealth=0x0000 # C +OP_TargetHoTT=0x0000 # C +OP_TargetBuffs=0x0000 # C +OP_BuffCreate=0x6bfb # V +OP_DeleteSpawn=0x7434 # was 0x7434 +OP_AutoAttack=0x0000 # C +OP_AutoAttack2=0x0000 # C +OP_Consume=0x0000 # V +OP_MoveItem=0x0000 # C +OP_DeleteItem=0x0000 # C +OP_DeleteCharge=0x0000 # C +OP_ItemPacket=0x7c87 # +OP_ItemLinkResponse=0x0000 # C +OP_ItemLinkClick=0x0000 # C +OP_NewSpawn=0x0000 # C +OP_Track=0x0000 # C +OP_TrackTarget=0x0000 # C +OP_TrackUnknown=0x0000 # C +OP_ClickDoor=0x470e # was 0x4f1f +OP_MoveDoor=0x48f9 # was 0x1516 +OP_EnvDamage=0x0000 # C +OP_BoardBoat=0x0000 # C +OP_Forage=0x0000 # C +OP_LeaveBoat=0x0000 # C +OP_ControlBoat=0x0000 # C +OP_SafeFallSuccess=0x0000 # C +OP_RezzComplete=0x0000 # C +OP_RezzRequest=0x0000 # C +OP_RezzAnswer=0x0000 # C +OP_Shielding=0x0000 # C +OP_RequestDuel=0x0000 # C +OP_MobRename=0x0000 # C +OP_AugmentItem=0x0000 # C +OP_WeaponEquip1=0x0000 # C +OP_WeaponEquip2=0x0000 # C +OP_WeaponUnequip2=0x0000 # C +OP_ApplyPoison=0x0000 # C +OP_Save=0x0000 # C +OP_TestBuff=0x0000 # C +OP_CustomTitles=0x0000 # C +OP_Split=0x0000 # C +OP_YellForHelp=0x0000 # C +OP_LoadSpellSet=0x0000 # C +OP_Bandolier=0x0000 # C +OP_PotionBelt=0x0000 # C +OP_DuelResponse=0x0000 # C +OP_DuelResponse2=0x0000 # C +OP_SaveOnZoneReq=0x2913 # was 0x2913 +OP_ReadBook=0x0000 # C +OP_Dye=0x0000 # C +OP_InterruptCast=0x0000 # C +OP_AAAction=0x0000 # C +OP_LeadershipExpToggle=0x0000 # C +OP_LeadershipExpUpdate=0x0000 # C +OP_PurchaseLeadershipAA=0x0000 # C +OP_UpdateLeadershipAA=0x0000 # C +OP_MarkNPC=0x0000 # C +OP_ClearNPCMarks=0x0000 # C +OP_DoGroupLeadershipAbility=0x0000 # C +OP_GroupLeadershipAAUpdate=0x0000 # C +OP_DelegateAbility=0x0000 # C +OP_SetGroupTarget=0x0000 # C +OP_Charm=0x0000 # C +OP_Stun=0x0000 # C +OP_SendFindableNPCs=0x49f6 # C +OP_FindPersonRequest=0x0000 # C +OP_FindPersonReply=0x0000 # C +OP_Sound=0x0000 # C +OP_PetBuffWindow=0x0000 # C +OP_LevelAppearance=0x0000 # C +OP_Translocate=0x0000 # C +OP_Sacrifice=0x0000 # C +OP_PopupResponse=0x0000 # C +OP_OnLevelMessage=0x0000 # C +OP_AugmentInfo=0x0000 # C +OP_Petition=0x0000 # C +OP_SomeItemPacketMaybe=0x4200 # C +OP_SomeItemPacketMaybe=0x0000 # C +OP_PVPStats=0x0000 # C +OP_PVPLeaderBoardRequest=0x0000 # C +OP_PVPLeaderBoardReply=0x0000 # C +OP_PVPLeaderBoardDetailsRequest=0x0000 # C +OP_PVPLeaderBoardDetailsReply=0x0000 # C +OP_RestState=0x0000 # C +OP_RespawnWindow=0x0000 # C +OP_DisciplineTimer=0x0000 # C +OP_LDoNButton=0x0000 # C +OP_SetStartCity=0x0000 # C +OP_VoiceMacroIn=0x0000 # C +OP_VoiceMacroOut=0x0000 # C +OP_ItemViewUnknown=0x0000 # C +OP_VetRewardsAvaliable=0x0000 # C Mispelled? +OP_VetClaimRequest=0x0000 # C +OP_VetClaimReply=0x0000 # C +OP_CrystalCountUpdate=0x0000 # C +OP_DisciplineUpdate=0x0000 # +OP_BecomeCorpse=0x0000 # +OP_Action2=0x0000 # C OP_Damage? +OP_MobUpdate=0x4656 # was 0x4656 +OP_NPCMoveUpdate=0x5bad # +OP_CameraEffect=0x0000 # V +OP_SpellEffect=0x0000 # V + +OP_DzQuit=0x0000 +OP_DzListTimers=0x0000 +OP_DzAddPlayer=0x0000 +OP_DzRemovePlayer=0x0000 +OP_DzSwapPlayer=0x0000 +OP_DzMakeLeader=0x0000 +OP_DzPlayerList=0x0000 +OP_DzJoinExpeditionConfirm=0x0000 +OP_DzJoinExpeditionReply=0x0000 +OP_DzExpeditionInfo=0x0000 +OP_DzMemberStatus=0x0000 +OP_DzLeaderStatus=0x0000 +OP_DzExpeditionEndsWarning=0x0000 +OP_DzExpeditionList=0x0000 +OP_DzMemberList=0x0000 +OP_DzCompass=0x0000 +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # C +OP_ManaUpdate=0x0000 # C +OP_EnduranceUpdate=0x0000 # C +OP_MobManaUpdate=0x0000 # C +OP_MobEnduranceUpdate=0x0000 # C + +# Looting +OP_LootRequest=0x0000 # C +OP_EndLootRequest=0x0000 # C +OP_LootItem=0x0000 # C +OP_LootComplete=0x0000 # C + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x0000 # C +OP_TraderDelItem=0x0000 # C +OP_BecomeTrader=0x0000 # C +OP_TraderShop=0x0000 # C +OP_Trader=0x0000 # C +OP_TraderBuy=0x0000 # C +OP_Barter=0x0000 # C +OP_ShopItem=0x0000 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # +OP_TraderItemUpdate=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x0000 # C +OP_TradeAcceptClick=0x0000 # C +OP_TradeRequestAck=0x0000 # C +OP_TradeCoins=0x0000 # C +OP_FinishTrade=0x0000 # C +OP_CancelTrade=0x0000 # C +OP_TradeMoneyUpdate=0x0000 # C +OP_MoneyUpdate=0x0000 # C +OP_TradeBusy=0x0000 # C + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x0000 # C +OP_FinishWindow2=0x0000 # C + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x0000 # C +OP_ItemVerifyReply=0x0000 # C + +# merchant crap +OP_ShopPlayerSell=0x0000 # C +OP_ShopRequest=0x1044 # was 0x1044 +OP_ShopEnd=0x3753 # was 0x3753 +OP_ShopEndConfirm=0x0000 # C +OP_ShopPlayerBuy=0x0000 # C +OP_ShopDelItem=0x0000 # C + +# tradeskill stuff: +OP_ClickObject=0x0000 # V +OP_ClickObjectAction=0x0000 # V +OP_ClearObject=0x0000 # C +OP_RecipeDetails=0x0000 # C +OP_RecipesFavorite=0x0000 # C +OP_RecipesSearch=0x0000 # C +OP_RecipeReply=0x0000 # C +OP_RecipeAutoCombine=0x0000 # C +OP_TradeSkillCombine=0x0000 # C + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x0000 # C +OP_OpenTributeMaster=0x0000 # C +OP_SelectTribute=0x0000 # C +OP_TributeItem=0x0000 # C +OP_TributeMoney=0x0000 # C +OP_TributeToggle=0x0000 # C +OP_TributePointUpdate=0x0000 # C +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x0000 # C +OP_AdventureFinish=0x0000 # C +OP_AdventureInfoRequest=0x0000 # C +OP_AdventureInfo=0x0000 # C +OP_AdventureRequest=0x0000 # C +OP_AdventureDetails=0x0000 # C +OP_AdventureData=0x0000 # C +OP_AdventureUpdate=0x0000 # C +OP_AdventureMerchantRequest=0x0000 # C +OP_AdventureMerchantResponse=0x0000 # C +OP_AdventureMerchantPurchase=0x0000 # C +OP_AdventureMerchantSell=0x0000 # C +OP_AdventurePointsUpdate=0x0000 # C +OP_AdventureStatsRequest=0x0000 # C +OP_AdventureStatsReply=0x0000 # C +OP_AdventureLeaderboardRequest=0x0000 # C +OP_AdventureLeaderboardReply=0x0000 # C + +# Group Opcodes +OP_GroupDisband=0x0000 # C +OP_GroupInvite=0x0000 # C +OP_GroupFollow=0x0000 # C +OP_GroupUpdate=0x0000 # C +OP_GroupUpdateB=0x0000 # C +OP_GroupCancelInvite=0x0000 # C - Same as OP_CancelInvite? +OP_GroupAcknowledge=0x0000 # C +OP_GroupDelete=0x0000 # +OP_CancelInvite=0x0000 # C +OP_GroupFollow2=0x0000 # C +OP_GroupInvite2=0x0000 # C +OP_GroupDisbandYou=0x0000 # C +OP_GroupDisbandOther=0x0000 # C +OP_GroupLeaderChange=0x0000 # C +OP_GroupRoles=0x0000 # C + +# LFG/LFP Opcodes +OP_LFGCommand=0x0000 # C +OP_LFGGetMatchesRequest=0x0000 # C +OP_LFGGetMatchesResponse=0x0000 # C +OP_LFPGetMatchesRequest=0x0000 # C +OP_LFPGetMatchesResponse=0x0000 # C +OP_LFPCommand=0x0000 # C +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x0000 # C +OP_RaidUpdate=0x0000 # C +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x0000 # C +OP_CombatAbility=0x0000 # C +OP_SenseTraps=0x0000 # C +OP_PickPocket=0x0000 # C +OP_DisarmTraps=0x0000 # +OP_Disarm=0x0000 # C +OP_Sneak=0x0000 # C +OP_Fishing=0x0000 # C +OP_InstillDoubt=0x0000 # C +OP_FeignDeath=0x0000 # C +OP_Mend=0x0000 # C +OP_LDoNOpen=0x0000 # C + +# Task packets +OP_TaskActivityComplete=0x0000 # C +OP_TaskMemberList=0x0000 # C +OP_OpenNewTasksWindow=0x0000 # C +OP_AvaliableTask=0x0000 # C Mispelled? +OP_AcceptNewTask=0x0000 # C +OP_TaskHistoryRequest=0x0000 # C +OP_TaskHistoryReply=0x0000 # C +OP_CancelTask=0x0000 # C +OP_DeclineAllTasks=0x0000 # + +# Title opcodes +OP_NewTitlesAvailable=0x0000 # C +OP_RequestTitles=0x0000 # C +OP_SendTitleList=0x0000 # C +OP_SetTitle=0x0000 # C +OP_SetTitleReply=0x0000 # C + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_IncreaseStats=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x0000 # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x0000 # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0000 # +OP_Login=0x0000 # +OP_ServerListRequest=0x0000 # +OP_PlayEverquestRequest=0x0000 # +OP_PlayEverquestResponse=0x0000 # +OP_ChatMessage=0x0000 # +OP_LoginAccepted=0x0000 # +OP_ServerListResponse=0x0000 # +OP_Poll=0x0000 # +OP_EnterChat=0x0000 # +OP_PollResponse=0x0000 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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_OPCode2511=0x2511 \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/patch_Feb11-2013.conf b/utils/EQExtractor2/EQExtractor2/patch_Feb11-2013.conf new file mode 100644 index 000000000..5add60724 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_Feb11-2013.conf @@ -0,0 +1,653 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 used for unknown explorer + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x5e9e +OP_ApproveWorld=0x5a47 +OP_LogServer=0x0c30 +OP_SendCharInfo=0x03cc +OP_ExpansionInfo=0x3c7a +OP_GuildsList=0x12bc +OP_EnterWorld=0x74de +OP_PostEnterWorld=0x6e03 +OP_World_Client_CRC1=0x2625 +OP_World_Client_CRC2=0x0799 +OP_SendSpellChecksum=0x0000 +OP_SendSkillCapsChecksum=0x0000 + +# Character Select Related: +OP_SendMaxCharacters=0x11be +OP_SendMembership=0x76f8 +OP_SendMembershipDetails=0x7475 +OP_CharacterCreateRequest=0x37c2 +OP_CharacterCreate=0x56aa +OP_DeleteCharacter=0x4572 +OP_RandomNameGenerator=0x045b +OP_ApproveName=0x077e +OP_MOTD=0x4c33 +OP_SetChatServer=0x5b4c +OP_SetChatServer2=0x70b3 +OP_ZoneServerInfo=0x559f +OP_WorldComplete=0x0d7c +OP_WorldUnknown001=0x0b85 +OP_FloatListThing=0x15f4 + +# Reasons for Disconnect: +OP_ZoneUnavail=0x4a6f +OP_WorldClientReady=0x27f4 +OP_CharacterStillInZone=0x0000 +OP_WorldChecksumFailure=0x0000 +OP_WorldLoginFailed=0x0000 +OP_WorldLogout=0x0000 +OP_WorldLevelTooHigh=0x0000 +OP_CharInacessable=0x0000 +OP_UserCompInfo=0x0000 +OP_SendExeChecksum=0x0000 +OP_SendBaseDataChecksum=0x0000 + +# Zone in opcodes +OP_AckPacket=0x5ee8 +OP_ZoneEntry=0x2cbe +OP_ReqNewZone=0x724c +OP_NewZone=0x082d +OP_ZoneSpawns=0x173c +OP_PlayerProfile=0x6725 +OP_TimeOfDay=0x2d98 +OP_LevelUpdate=0x698e +OP_Stamina=0x54fb +OP_RequestClientZoneChange=0x029d +OP_ZoneChange=0x499e +OP_LockoutTimerInfo=0x0000 +OP_ZoneServerReady=0x0000 +OP_ZoneInUnknown=0x0000 +OP_LogoutReply=0x0000 +OP_PreLogoutReply=0x0000 + +# Required to fully log in +OP_SpawnAppearance=0x6087 +OP_ChangeSize=0x0aad +OP_TributeUpdate=0x5026 +OP_TributeTimer=0x2027 +OP_SendTributes=0x38ef +OP_SendGuildTributes=0x115f +OP_TributeInfo=0x7097 +OP_Weather=0x1a00 +OP_ReqClientSpawn=0x6cd1 +OP_SpawnDoor=0x0e70 +OP_GroundSpawn=0x11ef +OP_SendZonepoints=0x0a1d +OP_BlockedBuffs=0x6095 +OP_RemoveBlockedBuffs=0x02cc +OP_ClearBlockedBuffs=0x1b26 +OP_WorldObjectsSent=0x3f13 +OP_SendExpZonein=0x7303 +OP_SendAATable=0x6b93 +OP_RespondAA=0x5a5c +OP_UpdateAA=0x1820 +OP_SendAAStats=0x6c5d +OP_AAExpUpdate=0x0eae +OP_ExpUpdate=0x7814 +OP_HPUpdate=0x069e +OP_ManaChange=0x0a79 +OP_TGB=0x2f0a +OP_SpecialMesg=0x7d12 +OP_GuildMemberList=0x210b +OP_GuildMOTD=0x7864 +OP_CharInventory=0x4cd2 +OP_WearChange=0x6a1a +OP_ClientUpdate=0x6cc2 +OP_ClientReady=0x2bd4 # 0x422d +OP_SetServerFilter=0x59ab + +# Guild Opcodes - Disabled until crashes are resolved in RoF +OP_GetGuildMOTD=0x58dd # Was 0x35dc +OP_GetGuildMOTDReply=0x45d5 # Was 0x4586 +OP_GuildMemberUpdate=0x6719 # Was 0x5643 +OP_GuildInvite=0x37d8 +OP_GuildRemove=0x0e16 +OP_GuildPeace=0x25c1 +OP_SetGuildMOTD=0x0b26 +OP_GuildList=0x0000 +OP_GuildWar=0x56fd +OP_GuildLeader=0x38cf +OP_GuildDelete=0x7135 +OP_GuildInviteAccept=0x3277 +OP_GuildDemote=0x3798 +OP_GuildPublicNote=0x36e2 +OP_GuildManageBanker=0x3ff3 # Was 0x0737 +OP_GuildBank=0x372e # Was 0x10c3 +OP_SetGuildRank=0x3d22 +OP_GuildUpdateURLAndChannel=0x6f0c +OP_GuildStatus=0x423b +OP_GuildCreate=0x057e # or maybe 0x086e +OP_GuildMemberLevelUpdate=0x0000 # Unused? +OP_ZoneGuildList=0x0000 # Unused? +OP_GetGuildsList=0x0000 # Unused? +OP_LFGuild=0x0000 +OP_GuildManageRemove=0x0000 +OP_GuildManageAdd=0x0000 +OP_GuildManageStatus=0x0000 + +# GM/Guide Opcodes +OP_GMServers=0x362a +OP_GMBecomeNPC=0x783b +OP_GMZoneRequest=0x6028 +OP_GMZoneRequest2=0x4cc5 +OP_GMGoto=0x4234 +OP_GMSearchCorpse=0x0ac5 +OP_GMHideMe=0x39a2 +OP_GMDelCorpse=0x2885 +OP_GMApproval=0x5e62 +OP_GMToggle=0x1c2c +OP_GMSummon=0x1b2a # Was 0x684f +OP_GMEmoteZone=0x54da # Was 0x0655 +OP_GMEmoteWorld=0x32ee # Was 0x1935 +OP_GMFind=0x3f2c +OP_GMKick=0x4bd5 +OP_GMKill=0x2591 +OP_GMNameChange=0x1e00 # Was 0x4434 +OP_GMLastName=0x0c17 # Was 0x3077 + +# Misc Opcodes +OP_InspectRequest=0x5440 +OP_InspectAnswer=0x31e3 +OP_InspectMessageUpdate=0x351a +OP_BeginCast=0x0b2b +OP_BuffFadeMsg=0x0076 +OP_ConsentResponse=0x7903 +OP_MemorizeSpell=0x5dbc +OP_SwapSpell=0x2642 +OP_CastSpell=0x0e18 +OP_Consider=0x0fcd +OP_FormattedMessage=0x748e +OP_SimpleMessage=0x3ecd +OP_Buff=0x1a9b +OP_Illusion=0x359a +OP_MoneyOnCorpse=0x1107 +OP_RandomReply=0x5cc5 +OP_DenyResponse=0x3c2d +OP_SkillUpdate=0x7eb4 +OP_GMTrainSkillConfirm=0x157c # 0x3960 +OP_RandomReq=0x4fd0 +OP_Death=0x62bd +OP_GMTraining=0x6f12 +OP_GMEndTraining=0x6f0a +OP_GMTrainSkill=0x2082 +OP_Animation=0x62f1 +OP_Begging=0x1a49 +OP_Consent=0x5b9e +OP_ConsentDeny=0x56ae +OP_AutoFire=0x479d +OP_PetCommands=0x46a0 +OP_DeleteSpell=0x27da +OP_Surname=0x5dd0 +OP_ClearSurname=0x5900 +OP_FaceChange=0x474e +OP_SenseHeading=0x2a34 +OP_Action=0x16a5 +OP_ConsiderCorpse=0x1683 +OP_HideCorpse=0x1102 +OP_CorpseDrag=0x32cc +OP_CorpseDrop=0x1224 +OP_Bug=0x41ac +OP_Feedback=0x3c4a +OP_Report=0x1b2f +OP_Damage=0x1375 +OP_ChannelMessage=0x0cf4 +OP_Assist=0x50c5 +OP_AssistGroup=0x7f67 +OP_MoveCoin=0x6928 +OP_ZonePlayerToBind=0x5a82 +OP_KeyRing=0x566e +OP_WhoAllRequest=0x0033 +OP_WhoAllResponse=0x68f6 +OP_FriendsWho=0x5988 +OP_ConfirmDelete=0x51d3 +OP_Logout=0x627f +OP_Rewind=0x29c7 +OP_TargetCommand=0x6952 +OP_Hide=0x5d4d +OP_Jump=0x7611 +OP_Camp=0x4863 +OP_Emote=0x0bff +OP_SetRunMode=0x26b3 +OP_BankerChange=0x36a6 +OP_TargetMouse=0x02f7 +OP_MobHealth=0x217a +OP_InitialMobHealth=0x0000 # Unused? +OP_TargetHoTT=0x1fd6 +OP_XTargetResponse=0x6d47 +OP_XTargetRequest=0x2603 +OP_XTargetAutoAddHaters=0x5fe3 +OP_TargetBuffs=0x4c77 +OP_BuffCreate=0x20ee +OP_BuffRemoveRequest=0x7ca3 +OP_DeleteSpawn=0x0ead +OP_AutoAttack=0x12fb +OP_AutoAttack2=0x1763 +OP_Consume=0x6aa6 +OP_MoveItem=0x68a2 +OP_DeleteItem=0x46c4 +OP_DeleteCharge=0x6933 +OP_ItemPacket=0x5ae8 +OP_ItemLinkResponse=0x2d88 +OP_ItemLinkClick=0x7404 +OP_ItemPreview=0x198c +OP_NewSpawn=0x56e9 +OP_Track=0x3089 +OP_TrackTarget=0x31d4 +OP_TrackUnknown=0x50b2 +OP_ClickDoor=0x0cc4 +OP_MoveDoor=0x3162 +OP_RemoveAllDoors=0x0712 +OP_EnvDamage=0x4a49 +OP_BoardBoat=0x10b0 +OP_Forage=0x02ed +OP_LeaveBoat=0x398e +OP_ControlBoat=0x1793 +OP_SafeFallSuccess=0x1c7e +OP_RezzComplete=0x62d6 +OP_RezzRequest=0x095b +OP_RezzAnswer=0x4b2c +OP_Shielding=0x2b69 +OP_RequestDuel=0x24d4 +OP_MobRename=0x7658 +OP_AugmentItem=0x7ba5 # Was 0x37cb +OP_WeaponEquip1=0x34bc +OP_WeaponEquip2=0x6f7e # Was 0x6022 +OP_WeaponUnequip2=0x1343 # Was 0x0110 +OP_ApplyPoison=0x7d23 +OP_Save=0x27d9 +OP_TestBuff=0x61f9 # Was 0x3772 +OP_CustomTitles=0x475f +OP_Split=0x5e9a +OP_YellForHelp=0x0696 +OP_LoadSpellSet=0x6208 +OP_Bandolier=0x7cf8 +OP_PotionBelt=0x3cf9 # Was 0x4d3b +OP_DuelResponse=0x4f58 +OP_DuelResponse2=0x3455 +OP_SaveOnZoneReq=0x0070 +OP_ReadBook=0x25b0 +OP_Dye=0x11a8 +OP_InterruptCast=0x14b2 +OP_AAAction=0x183b +OP_LeadershipExpToggle=0x75b0 +OP_LeadershipExpUpdate=0x0e8f +OP_PurchaseLeadershipAA=0x4269 +OP_UpdateLeadershipAA=0x4fc0 +OP_MarkNPC=0x13d3 +OP_ClearNPCMarks=0x1593 +OP_DelegateAbility=0x100f +OP_SetGroupTarget=0x2de8 +OP_Charm=0x5978 +OP_Stun=0x04bd +OP_SendFindableNPCs=0x7b8a +OP_FindPersonRequest=0x5d79 +OP_FindPersonReply=0x2b23 +OP_Sound=0x03fa +OP_PetBuffWindow=0x1e1c +OP_LevelAppearance=0x0442 +OP_Translocate=0x2e84 +OP_Sacrifice=0x0d88 +OP_PopupResponse=0x04f7 +OP_OnLevelMessage=0x6a6b +OP_AugmentInfo=0x1fce +OP_Petition=0x152e +OP_SomeItemPacketMaybe=0x6ce8 +OP_PVPStats=0x125a # Unsure +OP_PVPLeaderBoardRequest=0x673e +OP_PVPLeaderBoardReply=0x66fd +OP_PVPLeaderBoardDetailsRequest=0x6bbf +OP_PVPLeaderBoardDetailsReply=0x78bd +OP_RestState=0x0812 +OP_RespawnWindow=0x3594 +OP_LDoNButton=0x1071 +OP_SetStartCity=0x2f81 # Was 0x2d1b +OP_VoiceMacroIn=0x7ed5 +OP_VoiceMacroOut=0x72ed +OP_ItemViewUnknown=0x1915 +OP_VetRewardsAvaliable=0xbdf1 +OP_VetClaimRequest=0x1711 +OP_VetClaimReply=0x7497 +OP_DisciplineUpdate=0x594d # Was 0x2f05 +OP_DisciplineTimer=0x2dab # Was 0x5e3f +OP_BecomeCorpse=0x0000 # Unused? +OP_Action2=0x0000 # Unused? +OP_MobUpdate=0x282a +OP_NPCMoveUpdate=0x7342 +OP_CameraEffect=0x1cee +OP_SpellEffect=0x2a61 +OP_RemoveNimbusEffect=0x4f63 +OP_AltCurrency=0x0852 +OP_AltCurrencyMerchantRequest=0x2efc +OP_AltCurrencyMerchantReply=0x13fb +OP_AltCurrencyPurchase=0x3e5a +OP_AltCurrencySell=0x24b3 +OP_AltCurrencySellSelection=0x0019 +OP_AltCurrencyReclaim=0x7f65 +OP_CrystalCountUpdate=0x0a13 # Was 0x3f60 +OP_CrystalCreate=0x1e17 # Was 0x5a82 +OP_CrystalReclaim=0x22f6 # Was 0x7616 +OP_Untargetable=0x46e0 +OP_IncreaseStats=0x598c +OP_Weblink=0x6f7f +#OP_OpenInventory=0x0000 # Likely does not exist in RoF -U +OP_OpenContainer=0x0000 + +OP_DzQuit=0x4839 +OP_DzListTimers=0x0e73 +OP_DzAddPlayer=0x3b05 +OP_DzRemovePlayer=0x68e7 +OP_DzSwapPlayer=0x75ee +OP_DzMakeLeader=0x7703 +OP_DzPlayerList=0x76bb +OP_DzJoinExpeditionConfirm=0x3c5b +OP_DzJoinExpeditionReply=0x7410 +OP_DzExpeditionInfo=0x6ba0 +OP_DzExpeditionList=0x1d95 +OP_DzMemberStatus=0x23b6 +OP_DzLeaderStatus=0x6835 +OP_DzExpeditionEndsWarning=0x75ea +OP_DzMemberList=0x1a45 +OP_DzCompass=0x188f # Was 0x4f09 +OP_DzChooseZone=0x0000 # Maybe 0x29d6 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # Actually OP_MobUpdate ? +OP_ManaUpdate=0x0396 +OP_EnduranceUpdate=0x7b61 +OP_MobManaUpdate=0x0247 +OP_MobEnduranceUpdate=0x7902 + +# Mercenary Opcodes +OP_MercenaryDataUpdateRequest=0x65fc +OP_MercenaryDataUpdate=0x23de +OP_MercenaryDataRequest=0x306a +OP_MercenaryDataResponse=0xbdb8 +OP_MercenaryHire=0x0453 +OP_MercenaryDismiss=0x3cfc +OP_MercenaryTimerRequest=0x1357 +OP_MercenaryTimer=0x54f5 +OP_MercenaryUnknown1=0x319c +OP_MercenaryCommand=0x6d04 +OP_MercenarySuspendRequest=0x3a36 +OP_MercenarySuspendResponse=0x4f8a +OP_MercenaryUnsuspendResponse=0x295a + +# Looting +OP_LootRequest=0x27fb +OP_EndLootRequest=0x29e7 +OP_LootItem=0x1aea +OP_LootComplete=0x917c + +# bazaar trader stuff: +OP_BazaarSearch=0x4fa8 +OP_TraderDelItem=0x0000 +OP_BecomeTrader=0x6a33 +OP_TraderShop=0x1faa +OP_Trader=0x27c6 # Was 0x6790 +OP_TraderBuy=0x0000 +OP_Barter=0x760c +OP_ShopItem=0x0000 +OP_BazaarInspect=0x0000 +OP_Bazaar=0x0000 +OP_TraderItemUpdate=0x0000 + +# pc/npc trading +OP_TradeRequest=0x0b3f +OP_TradeAcceptClick=0x1ac5 +OP_TradeRequestAck=0x3aa1 +OP_TradeCoins=0x0ef7 +OP_FinishTrade=0x0e46 +OP_CancelTrade=0x7edb +OP_TradeMoneyUpdate=0x3755 +OP_MoneyUpdate=0x325f +OP_TradeBusy=0x31c2 + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x3b51 +OP_FinishWindow2=0x0f8e + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x6089 +OP_ItemVerifyReply=0x73fc + +# merchant stuff +OP_ShopPlayerSell=0x1456 +OP_ShopRequest=0x1799 +OP_ShopEnd=0x1a56 +OP_ShopEndConfirm=0x5628 +OP_ShopPlayerBuy=0xad00 +OP_ShopDelItem=0x4420 + +# tradeskill stuff: +OP_ClickObject=0x7f78 +OP_ClickObjectAction=0x7283 +OP_ClearObject=0x10ea +OP_RecipeDetails=0x2251 +OP_RecipesFavorite=0x1665 +OP_RecipesSearch=0x11d6 +OP_RecipeReply=0x1045 +OP_RecipeAutoCombine=0x588f +OP_TradeSkillCombine=0x556b + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x57e4 +OP_OpenTributeMaster=0x472d # Was 0x40f5 +OP_SelectTribute=0x75e3 +OP_TributeItem=0x5789 +OP_TributeMoney=0x7d50 # Was 0x6fed +OP_TributeToggle=0x0e59 +OP_TributePointUpdate=0x0dff +OP_TributeNPC=0x0000 +OP_GuildTributeInfo=0x0000 +OP_OpenTributeReply=0x0000 +OP_GuildTributeStatus=0x0000 + +# Adventure packets: +OP_LeaveAdventure=0x7d78 +OP_AdventureFinish=0x2ddc +OP_AdventureInfoRequest=0x1780 +OP_AdventureInfo=0x099a +OP_AdventureRequest=0x101b +OP_AdventureDetails=0x3a56 +OP_AdventureData=0x18d0 +OP_AdventureUpdate=0x255c +OP_AdventureMerchantRequest=0x4383 # Was 654d +OP_AdventureMerchantResponse=0x0975 # Was 7949 +OP_AdventureMerchantPurchase=0x2681 # Was 155a +OP_AdventureMerchantSell=0x0529 # Was 389c +OP_AdventurePointsUpdate=0x75cb # Was 7589 +OP_AdventureStatsRequest=0x7451 +OP_AdventureStatsReply=0x1595 +OP_AdventureLeaderboardRequest=0x1099 +OP_AdventureLeaderboardReply=0x1f56 + +# Group Opcodes +OP_GroupDisband=0x351e +OP_GroupInvite=0x7cd7 +OP_GroupFollow=0x352b +OP_GroupUpdate=0x2244 +OP_GroupUpdateB=0x20d5 +OP_GroupCancelInvite=0x0000 +OP_GroupAcknowledge=0x3391 +OP_GroupDelete=0x3e4c +OP_CancelInvite=0x5e6d +OP_GroupFollow2=0x63b4 +OP_GroupInvite2=0x33e8 +OP_GroupDisbandYou=0x79d6 +OP_GroupDisbandOther=0x65c0 +OP_GroupLeaderChange=0x626d +OP_GroupRoles=0x3438 +OP_GroupMakeLeader=0x3188 +OP_DoGroupLeadershipAbility=0x302d +OP_GroupLeadershipAAUpdate=0x71bd + +# LFG/LFP Opcodes +OP_LFGCommand=0x4fed +OP_LFGGetMatchesRequest=0x236c +OP_LFGGetMatchesResponse=0x18fb +OP_LFPGetMatchesRequest=0x1a28 +OP_LFPGetMatchesResponse=0x5daa +OP_LFPCommand=0x6751 +OP_LFGAppearance=0x0000 +OP_LFGResponse=0x0000 + +# Raid Opcodes +OP_RaidInvite=0x7383 +OP_RaidUpdate=0x0594 +OP_RaidJoin=0x0000 + +# Button-push commands +OP_Taunt=0x56df +OP_CombatAbility=0x31f9 +OP_SenseTraps=0x555b # Was 0x2ee0 +OP_PickPocket=0x3f61 +OP_DisarmTraps=0x0000 +OP_Disarm=0x0efe +OP_Sneak=0x4631 +OP_Fishing=0x46e3 +OP_InstillDoubt=0x2980 +OP_FeignDeath=0x6a76 +OP_Mend=0x54fe +OP_Bind_Wound=0x5066 +OP_LDoNOpen=0x0173 + +# Task packets +OP_TaskDescription=0x0736 +OP_TaskActivity=0x74a1 +OP_CompletedTasks=0x7392 +OP_TaskActivityComplete=0x44a6 +OP_AcceptNewTask=0x1bbf +OP_CancelTask=0x590b +OP_TaskMemberList=0x0e5c # Was 0x1656 +OP_OpenNewTasksWindow=0x22ac # Was 0x11de +OP_AvaliableTask=0x12cd # Was 0x2377 +OP_TaskHistoryRequest=0x1489 +OP_TaskHistoryReply=0x2d43 +OP_DeclineAllTasks=0x0000 + +# Title opcodes +OP_NewTitlesAvailable=0x21d0 +OP_RequestTitles=0x6b48 +OP_SendTitleList=0x6af1 +OP_SetTitle=0x6d7b +OP_SetTitleReply=0x41ef + +# mail opcodes +OP_Command=0x0000 +OP_MailboxHeader=0x0000 +OP_MailHeader=0x0000 +OP_MailBody=0x0000 +OP_NewMail=0x0000 +OP_SentConfirm=0x0000 + +########### Below this point should not be needed ########### + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 +OP_LocInfo=0x0000 +OP_ReloadUI=0x0000 +OP_ItemName=0x0000 +OP_ItemLinkText=0x0000 +OP_MultiLineMsg=0x0000 +OP_MendHPUpdate=0x0000 +OP_TargetReject=0x0000 +OP_SafePoint=0x0000 +OP_ApproveZone=0x0000 +OP_ZoneComplete=0x0000 +OP_ClientError=0x0000 +OP_DumpName=0x0000 +OP_Heartbeat=0x0000 +OP_CrashDump=0x0000 +OP_LoginComplete=0x0000 + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 +OP_PlayMP3=0x0000 +OP_ReclaimCrystals=0x0000 +OP_DynamicWall=0x0000 +OP_OpenDiscordMerchant=0x0000 +OP_DiscordMerchantInventory=0x0000 +OP_GiveMoney=0x0000 +OP_RequestKnowledgeBase=0x0000 +OP_KnowledgeBase=0x0000 +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 +OP_Some6ByteHPUpdate=0x0000 seems to happen when you target group members +OP_QueryResponseThing=0x0000 + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x46d3 # OP_SendSpellChecksum +#OP_LoginUnknown2=0x040b # OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 search term for petition +OP_PetitionSearchResults=0x0000 (list of?) matches from search +OP_PetitionSearchText=0x0000 text results of search + +OP_PetitionUpdate=0x0000 +OP_PetitionCheckout=0x0000 +OP_PetitionCheckIn=0x0000 +OP_PetitionQue=0x0000 +OP_PetitionUnCheckout=0x0000 +OP_PetitionDelete=0x0000 +OP_DeletePetition=0x0000 +OP_PetitionResolve=0x0000 +OP_PDeletePetition=0x0000 +OP_PetitionBug=0x0000 +OP_PetitionRefresh=0x0000 +OP_PetitionCheckout2=0x0000 +OP_PetitionViewPetition=0x0000 + +# Login opcodes +OP_SessionReady=0x0000 +OP_Login=0x0000 +OP_ServerListRequest=0x0000 +OP_PlayEverquestRequest=0x0000 +OP_PlayEverquestResponse=0x0000 +OP_ChatMessage=0x0000 +OP_LoginAccepted=0x0000 +OP_ServerListResponse=0x0000 +OP_Poll=0x0000 +OP_EnterChat=0x0000 +OP_PollResponse=0x0000 + +# raw opcodes +OP_RAWSessionRequest=0x0000 +OP_RAWSessionResponse=0x0000 +OP_RAWCombined=0x0000 +OP_RAWSessionDisconnect=0x0000 +OP_RAWKeepAlive=0x0000 +OP_RAWSessionStatRequest=0x0000 +OP_RAWSessionStatResponse=0x0000 +OP_RAWPacket=0x0000 +OP_RAWFragment=0x0000 +OP_RAWOutOfOrderAck=0x0000 +OP_RAWAck=0x0000 +OP_RAWAppCombined=0x0000 +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 diff --git a/utils/EQExtractor2/EQExtractor2/patch_Feb8-2011.conf b/utils/EQExtractor2/EQExtractor2/patch_Feb8-2011.conf new file mode 100644 index 000000000..00285ef1c --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_Feb8-2011.conf @@ -0,0 +1,611 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x0000a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x6893 # was 0x2683 +OP_ApproveWorld=0x115a # was 0x28a7 +OP_LogServer=0x701f # was 0x224f +OP_SendCharInfo=0x1b85 # was 0x2c27 +OP_ExpansionInfo=0x1771 # Same +OP_GuildsList=0x5b0b # Same +OP_EnterWorld=0x3288 # was 0x4f60 +OP_PostEnterWorld=0x2f2c # was 0x570c +OP_World_Client_CRC1=0x0000 # C +OP_World_Client_CRC2=0x0000 # C +OP_SendSpellChecksum=0x0000 # C +OP_SendSkillCapsChecksum=0x0000 # C + +# Character Select Related: +OP_DeleteCharacter=0x0000 # C +OP_CharacterCreateRequest=0x0000 # C +OP_CharacterCreate=0x0000 # C +OP_RandomNameGenerator=0x0000 # C 0x0000 +OP_ApproveName=0x0000 # C + +OP_MOTD=0x7312 # C 0x0000 +OP_SetChatServer=0x698a # +OP_SetChatServer2=0x0000 # Not sent any more ? +OP_ZoneServerInfo=0x0479 # was 0x03cc +OP_WorldComplete=0x510c # +OP_WorldUnknown001=0x0000 # C 0x0000 +OP_FloatListThing=0x0000 # V + +# Reasons for Disconnect: +OP_ZoneUnavail=0x0000 # C 0x0000 +OP_WorldClientReady=0x399b # C 0x0000 +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # was 0x3594 +OP_ZoneEntry=0x002b # was 0x538f +OP_ReqNewZone=0x5ca5 # was 0x5ca5 +OP_NewZone=0x0254 # was 0x0254 +OP_ZoneSpawns=0x0000 # ? +OP_PlayerProfile=0x6022 # was 0x6022 +OP_TimeOfDay=0x6015 # was 0x6015 +OP_LevelUpdate=0x0000 # V +OP_Stamina=0x0000 # V +OP_RequestClientZoneChange=0x3fd2 # was 0x4178 +OP_ZoneChange=0x0b93 # was 0x4a61 +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x7b64 # V +OP_TributeUpdate=0x4895 # +OP_TributeTimer=0x0000 # C +OP_TaskDescription=0x0000 # C +OP_TaskActivity=0x0000 # C +OP_CompletedTasks=0x0000 # C 0x0000 +OP_Weather=0x7ce4 # was 0x0ed5 +OP_SendAATable=0x1b26 # +OP_UpdateAA=0x0000 # V +OP_RespondAA=0x0000 # C 0x0000 +OP_ReqClientSpawn=0x54e8 # was 0x6618 +OP_SpawnDoor=0x4849 # +OP_GroundSpawn=0x5f0d # was 0x5f0d +OP_SendZonepoints=0x5f51 # was 0x0ff4 +OP_SendAAStats=0x0000 # C +OP_WorldObjectsSent=0x7b73 # was 0x7b73 +OP_BlockedBuffs=0x664a # was 0x4027 +OP_SendExpZonein=0x2c27 # was 0x1436 +OP_SendTributes=0x0000 # V +OP_TributeInfo=0x0000 # V +OP_SendGuildTributes=0x0000 # C 0x0000 +OP_AAExpUpdate=0x0000 # V +OP_ExpUpdate=0x0000 # V +OP_HPUpdate=0x0000 # V +OP_ManaChange=0x0000 # C +OP_TGB=0x0000 # C +OP_SpecialMesg=0x3bf5 # was 0x14ff +OP_GuildMemberList=0x0000 # C +OP_GuildMOTD=0x0000 # V +OP_CharInventory=0x465e # +OP_WearChange=0x0000 # V +OP_ClientUpdate=0x7062 # was 0x7062 +OP_ClientReady=0x6cdc # was 0x6cdc +OP_SetServerFilter=0x0000 # V + +# Guild Opcodes +OP_GetGuildMOTD=0x0000 # C +OP_GetGuildMOTDReply=0x0000 # C +OP_GuildMemberUpdate=0x0000 # C +OP_GuildInvite=0x0000 # C +OP_GuildRemove=0x0000 # C +OP_GuildPeace=0x0000 # C +OP_SetGuildMOTD=0x0000 # C +OP_GuildList=0x0000 # C +OP_GuildWar=0x0000 # C +OP_GuildLeader=0x0000 # C +OP_GuildDelete=0x0000 # C +OP_GuildInviteAccept=0x0000 # C +OP_GuildDemote=0x0000 # C +OP_GuildPublicNote=0x0000 # C +OP_GuildManageBanker=0x0000 # C +OP_GuildBank=0x0000 # C +OP_SetGuildRank=0x0000 # C +OP_GuildUpdateURLAndChannel=0x0000 # C +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x0000 # C +OP_GMBecomeNPC=0x0000 # C +OP_GMZoneRequest=0x6f79 # was 0x18ea +OP_GMZoneRequest2=0x02d6 # was 0x3ad9 +OP_GMGoto=0x0000 # C +OP_GMSearchCorpse=0x0000 # C +OP_GMHideMe=0x0000 # C +OP_GMDelCorpse=0x0000 # C +OP_GMApproval=0x0000 # C +OP_GMToggle=0x0000 # C 0x0000 +OP_GMSummon=0x0000 # C +OP_GMEmoteZone=0x0000 # C +OP_GMEmoteWorld=0x0000 # C +OP_GMFind=0x0000 # C +OP_GMKick=0x0000 # C +OP_GMKill=0x0000 # C +OP_GMNameChange=0x0000 # C +OP_GMLastName=0x0000 # C + +OP_InspectAnswer=0x0000 # C +OP_BeginCast=0x0000 # C +OP_BuffFadeMsg=0x50c2 # C +OP_ConsentResponse=0x0000 # C +OP_MemorizeSpell=0x0000 # C +OP_SwapSpell=0x0000 # C +OP_CastSpell=0x7286 # C +OP_Consider=0x0000 # C +OP_FormattedMessage=0x0000 # C +OP_SimpleMessage=0x0000 # C 0x0000 +OP_Buff=0x0000 # C +OP_Illusion=0x0000 # C +OP_MoneyOnCorpse=0x0000 # C +OP_RandomReply=0x0000 # C +OP_DenyResponse=0x0000 # C +OP_SkillUpdate=0x0000 # C +OP_GMTrainSkillConfirm=0x0000 # C +OP_RandomReq=0x0000 # C +OP_Death=0x0000 # C +OP_Bind_Wound=0x0000 # C +OP_GMTraining=0x0000 # C +OP_GMEndTraining=0x0000 # C +OP_GMTrainSkill=0x0000 # C +OP_Animation=0x0000 # Was 0x0000 +OP_Begging=0x0000 # C +OP_Consent=0x0000 # C +OP_ConsentDeny=0x0000 # C +OP_AutoFire=0x0000 # C +OP_PetCommands=0x0000 # C +OP_DeleteSpell=0x0000 # C +OP_Surname=0x0000 # C +OP_ClearSurname=0x0000 # C +OP_FaceChange=0x0000 # C +OP_SenseHeading=0x0000 # C +OP_Action=0x1513 # C +OP_ConsiderCorpse=0x0000 # C +OP_HideCorpse=0x0000 # C 0x0000 +OP_Bug=0x0000 # C +OP_Feedback=0x0000 # C +OP_Report=0x0000 # C +OP_Damage=0x7519 # C or OP_Action2? +OP_ChannelMessage=0x2e79 # was 0x2e79 +OP_Assist=0x0000 # C +OP_AssistGroup=0x0000 # C +OP_MoveCoin=0x0000 # C +OP_ZonePlayerToBind=0x0000 # C +OP_KeyRing=0x0000 # C +OP_WhoAllRequest=0x0000 # C +OP_WhoAllResponse=0x0000 # C +OP_FriendsWho=0x0000 # C +OP_ConfirmDelete=0x0000 # V +OP_Logout=0x44ae # was 0x64ec +OP_Rewind=0x0000 # C +OP_TargetCommand=0x0000 # C Was 0x0000 +OP_InspectRequest=0x0000 # C +OP_Hide=0x0000 # C +OP_Jump=0x0000 # C +OP_Camp=0x42ef # C +OP_Emote=0x0000 # C +OP_SetRunMode=0x0000 # C +OP_BankerChange=0x0000 # C +OP_TargetMouse=0x36f8 # C 0x0000 +OP_MobHealth=0x0000 # C +OP_InitialMobHealth=0x0000 # C +OP_TargetHoTT=0x0000 # C +OP_TargetBuffs=0x0000 # C +OP_BuffCreate=0x6bfb # V +OP_DeleteSpawn=0x7434 # was 0x7434 +OP_AutoAttack=0x0000 # C +OP_AutoAttack2=0x0000 # C +OP_Consume=0x0000 # V +OP_MoveItem=0x0000 # C +OP_DeleteItem=0x0000 # C +OP_DeleteCharge=0x0000 # C +OP_ItemPacket=0x35b2 # +OP_ItemLinkResponse=0x0000 # C +OP_ItemLinkClick=0x0000 # C +OP_NewSpawn=0x0000 # C +OP_Track=0x0000 # C +OP_TrackTarget=0x0000 # C +OP_TrackUnknown=0x0000 # C +OP_ClickDoor=0x470e # was 0x4f1f +OP_MoveDoor=0x48f9 # was 0x1516 +OP_EnvDamage=0x0000 # C +OP_BoardBoat=0x0000 # C +OP_Forage=0x0000 # C +OP_LeaveBoat=0x0000 # C +OP_ControlBoat=0x0000 # C +OP_SafeFallSuccess=0x0000 # C +OP_RezzComplete=0x0000 # C +OP_RezzRequest=0x0000 # C +OP_RezzAnswer=0x0000 # C +OP_Shielding=0x0000 # C +OP_RequestDuel=0x0000 # C +OP_MobRename=0x0000 # C +OP_AugmentItem=0x0000 # C +OP_WeaponEquip1=0x0000 # C +OP_WeaponEquip2=0x0000 # C +OP_WeaponUnequip2=0x0000 # C +OP_ApplyPoison=0x0000 # C +OP_Save=0x0000 # C +OP_TestBuff=0x0000 # C +OP_CustomTitles=0x0000 # C +OP_Split=0x0000 # C +OP_YellForHelp=0x0000 # C +OP_LoadSpellSet=0x0000 # C +OP_Bandolier=0x0000 # C +OP_PotionBelt=0x0000 # C +OP_DuelResponse=0x0000 # C +OP_DuelResponse2=0x0000 # C +OP_SaveOnZoneReq=0x2913 # was 0x2913 +OP_ReadBook=0x0000 # C +OP_Dye=0x0000 # C +OP_InterruptCast=0x0000 # C +OP_AAAction=0x0000 # C +OP_LeadershipExpToggle=0x0000 # C +OP_LeadershipExpUpdate=0x0000 # C +OP_PurchaseLeadershipAA=0x0000 # C +OP_UpdateLeadershipAA=0x0000 # C +OP_MarkNPC=0x0000 # C +OP_ClearNPCMarks=0x0000 # C +OP_DoGroupLeadershipAbility=0x0000 # C +OP_GroupLeadershipAAUpdate=0x0000 # C +OP_DelegateAbility=0x0000 # C +OP_SetGroupTarget=0x0000 # C +OP_Charm=0x0000 # C +OP_Stun=0x0000 # C +OP_SendFindableNPCs=0x7eeb # C +OP_FindPersonRequest=0x0000 # C +OP_FindPersonReply=0x0000 # C +OP_Sound=0x0000 # C +OP_PetBuffWindow=0x0000 # C +OP_LevelAppearance=0x0000 # C +OP_Translocate=0x0000 # C +OP_Sacrifice=0x0000 # C +OP_PopupResponse=0x0000 # C +OP_OnLevelMessage=0x0000 # C +OP_AugmentInfo=0x0000 # C +OP_Petition=0x0000 # C +OP_SomeItemPacketMaybe=0x4200 # C +OP_SomeItemPacketMaybe=0x0000 # C +OP_PVPStats=0x0000 # C +OP_PVPLeaderBoardRequest=0x0000 # C +OP_PVPLeaderBoardReply=0x0000 # C +OP_PVPLeaderBoardDetailsRequest=0x0000 # C +OP_PVPLeaderBoardDetailsReply=0x0000 # C +OP_RestState=0x0000 # C +OP_RespawnWindow=0x0000 # C +OP_DisciplineTimer=0x0000 # C +OP_LDoNButton=0x0000 # C +OP_SetStartCity=0x0000 # C +OP_VoiceMacroIn=0x0000 # C +OP_VoiceMacroOut=0x0000 # C +OP_ItemViewUnknown=0x0000 # C +OP_VetRewardsAvaliable=0x0000 # C Mispelled? +OP_VetClaimRequest=0x0000 # C +OP_VetClaimReply=0x0000 # C +OP_CrystalCountUpdate=0x0000 # C +OP_DisciplineUpdate=0x0000 # +OP_BecomeCorpse=0x0000 # +OP_Action2=0x0000 # C OP_Damage? +OP_MobUpdate=0x4656 # was 0x4656 +OP_NPCMoveUpdate=0x5d24 # +OP_CameraEffect=0x0000 # V +OP_SpellEffect=0x0000 # V + +OP_DzQuit=0x0000 +OP_DzListTimers=0x0000 +OP_DzAddPlayer=0x0000 +OP_DzRemovePlayer=0x0000 +OP_DzSwapPlayer=0x0000 +OP_DzMakeLeader=0x0000 +OP_DzPlayerList=0x0000 +OP_DzJoinExpeditionConfirm=0x0000 +OP_DzJoinExpeditionReply=0x0000 +OP_DzExpeditionInfo=0x0000 +OP_DzMemberStatus=0x0000 +OP_DzLeaderStatus=0x0000 +OP_DzExpeditionEndsWarning=0x0000 +OP_DzExpeditionList=0x0000 +OP_DzMemberList=0x0000 +OP_DzCompass=0x0000 +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # C +OP_ManaUpdate=0x0000 # C +OP_EnduranceUpdate=0x0000 # C +OP_MobManaUpdate=0x0000 # C +OP_MobEnduranceUpdate=0x0000 # C + +# Looting +OP_LootRequest=0x0000 # C +OP_EndLootRequest=0x0000 # C +OP_LootItem=0x0000 # C +OP_LootComplete=0x0000 # C + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x0000 # C +OP_TraderDelItem=0x0000 # C +OP_BecomeTrader=0x0000 # C +OP_TraderShop=0x0000 # C +OP_Trader=0x0000 # C +OP_TraderBuy=0x0000 # C +OP_Barter=0x0000 # C +OP_ShopItem=0x0000 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # +OP_TraderItemUpdate=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x0000 # C +OP_TradeAcceptClick=0x0000 # C +OP_TradeRequestAck=0x0000 # C +OP_TradeCoins=0x0000 # C +OP_FinishTrade=0x0000 # C +OP_CancelTrade=0x0000 # C +OP_TradeMoneyUpdate=0x0000 # C +OP_MoneyUpdate=0x0000 # C +OP_TradeBusy=0x0000 # C + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x0000 # C +OP_FinishWindow2=0x0000 # C + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x0000 # C +OP_ItemVerifyReply=0x0000 # C + +# merchant crap +OP_ShopPlayerSell=0x0000 # C +OP_ShopRequest=0x1044 # was 0x1044 +OP_ShopEnd=0x3753 # was 0x3753 +OP_ShopEndConfirm=0x0000 # C +OP_ShopPlayerBuy=0x0000 # C +OP_ShopDelItem=0x0000 # C + +# tradeskill stuff: +OP_ClickObject=0x0000 # V +OP_ClickObjectAction=0x0000 # V +OP_ClearObject=0x0000 # C +OP_RecipeDetails=0x0000 # C +OP_RecipesFavorite=0x0000 # C +OP_RecipesSearch=0x0000 # C +OP_RecipeReply=0x0000 # C +OP_RecipeAutoCombine=0x0000 # C +OP_TradeSkillCombine=0x0000 # C + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x0000 # C +OP_OpenTributeMaster=0x0000 # C +OP_SelectTribute=0x0000 # C +OP_TributeItem=0x0000 # C +OP_TributeMoney=0x0000 # C +OP_TributeToggle=0x0000 # C +OP_TributePointUpdate=0x0000 # C +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x0000 # C +OP_AdventureFinish=0x0000 # C +OP_AdventureInfoRequest=0x0000 # C +OP_AdventureInfo=0x0000 # C +OP_AdventureRequest=0x0000 # C +OP_AdventureDetails=0x0000 # C +OP_AdventureData=0x0000 # C +OP_AdventureUpdate=0x0000 # C +OP_AdventureMerchantRequest=0x0000 # C +OP_AdventureMerchantResponse=0x0000 # C +OP_AdventureMerchantPurchase=0x0000 # C +OP_AdventureMerchantSell=0x0000 # C +OP_AdventurePointsUpdate=0x0000 # C +OP_AdventureStatsRequest=0x0000 # C +OP_AdventureStatsReply=0x0000 # C +OP_AdventureLeaderboardRequest=0x0000 # C +OP_AdventureLeaderboardReply=0x0000 # C + +# Group Opcodes +OP_GroupDisband=0x0000 # C +OP_GroupInvite=0x0000 # C +OP_GroupFollow=0x0000 # C +OP_GroupUpdate=0x0000 # C +OP_GroupUpdateB=0x0000 # C +OP_GroupCancelInvite=0x0000 # C - Same as OP_CancelInvite? +OP_GroupAcknowledge=0x0000 # C +OP_GroupDelete=0x0000 # +OP_CancelInvite=0x0000 # C +OP_GroupFollow2=0x0000 # C +OP_GroupInvite2=0x0000 # C +OP_GroupDisbandYou=0x0000 # C +OP_GroupDisbandOther=0x0000 # C +OP_GroupLeaderChange=0x0000 # C +OP_GroupRoles=0x0000 # C + +# LFG/LFP Opcodes +OP_LFGCommand=0x0000 # C +OP_LFGGetMatchesRequest=0x0000 # C +OP_LFGGetMatchesResponse=0x0000 # C +OP_LFPGetMatchesRequest=0x0000 # C +OP_LFPGetMatchesResponse=0x0000 # C +OP_LFPCommand=0x0000 # C +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x0000 # C +OP_RaidUpdate=0x0000 # C +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x0000 # C +OP_CombatAbility=0x0000 # C +OP_SenseTraps=0x0000 # C +OP_PickPocket=0x0000 # C +OP_DisarmTraps=0x0000 # +OP_Disarm=0x0000 # C +OP_Sneak=0x0000 # C +OP_Fishing=0x0000 # C +OP_InstillDoubt=0x0000 # C +OP_FeignDeath=0x0000 # C +OP_Mend=0x0000 # C +OP_LDoNOpen=0x0000 # C + +# Task packets +OP_TaskActivityComplete=0x0000 # C +OP_TaskMemberList=0x0000 # C +OP_OpenNewTasksWindow=0x0000 # C +OP_AvaliableTask=0x0000 # C Mispelled? +OP_AcceptNewTask=0x0000 # C +OP_TaskHistoryRequest=0x0000 # C +OP_TaskHistoryReply=0x0000 # C +OP_CancelTask=0x0000 # C +OP_DeclineAllTasks=0x0000 # + +# Title opcodes +OP_NewTitlesAvailable=0x0000 # C +OP_RequestTitles=0x0000 # C +OP_SendTitleList=0x0000 # C +OP_SetTitle=0x0000 # C +OP_SetTitleReply=0x0000 # C + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_IncreaseStats=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x0000 # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x0000 # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0000 # +OP_Login=0x0000 # +OP_ServerListRequest=0x0000 # +OP_PlayEverquestRequest=0x0000 # +OP_PlayEverquestResponse=0x0000 # +OP_ChatMessage=0x0000 # +OP_LoginAccepted=0x0000 # +OP_ServerListResponse=0x0000 # +OP_Poll=0x0000 # +OP_EnterChat=0x0000 # +OP_PollResponse=0x0000 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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_OPCode2511=0x2511 \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/patch_Jan16-2013.conf b/utils/EQExtractor2/EQExtractor2/patch_Jan16-2013.conf new file mode 100644 index 000000000..a2fea9ca6 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_Jan16-2013.conf @@ -0,0 +1,653 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 used for unknown explorer + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x7b72 +OP_ApproveWorld=0x11fe +OP_LogServer=0x055d +OP_SendCharInfo=0x58ca +OP_ExpansionInfo=0x323b +OP_GuildsList=0x5c3a +OP_EnterWorld=0x4e2b +OP_PostEnterWorld=0x3931 +OP_World_Client_CRC1=0x67bb +OP_World_Client_CRC2=0x08ea +OP_SendSpellChecksum=0x0000 +OP_SendSkillCapsChecksum=0x0000 + +# Character Select Related: +OP_SendMaxCharacters=0x2960 +OP_SendMembership=0x46d8 +OP_SendMembershipDetails=0x1af8 +OP_CharacterCreateRequest=0x1a95 +OP_CharacterCreate=0x74c4 +OP_DeleteCharacter=0x63ba +OP_RandomNameGenerator=0x0528 +OP_ApproveName=0x5ae0 +OP_MOTD=0x5cc4 +OP_SetChatServer=0x07c6 +OP_SetChatServer2=0x421b +OP_ZoneServerInfo=0x1cfa +OP_WorldComplete=0x06f2 +OP_WorldUnknown001=0x39de +OP_FloatListThing=0x62d2 + +# Reasons for Disconnect: +OP_ZoneUnavail=0x5047 +OP_WorldClientReady=0x0b02 +OP_CharacterStillInZone=0x0000 +OP_WorldChecksumFailure=0x0000 +OP_WorldLoginFailed=0x0000 +OP_WorldLogout=0x0000 +OP_WorldLevelTooHigh=0x0000 +OP_CharInacessable=0x0000 +OP_UserCompInfo=0x0000 +OP_SendExeChecksum=0x0000 +OP_SendBaseDataChecksum=0x0000 + +# Zone in opcodes +OP_AckPacket=0x08d6 +OP_ZoneEntry=0x60a8 +OP_ReqNewZone=0x0010 +OP_NewZone=0x18cd +OP_ZoneSpawns=0x20da +OP_PlayerProfile=0x5772 +OP_TimeOfDay=0x5824 +OP_LevelUpdate=0x427d +OP_Stamina=0x4932 +OP_RequestClientZoneChange=0x70f9 +OP_ZoneChange=0xb646 +OP_LockoutTimerInfo=0x0000 +OP_ZoneServerReady=0x0000 +OP_ZoneInUnknown=0x0000 +OP_LogoutReply=0x0000 +OP_PreLogoutReply=0x0000 + +# Required to fully log in +OP_SpawnAppearance=0x201a +OP_ChangeSize=0x27b3 +OP_TributeUpdate=0x42e0 +OP_TributeTimer=0x5008 +OP_SendTributes=0x3cca +OP_SendGuildTributes=0x36e4 +OP_TributeInfo=0xde3a +OP_Weather=0x7fca +OP_ReqClientSpawn=0x0ed4 +OP_SpawnDoor=0x31ed +OP_GroundSpawn=0x56b2 +OP_SendZonepoints=0x0d72 +OP_BlockedBuffs=0x2b96 +OP_RemoveBlockedBuffs=0x1702 +OP_ClearBlockedBuffs=0x53eb +OP_WorldObjectsSent=0x59c9 +OP_SendExpZonein=0x17b6 +OP_SendAATable=0x7017 +OP_RespondAA=0x4f1a +OP_UpdateAA=0x7eb4 +OP_SendAAStats=0x46af +OP_AAExpUpdate=0x4212 +OP_ExpUpdate=0x4ecc +OP_HPUpdate=0x65e7 +OP_ManaChange=0x7d4d +OP_TGB=0x32ec +OP_SpecialMesg=0x7d26 +OP_GuildMemberList=0x183b +OP_GuildMOTD=0x5531 +OP_CharInventory=0x44db +OP_WearChange=0x32fb +OP_ClientUpdate=0x0be5 +OP_ClientReady=0x7aa4 # 0x422d +OP_SetServerFilter=0x7958 + +# Guild Opcodes - Disabled until crashes are resolved in RoF +OP_GetGuildMOTD=0x7332 # Was 0x35dc +OP_GetGuildMOTDReply=0x74dd # Was 0x4586 +OP_GuildMemberUpdate=0x24da # Was 0x5643 +OP_GuildInvite=0x15e1 +OP_GuildRemove=0x45ae +OP_GuildPeace=0x01fc +OP_SetGuildMOTD=0x4467 +OP_GuildList=0x0000 +OP_GuildWar=0x2ab7 +OP_GuildLeader=0x0b1a +OP_GuildDelete=0x16ff +OP_GuildInviteAccept=0x2fe8 +OP_GuildDemote=0x52de +OP_GuildPublicNote=0x3838 +OP_GuildManageBanker=0x0116 # Was 0x0737 +OP_GuildBank=0x2c0e # Was 0x10c3 +OP_SetGuildRank=0x3894 +OP_GuildUpdateURLAndChannel=0x11b1 +OP_GuildStatus=0x3002 +OP_GuildCreate=0x1246 # or maybe 0x086e +OP_GuildMemberLevelUpdate=0x0000 # Unused? +OP_ZoneGuildList=0x0000 # Unused? +OP_GetGuildsList=0x0000 # Unused? +OP_LFGuild=0x0000 +OP_GuildManageRemove=0x0000 +OP_GuildManageAdd=0x0000 +OP_GuildManageStatus=0x0000 + +# GM/Guide Opcodes +OP_GMServers=0x6c9b +OP_GMBecomeNPC=0x5efb +OP_GMZoneRequest=0x05db +OP_GMZoneRequest2=0x0b6f +OP_GMGoto=0x4cf4 +OP_GMSearchCorpse=0x7628 +OP_GMHideMe=0x0f7c +OP_GMDelCorpse=0x6175 +OP_GMApproval=0x2151 +OP_GMToggle=0x352d +OP_GMSummon=0x62f8 # Was 0x684f +OP_GMEmoteZone=0x5833 # Was 0x0655 +OP_GMEmoteWorld=0x2643 # Was 0x1935 +OP_GMFind=0x3450 +OP_GMKick=0x6dd9 +OP_GMKill=0x5501 +OP_GMNameChange=0x7202 # Was 0x51f7 +OP_GMLastName=0x72c5 # Was 0x4dd7 + +# Misc Opcodes +OP_InspectRequest=0x0853 +OP_InspectAnswer=0x3d94 +OP_InspectMessageUpdate=0x1562 +OP_BeginCast=0x7ec6 +OP_BuffFadeMsg=0x193a +OP_ConsentResponse=0x130b +OP_MemorizeSpell=0x72a0 +OP_SwapSpell=0x30c2 +OP_CastSpell=0x6bd3 +OP_Consider=0x1184 +OP_FormattedMessage=0x1bc4 +OP_SimpleMessage=0x4926 +OP_Buff=0x3526 +OP_Illusion=0x02e6 +OP_MoneyOnCorpse=0x2a0f +OP_RandomReply=0x0b4b +OP_DenyResponse=0x414c +OP_SkillUpdate=0x2389 +OP_GMTrainSkillConfirm=0x1eb6 # 0x3960 +OP_RandomReq=0x5d57 +OP_Death=0x2c22 +OP_GMTraining=0x7e86 +OP_GMEndTraining=0x0fcc +OP_GMTrainSkill=0x4478 +OP_Animation=0x26b3 +OP_Begging=0x1185 +OP_Consent=0x3292 +OP_ConsentDeny=0x722b +OP_AutoFire=0x03eb +OP_PetCommands=0x4e66 +OP_DeleteSpell=0x7dc1 +OP_Surname=0x3388 +OP_ClearSurname=0x31d9 +OP_FaceChange=0x2729 +OP_SenseHeading=0x3b65 +OP_Action=0x31b6 +OP_ConsiderCorpse=0x1837 +OP_HideCorpse=0x177c +OP_CorpseDrag=0x4bc7 +OP_CorpseDrop=0x4857 +OP_Bug=0x7866 +OP_Feedback=0x240f +OP_Report=0x15bb +OP_Damage=0x4598 +OP_ChannelMessage=0x7aba +OP_Assist=0x454e +OP_AssistGroup=0x8a9a +OP_MoveCoin=0x7944 +OP_ZonePlayerToBind=0x1698 +OP_KeyRing=0x07da +OP_WhoAllRequest=0x037d +OP_WhoAllResponse=0x72e8 +OP_FriendsWho=0x4640 +OP_ConfirmDelete=0x594e +OP_Logout=0x4739 +OP_Rewind=0x21b8 +OP_TargetCommand=0x717e +OP_Hide=0x6d0d +OP_Jump=0x7c8d +OP_Camp=0x6958 +OP_Emote=0x50d6 +OP_SetRunMode=0x20fd +OP_BankerChange=0x1d10 +OP_TargetMouse=0x0a2d +OP_MobHealth=0x05c3 +OP_InitialMobHealth=0x0000 # Unused? +OP_TargetHoTT=0x43ec +OP_XTargetResponse=0x18c5 +OP_XTargetRequest=0x47bc +OP_XTargetAutoAddHaters=0x48f8 +OP_TargetBuffs=0x0a99 +OP_BuffCreate=0x1cc2 +OP_BuffRemoveRequest=0x1338 +OP_DeleteSpawn=0x781f +OP_AutoAttack=0x1740 +OP_AutoAttack2=0x78de +OP_Consume=0x678e +OP_MoveItem=0x76c9 +OP_DeleteItem=0x1362 +OP_DeleteCharge=0x1721 +OP_ItemPacket=0x82a7 +OP_ItemLinkResponse=0x636e +OP_ItemLinkClick=0x234b +OP_ItemPreview=0x504d +OP_NewSpawn=0x3aa6 +OP_Track=0x01d5 +OP_TrackTarget=0x1bb5 +OP_TrackUnknown=0x5c41 +OP_ClickDoor=0x07be +OP_MoveDoor=0x45fe +OP_RemoveAllDoors=0x60cd +OP_EnvDamage=0x0518 +OP_BoardBoat=0x3097 +OP_Forage=0x0ec5 +OP_LeaveBoat=0x1180 +OP_ControlBoat=0x6e81 +OP_SafeFallSuccess=0x4cb2 +OP_RezzComplete=0x2cb3 +OP_RezzRequest=0x375e +OP_RezzAnswer=0x71c2 +OP_Shielding=0x75a5 +OP_RequestDuel=0x17ab +OP_MobRename=0x6ebb +OP_AugmentItem=0x3b4b # Was 0x37cb +OP_WeaponEquip1=0x7480 +OP_WeaponEquip2=0x6f55 # Was 0x6022 +OP_WeaponUnequip2=0x1d1e # Was 0x0110 +OP_ApplyPoison=0x3a21 +OP_Save=0x296a +OP_TestBuff=0x3296 # Was 0x3772 +OP_CustomTitles=0x26eb +OP_Split=0x1ee0 +OP_YellForHelp=0x4c55 +OP_LoadSpellSet=0x0994 +OP_Bandolier=0x6d0e +OP_PotionBelt=0x0e8e # Was 0x4d3b +OP_DuelResponse=0x6f44 +OP_DuelResponse2=0x4868 +OP_SaveOnZoneReq=0x317d +OP_ReadBook=0x5671 +OP_Dye=0x0b0d +OP_InterruptCast=0x6adf +OP_AAAction=0x7d94 +OP_LeadershipExpToggle=0x7a6b +OP_LeadershipExpUpdate=0x1499 +OP_PurchaseLeadershipAA=0x15aa +OP_UpdateLeadershipAA=0x0bcd +OP_MarkNPC=0x08d3 +OP_ClearNPCMarks=0x7bb8 +OP_DelegateAbility=0x2bb5 +OP_SetGroupTarget=0x49f2 +OP_Charm=0x7bea +OP_Stun=0x7f7e +OP_SendFindableNPCs=0x5de1 +OP_FindPersonRequest=0x4150 +OP_FindPersonReply=0x65f0 +OP_Sound=0x797b +OP_PetBuffWindow=0x12cd +OP_LevelAppearance=0x34b9 +OP_Translocate=0x7121 +OP_Sacrifice=0x609b +OP_PopupResponse=0x3c2d +OP_OnLevelMessage=0x0648 +OP_AugmentInfo=0x0000 +OP_Petition=0x4aad +OP_SomeItemPacketMaybe=0x1334 +OP_PVPStats=0x51fd # Unsure +OP_PVPLeaderBoardRequest=0x38cc +OP_PVPLeaderBoardReply=0x2ac0 +OP_PVPLeaderBoardDetailsRequest=0x4efd +OP_PVPLeaderBoardDetailsReply=0x4497 +OP_RestState=0x7496 +OP_RespawnWindow=0x2e76 +OP_LDoNButton=0x1589 +OP_SetStartCity=0x17d5 # Was 0x2d1b +OP_VoiceMacroIn=0x31f8 +OP_VoiceMacroOut=0x7f45 +OP_ItemViewUnknown=0x1aca +OP_VetRewardsAvaliable=0x786e +OP_VetClaimRequest=0x380d +OP_VetClaimReply=0x5899 +OP_DisciplineUpdate=0x47a9 # Was 0x2f05 +OP_DisciplineTimer=0x6394 # Was 0x5e3f +OP_BecomeCorpse=0x0000 # Unused? +OP_Action2=0x0000 # Unused? +OP_MobUpdate=0x142c +OP_NPCMoveUpdate=0x4788 +OP_CameraEffect=0x3056 +OP_SpellEffect=0x4d15 +OP_RemoveNimbusEffect=0x1455 +OP_AltCurrency=0x2044 +OP_AltCurrencyMerchantRequest=0x0fc1 +OP_AltCurrencyMerchantReply=0x57ee +OP_AltCurrencyPurchase=0x13bf +OP_AltCurrencySell=0x41c3 +OP_AltCurrencySellSelection=0x0a3a +OP_AltCurrencyReclaim=0x4097 +OP_CrystalCountUpdate=0x5307 # Was 0x3f60 +OP_CrystalCreate=0x6668 # Was 0x5a82 +OP_CrystalReclaim=0x00de # Was 0x7616 +OP_Untargetable=0x3210 +OP_IncreaseStats=0x5148 +OP_Weblink=0x5e65 +#OP_OpenInventory=0x0000 # Likely does not exist in RoF -U +OP_OpenContainer=0x0000 + +OP_DzQuit=0x7514 +OP_DzListTimers=0x572d +OP_DzAddPlayer=0x5aab +OP_DzRemovePlayer=0x4e78 +OP_DzSwapPlayer=0x14a4 +OP_DzMakeLeader=0x15d7 +OP_DzPlayerList=0x2dcd +OP_DzJoinExpeditionConfirm=0x0f30 +OP_DzJoinExpeditionReply=0x3b67 +OP_DzExpeditionInfo=0x24be +OP_DzExpeditionList=0x1ce9 +OP_DzMemberStatus=0x1bfc +OP_DzLeaderStatus=0x04bf +OP_DzExpeditionEndsWarning=0x5dea +OP_DzMemberList=0x2ef6 +OP_DzCompass=0x331d # Was 0x4f09 +OP_DzChooseZone=0x0000 # Maybe 0x29d6 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # Actually OP_MobUpdate ? +OP_ManaUpdate=0x089f +OP_EnduranceUpdate=0x3a7c +OP_MobManaUpdate=0x1013 +OP_MobEnduranceUpdate=0x68a5 + +# Mercenary Opcodes +OP_MercenaryDataUpdateRequest=0x1919 +OP_MercenaryDataUpdate=0x27a5 +OP_MercenaryDataRequest=0x6278 +OP_MercenaryDataResponse=0x5c06 +OP_MercenaryHire=0x18f9 +OP_MercenaryDismiss=0x9d9a +OP_MercenaryTimerRequest=0x39a1 +OP_MercenaryTimer=0x6f9e +OP_MercenaryUnknown1=0x6e0e +OP_MercenaryCommand=0x564f +OP_MercenarySuspendRequest=0x1d98 +OP_MercenarySuspendResponse=0x205c +OP_MercenaryUnsuspendResponse=0x4ab4 + +# Looting +OP_LootRequest=0x760c +OP_EndLootRequest=0x0c45 +OP_LootItem=0x29e8 +OP_LootComplete=0x2dc2 + +# bazaar trader stuff: +OP_BazaarSearch=0x37a5 +OP_TraderDelItem=0x0000 +OP_BecomeTrader=0x7d0b +OP_TraderShop=0x5137 +OP_Trader=0x7e2b +OP_TraderBuy=0x0000 +OP_Barter=0x0e48 +OP_ShopItem=0x0000 +OP_BazaarInspect=0x0000 +OP_Bazaar=0x0000 +OP_TraderItemUpdate=0x0000 + +# pc/npc trading +OP_TradeRequest=0x743e +OP_TradeAcceptClick=0x262a +OP_TradeRequestAck=0x02b0 +OP_TradeCoins=0x2eef +OP_FinishTrade=0x22b6 +OP_CancelTrade=0x4cd1 +OP_TradeMoneyUpdate=0x6d8c +OP_MoneyUpdate=0x4ae9 +OP_TradeBusy=0x7dcf + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x16fb +OP_FinishWindow2=0x6c2b + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x2e3a +OP_ItemVerifyReply=0x3b26 + +# merchant stuff +OP_ShopPlayerSell=0x51fb +OP_ShopRequest=0x49f3 +OP_ShopEnd=0x36ac +OP_ShopEndConfirm=0x6b28 +OP_ShopPlayerBuy=0x639e +OP_ShopDelItem=0x19fd + +# tradeskill stuff: +OP_ClickObject=0x607d +OP_ClickObjectAction=0x553d +OP_ClearObject=0x0e78 +OP_RecipeDetails=0x59b7 +OP_RecipesFavorite=0x56b5 +OP_RecipesSearch=0x0000 +OP_RecipeReply=0x57c9 +OP_RecipeAutoCombine=0x7cc0 +OP_TradeSkillCombine=0x1f0f + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x31a7 +OP_OpenTributeMaster=0x0212 # Was 0x40f5 +OP_SelectTribute=0x7604 +OP_TributeItem=0x72bc +OP_TributeMoney=0x7415 # Was 0x6fed +OP_TributeToggle=0x3046 +OP_TributePointUpdate=0x7203 +OP_TributeNPC=0x0000 +OP_GuildTributeInfo=0x0000 +OP_OpenTributeReply=0x0000 +OP_GuildTributeStatus=0x0000 + +# Adventure packets: +OP_LeaveAdventure=0x18f7 +OP_AdventureFinish=0x29ea +OP_AdventureInfoRequest=0x26a5 +OP_AdventureInfo=0x51f0 +OP_AdventureRequest=0x3eae +OP_AdventureDetails=0x44e5 +OP_AdventureData=0x22cf +OP_AdventureUpdate=0x5a41 +OP_AdventureMerchantRequest=0x2c46 # Was 654d +OP_AdventureMerchantResponse=0x8000 # Was 7949 +OP_AdventureMerchantPurchase=0x7ee6 # Was 155a +OP_AdventureMerchantSell=0x4faa # Was 389c +OP_AdventurePointsUpdate=0x30c0 # Was 7589 +OP_AdventureStatsRequest=0x730a +OP_AdventureStatsReply=0x3f32 +OP_AdventureLeaderboardRequest=0x1f8a +OP_AdventureLeaderboardReply=0x37f3 + +# Group Opcodes +OP_GroupDisband=0x559d +OP_GroupInvite=0x3877 +OP_GroupFollow=0x7842 +OP_GroupUpdate=0x35cc +OP_GroupUpdateB=0x2aca +OP_GroupCancelInvite=0x0000 +OP_GroupAcknowledge=0x7b5a +OP_GroupDelete=0x7102 +OP_CancelInvite=0x1afc +OP_GroupFollow2=0x269f +OP_GroupInvite2=0x4b77 +OP_GroupDisbandYou=0x23eb +OP_GroupDisbandOther=0x7862 +OP_GroupLeaderChange=0x07d8 +OP_GroupRoles=0x139a +OP_GroupMakeLeader=0x7056 +OP_DoGroupLeadershipAbility=0x6637 +OP_GroupLeadershipAAUpdate=0x0400 + +# LFG/LFP Opcodes +OP_LFGCommand=0x6b5a +OP_LFGGetMatchesRequest=0x3153 +OP_LFGGetMatchesResponse=0x1ed6 +OP_LFPGetMatchesRequest=0x5de8 +OP_LFPGetMatchesResponse=0x62bc +OP_LFPCommand=0x403e +OP_LFGAppearance=0x0000 +OP_LFGResponse=0x0000 + +# Raid Opcodes +OP_RaidInvite=0x3ab1 +OP_RaidUpdate=0x20a4 +OP_RaidJoin=0x0000 + +# Button-push commands +OP_Taunt=0x01ed +OP_CombatAbility=0x1f8e +OP_SenseTraps=0x5ccf # Was 0x2ee0 +OP_PickPocket=0x7876 +OP_DisarmTraps=0x0000 +OP_Disarm=0x4422 +OP_Sneak=0x117d +OP_Fishing=0x0cbd +OP_InstillDoubt=0x0734 +OP_FeignDeath=0x2321 +OP_Mend=0x5651 +OP_Bind_Wound=0x4ab2 +OP_LDoNOpen=0x1dfc + +# Task packets +OP_TaskDescription=0x5c2a +OP_TaskActivity=0x902b +OP_CompletedTasks=0x2a6f +OP_TaskActivityComplete=0x2459 +OP_AcceptNewTask=0x0b03 +OP_CancelTask=0x4c3f +OP_TaskMemberList=0x2f78 # Was 0x1656 +OP_OpenNewTasksWindow=0xd63f # Was 0x11de +OP_AvaliableTask=0x5f3e # Was 0x2377 +OP_TaskHistoryRequest=0x03fe +OP_TaskHistoryReply=0x2b9f +OP_DeclineAllTasks=0x0000 + +# Title opcodes +OP_NewTitlesAvailable=0x7c32 +OP_RequestTitles=0x1c5f +OP_SendTitleList=0x0d6c +OP_SetTitle=0x7660 +OP_SetTitleReply=0x5525 + +# mail opcodes +OP_Command=0x0000 +OP_MailboxHeader=0x0000 +OP_MailHeader=0x0000 +OP_MailBody=0x0000 +OP_NewMail=0x0000 +OP_SentConfirm=0x0000 + +########### Below this point should not be needed ########### + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 +OP_LocInfo=0x0000 +OP_ReloadUI=0x0000 +OP_ItemName=0x0000 +OP_ItemLinkText=0x0000 +OP_MultiLineMsg=0x0000 +OP_MendHPUpdate=0x0000 +OP_TargetReject=0x0000 +OP_SafePoint=0x0000 +OP_ApproveZone=0x0000 +OP_ZoneComplete=0x0000 +OP_ClientError=0x0000 +OP_DumpName=0x0000 +OP_Heartbeat=0x0000 +OP_CrashDump=0x0000 +OP_LoginComplete=0x0000 + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 +OP_PlayMP3=0x0000 +OP_ReclaimCrystals=0x0000 +OP_DynamicWall=0x0000 +OP_OpenDiscordMerchant=0x0000 +OP_DiscordMerchantInventory=0x0000 +OP_GiveMoney=0x0000 +OP_RequestKnowledgeBase=0x0000 +OP_KnowledgeBase=0x0000 +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 +OP_Some6ByteHPUpdate=0x0000 seems to happen when you target group members +OP_QueryResponseThing=0x0000 + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x46d3 # OP_SendSpellChecksum +#OP_LoginUnknown2=0x040b # OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 search term for petition +OP_PetitionSearchResults=0x0000 (list of?) matches from search +OP_PetitionSearchText=0x0000 text results of search + +OP_PetitionUpdate=0x0000 +OP_PetitionCheckout=0x0000 +OP_PetitionCheckIn=0x0000 +OP_PetitionQue=0x0000 +OP_PetitionUnCheckout=0x0000 +OP_PetitionDelete=0x0000 +OP_DeletePetition=0x0000 +OP_PetitionResolve=0x0000 +OP_PDeletePetition=0x0000 +OP_PetitionBug=0x0000 +OP_PetitionRefresh=0x0000 +OP_PetitionCheckout2=0x0000 +OP_PetitionViewPetition=0x0000 + +# Login opcodes +OP_SessionReady=0x0000 +OP_Login=0x0000 +OP_ServerListRequest=0x0000 +OP_PlayEverquestRequest=0x0000 +OP_PlayEverquestResponse=0x0000 +OP_ChatMessage=0x0000 +OP_LoginAccepted=0x0000 +OP_ServerListResponse=0x0000 +OP_Poll=0x0000 +OP_EnterChat=0x0000 +OP_PollResponse=0x0000 + +# raw opcodes +OP_RAWSessionRequest=0x0000 +OP_RAWSessionResponse=0x0000 +OP_RAWCombined=0x0000 +OP_RAWSessionDisconnect=0x0000 +OP_RAWKeepAlive=0x0000 +OP_RAWSessionStatRequest=0x0000 +OP_RAWSessionStatResponse=0x0000 +OP_RAWPacket=0x0000 +OP_RAWFragment=0x0000 +OP_RAWOutOfOrderAck=0x0000 +OP_RAWAck=0x0000 +OP_RAWAppCombined=0x0000 +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 diff --git a/utils/EQExtractor2/EQExtractor2/patch_July13-2012.conf b/utils/EQExtractor2/EQExtractor2/patch_July13-2012.conf new file mode 100644 index 000000000..b6ece368b --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_July13-2012.conf @@ -0,0 +1,633 @@ +# OPCodes not used by EQExtractor2 are most likely not correct. +# +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x4d5c # +OP_ApproveWorld=0x655c # +OP_LogServer=0x1497 # +OP_SendCharInfo=0x7f9e # +OP_ExpansionInfo=0x2c8f # +OP_GuildsList=0x5b0b # +OP_EnterWorld=0x710e # +OP_PostEnterWorld=0x7930 # +OP_World_Client_CRC1=0x7ce4 # +OP_World_Client_CRC2=0x7705 # +OP_SendSpellChecksum=0x0000 # +OP_SendSkillCapsChecksum=0x0000 # + +# Character Select Related: +OP_DeleteCharacter=0x5ca5 # +OP_CharacterCreateRequest=0x4eba # +OP_CharacterCreate=0x3237 # +OP_RandomNameGenerator=0x26aa # +OP_ApproveName=0x3154 # + +OP_MOTD=0x0b80 # +OP_SetChatServer=0x2c38 # +OP_SetChatServer2=0x441c # +OP_ZoneServerInfo=0x16f5 # +OP_WorldComplete=0x6c63 # +OP_WorldUnknown001=0x4274 # +OP_FloatListThing=0x1910 # + +# Reasons for Disconnect: +OP_ZoneUnavail=0x20bc # +OP_WorldClientReady=0x0b89 # Testing VoA 0x3f24 +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # +OP_ZoneEntry=0x6087 # +OP_ReqNewZone=0x0000 # +OP_NewZone=0x51ae # +OP_ZoneSpawns=0x5f5e # +OP_PlayerProfile=0x537c # +OP_TimeOfDay=0x6015 # +OP_LevelUpdate=0x6e1c # +OP_Stamina=0x7686 # +OP_RequestClientZoneChange=0x224f # +OP_ZoneChange=0x28c8 # + +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x3f55 # +OP_ChangeSize=0x0000 # +OP_TributeUpdate=0x0000 # +OP_TributeTimer=0x0000 # Testing VoA 0x1525 +OP_TaskDescription=0x2e4f # +OP_TaskActivity=0x512b # +OP_CompletedTasks=0x3141 # +OP_Weather=0x3e50 # +OP_SendAATable=0x6db5 # Testing VoA 0x6a7e +OP_UpdateAA=0x5363 # Testing VoA 0x5363 +OP_RespondAA=0x7bf6 # Testing VoA 0x0643 or maybe 0x7bf6 +OP_ReqClientSpawn=0x2c27 # +OP_SpawnDoor=0x3604 # +OP_GroundSpawn=0x1436 # +OP_SendZonepoints=0x13da # +OP_SendAAStats=0x4e22 # +OP_WorldObjectsSent=0x7b73 # +OP_BlockedBuffs=0x1681 # +OP_RemoveBlockedBuffs=0x6a86 # +OP_ClearBlockedBuffs=0x7ae1 # +OP_SendExpZonein=0x0f14 # +OP_SendTributes=0x010d # +OP_TributeInfo=0x047c # +OP_SendGuildTributes=0x0000 # +OP_AAExpUpdate=0x1318 # +OP_ExpUpdate=0x221a # Testing VoA 0x0555 +OP_HPUpdate=0x2369 # +OP_ManaChange=0x3887 # +OP_TGB=0x3672 # +OP_SpecialMesg=0x039d # +OP_GuildMemberList=0x51bc # +OP_GuildMOTD=0x0a1d # Testing VoA 0x0a1d +OP_CharInventory=0x6cfe # Testing VoA 0x6cfe +OP_WearChange=0x1a58 # +OP_ClientUpdate=0x7062 # +OP_ClientReady=0x1272 # +OP_SetServerFilter=0x053a # + +# Guild Opcodes +OP_GetGuildMOTD=0x3415 # +OP_GetGuildMOTDReply=0x709d # +OP_GuildMemberUpdate=0x589f # +OP_GuildInvite=0x5488 # +OP_GuildRemove=0x47d3 # +OP_GuildPeace=0x1d22 # +OP_SetGuildMOTD=0x5f85 # +OP_GuildList=0x0000 # +OP_GuildWar=0x0598 # +OP_GuildLeader=0x67c8 # +OP_GuildDelete=0x230e # +OP_GuildInviteAccept=0x2b5a # +OP_GuildDemote=0x1899 # +OP_GuildPublicNote=0x2dbd # +OP_GuildManageBanker=0x0000 # +OP_GuildBank=0x0000 # +OP_SetGuildRank=0x4ffe # +OP_GuildUpdateURLAndChannel=0x03d1 # +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +OP_GuildStatus=0x6916 # +OP_GuildCreate=0x0000 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x6989 # +OP_GMBecomeNPC=0x7093 # +OP_GMZoneRequest=0x701f # +OP_GMZoneRequest2=0x01 # +OP_GMGoto=0x15a1 # +OP_GMSearchCorpse=0x2bff # +OP_GMHideMe=0x7e4d # +OP_GMDelCorpse=0x072f # +OP_GMApproval=0x481f # +OP_GMToggle=0x2042 # +OP_GMSummon=0x6e47 # +OP_GMEmoteZone=0x307d # +OP_GMEmoteWorld=0x0c2d # +OP_GMFind=0x6e27 # +OP_GMKick=0x5a56 # +OP_GMKill=0x51fe # +OP_GMNameChange=0x0000 # +OP_GMLastName=0x1055 # + +OP_InspectAnswer=0x0c54 # +OP_BeginCast=0x0d5a # +OP_BuffFadeMsg=0x1b8a # +OP_ConsentResponse=0x35c6 # +OP_MemorizeSpell=0x569e # +OP_SwapSpell=0x28a7 # +OP_CastSpell=0x8543 # +OP_Consider=0x70c6 # +OP_FormattedMessage=0x4675 # +OP_SimpleMessage=0x0698 # +OP_Buff=0x4658 # +OP_Illusion=0x10b7 # +OP_MoneyOnCorpse=0x4074 # +OP_RandomReply=0x07fb # +OP_DenyResponse=0x0370 # +OP_SkillUpdate=0x7f01 # +OP_GMTrainSkillConfirm=0x0000 # +OP_RandomReq=0x303e # +OP_Death=0x6685 # +OP_Bind_Wound=0x7b64 # +OP_GMTraining=0x5960 # +OP_GMEndTraining=0x4a61 # +OP_GMTrainSkill=0x4885 # +OP_Animation=0x0b93 # +OP_Begging=0x0ca5 # +OP_Consent=0x6bb9 # +OP_ConsentDeny=0x6b7f # +OP_AutoFire=0x23fc # +OP_PetCommands=0x7312 # +OP_DeleteSpell=0x0142 # +OP_Surname=0x777c # +OP_ClearSurname=0x0000 # +OP_FaceChange=0x0507 # +OP_SenseHeading=0x1dad # +OP_Action=0x1b85 # +OP_ConsiderCorpse=0x0e94 # +OP_HideCorpse=0x0000 # +OP_CorpseDrag=0x0000 # +OP_CorpseDrop=0x0000 # +OP_Bug=0x3365 # +OP_Feedback=0x52b5 # +OP_Report=0x211a # +OP_Damage=0x45d6 # +OP_ChannelMessage=0x2e79 # +OP_Assist=0x5658 # +OP_AssistGroup=0x43bc # +OP_MoveCoin=0x1418 # +OP_ZonePlayerToBind=0x2480 # +OP_KeyRing=0x3b85 # +OP_WhoAllRequest=0x177a # +OP_WhoAllResponse=0x15de # +OP_FriendsWho=0x73d6 # +OP_ConfirmDelete=0x604d # +OP_Logout=0x6275 # +OP_Rewind=0x09e3 # +OP_TargetCommand=0x3088 # +OP_InspectRequest=0x0000 # +OP_Hide=0x2913 # +OP_Jump=0x243b # +OP_Camp=0x3cd6 # +OP_Emote=0x0000 # +OP_SetRunMode=0x59c3 # +OP_BankerChange=0x0000 # +OP_TargetMouse=0x3edc # +OP_MobHealth=0x5cb0 # +OP_InitialMobHealth=0x0000 # +OP_TargetHoTT=0x460e # +OP_TargetBuffs=0x7c24 # +OP_BuffCreate=0x0c98 # +OP_BuffRemoveRequest=0x3567 +OP_DeleteSpawn=0x3164 # +OP_AutoAttack=0x2257 # +OP_AutoAttack2=0x0000 # +OP_Consume=0x2ee2 # +OP_MoveItem=0x2963 # +OP_DeleteItem=0x7547 # +OP_DeleteCharge=0x6a90 # +OP_ItemPacket=0x38b0 # +OP_ItemLinkResponse=0x6948 # +OP_ItemLinkClick=0x3c66 # +OP_NewSpawn=0x016c # +OP_Track=0x2444 # +OP_TrackTarget=0x538f # +OP_TrackUnknown=0x10fa # +OP_ClickDoor=0x48f9 # +OP_MoveDoor=0x231f # +OP_RemoveAllDoors=0x24a3 # +OP_EnvDamage=0x2730 # +OP_BoardBoat=0x50c2 # +OP_Forage=0x3c02 # +OP_LeaveBoat=0x569a # +OP_ControlBoat=0x2641 # +OP_SafeFallSuccess=0x1dc6 # +OP_RezzComplete=0x7108 # +OP_RezzRequest=0x66c5 # +OP_RezzAnswer=0x71eb # +OP_Shielding=0x7598 # +OP_RequestDuel=0x0e71 # +OP_MobRename=0x6884 # +OP_AugmentItem=0x4cc6 # +OP_WeaponEquip1=0x11bc # +OP_WeaponEquip2=0x2121 # +OP_WeaponUnequip2=0x7b87 # +OP_ApplyPoison=0x3582 # +OP_Save=0x47e7 # +OP_TestBuff=0x712b # +OP_CustomTitles=0x6ade # +OP_Split=0x3a18 # +OP_YellForHelp=0x1f87 # +OP_LoadSpellSet=0x440f # +OP_Bandolier=0x3ad1 # +OP_PotionBelt=0x7b7f # +OP_DuelResponse=0x1df9 # +OP_DuelResponse2=0x36f8 # +OP_SaveOnZoneReq=0x5408 # +OP_ReadBook=0x41b5 # +OP_Dye=0x32c6 # +OP_InterruptCast=0x7706 # +OP_AAAction=0x55dd # +OP_LeadershipExpToggle=0x69d0 # +OP_LeadershipExpUpdate=0x3703 # +OP_PurchaseLeadershipAA=0x6e58 # +OP_UpdateLeadershipAA=0x0297 # +OP_MarkNPC=0x6b9e # +OP_ClearNPCMarks=0x074f # +OP_DoGroupLeadershipAbility=0x0000 # +OP_GroupLeadershipAAUpdate=0x0000 # +OP_DelegateAbility=0x105b # +OP_SetGroupTarget=0x42c7 # +OP_Charm=0x17f7 # +OP_Stun=0x41a6 # +OP_SendFindableNPCs=0x50c1 # +OP_FindPersonRequest=0x19a8 # +OP_FindPersonReply=0x7e45 # +OP_Sound=0x2d1d # +OP_PetBuffWindow=0x4895 # +OP_LevelAppearance=0x78b9 # +OP_Translocate=0x42ef # +OP_Sacrifice=0x2dc6 # +OP_PopupResponse=0x0000 # +OP_OnLevelMessage=0x0000 # +OP_AugmentInfo=0x0000 # +OP_Petition=0x31d1 # +OP_SomeItemPacketMaybe=0x1513 # +OP_PVPStats=0x0000 # +OP_PVPLeaderBoardRequest=0x0000 # +OP_PVPLeaderBoardReply=0x0000 # +OP_PVPLeaderBoardDetailsRequest=0x0000 # +OP_PVPLeaderBoardDetailsReply=0x0000 # +OP_RestState=0x0000 # +OP_RespawnWindow=0x435b # +OP_DisciplineTimer=0x74ca # +OP_LDoNButton=0x597d # +OP_SetStartCity=0x179d # +OP_VoiceMacroIn=0x0fce # +OP_VoiceMacroOut=0x1c36 # +OP_ItemViewUnknown=0x0000 # +OP_VetRewardsAvaliable=0x3637 # +OP_VetClaimRequest=0x032b # +OP_VetClaimReply=0x7b6e # +OP_CrystalCountUpdate=0x0000 # +OP_DisciplineUpdate=0x0d8d # +OP_BecomeCorpse=0x0000 # +OP_Action2=0x0000 # +OP_MobUpdate=0x5a05 # +OP_NPCMoveUpdate=0x5a4c # +OP_CameraEffect=0x6812 # +OP_SpellEffect=0x0989 # +OP_RemoveNimbusEffect=0x0000 # +OP_AltCurrency=0x2536 +OP_AltCurrencyMerchantRequest=0x5aac +OP_AltCurrencyMerchantReply=0x17c7 +OP_AltCurrencyPurchase=0x6efe +OP_AltCurrencySell=0x32d9 +OP_AltCurrencySellSelection=0x7eac +OP_AltCurrencyReclaim=0x1560 +OP_CrystalReclaim=0x0000 +OP_CrystalCreate=0x0000 +OP_Untargetable=0x101e +OP_IncreaseStats=0x3fdc + +OP_DzQuit=0x0000 +OP_DzListTimers=0x0000 +OP_DzAddPlayer=0x0000 +OP_DzRemovePlayer=0x0000 +OP_DzSwapPlayer=0x0000 +OP_DzMakeLeader=0x0000 +OP_DzPlayerList=0x0000 +OP_DzJoinExpeditionConfirm=0x0000 +OP_DzJoinExpeditionReply=0x0000 +OP_DzExpeditionInfo=0x0000 +OP_DzMemberStatus=0x0000 +OP_DzLeaderStatus=0x0000 +OP_DzExpeditionEndsWarning=0x0000 +OP_DzExpeditionList=0x0000 +OP_DzMemberList=0x0000 +OP_DzCompass=0x0000 +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # +OP_ManaUpdate=0x7c94 # +OP_EnduranceUpdate=0x5851 # +OP_MobManaUpdate=0x4d27 # +OP_MobEnduranceUpdate=0x190c # + +# Looting +OP_LootRequest=0x1d85 # +OP_EndLootRequest=0x0bc # +OP_LootItem=0x5bd9 # +OP_LootComplete=0x4ca1 # + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x2881 # +OP_TraderDelItem=0x0000 # +OP_BecomeTrader=0x358a # +OP_TraderShop=0x49f4 # +OP_Trader=0x058c # +OP_TraderBuy=0x783c # +OP_Barter=0x766f # +OP_ShopItem=0x0000 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # +OP_TraderItemUpdate=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x0c24 # +OP_TradeAcceptClick=0x064a # +OP_TradeRequestAck=0x606a # +OP_TradeCoins=0x0149 # +OP_FinishTrade=0x3ff6 # +OP_CancelTrade=0x527e # +OP_TradeMoneyUpdate=0x1ebb # +OP_MoneyUpdate=0x528f # +OP_TradeBusy=0x2c03 # + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x3c27 # +OP_FinishWindow2=0x2c6d # + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x0000 # +OP_ItemVerifyReply=0x0000 # + +# merchant crap +OP_ShopPlayerSell=0x0b27 # +OP_ShopRequest=0x33e5 # +OP_ShopEnd=0x2244 # +OP_ShopEndConfirm=0x4762 # +OP_ShopPlayerBuy=0x436a # +OP_ShopDelItem=0x537c # + +# tradeskill stuff: +OP_ClickObject=0x5f0d # +OP_ClickObjectAction=0x29df # +OP_ClearObject=0x38d1 # +OP_RecipeDetails=0x068 # +OP_RecipesFavorite=0x0000 # +OP_RecipesSearch=0x0239 # +OP_RecipeReply=0x1ecf # +OP_RecipeAutoCombine=0x66bf # +OP_TradeSkillCombine=0x4212 # + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x0000 # +OP_OpenTributeMaster=0x3ba3 # +OP_SelectTribute=0x314f # +OP_TributeItem=0x0000 # +OP_TributeMoney=0x1772 # +OP_TributeToggle=0x0000 # +OP_TributePointUpdate=0x0000 # +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x771f # +OP_AdventureFinish=0x3168 # +OP_AdventureInfoRequest=0x05d8 # +OP_AdventureInfo=0x164b # +OP_AdventureRequest=0x3a75 # +OP_AdventureDetails=0x05 # +OP_AdventureData=0x21df # +OP_AdventureUpdate=0x6129 # +OP_AdventureMerchantRequest=0x0000 # +OP_AdventureMerchantResponse=0x0000 # +OP_AdventureMerchantPurchase=0x0000 # +OP_AdventureMerchantSell=0x0000 # +OP_AdventurePointsUpdate=0x7d05 # +OP_AdventureStatsRequest=0x0000 # +OP_AdventureStatsReply=0x15a7 # +OP_AdventureLeaderboardRequest=0x0000 # +OP_AdventureLeaderboardReply=0x6bfb # + +# Group Opcodes +OP_GroupDisband=0x4200 # +OP_GroupInvite=0x3288 # +OP_GroupFollow=0x1190 # +OP_GroupUpdate=0x0000 # +OP_GroupUpdateB=0x0000 # +OP_GroupCancelInvite=0x0000 # +OP_GroupAcknowledge=0x0000 # +OP_GroupDelete=0x0000 # +OP_CancelInvite=0x132f # +OP_GroupFollow2=0x3519 # +OP_GroupInvite2=0x083b # +OP_GroupDisbandYou=0x0000 # +OP_GroupDisbandOther=0x0000 # +OP_GroupLeaderChange=0x0000 # +OP_GroupRoles=0x0000 # +OP_GroupMakeLeader=0x0000 + +# LFG/LFP Opcodes +OP_LFGCommand=0x457d # +OP_LFGGetMatchesRequest=0x49e7 # +OP_LFGGetMatchesResponse=0x4817 # +OP_LFPGetMatchesRequest=0x5fc7 # +OP_LFPGetMatchesResponse=0x3d0c # +OP_LFPCommand=0x20c6 # +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x550f # +OP_RaidUpdate=0x0000 # Testing VoA 0x0c08 +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x732c # +OP_CombatAbility=0x16a5 # +OP_SenseTraps=0x416b # +OP_PickPocket=0x13bd # +OP_DisarmTraps=0x0000 # +OP_Disarm=0x6def # +OP_Sneak=0x6eff # +OP_Fishing=0x0555 # +OP_InstillDoubt=0x4acf # +OP_FeignDeath=0x6145 # +OP_Mend=0x7fbc # +OP_LDoNOpen=0x7c87 # + +# Task packets +OP_TaskActivityComplete=0x0000 # +OP_TaskMemberList=0x0000 # +OP_OpenNewTasksWindow=0x0000 # +OP_AvaliableTask=0x0000 # +OP_AcceptNewTask=0x0000 # +OP_TaskHistoryRequest=0x6d1f # +OP_TaskHistoryReply=0x189b # +OP_CancelTask=0x47ea # +OP_DeclineAllTasks=0x0000 # + +# Title opcodes +OP_NewTitlesAvailable=0x6d95 # +OP_RequestTitles=0x7e3d # +OP_SendTitleList=0x3aef # +OP_SetTitle=0x725b # +OP_SetTitleReply=0x38e8 # + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x46d3 # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x040b # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0000 # +OP_Login=0x0000 # +OP_ServerListRequest=0x0000 # +OP_PlayEverquestRequest=0x0000 # +OP_PlayEverquestResponse=0x0000 # +OP_ChatMessage=0x0000 # +OP_LoginAccepted=0x0000 # +OP_ServerListResponse=0x0000 # +OP_Poll=0x0000 # +OP_EnterChat=0x0000 # +OP_PollResponse=0x0000 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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 # diff --git a/utils/EQExtractor2/EQExtractor2/patch_June25-2012.conf b/utils/EQExtractor2/EQExtractor2/patch_June25-2012.conf new file mode 100644 index 000000000..9a612b14b --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_June25-2012.conf @@ -0,0 +1,633 @@ +# OPCodes not used by EQExtractor2 are most likely not correct. +# +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x4d5c # +OP_ApproveWorld=0x655c # +OP_LogServer=0x1497 # +OP_SendCharInfo=0x7f9e # +OP_ExpansionInfo=0x2c8f # +OP_GuildsList=0x5b0b # +OP_EnterWorld=0x710e # +OP_PostEnterWorld=0x7930 # +OP_World_Client_CRC1=0x7ce4 # +OP_World_Client_CRC2=0x7705 # +OP_SendSpellChecksum=0x0000 # +OP_SendSkillCapsChecksum=0x0000 # + +# Character Select Related: +OP_DeleteCharacter=0x5ca5 # +OP_CharacterCreateRequest=0x4eba # +OP_CharacterCreate=0x3237 # +OP_RandomNameGenerator=0x26aa # +OP_ApproveName=0x3154 # + +OP_MOTD=0x0b80 # +OP_SetChatServer=0x2c38 # +OP_SetChatServer2=0x441c # +OP_ZoneServerInfo=0x16f5 # +OP_WorldComplete=0x6c63 # +OP_WorldUnknown001=0x4274 # +OP_FloatListThing=0x1910 # + +# Reasons for Disconnect: +OP_ZoneUnavail=0x20bc # +OP_WorldClientReady=0x0b89 # Testing VoA 0x3f24 +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # +OP_ZoneEntry=0x4892 # +OP_ReqNewZone=0x0000 # +OP_NewZone=0x4118 # +OP_ZoneSpawns=0x5f5e # +OP_PlayerProfile=0x6afd # +OP_TimeOfDay=0x6015 # +OP_LevelUpdate=0x6e1c # +OP_Stamina=0x7686 # +OP_RequestClientZoneChange=0x224f # +OP_ZoneChange=0x28c8 # + +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x3f55 # +OP_ChangeSize=0x0000 # +OP_TributeUpdate=0x0000 # +OP_TributeTimer=0x0000 # Testing VoA 0x1525 +OP_TaskDescription=0x2e4f # +OP_TaskActivity=0x512b # +OP_CompletedTasks=0x3141 # +OP_Weather=0x3e50 # +OP_SendAATable=0x0d98 # Testing VoA 0x6a7e +OP_UpdateAA=0x5363 # Testing VoA 0x5363 +OP_RespondAA=0x7bf6 # Testing VoA 0x0643 or maybe 0x7bf6 +OP_ReqClientSpawn=0x2c27 # +OP_SpawnDoor=0x5a55 # +OP_GroundSpawn=0x5c85 # +OP_SendZonepoints=0x2683 # +OP_SendAAStats=0x4e22 # +OP_WorldObjectsSent=0x7b73 # +OP_BlockedBuffs=0x1681 # +OP_RemoveBlockedBuffs=0x6a86 # +OP_ClearBlockedBuffs=0x7ae1 # +OP_SendExpZonein=0x0f14 # +OP_SendTributes=0x010d # +OP_TributeInfo=0x047c # +OP_SendGuildTributes=0x0000 # +OP_AAExpUpdate=0x1318 # +OP_ExpUpdate=0x221a # Testing VoA 0x0555 +OP_HPUpdate=0x2369 # +OP_ManaChange=0x3887 # +OP_TGB=0x3672 # +OP_SpecialMesg=0x039d # +OP_GuildMemberList=0x51bc # +OP_GuildMOTD=0x0a1d # Testing VoA 0x0a1d +OP_CharInventory=0x6cfe # Testing VoA 0x6cfe +OP_WearChange=0x1a58 # +OP_ClientUpdate=0x7062 # +OP_ClientReady=0x1272 # +OP_SetServerFilter=0x053a # + +# Guild Opcodes +OP_GetGuildMOTD=0x3415 # +OP_GetGuildMOTDReply=0x709d # +OP_GuildMemberUpdate=0x589f # +OP_GuildInvite=0x5488 # +OP_GuildRemove=0x47d3 # +OP_GuildPeace=0x1d22 # +OP_SetGuildMOTD=0x5f85 # +OP_GuildList=0x0000 # +OP_GuildWar=0x0598 # +OP_GuildLeader=0x67c8 # +OP_GuildDelete=0x230e # +OP_GuildInviteAccept=0x2b5a # +OP_GuildDemote=0x1899 # +OP_GuildPublicNote=0x2dbd # +OP_GuildManageBanker=0x0000 # +OP_GuildBank=0x0000 # +OP_SetGuildRank=0x4ffe # +OP_GuildUpdateURLAndChannel=0x03d1 # +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +OP_GuildStatus=0x6916 # +OP_GuildCreate=0x0000 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x6989 # +OP_GMBecomeNPC=0x7093 # +OP_GMZoneRequest=0x701f # +OP_GMZoneRequest2=0x01 # +OP_GMGoto=0x15a1 # +OP_GMSearchCorpse=0x2bff # +OP_GMHideMe=0x7e4d # +OP_GMDelCorpse=0x072f # +OP_GMApproval=0x481f # +OP_GMToggle=0x2042 # +OP_GMSummon=0x6e47 # +OP_GMEmoteZone=0x307d # +OP_GMEmoteWorld=0x0c2d # +OP_GMFind=0x6e27 # +OP_GMKick=0x5a56 # +OP_GMKill=0x51fe # +OP_GMNameChange=0x0000 # +OP_GMLastName=0x1055 # + +OP_InspectAnswer=0x0c54 # +OP_BeginCast=0x0d5a # +OP_BuffFadeMsg=0x1b8a # +OP_ConsentResponse=0x35c6 # +OP_MemorizeSpell=0x569e # +OP_SwapSpell=0x28a7 # +OP_CastSpell=0x8543 # +OP_Consider=0x70c6 # +OP_FormattedMessage=0x4675 # +OP_SimpleMessage=0x0698 # +OP_Buff=0x4658 # +OP_Illusion=0x10b7 # +OP_MoneyOnCorpse=0x4074 # +OP_RandomReply=0x07fb # +OP_DenyResponse=0x0370 # +OP_SkillUpdate=0x7f01 # +OP_GMTrainSkillConfirm=0x0000 # +OP_RandomReq=0x303e # +OP_Death=0x6685 # +OP_Bind_Wound=0x7b64 # +OP_GMTraining=0x5960 # +OP_GMEndTraining=0x4a61 # +OP_GMTrainSkill=0x4885 # +OP_Animation=0x0b93 # +OP_Begging=0x0ca5 # +OP_Consent=0x6bb9 # +OP_ConsentDeny=0x6b7f # +OP_AutoFire=0x23fc # +OP_PetCommands=0x7312 # +OP_DeleteSpell=0x0142 # +OP_Surname=0x777c # +OP_ClearSurname=0x0000 # +OP_FaceChange=0x0507 # +OP_SenseHeading=0x1dad # +OP_Action=0x1b85 # +OP_ConsiderCorpse=0x0e94 # +OP_HideCorpse=0x0000 # +OP_CorpseDrag=0x0000 # +OP_CorpseDrop=0x0000 # +OP_Bug=0x3365 # +OP_Feedback=0x52b5 # +OP_Report=0x211a # +OP_Damage=0x45d6 # +OP_ChannelMessage=0x2e79 # +OP_Assist=0x5658 # +OP_AssistGroup=0x43bc # +OP_MoveCoin=0x1418 # +OP_ZonePlayerToBind=0x2480 # +OP_KeyRing=0x3b85 # +OP_WhoAllRequest=0x177a # +OP_WhoAllResponse=0x15de # +OP_FriendsWho=0x73d6 # +OP_ConfirmDelete=0x604d # +OP_Logout=0x6275 # +OP_Rewind=0x09e3 # +OP_TargetCommand=0x3088 # +OP_InspectRequest=0x0000 # +OP_Hide=0x2913 # +OP_Jump=0x243b # +OP_Camp=0x3cd6 # +OP_Emote=0x0000 # +OP_SetRunMode=0x59c3 # +OP_BankerChange=0x0000 # +OP_TargetMouse=0x3edc # +OP_MobHealth=0x5cb0 # +OP_InitialMobHealth=0x0000 # +OP_TargetHoTT=0x460e # +OP_TargetBuffs=0x7c24 # +OP_BuffCreate=0x0c98 # +OP_BuffRemoveRequest=0x3567 +OP_DeleteSpawn=0x3164 # +OP_AutoAttack=0x2257 # +OP_AutoAttack2=0x0000 # +OP_Consume=0x2ee2 # +OP_MoveItem=0x2963 # +OP_DeleteItem=0x7547 # +OP_DeleteCharge=0x6a90 # +OP_ItemPacket=0x399b # +OP_ItemLinkResponse=0x6948 # +OP_ItemLinkClick=0x3c66 # +OP_NewSpawn=0x016c # +OP_Track=0x2444 # +OP_TrackTarget=0x538f # +OP_TrackUnknown=0x10fa # +OP_ClickDoor=0x48f9 # +OP_MoveDoor=0x231f # +OP_RemoveAllDoors=0x24a3 # +OP_EnvDamage=0x2730 # +OP_BoardBoat=0x50c2 # +OP_Forage=0x3c02 # +OP_LeaveBoat=0x569a # +OP_ControlBoat=0x2641 # +OP_SafeFallSuccess=0x1dc6 # +OP_RezzComplete=0x7108 # +OP_RezzRequest=0x66c5 # +OP_RezzAnswer=0x71eb # +OP_Shielding=0x7598 # +OP_RequestDuel=0x0e71 # +OP_MobRename=0x6884 # +OP_AugmentItem=0x4cc6 # +OP_WeaponEquip1=0x11bc # +OP_WeaponEquip2=0x2121 # +OP_WeaponUnequip2=0x7b87 # +OP_ApplyPoison=0x3582 # +OP_Save=0x47e7 # +OP_TestBuff=0x712b # +OP_CustomTitles=0x6ade # +OP_Split=0x3a18 # +OP_YellForHelp=0x1f87 # +OP_LoadSpellSet=0x440f # +OP_Bandolier=0x3ad1 # +OP_PotionBelt=0x7b7f # +OP_DuelResponse=0x1df9 # +OP_DuelResponse2=0x36f8 # +OP_SaveOnZoneReq=0x5408 # +OP_ReadBook=0x41b5 # +OP_Dye=0x32c6 # +OP_InterruptCast=0x7706 # +OP_AAAction=0x55dd # +OP_LeadershipExpToggle=0x69d0 # +OP_LeadershipExpUpdate=0x3703 # +OP_PurchaseLeadershipAA=0x6e58 # +OP_UpdateLeadershipAA=0x0297 # +OP_MarkNPC=0x6b9e # +OP_ClearNPCMarks=0x074f # +OP_DoGroupLeadershipAbility=0x0000 # +OP_GroupLeadershipAAUpdate=0x0000 # +OP_DelegateAbility=0x105b # +OP_SetGroupTarget=0x42c7 # +OP_Charm=0x17f7 # +OP_Stun=0x41a6 # +OP_SendFindableNPCs=0x099e # +OP_FindPersonRequest=0x19a8 # +OP_FindPersonReply=0x7e45 # +OP_Sound=0x2d1d # +OP_PetBuffWindow=0x4895 # +OP_LevelAppearance=0x78b9 # +OP_Translocate=0x42ef # +OP_Sacrifice=0x2dc6 # +OP_PopupResponse=0x0000 # +OP_OnLevelMessage=0x0000 # +OP_AugmentInfo=0x0000 # +OP_Petition=0x31d1 # +OP_SomeItemPacketMaybe=0x1513 # +OP_PVPStats=0x0000 # +OP_PVPLeaderBoardRequest=0x0000 # +OP_PVPLeaderBoardReply=0x0000 # +OP_PVPLeaderBoardDetailsRequest=0x0000 # +OP_PVPLeaderBoardDetailsReply=0x0000 # +OP_RestState=0x0000 # +OP_RespawnWindow=0x435b # +OP_DisciplineTimer=0x74ca # +OP_LDoNButton=0x597d # +OP_SetStartCity=0x179d # +OP_VoiceMacroIn=0x0fce # +OP_VoiceMacroOut=0x1c36 # +OP_ItemViewUnknown=0x0000 # +OP_VetRewardsAvaliable=0x3637 # +OP_VetClaimRequest=0x032b # +OP_VetClaimReply=0x7b6e # +OP_CrystalCountUpdate=0x0000 # +OP_DisciplineUpdate=0x0d8d # +OP_BecomeCorpse=0x0000 # +OP_Action2=0x0000 # +OP_MobUpdate=0x4656 # +OP_NPCMoveUpdate=0x4f35 # +OP_CameraEffect=0x6812 # +OP_SpellEffect=0x0989 # +OP_RemoveNimbusEffect=0x0000 # +OP_AltCurrency=0x2536 +OP_AltCurrencyMerchantRequest=0x5aac +OP_AltCurrencyMerchantReply=0x17c7 +OP_AltCurrencyPurchase=0x6efe +OP_AltCurrencySell=0x32d9 +OP_AltCurrencySellSelection=0x7eac +OP_AltCurrencyReclaim=0x1560 +OP_CrystalReclaim=0x0000 +OP_CrystalCreate=0x0000 +OP_Untargetable=0x101e +OP_IncreaseStats=0x3fdc + +OP_DzQuit=0x0000 +OP_DzListTimers=0x0000 +OP_DzAddPlayer=0x0000 +OP_DzRemovePlayer=0x0000 +OP_DzSwapPlayer=0x0000 +OP_DzMakeLeader=0x0000 +OP_DzPlayerList=0x0000 +OP_DzJoinExpeditionConfirm=0x0000 +OP_DzJoinExpeditionReply=0x0000 +OP_DzExpeditionInfo=0x0000 +OP_DzMemberStatus=0x0000 +OP_DzLeaderStatus=0x0000 +OP_DzExpeditionEndsWarning=0x0000 +OP_DzExpeditionList=0x0000 +OP_DzMemberList=0x0000 +OP_DzCompass=0x0000 +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # +OP_ManaUpdate=0x7c94 # +OP_EnduranceUpdate=0x5851 # +OP_MobManaUpdate=0x4d27 # +OP_MobEnduranceUpdate=0x190c # + +# Looting +OP_LootRequest=0x1d85 # +OP_EndLootRequest=0x0bc # +OP_LootItem=0x5bd9 # +OP_LootComplete=0x4ca1 # + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x2881 # +OP_TraderDelItem=0x0000 # +OP_BecomeTrader=0x358a # +OP_TraderShop=0x49f4 # +OP_Trader=0x058c # +OP_TraderBuy=0x783c # +OP_Barter=0x766f # +OP_ShopItem=0x0000 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # +OP_TraderItemUpdate=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x0c24 # +OP_TradeAcceptClick=0x064a # +OP_TradeRequestAck=0x606a # +OP_TradeCoins=0x0149 # +OP_FinishTrade=0x3ff6 # +OP_CancelTrade=0x527e # +OP_TradeMoneyUpdate=0x1ebb # +OP_MoneyUpdate=0x528f # +OP_TradeBusy=0x2c03 # + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x3c27 # +OP_FinishWindow2=0x2c6d # + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x0000 # +OP_ItemVerifyReply=0x0000 # + +# merchant crap +OP_ShopPlayerSell=0x0b27 # +OP_ShopRequest=0x442a # +OP_ShopEnd=0x078e # +OP_ShopEndConfirm=0x4762 # +OP_ShopPlayerBuy=0x436a # +OP_ShopDelItem=0x537c # + +# tradeskill stuff: +OP_ClickObject=0x5f0d # +OP_ClickObjectAction=0x29df # +OP_ClearObject=0x38d1 # +OP_RecipeDetails=0x068 # +OP_RecipesFavorite=0x0000 # +OP_RecipesSearch=0x0239 # +OP_RecipeReply=0x1ecf # +OP_RecipeAutoCombine=0x66bf # +OP_TradeSkillCombine=0x4212 # + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x0000 # +OP_OpenTributeMaster=0x3ba3 # +OP_SelectTribute=0x314f # +OP_TributeItem=0x0000 # +OP_TributeMoney=0x1772 # +OP_TributeToggle=0x0000 # +OP_TributePointUpdate=0x0000 # +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x771f # +OP_AdventureFinish=0x3168 # +OP_AdventureInfoRequest=0x05d8 # +OP_AdventureInfo=0x164b # +OP_AdventureRequest=0x3a75 # +OP_AdventureDetails=0x05 # +OP_AdventureData=0x21df # +OP_AdventureUpdate=0x6129 # +OP_AdventureMerchantRequest=0x0000 # +OP_AdventureMerchantResponse=0x0000 # +OP_AdventureMerchantPurchase=0x0000 # +OP_AdventureMerchantSell=0x0000 # +OP_AdventurePointsUpdate=0x7d05 # +OP_AdventureStatsRequest=0x0000 # +OP_AdventureStatsReply=0x15a7 # +OP_AdventureLeaderboardRequest=0x0000 # +OP_AdventureLeaderboardReply=0x6bfb # + +# Group Opcodes +OP_GroupDisband=0x4200 # +OP_GroupInvite=0x3288 # +OP_GroupFollow=0x1190 # +OP_GroupUpdate=0x0000 # +OP_GroupUpdateB=0x0000 # +OP_GroupCancelInvite=0x0000 # +OP_GroupAcknowledge=0x0000 # +OP_GroupDelete=0x0000 # +OP_CancelInvite=0x132f # +OP_GroupFollow2=0x3519 # +OP_GroupInvite2=0x083b # +OP_GroupDisbandYou=0x0000 # +OP_GroupDisbandOther=0x0000 # +OP_GroupLeaderChange=0x0000 # +OP_GroupRoles=0x0000 # +OP_GroupMakeLeader=0x13da + +# LFG/LFP Opcodes +OP_LFGCommand=0x457d # +OP_LFGGetMatchesRequest=0x49e7 # +OP_LFGGetMatchesResponse=0x4817 # +OP_LFPGetMatchesRequest=0x5fc7 # +OP_LFPGetMatchesResponse=0x3d0c # +OP_LFPCommand=0x20c6 # +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x550f # +OP_RaidUpdate=0x0000 # Testing VoA 0x0c08 +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x732c # +OP_CombatAbility=0x16a5 # +OP_SenseTraps=0x416b # +OP_PickPocket=0x13bd # +OP_DisarmTraps=0x0000 # +OP_Disarm=0x6def # +OP_Sneak=0x6eff # +OP_Fishing=0x0555 # +OP_InstillDoubt=0x4acf # +OP_FeignDeath=0x6145 # +OP_Mend=0x7fbc # +OP_LDoNOpen=0x7c87 # + +# Task packets +OP_TaskActivityComplete=0x0000 # +OP_TaskMemberList=0x0000 # +OP_OpenNewTasksWindow=0x0000 # +OP_AvaliableTask=0x0000 # +OP_AcceptNewTask=0x0000 # +OP_TaskHistoryRequest=0x6d1f # +OP_TaskHistoryReply=0x189b # +OP_CancelTask=0x47ea # +OP_DeclineAllTasks=0x0000 # + +# Title opcodes +OP_NewTitlesAvailable=0x6d95 # +OP_RequestTitles=0x7e3d # +OP_SendTitleList=0x3aef # +OP_SetTitle=0x725b # +OP_SetTitleReply=0x38e8 # + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x46d3 # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x040b # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0000 # +OP_Login=0x0000 # +OP_ServerListRequest=0x0000 # +OP_PlayEverquestRequest=0x0000 # +OP_PlayEverquestResponse=0x0000 # +OP_ChatMessage=0x0000 # +OP_LoginAccepted=0x0000 # +OP_ServerListResponse=0x0000 # +OP_Poll=0x0000 # +OP_EnterChat=0x0000 # +OP_PollResponse=0x0000 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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 # diff --git a/utils/EQExtractor2/EQExtractor2/patch_Mar15-2012.conf b/utils/EQExtractor2/EQExtractor2/patch_Mar15-2012.conf new file mode 100644 index 000000000..d8ec8a3ea --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_Mar15-2012.conf @@ -0,0 +1,631 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x4d5c # +OP_ApproveWorld=0x655c # +OP_LogServer=0x1497 # +OP_SendCharInfo=0x7f9e # +OP_ExpansionInfo=0x2c8f # +OP_GuildsList=0x5b0b # +OP_EnterWorld=0x710e # +OP_PostEnterWorld=0x7930 # +OP_World_Client_CRC1=0x7ce4 # +OP_World_Client_CRC2=0x7705 # +OP_SendSpellChecksum=0x0000 # +OP_SendSkillCapsChecksum=0x0000 # + +# Character Select Related: +OP_DeleteCharacter=0x5ca5 # +OP_CharacterCreateRequest=0x4eba # +OP_CharacterCreate=0x3237 # +OP_RandomNameGenerator=0x26aa # +OP_ApproveName=0x3154 # + +OP_MOTD=0x0b80 # +OP_SetChatServer=0x2c38 # +OP_SetChatServer2=0x441c # +OP_ZoneServerInfo=0x16f5 # +OP_WorldComplete=0x6c63 # +OP_WorldUnknown001=0x4274 # +OP_FloatListThing=0x1910 # + +# Reasons for Disconnect: +OP_ZoneUnavail=0x20bc # +OP_WorldClientReady=0x0b89 # Testing VoA 0x3f24 +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # +OP_ZoneEntry=0x5821 # +OP_ReqNewZone=0x4118 # +OP_NewZone=0x43ac # +OP_ZoneSpawns=0x5f5e # +OP_PlayerProfile=0x6afd # +OP_TimeOfDay=0x6015 # +OP_LevelUpdate=0x6e1c # +OP_Stamina=0x7686 # +OP_RequestClientZoneChange=0x224f # +OP_ZoneChange=0x28c8 # + +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x3f55 # +OP_ChangeSize=0x0000 # +OP_TributeUpdate=0x0000 # +OP_TributeTimer=0x0000 # Testing VoA 0x1525 +OP_TaskDescription=0x2e4f # +OP_TaskActivity=0x512b # +OP_CompletedTasks=0x3141 # +OP_Weather=0x3e50 # +OP_SendAATable=0x6a7e # Testing VoA 0x6a7e +OP_UpdateAA=0x5363 # Testing VoA 0x5363 +OP_RespondAA=0x7bf6 # Testing VoA 0x0643 or maybe 0x7bf6 +OP_ReqClientSpawn=0x2c27 # +OP_SpawnDoor=0x7113 # +OP_GroundSpawn=0x33e5 # +OP_SendZonepoints=0x399f # +OP_SendAAStats=0x4e22 # +OP_WorldObjectsSent=0x7b73 # +OP_BlockedBuffs=0x1681 # +OP_RemoveBlockedBuffs=0x6a86 # +OP_ClearBlockedBuffs=0x7ae1 # +OP_SendExpZonein=0x0f14 # +OP_SendTributes=0x010d # +OP_TributeInfo=0x047c # +OP_SendGuildTributes=0x0000 # +OP_AAExpUpdate=0x1318 # +OP_ExpUpdate=0x221a # Testing VoA 0x0555 +OP_HPUpdate=0x2369 # +OP_ManaChange=0x3887 # +OP_TGB=0x3672 # +OP_SpecialMesg=0x039d # +OP_GuildMemberList=0x51bc # +OP_GuildMOTD=0x0a1d # Testing VoA 0x0a1d +OP_CharInventory=0x6cfe # Testing VoA 0x6cfe +OP_WearChange=0x1a58 # +OP_ClientUpdate=0x7062 # +OP_ClientReady=0x1272 # +OP_SetServerFilter=0x053a # + +# Guild Opcodes +OP_GetGuildMOTD=0x3415 # +OP_GetGuildMOTDReply=0x709d # +OP_GuildMemberUpdate=0x589f # +OP_GuildInvite=0x5488 # +OP_GuildRemove=0x47d3 # +OP_GuildPeace=0x1d22 # +OP_SetGuildMOTD=0x5f85 # +OP_GuildList=0x0000 # +OP_GuildWar=0x0598 # +OP_GuildLeader=0x67c8 # +OP_GuildDelete=0x230e # +OP_GuildInviteAccept=0x2b5a # +OP_GuildDemote=0x1899 # +OP_GuildPublicNote=0x2dbd # +OP_GuildManageBanker=0x0000 # +OP_GuildBank=0x0000 # +OP_SetGuildRank=0x4ffe # +OP_GuildUpdateURLAndChannel=0x03d1 # +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +OP_GuildStatus=0x6916 # +OP_GuildCreate=0x0000 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x6989 # +OP_GMBecomeNPC=0x7093 # +OP_GMZoneRequest=0x701f # +OP_GMZoneRequest2=0x01 # +OP_GMGoto=0x15a1 # +OP_GMSearchCorpse=0x2bff # +OP_GMHideMe=0x7e4d # +OP_GMDelCorpse=0x072f # +OP_GMApproval=0x481f # +OP_GMToggle=0x2042 # +OP_GMSummon=0x6e47 # +OP_GMEmoteZone=0x307d # +OP_GMEmoteWorld=0x0c2d # +OP_GMFind=0x6e27 # +OP_GMKick=0x5a56 # +OP_GMKill=0x51fe # +OP_GMNameChange=0x0000 # +OP_GMLastName=0x1055 # + +OP_InspectAnswer=0x0c54 # +OP_BeginCast=0x0d5a # +OP_BuffFadeMsg=0x1b8a # +OP_ConsentResponse=0x35c6 # +OP_MemorizeSpell=0x569e # +OP_SwapSpell=0x28a7 # +OP_CastSpell=0x8543 # +OP_Consider=0x70c6 # +OP_FormattedMessage=0x4675 # +OP_SimpleMessage=0x0698 # +OP_Buff=0x4658 # +OP_Illusion=0x10b7 # +OP_MoneyOnCorpse=0x4074 # +OP_RandomReply=0x07fb # +OP_DenyResponse=0x0370 # +OP_SkillUpdate=0x7f01 # +OP_GMTrainSkillConfirm=0x0000 # +OP_RandomReq=0x303e # +OP_Death=0x6685 # +OP_Bind_Wound=0x7b64 # +OP_GMTraining=0x5960 # +OP_GMEndTraining=0x4a61 # +OP_GMTrainSkill=0x4885 # +OP_Animation=0x0b93 # +OP_Begging=0x0ca5 # +OP_Consent=0x6bb9 # +OP_ConsentDeny=0x6b7f # +OP_AutoFire=0x23fc # +OP_PetCommands=0x7312 # +OP_DeleteSpell=0x0142 # +OP_Surname=0x777c # +OP_ClearSurname=0x0000 # +OP_FaceChange=0x0507 # +OP_SenseHeading=0x1dad # +OP_Action=0x1b85 # +OP_ConsiderCorpse=0x0e94 # +OP_HideCorpse=0x0000 # +OP_CorpseDrag=0x0000 # +OP_CorpseDrop=0x0000 # +OP_Bug=0x3365 # +OP_Feedback=0x52b5 # +OP_Report=0x211a # +OP_Damage=0x45d6 # +OP_ChannelMessage=0x2e79 # +OP_Assist=0x5658 # +OP_AssistGroup=0x43bc # +OP_MoveCoin=0x1418 # +OP_ZonePlayerToBind=0x2480 # +OP_KeyRing=0x3b85 # +OP_WhoAllRequest=0x177a # +OP_WhoAllResponse=0x15de # +OP_FriendsWho=0x73d6 # +OP_ConfirmDelete=0x604d # +OP_Logout=0x6275 # +OP_Rewind=0x09e3 # +OP_TargetCommand=0x3088 # +OP_InspectRequest=0x2683 # +OP_Hide=0x2913 # +OP_Jump=0x243b # +OP_Camp=0x3cd6 # +OP_Emote=0x0000 # +OP_SetRunMode=0x59c3 # +OP_BankerChange=0x0000 # +OP_TargetMouse=0x3edc # +OP_MobHealth=0x5cb0 # +OP_InitialMobHealth=0x0000 # +OP_TargetHoTT=0x460e # +OP_TargetBuffs=0x7c24 # +OP_BuffCreate=0x0c98 # +OP_BuffRemoveRequest=0x3567 +OP_DeleteSpawn=0x3164 # +OP_AutoAttack=0x2257 # +OP_AutoAttack2=0x0000 # +OP_Consume=0x2ee2 # +OP_MoveItem=0x2963 # +OP_DeleteItem=0x7547 # +OP_DeleteCharge=0x6a90 # +OP_ItemPacket=0x2dd3 # +OP_ItemLinkResponse=0x6948 # +OP_ItemLinkClick=0x3c66 # +OP_NewSpawn=0x016c # +OP_Track=0x2444 # +OP_TrackTarget=0x538f # +OP_TrackUnknown=0x10fa # +OP_ClickDoor=0x48f9 # +OP_MoveDoor=0x231f # +OP_RemoveAllDoors=0x24a3 # +OP_EnvDamage=0x2730 # +OP_BoardBoat=0x50c2 # +OP_Forage=0x3c02 # +OP_LeaveBoat=0x569a # +OP_ControlBoat=0x2641 # +OP_SafeFallSuccess=0x1dc6 # +OP_RezzComplete=0x7108 # +OP_RezzRequest=0x66c5 # +OP_RezzAnswer=0x71eb # +OP_Shielding=0x7598 # +OP_RequestDuel=0x0e71 # +OP_MobRename=0x6884 # +OP_AugmentItem=0x4cc6 # +OP_WeaponEquip1=0x11bc # +OP_WeaponEquip2=0x2121 # +OP_WeaponUnequip2=0x7b87 # +OP_ApplyPoison=0x3582 # +OP_Save=0x47e7 # +OP_TestBuff=0x712b # +OP_CustomTitles=0x6ade # +OP_Split=0x3a18 # +OP_YellForHelp=0x1f87 # +OP_LoadSpellSet=0x440f # +OP_Bandolier=0x3ad1 # +OP_PotionBelt=0x7b7f # +OP_DuelResponse=0x1df9 # +OP_DuelResponse2=0x36f8 # +OP_SaveOnZoneReq=0x5408 # +OP_ReadBook=0x41b5 # +OP_Dye=0x32c6 # +OP_InterruptCast=0x7706 # +OP_AAAction=0x55dd # +OP_LeadershipExpToggle=0x69d0 # +OP_LeadershipExpUpdate=0x3703 # +OP_PurchaseLeadershipAA=0x6e58 # +OP_UpdateLeadershipAA=0x0297 # +OP_MarkNPC=0x6b9e # +OP_ClearNPCMarks=0x074f # +OP_DoGroupLeadershipAbility=0x0000 # +OP_GroupLeadershipAAUpdate=0x0000 # +OP_DelegateAbility=0x105b # +OP_SetGroupTarget=0x42c7 # +OP_Charm=0x17f7 # +OP_Stun=0x41a6 # +OP_SendFindableNPCs=0x3015 # +OP_FindPersonRequest=0x19a8 # +OP_FindPersonReply=0x7e45 # +OP_Sound=0x2d1d # +OP_PetBuffWindow=0x4895 # +OP_LevelAppearance=0x78b9 # +OP_Translocate=0x42ef # +OP_Sacrifice=0x2dc6 # +OP_PopupResponse=0x0000 # +OP_OnLevelMessage=0x0000 # +OP_AugmentInfo=0x0000 # +OP_Petition=0x31d1 # +OP_SomeItemPacketMaybe=0x1513 # +OP_PVPStats=0x0000 # +OP_PVPLeaderBoardRequest=0x0000 # +OP_PVPLeaderBoardReply=0x0000 # +OP_PVPLeaderBoardDetailsRequest=0x0000 # +OP_PVPLeaderBoardDetailsReply=0x0000 # +OP_RestState=0x0000 # +OP_RespawnWindow=0x435b # +OP_DisciplineTimer=0x74ca # +OP_LDoNButton=0x597d # +OP_SetStartCity=0x179d # +OP_VoiceMacroIn=0x0fce # +OP_VoiceMacroOut=0x1c36 # +OP_ItemViewUnknown=0x0000 # +OP_VetRewardsAvaliable=0x3637 # +OP_VetClaimRequest=0x032b # +OP_VetClaimReply=0x7b6e # +OP_CrystalCountUpdate=0x0000 # +OP_DisciplineUpdate=0x0d8d # +OP_BecomeCorpse=0x0000 # +OP_Action2=0x0000 # +OP_MobUpdate=0x4656 # +OP_NPCMoveUpdate=0x2339 # +OP_CameraEffect=0x6812 # +OP_SpellEffect=0x0989 # +OP_RemoveNimbusEffect=0x0000 # +OP_AltCurrency=0x2536 +OP_AltCurrencyMerchantRequest=0x5aac +OP_AltCurrencyMerchantReply=0x17c7 +OP_AltCurrencyPurchase=0x6efe +OP_AltCurrencySell=0x32d9 +OP_AltCurrencySellSelection=0x7eac +OP_AltCurrencyReclaim=0x1560 +OP_CrystalReclaim=0x0000 +OP_CrystalCreate=0x0000 +OP_Untargetable=0x101e +OP_IncreaseStats=0x3fdc + +OP_DzQuit=0x0000 +OP_DzListTimers=0x0000 +OP_DzAddPlayer=0x0000 +OP_DzRemovePlayer=0x0000 +OP_DzSwapPlayer=0x0000 +OP_DzMakeLeader=0x0000 +OP_DzPlayerList=0x0000 +OP_DzJoinExpeditionConfirm=0x0000 +OP_DzJoinExpeditionReply=0x0000 +OP_DzExpeditionInfo=0x0000 +OP_DzMemberStatus=0x0000 +OP_DzLeaderStatus=0x0000 +OP_DzExpeditionEndsWarning=0x0000 +OP_DzExpeditionList=0x0000 +OP_DzMemberList=0x0000 +OP_DzCompass=0x0000 +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # +OP_ManaUpdate=0x7c94 # +OP_EnduranceUpdate=0x5851 # +OP_MobManaUpdate=0x4d27 # +OP_MobEnduranceUpdate=0x190c # + +# Looting +OP_LootRequest=0x1d85 # +OP_EndLootRequest=0x0bc # +OP_LootItem=0x5bd9 # +OP_LootComplete=0x4ca1 # + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x2881 # +OP_TraderDelItem=0x0000 # +OP_BecomeTrader=0x358a # +OP_TraderShop=0x49f4 # +OP_Trader=0x058c # +OP_TraderBuy=0x783c # +OP_Barter=0x766f # +OP_ShopItem=0x0000 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # +OP_TraderItemUpdate=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x0c24 # +OP_TradeAcceptClick=0x064a # +OP_TradeRequestAck=0x606a # +OP_TradeCoins=0x0149 # +OP_FinishTrade=0x3ff6 # +OP_CancelTrade=0x527e # +OP_TradeMoneyUpdate=0x1ebb # +OP_MoneyUpdate=0x528f # +OP_TradeBusy=0x2c03 # + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x3c27 # +OP_FinishWindow2=0x2c6d # + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x0000 # +OP_ItemVerifyReply=0x0000 # + +# merchant crap +OP_ShopPlayerSell=0x0b27 # +OP_ShopRequest=0x4194 # +OP_ShopEnd=0x078e # +OP_ShopEndConfirm=0x4762 # +OP_ShopPlayerBuy=0x436a # +OP_ShopDelItem=0x537c # + +# tradeskill stuff: +OP_ClickObject=0x5f0d # +OP_ClickObjectAction=0x29df # +OP_ClearObject=0x38d1 # +OP_RecipeDetails=0x068 # +OP_RecipesFavorite=0x0000 # +OP_RecipesSearch=0x0239 # +OP_RecipeReply=0x1ecf # +OP_RecipeAutoCombine=0x66bf # +OP_TradeSkillCombine=0x4212 # + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x0000 # +OP_OpenTributeMaster=0x3ba3 # +OP_SelectTribute=0x314f # +OP_TributeItem=0x0000 # +OP_TributeMoney=0x1772 # +OP_TributeToggle=0x0000 # +OP_TributePointUpdate=0x0000 # +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x771f # +OP_AdventureFinish=0x3168 # +OP_AdventureInfoRequest=0x05d8 # +OP_AdventureInfo=0x164b # +OP_AdventureRequest=0x3a75 # +OP_AdventureDetails=0x05 # +OP_AdventureData=0x21df # +OP_AdventureUpdate=0x6129 # +OP_AdventureMerchantRequest=0x0000 # +OP_AdventureMerchantResponse=0x0000 # +OP_AdventureMerchantPurchase=0x0000 # +OP_AdventureMerchantSell=0x0000 # +OP_AdventurePointsUpdate=0x7d05 # +OP_AdventureStatsRequest=0x0000 # +OP_AdventureStatsReply=0x15a7 # +OP_AdventureLeaderboardRequest=0x0000 # +OP_AdventureLeaderboardReply=0x6bfb # + +# Group Opcodes +OP_GroupDisband=0x4200 # +OP_GroupInvite=0x3288 # +OP_GroupFollow=0x1190 # +OP_GroupUpdate=0x0000 # +OP_GroupUpdateB=0x0000 # +OP_GroupCancelInvite=0x0000 # +OP_GroupAcknowledge=0x0000 # +OP_GroupDelete=0x0000 # +OP_CancelInvite=0x132f # +OP_GroupFollow2=0x3519 # +OP_GroupInvite2=0x083b # +OP_GroupDisbandYou=0x0000 # +OP_GroupDisbandOther=0x0000 # +OP_GroupLeaderChange=0x0000 # +OP_GroupRoles=0x0000 # +OP_GroupMakeLeader=0x13da + +# LFG/LFP Opcodes +OP_LFGCommand=0x457d # +OP_LFGGetMatchesRequest=0x49e7 # +OP_LFGGetMatchesResponse=0x4817 # +OP_LFPGetMatchesRequest=0x5fc7 # +OP_LFPGetMatchesResponse=0x3d0c # +OP_LFPCommand=0x20c6 # +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x550f # +OP_RaidUpdate=0x0000 # Testing VoA 0x0c08 +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x732c # +OP_CombatAbility=0x16a5 # +OP_SenseTraps=0x416b # +OP_PickPocket=0x13bd # +OP_DisarmTraps=0x0000 # +OP_Disarm=0x6def # +OP_Sneak=0x6eff # +OP_Fishing=0x0555 # +OP_InstillDoubt=0x4acf # +OP_FeignDeath=0x6145 # +OP_Mend=0x7fbc # +OP_LDoNOpen=0x7c87 # + +# Task packets +OP_TaskActivityComplete=0x0000 # +OP_TaskMemberList=0x0000 # +OP_OpenNewTasksWindow=0x0000 # +OP_AvaliableTask=0x0000 # +OP_AcceptNewTask=0x0000 # +OP_TaskHistoryRequest=0x6d1f # +OP_TaskHistoryReply=0x189b # +OP_CancelTask=0x47ea # +OP_DeclineAllTasks=0x0000 # + +# Title opcodes +OP_NewTitlesAvailable=0x6d95 # +OP_RequestTitles=0x7e3d # +OP_SendTitleList=0x3aef # +OP_SetTitle=0x725b # +OP_SetTitleReply=0x38e8 # + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x46d3 # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x040b # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0000 # +OP_Login=0x0000 # +OP_ServerListRequest=0x0000 # +OP_PlayEverquestRequest=0x0000 # +OP_PlayEverquestResponse=0x0000 # +OP_ChatMessage=0x0000 # +OP_LoginAccepted=0x0000 # +OP_ServerListResponse=0x0000 # +OP_Poll=0x0000 # +OP_EnterChat=0x0000 # +OP_PollResponse=0x0000 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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 # diff --git a/utils/EQExtractor2/EQExtractor2/patch_March15-2011.conf b/utils/EQExtractor2/EQExtractor2/patch_March15-2011.conf new file mode 100644 index 000000000..b065333b0 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_March15-2011.conf @@ -0,0 +1,611 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x0000a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x6893 # was 0x2683 +OP_ApproveWorld=0x115a # was 0x28a7 +OP_LogServer=0x701f # was 0x224f +OP_SendCharInfo=0x1b85 # was 0x2c27 +OP_ExpansionInfo=0x1771 # Same +OP_GuildsList=0x5b0b # Same +OP_EnterWorld=0x3288 # was 0x4f60 +OP_PostEnterWorld=0x2f2c # was 0x570c +OP_World_Client_CRC1=0x0000 # C +OP_World_Client_CRC2=0x0000 # C +OP_SendSpellChecksum=0x0000 # C +OP_SendSkillCapsChecksum=0x0000 # C + +# Character Select Related: +OP_DeleteCharacter=0x0000 # C +OP_CharacterCreateRequest=0x0000 # C +OP_CharacterCreate=0x0000 # C +OP_RandomNameGenerator=0x0000 # C 0x0000 +OP_ApproveName=0x0000 # C + +OP_MOTD=0x7312 # C 0x0000 +OP_SetChatServer=0x698a # +OP_SetChatServer2=0x0000 # Not sent any more ? +OP_ZoneServerInfo=0x0479 # was 0x03cc +OP_WorldComplete=0x510c # +OP_WorldUnknown001=0x0000 # C 0x0000 +OP_FloatListThing=0x0000 # V + +# Reasons for Disconnect: +OP_ZoneUnavail=0x0000 # C 0x0000 +OP_WorldClientReady=0x399b # C 0x0000 +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # was 0x3594 +OP_ZoneEntry=0x002b # was 0x538f +OP_ReqNewZone=0x5ca5 # was 0x5ca5 +OP_NewZone=0x0254 # was 0x0254 +OP_ZoneSpawns=0x0000 # ? +OP_PlayerProfile=0x6022 # was 0x6022 +OP_TimeOfDay=0x6015 # was 0x6015 +OP_LevelUpdate=0x0000 # V +OP_Stamina=0x0000 # V +OP_RequestClientZoneChange=0x3fd2 # was 0x4178 +OP_ZoneChange=0x0b93 # was 0x4a61 +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x7b64 # V +OP_TributeUpdate=0x4895 # +OP_TributeTimer=0x0000 # C +OP_TaskDescription=0x0000 # C +OP_TaskActivity=0x0000 # C +OP_CompletedTasks=0x0000 # C 0x0000 +OP_Weather=0x7ce4 # was 0x0ed5 +OP_SendAATable=0x1b26 # +OP_UpdateAA=0x0000 # V +OP_RespondAA=0x0000 # C 0x0000 +OP_ReqClientSpawn=0x54e8 # was 0x6618 +OP_SpawnDoor=0x4849 # +OP_GroundSpawn=0x5f0d # was 0x5f0d +OP_SendZonepoints=0x5f51 # was 0x0ff4 +OP_SendAAStats=0x0000 # C +OP_WorldObjectsSent=0x7b73 # was 0x7b73 +OP_BlockedBuffs=0x664a # was 0x4027 +OP_SendExpZonein=0x2c27 # was 0x1436 +OP_SendTributes=0x0000 # V +OP_TributeInfo=0x0000 # V +OP_SendGuildTributes=0x0000 # C 0x0000 +OP_AAExpUpdate=0x0000 # V +OP_ExpUpdate=0x0000 # V +OP_HPUpdate=0x0000 # V +OP_ManaChange=0x0000 # C +OP_TGB=0x0000 # C +OP_SpecialMesg=0x3bf5 # was 0x14ff +OP_GuildMemberList=0x0000 # C +OP_GuildMOTD=0x0000 # V +OP_CharInventory=0x465e # +OP_WearChange=0x0000 # V +OP_ClientUpdate=0x7062 # was 0x7062 +OP_ClientReady=0x6cdc # was 0x6cdc +OP_SetServerFilter=0x0000 # V + +# Guild Opcodes +OP_GetGuildMOTD=0x0000 # C +OP_GetGuildMOTDReply=0x0000 # C +OP_GuildMemberUpdate=0x0000 # C +OP_GuildInvite=0x0000 # C +OP_GuildRemove=0x0000 # C +OP_GuildPeace=0x0000 # C +OP_SetGuildMOTD=0x0000 # C +OP_GuildList=0x0000 # C +OP_GuildWar=0x0000 # C +OP_GuildLeader=0x0000 # C +OP_GuildDelete=0x0000 # C +OP_GuildInviteAccept=0x0000 # C +OP_GuildDemote=0x0000 # C +OP_GuildPublicNote=0x0000 # C +OP_GuildManageBanker=0x0000 # C +OP_GuildBank=0x0000 # C +OP_SetGuildRank=0x0000 # C +OP_GuildUpdateURLAndChannel=0x0000 # C +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x0000 # C +OP_GMBecomeNPC=0x0000 # C +OP_GMZoneRequest=0x6f79 # was 0x18ea +OP_GMZoneRequest2=0x02d6 # was 0x3ad9 +OP_GMGoto=0x0000 # C +OP_GMSearchCorpse=0x0000 # C +OP_GMHideMe=0x0000 # C +OP_GMDelCorpse=0x0000 # C +OP_GMApproval=0x0000 # C +OP_GMToggle=0x0000 # C 0x0000 +OP_GMSummon=0x0000 # C +OP_GMEmoteZone=0x0000 # C +OP_GMEmoteWorld=0x0000 # C +OP_GMFind=0x0000 # C +OP_GMKick=0x0000 # C +OP_GMKill=0x0000 # C +OP_GMNameChange=0x0000 # C +OP_GMLastName=0x0000 # C + +OP_InspectAnswer=0x0000 # C +OP_BeginCast=0x0000 # C +OP_BuffFadeMsg=0x50c2 # C +OP_ConsentResponse=0x0000 # C +OP_MemorizeSpell=0x0000 # C +OP_SwapSpell=0x0000 # C +OP_CastSpell=0x7286 # C +OP_Consider=0x0000 # C +OP_FormattedMessage=0x0000 # C +OP_SimpleMessage=0x0000 # C 0x0000 +OP_Buff=0x0000 # C +OP_Illusion=0x0000 # C +OP_MoneyOnCorpse=0x0000 # C +OP_RandomReply=0x0000 # C +OP_DenyResponse=0x0000 # C +OP_SkillUpdate=0x0000 # C +OP_GMTrainSkillConfirm=0x0000 # C +OP_RandomReq=0x0000 # C +OP_Death=0x0000 # C +OP_Bind_Wound=0x0000 # C +OP_GMTraining=0x0000 # C +OP_GMEndTraining=0x0000 # C +OP_GMTrainSkill=0x0000 # C +OP_Animation=0x0000 # Was 0x0000 +OP_Begging=0x0000 # C +OP_Consent=0x0000 # C +OP_ConsentDeny=0x0000 # C +OP_AutoFire=0x0000 # C +OP_PetCommands=0x0000 # C +OP_DeleteSpell=0x0000 # C +OP_Surname=0x0000 # C +OP_ClearSurname=0x0000 # C +OP_FaceChange=0x0000 # C +OP_SenseHeading=0x0000 # C +OP_Action=0x1513 # C +OP_ConsiderCorpse=0x0000 # C +OP_HideCorpse=0x0000 # C 0x0000 +OP_Bug=0x0000 # C +OP_Feedback=0x0000 # C +OP_Report=0x0000 # C +OP_Damage=0x7519 # C or OP_Action2? +OP_ChannelMessage=0x2e79 # was 0x2e79 +OP_Assist=0x0000 # C +OP_AssistGroup=0x0000 # C +OP_MoveCoin=0x0000 # C +OP_ZonePlayerToBind=0x0000 # C +OP_KeyRing=0x0000 # C +OP_WhoAllRequest=0x0000 # C +OP_WhoAllResponse=0x0000 # C +OP_FriendsWho=0x0000 # C +OP_ConfirmDelete=0x0000 # V +OP_Logout=0x44ae # was 0x64ec +OP_Rewind=0x0000 # C +OP_TargetCommand=0x0000 # C Was 0x0000 +OP_InspectRequest=0x0000 # C +OP_Hide=0x0000 # C +OP_Jump=0x0000 # C +OP_Camp=0x42ef # C +OP_Emote=0x0000 # C +OP_SetRunMode=0x0000 # C +OP_BankerChange=0x0000 # C +OP_TargetMouse=0x36f8 # C 0x0000 +OP_MobHealth=0x0000 # C +OP_InitialMobHealth=0x0000 # C +OP_TargetHoTT=0x0000 # C +OP_TargetBuffs=0x0000 # C +OP_BuffCreate=0x6bfb # V +OP_DeleteSpawn=0x7434 # was 0x7434 +OP_AutoAttack=0x0000 # C +OP_AutoAttack2=0x0000 # C +OP_Consume=0x0000 # V +OP_MoveItem=0x0000 # C +OP_DeleteItem=0x0000 # C +OP_DeleteCharge=0x0000 # C +OP_ItemPacket=0x35b2 # +OP_ItemLinkResponse=0x0000 # C +OP_ItemLinkClick=0x0000 # C +OP_NewSpawn=0x0000 # C +OP_Track=0x0000 # C +OP_TrackTarget=0x0000 # C +OP_TrackUnknown=0x0000 # C +OP_ClickDoor=0x470e # was 0x4f1f +OP_MoveDoor=0x48f9 # was 0x1516 +OP_EnvDamage=0x0000 # C +OP_BoardBoat=0x0000 # C +OP_Forage=0x0000 # C +OP_LeaveBoat=0x0000 # C +OP_ControlBoat=0x0000 # C +OP_SafeFallSuccess=0x0000 # C +OP_RezzComplete=0x0000 # C +OP_RezzRequest=0x0000 # C +OP_RezzAnswer=0x0000 # C +OP_Shielding=0x0000 # C +OP_RequestDuel=0x0000 # C +OP_MobRename=0x0000 # C +OP_AugmentItem=0x0000 # C +OP_WeaponEquip1=0x0000 # C +OP_WeaponEquip2=0x0000 # C +OP_WeaponUnequip2=0x0000 # C +OP_ApplyPoison=0x0000 # C +OP_Save=0x0000 # C +OP_TestBuff=0x0000 # C +OP_CustomTitles=0x0000 # C +OP_Split=0x0000 # C +OP_YellForHelp=0x0000 # C +OP_LoadSpellSet=0x0000 # C +OP_Bandolier=0x0000 # C +OP_PotionBelt=0x0000 # C +OP_DuelResponse=0x0000 # C +OP_DuelResponse2=0x0000 # C +OP_SaveOnZoneReq=0x2913 # was 0x2913 +OP_ReadBook=0x0000 # C +OP_Dye=0x0000 # C +OP_InterruptCast=0x0000 # C +OP_AAAction=0x0000 # C +OP_LeadershipExpToggle=0x0000 # C +OP_LeadershipExpUpdate=0x0000 # C +OP_PurchaseLeadershipAA=0x0000 # C +OP_UpdateLeadershipAA=0x0000 # C +OP_MarkNPC=0x0000 # C +OP_ClearNPCMarks=0x0000 # C +OP_DoGroupLeadershipAbility=0x0000 # C +OP_GroupLeadershipAAUpdate=0x0000 # C +OP_DelegateAbility=0x0000 # C +OP_SetGroupTarget=0x0000 # C +OP_Charm=0x0000 # C +OP_Stun=0x0000 # C +OP_SendFindableNPCs=0x7eeb # C +OP_FindPersonRequest=0x0000 # C +OP_FindPersonReply=0x0000 # C +OP_Sound=0x0000 # C +OP_PetBuffWindow=0x0000 # C +OP_LevelAppearance=0x0000 # C +OP_Translocate=0x0000 # C +OP_Sacrifice=0x0000 # C +OP_PopupResponse=0x0000 # C +OP_OnLevelMessage=0x0000 # C +OP_AugmentInfo=0x0000 # C +OP_Petition=0x0000 # C +OP_SomeItemPacketMaybe=0x4200 # C +OP_SomeItemPacketMaybe=0x0000 # C +OP_PVPStats=0x0000 # C +OP_PVPLeaderBoardRequest=0x0000 # C +OP_PVPLeaderBoardReply=0x0000 # C +OP_PVPLeaderBoardDetailsRequest=0x0000 # C +OP_PVPLeaderBoardDetailsReply=0x0000 # C +OP_RestState=0x0000 # C +OP_RespawnWindow=0x0000 # C +OP_DisciplineTimer=0x0000 # C +OP_LDoNButton=0x0000 # C +OP_SetStartCity=0x0000 # C +OP_VoiceMacroIn=0x0000 # C +OP_VoiceMacroOut=0x0000 # C +OP_ItemViewUnknown=0x0000 # C +OP_VetRewardsAvaliable=0x0000 # C Mispelled? +OP_VetClaimRequest=0x0000 # C +OP_VetClaimReply=0x0000 # C +OP_CrystalCountUpdate=0x0000 # C +OP_DisciplineUpdate=0x0000 # +OP_BecomeCorpse=0x0000 # +OP_Action2=0x0000 # C OP_Damage? +OP_MobUpdate=0x4656 # was 0x4656 +OP_NPCMoveUpdate=0x3278 # +OP_CameraEffect=0x0000 # V +OP_SpellEffect=0x0000 # V + +OP_DzQuit=0x0000 +OP_DzListTimers=0x0000 +OP_DzAddPlayer=0x0000 +OP_DzRemovePlayer=0x0000 +OP_DzSwapPlayer=0x0000 +OP_DzMakeLeader=0x0000 +OP_DzPlayerList=0x0000 +OP_DzJoinExpeditionConfirm=0x0000 +OP_DzJoinExpeditionReply=0x0000 +OP_DzExpeditionInfo=0x0000 +OP_DzMemberStatus=0x0000 +OP_DzLeaderStatus=0x0000 +OP_DzExpeditionEndsWarning=0x0000 +OP_DzExpeditionList=0x0000 +OP_DzMemberList=0x0000 +OP_DzCompass=0x0000 +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # C +OP_ManaUpdate=0x0000 # C +OP_EnduranceUpdate=0x0000 # C +OP_MobManaUpdate=0x0000 # C +OP_MobEnduranceUpdate=0x0000 # C + +# Looting +OP_LootRequest=0x0000 # C +OP_EndLootRequest=0x0000 # C +OP_LootItem=0x0000 # C +OP_LootComplete=0x0000 # C + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x0000 # C +OP_TraderDelItem=0x0000 # C +OP_BecomeTrader=0x0000 # C +OP_TraderShop=0x0000 # C +OP_Trader=0x0000 # C +OP_TraderBuy=0x0000 # C +OP_Barter=0x0000 # C +OP_ShopItem=0x0000 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # +OP_TraderItemUpdate=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x0000 # C +OP_TradeAcceptClick=0x0000 # C +OP_TradeRequestAck=0x0000 # C +OP_TradeCoins=0x0000 # C +OP_FinishTrade=0x0000 # C +OP_CancelTrade=0x0000 # C +OP_TradeMoneyUpdate=0x0000 # C +OP_MoneyUpdate=0x0000 # C +OP_TradeBusy=0x0000 # C + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x0000 # C +OP_FinishWindow2=0x0000 # C + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x0000 # C +OP_ItemVerifyReply=0x0000 # C + +# merchant crap +OP_ShopPlayerSell=0x0000 # C +OP_ShopRequest=0x1044 # was 0x1044 +OP_ShopEnd=0x3753 # was 0x3753 +OP_ShopEndConfirm=0x0000 # C +OP_ShopPlayerBuy=0x0000 # C +OP_ShopDelItem=0x0000 # C + +# tradeskill stuff: +OP_ClickObject=0x0000 # V +OP_ClickObjectAction=0x0000 # V +OP_ClearObject=0x0000 # C +OP_RecipeDetails=0x0000 # C +OP_RecipesFavorite=0x0000 # C +OP_RecipesSearch=0x0000 # C +OP_RecipeReply=0x0000 # C +OP_RecipeAutoCombine=0x0000 # C +OP_TradeSkillCombine=0x0000 # C + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x0000 # C +OP_OpenTributeMaster=0x0000 # C +OP_SelectTribute=0x0000 # C +OP_TributeItem=0x0000 # C +OP_TributeMoney=0x0000 # C +OP_TributeToggle=0x0000 # C +OP_TributePointUpdate=0x0000 # C +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x0000 # C +OP_AdventureFinish=0x0000 # C +OP_AdventureInfoRequest=0x0000 # C +OP_AdventureInfo=0x0000 # C +OP_AdventureRequest=0x0000 # C +OP_AdventureDetails=0x0000 # C +OP_AdventureData=0x0000 # C +OP_AdventureUpdate=0x0000 # C +OP_AdventureMerchantRequest=0x0000 # C +OP_AdventureMerchantResponse=0x0000 # C +OP_AdventureMerchantPurchase=0x0000 # C +OP_AdventureMerchantSell=0x0000 # C +OP_AdventurePointsUpdate=0x0000 # C +OP_AdventureStatsRequest=0x0000 # C +OP_AdventureStatsReply=0x0000 # C +OP_AdventureLeaderboardRequest=0x0000 # C +OP_AdventureLeaderboardReply=0x0000 # C + +# Group Opcodes +OP_GroupDisband=0x0000 # C +OP_GroupInvite=0x0000 # C +OP_GroupFollow=0x0000 # C +OP_GroupUpdate=0x0000 # C +OP_GroupUpdateB=0x0000 # C +OP_GroupCancelInvite=0x0000 # C - Same as OP_CancelInvite? +OP_GroupAcknowledge=0x0000 # C +OP_GroupDelete=0x0000 # +OP_CancelInvite=0x0000 # C +OP_GroupFollow2=0x0000 # C +OP_GroupInvite2=0x0000 # C +OP_GroupDisbandYou=0x0000 # C +OP_GroupDisbandOther=0x0000 # C +OP_GroupLeaderChange=0x0000 # C +OP_GroupRoles=0x0000 # C + +# LFG/LFP Opcodes +OP_LFGCommand=0x0000 # C +OP_LFGGetMatchesRequest=0x0000 # C +OP_LFGGetMatchesResponse=0x0000 # C +OP_LFPGetMatchesRequest=0x0000 # C +OP_LFPGetMatchesResponse=0x0000 # C +OP_LFPCommand=0x0000 # C +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x0000 # C +OP_RaidUpdate=0x0000 # C +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x0000 # C +OP_CombatAbility=0x0000 # C +OP_SenseTraps=0x0000 # C +OP_PickPocket=0x0000 # C +OP_DisarmTraps=0x0000 # +OP_Disarm=0x0000 # C +OP_Sneak=0x0000 # C +OP_Fishing=0x0000 # C +OP_InstillDoubt=0x0000 # C +OP_FeignDeath=0x0000 # C +OP_Mend=0x0000 # C +OP_LDoNOpen=0x0000 # C + +# Task packets +OP_TaskActivityComplete=0x0000 # C +OP_TaskMemberList=0x0000 # C +OP_OpenNewTasksWindow=0x0000 # C +OP_AvaliableTask=0x0000 # C Mispelled? +OP_AcceptNewTask=0x0000 # C +OP_TaskHistoryRequest=0x0000 # C +OP_TaskHistoryReply=0x0000 # C +OP_CancelTask=0x0000 # C +OP_DeclineAllTasks=0x0000 # + +# Title opcodes +OP_NewTitlesAvailable=0x0000 # C +OP_RequestTitles=0x0000 # C +OP_SendTitleList=0x0000 # C +OP_SetTitle=0x0000 # C +OP_SetTitleReply=0x0000 # C + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_IncreaseStats=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x0000 # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x0000 # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0000 # +OP_Login=0x0000 # +OP_ServerListRequest=0x0000 # +OP_PlayEverquestRequest=0x0000 # +OP_PlayEverquestResponse=0x0000 # +OP_ChatMessage=0x0000 # +OP_LoginAccepted=0x0000 # +OP_ServerListResponse=0x0000 # +OP_Poll=0x0000 # +OP_EnterChat=0x0000 # +OP_PollResponse=0x0000 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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_OPCode2511=0x2511 \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/patch_May12-2010.conf b/utils/EQExtractor2/EQExtractor2/patch_May12-2010.conf new file mode 100644 index 000000000..17ba0744b --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_May12-2010.conf @@ -0,0 +1,619 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x13da # V +OP_ApproveWorld=0x86c7 # C +OP_LogServer=0x6f79 # C +OP_SendCharInfo=0x4200 # C +OP_ExpansionInfo=0x7e4d # C +OP_GuildsList=0x5b0b # C +OP_EnterWorld=0x51b9 # C +OP_PostEnterWorld=0x5d32 # C +OP_World_Client_CRC1=0x3a18 # C +OP_World_Client_CRC2=0x3e50 # C +OP_SendSpellChecksum=0x46d3 # C +OP_SendSkillCapsChecksum=0x040b # C + +# Character Select Related: +OP_DeleteCharacter=0x5ca5 # C +OP_CharacterCreateRequest=0x53a3 # C +OP_CharacterCreate=0x1b85 # C +OP_RandomNameGenerator=0x647a # C 0x440f +OP_ApproveName=0x4f1f # C + +OP_MOTD=0x7629 # C 0x2b59 +OP_SetChatServer=0x7d90 # C 0x0479 +OP_SetChatServer2=0x7327 # C 0x158f +OP_ZoneServerInfo=0x1190 # C 0x41c0 +OP_WorldComplete=0x441c # C +OP_WorldUnknown001=0x6f9d # C 0x77b1 +OP_FloatListThing=0x61ba # V + +# Reasons for Disconnect: +OP_ZoneUnavail=0x3288 # C 0x1190 +OP_WorldClientReady=0x7d05 # C 0x4786 +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # V +OP_ZoneEntry=0x4b61 # V +OP_ReqNewZone=0x4118 # V +OP_NewZone=0x43ac # V +OP_ZoneSpawns=0x7114 # ? +OP_PlayerProfile=0x6022 # V +OP_TimeOfDay=0x6015 # V +OP_LevelUpdate=0x6a99 # V +OP_Stamina=0x3d86 # V +OP_RequestClientZoneChange=0x18ea # C +OP_ZoneChange=0x6d37 # C + +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x3e17 # V +OP_TributeUpdate=0x684c # V +OP_TributeTimer=0x4895 # C +OP_TaskDescription=0x156c # C +OP_TaskActivity=0x31f3 # C +OP_CompletedTasks=0x82d7 # C 0x6d27 +OP_Weather=0x4658 # V +OP_SendAATable=0x6ef9 # V +OP_UpdateAA=0x7bf6 # V +OP_RespondAA=0x1fbd # C 0x2bad +OP_ReqClientSpawn=0x69cd # V +OP_SpawnDoor=0x6f2b # V +OP_GroundSpawn=0x5c85 # V +OP_SendZonepoints=0x2370 # V +OP_SendAAStats=0x78b9 # C +OP_WorldObjectsSent=0x7b73 # V +OP_BlockedBuffs=0x05d5 # V +OP_SendExpZonein=0x47e7 # V +OP_SendTributes=0x6bfb # V +OP_TributeInfo=0x5a67 # V +OP_SendGuildTributes=0x4df0 # C 0x5a01 +OP_AAExpUpdate=0x4aa2 # V +OP_ExpUpdate=0x0555 # V +OP_HPUpdate=0x6145 # V +OP_ManaChange=0x569a # C +OP_TGB=0x42ef # C +OP_SpecialMesg=0x016c # V +OP_GuildMemberList=0x51bc # C +OP_GuildMOTD=0x5658 # V +OP_CharInventory=0x47ae # V +OP_WearChange=0x0400 # V +OP_ClientUpdate=0x7062 # V +OP_ClientReady=0x6cdc # V +OP_SetServerFilter=0x2d74 # V + +# Guild Opcodes +OP_GetGuildMOTD=0x1899 # C +OP_GetGuildMOTDReply=0x4a5c # C +OP_GuildMemberUpdate=0x0a53 # C +OP_GuildInvite=0x1a58 # C +OP_GuildRemove=0x3c02 # C +OP_GuildPeace=0x2bff # C +OP_SetGuildMOTD=0x053a # C +OP_GuildList=0x5b0b # C +OP_GuildWar=0x5408 # C +OP_GuildLeader=0x0598 # C +OP_GuildDelete=0x3f55 # C +OP_GuildInviteAccept=0x7b64 # C +OP_GuildDemote=0x457d # C +OP_GuildPublicNote=0x2dbd # C +OP_GuildManageBanker=0x1e4c # C +OP_GuildBank=0x0d8a # C +OP_SetGuildRank=0x4ffe # C +OP_GuildUpdateURLAndChannel=0x5232 # C +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x6989 # C +OP_GMBecomeNPC=0x56e7 # C +OP_GMZoneRequest=0x3fd2 # C +OP_GMZoneRequest2=0x538f # C +OP_GMGoto=0x5ebc # C +OP_GMSearchCorpse=0x5a81 # C +OP_GMHideMe=0x28ef # C +OP_GMDelCorpse=0x655c # C +OP_GMApproval=0x7312 # C +OP_GMToggle=0x097f # C 0x7566 +OP_GMSummon=0x712b # C +OP_GMEmoteZone=0x1ac1 # C +OP_GMEmoteWorld=0x2444 # C +OP_GMFind=0x6e27 # C +OP_GMKick=0x0402 # C +OP_GMKill=0x799c # C +OP_GMNameChange=0x0f48 # C +OP_GMLastName=0x7bfb # C + +OP_InspectAnswer=0x0c2b # C +OP_BeginCast=0x0d5a # C +OP_BuffFadeMsg=0x71bf # C +OP_ConsentResponse=0x0e87 # C +OP_MemorizeSpell=0x3887 # C +OP_SwapSpell=0x0dda # C +OP_CastSpell=0x50c2 # C +OP_Consider=0x3c2d # C +OP_FormattedMessage=0x3b52 # C +OP_SimpleMessage=0x1f4d # C 0x5448 +OP_Buff=0x0d1d # C +OP_Illusion=0x231f # C +OP_MoneyOnCorpse=0x4a83 # C +OP_RandomReply=0x6d5d # C +OP_DenyResponse=0x6129 # C +OP_SkillUpdate=0x7f01 # C +OP_GMTrainSkillConfirm=0x3190 # C +OP_RandomReq=0x139d # C +OP_Death=0x7f9e # C +OP_Bind_Wound=0x4b1a # C +OP_GMTraining=0x51fa # C +OP_GMEndTraining=0x5479 # C +OP_GMTrainSkill=0x2257 # C +OP_Animation=0x4a61 # Was 0x47d3 +OP_Begging=0x53f9 # C +OP_Consent=0x6bb9 # C +OP_ConsentDeny=0x4cd1 # C +OP_AutoFire=0x5db5 # C +OP_PetCommands=0x7706 # C +OP_DeleteSpell=0x0698 # C +OP_Surname=0x44ae # C +OP_ClearSurname=0x2bc6 # C +OP_FaceChange=0x37a7 # C +OP_SenseHeading=0x1b8a # C +OP_Action=0x0f14 # C +OP_ConsiderCorpse=0x0a18 # C +OP_HideCorpse=0x2d08 # C 0x1842 +OP_Bug=0x2369 # C +OP_Feedback=0x7705 # C +OP_Report=0x50d0 # C +OP_Damage=0x631a # C or OP_Action2? +OP_ChannelMessage=0x2e79 # C +OP_Assist=0x35b1 # C +OP_AssistGroup=0x194f # C +OP_MoveCoin=0x6024 # C +OP_ZonePlayerToBind=0x382c # C +OP_KeyRing=0x5c06 # C +OP_WhoAllRequest=0x177a # C +OP_WhoAllResponse=0x6ffa # C +OP_FriendsWho=0x6275 # C +OP_ConfirmDelete=0x3edc # V +OP_Logout=0x224f # C +OP_Rewind=0x7d63 # C +OP_TargetCommand=0x756c # C Was 0x5f5e +OP_InspectRequest=0x7c94 # C +OP_Hide=0x3497 # C +OP_Jump=0x083b # C +OP_Camp=0x5f85 # C +OP_Emote=0x3164 # C +OP_SetRunMode=0x3d06 # C +OP_BankerChange=0x300a # C +OP_TargetMouse=0x5f5e # C 0x7bbb +OP_MobHealth=0x15de # C +OP_InitialMobHealth=0x5cb0 # C +OP_TargetHoTT=0x4d56 # C +OP_TargetBuffs=0x3f24 # C +OP_BuffCreate=0x2121 # V +OP_DeleteSpawn=0x58c5 # C +OP_AutoAttack=0x1df9 # C +OP_AutoAttack2=0x517b # C +OP_Consume=0x24c5 # V +OP_MoveItem=0x2641 # C +OP_DeleteItem=0x66e0 # C +OP_DeleteCharge=0x4ca1 # C +OP_ItemPacket=0x7b6e # C +OP_ItemLinkResponse=0x695c # C +OP_ItemLinkClick=0x3c66 # C +OP_NewSpawn=0x429b # C +OP_Track=0x3f49 # C +OP_TrackTarget=0x3f49 # C +OP_TrackUnknown=0x03e7 # C +OP_ClickDoor=0x6e97 # C +OP_MoveDoor=0x3154 # C +OP_EnvDamage=0x2730 # C +OP_BoardBoat=0x7554 # C +OP_Forage=0x739b # C +OP_LeaveBoat=0x7286 # C +OP_ControlBoat=0x7ea8 # C +OP_SafeFallSuccess=0x6df7 # C +OP_RezzComplete=0x30a4 # C +OP_RezzRequest=0x32af # C +OP_RezzAnswer=0x2d41 # C +OP_Shielding=0x4675 # C +OP_RequestDuel=0x6cfe # C +OP_MobRename=0x0507 # C +OP_AugmentItem=0x7c87 # C +OP_WeaponEquip1=0x4572 # C +OP_WeaponEquip2=0x399b # C +OP_WeaponUnequip2=0x416b # C +OP_ApplyPoison=0x5cd3 # C +OP_Save=0x6618 # C +OP_TestBuff=0x3415 # C +OP_CustomTitles=0x6a7e # C +OP_Split=0x1418 # C +OP_YellForHelp=0x55a8 # C +OP_LoadSpellSet=0x6617 # C +OP_Bandolier=0x510c # C +OP_PotionBelt=0x0651 # C +OP_DuelResponse=0x41a6 # C +OP_DuelResponse2=0x6d60 # C +OP_SaveOnZoneReq=0x2913 # C +OP_ReadBook=0x465e # C +OP_Dye=0x2137 # C +OP_InterruptCast=0x7566 # C +OP_AAAction=0x2bad # C +OP_LeadershipExpToggle=0x5033 # C +OP_LeadershipExpUpdate=0x074f # C +OP_PurchaseLeadershipAA=0x5f55 # C +OP_UpdateLeadershipAA=0x77ed # C +OP_MarkNPC=0x3ec7 # C +OP_ClearNPCMarks=0x5c29 # C +OP_DoGroupLeadershipAbility=0x0068 # C +OP_GroupLeadershipAAUpdate=0x27bf # C +OP_DelegateAbility=0x6e58 # C +OP_SetGroupTarget=0x6b9e # C +OP_Charm=0x1fd5 # C +OP_Stun=0x3d00 # C +OP_SendFindableNPCs=0x6193 # C +OP_FindPersonRequest=0x1e04 # C +OP_FindPersonReply=0x7cae # C +OP_Sound=0x737a # C +OP_PetBuffWindow=0x7b87 # C +OP_LevelAppearance=0x1bd4 # C +OP_Translocate=0x3d9c # C +OP_Sacrifice=0x301b # C +OP_PopupResponse=0x6d27 # C +OP_OnLevelMessage=0x24cb # C +OP_AugmentInfo=0x31b1 # C +OP_Petition=0x31d1 # C +OP_SomeItemPacketMaybe=0x2c27 # C +OP_PVPStats=0x6840 # C +OP_PVPLeaderBoardRequest=0x4973 # C +OP_PVPLeaderBoardReply=0x3842 # C +OP_PVPLeaderBoardDetailsRequest=0x6c75 # C +OP_PVPLeaderBoardDetailsReply=0x7fd7 # C +OP_RestState=0x5d24 # C +OP_RespawnWindow=0x107f # C +OP_DisciplineTimer=0x047c # C +OP_LDoNButton=0x1031 # C +OP_SetStartCity=0x68f0 # C +OP_VoiceMacroIn=0x1524 # C +OP_VoiceMacroOut=0x1d99 # C +OP_ItemViewUnknown=0x4eb3 # C +OP_VetRewardsAvaliable=0x0baa # C Mispelled? +OP_VetClaimRequest=0x34f8 # C +OP_VetClaimReply=0x6a5d # C +OP_CrystalCountUpdate=0x3fc8 # C +OP_DisciplineUpdate=0x0000 # +OP_BecomeCorpse=0x0000 # +OP_Action2=0x0000 # C OP_Damage? +OP_MobUpdate=0x4656 # Same as OP_SpawnPositionUpdate +OP_NPCMoveUpdate=0x0f3e # +OP_CameraEffect=0x6b0e # V +OP_SpellEffect=0x57a3 # V + +OP_DzQuit=0x1539 +OP_DzListTimers=0x21e9 +OP_DzAddPlayer=0x3657 +OP_DzRemovePlayer=0x054e +OP_DzSwapPlayer=0x4661 +OP_DzMakeLeader=0x226f +OP_DzPlayerList=0x74e4 +OP_DzJoinExpeditionConfirm=0x3c5e +OP_DzJoinExpeditionReply=0x1154 +OP_DzExpeditionInfo=0x1150 +OP_DzMemberStatus=0x2d17 +OP_DzLeaderStatus=0x2caf +OP_DzExpeditionEndsWarning=0x6ac2 +OP_DzExpeditionList=0x70d8 +OP_DzMemberList=0x15c4 +OP_DzCompass=0x01cb +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x4656 # C +OP_ManaUpdate=0x0433 # C +OP_EnduranceUpdate=0x6b76 # C +OP_MobManaUpdate=0x7901 # C +OP_MobEnduranceUpdate=0x6c5f # C + +# Looting +OP_LootRequest=0x6ad7 # C +OP_EndLootRequest=0x6546 # C +OP_LootItem=0x5960 # C +OP_LootComplete=0x604d # C + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x550f # C +OP_TraderDelItem=0x63c8 # C +OP_BecomeTrader=0x0a1d # C +OP_TraderShop=0x2881 # C +OP_Trader=0x0c08 # C +OP_TraderBuy=0x3672 # C +OP_Barter=0x6db5 # C +OP_ShopItem=0x0000 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # +OP_TraderItemUpdate=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x7113 # C +OP_TradeAcceptClick=0x064a # C +OP_TradeRequestAck=0x606a # C +OP_TradeCoins=0x0149 # C +OP_FinishTrade=0x3ff6 # C +OP_CancelTrade=0x527e # C +OP_TradeMoneyUpdate=0x2a6d # C +OP_MoneyUpdate=0xd677 # C +OP_TradeBusy=0x5ed3 # C + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x3c27 # C +OP_FinishWindow2=0x6759 # C + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x101e # C +OP_ItemVerifyReply=0x21c7 # C + +# merchant crap +OP_ShopPlayerSell=0x0b27 # C +OP_ShopRequest=0x442a # C +OP_ShopEnd=0x3753 # C +OP_ShopEndConfirm=0x4578 # C +OP_ShopPlayerBuy=0x436a # C +OP_ShopDelItem=0x63c8 # C + +# tradeskill stuff: +OP_ClickObject=0x33e5 # V +OP_ClickObjectAction=0x41b5 # V +OP_ClearObject=0x71d1 # C +OP_RecipeDetails=0x58d9 # C +OP_RecipesFavorite=0x7770 # C +OP_RecipesSearch=0x6948 # C +OP_RecipeReply=0x521c # C +OP_RecipeAutoCombine=0x0322 # C +OP_TradeSkillCombine=0x4212 # C + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x5e79 # C +OP_OpenTributeMaster=0x7c24 # C +OP_SelectTribute=0x0c98 # C +OP_TributeItem=0x0b89 # C +OP_TributeMoney=0x314f # C +OP_TributeToggle=0x6dc3 # C +OP_TributePointUpdate=0x15a7 # C +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x3ed4 # C +OP_AdventureFinish=0x6acc # C +OP_AdventureInfoRequest=0x3541 # C +OP_AdventureInfo=0x5cea # C +OP_AdventureRequest=0x2c03 # C +OP_AdventureDetails=0x1d40 # C +OP_AdventureData=0x34f2 # C +OP_AdventureUpdate=0x771f # C +OP_AdventureMerchantRequest=0x4e22 # C +OP_AdventureMerchantResponse=0x4dd5 # C +OP_AdventureMerchantPurchase=0x7b7f # C +OP_AdventureMerchantSell=0x179d # C +OP_AdventurePointsUpdate=0x7537 # C +OP_AdventureStatsRequest=0x4786 # C +OP_AdventureStatsReply=0x38b0 # C +OP_AdventureLeaderboardRequest=0x4cc6 # C +OP_AdventureLeaderboardReply=0x4423 # C + +# Group Opcodes +OP_GroupDisband=0x54e8 # C +OP_GroupInvite=0x4f60 # C +OP_GroupFollow=0x7f2b # C +OP_GroupUpdate=0x5331 # C +OP_GroupUpdateB=0x0786 # C +OP_GroupCancelInvite=0x2736 # C - Same as OP_CancelInvite? +OP_GroupAcknowledge=0x3e22 # C +OP_GroupDelete=0x58e6 # +OP_CancelInvite=0x2736 # C +OP_GroupFollow2=0x6c16 # C +OP_GroupInvite2=0x5251 # C +OP_GroupDisbandYou=0x0bd0 # C +OP_GroupDisbandOther=0x49f6 # C +OP_GroupLeaderChange=0x0c33 # C +OP_GroupRoles=0x116d # C + +# LFG/LFP Opcodes +OP_LFGCommand=0x2c38 # C +OP_LFGGetMatchesRequest=0x28d4 # C +OP_LFGGetMatchesResponse=0x7a16 # C +OP_LFPGetMatchesRequest=0x189e # C +OP_LFPGetMatchesResponse=0x589f # C +OP_LFPCommand=0x7429 # C +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x60b5 # C +OP_RaidUpdate=0x4d8b # C +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x30e2 # C +OP_CombatAbility=0x36f8 # C +OP_SenseTraps=0x7e45 # C +OP_PickPocket=0x5821 # C +OP_DisarmTraps=0x0000 # +OP_Disarm=0x6def # C +OP_Sneak=0x1d22 # C +OP_Fishing=0x7093 # C +OP_InstillDoubt=0x221a # C +OP_FeignDeath=0x002b # C +OP_Mend=0x10a6 # C +OP_LDoNOpen=0x032b # C + +# Task packets +OP_TaskActivityComplete=0x5832 # C +OP_TaskMemberList=0x66ba # C +OP_OpenNewTasksWindow=0x98f6 # C +OP_AvaliableTask=0x6255 # C Mispelled? +OP_AcceptNewTask=0x17d5 # C +OP_TaskHistoryRequest=0x547c # C +OP_TaskHistoryReply=0x4524 # C +OP_CancelTask=0x3bf5 # C +OP_DeclineAllTasks=0x0000 # + +# Title opcodes +OP_NewTitlesAvailable=0x4b49 # C +OP_RequestTitles=0x4d3e # C +OP_SendTitleList=0x0d96 # C +OP_SetTitle=0x675c # C +OP_SetTitleReply=0x75f5 # C + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_IncreaseStats=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x46d3 # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x040b # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + + + +# Login opcodes +OP_SessionReady=0x0001 # +OP_Login=0x0002 # +OP_ServerListRequest=0x0004 # +OP_PlayEverquestRequest=0x000d # +OP_PlayEverquestResponse=0x0021 # +OP_ChatMessage=0x0016 # +OP_LoginAccepted=0x0017 # +OP_ServerListResponse=0x0018 # +OP_Poll=0x0029 # +OP_EnterChat=0x000f # +OP_PollResponse=0x0011 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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_MercenaryDataRequest=0x3015 +OP_MercenaryDataResponse=0x0eaa +OP_MercenaryTimer=0x0cae +OP_MercenaryHire=0x099e +OP_MercenaryRelated1=0x2538 +OP_MercenaryUnknown1=0x367f diff --git a/utils/EQExtractor2/EQExtractor2/patch_May12-2011.conf b/utils/EQExtractor2/EQExtractor2/patch_May12-2011.conf new file mode 100644 index 000000000..fb5949d88 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_May12-2011.conf @@ -0,0 +1,611 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x0000a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x6893 # was 0x2683 +OP_ApproveWorld=0x115a # was 0x28a7 +OP_LogServer=0x701f # was 0x224f +OP_SendCharInfo=0x1b85 # was 0x2c27 +OP_ExpansionInfo=0x1771 # Same +OP_GuildsList=0x5b0b # Same +OP_EnterWorld=0x3288 # was 0x4f60 +OP_PostEnterWorld=0x2f2c # was 0x570c +OP_World_Client_CRC1=0x0000 # C +OP_World_Client_CRC2=0x0000 # C +OP_SendSpellChecksum=0x0000 # C +OP_SendSkillCapsChecksum=0x0000 # C + +# Character Select Related: +OP_DeleteCharacter=0x0000 # C +OP_CharacterCreateRequest=0x0000 # C +OP_CharacterCreate=0x0000 # C +OP_RandomNameGenerator=0x0000 # C 0x0000 +OP_ApproveName=0x0000 # C + +OP_MOTD=0x7312 # C 0x0000 +OP_SetChatServer=0x698a # +OP_SetChatServer2=0x0000 # Not sent any more ? +OP_ZoneServerInfo=0x0479 # was 0x03cc +OP_WorldComplete=0x510c # +OP_WorldUnknown001=0x0000 # C 0x0000 +OP_FloatListThing=0x0000 # V + +# Reasons for Disconnect: +OP_ZoneUnavail=0x0000 # C 0x0000 +OP_WorldClientReady=0x399b # C 0x0000 +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # was 0x3594 +OP_ZoneEntry=0x02d6 # was 0x538f +OP_ReqNewZone=0x5ca5 # was 0x5ca5 +OP_NewZone=0x0254 # was 0x0254 +OP_ZoneSpawns=0x0000 # ? +OP_PlayerProfile=0x6022 # was 0x6022 +OP_TimeOfDay=0x6015 # was 0x6015 +OP_LevelUpdate=0x0000 # V +OP_Stamina=0x0000 # V +OP_RequestClientZoneChange=0x3fd2 # was 0x4178 +OP_ZoneChange=0x0b93 # was 0x4a61 +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x7b64 # V +OP_TributeUpdate=0x4895 # +OP_TributeTimer=0x0000 # C +OP_TaskDescription=0x0000 # C +OP_TaskActivity=0x0000 # C +OP_CompletedTasks=0x0000 # C 0x0000 +OP_Weather=0x7ce4 # was 0x0ed5 +OP_SendAATable=0x1d99 # +OP_UpdateAA=0x0000 # V +OP_RespondAA=0x0000 # C 0x0000 +OP_ReqClientSpawn=0x54e8 # was 0x6618 +OP_SpawnDoor=0x6cfe # +OP_GroundSpawn=0x442a # was 0x5f0d +OP_SendZonepoints=0x5851 # was 0x0ff4 +OP_SendAAStats=0x0000 # C +OP_WorldObjectsSent=0x7b73 # was 0x7b73 +OP_BlockedBuffs=0x664a # was 0x4027 +OP_SendExpZonein=0x2c27 # was 0x1436 +OP_SendTributes=0x0000 # V +OP_TributeInfo=0x0000 # V +OP_SendGuildTributes=0x0000 # C 0x0000 +OP_AAExpUpdate=0x0000 # V +OP_ExpUpdate=0x0000 # V +OP_HPUpdate=0x0000 # V +OP_ManaChange=0x0000 # C +OP_TGB=0x0000 # C +OP_SpecialMesg=0x3bf5 # was 0x14ff +OP_GuildMemberList=0x0000 # C +OP_GuildMOTD=0x0000 # V +OP_CharInventory=0x465e # +OP_WearChange=0x0000 # V +OP_ClientUpdate=0x7062 # was 0x7062 +OP_ClientReady=0x6cdc # was 0x6cdc +OP_SetServerFilter=0x0000 # V + +# Guild Opcodes +OP_GetGuildMOTD=0x0000 # C +OP_GetGuildMOTDReply=0x0000 # C +OP_GuildMemberUpdate=0x0000 # C +OP_GuildInvite=0x0000 # C +OP_GuildRemove=0x0000 # C +OP_GuildPeace=0x0000 # C +OP_SetGuildMOTD=0x0000 # C +OP_GuildList=0x0000 # C +OP_GuildWar=0x0000 # C +OP_GuildLeader=0x0000 # C +OP_GuildDelete=0x0000 # C +OP_GuildInviteAccept=0x0000 # C +OP_GuildDemote=0x0000 # C +OP_GuildPublicNote=0x0000 # C +OP_GuildManageBanker=0x0000 # C +OP_GuildBank=0x0000 # C +OP_SetGuildRank=0x0000 # C +OP_GuildUpdateURLAndChannel=0x0000 # C +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x0000 # C +OP_GMBecomeNPC=0x0000 # C +OP_GMZoneRequest=0x6f79 # was 0x18ea +OP_GMZoneRequest2=0x02d6 # was 0x3ad9 +OP_GMGoto=0x0000 # C +OP_GMSearchCorpse=0x0000 # C +OP_GMHideMe=0x0000 # C +OP_GMDelCorpse=0x0000 # C +OP_GMApproval=0x0000 # C +OP_GMToggle=0x0000 # C 0x0000 +OP_GMSummon=0x0000 # C +OP_GMEmoteZone=0x0000 # C +OP_GMEmoteWorld=0x0000 # C +OP_GMFind=0x0000 # C +OP_GMKick=0x0000 # C +OP_GMKill=0x0000 # C +OP_GMNameChange=0x0000 # C +OP_GMLastName=0x0000 # C + +OP_InspectAnswer=0x0000 # C +OP_BeginCast=0x0000 # C +OP_BuffFadeMsg=0x50c2 # C +OP_ConsentResponse=0x0000 # C +OP_MemorizeSpell=0x0000 # C +OP_SwapSpell=0x0000 # C +OP_CastSpell=0x7286 # C +OP_Consider=0x0000 # C +OP_FormattedMessage=0x0000 # C +OP_SimpleMessage=0x0000 # C 0x0000 +OP_Buff=0x0000 # C +OP_Illusion=0x0000 # C +OP_MoneyOnCorpse=0x0000 # C +OP_RandomReply=0x0000 # C +OP_DenyResponse=0x0000 # C +OP_SkillUpdate=0x0000 # C +OP_GMTrainSkillConfirm=0x0000 # C +OP_RandomReq=0x0000 # C +OP_Death=0x0000 # C +OP_Bind_Wound=0x0000 # C +OP_GMTraining=0x0000 # C +OP_GMEndTraining=0x0000 # C +OP_GMTrainSkill=0x0000 # C +OP_Animation=0x0000 # Was 0x0000 +OP_Begging=0x0000 # C +OP_Consent=0x0000 # C +OP_ConsentDeny=0x0000 # C +OP_AutoFire=0x0000 # C +OP_PetCommands=0x0000 # C +OP_DeleteSpell=0x0000 # C +OP_Surname=0x0000 # C +OP_ClearSurname=0x0000 # C +OP_FaceChange=0x0000 # C +OP_SenseHeading=0x0000 # C +OP_Action=0x1513 # C +OP_ConsiderCorpse=0x0000 # C +OP_HideCorpse=0x0000 # C 0x0000 +OP_Bug=0x0000 # C +OP_Feedback=0x0000 # C +OP_Report=0x0000 # C +OP_Damage=0x7519 # C or OP_Action2? +OP_ChannelMessage=0x2e79 # was 0x2e79 +OP_Assist=0x0000 # C +OP_AssistGroup=0x0000 # C +OP_MoveCoin=0x0000 # C +OP_ZonePlayerToBind=0x0000 # C +OP_KeyRing=0x0000 # C +OP_WhoAllRequest=0x0000 # C +OP_WhoAllResponse=0x0000 # C +OP_FriendsWho=0x0000 # C +OP_ConfirmDelete=0x0000 # V +OP_Logout=0x44ae # was 0x64ec +OP_Rewind=0x0000 # C +OP_TargetCommand=0x0000 # C Was 0x0000 +OP_InspectRequest=0x0000 # C +OP_Hide=0x0000 # C +OP_Jump=0x0000 # C +OP_Camp=0x42ef # C +OP_Emote=0x0000 # C +OP_SetRunMode=0x0000 # C +OP_BankerChange=0x0000 # C +OP_TargetMouse=0x36f8 # C 0x0000 +OP_MobHealth=0x0000 # C +OP_InitialMobHealth=0x0000 # C +OP_TargetHoTT=0x0000 # C +OP_TargetBuffs=0x0000 # C +OP_BuffCreate=0x6bfb # V +OP_DeleteSpawn=0x7434 # was 0x7434 +OP_AutoAttack=0x0000 # C +OP_AutoAttack2=0x0000 # C +OP_Consume=0x0000 # V +OP_MoveItem=0x0000 # C +OP_DeleteItem=0x0000 # C +OP_DeleteCharge=0x0000 # C +OP_ItemPacket=0x7c87 # +OP_ItemLinkResponse=0x0000 # C +OP_ItemLinkClick=0x0000 # C +OP_NewSpawn=0x0000 # C +OP_Track=0x0000 # C +OP_TrackTarget=0x0000 # C +OP_TrackUnknown=0x0000 # C +OP_ClickDoor=0x3154 # +OP_MoveDoor=0x470e # +OP_EnvDamage=0x0000 # C +OP_BoardBoat=0x0000 # C +OP_Forage=0x0000 # C +OP_LeaveBoat=0x0000 # C +OP_ControlBoat=0x0000 # C +OP_SafeFallSuccess=0x0000 # C +OP_RezzComplete=0x0000 # C +OP_RezzRequest=0x0000 # C +OP_RezzAnswer=0x0000 # C +OP_Shielding=0x0000 # C +OP_RequestDuel=0x0000 # C +OP_MobRename=0x0000 # C +OP_AugmentItem=0x0000 # C +OP_WeaponEquip1=0x0000 # C +OP_WeaponEquip2=0x0000 # C +OP_WeaponUnequip2=0x0000 # C +OP_ApplyPoison=0x0000 # C +OP_Save=0x0000 # C +OP_TestBuff=0x0000 # C +OP_CustomTitles=0x0000 # C +OP_Split=0x0000 # C +OP_YellForHelp=0x0000 # C +OP_LoadSpellSet=0x0000 # C +OP_Bandolier=0x0000 # C +OP_PotionBelt=0x0000 # C +OP_DuelResponse=0x0000 # C +OP_DuelResponse2=0x0000 # C +OP_SaveOnZoneReq=0x2913 # was 0x2913 +OP_ReadBook=0x0000 # C +OP_Dye=0x0000 # C +OP_InterruptCast=0x0000 # C +OP_AAAction=0x0000 # C +OP_LeadershipExpToggle=0x0000 # C +OP_LeadershipExpUpdate=0x0000 # C +OP_PurchaseLeadershipAA=0x0000 # C +OP_UpdateLeadershipAA=0x0000 # C +OP_MarkNPC=0x0000 # C +OP_ClearNPCMarks=0x0000 # C +OP_DoGroupLeadershipAbility=0x0000 # C +OP_GroupLeadershipAAUpdate=0x0000 # C +OP_DelegateAbility=0x0000 # C +OP_SetGroupTarget=0x0000 # C +OP_Charm=0x0000 # C +OP_Stun=0x0000 # C +OP_SendFindableNPCs=0x390c # C +OP_FindPersonRequest=0x0000 # C +OP_FindPersonReply=0x0000 # C +OP_Sound=0x0000 # C +OP_PetBuffWindow=0x0000 # C +OP_LevelAppearance=0x0000 # C +OP_Translocate=0x0000 # C +OP_Sacrifice=0x0000 # C +OP_PopupResponse=0x0000 # C +OP_OnLevelMessage=0x0000 # C +OP_AugmentInfo=0x0000 # C +OP_Petition=0x0000 # C +OP_SomeItemPacketMaybe=0x4200 # C +OP_SomeItemPacketMaybe=0x0000 # C +OP_PVPStats=0x0000 # C +OP_PVPLeaderBoardRequest=0x0000 # C +OP_PVPLeaderBoardReply=0x0000 # C +OP_PVPLeaderBoardDetailsRequest=0x0000 # C +OP_PVPLeaderBoardDetailsReply=0x0000 # C +OP_RestState=0x0000 # C +OP_RespawnWindow=0x0000 # C +OP_DisciplineTimer=0x0000 # C +OP_LDoNButton=0x0000 # C +OP_SetStartCity=0x0000 # C +OP_VoiceMacroIn=0x0000 # C +OP_VoiceMacroOut=0x0000 # C +OP_ItemViewUnknown=0x0000 # C +OP_VetRewardsAvaliable=0x0000 # C Mispelled? +OP_VetClaimRequest=0x0000 # C +OP_VetClaimReply=0x0000 # C +OP_CrystalCountUpdate=0x0000 # C +OP_DisciplineUpdate=0x0000 # +OP_BecomeCorpse=0x0000 # +OP_Action2=0x0000 # C OP_Damage? +OP_MobUpdate=0x4656 # was 0x4656 +OP_NPCMoveUpdate=0x38e0 # +OP_CameraEffect=0x0000 # V +OP_SpellEffect=0x0000 # V + +OP_DzQuit=0x0000 +OP_DzListTimers=0x0000 +OP_DzAddPlayer=0x0000 +OP_DzRemovePlayer=0x0000 +OP_DzSwapPlayer=0x0000 +OP_DzMakeLeader=0x0000 +OP_DzPlayerList=0x0000 +OP_DzJoinExpeditionConfirm=0x0000 +OP_DzJoinExpeditionReply=0x0000 +OP_DzExpeditionInfo=0x0000 +OP_DzMemberStatus=0x0000 +OP_DzLeaderStatus=0x0000 +OP_DzExpeditionEndsWarning=0x0000 +OP_DzExpeditionList=0x0000 +OP_DzMemberList=0x0000 +OP_DzCompass=0x0000 +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # C +OP_ManaUpdate=0x0000 # C +OP_EnduranceUpdate=0x0000 # C +OP_MobManaUpdate=0x0000 # C +OP_MobEnduranceUpdate=0x0000 # C + +# Looting +OP_LootRequest=0x0000 # C +OP_EndLootRequest=0x0000 # C +OP_LootItem=0x0000 # C +OP_LootComplete=0x0000 # C + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x0000 # C +OP_TraderDelItem=0x0000 # C +OP_BecomeTrader=0x0000 # C +OP_TraderShop=0x0000 # C +OP_Trader=0x0000 # C +OP_TraderBuy=0x0000 # C +OP_Barter=0x0000 # C +OP_ShopItem=0x0000 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # +OP_TraderItemUpdate=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x0000 # C +OP_TradeAcceptClick=0x0000 # C +OP_TradeRequestAck=0x0000 # C +OP_TradeCoins=0x0000 # C +OP_FinishTrade=0x0000 # C +OP_CancelTrade=0x0000 # C +OP_TradeMoneyUpdate=0x0000 # C +OP_MoneyUpdate=0x0000 # C +OP_TradeBusy=0x0000 # C + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x0000 # C +OP_FinishWindow2=0x0000 # C + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x0000 # C +OP_ItemVerifyReply=0x0000 # C + +# merchant crap +OP_ShopPlayerSell=0x0000 # C +OP_ShopRequest=0x58c5 # was 0x1044 +OP_ShopEnd=0x3753 # was 0x3753 +OP_ShopEndConfirm=0x0000 # C +OP_ShopPlayerBuy=0x0000 # C +OP_ShopDelItem=0x0000 # C + +# tradeskill stuff: +OP_ClickObject=0x0000 # V +OP_ClickObjectAction=0x0000 # V +OP_ClearObject=0x0000 # C +OP_RecipeDetails=0x0000 # C +OP_RecipesFavorite=0x0000 # C +OP_RecipesSearch=0x0000 # C +OP_RecipeReply=0x0000 # C +OP_RecipeAutoCombine=0x0000 # C +OP_TradeSkillCombine=0x0000 # C + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x0000 # C +OP_OpenTributeMaster=0x0000 # C +OP_SelectTribute=0x0000 # C +OP_TributeItem=0x0000 # C +OP_TributeMoney=0x0000 # C +OP_TributeToggle=0x0000 # C +OP_TributePointUpdate=0x0000 # C +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x0000 # C +OP_AdventureFinish=0x0000 # C +OP_AdventureInfoRequest=0x0000 # C +OP_AdventureInfo=0x0000 # C +OP_AdventureRequest=0x0000 # C +OP_AdventureDetails=0x0000 # C +OP_AdventureData=0x0000 # C +OP_AdventureUpdate=0x0000 # C +OP_AdventureMerchantRequest=0x0000 # C +OP_AdventureMerchantResponse=0x0000 # C +OP_AdventureMerchantPurchase=0x0000 # C +OP_AdventureMerchantSell=0x0000 # C +OP_AdventurePointsUpdate=0x0000 # C +OP_AdventureStatsRequest=0x0000 # C +OP_AdventureStatsReply=0x0000 # C +OP_AdventureLeaderboardRequest=0x0000 # C +OP_AdventureLeaderboardReply=0x0000 # C + +# Group Opcodes +OP_GroupDisband=0x0000 # C +OP_GroupInvite=0x0000 # C +OP_GroupFollow=0x0000 # C +OP_GroupUpdate=0x0000 # C +OP_GroupUpdateB=0x0000 # C +OP_GroupCancelInvite=0x0000 # C - Same as OP_CancelInvite? +OP_GroupAcknowledge=0x0000 # C +OP_GroupDelete=0x0000 # +OP_CancelInvite=0x0000 # C +OP_GroupFollow2=0x0000 # C +OP_GroupInvite2=0x0000 # C +OP_GroupDisbandYou=0x0000 # C +OP_GroupDisbandOther=0x0000 # C +OP_GroupLeaderChange=0x0000 # C +OP_GroupRoles=0x0000 # C + +# LFG/LFP Opcodes +OP_LFGCommand=0x0000 # C +OP_LFGGetMatchesRequest=0x0000 # C +OP_LFGGetMatchesResponse=0x0000 # C +OP_LFPGetMatchesRequest=0x0000 # C +OP_LFPGetMatchesResponse=0x0000 # C +OP_LFPCommand=0x0000 # C +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x0000 # C +OP_RaidUpdate=0x0000 # C +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x0000 # C +OP_CombatAbility=0x0000 # C +OP_SenseTraps=0x0000 # C +OP_PickPocket=0x0000 # C +OP_DisarmTraps=0x0000 # +OP_Disarm=0x0000 # C +OP_Sneak=0x0000 # C +OP_Fishing=0x0000 # C +OP_InstillDoubt=0x0000 # C +OP_FeignDeath=0x0000 # C +OP_Mend=0x0000 # C +OP_LDoNOpen=0x0000 # C + +# Task packets +OP_TaskActivityComplete=0x0000 # C +OP_TaskMemberList=0x0000 # C +OP_OpenNewTasksWindow=0x0000 # C +OP_AvaliableTask=0x0000 # C Mispelled? +OP_AcceptNewTask=0x0000 # C +OP_TaskHistoryRequest=0x0000 # C +OP_TaskHistoryReply=0x0000 # C +OP_CancelTask=0x0000 # C +OP_DeclineAllTasks=0x0000 # + +# Title opcodes +OP_NewTitlesAvailable=0x0000 # C +OP_RequestTitles=0x0000 # C +OP_SendTitleList=0x0000 # C +OP_SetTitle=0x0000 # C +OP_SetTitleReply=0x0000 # C + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_IncreaseStats=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x0000 # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x0000 # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0000 # +OP_Login=0x0000 # +OP_ServerListRequest=0x0000 # +OP_PlayEverquestRequest=0x0000 # +OP_PlayEverquestResponse=0x0000 # +OP_ChatMessage=0x0000 # +OP_LoginAccepted=0x0000 # +OP_ServerListResponse=0x0000 # +OP_Poll=0x0000 # +OP_EnterChat=0x0000 # +OP_PollResponse=0x0000 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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_OPCode2511=0x2511 \ No newline at end of file diff --git a/utils/EQExtractor2/EQExtractor2/patch_Nov17-2011.conf b/utils/EQExtractor2/EQExtractor2/patch_Nov17-2011.conf new file mode 100644 index 000000000..f9ea38350 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_Nov17-2011.conf @@ -0,0 +1,607 @@ +# OPCodes that are not used by EQExtractor2 for SQL generation and have a none 0x0000 value are most likely incorrect. + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x4d5c #c +OP_ApproveWorld=0x655c #c +OP_LogServer=0x1497 #c +OP_SendCharInfo=0x1513 # +OP_ExpansionInfo=0x1771 # +OP_GuildsList=0x5b0b # +OP_EnterWorld=0x710e #c +OP_PostEnterWorld=0x7930 #c +OP_World_Client_CRC1=0x4552 # +OP_World_Client_CRC2=0x3e50 # +OP_SendSpellChecksum=0x0000 # +OP_SendSkillCapsChecksum=0x0000 # + +# Character Select Related: +OP_DeleteCharacter=0x0000 # +OP_CharacterCreateRequest=0x0000 # +OP_CharacterCreate=0x0000 # +OP_RandomNameGenerator=0x0000 # +OP_ApproveName=0x0000 # + +OP_MOTD=0x097f # +OP_SetChatServer=0x2c38 #c +OP_SetChatServer2=0x675c #c +OP_ZoneServerInfo=0x16f5 #c +OP_WorldComplete=0x3aef # +OP_WorldUnknown001=0x0000 # +OP_FloatListThing=0x45d7 # + +# Reasons for Disconnect: +OP_ZoneUnavail=0x66e4 # This is not the right opcode. Produces a 'Your character is inaccessible' message. +OP_WorldClientReady=0x3f24 # +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # +OP_ZoneEntry=0x5821 # +OP_ReqNewZone=0x1c36 # +OP_NewZone=0x4118 # +OP_ZoneSpawns=0x0000 # +OP_PlayerProfile=0x6afd #c +OP_TimeOfDay=0x6015 # +OP_LevelUpdate=0x0000 # +OP_Stamina=0x0000 # +OP_RequestClientZoneChange=0x18ea # +OP_ZoneChange=0x0000 # +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x3f55 #c +OP_TributeUpdate=0x4895 # +OP_TributeTimer=0x0000 # +OP_TaskDescription=0x0000 # +OP_TaskActivity=0x0000 # +OP_CompletedTasks=0x6d1f #c +OP_Weather=0x4658 # +OP_SendAATable=0x72f3 # +OP_UpdateAA=0x0000 # +OP_RespondAA=0x0000 # +OP_ReqClientSpawn=0x47e7 # +OP_SpawnDoor=0x0e24 # +OP_GroundSpawn=0x5c85 # +OP_SendZonepoints=0x399f # +OP_SendAAStats=0x0000 # +OP_WorldObjectsSent=0x7b73 # +OP_BlockedBuffs=0x664a # +OP_SendExpZonein=0x54e8 # +OP_SendTributes=0x0000 # +OP_TributeInfo=0x0000 # +OP_SendGuildTributes=0x0000 # +OP_AAExpUpdate=0x0000 # +OP_ExpUpdate=0x0000 # +OP_HPUpdate=0x2369 #c +OP_ManaChange=0x0000 # +OP_TGB=0x0000 # +OP_SpecialMesg=0x156c # +OP_GuildMemberList=0x51bc # +OP_GuildMOTD=0x0a1d # +OP_CharInventory=0x4849 #c +OP_WearChange=0x1a58 #c +OP_ClientUpdate=0x7062 # +OP_ClientReady=0x6cdc # +OP_SetServerFilter=0x053a #c + +# Guild Opcodes +OP_GetGuildMOTD=0x0000 # +OP_GetGuildMOTDReply=0x0000 # +OP_GuildMemberUpdate=0x0000 # +OP_GuildInvite=0x0000 # +OP_GuildRemove=0x0000 # +OP_GuildPeace=0x0000 # +OP_SetGuildMOTD=0x0000 # +OP_GuildList=0x5b0b # +OP_GuildWar=0x0000 # +OP_GuildLeader=0x0000 # +OP_GuildDelete=0x0000 # +OP_GuildInviteAccept=0x0000 # +OP_GuildDemote=0x0000 # +OP_GuildPublicNote=0x0000 # +OP_GuildManageBanker=0x0000 # +OP_GuildBank=0x0000 # +OP_SetGuildRank=0x0000 # +OP_GuildUpdateURLAndChannel=0x0000 # +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x0000 # +OP_GMBecomeNPC=0x0000 # +OP_GMZoneRequest=0x6f79 # +OP_GMZoneRequest2=0x02d6 # +OP_GMGoto=0x0000 # +OP_GMSearchCorpse=0x0000 # +OP_GMHideMe=0x0000 # +OP_GMDelCorpse=0x0000 # +OP_GMApproval=0x0000 # +OP_GMToggle=0x0000 # +OP_GMSummon=0x0000 # +OP_GMEmoteZone=0x0000 # +OP_GMEmoteWorld=0x0000 # +OP_GMFind=0x0000 # +OP_GMKick=0x0000 # +OP_GMKill=0x0000 # +OP_GMNameChange=0x0000 # +OP_GMLastName=0x0000 # + +OP_InspectAnswer=0x0000 # +OP_BeginCast=0x0d5a # +OP_BuffFadeMsg=0x50c2 # +OP_ConsentResponse=0x0000 # +OP_MemorizeSpell=0x0000 # +OP_SwapSpell=0x0000 # +OP_CastSpell=0x7286 # +OP_Consider=0x70c6 #c +OP_FormattedMessage=0x32c6 # +OP_SimpleMessage=0x0000 # C 0x0000 +OP_Buff=0x0000 # +OP_Illusion=0x4843 #c +OP_MoneyOnCorpse=0x0000 # +OP_RandomReply=0x0000 # +OP_DenyResponse=0x0000 # +OP_SkillUpdate=0x0000 # +OP_GMTrainSkillConfirm=0x0000 # +OP_RandomReq=0x0000 # +OP_Death=0x49b6 # +OP_Bind_Wound=0x0000 # +OP_GMTraining=0x0000 # +OP_GMEndTraining=0x0000 # +OP_GMTrainSkill=0x0000 # +OP_Animation=0x0b93 #c +OP_Begging=0x0000 # +OP_Consent=0x0000 # +OP_ConsentDeny=0x0000 # +OP_AutoFire=0x0000 # +OP_PetCommands=0x0000 # +OP_DeleteSpell=0x0000 # +OP_Surname=0x0000 # +OP_ClearSurname=0x0000 # +OP_FaceChange=0x0000 # +OP_SenseHeading=0x0000 # +OP_Action=0x0000 # 0x4200 +OP_ConsiderCorpse=0x0000 # +OP_HideCorpse=0x0000 # +OP_Bug=0x0000 # +OP_Feedback=0x0000 # +OP_Report=0x0000 # +OP_Damage=0x7519 # +OP_ChannelMessage=0x2e79 # +OP_Assist=0x0000 # +OP_AssistGroup=0x0000 # +OP_MoveCoin=0x0000 # +OP_ZonePlayerToBind=0x0000 # +OP_KeyRing=0x0000 # +OP_WhoAllRequest=0x0000 # +OP_WhoAllResponse=0x0000 # +OP_FriendsWho=0x0000 # +OP_ConfirmDelete=0x0000 # +OP_Logout=0x517b # +OP_Rewind=0x0000 # +OP_TargetCommand=0x0000 # +OP_InspectRequest=0x0000 # +OP_Hide=0x0000 # +OP_Jump=0x0000 # +OP_Camp=0x0000 # Was 0x1436 +OP_Emote=0x0000 # +OP_SetRunMode=0x0000 # +OP_BankerChange=0x0000 # +OP_TargetMouse=0x3edc # +OP_MobHealth=0x5cb0 #c +OP_InitialMobHealth=0x0000 # +OP_TargetHoTT=0x0000 # +OP_TargetBuffs=0x0000 # +OP_BuffCreate=0x0000 +OP_DeleteSpawn=0x58c5 #c +OP_AutoAttack=0x1df9 # +OP_AutoAttack2=0x0000 # +OP_Consume=0x0000 # +OP_MoveItem=0x0000 # +OP_DeleteItem=0x0000 # +OP_DeleteCharge=0x0000 # +OP_ItemPacket=0x3df8 # +OP_ItemLinkResponse=0x0000 # +OP_ItemLinkClick=0x0000 # +OP_NewSpawn=0x0000 # +OP_Track=0x0000 # +OP_TrackTarget=0x0000 # +OP_TrackUnknown=0x0000 # +OP_ClickDoor=0x3154 # +OP_MoveDoor=0x470e # +OP_EnvDamage=0x0000 # +OP_BoardBoat=0x0000 # +OP_Forage=0x0000 # +OP_LeaveBoat=0x0000 # +OP_ControlBoat=0x0000 # +OP_SafeFallSuccess=0x0000 # +OP_RezzComplete=0x0000 # +OP_RezzRequest=0x0000 # +OP_RezzAnswer=0x0000 # +OP_Shielding=0x0000 # +OP_RequestDuel=0x0000 # +OP_MobRename=0x0000 # +OP_AugmentItem=0x0000 # +OP_WeaponEquip1=0x7404 #c //looks like 1 & 2 use same opcode- 2nd uint32 specifies weapon 1 or 2 +OP_WeaponEquip2=0x0000 # +OP_WeaponUnequip2=0x0000 # +OP_ApplyPoison=0x0000 # +OP_Save=0x1436 # +OP_TestBuff=0x0000 # +OP_CustomTitles=0x0000 # +OP_Split=0x0000 # +OP_YellForHelp=0x0000 # +OP_LoadSpellSet=0x0000 # +OP_Bandolier=0x0000 # +OP_PotionBelt=0x0000 # +OP_DuelResponse=0x0000 # +OP_DuelResponse2=0x0000 # +OP_SaveOnZoneReq=0x2913 # +OP_ReadBook=0x0000 # +OP_Dye=0x0000 # +OP_InterruptCast=0x0000 # +OP_AAAction=0x0000 # +OP_LeadershipExpToggle=0x0000 # +OP_LeadershipExpUpdate=0x0000 # +OP_PurchaseLeadershipAA=0x0000 # +OP_UpdateLeadershipAA=0x0000 # +OP_MarkNPC=0x0000 # +OP_ClearNPCMarks=0x0000 # +OP_DoGroupLeadershipAbility=0x0000 # +OP_GroupLeadershipAAUpdate=0x1aae #c +OP_DelegateAbility=0x0000 # +OP_SetGroupTarget=0x0000 # +OP_Charm=0x0000 # +OP_Stun=0x0000 # +OP_SendFindableNPCs=0x3015 # +OP_FindPersonRequest=0x0000 # +OP_FindPersonReply=0x0000 # +OP_Sound=0x0000 # +OP_PetBuffWindow=0x0000 # +OP_LevelAppearance=0x0000 # +OP_Translocate=0x0000 # +OP_Sacrifice=0x0000 # +OP_PopupResponse=0x0000 # +OP_OnLevelMessage=0x0000 # +OP_AugmentInfo=0x0000 # +OP_Petition=0x0000 # +OP_SomeItemPacketMaybe=0x4200 # +OP_SomeItemPacketMaybe=0x0000 # +OP_PVPStats=0x0000 # +OP_PVPLeaderBoardRequest=0x0000 # +OP_PVPLeaderBoardReply=0x0000 # +OP_PVPLeaderBoardDetailsRequest=0x0000 # +OP_PVPLeaderBoardDetailsReply=0x0000 # +OP_RestState=0x0000 # +OP_RespawnWindow=0x0000 # +OP_DisciplineTimer=0x0000 # +OP_LDoNButton=0x0000 # +OP_SetStartCity=0x0000 # +OP_VoiceMacroIn=0x0000 # +OP_VoiceMacroOut=0x0000 # +OP_ItemViewUnknown=0x0000 # +OP_VetRewardsAvaliable=0x0000 # +OP_VetClaimRequest=0x0000 # +OP_VetClaimReply=0x0000 # +OP_CrystalCountUpdate=0x0000 # +OP_DisciplineUpdate=0x0000 # +OP_BecomeCorpse=0x0000 # +OP_Action2=0x0000 # +OP_MobUpdate=0x4656 # +OP_NPCMoveUpdate=0x2339 # +OP_CameraEffect=0x0000 # +OP_SpellEffect=0x0000 # + +OP_DzQuit=0x0000 +OP_DzListTimers=0x0000 +OP_DzAddPlayer=0x0000 +OP_DzRemovePlayer=0x0000 +OP_DzSwapPlayer=0x0000 +OP_DzMakeLeader=0x0000 +OP_DzPlayerList=0x0000 +OP_DzJoinExpeditionConfirm=0x0000 +OP_DzJoinExpeditionReply=0x0000 +OP_DzExpeditionInfo=0x0000 +OP_DzMemberStatus=0x0000 +OP_DzLeaderStatus=0x0000 +OP_DzExpeditionEndsWarning=0x0000 +OP_DzExpeditionList=0x0000 +OP_DzMemberList=0x0000 +OP_DzCompass=0x0000 +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # +OP_ManaUpdate=0x7c94 #c +OP_EnduranceUpdate=0x5851 #c +OP_MobManaUpdate=0x190c #c +OP_MobEnduranceUpdate=0x4d27 #c + +# Looting +OP_LootRequest=0x0000 # +OP_EndLootRequest=0x0000 # +OP_LootItem=0x0000 # +OP_LootComplete=0x0000 # + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x0000 # +OP_TraderDelItem=0x0000 # +OP_BecomeTrader=0x0000 # +OP_TraderShop=0x0000 # +OP_Trader=0x0000 # +OP_TraderBuy=0x0000 # +OP_Barter=0x0000 # +OP_ShopItem=0x0000 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # +OP_TraderItemUpdate=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x0000 # +OP_TradeAcceptClick=0x0000 # +OP_TradeRequestAck=0x0000 # +OP_TradeCoins=0x0000 # +OP_FinishTrade=0x0000 # +OP_CancelTrade=0x0000 # +OP_TradeMoneyUpdate=0x0000 # +OP_MoneyUpdate=0x0000 # +OP_TradeBusy=0x0000 # + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x0000 # +OP_FinishWindow2=0x0000 # + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x0000 # +OP_ItemVerifyReply=0x0000 # + +# merchant crap +OP_ShopPlayerSell=0x0000 # +OP_ShopRequest=0x442a # +OP_ShopEnd=0x078e # +OP_ShopEndConfirm=0x0000 # +OP_ShopPlayerBuy=0x0000 # +OP_ShopDelItem=0x0000 # + +# tradeskill stuff: +OP_ClickObject=0x0000 # +OP_ClickObjectAction=0x0000 # +OP_ClearObject=0x0000 # +OP_RecipeDetails=0x0000 # +OP_RecipesFavorite=0x0000 # +OP_RecipesSearch=0x0000 # +OP_RecipeReply=0x0000 # +OP_RecipeAutoCombine=0x0000 # +OP_TradeSkillCombine=0x0000 # + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x0000 # +OP_OpenTributeMaster=0x0000 # +OP_SelectTribute=0x0000 # +OP_TributeItem=0x0000 # +OP_TributeMoney=0x0000 # +OP_TributeToggle=0x0000 # +OP_TributePointUpdate=0x0000 # +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x0000 # +OP_AdventureFinish=0x0000 # +OP_AdventureInfoRequest=0x0000 # +OP_AdventureInfo=0x0000 # +OP_AdventureRequest=0x0000 # +OP_AdventureDetails=0x0000 # +OP_AdventureData=0x0000 # +OP_AdventureUpdate=0x0000 # +OP_AdventureMerchantRequest=0x0000 # +OP_AdventureMerchantResponse=0x0000 # +OP_AdventureMerchantPurchase=0x0000 # +OP_AdventureMerchantSell=0x0000 # +OP_AdventurePointsUpdate=0x0000 # +OP_AdventureStatsRequest=0x0000 # +OP_AdventureStatsReply=0x0000 # +OP_AdventureLeaderboardRequest=0x0000 # +OP_AdventureLeaderboardReply=0x0000 # + +# Group Opcodes +OP_GroupDisband=0x0000 # +OP_GroupInvite=0x3671 #c +OP_GroupFollow=0x0000 # +OP_GroupUpdate=0x4b82 #c +OP_GroupUpdateB=0x44c2 #c +OP_GroupCancelInvite=0x0000 # +OP_GroupAcknowledge=0x0000 # +OP_GroupDelete=0x0000 # +OP_CancelInvite=0x0000 # +OP_GroupFollow2=0x0000 # +OP_GroupInvite2=0x0000 # +OP_GroupDisbandYou=0x0000 # +OP_GroupDisbandOther=0x0000 # +OP_GroupLeaderChange=0x0000 # +OP_GroupRoles=0x6364 #c + +# LFG/LFP Opcodes +OP_LFGCommand=0x0000 # +OP_LFGGetMatchesRequest=0x0000 # +OP_LFGGetMatchesResponse=0x0000 # +OP_LFPGetMatchesRequest=0x0000 # +OP_LFPGetMatchesResponse=0x0000 # +OP_LFPCommand=0x0000 # +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x0000 # +OP_RaidUpdate=0x0c08 # +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x0000 # +OP_CombatAbility=0x0000 # +OP_SenseTraps=0x0000 # +OP_PickPocket=0x0000 # +OP_DisarmTraps=0x0000 # +OP_Disarm=0x0000 # +OP_Sneak=0x0000 # +OP_Fishing=0x0000 # +OP_InstillDoubt=0x0000 # +OP_FeignDeath=0x0000 # +OP_Mend=0x0000 # +OP_LDoNOpen=0x0000 # + +# Task packets +OP_TaskActivityComplete=0x0000 # +OP_TaskMemberList=0x0000 # +OP_OpenNewTasksWindow=0x0000 # +OP_AvaliableTask=0x0000 # +OP_AcceptNewTask=0x0000 # +OP_TaskHistoryRequest=0x0000 # +OP_TaskHistoryReply=0x0000 # +OP_CancelTask=0x0000 # +OP_DeclineAllTasks=0x0000 # + +# Title opcodes +OP_NewTitlesAvailable=0x0000 # +OP_RequestTitles=0x0000 # +OP_SendTitleList=0x0000 # +OP_SetTitle=0x0000 # +OP_SetTitleReply=0x0000 # + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_IncreaseStats=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x0000 # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x0000 # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0000 # +OP_Login=0x0000 # +OP_ServerListRequest=0x0000 # +OP_PlayEverquestRequest=0x0000 # +OP_PlayEverquestResponse=0x0000 # +OP_ChatMessage=0x0000 # +OP_LoginAccepted=0x0000 # +OP_ServerListResponse=0x0000 # +OP_Poll=0x0000 # +OP_EnterChat=0x0000 # +OP_PollResponse=0x0000 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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_MercenaryDataRequest=0x33b8 #c +OP_MercenaryDataResponse=0x0327 #c +OP_MercenaryHire=0x0000 # +OP_MercenaryAssign=0x0000 # +OP_MercenaryTimer=0x0000 # +OP_MercenaryUnknown1=0x0000 # diff --git a/utils/EQExtractor2/EQExtractor2/patch_Sep01-2010.conf b/utils/EQExtractor2/EQExtractor2/patch_Sep01-2010.conf new file mode 100644 index 000000000..c6585d394 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_Sep01-2010.conf @@ -0,0 +1,610 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x0000a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x2683 # V +OP_ApproveWorld=0x28a7 # C +OP_LogServer=0x224f # C +OP_SendCharInfo=0x2c27 # C +OP_ExpansionInfo=0x1771 # C +OP_GuildsList=0x5b0b # C +OP_EnterWorld=0x4f60 # C +OP_PostEnterWorld=0x570c # C +OP_World_Client_CRC1=0x0000 # C +OP_World_Client_CRC2=0x0000 # C +OP_SendSpellChecksum=0x0000 # C +OP_SendSkillCapsChecksum=0x0000 # C + +# Character Select Related: +OP_DeleteCharacter=0x0000 # C +OP_CharacterCreateRequest=0x0000 # C +OP_CharacterCreate=0x0000 # C +OP_RandomNameGenerator=0x0000 # C 0x0000 +OP_ApproveName=0x0000 # C + +OP_MOTD=0x7312 # C 0x0000 +OP_SetChatServer=0x1ce3 # C 0x0000 +OP_SetChatServer2=0x0000 # C 0x0000 +OP_ZoneServerInfo=0x03cc # C 0x0000 +OP_WorldComplete=0x1524 # C +OP_WorldUnknown001=0x0000 # C 0x0000 +OP_FloatListThing=0x0000 # V + +# Reasons for Disconnect: +OP_ZoneUnavail=0x0000 # C 0x0000 +OP_WorldClientReady=0x399b # C 0x0000 +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # V +OP_ZoneEntry=0x538f # V +OP_ReqNewZone=0x5ca5 # V +OP_NewZone=0x0254 # V +OP_ZoneSpawns=0x0000 # ? +OP_PlayerProfile=0x6022 # V +OP_TimeOfDay=0x6015 # V +OP_LevelUpdate=0x0000 # V +OP_Stamina=0x0000 # V +OP_RequestClientZoneChange=0x4178 # C +OP_ZoneChange=0x4a61 # C + +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x0000 # V +OP_TributeUpdate=0x7b87 # V +OP_TributeTimer=0x0000 # C +OP_TaskDescription=0x0000 # C +OP_TaskActivity=0x0000 # C +OP_CompletedTasks=0x0000 # C 0x0000 +OP_Weather=0x0ed5 # V +OP_SendAATable=0x6dc3 # V +OP_UpdateAA=0x0000 # V +OP_RespondAA=0x0000 # C 0x0000 +OP_ReqClientSpawn=0x6618 # V +OP_SpawnDoor=0x47ae # V +OP_GroundSpawn=0x5f0d # V +OP_SendZonepoints=0x0ff4 # V +OP_SendAAStats=0x0000 # C +OP_WorldObjectsSent=0x7b73 # V +OP_BlockedBuffs=0x4027 # V +OP_SendExpZonein=0x1436 # V +OP_SendTributes=0x0000 # V +OP_TributeInfo=0x0000 # V +OP_SendGuildTributes=0x0000 # C 0x0000 +OP_AAExpUpdate=0x0000 # V +OP_ExpUpdate=0x0000 # V +OP_HPUpdate=0x0000 # V +OP_ManaChange=0x0000 # C +OP_TGB=0x0000 # C +OP_SpecialMesg=0x14ff # V +OP_GuildMemberList=0x0000 # C +OP_GuildMOTD=0x0000 # V +OP_CharInventory=0x79e0 # V +OP_WearChange=0x0000 # V +OP_ClientUpdate=0x7062 # V +OP_ClientReady=0x6cdc # V +OP_SetServerFilter=0x0000 # V + +# Guild Opcodes +OP_GetGuildMOTD=0x0000 # C +OP_GetGuildMOTDReply=0x0000 # C +OP_GuildMemberUpdate=0x0000 # C +OP_GuildInvite=0x0000 # C +OP_GuildRemove=0x0000 # C +OP_GuildPeace=0x0000 # C +OP_SetGuildMOTD=0x0000 # C +OP_GuildList=0x0000 # C +OP_GuildWar=0x0000 # C +OP_GuildLeader=0x0000 # C +OP_GuildDelete=0x0000 # C +OP_GuildInviteAccept=0x0000 # C +OP_GuildDemote=0x0000 # C +OP_GuildPublicNote=0x0000 # C +OP_GuildManageBanker=0x0000 # C +OP_GuildBank=0x0000 # C +OP_SetGuildRank=0x0000 # C +OP_GuildUpdateURLAndChannel=0x0000 # C +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x0000 # C +OP_GMBecomeNPC=0x0000 # C +OP_GMZoneRequest=0x18ea # C +OP_GMZoneRequest2=0x3ad9 # C +OP_GMGoto=0x0000 # C +OP_GMSearchCorpse=0x0000 # C +OP_GMHideMe=0x0000 # C +OP_GMDelCorpse=0x0000 # C +OP_GMApproval=0x0000 # C +OP_GMToggle=0x0000 # C 0x0000 +OP_GMSummon=0x0000 # C +OP_GMEmoteZone=0x0000 # C +OP_GMEmoteWorld=0x0000 # C +OP_GMFind=0x0000 # C +OP_GMKick=0x0000 # C +OP_GMKill=0x0000 # C +OP_GMNameChange=0x0000 # C +OP_GMLastName=0x0000 # C + +OP_InspectAnswer=0x0000 # C +OP_BeginCast=0x0000 # C +OP_BuffFadeMsg=0x0000 # C +OP_ConsentResponse=0x0000 # C +OP_MemorizeSpell=0x0000 # C +OP_SwapSpell=0x0000 # C +OP_CastSpell=0x7286 # C +OP_Consider=0x0000 # C +OP_FormattedMessage=0x0000 # C +OP_SimpleMessage=0x0000 # C 0x0000 +OP_Buff=0x0000 # C +OP_Illusion=0x0000 # C +OP_MoneyOnCorpse=0x0000 # C +OP_RandomReply=0x0000 # C +OP_DenyResponse=0x0000 # C +OP_SkillUpdate=0x0000 # C +OP_GMTrainSkillConfirm=0x0000 # C +OP_RandomReq=0x0000 # C +OP_Death=0x0000 # C +OP_Bind_Wound=0x0000 # C +OP_GMTraining=0x0000 # C +OP_GMEndTraining=0x0000 # C +OP_GMTrainSkill=0x0000 # C +OP_Animation=0x0000 # Was 0x0000 +OP_Begging=0x0000 # C +OP_Consent=0x0000 # C +OP_ConsentDeny=0x0000 # C +OP_AutoFire=0x0000 # C +OP_PetCommands=0x0000 # C +OP_DeleteSpell=0x0000 # C +OP_Surname=0x0000 # C +OP_ClearSurname=0x0000 # C +OP_FaceChange=0x0000 # C +OP_SenseHeading=0x0000 # C +OP_Action=0x0000 # C +OP_ConsiderCorpse=0x0000 # C +OP_HideCorpse=0x0000 # C 0x0000 +OP_Bug=0x0000 # C +OP_Feedback=0x0000 # C +OP_Report=0x0000 # C +OP_Damage=0x0000 # C or OP_Action2? +OP_ChannelMessage=0x2e79 # C +OP_Assist=0x0000 # C +OP_AssistGroup=0x0000 # C +OP_MoveCoin=0x0000 # C +OP_ZonePlayerToBind=0x0a59 # C +OP_KeyRing=0x0000 # C +OP_WhoAllRequest=0x0000 # C +OP_WhoAllResponse=0x0000 # C +OP_FriendsWho=0x0000 # C +OP_ConfirmDelete=0x0000 # V +OP_Logout=0x64ec # C +OP_Rewind=0x0000 # C +OP_TargetCommand=0x0000 # C Was 0x0000 +OP_InspectRequest=0x0000 # C +OP_Hide=0x0000 # C +OP_Jump=0x0000 # C +OP_Camp=0x0000 # C +OP_Emote=0x0000 # C +OP_SetRunMode=0x0000 # C +OP_BankerChange=0x0000 # C +OP_TargetMouse=0x36f8 # C 0x0000 +OP_MobHealth=0x0000 # C +OP_InitialMobHealth=0x0000 # C +OP_TargetHoTT=0x0000 # C +OP_TargetBuffs=0x0000 # C +OP_BuffCreate=0x0000 # V +OP_DeleteSpawn=0x7434 # C +OP_AutoAttack=0x0000 # C +OP_AutoAttack2=0x0000 # C +OP_Consume=0x0000 # V +OP_MoveItem=0x0000 # C +OP_DeleteItem=0x0000 # C +OP_DeleteCharge=0x0000 # C +OP_ItemPacket=0x032b # C +OP_ItemLinkResponse=0x0000 # C +OP_ItemLinkClick=0x0000 # C +OP_NewSpawn=0x0000 # C +OP_Track=0x0000 # C +OP_TrackTarget=0x0000 # C +OP_TrackUnknown=0x0000 # C +OP_ClickDoor=0x4f1f # C +OP_MoveDoor=0x1516 # C +OP_EnvDamage=0x0000 # C +OP_BoardBoat=0x0000 # C +OP_Forage=0x0000 # C +OP_LeaveBoat=0x0000 # C +OP_ControlBoat=0x0000 # C +OP_SafeFallSuccess=0x0000 # C +OP_RezzComplete=0x0000 # C +OP_RezzRequest=0x0000 # C +OP_RezzAnswer=0x0000 # C +OP_Shielding=0x0000 # C +OP_RequestDuel=0x0000 # C +OP_MobRename=0x0000 # C +OP_AugmentItem=0x0000 # C +OP_WeaponEquip1=0x0000 # C +OP_WeaponEquip2=0x0000 # C +OP_WeaponUnequip2=0x0000 # C +OP_ApplyPoison=0x0000 # C +OP_Save=0x0000 # C +OP_TestBuff=0x0000 # C +OP_CustomTitles=0x0000 # C +OP_Split=0x0000 # C +OP_YellForHelp=0x0000 # C +OP_LoadSpellSet=0x0000 # C +OP_Bandolier=0x0000 # C +OP_PotionBelt=0x0000 # C +OP_DuelResponse=0x0000 # C +OP_DuelResponse2=0x0000 # C +OP_SaveOnZoneReq=0x2913 # C +OP_ReadBook=0x0000 # C +OP_Dye=0x0000 # C +OP_InterruptCast=0x0000 # C +OP_AAAction=0x0000 # C +OP_LeadershipExpToggle=0x0000 # C +OP_LeadershipExpUpdate=0x0000 # C +OP_PurchaseLeadershipAA=0x0000 # C +OP_UpdateLeadershipAA=0x0000 # C +OP_MarkNPC=0x0000 # C +OP_ClearNPCMarks=0x0000 # C +OP_DoGroupLeadershipAbility=0x0000 # C +OP_GroupLeadershipAAUpdate=0x0000 # C +OP_DelegateAbility=0x0000 # C +OP_SetGroupTarget=0x0000 # C +OP_Charm=0x0000 # C +OP_Stun=0x0000 # C +OP_SendFindableNPCs=0x162d # C +OP_FindPersonRequest=0x0000 # C +OP_FindPersonReply=0x0000 # C +OP_Sound=0x0000 # C +OP_PetBuffWindow=0x0000 # C +OP_LevelAppearance=0x0000 # C +OP_Translocate=0x0000 # C +OP_Sacrifice=0x0000 # C +OP_PopupResponse=0x0000 # C +OP_OnLevelMessage=0x0000 # C +OP_AugmentInfo=0x0000 # C +OP_Petition=0x0000 # C +OP_SomeItemPacketMaybe=0x0000 # C +OP_PVPStats=0x0000 # C +OP_PVPLeaderBoardRequest=0x0000 # C +OP_PVPLeaderBoardReply=0x0000 # C +OP_PVPLeaderBoardDetailsRequest=0x0000 # C +OP_PVPLeaderBoardDetailsReply=0x0000 # C +OP_RestState=0x0000 # C +OP_RespawnWindow=0x4e4e # C +OP_DisciplineTimer=0x0000 # C +OP_LDoNButton=0x0000 # C +OP_SetStartCity=0x0000 # C +OP_VoiceMacroIn=0x0000 # C +OP_VoiceMacroOut=0x0000 # C +OP_ItemViewUnknown=0x0000 # C +OP_VetRewardsAvaliable=0x0000 # C Mispelled? +OP_VetClaimRequest=0x0000 # C +OP_VetClaimReply=0x0000 # C +OP_CrystalCountUpdate=0x0000 # C +OP_DisciplineUpdate=0x0000 # +OP_BecomeCorpse=0x0000 # +OP_Action2=0x0000 # C OP_Damage? +OP_MobUpdate=0x4656 # Same as OP_SpawnPositionUpdate +OP_NPCMoveUpdate=0x7adb # +OP_CameraEffect=0x0000 # V +OP_SpellEffect=0x0000 # V + +OP_DzQuit=0x0000 +OP_DzListTimers=0x0000 +OP_DzAddPlayer=0x0000 +OP_DzRemovePlayer=0x0000 +OP_DzSwapPlayer=0x0000 +OP_DzMakeLeader=0x0000 +OP_DzPlayerList=0x0000 +OP_DzJoinExpeditionConfirm=0x0000 +OP_DzJoinExpeditionReply=0x0000 +OP_DzExpeditionInfo=0x0000 +OP_DzMemberStatus=0x0000 +OP_DzLeaderStatus=0x0000 +OP_DzExpeditionEndsWarning=0x0000 +OP_DzExpeditionList=0x0000 +OP_DzMemberList=0x0000 +OP_DzCompass=0x0000 +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # C +OP_ManaUpdate=0x0000 # C +OP_EnduranceUpdate=0x0000 # C +OP_MobManaUpdate=0x0000 # C +OP_MobEnduranceUpdate=0x0000 # C + +# Looting +OP_LootRequest=0x0000 # C +OP_EndLootRequest=0x0000 # C +OP_LootItem=0x0000 # C +OP_LootComplete=0x0000 # C + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x0000 # C +OP_TraderDelItem=0x0000 # C +OP_BecomeTrader=0x0000 # C +OP_TraderShop=0x0000 # C +OP_Trader=0x0000 # C +OP_TraderBuy=0x0000 # C +OP_Barter=0x0000 # C +OP_ShopItem=0x0000 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # +OP_TraderItemUpdate=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x0000 # C +OP_TradeAcceptClick=0x0000 # C +OP_TradeRequestAck=0x0000 # C +OP_TradeCoins=0x0000 # C +OP_FinishTrade=0x0000 # C +OP_CancelTrade=0x0000 # C +OP_TradeMoneyUpdate=0x0000 # C +OP_MoneyUpdate=0x0000 # C +OP_TradeBusy=0x0000 # C + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x0000 # C +OP_FinishWindow2=0x0000 # C + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x0000 # C +OP_ItemVerifyReply=0x0000 # C + +# merchant crap +OP_ShopPlayerSell=0x0000 # C +OP_ShopRequest=0x1044 # C +OP_ShopEnd=0x3753 # C +OP_ShopEndConfirm=0x0000 # C +OP_ShopPlayerBuy=0x0000 # C +OP_ShopDelItem=0x0000 # C + +# tradeskill stuff: +OP_ClickObject=0x0000 # V +OP_ClickObjectAction=0x0000 # V +OP_ClearObject=0x0000 # C +OP_RecipeDetails=0x0000 # C +OP_RecipesFavorite=0x0000 # C +OP_RecipesSearch=0x0000 # C +OP_RecipeReply=0x0000 # C +OP_RecipeAutoCombine=0x0000 # C +OP_TradeSkillCombine=0x0000 # C + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x0000 # C +OP_OpenTributeMaster=0x0000 # C +OP_SelectTribute=0x0000 # C +OP_TributeItem=0x0000 # C +OP_TributeMoney=0x0000 # C +OP_TributeToggle=0x0000 # C +OP_TributePointUpdate=0x0000 # C +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x0000 # C +OP_AdventureFinish=0x0000 # C +OP_AdventureInfoRequest=0x0000 # C +OP_AdventureInfo=0x0000 # C +OP_AdventureRequest=0x0000 # C +OP_AdventureDetails=0x0000 # C +OP_AdventureData=0x0000 # C +OP_AdventureUpdate=0x0000 # C +OP_AdventureMerchantRequest=0x0000 # C +OP_AdventureMerchantResponse=0x0000 # C +OP_AdventureMerchantPurchase=0x0000 # C +OP_AdventureMerchantSell=0x0000 # C +OP_AdventurePointsUpdate=0x0000 # C +OP_AdventureStatsRequest=0x0000 # C +OP_AdventureStatsReply=0x0000 # C +OP_AdventureLeaderboardRequest=0x0000 # C +OP_AdventureLeaderboardReply=0x0000 # C + +# Group Opcodes +OP_GroupDisband=0x0000 # C +OP_GroupInvite=0x0000 # C +OP_GroupFollow=0x0000 # C +OP_GroupUpdate=0x0000 # C +OP_GroupUpdateB=0x0000 # C +OP_GroupCancelInvite=0x0000 # C - Same as OP_CancelInvite? +OP_GroupAcknowledge=0x0000 # C +OP_GroupDelete=0x0000 # +OP_CancelInvite=0x0000 # C +OP_GroupFollow2=0x0000 # C +OP_GroupInvite2=0x0000 # C +OP_GroupDisbandYou=0x0000 # C +OP_GroupDisbandOther=0x0000 # C +OP_GroupLeaderChange=0x0000 # C +OP_GroupRoles=0x0000 # C + +# LFG/LFP Opcodes +OP_LFGCommand=0x0000 # C +OP_LFGGetMatchesRequest=0x0000 # C +OP_LFGGetMatchesResponse=0x0000 # C +OP_LFPGetMatchesRequest=0x0000 # C +OP_LFPGetMatchesResponse=0x0000 # C +OP_LFPCommand=0x0000 # C +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x0000 # C +OP_RaidUpdate=0x0000 # C +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x0000 # C +OP_CombatAbility=0x0000 # C +OP_SenseTraps=0x0000 # C +OP_PickPocket=0x0000 # C +OP_DisarmTraps=0x0000 # +OP_Disarm=0x0000 # C +OP_Sneak=0x0000 # C +OP_Fishing=0x0000 # C +OP_InstillDoubt=0x0000 # C +OP_FeignDeath=0x0000 # C +OP_Mend=0x0000 # C +OP_LDoNOpen=0x0000 # C + +# Task packets +OP_TaskActivityComplete=0x0000 # C +OP_TaskMemberList=0x0000 # C +OP_OpenNewTasksWindow=0x0000 # C +OP_AvaliableTask=0x0000 # C Mispelled? +OP_AcceptNewTask=0x0000 # C +OP_TaskHistoryRequest=0x0000 # C +OP_TaskHistoryReply=0x0000 # C +OP_CancelTask=0x0000 # C +OP_DeclineAllTasks=0x0000 # + +# Title opcodes +OP_NewTitlesAvailable=0x0000 # C +OP_RequestTitles=0x0000 # C +OP_SendTitleList=0x0000 # C +OP_SetTitle=0x0000 # C +OP_SetTitleReply=0x0000 # C + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_IncreaseStats=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x0000 # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x0000 # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0000 # +OP_Login=0x0000 # +OP_ServerListRequest=0x0000 # +OP_PlayEverquestRequest=0x0000 # +OP_PlayEverquestResponse=0x0000 # +OP_ChatMessage=0x0000 # +OP_LoginAccepted=0x0000 # +OP_ServerListResponse=0x0000 # +OP_Poll=0x0000 # +OP_EnterChat=0x0000 # +OP_PollResponse=0x0000 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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 # diff --git a/utils/EQExtractor2/EQExtractor2/patch_Sep22-2010.conf b/utils/EQExtractor2/EQExtractor2/patch_Sep22-2010.conf new file mode 100644 index 000000000..09188221d --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_Sep22-2010.conf @@ -0,0 +1,612 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x0000a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x6893 # was 0x2683 +OP_ApproveWorld=0x115a # was 0x28a7 +OP_LogServer=0x701f # was 0x224f +OP_SendCharInfo=0x1b85 # was 0x2c27 +OP_ExpansionInfo=0x1771 # Same +OP_GuildsList=0x5b0b # Same +OP_EnterWorld=0x3288 # was 0x4f60 +OP_PostEnterWorld=0x2f2c # was 0x570c +OP_World_Client_CRC1=0x0000 # C +OP_World_Client_CRC2=0x0000 # C +OP_SendSpellChecksum=0x0000 # C +OP_SendSkillCapsChecksum=0x0000 # C + +# Character Select Related: +OP_DeleteCharacter=0x0000 # C +OP_CharacterCreateRequest=0x0000 # C +OP_CharacterCreate=0x0000 # C +OP_RandomNameGenerator=0x0000 # C 0x0000 +OP_ApproveName=0x0000 # C + +OP_MOTD=0x7312 # C 0x0000 +OP_SetChatServer=0x37db # was 0x1ce3 +OP_SetChatServer2=0x0000 # C 0x0000 +OP_ZoneServerInfo=0x0479 # was 0x03cc +OP_WorldComplete=0x72f3 # was 0x1524 +OP_WorldUnknown001=0x0000 # C 0x0000 +OP_FloatListThing=0x0000 # V + +# Reasons for Disconnect: +OP_ZoneUnavail=0x0000 # C 0x0000 +OP_WorldClientReady=0x399b # C 0x0000 +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # was 0x3594 +OP_ZoneEntry=0x002b # was 0x538f +OP_ReqNewZone=0x5ca5 # was 0x5ca5 +OP_NewZone=0x0254 # was 0x0254 +OP_ZoneSpawns=0x0000 # ? +OP_PlayerProfile=0x6022 # was 0x6022 +OP_TimeOfDay=0x6015 # was 0x6015 +OP_LevelUpdate=0x0000 # V +OP_Stamina=0x0000 # V +OP_RequestClientZoneChange=0x3fd2 # was 0x4178 +OP_ZoneChange=0x0b93 # was 0x4a61 + +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x7b64 # V +OP_TributeUpdate=0x0c98 +OP_TributeTimer=0x0000 # C +OP_TaskDescription=0x0000 # C +OP_TaskActivity=0x0000 # C +OP_CompletedTasks=0x0000 # C 0x0000 +OP_Weather=0x7ce4 # was 0x0ed5 +OP_SendAATable=0x1bd4 # was 0x6dc3 +OP_UpdateAA=0x0000 # V +OP_RespondAA=0x0000 # C 0x0000 +OP_ReqClientSpawn=0x54e8 # was 0x6618 +OP_SpawnDoor=0x465e # was 0x47ae Need an Encode +OP_GroundSpawn=0x5f0d # was 0x5f0d +OP_SendZonepoints=0x5f51 # was 0x0ff4 +OP_SendAAStats=0x0000 # C +OP_WorldObjectsSent=0x7b73 # was 0x7b73 +OP_BlockedBuffs=0x664a # was 0x4027 +OP_SendExpZonein=0x2c27 # was 0x1436 +OP_SendTributes=0x0000 # V +OP_TributeInfo=0x0000 # V +OP_SendGuildTributes=0x0000 # C 0x0000 +OP_AAExpUpdate=0x0000 # V +OP_ExpUpdate=0x0000 # V +OP_HPUpdate=0x0000 # V +OP_ManaChange=0x0000 # C +OP_TGB=0x0000 # C +OP_SpecialMesg=0x3bf5 # was 0x14ff +OP_GuildMemberList=0x0000 # C +OP_GuildMOTD=0x0000 # V +OP_CharInventory=0x5dbc # was 0x79e0 +OP_WearChange=0x0000 # V +OP_ClientUpdate=0x7062 # was 0x7062 +OP_ClientReady=0x6cdc # was 0x6cdc +OP_SetServerFilter=0x0000 # V + +# Guild Opcodes +OP_GetGuildMOTD=0x0000 # C +OP_GetGuildMOTDReply=0x0000 # C +OP_GuildMemberUpdate=0x0000 # C +OP_GuildInvite=0x0000 # C +OP_GuildRemove=0x0000 # C +OP_GuildPeace=0x0000 # C +OP_SetGuildMOTD=0x0000 # C +OP_GuildList=0x0000 # C +OP_GuildWar=0x0000 # C +OP_GuildLeader=0x0000 # C +OP_GuildDelete=0x0000 # C +OP_GuildInviteAccept=0x0000 # C +OP_GuildDemote=0x0000 # C +OP_GuildPublicNote=0x0000 # C +OP_GuildManageBanker=0x0000 # C +OP_GuildBank=0x0000 # C +OP_SetGuildRank=0x0000 # C +OP_GuildUpdateURLAndChannel=0x0000 # C +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x0000 # C +OP_GMBecomeNPC=0x0000 # C +OP_GMZoneRequest=0x6f79 # was 0x18ea +OP_GMZoneRequest2=0x02d6 # was 0x3ad9 +OP_GMGoto=0x0000 # C +OP_GMSearchCorpse=0x0000 # C +OP_GMHideMe=0x0000 # C +OP_GMDelCorpse=0x0000 # C +OP_GMApproval=0x0000 # C +OP_GMToggle=0x0000 # C 0x0000 +OP_GMSummon=0x0000 # C +OP_GMEmoteZone=0x0000 # C +OP_GMEmoteWorld=0x0000 # C +OP_GMFind=0x0000 # C +OP_GMKick=0x0000 # C +OP_GMKill=0x0000 # C +OP_GMNameChange=0x0000 # C +OP_GMLastName=0x0000 # C + +OP_InspectAnswer=0x0000 # C +OP_BeginCast=0x0000 # C +OP_BuffFadeMsg=0x0000 # C +OP_ConsentResponse=0x0000 # C +OP_MemorizeSpell=0x0000 # C +OP_SwapSpell=0x0000 # C +OP_CastSpell=0x0000 # C +OP_Consider=0x0000 # C +OP_FormattedMessage=0x0000 # C +OP_SimpleMessage=0x0000 # C 0x0000 +OP_Buff=0x0000 # C +OP_Illusion=0x0000 # C +OP_MoneyOnCorpse=0x0000 # C +OP_RandomReply=0x0000 # C +OP_DenyResponse=0x0000 # C +OP_SkillUpdate=0x0000 # C +OP_GMTrainSkillConfirm=0x0000 # C +OP_RandomReq=0x0000 # C +OP_Death=0x0000 # C +OP_Bind_Wound=0x0000 # C +OP_GMTraining=0x0000 # C +OP_GMEndTraining=0x0000 # C +OP_GMTrainSkill=0x0000 # C +OP_Animation=0x0000 # Was 0x0000 +OP_Begging=0x0000 # C +OP_Consent=0x0000 # C +OP_ConsentDeny=0x0000 # C +OP_AutoFire=0x0000 # C +OP_PetCommands=0x0000 # C +OP_DeleteSpell=0x0000 # C +OP_Surname=0x0000 # C +OP_ClearSurname=0x0000 # C +OP_FaceChange=0x0000 # C +OP_SenseHeading=0x0000 # C +OP_Action=0x0000 # C +OP_ConsiderCorpse=0x0000 # C +OP_HideCorpse=0x0000 # C 0x0000 +OP_Bug=0x0000 # C +OP_Feedback=0x0000 # C +OP_Report=0x0000 # C +OP_Damage=0x0000 # C or OP_Action2? +OP_ChannelMessage=0x2e79 # was 0x2e79 +OP_Assist=0x0000 # C +OP_AssistGroup=0x0000 # C +OP_MoveCoin=0x0000 # C +OP_ZonePlayerToBind=0x0000 # C +OP_KeyRing=0x0000 # C +OP_WhoAllRequest=0x0000 # C +OP_WhoAllResponse=0x0000 # C +OP_FriendsWho=0x0000 # C +OP_ConfirmDelete=0x0000 # V +OP_Logout=0x44ae # was 0x64ec +OP_Rewind=0x0000 # C +OP_TargetCommand=0x0000 # C Was 0x0000 +OP_InspectRequest=0x0000 # C +OP_Hide=0x0000 # C +OP_Jump=0x0000 # C +OP_Camp=0x42ef # C +OP_Emote=0x0000 # C +OP_SetRunMode=0x0000 # C +OP_BankerChange=0x0000 # C +OP_TargetMouse=0x36f8 # C 0x0000 +OP_MobHealth=0x0000 # C +OP_InitialMobHealth=0x0000 # C +OP_TargetHoTT=0x0000 # C +OP_TargetBuffs=0x0000 # C +OP_BuffCreate=0x0000 # V +OP_DeleteSpawn=0x7434 # was 0x7434 +OP_AutoAttack=0x0000 # C +OP_AutoAttack2=0x0000 # C +OP_Consume=0x0000 # V +OP_MoveItem=0x0000 # C +OP_DeleteItem=0x0000 # C +OP_DeleteCharge=0x0000 # C +OP_ItemPacket=0x6a30 # was 0x032b +OP_ItemLinkResponse=0x0000 # C +OP_ItemLinkClick=0x0000 # C +OP_NewSpawn=0x0000 # C +OP_Track=0x0000 # C +OP_TrackTarget=0x0000 # C +OP_TrackUnknown=0x0000 # C +OP_ClickDoor=0x470e # was 0x4f1f +OP_MoveDoor=0x48f9 # was 0x1516 +OP_EnvDamage=0x0000 # C +OP_BoardBoat=0x0000 # C +OP_Forage=0x0000 # C +OP_LeaveBoat=0x0000 # C +OP_ControlBoat=0x0000 # C +OP_SafeFallSuccess=0x0000 # C +OP_RezzComplete=0x0000 # C +OP_RezzRequest=0x0000 # C +OP_RezzAnswer=0x0000 # C +OP_Shielding=0x0000 # C +OP_RequestDuel=0x0000 # C +OP_MobRename=0x0000 # C +OP_AugmentItem=0x0000 # C +OP_WeaponEquip1=0x0000 # C +OP_WeaponEquip2=0x0000 # C +OP_WeaponUnequip2=0x0000 # C +OP_ApplyPoison=0x0000 # C +OP_Save=0x0000 # C +OP_TestBuff=0x0000 # C +OP_CustomTitles=0x0000 # C +OP_Split=0x0000 # C +OP_YellForHelp=0x0000 # C +OP_LoadSpellSet=0x0000 # C +OP_Bandolier=0x0000 # C +OP_PotionBelt=0x0000 # C +OP_DuelResponse=0x0000 # C +OP_DuelResponse2=0x0000 # C +OP_SaveOnZoneReq=0x2913 # was 0x2913 +OP_ReadBook=0x0000 # C +OP_Dye=0x0000 # C +OP_InterruptCast=0x0000 # C +OP_AAAction=0x0000 # C +OP_LeadershipExpToggle=0x0000 # C +OP_LeadershipExpUpdate=0x0000 # C +OP_PurchaseLeadershipAA=0x0000 # C +OP_UpdateLeadershipAA=0x0000 # C +OP_MarkNPC=0x0000 # C +OP_ClearNPCMarks=0x0000 # C +OP_DoGroupLeadershipAbility=0x0000 # C +OP_GroupLeadershipAAUpdate=0x0000 # C +OP_DelegateAbility=0x0000 # C +OP_SetGroupTarget=0x0000 # C +OP_Charm=0x0000 # C +OP_Stun=0x0000 # C +OP_SendFindableNPCs=0x6b67 # was 0x162d +OP_FindPersonRequest=0x0000 # C +OP_FindPersonReply=0x0000 # C +OP_Sound=0x0000 # C +OP_PetBuffWindow=0x0000 # C +OP_LevelAppearance=0x0000 # C +OP_Translocate=0x0000 # C +OP_Sacrifice=0x0000 # C +OP_PopupResponse=0x0000 # C +OP_OnLevelMessage=0x0000 # C +OP_AugmentInfo=0x0000 # C +OP_Petition=0x0000 # C +OP_SomeItemPacketMaybe=0x0000 # C +OP_PVPStats=0x0000 # C +OP_PVPLeaderBoardRequest=0x0000 # C +OP_PVPLeaderBoardReply=0x0000 # C +OP_PVPLeaderBoardDetailsRequest=0x0000 # C +OP_PVPLeaderBoardDetailsReply=0x0000 # C +OP_RestState=0x0000 # C +OP_RespawnWindow=0x0000 # C +OP_DisciplineTimer=0x0000 # C +OP_LDoNButton=0x0000 # C +OP_SetStartCity=0x0000 # C +OP_VoiceMacroIn=0x0000 # C +OP_VoiceMacroOut=0x0000 # C +OP_ItemViewUnknown=0x0000 # C +OP_VetRewardsAvaliable=0x0000 # C Mispelled? +OP_VetClaimRequest=0x0000 # C +OP_VetClaimReply=0x0000 # C +OP_CrystalCountUpdate=0x0000 # C +OP_DisciplineUpdate=0x0000 # +OP_BecomeCorpse=0x0000 # +OP_Action2=0x0000 # C OP_Damage? +OP_MobUpdate=0x4656 # was 0x4656 +OP_NPCMoveUpdate=0x38f9 # was 0x7adb +OP_CameraEffect=0x0000 # V +OP_SpellEffect=0x0000 # V + +OP_DzQuit=0x0000 +OP_DzListTimers=0x0000 +OP_DzAddPlayer=0x0000 +OP_DzRemovePlayer=0x0000 +OP_DzSwapPlayer=0x0000 +OP_DzMakeLeader=0x0000 +OP_DzPlayerList=0x0000 +OP_DzJoinExpeditionConfirm=0x0000 +OP_DzJoinExpeditionReply=0x0000 +OP_DzExpeditionInfo=0x0000 +OP_DzMemberStatus=0x0000 +OP_DzLeaderStatus=0x0000 +OP_DzExpeditionEndsWarning=0x0000 +OP_DzExpeditionList=0x0000 +OP_DzMemberList=0x0000 +OP_DzCompass=0x0000 +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # C +OP_ManaUpdate=0x0000 # C +OP_EnduranceUpdate=0x0000 # C +OP_MobManaUpdate=0x0000 # C +OP_MobEnduranceUpdate=0x0000 # C + +# Looting +OP_LootRequest=0x0000 # C +OP_EndLootRequest=0x0000 # C +OP_LootItem=0x0000 # C +OP_LootComplete=0x0000 # C + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x0000 # C +OP_TraderDelItem=0x0000 # C +OP_BecomeTrader=0x0000 # C +OP_TraderShop=0x0000 # C +OP_Trader=0x0000 # C +OP_TraderBuy=0x0000 # C +OP_Barter=0x0000 # C +OP_ShopItem=0x0000 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # +OP_TraderItemUpdate=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x0000 # C +OP_TradeAcceptClick=0x0000 # C +OP_TradeRequestAck=0x0000 # C +OP_TradeCoins=0x0000 # C +OP_FinishTrade=0x0000 # C +OP_CancelTrade=0x0000 # C +OP_TradeMoneyUpdate=0x0000 # C +OP_MoneyUpdate=0x0000 # C +OP_TradeBusy=0x0000 # C + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x0000 # C +OP_FinishWindow2=0x0000 # C + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x0000 # C +OP_ItemVerifyReply=0x0000 # C + +# merchant crap +OP_ShopPlayerSell=0x0000 # C +OP_ShopRequest=0x1044 # was 0x1044 +OP_ShopEnd=0x3753 # was 0x3753 +OP_ShopEndConfirm=0x0000 # C +OP_ShopPlayerBuy=0x0000 # C +OP_ShopDelItem=0x0000 # C + +OP_MercenaryPurchaseWindow=0x239d + +# tradeskill stuff: +OP_ClickObject=0x0000 # V +OP_ClickObjectAction=0x0000 # V +OP_ClearObject=0x0000 # C +OP_RecipeDetails=0x0000 # C +OP_RecipesFavorite=0x0000 # C +OP_RecipesSearch=0x0000 # C +OP_RecipeReply=0x0000 # C +OP_RecipeAutoCombine=0x0000 # C +OP_TradeSkillCombine=0x0000 # C + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x0000 # C +OP_OpenTributeMaster=0x0000 # C +OP_SelectTribute=0x0000 # C +OP_TributeItem=0x0000 # C +OP_TributeMoney=0x0000 # C +OP_TributeToggle=0x0000 # C +OP_TributePointUpdate=0x0000 # C +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x0000 # C +OP_AdventureFinish=0x0000 # C +OP_AdventureInfoRequest=0x0000 # C +OP_AdventureInfo=0x0000 # C +OP_AdventureRequest=0x0000 # C +OP_AdventureDetails=0x0000 # C +OP_AdventureData=0x0000 # C +OP_AdventureUpdate=0x0000 # C +OP_AdventureMerchantRequest=0x0000 # C +OP_AdventureMerchantResponse=0x0000 # C +OP_AdventureMerchantPurchase=0x0000 # C +OP_AdventureMerchantSell=0x0000 # C +OP_AdventurePointsUpdate=0x0000 # C +OP_AdventureStatsRequest=0x0000 # C +OP_AdventureStatsReply=0x0000 # C +OP_AdventureLeaderboardRequest=0x0000 # C +OP_AdventureLeaderboardReply=0x0000 # C + +# Group Opcodes +OP_GroupDisband=0x0000 # C +OP_GroupInvite=0x0000 # C +OP_GroupFollow=0x0000 # C +OP_GroupUpdate=0x0000 # C +OP_GroupUpdateB=0x0000 # C +OP_GroupCancelInvite=0x0000 # C - Same as OP_CancelInvite? +OP_GroupAcknowledge=0x0000 # C +OP_GroupDelete=0x0000 # +OP_CancelInvite=0x0000 # C +OP_GroupFollow2=0x0000 # C +OP_GroupInvite2=0x0000 # C +OP_GroupDisbandYou=0x0000 # C +OP_GroupDisbandOther=0x0000 # C +OP_GroupLeaderChange=0x0000 # C +OP_GroupRoles=0x0000 # C + +# LFG/LFP Opcodes +OP_LFGCommand=0x0000 # C +OP_LFGGetMatchesRequest=0x0000 # C +OP_LFGGetMatchesResponse=0x0000 # C +OP_LFPGetMatchesRequest=0x0000 # C +OP_LFPGetMatchesResponse=0x0000 # C +OP_LFPCommand=0x0000 # C +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x0000 # C +OP_RaidUpdate=0x0000 # C +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x0000 # C +OP_CombatAbility=0x0000 # C +OP_SenseTraps=0x0000 # C +OP_PickPocket=0x0000 # C +OP_DisarmTraps=0x0000 # +OP_Disarm=0x0000 # C +OP_Sneak=0x0000 # C +OP_Fishing=0x0000 # C +OP_InstillDoubt=0x0000 # C +OP_FeignDeath=0x0000 # C +OP_Mend=0x0000 # C +OP_LDoNOpen=0x0000 # C + +# Task packets +OP_TaskActivityComplete=0x0000 # C +OP_TaskMemberList=0x0000 # C +OP_OpenNewTasksWindow=0x0000 # C +OP_AvaliableTask=0x0000 # C Mispelled? +OP_AcceptNewTask=0x0000 # C +OP_TaskHistoryRequest=0x0000 # C +OP_TaskHistoryReply=0x0000 # C +OP_CancelTask=0x0000 # C +OP_DeclineAllTasks=0x0000 # + +# Title opcodes +OP_NewTitlesAvailable=0x0000 # C +OP_RequestTitles=0x0000 # C +OP_SendTitleList=0x0000 # C +OP_SetTitle=0x0000 # C +OP_SetTitleReply=0x0000 # C + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_IncreaseStats=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x0000 # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x0000 # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0000 # +OP_Login=0x0000 # +OP_ServerListRequest=0x0000 # +OP_PlayEverquestRequest=0x0000 # +OP_PlayEverquestResponse=0x0000 # +OP_ChatMessage=0x0000 # +OP_LoginAccepted=0x0000 # +OP_ServerListResponse=0x0000 # +OP_Poll=0x0000 # +OP_EnterChat=0x0000 # +OP_PollResponse=0x0000 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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 # diff --git a/utils/EQExtractor2/EQExtractor2/patch_SoD.conf b/utils/EQExtractor2/EQExtractor2/patch_SoD.conf new file mode 100644 index 000000000..c428c0a76 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_SoD.conf @@ -0,0 +1,609 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x0ff4 # C +OP_ApproveWorld=0x701f # C +OP_LogServer=0x4762 # C +OP_SendCharInfo=0x0f14 # C +OP_ExpansionInfo=0x7519 # C +OP_GuildsList=0x5b0b # C +OP_EnterWorld=0x1c20 # C +OP_PostEnterWorld=0x7c94 # C +OP_World_Client_CRC1=0x0ca5 # C +OP_World_Client_CRC2=0x1cb3 # C +OP_SendSpellChecksum=0x5bad # C +OP_SendSkillCapsChecksum=0x5d24 # C + +# Character Select Related: +OP_DeleteCharacter=0x0254 # C +OP_CharacterCreateRequest=0x4463 # C +OP_CharacterCreate=0x1513 # C +OP_RandomNameGenerator=0x3604 # C +OP_ApproveName=0x413f # C + +OP_MOTD=0x192e # C +OP_SetChatServer=0x7f2b # C +OP_SetChatServer2=0x11f3 # C +OP_ZoneServerInfo=0x5da4 # C +OP_WorldComplete=0x37db # C +OP_WorldUnknown001=0x2c4c # C +OP_FloatListThing=0x10b5 # C + +# Reasons for Disconnect: +OP_ZoneUnavail=0x7930 # C +OP_WorldClientReady=0x1a84 # C +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # C +OP_ZoneEntry=0x5a6b # C +OP_ReqNewZone=0x43ac # C +OP_NewZone=0x5ca5 # C +OP_ZoneSpawns=0x72f8 # C +OP_PlayerProfile=0x6022 # C +OP_TimeOfDay=0x6015 # C +OP_LevelUpdate=0x6a99 # C +OP_Stamina=0x3e50 # C +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # + +OP_RequestClientZoneChange=0x4885 # C +OP_ZoneChange=0x051b # C + +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x10b7 # C +OP_TributeUpdate=0x399b # C +OP_TributeTimer=0x4423 # C +OP_TaskDescription=0x7b77 # C +OP_TaskActivity=0x0525 # C +OP_CompletedTasks=0x5412 # C +OP_Weather=0x2641 # C +OP_SendAATable=0x322f # C +OP_UpdateAA=0x466c # C +OP_RespondAA=0x0142 # C +OP_ReqClientSpawn=0x1436 # C +OP_SpawnDoor=0x102f # C +OP_GroundSpawn=0x33e5 # C +OP_SendZonepoints=0x5821 # C +OP_SendAAStats=0x57a3 # C +OP_WorldObjectsSent=0x7b73 # C +OP_BlockedBuffs=0x0baa # C +OP_SendExpZonein=0x69cd # C +OP_SendTributes=0x4786 # C +OP_TributeInfo=0x4cc6 # C +OP_SendGuildTributes=0x26c4 # C +OP_AAExpUpdate=0x3088 # C +OP_ExpUpdate=0x0e98 # C +OP_HPUpdate=0x538f # C +OP_ManaChange=0x50c2 # C +OP_TGB=0x2d74 # C +OP_SpecialMesg=0x074f # C +OP_GuildMemberList=0x51bc # C +OP_GuildMOTD=0xd677 # C +OP_CharInventory=0x709d # C +OP_WearChange=0x231f # C +OP_ClientUpdate=0x7062 # C +OP_ClientReady=0x6759 # C +OP_SetServerFilter=0x7312 # C + +# Guild Opcodes +OP_GetGuildMOTD=0x189e # C +OP_GetGuildMOTDReply=0x7a16 # C +OP_GuildMemberUpdate=0x41c0 # C +OP_GuildInvite=0x4843 # C +OP_GuildRemove=0x2b9d # C +OP_GuildPeace=0x3f21 # C +OP_SetGuildMOTD=0x481f # C +OP_GuildList=0x5b0b # C +OP_GuildWar=0x7bfb # C +OP_GuildLeader=0x1055 # C +OP_GuildDelete=0x75d0 # C +OP_GuildInviteAccept=0x5f25 # C +OP_GuildDemote=0x15b2 # C +OP_GuildPublicNote=0x2dbd # C +OP_GuildManageBanker=0x11bc # C +OP_GuildBank=0x0fce # C +OP_SetGuildRank=0x4ffe # C +OP_GuildUpdateURLAndChannel=0x5422 +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x6989 # C +OP_GMBecomeNPC=0x56e7 # C +OP_GMZoneRequest=0x4578 # C +OP_GMZoneRequest2=0x1912 # C +OP_GMGoto=0x0402 # C +OP_GMSearchCorpse=0x7872 # C +OP_GMHideMe=0x34a1 # C +OP_GMDelCorpse=0x0dda # C +OP_GMApproval=0x72fa # C +OP_GMToggle=0x7566 # C +OP_GMSummon=0x596d # C +OP_GMEmoteZone=0x3e7c # C +OP_GMEmoteWorld=0x3e7c # C +OP_GMFind=0x6e27 # C +OP_GMKick=0x799c # C +OP_GMKill=0x6685 # C +OP_GMNameChange=0x565d # C +OP_GMLastName=0x3563 # C + +OP_InspectAnswer=0x4938 # C +OP_Action2=0x7e4d # C OP_Damage? +OP_BeginCast=0x0d5a # C +OP_BuffFadeMsg=0x569a # C +OP_ConsentResponse=0x6e47 # C +OP_MemorizeSpell=0x8543 # C +OP_SwapSpell=0x3fd2 # C +OP_CastSpell=0x3582 # C +OP_Consider=0x6024 # C +OP_FormattedMessage=0x1318 # C +OP_SimpleMessage=0x5448 # C +OP_Buff=0x7ea8 # C +OP_Illusion=0x48f9 # C +OP_MoneyOnCorpse=0x6546 # C +OP_RandomReply=0x6cdc # C +OP_DenyResponse=0x7ce7 # C +OP_SkillUpdate=0x7f01 # C +OP_GMTrainSkillConfirm=0x7ae1 # C +OP_RandomReq=0x777c # C +OP_Death=0x1b85 # C +OP_Bind_Wound=0x3969 # C +OP_GMTraining=0x7b64 # C +OP_GMEndTraining=0x3c02 # C +OP_GMTrainSkill=0x7686 # C +OP_Animation=0x47d3 # C +OP_Begging=0x6d37 # C +OP_Consent=0x6bb9 # C +OP_ConsentDeny=0x01e3 # C +OP_AutoFire=0x1bd4 # C +OP_PetCommands=0x3ad6 # C +OP_DeleteSpell=0x0736 # C +OP_Surname=0x7547 # C +OP_ClearSurname=0x2edd # C +OP_FaceChange=0x5658 # C +OP_SenseHeading=0x3887 # C +OP_Action=0x2c27 # C +OP_ConsiderCorpse=0x37a7 # C +OP_HideCorpse=0x1842 +OP_Bug=0x02b # C +OP_Feedback=0x0d1d # C +OP_Report=0x6884 # C +OP_Damage=0x7e4d # C or OP_Action2? +OP_ChannelMessage=0x2e79 # C +OP_Assist=0x2b59 # C +OP_AssistGroup=0x5af6 # C +OP_MoveCoin=0x1fcd # C +OP_ZonePlayerToBind=0x1d3f # C +OP_KeyRing=0x0a18 # C +OP_WhoAllRequest=0x177a # C +OP_WhoAllResponse=0x31f3 # C +OP_FriendsWho=0x6a90 # C +OP_ConfirmDelete=0x3d00 # C +OP_Logout=0x66e0 # C +OP_Rewind=0x4b7b # C +OP_TargetCommand=0x17f7 # C +OP_InspectRequest=0x4892 # C +OP_Hide=0x1d22 # C +OP_Jump=0x6c16 # C +OP_Camp=0x3eec # C +OP_Emote=0x7434 # C +OP_SetRunMode=0x07fb # C +OP_BankerChange=0x21e9 # C +OP_TargetMouse=0x7bbb # C +OP_MobHealth=0x47ea # C +OP_InitialMobHealth=0x2d25 # C +OP_TargetHoTT=0x3ec7 # C +OP_TargetBuffs=0x3df8 +OP_DeleteSpawn=0x3164 # C +OP_AutoAttack=0x3d86 # C +OP_AutoAttack2=0x4ca1 # C +OP_Consume=0x7ce4 # C +OP_MoveItem=0x7f56 # C +OP_DeleteItem=0x36f8 # C +OP_DeleteCharge=0x1df9 # C +OP_ItemPacket=0x34f8 # C +OP_ItemLinkResponse=0x2bad # C +OP_ItemLinkClick=0x3c66 # C +OP_NewSpawn=0x5c29 # C +OP_Track=0x4817 # C +OP_TrackTarget=0x7337 # C +OP_TrackUnknown=0x7b56 # C +OP_ClickDoor=0x1516 # C +OP_MoveDoor=0x6e97 # C +OP_EnvDamage=0x5ac4 # C +OP_BoardBoat=0x5cd3 # C +OP_Forage=0x7b05 # C +OP_LeaveBoat=0x7554 # C +OP_ControlBoat=0x5bd9 # C +OP_SafeFallSuccess=0x6ad2 # C +OP_RezzComplete=0x42d0 # C +OP_RezzRequest=0x0976 # C +OP_RezzAnswer=0x6e1c # C +OP_Shielding=0x2f6a # C +OP_RequestDuel=0x79e0 # C +OP_MobRename=0x0a1d # C +OP_AugmentItem=0x0370 # C +OP_WeaponEquip1=0x719e # C +OP_WeaponEquip2=0x7b6e # C +OP_WeaponUnequip2=0x19a8 # C +OP_ApplyPoison=0x405b # C +OP_Save=0x5c85 # C +OP_TestBuff=0x7309 +OP_DestructableRelated=0x5ea1 +OP_CustomTitles=0x1b26 # C +OP_Split=0x53f9 # C +OP_YellForHelp=0x6f79 # C +OP_LoadSpellSet=0x7113 # C +OP_Bandolier=0x441c # C +OP_PotionBelt=0x5db5 # C +OP_DuelResponse=0x1ebb # C +OP_SaveOnZoneReq=0x6eff # C +OP_ReadBook=0xa53e +OP_Dye=0x3672 # C +OP_InterruptCast=0x072f # C +OP_AAAction=0x50d0 # C +OP_LeadershipExpToggle=0x34c5 # C +OP_LeadershipExpUpdate=0x69d0 # C +OP_PurchaseLeadershipAA=0x07b3 # C +OP_UpdateLeadershipAA=0x6948 # C +OP_MarkNPC=0x0d4b # C +OP_ClearNPCMarks=0x5033 # C +OP_DoGroupLeadershipAbility=0x540b # C +OP_GroupLeadershipAAUpdate=0x0c33 +OP_DelegateAbility=0x0322 # C +OP_SetGroupTarget=0x521c # C +OP_DuelResponse2=0x52b5 # C +OP_Charm=0x7108 # C +OP_Stun=0x2a6d # C +OP_SendFindableNPCs=0x5360 +OP_FindPersonRequest=0x3168 # C +OP_FindPersonReply=0x1ac8 # C +OP_Sound=0x303e # C +OP_PetBuffWindow=0x2dd3 # C +OP_LevelAppearance=0x6dc3 # C +OP_Translocate=0x2042 # C +OP_Sacrifice=0x5805 # C +OP_PopupResponse=0x2caf # C +OP_OnLevelMessage=0x1154 # C +OP_AugmentInfo=0x3683 # C +OP_Petition=0x31d1 # C +OP_SomeItemPacketMaybe=0x54e8 # C +OP_PVPStats=0x6a37 # C +OP_PVPLeaderBoardRequest=0x24cb # C +OP_PVPLeaderBoardReply=0x6ac2 # C +OP_PVPLeaderBoardDetailsRequest=0x2a99 # C +OP_PVPLeaderBoardDetailsReply=0x5156 # C +OP_RestState=0x292f # C +OP_RespawnWindow=0x268c # C was 0x1643 +OP_DisciplineTimer=0x684c # C +OP_LDoNButton=0x41b5 # C +OP_SetStartCity=0x7bf6 # C +OP_VoiceMacroIn=0x31b1 # C +OP_VoiceMacroOut=0x7880 # C +OP_ItemViewUnknown=0x21c7 # C +OP_VetRewardsAvaliable=0x4e4e # C +OP_VetClaimRequest=0x771f # C +OP_VetClaimReply=0x2f95 # C +OP_CrystalCountUpdate=0x2d0e # C +OP_DisciplineUpdate=0x0000 # +OP_BecomeCorpse=0x0000 # +OP_MobUpdate=0x4656 # Unused? +OP_NPCMoveUpdate=0x22a7 # + +# New Opcodes +OP_SpawnPositionUpdate=0x4656 # +OP_ManaUpdate=0x4b61 # +OP_EnduranceUpdate=0x02d6 # +OP_MobManaUpdate=0x2ac1 # +OP_MobEnduranceUpdate=0x6c5f # + + +# Looting +OP_LootRequest=0x2701 # C +OP_EndLootRequest=0x6ad7 # C +OP_LootItem=0x2b5a # C +OP_LootComplete=0x41a6 # C + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x4675 # C +OP_TraderDelItem=0x63c8 # C +OP_BecomeTrader=0x528f # C +OP_TraderShop=0x7598 # C +OP_TraderItemUpdate=0x0000 # +OP_Trader=0x7092 # C +OP_ShopItem=0x0000 # +OP_TraderBuy=0x053a # C +OP_Barter=0x4b49 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # + +OP_MarketPlace1=0x367b +OP_MarketPlace2=0x0e1d + +# pc/npc trading +OP_TradeRequest=0x47ae # C +OP_TradeAcceptClick=0x064a # C +OP_TradeRequestAck=0x606a # C +OP_TradeCoins=0x0149 # C +OP_FinishTrade=0x3ff6 # C +OP_CancelTrade=0x527e # C +OP_TradeMoneyUpdate=0x7452 # C +OP_MoneyUpdate=0x2d1d # C + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x3c27 # C +OP_FinishWindow2=0x2f5d # C + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x032a # C +OP_ItemVerifyReply=0x7866 # C + +# merchant crap +OP_ShopPlayerSell=0x0b27 # C +OP_ShopRequest=0x4194 # C +OP_ShopEnd=0x3753 # C +OP_ShopEndConfirm=0x7114 # C +OP_ShopPlayerBuy=0x436a # C +OP_ShopDelItem=0x63c8 # C + +# tradeskill stuff: +OP_ClickObject=0x5f0d # C +OP_ClickObjectAction=0x5dbc # C +OP_ClearObject=0x0b80 # C +OP_RecipeDetails=0x49f4 # C +OP_RecipesFavorite=0x1150 # C +OP_RecipesSearch=0x55dd # C +OP_RecipeReply=0x0643 # C +OP_RecipeAutoCombine=0x1564 # C +OP_TradeSkillCombine=0x4212 # C + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x6361 # C +OP_OpenTributeMaster=0x2daa # C +OP_SelectTribute=0x38b0 # C +OP_TributeItem=0x416b # C +OP_TributeMoney=0x0b89 # C +OP_TributeToggle=0x45b3 # C +OP_TributePointUpdate=0x7d05 # C +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x2ab6 # C +OP_AdventureFinish=0x624c # C +OP_AdventureInfoRequest=0x3a75 # C +OP_AdventureInfo=0x3a28 # C +OP_AdventureRequest=0x5a55 # C +OP_AdventureDetails=0x5ed3 # C +OP_AdventureData=0x05 # C +OP_AdventureUpdate=0x0dcd # C +OP_AdventureMerchantRequest=0x0989 # C +OP_AdventureMerchantResponse=0x6ade # C +OP_AdventureMerchantPurchase=0x23fc # C +OP_AdventureMerchantSell=0x5363 # C +OP_AdventurePointsUpdate=0x6b7f # C +OP_AdventureStatsRequest=0x7e45 # C +OP_AdventureStatsReply=0x6a30 # C +OP_AdventureLeaderboardRequest=0x7537 # C +OP_AdventureLeaderboardReply=0x7c87 # C + +# Group Opcodes +OP_GroupDisband=0x47e7 # C +OP_GroupInvite=0x5d32 # C +OP_GroupFollow=0x321a # C +OP_GroupUpdate=0x21be +OP_GroupUpdateB=0x7351 +OP_GroupCancelInvite=0x5251 # C - Same as OP_CancelInvite? +OP_GroupAcknowledge=0x3e22 # C +OP_GroupDelete=0x58e6 # +OP_CancelInvite=0x5251 # C +OP_GroupFollow2=0x2736 # C +OP_GroupInvite2=0x548c # C +OP_GroupDisbandYou=0xc56c +OP_GroupDisbandOther=0x162d +OP_GroupLeaderChange=0x7545 +OP_GroupRoles=0x6b67 +# LFG/LFP Opcodes +OP_LFGCommand=0x3288 # C +OP_LFGGetMatchesRequest=0x5613 # C +OP_LFGGetMatchesResponse=0x7d90 # C +OP_LFPGetMatchesRequest=0x0479 # C +OP_LFPGetMatchesResponse=0x16f5 # C +OP_LFPCommand=0x710e # C +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x3b52 # C +OP_RaidUpdate=0x32c6 # C +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x1418 # C +OP_CombatAbility=0x24c5 # C +OP_SenseTraps=0x1e04 # C +OP_PickPocket=0x25f0 # C +OP_DisarmTraps=0x0000 # +OP_Disarm=0x6def # C +OP_Sneak=0x03e7 # C +OP_Fishing=0x7093 # C +OP_InstillDoubt=0x2d41 # C +OP_FeignDeath=0x3f49 # C +OP_Mend=0x221a # C +OP_LDoNOpen=0x6129 # C + +# Task packets +OP_TaskActivityComplete=0x4df0 # C +OP_TaskMemberList=0x34ed # C +OP_OpenNewTasksWindow=0x4dd5 # C +OP_AvaliableTask=0x2136 # C +OP_AcceptNewTask=0x5832 # C +OP_TaskHistoryRequest=0x29d7 # C +OP_TaskHistoryReply=0x3d2a # C +OP_CancelTask=0x726b # C +OP_DeclineAllTasks=0x0000 # +OP_Shroud=0x6d1f +OP_ShroudRemove=0x17f6 +OP_ShroudUnknown1=0x169a +OP_ShroudUnknown2=0x4292 +OP_ShroudClearAA=0x3bef +OP_ShroudSelectionWindow=0x4d79 +OP_ShroudRequestStats=0x28ce +OP_ShroudRespondStats=0x33f2 +OP_ShroudSelect=0x194f +# Title opcodes +OP_NewTitlesAvailable=0x4399 # C +OP_RequestTitles=0x7327 # C +OP_SendTitleList=0x158f # C +OP_SetTitle=0x698a # C +OP_SetTitleReply=0x4d3e # C + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_IncreaseStats=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x5bad # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x5d24 # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0001 # +OP_Login=0x0002 # +OP_ServerListRequest=0x0004 # +OP_PlayEverquestRequest=0x000d # +OP_PlayEverquestResponse=0x0021 # +OP_ChatMessage=0x0016 # +OP_LoginAccepted=0x0017 # +OP_ServerListResponse=0x0018 # +OP_Poll=0x0029 # +OP_EnterChat=0x000f # +OP_PollResponse=0x0011 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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 # + +# Mercenary Opcodes +OP_MercenaryDataRequest=0x4717 # +OP_MercenaryDataResponse=0x1715 # +OP_MercenaryHire=0x0bfd # +OP_MercenaryAssign=0x6188 # +OP_MercenaryTimer=0x116d # +OP_MercenaryUnknown1=0x28c7 # diff --git a/utils/EQExtractor2/EQExtractor2/patch_TestServer-Feb5-2013.conf b/utils/EQExtractor2/EQExtractor2/patch_TestServer-Feb5-2013.conf new file mode 100644 index 000000000..a633e4f37 --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_TestServer-Feb5-2013.conf @@ -0,0 +1,653 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 used for unknown explorer + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x437c +OP_ApproveWorld=0x7c9b +OP_LogServer=0x729e +OP_SendCharInfo=0x79c4 +OP_ExpansionInfo=0x00ec +OP_GuildsList=0x3e97 +OP_EnterWorld=0x52ca +OP_PostEnterWorld=0x26d4 +OP_World_Client_CRC1=0x3f0e +OP_World_Client_CRC2=0x4628 +OP_SendSpellChecksum=0x0000 +OP_SendSkillCapsChecksum=0x0000 + +# Character Select Related: +OP_SendMaxCharacters=0x79df +OP_SendMembership=0x5584 +OP_SendMembershipDetails=0x7bee +OP_CharacterCreateRequest=0x4aac +OP_CharacterCreate=0x60ea +OP_DeleteCharacter=0x4415 +OP_RandomNameGenerator=0x2d39 +OP_ApproveName=0x6607 +OP_MOTD=0x237b +OP_SetChatServer=0x7fbc +OP_SetChatServer2=0x611a +OP_ZoneServerInfo=0x0610 +OP_WorldComplete=0x4357 +OP_WorldUnknown001=0x0f12 +OP_FloatListThing=0x5d33 + +# Reasons for Disconnect: +OP_ZoneUnavail=0x4e66 +OP_WorldClientReady=0x0059 +OP_CharacterStillInZone=0x0000 +OP_WorldChecksumFailure=0x0000 +OP_WorldLoginFailed=0x0000 +OP_WorldLogout=0x0000 +OP_WorldLevelTooHigh=0x0000 +OP_CharInacessable=0x0000 +OP_UserCompInfo=0x0000 +OP_SendExeChecksum=0x0000 +OP_SendBaseDataChecksum=0x0000 + +# Zone in opcodes +OP_AckPacket=0x5bf3 +OP_ZoneEntry=0x280b +OP_ReqNewZone=0x932b +OP_NewZone=0x6d6c +OP_ZoneSpawns=0x1451 +OP_PlayerProfile=0x3a78 +OP_TimeOfDay=0x5204 +OP_LevelUpdate=0x747e +OP_Stamina=0x4dab +OP_RequestClientZoneChange=0x782a +OP_ZoneChange=0x3654 +OP_LockoutTimerInfo=0x0000 +OP_ZoneServerReady=0x0000 +OP_ZoneInUnknown=0x0000 +OP_LogoutReply=0x0000 +OP_PreLogoutReply=0x0000 + +# Required to fully log in +OP_SpawnAppearance=0x4223 +OP_ChangeSize=0x4b3b +OP_TributeUpdate=0x58f5 +OP_TributeTimer=0x6d2e +OP_SendTributes=0x2017 +OP_SendGuildTributes=0x7fd8 +OP_TributeInfo=0x7a02 +OP_Weather=0x434b +OP_ReqClientSpawn=0x9668 +OP_SpawnDoor=0x49ac +OP_GroundSpawn=0x3855 +OP_SendZonepoints=0x76d3 +OP_BlockedBuffs=0x0966 +OP_RemoveBlockedBuffs=0x153a +OP_ClearBlockedBuffs=0x7aa7 +OP_WorldObjectsSent=0x0740 +OP_SendExpZonein=0x1cc3 +OP_SendAATable=0x70ba +OP_RespondAA=0x4d0e +OP_UpdateAA=0x3269 +OP_SendAAStats=0x3168 +OP_AAExpUpdate=0x0ded +OP_ExpUpdate=0x4326 +OP_HPUpdate=0x5cf1 +OP_ManaChange=0x1af5 +OP_TGB=0x0f18 +OP_SpecialMesg=0x6700 +OP_GuildMemberList=0x1d1d +OP_GuildMOTD=0x5095 +OP_CharInventory=0x3715 +OP_WearChange=0x3260 +OP_ClientUpdate=0x2f10 +OP_ClientReady=0x4da3 # 0x422d +OP_SetServerFilter=0x21dd + +# Guild Opcodes - Disabled until crashes are resolved in RoF +OP_GetGuildMOTD=0x76a3 # Was 0x35dc +OP_GetGuildMOTDReply=0x0e55 # Was 0x4586 +OP_GuildMemberUpdate=0x07ac # Was 0x5643 +OP_GuildInvite=0x6d0c +OP_GuildRemove=0x4df4 +OP_GuildPeace=0x41e5 +OP_SetGuildMOTD=0x361a +OP_GuildList=0x0000 +OP_GuildWar=0x299a +OP_GuildLeader=0x3892 +OP_GuildDelete=0x0d49 +OP_GuildInviteAccept=0x3c45 +OP_GuildDemote=0x3a8d +OP_GuildPublicNote=0x483c +OP_GuildManageBanker=0x04ad # Was 0x0737 +OP_GuildBank=0xe64c # Was 0x10c3 +OP_SetGuildRank=0x72aa +OP_GuildUpdateURLAndChannel=0x526c +OP_GuildStatus=0x3230 +OP_GuildCreate=0x1a3c # or maybe 0x086e +OP_GuildMemberLevelUpdate=0x0000 # Unused? +OP_ZoneGuildList=0x0000 # Unused? +OP_GetGuildsList=0x0000 # Unused? +OP_LFGuild=0x0000 +OP_GuildManageRemove=0x0000 +OP_GuildManageAdd=0x0000 +OP_GuildManageStatus=0x0000 + +# GM/Guide Opcodes +OP_GMServers=0x05ca +OP_GMBecomeNPC=0x7c0a +OP_GMZoneRequest=0x295d +OP_GMZoneRequest2=0x1c36 +OP_GMGoto=0x1659 +OP_GMSearchCorpse=0x537a +OP_GMHideMe=0x764e +OP_GMDelCorpse=0x2280 +OP_GMApproval=0x6768 +OP_GMToggle=0x4c4e +OP_GMSummon=0x17e5 # Was 0x684f +OP_GMEmoteZone=0x6c26 # Was 0x0655 +OP_GMEmoteWorld=0x1ff3 # Was 0x1935 +OP_GMFind=0x4a39 +OP_GMKick=0x5b0f +OP_GMKill=0x6d0d +OP_GMNameChange=0x07fc # Was 0x4434 +OP_GMLastName=0x57dd # Was 0x3077 + +# Misc Opcodes +OP_InspectRequest=0x4f0b +OP_InspectAnswer=0x3bf5 +OP_InspectMessageUpdate=0x22ae +OP_BeginCast=0x66fd +OP_BuffFadeMsg=0x4aa9 +OP_ConsentResponse=0x57a5 +OP_MemorizeSpell=0x351d +OP_SwapSpell=0x3069 +OP_CastSpell=0x568e +OP_Consider=0x0ea4 +OP_FormattedMessage=0x7cd3 +OP_SimpleMessage=0x4a83 +OP_Buff=0x3c26 +OP_Illusion=0x25dd +OP_MoneyOnCorpse=0x6fbd +OP_RandomReply=0x3939 +OP_DenyResponse=0x7dc7 +OP_SkillUpdate=0x365d +OP_GMTrainSkillConfirm=0x1a0c # 0x3960 +OP_RandomReq=0x50ce +OP_Death=0x09fc +OP_GMTraining=0x1d12 +OP_GMEndTraining=0x7d15 +OP_GMTrainSkill=0x1369 +OP_Animation=0x47d4 +OP_Begging=0x7da4 +OP_Consent=0x753e +OP_ConsentDeny=0x54dd +OP_AutoFire=0x3517 +OP_PetCommands=0x5c34 +OP_DeleteSpell=0x60e1 +OP_Surname=0x7d6b +OP_ClearSurname=0x5c93 +OP_FaceChange=0x09ac +OP_SenseHeading=0x7c17 +OP_Action=0x1384 +OP_ConsiderCorpse=0x4715 +OP_HideCorpse=0x31df +OP_CorpseDrag=0x4b1c +OP_CorpseDrop=0x36e2 +OP_Bug=0x5194 +OP_Feedback=0x29f6 +OP_Report=0x0c72 +OP_Damage=0x6004 +OP_ChannelMessage=0x0ead +OP_Assist=0x7923 +OP_AssistGroup=0x7737 +OP_MoveCoin=0x54b4 +OP_ZonePlayerToBind=0x5cb6 +OP_KeyRing=0x1490 +OP_WhoAllRequest=0x18e5 +OP_WhoAllResponse=0x1b8c +OP_FriendsWho=0x173c +OP_ConfirmDelete=0x417b +OP_Logout=0x2436 +OP_Rewind=0x25e3 +OP_TargetCommand=0x63cf +OP_Hide=0x2e6f +OP_Jump=0x7629 +OP_Camp=0x3297 +OP_Emote=0x1ad3 +OP_SetRunMode=0x3448 +OP_BankerChange=0x08cc +OP_TargetMouse=0x77c6 +OP_MobHealth=0x29b7 +OP_InitialMobHealth=0x0000 # Unused? +OP_TargetHoTT=0x042a +OP_XTargetResponse=0x5473 +OP_XTargetRequest=0x45a9 +OP_XTargetAutoAddHaters=0x5af8 +OP_TargetBuffs=0x156f +OP_BuffCreate=0x2013 +OP_BuffRemoveRequest=0x718c +OP_DeleteSpawn=0x69b2 +OP_AutoAttack=0x702f +OP_AutoAttack2=0x47b1 +OP_Consume=0x3008 +OP_MoveItem=0x1512 +OP_DeleteItem=0x4087 +OP_DeleteCharge=0x2a47 +OP_ItemPacket=0x1dc1 +OP_ItemLinkResponse=0x24cd +OP_ItemLinkClick=0x0a72 +OP_ItemPreview=0x46e4 +OP_NewSpawn=0x2302 +OP_Track=0x0192 +OP_TrackTarget=0x7618 +OP_TrackUnknown=0x1a71 +OP_ClickDoor=0x15fb +OP_MoveDoor=0x4665 +OP_RemoveAllDoors=0x4cca +OP_EnvDamage=0x2ed2 +OP_BoardBoat=0x4324 +OP_Forage=0x39e3 +OP_LeaveBoat=0x20e6 +OP_ControlBoat=0x599c +OP_SafeFallSuccess=0x31ee +OP_RezzComplete=0x79b9 +OP_RezzRequest=0x70e8 +OP_RezzAnswer=0x7158 +OP_Shielding=0x0bd4 +OP_RequestDuel=0x4719 +OP_MobRename=0x5b82 +OP_AugmentItem=0x1d6e # Was 0x37cb +OP_WeaponEquip1=0x1614 +OP_WeaponEquip2=0x1660 # Was 0x6022 +OP_WeaponUnequip2=0x75ea # Was 0x0110 +OP_ApplyPoison=0x197e +OP_Save=0x260c +OP_TestBuff=0x29c3 # Was 0x3772 +OP_CustomTitles=0x719f +OP_Split=0x27d5 +OP_YellForHelp=0x2a50 +OP_LoadSpellSet=0x4d0f +OP_Bandolier=0x2c88 +OP_PotionBelt=0x62e9 # Was 0x4d3b +OP_DuelResponse=0x23e8 +OP_DuelResponse2=0x3828 +OP_SaveOnZoneReq=0x44d3 +OP_ReadBook=0x4910 +OP_Dye=0x265b +OP_InterruptCast=0x2e8c +OP_AAAction=0x4f7c +OP_LeadershipExpToggle=0x0350 +OP_LeadershipExpUpdate=0x4a6e +OP_PurchaseLeadershipAA=0x45a5 +OP_UpdateLeadershipAA=0x2efb +OP_MarkNPC=0x43f8 +OP_ClearNPCMarks=0x6c57 +OP_DelegateAbility=0x2e22 +OP_SetGroupTarget=0x0da5 +OP_Charm=0x738b +OP_Stun=0x3333 +OP_SendFindableNPCs=0x6516 +OP_FindPersonRequest=0x4e65 +OP_FindPersonReply=0x5e04 +OP_Sound=0x6ad4 +OP_PetBuffWindow=0x5be1 +OP_LevelAppearance=0x7a6f +OP_Translocate=0x3a61 +OP_Sacrifice=0x18a6 +OP_PopupResponse=0x0f8f +OP_OnLevelMessage=0x213b +OP_AugmentInfo=0x1565 +OP_Petition=0x6002 +OP_SomeItemPacketMaybe=0x49ee +OP_PVPStats=0x26ea # Unsure +OP_PVPLeaderBoardRequest=0x13fd +OP_PVPLeaderBoardReply=0x5dd0 +OP_PVPLeaderBoardDetailsRequest=0x7a42 +OP_PVPLeaderBoardDetailsReply=0x2b8b +OP_RestState=0x5b4b +OP_RespawnWindow=0x0920 +OP_LDoNButton=0x09f2 +OP_SetStartCity=0x372f # Was 0x2d1b +OP_VoiceMacroIn=0x376d +OP_VoiceMacroOut=0x5a45 +OP_ItemViewUnknown=0x7bf0 +OP_VetRewardsAvaliable=0x7060 +OP_VetClaimRequest=0x345c +OP_VetClaimReply=0x1705 +OP_DisciplineUpdate=0x4b8f # Was 0x2f05 +OP_DisciplineTimer=0x0b96 # Was 0x5e3f +OP_BecomeCorpse=0x0000 # Unused? +OP_Action2=0x0000 # Unused? +OP_MobUpdate=0x09aa +OP_NPCMoveUpdate=0x4eca +OP_CameraEffect=0x0546 +OP_SpellEffect=0x1f9d +OP_RemoveNimbusEffect=0x59dc +OP_AltCurrency=0x1da9 +OP_AltCurrencyMerchantRequest=0x44f0 +OP_AltCurrencyMerchantReply=0x77d4 +OP_AltCurrencyPurchase=0x02ec +OP_AltCurrencySell=0x7bcf +OP_AltCurrencySellSelection=0x122a +OP_AltCurrencyReclaim=0x5b90 +OP_CrystalCountUpdate=0x37da # Was 0x3f60 +OP_CrystalCreate=0x22e4 # Was 0x5a82 +OP_CrystalReclaim=0x1c12 # Was 0x7616 +OP_Untargetable=0x6dd5 +OP_IncreaseStats=0x355f +OP_Weblink=0x3d99 +#OP_OpenInventory=0x0000 # Likely does not exist in RoF -U +OP_OpenContainer=0x0000 + +OP_DzQuit=0x7878 +OP_DzListTimers=0x6bbc +OP_DzAddPlayer=0x4185 +OP_DzRemovePlayer=0x6c01 +OP_DzSwapPlayer=0x5746 +OP_DzMakeLeader=0x47ba +OP_DzPlayerList=0x4848 +OP_DzJoinExpeditionConfirm=0x56d3 +OP_DzJoinExpeditionReply=0x28bf +OP_DzExpeditionInfo=0x575f +OP_DzExpeditionList=0x6ff3 +OP_DzMemberStatus=0x2456 +OP_DzLeaderStatus=0x5be9 +OP_DzExpeditionEndsWarning=0x3966 +OP_DzMemberList=0x215d +OP_DzCompass=0x5a12 # Was 0x4f09 +OP_DzChooseZone=0x0000 # Maybe 0x29d6 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # Actually OP_MobUpdate ? +OP_ManaUpdate=0x52de +OP_EnduranceUpdate=0x4730 +OP_MobManaUpdate=0x7f17 +OP_MobEnduranceUpdate=0x0336 + +# Mercenary Opcodes +OP_MercenaryDataUpdateRequest=0x7452 +OP_MercenaryDataUpdate=0x0b3a +OP_MercenaryDataRequest=0x28ec +OP_MercenaryDataResponse=0x0247 +OP_MercenaryHire=0x4950 +OP_MercenaryDismiss=0x1408 +OP_MercenaryTimerRequest=0x7691 +OP_MercenaryTimer=0x0459 +OP_MercenaryUnknown1=0x57b4 +OP_MercenaryCommand=0x3be8 +OP_MercenarySuspendRequest=0x4aea +OP_MercenarySuspendResponse=0x29a8 +OP_MercenaryUnsuspendResponse=0x17f8 + +# Looting +OP_LootRequest=0x2106 +OP_EndLootRequest=0x77fc +OP_LootItem=0x69a9 +OP_LootComplete=0x2c4b + +# bazaar trader stuff: +OP_BazaarSearch=0x31f0 +OP_TraderDelItem=0x0000 +OP_BecomeTrader=0x1028 +OP_TraderShop=0x0527 +OP_Trader=0x608b # Was 0x6790 +OP_TraderBuy=0x0000 +OP_Barter=0x0d79 +OP_ShopItem=0x0000 +OP_BazaarInspect=0x0000 +OP_Bazaar=0x0000 +OP_TraderItemUpdate=0x0000 + +# pc/npc trading +OP_TradeRequest=0x0910 +OP_TradeAcceptClick=0x2921 +OP_TradeRequestAck=0x6f1b +OP_TradeCoins=0x6aef +OP_FinishTrade=0x7b39 +OP_CancelTrade=0x1486 +OP_TradeMoneyUpdate=0x438a +OP_MoneyUpdate=0x5d8c +OP_TradeBusy=0x5856 + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x4a2a +OP_FinishWindow2=0x6b34 + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x34b4 +OP_ItemVerifyReply=0x04c1 + +# merchant stuff +OP_ShopPlayerSell=0x064a +OP_ShopRequest=0x1f03 +OP_ShopEnd=0x38b9 +OP_ShopEndConfirm=0x1362 +OP_ShopPlayerBuy=0x5d60 +OP_ShopDelItem=0x5e29 + +# tradeskill stuff: +OP_ClickObject=0x4c3e +OP_ClickObjectAction=0x5e24 +OP_ClearObject=0x5465 +OP_RecipeDetails=0x2695 +OP_RecipesFavorite=0x1e20 +OP_RecipesSearch=0x1335 +OP_RecipeReply=0x728c +OP_RecipeAutoCombine=0x2fba +OP_TradeSkillCombine=0x11d5 + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x11e5 +OP_OpenTributeMaster=0x7c65 # Was 0x40f5 +OP_SelectTribute=0x3f29 +OP_TributeItem=0x42ff +OP_TributeMoney=0x1beb # Was 0x6fed +OP_TributeToggle=0x3d5c +OP_TributePointUpdate=0x1065 +OP_TributeNPC=0x0000 +OP_GuildTributeInfo=0x0000 +OP_OpenTributeReply=0x0000 +OP_GuildTributeStatus=0x0000 + +# Adventure packets: +OP_LeaveAdventure=0x206b +OP_AdventureFinish=0x7f00 +OP_AdventureInfoRequest=0x2fc8 +OP_AdventureInfo=0x1aaf +OP_AdventureRequest=0x6553 +OP_AdventureDetails=0x5892 +OP_AdventureData=0x7599 +OP_AdventureUpdate=0x41b6 +OP_AdventureMerchantRequest=0x1e99 # Was 654d +OP_AdventureMerchantResponse=0x394e # Was 7949 +OP_AdventureMerchantPurchase=0x04b1 # Was 155a +OP_AdventureMerchantSell=0x1b22 # Was 389c +OP_AdventurePointsUpdate=0x36bf # Was 7589 +OP_AdventureStatsRequest=0x5754 +OP_AdventureStatsReply=0x31e2 +OP_AdventureLeaderboardRequest=0x5b3d +OP_AdventureLeaderboardReply=0x5001 + +# Group Opcodes +OP_GroupDisband=0x468d +OP_GroupInvite=0x174e +OP_GroupFollow=0x06fc +OP_GroupUpdate=0x050b +OP_GroupUpdateB=0x09e2 +OP_GroupCancelInvite=0x0000 +OP_GroupAcknowledge=0x6f78 +OP_GroupDelete=0x0d6b +OP_CancelInvite=0x67e2 +OP_GroupFollow2=0x5435 +OP_GroupInvite2=0x09af +OP_GroupDisbandYou=0x56f7 +OP_GroupDisbandOther=0x50e8 +OP_GroupLeaderChange=0x3d88 +OP_GroupRoles=0x48b2 +OP_GroupMakeLeader=0x6c31 +OP_DoGroupLeadershipAbility=0x4af2 +OP_GroupLeadershipAAUpdate=0x6a2b + +# LFG/LFP Opcodes +OP_LFGCommand=0x2c60 +OP_LFGGetMatchesRequest=0x7d32 +OP_LFGGetMatchesResponse=0x5fa3 +OP_LFPGetMatchesRequest=0x011c +OP_LFPGetMatchesResponse=0x6336 +OP_LFPCommand=0x076c +OP_LFGAppearance=0x0000 +OP_LFGResponse=0x0000 + +# Raid Opcodes +OP_RaidInvite=0x6f6a +OP_RaidUpdate=0x37bb +OP_RaidJoin=0x0000 + +# Button-push commands +OP_Taunt=0x5d7f +OP_CombatAbility=0x79ca +OP_SenseTraps=0x4795 # Was 0x2ee0 +OP_PickPocket=0x5376 +OP_DisarmTraps=0x0000 +OP_Disarm=0x5122 +OP_Sneak=0x6518 +OP_Fishing=0x4312 +OP_InstillDoubt=0x3392 +OP_FeignDeath=0x2887 +OP_Mend=0x4041 +OP_Bind_Wound=0x55bf +OP_LDoNOpen=0x16af + +# Task packets +OP_TaskDescription=0x0190 +OP_TaskActivity=0x0c6a +OP_CompletedTasks=0x304b +OP_TaskActivityComplete=0x0e9f +OP_AcceptNewTask=0x1c0f +OP_CancelTask=0xb199 +OP_TaskMemberList=0x4028 # Was 0x1656 +OP_OpenNewTasksWindow=0x1e5b # Was 0x11de +OP_AvaliableTask=0x1138 # Was 0x2377 +OP_TaskHistoryRequest=0x2e9b +OP_TaskHistoryReply=0x2adb +OP_DeclineAllTasks=0x0000 + +# Title opcodes +OP_NewTitlesAvailable=0x18c7 +OP_RequestTitles=0x1118 +OP_SendTitleList=0x4750 +OP_SetTitle=0x2a7e +OP_SetTitleReply=0x0867 + +# mail opcodes +OP_Command=0x0000 +OP_MailboxHeader=0x0000 +OP_MailHeader=0x0000 +OP_MailBody=0x0000 +OP_NewMail=0x0000 +OP_SentConfirm=0x0000 + +########### Below this point should not be needed ########### + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 +OP_LocInfo=0x0000 +OP_ReloadUI=0x0000 +OP_ItemName=0x0000 +OP_ItemLinkText=0x0000 +OP_MultiLineMsg=0x0000 +OP_MendHPUpdate=0x0000 +OP_TargetReject=0x0000 +OP_SafePoint=0x0000 +OP_ApproveZone=0x0000 +OP_ZoneComplete=0x0000 +OP_ClientError=0x0000 +OP_DumpName=0x0000 +OP_Heartbeat=0x0000 +OP_CrashDump=0x0000 +OP_LoginComplete=0x0000 + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 +OP_PlayMP3=0x0000 +OP_ReclaimCrystals=0x0000 +OP_DynamicWall=0x0000 +OP_OpenDiscordMerchant=0x0000 +OP_DiscordMerchantInventory=0x0000 +OP_GiveMoney=0x0000 +OP_RequestKnowledgeBase=0x0000 +OP_KnowledgeBase=0x0000 +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 +OP_Some6ByteHPUpdate=0x0000 seems to happen when you target group members +OP_QueryResponseThing=0x0000 + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x46d3 # OP_SendSpellChecksum +#OP_LoginUnknown2=0x040b # OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 search term for petition +OP_PetitionSearchResults=0x0000 (list of?) matches from search +OP_PetitionSearchText=0x0000 text results of search + +OP_PetitionUpdate=0x0000 +OP_PetitionCheckout=0x0000 +OP_PetitionCheckIn=0x0000 +OP_PetitionQue=0x0000 +OP_PetitionUnCheckout=0x0000 +OP_PetitionDelete=0x0000 +OP_DeletePetition=0x0000 +OP_PetitionResolve=0x0000 +OP_PDeletePetition=0x0000 +OP_PetitionBug=0x0000 +OP_PetitionRefresh=0x0000 +OP_PetitionCheckout2=0x0000 +OP_PetitionViewPetition=0x0000 + +# Login opcodes +OP_SessionReady=0x0000 +OP_Login=0x0000 +OP_ServerListRequest=0x0000 +OP_PlayEverquestRequest=0x0000 +OP_PlayEverquestResponse=0x0000 +OP_ChatMessage=0x0000 +OP_LoginAccepted=0x0000 +OP_ServerListResponse=0x0000 +OP_Poll=0x0000 +OP_EnterChat=0x0000 +OP_PollResponse=0x0000 + +# raw opcodes +OP_RAWSessionRequest=0x0000 +OP_RAWSessionResponse=0x0000 +OP_RAWCombined=0x0000 +OP_RAWSessionDisconnect=0x0000 +OP_RAWKeepAlive=0x0000 +OP_RAWSessionStatRequest=0x0000 +OP_RAWSessionStatResponse=0x0000 +OP_RAWPacket=0x0000 +OP_RAWFragment=0x0000 +OP_RAWOutOfOrderAck=0x0000 +OP_RAWAck=0x0000 +OP_RAWAppCombined=0x0000 +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 diff --git a/utils/EQExtractor2/EQExtractor2/patch_TestServer-Jan16-2013.conf b/utils/EQExtractor2/EQExtractor2/patch_TestServer-Jan16-2013.conf new file mode 100644 index 000000000..5a8baa8fa --- /dev/null +++ b/utils/EQExtractor2/EQExtractor2/patch_TestServer-Jan16-2013.conf @@ -0,0 +1,653 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 used for unknown explorer + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x317e +OP_ApproveWorld=0x2ef2 +OP_LogServer=0x5b24 +OP_SendCharInfo=0x2ad9 +OP_ExpansionInfo=0x1a9b +OP_GuildsList=0x6f57 +OP_EnterWorld=0x6078 +OP_PostEnterWorld=0x0f9f +OP_World_Client_CRC1=0x7978 +OP_World_Client_CRC2=0x2b1c +OP_SendSpellChecksum=0x0000 +OP_SendSkillCapsChecksum=0x0000 + +# Character Select Related: +OP_SendMaxCharacters=0x24db +OP_SendMembership=0x57f3 +OP_SendMembershipDetails=0x4183 +OP_CharacterCreateRequest=0x2cad +OP_CharacterCreate=0x15d7 +OP_DeleteCharacter=0x2f9a +OP_RandomNameGenerator=0x4bf8 +OP_ApproveName=0x78cc +OP_MOTD=0x282a +OP_SetChatServer=0x3f5e +OP_SetChatServer2=0x6a29 +OP_ZoneServerInfo=0x00c9 +OP_WorldComplete=0x48e6 +OP_WorldUnknown001=0x52d2 +OP_FloatListThing=0x2b9c + +# Reasons for Disconnect: +OP_ZoneUnavail=0x408a +OP_WorldClientReady=0x45b4 +OP_CharacterStillInZone=0x0000 +OP_WorldChecksumFailure=0x0000 +OP_WorldLoginFailed=0x0000 +OP_WorldLogout=0x0000 +OP_WorldLevelTooHigh=0x0000 +OP_CharInacessable=0x0000 +OP_UserCompInfo=0x0000 +OP_SendExeChecksum=0x0000 +OP_SendBaseDataChecksum=0x0000 + +# Zone in opcodes +OP_AckPacket=0x46c2 +OP_ZoneEntry=0x4a63 +OP_ReqNewZone=0x3ff0 +OP_NewZone=0x6727 +OP_ZoneSpawns=0x1d0a +OP_PlayerProfile=0x6926 +OP_TimeOfDay=0x6b7b +OP_LevelUpdate=0x1d84 +OP_Stamina=0x5114 +OP_RequestClientZoneChange=0x31b0 +OP_ZoneChange=0x7951 +OP_LockoutTimerInfo=0x0000 +OP_ZoneServerReady=0x0000 +OP_ZoneInUnknown=0x0000 +OP_LogoutReply=0x0000 +OP_PreLogoutReply=0x0000 + +# Required to fully log in +OP_SpawnAppearance=0x3143 +OP_ChangeSize=0x70d1 +OP_TributeUpdate=0x590f +OP_TributeTimer=0x4590 +OP_SendTributes=0x4124 +OP_SendGuildTributes=0x1df9 +OP_TributeInfo=0x37b0 +OP_Weather=0x63f0 +OP_ReqClientSpawn=0x37f1 +OP_SpawnDoor=0x7e51 +OP_GroundSpawn=0x07eb +OP_SendZonepoints=0x0067 +OP_BlockedBuffs=0x1375 +OP_RemoveBlockedBuffs=0x7996 +OP_ClearBlockedBuffs=0x7a20 +OP_WorldObjectsSent=0x0ae9 +OP_SendExpZonein=0x0380 +OP_SendAATable=0x7a7c +OP_RespondAA=0x11a3 +OP_UpdateAA=0x4559 +OP_SendAAStats=0x32fd +OP_AAExpUpdate=0x24a1 +OP_ExpUpdate=0x793d +OP_HPUpdate=0x6598 +OP_ManaChange=0x3a19 +OP_TGB=0x552e +OP_SpecialMesg=0x7545 +OP_GuildMemberList=0x2ef0 +OP_GuildMOTD=0x3880 +OP_CharInventory=0x4df2 +OP_WearChange=0x003a +OP_ClientUpdate=0x47c9 +OP_ClientReady=0x7538 +OP_SetServerFilter=0x178d + +# Guild Opcodes - Disabled until crashes are resolved in RoF +OP_GetGuildMOTD=0x3dfe +OP_GetGuildMOTDReply=0x7bff +OP_GuildMemberUpdate=0x45e4 +OP_GuildInvite=0x47d2 +OP_GuildRemove=0x245d +OP_GuildPeace=0x71b2 +OP_SetGuildMOTD=0x703b +OP_GuildList=0x0000 +OP_GuildWar=0x731c +OP_GuildLeader=0x01c6 +OP_GuildDelete=0x5592 +OP_GuildInviteAccept=0x7fa4 +OP_GuildDemote=0x144a +OP_GuildPublicNote=0x44fb +OP_GuildManageBanker=0x769e +OP_GuildBank=0x2591 +OP_SetGuildRank=0x4195 +OP_GuildUpdateURLAndChannel=0x2df0 +OP_GuildStatus=0x6e9e +OP_GuildCreate=0x1544 +OP_GuildMemberLevelUpdate=0x0000 # Unused? +OP_ZoneGuildList=0x0000 # Unused? +OP_GetGuildsList=0x0000 # Unused? +OP_LFGuild=0x0000 +OP_GuildManageRemove=0x0000 +OP_GuildManageAdd=0x0000 +OP_GuildManageStatus=0x0000 + +# GM/Guide Opcodes +OP_GMServers=0x2e1e +OP_GMBecomeNPC=0x1def +OP_GMZoneRequest=0x49ad +OP_GMZoneRequest2=0x54ff +OP_GMGoto=0x35a9 +OP_GMSearchCorpse=0x206c +OP_GMHideMe=0x0b62 +OP_GMDelCorpse=0x6344 +OP_GMApproval=0x35b3 +OP_GMToggle=0x3bd7 +OP_GMSummon=0x0c58 +OP_GMEmoteZone=0x28ab +OP_GMEmoteWorld=0x4903 +OP_GMFind=0x0ea3 +OP_GMKick=0x5f92 +OP_GMKill=0x3b7d +OP_GMNameChange=0x1c3f +OP_GMLastName=0x4ead + +# Misc Opcodes +OP_InspectRequest=0x3692 +OP_InspectAnswer=0x0af1 +OP_InspectMessageUpdate=0x1bc6 +OP_BeginCast=0x5df6 +OP_BuffFadeMsg=0x569a +OP_ConsentResponse=0x68c2 +OP_MemorizeSpell=0x72e2 +OP_SwapSpell=0x70c4 +OP_CastSpell=0x5c4b +OP_Consider=0x0e31 +OP_FormattedMessage=0x3735 +OP_SimpleMessage=0x4645 +OP_Buff=0x7fb5 +OP_Illusion=0x3772 +OP_MoneyOnCorpse=0x4ab0 +OP_RandomReply=0x76ea +OP_DenyResponse=0x0a88 +OP_SkillUpdate=0x2e5f +OP_GMTrainSkillConfirm=0x6692 +OP_RandomReq=0x16e6 +OP_Death=0x25ad +OP_GMTraining=0x3dcc +OP_GMEndTraining=0x5721 +OP_GMTrainSkill=0x38f0 +OP_Animation=0x53f6 +OP_Begging=0x4d3d +OP_Consent=0x4d7c +OP_ConsentDeny=0x3b03 +OP_AutoFire=0x4697 +OP_PetCommands=0x574a +OP_DeleteSpell=0x5279 +OP_Surname=0x181d +OP_ClearSurname=0x1097 +OP_FaceChange=0x59d8 +OP_SenseHeading=0x689a +OP_Action=0x280b +OP_ConsiderCorpse=0x6c92 +OP_HideCorpse=0x7dea +OP_CorpseDrag=0x338a +OP_CorpseDrop=0x409e +OP_Bug=0x7589 +OP_Feedback=0x5d5f +OP_Report=0x521c +OP_Damage=0x5e8a +OP_ChannelMessage=0x1770 +OP_Assist=0x7021 +OP_AssistGroup=0x6668 +OP_MoveCoin=0x6a56 +OP_ZonePlayerToBind=0x859e +OP_KeyRing=0x7613 +OP_WhoAllRequest=0x1d64 +OP_WhoAllResponse=0x20aa +OP_FriendsWho=0x527c +OP_ConfirmDelete=0x3221 +OP_Logout=0x4e12 +OP_Rewind=0x44d0 +OP_TargetCommand=0x726c +OP_Hide=0x23f2 +OP_Jump=0x3dbc +OP_Camp=0x2ca2 +OP_Emote=0x314d +OP_SetRunMode=0x276f +OP_BankerChange=0x21b0 +OP_TargetMouse=0x3605 +OP_MobHealth=0x1949 +OP_InitialMobHealth=0x0000 # Unused? +OP_TargetHoTT=0x1be6 +OP_XTargetResponse=0x0ad9 +OP_XTargetRequest=0x3034 +OP_XTargetAutoAddHaters=0x7efd +OP_TargetBuffs=0x5e3b +OP_BuffCreate=0x51da +OP_BuffRemoveRequest=0x6ce2 +OP_DeleteSpawn=0x0ef8 +OP_AutoAttack=0x577f +OP_AutoAttack2=0x4f56 +OP_Consume=0x7cca +OP_MoveItem=0x5ba6 +OP_DeleteItem=0x36dd +OP_DeleteCharge=0x6ec3 +OP_ItemPacket=0x75b5 +OP_ItemLinkResponse=0x5fa8 +OP_ItemLinkClick=0x0b40 +OP_ItemPreview=0x2688 +OP_NewSpawn=0x7cde +OP_Track=0x2f78 +OP_TrackTarget=0x74dc +OP_TrackUnknown=0x3127 +OP_ClickDoor=0x2b55 +OP_MoveDoor=0x5e21 +OP_RemoveAllDoors=0x73b3 +OP_EnvDamage=0x21da +OP_BoardBoat=0x1372 +OP_Forage=0x0d91 +OP_LeaveBoat=0x7582 +OP_ControlBoat=0x5d0e +OP_SafeFallSuccess=0x317f +OP_RezzComplete=0x017b +OP_RezzRequest=0x21a1 +OP_RezzAnswer=0x21ce +OP_Shielding=0x17d3 +OP_RequestDuel=0x4870 +OP_MobRename=0x3a4e +OP_AugmentItem=0x3d13 +OP_WeaponEquip1=0x6041 +OP_WeaponEquip2=0x23f7 +OP_WeaponUnequip2=0x4078 +OP_ApplyPoison=0x099a +OP_Save=0x1361 +OP_TestBuff=0x478a +OP_CustomTitles=0x6115 +OP_Split=0x617f +OP_YellForHelp=0x40d4 +OP_LoadSpellSet=0x0680 +OP_Bandolier=0x1627 +OP_PotionBelt=0x3208 +OP_DuelResponse=0x0d3b +OP_DuelResponse2=0x24e0 +OP_SaveOnZoneReq=0x13af +OP_ReadBook=0x641b +OP_Dye=0x401f +OP_InterruptCast=0x27e0 +OP_AAAction=0x2eae +OP_LeadershipExpToggle=0x0f66 +OP_LeadershipExpUpdate=0x3791 +OP_PurchaseLeadershipAA=0x2c3e +OP_UpdateLeadershipAA=0x18ef +OP_MarkNPC=0x3093 +OP_ClearNPCMarks=0x1404 +OP_DelegateAbility=0x29a8 +OP_SetGroupTarget=0x7b9f +OP_Charm=0x1c1f +OP_Stun=0x45e0 +OP_SendFindableNPCs=0x62da +OP_FindPersonRequest=0x41fb +OP_FindPersonReply=0x23e2 +OP_Sound=0x622a +OP_PetBuffWindow=0x53f8 +OP_LevelAppearance=0x5b03 +OP_Translocate=0x5e10 +OP_Sacrifice=0x2ee4 +OP_PopupResponse=0x7f5e +OP_OnLevelMessage=0x1ef3 +OP_AugmentInfo=0x0000 +OP_Petition=0x71e3 +OP_SomeItemPacketMaybe=0x1f4c +OP_PVPStats=0x1937 # Unsure +OP_PVPLeaderBoardRequest=0x739b +OP_PVPLeaderBoardReply=0x5c62 +OP_PVPLeaderBoardDetailsRequest=0x4ce0 +OP_PVPLeaderBoardDetailsReply=0x2a52 +OP_RestState=0x2342 +OP_RespawnWindow=0x3f23 +OP_LDoNButton=0x3820 +OP_SetStartCity=0x5d81 +OP_VoiceMacroIn=0x3144 +OP_VoiceMacroOut=0x75ed +OP_ItemViewUnknown=0x6cc1 +OP_VetRewardsAvaliable=0x67a6 +OP_VetClaimRequest=0x23e7 +OP_VetClaimReply=0x23eb +OP_DisciplineUpdate=0x5010 +OP_DisciplineTimer=0x0673 +OP_BecomeCorpse=0x0000 # Unused? +OP_Action2=0x0000 # Unused? +OP_MobUpdate=0x710b +OP_NPCMoveUpdate=0x51e9 +OP_CameraEffect=0x7a73 +OP_SpellEffect=0x30b6 +OP_RemoveNimbusEffect=0x1fb3 +OP_AltCurrency=0x32db +OP_AltCurrencyMerchantRequest=0x6588 +OP_AltCurrencyMerchantReply=0x393c +OP_AltCurrencyPurchase=0x43e8 +OP_AltCurrencySell=0x2c40 +OP_AltCurrencySellSelection=0x0eec +OP_AltCurrencyReclaim=0x0fbc +OP_CrystalCountUpdate=0x5cd9 +OP_CrystalCreate=0x0e4b +OP_CrystalReclaim=0x6506 +OP_Untargetable=0x3859 +OP_IncreaseStats=0x22b9 +OP_Weblink=0x2d92 +#OP_OpenInventory=0x0000 # Likely does not exist in RoF -U +OP_OpenContainer=0x0000 + +OP_DzQuit=0x20fb +OP_DzListTimers=0x50c1 +OP_DzAddPlayer=0x06e0 +OP_DzRemovePlayer=0x90ca +OP_DzSwapPlayer=0x6461 +OP_DzMakeLeader=0x599d +OP_DzPlayerList=0x6dfb +OP_DzJoinExpeditionConfirm=0x3e9d +OP_DzJoinExpeditionReply=0x25b7 +OP_DzExpeditionInfo=0x56a8 +OP_DzExpeditionList=0x6171 +OP_DzMemberStatus=0x52d9 +OP_DzLeaderStatus=0x7b38 +OP_DzExpeditionEndsWarning=0x26c5 +OP_DzMemberList=0x59e2 +OP_DzCompass=0x3435 +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # Actually OP_MobUpdate ? +OP_ManaUpdate=0x06bc +OP_EnduranceUpdate=0x4e20 +OP_MobManaUpdate=0x4738 +OP_MobEnduranceUpdate=0x6519 + +# Mercenary Opcodes +OP_MercenaryDataUpdateRequest=0x3a44 +OP_MercenaryDataUpdate=0x6c75 +OP_MercenaryDataRequest=0x4b77 +OP_MercenaryDataResponse=0x69a0 +OP_MercenaryHire=0x49cd +OP_MercenaryDismiss=0x74bd +OP_MercenaryTimer=0x34df +OP_MercenaryTimerRequest=0x5f36 +OP_MercenaryUnknown1=0x305c +OP_MercenaryCommand=0x2131 +OP_MercenarySuspendRequest=0x53e8 +OP_MercenarySuspendResponse=0x277d +OP_MercenaryUnsuspendResponse=0x6ccc + +# Looting +OP_LootRequest=0x396f +OP_EndLootRequest=0x12a6 +OP_LootItem=0x7e1d +OP_LootComplete=0x46e8 + +# bazaar trader stuff: +OP_BazaarSearch=0x0962 +OP_TraderDelItem=0x0000 +OP_BecomeTrader=0x3360 +OP_TraderShop=0x67f9 +OP_Trader=0x3f87 +OP_TraderBuy=0x0000 +OP_Barter=0x14b0 +OP_ShopItem=0x0000 +OP_BazaarInspect=0x0000 +OP_Bazaar=0x0000 +OP_TraderItemUpdate=0x0000 + +# pc/npc trading +OP_TradeRequest=0x1695 +OP_TradeAcceptClick=0x0ecc +OP_TradeRequestAck=0x6489 +OP_TradeCoins=0x35eb +OP_FinishTrade=0x6897 +OP_CancelTrade=0x7e73 +OP_TradeMoneyUpdate=0x5857 +OP_MoneyUpdate=0x2044 +OP_TradeBusy=0x5ee9 + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x00ef +OP_FinishWindow2=0x7e90 + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x3d17 +OP_ItemVerifyReply=0x0f0e + +# merchant stuff +OP_ShopPlayerSell=0x1650 +OP_ShopRequest=0x6dab +OP_ShopEnd=0x44f1 +OP_ShopEndConfirm=0x07ad +OP_ShopPlayerBuy=0x7d9b +OP_ShopDelItem=0x2cec + +# tradeskill stuff: +OP_ClickObject=0x4370 +OP_ClickObjectAction=0x0883 +OP_ClearObject=0x4dd6 +OP_RecipeDetails=0x584c +OP_RecipesFavorite=0x2e74 +OP_RecipesSearch=0x41ee +OP_RecipeReply=0x1d46 +OP_RecipeAutoCombine=0x413d +OP_TradeSkillCombine=0x268f + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x4b72 +OP_OpenTributeMaster=0x58fb +OP_SelectTribute=0x3cb7 +OP_TributeItem=0x796f +OP_TributeMoney=0x7bed +OP_TributeToggle=0x448c +OP_TributePointUpdate=0x0ca7 +OP_TributeNPC=0x0000 +OP_GuildTributeInfo=0x0000 +OP_OpenTributeReply=0x0000 +OP_GuildTributeStatus=0x0000 + +# Adventure packets: +OP_LeaveAdventure=0x37bd +OP_AdventureFinish=0x2ebd +OP_AdventureInfoRequest=0x258e +OP_AdventureInfo=0x5df3 +OP_AdventureRequest=0x078c +OP_AdventureDetails=0x451d +OP_AdventureData=0x10da +OP_AdventureUpdate=0x38bc +OP_AdventureMerchantRequest=0x4958 +OP_AdventureMerchantResponse=0x71ab +OP_AdventureMerchantPurchase=0x207d +OP_AdventureMerchantSell=0x3307 +OP_AdventurePointsUpdate=0x0a93 +OP_AdventureStatsRequest=0x1b35 +OP_AdventureStatsReply=0x4cce +OP_AdventureLeaderboardRequest=0x4ac4 +OP_AdventureLeaderboardReply=0x71c1 + +# Group Opcodes +OP_GroupDisband=0x4039 +OP_GroupInvite=0x3b75 +OP_GroupFollow=0x3158 +OP_GroupUpdate=0x6b4e +OP_GroupUpdateB=0x39c3 +OP_GroupCancelInvite=0x0000 +OP_GroupAcknowledge=0x4013 +OP_GroupDelete=0x2fb9 +OP_CancelInvite=0x4ea4 +OP_GroupFollow2=0x396c +OP_GroupInvite2=0x4cc2 +OP_GroupDisbandYou=0x739a +OP_GroupDisbandOther=0x416d +OP_GroupLeaderChange=0x3153 +OP_GroupRoles=0x3d35 +OP_GroupMakeLeader=0x77a0 +OP_DoGroupLeadershipAbility=0x0c82 +OP_GroupLeadershipAAUpdate=0x4d85 + +# LFG/LFP Opcodes +OP_LFGCommand=0x370c +OP_LFGGetMatchesRequest=0x54e8 +OP_LFGGetMatchesResponse=0x444a +OP_LFPGetMatchesRequest=0x55d6 +OP_LFPGetMatchesResponse=0x0928 +OP_LFPCommand=0x1df7 +OP_LFGAppearance=0x0000 +OP_LFGResponse=0x0000 + +# Raid Opcodes +OP_RaidInvite=0x7e7e +OP_RaidUpdate=0x1c5a +OP_RaidJoin=0x0000 + +# Button-push commands +OP_Taunt=0x4798 +OP_CombatAbility=0x182d +OP_SenseTraps=0x3662 +OP_PickPocket=0x23c8 +OP_DisarmTraps=0x0000 +OP_Disarm=0x0bc6 +OP_Sneak=0x54a4 +OP_Fishing=0x1e3b +OP_InstillDoubt=0x5a3f +OP_FeignDeath=0x7bb7 +OP_Mend=0x56fb +OP_Bind_Wound=0x174b +OP_LDoNOpen=0x139f + +# Task packets +OP_TaskDescription=0x1eac +OP_TaskActivity=0x4423 +OP_CompletedTasks=0x7046 +OP_TaskActivityComplete=0x7eee +OP_AcceptNewTask=0x3d60 +OP_CancelTask=0x26a8 +OP_TaskMemberList=0x411e +OP_OpenNewTasksWindow=0x2633 +OP_AvaliableTask=0x05cc +OP_TaskHistoryRequest=0x72e1 +OP_TaskHistoryReply=0x0c1e +OP_DeclineAllTasks=0x0000 + +# Title opcodes +OP_NewTitlesAvailable=0x201f +OP_RequestTitles=0x6034 +OP_SendTitleList=0x2142 +OP_SetTitle=0x740d +OP_SetTitleReply=0x3196 + +# mail opcodes +OP_Command=0x0000 +OP_MailboxHeader=0x0000 +OP_MailHeader=0x0000 +OP_MailBody=0x0000 +OP_NewMail=0x0000 +OP_SentConfirm=0x0000 + +########### Below this point should not be needed ########### + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 +OP_LocInfo=0x0000 +OP_ReloadUI=0x0000 +OP_ItemName=0x0000 +OP_ItemLinkText=0x0000 +OP_MultiLineMsg=0x0000 +OP_MendHPUpdate=0x0000 +OP_TargetReject=0x0000 +OP_SafePoint=0x0000 +OP_ApproveZone=0x0000 +OP_ZoneComplete=0x0000 +OP_ClientError=0x0000 +OP_DumpName=0x0000 +OP_Heartbeat=0x0000 +OP_CrashDump=0x0000 +OP_LoginComplete=0x0000 + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 +OP_PlayMP3=0x0000 +OP_ReclaimCrystals=0x0000 +OP_DynamicWall=0x0000 +OP_OpenDiscordMerchant=0x0000 +OP_DiscordMerchantInventory=0x0000 +OP_GiveMoney=0x0000 +OP_RequestKnowledgeBase=0x0000 +OP_KnowledgeBase=0x0000 +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 +OP_Some6ByteHPUpdate=0x0000 seems to happen when you target group members +OP_QueryResponseThing=0x0000 + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x46d3 # OP_SendSpellChecksum +#OP_LoginUnknown2=0x040b # OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 search term for petition +OP_PetitionSearchResults=0x0000 (list of?) matches from search +OP_PetitionSearchText=0x0000 text results of search + +OP_PetitionUpdate=0x0000 +OP_PetitionCheckout=0x0000 +OP_PetitionCheckIn=0x0000 +OP_PetitionQue=0x0000 +OP_PetitionUnCheckout=0x0000 +OP_PetitionDelete=0x0000 +OP_DeletePetition=0x0000 +OP_PetitionResolve=0x0000 +OP_PDeletePetition=0x0000 +OP_PetitionBug=0x0000 +OP_PetitionRefresh=0x0000 +OP_PetitionCheckout2=0x0000 +OP_PetitionViewPetition=0x0000 + +# Login opcodes +OP_SessionReady=0x0000 +OP_Login=0x0000 +OP_ServerListRequest=0x0000 +OP_PlayEverquestRequest=0x0000 +OP_PlayEverquestResponse=0x0000 +OP_ChatMessage=0x0000 +OP_LoginAccepted=0x0000 +OP_ServerListResponse=0x0000 +OP_Poll=0x0000 +OP_EnterChat=0x0000 +OP_PollResponse=0x0000 + +# raw opcodes +OP_RAWSessionRequest=0x0000 +OP_RAWSessionResponse=0x0000 +OP_RAWCombined=0x0000 +OP_RAWSessionDisconnect=0x0000 +OP_RAWKeepAlive=0x0000 +OP_RAWSessionStatRequest=0x0000 +OP_RAWSessionStatResponse=0x0000 +OP_RAWPacket=0x0000 +OP_RAWFragment=0x0000 +OP_RAWOutOfOrderAck=0x0000 +OP_RAWAck=0x0000 +OP_RAWAppCombined=0x0000 +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 diff --git a/utils/TaskMaster/EQEmuWxProject.sln b/utils/TaskMaster/EQEmuWxProject.sln new file mode 100644 index 000000000..a5ba3588a --- /dev/null +++ b/utils/TaskMaster/EQEmuWxProject.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EQEmuWxProject", "EQEmuWxProject.vcproj", "{5D3FC402-F6D9-49B7-8ABF-18F66E91AB6B}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {5D3FC402-F6D9-49B7-8ABF-18F66E91AB6B}.Debug.ActiveCfg = Debug|Win32 + {5D3FC402-F6D9-49B7-8ABF-18F66E91AB6B}.Debug.Build.0 = Debug|Win32 + {5D3FC402-F6D9-49B7-8ABF-18F66E91AB6B}.Release.ActiveCfg = Release|Win32 + {5D3FC402-F6D9-49B7-8ABF-18F66E91AB6B}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/utils/TaskMaster/EQEmuWxProject.vcproj b/utils/TaskMaster/EQEmuWxProject.vcproj new file mode 100644 index 000000000..772021f8e --- /dev/null +++ b/utils/TaskMaster/EQEmuWxProject.vcproj @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/utils/TaskMaster/ErrorLog.cpp b/utils/TaskMaster/ErrorLog.cpp new file mode 100644 index 000000000..26fccb017 --- /dev/null +++ b/utils/TaskMaster/ErrorLog.cpp @@ -0,0 +1,71 @@ +#include +#include +#include +#include "ErrorLog.h" + + +EQEmuErrorLog::EQEmuErrorLog() +{ + mErrorLog = NULL; + mErrorLog = fopen(LOG_FILE_NAME, "w"); + if(!mErrorLog) { + Log(eqEmuLogConsole,"Opening of %s for writing failed, debug output will be tied to console only.", LOG_FILE_NAME); + } + else + Log(eqEmuLogBoth, "EQEmuErrorLog Init Successful."); + + mErrorLogSQL = NULL; + mErrorLogSQL = fopen(SQL_LOG_FILE_NAME, "a"); + if(!mErrorLog) { + Log(eqEmuLogConsole, "SQL Log Init FAILED, %s could not be opened for writing", SQL_LOG_FILE_NAME); + } + else + { + Log(eqEmuLogConsole, "SQL Log Init Complete"); + } + +} +EQEmuErrorLog::~EQEmuErrorLog() +{ + Log(eqEmuLogBoth, "EQEmuErrorLog Shutdown."); + if(mErrorLog) { + fclose(mErrorLog); + mErrorLog = NULL; + } +} + +void EQEmuErrorLog::Log(unsigned int mOutputType, const char *msg, ...) +{ + va_list argptr; + char *buffer = new char[MAX_LOG_LEN]; + + va_start(argptr, msg); + _vsnprintf(buffer, MAX_LOG_LEN, msg, argptr); + va_end(argptr); + + time_t mClock; + struct tm *mTime; + time(&mClock); + mTime = localtime(&mClock); + + if(mOutputType & eqEmuLogConsole){ + printf("[Debug] [%02d.%02d.%02d - %02d:%02d:%02d] %s\n", mTime->tm_mon+1, mTime->tm_mday, mTime->tm_year%100, mTime->tm_hour, mTime->tm_min, mTime->tm_sec, buffer); + } + + if(mOutputType & eqEmuLogFile){ + if(mErrorLog){ + fprintf(mErrorLog, "[%02d.%02d.%02d - %02d:%02d:%02d] %s\n", mTime->tm_mon+1, mTime->tm_mday, mTime->tm_year%100, mTime->tm_hour, mTime->tm_min, mTime->tm_sec, buffer); + } + } + + if(mOutputType & eqEmuLogSQL) + { + if(mErrorLogSQL){ + fprintf(mErrorLogSQL, "# [%02d.%02d.%02d - %02d:%02d:%02d]\n", mTime->tm_mon+1, mTime->tm_mday, mTime->tm_year%100, mTime->tm_hour, mTime->tm_min, mTime->tm_sec); + fprintf(mErrorLogSQL, "%s;\n\n", buffer); + } + } + + if(buffer) + delete[] buffer; +} \ No newline at end of file diff --git a/utils/TaskMaster/ErrorLog.h b/utils/TaskMaster/ErrorLog.h new file mode 100644 index 000000000..2d1ada0c4 --- /dev/null +++ b/utils/TaskMaster/ErrorLog.h @@ -0,0 +1,35 @@ +#ifndef EQWX_ERRORLOG__H +#define EQWX_ERRORLOG__H + +#include +#include + +//Log options +enum{ + eqEmuLogConsole = 1, + eqEmuLogFile = 2, + eqEmuLogSQL = 4, + eqEmuLogBoth = (eqEmuLogConsole | eqEmuLogFile), +}; + +//log file output +#define LOG_FILE_NAME "debug.txt" +#define SQL_LOG_FILE_NAME "sql_log.sql" + +//max single log length +//same as a client:message() max len +#define MAX_LOG_LEN 4096 + +//much simplified logging function, similar to the one used on the server (Tho much simpler) +class EQEmuErrorLog { +public: + + EQEmuErrorLog(); + ~EQEmuErrorLog(); + void Log(unsigned int mOutputType, const char *msg, ...); +private: + FILE* mErrorLog; + FILE* mErrorLogSQL; +}; + +#endif \ No newline at end of file diff --git a/utils/TaskMaster/activities.cpp b/utils/TaskMaster/activities.cpp new file mode 100644 index 000000000..fac68536b --- /dev/null +++ b/utils/TaskMaster/activities.cpp @@ -0,0 +1,648 @@ +#include +#include +#include +#include "tasks.h" +#include "utility.h" +#include "base.h" +#include "ErrorLog.h" + +using namespace std; + +void MainFrame::ActivitiesListBoxSimpleSelect(wxCommandEvent& event) +{ + eqtask_activity_id *eqaid; + eqaid = (eqtask_activity_id*)ActivitiesSelectionList->GetClientData(event.GetInt()); + if(eqaid){ + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Error selected a NULL activity object!"); + } +} + +void MainFrame::ActivitiesListBoxDoubleClick(wxCommandEvent& event) +{ + eqtask_activity_id *eqaid; + eqaid = (eqtask_activity_id*)ActivitiesSelectionList->GetClientData(event.GetInt()); + if(eqaid){ + openedActivity.activityid = eqaid->activityid; + openedActivity.id = eqaid->id; + openedActivity.step = eqaid->step; + + FillActivity(eqaid->id, eqaid->activityid, eqaid->step); + mActText1->Enable(); + mActText2->Enable(); + mActText3->Enable(); + mActivityZone->Enable(); + mActivityOptional->Enable(); + mActID->Enable(); + mActStep->Enable(); + mActType->Enable(); + mActDeliver->Enable(); + mActMethod->Enable(); + mActGoalID->Enable(); + mActGoalCount->Enable(); + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Error double clicked a NULL activity object!"); + } +} + +void MainFrame::NewActivity(wxCommandEvent& event) +{ + if(!mMysql) + return; + + if(openedIndex == -1) + return; + + mErrorLog->Log(eqEmuLogBoth, "Creating new activity..."); + eqtask_activities newAct; + eqtask curTask; + + mErrorLog->Log(eqEmuLogBoth, "Opened Index %u", openedIndex); + vector::iterator tIter = taskList.begin(); + tIter += openedIndex; + curTask = *tIter; + + vector::iterator Iter; + unsigned int highestId = 0; + bool zeroExists; + if(!taskActivitiesList.empty()){ + for(Iter = taskActivitiesList.begin(); Iter != taskActivitiesList.end(); Iter++) + { + eqtask_activities curAct = *Iter; + if(curAct.id == curTask.id){ + if(curAct.activityId == 0) + zeroExists = true; + + if(curAct.activityId > highestId){ + highestId = curAct.activityId; + } + } + } + } + + mErrorLog->Log(eqEmuLogBoth, "Highest id: %u", highestId); + + if(highestId == 0) + { + if(zeroExists) + { + newAct.activityId = highestId+1; + } + else + { + newAct.activityId = 0; + } + } + else + { + newAct.activityId = highestId+1; + } + + newAct.id = curTask.id; + newAct.zoneid = curTask.startzone; + newAct.activityType = 0; + newAct.deliverToNpc = 0; + newAct.goalcount = 0; + newAct.goalid = 0; + newAct.goalmethod = 2; + newAct.optional = false; + newAct.step = 0; + strcpy(newAct.text1, ""); + strcpy(newAct.text2, ""); + strcpy(newAct.text3, ""); + + taskActivitiesList.push_back(newAct); + + eqtask_activity_id * eqaid = new eqtask_activity_id; + eqaid->id = newAct.id; + eqaid->step = 0; + eqaid->activityid = newAct.activityId; + + wxString taskStr; + taskStr.Printf("Step: %u", 0); + ActivitiesSelectionList->Append(taskStr, (void*)eqaid); + taskStr.clear(); + + if(mMysql){ + char * mActQuery = 0; + MakeAnyLenString(&mActQuery, "INSERT INTO `activities` (`taskid`,`activityid`,`step`,`activitytype`,`text1`,`text2`,`text3`,`goalid`,`goalmethod`,`goalcount`,`delivertonpc`,`zoneid`,`optional`) VALUES (%u,%u,%u,%u,'%s','%s','%s',%u,%u,%u,%u,%u,%u)", + newAct.id, newAct.activityId, newAct.step, newAct.activityType, newAct.text1, newAct.text2, newAct.text3, newAct.goalid, newAct.goalmethod, newAct.goalcount, newAct.deliverToNpc, newAct.zoneid, newAct.optional ); + + mErrorLog->Log(eqEmuLogSQL, "%s", mActQuery); + if (mysql_query(mMysql, mActQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + } + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Mysql not connected for activity creation."); + } +} + +void MainFrame::DeleteActivity(wxCommandEvent& event) +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Mysql not connected for delete activity"); + return; + } + + if(ActivitiesSelectionList->GetCount() == 0){ + mErrorLog->Log(eqEmuLogBoth, "No activities in list to select for delete"); + return; + } + + int reply = wxMessageBox("Are you sure?", "Confirm Delete", wxYES_NO, this); + if(reply != wxYES) + { + mErrorLog->Log(eqEmuLogBoth, "User aborted delete of activity."); + return; + } + + eqtask_activity_id *selAct = (eqtask_activity_id*)ActivitiesSelectionList->GetClientData(ActivitiesSelectionList->GetSelection()); + unsigned int aid = selAct->activityid; + unsigned int tid = selAct->id; + + if(selAct) + { + if(mMysql){ + char * mActQuery = 0; + MakeAnyLenString(&mActQuery, "DELETE FROM activities WHERE taskid=%u AND activityid=%u",selAct->id, selAct->activityid); + mErrorLog->Log(eqEmuLogSQL, "%s", mActQuery); + if (mysql_query(mMysql, mActQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + } + else{ + mErrorLog->Log(eqEmuLogBoth, "No mysql connection for delete"); + } + + for(unsigned int i = 0; i < ActivitiesSelectionList->GetCount(); i++) + { + eqtask_activity_id * id = (eqtask_activity_id*)ActivitiesSelectionList->GetClientData(i); + if(id){ + if(id->activityid == selAct->activityid && id->id == selAct->id) + { + if(openedActivity.activityid == selAct->activityid && openedActivity.id == selAct->id) + { + mActText1->Clear(); + mActText1->Disable(); + + mActText2->Clear(); + mActText2->Disable(); + + mActText3->Clear(); + mActText3->Disable(); + + mActivityZone->Select(0); + mActivityZone->Disable(); + + mActivityOptional->SetValue(false); + mActivityOptional->Disable(); + + mActID->Clear(); + mActID->Disable(); + + mActStep->Clear(); + mActStep->Disable(); + + mActType->Select(0); + mActType->Disable(); + + mActDeliver->Clear(); + mActDeliver->Disable(); + + mActMethod->Select(2); + mActMethod->Disable(); + + mActGoalID->Clear(); + mActGoalID->Disable(); + + mActGoalCount->Clear(); + mActGoalCount->Disable(); + } + + mErrorLog->Log(eqEmuLogBoth, "Erasing Activity Selection"); + ActivitiesSelectionList->Delete(i); + delete id; + mErrorLog->Log(eqEmuLogBoth, "Erased.."); + break; + } + } + } + + vector::iterator Iter; + for(Iter = taskActivitiesList.begin(); Iter!=taskActivitiesList.end(); Iter++) + { + eqtask_activities curAct = *Iter; + if(curAct.activityId == aid && curAct.id == tid){ + mErrorLog->Log(eqEmuLogBoth, "Erasing Activity Iterator"); + taskActivitiesList.erase(Iter); + mErrorLog->Log(eqEmuLogBoth, "Erased.."); + break; + } + } + } + else + { + mErrorLog->Log(eqEmuLogBoth, "No activity selected for delete."); + } +} + +void MainFrame::PopulateActivities() +{ + int taskId = -1; + mErrorLog->Log(eqEmuLogBoth, "Populating activities..."); + vector::iterator Iter; + int index = 0; + for(Iter = taskList.begin(); Iter != taskList.end(); Iter++) + { + if(index == openedIndex){ + eqtask eqt = *Iter; + taskId = eqt.id; + break; + } + index++; + } + + if(taskId > -1){ + vector::iterator aIter; + for(aIter = taskActivitiesList.begin(); aIter != taskActivitiesList.end(); aIter++) + { + eqtask_activities eqta = *aIter; + if(eqta.id == taskId){ + eqtask_activity_id * eqaid = new eqtask_activity_id; + + eqaid->id = eqta.id; + eqaid->step = eqta.step; + eqaid->activityid = eqta.activityId; + + wxString taskStr; + taskStr.Printf("Step: %u", eqta.step); + ActivitiesSelectionList->Append(taskStr, (void*)eqaid); + taskStr.clear(); + } + } + } +} + +void MainFrame::ClearActivities() +{ + mErrorLog->Log(eqEmuLogBoth, "Clearing activities..."); + for(unsigned int x = 0; x < ActivitiesSelectionList->GetCount(); x++) + { + eqtask_activity_id *eqaid = (eqtask_activity_id*)ActivitiesSelectionList->GetClientData(x); + if(eqaid) + { + mErrorLog->Log(eqEmuLogBoth, "Delete of activity index: %u (%u:%u)", x, eqaid->id, eqaid->step); + delete eqaid; + eqaid = NULL; + } + } + + ActivitiesSelectionList->Clear(); +} + +void MainFrame::FillActivity(int id, int activityid, int step) +{ + if(id > -1){ + vector::iterator Iter; + for(Iter = taskActivitiesList.begin(); Iter != taskActivitiesList.end();Iter++) + { + eqtask_activities eqta = *Iter; + if(eqta.id == id && eqta.activityId == activityid && eqta.step == step) + { + wxString actStr; + actStr.Clear(); + actStr.Printf("%s", eqta.text1); + mActText1->Clear(); + mActText1->AppendText(actStr); + actStr.Clear(); + + actStr.Printf("%s", eqta.text2); + mActText2->Clear(); + mActText2->AppendText(actStr); + actStr.Clear(); + + actStr.Printf("%s", eqta.text3); + mActText3->Clear(); + mActText3->AppendText(actStr); + actStr.Clear(); + + SetZoneSelectionByIdActivity(eqta.zoneid); + mActivityOptional->SetValue(eqta.optional); + + actStr.Printf("%u", eqta.activityId); + mActID->Clear(); + mActID->AppendText(actStr); + actStr.Clear(); + + actStr.Printf("%u", eqta.step); + mActStep->Clear(); + mActStep->AppendText(actStr); + actStr.Clear(); + + actStr.Printf("%u", eqta.deliverToNpc); + mActDeliver->Clear(); + mActDeliver->AppendText(actStr); + actStr.Clear(); + + actStr.Printf("%u", eqta.goalid); + mActGoalID->Clear(); + mActGoalID->AppendText(actStr); + actStr.Clear(); + + actStr.Printf("%u", eqta.goalcount); + mActGoalCount->Clear(); + mActGoalCount->AppendText(actStr); + actStr.Clear(); + + if(eqta.goalmethod >= 0 && eqta.goalmethod <= 2) + mActMethod->Select(eqta.goalmethod); + else + mActMethod->Select(2); + + if(eqta.activityType > 0 && eqta.activityType < 12) + { + mActType->Select(eqta.activityType); + } + else + { + if(eqta.activityType == 100){ + mActType->Select(12); + } + else if(eqta.activityType == 999) + { + mActType->Select(13); + } + else{ + mActType->Select(0); + } + } + + wxCommandEvent evnt; + evnt.SetInt(eqta.activityType); + ActivityChoiceChange(evnt); + return; + } + } + } +} + +void MainFrame::ActivityChoiceChange(wxCommandEvent& event) +{ + mErrorLog->Log(eqEmuLogBoth, "Activity Choice Change: %u", event.GetInt()); + switch(event.GetInt()){ + case 1: + mActInfoText->SetLabel("Deliver:\nDeliver a number of items to a NPC\n" + "text1: NPC Name\n" + "text2: Item Name" + "\n\nRemember: text3 can be used to \noverwrite text1 or text2"); + break; + case 2: + mActInfoText->SetLabel("Kill:\nKill a number of NPCs\n" + "text1: NPC Name\n" + "\n\nRemember: text3 can be used to \noverwrite text1 or text2"); + break; + case 3: + mActInfoText->SetLabel("Loot:\nLoot a number of items\n" + "text1: Item Name\n" + "\n\nRemember: text3 can be used to \noverwrite text1 or text2"); + break; + case 4: + mActInfoText->SetLabel("Speak To:\nSpeak with a specific NPC\n" + "text1: NPC Name\n" + "\n\nRemember: text3 can be used to \noverwrite text1 or text2"); + break; + case 5: + mActInfoText->SetLabel("Explore:\nExplore a certain area of the game\n" + "text1: Area Description\n" + "\n\nRemember: text3 can be used to \noverwrite text1 or text2"); + break; + case 6: + mActInfoText->SetLabel("Tradeskill:\nCreate a number of items\n" + "text1: Item Name\n" + "\n\nRemember: text3 can be used to \noverwrite text1 or text2"); + break; + case 7: + mActInfoText->SetLabel("Fish:\nObtain a number of items with fishing\n" + "text1: Item Name\n" + "\n\nRemember: text3 can be used to \noverwrite text1 or text2"); + break; + case 8: + mActInfoText->SetLabel("Forage:\nObtain a number of items via foraging\n" + "text1: Item Name\n" + "\n\nRemember: text3 can be used to \noverwrite text1 or text2"); + break; + case 11: + mActInfoText->SetLabel("Touch:\nEnter a certain zone\n" + "text1: Zone Name\n" + "\n\nRemember: text3 can be used to \noverwrite text1 or text2"); + break; + case 10: + case 9: + mActInfoText->SetLabel("Use:\nNot currently in use\n"); + break; + case 12: + mActInfoText->SetLabel("Give Cash:\nGive money to a certain npc\n" + "text3: Custom Description"); + break; + case 13: + mActInfoText->SetLabel("Custom:\nCustom task handled by the quest\nsystem\n" + "text3: Custom Description"); + break; + default: + mActInfoText->SetLabel("Unknown Activity Type:\nThis is not a valid activity type"); + break; + + } +} + +void MainFrame::SaveActivity(wxCommandEvent& event) +{ + mErrorLog->Log(eqEmuLogBoth, "Save activity requested..."); + + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Mysql not connected for save activity"); + return; + } + + if(openedActivity.activityid < 0 || openedActivity.id < 0){ + mErrorLog->Log(eqEmuLogBoth, "Activity not valid for save."); + return; + } + + eqtask_activities ourAct; + + vector::iterator Iter; + for(Iter = taskActivitiesList.begin(); Iter != taskActivitiesList.end(); Iter++) + { + if((*Iter).activityId == openedActivity.activityid && (*Iter).id == openedActivity.id) + { + ourAct = *Iter; + break; + } + } + + int delid = openedActivity.activityid; + bool canUpdate = false; + wxString getStr; + + getStr = mActText1->GetValue(); + strcpy(ourAct.text1, getStr.mb_str()); + getStr.Clear(); + + getStr = mActText2->GetValue(); + strcpy(ourAct.text2, getStr.mb_str()); + getStr.Clear(); + + getStr = mActText3->GetValue(); + strcpy(ourAct.text3, getStr.mb_str()); + getStr.Clear(); + + int * i = (int*)mActivityZone->GetClientData(mActivityZone->GetSelection()); + ourAct.zoneid = *i; + + ourAct.optional = mActivityOptional->GetValue(); + + getStr = mActID->GetValue(); + ourAct.activityId = atoi(getStr.mb_str()); + getStr.Clear(); + + getStr = mActStep->GetValue(); + ourAct.step = atoi(getStr.mb_str()); + getStr.Clear(); + + int type = mActType->GetSelection(); + if(type > 0 && type < 12){ + ourAct.activityType = type; + } + else{ + if(type == 12){ + ourAct.activityType = 100; + } + else if(type == 13){ + ourAct.activityType = 999; + } + else + ourAct.activityType = 0; + } + + getStr = mActDeliver->GetValue(); + ourAct.deliverToNpc = atoi(getStr.mb_str()); + getStr.Clear(); + + ourAct.goalmethod = mActMethod->GetSelection(); + + getStr = mActGoalID->GetValue(); + ourAct.goalid = atoi(getStr.mb_str()); + getStr.Clear(); + + getStr = mActGoalCount->GetValue(); + ourAct.goalcount = atoi(getStr.mb_str()); + getStr.Clear(); + + if(ourAct.activityId == openedActivity.activityid && ourAct.id == openedActivity.id){ + canUpdate = true; + mErrorLog->Log(eqEmuLogBoth, "Can use UPDATE."); + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Cannot use UPDATE must replace instead"); + } + + if(canUpdate) + { + char * mQuery = 0; + MakeAnyLenString(&mQuery, "UPDATE activities SET step=%u, activitytype=%u, text1='%s', text2='%s', text3='%s', goalid=%u, goalmethod=%u,goalcount=%u, delivertonpc=%u, zoneid=%u, optional=%u WHERE taskid=%u AND activityid =%u", + ourAct.step, ourAct.activityType, MakeStringSQLSafe(ourAct.text1).mb_str(), MakeStringSQLSafe(ourAct.text2).mb_str(), MakeStringSQLSafe(ourAct.text3).mb_str(), ourAct.goalid, ourAct.goalmethod, ourAct.goalcount, ourAct.deliverToNpc, ourAct.zoneid, ourAct.optional, ourAct.id, ourAct.activityId ); + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + } + else + { + char * mQuery = 0; + MakeAnyLenString(&mQuery, "DELETE FROM activities WHERE taskid=%u AND activityid=%u", ourAct.id, delid); + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + + MakeAnyLenString(&mQuery, "INSERT INTO `activities` (`taskid`,`activityid`,`step`,`activitytype`,`text1`,`text2`,`text3`,`goalid`,`goalmethod`,`goalcount`,`delivertonpc`,`zoneid`,`optional`) VALUES (%u,%u,%u,%u,'%s','%s','%s',%u,%u,%u,%u,%u,%u)", + ourAct.id, ourAct.activityId, ourAct.step, ourAct.activityType, MakeStringSQLSafe(ourAct.text1).mb_str(), MakeStringSQLSafe(ourAct.text2).mb_str(), MakeStringSQLSafe(ourAct.text3).mb_str(), ourAct.goalid, ourAct.goalmethod, ourAct.goalcount, ourAct.deliverToNpc, ourAct.zoneid, ourAct.optional); + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + } + + (*Iter).activityId = ourAct.activityId; + (*Iter).activityType = ourAct.activityType; + (*Iter).deliverToNpc = ourAct.deliverToNpc; + (*Iter).goalcount = ourAct.goalcount; + (*Iter).goalid = ourAct.goalid; + (*Iter).goalmethod = ourAct.goalmethod; + (*Iter).id = ourAct.id; + (*Iter).optional = ourAct.optional; + (*Iter).step = ourAct.step; + strcpy((*Iter).text1, ourAct.text1); + strcpy((*Iter).text2, ourAct.text2); + strcpy((*Iter).text3, ourAct.text3); + (*Iter).zoneid = ourAct.zoneid; + + int ourIndex = 0; + for(unsigned int Index = 0 ;Index < ActivitiesSelectionList->GetCount(); Index++) + { + eqtask_activity_id *eqta; + eqta = (eqtask_activity_id*)ActivitiesSelectionList->GetClientData(Index); + if(eqta){ + if(eqta->activityid == openedActivity.activityid && eqta->id == openedActivity.id){ + ourIndex = Index; + break; + } + } + else{ + mErrorLog->Log(eqEmuLogBoth, "NULL eqta, index %u", Index); + } + } + + getStr.Clear(); + getStr.Printf("Step: %u", (*Iter).step); + eqtask_activity_id * eqt = (eqtask_activity_id*)ActivitiesSelectionList->GetClientData(ourIndex); + eqt->activityid = ourAct.activityId; + eqt->id = ourAct.id; + eqt->step = ourAct.step; + ActivitiesSelectionList->SetString(ourIndex, getStr); + ActivitiesSelectionList->SetClientData(ourIndex, (void*)eqt); + + + openedActivity.activityid = ourAct.activityId; + openedActivity.id = ourAct.id; + openedActivity.step = ourAct.step; + + mErrorLog->Log(eqEmuLogBoth, "Save finished."); +} + +void MainFrame::ContextMenuActivityList() +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Context menu cannot open, not connected to db"); + return; + } + + wxMenu *mMenu; + mMenu = new wxMenu(); + + mMenu->Append(MENU_NewActivity, wxT("New Activity"), wxT("Creates a new activity")); + mMenu->Append(MENU_DeleteActivity, wxT("Delete Activity"), wxT("Deletes the selected activity")); + mMenu->AppendSeparator(); + mMenu->Append(MENU_SaveActivity, wxT("Save Activity"), wxT("Saves the opened activity")); + + PopupMenu(mMenu); + delete mMenu; +} \ No newline at end of file diff --git a/utils/TaskMaster/base.cpp b/utils/TaskMaster/base.cpp new file mode 100644 index 000000000..af119a961 --- /dev/null +++ b/utils/TaskMaster/base.cpp @@ -0,0 +1,672 @@ +#include +#include +#include +#include +#include +#include "tasks.h" +#include "utility.h" +#include "base.h" +#include "ErrorLog.h" + +using namespace std; + +//Defined Events +BEGIN_EVENT_TABLE ( MainFrame, wxFrame ) +EVT_MENU(MENU_Connect, MainFrame::Connect) +EVT_MENU(MENU_Quit, MainFrame::Quit) +EVT_MENU(MENU_NewTask, MainFrame::NewTask) +EVT_MENU(MENU_DeleteTask, MainFrame::DeleteTask) +EVT_MENU(MENU_SaveTask, MainFrame::SaveTask) +EVT_MENU(MENU_NewActivity, MainFrame::NewActivity) +EVT_MENU(MENU_DeleteActivity, MainFrame::DeleteActivity) +EVT_MENU(MENU_SaveActivity, MainFrame::SaveActivity) +EVT_MENU(MENU_About, MainFrame::About) +EVT_LISTBOX_DCLICK(LIST_Click, MainFrame::ListBoxDoubleClick) +EVT_LISTBOX(LIST_Click, MainFrame::ListBoxSimpleSelect) +EVT_BUTTON(BUTTON_Reward, MainFrame::OnRewardButton ) +EVT_LISTBOX_DCLICK(LIST_Click_Activities, MainFrame::ActivitiesListBoxDoubleClick) +EVT_LISTBOX(LIST_Click_Activities, MainFrame::ActivitiesListBoxSimpleSelect) +EVT_CHOICE(CHOICE_ActivityChoiceChange, MainFrame::ActivityChoiceChange) +EVT_CONTEXT_MENU(MainFrame::ContextClick) +EVT_LISTBOX_DCLICK(LIST_Click_Goals, MainFrame::GoalsListBoxDoubleClick) +EVT_MENU(MENU_NewGoal, MainFrame::NewGoal) +EVT_MENU(MENU_DeleteGoal, MainFrame::DeleteGoal) +EVT_MENU(MENU_AddGoalItem, MainFrame::NewGoalValue) +EVT_MENU(MENU_DeleteGoalItem, MainFrame::DeleteGoalValue) +EVT_MENU(MENU_ModifyGoalItem, MainFrame::ChangeGoalValue) +EVT_BUTTON(BUTTON_AddGoalItem, MainFrame::NewGoalValue) +EVT_BUTTON(BUTTON_DeleteGoalItem, MainFrame::DeleteGoalValue) +EVT_BUTTON(BUTTON_ModifyGoalItem, MainFrame::ChangeGoalValue) +EVT_MENU(MENU_NewProximity, MainFrame::NewProximity) +EVT_MENU(MENU_DeleteProximity, MainFrame::DeleteProximity) +EVT_MENU(MENU_SaveProximity, MainFrame::SaveProximity) +EVT_LISTBOX_DCLICK(LIST_Click_Proximity, MainFrame::ProximityListBoxDoubleClick) +END_EVENT_TABLE() + +//Our main() thread +IMPLEMENT_APP_CONSOLE(MainApp) + +bool MainApp::OnInit() +{ + MainFrame *MainWin = new MainFrame(wxT("Task Master"), wxDefaultPosition, //wxPoint(1,1), + wxSize(800, 600)); // Create an instance of our frame, or window + MainWin->Show(TRUE); // show the window + SetTopWindow(MainWin);// and finally, set it as the main window + + return TRUE; +} + +MainFrame::MainFrame(const wxString& title, + const wxPoint& pos, const wxSize& size) + : wxFrame((wxFrame *) NULL, -1, title, pos, size, (wxCAPTION|wxCLOSE_BOX|wxMINIMIZE_BOX|wxSYSTEM_MENU|wxCLIP_CHILDREN|wxTAB_TRAVERSAL)) +{ + mErrorLog = new EQEmuErrorLog; + CreateStatusBar(1, 0); + + mErrorLog->Log(eqEmuLogBoth, "Creating Menus."); + MainMenu = new wxMenuBar(); + wxMenu *FileMenu = new wxMenu(); + + FileMenu->Append(MENU_Connect, wxT("&Connect"), wxT("Connect to the Database")); + FileMenu->AppendSeparator(); + FileMenu->Append(MENU_Quit, wxT("&Quit"), wxT("Quit")); + MainMenu->Append(FileMenu, wxT("&Main")); + + wxMenu *EditMenu = new wxMenu(); + + EditMenu->Append(MENU_NewTask, wxT("&New Task"), wxT("Creates a new task")); + EditMenu->Append(MENU_DeleteTask, wxT("&Delete Task"), wxT("Deletes the selected task")); + EditMenu->AppendSeparator(); + EditMenu->Append(MENU_SaveTask, wxT("&Save Task"), wxT("Saves the opened task")); + MainMenu->Append(EditMenu, wxT("&Tasks")); + + wxMenu *ActMenu = new wxMenu(); + ActMenu->Append(MENU_NewActivity, wxT("&New Activity"), wxT("Create a new activity for this task")); + ActMenu->Append(MENU_DeleteActivity, wxT("&Delete Activity"), wxT("Deletes the selected activity for this task")); + ActMenu->AppendSeparator(); + ActMenu->Append(MENU_SaveActivity, wxT("&Save Activity"), wxT("Saves the opened task activity")); + MainMenu->Append(ActMenu, wxT("&Activities")); + + wxMenu *GoalsMenu = new wxMenu(); + GoalsMenu->Append(MENU_NewGoal, wxT("&New Goal"), wxT("Creates a new goal list entry")); + GoalsMenu->Append(MENU_DeleteGoal, wxT("&Delete Goal"), wxT("Deletes the selected goal list entry")); + GoalsMenu->AppendSeparator(); + GoalsMenu->Append(MENU_AddGoalItem, wxT("N&ew Goal Value"), wxT("Saves the opened task activity")); + GoalsMenu->Append(MENU_DeleteGoalItem, wxT("De&lete Goal Value"), wxT("Deletes the selected goal value entry")); + GoalsMenu->Append(MENU_ModifyGoalItem, wxT("M&odify Goal Value"), wxT("Attempts to change the selected goal value entry")); + MainMenu->Append(GoalsMenu, wxT("&Goals")); + + wxMenu *ProximityMenu = new wxMenu(); + ProximityMenu->Append(MENU_NewProximity, wxT("&New Proximity"), wxT("Creates a new proximity entry")); + ProximityMenu->Append(MENU_DeleteProximity, wxT("&Delete Proximity"), wxT("Deletes the selected proximity entry")); + ProximityMenu->AppendSeparator(); + ProximityMenu->Append(MENU_SaveProximity, wxT("&Save Proximity"), wxT("Saves the opened proximity entry")); + MainMenu->Append(ProximityMenu, wxT("&Proximity")); + + wxMenu *AboutMenu = new wxMenu(); + AboutMenu->Append(MENU_About, wxT("A&bout"), wxT("Program Information")); + MainMenu->Append(AboutMenu, wxT("A&bout")); + + SetMenuBar(MainMenu); + + mErrorLog->Log(eqEmuLogBoth, "Creating Sizer."); + BoxSizer1 = new wxBoxSizer(wxHORIZONTAL); + + mErrorLog->Log(eqEmuLogBoth, "Creating Item List Box."); + ItemSelectionList = new wxListBox(this, LIST_Click, wxDefaultPosition, wxSize( -1,-1 ), 0, NULL, 0); + + mErrorLog->Log(eqEmuLogBoth, "Adding Item List Box to Sizer."); + BoxSizer1->Add(ItemSelectionList, 0, wxALIGN_TOP|wxALL|wxEXPAND, 5); + + mErrorLog->Log(eqEmuLogBoth, "Creating Notebook Backing."); + MainNotebookBack = new wxNotebook(this, wxID_ANY, wxDefaultPosition, wxSize( -1,-1 ), 0); + + mErrorLog->Log(eqEmuLogBoth, "Creating Panels."); + MainPanel1 = new wxPanel(MainNotebookBack, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + MainPanel2 = new wxPanel(MainNotebookBack, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + MainPanel3 = new wxPanel(MainNotebookBack, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + MainPanel4 = new wxPanel(MainNotebookBack, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + MainPanel5 = new wxPanel(MainNotebookBack, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + + mErrorLog->Log(eqEmuLogBoth, "Adding Panels to Notebook Backing."); + MainNotebookBack->AddPage(MainPanel1, wxT("General"), true); + MainNotebookBack->AddPage(MainPanel2, wxT("Activities"), false); + MainNotebookBack->AddPage(MainPanel3, wxT("Goals"), false); + MainNotebookBack->AddPage(MainPanel4, wxT("Proximities"), false); + MainNotebookBack->AddPage(MainPanel5, wxT("Task Sets"), false); + + /*General Page Elements*/ + mErrorLog->Log(eqEmuLogBoth, "Adding elements to General Page"); + //name label + text + mTaskNameLabel = new wxStaticText(MainPanel1, -1, "Name:", wxPoint(0,2), wxDefaultSize); + mTaskName = new wxTextCtrl(MainPanel1, -1, wxT("Task Name"), wxPoint(65,0), wxSize(300,20)); + mTaskName->SetMaxLength(100); + mTaskName->Disable(); + + //desc label + text + mTaskDescLabel = new wxStaticText(MainPanel1, -1, "Desc:", wxPoint(0,32), wxDefaultSize); + mTaskDesc = new wxTextCtrl(MainPanel1, -1, wxT("Task Description"), wxPoint(65,30), wxSize(300,200), wxTE_MULTILINE); + mTaskDesc->SetMaxLength(2047); + mTaskDesc->Disable(); + + //Min Level label + text + mTaskMinLvlLabel = new wxStaticText(MainPanel1, -1, "Min Lvl:", wxPoint(375,2), wxDefaultSize); + mTaskMinLvl = new wxTextCtrl(MainPanel1, -1, wxT("0"), wxPoint(450,0), wxSize(25,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mTaskMinLvl->SetMaxLength(2); + mTaskMinLvl->Disable(); + + //Max Level label + text + mTaskMaxLvlLabel = new wxStaticText(MainPanel1, -1, "Max Lvl:", wxPoint(500,2), wxDefaultSize); + mTaskMaxLvl = new wxTextCtrl(MainPanel1, -1, wxT("0"), wxPoint(580,0), wxSize(25,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mTaskMaxLvl->SetMaxLength(2); + mTaskMaxLvl->Disable(); + + //duration + mTaskDurationLabel = new wxStaticText(MainPanel1, -1, "Duration:", wxPoint(375,32), wxDefaultSize); + mTaskDuration = new wxTextCtrl(MainPanel1, -1, wxT("0"), wxPoint(450,30), wxSize(70,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mTaskDuration->SetMaxLength(9); + mTaskDuration->Disable(); + + //box to enclose rewards + mRewardsBox = new wxStaticBox(MainPanel1, -1, "Rewards", wxPoint(0,300), wxSize(644,210)); + mRewardNameLabel = new wxStaticText(mRewardsBox, -1, "Reward:", wxPoint(10,17), wxDefaultSize); + mRewardName = new wxTextCtrl(mRewardsBox, -1, wxT(""), wxPoint(80,15), wxSize(280,20)); + mRewardName->SetMaxLength(64); + mRewardName->Disable(); + + mRewardIDLabel = new wxStaticText(mRewardsBox, -1, "Reward ID:", wxPoint(10,47), wxDefaultSize); + mRewardID = new wxTextCtrl(mRewardsBox, -1, wxT("0"), wxPoint(160,45), wxSize(70,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mRewardID->SetMaxLength(9); + mRewardID->Disable(); + + mRewardCashLabel = new wxStaticText(mRewardsBox, -1, "Money Rewarded:", wxPoint(10,77), wxDefaultSize); + mRewardCash = new wxTextCtrl(mRewardsBox, -1, wxT("0"), wxPoint(160,75), wxSize(70,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mRewardCash->SetMaxLength(9); + mRewardCash->Disable(); + + mRewardXPLabel = new wxStaticText(mRewardsBox, -1, "Experience Rewarded:", wxPoint(10,107), wxDefaultSize); + mRewardXP = new wxTextCtrl(mRewardsBox, -1, wxT("0"), wxPoint(160,105), wxSize(70,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mRewardXP->SetMaxLength(9); + mRewardXP->Disable(); + + mRewardMethodLabel = new wxStaticText(mRewardsBox, -1, "Reward Method:", wxPoint(10,137), wxDefaultSize); + mRewardMethod = new wxChoice(mRewardsBox, -1, wxPoint(160, 135), wxDefaultSize); + wxString mRewardMethodStr; + mRewardMethodStr.Printf("%s", "(0) Single Item Id"); + mRewardMethod->Append(mRewardMethodStr); + mRewardMethodStr.clear(); + mRewardMethodStr.Printf("%s", "(1) List from Goal List"); + mRewardMethod->Append(mRewardMethodStr); + mRewardMethodStr.clear(); + mRewardMethodStr.Printf("%s", "(2) Perl Quest Reward"); + mRewardMethod->Append(mRewardMethodStr); + mRewardMethod->Select(0); + mRewardMethod->Disable(); + + ShowRewardItems = new wxListBox(mRewardsBox, -1, wxPoint(375, 40), wxSize(250,160), 0, NULL, 0); + ShowRewardItems->Disable(); + + RefreshItems = new wxButton(mRewardsBox,BUTTON_Reward,"Refresh Item List", wxPoint(375,10), wxSize(250,25)); + RefreshItems->Disable(); + + mStartZoneLabel = new wxStaticText(MainPanel1, -1, "Start Zone:", wxPoint(375,62), wxDefaultSize); + mStartZone = new wxChoice(MainPanel1, -1, wxPoint(460, 60), wxDefaultSize, 0, NULL, wxCB_SORT); + mStartZone->Disable(); + + mTaskRepeatable = new wxCheckBox(MainPanel1, -1, "Repeatable:", wxPoint(375, 87), wxDefaultSize, wxCHK_2STATE | wxALIGN_RIGHT); + mTaskRepeatable->Disable(); + + /*General Page Elements End*/ + + /*Activities Page Elements*/ + mErrorLog->Log(eqEmuLogBoth, "Adding elements to Activities Page"); + ActivitiesSelectionList = new wxListBox(MainPanel2, LIST_Click_Activities, wxPoint(0,0), wxSize(100,510), 0, NULL, 0); + ActivitiesSelectionList->Disable(); + + + mActText1Label = new wxStaticText(MainPanel2, -1, "Text 1:", wxPoint(105,2), wxDefaultSize); + mActText1 = new wxTextCtrl(MainPanel2, -1, wxT(""), wxPoint(185,0), wxSize(180,20), wxTE_DONTWRAP); + mActText1->SetMaxLength(64); + mActText1->Disable(); + + mActText2Label = new wxStaticText(MainPanel2, -1, "Text 2:", wxPoint(105,22), wxDefaultSize); + mActText2 = new wxTextCtrl(MainPanel2, -1, wxT(""), wxPoint(185,20), wxSize(180,20), wxTE_DONTWRAP); + mActText2->SetMaxLength(64); + mActText2->Disable(); + + mActText3Label = new wxStaticText(MainPanel2, -1, "Text 3:", wxPoint(105,42), wxDefaultSize); + mActText3 = new wxTextCtrl(MainPanel2, -1, wxT(""), wxPoint(185,40), wxSize(180,60), wxTE_MULTILINE); + mActText3->SetMaxLength(128); + mActText3->Disable(); + + mActivityZoneLabel = new wxStaticText(MainPanel2, -1, "Zone:", wxPoint(380,2), wxDefaultSize); + mActivityZone = new wxChoice(MainPanel2, -1, wxPoint(440, 0), wxDefaultSize, 0, NULL, wxCB_SORT); + mActivityZone->Disable(); + + mActivityOptional = new wxCheckBox(MainPanel2, -1, "Optional:", wxPoint(580, 2), wxDefaultSize, wxCHK_2STATE | wxALIGN_RIGHT); + mActivityOptional->Disable(); + + mActIDLabel = new wxStaticText(MainPanel2, -1, "Activity ID:", wxPoint(105,107), wxDefaultSize); + mActID = new wxTextCtrl(MainPanel2, -1, wxT(""), wxPoint(185,105), wxSize(25,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mActID->SetMaxLength(2); + mActID->Disable(); + + mActStepLabel = new wxStaticText(MainPanel2, -1, "Step:", wxPoint(105,129), wxDefaultSize); + mActStep = new wxTextCtrl(MainPanel2, -1, wxT(""), wxPoint(185,127), wxSize(25,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mActStep->SetMaxLength(2); + mActStep->Disable(); + + mActTypeLabel = new wxStaticText(MainPanel2, -1, "Type:", wxPoint(105,155), wxDefaultSize); + mActType = new wxChoice(MainPanel2, CHOICE_ActivityChoiceChange, wxPoint(185, 153), wxDefaultSize); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(0) Unknown"); + mActType->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(1) Deliver"); + mActType->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(2) Kill"); + mActType->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(3) Loot"); + mActType->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(4) Speak To"); + mActType->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(5) Explore"); + mActType->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(6) Trade Skill"); + mActType->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(7) Fish"); + mActType->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(8) Forage"); + mActType->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(9) Use"); + mActType->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(10) Use"); + mActType->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(11) Touch"); + mActType->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(100) GiveCash"); + mActType->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(999) Custom"); + mActType->Append(mRewardMethodStr); + mActType->Disable(); + mActType->Select(0); + + mActInfo = new wxStaticBox(MainPanel2, -1, "Info", wxPoint(425,385), wxSize(219,125)); + mActInfoText = new wxStaticText(mActInfo, -1, "Unknown Activity Type:\nThis is not a valid activity type", wxPoint(25,25), wxDefaultSize); + + mActDeliverLabel = new wxStaticText(MainPanel2, -1, "Deliver to NPCID:", wxPoint(105,182), wxDefaultSize); + mActDeliver = new wxTextCtrl(MainPanel2, -1, wxT(""), wxPoint(215,180), wxSize(60,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mActDeliver->SetMaxLength(7); + mActDeliver->Disable(); + + mActMethodLabel = new wxStaticText(MainPanel2, -1, "Goal Method:", wxPoint(105,402), wxDefaultSize); + mActMethod = new wxChoice(MainPanel2, -1, wxPoint(200, 400), wxDefaultSize); + + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(0) Single Goal ID"); + mActMethod->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(1) Goal ID List"); + mActMethod->Append(mRewardMethodStr); + mRewardMethodStr.Clear(); + mRewardMethodStr.Printf("%s", "(2) Perl Quest"); + mActMethod->Append(mRewardMethodStr); + mActMethod->Select(2); + mActMethod->Disable(); + + mActGoalIDLabel = new wxStaticText(MainPanel2, -1, "Goal ID:", wxPoint(105,432), wxDefaultSize); + mActGoalID = new wxTextCtrl(MainPanel2, -1, wxT(""), wxPoint(200,430), wxSize(60,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mActGoalID->SetMaxLength(7); + mActGoalID->Disable(); + + mActGoalCountLabel = new wxStaticText(MainPanel2, -1, "Goal Count:", wxPoint(105,457), wxDefaultSize); + mActGoalCount = new wxTextCtrl(MainPanel2, -1, wxT(""), wxPoint(200,455), wxSize(60,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mActGoalCount->SetMaxLength(8); + mActGoalCount->Disable(); + + /*Activities Page Elements End*/ + + /*Goals Page Elements*/ + mErrorLog->Log(eqEmuLogBoth, "Adding elements to Goals Page"); + GoalsSelectionList = new wxListBox(MainPanel3, LIST_Click_Goals, wxPoint(0,0), wxSize(100,510), 0, NULL, 0); + + GoalsValuesList = new wxListBox(MainPanel3, -1, wxPoint(150,50), wxSize(150,300), 0, NULL, 0); + GoalsValuesList->Disable(); + + mGoalsNewValueButton = new wxButton(MainPanel3, BUTTON_AddGoalItem, "Add Value", wxPoint(302,275), wxSize(100,-1)); + mGoalsNewValueButton->Disable(); + mGoalsDeleteValueButton = new wxButton(MainPanel3, BUTTON_DeleteGoalItem, "Delete Value", wxPoint(302,300), wxSize(100,-1)); + mGoalsDeleteValueButton->Disable(); + mGoalsChangeValueButton = new wxButton(MainPanel3, BUTTON_ModifyGoalItem, "Change Value", wxPoint(302,325), wxSize(100,-1)); + mGoalsChangeValueButton->Disable(); + /*Goals Page Elements End*/ + + /*Proximities Page Elements*/ + mErrorLog->Log(eqEmuLogBoth, "Adding elements to Proximities Page"); + ProximitySelectionList = new wxListBox(MainPanel4, LIST_Click_Proximity, wxPoint(0,0), wxSize(100,510), 0, NULL, 0); + + mProxIdLabel = new wxStaticText(MainPanel4, -1, "Id:", wxPoint(105,12), wxDefaultSize); + mProxId = new wxTextCtrl(MainPanel4, -1, wxT(""), wxPoint(150,10), wxSize(50,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mProxId->SetMaxLength(6); + mProxId->Disable(); + mProxMinxLabel = new wxStaticText(MainPanel4, -1, "MinX:", wxPoint(105,42), wxDefaultSize); + mProxMinx = new wxTextCtrl(MainPanel4, -1, wxT(""), wxPoint(150,40), wxSize(80,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mProxMinx->SetMaxLength(12); + mProxMinx->Disable(); + mProxMaxxLabel = new wxStaticText(MainPanel4, -1, "MaxX:", wxPoint(255,42), wxDefaultSize); + mProxMaxx = new wxTextCtrl(MainPanel4, -1, wxT(""), wxPoint(300,40), wxSize(80,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mProxMaxx->SetMaxLength(12); + mProxMaxx->Disable(); + mProxMinyLabel = new wxStaticText(MainPanel4, -1, "MinY:", wxPoint(105,72), wxDefaultSize); + mProxMiny = new wxTextCtrl(MainPanel4, -1, wxT(""), wxPoint(150,70), wxSize(80,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mProxMiny->SetMaxLength(12); + mProxMiny->Disable(); + mProxMaxyLabel = new wxStaticText(MainPanel4, -1, "MaxY:", wxPoint(255,72), wxDefaultSize); + mProxMaxy = new wxTextCtrl(MainPanel4, -1, wxT(""), wxPoint(300,70), wxSize(80,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mProxMaxy->SetMaxLength(12); + mProxMaxy->Disable(); + mProxMinzLabel = new wxStaticText(MainPanel4, -1, "MinZ:", wxPoint(105,102), wxDefaultSize); + mProxMinz = new wxTextCtrl(MainPanel4, -1, wxT(""), wxPoint(150,100), wxSize(80,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mProxMinz->SetMaxLength(12); + mProxMinz->Disable(); + mProxMaxzLabel = new wxStaticText(MainPanel4, -1, "MaxZ:", wxPoint(255,102), wxDefaultSize); + mProxMaxz = new wxTextCtrl(MainPanel4, -1, wxT(""), wxPoint(300,100), wxSize(80,20), wxTE_DONTWRAP, wxTextValidator(wxFILTER_NUMERIC)); + mProxMaxz->SetMaxLength(12); + mProxMaxz->Disable(); + mProxZoneLabel = new wxStaticText(MainPanel4, -1, "Zone:", wxPoint(105,202), wxDefaultSize); + mProxZone = new wxChoice(MainPanel4, -1, wxPoint(150, 200), wxDefaultSize); + mProxZone->Disable(); + /*Proximities Page Elements End*/ + + mErrorLog->Log(eqEmuLogBoth, "Adding Notebook to Sizer."); + BoxSizer1->Add(MainNotebookBack, 1, wxALIGN_TOP|wxEXPAND, 5); + + mErrorLog->Log(eqEmuLogBoth, "Setting Frame Sizer."); + this->SetSizer(BoxSizer1); + mErrorLog->Log(eqEmuLogBoth, "Calculating Frame Layout."); + this->Layout(); + + selectedIndex = -1; //nothing selected + openedIndex = -1; //nothing opened + highestIndex = 0; + openedActivity.activityid = -1; + openedActivity.id = -1; + openedActivity.step = -1; + openedGoal = 0; + openedProximity.exploreid = 0xFFFFFFFF; + openedProximity.zoneid = 0xFFFFFFFF; + + //database setting init. + mMysql = NULL; + memset(server,0,256); + memset(user,0,256); + memset(password,0,256); + memset(database,0,256); + if(!GetDatabaseSettings()) + Close(TRUE); +} + +void MainFrame::Connect(wxCommandEvent& WXUNUSED(event)) +{ + if(mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Connect to database requested but database connection exists."); + return; + } + ItemSelectionList->Clear(); + mErrorLog->Log(eqEmuLogBoth, "Connect to database requested."); + + if(!mMysql){ + mMysql = mysql_init(NULL); + } + + if(mMysql){ + if (!mysql_real_connect(mMysql, server, + user, password, database, 0, NULL, 0)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Connection Error: %s", mysql_error(mMysql)); + Close(TRUE); + return; + } + + if(!LoadTasks()){ + mysql_close(mMysql); + mErrorLog->Log(eqEmuLogBoth, "Failed to load tasks, exiting."); + Close(TRUE); + return; + } + + if(!LoadGoals()){ + mysql_close(mMysql); + mErrorLog->Log(eqEmuLogBoth, "Failed to load goals, exiting."); + Close(TRUE); + return; + } + + if(!LoadActivities()){ + mysql_close(mMysql); + mErrorLog->Log(eqEmuLogBoth, "Failed to load activities, exiting."); + Close(TRUE); + return; + } + + if(!LoadItems()){ + mysql_close(mMysql); + mErrorLog->Log(eqEmuLogBoth, "Failed to load items, exiting."); + Close(TRUE); + return; + } + + if(!LoadZones()){ + mysql_close(mMysql); + mErrorLog->Log(eqEmuLogBoth, "Failed to load zones, exiting."); + Close(TRUE); + return; + } + + if(!LoadProximity()){ + mysql_close(mMysql); + mErrorLog->Log(eqEmuLogBoth, "Failed to load proximities, exiting."); + Close(TRUE); + return; + } + + PopulateGoals(); + PopulateProximity(); + } +} + +void MainFrame::Quit(wxCommandEvent& WXUNUSED(event)) +{ + mErrorLog->Log(eqEmuLogBoth, "Exit requested, exiting."); + delete mErrorLog; + mErrorLog = NULL; + mysql_close(mMysql); + + Close(TRUE); +} + +void MainFrame::ListBoxSimpleSelect(wxCommandEvent& event) +{ + selectedIndex = event.GetInt(); +} + +void MainFrame::ListBoxDoubleClick(wxCommandEvent& event) +{ + mErrorLog->Log(eqEmuLogBoth, "Double clicked on item %d.", event.GetInt()); + openedIndex = event.GetInt(); + + vector::iterator Iter; + Iter = taskList.begin(); + Iter += openedIndex; + eqtask mTask = *Iter; + + wxString mStr; + mStr.Printf("%s", mTask.title); + mTaskName->Clear(); + mTaskName->AppendText(mStr); + mTaskName->Enable(); + mStr.clear(); + + mStr.Printf("%s", mTask.desc); + mTaskDesc->Clear(); + mTaskDesc->AppendText(mStr); + mTaskDesc->Enable(); + mStr.clear(); + + mStr.Printf("%u", mTask.level_min); + mTaskMinLvl->Clear(); + mTaskMinLvl->AppendText(mStr); + mTaskMinLvl->Enable(); + mStr.clear(); + + mStr.Printf("%u", mTask.level_max); + mTaskMaxLvl->Clear(); + mTaskMaxLvl->AppendText(mStr); + mTaskMaxLvl->Enable(); + mStr.clear(); + + mStr.Printf("%u", mTask.duration); + mTaskDuration->Clear(); + mTaskDuration->AppendText(mStr); + mTaskDuration->Enable(); + mStr.clear(); + + mStr.Printf("%s", mTask.reward); + mRewardName->Clear(); + mRewardName->AppendText(mStr); + mRewardName->Enable(); + mStr.clear(); + + mStr.Printf("%u", mTask.rewardid); + mRewardID->Clear(); + mRewardID->AppendText(mStr); + mRewardID->Enable(); + mStr.clear(); + + mStr.Printf("%u", mTask.cashreward); + mRewardCash->Clear(); + mRewardCash->AppendText(mStr); + mRewardCash->Enable(); + mStr.clear(); + + mStr.Printf("%i", mTask.xpreward); + mRewardXP->Clear(); + mRewardXP->AppendText(mStr); + mRewardXP->Enable(); + mStr.clear(); + + mRewardMethod->Select(mTask.rewardmethod); + mRewardMethod->Enable(); + ShowRewardChange(mTask.rewardmethod, mTask.rewardid); + + mStartZone->Enable(); + SetZoneSelectionById(mTask.startzone); + + mTaskRepeatable->Enable(); + mTaskRepeatable->SetValue(mTask.repeatable); + + ClearActivities(); + ActivitiesSelectionList->Enable(); + PopulateActivities(); + openedActivity.activityid = -1; + openedActivity.id = -1; + openedActivity.step = -1; + + mActText1->Clear(); + mActText1->Disable(); + mActText2->Clear(); + mActText2->Disable(); + mActText3->Clear(); + mActText3->Disable(); + mActivityZone->Select(0); + mActivityZone->Disable(); + mActivityOptional->SetValue(false); + mActivityOptional->Disable(); + mActID->Clear(); + mActID->Disable(); + mActStep->Clear(); + mActStep->Disable(); + mActType->Select(0); + mActType->Disable(); + mActDeliver->Clear(); + mActDeliver->Disable(); + mActGoalID->Clear(); + mActGoalID->Disable(); + mActGoalCount->Clear(); + mActGoalCount->Disable(); + + mActMethod->Select(2); + mActMethod->Disable(); + + ShowRewardItems->Enable(); + RefreshItems->Enable(); +} + +void MainFrame::ContextClick(wxContextMenuEvent& event) +{ + mErrorLog->Log(eqEmuLogBoth, "Context Menu Requested"); + if(event.GetPosition() == wxDefaultPosition) + { + mErrorLog->Log(eqEmuLogBoth, "Context Menu Triggered From Keyboard."); + } + else{ + wxPoint p = event.GetPosition(); + mErrorLog->Log(eqEmuLogBoth, "Context Menu Triggered At (%d,%d)", p.x, p.y); + wxPoint stcp = ScreenToClient(p); + mErrorLog->Log(eqEmuLogBoth, "Context Menu Triggered At (%d,%d)(Adjusted for Client)", stcp.x, stcp.y); + + //big box on the side: holds tasks + if(stcp.x >= 7 && stcp.x <= 133) + { + if(stcp.y >=7 && stcp.y <= 258) + { + ContextMenuTaskList(); + } + } + + //small box on the 2nd tab: must be on top to work + if(stcp.x >= 146 && stcp.x <= 243) + { + if(stcp.y >=23 && stcp.y <= 529) + { + if(MainPanel2->IsShownOnScreen()) + ContextMenuActivityList(); + else if(MainPanel3->IsShownOnScreen()) + ContextMenuGoalList(); + else if(MainPanel4->IsShownOnScreen()) + ContextMenuProximity(); + } + } + + if(stcp.x >= 297 && stcp.x <= 442) + { + if(stcp.y >= 74 && stcp.y <= 368) + { + if(MainPanel3->IsShownOnScreen() && GoalsValuesList->IsEnabled()) + ContextMenuGoalValueList(); + } + } + } +} + +void MainFrame::About(wxCommandEvent& event) +{ + wxAboutDialogInfo info; + info.SetName(_("Task Master")); + info.SetVersion(_(TASK_MASTER_VERSION)); + info.SetDescription(_("Task Creation Tool")); + info.SetWebSite("www.eqemulator.net"); + + wxAboutBox(info); +} \ No newline at end of file diff --git a/utils/TaskMaster/base.h b/utils/TaskMaster/base.h new file mode 100644 index 000000000..58bb1100b --- /dev/null +++ b/utils/TaskMaster/base.h @@ -0,0 +1,255 @@ +#ifndef EQWX_BASE__H +#define EQWX_BASE__H +#include +#include +#include +#include +#include "ErrorLog.h" +#include "tasks.h" +#include "items.h" +#include + +#define TASK_MASTER_VERSION "1.0.8" + +class MainApp: public wxApp +{ +public: + virtual bool OnInit(); +}; + +class MainFrame: public wxFrame +{ +public: + MainFrame( const wxString& title, const wxPoint& pos, const wxSize& size ); + wxTextCtrl *MainEditBox; + wxMenuBar *MainMenu; + + //various elements + wxBoxSizer* BoxSizer1; + wxListBox *ItemSelectionList; + wxNotebook *MainNotebookBack; + + wxPanel *MainPanel1; + + /*General Panel Controls*/ + wxStaticText *mTaskNameLabel; + wxTextCtrl *mTaskName; + wxStaticText *mTaskDescLabel; + wxTextCtrl *mTaskDesc; + wxStaticText *mTaskMinLvlLabel; + wxTextCtrl *mTaskMinLvl; + wxStaticText *mTaskMaxLvlLabel; + wxTextCtrl *mTaskMaxLvl; + wxStaticText *mTaskDurationLabel; + wxTextCtrl *mTaskDuration; + wxStaticBox *mRewardsBox; + wxStaticText *mRewardNameLabel; + wxTextCtrl *mRewardName; + wxStaticText *mRewardIDLabel; + wxTextCtrl *mRewardID; + wxStaticText *mRewardCashLabel; + wxTextCtrl *mRewardCash; + wxStaticText *mRewardXPLabel; + wxTextCtrl *mRewardXP; + wxStaticText *mRewardMethodLabel; + wxChoice *mRewardMethod; + wxListBox *ShowRewardItems; + wxButton *RefreshItems; + wxStaticText *mStartZoneLabel; + wxChoice *mStartZone; + + wxCheckBox *mTaskRepeatable; + /*General Panel Controls End*/ + + wxPanel *MainPanel2; + + /*Activities Panel Controls*/ + wxListBox *ActivitiesSelectionList; + wxStaticText *mActText1Label; + wxTextCtrl *mActText1; + wxStaticText *mActText2Label; + wxTextCtrl *mActText2; + wxStaticText *mActText3Label; + wxTextCtrl *mActText3; + wxStaticText *mActivityZoneLabel; + wxChoice *mActivityZone; + wxCheckBox *mActivityOptional; + wxStaticText *mActIDLabel; + wxTextCtrl *mActID; + wxStaticText *mActStepLabel; + wxTextCtrl *mActStep; + wxStaticText *mActTypeLabel; + wxChoice *mActType; + wxStaticBox *mActInfo; + wxStaticText *mActInfoText; + wxStaticText *mActDeliverLabel; + wxTextCtrl *mActDeliver; + wxStaticText *mActMethodLabel; + wxChoice *mActMethod; + wxStaticText *mActGoalIDLabel; + wxTextCtrl *mActGoalID; + wxStaticText *mActGoalCountLabel; + wxTextCtrl *mActGoalCount; + /*Activities Panel Controls End*/ + + wxPanel *MainPanel3; + + /*Goals Panel Control Start*/ + wxListBox *GoalsSelectionList; + wxListBox *GoalsValuesList; + wxButton *mGoalsNewValueButton; + wxButton *mGoalsDeleteValueButton; + wxButton *mGoalsChangeValueButton; + /*Goals Panel Control End*/ + + wxPanel *MainPanel4; + + /*Proximity Panel Control Start*/ + wxListBox *ProximitySelectionList; + wxStaticText *mProxIdLabel; + wxTextCtrl *mProxId; + wxStaticText *mProxMinxLabel; + wxTextCtrl *mProxMinx; + wxStaticText *mProxMaxxLabel; + wxTextCtrl *mProxMaxx; + wxStaticText *mProxMinyLabel; + wxTextCtrl *mProxMiny; + wxStaticText *mProxMaxyLabel; + wxTextCtrl *mProxMaxy; + wxStaticText *mProxMinzLabel; + wxTextCtrl *mProxMinz; + wxStaticText *mProxMaxzLabel; + wxTextCtrl *mProxMaxz; + wxStaticText *mProxZoneLabel; + wxChoice *mProxZone; + /*Proximity Panel Control End*/ + + wxPanel *MainPanel5; + /*Task Set Panel Control Start*/ + /*Task Set Panel Control End*/ + + //error + EQEmuErrorLog *mErrorLog; + + /*database stuff*/ + MYSQL *mMysql; + char server[256]; + char user[256]; + char password[256]; + char database[256]; + bool GetDatabaseSettings(); + bool LoadTasks(); + bool LoadGoals(); + bool LoadActivities(); + bool LoadItems(); + bool LoadZones(); + bool LoadProximity(); + + //I use vectors for everything speed of adding/removing isn't an issue for me. + //Could use list for task, goals and activities if wanted. + std::vector taskList; + std::vector goalTaskList; + std::vector itemList; + std::vector taskActivitiesList; + std::vector taskZoneList; + std::vector taskProximityList; + + int selectedIndex; + int openedIndex; + unsigned int highestIndex; + eqtask_activity_id openedActivity; + int openedGoal; + eqtask_prox openedProximity; + + void Connect(wxCommandEvent& event); + void Save(wxCommandEvent& event); + void Quit(wxCommandEvent& event); + void NewTask(wxCommandEvent& event); + void DeleteTask(wxCommandEvent& event); + void SaveTask(wxCommandEvent& event); + void About(wxCommandEvent& event); + + void NewActivity(wxCommandEvent& event); + void DeleteActivity(wxCommandEvent& event); + void SaveActivity(wxCommandEvent& event); + + void ListBoxDoubleClick(wxCommandEvent& event); + void ListBoxSimpleSelect(wxCommandEvent& event); + void OnRewardButton(wxCommandEvent& event); + void ActivitiesListBoxSimpleSelect(wxCommandEvent& event); + void ActivitiesListBoxDoubleClick(wxCommandEvent& event); + void ActivityChoiceChange(wxCommandEvent& event); + void PopulateActivities(); + void FillActivity(int id, int activityid, int step); + + void GoalsListBoxDoubleClick(wxCommandEvent& event); + void PopulateGoals(); + void PopulateGoalValues(unsigned int goalid); + void ClearGoalValues(); + void NewGoal(wxCommandEvent& event); + void DeleteGoal(wxCommandEvent& event); + void NewGoalValue(wxCommandEvent& event); + void DeleteGoalValue(wxCommandEvent& event); + void ChangeGoalValue(wxCommandEvent& event); + + void ProximityListBoxDoubleClick(wxCommandEvent& event); + void NewProximity(wxCommandEvent& event); + void DeleteProximity(wxCommandEvent& event); + void SaveProximity(wxCommandEvent& event); + void PopulateProximity(); + void FillProximityValues(unsigned int zone, unsigned int explore); + + void ShowRewardChange(int rewardType, int rewardId); + void SetZoneSelectionById(int zid); + void SetZoneSelectionByIdActivity(int zid); + void SetZoneSelectionByIdProximity(int zid); + wxString MakeStringSQLSafe(const char * c); + + void ContextClick(wxContextMenuEvent& event); + void ContextMenuTaskList(); + void ContextMenuActivityList(); + void ContextMenuGoalList(); + void ContextMenuGoalValueList(); + void ContextMenuProximity(); + + + //we need to free the memory used when we change activites becase we attach it to + //the items in our activities table and don't have any other way to track them so + //Free it up before we call ActivitiesSelectionList->Clear(); + void ClearActivities(); + + DECLARE_EVENT_TABLE() +}; + +enum +{ + TEXT_Main = wxID_HIGHEST + 1, + MENU_Connect, + MENU_Quit, + MENU_NewTask, + MENU_DeleteTask, + MENU_SaveTask, + LIST_Click, + BUTTON_Reward, + LIST_Click_Activities, + MENU_NewActivity, + MENU_DeleteActivity, + MENU_SaveActivity, + CHOICE_ActivityChoiceChange, + MENU_About, + LIST_Click_Goals, + MENU_NewGoal, + MENU_DeleteGoal, + MENU_AddGoalItem, + MENU_DeleteGoalItem, + MENU_ModifyGoalItem, + BUTTON_AddGoalItem, + BUTTON_DeleteGoalItem, + BUTTON_ModifyGoalItem, + MENU_NewProximity, + MENU_DeleteProximity, + MENU_SaveProximity, + LIST_Click_Proximity, +}; + +#endif \ No newline at end of file diff --git a/utils/TaskMaster/base_utility.cpp b/utils/TaskMaster/base_utility.cpp new file mode 100644 index 000000000..181419b0e --- /dev/null +++ b/utils/TaskMaster/base_utility.cpp @@ -0,0 +1,442 @@ +#include +#include +#include +#include "tasks.h" +#include "utility.h" +#include "items.h" +#include "base.h" +#include "ErrorLog.h" + +using namespace std; + +bool MainFrame::GetDatabaseSettings(){ + FILE *mFile = NULL; + mFile = fopen("db.txt", "r"); + + //very little error checking + //we don't make sure the values in the file are valid + //or all there just that the file exists so: + //don't fuck this up. + if(mFile){ + //this isn't all that safe + char chunk[256]; + memset(chunk, 0, 256); + + fgets(chunk, 256, mFile); + strcpy(server, chunk); + for(int i = 0; i<256; i++){ + if(server[i] == '\n') + server[i] = '\0'; + } + memset(chunk, 0, 256); + + fgets(chunk, 256, mFile); + strcpy(database, chunk); + for(int i = 0; i<256; i++){ + if(database[i] == '\n') + database[i] = '\0'; + } + memset(chunk, 0, 256); + + fgets(chunk, 256, mFile); + strcpy(user, chunk); + for(int i = 0; i<256; i++){ + if(user[i] == '\n') + user[i] = '\0'; + } + memset(chunk, 0, 256); + + fgets(chunk, 256, mFile); + strcpy(password, chunk); + for(int i = 0; i<256; i++){ + if(password[i] == '\n') + password[i] = '\0'; + } + memset(chunk, 0, 256); + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Error opening file db.txt, could not read db settings."); + return false; + } + return true; +} + +bool MainFrame::LoadItems(){ + if(mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Loading Items..."); + unsigned int itemsLoaded = 0; + MYSQL_RES *res; + MYSQL_ROW row; + + if (mysql_query(mMysql, "SELECT name,id FROM items")) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Connection Error: %s", mysql_error(mMysql)); + return false; + } + res = mysql_use_result(mMysql); + while ((row = mysql_fetch_row(res)) != NULL){ + eqitem newIT; + strcpy(newIT.name, row[0]); + newIT.id = atoi(row[1]); + itemList.push_back(newIT); + itemsLoaded++; + } + mErrorLog->Log(eqEmuLogBoth, "%u Successfully Loaded Items", itemsLoaded); + mysql_free_result(res); + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Mysql connection did not exist for item load."); + return false; + } + return true; +} + +bool MainFrame::LoadZones() +{ + if(mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Loading Zones..."); + unsigned int zonesLoaded = 0; + MYSQL_RES *res; + MYSQL_ROW row; + + if (mysql_query(mMysql, "SELECT short_name,zoneidnumber FROM zone")) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Connection Error: %s", mysql_error(mMysql)); + return false; + } + + wxString zoneStr; + zoneStr.Printf("**Unknown Zone**"); + int * id = new int; + *id = -1; + mStartZone->Append(zoneStr, (void*)id); + + zoneStr.Clear(); + zoneStr.Printf("*Any Zone*"); + id = new int; + *id = 0; + mStartZone->Append(zoneStr, (void*)id); + + mActivityZone->Append(zoneStr, (void*)id); + mProxZone->Append(zoneStr, (void*)id); + zoneStr.clear(); + mStartZone->Select(0); + mActivityZone->Select(0); + mProxZone->Select(0); + + res = mysql_use_result(mMysql); + while ((row = mysql_fetch_row(res)) != NULL){ + eqtask_zones newZ; + strcpy(newZ.name, row[0]); + + newZ.id = atoi(row[1]); + taskZoneList.push_back(newZ); + + int * zoneId = new int; + *zoneId = newZ.id; + wxString zoneNameStr; + + zoneNameStr.Printf("%s", newZ.name); + mStartZone->Append(zoneNameStr, (void*)zoneId); + mActivityZone->Append(zoneNameStr, (void*)zoneId); + mProxZone->Append(zoneNameStr, (void*)zoneId); + zoneNameStr.clear(); + + zonesLoaded++; + } + mErrorLog->Log(eqEmuLogBoth, "%u Successfully Loaded Zones", zonesLoaded); + mysql_free_result(res); + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Mysql connection did not exist for zone load."); + return false; + } + return true; +} + +bool MainFrame::LoadTasks() +{ + if(mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Loading Tasks..."); + unsigned int tasksLoaded = 0; + MYSQL_RES *res; + MYSQL_ROW row; + + if (mysql_query(mMysql, "SELECT id, title, description, reward, rewardid, cashreward, xpreward, rewardmethod, startzone, duration, minlevel, maxlevel, repeatable FROM tasks")) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Connection Error: %s", mysql_error(mMysql)); + return false; + } + + res = mysql_use_result(mMysql); + while ((row = mysql_fetch_row(res)) != NULL){ + eqtask newT; + newT.id = atoi(row[0]); + + //This isn't all that safe + //Working under the assumption that: + //Any database you connect to is not + //Going to want to hurt you + strcpy(newT.title, row[1]); + strcpy(newT.desc, row[2]); + strcpy(newT.reward, row[3]); + + wxString str; + str.Printf("%d:%s", newT.id, newT.title); + ItemSelectionList->Append(str); + + if(newT.id > highestIndex) + highestIndex = newT.id; + + newT.rewardid = atoi(row[4]); + newT.cashreward = atoi(row[5]); + newT.xpreward = atoi(row[6]); + newT.rewardmethod = atoi(row[7]); + newT.startzone = atoi(row[8]); + newT.duration = atoi(row[9]); + newT.level_min = atoi(row[10]); + newT.level_max = atoi(row[11]); + newT.repeatable = atoi(row[12]) ? true : false; + + taskList.push_back(newT); + + tasksLoaded++; + } + mErrorLog->Log(eqEmuLogBoth, "%u Successfully Loaded Tasks", tasksLoaded); + mysql_free_result(res); + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Mysql connection did not exist for task load."); + return false; + } + return true; +} + +bool MainFrame::LoadGoals() +{ + if(mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Loading Goals..."); + unsigned int goalsLoaded = 0; + MYSQL_RES *res; + MYSQL_ROW row; + + if (mysql_query(mMysql, "SELECT listid, entry FROM goallists")) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Connection Error: %s", mysql_error(mMysql)); + return false; + } + + res = mysql_use_result(mMysql); + while ((row = mysql_fetch_row(res)) != NULL){ + eqtask_goallist newGL; + newGL.id = atoi(row[0]); + newGL.value = atoi(row[1]); + goalTaskList.push_back(newGL); + + goalsLoaded++; + } + mErrorLog->Log(eqEmuLogBoth, "%u Successfully Loaded Goals", goalsLoaded); + mysql_free_result(res); + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Mysql connection did not exist for goal load."); + return false; + } + return true; +} + +bool MainFrame::LoadActivities() +{ + if(mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Loading Activities..."); + unsigned int activitiesLoaded = 0; + MYSQL_RES *res; + MYSQL_ROW row; + + if (mysql_query(mMysql, "SELECT taskid, activityid, step, activitytype, text1, text2, text3, goalid, goalmethod, goalcount, delivertonpc, zoneid, optional FROM activities")) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Connection Error: %s", mysql_error(mMysql)); + return false; + } + + res = mysql_use_result(mMysql); + while ((row = mysql_fetch_row(res)) != NULL){ + eqtask_activities newAL; + + newAL.id = atoi(row[0]); + newAL.activityId = atoi(row[1]); + newAL.step = atoi(row[2]); + newAL.activityType = atoi(row[3]); + strcpy(newAL.text1, row[4]); + strcpy(newAL.text2, row[5]); + strcpy(newAL.text3, row[6]); + newAL.goalid = atoi(row[7]); + newAL.goalmethod = atoi(row[8]); + newAL.goalcount = atoi(row[9]); + newAL.deliverToNpc = atoi(row[10]); + newAL.zoneid = atoi(row[11]); + newAL.optional = atoi(row[12]) ? true : false; + + taskActivitiesList.push_back(newAL); + activitiesLoaded++; + } + mErrorLog->Log(eqEmuLogBoth, "%u Successfully Loaded Activities", activitiesLoaded); + mysql_free_result(res); + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Mysql connection did not exist for activity load."); + return false; + } + return true; +} + +bool MainFrame::LoadProximity() +{ + if(mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Loading Proximities..."); + unsigned int proximitiesLoaded = 0; + MYSQL_RES *res; + MYSQL_ROW row; + + if (mysql_query(mMysql, "SELECT zoneid, exploreid, minx, maxx, miny, maxy, minz, maxz FROM proximities")) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Connection Error: %s", mysql_error(mMysql)); + return false; + } + + res = mysql_use_result(mMysql); + while ((row = mysql_fetch_row(res)) != NULL){ + eqtask_proximity newPR; + + newPR.zoneid = atoi(row[0]); + newPR.exploreid = atoi(row[1]); + newPR.minx = atof(row[2]); + newPR.maxx = atof(row[3]); + newPR.miny = atof(row[4]); + newPR.maxy = atof(row[5]); + newPR.minz = atof(row[6]); + newPR.maxz = atof(row[7]); + + taskProximityList.push_back(newPR); + proximitiesLoaded++; + } + mErrorLog->Log(eqEmuLogBoth, "%u Successfully Loaded Proximities", proximitiesLoaded); + mysql_free_result(res); + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Mysql connection did not exist for proximity load."); + return false; + } + return true; +} + +void MainFrame::ShowRewardChange(int rewardType, int rewardId) +{ + if(rewardType == 2){ + ShowRewardItems->Clear(); + } + else if(rewardType == 0 && rewardId != 0){ + ShowRewardItems->Clear(); + vector::iterator Iter; + + for(Iter = itemList.begin(); Iter!=itemList.end(); Iter++) + { + eqitem eqi = *Iter; + if(eqi.id == rewardId){ + wxString itStr; + itStr.Printf("%u: %s", eqi.id, eqi.name); + ShowRewardItems->Append(itStr); + itStr.clear(); + } + } + } + else if(rewardType == 1 && rewardId != 0) + { + ShowRewardItems->Clear(); + vector::iterator Iter; + for(Iter = goalTaskList.begin(); Iter!=goalTaskList.end(); Iter++) + { + eqtask_goallist gli = *Iter; + if(gli.id == rewardId) + { + vector::iterator itemIter; + for(itemIter = itemList.begin(); itemIter!=itemList.end(); itemIter++) + { + eqitem eqi = *itemIter; + if(eqi.id == gli.value) + { + wxString itStr; + itStr.Printf("%u: %s", eqi.id, eqi.name); + ShowRewardItems->Append(itStr); + itStr.clear(); + } + } + } + } + } + else{ + ShowRewardItems->Clear(); + } +} + +void MainFrame::SetZoneSelectionById(int zid) +{ + for(unsigned int x = 0; x < mStartZone->GetCount(); x++) + { + int *i; + i = (int*) mStartZone->GetClientData(x); + if(i){ + if(*i == zid){ + mStartZone->Select(x); + return; + } + } + else + { + mErrorLog->Log(eqEmuLogBoth, "MainFrame::SetZoneSelectionById: i was NULL"); + } + } + mStartZone->Select(0); //we have no valid zone +} + +void MainFrame::SetZoneSelectionByIdActivity(int zid) +{ + for(unsigned int x = 0; x < mActivityZone->GetCount(); x++) + { + int *i; + i = (int*) mActivityZone->GetClientData(x); + if(i){ + if(*i == zid){ + mActivityZone->Select(x); + return; + } + } + else + { + mErrorLog->Log(eqEmuLogBoth, "MainFrame::SetZoneSelectionByIdActivity: i was NULL"); + } + } + mActivityZone->Select(0); //we have no valid zone +} + +void MainFrame::SetZoneSelectionByIdProximity(int zid) +{ + for(unsigned int x = 0; x < mProxZone->GetCount(); x++) + { + int *i; + i = (int*) mProxZone->GetClientData(x); + if(i){ + if(*i == zid){ + mProxZone->Select(x); + return; + } + } + else + { + mErrorLog->Log(eqEmuLogBoth, "MainFrame::SetZoneSelectionByIdProximity: i was NULL"); + } + } + mProxZone->Select(0); //we have no valid zone +} + +wxString MainFrame::MakeStringSQLSafe(const char * c) +{ + wxString ret; + ret.Printf("%s", c); + ret.Replace("\'", "\\\'"); + return ret; +} \ No newline at end of file diff --git a/utils/TaskMaster/changelog.txt b/utils/TaskMaster/changelog.txt new file mode 100644 index 000000000..478c8b6aa --- /dev/null +++ b/utils/TaskMaster/changelog.txt @@ -0,0 +1,21 @@ +=01/09/2010= +Trevius: The xpreward field now allows a negative value to match a change in Rev1803. + +=04/27/2009= +KLS: Fix for reward method not saving. + +=10/12/2008= +KLS: Improved some sql syntax to be more safe. +KLS: Increased the size of goal count box significantly. +KLS: Added id select for new task. +KLS: Made change goal value more smooth. +KLS: Removed database.cpp and database.h from project file. + +=10/11/2008= +KLS: SQL logging will now include a time stamped comment +KLS: Updated some SQL queries. +KLS: Changed default sql log file. +KLS: Added method to account for ' in SQL syntax. Can expand to other control characters if needed + +=10/10/2008= +KLS: Initial Entry. \ No newline at end of file diff --git a/utils/TaskMaster/goals.cpp b/utils/TaskMaster/goals.cpp new file mode 100644 index 000000000..2f78d6529 --- /dev/null +++ b/utils/TaskMaster/goals.cpp @@ -0,0 +1,433 @@ +#include +#include +#include +#include +#include "tasks.h" +#include "utility.h" +#include "base.h" +#include "ErrorLog.h" + +using namespace std; + +void MainFrame::GoalsListBoxDoubleClick(wxCommandEvent& event) +{ + int * i = (int*) GoalsSelectionList->GetClientData(event.GetInt()); + if(i){ + int mI = *i; + PopulateGoalValues(mI); + } +} + +void MainFrame::PopulateGoals() +{ + mErrorLog->Log(eqEmuLogBoth, "Adding goals to the goal list box"); + vector::iterator Iter; + + if(goalTaskList.size() == 0) + { + mErrorLog->Log(eqEmuLogBoth, "No goals in DB, cannot add to list box"); + return; + } + + for(Iter = goalTaskList.begin(); Iter != goalTaskList.end(); Iter++) + { + eqtask_goallist val = (*Iter); + bool exists = false; + if(GoalsSelectionList->GetCount() > 0){ + for(unsigned int i = 0; i < GoalsSelectionList->GetCount(); i++) + { + int *cd = (int*)GoalsSelectionList->GetClientData(i); + if(*cd == val.id){ + exists = true; + } + } + } + if(!exists){ + int *newCD = new int; + *newCD = val.id; + wxString newStr; + newStr.Printf("%u", *newCD); + GoalsSelectionList->Append(newStr, (void*)newCD); + } + } +} + +void MainFrame::PopulateGoalValues(unsigned int goalid) +{ + GoalsValuesList->Enable(); + mGoalsNewValueButton->Enable(); + mGoalsDeleteValueButton->Enable(); + mGoalsChangeValueButton->Enable(); + ClearGoalValues(); + openedGoal = goalid; + vector::iterator Iter; + if(goalTaskList.size() != 0){ + for(Iter = goalTaskList.begin(); Iter != goalTaskList.end(); Iter++) + { + if((*Iter).id == goalid){ + int *i = new int; + *i = (*Iter).value; + + wxString curStr; + curStr.Clear(); + curStr.Printf("%u", (*Iter).value); + + GoalsValuesList->Append(curStr, (void*)i); + } + } + } +} + +void MainFrame::ClearGoalValues() +{ + if(GoalsValuesList->GetCount() > 0){ + for(unsigned int x = 0; x < GoalsValuesList->GetCount(); x++) + { + int *i = (int*)GoalsValuesList->GetClientData(x); + if(i){ + delete i; + i = 0; + } + } + } + GoalsValuesList->Clear(); +} + +void MainFrame::NewGoal(wxCommandEvent& event) +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "New goal failed, not connected to db"); + return; + } + int newID = wxGetNumberFromUser("", "New ID:", "Input ID", 0, 0, 9999999); + int newVal = wxGetNumberFromUser("", "New Value:", "Input Value", 0, 0, 9999999); + + bool exists = false; + for(unsigned int x = 0; x < GoalsSelectionList->GetCount(); x++) + { + int *di = (int*)GoalsSelectionList->GetClientData(x); + if(*di == newID) + exists = true; + } + + if(exists){ + mErrorLog->Log(eqEmuLogBoth, "New goal failed, already exists"); + return; + } + + if(newID <= 0){ + mErrorLog->Log(eqEmuLogBoth, "New goal failed, ID <= 0"); + return; + } + + if(newVal < 0){ + mErrorLog->Log(eqEmuLogBoth, "New goal failed, Val < 0"); + return; + } + char * mQuery = 0; + MakeAnyLenString(&mQuery, "INSERT INTO `goallists` (`listid`,`entry`) VALUES (%u,%u)", newID, newVal); + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + + eqtask_goallist newGoal; + newGoal.id = newID; + newGoal.value = newVal; + goalTaskList.push_back(newGoal); + + wxString newGoalStr; + newGoalStr.Printf("%u", newGoal.id); + int * i = new int; + *i = newGoal.id; + GoalsSelectionList->Append(newGoalStr, (void*)i); +} + +void MainFrame::DeleteGoal(wxCommandEvent& event) +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Delete goal failed, not connected to db"); + return; + } + + if(GoalsSelectionList->GetCount() == 0) + { + mErrorLog->Log(eqEmuLogBoth, "Delete goal failed, does not exist"); + return; + } + + if(GoalsSelectionList->GetSelection() == wxNOT_FOUND) + { + mErrorLog->Log(eqEmuLogBoth, "Delete goal failed, nothing selected"); + return; + } + + int *mId; + mId = (int*)GoalsSelectionList->GetClientData(GoalsSelectionList->GetSelection()); + + if(!mId){ + mErrorLog->Log(eqEmuLogBoth, "Delete goal failed, mId is NULL"); + return; + } + + int reply = wxMessageBox("Are you sure?", "Confirm Delete", wxYES_NO, this); + if(reply != wxYES) + { + mErrorLog->Log(eqEmuLogBoth, "User aborted delete of goal"); + return; + } + + int delVal = *mId; + char * mQuery = 0; + MakeAnyLenString(&mQuery, "DELETE FROM goallists where listid=%u", delVal); + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + + vector::iterator Iter; + for(Iter = goalTaskList.begin(); Iter != goalTaskList.end(); Iter++) + { + if((*Iter).id == delVal) + { + goalTaskList.erase(Iter); + Iter = goalTaskList.begin(); + } + } + + if(delVal == openedGoal){ + ClearGoalValues(); + openedGoal = 0; + mGoalsNewValueButton->Disable(); + mGoalsDeleteValueButton->Disable(); + mGoalsChangeValueButton->Disable(); + } + + for(unsigned int x = 0; x < GoalsSelectionList->GetCount(); x++) + { + int * ni = (int*)GoalsSelectionList->GetClientData(x); + if(ni){ + if(*ni == delVal){ + delete ni; + ni = NULL; + GoalsSelectionList->Delete(x); + return; + } + } + } +} + +void MainFrame::NewGoalValue(wxCommandEvent& event) +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "New goal value failed, not connected to db"); + return; + } + + if(!GoalsValuesList->IsEnabled()) + { + mErrorLog->Log(eqEmuLogBoth, "New goal value failed, goal values not active"); + return; + } + int newVal = wxGetNumberFromUser("", "Value:", "Input Value", 0, 0, 9999999); + if(newVal < 0){ + mErrorLog->Log(eqEmuLogBoth, "New goal value failed, Val < 0"); + } + + int id = openedGoal; + + char * mQuery = 0; + MakeAnyLenString(&mQuery, "INSERT INTO `goallists` (`listid`,`entry`) VALUES (%u,%u)", id, newVal); + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + int * iptr = new int; + *iptr = newVal; + wxString iStr; + iStr.Printf("%u", newVal); + GoalsValuesList->Append(iStr, (void*)iptr); + + eqtask_goallist newGoal; + newGoal.id = id; + newGoal.value = newVal; + goalTaskList.push_back(newGoal); +} + +void MainFrame::DeleteGoalValue(wxCommandEvent& event) +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Delete goal value failed, not connected to db"); + return; + } + + if(!GoalsValuesList->IsEnabled()){ + mErrorLog->Log(eqEmuLogBoth, "Delete goal value failed, goal values not active"); + return; + } + + if(GoalsValuesList->GetCount() <= 1) + { + mErrorLog->Log(eqEmuLogBoth, "Delete goal value failed, too few values"); + return; + } + + int ourId = openedGoal; + int ourVal; + int *iPtr = (int*)GoalsValuesList->GetClientData(GoalsValuesList->GetSelection()); + + if(!iPtr){ + mErrorLog->Log(eqEmuLogBoth, "Delete goal value failed, iPtr is NULL"); + return; + } + + int reply = wxMessageBox("Are you sure?", "Confirm Delete", wxYES_NO, this); + if(reply != wxYES) + { + mErrorLog->Log(eqEmuLogBoth, "User aborted delete of goal value"); + return; + } + + ourVal = *iPtr; + + char * mQuery = 0; + MakeAnyLenString(&mQuery, "DELETE FROM goallists WHERE listid=%u AND entry=%u", ourId, ourVal); + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + + for(unsigned int x = 0; x < GoalsValuesList->GetCount(); x++) + { + int *ip = (int*)GoalsValuesList->GetClientData(x); + if(ip){ + if(*ip == ourVal){ + GoalsValuesList->Delete(x); + break; + } + } + } + vector::iterator Iter; + for(Iter = goalTaskList.begin(); Iter != goalTaskList.end(); Iter++) + { + if((*Iter).value == ourVal && (*Iter).id == ourId) + { + goalTaskList.erase(Iter); + return; + } + } +} + +void MainFrame::ChangeGoalValue(wxCommandEvent& event) +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Modify goal value failed, not connected to db"); + return; + } + + if(!GoalsValuesList->IsEnabled()){ + mErrorLog->Log(eqEmuLogBoth, "Modify goal value failed, goal values not active"); + return; + } + + if(GoalsValuesList->GetSelection() == wxNOT_FOUND) + { + mErrorLog->Log(eqEmuLogBoth, "Modify goal value failed, no selection found"); + return; + } + + int changedId = openedGoal; + int changedGoal; + int * changedGoalPtr = (int*)GoalsValuesList->GetClientData(GoalsValuesList->GetSelection()); + + if(!changedGoalPtr) + { + mErrorLog->Log(eqEmuLogBoth, "Modify goal value failed, changedGoalPtr was NULL"); + return; + } + + changedGoal = *changedGoalPtr; + + int newVal = wxGetNumberFromUser("", "Value:", "Input Value", changedGoal, 0, 9999999); + + char * mQuery = 0; + MakeAnyLenString(&mQuery, "DELETE FROM goallists WHERE listid=%u AND entry=%u", changedId, changedGoal); + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + + MakeAnyLenString(&mQuery, "INSERT INTO `goallists` (`listid`,`entry`) VALUES (%u,%u)", changedId, newVal); + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + + for(unsigned int x = 0; x < GoalsValuesList->GetCount(); x++) + { + int *ip = (int*)GoalsValuesList->GetClientData(x); + if(ip) + { + if(*ip == changedGoal) + { + *ip = newVal; + wxString newStr; + newStr.Printf("%u", newVal); + GoalsValuesList->SetString(x, newStr); + break; + } + } + } + + vector::iterator Iter; + for(Iter = goalTaskList.begin(); Iter != goalTaskList.end(); Iter++) + { + if((*Iter).id == changedId && (*Iter).value == changedGoal) + { + (*Iter).value = newVal; + return; + } + } + +} + +void MainFrame::ContextMenuGoalList() +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Context menu cannot open, not connected to db"); + return; + } + + wxMenu *mMenu; + mMenu = new wxMenu(); + + mMenu->Append(MENU_NewGoal, wxT("New Goal"), wxT("Creates a new goal list entry")); + mMenu->Append(MENU_DeleteGoal, wxT("Delete Goal"), wxT("Deletes the selected goal list entry")); + + PopupMenu(mMenu); + delete mMenu; +} + +void MainFrame::ContextMenuGoalValueList() +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Context menu cannot open, not connected to db"); + return; + } + + wxMenu *mMenu; + mMenu = new wxMenu(); + + mMenu->Append(MENU_AddGoalItem, wxT("New Goal Value"), wxT("Creates a new goal value entry for the opened list")); + mMenu->Append(MENU_DeleteGoalItem, wxT("Delete Goal Value"), wxT("Deletes the selected goal value entry")); + mMenu->Append(MENU_ModifyGoalItem, wxT("Modify Goal Value"), wxT("Attempts to change the selected goal value entry")); + + PopupMenu(mMenu); + delete mMenu; +} \ No newline at end of file diff --git a/utils/TaskMaster/items.h b/utils/TaskMaster/items.h new file mode 100644 index 000000000..841b1747f --- /dev/null +++ b/utils/TaskMaster/items.h @@ -0,0 +1,9 @@ +#ifndef EQWX_ITEMS__H +#define EQWX_ITEMS__H + +struct eqitem{ + unsigned int id; + char name[64]; +}; + +#endif \ No newline at end of file diff --git a/utils/TaskMaster/proximity.cpp b/utils/TaskMaster/proximity.cpp new file mode 100644 index 000000000..f4491b1e2 --- /dev/null +++ b/utils/TaskMaster/proximity.cpp @@ -0,0 +1,468 @@ +#include +#include +#include +#include +#include "tasks.h" +#include "utility.h" +#include "base.h" +#include "ErrorLog.h" + +using namespace std; + +void MainFrame::ProximityListBoxDoubleClick(wxCommandEvent& event) +{ + eqtask_prox *eqp = (eqtask_prox*)ProximitySelectionList->GetClientData(event.GetInt()); + if(eqp){ + FillProximityValues(eqp->zoneid, eqp->exploreid); + } + else{ + mErrorLog->Log(eqEmuLogBoth, "ProximityListBoxDoubleClick failed, eqp is NULL"); + } +} + +void MainFrame::PopulateProximity() +{ + mErrorLog->Log(eqEmuLogBoth, "Populating Proximity List"); + vector::iterator Iter; + for(Iter = taskProximityList.begin(); Iter != taskProximityList.end(); Iter++) + { + eqtask_proximity eqt; + eqt = *Iter; + eqtask_prox * prox = new eqtask_prox; + + prox->exploreid = eqt.exploreid; + prox->zoneid = eqt.zoneid; + + wxString zoneName; + zoneName.Printf("Unknown Zone"); + vector::iterator ZoneIter; + for(ZoneIter = taskZoneList.begin(); ZoneIter != taskZoneList.end(); ZoneIter++) + { + eqtask_zones curZone = *ZoneIter; + if(curZone.id == prox->zoneid) + { + zoneName.Clear(); + zoneName.Printf("%s", curZone.name); + } + } + + wxString newStr; + newStr.Printf("%s - %u", zoneName.mb_str(), prox->exploreid); + ProximitySelectionList->Append(newStr, (void*)prox); + } +} + +void MainFrame::FillProximityValues(unsigned int zone, unsigned int explore) +{ + mErrorLog->Log(eqEmuLogBoth,"zon %u, ex %u", zone, explore); + eqtask_proximity ourProx; + vector::iterator Iter; + for(Iter = taskProximityList.begin(); Iter != taskProximityList.end(); Iter++) + { + eqtask_proximity curProx = *Iter; + if(curProx.exploreid == explore && curProx.zoneid == zone){ + ourProx = curProx; + } + } + + wxString ourStr; + + ourStr.Clear(); + ourStr.Printf("%u", ourProx.exploreid); + mProxId->Enable(); + mProxId->SetValue(ourStr); + + ourStr.Clear(); + ourStr.Printf("%.4f", ourProx.minx); + mProxMinx->Enable(); + mProxMinx->SetValue(ourStr); + + ourStr.Clear(); + ourStr.Printf("%.4f", ourProx.maxx); + mProxMaxx->Enable(); + mProxMaxx->SetValue(ourStr); + + ourStr.Clear(); + ourStr.Printf("%.4f", ourProx.miny); + mProxMiny->Enable(); + mProxMiny->SetValue(ourStr); + + ourStr.Clear(); + ourStr.Printf("%.4f", ourProx.maxy); + mProxMaxy->Enable(); + mProxMaxy->SetValue(ourStr); + + ourStr.Clear(); + ourStr.Printf("%.4f", ourProx.minz); + mProxMinz->Enable(); + mProxMinz->SetValue(ourStr); + + ourStr.Clear(); + ourStr.Printf("%.4f", ourProx.maxz); + mProxMaxz->Enable(); + mProxMaxz->SetValue(ourStr); + + openedProximity.exploreid = explore; + openedProximity.zoneid = zone; + + SetZoneSelectionByIdProximity(zone); + mProxZone->Enable(); +} + +void MainFrame::NewProximity(wxCommandEvent& event) +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "New proximity failed, not connected to db"); + return; + } + + int newID = wxGetNumberFromUser("", "Explore ID:", "Explore ID", 0, 0, 9999999); + int newZoneID = wxGetNumberFromUser("", "Zone ID:", "Zone ID", 0, 0, 9999999); + + if(newID < 0 || newZoneID < 0) + { + mErrorLog->Log(eqEmuLogBoth, "New proximity failed, id or zone < 0"); + return; + } + + if(newID == 0xFFFFFFFF || newZoneID == 0xFFFFFFF) + { + mErrorLog->Log(eqEmuLogBoth, "New proximity failed, 0xFFFFFFFF cannot be set with editor"); + return; + } + + eqtask_proximity newProx; + newProx.exploreid = newID; + newProx.zoneid = newZoneID; + newProx.maxx = 0.0; + newProx.minx = 0.0; + newProx.maxy = 0.0; + newProx.miny = 0.0; + newProx.maxz = 0.0; + newProx.minz = 0.0; + + char * mQuery = 0; + MakeAnyLenString(&mQuery, "INSERT INTO `proximities` (`zoneid`,`exploreid`) VALUES (%u,%u)", newZoneID, newID); + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + + eqtask_prox * pPtr = new eqtask_prox; + pPtr->exploreid = newID; + pPtr->zoneid = newZoneID; + + wxString zoneName; + zoneName.Printf("Unknown Zone"); + vector::iterator ZoneIter; + for(ZoneIter = taskZoneList.begin(); ZoneIter != taskZoneList.end(); ZoneIter++) + { + eqtask_zones curZone = *ZoneIter; + if(curZone.id == newZoneID) + { + zoneName.Clear(); + zoneName.Printf("%s", curZone.name); + } + } + + wxString newStr; + newStr.Printf("%s - %u", zoneName.mb_str(), newID); + ProximitySelectionList->Append(newStr, (void*)pPtr); + + taskProximityList.push_back(newProx); +} + +void MainFrame::DeleteProximity(wxCommandEvent& event) +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Delete proximity failed, not connected to db"); + return; + } + + eqtask_prox * pPtr = (eqtask_prox*)ProximitySelectionList->GetClientData(ProximitySelectionList->GetSelection()); + + if(!pPtr){ + mErrorLog->Log(eqEmuLogBoth, "Delete proximity failed, pPtr is NULL"); + return; + } + + int explore = pPtr->exploreid; + int zone = pPtr->zoneid; + + int reply = wxMessageBox("Are you sure?", "Confirm Delete", wxYES_NO, this); + if(reply != wxYES) + { + mErrorLog->Log(eqEmuLogBoth, "User aborted delete of proximity."); + return; + } + + char * mQuery = 0; + MakeAnyLenString(&mQuery, "DELETE FROM proximities WHERE zoneid=%u AND exploreid=%u", zone, explore); + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + + for(unsigned int x = 0; x < ProximitySelectionList->GetCount(); x++) + { + eqtask_prox * p = (eqtask_prox*)ProximitySelectionList->GetClientData(x); + if(p){ + if(p->exploreid == explore && p->zoneid == zone) + { + delete p; + p = NULL; + ProximitySelectionList->Delete(x); + break; + } + } + } + + vector::iterator Iter; + for(Iter = taskProximityList.begin(); Iter != taskProximityList.end(); Iter++) + { + eqtask_proximity curProx = *Iter; + if(curProx.exploreid == explore && curProx.zoneid == zone){ + taskProximityList.erase(Iter); + break; + } + } + + if(openedProximity.exploreid == explore && openedProximity.zoneid == zone) + { + mProxId->Clear(); + mProxId->Disable(); + mProxMinx->Clear(); + mProxMinx->Disable(); + mProxMaxx->Clear(); + mProxMaxx->Disable(); + mProxMiny->Clear(); + mProxMiny->Disable(); + mProxMaxy->Clear(); + mProxMaxy->Disable(); + mProxMinz->Clear(); + mProxMinz->Disable(); + mProxMaxz->Clear(); + mProxMaxz->Disable(); + + mProxZone->Select(0); + mProxZone->Disable(); + openedProximity.exploreid = 0xFFFFFFFF; + openedProximity.zoneid = 0xFFFFFFFF; + } +} + +void MainFrame::SaveProximity(wxCommandEvent& event) +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Save proximity failed, not connected to db"); + return; + } + + if(openedProximity.exploreid == 0xFFFFFFFF && openedProximity.zoneid == 0xFFFFFFFF) + { + mErrorLog->Log(eqEmuLogBoth, "Save proximity failed, no proximity opened"); + return; + } + + mErrorLog->Log(eqEmuLogBoth, "Saving proximity..."); + + eqtask_proximity toSave; + wxString inStr; + int explore = openedProximity.exploreid; + int zone = openedProximity.zoneid; + bool canUpdate = false; + + inStr.Clear(); + inStr = mProxId->GetValue(); + toSave.exploreid = atoi(inStr.mb_str()); + + inStr.Clear(); + inStr = mProxMinx->GetValue(); + toSave.minx = atof(inStr.mb_str()); + + inStr.Clear(); + inStr = mProxMaxx->GetValue(); + toSave.maxx = atof(inStr.mb_str()); + + inStr.Clear(); + inStr = mProxMiny->GetValue(); + toSave.miny = atof(inStr.mb_str()); + + inStr.Clear(); + inStr = mProxMaxy->GetValue(); + toSave.maxy = atof(inStr.mb_str()); + + inStr.Clear(); + inStr = mProxMinz->GetValue(); + toSave.minz = atof(inStr.mb_str()); + + inStr.Clear(); + inStr = mProxMaxz->GetValue(); + toSave.maxz = atof(inStr.mb_str()); + + if(toSave.maxx < toSave.minx){ + mErrorLog->Log(eqEmuLogBoth, "min x and max x mismatched, correcting"); + int temp = toSave.minx; + toSave.minx = toSave.maxx; + toSave.maxx = temp; + + wxString tempStr; + tempStr.Printf("%.4f", toSave.minx); + mProxMinx->SetValue(tempStr); + tempStr.Clear(); + + tempStr.Printf("%.4f", toSave.maxx); + mProxMaxx->SetValue(tempStr); + tempStr.Clear(); + } + + if(toSave.maxy < toSave.miny){ + mErrorLog->Log(eqEmuLogBoth, "min y and max y mismatched, correcting"); + int temp = toSave.miny; + toSave.miny = toSave.maxy; + toSave.maxy = temp; + + wxString tempStr; + tempStr.Printf("%.4f", toSave.miny); + mProxMiny->SetValue(tempStr); + tempStr.Clear(); + + tempStr.Printf("%.4f", toSave.maxy); + mProxMaxy->SetValue(tempStr); + tempStr.Clear(); + } + + if(toSave.maxz < toSave.minz){ + mErrorLog->Log(eqEmuLogBoth, "min z and max z mismatched, correcting"); + int temp = toSave.minz; + toSave.minz = toSave.maxz; + toSave.maxz = temp; + + wxString tempStr; + tempStr.Printf("%.4f", toSave.minz); + mProxMinz->SetValue(tempStr); + tempStr.Clear(); + + tempStr.Printf("%.4f", toSave.maxz); + mProxMaxz->SetValue(tempStr); + tempStr.Clear(); + } + + int * iPtr = (int*)mProxZone->GetClientData(mProxZone->GetCurrentSelection()); + toSave.zoneid = *iPtr; + + if(!iPtr){ + mErrorLog->Log(eqEmuLogBoth, "Save proximity failed, iPtr is NULL"); + return; + } + + if(openedProximity.exploreid == toSave.exploreid && openedProximity.zoneid == toSave.zoneid) + { + mErrorLog->Log(eqEmuLogBoth, "Can use UPDATE"); + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Cannot use UPDATE must replace instead"); + } + + if(canUpdate){ + char * mQuery = 0; + MakeAnyLenString(&mQuery, "UPDATE proximities SET minx=%.4f, maxx=%.4f, miny=%.4f, maxy=%.4f, minz=%.4f, maxz=%.4f) WHERE zoneid=%u AND exploreid=%u", + toSave.minx, toSave.maxx, toSave.miny, toSave.maxy, toSave.minz, toSave.maxz, toSave.zoneid, toSave.exploreid); + + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + } + else + { + char * mQuery = 0; + MakeAnyLenString(&mQuery, "DELETE FROM proximities WHERE zoneid=%u AND exploreid=%u", zone, explore); + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + + MakeAnyLenString(&mQuery, "INSERT INTO `proximities` (`zoneid`,`exploreid`,`minx`,`maxx`,`miny`,`maxy`,`minz`,`maxz`) VALUES (%u,%u,%.4f,%.4f,%.4f,%.4f,%.4f,%.4f)" + , toSave.zoneid, toSave.exploreid, toSave.minx, toSave.maxx, toSave.miny, toSave.maxy, toSave.minz, toSave.maxz); + + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + } + + for(unsigned int x = 0; x < ProximitySelectionList->GetCount(); x++) + { + eqtask_prox * p = (eqtask_prox*)ProximitySelectionList->GetClientData(x); + if(p){ + if(p->exploreid == openedProximity.exploreid && p->zoneid == openedProximity.zoneid) + { + p->exploreid = toSave.exploreid; + p->zoneid = toSave.zoneid; + wxString newStr; + wxString zoneName; + zoneName.Printf("Unknown Zone"); + vector::iterator ZoneIter; + for(ZoneIter = taskZoneList.begin(); ZoneIter != taskZoneList.end(); ZoneIter++) + { + eqtask_zones curZone = *ZoneIter; + if(curZone.id == p->zoneid) + { + zoneName.Clear(); + zoneName.Printf("%s", curZone.name); + } + } + newStr.Printf("%s - %u", zoneName.mb_str(), p->exploreid ); + ProximitySelectionList->SetString(x, newStr); + break; + } + } + } + + + vector::iterator Iter; + for(Iter = taskProximityList.begin(); Iter != taskProximityList.end(); Iter++) + { + eqtask_proximity cur = *Iter; + if(cur.exploreid == openedProximity.exploreid && cur.zoneid == openedProximity.zoneid) + { + (*Iter).exploreid = toSave.exploreid; + (*Iter).zoneid = toSave.zoneid; + (*Iter).minx = toSave.minx; + (*Iter).maxx = toSave.maxx; + (*Iter).miny = toSave.miny; + (*Iter).maxy = toSave.maxy; + (*Iter).minz = toSave.minz; + (*Iter).maxz = toSave.maxz; + } + } + + openedProximity.exploreid = toSave.exploreid; + openedProximity.zoneid = toSave.zoneid; + mErrorLog->Log(eqEmuLogBoth, "Save finished."); +} + +void MainFrame::ContextMenuProximity() +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Context menu cannot open, not connected to db"); + return; + } + + wxMenu *mMenu; + mMenu = new wxMenu(); + + mMenu->Append(MENU_NewProximity, wxT("New Proximity"), wxT("Creates a new goal proximity entry")); + mMenu->Append(MENU_DeleteProximity, wxT("Delete Proximity"), wxT("Deletes the selected proximity entry")); + mMenu->AppendSeparator(); + mMenu->Append(MENU_SaveProximity, wxT("Save Proximity"), wxT("Saves the opened proximity entry")); + + PopupMenu(mMenu); + delete mMenu; +} \ No newline at end of file diff --git a/utils/TaskMaster/tasks.cpp b/utils/TaskMaster/tasks.cpp new file mode 100644 index 000000000..4929ad26a --- /dev/null +++ b/utils/TaskMaster/tasks.cpp @@ -0,0 +1,328 @@ +#include +#include +#include +#include +#include "tasks.h" +#include "utility.h" +#include "base.h" +#include "ErrorLog.h" + +using namespace std; + +void MainFrame::NewTask(wxCommandEvent& event) +{ + if(mMysql){ + eqtask newT; + newT.id = (highestIndex+1); + newT.duration = 0; + strcpy(newT.title, "Default Task Name"); + strcpy(newT.desc, "Default Task Description"); + strcpy(newT.reward, ""); + newT.cashreward = 0; + newT.xpreward = 0; + newT.rewardmethod = 2; + newT.rewardid = 0; + newT.startzone = -1; + newT.level_min = 0; + newT.level_max = 0; + newT.repeatable = true; + + unsigned int newID = wxGetNumberFromUser("", "ID:", "Input ID", (highestIndex+1), 0, 2147483600); + + newT.id = newID; + + char * mQuery = 0; + MakeAnyLenString(&mQuery, "INSERT INTO `tasks` (`id`,`duration`,`title`,`description`,`reward`,`rewardid`,`cashreward`,`xpreward`,`rewardmethod`,`startzone`, `minlevel`, `maxlevel`, `repeatable`) VALUES (%u,%u,'%s','%s','%s',%u,%u,%u,%u,%u,%u,%u,%u)", + newT.id, newT.duration, newT.title, newT.desc, newT.reward, newT.rewardid, newT.cashreward, newT.xpreward, newT.rewardmethod, newT.startzone, newT.level_min, newT.level_max, newT.repeatable); + + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + taskList.push_back(newT); + + wxString str; + str.Printf("%d:%s", newT.id, "Default Task Name"); + ItemSelectionList->Append(str); + if(highestIndex < (newID + 1)) + highestIndex = newID + 1; + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Error with new task create, mysql not initialized"); + } +} + +void MainFrame::DeleteTask(wxCommandEvent& event) +{ + if(selectedIndex < 0) + { + mErrorLog->Log(eqEmuLogBoth, "No item selected for delete, delete failing."); + return; + } + + if(mMysql){ + int reply = wxMessageBox("Are you sure?", "Confirm Delete", wxYES_NO, this); + if(reply != wxYES) + { + mErrorLog->Log(eqEmuLogBoth, "User aborted delete of task."); + return; + } + + vector::iterator Iter; + Iter = taskList.begin(); + Iter += selectedIndex; + + eqtask mTask = *Iter; + + char * mQuery = 0; + MakeAnyLenString(&mQuery, "DELETE FROM `tasks` WHERE id=%u", mTask.id); + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + + if(openedIndex == selectedIndex){ + wxString mStr; + mStr.Printf("%s", "Task Name"); + mTaskName->Clear(); + mTaskName->AppendText(mStr); + mTaskName->Disable(); + mStr.clear(); + + mStr.Printf("%s", "Task Description"); + mTaskDesc->Clear(); + mTaskDesc->AppendText(mStr); + mTaskDesc->Disable(); + mStr.clear(); + + mStr.Printf("%u", 0); + mTaskMinLvl->Clear(); + mTaskMinLvl->AppendText(mStr); + mTaskMinLvl->Disable(); + mStr.clear(); + + mStr.Printf("%u", 0); + mTaskMaxLvl->Clear(); + mTaskMaxLvl->AppendText(mStr); + mTaskMaxLvl->Disable(); + mStr.clear(); + + mStr.Printf("%u", 0); + mTaskDuration->Clear(); + mTaskDuration->AppendText(mStr); + mTaskDuration->Disable(); + mStr.clear(); + + mRewardName->Clear(); + mRewardName->Disable(); + + mStr.Printf("%u", 0); + mRewardID->Clear(); + mRewardID->AppendText(mStr); + mRewardID->Disable(); + mStr.clear(); + + mStr.Printf("%u", 0); + mRewardCash->Clear(); + mRewardCash->AppendText(mStr); + mRewardCash->Disable(); + mStr.clear(); + + mStr.Printf("%u", 0); + mRewardXP->Clear(); + mRewardXP->AppendText(mStr); + mRewardXP->Disable(); + mStr.clear(); + openedIndex = -1; + + mRewardMethod->Select(0); + mRewardMethod->Disable(); + + mStartZone->Select(0); + mStartZone->Disable(); + + mTaskRepeatable->SetValue(false); + mTaskRepeatable->Disable(); + + ShowRewardItems->Disable(); + ShowRewardItems->Clear(); + RefreshItems->Disable(); + + ClearActivities(); + ActivitiesSelectionList->Disable(); + openedActivity.activityid = -1; + openedActivity.id = -1; + openedActivity.step = -1; + mActText1->Clear(); + mActText1->Disable(); + mActText2->Clear(); + mActText2->Disable(); + mActText3->Clear(); + mActText3->Disable(); + mActivityZone->Select(0); + mActivityZone->Disable(); + mActivityOptional->SetValue(false); + mActivityOptional->Disable(); + mActID->Clear(); + mActID->Disable(); + mActStep->Clear(); + mActStep->Disable(); + mActType->Select(0); + mActType->Disable(); + mActDeliver->Clear(); + mActDeliver->Disable(); + mActMethod->Select(2); + mActMethod->Disable(); + mActGoalID->Clear(); + mActGoalID->Disable(); + mActGoalCount->Clear(); + mActGoalCount->Disable(); + } + + taskList.erase(Iter); + ItemSelectionList->Delete(selectedIndex); + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Error with task delete, mysql not initialized"); + } + selectedIndex = -1; +} + +void MainFrame::OnRewardButton(wxCommandEvent& event) +{ + wxString ridStr = mRewardID->GetValue(); + int rtype = mRewardMethod->GetCurrentSelection(); + int rid = atoi(ridStr.mb_str()); + + ShowRewardChange(rtype,rid); +} + +void MainFrame::SaveTask(wxCommandEvent& event) +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Mysql not connected for save of task"); + return; + } + + if(openedIndex>=0){ + mErrorLog->Log(eqEmuLogBoth, "Saving task..."); + vector::iterator Iter; + Iter = taskList.begin(); + Iter += openedIndex; + eqtask ourTask = *Iter; + wxString getStr; + + getStr = mTaskName->GetValue(); + strcpy(ourTask.title, getStr.mb_str()); + getStr.Clear(); + + getStr = mTaskDesc->GetValue(); + strcpy(ourTask.desc, getStr.mb_str()); + getStr.Clear(); + + getStr = mTaskMinLvl->GetValue(); + ourTask.level_min = atoi(getStr.mb_str()); + getStr.Clear(); + + getStr = mTaskMaxLvl->GetValue(); + ourTask.level_max = atoi(getStr.mb_str()); + getStr.Clear(); + + getStr = mTaskDuration->GetValue(); + ourTask.duration = atoi(getStr.mb_str()); + getStr.Clear(); + + getStr = mRewardName->GetValue(); + strcpy(ourTask.reward, getStr.mb_str()); + getStr.Clear(); + + getStr = mRewardID->GetValue(); + ourTask.rewardid = atoi(getStr.mb_str()); + getStr.Clear(); + + getStr = mRewardCash->GetValue(); + ourTask.cashreward = atoi(getStr.mb_str()); + getStr.Clear(); + + getStr = mRewardXP->GetValue(); + ourTask.xpreward = atoi(getStr.mb_str()); + getStr.Clear(); + + int * i = (int*)mStartZone->GetClientData(mStartZone->GetSelection()); + ourTask.startzone = *i; + + ourTask.rewardmethod = mRewardMethod->GetSelection(); + ourTask.repeatable = mTaskRepeatable->GetValue(); + + + char * mQuery = 0; + MakeAnyLenString(&mQuery, "UPDATE tasks SET duration=%u, title='%s', description='%s', reward='%s', rewardid=%u, cashreward=%u, xpreward=%i, rewardmethod=%u, startzone=%u, minlevel=%u, maxlevel=%u, repeatable=%u WHERE id=%u", + ourTask.duration, MakeStringSQLSafe(ourTask.title).mb_str(), MakeStringSQLSafe(ourTask.desc).mb_str(), MakeStringSQLSafe(ourTask.reward).mb_str(), ourTask.rewardid, ourTask.cashreward, ourTask.xpreward, ourTask.rewardmethod, ourTask.startzone, ourTask.level_min, ourTask.level_max, ourTask.repeatable, ourTask.id); + + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + + /*char * mQuery = 0; + MakeAnyLenString(&mQuery, "DELETE FROM tasks WHERE id=%u", (*Iter).id); + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + } + + MakeAnyLenString(&mQuery, "INSERT INTO `tasks` (`id`,`duration`,`title`,`description`,`reward`,`rewardid`,`cashreward`,`xpreward`,`rewardmethod`,`startzone`, `minlevel`, `maxlevel`) VALUES (%u,%u,'%s','%s','%s',%u,%u,%u,%u,%u,%u,%u)", + ourTask.id, ourTask.duration, MakeStringSQLSafe(ourTask.title).mb_str(), MakeStringSQLSafe(ourTask.desc).mb_str(), MakeStringSQLSafe(ourTask.reward).mb_str(), ourTask.rewardid, ourTask.cashreward, ourTask.xpreward, ourTask.rewardmethod, ourTask.startzone, ourTask.level_min, ourTask.level_max); + + mErrorLog->Log(eqEmuLogSQL, "%s", mQuery); + if (mysql_query(mMysql, mQuery)) { + mErrorLog->Log(eqEmuLogBoth, "MySQL Error: %s", mysql_error(mMysql)); + return; + }*/ + + (*Iter).cashreward = ourTask.cashreward; + strcpy((*Iter).desc, ourTask.desc); + (*Iter).duration = ourTask.duration; + (*Iter).level_max = ourTask.level_max; + (*Iter).level_min = ourTask.level_min; + strcpy((*Iter).reward, ourTask.reward); + (*Iter).rewardid = ourTask.rewardid; + (*Iter).rewardmethod = ourTask.rewardmethod; + (*Iter).startzone = ourTask.startzone; + strcpy((*Iter).title, ourTask.title); + (*Iter).xpreward = ourTask.xpreward; + (*Iter).repeatable = ourTask.repeatable; + + getStr.Printf("%u:%s", (*Iter).id, (*Iter).title); + ItemSelectionList->SetString(openedIndex, getStr); + mErrorLog->Log(eqEmuLogBoth, "Save finished."); + } + else{ + mErrorLog->Log(eqEmuLogBoth, "Opened index for Task Save not valid."); + } + +} + +void MainFrame::ContextMenuTaskList() +{ + if(!mMysql){ + mErrorLog->Log(eqEmuLogBoth, "Context menu cannot open, not connected to db"); + return; + } + + wxMenu *mMenu; + mMenu = new wxMenu(); + + mMenu->Append(MENU_NewTask, wxT("New Task"), wxT("Creates a new task")); + mMenu->Append(MENU_DeleteTask, wxT("Delete Task"), wxT("Deletes the selected task")); + mMenu->AppendSeparator(); + mMenu->Append(MENU_SaveTask, wxT("Save Task"), wxT("Saves the opened task")); + + PopupMenu(mMenu); + delete mMenu; +} \ No newline at end of file diff --git a/utils/TaskMaster/tasks.h b/utils/TaskMaster/tasks.h new file mode 100644 index 000000000..05c5bffdb --- /dev/null +++ b/utils/TaskMaster/tasks.h @@ -0,0 +1,75 @@ +#ifndef EQWX_TASKS__H +#define EQWX_TASKS__H + +struct eqtask +{ + unsigned int id; + unsigned int duration; //i + char title[100]; //i + char desc[2047]; //i + char reward[64]; //i + unsigned int rewardid; //i + unsigned int cashreward; //i + int xpreward; //i + unsigned short rewardmethod; //i + unsigned int startzone; //i + unsigned short level_min; //i + unsigned short level_max; //i + bool repeatable; +}; + +struct eqtask_goallist +{ + unsigned int id; + unsigned int value; +}; + +struct eqtask_activities +{ + unsigned int id; + unsigned int activityId; //i + unsigned int step; //i + unsigned int activityType; //i + char text1[64]; //i + char text2[64]; //i + char text3[128]; //i + unsigned int goalid; //n 7 digit textbox + unsigned int goalmethod; //i + unsigned int goalcount; //n 2 digit text box + unsigned int deliverToNpc; //i + unsigned int zoneid; //i + bool optional; //i +}; + +struct eqtask_activity_id +{ + unsigned int id; + unsigned int step; + unsigned int activityid; +}; + +struct eqtask_zones +{ + char name[32]; + unsigned int id; +}; + +struct eqtask_proximity +{ + unsigned int exploreid; + unsigned int zoneid; + double minx; + double maxx; + double miny; + double maxy; + double minz; + double maxz; +}; + +struct eqtask_prox +{ + unsigned int exploreid; + unsigned int zoneid; +}; + +#endif \ No newline at end of file diff --git a/utils/TaskMaster/utility.cpp b/utils/TaskMaster/utility.cpp new file mode 100644 index 000000000..a04293b4a --- /dev/null +++ b/utils/TaskMaster/utility.cpp @@ -0,0 +1,29 @@ +#include +#include +#include +#include "utility.h" + +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + +#define safe_delete(d) if(d) { delete d; d=0; } +#define safe_delete_array(d) if(d) { delete[] d; d=0; } + +int MakeAnyLenString(char** ret, const char* format, ...) { + int buf_len = 128; + int chars = -1; + va_list argptr; + va_start(argptr, format); + while (chars == -1 || chars >= buf_len) { + safe_delete_array(*ret); + if (chars == -1) + buf_len *= 2; + else + buf_len = chars + 1; + *ret = new char[buf_len]; + chars = vsnprintf(*ret, buf_len, format, argptr); + } + va_end(argptr); + return chars; +} \ No newline at end of file diff --git a/utils/TaskMaster/utility.h b/utils/TaskMaster/utility.h new file mode 100644 index 000000000..cfb9a2a69 --- /dev/null +++ b/utils/TaskMaster/utility.h @@ -0,0 +1,6 @@ +#ifndef EQWX_UTILITY__H +#define EQWX_UTILITY__H + +int MakeAnyLenString(char** ret, const char* format, ...); + +#endif \ No newline at end of file diff --git a/utils/azone2/3d.hpp b/utils/azone2/3d.hpp new file mode 100644 index 000000000..4d125817c --- /dev/null +++ b/utils/azone2/3d.hpp @@ -0,0 +1,101 @@ +#ifndef __OPENEQ_THREED__ +#define __OPENEQ_THREED__ + +#include "3d_base.hpp" +#include +#include +#include + +using namespace std; + +struct ObjectGroupEntry +{ + ObjectGroupEntry() + { + IncludeInMap = true; + } + + bool FromTOG; + bool IncludeInMap; + float TileX, TileY, TileZ; + float x, y, z; + float RotX, RotY, RotZ; + float ScaleX, ScaleY, ScaleZ; + list SubObjects; +}; + +class Octree_Node; + +class Bone { +public: + char *name; + + int bone1, bone2; + + float x, y, z; + float e[4]; + float scale[3]; + + int vert_count; + int *verts; +}; + +class Zone_Model { +public: + Zone_Model() {} + ~Zone_Model() {} + + Vertex **verts; + Polygon **polys; + Octree_Node *octree; + Texture **tex; + + int vert_count, poly_count, tex_count; +}; + +class MobModel { +public: + MobModel() {} + ~MobModel() {} + + Vertex **verts; + Polygon **polys; + Texture **tex; + + int vert_count, poly_count, tex_count; + + Bone *bones; + + int bone_count; +}; + +class Content_3D { +public: + Content_3D() { this->mob_model_count = 0; this->mob_count = 0; } + ~Content_3D() {} + + Model *GetModel(char *model_name); + + void GenerateOctree(); + + void MergeTextures(); + void MergeTexturesLazy(); + + Zone_Model *zone_model; + + Model **models; + Placeable **placeable; + + int model_count, plac_count; // Count of placeable object models and count of instances + int ObjectGroupCount; + + MobModel **mob_models; + Placeable **mob_locs; + int mob_model_count, mob_count; + + vector ObjectGroups; + vector ModelNames; + vector PlaceableList; +}; + +#endif diff --git a/utils/azone2/3d_base.hpp b/utils/azone2/3d_base.hpp new file mode 100644 index 000000000..6277a3bec --- /dev/null +++ b/utils/azone2/3d_base.hpp @@ -0,0 +1,107 @@ +#ifndef __OPENEQ_THREED_BASE__ +#define __OPENEQ_THREED_BASE__ + + +#pragma pack(1) + + +#include "archive.hpp" + +#ifdef GLMODELVIEWER +#include // Header File For The OpenGL32 Library +#endif + +struct Light { + float x, y, z; + float r, g, b; + float rad; +} typedef Light; + +struct Vertex { + float x, y, z; + float i, j, k; + float u, v; + +// int bone; + + Vertex() + { + x = y = z = i = j = k = u = v = 0.0f; + }; + + Vertex(float ix, float iy, float iz) + { + x = ix; + y = iy; + z = iz; + } +} typedef Vertex; + + +struct Polygon { + int flags; + + int v1, v2, v3; + + int tex; +} typedef Polygon; + + +struct Texture { + Archive *archive; + char frame_count; + char current_frame; +#ifdef GLMODELVIEWER + GLuint *tex; +#else + int32 *tex; +#endif + char **filenames; + + char flags; +} typedef Texture; + +class Model { +public: + Model() { IncludeInMap = false; verts = NULL; vert_count = 0; polys = NULL; poly_count = 0; tex = NULL; tex_count = 0; name = NULL; } + ~Model() {} + + Vertex **verts; + Polygon **polys; + Texture **tex; + + int vert_count, poly_count, tex_count; + + char *name; + bool IncludeInMap; // Include in EQEmu .map file +}; + + +class Placeable { +public: + Placeable() {} + + Placeable(int iModel, float ix, float iy, float iz, float irx, float iry, float irz, float iScale0, float iScale1, float iScale2) + { + model = iModel; + x = ix; + y = iy; + z = iz; + rx = irx; + ry = iry; + rz = irz; + scale[0] = iScale0; + scale[1] = iScale1; + scale[2] = iScale2; + } + + float x, y, z; + float rx, ry, rz; + float scale[3]; + + int model; +}; + +#pragma pack() + +#endif diff --git a/utils/azone2/CMakeLists.txt b/utils/azone2/CMakeLists.txt new file mode 100644 index 000000000..e6576fd90 --- /dev/null +++ b/utils/azone2/CMakeLists.txt @@ -0,0 +1,119 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +IF(NOT MSVC) + ADD_DEFINITIONS(-fpermissive) +ENDIF(NOT MSVC) + +#azone common +SET(azone_common_sources + dat.cpp + file.cpp + global.cpp + pfs.cpp + ter.cpp + wld.cpp + zon.cpp + zonv4.cpp +) + +SET(azone_common_headers + 3d.hpp + 3d_base.hpp + archive.hpp + dat.hpp + file.hpp + file_loader.hpp + global.hpp + octree.hpp + pfs.hpp + s3d.h + ter.hpp + types.h + wld.hpp + wld_structs.hpp + zon.hpp + zonv4.hpp +) + +ADD_LIBRARY(azone_common ${azone_common_sources} ${azone_common_headers}) +SET(LIBRARY_OUTPUT_PATH ../../Bin) + +#azone +SET(azone_sources + azone.cpp +) + +SET(azone_headers + azone.h +) + +ADD_EXECUTABLE(azone ${azone_sources} ${azone_headers}) +TARGET_LINK_LIBRARIES(azone azone_common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE}) +IF(MSVC) + SET_TARGET_PROPERTIES(azone PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF") + TARGET_LINK_LIBRARIES(azone "Ws2_32.lib") +ENDIF(MSVC) + +IF(UNIX) + TARGET_LINK_LIBRARIES(azone "z") +ENDIF(UNIX) + +SET(EXECUTABLE_OUTPUT_PATH ../../Bin) + +#awater +SET(awater_sources + awater.cpp +) + +SET(awater_headers + awater.h +) + +ADD_EXECUTABLE(awater ${awater_sources} ${awater_headers}) +TARGET_LINK_LIBRARIES(awater azone_common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE}) +IF(MSVC) + SET_TARGET_PROPERTIES(awater PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF") + TARGET_LINK_LIBRARIES(awater "Ws2_32.lib") +ENDIF(MSVC) + +IF(UNIX) + TARGET_LINK_LIBRARIES(awater "z") +ENDIF(UNIX) + +SET(EXECUTABLE_OUTPUT_PATH ../../Bin) + +#listobj +SET(listobj_sources + listobj.cpp +) + +SET(listobj_headers +) + +ADD_EXECUTABLE(listobj ${listobj_sources} ${listobj_headers}) +TARGET_LINK_LIBRARIES(listobj azone_common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE}) +IF(MSVC) + SET_TARGET_PROPERTIES(listobj PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF") + TARGET_LINK_LIBRARIES(listobj "Ws2_32.lib") +ENDIF(MSVC) + +IF(UNIX) + TARGET_LINK_LIBRARIES(listobj "z") +ENDIF(UNIX) + +#on windows: glmodelviewer +IF(MSVC) + SET(glmodelviewer_sources + GLModelViewer.cpp + ) + + SET(glmodelviewer_headers + GLModelViewer.h + ) + + ADD_EXECUTABLE(glmodelviewer WIN32 ${glmodelviewer_sources} ${glmodelviewer_headers}) + TARGET_LINK_LIBRARIES(glmodelviewer azone_common debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE} "opengl32.lib" "GLU32.lib" "Ws2_32.lib") + SET_TARGET_PROPERTIES(glmodelviewer PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF") + + SET(EXECUTABLE_OUTPUT_PATH ../../Bin) +ENDIF(MSVC) diff --git a/utils/azone2/GLModelViewer.cpp b/utils/azone2/GLModelViewer.cpp new file mode 100644 index 000000000..4c44d5257 --- /dev/null +++ b/utils/azone2/GLModelViewer.cpp @@ -0,0 +1,548 @@ + +// Quick and dirty EQ model viewer for Windows, using OpenGL +// +// Put together by Derision from EQEmu forums using basic Windows OpenGL framework from NeHe tutorials @ gamedev.net +// and S3D and EQG file loaders from OpenEQ (modified a bit to fix some bugs and support newer EQG formats). + + + +#ifdef WIN32 +#define Polygon Polygon_win32 +#include +#undef Polygon +#endif +#include +#include +#include +#include +#include +#include "types.h" +#include "wld.hpp" +#include "archive.hpp" +#include "pfs.hpp" +#include "file_loader.hpp" +#include "zon.hpp" +#include "zonv4.hpp" +#include "ter.hpp" + + +typedef struct _vertex{ + float x; + float y; + float z; + +}VERTEX; + +void DrawEQModel(FileLoader *fileloader, int modnum); +FileLoader *mfileloader; +bool ProcessZoneFile(const char *shortname); +enum EQFileType { S3D, EQG, UNKNOWN }; + +HDC hDC=NULL; +HGLRC hRC=NULL; +HWND hWnd=NULL; +HINSTANCE hInstance; + +bool keys[256]; +char ch; +bool active=true; +GLuint base; + +int modelnum = 1; // The Number of the model we are currently displaying. +float angle = 0; // used to rotate the model. Updated by a timer + +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc + +GLvoid BuildFont(GLvoid) +{ + HFONT font; + HFONT oldfont; + + base = glGenLists(96); + + font = CreateFont(-24, 0, 0, 0, FW_BOLD, false, false, false, ANSI_CHARSET, + OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, + FF_DONTCARE|DEFAULT_PITCH, "Courier New"); + + oldfont = (HFONT)SelectObject(hDC, font); + wglUseFontBitmaps(hDC, 32, 96, base); + SelectObject(hDC, oldfont); + DeleteObject(font); +} + +GLvoid KillFont(GLvoid) +{ + glDeleteLists(base, 96); +} + +GLvoid glPrint(const char *fmt, ...) +{ + char text[256]; + va_list ap; + + if (fmt == NULL) return; + + va_start(ap, fmt); + vsprintf(text, fmt, ap); + va_end(ap); + + glPushAttrib(GL_LIST_BIT); + glListBase(base - 32); + glCallLists(strlen(text), GL_UNSIGNED_BYTE, text); + glPopAttrib(); +} + +GLvoid ReSizeGLScene(GLsizei width, GLsizei height) +{ + if (height==0)height=1; + + glViewport(0,0,width,height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(60.0f,(GLfloat)width/(GLfloat)height,0.1f,12000.0f); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); +} + +int InitGL(GLvoid) +{ + glShadeModel(GL_SMOOTH); + glClearColor(0.0f, 0.0f, 0.0f, 0.5f); + glClearDepth(1.0f); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + BuildFont(); + return true; +} + +int DrawGLScene(char *ZoneFileName) +{ + char textBuffer[100]; + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glLoadIdentity(); + + if(mfileloader->model_data.models[modelnum]) + DrawEQModel(mfileloader, modelnum); + + glLoadIdentity(); + glTranslatef(0.0f,0.0f,-1.2f); + glColor3f(100.0f,0.0f,0.0f); + glRasterPos2f(-1.15f,0.56f); + + if(mfileloader->model_data.models[modelnum]) + if(mfileloader->model_data.models[modelnum]->name) + sprintf(textBuffer," %s: Model Number %4d. Name %s", ZoneFileName, modelnum, mfileloader->model_data.models[modelnum]->name); + else + sprintf(textBuffer," %s: Model Number %4d. Not Viewable (load failed).", ZoneFileName, modelnum); + else + sprintf(textBuffer," %s: Model Number %4d. Not Viewable (probably zone mesh).", ZoneFileName, modelnum); + + glPrint(textBuffer); + sprintf(textBuffer," Use the + and - keys to cycle through models."); + glRasterPos2f(-1.15f,0.50f); + glPrint(textBuffer); + + + return true; +} + +GLvoid KillGLWindow(GLvoid) { + if (hRC) { + if (!wglMakeCurrent(NULL,NULL)) { + MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); + } + + if (!wglDeleteContext(hRC)) { + MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); + } + hRC=NULL; + } + + if (hDC && !ReleaseDC(hWnd,hDC)) { + MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); + hDC=NULL; + } + + if (hWnd && !DestroyWindow(hWnd)) { + MessageBox(NULL,"Could Not Release hWnd.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); + hWnd=NULL; + } + + if (!UnregisterClass("OpenGL",hInstance)) { + MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); + hInstance=NULL; + } + KillFont(); +} + + +BOOL CreateGLWindow(char* title, int width, int height, int bits) { + GLuint PixelFormat; + WNDCLASS wc; + DWORD dwExStyle; + DWORD dwStyle; + RECT WindowRect; + WindowRect.left=(long)0; + WindowRect.right=(long)width; + WindowRect.top=(long)0; + WindowRect.bottom=(long)height; + + hInstance = GetModuleHandle(NULL); + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.lpfnWndProc = (WNDPROC) WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hInstance; + wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.lpszMenuName = NULL; + wc.lpszClassName = "OpenGL"; + + if (!RegisterClass(&wc)) { + MessageBox(NULL,"Failed To Register The Window Class.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return false; + } + + dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + dwStyle=WS_OVERLAPPEDWINDOW; + + AdjustWindowRectEx(&WindowRect, dwStyle, false, dwExStyle); + + if (!(hWnd=CreateWindowEx(dwExStyle, "OpenGL", title, dwStyle|WS_CLIPSIBLINGS|WS_CLIPCHILDREN, + 0, 0, WindowRect.right-WindowRect.left, WindowRect.bottom-WindowRect.top, + NULL, NULL, hInstance, NULL))) { + KillGLWindow(); + MessageBox(NULL,"Window Creation Error.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return false; + } + + static PIXELFORMATDESCRIPTOR pfd= { + sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER, + PFD_TYPE_RGBA, bits, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, PFD_MAIN_PLANE, + 0, 0, 0, 0 + }; + + if (!(hDC=GetDC(hWnd))) { + KillGLWindow(); + MessageBox(NULL,"Can't Create A GL Device Context.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return false; + } + + if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) { + KillGLWindow(); + MessageBox(NULL,"Can't Find A Suitable PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return false; + } + + if(!SetPixelFormat(hDC,PixelFormat,&pfd)) { + KillGLWindow(); + MessageBox(NULL,"Can't Set The PixelFormat.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return false; + } + + if (!(hRC=wglCreateContext(hDC))) { + KillGLWindow(); + MessageBox(NULL,"Can't Create A GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return false; + } + + if(!wglMakeCurrent(hDC,hRC)) { + KillGLWindow(); + MessageBox(NULL,"Can't Activate The GL Rendering Context.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return false; + } + + ShowWindow(hWnd,SW_SHOW); + SetForegroundWindow(hWnd); + SetFocus(hWnd); + ReSizeGLScene(width, height); + + if (!InitGL()) { + KillGLWindow(); + MessageBox(NULL,"Initialization Failed.","ERROR",MB_OK|MB_ICONEXCLAMATION); + return false; + } + + SetTimer(hWnd, 1, 50, (TIMERPROC) NULL); + + return true; +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch (uMsg) { + case WM_ACTIVATE: + if (!HIWORD(wParam)) active=true; + else + active=false; + + return 0; + case WM_SYSCOMMAND: + switch (wParam) { + case SC_SCREENSAVE: + case SC_MONITORPOWER: + return 0; + } + break; + case WM_CLOSE: + PostQuitMessage(0); + return 0; + case WM_KEYDOWN: + keys[wParam] = true; + return 0; + case WM_CHAR: + ch = wParam; + return 0; + case WM_KEYUP: + keys[wParam] = false; + return 0; + case WM_SIZE: + ReSizeGLScene(LOWORD(lParam),HIWORD(lParam)); + return 0; + case WM_TIMER: + angle = angle + 1; + if(angle>359) angle = 0; + return 0; + + } + + return DefWindowProc(hWnd,uMsg,wParam,lParam); +} + +void TranslateVertex(VERTEX &v, float XOffset, float YOffset, float ZOffset) { + + v.x = v.x + XOffset; + v.y = v.y + YOffset; + v.z = v.z + ZOffset; +} + +void ScaleVertex(VERTEX &v, float XScale, float YScale, float ZScale) { + + v.x = v.x * XScale; + v.y = v.y * YScale; + v.z = v.z * ZScale; +} + + +void DrawEQModel(FileLoader *fileloader, int modnum) { + + Polygon *poly; + Vertex *verts[3]; + VERTEX v1, v2, v3; + + float maxDimension = 0, minx = 999999, miny = 999999, minz = 999999, maxx = -999999, maxy=-999999, maxz=-999999; + Model *model = fileloader->model_data.models[modnum]; + + for(int i = 0; i < model->poly_count; ++i) { + poly = model->polys[i]; + + verts[0] = model->verts[poly->v1]; + verts[1] = model->verts[poly->v2]; + verts[2] = model->verts[poly->v3]; + + for(int j=0; j<3; j++) { + if(verts[j]->x > maxDimension) maxDimension = verts[j]->x; + if(verts[j]->y > maxDimension) maxDimension = verts[j]->y; + if(verts[j]->z > maxDimension) maxDimension = verts[j]->z; + + if(verts[j]->x < minx) minx = verts[j]->x; + if(verts[j]->y < miny) miny = verts[j]->y; + if(verts[j]->z < minz) minz = verts[j]->z; + + if(verts[j]->x > maxx) maxx = verts[j]->x; + if(verts[j]->y > maxy) maxy = verts[j]->y; + if(verts[j]->z > maxz) maxz = verts[j]->z; + } + } + + maxDimension = 0; + if(maxx-minx>maxDimension) maxDimension = maxx-minx; + if(maxy-miny>maxDimension) maxDimension = maxy-miny; + if(maxz-minz>maxDimension) maxDimension = maxz-minz; + + // Hack for very small models (e.g. spoons) + if(maxDimension>1) + glTranslatef(-1.5f,0.0f,-(maxDimension*2)); + else + glTranslatef(-1.5f,0.0f,-10); + + // angle is updated by a timer every 50ms. + glRotatef(angle, 1, 0, 0); + glRotatef(angle, 0, 1, 0); + glRotatef(angle, 0, 0, 1); + + glBegin(GL_TRIANGLES); + + for(int i = 0; i < model->poly_count; ++i) { + poly = model->polys[i]; + + verts[0] = model->verts[poly->v1]; + verts[1] = model->verts[poly->v2]; + verts[2] = model->verts[poly->v3]; + + v1.x = verts[0]->x; v1.y = verts[0]->y; v1.z = verts[0]->z; + v2.x = verts[1]->x; v2.y = verts[1]->y; v2.z = verts[1]->z; + v3.x = verts[2]->x; v3.y = verts[2]->y; v3.z = verts[2]->z; + + // The aim of this is to centre the model in the window + + TranslateVertex(v1, -(minx + (maxx-minx)/2), -(miny + (maxy-miny)/2), -(minz + (maxz-minz)/2)); + TranslateVertex(v2, -(minx + (maxx-minx)/2), -(miny + (maxy-miny)/2), -(minz + (maxz-minz)/2)); + TranslateVertex(v3, -(minx + (maxx-minx)/2), -(miny + (maxy-miny)/2), -(minz + (maxz-minz)/2)); + + // For very small models, magnify them + if(maxDimension<=1) { + ScaleVertex(v1, 10, 10, 10); + ScaleVertex(v2, 10, 10, 10); + ScaleVertex(v3, 10, 10, 10); + } + + // Assign a kind of random colour to each polygon + + //glColor3b((i%50)+50,(i*5)%200,(i*10)%200); + float col = (float)(100 + ((i*10) % 150)) /250 * 1.0f; + glColor3f(col, col, col); + + glVertex3f(v1.x, v1.z, v1.y); + glVertex3f(v2.x, v2.z, v2.y); + glVertex3f(v3.x, v3.z, v3.y); + + } + glEnd(); +} + +bool ProcessZoneFile(const char *shortname) { + + char bufs[96]; + Archive *archive; + + Zone_Model *zm; + FILE *fff; + EQFileType FileType = UNKNOWN; + GLuint *textures; + + sprintf(bufs, "%s.s3d", shortname); + + archive = new PFSLoader(); + fff = fopen(bufs, "rb"); + if(fff != NULL) + FileType = S3D; + else { + sprintf(bufs, "%s.eqg", shortname); + fff = fopen(bufs, "rb"); + if(fff != NULL) + FileType = EQG; + } + + if(FileType == UNKNOWN) { + MessageBox(NULL,"Unable to locate specified zone either as EQG or S3D","ERROR",MB_OK|MB_ICONEXCLAMATION); + return(false); + } + + if(archive->Open(fff) == 0) { + MessageBox(NULL,"Unable to open container file","ERROR",MB_OK|MB_ICONEXCLAMATION); + + return(false); + } + + switch(FileType) { + case S3D: + mfileloader = new WLDLoader(); + if(mfileloader->Open(NULL, (char *) shortname, archive) == 0) { + + MessageBox(NULL,"Error reading WLD from container file","ERROR",MB_OK|MB_ICONEXCLAMATION); + return(false); + } + break; + case EQG: + mfileloader = new ZonLoader(); + if(mfileloader->Open(NULL, (char *) shortname, archive) == 0) { + delete mfileloader; + mfileloader = new Zonv4Loader(); + if(mfileloader->Open(NULL, (char *) shortname, archive) == 0) { + MessageBox(NULL,"Error reading ZON/TER from container file","ERROR",MB_OK|MB_ICONEXCLAMATION); + return(false); + } + } + break; + case UNKNOWN: + break; + } + + if(mfileloader->model_data.model_count==0) { + MessageBox(NULL,"No models found. For S3D, probably could not locate _obj.s3d file","ERROR",MB_OK|MB_ICONEXCLAMATION); + return false; + } + + + zm = mfileloader->model_data.zone_model; + + return(true); +} + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + MSG msg; + BOOL done=false; + FILE *fp; + + for(int i=0;i<256;i++) + keys[i] = false; + + + char* buf = (char *) new char[strlen(lpCmdLine) + 1] ; + char *pTmp = buf; + + if(!strlen(lpCmdLine)) { + MessageBox(NULL,"Specify the shortname of the zone (without the S3D or EQG extension) on the command line.", + "ERROR",MB_OK | MB_ICONINFORMATION); + return 0; + } + + strcpy(buf,lpCmdLine); + + if(!ProcessZoneFile(buf)) return 0; + + if (!CreateGLWindow("EQ Model Viewer",1280,768,16)) + return 0; + + while(!done) { + if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) { + if (msg.message==WM_QUIT) + done=true; + else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + else { + if ((active && !DrawGLScene(buf)) || keys[VK_ESCAPE]) + done=true; + else + SwapBuffers(hDC); + + // Use + and - to cycle through the models + + if (toupper(ch)=='+') { + if(modelnum+1 < mfileloader->model_data.model_count) + modelnum++; + else + modelnum=0; + angle = 0; + } + if (toupper(ch)=='-') { + if(modelnum>0) + modelnum--; + else + modelnum = mfileloader->model_data.model_count - 1; + + angle = 0; + } + ch = 0; + } + } + + KillGLWindow(); + return (msg.wParam); +} diff --git a/utils/azone2/GLModelViewer.h b/utils/azone2/GLModelViewer.h new file mode 100644 index 000000000..d00d47e78 --- /dev/null +++ b/utils/azone2/GLModelViewer.h @@ -0,0 +1,3 @@ +#pragma once + +#include "resource.h" diff --git a/utils/azone2/README b/utils/azone2/README new file mode 100644 index 000000000..952c4b6c8 --- /dev/null +++ b/utils/azone2/README @@ -0,0 +1,79 @@ + +Azone2, awater, listobj, glModelViewer + +azone2 creates .map files for EQEmu, used in LOS and BestZ, etc. + +.S3D and .EQG zone files found with the Titanium EQ distribution are +supported. + +to use all of these utilities, copy them to a directory containing your .S3D and .EQG +files. + +IMPORTANT NOTE: + +Older EQGs had the .ZON file within the .EQG container. Newer EQGs, e.g. +tutorialb, dreadspire, have the .ZON file as a separate file in your EQ directory. +To include placeable objects for these, YOU MUST have both the .EQG and .ZON file +in the same directory as azone2. + +To generate a map file, specifit the shortname of the zone (without the extension). + +e.g. + +./azone2 tox +./azone2 anguish + +Copy the resultant .map file to the Maps directory on your server. + + +20/06/08 Derision: + To include placeable objects, use the listobj program to find the models you want, + e.g. ./listobj tox + +LISTOBJ: List Placeable Objects in .S3D or .EQG zone files. +Placeable Object 0 @ ( -420.42, 2443.43, -50.30 uses model 27 TIKI_DMSPRITEDEF +Placeable Object 1 @ ( -482.54, 2446.06, -45.13 uses model 27 TIKI_DMSPRITEDEF +Placeable Object 2 @ ( -439.99, 2603.35, -29.50 uses model 28 TORCH1_DMSPRITEDEF +Placeable Object 3 @ ( 158.28, 1094.74, -50.40 uses model 27 TIKI_DMSPRITEDEF +Placeable Object 4 @ ( 137.83, 1074.79, -49.91 uses model 17 HUT4_DMSPRITEDEF +Placeable Object 5 @ ( 150.64, 1205.48, -48.46 uses model 27 TIKI_DMSPRITEDEF +Placeable Object 6 @ ( 128.19, 1204.95, -48.37 uses model 27 TIKI_DMSPRITEDEF +Placeable Object 7 @ ( 139.57, 1153.50, -53.74 uses model 20 KDOCK_DMSPRITEDEF +Placeable Object 8 @ ( 1139.42, -837.60, -55.38 uses model 5 ERBRAZIER_DMSPRITEDEF + + ^ + | + | + This is the model number -------------| + +If you want azone to include all occurrences of HUT4_DMSPRITEDF (model 17) and KDOCK_DMSPRITEDF (20), +you would add a line to azone.ini as follows: + +# Include huts, houses and bridge from tox +tox.s3d,17,20 +# Include two bridges from Tutorialb +tutorialb.eqg,17,47 +# Include a couple of towers in Anguish +anguish.eqg,7,8 + +Note that as you can only specify a model number, it is not possible to only have selected occurrences +of that model included. + +Lines in azone.ini beginning with a # are ignored. + +TO INCLUDE PLACEABLE MODELS FOR .S3D ZONES, THE _OBJ.S3D FILE FOR THAT ZONE MUST ALSO BE PRESENT IN THE +SAME DIRECTORY. + +You can also use glmodelviewer (Windows only) to display the models in a zone to find the ones you want +to include. To run it, from a command prompt, e.g. + +glmodelviewer wallofslaughter + +Finally, awater generates .wtr files containing details of the water, lava, PVP etc areas in an S3D zone. +Usage example: + +./awater fieldofbone + +The windows solution and project files provided were produced with Visual C++ 2008 and as configured, +require a statically linkable zlib.lib. + diff --git a/utils/azone2/archive.hpp b/utils/azone2/archive.hpp new file mode 100644 index 000000000..8f3f5083d --- /dev/null +++ b/utils/azone2/archive.hpp @@ -0,0 +1,32 @@ +#ifndef __OPENEQ_ARCHIVE_API__ +#define __OPENEQ_ARCHIVE_API__ + +#include "global.hpp" + +#include + +class Archive { +public: + Archive() {} + virtual ~Archive() {} + + virtual int Open(FILE *fp) = 0; + virtual int Close() = 0; + + virtual int GetFile(char *name, uchar **buf, int *len) = 0; + virtual const char *FindExtension(const char *ext) = 0; + + char **filenames; + int count; + +protected: + uchar *buffer; + int buf_len; + + int status; + + uint32 *files; + FILE *fp; +}; + +#endif diff --git a/utils/azone2/awater.cpp b/utils/azone2/awater.cpp new file mode 100644 index 000000000..01704d4f2 --- /dev/null +++ b/utils/azone2/awater.cpp @@ -0,0 +1,375 @@ +/* + + AWater: Generate 'Water Maps' for EQEmu. 'Water Maps' is a misnomer as, + while originally intended to produce a map of water regions from .S3D + zone files, it also includes details of lava, PVP and other 'special' + areas. + + Based on Azone (Father NitWit) and OpenEQ (Daeken et al). Original + copyright notice from Azone follows: + + Father Nitwit's Zone to map conversion program. + Copyright (C) 2004 Father Nitwit (eqemu@8ass.com) + + This thing uses code from freaku, so whatever license that comes under + is relavent, if you care. + + the rest of it is GPL, even though I hate the GPL. + + 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 +*/ + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef short SHORT; +typedef unsigned long DWORD; + +#include +#include +#include +#include +//#include "EQWldData.h" +#include "awater.h" +#include "types.h" +#include "s3d.h" +#include "wld.hpp" + +#include "archive.hpp" +#include "pfs.hpp" + +#include "file_loader.hpp" +#include "zon.hpp" +#include "ter.hpp" + + +//this un-commented works with my map.cpp code correctly. +//with both of my inverts there commented. +#define INVERSEXY 1 + +#include +#include +using namespace std; + +//#define SPLIT_DEBUG + + +bool BuildWaterMap(const char *shortname, long DefaultRegionType); +void PrintBSP(BSP_Node *tree, long node_number); +long BSPFindRegion(BSP_Node *tree, long node_number, long region); +long BSPFindNode(BSP_Node *tree, long node_number, float x, float y, float z); +long BSPMarkRegion(BSP_Node *tree, long node_number, long region, int region_type); +long BSPCountNodes(BSP_Node *tree, long node_number); + + +int main(int argc, char *argv[]) { + + long DefaultRegionType=RegionTypeWater; + + if((argc < 2) || + (argc > 3) || +#ifndef WIN32 + ((argc == 3)&&(strcasecmp(argv[1],"-dl")))) { +#else + ((argc == 3)&&(_stricmp(argv[1],"-dl")))) { +#endif + + printf("Usage: %s [-dl] (zone short name) to generate a water/lava map\n\n", argv[0]); + printf(" -dl = mark special regions as lava if not tagged in .WLD\n"); + printf("\nIf -dl is omitted, untagged special regions will be marked as water\n"); + return(1); + } + + char bufm[250]; + + if(argc==3) + DefaultRegionType=RegionTypeLava; + sprintf(bufm, "%s.wtr", argv[argc-1]); + if(!BuildWaterMap(argv[argc-1],DefaultRegionType)) + return(1); + + return(0); + +} + + + +bool BuildWaterMap(const char *shortname, long DefaultRegionType) { + + char bufs[96]; + Archive *archive; + WLDLoader *fileloader; + long WaterOrLavaCount = 0; + + //TODO: clean up a LOT of memory that the freaku code does not + + + sprintf(bufs, "%s.s3d", shortname); + + archive = new PFSLoader(); + + FILE *s3df = fopen(bufs, "rb"); + if(s3df == NULL) { + // One day we may try EQG, but not today. + printf("Unable to open s3d file '%s'.\n", bufs); + return(false); + } + if(archive->Open(s3df) == 0) { + printf("Unable to open s3d file '%s'.\n", bufs); + return(false); + } + printf("Loading %s...\n", bufs); + + fileloader = new WLDLoader(); + + if(fileloader->Open(NULL, (char *) shortname, archive) == 0) { + printf("Error reading WLD from %s\n", bufs); + return(false); + } + + BSP_Node *tree = fileloader->tree; + struct_Data29 *data29; + + + for(int i=0; ifragcount; i++) { + if(fileloader->frags[i]->type == 0x29) { + printf("We have a type 0x29 fragment. "); + data29 = (struct_Data29 *) fileloader->frags[i]->frag; + printf("It has %ld regions and is marked as type %d. ", + data29->region_count, data29->region_type); + switch(data29->region_type) { + case RegionTypeUnsupported: { printf("Unsupported\n"); break; } + case RegionTypeUntagged: { + printf("Untagged. We will set it to "); + if(DefaultRegionType==RegionTypeWater) { + printf("Water\n"); + } else printf("Lava\n"); + data29->region_type = DefaultRegionType; + WaterOrLavaCount++; + break; + } + case RegionTypeWater: { printf("Water\n"); WaterOrLavaCount++; break; } + case RegionTypeLava: { printf("Lava\n"); WaterOrLavaCount++; break; } + case RegionTypeZoneLine: { printf("Zoneline\n"); break; } + case RegionTypePVP: { printf("PVP\n"); break; } + case RegionTypeSlime: { printf("Slime\n"); break; } + case RegionTypeIce: { printf("Ice\n"); break; } + case RegionTypeVWater: { printf("VWATER\n"); break; } + default: printf("UNKNOWN\n"); + } + } + + } + + + if(tree==NULL) { + printf("No BSP Tree. Bailing out\n"); + return(false); + } + + unsigned long BSPTreeSize = BSPCountNodes(tree, 1); + + printf("There are %ld nodes in the BSP tree\n", BSPTreeSize); + + // Now we mark each leaf in the BSP tree that is in a 'special area' with what type the area is + // Water, Lava, Zoneline etc + + for(int i=0; ifragcount; i++) { + if(fileloader->frags[i]->type == 0x29) { + data29 = (struct_Data29 *) fileloader->frags[i]->frag; + for(long j=0; jregion_count; j++) { + BSPMarkRegion(tree, 1,data29->region_array[j]+1, data29->region_type); + } + } + } + + // Now write out the file + + char bufm[250]; + sprintf(bufm, "%s.wtr", shortname); + + FILE *WaterFile = fopen(bufm, "wb"); + if(WaterFile == NULL) { + printf("Failed to open %s for writing\n", bufm); + return(false); + } + const char *WFMagic = "EQEMUWATER"; + const long WFVersion = 1; + + if(fwrite(WFMagic, strlen(WFMagic), 1, WaterFile) != 1) { + printf("Error writing output file\n"); + fclose(WaterFile); + return(false); + } + if(fwrite(&WFVersion, sizeof(WFVersion), 1, WaterFile) != 1) { + printf("Error writing output file\n"); + fclose(WaterFile); + return(false); + } + + if(fwrite(&BSPTreeSize, sizeof(BSPTreeSize), 1, WaterFile) != 1) { + printf("Error writing output file\n"); + fclose(WaterFile); + return(false); + } + + if(fwrite(tree, sizeof(BSP_Node), BSPTreeSize, WaterFile) != BSPTreeSize) { + printf("Error writing output file\n"); + fclose(WaterFile); + return(false); + } + + fclose(WaterFile); + return(true); +} + + +void PrintBSP(BSP_Node *tree, long node_number) { + printf("Node %ld, Left=%ld, Right=%ld\n", + node_number-1, + tree[node_number-1].left, + tree[node_number-1].right); + printf("Normals are %4.3f, %4.3f, %4.3f, SplitD is %4.3f\n", + tree[node_number-1].normal[0], + tree[node_number-1].normal[1], + tree[node_number-1].normal[2], + tree[node_number-1].splitdistance); + + if(tree[node_number-1].left!=0) PrintBSP(tree, tree[node_number-1].left); + if(tree[node_number-1].right!=0) PrintBSP(tree, tree[node_number-1].right); + if((tree[node_number-1].left==0)&& + (tree[node_number-1].right==0)) { + printf("Region pointer is %ld\n", tree[node_number-1].region); + } + +} + +long BSPCountNodes(BSP_Node *tree, long node_number) { + + long NodesInRightBranch = 0, NodesInLeftBranch = 0; + + if((tree[node_number-1].left==0)&& + (tree[node_number-1].right==0)) return 1; + + if(tree[node_number-1].left!=0) NodesInLeftBranch = BSPCountNodes(tree, (tree[node_number-1].left)); + if(tree[node_number-1].right!=0) NodesInRightBranch = BSPCountNodes(tree, (tree[node_number-1].right)); + + return(NodesInRightBranch + NodesInLeftBranch + 1); + +} + + +long BSPFindRegion(BSP_Node *tree, long node_number, long region) { + //printf("Find Region %ld in node %ld\n", region, node_number); + if(node_number<1) { + printf("Something went wrong\n"); + exit(1); + } + if((tree[node_number-1].left==0)&& + (tree[node_number-1].right==0)) { + if(tree[node_number-1].region==region) return node_number; + } + + + long retnode ; + if(tree[node_number-1].left!=0) { + retnode = BSPFindRegion(tree, tree[node_number-1].left, region); + if(retnode != 0) return retnode ; + } + + if(tree[node_number-1].right!=0) { + return BSPFindRegion(tree, tree[node_number-1].right, region); + } + + return 0; + +} + +long BSPFindNode(BSP_Node *tree, long node_number, float x, float y, float z) { + + float distance; + + + printf("BSP Find Node, currently in Node %ld\n", node_number); + // Are we at a leaf + + if((tree[node_number-1].left==0)&& + (tree[node_number-1].right==0)) { + return tree[node_number-1].region; + } + + // No, so determine which side of the split plane we are on + // + + distance = (x * tree[node_number-1].normal[0]) + + (y * tree[node_number-1].normal[1]) + + (z * tree[node_number-1].normal[2]) + + tree[node_number-1].splitdistance; + printf("Distance is %4.3f\n", distance); + // Guess which side to go down + if(distance == 0.0f) { + printf("Distance is 0, don't know what to do!\n"); + exit(1); + } + if(distance >0.0f) { + if(tree[node_number-1].left==0) { + printf("Pos and no left node, abort!\n"); + exit(1); + } + return BSPFindNode(tree, tree[node_number-1].left, + x, y, z); + } + + if(tree[node_number-1].right==0) { + printf("Neg and no right node, abort!\n"); + exit(1); + } + + return BSPFindNode(tree, tree[node_number-1].right, + x, y, z); + +} + + +long BSPMarkRegion(BSP_Node *tree, long node_number, long region, int region_type) { + //printf("Find Region %ld in node %ld\n", region, node_number); + if(node_number<1) { + printf("Something went wrong\n"); + exit(1); + } + if((tree[node_number-1].left==0)&& + (tree[node_number-1].right==0)) { + if(tree[node_number-1].region==region) { + tree[node_number-1].special=region_type; + return node_number; + } + } + + long retnode ; + if(tree[node_number-1].left!=0) { + retnode = BSPMarkRegion(tree, tree[node_number-1].left, region, region_type); + if(retnode != 0) return retnode ; + } + + if(tree[node_number-1].right!=0) { + return BSPMarkRegion(tree, tree[node_number-1].right, region, region_type); + } + + return 0; + +} + + + + diff --git a/utils/azone2/awater.h b/utils/azone2/awater.h new file mode 100644 index 000000000..51ef7ffd9 --- /dev/null +++ b/utils/azone2/awater.h @@ -0,0 +1,16 @@ +typedef enum { + RegionTypeUnsupported = -2, + RegionTypeUntagged = -1, + RegionTypeNormal = 0, + RegionTypeWater = 1, + RegionTypeLava = 2, + RegionTypeZoneLine = 3, + RegionTypePVP = 4, + RegionTypeSlime = 5, + RegionTypeIce = 6, + RegionTypeVWater =7 + + +} WaterRegionType; + + diff --git a/utils/azone2/azone.cpp b/utils/azone2/azone.cpp new file mode 100644 index 000000000..cc9e85468 --- /dev/null +++ b/utils/azone2/azone.cpp @@ -0,0 +1,1623 @@ +/* + + Father Nitwit's Zone to map conversion program. + Copyright (C) 2004 Father Nitwit (eqemu@8ass.com) + + This thing uses code from freaku, so whatever license that comes under + is relavent, if you care. + + the rest of it is GPL, even though I hate the GPL. + + + Derision: 20/06/2008 + Replaced S3D and EQG fileloaders with later OpenEQ based versions + Added placeable object support. + Altered order of vertices in polygons from EQG files so that map + files produced from S3D and EQG zones are consistent in the vertex + order and therefore surface normals. + + 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 +*/ + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef short SHORT; +typedef unsigned long DWORD; + +#include +#include +#include +#include +//#include "EQWldData.h" +#include "types.h" +#include "azone.h" +#include "wld.hpp" + +#include "archive.hpp" +#include "pfs.hpp" + +#include "file_loader.hpp" +#include "zon.hpp" +#include "ter.hpp" +#include "zonv4.hpp" + +//TODO: I am trimming faces for which all vertices are above MAX_Z +// but I am not taking out the vertices to go with them. + + +//this un-commented works with my map.cpp code correctly. +//with both of my inverts there commented. +#define INVERSEXY 1 +//#define DEBUG +#include +#include +using namespace std; + +//#define SPLIT_DEBUG + + + +#ifndef MAX_QUADRENT_FACES +#ifndef MIN_QUADRENT_SIZE +#error Umm... your asking for trouble by turning off both stopping criteria +#endif +#endif + +int main(int argc, char *argv[]) { + + printf("AZONE2: EQEmu .MAP file generator with placeable object support.\n"); + + if(argc != 2) { + printf("Usage: %s (zone short name)\n", argv[0]); + return(1); + } + + char bufm[250]; + + sprintf(bufm, "%s.map", argv[1]); + + QTBuilder QT; + + if(!QT.build(argv[1])) + return(1); + + if(!QT.writeMap(bufm)) + return(1); + + return(0); +} + +QTBuilder::QTBuilder() { + _root = NULL; + + faceCount = 0; + faceBlock = NULL; + +#ifdef COUNT_MACTHES + gEasyMatches = 0; + gEasyExcludes = 0; + gHardMatches = 0; + gHardExcludes = 0; +#endif +} + +QTBuilder::~QTBuilder() { + if(_root != NULL) + delete _root; + _root = NULL; + if(faceBlock != NULL) + delete [] faceBlock; + faceBlock = NULL; +} + +bool QTBuilder::build(const char *shortname) { + + + char bufs[96]; + Archive *archive; + FileLoader *fileloader; + Zone_Model *zm; + FILE *fff; + EQFileType FileType = UNKNOWN; + + bool V4Zone = false; + + sprintf(bufs, "%s.s3d", shortname); + + archive = new PFSLoader(); + fff = fopen(bufs, "rb"); + if(fff != NULL) + FileType = S3D; + else { + sprintf(bufs, "%s.eqg", shortname); + fff = fopen(bufs, "rb"); + if(fff != NULL) + FileType = EQG; + } + + if(FileType == UNKNOWN) { + printf("Unable to locate %s.s3d or %s.eqg\n", shortname, shortname); + return(false); + } + + if(archive->Open(fff) == 0) { + printf("Unable to open container file '%s'\n", bufs); + return(false); + } + + switch(FileType) { + case S3D: + fileloader = new WLDLoader(); + if(fileloader->Open(NULL, (char *) shortname, archive) == 0) { + printf("Error reading WLD from %s\n", bufs); + return(false); + } + break; + case EQG: + fileloader = new ZonLoader(); + if(fileloader->Open(NULL, (char *) shortname, archive) == 0) { + delete fileloader; + fileloader = new Zonv4Loader(); + if(fileloader->Open(NULL, (char *) shortname, archive) == 0) { + printf("Error reading ZON/TER from %s\n", bufs); + return(false); + } + V4Zone = true; + } + break; + case UNKNOWN: + // Just here to stop the compiler warning + break; + } + + zm = fileloader->model_data.zone_model; + + long i; + VERTEX v1, v2, v3; + for(i = 0; i < zm->poly_count; ++i) { +#ifdef INVERSEXY + v1.y = zm->verts[zm->polys[i]->v1]->x; + v1.x = zm->verts[zm->polys[i]->v1]->y; +#else + v1.x = zm->verts[zm->polys[i]->v1]->x; + v1.y = zm->verts[zm->polys[i]->v1]->y; +#endif + v1.z = zm->verts[zm->polys[i]->v1]->z; +#ifdef INVERSEXY + v2.y = zm->verts[zm->polys[i]->v2]->x; + v2.x = zm->verts[zm->polys[i]->v2]->y; +#else + v2.x = zm->verts[zm->polys[i]->v2]->x; + v2.y = zm->verts[zm->polys[i]->v2]->y; +#endif + v2.z = zm->verts[zm->polys[i]->v2]->z; +#ifdef INVERSEXY + v3.y = zm->verts[zm->polys[i]->v3]->x; + v3.x = zm->verts[zm->polys[i]->v3]->y; +#else + v3.x = zm->verts[zm->polys[i]->v3]->x; + v3.y = zm->verts[zm->polys[i]->v3]->y; +#endif + v3.z = zm->verts[zm->polys[i]->v3]->z; + + AddFace(v1, v2, v3); + } + + printf("There are %u vertices and %u faces.\n", _FaceList.size()*3, _FaceList.size()); + + if(fileloader->model_data.plac_count) + { + if(V4Zone) + { + vector::iterator Iterator; + + Iterator = fileloader->model_data.ObjectGroups.begin(); + AddPlaceableV4(fileloader, bufs, false); + } + else + AddPlaceable(fileloader, bufs, false); + } + else + printf("No placeable objects (or perhaps %s_obj.s3d not present).\n", shortname); + + printf("After processing placeable objects, there are %u vertices and %u faces.\n", _FaceList.size()*3, _FaceList.size()); + + unsigned long r; + + faceCount = _FaceList.size(); + + + + faceBlock = new FACE[faceCount]; + //im not going to assume I know vectors are stored in contiguous blocks + for(r = 0; r < faceCount; r++) { + faceBlock[r] = _FaceList[r]; + } + + //build quad tree... prolly much slower than it needs to be. + float minx, miny, maxx, maxy; + minx = 1e12; + miny = 1e12; + maxx = -1e12; + maxy = -1e12; + + //find our limits. + for(r = 0; r < faceCount; r++) { + //a bit of lazyness going on here... + { + VERTEX &v = faceBlock[r].a; + if(v.x > maxx) + maxx = v.x; + if(v.x < minx) + minx = v.x; + if(v.y > maxy) + maxy = v.y; + if(v.y < miny) + miny = v.y; + } + { + VERTEX &v = faceBlock[r].b; + if(v.x > maxx) + maxx = v.x; + if(v.x < minx) + minx = v.x; + if(v.y > maxy) + maxy = v.y; + if(v.y < miny) + miny = v.y; + } + { + VERTEX &v = faceBlock[r].c; + if(v.x > maxx) + maxx = v.x; + if(v.x < minx) + minx = v.x; + if(v.y > maxy) + maxy = v.y; + if(v.y < miny) + miny = v.y; + } + } + + printf("Bounding box: %.2f < x < %.2f, %.2f < y < %.2f\n", minx, maxx, miny, maxy); + + printf("Building quadtree.\n"); + + _root = new QTNode(this, minx, maxx, miny, maxy); + if(_root == NULL) { + printf("Unable to allocate new QTNode.\n"); + return(false); + } + + //build our initial set of faces... all of them: + FACE *faceptr = faceBlock; + _root->faces.resize(faceCount); + for(r = 0; r < faceCount; r++) { + _root->faces[r].face = faceptr; + _root->faces[r].index = r; + faceptr++; + } + + _root->divideYourself(0); + + printf("Done building quad tree...\n"); + +#ifdef COUNT_MACTHES + printf("Match counters: %lu easy in, %lu easy out, %lu hard in, %lu hard out.\n", gEasyMatches, gEasyExcludes, gHardMatches, gHardExcludes); +#endif + + fileloader->Close(); + + delete fileloader; + + archive->Close(); + + delete archive; + + return(true); +} + + + +bool QTBuilder::writeMap(const char *file) { + if(_root == NULL) + return(false); + + printf("Writing map file.\n"); + + FILE *out = fopen(file, "wb"); + if(out == NULL) { + printf("Unable to open output file '%s'.\n", file); + return(1); + } + + mapHeader head; + head.version = MAP_VERSION; + head.face_count = faceCount; + head.node_count = _root->countNodes(); + head.facelist_count = _root->countFacelists(); + + if(fwrite(&head, sizeof(head), 1, out) != 1) { + printf("Error writing map file header.\n"); + fclose(out); + return(1); + + } + + printf("Map header: Version: 0x%08lX. %lu faces, %u nodes, %lu facelists\n", head.version, head.face_count, head.node_count, head.facelist_count); + + + + //write faceBlock + if(fwrite(faceBlock, sizeof(FACE), faceCount, out) != faceCount) { + printf("Error writing map file faces.\n"); + fclose(out); + return(1); + } + + //make our node blocks to write out... + nodeHeader *nodes = new nodeHeader[head.node_count]; + unsigned long *facelist = new unsigned long[head.facelist_count]; + if(nodes == NULL || facelist == NULL) { + printf("Error allocating temporary memory for output.\n"); + fclose(out); + return(1); //no memory + } + + unsigned long hindex = 0; + unsigned long findex = 0; + _root->fillBlocks(nodes, facelist, hindex, findex); + + if(fwrite(nodes, sizeof(nodeHeader), head.node_count, out) != head.node_count) { + printf("Error writing map file nodes.\n"); + fclose(out); + return(1); + } + if(fwrite(facelist, sizeof(unsigned long), head.facelist_count, out) != head.facelist_count) { + printf("Error writing map file face list.\n"); + fclose(out); + return(1); + } + + + long MapFileSize = ftell(out); + fclose(out); + delete[] nodes; + delete[] facelist; + + printf("Done writing map (%3.2fMB).\n", (float)MapFileSize/1048576); + + return(0); +} + + +QTNode::QTNode(QTBuilder *b, float Tminx, float Tmaxx, float Tminy, float Tmaxy) { + node1 = NULL; + node2 = NULL; + node3 = NULL; + node4 = NULL; + minx = Tminx; + maxx = Tmaxx; + miny = Tminy; + maxy = Tmaxy; + final = false; + buildVertexes(); + + builder = b; +} + +QTNode::~QTNode() { + clearNodes(); +} + +void QTNode::clearNodes() { + if(node1 != NULL) + delete node1; + if(node2 != NULL) + delete node2; + if(node3 != NULL) + delete node3; + if(node4 != NULL) + delete node4; + node1 = NULL; + node2 = NULL; + node3 = NULL; + node4 = NULL; +} + +//assumes that both supplied arrays are big enough per countNodes/Facelists +void QTNode::fillBlocks(nodeHeader *heads, unsigned long *flist, unsigned long &hindex, unsigned long &findex) { + nodeHeader *head = &heads[hindex]; + hindex++; + + head->minx = minx; + head->maxx = maxx; + head->miny = miny; + head->maxy = maxy; + head->flags = 0; +//printf("Node %u: (%.2f -> %.2f, %.2f -> %.2f)\n", hindex-1, head->minx, head->maxx, head->miny, head->maxy); + if(final) { + head->flags |= nodeFinal; + head->faces.count = faces.size(); + head->faces.offset = findex; +//printf(" Final node with %u faces, list offset %lu.\n", head->faces.count, head->faces.offset); + unsigned long r; + for(r = 0; r < head->faces.count; r++) { + flist[findex] = faces[r].index; + findex++; + } +// findex += head->faces.count; + } else { + //branch node. + head->flags = 0; + + if(node1 != NULL) { + head->nodes[0] = hindex; + node1->fillBlocks(heads, flist, hindex, findex); + } else { + head->nodes[0] = NODE_NONE; + } + if(node2 != NULL) { + head->nodes[1] = hindex; + node2->fillBlocks(heads, flist, hindex, findex); + } else { + head->nodes[1] = NODE_NONE; + } + if(node3 != NULL) { + head->nodes[2] = hindex; + node3->fillBlocks(heads, flist, hindex, findex); + } else { + head->nodes[2] = NODE_NONE; + } + if(node4 != NULL) { + head->nodes[3] = hindex; + node4->fillBlocks(heads, flist, hindex, findex); + } else { + head->nodes[3] = NODE_NONE; + } + } +} + +unsigned long QTNode::countNodes() const { + unsigned long c = 1; + if(node1 != NULL) + c += node1->countNodes(); + if(node2 != NULL) + c += node2->countNodes(); + if(node3 != NULL) + c += node3->countNodes(); + if(node4 != NULL) + c += node4->countNodes(); + return(c); +} + +unsigned long QTNode::countFacelists() const { + unsigned long c = final? faces.size() : 0; + if(node1 != NULL) + c += node1->countFacelists(); + if(node2 != NULL) + c += node2->countFacelists(); + if(node3 != NULL) + c += node3->countFacelists(); + if(node4 != NULL) + c += node4->countFacelists(); + return(c); +} + +/* +Map Format: + +1x mapHeader (head) +head.face_count x FACE +head.node_count x nodeHeader +head.facelist_count x unsigned long (indexes into face array) + + +*/ + +void QTNode::divideYourself(int depth) { +// printf("Dividing in box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d faces.\n", +// minx, maxx, miny, maxy, depth, faces.size()); + + unsigned long cc; + cc = faces.size(); +#ifdef MAX_QUADRENT_FACES + if(cc <= MAX_QUADRENT_FACES) { +#ifdef SPLIT_DEBUG +printf("Stopping (facecount) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d faces.\n", + minx, maxx, miny, maxy, depth, cc); +#endif + final = true; + return; + } +#endif + +#ifdef MIN_QUADRENT_SIZE + if((maxx - minx) < MIN_QUADRENT_SIZE || (maxy - miny) < MIN_QUADRENT_SIZE) { +#ifdef SPLIT_DEBUG +printf("Stopping on box (size) (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d faces.\n", + minx, maxx, miny, maxy, depth, cc); +#endif + final = true; + return; + } +#endif + + doSplit(); + + //get counts on our split + float c1, c2, c3, c4; + c1 = node1? node1->faces.size() : 0; + c2 = node2? node2->faces.size() : 0; + c3 = node3? node3->faces.size() : 0; + c4 = node4? node4->faces.size() : 0; + +#ifdef MIN_QUADRENT_GAIN + int miss = 0; + float gain1 = 1.0 - c1 / cc; + float gain2 = 1.0 - c2 / cc; + float gain3 = 1.0 - c3 / cc; + float gain4 = 1.0 - c4 / cc; + + //see how many missed the gain mark + if(gain1 < MIN_QUADRENT_GAIN) + miss++; + if(gain2 < MIN_QUADRENT_GAIN) + miss++; + if(gain3 < MIN_QUADRENT_GAIN) + miss++; + if(gain4 < MIN_QUADRENT_GAIN) + miss++; + + if(miss > MAX_QUADRENT_MISSES) { +#ifdef SPLIT_DEBUG +printf("Stopping (gain) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d faces.\n", + minx, maxx, miny, maxy, depth, cc); +#endif + final = true; + return; + } +#endif + + + //if all faces pass through all quadrents, then we are done + //partially obsoleted by gain test. + if(c1 == c2 && c1 == c3 && c1 == c4) { +#ifdef SPLIT_DEBUG +printf("Stopping (empty) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d faces.\n", + minx, maxx, miny, maxy, depth, cc); +#endif + final = true; + return; + } + + //there are prolly some more intelligent stopping criteria... + + depth++; + + if(node1 != NULL) + node1->divideYourself(depth); + if(node2 != NULL) + node2->divideYourself(depth); + if(node3 != NULL) + node3->divideYourself(depth); + if(node4 != NULL) + node4->divideYourself(depth); + + +} + +void QTNode::buildVertexes() { + + v[0].x = v[1].x = v[2].x = v[3].x = minx; + v[4].x = v[5].x = v[6].x = v[7].x = maxx; + + v[0].y = v[1].y = v[4].y = v[5].y = miny; + v[2].y = v[3].y = v[6].y = v[7].y = maxy; + + v[0].z = v[3].z = v[4].z = v[7].z = -999999; + v[1].z = v[2].z = v[5].z = v[6].z = 9999999; +} + + +static const GVector gNormals[6] = { + GVector(-1.0, 0.0, 0.0), + GVector(0.0, 1.0, 0.0), + GVector(1.0, 0.0, 0.0), + GVector(0.0, -1.0, 0.0), + GVector(0.0, 0.0, 1.0), + GVector(0.0, 0.0, -1.0), +}; + +static const unsigned short gIntFaces[6][4] = +{ + {0, 1, 2, 3}, + {3, 2, 6, 7}, + {7, 6, 5, 4}, + {4, 5, 1, 0}, + {5, 6, 2, 1}, + {7, 4, 0, 3} +}; + + +//stolen in haste from my fear pathing program, and untested in here... +bool edges_cross(GPoint *pt1, GPoint *pt2, const VERTEX *pt3, const VERTEX *pt4) { +//I love macros +#define IntersectDenom(p1, p2, p3, p4) \ +((p4->y - p3->y)*(p2->x - p1->x) - (p4->x - p3->x)*(p2->y - p1->y)) +#define IntersectNumerX(p1, p2, p3, p4) \ +((p4->x - p3->x)*(p1->y - p3->y) - (p4->y - p3->y)*(p1->x - p3->x)) +#define IntersectNumerY(p1, p2, p3, p4) \ +((p2->x - p1->x)*(p1->y - p3->y) - (p2->y - p1->y)*(p1->x - p3->x)) + +#define IntersectX(p1, p2, p3, p4, denom) \ +(p1->x + IntersectNumerX(p1, p2, p3, p4)*(p2->x - p1->x)/denom) +#define IntersectY(p1, p2, p3, p4, denom) \ +(p1->y + IntersectNumerX(p1, p2, p3, p4)*(p2->y - p1->y)/denom) + +#define CheckEqualXY(p1, p2) \ + (p1->x == p2->x && p1->y == p2->y) + +#define CoordOnLine(p1, p2, coord, dim) \ + (p1->dim > p2->dim? (coord >= p2->dim && coord <= p1->dim) : (coord >= p1->dim && coord <= p2->dim)) + +#define IntersectZfromX(p1, p2, inter) \ + (p2->x > p1->x? \ + (p1->z + ((inter - p1->x)/(p2->x - p1->x) * (p2->z - p1->z))) \ + :(p2->z + ((inter - p2->x)/(p1->x - p2->x) * (p1->z - p2->z)))) + + float denom = IntersectDenom(pt1, pt2, pt3, pt4); + if(denom != 0) { + + //the lines intersect, check segments now + float xinter = IntersectX(pt1, pt2, pt3, pt4, denom); + float yinter = IntersectY(pt1, pt2, pt3, pt4, denom); + + //now see if this point is on both segments + if( CoordOnLine(pt1, pt2, xinter, x) + && CoordOnLine(pt1, pt2, yinter, y) + && CoordOnLine(pt3, pt4, xinter, x) + && CoordOnLine(pt3, pt4, yinter, y) ){ +// printf("Line (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f) d=%.3f\n", pt1->x, pt1->y, pt1->z, pt2->x, pt2->y, pt2->z, pt1->Dist2(pt2)); +// printf("Hits (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f) d=%.3f\n", pt3->x, pt3->y, pt3->z, pt4->x, pt4->y, pt4->z, pt3->Dist2(pt4)); +// printf("At (%.3f, %.3f), which IS on both segments.\n", xinter, yinter); + + return(true); + } +// printf("At (%.3f, %.3f), which is not on both segments.\n", xinter, yinter); + } + return(false); +} + + +#define MAX(x,y) ((x) MIN(p1.y,p2.y)) { + if (py <= MAX(p1.y,p2.y)) { + if (px <= MAX(p1.x,p2.x)) { + if (p1.y != p2.y) { + xinters = (py-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x; + if (p1.x == p2.x || px <= xinters) + counter++; + } + } + } + } + p1 = p2; + } + + if (counter % 2 == 0) + return(false); + else + return(true); +} + +//quick function which got too messy in the loop below. +bool QTBuilder::FaceInNode(const QTNode *q, const FACE *f) { + const VERTEX *v1 = &f->a; + const VERTEX *v2 = &f->b; + const VERTEX *v3 = &f->c; + +#ifdef COUNT_MACTHES + gEasyMatches++; +#endif + + //Easy matches, points are within the quadrant. + if( ! ( v1->x <= q->minx || v1->x > q->maxx + || v1->y <= q->miny || v1->y > q->maxy ) ) { + return(true); + } + if( ! ( v2->x <= q->minx || v2->x > q->maxx + || v2->y <= q->miny || v2->y > q->maxy ) ) { + return(true); + } + if( ! ( v3->x <= q->minx || v3->x > q->maxx + || v3->y <= q->miny || v3->y > q->maxy ) ) { + return(true); + } + +#ifdef COUNT_MACTHES + gEasyMatches--; + gEasyExcludes++; +#endif + + //make sure it is even possible to insersect: + if( v1->x < q->minx && v2->x < q->minx && v3->x < q->minx ) + return(false); + if( v1->x > q->maxx && v2->x > q->maxx && v3->x > q->maxx ) + return(false); + if( v1->y < q->miny && v2->y < q->miny && v3->y < q->miny ) + return(false); + if( v1->y > q->maxy && v2->y > q->maxy && v3->y > q->maxy ) + return(false); + + +#ifdef COUNT_MACTHES + gEasyExcludes--; +#endif + +#ifdef COUNT_MACTHES + gHardMatches++; +#endif +// return(true); + + //harder: no points are in the cube + + //4 points of this node + GPoint pt1(q->minx, q->miny, 0), + pt2(q->minx, q->maxy, 0), + pt3(q->maxx, q->miny, 0), + pt4(q->maxx, q->maxy, 0); + + /* + //box lines: + pt1, pt2 + pt3, pt4 + pt1, pt3 + pt2, pt4 + + //tri lines + v1, v2 + v1, v3 + v2, v3 + */ + +#define CheckIntersect(p1, p2, p3, p4) \ +(((p4->y - p3->y)*(p2.x - p1.x) - (p4->x - p3->x)*(p2.y - p1.y)) != 0) + + int finaltest = + (edges_cross(&pt1, &pt2, v1, v2) + || edges_cross(&pt1, &pt2, v1, v3) + || edges_cross(&pt1, &pt2, v2, v3) + || edges_cross(&pt3, &pt4, v1, v2) + || edges_cross(&pt3, &pt4, v1, v3) + || edges_cross(&pt3, &pt4, v2, v3) + || edges_cross(&pt1, &pt3, v1, v2) + || edges_cross(&pt1, &pt3, v1, v3) + || edges_cross(&pt1, &pt3, v2, v3) + || edges_cross(&pt2, &pt4, v1, v2) + || edges_cross(&pt2, &pt4, v1, v3) + || edges_cross(&pt2, &pt4, v2, v3)); + + if(finaltest) return finaltest; + + VERTEX Triangle[3]; + Triangle[0]=*v1; Triangle[1]=*v2; Triangle[2] = *v3; + finaltest = PointInTriangle(Triangle, q->minx, q->miny) || + PointInTriangle(Triangle, q->minx, q->maxy) || + PointInTriangle(Triangle, q->maxx, q->maxy) || + PointInTriangle(Triangle, q->maxx, q->miny); + + return finaltest; +} + + + +void QTNode::doSplit() { + + + //find midpoints... + float midx = minx + (maxx - minx) / 2.0; + float midy = miny + (maxy - miny) / 2.0; + + //ordering following definitions in map.h + node1 = new QTNode(builder, midx, maxx, midy, maxy); + node2 = new QTNode(builder, minx, midx, midy, maxy); + node3 = new QTNode(builder, minx, midx, miny, midy); + node4 = new QTNode(builder, midx, maxx, miny, midy); + if(node1 == NULL || node2 == NULL || node3 == NULL || node4 == NULL) { + printf("Error: unable to allocate new QTNode, giving up.\n"); + return; + } + + unsigned long r,l; + l = faces.size(); + for(r = 0; r < l; r++) { + FaceRecord &cur = faces[r]; + if(builder->FaceInNode(node1, cur.face)) + node1->faces.push_back(cur); + if(builder->FaceInNode(node2, cur.face)) + node2->faces.push_back(cur); + if(builder->FaceInNode(node3, cur.face)) + node3->faces.push_back(cur); + if(builder->FaceInNode(node4, cur.face)) + node4->faces.push_back(cur); + } + + //clean up empty sets. + if(node1->faces.size() == 0) { + delete node1; + node1 = NULL; + } + if(node2->faces.size() == 0) { + delete node2; + node2 = NULL; + } + if(node3->faces.size() == 0) { + delete node3; + node3 = NULL; + } + if(node4->faces.size() == 0) { + delete node4; + node4 = NULL; + } + +} + +void QTBuilder::AddFace(VERTEX &v1, VERTEX &v2, VERTEX &v3) { + FACE f; + +#ifdef MAX_Z + if(v1.z > MAX_Z && v2.z > MAX_Z && v3.z > MAX_Z) + return; +#endif + + + //this still might not work + f.nx = (v2.y - v1.y)*(v3.z - v1.z) - (v2.z - v1.z)*(v3.y - v1.y); + f.ny = (v2.z - v1.z)*(v3.x - v1.x) - (v2.x - v1.x)*(v3.z - v1.z); + f.nz = (v2.x - v1.x)*(v3.y - v1.y) - (v2.y - v1.y)*(v3.x - v1.x); + NormalizeN(&f); + f.nd = - f.nx * v1.x - f.ny * v1.y - f.nz * v1.z; + + f.a = v1; + f.b = v2; + f.c = v3; + + _FaceList.push_back(f); +} + + +GPoint::GPoint() { + x = 0; + y = 0; + z = 0; +} + +GPoint::GPoint(VERTEX &v) { + x = v.x; + y = v.y; + z = v.z; +} + +GPoint::GPoint(float nx, float ny, float nz) { + x = nx; + y = ny; + z = nz; +} + +//dot of x,y,z +float GPoint::dot3(const GPoint &them) const { + return((x * them.x) + (y * them.y) + + (z * them.z)); +} + +//cross product +GPoint GPoint::cross(const GPoint &them) const { + return(GPoint(y * them.z - z * them.y, + z * them.x - x * them.z, + x * them.y - y * them.x)); +} + +GPoint operator-(const GPoint &v1, const GPoint &v2) { + return(GPoint(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z)); +} + + +GVector::GVector() : GPoint() { + W = 0; +} + +GVector::GVector(const GPoint &them) : GPoint(them) { + W = 1.0f; +} + +GVector::GVector(float x, float y, float z, float w) : GPoint(x, y, z) { + W = w; +} + +//dot product of x,y,z,w +float GVector::dot4(const GVector &them) const { + return((x * them.x) + (y * them.y) + + (z * them.z) + (W * them.W)); +} + +//dot product of x,y,z+w +float GVector::dot4(const GPoint &them) const { + return((x * them.x) + (y * them.y) + + (z * them.z) + W); +} + +float GVector::length() { + return(sqrt((x * x) + (y * y) + (z * z))); +} + +void GVector::normalize() { + float len = length(); //stupid square roots take forever + x /= len; + y /= len; + z /= len; +} + +//stolen from: http://gamecode.tripod.com/tut/tut04.htm +int QTBuilder::ClipPolygon(POLYGON *poly, GVector *plane) { + /* Plan: cycle through the vertices, considering pairs of them. + If both vertices are visible, add them to the new array. + If both vertices are invisible, add neither to the new array. + If one vertex is visible and the other is not, move the one + that's not, and then add both vertices to the new array. + */ + float dist1, dist2; // distances of points to plane + float distratio; // fraction of distance between two points + // new vertices might be created. Don't change + // polygon's vertices because we might still need + // them. Instead, use tempvtx array and update vertices + // array at the end. Create tempvtx array once only. + int i, ii, j=0; + + /* Check if plane is a valid plane */ + if (!(plane->x || plane->y || plane->z)) return -1; + // if not valid plane, don't change polygon and return an error; + + /* The vertices should, as for all functions, be arranged in cyclic + order. That is, if a line was drawn from each vertex to the next + it would form the correct outline of the polygon in 3D space. + This routine might create new vertices because of the clipping, + but the cyclic order will be preserved. + */ + + for (i=0; icount; i++) + { + ii = (i+1)%poly->count; + dist1 = plane->x * poly->c[i].x + plane->y * poly->c[i].y + + plane->z * poly->c[i].z + plane->W; + dist2 = plane->x * poly->c[ii].x + plane->y * poly->c[ii].y + + plane->z * poly->c[ii].z + plane->W; + if (dist1<0 && dist2<0) // line unclipped and invisible + continue; + if (dist1>0 && dist2>0) // line unclipped and visible + tempvtx[j++]=poly->c[i]; + else // line partially visible + if (dist1>0) // first vertex is visible + { + distratio = dist1/(dist1-dist2); + tempvtx[j] = poly->c[i]; + j++; // Copied 1st vertex + tempvtx[j].x = poly->c[i].x + + (poly->c[ii].x - poly->c[i].x) * distratio; + tempvtx[j].y = poly->c[i].y + + (poly->c[ii].y - poly->c[i].y) * distratio; + tempvtx[j].z = poly->c[i].z + + (poly->c[ii].z - poly->c[i].z) * distratio; + j++; // Copied second vertex + } + else // second vertex is visible + { + distratio = dist2/(dist2-dist1); + tempvtx[j].x = poly->c[ii].x + + (poly->c[i].x - poly->c[ii].x) * distratio; + tempvtx[j].y = poly->c[ii].y + + (poly->c[i].y - poly->c[ii].y) * distratio; + tempvtx[j].z = poly->c[ii].z + + (poly->c[i].z - poly->c[ii].z) * distratio; + j++; // Copy only first vertex. 2nd vertex will be copied + // in next iteration of loop + } + } + + for (i=0; ic[i] = tempvtx[i]; // Update the vertices in polygon + poly->count = j; // Update the vertex count + return j; +} + + +void QTBuilder::NormalizeN(FACE *p) { + float len = sqrt(p->nx*p->nx + p->ny*p->ny + p->nz*p->nz); + p->nx /= len; + p->ny /= len; + p->nz /= len; +} + +void QTBuilder::AddPlaceable(FileLoader *fileloader, char *ZoneFileName, bool ListPlaceable) { + Polygon *poly; + Vertex *verts[3]; + float XOffset, YOffset, ZOffset; + float RotX, RotY, RotZ; + float XScale, YScale, ZScale; + VERTEX v1, v2, v3, tmpv; + + + // Ghetto ini file parser to see which models to include + // The format of each line in azone.ini is: + // + // shortname.[eqg|s3d],model number, model numner, ... + // E.g. + // tox.s3d,1,17,34 + // anguish.eqg,25,69,70 + // + + const int IniBufferSize = 255; + enum ReadingState { ReadingZoneName, ReadingModelNumbers }; + ReadingState State = ReadingZoneName; + bool INIEntryFound = false; + int INIModelCount = 0; + char IniBuffer[IniBufferSize], ch; + vector ModelNumbers; + int StrIndex = 0; + int ModelNumber; + + FILE *IniFile = fopen("azone.ini", "rb"); + + if(!IniFile) { + printf("azone.ini not found in current directory. Not processing placeable models.\n"); + return; + } + printf("Processing azone.ini for placeable models.\n"); + + while(!feof(IniFile)) { + ch = fgetc(IniFile); + if((ch=='#')&&(StrIndex==0)) { // Discard comment lines beginning with a hash + while((ch!=EOF)&&(ch!='\n')) + ch = fgetc(IniFile); + + continue; + } + if((ch=='\n') && (State==ReadingZoneName)) { + StrIndex = 0; + continue; + } + if(ch=='\r') continue; + if((ch==EOF)||(ch=='\n')) { + IniBuffer[StrIndex] = '\0'; + if(State == ReadingModelNumbers) { + ModelNumber = atoi(IniBuffer); + if((ModelNumber >= 0) && (ModelNumber < fileloader->model_data.model_count)) + { + fileloader->model_data.models[ModelNumber]->IncludeInMap = true; + INIModelCount++; + } + else + printf("ERROR: Specified model %s invalid, must be in range 0 to %i\n", IniBuffer, fileloader->model_data.model_count - 1); + } + break; + } + if(ch==',') { + IniBuffer[StrIndex]='\0'; + StrIndex = 0; + if(State == ReadingZoneName) { + if(strcmp(ZoneFileName, IniBuffer)) { + StrIndex = 0; + // Not our zone, skip to next line + while((ch!=EOF)&&(ch!='\n')) + ch = fgetc(IniFile); + continue; + } + else { + State = ReadingModelNumbers; + INIEntryFound = true; + } + } + else { + ModelNumber = atoi(IniBuffer); + if((ModelNumber >= 0) && (ModelNumber < fileloader->model_data.model_count)) + { + fileloader->model_data.models[ModelNumber]->IncludeInMap = true; + INIModelCount++; + } + else + printf("ERROR: Specified model %s invalid, must be in range 0 to %i\n", IniBuffer, fileloader->model_data.model_count - 1); + } + continue; + } + IniBuffer[StrIndex++] = tolower(ch); + } + fclose(IniFile); + + if(INIEntryFound) { + printf("azone.ini entry found for this zone. "); + if(INIModelCount > 0) + printf("Including %d models.\n", INIModelCount); + else + printf("No valid model numbers specified.\n"); + } + else { + printf("No azone.ini entry found for zone %s\n", ZoneFileName); + } + + + + for(int i = 0; i < fileloader->model_data.plac_count; ++i) { + if(fileloader->model_data.placeable[i]->model==-1) continue; + // The model pointer should only really be NULL for the zone model, as we process that separately. + if(fileloader->model_data.models[fileloader->model_data.placeable[i]->model] == NULL) continue; + if(ListPlaceable) + printf("Placeable Object %4d @ (%9.2f, %9.2f, %9.2f uses model %4d %s\n",i, + fileloader->model_data.placeable[i]->y, + fileloader->model_data.placeable[i]->x, + fileloader->model_data.placeable[i]->z, + fileloader->model_data.placeable[i]->model, + fileloader->model_data.models[fileloader->model_data.placeable[i]->model]->name); + + + if(!fileloader->model_data.models[fileloader->model_data.placeable[i]->model]->IncludeInMap) + continue; + printf("Including Placeable Object %4d using model %4d (%s).\n", i, + fileloader->model_data.placeable[i]->model, + fileloader->model_data.models[fileloader->model_data.placeable[i]->model]->name); + + if(fileloader->model_data.placeable[i]->model>fileloader->model_data.model_count) continue; + + XOffset = fileloader->model_data.placeable[i]->x; + YOffset = fileloader->model_data.placeable[i]->y; + ZOffset = fileloader->model_data.placeable[i]->z; + + RotX = fileloader->model_data.placeable[i]->rx * 3.14159 / 180; // Convert from degrees to radians + RotY = fileloader->model_data.placeable[i]->ry * 3.14159 / 180; + RotZ = fileloader->model_data.placeable[i]->rz * 3.14159 / 180; + + XScale = fileloader->model_data.placeable[i]->scale[0]; + YScale = fileloader->model_data.placeable[i]->scale[1]; + ZScale = fileloader->model_data.placeable[i]->scale[2]; + + + Model *model = fileloader->model_data.models[fileloader->model_data.placeable[i]->model]; + + + for(int j = 0; j < model->poly_count; ++j) { + + poly = model->polys[j]; + + verts[0] = model->verts[poly->v1]; + verts[1] = model->verts[poly->v2]; + verts[2] = model->verts[poly->v3]; + + v1.x = verts[0]->x; v1.y = verts[0]->y; v1.z = verts[0]->z; + v2.x = verts[1]->x; v2.y = verts[1]->y; v2.z = verts[1]->z; + v3.x = verts[2]->x; v3.y = verts[2]->y; v3.z = verts[2]->z; + + + RotateVertex(v1, RotX, RotY, RotZ); + RotateVertex(v2, RotX, RotY, RotZ); + RotateVertex(v3, RotX, RotY, RotZ); + + ScaleVertex(v1, XScale, YScale, ZScale); + ScaleVertex(v2, XScale, YScale, ZScale); + ScaleVertex(v3, XScale, YScale, ZScale); + + TranslateVertex(v1, XOffset, YOffset, ZOffset); + TranslateVertex(v2, XOffset, YOffset, ZOffset); + TranslateVertex(v3, XOffset, YOffset, ZOffset); + + + // Swap X & Y + // + tmpv = v1; v1.x = tmpv.y; v1.y = tmpv.x; + tmpv = v2; v2.x = tmpv.y; v2.y = tmpv.x; + tmpv = v3; v3.x = tmpv.y; v3.y = tmpv.x; + + AddFace(v1, v2, v3); + } + } +} + + +void QTBuilder::AddPlaceableV4(FileLoader *fileloader, char *ZoneFileName, bool ListPlaceable) { + Polygon *poly; + Vertex *verts[3]; + float XOffset, YOffset, ZOffset; + float RotX, RotY, RotZ; + float XScale, YScale, ZScale; + VERTEX v1, v2, v3, tmpv; + + //return; + + printf("EQG V4 Placeable Zone Support\n"); + printf("ObjectGroupCount = %i\n", fileloader->model_data.ObjectGroups.size()); + + vector::iterator Iterator; + + int OGNum = 0; + + bool BailOut = false; + + // Ghetto ini file parser to see which models to include + // The format of each line in azone.ini is: + // + // shortname.[eqg|s3d],model number, model numner, ... + // E.g. + // tox.s3d,1,17,34 + // anguish.eqg,25,69,70 + // + + const int IniBufferSize = 255; + enum ReadingState { ReadingZoneName, ReadingModelNumbers }; + ReadingState State = ReadingZoneName; + bool INIEntryFound = false; + int INIModelCount = 0; + char IniBuffer[IniBufferSize], ch; + vector ModelNumbers; + int StrIndex = 0; + int ModelNumber; + + FILE *IniFile = fopen("azone.ini", "rb"); + + if(!IniFile) + printf("azone.ini not found in current directory. Including default models.\n"); + else + { + printf("Processing azone.ini for placeable models.\n"); + + while(!feof(IniFile)) { + ch = fgetc(IniFile); + if((ch=='#')&&(StrIndex==0)) { // Discard comment lines beginning with a hash + while((ch!=EOF)&&(ch!='\n')) + ch = fgetc(IniFile); + + continue; + } + if((ch=='\n') && (State==ReadingZoneName)) { + StrIndex = 0; + continue; + } + if(ch=='\r') continue; + if((ch==EOF)||(ch=='\n')) { + IniBuffer[StrIndex] = '\0'; + if(State == ReadingModelNumbers) { + bool Exclude = false; + bool Group = false; + if(IniBuffer[0]=='-') + { + Exclude = true; + strcpy(IniBuffer, IniBuffer+1); + } + if(IniBuffer[0]=='g') + { + Group = true; + strcpy(IniBuffer, IniBuffer+1); + } + ModelNumber = atoi(IniBuffer); + if(!Group) + { + if((ModelNumber >= 0) && (ModelNumber < fileloader->model_data.model_count)) + fileloader->model_data.models[ModelNumber]->IncludeInMap = Exclude ? false : true; + else + printf("ERROR: Specified model %s invalid, must be in range 0 to %i\n", IniBuffer, fileloader->model_data.model_count - 1); + } + else + { + if((ModelNumber >= 0) && ((unsigned int)ModelNumber < fileloader->model_data.ObjectGroups.size())) + fileloader->model_data.ObjectGroups[ModelNumber].IncludeInMap = Exclude ? false : true; + + } + } + break; + } + if(ch==',') { + IniBuffer[StrIndex]='\0'; + StrIndex = 0; + if(State == ReadingZoneName) { + if(strcmp(ZoneFileName, IniBuffer)) { + StrIndex = 0; + // Not our zone, skip to next line + while((ch!=EOF)&&(ch!='\n')) + ch = fgetc(IniFile); + continue; + } + else { + State = ReadingModelNumbers; + INIEntryFound = true; + } + } + else { + bool Exclude = false; + bool Group = false; + if(IniBuffer[0]=='-') + { + Exclude = true; + strcpy(IniBuffer, IniBuffer+1); + } + if(IniBuffer[0]=='g') + { + Group = true; + strcpy(IniBuffer, IniBuffer+1); + } + ModelNumber = atoi(IniBuffer); + if(!Group) + { + if((ModelNumber >= 0) && (ModelNumber < fileloader->model_data.model_count)) + fileloader->model_data.models[ModelNumber]->IncludeInMap = Exclude ? false : true; + else + printf("ERROR: Specified model %s invalid, must be in range 0 to %i\n", IniBuffer, fileloader->model_data.model_count - 1); + } + else + { + if((ModelNumber >= 0) && ((unsigned int)ModelNumber < fileloader->model_data.ObjectGroups.size())) + fileloader->model_data.ObjectGroups[ModelNumber].IncludeInMap = Exclude ? false : true; + + } + } + continue; + } + IniBuffer[StrIndex++] = tolower(ch); + } + fclose(IniFile); + + if(INIEntryFound) + printf("azone.ini entry found for this zone. "); + else + printf("No azone.ini entry found for zone %s\n", ZoneFileName); + } + + Iterator = fileloader->model_data.ObjectGroups.begin(); + + while(Iterator != fileloader->model_data.ObjectGroups.end()) + { + if(!(*Iterator).IncludeInMap) + { + ++OGNum; + ++Iterator; + continue; + } + + +#ifdef DEBUG + printf("ObjectGroup Number: %i\n", OGNum++); + + printf("ObjectGroup Coordinates: %8.3f, %8.3f, %8.3f\n", + (*Iterator).x, (*Iterator).y, (*Iterator).z); + + printf("Tile: %8.3f, %8.3f, %8.3f\n", + (*Iterator).TileX, (*Iterator).TileY, (*Iterator).TileZ); + + printf("ObjectGroup Rotations : %8.3f, %8.3f, %8.3f\n", + (*Iterator).RotX, (*Iterator).RotY, (*Iterator).RotZ); + + printf("ObjectGroup Scale : %8.3f, %8.3f, %8.3f\n", + (*Iterator).ScaleX, (*Iterator).ScaleY, (*Iterator).ScaleZ); +#endif + + list::iterator ModelIterator; + + ModelIterator = (*Iterator).SubObjects.begin(); + + while(ModelIterator != (*Iterator).SubObjects.end()) + { + int SubModel = (*ModelIterator); + +#ifdef DEBUG + printf(" SubModel: %i\n", (*ModelIterator)); +#endif + + XOffset = fileloader->model_data.placeable[SubModel]->x; + YOffset = fileloader->model_data.placeable[SubModel]->y; + ZOffset = fileloader->model_data.placeable[SubModel]->z; +#ifdef DEBUG + printf(" Translations: %8.3f, %8.3f, %8.3f\n", XOffset, YOffset, ZOffset); + printf(" Rotations : %8.3f, %8.3f, %8.3f\n", fileloader->model_data.placeable[SubModel]->rx, + fileloader->model_data.placeable[SubModel]->ry, fileloader->model_data.placeable[SubModel]->rz); + printf(" Scale : %8.3f, %8.3f, %8.3f\n", fileloader->model_data.placeable[SubModel]->scale[0], + fileloader->model_data.placeable[SubModel]->scale[1], fileloader->model_data.placeable[SubModel]->scale[2]); +#endif + + RotX = fileloader->model_data.placeable[SubModel]->rx * 3.14159 / 180; // Convert from degrees to radians + RotY = fileloader->model_data.placeable[SubModel]->ry * 3.14159 / 180; + RotZ = fileloader->model_data.placeable[SubModel]->rz * 3.14159 / 180; + + XScale = fileloader->model_data.placeable[SubModel]->scale[0]; + YScale = fileloader->model_data.placeable[SubModel]->scale[1]; + ZScale = fileloader->model_data.placeable[SubModel]->scale[2]; + + Model *model = fileloader->model_data.models[fileloader->model_data.placeable[SubModel]->model]; + +#ifdef DEBUG + printf("Model %i, name is %s\n", fileloader->model_data.placeable[SubModel]->model, model->name); +#endif + if(!model->IncludeInMap) + { + ++ModelIterator; +#ifdef DEBUG + printf("Not including in .map\n"); +#endif + continue; + } + for(int j = 0; j < model->poly_count; ++j) { + + poly = model->polys[j]; + + verts[0] = model->verts[poly->v1]; + verts[1] = model->verts[poly->v2]; + verts[2] = model->verts[poly->v3]; + + v1.x = verts[0]->x; v1.y = verts[0]->y; v1.z = verts[0]->z; + v2.x = verts[1]->x; v2.y = verts[1]->y; v2.z = verts[1]->z; + v3.x = verts[2]->x; v3.y = verts[2]->y; v3.z = verts[2]->z; + + // Scale by the values specified for the individual object. + // + ScaleVertex(v1, XScale, YScale, ZScale); + ScaleVertex(v2, XScale, YScale, ZScale); + ScaleVertex(v3, XScale, YScale, ZScale); + // Translate by the values specified for the individual object + // + TranslateVertex(v1, XOffset, YOffset, ZOffset); + TranslateVertex(v2, XOffset, YOffset, ZOffset); + TranslateVertex(v3, XOffset, YOffset, ZOffset); + // Rotate by the values specified for the group + // TODO: The X/Y rotations can be combined + // + RotateVertex(v1, (*Iterator).RotX * 3.14159 / 180.0f, 0, 0); + RotateVertex(v2, (*Iterator).RotX * 3.14159 / 180.0f, 0, 0); + RotateVertex(v3, (*Iterator).RotX * 3.14159 / 180.0f, 0, 0); + + RotateVertex(v1, 0, (*Iterator).RotY * 3.14159 / 180.0f, 0); + RotateVertex(v2, 0, (*Iterator).RotY * 3.14159 / 180.0f, 0); + RotateVertex(v3, 0, (*Iterator).RotY * 3.14159 / 180.0f, 0); + + // To make things align properly, we need to translate the object back to the origin + // before applying the model Z rotation. This is what the Correction VERTEX is for. + // + VERTEX Correction(XOffset, YOffset, ZOffset); + + RotateVertex(Correction, (*Iterator).RotX * 3.14159 / 180.0f, 0, 0); + + TranslateVertex(v1, -Correction.x, -Correction.y, -Correction.z); + TranslateVertex(v2, -Correction.x, -Correction.y, -Correction.z); + TranslateVertex(v3, -Correction.x, -Correction.y, -Correction.z); + // Rotate by model Z rotation + // + // + RotateVertex(v1, RotX, 0, 0); + RotateVertex(v2, RotX, 0, 0); + RotateVertex(v3, RotX, 0, 0); + + // Don't know why the Y rotation needs to be negative + // + RotateVertex(v1, 0, -RotY, 0); + RotateVertex(v2, 0, -RotY, 0); + RotateVertex(v3, 0, -RotY, 0); + + RotateVertex(v1, 0, 0, RotZ); + RotateVertex(v2, 0, 0, RotZ); + RotateVertex(v3, 0, 0, RotZ); + // Now we have applied the individual model rotations, translate back to where we were. + // + TranslateVertex(v1, Correction.x, Correction.y, Correction.z); + TranslateVertex(v2, Correction.x, Correction.y, Correction.z); + TranslateVertex(v3, Correction.x, Correction.y, Correction.z); + // Rotate by the Z rotation value specified for the object group + // + RotateVertex(v1, 0, 0, (*Iterator).RotZ * 3.14159 / 180.0f); + RotateVertex(v2, 0, 0, (*Iterator).RotZ * 3.14159 / 180.0f); + RotateVertex(v3, 0, 0, (*Iterator).RotZ * 3.14159 / 180.0f); + // Scale by the object group values + // + ScaleVertex(v1, (*Iterator).ScaleX, (*Iterator).ScaleY, (*Iterator).ScaleZ); + ScaleVertex(v2, (*Iterator).ScaleX, (*Iterator).ScaleY, (*Iterator).ScaleZ); + ScaleVertex(v3, (*Iterator).ScaleX, (*Iterator).ScaleY, (*Iterator).ScaleZ); + // Translate to the relevant tile starting co-ordinates + // + TranslateVertex(v1, (*Iterator).TileX, (*Iterator).TileY, (*Iterator).TileZ); // Y+14, Z + 68 + TranslateVertex(v2, (*Iterator).TileX, (*Iterator).TileY, (*Iterator).TileZ); + TranslateVertex(v3, (*Iterator).TileX, (*Iterator).TileY, (*Iterator).TileZ); + // Translate to the position within the tile for this object group + // + TranslateVertex(v1, (*Iterator).x, (*Iterator).y, (*Iterator).z); + TranslateVertex(v2, (*Iterator).x, (*Iterator).y, (*Iterator).z); + TranslateVertex(v3, (*Iterator).x, (*Iterator).y, (*Iterator).z); + // Swap X & Y + // + tmpv = v1; v1.x = tmpv.y; v1.y = tmpv.x; + tmpv = v2; v2.x = tmpv.y; v2.y = tmpv.x; + tmpv = v3; v3.x = tmpv.y; v3.y = tmpv.x; + + AddFace(v1, v2, v3); + + } + ++ModelIterator; + } + + ++Iterator; + } + + +} + +void QTBuilder::RotateVertex(VERTEX &v, float XRotation, float YRotation, float ZRotation) { + + VERTEX nv; + + nv = v; + + // Rotate about the X axis + // + nv.y = (cos(XRotation) * v.y) - (sin(XRotation) * v.z); + nv.z = (sin(XRotation) * v.y) + (cos(XRotation) * v.z); + + v = nv; + + + // Rotate about the Y axis + // + nv.x = (cos(YRotation) * v.x) + (sin(YRotation) * v.z) ; + nv.z = -(sin(YRotation) * v.x) + (cos(YRotation) * v.z); + + v = nv; + + // Rotate about the Z axis + // + nv.x = (cos(ZRotation) * v.x) - (sin(ZRotation) * v.y) ; + nv.y = (sin(ZRotation) * v.x) + (cos(ZRotation) * v.y) ; + + v = nv; +} + +void QTBuilder::ScaleVertex(VERTEX &v, float XScale, float YScale, float ZScale) { + + v.x = v.x * XScale; + v.y = v.y * YScale; + v.z = v.z * ZScale; +} + + +void QTBuilder::TranslateVertex(VERTEX &v, float XOffset, float YOffset, float ZOffset) { + + v.x = v.x + XOffset; + v.y = v.y + YOffset; + v.z = v.z + ZOffset; +} + + + + + + diff --git a/utils/azone2/azone.h b/utils/azone2/azone.h new file mode 100644 index 000000000..641676194 --- /dev/null +++ b/utils/azone2/azone.h @@ -0,0 +1,200 @@ +#ifndef AZONE_H +#define AZONE_H + +//pull in datatypes from zone's map.h +#include "../../zone/map.h" +#include "file_loader.hpp" + +/* + + Father Nitwit's zone to map file converter. + +*/ + +#include +#include +#include +using namespace std; + + +#define COUNT_MACTHES 1 + +//this is the version number to put in the map header +#undef MAP_VERSION //override this from map.h with our version +#define MAP_VERSION 0x01000000 + +//quadtree stopping criteria, comment any to disable them +#define MAX_QUADRENT_FACES 50 //if box has fewer than this, stop +#define MIN_QUADRENT_SIZE 100.0f //if box has a dimention smaller than this, stop +#define MIN_QUADRENT_GAIN 0.3f //minimum split ratio before stopping +#define MAX_QUADRENT_MISSES 2 //maximum number of quads which can miss their gains + //1 or 2 make sense, others are less useful + +//attepmt to trim the data a little +#define MAX_Z 3000.0 //seems to be a lot of points above this + //if all points on poly are, kill it. +/* + This is used for the recursive node structure + unsigned shorts are adequate because, worst case + even in a zone that is 6000x6000 with a small node + size of 30x30, there are only 40000 nodes. + + quadrent definitions: + quad 1 (nodes[0]): + x>=0, y>=0 + quad 2 (nodes[1]): + x<0, y>=0 + quad 3 (nodes[2]): + x<0, y<0 + quad 4 (nodes[3]): + x>=0, y<0 + + */ +#define MAX_POLY_VTX 24 //arbitrary, im too lazy to figure it out + //cut a triangle at most 6 times.... + +enum EQFileType { S3D, EQG, UNKNOWN }; + +struct POLYGON { + VERTEX /*w[MAX_POLY_VTX], */c[MAX_POLY_VTX]; + int count; // w is world points, c is for camera points +}; + +class GPoint { +public: + GPoint(); + GPoint(VERTEX &v); + GPoint(float x, float y, float z); + + inline void operator()(float nx, float ny, float nz) { x = nx; y = ny; z = nz; } + + GPoint cross(const GPoint &them) const; + float dot3(const GPoint &them) const; + + float x; + float y; + float z; + +}; +GPoint operator-(const GPoint &v1, const GPoint &v2); + +class GVector : public GPoint { +public: + GVector(); + GVector(const GPoint &them); + GVector(float x, float y, float z, float w = 1.0f); + + inline void operator()(float nx, float ny, float nz, float nw) { x = nx; y = ny; z = nz; W = nw; } + float dot4(const GVector &them) const; + float dot4(const GPoint &them) const; + void normalize(); + float length(); + + float W; +}; + +struct FaceRecord { + FACE *face; + unsigned long index; +}; + +class QTNode; + +class QTBuilder { +public: + QTBuilder(); + ~QTBuilder(); + + bool build(const char *shortname); + bool build_eqg(const char *shortname); + void AddPlaceable(FileLoader *fileloader, char *ZoneFileName, bool ListPlaceable=false); + void AddPlaceableV4(FileLoader *fileloader, char *ZoneFileName, bool ListPlaceable=false); + void RotateVertex(VERTEX &v, float XRotation, float YRotation, float ZRotation); + void ScaleVertex(VERTEX &v, float XScale, float YScale, float ZScale); + void TranslateVertex(VERTEX &v, float XOffset, float YOffset, float ZOffset); + bool writeMap(const char *file); + + bool FaceInNode(const QTNode *q, const FACE *f); +protected: + + void AddFace(VERTEX &v1, VERTEX &v2, VERTEX &v3); + + int ClipPolygon(POLYGON *poly, GVector *plane); + + //dynamic during load +// vector _VertexList; + vector _FaceList; + + //static once loaded +// unsigned long vertexCount; + unsigned long faceCount; +// VERTEX * vertexBlock; + FACE * faceBlock; + + VERTEX tempvtx[MAX_POLY_VTX]; + + QTNode *_root; + + static void NormalizeN(FACE *p); + +#ifdef COUNT_MACTHES + unsigned long gEasyMatches; + unsigned long gEasyExcludes; + unsigned long gHardMatches; + unsigned long gHardExcludes; +#endif + +}; + +//quadtree node container +class QTNode { +public: + QTNode(QTBuilder *builder, float Tminx, float Tmaxx, float Tminy, float Tmaxy); + ~QTNode(); + + void clearNodes(); + + void doSplit(); + void divideYourself(int depth); + + void buildVertexes(); + +// bool writeFile(FILE *out); + + unsigned long countNodes() const; + unsigned long countFacelists() const; + + void fillBlocks(nodeHeader *heads, unsigned long *flist, unsigned long &hindex, unsigned long &findex); + + float minx; + float miny; + float maxx; + float maxy; + unsigned long nfaces; + vector faces; + + /* + quadrent definitions: + quad 1 (node1): + x>=0, y>=0 + quad 2 (node2): + x<0, y>=0 + quad 3 (node3): + x<0, y<0 + quad 4 (node4): + x>=0, y<0 + */ + QTNode *node1; + QTNode *node2; + QTNode *node3; + QTNode *node4; + GPoint v[8]; + bool final; + +protected: + QTBuilder *builder; +}; + + +#endif + diff --git a/utils/azone2/azone.ini b/utils/azone2/azone.ini new file mode 100644 index 000000000..ac121596e --- /dev/null +++ b/utils/azone2/azone.ini @@ -0,0 +1,10 @@ +# Include huts, houses dock and bridge from tox +tox.s3d,7,17,20,26 +# Include two bridges from Tutorialb +tutorialb.eqg,17,47 +# Include prison, ramp and a couple of towers in Anguish +anguish.eqg,5,7,8,119 +# Include ruined buildings in LOIO +lakeofillomen.s3d,49,50,51,52,53,54,55,56,57 +# Include bridges in thundercrest +thundercrest.eqg,14,51 \ No newline at end of file diff --git a/utils/azone2/dat.cpp b/utils/azone2/dat.cpp new file mode 100644 index 000000000..4d1dc8459 --- /dev/null +++ b/utils/azone2/dat.cpp @@ -0,0 +1,1008 @@ + +#include +#include +#include +#include + +#include "file.hpp" +#include "dat.hpp" +#include "../../zone/map.h" +//#define DEBUGDAT + +#define VARSTRUCT_DECODE_TYPE(Type, Buffer) *(Type *)Buffer; Buffer += sizeof(Type); +#define VARSTRUCT_DECODE_STRING(String, Buffer) strcpy(String, Buffer); Buffer += strlen(String)+1; +#define VARSTRUCT_ENCODE_STRING(Buffer, String) sprintf(Buffer, String); Buffer += strlen(String) + 1; +#define VARSTRUCT_ENCODE_INTSTRING(Buffer, Number) sprintf(Buffer, "%i", Number); Buffer += strlen(Buffer) + 1; +#define VARSTRUCT_ENCODE_TYPE(Type, Buffer, Value) *(Type *)Buffer = Value; Buffer += sizeof(Type); +#define VARSTRUCT_SKIP_TYPE(Type, Buffer) Buffer += sizeof(Type); + + +float HeightWithinQuad(VERTEX p1, VERTEX p2, VERTEX p3, VERTEX p4, float x, float y) +{ + // This function returns the height of the given x/y point with the quad defined by p1,p2,p3 and p4. + // + // It is assumed that the point does in fact lie somewhere within the quad. If for some reason it doesn't, + // the program will abort, as thre is a logic flaw. + // + /* + printf("Quad: %8.3f, %8.3f, %8.3f\n", p1.x, p1.y, p1.z); + printf(" %8.3f, %8.3f, %8.3f\n", p2.x, p2.y, p2.z); + printf(" %8.3f, %8.3f, %8.3f\n", p3.x, p3.y, p3.z); + printf(" %8.3f, %8.3f, %8.3f\n", p4.x, p4.y, p4.z); + printf("\n\nPoint: %8.3f, %8.3f\n", x, y); + */ + + FACE f; + + int inTriangle = 0; + + float fAB = (y-p1.y) * (p2.x-p1.x) - (x-p1.x) * (p2.y-p1.y); + float fBC = (y-p2.y) * (p3.x-p2.x) - (x-p2.x) * (p3.y-p2.y); + float fCA = (y-p3.y) * (p1.x-p3.x) - (x-p3.x) * (p1.y-p3.y); + + if((fAB * fBC >= 0) && (fBC * fCA >= 0)) + { + inTriangle = 1; + f.a = p1; + f.b = p2; + f.c = p3; + } + + fAB = (y-p1.y) * (p3.x-p1.x) - (x-p1.x) * (p3.y-p1.y); + fBC = (y-p3.y) * (p4.x-p3.x) - (x-p3.x) * (p4.y-p3.y); + fCA = (y-p4.y) * (p1.x-p4.x) - (x-p4.x) * (p1.y-p4.y); + + + if((fAB * fBC >= 0) && (fBC * fCA >= 0)) + { + inTriangle = 2; + f.a = p1; + f.b = p3; + f.c = p4; + } + + if(inTriangle == 0) + { + printf("Something went wrong. Aborting in HeightWithinQuad\n"); + exit(0); + } + + f.nx = (f.b.y - f.a.y)*(f.c.z - f.a.z) - (f.b.z - f.a.z)*(f.c.y - f.a.y); + f.ny = (f.b.z - f.a.z)*(f.c.x - f.a.x) - (f.b.x - f.a.x)*(f.c.z - f.a.z); + f.nz = (f.b.x - f.a.x)*(f.c.y - f.a.y) - (f.b.y - f.a.y)*(f.c.x - f.a.x); + + float len = sqrt(f.nx*f.nx + f.ny*f.ny + f.nz*f.nz); + + f.nx /= len; + f.ny /= len; + f.nz /= len; + + return (((f.nx) * (x - f.a.x) + (f.ny) * (y - f.a.y)) / -f.nz) + f.a.z; +} + + +string GetToken(uchar *&Buffer, int &Position) +{ + // This is used to return each Token, keyword, etc. from an EQG v4 .zon or .tog file + // + string Token; + + while((Buffer[Position] == 9) || (Buffer[Position] == 32)) + ++Position; + + while((Buffer[Position] != 10) && (Buffer[Position] != 9) && (Buffer[Position] != 32)) + { + if(Buffer[Position] > 32) + Token = Token + (char)Buffer[Position]; + + ++Position; + } + ++Position; + //printf("Returning %s\n", Token.c_str()); + return Token; +} + +int AddModelName(Content_3D *C3D, string ModelName) +{ + vector::iterator Iterator; + + for(unsigned int i = 0; i < C3D->ModelNames.size(); ++i) + { +#ifndef WIN32 + if(!strcasecmp(C3D->ModelNames[i].c_str(), ModelName.c_str())) +#else + if(!_stricmp(C3D->ModelNames[i].c_str(), ModelName.c_str())) +#endif + return i; + } + + + + C3D->ModelNames.push_back(ModelName); + + return C3D->ModelNames.size() - 1; +} + +DATLoader::DATLoader() +{ + this->buffer = NULL; + this->buf_len = -1; + this->archive = NULL; + this->status = 0; +} + +DATLoader::~DATLoader() +{ + this->Close(); +} + +int DATLoader::Open(char *base_path, char *zone_name, Archive *archive) { + +#ifdef DEBUGDAT + printf("DATLoader::Open %s, [%s]\n", base_path, zone_name); + fflush(stdout); +#endif + unsigned int i, j, mat_count = 0; + + Zone_Model *zm; + + char *buffer, *buf_start; + int buf_len, bone_count; + + char *filename; + + if(zone_name[strlen(zone_name) - 4] == '.') + filename = zone_name; + else + { + filename = new char[strlen(zone_name) + 5]; + sprintf(filename, "%s.ter", zone_name); + } + + if(!GetFile((uchar **)&buffer, &buf_len, base_path, filename, archive)) + { + if(filename != zone_name) + delete[] filename; + return 0; + } +#ifdef DEBUGDAT + printf("Filename = %s, zonename = %s\n", filename, zone_name); +#endif + if(filename != zone_name) + delete[] filename; + + + buf_start = buffer; + + //TODO: Find out what these three unknowns are + int Unknown1 = VARSTRUCT_DECODE_TYPE(uint32, buffer); + int Unknown2 = VARSTRUCT_DECODE_TYPE(uint32, buffer); + int Unknown3 = VARSTRUCT_DECODE_TYPE(uint32, buffer); + +#ifdef DEBUGDAT + printf("Unknowns: %i, %i, %i\n", Unknown1, Unknown2, Unknown3); +#endif + + char s[255]; + + VARSTRUCT_DECODE_STRING(s, buffer) + +#ifdef DEBUGDAT + printf("('%s',)\n", s); +#endif + + int TileCount = VARSTRUCT_DECODE_TYPE(uint32, buffer); + +#ifdef DEBUGDAT + printf("TileCount is %i\n", TileCount); +#endif + + // Most of these default values will be overwritten when we read the .zon file + // + int QuadsPerTile = 32; + + float MinLAT = -24; + float MaxLAT = -1; + float MinLNG = 0; + float MaxLNG = 62; + + float UnitsPerVert = 5.0; + + float ZoneMinX = 0; + float ZoneMaxX = 0; + + float ZoneMinY = 0; + float ZoneMaxY = 0; + + float MinExtentX = -999999, MinExtentY = -999999, MinExtentZ = -999999; + float MaxExtentX = 999999, MaxExtentY = 999999, MaxExtentZ = 999999; + + // Parse .zon file. + // + uchar *ZonBuffer; + + int ZonBufferLength, ZonPosition; + + if(!GetFile((uchar **)&ZonBuffer, &ZonBufferLength, base_path, "*.zon", archive)) + { + printf("Unable to open .zon\n"); + exit(0); + } + else + { + ZonPosition = 0; + + while(ZonPosition < ZonBufferLength) + { + string Token = GetToken(ZonBuffer, ZonPosition); + + if(Token == "*MINLNG") + { + Token = GetToken(ZonBuffer, ZonPosition); + MinLNG = atof(Token.c_str()); +#ifdef DEBUGDAT + printf("Set MinLNG to %8.3f\n", MinLNG); +#endif + } + if(Token == "*MAXLNG") + { + Token = GetToken(ZonBuffer, ZonPosition); + MaxLNG = atof(Token.c_str()); +#ifdef DEBUGDAT + printf("Set MaxLNG to %8.3f\n", MaxLNG); +#endif + } + if(Token == "*MINLAT") + { + Token = GetToken(ZonBuffer, ZonPosition); + MinLAT = atof(Token.c_str()); +#ifdef DEBUGDAT + printf("Set MinLAT to %8.3f\n", MinLAT); +#endif + } + if(Token == "*MAXLAT") + { + Token = GetToken(ZonBuffer, ZonPosition); + MaxLAT = atof(Token.c_str()); +#ifdef DEBUGDAT + printf("Set MaxLAT to %8.3f\n", MaxLAT); +#endif + } + if(Token == "*UNITSPERVERT") + { + Token = GetToken(ZonBuffer, ZonPosition); + UnitsPerVert = atof(Token.c_str()); +#ifdef DEBUGDAT + printf("Set UnitsPerVert to %8.3f\n", UnitsPerVert); +#endif + } + if(Token == "*QUADSPERTILE") + { + Token = GetToken(ZonBuffer, ZonPosition); + QuadsPerTile = atoi(Token.c_str()); +#ifdef DEBUGDAT + printf("Set QuadsPerTile to %i\n", QuadsPerTile); +#endif + } + if(Token == "*MIN_EXTENTS") + { + Token = GetToken(ZonBuffer, ZonPosition); + MinExtentX = atof(Token.c_str()); + Token = GetToken(ZonBuffer, ZonPosition); + MinExtentY = atof(Token.c_str()); + Token = GetToken(ZonBuffer, ZonPosition); + MinExtentZ = atof(Token.c_str()); +#ifdef DEBUGDAT + printf("Set MinExtents to %8.3f, %8.3f, %8.3f\n", MinExtentX, MinExtentY, MinExtentZ); +#endif + } + if(Token == "*MAX_EXTENTS") + { + Token = GetToken(ZonBuffer, ZonPosition); + MaxExtentX = atof(Token.c_str()); + Token = GetToken(ZonBuffer, ZonPosition); + MaxExtentY = atof(Token.c_str()); + Token = GetToken(ZonBuffer, ZonPosition); + MaxExtentZ = atof(Token.c_str()); +#ifdef DEBUGDAT + printf("Set MaxExtents to %8.3f, %8.3f, %8.3f\n", MaxExtentX, MaxExtentY, MaxExtentZ); +#endif + } + } + } + + float UnitsPerVertX = UnitsPerVert; + float UnitsPerVertY = UnitsPerVert; + + float LATRange = (MaxLAT - MinLAT); + float LNGRange = (MaxLNG - MinLNG); + + ZoneMinX = MinLAT * QuadsPerTile * UnitsPerVert; + ZoneMaxX = (MaxLAT + 1) * QuadsPerTile * UnitsPerVert; + + ZoneMinY = MinLNG * QuadsPerTile * UnitsPerVert; + ZoneMaxY = (MaxLNG + 1) * QuadsPerTile * UnitsPerVert; + + int QuadCount = (QuadsPerTile * QuadsPerTile); + int VertCount = ((QuadsPerTile + 1) * (QuadsPerTile + 1)); +#ifdef DEBUGDAT + printf("X Range: %8.3f to %8.3f\n", ZoneMinX, ZoneMaxX); + printf("Y Range: %8.3f to %8.3f\n", ZoneMinY, ZoneMaxY); + + + printf("UnitsPerVertX = %8.3f, UnitsPerVertY = %8.3f\n", UnitsPerVertX, UnitsPerVertY); + + printf("LATRange is from %8.3f to %8.3f (%8.3f), x = %8.3f to %8.3f (%8.3f)\n", + MinLAT, MaxLAT, LATRange, ZoneMinX, ZoneMaxX, ZoneMaxX - ZoneMinX); + +#endif + int VertexNumber = -1; + int PolyNumber = -1; + + this->model_data.zone_model = new Zone_Model; + zm = this->model_data.zone_model; + + zm->vert_count = QuadCount * TileCount * 4; + zm->poly_count = QuadCount * 2 * TileCount; + + zm->verts = new Vertex *[zm->vert_count]; + zm->polys = new Polygon *[zm->poly_count]; + + this->model_data.plac_count = 0; + this->model_data.model_count = 0; + this->model_data.ObjectGroupCount = 0; + + + float *Floats = new float[VertCount]; + int *Color1 = new int[VertCount]; + int *Color2 = new int[VertCount]; + uint8 *Bytes = new uint8[QuadCount]; + + for(int TileNumber = 0; TileNumber < TileCount; ++TileNumber) + { + + int TileLNG = VARSTRUCT_DECODE_TYPE(uint32, buffer); + int TileLAT = VARSTRUCT_DECODE_TYPE(uint32, buffer); + int TileUNK = VARSTRUCT_DECODE_TYPE(uint32, buffer); + + float TileYStart = ZoneMinY + (TileLNG - 100000 - MinLNG) * UnitsPerVertY * QuadsPerTile; + float TileXStart = ZoneMinX + (TileLAT - 100000 - MinLAT) * UnitsPerVertX * QuadsPerTile; + +#ifdef DEBUGDAT + printf("TileXStart = %8.3f + %f * %f * %i\n", ZoneMinX, (TileLAT - 100000 - MinLAT) , UnitsPerVertX, QuadsPerTile); + + + printf("****** Tile # %5d header %6d, %6d, %6d (LNG: %6d, LAT: %6d)\n", TileNumber, TileLNG, TileLAT, TileUNK, + TileLNG - 100000, TileLAT - 100000); + + printf("TileXStart = %8.3f, TileYStart = %8.3f\n", TileXStart, TileYStart); + printf("TileXEnd = %8.3f, TileYEnd = %8.3f\n", TileXStart + QuadsPerTile * UnitsPerVertX, + TileYStart + QuadsPerTile * UnitsPerVertY); +#endif + + // Height Values + // + bool AllFloatsTheSame = true; + + for(int i = 0; i < VertCount; ++i) + { + Floats[i] = VARSTRUCT_DECODE_TYPE(float, buffer); + if((i > 0) && (Floats[i] != Floats[0])) + AllFloatsTheSame = false; + } + + + // It is an assumption that these next two groups are Color info, but they look like it. + // + for(int i = 0; i < VertCount; ++i) + { + Color1[i] = VARSTRUCT_DECODE_TYPE(uint32, buffer); + } + + for(int i = 0; i < VertCount; ++i) + { + Color2[i] = VARSTRUCT_DECODE_TYPE(uint32, buffer); + } + + // The low order bit of this group of bytes indicates whether the corresponding quad should + // be rendered or not. + for(int i = 0; i < QuadCount; ++i) + { + Bytes[i] = VARSTRUCT_DECODE_TYPE(uint8, buffer); + } + + + float UnkFloat = VARSTRUCT_DECODE_TYPE(float, buffer); + + int UnkUnk = VARSTRUCT_DECODE_TYPE(uint32, buffer); + + buffer -= 4; + + float UnkUnk2 = VARSTRUCT_DECODE_TYPE(float, buffer); +#ifdef DEBUGDAT + printf("Unknowns %.1f %i (%8.3f)\n", UnkFloat, UnkUnk, UnkUnk2); +#endif + + if(UnkUnk > 0) + { + int8 UnkByte = VARSTRUCT_DECODE_TYPE(uint8, buffer); +#ifdef DEBUGDAT + printf("Byte is (%i,)\n", UnkByte); +#endif + if(UnkByte > 0) + { + float f1 = VARSTRUCT_DECODE_TYPE(float, buffer); + float f2 = VARSTRUCT_DECODE_TYPE(float, buffer); + float f3 = VARSTRUCT_DECODE_TYPE(float, buffer); + float f4 = VARSTRUCT_DECODE_TYPE(float, buffer); +#ifdef DEBUGDAT + printf("Floats: %.1f %.1f %1.f %.1f\n", f1, f2, f3, f4); +#endif + } + + float f1 = VARSTRUCT_DECODE_TYPE(float, buffer); +#ifdef DEBUGDAT + printf("Float: (%.1f,)\n", f1); +#endif + } + + int LayerCount = VARSTRUCT_DECODE_TYPE(uint32, buffer); + + VARSTRUCT_DECODE_STRING(s, buffer); + + int OverlayCount = 0; + + for(int Layer = 1; Layer < LayerCount; ++Layer) + { + VARSTRUCT_DECODE_STRING(s, buffer); + + int Size = VARSTRUCT_DECODE_TYPE(uint32, buffer); + + for(int b = 0; b < (Size * Size); ++b) + { + uint8 Byte = VARSTRUCT_DECODE_TYPE(uint8, buffer); + } + + ++OverlayCount; + + } + + int Count1 = VARSTRUCT_DECODE_TYPE(uint32, buffer); +#ifdef DEBUGDAT + printf("Count 1 is (%iL,)\n", Count1); +#endif + + // The Count1 section is a list of single placeable models + // + for(int j = 0; j < Count1; ++j) + { + char ModelName[255]; + + VARSTRUCT_DECODE_STRING(ModelName, buffer); +#ifdef DEBUGDAT + printf("%s\n", ModelName); +#endif + int ModelNumber = AddModelName(&model_data, ModelName); + + VARSTRUCT_DECODE_STRING(s, buffer); +#ifdef DEBUGDAT + printf("%s\n", s); +#endif + + // Although the LNG/LAT is included for each model within this tile, the values always appear to be + // the same as for the parent tile. + // + int Longitude = VARSTRUCT_DECODE_TYPE(uint32, buffer); + + int Latitude = VARSTRUCT_DECODE_TYPE(uint32, buffer); + + float x = VARSTRUCT_DECODE_TYPE(float, buffer); + float y = VARSTRUCT_DECODE_TYPE(float, buffer); + float z = VARSTRUCT_DECODE_TYPE(float, buffer); + + float RotX = VARSTRUCT_DECODE_TYPE(float, buffer); + float RotY = VARSTRUCT_DECODE_TYPE(float, buffer); + float RotZ = VARSTRUCT_DECODE_TYPE(float, buffer); + + float ScaleX = VARSTRUCT_DECODE_TYPE(float, buffer); + float ScaleY = VARSTRUCT_DECODE_TYPE(float, buffer); + float ScaleZ = VARSTRUCT_DECODE_TYPE(float, buffer); + + VARSTRUCT_DECODE_TYPE(uint8, buffer); + + Placeable p(ModelNumber, 0, 0, 0, RotX, RotY, RotZ, ScaleX, ScaleY, ScaleZ); + + model_data.PlaceableList.push_back(p); +#ifdef DEBUGDAT + printf("Model: %s LOC: %8.3f, %8.3f, %8.3f, ROT: %8.3f, %8.3f, %8.3f\n", ModelName, x, y, z, RotX, RotY, RotZ); +#endif + ObjectGroupEntry NewObjectGroup; + + NewObjectGroup.FromTOG = false; + + NewObjectGroup.y = y; + NewObjectGroup.x = x; + NewObjectGroup.z = z; + + NewObjectGroup.TileX = TileYStart; + NewObjectGroup.TileY = TileXStart; + + // The objects in the Count1 section have a Z offset relative to the ground level, so we calculate the + // ground level within this quad for the given x/y offsets. However ... + // + // Some objects within a tile have an X/Y offset specified such that they actually sit within the bounds + // of another tile, which means based on the data for the tile we are processing, we should be unable to + // calculate the Z value for that object. + // + // By hand editing an .EQG, it was discovered that what the client appears to do is calculate the Z value + // for these objects by adjusting the x/y offsets by +/- (QuadsPerTile * UnitsPerVert) until the offsets + // are within this tiles boundaries and using the corresponding height values. + // + // For example, if a model has an offset of -157.5, -157.5 and QPT * UPV = 160, then the height value used + // would be the height at +2.5, +2.5 in this tile. + // + // This doesn't make a great deal of sense, but it is how the EQ graphics engine appears to do things. + // + // These 'Adjusted' x/y offsets are used only for determing the ground Z for the object. The actual placement + // of the object relative to the terrain uses the unadjusted offsets. + // + // For the record, as of SoF, this applies to the following zones and objects: + // + // arcstone: Model translations adjusted from -14.000, 219.000 to 146.000, 59.000 + // arcstone: Model translations adjusted from 159.692, 256.589 to 159.692, 96.589 + // arcstone: Model translations adjusted from 122.314, 170.098 to 122.314, 10.098 + // elddar: Model translations adjusted from 144.980, 396.406 to 144.980, 76.406 + // elddar: Model translations adjusted from 167.534, -193.584 to 7.534, 126.416 + // kithicor: Model translations adjusted from 18.217, 166.512 to 18.217, 6.512 + // hro.out: Model translations adjusted from 38.249, -4.658 to 38.249, 155.342 + // steppes: Model translations adjusted from 101.380, 380.000 to 101.380, 124.000 + // steppes: Model translations adjusted from 32.770, -210.000 to 32.770, 46.000 + // steppes: Model translations adjusted from 560.000, 150.000 to 48.000, 22.000 + // steppes: Model translations adjusted from 420.000, 445.000 to 36.000, 61.000 + // + float TerrainHeight = 0; + + float AdjustedX = x; + + float AdjustedY = y; + + if(AdjustedX < 0) + AdjustedX = AdjustedX + (-(int)(AdjustedX / (UnitsPerVertX * QuadsPerTile)) + 1) * (UnitsPerVertX * QuadsPerTile); + else + AdjustedX = fmod(AdjustedX, UnitsPerVertX * QuadsPerTile); + + if(AdjustedY < 0) + AdjustedY = AdjustedY + (-(int)(AdjustedY / (UnitsPerVertX * QuadsPerTile)) + 1) * (UnitsPerVertX * QuadsPerTile); + else + AdjustedY = fmod(AdjustedY, UnitsPerVertY * QuadsPerTile); + +#ifdef DEBUGDAT + if((AdjustedX != x) || (AdjustedY != y)) + printf(" Model translations adjusted from %8.3f, %8.3f to %8.3f, %8.3f\n", x, y, AdjustedX, AdjustedY); +#endif + + // Calculated which quad the adjusted x/y coordinated of the object lie in. + // + int RowNumber = (int)(AdjustedY / UnitsPerVertY); + + int Column = (int)(AdjustedX / UnitsPerVertX); + + int Quad = RowNumber * QuadsPerTile + Column; + + float QuadVertex1Z = Floats[Quad + RowNumber]; + float QuadVertex2Z = Floats[Quad + RowNumber + QuadsPerTile + 1]; + float QuadVertex3Z = Floats[Quad + RowNumber + QuadsPerTile + 2]; + float QuadVertex4Z = Floats[Quad + RowNumber + 1]; + + VERTEX P1(RowNumber * UnitsPerVertX, (Quad % QuadsPerTile) * UnitsPerVertY, QuadVertex1Z); + VERTEX P2(P1.x + UnitsPerVertX, P1.y, QuadVertex2Z); + VERTEX P3(P1.x + UnitsPerVertX, P1.y + UnitsPerVertY, QuadVertex3Z); + VERTEX P4(P1.x, P1.y + UnitsPerVertY, QuadVertex4Z); + + TerrainHeight = HeightWithinQuad(P1, P2, P3, P4, AdjustedY, AdjustedX); + +#ifdef DEBUGDAT + printf("XY: %8.3f, %8.3f Row: %i, Columm: %i, Quad: %i\n", x, y, RowNumber, Column, Quad); + printf("Zs: %8.3f, %8.3f, %8.3f, %8.3f\n", QuadVertex1Z, QuadVertex2Z, QuadVertex3Z, QuadVertex4Z); + printf("Height is %8.3f\n", TerrainHeight); +#endif + NewObjectGroup.TileZ = TerrainHeight; + + NewObjectGroup.RotX = 0; + NewObjectGroup.RotY = 0; + NewObjectGroup.RotZ = 0; + + NewObjectGroup.ScaleX = 1; + NewObjectGroup.ScaleY = 1; + NewObjectGroup.ScaleZ = 1; + + NewObjectGroup.SubObjects.push_back(model_data.PlaceableList.size()-1); + + model_data.ObjectGroups.push_back(NewObjectGroup); +#ifdef DEBUGDAT + printf("ObjectGroup %i created.\n", model_data.ObjectGroups.size()-1); +#endif + + } + + // We don't currently do anything with the Count2 objects. + // Things like water, teleports, zonelines seem to be in here, judging from the strings. + // + int Count2 = VARSTRUCT_DECODE_TYPE(uint32, buffer); +#ifdef DEBUGDAT + printf("Count 2 is (%iL,)\n", Count2); +#endif + for(int j = 0; j < Count2; ++j) + { + VARSTRUCT_DECODE_STRING(s, buffer); +#ifdef DEBUGDAT + printf("%s\n", s); +#endif + + VARSTRUCT_DECODE_TYPE(uint32, buffer); + + VARSTRUCT_DECODE_STRING(s, buffer); +#ifdef DEBUGDAT + printf("%s\n", s); +#endif + + int Longitude = VARSTRUCT_DECODE_TYPE(uint32, buffer); + int Latitude = VARSTRUCT_DECODE_TYPE(uint32, buffer); + + float x = VARSTRUCT_DECODE_TYPE(float, buffer); + float y = VARSTRUCT_DECODE_TYPE(float, buffer); + float z = VARSTRUCT_DECODE_TYPE(float, buffer); + + float RotX = VARSTRUCT_DECODE_TYPE(float, buffer); + float RotY = VARSTRUCT_DECODE_TYPE(float, buffer); + float RotZ = VARSTRUCT_DECODE_TYPE(float, buffer); + + float ScaleX = VARSTRUCT_DECODE_TYPE(float, buffer); + float ScaleY = VARSTRUCT_DECODE_TYPE(float, buffer); + float ScaleZ = VARSTRUCT_DECODE_TYPE(float, buffer); + + float SizeX = VARSTRUCT_DECODE_TYPE(float, buffer); + float SizeY = VARSTRUCT_DECODE_TYPE(float, buffer); + float SizeZ = VARSTRUCT_DECODE_TYPE(float, buffer); + } + + int Count3 = VARSTRUCT_DECODE_TYPE(uint32, buffer); + + // We don't currently do anything with the Count 3 data. Mostly seems to be camp fires and suchlike. + // +#ifdef DEBUGDAT + printf("Count 3 is (%iL,)\n", Count3); +#endif + for(int j = 0; j < Count3; ++j) + { + VARSTRUCT_DECODE_STRING(s, buffer); +#ifdef DEBUGDAT + printf("%s\n", s); +#endif + VARSTRUCT_DECODE_STRING(s, buffer); +#ifdef DEBUGDAT + printf("%s\n", s); +#endif + + VARSTRUCT_DECODE_TYPE(uint8, buffer); + + int Longitude = VARSTRUCT_DECODE_TYPE(uint32, buffer); + int Latitude = VARSTRUCT_DECODE_TYPE(uint32, buffer); + + float x = VARSTRUCT_DECODE_TYPE(float, buffer); + float y = VARSTRUCT_DECODE_TYPE(float, buffer); + float z = VARSTRUCT_DECODE_TYPE(float, buffer); + + float RotX = VARSTRUCT_DECODE_TYPE(float, buffer); + float RotY = VARSTRUCT_DECODE_TYPE(float, buffer); + float RotZ = VARSTRUCT_DECODE_TYPE(float, buffer); + + float ScaleX = VARSTRUCT_DECODE_TYPE(float, buffer); + float ScaleY = VARSTRUCT_DECODE_TYPE(float, buffer); + float ScaleZ = VARSTRUCT_DECODE_TYPE(float, buffer); + + VARSTRUCT_DECODE_TYPE(float, buffer); + } + + int Count4 = VARSTRUCT_DECODE_TYPE(uint32, buffer); + + // The data in this section mostly references .tog (Object Group) files. + // +#ifdef DEBUGDAT + printf("Count 4 is (%iL,)\n", Count4); +#endif + for(int j = 0; j < Count4; ++j) + { + char TogName[255]; + + VARSTRUCT_DECODE_STRING(TogName, buffer); +#ifdef DEBUGDAT + printf("%s\n", TogName); +#endif + + int Longitude = VARSTRUCT_DECODE_TYPE(uint32, buffer); + int Latitude = VARSTRUCT_DECODE_TYPE(uint32, buffer); + + float x = VARSTRUCT_DECODE_TYPE(float, buffer); + float y = VARSTRUCT_DECODE_TYPE(float, buffer); + float z = VARSTRUCT_DECODE_TYPE(float, buffer); + + float RotX = VARSTRUCT_DECODE_TYPE(float, buffer); + float RotY = VARSTRUCT_DECODE_TYPE(float, buffer); + float RotZ = VARSTRUCT_DECODE_TYPE(float, buffer); + + float ScaleX = VARSTRUCT_DECODE_TYPE(float, buffer); + float ScaleY = VARSTRUCT_DECODE_TYPE(float, buffer); + float ScaleZ = VARSTRUCT_DECODE_TYPE(float, buffer); + + float ZAdjust = VARSTRUCT_DECODE_TYPE(float, buffer); +#ifdef DEBUGDAT + printf("ZAdjust %8.3f\n", ZAdjust); +#endif + char TogFileName[255]; + + sprintf(TogFileName, "%s.tog", TogName); + + uchar *TogBuffer; + int TogBufferLength, TogPosition; + + if(!GetFile((uchar **)&TogBuffer, &TogBufferLength, base_path, TogFileName, archive)) + { + printf("Unable to open %s\n", TogFileName); + } + else + { + ObjectGroupEntry NewObjectGroup; + + NewObjectGroup.FromTOG = true; + + NewObjectGroup.y = y; + NewObjectGroup.x = x; + NewObjectGroup.z = z; + + NewObjectGroup.TileX = TileYStart; + NewObjectGroup.TileY = TileXStart; + + NewObjectGroup.TileZ = 0; + + NewObjectGroup.RotX = RotX; + NewObjectGroup.RotY = RotY; + NewObjectGroup.RotZ = RotZ; + + NewObjectGroup.ScaleX = ScaleX; + NewObjectGroup.ScaleY = ScaleY; + NewObjectGroup.ScaleZ = ScaleZ; + + NewObjectGroup.z = NewObjectGroup.z + (NewObjectGroup.ScaleZ * ZAdjust); + + Placeable p; + + TogPosition = 0; + + string ModelName; + + while(TogPosition < TogBufferLength) + { + string Token = GetToken(TogBuffer, TogPosition); + + if(Token == "*NAME") + { + Token = GetToken(TogBuffer, TogPosition); + ModelName = Token; + p.model = AddModelName(&model_data, Token.c_str()); + } + if(Token == "*POSITION") + { + p.x = atof(GetToken(TogBuffer, TogPosition).c_str()); + p.y = atof(GetToken(TogBuffer, TogPosition).c_str()); + p.z = atof(GetToken(TogBuffer, TogPosition).c_str()); + } + if(Token == "*ROTATION") + { + p.rx = atof(GetToken(TogBuffer, TogPosition).c_str()); + p.ry = atof(GetToken(TogBuffer, TogPosition).c_str()); + p.rz = atof(GetToken(TogBuffer, TogPosition).c_str()); + } + if(Token == "*SCALE") + { + p.scale[0] = atof(GetToken(TogBuffer, TogPosition).c_str()); + p.scale[1] = p.scale[2] = p.scale[0]; + } + if(Token == "*END_OBJECT") + { +#ifdef DEBUGDAT + printf("Pushing back object with model: %3i %s\n", p.model, ModelName.c_str()); + printf(" Position : %8.3f, %8.3f, %8.3f\n", p.x, p.y, p.z); + printf(" Rotations : %8.3f, %8.3f, %8.3f\n", p.rx, p.ry, p.rz); + printf(" Scale : %8.3f, %8.3f, %8.3f\n", p.scale[0], p.scale[1], p.scale[2]); +#endif + model_data.PlaceableList.push_back(p); + + NewObjectGroup.SubObjects.push_back(model_data.PlaceableList.size()-1); + } + if(Token == "*END_OBJECTGROUP") + { +#ifdef DEBUGDAT + printf("Pushing back new ObjectGroup\n"); + printf(" Position : %8.3f, %8.3f, %8.3f\n", NewObjectGroup.x, NewObjectGroup.y, NewObjectGroup.z); + printf(" Rotations : %8.3f, %8.3f, %8.3f\n", NewObjectGroup.RotX, NewObjectGroup.RotY, NewObjectGroup.RotZ); + printf(" Scale : %8.3f, %8.3f, %8.3f\n", NewObjectGroup.ScaleX, NewObjectGroup.ScaleY, NewObjectGroup.ScaleZ); + printf("Adjusting NewObjectGroup.z by %8.3f\n", NewObjectGroup.ScaleZ * ZAdjust); +#endif + model_data.ObjectGroups.push_back(NewObjectGroup); +#ifdef DEBUGDAT + printf("ObjectGroup %i created.\n", model_data.ObjectGroups.size()-1); + // There can be several object groups defined in the same .tog + NewObjectGroup.SubObjects.clear(); +#endif + } + if(Token == "*BEGIN_AREA") + { + while(Token != "*END_AREA") + { +#ifdef DEBUGDAT + printf("Skipping %s\n", Token.c_str()); +#endif + Token = GetToken(TogBuffer, TogPosition); + } + } + + + } + } + + } + + // If all the height values in this tile are the same, then we can just make one big quad of two triangles. + // + // This cuts over 200MB off the size of the oceanoftears .map, however not very much for other zones. + // + if(AllFloatsTheSame) + { + + float QuadVertex1X = TileXStart; + float QuadVertex1Y = TileYStart; + float QuadVertex1Z = Floats[0]; + + float QuadVertex2X = QuadVertex1X + (QuadsPerTile * UnitsPerVertX); + float QuadVertex2Y = QuadVertex1Y; + float QuadVertex2Z = QuadVertex1Z; + + float QuadVertex3X = QuadVertex2X; + float QuadVertex3Y = QuadVertex1Y + (QuadsPerTile * UnitsPerVertY); + float QuadVertex3Z = QuadVertex1Z; + + float QuadVertex4X = QuadVertex1X; + float QuadVertex4Y = QuadVertex3Y; + float QuadVertex4Z = QuadVertex1Z; + + + ++VertexNumber; + + zm->verts[VertexNumber++] = new Vertex(QuadVertex1Y, QuadVertex1X, QuadVertex1Z); + + zm->verts[VertexNumber++] = new Vertex(QuadVertex2Y, QuadVertex2X, QuadVertex2Z); + + zm->verts[VertexNumber++] = new Vertex(QuadVertex3Y, QuadVertex3X, QuadVertex3Z); + + zm->verts[VertexNumber] = new Vertex(QuadVertex4Y, QuadVertex4X, QuadVertex4Z); + + PolyNumber++; + + zm->polys[PolyNumber] = new Polygon; + + zm->polys[PolyNumber]->v1 = VertexNumber; + zm->polys[PolyNumber]->v2 = VertexNumber - 2; + zm->polys[PolyNumber]->v3 = VertexNumber - 1; + zm->polys[PolyNumber]->tex = -1; + + PolyNumber++; + + zm->polys[PolyNumber] = new Polygon; + + zm->polys[PolyNumber]->v1 = VertexNumber; + zm->polys[PolyNumber]->v2 = VertexNumber - 3; + zm->polys[PolyNumber]->v3 = VertexNumber - 2; + zm->polys[PolyNumber]->tex = -1; + } + else + { + int RowNumber = -1; + + // This is the code that generates triangles for this tile of the terrain based on the height values we decode above. + // + for(int Quad = 0; Quad < QuadCount; ++Quad) + { + if((Quad % QuadsPerTile) == 0) + ++RowNumber; + + // Other common values for this Byte are 0x80, 0x82. Setting them all to zero has no obvious visual + // effect. + if(Bytes[Quad] & 0x01) // Indicates Quad should not be included because an object will overlay it. + continue; + + float QuadVertex1X = TileXStart + (RowNumber * UnitsPerVertX); + float QuadVertex1Y = TileYStart + (Quad % QuadsPerTile) * UnitsPerVertY; + float QuadVertex1Z = Floats[Quad + RowNumber]; + + float QuadVertex2X = QuadVertex1X + UnitsPerVertX; + float QuadVertex2Y = QuadVertex1Y; + float QuadVertex2Z = Floats[Quad + RowNumber + QuadsPerTile + 1]; + + float QuadVertex3X = QuadVertex1X + UnitsPerVertX; + float QuadVertex3Y = QuadVertex1Y + UnitsPerVertY; + float QuadVertex3Z = Floats[Quad + RowNumber + QuadsPerTile + 2]; + + float QuadVertex4X = QuadVertex1X; + float QuadVertex4Y = QuadVertex1Y + UnitsPerVertY; + float QuadVertex4Z = Floats[Quad + RowNumber + 1]; + + ++VertexNumber; + + zm->verts[VertexNumber++] = new Vertex(QuadVertex1Y, QuadVertex1X, QuadVertex1Z); + + zm->verts[VertexNumber++] = new Vertex(QuadVertex2Y, QuadVertex2X, QuadVertex2Z); + + zm->verts[VertexNumber++] = new Vertex(QuadVertex3Y, QuadVertex3X, QuadVertex3Z); + + zm->verts[VertexNumber] = new Vertex(QuadVertex4Y, QuadVertex4X, QuadVertex4Z); + + PolyNumber++; + + zm->polys[PolyNumber] = new Polygon; + + zm->polys[PolyNumber]->v1 = VertexNumber; + zm->polys[PolyNumber]->v2 = VertexNumber - 2; + zm->polys[PolyNumber]->v3 = VertexNumber - 1; + zm->polys[PolyNumber]->tex = -1; + + PolyNumber++; + + zm->polys[PolyNumber] = new Polygon; + + zm->polys[PolyNumber]->v1 = VertexNumber; + zm->polys[PolyNumber]->v2 = VertexNumber - 3; + zm->polys[PolyNumber]->v3 = VertexNumber - 2; + zm->polys[PolyNumber]->tex = -1; + + } + } + zm->vert_count = VertexNumber + 1; + zm->poly_count = PolyNumber + 1; + + } + + delete [] Floats; + delete [] Color1; + delete [] Color2; + delete [] Bytes; + + this->status = 1; + return 1; +} + +int DATLoader::Close() { + + if(!status) + return 1; + + Zone_Model *zm = this->model_data.zone_model; + + int i; + + for(i = 0; i < zm->vert_count; ++i) + delete zm->verts[i]; + + for(i = 0; i < zm->poly_count; ++i) + delete zm->polys[i]; + + delete[] zm->verts; + + delete[] zm->polys; + + delete this->model_data.zone_model; + + status = 0; + + return 1; +} diff --git a/utils/azone2/dat.hpp b/utils/azone2/dat.hpp new file mode 100644 index 000000000..84f519de1 --- /dev/null +++ b/utils/azone2/dat.hpp @@ -0,0 +1,61 @@ +#ifndef __OPENEQ_DAT__ +#define __OPENEQ_DAT__ + +#include "global.hpp" +#include "file_loader.hpp" + +#pragma pack(1) + +struct dat_header { + unsigned long version, list_len, mat_count, vert_count, tri_count; +} typedef dat_header; + +struct dat_vertex { + float x, y, z; + float i, j, k; + float u, v; +} typedef dat_vertex; + +struct dat_vertexV3 { + float x, y, z; + float i, j, k; + long unk1; + float unk2, unk3; + float u, v; +} typedef dat_vertexV3; + +struct dat_triangle { + long v1, v2, v3; + long group; + long unk; +} typedef dat_triangle; + +struct dat_object { + long index; + long name_offset, another_name_offset; + long property_count; +} typedef dat_object; + +struct dat_property { + long name_offset, type, value; +} typedef dat_property; + +struct dat_material { + char *name; + char *basetex; + char var_count; + char **var_names; + char **var_vals; +} typedef dat_material; + +#pragma pack() + +class DATLoader : public FileLoader { +public: + DATLoader(); + ~DATLoader(); + virtual int Open(char *base_path, char *zone_name, Archive *archive); + virtual int Close(); +}; + +#endif diff --git a/utils/azone2/file.cpp b/utils/azone2/file.cpp new file mode 100644 index 000000000..681dad3be --- /dev/null +++ b/utils/azone2/file.cpp @@ -0,0 +1,31 @@ +#include +#include +#include "file.hpp" + +int GetFile(uchar **buffer, int *buf_len, char *base_path, char *file_name, Archive *archive) { + FILE *fp; + char *temp; + + if(base_path) { + temp = new char[strlen(base_path) + strlen(file_name) + 2]; + sprintf(temp, "%s/%s", base_path, file_name); + + fp = fopen(temp, "rb"); + + delete[] temp; + + if(!fp) + return 0; + + fseek(fp, 0, SEEK_END); + *buf_len = ftell(fp); + fseek(fp, 0, SEEK_SET); + *buffer = new uchar[*buf_len]; + fread(*buffer, 1, *buf_len, fp); + + fclose(fp); + return 1; + } + else + return archive->GetFile(file_name, buffer, buf_len); +} diff --git a/utils/azone2/file.hpp b/utils/azone2/file.hpp new file mode 100644 index 000000000..3175a4f8f --- /dev/null +++ b/utils/azone2/file.hpp @@ -0,0 +1,8 @@ +#ifndef __OPENEQ_FILE__ +#define __OPENEQ_FILE__ + +#include "archive.hpp" + +int GetFile(uchar **buffer, int *buf_len, char *base_path, char *file_name, Archive *archive); + +#endif diff --git a/utils/azone2/file_loader.hpp b/utils/azone2/file_loader.hpp new file mode 100644 index 000000000..adbe752b4 --- /dev/null +++ b/utils/azone2/file_loader.hpp @@ -0,0 +1,24 @@ +#ifndef __OPENEQ_FL_API__ +#define __OPENEQ_FL_API__ + +#include "archive.hpp" +#include "3d.hpp" + +class FileLoader { +public: + FileLoader() {} + virtual ~FileLoader() {} + + virtual int Open(char *base_path, char *zone_name, Archive *archive) = 0; + virtual int Close() = 0; + + Content_3D model_data; +protected: + uchar *buffer; + int buf_len; + Archive *archive; + + int status; +}; + +#endif diff --git a/utils/azone2/global.cpp b/utils/azone2/global.cpp new file mode 100644 index 000000000..aa22dd68e --- /dev/null +++ b/utils/azone2/global.cpp @@ -0,0 +1,16 @@ +#include "global.hpp" + +#define swapl +#define swaps + +uint16 GetUint16(uchar **buf) { + uint16 ret = swaps(*((uint16 *) *buf)); + *buf += 2; + return ret; +} + +uint32 GetUint32(uchar **buf) { + uint32 ret = swapl(*((uint32 *) *buf)); + *buf += 4; + return ret; +} diff --git a/utils/azone2/global.hpp b/utils/azone2/global.hpp new file mode 100644 index 000000000..59ffbc9c6 --- /dev/null +++ b/utils/azone2/global.hpp @@ -0,0 +1,34 @@ +#ifndef __OPENEQ_GLOBALS__ +#define __OPENEQ_GLOBALS__ + +#ifdef WIN32 +#define Polygon Polygon_Win32 +#include +#undef Polygon +#else +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +typedef unsigned long uint32; +typedef long int32; + +typedef unsigned short uint16; +typedef short int16; + +typedef unsigned char uint8; +typedef unsigned char uchar; +typedef char int8; + +uint16 GetUint16(uchar **buf); +uint32 GetUint32(uchar **buf); + +#define uint16(buf) \ +GetUint16((uchar **) &buf) +#define uint32(buf) \ +GetUint32((uchar **) &buf) + +#endif diff --git a/utils/azone2/listobj.cpp b/utils/azone2/listobj.cpp new file mode 100644 index 000000000..40d603230 --- /dev/null +++ b/utils/azone2/listobj.cpp @@ -0,0 +1,214 @@ +/* + + List Placeable Objects in an S3D or EQG. + By Derision, based on OpenEQ File Loaders by Daeken et al. + + 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 +*/ + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef short SHORT; +typedef unsigned long DWORD; + +#include +#include +#include +#include "types.h" +#include "wld.hpp" + +#include "archive.hpp" +#include "pfs.hpp" + +#include "file_loader.hpp" +#include "zon.hpp" +#include "ter.hpp" +#include "zonv4.hpp" + + + +bool ProcessZoneFile(const char *shortname); +void ListPlaceable(FileLoader *fileloader, char *ZoneFileName); +void ListPlaceableV4(FileLoader *fileloader, char *ZoneFileName); +enum EQFileType { S3D, EQG, UNKNOWN }; + + +int main(int argc, char *argv[]) { + + printf("LISTOBJ: List Placeable Objects in .S3D or .EQG zone files.\n"); + + if(argc != 2) { + printf("Usage: %s (zone short name)\n", argv[0]); + return(1); + } + + return(ProcessZoneFile(argv[1])); +} + +bool ProcessZoneFile(const char *shortname) { + + char bufs[96]; + Archive *archive; + FileLoader *fileloader; + Zone_Model *zm; + FILE *fff; + EQFileType FileType = UNKNOWN; + + sprintf(bufs, "%s.s3d", shortname); + + archive = new PFSLoader(); + fff = fopen(bufs, "rb"); + if(fff != NULL) + FileType = S3D; + else { + sprintf(bufs, "%s.eqg", shortname); + fff = fopen(bufs, "rb"); + if(fff != NULL) + FileType = EQG; + } + + if(FileType == UNKNOWN) { + printf("Unable to locate %s.s3d or %s.eqg\n", shortname, shortname); + return(false); + } + + if(archive->Open(fff) == 0) { + printf("Unable to open container file '%s'\n", bufs); + return(false); + } + + bool V4Zone = false; + + switch(FileType) { + case S3D: + fileloader = new WLDLoader(); + if(fileloader->Open(NULL, (char *) shortname, archive) == 0) { + printf("Error reading WLD from %s\n", bufs); + return(false); + } + break; + + case EQG: + fileloader = new ZonLoader(); + if(fileloader->Open(NULL, (char *) shortname, archive) == 0) { + delete fileloader; + fileloader = new Zonv4Loader(); + if(fileloader->Open(NULL, (char *) shortname, archive) == 0) { + printf("Error reading ZON/TER from %s\n", bufs); + return(false); + } + V4Zone = true; + } + break; + case UNKNOWN: + break; + } + + + zm = fileloader->model_data.zone_model; + + if(!V4Zone) + ListPlaceable(fileloader, bufs); + else + ListPlaceableV4(fileloader, bufs); + + return(true); +} + +void ListPlaceable(FileLoader *fileloader, char *ZoneFileName) { + + for(int i = 0; i < fileloader->model_data.plac_count; ++i) { + if(fileloader->model_data.placeable[i]->model==-1) continue; + if(fileloader->model_data.models[fileloader->model_data.placeable[i]->model] == NULL) continue; + printf("Placeable Object %4d @ (%9.2f, %9.2f, %9.2f uses model %4d %s\n",i, + fileloader->model_data.placeable[i]->y, + fileloader->model_data.placeable[i]->x, + fileloader->model_data.placeable[i]->z, + fileloader->model_data.placeable[i]->model, + fileloader->model_data.models[fileloader->model_data.placeable[i]->model]->name); + } +} + + +void ListPlaceableV4(FileLoader *fileloader, char *ZoneFileName) +{ + + float XOffset, YOffset, ZOffset; + float RotX, RotY, RotZ; + float XScale, YScale, ZScale; + + + vector::iterator Iterator; + + int OGNum = 0; + + Iterator = fileloader->model_data.ObjectGroups.begin(); + + while(Iterator != fileloader->model_data.ObjectGroups.end()) + { + printf("ObjectGroup Number: %i\n", OGNum++); + + printf("ObjectGroup Coordinates: %8.3f, %8.3f, %8.3f\n", + (*Iterator).x, (*Iterator).y, (*Iterator).z); + + printf("Tile: %8.3f, %8.3f, %8.3f\n", + (*Iterator).TileX, (*Iterator).TileY, (*Iterator).TileZ); + + printf("ObjectGroup Rotations : %8.3f, %8.3f, %8.3f\n", + (*Iterator).RotX, (*Iterator).RotY, (*Iterator).RotZ); + + printf("ObjectGroup Scale : %8.3f, %8.3f, %8.3f\n", + (*Iterator).ScaleX, (*Iterator).ScaleY, (*Iterator).ScaleZ); + + list::iterator ModelIterator; + + ModelIterator = (*Iterator).SubObjects.begin(); + + while(ModelIterator != (*Iterator).SubObjects.end()) + { + int SubModel = (*ModelIterator); + + Model *model = fileloader->model_data.models[fileloader->model_data.placeable[SubModel]->model]; + + printf(" SubModel: %i [Model: %i, %s] ", SubModel, fileloader->model_data.placeable[SubModel]->model, model->name ); + printf("Default to include in map: %s\n", model->IncludeInMap ? "YES" : "NO"); + + XOffset = fileloader->model_data.placeable[SubModel]->x; + YOffset = fileloader->model_data.placeable[SubModel]->y; + ZOffset = fileloader->model_data.placeable[SubModel]->z; + + printf(" Translations: %8.3f, %8.3f, %8.3f\n", XOffset, YOffset, ZOffset); + printf(" Rotations : %8.3f, %8.3f, %8.3f\n", fileloader->model_data.placeable[SubModel]->rx, + fileloader->model_data.placeable[SubModel]->ry, fileloader->model_data.placeable[SubModel]->rz); + printf(" Scale : %8.3f, %8.3f, %8.3f\n", fileloader->model_data.placeable[SubModel]->scale[0], + fileloader->model_data.placeable[SubModel]->scale[1], fileloader->model_data.placeable[SubModel]->scale[2]); + + + RotX = fileloader->model_data.placeable[SubModel]->rx * 3.14159 / 180; // Convert from degrees to radians + RotY = fileloader->model_data.placeable[SubModel]->ry * 3.14159 / 180; + RotZ = fileloader->model_data.placeable[SubModel]->rz * 3.14159 / 180; + + XScale = fileloader->model_data.placeable[SubModel]->scale[0]; + YScale = fileloader->model_data.placeable[SubModel]->scale[1]; + ZScale = fileloader->model_data.placeable[SubModel]->scale[2]; + + ++ModelIterator; + } + printf("\n"); + Iterator++; + } + + +} diff --git a/utils/azone2/octree.hpp b/utils/azone2/octree.hpp new file mode 100644 index 000000000..5115bd41e --- /dev/null +++ b/utils/azone2/octree.hpp @@ -0,0 +1,43 @@ +#ifndef __OPENEQ_OCTREE__ +#define __OPENEQ_OCTREE__ + +#ifdef WIN32 +#define Polygon Polygon_win32 +#include +#undef Polygon +#endif + +#include "3d_base.hpp" + +class Octree_Node { +public: + Octree_Node(); + Octree_Node(int poly_count, Polygon **polys, Vertex **verts, int plac_count, Placeable **plac); + Octree_Node(int poly_count, Polygon **polys, Vertex **verts, int plac_count, Placeable **plac, float min[3], float max[3]); + ~Octree_Node(); + void FindMinMax(); + void Split(char level); + + float min[3], max[3]; + float x, y, z, size, sizes[3]; + + int poly_count; + Vertex **verts; + Polygon **polys; + + int plac_count; + Placeable **plac; + + GLuint li; + + Octree_Node *nodes[8]; +}; + +#define in_box(obj, x, y, z) \ +( \ + (x) >= (obj)->min[0] && (x) <= (obj)->max[0] && \ + (y) >= (obj)->min[1] && (y) <= (obj)->max[1] &&\ + (z) >= (obj)->min[2] && (z) <= (obj)->max[2] \ +) + +#endif diff --git a/utils/azone2/pfs.cpp b/utils/azone2/pfs.cpp new file mode 100644 index 000000000..8575f8440 --- /dev/null +++ b/utils/azone2/pfs.cpp @@ -0,0 +1,253 @@ + +// This source is from OpenEQ by Daeken et al. Modified a bit by Derision, some bug fixes etc. + + +#include + +#include + +#include "pfs.hpp" + +#pragma pack(1) +//#define DEBUGPFS + +struct struct_header { + uint32 offset; + char magicCookie[4]; + uint32 unknown; +} typedef struct_header; + +struct struct_directory_header { + uint32 count; +} typedef struct_directory_header; + +struct struct_directory { + uint32 crc, offset, size; +} typedef struct_directory; + +struct struct_data_block { + uint32 deflen, inflen; +} typedef struct_data_block; + +struct struct_fn_header { + uint32 fncount; +} typedef struct_fn_header; + +struct struct_fn_entry { + uint32 fnlen; +} typedef struct_fn_entry; + +#pragma pack() + +inline void decompress(char *p, char *p2, int len, int uLen) { + int status; + z_stream d_stream; + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = (Bytef *) p; + d_stream.avail_in = len; + d_stream.next_out = (Bytef *) p2; + d_stream.avail_out = uLen; + + inflateInit(&d_stream); + status = inflate(&d_stream, Z_NO_FLUSH); + inflateEnd(&d_stream); +} + +inline void Lower(char *str) { + while(*str) { + if(*str >= 'A' && *str <= 'Z') + *str += 'a' - 'A'; + ++str; + } +} + +PFSLoader::PFSLoader() { + // Set the status of the loader that nothing is loaded. + this->buffer = NULL; + this->buf_len = -1; + this->status = 0; +} + +PFSLoader::~PFSLoader() { + this->Close(); +} + +int PFSLoader::Open(FILE *fp) +{ + struct_header s3d_header; + struct_directory_header s3d_dir_header; + struct_directory s3d_dir; + struct_data_block s3d_data; + struct_fn_header *s3d_fn_header; + struct_fn_entry *s3d_fn_entry; + + uint32 *offsets; + + char *temp, *temp2; + int i, j, pos, inf, tmp, running = 0; + + if(!fp) + return 0; + + fread(&s3d_header, sizeof(struct_header), 1, fp); + + if(s3d_header.magicCookie[0] != 'P' || s3d_header.magicCookie[1] != 'F' + || s3d_header.magicCookie[2] != 'S' || s3d_header.magicCookie[3] != ' ') + return 0; + + this->fp = fp; + +#ifdef DEBUGPFS + printf("Offset: %d\n", s3d_header.offset); +#endif + + fseek(fp, s3d_header.offset, SEEK_SET); + fread(&s3d_dir_header, sizeof(struct_directory_header), 1, fp); + + this->count = s3d_dir_header.count - 1; + this->filenames = new char *[s3d_dir_header.count]; + this->files = new uint32[s3d_dir_header.count - 1]; + offsets = new uint32[s3d_dir_header.count - 1]; + + for(i = 0; i < (int)s3d_dir_header.count; ++i) + { + fread(&s3d_dir, sizeof(struct_directory), 1, fp); + + if(s3d_dir.crc == ntohl(0xC90A5861)) + { + pos = ftell(fp); + fseek(fp, s3d_dir.offset, SEEK_SET); + temp = new char[s3d_dir.size]; + memset(temp, 0, s3d_dir.size); + inf = 0; + + while(inf < (int)s3d_dir.size) + { + fread(&s3d_data, sizeof(struct_data_block), 1, fp); + temp2 = new char[s3d_data.deflen]; + fread(temp2, s3d_data.deflen, 1, fp); + decompress(temp2, temp + inf, s3d_data.deflen, s3d_data.inflen); + delete[] temp2; + inf += s3d_data.inflen; + } + fseek(fp, pos, SEEK_SET); + s3d_fn_header = (struct_fn_header *) temp; + pos = sizeof(struct_fn_header); + for(j = 0; j < (int)s3d_fn_header->fncount; ++j) + { + s3d_fn_entry = (struct_fn_entry *) &temp[pos]; + this->filenames[j] = new char[s3d_fn_entry->fnlen + 1]; + this->filenames[j][s3d_fn_entry->fnlen] = 0; + memcpy(this->filenames[j], &temp[pos + sizeof(struct_fn_entry)], s3d_fn_entry->fnlen); + pos += sizeof(struct_fn_entry) + s3d_fn_entry->fnlen; + } + delete [] temp; + } + + else + { + this->files[running] = ftell(fp) - 12; + offsets[running] = s3d_dir.offset; + ++running; + } + + } + + for(i = s3d_dir_header.count - 2; i > 0; i--) + { + for(j = 0; j < i; j++) + { + if(offsets[j] > offsets[j+1]) + { + tmp = offsets[j]; + offsets[j] = offsets[j + 1]; + offsets[j + 1] = tmp; + tmp = this->files[j]; + this->files[j] = this->files[j + 1]; + this->files[j + 1] = tmp; + } + } + } + + delete [] offsets; + + this->status = 1; + + return 1; +} + +int PFSLoader::Close() +{ + if(this->status) + { + while(this->count > 0) + { + delete [] this->filenames[this->count - 1]; + --this->count; + } + delete[] this->filenames; + delete[] this->files; + } + else + return 0; + + this->buffer = NULL; + this->buf_len = -1; + this->status = 0; + + return 1; +} + +const char *PFSLoader::FindExtension(const char *ext) { + int i; + + int elen = strlen(ext); + + for(i = 0; i < this->count; ++i) { + int flen = strlen(this->filenames[i]); + if(flen <= elen) + continue; + if(!strcmp(this->filenames[i]+(flen-elen), ext)) + return(this->filenames[i]); + } + return(NULL); +} + +int PFSLoader::GetFile(char *name, uchar **buf, int *len) { + struct_directory s3d_dir; + struct_data_block s3d_data; + char *temp2; + long inf; + int i; + Lower(name); + + for(i = 0; i < this->count; ++i) { + + // Derision: Added basic wildcard ability as .ZON filenames don't always match the EQG name + if(!strcmp(this->filenames[i], name) || + ((name[0]=='*') && !strcmp(this->filenames[i]+strlen(filenames[i])-strlen(name)+2, name+2))) { + fseek(this->fp, this->files[i], SEEK_SET); + fread(&s3d_dir, sizeof(struct_directory), 1, this->fp); + fseek(this->fp, s3d_dir.offset, SEEK_SET); + *buf = new uchar[s3d_dir.size]; + + inf = 0; + while(inf < (int)s3d_dir.size) { + fread(&s3d_data, sizeof(struct_data_block), 1, this->fp); + temp2 = new char[s3d_data.deflen]; + fread(temp2, s3d_data.deflen, 1, this->fp); + decompress(temp2, (char *) *buf + inf, s3d_data.deflen, s3d_data.inflen); + delete [] temp2; + inf += s3d_data.inflen; + } + + *len = inf; + return 1; + } + } + return 0; +} diff --git a/utils/azone2/pfs.hpp b/utils/azone2/pfs.hpp new file mode 100644 index 000000000..e58246499 --- /dev/null +++ b/utils/azone2/pfs.hpp @@ -0,0 +1,21 @@ +#ifndef __OPENEQ_PFS__ +#define __OPENEQ_PFS__ + +#include +#include "global.hpp" +#include "archive.hpp" + + +class PFSLoader : public Archive { +public: + PFSLoader(); + ~PFSLoader(); + + virtual int Open(FILE *fp); + virtual int Close(); + + virtual int GetFile(char *name, uchar **buf, int *len); + virtual const char *FindExtension(const char *ext); +}; + +#endif diff --git a/utils/azone2/s3d.h b/utils/azone2/s3d.h new file mode 100644 index 000000000..44936d5e6 --- /dev/null +++ b/utils/azone2/s3d.h @@ -0,0 +1,51 @@ +#ifndef __EQCLIENT_S3D_H_ +#define __EQCLIENT_S3D_H_ +#include +#include "types.h" + +typedef struct struct_header { + uint32 offset; + char magicCookie[4]; + uint32 unknown; +} struct_header; + +typedef struct struct_directory_header { + uint32 count; +} struct_directory_header; + +typedef struct struct_directory { + uint32 crc, offset, size; +} struct_directory; + +typedef struct struct_data_block { + uint32 deflen, inflen; +} struct_data_block; + +typedef struct struct_fn_header { + uint32 fncount; +} struct_fn_header; + +typedef struct struct_fn_entry { + uint32 fnlen; +} struct_fn_entry; + +typedef struct s3d_object { + FILE *fp; + long count; + char **filenames; + uint32 *files; +} s3d_object; + +#ifdef __cplusplus +extern "C" { +#endif + +int S3D_Init(s3d_object *obj, FILE *fp); +size_t S3D_GetFile(s3d_object *obj, char *filename, uchar **out); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/utils/azone2/ter.cpp b/utils/azone2/ter.cpp new file mode 100644 index 000000000..dc6c1c0f5 --- /dev/null +++ b/utils/azone2/ter.cpp @@ -0,0 +1,243 @@ + +// This source is from OpenEQ by Daeken et al. Modified a bit by Derision, some bug fixes etc. + +#include +#include +#include + +#include "file.hpp" +#include "ter.hpp" +//#define DEBUGTER + +TERLoader::TERLoader() { + this->buffer = NULL; + this->buf_len = -1; + this->archive = NULL; + this->status = 0; +} + +TERLoader::~TERLoader() { + this->Close(); +} + +int TERLoader::Open(char *base_path, char *zone_name, Archive *archive) { + +#ifdef DEBUGTER + printf("TERLoader::Open %s, [%s]\n", base_path, zone_name); + fflush(stdout); +#endif + ter_header *thdr; + ter_vertex *tver; + ter_vertexV3 *tverV3; + ter_triangle *ttri; + uchar *ter_orig, *ter_tmp, *StartOfNameList; + int model = 0; + unsigned int i, j, mat_count = 0; + + Zone_Model *zm; + + material *mlist; + + uchar *buffer; + int buf_len, bone_count; + + char *filename; + + if(zone_name[strlen(zone_name) - 4] == '.') + filename = zone_name; + else { + filename = new char[strlen(zone_name) + 5]; + sprintf(filename, "%s.ter", zone_name); + } + + if(!GetFile(&buffer, &buf_len, base_path, filename, archive)) { + if(filename != zone_name) + delete[] filename; + return 0; + } + if(filename != zone_name) + delete[] filename; + + thdr = (ter_header *) buffer; + + mlist = new material[thdr->mat_count]; + + ter_orig = buffer; + + if(thdr->magic[0] != 'E' || thdr->magic[1] != 'Q' || thdr->magic[2] != 'G') + return 0; + + if(thdr->magic[3] == 'M') { + model = 1; + buffer += sizeof(ter_header); + bone_count = *((uint32 *) buffer); + buffer += sizeof(uint32); + } + else if(thdr->magic[3] == 'T') + buffer += sizeof(ter_header); + else + return 0; + + ter_tmp = buffer + thdr->list_len; + + if(sizeof(ter_header) + thdr->list_len + (thdr->vert_count * sizeof(ter_vertex)) + (thdr->tri_count * sizeof(ter_triangle)) > (unsigned int)buf_len) + return 0; + + for(i = 0; i < thdr->mat_count; ++i) { + mlist[i].name = NULL; + mlist[i].basetex = NULL; + } + + StartOfNameList = buffer; + buffer = buffer + thdr->list_len; +#ifdef DEBUGTER + printf("Offset is %X\n", buffer - ter_orig); +#endif + for(j=0; j< thdr->mat_count; j++) { + struct ter_object *tobj = (struct ter_object *)buffer; + buffer += sizeof(struct ter_object); + for (unsigned int i=0; i<(unsigned int)tobj->property_count; i++) { + struct ter_property *tprop = (struct ter_property *)buffer; + buffer += sizeof(struct ter_property); + } + + } + + this->model_data.zone_model = new Zone_Model; + zm = this->model_data.zone_model; + + zm->vert_count = thdr->vert_count; + zm->poly_count = thdr->tri_count; + + zm->verts = new Vertex *[zm->vert_count]; + zm->polys = new Polygon *[zm->poly_count]; + + this->model_data.plac_count = 0; + this->model_data.model_count = 0; + + buffer = ter_orig + thdr->list_len + sizeof(ter_header); + if(thdr->magic[3] == 'M') buffer = buffer + 4; +#ifdef DEBUGTER + printf("Starting offset is %8X\n", buffer-ter_orig); +#endif + for(unsigned int b=0; bmat_count; b++) { + unsigned long property_count = *((unsigned long *)(buffer+12)); +#ifdef DEBUGTER + printf("Property count is %d\n", property_count); fflush(stdout); +#endif + buffer += 16; + for (unsigned int a=0; avert_count; ++i) { + if(thdr->version<3) { + tver = (ter_vertex *) buffer; + zm->verts[i] = new Vertex; + zm->verts[i]->x = tver->x; + zm->verts[i]->y = tver->y; + zm->verts[i]->z = tver->z; + zm->verts[i]->u = tver->u; + zm->verts[i]->v = tver->v; + buffer += sizeof(ter_vertex); + } + else { + tverV3 = (ter_vertexV3 *) buffer; + zm->verts[i] = new Vertex; + zm->verts[i]->x = tverV3->x; + zm->verts[i]->y = tverV3->y; + zm->verts[i]->z = tverV3->z; + zm->verts[i]->u = tverV3->u; + zm->verts[i]->v = tverV3->v; + buffer += sizeof(ter_vertexV3); + } + + + } + + j = 0; + for(i = 0; i < (unsigned int)zm->poly_count; ++i) { + ttri = (ter_triangle *) buffer; + if(ttri->group == -1) { + buffer += sizeof(ter_triangle); + continue; + } + zm->polys[j] = new Polygon; +/* + zm->polys[j]->v1 = ttri->v1; + zm->polys[j]->v2 = ttri->v2; + zm->polys[j]->v3 = ttri->v3; +*/ + +// Change the order of the vertices to keep the normals consistent with map files generated for S3Ds in prior versions of azone. + +#ifndef KEEP_OLD_EQG_VERTEX_ORDER + zm->polys[j]->v1 = ttri->v3; + zm->polys[j]->v2 = ttri->v2; + zm->polys[j]->v3 = ttri->v1; +#else + zm->polys[j]->v1 = ttri->v1; + zm->polys[j]->v2 = ttri->v2; + zm->polys[j]->v3 = ttri->v3; +#endif + zm->polys[j]->flags = ttri->unk; + + + if(ttri->group == -1) { +#ifdef DEBUGTER + printf("TERLoader:: zm->poly[%d]->tex = mat_count%d\n", j, thdr->mat_count); + fflush(stdout); +#endif + zm->polys[j]->tex = thdr->mat_count; + } + else { +#ifdef DEBUGTER + printf("TERLoader:: zm->poly[%d]->tex = ttri->group %d, Unk=%d\n", j, ttri->group, ttri->unk); + fflush(stdout); +#endif + zm->polys[j]->tex = ttri->group; + } + + ++j; + buffer += sizeof(ter_triangle); + } + + zm->poly_count = j; + + zm->tex_count = 0; + + delete[] mlist; + + delete [] ter_orig; + + this->status = 1; + return 1; +} + +int TERLoader::Close() { + Zone_Model *zm = this->model_data.zone_model; + int i; + + //return 1; + + if(!this->status) + return 1; + + for(i = 0; i < zm->vert_count; ++i) + delete zm->verts[i]; + for(i = 0; i < zm->poly_count; ++i) + delete zm->polys[i]; + + delete[] zm->verts; + delete[] zm->polys; + + delete this->model_data.zone_model; + + status = 0; + + return 1; +} diff --git a/utils/azone2/ter.hpp b/utils/azone2/ter.hpp new file mode 100644 index 000000000..197c89fa4 --- /dev/null +++ b/utils/azone2/ter.hpp @@ -0,0 +1,62 @@ +#ifndef __OPENEQ_TER__ +#define __OPENEQ_TER__ + +#include "global.hpp" +#include "file_loader.hpp" + +#pragma pack(1) + +struct ter_header { + char magic[4]; + unsigned long version, list_len, mat_count, vert_count, tri_count; +} typedef ter_header; + +struct ter_vertex { + float x, y, z; + float i, j, k; + float u, v; +} typedef ter_vertex; + +struct ter_vertexV3 { + float x, y, z; + float i, j, k; + long unk1; + float unk2, unk3; + float u, v; +} typedef ter_vertexV3; + +struct ter_triangle { + long v1, v2, v3; + long group; + long unk; +} typedef ter_triangle; + +struct ter_object { + long index; + long name_offset, another_name_offset; + long property_count; +} typedef ter_object; + +struct ter_property { + long name_offset, type, value; +} typedef ter_property; + +struct material { + char *name; + char *basetex; + char var_count; + char **var_names; + char **var_vals; +} typedef material; + +#pragma pack() + +class TERLoader : public FileLoader { +public: + TERLoader(); + ~TERLoader(); + virtual int Open(char *base_path, char *zone_name, Archive *archive); + virtual int Close(); +}; + +#endif diff --git a/utils/azone2/types.h b/utils/azone2/types.h new file mode 100644 index 000000000..f3d6c3016 --- /dev/null +++ b/utils/azone2/types.h @@ -0,0 +1,16 @@ +#ifndef __EQCLIENT_TYPES_H_ +#define __EQCLIENT_TYPES_H_ + +typedef unsigned long uint32; +typedef long int32; + +typedef unsigned short uint16; +typedef short int16; + +typedef unsigned char uint8; +typedef unsigned char uchar; +typedef char int8; + +#define null 0 + +#endif diff --git a/utils/azone2/wld.cpp b/utils/azone2/wld.cpp new file mode 100644 index 000000000..143af6474 --- /dev/null +++ b/utils/azone2/wld.cpp @@ -0,0 +1,758 @@ + +// This source is from OpenEQ by Daeken et al. Modified a bit by Derision, some bug fixes etc. + +#include "wld.hpp" +#include "file.hpp" +#include "pfs.hpp" + +#include +#include +//#define DEBUGWLD + +static uchar encarr[] = {0x95, 0x3A, 0xC5, 0x2A, 0x95, 0x7A, 0x95, 0x6A}; + +inline void decode(uchar *str, int len) { int i; for(i = 0; i < len; ++i) str[i] ^= encarr[i % 8]; } + + +FRAG_CONSTRUCTOR(Data03) { + int i, count, fnlen; + Texture *tex; + + count = uint32(buf); + if(!count) count = 1; + + tex = new Texture; + tex->filenames = new char *[count]; + tex->frame_count = count; + tex->current_frame = 0; + tex->archive = NULL; + tex->flags = 0; + + for(i = 0; i < count; ++i) { + fnlen = uint16(buf); + tex->filenames[i] = new char[fnlen]; + memcpy(tex->filenames[i], buf, fnlen); + decode((uchar *) tex->filenames[i], fnlen); + + //printf("fnlen is %d, %s\n", fnlen, tex->filenames[i]); + // Derision: Not sure why this check is here, but need to check fnlen is >=18 + if(fnlen>=18) { + if(tex->filenames[i][fnlen - 8] == '.') + tex->filenames[i][fnlen - 17] = '\0'; + } + + buf += fnlen; + } + this->frag = (void *) tex; +} + +FRAG_CONSTRUCTOR(Data04) { + int flags, count, i; + Texture *tex, *texref; + + flags = uint32(buf); + count = uint32(buf); + + if(flags & (1 << 2)) + buf += 4; + if(flags & (1 << 3)) + buf += 4; + + if(count == 0 || count == 1) + this->frag = wld->frags[uint32(buf) - 1]->frag; + else { + tex = new Texture; + tex->frame_count = count; + tex->current_frame = 0; + tex->archive = NULL; + tex->filenames = new char *[count]; + tex->flags = 0; + + for(i = 0; i < count; ++i) { + texref = (Texture *) wld->frags[uint32(buf) - 1]->frag; + tex->filenames[i] = texref->filenames[texref->frame_count - 1]; + } + + this->frag = tex; + } +} + +FRAG_CONSTRUCTOR(Data05) { + this->frag = wld->frags[uint32(buf) - 1]->frag; +} + +FRAG_CONSTRUCTOR(Data15) { + struct_Data15 *hdr = (struct_Data15 *) buf; + Placeable *plac = new Placeable; + Placeable **pl; + + plac->x = hdr->trans[0]; + plac->y = hdr->trans[1]; + plac->z = hdr->trans[2]; + + plac->rx = hdr->rot[2] / 512.f * 360.f; + plac->ry = hdr->rot[1] / 512.f * 360.f; + plac->rz = hdr->rot[0] / 512.f * 360.f; + + plac->scale[0] = hdr->scale[2]; + plac->scale[1] = hdr->scale[1]; + plac->scale[2] = hdr->scale[1]; + + plac->model = atoi((const char*)&wld->sHash[-(int)hdr->ref]); + + pl = new Placeable *[wld->model_data.plac_count + 1]; + memcpy(pl, wld->model_data.placeable, sizeof(Placeable *) * wld->model_data.plac_count); + if(wld->model_data.plac_count) + delete[] wld->model_data.placeable; + pl[wld->model_data.plac_count] = plac; + wld->model_data.placeable = pl; + ++wld->model_data.plac_count; +} + +FRAG_CONSTRUCTOR(Data1B) { + struct_Data1B *data = (struct_Data1B *) buf; + Light *light = new Light; + this->frag = light; + light->x = light->y = light->z = light->rad = 0; + if(data->flags & (1 << 3)) { + light->r = data->color[0]; + light->g = data->color[1]; + light->b = data->color[2]; + } + else + light->r = light->g = light->b = 1.0f; +} + +FRAG_CONSTRUCTOR(Data1C) { + this->frag = wld->frags[uint32(buf) - 1]->frag; +} + +FRAG_CONSTRUCTOR(Data21) { + + struct_Data21 *data; + long count = *((long *) buf); + long i; + + wld->tree = (BSP_Node *) malloc(count * sizeof(BSP_Node)); + + // Build the BSP Tree + // + for(i = 0; i < count; ++i) { + wld->tree[i].node_number=i; + data = (struct_Data21 *) (buf + (i * sizeof(struct_Data21)) + 4); + wld->tree[i].normal[0] = data->normal[0]; + wld->tree[i].normal[1] = data->normal[1]; + wld->tree[i].normal[2] = data->normal[2]; + wld->tree[i].splitdistance = data->splitdistance; + wld->tree[i].region = data->region; + wld->tree[i].left = data->node[0]; + wld->tree[i].right = data->node[1]; + } + + + +} + +FRAG_CONSTRUCTOR(Data22) { + + int pos; + + uchar *data6area ; + + struct_Data22 *data = (struct_Data22 *) buf; + + pos = sizeof(struct_Data22) + (12 * data->size1) + (8 * data->size2); + + + + + data6area = buf + pos; + + if((data->size3!=0)||(data->size4!=0)) { + printf("Size 3 and 4 not zero, we can't handle that yet\n"); + exit(1); + } + data6area = data6area + ((data->size5) * 7 * 4); + + unsigned short d6size = *((unsigned short *) data6area); + data6area = data6area + 2; // Move past d6 size + data6area = data6area + d6size; // Move past RLE data ? Hopefully; + + float f1, f2, f3, f4; + + f1 = *((float *) data6area); + f2 = *((float *) (data6area+4)); + f3 = *((float *) (data6area+8)); + f4 = *((float *) (data6area+12)); + + long Frag36Ref; + if(data->flags==0x181) { + Frag36Ref = *((long *) (data6area+20)); + //printf("Frag 36 reference?: %ld\n", *((long *) (data6area+20))); + } + + + + + +} + +FRAG_CONSTRUCTOR(Data29) { + + long a,flags, numregions, lenstr; + + struct_Data29 *data29 = (struct_Data29 *) malloc(sizeof(struct_Data29)); + data29->region_type = -1; // Start of by flagging type as unknown + + if(!strncmp((char *) &wld->sHash[-frag_name], "WT", 2)) data29->region_type = 1; // Water + else if(!strncmp((char *) &wld->sHash[-frag_name], "LA", 2)) data29->region_type = 2; // Lava + else if(!strncmp((char *) &wld->sHash[-frag_name], "DRNTP", 5)) data29->region_type = 3; // Zone Line + else if(!strncmp((char *) &wld->sHash[-frag_name], "DRP_", 4)) data29->region_type = 4; // PVP + else if(!strncmp((char *) &wld->sHash[-frag_name], "SL", 2)) data29->region_type = 5; // Slippery/Slime ? + else if(!strncmp((char *) &wld->sHash[-frag_name], "DRN", 3)) data29->region_type = 6; // Ice/Ice Water + else if(!strncmp((char *) &wld->sHash[-frag_name], "VWA", 3)) data29->region_type = 7; // VWater ? + + this->frag = (void *) data29; + + flags = *buf; + numregions = *((long *)(buf+4)); + data29->region_count = numregions ; + data29->region_array = (long *)malloc(numregions * sizeof(long)); + for(a=0;aregion_array[a] = *((long *)(buf+8+(a*4))); + } + lenstr = *((long *)(buf+8+(numregions*4))); + data29->strlen=lenstr; + if(lenstr==0) return; + + data29->str = (char *)malloc(lenstr); + + char *encstr; + + encstr = (char *)buf+8+(++a*4); + + decode((uchar *)encstr, lenstr); + strcpy(data29->str, encstr); +// printf(" data2 name is %s\n", data29->str); + if(lenstr>=5) + if(!strncmp(encstr,"WT",2)) data29->region_type = 1; // Water + else if(!strncmp(encstr,"LA",2)) data29->region_type = 2; // Lava + else if(!strncmp(encstr,"DRNTP",5)) data29->region_type = 3; // Zone Line ? + else if(!strncmp(encstr,"DRP_",4)) data29->region_type = 4; //PVP + else if(!strncmp(encstr,"SL",2)) data29->region_type = 5; // Slippery/Slime ? + else if(!strncmp(encstr,"DRN",3)) data29->region_type = 6; // Ice/Ice Water ? + else if(!strncmp(encstr,"VWN",3)) data29->region_type = 7; // Ice/Ice Water ? + else data29->region_type = -2; // Flag this so that it doesn't get set to the default water or lava + // later, because it is neither. + // + +} + +FRAG_CONSTRUCTOR(Data28) { +// struct_Data28 *data = (struct_Data28 *) buf; // obviously unused for now. +} + +FRAG_CONSTRUCTOR(Data30) { + struct_Data30 *data; + uint32 ref; + Texture *tex; + + data = (struct_Data30 *) buf; + + buf += sizeof(struct_Data30); + + if(!data->flags) + buf += 8; + + ref = uint32(buf); + + if(!data->params1 || !ref) { + tex = new Texture; + tex->frame_count = 1; + tex->current_frame = 0; + tex->filenames = new char *[1]; + tex->filenames[0] = "collide.dds"; + tex->archive = NULL; + tex->flags = 1; + this->frag = (void *) tex; + return; + } + + tex = (Texture *) wld->frags[ref - 1]->frag; + + if(data->params1 & (1 << 1) || data->params1 & (1 << 2) || data->params1 & (1 << 3) || data->params1 & (1 << 4)) + tex->flags = 1; + else + tex->flags = 0; + + this->frag = (void *) tex; +} + +FRAG_CONSTRUCTOR(Data31) { + TexRef *tr; + int i, count; + + buf += 4; + count = uint32(buf); + + tr = new TexRef; + tr->tex_count = count; + tr->tex = new Texture *[count]; + + for(i = 0; i < count; ++i) + tr->tex[i] = (Texture *) wld->frags[uint32(buf) - 1]->frag; + + this->frag = (void *) tr; +} + +FRAG_CONSTRUCTOR(Data36) { + struct_Data36 *header = (struct_Data36 *) buf; + Poly *p; + TexCoordsNew *tex_new; + TexCoordsOld *tex_old; + VertexNormal *vn; + Vert *v; + + + + TexRef *tr; + + Model *model; + Vertex *vert; + Polygon *poly; + + float scale = 1.0f / (float) (1 << header->scale); + + int i, j, pc; + TexMap *tm; + + float recip_255 = 1.0f / 256.0f, recip_127 = 1.0f / 127.0f; + + buf += sizeof(struct_Data36); + + model = new Model; + model->vert_count = header->vertexCount; + model->poly_count = header->PolygonsCount; + model->verts = new Vertex *[model->vert_count]; + model->polys = new Polygon *[model->poly_count]; + tr = (TexRef *) wld->frags[header->fragment1 - 1]->frag; + model->tex = tr->tex; // Reference to type 0x31 Texture List fragment + model->tex_count = tr->tex_count; + model->name = (char *) &wld->sHash[-frag_name]; + +#ifdef DEBUGWLD + + printf("Frag 0x36, %4d Vertices, %4d Polys, %4d TexCoords, %4d Normals, %3d Colors, %3d Polytex, %3d VertexTex, Name %s\n", + header->vertexCount, header->PolygonsCount, header->texCoordsCount, header->normalsCount, header->colorCount, header->PolygonTexCount, header->vertexTexCount, model->name); +#endif + + for(i = 0; i < header->vertexCount; ++i) { + v = (Vert *) buf; + + vert = model->verts[i] = new Vertex; + + vert->x = header->centerX + v->x * scale; + vert->y = header->centerY + v->y * scale; + vert->z = header->centerZ + v->z * scale; +#ifdef DEBUGWLD2 + printf(" Vertex %4d: X=%4.2f, Y=%4.2f, Z=%4.2f\n", i, vert->x, vert->y, vert->z); +#endif + + buf += sizeof(Vert); + } + + if(wld->old) { + for(i = 0; i < header->texCoordsCount; ++i) { + tex_old = (TexCoordsOld *) buf; + vert = model->verts[i]; + + vert->u = (float) tex_old->tx * recip_255; + vert->v = (float) tex_old->tz * recip_255; + +#ifdef DEBUGWLD2 + printf(" TexCoord %4d: U=%4.2f, V=%4.2f\n", i, vert->u, vert->v); +#endif + + buf += sizeof(TexCoordsOld); + } + } + else { + for(i = 0; i < header->texCoordsCount; ++i) { + tex_new = (TexCoordsNew *) buf; + vert = model->verts[i]; + + vert->u = tex_new->tx; + vert->v = tex_new->tz; + +#ifdef DEBUGWLD2 + printf(" TexCoord %4d: U=%4.2f, V=%4.2f\n", i, vert->u, vert->v); +#endif + buf += sizeof(TexCoordsNew); + } + } + + + for(i = 0; i < header->normalsCount; ++i) { + + if(ivertexCount) { + vn = (VertexNormal *) buf; + vert = model->verts[i]; + + vert->i = (float) vn->nx * recip_127; + vert->j = (float) vn->ny * recip_127; + vert->k = (float) vn->nz * recip_127; + } + + buf += sizeof(VertexNormal); + } + + buf += 4 * header->colorCount; + + for(i = 0; i < header->PolygonsCount; ++i) { + p = (Poly *) buf; + + poly = model->polys[i] = new Polygon; + + poly->flags = p->flags; + // Derision: 16/06/08 + // Previously, this was assigning v1=v1, v2=v3, v3=v2. This is different to how it was done + // in the WLD loader in the stock azone, and has the effect of screwing up the normals, + // Changed to keep things consistent with prior versions. + poly->v1 = p->v1; + poly->v2 = p->v2; + poly->v3 = p->v3; +#ifdef DEBUGWLD2 + printf("Frag36: Polygon: %5d Vertices: %5d, %5d, %5d\n", i, poly->v1, poly->v2, poly->v3); +#endif + buf += sizeof(Poly); + } + + buf += 4 * header->size6; + + pc = 0; + + // For each polygontex entry ... + for(i = 0; i < header->PolygonTexCount; ++i) { + tm = (TexMap *) buf; + + for(j = 0; j < tm->polycount; ++j) { + if(pc>=(model->poly_count)) { + break; // Derision: Crashing here in acrylia because of this. TODO: Find root cause, for now, this lets zone load + } + // there are tm->polycount consecutive polys that use this tex + model->polys[pc++]->tex = tm->tex; + } + + buf += 4; + } + + +#ifdef DEBUGWLD + fflush(stdout); +#endif + + this->frag = (void *) model; +} + +WLDLoader::WLDLoader() { +} + +WLDLoader::~WLDLoader() { + this->Close(); +} + +int WLDLoader::Open(char *base_path, char *zone_name, Archive *archive) { + uchar *buffer; + int buf_len; + int i, j, vc, pc, bc, mlen, *pmap; + Zone_Model *zm; + Model *m; + + Archive *obj_archive; + + char *filename, *model_name; + + struct_wld_header *header; + struct_wld_basic_frag *frag; + +#ifdef DEBUGWLD + printf("Zone: %s\n", zone_name); +#endif + this->model_data.zone_model = NULL; + + this->model_data.plac_count = 0; + this->model_data.placeable = 0; + + this->obj_loader = this->plac_loader = NULL; + + this->clear_plac = 0; + + filename = new char[strlen(zone_name) + 5]; + sprintf(filename, "%s.wld", zone_name); + + if(!GetFile(&buffer, &buf_len, NULL, filename, archive)) { + delete[] filename; + return 0; + } + delete[] filename; + + header = (struct_wld_header *) buffer; + + if(header->magic != 0x54503d02) + return 0; + + buffer += sizeof(struct_wld_header); + + this->sHash = buffer; + decode(this->sHash, header->stringHashSize); + buffer += header->stringHashSize; + + if(header->version == 0x00015500) + this->old = 1; + else + this->old = 0; + + this->fragcount = header->fragmentCount; + this->frags = new Fragment *[header->fragmentCount]; + + + for(i = 0; i < this->fragcount; ++i) { + frag = (struct_wld_basic_frag *) buffer; + + buffer += sizeof(struct_wld_basic_frag); + + switch(frag->id) { + case 0x03: FRAGMENT(Data03); break; + case 0x04: FRAGMENT(Data04); break; + case 0x05: FRAGMENT(Data05); break; + case 0x15: FRAGMENT(Data15); break; + case 0x1B: FRAGMENT(Data1B); break; + case 0x1C: FRAGMENT(Data1C); break; + case 0x21: FRAGMENT(Data21); break; + case 0x22: FRAGMENT(Data22); break; + case 0x29: FRAGMENT(Data29); break; + case 0x28: FRAGMENT(Data28); break; + case 0x30: FRAGMENT(Data30); break; + case 0x31: FRAGMENT(Data31); break; + case 0x36: FRAGMENT(Data36); break; + + default: this->frags[i] = new Fragment; break; + } + + this->frags[i]->type = frag->id; + this->frags[i]->name = frag->nameRef; + + buffer += frag->size - 4; + } + + + + + if(!strcmp(&zone_name[strlen(zone_name) - 4], "_obj")) { + this->model_data.plac_count = 0; + this->model_data.model_count = 0; + // for an obj_s3d file, find out how many placeabale objects there are (each 0x36 Frag is a placeable object in an obj_s3d file + for(i = 0; i < this->fragcount; ++i) { + if(this->frags[i]->type == 0x36) + ++this->model_data.model_count; + } + // allocate space for this many models + this->model_data.models = new Model *[this->model_data.model_count]; + this->model_data.model_count = 0; + for(i = 0; i < this->fragcount; ++i) { + if(this->frags[i]->type == 0x36) + this->model_data.models[this->model_data.model_count++] = (Model *) this->frags[i]->frag; + } + } + else if(!strcmp(&zone_name[strlen(zone_name) - 4], "_chr")) { + } + else if(!strcmp(zone_name, "objects")) { + this->clear_plac = 1; + } + else { + // We are procesing the zone mesh S3D + zm = this->model_data.zone_model = new Zone_Model; + zm->vert_count = 0; + zm->poly_count = 0; + for(i = 0; i < this->fragcount; ++i) { + if(this->frags[i]->type != 0x36) + continue; + m = (Model *) this->frags[i]->frag; + zm->vert_count += m->vert_count; + zm->poly_count += m->poly_count; + zm->tex_count = m->tex_count; // The texcount and tex fields are the same for every 0x36 frag in the zone mesh S3D + zm->tex = m->tex; + } + + + + zm->verts = new Vertex *[zm->vert_count]; + zm->polys = new Polygon *[zm->poly_count]; + + vc = pc = 0; + + for(i = 0; i < this->fragcount; ++i) { + if(this->frags[i]->type != 0x36) + continue; + m = (Model *) this->frags[i]->frag; + bc = vc; + // Copy the vertexes from the 0x36 frag into our zone model + for(j = 0; j < m->vert_count; ++j) + zm->verts[vc++] = m->verts[j]; + // Adjust the polygon vertices indices to match the indices in our zone model vertex array + for(j = 0; j < m->poly_count; ++j) { + m->polys[j]->v1 += bc; + m->polys[j]->v2 += bc; + m->polys[j]->v3 += bc; +#ifdef DEBUGWLD + printf("Polygon: %5d Vertices %5d, %5d, %5d\n", j, + m->polys[j]->v1, m->polys[j]->v2, m->polys[j]->v3); +#endif + +#ifdef DEBUGWLD + printf("Zone Frag36 No. %5d: Poly %5d V3 = %4.3f, %4.3f, %4.3f\n", i, j, zm->verts[m->polys[j]->v3]->x, zm->verts[m->polys[j]->v3]->y, zm->verts[m->polys[j]->v3]->z); + fflush(stdout); +#endif + zm->polys[pc++] = m->polys[j]; + } + } + + this->model_data.plac_count = 0; + this->model_data.model_count = 0; + + filename = new char[strlen(zone_name) + 10]; + sprintf(filename, "%s_obj.s3d",zone_name); + + obj_archive = new PFSLoader; + if(!obj_archive->Open(fopen(filename, "rb"))) { +// printf("_obj.s3d file not found.\n"); + return 1; + } + + delete[] filename; + + filename = new char[strlen(zone_name) + 5]; + sprintf(filename, "%s_obj", zone_name); + + this->obj_loader = new WLDLoader(); + this->obj_loader->Open(NULL, filename, obj_archive); + + delete[] filename; + + this->model_data.model_count = this->obj_loader->model_data.model_count; + this->model_data.models = this->obj_loader->model_data.models; + + this->plac_loader = new WLDLoader(); + this->plac_loader->Open(NULL, "objects", archive); + + if(this->model_data.plac_count) { + delete this->model_data.placeable[0]; + delete[] this->model_data.placeable; + } + + this->model_data.plac_count = this->plac_loader->model_data.plac_count; + this->model_data.placeable = this->plac_loader->model_data.placeable; + + + for(i = 0; i < this->model_data.plac_count; ++i) { + mlen = strlen((char *) this->model_data.placeable[i]->model) - 8; + model_name = new char[mlen + 12]; + memcpy(model_name, (char *) this->model_data.placeable[i]->model, mlen); + model_name[mlen] = 0; + sprintf(model_name, "%sDMSPRITEDEF", model_name); + this->model_data.placeable[i]->model = -1; + + for(j = 0; j < this->model_data.model_count; ++j) { + if(!strcmp(this->model_data.models[j]->name, model_name)) { + this->model_data.placeable[i]->model = j; +// printf("Placeable object %d name is %s\n", j, model_name); + break; + } + } + + + delete[] model_name; + } + + for(i = 0; i < this->model_data.model_count; ++i) { + for(j = 0; j < this->model_data.models[i]->tex_count; ++j) + this->model_data.models[i]->tex[j]->archive = obj_archive; + } + + for(i = 0; i < this->model_data.model_count; ++i) { + pmap = new int[this->model_data.models[i]->poly_count]; + memset(pmap, 0, sizeof(int) * this->model_data.models[i]->poly_count); + for(j = 0; j < this->model_data.models[i]->tex_count; ++j) { + filename = this->model_data.models[i]->tex[j]->filenames[0]; + for(bc = 0; bc < this->model_data.zone_model->tex_count; ++bc) { + if(!strcmp(filename, this->model_data.zone_model->tex[bc]->filenames[0])) { + for(pc = 0; pc < this->model_data.models[i]->poly_count; ++pc) { + if(!pmap[pc] && this->model_data.models[i]->polys[pc]->tex == j) { + this->model_data.models[i]->polys[pc]->tex = bc; + pmap[pc] = 1; + } + } + break; + } + } + } + delete[] pmap; + } + } + + return 1; +} + +int WLDLoader::Close() { + int i, j; + Zone_Model *zm = this->model_data.zone_model; + Model *m; + + return -1; + + if(zm) { + for(j = 0; j < zm->tex_count; ++j) + delete zm->tex[j]; + delete[] zm->tex; + + delete[] zm->verts; + delete[] zm->polys; + } + + for(i = 0; i < this->fragcount; ++i) { + if(this->frags[i]->type == 0x36) { + m = (Model *) this->frags[i]->frag; + + for(j = 0; j < m->vert_count; ++j) + delete m->verts[j]; + for(j = 0; j < m->poly_count; ++j) + delete m->polys[j]; + + if(!zm) { + for(j = 0; j < m->tex_count; ++j) + delete m->tex[j]; + delete[] m->tex; + } + + delete[] m->verts; + delete[] m->polys; + delete m; + } + else + delete this->frags[i]; + } + delete[] this->frags; + + if(this->clear_plac) { + // for(i = 0; i < this->model_data.plac_count; ++i) + // delete[] this->model_data.placeable[i]; + } + + delete[] this->model_data.placeable; + + delete this->obj_loader; + delete this->plac_loader; + + return 1; +} + diff --git a/utils/azone2/wld.hpp b/utils/azone2/wld.hpp new file mode 100644 index 000000000..b6760ea8a --- /dev/null +++ b/utils/azone2/wld.hpp @@ -0,0 +1,84 @@ +#ifndef __OPENEQ_WLD__ +#define __OPENEQ_WLD__ + +#include + +#include "global.hpp" + +#include "file_loader.hpp" + +#include "wld_structs.hpp" + +#include "3d.hpp" + +#define FRAGMENT(name) \ +this->frags[i] = new name(this, buffer, frag->size, frag->nameRef); + +class TexRef { +public: + Texture **tex; + int tex_count; +}; + +class Fragment { +public: + Fragment() {} + virtual ~Fragment() {} + + int type, name; + void *frag; +}; + +class WLDLoader : public FileLoader { +public: + WLDLoader(); + ~WLDLoader(); + + virtual int Open(char *base_path, char *zone_name, Archive *archive); + virtual int Close(); + + int fragcount; + Fragment **frags; + BSP_Node *tree; + + uchar *sHash; + + char old; + + WLDLoader *obj_loader; + WLDLoader *plac_loader; + + char clear_plac; +}; + +#define FRAG_CLASS(name) \ +class name : public Fragment { \ +public: \ + name(WLDLoader *wld, uchar *buf, int len, int frag_name); \ + ~name() {}; \ +} + +#define FRAG_CONSTRUCTOR(name) \ +name::name(WLDLoader *wld, uchar *buf, int len, int frag_name) + +#define FRAG_DECONSTRUCTOR(name) \ +name::~name() + +FRAG_CLASS(Data03); +FRAG_CLASS(Data04); +FRAG_CLASS(Data05); +FRAG_CLASS(Data15); +FRAG_CLASS(Data1B); +FRAG_CLASS(Data1C); +FRAG_CLASS(Data21); +FRAG_CLASS(Data22); +FRAG_CLASS(Data29); +FRAG_CLASS(Data28); +FRAG_CLASS(Data30); +FRAG_CLASS(Data31); +FRAG_CLASS(Data36); + +void DoubleLinkBSP(BSP_Node *tree, long node_number, long parent); +long BSPMarkRegion(BSP_Node *tree, long node_number, long region, int region_type); + +#endif diff --git a/utils/azone2/wld_structs.hpp b/utils/azone2/wld_structs.hpp new file mode 100644 index 000000000..bf8c9fb70 --- /dev/null +++ b/utils/azone2/wld_structs.hpp @@ -0,0 +1,137 @@ +#ifndef __OPENEQ_WLD_STRUCTS__ +#define __OPENEQ_WLD_STRUCTS__ + +#pragma pack(1) + +struct struct_wld_header { + long magic; + long version; + long fragmentCount; + long header3; + long header4; + long stringHashSize; + long header6; +} typedef struct_wld_header; + +struct struct_wld_basic_frag { + long size; + long id; + long nameRef; +} typedef struct_wld_basic_frag; + + +struct struct_Data15 { + uint32 ref, flags, fragment1; + float trans[3], rot[3]; + float scale[3]; + uint32 fragment2, flags2; +} typedef struct_Data15; + +struct struct_Data36 { + long flags; + long fragment1; + long fragment2; + long fragment3; + long fragment4; + float centerX; + float centerY; + float centerZ; + long params2[3]; // 48 + float maxDist; + float minX; + float minY; + float minZ; + float maxX; + float maxY; + float maxZ; // 24 + short int vertexCount; + short int texCoordsCount; + short int normalsCount; + short int colorCount; + short int PolygonsCount; + short int size6; + short int PolygonTexCount; + short int vertexTexCount; + short int size9; + short int scale; // 20 +} typedef struct_Data36; + +struct struct_Data10 { + long flags, size1, fragment; +} typedef struct_Data10; + +struct struct_Data1B { + uint32 flags, params1; + uint32 params3b; + float color[3]; +} typedef struct_Data1B; + +struct struct_Data21 { + float normal[3], splitdistance; + long region, node[2]; +} typedef struct_Data21; + +struct struct_Data22 { + long flags, fragment1, size1, size2, params1, size3, size4, params2, size5, size6; +} typedef struct_Data22; + +struct struct_Data28 { + uint32 flags; + float x, y, z, rad; +} typedef struct_Data28; + +typedef struct struct_Data29 { + long region_count; + long *region_array; + long strlen; + char *str; + int region_type; // We fill this in with -1 for unknown, 1 for water, 2 for lava +} struct_Data29; + + +struct struct_Data30 { + uint32 flags, params1, params2; + float params3[2]; +} typedef struct_Data30; + +typedef struct BSP_Node { + long node_number; + float normal[3], splitdistance; + long region; + int special; + long left, right; +} BSP_Node; + + +struct Vert { + signed short int x, y, z; +} typedef Vert; + +struct TexCoordsNew { + float tx, tz; +} typedef TexCoordsNew; + +struct TexCoordsOld { + signed short int tx, tz; +} typedef TexCoordsOld; + +struct VertexNormal { + signed char nx, ny, nz; +} typedef VertexNormal; + +struct VertexColor { + char color[4]; +} typedef VertexColor; + +struct Poly { + short int flags, v1, v2, v3; +} typedef Poly; + +struct TexMap { + uint16 polycount; + uint16 tex; +} typedef TexMap; + +#pragma pack() + +#endif diff --git a/utils/azone2/zon.cpp b/utils/azone2/zon.cpp new file mode 100644 index 000000000..0f7d43bd3 --- /dev/null +++ b/utils/azone2/zon.cpp @@ -0,0 +1,433 @@ + +// This source is from OpenEQ by Daeken et al. Modified a bit by Derision, some bug fixes etc. + +#include +#include +#include +#include + +#include "3d.hpp" +#include "zon.hpp" +//#define DEBUGEQG +//#define DEBUGPLAC + +ZonLoader::ZonLoader() { + this->buffer = NULL; + this->buf_len = -1; + this->archive = NULL; + this->status = 0; +} + +ZonLoader::~ZonLoader() { + this->Close(); +} + +int ZonLoader::Open(char *base_path, char *zone_name, Archive *archive) { + uchar *buffer, *orig_buffer; + int buf_len; + + Texture **tex; + int tex_count, tex_tmp, *tex_map; + + int i, j, k, l; + char **model_names; + + char *filename; + + uchar *zon_tmp, *zon_orig; + + float rot_change = 180.0f / 3.14159f; + // float rot_change = 1.0f; + float base[3]; + +#ifdef DEBUGEQG + printf("Attempting to load EQG %s\n", zone_name); +#endif + filename = new char[strlen(zone_name) + 5]; + //sprintf(filename, "%s.zon", zone_name); + // Derision: .zon name is not always the same as the EQG base name. Added wildcard ability to the PFS GetFile + // routine. + sprintf(filename, "*.zon"); +#ifdef DEBUGEQG + printf("Looking for ZON file %s\n", filename); + archive->GetFile("*.zon", &buffer, &buf_len); +#endif + if(!archive->GetFile(filename, &buffer, &buf_len)) { +#ifdef DEBUGEQG + printf("Couldn't find ZON file\n"); + printf("%s\n", zone_name); +#endif + // Look in the filesystem for the .ZON file. + char *FSZonName = new char[strlen(zone_name) + 6]; + sprintf(FSZonName, "%s.zon", zone_name); + // Hack incoming for westkorlach zones because the zon filename for e.g. westkorlacha is westkorlachA + if(!strncmp(FSZonName, "westkorlach", 11)) + FSZonName[11] = toupper(FSZonName[11]); + printf(".ZON file not found inside EQG. Looking for %s in filesystem. ", FSZonName); + FILE *FSZon = fopen(FSZonName, "rb"); + + delete [] FSZonName; + + if(FSZon) { + printf("Found.\n"); + fseek(FSZon, 0, SEEK_END); + long FSZonSize = ftell(FSZon); + fseek(FSZon, 0, SEEK_SET); +// printf("File size is %d\n", FSZonSize); + buffer = new uchar[FSZonSize]; + fread(buffer, FSZonSize, 1, FSZon); +// printf("Read file\n"); + } + else { + printf("\n.ZON file not present inside EQG file. Did not find it in the filesystem either.\n"); + return 0; + } + } + delete[] filename; + + orig_buffer = buffer; + + zon_header *hdr = (zon_header *) buffer; + zon_placeable *plac; + + if(hdr->magic[0] != 'E' || hdr->magic[1] != 'Q' || hdr->magic[2] != 'G' || hdr->magic[3] != 'Z') + return 0; + + buffer += sizeof(zon_header); + + // The original OpenEQ code I found assumed the first filename was the .TER file. This + // is not always the case. + // + +#ifdef DEBUGEQG + printf("Seeking .TER file\n"); +#endif + + + + uchar *StartOfModelNames = buffer; + while(strcmp((char *)(buffer+strlen((char *)buffer))-3, "TER")) + buffer += strlen((char *)buffer) + 1; + +#ifdef DEBUGEQG + printf("Found .TER file %s\n", (char *)buffer); +#endif + this->terloader.Open(NULL, (char *) buffer, archive); + buffer = StartOfModelNames; + this->model_data.zone_model = terloader.model_data.zone_model; + + // Derision: + // After the ZON header, there is a list of strings like this (using guildhall.EQG as an example) + // TER_GuildHall.TER + // TER_GuildHall + // OBJ_sun_frame.MOD + // OBJ_sun_frame + // OBJ_chandelier.MOD + // OBJ_chandelier + // OBJ_key_door_open_.MOD + // OBJ_key_door_open_01 + // OBJ_key_door_open_02 + // OBJ_key_door_.MOD + // OBJ_key_door_01 + // OBJ_key_door_02 + // OBJ_key_door_03 + // + // This format appears to be Mesh (model) filename, followed by a list of objects that use that model. + // So we have the zone model (ground mesh), i.e. the .TER file, which is used by object 'TER_Guildhall', + // then a list of placeable models. It would appear that one .MOD file can be used by multiple objects, + // i.e. the key_door_open and key_door models in the example above. + // + // After this list, there is a list of hdr->NumberOfModels longs, one for each Model. Each long is an offset into + // the list above, pointing to the .MOD file for that model. + + zon_orig = buffer; + zon_tmp = buffer + hdr->list_len; + + // Create an array of Model filenames and populate it using the offsets + // + buffer = zon_orig + hdr->list_len; + model_names = new char *[hdr->NumberOfModels]; + for(int ModelNumber=0; ModelNumber< hdr->NumberOfModels; ModelNumber++) { + long offset = *((long *)(buffer+ModelNumber*4)); + model_names[ModelNumber] = (char *)(zon_orig + offset); + // + // Derision: 23/06/08 + // dranikcatacombsa.eqg has a couple of model names with a left parenthesis where there should + // be an underscore. E.g. OBP_DZ_Lbanner0)_00.MOD instead of OBP_DZ_Lbanner0__00.MOD + // This is the only zone I have seen this in, but is the reason for the follow code to replace + // the parenthesis with an underscore. + // + for(unsigned int i=0; imodel_data.model_count = hdr->NumberOfModels; +#if defined(DEBUGEQG) || defined(DEBUGPLAC) + printf("model_data.model_count is %d\n", this->model_data.model_count); + fflush(stdout); +#endif + this->model_data.models = new Model *[this->model_data.model_count]; + + + + // Skip over the + buffer = zon_orig + hdr->list_len + hdr->NumberOfModels * 4; + + this->model_data.plac_count = hdr->obj_count - 1; +/* + if(hdr->version > 1) { + // Don't understand the complete format of version 2 zons + this->model_data.plac_count = 0; + } +*/ + +#ifdef DEBUGPLAC + printf(" Placeable count is %d\n", this->model_data.plac_count); +#endif + this->model_data.placeable = new Placeable *[this->model_data.plac_count]; + + plac = (zon_placeable *) buffer; + base[0] = plac->x; + base[1] = plac->y; + base[2] = plac->z; + +// printf("(%f %f %f) (%f %f %f)\n", plac->x, plac->y, plac->z, plac->rz, plac->ry, plac->rx); + + buffer += sizeof(zon_placeable); + + if(hdr->version > 1) { + long UnknownSize = *((long *)(buffer)); +// printf("Unknown Size is %d\n", UnknownSize); + buffer = buffer + 4 + (UnknownSize * 4); + } + + +#ifdef DEBUGEQG + printf("TER.CPP:107 model_data.plac_count is %d\n", this->model_data.plac_count); +#endif + + + for(i = 0; i < this->model_data.plac_count; ++i) { + plac = (zon_placeable *) buffer; + + zon_tmp = zon_orig + plac->loc; + + this->model_data.placeable[i] = new Placeable; + this->model_data.placeable[i]->model = plac->id ; + this->model_data.placeable[i]->x = plac->x; + this->model_data.placeable[i]->y = plac->y; + this->model_data.placeable[i]->z = plac->z; + this->model_data.placeable[i]->rx = plac->rz * rot_change; + this->model_data.placeable[i]->ry = plac->ry * rot_change; + this->model_data.placeable[i]->rz = plac->rx * rot_change; + + this->model_data.placeable[i]->scale[0] = plac->scale; + this->model_data.placeable[i]->scale[1] = plac->scale; + this->model_data.placeable[i]->scale[2] = plac->scale; +#ifdef DEBUG_MODE + printf("%s (%f %f %f) (%f %f %f) %f\n", + zon_tmp, + this->model_data.placeable[i]->x, + this->model_data.placeable[i]->y, + this->model_data.placeable[i]->z, + this->model_data.placeable[i]->rx, + this->model_data.placeable[i]->ry, + this->model_data.placeable[i]->rz, + this->model_data.placeable[i]->scale); +#endif + if(this->model_data.placeable[i]->model == -1) { + printf("Unable to find model for offset %5ld\n", plac->loc); + } + +#ifdef DEBUGPLAC + printf(" Placeable object %i Model no. %d at (%4.2f, %4.2f, %4.2f)\n", i, this->model_data.placeable[i]->model, plac->x,plac->y, plac->z); +#endif + buffer += sizeof(zon_placeable); + if(hdr->version > 1) { + long UnknownSize = *((long *)(buffer)); + buffer = buffer + 4 + (UnknownSize * 4); + } + + + } + + model_loaders = new TERLoader[this->model_data.model_count]; + + tex_count = this->model_data.zone_model->tex_count; +#ifdef DEBUGEQG + printf("zon.cpp line 154 zm tex count is %d\n", this->model_data.zone_model->tex_count); +#endif + + for(j = 0; j < this->model_data.model_count; ++j) { + + // We have already loaded the zone mesh, so skip the .TER file when processing models. + // + if(!(strcmp(model_names[j]+strlen(model_names[j])-3, "ter"))) { +// printf("Skipping .TER file at model number %d\n", j); + this->model_data.models[j] = NULL; + continue; + } +// printf("Attempting to open MOD file %s\n", model_names[j]); fflush(stdout); + if(model_loaders[j].Open(NULL, model_names[j], archive)) { + + this->model_data.models[j] = new Model; + this->model_data.models[j]->vert_count = model_loaders[j].model_data.zone_model->vert_count; + this->model_data.models[j]->poly_count = model_loaders[j].model_data.zone_model->poly_count; + this->model_data.models[j]->tex_count = model_loaders[j].model_data.zone_model->tex_count; + this->model_data.models[j]->verts = model_loaders[j].model_data.zone_model->verts; + this->model_data.models[j]->polys = model_loaders[j].model_data.zone_model->polys; + this->model_data.models[j]->tex = model_loaders[j].model_data.zone_model->tex; + this->model_data.models[j]->name = new char[strlen(model_names[j])+1]; + strcpy(this->model_data.models[j]->name, model_names[j]); + + tex_tmp = 1; + // I think this is looking to see if the placeable model textures already exist in the zone model. + // + for(i = 0; i < this->model_data.models[j]->tex_count; ++i) { + tex_tmp = 1; // Derision + for(k = 0; k < this->model_data.zone_model->tex_count; ++k) { +// printf(" Checking zm tex filename %s againt model filename %s\n", this->model_data.zone_model->tex[k]->filenames[0], +// this->model_data.models[j]->tex[i]->filenames[0]); + if((!this->model_data.zone_model->tex[k]->filenames[0] && !this->model_data.models[j]->tex[i]->filenames[0]) || + (this->model_data.zone_model->tex[k]->filenames[0] && + this->model_data.models[j]->tex[i]->filenames[0] && + !strcmp(this->model_data.zone_model->tex[k]->filenames[0], this->model_data.models[j]->tex[i]->filenames[0]))) { + tex_tmp = 0; + break; + } + } + if(tex_tmp) + ++tex_count; + else { + for(k = 0; k < j; ++k) { + if(!this->model_data.models[k]) { +#ifdef DEBUGEQG + printf("NULL this->model_data.models[k])\n"); +#endif + continue; + } + for(l = 0; l < this->model_data.models[k]->tex_count; ++l) { + if(this->model_data.models[k]->tex[l]->filenames[0] == this->model_data.models[j]->tex[i]->filenames[0] || + (this->model_data.models[k]->tex[l]->filenames[0] && + this->model_data.models[j]->tex[i]->filenames[0] && + !strcmp(this->model_data.models[k]->tex[l]->filenames[0], this->model_data.models[j]->tex[i]->filenames[0]))) { + tex_tmp = 0; + break; + } + } + } + if(tex_tmp) + ++tex_count; + } + } + } + else this->model_data.models[j] = NULL; + } + +// printf("Tex count is %i %X\n", tex_count, tex_count); + // Allocate a new Texture array + tex_count = 0; + tex_tmp = 1; + // Set the first tex_count textures in the new Texture array to the textures from the zone model + // For i in each model + for(i = 0; i < this->model_data.model_count; ++i) { + if(!this->model_data.models[i]) { +#ifdef DEBUGEQG + printf("NULL this->model_data.models[i])\n"); +#endif + continue; + } + // For j = each texture in this model + for(j = 0; j < this->model_data.models[i]->tex_count; ++j) { + tex_tmp = 1; // Derision + // for k = each texture in the zone model + for(k = 0; k < tex_count; ++k) { + // if the texture filenames are at the same address or, the texture filenames are the same, we set tex_tmp to 0 + if(tex[k]->filenames[0] == this->model_data.models[i]->tex[j]->filenames[0] || + (tex[k]->filenames[0] && + this->model_data.models[i]->tex[j]->filenames[0] && + !strcmp(tex[k]->filenames[0], this->model_data.models[i]->tex[j]->filenames[0]))) { + tex_tmp = 0; + break; + } + } + // if tex_tmp is 0, i.e. this texture is not in the zone model, we add this texture to our new Texture array, at the end + if(tex_tmp) { + tex[tex_count] = this->model_data.models[i]->tex[j]; + ++tex_count; + } + } + } + for(i = 0; i < this->model_data.model_count; ++i) { + if(!this->model_data.models[i]) { +#ifdef DEBUGEQG + printf(">NULL this->model_data.models[i])\n"); +#endif + continue; + } + tex_map = new int[this->model_data.models[i]->tex_count]; + + for(j = 0; j < this->model_data.models[i]->tex_count; ++j) { + tex_map[j] = 0; + for(k = 0; k < tex_count; ++k) { + if(tex[k]->filenames[0] == this->model_data.models[i]->tex[j]->filenames[0] || + (tex[k]->filenames[0] && + this->model_data.models[i]->tex[j]->filenames[0] && + !strcmp(tex[k]->filenames[0], this->model_data.models[i]->tex[j]->filenames[0]))) { + tex_map[j] = k; + break; + } + } + } + + delete[] tex_map; + } + + delete [] model_names; + + this->model_data.zone_model->tex = 0; + this->model_data.zone_model->tex_count = 0; + delete [] orig_buffer; + + this->status = 1; + return 1; +} + + +int ZonLoader::Close() { + + int i; + + if(!this->status) + return 1; + + this->terloader.Close(); + + for(i = 0; i < this->model_data.model_count; ++i) + this->model_loaders[i].Close(); + + for(i = 0; i < this->model_data.model_count; ++i) + { + if(this->model_data.models[i]) + { + delete [] this->model_data.models[i]->name; + delete this->model_data.models[i]; + } + } + + for(i = 0; i < this->model_data.plac_count; ++i) + delete this->model_data.placeable[i]; + + delete [] this->model_data.placeable; + + delete [] this->model_data.models; + + delete[] this->model_loaders; + + status = 0; + + return 1; +} diff --git a/utils/azone2/zon.hpp b/utils/azone2/zon.hpp new file mode 100644 index 000000000..876bdfc4e --- /dev/null +++ b/utils/azone2/zon.hpp @@ -0,0 +1,47 @@ +#ifndef __OPENEQ_ZON__ +#define __OPENEQ_ZON__ + +#include "ter.hpp" + +/* Big thanks to jbb on the ZON stuff! */ + +#pragma pack(1) + +struct zon_header { + char magic[4]; // Constant at EQGZ + long version; // Constant at 2 + long list_len; // Length of the list to follow. + long NumberOfModels; + long obj_count; // Placeable object count. + long unk[2]; // Unknown. +} typedef zon_header; + +struct zon_placeable { + long id; + long loc; + float x, y, z; + float rx, ry, rz; + float scale; +} typedef zon_placeable; + +struct zon_object { + long offset; + long id; +} typedef zon_object; + +#pragma pack() + +class ZonLoader : public FileLoader { +public: + ZonLoader(); + ~ZonLoader(); + + virtual int Open(char *base_path, char *zone_name, Archive *archive); + virtual int Close(); + +private: + TERLoader terloader; + TERLoader *model_loaders; +}; + +#endif diff --git a/utils/azone2/zonv4.cpp b/utils/azone2/zonv4.cpp new file mode 100644 index 000000000..71a6d258f --- /dev/null +++ b/utils/azone2/zonv4.cpp @@ -0,0 +1,255 @@ + +#include +#include +#include +#include + +#include "3d.hpp" +#include "zonv4.hpp" +//#define DEBUGEQG +//#define DEBUGPLAC + +string GetToken(uchar *&Buffer, int &Position); + +Zonv4Loader::Zonv4Loader() +{ + this->buffer = NULL; + this->buf_len = -1; + this->archive = NULL; + this->status = 0; +} + +Zonv4Loader::~Zonv4Loader() +{ + this->Close(); +} + +int Zonv4Loader::Open(char *base_path, char *zone_name, Archive *archive) +{ + + uchar *buffer; + int position; + int buf_len; + + Texture **tex; + int tex_count, tex_tmp, *tex_map; + + int i, j, k, l; + char **model_names; + + char *filename; + + uchar *zon_tmp, *zon_orig; + + float rot_change = 180.0f / 3.14159f; + + float base[3]; + + printf("Attempting to load EQG %s\n", zone_name); + + filename = new char[strlen(zone_name) + 5]; + + sprintf(filename, "*.zon"); +#ifdef DEBUGEQG + printf("Looking for ZON file %s\n", filename); + archive->GetFile("*.zon", &buffer, &buf_len); +#endif + if(!archive->GetFile(filename, &buffer, &buf_len)) + { + printf("Couldn't find ZON file %s\n", zone_name); + + return 0; + } + delete[] filename; + + zonv4_header *hdr = (zonv4_header *) buffer; + + zonv4_placeable *plac; + + if(hdr->magic[0] != 'E' || hdr->magic[1] != 'Q' || hdr->magic[2] != 'T' || hdr->magic[3] != 'Z') + return 0; +#ifdef DEBUGEQG + printf("Found V4 EQG.\n"); fflush(stdout); + + printf("Seeking .DAT file\n"); +#endif + buffer += sizeof(zonv4_header); + + position = 0; + + char ZonName[255]; + + while(position < buf_len) + { + string Token = GetToken(buffer, position); + + if(Token == "*NAME") + { + Token = GetToken(buffer, position); +#ifdef DEBUGEQG + printf(".zon name is %s\n", Token.c_str()); +#endif + sprintf(ZonName, "%s.dat", Token.c_str()); + break; + } + } + + this->datloader.Open(NULL, ZonName, archive); + + this->model_data.zone_model = datloader.model_data.zone_model; + + model_loaders = new TERLoader[this->datloader.model_data.ModelNames.size() + 1]; + + this->model_data.models = new Model *[this->datloader.model_data.ModelNames.size() + 1]; + + this->model_data.placeable = new Placeable *[this->datloader.model_data.PlaceableList.size()]; + + for(unsigned int i = 0; i < this->datloader.model_data.ModelNames.size(); ++i) + { + char tmp[200]; + //printf("Opening %s.mod\n", this->datloader.model_data.ModelNames[i].c_str()); + sprintf(tmp, "%s.mod", this->datloader.model_data.ModelNames[i].c_str()); + + char *str = tmp; + + while(*str) { + if(*str >= 'A' && *str <= 'Z') + *str += 'a' - 'A'; + ++str; + } + + if(model_loaders[i].Open(NULL, tmp, archive)) + { + this->model_data.models[i] = new Model; + this->model_data.models[i]->vert_count = model_loaders[i].model_data.zone_model->vert_count; + this->model_data.models[i]->poly_count = model_loaders[i].model_data.zone_model->poly_count; + this->model_data.models[i]->tex_count = model_loaders[i].model_data.zone_model->tex_count; + this->model_data.models[i]->verts = model_loaders[i].model_data.zone_model->verts; + this->model_data.models[i]->polys = model_loaders[i].model_data.zone_model->polys; + this->model_data.models[i]->tex = model_loaders[i].model_data.zone_model->tex; + this->model_data.models[i]->name = new char[100]; + strcpy(this->model_data.models[i]->name, tmp); + + this->model_data.models[i]->IncludeInMap = true; + + // Attempt to cut down on the .map file size by defaulting some objects to not be included. The user can + // always change this in the azone.ini + // + // For example, taking out all the tak_braziers from elddar saves around 30MB + // + if(strstr(tmp, "tree") || strstr(tmp, "pine") || strstr(tmp, "palm") || strstr(tmp, "rock") || + strstr(tmp, "shrub") || strstr(tmp, "fern") || strstr(tmp, "bamboo") || strstr(tmp, "coral") || + strstr(tmp, "camp_bones") || strstr(tmp, "sponge") || strstr(tmp, "plant") || strstr(tmp, "shortplm") || + strstr(tmp, "tak_brazier") || strstr(tmp, "tak_banner") || strstr(tmp, "fung") || strstr(tmp, "bolete") || + strstr(tmp, "amanita") || strstr(tmp, "leopita")) + { + if(!strstr(tmp, "arch")) + { + this->model_data.models[i]->IncludeInMap = false; + } + } + } + else + { + printf("Unable to open model %s, but continuing.\n", tmp); + this->model_data.models[i] = new Model; + this->model_data.models[i]->IncludeInMap = false; + } + } + +#ifdef DEBUGPLAC + printf("Placeable list:\n"); + + for(int i = 0; i < this->datloader.model_data.ObjectGroups.size(); ++i) + { + printf("ObjectGroup: %i\n", i); + printf(" XYZ: %8.3f, %8.3f, %8.3f Tile: (%8.3f, %8.3f, %8.3f) Rots: (%8.3f, %8.3f, %8.3f) Scale: %8.3f\n", + this->datloader.model_data.ObjectGroups[i].x, + this->datloader.model_data.ObjectGroups[i].y, + this->datloader.model_data.ObjectGroups[i].z, + this->datloader.model_data.ObjectGroups[i].TileX, + this->datloader.model_data.ObjectGroups[i].TileY, + this->datloader.model_data.ObjectGroups[i].TileZ, + this->datloader.model_data.ObjectGroups[i].RotX, + this->datloader.model_data.ObjectGroups[i].RotY, + this->datloader.model_data.ObjectGroups[i].RotZ, + this->datloader.model_data.ObjectGroups[i].ScaleX); + + list::iterator ModelIterator; + + ModelIterator = this->datloader.model_data.ObjectGroups[i].SubObjects.begin(); + + while(ModelIterator != this->datloader.model_data.ObjectGroups[i].SubObjects.end()) + { + printf(" Uses Placeable: %i\n", (*ModelIterator)); + printf(" %s\n", + this->datloader.model_data.ModelNames[this->datloader.model_data.PlaceableList[(*ModelIterator)].model].c_str()); + printf(" Model: %3i XYZ: (%8.3f, %8.3f, %8.3f) ROT: (%8.3f, %8.3f, %8.3f) SCALE: (%8.3f, %8.3f, %8.3f)\n", + this->datloader.model_data.PlaceableList[(*ModelIterator)].model, + this->datloader.model_data.PlaceableList[(*ModelIterator)].x, + this->datloader.model_data.PlaceableList[(*ModelIterator)].y, + this->datloader.model_data.PlaceableList[(*ModelIterator)].z, + this->datloader.model_data.PlaceableList[(*ModelIterator)].rx, + this->datloader.model_data.PlaceableList[(*ModelIterator)].ry, + this->datloader.model_data.PlaceableList[(*ModelIterator)].rz, + this->datloader.model_data.PlaceableList[(*ModelIterator)].scale[0], + this->datloader.model_data.PlaceableList[(*ModelIterator)].scale[1], + this->datloader.model_data.PlaceableList[(*ModelIterator)].scale[2]); + + + ++ModelIterator; + } + } +#endif + + for(unsigned int i = 0; i < this->datloader.model_data.PlaceableList.size(); ++i) + { + this->model_data.placeable[i] = new Placeable; + + this->model_data.placeable[i]->model = this->datloader.model_data.PlaceableList[i].model; + + this->model_data.placeable[i]->x = this->datloader.model_data.PlaceableList[i].x; + this->model_data.placeable[i]->y = this->datloader.model_data.PlaceableList[i].y; + this->model_data.placeable[i]->z = this->datloader.model_data.PlaceableList[i].z; + + this->model_data.placeable[i]->rx = this->datloader.model_data.PlaceableList[i].rx; + this->model_data.placeable[i]->ry = this->datloader.model_data.PlaceableList[i].ry; + this->model_data.placeable[i]->rz = this->datloader.model_data.PlaceableList[i].rz; + + this->model_data.placeable[i]->scale[0] = this->datloader.model_data.PlaceableList[i].scale[0]; + this->model_data.placeable[i]->scale[1] = this->datloader.model_data.PlaceableList[i].scale[1]; + this->model_data.placeable[i]->scale[2] = this->datloader.model_data.PlaceableList[i].scale[2]; + } + + this->model_data.ObjectGroups = this->datloader.model_data.ObjectGroups; + + this->model_data.plac_count = this->datloader.model_data.PlaceableList.size(); + + this->model_data.model_count = this->datloader.model_data.ModelNames.size(); + + this->status = 1; + + return 1; +} + + +int Zonv4Loader::Close() +{ + int i; + + if(!this->status) + return 1; + + //TODO: Free up all the memory we used + + this->datloader.Close(); + + for(i = 0; i < this->model_data.model_count; ++i) + this->model_loaders[i].Close(); + + delete[] this->model_loaders; + + this->status = 0; + + return 1; +} diff --git a/utils/azone2/zonv4.hpp b/utils/azone2/zonv4.hpp new file mode 100644 index 000000000..c97b3bbd5 --- /dev/null +++ b/utils/azone2/zonv4.hpp @@ -0,0 +1,43 @@ +#ifndef __OPENEQ_ZONV4__ +#define __OPENEQ_ZONV4__ + +#include "dat.hpp" +#include "ter.hpp" + +/* Big thanks to jbb on the ZON stuff! */ + +#pragma pack(1) + +struct zonv4_header { + char magic[4]; // Constant at EQGZ +} typedef zonv4_header; + +struct zonv4_placeable { + long id; + long loc; + float x, y, z; + float rx, ry, rz; + float scale; +} typedef zonv4_placeable; + +struct zonv4_object { + long offset; + long id; +} typedef zonv4_object; + +#pragma pack() + +class Zonv4Loader : public FileLoader { +public: + Zonv4Loader(); + ~Zonv4Loader(); + + virtual int Open(char *base_path, char *zone_name, Archive *archive); + virtual int Close(); + +private: + DATLoader datloader; + TERLoader *model_loaders; +}; + +#endif diff --git a/utils/cleanipc.cpp b/utils/cleanipc.cpp new file mode 100644 index 000000000..b8681853f --- /dev/null +++ b/utils/cleanipc.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include + +#include + +int main(int argc, char** argv) { + using std::cout; + using std::endl; + + key_t shm_key; + shmid_ds* memuser_ds = new shmid_ds; + + bool print = false; + if (argc >= 2) { + print = true; + } + + for ( int i = 0; i < 10; i++) { + memuser_ds->shm_nattch = 0; + const char *n = "ERROR"; + switch (i) { + case 0: shm_key = ftok(".", 'I'); n="items"; break; + case 1: shm_key = ftok(".", 'N'); n="unused"/*npc_types*/; break; + case 2: shm_key = ftok(".", 'D'); n="unused"/*doors*/; break; + case 3: shm_key = ftok(".", 'S'); n="spells"; break; + case 4: shm_key = ftok(".", 'F'); n="factions"; break; + case 5: shm_key = ftok(".", 'L'); n="loot"; break; + case 6: shm_key = ftok(".", 'M'); n="unused"/*??*/; break; + case 7: shm_key = ftok(".", 'O'); n="opcodes"; break; + case 8: shm_key = ftok(".", 'Z'); n="unused"/*item serialization*/; break; + case 9: shm_key = ftok(".", 'K'); n="skillcaps"; break; + default: break; + } + int semid = semget(shm_key, 1, 0); + if(print){ + cout<<"ID="<shm_nattch > 0) { + cout << "Something is still attached to " << shm_key << " (" << n << ")" << endl; + } else { + cout << "Successfully removed " << shm_key << " (" << n << ")" << endl; + } + if(print){ + cout<<" useing_shm="; + cout<shm_nattch; + } + if (memuser_ds->shm_nattch == 0) { + if(print){ + cout<<" Deleteing stale shares"< + + + setme + I Forgot To Edit My Config + + diff --git a/utils/defaults/eqemu_config.xml.full b/utils/defaults/eqemu_config.xml.full new file mode 100644 index 000000000..74065a510 --- /dev/null +++ b/utils/defaults/eqemu_config.xml.full @@ -0,0 +1,90 @@ + + + + setme + I Forgot To Edit My Config + + + + + + + + eqemulator.net + 5998 + + + + + + + + + + + + + some long random string + + + + + + + + channels.eqemulator.net + 7778 + + + + + channels.eqemulator.net + 7778 + + + + 20 + + + + + + + + 127.0.0.1 + 3306 + eq + eq + eq + + + + 127.0.0.1 + 3306 + eq + eq + eq + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/utils/defaults/log.ini b/utils/defaults/log.ini new file mode 100644 index 000000000..e69de29bb diff --git a/utils/defaults/logs/.keep b/utils/defaults/logs/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/utils/defaults/mime.types b/utils/defaults/mime.types new file mode 100644 index 000000000..e0ef69119 --- /dev/null +++ b/utils/defaults/mime.types @@ -0,0 +1,26 @@ +# example mime.types file. +# see the NCSA X Mosaic documentation at +# http://www.ncsa.uiuc.edu/SDG/Software/Mosaic/Docs/extension-map.html +# for more details + +application/postscript ai eps ps +application/rtf rtf +application/x-tex tex +application/x-texinfo texinfo texi +application/x-troff t tr roff +audio/basic au snd +audio/x-aiff aif aiff aifc +audio/x-wav wav +image/gif gif +image/ief ief +image/jpeg jpeg jpg jpe +image/tiff tiff tif +image/x-xwindowdump xwd +text/html html +text/plain txt c cc h +video/mpeg mpeg mpg mpe +video/quicktime qt mov +video/x-msvideo avi +video/x-sgi-movie movie +text/css css +image/png png diff --git a/utils/defaults/plugin.pl b/utils/defaults/plugin.pl new file mode 100644 index 000000000..e69de29bb diff --git a/utils/defaults/plugins/.keep b/utils/defaults/plugins/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/utils/defaults/plugins/check_handin.pl b/utils/defaults/plugins/check_handin.pl new file mode 100644 index 000000000..57da2fd73 --- /dev/null +++ b/utils/defaults/plugins/check_handin.pl @@ -0,0 +1,78 @@ +# plugin::check_handin($item1 => #required_amount,...); +# autoreturns extra unused items on success +sub check_handin { + my $hashref = shift; + my %required = @_; + foreach my $req (keys %required) { + if ((!defined $hashref->{$req}) || ($hashref->{$req} != $required{$req})) { + return(0); + } + } + foreach my $req (keys %required) { + if ($required{$req} < $hashref->{$req}) { + $hashref->{$req} -= $required{$req}; + } else { + delete $hashref->{$req}; + } + } + quest::clearhandin(); + return 1; +} + +sub check_mq_handin { + my $hashref = shift; + my %required = @_; + quest::resethandin(); + foreach my $req (keys %required) { + $charges = $required{$req}; + if ( !quest::handleturnin($req,$charges) ) + { + return(0); + } + } + quest::completehandin(); + return 1; +} + +sub return_items { + my $hashref = plugin::var('$itemcount'); + my $client = plugin::val('$client'); + my $items_returned = 0; + + my %ItemHash = ( + 0 => [ plugin::val('$item1'), plugin::val('$item1_charges'), plugin::val('$item1_attuned') ], + 1 => [ plugin::val('$item2'), plugin::val('$item2_charges'), plugin::val('$item2_attuned') ], + 2 => [ plugin::val('$item3'), plugin::val('$item3_charges'), plugin::val('$item3_attuned') ], + 3 => [ plugin::val('$item4'), plugin::val('$item4_charges'), plugin::val('$item4_attuned') ], + ); + + foreach my $k (keys(%{$hashref})) + { + next if($k == 0); + my $rcount = $hashref->{$k}; + my $r; + for ($r = 0; $r < 4; $r++) + { + if ($rcount > 0 && $ItemHash{$r}[0] && $ItemHash{$r}[0] == $k) + { + if ($client) + { + $client->SummonItem($k, $ItemHash{$r}[1], $ItemHash{$r}[2]); + $items_returned = 1; + } + else + { + # This shouldn't be needed, but just in case + quest::summonitem($k, 0); + $items_returned = 1; + } + $rcount--; + } + } + delete $hashref->{$k}; + } + quest::clearhandin(); + # Return true if items were returned + return $items_returned; + +} \ No newline at end of file diff --git a/utils/defaults/quests/.keep b/utils/defaults/quests/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/utils/defaults/templates/TMimages/branch.gif b/utils/defaults/templates/TMimages/branch.gif new file mode 100644 index 0000000000000000000000000000000000000000..32e0f683d809fbb8b5d424825d3943e8573ff133 GIT binary patch literal 70 zcmZ?wbhEHb6k!lyX!y@?;J|^FrWVDYEQ}05paUX6G7e0#E&VG`zvW*%XUi?`!&j$; V&(Cde>r<;(FiC3TgxMkt)&LAK7=Zu) literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/TMimages/branchbottom.gif b/utils/defaults/templates/TMimages/branchbottom.gif new file mode 100644 index 0000000000000000000000000000000000000000..55ceb453fbaf8693dab2d5ea36c00c555681e471 GIT binary patch literal 72 zcmZ?wbhEHb6k!lyX!y@?;J|^FrWVDYEQ}05paUX6G7d~qE&VG`zvW*%XUi?`!&j$; X&(Cde>r?Akx+;W$!FpK;BZD;nIK3Dv literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/TMimages/branchtop.gif b/utils/defaults/templates/TMimages/branchtop.gif new file mode 100644 index 0000000000000000000000000000000000000000..f6bd127b420b37b02d802f070ce04a5653ca395f GIT binary patch literal 67 zcmZ?wbhEHb6k!lyXkcJCaNs~oQ;XtH7Dfgj&;b!383!h*mj0Ee-|{b>v*lLz=6f-$ SA6g&$s0@!>IOUcIgEat`5EqL8 literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/TMimages/folder.gif b/utils/defaults/templates/TMimages/folder.gif new file mode 100644 index 0000000000000000000000000000000000000000..842989f2ecb79897d03f79408d8400986a0847dc GIT binary patch literal 922 zcmZ?wbhEHb6k!ly_|CwfsHoV|(z0}^;1Tj^%A_kn o{o+FJOu33xTO2~>%5Sbq(GqC5zCK~Un~e9iH8(dhGBQ{L0ML{t=l}o! literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/TMimages/line.gif b/utils/defaults/templates/TMimages/line.gif new file mode 100644 index 0000000000000000000000000000000000000000..a567ef9e53e0c2945efa03c306efc814953308f9 GIT binary patch literal 63 zcmZ?wbhEHb6k!lyX!y@?;J|^FrWVDYEQ}05paUX6G7d~)E&VG`zvW*%XUi?`!&j$; O&(BRVuM1>kum%8ObQHA! literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/TMimages/linebottom.gif b/utils/defaults/templates/TMimages/linebottom.gif new file mode 100644 index 0000000000000000000000000000000000000000..a960f1ff06cc48b0a39e940cf563233d50ada6c6 GIT binary patch literal 58 zcmZ?wbhEHb6k!lyX!y@?;J^U}1_s5SEQ~;kK?g*DWE_|TTl!a?e#^gj&X!xWA8!W`MG0zTbf#`=WJzQVE7LPKn5Hr z{$ycfVBluZ0f~UjU|{hmIO(~1?ekY>3;#cu(z>K|j>acNuh&{y$M<;&K|j>acNuh&{y$Mb|E2i_)aUKyO}qc>Zb!5RQV%Rngr literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/TMimages/minustop.gif b/utils/defaults/templates/TMimages/minustop.gif new file mode 100644 index 0000000000000000000000000000000000000000..668c6625c9c55229677f8866d439f703c8b0310c GIT binary patch literal 151 zcmZ?wbhEHb6k!ly*v!q)($w<&-nH-Fzn?p{^xCy!??K@Cxnp~)=WJzQVE7LPKn5Hr z{$ycfVBluZ0f~UjU|?}6IO(~1uf^-LyZ=9E?NK?T=(S2~S=+j<3uzq*w_OdAJtqFK pzsqExBc!^wDN#~j*Q^$ehZ}?i*>2>7hDf#Ub2a#}qmzNb8UWjNKhFRF literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/TMimages/plus.gif b/utils/defaults/templates/TMimages/plus.gif new file mode 100644 index 0000000000000000000000000000000000000000..070ba2abab6e818adaf3de4ae8b8960521c4b169 GIT binary patch literal 159 zcmZ?wbhEHb6k!ly*v!pv?%2}j_pW{a{{7muWA8!W`MG0zTbf#`=WJzQVE7LPKn5Hr z{$ycfVBluZ0f~UjU|{hnIO(~1?ekY>3;#cu(z>K|j>acNuh&{y$G3TEACXpi)9Jus x!P_)JL#(m8$H-Drh^y0yL31{rP;;YVg(DBc1B30et)`w++7e~-%7uZ!8UV8AKmY&$ literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/TMimages/plusbottom.gif b/utils/defaults/templates/TMimages/plusbottom.gif new file mode 100644 index 0000000000000000000000000000000000000000..b45fe774e2f9238d51290417aedd3f9a19f498f0 GIT binary patch literal 156 zcmZ?wbhEHb6k!ly*v!q)($w<&-nDbbmVW>K{o1u-??K@Cxnp~)=WJzQVE7LPKn5Hr z{$ycfVBluZ0f~UjU|{hqIO(~1?Sofm3;#cu(z>K|j>acNuh&{y$G3S}ACWeCb25O% uf_Kpb4Y9`V9wSRhF&<852F=+*LM(?AD;xz_9vEz&Z8i1Wi!D}64AubT{y!rC literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/TMimages/plustop.gif b/utils/defaults/templates/TMimages/plustop.gif new file mode 100644 index 0000000000000000000000000000000000000000..182c0629f7c68e4411bbe4cf3082f951081f5468 GIT binary patch literal 154 zcmZ?wbhEHb6k!ly*v!q)($w<&-nDbbmVW>K{o1u-??K@Cxnp~)=WJzQVE7LPKn5Hr z{$ycfVBluZ0f~UjU|?}CIO(~1uf^-LyZ=9E?NK?T=(S2~S=%yCt1Xg7cTNVdn6!y> sCQR7QAky!4a(#k_1e1&SlnDv~e71s)2i~T+E)C46QJFk>HV1<>07yJObN~PV literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/TreeMenu.css b/utils/defaults/templates/TreeMenu.css new file mode 100644 index 000000000..974be6379 --- /dev/null +++ b/utils/defaults/templates/TreeMenu.css @@ -0,0 +1,60 @@ +/* TreeMenu.css + A component of HTML_TreeMenu as extended by Chip Chapin + 2002-10-31 Chip Chapin +*/ + +/******************************************************************* +* HTML_TreeMenuXL style entries +* The following entries are used by HTML_TreeMenuXL +* See http://www.chipchapin.com/WebTools/MenuTools/HTML_TreeMenuXL/ +********************************************************************/ +.tmenu0text { /* Normal paragraph font */ + font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif; + font-size: 13px; /* x-small, 80% */ + font-weight: bold; +} +.tmenu1text { /* smalltext */ + font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif; + font-size: 11px; /* xx-small, 70% */ +} +.tmenu2text { /* smallitalic */ + font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif; + font-size: 11px; + font-style: italic; +} +.tmenu3text { /* xsmalltext */ + font-family: Arial, Tahoma, sans-serif; + font-size: 10px; /* xx-small, 55%, 63%; for Arial */ + font-style: normal; +} + +/* Since all menu items are links, the following can be equally important + * to your menu appearance. + * The main thing you may want to change are the A:link and A:visited colors. + */ +*.tmenu0text A:link,*.tmenu1text A:link,*.tmenu2text A:link,*.tmenu3text A:link + { text-decoration:none; color:#505080 } +*.tmenu0text A:visited,*.tmenu1text A:visited,*.tmenu2text A:visited,*.tmenu3text A:visited + { text-decoration:none; color:#505080 } +*.tmenu0text A:active,*.tmenu1text A:active,*.tmenu2text A:active,*.tmenu3text A:active + { text-decoration:none; color:#805050 } +*.tmenu0text A:hover,*.tmenu1text A:hover,*.tmenu2text A:hover,*.tmenu3text A:hover + { text-decoration:underline; color:#FF0000 } + + +/* .tmlistbox controls the appearance of Listbox menus */ +.tmlistbox { + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 11px; /* match 'smalltext' value */ + font-size-adjust: 0.58; /* Verdana */ + margin-bottom: 0px; +} + +/* .tmenuSelected is used with linkSelectKey to highlight selected items */ +.tmenuSelected { + background-color: yellow; +} +*.tmenuSelected A:link { text-decoration:none; color:#2020ff } +*.tmenuSelected A:visited { text-decoration:none; color:#2020ff } +*.tmenuSelected A:active { text-decoration:none; color:#ff2020 } +*.tmenuSelected A:hover { text-decoration:underline; color:#FF0000 } diff --git a/utils/defaults/templates/TreeMenu.js b/utils/defaults/templates/TreeMenu.js new file mode 100644 index 000000000..90171c157 --- /dev/null +++ b/utils/defaults/templates/TreeMenu.js @@ -0,0 +1,636 @@ +// cc 2002-11-12 modifed for HTML_TreeMenuXL + +// +-----------------------------------------------------------------------+ +// | Copyright (c) 2002, Richard Heyes, Harald Radi | +// | All rights reserved. | +// | | +// | Redistribution and use in source and binary forms, with or without | +// | modification, are permitted provided that the following conditions | +// | are met: | +// | | +// | o Redistributions of source code must retain the above copyright | +// | notice, this list of conditions and the following disclaimer. | +// | o Redistributions in binary form must reproduce the above copyright | +// | notice, this list of conditions and the following disclaimer in the | +// | documentation and/or other materials provided with the distribution.| +// | o The names of the authors may not be used to endorse or promote | +// | products derived from this software without specific prior written | +// | permission. | +// | | +// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | +// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | +// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | +// | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | +// | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | +// | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | +// | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | +// | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | +// | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | +// | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | +// | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | +// | | +// +-----------------------------------------------------------------------+ +// | Author: Richard Heyes | +// | Harald Radi | +// +-----------------------------------------------------------------------+ +// +// $Id: TreeMenu.js,v 1.8 2002/11/10 18:16:19 richard Exp $ + + +/** +* TreeMenu class +*/ + function TreeMenu(iconpath, myname, linkTarget, defaultClass, usePersistence) + { + // Properties + this.iconpath = iconpath; + this.myname = myname; + this.linkTarget = linkTarget; + this.defaultClass = defaultClass; + this.usePersistence = usePersistence; + this.imgWidth = 20; + this.imgHeight = 20; + this.n = new Array(); + + this.branches = new Array(); + this.branchStatus = new Array(); + this.layerRelations = new Array(); + this.childParents = new Array(); + this.cookieStatuses = new Array(); + + // cc 2002-11-12 + this.style = false; + this.lineImageWidth = this.imgWidth; + this.lineImageHeight= this.imgHeight; + this.iconImageWidth = this.imgWidth; + this.iconImageHeight= this.imgHeight; + + this.preloadImages(); + } + +/** +* Adds a node to the tree +*/ + TreeMenu.prototype.addItem = function (newNode) + { + newIndex = this.n.length; + newNode.iconImageWidth = this.imgWidth; + newNode.iconImageHeight = this.imgHeight; + newNode.lineImageWidth = this.imgWidth; + newNode.lineImageHeight = this.imgHeight; + this.n[newIndex] = newNode; + + return this.n[newIndex]; + } + + TreeMenu.prototype.SetImageSize = function(w, h) { + this.imgWidth = w; + this.imgHeight = h; + } + +/** +* Preload images hack for Mozilla +*/ + TreeMenu.prototype.preloadImages = function () + { + var plustop = new Image; plustop.src = this.iconpath + '/plustop.gif'; + var plusbottom = new Image; plusbottom.src = this.iconpath + '/plusbottom.gif'; + var plus = new Image; plus.src = this.iconpath + '/plus.gif'; + + var minustop = new Image; minustop.src = this.iconpath + '/minustop.gif'; + var minusbottom = new Image; minusbottom.src = this.iconpath + '/minusbottom.gif'; + var minus = new Image; minus.src = this.iconpath + '/minus.gif'; + + var branchtop = new Image; branchtop.src = this.iconpath + '/branchtop.gif'; + var branchbottom = new Image; branchbottom.src = this.iconpath + '/branchbottom.gif'; + var branch = new Image; branch.src = this.iconpath + '/branch.gif'; + + var linebottom = new Image; linebottom.src = this.iconpath + '/linebottom.gif'; + var line = new Image; line.src = this.iconpath + '/line.gif'; + } + +/** +* Main function that draws the menu and assigns it +* to the layer (or document.write()s it) +*/ + TreeMenu.prototype.drawMenu = function ()// OPTIONAL ARGS: nodes = [], level = [], prepend = '', expanded = false, visbility = 'inline', parentLayerID = null + { + /** + * Necessary variables + */ + var output = ''; + var modifier = ''; + var layerID = ''; + var parentLayerID = ''; + + /** + * Parse any optional arguments + */ + var nodes = arguments[0] ? arguments[0] : this.n + var level = arguments[1] ? arguments[1] : []; + var prepend = arguments[2] ? arguments[2] : ''; + var expanded = arguments[3] ? arguments[3] : false; + var visibility = arguments[4] ? arguments[4] : 'inline'; + var parentLayerID = arguments[5] ? arguments[5] : null; + + var currentlevel = level.length; + + for (var i=0; i 1 ? "top" : 'single'; + } else if(i == (nodes.length-1)) { + modifier = "bottom"; + } else { + modifier = ""; + } + + /** + * Single root branch is always expanded + */ + if (!this.doesMenu() || (parentLayerID == null && nodes.length == 1)) { + expanded = true; + + } else if (nodes[i].expanded) { + expanded = true; + + } else { + expanded = false; + } + + /** + * Make sure visibility is correct based on parent status + */ + visibility = this.checkParentVisibility(layerID) ? visibility : 'none'; + + /** + * Setup branch status and build an indexed array + * of branch layer ids + */ + if (nodes[i].n.length > 0) { + this.branchStatus[layerID] = expanded; + this.branches[this.branches.length] = layerID; + } + + /** + * Setup toggle relationship + */ + if (!this.layerRelations[parentLayerID]) { + this.layerRelations[parentLayerID] = new Array(); + } + this.layerRelations[parentLayerID][this.layerRelations[parentLayerID].length] = layerID; + + /** + * Branch images + */ + var gifname = nodes[i].n.length && this.doesMenu() && nodes[i].isDynamic ? (expanded ? 'minus' : 'plus') : 'branch'; + // cc 2002-11-12 variable image dimensions to line below + var iconimg = nodes[i].icon ? this.stringFormat('', this.iconpath, nodes[i].icon, nodes[i].iconImageWidth, nodes[i].iconImageHeight) : ''; + + + /** + * Build the html to write to the document + * IMPORTANT: + * document.write()ing the string: '
', layerID, visibility, (nodes[i].cssClass ? nodes[i].cssClass : this.defaultClass)) : this.stringFormat('
', ''); + var onMDown = this.doesMenu() && nodes[i].n.length && nodes[i].isDynamic ? this.stringFormat('onmousedown="{0}.toggleBranch(\'{1}\', true)" style="cursor: pointer; cursor: hand"', this.myname, layerID) : ''; + // cc 2002-11-12 variable image dimensions to line below + var imgTag = this.stringFormat('', this.iconpath, gifname, modifier, layerID, onMDown, nodes[i].lineImageWidth, nodes[i].lineImageHeight); + var linkStart = nodes[i].link ? this.stringFormat('', nodes[i].link, this.linkTarget) : ''; + var linkEnd = nodes[i].link ? '' : ''; + + + // cc 2002-11-12 All these are additions + var selectedStart = nodes[i].selected ? "" : ''; + var selectedEnd = nodes[i].selected ? '' : ''; + var nobrStart = this.brOK ? '' : ''; + var nobrEnd = this.brOK ? '' : ''; + + // cc 2002-11-12 modified + output = this.stringFormat('{0}{1}{2}{3}{4}{5}{6}{7}{8}{9}{10}
', + layerTag, + nobrStart, + prepend, + parentLayerID == null && nodes.length == 1 ? '' : imgTag, + iconimg, + selectedStart, linkStart, + nodes[i].title, + linkEnd, selectedEnd, + nobrEnd); + + /** + * Write out the HTML. Uses document.write for speed over layers and + * innerHTML. This however means no dynamic adding/removing nodes on + * the client side. This could be conditional I guess if dynamic + * adding/removing is required. + */ + document.write(output + "\r\n"); + + /** + * Traverse sub nodes ? + */ + if (nodes[i].n.length) { + /** + * Determine what to prepend. If there is only one root + * node then the prepend to pass to children is nothing. + * Otherwise it depends on where we are in the tree. + */ + if (parentLayerID == null && nodes.length == 1) { + var newPrepend = ''; + + } else if (i < (nodes.length - 1)) { + // cc 2002-11-12 Both lines, added image size parameters. + var newPrepend = prepend + this.stringFormat('', this.iconpath, nodes[i].lineImageWidth, nodes[i].lineImageHeight); + + } else { + var newPrepend = prepend + this.stringFormat('', this.iconpath, nodes[i].lineImageWidth, nodes[i].lineImageHeight); + } + + this.drawMenu(nodes[i].n, + level, + newPrepend, + nodes[i].expanded, + expanded ? 'inline' : 'none', + layerID); + } + } + } + +/** +* Toggles a branches visible status. Called from resetBranches() +* and also when a +/- graphic is clicked. +*/ + TreeMenu.prototype.toggleBranch = function (layerID, updateStatus) // OPTIONAL ARGS: noSave = false + { + var currentDisplay = this.getLayer(layerID).style.display; + var newDisplay = (this.branchStatus[layerID] && currentDisplay == 'inline') ? 'none' : 'inline' + + for (var i=0; i= 5)); + var is_gecko = (agt.indexOf('gecko') != -1); + + + var is_ie = ((agt.indexOf("msie") != -1) && (agt.indexOf("opera") == -1)); + var is_ie4 = (is_ie && (is_major == 4) && (agt.indexOf("msie 4")!=-1) ); + var is_ie4up = (is_ie && (is_major >= 4)); +//--> end hide JavaScript diff --git a/utils/defaults/templates/account.html b/utils/defaults/templates/account.html new file mode 100644 index 000000000..7db674410 --- /dev/null +++ b/utils/defaults/templates/account.html @@ -0,0 +1,194 @@ + + + + + + + + +

Account Details

+
+get("name", ""); +my $act = $request->get("action", ""); +if($acct eq "") { + print "Missing account name."; +} elsif($act eq "move") { + my $cid = $request->getInt("charid", 0); + my $cname = $request->get("charname", "NONE"); + if($cid < 1) { + print "Missing char ID"; + } else { + print <<"HTML"; +
+ Move $cname to another account...
+ + + + + New Account ID:
+ +
+HTML + } +} elsif($act eq "move2") { + my $cid = $request->getInt("charid", 0); + my $na = $request->getInt("newacct", 0); + my $cname = $request->get("charname", "NONE"); + my $acctE = $EQDB->escape_string($acct); + if($cid < 1 || $na < 1) { + print "Missing char ID"; + } else { + my $q = "SELECT name FROM account WHERE id=$na"; + my $res=$EQDB->query($q); + my $a; + if(!$res) { + print "Query name error."; + } elsif(!($a = $res->fetch_row_hash)) { + print "No such account $na."; + } else { + $an = $a->{name}; + $q = "UPDATE character_ SET account_id=$na WHERE id=$cid"; + if($EQDB->query($q)) { + print "$cname has been moved to account $an ($na)"; + print "

Back to $acct..."; + print "
Go to $an..."; + } else { + print "Update error."; + } + } + } +} elsif($act eq "listips") { + my $ipadd = $request->get("ipaddress", "NONE"); + my $acctE = $EQDB->escape_string($acct); + if($ipadd eq '') { + print "Missing IP Address"; + } else { + my $q = "SELECT accid FROM account_ip WHERE ip='$ipadd'"; + my $res=$EQDB->query($q); + if($res) { + print <<"HTML"; +
+ + + + + + +HTML + while(my $row4 = $res->fetch_row_hash) { + my $accountid = $row4->{accid}; + my $q; + $q = "SELECT name FROM account WHERE id=$accountid"; + my $res2=$EQDB->query($q); + if ($res2) { + my $row5=$res2->fetch_row_hash; + print <<"CHAR"; + + + + + +CHAR + } else { + print "Account Name query error."; + } + } + print "
Account IDAccount NameIP Address
$row4->{accid}$row5->{name}$ipadd
"; + } else { + print "IP Address query error."; + } + } +} else { + my $acctE = $EQDB->escape_string($acct); + my $q; + $q="select id,charname,sharedplat,status,revoked from account where name='$acctE'"; + + my $res=$EQDB->query($q); + if ($res) { + my $row=$res->fetch_row_hash; + print <<"HTML"; + + + + + + + +
Name:$acct
Account ID:$row->{id}
Status:$row->{status}
Revoked:$row->{revoked}
Last Used Char:$row->{charname}
Shared Platinum:$row->{sharedplat}
+HTML + $q = "SELECT id,name,zonename FROM character_ WHERE account_id=$row->{id}"; + my $res2 = $EQDB->query($q); + if($res2) { + print <<"HTML"; +
+ + + + + + + +HTML + while(my $row2 = $res2->fetch_row_hash) { + print <<"CHAR"; + + + + + + +CHAR + } + print "
IDChar NameCurrent ZoneActions
$row2->{id}$row2->{name}$row2->{zonename} + delete, + move acct, + move zone +
"; + } else { + print "Char query error."; + } + + $q="SELECT ip,count FROM account_ip WHERE accid=$row->{id}"; + my $res3=$EQDB->query($q); + if($res3) { + print <<"HTML"; +
+ + + + + +HTML + while(my $row3 = $res3->fetch_row_hash) { + print <<"CHAR"; + + + + +CHAR + } + print "
IPs UsedCount
$row3->{ip}$row3->{count}
"; + } else { + print "No IPs associated with this account or IP query error."; + } + + print <<"HTML"; +
+
+ + + Telnet/HTTP Password: (blank=disable) +
+HTML + } else { + print "Query error."; + } + + print "Actions: ban/unban, set status, set world password, delete chars, move chars, delete account and chars"; +} + +?> + + \ No newline at end of file diff --git a/utils/defaults/templates/accounts.html b/utils/defaults/templates/accounts.html new file mode 100644 index 000000000..7166e0d91 --- /dev/null +++ b/utils/defaults/templates/accounts.html @@ -0,0 +1,77 @@ + + + + + + + + + +

Account Management

+
+get("name", ""); +$accid = $request->getInt("accountid", 0); +$newstatus = $request->getInt("newstatus", 0); +$type = $request->get("type", ""); +?> +
+Account Search:
+ > Account Name   + > Character Name +
+ + +
+ 0) { + my $q2 = "UPDATE account SET status=$newstatus WHERE id=$accid"; + $EQDB->query($q2); + print "Status update for Account ID: $accid Completed"; +} + +if ($query) { + $query = $EQDB->escape_string($query); + my $q; + if($type eq "account") { + $q="select id,name,status from account where name rlike '$query'"; + } else { + $q="select b.name as cname,a.id,a.name,a.status from account a, character_ b where b.account_id= a.id and b.name rlike '$query'"; + } + + my $res=$EQDB->query($q); + if ($res) { + + print "\n"; + print "\t\n"; + if ($type eq "char") { + printf "\t\t\n"; + } + printf "\t\t\n"; + printf "\t\t\n"; + printf "\t\t\n"; + printf "\t\t\n"; + printf "\t\n"; + while(my $row=$res->fetch_row_hash) { + print "\t\n"; + if ($type eq "char") { + printf "\t\t\n",$row->{cname}; + } + printf "\t\t\n",$row->{id}; + printf "\t\t\n",$row->{name},$row->{name}; + printf "\t\t\n",$row->{status}; + print ""; + printf "\t\t\n",$row->{id}; + printf "\t\t\n", + printf "\t\t\n", + printf "\t\t\n"; + print "\t\n"; + print ""; + } + print "
Char NameIDAcct NameStatusNew Status
%s%d%s%d
"; + } +} +?> + + diff --git a/utils/defaults/templates/action.html b/utils/defaults/templates/action.html new file mode 100644 index 000000000..e741683a2 --- /dev/null +++ b/utils/defaults/templates/action.html @@ -0,0 +1,43 @@ +get("action", "NONE"); +if($act eq "acctpasswd") { + my $aname = $request->getEscaped("name", "_"); + my $apass = $request->getEscaped("password", ""); + if($aname eq "_") { + print "Missing name"; + } else { + my $q; + if($apass eq "") { + #set the password to something that somebody could not likely guess + $q = "UPDATE account SET password=MD5(unix_timestamp()) WHERE name='$aname'"; + } else { + $q = "UPDATE account SET password=MD5('$apass') WHERE name='$aname'"; + } + if(!$EQDB->query($q)) { + $result = "Error in query."; + } else { + $request->redirect("account.html?name=$aname"); + } + } +} + +?> + + + + + + + + + +

Action Taken

+
+ + + diff --git a/utils/defaults/templates/bootzone.html b/utils/defaults/templates/bootzone.html new file mode 100644 index 000000000..895533e66 --- /dev/null +++ b/utils/defaults/templates/bootzone.html @@ -0,0 +1,41 @@ +get("type"); +if($act eq "static") { + my $z = $request->get("zone"); + if($z eq "") { + print "Invalid zone name."; + } else { + $EQW->BootStaticZone($z); + $request->SetResponseCode(302); + $request->header("Location", "zones.html"); + print "Booting static zone $z..."; + } + print "
\n"; +} elsif($act eq "dynamic") { + $EQW->BootDynamicZone(); + $request->SetResponseCode(302); + $request->header("Location", "zones.html"); + print "Booting a dynamic zone..."; + print "
\n"; +} +?> + + + + + + + + +

Boot A New Zone

+
+
+Zone Type:
+ Dynamic
+ Static. Short Name:
+
+ +
+ + diff --git a/utils/defaults/templates/bugs.html b/utils/defaults/templates/bugs.html new file mode 100644 index 000000000..063bd6d10 --- /dev/null +++ b/utils/defaults/templates/bugs.html @@ -0,0 +1,81 @@ + + + +Bugs + + + +get("action", "NONE"); + if($action eq "resolved") { + my $id = $request->get("id", "0"); + $EQW->ResolveBug($id); + } + + $offset = $request->get("offset", "0"); + $count = $EQW->CountBugs(); + @bugs = $EQW->ListBugs($offset); +# @bugs = ( +# { who => "Kim", where => "arena (0.0, 4.0, 5.0)", target => "an_arena_fighter000", description => "Wont accept a simple summons item" } +# ); + +?> + + +

Bug List

+
+
+$name "; + } +?> +
+ +
+ + + + + + + + +GetBugDetails($bugkey); + if(!$bug) { + next; + } + + print "\n"; + print ""; + print ""; + print ""; + print ""; + print ""; + print "\n"; + } +?> +
ActionsReporterWhereTargetDescripton
Resolved$bug->{name}$bug->{zone}: ($bug->{x}, $bug->{y}, $bug->{z})$bug->{target}$bug->{bug}
+
+$name "; + } +?> +
+ + + + + diff --git a/utils/defaults/templates/chat.html b/utils/defaults/templates/chat.html new file mode 100644 index 000000000..7593b17ed --- /dev/null +++ b/utils/defaults/templates/chat.html @@ -0,0 +1,15 @@ + + + + + + + + + +

Chat

+
+show the last -n- lines of OOC, maybe tells too... have the ability to send world emotes, OOC, etc... + + diff --git a/utils/defaults/templates/commands.html b/utils/defaults/templates/commands.html new file mode 100644 index 000000000..441434e91 --- /dev/null +++ b/utils/defaults/templates/commands.html @@ -0,0 +1,104 @@ + + + + + + + + + +

Command Settings

+
+get_all; +my $error=0; +if (defined($gets->{"update.x"})) { + my $i=0; + my $max=$gets->{maxid}; + $EQDB->query("delete from commands"); + for(my $i=0;$i<$max;++$i) { + my ($command,$access) = ($gets->{"command$i"},$gets->{"access$i"}); + next if (!$command); + $q=sprintf("replace into commands (command,access) values('%s',%s)",$EQDB->escape_string($command),$EQDB->escape_string($access)); + $EQDB->query($q); + if ($EQDB->get_errno) { + $error=1; + printf("Error adding command %s: %s
\n",$command,$EQDB->error); + } + } + if ($error==0) { + printf("

Command settings updated, will take affect on zone restart.


\n"); + } +} +?> +
+query($q); + if ($res) { + print "\n"; + printf "\t\t\n"; + printf "\t\t\n"; + printf "\t\n"; + + while(my $row=$res->fetch_row_hash) { + printf "\t\t\n",$row->{command},$i,$i,$row->{command}; + printf "\t\t\n",$row->{access},$i,$i; + print "\t\n"; + ++$i; + } + } + printf("\n"); + printf("
CommandAccess
#%s \"delete\"
\n",$i); + printf("\n",$i-1); + +?> + +

+ + +

+ + + diff --git a/utils/defaults/templates/config.html b/utils/defaults/templates/config.html new file mode 100644 index 000000000..523e6ead8 --- /dev/null +++ b/utils/defaults/templates/config.html @@ -0,0 +1,33 @@ + + + + + + + + + +

Configuration File

+
+

Download XML Config

+
+ +
+) {
+		s//>/g;
+		print;
+	}
+	close(F);
+} else {
+	print "Unable to open $file";
+}
+
+?>
+
+ + diff --git a/utils/defaults/templates/configdl.html b/utils/defaults/templates/configdl.html new file mode 100644 index 000000000..934d03a58 --- /dev/null +++ b/utils/defaults/templates/configdl.html @@ -0,0 +1,10 @@ +get("Content-type", "text/xml"); + while() { + print; + } + close(F); + } +?> \ No newline at end of file diff --git a/utils/defaults/templates/database.html b/utils/defaults/templates/database.html new file mode 100644 index 000000000..68e8c23ba --- /dev/null +++ b/utils/defaults/templates/database.html @@ -0,0 +1,19 @@ + + + + + + + + + +

Database Operations

+
+

Some basic database operations...

+

Accounts - View and edit characters and accounts

+

Variables - View and change in-database variables

+

Filters And Bans - View and edit name filters and banned accounts

+

Mini-login Change Server Type and Settings

+ + diff --git a/utils/defaults/templates/delete.gif b/utils/defaults/templates/delete.gif new file mode 100644 index 0000000000000000000000000000000000000000..0bc60689c6dcbfb71770789a3c2e98f51f6eecd7 GIT binary patch literal 339 zcmZ?wbhEHb6krfwxXQrrHNWU4 z&?^&f5)>{J;LlXy?-r + + + + + + + + +

Filter Management

+
+view blocked names, add/delete them.
+list banned accounts, add/delete them. + + diff --git a/utils/defaults/templates/guild.html b/utils/defaults/templates/guild.html new file mode 100644 index 000000000..53423433f --- /dev/null +++ b/utils/defaults/templates/guild.html @@ -0,0 +1,115 @@ + + + + + + + + + +

Guild Details

+
+getInt("id", 0); +if($gid > 0) { + my $act = $request->get("action", ""); + + #note: delete action should be done on the actions.html page, so we can redirect elsewhere. + if($act eq "makeleader") { + my $char = $request->getInt("char", 0); + if($char > 0) { + if(!$EQW->SetGuildLeader($gid, $char)) { + print "Failed to change guild leader, check your logs.
"; + } else { + print "Guild leader changed.
"; + } + } else { + print "Invalid char in makeleader.
"; + } + } elsif($act eq "remove") { + my $char = $request->getInt("char", 0); + if($char > 0) { + if(!$EQW->SetGuild($char, -1, 3)) { + print "Failed to remove member, check your logs.
"; + } else { + print "Guild member removed.
"; + } + } else { + print "Invalid char in remove member.
"; + } + } + + + + my $q = "SELECT g.id,g.name,g.leader,g.motd,g.motd_setter,g.tribute,c.name AS lname FROM guilds AS g LEFT JOIN character_ AS c ON g.leader=c.id WHERE g.id=$gid"; + my $res=$EQDB->query($q); + my $row; + if($res && ($row = $res->fetch_row_hash)) { + print <<"HTML"; + + + + + +
Name:$row->{name}
Leader:$row->{lname} ($row->{leader})
MOTD:$row->{motd_setter} - $row->{motd}
Tribute:$row->{tribute}
+
+

Guild Members

+HTML + $q = "SELECT g.char_id,c.name,g.rank,gr.title,g.banker,g.public_note,g.tribute_enable,g.total_tribute " + ."FROM guild_members AS g " + ."LEFT JOIN character_ AS c ON g.char_id=c.id " + ."LEFT JOIN guild_ranks AS gr ON g.rank=gr.rank AND g.guild_id=gr.guild_id " + ."WHERE g.guild_id=$gid"; + + my $res2 = $EQDB->query($q); + if($res2) { + print <<"HTML"; +
+ + + + + + + + + +HTML + while(my $row2 = $res2->fetch_row_hash) { + my $tline = "Off"; + if($row2->{tribute_enable}) { + $tline = "On"; + } + my $bline = ""; + if($row2->{banker}) { + $bline = "Banker"; + } + print <<"CHAR"; + + + + + + + + +CHAR + } + print "
Char IDChar NameRankTributePublic NoteActions
$row2->{char_id}$row2->{name}$row2->{title} ($row2->{rank}) $bline$tline/$row2->{total_tribute}$row2->{public_note} + Remove - + Make Leader
"; + } + } else { + print "Unable to query guild details."; + } +} else { + print "Invalid guild id."; +} + +?> + +TODO:
+online status, delete guild, add member, change motd, change public note, set tribute, make banker, rename guild, view/edit custom ranks, view/edit guild relations. + + diff --git a/utils/defaults/templates/guildcreate.html b/utils/defaults/templates/guildcreate.html new file mode 100644 index 000000000..913fa0867 --- /dev/null +++ b/utils/defaults/templates/guildcreate.html @@ -0,0 +1,47 @@ + + + + + + + + + +

Create A Guild

+
+ +get("name", ""); +$lname = $request->get("leader", ""); + +if($gname ne "" && $lname ne "") { + $q="select id from character_ where name='".$EQDB->escape_string($lname)."'"; + my $res=$EQDB->query($q); + if($res) { + if(my $row=$res->fetch_row_hash) { + my $lid = $row->{id}; + my $gid = $EQW->CreateGuild($gname, $lid); + if($gid > 0 && $gid < 3000) { + print "Guild $gname successfully created with ID $gid
"; + } else { + print "Error creating guild. Check your logs.
"; + } + } else { + print "Unable to find '$lname'
"; + } + } else { + print "Unable to query leader's name.
"; + } +} + +?> + +
+Guild Name:
+Leader Char Name:
+ +
+ + + diff --git a/utils/defaults/templates/guilds.html b/utils/defaults/templates/guilds.html new file mode 100644 index 000000000..a2392553e --- /dev/null +++ b/utils/defaults/templates/guilds.html @@ -0,0 +1,40 @@ + + + + + + + + + +

Guild List

+
+ +
+query($q); + if ($res) { + print <<"HTML"; + + + + + +HTML + + while(my $row=$res->fetch_row_hash) { + printf "\t\t\n",$row->{id}; + printf "\t\t\n",$row->{id},$row->{name}; + printf "\t\t\n",$row->{leader_name}; + print "\t\n"; + } + } +?> + + + diff --git a/utils/defaults/templates/guildsearch.html b/utils/defaults/templates/guildsearch.html new file mode 100644 index 000000000..6dc1233cd --- /dev/null +++ b/utils/defaults/templates/guildsearch.html @@ -0,0 +1,40 @@ + + + + + + + + + +

Search Guilds

+
+get("name", ""); +$type = $request->get("type", ""); +?> + +Guild Search:
+ > Guild Name
+ > Leader Name
+ > Member Name +
+ + + + + + diff --git a/utils/defaults/templates/head_01_nb.jpg b/utils/defaults/templates/head_01_nb.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f5e108286694e5e3f4bde850f9aea97ef2220347 GIT binary patch literal 7645 zcma)=WmFVU*Y9V5p=&6KK^VG4N~F7U2w@0CKspBLt|6sURAA_kk{SjOK^mpILy%5s zkk{wEcRlNVyuY)~UT6LHKKtzRaqoxOhgAT%hKjlh0Eh(u*!)|7hkXE8$;;Zw4*&$< z0001$f6^gPumFez;9&oUe-;N9=ikNy;oyMq@NseRaB=Yo@d^Hc zPe@4oAO2Yq5@KTTf1vs=KpYSV9}l05kdTaul$7+p`v3Rg;Tr&q|F7aaY#P~joMgcMj)V8Ya>y3W@g;fnaFU2HUCYXt_@Y2QVZ*dKxa zPYN6yVqEP1eHi#JI~WK0lmaAAiK}mgUKx?ZYA&Zu+?1vbsnL6`DO%ja<%s#t>k9N z17P79yt;$y0gyZF@p_p7e(Mg5wFk?EDS zt2lPpop^79N{cb*nDI_iF8iD^mKMUZZx%o?XK7+{+T_DXC@EoCa>93cZ()LRnv6Op?=to@ize3TtrEZ5i}rs|1v|k%G8O=2KDlBoE|wIKQl3T zDOASa&$Urx;fw3%tnqYcQ>siRvbxcfrIA1yEw0vYlt3RS892gEaD3;POn%Cf&eV*j z&iiH{Fbe9$CJt8e4MMZ5sxh2-sHCoF6IFmb4!&6seYkY)rD-e<0S^{lyh2LY8+moc z#EUv-@a7G@zgR_zgP#0QATMU#s(C}0i4W-`22JX|_lsh*GUZDtqc1{oh_+@LufKSMD^(C>(@Pm0~z8XZ1B z_w=IJ18cC5sB3`=07tnZiyNWe7y=_l2mgD>8< zJE={qg0}toKt_of{c6Mgqb`cU{<*5HFxl9en5_KpFi8x#8 zwMj&66>Bq}F&WHkFP{{u`v6!QDKsV`2!Q>5oqbTS!!$IQbWTGfudACPPshdehEV*S zsmt#~Z@gYdYH#}$hV8qF>A|_vL*d!D1-5f_x0iBia<;3&a;P6OtV5&(0K<~VDQQlIR2Y{-p zsf&$%SGcn8sI8WI2~uOI_BnXq8E>4_m$z`*AEmm?1Z*is;zSvGk`X=~Jj3xkkV&NE zj3R-EOP%&(nZG|kqZE)*2w=<-%ds&pW&)%US&Bf5Z?tm^NOim+GOQ(hKI89I?V1;> znkoLSfbDZmXbs&)B6Fz7U)M!yzDE9bE72==a`-1WcvS@p-&4%O9oBw@&uw>bAog9F zQz9qjMIf6!C(kPZtk&Q)sB2AGBe;-_jj-r)tt&?3j4l3SnyZsp^D|izxyXF(=bvnT zf3vDun&2-Kl4Z`FU-;@1MaunU+70|!b78zVzsrkJXu+OfoTl9dkBt7KzQ9e@waTDx zr6;4==DNxakTK55ALJxTRD|7_e=3E`mQID-U*rXOWz043T_h zjRG_dT8;$3=`}U$nucltgvR3b;J^+yHPVfm0Ba4<-?6C8Ty(yOx*2*q< zUfCzE;;;k)=Z=X=-- zq_FhFkZWF*ZA}?Mt?6wRZSmHu@#?GL#cC0!w#a~mBo;%p^Q~D@85$#wmFPJ7*aQpR zD-nju8X1c|g})hIztuO)*i*E8eL}^wBj{Dm`b$mqK>W)ew|gPqdmg;9+5l-D43a=+ zh!_g(sbq`vElZozvgawOvgl}_w{o8c(4`uao1sQU^(HTY@4F0sK;QNI;(XK+R-+ZK zeBtdn6jB->?b>bsd=oqoH$I%sKZT{SSSrA4`E;*5VgFCa0%^MflpaMSK$jU6TV>1xLF^^ zEId)3D&#z%y?QsT8)W|q9Vi`r&0XnJ%+Pza{%h&R%)e{Mc1L&<9f3-ZOC=9iA8N{u zZrhDFaZmgrTT50f9r5PVRhvCyMetJaA6948y9&OjBMTePHyWP^-0+f>NobmOmtO+i z!bYTCl*9+N_+}?i_2b>3Zwwbz-4Z_5$4VKI1vV|9R_uBkhbRt6Fj}Uf>+|*0YuRh@ z_vs@!vz zoy_)0OtsPvau){FvTW=uT zM#Qsf*(!ghto&nZp$v#cGaask&EyErMj>?|MB)J3K=-TkRh0|F7-!bgW0`^yACh|2 zT&WCAr^v~j&&>jI@zg!{4!k(U4G4}1Vfmq4{xtEr$S{~V7yh?^ZyfPO9Q)FlE5$S1 znUzkq1kH3~0;Ek^J>~q!Xu@f%w3lcu$-2yh^@H1ZV35>g{?OG`<=O)OMuQ)KiQlm@ zPfeOFWYSN_)yJrn$iZwlmV!3l?=q%Fy`UDx#bx}3ghvwLx#ELruGKR-x|SR)KWGhU zm&wS71#Cx;6Vda3-pZ+EGD{}}!><(NK^1PE_w7IaR+BMK@&M0%PszPg+owVo4Y{w@YoRR6S^?K-?!~u^1G}VyW zi8qUGC|pTI*x`Ap88V?QT^Q1iZ>H2ATHM|aBsY0}-3(T)BKWO=1mD>`x@9a`50UPU zypy5h$(Y{6!Acg2?V3Tj4R;{iqpA9dzhE468sGhtj`*`?S&(gJ_62-gplcnuID8#f z@c=+CoOSG%z=>FY_Je;);tMb}aAX&Fp7~{+%UU%WSN%G5abIuga9Uh)l=@()0W*%( z&-CMi>@50iz@3!FG9N?r3e$U!PddGGmlyXi2LaBf?T!Zcg!=3DLYKeaVcBa5wSP*4 zI%R$x3)_e*lD5o#)H1q0b(N9=cW2|bubIUaJxoJfDo8?tG-Y?i#B+ZPY-y$E-t*7h^LBPNb<uB?m3TfX&7{x*ICzTief~etyz}pOe45Ls!z14^eBgd#d!| z*WcL0e@w=LTMej`yvI_zMWnlF3A;+*g?G3VsXD+4E~{5!G5U6kG@JQ9!}q8?eKE(= z?XLp#d;{DYGfo<9k~>()@=?;+xhiTs;Q}SNCvUnP%CDBZb-OdR6c%L0Vx||ezP*8v z5T>UZ6X4h#AWi&Rnl|=t3!lt?7;|bX{HZAQK~Kc12C){uHPZ5a6?OB&&}Dceja^0A zY*ZjfS7pgzV#9J}tTHzZVn8=hQ2kr~{%E=e`~W~DU9@Wy|4BUaNowC0EF3}z&Le*L zdFbjE?b%?szjx#Mj$^{{SFG7pL2C0hENii6lks5zlP(z?9nnvb4}d65J8F_B2@{K8 z$_D^e6+{31W=G6*S2LR_&F~CBd^HCm5Jaw* z{#kve{U`oM^?pa^>`ayrSDn#%%<6O)g1gwkVALb~^Pkz`UwIMdSNp^8`d0yp{UnyBAtFE`dT95$uDYXoyOrFYFU9EA(A!Q1e(2^r)tnrnFAe zOvUF_N|08{)YmUxCE%zFcDqNp*)IGeN#%wTG1K0`OmNqE&DR?Z1+!S9b>dPBWimX- z&ok$*nf%$aY&VcHwomE_=SS~^jRe7~V(77G?YKNFDVZPkU_k&GwQ>&ZRL_=CdGt)& zxSV~Ip=5(}YKN$H0Jt1!qIk8h7Hm4q(Mgu@R;C5z@c}(e~Qcd$4J4V4;Gds9GD2bpKN&gk?%!K0^et#9IKwh6qC(6J?zfn)1{4W8+fz8EGp6|9 zL?PFRV-^?J?7wqP*AoLI{P_EJ<*fcDy$M~dm?>0zfQ~?sjqxgfe!w~9Vb5MrYUYh> z&2>faPnyAvy2s7gWJ z*+z|(Ue!grj*YJ?H1Rh~PvsIepL?p+9c5_0tNvJa&Pl3H40W*pi+dRjlBt@VB>*ob@@czV&&x+h$2!&^`yzzeyE% zg;!^Zan*3L3wK`8l57z}H>#?_orLLvC0~UaYn$bB3J_Lv-v%J7Rs{379q=Ygo9&t2 zx}mZ$63lO~m0*E5=nyQfBC`xF;( zh2BbjO+-RfFT^kgCdsRQ705B8k*E3>R*8EeJ_heuyXkp#W++$VvzWW7vM{#QJAnp^ za2xx>b=`xWTf1l_>~@1^?|G#qDRm~7u-&RhM1*Ym!gqvdapT|wqF8O)pXJ@W1J_S7 zYm2=d%9+BE?Q>P}XqkhY?ZtSf_~}7Qmp^I>q@M1cf2O3i+tlH%ed#!3$*Yp(XE*6C zy`z4SwF&Ip7As*+t>sOejUJH$E@wy`ejj3Fdo&zl?PVzEC5_u%|T-PtR=Xv=3VW! zAZaSU&$%b+t0f&mkt}EIS#dKbxYmrX!uqLgo-#GNG@vP&YGn)`0B_r-e&O#0{gI`- zSJv{`__H*Y!Y{sz4E2LN70i?+p7p(?W@m^@nFu4Yei1KXb*+pnrL$^y&SE`MMcUSg z!u_NW2b_&0n?W8jAP@&OLr|?0ekHvC+Oze7LrUv@@=f&mm}o3O@TVxW_a{yLWVJH8 zbheju`NwJ8)$PB0vM{&6ZkfUZ!+j3UWwrJpKAIYJe5o2b1DDIgK=viFli&6J$#UnE&?t!9nzM; zR8K0Q^Qw~#PfX%#c>c553OY`dJ`H2nW;viN9Q7KWhJvyzG^-U(n?$tmBX=q zoH)5lX@pe#hc5}rG(VJj;fS<)ibXhzkcr2oDV`GlT~xgNz#8lY8(z#~<G-PO$_f78w>Jy%BP(ts=V?l)pThZx1_uQ>6VkZ zQz7$gWX;%hCI-(wnr$sS02~fyS$5p)ffRBtH8i0W+Y%#7H4*zgh$fHk?gGrg(FnRJ zjUk-3LP@kdsXF}&;E(tAeuuAq0{s6_pjkg;|(wv!J$z=v%)|*i9J=x$|CY2 zg;aiwyQU;O*fWvp=sQEDgsjPc>1Vgd@Mw`s20x39*-e)z^un|$XPHU*cU{kE60e4h z(DM3y@u*7`I-bSJW{Ef+u~mibJ5lH#F;Avl=A2{ZH6JFri??Y6yLzk$;O z0=A4^MO^EUe#cblE8nH@8?Z{$zzRGtq*F<|VWD5Mlkd>%Nwaj7v-Jbu^Bd=Do#Y{+ zbiF~EwO>*1CqH*2QINYn^I($6^tC>_CwSLt+r~GoE+ZZ|SkneSB46>)?!~T?*sJhD zIjHzu(fF?2+Rh|6<}1AmHf7cq?WKtnvyW=nlMQLGeqX5n=81sF&GeWw3vb`k3oE&G zT>MCW0}J61%VPDKsG>@ot;bv$rhQQ~vu_s-B7e-He!#Y-zeZR!7%9C_J#p^}oP3|+ z=)H(e5Vw$jTV8nFTZ}!jq{TI3+(4oLw%~r*)5oXVc@DPuNl#m0h$RY8 z3CgzMK$$lPw#0i@fpE6MYl|rYdJu}vha{?7e8lb%<9lLzIHQ98@vO5RBcu!*M5rMS zHi6;aAH$8&6$CE7_#b0Y_~j&dV}IwM;(_(K2i5Ni!MK*e{U;4K)EYYkM7nPDYp6PD z6#T!9G37U~F!tkqbsclH2rF21$&~EVFm@yX7Mq=%DPY!ohpNZYM1&CG-%&y- zVjx~8boR@|9Frw6fzY7kg2&j$5>7ld9^Xo6KfIVSXZ;$sUuF1jEQXWrN`$1hPcWYS zhD>Lh!bz5KuJ8O6nlj`DnbP!V4H_67smK?2H1G_>9o5)KGR%+sT$*GhvN0Duro>96 zweb!_3nQG8pB!0QJL>8l<7yljj}BkFvM7TE;EV5R>uaPnHH1zq8EDwUDS|_2hxWg# ze@Y~%9JH=)-XgTZ!BO_}@`&i0q+*Fo%i6D|Z`r}J&sm8=yJ1!`*|#bp&*aAXlZEe1 zHr3U$_;GvqM@5UTVGs#LmiU(vwzJ&15HSm)ut~U8^J9h}Zo-|0RRJLkrPent3E*%k zrE&ASVnPp{w&Xo8;2O#SWipNWp2#jrXyg$~_aSa{jz!;~poraCz=*m>H!c|T+*3Y7lV!>Miy@?2wJs=IoO`^d$%4Sl6^jsL z@5l9vJJh_zs1f~VIx$C-Y}XhGl+kgg2aH!9APRplU+bSNrqgl~OkEX`32&vw zkfqUhpcL5wt&S>7X1a}&i0jAb={eY4Yl1Mu{#~4u{4}yfV;cwovRO6Nf^qTjoHOwR zs$?42v+dc0>D-=_t$T&)5js{G;zrisJkl8~M+g;V{d(?oKmXJXQq1-!I_=uO<0?la zBn$V#!A+=VWJr{?YI;K!X!Lt}2@C3-y+2M%Csve$=4brdQz&{bhY_8m?`yYc z&blL|9;?-v+$tlcwTYYB5SxT8MeS3}*yQfa=0qVQq)6Xr`vH*ouhswg0N~WTSX~|< z6XKY=#{0x5(2;ZPkC7dS=unBQKQAfTy{o#HxOwjN_Yxu)(t24WHAo$ja!!_gb-ml= zyczTWNO|-lHsP04%k~S@%hF1foJ*%~A*_iVDP%%F{GOCns(RP(UtUiT+%mPb#PNq@ zUZ;(p-n)?mHgzOjyQ_N04qU^Osb;cnB>b7k#%5|tPwvi-Zf6xQR({>ZXS1xIOSPtN zd$wWD_a=^=WZz!bHUuAB{#9P2vcFr<@DFy<=_sApcApx|6QH^8@Ye;!VeYBV{g<@u zYhG-W{OiCcB6TrAM=9=OlngavKL0*!-0meyn)iX^*QNAJx6`6e1@TVp76iN7Gh1U5 Y)nBV5JKh4n{#Mj}nL+lZ<6-W90OO=E=l}o! literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/head_02_nb.jpg b/utils/defaults/templates/head_02_nb.jpg new file mode 100644 index 0000000000000000000000000000000000000000..abc1cad58dfd20b687d2c2596f0bfb9217bcf9df GIT binary patch literal 14172 zcmY*<1ymftw(UT0cY+6J@ZcUK*kHrp9$W`^C&3Bs5Zq<3AcGTJgX=)Bgdo8!K!DG^ z@2~gQ-`&+!t4{UmUDn-aS3R#hZvhBE3d#xqBxC@<>g51D{{axb_p)^K10VrV004mc zOV$YhPsYl{!Wv-J_mYG3i~xiKULpSnFV|NnuU-P`s~4c6qM-ceL`Ow?0V+B=1_mYu z20Au24$gnV3-JCINdGO5iiM7jg^z=UgHQAy3lZUe?S%gm;r-v}fA9JK63=}ALJWWv z($*^^LI5%$(knuw=Rp7*0C=Hqp^3j67m{g95?0F4<^Xi2k;VVMGJHWpu&ocB6e|9&F)Ol|h$R~gClfGeg z26C0AegI9iux`G`omurin*Tg4KlJg*Bu(H;DzcMWoJ#Jh8LF>xi$bNI!(rwtImpmH z`7fq)rUjDpRHy?1Wh!iB0306L9U)8TMpigVghD6snC3mAP*)H437F!YO0e3COrdm? zl}{>I)WFh3i4D)Q!=ov^osVI#2Aox@4kIfSzM`k@F@TIKU9HSQ1LMH|T4xzSrH5mtyel7B0{IwhH_epzFkb!SQe_NIjx-x0T z3df&P`ZqD5GVEpxV%STSJ030-axUQyv$UZ44=@av%Buo_9ART^Z`KM|$~@VCDKiZ1 z-|JBFl~HW#sa#L=n0Yz9Z{P{h+|t)a81xK%09oGvhZPlKoJHBaH^NDgrpoAcP1k>e zuIAfJQF+@D>2!>lzs2nB+#Dd#LHui7s*HUff&17J*~zU5t8_fp2+Q_YFpGn-Uv3{< zR;v?TtZf{k6gCDRqq_@hn2eg*Q&iB&rF>=3Xf;J06C{6)Ffpe&RdZD!?{L~`NVYgsyN`n2;(S?RUt zBI2rYGMPC*C7Vr}uqJ5+CmS~5744y?4n1|KH9K_~DO(sk*^zczC&vPhqlpr1$jyaV z1!pU``yI?@$q`W-&aAA2!`_D{Y~iZ*ft4zH+8EVFpkL zwTcqC;V2-$E-a(zlgLU}`5*6pir4FvD2I-xTpFD4`sg`w2B0$9<3j%)UTWV0$s3)6 zB%E+kw|~tTfu2ItY$!P*3zM>XB+{50zF>@Y^%e|LVNwjivTC{4PF_j&#@h%*CnvA18gsE6(Yjm;TEroVAVyE6!w=cZ%R96#2@k z1tAd+%w?YteAjwS$H_$8An!7rzTY&K^Xfu)3#-6|c!$o|$YbWqY^%5bnwp9#yB}&$ zW)W@tO5aEHxa5KN9Fl{cToP({Mh|goI&|ZPiX3C>RaJ zIKxhCfWNG*wWLig{!>Yu8#bcRw* z3VtjyP;kMoQVKo+G$vF6?IfD3ejAKDfl9Xb3vaxS5$|x@O5fSB#H`I1|0O2!9(5r_TpmO$h6XMgjxJ3p z3}oG@l+Iusxb9>p_}2Rh95E~yK43Q*pqPeRf33CoURQ4-rAnRDRdGSXOC|$(yV%#Q zC`~kmxm$9P?_+7}ifdI`c&@dI?#HoXK@4TbSAB`$_U}=gNafg+3k}OPRr5(?Wt3SX zWVPvd48w8O-Anq5-#b~R5pc`%w(ybpY7@QhcXk1ruf(St2<>VhXE)8ldzcX06-5f} zw^TOXYq!S?u$Y`ZmfNtjZ4glz6~|$o08JY#10kAOn9w*^4-X}g@Szv(u5&DM+Bzg- zRfr+!MfOnLh;--u5ziio%fgGL49YulK2U8r5^<11^J%9 z4wf0NUnNWQV-uAth9Zn_(kb2WQXVqi>?<`%w?=lMebw+#W3Q+CBn4|bqOWN^+{6h+ z5BSIDrOOH{vvNCCilOxgyj>gVOS$YzuR^*Nk~kdq$tw+R8|u?*UcHta9s2s4&UK%( z9mMf*h)Sc%L~Pv(HXIFRT1inpeRsd?@K`dnU*9pC?{|n{jbBB1zWb+6b;}3;BJtiBYr;)?-w^JBIaTwe#CdIW$BS!2n}q8}O2qg!@<-e%_Y4*_%<@85#H-c! z+LI{j2eO}1e;J0d+u9*#Y5c^rNNN@mM$ecUy;$*eQBA&2o~gjNU9s2R?8OFswdXF+ zlgc^%tt8(zv!b(qTX|rc?^+jbxXtVok^D#fXqn>~Fbln|@Cvj)EyMd0JhwsZP}_E_ zbE{ikpEPK7&Xct&Qa2PxgheZ(4Bs8@SgIR7tTl8jXT{en@3b*+9vb>9C}UOVy|_On zq!gwm8=KYU1$rfV(OGFt!RtUqRwO$(dQSFE9(iyQ1z~82n>C8w6sjEVGf$8QgUJ$6 zY7(=(XG;a^8pv98G=#^c_GvLxNDbK+>(DGnf$r4o3DXacsswlgNJpEtJ^#5vuE;oO8qR${hpj2#)im@l&1AZn13H zI>1t)mHw{9^mUYs)QKjyO0949XqA^t}K%B3Y_v6O`uV*djTTDZ@ zQ-0*chOkNNvK~zmFs2-UJg$smY39YTgAw6{BWKJ>)gAj%+; ziHLwN%ziTC@fYV$W#rrBwX1{vRg4sg@f~E!F@#?Vm|9I)hHf&nSCo{Elw`eedEf`e z8y|P|wCUcB{s~&PX{Gr!9=9^Bem!_o*8FSPqc4G>+osrd+B4%^gdEbG$k%}Scjb0j z$hM?;Kf!N-@MBUl?QXpfWmSo6hx_MJccv=Iw&QABr$t-M(2v=Is$0%2!KEtFOWq0L zEI5S;BoIccP@$#tNE`+L=A(d$YE1-yK{6d_a?PK-7+tI9&w9>Ad}uV*PpLKM7TQ6k zJnoSB)^$mQm!WnEwbdiLe|=lekBs(qQ1yauFKzTmmU_j(_r#&V8kG6BMf8pI2WpSC zHU{$JesLYz|FY5Vn^TCmV7`*ZJPv`&Ce{Z7efQ<@2Ui&v2FlFSu@z=y%o*3*oY`uF z`WNjC4ODV*xE-l$7$~gHJQW#LD@OU9t5v#b75J ztEQuU*`!WMT7^z+(iWKyp`JkCjh>#vi9!1>m-%zi5ZCV;2$F87;8FoTzJ5VfPb5#d zTciJ{iiOK0mR-u}hKc<*?~n$@gI30wPTrcKw&gNa(UKaM>CsLo!*zz#wD`VVxC|_B z?*z6Pef47&7rA)`c>0w+sSHa=QGb_f-Rs1-n2*V8fnu>sC*13P%;N7)F8-@DJUg7! z&MyS7BvA$y5`>A_gCjBKiZIS9^_5wMuihs+CZv_q%Ebsll-|s$R@<$bldT4EhmsJ< zi2tS1WVe)*$@bt8Ty?N77Y%oI5PPL&43Gd)i)if;5f-U#ZCE-QhF)`>tqOe*WD=!m zTe>tZU)XQA4+z_1@>wxDD3|xpA$1#6#v&``K`z!NTT*%jQiw|%%Id4S6{_e@nE$uX zu6T+*s^HInqU$Nt_~wjm-vRtKheHeVOo|xt$ySbVyXWGgtVT~fiic8je6-hlRJ=7W z|kp?WRAfz0SKDg3jEi;Hrr3ab-MR}ByP5udeKZq6kJqZ56rHF?`txtL|zQb zWyw}DC?ewPwK3Siz&Q#js5H6G#Bfe3+=rVPy;2 z*;CA6Gd+KY|K%p{g@)y+uB?W?w_Bm8ay}K5iahC zns@I0W*a1N`Ia{uRaBU0fkEyqACBmRUqhN5HwGB}F#qtJ^2czd_Ah*n-v?Vk8n*d0 z2F2iR18z*;XMh%b=l+hyHq5~`{!0yR8yy$9J$3tf;}n3dTD@e&(Rmi$ZSwhenNn}? zr%Ud8Ps1cDG=ZQtXrf7y>4X^zG?& zx9Sly2NN8&?RrZ$_-oAC5#8*S5XUmJ%Q3RsthxR;^O3X{U3eMuP z8)42s|3}h+<&&i521&AwHqg-^9{)_eVt$4NC_Mv`0tu}bBz==kBq))KEARJ^`*@-p z7^1bTzXFxw`u9%G-E3=Q?oTePg6_-s;BNtHTmP&8|L?8j_QLZWjrASe<@2!v^MlJC z$DIQG8uKg0o@QH}^MPcF->*3?F3*!RxY~NYeGK4^tSwfAE;>xtJk%W}CR_jQwWYr* zfUZF<#jduz*?MyCwdPk^KHIAA6!9f~j;_VVc7w#a+#QWL`owFEw6ok_fFDw3HZ38L0D$ha?7$+^?D<7_iyI;|lB?(GXJFW)R_c*%z zaP4KU7RrAO$;Y@rJk6FhzWz>FE^z&2tgaz~n2MOR;GMC4@-|EV z$i)MP_gBY`dhr>*uU?OKUf#zSBbF^**2mie_z`YGA_=zl2?Dj5D$;pruf0OMau{K} z3`FShoYmuQgQ!e#Oy}fmqzV4Ej@U46&)=t8F8QYp`udk%6&YRk|8S#(_kWk`pJYZQ zk6ZZ)zSvOaEHROaSIvG^Xhmpo+~nJuNdE%7$|LT~UeAv?c?t+lc%xjk}LaLGJ&wsL9clf7=*>Q~BwJ@Q6et0mB_Zj5Y zQfV(`S3znGB5Ee$cV^ZSu(9G z9V6{_0arEI+!2hyhb#NKQ4qt5wWA1)QYZSIZZxHEpmKD3ZMg;6Pm&}PmG{gJG<``> zT?#8Gf8gqOCHV#oc>c1tYa7|h3K zq?HhkaxTCR@S}#uM-d@u_DtKK-fEH<5A&oP{!?tu7cP;uEgbZX@7@M>baoyzMeiBP z4;DW_kc2A6Atz+4qej6zw=HhZtbQDx;F-kmAhN8?8N#HbNOWyR;76*G0T0Ov(eaNMdG1yBOBVAqSC> zAZHsfBd@LA?IOm&e~Be(CGURXgYS_weVdT}u*Mli}T zb6%0l_lecax8=Gg7&jQb)_;U}{~4e(m6tfRmlu;X;^eF?N<@hzyW}Jq&L_p|%ks2# zFyf|>{QJk!9i`S&$GzskU=TOzx6D@zJO!| z%-z|3XTv&XpVKtGcg-P4@Q}KTR?Re6_7~s7=Z-Rw;)bMEux|L%qF&`3N0O&D&%9$^ zC%hxS>#Ah&v$uRcI0a$k=eQTa4DzJIz*H6{qbZ5-jfd7`XxLZ4>p1bUPOHE~{rxtq zDVyljhRWbqF*nOVo75r78u_pZvD8JYHqLh3jbk7xmaWg*7Nm-3x*&$t-=caX#%-@_ zX<4c=uHOl!*~wW)spw(Kxuuk?t?8pAkd{-iYmjL?MmVI1w$;&S(5&-lT>Vvv8D(DH z`&km62G1Kv8)c22sa&pqFjCC;g-9g*7HpgRP@NR z5tniE*V=ZtdsV4_dd}4Mq3(py)`k5ZU8<^Npe%@pL4h!Z0z=5&NXsk4-fi7+5lU_e zI&YxhiaI_Tn=1LgbRo#|!1_?rHkdY?+A5sjAW4!5yPF zoQdYQ3)mdLQGoqavtkRJs>Bj~%@=<_YJG1AuRgij0}OFIX0!3fltCEnnq>3W@(%|? zlfWtMOutwu;zFAKaHZiKr<*{Uj`{bn41$tV;TG7&rh2mR2Da~HhVnBYwj1zgIl>@D z#oR3A`EO8|rCM7s z@|RaoK6%;l{)^~sFbdgXodXU%!`@C(3kE#*yi%m(v$HUAQWGBo140Wd$U1tq@s3o6 zAJrZf+q*=QHmYDa*xo4SlVhdBx0=p|@=!7>VRf^&?OjAb|zEeE5Empiv3R6Ld^ zPW*}|m2g@=?&QV>V>8`!_Nq{sZu0x#n>F_UF!EHJD z5%aZrEH@6y{mD+;%6D;YX;fSTZ_u6rZyRB~%GEBP_+T~>Axuwi)??xmHo5+8Nqh>L zN?`dqEmJ);9;LptTd28(xF+>L@u6Oymtk!&G+v6M^332%y`c~kFJ*oP7~8S@!Kh^yeG$xWWi89ZwK)T&bM8YBYr)??t_9lb2LAS2 z0_`>J{xYP}+n$)ezK=iD#vzGT$~ec#AWp?p_0Ba+t0JbwEolDP6p?|r|6WCoDvVxK z+flm)UEZAzU1Rt+oIoELu zJD6xNsKi2KQ=G6im@2VT%{|Ob#X}H_8+!iOlkJyP`!_+BZ55ii`mXYkdZi(FlEe!o zH6D+YRV#iDS_y^Yx<(T~dDehN1Bj8mST4{i%$ZbU$yu)`g`DkC`|5`Hz*ETROGzXR z?7Q(ZKt0y-%JYooy;2-k6QYePBaWI#Mp-xfQ%85^n#05VGvMakA5)GU zzh3xajn9+Zoy)f^k2K>m(xXJ)0hE1606W0Mt{3V32P)jFQVj1krnM|(bUDeL!t5Bb zL~H567#XzT!v2%Ovoac9Bj|9bYMI!JTu&sTiPT<{Za9$mGB_knEuF?b-y9=6$+|~^ zOD<*$sW2=>NEET%T9B}AbAg(gls7X;`;#9^ImG~CtC**ZZRuvH8lOCj&hcv7RPmmu7OHS*tWA2B~h}wY(g!my>gZV zTKuk1F^7B{4yBtFU%WsiGbYeZiKYC*jEOU|aw!Ep>rJwo9PhrS-BOx6rlWUhKs6c4 zFH~%!p%)>XXa*wPB0Ip<>VsvaJ%N_mfPFLnzA6I;o`lwO-&`$2wvW6E3jM5+>u2mz z;q|_6MuYD#-MkuZde#4nvsl^n4YR^-!(w#t4i?Gm9`jyOLo*4JXi#YWi&nUI(jjWr zqV)7DRxCiMN6c|C^Vs(-@n`EQf{ZWx?pDpEVhumT+bM=~^-#?k@);JlTX?%tA*tF2 zvDb9Ua~$MAw#tsn`W?L;oSw+*C?M&|+d`ZH#^w7JyS2i(y?LR$TTlEVtr`LT^3cD5wo>ehfXL@AmDJyq~ufvaOxO;q( zG~4lB_o*}aAjvUw?~(BBL+Y#v6?0ek*z~ZG0q2*lP*3J;9_ED_HznOghH-Hs9w?q; zj&J`%r|xvyI`RTx{6QN!sf-v1axj^?jtfyIts7qM@Ta_ z?`MRGL%3_o^B)m0;|>w`*A8o%pLQH2MX{@zVs+^VQyhuf*|_$!h~jtELP_Evz&Khs@;vgt>( z=k&HQk<^9DrqUc%4@!VKn)Af+4+%o+OijK1HoXgv)cM&1J2<5x{2oTy?KK>DP31r2 z)Eg*(^LDa5DQccqF4N2hle?j?{&Tl6;3d`7gFvG z;#HtXP@(;la zDMaX03(V>fKaB%C;+r9J-+~Ia7g|5ctf{pb4pQNb&%VwwLZ39ulNbmH<$`q~4MI_t!ZxJh3P+2`D8<$0R-1YV0Adrw z9e80dv5f1tgwAr^21zzYB$GQ1H>AMYX7UfilKLsnPN;bt5h@9X?yXaZl9e!8;=5HM z1JxCEK6pccOAZszfob14mu2nKpw?jV?=b2N#auxTQ&~;sYpy}IHlW&j!x^$`+owalZ{OXk~ z1K$kW8-1M!1ri@eaOehl!vXp!Sd&PH7bG_iBdaoB4DBWzD6Lc*;_@vchpM(S!iPFN&2oo%62DYQY9} zP0?!bCSwozH-xTu`P;^l&I7 zT>2X@X2PqtXxBCh7eLn$K)2um;%xW#c9L>Se^tp0W`@N;?Jl53iA<)Nl<{?HArB&? z+aD<|gvUG?_q`f}*NHWL+A{mTrt0sw{(y4E?u<5^Iu>^(Rpc5-auHG1ekPl`r5C>W zxi<6SP>sIJg6c2(!6*h=qo zi`$y!j~{Ul{GLAFc<*K!eT}a5lg~H-=J763q|&;@z_nP>P}kHG)vip$8x^+-;sPfE z3hEgxdKNVeb=Y%E&X(cCP4i^~J(w=*%9(^poIE`0u=C1_3#B!cy0I#tGt(l*C+w+x z*QHPC>B5BR)_jEoy(YX~f8&Wxc&k!Sj-R0?+REw-O5%hwG8pNu&=y z7G593aiLH>*pZ>yzc~OK;PXw5cu|tJ3L&(wII1q@G^`22jD$-mlr)zxmKNB=tb~vS zN!h3ebypDX zPwU7y5%iARD4IiBwohDbv?=17FnJ9>wgyog;~Vg!kJMVj=r9@M zsY=;7W9C2Qbxw#e>-KyER)id}@$c1snYkOfX%)yCIUMKxlSir^Z99ztWd@Niq%qMx=;zptDiZ4aB75i>%B`hZc@SsU#zM)>D&tJc-P|vs zKoW$A2sQhxv!*~1`QA!1Sw;`36yNcR^|&;YMTW|q`6Hr zR5DWkDCsXmj=cI&<{R*`=SA3{3AMWgBQWEOVe1=bJ^PbNnbxy$w!O-eTkyE*H^g8e z0ZG2V0^uZ|E0&v&+DAo70d!ZfVnIT2DsnUXVyp#Nu-H1PH@uWHx+r43U;^xrq|K1* zsLd>sRc96038c&&+ zwN{J`4cLxZ_VMbPZ6Zd8xxQHicF<^VlIgzsW*waf<41vcU2vPC^J9mUM1jDICfa|J z|I6ZRubp>wI&&)H%MJ5j&yr5_WFFaD&?Kf=r>9tX7p6)qpf#U~2FkFxgb*c!6)Y8UxH@{8kgsc+0iAttZxjx%~|9r&&K9r&M@P-h;I zK>LhN48=Nv$_Cr**Lz2S)6^LHg(i~H7a_+Bi7PHQxi61hlu5}b?JsTB)$Qz$c%S+F zbZ1qvB3rR})Re=|-_4hEpnrMiC>-e~w914Ms12z+snSWSloxbW+SsZ+YxQfJD0!;8 zU2^sVwf|^T5=@3@H7LC$rAn)y$Odjj-sJu>I>C-$8QIl{u=v61Hf!8oX<(Lq&%b`W z7U%s(Vmw8WaO7+3)dM+qu($3>Wzaj30dZVeAN9!x#+zUps>-CnVOlA|YC|_6F9}>{ zdr@{*eR_IfXYMXjmTEp1OMel|q{~{ham18dGIc9+>wR2w5ch_XC$^n-8aySCouPRa zw6kyNL+#X4p>?I^w`zjBNSn7!U*?DT_ohi^@>6^ZMFXN*?t7I6ZV@xO-40f5o-*m! z@|yFF&F0rjaYM3=&s91(J!S<5-`{e-3v)o%kube;{|R3x;9gH6nrQsQ0R&}$(zNCO zv9*LPRIGcd*JR5N!xSSZCQ5ec?Dxm?;?;?Wl2?f2#~7~X+awk>jq!9wTNLh|n;Hps*G);Ga6jRz(Fe9TVAL1cUBB(MSM&I%k$rUK zKL5it_PlL$CQFvo~7O z$W2bg@JHDRbDbqsyo?|XYq=5&_25EhZuy84w&F-RrlTgZiZ7amOUmr7FTVu#tPnMc z@R-+il7!SNN594~I5e>^fogpX?d>d&d1+LX5ms3VV!JUiKX$xOX}J+JiakkxAa+;G|?ZMeT+L~NO zY9lBeYdB1blvPD5_8nGhB*;)UQ@kyJJi|mP;WYEd2 z`M~YY0pKkL>Bf~&_=K|0PQcD+APjDiaS(`10jS6g*T?8!X0@1?%TdGDZs*g*vT;|A zHD(-87(S+?5J179D1xUj7XdG*7jJko|U zslAFh5?EZ<>tq`Y;gGEjfP@ID0x-HcrSCw-Y$Ig@Zxl2l>fDrMOlQqI`moPiEZf@j z+k84-?|AZ@hLGts*@dZ2{6$O)nYk&C)jUd^5V!6Y*R15B}Wm%yv5z z7tk(5vp0CKliDxRCXzM}Z2cf_2^$+_wbPlHWjH(|fGQi;t!cZxK|R#!dx;9k75BxP z>h}Xjbv$!dKZ!pd{6Vo7^|Wp}bjw7k0)h%H%9UhW`Ka{5DkQ{xpnpiGeQ3H2mf9S9 zJyI$Lw`lxQ56L`4anRgaZT%=TEH8*Ql)040C06Zurc|)jerAobdK_a{k8O6U#CfL- zS(=5Zt&VkQqzX#pj}A)#V>nVKb(ceUdo0=Z*pRaZ_vItEn!{5=!U@&v*>}S-{vAY- z`1hW*-Vm3I1^fo@4ST#z_n7QyW`qCHDf%UEon#Ee<#AafQ2Jr2R^JeNZsskXV5)@( zg2QU`znGnCm$e*PyoCv&u;@d!dFR7slDVb^KZczXyVu~yUe7!@ulM95kb#BI!o8v+Kk6av`oZDjP+}sx5vkIELG9$ww4z!SubKcAH{L< z!X>&&hkfG|eajqlx7)ckxEq&PrGDf{CXF+^(dON#^A!JVP99Sm9=Lm{V{LL zD6*}Fktlnz%K}-;W!zz$Vm(Tt0b|<{*KP(BJu2(<>H^X@ zhxb5f{j=SZ5~9X%2_&R<1At}o_^8_D9vbeGgHW}FHFHE4KP}L5Bs`zZkP13AF>TeP1=^q0l5yG?C~BH zt{`ddqu-&3q!3I4nxc z%`5rV)0(Bw&0GsRki@?T;RtW6IeeK@h?ZH;Q;AmJduw7epL(CZ_jZG1cS_oUo14Q~ zUO}dcs<5BighwQbOl`Bt5DU5Qyk4}&IMaNbI??{-DTn=DYftam_)m>%Ll8<9kN9CQ z>GI0HHN15-Eaf0W<)kxCI}}|0v&_F~1>L>5HySBUqc(n}iZuqYWJi+?XWNaJG)1R8 z;H)M7IIS{4E|x2}#FtF)p+B%$CP&4&3o&==yWz~B)Z_{J!m@Q-R|W^F(2|Ixd@;A5 zk5^LIcUq^d9`W2%tD(yZKp7yi9CEijGfGVxXI39gK@=%Lv{$TNWN3iUjSgQNyowsV zp|~tTK<6f>UVmWVr@OCHvu{cR%$BK+o|qmg?F1}5Dsof4d_&4+!c}_QHt3M+4BE^N zfgqM(?d)RycPIsn#Ph(^r2X&3pvx3d*98xB5PmK>T6SIsmLrA$$x!Fonidx58k2XvX_o3(gG1Pg(P>HD> z104BpLTz4NO<#idI~&%Ofv=)|mlYn$?oL}SPCCI>3Vm16wezn+1l?OY{o7;p^Ps(+ z=@kka_$p6SkFtFhXZ&v_|Nd46yTS#)azG@k8*E}T_J{nbYyZY-rTgI7%I0I1b8#Wd zGBKltb?lYZJ&D@Du*Nju8x^SK$D|VX2$rtBjgCVBy{{E9^Gnc-mKUevTfX_x%gh&R zE2MFEjr{YJEgmOI1~JBTqC4`&<=X7;nQeSrELmozUUd$MJ3rV7%nW#N(JS^UcMRsZ zJQMg6OldTMpvk0Wye887mwsrRvg7yJ4EaExvs|*dU5xR3O^jKix%}HLDUt#?bZk~& z{KTl8R5u6t2)hwrgwPex&ejCNVwnwZ4b$LSma#%gZ5_q3AWuHaBB4FH{R9DO#fb4F zFOZtOp{(Vi|3)KC4 zciq3%lTpBP>-9J|PzBr+=m^{#Xq4ziJOjMyv2XM|hyr?$@m)4|x9=*qglLq$wk2KP zxcfOgo*w-T@%nlM@r!$V2;@oqnY-um6LILYaUSG=e0Pu1=OJY=Jucc4;2+qEINc1v z+eo*e!Ib!sMmy=Znbmy>eG17591(kHZ#zFJIUz>I{B`lq7}lLz9S{>*lPm07`GHz2 z?1AouhI={6K$Xx)z54if$dpX_ZU~YTnwU?CT z)T6rI!vfrs3aRa^mpR9=-t1|$=`l5)4~Zl zeX9Spc}preco+1tzBWSl4B&a%tAJ~;YOcJj1_{L%u=YOc95n9hUEC+iKb(jB6M6=$ zUYZq0XZeHu>brA$Hh&>PYzyw~Vt!o}D)yW~AMjZxpAfk&Q!3HV0M;rU*3*+*DYc2m zUg3}_Euz5uG9kIs{L>vNy^z(*cjZMs6Nk)C>@V#T*Y+q+a3}Ur?+0}A@99HqR_fS7 z(!*lgz{OF>0{E8nUr+hFA-aEbr&a$pFfZ=oByXdR{P0h!{W^$$T@wZT?ll?@2~d7{ zCQl`5;C$Oeh@b9=u$<)gWymt_ZQJ-ysbfE$F6#R2F4&WO>+g`{-;as>F8`+gdfKk@ zA;t=lXC$$n0ldM-A(2utmpu+o(A$B%O<#D0ZO-q5@$Ika+qGH~8o$oPGinO{qF%@S z`*9H;w0|dby4{sIF0pjVzen}c{olx^;3KzM{;{=!u*IkaAI1j{O8lzL5bvK)7Z>ej z{vr3zfXvgqz-bG=0?xh13XZ(<5c{*`f|*D3RF;siL*bCe0tde)E#dR}r+3+Mhlx{C zmljeN7gA4wE%iD7q6+r9Hsew+r4-MeLTqUjdd~yB_G_)5M0?OP*2S~W$DYbF(|-nT z^yd53*`9U%fVPVZJ!bM}?zVn^H!VxSN8#kdf9Lz4(hCVTza6P~a-6(N*n3jBaCxjd z<-ZB=>*0xWxnlF<;~V;M@(iFJWWEUgTQPkhF?BM~}iD|*0<;~r{L7k2zMHRX9W6L&AA=%!W`K$Gew#P1Y zkt{r{T3DAxI-L_{ZMW%|QIN(b_MT1WjYU%$>|OPeES{W8@{73Rb0+D zMAq*`2SWIDQy0Mkl63o-cV*u#_o(aB9VdT3U1qL!tgZcXSv8~GluG0{UaFXX8Tyq2 zf^7aCWqu(%QUUSlA)*htN5fBaW2?rJJbygvL3s%(!pD2|-EOzjKMS^|lZ_@Be#k-Z z0@6d)A01s02otJ}d&Tbj{J9smT(UO(hO+l%ZsgK&PCf&&X;n@VmhUzt+fGhICF;C0 zH_vVsGio*y=YGGm8KGcM&wA%wK;WYZKCjoi?7Obb)H{uoOP!y$m+fCV|GKQ6v;;@Z z2I*Nyu6K|7RXrI$_OSkrdj@Fy$aHz<+++CHU2vqwyz@A)rPnss0j`5MMDaVKHDh#f z8R>{6GU=(<96COCZ!ef{+be$jxH`5Ve=6L04AXvyhkK#;n3=Rl4wx?t_kLB0k~Lp= GUj08sn-nwv literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/head_03_nb.jpg b/utils/defaults/templates/head_03_nb.jpg new file mode 100644 index 0000000000000000000000000000000000000000..927a164fb0f5983950424dee959d15f96fee30cd GIT binary patch literal 10242 zcmaKRRa6{GuRybyCk^l;O_3OgF6J*;I6@fI}9Fz2X`ObWr9QS$GIPO-LJQM zwN-U>@9v+jRrR*~wgteDlaiGJz`z0k7Vib{b_T$d^f0&g0l)x00001;chO$}nwW*N znI*vD_qznvG-&80K{}=z6ysvuK z!y_TUBm5uD|1SCe58i$QFp=IFH^RYS0$?#=;4op{1^^QP!27=caeHU_KR`r6`1k=H z84mWHG5`kt|7ZY!53q3X2#5d}Bn$uy+&gc0LUvZFN#Hn!c zFxAXlIjHf!gd|I-f20so?ct#ri0>!-2Us|)50qcA;l*(fFx99oU`)+iLpZ*O6*cxr;385KCeQAeQ>z9iU8&Q! zg%(R{aN)hJ0zSdPyx$ED6CeUO5XRcJ{>+jEiM|ZUAM+7?p+PTL8aPSyLa9F*>!aV0 z!`zbLC`#i$s!hnOH@$&3v$&f0-bH1gxB_kSq21Ofty@A@Bo_o7O$3_AxC9_(CY7zx z-CBmt`stSGAx6U!kb_?gmB;%?5w+BHR+so_oiKHKeo3FTJKufp+3Lp$vm;Z}^B|hZ z5zGp+P}+0oP@?KzqeubbM%E<|Q4Dr`z(}OVw&A9J4A}j`_5_WiOsr9v!il{jGG*xJ zU8PrfCO^0;~jS@Z3 z+Cx&k?7QF7Nc+QH{^9hW8trDNp+e#kg10sOGMKe~CS)!($(dY(t;MvPy9%$Nz6#&Z0PBr(ty zGq|>QB!2NEsv=W>rcqkH^Y^*45yII1ekyupb%kVloPEyOgYPhh&~mnr@Y!Y;Ng0#` zzd;$_EJHP>y;%Df!v;F`Bf*Vo$YGog_se?gLRlNR1ZsX};8~>dzi^Slc;4$tUAL=S z;Tl1gAusD^!8Xvv=b`-BfU+x7Q6~bZ?BT^J?S5$7uYzisyWcOMUV4N#z~`woYhqVy zAAO&xh}yd+JLx5H*77Ip{tYu>trB+d+y^cxMm3GKV*3phcdzequ=FED!=ftDnWXvx z{o%75<7#a8v^jG9U8+q(5fi&^P~!X-?|TI`2%(FXd4)zwj>+cTfbm0dP^ zSI4c)iP*L))Z97a{Dwyz3Ku2lnY6lx^{-K%T~CzXM&(~_*u1PA)v}_=_$Y^t-)Gy2FSa;V7TBLovCX+E>mME9Tvey;R{D#S4WufM9)jQ8n4lT~fu~ zRLYC^L56Dbx7H$JIjOSr>SF2me{jhT(Tn>{974CQc5ITKv9N#h;XCW4COl9l20s}V zYU^ec^-+VeesPlrG1Ph9@3UM zC%am$Y1Yv+265(B0~d0a+#dKm2eD`cAm{V|k!mKew@Ww$xqhK_KFG{>J7?h5l;zwM-QF3mH(I2oxF_yMAOZB}{`2=%%hG zvZt(7ZCxAxBv_5e7(bbIf&#CRV6<4xAX77P}4MK%@G>Grq>sI)BZ+CXSII zIm@I-m9NW&lWDM0@BLC)Q45pnk*)iKT<{`~V$wvb?n&r3oSP&E2a}=1>x$lM;485O zl6(l8>9NatV>=r$x!dqv6EebMHRuLbY=f#}0fUVWJ)}oN5dgz}+XS$l-J6M`qEY$8CKp!d^9D`Qh&K6Dd zc^-{Y>M}V;HrL0}X8XGmH@=b%b9`4LU$VZv;9Tq8^|(ZT!U-*JyFIW}Oru=5j2%m| zx^L2uyo;&90=bx@y#nWDnxjQmH)eBt*i1$=SZQVnscdf5OU;&Y#V-jb(USYqq6`>o z7edRZ*_5lB?}?bv=WRI_U*OJ%tx0RF5g)M6PYgHlH}u)Di3m2g|Ed)ir(c!mb3qrx zGi6BZ^TbHRtG%K76RXsyns1fWf|4EcY39Dm8t1}>P#4X;Oka%;`fTx428=U^ZNqdYip8C5 zK9JES`yn$&Ip&%Ek`5vKO&NvXlzgmmXArVM_)xFlZNx|AN=lW z(e5NuZkuflW!F#uu1o6tj9YIIKja$=JqGr1wmF3hAsSDeJS->2)vjw5_nLmd8591r z67oIQu>#HSoAn*nUp$l_v9==?PMJ6F-;pf0l`D63;l3QtX<6$MT44mnA4Ud4X*g{k z)!>e1OMwGS(M0bFk_GzHB0uu!Qxjo+*)n6~2DtBY66!MZ4lRxw9LaXceoxRO4f_7^ zOu$at5F1v9*53o~5)F9}XrHT*fSMsv-DLbl4kS=WfzyJ0pKa)Pbejddu^z9tS6DQqn1=;NK`Xt5Vm^qFBqGB+9)w?gP60@>+6%;`Gd!QQ*2LPWP%gsKO)$$ zJKMGy5cYUB!Oky9LcfEtjLex~nyTauZA__G{^9kf<|uzI9(Wu{j`7f7*1newJVImAwys*> zJ3ow+*mbYd{b^A{;1)I4E$YU2G9?~^DQ1PPY4MU7w4YldakQ(oe9o66S;c?wCGW96 zrPJLYMCyv1T_gc(kH5Y4Z&%_&&`dl@`3Nvl`s%`i@R{w|?O`lt{QD{d*VN2Sd?xGgerZ{|Z zvKX_R8ePG7J*e_eYE@e(bA^Wk^Wch9^e(1Mcjmz3wEw$g8L$e$AvE~M#b4{feORV9 zz!WtHMxvto=?9Wb#}En??e3O^->!p74tqnZQ>-8U$kRT^%+0&coY{D#2(xNRAOG=` zs%J?RCB{jxJlg(c_v3I?jVZvu%ou&F3Y8K($88(_;HN}%JjlAv0mKEMIE+s%Kk6J~ zkS)xp82$}lm|OE1i!IRkJG2D9W~c}Sm+5m0e+%kPK+H>tlD);-E*u+L9H8@RP&Q}h z7Wt`jONDqK`jQ#O2Z2srk7#kJ7m0DE3PQVMu0x8KH~MT<^`J6r3+p^heS4`=%Z3xv zy&dzzUB%Q(w|yz6+u$f)rfPzIV_FcuLHGg#x{JPpVBi#`w9>rsO*Ubrpp~Mf)S&H_ z-ybgMx&*htt29W~n~C2s>>!XSlVG1fPa6@Hi+4gAWz8YcixIOyF@cV6e9c{;6)ZG^ z8*6V(n4{P6lj^vp_GEpdk^{$UJoMEd-~M*S#K=?RJH_9$RMRRFxDy&<-9z&AgH;~R zBc5?GV0EKe{!&hh7UlWIwkrKLw$Los>EJL>(#~nBofC3Xpm>A3n{URvHo4KZwSDke z&nsyJ9t=s?P?h^dkP9AB374}~$rvEkFS+XM`N)8Cjw4h15N@7Xn9HSo2ngc&c_yH-a`q-FL0O*k>+9tL7{o0TEORc{3&Nz1u+-KP1IP-Em z37a>lTDRNK50l^gPxq}^*d(D*MzEWaF*8+ zrT=S5C9X9~S3>OPv`y~H#lV&|FVy<$D5Re#jj>G&W|dR4c!9O{wzMcb#n$V?^(^Pv z5;0ZIODJ9n<&_b^`0}gDmKtbh`!}u`<=VpKwj}{sWm?{Pe=P_vwJnb-v&NwVq9YxD zipJ@os{G-c!*J)GnDUY*Ivx0dZ6VrC1!0-?8MK%kd_R-%o$^;4+oRfPX(^L^(3x{U z2-SE(p;pV+5I6`RG#D+_o80d1^Gb2 z7?p(eAqcfY4j&pObvfP`CUc=>;+?rl;zG>#*)B51`Chype0+E5+t7^I6lt8M8y72k zy0VTDAwKJMe5UW;CcA^5mP^y6Ph^`bD|ytbR0@@wNeHdoV;K$1wPUeAZLW5Kuf<+# z)$`gr)#R|`)Ub4W5UJ1_p%%m$suuB~=w;udU1)4f*}{)qIXh8fPz702Iu(7I)*iSo zf~6xpZ(fa+$)=f9^fAzfzePvzC=?r%yHn;{SA@rPhS|tlJ`QD77od^Haw!ztU9^b$ zSP}{G1l#rQy77rlDxZCuLXZj*nlFv81=hz7J3_#*x|;?NruulA&*Du!4AvdaR)V@6 zR<=bG$?jI_?MIIJG*nED^ZlES$K6#Xe2S>zs$;RiziAzsp~>ISk93&OzBtxfzvNMu z74e>|XTYptioOa5p5u3~KlVSE+qq}YG=NBmxO4toD2ImHw-sARm{U(VngP(Jy&8yF z>WDHFTm60~`O_IR-%C*COgv@Ecz|h1+TgTpQ9EY%0hf5wl2eJzzzzduSt%F%1#%p_ zlA7sBA4&DO)lY=`!o_S6AmrSX!R{y;ap1`;RdZ!#^%U!2i_vKZ8(XxCkLjo!v%c0# z>y?YhEiRMkeSLZG2Hslce5wcR)&a{QvFZu`9pr1T9<2%Pv=jo(cND92D35@9I%4_I zJv8~=rqjC?^IK13_Gr5bq>1}rr%9QzW4?GAGLW8xG9DHyA~vJ5Ay)(;AqL(1e&a?* z13IAyo$wG)?@wvm*k?}Yw%w3%U!=|oX@_RI_mPpEgwd-M=!TTg#v)ShuI8&^{4DYb z%U|HFgZ#y63H<2Ou*zI>RBKhYFe>1yU8uz8u|)r*os~xyHm{FvNGu?)*a9-QaYOZ> ztPtS!;tUMlkco-;NGDUiEnGpb6WQG&J6rp8uHkP&>-ubh2D1o?ZFf@<=su$82>w^! zT#Rx|dkdUA#03SYz#QwlA!ubL)r1&JnPICaTdhB`&=MmwP?rj+aWwnaY_;9~_c^X3)#W z&Jw7!WwnHT^}0yI7xXmJ+{WzW$kuNyo}~SQGb@&}yhhZOP|6Ejc;Sb)qgV%i;+ zj&d!$0lvQ?Kkj&k3#tOL1AbnjQ5T2f^TD|YTv$I=J@sBIZs`L#cQ?!Z&9cIZ7c{uO zOKet;-=Fps5R85mi(6JUtkv?){wB62p&4M9*OwN3skGq5mXs zI$0(|%{ZXTd5k6Y5=Rf3rO$F;w#2DmCY#2O@J}F> z%II+C8vH)MX%?Orm^7=DEl3=(9Gi(2H(*4WT?6q&Y>)a=TEWDHA1jYU*X^S;E1#mv zS)jjJL1R63EP}+myN!>uTxL_xrD=txD&wV3bb>J#u{uHLeyi-UuHnvls>OX6oEq$1 zCLiGzH8Xq5av^l1B|R&7tOB|LA1(YC*G4O&cpkdy{W5CIucy?=0x5H_XB=B?wJ5?3 z`KnNs-|dTF-KtmKg;%QT=!nPUrFD;D%Uy?(oI7AJ`}GZws{L@-T)O~ep<9qoW@43N zUf0Wz=zV+ykOTQdpgD{ZFB>d|C_?lXenZX=NtQ2?9CeL@t_bo4ze%%pMZZ3NqWg5p z#q_{FeVDm)5Ar1o#Fpt8AYv%24m_I@c{Md0mr9RBu2&Up94TrFO%xfLsO=L`M$te> zi%^@gV(Cx^Hky2+gAy0c-$L?{HL09PtQ~a|EoTc*&k<*!*vMBwn<9CkMHEibY#GZz zQF?V{p2hbFsX{A4xI{%hIX-M8up01W!COa};prRILOUb=9yyiS!5~3V!ojvQ|4B6A z??dBLkq%LaKVrmrzZ8&uV8NxW^U2H=oIiP@Hb77_`58c0pB1rXa#WvqUwQs*$kCtj zWj8$CMSP-Hx&N*}0@+8((c#z))Mqm1__Kj1$LrCQ`t{&L&bAn>vVDEI9a%QW_Yam9OQkvZ)T+4YX)zYc(b!+w0%w*E$ zJrx(xzx`~$KJLuu=!o}nbzvb zuFXZ(iJMxe)!JkChP$Ljp*{;|>k7;x?kKREu7=0_MXwEW0dC>r;o?Ewq~+^`+C~q{ z{pIV`#P2hHpy@*i7uqNN2|wT9t>fV}HbYC?0M|f#w6v=pbS8>{52fA2sJs_b&Iu0a zrX&T`D0pA<6Y9$Xx?0`<(YHE_{OPCi7V?v)(zmG{O3GYbng?g{hGA*(Q-ydAtJK>k zO{o3sGssK;BCs+Z{3;JGdw1(BqEqQ=*>JT$26fumOGwnchJZ&ZGLAy2GHvJ^U~X%; z5rWTBzv{FWgfqaV$R?z1>L9Vgqq$-w*r{6$rFAA6?ev-MWY_4;%B240xHqR=8m#WP z9(4IDarfL$JKzmqA^xyDr>H$|+sVxK)JO(``ZSa2dOqu8JFY8f!OG&(K99C^A4Znj zVh$Tay&4?PnP1Mz^v=0cHjX6|GQPkrkRnSP`mkBs%RRNPV;V3z;VOk^P5}ipJ1yfA zzHFfy#e~POF11~)g?vq3+gQz5Y?6GqCW&KSe>SKN`&8v-%JiQnNSNOHqmfQ5_%VWD zJ?&)3|C&+lhL7F>sS87fJk^s%*OhQBfhRCrz&;zlGN?lrahfMgRM6!mB`-^XZ{P;i*+p> z9p;A`c5Qi0Qem?L)~CCBX+?M+%VN?s==pm7ijm-44H zm_hTapQyScPzcn&`X0jk5?R0R3&6J6SNOet^_Y|Cra>-G0K(szgM3tIQV@|rudb{3 z<|OQ@ws73Hr{{E`o6Ld2BJQvjiBS|jU+h0MKgd>*0ZW{*4_u1<3L-gh!<~@kf)rd! zJ6>x$>p0W$*+%NkAY7x=R^`aa$!4-bv%mh>bj<$glscl~+3g6?)~sboqKTz*pQ$r; z?n>Kgld~Niy3#gwKh6BNk!8rQY9pYm!YK7v?9>+fGoN-)8Eb!>FI#?m{4QK%i;VEk8afk#D_pydqvvM? zzkd)`mRSwqfciFXvb2G?FX+M-(M)aFj+TR?>*%MWP7|Txs7;U>Yhr@$>GVZIlS)A5 z#%F3zo=i**fw@g+Rh~bxm>^ zXHMpGRK4kS(F53LC(lP73$D5zxA5)8fVIu zrx1J7#maj*OdN{b>!7s?MWb_>7&!E*VE< zAkv18lA-VwDxSV9`K8x2xinypkc}_76N^4tc`i?(e4KuvLhE02&jtNkctoc{|eO zCW>=mG8`%_x#o9h>mMv{A_dE2*VsIz(WP}Oz5@+RP_Rjtaw>giTLe16k ztq>_uc9;`k*nb0fyKmWKA{Y^MmM*qejA*_A2<-Iz7wVH*2&Zai=n^lbR|LX(lg4n=*3=mf`hmZnOpA zzwDYjKMxozJ$uVR9v0=2Mqo<*_!VZS6nT_`JDv37nj!s8h9Y;V)}4gzu!e-rx@^NQ z2kpjCIwHrWuwodU1DS z<#*o?ye!Z?SBc8BpFKWTqJm&YF8}A^-V3d$7|%7Nx2?slrS`|k_K;t6AoFZJ)3UDE zMWHd9ce?Ri-N0uTiw#Vh?$(qKsQZ1%2NPpp6F&<|fB}+Te7XwohKwtVQiHgz9`^(5<{`+ndNKWxT79v#`)M@`pTMq4(f^ z;SeKMTf}zGjL|DVZD`vS1_M4tk!*VElwG|1Q!V$Sp9Acl8(CS}k6`(ga${X7WO24h zg2a3&U5k|vAJEy=y>JhDIZ0b|v1MBtlm1il>(qQNy>z{1J?jIMv+MJ@y|6^*0rYyp zTE&xT37Yht9i$0uqF~wXJBCMJtt-V#FVSlxIvrE+9o2V%rfBPM2605EHRUt=Sw%Tl z4@|9;3v`wykZ#8E9){y-_ud9YE@J+=Lin5EPFjYvGHg9x-52+@cZTnC6DtRToR7x# zlk@M)hZ|Ge1hrz!GVQ)`)mZ(?%~hJE%{uhrZ(|sH683V+6eK_v@c^;~c){17Waa1? z6!M17ONpu=Z`2tD4qtnIIP;GZka9+R^bIlV;~xGF?XlFRA(!;D2OMbLCA*wfoCBm! z3IkeI*HAs+c5+HD(45W~Anl$qSklF6_Xfz|wlxQ3V7;7Vk^53pWh8wKM_-xJgBBI^ zx52TqC&yrX{?YH$F+0fSl$Ne;b%K{WJC(}zD)t5$H|!E~ zvIn|5LvzBTx+w>S?j`RoF8RllR~ep2>ORbYih<8G0Y}#Q01Hd0dxxs^Vjx824u)XhvZ+9c+% z6L;7vJiY)MrvYI#1$4;&Y zMkddnZqo7?*kjHS7U>lKLomwCJ}lK?pf@;_-Fpv4TTnLA3=ka!_3%Ga*88h<$E`B3 zut)C&zJfpme>KSG&TjHVq%9N8IOrymPOiz`+8wr>M~N0%7}j^{hp!{mYirsN2$6Jn z+LCBO?{a*2R*HhUe*&M3ZK(X$YgxeMy5y|n>kO0EUAM^BO*K_$nxbkOEloor^k_}M z(Jmn3QSiLPDIOPm{hV*&uoI7T8a<-JnWgHQUi19%v$t=uP;Inxn8f(e8$iX$RHJ_C zt0u>LKE5|CY5!S&IqnSrdm1^5$%1xyj^iDV67edydU_K?#^(XECO^wLM~?GU)8pnv z=jkJ0JlmBO7+Dx9s4NP1^#%}16;wc_fHEv+-ri5B)Xooj|7P`H2vq?#~;5UC6cn>HHbgA`Ej+H^4lZJBc5=CD3~M zG$qow?f*RP6$VaeXc;PCDmGIim_ahs3!i1*9D%I}cyKlHH zcg@Xoj?R{Xaw$Y!Epb&KBu0jQU$(op_m7FO$NTCYe1N;-i_la2YAO`}axKukiF$ER zok4<#ztN>=q%=0Nf9BuvoaXT0)o^P6h!&f)xC7<$ntuZzdY>F#z5(KdDZOGgKJV~4 zUF|jrD6?8`UmXSsGwH_%;qP+_kjk;&-I1biZvK*XJ=PuyLcG+~DhRs2_x6hLGS!aq z4jdlq90D!Zt-3-skN$sO{W*4C@$YB6uZM8&5fOj;P5E8u z3+?s>CZwE`1zATe(^vIEix(eIEPNql1$k#n51owj_rHQJ`K4K}|K+^_L_xwR6C`ny zkGpBut6o23EcV-M3d})f3nkh%k=!jaaG^Zg7yRB7WDw}4T#Yz*xpru)-j*uE0 zQ*2wxB&8YoT{Cu!!_JwcEfXoC6q_tl4Q1J7_HmZe{!Ai_^O#A2QP58$lZd2GJo6#{Zx~bnC&P+kFa!rEj$k-qXa>gt zm@NU;`7ekhV1yCK#t3SHAOHgxGUtIef)P+6$;bk>L&C@iMx65z5SsIk3yB`w%kS`j z98W7Msjs4q9^<+rCn|j>$nJUND78e`|7`V>ZI;d}_5WlCtv>j{k zZJCrX`Dck#Zc3QTiU`QF!6*|{nyxAJ1eL)>7G?+j6h}4PN=hB`pu2v)Y*4r_ce0g7 zN~`DDj`d%VI_P@64{QH+S{PW6v0W^&$zHO#hE}|e73M-+!RXVJ6m1qd<_{9Ei*ZSj z0#4EfYYQ%|J@fWP(Jf7^!Z%-{ls!5R0*kXwMQDsGDBC0nmnA>HN{o&=C6$A+ zbb8_Mc5JoZ7>Yb8RD2aKfPzW76IC?d+V?`YFqcJ$dRvhJSc!!ZW!0hv=g^*96K2QH zJ6G4=Y*)%CIqKR$f@WRGoph1&j2sC?GHtKJmBdS0hYWtJ(VGR+_BFyOIVFWuJ|9KljGELZus>PAR>x>!8=O20=}LB4MPTQ*Z~>g39&rBRPgaua;?S&JjCO=Vd+X3H6W)$RVRQvdWi zD`Y91$rt*;SHpO>g}kadEV-`bSmScM_Ju&W zI8G!O@|3P^QOdhQI%8BpBgTJNdaIKkhZf)a`ocL~Lu}#j5AEZ(BomNG!pbTgdb_sp z#)sL#BQB=%>Mq~OnA#zmw5u}D9(TG~W4}Ga+FhI0;{Rq?)%LnYCU4_MX{23N9TJ~W zR#fmfFY{xoeQ-~pszkB;vu}nO(>_u;V*!*yrtiE_J~O+`9@ob%&{ALhmDsBfDp+IIIurk1SzX-vJq0PfQ6O)$!I1NA%O0|ooL zGY?gd?pFT#_ohAOQ_-LKc$Kp Ay#N3J literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/index.html b/utils/defaults/templates/index.html new file mode 100644 index 000000000..aa5bd3286 --- /dev/null +++ b/utils/defaults/templates/index.html @@ -0,0 +1,17 @@ + + + +EQEmulator Management + + + + + + + + + + +<body> +</body> + diff --git a/utils/defaults/templates/launcher.html b/utils/defaults/templates/launcher.html new file mode 100644 index 000000000..ba6cf48e5 --- /dev/null +++ b/utils/defaults/templates/launcher.html @@ -0,0 +1,200 @@ + + + + + + + +get("name","ERROR"); +?> + +

Launcher Details

+
+get("action", "none"); +if($act eq "Add") { + my $dynamics = $request->getInt("dynamics", "0"); + if($lname eq "" || $lname eq "ERROR") { + print "Missing launcher name in Add action."; + } elsif($dynamics < 0) { + print "Invalid dynamics count in Add action."; + } else { + print "Added launcher '$lname' with $dynamics dynamic zones."; + $EQW->CreateLauncher($lname, $dynamics); + } + print "
"; +} + +$config = $EQW->GetLauncher($lname); +if(!$config) { + print "

Unable to find launcher $lname

"; +} + + +if($act eq "Boot") { + my $zone = $request->get("zone", "none"); + my $port = $request->getInt("port", 0); + if($lname eq "ERROR" || $zone eq "none" || $zone eq "" || $port < 0 || $port > 65535) { + print "Invalid zone, port, or launcher name in Boot action."; + } else { + if(!$config->BootStaticZone($zone, $port)) { + print "Failed to boot '$zone' on launcher $lname with port $port. Invalid zone?"; + } else { + print "Booting new zone $zone on launcher $lname with port $port."; + } + } + print "
"; +} elsif($act eq "Change") { + my $count = $request->getInt("count", 0); + if($lname eq "ERROR" || $count < 0) { + print "Invalid launcher name or count"; + } else { + $config->SetDynamicCount($count); + print "Changed dynamic count to $count. Notifying launcher."; + } + print "
"; +} elsif($act eq "remove") { + my $zone = $request->get("zone", "none"); + if($lname eq "ERROR" || $zone eq "none" || $zone eq "") { + print "Invalid zone or launcher name in $act action."; + } else { + if(!$config->DeleteStaticZone($zone)) { + print "Failed to remove '$zone' on launcher $lname. Invalid zone?"; + } else { + print "Removed $zone from launcher $lname"; + } + } + print "
"; +} elsif($act eq "restart") { + my $zone = $request->get("zone", "none"); + if($lname eq "ERROR" || $zone eq "none" || $zone eq "") { + print "Invalid zone or launcher name in $act action."; + } else { + $config->RestartZone($zone); + print "Sent restart for $zone to launcher $lname"; + } + print "
"; +} elsif($act eq "start") { + my $zone = $request->get("zone", "none"); + if($lname eq "ERROR" || $zone eq "none" || $zone eq "") { + print "Invalid zone or launcher name in $act action."; + } else { + $config->StartZone($zone); + print "Sent start for $zone to launcher $lname"; + } + print "
"; +} elsif($act eq "kill") { + my $zone = $request->get("zone", "none"); + if($lname eq "ERROR" || $zone eq "none" || $zone eq "") { + print "Invalid zone or launcher name in $act action."; + } else { + $config->StopZone($zone); + print "Sent stop for $zone to launcher $lname"; + } + print "
"; +} elsif($act eq "rebootall") { + foreach my $z($config->ListZones()) { + $config->RestartZone($z); + } + print "Restarted all zones.\n
"; +} elsif($act eq "stopall") { + foreach my $z($config->ListZones()) { + $config->StopZone($z); + } + print "Stopped all zones.\n
"; +} elsif($act eq "startall") { + foreach my $z($config->ListZones()) { + $config->StartZone($z); + } + print "Started all zones.\n
"; +} + +?> + +
+
IDGuild NameLeader Name
%d%s%s
+ + + + + + +
+
+ + Add: + Zone Short Name: + Port: + +
+
+
+ + Change Dynamic Count: + +
+
+
+ + + + + + + + +ListZones(); + + for my $zone (sort @zones) { + my $z = $config->GetZoneDetails($zone); + if(!$z) { + $zone->{name} = "ERROR: no launcher"; + } elsif($z->{error}) { + $zone->{name} = "ERROR: $z->{error}"; + } + + print "\n"; + print "\t"; + if($z->{up}) { + print ""; + } else { + print ""; + } + if($z->{port} == 0) { + print ""; + } else { + print ""; + } + print ""; + print "\n"; + print "\n"; + } +?> +
Zone NameStatePortStart CountActions
$z->{name}UPDOWNDynamic$z->{port}$z->{starts}"; + if($config->IsConnected()) { + print "Restart - "; + if($z->{up}) { + print "Stop"; + } else { + print "Start"; + } + if($z->{name} !~ /dynamic/) { + print " - "; + } + } + if($z->{name} !~ /dynamic/) { + print "Remove"; + } + print "
+ + + diff --git a/utils/defaults/templates/launchers.html b/utils/defaults/templates/launchers.html new file mode 100644 index 000000000..69d6b5fa0 --- /dev/null +++ b/utils/defaults/templates/launchers.html @@ -0,0 +1,59 @@ + + + + + + + +ListLaunchers(); + +?> + +

Launchers

+
+ +
+ + + + + + + +GetLauncher($lk); + if(!$l) { + #$zone->{name} = "ERROR: no launcher"; + } + print "\n"; + if($l->IsConnected()) { + print "\t"; + print ""; + } else { + print ""; + print ""; + } + print ""; + print "\n"; + print "\n"; + } +?> +
Launcher Name IPZone CountActions
".$l->GetName()."_IP_".$l->GetName()."
Not Connected
 ".$l->GetStaticCount()."s, ".$l->GetDynamicCount()."d"; + print "Details - "; + print "Remove"; + print "
+ +
+Add Launcher: Name: +Dynamic Count: + +
+ + + diff --git a/utils/defaults/templates/main.css b/utils/defaults/templates/main.css new file mode 100644 index 000000000..1549282c2 --- /dev/null +++ b/utils/defaults/templates/main.css @@ -0,0 +1,200 @@ +body { + font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif; + font-size: 13px; + text-align: justify; + line-height: 20px; + background-color: #091315; //#12282D; + color: #8EAEB0; +} +.edgebody { + font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif; + font-size: 13px; + text-align: justify; + line-height: 20px; + background-color: #091315; //#12282D; + color: #8EAEB0; + margin: 0px; +} +.menuborder { + background-color: #2B6874; +} + +.menu { + background-color: #12282D; +} +img { + margin: 0; +} +a { + color: #555599; + TEXT-DECORATION: none +} +a:hover { + TEXT-DECORATION: underline; +} +.zonelist { + text-align: center; +} +h1, h2, h3, h4, h5 { + color: #CC9900; + margin: 0px; + padding: 0px; +} + +table { + border-collapse:collapse; +} +th { + margin:2px; + padding: 2px; + border:1px solid #2b6874; + background-color:#2b6874; + color:#dddddd; + font-weight:bold; +} + +td { + margin:2px; + padding: 2px; + border:1px solid #2b6874; + background-color:#12282F; + color:#aaaaaa; +} + +.headtbl { + border: 0px; + margin: 0px; + padding: 0px; + background-color: #091315; +} + +/* +a.menus { + cursor: default; + color: #555599; //#6C819E; +} +a.menus:hover { + cursor: default; + color: #555599; + text-decoration: underline; +} +a.default { + display: none; + text-decoration: none; +} +a.highres { + cursor: default; + color: #AAAAAA; + text-decoration: underline; +} +a.highres:hover { + cursor: default; + color: #AAAAAA; + text-decoration: none; +} +a.copywrite { + cursor: pointer; + color: #999999; +} +a.copywrite:hover { + cursor: pointer; + color: #999999; + text-decoration: none; +} +a.misctext { + cursor: pointer; + color: #999999; +} +a.misctext:hover { + cursor: pointer; + color: #999999; + text-decoration: none; +} +.logo { + position: absolute; + left: 0px; + top: 0px; + width: 531px; + height: 86px; +} +.logohighres { + display: none; +} +div.menus { + float: left; + font-size: 11px; + font-family: Verdana, Arial, Helvetica, sans-serif; +} +.menu { + text-align: left; + width: 140px; + color: white; + padding-bottom: 2px; + margin-bottom: 3px; + cursor: default; +} +div.styleswitcher { + font-family: Verdana, Arial, Helvetica, sans-serif; + color: #999999; + background-color: #333333; + width: 10px; + padding-bottom: 2px; + cursor: default; + margin-right: 1px; +} +.content { + color: #8EAEB0; + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 12px; + margin-bottom: 3px; +} +.news { + margin-bottom: 5px; +} +.newstitle { + font-weight: bold; + margin-right: 1px; +} +.newsdate { + font-size: 10px; + color: #DDDDDD; +} +table { + font-size: 10px; +} +.newstext { + font-size: 10px; +} +.title { + font-weight: bold; + font-size: 14px; +} +div.copywrite { + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 10px; + color: #666666; + cursor: default; + margin-left: 5%; +} +.misctext { + font-family: Verdana, Arial, Helvetica, sans-serif; + font-size: 10px; + color: #666666; + margin-top: 90px; +} +.tdhilite { + background-color: #12282D +} +.page { + color: #8EAEB0; + font-size: 13px; + font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif; + background-color: #091315; + padding: 15px; + border: 1px inset; + border-bottom: none; + height: 100%; +} +*/ + + diff --git a/utils/defaults/templates/menu.html b/utils/defaults/templates/menu.html new file mode 100644 index 000000000..46c714a43 --- /dev/null +++ b/utils/defaults/templates/menu.html @@ -0,0 +1,52 @@ + + + + +Untitled Document + + + + + + + + + + + + + + + + + diff --git a/utils/defaults/templates/minilogin.html b/utils/defaults/templates/minilogin.html new file mode 100644 index 000000000..f2ee37f02 --- /dev/null +++ b/utils/defaults/templates/minilogin.html @@ -0,0 +1,20 @@ + + + + + + + + + +

Mini-Login Management

+
+
+Minilogin: + Enabled + Disabled +
+... change IP addrs and stuff ... + + diff --git a/utils/defaults/templates/petitions.html b/utils/defaults/templates/petitions.html new file mode 100644 index 000000000..7dce8106b --- /dev/null +++ b/utils/defaults/templates/petitions.html @@ -0,0 +1,39 @@ + + + + + + + + + +

Petition Management

+
+query($q); + if ($res) { + print "\n"; + print "\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\n"; + while (my $row=$res->fetch_row_hash) { + print "\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\n"; + } + print "
AccountCharacterTextDate/Time
{accountname}\">$row->{accountname}$row->{charname}$row->{petitiontext}$row->{senttime}
\n"; + } else { + print "No petitions" + } +?> +Delete petitions + + diff --git a/utils/defaults/templates/players.html b/utils/defaults/templates/players.html new file mode 100644 index 000000000..fbc800df1 --- /dev/null +++ b/utils/defaults/templates/players.html @@ -0,0 +1,72 @@ + + + + + + + +get("zone", ""); + @chars = $EQW->ListPlayers($zone); +#( +# { character => "Joe", account => "yay", location_short => "arena", location_long => "Arena" }, +# { character => "Bob", account => "yay", location_short => "freportw", location_long => "West Freeport" }, +# { character => "Monkey", account => "notyay", location_short => "freportn", location_long => "North Freeport" } +# ); +?> + +

Online Player List

+
+ +
+ + + + + + + +GetPlayerDetails($charname); + if(!$char) { + $char->{character} = "ERROR: no char"; + } elsif($char->{error}) { + $char->{character} = "ERROR: ".$char->{error}; + } + print ""; + if($char->{character} eq "") { + print ""; + } else { + print ""; + } + print ""; + print ""; + print ""; + print ""; +} + +?> +
CharacterAccount NameLocationActions
Not Selected$char->{character}"; + if(defined($char->{guild_id}) && $char->{guild_id} > 0) { + print " <guild #$char->{guild_id}>"; + } + print "
"; + print "$char->{level} $char->{race} $char->{class}"; + print "
$char->{account}"; + if($char->{status} > 0) { + print " (status $char->{status})"; + } + print "
"; + print " $char->{ip}
$char->{location_long} ($char->{location_short})"; + print "Kick"; + print "
+ + diff --git a/utils/defaults/templates/reset.png b/utils/defaults/templates/reset.png new file mode 100644 index 0000000000000000000000000000000000000000..f10a94bd37f7459ade4eb7d9cf205daabee928a0 GIT binary patch literal 1305 zcmV+!1?KvRP)ybPgeej4{@3ZNJld@SR^j6@GiShH0tAwcVt`O#WywD1s-FlV-`HIjyVcH>U1To++SP_CO=l4y;R z66HB)A(5`lgi2(TbP3l1 z<^}_Jp;naeK&ePF~4! zO80)tl)PM4&M$i4Ao)WFv-`s(T`Oq%@TL1P43hyAVLPR|#Wm2m7Oq0U#qX4M6R3H2PqgQ6}(zsEquRO%M%7YY7E8yLirvtF*orR3d zd4k!c8(6$;C;3AMvvJXagmLjOU4g~dN9B1a$7%PR)=4+%x{7knY~R4zH|J4vxt_Ah zL$SN(M~#T4?CEo{w4Iqh8i1^aG62}N2qqflmXEHkzN3lee)e5%Vy}n$5_2!F4bkL zm@$5I!nped)4GE~M?I&7hiY%s$&}!u=Pt6N>M-xmDP-xK8LZzQRWBHh(7R_A6PK+b zWZRwMs!r5WU3-p#ykWdJW+byGj^(pAUgf8gr@M{2pQ%L)iL|Y##e|3|Cn8~#=lrt@ zR(*s!Z6S)MO(LgX9{`S=JrBUF31b*Lco20JO?KaFJ7TnRwl1MH<`Hw6REjU&x+mq2u3W9UAWAk{(V?H^972h7I3=Z zPgd>un&#F}w{ZZCfgqFfhB1D~lN|c>WIP$lb#a<+6TW!^?YXp^KV!tkui=Z9Y5>D$ zf?@u5_8P5Fu7}-no6xmCu$mf4%giEhsm|y$TMn83wl>q!@%jB2rVk7wg`qTBDU|2p z1nv;JeiiH1O$@^%biLv3*-}f1@WM#P#+UBLO!s4$MqCd`k>Oc`k^*T(aDxFNjW@8G z8UZ5)*5%5RQ&g-)Bn*ZToz;9jQU@xoQcK&yX=%m@v>+m(PKqU5RtHR-uBGE39Sh|N z&?E(Lx|Sms`h=-jm@k + + + + + + + + +

Rules Management

+
+query($q); + if ($res) { + print "\n"; + print "\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\t\n"; + #print "\t\t\n"; #delete column + print "\t\n"; + while (my $row = $res->fetch_row_hash) { + print "\t\n"; + print "\t\t\n"; + print "\t\t\n"; + + my $q2 = "SELECT COUNT(ruleset_id) AS num_rules FROM rule_values WHERE ruleset_id = '$row->{ruleset_id}'"; + my $res2 = $EQDB->query($q2); + my $row2 = $res2->fetch_row_hash; + + print "\t\t\n"; + #print "\t\t\n"; + print "\t\n"; + } + } else { + print "No rule sets"; + } +?> + +Todo: show active rule set, change active rule set + + diff --git a/utils/defaults/templates/rules.html b/utils/defaults/templates/rules.html new file mode 100644 index 000000000..20ea10565 --- /dev/null +++ b/utils/defaults/templates/rules.html @@ -0,0 +1,103 @@ + + + + + + + + + +

Rules Management

+
+
Note: changes won't go into effect until you restart the server.
+ +get("rule_name"); + if ($rule_name =~ m/\w+:\w+/i) { #if we have a value matching the pattern "Type:Name", we can assume we want to make the change + my $rule_value = $request->get("rule_value"); #not safe from injection, should be able to just escape quotes + my $q3 = "UPDATE rule_values SET rule_value = '$rule_value' WHERE rule_name = '$rule_name'"; + my $res3 = $EQDB->query($q3); + print "
\n\t"; + if ($res3) { # && ($EQDB->affected_rows > 0) + print "$rule_name successfully changed to '$rule_value'
\n"; + } else { + my $errno3 = $EQDB->get_errno; + my $err3 = $EQDB->error; + print "Update of $rule_name to '$rule_value' failed! (Error $errno3: $err3)
\n"; + } + print "
\n"; + } + #for some reason, when we submit a change, we go back to the default view + + my $ruleset_id = $request->get("ruleset_id", 1); #if we don't put a value, this gets defaulted to 0 no matter what we put for the default value using getInt, so we'll just do our own check + if ($ruleset_id !~ m/^[0-9]+$/) {$ruleset_id = 1;} #this should default any non-numeric values (particularly blank ones), also making it free from injections + my $rule_type = $request->get("rule_type", "All"); #not safe from injection as-is + if ($rule_type !~ m/^\w+$/i) {$rule_type = "All";} #this should make it safe from injection, but may cause issues if we start using non-alphanumeric characters in the first part of the rule_name + + #now, we'll put together a list of the rule categories that we can filter by + print "
\n\t"; + + if ($rule_type eq "All") {print "";} + print "All"; + if ($rule_type eq "All") {print "";} + + my $q = "SELECT DISTINCT(SUBSTRING(rule_name, 1, LOCATE(':', rule_name) - 1)) AS rule_type FROM rule_values WHERE ruleset_id = '$ruleset_id' ORDER BY rule_type ASC"; + my $res = $EQDB->query($q); + if ($res) { + while (my $row = $res->fetch_row_hash) { + print " | \n\t"; + if ($rule_type eq $row->{rule_type}) {print "";} + print "{rule_type}\">$row->{rule_type}"; + if ($rule_type eq $row->{rule_type}) {print "";} + } + } + + print "\n
\n"; + + #next, we create the table, including the first line which will let us put in a new rule from scratch (eventually) + print "
NameIDRules
{ruleset_id}\">$row->{name}{ruleset_id}\">$row->{ruleset_id}$row2->{num_rules}
\n"; + print "\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\t\n"; + #print "\t\t\n"; + print "\t\n"; + #print "\t\n"; + #print "\t\t\n"; + #print "\t\t\n"; + #print "\t\t\n"; + #print "\t\t\n"; + #print "\t\t\n"; + #print "\t\n"; + + #lastly, output any matching rules + my $w2 = "WHERE ruleset_id = '$ruleset_id'"; + if ($rule_type ne "All") { + $w2 .= " AND rule_name LIKE '$rule_type:%'"; + } + my $q2 = "SELECT ruleset_id, SUBSTRING(rule_name, 1, LOCATE(':', rule_name) - 1) AS rule_type, SUBSTRING(rule_name, LOCATE(':', rule_name) + 1) AS rule_name_short, rule_name, rule_value, notes FROM rule_values $w2 ORDER BY rule_name ASC"; + my $res2 = $EQDB->query($q2); + if ($res) { + while (my $row2 = $res2->fetch_row_hash) { + print "\t\n"; + print "\t\t\n"; + print "\t\t\n"; #if we don't pass this in POST, for some reason we don't pick them up from the GET info from the form action + print "\t\t\n"; #ditto + print "\t\t\n"; + print "\t\t\n"; + print "\t\t{rule_name}\">\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\t\n"; + #print "\t\t\n"; + print "\t\t\n"; + print "\t\n"; + } + } +?> + + diff --git a/utils/defaults/templates/spacer.gif b/utils/defaults/templates/spacer.gif new file mode 100644 index 0000000000000000000000000000000000000000..35d42e808f0a8017b8d52a06be2f8fec0b466a66 GIT binary patch literal 43 scmZ?wbhEHbWMp7uXkcLY|NlP&1B2pE7Dgb&paUX6G7L;iE{qJ;0LZEa`2YX_ literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/status.html b/utils/defaults/templates/status.html new file mode 100644 index 000000000..c35285894 --- /dev/null +++ b/utils/defaults/templates/status.html @@ -0,0 +1,17 @@ + + + + + + + + + +

Status Overview

+
+ + + + + diff --git a/utils/defaults/templates/submit.png b/utils/defaults/templates/submit.png new file mode 100644 index 0000000000000000000000000000000000000000..369ee1165dfd24e302672f24c1f390d9fe1a9f84 GIT binary patch literal 1305 zcmV+!1?KvRP)E!=aRx%S!Ab!Zgz~(lj;7ZbHrkWomy!QP@nO z%-WhIYF@e^Vm3)KLkrR%gmR`18#p&plj+>fb}sMjcj=F_oi=yY*>r@9p1;m}em|b~ zeLm0keSXjL!vBUyw_$W~snJq`F}*H0V+=<6$asvSB`ZX?A>9A&2BSR>>BP~V*E>^e z3|c9)?_#7vc@8HwmWZ?v9(;d`QJ#y8MUZwJkpIrz~iMoFr7Y!xXX zv;y#&!uZi3ZIpJB!Rv_r zN|3Mw!U~`qo57Q&84Ey5nWzCur7eJK%J-nPCimu>*i=-==AuIG9&+0iWpL;%gDBhm z9i#Gcd1dBf9o9$X<+5B z!I&;LL*M>#gy-J-h~uZvB$}yVd3?5PA+yGg={AH9zuwEfierhB>5gF?*0=6IK-IbP z$?FY!f8bbc;=Y3h^uO9B0*t{JlYn4aAau14I9y%J>{TT!DEow}vwtFlVE&^My8Rf< zahr(k0B}Rrbt%`|Dzoji5aOx;7}FYUD+KAITNlFI-Ll?R$7lVcc*d7|n%42mg-v_IZm9fCyM1 ztpDD;Y&6>QaT@E1{&5QXVjX?5`x36IY@hbLIWHQkPmn+;gs=i|RmJDpnIw%)D8LOj z5Is|iec?PpSVYg%betWGQb<3BavcJ}5LPgRutb})wU-T12DFkWJC4^BCSG?A`(hm+ zk^oYU(z(-~GBO?mArd`yAfxvM-)3l1j*Z)R2{+t`j7QrkAniw$oN*J4a$S^bqkRcR z_dWPpFq(|4Y{FHQ=|8GmZi6RHGum}~>GuSKkrL&5TsU;F(^&rkIze}UgdrEy P00000NkvXXu0mjfAoXUO literal 0 HcmV?d00001 diff --git a/utils/defaults/templates/top.html b/utils/defaults/templates/top.html new file mode 100644 index 000000000..b744adba7 --- /dev/null +++ b/utils/defaults/templates/top.html @@ -0,0 +1,22 @@ + + + +EQEmulator Management + + + + + +
TypeNameValueNotes
{rule_type}\">$row2->{rule_type}$row2->{rule_name_short}{rule_value}\" />$row2->{notes}
+ + + + + + + +
+  
+ + diff --git a/utils/defaults/templates/update.png b/utils/defaults/templates/update.png new file mode 100644 index 0000000000000000000000000000000000000000..589b26364bb7b6c123d673ffe4ed3d927b63ad9b GIT binary patch literal 1431 zcmV;I1!($-P))kwf_JB1tdvC zK~z}7?U-w9R7DiWe|ML*OJ7!ABBWSeQUn79X%#A{gl9nk!2l{DAtCq#1W6PnD2kvZ zr2QaGkfeqfA|-?(1Vug=5Tzg>V0i?gAR)38=q}y6_ce3J58Ey++foW7(8Tj;b7s!Y zJ^%Ti|I8Wq-_V+xkiD!>xk7+aEiO5w6iPUVu+Gtf&$MQP@XS|hm22AwGmLB7Epv;N z!j%%&u~0%HZIct<6lhI?Fl2SHlD37=s}V*RX__c0BmaqN!IamKwuuu8p`_qU$+udA zAk+e22Lm{v>$t)J*NsxEMc2Kip)j?d_MoAf| zt2zLb7v)o4lwWUwhJ4MjyN$nHeL(ZzjZZ-Hw!+av(J@b54G?-YcJ*~!$3`jlf!)$ex1tEJ` zp%P|91Zy?mZmgEt3)ZgZ9uG+kf{@pTIFexwUe}D3L2K4C4n(aT+uiIlW zabM1QmCbw0$ey>DX)6lp+4)gEpD`7HK64gE=hgmRFe#5w1N*UJV<}&3+`_cyhVbUN zkxgEYT*tw+tQt*F>aH(C4S`o!x`SPZj{z{WZ||r-_laHr{B-aLUvJwP6E}55Ar+S@ znLlwX%V$QC?~s<-=-Myz>kGiHKmUrZH+E3}roI?zMBfulK&jih2Uw2Nh+o9`f?w^%-lJ7BN*O4Le<^JPDK5C%1;6?BI`+Jj~N~l zr`IHsnckTxxlcEkG(eIsvH`ywI}Sizb`}G=XHZ_0&!)xm8^*wO5r&RcbroqENXNzt z;4j<#Nr!=hmuhH{TDgTC{JR2Qd>j)8XY=k$qsdI~#HL^OQCRvTZjCOyRU=B9_wHxW z7CxW-nc&y zB)3m5p6bz!(tUqWcJef7$w^FqK9|WuayW4M3?CM6AQ(1oMgZ5gv94Z0|Mw!wv8g(K z^adAu&zh?MG_MCV&pkOJ>ni#EZ>C&a+rg~5Lg=5f7?pnFl9CCWD!-BKiFE~)faYNQtzp2mlw(VO09jG!KDO + + + + + + + + +

Variable Settings

+
+get_all; +my $error=0; +if (defined($gets->{"update.x"})) { + $EQDB->query("delete from variables"); + my $max=$gets->{maxid}; + for(my $i=0;$i<$max;++$i) { + my ($varname,$info,$value) = ($gets->{"variable$i"},$gets->{"info$i"},$gets->{"value$i"}); + next if (!$varname); + $q=sprintf("replace into variables (varname,information,value) values('%s','%s','%s')",$EQDB->escape_string($varname),$EQDB->escape_string($info),$EQDB->escape_string($value)); + $EQDB->query($q); + if ($EQDB->get_errno) { + $error=1; + printf("Error adding variable %s: %s
\n",$varname,$EQDB->error); + } + } + if ($error==0) { + printf("

Variable settings updated, will take affect on server restart.


\n"); + } +} +?> +
+query($q); + if ($res) { + print "\n"; + printf "\t\t\n"; + printf "\t\t\n"; + printf "\t\t\n"; + printf "\t\n"; + + while(my $row=$res->fetch_row_hash) { + printf "\t\t\n",$row->{varname},$i,$i,$row->{varname}; + printf "\t\t\n",$row->{information},$i,$row->{information}; + printf "\t\t\n",$i,$row->{value},$i; + print "\t\n"; + ++$i; + } + } + printf("\n"); + printf("
VariableDescriptionValue
%s%s \"delete\"
\n",$i); + printf("\n",$i-1); +?> + +

+ + +

+ + + diff --git a/utils/defaults/templates/world.html b/utils/defaults/templates/world.html new file mode 100644 index 000000000..7f22f76e3 --- /dev/null +++ b/utils/defaults/templates/world.html @@ -0,0 +1,32 @@ + + + + + + + + + +

World Management

+
+ + +get("action", "none"); +if($act eq "unlock") { + $EQW->UnlockWorld(); +} elsif($act eq "lock") { + $EQW->LockWorld(); +} elsif($act eq "lsreconnect") { + $EQW->LSReconnect(); +} +?> +

+ +... some world info or something.... + + diff --git a/utils/defaults/templates/worldstatus.html b/utils/defaults/templates/worldstatus.html new file mode 100644 index 000000000..8c8a3966d --- /dev/null +++ b/utils/defaults/templates/worldstatus.html @@ -0,0 +1,23 @@ + + if($EQW->LSConnected()) { + my $ls = $EQW->GetConfig("LoginHost"); + my $lsp = $EQW->GetConfig("LoginPort"); + print "World is connected to login server $ls:$lsp.
"; + } else { + my $ls = $EQW->GetConfig("LoginHost"); + my $lsp = $EQW->GetConfig("LoginPort"); + print "World NOT connected to login server $ls:$lsp. Restart Auto Reconnect.
"; + } + + if($EQW->GetConfig("Locked") eq "true") { + print "World is locked. Unlock World.
"; + } else { + print "World is NOT locked. Lock World.
"; + } + + print "
\n"; + my $zcount = $EQW->CountZones(); + my $lcount = $EQW->CountLaunchers(1); + my $pcount = $EQW->CountPlayers(); + print "There are $zcount zones booted, $lcount launchers connected, and $pcount players online.
"; + diff --git a/utils/defaults/templates/zone.html b/utils/defaults/templates/zone.html new file mode 100644 index 000000000..fd5d6157d --- /dev/null +++ b/utils/defaults/templates/zone.html @@ -0,0 +1,17 @@ + + + + + + + + + +

Zone Server Details

+
+zone's IP, build date and version, what launcher is it under, +how many players are in the zone, link to player list, +does this zone have its map loaded, is perl enabled... + + diff --git a/utils/defaults/templates/zones.html b/utils/defaults/templates/zones.html new file mode 100644 index 000000000..a67d9daf5 --- /dev/null +++ b/utils/defaults/templates/zones.html @@ -0,0 +1,75 @@ + + + +Untitled Document + + + +ListBootedZones(); +# @zones = ( +# { type => "dynamic", short_name => "arena", long_name => "Arena", port => 8001 }, +# { type => "static", short_name => "freportw", long_name => "West Freeport", port => 8002 }, +# { type => "static", short_name => "freportn", long_name => "North Freeport", port => 8003 } +# ); + +?> + + +

Zone List

+
+ +
+ +
+ + + + + + + + +GetZoneDetails($zonekey); + if(!$zone) { + $zone->{long_name} = "ERROR: no zone"; + } elsif($zone->{error}) { + $zone->{long_name} = "ERROR: $zone->{error}"; + } + + print "\n"; + if($zone->{type} eq "dynamic") { + if($zone->{zone_id} == 0) { + print "\t"; + } else { + print "\t"; + } + } else { + print ""; + } + print ""; + print ""; + print ""; + print "\n"; + print "\n"; + } +?> +
Zone Name PortPlayersLog File Actions
$zone->{launched_name}: Idle$zone->{launched_name}: $zone->{long_name} ($zone->{short_name})$zone->{long_name} ($zone->{short_name})$zone->{port}PlayersView Log"; + print "Restart - "; + print "Kill"; + print "
+ + + + + + diff --git a/utils/defaults/updated_templates/access.txt b/utils/defaults/updated_templates/access.txt new file mode 100644 index 000000000..36ffb2a52 --- /dev/null +++ b/utils/defaults/updated_templates/access.txt @@ -0,0 +1,8 @@ +bugview.html 0 +playerview.html 0 +zoneview.html 0 +scripts/jquery.js 0 +style/style.css 0 +data/bug_data.html 0 +data/player_data.html 0 +data/zone_data.html 0 \ No newline at end of file diff --git a/utils/defaults/updated_templates/actions/bug_action.html b/utils/defaults/updated_templates/actions/bug_action.html new file mode 100644 index 000000000..8cfea78f1 --- /dev/null +++ b/utils/defaults/updated_templates/actions/bug_action.html @@ -0,0 +1,25 @@ +get("action", "none"); + my $bug_id = $request->get("bug_id", "-1"); + + if($action eq "resolve") { + if($bug_id != -1) { + $EQW->ResolveBug($bug_id); + print "{"; + print "\"status\": 1,"; + print "\"message\": \"\""; + print "}"; + } else { + print "{"; + print "\"status\": 0,"; + print "\"message\": \"Invalid bug id.\""; + print "}"; + } + } else { + print "{"; + print "\"status\": 0,"; + print "\"message\": \"Invalid action.\""; + print "}"; + } + +?> \ No newline at end of file diff --git a/utils/defaults/updated_templates/actions/console_action.html b/utils/defaults/updated_templates/actions/console_action.html new file mode 100644 index 000000000..6213b6d0f --- /dev/null +++ b/utils/defaults/updated_templates/actions/console_action.html @@ -0,0 +1,25 @@ +get("action", "none"); + my $text = $request->get("text", ""); + if($action eq "input") { + my $rep = $EQW->SendConsoleMessage($text); + if($rep ne "") { + print "{"; + print "\"status\" : 0,"; + print "\"message\" : \"$rep\""; + print "}"; + return; + } + } else { + print "{"; + print "\"status\" : 0,"; + print "\"message\" : \"Unknown action sent to console.\""; + print "}"; + return; + } + + print "{"; + print "\"status\" : 1,"; + print "\"message\" : \"\""; + print "}"; +?> \ No newline at end of file diff --git a/utils/defaults/updated_templates/actions/launcher_action.html b/utils/defaults/updated_templates/actions/launcher_action.html new file mode 100644 index 000000000..b4a925d9b --- /dev/null +++ b/utils/defaults/updated_templates/actions/launcher_action.html @@ -0,0 +1,129 @@ +get("action", "none"); + my $launcher_name = $request->get("launcher_name", "none"); + if($launcher_name eq "" || $launcher_name eq "none") { + print "{"; + print "\"status\" : 0, "; + print "\"message\" : \"Missing launcher name in Remove action\""; + print "}"; + return; + } + + if($action eq "add") { + my $dynamic_count = $request->get("dynamic_count", "0"); + if($dynamic_count < 0 || $dynamic_count > 254) { + print "{"; + print "\"status\" : 0, "; + print "\"message\" : \"Invalid dynamics count in Add action\""; + print "}"; + return; + } else { + $EQW->CreateLauncher($launcher_name, $dynamic_count); + } + } elsif($action eq "remove") { + my $l = $EQW->GetLauncher($launcher_name); + if(!$l) { + print "{"; + print "\"status\" : 0, "; + print "\"message\" : \"Launcher not found during Remove action\""; + print "}"; + return; + } + $l->DeleteLauncher(); + } elsif($action eq "boot") { + my $zone = $request->get("zone", "none"); + my $port = $request->get("port", "0"); + if($zone eq "none" || $zone eq "") { + print "{"; + print "\"status\" : 0, "; + print "\"message\" : \"Missing zone name in Boot action\""; + print "}"; + return; + } + + if($port < 0 || $port > 65535) { + print "{"; + print "\"status\" : 0, "; + print "\"message\" : \"Port out of range in Boot action\""; + print "}"; + return; + } + + if(!$config->BootStaticZone($zone, $port)) { + print "{"; + print "\"status\" : 0, "; + print "\"message\" : \"Failed to boot '$zone' on launcher $launcher_name with port $port. Invalid zone?\""; + print "}"; + return; + } + } elsif($action eq "change_dynamic_count") { + my $dynamic_count = $request->get("dynamic_count", "0"); + $config->SetDynamicCount($dynamic_count); + } elsif($action eq "remove_zone") { + my $zone = $request->get("zone", "none"); + if($zone eq "none" || $zone eq "") { + print "{"; + print "\"status\" : 0, "; + print "\"message\" : \"Invalid zone name in Remove Zone action.\""; + print "}"; + return; + } else { + if(!$config->DeleteStaticZone($zone)) { + print "{"; + print "\"status\" : 0, "; + print "\"message\" : \"Failed to remove '$zone' on launcher $launcher_name. Invalid zone?\""; + print "}"; + return; + } + } + } elsif($action eq "reboot_all") { + foreach my $z($config->ListZones()) { + $config->RestartZone($z); + } + } elsif($action eq "stop_all") { + foreach my $z($config->ListZones()) { + $config->StopZone($z); + } + } elsif($action eq "start_all") { + foreach my $z($config->ListZones()) { + $config->StartZone($z); + } + } elsif($action eq "restart_zone") { + my $zone = $request->get("zone", "none"); + if($zone eq "none" || $zone eq "") { + print "{"; + print "\"status\" : 0, "; + print "\"message\" : \"Invalid zone name in Restart Zone action.\""; + print "}"; + return; + } else { + $config->RestartZone($zone); + } + } elsif($action eq "start_zone") { + my $zone = $request->get("zone", "none"); + if($zone eq "none" || $zone eq "") { + print "{"; + print "\"status\" : 0, "; + print "\"message\" : \"Invalid zone name in Start Zone action.\""; + print "}"; + return; + } else { + $config->StartZone($zone); + } + } elsif($action eq "stop_zone") { + my $zone = $request->get("zone", "none"); + if($zone eq "none" || $zone eq "") { + print "{"; + print "\"status\" : 0, "; + print "\"message\" : \"Invalid zone name in Stop Zone action.\""; + print "}"; + return; + } else { + $config->StopZone($zone); + } + } + + print "{"; + print "\"status\" : 1"; + print "}"; +?> \ No newline at end of file diff --git a/utils/defaults/updated_templates/actions/world_action.html b/utils/defaults/updated_templates/actions/world_action.html new file mode 100644 index 000000000..6d50b239b --- /dev/null +++ b/utils/defaults/updated_templates/actions/world_action.html @@ -0,0 +1,23 @@ +get("action", "none"); + if($act eq "unlock") { + $EQW->UnlockWorld(); + } elsif($act eq "lock") { + $EQW->LockWorld(); + } elsif($act eq "lsreconnect") { + $EQW->LSReconnect(); + } + + print "{"; + if($EQW->LSConnected()) { + print "\"connected\" : \"1\","; + } else { + print "\"connected\" : \"0\","; + } + if($EQW->GetConfig("Locked") eq "true") { + print "\"locked\" : \"1\""; + } else { + print "\"locked\" : \"0\""; + } + print "}"; +?> \ No newline at end of file diff --git a/utils/defaults/updated_templates/actions/zone_action.html b/utils/defaults/updated_templates/actions/zone_action.html new file mode 100644 index 000000000..5813cfb2f --- /dev/null +++ b/utils/defaults/updated_templates/actions/zone_action.html @@ -0,0 +1,16 @@ +get("action", "none"); + if($act eq "kill") { + my $short_name = $request->get("short_name", "none"); + my $instance_id = $request->get("instance_id", "none"); + } elsif($act eq "restart") { + my $short_name = $request->get("short_name", "none"); + my $instance_id = $request->get("instance_id", "none"); + } elsif($act eq "killall") { + } elsif($act eq "restartall") { + } + + print "{"; + print "\"status\" : 1"; + print "}"; +?> \ No newline at end of file diff --git a/utils/defaults/updated_templates/blank.temp.html b/utils/defaults/updated_templates/blank.temp.html new file mode 100644 index 000000000..f8e59f802 --- /dev/null +++ b/utils/defaults/updated_templates/blank.temp.html @@ -0,0 +1,22 @@ + + + + EQEmu + + + + + +
+ +
+
+
+ + + diff --git a/utils/defaults/updated_templates/bugs.html b/utils/defaults/updated_templates/bugs.html new file mode 100644 index 000000000..5f2dc9703 --- /dev/null +++ b/utils/defaults/updated_templates/bugs.html @@ -0,0 +1,157 @@ + + + + EQEmu + + + + +ListBugs(0); + $bug_count = $EQW->CountBugs(); +?> + +
+ +
+

Bugs

+
+ $name "; + } + else { + print "$name "; + } + } + ?> +
+ + + + + + + + + GetBugDetails($bugkey); + if(!$bug) { + next; + } + + if($i % 2 == 1) { + print ""; + } else { + print ""; + } + + my $bug_val = $bug->{bug}; + $bug_val =~ s/\r//g; + $bug_val =~ s/\n/
/g; + print ""; + print ""; + print ""; + print ""; + print ""; + print ""; + $i++; + } + ?> +
ActionsReporterWhereTargetDescripton
Resolve$bug->{name}$bug->{zone}: ($bug->{x}, $bug->{y}, $bug->{z})$bug->{target}$bug_val
+
+
+ + + diff --git a/utils/defaults/updated_templates/bugview.html b/utils/defaults/updated_templates/bugview.html new file mode 100644 index 000000000..b41ee29c9 --- /dev/null +++ b/utils/defaults/updated_templates/bugview.html @@ -0,0 +1,141 @@ + + + + EQEmu + + + + +ListBugs(0); + $bug_count = $EQW->CountBugs(); +?> + +
+ +
+

Bugs

+
+ $name "; + } + else { + print "$name "; + } + } + ?> +
+ + + + + + + + GetBugDetails($bugkey); + if(!$bug) { + next; + } + + if($i % 2 == 1) { + print ""; + } else { + print ""; + } + + my $bug_val = $bug->{bug}; + $bug_val =~ s/\r//g; + $bug_val =~ s/\n/
/g; + print ""; + print ""; + print ""; + print ""; + print ""; + $i++; + } + ?> +
ReporterWhereTargetDescripton
$bug->{name}$bug->{zone}: ($bug->{x}, $bug->{y}, $bug->{z})$bug->{target}$bug_val
+
+
+ + + diff --git a/utils/defaults/updated_templates/console.html b/utils/defaults/updated_templates/console.html new file mode 100644 index 000000000..e0bb46256 --- /dev/null +++ b/utils/defaults/updated_templates/console.html @@ -0,0 +1,138 @@ + + + + EQEmu + + + + + +
+ +
+
+ + + +
+
+
+ + + diff --git a/utils/defaults/updated_templates/data/bug_data.html b/utils/defaults/updated_templates/data/bug_data.html new file mode 100644 index 000000000..52cb5a4a9 --- /dev/null +++ b/utils/defaults/updated_templates/data/bug_data.html @@ -0,0 +1,44 @@ +get("offset", "0"); + my @bugs = $EQW->ListBugs($offset); + my $bug_count = @bugs; + my $total_bug_count = $EQW->CountBugs(); + print "{"; + print "\"total_bug_count\" : $total_bug_count,"; + print "\"bug_count\" : $bug_count,"; + print "\"bugs\" : "; + print "["; + my $i = 0; + for my $bugkey (@bugs) { + my $bug = $EQW->GetBugDetails($bugkey); + + my $bug_val = $bug->{bug}; + $bug_val =~ s/\\/\\\\/g; + $bug_val =~ s/\//\\\//g; + $bug_val =~ s/\"/\\\"/g; + $bug_val =~ s/\n/
/g; + $bug_val =~ s/\r/\\r/g; + $bug_val =~ s/\t/\\t/g; + $bug_val =~ s/\x08/\\f/g; + $bug_val =~ s/\x0C/\\b/g; + $bug_val =~ s/[\x00-\x1F]/\./g; + $bug_val =~ s/[\x7F-\xFF]/\./g; + print "{"; + print "\"id\" : \"$bug->{id}\","; + print "\"name\" : \"$bug->{name}\","; + print "\"zone\" : \"$bug->{zone}\","; + print "\"target\" : \"$bug->{target}\","; + print "\"bug\" : \"$bug_val\","; + print "\"x\" : \"$bug->{x}\","; + print "\"y\" : \"$bug->{y}\","; + print "\"z\" : \"$bug->{z}\""; + + print "}"; + if($i != $bug_count - 1) { + print ","; + } + $i++; + } + print "]"; + print "}"; +?> \ No newline at end of file diff --git a/utils/defaults/updated_templates/data/console_data.html b/utils/defaults/updated_templates/data/console_data.html new file mode 100644 index 000000000..99a3271ff --- /dev/null +++ b/utils/defaults/updated_templates/data/console_data.html @@ -0,0 +1,31 @@ +CountConsoleMessages(); + print "{"; + print "\"message_count\" : $message_count,"; + print "\"messages\" : "; + print "["; + for(my $i = 0; $i < $message_count; $i++) + { + print "{"; + my $msg = $EQW->GetConsoleMessage($i); + $message = $msg->{message}; + $message =~ s/\\/\\\\/g; + $message =~ s/\//\\\//g; + $message =~ s/\"/\\\"/g; + $message =~ s/\n/\\n/g; + $message =~ s/\r/\\r/g; + $message =~ s/\t/\\t/g; + $message =~ s/\x08/\\f/g; + $message =~ s/\x0C/\\b/g; + $message =~ s/[\x00-\x1F]/\./g; + $message =~ s/[\x7F-\xFF]/\./g; + + print "\"message\" : \"$message\""; + print "}"; + if($i != $message_count - 1) { + print ","; + } + } + print "]"; + print "}"; +?> \ No newline at end of file diff --git a/utils/defaults/updated_templates/data/launcher_count.html b/utils/defaults/updated_templates/data/launcher_count.html new file mode 100644 index 000000000..b737e9cbd --- /dev/null +++ b/utils/defaults/updated_templates/data/launcher_count.html @@ -0,0 +1,6 @@ +CountLaunchers(1); + print "{"; + print "\"launcher_count\" : " . $launcher_count; + print "}"; +?> \ No newline at end of file diff --git a/utils/defaults/updated_templates/data/launcher_data.html b/utils/defaults/updated_templates/data/launcher_data.html new file mode 100644 index 000000000..9ce4d548b --- /dev/null +++ b/utils/defaults/updated_templates/data/launcher_data.html @@ -0,0 +1,69 @@ +get("launcher", "none"); + + if($launcher_name eq "none") { + @launchers = sort $EQW->ListLaunchers(); + my $launcher_count = @launchers; + print "{"; + print "\"launcher_count\" : $launcher_count,"; + print "\"launchers\" : "; + print "["; + + my $i = 0; + for my $lk (@launchers) { + my $l = $EQW->GetLauncher($lk); + print "{"; + print "\"name\" : \"". $l->GetName() ."\","; + print "\"static_count\" : ". $l->GetStaticCount() .","; + print "\"dynamic_count\" : ". $l->GetDynamicCount() .","; + $conn = 0; + if($l->IsConnected()) { + $conn = 1; + } + print "\"connected\" : ". $conn; + print "}"; + + if($i != $launcher_count - 1) { + print ","; + } + $i++; + } + + print "]"; + print "}"; + } else { + my $l = $EQW->GetLauncher($lname); + print "{"; + print "\"name\" : \"". $l->GetName() ."\","; + print "\"static_count\" : ". $l->GetStaticCount() .","; + print "\"dynamic_count\" : ". $l->GetDynamicCount() .","; + $conn = 0; + if($l->IsConnected()) { + $conn = 1; + } + print "\"connected\" : ". $conn . ","; + my @zones = $l->ListZones(); + my $zone_count = @zones; + print "\"zone_count\" : ". $zone_count . ","; + print "\"zones\" : "; + print "["; + my $i = 0; + for my $zone (@zones) { + my $z = $config->GetZoneDetails($zone); + print "{"; + print "\"name\" : \"$z->{name}\","; + print "\"up\" : $z->{up},"; + print "\"starts\" : $z->{starts},"; + print "\"port\" : $z->{port}"; + print "}"; + + if($i != $zone_count - 1) { + print ", "; + } + $i++; + } + + print "]"; + print "}"; + } +?> \ No newline at end of file diff --git a/utils/defaults/updated_templates/data/player_count.html b/utils/defaults/updated_templates/data/player_count.html new file mode 100644 index 000000000..ef4aa99e0 --- /dev/null +++ b/utils/defaults/updated_templates/data/player_count.html @@ -0,0 +1,6 @@ +CountPlayers(); + print "{"; + print "\"player_count\" : " . $player_count; + print "}"; +?> \ No newline at end of file diff --git a/utils/defaults/updated_templates/data/player_data.html b/utils/defaults/updated_templates/data/player_data.html new file mode 100644 index 000000000..9220792c4 --- /dev/null +++ b/utils/defaults/updated_templates/data/player_data.html @@ -0,0 +1,42 @@ +get("zone", ""); + $instance_id = $request->get("instance_id", "0"); + my @players = $EQW->ListPlayers($zone, $instance_id); + my $player_count = @players; + print "{"; + print "\"player_count\" : $player_count,"; + print "\"players\" : "; + print "["; + + my $i = 0; + for my $player (@players) { + my $pd = $EQW->GetPlayerDetails($player); + + print "{"; + print "\"character\" : \"$pd->{character}\","; + print "\"account\" : \"$pd->{account}\","; + print "\"account_id\" : \"$pd->{account_id}\","; + print "\"location_short\" : \"$pd->{location_short}\","; + print "\"location_long\" : \"$pd->{location_long}\","; + print "\"ip\" : \"$pd->{ip}\","; + print "\"level\" : \"$pd->{level}\","; + print "\"race\" : \"$pd->{race}\","; + print "\"race_id\" : \"$pd->{race_id}\","; + print "\"class\" : \"$pd->{class}\","; + print "\"class_id\" : \"$pd->{class_id}\","; + print "\"guild_id\" : \"$pd->{guild_id}\","; + print "\"guild\" : \"$pd->{guild}\","; + print "\"status\" : \"$pd->{status}\","; + print "\"client_version\" : \"$pd->{client_version}\""; + print "}"; + + if($i != $player_count - 1) { + print ","; + } + $i++; + } + + print "]"; + print "}"; + +?> \ No newline at end of file diff --git a/utils/defaults/updated_templates/data/world_status.html b/utils/defaults/updated_templates/data/world_status.html new file mode 100644 index 000000000..47551445c --- /dev/null +++ b/utils/defaults/updated_templates/data/world_status.html @@ -0,0 +1,14 @@ +LSConnected()) { + print "\"connected\" : \"1\","; + } else { + print "\"connected\" : \"0\","; + } + if($EQW->GetConfig("Locked") eq "true") { + print "\"locked\" : \"1\""; + } else { + print "\"locked\" : \"0\""; + } + print "}"; +?> \ No newline at end of file diff --git a/utils/defaults/updated_templates/data/zone_count.html b/utils/defaults/updated_templates/data/zone_count.html new file mode 100644 index 000000000..19357b55f --- /dev/null +++ b/utils/defaults/updated_templates/data/zone_count.html @@ -0,0 +1,6 @@ +CountZones(); + print "{"; + print "\"zone_count\" : " . $zone_count; + print "}"; +?> \ No newline at end of file diff --git a/utils/defaults/updated_templates/data/zone_data.html b/utils/defaults/updated_templates/data/zone_data.html new file mode 100644 index 000000000..11b27ed29 --- /dev/null +++ b/utils/defaults/updated_templates/data/zone_data.html @@ -0,0 +1,33 @@ +ListBootedZones(); +@zones = sort @zones; +$zone_count = @zones; + +print "{"; +print "\"zone_count\" : $zone_count,"; +print "\"zones\" : "; +print "["; + +for(my $i = 0; $i < $zone_count; $i++) { + my $zone = $EQW->GetZoneDetails($zones[$i]); + print "{"; + print "\"type\" : \"$zone->{type}\","; + print "\"zone_id\" : $zone->{zone_id},"; + print "\"launch_name\" : \"$zone->{launch_name}\","; + print "\"short_name\" : \"$zone->{short_name}\","; + print "\"long_name\" : \"$zone->{long_name}\","; + print "\"port\" : $zone->{port},"; + print "\"player_count\" : $zone->{player_count},"; + print "\"instance_id\" : $zone->{instance_id}"; + print "}"; + + if($i != $zone_count - 1) { + print ","; + } +} + +print "]"; +print "}"; + +?> \ No newline at end of file diff --git a/utils/defaults/updated_templates/images/loading.gif b/utils/defaults/updated_templates/images/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..d0bce1542342e912da81a2c260562df172f30d73 GIT binary patch literal 673 zcmZ?wbhEHb6krfw_{6~Q|Nnmm28Kh24mmkF0U1e2Nli^nlO|14{Lk&@8WQa67~pE8 zXTZz|lvDgC+Z`3#dv5h=E26FfcG1 zbL_hF&)}42ws10s6^G;;cE1^EoUR)U5A70}d2pLv!jVIT7j&Z~EblI3x0K*v_sV|m z0kj3v921Z^em#l`(k(o@H$3ZdDRc@9NidXDNbqrumReCGv$gd8+e8WW28HVqkJ_9i zH>s*<31KtHjANIPvi2#*6BEu%3Dak5O_t&NBI)H?V$TxT}#l{vOTn5naXTfF^&~Hhq+NX@#Ccc>y7T?;vjI&jdhsDsPJyAw*m0Qz>i}K7# zL9w50Ng{fT}A5JUe8lRK1h7_Y2;BWJDd=c6f&i?Wv5(5q?6|P zQw{>maxZP<537OA37Uk}7@%_$4o$EWe_Zl>&#id|lE-BpDC#+Fn|msJ%_2h{Hg1vP z#N8WAzfWasG}yq|xqE)DrWaOofX=z|?*pgc%{ig5vl!pqDlC|q&~Z0$&Rvsft&VO- z4MZj+%-+Vx%W}v;V76hyp=;+R;x+~t^Q%*xuFTQAF2})fSfTHDAs>sO!OBw`)&)o$ c0!CNZt))x~rAZP^^P&YOFfdqy5)K#u0POD40{{R3 literal 0 HcmV?d00001 diff --git a/utils/defaults/updated_templates/index.html b/utils/defaults/updated_templates/index.html new file mode 100644 index 000000000..619674448 --- /dev/null +++ b/utils/defaults/updated_templates/index.html @@ -0,0 +1,191 @@ + + + + EQEmu + + + + + +
+ +
+ CountZones(); + my $lcount = $EQW->CountLaunchers(1); + my $pcount = $EQW->CountPlayers(); + + print ""; + print ""; + print ""; + print ""; + print ""; + print "
Zones Booted"; + print "Launchers Connected"; + print "Players Online"; + print "
$zcount"; + print "$lcount"; + print "$pcount"; + print "

"; + + if($EQW->LSConnected()) { + my $ls = $EQW->GetConfig("LoginHost"); + my $lsp = $EQW->GetConfig("LoginPort"); + $ls_str = "World is connected to login server $ls:$lsp.

"; + print ""; + } else { + my $ls = $EQW->GetConfig("LoginHost"); + my $lsp = $EQW->GetConfig("LoginPort"); + $ls_str = "World is NOT connected to login server $ls:$lsp.
Restart Auto Reconnect
"; + } + print "
"; + print "$ls_str"; + print "

"; + + if($EQW->GetConfig("Locked") eq "true") { + $locked_str = "World is locked. Unlock World.
"; + print ""; + } else { + $locked_str = "World is NOT locked. Lock World.
"; + } + + print "
"; + print "$locked_str"; + print "
"; + ?> +
+
+ + + diff --git a/utils/defaults/updated_templates/launcher.html b/utils/defaults/updated_templates/launcher.html new file mode 100644 index 000000000..0cc2c3121 --- /dev/null +++ b/utils/defaults/updated_templates/launcher.html @@ -0,0 +1,317 @@ + + + + EQEmu + + + + + +
+ +
+ GetLauncher($lname); + if(!$config) { + print "

Unable to find launcher $lname

"; + } + ?> + +
+ + + + + + + + + + + + + + + + + + + ListZones(); + + for my $zone (sort @zones) { + my $z = $config->GetZoneDetails($zone); + if(!$z) { + $zone->{name} = "ERROR: no launcher"; + } elsif($z->{error}) { + $zone->{name} = "ERROR: $z->{error}"; + } + + print "\n"; + print "\t"; + if($z->{up}) { + print ""; + } else { + print ""; + } + if($z->{port} == 0) { + print ""; + } else { + print ""; + } + print ""; + print "\n"; + print "\n"; + } + ?> +
Zone NameStatePortStart CountActions
$z->{name}UPDOWNDynamic$z->{port}$z->{starts}"; + if($config->IsConnected()) { + print "Restart - "; + if($z->{up}) { + print "Stop"; + } else { + print "Start"; + } + if($z->{name} !~ /dynamic/) { + print " - "; + } + } + if($z->{name} !~ /dynamic/) { + print "Remove"; + } + print "
+
+
+ + + diff --git a/utils/defaults/updated_templates/launchers.html b/utils/defaults/updated_templates/launchers.html new file mode 100644 index 000000000..b7b8eb0c1 --- /dev/null +++ b/utils/defaults/updated_templates/launchers.html @@ -0,0 +1,166 @@ + + + + EQEmu + + + + +ListLaunchers(); +?> + +
+ +
+

Launchers

+ + + + + + +GetLauncher($lk); + if(!$l) { + next; + } + print "\n"; + if($l->IsConnected()) { + print "\t"; + } else { + print ""; + } + print ""; + print "\n"; + print "\n"; + } +?> +
Launcher Name Zone CountActions
".$l->GetName()."".$l->GetName()."
Not Connected
".$l->GetStaticCount()."s, ".$l->GetDynamicCount()."d"; + print "Details - "; + print "Remove"; + print "
+ +
+ + + + + + +
+
+ + + diff --git a/utils/defaults/updated_templates/petitions.html b/utils/defaults/updated_templates/petitions.html new file mode 100644 index 000000000..45c0bc3cf --- /dev/null +++ b/utils/defaults/updated_templates/petitions.html @@ -0,0 +1,43 @@ + + + + EQEmu + + + + +
+ +
+

Petition Management

+ query($q); + if ($res) { + print "\n"; + print "\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\n"; + while (my $row=$res->fetch_row_hash) { + print "\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\t\n"; + print "\t\n"; + } + print "
AccountCharacterTextDate/Time
{accountname}\">$row->{accountname}$row->{charname}$row->{petitiontext}$row->{senttime}
\n"; + } else { + print "No petitions" + } + ?> +
+
+ + + diff --git a/utils/defaults/updated_templates/players.html b/utils/defaults/updated_templates/players.html new file mode 100644 index 000000000..cbf67b677 --- /dev/null +++ b/utils/defaults/updated_templates/players.html @@ -0,0 +1,142 @@ + + +get("zone", ""); + $instance_id = $request->get("instance_id", "0"); + @chars = $EQW->ListPlayers($zone, $instance_id); +#( +# { character => "Joe", account => "yay", location_short => "arena", location_long => "Arena" }, +# { character => "Bob", account => "yay", location_short => "freportw", location_long => "West Freeport" }, +# { character => "Monkey", account => "notyay", location_short => "freportn", location_long => "North Freeport" } +# ); +?> + + EQEmu + + + + + +
+ +
+

Online Player List

+
+ ".($#chars+1)." players logged in."; + } else { + print "There are ".($#chars+1)." players in $zone."; + } + ?> +
+
+ + + + + + + + GetPlayerDetails($charname); + if(!$char) { + $char->{character} = "ERROR: no char"; + } elsif($char->{error}) { + $char->{character} = "ERROR: ".$char->{error}; + } + print "{character}\">"; + if($char->{character} eq "") { + print ""; + } else { + print ""; + } + print ""; + print ""; + print ""; + } + ?> +
CharacterAccount NameLocation
Not Selected$char->{character}"; + if(defined($char->{guild_id}) && $char->{guild_id} > 0) { + print " <$char->{guild}>"; + } + print "
"; + print "$char->{level} $char->{race} $char->{class}"; + print "
$char->{account}"; + if($char->{status} > 0) { + print " (status $char->{status})"; + } + print "
"; + print " $char->{ip}
$char->{location_long} ($char->{location_short})
+
+
+ + + diff --git a/utils/defaults/updated_templates/playerview.html b/utils/defaults/updated_templates/playerview.html new file mode 100644 index 000000000..38acc05a6 --- /dev/null +++ b/utils/defaults/updated_templates/playerview.html @@ -0,0 +1,121 @@ + + +get("zone", ""); + $instance_id = $request->get("instance_id", "0"); + @chars = $EQW->ListPlayers($zone, $instance_id); +#( +# { character => "Joe", account => "yay", location_short => "arena", location_long => "Arena" }, +# { character => "Bob", account => "yay", location_short => "freportw", location_long => "West Freeport" }, +# { character => "Monkey", account => "notyay", location_short => "freportn", location_long => "North Freeport" } +# ); +?> + + EQEmu + + + + + +
+ +
+

Online Player List

+
+ ".($#chars+1)." players logged in."; + } else { + print "There are ".($#chars+1)." players in $zone."; + } + ?> +
+
+ + + + + + + GetPlayerDetails($charname); + if(!$char) { + $char->{character} = "ERROR: no char"; + } elsif($char->{error}) { + $char->{character} = "ERROR: ".$char->{error}; + } + print "{character}\">"; + if($char->{character} eq "") { + print ""; + } else { + print ""; + } + print ""; + print ""; + } + ?> +
CharacterLocation
Not Selected$char->{character}"; + if(defined($char->{guild_id}) && $char->{guild_id} > 0) { + print " <$char->{guild}>"; + } + print "
"; + print "$char->{level} $char->{race} $char->{class}"; + print "
$char->{location_long} ($char->{location_short})
+
+
+ + + diff --git a/utils/defaults/updated_templates/scripts/jquery.js b/utils/defaults/updated_templates/scripts/jquery.js new file mode 100644 index 000000000..93adea19f --- /dev/null +++ b/utils/defaults/updated_templates/scripts/jquery.js @@ -0,0 +1,4 @@ +/*! jQuery v1.7.2 jquery.com | jquery.org/license */ +(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"":"")+""),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;e=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="
"+""+"
",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="
t
",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="
",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function( +a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f +.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(;d1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]===""&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file diff --git a/utils/defaults/updated_templates/scripts/menu.pl b/utils/defaults/updated_templates/scripts/menu.pl new file mode 100644 index 000000000..0016df659 --- /dev/null +++ b/utils/defaults/updated_templates/scripts/menu.pl @@ -0,0 +1,18 @@ +print("
"); +print("
    "); +print("
  • "); +print(" World"); +print(" "); +print("
  • "); +print("
  • "); +print(" Console"); +print("
  • "); +print("
  • "); +print(" Bugs"); +print("
  • "); +print("
"); +print("
"); \ No newline at end of file diff --git a/utils/defaults/updated_templates/scripts/menu_noaccess.pl b/utils/defaults/updated_templates/scripts/menu_noaccess.pl new file mode 100644 index 000000000..a6e737edc --- /dev/null +++ b/utils/defaults/updated_templates/scripts/menu_noaccess.pl @@ -0,0 +1,13 @@ +print("
"); +print("
    "); +print("
  • "); +print(" Zones"); +print("
  • "); +print("
  • "); +print(" Players"); +print("
  • "); +print("
  • "); +print(" Bugs"); +print("
  • "); +print("
"); +print("
"); \ No newline at end of file diff --git a/utils/defaults/updated_templates/style/style.css b/utils/defaults/updated_templates/style/style.css new file mode 100644 index 000000000..42b826f3a --- /dev/null +++ b/utils/defaults/updated_templates/style/style.css @@ -0,0 +1,268 @@ +body, html { + background-color: rgba(206, 227, 248, 1.0); + font-size:12px; + font-family: Arial, sans-serif; + padding: 0; + margin: 0; +} + +div.container { + width: 90%; + min-width: 800px; + min-height: 900px; + display: block; + background-color: #FFFFFF; + margin-left: auto; + margin-right: auto; + margin-top: 0; + border-radius: 0px 0px 15px 15px; + box-shadow: 0px 0px 20px rgba(0,0,0,0.18); + border-left: 1px solid rgba(95, 153, 207, 1.0); + border-bottom: 1px solid rgba(95, 153, 207, 1.0); + border-right: 1px solid rgba(95, 153, 207, 1.0); +} + +div.header { + text-align: center; + font-size: 20px; +} + +div.nav { + height: 2.25em; + background-color: rgba(80, 80, 80, 1.0); + background: -moz-linear-gradient(top, rgba(120,120,120,1.0), rgba(65,65,65,1.0)); + background: -webkit-gradient(linear, left top, left bottom, from(rgba(120,120,120,1.0)), to(rgba(65,65,65,1.0))); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#787878', endColorstr='#414141'); + border-top: 0; + padding-left: 1.0em; + padding-right: 1.0em; + text-align: left; + color: #333; + border-top: 1px solid rgba(141, 141, 141, 1.0); +} + +div.user_status { + font-size: 10px; + text-align: right; + padding: 0.5em; + padding-left: 1.0em; + background: rgba(239, 247, 255, 1.0); + display: block; + float: right; + border-radius: 0px 0px 0px 6px; +} + +div.user_status a { + color: rgba(51, 102, 153, 1.0); + text-decoration: none; + font-weight: bold; +} + +div.user_status a:visited { + color: rgba(51, 102, 153, 1.0); + text-decoration: none; + font-weight: bold; +} + +div.user_status a:hover { + color: rgba(51, 102, 153, 1.0); + text-decoration: none; + font-weight: bold; + text-decoration: underline; +} + +div.main { + display: block; + min-height: 600px; + padding: 0.5em; +} + +div.footer { + height: 2em; + font-size:9px; + width: 90%; + margin-top: 0.5em; + margin-left: auto; + margin-right: auto; + text-align: right; +} + +.navmenu { + margin: 0; + padding: 0; +} + +.navmenu li { + list-style: none; + float: left; +} + +.navmenu li a { + display: block; + padding-left: 1.0em; + padding-right: 1.0em; + padding-top: 0.5em; + padding-bottom: 0.5em; + background-color: rgba(80, 80, 80, 1.0); + background: -moz-linear-gradient(top, rgba(120,120,120,1.0), rgba(65,65,65,1.0)); + background: -webkit-gradient(linear, left top, left bottom, from(rgba(120,120,120,1.0)), to(rgba(65,65,65,1.0))); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#787878', endColorstr='#414141'); + color: #fff; + text-decoration: none; + font-weight: bold; +} + +.navmenu ul li a { + display: block; + padding-left: 1.0em; + padding-right: 1.0em; + padding-top: 0.5em; + padding-bottom: 0.5em; + background: 0; + filter: 0; + background-color: rgba(221,221,221, 1.0); + color: #000; + text-decoration: none; + font-weight: bold; + border: 0; +} + +.navmenu ul li a:hover { + color: #07B; +} + +.navmenu .current a, .navmenu li:hover > a { + background: 0; + filter: 0; + background-color: rgba(221,221,221, 1.0); + color: #07B; +} + +.navmenu li ul { + display: none; + border: 1px solid rgba(103, 103, 103, 1.0); + border-top: 0; + box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.3); +} + +.navmenu li:hover ul, .navmenu li.hover ul { + display: block; + position: absolute; + margin: 0; + padding: 0; +} + +.navmenu li:hover li, .navmenu li.hover li { + float: none; +} + +.lt_left { + text-align: left; +} + +.lt_right { + text-align: right; +} + +.styled_button { + padding:2px 4px 2px 4px; + margin:3px 3px 0 0; + background-color:#f5f5f5; + border:1px solid #bcbcbc; + border-top:1px solid #ccc; + border-left:1px solid #ccc; + border-radius: 4px; + color:#336699; + text-decoration:none; + line-height:130%; + cursor:pointer; + width:auto; + overflow:visible; + font-weight:bold; + box-shadow: 0 0 4px rgba(128, 128, 128, 0.2); +} + +.styled_button:hover { + background-color:#dff4ff; + border:1px solid #c2e1ef; + color:#336699; +} + +.styled_button:active { + background-color:#6299c5; + border:1px solid #6299c5; + color:#fff; +} + +.styled_button img { + margin:0 3px -3px 0 !important; +} + +.styled_button_disabled { + padding:2px 4px 2px 4px; + margin:3px 3px 0 0; + background-color:#dddddd; + border:1px solid #9a9a9a; + border-top:1px solid #aaa; + border-left:1px solid #aaa; + border-radius: 4px; + color: Gray; + text-decoration:none; + line-height:130%; + cursor:default; + width:auto; + overflow:visible; + font-weight:bold; + box-shadow: 0 0 4px rgba(128, 128, 128, 0.2); +} + +.styled_button_disabled img { + margin:0 3px -3px 0 !important; +} + +.styled_table { + border: 1px solid #000; + text-align: left; + background-color: #f5f5f5; + margin-right:auto; + margin-left:auto; + box-shadow: 0 0 5px rgba(128, 128, 128, 0.4); +} + +.styled_table th { + padding-left: 1.0em; + padding-right: 1.0em; + padding-top: 0.5em; + padding-bottom: 0.5em; + background-color: rgba(80, 80, 80, 1.0); + background: -moz-linear-gradient(top, rgba(120,120,120,1.0), rgba(65,65,65,1.0)); + background: -webkit-gradient(linear, left top, left bottom, from(rgba(120,120,120,1.0)), to(rgba(65,65,65,1.0))); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#787878', endColorstr='#414141'); + color: #fff; + text-decoration: none; + font-weight: bold; +} + +.styled_table td, .styled_table th { + padding: 4px 5px; + text-align: center; +} + +.odd { + background-color: #f0f0f0; +} + +.styled_table tr td img { + margin:0 3px -3px 0 !important; +} + +.error { + color:red; + width: 75.5em; + text-align: left; +} + +.console_out { + box-shadow: 0 0 5px rgba(128, 128, 128, 0.4); +} + diff --git a/utils/defaults/updated_templates/zone.html b/utils/defaults/updated_templates/zone.html new file mode 100644 index 000000000..d5b8b3beb --- /dev/null +++ b/utils/defaults/updated_templates/zone.html @@ -0,0 +1,18 @@ + + + + EQEmu + + + + +
+ +
+
+
+ + + diff --git a/utils/defaults/updated_templates/zones.html b/utils/defaults/updated_templates/zones.html new file mode 100644 index 000000000..f9749c245 --- /dev/null +++ b/utils/defaults/updated_templates/zones.html @@ -0,0 +1,130 @@ + + + + EQEmu + + + + +ListBootedZones(); +# @zones = ( +# { type => "dynamic", short_name => "arena", long_name => "Arena", port => 8001 }, +# { type => "static", short_name => "freportw", long_name => "West Freeport", port => 8002 }, +# { type => "static", short_name => "freportn", long_name => "North Freeport", port => 8003 } +# ); + +?> + +
+ +
+

Zone List

+ ".($#zones+1)." zones running."; + ?> +
+ + + + + + + + GetZoneDetails($zonekey); + if(!$zone) { + $zone->{long_name} = "ERROR: no zone"; + } elsif($zone->{error}) { + $zone->{long_name} = "ERROR: $zone->{error}"; + } + + print "\n"; + if($zone->{type} eq "dynamic") { + if($zone->{zone_id} == 0) { + print "\t"; + } else { + print "\t"; + } + } else { + print ""; + } + print ""; + print ""; + print ""; + print "\n"; + print "\n"; + } + ?> +
Zone Name PortInstance IdPlayersActions
$zone->{launch_name}: Idle$zone->{launch_name}: $zone->{long_name} ($zone->{short_name})$zone->{long_name} ($zone->{short_name})$zone->{port}$zone->{instance_id}Players"; + print "Kill"; + print "
+
+ + + + diff --git a/utils/defaults/updated_templates/zoneview.html b/utils/defaults/updated_templates/zoneview.html new file mode 100644 index 000000000..92b8e20a0 --- /dev/null +++ b/utils/defaults/updated_templates/zoneview.html @@ -0,0 +1,111 @@ + + + + EQEmu + + + + +ListBootedZones(); +# @zones = ( +# { type => "dynamic", short_name => "arena", long_name => "Arena", port => 8001 }, +# { type => "static", short_name => "freportw", long_name => "West Freeport", port => 8002 }, +# { type => "static", short_name => "freportn", long_name => "North Freeport", port => 8003 } +# ); + +?> + +
+ +
+

Zone List

+ ".($#zones+1)." zones running."; + ?> + + + + + + + GetZoneDetails($zonekey); + if(!$zone) { + $zone->{long_name} = "ERROR: no zone"; + } elsif($zone->{error}) { + $zone->{long_name} = "ERROR: $zone->{error}"; + } + + print "\n"; + if($zone->{type} eq "dynamic") { + if($zone->{zone_id} == 0) { + print "\t"; + } else { + print "\t"; + } + } else { + print ""; + } + print ""; + print ""; + print "\n"; + } + ?> +
Zone Name Instance IdPlayers
$zone->{launch_name}: Idle$zone->{launch_name}: $zone->{long_name} ($zone->{short_name})$zone->{long_name} ($zone->{short_name})$zone->{instance_id}Players
+
+
+ + + diff --git a/utils/defaults/worldui.pl b/utils/defaults/worldui.pl new file mode 100644 index 000000000..e69de29bb diff --git a/utils/deprecated/0.6.1-upgrade.sql b/utils/deprecated/0.6.1-upgrade.sql new file mode 100644 index 000000000..e547493d0 --- /dev/null +++ b/utils/deprecated/0.6.1-upgrade.sql @@ -0,0 +1,328 @@ +create table titles ( + id int UNSIGNED primary key AUTO_INCREMENT, + skill_id tinyint UNSIGNED NOT NULL, + skill_value mediumint UNSIGNED NOT NULL, + aa_points TINYINT UNSIGNED NOT NULL, + title varchar(32) NOT NULL +); + +alter table inventory add instnodrop tinyint(1) unsigned default 0 not null; + +alter table tributes ADD isguild TINYINT NOT NULL DEFAULT '0'; +alter table tributes drop primary key; +alter table tributes ADD primary key(id,isguild); + +ALTER TABLE zone ADD fog_red2 tinyint(3) unsigned NOT NULL default '0'; +ALTER TABLE zone ADD fog_green2 tinyint(3) unsigned NOT NULL default '0'; +ALTER TABLE zone ADD fog_blue2 tinyint(3) unsigned NOT NULL default '0'; +ALTER TABLE zone ADD fog_minclip2 float NOT NULL default '450'; +ALTER TABLE zone ADD fog_maxclip2 float NOT NULL default '450'; +ALTER TABLE zone ADD fog_red3 tinyint(3) unsigned NOT NULL default '0'; +ALTER TABLE zone ADD fog_green3 tinyint(3) unsigned NOT NULL default '0'; +ALTER TABLE zone ADD fog_blue3 tinyint(3) unsigned NOT NULL default '0'; +ALTER TABLE zone ADD fog_minclip3 float NOT NULL default '450'; +ALTER TABLE zone ADD fog_maxclip3 float NOT NULL default '450'; +ALTER TABLE zone ADD fog_red4 tinyint(3) unsigned NOT NULL default '0'; +ALTER TABLE zone ADD fog_green4 tinyint(3) unsigned NOT NULL default '0'; +ALTER TABLE zone ADD fog_blue4 tinyint(3) unsigned NOT NULL default '0'; +ALTER TABLE zone ADD fog_minclip4 float NOT NULL default '450'; +ALTER TABLE zone ADD fog_maxclip4 float NOT NULL default '450'; +UPDATE zone SET fog_red2=fog_red,fog_red3=fog_red,fog_red4=fog_red, +fog_green2=fog_green,fog_green3=fog_green,fog_green4=fog_green, +fog_blue2=fog_blue,fog_blue3=fog_blue,fog_blue4=fog_blue, +fog_minclip2=fog_minclip,fog_minclip3=fog_minclip,fog_minclip4=fog_minclip, +fog_maxclip2=fog_maxclip,fog_maxclip3=fog_maxclip,fog_maxclip4=fog_maxclip; + +ALTER TABLE zone_points ADD target_zone_id int UNSIGNED NOT NULL; +UPDATE zone_points SET target_zone_id=39 WHERE target_zone='hole'; +UPDATE zone_points SET target_zone_id=75 WHERE target_zone='paineel'; +UPDATE zone_points SET target_zone_id=100 WHERE target_zone='stonebrunt'; +UPDATE zone_points SET target_zone_id=101 WHERE target_zone='warrens'; +UPDATE zone_points SET target_zone_id=109 WHERE target_zone='veksar'; +UPDATE zone_points SET target_zone_id=181 WHERE target_zone='jaggedpine'; +UPDATE zone_points SET target_zone_id=1 WHERE target_zone='qeynos'; +UPDATE zone_points SET target_zone_id=2 WHERE target_zone='qeynos2'; +UPDATE zone_points SET target_zone_id=3 WHERE target_zone='qrg'; +UPDATE zone_points SET target_zone_id=4 WHERE target_zone='qeytoqrg'; +UPDATE zone_points SET target_zone_id=5 WHERE target_zone='highpass'; +UPDATE zone_points SET target_zone_id=6 WHERE target_zone='highkeep'; +UPDATE zone_points SET target_zone_id=8 WHERE target_zone='freportn'; +UPDATE zone_points SET target_zone_id=9 WHERE target_zone='freportw'; +UPDATE zone_points SET target_zone_id=10 WHERE target_zone='freporte'; +UPDATE zone_points SET target_zone_id=11 WHERE target_zone='runnyeye'; +UPDATE zone_points SET target_zone_id=12 WHERE target_zone='qey2hh1'; +UPDATE zone_points SET target_zone_id=13 WHERE target_zone='northkarana'; +UPDATE zone_points SET target_zone_id=14 WHERE target_zone='southkarana'; +UPDATE zone_points SET target_zone_id=15 WHERE target_zone='eastkarana'; +UPDATE zone_points SET target_zone_id=16 WHERE target_zone='beholder'; +UPDATE zone_points SET target_zone_id=17 WHERE target_zone='blackburrow'; +UPDATE zone_points SET target_zone_id=18 WHERE target_zone='paw'; +UPDATE zone_points SET target_zone_id=19 WHERE target_zone='rivervale'; +UPDATE zone_points SET target_zone_id=20 WHERE target_zone='kithicor'; +UPDATE zone_points SET target_zone_id=21 WHERE target_zone='commons'; +UPDATE zone_points SET target_zone_id=22 WHERE target_zone='ecommons'; +UPDATE zone_points SET target_zone_id=23 WHERE target_zone='erudnint'; +UPDATE zone_points SET target_zone_id=24 WHERE target_zone='erudnext'; +UPDATE zone_points SET target_zone_id=25 WHERE target_zone='nektulos'; +UPDATE zone_points SET target_zone_id=26 WHERE target_zone='cshome'; +UPDATE zone_points SET target_zone_id=27 WHERE target_zone='lavastorm'; +UPDATE zone_points SET target_zone_id=29 WHERE target_zone='halas'; +UPDATE zone_points SET target_zone_id=30 WHERE target_zone='everfrost'; +UPDATE zone_points SET target_zone_id=31 WHERE target_zone='soldunga'; +UPDATE zone_points SET target_zone_id=32 WHERE target_zone='soldungb'; +UPDATE zone_points SET target_zone_id=33 WHERE target_zone='misty'; +UPDATE zone_points SET target_zone_id=34 WHERE target_zone='nro'; +UPDATE zone_points SET target_zone_id=35 WHERE target_zone='sro'; +UPDATE zone_points SET target_zone_id=36 WHERE target_zone='befallen'; +UPDATE zone_points SET target_zone_id=37 WHERE target_zone='oasis'; +UPDATE zone_points SET target_zone_id=38 WHERE target_zone='tox'; +UPDATE zone_points SET target_zone_id=40 WHERE target_zone='neriaka'; +UPDATE zone_points SET target_zone_id=41 WHERE target_zone='neriakb'; +UPDATE zone_points SET target_zone_id=42 WHERE target_zone='neriakc'; +UPDATE zone_points SET target_zone_id=44 WHERE target_zone='najena'; +UPDATE zone_points SET target_zone_id=46 WHERE target_zone='innothule'; +UPDATE zone_points SET target_zone_id=47 WHERE target_zone='feerrott'; +UPDATE zone_points SET target_zone_id=48 WHERE target_zone='cazicthule'; +UPDATE zone_points SET target_zone_id=49 WHERE target_zone='oggok'; +UPDATE zone_points SET target_zone_id=50 WHERE target_zone='rathemtn'; +UPDATE zone_points SET target_zone_id=51 WHERE target_zone='lakerathe'; +UPDATE zone_points SET target_zone_id=52 WHERE target_zone='gukta'; +UPDATE zone_points SET target_zone_id=54 WHERE target_zone='gfaydark'; +UPDATE zone_points SET target_zone_id=55 WHERE target_zone='akanon'; +UPDATE zone_points SET target_zone_id=56 WHERE target_zone='steamfont'; +UPDATE zone_points SET target_zone_id=57 WHERE target_zone='lfaydark'; +UPDATE zone_points SET target_zone_id=58 WHERE target_zone='crushbone'; +UPDATE zone_points SET target_zone_id=59 WHERE target_zone='mistmoore'; +UPDATE zone_points SET target_zone_id=60 WHERE target_zone='kaladima'; +UPDATE zone_points SET target_zone_id=61 WHERE target_zone='felwithea'; +UPDATE zone_points SET target_zone_id=62 WHERE target_zone='felwitheb'; +UPDATE zone_points SET target_zone_id=63 WHERE target_zone='unrest'; +UPDATE zone_points SET target_zone_id=64 WHERE target_zone='kedge'; +UPDATE zone_points SET target_zone_id=65 WHERE target_zone='guktop'; +UPDATE zone_points SET target_zone_id=66 WHERE target_zone='gukbottom'; +UPDATE zone_points SET target_zone_id=67 WHERE target_zone='kaladimb'; +UPDATE zone_points SET target_zone_id=68 WHERE target_zone='butcher'; +UPDATE zone_points SET target_zone_id=69 WHERE target_zone='oot'; +UPDATE zone_points SET target_zone_id=70 WHERE target_zone='cauldron'; +UPDATE zone_points SET target_zone_id=71 WHERE target_zone='airplane'; +UPDATE zone_points SET target_zone_id=72 WHERE target_zone='fearplane'; +UPDATE zone_points SET target_zone_id=73 WHERE target_zone='permafrost'; +UPDATE zone_points SET target_zone_id=74 WHERE target_zone='kerraridge'; +UPDATE zone_points SET target_zone_id=76 WHERE target_zone='hateplane'; +UPDATE zone_points SET target_zone_id=77 WHERE target_zone='arena'; +UPDATE zone_points SET target_zone_id=180 WHERE target_zone='arena2'; +UPDATE zone_points SET target_zone_id=98 WHERE target_zone='erudsxing'; +UPDATE zone_points SET target_zone_id=45 WHERE target_zone='qcat'; +UPDATE zone_points SET target_zone_id=78 WHERE target_zone='fieldofbone'; +UPDATE zone_points SET target_zone_id=79 WHERE target_zone='warslikswood'; +UPDATE zone_points SET target_zone_id=80 WHERE target_zone='soltemple'; +UPDATE zone_points SET target_zone_id=81 WHERE target_zone='droga'; +UPDATE zone_points SET target_zone_id=82 WHERE target_zone='cabwest'; +UPDATE zone_points SET target_zone_id=83 WHERE target_zone='swampofnohope'; +UPDATE zone_points SET target_zone_id=84 WHERE target_zone='firiona'; +UPDATE zone_points SET target_zone_id=85 WHERE target_zone='lakeofillomen'; +UPDATE zone_points SET target_zone_id=86 WHERE target_zone='dreadlands'; +UPDATE zone_points SET target_zone_id=87 WHERE target_zone='burningwood'; +UPDATE zone_points SET target_zone_id=88 WHERE target_zone='kaesora'; +UPDATE zone_points SET target_zone_id=89 WHERE target_zone='sebilis'; +UPDATE zone_points SET target_zone_id=90 WHERE target_zone='citymist'; +UPDATE zone_points SET target_zone_id=91 WHERE target_zone='skyfire'; +UPDATE zone_points SET target_zone_id=92 WHERE target_zone='frontiermtns'; +UPDATE zone_points SET target_zone_id=93 WHERE target_zone='overthere'; +UPDATE zone_points SET target_zone_id=94 WHERE target_zone='emeraldjungle'; +UPDATE zone_points SET target_zone_id=95 WHERE target_zone='trakanon'; +UPDATE zone_points SET target_zone_id=96 WHERE target_zone='timorous'; +UPDATE zone_points SET target_zone_id=97 WHERE target_zone='kurn'; +UPDATE zone_points SET target_zone_id=102 WHERE target_zone='karnor'; +UPDATE zone_points SET target_zone_id=103 WHERE target_zone='chardok'; +UPDATE zone_points SET target_zone_id=104 WHERE target_zone='dalnir'; +UPDATE zone_points SET target_zone_id=105 WHERE target_zone='charasis'; +UPDATE zone_points SET target_zone_id=106 WHERE target_zone='cabeast'; +UPDATE zone_points SET target_zone_id=107 WHERE target_zone='nurga'; +UPDATE zone_points SET target_zone_id=108 WHERE target_zone='veeshan'; +UPDATE zone_points SET target_zone_id=186 WHERE target_zone='hateplaneb'; +UPDATE zone_points SET target_zone_id=188 WHERE target_zone='tutoriala'; +UPDATE zone_points SET target_zone_id=189 WHERE target_zone='tutorialb'; +UPDATE zone_points SET target_zone_id=110 WHERE target_zone='iceclad'; +UPDATE zone_points SET target_zone_id=111 WHERE target_zone='frozenshadow'; +UPDATE zone_points SET target_zone_id=112 WHERE target_zone='velketor'; +UPDATE zone_points SET target_zone_id=113 WHERE target_zone='kael'; +UPDATE zone_points SET target_zone_id=114 WHERE target_zone='skyshrine'; +UPDATE zone_points SET target_zone_id=115 WHERE target_zone='thurgadina'; +UPDATE zone_points SET target_zone_id=116 WHERE target_zone='eastwastes'; +UPDATE zone_points SET target_zone_id=117 WHERE target_zone='cobaltscar'; +UPDATE zone_points SET target_zone_id=118 WHERE target_zone='greatdivide'; +UPDATE zone_points SET target_zone_id=119 WHERE target_zone='wakening'; +UPDATE zone_points SET target_zone_id=120 WHERE target_zone='westwastes'; +UPDATE zone_points SET target_zone_id=121 WHERE target_zone='crystal'; +UPDATE zone_points SET target_zone_id=123 WHERE target_zone='necropolis'; +UPDATE zone_points SET target_zone_id=124 WHERE target_zone='templeveeshan'; +UPDATE zone_points SET target_zone_id=125 WHERE target_zone='sirens'; +UPDATE zone_points SET target_zone_id=126 WHERE target_zone='mischiefplane'; +UPDATE zone_points SET target_zone_id=127 WHERE target_zone='growthplane'; +UPDATE zone_points SET target_zone_id=128 WHERE target_zone='sleeper'; +UPDATE zone_points SET target_zone_id=129 WHERE target_zone='thurgadinb'; +UPDATE zone_points SET target_zone_id=183 WHERE target_zone='tutorial'; +UPDATE zone_points SET target_zone_id=184 WHERE target_zone='load'; +UPDATE zone_points SET target_zone_id=185 WHERE target_zone='load2'; +UPDATE zone_points SET target_zone_id=150 WHERE target_zone='Shadowhaven'; +UPDATE zone_points SET target_zone_id=151 WHERE target_zone='Bazaar'; +UPDATE zone_points SET target_zone_id=152 WHERE target_zone='Nexus'; +UPDATE zone_points SET target_zone_id=153 WHERE target_zone='echo'; +UPDATE zone_points SET target_zone_id=154 WHERE target_zone='acrylia'; +UPDATE zone_points SET target_zone_id=155 WHERE target_zone='sharvahl'; +UPDATE zone_points SET target_zone_id=156 WHERE target_zone='paludal'; +UPDATE zone_points SET target_zone_id=157 WHERE target_zone='fungusgrove'; +UPDATE zone_points SET target_zone_id=158 WHERE target_zone='vexthal'; +UPDATE zone_points SET target_zone_id=159 WHERE target_zone='sseru'; +UPDATE zone_points SET target_zone_id=160 WHERE target_zone='katta'; +UPDATE zone_points SET target_zone_id=161 WHERE target_zone='netherbian'; +UPDATE zone_points SET target_zone_id=162 WHERE target_zone='ssratemple'; +UPDATE zone_points SET target_zone_id=163 WHERE target_zone='griegsend'; +UPDATE zone_points SET target_zone_id=164 WHERE target_zone='thedeep'; +UPDATE zone_points SET target_zone_id=165 WHERE target_zone='shadeweaver'; +UPDATE zone_points SET target_zone_id=166 WHERE target_zone='hollowshade'; +UPDATE zone_points SET target_zone_id=167 WHERE target_zone='grimling'; +UPDATE zone_points SET target_zone_id=168 WHERE target_zone='mseru'; +UPDATE zone_points SET target_zone_id=169 WHERE target_zone='letalis'; +UPDATE zone_points SET target_zone_id=170 WHERE target_zone='twilight'; +UPDATE zone_points SET target_zone_id=171 WHERE target_zone='thegrey'; +UPDATE zone_points SET target_zone_id=172 WHERE target_zone='tenebrous'; +UPDATE zone_points SET target_zone_id=173 WHERE target_zone='maiden'; +UPDATE zone_points SET target_zone_id=174 WHERE target_zone='dawnshroud'; +UPDATE zone_points SET target_zone_id=175 WHERE target_zone='scarlet'; +UPDATE zone_points SET target_zone_id=176 WHERE target_zone='umbral'; +UPDATE zone_points SET target_zone_id=179 WHERE target_zone='akheva'; +UPDATE zone_points SET target_zone_id=995 WHERE target_zone='arttest'; +UPDATE zone_points SET target_zone_id=200 WHERE target_zone='codecay'; +UPDATE zone_points SET target_zone_id=201 WHERE target_zone='pojustice'; +UPDATE zone_points SET target_zone_id=202 WHERE target_zone='poknowledge'; +UPDATE zone_points SET target_zone_id=203 WHERE target_zone='potranquility'; +UPDATE zone_points SET target_zone_id=204 WHERE target_zone='ponightmare'; +UPDATE zone_points SET target_zone_id=205 WHERE target_zone='podisease'; +UPDATE zone_points SET target_zone_id=206 WHERE target_zone='poinnovation'; +UPDATE zone_points SET target_zone_id=207 WHERE target_zone='potorment'; +UPDATE zone_points SET target_zone_id=208 WHERE target_zone='povalor'; +UPDATE zone_points SET target_zone_id=209 WHERE target_zone='bothunder'; +UPDATE zone_points SET target_zone_id=210 WHERE target_zone='postorms'; +UPDATE zone_points SET target_zone_id=211 WHERE target_zone='hohonora'; +UPDATE zone_points SET target_zone_id=212 WHERE target_zone='solrotower'; +UPDATE zone_points SET target_zone_id=213 WHERE target_zone='powar'; +UPDATE zone_points SET target_zone_id=214 WHERE target_zone='potactics'; +UPDATE zone_points SET target_zone_id=215 WHERE target_zone='poair'; +UPDATE zone_points SET target_zone_id=216 WHERE target_zone='powater'; +UPDATE zone_points SET target_zone_id=217 WHERE target_zone='pofire'; +UPDATE zone_points SET target_zone_id=218 WHERE target_zone='poeartha'; +UPDATE zone_points SET target_zone_id=219 WHERE target_zone='potimea'; +UPDATE zone_points SET target_zone_id=220 WHERE target_zone='hohonorb'; +UPDATE zone_points SET target_zone_id=221 WHERE target_zone='nightmareb'; +UPDATE zone_points SET target_zone_id=222 WHERE target_zone='poearthb'; +UPDATE zone_points SET target_zone_id=223 WHERE target_zone='potimeb'; +UPDATE zone_points SET target_zone_id=224 WHERE target_zone='gunthak'; +UPDATE zone_points SET target_zone_id=225 WHERE target_zone='dulak'; +UPDATE zone_points SET target_zone_id=226 WHERE target_zone='torgiran'; +UPDATE zone_points SET target_zone_id=227 WHERE target_zone='nadox'; +UPDATE zone_points SET target_zone_id=228 WHERE target_zone='hatesfury'; +UPDATE zone_points SET target_zone_id=277 WHERE target_zone='chardokb'; +UPDATE zone_points SET target_zone_id=278 WHERE target_zone='soldungc'; +UPDATE zone_points SET target_zone_id=229 WHERE target_zone='guka'; +UPDATE zone_points SET target_zone_id=234 WHERE target_zone='gukb'; +UPDATE zone_points SET target_zone_id=239 WHERE target_zone='gukc'; +UPDATE zone_points SET target_zone_id=244 WHERE target_zone='gukd'; +UPDATE zone_points SET target_zone_id=249 WHERE target_zone='guke'; +UPDATE zone_points SET target_zone_id=254 WHERE target_zone='gukf'; +UPDATE zone_points SET target_zone_id=259 WHERE target_zone='gukg'; +UPDATE zone_points SET target_zone_id=264 WHERE target_zone='gukh'; +UPDATE zone_points SET target_zone_id=232 WHERE target_zone='mira'; +UPDATE zone_points SET target_zone_id=237 WHERE target_zone='mirb'; +UPDATE zone_points SET target_zone_id=242 WHERE target_zone='mirc'; +UPDATE zone_points SET target_zone_id=247 WHERE target_zone='mird'; +UPDATE zone_points SET target_zone_id=252 WHERE target_zone='mire'; +UPDATE zone_points SET target_zone_id=257 WHERE target_zone='mirf'; +UPDATE zone_points SET target_zone_id=262 WHERE target_zone='mirg'; +UPDATE zone_points SET target_zone_id=267 WHERE target_zone='mirh'; +UPDATE zone_points SET target_zone_id=271 WHERE target_zone='miri'; +UPDATE zone_points SET target_zone_id=275 WHERE target_zone='mirj'; +UPDATE zone_points SET target_zone_id=233 WHERE target_zone='mmca'; +UPDATE zone_points SET target_zone_id=238 WHERE target_zone='mmcb'; +UPDATE zone_points SET target_zone_id=243 WHERE target_zone='mmcc'; +UPDATE zone_points SET target_zone_id=248 WHERE target_zone='mmcd'; +UPDATE zone_points SET target_zone_id=253 WHERE target_zone='mmce'; +UPDATE zone_points SET target_zone_id=258 WHERE target_zone='mmcf'; +UPDATE zone_points SET target_zone_id=263 WHERE target_zone='mmcg'; +UPDATE zone_points SET target_zone_id=268 WHERE target_zone='mmch'; +UPDATE zone_points SET target_zone_id=272 WHERE target_zone='mmci'; +UPDATE zone_points SET target_zone_id=276 WHERE target_zone='mmcj'; +UPDATE zone_points SET target_zone_id=230 WHERE target_zone='ruja'; +UPDATE zone_points SET target_zone_id=235 WHERE target_zone='rujb'; +UPDATE zone_points SET target_zone_id=240 WHERE target_zone='rujc'; +UPDATE zone_points SET target_zone_id=245 WHERE target_zone='rujd'; +UPDATE zone_points SET target_zone_id=250 WHERE target_zone='ruje'; +UPDATE zone_points SET target_zone_id=255 WHERE target_zone='rujf'; +UPDATE zone_points SET target_zone_id=260 WHERE target_zone='rujg'; +UPDATE zone_points SET target_zone_id=265 WHERE target_zone='rujh'; +UPDATE zone_points SET target_zone_id=269 WHERE target_zone='ruji'; +UPDATE zone_points SET target_zone_id=273 WHERE target_zone='rujj'; +UPDATE zone_points SET target_zone_id=231 WHERE target_zone='taka'; +UPDATE zone_points SET target_zone_id=236 WHERE target_zone='takb'; +UPDATE zone_points SET target_zone_id=241 WHERE target_zone='takc'; +UPDATE zone_points SET target_zone_id=246 WHERE target_zone='takd'; +UPDATE zone_points SET target_zone_id=251 WHERE target_zone='take'; +UPDATE zone_points SET target_zone_id=256 WHERE target_zone='takf'; +UPDATE zone_points SET target_zone_id=261 WHERE target_zone='takg'; +UPDATE zone_points SET target_zone_id=266 WHERE target_zone='takh'; +UPDATE zone_points SET target_zone_id=270 WHERE target_zone='taki'; +UPDATE zone_points SET target_zone_id=274 WHERE target_zone='takj'; +UPDATE zone_points SET target_zone_id=187 WHERE target_zone='shadowrest'; +UPDATE zone_points SET target_zone_id=279 WHERE target_zone='abysmal'; +UPDATE zone_points SET target_zone_id=280 WHERE target_zone='natimbi'; +UPDATE zone_points SET target_zone_id=281 WHERE target_zone='qinimi'; +UPDATE zone_points SET target_zone_id=282 WHERE target_zone='riwwi'; +UPDATE zone_points SET target_zone_id=283 WHERE target_zone='barindu'; +UPDATE zone_points SET target_zone_id=284 WHERE target_zone='ferubi'; +UPDATE zone_points SET target_zone_id=285 WHERE target_zone='snpool'; +UPDATE zone_points SET target_zone_id=286 WHERE target_zone='snlair'; +UPDATE zone_points SET target_zone_id=287 WHERE target_zone='snplant'; +UPDATE zone_points SET target_zone_id=288 WHERE target_zone='sncrematory'; +UPDATE zone_points SET target_zone_id=289 WHERE target_zone='tipt'; +UPDATE zone_points SET target_zone_id=290 WHERE target_zone='vxed'; +UPDATE zone_points SET target_zone_id=291 WHERE target_zone='yxtta'; +UPDATE zone_points SET target_zone_id=292 WHERE target_zone='uqua'; +UPDATE zone_points SET target_zone_id=293 WHERE target_zone='kodtaz'; +UPDATE zone_points SET target_zone_id=294 WHERE target_zone='ikkinz'; +UPDATE zone_points SET target_zone_id=295 WHERE target_zone='inktuta'; +UPDATE zone_points SET target_zone_id=296 WHERE target_zone='txevu'; +UPDATE zone_points SET target_zone_id=297 WHERE target_zone='tacvi'; +UPDATE zone_points SET target_zone_id=298 WHERE target_zone='qvic'; +UPDATE zone_points SET target_zone_id=299 WHERE target_zone='qvicb'; +UPDATE zone_points SET target_zone_id=182 WHERE target_zone='nedaria'; +UPDATE zone_points SET target_zone_id=300 WHERE target_zone='wallofslaughter'; +UPDATE zone_points SET target_zone_id=301 WHERE target_zone='bloodfields'; +UPDATE zone_points SET target_zone_id=302 WHERE target_zone='draniksscar'; +UPDATE zone_points SET target_zone_id=303 WHERE target_zone='causeway'; +UPDATE zone_points SET target_zone_id=304 WHERE target_zone='chambersa'; +UPDATE zone_points SET target_zone_id=305 WHERE target_zone='chambersb'; +UPDATE zone_points SET target_zone_id=306 WHERE target_zone='chambersc'; +UPDATE zone_points SET target_zone_id=307 WHERE target_zone='chambersd'; +UPDATE zone_points SET target_zone_id=308 WHERE target_zone='chamberse'; +UPDATE zone_points SET target_zone_id=309 WHERE target_zone='chambersf'; +UPDATE zone_points SET target_zone_id=316 WHERE target_zone='provinggrounds'; +UPDATE zone_points SET target_zone_id=317 WHERE target_zone='anguish'; +UPDATE zone_points SET target_zone_id=318 WHERE target_zone='dranikhollowsa'; +UPDATE zone_points SET target_zone_id=319 WHERE target_zone='dranikhollowsb'; +UPDATE zone_points SET target_zone_id=320 WHERE target_zone='dranikhollowsc'; +UPDATE zone_points SET target_zone_id=328 WHERE target_zone='dranikcatacombsa'; +UPDATE zone_points SET target_zone_id=329 WHERE target_zone='dranikcatacombsb'; +UPDATE zone_points SET target_zone_id=330 WHERE target_zone='dranikcatacombsc'; +UPDATE zone_points SET target_zone_id=331 WHERE target_zone='draniksewersa'; +UPDATE zone_points SET target_zone_id=332 WHERE target_zone='draniksewersb'; +UPDATE zone_points SET target_zone_id=333 WHERE target_zone='draniksewersc'; +UPDATE zone_points SET target_zone_id=334 WHERE target_zone='riftseekers'; +UPDATE zone_points SET target_zone_id=335 WHERE target_zone='harbingers'; +UPDATE zone_points SET target_zone_id=336 WHERE target_zone='dranik'; +ALTER TABLE zone_points DROP target_zone; + +ALTER TABLE account ADD hideme TINYINT NOT NULL DEFAULT 0; + +ALTER TABLE `character_` ADD `extprofile` BLOB NOT NULL; + diff --git a/utils/deprecated/ZONECFG.SQL b/utils/deprecated/ZONECFG.SQL new file mode 100644 index 000000000..0f99142e9 --- /dev/null +++ b/utils/deprecated/ZONECFG.SQL @@ -0,0 +1,254 @@ +ALTER TABLE `zone` ADD `underworld` FLOAT DEFAULT "0" NOT NULL; +ALTER TABLE `zone` ADD `minclip` FLOAT DEFAULT "450" NOT NULL; +ALTER TABLE `zone` ADD `maxclip` FLOAT DEFAULT "450" NOT NULL; +ALTER TABLE `zone` ADD `fog_minclip` FLOAT DEFAULT "450" NOT NULL; +ALTER TABLE `zone` ADD `fog_maxclip` FLOAT DEFAULT "450" NOT NULL; +ALTER TABLE `zone` ADD `fog_blue` TINYINT(3) UNSIGNED DEFAULT "0" NOT NULL; +ALTER TABLE `zone` ADD `fog_red` TINYINT(3) UNSIGNED DEFAULT "0" NOT NULL; +ALTER TABLE `zone` ADD `fog_green` TINYINT(3) UNSIGNED DEFAULT "0" NOT NULL; +ALTER TABLE `zone` ADD `sky` TINYINT(3) UNSIGNED DEFAULT "1" NOT NULL; +ALTER TABLE `zone` ADD `ztype` TINYINT(3) UNSIGNED DEFAULT "1" NOT NULL; +ALTER TABLE `zone` ADD `zone_exp_multiplier` FLOAT DEFAULT "1" NOT NULL; +ALTER TABLE `zone` ADD `walkspeed` FLOAT DEFAULT ".4" NOT NULL; +ALTER TABLE `zone` ADD `time_type` TINYINT(3) UNSIGNED DEFAULT "2" NOT NULL; +update zone set underworld=-320,minclip=450,maxclip=450,fog_minclip=10,fog_maxclip=450,fog_blue=210,fog_red=200,fog_green=200,sky=1,ztype=0,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=1; +update zone set underworld=-404,minclip=450,maxclip=1000,fog_minclip=10,fog_maxclip=450,fog_blue=220,fog_red=200,fog_green=200,sky=1,ztype=0,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=2; +update zone set underworld=-58,minclip=450,maxclip=450,fog_minclip=10,fog_maxclip=450,fog_blue=183,fog_red=180,fog_green=175,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=3; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=4; +update zone set underworld=-60,minclip=400,maxclip=400,fog_minclip=10,fog_maxclip=400,fog_blue=200,fog_red=200,fog_green=200,sky=1,ztype=0,zone_exp_multiplier=0.8,walkspeed=0.4,time_type=2 where zoneidnumber=5; +update zone set underworld=-94,minclip=600,maxclip=600,fog_minclip=0,fog_maxclip=0,fog_blue=0,fog_red=0,fog_green=0,sky=1,ztype=0,zone_exp_multiplier=1.5,walkspeed=0.4,time_type=2 where zoneidnumber=6; +update zone set underworld=-54,minclip=450,maxclip=450,fog_minclip=10,fog_maxclip=450,fog_blue=200,fog_red=230,fog_green=200,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=8; +update zone set underworld=-126,minclip=450,maxclip=450,fog_minclip=10,fog_maxclip=450,fog_blue=200,fog_red=230,fog_green=200,sky=1,ztype=255,zone_exp_multiplier=1,walkspeed=0.4,time_type=2 where zoneidnumber=9; +update zone set underworld=-298,minclip=450,maxclip=450,fog_minclip=10,fog_maxclip=450,fog_blue=200,fog_red=230,fog_green=200,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=10; +update zone set underworld=-192,minclip=600,maxclip=600,fog_minclip=10,fog_maxclip=600,fog_blue=25,fog_red=75,fog_green=150,sky=1,ztype=1,zone_exp_multiplier=1,walkspeed=0.4,time_type=2 where zoneidnumber=11; +update zone set underworld=-131,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=800,fog_blue=220,fog_red=200,fog_green=200,sky=1,ztype=0,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=12; +update zone set underworld=-214,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=800,fog_blue=220,fog_red=200,fog_green=200,sky=1,ztype=0,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=13; +update zone set underworld=-292,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=800,fog_blue=220,fog_red=200,fog_green=200,sky=1,ztype=0,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=14; +update zone set underworld=-250,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=800,fog_blue=220,fog_red=200,fog_green=200,sky=1,ztype=0,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=15; +update zone set underworld=-105,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=600,fog_blue=150,fog_red=240,fog_green=180,sky=1,ztype=0,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=16; +update zone set underworld=-252,minclip=700,maxclip=700,fog_minclip=10,fog_maxclip=700,fog_blue=90,fog_red=50,fog_green=100,sky=1,ztype=0,zone_exp_multiplier=1,walkspeed=0.4,time_type=2 where zoneidnumber=17; +update zone set underworld=-96,minclip=180,maxclip=180,fog_minclip=10,fog_maxclip=180,fog_blue=10,fog_red=30,fog_green=25,sky=0,ztype=255,zone_exp_multiplier=0.9,walkspeed=0.4,time_type=2 where zoneidnumber=18; +update zone set underworld=-72,minclip=400,maxclip=400,fog_minclip=10,fog_maxclip=400,fog_blue=200,fog_red=200,fog_green=210,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=19; +update zone set underworld=-231,minclip=150,maxclip=250,fog_minclip=10,fog_maxclip=200,fog_blue=100,fog_red=120,fog_green=140,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=20; +update zone set underworld=-295,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=800,fog_blue=220,fog_red=200,fog_green=200,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=21; +update zone set underworld=-171,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=800,fog_blue=220,fog_red=200,fog_green=200,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=22; +update zone set underworld=-48,minclip=600,maxclip=600,fog_minclip=0,fog_maxclip=0,fog_blue=0,fog_red=0,fog_green=0,sky=4,ztype=0,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=23; +update zone set underworld=-84,minclip=550,maxclip=550,fog_minclip=10,fog_maxclip=550,fog_blue=220,fog_red=200,fog_green=200,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=24; +update zone set underworld=-189,minclip=200,maxclip=275,fog_minclip=10,fog_maxclip=250,fog_blue=70,fog_red=80,fog_green=90,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=25; +update zone set underworld=-84,minclip=550,maxclip=550,fog_minclip=10,fog_maxclip=550,fog_blue=220,fog_red=200,fog_green=200,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=26; +update zone set underworld=-368,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=800,fog_blue=10,fog_red=255,fog_green=50,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=27; +update zone set underworld=-162,minclip=300,maxclip=300,fog_minclip=10,fog_maxclip=300,fog_blue=255,fog_red=200,fog_green=230,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=29; +update zone set underworld=-374,minclip=300,maxclip=600,fog_minclip=10,fog_maxclip=500,fog_blue=255,fog_red=200,fog_green=230,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=30; +update zone set underworld=-48,minclip=100,maxclip=100,fog_minclip=10,fog_maxclip=100,fog_blue=30,fog_red=180,fog_green=30,sky=0,ztype=255,zone_exp_multiplier=1.3,walkspeed=0.4,time_type=2 where zoneidnumber=31; +update zone set underworld=-148,minclip=350,maxclip=350,fog_minclip=10,fog_maxclip=350,fog_blue=30,fog_red=180,fog_green=30,sky=1,ztype=255,zone_exp_multiplier=0.8,walkspeed=0.4,time_type=2 where zoneidnumber=32; +update zone set underworld=-126,minclip=450,maxclip=550,fog_minclip=10,fog_maxclip=500,fog_blue=50,fog_red=100,fog_green=120,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=33; +update zone set underworld=-101,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=800,fog_blue=180,fog_red=250,fog_green=250,sky=2,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=34; +update zone set underworld=-120,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=800,fog_blue=180,fog_red=250,fog_green=250,sky=2,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=35; +update zone set underworld=-100,minclip=175,maxclip=175,fog_minclip=10,fog_maxclip=175,fog_blue=90,fog_red=30,fog_green=30,sky=0,ztype=0,zone_exp_multiplier=1.6,walkspeed=0.4,time_type=2 where zoneidnumber=36; +update zone set underworld=-240,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=800,fog_blue=180,fog_red=250,fog_green=250,sky=2,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=37; +update zone set underworld=-231,minclip=200,maxclip=300,fog_minclip=50,fog_maxclip=250,fog_blue=30,fog_red=220,fog_green=200,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=38; +update zone set underworld=-1300,minclip=300,maxclip=600,fog_minclip=200,fog_maxclip=500,fog_blue=10,fog_red=10,fog_green=10,sky=0,ztype=0,zone_exp_multiplier=1,walkspeed=0.4,time_type=2 where zoneidnumber=39; +update zone set underworld=-26,minclip=250,maxclip=250,fog_minclip=10,fog_maxclip=250,fog_blue=60,fog_red=10,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=40; +update zone set underworld=-101,minclip=250,maxclip=250,fog_minclip=10,fog_maxclip=250,fog_blue=60,fog_red=10,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=41; +update zone set underworld=-152,minclip=250,maxclip=250,fog_minclip=10,fog_maxclip=250,fog_blue=60,fog_red=10,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=42; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=43; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=28; +update zone set underworld=-66,minclip=110,maxclip=110,fog_minclip=10,fog_maxclip=110,fog_blue=40,fog_red=30,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=1.3,walkspeed=0.4,time_type=2 where zoneidnumber=44; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=45; +update zone set underworld=-85,minclip=400,maxclip=550,fog_minclip=10,fog_maxclip=500,fog_blue=90,fog_red=170,fog_green=160,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=46; +update zone set underworld=-192,minclip=150,maxclip=200,fog_minclip=10,fog_maxclip=175,fog_blue=30,fog_red=60,fog_green=90,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=47; +update zone set underworld=-332,minclip=450,maxclip=450,fog_minclip=10,fog_maxclip=450,fog_blue=20,fog_red=50,fog_green=80,sky=1,ztype=0,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=48; +update zone set underworld=-182,minclip=300,maxclip=300,fog_minclip=10,fog_maxclip=300,fog_blue=80,fog_red=130,fog_green=140,sky=0,ztype=0,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=49; +update zone set underworld=-230,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=800,fog_blue=220,fog_red=200,fog_green=200,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=50; +update zone set underworld=-454,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=800,fog_blue=220,fog_red=200,fog_green=200,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=51; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=52; +update zone set underworld=-90,minclip=250,maxclip=325,fog_minclip=10,fog_maxclip=300,fog_blue=200,fog_red=230,fog_green=255,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=54; +update zone set underworld=-614,minclip=600,maxclip=600,fog_minclip=10,fog_maxclip=600,fog_blue=30,fog_red=30,fog_green=60,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=55; +update zone set underworld=-222,minclip=600,maxclip=800,fog_minclip=10,fog_maxclip=800,fog_blue=220,fog_red=200,fog_green=200,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=56; +update zone set underworld=-77,minclip=275,maxclip=325,fog_minclip=10,fog_maxclip=300,fog_blue=200,fog_red=230,fog_green=255,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=57; +update zone set underworld=-212,minclip=400,maxclip=400,fog_minclip=10,fog_maxclip=400,fog_blue=190,fog_red=90,fog_green=90,sky=1,ztype=0,zone_exp_multiplier=1.6,walkspeed=0.4,time_type=2 where zoneidnumber=58; +update zone set underworld=-292,minclip=250,maxclip=250,fog_minclip=10,fog_maxclip=250,fog_blue=90,fog_red=60,fog_green=30,sky=0,ztype=255,zone_exp_multiplier=0.9,walkspeed=0.4,time_type=2 where zoneidnumber=59; +update zone set underworld=-62,minclip=175,maxclip=175,fog_minclip=10,fog_maxclip=175,fog_blue=20,fog_red=70,fog_green=50,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=60; +update zone set underworld=-92,minclip=300,maxclip=300,fog_minclip=10,fog_maxclip=300,fog_blue=100,fog_red=100,fog_green=130,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=61; +update zone set underworld=-82,minclip=300,maxclip=300,fog_minclip=10,fog_maxclip=300,fog_blue=100,fog_red=100,fog_green=130,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=62; +update zone set underworld=-112,minclip=300,maxclip=300,fog_minclip=10,fog_maxclip=300,fog_blue=60,fog_red=40,fog_green=10,sky=0,ztype=1,zone_exp_multiplier=1.3,walkspeed=0.4,time_type=2 where zoneidnumber=63; +update zone set underworld=-364,minclip=600,maxclip=600,fog_minclip=0,fog_maxclip=0,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=0,zone_exp_multiplier=1,walkspeed=0.4,time_type=2 where zoneidnumber=64; +update zone set underworld=-180,minclip=140,maxclip=140,fog_minclip=10,fog_maxclip=140,fog_blue=20,fog_red=40,fog_green=45,sky=0,ztype=255,zone_exp_multiplier=1.5,walkspeed=0.4,time_type=2 where zoneidnumber=65; +update zone set underworld=-292,minclip=140,maxclip=140,fog_minclip=10,fog_maxclip=140,fog_blue=20,fog_red=50,fog_green=45,sky=0,ztype=255,zone_exp_multiplier=0.8,walkspeed=0.4,time_type=2 where zoneidnumber=66; +update zone set underworld=-112,minclip=175,maxclip=175,fog_minclip=10,fog_maxclip=175,fog_blue=20,fog_red=70,fog_green=50,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=67; +update zone set underworld=-32756,minclip=800,maxclip=1400,fog_minclip=10,fog_maxclip=1000,fog_blue=140,fog_red=150,fog_green=170,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=68; +update zone set underworld=-342,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=800,fog_blue=220,fog_red=200,fog_green=200,sky=1,ztype=0,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=69; +update zone set underworld=-486,minclip=800,maxclip=1400,fog_minclip=10,fog_maxclip=1000,fog_blue=140,fog_red=100,fog_green=100,sky=1,ztype=0,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=70; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=3,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=71; +update zone set underworld=-42,minclip=800,maxclip=1400,fog_minclip=10,fog_maxclip=1000,fog_blue=10,fog_red=255,fog_green=50,sky=5,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=72; +update zone set underworld=-120,minclip=180,maxclip=180,fog_minclip=10,fog_maxclip=180,fog_blue=45,fog_red=25,fog_green=35,sky=0,ztype=255,zone_exp_multiplier=0.9,walkspeed=0.4,time_type=2 where zoneidnumber=73; +update zone set underworld=-72,minclip=600,maxclip=600,fog_minclip=10,fog_maxclip=600,fog_blue=200,fog_red=220,fog_green=220,sky=1,ztype=0,zone_exp_multiplier=0.9,walkspeed=0.4,time_type=2 where zoneidnumber=74; +update zone set underworld=-320,minclip=400,maxclip=1000,fog_minclip=200,fog_maxclip=850,fog_blue=150,fog_red=150,fog_green=150,sky=1,ztype=0,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=75; +update zone set underworld=-1000,minclip=200,maxclip=200,fog_minclip=30,fog_maxclip=200,fog_blue=128,fog_red=128,fog_green=128,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=76; +update zone set underworld=-454,minclip=1000,maxclip=1500,fog_minclip=10,fog_maxclip=1500,fog_blue=100,fog_red=100,fog_green=100,sky=1,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=77; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=180; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=53; +update zone set underworld=-370,minclip=300,maxclip=1400,fog_minclip=200,fog_maxclip=800,fog_blue=235,fog_red=235,fog_green=235,sky=1,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=78; +update zone set underworld=-500,minclip=300,maxclip=800,fog_minclip=60,fog_maxclip=600,fog_blue=210,fog_red=210,fog_green=235,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=79; +update zone set underworld=-85,minclip=500,maxclip=500,fog_minclip=50,fog_maxclip=500,fog_blue=5,fog_red=180,fog_green=5,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=80; +update zone set underworld=-400,minclip=300,maxclip=800,fog_minclip=100,fog_maxclip=300,fog_blue=0,fog_red=0,fog_green=15,sky=0,ztype=1,zone_exp_multiplier=0.95,walkspeed=0.4,time_type=2 where zoneidnumber=81; +update zone set underworld=-170,minclip=100,maxclip=300,fog_minclip=40,fog_maxclip=300,fog_blue=80,fog_red=150,fog_green=120,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=82; +update zone set underworld=-270,minclip=300,maxclip=1000,fog_minclip=60,fog_maxclip=400,fog_blue=210,fog_red=210,fog_green=200,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=83; +update zone set underworld=-350,minclip=300,maxclip=1800,fog_minclip=200,fog_maxclip=800,fog_blue=235,fog_red=235,fog_green=235,sky=4,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=84; +update zone set underworld=-1000,minclip=300,maxclip=1800,fog_minclip=200,fog_maxclip=800,fog_blue=235,fog_red=235,fog_green=235,sky=1,ztype=1,zone_exp_multiplier=0.6,walkspeed=0.4,time_type=2 where zoneidnumber=85; +update zone set underworld=-1000,minclip=300,maxclip=1800,fog_minclip=200,fog_maxclip=600,fog_blue=235,fog_red=235,fog_green=235,sky=4,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=86; +update zone set underworld=-776,minclip=300,maxclip=1200,fog_minclip=60,fog_maxclip=400,fog_blue=235,fog_red=235,fog_green=235,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=87; +update zone set underworld=-400,minclip=200,maxclip=800,fog_minclip=20,fog_maxclip=200,fog_blue=0,fog_red=0,fog_green=10,sky=0,ztype=255,zone_exp_multiplier=1.1,walkspeed=0.4,time_type=2 where zoneidnumber=88; +update zone set underworld=-300,minclip=100,maxclip=600,fog_minclip=50,fog_maxclip=400,fog_blue=60,fog_red=20,fog_green=10,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=89; +update zone set underworld=-130,minclip=200,maxclip=400,fog_minclip=50,fog_maxclip=275,fog_blue=60,fog_red=90,fog_green=110,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=90; +update zone set underworld=-430,minclip=100,maxclip=1800,fog_minclip=200,fog_maxclip=600,fog_blue=200,fog_red=235,fog_green=200,sky=2,ztype=1,zone_exp_multiplier=0.8,walkspeed=0.4,time_type=2 where zoneidnumber=91; +update zone set underworld=-777,minclip=300,maxclip=1800,fog_minclip=200,fog_maxclip=800,fog_blue=235,fog_red=235,fog_green=235,sky=4,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=92; +update zone set underworld=-762,minclip=300,maxclip=1800,fog_minclip=200,fog_maxclip=800,fog_blue=235,fog_red=235,fog_green=235,sky=1,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=93; +update zone set underworld=-605,minclip=100,maxclip=800,fog_minclip=60,fog_maxclip=200,fog_blue=210,fog_red=200,fog_green=235,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=94; +update zone set underworld=-648,minclip=100,maxclip=1000,fog_minclip=60,fog_maxclip=250,fog_blue=213,fog_red=210,fog_green=235,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=95; +update zone set underworld=-611,minclip=300,maxclip=1400,fog_minclip=100,fog_maxclip=700,fog_blue=230,fog_red=225,fog_green=225,sky=4,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=96; +update zone set underworld=-220,minclip=200,maxclip=800,fog_minclip=10,fog_maxclip=200,fog_blue=20,fog_red=50,fog_green=50,sky=0,ztype=1,zone_exp_multiplier=1.5,walkspeed=0.4,time_type=2 where zoneidnumber=97; +update zone set underworld=-350,minclip=600,maxclip=1400,fog_minclip=10,fog_maxclip=800,fog_blue=220,fog_red=200,fog_green=200,sky=1,ztype=0,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=98; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=130; +update zone set underworld=-1000,minclip=600,maxclip=1200,fog_minclip=10,fog_maxclip=800,fog_blue=235,fog_red=235,fog_green=235,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=100; +update zone set underworld=-10000,minclip=300,maxclip=800,fog_minclip=100,fog_maxclip=300,fog_blue=0,fog_red=0,fog_green=15,sky=0,ztype=1,zone_exp_multiplier=1.5,walkspeed=0.4,time_type=2 where zoneidnumber=101; +update zone set underworld=-272,minclip=100,maxclip=800,fog_minclip=10,fog_maxclip=350,fog_blue=20,fog_red=50,fog_green=20,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=102; +update zone set underworld=-405,minclip=100,maxclip=600,fog_minclip=30,fog_maxclip=300,fog_blue=6,fog_red=90,fog_green=53,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=103; +update zone set underworld=-210,minclip=210,maxclip=600,fog_minclip=30,fog_maxclip=210,fog_blue=25,fog_red=20,fog_green=10,sky=0,ztype=1,zone_exp_multiplier=0.95,walkspeed=0.4,time_type=2 where zoneidnumber=104; +update zone set underworld=-230,minclip=600,maxclip=1400,fog_minclip=50,fog_maxclip=400,fog_blue=200,fog_red=160,fog_green=180,sky=0,ztype=255,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=105; +update zone set underworld=-170,minclip=100,maxclip=300,fog_minclip=40,fog_maxclip=300,fog_blue=80,fog_red=150,fog_green=120,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=106; +update zone set underworld=-400,minclip=300,maxclip=800,fog_minclip=100,fog_maxclip=300,fog_blue=0,fog_red=0,fog_green=15,sky=0,ztype=1,zone_exp_multiplier=0.95,walkspeed=0.4,time_type=2 where zoneidnumber=107; +update zone set underworld=-220,minclip=1200,maxclip=1200,fog_minclip=100,fog_maxclip=1200,fog_blue=0,fog_red=20,fog_green=0,sky=2,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=108; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=109; +update zone set underworld=-250,minclip=800,maxclip=1800,fog_minclip=200,fog_maxclip=1800,fog_blue=200,fog_red=200,fog_green=200,sky=4,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=110; +update zone set underworld=-31,minclip=150,maxclip=350,fog_minclip=10,fog_maxclip=350,fog_blue=25,fog_red=25,fog_green=25,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=111; +update zone set underworld=-10000,minclip=300,maxclip=600,fog_minclip=10,fog_maxclip=500,fog_blue=130,fog_red=10,fog_green=130,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=112; +update zone set underworld=-2000,minclip=300,maxclip=600,fog_minclip=20,fog_maxclip=500,fog_blue=50,fog_red=10,fog_green=10,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=113; +update zone set underworld=-250,minclip=100,maxclip=600,fog_minclip=100,fog_maxclip=600,fog_blue=200,fog_red=50,fog_green=0,sky=1,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=114; +update zone set underworld=-150,minclip=300,maxclip=600,fog_minclip=100,fog_maxclip=300,fog_blue=25,fog_red=25,fog_green=25,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=115; +update zone set underworld=-950,minclip=1500,maxclip=1800,fog_minclip=200,fog_maxclip=1800,fog_blue=200,fog_red=200,fog_green=200,sky=4,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=116; +update zone set underworld=-110,minclip=1000,maxclip=1800,fog_minclip=200,fog_maxclip=1800,fog_blue=180,fog_red=180,fog_green=180,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=117; +update zone set underworld=-1061,minclip=800,maxclip=1800,fog_minclip=200,fog_maxclip=1800,fog_blue=172,fog_red=160,fog_green=160,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=118; +update zone set underworld=-300,minclip=300,maxclip=1200,fog_minclip=60,fog_maxclip=600,fog_blue=254,fog_red=254,fog_green=254,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=119; +update zone set underworld=-439,minclip=800,maxclip=1800,fog_minclip=200,fog_maxclip=1800,fog_blue=160,fog_red=128,fog_green=128,sky=4,ztype=1,zone_exp_multiplier=0.8,walkspeed=0.4,time_type=2 where zoneidnumber=120; +update zone set underworld=-1000,minclip=400,maxclip=800,fog_minclip=0,fog_maxclip=0,fog_blue=0,fog_red=0,fog_green=0,sky=255,ztype=0,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=121; +update zone set underworld=-355,minclip=1500,maxclip=2000,fog_minclip=10,fog_maxclip=2000,fog_blue=35,fog_red=35,fog_green=50,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=123; +update zone set underworld=-2500,minclip=100,maxclip=1200,fog_minclip=30,fog_maxclip=300,fog_blue=10,fog_red=60,fog_green=10,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=124; +update zone set underworld=-400,minclip=300,maxclip=600,fog_minclip=10,fog_maxclip=500,fog_blue=130,fog_red=30,fog_green=100,sky=0,ztype=0,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=125; +update zone set underworld=-10000,minclip=300,maxclip=800,fog_minclip=60,fog_maxclip=600,fog_blue=210,fog_red=210,fog_green=235,sky=0,ztype=1,zone_exp_multiplier=1,walkspeed=0.4,time_type=2 where zoneidnumber=126; +update zone set underworld=-1000,minclip=300,maxclip=1200,fog_minclip=60,fog_maxclip=1200,fog_blue=100,fog_red=0,fog_green=50,sky=0,ztype=1,zone_exp_multiplier=0.8,walkspeed=0.4,time_type=2 where zoneidnumber=127; +update zone set underworld=-1200,minclip=300,maxclip=800,fog_minclip=200,fog_maxclip=800,fog_blue=220,fog_red=80,fog_green=80,sky=0,ztype=1,zone_exp_multiplier=1,walkspeed=0.4,time_type=2 where zoneidnumber=128; +update zone set underworld=-160,minclip=300,maxclip=600,fog_minclip=100,fog_maxclip=300,fog_blue=25,fog_red=25,fog_green=25,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=129; +update zone set underworld=-510,minclip=100,maxclip=650,fog_minclip=200,fog_maxclip=600,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=150; +update zone set underworld=-200,minclip=100,maxclip=400,fog_minclip=200,fog_maxclip=400,fog_blue=50,fog_red=50,fog_green=50,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=151; +update zone set underworld=-510,minclip=100,maxclip=1000,fog_minclip=200,fog_maxclip=300,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=152; +update zone set underworld=-150,minclip=100,maxclip=400,fog_minclip=100,fog_maxclip=300,fog_blue=10,fog_red=10,fog_green=10,sky=0,ztype=1,zone_exp_multiplier=0.8,walkspeed=0.4,time_type=2 where zoneidnumber=153; +update zone set underworld=-450,minclip=100,maxclip=650,fog_minclip=450,fog_maxclip=600,fog_blue=50,fog_red=50,fog_green=50,sky=0,ztype=1,zone_exp_multiplier=0.9,walkspeed=0.4,time_type=2 where zoneidnumber=154; +update zone set underworld=-1000,minclip=100,maxclip=600,fog_minclip=10,fog_maxclip=600,fog_blue=5,fog_red=5,fog_green=5,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=155; +update zone set underworld=-10000,minclip=100,maxclip=700,fog_minclip=1,fog_maxclip=700,fog_blue=10,fog_red=10,fog_green=10,sky=0,ztype=1,zone_exp_multiplier=1.6,walkspeed=0.4,time_type=2 where zoneidnumber=156; +update zone set underworld=-10000,minclip=100,maxclip=600,fog_minclip=100,fog_maxclip=600,fog_blue=50,fog_red=25,fog_green=50,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=157; +update zone set underworld=-200,minclip=508,maxclip=800,fog_minclip=50,fog_maxclip=800,fog_blue=60,fog_red=90,fog_green=110,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=158; +update zone set underworld=-200,minclip=100,maxclip=800,fog_minclip=100,fog_maxclip=800,fog_blue=72,fog_red=72,fog_green=72,sky=9,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=159; +update zone set underworld=-6000,minclip=100,maxclip=800,fog_minclip=10,fog_maxclip=800,fog_blue=40,fog_red=100,fog_green=40,sky=7,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=160; +update zone set underworld=-500,minclip=100,maxclip=700,fog_minclip=1,fog_maxclip=700,fog_blue=10,fog_red=10,fog_green=10,sky=0,ztype=1,zone_exp_multiplier=0.8,walkspeed=0.4,time_type=2 where zoneidnumber=161; +update zone set underworld=-10000,minclip=100,maxclip=400,fog_minclip=10,fog_maxclip=400,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=1,zone_exp_multiplier=1,walkspeed=0.4,time_type=2 where zoneidnumber=162; +update zone set underworld=-1000,minclip=100,maxclip=1800,fog_minclip=100,fog_maxclip=1000,fog_blue=0,fog_red=0,fog_green=0,sky=8,ztype=1,zone_exp_multiplier=0.9,walkspeed=0.4,time_type=2 where zoneidnumber=163; +update zone set underworld=-10000,minclip=100,maxclip=1400,fog_minclip=100,fog_maxclip=1400,fog_blue=50,fog_red=50,fog_green=50,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=164; +update zone set underworld=-500,minclip=100,maxclip=1500,fog_minclip=200,fog_maxclip=1500,fog_blue=122,fog_red=11,fog_green=76,sky=7,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=165; +update zone set underworld=-600,minclip=100,maxclip=2000,fog_minclip=200,fog_maxclip=2000,fog_blue=40,fog_red=100,fog_green=110,sky=7,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=166; +update zone set underworld=-500,minclip=100,maxclip=1000,fog_minclip=100,fog_maxclip=1000,fog_blue=80,fog_red=70,fog_green=50,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=167; +update zone set underworld=-2000,minclip=100,maxclip=1000,fog_minclip=10,fog_maxclip=1000,fog_blue=255,fog_red=200,fog_green=230,sky=6,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=168; +update zone set underworld=-2000,minclip=100,maxclip=700,fog_minclip=10,fog_maxclip=700,fog_blue=255,fog_red=200,fog_green=230,sky=6,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=169; +update zone set underworld=-500,minclip=100,maxclip=1000,fog_minclip=200,fog_maxclip=1000,fog_blue=0,fog_red=0,fog_green=0,sky=7,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=170; +update zone set underworld=-500,minclip=100,maxclip=2000,fog_minclip=100,fog_maxclip=700,fog_blue=24,fog_red=24,fog_green=24,sky=9,ztype=1,zone_exp_multiplier=0.65,walkspeed=0.4,time_type=2 where zoneidnumber=171; +update zone set underworld=-150,minclip=100,maxclip=800,fog_minclip=10,fog_maxclip=800,fog_blue=65,fog_red=45,fog_green=4,sky=7,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=172; +update zone set underworld=-2000,minclip=100,maxclip=1000,fog_minclip=10,fog_maxclip=1000,fog_blue=255,fog_red=200,fog_green=230,sky=6,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=173; +update zone set underworld=-2000,minclip=100,maxclip=1000,fog_minclip=10,fog_maxclip=1000,fog_blue=255,fog_red=200,fog_green=230,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=174; +update zone set underworld=-10000,minclip=100,maxclip=700,fog_minclip=100,fog_maxclip=700,fog_blue=20,fog_red=220,fog_green=20,sky=0,ztype=1,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=175; +update zone set underworld=-1000,minclip=100,maxclip=700,fog_minclip=10,fog_maxclip=700,fog_blue=30,fog_red=30,fog_green=10,sky=7,ztype=1,zone_exp_multiplier=0.9,walkspeed=0.4,time_type=2 where zoneidnumber=176; +update zone set underworld=-510,minclip=100,maxclip=500,fog_minclip=200,fog_maxclip=500,fog_blue=1,fog_red=1,fog_green=1,sky=0,ztype=1,zone_exp_multiplier=0.85,walkspeed=0.4,time_type=2 where zoneidnumber=179; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=183; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=184; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=185; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=200; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=201; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=202; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=203; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=204; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=205; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=206; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=207; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=208; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=209; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=210; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=211; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=212; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=213; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=214; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=215; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=216; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=217; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=218; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=219; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=220; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=221; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=222; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=223; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=181; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=186; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=224; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=225; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=226; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=227; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=228; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=277; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=278; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=229; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=234; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=239; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=244; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=249; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=254; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=259; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=264; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=232; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=237; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=242; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=247; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=252; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=257; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=262; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=267; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=271; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=275; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=233; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=238; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=243; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=248; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=253; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=258; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=263; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=268; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=272; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=276; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=230; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=235; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=240; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=245; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=250; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=255; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=260; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=265; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=269; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=273; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=231; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=236; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=241; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=246; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=251; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=256; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=261; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=266; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=270; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=274; +update zone set underworld=-2030,minclip=1500,maxclip=2000,fog_minclip=500,fog_maxclip=2000,fog_blue=0,fog_red=0,fog_green=0,sky=0,ztype=255,zone_exp_multiplier=0.75,walkspeed=0.4,time_type=2 where zoneidnumber=995; \ No newline at end of file diff --git a/utils/deprecated/apathing/actions.cpp b/utils/deprecated/apathing/actions.cpp new file mode 100644 index 000000000..387f1062e --- /dev/null +++ b/utils/deprecated/apathing/actions.cpp @@ -0,0 +1,2130 @@ + +//put all the non-boost crap which changes a lot in here +//to reduce compile times, cause boost is a bitch +#include "../common/types.h" +#include "../zone/map.h" +#include "apathing.h" +#include "quadtree.h" +#include +#include +#include +#include +#include +#include "gpoint.h" + +#include +#include +#include +#include +using namespace std; + + + +//stats variables... quite lazy +int load_split_paths = 0; +int z_fixed_count = 0; +int z_not_fixed_count = 0; +int z_no_map_count = 0; +float z_fixed_diffs = 0; +float z_not_fixed_diffs = 0; +int wp_reduce_count = 0; +int trivial_merge_count = 0; +int closest_merge_count = 0; +int closest_merge2_count = 0; +int link_spawn_count = 0; +int link_spawn_invalid = 0; +int link_spawn2_count = 0; +int link_spawn3_count = 0; +int link_spawn_nocount = 0; +int combine_broke_los = 0; +int combined_grid_points = 0; +int removed_edges_los = 0; +int removed_long_edges_los = 0; +int broke_paths = 0; +int cross_edge_count = 0; +int cross_add_count = 0; +int los_cache_misses = 0; +int los_cache_hits = 0; + +/* +//ye-olde prototypes +void repair_a_high_waypoint(Map *map, PathNode *it); +void repair_high_waypoints(Map *map, list &db_paths, list &db_spawns); +bool almost_colinear(PathNode *first, PathNode *second, PathNode *third); +void reduce_waypoints(list &db_paths); +//void build_big_graph(PathGraph *big, list &db_paths, list &db_spawns); +void combine_trivial_grids(Map *map, list &db_paths); +void combine_closest_grids(Map *map, list &db_paths); +void link_spawns(Map *map, PathGraph *big, list &db_spawns); +void combine_grid_points(Map *map, PathGraph *big, float close_enough); +void draw_paths(Map *map, list &edges, const char *fname); +void draw_paths2(Map *map, list &edges1, list &edges2, const char *fname); +void check_edge_los(Map *map, PathGraph *big); +void check_long_edge_los(Map *map, PathGraph *big); +bool CheckLOS(Map *map, PathNode *from, PathNode *to); +void rebuild_node_list(list &edges, list &nodes, list *excess_nodes = NULL); +//void edge_stats(list &edges, const char *s); + +void DrawGradientLine(gdImagePtr im, GPoint *first, GPoint *second, vector &colors); +void allocateGradient(gdImagePtr im, float r1, float g1, float b1, float r2, float g2, float b2, + float min, float max, float divs, vector &colors); +*/ + +void repair_a_high_waypoint(Map *map, PathNode *it) { + VERTEX pt, res; + pt.x = it->x; + pt.y = it->y; + pt.z = it->z + 10; + + float newz = map->FindBestZ(map->GetRoot(), pt, &res); + + if(newz == BEST_Z_INVALID) { + pt.x += X_JITTER; + pt.y += Y_JITTER; + newz = map->FindBestZ(map->GetRoot(), pt, &res); + } + + if(newz != BEST_Z_INVALID) { + newz += 6; //just some arbitrary height + float diff = it->z - newz; + if(diff > MIN_FIX_Z) { + z_fixed_count++; + z_fixed_diffs += diff; + it->z = newz; + } else { + z_not_fixed_count++; + z_not_fixed_diffs += diff; + } + } else { + z_no_map_count++; + //printf("Missed: (%.3f, %.3f, %.3f)\n", it->x, it->y, it->z); + } +} + +void repair_high_waypoints(Map *map, list &db_paths, list &db_spawns) { +/* - do not wanna drop people below docks though, so set min height first + - for each waypoint/spawn point + - if the best Z below the point (+6 on Z) is more than DROP_HEIGHT + - then lower the waypoint to bestZ + 10ish*/ + list::iterator cur, end; + cur = db_paths.begin(); + end = db_paths.end(); + for(; cur != end; cur++) { + PathGraph *g = *cur; + list::iterator cur2,end2; + cur2 = g->nodes.begin(); + end2 = g->nodes.end(); + for(; cur2 != end2; cur2++) { + repair_a_high_waypoint(map, *cur2); + } + } + + list::iterator cur3,end3; + cur3 = db_spawns.begin(); + end3 = db_spawns.end(); + for(; cur3 != end3; cur3++) { + repair_a_high_waypoint(map, *cur3); + } +} + +bool almost_colinear(PathNode *first, PathNode *second, PathNode *third) { + //basically a dot product and a compare. + GVector v1(*first, *second); + GVector v2(*second, *third); + + //the - operator is apparently not working or something + v1.x = second->x - first->x; + v1.y = second->y - first->y; + v1.z = second->z - first->z; + + v2.x = third->x - second->x; + v2.y = third->y - second->y; + v2.z = third->z - second->z; + + //just another option to consider: + //v1.z = 0; + //v2.z = 0; + + +/* printf("(%.3f, %.3f, %.3f) (%.3f, %.3f, %.3f) (%.3f, %.3f, %.3f)\n", + first->x, first->y, first->z, + second->x, second->y, second->z, + third->x, third->y, third->z); + printf("v1(%.3f, %.3f) v2(%.3f, %.3f)\n", v1.x, v1.y, v2.x, v2.y); +// printf("BB(%.3f, %.3f) BB(%.3f, %.3f)\n", , ,, ); +*/ + + v1.normalize(); + v2.normalize(); + + float cos_angle = v1.dot3(v2); + + return(cos_angle > ALMOST_COLINEAR_COS); +} + +void reduce_waypoints(list &db_paths) { +/* + - For each waypoint W where 0 <= W < N-2 + - determine unit vector from W to W+1 + - determine unit vector from W+1 to W+2 + - dot product the two vectors + - if the dot product is very close to 1.0, eliminate W+1, and connect W to W+2 + - Run code from node W again, dont go to W+2 next +*/ + list::iterator cur, end; + cur = db_paths.begin(); + end = db_paths.end(); + for(; cur != end; cur++) { + PathGraph *g = *cur; + if(g->nodes.size() < 3) + continue; + + list::iterator cur2,end2,trail; + PathNode *first,*second,*last; +RESTART_WP_REDUCE: + cur2 = g->nodes.begin(); + end2 = g->nodes.end(); + + first = *cur2; + cur2++; + second = *cur2; + trail = cur2; + cur2++; + + int pos = 2; + for(; cur2 != end2; cur2++) { + + last = *cur2; + if(almost_colinear(first, second, last)) { + //we are removing the second one + wp_reduce_count++; + + //printf("pos=%d/%d\n", pos, g->nodes.size()); + //printf("trail = 0x%x(0x%x) (%.3f, %.3f, %.3f)\n", + // *trail, second, (*trail)->x, (*trail)->y, (*trail)->z); + //trail = cur2; + + //need to do something with its old edges... + //we can assume at this point that there is only + //one edge starting from us and one edge ending at us + list::iterator cure, ende, rme = g->edges.end(); + cure = g->edges.begin(); + ende = g->edges.end(); + + bool found_one = false; + for(; cure != ende; cure++) { + if((*cure)->to == second) { + (*cure)->to = last; + if(found_one) + break; + found_one = true; + } else if((*cure)->from == second) { + rme = cure; + if(found_one) + break; + found_one = true; + } + } + if(rme != g->edges.end()) + g->edges.erase(rme); + + g->nodes.erase(trail); + delete second; + //this is doing something fucked up, so we'll play dumb + goto RESTART_WP_REDUCE; + } else { + + first = second; + trail++; + } + pos++; + second = last; + } + } + +} + +void break_long_lines(list &db_paths) { +/* + Idea being to split up long lines into several line segments, + generating several more nodes along paths in a controlled fashion. +*/ + GVector v1; + + GPoint curp; + GPoint last; + + PathEdge *e,*ee; + PathNode *n, *last_node; + + float cutlen2 = SPLIT_LINE_LENGTH*SPLIT_LINE_LENGTH; + list::iterator cur4,end4; + + + list::iterator cur, end; + cur = db_paths.begin(); + end = db_paths.end(); + for(; cur != end; cur++) { + PathGraph *g = *cur; + + cur4 = g->edges.begin(); + end4 = g->edges.end(); + for(; cur4 != end4; cur4++) { + e = *cur4; + + //first check length of the edge... + float len2 = e->from->Dist2(e->to); + if(len2 < cutlen2) + continue; + +//printf("Cut (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f) d2=%.3f\n", e->from->x, e->from->y, e->from->z, e->to->x, e->to->y, e->to->z, len2); + + float len = sqrt(len2); + v1.x = (e->to->x - e->from->x)/len; + v1.y = (e->to->y - e->from->y)/len; + v1.z = (e->to->z - e->from->z)/len; + + float cuts = len / SPLIT_LINE_INTERVAL; + + v1 *= len / cuts; + + curp = *e->from; + last_node = e->from; //first source is the original from node + for(; cuts > 1; cuts -= 1) { + curp += v1; + + n = new PathNode(curp); + ee = new PathEdge(last_node, n); + last_node = n; + +//printf(" (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f) d2=%.3f\n", ee->from->x, ee->from->y, ee->from->z, ee->to->x, ee->to->y, ee->to->z, e->from->Dist2(e->to)); + g->edges.push_back(ee); + g->nodes.push_back(n); + + broke_paths++; + } + //set the old edge to point from the last break to the original end node + e->from = last_node; + } + } + +} + + +void combine_trivial_grids(Map *map, list &db_paths) { + list::iterator cur, end, check_cur, check_end; + PathGraph *g; + + float CloseEnough2 = CLOSE_ENOUGH*CLOSE_ENOUGH; + + int cur_pos = 0; + int r; + bool merge; //do we merge the current two grids + PathNode *match1 = NULL, *match2 = NULL; //the two points causing the merge + PathGraph *look = NULL; //the grid were looking at for merging into + +//we are restarting so we dont have to deal with iterators +//when we delete a grid... they make my life difficult +RESTART_TRIVIAL_COMBINE: + cur = db_paths.begin(); + end = db_paths.end(); + for(r = 0; r < cur_pos; r++) + cur++; + for(; cur != end; cur++, cur_pos++) { + g = *cur; + check_cur = cur; + check_cur++; + check_end = db_paths.end(); + + merge = false; + + for(; check_cur != check_end && !merge; check_cur++) { + look = *check_cur; + if(g == look) + continue; + list::iterator cur2,end2,cur3,end3; + cur2 = g->nodes.begin(); + end2 = g->nodes.end(); + for(; cur2 != end2 && !merge; cur2++) { + + PathNode *git = *cur2; + cur3 = look->nodes.begin(); + end3 = look->nodes.end(); + for(; cur3 != end3; cur3++) { + if(git->Dist2(*cur3) < CloseEnough2) { + match1 = git; + match2 = *cur3; + merge = true; + break; + } + } + } + } + + if(look && merge) { + /*printf("Merge on (%.3f, %.3f, %.3f) (%.3f, %.3f, %.3f)\n", + match1->x, match1->y, match1->z, + match2->x, match2->y, match2->z); + */ + + //merge look into cur + //this may or may not be a good idea: +// look->nodes.reserve(look->nodes.size() + g->nodes.size()); +// look->edges.reserve(look->edges.size() + g->edges.size() + 1); + + //steal all the nodes + list::iterator cur2,end2; + cur2 = g->nodes.begin(); + end2 = g->nodes.end(); + for(; cur2 != end2; cur2++) { + if(*cur2 != match1) + look->nodes.push_back(*cur2); + } + + match1->valid = false; + + //steal all the edges + list::iterator cur4,end4; + cur4 = g->edges.begin(); + end4 = g->edges.end(); + for(; cur4 != end4; cur4++) { + PathEdge *e = *cur4; + //remap old node to new node + if(e->to == match1) + e->to = match2; + if(e->from == match1) + e->from = match2; + look->edges.push_back(e); + } + + //stop it from freeing up all the stuff we just stole. + g->nodes.clear(); + g->edges.clear(); + delete g; + + trivial_merge_count++; + + db_paths.erase(cur); + goto RESTART_TRIVIAL_COMBINE; + } + } +} + +void combine_closest_grids(Map *map, list &db_paths) { +/* + +run algorithm to connect all grids to eachother + - form one large connected graph + - maybe choose several connection points for each grid + - only if they are very different (begin, middle, end?? 3 closest disjoint?) + - check LOS for each connection + - avoid large Z variance if possible +*/ + list::iterator cur, end, check_cur, check_end; + PathGraph *g; + + int cur_pos = 0; + + float md2 = MERGE_MIN_SECOND_DIST*MERGE_MIN_SECOND_DIST; + + float dist, dist2, cdist; + PathGraph *source1 = NULL, *source2 = NULL; + PathNode *match1 = NULL, *match2 = NULL; //the two points closest together + PathNode *match3 = NULL, *match4 = NULL; //the two points 2nd closest +// VERTEX p1, p2, liz_res; + + PathGraph *look = NULL; //the grid were looking at for merging into + + bool printing = false; +// if(db_paths.size() > 100) { + printf("Combining Grids (%d dots)", db_paths.size()); + fflush(stdout); + printing = true; +// } + +//we are restarting so we dont have to deal with iterators +//when we delete a grid... they make my life difficult +RESTART_CLOSEST_COMBINE: + cur = db_paths.begin(); + end = db_paths.end(); + for(; cur_pos > 0; cur_pos--) + cur++; + //for each grid + for(; cur != end; cur++) { + cur_pos++; + g = *cur; + //check_cur = cur; + //check_cur++; + check_cur = db_paths.begin(); + check_end = db_paths.end(); + + if(printing) { + printf("."); + fflush(stdout); + } + + dist = 9999999999e100f; + dist2 = 9999999999e100f; + match1 = NULL; + match2 = NULL; + match3 = NULL; + match4 = NULL; + source1 = NULL; + source2 = NULL; + + //for each other grid + for(; check_cur != check_end; check_cur++) { + look = *check_cur; + if(g == look) + continue; + list::iterator cur2,end2,cur3,end3; + cur2 = g->nodes.begin(); + end2 = g->nodes.end(); + //for each node in g + for(; cur2 != end2; cur2++) { + + PathNode *git = *cur2; + cur3 = look->nodes.begin(); + end3 = look->nodes.end(); + //for each node in look + for(; cur3 != end3; cur3++) { + + cdist = git->Dist2(*cur3); + if(cdist > dist2) + continue; //not a candidate + + if(!CheckLOS(map, git, *cur3)) + continue; //cannot see + + if(cdist < dist) { + //new closest + //demote this closest to #2 if its far enough away + if(match2 && dist < dist2 && match2->Dist2(*cur3) > md2) { + dist2 = dist; + match3 = match1; + match4 = match2; + source2 = source1; + } + + //setup new closest + dist = cdist; + match1 = git; + match2 = *cur3; + source1 = look; + } else if(cdist < dist2 && match2->Dist2(*cur3) > md2) { + //new second closest + dist2 = cdist; + match3 = git; + match4 = *cur3; + source2 = look; + } + } + } + } + + if(look != NULL && source1 != NULL) { + /*printf("Link on (%.3f, %.3f, %.3f) (%.3f, %.3f, %.3f) at dist %.3f\n", + match1->x, match1->y, match1->z, + match2->x, match2->y, match2->z, dist); + printf("Combine %d nodes with %d nodes.\n", source1->nodes.size(), g->nodes.size()); + */ + + //merge look into cur + //this may or may not be a good idea: +// source1->nodes.reserve(look->nodes.size() + g->nodes.size()); +// source1->edges.reserve(look->edges.size() + g->edges.size() + 1); + + //steal all the nodes + /* this just gets done later by re-node so dont do it now. + + list::iterator cur2,end2; + cur2 = g->nodes.begin(); + end2 = g->nodes.end(); + for(; cur2 != end2; cur2++) { + + if(*cur2 != match1) + source1->nodes.push_back(*cur2); + }*/ + + //steal all the edges + list::iterator cur4,end4; + cur4 = g->edges.begin(); + end4 = g->edges.end(); + for(; cur4 != end4; cur4++) { + PathEdge *e = *cur4; + //remap old node to new node + if(e->to == match1) + e->to = match2; + if(e->from == match1) + e->from = match2; + + source1->edges.push_back(e); + } + + + + check_cur = cur; + check_cur++; + check_end = db_paths.end(); + for(; check_cur != check_end; check_cur++) { + look = *check_cur; + list::iterator cur4,end4; + cur4 = g->edges.begin(); + end4 = g->edges.end(); + for(; cur4 != end4; cur4++) { + PathEdge *e = *cur4; + //remap old node to new node + if(e->to == match1) + e->to = match2; + if(e->from == match1) + e->from = match2; + } + } + + closest_merge_count++; + } + + //kinda a hack... assume the 'same node merge' code will bridge this + //new point with the other point in the real 'source2' graph. + if(source2 != NULL) { + /*printf("Link2 on (%.3f, %.3f, %.3f) (%.3f, %.3f, %.3f) at dist %.3f\n", + match3->x, match3->y, match3->z, + match4->x, match4->y, match4->z, dist);*/ + + PathNode *n = new PathNode(*match4); + source1->nodes.push_back(n); + source1->add_edge(match3, n); + + closest_merge2_count++; + } + + + if(source1 != NULL) { + //stop it from freeing up all the stuff we just stole. + g->nodes.clear(); + g->edges.clear(); + delete g; + + db_paths.erase(cur); + goto RESTART_CLOSEST_COMBINE; + } + } + + if(printing) { + printf("\n"); + } + + + //finally just do a dummy merge of the remaining grids into the first one + g = db_paths.front(); + cur = db_paths.begin(); + end = db_paths.end(); + cur++; //skip the first one + for(; cur != end; cur++) { + g->add_edges((*cur)->edges); + } + + + //rebuild our node array based on the edges we have... + //this is because there is some sort of error where a node is used in + //an edge which is supposed to have been combined with another node. + //I cannot figure out why, and this fixes it without any harmful effects + //as far as I can tell, the trees still span and what not. + printf("Closest Merge: had %d nodes and %d edges\n", g->nodes.size(), g->edges.size()); + + +/* g->nodes.resize(0); + std::map havenodelist; + list::iterator cur4,end4; + cur4 = g->edges.begin(); + end4 = g->edges.end(); + for(; cur4 != end4; cur4++) { + PathEdge *e = *cur4; + //remap old node to new node + if(havenodelist.count(e->from) != 1) { + g->nodes.push_back(e->from); + havenodelist[e->from] = 1; + } + if(havenodelist.count(e->to) != 1) { + g->nodes.push_back(e->to); + havenodelist[e->to] = 1; + } + }*/ + rebuild_node_list(g->edges, g->nodes); + + printf("Closest Merge: re-node yeilded %d nodes and %d edges\n", g->nodes.size(), g->edges.size()); +} + + +/*void build_big_graph(PathGraph *big, list &db_paths, list &db_spawns) { +//basically just move all the nodes and edges into one big graph + list::iterator cur, end; + cur = db_paths.begin(); + end = db_paths.end(); + for(; cur != end; cur++) { + PathGraph *g = *cur; + + //this may or may not be a good idea: + big->nodes.reserve(big->nodes.size() + g->nodes.size()); + big->edges.reserve(big->edges.size() + g->edges.size()); + + //steal all the nodes + list::iterator cur2,end2; + cur2 = g->nodes.begin(); + end2 = g->nodes.end(); + for(; cur2 != end2; cur2++) { + big->nodes.push_back(*cur2); + } + + //steal all the edges + list::iterator cur4,end4; + cur4 = g->edges.begin(); + end4 = g->edges.end(); + for(; cur4 != end4; cur4++) { + big->edges.push_back(*cur4); + } + + g->nodes.clear(); + g->edges.clear(); + delete g; + } + db_paths.clear(); + + list::iterator cur3,end3; + big->nodes.reserve(big->nodes.size() + db_spawns.size()); + cur3 = db_spawns.begin(); + end3 = db_spawns.end(); + for(; cur3 != end3; cur3++) { + big->nodes.push_back(*cur3); + } + + db_spawns.clear(); + + +} +*/ + +void link_spawns(Map *map, PathGraph *big, list &db_spawns, + float maxdist, map< pair, bool > *edgelist) { +/* + +run algorithm to connect all spawn points to the big grid + - first find the closest node we have LOS to + - optionally try to find a second node too, just to give us more options + - I am not sure if this will buy us anything... since MST will very likely kill this link + - find the second closest node which has a vector thats not close to the closest + - for each node in big grid N + - Check LOS between mob and N + - find distance from mob to N, if not possibly 2nd closest: continue + - determine + - check LOS for each connection + - connect to 1 or 2 of the closest points (prolly 2 if possible) + - avoid large Z variance if possible +*/ + + //still not sure if this actually does us any good. +// big->nodes.reserve(big->nodes.size() + db_spawns.size()); + + + printf("Linking (%d dots)", db_spawns.size()); + fflush(stdout); + float md2 = SPAWN_MIN_SECOND_DIST*SPAWN_MIN_SECOND_DIST; + float maxdist2 = maxdist*maxdist; + + float dist,tmp; + PathNode *closest = NULL; + float dist2; + PathNode *closest2 = NULL; + float dist3; + PathNode *closest3 = NULL; + + VERTEX p1, liz_res /*, p2*/; + + list::iterator cur,end; + list::iterator cur3,end3; + + cur3 = db_spawns.begin(); + end3 = db_spawns.end(); + for(; cur3 != end3; cur3++) { + printf("."); + fflush(stdout); + PathNode *n = *cur3; + big->nodes.push_back(n); + p1.x = n->x; p1.y = n->y; p1.z = n->z; + //elevate a little, to about eye height + p1.z += 6.0f; + + //make sure this spawn point is even within the map + NodeRef mynode; + mynode = map->SeekNode(map->GetRoot(), p1.x, p1.y); + if(mynode == NODE_NONE) { + link_spawn_invalid++; + continue; + } + if(map->FindBestZ(mynode, p1, &liz_res) == BEST_Z_INVALID) { + link_spawn_invalid++; + continue; + } + + dist = 999999e100f; + closest = NULL; + dist2 = 999999e100f; + closest2 = NULL; + dist3 = 999999e100f; + closest3 = NULL; + + cur = big->nodes.begin(); + end = big->nodes.end(); + for(; cur != end; cur++) { + if(n == *cur) + continue; //dont link to ourself + + //if an edge list was supplied, make sure this edge isnt on it + if(edgelist) { + pair id; + if(int32(n) < int32(*cur)) { + id.first = *cur; + id.second = n; + } else { + id.first = n; + id.second = *cur; + } + if(edgelist->find(id) != edgelist->end()) + continue; //found in the list + } + + //get the distance between the + tmp = n->Dist2(*cur); + if(tmp > dist2) + continue; + + if(!CheckLOS(map, n, *cur)) + continue; //cannot see + + //we can see to it, see if its closer + if(tmp < dist) { + //its a new closest + + //see if we bump #2 + if(SPAWN_LINK_TWICE && closest && dist < dist2 && closest->Dist2(*cur) > md2) { + //the old #1 replaces #2 + //see if we bump #3 + if(SPAWN_LINK_THRICE && closest2 && dist2 < dist3 + && closest2->Dist2(*cur) > md2 + && closest2->Dist2(closest) > md2) { + dist3 = dist2; + closest3 = closest2; + } + dist2 = dist; + closest2 = closest; + } + + dist = tmp; + closest = *cur; + } + //we can assume closest is set, or else we would never get here + else if(SPAWN_LINK_TWICE && tmp < dist2 && closest->Dist2(*cur) > md2) { + //see if we bump #3 + if(SPAWN_LINK_THRICE && closest2 && dist2 < dist3 + && closest2->Dist2(*cur) > md2 + && closest2->Dist2(closest) > md2) { + dist3 = dist2; + closest3 = closest2; + } + dist2 = tmp; + closest2 = *cur; + } + //we can assume closest and closest2 are set, or else we would never get here + else if(SPAWN_LINK_THRICE && tmp < dist3 && closest->Dist2(*cur) > md2 && closest2->Dist2(*cur) > md2) { + dist3 = tmp; + closest3 = *cur; + } + } + + if(closest == NULL || dist > maxdist2) { + link_spawn_nocount++; + //should delete this point.... + continue; + } + +/*printf("SL (%.3f, %.3f, %.3f) (%.3f, %.3f, %.3f) d2=%.3f\n", + n->x, n->y, n->z, + closest->x, closest->y, closest->z, n->Dist2(closest)); + link_spawn_count++; +*/ + + big->add_edge(n, closest); + + if(SPAWN_LINK_TWICE && closest2 && dist2 < maxdist2) { + +/*printf("SL2 (%.3f, %.3f, %.3f) (%.3f, %.3f, %.3f) d2=%.3f\n", + n->x, n->y, n->z, + closest2->x, closest2->y, closest2->z, n->Dist2(closest2)); +*/ big->add_edge(n, closest2); + link_spawn2_count++; + } + if(SPAWN_LINK_THRICE && closest3 && dist3 < maxdist2) { + +/*printf("SL3 (%.3f, %.3f, %.3f) (%.3f, %.3f, %.3f) d2=%.3f\n", + n->x, n->y, n->z, + closest2->x, closest2->y, closest2->z, n->Dist2(closest2)); +*/ big->add_edge(n, closest3); + link_spawn3_count++; + } + } + + printf("\n"); +} + +map< pair, bool > _los_cache; + +bool CheckLOS(Map *map, PathNode *from, PathNode *to) { + static VERTEX p1, p2, liz_res; //no reason to allocate them several times + + //hack to make order on the ID not matter + if(int32(from) < int32(to)) { + PathNode *tmp = from; + from = to; + to = tmp; + } + + pair id(from, to); + if(_los_cache.count(id) == 1) { + los_cache_hits++; + return(_los_cache[id]); + } + + //if its a candidate, check LOS + p1.x = from->x; p1.y = from->y; p1.z = from->z; + p2.x = to->x; p2.y = to->y; p2.z = to->z; + //elevate a little, to about eye height + p1.z += 6.0f; p2.z += 6.0f; + + bool res = !map->LineIntersectsZone(p1, p2, 0.5, &liz_res); + + _los_cache[id] = res; + los_cache_misses++; + + return(res); +} + +void combine_grid_points(Map *map, PathGraph *big, float close_enough) { +/* +run an algorithm to remove redundancy: + - Two points close to eachother (connected or not): merge links into 1, delete other + - for each node, check dist to all other nodes... + - Two of the same link after merge: delete one + - for each node, for each link in that node, see if its the same as any other link + - do we need this? MST will eliminate these, but will run slower +*/ + list::iterator cur,end,cur2; + list::iterator cur4,end4; + PathNode *n,*f; + PathEdge *e; + + combined_grid_points = 0; + combine_broke_los = 0; + float ce2 = close_enough*close_enough; + bool merge; + int cur_pos = 0, stat = 0; + int r; + + printf("Combining."); + fflush(stdout); + +#ifdef COMBINE_CHECK_ALL_LOS + bool badlos = false; + //build our node->edge map for use later checking combine LOS + std::map > node_edges; + vector::iterator curE,endE; + + cur4 = big->edges.begin(); + end4 = big->edges.end(); + for(; cur4 != end4; cur4++) { + e = *cur4; + + if(node_edges.count(e->from) == 1) { + node_edges[e->from].push_back(e); + } else { + vector t(1); + t[0] = e; + node_edges[e->from] = t; + } + + if(node_edges.count(e->to) == 1) { + node_edges[e->to].push_back(e); + } else { + vector t(1); + t[0] = e; + node_edges[e->to] = t; + } + } + +#endif + +//restart since deleting a node pisses the iterators off and +//im too lazy to figure out how to make them happy right now +RESTART_GRID_CLEAN: + cur = big->nodes.begin(); + end = big->nodes.end(); +//printf("Starting clean at pos %d with %d nodes\n", cur_pos, big->nodes.size()); + for(r = 0; r < cur_pos; r++) + cur++; + for(; cur != end; cur++, cur_pos++, stat++) { + if(stat%1000 == 0) { + printf("."); + fflush(stdout); + } + + n = *cur; + cur2 = cur; + cur2++; + merge = false; + + if(!n->valid) + continue; + + for(; cur2 != end; cur2++) { + f = *cur2; + if(n == f) + continue; + if(n->Dist2(f) > ce2) { +//printf("Dist %f, ce2 %f\n", n->Dist2(f), ce2); + continue; + } +//printf("Distbbb %f, ce2 %f, (%f,%f), (%f,%f)\n", n->Dist2(f), ce2, n->x, n->y, f->x, f->y); + +#ifdef COMBINE_CHECK_ALL_LOS + //we should merge these. Now we wanna check to make sure combining + //them will not cause the old node's edges to become invalid. + +//printf("Checking combine LOS on %d, with %d edges.\n", r, big->edges.size()); + badlos = false; + + if(node_edges.count(f) == 1) { + curE = node_edges[f].begin(); + endE = node_edges[f].end(); + for(; curE != endE; curE++) { + e = *curE; + if(e->to == f) { + if(!CheckLOS(map, n, e->from)) { + badlos = true; + combine_broke_los++; + break; + } + } else if(e->from == f) { + if(!CheckLOS(map, n, e->to)) { + badlos = true; + combine_broke_los++; + break; + } + } + } + } + +/* cur4 = big->edges.begin(); + end4 = big->edges.end(); + for(; cur4 != end4; cur4++) { + e = *cur4; + if(e->to == f) { + if(!CheckLOS(map, n, e->from)) { + badlos = true; + combine_broke_los++; + break; + } + } else if(e->from == f) { + if(!CheckLOS(map, n, e->to)) { + badlos = true; + combine_broke_los++; + break; + } + } + } +*/ + if(badlos) + continue; +#else + //check LOS between the nodes is an OK compromise + if(!CheckLOS(map, n, f)) + continue; +#endif + //cant merge two forced nodes + if(n->forced && f->forced) + continue; + + //passed the checks, lets merge with it. + merge = true; + break; + } +// printf("checked %d, merge? %d\n", cur_pos, merge); + if(merge) { + f = *cur2; + + //normally we merge into n, from f. + //if f is forced, then we must reverse that... + if(f->forced) { + *cur = f; //swap them in the array + PathNode *tmp = f; + f = n; + n = tmp; + } + + //steal all its edges... + //changing references to old node to point to the other node + cur4 = big->edges.begin(); + end4 = big->edges.end(); + for(; cur4 != end4; cur4++) { + e = *cur4; + if(e->to == f) + e->to = n; + if(e->from == f) + e->from = n; + } + +#ifdef COMBINE_CHECK_ALL_LOS + //merge over the edge list too.. + if(node_edges.count(f) == 1 && node_edges.count(n) == 1) { + vector &dst = node_edges[n]; + curE = node_edges[f].begin(); + endE = node_edges[f].end(); + for(; curE != endE; curE++) { + e = *curE; + dst.push_back(e); + } + } +#endif + + combined_grid_points++; + + /*printf("Removed point using %d (0x%x and 0x%x)\n", cur_pos, n, f); + + printf("Pts (%.3f, %.3f, %.3f) (%.3f, %.3f, %.3f) at dist %.3f\n", + n->x, n->y, n->z, + f->x, f->y, f->z, n->Dist2(f));*/ + + //who needs to free memory, when we can leak it.... + //delete f; + + big->nodes.erase(cur2); + goto RESTART_GRID_CLEAN; + } + } + printf("\n"); +} + + +void draw_paths(Map *map, list &edges, list &edges2, const char *fname) { + FILE *pngout; + pngout = fopen(fname, "wb"); + if(pngout == NULL) { + printf("Unable to open %s\n", fname); + return; + } + + list::iterator cur,end; + PathEdge *e; + + gdImagePtr im; + int minx = int(map->GetMinX()); + int maxx = int(map->GetMaxX()); + int miny = int(map->GetMinY()); + int maxy = int(map->GetMaxY()); + +// float minz = map->GetMinZ(); +// float maxz = map->GetMaxZ(); + //find better z ranges + float minz = 9999e99; + float maxz = -9999e99; + cur = edges.begin(); + end = edges.end(); + for(; cur != end; cur++) { + e = *cur; + if((*cur)->from->z < minz) + minz = (*cur)->from->z; + if((*cur)->from->z > maxz) + maxz = (*cur)->from->z; + if((*cur)->to->z < minz) + minz = (*cur)->to->z; + if((*cur)->to->z > maxz) + maxz = (*cur)->to->z; + } + + + im = gdImageCreate((maxx - minx)/IMAGE_SCALE, (maxy - miny)/IMAGE_SCALE); + + //allocate this first, to make it the BG color. + /*int black =*/ gdImageColorAllocate(im, 0, 0, 0); + + int blue = gdImageColorAllocate(im, 200, 200, 255); + + + //draw our EQ map + cur = edges2.begin(); + end = edges2.end(); + for(; cur != end; cur++) { + e = *cur; + int x1 = int(e->from->x) - minx; + int y1 = int(e->from->y) - miny; + int x2 = int(e->to->x) - minx; + int y2 = int(e->to->y) - miny; + x1 /= IMAGE_SCALE; + y1 /= IMAGE_SCALE; + x2 /= IMAGE_SCALE; + y2 /= IMAGE_SCALE; + gdImageLine(im, x1, y1, x2, y2, blue); + } + + + GPoint p1, p2; + vector colors; + allocateGradient(im, 255, 255, 0, 255, 0, 0, minz, maxz, 100, colors); + + //draw the edges supplied with gradient lines + cur = edges.begin(); + end = edges.end(); + for(; cur != end; cur++) { + e = *cur; + + p1 = *e->from; + p2 = *e->to; + p1.x -= minx; + p1.y -= miny; + p2.x -= minx; + p2.y -= miny; + p1.x /= IMAGE_SCALE; + p1.y /= IMAGE_SCALE; + p2.x /= IMAGE_SCALE; + p2.y /= IMAGE_SCALE; + DrawGradientLine(im, &p1, &p2, colors); + } + + gdImagePng(im, pngout); + gdImageDestroy(im); + + fclose(pngout); + + printf("Wrote image: %s\n", fname); +} + + +void draw_paths2(Map *map, list &edges, list &edges2, list &edges3, list &spawns, const char *fname) { + FILE *pngout; + pngout = fopen(fname, "wb"); + if(pngout == NULL) { + printf("Unable to open %s\n", fname); + return; + } + + list::iterator cur,end; + list::iterator curn,endn; + PathEdge *e; + PathNode *n; + + gdImagePtr im; + int minx = int(map->GetMinX()); + int maxx = int(map->GetMaxX()); + int miny = int(map->GetMinY()); + int maxy = int(map->GetMaxY()); + + +// float minz = map->GetMinZ(); +// float maxz = map->GetMaxZ(); + //find better z ranges + float minz = 9999e99; + float maxz = -9999e99; + cur = edges2.begin(); + end = edges2.end(); + for(; cur != end; cur++) { + e = *cur; + if((*cur)->from->z < minz) + minz = (*cur)->from->z; + if((*cur)->from->z > maxz) + maxz = (*cur)->from->z; + if((*cur)->to->z < minz) + minz = (*cur)->to->z; + if((*cur)->to->z > maxz) + maxz = (*cur)->to->z; + } + + im = gdImageCreate((maxx - minx)/IMAGE_SCALE, (maxy - miny)/IMAGE_SCALE); + + //allocate this first, to make it the BG color. + /*int black =*/ gdImageColorAllocate(im, 0, 0, 0); + + int grey = gdImageColorAllocate(im, 100, 100, 100); + int purple = gdImageColorAllocate(im, 255, 100, 255); + +// int red = gdImageColorAllocate(im, 190, 0, 0); + int axis = gdImageColorAllocate(im, 75, 0, 0); + int blue = gdImageColorAllocate(im, 200, 200, 255); + int green = gdImageColorAllocate(im, 0, 255, 0); + + + //draw the axes + gdImageLine(im, -minx/IMAGE_SCALE, 0, -minx/IMAGE_SCALE, (maxy - miny)/IMAGE_SCALE, axis); + gdImageLine(im, 0, -miny/IMAGE_SCALE, (maxx - minx)/IMAGE_SCALE, -miny/IMAGE_SCALE, axis); + + + //draw the original paths in grey + //draw these first cause they are messy and not important really + cur = edges.begin(); + end = edges.end(); + for(; cur != end; cur++) { + e = *cur; + int x1 = int(e->from->x) - minx; + int y1 = int(e->from->y) - miny; + int x2 = int(e->to->x) - minx; + int y2 = int(e->to->y) - miny; + x1 /= IMAGE_SCALE; + y1 /= IMAGE_SCALE; + x2 /= IMAGE_SCALE; + y2 /= IMAGE_SCALE; + gdImageLine(im, x1, y1, x2, y2, grey); + } + + + //draw the EQ map second, so we can see it + cur = edges3.begin(); + end = edges3.end(); + for(; cur != end; cur++) { + e = *cur; + int x1 = int(e->from->x) - minx; + int y1 = int(e->from->y) - miny; + int x2 = int(e->to->x) - minx; + int y2 = int(e->to->y) - miny; + + //invert the EQ map in screen coords + /*int tmp; + tmp = x1; x1 = y1; y1 = tmp; + tmp = x2; x2 = y2; y2 = tmp;*/ + + x1 /= IMAGE_SCALE; + y1 /= IMAGE_SCALE; + x2 /= IMAGE_SCALE; + y2 /= IMAGE_SCALE; + gdImageLine(im, x1, y1, x2, y2, blue); + } + + GPoint p1, p2; + vector colors; + allocateGradient(im, 255, 255, 0, 255, 0, 0, minz, maxz, 100, colors); + + cur = edges2.begin(); + end = edges2.end(); + for(; cur != end; cur++) { + e = *cur; + + if(e->valid) { + p1 = *e->from; + p2 = *e->to; + p1.x -= minx; + p1.y -= miny; + p2.x -= minx; + p2.y -= miny; + p1.x /= IMAGE_SCALE; + p1.y /= IMAGE_SCALE; + p2.x /= IMAGE_SCALE; + p2.y /= IMAGE_SCALE; + DrawGradientLine(im, &p1, &p2, colors); + } else { + int x1 = int(e->from->x) - minx; + int y1 = int(e->from->y) - miny; + int x2 = int(e->to->x) - minx; + int y2 = int(e->to->y) - miny; + x1 /= IMAGE_SCALE; + y1 /= IMAGE_SCALE; + x2 /= IMAGE_SCALE; + y2 /= IMAGE_SCALE; + gdImageLine(im, x1, y1, x2, y2, green); +// printf("L (%d,%d,%.1f) -> (%d,%d,%.1f) dist=%.3f\n", x1, y1, e->from->z, x2, y2, e->to->z, e->from->Dist2(e->to)); + } + } + + curn = spawns.begin(); + endn = spawns.end(); + for(; curn != endn; curn++) { + n = *curn; + int x1 = int(n->x) - minx; + int y1 = int(n->y) - miny; + x1 /= IMAGE_SCALE; + y1 /= IMAGE_SCALE; + gdImageSetPixel(im, x1, y1, purple); + } + + gdImagePng(im, pngout); + gdImageDestroy(im); + + fclose(pngout); + + printf("Wrote image: %s\n", fname); +} + + +void check_edge_los(Map *map, PathGraph *big) { + list::iterator cur,end,tmp; + PathEdge *e; +// VERTEX p1, p2, liz_res; + cur = big->edges.begin(); + end = big->edges.end(); + for(; cur != end;) { + e = *cur; + +/* //if its a candidate, check LOS + p1.x = e->from->x; p1.y = e->from->y; p1.z = e->from->z; + p2.x = (e->to)->x; p2.y = (e->to)->y; p2.z = (e->to)->z; + //elevate a little, to about eye height + p1.z += 6.0f; p2.z += 6.0f; + + if(map->LineIntersectsZone(p1, p2, 0.1, &liz_res)) {*/ + if(!CheckLOS(map, e->from, e->to)) { + tmp = cur; + cur++; + big->edges.erase(tmp); + removed_edges_los++; + } else { + cur++; + } + } + +} + + +void check_long_edge_los(Map *map, PathGraph *big) { +#ifdef LONG_PATH_CHECK_LOS + list::iterator cur,end,tmp; + PathEdge *e; + + float ml2 = LONG_PATH_CHECK_LOS*LONG_PATH_CHECK_LOS; +// VERTEX p1, p2, liz_res; + cur = big->edges.begin(); + end = big->edges.end(); + for(; cur != end;) { + e = *cur; + + if(e->from->Dist2(e->to) < ml2) { + cur++; + continue; + } + +/* //if its a candidate, check LOS + p1.x = e->from->x; p1.y = e->from->y; p1.z = e->from->z; + p2.x = (e->to)->x; p2.y = (e->to)->y; p2.z = (e->to)->z; + //elevate a little, to about eye height + p1.z += 6.0f; p2.z += 6.0f; + + if(map->LineIntersectsZone(p1, p2, 0.1, &liz_res)) {*/ + if(!CheckLOS(map, e->from, e->to)) { + tmp = cur; + cur++; + big->edges.erase(tmp); + removed_long_edges_los++; + } else { + cur++; + } + } +#endif +} + +//take rgb as floats for simplicity +void allocateGradient(gdImagePtr im, float r1, float g1, float b1, float r2, float g2, float b2, + float min, float max, float divs, vector &colors) { + + float step = (max - min) / divs; + float rstep = (r2 - r1)/divs; + float gstep = (g2 - g1)/divs; + float bstep = (b2 - b1)/divs; + + ColorRecord c; + + c.height = min; + float r; + for(r = 0; r < divs; r++) { + c.color = gdImageColorAllocate(im, int(r1), int(g1), int(b1)); + c.height += step; + r1 += rstep; + g1 += gstep; + b1 += bstep; + colors.push_back(c); + } +} + + +void DrawGradientLine(gdImagePtr im, GPoint *first, GPoint *second, vector &colors) { + GVector v1(*first, *second); + +// float len = sqrt(first->Dist2(second)); + + //the - operator is apparently not working or something + v1.x = second->x - first->x; + v1.y = second->y - first->y; + v1.z = second->z - first->z; + +// float len = v1.length(); + +// v1.normalize(); //calcs length again, but im lazy. + + + GPoint cur(*first); + GPoint last; + + float step = 1/100.0f; + float pos; + + v1 *= step; + +/*printf("Line From (%.3f, %.3f, %.3f) (%.3f, %.3f, %.3f)\n", + first->x, first->y, first->z, + second->x, second->y, second->z);*/ + + vector::iterator curc,end; + + for(pos = 0; pos < 1; pos += step) { + last = cur; + cur += v1; + //shitty method, dont care, lazy + curc = colors.begin(); + end = colors.end(); + for(; curc != end; curc++) { + if((*curc).height > last.z) + break; + } + int ccolor; + if(curc == end) { +// printf("Unable to find color at height %.3f (range %.3f -> %.3f)\n", +// last.z, colors[0].height, colors[colors.size()-1].height); + ccolor = colors[colors.size()-1].color; + } else { +// printf("Found color at height %.3f (range %.3f -> %.3f)\n", +// last.z, colors[0].height, colors[colors.size()-1].height); + ccolor = (*curc).color; + } + gdImageLine(im, int(last.x), int(last.y), int(cur.x), int(cur.y), ccolor); + +/*printf(" Segment (%.3f, %.3f) (%.3f, %.3f) color %d\n", + last.x, last.y, + cur.x, cur.y, ccolor);*/ + } +} + +bool edges_cross(PathEdge *e1, PathEdge *e2, GPoint &out) { +//I love macros +#define IntersectDenom(p1, p2, p3, p4) \ +((p4->y - p3->y)*(p2->x - p1->x) - (p4->x - p3->x)*(p2->y - p1->y)) +#define IntersectNumerX(p1, p2, p3, p4) \ +((p4->x - p3->x)*(p1->y - p3->y) - (p4->y - p3->y)*(p1->x - p3->x)) +#define IntersectNumerY(p1, p2, p3, p4) \ +((p2->x - p1->x)*(p1->y - p3->y) - (p2->y - p1->y)*(p1->x - p3->x)) + +#define IntersectX(p1, p2, p3, p4, denom) \ +(p1->x + IntersectNumerX(p1, p2, p3, p4)*(p2->x - p1->x)/denom) +#define IntersectY(p1, p2, p3, p4, denom) \ +(p1->y + IntersectNumerX(p1, p2, p3, p4)*(p2->y - p1->y)/denom) + +#define CheckEqualXY(p1, p2) \ + (p1->x == p2->x && p1->y == p2->y) + +#define CoordOnLine(p1, p2, coord, dim) \ + (p1->dim > p2->dim? (coord > p2->dim && coord < p1->dim) : (coord > p1->dim && coord < p2->dim)) + +#define IntersectZfromX(p1, p2, inter) \ + (p2->x > p1->x? \ + (p1->z + ((inter - p1->x)/(p2->x - p1->x) * (p2->z - p1->z))) \ + :(p2->z + ((inter - p2->x)/(p1->x - p2->x) * (p1->z - p2->z)))) + + if(e1 == e2) + return(false); + + float denom = IntersectDenom(e1->from, e1->to, e2->from, e2->to); + if(denom != 0) { + + //see if this is at the end points... that dosent count. + //caught below by strict inequality +/* if( CheckEqualXY(e1->from, e2->from) + || CheckEqualXY(e1->from, e2->to) + || CheckEqualXY(e1->to, e2->from) + || CheckEqualXY(e1->to, e2->to) ) { + return(false); + }*/ + + + //the lines intersect, check segments now + float xinter = IntersectX(e1->from, e1->to, e2->from, e2->to, denom); + float yinter = IntersectY(e1->from, e1->to, e2->from, e2->to, denom); + + //need to add a Z check in here + float zinter1 = IntersectZfromX(e1->from, e1->to, xinter); + float zinter2 = IntersectZfromX(e2->from, e2->to, xinter); + + float dist = zinter1 - zinter2; + zinter1 += dist * 0.5; //middle ground on intersect point. + + if(dist < 0) { //z2 is above z1 + dist = 0 - dist; + } + + if(dist > CROSS_MAX_Z_DIFF) + return(false); + + //now see if this point is on both segments + if( CoordOnLine(e1->from, e1->to, xinter, x) + && CoordOnLine(e1->from, e1->to, yinter, y) + && CoordOnLine(e2->from, e2->to, xinter, x) + && CoordOnLine(e2->from, e2->to, yinter, y) ){ +// printf("Line (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f) d=%.3f\n", e1->from->x, e1->from->y, e1->from->z, e1->to->x, e1->to->y, e1->to->z, e1->from->Dist2(e1->to)); +// printf("Hits (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f) d=%.3f\n", e2->from->x, e2->from->y, e2->from->z, e2->to->x, e2->to->y, e2->to->z, e2->from->Dist2(e2->to)); +// printf("At (%.3f, %.3f), which IS on both segments.\n", xinter, yinter); + out.x = xinter; + out.y = yinter; + out.z = zinter1; + return(true); + } +// printf("At (%.3f, %.3f), which is not on both segments.\n", xinter, yinter); + } + return(false); +} + +void count_crossing_lines(list &edges, PathGraph *out, PathGraph *excess, map > &cross_list) { + list::iterator cur,end,cur2; + PathEdge *e, *look; + + out->edges.resize(0); + excess->edges.resize(0); + + float cml2 = CROSS_MIN_LENGTH*CROSS_MIN_LENGTH; + + cur = edges.begin(); + end = edges.end(); + vector hits; + GPoint hit; + for(; cur != end; cur++) { + e = *cur; + + //assume that small edges dont matter... + if(e->from->Dist2(e->to) < cml2) + continue; + + hits.clear(); + int count = 0; + + cur2 = edges.begin(); + for(; cur2 != end; cur2++) { + look = *cur2; + + if(edges_cross(e, look, hit)) { + count++; + hits.push_back(hit); + } + } + + + if(count >= CROSS_REDUCE_COUNT) { +//printf("Edge crosses %d edges.\n", count); + out->edges.push_back(e); + cross_edge_count++; + cross_list[e] = hits; + } else { + excess->edges.push_back(e); + } + } + + rebuild_node_list(out->edges, out->nodes, &excess->nodes); +} + +void rebuild_node_list(list &edges, list &nodes, list *excess_nodes) { + list in_nodes; + if(excess_nodes != NULL) { + in_nodes = nodes; + } + + nodes.resize(0); + std::map havenodelist; + list::iterator cur4,end4; + cur4 = edges.begin(); + end4 = edges.end(); + for(; cur4 != end4; cur4++) { + PathEdge *e = *cur4; + if(havenodelist.count(e->from) != 1) { + nodes.push_back(e->from); + havenodelist[e->from] = 1; + } + if(havenodelist.count(e->to) != 1) { + nodes.push_back(e->to); + havenodelist[e->to] = 1; + } + } + + //if they wanted a list of nodes not used, give it to them. + if(excess_nodes != NULL) { + list::iterator cur, end; + cur = in_nodes.begin(); + end = in_nodes.end(); + for(; cur != end; cur++) { + if(havenodelist.count(*cur) != 1) + excess_nodes->push_back(*cur); + } + } +} + +void cut_crossed_grids(PathGraph *big, map > &cross_list) { + map >::iterator cur,end; + vector::iterator curp, endp; + + cur = cross_list.begin(); + end = cross_list.end(); + for(; cur != end; cur++) { + PathEdge *e = cur->first; + vector &it = cur->second; + + sort(it.begin(), it.end()); + + //if the first point is to the left of our from, reverse the ordering + if(*e->from > it[0]) { + reverse(it.begin(), it.end()); + } + + PathNode *last,*curn; + last = e->from; + + curp = it.begin(); + endp = it.end(); + for(; curp != endp; curp++) { + curn = new PathNode(*curp); + big->nodes.push_back(curn); + big->add_edge(last, curn); + last = curn; + cross_add_count++; + } + e->from = last; + } + +} + +//written fast cause I dont care +bool load_eq_map(const char *zone, PathGraph *eqmap) { + char buf[256]; + FILE *in; + + //try to locate the file + sprintf(buf, "eqmaps/%s.txt", zone); + in = fopen(buf, "r"); + if(in == NULL) { + sprintf(buf, "eqmaps/%s_1.txt", zone); + in = fopen(buf, "r"); + if(in == NULL) { +// printf("Unable to load '%s'\n", buf); + return(false); + } + } + + //assume that a map file is smaller than a meg + char *file = new char[1024 * 1024]; + + //read the whole thing in at once + int len; + len = fread(file, 1, 1024*1024, in); + if(len == -1) { + printf("Unable to read EQ map file.\n"); + return(false); + } + + fclose(in); + + + //start parsing it + int pos = 0; + char *start, *next; + + next = file; + + while(pos < len) { + start = next; + + //find the end of the line + for(; pos < len; pos++, next++) { + if(*next == '\n' || *next == '\r') + break; + } + if(pos >= len) + break; + *next = '\0'; //null therminate the 'start' string + next++; + pos++; + //watch for goodl old windows line endings + if(*next == '\n' || *next == '\r') { //could write past EOF, dont care, buffer is big + next++; //skip over it too. + pos++; + } + + //now start is our line, and next is in position, parse start. + GPoint p1, p2, color; + //apparently the map files allready use our inverted XY + //try the first common format + if(sscanf(start, "L %f, %f, %f, %f, %f, %f, %f, %f, %f", + &p1.y, &p1.x, &p1.z, &p2.y, &p2.x, &p2.z, &color.y, &color.x, &color.z) + == 9) { + //do nothing, using if structure + //try another format, not sure how sscanf handles white space + } else if(sscanf(start, "L %f, %f, %f, %f, %f, %f, %f, %f, %f", + &p1.y, &p1.x, &p1.z, &p2.y, &p2.x, &p2.z, &color.y, &color.x, &color.z) + == 9) { + //do nothing, using if structure + } else { + //cannot parse this line... + continue; + } + + //invert our XY, and +- + float tmp; + tmp = p1.x; p1.x = -p1.y; p1.y = -tmp; + tmp = p2.x; p2.x = -p2.y; p2.y = -tmp; + + //color is prolly really integers + + //now we have our points... + + //sloppily add both nodes and the edge, prolly uses a lot more nodes than needed + PathNode *n1, *n2; + n1 = new PathNode(p1); + n2 = new PathNode(p2); + eqmap->nodes.push_back(n1); + eqmap->nodes.push_back(n2); + eqmap->add_edge(n1, n2); + } + + delete[] file; + + return(true); +} + + +void write_eq_map(list &edges, const char *fname) { + FILE *mapout; + mapout = fopen(fname, "w"); + if(mapout == NULL) { + printf("Unable to open EQ Client map %s\n", fname); + return; + } + + list::iterator cur,end; + PathEdge *e; + + //draw our EQ map + cur = edges.begin(); + end = edges.end(); + for(; cur != end; cur++) { + e = *cur; + + GPoint p1(*e->from), p2(*e->to); +// float tmp; +// tmp = p1.x; p1.x = -p1.y; p1.y = -tmp; +// tmp = p2.x; p2.x = -p2.y; p2.y = -tmp; + p1.x = -p1.x; p1.y = -p1.y; + p2.x = -p2.x; p2.y = -p2.y; + + fprintf(mapout, "L %.3f, %.3f, %.3f, %.3f, %.3f, %.3f, 255, 165, 0\n", + p1.x, p1.y, p1.z, p2.x, p2.y, p2.z); + + } + + fclose(mapout); + + printf("Wrote EQ Client map: %s\n", fname); +} + +QTNode *build_quadtree(Map *map, PathGraph *big) { + + //TODO: make sure we have the right XY in here... + QTNode *_root = new QTNode(map, FEAR_MAXIMUM_DISTANCE*FEAR_MAXIMUM_DISTANCE, + map->GetMinX(), map->GetMaxX(), map->GetMinY(), map->GetMaxY()); + if(_root == NULL) { + printf("Unable to allocate new QTNode.\n"); + return(false); + } + + _root->nodes = big->nodes; + list::iterator curs,end; + curs = _root->nodes.begin(); + end = _root->nodes.end(); +//int findex = 0; + for(; curs != end; curs++) { +//printf("Started With node index %d (0x%x)\n", findex++, *curs); +} + + _root->divideYourself(0); + + return(_root); +} + +bool write_path_file(QTNode *_root, PathGraph *big, const char *file, vector< vector > &path_finding) { + if(_root == NULL) + return(false); + + //im too lazy to give reasons for errors + FILE *out = fopen(file, "w"); + if(out == NULL) { + printf("Unable to open output file '%s'.\n", file); + return(false); + } + + PathFile_Header head; + head.version = PATHFILE_VERSION; + head.node_count = big->nodes.size(); + head.link_count = big->edges.size() * 2; //each edge has a to and from node + head.qtnode_count = _root->countQTNodes(); + head.nodelist_count = _root->countPathNodes(); + + if(fwrite(&head, sizeof(head), 1, out) != 1) { + printf("Error writting path file header.\n"); + fclose(out); + return(false); + + } + + printf("Path File: %lu fear nodes, %lu fear links, %u QT nodes, %lu node lists\n", head.node_count, head.link_count, head.qtnode_count, head.nodelist_count); + + //build our blocks... + PathNode_Struct *nodeBlock = new PathNode_Struct[head.node_count]; + PathLink_Struct *linkBlock = new PathLink_Struct[head.link_count]; + //too big to store right now, since we dont _NEED_ it... +// PathNodeRef *reachability = new PathNodeRef[head.node_count*head.node_count/2]; + + PathNode *n; + PathEdge *e; + list::iterator cur4,end4; + list::iterator cur, end; + + PathNode_Struct *curn = nodeBlock; + PathLink_Struct *curl = linkBlock; + + cur = big->nodes.begin(); + end = big->nodes.end(); + + //number all the nodes for a final time. + int index = 0; + for(; cur != end; cur++, index++) { + (*cur)->node_id = index; + } + + //maps to get edge list offsets for our pathing stuff + std::map to_edges; + std::map from_edges; + + uint32 eoffset = 0; + //fill the node block and edge block, N*E complexity + cur = big->nodes.begin(); + end = big->nodes.end(); +int nn = 0; + for(; cur != end; cur++, curn++, nn++) { +//printf("Filling node %d/%d n=0x%x, lb=0x%x, curl=0x%x/0x%x\n", nn, index, *cur, linkBlock, curl, &linkBlock[eoffset]); + n = *cur; + if(n == NULL) { + printf("Got NULL node building quadtree, WTF."); + continue; + } + curn->x = n->x; + curn->y = n->y; + curn->z = n->z; + curn->link_offset = eoffset; + curn->distance = n->longest_path; +//printf("Node %d: (%.2f,%.2f,%.2f) LO %d, D %d\n", nn, curn->x, curn->y, curn->z, curn->link_offset, curn->distance); + + int ecount = 0; + cur4 = big->edges.begin(); + end4 = big->edges.end(); + for(; cur4 != end4; cur4++, curl++) { + e = *cur4; + if(e->from == n) { +//using this instead of pointer addition because it is giving me some +//strange ass results by doing curl++, like adding 1242 instead of 6... + curl = &linkBlock[eoffset]; + curl->dest_node = e->to->node_id; + curl->reach = e->normal_reach; +//printf("\tLinkFrom %d: dest %d, reach %d\n", eoffset, curl->dest_node, curl->reach); + from_edges[e] = ecount; + ecount++; + eoffset++; +// curl++; + } else if(e->to == n) { + curl = &linkBlock[eoffset]; + curl->dest_node = e->from->node_id; + curl->reach = e->reverse_reach; +//printf("\tLinkTo %d: dest %d, reach %d\n", eoffset, curl->dest_node, curl->reach); + to_edges[e] = ecount; + ecount++; + eoffset++; +// curl++; + } + } + if(ecount >= PATH_LINK_OFFSET_NONE) { + printf("ERROR: a node has more than %d links, number will be truncated!", PATH_LINK_OFFSET_NONE-1); + } + curn->link_count = ecount; +//printf("Used up to slot %d of %d in links\n", eoffset, head.link_count); + } + + //write vertexBlock + if(fwrite(nodeBlock, sizeof(PathNode_Struct), head.node_count, out) != head.node_count) { + printf("Error writting path file nodes.\n"); + fclose(out); + return(false); + + } + + //write faceBlock + if(fwrite(linkBlock, sizeof(PathLink_Struct), head.link_count, out) != head.link_count) { + printf("Error writting path file edges.\n"); + fclose(out); + return(false); + } + + delete[] nodeBlock; + delete[] linkBlock; + + //make our node blocks to write out... + PathTree_Struct *qtnodes = new PathTree_Struct[head.qtnode_count]; + PathPointRef *nodelist = new PathPointRef[head.nodelist_count]; + if(qtnodes == NULL || nodelist == NULL) { + printf("Error allocating temporary memory for output.\n"); + fclose(out); + return(false); + } + + //extract the quad tree structure into linear arrays + unsigned long hindex = 0; + unsigned long findex = 0; + _root->fillBlocks(qtnodes, nodelist, hindex, findex); + + if(fwrite(qtnodes, sizeof(PathTree_Struct), head.qtnode_count, out) != head.qtnode_count) { + printf("Error writting path file quadtree nodes.\n"); + fclose(out); + return(false); + } + if(fwrite(nodelist, sizeof(PathPointRef), head.nodelist_count, out) != head.nodelist_count) { + printf("Error writting path file nodelist.\n"); + fclose(out); + return(false); + } + + delete[] qtnodes; + delete[] nodelist; + + //assumes that path_finding is not empty + PathLinkOffsetRef *refs = new PathLinkOffsetRef[head.node_count]; + //finally make our path finding blocks. + std::map::iterator rese, ende; + vector< vector >::iterator curoe, endoe; + vector::iterator curie, endie; + curoe = path_finding.begin(); + endoe = path_finding.end(); + int o,i; + for(i = 0; curoe != endoe; curoe++, i++) { + curie = (*curoe).begin(); + endie = (*curoe).end(); + for(o = 0; curie != endie; curie++, o++) { + e = *curie; + if(e == NULL) { //not reachable + refs[o] = PATH_LINK_OFFSET_NONE; + continue; + } + if(e->from->node_id == i) { + rese = to_edges.find(e); + ende = to_edges.end(); + } else { //assume to == i + rese = from_edges.find(e); + ende = from_edges.end(); + } + if(rese == ende) { + //not found in map... should this happen? + refs[o] = PATH_LINK_OFFSET_NONE; + continue; + } + refs[o] = rese->second; + } + + if(fwrite(refs, sizeof(PathLinkOffsetRef), head.node_count, out) != head.node_count) { + printf("Error writting path file's pathing information for node %d/%d.\n", i, head.node_count); + fclose(out); + return(false); + } + } + delete[] refs; + + fclose(out); + + return(true); +} + + +/* +void edge_stats(list &edges, const char *s) { + list::iterator cur,end; + cur = edges.begin(); + end = edges.end(); + + printf("Long edges for %s:\n", s); + + float watchlen = 1000*1000; + + for(; cur != end; cur++) { + PathEdge *e = *cur; + float d2 = e->from->Dist2(e->to); + if(d2 > watchlen) { + printf("LL (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f) d2=%.3f\n", e->from->x, e->from->y, e->from->z, e->to->x, e->to->y, e->to->z, d2); + } + } +}*/ + +void find_node_edges(PathGraph *big, + std::map > &node_edges) { + list::iterator curE,endE; + PathEdge *e; + + curE = big->edges.begin(); + endE = big->edges.end(); + for(; curE != endE; curE++) { + e = *curE; + + if(node_edges.count(e->from) == 1) { + node_edges[e->from].push_back(e); + } else { + vector t(1); + t[0] = e; + node_edges[e->from] = t; + } + + if(node_edges.count(e->to) == 1) { + node_edges[e->to].push_back(e); + } else { + vector t(1); + t[0] = e; + node_edges[e->to] = t; + } + } +} + +void validate_edges(Map *map, PathGraph *big) { + list::iterator curE,endE; + PathEdge *e; + + curE = big->edges.begin(); + endE = big->edges.end(); + int r; + for(r = 0; curE != endE; curE++, r++) { + e = *curE; + + if(!CheckLOS(map, e->from, e->to)) { + printf("Edge %d does not have LOS. Between nodes %d and %d.\n", r, e->from->node_id, e->to->node_id); + e->valid = false; + } + } +} + + + + + + + + + + diff --git a/utils/deprecated/apathing/apathing.cpp b/utils/deprecated/apathing/apathing.cpp new file mode 100644 index 000000000..600b9b8dd --- /dev/null +++ b/utils/deprecated/apathing/apathing.cpp @@ -0,0 +1,591 @@ +/* + +Fear Pathing generation utility. +(c) 2005 Father Nitwit + + + +Settings table: + +CREATE TABLE fear_settings ( + zone VARCHAR(16) NOT NULL PRIMARY KEY, + + #general settings: + use_doors TINYINT NOT NULL DEFAULT 1, + min_fix_z FLOAT NOT NULL DEFAULT 20, + max_fear_distance FLOAT NOT NULL DEFAULT 250, + image_scale TINYINT NOT NULL DEFAULT 4, + + #path related + check_initial_los TINYINT NOT NULL DEFAULT 0, + split_invalid_paths TINYINT NOT NULL DEFAULT 0, + link_path_endpoints TINYINT NOT NULL DEFAULT 1, + end_distance FLOAT NOT NULL DEFAULT 25, + split_long_min FLOAT NOT NULL DEFAULT 300, + split_long_step FLOAT NOT NULL DEFAULT 200, + + #node combining settings: + same_dist FLOAT NOT NULL DEFAULT 2.5, + node_combine_dist FLOAT NOT NULL DEFAULT 30, + grid_combine_dist FLOAT NOT NULL DEFAULT 30, + close_all_los TINYINT NOT NULL DEFAULT 0, + + #line-crossing reduction settings: + cross_count INT NOT NULL DEFAULT 5, + cross_min_length FLOAT NOT NULL DEFAULT 1, + cross_max_z_diff FLOAT NOT NULL DEFAULT 20, + cross_combine_dist FLOAT NOT NULL DEFAULT 120, + + #linking: + second_link_dist FLOAT NOT NULL DEFAULT 100, + link_max_dist FLOAT NOT NULL DEFAULT 400, + link_count TINYINT NOT NULL DEFAULT 1 + +); + +*/ + + + + +#include "../common/types.h" +#include "../zone/map.h" +#include "../common/rdtsc.h" +#include "quadtree.h" +#include "apathing.h" +#include "boostcrap.h" +#include +#include +#include +#include +#include + + + + +//parameters: +bool INCLUDE_DOORS = true; +float FEAR_MAXIMUM_DISTANCE = 250; +float ENDPOINT_CONNECT_MAX_DISTANCE = 25; +float MIN_FIX_Z = 20.0f; +float CLOSE_ENOUGH = 2.5; +bool COMBINE_CHECK_ALL_LOS = false; +float CLOSE_ENOUGH_COMBINE = 30; //30; +float SPAWN_MIN_SECOND_DIST = 100; +bool SPLIT_INVALID_PATHS = false; +bool LINK_PATH_ENDPOINTS = true; +float MAX_LINK_SPAWN_DIST = 400; +float FINAL_LINK_POINTS_DIST = 30; +bool SPAWN_LINK_TWICE = true; +bool SPAWN_LINK_THRICE = true; +float MERGE_MIN_SECOND_DIST = 30; +float SPLIT_LINE_LENGTH = 300; +float SPLIT_LINE_INTERVAL = 200; +float LONG_PATH_CHECK_LOS = 0; //0=disable +int CROSS_REDUCE_COUNT = 5; +float CROSS_MIN_LENGTH = 1; +float CROSS_MAX_Z_DIFF = 20; +float CLOSE_ENOUGH_CROSS = 120; +int IMAGE_SCALE = 4; + + + + +int main(int argc, char *argv[]) { + + srand(2038833498); + +/* const char *zone = + //"qeynos"; + "qeynos2"; + //"northkarana"; + */ + if(argc != 2) { + printf("Usage: %s [zone_short_name]\n", argv[0]); + return(1); + } + const char *zone = argv[1]; + char buf[256]; + + MYSQL m; + + list db_paths; + list db_spawns; + + mysql_init(&m); + + if(!mysql_real_connect(&m, DB_HOST, DB_LOGIN, DB_PASSWORD, DB_NAME, 0, NULL, 0)) { + printf("Unable to connect: %s.\n", mysql_error(&m)); + return(1); + } + + /* + Data loading phase + */ + + //load up our map file + Map *map = Map::LoadMapfile(zone); + if(map == NULL) { + printf("Unable to load map file."); + return(1); + } + + //try to load the EQ map file to make our pictures prettier + PathGraph eqmap; + if(load_eq_map(zone, &eqmap)) { + printf("Loaded EQ Client map: %d edges.\n", eqmap.edges.size()); + } else { + printf("Unable to load EQ Client map, continuing without it.\n"); + } + + //load our crap from the DB... + if(!load_paths_from_db(&m, map, zone, db_paths, db_spawns)) + return(1); + + if(db_paths.size() == 0) + db_paths.push_back(new PathGraph()); + + if(!load_spawns_from_db(&m, zone, db_spawns)) + return(1); + + if(INCLUDE_DOORS) { + if(!load_doors_from_db(&m, zone, db_spawns)) + return(1); + } + + if(!load_hints_from_db(&m, zone, db_spawns)) + return(1); + + + /*{ + PathGraph *g; + PathNode *cur = NULL, *last = NULL; + g = new PathGraph(); + g->nodes.push_back(cur = new PathNode(200, 200, 5)); + last = cur; + g->nodes.push_back(cur = new PathNode(GPoint(700, 200, 5))); + g->add_edge(last, cur); last = cur; + g->nodes.push_back(cur = new PathNode(GPoint(700, -300, 5))); + g->add_edge(last, cur); last = cur; + g->nodes.push_back(cur = new PathNode(GPoint(200, -300, 5))); + g->add_edge(last, cur); last = cur; + g->nodes.push_back(cur = new PathNode(GPoint(200, 200, 5))); + g->add_edge(last, cur); last = cur; + db_paths.push_back(g); + }*/ + /*{ + PathGraph *g; + PathNode *cur = NULL, *last = NULL; + g = new PathGraph(); + g->nodes.push_back(cur = new PathNode(200, 200, 5)); + last = cur; + g->nodes.push_back(cur = new PathNode(GPoint(385, 35, 23))); + g->add_edge(last, cur); last = cur; + g->nodes.push_back(cur = new PathNode(GPoint(450, -50, 23))); + g->add_edge(last, cur); last = cur; + g->nodes.push_back(cur = new PathNode(GPoint(535, -115, 23))); + g->add_edge(last, cur); last = cur; + g->nodes.push_back(cur = new PathNode(GPoint(700, -300, 5))); + g->add_edge(last, cur); last = cur; + db_paths.push_back(g); + + g = new PathGraph(); + g->nodes.push_back(cur = new PathNode(700, 200, 5)); + last = cur; + g->nodes.push_back(cur = new PathNode(GPoint(535, 35, 23))); + g->add_edge(last, cur); last = cur; + g->nodes.push_back(cur = new PathNode(GPoint(450, -50, 23))); + g->add_edge(last, cur); last = cur; + g->nodes.push_back(cur = new PathNode(GPoint(385, -115, 23))); + g->add_edge(last, cur); last = cur; + g->nodes.push_back(cur = new PathNode(GPoint(200, -300, 5))); + g->add_edge(last, cur); last = cur; + db_paths.push_back(g); + }*/ + + + //try to load settings, dont care if it fails + if(load_settings_from_db(&m, zone)) + printf("Loaded zone settings from the database.\n"); + else + printf("Unable to load settings from database. Using defaults.\n"); + + + printf("Load: got %d paths and %d spawn points from the database.\n", db_paths.size(), db_spawns.size()); + printf("Load: had to split up %d invalid paths.\n", load_split_paths); + + /* + The make-the-db-suck-less phase + */ + + //try to lower waypoints way in the sky: + repair_high_waypoints(map, db_paths, db_spawns); + printf("Fix Z: %d missed map, %d were not broken, %d were fixed.\n", z_no_map_count, z_not_fixed_count, z_fixed_count); + printf("Fix Z: broken avg diff=%.3f, not broken avg diff=%.3f\n", z_fixed_diffs/z_fixed_count, z_not_fixed_diffs/z_not_fixed_count); + + //run a waypoint reduction algorithm in 3space: + reduce_waypoints(db_paths); + printf("WP Reduce: removed %d redundant waypoints.\n", wp_reduce_count); + + /* + Graph connection and merging phase + */ + //make trivial connections of nodes at about the same spot on diff grids + combine_trivial_grids(map, db_paths); + printf("Trivial Merge: %d grids merged.\n", trivial_merge_count); + + //now do the 'closest with LOS' connection method + combine_closest_grids(map, db_paths); + printf("Closest Merge: %d grids linked, %d grids double-linked.\n", closest_merge_count, closest_merge2_count); + PathGraph *big = db_paths.front(); + + //now add in the spawn points, and link to closest with LOS + link_spawns(map, big, db_spawns, MAX_LINK_SPAWN_DIST, NULL); + printf("Link Spawns: %d linked once, %d linked twice, %d not linked, %d invalid.\n", link_spawn_count, link_spawn2_count, link_spawn_nocount, link_spawn_invalid); + + //combining close points might be causing small LOS obstacles... + //so we want to run this before we combine them. + //this seems to do more harm than good right now +// check_edge_los(map, big); +// printf("Bad Edges: removed %d no-LOS edges.\n", removed_edges_los); + +#ifdef LONG_PATH_CHECK_LOS + //check long paths LOS, we seem to have a problem with random long links + //disable this if we check all edges above... + check_long_edge_los(map, big); + printf("Bad Edges: removed %d long no-LOS edges.\n", removed_long_edges_los); +#endif + + //clean up points close enough to eachother to be the same point. + combine_grid_points(map, big, CLOSE_ENOUGH_COMBINE); + printf("Point Combine: combined %d very close nodes (%d missed strict LOS).\n", combined_grid_points, combine_broke_los); + printf("Point Combine: so far, %d LOS cache hits, %d LOS cache misses.\n", los_cache_hits, los_cache_misses); + + list all_edges = big->edges; + printf("Big Graph: %d original nodes, %d original edges.\n", big->nodes.size(), big->edges.size()); + +#ifdef DRAW_PRETREE_GRAPH + //draw out our graph before trimming + sprintf(buf, "paths-%s-pretree.png", zone); + draw_paths(map, big->edges, eqmap.edges, buf); +#endif + + + /* + Graph algorithm application (boost) + run it on each disjoint graph + */ + + vector disjoints; + vector start_nodes; + + //find all the disjoint graphs + { + //build the boost graph + MyGraph boost_graph(big->nodes.size()); + property_map::type weightlist; + std::map edgemap; + build_boost_graph(boost_graph, weightlist, edgemap, big, false); + + //find the grid which has most of the edges + sprintf(buf, "paths-%s-colors.png", zone); + find_disjoint_grids(map, boost_graph, big, buf, start_nodes, disjoints); + } + printf("\nSplit: There are %d valid disjoint graphs.\n", disjoints.size()); + + //for each disjoint graph.... + int djnum = 0; + PathGraph *tmpg; int start_node; + vector::iterator cur,end; + vector::iterator curs,ends; + cur = disjoints.begin(); curs = start_nodes.begin(); + end = disjoints.end(); ends = start_nodes.end(); + for(; cur != end; cur++,curs++) { + big = *cur; + start_node = *curs; + + printf("Disjoint %d: has %d edges and %d nodes.\n", djnum, big->edges.size(), big->nodes.size()); + + //reset our stats... + combine_broke_los = 0; + combined_grid_points = 0; + removed_edges_los = 0; + removed_long_edges_los = 0; + broke_paths = 0; + cross_edge_count = 0; + cross_add_count = 0; + + { + //build the boost graph + MyGraph boost_graph(big->nodes.size()); + property_map::type weightlist; + std::map edgemap; + build_boost_graph(boost_graph, weightlist, edgemap, big); + + //calculate the MST + run_min_spanning_tree(boost_graph, weightlist, edgemap, big, start_node); + printf("Ran Min Spanning Tree: ended with %d edges\n", big->edges.size()); + } + + /* + Now we have our minimal spanning tree, try to refine it. + + the goal of this crap is to fix newbie fields and open zones + */ + std::map > cross_list; + PathGraph *cross_big = new PathGraph(); + PathGraph *cross_excess = new PathGraph(); + + //count the number of times each edge crosses another edge, and record + //the intersection points. Also seperate crossers from non-crossers + count_crossing_lines(big->edges, cross_big, cross_excess, cross_list); + printf("Cross Count: %d edges cross more than the specified number of other edges.\n", cross_edge_count); + + if(cross_edge_count > 2) { + //Make waypoints at all points of intersection + cut_crossed_grids(cross_big, cross_list); + printf("Cross Cut: Created %d new nodes cutting intersections\n", cross_add_count); + + //combine close points with a somewhat big radius... + combine_grid_points(map, cross_big, CLOSE_ENOUGH_CROSS); + printf("Cross Combine: combined %d nodes. (%d missed strict LOS)\n", combined_grid_points, combine_broke_los); + printf("Cross Combine: so far, %d LOS cache hits, %d LOS cache misses.\n", los_cache_hits, los_cache_misses); + + //build our boost graph, so we can do reachability + MyGraph cross_graph(cross_big->nodes.size()); + property_map::type cross_weightlist; + std::map cross_edgemap; + build_boost_graph(cross_graph, cross_weightlist, cross_edgemap, cross_big, false); + + //isolate each disjoint graph and try to reduce it, gathering + //all non-cross points and edges while we are at it. + sprintf(buf, "paths-%s-crosses.png", zone); + consolidate_cross_graphs(map, cross_big, cross_excess, cross_graph, buf); + + //rebuild the big graph by merging cross_big and cross_excess + //might be as simple as append the two arrays and run a combine on it. + //leaks 'big' + *cur = big = cross_excess; + cross_excess->add_edges(cross_big->edges); + rebuild_node_list(big->edges, big->nodes); + + //This is used to re-link the cross grids with the non-cross stuff + combine_grid_points(map, big, CLOSE_ENOUGH_COMBINE); + printf("Cross Merge: combined %d close nodes. (%d missed strict LOS)\n", combined_grid_points, combine_broke_los); + printf("Cross Merge: so far, %d LOS cache hits, %d LOS cache misses.\n", los_cache_hits, los_cache_misses); + + //build yet another boost graph so we can run MST + MyGraph cross_graph_final(big->nodes.size()); + property_map::type cross_weightlist_final; + std::map cross_edgemap_final; + build_boost_graph(cross_graph_final, cross_weightlist_final, cross_edgemap_final, big); + + //run our MST to reduce the new cross-reduced graph + run_min_spanning_tree(cross_graph_final, cross_weightlist_final, cross_edgemap_final, big, 0); + printf("Ran Min Spanning Tree 2: ended with %d edges\n", big->edges.size()); + } //end if there were some cross edges + + /* + Final refinement phase, the graph has been reduced to our final + form, this phase is for adding anything back in we might want + */ + + //now that we reduced all our co-linear points, add a bunch back in for + //long paths, so we have more points over space. Idea is that this way + //we control how many colinear points there are, it isnt random + //this counteracts any attempts to use line-crossing as a criteria for reduction + //for some stupid reason, this just destroys the graph + // break_long_lines(db_paths); + // printf("WP Increase: created %d waypoints on long paths.\n", broke_paths); + + + /* + Things we might want to do: + - Try to create some cycles. Specifically large cycles. + - do this after reachability calculations + - use allready calculated reacahbility and distances, just adjust them as cycles added + - these will add more realism to the pathing + - might be implemented like this: + - run all pairs shortest path on the MST (is this a byproduct?) + - for each node N, for each other node K + - if path(N, K) is at least MIN_CYCLE_JOIN_PATH (to prevent making small cycles) + - and dist(N,K) is less than MAX_CYCLE_JOIN_DIST (do not want to invent long paths) + - and there is LOS from N to K + - connect N and K + - have to re-run all pairs again... that sucks + - another twist on the implementation would be to only look at paths + which were discarded by the MST. Or maybe run this first, then the other. + */ + + /* + The final tree has been built, remove anything we dont need from + it, and gather some information. + */ + + //build a boost graph out of our minimal spanning tree. + MyGraph boost_mst(big->nodes.size()); + property_map::type weightlist_mst; + std::map edgemap_mst; + build_boost_graph(boost_mst, weightlist_mst, edgemap_mst, big, true); + + //determine the path lengths to all nodes from all others + //including the longest path reachable by each node. + //this also cleans up big by removing anything unreachable + // vector< vector > AllPairs; + sprintf(buf, "paths-%s-mstcolors%d.png", zone, djnum++); + // calc_path_lengths(map, boost_mst, big, AllPairs, buf); + calc_path_lengths(map, boost_mst, big, edgemap_mst, buf); + printf("Calculated the longest paths from each node.\n"); + printf("\n"); + } + + printf("Combining all disjoint graphs...\n"); + //now combine all our disjoint graphs into one big one... + big = new PathGraph(); + cur = disjoints.begin(); + end = disjoints.end(); + big->nodes.clear(); + big->edges.clear(); + djnum = 1; + for(; cur != end; cur++, djnum++) { + tmpg = *cur; + list::iterator cure,ende; + cure = tmpg->edges.begin(); + ende = tmpg->edges.end(); + for(; cure != ende; cure++) { + big->edges.push_back(*cure); + } +// sprintf(buf, "paths-%s-dj%d.png", zone, djnum-1); +// just_color_the_damned_thing(map, tmpg, buf); + } + rebuild_node_list(big->edges, big->nodes, NULL); + + printf("Validating results...\n"); + validate_edges(map, big); + + //now we have our final node and edge set. + //build a graph of all final nodes to find pathing info +// MyGraph final(big->nodes.size()); +// property_map::type weightlist_final; +// std::map edgemap_final; +// build_boost_graph(final, weightlist_final, edgemap_final, big, true); + +// sprintf(buf, "paths-%s-mstcolors%d-ppi.png", zone, djnum-1); +// just_color_the_damned_thing(map, big, buf); + + vector< vector > path_finding; + find_path_info(map, big, path_finding); + printf("Calculated pathing information...\n"); + + //write out a nice image of our MST + sprintf(buf, "paths-%s-mstree.png", zone); + draw_paths2(map, all_edges, big->edges, eqmap.edges, db_spawns, buf); + + //write out a map for inside the EQ client, not as pretty as the PNG + sprintf(buf, "eqmaps_out/%s_2.txt", zone); + write_eq_map(big->edges, buf); + + /* + Build structures, and write out the path file. + */ + RDTSC_Timer t1, t2; + QTNode *root; + root = build_quadtree(map, big); + if(root == NULL) { + printf("Failed to build quadtree, quitting.\n"); + return(1); + } + + t2.start(); + sprintf(buf, "%s.path", zone); + if(!write_path_file(root, big, buf, path_finding)) { + printf("Unable to write path file.\n"); + return(1); + } + + printf("Everything completed successfully. %s.path has been generated.\n", zone); + +/* + +** now we have a minimal connected graph such that any mob can get anywhere + by only a single path. + + + +** now we have our final pathing grid. determine some useful info for each node. + +look for dead ends/node preference: + - at each node, sum up the length of all edges reachable by using that link + - for each node as N + -- for each edge leaving N as E + ---- reset marks on all edges + ---- mark all edges leaving N + ---- perform a depth first search of all reachable nodes + ------ Only traverse unmarked edges. Mark each edge as it is traversed. + ---- unmark all edges leaving N, except E + ---- sum the length of all marked edges + ---- store this reachable length as a weight for this edge of this node. + -- for each edge leaving N as E + ---- determine highest edge weight in this node + -- for each edge leaving N as E + ---- count number of edges within RANDOM_WALK % of the highest node. + -- sort the edge list for this node to have all random nodes first + -- store the random walkable counter for this node + + + +** Finally we have all the calculation + +things we need to store: +nodes +edge lists +quadtree containing nodes + + - We do not really need to store edges which are not on the random walk + list since the pathing code will never consider using them. + +Node { + x, y, z + edge list pointer + edge list length + random walkable counter +} + +edge list entry { + ending node pointer + reachable edge weight? (not used by pathing, might have other purpose) +} + +quadtree node { + minx, maxx, miny, maxy + union { + node pointers: q1, q2, q3, q4 + node list offset and length + } +} + + + + +how to do fear pathing... +when a mob is feared, it uses the quadtree to find the node closest to it +that it can see to. The LOS requirement will make this more difficult, but feasible. + +Once it has found its first node, the mob will set this as its waypoint and go there. + +Once the mob reaches a node, it will look at the node's random counter (RC) + if RC is 1, take the first edge and use its terminal as next waypoint + if RC is >1, randomly choose an edge from 0 to RC-1, and walk that edge. +*/ + + + + + mysql_close(&m); +} + + + + + + diff --git a/utils/deprecated/apathing/apathing.h b/utils/deprecated/apathing/apathing.h new file mode 100644 index 000000000..fdd14312f --- /dev/null +++ b/utils/deprecated/apathing/apathing.h @@ -0,0 +1,290 @@ +#ifndef APATHING_H +#define APATHING_H + +#include +#include + +#define DB_HOST "10.1.1.1" +#define DB_LOGIN "eqserver" +#define DB_PASSWORD "pw4eqserver" +#define DB_NAME "peq" + +//for now, all of these were arbitrarily chosen + +//load up doors as spawn points, since they are also valid locations +extern bool INCLUDE_DOORS; + +//this is the furthest a mob can be from a fear point and still expect +//to find the node. +extern float FEAR_MAXIMUM_DISTANCE; + +extern float ENDPOINT_CONNECT_MAX_DISTANCE; //begin and end point must be this close +extern float MIN_FIX_Z; //minimum drop before correcting waypoint + +#define ALMOST_COLINEAR_COS 0.99f //cosine of an angle to consider colinear... .99== 8 degrees + +//if we miss the map on one Z-checking try, move the point by these +#define X_JITTER 3 +#define Y_JITTER 3 + +//if two nodes are this close together, consider them the same +extern float CLOSE_ENOUGH; + +//this causes us to check all of a node's edges for LOS from the new +//node when we are considering combining into that node +//this is not working as well as one might hope, leads to many invalids +extern bool COMBINE_CHECK_ALL_LOS; + +//this is bigger than close enough, since we check LOS when combining these +//so that we prevent little juntions +extern float CLOSE_ENOUGH_COMBINE; + +//a second link on a spawn must be this far away from the first. +extern float SPAWN_MIN_SECOND_DIST; + +//uncomment to split a pathin with two waypoints which cannot see eachother into two +extern bool SPLIT_INVALID_PATHS; + +//enabled linking of path endpoints as if they were spawn points. +extern bool LINK_PATH_ENDPOINTS; + +//the maximum distance of the closest point to a spawn inorder to link it +extern float MAX_LINK_SPAWN_DIST; + +//before we generate pathing info, we try to link all points within this range to eachother +extern float FINAL_LINK_POINTS_DIST; + +//enables linking a spawn point to two nodes instead of just one. +extern bool SPAWN_LINK_TWICE; +extern bool SPAWN_LINK_THRICE; //link up to 3 times.. requires twice enabled + +//a second link point must be further than this from the first for closest merge +extern float MERGE_MIN_SECOND_DIST; + +//if an edge is longer than this, it will get cut into pieces interval long +extern float SPLIT_LINE_LENGTH; +extern float SPLIT_LINE_INTERVAL; + +//an edge must cross this many lines before being considered for cross reduction +extern int CROSS_REDUCE_COUNT; +//an edge must be longer than this before we think it can cross anything +extern float CROSS_MIN_LENGTH; +//The intersect points of two edges must be within this range of Z to count +extern float CROSS_MAX_Z_DIFF; +//the max distance between two nodes to consider them the same when crossing +extern float CLOSE_ENOUGH_CROSS; + +//check long paths on load for LOS +//#define LONG_PATH_CHECK_LOS + +//minimum number of nodes for a graph to possible be a disjoint graph +#define MIN_DISJOINT_NODES 3 //5 + +//divide the image scale by this number in each direction +extern int IMAGE_SCALE; + +//enable drawing of a color-by-reachability graph when coloring a graph +#define DRAW_ALL_COLORS 1 + +//enable drawing of the original non-reduced combined graph. +#define DRAW_PRETREE_GRAPH 1 + +#include "gpoint.h" +#include +#include +using namespace std; + +class PathNode : public GPoint { +public: + PathNode() { + init(); + } + PathNode(const GPoint &them) : GPoint(them) { + init(); + } + PathNode(float ix, float iy, float iz) : GPoint(ix, iy, iz) { + init(); + } + void init() { + color = 0; +// color2 = 0; + final_id = -1; + longest_path = 0; + valid = true; + forced = false; + disjoint = false; + } + + int node_id; + int final_id; +// inherited: +// float x; +// float y; +// float z; +// VertDesc vert; + + int color; + int longest_path; + + char valid:1, + forced:1, + disjoint:1, + extra:5; + + float Dist2(const GPoint *o) const { + float tmp; + float sum; + tmp = x - o->x; + sum = tmp*tmp; + tmp = y - o->y; + sum += tmp*tmp; + tmp = z - o->z; + sum += tmp*tmp; + return(sum); + } +}; + +class PathEdge { +public: + PathEdge( PathNode *_from, PathNode *_to) { + from = _from; + to = _to; + normal_reach = -1; + reverse_reach = -1; + valid = true; + } + PathNode *from; + PathNode *to; + + int normal_reach; + int reverse_reach; + bool valid; +// EdgeDesc edge_id; +}; + +class PathGraph { +public: + ~PathGraph() { + { + list::iterator cur,end; + cur = nodes.begin(); + end = nodes.end(); + for(; cur != end; cur++) { + delete *cur; + } + } + { + list::iterator cur,end; + cur = edges.begin(); + end = edges.end(); + for(; cur != end; cur++) { + delete *cur; + } + } + } + + void add_edge(PathNode *b, PathNode *e) { + edges.push_back(new PathEdge(b, e)); + } + + void add_edges(list &o) { + list::iterator cur,end; + cur = o.begin(); + end = o.end(); + for(; cur != end; cur++) { + edges.push_back(*cur); + } + } + + list nodes; + list edges; + + //used for graph color accounts + int curcolor; + int ccount; +}; + + +class ColorRecord { +public: + int color; + float height; +}; + + + + +extern int load_split_paths; +extern int z_fixed_count; +extern int z_not_fixed_count; +extern int z_no_map_count; +extern float z_fixed_diffs; +extern float z_not_fixed_diffs; +extern int wp_reduce_count; +extern int trivial_merge_count; +extern int closest_merge_count; +extern int closest_merge2_count; +extern int link_spawn_count; +extern int link_spawn_invalid; +extern int link_spawn2_count; +extern int link_spawn3_count; +extern int link_spawn_nocount; +extern int combine_broke_los; +extern int combined_grid_points; +extern int removed_edges_los; +extern int removed_long_edges_los; +extern int broke_paths; +extern int cross_edge_count; +extern int cross_add_count; +extern int los_cache_misses; +extern int los_cache_hits; + + +#include +#include +#include +#include +using namespace std; + +class QTNode; + +//ye-olde prototypes +bool load_paths_from_db(MYSQL *m, Map *map, const char *zone, list &db_paths, list &end_points); +bool load_spawns_from_db(MYSQL *m, const char *zone, list &db_spawns); +bool load_doors_from_db(MYSQL *m, const char *zone, list &db_spawns); +bool load_hints_from_db(MYSQL *m, const char *zone, list &db_spawns); +bool load_settings_from_db(MYSQL *m, const char *zone); +void repair_a_high_waypoint(Map *map, PathNode *it); +void repair_high_waypoints(Map *map, list &db_paths, list &db_spawns); +bool almost_colinear(PathNode *first, PathNode *second, PathNode *third); +void reduce_waypoints(list &db_paths); +void break_long_lines(list &db_paths); +//void build_big_graph(PathGraph *big, list &db_paths, list &db_spawns); +void combine_trivial_grids(Map *map, list &db_paths); +void combine_closest_grids(Map *map, list &db_paths); +void link_spawns(Map *map, PathGraph *big, list &db_spawns, float maxdist, map< pair, bool > *edgelist); +void combine_grid_points(Map *map, PathGraph *big, float close_enough); +void draw_paths(Map *map, list &edges, list &edges2, const char *fname); +void draw_paths2(Map *map, list &edges1, list &edges2, list &edges3, list &spawns, const char *fname); +void check_edge_los(Map *map, PathGraph *big); +void check_long_edge_los(Map *map, PathGraph *big); +bool CheckLOS(Map *map, PathNode *from, PathNode *to); +void cut_crossed_grids(PathGraph *big, map > &cross_list); +void rebuild_node_list(list &edges, list &nodes, list *excess_nodes = NULL); +QTNode *build_quadtree(Map *map, PathGraph *big); +bool write_path_file(QTNode *_root, PathGraph *big, const char *file, vector< vector > &path_finding); +bool load_eq_map(const char *zone, PathGraph *eqmap); +void write_eq_map(list &edges, const char *fname); +//void edge_stats(list &edges, const char *s); +void choose_biggest_graph(PathGraph *big, vector &counts, vector &first_node); +void find_path_info(Map *map, PathGraph *big, vector< vector > &path_finding); +void find_node_edges(PathGraph *big, std::map > &node_edges); +void validate_edges(Map *map, PathGraph *big); + +void DrawGradientLine(gdImagePtr im, GPoint *first, GPoint *second, vector &colors); +void allocateGradient(gdImagePtr im, float r1, float g1, float b1, float r2, float g2, float b2, + float min, float max, float divs, vector &colors); + + +#endif + diff --git a/utils/deprecated/apathing/boostcrap.cpp b/utils/deprecated/apathing/boostcrap.cpp new file mode 100644 index 000000000..230c1bfb3 --- /dev/null +++ b/utils/deprecated/apathing/boostcrap.cpp @@ -0,0 +1,759 @@ +/* + +Fear Pathing generation utility. +(c) 2005 Father Nitwit + +*/ + + + + +#include "../common/types.h" +#include "../zone/map.h" +#include "../common/rdtsc.h" +#include "quadtree.h" +#include "apathing.h" +#include "boostcrap.h" +#include +#include +#include +#include +#include + + + + + + + + +/* + We assume this overwrites the edge array in big + and does not free the old edges +*/ +void build_boost_graph(MyGraph &vg, property_map::type &weightmap, map &em, PathGraph *big, bool set_weights) { + + list::iterator cur,end; + list::iterator curn,endn; + PathNode *n; + PathEdge *e; + + //first we need to number our nodes... + curn = big->nodes.begin(); + endn = big->nodes.end(); + int r = 0; + for(; curn != endn; curn++) { + n = *curn; + n->node_id = r; + r++; + } + + typedef std::pair < int, int > E; //our edge type + + weightmap = get(edge_weight, vg); + + //now add all our edges to the graph + cur = big->edges.begin(); + end = big->edges.end(); + for(r = 0; cur != end; cur++, r++) { + e = *cur; + if(e->from == e->to) + continue; + +// printf("A %d/%d (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f) d=%.3f\n", r, big->edges.size(), e->from->x, e->from->y, e->from->z, e->to->x, e->to->y, e->to->z, e->from->Dist2(e->to)); + EdgeDesc ed; + bool inserted; + tie(ed, inserted) = add_edge(e->from->node_id, e->to->node_id, vg); + if(set_weights) + weightmap[ed] = int(e->from->Dist2(e->to)); + else + weightmap[ed] = 1; + em[e] = ed; +// e->edge_id = ed; + } + //now we should have a nice happy undirected graph... +} + +void run_min_spanning_tree(MyGraph &vg, property_map::type &weightmap, map &em, PathGraph *big, int start_node) { + int noedge = 0; + list out_paths; + + vector < EdgeDesc > spanning_tree; + + kruskal_minimum_spanning_tree(vg, back_inserter(spanning_tree)); + + vector < EdgeDesc >::iterator cur,end; + list::iterator cure,ende; + cur = spanning_tree.begin(); + end = spanning_tree.end(); + for(; cur != end; cur++) { + //find the edge + cure = big->edges.begin(); + ende = big->edges.end(); + for(; cure != ende; cure++) { + if(em[*cure] == *cur) { + out_paths.push_back(new PathEdge((*cure)->from, (*cure)->to)); + break; + } + } + } + noedge = big->edges.size() - out_paths.size() - 1; + printf("Ran Min Spanning Tree: %d paths were eliminated.\n", noedge); + + + /* + //make our results list. + vector < VertDesc > p(num_vertices(vg)); + + //finally run the stupid algorithm. + prim_minimum_spanning_tree(vg, &p[0], root_vertex(start_node)); + + + for (std::size_t i = 0; i != p.size(); ++i) { + if (p[i] != i) { + out_paths.push_back(new PathEdge(big->nodes[p[i]], big->nodes[i])); + } else { + //no edge here... + noedge++; + } + } + printf("Ran Min Spanning Tree: %d nodes were disconnected.\n", noedge); + + + */ + + //now swap out our edge list to the MST. + big->edges = out_paths; +} + +void find_disjoint_grids(Map *map, MyGraph &vg, PathGraph *big, const char *fname, vector &start_nodes, vector &disjoints) { + + vector< vector > D; + vector counts; + vector disjoint_counts; + vector first_node; + + //color the graph and get us the info we need to do out job + color_disjoint_graphs(big, vg, map, fname, D, counts, disjoint_counts, first_node); + + + //find the biggest grid, that one gets to be included no matter what + int best_graph = 0; + int best_count = counts[0]; + unsigned int r; + for(r = 1; r < counts.size(); r++) { + if(best_count < counts[r]) { + best_count = counts[r]; + best_graph = r; + } + } + disjoint_counts[best_graph] = 1; + + + //break up the graphs based on color if we want them. + vector::iterator ccur,djcur,fcur,cend; + list::iterator cur,end; + PathEdge *e; + PathGraph *pg; + ccur = counts.begin(); + djcur = disjoint_counts.begin(); + fcur = first_node.begin(); + cend = counts.end(); + int color = 0; + for(; ccur != cend; ccur++, djcur++, fcur++, color++) { + int count = *ccur; + int dj = *djcur; +// int fn = *fcur; + + if(dj < 1) + continue; //skip disjoint sets not marked for use. + + if(count < MIN_DISJOINT_NODES) + continue; //make sure we have a reasonable node count + + pg = new PathGraph(); + + cur = big->edges.begin(); + end = big->edges.end(); + for(; cur != end; cur++) { + e = *cur; + if(e->from->color != e->to->color) { + printf("Color Mismatch %d-%d: #%d(%.3f,%.3f,%.3f) -> #%d(%.3f,%.3f,%.3f)\n", e->from->color, e->to->color, e->from->node_id, e->from->x, e->from->y, e->from->z, e->to->node_id, e->to->x, e->to->y, e->to->z); + //... what to do... + } + //just use the color of the from node + if(e->from->color == color) { + pg->edges.push_back(e); + } + } + + //get our list of nodes based on our edge list. + rebuild_node_list(pg->edges, pg->nodes, NULL); + + disjoints.push_back(pg); + start_nodes.push_back(1); //each graph only contains its own nodes, so any node will work. + } + + + + /* + int best_graph = 0; + int best_count = counts[0]; + unsigned int r; + for(r = 1; r < counts.size(); r++) { + if(best_count < counts[r]) { + best_count = counts[r]; + best_graph = r; + } +// printf("Graph %d has %d edges\n", r, counts[r]); + } + + start_node = first_node[best_graph]; + printf("Best sub-graph: chose #%d of %d, has %d nodes, and contains node %d\n", best_graph, counts.size()-1, best_count, start_node); + //todo: eliminate other graphs... + */ + +/* list::iterator cure,ende; + PathEdge *e; + cure = big->edges.begin(); + ende = big->edges.end(); + for(; cure != ende; cure++) { + e = *cure; + if(e->from->color != e->to->color) { + printf("Color Mismatch %d-%d: #%d(%.3f,%.3f,%.3f) -> #%d(%.3f,%.3f,%.3f)\n", e->from->color, e->to->color, e->from->node_id, e->from->x, e->from->y, e->from->z, e->to->node_id, e->to->x, e->to->y, e->to->z); + } else if(e->from->color != best_graph) { + printf("Color %d: (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f)\n", e->from->color, e->from->x, e->from->y, e->from->z, e->to->x, e->to->y, e->to->z); + } + } +*/ +} + + + +static const int INT_LIMIT = (std::numeric_limits < int >::max)(); + +void color_disjoint_graphs( + PathGraph *big, + MyGraph &vg, + Map *map, + const char *fname, + + vector< vector > &D, //output + vector &counts, //output + vector &disjoint_counts, //output + vector &first_node //output +) { + + + int count = big->nodes.size(); +// int r; + +// vector< vector > D(count, vector(count, INT_LIMIT)); +// vector counts(1, 0); +// vector first_node(1, 0); + + //make sure everything is inited right... + D.resize(0); + D.resize(count, vector(count, INT_LIMIT)); + counts.resize(1); + counts[0] = 0; + disjoint_counts.resize(1); + disjoint_counts[0] = 0; + first_node.resize(1); + first_node[0] = 0; + + //make up a weight map with all 1s, done while building now +/* property_map < MyGraph, edge_weight_t >::type w = get(edge_weight, vg); + graph_traits < MyGraph >::edge_iterator e, e_end; + for (boost::tie(e, e_end) = edges(vg); e != e_end; ++e) + w[*e] = 1;*/ + + + johnson_all_pairs_shortest_paths(vg, D); + + list::iterator cur,end,cur2; + PathNode *n,*f; + + int cur_color = 1; + int cc,djc; + + //clear node colors + cur = big->nodes.begin(); + end = big->nodes.end(); + for(; cur != end; cur++) { + n = *cur; + n->color = 0; + } + + + //color the graph based on reachability, basically labeling subgraphs + //this finds the number of nodes reachable from each node + //which is used to pick the best disconnected graph in the tree. + //its a series of wrong bullshit that forces us to do this, but it works + cur = big->nodes.begin(); + end = big->nodes.end(); + for(; cur != end; cur++) { + n = *cur; + if(n->color != 0) + continue; //allready visited + +//printf("New Color at: (%.3f,%.3f,%.3f)\n", n->x, n->y, n->z); + + cc = 1; + djc = 0; + + if(n->disjoint) + djc++; + + n->color = cur_color; + cur_color++; + + cur2 = cur; + for(; cur2 != end; cur2++) { + f = *cur2; + if(f->color == 0 && D[n->node_id][f->node_id] != INT_LIMIT) { + cc++; + f->color = n->color; + } + if(f->disjoint) + djc++; + } + counts.push_back(cc); + disjoint_counts.push_back(djc); + first_node.push_back(n->node_id); + } + +#ifdef DRAW_ALL_COLORS + + if(fname != NULL) { + printf("Drawing with %d seperate sub-graphs from %d nodes and %d edges\n", cur_color-1, big->nodes.size(), big->edges.size()); + + FILE *pngout; + pngout = fopen(fname, "wb"); + if(pngout == NULL) { + printf("Unable to open %s\n", fname); + return; + } + + gdImagePtr im; + int minx = int(map->GetMinX()); + int maxx = int(map->GetMaxX()); + int miny = int(map->GetMinY()); + int maxy = int(map->GetMaxY()); + + im = gdImageCreate((maxx - minx)/IMAGE_SCALE, (maxy - miny)/IMAGE_SCALE); + // im = gdImageCreate(maxx - minx, maxy - miny); + + //allocate this first, to make it the BG color. + /*int black =*/ gdImageColorAllocate(im, 0, 0, 0); + + // int grey = gdImageColorAllocate(im, 100, 100, 100); + + int *clist = new int[cur_color]; + int r; + for(r = 0; r < cur_color; r++) { + clist[r] = gdImageColorAllocate(im, rand()%255, rand()%255, rand()%255); + } + + { + list::iterator cur,end; + PathEdge *e; + + cur = big->edges.begin(); + end = big->edges.end(); + for(; cur != end; cur++) { + e = *cur; + int x1 = int(e->from->x) - minx; + int y1 = int(e->from->y) - miny; + int x2 = int(e->to->x) - minx; + int y2 = int(e->to->y) - miny; + x1 /= IMAGE_SCALE; + y1 /= IMAGE_SCALE; + x2 /= IMAGE_SCALE; + y2 /= IMAGE_SCALE; + gdImageLine(im, x1, y1, x2, y2, clist[e->from->color]); + } + } + delete[] clist; + + gdImagePng(im, pngout); + gdImageDestroy(im); + + fclose(pngout); + + printf("Wrote image: %s\n", fname); + } +#endif //DRAW_ALL_COLORS +} + +void calc_path_lengths(Map *map, MyGraph &vg, PathGraph *big, map &em, const char *fname) { + + vector counts; + vector disjoint_counts; + vector first_node; + vector< vector > D; + list::iterator cur,end; + PathNode *n; + + /* + Node distances: + 1. find the root node. + - find the longest path from each node to any other node + - the node with the shortest of these longest paths is the root + 2. record each node's distance from that root node + */ + + //color the graph and get us the info we need to do out job + color_disjoint_graphs(big, vg, map, fname, D, counts, disjoint_counts, first_node); + + + vector::iterator curp,endp; + + int shortest_node = 0; + int shortest = 0xFFFFFF; + int the_longest = 0; + int longest_node = 0; + + //stores the longest path from each node + vector longest_dists(D.size(), 0); + + //find the node with the longest path of all, so we know what + //tree we are trying to find the root of + cur = big->nodes.begin(); + end = big->nodes.end(); + for(; cur != end; cur++) { + n = *cur; + vector &cv = D[n->node_id]; + curp = cv.begin(); + endp = cv.end(); + int longest = 0; + int discount = 0; + for(; curp != endp; curp++) { + if(*curp == INT_LIMIT) { + discount++; + continue; + } + if(*curp > longest) + longest = *curp; + } + + longest_dists[n->node_id] = longest; + + if(longest > the_longest) { + the_longest = longest; + longest_node = n->node_id; + } + } + + //find the node with the shortest, longest path + //the idea is to locate the 'root' of the tree. + cur = big->nodes.begin(); + end = big->nodes.end(); + for(; cur != end; cur++) { + n = *cur; + vector &cv = D[n->node_id]; + if(cv[longest_node] == INT_LIMIT) + continue; //this node cannot reach the root + + int longest = longest_dists[n->node_id]; + //n->longest_path = longest; +//printf("Node %d's longest path is %d\n", n->node_id, longest); + + if(longest < shortest) { + shortest = longest; + shortest_node = n->node_id; + } + } + + //now we have our root, set each node to their distance from the root + vector &root_dists = D[shortest_node]; +printf("The tree's root is %d\n", shortest_node); + cur = big->nodes.begin(); + end = big->nodes.end(); + for(; cur != end; cur++) { + n = *cur; + if(n->node_id == shortest_node) + n->longest_path = 0; + else + n->longest_path = root_dists[n->node_id]; +//printf("Node %d's distance from root is %d\n", n->node_id, root_dists[n->node_id]); + } + + + + + + /* + the reachability is the number of nodes which we could possibly get to + by traveling each direction across an edge. + + to find reachability: + loop through each edge e + Remove that edge from the graph + color_disjoint_graphs + fc = count number of nodes with the same color as 'e->from' + tc = count number of nodes with the same color as 'e->to' + e->normal_reach = tc; + e->reverse_reach = fc; + */ + property_map::type weightmap; + weightmap = get(edge_weight, vg); + + int tc,fc; + int from_color, to_color; + + printf("Finding lengths (%d dots)", 1+big->edges.size()/10); + int pos = 0; + + PathEdge *e; + list::iterator cur4,end4; + cur4 = big->edges.begin(); + end4 = big->edges.end(); + for(; cur4 != end4; cur4++, pos++) { + if(pos % 10 == 0) { + printf("."); + fflush(stdout); + } + e = *cur4; + + //remove this edge from the boost graph.. + remove_edge(em[e], vg); + + //color the graph and get us the info we need to do our job + //char out[64]; + //sprintf(out, "lengraph-%d.png", pos); + color_disjoint_graphs(big, vg, map, + NULL, + D, counts, disjoint_counts, first_node); + + //add the edge back in + EdgeDesc ed; + bool inserted; + tie(ed, inserted) = add_edge(e->from->node_id, e->to->node_id, vg); + em[e] = ed; + weightmap[em[e]] = int(e->from->Dist2(e->to)); //cause its a new edge + + //make sure this stupid thing worked. + if(e->from->color == e->to->color) { + printf("Cycle detected in MST... WTF?\n"); + e->normal_reach = -1; + e->reverse_reach = -1; + continue; + } + from_color = e->from->color; + to_color = e->to->color; + + //count our crap. + tc = 0; + fc = 0; + cur = big->nodes.begin(); + end = big->nodes.end(); + for(; cur != end; cur++) { + n = *cur; + if(n->color == to_color) + tc++; + else if(n->color == from_color) + fc++; + } + + //put it on our edge + e->normal_reach = tc; + e->reverse_reach = fc; + } + + printf("\n"); + + //re-color the graph, since we dicked with it above + color_disjoint_graphs(big, vg, map, fname, D, counts, disjoint_counts, first_node); + choose_biggest_graph(big, counts, first_node); +} + + + +void just_color_the_damned_thing(Map *map, PathGraph *big, const char *fname) { + MyGraph vg(big->nodes.size()); + property_map::type weightlist_mst; + std::map edgemap_mst; + build_boost_graph(vg, weightlist_mst, edgemap_mst, big, true); + + vector counts; + vector disjoint_counts; + vector first_node; + vector< vector > D; + list::iterator cur,end; + + color_disjoint_graphs(big, vg, map, fname, D, counts, disjoint_counts, first_node); +} + +/* +void calc_path_lengths(Map *map, MyGraph &vg, PathGraph *big, vector< vector > &D, const char *fname) { + +// vector< vector > D; + vector counts; + vector first_node; + + + choose_biggest_graph(big, counts, first_node); +} +*/ + + +//assumes that the graph is freshly colored disjoint, and counts/first_node are results from it +void choose_biggest_graph(PathGraph *big, vector &counts, vector &first_node) { + int best_graph = 0; + int best_count = counts[0]; + unsigned int r; + for(r = 1; r < counts.size(); r++) { + if(best_count < counts[r]) { + best_count = counts[r]; + best_graph = r; + } + printf("Graph %d has %d edges\n", r, counts[r]); + } + + printf("Best Tree: Detected %d graphs in the MST, selected #%d\n", counts.size(), best_graph); + + list new_nodes; + list new_edges; + + //rebuild the node list to include only nodes which are in + //the best graph, also only keeping the edges which connect them. + std::map havenodelist; + list::iterator cur4,end4; + cur4 = big->edges.begin(); + end4 = big->edges.end(); + for(; cur4 != end4; cur4++) { + PathEdge *e = *cur4; + + //remove the edge if it is not the main color + //this also causes the removal of the nodes if they + //are not used in any other edges. + if(e->from->color != e->to->color) { +printf("Miscolor.\n"); + continue; + } + if(e->from->color != best_graph) { + continue; + } + if(e->to->color != best_graph) { + continue; + } + + new_edges.push_back(e); + + if(havenodelist.count(e->from) != 1) { + e->from->final_id = new_nodes.size(); + new_nodes.push_back(e->from); + havenodelist[e->from] = 1; + } + if(havenodelist.count(e->to) != 1) { + e->to->final_id = new_nodes.size(); + new_nodes.push_back(e->to); + havenodelist[e->to] = 1; + } + } + + printf("Best Tree: Removed %d nodes and %d edges which were disconnected.\n", + big->nodes.size() - new_nodes.size(), big->edges.size() - new_edges.size()); + big->nodes = new_nodes; + big->edges = new_edges; +} + +void consolidate_cross_graphs(Map *map, PathGraph *big, PathGraph *excess, MyGraph &cross_graph, const char *fname) { + + vector< vector > D; + vector counts; + vector disjoint_counts; + vector first_node; + + //color the graph and get us the info we need to do our job + color_disjoint_graphs(big, cross_graph, map, fname, D, counts, disjoint_counts, first_node); +} + +void find_path_info(Map *map, MyGraph &vg, vector< vector > &path_finding, PathGraph *big) { + //make sure our path finding vector is big enough. + { + int size = big->nodes.size(); + vector tmp(size, (PathEdge*)NULL); + path_finding.resize(0); + path_finding.resize(size, tmp); + } + + vector< vector > D; + vector counts; + vector disjoint_counts; + vector first_node; + + //color the graph and get us the info we need to do our job + color_disjoint_graphs(big, vg, map, NULL, D, counts, disjoint_counts, first_node); + + //figure out what edges link to each node. + std::map > node_edges; + find_node_edges(big, node_edges); + + + //for each node, find best edge to reach each other node. + list::iterator cur,end; + PathNode *n; + vector::iterator curp,endp; + vector::iterator cure,ende; + int r; + cur = big->nodes.begin(); + end = big->nodes.end(); + for(; cur != end; cur++) { + n = *cur; + //get all our vector refs that we need since this is kinda expensive + vector &cv = D[n->node_id]; //distance array + vector &pf = path_finding[n->node_id]; //result + vector &el = node_edges[n]; //our edges + curp = cv.begin(); + endp = cv.end(); + for(r = 0; curp != endp; curp++, r++) { + if(n->node_id == r) { + //this is the end of the path, we cannot take an edge to reach ourself + pf[r] = NULL; + continue; + } + int my_dist = *curp; + if(my_dist == INT_MAX) { + //this node is unreachable. + pf[r] = NULL; + continue; + } + + //else, node r reachable from us, find best edge. + cure = el.begin(); + ende = el.end(); +// int shortest; + PathEdge *ce; + PathNode *cn; + //for each edge + for(; cure != ende; cure++) { + ce = *cure; + //find the node other than ourself on the edge + if(ce->from == n) + cn = ce->to; + else + cn = ce->from; + //see how far away this node is + int cdist = D[cn->node_id][r]; + if(cdist < my_dist) { + //found one which is closer... due to min span tree, there + //should only be one path possible so just go with it. + pf[r] = ce; + break; + } + } + //assume that this node got assigned.. next +if(pf[r] == NULL) { +printf("Node id %d was not able to find a good path to node %d\n", n->node_id, r); +} + } + } +} + + + + + + diff --git a/utils/deprecated/apathing/boostcrap.h b/utils/deprecated/apathing/boostcrap.h new file mode 100644 index 000000000..7c6e349aa --- /dev/null +++ b/utils/deprecated/apathing/boostcrap.h @@ -0,0 +1,32 @@ +#ifndef BOOSTCRAP_H +#define BOOSTCRAP_H + +#include "apathing.h" + +#include +#include +#include +#include +#include +using namespace boost; + + +//man I hate boost +typedef adjacency_list < vecS, vecS, undirectedS, + property, property < edge_weight_t, int > > + MyGraph; + +typedef graph_traits < MyGraph >::vertex_descriptor VertDesc; +typedef graph_traits < MyGraph >::edge_descriptor EdgeDesc; + +void build_boost_graph(MyGraph &vg, property_map::type &weightmap, map &em, PathGraph *big, bool set_weights = true); +void run_min_spanning_tree(MyGraph &vg, property_map::type &weightmap, map &em, PathGraph *big, int start_node); +void find_disjoint_grids(Map *map, MyGraph &vg, PathGraph *big, const char *file, vector &start_nodes, vector &disjoints); +void calc_path_lengths(Map *map, MyGraph &vg, PathGraph *big, map &em, const char *fname); +void color_disjoint_graphs(PathGraph *big, MyGraph &vg, Map *map, const char *fname, vector< vector > &D, vector &counts, vector &disjoint_counts, vector &first_node ); +void count_crossing_lines(list &edges, PathGraph *out, PathGraph *excess, map > &cross_list); +void consolidate_cross_graphs(Map *map, PathGraph *cross_big, PathGraph *cross_excess, MyGraph &cross_graph, const char *fname); +void just_color_the_damned_thing(Map *map, PathGraph *big, const char *fname); + +#endif + diff --git a/utils/deprecated/apathing/gpoint.cpp b/utils/deprecated/apathing/gpoint.cpp new file mode 100644 index 000000000..e50ef77d1 --- /dev/null +++ b/utils/deprecated/apathing/gpoint.cpp @@ -0,0 +1,113 @@ +#include +#include +#include "gpoint.h" + + +GPoint::GPoint() { + x = 0; + y = 0; + z = 0; +} + +GPoint::GPoint(VERTEX &v) { + x = v.x; + y = v.y; + z = v.z; +} + +GPoint::GPoint(float ix, float iy, float iz) { + x = ix; + y = iy; + z = iz; +} + +GPoint::GPoint(const GPoint &them) { + x = them.x; + y = them.y; + z = them.z; +} + +//dot of x,y,z +float GPoint::dot3(const GPoint &them) const { + return((x * them.x) + (y * them.y) + + (z * them.z)); +} + +//cross product +GPoint GPoint::cross(const GPoint &them) const { + return(GPoint(y * them.z - z * them.y, + z * them.x - x * them.z, + x * them.y - y * them.x)); +} + + +const GPoint &GPoint::operator+=(const GPoint &them) { + x += them.x; + y += them.y; + z += them.z; + return(*this); +} +const GPoint &GPoint::operator*=(const float num) { + x *= num; + y *= num; + z *= num; + return(*this); +} + +GPoint operator-(const GPoint &v1, const GPoint &v2) { + return(GPoint(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z)); +} + +//ordering on X only +bool operator<(const GPoint &v1, const GPoint &v2) { + return(v1.x < v2.x); +} + +//ordering on X only +bool operator>(const GPoint &v1, const GPoint &v2) { + return(v1.x > v2.x); +} + + +GVector::GVector() : GPoint() { + W = 0; +} + +GVector::GVector(const GPoint &them) : GPoint(them) { + W = 1.0f; +} + +GVector::GVector(const GPoint &from, const GPoint &to) +: GPoint(to.x - from.x, to.y - from.y, to.z - from.z) +{ + W = 1.0f; +} + +GVector::GVector(float x, float y, float z, float w) : GPoint(x, y, z) { + W = w; +} + +//dot product of x,y,z,w +float GVector::dot4(const GVector &them) const { + return((x * them.x) + (y * them.y) + + (z * them.z) + (W * them.W)); +} + +//dot product of x,y,z+w +float GVector::dot4(const GPoint &them) const { + return((x * them.x) + (y * them.y) + + (z * them.z) + W); +} + +float GVector::length() { + return(sqrt((x * x) + (y * y) + (z * z))); +} + +void GVector::normalize() { + float len = length(); //stupid square roots take forever + x /= len; + y /= len; + z /= len; +} + + diff --git a/utils/deprecated/apathing/gpoint.h b/utils/deprecated/apathing/gpoint.h new file mode 100644 index 000000000..05e0cb45d --- /dev/null +++ b/utils/deprecated/apathing/gpoint.h @@ -0,0 +1,49 @@ +#ifndef GPOINT_H +#define GPOINT_H + +#include "../zone/map.h" + +class GPoint { +public: + GPoint(); + GPoint(const GPoint &them); + GPoint(VERTEX &v); + GPoint(float x, float y, float z); + + inline void operator()(float nx, float ny, float nz) { x = nx; y = ny; z = nz; } + + GPoint cross(const GPoint &them) const; + float dot3(const GPoint &them) const; + + const GPoint &operator+=(const GPoint &them); + const GPoint &operator*=(const float num); + + float x; + float y; + float z; + +}; +GPoint operator-(const GPoint &v1, const GPoint &v2); +bool operator<(const GPoint &v1, const GPoint &v2); +bool operator>(const GPoint &v1, const GPoint &v2); + +class GVector : public GPoint { +public: + GVector(); + GVector(const GPoint &them); + GVector(const GPoint &from, const GPoint &to); + GVector(float x, float y, float z, float w = 1.0f); + + inline void operator()(float nx, float ny, float nz, float nw) { x = nx; y = ny; z = nz; W = nw; } + float dot4(const GVector &them) const; + float dot4(const GPoint &them) const; + void normalize(); + float length(); + + + float W; +}; + + +#endif + diff --git a/utils/deprecated/apathing/load_db.cpp b/utils/deprecated/apathing/load_db.cpp new file mode 100644 index 000000000..44a5f7b80 --- /dev/null +++ b/utils/deprecated/apathing/load_db.cpp @@ -0,0 +1,276 @@ +/* + +Fear Pathing generation utility. +(c) 2005 Father Nitwit + +*/ +#include "../common/types.h" +#include "../zone/map.h" +#include "../common/rdtsc.h" +#include "quadtree.h" +#include "apathing.h" +#include +#include +#include +#include +#include + + + +bool load_paths_from_db(MYSQL *m, Map *map, const char *zone, list &db_paths, list &end_points) { + char query[512]; + + sprintf(query, + "SELECT x,y,z,gridid FROM grid_entries,zone " + "WHERE zone.zoneidnumber=zoneid AND short_name='%s' " + "ORDER BY gridid,number", zone); + if(mysql_query(m, query) != 0) { + printf("Unable to query: %s\n", mysql_error(m)); + return(false); + } + + MYSQL_RES *res = mysql_store_result(m); + if(res == NULL) { + printf("Unable to store res: %s\n", mysql_error(m)); + return(false); + } + + MYSQL_ROW row; + + PathNode *cur = NULL, *last = NULL, *first = NULL; + PathGraph *g = NULL; + int cur_g = -1,last_g = -1; + +// int lid = 0; + + while((row = mysql_fetch_row(res))) { + last = cur; + cur = new PathNode; +// cur->load_id = lid++; + cur->x = atof(row[0]); + cur->y = atof(row[1]); + cur->z = atof(row[2]); + cur_g = atoi(row[3]); + if(cur_g != last_g) { + if(g != NULL) { + //if we have a first and last node for this path + //and they are not the same node, try to connect them. + if(first != NULL && last != NULL && first != last && last->Dist2(first) < ENDPOINT_CONNECT_MAX_DISTANCE*ENDPOINT_CONNECT_MAX_DISTANCE) { + if(CheckLOS(map, last, first)) + g->add_edge(last, first); + } +#ifdef LINK_PATH_ENDPOINTS + if(first != last && last != NULL) + end_points.push_back(last); +#endif + db_paths.push_back(g); + } + g = new PathGraph(); + first = cur; + last_g = cur_g; + last = NULL; + } + + g->nodes.push_back(cur); + +#ifdef LINK_PATH_ENDPOINTS + //this is a begining point + if(last == NULL) + end_points.push_back(cur); +#endif + + if(last != NULL) { +#ifdef SPLIT_INVALID_PATHS + if(CheckLOS(map, last, cur)) { + g->edges.push_back(new PathEdge(last, cur)); + } else { + //no LOS, split the path into two + load_split_paths++; + last_g = -1; //tell this thing to start over + } +#else + g->edges.push_back(new PathEdge(last, cur)); +#endif + } + } + + //handle the last active path + if(g != NULL) { + if(first != NULL && cur->Dist2(first) < ENDPOINT_CONNECT_MAX_DISTANCE*ENDPOINT_CONNECT_MAX_DISTANCE) { + if(CheckLOS(map, cur, first)) + g->add_edge(cur, first); + } + db_paths.push_back(g); + } + + mysql_free_result(res); + return(true); +} + + +bool load_spawns_from_db(MYSQL *m, const char *zone, list &db_spawns) { + char query[512]; + + sprintf(query, + "SELECT x,y,z FROM spawn2 " + "WHERE zone='%s'", zone); + if(mysql_query(m, query) != 0) { + printf("Unable to query: %s\n", mysql_error(m)); + return(false); + } + + MYSQL_RES *res = mysql_store_result(m); + if(res == NULL) { + printf("Unable to store res: %s\n", mysql_error(m)); + return(false); + } + + MYSQL_ROW row; + + PathNode *cur = NULL; + + while((row = mysql_fetch_row(res))) { + cur = new PathNode; + cur->x = atof(row[0]); + cur->y = atof(row[1]); + cur->z = atof(row[2]); + db_spawns.push_back(cur); + } + + mysql_free_result(res); + + return(true); +} + + +bool load_doors_from_db(MYSQL *m, const char *zone, list &db_spawns) { + char query[512]; + + sprintf(query, + "SELECT pos_x,pos_y,pos_z FROM doors " + "WHERE zone='%s'", zone); + if(mysql_query(m, query) != 0) { + printf("Unable to query: %s\n", mysql_error(m)); + return(false); + } + + MYSQL_RES *res = mysql_store_result(m); + if(res == NULL) { + printf("Unable to store res: %s\n", mysql_error(m)); + return(false); + } + + MYSQL_ROW row; + + PathNode *cur = NULL; + + while((row = mysql_fetch_row(res))) { + cur = new PathNode; + cur->x = atof(row[0]); + cur->y = atof(row[1]); + cur->z = atof(row[2]); + //TODO: it would be nice if we could get to the middle of these + //doors, not the edge of them which I assume these points are + db_spawns.push_back(cur); + } + + mysql_free_result(res); + + return(true); +} + + +bool load_hints_from_db(MYSQL *m, const char *zone, list &db_spawns) { + char query[512]; + + sprintf(query, + "SELECT x,y,z,forced,disjoint FROM fear_hints " + "WHERE zone='%s'", zone); + if(mysql_query(m, query) != 0) { + printf("Unable to query: %s\n", mysql_error(m)); + return(false); + } + + MYSQL_RES *res = mysql_store_result(m); + if(res == NULL) { + printf("Unable to store res: %s\n", mysql_error(m)); + return(false); + } + + MYSQL_ROW row; + + PathNode *cur = NULL; + + while((row = mysql_fetch_row(res))) { + cur = new PathNode; + cur->x = atof(row[0]); + cur->y = atof(row[1]); + cur->z = atof(row[2]); + cur->forced = atoi(row[3])?true:false; + cur->disjoint = atoi(row[4])?true:false; + db_spawns.push_back(cur); + } + + mysql_free_result(res); + + return(true); +} + + +bool load_settings_from_db(MYSQL *m, const char *zone) { + char query[512]; + + sprintf(query, + "SELECT use_doors, min_fix_z, max_fear_distance, image_scale, split_invalid_paths," + " link_path_endpoints, end_distance, split_long_min, split_long_step, same_dist, node_combine_dist," + " grid_combine_dist, close_all_los, cross_count, cross_min_length, cross_max_z_diff, cross_combine_dist," + " second_link_dist, link_max_dist, link_count" + " FROM fear_settings" + " WHERE zone='%s'", zone); + if(mysql_query(m, query) != 0) { +// printf("Unable to query: %s\n", mysql_error(m)); + return(false); + } + + MYSQL_RES *res = mysql_store_result(m); + if(res == NULL) { +// printf("Unable to store res: %s\n", mysql_error(m)); + return(false); + } + + MYSQL_ROW row; + + int r = 0; + if((row = mysql_fetch_row(res))) { + INCLUDE_DOORS = atoi(row[r++])?true:false; + MIN_FIX_Z = atof(row[r++]); + FEAR_MAXIMUM_DISTANCE = atof(row[r++]); + IMAGE_SCALE = atoi(row[r++]); + SPLIT_INVALID_PATHS = atoi(row[r++])?true:false; + LINK_PATH_ENDPOINTS = atoi(row[r++])?true:false; + ENDPOINT_CONNECT_MAX_DISTANCE = atof(row[r++]); + SPLIT_LINE_LENGTH = atof(row[r++]); + SPLIT_LINE_INTERVAL = atof(row[r++]); + CLOSE_ENOUGH = atof(row[r++]); + CLOSE_ENOUGH_COMBINE = atof(row[r++]); + MERGE_MIN_SECOND_DIST = atof(row[r++]); + COMBINE_CHECK_ALL_LOS = atoi(row[r++])?true:false; + CROSS_REDUCE_COUNT = atoi(row[r++]); + CROSS_MIN_LENGTH = atof(row[r++]); + CROSS_MAX_Z_DIFF = atof(row[r++]); + CLOSE_ENOUGH_CROSS = atof(row[r++]); + + SPAWN_MIN_SECOND_DIST = atof(row[r++]); + MAX_LINK_SPAWN_DIST = atof(row[r++]); + int sc = atoi(row[r++]); + SPAWN_LINK_TWICE = sc >= 2?true:false; + SPAWN_LINK_THRICE = sc >= 3?true:false; + mysql_free_result(res); + return(true); + } + + mysql_free_result(res); + + return(false); +} + diff --git a/utils/deprecated/apathing/pathfinding.cpp b/utils/deprecated/apathing/pathfinding.cpp new file mode 100644 index 000000000..0ff7272ec --- /dev/null +++ b/utils/deprecated/apathing/pathfinding.cpp @@ -0,0 +1,212 @@ + +//this is the path finding portion of the program. + +#include "../common/types.h" +#include "../zone/map.h" +#include "../common/rdtsc.h" +#include "quadtree.h" +#include "apathing.h" +#include "boostcrap.h" +#include +#include +#include +#include + +#include +#include +#include +#include +using namespace std; + +void find_path_info(Map *map, PathGraph *big, vector< vector > &path_finding) { + //make sure our path finding vector is big enough, and all NULL + { + int size = big->nodes.size(); + vector tmp(size, (PathEdge*)NULL); + path_finding.resize(0); + path_finding.resize(size, tmp); + } + + //take our minimal spanning tree and try to link every single + //point in it like spawns are linked. The idea is to create + //a large set of alternative paths and cycles. + //get our list of nodes. + list all_points = big->nodes; + big->nodes.clear(); + //make our current edge list + std::map< pair, bool > edgelist; + list::iterator cure,ende,tmp; + PathEdge *e; + cure = big->edges.begin(); + ende = big->edges.end(); + PathNode *from; + PathNode *to; + for(; cure != ende; cure++) { + e = *cure; + from = e->from; + to = e->to; + //hack to make order on the ID not matter + if(int32(from) < int32(to)) { + PathNode *tmp = from; + from = to; + to = tmp; + } + pair id(from, to); + edgelist[id] = true; + } + //link up the points, making sure not to add edges allready in the MST + link_spawns(map, big, all_points, FINAL_LINK_POINTS_DIST, &edgelist); + + //now we have our graph all connected up. + + /* + disabled in favor of all-pairs shortest path. + + //get the list of edges leaving each node. + std::map > node_edges; + find_node_edges(big, node_edges); + + //color every node 0 + list::iterator curn, endn; + curn = big->nodes.begin(); + endn = big->nodes.end(); + for(; curn != endn; curn++) { + (*curn)->color = 0; + } + + //for each node, run a breadth first search from each node to find the + //shortest path to each node from each node. + */ + + + //run all pairs shortest path so we have some information to + //use for our metric in A* + + //build our boost graph with length weights + MyGraph vg(big->nodes.size()); + property_map::type weightlist; + std::map edgemap; + build_boost_graph(vg, weightlist, edgemap, big, true); //set weights to distances + + + vector< vector > D; + vector counts; + vector disjoint_counts; + vector first_node; + + //color the graph and get us the info we need to do our job (D) + color_disjoint_graphs(big, vg, map, NULL, D, counts, disjoint_counts, first_node); + + //figure out what edges link to each node. + std::map > node_edges; + find_node_edges(big, node_edges); + + int cur_node_id; + list::iterator curn,endn; + PathNode *n; + vector::iterator curp,endp; + vector::iterator curev,endev; + vector::iterator cur_res, end_res; + + curn = big->nodes.begin(); + endn = big->nodes.end(); + for(; curn != endn; curn++) { + n = *curn; + + //get all our vector refs that we need since + vector &cv = D[n->node_id]; //distance array + vector &pf = path_finding[n->node_id]; //result + vector &el = node_edges[n]; //our edges + + //for each other node, find an edge which gets us closer to that node + curp = cv.begin(); + endp = cv.end(); + cur_res = pf.begin(); + end_res = pf.end(); + for(cur_node_id = 0; curp != endp && cur_res != end_res; curp++, cur_node_id++, cur_res++) { + int distance_to_cur_node = *curp; + + //if this ourself, we have no path basically. + if(n->node_id == cur_node_id) { + *cur_res = NULL; + continue; + } + //if we cant reach them, why look + if(distance_to_cur_node == INT_MAX) { + *cur_res = NULL; + continue; + } + + //see which edge gets us closer + int closest = distance_to_cur_node; //start with the distance between this node and the target node, we need something closer + PathEdge *best_edge = NULL; + curev = el.begin(); + endev = el.end(); + for(; curev != endev; curev++) { + e = *curev; + int this_dist; //distance from the other end of this edge to the current target node + if(e->from == n) { + if(e->to->node_id == cur_node_id) + this_dist = 0; //this is the goal node + else + this_dist = D[e->to->node_id][cur_node_id]; + } else { //assume e->to == n + if(e->from->node_id == cur_node_id) + this_dist = 0; //this is the goal node + else + this_dist = D[e->from->node_id][cur_node_id]; + } + if(this_dist == 0) { + //this will be the best. + best_edge = e; + break; + } + if(this_dist == INT_MAX) + continue; //not reachable + if(this_dist < closest) { + closest = this_dist; + best_edge = e; + } + } + if(best_edge == NULL) { +//unable to find a path... + printf("Should have been able to find a path from %d to %d, but couldent.\n", n->node_id, cur_node_id); + } + *cur_res = best_edge; + } + } + + + + + /* + we have our node edges, our all pairs shortest path + our graphs, and our MST edge list. + now we can run A* + + - our metric should use the all pairs shortest path to + determine the value for h. + - We want to favor edges from the MST over newly created edges, + so we will give such edges a lower weight, maybe by 20%? + + + + */ + +} + + + + + + + + + + + + + + + + diff --git a/utils/deprecated/apathing/quadtree.cpp b/utils/deprecated/apathing/quadtree.cpp new file mode 100644 index 000000000..fad704505 --- /dev/null +++ b/utils/deprecated/apathing/quadtree.cpp @@ -0,0 +1,366 @@ +/* + + Father Nitwit's Fear Pathing File Maker Thing + Copyright (C) 2005 Father Nitwit (eqemu@8ass.com) + + I'll release thisunder the GPL, even though I hate the GPL. + + 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 +*/ + +/* + + This is a modified quadtree that stores a list of all + nodes reachable within a defined distance from the bounds + of that quadtree node. Therefor this distance should be + the maximum distance used to search for a node. + + In case this distance is not big enough, we also store a + complete list of reachable nodes at each quadtree level, so + the root has all nodes for sure. This makes it possible to + garuntee that we can find A node which is closest. + +*/ + +#include "quadtree.h" +#include "apathing.h" +#include "../zone/map.h" + +//#define SPLIT_DEBUG + +QTNode::QTNode(Map *_map, float dist2, float Tminx, float Tmaxx, float Tminy, float Tmaxy) { + node1 = NULL; + node2 = NULL; + node3 = NULL; + node4 = NULL; + minx = Tminx; + maxx = Tmaxx; + miny = Tminy; + maxy = Tmaxy; + map = _map; + search_dist2 = dist2; //this is a distance-squared + final = false; + buildVertexes(); + +} + +QTNode::~QTNode() { + clearNodes(); +} + +void QTNode::clearNodes() { + if(node1 != NULL) + delete node1; + if(node2 != NULL) + delete node2; + if(node3 != NULL) + delete node3; + if(node4 != NULL) + delete node4; + node1 = NULL; + node2 = NULL; + node3 = NULL; + node4 = NULL; +} + + +//assumes that both supplied arrays are big enough per countQTNodes/Facelists +void QTNode::fillBlocks(PathTree_Struct *heads, PathPointRef *flist, unsigned long &hindex, unsigned long &findex) { + PathTree_Struct *head = &heads[hindex]; + hindex++; + + head->minx = minx; + head->maxx = maxx; + head->miny = miny; + head->maxy = maxy; + head->flags = 0; +//printf("Node %u: (%.2f -> %.2f, %.2f -> %.2f)\n", hindex-1, head->minx, head->maxx, head->miny, head->maxy); + + //rearranged to give all QT nodes a node list + head->nodelist.count = nodes.size(); + head->nodelist.offset = findex; +//printf(" Final node with %u nodes, list offset %lu.\n", head->nodes.count, head->nodes.offset); + list::iterator curs,end; + curs = nodes.begin(); + end = nodes.end(); + for(; curs != end; curs++) { +//printf("Got to node index %d (0x%x)\n", findex, *curs); + PathNode *cur = *curs; + flist[findex] = cur->node_id; + findex++; + } +// findex += head->nodes.count; + + + if(final) { + head->flags |= pathNodeFinal; + } else { + head->flags = 0; + //branch node. + + if(node1 != NULL) { + head->nodes[0] = hindex; + node1->fillBlocks(heads, flist, hindex, findex); + } else { + head->nodes[0] = PATH_NODE_NONE; + } + if(node2 != NULL) { + head->nodes[1] = hindex; + node2->fillBlocks(heads, flist, hindex, findex); + } else { + head->nodes[1] = PATH_NODE_NONE; + } + if(node3 != NULL) { + head->nodes[2] = hindex; + node3->fillBlocks(heads, flist, hindex, findex); + } else { + head->nodes[2] = PATH_NODE_NONE; + } + if(node4 != NULL) { + head->nodes[3] = hindex; + node4->fillBlocks(heads, flist, hindex, findex); + } else { + head->nodes[3] = PATH_NODE_NONE; + } + } +} + +unsigned long QTNode::countQTNodes() const { + unsigned long c = 1; + if(node1 != NULL) + c += node1->countQTNodes(); + if(node2 != NULL) + c += node2->countQTNodes(); + if(node3 != NULL) + c += node3->countQTNodes(); + if(node4 != NULL) + c += node4->countQTNodes(); + return(c); +} + +/*unsigned long QTNode::countNodes() const { + unsigned long c = nodes.size(); + if(node1 != NULL) + c += node1->countNodes(); + if(node2 != NULL) + c += node2->countNodes(); + if(node3 != NULL) + c += node3->countNodes(); + if(node4 != NULL) + c += node4->countNodes(); + return(c); +}*/ + +unsigned long QTNode::countPathNodes() const { +// unsigned long c = final? nodes.size() : 0; + unsigned long c = nodes.size(); + if(node1 != NULL) + c += node1->countPathNodes(); + if(node2 != NULL) + c += node2->countPathNodes(); + if(node3 != NULL) + c += node3->countPathNodes(); + if(node4 != NULL) + c += node4->countPathNodes(); + return(c); +} + +void QTNode::divideYourself(int depth) { +// printf("Dividing in box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n", +// minx, maxx, miny, maxy, depth, nodes.size()); + + unsigned long cc; + cc = nodes.size(); +#ifdef MAX_QUADRENT_NODES + if(cc <= MAX_QUADRENT_NODES) { +#ifdef SPLIT_DEBUG +printf("Stopping (nodecount) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n", + minx, maxx, miny, maxy, depth, cc); +#endif + final = true; + return; + } +#endif + +#ifdef MIN_QUADRENT_SIZE + if((maxx - minx) < MIN_QUADRENT_SIZE || (maxy - miny) < MIN_QUADRENT_SIZE) { +#ifdef SPLIT_DEBUG +printf("Stopping on box (size) (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n", + minx, maxx, miny, maxy, depth, cc); +#endif + final = true; + return; + } +#endif + + doSplit(); + + //get counts on our split + float c1, c2, c3, c4; + c1 = node1? node1->nodes.size() : 0; + c2 = node2? node2->nodes.size() : 0; + c3 = node3? node3->nodes.size() : 0; + c4 = node4? node4->nodes.size() : 0; + +#ifdef MIN_QUADRENT_GAIN + int miss = 0; + float gain1 = 1.0 - c1 / cc; + float gain2 = 1.0 - c2 / cc; + float gain3 = 1.0 - c3 / cc; + float gain4 = 1.0 - c4 / cc; + + //see how many missed the gain mark + if(gain1 < MIN_QUADRENT_GAIN) + miss++; + if(gain2 < MIN_QUADRENT_GAIN) + miss++; + if(gain3 < MIN_QUADRENT_GAIN) + miss++; + if(gain4 < MIN_QUADRENT_GAIN) + miss++; + + if(miss > MAX_QUADRENT_MISSES) { +#ifdef SPLIT_DEBUG +printf("Stopping (gain) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n", + minx, maxx, miny, maxy, depth, cc); +#endif + final = true; + return; + } +#endif + + + //if all nodes pass through all quadrents, then we are done + //partially obsoleted by gain test. + if(c1 == c2 && c1 == c3 && c1 == c4) { +#ifdef SPLIT_DEBUG +printf("Stopping (empty) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d nodes.\n", + minx, maxx, miny, maxy, depth, cc); +printf("Our counts: %.3f, %.3f, %.3f, %.3f\n", c1, c2, c3, c4); +#endif + final = true; + return; + } + + //there are prolly some more intelligent stopping criteria... + + depth++; + + if(node1 != NULL) + node1->divideYourself(depth); + if(node2 != NULL) + node2->divideYourself(depth); + if(node3 != NULL) + node3->divideYourself(depth); + if(node4 != NULL) + node4->divideYourself(depth); + + +} + +void QTNode::buildVertexes() { + + v[0].x = v[1].x = v[2].x = v[3].x = minx; + v[4].x = v[5].x = v[6].x = v[7].x = maxx; + + v[0].y = v[1].y = v[4].y = v[5].y = miny; + v[2].y = v[3].y = v[6].y = v[7].y = maxy; + + v[0].z = v[3].z = v[4].z = v[7].z = -999999; + v[1].z = v[2].z = v[5].z = v[6].z = 9999999; +} + +bool QTNode::IsInNode(const QTNode *n, const PathNode *o) { +//printf("IIN: (%.3f,%.3f) in (%.3f -> %.3f, %.3f -> %.3f)\n", o->x, o->y, n->minx, n->maxx, n->miny, n->maxy); + if( o->x >= n->minx && o->x < n->maxx + && o->y >= n->miny && o->y < n->maxy ) + return(true); + + //well its not inside the node, so see if it is reachable from it + + //4 points of this node + GPoint pt1(n->minx, n->miny, 0), + pt2(n->minx, n->maxy, 0), + pt3(n->maxx, n->miny, 0), + pt4(n->maxx, n->maxy, 0); + if( o->Dist2(&pt1) < search_dist2 + || o->Dist2(&pt2) < search_dist2 + || o->Dist2(&pt3) < search_dist2 + || o->Dist2(&pt4) < search_dist2) + return(true); + + //not inside, and not reachable... + + return(false); +} + +void QTNode::doSplit() { + + + //find midpoints... + float midx = minx + (maxx - minx) / 2.0; + float midy = miny + (maxy - miny) / 2.0; + + //ordering following definitions in map.h + node1 = new QTNode(map, search_dist2, midx, maxx, midy, maxy); + node2 = new QTNode(map, search_dist2, minx, midx, midy, maxy); + node3 = new QTNode(map, search_dist2, minx, midx, miny, midy); + node4 = new QTNode(map, search_dist2, midx, maxx, miny, midy); + if(node1 == NULL || node2 == NULL || node3 == NULL || node4 == NULL) { + printf("Error: unable to allocate new QTNode, giving up.\n"); + return; + } + +// unsigned long l; +// l = faces.size(); +// for(r = 0; r < l; r++) { +// PathNode *cur = faces[r]; + list::iterator curs,end; + curs = nodes.begin(); + end = nodes.end(); + for(; curs != end; curs++) { + PathNode *cur = *curs; + if(IsInNode(node1, cur)) + node1->nodes.push_back(cur); + if(IsInNode(node2, cur)) + node2->nodes.push_back(cur); + if(IsInNode(node3, cur)) + node3->nodes.push_back(cur); + if(IsInNode(node4, cur)) + node4->nodes.push_back(cur); + } + + //clean up empty sets. + if(node1->nodes.size() == 0) { + delete node1; + node1 = NULL; + } + if(node2->nodes.size() == 0) { + delete node2; + node2 = NULL; + } + if(node3->nodes.size() == 0) { + delete node3; + node3 = NULL; + } + if(node4->nodes.size() == 0) { + delete node4; + node4 = NULL; + } + +} + + + diff --git a/utils/deprecated/apathing/quadtree.h b/utils/deprecated/apathing/quadtree.h new file mode 100644 index 000000000..1b568774a --- /dev/null +++ b/utils/deprecated/apathing/quadtree.h @@ -0,0 +1,88 @@ +#ifndef FPQUADTREE_H +#define FPQUADTREE_H + +//pull in datatypes from zone's map.h +#include "../zone/pathing.h" +#include "gpoint.h" + +/* + + Father Nitwit's Fear Pathing File Maker Thing + +*/ + +#include +#include +#include +using namespace std; + + +#define COUNT_MACTHES 1 + +//this is the version number to put in the map header +#undef PATHFILE_VERSION //override this from fearpath.h with our version +#define PATHFILE_VERSION 0x02000000 + +//quadtree stopping criteria, comment any to disable them +//you want to keep the nodes small since the fear space is very sparse +#define MAX_QUADRENT_NODES 4 //if box has fewer than this, stop +#define MIN_QUADRENT_SIZE 50.0f //if box has a dimention smaller than this, stop +#define MIN_QUADRENT_GAIN 0.1f //minimum split ratio before stopping +#define MAX_QUADRENT_MISSES 2 //maximum number of quads which can miss their gains + //1 or 2 make sense, others are less useful + +class PathNode; +class Map; + +//quadtree node container +class QTNode { +public: + QTNode(Map *_map, float _dist2, float Tminx, float Tmaxx, float Tminy, float Tmaxy); + ~QTNode(); + + void clearNodes(); + + void doSplit(); + void divideYourself(int depth); + + void buildVertexes(); + + unsigned long countQTNodes() const; //counts QT nodes + unsigned long countPathNodes() const; //counts PathNodes + + void fillBlocks(PathTree_Struct *heads, PathPointRef *flist, unsigned long &hindex, unsigned long &findex); + + float minx; + float miny; + float maxx; + float maxy; + unsigned long nnodes; + list nodes; + + bool IsInNode(const QTNode *n, const PathNode *o); + + /* + quadrent definitions: + quad 1 (node1): + x>=0, y>=0 + quad 2 (node2): + x<0, y>=0 + quad 3 (node3): + x<0, y<0 + quad 4 (node4): + x>=0, y<0 + */ + QTNode *node1; + QTNode *node2; + QTNode *node3; + QTNode *node4; + GPoint v[8]; + bool final; + + Map *map; + float search_dist2; +}; + + +#endif + diff --git a/utils/deprecated/apathing/runall b/utils/deprecated/apathing/runall new file mode 100644 index 000000000..33aa26f17 --- /dev/null +++ b/utils/deprecated/apathing/runall @@ -0,0 +1,23 @@ +#!/bin/sh + +#shortnames update... +#echo "SELECT short_name FROM zone" | ~/eqemu/runmysql -B|grep -v _ >shortnames + +mkdir -p images +mkdir -p output + +for f in `cat shortnames` +do + if [ -r "$f.path" ]; then + echo "We allready have $f's path file. Remove it to rebuild" + continue; + fi + + echo + echo + echo "********************************************************************************" + echo "Building $f" + + ./apath "$f" | tee "output/$f.txt" + mv "paths-${f}-mstree.png" images/ +done diff --git a/utils/deprecated/apathing/shortnames b/utils/deprecated/apathing/shortnames new file mode 100644 index 000000000..7bf72d14a --- /dev/null +++ b/utils/deprecated/apathing/shortnames @@ -0,0 +1,131 @@ +airplane +akanon +arena +arena2 +befallen +beholder +blackburrow +burningwood +butcher +cabeast +cabwest +cauldron +cazicthule +charasis +chardok +citymist +cobaltscar +commons +crushbone +crystal +cshome +dalnir +dreadlands +droga +eastkarana +eastwastes +ecommons +emeraldjungle +erudnext +erudnint +erudsxing +everfrost +fearplane +feerrott +felwithea +felwitheb +fieldofbone +firiona +freporte +freportn +freportw +frontiermtns +frozenshadow +gfaydark +greatdivide +growthplane +gukbottom +gukta +guktop +halas +hateplane +hateplaneb +highkeep +highpass +hole +iceclad +innothule +jaggedpine +kael +kaesora +kaladima +kaladimb +karnor +kedge +kerraridge +kithicor +kurn +lakeofillomen +lakerathe +lavastorm +lfaydark +load +load2 +mischiefplane +mistmoore +misty +najena +necropolis +nektulos +neriaka +neriakb +neriakc +northkarana +nro +nurga +oasis +oggok +oot +overthere +paineel +paw +permafrost +qcat +qey2hh1 +qeynos +qeynos2 +qeytoqrg +qrg +rathemtn +rivervale +runnyeye +sebilis +sirens +skyfire +skyshrine +sleeper +soldunga +soldungb +soltemple +southkarana +sro +steamfont +stonebrunt +swampofnohope +templeveeshan +thurgadina +thurgadinb +timorous +tox +trakanon +tutorial +tutoriala +tutorialb +unrest +veeshan +veksar +velketor +wakening +warrens +warslikswood +westwastes diff --git a/utils/deprecated/asmtools/locate_stringids.pl b/utils/deprecated/asmtools/locate_stringids.pl new file mode 100644 index 000000000..7d9482f67 --- /dev/null +++ b/utils/deprecated/asmtools/locate_stringids.pl @@ -0,0 +1,32 @@ +#! /usr/bin/perl -W + +#reads the output of `objdump -D eqgame.exe` from stdin +#reads eqstr_us.txt from . + +open(F, ") { + s/\r?\n//g; + next unless(/^([0-9]+)\s+(.+)$/); + $strs{$1} = $2; +} +close(F); + +while() { + if (/push\s+(.*)/) { + push(@stack,$1); + shift @stack if ($#stack>4); + } + #address of DisplayStringID + if (/ ([0-9a-f]+):.*call 0x5DFA00/i) { + my $loc = $1; + if($stack[$#stack] =~ /(0x[0-9a-fA-F]+)/) { + my $sid = hex($1); + if(defined($strs{$sid})) { + printf("%s Sends %s\n",$loc,$strs{$sid}); + } else { + printf("%s Sends %s\n",$loc,"UNDEFINED STRING"); + } + } + } +} diff --git a/utils/deprecated/asmtools/opcodes_to_ida.pl b/utils/deprecated/asmtools/opcodes_to_ida.pl new file mode 100644 index 000000000..68146b32e --- /dev/null +++ b/utils/deprecated/asmtools/opcodes_to_ida.pl @@ -0,0 +1,17 @@ +#!/usr/bin/perl + +print "#include \"idc.idc\"\n\nstatic main() {\n"; + +print "\tauto id;\n"; +print "\tDelEnum(GetEnum(\"EQOpcode\"));\n"; +print "\tid = AddEnum(GetEnumQty(), \"EQOpcode\", 0);\n"; + +while(<>) { + next unless(/^(OP_[^= \t]+)=(0x[0-9a-fA-F]+)/); + next if($2 eq "0x0000" || hex($2) == 0); + printf("\tAddConstEx(id, \"$1\", $2, -1);\n"); +} + +print "}\n\n"; + + diff --git a/utils/deprecated/asmtools/sig_to_ida.pl b/utils/deprecated/asmtools/sig_to_ida.pl new file mode 100644 index 000000000..559101795 --- /dev/null +++ b/utils/deprecated/asmtools/sig_to_ida.pl @@ -0,0 +1,14 @@ +#!/usr/bin/perl + +do $ARGV[0]; + +print "#include \"idc.idc\"\n\nstatic main() {\n"; + +foreach my $sig(@signatures) { + my $off = $sig->{offset} + 0x00400000; + my $name = $sig->{name}; + my $op = $sig->{opcode}; + printf("MakeName(0x%x, \"${name}_0x%x\");\n", $off, $op); +} + +print "}\n\n"; diff --git a/utils/deprecated/asmtools/stringids_to_ida.pl b/utils/deprecated/asmtools/stringids_to_ida.pl new file mode 100644 index 000000000..58ac2c27c --- /dev/null +++ b/utils/deprecated/asmtools/stringids_to_ida.pl @@ -0,0 +1,20 @@ +#!/usr/bin/perl + +#reads the output of locate_stringids.pl from stdin +#produces a .IDC file on stdout which we can feed to IDA + +print "#include \"idc.idc\"\n\nstatic main() {\n"; + +while(<>) { + s/\r?\n//g; + next unless(/^([0-9a-fA-F]+) Sends (.*)/); + next if(hex($1) == 0); + my $off = hex($1); + my $str = substr($2, 0, 200); + $str =~ s/"/\\"/g; + printf("\tMakeComm($off, \"$str\");\n"); +} + +print "}\n\n"; + + diff --git a/utils/deprecated/azone/3d.hpp b/utils/deprecated/azone/3d.hpp new file mode 100644 index 000000000..1413dc33c --- /dev/null +++ b/utils/deprecated/azone/3d.hpp @@ -0,0 +1,75 @@ +#ifndef __OPENEQ_THREED__ +#define __OPENEQ_THREED__ + +#include "3d_base.hpp" + +class Octree_Node; + +class Bone { +public: + char *name; + + int bone1, bone2; + + float x, y, z; + float e[4]; + float scale[3]; + + int vert_count; + int *verts; +}; + +class Zone_Model { +public: + Zone_Model() {} + ~Zone_Model() {} + + Vertex **verts; + Polygon **polys; + Octree_Node *octree; + Texture **tex; + + int vert_count, poly_count, tex_count; +}; + +class MobModel { +public: + MobModel() {} + ~MobModel() {} + + Vertex **verts; + Polygon **polys; + Texture **tex; + + int vert_count, poly_count, tex_count; + + Bone *bones; + + int bone_count; +}; + +class Content_3D { +public: + Content_3D() { this->mob_model_count = 0; this->mob_count = 0; } + ~Content_3D() {} + + Model *GetModel(char *model_name); + + void GenerateOctree(); + + void MergeTextures(); + void MergeTexturesLazy(); + + Zone_Model *zone_model; + + Model **models; + Placeable **placeable; + + int model_count, plac_count; // Count of placeable object models and count of instances + + MobModel **mob_models; + Placeable **mob_locs; + int mob_model_count, mob_count; +}; + +#endif diff --git a/utils/deprecated/azone/3d_base.hpp b/utils/deprecated/azone/3d_base.hpp new file mode 100644 index 000000000..025fab15c --- /dev/null +++ b/utils/deprecated/azone/3d_base.hpp @@ -0,0 +1,73 @@ +#ifndef __OPENEQ_THREED_BASE__ +#define __OPENEQ_THREED_BASE__ + +#pragma pack(1) + +#include "archive.hpp" + +struct Light { + float x, y, z; + float r, g, b; + float rad; +} typedef Light; + +struct Vertex { + float x, y, z; + float i, j, k; + float u, v; + + int bone; +} typedef Vertex; + + +struct Polygon { + int flags; + + int v1, v2, v3; + + int tex; +} typedef Polygon; + + +struct Texture { + Archive *archive; + char frame_count; + char current_frame; + uint32 *tex; + char **filenames; + + char flags; +} typedef Texture; + +class Model { +public: + Model() {} + ~Model() {} + + Vertex **verts; + Polygon **polys; + Texture **tex; + + int vert_count, poly_count, tex_count; + + char *name; +}; + + +class Placeable { +public: + float x, y, z; + float rx, ry, rz; + float scale[3]; + + int model; + + char rendered; + + Vertex bounds[8]; + void CalculateBoundaries(Model *model); +}; + +#pragma pack() + +#endif diff --git a/utils/deprecated/azone/Azone.vcproj b/utils/deprecated/azone/Azone.vcproj new file mode 100644 index 000000000..520fb5c93 --- /dev/null +++ b/utils/deprecated/azone/Azone.vcproj @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/utils/deprecated/azone/README b/utils/deprecated/azone/README new file mode 100644 index 000000000..f60fb22b5 --- /dev/null +++ b/utils/deprecated/azone/README @@ -0,0 +1,24 @@ +Father Nitwit's Zone file converter + +purpose: convert 23d files into .map files for LOS + +If there are any 3d math folks reading this who know how to figure out +if a 3d triangle passes through a 3d, infinitely tall box (bounded x,y), +please let me know. The code in the converter dosent work right now, and +has been 'commented' out with the return on line 717 of azone.cpp + + +This does not work on windows, if you get it working, please let me know how. +You need to use a unix box to convert your map files, then the .maps should work +on your windows server. + +how to use it: +built it with make +upload your s3d files into a directory. + You do not need the _obj or _chr files, just the zone.s3d files. +copy the azone binary to that directory. +run it on each map file: +example: converting gfaydark + ./azone gfaydark + this will read gfaydark.s3d and produce gfaydark.map +put these map files into Maps/ in your eqemu running directory. diff --git a/utils/deprecated/azone/archive.hpp b/utils/deprecated/azone/archive.hpp new file mode 100644 index 000000000..8f3f5083d --- /dev/null +++ b/utils/deprecated/azone/archive.hpp @@ -0,0 +1,32 @@ +#ifndef __OPENEQ_ARCHIVE_API__ +#define __OPENEQ_ARCHIVE_API__ + +#include "global.hpp" + +#include + +class Archive { +public: + Archive() {} + virtual ~Archive() {} + + virtual int Open(FILE *fp) = 0; + virtual int Close() = 0; + + virtual int GetFile(char *name, uchar **buf, int *len) = 0; + virtual const char *FindExtension(const char *ext) = 0; + + char **filenames; + int count; + +protected: + uchar *buffer; + int buf_len; + + int status; + + uint32 *files; + FILE *fp; +}; + +#endif diff --git a/utils/deprecated/azone/awater.cpp b/utils/deprecated/azone/awater.cpp new file mode 100644 index 000000000..cffdfdb8b --- /dev/null +++ b/utils/deprecated/azone/awater.cpp @@ -0,0 +1,362 @@ +/* + + Father Nitwit's Zone to map conversion program. + Copyright (C) 2004 Father Nitwit (eqemu@8ass.com) + + This thing uses code from freaku, so whatever license that comes under + is relavent, if you care. + + the rest of it is GPL, even though I hate the GPL. + + 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 +*/ + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef short SHORT; +typedef unsigned long DWORD; + +#include +#include +#include +//#include "EQWldData.h" +#include "awater.h" +#include "types.h" +#include "s3d.h" +#include "wld.h" + +#include "archive.hpp" +#include "pfs.hpp" + +#include "file_loader.hpp" +#include "zon.hpp" +#include "ter.hpp" + + +//this un-commented works with my map.cpp code correctly. +//with both of my inverts there commented. +#define INVERSEXY 1 + +#include +#include +using namespace std; + +//#define SPLIT_DEBUG + + +bool BuildWaterMap(const char *shortname, long DefaultRegionType); +void PrintBSP(BSP_Node *tree, long node_number); +long BSPFindRegion(BSP_Node *tree, long node_number, long region); +long BSPFindNode(BSP_Node *tree, long node_number, float x, float y, float z); +long BSPMarkRegion(BSP_Node *tree, long node_number, long region, int region_type); +long BSPCountNodes(BSP_Node *tree, long node_number); + + +int main(int argc, char *argv[]) { + + long DefaultRegionType=RegionTypeWater; + + if((argc < 2) || + (argc > 3) || + ((argc == 3)&&(strcasecmp(argv[1],"-dl")))) { + + printf("Usage: %s [-dl] (zone short name) to generate a water/lava map\n\n", argv[0]); + printf(" -dl = mark special regions as lava if not tagged in .WLD\n"); + printf("\nIf -dl is omitted, untagged special regions will be marked as water\n"); + return(1); + } + + char bufm[250]; + + if(argc==3) + DefaultRegionType=RegionTypeLava; + sprintf(bufm, "%s.wtr", argv[argc-1]); + if(!BuildWaterMap(argv[argc-1],DefaultRegionType)) + return(1); + + return(0); + +} + + + +bool BuildWaterMap(const char *shortname, long DefaultRegionType) { + +// char bufm[96]; + char bufs[96], bufw[96]; + long WaterOrLavaCount = 0; + + //TODO: clean up a LOT of memory that the freaku code does not + +// sprintf(bufm, "%s.map", shortname); + sprintf(bufs, "%s.s3d", shortname); + sprintf(bufw, "%s.wld", shortname); + + s3d_object s3d/*, s3d_obj, s3d_chr*/; + wld_object wld/*, wld2, wld_obj, wld_chr*/; + unsigned char *buf; + + FILE *s3df = fopen(bufs, "rb"); + if(s3df == NULL) { + // One day we may try EQG, but not today. + printf("Unable to open s3d file '%s'.\n", bufs); + return(false); + } + + printf("Loading %s...\n", bufs); + + + S3D_Init(&s3d, s3df); + S3D_GetFile(&s3d, bufw, &buf); + WLD_Init(&wld, buf, &s3d, 1); + + BSP_Node *tree = NULL; + struct_Data29 *data29; + + for(int i=0; itype == 0x21) { + printf("We have a type 0x21 fragment\n"); + tree = (BSP_Node *) wld.frags[i]->frag; + } + if(wld.frags[i]->type == 0x29) { + printf("We have a type 0x29 fragment. "); + data29 = (struct_Data29 *) wld.frags[i]->frag; + printf("It has %ld regions and is marked as type %d. ", + data29->region_count, data29->region_type); + switch(data29->region_type) { + case RegionTypeUnsupported: { printf("Unsupported\n"); break; } + case RegionTypeUntagged: { + printf("Untagged. We will set it to "); + if(DefaultRegionType==RegionTypeWater) { + printf("Water\n"); + } else printf("Lava\n"); + data29->region_type = DefaultRegionType; + WaterOrLavaCount++; + break; + } + case RegionTypeWater: { printf("Water\n"); WaterOrLavaCount++; break; } + case RegionTypeLava: { printf("Lava\n"); WaterOrLavaCount++; break; } + case RegionTypeZoneLine: { printf("Zoneline\n"); break; } + default: printf("UNKNOWN\n"); + } + } + + } + if(tree==NULL) { + printf("No BSP Tree. Bailing out\n"); + return(false); + } + if(WaterOrLavaCount==0) { + printf("No water or lava in this zone. Nothing for us to do.\n"); + return(false); + } + + + long BSPTreeSize = BSPCountNodes(tree, 1); + + printf("There are %ld nodes in the BSP tree\n", BSPTreeSize); + + // Now we mark each leaf in the BSP tree that is in a 'special area' with what type the area is + // Water, Lava, Zoneline etc + + for(int i=0; itype == 0x29) { + data29 = (struct_Data29 *) wld.frags[i]->frag; + for(long j=0; jregion_count; j++) { + BSPMarkRegion(tree, 1,data29->region_array[j]+1, data29->region_type); + } + } + } + + // Now write out the file + + char bufm[250]; + sprintf(bufm, "%s.wtr", shortname); + + FILE *WaterFile = fopen(bufm, "wb"); + if(WaterFile == NULL) { + printf("Failed to open %s for writing\n", bufm); + return(false); + } + const char *WFMagic = "EQEMUWATER"; + const long WFVersion = 1; + + if(fwrite(WFMagic, strlen(WFMagic), 1, WaterFile) != 1) { + printf("Error writing output file\n"); + fclose(WaterFile); + return(false); + } + if(fwrite(&WFVersion, sizeof(WFVersion), 1, WaterFile) != 1) { + printf("Error writing output file\n"); + fclose(WaterFile); + return(false); + } + + if(fwrite(&BSPTreeSize, sizeof(BSPTreeSize), 1, WaterFile) != 1) { + printf("Error writing output file\n"); + fclose(WaterFile); + return(false); + } + + if(fwrite(tree, sizeof(BSP_Node), BSPTreeSize, WaterFile) != BSPTreeSize) { + printf("Error writing output file\n"); + fclose(WaterFile); + return(false); + } + + fclose(WaterFile); + return(true); +} + + +void PrintBSP(BSP_Node *tree, long node_number) { + printf("Node %ld, Left=%ld, Right=%ld\n", + node_number-1, + tree[node_number-1].left, + tree[node_number-1].right); + printf("Normals are %4.3f, %4.3f, %4.3f, SplitD is %4.3f\n", + tree[node_number-1].normal[0], + tree[node_number-1].normal[1], + tree[node_number-1].normal[2], + tree[node_number-1].splitdistance); + + if(tree[node_number-1].left!=0) PrintBSP(tree, tree[node_number-1].left); + if(tree[node_number-1].right!=0) PrintBSP(tree, tree[node_number-1].right); + if((tree[node_number-1].left==0)&& + (tree[node_number-1].right==0)) { + printf("Region pointer is %ld\n", tree[node_number-1].region); + } + +} + +long BSPCountNodes(BSP_Node *tree, long node_number) { + + long NodesInRightBranch = 0, NodesInLeftBranch = 0; + + if((tree[node_number-1].left==0)&& + (tree[node_number-1].right==0)) return 1; + + if(tree[node_number-1].left!=0) NodesInLeftBranch = BSPCountNodes(tree, (tree[node_number-1].left)); + if(tree[node_number-1].right!=0) NodesInRightBranch = BSPCountNodes(tree, (tree[node_number-1].right)); + + return(NodesInRightBranch + NodesInLeftBranch + 1); + +} + + +long BSPFindRegion(BSP_Node *tree, long node_number, long region) { + //printf("Find Region %ld in node %ld\n", region, node_number); + if(node_number<1) { + printf("Something went wrong\n"); + exit(1); + } + if((tree[node_number-1].left==0)&& + (tree[node_number-1].right==0)) { + if(tree[node_number-1].region==region) return node_number; + } + + + long retnode ; + if(tree[node_number-1].left!=0) { + retnode = BSPFindRegion(tree, tree[node_number-1].left, region); + if(retnode != 0) return retnode ; + } + + if(tree[node_number-1].right!=0) { + return BSPFindRegion(tree, tree[node_number-1].right, region); + } + + return 0; + +} + +long BSPFindNode(BSP_Node *tree, long node_number, float x, float y, float z) { + + float distance; + + + printf("BSP Find Node, currently in Node %ld\n", node_number); + // Are we at a leaf + + if((tree[node_number-1].left==0)&& + (tree[node_number-1].right==0)) { + return tree[node_number-1].region; + } + + // No, so determine which side of the split plane we are on + // + + distance = (x * tree[node_number-1].normal[0]) + + (y * tree[node_number-1].normal[1]) + + (z * tree[node_number-1].normal[2]) + + tree[node_number-1].splitdistance; + printf("Distance is %4.3f\n", distance); + // Guess which side to go down + if(distance == 0.0f) { + printf("Distance is 0, don't know what to do!\n"); + exit(1); + } + if(distance >0.0f) { + if(tree[node_number-1].left==0) { + printf("Pos and no left node, abort!\n"); + exit(1); + } + return BSPFindNode(tree, tree[node_number-1].left, + x, y, z); + } + + if(tree[node_number-1].right==0) { + printf("Neg and no right node, abort!\n"); + exit(1); + } + + return BSPFindNode(tree, tree[node_number-1].right, + x, y, z); + +} + + +long BSPMarkRegion(BSP_Node *tree, long node_number, long region, int region_type) { + //printf("Find Region %ld in node %ld\n", region, node_number); + if(node_number<1) { + printf("Something went wrong\n"); + exit(1); + } + if((tree[node_number-1].left==0)&& + (tree[node_number-1].right==0)) { + if(tree[node_number-1].region==region) { + tree[node_number-1].special=region_type; + return node_number; + } + } + + + long retnode ; + if(tree[node_number-1].left!=0) { + retnode = BSPMarkRegion(tree, tree[node_number-1].left, region, region_type); + if(retnode != 0) return retnode ; + } + + if(tree[node_number-1].right!=0) { + return BSPMarkRegion(tree, tree[node_number-1].right, region, region_type); + } + + return 0; + +} + + + + diff --git a/utils/deprecated/azone/awater.h b/utils/deprecated/azone/awater.h new file mode 100644 index 000000000..0910a3a85 --- /dev/null +++ b/utils/deprecated/azone/awater.h @@ -0,0 +1,10 @@ +typedef enum { + RegionTypeUnsupported = -2, + RegionTypeUntagged = -1, + RegionTypeNormal = 0, + RegionTypeWater = 1, + RegionTypeLava = 2, + RegionTypeZoneLine = 3 +} WaterRegionType; + + diff --git a/utils/deprecated/azone/azone.cpp b/utils/deprecated/azone/azone.cpp new file mode 100644 index 000000000..9fc9f2466 --- /dev/null +++ b/utils/deprecated/azone/azone.cpp @@ -0,0 +1,1348 @@ +/* + + Father Nitwit's Zone to map conversion program. + Copyright (C) 2004 Father Nitwit (eqemu@8ass.com) + + This thing uses code from freaku, so whatever license that comes under + is relavent, if you care. + + the rest of it is GPL, even though I hate the GPL. + + 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 +*/ + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef short SHORT; +typedef unsigned long DWORD; + +#include +#ifdef WIN32 //vc++ chokes here without this +#include +#else +#include +#endif +#include +//#include "EQWldData.h" +#include "azone.h" +#include "types.h" +#include "s3d.h" +#include "wld.h" + +#include "archive.hpp" +#include "pfs.hpp" + +#include "file_loader.hpp" +#include "zon.hpp" +#include "ter.hpp" + +//TODO: I am trimming faces for which all vertices are above MAX_Z +// but I am not taking out the vertices to go with them. + + +//this un-commented works with my map.cpp code correctly. +//with both of my inverts there commented. +#define INVERSEXY 1 + +#include +#include +using namespace std; + +//#define SPLIT_DEBUG + +#ifdef WIN32 +#pragma comment( lib, "wsock32.lib" ) +#endif + +#ifndef MAX_QUADRENT_FACES +#ifndef MIN_QUADRENT_SIZE +#error Umm... your asking for trouble by turning off both stopping criteria +#endif +#endif + +int main(int argc, char *argv[]) { + +// if(argc != 3) { +// printf("Usage: %s (zone.wld) (out.map)\n", argv[0]); + if(argc != 2) { + printf("Usage: %s (zone short name)\n", argv[0]); + return(1); + } + + char bufm[250]; + + sprintf(bufm, "%s.map", argv[1]); + + QTBuilder QT; + + if(!QT.build(argv[1])) + return(1); + + if(!QT.writeMap(bufm)) + return(1); + + return(0); +} + +QTBuilder::QTBuilder() { + _root = NULL; + +// vertexCount = 0; + faceCount = 0; +// vertexBlock = NULL; + faceBlock = NULL; + +#ifdef COUNT_MACTHES + gEasyMatches = 0; + gEasyExcludes = 0; + gHardMatches = 0; + gHardExcludes = 0; +#endif +} + +QTBuilder::~QTBuilder() { + if(_root != NULL) + delete _root; + _root = NULL; +/* if(vertexBlock != NULL) + delete vertexBlock; + vertexBlock = NULL;*/ + if(faceBlock != NULL) + delete faceBlock; + faceBlock = NULL; +} + +bool QTBuilder::build(const char *shortname) { +// char bufm[96]; + char bufs[96], bufw[96]; + + //TODO: clean up a LOT of memory that the freaku code does not + +// sprintf(bufm, "%s.map", shortname); + sprintf(bufs, "%s.s3d", shortname); + sprintf(bufw, "%s.wld", shortname); + + ZoneMesh meshi; + ZoneMesh *mesh = &meshi; + s3d_object s3d/*, s3d_obj, s3d_chr*/; + wld_object wld/*, wld2, wld_obj, wld_chr*/; + unsigned char *buf; + + FILE *s3df = fopen(bufs, "rb"); + if(s3df == NULL) { + //try EQG. + if(!build_eqg(shortname)) { + printf("Unable to open s3d file '%s'.\n", bufs); + return(false); + } else { + return(true); + } + } + + printf("Loading %s...\n", bufs); + + + S3D_Init(&s3d, s3df); + S3D_GetFile(&s3d, bufw, &buf); + WLD_Init(&wld, buf, &s3d, 1); + WLD_GetZoneMesh(&wld, &meshi); + + long i; + VERTEX v1, v2, v3; + for(i = 0; i < mesh->polygonCount; ++i) { +#ifdef INVERSEXY + v1.y = mesh->verti[mesh->poly[i]->v1]->x; + v1.x = mesh->verti[mesh->poly[i]->v1]->y; +#else + v1.x = mesh->verti[mesh->poly[i]->v1]->x; + v1.y = mesh->verti[mesh->poly[i]->v1]->y; +#endif + v1.z = mesh->verti[mesh->poly[i]->v1]->z; +#ifdef INVERSEXY + v2.y = mesh->verti[mesh->poly[i]->v2]->x; + v2.x = mesh->verti[mesh->poly[i]->v2]->y; +#else + v2.x = mesh->verti[mesh->poly[i]->v2]->x; + v2.y = mesh->verti[mesh->poly[i]->v2]->y; +#endif + v2.z = mesh->verti[mesh->poly[i]->v2]->z; +#ifdef INVERSEXY + v3.y = mesh->verti[mesh->poly[i]->v3]->x; + v3.x = mesh->verti[mesh->poly[i]->v3]->y; +#else + v3.x = mesh->verti[mesh->poly[i]->v3]->x; + v3.y = mesh->verti[mesh->poly[i]->v3]->y; +#endif + v3.z = mesh->verti[mesh->poly[i]->v3]->z; + + AddFace(v1, v2, v3); + } + + printf(" There are %lu vertices and %u faces.\n", _FaceList.size()*3, _FaceList.size()); + + unsigned long r; + +// vertexCount = _VertexList.size(); + faceCount = _FaceList.size(); + + +/* vertexBlock = new VERTEX[vertexCount]; + //im not going to assume I know vectors are stored in contiguous blocks + for(r = 0; r < vertexCount; r++) { + vertexBlock[r] = _VertexList[r]; + }*/ + + faceBlock = new FACE[faceCount]; + //im not going to assume I know vectors are stored in contiguous blocks + for(r = 0; r < faceCount; r++) { + faceBlock[r] = _FaceList[r]; + } + + //build quad tree... prolly much slower than it needs to be. + float minx, miny, maxx, maxy; + minx = 1e12; + miny = 1e12; + maxx = -1e12; + maxy = -1e12; + + //find our limits. + for(r = 0; r < faceCount; r++) { + //a bit of lazyness going on here... + { + VERTEX &v = faceBlock[r].a; + if(v.x > maxx) + maxx = v.x; + if(v.x < minx) + minx = v.x; + if(v.y > maxy) + maxy = v.y; + if(v.y < miny) + miny = v.y; + } + { + VERTEX &v = faceBlock[r].b; + if(v.x > maxx) + maxx = v.x; + if(v.x < minx) + minx = v.x; + if(v.y > maxy) + maxy = v.y; + if(v.y < miny) + miny = v.y; + } + { + VERTEX &v = faceBlock[r].c; + if(v.x > maxx) + maxx = v.x; + if(v.x < minx) + minx = v.x; + if(v.y > maxy) + maxy = v.y; + if(v.y < miny) + miny = v.y; + } + } + + printf(" Bounding box: %.2f < x < %.2f, %.2f < y < %.2f\n", minx, maxx, miny, maxy); + printf("World file loaded.\n"); + + printf("Building quadtree.\n"); + + _root = new QTNode(this, minx, maxx, miny, maxy); + if(_root == NULL) { + printf("Unable to allocate new QTNode.\n"); + return(false); + } + + //build our initial set of faces... all of them: + FACE *faceptr = faceBlock; + _root->faces.resize(faceCount); + for(r = 0; r < faceCount; r++) { + _root->faces[r].face = faceptr; + _root->faces[r].index = r; + faceptr++; + } +/* _root->faces.resize(faceCount); + for(r = 0; r < faceCount; r++) { + _root->faces[r].face = &faceBlock[r]; //this is kinda bad, we dont own this + //memory, so we shouldent be taking addrs... + _root->faces[r].index = r; + }*/ + + _root->divideYourself(0); + + printf("Done building quad tree...\n"); + +#ifdef COUNT_MACTHES + fprintf(stderr, "Match counters: %lu easy in, %lu easy out, %lu hard in, %lu hard out.\n", gEasyMatches, gEasyExcludes, gHardMatches, gHardExcludes); +#endif + + + return(true); +} + + +bool QTBuilder::build_eqg(const char *shortname) { +// char bufm[96]; + char bufs[96]; + Archive *archive; + FileLoader *fileloader; + Zone_Model *zm; + + sprintf(bufs, "%s.eqg", shortname); + + archive = new PFSLoader(); + FILE *fff = fopen(bufs, "rb"); + if(fff == NULL) { + printf("Failed to open '%s'\n", bufs); + return(false); + } + if(archive->Open(fff) == 0) { + printf("Unable to open eqg file '%s'.\n", bufs); + return(false); + } + + fileloader = new ZonLoader(); + if(fileloader->Open(NULL, (char *) shortname, archive) == 0) { + printf("Error reading ZON from %s\n", bufs); + return(false); + } + zm = fileloader->model_data.zone_model; + + long i; + VERTEX v1, v2, v3; + for(i = 0; i < zm->poly_count; ++i) { +#ifdef INVERSEXY + v1.y = zm->verts[zm->polys[i]->v1]->x; + v1.x = zm->verts[zm->polys[i]->v1]->y; +#else + v1.x = zm->verts[zm->polys[i]->v1]->x; + v1.y = zm->verts[zm->polys[i]->v1]->y; +#endif + v1.z = zm->verts[zm->polys[i]->v1]->z; +#ifdef INVERSEXY + v2.y = zm->verts[zm->polys[i]->v2]->x; + v2.x = zm->verts[zm->polys[i]->v2]->y; +#else + v2.x = zm->verts[zm->polys[i]->v2]->x; + v2.y = zm->verts[zm->polys[i]->v2]->y; +#endif + v2.z = zm->verts[zm->polys[i]->v2]->z; +#ifdef INVERSEXY + v3.y = zm->verts[zm->polys[i]->v3]->x; + v3.x = zm->verts[zm->polys[i]->v3]->y; +#else + v3.x = zm->verts[zm->polys[i]->v3]->x; + v3.y = zm->verts[zm->polys[i]->v3]->y; +#endif + v3.z = zm->verts[zm->polys[i]->v3]->z; + + AddFace(v1, v2, v3); + } + + printf(" There are %lu vertices and %u faces.\n", _FaceList.size()*3, _FaceList.size()); + + unsigned long r; + +// vertexCount = _VertexList.size(); + faceCount = _FaceList.size(); + + +/* vertexBlock = new VERTEX[vertexCount]; + //im not going to assume I know vectors are stored in contiguous blocks + for(r = 0; r < vertexCount; r++) { + vertexBlock[r] = _VertexList[r]; + }*/ + + faceBlock = new FACE[faceCount]; + //im not going to assume I know vectors are stored in contiguous blocks + for(r = 0; r < faceCount; r++) { + faceBlock[r] = _FaceList[r]; + } + + //build quad tree... prolly much slower than it needs to be. + float minx, miny, maxx, maxy; + minx = 1e12; + miny = 1e12; + maxx = -1e12; + maxy = -1e12; + + //find our limits. + for(r = 0; r < faceCount; r++) { + //a bit of lazyness going on here... + { + VERTEX &v = faceBlock[r].a; + if(v.x > maxx) + maxx = v.x; + if(v.x < minx) + minx = v.x; + if(v.y > maxy) + maxy = v.y; + if(v.y < miny) + miny = v.y; + } + { + VERTEX &v = faceBlock[r].b; + if(v.x > maxx) + maxx = v.x; + if(v.x < minx) + minx = v.x; + if(v.y > maxy) + maxy = v.y; + if(v.y < miny) + miny = v.y; + } + { + VERTEX &v = faceBlock[r].c; + if(v.x > maxx) + maxx = v.x; + if(v.x < minx) + minx = v.x; + if(v.y > maxy) + maxy = v.y; + if(v.y < miny) + miny = v.y; + } + } + + printf(" Bounding box: %.2f < x < %.2f, %.2f < y < %.2f\n", minx, maxx, miny, maxy); + printf("World file loaded.\n"); + + printf("Building quadtree.\n"); + + _root = new QTNode(this, minx, maxx, miny, maxy); + if(_root == NULL) { + printf("Unable to allocate new QTNode.\n"); + return(false); + } + + //build our initial set of faces... all of them: + FACE *faceptr = faceBlock; + _root->faces.resize(faceCount); + for(r = 0; r < faceCount; r++) { + _root->faces[r].face = faceptr; + _root->faces[r].index = r; + faceptr++; + } +/* _root->faces.resize(faceCount); + for(r = 0; r < faceCount; r++) { + _root->faces[r].face = &faceBlock[r]; //this is kinda bad, we dont own this + //memory, so we shouldent be taking addrs... + _root->faces[r].index = r; + }*/ + + _root->divideYourself(0); + + printf("Done building quad tree...\n"); + +#ifdef COUNT_MACTHES + fprintf(stderr, "Match counters: %lu easy in, %lu easy out, %lu hard in, %lu hard out.\n", gEasyMatches, gEasyExcludes, gHardMatches, gHardExcludes); +#endif + + + return(true); +} + +bool QTBuilder::writeMap(const char *file) { + if(_root == NULL) + return(false); + + printf("Writting map file.\n"); + + //im too lazy to give reasons for errors + FILE *out = fopen(file, "wb"); + if(out == NULL) { + printf("Unable to open output file '%s'.\n", file); + return(1); + } + + mapHeader head; + head.version = MAP_VERSION; +// head.vertex_count = vertexCount; + head.face_count = faceCount; + head.node_count = _root->countNodes(); + head.facelist_count = _root->countFacelists(); + + if(fwrite(&head, sizeof(head), 1, out) != 1) { + printf("Error writting map file header.\n"); + fclose(out); + return(1); + + } + + printf(" Map header: %lu faces, %u nodes, %lu facelists\n", head.face_count, head.node_count, head.facelist_count); + + + //write vertexBlock +/* if(fwrite(vertexBlock, sizeof(VERTEX), vertexCount, out) != vertexCount) { + printf("Error writting map file vertices.\n"); + fclose(out); + return(1); + + }*/ + + //write faceBlock + if(fwrite(faceBlock, sizeof(FACE), faceCount, out) != faceCount) { + printf("Error writting map file faces.\n"); + fclose(out); + return(1); + } + + //make our node blocks to write out... + nodeHeader *nodes = new nodeHeader[head.node_count]; + unsigned long *facelist = new unsigned long[head.facelist_count]; + if(nodes == NULL || facelist == NULL) { + printf("Error allocating temporary memory for output.\n"); + fclose(out); + return(1); //no memory + } + + unsigned long hindex = 0; + unsigned long findex = 0; + _root->fillBlocks(nodes, facelist, hindex, findex); + + if(fwrite(nodes, sizeof(nodeHeader), head.node_count, out) != head.node_count) { + printf("Error writting map file nodes.\n"); + fclose(out); + return(1); + } + if(fwrite(facelist, sizeof(unsigned long), head.facelist_count, out) != head.facelist_count) { + printf("Error writting map file face list.\n"); + fclose(out); + return(1); + } + +/* if(!_root->writeFile(out)) { + printf("Error writting map file quadtree nodes.\n"); + fclose(out); + return(1); + }*/ + + fclose(out); + delete[] nodes; + delete[] facelist; + + printf("Done writting map.\n"); + + return(0); +} + + +QTNode::QTNode(QTBuilder *b, float Tminx, float Tmaxx, float Tminy, float Tmaxy) { + node1 = NULL; + node2 = NULL; + node3 = NULL; + node4 = NULL; + minx = Tminx; + maxx = Tmaxx; + miny = Tminy; + maxy = Tmaxy; + final = false; + buildVertexes(); + + builder = b; +} + +QTNode::~QTNode() { + clearNodes(); +} + +void QTNode::clearNodes() { + if(node1 != NULL) + delete node1; + if(node2 != NULL) + delete node2; + if(node3 != NULL) + delete node3; + if(node4 != NULL) + delete node4; + node1 = NULL; + node2 = NULL; + node3 = NULL; + node4 = NULL; +} + +//assumes that both supplied arrays are big enough per countNodes/Facelists +void QTNode::fillBlocks(nodeHeader *heads, unsigned long *flist, unsigned long &hindex, unsigned long &findex) { + nodeHeader *head = &heads[hindex]; + hindex++; + + head->minx = minx; + head->maxx = maxx; + head->miny = miny; + head->maxy = maxy; + head->flags = 0; +//printf("Node %u: (%.2f -> %.2f, %.2f -> %.2f)\n", hindex-1, head->minx, head->maxx, head->miny, head->maxy); + if(final) { + head->flags |= nodeFinal; + head->faces.count = faces.size(); + head->faces.offset = findex; +//printf(" Final node with %u faces, list offset %lu.\n", head->faces.count, head->faces.offset); + unsigned long r; + for(r = 0; r < head->faces.count; r++) { + flist[findex] = faces[r].index; + findex++; + } +// findex += head->faces.count; + } else { + //branch node. + head->flags = 0; + + if(node1 != NULL) { + head->nodes[0] = hindex; + node1->fillBlocks(heads, flist, hindex, findex); + } else { + head->nodes[0] = NODE_NONE; + } + if(node2 != NULL) { + head->nodes[1] = hindex; + node2->fillBlocks(heads, flist, hindex, findex); + } else { + head->nodes[1] = NODE_NONE; + } + if(node3 != NULL) { + head->nodes[2] = hindex; + node3->fillBlocks(heads, flist, hindex, findex); + } else { + head->nodes[2] = NODE_NONE; + } + if(node4 != NULL) { + head->nodes[3] = hindex; + node4->fillBlocks(heads, flist, hindex, findex); + } else { + head->nodes[3] = NODE_NONE; + } + } +} + +unsigned long QTNode::countNodes() const { + unsigned long c = 1; + if(node1 != NULL) + c += node1->countNodes(); + if(node2 != NULL) + c += node2->countNodes(); + if(node3 != NULL) + c += node3->countNodes(); + if(node4 != NULL) + c += node4->countNodes(); + return(c); +} + +unsigned long QTNode::countFacelists() const { + unsigned long c = final? faces.size() : 0; + if(node1 != NULL) + c += node1->countFacelists(); + if(node2 != NULL) + c += node2->countFacelists(); + if(node3 != NULL) + c += node3->countFacelists(); + if(node4 != NULL) + c += node4->countFacelists(); + return(c); +} + +/* +Map Format: + +1x mapHeader (head) +head.face_count x FACE +head.node_count x nodeHeader +head.facelist_count x unsigned long (indexes into face array) + + +*/ +/*bool QTNode::writeFile(FILE *out) { + static nodeHeader head; + head.minx = minx; + head.maxx = maxx; + head.miny = miny; + head.maxy = maxy; + + if(final) { + head.faces.count = faces.size(); + if(fwrite(&head, sizeof(nodeHeader), 1, out) != 1) { + return(false); + } + + unsigned long *fblock = new unsigned long[head.faces.count]; + if(fblock == NULL) + return(false); + unsigned long r; + for(r = 0; r < head.faces.count; r++) { + fblock[r] = faces[r].index; + } + if(fwrite(fblock, sizeof(unsigned long), head.faces.count, out) != head.faces.count) { + return(false); + } + + delete[] fblock; + + return(true); + } + + //not final, write mask and tell children to write themselves... + + head.faces.count = 0; + + if(fwrite(&head, sizeof(nodeHeader), 1, out) != 1) { + return(false); + } + if(fwrite(&mask, 1, 1, out) != 1) { + return(false); + } + + if(node1 != NULL) { + if(!node1->writeFile(out)) + return(false); + } + + if(node2 != NULL) { + if(!node2->writeFile(out)) + return(false); + } + + if(node3 != NULL) { + if(!node3->writeFile(out)) + return(false); + } + + if(node4 != NULL) { + if(!node4->writeFile(out)) + return(false); + } + + + return(true); +}*/ + +void QTNode::divideYourself(int depth) { +// printf("Dividing in box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d faces.\n", +// minx, maxx, miny, maxy, depth, faces.size()); + + unsigned long cc; + cc = faces.size(); +#ifdef MAX_QUADRENT_FACES + if(cc <= MAX_QUADRENT_FACES) { +#ifdef SPLIT_DEBUG +printf("Stopping (facecount) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d faces.\n", + minx, maxx, miny, maxy, depth, cc); +#endif + final = true; + return; + } +#endif + +#ifdef MIN_QUADRENT_SIZE + if((maxx - minx) < MIN_QUADRENT_SIZE || (maxy - miny) < MIN_QUADRENT_SIZE) { +#ifdef SPLIT_DEBUG +printf("Stopping on box (size) (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d faces.\n", + minx, maxx, miny, maxy, depth, cc); +#endif + final = true; + return; + } +#endif + + doSplit(); + + //get counts on our split + float c1, c2, c3, c4; + c1 = node1? node1->faces.size() : 0; + c2 = node2? node2->faces.size() : 0; + c3 = node3? node3->faces.size() : 0; + c4 = node4? node4->faces.size() : 0; + +#ifdef MIN_QUADRENT_GAIN + int miss = 0; + float gain1 = 1.0 - c1 / cc; + float gain2 = 1.0 - c2 / cc; + float gain3 = 1.0 - c3 / cc; + float gain4 = 1.0 - c4 / cc; + + //see how many missed the gain mark + if(gain1 < MIN_QUADRENT_GAIN) + miss++; + if(gain2 < MIN_QUADRENT_GAIN) + miss++; + if(gain3 < MIN_QUADRENT_GAIN) + miss++; + if(gain4 < MIN_QUADRENT_GAIN) + miss++; + + if(miss > MAX_QUADRENT_MISSES) { +#ifdef SPLIT_DEBUG +printf("Stopping (gain) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d faces.\n", + minx, maxx, miny, maxy, depth, cc); +#endif + final = true; + return; + } +#endif + + + //if all faces pass through all quadrents, then we are done + //partially obsoleted by gain test. + if(c1 == c2 && c1 == c3 && c1 == c4) { +#ifdef SPLIT_DEBUG +printf("Stopping (empty) on box (%.2f -> %.2f, %.2f -> %.2f) at depth %d with %d faces.\n", + minx, maxx, miny, maxy, depth, cc); +#endif + final = true; + return; + } + + //there are prolly some more intelligent stopping criteria... + + depth++; + + if(node1 != NULL) + node1->divideYourself(depth); + if(node2 != NULL) + node2->divideYourself(depth); + if(node3 != NULL) + node3->divideYourself(depth); + if(node4 != NULL) + node4->divideYourself(depth); + + +} + +void QTNode::buildVertexes() { + + v[0].x = v[1].x = v[2].x = v[3].x = minx; + v[4].x = v[5].x = v[6].x = v[7].x = maxx; + + v[0].y = v[1].y = v[4].y = v[5].y = miny; + v[2].y = v[3].y = v[6].y = v[7].y = maxy; + + v[0].z = v[3].z = v[4].z = v[7].z = -999999; + v[1].z = v[2].z = v[5].z = v[6].z = 9999999; +} + + +static const GVector gNormals[6] = { + GVector(-1.0, 0.0, 0.0), + GVector(0.0, 1.0, 0.0), + GVector(1.0, 0.0, 0.0), + GVector(0.0, -1.0, 0.0), + GVector(0.0, 0.0, 1.0), + GVector(0.0, 0.0, -1.0), +}; + +static const unsigned short gIntFaces[6][4] = +{ + {0, 1, 2, 3}, + {3, 2, 6, 7}, + {7, 6, 5, 4}, + {4, 5, 1, 0}, + {5, 6, 2, 1}, + {7, 4, 0, 3} +}; + + +//stolen in haste from my fear pathing program, and untested in here... +bool edges_cross(GPoint *pt1, GPoint *pt2, const VERTEX *pt3, const VERTEX *pt4) { +//I love macros +#define IntersectDenom(p1, p2, p3, p4) \ +((p4->y - p3->y)*(p2->x - p1->x) - (p4->x - p3->x)*(p2->y - p1->y)) +#define IntersectNumerX(p1, p2, p3, p4) \ +((p4->x - p3->x)*(p1->y - p3->y) - (p4->y - p3->y)*(p1->x - p3->x)) +#define IntersectNumerY(p1, p2, p3, p4) \ +((p2->x - p1->x)*(p1->y - p3->y) - (p2->y - p1->y)*(p1->x - p3->x)) + +#define IntersectX(p1, p2, p3, p4, denom) \ +(p1->x + IntersectNumerX(p1, p2, p3, p4)*(p2->x - p1->x)/denom) +#define IntersectY(p1, p2, p3, p4, denom) \ +(p1->y + IntersectNumerX(p1, p2, p3, p4)*(p2->y - p1->y)/denom) + +#define CheckEqualXY(p1, p2) \ + (p1->x == p2->x && p1->y == p2->y) + +#define CoordOnLine(p1, p2, coord, dim) \ +(p1->dim > p2->dim? (coord >= p2->dim && coord <= p1->dim) : (coord >= p1->dim && coord <= p2->dim)) + +#define IntersectZfromX(p1, p2, inter) \ + (p2->x > p1->x? \ + (p1->z + ((inter - p1->x)/(p2->x - p1->x) * (p2->z - p1->z))) \ + :(p2->z + ((inter - p2->x)/(p1->x - p2->x) * (p1->z - p2->z)))) + + float denom = IntersectDenom(pt1, pt2, pt3, pt4); + if(denom != 0) { + + //the lines intersect, check segments now + float xinter = IntersectX(pt1, pt2, pt3, pt4, denom); + float yinter = IntersectY(pt1, pt2, pt3, pt4, denom); + + //now see if this point is on both segments + if( CoordOnLine(pt1, pt2, xinter, x) + && CoordOnLine(pt1, pt2, yinter, y) + && CoordOnLine(pt3, pt4, xinter, x) + && CoordOnLine(pt3, pt4, yinter, y) ){ +// printf("Line (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f) d=%.3f\n", pt1->x, pt1->y, pt1->z, pt2->x, pt2->y, pt2->z, pt1->Dist2(pt2)); +// printf("Hits (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f) d=%.3f\n", pt3->x, pt3->y, pt3->z, pt4->x, pt4->y, pt4->z, pt3->Dist2(pt4)); +// printf("At (%.3f, %.3f), which IS on both segments.\n", xinter, yinter); + + return(true); + } +// printf("At (%.3f, %.3f), which is not on both segments.\n", xinter, yinter); + } + return(false); +} + +#define MAX(x,y) ((x) MIN(p1.y,p2.y)) { + if (py <= MAX(p1.y,p2.y)) { + if (px <= MAX(p1.x,p2.x)) { + if (p1.y != p2.y) { + xinters = (py-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x; + if (p1.x == p2.x || px <= xinters) + counter++; + } + } + } + } + p1 = p2; + } + + if (counter % 2 == 0) + return(false); + else + return(true); +} + +//quick function which got too messy in the loop below. +bool QTBuilder::FaceInNode(const QTNode *q, const FACE *f) { + const VERTEX *v1 = &f->a; + const VERTEX *v2 = &f->b; + const VERTEX *v3 = &f->c; + +#ifdef COUNT_MACTHES + gEasyMatches++; +#endif + + //Easy matches, points are within the quadrant. + if( ! ( v1->x <= q->minx || v1->x > q->maxx + || v1->y <= q->miny || v1->y > q->maxy ) ) { + return(true); + } + if( ! ( v2->x <= q->minx || v2->x > q->maxx + || v2->y <= q->miny || v2->y > q->maxy ) ) { + return(true); + } + if( ! ( v3->x <= q->minx || v3->x > q->maxx + || v3->y <= q->miny || v3->y > q->maxy ) ) { + return(true); + } + +#ifdef COUNT_MACTHES + gEasyMatches--; + gEasyExcludes++; +#endif + + //make sure it is even possible to insersect: + if( v1->x < q->minx && v2->x < q->minx && v3->x < q->minx ) + return(false); + if( v1->x > q->maxx && v2->x > q->maxx && v3->x > q->maxx ) + return(false); + if( v1->y < q->miny && v2->y < q->miny && v3->y < q->miny ) + return(false); + if( v1->y > q->maxy && v2->y > q->maxy && v3->y > q->maxy ) + return(false); + + +#ifdef COUNT_MACTHES + gEasyExcludes--; +#endif + +#ifdef COUNT_MACTHES + gHardMatches++; +#endif +// return(true); + + //harder: no points are in the cube + + //4 points of this node + GPoint pt1(q->minx, q->miny, 0), + pt2(q->minx, q->maxy, 0), + pt3(q->maxx, q->miny, 0), + pt4(q->maxx, q->maxy, 0); + + /* + //box lines: + pt1, pt2 + pt3, pt4 + pt1, pt3 + pt2, pt4 + + //tri lines + v1, v2 + v1, v3 + v2, v3 + */ + +#define CheckIntersect(p1, p2, p3, p4) \ +(((p4->y - p3->y)*(p2.x - p1.x) - (p4->x - p3->x)*(p2.y - p1.y)) != 0) + + int finaltest = + (edges_cross(&pt1, &pt2, v1, v2) + || edges_cross(&pt1, &pt2, v1, v3) + || edges_cross(&pt1, &pt2, v2, v3) + || edges_cross(&pt3, &pt4, v1, v2) + || edges_cross(&pt3, &pt4, v1, v3) + || edges_cross(&pt3, &pt4, v2, v3) + || edges_cross(&pt1, &pt3, v1, v2) + || edges_cross(&pt1, &pt3, v1, v3) + || edges_cross(&pt1, &pt3, v2, v3) + || edges_cross(&pt2, &pt4, v1, v2) + || edges_cross(&pt2, &pt4, v1, v3) + || edges_cross(&pt2, &pt4, v2, v3)); + + if(finaltest) return finaltest; + + VERTEX Triangle[3]; + Triangle[0]=*v1; Triangle[1]=*v2; Triangle[2] = *v3; + finaltest = PointInTriangle(Triangle, q->minx, q->miny) || + PointInTriangle(Triangle, q->minx, q->maxy) || + PointInTriangle(Triangle, q->maxx, q->maxy) || + PointInTriangle(Triangle, q->maxx, q->miny); + + return finaltest; + + +/* + + + return(false); + + + POLYGON it; + it.c[0] = *v1; + it.c[1] = *v2; + it.c[2] = *v3; + it.count = 3; + + GVector plane; + GPoint pt1, pt2, pt3, pt4; + //loop through each face of the cube. + //chop off whatever is above that plane. + int i; + for(i = 0; i < 6; i++) { + //q->v[gIntFaces[i][0]], q->v[gIntFaces[i][1]], q->v[gIntFaces[i][2]], q->v[gIntFaces[i][3]], gNormals[i] + + //get the 4 points on this face of the cube. + pt1 = q->v[gIntFaces[i][0]]; + pt2 = q->v[gIntFaces[i][1]]; + pt3 = q->v[gIntFaces[i][2]]; + pt4 = q->v[gIntFaces[i][3]]; + + //gNormals[i] + + //this is the plane containing this face of the cube. + plane = (pt1 - pt3).cross(pt2 - pt3); + plane.normalize(); + plane.W = -(plane.dot3(pt4)); + + int res = ClipPolygon(&it, &plane); + if(res == -1) { + printf("Invalid plane for intersection test.\n"); + return(false); + } + if(res == 0) { + break; //nothing left in the polygon. + } + } + +#ifdef COUNT_MACTHES + if(it.count > 1) { + gHardMatches++; + } else { + gHardExcludes++; + } +#endif + + //if we have anything left, it intersects us. + return(it.count > 1); +*/ +} + + + +void QTNode::doSplit() { + + + //find midpoints... + float midx = minx + (maxx - minx) / 2.0; + float midy = miny + (maxy - miny) / 2.0; + + //ordering following definitions in map.h + node1 = new QTNode(builder, midx, maxx, midy, maxy); + node2 = new QTNode(builder, minx, midx, midy, maxy); + node3 = new QTNode(builder, minx, midx, miny, midy); + node4 = new QTNode(builder, midx, maxx, miny, midy); + if(node1 == NULL || node2 == NULL || node3 == NULL || node4 == NULL) { + printf("Error: unable to allocate new QTNode, giving up.\n"); + return; + } + + unsigned long r,l; + l = faces.size(); + for(r = 0; r < l; r++) { + FaceRecord &cur = faces[r]; + if(builder->FaceInNode(node1, cur.face)) + node1->faces.push_back(cur); + if(builder->FaceInNode(node2, cur.face)) + node2->faces.push_back(cur); + if(builder->FaceInNode(node3, cur.face)) + node3->faces.push_back(cur); + if(builder->FaceInNode(node4, cur.face)) + node4->faces.push_back(cur); + } + + //clean up empty sets. + if(node1->faces.size() == 0) { + delete node1; + node1 = NULL; + } + if(node2->faces.size() == 0) { + delete node2; + node2 = NULL; + } + if(node3->faces.size() == 0) { + delete node3; + node3 = NULL; + } + if(node4->faces.size() == 0) { + delete node4; + node4 = NULL; + } + +} + +void QTBuilder::AddFace(VERTEX &v1, VERTEX &v2, VERTEX &v3) { + FACE f; + +#ifdef MAX_Z + if(v1.z > MAX_Z && v2.z > MAX_Z && v3.z > MAX_Z) + return; +#endif + + //this dosent work, trying new code below.. + /* + //Make points, so I can use my functions, im lazy (: + GPoint v1(v1); + GPoint v2(v2); + GPoint v3(v3); + GVector res = (pt1 - pt3).cross(pt2 - pt3); + float len = res.length(); +#ifdef MIN_POLYGON_AREA + if(len < MIN_POLYGON_AREA) //skip small polygons.. + return; +#endif + f.nx = res.x / len; + f.ny = res.y / len; + f.nz = res.z / len; + f.nd = -( res.dot3(pt1) ); + */ + + //this still might not work + f.nx = (v2.y - v1.y)*(v3.z - v1.z) - (v2.z - v1.z)*(v3.y - v1.y); + f.ny = (v2.z - v1.z)*(v3.x - v1.x) - (v2.x - v1.x)*(v3.z - v1.z); + f.nz = (v2.x - v1.x)*(v3.y - v1.y) - (v2.y - v1.y)*(v3.x - v1.x); + NormalizeN(&f); + f.nd = - f.nx * v1.x - f.ny * v1.y - f.nz * v1.z; + + f.a = v1; + f.b = v2; + f.c = v3; + /* f.a = _VertexList.size(); + _VertexList.push_back(v1); + f.b = _VertexList.size(); + _VertexList.push_back(v2); + f.c = _VertexList.size(); + _VertexList.push_back(v3);*/ + + _FaceList.push_back(f); +} + + +GPoint::GPoint() { + x = 0; + y = 0; + z = 0; +} + +GPoint::GPoint(VERTEX &v) { + x = v.x; + y = v.y; + z = v.z; +} + +GPoint::GPoint(float nx, float ny, float nz) { + x = nx; + y = ny; + z = nz; +} + +//dot of x,y,z +float GPoint::dot3(const GPoint &them) const { + return((x * them.x) + (y * them.y) + + (z * them.z)); +} + +//cross product +GPoint GPoint::cross(const GPoint &them) const { + return(GPoint(y * them.z - z * them.y, + z * them.x - x * them.z, + x * them.y - y * them.x)); +} + +GPoint operator-(const GPoint &v1, const GPoint &v2) { + return(GPoint(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z)); +} + + +GVector::GVector() : GPoint() { + W = 0; +} + +GVector::GVector(const GPoint &them) : GPoint(them) { + W = 1.0f; +} + +GVector::GVector(float x, float y, float z, float w) : GPoint(x, y, z) { + W = w; +} + +//dot product of x,y,z,w +float GVector::dot4(const GVector &them) const { + return((x * them.x) + (y * them.y) + + (z * them.z) + (W * them.W)); +} + +//dot product of x,y,z+w +float GVector::dot4(const GPoint &them) const { + return((x * them.x) + (y * them.y) + + (z * them.z) + W); +} + +float GVector::length() { + return(sqrt((x * x) + (y * y) + (z * z))); +} + +void GVector::normalize() { + float len = length(); //stupid square roots take forever + x /= len; + y /= len; + z /= len; +} + +//stolen from: http://gamecode.tripod.com/tut/tut04.htm +int QTBuilder::ClipPolygon(POLYGON *poly, GVector *plane) { + /* Plan: cycle through the vertices, considering pairs of them. + If both vertices are visible, add them to the new array. + If both vertices are invisible, add neither to the new array. + If one vertex is visible and the other is not, move the one + that's not, and then add both vertices to the new array. + */ + float dist1, dist2; // distances of points to plane + float distratio; // fraction of distance between two points + // new vertices might be created. Don't change + // polygon's vertices because we might still need + // them. Instead, use tempvtx array and update vertices + // array at the end. Create tempvtx array once only. + int i, ii, j=0; + + /* Check if plane is a valid plane */ + if (!(plane->x || plane->y || plane->z)) return -1; + // if not valid plane, don't change polygon and return an error; + + /* The vertices should, as for all functions, be arranged in cyclic + order. That is, if a line was drawn from each vertex to the next + it would form the correct outline of the polygon in 3D space. + This routine might create new vertices because of the clipping, + but the cyclic order will be preserved. + */ + + for (i=0; icount; i++) + { + ii = (i+1)%poly->count; + dist1 = plane->x * poly->c[i].x + plane->y * poly->c[i].y + + plane->z * poly->c[i].z + plane->W; + dist2 = plane->x * poly->c[ii].x + plane->y * poly->c[ii].y + + plane->z * poly->c[ii].z + plane->W; + if (dist1<0 && dist2<0) // line unclipped and invisible + continue; + if (dist1>0 && dist2>0) // line unclipped and visible + tempvtx[j++]=poly->c[i]; + else // line partially visible + if (dist1>0) // first vertex is visible + { + distratio = dist1/(dist1-dist2); + tempvtx[j] = poly->c[i]; + j++; // Copied 1st vertex + tempvtx[j].x = poly->c[i].x + + (poly->c[ii].x - poly->c[i].x) * distratio; + tempvtx[j].y = poly->c[i].y + + (poly->c[ii].y - poly->c[i].y) * distratio; + tempvtx[j].z = poly->c[i].z + + (poly->c[ii].z - poly->c[i].z) * distratio; + j++; // Copied second vertex + } + else // second vertex is visible + { + distratio = dist2/(dist2-dist1); + tempvtx[j].x = poly->c[ii].x + + (poly->c[i].x - poly->c[ii].x) * distratio; + tempvtx[j].y = poly->c[ii].y + + (poly->c[i].y - poly->c[ii].y) * distratio; + tempvtx[j].z = poly->c[ii].z + + (poly->c[i].z - poly->c[ii].z) * distratio; + j++; // Copy only first vertex. 2nd vertex will be copied + // in next iteration of loop + } + } + + for (i=0; ic[i] = tempvtx[i]; // Update the vertices in polygon + poly->count = j; // Update the vertex count + return j; +} + + +void QTBuilder::NormalizeN(FACE *p) { + float len = sqrt(p->nx*p->nx + p->ny*p->ny + p->nz*p->nz); + p->nx /= len; + p->ny /= len; + p->nz /= len; +} + diff --git a/utils/deprecated/azone/azone.h b/utils/deprecated/azone/azone.h new file mode 100644 index 000000000..caba4b1fc --- /dev/null +++ b/utils/deprecated/azone/azone.h @@ -0,0 +1,192 @@ +#ifndef AZONE_H +#define AZONE_H + +//pull in datatypes from zone's map.h +#include "../../zone/map.h" + +/* + + Father Nitwit's zone to map file converter. + +*/ + +#include +#include +#include +using namespace std; + + +#define COUNT_MACTHES 1 + +//this is the version number to put in the map header +#undef MAP_VERSION //override this from map.h with our version +#define MAP_VERSION 0x01000000 + +//quadtree stopping criteria, comment any to disable them +#define MAX_QUADRENT_FACES 50 //if box has fewer than this, stop +#define MIN_QUADRENT_SIZE 100.0f //if box has a dimention smaller than this, stop +#define MIN_QUADRENT_GAIN 0.3f //minimum split ratio before stopping +#define MAX_QUADRENT_MISSES 2 //maximum number of quads which can miss their gains + //1 or 2 make sense, others are less useful + +//attepmt to trim the data a little +#define MAX_Z 3000.0 //seems to be a lot of points above this + //if all points on poly are, kill it. +/* + This is used for the recursive node structure + unsigned shorts are adequate because, worst case + even in a zone that is 6000x6000 with a small node + size of 30x30, there are only 40000 nodes. + + quadrent definitions: + quad 1 (nodes[0]): + x>=0, y>=0 + quad 2 (nodes[1]): + x<0, y>=0 + quad 3 (nodes[2]): + x<0, y<0 + quad 4 (nodes[3]): + x>=0, y<0 + + */ +#define MAX_POLY_VTX 24 //arbitrary, im too lazy to figure it out + //cut a triangle at most 6 times.... + +struct POLYGON { + VERTEX /*w[MAX_POLY_VTX], */c[MAX_POLY_VTX]; + int count; // w is world points, c is for camera points +}; + +class GPoint { +public: + GPoint(); + GPoint(VERTEX &v); + GPoint(float x, float y, float z); + + inline void operator()(float nx, float ny, float nz) { x = nx; y = ny; z = nz; } + + GPoint cross(const GPoint &them) const; + float dot3(const GPoint &them) const; + + float x; + float y; + float z; + +}; +GPoint operator-(const GPoint &v1, const GPoint &v2); + +class GVector : public GPoint { +public: + GVector(); + GVector(const GPoint &them); + GVector(float x, float y, float z, float w = 1.0f); + + inline void operator()(float nx, float ny, float nz, float nw) { x = nx; y = ny; z = nz; W = nw; } + float dot4(const GVector &them) const; + float dot4(const GPoint &them) const; + void normalize(); + float length(); + + float W; +}; + +struct FaceRecord { + FACE *face; + unsigned long index; +}; + +class QTNode; + +class QTBuilder { +public: + QTBuilder(); + ~QTBuilder(); + + bool build(const char *shortname); + bool build_eqg(const char *shortname); + bool writeMap(const char *file); + + bool FaceInNode(const QTNode *q, const FACE *f); +protected: + + void AddFace(VERTEX &v1, VERTEX &v2, VERTEX &v3); + + int ClipPolygon(POLYGON *poly, GVector *plane); + + //dynamic during load +// vector _VertexList; + vector _FaceList; + + //static once loaded +// unsigned long vertexCount; + unsigned long faceCount; +// VERTEX * vertexBlock; + FACE * faceBlock; + + VERTEX tempvtx[MAX_POLY_VTX]; + + QTNode *_root; + + static void NormalizeN(FACE *p); + +#ifdef COUNT_MACTHES + unsigned long gEasyMatches; + unsigned long gEasyExcludes; + unsigned long gHardMatches; + unsigned long gHardExcludes; +#endif + +}; + +//quadtree node container +class QTNode { +public: + QTNode(QTBuilder *builder, float Tminx, float Tmaxx, float Tminy, float Tmaxy); + ~QTNode(); + + void clearNodes(); + + void doSplit(); + void divideYourself(int depth); + + void buildVertexes(); + +// bool writeFile(FILE *out); + + unsigned long countNodes() const; + unsigned long countFacelists() const; + + void fillBlocks(nodeHeader *heads, unsigned long *flist, unsigned long &hindex, unsigned long &findex); + + float minx; + float miny; + float maxx; + float maxy; + unsigned long nfaces; + vector faces; + + /* + quadrent definitions: + quad 1 (node1): + x>=0, y>=0 + quad 2 (node2): + x<0, y>=0 + quad 3 (node3): + x<0, y<0 + quad 4 (node4): + x>=0, y<0 + */ + QTNode *node1; + QTNode *node2; + QTNode *node3; + QTNode *node4; + GPoint v[8]; + bool final; + +protected: + QTBuilder *builder; +}; + + +#endif + diff --git a/utils/deprecated/azone/file.cpp b/utils/deprecated/azone/file.cpp new file mode 100644 index 000000000..681dad3be --- /dev/null +++ b/utils/deprecated/azone/file.cpp @@ -0,0 +1,31 @@ +#include +#include +#include "file.hpp" + +int GetFile(uchar **buffer, int *buf_len, char *base_path, char *file_name, Archive *archive) { + FILE *fp; + char *temp; + + if(base_path) { + temp = new char[strlen(base_path) + strlen(file_name) + 2]; + sprintf(temp, "%s/%s", base_path, file_name); + + fp = fopen(temp, "rb"); + + delete[] temp; + + if(!fp) + return 0; + + fseek(fp, 0, SEEK_END); + *buf_len = ftell(fp); + fseek(fp, 0, SEEK_SET); + *buffer = new uchar[*buf_len]; + fread(*buffer, 1, *buf_len, fp); + + fclose(fp); + return 1; + } + else + return archive->GetFile(file_name, buffer, buf_len); +} diff --git a/utils/deprecated/azone/file.hpp b/utils/deprecated/azone/file.hpp new file mode 100644 index 000000000..3175a4f8f --- /dev/null +++ b/utils/deprecated/azone/file.hpp @@ -0,0 +1,8 @@ +#ifndef __OPENEQ_FILE__ +#define __OPENEQ_FILE__ + +#include "archive.hpp" + +int GetFile(uchar **buffer, int *buf_len, char *base_path, char *file_name, Archive *archive); + +#endif diff --git a/utils/deprecated/azone/file_loader.hpp b/utils/deprecated/azone/file_loader.hpp new file mode 100644 index 000000000..adbe752b4 --- /dev/null +++ b/utils/deprecated/azone/file_loader.hpp @@ -0,0 +1,24 @@ +#ifndef __OPENEQ_FL_API__ +#define __OPENEQ_FL_API__ + +#include "archive.hpp" +#include "3d.hpp" + +class FileLoader { +public: + FileLoader() {} + virtual ~FileLoader() {} + + virtual int Open(char *base_path, char *zone_name, Archive *archive) = 0; + virtual int Close() = 0; + + Content_3D model_data; +protected: + uchar *buffer; + int buf_len; + Archive *archive; + + int status; +}; + +#endif diff --git a/utils/deprecated/azone/global.hpp b/utils/deprecated/azone/global.hpp new file mode 100644 index 000000000..abc858062 --- /dev/null +++ b/utils/deprecated/azone/global.hpp @@ -0,0 +1,24 @@ +#ifndef __OPENEQ_GLOBALS__ +#define __OPENEQ_GLOBALS__ + +#ifdef WIN32 +//#include +#else +#include +#endif + +#ifndef NULL +#define NULL 0 +#endif + +typedef unsigned long uint32; +typedef long int32; + +typedef unsigned short uint16; +typedef short int16; + +typedef unsigned char uint8; +typedef unsigned char uchar; +typedef char int8; + +#endif diff --git a/utils/deprecated/azone/octree.hpp b/utils/deprecated/azone/octree.hpp new file mode 100644 index 000000000..5115bd41e --- /dev/null +++ b/utils/deprecated/azone/octree.hpp @@ -0,0 +1,43 @@ +#ifndef __OPENEQ_OCTREE__ +#define __OPENEQ_OCTREE__ + +#ifdef WIN32 +#define Polygon Polygon_win32 +#include +#undef Polygon +#endif + +#include "3d_base.hpp" + +class Octree_Node { +public: + Octree_Node(); + Octree_Node(int poly_count, Polygon **polys, Vertex **verts, int plac_count, Placeable **plac); + Octree_Node(int poly_count, Polygon **polys, Vertex **verts, int plac_count, Placeable **plac, float min[3], float max[3]); + ~Octree_Node(); + void FindMinMax(); + void Split(char level); + + float min[3], max[3]; + float x, y, z, size, sizes[3]; + + int poly_count; + Vertex **verts; + Polygon **polys; + + int plac_count; + Placeable **plac; + + GLuint li; + + Octree_Node *nodes[8]; +}; + +#define in_box(obj, x, y, z) \ +( \ + (x) >= (obj)->min[0] && (x) <= (obj)->max[0] && \ + (y) >= (obj)->min[1] && (y) <= (obj)->max[1] &&\ + (z) >= (obj)->min[2] && (z) <= (obj)->max[2] \ +) + +#endif diff --git a/utils/deprecated/azone/pfs.cpp b/utils/deprecated/azone/pfs.cpp new file mode 100644 index 000000000..9002ae977 --- /dev/null +++ b/utils/deprecated/azone/pfs.cpp @@ -0,0 +1,222 @@ +#include +#include +#include "pfs.hpp" + +#ifdef WIN32 +#include +#endif + +#pragma pack(1) + +struct struct_header { + uint32 offset; + char magicCookie[4]; + uint32 unknown; +} typedef struct_header; + +struct struct_directory_header { + uint32 count; +} typedef struct_directory_header; + +struct struct_directory { + uint32 crc, offset, size; +} typedef struct_directory; + +struct struct_data_block { + uint32 deflen, inflen; +} typedef struct_data_block; + +struct struct_fn_header { + uint32 fncount; +} typedef struct_fn_header; + +struct struct_fn_entry { + uint32 fnlen; +} typedef struct_fn_entry; + +#pragma pack() + +inline void decompress(char *p, char *p2, int len, int uLen) { + int status; + z_stream d_stream; + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = (Bytef *) p; + d_stream.avail_in = len; + d_stream.next_out = (Bytef *) p2; + d_stream.avail_out = uLen; + + inflateInit(&d_stream); + status = inflate(&d_stream, Z_NO_FLUSH); + inflateEnd(&d_stream); +} + +inline void Lower(char *str) { + while(*str) { + if(*str >= 'A' && *str <= 'Z') + *str += 'a' - 'A'; + ++str; + } +} + +PFSLoader::PFSLoader() { + // Set the status of the loader that nothing is loaded. + this->buffer = NULL; + this->buf_len = -1; + this->status = 0; +} + +PFSLoader::~PFSLoader() { + this->Close(); +} + +int PFSLoader::Open(FILE *fp) { + struct_header s3d_header; + struct_directory_header s3d_dir_header; + struct_directory s3d_dir; + struct_data_block s3d_data; + struct_fn_header *s3d_fn_header; + struct_fn_entry *s3d_fn_entry; + + uint32 *offsets; + + char *temp, *temp2; + int i, j, pos, inf, tmp, running = 0; + + fread(&s3d_header, sizeof(struct_header), 1, fp); + if(s3d_header.magicCookie[0] != 'P' || s3d_header.magicCookie[1] != 'F' || s3d_header.magicCookie[2] != 'S' || s3d_header.magicCookie[3] != ' ') + return 0; + + this->fp = fp; + + fseek(fp, s3d_header.offset, SEEK_SET); + fread(&s3d_dir_header, sizeof(struct_directory_header), 1, fp); + + this->count = s3d_dir_header.count - 1; + this->filenames = new char *[s3d_dir_header.count]; + this->files = new uint32[s3d_dir_header.count - 1]; + offsets = new uint32[s3d_dir_header.count - 1]; + + for(i = 0; i < (int)s3d_dir_header.count; ++i) { + fread(&s3d_dir, sizeof(struct_directory), 1, fp); + + if(s3d_dir.crc == ntohl(0xC90A5861)) { + pos = ftell(fp); + fseek(fp, s3d_dir.offset, SEEK_SET); + temp = new char[s3d_dir.size]; + memset(temp, 0, s3d_dir.size); + inf = 0; + while(inf < (int)s3d_dir.size) { + fread(&s3d_data, sizeof(struct_data_block), 1, fp); + temp2 = new char[s3d_data.deflen]; + fread(temp2, s3d_data.deflen, 1, fp); + decompress(temp2, temp + inf, s3d_data.deflen, s3d_data.inflen); + delete[] temp2; + inf += s3d_data.inflen; + } + fseek(fp, pos, SEEK_SET); + s3d_fn_header = (struct_fn_header *) temp; + pos = sizeof(struct_fn_header); + for(j = 0; j < (int)s3d_fn_header->fncount; ++j) { + s3d_fn_entry = (struct_fn_entry *) &temp[pos]; + this->filenames[j] = new char[s3d_fn_entry->fnlen + 1]; + this->filenames[j][s3d_fn_entry->fnlen] = 0; + memcpy(this->filenames[j], &temp[pos + sizeof(struct_fn_entry)], s3d_fn_entry->fnlen); + pos += sizeof(struct_fn_entry) + s3d_fn_entry->fnlen; + } + } + + else { + this->files[running] = ftell(fp) - 12; + offsets[running] = s3d_dir.offset; + ++running; + } + + } + + for(i = s3d_dir_header.count - 2; i > 0; i--) { + for(j = 0; j < i; j++) { + if(offsets[j] > offsets[j+1]) { + tmp = offsets[j]; + offsets[j] = offsets[j + 1]; + offsets[j + 1] = tmp; + tmp = this->files[j]; + this->files[j] = this->files[j + 1]; + this->files[j + 1] = tmp; + } + } + } + + return 1; +} + +int PFSLoader::Close() { + if(this->status) { + while(this->count > 0) { + delete this->filenames[this->count - 1]; + --this->count; + } + delete[] this->filenames; + delete[] this->files; + } + else + return 0; + + this->buffer = NULL; + this->buf_len = -1; + this->status = 0; + + return 1; +} + +const char *PFSLoader::FindExtension(const char *ext) { + int i; + + int elen = strlen(ext); + + for(i = 0; i < this->count; ++i) { +//printf("Look for %s: got %s\n", ext, this->filenames[i]); + int flen = strlen(this->filenames[i]); + if(flen <= elen) + continue; + if(!strcmp(this->filenames[i]+(flen-elen), ext)) + return(this->filenames[i]); + } + return(NULL); +} + +int PFSLoader::GetFile(char *name, uchar **buf, int *len) { + struct_directory s3d_dir; + struct_data_block s3d_data; + char *temp2; + long inf; + int i; + Lower(name); + + for(i = 0; i < this->count; ++i) { +//printf("Look for %s: got %s\n", name, this->filenames[i]); + if(!strcmp(this->filenames[i], name)) { + fseek(this->fp, this->files[i], SEEK_SET); + fread(&s3d_dir, sizeof(struct_directory), 1, this->fp); + fseek(this->fp, s3d_dir.offset, SEEK_SET); + *buf = new uchar[s3d_dir.size]; + + inf = 0; + while(inf < (int)s3d_dir.size) { + fread(&s3d_data, sizeof(struct_data_block), 1, this->fp); + temp2 = new char[s3d_data.deflen]; + fread(temp2, s3d_data.deflen, 1, this->fp); + decompress(temp2, (char *) *buf + inf, s3d_data.deflen, s3d_data.inflen); + delete[] temp2; + inf += s3d_data.inflen; + } + + *len = inf; + return 1; + } + } + return 0; +} diff --git a/utils/deprecated/azone/pfs.hpp b/utils/deprecated/azone/pfs.hpp new file mode 100644 index 000000000..e58246499 --- /dev/null +++ b/utils/deprecated/azone/pfs.hpp @@ -0,0 +1,21 @@ +#ifndef __OPENEQ_PFS__ +#define __OPENEQ_PFS__ + +#include +#include "global.hpp" +#include "archive.hpp" + + +class PFSLoader : public Archive { +public: + PFSLoader(); + ~PFSLoader(); + + virtual int Open(FILE *fp); + virtual int Close(); + + virtual int GetFile(char *name, uchar **buf, int *len); + virtual const char *FindExtension(const char *ext); +}; + +#endif diff --git a/utils/deprecated/azone/s3d.c b/utils/deprecated/azone/s3d.c new file mode 100644 index 000000000..68888ea91 --- /dev/null +++ b/utils/deprecated/azone/s3d.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include + +#include "s3d.h" + +void decompress(char *p, char *p2, int len, int uLen) { + int status; + z_stream d_stream; + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = p; + d_stream.avail_in = len; + d_stream.next_out = p2; + d_stream.avail_out = uLen; + + inflateInit(&d_stream); + status = inflate(&d_stream, Z_NO_FLUSH); + inflateEnd(&d_stream); +} + +int S3D_Init(s3d_object *obj, FILE *fp) { + struct_header s3d_header; + struct_directory_header s3d_dir_header; + struct_directory s3d_dir; + struct_data_block s3d_data; + struct_fn_header *s3d_fn_header; + struct_fn_entry *s3d_fn_entry; + + uint32 *offsets; + + char *temp, *temp2; + int i, j, pos, inf, tmp, running = 0; + + obj->fp = fp; + + fread(&s3d_header, sizeof(struct_header), 1, fp); + if(s3d_header.magicCookie[0] != 'P' || s3d_header.magicCookie[1] != 'F' || s3d_header.magicCookie[2] != 'S' || s3d_header.magicCookie[3] != ' ') + return -1; + fseek(fp, s3d_header.offset, SEEK_SET); + fread(&s3d_dir_header, sizeof(struct_directory_header), 1, fp); + + obj->count = s3d_dir_header.count; + obj->filenames = (char **) malloc(s3d_dir_header.count * sizeof(char *)); + obj->files = (uint32 *) malloc((s3d_dir_header.count - 1) * sizeof(uint32)); + offsets = (uint32 *) malloc((s3d_dir_header.count - 1) * sizeof(uint32)); + + for(i = 0; i < s3d_dir_header.count; ++i) { + fread(&s3d_dir, sizeof(struct_directory), 1, fp); + + if(s3d_dir.crc == 0x61580AC9) { + pos = ftell(fp); + fseek(fp, s3d_dir.offset, SEEK_SET); + temp = (char *) malloc(s3d_dir.size); + memset(temp, 0, s3d_dir.size); + inf = 0; + while(inf < s3d_dir.size) { + fread(&s3d_data, sizeof(struct_data_block), 1, fp); + temp2 = (char *) malloc(s3d_data.deflen); + fread(temp2, s3d_data.deflen, 1, fp); + decompress(temp2, temp + inf, s3d_data.deflen, s3d_data.inflen); + free(temp2); + inf += s3d_data.inflen; + } + fseek(fp, pos, SEEK_SET); + s3d_fn_header = (struct_fn_header *) temp; + pos = sizeof(struct_fn_header); + for(j = 0; j < s3d_fn_header->fncount; ++j) { + s3d_fn_entry = (struct_fn_entry *) &temp[pos]; + obj->filenames[j] = (char *) malloc(s3d_fn_entry->fnlen + 1); + obj->filenames[j][s3d_fn_entry->fnlen] = 0; + memcpy(obj->filenames[j], &temp[pos + sizeof(struct_fn_entry)], s3d_fn_entry->fnlen); + pos += sizeof(struct_fn_entry) + s3d_fn_entry->fnlen; + } + } + + else { + obj->files[running] = ftell(fp) - 12; + offsets[running] = s3d_dir.offset; + ++running; + } + + } + + for(i = s3d_dir_header.count - 2; i > 0; i--) { + for(j = 0; j < i; j++) { + if(offsets[j] > offsets[j+1]) { + tmp = offsets[j]; + offsets[j] = offsets[j + 1]; + offsets[j + 1] = tmp; + tmp = obj->files[j]; + obj->files[j] = obj->files[j + 1]; + obj->files[j + 1] = tmp; + } + } + } + + return 0; +} + +size_t S3D_GetFile(s3d_object *obj, char *filename, uchar **out) { + struct_directory s3d_dir; + struct_data_block s3d_data; + uchar *buf; + char *temp2; + long inf; + int i; + + for(i = 0; i < obj->count; ++i) { + if(!strcmp(obj->filenames[i], filename)) { + fseek(obj->fp, obj->files[i], SEEK_SET); + fread(&s3d_dir, sizeof(struct_directory), 1, obj->fp); + fseek(obj->fp, s3d_dir.offset, SEEK_SET); + buf = (uchar *) malloc(s3d_dir.size); + + inf = 0; + while(inf < s3d_dir.size) { + fread(&s3d_data, sizeof(struct_data_block), 1, obj->fp); + temp2 = (char *) malloc(s3d_data.deflen); + fread(temp2, s3d_data.deflen, 1, obj->fp); + decompress(temp2, (char *) buf + inf, s3d_data.deflen, s3d_data.inflen); + free(temp2); + inf += s3d_data.inflen; + } + + *out = buf; + return inf; + } + } + return -1; +} + diff --git a/utils/deprecated/azone/s3d.h b/utils/deprecated/azone/s3d.h new file mode 100644 index 000000000..44936d5e6 --- /dev/null +++ b/utils/deprecated/azone/s3d.h @@ -0,0 +1,51 @@ +#ifndef __EQCLIENT_S3D_H_ +#define __EQCLIENT_S3D_H_ +#include +#include "types.h" + +typedef struct struct_header { + uint32 offset; + char magicCookie[4]; + uint32 unknown; +} struct_header; + +typedef struct struct_directory_header { + uint32 count; +} struct_directory_header; + +typedef struct struct_directory { + uint32 crc, offset, size; +} struct_directory; + +typedef struct struct_data_block { + uint32 deflen, inflen; +} struct_data_block; + +typedef struct struct_fn_header { + uint32 fncount; +} struct_fn_header; + +typedef struct struct_fn_entry { + uint32 fnlen; +} struct_fn_entry; + +typedef struct s3d_object { + FILE *fp; + long count; + char **filenames; + uint32 *files; +} s3d_object; + +#ifdef __cplusplus +extern "C" { +#endif + +int S3D_Init(s3d_object *obj, FILE *fp); +size_t S3D_GetFile(s3d_object *obj, char *filename, uchar **out); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/utils/deprecated/azone/ter.cpp b/utils/deprecated/azone/ter.cpp new file mode 100644 index 000000000..11212c570 --- /dev/null +++ b/utils/deprecated/azone/ter.cpp @@ -0,0 +1,413 @@ +#include +#ifdef WIN32 //vc++ chokes here without this +#include +#else +#include +#endif +#include +#include + +#include "file.hpp" +#include "ter.hpp" + +using namespace std; + +TERLoader::TERLoader() { + this->buffer = NULL; + this->buf_len = -1; + this->archive = NULL; + this->status = 0; +} + +TERLoader::~TERLoader() { + this->Close(); +} + + + +void build_hex_line(const char *buffer, unsigned long length, unsigned long offset, char *out_buffer, unsigned char padding) +{ +char *ptr=out_buffer; +int i; +char printable[17]; + ptr+=sprintf(ptr,"%0*i:",padding,offset); + for(i=0;i<16; i++) { + if (i==8) { + strcpy(ptr," -"); + ptr+=2; + } + if (i+offset < length) { + unsigned char c=*(const unsigned char *)(buffer+offset+i); + ptr+=sprintf(ptr," %02x",c); + printable[i]=isprint(c) ? c : '.'; + } else { + ptr+=sprintf(ptr," "); + printable[i]=0; + } + } + sprintf(ptr," | %.16s",printable); +} + +void print_hex(const char *data, unsigned long length) { + char buffer[80]; + uint32 offset; + for(offset=0;offsetmat_count]; + + ter_orig = buffer; + + if(thdr->magic[0] != 'E' || thdr->magic[1] != 'Q' || thdr->magic[2] != 'G') + return 0; + + if(thdr->magic[3] == 'M') { + printf("Model File encountered in TER loader!\n"); + return(0); + } + else if(thdr->magic[3] == 'T') { + buffer += sizeof(ter_header); + } else + return 0; + + ter_tmp = buffer + thdr->list_len; + + if(thdr->version == 3) + { + if(sizeof(ter_header) + thdr->list_len + (thdr->vert_count * sizeof(ter_vertex_v3)) + (thdr->tri_count * sizeof(ter_triangle)) > (unsigned int)buf_len) + { + printf("Buffer Range Check Failed.\n"); + return 0; + } + } + else + { + if(sizeof(ter_header) + thdr->list_len + (thdr->vert_count * sizeof(ter_vertex)) + (thdr->tri_count * sizeof(ter_triangle)) > (unsigned int)buf_len) + { + printf("Buffer Range Check Failed(v3).\n"); + return 0; + } + } + + for(i = 0; i < (int)thdr->mat_count; ++i) { + mlist[i].name = NULL; + mlist[i].basetex = NULL; + } + +int line_count = 0; + j = 0; + while(buffer < ter_tmp) { + val_len = strlen((char *) buffer); + val = (char *) buffer; + + buffer += val_len + 1; + var_len = strlen((char *) buffer); + var = (char *) buffer; + line_count++; + + // printf("%s %s\n", val, var); + + if(strlen(var) == 0 || strlen(val) == 0) { + ++buffer; + continue; + } + + if(!strcmp(val, "e_fShininess0")) { + // Shiny! + continue; + } + else if(!strcmp(var, "e_TextureDiffuse0")) { + mlist[j].basetex = new char[val_len + 1]; + memcpy(mlist[j].basetex, val, val_len + 1); + ++mat_count; + // printf("%i: Basetex: %s\n", j + 1, mlist[j].basetex); + ++j; + } + else if(val[0] != 'e' && val[1] != '_' && ((var[0] != 'e' && var[1] != '_') || !strcmp(val, "e_fShininess0"))) { + if(val[val_len - 3] == '.') { + mlist[j].name = new char[var_len + 1]; + memcpy(mlist[j].name, var, var_len + 1); + } + else { + mlist[j].name = new char[val_len + 1]; + memcpy(mlist[j].name, val, val_len + 1); + continue; + } + // printf("Named: %s\n", mlist[j].name); + } + + buffer += var_len + 1; + } + +printf("%s: Material count: %d, list len=%d, line count=%d\n", zone_name, thdr->mat_count, thdr->list_len, line_count); + + this->model_data.zone_model = new Zone_Model; + zm = this->model_data.zone_model; + + zm->vert_count = thdr->vert_count; + zm->poly_count = thdr->tri_count; + + zm->verts = new Vertex *[zm->vert_count]; + zm->polys = new Polygon *[zm->poly_count]; + + this->model_data.plac_count = 0; + this->model_data.model_count = 0; + + //this is not always correct! + // Derision. Fix offset calculation for EQG version 2 + // +//#define DEBUGTER + buffer = ter_orig + thdr->list_len + sizeof(ter_header); + if(thdr->magic[3] == 'M') buffer = buffer + 4; +#ifdef DEBUGTER + printf("Starting offset is %8X\n", buffer-ter_orig); +#endif + for(int b=0; bmat_count; b++) { + unsigned long property_count = *((unsigned long *)(buffer+12)); +#ifdef DEBUGTER + printf("Property count is %d\n", property_count); fflush(stdout); +#endif + buffer += 16; + for (int a=0; avert_count = (0x2ABF90-0x382BB-4)/sizeof(ter_vertex); + } else if(string("ter_volcano.ter") == zone_name) { + if(thdr->mat_count == 19320) { + buffer = ter_orig + 0x39BB66; + } else { + buffer = ter_orig + 0x38BF0; + } + } else if(string("ter_guildhall.ter") == zone_name) { + buffer = ter_orig + 0x3080 - 2; + } else if(string("ter_guildlobby.ter") == zone_name) { + buffer = ter_orig + 0x4190 + 19; + } else if(string("ter_harbingers.ter") == zone_name) { + buffer = ter_orig + 0x1178; + } else if(string("ter_main.ter") == zone_name) { + //stillmoona + buffer = ter_orig + 0x71944+4; + } else if(string("ter_easterntemple.ter") == zone_name) { + //stillmoonb + buffer = ter_orig + 0xE340-2; + } else if(string("ter_abyss01.ter") == zone_name) { + //thenest + buffer = ter_orig + 0xCA244; + } else if(string("ter_stormtower01.ter") == zone_name) { + //thundercrest + buffer = ter_orig + 0x62230-4; + }*/ + +/* buffer = ter_orig + 0x382BB - 4; + + printf("Good Delta %d 0x%x\n", buffer-ter_tmp, buffer-ter_tmp);*/ + +/* + uchar *pptr2 = (buffer - (sizeof(ter_vertex)*1)); +printf("v dump start: 0x%x\n", (pptr2-ter_orig)); + print_hex((const char *)pptr2, (buffer-pptr2)+sizeof(ter_vertex)*10); +*/ + +printf("%d verts start at %d (0x%x)\n", zm->vert_count, (buffer-ter_orig), (buffer-ter_orig)); +print_hex((const char *)(buffer-16), sizeof(ter_vertex)*3+16); + + for(i = 0; i < zm->vert_count; ++i) { + if(thdr->version == 3) + { + tver3 = (ter_vertex_v3 *) buffer; + zm->verts[i] = new Vertex; + zm->verts[i]->x = tver3->x; + zm->verts[i]->y = tver3->y; + zm->verts[i]->z = tver3->z; + zm->verts[i]->u = tver3->u; + zm->verts[i]->v = tver3->v; + buffer += sizeof(ter_vertex_v3); + } + else + { + tver = (ter_vertex *) buffer; + zm->verts[i] = new Vertex; + zm->verts[i]->x = tver->x; + zm->verts[i]->y = tver->y; + zm->verts[i]->z = tver->z; + zm->verts[i]->u = tver->u; + zm->verts[i]->v = tver->v; + buffer += sizeof(ter_vertex); + } + } + +/* uchar *pptr = (buffer); + +printf("dump tri start: 0x%x\n", (pptr-ter_orig)); + print_hex((const char *)(pptr-16), 16+sizeof(ter_triangle)*10); + +int skipped = 0; + j = 0; +printf("%d tris start at %d (0x%x)\n", zm->poly_count, (buffer-ter_orig), (buffer-ter_orig));*/ + + printf("dump tri start: 0x%x\n", (buffer)); + print_hex((const char *)(buffer-16), 16+sizeof(ter_triangle)*10); + printf("%d tris start at %d (0x%x)\n", zm->poly_count, (buffer), (buffer)); + + uint8 errored = 0; + int skipped = 0; + j = 0; + for(i = 0; i < zm->poly_count; ++i) { + ttri = (ter_triangle *) buffer; + if(ttri->group == -1) { + skipped++; + buffer += sizeof(ter_triangle); + continue; + } + zm->polys[j] = new Polygon; + + //printf(" v1=%d, v2=%d, v3=%d, g=%d, unk=%d\n", ttri->v1, ttri->v2, ttri->v3, ttri->group, ttri->unk); + + if(ttri->v1 >= zm->vert_count && errored < 10) { + printf("Tri %d/%d (s %d) @0x%x: invalid v1: %d >= %d\n", i, zm->poly_count, skipped, (buffer-ter_orig), ttri->v1, zm->vert_count); + errored++; + } + if(ttri->v2 >= zm->vert_count && errored < 10) { + printf("Tri %d/%d (s %d) @0x%x: invalid v2: %d >= %d\n", i, zm->poly_count, skipped, (buffer-ter_orig), ttri->v2, zm->vert_count); + errored++; + } + if(ttri->v3 >= zm->vert_count && errored < 10) { + printf("Tri %d/%d (s %d) @0x%x: invalid v3: %d >= %d\n", i, zm->poly_count, skipped, (buffer-ter_orig), ttri->v3, zm->vert_count); + errored++; + } + + if(ttri->v1 == 0x80000 && ttri->v1 >= zm->vert_count) { + zm->polys[j]->v1 = 0; + zm->polys[j]->v2 = 0; + zm->polys[j]->v3 = 0; + } else { + zm->polys[j]->v1 = ttri->v1; + zm->polys[j]->v2 = ttri->v2; + zm->polys[j]->v3 = ttri->v3; + + if(ttri->group == -1) + zm->polys[j]->tex = thdr->mat_count; + else + zm->polys[j]->tex = ttri->group; + + } + + ++j; + buffer += sizeof(ter_triangle); + } + printf("Skipped %d, %d total\n", skipped, zm->poly_count); + + zm->poly_count = j; + + zm->tex_count = thdr->mat_count; + zm->tex = new Texture *[thdr->mat_count]; + + for(i = 0; i < (int)thdr->mat_count; ++i) { + zm->tex[i] = new Texture; + zm->tex[i]->frame_count = 1; + zm->tex[i]->filenames = new char *[1]; + if(mlist[i].basetex) { + zm->tex[i]->filenames[0] = new char[strlen(mlist[i].basetex) + 1]; + memcpy(zm->tex[i]->filenames[0], mlist[i].basetex, strlen(mlist[i].basetex) + 1); + + delete[] mlist[i].basetex; + } + else if(mlist[i].name) { + zm->tex[i]->filenames[0] = new char[strlen(mlist[i].name) + 1]; + memcpy(zm->tex[i]->filenames[0], mlist[i].name, strlen(mlist[i].name) + 1); + delete[] mlist[i].name; + } + } + // zm->tex[thdr->mat_count] = new Texture; + // zm->tex[thdr->mat_count]->filename = NULL; + + delete[] mlist; + + this->status = 1; + return 1; +} + +int TERLoader::Close() { + Zone_Model *zm = this->model_data.zone_model; + int i; + + if(!this->status) + return 1; + + for(i = 0; i < zm->vert_count; ++i) + delete zm->verts[i]; + for(i = 0; i < zm->poly_count; ++i) + delete zm->polys[i]; + for(i = 0; i < zm->tex_count; ++i) { + delete[] zm->tex[i]->filenames[0]; + delete[] zm->tex[i]->filenames; + delete zm->tex[i]; + } + + delete[] zm->verts; + delete[] zm->polys; + + delete this->model_data.zone_model; + + return 1; +} diff --git a/utils/deprecated/azone/ter.hpp b/utils/deprecated/azone/ter.hpp new file mode 100644 index 000000000..ce0f82792 --- /dev/null +++ b/utils/deprecated/azone/ter.hpp @@ -0,0 +1,51 @@ +#ifndef __OPENEQ_TER__ +#define __OPENEQ_TER__ + +#include "global.hpp" +#include "file_loader.hpp" + +#pragma pack(1) + +struct ter_header { + char magic[4]; + unsigned long version, list_len, mat_count, vert_count, tri_count; +} typedef ter_header; + +struct ter_vertex { + float x, y, z; + float i, j, k; + float u, v; +} typedef ter_vertex; + +struct ter_triangle { + long v1, v2, v3; + long group; + long unk; +} typedef ter_triangle; + +struct ter_vertex_v3 { + float x, y, z; + float i, j, k; + float unk1, unk2, unk3; + float u, v; +} typedef ter_vertex_v3; + +struct material { + char *name; + char *basetex; + char var_count; + char **var_names; + char **var_vals; +} typedef material; + +#pragma pack() + +class TERLoader : public FileLoader { +public: + TERLoader(); + ~TERLoader(); + virtual int Open(char *base_path, char *zone_name, Archive *archive); + virtual int Close(); +}; + +#endif diff --git a/utils/deprecated/azone/types.h b/utils/deprecated/azone/types.h new file mode 100644 index 000000000..f3d6c3016 --- /dev/null +++ b/utils/deprecated/azone/types.h @@ -0,0 +1,16 @@ +#ifndef __EQCLIENT_TYPES_H_ +#define __EQCLIENT_TYPES_H_ + +typedef unsigned long uint32; +typedef long int32; + +typedef unsigned short uint16; +typedef short int16; + +typedef unsigned char uint8; +typedef unsigned char uchar; +typedef char int8; + +#define null 0 + +#endif diff --git a/utils/deprecated/azone/wld.c b/utils/deprecated/azone/wld.c new file mode 100644 index 000000000..e11743390 --- /dev/null +++ b/utils/deprecated/azone/wld.c @@ -0,0 +1,480 @@ +#include +#include +#include +#include "s3d.h" +#include "wld.h" +// #include "mob.h" + +// #define FRAG_DEBUG + +int WLD_GetObjectMesh(wld_object *obj, char *name, Mesh **mesh); + +#define WORD_A(c) sizeof(short) * (c) +#define LONG_A(c) sizeof(long) * (c) + +#define WORD_P(offset) *((short *) (buf + pos + (offset))) +#define LONG_P(offset) *((long *) (buf + pos + (offset))) + + +uchar encarr[] = {0x95, 0x3A, 0xC5, 0x2A, 0x95, 0x7A, 0x95, 0x6A}; + +void decode(uchar *str, int len) { int i; for(i = 0; i < len; ++i) str[i] ^= encarr[i % 8]; } + +FRAGMENT_FUNC(Data03) { + int i, pos, nameLen,r; + WLDTexture *tex = (WLDTexture *) malloc(sizeof(WLDTexture)); + tex->count = *((long *) buf) + 1; + tex->flags = (int *) malloc(sizeof(int) * tex->count); + tex->filenames = (char **) malloc(sizeof(char *) * tex->count); + *obj = (void *) tex; + +#ifdef FRAG_DEBUG + printf("Data03: %p %p\n", obj, *obj); +#endif + + pos = sizeof(long); + for(i = 0; i < tex->count; ++i) { + nameLen = *((short *) (buf + pos)); + tex->filenames[i] = (char *) malloc(nameLen + 1); + memcpy(tex->filenames[i], (char *) (buf + pos + sizeof(short)), nameLen + 1); + decode(tex->filenames[i], nameLen); + for(r = 0; r < nameLen; r++) { + (tex->filenames[i])[r] = tolower((tex->filenames[i])[r]); + } +// Lower(tex->filenames[i]); + } + return 0; +} +FRAGMENT_FUNC(Data04) { + int flags, i, pos; + WLDTexture *tex = (WLDTexture *) malloc(sizeof(WLDTexture)); + tex->count = *((long *) (buf + sizeof(long))); + tex->flags = (int *) malloc(sizeof(int) * tex->count); + tex->filenames = (char **) malloc(sizeof(char *) * tex->count); + *obj = (void *) tex; + +#ifdef FRAG_DEBUG + printf("Data04: %p %p\n", obj, *obj); +#endif + + flags = *((long *) buf); + pos = sizeof(long) * 2; + if(flags & (1 << 2)) + pos += sizeof(long); + if(flags & (1 << 3)) + pos += sizeof(long); + + for(i = 0; i < 1 /* tex->count */; ++i) { + tex->flags[i] = flags; + tex->filenames[i] = ((WLDTexture *) wld->frags[*((long *) (buf + pos)) - 1]->frag)->filenames[i]; + pos += sizeof(long); + } + + return 0; +} + +FRAGMENT_FUNC(Data05) { + *obj = wld->frags[*((long *) buf) - 1]->frag; + +#ifdef FRAG_DEBUG + printf("Data05: %p %p\n", obj, *obj); +#endif + + return 0; +} + +FRAGMENT_FUNC(Data15) { + char *name, *temp; + WLDPlaceable *place = (WLDPlaceable *) malloc(sizeof(WLDPlaceable)); + struct_Data15 *hdr = (struct_Data15 *) buf; + + if(((signed long) -hdr->ref) < (signed long) 0) + return -1; + + name = (char *) malloc(strlen(&wld->sHash[-hdr->ref]) - 9 + 1); + memcpy(name, &wld->sHash[-hdr->ref], strlen(&wld->sHash[-hdr->ref]) - 9); + name[strlen(&wld->sHash[-hdr->ref]) - 9] = 0; + temp = (char *) malloc(strlen(name) + 13); + sprintf(temp, "%s_DMSPRITEDEF", name); + + WLD_GetObjectMesh(wld, temp, &place->mesh); + if(!place->mesh) { + printf("Object not found: %s. Original Name: %s. Cut Name: %s\n", temp, &wld->sHash[-hdr->ref], name); + return 0; + } + + free(name); + + place->trans[0] = hdr->trans[0]; + place->trans[1] = hdr->trans[1]; + place->trans[2] = hdr->trans[2]; + + place->rot[0] = hdr->rot[2] / 512.f * 360.f; + place->rot[1] = hdr->rot[1] / 512.f * 360.f; + place->rot[2] = hdr->rot[0] / 512.f * 360.f; + + place->scale[0] = hdr->scale[2]; + place->scale[1] = hdr->scale[1]; + place->scale[2] = hdr->scale[0]; + + if(wld->placeable == null) + wld->placeable_cur = wld->placeable = (Placeable_LL *) malloc(sizeof(Placeable_LL)); + else + wld->placeable_cur = wld->placeable_cur->next = (Placeable_LL *) malloc(sizeof(Placeable_LL)); + wld->placeable_cur->obj = place; + wld->placeable_cur->next = null; + + return 0; +} + +FRAGMENT_FUNC(Data21) { + struct_Data21 *data; + long count = *((long *) buf); + long i; + BSP_Node *tree = (BSP_Node *) malloc(count * sizeof(BSP_Node)); + + + // Build the BSP Tree + // + for(i = 0; i < count; ++i) { + tree[i].node_number=i; + data = (struct_Data21 *) (buf + (i * sizeof(struct_Data21)) + 4); + tree[i].normal[0] = data->normal[0]; + tree[i].normal[1] = data->normal[1]; + tree[i].normal[2] = data->normal[2]; + tree[i].splitdistance = data->splitdistance; + tree[i].region = data->region; + tree[i].left = data->node[0]; + tree[i].right = data->node[1]; + } + + *obj = (void *) tree; + + return 0; +} + +FRAGMENT_FUNC(Data22) { + + int pos; + + uchar *data6area ; + + struct_Data22 *data = (struct_Data22 *) buf; + + if(!wld->loadBSP) + return -1; + + pos = sizeof(struct_Data22) + (12 * data->size1) + (8 * data->size2); + + // Derision: We don't need any of the data from this fragment for determining the water/lava + // areas, however I left the following code here for possible future use. + // The four floats are the x,y,z of the centre of the region and the max distance of any point + // in the region from the centre. + + data6area = buf + pos; + + data6area = data6area + ((data->size5) * 7 * 4); + + unsigned short d6size = *((unsigned short *) data6area); + data6area = data6area + 2; // Move past d6 size + data6area = data6area + d6size; // Move past RLE data ? Hopefully; + + float f1, f2, f3, f4; + + f1 = *((float *) data6area); + f2 = *((float *) (data6area+4)); + f3 = *((float *) (data6area+8)); + f4 = *((float *) (data6area+12)); + //if(data->flags==0x181) printf("Frag 36 reference?: %ld\n", *((long *) (data6area+20))); + + + return 0; +} + +FRAGMENT_FUNC(Data29) { + + long a,flags, numregions, lenstr; + + struct_Data29 *data29 = (struct_Data29 *) malloc(sizeof(struct_Data29)); + + *obj = (void *) data29; + + + flags = *buf; + numregions = *((long *)(buf+4)); + data29->region_count = numregions ; + data29->region_array = (long *)malloc(numregions * sizeof(long)); + for(a=0;aregion_array[a] = *((long *)(buf+8+(a*4))); + } + lenstr = *((long *)(buf+8+(numregions*4))); + data29->strlen=lenstr; + if(lenstr==0) { + data29->region_type = -1 ; // Flag the type as unknown. Later processing will set it to a default type. + return 0 ; + } + data29->str = (char *)malloc(lenstr); + + char *encstr; + + encstr = buf+8+(++a*4); + decode(encstr, lenstr); + strcpy(data29->str, encstr); + if(lenstr>=2) + if(!strncmp(encstr,"WT",2)) data29->region_type = 1; // Water + else if(!strncmp(encstr,"LA",2)) data29->region_type = 2; // Lava + else if(!strncmp(encstr,"DR",2)) data29->region_type = 3; // Zone Line ? + else data29->region_type = -2; // Flag this so that it doesn't get set to the default water or lava + // later, because it is neither. + // + + return 0 ; +} + +FRAGMENT_FUNC(Data30) { + WLDTexture *temp; + int params; + params = *((long *) (buf + 4)); + if(!params || !*((long *) (buf + 20))) { + *obj = (void *) malloc(sizeof(WLDTexture)); + temp = (WLDTexture *) *obj; + temp->count = 1; + temp->filenames = (char **) malloc(sizeof(char *)); + temp->filenames[0] = "collide.dds"; + temp->flags = (int *) malloc(sizeof(int)); + temp->flags[0] = 0; + } + else { + if(!*((long *) buf)) + *obj = wld->frags[*((long *) (buf + 28)) - 1]->frag; + else + *obj = wld->frags[*((long *) (buf + 20)) - 1]->frag; + ((WLDTexture *) *obj)->params = params; + } + +#ifdef FRAG_DEBUG + printf("Data30: %p %p\n", obj, *obj); +#endif + + return 0; +} + +FRAGMENT_FUNC(Data31) { + int i, pos; + WLDTexture *tex = (WLDTexture *) malloc(sizeof(WLDTexture)); + tex->count = *((long *) (buf + sizeof(long))); + tex->flags = (int *) malloc(sizeof(int) * tex->count); + tex->filenames = (char **) malloc(sizeof(char *) * tex->count); + *obj = (void *) tex; + +#ifdef FRAG_DEBUG + printf("Data31: %p %p\n", obj, *obj); +#endif + + pos = sizeof(long) * 2; + + for(i = 0; i < tex->count; ++i) { + tex->flags[i] = ((WLDTexture *) wld->frags[*((long *) (buf + pos)) - 1]->frag)->flags[0]; + tex->filenames[i] = ((WLDTexture *) wld->frags[*((long *) (buf + pos)) - 1]->frag)->filenames[0]; + tex->params = ((WLDTexture *) wld->frags[*((long *) (buf + pos)) - 1]->frag)->params; + pos += sizeof(long); + } + + return 0; +} + +FRAGMENT_FUNC(Data36) { + struct_Data36 *header = (struct_Data36 *) buf; + WLDVertex *verti = (WLDVertex *) malloc(header->vertexCount * sizeof(WLDVertex)); + Poly *poly = (Poly *) malloc(header->polygonsCount * sizeof(Poly)); + WLDPolygon *p; + TexCoordsNew *tex_new; + TexCoordsOld *tex_old; + Vert *v; + Mesh *mesh = (Mesh *) malloc(sizeof(Mesh)); + int i, j, polyCount, vertCount, texIndex, skinIndex, pc, pos = sizeof(struct_Data36); + float scale = 1.0f / (float) (1 << header->scale); + mesh->name = -frag_name; + mesh->tex = ((WLDTexture *) wld->frags[header->fragment1 - 1]->frag); + mesh->polygonCount = header->polygonsCount; + mesh->vertexCount = header->vertexCount; + + for(i = 0; i < header->vertexCount; ++i) { + v = (Vert *) (buf + pos); + verti[i].x = header->centerX + v->x * scale; + verti[i].y = header->centerY + v->y * scale; + verti[i].z = header->centerZ + v->z * scale; + pos += sizeof(Vert); + } + + if(wld->_new) { + for(i = 0; i < header->texCoordsCount; ++i) { + tex_new = (TexCoordsNew *) (buf + pos); + verti[i].u = tex_new->tx / 256.0f; + verti[i].v = tex_new->tz / 256.0f; + pos += sizeof(TexCoordsNew); + } + } + else { + for(i = 0; i < header->texCoordsCount; ++i) { + tex_old = (TexCoordsOld *) (buf + pos); + verti[i].u = tex_old->tx / 256.0f; + verti[i].v = tex_old->tz / 256.0f; + pos += sizeof(TexCoordsOld); + } + } + + pos += 3 * header->normalsCount; + pos += 4 * header->colorCount; + + for(i = 0; i < header->polygonsCount; ++i) { + p = (WLDPolygon *) (buf + pos); + poly[i].flags = p->flags; + poly[i].v1 = p->v1; + poly[i].v2 = p->v2; + poly[i].v3 = p->v3; + pos += sizeof(WLDPolygon); + } + + pc = 0; + for(i = 0; i < header->size6; ++i) { + vertCount = *((short *) (buf + pos)); + skinIndex = *((short *) (buf + pos + 2)); + for(j = 0; j < vertCount; ++j, ++pc) { + verti[pc].skin = skinIndex; + } + pos += 4; + } + + pc = 0; + for(i = 0; i < header->polygonTexCount; ++i) { + polyCount = *((short *) (buf + pos)); + texIndex = *((short *) (buf + pos + 2)); + for(j = 0; j < polyCount; ++j, ++pc) + poly[pc].tex = texIndex; + pos += 4; + } + + mesh->verti = verti; + mesh->poly = poly; + + *obj = (void *) mesh; + +#ifdef FRAG_DEBUG + printf("Data36: %p %p\n", obj, *obj); +#endif + + return 0; +} + +int WLD_Init(wld_object *obj, uchar *wld, s3d_object *s3d, char loadBSP) { + int pos, i, status; + void *frag_obj; + struct_wld_header *header; + struct_wld_basic_frag *frag; + + obj->wld = wld; + obj->s3d = s3d; + header = (struct_wld_header *) wld; + pos = sizeof(struct_wld_header); + if(header->magic != 0x54503d02) + return -1; + if(header->version == 0x00015500) + obj->_new = 0; + else + obj->_new = 1; + obj->sHash = wld + pos; + decode(obj->sHash, header->stringHashSize); + + obj->frags = (struct_frag **) malloc(sizeof(struct_frag *) * header->fragmentCount); + obj->fragCount = header->fragmentCount; + obj->loadBSP = loadBSP; + obj->placeable = null; + + pos += header->stringHashSize; + for(i = 0; i < header->fragmentCount; ++i) { + frag = (struct_wld_basic_frag *) (wld + pos); + obj->frags[i] = (struct_frag *) malloc(sizeof(struct_frag)); + switch(frag->id) { + case 0x35: status = -1; break; + case 0x03: FRAGMENT(Data03); break; + case 0x04: FRAGMENT(Data04); break; + case 0x05: FRAGMENT(Data05); break; + case 0x15: FRAGMENT(Data15); break; + case 0x21: FRAGMENT(Data21); break; + case 0x22: FRAGMENT(Data22); break; + case 0x29: FRAGMENT(Data29); break; + case 0x30: FRAGMENT(Data30); break; + case 0x31: FRAGMENT(Data31); break; + case 0x36: FRAGMENT(Data36); break; + } + +#ifdef FRAG_DEBUG + if(frag->id != 0x35) + printf("Init: %i - %p\n", i, frag_obj); +#endif + + obj->frags[i]->type = frag->id; + obj->frags[i]->name = frag->nameRef; + obj->frags[i]->frag = frag_obj; + pos += sizeof(struct_wld_basic_frag) + frag->size - 4; + } + + + return 0; +} + +int WLD_GetObjectMesh(wld_object *obj, char *name, Mesh **mesh) { + int i; + for(i = 0; i < obj->objs->fragCount; ++i) { + if(obj->objs->frags[i]->type != 0x36) + continue; + if(!strcmp(&obj->objs->sHash[-obj->objs->frags[i]->name], name)) { + *mesh = (Mesh *) obj->objs->frags[i]->frag; + return 0; + } + } + return -1; +} + +int WLD_GetZoneMesh(wld_object *obj, ZoneMesh *mesh) { + Mesh *temp = NULL; + int i, j; + int polygonCount = 0, vertexCount = 0; + + for(i = 0; i < obj->fragCount; ++i) { + if(obj->frags[i]->type != 0x36) + continue; + temp = (Mesh *) obj->frags[i]->frag; + polygonCount += temp->polygonCount; + vertexCount += temp->vertexCount; + } + + mesh->polygonCount = polygonCount; + mesh->vertexCount = vertexCount; + mesh->poly = (Poly **) malloc(sizeof(Poly *) * polygonCount); + mesh->verti = (WLDVertex **) malloc(sizeof(WLDVertex *) * vertexCount); + polygonCount = 0; + vertexCount = 0; + + if(temp != NULL) + mesh->tex = temp->tex; + + for(i = 0; i < obj->fragCount; ++i) { + if(obj->frags[i]->type != 0x36) + continue; + temp = (Mesh *) obj->frags[i]->frag; + for(j = 0; j < temp->polygonCount; ++j) { + mesh->poly[polygonCount + j] = &temp->poly[j]; + mesh->poly[polygonCount + j]->v1 += vertexCount; + mesh->poly[polygonCount + j]->v2 += vertexCount; + mesh->poly[polygonCount + j]->v3 += vertexCount; + } + for(j = 0; j < temp->vertexCount; ++j) { + mesh->verti[vertexCount + j] = &temp->verti[j]; + } + polygonCount += temp->polygonCount; + vertexCount += temp->vertexCount; + } + return 0; +} diff --git a/utils/deprecated/azone/wld.h b/utils/deprecated/azone/wld.h new file mode 100644 index 000000000..983147b86 --- /dev/null +++ b/utils/deprecated/azone/wld.h @@ -0,0 +1,195 @@ +#ifndef __EQCLIENT_WLD_H_ +#define __EQCLIENT_WLD_H_ + +#include "s3d.h" +#include "types.h" + +#define FRAGMENT(name) status = name(&frag_obj, obj, (wld + pos + sizeof(struct_wld_basic_frag)), frag->size, frag->nameRef) +#define FRAGMENT_FUNC(name) int name(void **obj, wld_object *wld, uchar *buf, int len, int frag_name) + +typedef struct struct_wld_header { + long magic; + long version; + long fragmentCount; + long header3; + long header4; + long stringHashSize; + long header6; +} struct_wld_header; + +typedef struct struct_wld_basic_frag { + long size; + long id; + long nameRef; +} struct_wld_basic_frag; + + +typedef struct struct_Data36 { + long flags; + long fragment1; + long fragment2; + long fragment3; + long fragment4; + float centerX; + float centerY; + float centerZ; + long params2[3]; // 48 + float maxDist; + float minX; + float minY; + float minZ; + float maxX; + float maxY; + float maxZ; // 24 + short int vertexCount; + short int texCoordsCount; + short int normalsCount; + short int colorCount; + short int polygonsCount; + short int size6; + short int polygonTexCount; + short int vertexTexCount; + short int size9; + short int scale; // 20 +} struct_Data36; + +struct Data10 { + long flags, size1, fragment; +}; + +typedef struct WLDVertex { + double x, y, z; + float u, v; + short skin; +} WLDVertex; + +typedef struct Vert { + signed short int x, y, z; +} Vert; + +typedef struct TexCoordsNew { + signed long tx, tz; +} TexCoordsNew; + +typedef struct TexCoordsOld { + signed short int tx, tz; +} TexCoordsOld; + +typedef struct VertexNormal { + signed char nx, ny, nz; +} VertexNormal; + +typedef struct VertexColor { + char color[4]; +} VertexColor; + +typedef struct WLDPolygon { + short int flags, v1, v2, v3; +} WLDPolygon; + +typedef struct Poly { + long int flags, v1, v2, v3, tex; +} Poly; + +typedef struct WLDTexture { + int count; + int *flags; + int params; + char **filenames; + uint32 *textures; +} WLDTexture; + +typedef struct Mesh { + long name; + long polygonCount; + long vertexCount; + WLDVertex *verti; + Poly *poly; + WLDTexture *tex; +} Mesh; + +typedef struct ZoneMesh { + long name; + long polygonCount; + long vertexCount; + WLDVertex **verti; + Poly **poly; + WLDTexture *tex; +} ZoneMesh; + +typedef struct struct_frag { + long type; + long name; + void *frag; +}struct_frag; + +typedef struct struct_Data21 { + float normal[3], splitdistance; + long region, node[2]; +} struct_Data21; + +typedef struct struct_Data22 { + long flags, fragment1, size1, size2, params1, size3, size4, params2, size5, size6; +} struct_Data22; + +typedef struct struct_Data29 { + long region_count; + long *region_array; + long strlen; + char *str; + int region_type; // We fill this in with -1 for unknown, 1 for water, 2 for lava +} struct_Data29; + +/*typedef struct BSP_Region { + +} BSP_Region;*/ + +typedef struct BSP_Node { + long node_number; + float normal[3], splitdistance; + long region; + int special; + long left, right; +} BSP_Node; + +typedef struct struct_Data15 { + uint32 ref, flags, fragment1; + float trans[3], rot[3]; + float scale[3]; + uint32 fragment2, flags2; +} struct_Data15; + +typedef struct WLDPlaceable { + float trans[3], rot[3], scale[3]; + Mesh *mesh; +} WLDPlaceable; + +typedef struct Placeable_LL { + WLDPlaceable *obj; + struct Placeable_LL *next; +} Placeable_LL; + +typedef struct wld_object { + int fragCount; + char loadBSP; + uchar *wld; + uchar *sHash; + uint8 _new; + s3d_object *s3d; + Placeable_LL *placeable, *placeable_cur; + struct wld_object *objs; + struct_frag **frags; +} wld_object; + +#ifdef __cplusplus +extern "C" { +#endif + +int WLD_Init(wld_object *obj, uchar *wld, s3d_object *s3d, char loadBSP); +int WLD_GetZoneMesh(wld_object *obj, ZoneMesh *mesh); +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/utils/deprecated/azone/zon.cpp b/utils/deprecated/azone/zon.cpp new file mode 100644 index 000000000..7730108d3 --- /dev/null +++ b/utils/deprecated/azone/zon.cpp @@ -0,0 +1,299 @@ +#include +#include + +#include "3d.hpp" +#include "zon.hpp" + +ZonLoader::ZonLoader() { + this->buffer = NULL; + this->buf_len = -1; + this->archive = NULL; + this->status = 0; +} + +ZonLoader::~ZonLoader() { + this->Close(); +} + +int ZonLoader::Open(char *base_path, char *zone_name, Archive *archive) { + uchar *buffer; + int buf_len; + + Texture **tex; + int tex_count, tex_tmp, *tex_map; + + int i, j, k, l, len; + char **model_names; + + char *filename; + + uchar *zon_tmp, *zon_orig; + + float rot_change = 180.0f / 3.14159f; + // float rot_change = 1.0f; + float base[3]; + +// filename = new char[strlen(zone_name) + 5]; +// sprintf(filename, "%s.zon", zone_name); + const char *fn = archive->FindExtension(".zon"); + if(fn == NULL) { + printf("Unable to find a .zon file in %s\n", zone_name); + //try looking just for the .ter file... + + const char *tfn = archive->FindExtension(".ter"); + if(tfn == NULL) + return(0); + + if(this->terloader.Open(NULL, (char *) tfn, archive) == 0) { + printf("failed to direct load TER file in %s\n", zone_name); + return(0); + } + printf("Got direct TER, it is %p\n", terloader.model_data.zone_model); + this->model_data.zone_model = terloader.model_data.zone_model; + + return 1; + } + + filename = new char[strlen(fn)+1]; + strcpy(filename, fn); + + if(!archive->GetFile(filename, &buffer, &buf_len)) { + printf("Unable to load '%s' from '%s'\n", filename, zone_name); + delete[] filename; + return 0; + } + +printf("Writting ZON to '%s'\n", filename); +FILE *ttt = fopen(filename, "wb"); +fwrite(buffer, 1, buf_len, ttt); +fclose(ttt); + + delete[] filename; + + zon_header *hdr = (zon_header *) buffer; + zon_placeable *plac; + + if(hdr->magic[0] != 'E' || hdr->magic[1] != 'Q' || hdr->magic[2] != 'G' || hdr->magic[3] != 'Z') { + printf("Bad file header in ZON %s\n", zone_name); + return 0; + } + + buffer += sizeof(zon_header); + + //search for a nice TER file... + uchar *buf_end = buffer + buf_len; + while(buffer < buf_end) { + if(strstr((const char *) buffer, ".ter") != NULL || strstr((const char *) buffer, ".TER") != NULL) { + if(this->terloader.Open(NULL, (char *) buffer, archive) == 0) { + printf("failed to load TER file in %s\n", zone_name); + return(0); + } + printf("Got TER, it is %p\n", terloader.model_data.zone_model); + this->model_data.zone_model = terloader.model_data.zone_model; + break; + } + buffer += strlen((const char *) buffer)+1; + } + + return 1; +/*******************************************/ + + zon_orig = buffer; + zon_tmp = buffer + hdr->list_len; + buffer += strlen((char *) buffer) + 1; + + i = 0; + while(buffer < zon_tmp) { + len = strlen((char *) buffer); + if(buffer[len - 4] == '.') + ++i; + buffer += len + 1; + } + this->model_data.model_count = i; + model_names = new char *[this->model_data.model_count]; + this->model_data.models = new Model *[this->model_data.model_count]; + + buffer = zon_orig; + buffer += strlen((char *) buffer) + 1; + i = 0; + while(buffer < zon_tmp) { + len = strlen((char *) buffer); + if(buffer[len - 4] == '.') { + model_names[i] = (char *) buffer; + ++i; + } + buffer += len + 1; + } + + buffer = zon_orig + hdr->list_len + hdr->unid * 4; + + this->model_data.plac_count = hdr->obj_count - 1; + this->model_data.plac_count = 0; + // this->model_data.placeable = new Placeable *[this->model_data.plac_count]; + this->model_data.placeable = 0; + + plac = (zon_placeable *) buffer; + base[0] = plac->x; + base[1] = plac->y; + base[2] = plac->z; + + printf("(%f %f %f) (%f %f %f)\n", plac->x, plac->y, plac->z, plac->rz, plac->ry, plac->rx); + + buffer += sizeof(zon_placeable); + + for(i = 0; i < this->model_data.plac_count; ++i) { + plac = (zon_placeable *) buffer; + + zon_tmp = zon_orig + plac->loc; + + this->model_data.placeable[i] = new Placeable; + + this->model_data.placeable[i]->x = plac->x; + this->model_data.placeable[i]->y = plac->y; + this->model_data.placeable[i]->z = plac->z; + + this->model_data.placeable[i]->rx = plac->rz * rot_change; + this->model_data.placeable[i]->ry = plac->ry * rot_change; + this->model_data.placeable[i]->rz = plac->rx * rot_change; + + this->model_data.placeable[i]->scale[0] = plac->scale; + this->model_data.placeable[i]->scale[1] = plac->scale; + this->model_data.placeable[i]->scale[2] = plac->scale; + this->model_data.placeable[i]->model = -1; +#ifdef DEBUG_MODE + printf("%s (%f %f %f) (%f %f %f) %f\n", + zon_tmp, + this->model_data.placeable[i]->x, + this->model_data.placeable[i]->y, + this->model_data.placeable[i]->z, + this->model_data.placeable[i]->rx, + this->model_data.placeable[i]->ry, + this->model_data.placeable[i]->rz, + this->model_data.placeable[i]->scale); +#endif + while(zon_tmp[strlen((char *) zon_tmp) - 4] != '.') + zon_tmp += strlen((char *) zon_tmp) + 1; + + for(j = 0; j < this->model_data.model_count; ++j) { + if(!strcmp(model_names[j], (char *) zon_tmp)) { + this->model_data.placeable[i]->model = j; + break; + } + } + + buffer += sizeof(zon_placeable); + } + + // model_loaders = new TERLoader[this->model_data.model_count]; + model_loaders = 0; + + tex_count = this->model_data.zone_model->tex_count; + + for(j = 0; j < this->model_data.model_count; ++j) { + model_loaders[j].Open(NULL, model_names[j], archive); + + this->model_data.models[j] = new Model; + this->model_data.models[j]->vert_count = model_loaders[j].model_data.zone_model->vert_count; + this->model_data.models[j]->poly_count = model_loaders[j].model_data.zone_model->poly_count; + this->model_data.models[j]->tex_count = model_loaders[j].model_data.zone_model->tex_count; + this->model_data.models[j]->verts = model_loaders[j].model_data.zone_model->verts; + this->model_data.models[j]->polys = model_loaders[j].model_data.zone_model->polys; + this->model_data.models[j]->tex = model_loaders[j].model_data.zone_model->tex; + + tex_tmp = 1; + for(i = 0; i < this->model_data.models[j]->tex_count; ++i) { + for(k = 0; k < this->model_data.zone_model->tex_count; ++k) { + if((!this->model_data.zone_model->tex[k]->filenames[0] && !this->model_data.models[j]->tex[i]->filenames[0]) || + (this->model_data.zone_model->tex[k]->filenames[0] && + this->model_data.models[j]->tex[i]->filenames[0] && + !strcmp(this->model_data.zone_model->tex[k]->filenames[0], this->model_data.models[j]->tex[i]->filenames[0]))) { + tex_tmp = 0; + break; + } + } + if(tex_tmp) + ++tex_count; + else { + for(k = 0; k < j; ++k) { + for(l = 0; l < this->model_data.models[k]->tex_count; ++l) { + if(this->model_data.models[k]->tex[l]->filenames[0] == this->model_data.models[j]->tex[i]->filenames[0] || + (this->model_data.models[k]->tex[l]->filenames[0] && + this->model_data.models[j]->tex[i]->filenames[0] && + !strcmp(this->model_data.models[k]->tex[l]->filenames[0], this->model_data.models[j]->tex[i]->filenames[0]))) { + tex_tmp = 0; + break; + } + } + } + if(tex_tmp) + ++tex_count; + } + } + } + + printf("%i\n", tex_count); + tex = new Texture *[tex_count]; + tex_count = this->model_data.zone_model->tex_count; + tex_tmp = 1; + for(i = 0; i < tex_count; ++i) + tex[i] = this->model_data.zone_model->tex[i]; + for(i = 0; i < this->model_data.model_count; ++i) { + for(j = 0; j < this->model_data.models[i]->tex_count; ++j) { + for(k = 0; k < tex_count; ++k) { + if(tex[k]->filenames[0] == this->model_data.models[i]->tex[j]->filenames[0] || + (tex[k]->filenames[0] && + this->model_data.models[i]->tex[j]->filenames[0] && + !strcmp(tex[k]->filenames[0], this->model_data.models[i]->tex[j]->filenames[0]))) { + tex_tmp = 0; + break; + } + } + if(tex_tmp) { + tex[tex_count] = this->model_data.models[i]->tex[j]; + ++tex_count; + } + } + } + for(i = 0; i < this->model_data.model_count; ++i) { + tex_map = new int[this->model_data.models[i]->tex_count]; + + for(j = 0; j < this->model_data.models[i]->tex_count; ++j) { + tex_map[j] = 0; + for(k = 0; k < tex_count; ++k) { + if(tex[k]->filenames[0] == this->model_data.models[i]->tex[j]->filenames[0] || + (tex[k]->filenames[0] && + this->model_data.models[i]->tex[j]->filenames[0] && + !strcmp(tex[k]->filenames[0], this->model_data.models[i]->tex[j]->filenames[0]))) { + tex_map[j] = k; + break; + } + } + } + + for(j = 0; j < this->model_data.models[i]->poly_count; ++j) + this->model_data.models[i]->polys[j]->tex = tex_map[this->model_data.models[i]->polys[j]->tex]; + delete[] tex_map; + } + delete[] this->model_data.zone_model->tex; + this->model_data.zone_model->tex = tex; + this->model_data.zone_model->tex_count = tex_count; + + this->status = 1; + return 1; +} + +// ... in my pants. + +int ZonLoader::Close() { + int i; + + if(!this->status) + return 1; + + this->terloader.Close(); + for(i = 0; i < this->model_data.model_count; ++i) + this->model_loaders[i].Close(); + delete[] this->model_loaders; + + return 1; +} diff --git a/utils/deprecated/azone/zon.hpp b/utils/deprecated/azone/zon.hpp new file mode 100644 index 000000000..a8e04f9db --- /dev/null +++ b/utils/deprecated/azone/zon.hpp @@ -0,0 +1,42 @@ +#ifndef __OPENEQ_ZON__ +#define __OPENEQ_ZON__ + +#include "ter.hpp" + +/* Big thanks to jbb on the ZON stuff! */ + +#pragma pack(1) + +struct zon_header { + char magic[4]; // Constant at EQGZ + long version; // Constant at 2 + long list_len; // Length of the list to follow. + long unid; // Number of 4 byte entries in an unidentified table. + long obj_count; // Placeable object count. + long unk[2]; // Unknown. +} typedef zon_header; + +struct zon_placeable { + long id; + long loc; + float x, y, z; + float rx, ry, rz; + float scale; +} typedef zon_placeable; + +#pragma pack() + +class ZonLoader : public FileLoader { +public: + ZonLoader(); + ~ZonLoader(); + + virtual int Open(char *base_path, char *zone_name, Archive *archive); + virtual int Close(); + +private: + TERLoader terloader; + TERLoader *model_loaders; +}; + +#endif diff --git a/utils/deprecated/charmove/Makefile.bsd b/utils/deprecated/charmove/Makefile.bsd new file mode 100644 index 000000000..1672b39bd --- /dev/null +++ b/utils/deprecated/charmove/Makefile.bsd @@ -0,0 +1,16 @@ + +#Mysql flags, for linuxthreads +#MYSQL_FLAGS=`mysql_config --cflags` +#MYSQL_LIB=`mysql_config --libs` +MYSQL_FLAGS=-I'/usr/local/include/mysql' +MYSQL_LIBS=-L'/usr/local/lib/mysql' -llmysqlclient -lz -lcrypt -lm + +#linux threads flags: +LTFLAGS=-I/usr/local/include/pthread/linuxthreads -D_GNU_SOURCE -D__USE_UNIX98 -D_REENTRANT -D_THREAD_SAFE +LTLDFLAGS=-llthread -llstdc++ -llsupc++ -llgcc_r -L/usr/local/lib + +CXXFLAGS=-ggdb -O -pipe -Wall -I. -I../common -fno-inline -DFREEBSD $(LTFLAGS) $(MYSQL_FLAGS) +LDFLAGS=-lpcap -lz -rdynamic -O2 -ggdb -pipe -L/usr/local/lib $(LTLDFLAGS) + + +include Makefile.common diff --git a/utils/deprecated/charmove/Makefile.common b/utils/deprecated/charmove/Makefile.common new file mode 100644 index 000000000..9d49a9e91 --- /dev/null +++ b/utils/deprecated/charmove/Makefile.common @@ -0,0 +1,24 @@ + +GOBJS=.obj/Mutex.o .obj/unix.o \ + .obj/dbcore.o + +all: export_char import_char + +export_char: $(GOBJS) export_char.o + g++ -I.. $^ $(LDFLAGS) $(MYSQL_LIBS) -o $@ + +import_char: $(GOBJS) import_char.o + g++ -I.. $^ $(LDFLAGS) $(MYSQL_LIBS) -o $@ + +clean: + rm -f import_char export_char *.o + +.obj/%.o: ../../common/%.cpp ../../common/%.h + mkdir -p .obj + g++ -I.. $(CXXFLAGS) -c $< -o $@ + +%.o: %.cpp %.h + mkdir -p .obj + g++ -I.. $(CXXFLAGS) -c $< -o $@ + + diff --git a/utils/deprecated/charmove/char_format.h b/utils/deprecated/charmove/char_format.h new file mode 100644 index 000000000..4e4840404 --- /dev/null +++ b/utils/deprecated/charmove/char_format.h @@ -0,0 +1,77 @@ +#ifndef CHAR_FORMAT_H +#define CHAR_FORMAT_H + +#include "../common/types.h" + +#define CHAR_MAGIC 0xaa77e598 + +#pragma pack(1) +struct CharHeader { + uint32 char_magic; + + //character_ data + char name[64]; + float x; + float y; + float z; + char zone_name[32]; + uint16 zone_id; + uint32 profile_length; + uint32 extprofile_length; + + //faction_values + uint32 faction_value_count; + + //inventory + uint32 inventory_count; + + //sharedbank + uint32 sharedbank_count; + + //quest_globals + uint32 quest_globals_count; + + /* + Variable length fields: + byte PlayerProfile[profile_length]; + byte ExtendedProfile[extprofile_length]; + FactionValueRecord faction_values[faction_value_count]; + InventoryEntry inventory[inventory_count]; + InventoryEntry sharedbank[sharedbank_count]; + QuestGlobalEntry quest_globals[quest_globals_count]; + + */ +}; + + +struct FactionValueRecord { + sint32 faction_id; + sint32 current_value; +}; + +struct InventoryEntry { + uint16 slotid; + uint32 itemid; + sint16 charges; + uint32 colors; + uint32 augs[5]; + uint8 instnodrop; +}; + +struct QuestGlobalEntry { + sint32 npcid; + sint32 zoneid; + char name[66]; + char value[66]; + sint32 expdate; +}; + + +#pragma pack() + + + +#endif + + + diff --git a/utils/deprecated/charmove/charmove.dsp b/utils/deprecated/charmove/charmove.dsp new file mode 100644 index 000000000..a58a10845 --- /dev/null +++ b/utils/deprecated/charmove/charmove.dsp @@ -0,0 +1,110 @@ +# Microsoft Developer Studio Project File - Name="charmove" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=charmove - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "charmove.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "charmove.mak" CFG="charmove - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "charmove - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "charmove - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "charmove - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "." /I ".." /I "../common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib zlib.lib mysqlclient.lib /nologo /subsystem:console /machine:I386 /libpath:"c:\mysql\lib\opt" /libpath:"c:\eqemu\lib" + +!ELSEIF "$(CFG)" == "charmove - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FD /GZ /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib zlib.lib mysqlclient.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"c:\mysql\lib\opt" /libpath:"c:\eqemu\lib" + +!ENDIF + +# Begin Target + +# Name "charmove - Win32 Release" +# Name "charmove - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\common\dbcore.cpp +# End Source File +# Begin Source File + +SOURCE=.\export_char.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\common\Mutex.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/utils/deprecated/charmove/export_char.cpp b/utils/deprecated/charmove/export_char.cpp new file mode 100644 index 000000000..293c3d7b9 --- /dev/null +++ b/utils/deprecated/charmove/export_char.cpp @@ -0,0 +1,263 @@ +#include "../common/types.h" +//#include "../common/eq_packet_structs.h" +#include +#include +#include +#include + +#include +using namespace std; + +#include "../common/dbcore.h" + +#include "char_format.h" + +void usage() { + printf("Usage: export_char [list|charid] [outfile]\n"); + printf(" if charid is 'list' then all char IDs will be listed with names\n"); + printf(" note: this will not work on char ID 0\n"); + exit(1); +} + +int main(int argc, char *argv[]) { + + const char *outfile; + + if(argc != 3) + usage(); + int charid = atoi(argv[1]); + if(charid == 0) + usage(); + outfile = argv[2]; + + char *query = new char[1024]; + char host[200], user[200], passwd[200], database[200]; + int32 port=0; + bool compression = false; + bool items[6] = {false, false, false, false, false, false}; + + if(!DBcore::ReadDBINI(host, user, passwd, database, port, compression, items)) { + exit(1); + } + + if (!items[0] || !items[1] || !items[2] || !items[3]) + { + printf ("Incomplete DB.INI file.\n"); + exit (1); + } + + MYSQL m; + + mysql_init(&m); + + if(!mysql_real_connect(&m, host, user, passwd, database, 0, NULL, 0)) { + printf("Unable to connect 1: %s.\n", mysql_error(&m)); + return(1); + } + + CharHeader header; + char *ppbuffer; + char *eppbuffer; +/* FactionValueRecord *factions; + InventoryEntry *inventory; + InventoryEntry *sharedbank; + QuestGlobalEntry *qglobals;*/ + + sprintf(query, "SELECT name,profile,extprofile,x,y,z,zonename,zoneid FROM character_ WHERE id=%d", charid); + if(mysql_query(&m, query) != 0) { + printf("Unable to query.\n"); + return(1); + } + + MYSQL_RES *res = mysql_use_result(&m); + if(res == NULL) { + printf("Unable to use character_ res.\n"); + return(1); + } + MYSQL_ROW row; + row = mysql_fetch_row(res); + if(row == NULL) { + printf("Unable to find a char with id %d\n", charid); + return(1); + } + unsigned long* lengths; + lengths = mysql_fetch_lengths(res); + + header.char_magic = CHAR_MAGIC; + strcpy(header.name, row[0]); + header.profile_length = lengths[1]; + header.extprofile_length = lengths[2]; + header.x = atof(row[3]); + header.y = atof(row[4]); + header.z = atof(row[5]); + strcpy(header.zone_name, row[6]); + header.zone_id = atoi(row[7]); + //copy in blobs + ppbuffer = new char[header.profile_length]; + eppbuffer = new char[header.extprofile_length]; + memcpy(ppbuffer, row[1], header.profile_length); + memcpy(eppbuffer, row[2], header.extprofile_length); + + mysql_free_result(res); + + //query faction + sprintf(query, "SELECT faction_id,current_value FROM faction_values WHERE char_id=%d", charid); + if(mysql_query(&m, query) != 0) { + printf("Unable to query faction: %s\n", mysql_error(&m)); + return(1); + } + res = mysql_use_result(&m); + if(res == NULL) { + printf("Unable to use faction res.\n"); + return(1); + } + vector fr; + while((row = mysql_fetch_row(res))) { + FactionValueRecord r; + r.faction_id = atoi(row[0]); + r.current_value = atoi(row[1]); + fr.push_back(r); + } + mysql_free_result(res); + + //query inventory + sprintf(query, "SELECT slotid,itemid,charges,color,augslot1,augslot2,augslot3,augslot4,augslot5,instnodrop FROM inventory WHERE charid=%d", charid); + if(mysql_query(&m, query) != 0) { + printf("Unable to query inventory: %s\n", mysql_error(&m)); + return(1); + } + res = mysql_use_result(&m); + if(res == NULL) { + printf("Unable to use inventory res.\n"); + return(1); + } + vector inv; + while((row = mysql_fetch_row(res))) { + InventoryEntry r; + r.slotid = atoi(row[0]); + r.itemid = atoi(row[1]); + r.charges = atoi(row[2]); + r.colors = atoi(row[3]); + r.augs[0] = atoi(row[4]); + r.augs[1] = atoi(row[5]); + r.augs[2] = atoi(row[6]); + r.augs[3] = atoi(row[7]); + r.augs[4] = atoi(row[8]); + inv.push_back(r); + } + mysql_free_result(res); + + //query shared bank + sprintf(query, "SELECT slotid,itemid,charges,augslot1,augslot2,augslot3,augslot4,augslot5 FROM sharedbank,character_ WHERE sharedbank.acctid = character_.account_id AND character_.id=%d", charid); + if(mysql_query(&m, query) != 0) { + printf("Unable to query shared bank: %s\n", mysql_error(&m)); + return(1); + } + res = mysql_use_result(&m); + if(res == NULL) { + printf("Unable to use shared bank res.\n"); + return(1); + } + vector sb; + while((row = mysql_fetch_row(res))) { + InventoryEntry r; + r.slotid = atoi(row[0]); + r.itemid = atoi(row[1]); + r.charges = atoi(row[2]); + r.augs[0] = atoi(row[3]); + r.augs[1] = atoi(row[4]); + r.augs[2] = atoi(row[5]); + r.augs[3] = atoi(row[6]); + r.augs[4] = atoi(row[7]); + sb.push_back(r); + } + mysql_free_result(res); + + //query quest_globals + sprintf(query, "SELECT npcid,zoneid,name,value,expdate FROM quest_globals WHERE charid=%d", charid); + if(mysql_query(&m, query) != 0) { + printf("Unable to query quest globals: %s\n", mysql_error(&m)); + return(1); + } + res = mysql_use_result(&m); + if(res == NULL) { + printf("Unable to use quest globals res.\n"); + return(1); + } + vector qg; + while((row = mysql_fetch_row(res))) { + QuestGlobalEntry r; + r.npcid = atoi(row[0]); + r.zoneid = atoi(row[1]); + strcpy(r.name, row[2]); + strcpy(r.value, row[3]); + r.expdate = atoi(row[4]); + qg.push_back(r); + } + mysql_free_result(res); + + mysql_close(&m); + + + FILE *outf = fopen(outfile, "wb"); + if(outf == NULL) { + printf("Unable to open output file %s\n", outfile); + return(1); + } + + //fill in our counters + header.faction_value_count = fr.size(); + header.inventory_count = inv.size(); + header.sharedbank_count = sb.size(); + header.quest_globals_count = qg.size(); + + //write header + fwrite(&header, 1, sizeof(header), outf); + + //write out pp and epp + fwrite(ppbuffer, 1, header.profile_length, outf); + fwrite(eppbuffer, 1, header.extprofile_length, outf); + + //write out our variable length segments + vector::iterator curfr, endfr; + curfr = fr.begin(); + endfr = fr.end(); + for(; curfr != endfr; curfr++) { + FactionValueRecord &r = *curfr; + fwrite(&r, 1, sizeof(FactionValueRecord), outf); + } + + vector::iterator curi, endi; + curi = inv.begin(); + endi = inv.end(); + for(; curi != endi; curi++) { + InventoryEntry &i = *curi; + fwrite(&i, 1, sizeof(InventoryEntry), outf); + } + + curi = sb.begin(); + endi = sb.end(); + for(; curi != endi; curi++) { + InventoryEntry &i = *curi; + fwrite(&i, 1, sizeof(InventoryEntry), outf); + } + + vector::iterator curqg, endqg; + curqg = qg.begin(); + endqg = qg.end(); + for(; curqg != endqg; curqg++) { + QuestGlobalEntry &r = *curqg; + fwrite(&r, 1, sizeof(QuestGlobalEntry), outf); + } + + fclose(outf); + + return(0); +} + + + + + + + diff --git a/utils/deprecated/charmove/import_char.cpp b/utils/deprecated/charmove/import_char.cpp new file mode 100644 index 000000000..48bb009a5 --- /dev/null +++ b/utils/deprecated/charmove/import_char.cpp @@ -0,0 +1,190 @@ +#include "../common/types.h" +//#include "../common/eq_packet_structs.h" +#include +#include +#include +#include + +#include "../common/dbcore.h" + +#include "char_format.h" + +void usage() { + printf("Usage: import_char [accountid] [infile]\n"); + exit(1); +} + +int main(int argc, char *argv[]) { + + const char *infile; + + if(argc != 3) + usage(); + int accountid = atoi(argv[1]); + if(accountid == 0) + usage(); + infile = argv[2]; + + char *query = new char[1024000]; + char *cursor; + char host[200], user[200], passwd[200], database[200]; + int32 port=0; + bool compression = false; + bool items[6] = {false, false, false, false, false, false}; + + if(!DBcore::ReadDBINI(host, user, passwd, database, port, compression, items)) { + exit(1); + } + + if (!items[0] || !items[1] || !items[2] || !items[3]) + { + printf ("Incomplete DB.INI file.\n"); + exit (1); + } + + MYSQL m; + + mysql_init(&m); + + if(!mysql_real_connect(&m, host, user, passwd, database, 0, NULL, 0)) { + printf("Unable to connect 1: %s.\n", mysql_error(&m)); + return(1); + } + + CharHeader header; + char *ppbuffer; + char *eppbuffer; + FactionValueRecord *factions; + InventoryEntry *inventory; + InventoryEntry *sharedbank; + QuestGlobalEntry *qglobals; + + FILE *inf = fopen(infile, "rb"); + if(inf == NULL) { + printf("Unable to open infile %s\n", infile); + return(1); + } + + if(fread(&header, sizeof(header), 1, inf) != 1) { + printf("Error reading header.\n"); + return(1); + } + + ppbuffer = new char[header.profile_length]; + eppbuffer = new char[header.extprofile_length]; + factions = new FactionValueRecord[header.faction_value_count]; + inventory = new InventoryEntry[header.inventory_count]; + sharedbank = new InventoryEntry[header.sharedbank_count]; + qglobals = new QuestGlobalEntry[header.quest_globals_count]; + + //read all the shit in + if(fread(ppbuffer, header.profile_length, 1, inf) != 1) { + printf("Error reading pp.\n"); + return(1); + } + if(fread(eppbuffer, header.extprofile_length, 1, inf) != 1) { + printf("Error reading epp.\n"); + return(1); + } + if(fread(factions, sizeof(FactionValueRecord), header.faction_value_count, inf) != header.faction_value_count) { + printf("Error reading factions.\n"); + return(1); + } + if(fread(inventory, sizeof(InventoryEntry), header.inventory_count, inf) != header.inventory_count) { + printf("Error reading inventory.\n"); + return(1); + } + if(fread(sharedbank, sizeof(InventoryEntry), header.sharedbank_count, inf) != header.sharedbank_count) { + printf("Error reading sharedbank.\n"); + return(1); + } + if(fread(qglobals, sizeof(QuestGlobalEntry), header.quest_globals_count, inf) != header.quest_globals_count) { + printf("Error reading quest globals.\n"); + return(1); + } + + fclose(inf); + + cursor = query; + cursor += sprintf(cursor, "INSERT INTO character_ " + "(account_id,name,profile,extprofile,x,y,z,zonename,zoneid)" + "VALUES(%d,'%s','", accountid, header.name ); + cursor += mysql_real_escape_string(&m, cursor, (const char *) ppbuffer, header.profile_length); + cursor += sprintf(cursor, "','"); + cursor += mysql_real_escape_string(&m, cursor, (const char *) eppbuffer, header.extprofile_length); + sprintf(cursor, "',%f,%f,%f,'%s',%d)", header.x, header.y, header.z, header.zone_name, header.zone_id); + if(mysql_query(&m, query) != 0) { + printf("Unable to insert character: %s\n", mysql_error(&m)); + return(1); + } + int charid = mysql_insert_id(&m); + uint32 r; + + //faction + for(r = 0; r < header.faction_value_count; r++) { + sprintf(query, "INSERT INTO faction_values (char_id,faction_id,current_value)" + " VALUES(%d,%d,%d)", charid, factions[r].faction_id, factions[r].current_value); + if(mysql_query(&m, query) != 0) { + printf("Unable to insert faction: %s\n", mysql_error(&m)); + return(1); + } + } + + //inventory + for(r = 0; r < header.inventory_count; r++) { + sprintf(query, "INSERT INTO inventory (charid,slotid,itemid,charges,color,augslot1,augslot2,augslot3,augslot4,augslot5,instnodrop)" + " VALUES(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", charid, + inventory[r].slotid, inventory[r].itemid, inventory[r].charges, inventory[r].colors, + inventory[r].augs[0], inventory[r].augs[1], inventory[r].augs[2], inventory[r].augs[3], + inventory[r].augs[4], inventory[r].instnodrop + ); + if(mysql_query(&m, query) != 0) { + printf("Unable to insert faction: %s\n", mysql_error(&m)); + return(1); + } + } + + //shared bank + //this is far from perfect since this is per-account, works great with empty shard bank... + for(r = 0; r < header.sharedbank_count; r++) { + sprintf(query, "INSERT INTO sharedbank (acctid,slotid,itemid,charges,augslot1,augslot2,augslot3,augslot4,augslot5)" + " VALUES(%d,%d,%d,%d,%d,%d,%d,%d,%d)", accountid, + sharedbank[r].slotid, sharedbank[r].itemid, sharedbank[r].charges, + sharedbank[r].augs[0], sharedbank[r].augs[1], sharedbank[r].augs[2], sharedbank[r].augs[3], sharedbank[r].augs[4] + ); + if(mysql_query(&m, query) != 0) { + //ignore errors on insert + printf("Shared bank warning: %s\n", mysql_error(&m)); + // return(1); + } + } + + //quest globals + for(r = 0; r < header.quest_globals_count; r++) { + cursor = query; + cursor += sprintf(cursor, "INSERT INTO quest_globals (charid,npcid,zoneid,name,value,expdate)" + " VALUES(%d,%d,%d,'", charid, qglobals[r].npcid, qglobals[r].zoneid); + cursor += mysql_real_escape_string(&m, cursor, (const char *) qglobals[r].name, strlen(qglobals[r].name)); + cursor += sprintf(cursor, "','"); + cursor += mysql_real_escape_string(&m, cursor, (const char *) qglobals[r].value, strlen(qglobals[r].value)); + sprintf(cursor, "',%d)", qglobals[r].expdate); + + if(mysql_query(&m, query) != 0) { + printf("Unable to insert quest global: %s\n", mysql_error(&m)); + return(1); + } + } + + mysql_close(&m); + + printf("Successfully imported %s as character id %d\n", header.name, charid); + + return(0); +} + + + + + + + diff --git a/utils/deprecated/convert_ops.pl b/utils/deprecated/convert_ops.pl new file mode 100644 index 000000000..8a9361dbc --- /dev/null +++ b/utils/deprecated/convert_ops.pl @@ -0,0 +1,30 @@ +#! /usr/bin/perl + +print< +#include + +using namespace std; + +map opcode_map; + +string get_opcode_name(unsigned long opcode) +{ +map::iterator itr;; + + return (itr=opcode_map.find(opcode))!=opcode_map.end() ? itr->second : "OP_Unknown"; +} +EOF + +printf("void load_opcode_names()\n{\n"); +open(OPS,"$ARGV[0]") || die; +while($_=) { + if (/^#define\s+(\S+)\s+(\S+)/) { + if ($2 ne "0x9999") { + printf("\topcode_map[%s]=\"%s\";\n",$2,$1); + } + } +} +close(OPS); +printf("}\n"); diff --git a/utils/deprecated/hexvis/HexRichEdit.cpp b/utils/deprecated/hexvis/HexRichEdit.cpp new file mode 100644 index 000000000..062afb037 --- /dev/null +++ b/utils/deprecated/hexvis/HexRichEdit.cpp @@ -0,0 +1,75 @@ +// HexRichEdit.cpp : implementation file +// + +#include "stdafx.h" +#include "hexvis.h" +#include "HexRichEdit.h" +#include "hexvisDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// HexRichEdit + +HexRichEdit::HexRichEdit() +: parent(NULL) +{ +} + +HexRichEdit::~HexRichEdit() +{ +} + + +BEGIN_MESSAGE_MAP(HexRichEdit, CRichEditCtrl) + //{{AFX_MSG_MAP(HexRichEdit) + ON_WM_KEYDOWN() + ON_WM_KEYUP() + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// HexRichEdit message handlers + +void HexRichEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + // TODO: Add your message handler code here and/or call default + switch(nChar) { + case VK_LEFT: + if(parent != NULL) + parent->PrevByte(); + return; + case VK_UP: + if(parent != NULL) + parent->PrevLine(); + return; + case VK_RIGHT: + if(parent != NULL) + parent->NextByte(); + return; + case VK_DOWN: + if(parent != NULL) + parent->NextLine(); + return; + } + + CRichEditCtrl::OnKeyDown(nChar, nRepCnt, nFlags); +} + +void HexRichEdit::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags) +{ + switch(nChar) { + case VK_LEFT: + case VK_UP: + case VK_RIGHT: + case VK_DOWN: + return; /* just make sure these dont get processed */ + } + // TODO: Add your message handler code here and/or call default + + CRichEditCtrl::OnKeyUp(nChar, nRepCnt, nFlags); +} diff --git a/utils/deprecated/hexvis/HexRichEdit.h b/utils/deprecated/hexvis/HexRichEdit.h new file mode 100644 index 000000000..2cc65ac25 --- /dev/null +++ b/utils/deprecated/hexvis/HexRichEdit.h @@ -0,0 +1,52 @@ +#if !defined(AFX_HEXRICHEDIT_H__29F129EE_0086_45A7_8A61_7ACCB6507BCE__INCLUDED_) +#define AFX_HEXRICHEDIT_H__29F129EE_0086_45A7_8A61_7ACCB6507BCE__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 +// HexRichEdit.h : header file +// + +///////////////////////////////////////////////////////////////////////////// +// HexRichEdit window + +class CHexvisDlg; + +class HexRichEdit : public CRichEditCtrl +{ +// Construction +public: + HexRichEdit(); + +// Attributes +public: + CHexvisDlg *parent; + +// Operations +public: + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(HexRichEdit) + //}}AFX_VIRTUAL + +// Implementation +public: + virtual ~HexRichEdit(); + + // Generated message map functions +protected: + //{{AFX_MSG(HexRichEdit) + afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); + afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags); + //}}AFX_MSG + + DECLARE_MESSAGE_MAP() +}; + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_HEXRICHEDIT_H__29F129EE_0086_45A7_8A61_7ACCB6507BCE__INCLUDED_) diff --git a/utils/deprecated/hexvis/StdAfx.cpp b/utils/deprecated/hexvis/StdAfx.cpp new file mode 100644 index 000000000..5eb385ce7 --- /dev/null +++ b/utils/deprecated/hexvis/StdAfx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// hexvis.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + + + diff --git a/utils/deprecated/hexvis/StdAfx.h b/utils/deprecated/hexvis/StdAfx.h new file mode 100644 index 000000000..b9c9eff82 --- /dev/null +++ b/utils/deprecated/hexvis/StdAfx.h @@ -0,0 +1,27 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__2BA1C837_5B64_460A_9580_0ECEDF126247__INCLUDED_) +#define AFX_STDAFX_H__2BA1C837_5B64_460A_9580_0ECEDF126247__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include // MFC core and standard components +#include // MFC extensions +#include // MFC Automation classes +#include // MFC support for Internet Explorer 4 Common Controls +#ifndef _AFX_NO_AFXCMN_SUPPORT +#include // MFC support for Windows Common Controls +#endif // _AFX_NO_AFXCMN_SUPPORT + + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__2BA1C837_5B64_460A_9580_0ECEDF126247__INCLUDED_) diff --git a/utils/deprecated/hexvis/hexvis.clw b/utils/deprecated/hexvis/hexvis.clw new file mode 100644 index 000000000..5b30d34db --- /dev/null +++ b/utils/deprecated/hexvis/hexvis.clw @@ -0,0 +1,95 @@ +; CLW file contains information for the MFC ClassWizard + +[General Info] +Version=1 +LastClass=CHexvisDlg +LastTemplate=CRichEditCtrl +NewFileInclude1=#include "stdafx.h" +NewFileInclude2=#include "hexvis.h" + +ClassCount=4 +Class1=CHexvisApp +Class2=CHexvisDlg +Class3=CAboutDlg + +ResourceCount=3 +Resource1=IDD_ABOUTBOX +Resource2=IDR_MAINFRAME +Class4=HexRichEdit +Resource3=IDD_HEXVIS_DIALOG + +[CLS:CHexvisApp] +Type=0 +HeaderFile=hexvis.h +ImplementationFile=hexvis.cpp +Filter=N + +[CLS:CHexvisDlg] +Type=0 +HeaderFile=hexvisDlg.h +ImplementationFile=hexvisDlg.cpp +Filter=D +BaseClass=CDialog +VirtualFilter=dWC +LastObject=CHexvisDlg + +[CLS:CAboutDlg] +Type=0 +HeaderFile=hexvisDlg.h +ImplementationFile=hexvisDlg.cpp +Filter=D + +[DLG:IDD_ABOUTBOX] +Type=1 +Class=CAboutDlg +ControlCount=4 +Control1=IDC_STATIC,static,1342177283 +Control2=IDC_STATIC,static,1342308480 +Control3=IDC_STATIC,static,1342308352 +Control4=IDOK,button,1342373889 + +[DLG:IDD_HEXVIS_DIALOG] +Type=1 +Class=CHexvisDlg +ControlCount=31 +Control1=IDC_PACKET_BODY,RICHEDIT,1353781700 +Control2=IDC_PROCESS,button,1342242817 +Control3=IDC_CLEAR,button,1342242816 +Control4=IDC_LOAD_FILE,button,1342242816 +Control5=IDC_SEARCH_NUMBER,edit,1350631552 +Control6=IDC_FIND_NUMBER,button,1342242816 +Control7=IDC_STATIC,button,1342177287 +Control8=IDC_STATIC,static,1342308352 +Control9=IDC_BYTE,edit,1350633600 +Control10=IDC_STATIC,static,1342308352 +Control11=IDC_SHORT_BE,edit,1350633600 +Control12=IDC_SHORT_LE,edit,1350633600 +Control13=IDC_STATIC,static,1342308352 +Control14=IDC_LONG_BE,edit,1350633600 +Control15=IDC_LONG_LE,edit,1350633600 +Control16=IDC_STATIC,static,1342308352 +Control17=IDC_UNIX_TIME,edit,1350633600 +Control18=IDC_STATIC,static,1342308352 +Control19=IDC_REAL,edit,1350633600 +Control20=IDC_STATIC,static,1342308352 +Control21=IDC_INT64_BE,edit,1350633600 +Control22=IDC_INT64_LE,edit,1350633600 +Control23=IDC_STATIC,static,1342308352 +Control24=IDC_STRING,edit,1350633600 +Control25=IDC_STATIC,static,1342308352 +Control26=IDC_OFFSET,edit,1350633600 +Control27=IDC_STATIC,static,1342308352 +Control28=IDC_STATIC,static,1342308352 +Control29=IDC_BIN_REP,edit,1350633600 +Control30=IDC_STATE,static,1342308352 +Control31=IDC_COPY_TO_CLIP,button,1342242816 + +[CLS:HexRichEdit] +Type=0 +HeaderFile=HexRichEdit.h +ImplementationFile=HexRichEdit.cpp +BaseClass=CRichEditCtrl +Filter=W +VirtualFilter=WC +LastObject=HexRichEdit + diff --git a/utils/deprecated/hexvis/hexvis.cpp b/utils/deprecated/hexvis/hexvis.cpp new file mode 100644 index 000000000..00fc1cc46 --- /dev/null +++ b/utils/deprecated/hexvis/hexvis.cpp @@ -0,0 +1,75 @@ +// hexvis.cpp : Defines the class behaviors for the application. +// + +#include "stdafx.h" +#include "hexvis.h" +#include "hexvisDlg.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CHexvisApp + +BEGIN_MESSAGE_MAP(CHexvisApp, CWinApp) + //{{AFX_MSG_MAP(CHexvisApp) + // NOTE - the ClassWizard will add and remove mapping macros here. + // DO NOT EDIT what you see in these blocks of generated code! + //}}AFX_MSG + ON_COMMAND(ID_HELP, CWinApp::OnHelp) +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CHexvisApp construction + +CHexvisApp::CHexvisApp() +{ + // TODO: add construction code here, + // Place all significant initialization in InitInstance +} + +///////////////////////////////////////////////////////////////////////////// +// The one and only CHexvisApp object + +CHexvisApp theApp; + +///////////////////////////////////////////////////////////////////////////// +// CHexvisApp initialization + +BOOL CHexvisApp::InitInstance() +{ + AfxEnableControlContainer(); + + // Standard initialization + // If you are not using these features and wish to reduce the size + // of your final executable, you should remove from the following + // the specific initialization routines you do not need. + +#ifdef _AFXDLL + Enable3dControls(); // Call this when using MFC in a shared DLL +#else + Enable3dControlsStatic(); // Call this when linking to MFC statically +#endif + AfxInitRichEdit(); + + CHexvisDlg dlg; + m_pMainWnd = &dlg; + int nResponse = dlg.DoModal(); + if (nResponse == IDOK) + { + // TODO: Place code here to handle when the dialog is + // dismissed with OK + } + else if (nResponse == IDCANCEL) + { + // TODO: Place code here to handle when the dialog is + // dismissed with Cancel + } + + // Since the dialog has been closed, return FALSE so that we exit the + // application, rather than start the application's message pump. + return FALSE; +} diff --git a/utils/deprecated/hexvis/hexvis.dsp b/utils/deprecated/hexvis/hexvis.dsp new file mode 100644 index 000000000..fa2d520ec --- /dev/null +++ b/utils/deprecated/hexvis/hexvis.dsp @@ -0,0 +1,154 @@ +# Microsoft Developer Studio Project File - Name="hexvis" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Application" 0x0101 + +CFG=hexvis - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "hexvis.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "hexvis.mak" CFG="hexvis - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "hexvis - Win32 Release" (based on "Win32 (x86) Application") +!MESSAGE "hexvis - Win32 Debug" (based on "Win32 (x86) Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "hexvis - Win32 Release" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 6 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /Yu"stdafx.h" /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" +# ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /machine:I386 +# ADD LINK32 /nologo /subsystem:windows /machine:I386 + +!ELSEIF "$(CFG)" == "hexvis - Win32 Debug" + +# PROP BASE Use_MFC 6 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 6 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_AFXDLL" /D "_MBCS" /FR /Yu"stdafx.h" /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" +# ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept +# ADD LINK32 /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "hexvis - Win32 Release" +# Name "hexvis - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\HexRichEdit.cpp +# End Source File +# Begin Source File + +SOURCE=.\hexvis.cpp +# End Source File +# Begin Source File + +SOURCE=.\hexvis.rc +# End Source File +# Begin Source File + +SOURCE=.\hexvisDlg.cpp +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.cpp +# ADD CPP /Yc"stdafx.h" +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\HexRichEdit.h +# End Source File +# Begin Source File + +SOURCE=.\hexvis.h +# End Source File +# Begin Source File + +SOURCE=.\hexvisDlg.h +# End Source File +# Begin Source File + +SOURCE=.\Resource.h +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\res\hexvis.ico +# End Source File +# Begin Source File + +SOURCE=.\res\hexvis.rc2 +# End Source File +# End Group +# Begin Source File + +SOURCE=.\ReadMe.txt +# End Source File +# End Target +# End Project diff --git a/utils/deprecated/hexvis/hexvis.dsw b/utils/deprecated/hexvis/hexvis.dsw new file mode 100644 index 000000000..12c138c9a --- /dev/null +++ b/utils/deprecated/hexvis/hexvis.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "hexvis"=.\hexvis.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/utils/deprecated/hexvis/hexvis.h b/utils/deprecated/hexvis/hexvis.h new file mode 100644 index 000000000..737e0f8ea --- /dev/null +++ b/utils/deprecated/hexvis/hexvis.h @@ -0,0 +1,49 @@ +// hexvis.h : main header file for the HEXVIS application +// + +#if !defined(AFX_HEXVIS_H__4CCA845D_D36E_41F3_A82B_A1D8A2360F84__INCLUDED_) +#define AFX_HEXVIS_H__4CCA845D_D36E_41F3_A82B_A1D8A2360F84__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#ifndef __AFXWIN_H__ + #error include 'stdafx.h' before including this file for PCH +#endif + +#include "resource.h" // main symbols + +///////////////////////////////////////////////////////////////////////////// +// CHexvisApp: +// See hexvis.cpp for the implementation of this class +// + +class CHexvisApp : public CWinApp +{ +public: + CHexvisApp(); + +// Overrides + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CHexvisApp) + public: + virtual BOOL InitInstance(); + //}}AFX_VIRTUAL + +// Implementation + + //{{AFX_MSG(CHexvisApp) + // NOTE - the ClassWizard will add and remove member functions here. + // DO NOT EDIT what you see in these blocks of generated code ! + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + + +///////////////////////////////////////////////////////////////////////////// + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_HEXVIS_H__4CCA845D_D36E_41F3_A82B_A1D8A2360F84__INCLUDED_) diff --git a/utils/deprecated/hexvis/hexvis.rc b/utils/deprecated/hexvis/hexvis.rc new file mode 100644 index 000000000..a967124ba --- /dev/null +++ b/utils/deprecated/hexvis/hexvis.rc @@ -0,0 +1,239 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#define _AFX_NO_SPLITTER_RESOURCES\r\n" + "#define _AFX_NO_OLE_RESOURCES\r\n" + "#define _AFX_NO_TRACKER_RESOURCES\r\n" + "#define _AFX_NO_PROPERTY_RESOURCES\r\n" + "\r\n" + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif //_WIN32\r\n" + "#include ""res\\hexvis.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#include ""afxres.rc"" // Standard components\r\n" + "#endif\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDR_MAINFRAME ICON DISCARDABLE "res\\hexvis.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_ABOUTBOX DIALOG DISCARDABLE 0, 0, 235, 55 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "About hexvis" +FONT 8, "MS Sans Serif" +BEGIN + ICON IDR_MAINFRAME,IDC_STATIC,11,17,20,20 + LTEXT "hexvis Version 1.2",IDC_STATIC,40,10,119,8,SS_NOPREFIX + LTEXT "Copyright (C) 2006",IDC_STATIC,40,25,119,8 + DEFPUSHBUTTON "OK",IDOK,178,7,50,14,WS_GROUP +END + +IDD_HEXVIS_DIALOG DIALOGEX 0, 0, 387, 314 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_APPWINDOW +CAPTION "hexvis" +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "",IDC_PACKET_BODY,"RICHEDIT",ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_NOHIDESEL | + ES_WANTRETURN | WS_BORDER | WS_VSCROLL | WS_HSCROLL | + WS_TABSTOP,7,7,373,107 + DEFPUSHBUTTON "Process",IDC_PROCESS,330,116,50,12 + PUSHBUTTON "Clear",IDC_CLEAR,263,116,50,12 + PUSHBUTTON "Load File",IDC_LOAD_FILE,330,135,50,12 + EDITTEXT IDC_SEARCH_NUMBER,39,128,63,12,ES_AUTOHSCROLL + PUSHBUTTON "Find",IDC_FIND_NUMBER,107,128,26,12 + GROUPBOX "Field Values",IDC_STATIC,7,151,373,151 + LTEXT "Byte:",IDC_STATIC,154,166,20,8 + EDITTEXT IDC_BYTE,175,163,62,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Short:",IDC_STATIC,24,183,21,8 + EDITTEXT IDC_SHORT_BE,50,180,99,14,ES_AUTOHSCROLL | ES_READONLY + EDITTEXT IDC_SHORT_LE,50,197,99,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Long:",IDC_STATIC,154,182,19,8 + EDITTEXT IDC_LONG_BE,175,180,199,14,ES_AUTOHSCROLL | ES_READONLY + EDITTEXT IDC_LONG_LE,175,197,199,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "UNIX Time:",IDC_STATIC,11,216,38,8 + EDITTEXT IDC_UNIX_TIME,50,214,324,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Real:",IDC_STATIC,24,233,21,8 + EDITTEXT IDC_REAL,50,231,324,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "INT64:",IDC_STATIC,22,250,21,8 + EDITTEXT IDC_INT64_BE,50,248,324,14,ES_AUTOHSCROLL | ES_READONLY + EDITTEXT IDC_INT64_LE,50,265,324,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "String:",IDC_STATIC,23,283,21,8 + EDITTEXT IDC_STRING,50,282,324,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Offset:",IDC_STATIC,23,168,23,8 + EDITTEXT IDC_OFFSET,50,163,99,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Number:",IDC_STATIC,7,130,28,8 + LTEXT "Binary:",IDC_STATIC,250,165,21,8 + EDITTEXT IDC_BIN_REP,274,163,100,14,ES_AUTOHSCROLL | ES_READONLY + LTEXT "Paste text above an process",IDC_STATE,7,117,191,8 + PUSHBUTTON "Copy",IDC_COPY_TO_CLIP,263,134,50,12 +END + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904B0" + BEGIN + VALUE "CompanyName", "\0" + VALUE "FileDescription", "hexvis MFC Application\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "hexvis\0" + VALUE "LegalCopyright", "Copyright (C) 2006\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "hexvis.EXE\0" + VALUE "ProductName", "hexvis Application\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_ABOUTBOX, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 228 + TOPMARGIN, 7 + BOTTOMMARGIN, 48 + END + + IDD_HEXVIS_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 380 + VERTGUIDE, 50 + VERTGUIDE, 374 + TOPMARGIN, 7 + BOTTOMMARGIN, 307 + HORZGUIDE, 128 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_ABOUTBOX "&About hexvis..." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#define _AFX_NO_SPLITTER_RESOURCES +#define _AFX_NO_OLE_RESOURCES +#define _AFX_NO_TRACKER_RESOURCES +#define _AFX_NO_PROPERTY_RESOURCES + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif //_WIN32 +#include "res\hexvis.rc2" // non-Microsoft Visual C++ edited resources +#include "afxres.rc" // Standard components +#endif + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/utils/deprecated/hexvis/hexvisDlg.cpp b/utils/deprecated/hexvis/hexvisDlg.cpp new file mode 100644 index 000000000..ead9932c8 --- /dev/null +++ b/utils/deprecated/hexvis/hexvisDlg.cpp @@ -0,0 +1,1050 @@ +// hexvisDlg.cpp : implementation file +// + +#include "stdafx.h" +#include "hexvis.h" +#include "hexvisDlg.h" +#include +#include "Winuser.h" + +#ifdef _DEBUG +#define new DEBUG_NEW +#undef THIS_FILE +static char THIS_FILE[] = __FILE__; +#endif + +///////////////////////////////////////////////////////////////////////////// +// CAboutDlg dialog used for App About + +class CAboutDlg : public CDialog +{ +public: + CAboutDlg(); + +// Dialog Data + //{{AFX_DATA(CAboutDlg) + enum { IDD = IDD_ABOUTBOX }; + //}}AFX_DATA + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CAboutDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + //{{AFX_MSG(CAboutDlg) + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) +{ + //{{AFX_DATA_INIT(CAboutDlg) + //}}AFX_DATA_INIT +} + +void CAboutDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CAboutDlg) + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) + //{{AFX_MSG_MAP(CAboutDlg) + // No message handlers + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CHexvisDlg dialog + +CHexvisDlg::CHexvisDlg(CWnd* pParent /*=NULL*/) + : CDialog(CHexvisDlg::IDD, pParent) +{ + //{{AFX_DATA_INIT(CHexvisDlg) + //}}AFX_DATA_INIT + // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 + m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); + m_processed = false; +} + +void CHexvisDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + //{{AFX_DATA_MAP(CHexvisDlg) + DDX_Control(pDX, IDC_STATE, m_status); + DDX_Control(pDX, IDC_BIN_REP, m_binRep); + DDX_Control(pDX, IDC_SEARCH_NUMBER, m_searchNumber); + DDX_Control(pDX, IDC_OFFSET, m_offset); + DDX_Control(pDX, IDC_UNIX_TIME, m_unix_time); + DDX_Control(pDX, IDC_STRING, m_string); + DDX_Control(pDX, IDC_SHORT_LE, m_short_le); + DDX_Control(pDX, IDC_SHORT_BE, m_short_be); + DDX_Control(pDX, IDC_REAL, m_real); + DDX_Control(pDX, IDC_PACKET_BODY, m_packetBody); + DDX_Control(pDX, IDC_LONG_LE, m_long_le); + DDX_Control(pDX, IDC_LONG_BE, m_long_be); + DDX_Control(pDX, IDC_INT64_LE, m_int64_le); + DDX_Control(pDX, IDC_INT64_BE, m_int64_be); + DDX_Control(pDX, IDC_BYTE, m_byte); + //}}AFX_DATA_MAP +} + +BEGIN_MESSAGE_MAP(CHexvisDlg, CDialog) + //{{AFX_MSG_MAP(CHexvisDlg) + ON_WM_SYSCOMMAND() + ON_WM_PAINT() + ON_WM_QUERYDRAGICON() + ON_BN_CLICKED(IDC_PROCESS, OnProcess) + ON_EN_UPDATE(IDC_PACKET_BODY, OnUpdatePacketBody) + ON_NOTIFY(EN_SELCHANGE, IDC_PACKET_BODY, OnSelchangePacketBody) + ON_BN_CLICKED(IDC_CLEAR, OnClear) + ON_BN_CLICKED(IDC_FIND_NUMBER, OnFindNumber) + ON_BN_CLICKED(IDC_LOAD_FILE, OnLoadFile) + ON_BN_CLICKED(IDC_COPY_TO_CLIP, OnCopyToClip) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +///////////////////////////////////////////////////////////////////////////// +// CHexvisDlg message handlers + +BOOL CHexvisDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + // Add "About..." menu item to system menu. + + // IDM_ABOUTBOX must be in the system command range. + ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); + ASSERT(IDM_ABOUTBOX < 0xF000); + + CMenu* pSysMenu = GetSystemMenu(FALSE); + if (pSysMenu != NULL) + { + CString strAboutMenu; + strAboutMenu.LoadString(IDS_ABOUTBOX); + if (!strAboutMenu.IsEmpty()) + { + pSysMenu->AppendMenu(MF_SEPARATOR); + pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); + } + } + + // Set the icon for this dialog. The framework does this automatically + // when the application's main window is not a dialog + SetIcon(m_hIcon, TRUE); // Set big icon + SetIcon(m_hIcon, FALSE); // Set small icon + + m_packetBody.SetEventMask(m_packetBody.GetEventMask() | ENM_UPDATE | ENM_SELCHANGE); + + CHARFORMAT cf; + cf.cbSize = sizeof(cf); + cf.dwMask = CFM_FACE; + _tcsncpy(cf.szFaceName, "Courier New", LF_FACESIZE); + m_packetBody.SetDefaultCharFormat(cf); + + m_packetBody.parent = this; + + return TRUE; // return TRUE unless you set the focus to a control +} + +void CHexvisDlg::OnSysCommand(UINT nID, LPARAM lParam) +{ + if ((nID & 0xFFF0) == IDM_ABOUTBOX) + { + CAboutDlg dlgAbout; + dlgAbout.DoModal(); + } + else + { + CDialog::OnSysCommand(nID, lParam); + } +} + +// If you add a minimize button to your dialog, you will need the code below +// to draw the icon. For MFC applications using the document/view model, +// this is automatically done for you by the framework. + +void CHexvisDlg::OnPaint() +{ + if (IsIconic()) + { + CPaintDC dc(this); // device context for painting + + SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); + + // Center icon in client rectangle + int cxIcon = GetSystemMetrics(SM_CXICON); + int cyIcon = GetSystemMetrics(SM_CYICON); + CRect rect; + GetClientRect(&rect); + int x = (rect.Width() - cxIcon + 1) / 2; + int y = (rect.Height() - cyIcon + 1) / 2; + + // Draw the icon + dc.DrawIcon(x, y, m_hIcon); + } + else + { + CDialog::OnPaint(); + } +} + +// The system calls this to obtain the cursor to display while the user drags +// the minimized window. +HCURSOR CHexvisDlg::OnQueryDragIcon() +{ + return (HCURSOR) m_hIcon; +} + +void CHexvisDlg::OnProcess() { + CString packet; + m_packetBody.GetWindowText(packet); + + m_offsetStart = -1; + m_digits.clear(); + m_digits.reserve(packet.GetLength()/3); + m_lineStarts.clear(); + + m_processed = false; + + + int linelen; + const char *start = packet.GetBuffer(0); + const char *cur = start; + linelen = 0; + while(*cur != '\0') { + if(*cur == '\r' || *cur == '\n') { + ProcessLine(start, linelen); + cur++; + if(*cur == '\n') + cur++; + start = cur; + linelen = 0; + } else { + linelen++; + cur++; + } + } + if(linelen > 0) + ProcessLine(start, linelen); + + DisplayDigits(); + + m_processed = true; + + //this will trigger the sel change + m_packetBody.SetSel(8, 9); + + m_status.SetWindowText("Press clear to process another block"); +} + +void CHexvisDlg::build_hex_line(const char *buffer, unsigned long length, unsigned long offset, unsigned char padding) +{ + char out_buffer[128]; + char *ptr=out_buffer; + int i; + char printable[17]; + + CHARFORMAT cf; + cf.cbSize = sizeof(cf); + cf.dwMask = CFM_COLOR; + cf.dwEffects = 0; + + m_packetBody.SetSel(-1, -1); + sprintf(out_buffer,"%0*i:",padding, offset + m_offsetStart); + cf.crTextColor = RGB(0,0,0); + m_packetBody.SetSelectionCharFormat(cf); + m_packetBody.ReplaceSel(out_buffer, false); + + m_lineStarts.push_back(m_packetBody.GetTextLength()); + //m_packetBody.SetSel(-1, -1); + + for(i=0;i<16; i++) { + if (i==8) { + strcpy(ptr," -"); + ptr+=2; + } + if (i+offset < length) { + unsigned char c=*(const unsigned char *)(buffer+offset+i); + ptr+=sprintf(ptr," %02x",c); + printable[i]=isprint(c) ? c : '.'; + } else { + ptr+=sprintf(ptr," "); + printable[i]=0; + } + } + + //hex contents + cf.crTextColor = RGB(0,155,0); + m_packetBody.SetSelectionCharFormat(cf); + m_packetBody.ReplaceSel(out_buffer, false); + + //ascii + sprintf(out_buffer," | %.16s\r\n",printable); + cf.crTextColor = RGB(0,0,155); + m_packetBody.SetSelectionCharFormat(cf); + m_packetBody.ReplaceSel(out_buffer, false); +} + +void CHexvisDlg::DisplayDigits() { + m_packetBody.SetSel(0, -1); + m_packetBody.Clear(); + + int len = m_digits.size(); + if(len == 0) + return; + + const char *buf = (const char *) &m_digits[0]; + int offset; + for(offset = 0; offset < len; offset += 16) { + build_hex_line(buf, len, offset, 4); + } + + /* + CHARFORMAT cf; + cf.cbSize = sizeof(cf); + cf.dwMask = CFM_FACE; + _tcsncpy(cf.szFaceName, "Courier New", LF_FACESIZE); + m_packetBody.SetSel(0, -1); + m_packetBody.SetSelectionCharFormat(cf);*/ + + + /*CHARFORMAT cf; + cf.cbSize = sizeof(cf); + cf.dwMask = CFM_COLOR; + cf.dwEffects = 0; + cf.crTextColor = RGB(0,0,155); + m_packetBody.SetSel(0, -1); + m_packetBody.SetSelectionCharFormat(cf);*/ + + m_packetBody.SetSel(-1, -1); + + /* + CHARFORMAT cf; + cf.cbSize = sizeof(cf); + + cf.cbSize = sizeof(CHARFORMAT); + cf.dwMask = CFM_BOLD|CFM_COLOR|CFM_ITALIC|CFM_STRIKEOUT|CFM_UNDERLINE; + cf.dwEffects = 0; +//per ANSI, he bold attribute changes the color, and does not actually bold the text. +// if(f.bold) +// cf.dwEffects |= CFE_BOLD; + if(f.italics) + cf.dwEffects |= CFE_ITALIC; + if(f.underline) + cf.dwEffects |= CFE_UNDERLINE; + if(f.strikethrough) + cf.dwEffects |= CFE_STRIKEOUT; + //f.inverse + + cf.crTextColor = lt_colors[(f.bold?10:0) + f.color_index]; + //bg_color_index + + SetSelectionCharFormat(cf); */ +} + + +void CHexvisDlg::ProcessLine(const char *line, int len) { + const char *startnum = NULL; + int numlen = 0; + bool maybe_hex = false; + bool found_offset = false; + + for(; len > 0; len--, line++) { + switch(*line) { + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + maybe_hex = true; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if(startnum == NULL) { + startnum = line; + numlen = 1; + } else { + numlen++; + } + break; + case 'x': + if(startnum != NULL) { + if(numlen == 1) { + numlen++; //could be valid 0x notation + maybe_hex = true; + } else + startnum = NULL; //out of place. + } + break; + case ':': + case '|': + case '>': + if(found_offset) { + //this is assumed to be the start of the ascii + len = 0; //'break' + } else { + if(m_offsetStart == -1) + if(startnum != NULL) { + //we found a leading offset + std::string num(startnum, numlen); + m_offsetStart = strtoul(num.c_str(), NULL, maybe_hex?16:10); + } else { + m_offsetStart = 0; + } + found_offset = true; + } + startnum = NULL; + maybe_hex = false; + break; + case ' ': + case '\t': + case '-': + if(!found_offset) { + if(startnum != NULL) { + if(_ProcessDigits(startnum, numlen)) { + if(m_offsetStart == -1) + m_offsetStart = 0; + found_offset = true; + } + } else { + //just leading junk... do nothing + } + } else { + if(startnum != NULL) { + _ProcessDigits(startnum, numlen); + } + } + startnum = NULL; + maybe_hex = false; + break; + default: + startnum = NULL; + maybe_hex = false; + break; + } + } + if(startnum != NULL) { + if(m_offsetStart == -1) + m_offsetStart = 0; + _ProcessDigits(startnum, numlen); + } +} + +bool CHexvisDlg::_ProcessDigits(const char *startnum, int numlen) { + /*if(numlen == 2) { + std::string num(startnum, 2); + unsigned char digit = strtoul(num.c_str(), NULL, 16); + m_digits.push_back(digit); + return(true); + } else if(numlen == 4) { + std::string num(startnum, 2); + unsigned char digit = strtoul(num.c_str(), NULL, 16); + m_digits.push_back(digit); + + std::string num2(startnum+2, 2); + digit = strtoul(num2.c_str(), NULL, 16); + m_digits.push_back(digit); + return(true); + } else */ + if(numlen > 0 && (numlen & 0x1) == 0) { + //an even number of hex digits, divide them in twos + int doffset; + for(doffset = 0; doffset < numlen; doffset += 2) { + std::string num(startnum+doffset, 2); + unsigned char digit = strtoul(num.c_str(), NULL, 16); + m_digits.push_back(digit); + } + return(true); + } + return(false); +} + +void CHexvisDlg::AnalyzeBytes(int byte) { + CString s; + + int maxlen = m_digits.size() - byte; + const unsigned char *bptr = (const unsigned char *) &m_digits[byte]; + + s.Format("%d = 0x%X", byte+m_offsetStart, byte+m_offsetStart); + m_offset.SetWindowText(s); + + if(maxlen >= sizeof(char)) { + s.Format("0x%02X = %d = %c", *bptr, *bptr, *bptr); + m_byte.SetWindowText(s); + + s.Format("%.*s", maxlen, bptr); + m_string.SetWindowText(s); + + CString b; + s = ""; + int r; + for(r = 0; r < 1 && r < maxlen; r++) { + unsigned char cb = *(bptr+r); + b.Format("%d%d%d%d %d%d%d%d", + (cb&0x80)>>7, + (cb&0x40)>>6, + (cb&0x20)>>5, + (cb&0x10)>>4, + (cb&0x08)>>3, + (cb&0x04)>>2, + (cb&0x02)>>1, + (cb&0x01) ); + if(!s.IsEmpty()) + s += " - "; + s += b; + } + m_binRep.SetWindowText(s); + } else { + m_byte.SetWindowText(""); + m_binRep.SetWindowText(""); + m_string.SetWindowText(""); + } + + if(maxlen >= sizeof(short)) { + const unsigned short *le = (const unsigned short *) bptr; + signed short sle = *le; + unsigned short be = ((*le & 0xFF)<<8) | ((*le &0xFF00)>>8); + signed short sbe = be; + + s.Format("0x%04X = %d = %u", *le, sle, *le); + m_short_le.SetWindowText(s); + + s.Format("0x%04X = %d = %u", be, sbe, be); + m_short_be.SetWindowText(s); + } else { + m_short_le.SetWindowText(""); + m_short_be.SetWindowText(""); + } + + + if(maxlen >= sizeof(long)) { + const unsigned long *le = (const unsigned long *) bptr; + signed long sle = *le; + unsigned long be = + ((*le & 0x000000FF) <<24) + | ((*le & 0x0000FF00L)<<8) + | ((*le & 0x00FF0000L)>>8) + | ((*le & 0xFF000000L)>>24) + ; + signed long sbe = be; + + s.Format("0x%08lX = %ld = %lu", *le, sle, *le); + m_long_le.SetWindowText(s); + + s.Format("0x%08lX = %ld = %lu", be, sbe, be); + m_long_be.SetWindowText(s); + + std::string tt; + time_t t = *le; + const char *p = ctime(&t); + if(p == NULL) + p = "(ERROR)"; + tt = p; + t = be; + p = ctime(&t); + if(p == NULL) + p = "(ERROR)"; + s.Format("%s | %s", tt.c_str(), p); + m_unix_time.SetWindowText(s); + } else { + m_long_le.SetWindowText(""); + m_long_be.SetWindowText(""); + m_unix_time.SetWindowText(""); + } + + if(maxlen >= sizeof(float)) { + const float *f = (const float *) bptr; + + if(maxlen >= sizeof(double)) { + const double *d = (const double *) bptr; + s.Format("%.13f %.13f", *f, *d); + m_real.SetWindowText(s); + } else { + s.Format("%.13f", *f); + m_real.SetWindowText(s); + } + } else { + m_real.SetWindowText(""); + } + + + if(maxlen >= sizeof(__int64)) { + const unsigned __int64 *le = (const unsigned __int64 *) bptr; + signed __int64 sle = *le; + unsigned __int64 be = + ((*le & 0x00000000000000FF)<<56) + | ((*le & 0x000000000000FF00)<<40) + | ((*le & 0x0000000000FF0000)<<24) + | ((*le & 0x00000000FF000000)<<8) + | ((*le & 0x000000FF00000000)>>8) + | ((*le & 0x0000FF0000000000)>>24) + | ((*le & 0x00FF000000000000)>>40) + | ((*le & 0xFF00000000000000)>>56) + ; + signed __int64 sbe = be; + + s.Format("0x%I64X = %I64d = %I64u", *le, sle, *le); + m_int64_le.SetWindowText(s); + + s.Format("0x%I64X = %I64d = %I64u", be, sbe, be); + m_int64_be.SetWindowText(s); + } else { + m_int64_le.SetWindowText(""); + m_int64_be.SetWindowText(""); + } + +} + +void CHexvisDlg::OnUpdatePacketBody() +{ + // TODO: If this is a RICHEDIT control, the control will not + // send this notification unless you override the CDialog::OnInitDialog() + // function to send the EM_SETEVENTMASK message to the control + // with the ENM_UPDATE flag ORed into the lParam mask. + + // TODO: Add your control notification handler code here +} + +void CHexvisDlg::OnSelchangePacketBody(NMHDR* pNMHDR, LRESULT* pResult) +{ + SELCHANGE *pSelChange = reinterpret_cast(pNMHDR); + // TODO: The control will not send this notification unless you override the + // CDialog::OnInitDialog() function to send the EM_SETEVENTMASK message + // to the control with the ENM_SELCHANGE flag ORed into the lParam mask. + + static int line_mid = 3*8; + static int ascii_start = 3*16 + 3; + static int ascii_end = 3*16 + 5 + 16; + + if(m_processed) { + + long start, end; + + m_packetBody.GetSel(start, end); + if(start != -1) { + int line = m_packetBody.LineFromChar(start); + if(line >= 0 && line < m_lineStarts.size()) { + int lstart = m_lineStarts[line]+1; + int loffset = start - lstart; + + if(loffset > line_mid) + loffset -= 2; + + int coffset; + if(loffset <= 0) + coffset = 0; + else if(loffset < ascii_start) { + coffset = (loffset / 3); + if(coffset >= 16) + coffset = 15; + } else if(loffset < ascii_end) { + //into the ascii section + coffset = (loffset-ascii_start); + } else { + coffset = 15; + } + + + + int abscoffset = coffset + line*16; + + if(abscoffset >= m_digits.size()) { + int diff = 1 + abscoffset - m_digits.size(); + coffset -= diff; + abscoffset -= diff; + } + + int seloffset = lstart + 3*coffset; + if(coffset >= 8) + seloffset += 2; + + + //they have selected byte 'abscoffset' + AnalyzeBytes(abscoffset); + m_activeByte = abscoffset; + + m_packetBody.SetSel(seloffset, seloffset + 2); + } + } + } + + *pResult = 0; +} + +void CHexvisDlg::SelectByte(int byte) { + if(byte < 0) + byte = 0; + if(byte >= m_digits.size()) + byte = m_digits.size()-1; + + int seloffset = 0; + if(byte > 0) { + //first figure out what line its on... + //then figure out the char offset into that line. + int line = byte/16; + int coffset = byte%16; + int lstart = m_lineStarts[line]+1; + seloffset = lstart + 3*coffset; + if(coffset >= 8) + seloffset += 2; + + m_activeByte = byte; + } + + //this will trigger a sel change which will update the fields + m_packetBody.SetSel(seloffset, seloffset + 2); +} + +void CHexvisDlg::OnClear() +{ + m_processed = false; + m_activeByte = -1; + m_packetBody.SetSel(0, -1); + m_packetBody.Clear(); + + m_status.SetWindowText("Paste text above an process"); +} + +static __int64 _strtoll(char *str) +{ + __int64 result = 0; + int negative=0; + + while (*str == ' ' || *str == '\t') + str++; + if (*str == '+') + str++; + else if (*str == '-') { + negative = 1; + str++; + } + + while (*str >= '0' && *str <= '9') { + result = (result*10)+(*str++ - '0'); + } + return negative ? -result : result; +} + + +void CHexvisDlg::OnFindNumber() +{ + if(!m_processed || m_digits.empty()) + return; + + CString num; + m_searchNumber.GetWindowText(num); + const char *buf = num.GetBuffer(0); + if(buf[0] == '\0') + return; + + FindSpec s; + //we dont handle unsigned 64 bit ints very well. + s.intval = strtol(buf, NULL, 0); + s.dval = strtod(buf, NULL); + s.fval = s.dval; //only cast it down once. + + int bytecount = m_digits.size(); + int sval = m_activeByte; + int eval = bytecount-1; + if(sval < 1) { + sval = 0; + } else { + eval = sval; + sval++; + } + + m_status.SetWindowText("Searching..."); + + bool found = false; + while(true) { + if(sval >= bytecount) + sval = 0; + + if(FindNumber(sval, &s)) { + SelectByte(sval); + found = true; + break; + } + + if(sval == eval) + break; + sval++; + } + if(!found) + m_status.SetWindowText("Not found"); +} + +static bool isnormal(double f) { + int c = _fpclass(f); + return( + (c & (_FPCLASS_SNAN | _FPCLASS_QNAN | _FPCLASS_NINF | _FPCLASS_PINF)) == 0 + ); +} + +static void _findDebug() { + static int x; + x++; +} + +bool CHexvisDlg::FindNumber(int byte, const FindSpec *f) { + + int maxlen = m_digits.size() - byte; + const unsigned char *bptr = (const unsigned char *) &m_digits[byte]; + + if(maxlen >= sizeof(char)) { + if(*bptr == f->intval) { + _findDebug(); + m_status.SetWindowText("Found as a byte"); + return(true); + } + } + + if(maxlen >= sizeof(short)) { + const unsigned short *le = (const unsigned short *) bptr; + signed short sle = *le; + unsigned short be = ((*le & 0xFF)<<8) | ((*le &0xFF00)>>8); + signed short sbe = be; + + if(*le == f->intval) { + m_status.SetWindowText("Found as a native short"); + _findDebug(); + return(true); + } + if(sle == f->intval) { + m_status.SetWindowText("Found as a signed native short"); + _findDebug(); + return(true); + } + if(be == f->intval) { + m_status.SetWindowText("Found as a swapped short"); + _findDebug(); + return(true); + } + if(sbe == f->intval) { + m_status.SetWindowText("Found as a signed swapped short"); + _findDebug(); + return(true); + } + } + + + if(maxlen >= sizeof(long)) { + const unsigned long *le = (const unsigned long *) bptr; + signed long sle = *le; + unsigned long be = + ((*le & 0x000000FF) <<24) + | ((*le & 0x0000FF00L)<<8) + | ((*le & 0x00FF0000L)>>8) + | ((*le & 0xFF000000L)>>24) + ; + signed long sbe = be; + + if(*le == f->intval) { + m_status.SetWindowText("Found as a native long"); + _findDebug(); + return(true); + } + if(sle == f->intval) { + m_status.SetWindowText("Found as a signed native long"); + _findDebug(); + return(true); + } + if(be == f->intval) { + m_status.SetWindowText("Found as a swapped long"); + _findDebug(); + return(true); + } + if(sbe == f->intval) { + m_status.SetWindowText("Found as a signed swapped long"); + _findDebug(); + return(true); + } + } + + + if(f->fval != 0.0f && maxlen >= sizeof(float)) { + const float *fv = (const float *) bptr; + + if(isnormal(*fv)) { + if(*fv == f->fval) { + m_status.SetWindowText("Found as a float"); + _findDebug(); + return(true); + } + if(__int64(*fv) == f->intval) { + m_status.SetWindowText("Found as a rounded float"); + _findDebug(); + return(true); + } + } + } + + + if(f->dval != 0.0f && maxlen >= sizeof(double)) { + const double *d = (const double *) bptr; + if(isnormal(*d)) { + if(*d == f->dval) { + m_status.SetWindowText("Found as a double"); + _findDebug(); + return(true); + } + if(__int64(*d) == f->intval) { + m_status.SetWindowText("Found as a rounded double"); + _findDebug(); + return(true); + } + } + } + + if(maxlen >= sizeof(__int64)) { + const unsigned __int64 *le = (const unsigned __int64 *) bptr; + signed __int64 sle = *le; + unsigned __int64 be = + ((*le & 0x00000000000000FF)<<56) + | ((*le & 0x000000000000FF00)<<40) + | ((*le & 0x0000000000FF0000)<<24) + | ((*le & 0x00000000FF000000)<<8) + | ((*le & 0x000000FF00000000)>>8) + | ((*le & 0x0000FF0000000000)>>24) + | ((*le & 0x00FF000000000000)>>40) + | ((*le & 0xFF00000000000000)>>56) + ; + signed __int64 sbe = be; + + if(*le == f->intval) { + m_status.SetWindowText("Found as a native int64"); + _findDebug(); + return(true); + } + if(sle == f->intval) { + m_status.SetWindowText("Found as a signed native int64"); + _findDebug(); + return(true); + } + if(be == f->intval) { + m_status.SetWindowText("Found as a swapped int64"); + _findDebug(); + return(true); + } + if(sbe == f->intval) { + m_status.SetWindowText("Found as a signed swapped int64"); + _findDebug(); + return(true); + } + } + + return(false); +} + + +void CHexvisDlg::PrevByte() { + if(m_activeByte > 0) + SelectByte(m_activeByte-1); +} + +void CHexvisDlg::NextByte() { + int t = m_activeByte+1; + if(t < m_digits.size()) + SelectByte(t); +} + +void CHexvisDlg::PrevLine() { + if(m_activeByte == 0) + return; + if(m_activeByte > 15) + SelectByte(m_activeByte-16); + else + SelectByte(0); +} + +void CHexvisDlg::NextLine() { + int max = m_digits.size(); + if((m_activeByte+16) < max) + SelectByte(m_activeByte+16); + else + SelectByte(max-1); +} + +void CHexvisDlg::OnLoadFile() +{ + CFileDialog *fileopendialog = new CFileDialog( true, NULL, NULL, + OFN_HIDEREADONLY , + "Any Raw File (*.*)|*.*||", this ); + + if ( fileopendialog->DoModal() == IDOK ) { + HANDLE hFile; + hFile = CreateFile(fileopendialog->GetPathName(), + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + if(hFile == INVALID_HANDLE_VALUE) { + m_status.SetWindowText("Failed to open file."); + return; + } + + int size = GetFileSize(hFile, NULL); + + m_offsetStart = 0; + m_digits.clear(); + m_digits.resize(size); + m_lineStarts.clear(); + + m_processed = false; + + DWORD out; + if(!ReadFile(hFile, &m_digits[0], size, &out, NULL)) { + m_status.SetWindowText("Failed to load file."); + m_digits.resize(0); + return; + } + + CloseHandle(hFile); + + DisplayDigits(); + + m_processed = true; + + //this will trigger the sel change + m_packetBody.SetSel(8, 9); + + m_status.SetWindowText("Press clear to process another block"); + } +} + +void CHexvisDlg::OnCopyToClip() +{ + bool v = m_processed; + if(v) { + m_processed = false; + } + + m_packetBody.SetSel(0, -1); + m_packetBody.Copy(); + + if(v) { + m_processed = true; + m_packetBody.SetSel(8, 9); + } +} diff --git a/utils/deprecated/hexvis/hexvisDlg.h b/utils/deprecated/hexvis/hexvisDlg.h new file mode 100644 index 000000000..12912fc37 --- /dev/null +++ b/utils/deprecated/hexvis/hexvisDlg.h @@ -0,0 +1,100 @@ +// hexvisDlg.h : header file +// + +#if !defined(AFX_HEXVISDLG_H__CA785941_36B6_4928_A1EB_B189A9AE2EA2__INCLUDED_) +#define AFX_HEXVISDLG_H__CA785941_36B6_4928_A1EB_B189A9AE2EA2__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "HexRichEdit.h" + +#include + +///////////////////////////////////////////////////////////////////////////// +// CHexvisDlg dialog + +class CHexvisDlg : public CDialog +{ +// Construction +public: + CHexvisDlg(CWnd* pParent = NULL); // standard constructor + + void PrevByte(); + void NextByte(); + void PrevLine(); + void NextLine(); + +// Dialog Data + //{{AFX_DATA(CHexvisDlg) + enum { IDD = IDD_HEXVIS_DIALOG }; + CStatic m_status; + CEdit m_binRep; + CEdit m_searchNumber; + CEdit m_offset; + CEdit m_unix_time; + CEdit m_string; + CEdit m_short_le; + CEdit m_short_be; + CEdit m_real; + HexRichEdit m_packetBody; + CEdit m_long_le; + CEdit m_long_be; + CEdit m_int64_le; + CEdit m_int64_be; + CEdit m_byte; + //}}AFX_DATA + + // ClassWizard generated virtual function overrides + //{{AFX_VIRTUAL(CHexvisDlg) + protected: + virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support + //}}AFX_VIRTUAL + +// Implementation +protected: + HICON m_hIcon; + + bool m_processed; + int m_offsetStart; + int m_activeByte; + std::vector m_digits; + std::vector m_lineStarts; + + void ProcessLine(const char *line, int len); + bool _ProcessDigits(const char *startnum, int numlen); + + void DisplayDigits(); + void AnalyzeBytes(int byte); + void SelectByte(int byte); + void build_hex_line(const char *buffer, unsigned long length, unsigned long offset, unsigned char padding); + + typedef struct { + unsigned __int64 intval; + double dval; + float fval; + } FindSpec; + bool FindNumber(int offset, const FindSpec *f); + + // Generated message map functions + //{{AFX_MSG(CHexvisDlg) + virtual BOOL OnInitDialog(); + afx_msg void OnSysCommand(UINT nID, LPARAM lParam); + afx_msg void OnPaint(); + afx_msg HCURSOR OnQueryDragIcon(); + afx_msg void OnProcess(); + afx_msg void OnUpdatePacketBody(); + afx_msg void OnSelchangePacketBody(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnClear(); + afx_msg void OnFindNumber(); + afx_msg void OnLoadFile(); + afx_msg void OnCopyToClip(); + //}}AFX_MSG + DECLARE_MESSAGE_MAP() +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_HEXVISDLG_H__CA785941_36B6_4928_A1EB_B189A9AE2EA2__INCLUDED_) diff --git a/utils/deprecated/hexvis/res/hexvis.ico b/utils/deprecated/hexvis/res/hexvis.ico new file mode 100644 index 0000000000000000000000000000000000000000..7eef0bcbe6580a6f464d688906172c2d9de44262 GIT binary patch literal 1078 zcmc&zF>b>!3}jLb9s)T}@Kod(893@u8ajANzT`op9^o+)S?=nU(FD@%0s)Sg^oyC8{H z9myetc;MEP)59v(LMa~xK8Yu^jIR*H22uCFiq5%C{s7(PJi>o15i^bmX4(vPxWAio z9ryY#AU_jfnd047-@`)XzL?%iS$gQyFP{44kS9X)fN{{QoL~hO-&=q&20Zr*cxFAt PkaNE{wR~2C$NfnjhSXWT literal 0 HcmV?d00001 diff --git a/utils/deprecated/hexvis/res/hexvis.rc2 b/utils/deprecated/hexvis/res/hexvis.rc2 new file mode 100644 index 000000000..7748790a4 --- /dev/null +++ b/utils/deprecated/hexvis/res/hexvis.rc2 @@ -0,0 +1,13 @@ +// +// HEXVIS.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED + #error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/utils/deprecated/hexvis/resource.h b/utils/deprecated/hexvis/resource.h new file mode 100644 index 000000000..0a32e0780 --- /dev/null +++ b/utils/deprecated/hexvis/resource.h @@ -0,0 +1,40 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by hexvis.rc +// +#define IDM_ABOUTBOX 0x0010 +#define IDD_ABOUTBOX 100 +#define IDS_ABOUTBOX 101 +#define IDD_HEXVIS_DIALOG 102 +#define IDR_MAINFRAME 128 +#define IDC_PACKET_BODY 1000 +#define IDC_PROCESS 1001 +#define IDC_BYTE 1002 +#define IDC_SHORT_BE 1003 +#define IDC_SHORT_LE 1004 +#define IDC_LONG_BE 1005 +#define IDC_LONG_LE 1006 +#define IDC_UNIX_TIME 1007 +#define IDC_REAL 1008 +#define IDC_INT64_BE 1009 +#define IDC_INT64_LE 1010 +#define IDC_STRING 1011 +#define IDC_OFFSET 1012 +#define IDC_CLEAR 1013 +#define IDC_SEARCH_NUMBER 1014 +#define IDC_FIND_NUMBER 1015 +#define IDC_BIN_REP 1016 +#define IDC_STATE 1017 +#define IDC_LOAD_FILE 1018 +#define IDC_COPY_TO_CLIP 1019 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1018 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/utils/deprecated/items-0.6.0-DR2-0.6.1-DR1-convert.sql b/utils/deprecated/items-0.6.0-DR2-0.6.1-DR1-convert.sql new file mode 100644 index 000000000..1a30787e5 --- /dev/null +++ b/utils/deprecated/items-0.6.0-DR2-0.6.1-DR1-convert.sql @@ -0,0 +1,80 @@ +alter table items change unknown021 UNK012 int NOT NULL DEFAULT '0'; +alter table items change unknown022 UNK013 int NOT NULL DEFAULT '0'; +alter table items change unknown023 benefitflag int NOT NULL DEFAULT '0'; +alter table items change unknown064 UNK054 int NOT NULL DEFAULT '0'; +alter table items change unknown070 UNK059 int NOT NULL DEFAULT '0'; +alter table items change unknown072 UNK061 int NOT NULL DEFAULT '0'; +alter table items change combatskill extradmgskill int NOT NULL DEFAULT '0'; +alter table items change combatskilldmg extradmgamt int NOT NULL DEFAULT '0'; +alter table items change booktype book int NOT NULL DEFAULT '0'; +alter table items change unknown108 booktype int NOT NULL DEFAULT '0'; +alter table items change banedmgamt2 banedmgraceamt int NOT NULL DEFAULT '0'; +alter table items change augmentrestriction augrestrict int NOT NULL DEFAULT '0'; +alter table items change tribute favor int NOT NULL DEFAULT '0'; +alter table items change attackbonus attack int NOT NULL DEFAULT '0'; +alter table items change hpregen regen int NOT NULL DEFAULT '0'; +alter table items change hastepercent haste int NOT NULL DEFAULT '0'; +alter table items change unknown125 recastdelay int NOT NULL DEFAULT '0'; +alter table items change unknown126 recasttype int NOT NULL DEFAULT '0'; +alter table items change unknown127 guildfavor int NOT NULL DEFAULT '0'; +alter table items change distiller augdistiller int NOT NULL DEFAULT '0'; +alter table items change unknown129 UNK117 int NOT NULL DEFAULT '0'; +alter table items change unknown130 UNK118 int NOT NULL DEFAULT '0'; +alter table items change attuneable old_attuneable int NOT NULL DEFAULT '0'; +alter table items change unknown131 attuneable int NOT NULL DEFAULT '0'; +alter table items change unknown132 nopet int NOT NULL DEFAULT '0'; +alter table items add UNK121 int NOT NULL DEFAULT '0'; +alter table items add pointtype int NOT NULL DEFAULT '0'; +alter table items add potionbelt int NOT NULL DEFAULT '0'; +alter table items add UNK124 int NOT NULL DEFAULT '0'; +alter table items add stacksize int NOT NULL DEFAULT '0'; +alter table items change hasteproclvl level int NOT NULL DEFAULT '0'; +alter table items change spellid clickeffect int NOT NULL DEFAULT '0'; +alter table items change focusid focuseffect int NOT NULL DEFAULT '0'; +alter table items change effecttype clicktype int NOT NULL DEFAULT '0'; +alter table items change level clicklevel int NOT NULL DEFAULT '0'; +alter table items add UNK129 int NOT NULL DEFAULT '0'; +alter table items add proceffect int NOT NULL DEFAULT '0'; +alter table items add proctype int NOT NULL DEFAULT '0'; +alter table items add proclevel int NOT NULL DEFAULT '0'; +alter table items add UNK133 int NOT NULL DEFAULT '0'; +alter table items add worneffect int NOT NULL DEFAULT '0'; +alter table items add worntype int NOT NULL DEFAULT '0'; +alter table items add wornlevel int NOT NULL DEFAULT '0'; +alter table items add UNK137 int NOT NULL DEFAULT '0'; +alter table items add focustype int NOT NULL DEFAULT '0'; +alter table items add focuslevel int NOT NULL DEFAULT '0'; +alter table items add UNK141 int NOT NULL DEFAULT '0'; +alter table items add scrolleffect int NOT NULL DEFAULT '0'; +alter table items add scrolltype int NOT NULL DEFAULT '0'; +alter table items add scrolllevel int NOT NULL DEFAULT '0'; +alter table items add UNK145 int NOT NULL DEFAULT '0'; +alter table items change cost price int NOT NULL DEFAULT '0'; +alter table items change casttime2 casttime_ int NOT NULL DEFAULT '0'; +alter table items change proc_rate_mod procrate int NOT NULL DEFAULT '0'; +alter table items change charmformula charmfileid varchar(32) NOT NULL DEFAULT ''; +alter table items modify filename varchar(32) NOT NULL DEFAULT ''; +alter table items change ldonpointtheme ldontheme int NOT NULL DEFAULT '0'; +alter table items change ldonpointcost ldonprice int NOT NULL DEFAULT '0'; +alter table items change gmflag fvnodrop int NOT NULL DEFAULT '0'; +alter table items add serialized datetime; +alter table items add verified datetime; +alter table items add serialization text; +alter table items add source varchar(20) NOT NULL; + +alter table items add UNK033 int NOT NULL DEFAULT '0'; + +alter table items drop column charges; +alter table items drop column unknown001; +alter table items drop column collected_slot_id; +alter table items drop column merchantprice; +alter table items drop column unknown004; +alter table items drop column collected_inst_id; +alter table items drop column unknown006; +alter table items drop column spellcharges; +alter table items drop column old_attuneable; + +update items set proctype=clicktype,proceffect=clickeffect,proclevel=clicklevel,clicktype=0,clickeffect=0,clicklevel=0 where clickeffect>0 and clicktype =0; +update items set worntype=clicktype,proceffect=clickeffect,proclevel=clicklevel,clicktype=0,clickeffect=0,clicklevel=0 where clickeffect>0 and clicktype =2; +update items set focustype=6 where focuseffect>0; +update items set scrolltype=7,scrolleffect=clickeffect,scrolllevel=clicklevel,clicktype=0,clickeffect=0,clicklevel=0 where clickeffect>0 and itemtype =20; diff --git a/utils/deprecated/itemtablechanges.sql b/utils/deprecated/itemtablechanges.sql new file mode 100644 index 000000000..714ce7364 --- /dev/null +++ b/utils/deprecated/itemtablechanges.sql @@ -0,0 +1,22 @@ +--Obsolete if you have the latest items db. + +ALTER TABLE `items` ADD `attackbonus` INT(11) DEFAULT "0" NOT NULL AFTER `astr`; +ALTER TABLE `items` CHANGE `unknown108` `augmentrestriction` INT(11) DEFAULT "0" NOT NULL; +ALTER TABLE `items` CHANGE `unknown107` `banedmgamt2` INT(11) DEFAULT "0" NOT NULL; +ALTER TABLE `items` ADD `charges` INT(11) DEFAULT "0" NOT NULL AFTER `casttime2`; +ALTER TABLE `items` CHANGE `unknown076` `combatskill` INT(11) DEFAULT "0" NOT NULL; +ALTER TABLE `items` CHANGE `unknown077` `combatskilldmg` INT(11) DEFAULT "0" NOT NULL; +ALTER TABLE `items` ADD `damageshield` INT(11) DEFAULT "0" NOT NULL AFTER `damage`; +ALTER TABLE `items` ADD `dotshielding` INT(11) DEFAULT "0" NOT NULL AFTER `damageshield`; +ALTER TABLE `items` ADD `endur` INT(11) DEFAULT "0" NOT NULL AFTER `damageshield`; +ALTER TABLE `items` ADD `hastepercent` INT(11) DEFAULT "0" NOT NULL AFTER `gmflag`; +ALTER TABLE `items` ADD `hpregen` INT(11) DEFAULT "0" NOT NULL AFTER `hp`; +ALTER TABLE `items` CHANGE `unknown099` `ldonsold` INT(11) DEFAULT "0" NOT NULL; +ALTER TABLE `items` ADD `manaregen` INT(11) DEFAULT "0" NOT NULL AFTER `mana`; +ALTER TABLE `items` CHANGE `unknown004` `merchantprice` INT(11) DEFAULT "0" NOT NULL; +ALTER TABLE `items` CHANGE `unknown070` `proc_rate_mod` INT(11) DEFAULT "0" NOT NULL; +ALTER TABLE `items` ADD `unknown003` INT(11) DEFAULT "0" NOT NULL; +ALTER TABLE `items` ADD `unknown006` INT(11) DEFAULT "0" NOT NULL; +ALTER TABLE `items` ADD `unknown007` INT(11) DEFAULT "0" NOT NULL; +update items set filename='' where filename='0'; +update items set charmfile='' where charmfile='0'; \ No newline at end of file diff --git a/utils/deprecated/perlxs/EQDB.h b/utils/deprecated/perlxs/EQDB.h new file mode 100644 index 000000000..73375705f --- /dev/null +++ b/utils/deprecated/perlxs/EQDB.h @@ -0,0 +1,9 @@ + //NOTE: you must have a space after the * of a return value + + unsigned int field_count(); + unsigned long affected_rows(); + unsigned long insert_id(); + unsigned int get_errno(); + Const_char * error(); + EQDBRes * query(Const_char *q); + Const_char * escape_string(Const_char *from); //NOT THREAD SAFE! (m_escapeBuffer) diff --git a/utils/deprecated/perlxs/EQDB.h.xs b/utils/deprecated/perlxs/EQDB.h.xs new file mode 100644 index 000000000..fb1b3af33 --- /dev/null +++ b/utils/deprecated/perlxs/EQDB.h.xs @@ -0,0 +1,37 @@ + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 +*/ + +typedef const char Const_char; + +#ifdef EMBPERL +#include "../common/debug.h" +#include "../common/useperl.h" +#include "EQDB.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +MODULE = EQDB PACKAGE = EQDB + +PROTOTYPES: ENABLE + + + diff --git a/utils/deprecated/perlxs/EQDBRes.h b/utils/deprecated/perlxs/EQDBRes.h new file mode 100644 index 000000000..67a5554ee --- /dev/null +++ b/utils/deprecated/perlxs/EQDBRes.h @@ -0,0 +1,7 @@ + unsigned long num_rows() { return (res) ? mysql_num_rows(res) : 0; } + unsigned long num_fields() { return (res) ? mysql_num_fields(res) : 0; } + void DESTROY() { } + void finish() { if (res) mysql_free_result(res); res=NULL; }; + vector fetch_row_array(); + map fetch_row_hash(); + unsigned long * fetch_lengths() { return (res) ? mysql_fetch_lengths(res) : 0; } diff --git a/utils/deprecated/perlxs/EQDBRes.h.xs b/utils/deprecated/perlxs/EQDBRes.h.xs new file mode 100644 index 000000000..8b50e1f8b --- /dev/null +++ b/utils/deprecated/perlxs/EQDBRes.h.xs @@ -0,0 +1,33 @@ + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 +*/ + +typedef const char Const_char; + +#ifdef EMBPERL +#include "../common/debug.h" +#include "../common/useperl.h" +#include "EQDBRes.h" + + +MODULE = EQDBRes PACKAGE = EQDBRes + +PROTOTYPES: ENABLE + + + diff --git a/utils/deprecated/perlxs/EQLConfig.h b/utils/deprecated/perlxs/EQLConfig.h new file mode 100644 index 000000000..b0f352834 --- /dev/null +++ b/utils/deprecated/perlxs/EQLConfig.h @@ -0,0 +1,19 @@ + Const_char * GetName() const { return(m_name.c_str()); } + int GetStaticCount() const { return(m_zones.size()); } + bool IsConnected() const; //is this launcher connected to world + + void DeleteLauncher(); //kill this launcher and all its zones. + + void RestartZone(Const_char *zone_ref); + void StopZone(Const_char *zone_ref); + void StartZone(Const_char *zone_ref); + + bool BootStaticZone(Const_char *short_name, uint16 port); + bool ChangeStaticZone(Const_char *short_name, uint16 port); + bool DeleteStaticZone(Const_char *short_name); + + bool SetDynamicCount(int count); + int GetDynamicCount() const; + + vector ListZones(); //returns an array of zone refs + map GetZoneDetails(Const_char *zone_ref); diff --git a/utils/deprecated/perlxs/EQLConfig.h.xs b/utils/deprecated/perlxs/EQLConfig.h.xs new file mode 100644 index 000000000..7d5c4d797 --- /dev/null +++ b/utils/deprecated/perlxs/EQLConfig.h.xs @@ -0,0 +1,37 @@ + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 +*/ + +typedef const char Const_char; + +#ifdef EMBPERL +#include "../common/debug.h" +#include "EQWParser.h" +#include "EQLConfig.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +MODULE = EQLConfig PACKAGE = EQLConfig + +PROTOTYPES: ENABLE + + + diff --git a/utils/deprecated/perlxs/EQW.h b/utils/deprecated/perlxs/EQW.h new file mode 100644 index 000000000..e1dc00b8d --- /dev/null +++ b/utils/deprecated/perlxs/EQW.h @@ -0,0 +1,36 @@ + //NOTE: you must have a space after the * of a return value + Const_char * GetConfig(Const_char *var_name); + void LockWorld(); + void UnlockWorld(); + + bool LSConnected(); + void LSReconnect(); + + int CountZones(); + vector ListBootedZones(); //returns an array of zone_refs (opaque) + map GetZoneDetails(Const_char *zone_ref); //returns a hash ref of details + + int CountPlayers(); + vector ListPlayers(Const_char *zone_name = ""); //returns an array of player refs (opaque) + map GetPlayerDetails(Const_char *player_ref); //returns a hash ref of details + + int CountLaunchers(bool active_only); +// vector ListActiveLaunchers(); //returns an array of launcher names + vector ListLaunchers(); //returns an array of launcher names + EQLConfig * GetLauncher(Const_char *launcher_name); //returns the EQLConfig object for the specified launcher. + void CreateLauncher(Const_char *launcher_name, int dynamic_count); +// EQLConfig * FindLauncher(Const_char *zone_ref); + + //Guild routines, mostly wrappers around guild_mgr + int32 CreateGuild(const char* name, int32 leader_char_id); + bool DeleteGuild(int32 guild_id); + bool RenameGuild(int32 guild_id, const char* name); + bool SetGuildMOTD(int32 guild_id, const char* motd, const char *setter); + bool SetGuildLeader(int32 guild_id, int32 leader_char_id); + bool SetGuild(int32 charid, int32 guild_id, int8 rank); + bool SetGuildRank(int32 charid, int8 rank); + bool SetBankerFlag(int32 charid, bool is_banker); + bool SetTributeFlag(int32 charid, bool enabled); + bool SetPublicNote(int32 charid, const char *note); + + diff --git a/utils/deprecated/perlxs/EQW.h.xs b/utils/deprecated/perlxs/EQW.h.xs new file mode 100644 index 000000000..af40c9abd --- /dev/null +++ b/utils/deprecated/perlxs/EQW.h.xs @@ -0,0 +1,37 @@ + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 +*/ + +typedef const char Const_char; + +#ifdef EMBPERL +#include "../common/debug.h" +#include "EQWParser.h" +#include "EQW.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +MODULE = EQW PACKAGE = EQW + +PROTOTYPES: ENABLE + + + diff --git a/utils/deprecated/perlxs/HTTPRequest.h b/utils/deprecated/perlxs/HTTPRequest.h new file mode 100644 index 000000000..4a4bdd38a --- /dev/null +++ b/utils/deprecated/perlxs/HTTPRequest.h @@ -0,0 +1,14 @@ + + + Const_char * get(Const_char *name, Const_char *default_value = "") const; + int getInt(Const_char *name, int default_value = 0) const; + float getFloat(Const_char *name, float default_value = 0.0) const; + + //returns a database-safe string + Const_char * getEscaped(Const_char *name, Const_char *default_value = "") const; + + map get_all() const; + + void redirect(Const_char *URL); + void SetResponseCode(Const_char *code); + void header(Const_char *name, Const_char *value); diff --git a/utils/deprecated/perlxs/HTTPRequest.h.xs b/utils/deprecated/perlxs/HTTPRequest.h.xs new file mode 100644 index 000000000..1ce1a26b9 --- /dev/null +++ b/utils/deprecated/perlxs/HTTPRequest.h.xs @@ -0,0 +1,37 @@ + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 +*/ + +typedef const char Const_char; + +#ifdef EMBPERL +#include "../common/debug.h" +#include "EQWParser.h" +#include "HTTPRequest.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +MODULE = HTTPRequest PACKAGE = HTTPRequest + +PROTOTYPES: ENABLE + + + diff --git a/utils/deprecated/perlxs/PlayerCorpse.h b/utils/deprecated/perlxs/PlayerCorpse.h new file mode 100644 index 000000000..e20c1ae71 --- /dev/null +++ b/utils/deprecated/perlxs/PlayerCorpse.h @@ -0,0 +1,28 @@ + int32 GetCharID() { return charid; } + int32 GetDecayTime() { if (!corpse_decay_timer.Enabled()) return 0xFFFFFFFF; else return corpse_decay_timer.GetRemainingTime(); } + inline void Lock() { pLocked = true; } + inline void UnLock() { pLocked = false; } + inline bool IsLocked() { return pLocked; } + inline void ResetLooter() { BeingLootedBy = 0xFFFFFFFF; } + inline int32 GetDBID() { return dbid; } + inline char* GetOwnerName() { return orgname;} + void SetDecayTimer(int32 decaytime); + bool IsEmpty(); + void AddItem(uint32 itemnum, int8 charges, sint16 slot = 0); + uint32 GetWornItem(sint16 equipSlot) const; + void RemoveItem(int16 lootslot); + void SetCash(int16 in_copper, int16 in_silver, int16 in_gold, int16 in_platinum); + void RemoveCash(); + int32 CountItems(); + void Delete(); + uint32 GetCopper() { return copper; } + uint32 GetSilver() { return silver; } + uint32 GetGold() { return gold; } + uint32 GetPlatinum() { return platinum; } + void Summon(Client* client, bool spell); + void CastRezz(int16 spellid, Mob* Caster); + void CompleteRezz(); + bool CanMobLoot(int charid); + void AllowMobLoot(Mob *them, int8 slot); + void AddLooter(Mob *who); + bool IsRezzed() { return isrezzed; } diff --git a/utils/deprecated/perlxs/PlayerCorpse.h.xs b/utils/deprecated/perlxs/PlayerCorpse.h.xs new file mode 100644 index 000000000..d25570ff9 --- /dev/null +++ b/utils/deprecated/perlxs/PlayerCorpse.h.xs @@ -0,0 +1,37 @@ + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +#include "PlayerCorpse.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +MODULE = Corpse PACKAGE = Corpse + +PROTOTYPES: ENABLE + + + diff --git a/utils/deprecated/perlxs/all_convert b/utils/deprecated/perlxs/all_convert new file mode 100644 index 000000000..64c4191e8 --- /dev/null +++ b/utils/deprecated/perlxs/all_convert @@ -0,0 +1,27 @@ +#!/bin/sh + +if [ ! -h ./miniperl ]; then + echo "You must have a link in this dir to perl named ./miniperl" + exit 1 +fi + +./convert client.h Client +./convert entity.h EntityList +./convert mob.h Mob +./convert npc.h NPC +./convert PlayerCorpse.h Corpse +./convert groups.h Group +./convert perlpacket.h PerlPacket +./export ../../world/EQW.h EQW.h +./convert EQW.h EQW +./export ../../common/EQDB.h EQDB.h +./convert EQDB.h EQDB +./export ../../common/EQDBRes.h EQDBRes.h +./convert EQDBRes.h EQDBRes +./export ../../world/HTTPRequest.h HTTPRequest.h +./convert HTTPRequest.h HTTPRequest +./export ../../world/EQLConfig.h EQLConfig.h +./convert EQLConfig.h EQLConfig + +echo "Putting files in place..." +./put diff --git a/utils/deprecated/perlxs/class.sh b/utils/deprecated/perlxs/class.sh new file mode 100644 index 000000000..917851fcf --- /dev/null +++ b/utils/deprecated/perlxs/class.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +xsubpp -typemap class.typemap class.xs diff --git a/utils/deprecated/perlxs/class.typemap b/utils/deprecated/perlxs/class.typemap new file mode 100644 index 000000000..bc475e8ed --- /dev/null +++ b/utils/deprecated/perlxs/class.typemap @@ -0,0 +1,442 @@ +#our object types +EQLConfig * T_PTROBJ +EQDB * T_PTROBJ +EQDBRes * T_PTROBJ +Client * T_PTROBJ +NPC * T_PTROBJ +Mob * T_PTROBJ +Group * T_PTROBJ +Corpse * T_PTROBJ +EntityList * T_PTROBJ +PerlPacket * T_PTROBJ +EQW * T_PTROBJ +HTTPRequest * T_PTROBJ +Const_char * T_PV + +#some nice c++ types +vector T_STRINGARRAY +map T_STRINGMAP + +#our int types +FACTION_VALUE T_IV +EmuAppearance T_UV +SkillType T_UV +Mob::eStandingPetOrder T_IV +int32 T_UV +int16 T_UV +int8 T_UV +uint32 T_UV +uint16 T_UV +uint8 T_UV +sint32 T_IV +sint16 T_IV +sint8 T_IV + +int * T_INTARRAY +unsigned long * T_INTARRAY + +# basic C types +int T_IV +unsigned T_UV +unsigned int T_UV +long T_IV +unsigned long T_UV +unsigned long long T_UV +short T_IV +unsigned short T_UV +char T_CHAR +unsigned char T_U_CHAR +char * T_PV +unsigned char * T_PV +const char * T_PV +caddr_t T_PV +wchar_t * T_PV +wchar_t T_IV +# bool_t is defined in +bool_t T_IV +size_t T_UV +ssize_t T_IV +time_t T_NV +unsigned long * T_OPAQUEPTR +char ** T_PACKEDARRAY +void * T_PTR +Time_t * T_PV +SV * T_SV +SVREF T_SVREF +AV * T_AVREF +HV * T_HVREF +CV * T_CVREF + +IV T_IV +UV T_UV +NV T_NV +I32 T_IV +I16 T_IV +I8 T_IV +STRLEN T_UV +U32 T_U_LONG +U16 T_U_SHORT +U8 T_UV +Result T_U_CHAR +Boolean T_BOOL +float T_FLOAT +double T_DOUBLE +SysRet T_SYSRET +SysRetLong T_SYSRET +FILE * T_STDIO +PerlIO * T_INOUT +FileHandle T_PTROBJ +InputStream T_IN +InOutStream T_INOUT +OutputStream T_OUT +bool T_BOOL + +############################################################################# +INPUT +T_SV + $var = $arg +T_SVREF + if (SvROK($arg)) + $var = (SV*)SvRV($arg); + else + Perl_croak(aTHX_ \"$var is not a reference\"); +T_AVREF + if (SvROK($arg) && SvTYPE(SvRV($arg))==SVt_PVAV) + $var = (AV*)SvRV($arg); + else + Perl_croak(aTHX_ \"$var is not an array reference\"); +T_HVREF + if (SvROK($arg) && SvTYPE(SvRV($arg))==SVt_PVHV) + $var = (HV*)SvRV($arg); + else + Perl_croak(aTHX_ \"$var is not a hash reference\"); +T_CVREF + if (SvROK($arg) && SvTYPE(SvRV($arg))==SVt_PVCV) + $var = (CV*)SvRV($arg); + else + Perl_croak(aTHX_ \"$var is not a code reference\"); +T_SYSRET + $var NOT IMPLEMENTED +T_UV + $var = ($type)SvUV($arg) +T_IV + $var = ($type)SvIV($arg) +T_INT + $var = (int)SvIV($arg) +T_ENUM + $var = ($type)SvIV($arg) +T_BOOL + $var = (bool)SvTRUE($arg) +T_U_INT + $var = (unsigned int)SvUV($arg) +T_SHORT + $var = (short)SvIV($arg) +T_U_SHORT + $var = (unsigned short)SvUV($arg) +T_LONG + $var = (long)SvIV($arg) +T_U_LONG + $var = (unsigned long)SvUV($arg) +T_CHAR + $var = (char)*SvPV_nolen($arg) +T_U_CHAR + $var = (unsigned char)SvUV($arg) +T_FLOAT + $var = (float)SvNV($arg) +T_NV + $var = ($type)SvNV($arg) +T_DOUBLE + $var = (double)SvNV($arg) +T_PV + $var = ($type)SvPV_nolen($arg) +T_PTR + $var = INT2PTR($type,SvIV($arg)) +T_PTRREF + if (SvROK($arg)) { + IV tmp = SvIV((SV*)SvRV($arg)); + $var = INT2PTR($type,tmp); + } + else + Perl_croak(aTHX_ \"$var is not a reference\"); + if($var == NULL) + Perl_croak(aTHX_ \"$var is NULL, avoiding crash.\"); +T_REF_IV_REF + if (sv_isa($arg, \"${ntype}\")) { + IV tmp = SvIV((SV*)SvRV($arg)); + $var = *INT2PTR($type *, tmp); + } + else + Perl_croak(aTHX_ \"$var is not of type ${ntype}\"); +T_REF_IV_PTR + if (sv_isa($arg, \"${ntype}\")) { + IV tmp = SvIV((SV*)SvRV($arg)); + $var = INT2PTR($type, tmp); + } + else + Perl_croak(aTHX_ \"$var is not of type ${ntype}\"); + if($var == NULL) + Perl_croak(aTHX_ \"$var is NULL, avoiding crash.\"); +T_PTROBJ + if (sv_derived_from($arg, \"${ntype}\")) { + IV tmp = SvIV((SV*)SvRV($arg)); + $var = INT2PTR($type,tmp); + } + else + Perl_croak(aTHX_ \"$var is not of type ${ntype}\"); + if($var == NULL) + Perl_croak(aTHX_ \"$var is NULL, avoiding crash.\"); +T_PTRDESC + if (sv_isa($arg, \"${ntype}\")) { + IV tmp = SvIV((SV*)SvRV($arg)); + ${type}_desc = (\U${type}_DESC\E*) tmp; + $var = ${type}_desc->ptr; + } + else + Perl_croak(aTHX_ \"$var is not of type ${ntype}\"); + if($var == NULL) + Perl_croak(aTHX_ \"$var is NULL, avoiding crash.\"); +T_REFREF + if (SvROK($arg)) { + IV tmp = SvIV((SV*)SvRV($arg)); + $var = *INT2PTR($type,tmp); + } + else + Perl_croak(aTHX_ \"$var is not a reference\"); + if($var == NULL) + Perl_croak(aTHX_ \"$var is NULL, avoiding crash.\"); +T_REFOBJ + if (sv_isa($arg, \"${ntype}\")) { + IV tmp = SvIV((SV*)SvRV($arg)); + $var = *INT2PTR($type,tmp); + } + else + Perl_croak(aTHX_ \"$var is not of type ${ntype}\"); + if($var == NULL) + Perl_croak(aTHX_ \"$var is NULL, avoiding crash.\"); +T_OPAQUE + $var = *($type *)SvPV_nolen($arg) +T_OPAQUEPTR + $var = ($type)SvPV_nolen($arg) +T_PACKED + $var = XS_unpack_$ntype($arg) +T_PACKEDARRAY + $var = XS_unpack_$ntype($arg) +T_CALLBACK + $var = make_perl_cb_$type($arg) +T_ARRAY + U32 ix_$var = $argoff; + $var = $ntype(items -= $argoff); + while (items--) { + DO_ARRAY_ELEM; + ix_$var++; + } + /* this is the number of elements in the array */ + ix_$var -= $argoff +T_INTARRAY + AV *av_$var; + if (SvROK($arg) && SvTYPE(SvRV($arg))==SVt_PVAV) + av_$var = (AV*)SvRV($arg); + else + Perl_croak(aTHX_ \"$var is not an array reference\"); + I32 len_$var = av_len(av_$var) + 1; + I32 ix_$var; + $var = new $ntype\[len_$var\]; + for(ix_$var = 0; ix_$var < len_$var; ix_$var ++) { + SV **tmp = av_fetch(av_$var, ix_$var, 0); + if(tmp == NULL || *tmp == NULL) { + $var\[ix_$var\] = 0; + continue; + } + $var\[ix_$var\] = ($ntype)SvIV(*tmp); + } +T_STDIO + $var = PerlIO_findFILE(IoIFP(sv_2io($arg))) +T_IN + $var = IoIFP(sv_2io($arg)) +T_INOUT + $var = IoIFP(sv_2io($arg)) +T_OUT + $var = IoOFP(sv_2io($arg)) +T_STRINGARRAY + $var NOT IMPLEMENTED +T_STRINGMAP + $var NOT IMPLEMENTED + + +############################################################################# +OUTPUT +T_SV + $arg = $var; +T_SVREF + $arg = newRV((SV*)$var); +T_AVREF + $arg = newRV((SV*)$var); +T_HVREF + $arg = newRV((SV*)$var); +T_CVREF + $arg = newRV((SV*)$var); +T_IV + sv_setiv($arg, (IV)$var); +T_UV + sv_setuv($arg, (UV)$var); +T_INT + sv_setiv($arg, (IV)$var); +T_SYSRET + if ($var != -1) { + if ($var == 0) + sv_setpvn($arg, "0 but true", 10); + else + sv_setiv($arg, (IV)$var); + } +T_ENUM + sv_setiv($arg, (IV)$var); +T_BOOL + $arg = boolSV($var); +T_U_INT + sv_setuv($arg, (UV)$var); +T_SHORT + sv_setiv($arg, (IV)$var); +T_U_SHORT + sv_setuv($arg, (UV)$var); +T_LONG + sv_setiv($arg, (IV)$var); +T_U_LONG + sv_setuv($arg, (UV)$var); +T_CHAR + sv_setpvn($arg, (char *)&$var, 1); +T_U_CHAR + sv_setuv($arg, (UV)$var); +T_FLOAT + sv_setnv($arg, (double)$var); +T_NV + sv_setnv($arg, (NV)$var); +T_DOUBLE + sv_setnv($arg, (double)$var); +T_PV + sv_setpv((SV*)$arg, $var); +T_PTR + sv_setiv($arg, PTR2IV($var)); +T_PTRREF + sv_setref_pv($arg, Nullch, (void*)$var); +T_REF_IV_REF + sv_setref_pv($arg, \"${ntype}\", (void*)new $ntype($var)); +T_REF_IV_PTR + sv_setref_pv($arg, \"${ntype}\", (void*)$var); +T_PTROBJ + sv_setref_pv($arg, \"${ntype}\", (void*)$var); +T_PTRDESC + sv_setref_pv($arg, \"${ntype}\", (void*)new\U${type}_DESC\E($var)); +T_REFREF + NOT_IMPLEMENTED +T_REFOBJ + NOT IMPLEMENTED +T_OPAQUE + sv_setpvn($arg, (char *)&$var, sizeof($var)); +T_OPAQUEPTR + sv_setpvn($arg, (char *)$var, sizeof(*$var)); +T_PACKED + XS_pack_$ntype($arg, $var); +T_PACKEDARRAY + XS_pack_$ntype($arg, $var, count_$ntype); +T_DATAUNIT + sv_setpvn($arg, $var.chp(), $var.size()); +T_CALLBACK + sv_setpvn($arg, $var.context.value().chp(), + $var.context.value().size()); +T_ARRAY + { + U32 ix_$var; + EXTEND(SP,size_$var); + for (ix_$var = 0; ix_$var < size_$var; ix_$var++) { + ST(ix_$var) = sv_newmortal(); + DO_ARRAY_ELEM + } + } +T_STDIO + { + GV *gv = newGVgen("$Package"); + PerlIO *fp = PerlIO_importFILE($var,0); + if ( fp && do_open(gv, "+<&", 3, FALSE, 0, 0, fp) ) + sv_setsv($arg, sv_bless(newRV((SV*)gv), gv_stashpv("$Package",1))); + else + $arg = &PL_sv_undef; + } +T_IN + { + GV *gv = newGVgen("$Package"); + if ( do_open(gv, "<&", 2, FALSE, 0, 0, $var) ) + sv_setsv($arg, sv_bless(newRV((SV*)gv), gv_stashpv("$Package",1))); + else + $arg = &PL_sv_undef; + } +T_INOUT + { + GV *gv = newGVgen("$Package"); + if ( do_open(gv, "+<&", 3, FALSE, 0, 0, $var) ) + sv_setsv($arg, sv_bless(newRV((SV*)gv), gv_stashpv("$Package",1))); + else + $arg = &PL_sv_undef; + } +T_OUT + { + GV *gv = newGVgen("$Package"); + if ( do_open(gv, "+>&", 3, FALSE, 0, 0, $var) ) + sv_setsv($arg, sv_bless(newRV((SV*)gv), gv_stashpv("$Package",1))); + else + $arg = &PL_sv_undef; + } +T_STRINGARRAY + { + U32 ix_$var; + /* pop crap off the stack we dont really want */ + POPs; + POPs; + /* grow the stack to the number of elements being returned */ + EXTEND(SP, $var.size()); + for (ix_$var = 0; ix_$var < $var.size(); ix_$var++) { + const string &it = $var\[ix_$var\]; + ST(ix_$var) = sv_newmortal(); + sv_setpvn(ST(ix_$var), it.c_str(), it.length()); + } + /* hackish, but im over it. The normal xsubpp return will be right below this */ + XSRETURN($var.size()); + } +T_STRINGMAP + if (RETVAL.begin()!=RETVAL.end()) + { + //NOTE: we are leaking the original $arg right now + HV *hv = newHV(); + sv_2mortal((SV*)hv); + $arg = newRV((SV*)hv); + + map::const_iterator cur, end; + cur = $var.begin(); + end = $var.end(); + for(; cur != end; cur++) { + /* get the element from the hash, creating if needed (will be needed) */ + SV**ele = hv_fetch(hv, cur->first.c_str(), cur->first.length(), TRUE); + if(ele == NULL) { + Perl_croak(aTHX_ \"Unable to create a hash element for $var\"); + break; + } + /* put our string in the SV associated with this element in the hash */ + sv_setpvn(*ele, cur->second.c_str(), cur->second.length()); + } + } + + + + + + + + + + + + + + + + + diff --git a/utils/deprecated/perlxs/client.h b/utils/deprecated/perlxs/client.h new file mode 100644 index 000000000..6c12e88a3 --- /dev/null +++ b/utils/deprecated/perlxs/client.h @@ -0,0 +1,177 @@ + +//BEGIN CLIENT METHODS + + void SendSound(); + bool Save(int8 iCommitNow); + void SaveBackup(); + + inline bool Connected(); + inline bool InZone(); + inline void Kick(); + inline void Disconnect(); + inline bool IsLD(); + void WorldKick(); + inline int8 GetAnon(); + void Duck(); + void Stand(); + + void SetGM(bool toggle); + void SetPVP(bool toggle); + inline bool GetPVP(); + inline bool GetGM(); + + inline void SetBaseClass(uint32 i); + inline void SetBaseRace(uint32 i); + inline void SetBaseGender(uint32 i); + + inline int8 GetBaseFace(); + + inline int8 GetLanguageSkill(int16 n); + inline Const_char * GetLastName(); + inline int32 GetLDoNPoints(); + + + inline int8 GetBaseSTR(); + inline int8 GetBaseSTA(); + inline int8 GetBaseCHA(); + inline int8 GetBaseDEX(); + inline int8 GetBaseINT(); + inline int8 GetBaseAGI(); + inline int8 GetBaseWIS(); + + int16 GetWeight(); + inline uint32 GetEXP(); + bool UpdateLDoNPoints(sint32 points, int32 theme); + inline void SetDeity(uint32 i); + + void AddEXP(uint32 add_exp, int8 conlevel = 0xFF, bool resexp = false); + void SetEXP(uint32 set_exp, uint32 set_aaxp, bool resexp=false); + + void SetBindPoint(int to_zone = -1, float new_x = 0.0f, float new_y = 0.0f, float new_z = 0.0f); + void MovePC(int32 zoneID, float x, float y, float z, float heading); + void ChangeLastName(const char* in_lastname); + + FACTION_VALUE GetFactionLevel(int32 char_id, int32 npc_id, int32 p_race, int32 p_class, int32 p_deity, sint32 pFaction, Mob* tnpc); + + void SetFactionLevel(int32 char_id, int32 npc_id, int8 char_class, int8 char_race, int8 char_deity); + void SetFactionLevel2(int32 char_id, sint32 faction_id, int8 char_class, int8 char_race, int8 char_deity, sint32 value); + sint16 GetRawItemAC(); + + inline int32 AccountID(); + inline Const_char * AccountName(); + inline sint16 Admin(); + inline int32 CharacterID(); + void UpdateAdmin(bool iFromDB = true); + void UpdateWho(int8 remove = 0); + + inline int8 GuildRank(); + inline int32 GuildID(); + + int8 GetFace(); + + bool TakeMoneyFromPP(uint32 copper); + void AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold,uint32 platinum,bool updateclient); + + bool TGB() {return tgb;} + + int16 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) { if (skill_id < 26) { m_pp.languages[skill_id] += value; } } + virtual uint16 GetSkill(SkillType 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(SkillType skill_id) const { if (skill_id <= HIGHEST_SKILL) { return(m_pp.skills[skill_id]); } return 0; } + bool HasSkill(SkillType skill_id) const; + bool CanHaveSkill(SkillType skill_id) const; + void SetSkill(SkillType skill_num, int8 value); // socket 12-29-01 + void AddSkill(SkillType skillid, int8 value); + void CheckSpecializeIncrease(int16 spell_id); + bool CheckIncreaseSkill(SkillType skillid, int chancemodi = 0); + void SetLanguageSkill(int langid, int value); + int8 MaxSkill(SkillType skillid); + + void GMKill(); + inline bool IsMedding(); + inline int16 GetDuelTarget(); + inline bool IsDueling(); + inline void SetDuelTarget(int16 set_id); + inline void SetDueling(bool duel); + + void ResetAA(); + + void MemSpell(int16 spell_id, int slot, bool update_client = true); + void UnmemSpell(int slot, bool update_client = true); + void UnmemSpellAll(bool update_client = true); + void ScribeSpell(int16 spell_id, int slot, bool update_client = true); + void UnscribeSpell(int slot, bool update_client = true); + void UnscribeSpellAll(bool update_client = true); + + inline bool IsSitting(); + inline bool IsBecomeNPC(); + inline int8 GetBecomeNPCLevel(); + inline void SetBecomeNPC(bool flag); + inline void SetBecomeNPCLevel(int8 level); + bool LootToStack(uint32 itemid); + void SetFeigned(bool in_feigned); + inline bool GetFeigned(); + + inline bool AutoSplitEnabled(); + + void SetHorseId(int16 horseid_in); + int16 GetHorseId(); + + uint32 NukeItem(uint32 itemnum); + void SetTint(sint16 slot_id, uint32 color); + void SetMaterial(sint16 slot_id, uint32 item_id); + void Undye(); + uint32 GetItemIDAt(sint16 slot_id); + void DeleteItemInInventory(sint16 slot_id, sint8 quantity = 0, bool client_update = false); + void SummonItem(uint32 item_id, sint8 charges = 0); + void SetStats(int8 type,sint16 increase_val); + void IncStats(int8 type,sint16 increase_val); + void DropItem(sint16 slot_id); + + void BreakInvis(); + Group* GetGroup(); + void LeaveGroup(); + + bool Hungry(); + bool Thirsty(); + int16 GetInstrumentMod(int16 spell_id); + bool DecreaseByID(int32 type, int8 amt); + int8 SlotConvert2(int8 slot); + void Escape(); + void RemoveNoRent(); + void RangedAttack(Mob* other); + void ThrowingAttack(Mob* other); + void GoFish(); + void ForageItem(); + float CalcPriceMod(Mob* other = 0, bool reverse = false); + void ResetTrade(); + bool UseDiscipline(int32 spell_id, int32 target); + sint32 GetCharacterFactionLevel(sint32 faction_id); + + + void SetZoneFlag(uint32 zone_id); + void ClearZoneFlag(uint32 zone_id); + bool HasZoneFlag(uint32 zone_id) const; + void SendZoneFlagInfo(Client *to) const; + void LoadZoneFlags(); + void SetAATitle(const char *txt); + +//TODO: inventory and ptimers interfaces +//I think there are two GetAA methods in client + + + + + + + + + + + + + + diff --git a/utils/deprecated/perlxs/client.h.xs b/utils/deprecated/perlxs/client.h.xs new file mode 100644 index 000000000..ede992bd5 --- /dev/null +++ b/utils/deprecated/perlxs/client.h.xs @@ -0,0 +1,36 @@ + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +#include "client.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + +MODULE = Client PACKAGE = Client + +PROTOTYPES: ENABLE + + + diff --git a/utils/deprecated/perlxs/convert b/utils/deprecated/perlxs/convert new file mode 100644 index 000000000..20aaf845c --- /dev/null +++ b/utils/deprecated/perlxs/convert @@ -0,0 +1,44 @@ +#!/bin/sh + +if [ ! -r "$1" -o -z "$2" ]; then + echo "Usage: $0 headerfile classname" +fi + +name=`basename "$1" .h` +name="perl_${name}" + +echo "Starting conversion of $2" >&2 + +{ +cat "$1.xs" + +cat "$1" \ +| sed -e 's/inline//g' -e 's/const//g' -e 's/virtual//g' \ +-e 's/{.*}/;/g' -e 's#//.*$##g' \ +-e 's/(void)/()/g' \ +| tr -d '[&;]' \ +| perl -p -e "s/^\\s+(.+)[ \\t]([a-zA-Z0-9_]*)\\(/&\$1&$2::\$2(/g" \ +| tr '&' '\n' + +} > tmp + +{ + +xsubpp -typemap class.typemap tmp \ +| sed -e "s/\"$2::/\"/g" \ + -e 's/("\([a-zA-Z0-9_]*\)"/(strcpy(buf, "\1")/g' \ + -e 's/ / /g' -e 's/ / /g' \ + -e 's#char\* file = __FILE__;#char file[256];^ strncpy(file, __FILE__, 256);^ file[255] = '\0';^ ^ if(items != 1)^ fprintf(stderr, "boot_quest does not take any arguments.");^ char buf[128];^^ //add the strcpy stuff to get rid of const warnings....^^#g' \ + -e 's/Ptr//g' \ + -e 's/Mob__e/Mob::e/g' \ +| tr '^' '\n' \ +| grep -v "^#line" + +echo "#endif //EMBPERL_XS_CLASSES" +echo +} > "$name.cpp" + + +#rm -f tmp + + diff --git a/utils/deprecated/perlxs/entity.h b/utils/deprecated/perlxs/entity.h new file mode 100644 index 000000000..3a8f20c44 --- /dev/null +++ b/utils/deprecated/perlxs/entity.h @@ -0,0 +1,62 @@ + + Mob* GetMobID(int16 id); + Mob* GetMob(const char* name); + Mob* GetMobByNpcTypeID(int32 get_id); + Client* GetClientByName(const char *name); + Client* GetClientByAccID(int32 accid); + Client* GetClientByID(int16 id); + Client* GetClientByCharID(int32 iCharID); + Client* GetClientByWID(int32 iWID); + Group* GetGroupByMob(Mob* mob); + Group* GetGroupByClient(Client* client); + Group* GetGroupByID(int32 id); + Group* GetGroupByLeaderName(char* leader); + Corpse* GetCorpseByOwner(Client* client); + Corpse* GetCorpseByID(int16 id); + Corpse* GetCorpseByName(char* name); + void ClearClientPetitionQueue(); + bool CanAddHateForMob(Mob *p); + + void Clear(); + bool RemoveMob(int16 delete_id); + bool RemoveClient(int16 delete_id); + bool RemoveNPC(int16 delete_id); + bool RemoveGroup(int32 delete_id); + bool RemoveCorpse(int16 delete_id); + bool RemoveDoor(int16 delete_id); + bool RemoveTrap(int16 delete_id); + bool RemoveObject(int16 delete_id); + void RemoveAllMobs(); + void RemoveAllClients(); + void RemoveAllNPCs(); + void RemoveAllGroups(); + void RemoveAllCorpses(); + void RemoveAllDoors(); + void RemoveAllTraps(); + void RemoveAllObjects(); + + void Message(int32 to_guilddbid, int32 type, const char* message, ...); + void MessageStatus(int32 to_guilddbid, int to_minstatus, int32 type, const char* message, ...); + void MessageClose(Mob* sender, bool skipsender, float dist, int32 type, const char* message, ...); + + void RemoveFromTargets(Mob* mob); + void ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget); + + void OpenDoorsNear(NPC* opener); + + char* MakeNameUnique(char* name); + static char* RemoveNumbers(char* name); +// signal quest command support + void SignalMobsByNPCID(int32 npc_type, int signal_id); + void RemoveEntity(int16 id); + + sint32 DeleteNPCCorpses(); + sint32 DeletePlayerCorpses(); + void HalveAggro(Mob* who); + void DoubleAggro(Mob* who); + void ClearFeignAggro(Mob* targ); + + bool Fighting(Mob* targ); + void RemoveFromHateLists(Mob* mob, bool settoone = false); + void MessageGroup(Mob* sender, bool skipclose, int32 type, const char* message, ...); + diff --git a/utils/deprecated/perlxs/entity.h.xs b/utils/deprecated/perlxs/entity.h.xs new file mode 100644 index 000000000..84a85bb97 --- /dev/null +++ b/utils/deprecated/perlxs/entity.h.xs @@ -0,0 +1,37 @@ + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +#include "entity.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +MODULE = EntityList PACKAGE = EntityList + +PROTOTYPES: ENABLE + + + diff --git a/utils/deprecated/perlxs/export b/utils/deprecated/perlxs/export new file mode 100644 index 000000000..952af2170 --- /dev/null +++ b/utils/deprecated/perlxs/export @@ -0,0 +1,12 @@ +#!/bin/sh + +FROM="$1" +TO="$2" + +if [ ! -r "$1" -o -z "$2" ]; then + echo "Usage: $0 [src] [dest]" +fi + +grep -A100000 "BEGIN PERL EXPORT" "$FROM" \ +| grep -B 100000 "END PERL EXPORT" \ +| grep -v "PERL EXPORT" >"$TO" diff --git a/utils/deprecated/perlxs/groups.h b/utils/deprecated/perlxs/groups.h new file mode 100644 index 000000000..99887e1e8 --- /dev/null +++ b/utils/deprecated/perlxs/groups.h @@ -0,0 +1,17 @@ + void DisbandGroup(); + bool IsGroupMember(Mob* client); + void CastGroupSpell(Mob* caster,uint16 spellid); + void SplitExp(uint32 exp, Mob* other); + void GroupMessage(Mob* sender,const char* message); + int32 GetTotalGroupDamage(Mob* other); + void SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum); + void SetLeader(Mob* newleader){ leader=newleader; }; + Mob* GetLeader(){ return leader; }; + char* GetLeaderName(){ return membername[0]; }; + void SendHPPacketsTo(Mob* newmember); + void SendHPPacketsFrom(Mob* newmember); + bool IsLeader(Mob* leadertest) { return leadertest==leader; }; + int8 GroupCount(); + int32 GetHighestLevel(); + void TeleportGroup(Mob* sender, int32 zoneID, float x, float y, float z, float heading); + inline const int32 GetID() const { return id; } diff --git a/utils/deprecated/perlxs/groups.h.xs b/utils/deprecated/perlxs/groups.h.xs new file mode 100644 index 000000000..c142a764f --- /dev/null +++ b/utils/deprecated/perlxs/groups.h.xs @@ -0,0 +1,37 @@ + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +#include "groups.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +MODULE = Group PACKAGE = Group + +PROTOTYPES: ENABLE + + + diff --git a/utils/deprecated/perlxs/mob.h b/utils/deprecated/perlxs/mob.h new file mode 100644 index 000000000..0485e299d --- /dev/null +++ b/utils/deprecated/perlxs/mob.h @@ -0,0 +1,249 @@ + +//BEGIN ENTITY METHODS + + virtual bool IsClient() { return false; } + virtual bool IsNPC() { return false; } + virtual bool IsMob() { return false; } + virtual bool IsCorpse() { return false; } + virtual bool IsPlayerCorpse() { return false; } + virtual bool IsNPCCorpse() { return false; } + virtual bool IsObject() { return false; } +// virtual bool IsGroup() { return false; } + virtual bool IsDoor() { return false; } + virtual bool IsTrap() { return false; } + virtual bool IsBeacon() { return false; } + + Client* CastToClient(); + NPC* CastToNPC(); + Mob* CastToMob(); + Corpse* CastToCorpse(); +// Object* CastToObject(); +// Group* CastToGroup(); +// Doors* CastToDoors(); +// Trap* CastToTrap(); +// Beacon* CastToBeacon(); + +//BEGIN MOB METHODS + int16& GetID(); + virtual Const_char * GetName(); + void Depop(bool StartSpawnTimer = true); + + void RogueAssassinate(Mob* other); + bool BehindMob(Mob* other = 0, float playerx = 0.0f, float playery = 0.0f); + void SetLevel(uint8 in_level, bool command = false); + + uint32 GetSkill(SkillType skill_num); + void SendWearChange(int8 material_slot); + sint32 GetEquipment(int8 material_slot); // returns item id + sint32 GetEquipmentMaterial(int8 material_slot); + sint32 GetEquipmentColor(int8 material_slot); + bool IsMoving(); + void GoToBind(); + void Gate(); + bool Attack(Mob* other, int Hand = 13, bool FromRiposte = false) + virtual void Damage(Mob* from, sint32 damage, int16 spell_id, SkillType attack_skill, bool avoidable = true, sint8 buffslot = -1, bool iBuffTic = false) {}; + + void Heal(); + void SetMaxHP(); + int32 GetLevelCon(int8 iOtherLevel); + void SetHP(sint32 hp); + void DoAnim(const int animnum, int type=1); + void ChangeSize(float in_size, bool bNoRestriction = false); + void GMMove(float x, float y, float z, float heading = 0.01); + void SendPosUpdate(int8 iSendToSelf = 0); + void SendPosition(); + bool HasProcs(); + //bool CheckLos(Mob* other); + bool IsInvisible(Mob *other = 0); + void SetInvisible(bool state); + bool FindBuff(int16 spellid); + bool FindType(int8 type, bool bOffensive = false, int16 threshold = 100); + sint8 GetBuffSlotFromType(int8 type); + void MakePet(int16 spell_id, const char* pettype, const char *name=NULL); + virtual inline int16 GetBaseRace(); + virtual inline int8 GetBaseGender(); + virtual inline int8 GetDeity(); + inline const int16& GetRace(); + inline const int8& GetGender(); + inline const int8& GetTexture(); + inline const int8& GetHelmTexture(); + inline const int8& GetClass(); + inline const uint8& GetLevel(); + Const_char * GetCleanName(); + inline Mob* GetTarget(); + virtual inline void SetTarget(Mob* mob); + virtual inline float GetHPRatio(); + + bool IsWarriorClass(); + virtual inline const sint32& GetHP(); + virtual inline const sint32& GetMaxHP(); + virtual float GetWalkspeed(); + virtual float GetRunspeed(); + virtual int GetCasterLevel(int16 spell_id); + virtual inline const sint32& GetMaxMana(); + virtual inline const sint32& GetMana(); + virtual void SetMana(sint32 amount); + virtual inline float GetManaRatio(); + + inline virtual int16 GetAC(); + inline virtual int16 GetATK(); + inline virtual sint16 GetSTR(); + inline virtual sint16 GetSTA(); + inline virtual sint16 GetDEX(); + inline virtual sint16 GetAGI(); + inline virtual sint16 GetINT(); + inline virtual sint16 GetWIS(); + inline virtual sint16 GetCHA(); + inline virtual sint16 GetMR(); + inline virtual sint16 GetFR(); + inline virtual sint16 GetDR(); + inline virtual sint16 GetPR(); + inline virtual sint16 GetCR(); + + inline virtual sint16 GetMaxSTR(); + inline virtual sint16 GetMaxSTA(); + inline virtual sint16 GetMaxDEX(); + inline virtual sint16 GetMaxAGI(); + inline virtual sint16 GetMaxINT(); + inline virtual sint16 GetMaxWIS(); + inline virtual sint16 GetMaxCHA(); + + virtual float GetActSpellRange(int16 spell_id, float range); + virtual sint32 GetActSpellDamage(int16 spell_id, sint32 value); + virtual sint32 GetActSpellHealing(int16 spell_id, sint32 value); + virtual sint32 GetActSpellCost(int16 spell_id, sint32 cost); + virtual sint32 GetActSpellDuration(int16 spell_id, sint32 duration); + virtual sint32 GetActSpellCasttime(int16 spell_id, sint32 casttime); + double ResistSpell(uint8 ressit_type, int16 spell_id, Mob *caster); + uint16 GetSpecializeSkillValue(int16 spell_id) const; + + int32 GetNPCTypeID(); + bool IsTargeted(); + + inline const float& GetX(); + inline const float& GetY(); + inline const float& GetZ(); + inline const float& GetHeading(); + inline const float& GetSize(); + + void SetFollowID(int32 id); + int32 GetFollowID(); + + virtual void Message(int32 type, const char* message, ...); + virtual void Message_StringID(int32 type, int32 string_id, int32 distance = 0); + void Say(const char *format, ...); + void Shout(const char *format, ...); + void Emote(const char *format, ...); + void InterruptSpell(int16 spellid = 0xFFFF); + + virtual void CastSpell(int16 spell_id, int16 target_id, int16 slot = 10, sint32 casttime = -1, sint32 mana_cost = -1); + bool IsImmuneToSpell(int16 spell_id, Mob *caster); + void BuffFadeBySpellID(int16 spell_id); + void BuffFadeByEffect(int effectid, int skipslot = -1); + void BuffFadeAll(); + void BuffFadeBySlot(int slot, bool iRecalcBonuses = true); + + int CanBuffStack(int16 spellid, int8 caster_level, bool iFailIfOverwrite = false); + inline bool IsCasting(); + int16 CastingSpellID(); + + void SetAppearance(EmuAppearance app, bool iIgnoreSelf = true); + + inline EmuAppearance GetAppearance(); + inline const int8& GetRunAnimSpeed(); + inline void SetRunAnimSpeed(sint8 in); + + void SetPetID(int16 NewPetID); + inline const int16& GetPetID(); + void SetOwnerID(int16 NewOwnerID); + inline const int16& GetOwnerID(); + inline const int16& GetPetType(); + + inline const int8& GetBodyType(); + virtual void Stun(int duration); + + void Spin(); + void Kill(); + + inline void SetInvul(bool invul); + inline bool GetInvul(); + inline void SetExtraHaste(int Haste); + virtual int GetHaste(); + int GetMonkHandToHandDamage(void); + + bool CanThisClassDoubleAttack(void); + bool CanThisClassDualWield(void); + bool CanThisClassRiposte(void); + bool CanThisClassDodge(void); + bool CanThisClassParry(void); + + int GetMonkHandToHandDelay(void); + int8 GetClassLevelFactor(); + void Mesmerize(); + inline bool IsMezzed(); + inline bool IsStunned(); + + void StartEnrage(); + bool IsEnraged(); + + FACTION_VALUE GetReverseFactionCon(Mob* iOther); + inline const bool& IsAIControlled(); + inline const float& GetAggroRange(); + inline const float& GetAssistRange(); + + inline void SetPetOrder(Mob::eStandingPetOrder i); + inline const Mob::eStandingPetOrder& GetPetOrder(); + inline const bool& IsRoamer(); + inline const bool IsRooted(); + + void AddToHateList(Mob* other, sint32 hate = 0, sint32 damage = 0, bool iYellForHelp = true, bool bFrenzy = false, bool iBuffTic = false); + void SetHate(Mob* other, sint32 hate = 0, sint32 damage = 0); + int32 GetHateAmount(Mob* tmob, bool is_dam = false); + int32 GetDamageAmount(Mob* tmob); + Mob* GetHateTop(); + Mob* GetHateDamageTop(Mob* other); + Mob* GetHateRandom(); + bool IsEngaged(); + bool HateSummon(); + void FaceTarget(Mob* MobToFace = 0, bool update = false); + void SetHeading(float iHeading); + void WipeHateList(); + + inline bool CheckAggro(Mob* other); + + sint8 CalculateHeadingToTarget(float in_x, float in_y); + bool CalculateNewPosition(float x, float y, float z, float speed, bool checkZ = false); + bool CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ = false); + float CalculateDistance(float x, float y, float z); + void SendTo(float new_x, float new_y, float new_z); + void SendToFixZ(float new_x, float new_y, float new_z); + void NPCSpecialAttacks(const char* parse, int permtag); + inline int32& DontHealMeBefore(); + inline int32& DontBuffMeBefore(); + inline int32& DontDotMeBefore(); + inline int32& DontRootMeBefore(); + inline int32& DontSnareMeBefore(); + + sint16 GetResist(int8 type); + Mob* GetShieldTarget(); + void SetShieldTarget(Mob* mob); + bool Charmed(); + int32 GetLevelHP(int8 tlevel); + int32 GetZoneID() const; + int16 CheckAggroAmount(int16 spellid); + int16 CheckHealAggroAmount(int16 spellid); + virtual int32 GetAA(int32 aa_id); + bool DivineAura(); + + void AddFeignMemory(Client* attacker); + void RemoveFromFeignMemory(Client* attacker); + void ClearFeignMemory(); + +//END MOB METHODS + + + + + + + diff --git a/utils/deprecated/perlxs/mob.h.xs b/utils/deprecated/perlxs/mob.h.xs new file mode 100644 index 000000000..692247173 --- /dev/null +++ b/utils/deprecated/perlxs/mob.h.xs @@ -0,0 +1,39 @@ + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +typedef const char Const_char; + +#include "mob.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +MODULE = Mob PACKAGE = Mob + +PROTOTYPES: ENABLE + + + diff --git a/utils/deprecated/perlxs/npc.h b/utils/deprecated/perlxs/npc.h new file mode 100644 index 000000000..93e8581e1 --- /dev/null +++ b/utils/deprecated/perlxs/npc.h @@ -0,0 +1,63 @@ + void SignalNPC(int _signal_id); + FACTION_VALUE CheckNPCFactionAlly(sint32 other_faction); + void AddItem(int32 itemid, int8 charges, int8 slot = 0); + void AddLootTable(); + void RemoveItem(uint16 item_id, int16 quantity = 0, int16 slot = 0); + void ClearItemList(); + void AddCash(int16 in_copper, int16 in_silver, int16 in_gold, int16 in_platinum); + void RemoveCash(); + int32 CountLoot(); + inline int32 GetLoottableID() { return loottable_id; } + inline uint32 GetCopper() { return copper; } + inline uint32 GetSilver() { return silver; } + inline uint32 GetGold() { return gold; } + inline uint32 GetPlatinum() { return platinum; } + + inline void SetCopper(uint32 amt) { copper = amt; } + inline void SetSilver(uint32 amt) { silver = amt; } + inline void SetGold(uint32 amt) { gold = amt; } + inline void SetPlatinum(uint32 amt) { platinum = amt; } + void SetGrid(int16 grid_){ grid=grid_; } + void SetSp2(int32 sg2){ spawn_group=sg2; } + int16 GetWaypointMax(){ return wp_m; } + sint16 GetGrid(){ return grid; } + int32 GetSp2(){ return spawn_group; } + + inline const sint32& GetNPCFactionID() { return npc_faction_id; } + inline sint32 GetPrimaryFaction() { return primary_faction; } + sint32 GetNPCHate(Mob* in_ent) {return hate_list.GetEntHate(in_ent);} + bool IsOnHatelist(Mob*p) { return hate_list.IsOnHateList(p);} + + void SetNPCFactionID(sint32 in) { npc_faction_id = in; database.GetFactionIdsForNPC(npc_faction_id, &faction_list, &primary_faction); } + int16 GetMaxDMG() {return max_dmg;} + bool IsAnimal() { return(bodytype == 21); } + int16 GetPetSpellID() {return pet_spell_id;} + void SetPetSpellID(int16 amt) {pet_spell_id = amt;} + int32 GetMaxDamage(int8 tlevel); + void SetTaunting(bool tog) {taunting = tog;} + void PickPocket(Client* thief); + void StartSwarmTimer(int32 duration) { swarm_timer.Start(duration); } + void DoClassAttacks(Mob *target); + + + + int GetMaxWp() const { return max_wp; } + void DisplayWaypointInfo(Client *to); + void CalculateNewWaypoint(); +// int8 CalculateHeadingToNextWaypoint(); +// float CalculateDistanceToNextWaypoint(); + void AssignWaypoints(int32 grid); + void SetWaypointPause(); + void UpdateWaypoint(int wp_index); + // quest wandering commands + void StopWandering(); + void ResumeWandering(); + void PauseWandering(int pausetime); + void MoveTo(float mtx, float mty, float mtz, float mth, bool saveguardspot); + + void NextGuardPosition(); + void SaveGuardSpot(bool iClearGuardSpot = false); + inline bool IsGuarding() const { return(guard_heading != 0); } + void AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, int32 iDelay = 2500); + const int32& GetNPCSpellsID(); + diff --git a/utils/deprecated/perlxs/npc.h.xs b/utils/deprecated/perlxs/npc.h.xs new file mode 100644 index 000000000..b84085f58 --- /dev/null +++ b/utils/deprecated/perlxs/npc.h.xs @@ -0,0 +1,39 @@ + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +typedef const char Const_char; + +#include "npc.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +MODULE = NPC PACKAGE = NPC + +PROTOTYPES: ENABLE + + + diff --git a/utils/deprecated/perlxs/perlpacket.h b/utils/deprecated/perlxs/perlpacket.h new file mode 100644 index 000000000..58fba479c --- /dev/null +++ b/utils/deprecated/perlxs/perlpacket.h @@ -0,0 +1,28 @@ +PerlPacket * +PerlPacket::new(const char *opcode = "OP_Unknown", uint32 len = 0); + void DESTROY(); + + bool SetOpcode(const char *opcode); + void Resize(uint32 len); + + //sending functions + void SendTo(Client *who); + void SendToAll(); + + //editing + void Zero(); + void FromArray(int *numbers, uint32 length); + void SetByte(uint32 pos, uint8 val); + void SetShort(uint32 pos, uint16 val); + void SetLong(uint32 pos, uint32 val); + void SetFloat(uint32 pos, float val); + void SetString(uint32 pos, char *str); + + void SetEQ1319(uint32 pos, float part13, float part19); + void SetEQ1913(uint32 pos, float part19, float part13); + + //reading + uint8 GetByte(uint32 pos); + uint16 GetShort(uint32 pos); + uint32 GetLong(uint32 pos); + float GetFloat(uint32 pos); diff --git a/utils/deprecated/perlxs/perlpacket.h.xs b/utils/deprecated/perlxs/perlpacket.h.xs new file mode 100644 index 000000000..6ba3ab910 --- /dev/null +++ b/utils/deprecated/perlxs/perlpacket.h.xs @@ -0,0 +1,36 @@ + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +#include "perlpacket.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + +MODULE = PerlPacket PACKAGE = PerlPacket + +PROTOTYPES: ENABLE + + + diff --git a/utils/deprecated/perlxs/put b/utils/deprecated/perlxs/put new file mode 100644 index 000000000..54009f6fa --- /dev/null +++ b/utils/deprecated/perlxs/put @@ -0,0 +1,10 @@ +#!/bin/sh + +cp perl_PlayerCorpse.cpp perl_client.cpp perl_entity.cpp perl_groups.cpp \ + perl_mob.cpp perl_npc.cpp perl_perlpacket.cpp \ +../../zone/ + +cp perl_EQW.cpp perl_HTTPRequest.cpp perl_EQLConfig.cpp ../../world/ + +cp perl_EQDB.cpp perl_EQDBRes.cpp ../../common/ + diff --git a/utils/deprecated/ppconvert/Makefile.bsd b/utils/deprecated/ppconvert/Makefile.bsd new file mode 100644 index 000000000..1672b39bd --- /dev/null +++ b/utils/deprecated/ppconvert/Makefile.bsd @@ -0,0 +1,16 @@ + +#Mysql flags, for linuxthreads +#MYSQL_FLAGS=`mysql_config --cflags` +#MYSQL_LIB=`mysql_config --libs` +MYSQL_FLAGS=-I'/usr/local/include/mysql' +MYSQL_LIBS=-L'/usr/local/lib/mysql' -llmysqlclient -lz -lcrypt -lm + +#linux threads flags: +LTFLAGS=-I/usr/local/include/pthread/linuxthreads -D_GNU_SOURCE -D__USE_UNIX98 -D_REENTRANT -D_THREAD_SAFE +LTLDFLAGS=-llthread -llstdc++ -llsupc++ -llgcc_r -L/usr/local/lib + +CXXFLAGS=-ggdb -O -pipe -Wall -I. -I../common -fno-inline -DFREEBSD $(LTFLAGS) $(MYSQL_FLAGS) +LDFLAGS=-lpcap -lz -rdynamic -O2 -ggdb -pipe -L/usr/local/lib $(LTLDFLAGS) + + +include Makefile.common diff --git a/utils/deprecated/ppconvert/Makefile.common b/utils/deprecated/ppconvert/Makefile.common new file mode 100644 index 000000000..4e8b8b634 --- /dev/null +++ b/utils/deprecated/ppconvert/Makefile.common @@ -0,0 +1,26 @@ +#objects for EQ extractor +PPOBJS=ppconvert.o .obj/Mutex.o .obj/unix.o \ + .obj/dbcore.o + +JOE= ExtractDB.o .obj/MiscFunctions.o Explorers.o ../common/misc.o \ + StructExplorer.o ../common/packet_dump.o ../common/buildfile.o \ + patches/versions.o patches/patch_021505.o patches/patch_current.o \ + patches/patch_local.o patches/patch_121504.o + +all: ppconvert + +ppconvert: $(PPOBJS) + g++ -I.. $^ $(LDFLAGS) $(MYSQL_LIBS) -o $@ + +clean: + rm -f ppconvert $(PPOBJS) + +.obj/%.o: ../../common/%.cpp ../../common/%.h + mkdir -p .obj + g++ -I.. $(CXXFLAGS) -c $< -o $@ + +%.o: %.cpp %.h + mkdir -p .obj + g++ -I.. $(CXXFLAGS) -c $< -o $@ + + diff --git a/utils/deprecated/ppconvert/ppconvert.cpp b/utils/deprecated/ppconvert/ppconvert.cpp new file mode 100644 index 000000000..e8d2a85b9 --- /dev/null +++ b/utils/deprecated/ppconvert/ppconvert.cpp @@ -0,0 +1,325 @@ +#include "../common/debug.h" +#include "../common/types.h" +#include "../common/eq_packet_structs.h" +#include +#include +#include +#include +#include + +using namespace std; + +#include "../common/dbcore.h" + +#define MAX_CONVERT_STEPS 10 + +int convert_profile_once(char *src, char *dst, int len); + +int main() { + + char host[200], user[200], passwd[200], database[200]; + int32 port=0; + bool compression = false; + bool items[6] = {false, false, false, false, false, false}; + + if(!DBcore::ReadDBINI(host, user, passwd, database, port, compression, items)) { + exit(1); + } + + if (!items[0] || !items[1] || !items[2] || !items[3]) + { + printf ("Incomplete DB.INI file.\n"); + exit (1); + } + + vector updates; + + MYSQL m; + MYSQL out; + + mysql_init(&m); + mysql_init(&out); + + if(!mysql_real_connect(&m, host, user, passwd, database, 0, NULL, 0)) { + printf("Unable to connect 1: %s.\n", mysql_error(&m)); + return(1); + } + if(!mysql_real_connect(&out, host, user, passwd, database, 0, NULL, 0)) { + printf("Unable to connect 2: %s.\n", mysql_error(&m)); + return(1); + } + + if(mysql_query(&m, "SELECT id,name,profile FROM character_") != 0) { + printf("Unable to query.\n"); + return(1); + } + + MYSQL_RES *res = mysql_use_result(&m); + if(res == NULL) { + printf("Unable to use res.\n"); + return(1); + } + + char *inbuffer = new char[sizeof(PlayerProfile_Struct)]; + + MYSQL_ROW row; + unsigned long *lengths; + + int convert_count = 0; + int correct_count = 0; + int failed_count = 0; + + while((row = mysql_fetch_row(res))) { + lengths = mysql_fetch_lengths(res); + unsigned long id = atoi(row[0]); + + int curlen = lengths[2]; + + if(curlen == sizeof(PlayerProfile_Struct)) { + correct_count++; + continue; //allready current. + } + + fprintf(stderr, "Converting char %lu: %s...", id, row[1]); + + //make a copy of the version in the DB + memcpy(inbuffer, row[2], curlen); + + char *outbuffer = new char[2*sizeof(PlayerProfile_Struct) + 512]; + + int steps; + for(steps = 0; steps < MAX_CONVERT_STEPS && curlen != sizeof(PlayerProfile_Struct); steps++) { + //clear outbuffer + memset(outbuffer, 0, sizeof(PlayerProfile_Struct)); + + fprintf(stderr, " |"); + fflush(stderr); + + //convert inbuffer one step closer to the current profile, into outbuffer + curlen = convert_profile_once(inbuffer, outbuffer, curlen); + if(curlen == 0) + break; + + //copy outbuffer into inbuffer for the next convert step + memcpy(inbuffer, outbuffer, curlen); + } + if(curlen != sizeof(PlayerProfile_Struct)) { + failed_count++; + fprintf(stderr, " failed.\n"); + delete[] outbuffer; + continue; + } + fprintf(stderr, " *"); + fflush(stderr); + + //the correct profile ends up in inbuffer, so we can escape it into outbuffer + char *bptr = outbuffer; + bptr += snprintf(bptr, 128, "UPDATE character_ SET profile='"); + bptr += mysql_real_escape_string(&m, bptr, (const char *) inbuffer, sizeof(PlayerProfile_Struct)); + snprintf(bptr, 128, "' WHERE id=%lu", id); + +// printf("Query: '%s'\n", outbuffer); +//printf(outbuffer); +//printf(";\n"); +/* if(mysql_query(&out, outbuffer) != 0) { + failed_count++; + printf(" Error updating char id %lu: %s\n", id, mysql_error(&m)); + continue; + } +*/ +updates.push_back(outbuffer); + fprintf(stderr, " done.\n"); +// convert_count++; + } +// mysql_free_result(res); + + + vector::iterator cur, end; + cur = updates.begin(); + end = updates.end(); + printf("Querying."); + for(; cur != end; cur++) { + if(mysql_query(&out, *cur) != 0) { + failed_count++; + printf(" Error updating some char: %s\n", mysql_error(&m)); + continue; + } +printf("."); +fflush(stdout); + delete[] *cur; + + convert_count++; + } + + fprintf(stderr, "%d chars converted, %d errors, %d chars were up to date.\n", convert_count, failed_count, correct_count); + + mysql_close(&m); + mysql_close(&out); + return(0); +} + + +//dst is garunteed to be all 0's +int convert_profile_once(char *src, char *dst, int len) { + switch(len) { + case sizeof(BeforeFeb18_PlayerProfile_Struct): { + BeforeFeb18_PlayerProfile_Struct* ops = (BeforeFeb18_PlayerProfile_Struct*) src; + memcpy(dst, ops, sizeof(BeforeFeb18_PlayerProfile_Struct)); + len = sizeof(BeforeApril14th_PlayerProfile_Struct); + break; + } + case sizeof(BeforeApril14th_PlayerProfile_Struct): { + BeforeApril14th_PlayerProfile_Struct* ops = (BeforeApril14th_PlayerProfile_Struct*) src; + memcpy(dst, ops, sizeof(BeforeApril14th_PlayerProfile_Struct)); + len = sizeof(BeforeApr21st_PlayerProfile_Struct); + break; + } + case sizeof(BeforeApr21st_PlayerProfile_Struct): { + BeforeApr21st_PlayerProfile_Struct* ops = (BeforeApr21st_PlayerProfile_Struct*)src; + memcpy(dst, ops, sizeof(BeforeApr21st_PlayerProfile_Struct)); + len = sizeof(BeforeMay5th_PlayerProfile_Struct); + break; + } + case sizeof(BeforeMay5th_PlayerProfile_Struct): { + BeforeMay5th_PlayerProfile_Struct* ops = (BeforeMay5th_PlayerProfile_Struct*)src; + memcpy(dst, ops, sizeof(BeforeMay5th_PlayerProfile_Struct)); + len = sizeof(PlayerProfile_Struct_Before_May26th); + break; + } + case sizeof(PlayerProfile_Struct_Before_May26th): { + PlayerProfile_Struct_Before_May26th* ops = (PlayerProfile_Struct_Before_May26th*)src; + memcpy(dst, ops, sizeof(PlayerProfile_Struct_Before_May26th)); + len = sizeof(Before_Aug13th_PlayerProfile_Struct); + break; + } + case sizeof(Before_Aug13th_PlayerProfile_Struct): { + Before_Aug13th_PlayerProfile_Struct* ops = (Before_Aug13th_PlayerProfile_Struct*)src; + Before_Sep14th_PlayerProfile_Struct* pp = (Before_Sep14th_PlayerProfile_Struct*)dst; + uchar* newpps = new uchar[sizeof(Before_Sep14th_PlayerProfile_Struct)]; + memset(newpps, 0, sizeof(Before_Sep14th_PlayerProfile_Struct)); + uchar* ptr_old=(uchar*)newpps; + uchar* c_ptr=(uchar*)ops; + memcpy(ptr_old,c_ptr,588); + c_ptr+=588; + ptr_old+=1304; + memcpy(ptr_old,c_ptr,4440); + + uchar* ptr=(uchar*)dst; + uchar* s_ptr=(uchar*)newpps; + memcpy(ptr,s_ptr,124); + s_ptr+=140; + ptr+=220; + memcpy(ptr,s_ptr,88); + s_ptr+=88; + ptr+=92; + memcpy(ptr,s_ptr,1140); + s_ptr+=1140; + ptr+=1176; + memcpy(ptr,s_ptr,8); + s_ptr+=8; + ptr+=12; + memcpy(ptr,s_ptr,3700); + + pp->bind_x=((Before_Sep14th_PlayerProfile_Struct*)newpps)->bind_x; + pp->bind_y=((Before_Sep14th_PlayerProfile_Struct*)newpps)->bind_y; + pp->bind_z=((Before_Sep14th_PlayerProfile_Struct*)newpps)->bind_z; + + len = sizeof(Before_Sep14th_PlayerProfile_Struct); + break; + } + case sizeof(Before_Sep14th_PlayerProfile_Struct): { + Before_Sep14th_PlayerProfile_Struct* oldpp =(Before_Sep14th_PlayerProfile_Struct*)src; + Before_Dec15th_PlayerProfile_Struct* pp =(Before_Dec15th_PlayerProfile_Struct*)dst; + uchar* ptr=(uchar*)dst; + uchar* s_ptr=(uchar*)oldpp; + memcpy(ptr,s_ptr,124); + s_ptr+=140; + ptr+=220; + memcpy(ptr,s_ptr,88); + s_ptr+=88; + ptr+=92; + memcpy(ptr,s_ptr,1140); + s_ptr+=1140; + ptr+=1176; + memcpy(ptr,s_ptr,8); + s_ptr+=8; + ptr+=12; + memcpy(ptr,s_ptr,3700); + //s_ptr+=3020; + //ptr+=3024; + //memcpy(ptr,s_ptr,680); + pp->bind_x[0]=oldpp->bind_x; + pp->bind_y[0]=oldpp->bind_y; + pp->bind_z[0]=oldpp->bind_z; + + len = sizeof(Before_Dec15th_PlayerProfile_Struct); + break; + } + case sizeof(Before_Dec15th_PlayerProfile_Struct): { +#define StructDist(in, f1, f2) (uint32(&in->f2)-uint32(&in->f1)) + Before_Dec15th_PlayerProfile_Struct* ops = (Before_Dec15th_PlayerProfile_Struct*)src; + Before_June29th_PlayerProfile_Struct* pp = (Before_June29th_PlayerProfile_Struct*)dst; + + //start with the basics + memcpy(dst, ops, sizeof(Before_June29th_PlayerProfile_Struct)); + + pp->anon = ops->anon; + pp->guildrank = ops->guildrank; + memcpy(pp->unknown0245, &ops->fatigue, StructDist(ops, fatigue, guildid2)); + //just zero out buffs and groups + memset(pp->buffs, 0, StructDist(pp, buffs, unknown6392)); + //shift down everything after that. + memcpy(pp->unknown6392, &ops->unknown5248, StructDist(ops, unknown5248, unknown11376)); + //put the tribute block in the right place + memcpy(&pp->tribute_time_remaining, &ops->tribute_time_remaining, StructDist(ops, tribute_time_remaining, unknown5764)); + + //copy over things that maybe moved, but I havent figure out how yet + pp->aapoints = ops->aapoints; + pp->aapoints_spent = ops->aapoints_spent; + + len = sizeof(Before_June29th_PlayerProfile_Struct); + } + case sizeof(Before_June29th_PlayerProfile_Struct): { + Before_June29th_PlayerProfile_Struct* ops = (Before_June29th_PlayerProfile_Struct*)src; + memcpy(dst, ops, sizeof(Before_June29th_PlayerProfile_Struct)); + + len = sizeof(Before_May12_PlayerProfile_Struct); + } + case sizeof(Before_May12_PlayerProfile_Struct): { + Before_May12_PlayerProfile_Struct* ops = (Before_May12_PlayerProfile_Struct*)src; + PlayerProfile_Struct* pp = (PlayerProfile_Struct*)dst; + memcpy(dst, ops, sizeof(Before_May12_PlayerProfile_Struct)); + + memcpy(&pp->checksum, &ops->checksum, StructDist(ops, checksum, haircolor)); + memcpy(&pp->haircolor, &ops->haircolor, StructDist(ops, haircolor, unknown0310[0])); + memcpy(&pp->item_material[0], &ops->item_material[0], StructDist(ops, item_material[0], servername[0])); + memcpy(&pp->servername[0], &ops->servername[0], StructDist(ops, servername[0], skills[0])); + memcpy(&pp->skills[0], &ops->skills[0], StructDist(ops, skills[0], zone_change_count)); + memcpy(&pp->zone_change_count, &ops->zone_change_count, StructDist(ops, zone_change_count, unknown4436[0])); + memcpy(&pp->expAA, &ops->expAA, StructDist(ops, expAA, unknown4484)); + memcpy(&pp->expansion, &ops->expansion, StructDist(ops, expansion, buffs[0])); + memcpy(&pp->ldon_points_guk, &ops->ldon_points_guk, StructDist(ops, ldon_points_guk, tribute_time_remaining)); + memcpy(&pp->tribute_time_remaining, &ops->tribute_time_remaining, StructDist(ops, tribute_time_remaining, unknown6860)); + memcpy(&pp->leader_abilities, &ops->leader_abilities, StructDist(ops, leader_abilities, unknown6932[0])); + memcpy(&pp->air_remaining, &ops->air_remaining, StructDist(ops, air_remaining, unknown18492)); + + /* + * This is the last statement in this switch. + */ + len = sizeof(PlayerProfile_Struct); + break; + } + default: + fprintf(stderr, "Unknown length of player profile: %d, %d\n", len, sizeof(Before_June29th_PlayerProfile_Struct)); + len = 0; + break; + } + return(len); +} + + + + + + + diff --git a/utils/deprecated/ppconvert/ppconvert.dsp b/utils/deprecated/ppconvert/ppconvert.dsp new file mode 100644 index 000000000..07490a891 --- /dev/null +++ b/utils/deprecated/ppconvert/ppconvert.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="ppconvert" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=ppconvert - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ppconvert.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ppconvert.mak" CFG="ppconvert - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ppconvert - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "ppconvert - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ppconvert - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "." /I ".." /I "../common" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib zlib.lib mysqlclient.lib /nologo /subsystem:console /machine:I386 /libpath:"c:\mysql\lib\opt" /libpath:"c:\eqemu\lib" + +!ELSEIF "$(CFG)" == "ppconvert - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "C:\Ihatewindows\Debug" +# PROP Intermediate_Dir "C:\Ihatewindows\Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "." /I ".." /I "../common" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib zlib.lib mysqlclient.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"LIBCMTD" /nodefaultlib:"LIBC" /pdbtype:sept /libpath:"c:\mysql\lib\opt" /libpath:"c:\eqemu\lib" + +!ENDIF + +# Begin Target + +# Name "ppconvert - Win32 Release" +# Name "ppconvert - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\common\dbcore.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\common\Mutex.cpp +# End Source File +# Begin Source File + +SOURCE=.\ppconvert.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/utils/deprecated/ppskillfix/Makefile.bsd b/utils/deprecated/ppskillfix/Makefile.bsd new file mode 100644 index 000000000..1672b39bd --- /dev/null +++ b/utils/deprecated/ppskillfix/Makefile.bsd @@ -0,0 +1,16 @@ + +#Mysql flags, for linuxthreads +#MYSQL_FLAGS=`mysql_config --cflags` +#MYSQL_LIB=`mysql_config --libs` +MYSQL_FLAGS=-I'/usr/local/include/mysql' +MYSQL_LIBS=-L'/usr/local/lib/mysql' -llmysqlclient -lz -lcrypt -lm + +#linux threads flags: +LTFLAGS=-I/usr/local/include/pthread/linuxthreads -D_GNU_SOURCE -D__USE_UNIX98 -D_REENTRANT -D_THREAD_SAFE +LTLDFLAGS=-llthread -llstdc++ -llsupc++ -llgcc_r -L/usr/local/lib + +CXXFLAGS=-ggdb -O -pipe -Wall -I. -I../common -fno-inline -DFREEBSD $(LTFLAGS) $(MYSQL_FLAGS) +LDFLAGS=-lpcap -lz -rdynamic -O2 -ggdb -pipe -L/usr/local/lib $(LTLDFLAGS) + + +include Makefile.common diff --git a/utils/deprecated/ppskillfix/Makefile.common b/utils/deprecated/ppskillfix/Makefile.common new file mode 100644 index 000000000..05e850462 --- /dev/null +++ b/utils/deprecated/ppskillfix/Makefile.common @@ -0,0 +1,26 @@ +#objects for EQ extractor +PPOBJS=ppskillfix.o .obj/Mutex.o .obj/unix.o \ + .obj/dbcore.o + +JOE= ExtractDB.o .obj/MiscFunctions.o Explorers.o ../common/misc.o \ + StructExplorer.o ../common/packet_dump.o ../common/buildfile.o \ + patches/versions.o patches/patch_021505.o patches/patch_current.o \ + patches/patch_local.o patches/patch_121504.o + +all: ppskillfix + +ppskillfix: $(PPOBJS) + g++ -I.. $^ $(LDFLAGS) $(MYSQL_LIBS) -o $@ + +clean: + rm -f ppskillfix $(PPOBJS) + +.obj/%.o: ../../common/%.cpp ../../common/%.h + mkdir -p .obj + g++ -I.. $(CXXFLAGS) -c $< -o $@ + +%.o: %.cpp %.h + mkdir -p .obj + g++ -I.. $(CXXFLAGS) -c $< -o $@ + + diff --git a/utils/deprecated/ppskillfix/ppskillfix.cpp b/utils/deprecated/ppskillfix/ppskillfix.cpp new file mode 100644 index 000000000..758073c61 --- /dev/null +++ b/utils/deprecated/ppskillfix/ppskillfix.cpp @@ -0,0 +1,160 @@ +#include "../common/debug.h" +#include "../common/types.h" +#include "../common/eq_packet_structs.h" +#include +#include +#include +#include +#include + +using namespace std; + +#include "../common/dbcore.h" + +#define MAX_CONVERT_STEPS 10 + +int convert_profile_once(char *src, char *dst, int len); + +int main() { + + char host[200], user[200], passwd[200], database[200]; + int32 port=0; + bool compression = false; + bool items[6] = {false, false, false, false, false, false}; + + if(!DBcore::ReadDBINI(host, user, passwd, database, port, compression, items)) { + exit(1); + } + + if (!items[0] || !items[1] || !items[2] || !items[3]) + { + printf ("Incomplete DB.INI file.\n"); + exit (1); + } + + vector updates; + + MYSQL m; + MYSQL out; + + mysql_init(&m); + mysql_init(&out); + + if(!mysql_real_connect(&m, host, user, passwd, database, 0, NULL, 0)) { + printf("Unable to connect 1: %s.\n", mysql_error(&m)); + return(1); + } + if(!mysql_real_connect(&out, host, user, passwd, database, 0, NULL, 0)) { + printf("Unable to connect 2: %s.\n", mysql_error(&m)); + return(1); + } + + if(mysql_query(&m, "SELECT id,name,profile FROM character_") != 0) { + printf("Unable to query.\n"); + return(1); + } + + MYSQL_RES *res = mysql_use_result(&m); + if(res == NULL) { + printf("Unable to use res.\n"); + return(1); + } + + char *inbuffer = new char[sizeof(PlayerProfile_Struct)]; + PlayerProfile_Struct *in_pp = (PlayerProfile_Struct *) inbuffer; + + MYSQL_ROW row; + unsigned long *lengths; + + int convert_count = 0; + int correct_count = 0; + int failed_count = 0; + + while((row = mysql_fetch_row(res))) { + lengths = mysql_fetch_lengths(res); + unsigned long id = atoi(row[0]); + + int curlen = lengths[2]; + + if(curlen != sizeof(PlayerProfile_Struct)) { + fprintf(stderr, "Char '%s' has the wrong size. Expected %d, got %d\n", row[1], sizeof(PlayerProfile_Struct), curlen); + failed_count++; + continue; + } + + + //make a copy of the version in the DB + memcpy(inbuffer, row[2], curlen); + + bool change = false; + int r; + for(r = 0; r < MAX_PP_SKILL; r++) { +fprintf(stderr, "Char '%s' skill %d = %d\n", row[1], r, in_pp->skills[r]); + if(in_pp->skills[r] == 254) { + in_pp->skills[r] = 0; + change = true; + } + } + if(!change) { + correct_count++; + continue; + } + + fprintf(stderr, "Converting char %lu: %s...", id, row[1]); + convert_count++; + + char *outbuffer = new char[2*sizeof(PlayerProfile_Struct) + 512]; + + + //the correct profile ends up in inbuffer, so we can escape it into outbuffer + char *bptr = outbuffer; + bptr += snprintf(bptr, 128, "UPDATE character_ SET profile='"); + bptr += mysql_real_escape_string(&m, bptr, (const char *) inbuffer, sizeof(PlayerProfile_Struct)); + snprintf(bptr, 128, "' WHERE id=%lu", id); + +// printf("Query: '%s'\n", outbuffer); +//printf(outbuffer); +//printf(";\n"); +/* if(mysql_query(&out, outbuffer) != 0) { + failed_count++; + printf(" Error updating char id %lu: %s\n", id, mysql_error(&m)); + continue; + } +*/ +updates.push_back(outbuffer); + fprintf(stderr, " done.\n"); +// convert_count++; + } +// mysql_free_result(res); + + + vector::iterator cur, end; + cur = updates.begin(); + end = updates.end(); + printf("Querying."); + for(; cur != end; cur++) { + /*if(mysql_query(&out, *cur) != 0) { + failed_count++; + printf(" Error updating some char: %s\n", mysql_error(&m)); + continue; + } +printf("."); +fflush(stdout);*/ + delete[] *cur; + + convert_count++; + } + + fprintf(stderr, "%d chars converted, %d errors, %d chars were up to date.\n", convert_count, failed_count, correct_count); + + mysql_close(&m); + mysql_close(&out); + return(0); +} + + + + + + + diff --git a/utils/deprecated/ppskillfix/ppskillfix.dsp b/utils/deprecated/ppskillfix/ppskillfix.dsp new file mode 100644 index 000000000..591e22d39 --- /dev/null +++ b/utils/deprecated/ppskillfix/ppskillfix.dsp @@ -0,0 +1,111 @@ +# Microsoft Developer Studio Project File - Name="ppskillfix" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=ppskillfix - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ppskillfix.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ppskillfix.mak" CFG="ppskillfix - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ppskillfix - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "ppskillfix - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ppskillfix - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "." /I ".." /I "../common" /I "c:\mysql\include" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FD /c +# SUBTRACT CPP /YX +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib zlib.lib mysqlclient.lib /nologo /subsystem:console /machine:I386 /libpath:"c:\mysql\lib\opt" /libpath:"c:\eqemu\lib" + +!ELSEIF "$(CFG)" == "ppskillfix - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "C:\Ihatewindows\Debug" +# PROP Intermediate_Dir "C:\Ihatewindows\Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "." /I ".." /I "../common" /I "c:\mysql\include" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib zlib.lib mysqlclient.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept /libpath:"c:\mysql\lib\opt" /libpath:"c:\eqemu\lib" + +!ENDIF + +# Begin Target + +# Name "ppskillfix - Win32 Release" +# Name "ppskillfix - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\..\common\dbcore.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\common\Mutex.cpp +# End Source File +# Begin Source File + +SOURCE=.\ppskillfix.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/utils/deprecated/ppskillfix/ppskillfix.dsw b/utils/deprecated/ppskillfix/ppskillfix.dsw new file mode 100644 index 000000000..9ab07c8ef --- /dev/null +++ b/utils/deprecated/ppskillfix/ppskillfix.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "ppskillfix"=.\ppskillfix.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/utils/deprecated/spell_explorer.cpp b/utils/deprecated/spell_explorer.cpp new file mode 100644 index 000000000..83d116a46 --- /dev/null +++ b/utils/deprecated/spell_explorer.cpp @@ -0,0 +1,188 @@ +#include +#include "../common/types.h" +#include "../common/seperator.h" +#include "../zone/spdat.h" + +int main(int argc, char** argv) { + + int spid = 0; + + if(argc != 2) { + printf("Invalid args: %s [spell id]\n", argv[0]); + return(1); + } + + spid = atoi(argv[1]); + + + int tempid=0; + int16 counter=0; + char spell_line[2048]; + + FILE *sf = fopen("spells_us.txt", "r"); + + if(sf == NULL) { + printf("Unable to open spells_us.txt file.\n"); + return false; + } + + SPDat_Spell_Struct sp; + + while(!feof(sf)) { + fgets(spell_line, sizeof(spell_line), sf); + Seperator sep(spell_line, '^', 205, 100, false, 0, 0, false); + + if(spell_line[0]=='\0') + break; + + tempid = atoi(sep.arg[0]); + if(tempid != spid) + continue; + + printf("Found spell %d\n", spid); + + counter++; + strcpy(sp.name, sep.arg[1]); + strcpy(sp.player_1, sep.arg[2]); + strcpy(sp.teleport_zone, sep.arg[3]); + strcpy(sp.you_cast, sep.arg[4]); + strcpy(sp.other_casts, sep.arg[5]); + strcpy(sp.cast_on_you, sep.arg[6]); + strcpy(sp.cast_on_other, sep.arg[7]); + strcpy(sp.spell_fades, sep.arg[8]); + + sp.range=atof(sep.arg[9]); + sp.aoerange=atof(sep.arg[10]); + sp.pushback=atof(sep.arg[11]); + sp.pushup=atof(sep.arg[12]); + sp.cast_time=atoi(sep.arg[13]); + sp.recovery_time=atoi(sep.arg[14]); + sp.recast_time=atoi(sep.arg[15]); + sp.buffdurationformula=atoi(sep.arg[16]); + sp.buffduration=atoi(sep.arg[17]); + sp.AEDuration=atoi(sep.arg[18]); + sp.mana=atoi(sep.arg[19]); + + int y=0; + for(y=0; y < EFFECT_COUNT;y++) + sp.base[y]=atoi(sep.arg[20+y]); + for(y=0;y<11;y++) + sp.base2[y]=atoi(sep.arg[33+y]); + for(y=0; y < EFFECT_COUNT;y++) + sp.max[y]=atoi(sep.arg[44+y]); + + sp.icon=atoi(sep.arg[56]); + sp.memicon=atoi(sep.arg[57]); + + for(y=0; y< 4;y++) + sp.components[y]=atoi(sep.arg[58+y]); + + for(y=0; y< 4;y++) + sp.component_counts[y]=atoi(sep.arg[62+y]); + + for(y=0; y< 4;y++) + sp.NoexpendReagent[y]=atoi(sep.arg[66+y]); + + for(y=0; y< 12;y++) + sp.formula[y]=atoi(sep.arg[70+y]); + + sp.LightType=atoi(sep.arg[82]); + sp.goodEffect=atoi(sep.arg[83]); + sp.Activated=atoi(sep.arg[84]); + sp.resisttype=atoi(sep.arg[85]); + + for(y=0; y< 12;y++) + sp.effectid[y]=atoi(sep.arg[86+y]); + + sp.targettype=(SpellTargetType)atoi(sep.arg[98]); + sp.basediff=atoi(sep.arg[99]); + sp.skill=(SkillType)atoi(sep.arg[100]); + sp.zonetype=atoi(sep.arg[101]); + sp.EnvironmentType=atoi(sep.arg[102]); + sp.TimeOfDay=atoi(sep.arg[103]); + + for(y=0; y< 16;y++) + sp.classes[y]=atoi(sep.arg[104+y]); + + sp.CastingAnim=atoi(sep.arg[120]); + sp.TargetAnim=atoi(sep.arg[121]); + sp.TravelType=atoi(sep.arg[122]); + sp.SpellAffectIndex=atoi(sep.arg[123]); + + for(y=0; y< 23;y++) { + sp.spacing124[y]=atoi(sep.arg[124+y]); + } + + sp.ResistDiff=atoi(sep.arg[147]); + sp.dot_stacking_exempt=atoi(sep.arg[148]); + sp.deletable=atoi(sep.arg[149]); + + sp.RecourseLink = atoi(sep.arg[150]); + sp.descnum = atoi(sep.arg[155]); + sp.typedescnum = atoi(sep.arg[156]); + sp.effectdescnum = atoi(sep.arg[157]); + +// for(y=0; y< 17;y++) +// sp.Spacing4[y] = atoi(sep.arg[158+y]); + + break; + } + + fclose(sf); + + const struct SPDat_Spell_Struct *s=&sp; + + printf("Spell info for spell #%d:\n", spid); + printf(" name: %s\n", s->name); + printf(" player_1: %s\n", s->player_1); + printf(" teleport_zone: %s\n", s->teleport_zone); + printf(" you_cast: %s\n", s->you_cast); + printf(" other_casts: %s\n", s->other_casts); + printf(" cast_on_you: %s\n", s->cast_on_you); + printf(" spell_fades: %s\n", s->spell_fades); + printf(" range: %f\n", s->range); + printf(" aoerange: %f\n", s->aoerange); + printf(" pushback: %f\n", s->pushback); + printf(" pushup: %f\n", s->pushup); + printf(" cast_time: %d\n", s->cast_time); + printf(" recovery_time: %d\n", s->recovery_time); + printf(" recast_time: %d\n", s->recast_time); + printf(" buffdurationformula: %d\n", s->buffdurationformula); + printf(" buffduration: %d\n", s->buffduration); + printf(" AEDuration: %d\n", s->AEDuration); + printf(" mana: %d\n", s->mana); + printf(" base[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", s->base[0], s->base[1], s->base[2], s->base[3], s->base[4], s->base[5], s->base[6], s->base[7], s->base[8], s->base[9], s->base[10], s->base[11]); + printf(" base2[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", s->base2[0], s->base2[1], s->base2[2], s->base2[3], s->base2[4], s->base2[5], s->base2[6], s->base2[7], s->base2[8], s->base2[9], s->base2[10], s->base2[11]); + printf(" max[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", s->max[0], s->max[1], s->max[2], s->max[3], s->max[4], s->max[5], s->max[6], s->max[7], s->max[8], s->max[9], s->max[10], s->max[11]); + printf(" icon: %d\n", s->icon); + printf(" memicon: %d\n", s->memicon); + printf(" components[4]: %d, %d, %d, %d\n", s->components[0], s->components[1], s->components[2], s->components[3]); + printf(" component_counts[4]: %d, %d, %d, %d\n", s->component_counts[0], s->component_counts[1], s->component_counts[2], s->component_counts[3]); + printf(" NoexpendReagent[4]: %d, %d, %d, %d\n", s->NoexpendReagent[0], s->NoexpendReagent[1], s->NoexpendReagent[2], s->NoexpendReagent[3]); + printf(" formula[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", s->formula[0], s->formula[1], s->formula[2], s->formula[3], s->formula[4], s->formula[5], s->formula[6], s->formula[7], s->formula[8], s->formula[9], s->formula[10], s->formula[11]); + printf(" LightType: %d\n", s->LightType); + printf(" goodEffect: %d\n", s->goodEffect); + printf(" Activated: %d\n", s->Activated); + printf(" resisttype: %d\n", s->resisttype); + printf(" effectid[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", s->effectid[0], s->effectid[1], s->effectid[2], s->effectid[3], s->effectid[4], s->effectid[5], s->effectid[6], s->effectid[7], s->effectid[8], s->effectid[9], s->effectid[10], s->effectid[11]); + printf(" targettype: %d\n", s->targettype); + printf(" basediff: %d\n", s->basediff); + printf(" skill: %d\n", s->skill); + printf(" zonetype: %d\n", s->zonetype); + printf(" EnvironmentType: %d\n", s->EnvironmentType); + printf(" TimeOfDay: %d\n", s->TimeOfDay); + printf(" classes[15]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", + s->classes[0], s->classes[1], s->classes[2], s->classes[3], s->classes[4], + s->classes[5], s->classes[6], s->classes[7], s->classes[8], s->classes[9], + s->classes[10], s->classes[11], s->classes[12], s->classes[13], s->classes[14]); + printf(" CastingAnim: %d\n", s->CastingAnim); + printf(" TargetAnim: %d\n", s->TargetAnim); + printf(" SpellAffectIndex: %d\n", s->SpellAffectIndex); + printf(" RecourseLink: %d\n", s->RecourseLink); + printf(" Spacing124[23]: %d, %d, %d, %d, %d\n", s->spacing124[0], s->spacing124[1], s->spacing124[2], s->spacing124[3], s->spacing124[4]); + + + + return(0); +} + diff --git a/utils/deprecated/wr_update.sql b/utils/deprecated/wr_update.sql new file mode 100644 index 000000000..03c01a58e --- /dev/null +++ b/utils/deprecated/wr_update.sql @@ -0,0 +1,824 @@ +ALTER TABLE items ADD attuneable tinyint(3) unsigned NOT NULL default '0'; + +CREATE TABLE traps ( + id int(11) NOT NULL auto_increment, + zone varchar(16) NOT NULL default '', + x int(11) NOT NULL default '0', + y int(11) NOT NULL default '0', + z int(11) NOT NULL default '0', + chance tinyint NOT NULL default '0', + maxzdiff float NOT NULL default '0', + radius float NOT NULL default '0', + effect int(11) NOT NULL default '0', + effectvalue int(11) NOT NULL default '0', + effectvalue2 int(11) NOT NULL default '0', + skill int(11) NOT NULL default '0', + spawnchance int(11) NOT NULL default '0', + PRIMARY KEY (id) +) TYPE=MyISAM; + +drop table forage; +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; + +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; + +CREATE TABLE timers ( + char_id INT(11) NOT NULL, + type MEDIUMINT UNSIGNED NOT NULL, + start INT UNSIGNED NOT NULL, + duration INT UNSIGNED NOT NULL, + enable TINYINT NOT NULL, + PRIMARY KEY(char_id, type) +); + +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) +); + +INSERT INTO `aa_actions` VALUES (35,0,4320,0,0,1,1,0,0,264,10); +INSERT INTO `aa_actions` VALUES (36,0,64800,2738,1,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (37,0,7,2739,1,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (38,0,900,2740,100,1,6,0,0,0,0); +INSERT INTO `aa_actions` VALUES (39,0,8640,2741,0,2,0,0,0,154,10); +INSERT INTO `aa_actions` VALUES (40,0,4320,2776,2500,2,0,0,0,155,10); +INSERT INTO `aa_actions` VALUES (40,1,4320,2777,2500,2,0,0,0,155,10); +INSERT INTO `aa_actions` VALUES (40,2,4320,2778,2500,2,0,0,0,155,10); +INSERT INTO `aa_actions` VALUES (41,0,1800,2742,0,2,0,0,0,156,10); +INSERT INTO `aa_actions` VALUES (43,0,4320,2771,100,1,0,0,0,159,10); +INSERT INTO `aa_actions` VALUES (46,0,4320,2761,6000,2,7,0,0,0,0); +INSERT INTO `aa_actions` VALUES (46,1,4320,2760,6000,2,7,0,0,0,0); +INSERT INTO `aa_actions` VALUES (46,2,4320,2759,6000,2,7,0,0,0,0); +INSERT INTO `aa_actions` VALUES (38,1,900,3250,100,1,6,0,0,0,0); +INSERT INTO `aa_actions` VALUES (38,2,900,3251,100,1,6,0,0,0,0); +INSERT INTO `aa_actions` VALUES (47,0,180,2749,2500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (50,0,7200,2750,100,1,0,0,0,158,10); +INSERT INTO `aa_actions` VALUES (52,1,480,3264,16000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (52,0,480,2758,16000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (53,0,4320,2734,100,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (57,0,8640,2753,2000,1,0,0,0,157,10); +INSERT INTO `aa_actions` VALUES (58,0,2160,2752,1,5,0,0,0,161,10); +INSERT INTO `aa_actions` VALUES (60,0,4320,2754,2000,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (61,0,900,2795,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (61,1,900,2796,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (61,2,900,2797,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (62,0,900,2798,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (62,1,900,2799,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (62,2,900,2800,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (63,0,900,2792,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (63,1,900,2793,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (63,2,900,2794,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (64,0,900,2789,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (64,1,900,2790,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (64,2,900,2791,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (66,0,4320,2779,2500,1,0,0,0,162,10); +INSERT INTO `aa_actions` VALUES (66,1,4320,2780,2500,1,0,0,0,162,10); +INSERT INTO `aa_actions` VALUES (66,2,4320,2781,2500,1,0,0,0,162,10); +INSERT INTO `aa_actions` VALUES (68,0,8640,2755,6000,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (69,0,4320,2756,3000,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (70,0,4320,2757,3000,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (71,0,7,2772,3000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (72,0,4320,2764,5000,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (73,0,30,2190,1500,2,0,0,0,196,24); +INSERT INTO `aa_actions` VALUES (76,0,4320,2775,0,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (77,0,1,2871,2000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (80,0,7,2765,1,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (85,0,1,2918,2000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (86,0,4320,0,1,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (87,0,4320,2766,1,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (98,0,4320,2190,0,1,0,0,0,165,10); +INSERT INTO `aa_actions` VALUES (102,0,4320,5244,0,1,0,0,0,166,10); +INSERT INTO `aa_actions` VALUES (107,0,4320,5232,0,1,0,0,0,167,10); +INSERT INTO `aa_actions` VALUES (109,0,6000,5233,0,1,0,0,0,164,10); +INSERT INTO `aa_actions` VALUES (110,0,900,5234,0,1,0,0,0,163,10); +INSERT INTO `aa_actions` VALUES (111,0,2160,5229,0,4,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (111,1,2160,5230,0,4,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (111,2,2160,5231,0,4,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (117,0,4320,2748,2000,2,0,0,0,160,10); +INSERT INTO `aa_actions` VALUES (126,0,300,3290,3000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (127,0,720,3289,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (128,0,900,3291,5000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (153,0,180,3297,3500,1,0,0,0,256,10); +INSERT INTO `aa_actions` VALUES (153,1,180,3297,3500,1,0,0,0,256,10); +INSERT INTO `aa_actions` VALUES (153,2,180,3297,3500,1,0,0,0,256,10); +INSERT INTO `aa_actions` VALUES (169,0,180,3252,750,4,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (169,1,180,3253,750,4,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (169,2,180,3254,750,4,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (170,0,240,3255,1000,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (170,1,240,3255,1000,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (170,2,240,3255,1000,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (171,0,120,3274,2000,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (171,1,120,3274,2000,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (171,2,120,3274,2000,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (172,0,480,3338,10000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (173,0,600,3258,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (173,1,600,3258,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (173,2,600,3258,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (174,0,540,3265,500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (174,1,540,3266,500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (174,2,540,3267,500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (175,0,540,3268,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (175,1,540,3269,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (175,2,540,3270,8000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (176,0,0,3248,4000,5,10,0,0,0,0); +INSERT INTO `aa_actions` VALUES (176,1,0,3249,4000,5,10,0,0,0,0); +INSERT INTO `aa_actions` VALUES (177,0,720,3283,6500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (177,1,720,3284,6500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (177,2,720,3285,6500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (180,0,2160,3261,0,1,0,0,0,282,10); +INSERT INTO `aa_actions` VALUES (180,1,2160,3262,0,1,0,0,0,282,10); +INSERT INTO `aa_actions` VALUES (180,2,2160,3263,0,1,0,0,0,282,10); +INSERT INTO `aa_actions` VALUES (184,0,900,3271,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (184,1,900,3272,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (184,2,900,3273,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (185,0,1320,3277,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (185,1,1320,3278,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (185,2,1320,3279,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (199,0,18,3282,0,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (207,0,1320,3286,6500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (207,1,1320,3287,6500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (207,2,1320,3288,6500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (208,0,900,3292,6500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (208,1,900,3293,6500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (208,2,900,3294,6500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (212,0,0,5243,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (217,0,0,0,0,1,13,0,0,0,0); +INSERT INTO `aa_actions` VALUES (219,0,5,3614,1750,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (226,0,8640,2751,6000,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (245,0,4320,0,0,1,11,0,0,0,0); +INSERT INTO `aa_actions` VALUES (254,0,7200,4549,3000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (247,0,60,4788,0,2,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (254,1,7200,4550,3000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (254,2,7200,4551,3000,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (255,0,1800,4790,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (255,1,1800,4791,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (255,2,1800,4792,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (255,3,1800,4793,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (255,4,1800,4794,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (257,0,1800,4796,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (257,1,1800,4797,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (257,2,1800,4798,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (257,3,1800,4799,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (257,4,1800,4800,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (261,0,4320,4552,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (261,1,4320,4553,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (261,2,4320,4554,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (265,0,900,0,0,1,5,0,0,0,0); +INSERT INTO `aa_actions` VALUES (276,0,3600,0,0,1,4,0,10000,0,0); +INSERT INTO `aa_actions` VALUES (277,0,1800,4564,500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (277,1,1800,4565,500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (277,2,1800,4566,500,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (285,0,180,3297,0,1,0,0,0,256,10); +INSERT INTO `aa_actions` VALUES (285,1,180,3298,0,1,0,0,0,256,10); +INSERT INTO `aa_actions` VALUES (285,2,180,3299,0,1,0,0,0,256,10); +INSERT INTO `aa_actions` VALUES (286,0,4320,0,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (289,0,4320,0,0,1,2,0,0,0,0); +INSERT INTO `aa_actions` VALUES (290,0,4320,0,0,1,3,0,0,0,0); +INSERT INTO `aa_actions` VALUES (298,0,1800,4828,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (298,1,1800,4829,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (298,2,1800,4830,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (298,3,1800,4831,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (298,4,1800,4832,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (300,0,3600,4925,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (300,1,3600,4926,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (300,2,3600,4927,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (303,0,60,4833,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (304,0,60,4834,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (305,0,69,4835,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (307,0,1800,4836,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (307,1,1800,4837,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (307,2,1800,4838,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (307,3,1800,4839,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (307,4,1800,4840,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (308,0,4320,5245,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (308,1,4320,5245,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (308,2,4320,5245,0,1,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (89,0,0,1471,0,0,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (89,1,0,2712,0,0,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (89,2,0,2718,0,0,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (89,3,0,3228,0,0,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (89,4,0,4908,0,0,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (89,5,0,4910,0,0,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (68,1,0,2488,0,0,0,0,0,0,0); +INSERT INTO `aa_actions` VALUES (276,1,3600,0,0,1,4,0,10000,0,0); +INSERT INTO `aa_actions` VALUES (276,2,3600,0,0,1,4,0,10000,0,0); + + +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51176, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51191, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51185, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51200, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51186, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51201, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51187, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51202, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51188, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51203, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51189, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51204, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51190, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51205, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51177, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51192, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51178, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51193, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51179, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51194, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51180, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51195, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51181, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51196, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51182, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51197, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51183, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51198, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51184, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 51199, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(55, 22763, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(17, 22765, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(68, 22757, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(90, 22746, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(90, 14974, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(117, 22766, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(117, 22770, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(58, 22754, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(121, 22771, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(70, 22756, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(229, 14977, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(229, 65601, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(229, 12784, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(229, 65600, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(225, 65475, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(225, 65474, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(225, 65473, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14989, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14990, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14991, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14992, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14993, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14994, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14995, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14996, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14981, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14982, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14983, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14984, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14985, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14986, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14987, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14988, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14919, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14939, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14940, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 14917, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(106, 12444, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(15, 22751, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(116, 22769, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(98, 22761, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(47, 22752, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14989, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14990, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14991, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14992, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14993, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14994, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14995, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14996, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14981, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14982, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14983, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14984, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14985, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14986, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14987, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14988, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14919, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14939, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14940, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 14917, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(84, 12444, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(224, 65483, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(224, 65478, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(224, 65484, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(224, 65485, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(5, 22750, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(110, 22767, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(110, 22768, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(46, 22760, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(46, 22752, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(181, 22751, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(113, 22772, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(102, 14978, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(74, 22748, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(51, 22749, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(107, 14977, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 13076, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 13019, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 7007, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(280, 51176, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(280, 51191, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(280, 51177, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(280, 51192, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(280, 51178, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(280, 51193, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(280, 51179, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(280, 51194, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(280, 51180, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(280, 51195, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(280, 58684, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(182, 22751, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(61, 22764, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(13, 22751, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(69, 22745, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(69, 19113, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(205, 12561, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(204, 12560, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(204, 30887, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(210, 30886, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(208, 12560, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(208, 30889, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(216, 30891, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(216, 30890, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(0, 14977, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(125, 22773, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(128, 22774, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(62, 22764, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(14, 22751, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(3, 22762, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(83, 22759, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(83, 14972, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(93, 14976, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(75, 22758, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(89, 14973, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(96, 19113, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(96, 14980, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(226, 65480, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(226, 65479, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(226, 65478, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(38, 22747, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(296, 51185, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(296, 51186, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(296, 51201, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(296, 51202, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(296, 51182, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(296, 51197, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(296, 51183, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(296, 51198, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(296, 51199, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(296, 13076, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(296, 7007, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14989, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14990, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14991, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14992, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14993, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14994, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14995, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14996, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14981, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14982, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14983, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14984, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14985, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14986, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14987, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14988, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14919, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14939, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14940, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 14917, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(82, 12444, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(12, 22751, 0, 100); +INSERT INTO fishing (zoneid,Itemid,skill_level,chance) VALUES(120, 22775, 0, 100); + +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51206, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51381, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51215, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51390, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51216, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51391, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51217, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51392, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51218, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51393, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51219, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51394, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51220, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51395, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51207, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51382, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51208, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51383, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51209, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51384, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51210, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51385, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51211, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51386, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51212, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51387, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51213, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51388, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51214, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 51389, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 58684, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 58685, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 58683, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 58686, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 58153, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 55577, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(283, 63833, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(209, 28022, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(87, 12728, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(68, 14968, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(117, 19125, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(117, 19122, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(117, 19123, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(117, 19120, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(117, 19121, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(117, 19126, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(117, 19124, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(227, 59044, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(121, 19125, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(121, 19122, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(121, 19123, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(70, 14970, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(229, 65593, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(229, 65591, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(229, 65592, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(229, 65590, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(86, 12634, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(86, 12795, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(22, 14922, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(15, 14915, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(116, 19140, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(94, 12767, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(98, 14998, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(30, 12634, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(30, 14930, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(47, 14947, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51206, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51381, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51215, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51390, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51216, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51391, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51217, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51392, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51218, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51393, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51219, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51394, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51220, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51395, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51207, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51382, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51208, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51383, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51209, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51384, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51210, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51385, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51211, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51386, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51212, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51387, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51213, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51388, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51214, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 51389, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 58684, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 58685, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 58683, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(284, 58686, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(78, 12464, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(78, 12633, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(84, 12633, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(92, 12795, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(92, 12633, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(92, 12632, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(157, 14909, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(157, 14910, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(118, 19122, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(54, 14954, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(54, 16594, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(224, 65480, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(224, 59044, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(224, 65489, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(166, 14909, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(166, 14910, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(110, 19125, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(110, 19122, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(110, 19123, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(46, 14946, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(181, 8868, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(181, 8154, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(181, 8792, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(64, 13016, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(64, 8798, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(64, 13076, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(64, 13019, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(64, 7007, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(20, 16490, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(85, 12778, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(51, 14951, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(27, 14927, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(57, 16594, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(57, 14957, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(237, 65597, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(237, 65587, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(237, 65595, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(237, 65596, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(59, 65588, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(59, 65585, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(59, 13041, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(59, 13099, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(33, 14933, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(33, 16496, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51206, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51381, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51215, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51390, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51216, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51391, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51217, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51392, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51218, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51393, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51219, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51394, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51220, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51395, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51207, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51382, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51208, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51383, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51209, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51384, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51210, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51385, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51211, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51386, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51212, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51387, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51213, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51388, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51214, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 51389, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 58684, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 58685, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 58683, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 58686, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 58153, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 58154, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 55577, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(280, 63833, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(182, 8868, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(182, 58686, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(182, 8154, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(182, 8994, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(182, 8792, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(25, 14925, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(13, 14913, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(34, 14934, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(37, 14937, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(69, 14969, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(217, 29748, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(127, 19142, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(76, 12574, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(206, 12564, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(201, 12573, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(201, 12574, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(202, 29748, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(207, 12577, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(216, 16498, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(4, 14904, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51206, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51381, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51215, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51390, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51216, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51391, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51217, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51392, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51218, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51393, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51219, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51394, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51220, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51395, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51207, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51382, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51208, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51383, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51209, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51384, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51210, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51385, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51211, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51386, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51212, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51387, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51213, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51388, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51214, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 51389, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 58684, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 58685, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 58683, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 58686, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(281, 63833, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(298, 58685, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(50, 14950, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51206, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51381, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51215, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51390, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51216, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51391, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51217, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51392, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51218, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51393, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51219, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51394, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51220, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51395, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51207, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51382, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51208, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51383, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51209, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51384, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51210, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51385, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51211, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51386, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51212, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51387, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51213, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51388, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51214, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 51389, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 58684, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 58685, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 58683, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(282, 58686, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(286, 13047, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51206, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51381, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51215, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51390, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51216, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51391, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51217, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51392, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51218, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51393, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51219, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51394, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51220, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51395, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51207, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51382, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51208, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51383, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51209, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51384, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51210, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51385, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51211, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51386, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51212, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51387, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51213, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51388, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51214, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 51389, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 58685, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 58683, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 58686, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(285, 63833, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(165, 30619, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(91, 12782, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(91, 12487, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(91, 12781, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(91, 12783, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(114, 19122, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(114, 19123, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(114, 19137, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(114, 19138, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(162, 24092, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(162, 24093, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(56, 14956, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(100, 6973, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(83, 12785, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(83, 12786, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(83, 12632, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(83, 12784, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(220, 29793, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(220, 29746, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(124, 19139, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(124, 19137, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(124, 19138, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(230, 13044, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(171, 12694, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(93, 12787, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(93, 12788, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(89, 12767, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(115, 19122, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(115, 19123, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(226, 65480, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(111, 12694, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(38, 14938, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(95, 12787, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(95, 12767, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 51206, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 51215, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 51216, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 51391, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 51217, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 51395, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 51207, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 51382, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 51383, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 51209, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 51210, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 51213, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 51388, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 58684, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 58685, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 58683, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 58686, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 13044, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 58153, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(292, 63833, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(109, 14905, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(109, 12778, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(158, 24093, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(119, 19140, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(119, 16589, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(79, 16490, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(21, 14921, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(120, 19139, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(120, 19137, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(120, 19138, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 51206, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 51381, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 51216, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 51393, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 51382, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 51208, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 51383, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 51209, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 51384, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 51210, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 51385, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 51211, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 51387, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 51213, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 58684, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 58685, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 58683, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 58686, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 13044, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 58153, 0, 100); +INSERT INTO forage (zoneid,Itemid,level,chance) VALUES(291, 63833, 0, 100); + + + + diff --git a/utils/patches/mail_opcodes.conf b/utils/patches/mail_opcodes.conf new file mode 100644 index 000000000..6bb3b89d9 --- /dev/null +++ b/utils/patches/mail_opcodes.conf @@ -0,0 +1,14 @@ +#Mail and Chat Channel opcodes +OP_MailLogin=0x01 +OP_Mail=0x02 +OP_ChannelMessage=0x03 +OP_ChannelAnnounceJoin=0x04 +OP_ChannelAnnounceLeave=0x05 +OP_Buddy=0x06 +OP_MailHeaderCount=0x0d +OP_MailHeader=0x0e +OP_MailSendBody=0x0f +OP_MailNew=0x10 +OP_MailDeliveryStatus=0x12 +OP_MailboxChange=0x14 +OP_Ignore=0x15 diff --git a/utils/patches/opcodes.conf b/utils/patches/opcodes.conf new file mode 100644 index 000000000..92f208e00 --- /dev/null +++ b/utils/patches/opcodes.conf @@ -0,0 +1,512 @@ +#ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +#Unknown Mapping: +#OP_Action2 -> OP_Damage +#OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +#Name Differences: +#OP_CancelInvite -> OP_GroupCancelInvite +#OP_GMFind -> OP_FindPersonRequest +#OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 #used for unknown explorer + +#world packets +OP_ApproveWorld=0x3c25 # ShowEQ 10/27/05 +OP_LogServer=0x0fa6 # ShowEQ 10/27/05 +OP_MOTD=0x024d # ShowEQ 10/27/05 +OP_SendLoginInfo=0x4dd0 # ShowEQ 10/27/05 +OP_DeleteCharacter=0x26c9 # ShowEQ 10/27/05 +OP_SendCharInfo=0x4513 # ShowEQ 10/27/05 +OP_ExpansionInfo=0x04ec # ShowEQ 10/27/05 +OP_CharacterCreate=0x10b2 # EQEmu 11/28/05 +OP_RandomNameGenerator=0x23d4 # EQEmu 11/28/05 +OP_GuildsList=0x6957 #same as zone guild list afaik +OP_ApproveName=0x3ea6 # EQEmu 11/28/05 +OP_EnterWorld=0x7cba # ShowEQ 10/27/05 +OP_PostEnterWorld=0x52A4 # EQEmu 06/29/05 +OP_World_Client_CRC1=0x5072 # ShowEQ 10/27/05 +OP_World_Client_CRC2=0x5b18 # ShowEQ 10/27/05 +OP_SetChatServer=0x00d7 # ShowEQ 10/27/05 +OP_SetChatServer2=0x6536 # ShowEQ 10/27/05 +OP_ZoneServerInfo=0x61b6 # ShowEQ 10/27/05 +OP_WorldComplete=0x509d # ShowEQ 10/27/05 +OP_ZoneUnavail=0x407C +OP_WorldClientReady=0x5e99 # EQEmu 11/28/05 (Guess - Doodman) +OP_WorldUnknown001=0x0000 # EQEmu 06/29/05 (New to 6/29) +OP_CharacterStillInZone=0x60fa # world->client. reject. +OP_WorldChecksumFailure=0x7D37 # world->client. reject. +OP_WorldLoginFailed=0x8DA7 # world->client. reject. +OP_WorldLogout=0x7718 # client->world +OP_WorldLevelTooHigh=0x583b # world->client. Cancels zone in. +OP_CharInacessable=0x436A # world->client. Cancels zone in. + +#Zone in opcodes +OP_ZoneEntry=0x7213 # ShowEQ 10/27/05 +OP_ZoneInUnknown=0x0000 +OP_AckPacket=0x7752 # ShowEQ 10/27/05 +OP_NewZone=0x0920 # ShowEQ 10/27/05 +OP_ReqClientSpawn=0x0322 # ShowEQ 10/27/05 +OP_ZoneSpawns=0x2e78 # ShowEQ 10/27/05 +OP_CharInventory=0x5394 # EQEmu 06/29/05 +OP_SetServerFilter=0x6563 # ShowEQ 10/27/05 +OP_LockoutTimerInfo=0x7c12 # ShowEQ 10/27/05 +OP_SendZonepoints=0x3eba # ShowEQ 10/27/05 +OP_SpawnDoor=0x4c24 # ShowEQ 10/27/05 +OP_ReqNewZone=0x7ac5 # ShowEQ 10/27/05 +OP_PlayerProfile=0x75df # ShowEQ 10/27/05 +OP_TimeOfDay=0x1580 # ShowEQ 10/27/05 +OP_SendExpZonein=0x0587 # ShowEQ 10/27/05 +OP_WorldObjectsSent=0x1FA1 # New for SoF 1/1/09 +OP_BlockedBuffs=0x39C4 # New for SoF 1/1/09 + +OP_Logout=0x61ff +OP_LogoutReply=0x48c2 #0x0F66 is not quite right.. this causes disconnect error... +OP_LevelUpdate=0x6d44 +OP_Stamina=0x7a83 # ShowEQ 10/27/05 + +#Petition Opcodes +OP_PetitionSearch=0x0000 #search term for petition +OP_PetitionSearchResults=0x0000 #(list of?) matches from search +OP_PetitionSearchText=0x0000 #text results of search +OP_Petition=0x251f +OP_PetitionUpdate=0x0000 #guess +OP_PetitionCheckout=0x0000 +OP_PetitionCheckIn=0x0000 +OP_PetitionQue=0x0000 +OP_PetitionUnCheckout=0x0000 +OP_PetitionDelete=0x5692 +OP_DeletePetition=0x0000 +OP_PetitionResolve=0x0000 #0x0000 +OP_PDeletePetition=0x0000 +OP_PetitionBug=0x0000 +OP_PetitionRefresh=0x0000 +OP_PetitionCheckout2=0x0000 +OP_PetitionViewPetition=0x0000 + +#Guild Opcodes +#list to client : 0x0F4D, 0x147d, 0x18B7, 0x2ec9, 0x3230, 0x32CF, 0x461A, 0x4CC7 +# 0x6966, 0x712A, 0x754E, 0x7C32, 0x7C59 +# some from client: 0x7825 +OP_ZoneGuildList=0x6957 (one entry too long) # ShowEQ 10/27/05 +OP_GetGuildMOTD=6d5d +OP_GuildMemberList=0x147d # ShowEQ 10/27/05 +OP_GuildMemberUpdate=0x0f4d # ShowEQ 10/27/05 +OP_GuildRemove=0x0000 +OP_GuildPeace=0x0000 +OP_GuildWar=0x0000 +OP_GuildLeader=0x0000 +OP_GuildDemote=0x0000 +OP_GuildMOTD=0x475a # ShowEQ 10/27/05 +OP_SetGuildMOTD=0x0000 +OP_GetGuildsList=0x0000 +OP_GuildInvite=0x0000 +OP_GuildPublicNote=0x17a2 # ShowEQ 10/27/05 +OP_GuildDelete=0x0000 +OP_GuildInviteAccept=0x0000 +OP_GuildManageRemove=0x0000 +OP_GuildManageAdd=0x0000 +OP_GuildManageStatus=0x0000 +OP_GuildBank=0x0000 + + +#GM/guide opcodes +OP_GMServers=0x3387 #/servers +OP_GMBecomeNPC=0x7864 #/becomenpc +OP_GMZoneRequest=0x1306 #/zone +OP_GMSearchCorpse=0x3c32 #/searchcorpse +OP_GMHideMe=0x15b2 #/hideme +OP_GMGoto=0x1cee #/goto +OP_GMDelCorpse=0x0000 #/delcorpse +OP_GMApproval=0x0c0f #/approval +OP_GMToggle=0x7fea #/toggletell +OP_GMZoneRequest2=0x0000 +OP_GMSummon=0x1edc #/summon +OP_GMEmoteZone=0x39f2 #/emotezone +OP_GMEmoteWorld=0x3383 #/emoteworld (not implemented) +OP_GMFind=0x5930 #/find +OP_GMKick=0x692c #/kick +OP_GMNameChange=0x0000 + +OP_SafePoint=0x0000 +OP_Bind_Wound=0x601d +OP_GMTraining=0x238f +OP_GMEndTraining=0x613d +OP_GMTrainSkill=0x11d2 +# OP_GMEndTrainingResponse=0x0000 +OP_Animation=0x2acf # ShowEQ 10/27/05 +OP_Stun=0x0000 +OP_MoneyUpdate=0x267c +OP_IncreaseStats=0x0000 +OP_CrashDump=0x7825 +OP_ReadBook=0x1496 +OP_Dye=0x00dd # ShowEQ 10/27/05 +OP_Consume=0x77d7 # ShowEQ 10/27/05 +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_Consent=0x1081 # ShowEQ 10/27/05 +OP_LFGCommand=0x68ac # ShowEQ 10/27/05 +OP_LFGGetMatchesRequest=0x022f # ShowEQ 10/27/05 +OP_LFGAppearance=0x0000 +OP_LFGResponse=0x0000 +OP_LFGGetMatchesResponse=0x45d0 # ShowEQ 10/27/05 +OP_LootItem=0x7081 # ShowEQ 10/27/05 +OP_Bug=0x7ac2 # ShowEQ 10/27/05 +OP_BoardBoat=0x4298 # ShowEQ 10/27/05 +OP_Save=0x736b # ShowEQ 10/27/05 +OP_Camp=0x78c1 # ShowEQ 10/27/05 +OP_EndLootRequest=0x2316 # ShowEQ 10/27/05 +OP_LoadSpellSet=0x403e # ShowEQ 10/27/05 +OP_AutoAttack=0x5e55 # ShowEQ 10/27/05 +OP_Consider=0x65ca # ShowEQ 10/27/05 +OP_Emote=0x547a # ShowEQ 10/27/05 +OP_PetCommands=0x10a1 # ShowEQ 10/27/05 +OP_SpawnAppearance=0x7c32 # ShowEQ 10/27/05 +OP_DeleteSpawn=0x55bc # ShowEQ 10/27/05 +OP_FormattedMessage=0x5a48 # ShowEQ 10/27/05 +OP_WhoAllRequest=0x5cdd # ShowEQ 10/27/05 +OP_WhoAllResponse=0x757b # ShowEQ 10/27/05 +OP_AutoAttack2=0x0701 # ShowEQ 10/27/05 +OP_SetRunMode=0x4aba # ShowEQ 10/27/05 +OP_SimpleMessage=0x673c # ShowEQ 10/27/05 +OP_SaveOnZoneReq=0x1540 # ShowEQ 10/27/05 +OP_SenseHeading=0x05ac # ShowEQ 10/27/05 +OP_Buff=0x6a53 # ShowEQ 10/27/05 +OP_LootComplete=0x0a94 # ShowEQ 10/27/05 +OP_EnvDamage=0x31b3 # ShowEQ 10/27/05 +OP_Split=0x4848 # ShowEQ 10/27/05 +OP_Surname=0x4668 # ShowEQ 10/27/05 +OP_MoveItem=0x420f # ShowEQ 10/27/05 +OP_FaceChange=0x0f8e # ShowEQ 10/27/05 +OP_ItemPacket=0x3397 # ShowEQ 10/27/05 +OP_ItemLinkResponse=0x667c # ShowEQ 10/27/05 +OP_ClientReady=0x5e20 # ShowEQ 10/27/05 +OP_ZoneChange=0x5dd8 # ShowEQ 10/27/05 +OP_MemorizeSpell=0x308e # ShowEQ 10/27/05 +OP_ItemLinkClick=0x53e5 # ShowEQ 10/27/05 +OP_SwapSpell=0x2126 # ShowEQ 10/27/05 +OP_Forage=0x7c33 # ShowEQ 10/27/05 +OP_ConsentResponse=0x6380 # ShowEQ 10/27/05 +OP_BazaarSearch=0x1ee9 # ShowEQ 10/27/05 +OP_NewSpawn=0x1860 # ShowEQ 10/27/05 +OP_WearChange=0x7441 # ShowEQ 10/27/05 +OP_Action=0x497c # ShowEQ 10/27/05 +OP_SpecialMesg=0x2372 # ShowEQ 10/27/05 +OP_Bazaar=0x0000 +OP_LeaveBoat=0x67c9 # ShowEQ 10/27/05 +OP_Weather=0x254d # ShowEQ 10/27/05 +OP_LFPGetMatchesRequest=0x35a6 # ShowEQ 10/27/05 +OP_Illusion=0x448d # ShowEQ 10/27/05 +OP_TargetMouse=0x6c47 # ShowEQ 10/27/05 +OP_InspectAnswer=0x2403 # ShowEQ 10/27/05 +OP_GMKill=0x6980 # ShowEQ 10/27/05 +OP_MoneyOnCorpse=0x7fe4 # ShowEQ 10/27/05 +OP_ClickDoor=0x043b # ShowEQ 10/27/05 +OP_MoveDoor=0x700d # ShowEQ 10/27/05 +OP_LootRequest=0x6f90 # ShowEQ 10/27/05 +OP_YellForHelp=0x61ef # ShowEQ 10/27/05 +OP_ManaChange=0x4839 # ShowEQ 10/27/05 +OP_ConsentDeny=0x4e8c # ShowEQ 10/27/05 +OP_LFPCommand=0x6f82 # ShowEQ 10/27/05 +OP_RandomReply=0x6cd5 # ShowEQ 10/27/05 +OP_DenyResponse=0x7c66 # ShowEQ 10/27/05 +OP_ConsiderCorpse=0x773f # ShowEQ 10/27/05 +OP_ConfirmDelete=0x3838 # ShowEQ 10/27/05 +OP_MobHealth=0x0695 # ShowEQ 10/27/05 +OP_SkillUpdate=0x6a93 # ShowEQ 10/27/05 +OP_RandomReq=0x5534 # ShowEQ 10/27/05 +OP_CastSpell=0x304b # ShowEQ 10/27/05 +OP_ClientUpdate=0x14cb # ShowEQ 10/27/05 +OP_Report=0x7f9d +OP_GroundSpawn=0x0f47 # ShowEQ 10/27/05 +OP_TargetCommand=0x0dfe # ShowEQ 10/27/05 +OP_LFPGetMatchesResponse=0x45d0 # ShowEQ 10/27/05 +OP_Jump=0x0797 # ShowEQ 10/27/05 +OP_ExpUpdate=0x5ecd # ShowEQ 10/27/05 +OP_Death=0x6160 # ShowEQ 10/27/05 +OP_BecomeCorpse=0x0000 +OP_GMLastName=0x23a1 # ShowEQ 10/27/05 +OP_InitialMobHealth=0x3d2d # ShowEQ 10/27/05 +OP_Mend=0x14ef # ShowEQ 10/27/05 +OP_MendHPUpdate=0x0000 +OP_Feedback=0x5306 # ShowEQ 10/27/05 +OP_TGB=0x0c11 # ShowEQ 10/27/05 +OP_InterruptCast=0x0000 +OP_Damage=0x5c78 # ShowEQ 10/27/05 +OP_ChannelMessage=0x1004 # ShowEQ 10/27/05 +OP_LevelAppearance=0x0000 +OP_MultiLineMsg=0x0000 +OP_Charm=0x0000 +OP_DeleteSpell=0x0000 +OP_ApproveZone=0x0000 +OP_Assist=0x7709 +OP_AugmentItem=0x0000 +OP_BazaarInspect=0x0000 +OP_ClientError=0x0000 +OP_DeleteItem=0x0000 +OP_ControlBoat=0x0000 +OP_DumpName=0x0000 +OP_FeignDeath=0x0000 +OP_Heartbeat=0x0000 +OP_ItemName=0x0000 +OP_LDoNButton=0x0000 +OP_MoveCoin=0x0000 +OP_ReloadUI=0x0000 +OP_CameraEffect=0x0000 +OP_SpellEffect=0x0000 +OP_RemoveNimbusEffect=0x0000 + +# SoD Opcodes +OP_SpawnPositionUpdate=0x0000 # 0x4656 +OP_ManaUpdate=0x0000 # 0x4b61 +OP_EnduranceUpdate=0x0000 # 0x02d6 +OP_MobManaUpdate=0x0000 # 0x2ac1 +OP_MobEnduranceUpdate=0x0000 # 0x6c5f + +#bazaar trader stuff stuff: +#become and buy from +#Server->Client: [ Opcode: OP_Unknown (0x0000) Size: 8 ] +# 0: 46 01 00 00 39 01 00 00 | F...9... +OP_TraderDelItem=0x0000 +OP_BecomeTrader=0x0000 +OP_TraderShop=0x0000 +OP_TraderItemUpdate=0x0000 +OP_Trader=0x0000 +OP_ShopItem=0x0000 +OP_TraderBuy=0x6dd8 # ShowEQ 10/27/05 + +#pc/npc trading +OP_TradeRequest=0x3ea0 # ShowEQ 10/27/05 +OP_TradeAcceptClick=0x0065 # ShowEQ 10/27/05 +OP_TradeRequestAck=0x4048 # ShowEQ 10/27/05 +OP_TradeCoins=0x0000 +OP_FinishTrade=0x0000 +OP_CancelTrade=0x2dc1 # ShowEQ 10/27/05 +OP_TradeMoneyUpdate=0x0000 #not sure +OP_TradeBusy=0x6839 # + +#merchant crap +OP_ShopPlayerSell=0x0e13 # ShowEQ 10/27/05 +OP_ShopEnd=0x7e03 # ShowEQ 10/27/05 +OP_ShopPlayerBuy=0x221e +OP_ShopRequest=0x45f9 # ShowEQ 10/27/05 +OP_ShopDelItem=0x0000 #0x0000 maybe, 16 bytes though + +#tradeskill stuff: +OP_ClickObject=0x3bc2 # ShowEQ 10/27/05 +OP_ClickObjectAction=0x6937 +OP_RecipeDetails=0x4ea2 +OP_RecipesFavorite=0x23f0 +OP_RecipesSearch=0x164d +OP_RecipeReply=0x0000 +OP_RecipeAutoCombine=0x0353 +OP_TradeSkillCombine=0x0b40 + +OP_RequestDuel=0x0000 +OP_DuelResponse=0x0000 +OP_DuelResponse2=0x0000 #when accepted + +OP_RezzComplete=0x0000 #packet wrong on this +OP_RezzRequest=0x0000 #packet wrong on this +OP_RezzAnswer=0x0000 #packet wrong on this +OP_SafeFallSuccess=0x0000 +OP_Shielding=0x0000 +OP_TargetReject=0x0000 +OP_TestBuff=0x0000 +OP_Track=0x5011 # ShowEQ 10/27/05 +OP_TrackTarget=0x0000 +OP_TrackUnknown=0x0000 #size 0 right after OP_Track + +#Tribute Packets: +OP_OpenGuildTributeMaster=0x0000 +OP_OpenTributeMaster=0x512e #open tribute master window +OP_OpenTributeReply=0x27B3 #reply to open request +OP_SelectTribute=0x625d #clicking on a tribute, and text reply +OP_TributeItem=0x6f6c #donating an item +OP_TributeMoney=0x27b3 #donating money +OP_TributeNPC=0x7f25 #seems to be missing now +OP_TributeToggle=0x2688 #activating/deactivating tribute +OP_TributeTimer=0x4665 #testing #a 4 byte tier update, 10 minutes for seconds +OP_TributePointUpdate=0x6463 #16 byte point packet +OP_TributeUpdate=0x5639 # ShowEQ 10/27/05 +OP_GuildTributeInfo=0x5e3d # ShowEQ 10/27/05 +OP_TributeInfo=0x152d +OP_SendGuildTributes=0x5e3a # request packet, 4 bytes +OP_SendTributes=0x067a # request packet, 4 bytes, migth be backwards +# 27b3 4665 + +#Adventure packets: +# Opcodes: 0x3906, 0x0677, 0x0FF1, 0x16C6 +# 0x3F26, 0x49A5, 0x64AC +OP_LeaveAdventure=0x0000 +OP_AdventureFinish=0x0000 +OP_AdventureInfoRequest=0x0000 #right click adventure recruiter +OP_AdventureInfo=0x0000 #text reply to right click +OP_AdventureUpdate=0x0000 #prolly 0x0677, it marks the compass. +OP_AdventureRequest=0x0000 +OP_AdventureDetails=0x0000 #prolly 0x0D0F +OP_AdventureData=0x0000 +OP_AdventureMerchantRequest=0x0000 +OP_AdventureMerchantResponse=0x0000 +OP_AdventureMerchantPurchase=0x0000 +OP_AdventureMerchantSell=0x0000 +OP_AdventurePointsUpdate=0x0000 #not sure, followed purchase +# request stats: 0x0000, reply 0x0000 +# request leaderboard: 0x0000, reply 0x0000 + +#Group Opcodes +OP_GroupDisband=0x0e76 # ShowEQ 10/27/05 +OP_GroupInvite=0x1b48 # ShowEQ 10/27/05 +OP_GroupFollow=0x7bc7 # ShowEQ 10/27/05 +OP_GroupUpdate=0x2dd6 # ShowEQ 10/27/05 +OP_GroupAcknowledge=0x0000 +OP_GroupCancelInvite=0x1f27 # ShowEQ 10/27/05 +OP_GroupDelete=0x0000 +OP_GroupFollow2=0x0000 #not sure #follow into an allready established group +OP_GroupInvite2=0x0000 #this is an invite, but with a strange length (193) +OP_CancelInvite=0x0000 + +OP_RaidJoin=0x1f21 # ShowEQ 10/27/05 +OP_RaidInvite=0x5891 # ShowEQ 10/27/05 +OP_RaidUpdate=0x1f21 # EQEmu 06/29/05 + + +OP_ZoneComplete=0x0000 +OP_ItemLinkText=0x0000 +OP_ClearObject=0x0000 +OP_DisciplineUpdate=0x0000 +OP_LocInfo=0x0000 +OP_FindPersonRequest=0x3c41 # ShowEQ 10/27/05 +OP_FindPersonReply=0x5711 # ShowEQ 10/27/05 +OP_ForceFindPerson=0x0000 +OP_LoginComplete=0x0000 +OP_ShopEndConfirm=0x0000 +OP_Sound=0x0000 +#OP_Zone_MissingName01=0x0000 #remove on recompile +OP_MobRename=0x0498 # ShowEQ 10/27/05 +OP_BankerChange=0x0000 + +#Button-push commands +OP_Taunt=0x5e48 +OP_CombatAbility=0x5ee8 +OP_SenseTraps=0x5666 # ShowEQ 10/27/05 +OP_PickPocket=0x0000 +OP_DisarmTraps=0x0000 +OP_Disarm=0x0000 +OP_Hide=0x0000 +OP_Sneak=0x0000 +OP_Fishing=0x0b36 +OP_InstillDoubt=0x0000 + +#Task packets +OP_CompletedTasks=0x76a2 # ShowEQ 10/27/05 +OP_TaskDescription=0x5ef7 # ShowEQ 10/27/05 +OP_TaskActivity=0x682d # ShowEQ 10/27/05 +OP_TaskMemberList=0x0000 #not sure +OP_OpenNewTasksWindow=0x0000 #combined with OP_AvaliableTask I think +OP_AvaliableTask=0x0000 +OP_AcceptNewTask=0x0000 +OP_CancelTask=0x0000 +OP_DeclineAllTasks=0x0000 #not sure, 12 bytes +#task complete related: 0x0000 (24 bytes), 0x0000 (8 bytes), 0x0000 (4 bytes) + + +OP_RequestClientZoneChange=0x7834 # ShowEQ 10/27/05 + +OP_SendAATable=0x367d # ShowEQ 10/27/05 +OP_UpdateAA=0x0000 +OP_RespondAA=0x0000 +OP_SendAAStats=0x5918 # ShowEQ 10/27/05 +OP_AAAction=0x0681 # ShowEQ 10/27/05 +OP_AAExpUpdate=0x5f58 # ShowEQ 10/27/05 + +OP_PurchaseLeadershipAA=0x0000 +OP_UpdateLeadershipAA=0x0000 +OP_LeadershipExpUpdate=0x0000 +OP_LeadershipExpToggle=0x5b37 + +#discovered opcodes not yet used: +OP_PlayMP3=0x0000 +OP_FriendsWho=0x0000 +OP_MoveLogRequest=0x7510 #gone I think +OP_MoveLogDisregard=0x0000 #gone I think +OP_ReclaimCrystals=0x7cfe +OP_CrystalCountUpdate=0x0000 +OP_DynamicWall=0x0000 +#new titles avaliable: 0x4ECA +OP_CustomTitles=0x2a28 # ShowEQ 10/27/05 +OP_RequestTitles=0x0000 # EQEmu 06/29/05 +OP_SendTitleList=0x0000 # EQEmu 06/29/05 +OP_SetTitle=0x0000 # EQEmu 06/29/05 +OP_SetTitleReply=0x0000 # EQEmu 06/29/05 +OP_Bandolier=0x6f0c +OP_OpenDiscordMerchant=0x0000 #8 bytes +OP_DiscordMerchantInventory=0x0000 #long item packet +OP_GiveMoney=0x0000 #16 bytes, pp, gp, sp, cp. +OP_OnLevelMessage=0x0000 +OP_RequestKnowledgeBase=0x0000 +OP_KnowledgeBase=0x0000 +OP_SlashAdventure=0x571a # /adventure +OP_VetRewardsAvaliable=0x0557 +OP_BecomePVPPrompt=0x36B2 #guessed from ASM +OP_PickLockSuccess=0x40E7 + +#named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x729c +OP_Some6ByteHPUpdate=0x0000 #seems to happen when you target group members +OP_SomeItemPacketMaybe=0x0000 # EQEmu 06/29/05 +OP_QueryResponseThing=0x0000 +OP_FloatListThing=0x6a1b # EQEmu 06/29/05 + +#Login opcodes +OP_SessionReady=0x0001 +OP_Login=0x0002 +OP_ServerListRequest=0x0004 +OP_PlayEverquestRequest=0x000d +OP_PlayEverquestResponse=0x0021 +OP_ChatMessage=0x0016 +OP_LoginAccepted=0x0017 +OP_ServerListResponse=0x0018 +OP_Poll=0x0029 +OP_EnterChat=0x000f +OP_PollResponse=0x0011 + +#raw opcodes +OP_RAWSessionRequest=0x0000 +OP_RAWSessionResponse=0x0000 +OP_RAWCombined=0x0000 +OP_RAWSessionDisconnect=0x0000 +OP_RAWKeepAlive=0x0000 +OP_RAWSessionStatRequest=0x0000 +OP_RAWSessionStatResponse=0x0000 +OP_RAWPacket=0x0000 +OP_RAWFragment=0x0000 +OP_RAWOutOfOrderAck=0x0000 +OP_RAWAck=0x0000 +OP_RAWAppCombined=0x0000 +OP_RAWOutOfSession=0x0000 + +#mail opcodes +OP_Command=0x0000 +OP_MailboxHeader=0x0000 +OP_MailHeader=0x0000 +OP_MailBody=0x0000 +OP_NewMail=0x0000 +OP_SentConfirm=0x0000 + + +OP_MobUpdate=0x0000 #not used anymore, here for backwards compat + +#we need to document the differences between these packets to make identifying them easier +OP_MobHealth=0x0695 +OP_HPUpdate=0x3bcf # ShowEQ 10/27/05 +OP_Some3ByteHPUpdate=0x0000 #initial HP update for mobs +OP_InitialHPUpdate=0x0000 + +# Unmatched: OP_ItemPlayerPacket=5394 +# Unmatched: OP_GetGuildMOTD=6d5d +# Unmatched: OP_GuildTributeStatus=7584 diff --git a/utils/patches/patch_6.2.conf b/utils/patches/patch_6.2.conf new file mode 100644 index 000000000..ad2b17d30 --- /dev/null +++ b/utils/patches/patch_6.2.conf @@ -0,0 +1,539 @@ +#ShowEQ Import Notes: +# ZERO THE FILE first +# sed 's/0x[0-9a-fA-F]{4}/0x0000/g' +#Unknown Mapping: +#OP_Action2 -> OP_Damage +#OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +#Name Differences: +#OP_CancelInvite -> OP_GroupCancelInvite +#OP_GMFind -> OP_FindPersonRequest + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0292 #used for unknown explorer +#world packets +OP_ApproveWorld=0x41b1 # ShowEQ 06/29/05 +OP_LogServer=0x4668 # EQEmu 06/29/05 +OP_MOTD=0x7fea # EQEmu 06/29/05 +OP_SendLoginInfo=0x2792 # EQEmu 06/29/05 +OP_DeleteCharacter=0x0920 # ShowEQ 06/29/05 +OP_SendCharInfo=0x732c # EQEmu 06/29/05 +OP_ExpansionInfo=0x5c78 # EQEmu 06/29/05 +OP_CharacterCreate=0x6160 # EQEmu 06/29/05 +OP_RandomNameGenerator=0x23d4 # ShowEQ 06/29/05 +OP_GuildsList=0x6957 # ShowEQ 06/29/05 +OP_ApproveName=0x46b1 # EQEmu 06/29/05 +OP_EnterWorld=0x7262 # EQEmu 06/29/05 +OP_PostEnterWorld=0x0df0 # EQEmu 06/29/05 +OP_World_Client_CRC1=0x254d # ShowEQ 06/29/05 +OP_World_Client_CRC2=0x16c9 # ShowEQ 06/29/05 +OP_SetChatServer=0x6238 # EQEmu 06/29/05 +OP_SetChatServer2=0x6536 # EQEmu 06/29/05 +OP_ZoneServerInfo=0x407c # EQEmu 06/29/05 +OP_WorldComplete=0x509d # EQEmu 06/29/05 +OP_WorldClientReady=0x5e99 # EQEmu 06/29/05 (Guess - Doodman) +OP_WorldUnknown001=0x7510 # EQEmu 06/29/05 (New to 6/29) +OP_SendSystemStats=0x681b # anonymous system stats reporting +OP_WorldLogout=0x7186 +OP_ZoneUnavail=0x6d95 +OP_WorldLevelTooHigh=0x583b # world->client. Cancels zone in. +OP_CharInacessable=0x28aa # world->client. Cancels zone in. +#OP_InvalidLoginPassword=0x52A4 + +#Zone in opcodes +OP_ZoneEntry=0x2ec9 # ShowEQ 06/29/05 +OP_ZoneInUnknown=0x0000 +OP_AckPacket=0x7752 # ShowEQ 06/29/05 +OP_NewZone=0x7ac5 # ShowEQ 06/29/05 +OP_ReqClientSpawn=0x0e76 # ShowEQ 06/29/05 +OP_ZoneSpawns=0x5ee8 # ShowEQ 06/29/05 +OP_CharInventory=0x15ff # EQEmu 06/29/05 +OP_SetServerFilter=0x7709 # ShowEQ 06/29/05 +OP_LockoutTimerInfo=0x7c12 # EQEmu 06/29/05 +OP_SendZonepoints=0x775d # EQEmu 06/29/05 +OP_SpawnDoor=0x4c24 # ShowEQ 06/29/05 +OP_ReqNewZone=0x47c9 # ShowEQ 06/29/05 +OP_PlayerProfile=0x75df # ShowEQ 06/29/05 +OP_TimeOfDay=0x1580 # ShowEQ 06/29/05 +OP_SendAATable=0x367d # ShowEQ 06/29/05 +OP_ZoneServerReady=0x0000 #dosent exist in this version + + +OP_Logout=0x0701 # EQEmu 06/29/05 +OP_LogoutReply=0x48c2 # ShowEQ 06/29/05 (OP_Logout) +OP_PreLogoutReply=0x711e #0 len packet sent during logout/zoning +OP_LevelUpdate=0x6d44 # ShowEQ 06/29/05 +OP_MobUpdate=0x0000 +OP_Stamina=0x74ab # ShowEQ 06/29/05 + +#Petition Opcodes +OP_PetitionSearch=0x2aec #search term for petition +OP_PetitionSearchResults=0x2aec #(list of?) matches from search +OP_PetitionSearchText=0x0000 #text results of search +OP_Petition=0x251f #0x73cb +OP_PetitionUpdate=0x3813 #guess +OP_PetitionCheckout=0x0000 +OP_PetitionCheckIn=0x0000 +OP_PetitionQue=0x0000 +OP_PetitionUnCheckout=0x0000 +OP_PetitionDelete=0x0000 +OP_DeletePetition=0x0000 +OP_PetitionResolve=0x6ea9 #0x688f # ShowEQ 5/11/05 +OP_PDeletePetition=0x0000 +OP_PetitionBug=0x0000 +OP_PetitionRefresh=0x0000 +OP_PetitionCheckout2=0x0000 +OP_PetitionViewPetition=0x0000 + +#Guild Opcodes +OP_ZoneGuildList=0x68AC +OP_GuildMemberList=0x147d # ShowEQ 06/29/05 +OP_GuildMemberUpdate=0x4cc7 # ShowEQ 06/29/05 +OP_GuildMemberLevelUpdate=0x0000 #dosent exist in this version. +OP_GuildRemove=0x6cce # EQEmu - Doodman (found 8/26/05 +OP_GuildPeace=0x0e37 # EQEmu - Doodman (found 8/26/05 +OP_GuildWar=0x186d # EQEmu - Doodman (found 8/26/05 +OP_GuildLeader=0x1ef1 # EQEmu - Doodman (found 8/26/05) +OP_GuildDemote=0x1f46 # EQEmu - Doodman (found 8/26/05) +OP_GuildMOTD=0x21ed # ShowEQ 06/29/05 +OP_SetGuildMOTD=0x475a # EQEmu - Doodman (found 8/26/05) +OP_GetGuildsList=0x7fec # ShowEQ 06/29/05 +OP_GuildInvite=0x61d0 # EQEmu - Doodman (found 8/26/05) +OP_GuildPublicNote=0x17a2 # EQEmu - Doodman (found 8/26/05) +OP_GuildDelete=0x5a4d # EQEmu - Doodman (found 8/26/05 +OP_GuildInviteAccept=0x0179 # EQEmu - Doodman (found 8/26/05) +OP_GetGuildMOTD=0x3246 +OP_GetGuildMOTDReply=0x5B14 +OP_GuildManageBanker=0x3d1e +OP_GuildBank=0x0000 +#guild opcodes: 549F (clear guild?), 0x0695, 0x0AC0, 0x1864, 0x32cf, 0x4cc7, +# 0x461A, 0x6966, 0x7085 + + +#GM/guide opcodes +OP_GMServers=0x3387 #/servers +OP_GMBecomeNPC=0x7864 #/becomenpc +OP_GMZoneRequest=0x61ff #/zone +OP_GMSearchCorpse=0x7f3e #/searchcorpse +OP_GMHideMe=0x0581 #/hideme +OP_GMGoto=0x69c8 #/goto +OP_GMDelCorpse=0x727a #/delcorpse +OP_GMApproval=0x0000 +OP_GMToggle=0x5ce2 #/toggle +OP_GMZoneRequest2=0x0000 +OP_GMSummon=0x3383 #/summon +OP_GMEmoteZone=0x3905 #/emotezone +OP_GMFind=0x5e6a #/find +OP_GMKick=0x1cee #/kick +OP_GMNameChange=0x0000 + +OP_SafePoint=0x0000 +OP_Bind_Wound=0x4796 +OP_GMTraining=0x613d +OP_GMEndTraining=0x181c +OP_GMTrainSkill=0x3838 +OP_GMEndTrainingResponse=0x0a94 +OP_Animation=0x0b71 # EQEmu 06/29/05 +OP_Taunt=0x5306 +OP_Stun=0x7DCF +OP_MoneyUpdate=0x425c +OP_SendAAStats=0x5966 # EQEmu 06/29/05 +OP_SendExpZonein=0x0587 # ShowEQ 06/29/05 +OP_RespondAA=0x35e8 +OP_UpdateAA=0x0000 +OP_IncreaseStats=0x0000 +OP_ReadBook=0x1496 +OP_CombatAbility=0x5e55 +OP_Dye=0x773f # ShowEQ 06/29/05 +OP_Consume=0x7a83 # ShowEQ 06/29/05 +OP_Begging=0x2c81 # ShowEQ 06/29/05 +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_Consent=0x1081 # ShowEQ 06/29/05 +OP_LFGCommand=0x022f # ShowEQ 06/29/05 +OP_LFGGetMatchesRequest=0x6f82 # ShowEQ 06/29/05 +OP_LFGAppearance=0x024d +OP_LFGResponse=0x1fe1 # ShowEQ 5/11/05 +OP_LFGGetMatchesResponse=0x06c5 # ShowEQ 06/29/05 +OP_LootItem=0x2acf # ShowEQ 06/29/05 +OP_Bug=0x3eba # ShowEQ 06/29/05 +OP_BoardBoat=0x67c9 # ShowEQ 06/29/05 +OP_Save=0x6d9b # ShowEQ 06/29/05 +OP_Camp=0x2844 # ShowEQ 06/29/05 +OP_EndLootRequest=0x7fe4 # ShowEQ 06/29/05 +OP_LoadSpellSet=0x403e # ShowEQ 06/29/05 +OP_AutoAttack=0x6c47 # ShowEQ 06/29/05 +OP_AutoFire=0x6c53 +OP_Consider=0x2717 # ShowEQ 06/29/05 +OP_Emote=0x55bc # ShowEQ 06/29/05 +OP_PetCommands=0x58b2 # ShowEQ 06/29/05 +OP_PetBuffWindow=0x4e31 +OP_SpawnAppearance=0x18b7 # ShowEQ 06/29/05 +OP_DeleteSpawn=0x1a64 # ShowEQ 06/29/05 +OP_FormattedMessage=0x42cd # ShowEQ 06/29/05 +OP_WhoAllResponse=0x6686 # ShowEQ 06/29/05 +OP_AutoAttack2=0x0fa6 # ShowEQ 06/29/05 +OP_SetRunMode=0x0342 # ShowEQ 06/29/05 +OP_SimpleMessage=0x5a48 # ShowEQ 06/29/05 +OP_SaveOnZoneReq=0x2baf # ShowEQ 06/29/05 +OP_MoveDoor=0x0ef7 # ShowEQ 06/29/05 +OP_SenseHeading=0x5666 # ShowEQ 06/29/05 +OP_Buff=0x33dc # ShowEQ 06/29/05 +OP_LootComplete=0x20b2 # ShowEQ 06/29/05 +OP_EnvDamage=0x0990 +OP_Split=0x6a53 # ShowEQ 06/29/05 +OP_Surname=0x48fe # ShowEQ 06/29/05 +OP_ClearSurname=0x0000 +OP_MoveItem=0x7657 # ShowEQ 06/29/05 +OP_FaceChange=0x763b # ShowEQ 06/29/05 +OP_ItemPacket=0x3397 # ShowEQ 06/29/05 +OP_ItemLinkResponse=0x1d43 # ShowEQ 06/29/05 +OP_ClientReady=0x5e20 # ShowEQ 06/29/05 +OP_ZoneChange=0x60ef # ShowEQ 06/29/05 +OP_MemorizeSpell=0x05ac # ShowEQ 06/29/05 +OP_ItemLinkClick=0x53e5 # ShowEQ 06/29/05 +OP_SwapSpell=0x36b2 # ShowEQ 06/29/05 +OP_Forage=0x7c32 # ShowEQ 06/29/05 +OP_ConsentResponse=0x6380 # ShowEQ 06/29/05 +OP_BazaarSearch=0x524e # ShowEQ 06/29/05 +OP_NewSpawn=0x4f11 # ShowEQ 06/29/05 +OP_WearChange=0x601d # ShowEQ 06/29/05 +OP_Action=0x4513 # ShowEQ 06/29/05 +OP_SpecialMesg=0x5ef7 # ShowEQ 06/29/05 +OP_Bazaar=0x0000 +OP_LeaveBoat=0x7187 # ShowEQ 06/29/05 +OP_Weather=0x65ca # ShowEQ 06/29/05 +OP_LFPGetMatchesRequest=0x45d0 # ShowEQ 06/29/05 +OP_Illusion=0x7441 # ShowEQ 06/29/05 +OP_TargetMouse=0x2274 # ShowEQ 06/29/05 +OP_InspectAnswer=0x2dd6 # ShowEQ 06/29/05 +OP_GMKill=0x692c # ShowEQ 06/29/05 +OP_MoneyOnCorpse=0x2fca # ShowEQ 06/29/05 +OP_ClickDoor=0x700d # ShowEQ 06/29/05 +OP_LootRequest=0x2316 # ShowEQ 06/29/05 +OP_YellForHelp=0x2e20 # ShowEQ 06/29/05 +OP_ManaChange=0x0b2d # ShowEQ 06/29/05 +OP_ConsentDeny=0x4e8c # ShowEQ 06/29/05 +OP_LFPCommand=0x35a6 # ShowEQ 06/29/05 +OP_RandomReply=0x6cd5 # ShowEQ 06/29/05 +OP_DenyResponse=0x7c66 # ShowEQ 06/29/05 +OP_ConsiderCorpse=0x673c # ShowEQ 06/29/05 +OP_CorpseDrag=0x50c0 # +OP_CorpseDrop=0x7c7c # +OP_ConfirmDelete=0x28f2 # ShowEQ 06/29/05 +OP_MobHealth=0x3d2d # ShowEQ 06/29/05 +OP_SkillUpdate=0x6a93 # ShowEQ 06/29/05 +OP_RandomReq=0x21e0 # ShowEQ 06/29/05 +OP_CastSpell=0x4839 # ShowEQ 06/29/05 +OP_ClientUpdate=0x14cb # ShowEQ 06/29/05 +OP_MobUpdate=0x0000 #not used anymore, here for backwards compat +OP_Report=0x0375 # ShowEQ 06/29/05 +OP_GroundSpawn=0x736b # ShowEQ 06/29/05 +OP_TargetCommand=0x6563 #0x0dfe # ShowEQ 06/29/05 +OP_TargetHoTT=0x3ef6 +OP_LFPGetMatchesResponse=0x63fe # ShowEQ 06/29/05 +OP_Jump=0x4dbc # ShowEQ 06/29/05 +OP_ExpUpdate=0x5ecd # ShowEQ 06/29/05 +OP_AAAction=0x3af4 # ShowEQ 06/29/05 +OP_Death=0x5188 # ShowEQ 06/29/05 +OP_BecomeCorpse=0x128c +OP_GMLastName=0x0c81 # ShowEQ 06/29/05 +OP_InitialMobHealth=0x2ecc # ShowEQ 06/29/05 +OP_Mend=0x14ef # ShowEQ 06/29/05 +OP_MendHPUpdate=0x1ffa # ShowEQ 5/11/05 +OP_Feedback=0x224d # ShowEQ 06/29/05 +OP_TGB=0x5e51 # ShowEQ 06/29/05 +OP_InterruptCast=0x0db3 +OP_Damage=0x1848 # ShowEQ 06/29/05 (OP_Action2) +OP_ChannelMessage=0x1004 # ShowEQ 06/29/05 (OP_CommonMessage) +OP_LevelAppearance=0x358e +OP_MultiLineMsg=0x0000 +OP_Charm=0x10a1 +OP_DeleteSpell=0x3fe6 +OP_ApproveZone=0x0000 +OP_Assist=0x5D02 +OP_AugmentItem=0x539b +OP_BazaarInspect=0x0000 +OP_AAExpUpdate=0x6dd8 # ShowEQ 06/29/05 +OP_ClientError=0x0000 +OP_DeleteItem=0x1c4a +OP_DeleteCharge=0x0841 +OP_ControlBoat=0x0000 +OP_DumpName=0x0000 +OP_FeignDeath=0x2ad8 +OP_Fishing=0x0b36 +OP_Heartbeat=0x0000 +OP_InstillDoubt=0x389e +OP_ItemName=0x0000 +OP_LDoNButton=0x0000 +OP_LDoNOpen=0x083b +OP_MoveCoin=0x1dd9 +OP_ReloadUI=0x0000 +OP_ZonePlayerToBind=0x385e # FNW Discovered on Feb 9, 2007 +OP_Translocate=0x78c1 +OP_Sacrifice=0x5f62 +OP_ApplyPoison=0x4298 + +#bazaar trader stuff stuff: +#become and buy from +#Server->Client: [ Opcode: OP_Unknown (0x2403) Size: 8 ] +# 0: 46 01 00 00 39 01 00 00 | F...9... +OP_TraderDelItem=0x0da9 +OP_BecomeTrader=0x66f9 +OP_TraderShop=0x19d8 +OP_TraderItemUpdate=0x0000 +OP_Trader=0x0681 +OP_ShopItem=0x0000 +OP_TraderBuy=0x0f8e # ShowEQ 06/29/05 +OP_Barter=0x7460 + +#pc/npc trading +OP_TradeRequest=0x372f # ShowEQ 06/29/05 +OP_TradeAcceptClick=0x0065 # ShowEQ 06/29/05 +OP_TradeRequestAck=0x4048 # ShowEQ 06/29/05 +OP_TradeCoins=0x34c1 +OP_FinishTrade=0x6014 +OP_CancelTrade=0x2dc1 # ShowEQ 06/29/05 +OP_TradeMoneyUpdate=0x3e63 #not sure + +#merchant crap +OP_ShopPlayerSell=0x0e13 # ShowEQ 06/29/05 +OP_ShopEnd=0x7e03 # ShowEQ 06/29/05 +OP_ShopEndConfirm=0x0000 +OP_ShopPlayerBuy=0x221e +OP_ShopRequest=0x3c4b # ShowEQ 06/29/05 +OP_ShopDelItem=0x0000 #0x0da9 maybe, 16 bytes though + +#tradeskill stuff: +OP_ClickObject=0x0f47 # ShowEQ 06/29/05 +OP_ClickObjectAction=0x6937 # EQEMu 06/29/05 +OP_RecipeDetails=0x0353 # EQEMu 06/29/05 +OP_RecipesFavorite=0x23f0 +OP_RecipesSearch=0x31f8 # EQEmu 06/29/05 +OP_RecipeReply=0x4ea2 # EQEmu 06/29/05 +OP_RecipeAutoCombine=0x7822 +OP_TradeSkillCombine=0x0b40 # ShowEQ 06/29/05 + +OP_RequestDuel=0x28e1 +OP_DuelResponse=0x2e78 +OP_DuelResponse2=0x3bad #when accepted + +OP_RezzComplete=0x60fa +OP_RezzRequest=0x417a +OP_RezzAnswer=0x6219 +OP_SafeFallSuccess=0x5309 +OP_Shielding=0x0000 +OP_TargetReject=0x0000 +OP_TestBuff=0x0000 +OP_Track=0x5205 # ShowEQ 06/29/05 +OP_TrackTarget=0x71ae +OP_TrackUnknown=0x74e1 #size 0 right after OP_Track + +#Tribute Packets: +OP_OpenGuildTributeMaster=0x60b6 #open guild tribute master window +OP_OpenTributeMaster=0x512e #open tribute master window +OP_OpenTributeReply=0x0000 #reply to open request +OP_SelectTribute=0x625d #clicking on a tribute, and text reply +OP_TributeItem=0x6f6c #donating an item +OP_TributeMoney=0x27b3 #donating money +OP_TributeNPC=0x0000 #seems to be missing now +OP_TributeToggle=0x2688 #activating/deactivating tribute +OP_TributeTimer=0x4665 #a 4 byte tier update, 10 minutes for seconds +OP_TributePointUpdate=0x6463 #16 byte point packet +OP_TributeUpdate=0x5639 # ShowEQ 06/29/05 +OP_GuildTributeInfo=0x5e3d # EQEmu 06/29/05 +OP_TributeInfo=0x152d # EQEmu 06/29/05 +OP_SendGuildTributes=0x5e3a # request packet, 4 bytes +OP_SendTributes=0x067a # request packet, 4 bytes, migth be backwards +OP_CloseTributeMaster=0x7f25 #sent by client when they close window + +#Adventure packets: +OP_LeaveAdventure=0x0c0d +OP_AdventureFinish=0x3906 +OP_AdventureInfoRequest=0x2aaf #right click adventure recruiter +OP_AdventureInfo=0x1db5 #text reply to right click +OP_AdventureRequest=0x43fd +OP_AdventureDetails=0x3f26 +OP_AdventureData=0x0677 +OP_AdventureUpdate=0x64ac +OP_AdventureMerchantRequest=0x0950 +OP_AdventureMerchantResponse=0x4416 +OP_AdventureMerchantPurchase=0x413d +OP_AdventureMerchantSell=0x0097 +OP_AdventurePointsUpdate=0x420a #not sure, followed purchase +OP_AdventureStatsRequest=0x5fc7 +OP_AdventureStatsReply=0x56cd +OP_AdventureLeaderboardRequest=0x230a +OP_AdventureLeaderboardReply=0x0d0f +# request stats: 0x5fc7, reply 0x56cd +# request leaderboard: 0x230a, reply 0x0d0f + +#Group Opcodes +OP_GroupDisband=0x4033 # ShowEQ 06/29/05 +OP_GroupInvite=0x3853 # ShowEQ 06/29/05 +OP_GroupFollow=0x1b48 # ShowEQ 06/29/05 +OP_GroupUpdate=0x66ba # ShowEQ 06/29/05 +OP_GroupAcknowledge=0x0797 +OP_GroupCancelInvite=0x42c9 # ShowEQ 06/29/05 +OP_GroupDelete=0x0000 +OP_GroupFollow2=0x0000 #used with GroupInvite2 +OP_GroupInvite2=0x1f27 #this is sometimes sent instead of OP_GroupInvite +OP_CancelInvite=0x0000 + +OP_RaidJoin=0x0000 # ShowEQ 06/29/05 +OP_RaidInvite=0x1f21 # ShowEQ 06/29/05 +OP_RaidUpdate=0x1974 # EQEmu 06/29/05 + + +OP_ZoneComplete=0x0000 +OP_ItemLinkText=0x0000 +OP_ClearObject=0x8258 +OP_DisciplineUpdate=0x7180 +OP_DisciplineTimer=0x53df +OP_LocInfo=0x0000 +OP_FindPersonRequest=0x3c41 # ShowEQ 06/29/05 +OP_FindPersonReply=0x5711 +OP_ForceFindPerson=0x0000 +OP_LoginComplete=0x0000 +OP_Sound=0x4a1d +#OP_Zone_MissingName01=0x0000 #remove on recompile +OP_MobRename=0x3230 +OP_BankerChange=0x6a5b + +#Rogue packets +OP_SenseTraps=0x24ab # ShowEQ 06/29/05 +OP_PickPocket=0x4a2a +OP_DisarmTraps=0x0000 +OP_Disarm=0x17d9 +OP_Hide=0x6f64 # ShowEQ 06/29/05 +OP_Sneak=0x4312 # ShowEQ 06/29/05 + +#Task packets +#task complete related: 0x54eb (24 bytes), 0x4c8c (8 bytes), 0x6a1d (4 bytes) +OP_TaskActivityComplete=0x54eb +OP_CompletedTasks=0x53c4 # ShowEQ 06/29/05 +OP_TaskDescription=0x682d # ShowEQ 06/29/05 +OP_TaskActivity=0x3ba8 # ShowEQ 06/29/05 +OP_TaskMemberList=0x09b6 #not sure +OP_OpenNewTasksWindow=0x5e7c #combined with OP_AvaliableTask I think +OP_AvaliableTask=0x0000 +OP_AcceptNewTask=0x207f +OP_TaskHistoryRequest=0x3274 +OP_TaskHistoryReply=0x009c +OP_CancelTask=0x4c8c +OP_DeclineAllTasks=0x207f #not sure, 12 bytes + + +OP_RequestClientZoneChange=0x1235 +OP_PurchaseLeadershipAA=0x07f1 +OP_UpdateLeadershipAA=0x3eec +OP_LeadershipExpUpdate=0x7416 +OP_LeadershipExpToggle=0x17bf +OP_GroupUpdateLeaderAA=0x4c3f +OP_MarkNPC=0x5b37 +OP_ClearNPCMarks=0x1794 +OP_DoGroupLeadershipAbility=0x4ffe +OP_DelegateAbility=0x56eb + +#The following 4 Opcodes are for SoF only: +OP_FinishWindow=0x0000 #Trevius 03/15/09 +OP_FinishWindow2=0x0000 #Trevius 03/15/09 +OP_ItemVerifyRequest=0x0000 #Trevius 03/15/09 +OP_ItemVerifyReply=0x0000 #Trevius 03/15/09 + +#discovered opcodes not yet used: +OP_CrashDump=0x6d5d +OP_PlayMP3=0x0000 +OP_FriendsWho=0x41db +OP_MoveLogRequest=0x78e8 #gone I think +OP_MoveLogDisregard=0x0000 #gone I think +OP_ReclaimCrystals=0x12fd +OP_CrystalCountUpdate=0x0000 +OP_DynamicWall=0x0000 +OP_CustomTitles=0x2a28 # ShowEQ 06/29/05 +OP_RequestTitles=0x5eba # EQEmu 06/29/05 +OP_SendTitleList=0x3e89 # EQEmu 06/29/05 +OP_SetTitle=0x1f22 # EQEmu 06/29/05 +OP_SetTitleReply=0x5eab # EQEmu 06/29/05 +OP_Bandolier=0x6f0c +OP_PotionBelt=0x0719 +OP_OpenDiscordMerchant=0x0000 #8 bytes +OP_DiscordMerchantInventory=0x0000 #long item packet +OP_GiveMoney=0x56d4 #16 bytes, pp, gp, sp, cp. +OP_OnLevelMessage=0x1dde +OP_PopupResponse=0x3816 +OP_RequestKnowledgeBase=0x7584 +OP_KnowledgeBase=0x4a52 +OP_PlayerUnderWorld=0x46d9 +OP_PVPStats=0x5cc0 +OP_PVPLeaderBoardRequest=0x61d2 +OP_PVPLeaderBoardReply=0x1a59 +OP_PVPLeaderBoardDetailsRequest=0x06a2 +OP_PVPLeaderBoardDetailsReply=0x246a +OP_WeaponEquip1=0x6c5e +OP_WeaponEquip2=0x63da +OP_WeaponUnequip2=0x381d +OP_VoiceMacroIn=0x2866 +OP_VoiceMacroOut=0x2ec6 + +#named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # EQEmu 06/29/05 +OP_Some6ByteHPUpdate=0x0000 #seems to happen when you target group members +OP_SomeItemPacketMaybe=0x497c # EQEmu 06/29/05 +OP_QueryResponseThing=0x6379 +OP_FloatListThing=0x7510 # EQEmu 06/29/05 + +#Login opcodes +OP_SessionReady=0x0001 +OP_Login=0x0002 +OP_ServerListRequest=0x0004 +OP_PlayEverquestRequest=0x000d +OP_PlayEverquestResponse=0x0021 +OP_ChatMessage=0x0016 +OP_LoginAccepted=0x0017 +OP_ServerListResponse=0x0018 +OP_Poll=0x0029 +OP_EnterChat=0x000f +OP_PollResponse=0x0011 + +#raw opcodes +OP_RAWSessionRequest=0x0000 +OP_RAWSessionResponse=0x0000 +OP_RAWCombined=0x0000 +OP_RAWSessionDisconnect=0x0000 +OP_RAWKeepAlive=0x0000 +OP_RAWSessionStatRequest=0x0000 +OP_RAWSessionStatResponse=0x0000 +OP_RAWPacket=0x0000 +OP_RAWFragment=0x0000 +OP_RAWOutOfOrderAck=0x0000 +OP_RAWAck=0x0000 +OP_RAWAppCombined=0x0000 +OP_RAWOutOfSession=0x0000 + +#mail opcodes +OP_Command=0x0000 +OP_MailboxHeader=0x0000 +OP_MailHeader=0x0000 +OP_MailBody=0x0000 +OP_NewMail=0x0000 +OP_SentConfirm=0x0000 + + +#we need to document the differences between these packets to make identifying them easier +OP_MobHealth=0x3d2d # ShowEQ 06/29/05 +OP_HPUpdate=0x217b # ShowEQ 06/29/05 +OP_Some3ByteHPUpdate=0x0000 #initial HP update for mobs +OP_InitialHPUpdate=0x7ac2 # ShowEQ 06/29/05 + +#remove these +#junk +OP_0x0193=0x0000 +OP_0x0347=0x0000 +OP_ConsumeAmmo=0x0000 +OP_EmoteAnim=0x0000 +OP_Deny=0x0000 +OP_SetDataRate=0x0000 + diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf new file mode 100644 index 000000000..e569277ed --- /dev/null +++ b/utils/patches/patch_RoF.conf @@ -0,0 +1,653 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 used for unknown explorer + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x11b6 +OP_ApproveWorld=0x7e47 +OP_LogServer=0x19ab +OP_SendCharInfo=0x06a2 +OP_ExpansionInfo=0x711a +OP_GuildsList=0x2d38 +OP_EnterWorld=0x57c3 +OP_PostEnterWorld=0x0c3d +OP_World_Client_CRC1=0x0044 +OP_World_Client_CRC2=0x26df +OP_SendSpellChecksum=0x0000 +OP_SendSkillCapsChecksum=0x0000 + +# Character Select Related: +OP_SendMaxCharacters=0x5349 +OP_SendMembership=0x03b2 +OP_SendMembershipDetails=0x0b1f +OP_CharacterCreateRequest=0x006b +OP_CharacterCreate=0x0afc +OP_DeleteCharacter=0x0ab4 +OP_RandomNameGenerator=0x4453 +OP_ApproveName=0x213a +OP_MOTD=0x1ae3 +OP_SetChatServer=0x2a81 +OP_SetChatServer2=0x4099 +OP_ZoneServerInfo=0x49d9 +OP_WorldComplete=0x12d4 +OP_WorldUnknown001=0x6be3 +OP_FloatListThing=0x7a19 + +# Reasons for Disconnect: +OP_ZoneUnavail=0x6281 +OP_WorldClientReady=0x43b0 +OP_CharacterStillInZone=0x0000 +OP_WorldChecksumFailure=0x0000 +OP_WorldLoginFailed=0x0000 +OP_WorldLogout=0x0000 +OP_WorldLevelTooHigh=0x0000 +OP_CharInacessable=0x0000 +OP_UserCompInfo=0x0000 +OP_SendExeChecksum=0x0000 +OP_SendBaseDataChecksum=0x0000 + +# Zone in opcodes +OP_AckPacket=0x47a6 +OP_ZoneEntry=0x1665 +OP_ReqNewZone=0x3dec +OP_NewZone=0x7fff +OP_ZoneSpawns=0x0980 +OP_PlayerProfile=0x46bb +OP_TimeOfDay=0x3be2 +OP_LevelUpdate=0x7ce0 +OP_Stamina=0x1949 +OP_RequestClientZoneChange=0x647a +OP_ZoneChange=0x1eb4 +OP_LockoutTimerInfo=0x0000 +OP_ZoneServerReady=0x0000 +OP_ZoneInUnknown=0x0000 +OP_LogoutReply=0x0000 +OP_PreLogoutReply=0x0000 + +# Required to fully log in +OP_SpawnAppearance=0x7360 +OP_ChangeSize=0x355c +OP_TributeUpdate=0x2691 +OP_TributeTimer=0x3294 +OP_SendTributes=0x0a1c +OP_SendGuildTributes=0x1781 +OP_TributeInfo=0x5dd2 +OP_Weather=0x0bd7 +OP_ReqClientSpawn=0x5a4f +OP_SpawnDoor=0x7b6c +OP_GroundSpawn=0x4286 +OP_SendZonepoints=0x7922 +OP_BlockedBuffs=0x5a71 +OP_RemoveBlockedBuffs=0x78ba +OP_ClearBlockedBuffs=0x5d3c +OP_WorldObjectsSent=0x7fa8 +OP_SendExpZonein=0x25ab +OP_SendAATable=0x7791 +OP_RespondAA=0x379d +OP_UpdateAA=0x504f +OP_SendAAStats=0x3d1c +OP_AAExpUpdate=0x25c5 +OP_ExpUpdate=0x47e3 +OP_HPUpdate=0x07b8 +OP_ManaChange=0x1e3b +OP_TGB=0x10a6 +OP_SpecialMesg=0x362c +OP_GuildMemberList=0x4053 +OP_GuildMOTD=0x0561 +OP_CharInventory=0x786e +OP_WearChange=0x1ad3 +OP_ClientUpdate=0x455d +OP_ClientReady=0x3f83 # 0x422d +OP_SetServerFilter=0x5b27 + +# Guild Opcodes - Disabled until crashes are resolved in RoF +OP_GetGuildMOTD=0x7ba3 # Was 0x35dc +OP_GetGuildMOTDReply=0x42a2 # Was 0x4586 +OP_GuildMemberUpdate=0x0048 # Was 0x5643 +OP_GuildInvite=0x5f8e +OP_GuildRemove=0x296d +OP_GuildPeace=0x17eb +OP_SetGuildMOTD=0x44d0 +OP_GuildList=0x2d38 +OP_GuildWar=0x395a +OP_GuildLeader=0x7c6f +OP_GuildDelete=0x241b +OP_GuildInviteAccept=0x78a5 +OP_GuildDemote=0x3100 +OP_GuildPublicNote=0x3c2c +OP_GuildManageBanker=0x096d # Was 0x0737 +OP_GuildBank=0x2ab0 # Was 0x10c3 +OP_SetGuildRank=0x3599 +OP_GuildUpdateURLAndChannel=0x7851 +OP_GuildStatus=0x25a5 +OP_GuildCreate=0xd2d1 # or maybe 0x086e +OP_GuildMemberLevelUpdate=0x0000 # Unused? +OP_ZoneGuildList=0x0000 # Unused? +OP_GetGuildsList=0x0000 # Unused? +OP_LFGuild=0x0000 +OP_GuildManageRemove=0x0000 +OP_GuildManageAdd=0x0000 +OP_GuildManageStatus=0x0000 + +# GM/Guide Opcodes +OP_GMServers=0x2612 +OP_GMBecomeNPC=0x5201 +OP_GMZoneRequest=0x69e5 +OP_GMZoneRequest2=0x65d6 +OP_GMGoto=0x0345 +OP_GMSearchCorpse=0x5bed +OP_GMHideMe=0x4c65 +OP_GMDelCorpse=0x6034 +OP_GMApproval=0x0e37 +OP_GMToggle=0x1276 +OP_GMSummon=0x0655 # Was 0x684f +OP_GMEmoteZone=0x1b39 # Was 0x0655 +OP_GMEmoteWorld=0x1f8c # Was 0x1935 +OP_GMFind=0x6160 +OP_GMKick=0x5646 +OP_GMKill=0x507e +OP_GMNameChange=0x3077 # Was 0x4434 +OP_GMLastName=0x4dd7 # Was 0x3077 + +# Misc Opcodes +OP_InspectRequest=0x23f1 +OP_InspectAnswer=0x5794 +OP_InspectMessageUpdate=0x3064 +OP_BeginCast=0x17ff +OP_BuffFadeMsg=0x41cb +OP_ConsentResponse=0x183d +OP_MemorizeSpell=0x2fac +OP_SwapSpell=0x4736 +OP_CastSpell=0x1cb5 +OP_Consider=0x4d8d +OP_FormattedMessage=0x6afe +OP_SimpleMessage=0x02a5 +OP_Buff=0x08ed +OP_Illusion=0x6c43 +OP_MoneyOnCorpse=0x1837 +OP_RandomReply=0x6525 +OP_DenyResponse=0x344a +OP_SkillUpdate=0x52c6 +OP_GMTrainSkillConfirm=0x35cc # 0x3960 +OP_RandomReq=0x59db +OP_Death=0x3a65 +OP_GMTraining=0x6e8f +OP_GMEndTraining=0x7ffe +OP_GMTrainSkill=0x444c +OP_Animation=0x5e0c +OP_Begging=0x54ac +OP_Consent=0x400e +OP_ConsentDeny=0x34c1 +OP_AutoFire=0x314e +OP_PetCommands=0x0093 +OP_DeleteSpell=0x305c +OP_Surname=0x1a87 +OP_ClearSurname=0x17b6 +OP_FaceChange=0x5b06 +OP_SenseHeading=0x46c8 +OP_Action=0x0ea7 +OP_ConsiderCorpse=0x06c5 +OP_HideCorpse=0x3c4b +OP_CorpseDrag=0x0be7 +OP_CorpseDrop=0xdf77 +OP_Bug=0x0c07 +OP_Feedback=0x45ca +OP_Report=0x6d94 +OP_Damage=0x5428 +OP_ChannelMessage=0x33bc +OP_Assist=0x4cd1 +OP_AssistGroup=0x63df +OP_MoveCoin=0x765a +OP_ZonePlayerToBind=0x7f99 +OP_KeyRing=0x04ba +OP_WhoAllRequest=0x78eb +OP_WhoAllResponse=0x3393 +OP_FriendsWho=0x3dac +OP_ConfirmDelete=0x78e0 +OP_Logout=0x64bd +OP_Rewind=0x5521 +OP_TargetCommand=0x000a +OP_Hide=0x2da1 +OP_Jump=0x1d47 +OP_Camp=0x41cd +OP_Emote=0x37fd +OP_SetRunMode=0x2ce6 +OP_BankerChange=0x4e20 +OP_TargetMouse=0x0e25 +OP_MobHealth=0x218d +OP_InitialMobHealth=0x0000 # Unused? +OP_TargetHoTT=0x3af5 +OP_XTargetResponse=0x7f64 +OP_XTargetRequest=0x6753 +OP_XTargetAutoAddHaters=0x5f51 +OP_TargetBuffs=0x1c71 +OP_BuffCreate=0x71f5 +OP_BuffRemoveRequest=0x7efd +OP_DeleteSpawn=0x3b06 +OP_AutoAttack=0x0d14 +OP_AutoAttack2=0x3912 +OP_Consume=0x4692 +OP_MoveItem=0x62a2 +OP_DeleteItem=0x3eb5 +OP_DeleteCharge=0x2d5b +OP_ItemPacket=0x5e0e +OP_ItemLinkResponse=0x2fdb +OP_ItemLinkClick=0x0353 +OP_ItemPreview=0x05cf +OP_NewSpawn=0x3f5b +OP_Track=0x7d43 +OP_TrackTarget=0x4c42 +OP_TrackUnknown=0x2395 +OP_ClickDoor=0x7cc0 +OP_MoveDoor=0x5611 +OP_RemoveAllDoors=0x39c9 +OP_EnvDamage=0x52e9 +OP_BoardBoat=0x2731 +OP_Forage=0x416d +OP_LeaveBoat=0x6cc1 +OP_ControlBoat=0x4aa0 +OP_SafeFallSuccess=0x6d27 +OP_RezzComplete=0x3297 +OP_RezzRequest=0x521b +OP_RezzAnswer=0x004e +OP_Shielding=0x17d9 +OP_RequestDuel=0x1ea9 +OP_MobRename=0x5040 +OP_AugmentItem=0x1627 # Was 0x37cb +OP_WeaponEquip1=0x35c3 +OP_WeaponEquip2=0x012f # Was 0x6022 +OP_WeaponUnequip2=0x1076 # Was 0x0110 +OP_ApplyPoison=0x1499 +OP_Save=0x2e6f +OP_TestBuff=0x046e # Was 0x3772 +OP_CustomTitles=0x471a +OP_Split=0x269e +OP_YellForHelp=0x0017 +OP_LoadSpellSet=0x38b4 +OP_Bandolier=0x2b6f +OP_PotionBelt=0x2d1b # Was 0x4d3b +OP_DuelResponse=0x0dee +OP_DuelResponse2=0x5e04 +OP_SaveOnZoneReq=0x36b1 +OP_ReadBook=0x383c +OP_Dye=0x62d8 +OP_InterruptCast=0x7470 +OP_AAAction=0x719a +OP_LeadershipExpToggle=0x3ea6 +OP_LeadershipExpUpdate=0x6922 +OP_PurchaseLeadershipAA=0x1962 +OP_UpdateLeadershipAA=0x56aa +OP_MarkNPC=0x2d9f +OP_ClearNPCMarks=0x0d2d +OP_DelegateAbility=0x7820 +OP_SetGroupTarget=0x118a +OP_Charm=0x7118 +OP_Stun=0x53c0 +OP_SendFindableNPCs=0x34c3 +OP_FindPersonRequest=0x2f3b +OP_FindPersonReply=0x44f7 +OP_Sound=0x3cec +OP_PetBuffWindow=0x7197 +OP_LevelAppearance=0x7c4d +OP_Translocate=0x6f01 +OP_Sacrifice=0x76ab +OP_PopupResponse=0x2fa1 +OP_OnLevelMessage=0x09cd +OP_AugmentInfo=0x7812 +OP_Petition=0x2885 +OP_SomeItemPacketMaybe=0x11a0 +OP_PVPStats=0x3034 # Unsure +OP_PVPLeaderBoardRequest=0x1bce +OP_PVPLeaderBoardReply=0x3e78 +OP_PVPLeaderBoardDetailsRequest=0x46f4 +OP_PVPLeaderBoardDetailsReply=0x6a60 +OP_RestState=0x3ad5 +OP_RespawnWindow=0x7767 +OP_LDoNButton=0x596e +OP_SetStartCity=0x7936 # Was 0x2d1b +OP_VoiceMacroIn=0x202e +OP_VoiceMacroOut=0x3920 +OP_ItemViewUnknown=0x0b64 +OP_VetRewardsAvaliable=0x05d9 +OP_VetClaimRequest=0xcdde +OP_VetClaimReply=0x361b +OP_DisciplineUpdate=0x2483 # Was 0x2f05 +OP_DisciplineTimer=0x4933 # Was 0x5e3f +OP_BecomeCorpse=0x0000 # Unused? +OP_Action2=0x0000 # Unused? +OP_MobUpdate=0x6b5a +OP_NPCMoveUpdate=0x5bd9 +OP_CameraEffect=0x5712 +OP_SpellEffect=0x72b6 +OP_RemoveNimbusEffect=0x3ba7 +OP_AltCurrency=0x8fcb +OP_AltCurrencyMerchantRequest=0x7e3e +OP_AltCurrencyMerchantReply=0x0b60 +OP_AltCurrencyPurchase=0x74a8 +OP_AltCurrencySell=0x3167 +OP_AltCurrencySellSelection=0x3c47 +OP_AltCurrencyReclaim=0x46ed +OP_CrystalCountUpdate=0x5a82 # Was 0x3f60 +OP_CrystalCreate=0x7616 # Was 0x5a82 +OP_CrystalReclaim=0x73db # Was 0x7616 +OP_Untargetable=0x7717 +OP_IncreaseStats=0x0711 +OP_Weblink=0x7cce +#OP_OpenInventory=0x0000 # Likely does not exist in RoF -U +OP_OpenContainer=0x654f + +OP_DzQuit=0x5fc8 +OP_DzListTimers=0x67b9 +OP_DzAddPlayer=0x5ca2 +OP_DzRemovePlayer=0x0dc1 +OP_DzSwapPlayer=0x4995 +OP_DzMakeLeader=0x17b2 +OP_DzPlayerList=0x1aff +OP_DzJoinExpeditionConfirm=0x30df +OP_DzJoinExpeditionReply=0x15d4 +OP_DzExpeditionInfo=0x3861 +OP_DzExpeditionList=0x0b3b +OP_DzMemberStatus=0x26c2 +OP_DzLeaderStatus=0x4021 +OP_DzExpeditionEndsWarning=0x32eb +OP_DzMemberList=0x348f +OP_DzCompass=0x0e01 # Was 0x4f09 +OP_DzChooseZone=0x0000 # Maybe 0x29d6 + +# New Opcodes +OP_SpawnPositionUpdate=0x0000 # Actually OP_MobUpdate ? +OP_ManaUpdate=0x6675 +OP_EnduranceUpdate=0x71fb +OP_MobManaUpdate=0x48b5 +OP_MobEnduranceUpdate=0x7cb5 + +# Mercenary Opcodes +OP_MercenaryDataUpdateRequest=0x6774 +OP_MercenaryDataUpdate=0x5a7c +OP_MercenaryDataRequest=0x612e +OP_MercenaryDataResponse=0x0768 +OP_MercenaryHire=0x18ba +OP_MercenaryDismiss=0x7ff4 +OP_MercenaryTimerRequest=0x4326 +OP_MercenaryTimer=0x6f9e +OP_MercenaryUnknown1=0x7db4 +OP_MercenaryCommand=0x7b21 +OP_MercenarySuspendRequest=0x61b3 +OP_MercenarySuspendResponse=0x5975 +OP_MercenaryUnsuspendResponse=0x68b1 + +# Looting +OP_LootRequest=0x43a7 +OP_EndLootRequest=0x3d8c +OP_LootItem=0x515b +OP_LootComplete=0x256d + +# bazaar trader stuff: +OP_BazaarSearch=0x2ad2 +OP_TraderDelItem=0x63c8 +OP_BecomeTrader=0x74bc +OP_TraderShop=0x0a5e +OP_Trader=0x6a6a # Was 0x6790 +OP_TraderBuy=0x0000 +OP_Barter=0x6854 +OP_ShopItem=0x0000 +OP_BazaarInspect=0x0000 +OP_Bazaar=0x0000 +OP_TraderItemUpdate=0x0000 + +# pc/npc trading +OP_TradeRequest=0x5f9e +OP_TradeAcceptClick=0x13db +OP_TradeRequestAck=0x6852 +OP_TradeCoins=0x584d +OP_FinishTrade=0x3fbf +OP_CancelTrade=0x0234 +OP_TradeMoneyUpdate=0x65f6 +OP_MoneyUpdate=0x6e42 +OP_TradeBusy=0x7c6d + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x1939 +OP_FinishWindow2=0x68df + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x2c0c +OP_ItemVerifyReply=0x40de + +# merchant stuff +OP_ShopPlayerSell=0x6b4d +OP_ShopRequest=0x16ba +OP_ShopEnd=0x0d07 +OP_ShopEndConfirm=0x1860 +OP_ShopPlayerBuy=0x3bab +OP_ShopDelItem=0x509c + +# tradeskill stuff: +OP_ClickObject=0x7992 +OP_ClickObjectAction=0x5507 +OP_ClearObject=0x2ead +OP_RecipeDetails=0x7ad0 +OP_RecipesFavorite=0x6960 +OP_RecipesSearch=0x30f7 +OP_RecipeReply=0x544e +OP_RecipeAutoCombine=0x46a4 +OP_TradeSkillCombine=0x7447 + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x107b +OP_OpenTributeMaster=0x6fed # Was 0x40f5 +OP_SelectTribute=0x5513 +OP_TributeItem=0x105b +OP_TributeMoney=0x4b6d # Was 0x6fed +OP_TributeToggle=0x1f50 +OP_TributePointUpdate=0x01ec +OP_TributeNPC=0x0000 +OP_GuildTributeInfo=0x0000 +OP_OpenTributeReply=0x0000 +OP_GuildTributeStatus=0x0000 + +# Adventure packets: +OP_LeaveAdventure=0x18b3 +OP_AdventureFinish=0x3d8b +OP_AdventureInfoRequest=0x38db +OP_AdventureInfo=0x1036 +OP_AdventureRequest=0x325c +OP_AdventureDetails=0x4975 +OP_AdventureData=0x729b +OP_AdventureUpdate=0x2df4 +OP_AdventureMerchantRequest=0x2aa0 # Was 654d +OP_AdventureMerchantResponse=0x5aed # Was 7949 +OP_AdventureMerchantPurchase=0x725e # Was 155a +OP_AdventureMerchantSell=0x7882 # Was 389c +OP_AdventurePointsUpdate=0x37cb # Was 7589 +OP_AdventureStatsRequest=0x68c2 +OP_AdventureStatsReply=0x62fa +OP_AdventureLeaderboardRequest=0x577a +OP_AdventureLeaderboardReply=0x2533 + +# Group Opcodes +OP_GroupDisband=0x1ed0 +OP_GroupInvite=0x1602 +OP_GroupFollow=0x2789 +OP_GroupUpdate=0x0ba4 +OP_GroupUpdateB=0x5a07 +OP_GroupCancelInvite=0x2b26 +OP_GroupAcknowledge=0x5fae +OP_GroupDelete=0x33ca +OP_CancelInvite=0x2b26 +OP_GroupFollow2=0x4964 +OP_GroupInvite2=0x6e80 +OP_GroupDisbandYou=0x623d +OP_GroupDisbandOther=0x74fa +OP_GroupLeaderChange=0x46fc +OP_GroupRoles=0x047c +OP_GroupMakeLeader=0x4129 +OP_DoGroupLeadershipAbility=0x17d7 +OP_GroupLeadershipAAUpdate=0x6567 + +# LFG/LFP Opcodes +OP_LFGCommand=0x4463 +OP_LFGGetMatchesRequest=0x1373 +OP_LFGGetMatchesResponse=0x31d8 +OP_LFPGetMatchesRequest=0x7fd9 +OP_LFPGetMatchesResponse=0x4111 +OP_LFPCommand=0x6e8b +OP_LFGAppearance=0x0000 +OP_LFGResponse=0x0000 + +# Raid Opcodes +OP_RaidInvite=0x1bd1 +OP_RaidUpdate=0x548e +OP_RaidJoin=0x0000 + +# Button-push commands +OP_Taunt=0x0af6 +OP_CombatAbility=0x6e4c +OP_SenseTraps=0x7462 # Was 0x2ee0 +OP_PickPocket=0x047b +OP_DisarmTraps=0x0000 +OP_Disarm=0x70a9 +OP_Sneak=0x62f4 +OP_Fishing=0x53c2 +OP_InstillDoubt=0x63a5 +OP_FeignDeath=0x677d +OP_Mend=0x324f +OP_Bind_Wound=0x585d +OP_LDoNOpen=0x3567 + +# Task packets +OP_TaskDescription=0x2294 +OP_TaskActivity=0x7181 +OP_CompletedTasks=0x9495 +OP_TaskActivityComplete=0x71cd +OP_AcceptNewTask=0x394d +OP_CancelTask=0x0c7f +OP_TaskMemberList=0x748e # Was 0x1656 +OP_OpenNewTasksWindow=0x436c # Was 0x11de +OP_AvaliableTask=0x2bf8 # Was 0x2377 +OP_TaskHistoryRequest=0x6cf6 +OP_TaskHistoryReply=0x25eb +OP_DeclineAllTasks=0x0000 + +# Title opcodes +OP_NewTitlesAvailable=0x45d1 +OP_RequestTitles=0x1b5a +OP_SendTitleList=0x0d64 +OP_SetTitle=0x0bfc +OP_SetTitleReply=0x7e20 + +# mail opcodes +OP_Command=0x0000 +OP_MailboxHeader=0x0000 +OP_MailHeader=0x0000 +OP_MailBody=0x0000 +OP_NewMail=0x0000 +OP_SentConfirm=0x0000 + +########### Below this point should not be needed ########### + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 +OP_LocInfo=0x0000 +OP_ReloadUI=0x0000 +OP_ItemName=0x0000 +OP_ItemLinkText=0x0000 +OP_MultiLineMsg=0x0000 +OP_MendHPUpdate=0x0000 +OP_TargetReject=0x0000 +OP_SafePoint=0x0000 +OP_ApproveZone=0x0000 +OP_ZoneComplete=0x0000 +OP_ClientError=0x0000 +OP_DumpName=0x0000 +OP_Heartbeat=0x0000 +OP_CrashDump=0x0000 +OP_LoginComplete=0x0000 + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 +OP_PlayMP3=0x0000 +OP_ReclaimCrystals=0x0000 +OP_DynamicWall=0x0000 +OP_OpenDiscordMerchant=0x0000 +OP_DiscordMerchantInventory=0x0000 +OP_GiveMoney=0x0000 +OP_RequestKnowledgeBase=0x0000 +OP_KnowledgeBase=0x0000 +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 +OP_Some6ByteHPUpdate=0x0000 seems to happen when you target group members +OP_QueryResponseThing=0x0000 + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x46d3 # OP_SendSpellChecksum +#OP_LoginUnknown2=0x040b # OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 search term for petition +OP_PetitionSearchResults=0x0000 (list of?) matches from search +OP_PetitionSearchText=0x0000 text results of search + +OP_PetitionUpdate=0x0000 +OP_PetitionCheckout=0x0000 +OP_PetitionCheckIn=0x0000 +OP_PetitionQue=0x0000 +OP_PetitionUnCheckout=0x0000 +OP_PetitionDelete=0x0000 +OP_DeletePetition=0x0000 +OP_PetitionResolve=0x0000 +OP_PDeletePetition=0x0000 +OP_PetitionBug=0x0000 +OP_PetitionRefresh=0x0000 +OP_PetitionCheckout2=0x0000 +OP_PetitionViewPetition=0x0000 + +# Login opcodes +OP_SessionReady=0x0000 +OP_Login=0x0000 +OP_ServerListRequest=0x0000 +OP_PlayEverquestRequest=0x0000 +OP_PlayEverquestResponse=0x0000 +OP_ChatMessage=0x0000 +OP_LoginAccepted=0x0000 +OP_ServerListResponse=0x0000 +OP_Poll=0x0000 +OP_EnterChat=0x0000 +OP_PollResponse=0x0000 + +# raw opcodes +OP_RAWSessionRequest=0x0000 +OP_RAWSessionResponse=0x0000 +OP_RAWCombined=0x0000 +OP_RAWSessionDisconnect=0x0000 +OP_RAWKeepAlive=0x0000 +OP_RAWSessionStatRequest=0x0000 +OP_RAWSessionStatResponse=0x0000 +OP_RAWPacket=0x0000 +OP_RAWFragment=0x0000 +OP_RAWOutOfOrderAck=0x0000 +OP_RAWAck=0x0000 +OP_RAWAppCombined=0x0000 +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 diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf new file mode 100644 index 000000000..833c30610 --- /dev/null +++ b/utils/patches/patch_SoD.conf @@ -0,0 +1,661 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x0ff4 # C +OP_ApproveWorld=0x701f # C +OP_LogServer=0x4762 # C +OP_SendCharInfo=0x0f14 # C +OP_ExpansionInfo=0x7519 # C +OP_GuildsList=0x5b0b # C +OP_EnterWorld=0x1c20 # C +OP_PostEnterWorld=0x7c94 # C +OP_World_Client_CRC1=0x0ca5 # C +OP_World_Client_CRC2=0x1cb3 # C +OP_SendSpellChecksum=0x5bad # C +OP_SendSkillCapsChecksum=0x5d24 # C + +# Character Select Related: +OP_DeleteCharacter=0x0254 # C +OP_CharacterCreateRequest=0x4463 # C +OP_CharacterCreate=0x1513 # C +OP_RandomNameGenerator=0x3604 # C +OP_ApproveName=0x413f # C + +OP_MOTD=0x192e # C +OP_SetChatServer=0x7f2b # C +OP_SetChatServer2=0x11f3 # C +OP_ZoneServerInfo=0x5da4 # C +OP_WorldComplete=0x37db # C +OP_WorldUnknown001=0x2c4c # C +OP_FloatListThing=0x10b5 # C + +# Reasons for Disconnect: +OP_ZoneUnavail=0x7930 # C +OP_WorldClientReady=0x1a84 # C +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # C +OP_ZoneEntry=0x5a6b # C +OP_ReqNewZone=0x43ac # C +OP_NewZone=0x5ca5 # C +OP_ZoneSpawns=0x72f8 # C +OP_PlayerProfile=0x6022 # C +OP_TimeOfDay=0x6015 # C +OP_LevelUpdate=0x6a99 # C +OP_Stamina=0x3e50 # C +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # + +OP_RequestClientZoneChange=0x4885 # C +OP_ZoneChange=0x051b # C + +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x10b7 # C +OP_TributeUpdate=0x399b # C +OP_TributeTimer=0x4423 # C +OP_TaskDescription=0x7b77 # C +OP_TaskActivity=0x0525 # C +OP_CompletedTasks=0x5412 # C +OP_Weather=0x2641 # C +OP_SendAATable=0x322f # C +OP_UpdateAA=0x466c # C +OP_RespondAA=0x0142 # C +OP_ReqClientSpawn=0x1436 # C +OP_SpawnDoor=0x102f # C +OP_GroundSpawn=0x33e5 # C +OP_SendZonepoints=0x5821 # C +OP_SendAAStats=0x57a3 # C +OP_WorldObjectsSent=0x7b73 # C +OP_BlockedBuffs=0x0baa # C +OP_RemoveBlockedBuffs=0x064d # C +OP_ClearBlockedBuffs=0x4027 # C +OP_SendExpZonein=0x69cd # C +OP_SendTributes=0x4786 # C +OP_TributeInfo=0x4cc6 # C +OP_SendGuildTributes=0x26c4 # C +OP_AAExpUpdate=0x3088 # C +OP_ExpUpdate=0x0e98 # C +OP_HPUpdate=0x538f # C +OP_ManaChange=0x50c2 # C +OP_TGB=0x2d74 # C +OP_SpecialMesg=0x074f # C +OP_GuildMemberList=0x51bc # C +OP_GuildMOTD=0xd677 # C +OP_CharInventory=0x709d # C +OP_WearChange=0x231f # C +OP_ClientUpdate=0x7062 # C +OP_ClientReady=0x6759 # C +OP_SetServerFilter=0x7312 # C + +# Guild Opcodes +OP_GetGuildMOTD=0x189e # C +OP_GetGuildMOTDReply=0x7a16 # C +OP_GuildMemberUpdate=0x41c0 # C +OP_GuildInvite=0x4843 # C +OP_GuildRemove=0x2b9d # C +OP_GuildPeace=0x3f21 # C +OP_SetGuildMOTD=0x481f # C +OP_GuildList=0x5b0b # C +OP_GuildWar=0x7bfb # C +OP_GuildLeader=0x1055 # C +OP_GuildDelete=0x75d0 # C +OP_GuildInviteAccept=0x5f25 # C +OP_GuildDemote=0x15b2 # C +OP_GuildPublicNote=0x2dbd # C +OP_GuildManageBanker=0x11bc # C +OP_GuildBank=0x0fce # C +OP_SetGuildRank=0x4ffe # C +OP_GuildUpdateURLAndChannel=0x5422 +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +OP_GuildStatus=0x5479 # +OP_LFGuild=0x40e8 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x6989 # C +OP_GMBecomeNPC=0x56e7 # C +OP_GMZoneRequest=0x4578 # C +OP_GMZoneRequest2=0x1912 # C +OP_GMGoto=0x0402 # C +OP_GMSearchCorpse=0x7872 # C +OP_GMHideMe=0x34a1 # C +OP_GMDelCorpse=0x0dda # C +OP_GMApproval=0x72fa # C +OP_GMToggle=0x7566 # C +OP_GMSummon=0x596d # C +OP_GMEmoteZone=0x3e7c # C +OP_GMEmoteWorld=0x3e7c # C +OP_GMFind=0x6e27 # C +OP_GMKick=0x799c # C +OP_GMKill=0x6685 # C +OP_GMNameChange=0x565d # C +OP_GMLastName=0x3563 # C + +OP_InspectAnswer=0x4938 # C +OP_Action2=0x7e4d # C OP_Damage? +OP_BeginCast=0x0d5a # C +OP_BuffFadeMsg=0x569a # C +OP_ConsentResponse=0x6e47 # C +OP_MemorizeSpell=0x8543 # C +OP_SwapSpell=0x3fd2 # C +OP_CastSpell=0x3582 # C +OP_Consider=0x6024 # C +OP_FormattedMessage=0x1318 # C +OP_SimpleMessage=0x5448 # C +OP_Buff=0x7ea8 # C +OP_Illusion=0x48f9 # C +OP_MoneyOnCorpse=0x6546 # C +OP_RandomReply=0x6cdc # C +OP_DenyResponse=0x7ce7 # C +OP_SkillUpdate=0x7f01 # C +OP_GMTrainSkillConfirm=0x7ae1 # C +OP_RandomReq=0x777c # C +OP_Death=0x1b85 # C +OP_Bind_Wound=0x3969 # C +OP_GMTraining=0x7b64 # C +OP_GMEndTraining=0x3c02 # C +OP_GMTrainSkill=0x7686 # C +OP_Animation=0x47d3 # C +OP_Begging=0x6d37 # C +OP_Consent=0x6bb9 # C +OP_ConsentDeny=0x01e3 # C +OP_AutoFire=0x1bd4 # C +OP_PetCommands=0x3ad6 # C +OP_DeleteSpell=0x0736 # C +OP_Surname=0x7547 # C +OP_ClearSurname=0x2edd # C +OP_FaceChange=0x5658 # C +OP_SenseHeading=0x3887 # C +OP_Action=0x2c27 # C +OP_ConsiderCorpse=0x37a7 # C +OP_HideCorpse=0x1842 +OP_CorpseDrag=0x5a6f # C +OP_CorpseDrop=0x5a01 # C +OP_Bug=0x02b # C +OP_Feedback=0x0d1d # C +OP_Report=0x6884 # C +OP_Damage=0x7e4d # C or OP_Action2? +OP_ChannelMessage=0x2e79 # C +OP_Assist=0x2b59 # C +OP_AssistGroup=0x5af6 # C +OP_MoveCoin=0x1fcd # C +OP_ZonePlayerToBind=0x1d3f # C +OP_KeyRing=0x0a18 # C +OP_WhoAllRequest=0x177a # C +OP_WhoAllResponse=0x31f3 # C +OP_FriendsWho=0x6a90 # C +OP_ConfirmDelete=0x3d00 # C +OP_Logout=0x66e0 # C +OP_Rewind=0x4b7b # C +OP_TargetCommand=0x17f7 # C +OP_InspectRequest=0x4892 # C +OP_Hide=0x1d22 # C +OP_Jump=0x6c16 # C +OP_Camp=0x3eec # C +OP_Emote=0x7434 # C +OP_SetRunMode=0x07fb # C +OP_BankerChange=0x21e9 # C +OP_TargetMouse=0x7bbb # C +OP_MobHealth=0x47ea # C +OP_InitialMobHealth=0x2d25 # C +OP_TargetHoTT=0x3ec7 # C +OP_TargetBuffs=0x3df8 +OP_BuffRemoveRequest=0x6f9d +OP_DeleteSpawn=0x3164 # C +OP_AutoAttack=0x3d86 # C +OP_AutoAttack2=0x4ca1 # C +OP_Consume=0x7ce4 # C +OP_MoveItem=0x7f56 # C +OP_DeleteItem=0x36f8 # C +OP_DeleteCharge=0x1df9 # C +OP_ItemPacket=0x34f8 # C +OP_ItemLinkResponse=0x2bad # C +OP_ItemLinkClick=0x3c66 # C +OP_NewSpawn=0x5c29 # C +OP_Track=0x4817 # C +OP_TrackTarget=0x7337 # C +OP_TrackUnknown=0x7b56 # C +OP_ClickDoor=0x1516 # C +OP_MoveDoor=0x6e97 # C +OP_RemoveAllDoors=0x1a58 # C +OP_EnvDamage=0x5ac4 # C +OP_BoardBoat=0x5cd3 # C +OP_Forage=0x7b05 # C +OP_LeaveBoat=0x7554 # C +OP_ControlBoat=0x5bd9 # C +OP_SafeFallSuccess=0x6ad2 # C +OP_RezzComplete=0x42d0 # C +OP_RezzRequest=0x0976 # C +OP_RezzAnswer=0x6e1c # C +OP_Shielding=0x2f6a # C +OP_RequestDuel=0x79e0 # C +OP_MobRename=0x0a1d # C +OP_AugmentItem=0x0370 # C +OP_WeaponEquip1=0x719e # C +OP_WeaponEquip2=0x7b6e # C +OP_WeaponUnequip2=0x19a8 # C +OP_ApplyPoison=0x405b # C +OP_Save=0x5c85 # C +OP_TestBuff=0x5fc7 # C +OP_DestructableRelated=0x5ea1 +OP_CustomTitles=0x1b26 # C +OP_Split=0x53f9 # C +OP_YellForHelp=0x6f79 # C +OP_LoadSpellSet=0x7113 # C +OP_Bandolier=0x441c # C +OP_PotionBelt=0x5db5 # C +OP_DuelResponse=0x1ebb # C +OP_SaveOnZoneReq=0x6eff # C +OP_ReadBook=0x2444 # C +OP_Dye=0x3672 # C +OP_InterruptCast=0x072f # C +OP_AAAction=0x50d0 # C +OP_LeadershipExpToggle=0x34c5 # C +OP_LeadershipExpUpdate=0x69d0 # C +OP_PurchaseLeadershipAA=0x07b3 # C +OP_UpdateLeadershipAA=0x6948 # C +OP_MarkNPC=0x0d4b # C +OP_ClearNPCMarks=0x5033 # C +OP_DoGroupLeadershipAbility=0x540b # C +OP_GroupLeadershipAAUpdate=0x0c33 +OP_DelegateAbility=0x0322 # C +OP_SetGroupTarget=0x521c # C +OP_DuelResponse2=0x52b5 # C +OP_Charm=0x7108 # C +OP_Stun=0x2a6d # C +OP_SendFindableNPCs=0x5360 +OP_FindPersonRequest=0x3168 # C +OP_FindPersonReply=0x1ac8 # C +OP_Sound=0x303e # C +OP_PetBuffWindow=0x2dd3 # C +OP_LevelAppearance=0x6dc3 # C +OP_Translocate=0x2042 # C +OP_Sacrifice=0x5805 # C +OP_PopupResponse=0x2caf # C +OP_OnLevelMessage=0x1154 # C +OP_AugmentInfo=0x3683 # C +OP_Petition=0x31d1 # C +OP_SomeItemPacketMaybe=0x54e8 # C +OP_PVPStats=0x6a37 # C +OP_PVPLeaderBoardRequest=0x24cb # C +OP_PVPLeaderBoardReply=0x6ac2 # C +OP_PVPLeaderBoardDetailsRequest=0x2a99 # C +OP_PVPLeaderBoardDetailsReply=0x5156 # C +OP_RestState=0x292f # C +OP_RespawnWindow=0x268c # C was 0x1643 +OP_DisciplineTimer=0x684c # C +OP_LDoNButton=0x41b5 # C +OP_SetStartCity=0x7bf6 # C +OP_VoiceMacroIn=0x31b1 # C +OP_VoiceMacroOut=0x7880 # C +OP_ItemViewUnknown=0x21c7 # C +OP_VetRewardsAvaliable=0x4e4e # C +OP_VetClaimRequest=0x771f # C +OP_VetClaimReply=0x2f95 # C +OP_CrystalCountUpdate=0x2d0e # C +OP_CrystalReclaim=0x4d4b +OP_CrystalCreate=0x6c80 +OP_DisciplineUpdate=0x7b87 # +OP_BecomeCorpse=0x0000 # +OP_MobUpdate=0x0000 # Unused? +OP_CameraEffect=0x0d96 # V +OP_SpellEffect=0x7309 # V +OP_AltCurrency=0x25e4 +OP_AltCurrencyMerchantRequest=0x440d +OP_AltCurrencyMerchantReply=0x3c37 +OP_AltCurrencyPurchase=0x5270 +OP_AltCurrencySell=0x7a21 +OP_AltCurrencySellSelection=0x26d9 +OP_AltCurrencyReclaim=0x712c +OP_ShroudProgress=0x0296 +OP_RemoveNimbusEffect=0x5272 # C +OP_Untargetable=0x5ea1 # 0x301d on UF? +OP_IncreaseStats=0x71eb +OP_InspectMessageUpdate=0x53a3 # C +OP_OpenInventory=0x1003 +OP_OpenContainer=0x3278 + +# Expedition +OP_DzQuit=0x054e +OP_DzListTimers=0x3200 +OP_DzAddPlayer=0x3ba3 +OP_DzRemovePlayer=0xa682 +OP_DzSwapPlayer=0x0d8d +OP_DzMakeLeader=0x1caa +OP_DzPlayerList=0x74ca +OP_DzJoinExpeditionConfirm=0x1772 +OP_DzJoinExpeditionReply=0x3c13 +OP_DzExpeditionInfo=0x128e +OP_DzMemberStatus=0x4661 +OP_DzLeaderStatus=0x226f +OP_DzExpeditionEndsWarning=0x1879 +OP_DzExpeditionList=0x3657 +OP_DzMemberList=0x74e4 +OP_DzCompass=0x35d3 +OP_DzChooseZone=0xd8a +#0x1d99 was grouped with these too but I don't really know it's purpose. + +# New Opcodes +OP_SpawnPositionUpdate=0x4656 # +OP_ManaUpdate=0x4b61 # +OP_EnduranceUpdate=0x02d6 # +OP_MobManaUpdate=0x2ac1 # +OP_MobEnduranceUpdate=0x6c5f # + +# Looting +OP_LootRequest=0x2701 # C +OP_EndLootRequest=0x6ad7 # C +OP_LootItem=0x2b5a # C +OP_LootComplete=0x41a6 # C + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x4675 # C +OP_TraderDelItem=0x63c8 # C +OP_BecomeTrader=0x528f # C +OP_TraderShop=0x7598 # C +OP_TraderItemUpdate=0x0000 # +OP_Trader=0x7092 # C +OP_ShopItem=0x0000 # +OP_TraderBuy=0x053a # C +OP_Barter=0x4b49 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x47ae # C +OP_TradeAcceptClick=0x064a # C +OP_TradeRequestAck=0x606a # C +OP_TradeCoins=0x0149 # C +OP_FinishTrade=0x3ff6 # C +OP_CancelTrade=0x527e # C +OP_TradeMoneyUpdate=0x7452 # C +OP_MoneyUpdate=0x2d1d # C +OP_TradeBusy=0x0c2d # V + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x3c27 # C +OP_FinishWindow2=0x2f5d # C + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x032a # C +OP_ItemVerifyReply=0x7866 # C + +# merchant crap +OP_ShopPlayerSell=0x0b27 # C +OP_ShopRequest=0x4194 # C +OP_ShopEnd=0x3753 # C +OP_ShopEndConfirm=0x7114 # C +OP_ShopPlayerBuy=0x436a # C +OP_ShopDelItem=0x63c8 # C + +# tradeskill stuff: +OP_ClickObject=0x5f0d # C +OP_ClickObjectAction=0x5dbc # C +OP_ClearObject=0x0b80 # C +OP_RecipeDetails=0x49f4 # C +OP_RecipesFavorite=0x1150 # C +OP_RecipesSearch=0x55dd # C +OP_RecipeReply=0x0643 # C +OP_RecipeAutoCombine=0x1564 # C +OP_TradeSkillCombine=0x4212 # C + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x6361 # C +OP_OpenTributeMaster=0x2daa # C +OP_SelectTribute=0x38b0 # C +OP_TributeItem=0x416b # C +OP_TributeMoney=0x0b89 # C +OP_TributeToggle=0x45b3 # C +OP_TributePointUpdate=0x7d05 # C +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x2ab6 # C +OP_AdventureFinish=0x624c # C +OP_AdventureInfoRequest=0x3a75 # C +OP_AdventureInfo=0x3a28 # C +OP_AdventureRequest=0x5a55 # C +OP_AdventureDetails=0x5ed3 # C +OP_AdventureData=0x05 # C +OP_AdventureUpdate=0x0dcd # C +OP_AdventureMerchantRequest=0x0989 # C +OP_AdventureMerchantResponse=0x6ade # C +OP_AdventureMerchantPurchase=0x23fc # C +OP_AdventureMerchantSell=0x5363 # C +OP_AdventurePointsUpdate=0x6b7f # C +OP_AdventureStatsRequest=0x7e45 # C +OP_AdventureStatsReply=0x6a30 # C +OP_AdventureLeaderboardRequest=0x7537 # C +OP_AdventureLeaderboardReply=0x7c87 # C + +# Group Opcodes +OP_GroupDisband=0x47e7 # C +OP_GroupInvite=0x5d32 # C +OP_GroupFollow=0x321a # C +OP_GroupUpdate=0x21be +OP_GroupUpdateB=0x7351 +OP_GroupCancelInvite=0x5251 # C - Same as OP_CancelInvite? +OP_GroupAcknowledge=0x3e22 # C +OP_GroupDelete=0x58e6 # +OP_CancelInvite=0x5251 # C +OP_GroupFollow2=0x2736 # C +OP_GroupInvite2=0x548c # C +OP_GroupDisbandYou=0xc56c +OP_GroupDisbandOther=0x162d +OP_GroupLeaderChange=0x7545 +OP_GroupRoles=0x6b67 +OP_GroupMakeLeader=0x6087 +# LFG/LFP Opcodes +OP_LFGCommand=0x3288 # C +OP_LFGGetMatchesRequest=0x5613 # C +OP_LFGGetMatchesResponse=0x7d90 # C +OP_LFPGetMatchesRequest=0x0479 # C +OP_LFPGetMatchesResponse=0x16f5 # C +OP_LFPCommand=0x710e # C +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x3b52 # C +OP_RaidUpdate=0x32c6 # C +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x1418 # C +OP_CombatAbility=0x24c5 # C +OP_SenseTraps=0x1e04 # C +OP_PickPocket=0x25f0 # C +OP_DisarmTraps=0x0000 # +OP_Disarm=0x6def # C +OP_Sneak=0x03e7 # C +OP_Fishing=0x7093 # C +OP_InstillDoubt=0x2d41 # C +OP_FeignDeath=0x3f49 # C +OP_Mend=0x221a # C +OP_LDoNOpen=0x6129 # C + +# Task packets +OP_TaskActivityComplete=0x4df0 # C +OP_TaskMemberList=0x34ed # C +OP_OpenNewTasksWindow=0x4dd5 # C +OP_AvaliableTask=0x2136 # C +OP_AcceptNewTask=0x5832 # C +OP_TaskHistoryRequest=0x29d7 # C +OP_TaskHistoryReply=0x3d2a # C +OP_CancelTask=0x726b # C +OP_DeclineAllTasks=0x0000 # +OP_Shroud=0x6d1f +OP_ShroudRemove=0x17f6 +OP_ShroudUnknown1=0x169a +OP_ShroudUnknown2=0x4292 +OP_ShroudClearAA=0x3bef +OP_ShroudSelectionWindow=0x4d79 +OP_ShroudRequestStats=0x28ce +OP_ShroudRespondStats=0x33f2 +OP_ShroudSelect=0x194f + +# Title opcodes +OP_NewTitlesAvailable=0x4399 # C +OP_RequestTitles=0x7327 # C +OP_SendTitleList=0x158f # C +OP_SetTitle=0x698a # C +OP_SetTitleReply=0x4d3e # C + +# Mercenary Opcodes +OP_MercenaryDataRequest=0x4717 # +OP_MercenaryDataResponse=0x1715 # +OP_MercenaryHire=0x0bfd # +OP_MercenaryAssign=0x6188 # +OP_MercenaryTimer=0x116d # +OP_MercenaryUnknown1=0x28c7 # +OP_MercenaryDataUpdate=0x0786 # +OP_MercenaryCommand=0x27bf # +OP_MercenarySuspendRequest=0x05f1 # +OP_MercenarySuspendResponse=0x57f2 # +OP_MercenaryUnsuspendResponse=0x3015 +OP_MercenaryDataUpdateRequest=0x390c # +OP_MercenaryDismiss=0x0bd0 # +OP_MercenaryTimerRequest=0x0924 # + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_IncreaseStats=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x5bad # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x5d24 # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0001 # +OP_Login=0x0002 # +OP_ServerListRequest=0x0004 # +OP_PlayEverquestRequest=0x000d # +OP_PlayEverquestResponse=0x0021 # +OP_ChatMessage=0x0016 # +OP_LoginAccepted=0x0017 # +OP_ServerListResponse=0x0018 # +OP_Poll=0x0029 # +OP_EnterChat=0x000f # +OP_PollResponse=0x0011 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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 diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf new file mode 100644 index 000000000..73bbb44c6 --- /dev/null +++ b/utils/patches/patch_SoF.conf @@ -0,0 +1,646 @@ + +#ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +#Unknown Mapping: +#OP_Action2 -> OP_Damage +#OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +#Name Differences: +#OP_CancelInvite -> OP_GroupCancelInvite +#OP_GMFind -> OP_FindPersonRequest +#OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 #used for unknown explorer + +#world packets +#Required to reach Char Select: +OP_SendLoginInfo=0x6c3c #SEQ 12/04/08 +OP_ApproveWorld=0x7FC0 #SEQ 12/04/08 +OP_LogServer=0x129A #SEQ 12/04/08 +OP_SendCharInfo=0x6040 #SEQ 12/04/08 +OP_ExpansionInfo=0x0A1B #SEQ 12/04/08 +OP_GuildsList=0x04FB #SEQ 12/04/08 +OP_EnterWorld=0x1340 #SEQ 12/04/08 +OP_PostEnterWorld=0x1AEE #SEQ 12/04/08 +OP_World_Client_CRC1=0x7A9E #SEQ 12/04/08 +OP_World_Client_CRC2=0x3795 #SEQ 12/04/08 +OP_SendSpellChecksum=0x22CF #SEQ 12/04/08 +OP_SendSkillCapsChecksum=0x43BA #SEQ 12/04/08 + +#Character Select Related: +OP_DeleteCharacter=0x789F #SEQ 12/04/08 +OP_CharacterCreateRequest=0x7E32 #New for SoF 02/12/09 +OP_CharacterCreate=0x009B #SEQ 12/04/08 +OP_RandomNameGenerator=0x0149A #Derision 02/12/09 +OP_ApproveName=0x2037 #SEQ 12/04/08 + +OP_MOTD=0x5711 #SEQ 12/04/08 +OP_SetChatServer=0x71B8 #SEQ 12/04/08 +OP_SetChatServer2=0x32CC #SEQ 12/04/08 +OP_ZoneServerInfo=0x18B1 #SEQ 12/04/08 +OP_WorldComplete=0x2486 #SEQ 12/04/08 +OP_WorldUnknown001=0x7930 #Trevius 1/20/08 +OP_FloatListThing=0x6177 #Trevius 12/20/08 + +#Reasons for Disconnect: +OP_ZoneUnavail=0x07a2 #Derision 02/17/09 +OP_WorldClientReady=0x2EBD #SEQ 12/04/08 +OP_CharacterStillInZone=0x0000 # world->client. reject. +OP_WorldChecksumFailure=0x0000 # world->client. reject. +OP_WorldLoginFailed=0x0000 # world->client. reject. +OP_WorldLogout=0x0000 # client->world +OP_WorldLevelTooHigh=0x0000 # world->client. Cancels zone in. +OP_CharInacessable=0x0000 # world->client. Cancels zone in. +OP_UserCompInfo=0x0000 #NEW FROM SEQ 12/04/08 0x02a5 +#OP_SendExeChecksum=0x0000 #SEQ 12/04/08 +#OP_SendBaseDataChecksum=0x0000 #SEQ 12/04/08 + +#Zone in opcodes +OP_AckPacket=0x4D38 #SEQ 12/04/08 +OP_ZoneEntry=0x737E #SEQ 12/04/08 +OP_ReqNewZone=0x5417 #SEQ 12/04/08 +OP_NewZone=0x5D22 #SEQ 12/04/08 +OP_ZoneSpawns=0x5AF2 #SEQ 12/04/08 +OP_PlayerProfile=0x0FEB #SEQ 12/04/08 +OP_TimeOfDay=0x7274 #SEQ 12/04/08 +OP_LevelUpdate=0x1036 #SEQ 12/04/08 +OP_Stamina=0x7120 #SEQ 12/04/08 0x45E2 0x7120 0x389D +OP_LockoutTimerInfo=0x0000 #SEQ 12/04/08 0x7f63 +OP_ZoneServerReady=0x0000 #doesn't exist in Titanium + +OP_RequestClientZoneChange=0x7899 #Trevius 02/08/09 +OP_ZoneChange=0x201e #Trevius 02/08/09 + +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # 0x711E + +#Required to fully log in +OP_SpawnAppearance=0x50EC #Trevius 12/20/08 +OP_TributeUpdate=0x71FB #SEQ 12/04/08 +OP_TributeTimer=0x2871 #Derision 2009 +OP_TaskDescription=0x0AF9 #Derision 2009 +OP_TaskActivity=0x2E60 #SEQ 12/04/08 +OP_CompletedTasks=0x75AC #Derision 2009 +OP_Weather=0x70A5 #SEQ 12/04/08 +OP_SendAATable=0x6F05 #Trevius 12/20/08 +OP_UpdateAA=0x45D2 #Trevius 12/20/08 +OP_RespondAA=0x4426 #Trevius 12/20/08 +OP_ReqClientSpawn=0x014C #SEQ 12/04/08 +OP_SpawnDoor=0x68C8 #SEQ 12/04/08 +OP_GroundSpawn=0x30da #SEQ 12/04/08 +OP_SendZonepoints=0x2992 #SEQ 12/04/08 +OP_SendAAStats=0x60DB #Trevius 12/20/08 0x71B9 0x60DB +OP_WorldObjectsSent=0x3703 #New for SoF 1/1/09 +OP_BlockedBuffs=0x39C4 #New for SoF 1/1/09 +OP_RemoveBlockedBuffs=0x125e # +OP_ClearBlockedBuffs=0x2f8b # +OP_SendExpZonein=0x1FA1 #Trevius 12/20/08 +OP_SendTributes=0x6764 #Trevius 12/20/08 +OP_TributeInfo=0x68C2 #Derision 2009 +OP_SendGuildTributes=0x3F33 #Trevius 12/20/08 +OP_AAExpUpdate=0x3518 #SEQ 12/04/08 +OP_ExpUpdate=0x6d0b #SEQ 12/04/08 +OP_HPUpdate=0x62F6 #SEQ 12/04/08 +OP_ManaChange=0x0659 #SEQ 12/04/08 +OP_TGB=0x0137 #SEQ 12/04/08 +OP_SpecialMesg=0x1126 #SEQ 12/04/08 +OP_GuildMemberList=0x317C # +OP_GuildMOTD=0x3309 #Xinu 02/21/09 +OP_CharInventory=0x08A3 #SEQ 12/04/08 +OP_WearChange=0x25F0 #SEQ 12/04/08 +OP_ClientUpdate=0x5Cf3 #SEQ 12/04/08 +OP_ClientReady=0x2854 #Trevius 12/20/08 +OP_SetServerFilter=0x7038 #SEQ 12/04/08 + +#Guild Opcodes +OP_GetGuildMOTD=0x0b50 # +OP_GetGuildMOTDReply=0x4289 # +OP_GuildMemberUpdate=0x71D1 #SEQ 12/04/08 +OP_GuildInvite=0x430F # or 0x5C2F +OP_GuildMemberLevelUpdate=0x0000 #doesn't exist in this version +OP_GuildRemove=0x7149 #Xinu 02/20/09 +OP_GuildPeace=0x1B42 #Xinu 02/20/09 +OP_SetGuildMOTD=0x273F #Xinu 2/20/09 +OP_GuildList=0x04FB #ShowEQ Same as GuildsList??? +OP_GuildWar=0x5F91 #Xinu 02/22/09 +OP_GuildLeader=0x7703 #Xinu 02/22/09 +OP_GuildDelete=0x0B9A #Xinu 02/22/09 +OP_GuildInviteAccept=0x110F #Xinu 02/22/09 +OP_ZoneGuildList=0x0000 # +OP_GuildDemote=0x2cdf # +OP_GetGuildsList=0x0000 # +OP_GuildPublicNote=0x28a9 # +OP_GuildManageBanker=0x899b # +OP_GuildBank=0x43ff # +OP_SetGuildRank=0x4914 # +OP_GuildUpdateURLAndChannel=0x60a5 # +OP_LFGuild=0x3563 # +#OP_GuildManageRemove=0x0000 # +#OP_GuildManageAdd=0x0000 # +#OP_GuildManageStatus=0x0000 + +#GM/guide opcodes +OP_GMServers=0x1F23 #/servers +OP_GMBecomeNPC=0x42FA #/becomenpc +OP_GMZoneRequest=0x7678 #/zone +OP_GMZoneRequest2=0x6833 #Extra packet sent after /zone request +OP_GMGoto=0x1E2C #/goto +OP_GMSearchCorpse=0x5F15 #/searchcorpse +OP_GMHideMe=0x2E3C #/hideme +OP_GMDelCorpse=0x79DF #/delcorpse +OP_GMApproval=0x5546 #/approval +OP_GMToggle=0x7AB6 #/toggletell +OP_GMSummon=0x3235 #/summon +OP_GMEmoteZone=0x26CC #/emotezone +OP_GMEmoteWorld=0x6256 #/emoteworld (not implemented) +OP_GMFind=0x24D8 #/find +OP_GMKick=0x7A92 #/kick +OP_GMKill=0x7B6B #/kill +OP_GMNameChange=0x74B5 #/name +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_ConsentResponse=0x4D30 #SEQ 12/04/08 +OP_MemorizeSpell=0x6A93 #SEQ 12/04/08 +OP_SwapSpell=0x1418 #SEQ 12/04/08 +OP_CastSpell=0x7F5D #SEQ 12/04/08 +OP_Consider=0x32E1 #SEQ 12/04/08 +OP_FormattedMessage=0x5B9E #SEQ 12/04/08 +OP_SimpleMessage=0x553E #SEQ 12/04/08 +OP_Buff=0x7BD6 #SEQ 12/04/08 +OP_Illusion=0x7F86 #SEQ 12/04/08 +OP_MoneyOnCorpse=0x51C9 #SEQ 12/04/08 +OP_RandomReply=0x649C #SEQ 12/04/08 +OP_DenyResponse=0x607E #SEQ 12/04/08 +OP_SkillUpdate=0x29E0 #SEQ 12/04/08 +OP_GMTrainSkillConfirm=0x33A3 #New opcode in SoF +OP_RandomReq=0x4E2D #SEQ 12/04/08 +OP_Death=0x596F #SEQ 12/04/08 +OP_Bind_Wound=0x351E #Xinu 02/20/09 +OP_GMTraining=0x0362 #Trevius 01/16/09 +OP_GMEndTraining=0x6768 #Trevius 01/16/09 +OP_GMTrainSkill=0x7498 #Trevius 02/18/09 +OP_Animation=0x13A1 #Trevius 02/16/09 +OP_Begging=0x7AE0 #Trevius 01/16/09 +OP_Consent=0x0FB9 #Trevius 01/16/09 +OP_ConsentDeny=0x79D7 #Trevius 01/16/09 +OP_AutoFire=0x1486 #Trevius 02/17/09 +OP_PetCommands=0x573A #Trevius 02/17/09 +OP_DeleteSpell=0x6D7E #Xinu 02/20/09 +OP_Surname=0x683E #Xinu 02/21/09 +OP_ClearSurname=0x2613 +OP_FaceChange=0x482D #Trevius 01/16/09 +OP_SenseHeading=0x1237 #Trevius 01/16/09 +OP_Action=0x5285 #Trevius 01/16/09 +OP_ConsiderCorpse=0x4CBB #Xinu 02/20/09 +OP_CorpseDrag=0x51e1 # +OP_CorpseDrop=0x77a6 # +OP_Bug=0x1CF0 #Trevius 01/16/09 +OP_Feedback=0x216C #Trevius 01/16/09 - size: 1148 +OP_Report=0x5BD9 #Trevius 01/16/09 - size: 729 +OP_Damage=0x0EF2 #Trevius 02/18/09 +OP_ChannelMessage=0x3C7D #SEQ 12/04/08 +OP_Assist=0x15A4 #Xinu 02/20/09 +OP_AssistGroup=0x3ce # +OP_MoveCoin=0x1D9D #Xinu 02/20/09 +OP_ZonePlayerToBind=0x6542 #Derision 02/17/09 +OP_KeyRing=0x2F10 #Xinu 02/20/09 +OP_WhoAllRequest=0x34D4 #Trevius 12/20/08 +OP_WhoAllResponse=0x3817 #Trevius 12/20/08 +OP_FriendsWho=0x237b #Trevius 03/23/09 +OP_ConfirmDelete=0x79A5 #Trevius 12/20/08 +OP_Logout=0x3874 #SEQ 12/04/08 +OP_Rewind=0x428F #Trevius 1/20/09 +OP_TargetCommand=0x2DA9 #Trevius 1/20/09 +OP_InspectRequest=0x5A79 #Trevius 1/20/09 +OP_Hide=0x65FF #Trevius 1/20/09 +OP_Jump=0x7871 #Trevius 1/20/09 +OP_Camp=0x6807 #Trevius 1/20/09 +OP_Emote=0x5f4D #Trevius 2/16/09 +OP_SetRunMode=0x2716 #Trevius 1/20/09 +OP_BankerChange=0x03DD #Trevius 1/20/09 +OP_TargetMouse=0x4395 #Trevius 1/20/09 +OP_MobHealth=0x311A #Trevius 1/20/09 +OP_InitialMobHealth=0x23F1 #Trevius 1/20/09 +OP_TargetHoTT=0x1306 #Derision 2009 +OP_DeleteSpawn=0x15da #Trevius 1/20/09 +OP_MobUpdate=0x7647 #SEQ 01/17/08 +OP_AutoAttack=0x3427 #Trevius 01/20/09 +OP_AutoAttack2=0x6017 #Trevius 01/20/09 +OP_Consume=0x729a #Trevius 02/08/09 +OP_MoveItem=0x14B3 #Trevius 02/08/09 +OP_DeleteItem=0x7DD4 #Xinu 03/08/09 0x41EE 0x018E 0x070C +OP_DeleteCharge=0x32e2 #Trevius 03/23/09 +OP_ItemPacket=0x78Cd #Trevius 02/08/09 +OP_ItemLinkResponse=0x31E4 #Trevius 02/16/09 +OP_ItemLinkClick=0x2DE4 #Trevius 02/08/09 +OP_NewSpawn=0x581A #Trevius 01/20/09 +OP_Track=0x060A #Trevius 02/17/09 +OP_TrackTarget=0x21e8 #Xinu 02/22/09 +OP_TrackUnknown=0x6A70 #Trevius 02/17/09 +OP_ClickDoor=0x5AC1 #Trevius 02/22/09 +OP_MoveDoor=0x61DF #Trevius 02/23/09 +OP_RemoveAllDoors=0x69d8 #C +OP_EnvDamage=0x20e7 #Xinu 02/22/09 +OP_BoardBoat=0x54A7 #Xinu 02/22/09 +OP_Forage=0x54C5 #Xinu 02/22/09 +OP_LeaveBoat=0x56A2 #Xinu 02/22/09 +OP_ControlBoat=0x4CE7 #Xinu 02/22/09 +OP_SafeFallSuccess=0x1584 #Xinu 02/22/09 +OP_RezzComplete=0x4b0d #Derision 02/10/09 +OP_RezzRequest=0x2bb7 #Derision 02/10/09 +OP_RezzAnswer=0x076d #Derision 02/10/09 +OP_Shielding=0x0829 #Trevius 01/16/09 +OP_RequestDuel=0x3A2B #Xinu 02/22/09 +OP_MobRename=0x6be5 #Trevius 01/16/09 +OP_AugmentItem=0x172A #Trevius 03/14/09 +OP_WeaponEquip1=0x7260 #Trevius 02/27/09 +OP_WeaponEquip2=0x5C2F #Trevius 02/27/09 +OP_WeaponUnequip2=0x6213 #Trevius 02/27/09 +OP_ApplyPoison=0x4543 #WildcardX 03/6/09 +OP_Save=0x72F2 #Trevius 03/15/09 +OP_TestBuff=0x07BF #/testbuff +OP_CustomTitles=0x2e8f #Trevius 03/12/09 +OP_Split=0x198E #Trevius 03/19/09 +OP_YellForHelp=0x4F4A #Trevius 03/19/09 +OP_LoadSpellSet=0x05B5 #Trevius 03/19/09 +OP_Bandolier=0x3FD4 #Trevius 03/19/09 +OP_PotionBelt=0x16F3 #Trevius 03/19/09 +OP_DuelResponse=0x5E59 #Derision 2009 +OP_SaveOnZoneReq=0x1103 #Trevius 03/20/09 +OP_ReadBook=0x424a #Xinu 03/19/09 +OP_Dye=0x3611 #Xinu 03/19/09 +OP_InterruptCast=0x51F6 #Xinu 02/25/09 +OP_AAAction=0x0A4F #Xinu 02/20/09 +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_ClearNPCMarks=0x2ff2 # +OP_DoGroupLeadershipAbility=0x5a64 #Derision 2009 +OP_DelegateAbility=0x57e3 #Derision 2009 +OP_SetGroupTarget=0x1651 #Derision 2009 +OP_DuelResponse2=0x2A85 #Derision 2009 +OP_Charm=0x2F32 #Derision 2009 +OP_Stun=0x55BF #Derision 2009 +OP_FindPersonRequest=0x07F0 #Derision 2009 +OP_FindPersonReply=0x7770 #Derision 2009 +OP_Sound=0x2B02 #Derision 2009 +OP_PetBuffWindow=0x124A #Derision 2009 +OP_LevelAppearance=0x3EC8 #Derision 2009 +OP_Translocate=0x1F0F #Derision 2009 +OP_Sacrifice=0x55C9 #Derision 2009 +OP_PopupResponse=0x028B #Derision 2009 +OP_OnLevelMessage=0x0332 #Derision 2009 +OP_AugmentInfo=0x08f8 #RealityIncarnate 4/28/09 +OP_Petition=0x3A46 #Derision 2009 /guidehelp +OP_SomeItemPacketMaybe=0x08FA #Derision 2009 +OP_PVPStats=0x6af1 #Derision 2009 +OP_PVPLeaderBoardRequest=0x5b3b #Derision 2009 +OP_PVPLeaderBoardReply=0x3a69 #Derision 2009 +OP_PVPLeaderBoardDetailsRequest=0x43b6 #Derision 2009 +OP_PVPLeaderBoardDetailsReply=0x680c #Derision 2009 +OP_RestState=0x116E #Derision 2009 +OP_RespawnWindow=0x7bf6 #SEQ 12/04/08 or 0x7BF6 +OP_DisciplineTimer=0x53c5 #Derision 2009 +OP_LDoNButton=0x7eac # +OP_SetStartCity=0x7e91 #realityincarnate 6/25/09 +OP_VoiceMacroIn=0x6276 #Trevius 8/11/09 +OP_VoiceMacroOut=0x30d2 #Trevius 8/11/09 +OP_DisciplineUpdate=0x20e8 # +OP_BecomeCorpse=0x0000 # +OP_CameraEffect=0x23f9 # C +OP_SpellEffect=0x3e90 # C +OP_AltCurrency=0x0f05 +OP_AltCurrencyMerchantRequest=0x433f +OP_AltCurrencyMerchantReply=0x7b20 +OP_AltCurrencyPurchase=0x3994 +OP_AltCurrencySell=0x2ac3 +OP_AltCurrencySellSelection=0x7d00 +OP_AltCurrencyReclaim=0x1996 +OP_RemoveNimbusEffect=0x5872 # C +OP_InspectMessageUpdate=0x67e9 # C +OP_OpenInventory=0x66c8 +OP_OpenContainer=0x10e3 + +#expedition +OP_DzQuit=0x20d6 +OP_DzListTimers=0x4e4b +OP_DzAddPlayer=0x426a +OP_DzRemovePlayer=0x2ce8 +OP_DzSwapPlayer=0x2c3e +OP_DzMakeLeader=0x1a75 +OP_DzPlayerList=0x5116 +OP_DzJoinExpeditionConfirm=0x1793 +OP_DzJoinExpeditionReply=0x7a6f +OP_DzExpeditionInfo=0x60a6 +OP_DzMemberStatus=0x0516 +OP_DzLeaderStatus=0x79d3 +OP_DzExpeditionEndsWarning=0x5153 +OP_DzExpeditionList=0x02ac +OP_DzMemberList=0x5e14 +OP_DzCompass=0x531d +OP_DzChooseZone=0x3c5b + +#Looting +OP_LootRequest=0x36E3 #Trevius 02/16/09 +OP_EndLootRequest=0x6599 #Trevius 02/16/09 +OP_LootItem=0x3760 #Trevius 02/16/09 +OP_LootComplete=0x48F3 #Trevius 02/23/09 + +#bazaar trader stuff stuff: +OP_BazaarSearch=0x14F2 #SEQ 12/04/08 0x4675 +OP_TraderDelItem=0x068D #Derision 2009 +OP_BecomeTrader=0x32B7 #Derision 2009 +OP_TraderShop=0x1ACF #Derision 2009 +OP_TraderItemUpdate=0x0000 # +OP_Trader=0x6b41 #Xinu 02/21/09 +OP_ShopItem=0x0000 # +OP_TraderBuy=0x7569 #Derision 2009 +OP_Barter=0x51bf #Xinu 02/21/09 +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # + +#pc/npc trading +OP_TradeRequest=0x180D #Trevius 02/16/09 +OP_TradeAcceptClick=0x1D07 #Trevius 03/09/09 +OP_TradeRequestAck=0x1255 #Xinu 02/24/09 +OP_TradeCoins=0x08CA #Trevius 03/10/09 +OP_FinishTrade=0x30B7 #Trevius 03/09/09 +OP_CancelTrade=0x7A43 #Xinu 02/24/09 +OP_TradeMoneyUpdate=0x69E7 +OP_MoneyUpdate=0x305A +OP_TradeBusy=0x5f49 # + +#Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x4919 #Trevius 03/15/09 +OP_FinishWindow2=0x1758 #Trevius 03/15/09 + +#Sent on Live for what seems to be item existance verification +#Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x0cc2 #Trevius 03/15/09 +OP_ItemVerifyReply=0x41ee #Trevius 03/15/09 + +#merchant crap +OP_ShopPlayerSell=0x5A58 #Trevius 02/16/09 +OP_ShopRequest=0x5FB7 #Trevius 2/16/09 +OP_ShopEnd=0x0BD9 #Xinu 02/20/09 +OP_ShopEndConfirm=0x475F #Trevius 02/16/09 +OP_ShopPlayerBuy=0x09C4 #Trevius 02/16/09 +OP_ShopDelItem=0x068D + +#tradeskill stuff: +OP_ClickObject=0x21B3 #SEQ 12/04/08 +OP_ClickObjectAction=0x67C0 #Trevius 03/14/09 +OP_ClearObject=0x0e66 #Trevius 03/14/09 +OP_RecipeDetails=0x0706 #Trevius 03/14/09 +OP_RecipesFavorite=0x2E2C #Trevius 03/14/09 +OP_RecipesSearch=0x41A2 #Xinu 02/20/09 +OP_RecipeReply=0x67f5 #Trevius 03/14/09 +OP_RecipeAutoCombine=0x0427 #Trevius 03/14/09 +OP_TradeSkillCombine=0x163C #Xinu 02/20/09 + +#Tribute Packets: +OP_OpenGuildTributeMaster=0x3223 #Xinu 02/20/09 +OP_OpenTributeMaster=0x5027 #open tribute master window +OP_OpenTributeReply=0x0000 #reply to open request +OP_SelectTribute=0x1D8C #clicking on a tribute, and text reply +OP_TributeItem=0x16D6 #donating an item +OP_TributeMoney=0x65BC #donating money +OP_TributeNPC=0x0000 #seems to be missing now +OP_TributeToggle=0xE59F #activating/deactivating tribute +OP_TributePointUpdate=0x656C #16 byte point packet +OP_GuildTributeInfo=0x0000 # +#OP_GuildTributeStatus=0x0000 # + +#Adventure packets: +OP_LeaveAdventure=0x1342 # +OP_AdventureFinish=0x0974 # +OP_AdventureInfoRequest=0x6e84 #Xinu 03/19/09 +OP_AdventureInfo=0x526d #Xinu 03/19/09 +OP_AdventureRequest=0x4f4c #Xinu 03/19/09 +OP_AdventureDetails=0x7f4d # +OP_AdventureData=0x10b9 # +OP_AdventureUpdate=0x53c2 # +OP_AdventureMerchantRequest=0x71b2 #Xinu 03/19/09 +OP_AdventureMerchantResponse=0x40d6 #Xinu 03/19/09 +OP_AdventureMerchantPurchase=0x5488 #Xinu 03/19/09 +OP_AdventureMerchantSell=0x1c26 #Xinu 03/19/09 +OP_AdventurePointsUpdate=0x6583 #Xinu 03/19/09 +OP_AdventureStatsRequest=0x70B2 #Trevius 06/03/09 +OP_AdventureStatsReply=0x5A3A #Trevius 06/03/09 +OP_AdventureLeaderboardRequest=0x2A28 #Trevius 06/03/09 +OP_AdventureLeaderboardReply=0x6555 #Trevius 06/03/09 + +#Group Opcodes +OP_GroupDisband=0x4B26 #SEQ 12/04/08 +OP_GroupInvite=0x6321 #SEQ 12/04/08 +OP_GroupFollow=0x1A12 #SEQ 12/04/08 +OP_GroupUpdate=0x2E5C #SEQ 12/04/08 +OP_GroupCancelInvite=0x596C #SEQ 12/04/08 +OP_GroupAcknowledge=0x1306 #Trevius 02/17/09 +OP_GroupDelete=0x0000 # +OP_CancelInvite=0x596C #Trevius 03/02/09 +OP_GroupFollow2=0x59D4 #Xinu 02/20/09 +OP_GroupInvite2=0x07F6 #Xinu 02/20/09 + +#LFG/LFP Opcodes +OP_LFGCommand=0x5D81 #Trevius 01/16/09 +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # +OP_LFGGetMatchesRequest=0x21E0 #Xinu 02/23/09 +OP_LFGGetMatchesResponse=0x6dca #Xinu 03/19/09 +OP_LFPGetMatchesRequest=0x601A #Xinu 02/23/09 +OP_LFPGetMatchesResponse=0x2274 #Xinu 03/19/09 +OP_LFPCommand=0x1F00 #Trevius 02/27/09 (this actually sends 0x0000) 0x1F00 + +#Raid Opcodes +OP_RaidJoin=0x0000 #RaidJoin and RaidUpdate seem to be using the same opcode on Live +OP_RaidInvite=0x0E11 #Xinu 02/20/09 +OP_RaidUpdate=0x237E #Xinu 02/20/09 + +#Button-push commands +OP_Taunt=0x094D #Xinu 2/20/09 +OP_CombatAbility=0x045D #Xinu 2/20/09 +OP_SenseTraps=0x7466 #Xinu 2/20/09 +OP_PickPocket=0x768A #Xinu 2/20/09 +OP_DisarmTraps=0x0000 # +OP_Disarm=0x19B6 #Xinu 2/20/09 +OP_Sneak=0x17C4 #Xinu 2/20/09 +OP_Fishing=0x73AB #Xinu 2/20/09 +OP_InstillDoubt=0x6865 #Xinu 2/20/09 +OP_FeignDeath=0x1C68 #Trevius 02/17/09 +OP_Mend=0x7767 #Trevius 02/17/09 +OP_LDoNOpen=0x4b92 #Xinu 03/19/09 + +#Task packets +OP_TaskActivityComplete=0x7338 # +OP_OpenNewTasksWindow=0x17C3 # +OP_AvaliableTask=0x5d1d #Xinu 03/19/09 +OP_AcceptNewTask=0x66A8 # +OP_TaskHistoryRequest=0x3035 # +OP_TaskHistoryReply=0x3A60 # +OP_CancelTask=0x4db6 #Xinu or 0x2c8c or 0x4db6 +OP_DeclineAllTasks=0x0000 #not sure, 12 bytes +OP_TaskMemberList=0x3713 +OP_TaskMemberInvite=0x3cde +OP_TaskMemberInviteResponse=0x6cab +OP_TaskMemberChange=0x354a +OP_TaskMakeLeader=0x5050 +OP_TaskAddPlayer=0x5d1d +OP_TaskRemovePlayer=0x516f +OP_TaskPlayerList=0x0ad6 +OP_TaskQuit=0x2c8c + +#Title opcodes +OP_NewTitlesAvailable=0x179c # +OP_RequestTitles=0x3445 # +OP_SendTitleList=0x3754 # +OP_SetTitle=0x54fe # +OP_SetTitleReply=0x690b # + +#mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +########### Below this point should not be needed ########### + +#This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_IncreaseStats=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +#discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_VetRewardsAvaliable=0x044b # +OP_VetClaimRequest=0x7503 +OP_VetClaimReply=0x01e1 +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_CrystalCountUpdate=0x64C1 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 #/adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 #gone I think +OP_MoveLogDisregard=0x0000 #gone I think + +#named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 #Trevius 02/20/09 +OP_Some6ByteHPUpdate=0x0000 #seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +OP_LoginUnknown1=0x22cf +OP_LoginUnknown2=0x43ba +OP_ItemViewUnknown=0x4db4 + +#Petition Opcodes +OP_PetitionSearch=0x0000 #search term for petition +OP_PetitionSearchResults=0x0000 #(list of?) matches from search +OP_PetitionSearchText=0x0000 #text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +#Login opcodes +OP_SessionReady=0x0001 # +OP_Login=0x0002 # +OP_ServerListRequest=0x0004 # +OP_PlayEverquestRequest=0x000d # +OP_PlayEverquestResponse=0x0021 # +OP_ChatMessage=0x0016 # +OP_LoginAccepted=0x0017 # +OP_ServerListResponse=0x0018 # +OP_Poll=0x0029 # +OP_EnterChat=0x000f # +OP_PollResponse=0x0011 # + +#raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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 # + + +# Opcodes from the client that are currently Unknowns: +# 0x3E85 - Sent when Guild Management window is opened +# 0x66C8 (size 4) - Sent when Inventory window is opened +# 0x5BDA (size 8) - Sent when opening Shroud Bank window +# 0x03DD (size 4) - Sent when clicking the Change button in the Bank window +# 0x58FB (size 8) - Auto-AFK after 15 minutes +# 0x7260 (size 12) - unknown - Client crash if server sends this - Came in when item was moved to a new slot +# 0x10E3 (size 4) - Open a bag +# 0x078E (size 76)- Some new Opcode that sends GM Last Name from server to client after training skill points +# 0x +# 0x +# 0x + +#OP_UnderWorld=0x7850 #New for SoF 1/1/09 +#OP_InventoryWindow=0x66C8 #New From SoF Client when inventory window is opened +#OP_ItemPlayerPacket=0x0000 # +#OP_GetGuildMOTD=0x0000 # +#OP_GuildTributeStatus=0x0000 # +#OP_GMEndTrainingResponse=0x0000 # +#OP_ObfuscatorInfo=0x7930 #SEQ 12/04/08 --NEW FROM SEQ +#OP_GroupDisband2=0x0000 #SEQ 10/07/08 --NEW FROM SEQ +#OP_GroupLeader=0x0000 #SEQ 10/07/08 --NEW FROM SEQ +#OP_CorpseLocResponse=0x0292 #SEQ 12/04/08 --NEW FROM SEQ +#OP_RespawnFromHover=0x7BF6 #SEQ 12/04/08 --NEW FROM SEQ +#OP_RemoveSpawn=0x0000 #SEQ 04/17/08 --NEW FROM SEQ +#OP_Shroud=0x7580 #SEQ 12/04/08 --NEW FROM SEQ +#OP_NpcMoveUpdate=0x0d11 #SEQ 10/07/08 --NEW FROM SEQ +#OP_Zone_MissingName01=0x0000 # +#new titles avaliable: # diff --git a/utils/patches/patch_Titanium.conf b/utils/patches/patch_Titanium.conf new file mode 100644 index 000000000..2c05520d0 --- /dev/null +++ b/utils/patches/patch_Titanium.conf @@ -0,0 +1,594 @@ + +#ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +#Unknown Mapping: +#OP_Action2 -> OP_Damage +#OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +#Name Differences: +#OP_CancelInvite -> OP_GroupCancelInvite +#OP_GMFind -> OP_FindPersonRequest +#OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 #used for unknown explorer + +#world packets +OP_ApproveWorld=0x3c25 # ShowEQ 10/27/05 +OP_LogServer=0x0fa6 # ShowEQ 10/27/05 +OP_MOTD=0x024d # ShowEQ 10/27/05 +OP_SendLoginInfo=0x4dd0 # ShowEQ 10/27/05 +OP_DeleteCharacter=0x26c9 # ShowEQ 10/27/05 +OP_SendCharInfo=0x4513 # ShowEQ 10/27/05 +OP_ExpansionInfo=0x04ec # ShowEQ 10/27/05 +OP_CharacterCreate=0x10b2 # EQEmu 11/28/05 +OP_RandomNameGenerator=0x23d4 # EQEmu 11/28/05 +OP_GuildsList=0x6957 #same as zone guild list afaik +OP_ApproveName=0x3ea6 # EQEmu 11/28/05 +OP_EnterWorld=0x7cba # ShowEQ 10/27/05 +OP_PostEnterWorld=0x52A4 # EQEmu 06/29/05 +OP_World_Client_CRC1=0x5072 # ShowEQ 10/27/05 +OP_World_Client_CRC2=0x5b18 # ShowEQ 10/27/05 +OP_SetChatServer=0x00d7 # ShowEQ 10/27/05 +OP_SetChatServer2=0x6536 # ShowEQ 10/27/05 +OP_ZoneServerInfo=0x61b6 # ShowEQ 10/27/05 +OP_WorldComplete=0x509d # ShowEQ 10/27/05 +OP_ZoneUnavail=0x407C +OP_WorldClientReady=0x5e99 # EQEmu 11/28/05 (Guess - Doodman) +OP_WorldUnknown001=0x0000 # EQEmu 06/29/05 (New to 6/29) +OP_CharacterStillInZone=0x60fa # world->client. reject. +OP_WorldChecksumFailure=0x7D37 # world->client. reject. +OP_WorldLoginFailed=0x8DA7 # world->client. reject. +OP_WorldLogout=0x7718 # client->world +OP_WorldLevelTooHigh=0x583b # world->client. Cancels zone in. +OP_CharInacessable=0x436A # world->client. Cancels zone in. + +#Zone in opcodes +OP_ZoneEntry=0x7213 # ShowEQ 10/27/05 +OP_ZoneInUnknown=0x0000 +OP_AckPacket=0x7752 # ShowEQ 10/27/05 +OP_NewZone=0x0920 # ShowEQ 10/27/05 +OP_ReqClientSpawn=0x0322 # ShowEQ 10/27/05, may actually be 0x5966 +OP_ZoneSpawns=0x2e78 # ShowEQ 10/27/05 +OP_CharInventory=0x5394 # EQEmu 06/29/05 +OP_SetServerFilter=0x6563 # ShowEQ 10/27/05 +OP_LockoutTimerInfo=0x7f63 +OP_SendZonepoints=0x3eba # ShowEQ 10/27/05 +OP_SpawnDoor=0x4c24 # ShowEQ 10/27/05 +OP_ReqNewZone=0x7ac5 # ShowEQ 10/27/05 +OP_PlayerProfile=0x75df # ShowEQ 10/27/05 +OP_TimeOfDay=0x1580 # ShowEQ 10/27/05 +OP_ZoneServerReady=0x0000 #dosent exist in this version + + +OP_Logout=0x61ff +OP_LogoutReply=0x3cdc #0x48c2 #0x0F66 is not quite right.. this causes disconnect error... +OP_PreLogoutReply=0x711e #0 len packet sent during logout/zoning +OP_LevelUpdate=0x6d44 +OP_Stamina=0x7a83 # ShowEQ 10/27/05 + +#Petition Opcodes +OP_PetitionSearch=0x0000 #search term for petition +OP_PetitionSearchResults=0x0000 #(list of?) matches from search +OP_PetitionSearchText=0x0000 #text results of search +OP_Petition=0x251f +OP_PetitionUpdate=0x0000 #guess +OP_PetitionCheckout=0x0000 +OP_PetitionCheckIn=0x0000 +OP_PetitionQue=0x33c3 +OP_PetitionUnCheckout=0x0000 +OP_PetitionDelete=0x5692 +OP_DeletePetition=0x0000 +OP_PetitionResolve=0x0000 #0x0000 +OP_PDeletePetition=0x0000 +OP_PetitionBug=0x0000 +OP_PetitionRefresh=0x0000 +OP_PetitionCheckout2=0x0000 +OP_PetitionViewPetition=0x0000 + +#Guild Opcodes +#list to client : 0x0F4D, 0x147d, 0x18B7, 0x2ec9, 0x3230, 0x32CF, 0x461A, 0x4CC7 +# 0x6966, 0x712A, 0x754E, 0x7C32, 0x7C59 +# some from client: 0x7825 +OP_ZoneGuildList=0x6957 (one entry too long) # ShowEQ 10/27/05 +OP_GetGuildMOTD=0x7fec +OP_GetGuildMOTDReply=0x3246 +OP_GuildMemberList=0x147d # ShowEQ 10/27/05 +OP_GuildMemberUpdate=0x0f4d # ShowEQ 10/27/05 +OP_GuildMemberLevelUpdate=0x0000 #dosent exist in this version +OP_GuildRemove=0x0179 +OP_GuildPeace=0x215a +OP_GuildWar=0x0c81 +OP_GuildLeader=0x12b1 +OP_GuildDemote=0x4eb9 +OP_GuildMOTD=0x475a +OP_SetGuildMOTD=0x591c +OP_GetGuildsList=0x0000 +OP_GuildInvite=0x18b7 +OP_GuildPublicNote=0x17a2 # ShowEQ 10/27/05 +OP_GuildDelete=0x6cce +OP_GuildInviteAccept=0x61d0 +#OP_GuildManageRemove=0x0000 +#OP_GuildManageAdd=0x0000 +#OP_GuildManageStatus=0x0000 +OP_GuildManageBanker=0x3d1e +OP_GuildBank=0xb4ab +OP_SetGuildRank=0x6966 +OP_LFGuild=0x1858 + +#GM/guide opcodes +OP_GMServers=0x3387 #/servers +OP_GMBecomeNPC=0x7864 #/becomenpc +OP_GMZoneRequest=0x1306 #/zone +OP_GMSearchCorpse=0x3c32 #/searchcorpse +OP_GMHideMe=0x15b2 #/hideme +OP_GMGoto=0x1cee #/goto +OP_GMDelCorpse=0x0b2f #/delcorpse +OP_GMApproval=0x0c0f #/approval +OP_GMToggle=0x7fea #/toggletell +OP_GMZoneRequest2=0x244c +OP_GMSummon=0x1edc #/summon +OP_GMEmoteZone=0x39f2 #/emotezone +OP_GMEmoteWorld=0x3383 #/emoteworld (not implemented) +OP_GMFind=0x5930 #/find +OP_GMKick=0x692c #/kick +OP_GMNameChange=0x0000 + +OP_SafePoint=0x0000 +OP_Bind_Wound=0x601d +OP_GMTraining=0x238f +OP_GMEndTraining=0x613d +OP_GMTrainSkill=0x11d2 +# OP_GMEndTrainingResponse=0x0000 +OP_Animation=0x2acf # ShowEQ 10/27/05 +OP_Stun=0x1E51 +OP_MoneyUpdate=0x267c +OP_SendExpZonein=0x0587 # ShowEQ 10/27/05 +OP_IncreaseStats=0x5b7b +OP_CrashDump=0x7825 +OP_ReadBook=0x1496 +OP_Dye=0x00dd # ShowEQ 10/27/05 +OP_Consume=0x77d6 # ShowEQ 10/27/05 +OP_Begging=0x13e7 # ShowEQ 10/27/05 +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_Consent=0x1081 # ShowEQ 10/27/05 +OP_ConsentDeny=0x4e8c # ShowEQ 10/27/05 +OP_ConsentResponse=0x6380 # ShowEQ 10/27/05 +OP_LFGCommand=0x68ac # ShowEQ 10/27/05 +OP_LFGGetMatchesRequest=0x022f # ShowEQ 10/27/05 +OP_LFGAppearance=0x1a85 +OP_LFGResponse=0x0000 +OP_LFGGetMatchesResponse=0x45d0 # ShowEQ 10/27/05 +OP_LootItem=0x7081 # ShowEQ 10/27/05 +OP_Bug=0x7ac2 # ShowEQ 10/27/05 +OP_BoardBoat=0x4298 # ShowEQ 10/27/05 +OP_Save=0x736b # ShowEQ 10/27/05 +OP_Camp=0x78c1 # ShowEQ 10/27/05 +OP_EndLootRequest=0x2316 # ShowEQ 10/27/05 +OP_MemorizeSpell=0x308e # ShowEQ 10/27/05 +OP_SwapSpell=0x2126 # ShowEQ 10/27/05 +OP_CastSpell=0x304b # ShowEQ 10/27/05 +OP_DeleteSpell=0x4f37 +OP_LoadSpellSet=0x403e # ShowEQ 10/27/05 +OP_AutoAttack=0x5e55 # ShowEQ 10/27/05 +OP_AutoFire=0x6c53 +OP_Consider=0x65ca # ShowEQ 10/27/05 +OP_Emote=0x547a # ShowEQ 10/27/05 +OP_PetCommands=0x10a1 # ShowEQ 10/27/05 +OP_PetBuffWindow=0x4e31 +OP_SpawnAppearance=0x7c32 # ShowEQ 10/27/05 +OP_DeleteSpawn=0x55bc # ShowEQ 10/27/05 +OP_FormattedMessage=0x5a48 # ShowEQ 10/27/05 +OP_WhoAllRequest=0x5cdd # ShowEQ 10/27/05 +OP_WhoAllResponse=0x757b # ShowEQ 10/27/05 +OP_AutoAttack2=0x0701 # ShowEQ 10/27/05 +OP_SetRunMode=0x4aba # ShowEQ 10/27/05 +OP_SimpleMessage=0x673c # ShowEQ 10/27/05 +OP_SaveOnZoneReq=0x1540 # ShowEQ 10/27/05 +OP_SenseHeading=0x05ac # ShowEQ 10/27/05 +OP_Buff=0x6a53 # ShowEQ 10/27/05 +OP_LootComplete=0x0a94 # ShowEQ 10/27/05 +OP_EnvDamage=0x31b3 # ShowEQ 10/27/05 +OP_Split=0x4848 # ShowEQ 10/27/05 +OP_Surname=0x4668 # ShowEQ 10/27/05 +OP_ClearSurname=0x6cdb +OP_MoveItem=0x420f # ShowEQ 10/27/05 +OP_FaceChange=0x0f8e # ShowEQ 10/27/05 +OP_ItemPacket=0x3397 # ShowEQ 10/27/05 +OP_ItemLinkResponse=0x667c # ShowEQ 10/27/05 +OP_ClientReady=0x5e20 # ShowEQ 10/27/05 +OP_ZoneChange=0x5dd8 # ShowEQ 10/27/05 +OP_ItemLinkClick=0x53e5 # ShowEQ 10/27/05 +OP_Forage=0x4796 +OP_BazaarSearch=0x1ee9 # ShowEQ 10/27/05 +OP_NewSpawn=0x1860 # ShowEQ 10/27/05 +#a similar unknonw packet to NewSpawn: 0x12b2 +OP_WearChange=0x7441 # ShowEQ 10/27/05 +OP_Action=0x497c # ShowEQ 10/27/05 +OP_SpecialMesg=0x2372 # ShowEQ 10/27/05 +OP_Bazaar=0x0000 +OP_LeaveBoat=0x67c9 # ShowEQ 10/27/05 +OP_Weather=0x254d # ShowEQ 10/27/05 +OP_LFPGetMatchesRequest=0x35a6 # ShowEQ 10/27/05 +OP_Illusion=0x448d # ShowEQ 10/27/05 +OP_TargetReject=0x0000 +OP_TargetCommand=0x1477 +OP_TargetMouse=0x6c47 # ShowEQ 10/27/05 +OP_TargetHoTT=0x6a12 +OP_GMKill=0x6980 # ShowEQ 10/27/05 +OP_MoneyOnCorpse=0x7fe4 # ShowEQ 10/27/05 +OP_ClickDoor=0x043b # ShowEQ 10/27/05 +OP_MoveDoor=0x700d # ShowEQ 10/27/05 +OP_RemoveAllDoors=0x77d0 +OP_LootRequest=0x6f90 # ShowEQ 10/27/05 +OP_YellForHelp=0x61ef # ShowEQ 10/27/05 +OP_ManaChange=0x4839 # ShowEQ 10/27/05 +OP_LFPCommand=0x6f82 # ShowEQ 10/27/05 +OP_RandomReply=0x6cd5 # ShowEQ 10/27/05 +OP_DenyResponse=0x7c66 # ShowEQ 10/27/05 +OP_ConsiderCorpse=0x773f # ShowEQ 10/27/05 +OP_CorpseDrag=0x50c0 # +OP_CorpseDrop=0x7c7c # +OP_ConfirmDelete=0x3838 # ShowEQ 10/27/05 +OP_MobHealth=0x0695 # ShowEQ 10/27/05 +OP_SkillUpdate=0x6a93 # ShowEQ 10/27/05 +OP_RandomReq=0x5534 # ShowEQ 10/27/05 +OP_ClientUpdate=0x14cb # ShowEQ 10/27/05 +OP_Report=0x7f9d +OP_GroundSpawn=0x0f47 # ShowEQ 10/27/05 +OP_LFPGetMatchesResponse=0x06c5 +OP_Jump=0x0797 # ShowEQ 10/27/05 +OP_ExpUpdate=0x5ecd # ShowEQ 10/27/05 +OP_Death=0x6160 # ShowEQ 10/27/05 +OP_BecomeCorpse=0x4DBC +OP_GMLastName=0x23a1 # ShowEQ 10/27/05 +OP_InitialMobHealth=0x3d2d # ShowEQ 10/27/05 +OP_Mend=0x14ef # ShowEQ 10/27/05 +OP_MendHPUpdate=0x0000 +OP_Feedback=0x5306 # ShowEQ 10/27/05 +OP_TGB=0x0c11 # ShowEQ 10/27/05 +OP_InterruptCast=0x0b97 +OP_Damage=0x5c78 # ShowEQ 10/27/05 +OP_ChannelMessage=0x1004 # ShowEQ 10/27/05 +OP_LevelAppearance=0x358e +OP_MultiLineMsg=0x0000 +OP_Charm=0x12e5 +OP_ApproveZone=0x0000 +OP_Assist=0x7709 +OP_AssistGroup=0x5104 +OP_AugmentItem=0x539b +OP_BazaarInspect=0x0000 +OP_ClientError=0x0000 +OP_DeleteItem=0x4d81 +OP_DeleteCharge=0x1c4a +OP_ControlBoat=0x2c81 +OP_DumpName=0x0000 +OP_FeignDeath=0x7489 +OP_Heartbeat=0x0000 +OP_ItemName=0x0000 +OP_LDoNButton=0x13c8 +OP_MoveCoin=0x7657 +OP_ReloadUI=0x0000 +OP_ZonePlayerToBind=0x385e # FNW Discovered on Feb 9, 2007 +OP_Rewind=0x4cfa # Lieka 4/20/08: /rewind command +OP_Translocate=0x8258 +OP_Sacrifice=0x727a +OP_KeyRing=0x68c4 +OP_ApplyPoison=0x0c2c +OP_AugmentInfo=0x45ff #RealityIncarnate 4/28/09 +OP_SetStartCity=0x41dc #realityincarnate 6/25/09 +OP_SpellEffect=0x22C5 +OP_RemoveNimbusEffect=0x0000 +OP_CrystalReclaim=0x7cfe +OP_CrystalCreate=0x62c3 + +OP_DzQuit=0x486d +OP_DzListTimers=0x39aa +OP_DzAddPlayer=0x7fba +OP_DzRemovePlayer=0x540b +OP_DzSwapPlayer=0x794a +OP_DzMakeLeader=0x0ce9 +OP_DzPlayerList=0xada0 +OP_DzJoinExpeditionConfirm=0x3817 +OP_DzJoinExpeditionReply=0x5da9 +OP_DzExpeditionInfo=0x98e +OP_DzMemberStatus=0x1826 +OP_DzLeaderStatus=0x7abc +OP_DzExpeditionEndsWarning=0x1c3f +OP_DzExpeditionList=0x7c12 +OP_DzMemberList=0x9b6 +OP_DzCompass=0x28aa +OP_DzChooseZone=0x1022 +#0x330d is something but I'm not sure what yet. + +#bazaar trader stuff stuff: +#become and buy from +#Server->Client: [ Opcode: OP_Unknown (0x0000) Size: 8 ] +# 0: 46 01 00 00 39 01 00 00 | F...9... +OP_TraderDelItem=0x0da9 +OP_BecomeTrader=0x2844 +OP_TraderShop=0x35e8 +OP_TraderItemUpdate=0x0000 +OP_Trader=0x524e +OP_ShopItem=0x0000 +OP_TraderBuy=0x6dd8 # ShowEQ 10/27/05 +OP_Barter=0x7460 + +#pc/npc trading +OP_TradeRequest=0x372f +OP_TradeAcceptClick=0x0065 # ShowEQ 10/27/05 +OP_TradeRequestAck=0x4048 # ShowEQ 10/27/05 +OP_TradeCoins=0x34c1 #guess... +OP_FinishTrade=0x6014 +OP_CancelTrade=0x2dc1 # ShowEQ 10/27/05 +OP_TradeBusy=0x6839 # +OP_TradeMoneyUpdate=0x0000 #not sure + +#merchant crap +OP_ShopPlayerSell=0x0e13 # ShowEQ 10/27/05 +OP_ShopEnd=0x7e03 # ShowEQ 10/27/05 +OP_ShopEndConfirm=0x20b2 +OP_ShopPlayerBuy=0x221e +OP_ShopRequest=0x45f9 # ShowEQ 10/27/05 +OP_ShopDelItem=0x0da9 + +#tradeskill stuff: +#something 0x21ed (8) +#something post combine 0x5f4e (8) +OP_ClickObject=0x3bc2 # ShowEQ 10/27/05 +OP_ClickObjectAction=0x6937 +OP_ClearObject=0x21ed #was 0x711e +#0x711e of len 0 comes right after OP_ClickObjectAck from server +OP_RecipeDetails=0x4ea2 +OP_RecipesFavorite=0x23f0 +OP_RecipesSearch=0x164d +OP_RecipeReply=0x31f8 +OP_RecipeAutoCombine=0x0353 +OP_TradeSkillCombine=0x0b40 + +OP_RequestDuel=0x28e1 +OP_DuelResponse=0x3bad +OP_DuelResponse2=0x1b09 #when accepted + +OP_RezzComplete=0x4b05 +OP_RezzRequest=0x1035 +OP_RezzAnswer=0x6219 +OP_SafeFallSuccess=0x3b21 +OP_Shielding=0x3fe6 +OP_TestBuff=0x6ab0 #/testbuff +OP_Track=0x5d11 # ShowEQ 10/27/05 +OP_TrackTarget=0x7085 +OP_TrackUnknown=0x6177 #size 0 right after OP_Track + +#Tribute Packets: +OP_OpenGuildTributeMaster=0x0000 +OP_OpenTributeMaster=0x512e #open tribute master window +OP_OpenTributeReply=0x27B3 #reply to open request +OP_SelectTribute=0x625d #clicking on a tribute, and text reply +OP_TributeItem=0x6f6c #donating an item +OP_TributeMoney=0x27b3 #donating money +OP_TributeNPC=0x7f25 #seems to be missing now +OP_TributeToggle=0x2688 #activating/deactivating tribute +OP_TributeTimer=0x4665 #testing #a 4 byte tier update, 10 minutes for seconds +OP_TributePointUpdate=0x6463 #16 byte point packet +OP_TributeUpdate=0x5639 # ShowEQ 10/27/05 +OP_GuildTributeInfo=0x5e3d # ShowEQ 10/27/05 +OP_TributeInfo=0x152d +OP_SendGuildTributes=0x5e3a # request packet, 4 bytes +OP_SendTributes=0x067a # request packet, 4 bytes, migth be backwards +# 27b3 4665 + +#Adventure packets: +OP_LeaveAdventure=0x0c0d +OP_AdventureFinish=0x3906 +OP_AdventureInfoRequest=0x2aaf #right click adventure recruiter +OP_AdventureInfo=0x1db5 #text reply to right click +OP_AdventureRequest=0x43fd +OP_AdventureDetails=0x3f26 +OP_AdventureData=0x0677 +OP_AdventureUpdate=0x64ac +OP_AdventureMerchantRequest=0x0950 +OP_AdventureMerchantResponse=0x4416 +OP_AdventureMerchantPurchase=0x413d +OP_AdventureMerchantSell=0x0097 +OP_AdventurePointsUpdate=0x420a #not sure, followed purchase +OP_AdventureStatsRequest=0x5fc7 +OP_AdventureStatsReply=0x56cd +OP_AdventureLeaderboardRequest=0x230a +OP_AdventureLeaderboardReply=0x0d0f +# request stats: 0x5fc7, reply 0x56cd? +# request leaderboard: 0x230a?, reply 0x0d0f? + +#Group Opcodes +OP_GroupDisband=0x0e76 # ShowEQ 10/27/05 +OP_GroupInvite=0x1b48 # ShowEQ 10/27/05 +OP_GroupFollow=0x7bc7 # ShowEQ 10/27/05 +OP_GroupUpdate=0x2dd6 # ShowEQ 10/27/05 +OP_GroupAcknowledge=0x0000 +OP_GroupCancelInvite=0x1f27 # ShowEQ 10/27/05 +OP_GroupDelete=0x0000 +OP_GroupFollow2=0x0000 #used in conjunction with OP_GroupInvite2 +OP_GroupInvite2=0x12d6 #sometimes sent instead of OP_GroupInvite +OP_CancelInvite=0x0000 + +OP_RaidJoin=0x1f21 # ShowEQ 10/27/05 +OP_RaidInvite=0x5891 # ShowEQ 10/27/05 +OP_RaidUpdate=0x1f21 # EQEmu 06/29/05 + + +OP_ZoneComplete=0x0000 +OP_ItemLinkText=0x0000 +OP_DisciplineUpdate=0x7180 +OP_DisciplineTimer=0x53df +OP_LocInfo=0x0000 +OP_FindPersonRequest=0x3c41 # ShowEQ 10/27/05 +OP_FindPersonReply=0x5711 # ShowEQ 10/27/05 +OP_ForceFindPerson=0x0000 +OP_LoginComplete=0x0000 +OP_Sound=0x541e +#OP_Zone_MissingName01=0x0000 #remove on recompile +OP_MobRename=0x0498 # ShowEQ 10/27/05 +OP_BankerChange=0x6a5b + +#Button-push commands +OP_Taunt=0x5e48 +OP_CombatAbility=0x5ee8 +OP_SenseTraps=0x5666 # ShowEQ 10/27/05 +OP_PickPocket=0x2ad8 +OP_DisarmTraps=0x1241 +OP_Disarm=0x17d9 +OP_Hide=0x4312 +OP_Sneak=0x74e1 +OP_Fishing=0x0b36 +OP_InstillDoubt=0x389e #intimidation +OP_LDoNOpen=0x083b + +#Task packets +OP_TaskActivityComplete=0x54eb +OP_CompletedTasks=0x76a2 # ShowEQ 10/27/05 +OP_TaskDescription=0x5ef7 # ShowEQ 10/27/05 +OP_TaskActivity=0x682d # ShowEQ 10/27/05 +OP_TaskMemberList=0x722f #not sure +OP_OpenNewTasksWindow=0x5e7c #combined with OP_AvaliableTask I think +OP_AvaliableTask=0x0000 +OP_AcceptNewTask=0x207f +OP_TaskHistoryRequest=0x5df4 +OP_TaskHistoryReply=0x397d +OP_CancelTask=0x3ba8 +OP_DeclineAllTasks=0x0000 #not sure, 12 bytes +OP_TaskMemberInvite=0x79b4 +OP_TaskMemberInviteResponse=0x0358 +OP_TaskMemberChange=0x5886 +OP_TaskMakeLeader=0x1b25 +OP_TaskAddPlayer=0x6bc4 +OP_TaskRemovePlayer=0x37b9 +OP_TaskPlayerList=0x3961 +OP_TaskQuit=0x35dd +#task complete related: 0x0000 (24 bytes), 0x0000 (8 bytes), 0x0000 (4 bytes) + + +OP_RequestClientZoneChange=0x7834 # ShowEQ 10/27/05 + +OP_SendAATable=0x367d # ShowEQ 10/27/05 +OP_UpdateAA=0x5966 +OP_RespondAA=0x3af4 +OP_SendAAStats=0x5996 # ShowEQ 10/27/05 +OP_AAAction=0x0681 # ShowEQ 10/27/05 +OP_AAExpUpdate=0x5f58 # ShowEQ 10/27/05 + +OP_PurchaseLeadershipAA=0x17bf +OP_UpdateLeadershipAA=0x07f1 +OP_LeadershipExpUpdate=0x596e +OP_LeadershipExpToggle=0x5b37 +OP_MarkNPC=0x5483 +OP_ClearNPCMarks=0x3ef6 +OP_DoGroupLeadershipAbility=0x569e +OP_DelegateAbility=0x10f4 +OP_SetGroupTarget=0x3eec + +#The following 4 Opcodes are for SoF only: +OP_FinishWindow=0x0000 #Trevius 03/15/09 +OP_FinishWindow2=0x0000 #Trevius 03/15/09 +OP_ItemVerifyRequest=0x0000 #Trevius 03/15/09 +OP_ItemVerifyReply=0x0000 #Trevius 03/15/09 + +#discovered opcodes not yet used: +OP_PlayMP3=0x0000 +OP_FriendsWho=0x48fe +OP_MoveLogRequest=0x7510 #gone I think +OP_MoveLogDisregard=0x0000 #gone I think +OP_ReclaimCrystals=0x7cfe +OP_CrystalCountUpdate=0x0ce3 +OP_DynamicWall=0x0000 +OP_CustomTitles=0x2a28 # ShowEQ 10/27/05 +OP_NewTitlesAvailable=0x4eca # +OP_RequestTitles=0x5eba # +OP_SendTitleList=0x3e89 # EQEmu 06/29/05 +OP_SetTitle=0x1f22 # +OP_SetTitleReply=0x5eab # +OP_Bandolier=0x6f0c +OP_PotionBelt=0x0719 +OP_OpenDiscordMerchant=0x0000 #8 bytes +OP_DiscordMerchantInventory=0x0000 #long item packet +OP_GiveMoney=0x0000 #16 bytes, pp, gp, sp, cp. +OP_OnLevelMessage=0x1dde +OP_PopupResponse=0x3816 +OP_RequestKnowledgeBase=0x0000 +OP_KnowledgeBase=0x0000 +OP_SlashAdventure=0x571a # /adventure +OP_VetRewardsAvaliable=0x0557 +OP_VetClaimRequest=0x6ba0 +OP_VetClaimReply=0x0000 +OP_BecomePVPPrompt=0x36B2 #guessed from ASM +OP_PVPStats=0x5cc0 +OP_PVPLeaderBoardRequest=0x61d2 +OP_PVPLeaderBoardReply=0x1a59 +OP_PVPLeaderBoardDetailsRequest=0x06a2 +OP_PVPLeaderBoardDetailsReply=0x246a +OP_PickLockSuccess=0x40E7 +OP_WeaponEquip1=0x6c5e +OP_WeaponEquip2=0x63da +OP_WeaponUnequip2=0x381d +OP_VoiceMacroIn=0x2866 # Client to Server +OP_VoiceMacroOut=0x2ec6 # Server to Client +OP_CameraEffect=0x0937 # Correct + +#named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x729c +OP_Some6ByteHPUpdate=0x0000 #seems to happen when you target group members +OP_SomeItemPacketMaybe=0x4033 # EQEmu 06/29/05 +OP_QueryResponseThing=0x1974 +OP_FloatListThing=0x6a1b # EQEmu 06/29/05 + +#Login opcodes +OP_SessionReady=0x0001 +OP_Login=0x0002 +OP_ServerListRequest=0x0004 +OP_PlayEverquestRequest=0x000d +OP_PlayEverquestResponse=0x0021 +OP_ChatMessage=0x0016 +OP_LoginAccepted=0x0017 +OP_ServerListResponse=0x0018 +OP_Poll=0x0029 +OP_EnterChat=0x000f +OP_PollResponse=0x0011 + +#raw opcodes +OP_RAWSessionRequest=0x0000 +OP_RAWSessionResponse=0x0000 +OP_RAWCombined=0x0000 +OP_RAWSessionDisconnect=0x0000 +OP_RAWKeepAlive=0x0000 +OP_RAWSessionStatRequest=0x0000 +OP_RAWSessionStatResponse=0x0000 +OP_RAWPacket=0x0000 +OP_RAWFragment=0x0000 +OP_RAWOutOfOrderAck=0x0000 +OP_RAWAck=0x0000 +OP_RAWAppCombined=0x0000 +OP_RAWOutOfSession=0x0000 + +#mail opcodes +OP_Command=0x0000 +OP_MailboxHeader=0x0000 +OP_MailHeader=0x0000 +OP_MailBody=0x0000 +OP_NewMail=0x0000 +OP_SentConfirm=0x0000 + + +OP_MobUpdate=0x0000 #not used anymore, here for backwards compat + +#we need to document the differences between these packets to make identifying them easier +OP_MobHealth=0x0695 +OP_HPUpdate=0x3bcf # ShowEQ 10/27/05 +OP_Some3ByteHPUpdate=0x0000 #initial HP update for mobs +OP_InitialHPUpdate=0x0000 + +# Unmatched: OP_ItemPlayerPacket=5394 +# Unmatched: OP_GetGuildMOTD=6d5d +# Unmatched: OP_GuildTributeStatus=7584 diff --git a/utils/patches/patch_Underfoot.conf b/utils/patches/patch_Underfoot.conf new file mode 100644 index 000000000..e9cbedd57 --- /dev/null +++ b/utils/patches/patch_Underfoot.conf @@ -0,0 +1,655 @@ +# ShowEQ Import Notes: +# ZERO THE FILE first +# perl -pi -e 's/0x[0-9a-fA-F]{4}/0x0000/g' opcodes.conf +# Unknown Mapping: +# OP_Action2 -> OP_Damage +# OP_EnvDamage -> OP_Damage ---> might have been a one time mistake +# Name Differences: +# OP_CancelInvite -> OP_GroupCancelInvite +# OP_GMFind -> OP_FindPersonRequest +# OP_CommonMessage -> OP_ChannelMessage + +OP_Unknown=0x0000 +OP_ExploreUnknown=0x0000 # used for unknown explorer + +# V = Verified correct +# C = Most likely correct +# U = Unsure, but should be correct or close + +# world packets +# Required to reach Char Select: +OP_SendLoginInfo=0x13da # V +OP_ApproveWorld=0x86c7 # C +OP_LogServer=0x6f79 # C +OP_SendCharInfo=0x4200 # C +OP_ExpansionInfo=0x7e4d # C +OP_GuildsList=0x5b0b # C +OP_EnterWorld=0x51b9 # C +OP_PostEnterWorld=0x5d32 # C +OP_World_Client_CRC1=0x3a18 # C +OP_World_Client_CRC2=0x3e50 # C +OP_SendSpellChecksum=0x46d3 # C +OP_SendSkillCapsChecksum=0x040b # C + +# Character Select Related: +OP_DeleteCharacter=0x5ca5 # C +OP_CharacterCreateRequest=0x53a3 # C +OP_CharacterCreate=0x1b85 # C +OP_RandomNameGenerator=0x647a # C 0x440f +OP_ApproveName=0x4f1f # C + +OP_MOTD=0x7629 # C 0x2b59 +OP_SetChatServer=0x7d90 # C 0x0479 +OP_SetChatServer2=0x158f # C 0x158f +OP_ZoneServerInfo=0x1190 # C 0x41c0 +OP_WorldComplete=0x441c # C +OP_WorldUnknown001=0x6f9d # C 0x77b1 +OP_FloatListThing=0x61ba # V + +# Reasons for Disconnect: +OP_ZoneUnavail=0x3288 # C 0x1190 +OP_WorldClientReady=0x7d05 # C 0x4786 +OP_CharacterStillInZone=0x0000 # +OP_WorldChecksumFailure=0x0000 # +OP_WorldLoginFailed=0x0000 # +OP_WorldLogout=0x0000 # +OP_WorldLevelTooHigh=0x0000 # +OP_CharInacessable=0x0000 # +OP_UserCompInfo=0x0000 # +# OP_SendExeChecksum=0x0000 # +# OP_SendBaseDataChecksum=0x0000 # + +# Zone in opcodes +OP_AckPacket=0x3594 # V +OP_ZoneEntry=0x4b61 # V +OP_ReqNewZone=0x4118 # V +OP_NewZone=0x43ac # V +OP_ZoneSpawns=0x7114 # ? +OP_PlayerProfile=0x6022 # V +OP_TimeOfDay=0x6015 # V +OP_LevelUpdate=0x6a99 # V +OP_Stamina=0x3d86 # V +OP_RequestClientZoneChange=0x18ea # C +OP_ZoneChange=0x6d37 # C + +OP_LockoutTimerInfo=0x0000 # +OP_ZoneServerReady=0x0000 # +OP_ZoneInUnknown=0x0000 # +OP_LogoutReply=0x0000 # +OP_PreLogoutReply=0x0000 # + +# Required to fully log in +OP_SpawnAppearance=0x3e17 # V +OP_ChangeSize=0x6942 # +OP_TributeUpdate=0x684c # V +OP_TributeTimer=0x4895 # C +OP_TaskDescription=0x156c # C +OP_TaskActivity=0x31f3 # C +OP_CompletedTasks=0x687f # C +OP_Weather=0x4658 # V +OP_SendAATable=0x6ef9 # V +OP_UpdateAA=0x7bf6 # V +OP_RespondAA=0x1fbd # C 0x2bad +OP_ReqClientSpawn=0x69cd # V +OP_SpawnDoor=0x6f2b # V +OP_GroundSpawn=0x5c85 # V +OP_SendZonepoints=0x2370 # V +OP_SendAAStats=0x78b9 # C +OP_WorldObjectsSent=0x7b73 # V +OP_BlockedBuffs=0x05d5 # V +OP_RemoveBlockedBuffs=0x37c1 # V +OP_ClearBlockedBuffs=0x5570 # C +OP_SendExpZonein=0x47e7 # V +OP_SendTributes=0x6bfb # V +OP_TributeInfo=0x5a67 # V +OP_SendGuildTributes=0x4df0 # C 0x5a01 +OP_AAExpUpdate=0x4aa2 # V +OP_ExpUpdate=0x0555 # V +OP_HPUpdate=0x6145 # V +OP_ManaChange=0x569a # C +OP_TGB=0x42ef # C +OP_SpecialMesg=0x016c # V +OP_GuildMemberList=0x51bc # C +OP_GuildMOTD=0x5658 # V +OP_CharInventory=0x47ae # V +OP_WearChange=0x0400 # V +OP_ClientUpdate=0x7062 # V +OP_ClientReady=0x6cdc # V +OP_SetServerFilter=0x2d74 # V + +# Guild Opcodes +OP_GetGuildMOTD=0x1899 # C +OP_GetGuildMOTDReply=0x4a5c # C +OP_GuildMemberUpdate=0x0a53 # C +OP_GuildInvite=0x1a58 # C +OP_GuildRemove=0x3c02 # C +OP_GuildPeace=0x2bff # C +OP_SetGuildMOTD=0x053a # C +OP_GuildList=0x5b0b # C +OP_GuildWar=0x5408 # C +OP_GuildLeader=0x0598 # C +OP_GuildDelete=0x3f55 # C +OP_GuildInviteAccept=0x7b64 # C +OP_GuildDemote=0x457d # C +OP_GuildPublicNote=0x2dbd # C +OP_GuildManageBanker=0x1e4c # C +OP_GuildBank=0x0d8a # C +OP_SetGuildRank=0x4ffe # C +OP_GuildUpdateURLAndChannel=0x5232 # C +OP_GuildMemberLevelUpdate=0x0000 # +OP_ZoneGuildList=0x0000 # +OP_GetGuildsList=0x0000 # +OP_GuildStatus=0x28c8 # +OP_GuildCreate=0x192d # +OP_LFGuild=0x7e23 # +# OP_GuildManageRemove=0x0000 # +# OP_GuildManageAdd=0x0000 # +# OP_GuildManageStatus=0x0000 # + +# GM/guide opcodes +OP_GMServers=0x6989 # C +OP_GMBecomeNPC=0x56e7 # C +OP_GMZoneRequest=0x3fd2 # C +OP_GMZoneRequest2=0x538f # C +OP_GMGoto=0x5ebc # C +OP_GMSearchCorpse=0x5a81 # C +OP_GMHideMe=0x28ef # C +OP_GMDelCorpse=0x655c # C +OP_GMApproval=0x7312 # C +OP_GMToggle=0x097f # C 0x7566 +OP_GMSummon=0x712b # C +OP_GMEmoteZone=0x1ac1 # C +OP_GMEmoteWorld=0x2444 # C +OP_GMFind=0x6e27 # C +OP_GMKick=0x0402 # C +OP_GMKill=0x799c # C +OP_GMNameChange=0x0f48 # C +OP_GMLastName=0x7bfb # C + +OP_InspectAnswer=0x0c2b # C +OP_BeginCast=0x0d5a # C +OP_BuffFadeMsg=0x71bf # C +OP_ConsentResponse=0x0e87 # C +OP_MemorizeSpell=0x3887 # C +OP_SwapSpell=0x5805 # C +OP_CastSpell=0x50c2 # C +OP_Consider=0x3c2d # C +OP_FormattedMessage=0x3b52 # C +OP_SimpleMessage=0x1f4d # C 0x5448 +OP_Buff=0x0d1d # C +OP_Illusion=0x231f # C +OP_MoneyOnCorpse=0x4a83 # C +OP_RandomReply=0x6d5d # C +OP_DenyResponse=0x6129 # C +OP_SkillUpdate=0x7f01 # C +OP_GMTrainSkillConfirm=0x3190 # C +OP_RandomReq=0x139d # C +OP_Death=0x7f9e # C +OP_Bind_Wound=0x4b1a # C +OP_GMTraining=0x51fa # C +OP_GMEndTraining=0x5479 # C +OP_GMTrainSkill=0x2257 # C +OP_Animation=0x4a61 # Was 0x47d3 +OP_Begging=0x53f9 # C +OP_Consent=0x6bb9 # C +OP_ConsentDeny=0x4cd1 # C +OP_AutoFire=0x5db5 # C +OP_PetCommands=0x7706 # C +OP_DeleteSpell=0x0698 # C +OP_Surname=0x44ae # C +OP_ClearSurname=0x6705 # C +OP_FaceChange=0x37a7 # C +OP_SenseHeading=0x1b8a # C +OP_Action=0x0f14 # C +OP_ConsiderCorpse=0x0a18 # C +OP_HideCorpse=0x2d08 # C 0x1842 +OP_CorpseDrag=0x3331 # +OP_CorpseDrop=0x2e70 # +OP_Bug=0x2369 # C +OP_Feedback=0x7705 # C +OP_Report=0x50d0 # C +OP_Damage=0x631a # C or OP_Action2? +OP_ChannelMessage=0x2e79 # C +OP_Assist=0x35b1 # C +OP_AssistGroup=0x194f # C +OP_MoveCoin=0x6024 # C +OP_ZonePlayerToBind=0x382c # C +OP_KeyRing=0x5c06 # C +OP_WhoAllRequest=0x177a # C +OP_WhoAllResponse=0x6ffa # C +OP_FriendsWho=0x6275 # C +OP_ConfirmDelete=0x3edc # V +OP_Logout=0x224f # C +OP_Rewind=0x7d63 # C +OP_TargetCommand=0x756c # C Was 0x5f5e +OP_InspectRequest=0x7c94 # C +OP_Hide=0x3497 # C +OP_Jump=0x083b # C +OP_Camp=0x5f85 # C +OP_Emote=0x3164 # C +OP_SetRunMode=0x3d06 # C +OP_BankerChange=0x300a # C +OP_TargetMouse=0x5f5e # C 0x7bbb +OP_MobHealth=0x15de # C +OP_InitialMobHealth=0x5cb0 # C +OP_TargetHoTT=0x790c # C +OP_XTargetResponse=0x6eb5 # +OP_XTargetRequest=0x4750 # +OP_XTargetAutoAddHaters=0x1a28 # +OP_TargetBuffs=0x3f24 # C +OP_BuffCreate=0x2121 # V +OP_BuffRemoveRequest=0x4065 +OP_DeleteSpawn=0x58c5 # C +OP_AutoAttack=0x1df9 # C +OP_AutoAttack2=0x517b # C +OP_Consume=0x24c5 # V +OP_MoveItem=0x2641 # C +OP_DeleteItem=0x66e0 # C +OP_DeleteCharge=0x4ca1 # C +OP_ItemPacket=0x7b6e # C +OP_ItemLinkResponse=0x695c # C +OP_ItemLinkClick=0x3c66 # C +OP_NewSpawn=0x429b # C +OP_Track=0x709d # C +OP_TrackTarget=0x3f49 # C +OP_TrackUnknown=0x03e7 # C +OP_ClickDoor=0x6e97 # C +OP_MoveDoor=0x3154 # C +OP_RemoveAllDoors=0x6215 # C +OP_EnvDamage=0x2730 # C +OP_BoardBoat=0x7554 # C +OP_Forage=0x739b # C +OP_LeaveBoat=0x7286 # C +OP_ControlBoat=0x7ea8 # C +OP_SafeFallSuccess=0x6df7 # C +OP_RezzComplete=0x30a4 # C +OP_RezzRequest=0x32af # C +OP_RezzAnswer=0x2d41 # C +OP_Shielding=0x4675 # C +OP_RequestDuel=0x6cfe # C +OP_MobRename=0x0507 # C +OP_AugmentItem=0x7c87 # C +OP_WeaponEquip1=0x4572 # C +OP_WeaponEquip2=0x399b # C +OP_WeaponUnequip2=0x416b # C +OP_ApplyPoison=0x5cd3 # C +OP_Save=0x6618 # C +OP_TestBuff=0x3415 # C +OP_CustomTitles=0x6a7e # C +OP_Split=0x1418 # C +OP_YellForHelp=0x55a8 # C +OP_LoadSpellSet=0x6617 # C +OP_Bandolier=0x510c # C +OP_PotionBelt=0x0651 # C +OP_DuelResponse=0x41a6 # C +OP_DuelResponse2=0x6d60 # C +OP_SaveOnZoneReq=0x2913 # C +OP_ReadBook=0x465e # C +OP_Dye=0x2137 # C +OP_InterruptCast=0x7566 # C +OP_AAAction=0x2bad # C +OP_LeadershipExpToggle=0x5033 # C +OP_LeadershipExpUpdate=0x074f # C +OP_PurchaseLeadershipAA=0x5f55 # C +OP_UpdateLeadershipAA=0x77ed # C +OP_MarkNPC=0x3ec7 # C +OP_ClearNPCMarks=0x5c29 # C +OP_DoGroupLeadershipAbility=0x0068 # C +OP_GroupLeadershipAAUpdate=0x167b # C +OP_DelegateAbility=0x6e58 # C +OP_SetGroupTarget=0x6b9e # C +OP_Charm=0x1fd5 # C +OP_Stun=0x3d00 # C +OP_SendFindableNPCs=0x6193 # C +OP_FindPersonRequest=0x1e04 # C +OP_FindPersonReply=0x7cae # C +OP_Sound=0x737a # C +OP_PetBuffWindow=0x7b87 # C +OP_LevelAppearance=0x1bd4 # C +OP_Translocate=0x3d9c # C +OP_Sacrifice=0x301b # C +OP_PopupResponse=0x6d27 # C +OP_OnLevelMessage=0x24cb # C +OP_AugmentInfo=0x31b1 # C +OP_Petition=0x31d1 # C +OP_SomeItemPacketMaybe=0x2c27 # C +OP_PVPStats=0x0000 # +OP_PVPLeaderBoardRequest=0x4973 # C +OP_PVPLeaderBoardReply=0x3842 # C +OP_PVPLeaderBoardDetailsRequest=0x6c75 # C +OP_PVPLeaderBoardDetailsReply=0x7fd7 # C +OP_RestState=0x5d24 # C +OP_RespawnWindow=0x107f # C +OP_DisciplineTimer=0x047c # C +OP_LDoNButton=0x1031 # C +OP_SetStartCity=0x68f0 # C +OP_VoiceMacroIn=0x1524 # C +OP_VoiceMacroOut=0x1d99 # C +OP_ItemViewUnknown=0x4eb3 # C +OP_VetRewardsAvaliable=0x0baa # C Mispelled? +OP_VetClaimRequest=0x34f8 # C +OP_VetClaimReply=0x6a5d # C +OP_CrystalCountUpdate=0x3fc8 # C +OP_DisciplineUpdate=0x6ed3 # +OP_BecomeCorpse=0x0000 # +OP_Action2=0x0000 # C OP_Damage? +OP_MobUpdate=0x4656 # Same as OP_SpawnPositionUpdate +OP_NPCMoveUpdate=0x0f3e # +OP_CameraEffect=0x6b0e # V +OP_SpellEffect=0x57a3 # V +OP_RemoveNimbusEffect=0x2c77 # C +OP_AltCurrency=0x659e +OP_AltCurrencyMerchantRequest=0x214C +OP_AltCurrencyMerchantReply=0x4348 +OP_AltCurrencyPurchase=0x4ad7 +OP_AltCurrencySell=0x14cf +OP_AltCurrencySellSelection=0x322a +OP_AltCurrencyReclaim=0x365d +OP_CrystalReclaim=0x726e +OP_CrystalCreate=0x12f3 +OP_Untargetable=0x301d +OP_IncreaseStats=0x4acf +OP_Weblink=0x6840 # C +OP_InspectMessageUpdate=0x7fa1 # C +#OP_OpenInventory=0x0000 # Likely does not exist in UF -U +OP_OpenContainer=0x041a + +OP_DzQuit=0x1539 +OP_DzListTimers=0x21e9 +OP_DzAddPlayer=0x3657 +OP_DzRemovePlayer=0x054e +OP_DzSwapPlayer=0x4661 +OP_DzMakeLeader=0x226f +OP_DzPlayerList=0x74e4 +OP_DzJoinExpeditionConfirm=0x3c5e +OP_DzJoinExpeditionReply=0x1154 +OP_DzExpeditionInfo=0x1150 +OP_DzMemberStatus=0x2d17 +OP_DzLeaderStatus=0x2caf +OP_DzExpeditionEndsWarning=0x6ac2 +OP_DzExpeditionList=0x70d8 +OP_DzMemberList=0x15c4 +OP_DzCompass=0x01cb +OP_DzChooseZone=0x0000 + +# New Opcodes +OP_SpawnPositionUpdate=0x4656 # C +OP_ManaUpdate=0x0433 # C +OP_EnduranceUpdate=0x6b76 # C +OP_MobManaUpdate=0x7901 # C +OP_MobEnduranceUpdate=0x6c5f # C + +# Looting +OP_LootRequest=0x6ad7 # C +OP_EndLootRequest=0x6546 # C +OP_LootItem=0x5960 # C +OP_LootComplete=0x604d # C + +# bazaar trader stuff stuff: +OP_BazaarSearch=0x550f # C +OP_TraderDelItem=0x63c8 # C +OP_BecomeTrader=0x0a1d # C +OP_TraderShop=0x2881 # C +OP_Trader=0x0c08 # C +OP_TraderBuy=0x3672 # C +OP_Barter=0x6db5 # C +OP_ShopItem=0x0000 # +OP_BazaarInspect=0x0000 # +OP_Bazaar=0x0000 # +OP_TraderItemUpdate=0x0000 # + +# pc/npc trading +OP_TradeRequest=0x7113 # C +OP_TradeAcceptClick=0x064a # C +OP_TradeRequestAck=0x606a # C +OP_TradeCoins=0x0149 # C +OP_FinishTrade=0x3ff6 # C +OP_CancelTrade=0x527e # C +OP_TradeMoneyUpdate=0x2a6d # C +OP_MoneyUpdate=0xd677 # C +OP_TradeBusy=0x5ed3 # C + +# Sent after canceling trade or after closing tradeskill object +OP_FinishWindow=0x3c27 # C +OP_FinishWindow2=0x6759 # C + +# Sent on Live for what seems to be item existance verification +# Ex. Before Right Click Effect happens from items +OP_ItemVerifyRequest=0x101e # C +OP_ItemVerifyReply=0x21c7 # C + +# merchant crap +OP_ShopPlayerSell=0x0b27 # C +OP_ShopRequest=0x442a # C +OP_ShopEnd=0x3753 # C +OP_ShopEndConfirm=0x4578 # C +OP_ShopPlayerBuy=0x436a # C +OP_ShopDelItem=0x63c8 # C + +# tradeskill stuff: +OP_ClickObject=0x33e5 # V +OP_ClickObjectAction=0x41b5 # V +OP_ClearObject=0x71d1 # C +OP_RecipeDetails=0x58d9 # C +OP_RecipesFavorite=0x7770 # C +OP_RecipesSearch=0x6948 # C +OP_RecipeReply=0x521c # C +OP_RecipeAutoCombine=0x0322 # C +OP_TradeSkillCombine=0x4212 # C + +# Tribute Packets: +OP_OpenGuildTributeMaster=0x5e79 # C +OP_OpenTributeMaster=0x7c24 # C +OP_SelectTribute=0x0c98 # C +OP_TributeItem=0x0b89 # C +OP_TributeMoney=0x314f # C +OP_TributeToggle=0x6dc3 # C +OP_TributePointUpdate=0x15a7 # C +OP_TributeNPC=0x0000 # +OP_GuildTributeInfo=0x0000 # +OP_OpenTributeReply=0x0000 # +# OP_GuildTributeStatus=0x0000 # + +# Adventure packets: +OP_LeaveAdventure=0x3ed4 # C +OP_AdventureFinish=0x6acc # C +OP_AdventureInfoRequest=0x3541 # C +OP_AdventureInfo=0x5cea # C +OP_AdventureRequest=0x2c03 # C +OP_AdventureDetails=0x1d40 # C +OP_AdventureData=0x34f2 # C +OP_AdventureUpdate=0x771f # C +OP_AdventureMerchantRequest=0x4e22 # C +OP_AdventureMerchantResponse=0x4dd5 # C +OP_AdventureMerchantPurchase=0x7b7f # C +OP_AdventureMerchantSell=0x179d # C +OP_AdventurePointsUpdate=0x7537 # C +OP_AdventureStatsRequest=0x4786 # C +OP_AdventureStatsReply=0x38b0 # C +OP_AdventureLeaderboardRequest=0x4cc6 # C +OP_AdventureLeaderboardReply=0x4423 # C + +# Group Opcodes +OP_GroupDisband=0x54e8 # C +OP_GroupInvite=0x4f60 # C +OP_GroupFollow=0x7f2b # C +OP_GroupUpdate=0x5331 # C +OP_GroupUpdateB=0x0786 # C +OP_GroupCancelInvite=0x2736 # C - Same as OP_CancelInvite? +OP_GroupAcknowledge=0x3e22 # C +OP_GroupDelete=0x58e6 # +OP_CancelInvite=0x2736 # C +OP_GroupFollow2=0x6c16 # C +OP_GroupInvite2=0x5251 # C +OP_GroupDisbandYou=0x0bd0 # C +OP_GroupDisbandOther=0x49f6 # C +OP_GroupLeaderChange=0x0c33 # C +OP_GroupRoles=0x116d # C +OP_GroupMakeLeader=0x5851 + +# LFG/LFP Opcodes +OP_LFGCommand=0x2c38 # C +OP_LFGGetMatchesRequest=0x28d4 # C +OP_LFGGetMatchesResponse=0x7a16 # C +OP_LFPGetMatchesRequest=0x189e # C +OP_LFPGetMatchesResponse=0x589f # C +OP_LFPCommand=0x7429 # C +OP_LFGAppearance=0x0000 # +OP_LFGResponse=0x0000 # + +# Raid Opcodes +OP_RaidInvite=0x60b5 # C +OP_RaidUpdate=0x4d8b # C +OP_RaidJoin=0x0000 # + +# Button-push commands +OP_Taunt=0x30e2 # C +OP_CombatAbility=0x36f8 # C +OP_SenseTraps=0x7e45 # C +OP_PickPocket=0x5821 # C +OP_DisarmTraps=0x0000 # +OP_Disarm=0x6def # C +OP_Sneak=0x1d22 # C +OP_Fishing=0x7093 # C +OP_InstillDoubt=0x221a # C +OP_FeignDeath=0x002b # C +OP_Mend=0x10a6 # C +OP_LDoNOpen=0x032b # C + +# Task packets +OP_TaskActivityComplete=0x5832 # C +OP_TaskMemberList=0x66ba # C +OP_OpenNewTasksWindow=0x98f6 # C +OP_AvaliableTask=0x6255 # C Mispelled? +OP_AcceptNewTask=0x17d5 # C +OP_TaskHistoryRequest=0x547c # C +OP_TaskHistoryReply=0x4524 # C +OP_CancelTask=0x3bf5 # C +OP_DeclineAllTasks=0x0000 # + +# Title opcodes +OP_NewTitlesAvailable=0x4b49 # C +OP_RequestTitles=0x4d3e # C +OP_SendTitleList=0x0d96 # C +OP_SetTitle=0x675c # C +OP_SetTitleReply=0x75f5 # C + +# Mercenary Opcodes +OP_MercenaryDataRequest=0x3015 # +OP_MercenaryDataResponse=0x0eaa # +OP_MercenaryHire=0x099e # +OP_MercenaryTimer=0x0cae # +OP_MercenaryAssign=0x2538 # +OP_MercenaryUnknown1=0x367f # +OP_MercenaryDataUpdate=0x57f2 # +OP_MercenaryCommand=0x50c1 # +OP_MercenarySuspendRequest=0x3c58 # +OP_MercenarySuspendResponse=0x4b82 # +OP_MercenaryUnsuspendResponse=0x5fe3 +OP_MercenaryDataUpdateRequest=0x05f1 +OP_MercenaryDismiss=0x319a # +OP_MercenaryTimerRequest=0x184e # + +# mail opcodes +OP_Command=0x0000 # +OP_MailboxHeader=0x0000 # +OP_MailHeader=0x0000 # +OP_MailBody=0x0000 # +OP_NewMail=0x0000 # +OP_SentConfirm=0x0000 # + +# # # # # # # # # # # Below this point should not be needed # # # # # # # # # # # + +# This section are all unknown in Titanium +OP_ForceFindPerson=0x0000 # +OP_LocInfo=0x0000 # +OP_ReloadUI=0x0000 # +OP_ItemName=0x0000 # +OP_ItemLinkText=0x0000 # +OP_MultiLineMsg=0x0000 # +OP_MendHPUpdate=0x0000 # +OP_TargetReject=0x0000 # +OP_SafePoint=0x0000 # +OP_ApproveZone=0x0000 # +OP_ZoneComplete=0x0000 # +OP_ClientError=0x0000 # +OP_DumpName=0x0000 # +OP_Heartbeat=0x0000 # +OP_CrashDump=0x0000 # +OP_LoginComplete=0x0000 # + +# discovered opcodes not yet used: +OP_PickLockSuccess=0x0000 # +OP_PlayMP3=0x0000 # +OP_ReclaimCrystals=0x0000 # +OP_DynamicWall=0x0000 # +OP_OpenDiscordMerchant=0x0000 # +OP_DiscordMerchantInventory=0x0000 # +OP_GiveMoney=0x0000 # +OP_RequestKnowledgeBase=0x0000 # +OP_KnowledgeBase=0x0000 # +OP_SlashAdventure=0x0000 # /adventure +OP_BecomePVPPrompt=0x0000 # +OP_MoveLogRequest=0x0000 # gone I think +OP_MoveLogDisregard=0x0000 # gone I think + +# named unknowns, to make looking for real unknown easier +OP_AnnoyingZoneUnknown=0x0000 # +OP_Some6ByteHPUpdate=0x0000 # seems to happen when you target group members +OP_QueryResponseThing=0x0000 # + + +# realityincarnate: these are just here to stop annoying several thousand byte packet dumps +#OP_LoginUnknown1=0x46d3 # U OP_SendSpellChecksum +#OP_LoginUnknown2=0x040b # U OP_SendSkillCapsChecksum + +# Petition Opcodes +OP_PetitionSearch=0x0000 # search term for petition +OP_PetitionSearchResults=0x0000 # (list of?) matches from search +OP_PetitionSearchText=0x0000 # text results of search + +OP_PetitionUpdate=0x0000 # +OP_PetitionCheckout=0x0000 # +OP_PetitionCheckIn=0x0000 # +OP_PetitionQue=0x0000 # +OP_PetitionUnCheckout=0x0000 # +OP_PetitionDelete=0x0000 # +OP_DeletePetition=0x0000 # +OP_PetitionResolve=0x0000 # +OP_PDeletePetition=0x0000 # +OP_PetitionBug=0x0000 # +OP_PetitionRefresh=0x0000 # +OP_PetitionCheckout2=0x0000 # +OP_PetitionViewPetition=0x0000 # + +# Login opcodes +OP_SessionReady=0x0001 # +OP_Login=0x0002 # +OP_ServerListRequest=0x0004 # +OP_PlayEverquestRequest=0x000d # +OP_PlayEverquestResponse=0x0021 # +OP_ChatMessage=0x0016 # +OP_LoginAccepted=0x0017 # +OP_ServerListResponse=0x0018 # +OP_Poll=0x0029 # +OP_EnterChat=0x000f # +OP_PollResponse=0x0011 # + +# raw opcodes +OP_RAWSessionRequest=0x0000 # +OP_RAWSessionResponse=0x0000 # +OP_RAWCombined=0x0000 # +OP_RAWSessionDisconnect=0x0000 # +OP_RAWKeepAlive=0x0000 # +OP_RAWSessionStatRequest=0x0000 # +OP_RAWSessionStatResponse=0x0000 # +OP_RAWPacket=0x0000 # +OP_RAWFragment=0x0000 # +OP_RAWOutOfOrderAck=0x0000 # +OP_RAWAck=0x0000 # +OP_RAWAppCombined=0x0000 # +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 # diff --git a/utils/pfs_list/CMakeLists.txt b/utils/pfs_list/CMakeLists.txt new file mode 100644 index 000000000..063cf5079 --- /dev/null +++ b/utils/pfs_list/CMakeLists.txt @@ -0,0 +1,20 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +PROJECT(PFSUtil) + +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE "Debug") +ENDIF(NOT CMAKE_BUILD_TYPE) + +IF(WIN32) + ADD_DEFINITIONS(/D _CRT_SECURE_NO_WARNINGS) + ADD_DEFINITIONS(/WX) +ENDIF(WIN32) + +SET(ZLIB_ROOT, "${CMAKE_CURRENT_SOURCE_DIR}/zlib") +FIND_PACKAGE(ZLIB REQUIRED) +INCLUDE_DIRECTORIES("${ZLIB_INCLUDE_DIRS}") + + +ADD_SUBDIRECTORY(Common) +ADD_SUBDIRECTORY(PFSList) diff --git a/utils/pfs_list/Common/CMakeLists.txt b/utils/pfs_list/Common/CMakeLists.txt new file mode 100644 index 000000000..250862e49 --- /dev/null +++ b/utils/pfs_list/Common/CMakeLists.txt @@ -0,0 +1,17 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +SET(common_sources + Source/PFSArchive.cpp + Source/Compression.cpp +) + +SET(common_headers + Include/Archive.h + Include/PFSArchive.h + Include/PFSDataStructs.h + Include/Compression.h +) + +INCLUDE_DIRECTORIES(Include) + +ADD_LIBRARY(Common ${common_sources} ${common_headers}) diff --git a/utils/pfs_list/Common/Include/Archive.h b/utils/pfs_list/Common/Include/Archive.h new file mode 100644 index 000000000..a67d85b92 --- /dev/null +++ b/utils/pfs_list/Common/Include/Archive.h @@ -0,0 +1,20 @@ +#ifndef __AZONE_COMMON_ARCHIVE_H +#define __AZONE_COMMON_ARCHIVE_H + +#include +#include +#include + +class Archive { +public: + Archive() { } + virtual ~Archive() { } + + virtual bool Open(std::string filename) = 0; + virtual bool Close() = 0; + virtual bool Get(std::string filename, char** buffer, size_t& buffer_size) = 0; + virtual bool Exists(std::string filename) = 0; + virtual bool GetFiles(std::string ext, std::list& files) = 0; +}; + +#endif diff --git a/utils/pfs_list/Common/Include/Compression.h b/utils/pfs_list/Common/Include/Compression.h new file mode 100644 index 000000000..647d17562 --- /dev/null +++ b/utils/pfs_list/Common/Include/Compression.h @@ -0,0 +1,9 @@ +#ifndef __AZONE_COMMON_COMPRESSION_H +#define __AZONE_COMMON_COMPRESSION_H + +#include +#include + +void decompress(const char* in, size_t in_len, char* out, size_t out_len); + +#endif diff --git a/utils/pfs_list/Common/Include/PFSArchive.h b/utils/pfs_list/Common/Include/PFSArchive.h new file mode 100644 index 000000000..503cb4619 --- /dev/null +++ b/utils/pfs_list/Common/Include/PFSArchive.h @@ -0,0 +1,35 @@ +#ifndef __AZONE_COMMON_PFSARCHIVE_H +#define __AZONE_COMMON_PFSARCHIVE_H + +#include +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif +#include "Archive.h" +#include "PFSDataStructs.h" +#include "Compression.h" + +class PFSArchive : public Archive { +public: + PFSArchive(); + virtual ~PFSArchive(); + + virtual bool Open(std::string filename); + virtual bool Close(); + virtual bool Get(std::string filename, char **buffer, size_t& buffer_size); + virtual bool Exists(std::string filename); + virtual bool GetFiles(std::string ext, std::list& files); +private: + bool ReadIntoBuffer(std::string filename); + std::vector _filenames; + std::vector _files; + char* _buffer; + size_t _buffer_size; +}; + +#endif diff --git a/utils/pfs_list/Common/Include/PFSDataStructs.h b/utils/pfs_list/Common/Include/PFSDataStructs.h new file mode 100644 index 000000000..4492a108e --- /dev/null +++ b/utils/pfs_list/Common/Include/PFSDataStructs.h @@ -0,0 +1,36 @@ +#ifndef __AZONE_COMMON_PFSDATASTRUCTS_H +#define __AZONE_COMMON_PFSDATASTRUCTS_H + +#include + +#pragma pack(1) + +struct PFSHeader { + uint32_t offset; + char magic[4]; + uint32_t unknown; +}; + +struct PFSDirectoryHeader { + uint32_t count; +}; + +struct PFSDirectory { + uint32_t crc, offset, size; +}; + +struct PFSDataBlock { + uint32_t deflate_length, inflate_length; +}; + +struct PFSFilenameHeader { + uint32_t filename_count; +}; + +struct PFSFilenameEntry { + uint32_t filename_length; +}; + +#pragma pack() + +#endif diff --git a/utils/pfs_list/Common/Source/Compression.cpp b/utils/pfs_list/Common/Source/Compression.cpp new file mode 100644 index 000000000..e18827518 --- /dev/null +++ b/utils/pfs_list/Common/Source/Compression.cpp @@ -0,0 +1,19 @@ +#include "Compression.h" + +void decompress(const char* in, size_t in_len, char* out, size_t out_len) { + int status; + z_stream d_stream; + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = (Bytef*)in; + d_stream.avail_in = in_len; + d_stream.next_out = (Bytef*)out; + d_stream.avail_out = out_len; + + inflateInit(&d_stream); + status = inflate(&d_stream, Z_NO_FLUSH); + inflateEnd(&d_stream); +} diff --git a/utils/pfs_list/Common/Source/PFSArchive.cpp b/utils/pfs_list/Common/Source/PFSArchive.cpp new file mode 100644 index 000000000..9fb479fa4 --- /dev/null +++ b/utils/pfs_list/Common/Source/PFSArchive.cpp @@ -0,0 +1,220 @@ +#include "PFSArchive.h" + +#define BufferRead(x, y) x = (y*)&_buffer[position]; position += sizeof(y); +#define BufferReadLength(x, y) memcpy(x, &_buffer[position], y); position += y; +#define MAX_FILENAME_SIZE 1024 + +PFSArchive::PFSArchive() +:_buffer(NULL), _buffer_size(0) +{ +} + +PFSArchive::~PFSArchive() { + Close(); +} + +bool PFSArchive::Open(std::string filename) +{ + if(!ReadIntoBuffer(filename)) { + Close(); + return false; + } + + PFSHeader *header = NULL; + PFSDirectoryHeader *directory_header = NULL; + PFSDirectory *directory = NULL; + PFSDataBlock *data_block = NULL; + PFSFilenameHeader *filename_header = NULL; + PFSFilenameEntry *filename_entry = NULL; + size_t position = 0; + + BufferRead(header, PFSHeader); + + if(header->magic[0] != 'P' || + header->magic[1] != 'F' || + header->magic[2] != 'S' || + header->magic[3] != ' ') + { + Close(); + return false; + } + + position = header->offset; + BufferRead(directory_header, PFSDirectoryHeader); + + std::vector offsets(directory_header->count, 0); + _filenames.resize(directory_header->count); + _files.resize(directory_header->count); + + size_t i = 0; + size_t j = 0; + size_t running = 0; + size_t temp_position = 0; + size_t inflate = 0; + char temp_buffer[32768]; + char temp_buffer2[32768]; + char temp_string[MAX_FILENAME_SIZE]; + for(; i < directory_header->count; ++i) { + BufferRead(directory, PFSDirectory); + if(directory->crc == ntohl(0xC90A5861)) { + temp_position = position; + position = directory->offset; + memset(temp_buffer, 0, directory->size); + inflate = 0; + + while(inflate < directory->size) { + BufferRead(data_block, PFSDataBlock); + BufferReadLength(temp_buffer2, data_block->deflate_length); + decompress(temp_buffer2, data_block->deflate_length, temp_buffer + inflate, data_block->inflate_length); + inflate += data_block->inflate_length; + } + + position = temp_position; + filename_header = (PFSFilenameHeader*)&temp_buffer[0]; + temp_position = sizeof(PFSFilenameHeader); + + for(j = 0; j < filename_header->filename_count; ++j) + { + filename_entry = (PFSFilenameEntry*)&temp_buffer[temp_position]; + if(filename_entry->filename_length + 1 >= MAX_FILENAME_SIZE) { + Close(); + return false; + } + temp_string[filename_entry->filename_length] = 0; + memcpy(temp_string, &temp_buffer[temp_position + sizeof(PFSFilenameEntry)], filename_entry->filename_length); + _filenames[j] = temp_string; + temp_position += sizeof(PFSFilenameEntry) + filename_entry->filename_length; + } + } else { + _files[running] = position - 12; + offsets[running] = directory->offset; + ++running; + } + } + + uint32_t temp = 0; + for(i = directory_header->count - 2; i > 0; i--) { + for(j = 0; j < i; j++) { + if(offsets[j] > offsets[j + 1]) { + temp = offsets[j]; + offsets[j] = offsets[j + 1]; + offsets[j + 1] = temp; + temp = _files[j]; + _files[j] = _files[j + 1]; + _files[j + 1] = temp; + } + } + } + + return true; +} + +bool PFSArchive::Close() +{ + if(_buffer) { + delete[] _buffer; + _buffer = 0; + _buffer_size = 0; + _filenames.resize(0); + _files.resize(0); + return true; + } + return false; +} + +//I would love to get rid of the allocations in the while loop below but sadly +//I can't predict the size of data blocks well enough to be able to and feel +//comfortable with it (I could however reduce the number of times I need to +//reallocate with a little clever logic which i think I will do +bool PFSArchive::Get(std::string filename, char **buffer, size_t& buffer_size) { + size_t sz = _filenames.size(); + for(size_t index = 0; index < sz; ++index) { + if(!_filenames[index].compare(filename)) { + PFSDirectory* directory = NULL; + PFSDataBlock* data_block = NULL; + char *temp = NULL; + + size_t position = _files[index]; + BufferRead(directory, PFSDirectory); + position = directory->offset; + + *buffer = new char[directory->size]; + buffer_size = directory->size; + + size_t inflate = 0; + while(inflate < directory->size) { + BufferRead(data_block, PFSDataBlock); + temp = new char[data_block->deflate_length]; + + memcpy(temp, &_buffer[position], data_block->deflate_length); + position += data_block->deflate_length; + + decompress(temp, data_block->deflate_length, *buffer + inflate, data_block->inflate_length); + delete[] temp; + inflate += data_block->inflate_length; + } + buffer_size = inflate; + return true; + } + } + + return false; +} + +bool PFSArchive::Exists(std::string filename) { + size_t count = _filenames.size(); + for(size_t i = 0; i < count; ++i) { + if(!_filenames[i].compare(filename.c_str())) + return true; + } + return false; +} + +bool PFSArchive::GetFiles(std::string ext, std::list& files) { + int elen = ext.length(); + bool all_files = !ext.compare("*"); + files.clear(); + + size_t count = _filenames.size(); + for(size_t i = 0; i < count; ++i) { + int flen = _filenames[i].length(); + if(flen <= elen) + continue; + + if(!strcmp(_filenames[i].c_str() + (flen - elen), ext.c_str()) || all_files) + files.push_back(_filenames[i]); + } + + return files.size() > 0; +} + +bool PFSArchive::ReadIntoBuffer(std::string filename) { + FILE* f = fopen(filename.c_str(), "rb"); + if(!f) { + return false; + } + + fseek(f, 0, SEEK_END); + size_t total_size = ftell(f); + rewind(f); + + if(!total_size) { + fclose(f); + return false; + } + + if(_buffer) { + delete[] _buffer; + } + + _buffer = new char[total_size]; + size_t bytes_read = fread(_buffer, 1, total_size, f); + + if(bytes_read != total_size) { + return false; + } + + _buffer_size = total_size; + fclose(f); + return true; +} diff --git a/utils/pfs_list/PFSList/CMakeLists.txt b/utils/pfs_list/PFSList/CMakeLists.txt new file mode 100644 index 000000000..9a36798f2 --- /dev/null +++ b/utils/pfs_list/PFSList/CMakeLists.txt @@ -0,0 +1,23 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) + +SET(pfslist_sources + Source/main.cpp +) + +SET(pfslist_headers +) + +INCLUDE_DIRECTORIES(Include + ../Common/Include +) + +ADD_EXECUTABLE(PFSList ${pfslist_sources} ${pfslist_headers}) + +TARGET_LINK_LIBRARIES(PFSList Common ${ZLIB_LIBRARY} "Ws2_32.lib") + +IF(MSVC) + ADD_DEFINITIONS(/D _CONSOLE) + SET_TARGET_PROPERTIES(PFSList PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF") +ENDIF(MSVC) + +SET(EXECUTABLE_OUTPUT_PATH ../Build/PFSList) diff --git a/utils/pfs_list/PFSList/Source/main.cpp b/utils/pfs_list/PFSList/Source/main.cpp new file mode 100644 index 000000000..5a5374409 --- /dev/null +++ b/utils/pfs_list/PFSList/Source/main.cpp @@ -0,0 +1,35 @@ +#include +#include +#include "PFSArchive.h" + +int main(int argc, char **argv) { + if(argc < 3) { + printf("Usage: %s ext file1 [file2...]\n", argv[0]); + return 1; + } + + for(int i = 2; i < argc; ++i) { + PFSArchive archive; + if(!archive.Open(argv[i])) { + printf("Error: couldn't open %s\n", argv[i]); + continue; + } + + std::list files; + if(!archive.GetFiles(argv[1], files)) { + printf("Error: couldn't return the files with ext: %s\n", argv[1]); + continue; + } + + printf("Files in %s:\n", argv[i]); + std::list::const_iterator iter = files.begin(); + while(iter != files.end()) { + printf("%s\n", (*iter).c_str()); + iter++; + } + printf("\n"); + + archive.Close(); + } + return 0; +} diff --git a/utils/player_profile_set/Readme.txt b/utils/player_profile_set/Readme.txt new file mode 100644 index 000000000..663de2f5c --- /dev/null +++ b/utils/player_profile_set/Readme.txt @@ -0,0 +1 @@ +Simple utility to modify fields in player profile... far from complete. Right now just sets ldon points to 0 but going to expand it. \ No newline at end of file diff --git a/utils/player_profile_set/bin/database.ini b/utils/player_profile_set/bin/database.ini new file mode 100644 index 000000000..76b62c661 --- /dev/null +++ b/utils/player_profile_set/bin/database.ini @@ -0,0 +1,4 @@ +host=localhost +user=root +password=pass +db=eq \ No newline at end of file diff --git a/utils/player_profile_set/player_profile_set.sln b/utils/player_profile_set/player_profile_set.sln new file mode 100644 index 000000000..9ccf77ddb --- /dev/null +++ b/utils/player_profile_set/player_profile_set.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "player_profile_set", "player_profile_set\player_profile_set.vcproj", "{3307DA97-CDDB-4C89-AAE7-BC0AF03D3D6D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3307DA97-CDDB-4C89-AAE7-BC0AF03D3D6D}.Debug|Win32.ActiveCfg = Debug|Win32 + {3307DA97-CDDB-4C89-AAE7-BC0AF03D3D6D}.Debug|Win32.Build.0 = Debug|Win32 + {3307DA97-CDDB-4C89-AAE7-BC0AF03D3D6D}.Release|Win32.ActiveCfg = Release|Win32 + {3307DA97-CDDB-4C89-AAE7-BC0AF03D3D6D}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/utils/player_profile_set/player_profile_set/MiscFunctions.cpp b/utils/player_profile_set/player_profile_set/MiscFunctions.cpp new file mode 100644 index 000000000..cdf8808fd --- /dev/null +++ b/utils/player_profile_set/player_profile_set/MiscFunctions.cpp @@ -0,0 +1,331 @@ +#include "MiscFunctions.h" +#include +#include +#include +#ifndef WIN32 +#include +#include +#endif +#include +#include +#ifdef WIN32 + #include +#endif + +using namespace std; + +#ifdef WIN32 + #include + + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#else + #include + #include + #include + #include + #include +#ifdef FREEBSD //Timothy Whitman - January 7, 2003 + #include + #include + #endif + #include + #include + #include + #include +#endif + +char* strn0cpy(char* dest, const char* source, int32 size) { + if (!dest) + return 0; + if (size == 0 || source == 0) { + dest[0] = 0; + return dest; + } + strncpy(dest, source, size); + dest[size - 1] = 0; + return dest; +} + +// String N w/null Copy Truncated? +// return value =true if entire string(source) fit, false if it was truncated +bool strn0cpyt(char* dest, const char* source, int32 size) { + if (!dest) + return 0; + if (size == 0 || source == 0) { + dest[0] = 0; + return dest; + } + strncpy(dest, source, size); + dest[size - 1] = 0; + return (bool) (source[strlen(dest)] == 0); +} + +const char *MakeUpperString(const char *source) { + static char str[128]; + if (!source) + return NULL; + MakeUpperString(source, str); + return str; +} + +void MakeUpperString(const char *source, char *target) { + if (!source || !target) { + *target=0; + return; + } + while (*source) + { + *target = toupper(*source); + target++;source++; + } + *target = 0; +} + +const char *MakeLowerString(const char *source) { + static char str[128]; + if (!source) + return NULL; + MakeLowerString(source, str); + return str; +} + +void MakeLowerString(const char *source, char *target) { + if (!source || !target) { + *target=0; + return; + } + while (*source) + { + *target = tolower(*source); + target++;source++; + } + *target = 0; +} + +int MakeAnyLenString(char** ret, const char* format, ...) { + int buf_len = 128; + int chars = -1; + va_list argptr; + va_start(argptr, format); + while (chars == -1 || chars >= buf_len) { + safe_delete_array(*ret); + if (chars == -1) + buf_len *= 2; + else + buf_len = chars + 1; + *ret = new char[buf_len]; + chars = vsnprintf(*ret, buf_len, format, argptr); + } + va_end(argptr); + return chars; +} + +int32 AppendAnyLenString(char** ret, int32* bufsize, int32* strlen, const char* format, ...) { + if (*bufsize == 0) + *bufsize = 256; + if (*ret == 0) + *strlen = 0; + int chars = -1; + char* oldret = 0; + va_list argptr; + va_start(argptr, format); + while (chars == -1 || chars >= (sint32)(*bufsize-*strlen)) { + if (chars == -1) + *bufsize += 256; + else + *bufsize += chars + 25; + oldret = *ret; + *ret = new char[*bufsize]; + if (oldret) { + if (*strlen) + memcpy(*ret, oldret, *strlen); + safe_delete_array(oldret); + } + chars = vsnprintf(&(*ret)[*strlen], (*bufsize-*strlen), format, argptr); + } + va_end(argptr); + *strlen += chars; + return *strlen; +} + +int32 hextoi(char* num) { + int len = strlen(num); + if (len < 3) + return 0; + + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + return 0; + + int32 ret = 0; + int mul = 1; + for (int i=len-1; i>=2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') + ret += ((num[i] - 'A') + 10) * mul; + else if (num[i] >= 'a' && num[i] <= 'f') + ret += ((num[i] - 'a') + 10) * mul; + else if (num[i] >= '0' && num[i] <= '9') + ret += (num[i] - '0') * mul; + else + return 0; + mul *= 16; + } + return ret; +} + +int64 hextoi64(char* num) { + int len = strlen(num); + if (len < 3) + return 0; + + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + return 0; + + int64 ret = 0; + int mul = 1; + for (int i=len-1; i>=2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') + ret += ((num[i] - 'A') + 10) * mul; + else if (num[i] >= 'a' && num[i] <= 'f') + ret += ((num[i] - 'a') + 10) * mul; + else if (num[i] >= '0' && num[i] <= '9') + ret += (num[i] - '0') * mul; + else + return 0; + mul *= 16; + } + return ret; +} + +bool atobool(char* iBool) { + if (!strcasecmp(iBool, "true")) + return true; + if (!strcasecmp(iBool, "false")) + return false; + if (!strcasecmp(iBool, "yes")) + return true; + if (!strcasecmp(iBool, "no")) + return false; + if (!strcasecmp(iBool, "on")) + return true; + if (!strcasecmp(iBool, "off")) + return false; + if (!strcasecmp(iBool, "enable")) + return true; + if (!strcasecmp(iBool, "disable")) + return false; + if (!strcasecmp(iBool, "enabled")) + return true; + if (!strcasecmp(iBool, "disabled")) + return false; + if (!strcasecmp(iBool, "y")) + return true; + if (!strcasecmp(iBool, "n")) + return false; + if (atoi(iBool)) + return true; + return false; +} + +/* + * solar: generate a random integer in the range low-high + * this should be used instead of the rand()%limit method + */ +int MakeRandomInt(int low, int high) +{ + if(low >= high) + return(low); + + return (rand()%(high-low+1) + (low)); +} + +double MakeRandomFloat(double low, double high) +{ + if(low >= high) + return(low); + + return (rand() / (double)RAND_MAX * (high - low) + low); +} + +// solar: removes the crap and turns the underscores into spaces. +char *CleanMobName(const char *in, char *out) +{ + unsigned i, j; + + for(i = j = 0; i < strlen(in); i++) + { + // convert _ to space.. any other conversions like this? I *think* this + // is the only non alpha char that's not stripped but converted. + if(in[i] == '_') + { + out[j++] = ' '; + } + else + { + if(isalpha(in[i]) || (in[i] == '`')) // numbers, #, or any other crap just gets skipped + out[j++] = in[i]; + } + } + out[j] = 0; // terimnate the string before returning it + return out; +} + +const char *ConvertArray(int input, char *returnchar) +{ + sprintf(returnchar, "%i" ,input); + return returnchar; +} + +const char *ConvertArrayF(float input, char *returnchar) +{ + sprintf(returnchar, "%0.2f", input); + return returnchar; +} + +float EQ13toFloat(int d) +{ + return ( float(d)/float(1<<2)); +} + +float NewEQ13toFloat(int d) +{ + return ( float(d)/float(1<<6)); +} + +float EQ19toFloat(int d) +{ + return ( float(d)/float(1<<3)); +} + +int FloatToEQ13(float d) +{ + return int(d*float(1<<2)); +} + +int NewFloatToEQ13(float d) +{ + return int(d*float(1<<6)); +} + +int FloatToEQ19(float d) +{ + return int(d*float(1<<3)); +} + +/* + Heading of 0 points in the pure positive Y direction + +*/ +int FloatToEQH(float d) +{ + return(int((360.0f - d) * float(1<<11)) / 360); +} + +float EQHtoFloat(int d) +{ + return(360.0f - float((d * 360) >> 11)); +} \ No newline at end of file diff --git a/utils/player_profile_set/player_profile_set/MiscFunctions.h b/utils/player_profile_set/player_profile_set/MiscFunctions.h new file mode 100644 index 000000000..7e50b8a16 --- /dev/null +++ b/utils/player_profile_set/player_profile_set/MiscFunctions.h @@ -0,0 +1,79 @@ +/* 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 MISCFUNCTIONS_H +#define MISCFUNCTIONS_H + +#include "types.h" +#include +#include + + +#ifndef ERRBUF_SIZE +#define ERRBUF_SIZE 1024 +#endif + +// These are helper macros for dealing with packets of variable length, typically those that contain +// variable length strings where it is not convenient to use a fixed length struct. +// +#define VARSTRUCT_DECODE_TYPE(Type, Buffer) *(Type *)Buffer; Buffer += sizeof(Type); +#define VARSTRUCT_DECODE_STRING(String, Buffer) strcpy(String, Buffer); Buffer += strlen(String)+1; +#define VARSTRUCT_ENCODE_STRING(Buffer, String) sprintf(Buffer, String); Buffer += strlen(String) + 1; +#define VARSTRUCT_ENCODE_INTSTRING(Buffer, Number) sprintf(Buffer, "%i", Number); Buffer += strlen(Buffer) + 1; +#define VARSTRUCT_ENCODE_TYPE(Type, Buffer, Value) *(Type *)Buffer = Value; Buffer += sizeof(Type); + +////////////////////////////////////////////////////////////////////// +// +// MakeUpperString +// i : source - allocated null-terminated string +// return: pointer to static buffer with the target string +const char *MakeUpperString(const char *source); +const char *MakeLowerString(const char *source); +////////////////////////////////////////////////////////////////////// +// +// MakeUpperString +// i : source - allocated null-terminated string +// io: target - allocated buffer, at least of size strlen(source)+1 +void MakeUpperString(const char *source, char *target); +void MakeLowerString(const char *source, char *target); + + +int MakeAnyLenString(char** ret, const char* format, ...); +int32 AppendAnyLenString(char** ret, int32* bufsize, int32* strlen, const char* format, ...); +int32 hextoi(char* num); +int64 hextoi64(char* num); +bool atobool(char* iBool); +void CoutTimestamp(bool ms = true); +char* strn0cpy(char* dest, const char* source, int32 size); + // return value =true if entire string(source) fit, false if it was truncated +bool strn0cpyt(char* dest, const char* source, int32 size); +int MakeRandomInt(int low, int high); +double MakeRandomFloat(double low, double high); +char *CleanMobName(const char *in, char *out); +const char *ConvertArray(int input, char *returnchar); +const char *ConvertArrayF(float input, char *returnchar); +float EQ13toFloat(int d); +float NewEQ13toFloat(int d); +float EQ19toFloat(int d); +float EQHtoFloat(int d); +int FloatToEQ13(float d); +int NewFloatToEQ13(float d); +int FloatToEQ19(float d); +int FloatToEQH(float d); + +#endif + diff --git a/utils/player_profile_set/player_profile_set/database.cpp b/utils/player_profile_set/player_profile_set/database.cpp new file mode 100644 index 000000000..f44ed7c73 --- /dev/null +++ b/utils/player_profile_set/player_profile_set/database.cpp @@ -0,0 +1,133 @@ +#include "database.h" +#include "main.h" +#include "eq_player_structs.h" +#include + +extern std::vector player_list; + +EQEmuDatabase::EQEmuDatabase(std::string ServerName, std::string DatabaseName, std::string DBUsername, std::string DBPassword) { + SetServerName(ServerName); + SetDatabaseName(DatabaseName); + SetDBUsername(DBUsername); + SetDBPassword(DBPassword); + + _mysql = mysql_init(NULL); + if(_mysql) + { + if (!mysql_real_connect(_mysql, GetServerName().c_str(), GetDBUsername().c_str(), + GetDBPassword().c_str(), GetDatabaseName().c_str(), 0, NULL, 0)) + { + cout << "MYSQL CONNECT FAILED: " << endl; + cout << GetServerName() << endl; + cout << GetDBUsername() << endl; + cout << GetDBPassword() << endl; + cout << GetDatabaseName() << endl; + mysql_close(_mysql); + _mysql = 0; + } + } +} + +EQEmuDatabase::~EQEmuDatabase() +{ + if(_mysql) + { + mysql_close(_mysql); + } +} + +void EQEmuDatabase::GetPlayer(std::string name) +{ + bool result = false; + if(!_mysql) + { + cout << "NOT CONNECTED TO MYQSL: " << endl; + return; + } + + MYSQL_RES *res; + MYSQL_ROW row; + char * mQuery = 0; + MakeAnyLenString(&mQuery, "SELECT profile, id FROM character_ WHERE name='%s'", name.c_str()); + if (mysql_query(_mysql, mQuery)) { + cout << "Query failed: " << mQuery << endl; + return; + } + + res = mysql_use_result(_mysql); + if(res) + { + while ((row = mysql_fetch_row(res)) != NULL) + { + player_entry pe; + pe.id = atoi(row[1]); + pe.data = new char[sizeof(PlayerProfile_Struct)]; + memcpy(pe.data, row[0], sizeof(PlayerProfile_Struct)); + player_list.push_back(pe); + + PlayerProfile_Struct *m_pp = (PlayerProfile_Struct*)pe.data; + } + mysql_free_result(res); + } + delete[] mQuery; + mQuery = 0; +} + +void EQEmuDatabase::GetPlayers() +{ + bool result = false; + if(!_mysql) + { + cout << "NOT CONNECTED TO MYQSL: " << endl; + return; + } + + MYSQL_RES *res; + MYSQL_ROW row; + char * mQuery = 0; + MakeAnyLenString(&mQuery, "SELECT profile, id FROM character_"); + if (mysql_query(_mysql, mQuery)) { + cout << "Query failed: " << mQuery << endl; + return; + } + + res = mysql_use_result(_mysql); + if(res) + { + while ((row = mysql_fetch_row(res)) != NULL) + { + player_entry pe; + pe.id = atoi(row[1]); + pe.data = new char[sizeof(PlayerProfile_Struct)]; + memcpy(pe.data, row[0], sizeof(PlayerProfile_Struct)); + player_list.push_back(pe); + + PlayerProfile_Struct *m_pp = (PlayerProfile_Struct*)pe.data; + } + mysql_free_result(res); + } + delete[] mQuery; + mQuery = 0; +} + +void EQEmuDatabase::StorePlayer(int32 charid, char* data) +{ + if(!_mysql) + { + cout << "NOT CONNECTED TO MYQSL: " << endl; + return; + } + + char *outbuffer = new char[2*sizeof(PlayerProfile_Struct) + 512]; + char *bptr = outbuffer; + bptr += snprintf(bptr, 128, "UPDATE character_ SET profile='"); + bptr += mysql_real_escape_string(_mysql, bptr, (const char *) data, sizeof(PlayerProfile_Struct)); + snprintf(bptr, 128, "' WHERE id=%lu", charid); + + if (mysql_query(_mysql, outbuffer)) { + cout << "Query failed: " << outbuffer << endl; + } + + delete[] outbuffer; + outbuffer = 0; +} diff --git a/utils/player_profile_set/player_profile_set/database.h b/utils/player_profile_set/player_profile_set/database.h new file mode 100644 index 000000000..499449634 --- /dev/null +++ b/utils/player_profile_set/player_profile_set/database.h @@ -0,0 +1,45 @@ +#if !defined(_L__EQDATAB__H) +#define _L__EQDATAB__H + +#include "MiscFunctions.h" + +#ifdef WIN32 +#include +#endif +#include +#include +#include +#include + +using namespace std; + +#pragma comment ( lib, "libmysql" ) + +class EQEmuDatabase { +public: + EQEmuDatabase(string serverName, string databaseName, string dbUsername, string dbPassword); + ~EQEmuDatabase(); + + bool Connected() { if(_mysql){ return true; }else{ return false; } } + + void SetDBUsername(string dbUsername) { _dbUsername = dbUsername; }; + void SetDBPassword(string dbPassword) { _dbPassword = dbPassword; }; + void SetDatabaseName(string databaseName) { _databaseName = databaseName; }; + void SetServerName(string serverName) { _serverName = serverName; }; + string GetDBUsername() { return _dbUsername; }; + string GetDBPassword() { return _dbPassword; }; + string GetDatabaseName() { return _databaseName; }; + string GetServerName() { return _serverName; }; + + void GetPlayer(std::string name); + void GetPlayers(); + void StorePlayer(int32 charid, char* data); + +private: + string _dbUsername; + string _dbPassword; + string _databaseName; + string _serverName; + MYSQL *_mysql; +}; +#endif \ No newline at end of file diff --git a/utils/player_profile_set/player_profile_set/eq_player_structs.h b/utils/player_profile_set/player_profile_set/eq_player_structs.h new file mode 100644 index 000000000..2696c8906 --- /dev/null +++ b/utils/player_profile_set/player_profile_set/eq_player_structs.h @@ -0,0 +1,4027 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 EQ_PACKET_STRUCTS_H +#define EQ_PACKET_STRUCTS_H + +#include "types.h" +#include +#include +#include +//#include "../common/version.h" +//#include "../common/item_struct.h" + +static const uint32 BUFF_COUNT = 25; + +//#include "eq_constants.h" + +/* +** Compiler override to ensure +** byte aligned structures +*/ +#pragma pack(1) + +struct LoginInfo_Struct { +/*000*/ char login_info[64]; +/*064*/ uint8 unknown064[124]; +/*188*/ uint8 zoning; // 01 if zoning, 00 if not +/*189*/ uint8 unknown189[275]; +/*488*/ +}; + +struct EnterWorld_Struct { +/*000*/ char name[64]; +/*064*/ uint32 tutorial; // 01 on "Enter Tutorial", 00 if not +/*068*/ uint32 return_home; // 01 on "Return Home", 00 if not +}; + +struct ExpansionInfo_Struct { +/*0000*/ uint32 Expansions; +}; + +/* Name Approval Struct */ +/* Len: */ +/* Opcode: 0x8B20*/ +struct NameApproval +{ + char name[64]; + int32 race; + int32 class_; + int32 deity; +}; + +/* +** Entity identification struct +** Size: 4 bytes +** OPCodes: OP_DeleteSpawn, OP_Assist +*/ +struct EntityId_Struct +{ +/*00*/ uint32 entity_id; +/*04*/ +}; + +struct Duel_Struct +{ + int32 duel_initiator; + int32 duel_target; +}; + +struct DuelResponse_Struct +{ + int32 target_id; + int32 entity_id; + int32 unknown; +}; + +//adventure stuff +enum AdventureObjective +{ + Adventure_Random = 0, + Adventure_Assassinate = 1, + Adventure_Kill = 2, + Adventure_Collect = 3, + Adventure_Rescue = 4 +}; + +struct AdventureInfo +{ + uint32 id; + std::string zone_name; + uint16 zone_version; + uint16 is_hard; + uint8 is_raid; + uint16 min_level; + uint16 max_level; + AdventureObjective type; + uint32 type_data; + uint16 type_count; + std::string text; + uint32 duration; + uint32 zone_in_time; + uint8 theme; + uint16 win_points; + uint16 lose_points; + uint16 zone_in_zone_id; + double zone_in_x; //loc of zone in object + double zone_in_y; //loc of zone in object + sint16 zone_in_object_id; //actually a global door id + double dest_x; //x we zone into + double dest_y; //y we zone into + double dest_z; //z we zone into + double dest_h; //h we zone into +}; + +struct AdventureDetails +{ + uint32 id; + AdventureInfo *ai; + sint32 instance_id; + int32 count; + int8 status; + int32 assassinate_count; + int32 time_created; + int32 time_zoned; + int32 time_completed; +}; +/////////////////////////////////////////////////////////////////////////////// + + +/* +** Color_Struct +** Size: 4 bytes +** Used for convenience +** Merth: Gave struct a name so gcc 2.96 would compile +** +*/ +struct Color_Struct +{ + union + { + struct + { + int8 blue; + int8 green; + int8 red; + uint8 use_tint; // if there's a tint this is FF + } rgb; + uint32 color; + }; +}; + +/* +** Character Selection Struct +** Length: 1704 Bytes +** +*/ +struct CharacterSelect_Struct { +/*0000*/ int32 race[10]; // Characters Race +/*0040*/ Color_Struct cs_colors[10][9]; // Characters Equipment Colors +/*0400*/ int8 beardcolor[10]; // Characters beard Color +/*0410*/ int8 hairstyle[10]; // Characters hair style +/*0420*/ int32 equip[10][9]; // 0=helm, 1=chest, 2=arm, 3=bracer, 4=hand, 5=leg, 6=boot, 7=melee1, 8=melee2 (Might not be) +/*0780*/ int32 secondary[10]; // Characters secondary IDFile number +/*0820*/ int32 drakkin_heritage[10]; // added for SoF +/*0860*/ int32 drakkin_tattoo[10]; // added for SoF +/*0900*/ int32 drakkin_details[10]; // added for SoF +/*0940*/ int32 deity[10]; // Characters Deity +/*0980*/ int8 gohome[10]; // 1=Go Home available, 0=not +/*0990*/ int8 tutorial[10]; // 1=Tutorial available, 0=not +/*1000*/ int8 beard[10]; // Characters Beard Type +/*1010*/ int8 unknown902[10]; // 10x ff +/*1020*/ int32 primary[10]; // Characters primary IDFile number +/*1060*/ int8 haircolor[10]; // Characters Hair Color +/*1070*/ int8 unknown0962[2]; // 2x 00 +/*1072*/ int32 zone[10]; // Characters Current Zone +/*1112*/ int8 class_[10]; // Characters Classes +/*1022*/ int8 face[10]; // Characters Face Type +/*1032*/ char name[10][64]; // Characters Names +/*1672*/ int8 gender[10]; // Characters Gender +/*1682*/ int8 eyecolor1[10]; // Characters Eye Color +/*1692*/ int8 eyecolor2[10]; // Characters Eye 2 Color +/*1702*/ int8 level[10]; // Characters Levels +/*1712*/ +}; + +/* +** Generic Spawn Struct +** Length: 257 Bytes +** Fields from old struct not yet found: +** float size; +** float walkspeed; // probably one of the ff 33 33 33 3f +** float runspeed; // probably one of the ff 33 33 33 3f +** int8 traptype; // 65 is disarmable trap, 66 and 67 are invis triggers/traps +** int8 npc_armor_graphic; // 0xFF=Player, 0=none, 1=leather, 2=chain, 3=steelplate +** int8 npc_helm_graphic; // 0xFF=Player, 0=none, 1=leather, 2=chain, 3=steelplate +** +*/ + +/* +** Generic Spawn Struct +** Length: 383 Octets +** Used in: +** spawnZoneStruct +** dbSpawnStruct +** petStruct +** newSpawnStruct +*/ +/* +showeq -> eqemu +sed -e 's/_t//g' -e 's/seto_0xFF/set_to_0xFF/g' +*/ +struct Spawn_Struct { +/*0000*/ uint8 unknown0000; +/*0001*/ uint8 gm; // 0=no, 1=gm +/*0002*/ uint8 unknown0003; +/*0003*/ int8 aaitle; // 0=none, 1=general, 2=archtype, 3=class +/*0004*/ uint8 unknown0004; +/*0005*/ uint8 anon; // 0=normal, 1=anon, 2=roleplay +/*0006*/ uint8 face; // Face id for players +/*0007*/ char name[64]; // Player's Name +/*0071*/ int16 deity; // Player's Deity +/*0073*/ uint16 unknown0073; +/*0075*/ float size; // Model size +/*0079*/ uint32 unknown0079; +/*0083*/ uint8 NPC; // 0=player,1=npc,2=pc corpse,3=npc corpse,a +/*0084*/ uint8 invis; // Invis (0=not, 1=invis) +/*0085*/ uint8 haircolor; // Hair color +/*0086*/ uint8 curHp; // Current hp %%% wrong +/*0087*/ uint8 max_hp; // (name prolly wrong)takes on the value 100 for players, 100 or 110 for NPCs and 120 for PC corpses... +/*0088*/ uint8 findable; // 0=can't be found, 1=can be found +/*0089*/ uint8 unknown0089[5]; +/*0094*/ signed deltaHeading:10;// change in heading + signed x:19; // x coord + signed padding0054:3; // ***Placeholder +/*0098*/ signed y:19; // y coord + signed animation:10; // animation + signed padding0058:3; // ***Placeholder +/*0102*/ signed z:19; // z coord + signed deltaY:13; // change in y +/*0106*/ signed deltaX:13; // change in x + unsigned heading:12; // heading + signed padding0066:7; // ***Placeholder +/*0110*/ signed deltaZ:13; // change in z + signed padding0070:19; // ***Placeholder +/*0114*/ uint8 eyecolor1; // Player's left eye color +/*0115*/ uint8 unknown0115[12]; // Was [24] +/*0127*/ uint32 drakkin_heritage; // Added for SoF +/*0131*/ uint32 drakkin_tattoo; // Added for SoF +/*0135*/ uint32 drakkin_details; // Added for SoF +/*0139*/ uint8 showhelm; // 0=no, 1=yes +/*0140*/ uint8 unknown0140[4]; +/*0144*/ uint8 is_npc; // 0=no, 1=yes +/*0145*/ uint8 hairstyle; // Hair style +/*0146*/ uint8 beard; // Beard style (not totally, sure but maybe!) +/*0147*/ uint8 unknown0147[4]; +/*0151*/ uint8 level; // Spawn Level +/*0152*/ uint8 unknown0259[4]; // ***Placeholder +/*0156*/ uint8 beardcolor; // Beard color +/*0157*/ char suffix[32]; // Player's suffix (of Veeshan, etc.) +/*0189*/ uint32 petOwnerId; // If this is a pet, the spawn id of owner +/*0193*/ int8 guildrank; // 0=normal, 1=officer, 2=leader +/*0194*/ uint8 unknown0194[3]; +/*0197*/ union + { + struct + { + /*0197*/ uint32 equip_helmet; // Equipment: Helmet Visual + /*0201*/ uint32 equip_chest; // Equipment: Chest Visual + /*0205*/ uint32 equip_arms; // Equipment: Arms Visual + /*0209*/ uint32 equip_bracers; // Equipment: Bracers Visual + /*0213*/ uint32 equip_hands; // Equipment: Hands Visual + /*0217*/ uint32 equip_legs; // Equipment: Legs Visual + /*0221*/ uint32 equip_feet; // Equipment: Feet Visual + /*0225*/ uint32 equip_primary; // Equipment: Primary Visual + /*0229*/ uint32 equip_secondary; // Equipment: Secondary Visual + } equip; + /*0197*/ uint32 equipment[9]; // Array elements correspond to struct equipment above + }; +/*0233*/ float runspeed; // Speed when running +/*0036*/ uint8 afk; // 0=no, 1=afk +/*0238*/ uint32 guildID; // Current guild +/*0242*/ char title[32]; // Title +/*0274*/ uint8 unknown0274; +/*0275*/ uint8 set_to_0xFF[8]; // ***Placeholder (all ff) +/*0283*/ uint8 helm; // Helm texture +/*0284*/ uint32 race; // Spawn race +/*0288*/ uint32 unknown0288; +/*0292*/ char lastName[32]; // Player's Lastname +/*0324*/ float walkspeed; // Speed when walking +/*0328*/ uint8 unknown0328; +/*0329*/ uint8 is_pet; // 0=no, 1=yes +/*0330*/ uint8 light; // Spawn's lightsource %%% wrong +/*0331*/ uint8 class_; // Player's class +/*0332*/ uint8 eyecolor2; // Left eye color +/*0333*/ uint8 flymode; +/*0334*/ uint8 gender; // Gender (0=male, 1=female) +/*0335*/ uint8 bodytype; // Bodytype +/*0336*/ uint8 unknown0336[3]; +union +{ +/*0339*/ int8 equip_chest2; // Second place in packet for chest texture (usually 0xFF in live packets) + // Not sure why there are 2 of them, but it effects chest texture! +/*0339*/ int8 mount_color; // drogmor: 0=white, 1=black, 2=green, 3=red + // horse: 0=brown, 1=white, 2=black, 3=tan +}; +/*0340*/ uint32 spawnId; // Spawn Id +/*0344*/ uint8 unknown0344[4]; +/*0348*/ union + { + struct + { + /*0348*/ Color_Struct color_helmet; // Color of helmet item + /*0352*/ Color_Struct color_chest; // Color of chest item + /*0356*/ Color_Struct color_arms; // Color of arms item + /*0360*/ Color_Struct color_bracers; // Color of bracers item + /*0364*/ Color_Struct color_hands; // Color of hands item + /*0368*/ Color_Struct color_legs; // Color of legs item + /*0372*/ Color_Struct color_feet; // Color of feet item + /*0376*/ Color_Struct color_primary; // Color of primary item + /*0380*/ Color_Struct color_secondary; // Color of secondary item + } equipment_colors; + /*0348*/ Color_Struct colors[9]; // Array elements correspond to struct equipment_colors above + }; +/*0384*/ uint8 lfg; // 0=off, 1=lfg on +/*0385*/ + +}; + +/* +** New Spawn +** Length: 176 Bytes +** OpCode: 4921 +*/ +struct NewSpawn_Struct +{ + struct Spawn_Struct spawn; // Spawn Information +}; + +struct ClientZoneEntry_Struct { +/*0000*/ uint32 unknown00; +/*0004*/ char char_name[64]; // Character Name +}; + +/* +** Server Zone Entry Struct +** OPCodes: OP_ServerZoneEntry +** +*/ +struct ServerZoneEntry_Struct +{ + struct NewSpawn_Struct player; +}; + +struct NewZone_Struct { +/*0000*/ char char_name[64]; // Character Name +/*0064*/ char zone_short_name[32]; // Zone Short Name +/*0096*/ char zone_long_name[278]; // Zone Long Name +/*0374*/ uint8 ztype; // Zone type (usually FF) +/*0375*/ uint8 fog_red[4]; // Zone fog (red) +/*0379*/ uint8 fog_green[4]; // Zone fog (green) +/*0383*/ uint8 fog_blue[4]; // Zone fog (blue) +/*0387*/ uint8 unknown323; +/*0388*/ float fog_minclip[4]; +/*0404*/ float fog_maxclip[4]; +/*0420*/ float gravity; +/*0424*/ int8 time_type; +/*0425*/ uint8 unknown360[49]; +/*0474*/ uint8 sky; // Sky Type +/*0475*/ uint8 unknown331[13]; // ***Placeholder +/*0488*/ float zone_exp_multiplier; // Experience Multiplier +/*0492*/ float safe_y; // Zone Safe Y +/*0496*/ float safe_x; // Zone Safe X +/*0500*/ float safe_z; // Zone Safe Z +/*0504*/ float max_z; // Guessed +/*0508*/ float underworld; // Underworld, min z (Not Sure?) +/*0512*/ float minclip; // Minimum View Distance +/*0516*/ float maxclip; // Maximum View DIstance +/*0520*/ int8 unknown_end[84]; // ***Placeholder +/*0604*/ char zone_short_name2[68]; +/*0672*/ char unknown672[12]; +/*0684*/ uint16 zone_id; +/*0686*/ uint16 zone_instance; +/*0688*/ uint32 unknown688; +/*0692*/ uint8 unknown692[8]; +/*0700*/ +}; + +/* +** Memorize Spell Struct +** Length: 12 Bytes +** +*/ +struct MemorizeSpell_Struct { +int32 slot; // Spot in the spell book/memorized slot +int32 spell_id; // Spell id (200 or c8 is minor healing, etc) +int32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if un-memming +int32 unknown12; +}; + +/* +** Make Charmed Pet +** Length: 12 Bytes +** +*/ +struct Charm_Struct { +/*00*/ int32 owner_id; +/*04*/ int32 pet_id; +/*08*/ int32 command; // 1: make pet, 0: release pet +/*12*/ +}; + +struct InterruptCast_Struct +{ + int32 spawnid; + int32 messageid; + char* message; +}; + +struct DeleteSpell_Struct +{ +/*000*/sint16 spell_slot; +/*002*/int8 unknowndss002[2]; +/*004*/int8 success; +/*005*/int8 unknowndss006[3]; +/*008*/ +}; + +struct ManaChange_Struct +{ + int32 new_mana; // New Mana AMount + int32 stamina; + int32 spell_id; + int32 unknown12; +}; + +struct SwapSpell_Struct +{ + int32 from_slot; + int32 to_slot; + + +}; + +struct BeginCast_Struct +{ + // len = 8 +/*000*/ int16 caster_id; +/*002*/ int16 spell_id; +/*004*/ int32 cast_time; // in miliseconds +}; + +struct CastSpell_Struct +{ + int32 slot; + int32 spell_id; + int32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast + int32 target_id; + int8 cs_unknown[4]; +}; + +/* +** SpawnAppearance_Struct +** Changes client appearance for all other clients in zone +** Size: 8 bytes +** Used in: OP_SpawnAppearance +** +*/ +struct SpawnAppearance_Struct +{ +/*0000*/ uint16 spawn_id; // ID of the spawn +/*0002*/ uint16 type; // Values associated with the type +/*0004*/ uint32 parameter; // Type of data sent +/*0008*/ +}; + + +// solar: this is used inside profile +struct SpellBuff_Struct +{ +/*000*/ int8 slotid; //badly named... seems to be 2 for a real buff, 0 otherwise +/*001*/ int8 level; +/*002*/ int8 bard_modifier; +/*003*/ int8 effect; //not real +/*004*/ int32 spellid; +/*008*/ int32 duration; +/*012*/ int16 dmg_shield_remaining; +//these last four bytes are really the caster's global player ID for wearoff +/*013*/ int8 persistant_buff; //prolly not real, used for perm illusions +/*014*/ int8 reserved; //proll not real, reserved will use for something else later +/*012*/ int32 player_id; //'global' ID of the caster, for wearoff messages +}; + +struct SpellBuffFade_Struct { +/*000*/ uint32 entityid; +/*004*/ int8 slot; +/*005*/ int8 level; +/*006*/ int8 effect; +/*007*/ int8 unknown7; +/*008*/ uint32 spellid; +/*012*/ uint32 duration; +/*016*/ uint32 unknown016; +/*020*/ uint32 unknown020; //prolly global player ID +/*024*/ uint32 slotid; +/*028*/ uint32 bufffade; +/*032*/ +}; + +struct PetBuff_Struct { +/*000*/ uint32 petid; +/*004*/ uint32 spellid[BUFF_COUNT]; +/*104*/ uint32 unknown700; +/*108*/ uint32 unknown701; +/*112*/ uint32 unknown702; +/*116*/ uint32 unknown703; +/*120*/ uint32 unknown704; +/*124*/ uint32 ticsremaining[BUFF_COUNT]; +/*224*/ uchar unknown705[20]; +/*244*/ uint32 buffcount; +}; + +struct ItemNamePacket_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 unkown004; +/*008*/ char name[64]; +/*072*/ +}; + +// Length: 10 +struct ItemProperties_Struct { + +int8 unknown01[2]; +int8 charges; +int8 unknown02[13]; +}; + +struct GMTrainee_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ uint32 skills[73]; + /*300*/ int8 unknown300[148]; + /*448*/ +}; + +struct GMTrainEnd_Struct +{ + /*000*/ uint32 npcid; + /*004*/ uint32 playerid; + /*008*/ +}; + +struct GMSkillChange_Struct { +/*000*/ int16 npcid; +/*002*/ int8 unknown1[2]; // something like PC_ID, but not really. stays the same thru the session though +/*002*/ int16 skillbank; // 0 if normal skills, 1 if languages +/*002*/ int8 unknown2[2]; +/*008*/ uint16 skill_id; +/*010*/ int8 unknown3[2]; +}; + +struct GMTrainSkillConfirm_Struct { // SoF only +/*000*/ uint32 SkillID; +/*004*/ uint32 Cost; +/*008*/ uint8 NewSkill; // Set to 1 for 'You have learned the basics' message. +/*009*/ char TrainerName[64]; +/*073*/ +}; + +struct ConsentResponse_Struct { + char grantname[64]; + char ownername[64]; + int8 permission; + char zonename[32]; +}; + +/* +** Name Generator Struct +** Length: 72 bytes +** OpCode: 0x0290 +*/ +struct NameGeneration_Struct +{ +/*0000*/ int32 race; +/*0004*/ int32 gender; +/*0008*/ char name[64]; +/*0072*/ +}; + +/* +** Character Creation struct +** Length: 140 Bytes +** OpCode: 0x0113 +*/ +struct CharCreate_Struct +{ +/*0000*/ int32 class_; +/*0004*/ int32 haircolor; // Might be hairstyle +/*0008*/ int32 beardcolor; // Might be beard +/*0012*/ int32 beard; // Might be beardcolor +/*0016*/ int32 gender; +/*0020*/ int32 race; +/*0024*/ int32 start_zone; + // 0 = odus + // 1 = qeynos + // 2 = halas + // 3 = rivervale + // 4 = freeport + // 5 = neriak + // 6 = gukta/grobb + // 7 = ogguk + // 8 = kaladim + // 9 = gfay + // 10 = felwithe + // 11 = akanon + // 12 = cabalis + // 13 = shar vahl +/*0028*/ int32 hairstyle; // Might be haircolor +/*0032*/ int32 deity; +/*0036*/ int32 STR; +/*0040*/ int32 STA; +/*0044*/ int32 AGI; +/*0048*/ int32 DEX; +/*0052*/ int32 WIS; +/*0056*/ int32 INT; +/*0060*/ int32 CHA; +/*0064*/ int32 face; // Could be unknown0076 +/*0068*/ int32 eyecolor1; //its possiable we could have these switched +/*0073*/ int32 eyecolor2; //since setting one sets the other we really can't check +/*0076*/ int32 drakkin_heritage; // added for SoF +/*0080*/ int32 drakkin_tattoo; // added for SoF +/*0084*/ int32 drakkin_details; // added for SoF +/*0088*/ +}; + +/* + *Used in PlayerProfile + */ +struct AA_Array +{ + int32 AA; + int32 value; +}; + + +static const uint32 MAX_PP_DISCIPLINES = 100; +static const uint32 MAX_DISCIPLINE_TIMERS = 20; + +struct Disciplines_Struct { + uint32 values[MAX_PP_DISCIPLINES]; +}; + +static const uint32 TRIBUTE_SLOT_START = 400; +static const uint32 MAX_PLAYER_TRIBUTES = 5; +static const uint32 MAX_PLAYER_BANDOLIER = 4; +static const uint32 MAX_PLAYER_BANDOLIER_ITEMS = 4; +static const uint32 MAX_POTIONS_IN_BELT = 4; +static const uint32 TRIBUTE_NONE = 0xFFFFFFFF; +struct Tribute_Struct { + uint32 tribute; + uint32 tier; +}; + +//len = 72 +struct BandolierItem_Struct { + uint32 item_id; + uint32 icon; + char item_name[64]; +}; + +//len = 320 +enum { //bandolier item positions + bandolierMainHand = 0, + bandolierOffHand, + bandolierRange, + bandolierAmmo +}; +struct Bandolier_Struct { + char name[32]; + BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS]; +}; +struct PotionBelt_Struct { + BandolierItem_Struct items[MAX_POTIONS_IN_BELT]; +}; + +struct MovePotionToBelt_Struct { + uint32 Action; + uint32 SlotNumber; + uint32 ItemID; +}; + +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]; +}; +struct RaidLeadershipAA_Struct { + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; +}; + + /** +* A bind point. +* Size: 20 Octets +*/ +struct BindStruct { + /*000*/ uint32 zoneId; + /*004*/ float x; + /*008*/ float y; + /*012*/ float z; + /*016*/ float heading; + /*020*/ +}; + + +/* +** Player Profile +** +** Length: 4308 bytes +** OpCode: 0x006a + */ +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 = 75; +static const uint32 MAX_PP_AA_ARRAY = 240; +static const uint32 MAX_GROUP_MEMBERS = 6; + +/* +showeq -> eqemu +sed -e 's/_t//g' -e 's/MAX_AA/MAX_PP_AA_ARRAY/g' \ + -e 's/MAX_SPELL_SLOTS/MAX_PP_MEMSPELL/g' \ + -e 's/MAX_KNOWN_SKILLS/MAX_PP_SKILL/g' \ + -e 's/MAXRIBUTES/MAX_PLAYER_TRIBUTES/g' \ + -e 's/MAX_BUFFS/BUFF_COUNT/g' \ + -e 's/MAX_KNOWN_LANGS/MAX_PP_LANGUAGE/g' \ + -e 's/MAX_RECASTYPES/MAX_RECAST_TYPES/g' \ + -e 's/spellBuff/SpellBuff_Struct/g' \ + -e 's/lastName/last_name/g' \ + -e 's/guildID/guildid/g' \ + -e 's/itemint/item_tint/g' \ + -e 's/MANA/mana/g' \ + -e 's/curHp/cur_hp/g' \ + -e 's/sSpellBook/spell_book/g' \ + -e 's/sMemSpells/mem_spells/g' \ + -e 's/uint32[ \t]*disciplines\[MAX_DISCIPLINES\]/Disciplines_Struct disciplines/g' \ + -e 's/aa_unspent/aapoints/g' \ + -e 's/aa_spent/aapoints_spent/g' \ + -e 's/InlineItem[ \t]*potionBelt\[MAX_POTIONS_IN_BELT\]/PotionBelt_Struct potionbelt/g' \ + -e 's/ldon_guk_points/ldon_points_guk/g' \ + -e 's/ldon_mir_points/ldon_points_mir/g' \ + -e 's/ldon_mmc_points/ldon_points_mmc/g' \ + -e 's/ldon_ruj_points/ldon_points_ruj/g' \ + -e 's/ldonak_points/ldon_points_tak/g' \ + -e 's/ldon_avail_points/ldon_points_available/g' \ + -e 's/tributeTime/tribute_time_remaining/g' \ + -e 's/careerTribute/career_tribute_points/g' \ + -e 's/currentTribute/tribute_points/g' \ + -e 's/tributeActive/tribute_active/g' \ + -e 's/TributeStruct/Tribute_Struct/g' \ + -e 's/expGroupLeadAA/group_leadership_exp/g' \ + -e 's/expRaidLeadAA/raid_leadership_exp/g' \ + -e 's/groupLeadAAUnspent/group_leadership_points/g' \ + -e 's/raidLeadAAUnspent/raid_leadership_points/g' \ + -e 's/uint32[ \t]*leadershipAAs\[MAX_LEAD_AA\]/LeadershipAA_Struct leader_abilities/g' \ + -e 's/BandolierStruct/Bandolier_Struct/g' \ + -e 's/MAX_BANDOLIERS/MAX_PLAYER_BANDOLIER/g' \ + -e 's/birthdayTime/birthday/g' \ + -e 's/lastSaveTime/lastlogin/g' \ + -e 's/zoneId/zone_id/g' \ + -e 's/hunger/hunger_level/g' \ + -e 's/thirst/thirst_level/g' \ + -e 's/guildstatus/guildrank/g' \ + -e 's/airRemaining/air_remaining/g' \ + + + + */ +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*/ BindStruct binds[5]; // Bind points (primary is first) +/*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 unknown0245[7]; // +/*0252*/ uint32 intoxication; +/*0256*/ uint32 spellSlotRefresh[MAX_PP_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[9]; // Item texture/material of worn/held items +/*0348*/ uint8 unknown0256[44]; +/*0396*/ Color_Struct item_tint[9]; +/*0432*/ AA_Array aa_array[MAX_PP_AA_ARRAY]; +/*2348*/ float unknown2348; //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 unknown1496; +/*2460*/ uint32 points; // Unspent Practice points +/*2464*/ uint32 mana; // current mana +/*2468*/ uint32 cur_hp; // current hp +/*2472*/ uint32 unknown1512; // 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 unknown1545[47]; // ? +/*2552*/ uint8 languages[MAX_PP_LANGUAGE]; +/*2580*/ uint8 unknown1620[4]; +/*2584*/ int32 spell_book[MAX_PP_SPELLBOOK]; +/*4184*/ uint8 unknown3224[448]; // all 0xff +/*4632*/ int32 mem_spells[MAX_PP_MEMSPELL]; +/*4668*/ uint8 unknown3704[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 unknown3756[4]; // +/*4720*/ sint32 platinum; // Platinum Pieces on player +/*4724*/ sint32 gold; // Gold Pieces on player +/*4728*/ sint32 silver; // Silver Pieces on player +/*4732*/ sint32 copper; // Copper Pieces on player +/*4736*/ sint32 platinum_bank; // Platinum Pieces in Bank +/*4740*/ sint32 gold_bank; // Gold Pieces in Bank +/*4744*/ sint32 silver_bank; // Silver Pieces in Bank +/*4748*/ sint32 copper_bank; // Copper Pieces in Bank +/*4752*/ sint32 platinum_cursor; // Platinum on cursor +/*4756*/ sint32 gold_cursor; // Gold on cursor +/*4760*/ sint32 silver_cursor; // Silver on cursor +/*4764*/ sint32 copper_cursor; // Copper on cursor +/*4768*/ sint32 platinum_shared; // Platinum shared between characters +/*4772*/ uint8 unknown3812[24]; // @bp unknown skills? +/*4796*/ uint32 skills[MAX_PP_SKILL]; +/*5096*/ uint8 unknown5096[284]; // @bp unknown skills? +/*5380*/ int32 pvp2; // +/*5384*/ int32 unknown4420; // +/*5388*/ int32 pvptype; // +/*5392*/ int32 unknown4428; // +/*5396*/ uint32 ability_down; // Doodman - Guessing +/*5400*/ uint8 unknown4436[8]; // +/*5408*/ uint32 autosplit; //not used right now +/*5412*/ uint8 unknown4448[8]; +/*5420*/ int32 zone_change_count; // Number of times user has zoned in their career (guessing) +/*5424*/ uint8 unknown4460[16]; // +/*5440*/ uint32 drakkin_heritage; // +/*5444*/ uint32 drakkin_tattoo; // +/*5448*/ uint32 drakkin_details; // +/*5452*/ int32 expansions; // expansion setting, bit field of expansions avaliable +/*5456*/ sint32 toxicity; //from drinking potions, seems to increase by 3 each time you drink +/*5460*/ char unknown4496[16]; // +/*5476*/ sint32 hunger_level; +/*5480*/ sint32 thirst_level; +/*5484*/ int32 ability_up; +/*5488*/ char unknown4524[16]; +/*5504*/ uint16 zone_id; // Current zone of the player +/*5506*/ uint16 zoneInstance; // Instance ID +/*5508*/ SpellBuff_Struct buffs[BUFF_COUNT]; // Buffs currently on the player +/*6008*/ char groupMembers[6][64]; // +/*6392*/ char unknown6392[656]; +/*7048*/ uint32 entityid; +/*7052*/ uint32 leadAAActive; +/*7056*/ uint32 unknown7056; +/*7060*/ sint32 ldon_points_guk; //client uses these as signed +/*7064*/ sint32 ldon_points_mir; +/*7068*/ sint32 ldon_points_mmc; +/*7072*/ sint32 ldon_points_ruj; +/*7076*/ sint32 ldon_points_tak; +/*7080*/ sint32 ldon_points_available; +/*7084*/ uint8 unknown5940[112]; +/*7196*/ uint32 tribute_time_remaining; //in miliseconds +/*7200*/ uint32 showhelm; +/*7204*/ uint32 career_tribute_points; +/*7208*/ uint32 unknown6056; +/*7212*/ uint32 tribute_points; +/*7216*/ uint32 unknown6064; +/*7220*/ uint32 tribute_active; //1=active +/*7224*/ Tribute_Struct tributes[MAX_PLAYER_TRIBUTES]; +/*7264*/ Disciplines_Struct disciplines; //fathernitwit: 10-06-04 +/*7664*/ char unknown7464[240]; +/*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*/ LeadershipAA_Struct leader_abilities; +/*8052*/ uint8 unknown8052[132]; +/*8184*/ uint32 air_remaining; +/*8188*/ uint8 unknown8188[4608]; //probably raid members 4608 = 64 * 72 +/*12796*/ uint32 aapoints_spent; +/*12800*/ uint32 expAA; +/*12804*/ uint32 aapoints; //avaliable, unspent +/*12808*/ uint8 unknown12808[36]; +/*12844*/ Bandolier_Struct bandoliers[MAX_PLAYER_BANDOLIER]; +/*14124*/ uint8 unknown14124[5116]; +/*19240*/ uint32 timeentitledonaccount; +/*19244*/ PotionBelt_Struct potionbelt; //there should be 3 more of these +/*19532*/ uint8 unknown19532[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 unknown19559[5]; // ***Placeholder (6/29/2005) +/*19564*/ uint32 RestTimer; +/*19568*/ +}; + + + + +/* +** Client Target Struct +** Length: 2 Bytes +** OpCode: 6221 +*/ +struct ClientTarget_Struct { +/*000*/ int32 new_target; // Target ID +}; + +/* +** Target Rejection Struct +** Length: 12 Bytes +** OpCode: OP_TargetReject +*/ +struct TargetReject_Struct { +/*00*/ uint8 unknown00[12]; +}; + +struct PetCommand_Struct { +/*000*/ int32 command; +/*004*/ int32 unknown; +}; + +/* +** Delete Spawn +** Length: 4 Bytes +** OpCode: OP_DeleteSpawn +*/ +struct DeleteSpawn_Struct +{ +/*00*/ int32 spawn_id; // Spawn ID to delete +/*04*/ +}; + +/* +** Channel Message received or sent +** Length: 144 Bytes + Variable Length + 1 +** OpCode: OP_ChannelMessage +** +*/ +struct ChannelMessage_Struct +{ +/*000*/ char targetname[64]; // Tell recipient +/*064*/ char sender[64]; // The senders name (len might be wrong) +/*128*/ int32 language; // Language +/*132*/ int32 chan_num; // Channel +/*136*/ int32 cm_unknown4[2]; // ***Placeholder +/*144*/ int32 skill_in_language; // The players skill in this language? might be wrong +/*148*/ char *message; // Variable length message +}; + +/* +** Special Message +** Length: 4 Bytes + Variable Text Length + 1 +** OpCode: OP_SpecialMesg +** +*/ +/* + Theres something wrong with this... example live packet: +Server->Client: [ Opcode: OP_SpecialMesg (0x0fab) Size: 244 ] + 0: 01 02 00 0A 00 00 00 09 - 05 00 00 42 61 72 73 74 | ...........Barst + 16: 72 65 20 53 6F 6E 67 77 - 65 61 76 65 72 00 7C F9 | re Songweaver.|. + 32: FF FF 84 FF FF FF 03 00 - 00 00 47 72 65 65 74 69 | ..........Greeti + +*/ +struct SpecialMesg_Struct +{ +/*00*/ char header[3]; // 04 04 00 <-- for #emote style msg +/*03*/ uint32 msg_type; // Color of text (see MT_*** below) +/*07*/ uint32 target_spawn_id; // Who is it being said to? +/*11*/ char sayer[1]; // Who is the source of the info +/*12*/ uint8 unknown12[12]; +/*24*/ char message[1]; // What is being said? +}; + +/* +** When somebody changes what they're wearing +** or give a pet a weapon (model changes) +** Length: 16 Bytes +** Opcode: 9220 +*/ +struct WearChange_Struct{ +/*000*/ int16 spawn_id; +/*002*/ int16 material; +/*004*/ Color_Struct color; +/*009*/ int8 wear_slot_id; +}; + +/* +** Type: Bind Wound Structure +** Length: 8 Bytes +*/ +//Fixed for 7-14-04 patch +struct BindWound_Struct +{ +/*002*/ int16 to; // TargetID +/*004*/ int16 unknown2; // ***Placeholder +/*006*/ int16 type; +/*008*/ int16 unknown6; +}; + + +/* +** Type: Zone Change Request (before hand) +** Length: 88 bytes +** OpCode: a320 +*/ + +struct ZoneChange_Struct { +/*000*/ char char_name[64]; // Character Name +/*064*/ uint16 zoneID; +/*066*/ uint16 instanceID; +/*068*/ float y; +/*072*/ float x; +/*076*/ float z; +/*080*/ int32 zone_reason; //0x0A == death, I think +/*084*/ sint32 success; // =0 client->server, =1 server->client, -X=specific error +/*088*/ +}; + +// Whatever you send to the client in RequestClientZoneChange_Struct.type, the client will send back +// to the server in ZoneChange_Struct.zone_reason. My guess is this is a memo field of sorts. +// WildcardX 27 January 2008 + +struct RequestClientZoneChange_Struct { +/*00*/ uint16 zone_id; +/*02*/ uint16 instance_id; +/*04*/ float y; +/*08*/ float x; +/*12*/ float z; +/*16*/ float heading; +/*20*/ uint32 type; //unknown... values +}; + +struct Animation_Struct { +/*00*/ int16 spawnid; +/*02*/ int8 action; +/*03*/ int8 value; +/*04*/ +}; + +// solar: this is what causes the caster to animate and the target to +// get the particle effects around them when a spell is cast +// also causes a buff icon +struct Action_Struct +{ + /* 00 */ int16 target; // id of target + /* 02 */ int16 source; // id of caster + /* 04 */ uint16 level; // level of caster + /* 06 */ uint16 instrument_mod; + /* 08 */ uint32 unknown08; + /* 12 */ uint16 unknown16; +// some kind of sequence that's the same in both actions +// as well as the combat damage, to tie em together? + /* 14 */ int32 sequence; + /* 18 */ uint32 unknown18; + /* 22 */ int8 type; // 231 (0xE7) for spells + /* 23 */ uint32 unknown23; + /* 27 */ int16 spell; // spell id being cast + /* 29 */ int8 unknown29; +// this field seems to be some sort of success flag, if it's 4 + /* 30 */ int8 buff_unknown; // if this is 4, a buff icon is made + /* 31 */ +}; + +// solar: this is what prints the You have been struck. and the regular +// melee messages like You try to pierce, etc. It's basically the melee +// and spell damage message +struct CombatDamage_Struct +{ +/* 00 */ int16 target; +/* 02 */ int16 source; +/* 04 */ int8 type; //slashing, etc. 231 (0xE7) for spells +/* 05 */ int16 spellid; +/* 07 */ int32 damage; +/* 11 */ int32 unknown11; +/* 15 */ int32 sequence; // see above notes in Action_Struct +/* 19 */ int32 unknown19; +/* 23 */ +}; + +/* +** Consider Struct +*/ +struct Consider_Struct{ +/*000*/ uint32 playerid; // PlayerID +/*004*/ uint32 targetid; // TargetID +/*008*/ int32 faction; // Faction +/*0012*/ int32 level; // Level +/*016*/ sint32 cur_hp; // Current Hitpoints +/*020*/ sint32 max_hp; // Maximum Hitpoints +/*024*/ int8 pvpcon; // Pvp con flag 0/1 +/*025*/ int8 unknown3[3]; +}; + +/* +** Spawn Death Blow +** Length: 32 Bytes +** OpCode: 0114 +*/ +struct Death_Struct +{ +/*000*/ int32 spawn_id; +/*004*/ int32 killer_id; +/*008*/ int32 corpseid; // was corpseid +/*012*/ int32 bindzoneid; +/*016*/ int32 spell_id; +/*020*/ int32 attack_skill; +/*024*/ int32 damage; +/*028*/ int32 unknown028; +}; + +struct BecomeCorpse_Struct { + int32 spawn_id; + float y; + float x; + float z; +}; + +/* +** Spawn position update +** Struct sent from server->client to update position of +** another spawn's position update in zone (whether NPC or PC) +** +*/ +struct PlayerPositionUpdateServer_Struct +{ +/*0000*/ uint16 spawn_id; +/*0002*/ sint32 delta_heading:10, // change in heading + x_pos:19, // x coord + padding0002:3; // ***Placeholder +/*0006*/ sint32 y_pos:19, // y coord + animation:10, // animation + padding0006:3; // ***Placeholder +/*0010*/ sint32 z_pos:19, // z coord + delta_y:13; // change in y +/*0014*/ sint32 delta_x:13, // change in x + heading:12, // heading + padding0014:7; // ***Placeholder +/*0018*/ sint32 delta_z:13, // change in z + padding0018:19; // ***Placeholder +/*0022*/ +}; + +/* +** Player position update +** Struct sent from client->server to update +** player position on server +** +*/ +struct PlayerPositionUpdateClient_Struct +{ +/*0000*/ uint16 spawn_id; +/*0022*/ uint16 sequence; //increments one each packet +/*0004*/ float y_pos; // y coord +/*0008*/ float delta_z; // Change in z +/*0016*/ float delta_x; // Change in x +/*0012*/ float delta_y; // Change in y +/*0020*/ sint32 animation:10, // animation + delta_heading:10, // change in heading + padding0020:12; // ***Placeholder (mostly 1) +/*0024*/ float x_pos; // x coord +/*0028*/ float z_pos; // z coord +/*0034*/ uint16 heading:12, // Directional heading + padding0004:4; // ***Placeholder +/*0032*/ uint8 unknown0006[2]; // ***Placeholder +/*0036*/ +}; + +/* +** Spawn HP Update +** Length: 10 Bytes +** OpCode: OP_HPUpdate +*/ +struct SpawnHPUpdate_Struct +{ +/*00*/ uint32 cur_hp; // Id of spawn to update +/*04*/ sint32 max_hp; // Maximum hp of spawn +/*08*/ sint16 spawn_id; // Current hp of spawn +/*10*/ +}; +struct SpawnHPUpdate_Struct2 +{ +/*01*/ sint16 spawn_id; +/*00*/ int8 hp; +}; +/* +** Stamina +** Length: 8 Bytes +** OpCode: 5721 +*/ +struct Stamina_Struct { +/*00*/ int32 food; // (low more hungry 127-0) +/*02*/ int32 water; // (low more thirsty 127-0) +}; + +/* +** Level Update +** Length: 12 Bytes +*/ +struct LevelUpdate_Struct +{ +/*00*/ uint32 level; // New level +/*04*/ uint32 level_old; // Old level +/*08*/ uint32 exp; // Current Experience +}; + +/* +** Experience Update +** Length: 14 Bytes +** OpCode: 9921 +*/ +struct ExpUpdate_Struct +{ +/*0000*/ uint32 exp; // Current experience ratio from 0 to 330 +/*0004*/ uint32 aaxp; // @BP ?? +}; + +/* +** Item Packet Struct - Works on a variety of opcodes +** Packet Types: See ItemPacketType enum +** +*/ +enum ItemPacketType +{ + ItemPacketViewLink = 0x00, + ItemPacketTradeView = 0x65, + ItemPacketLoot = 0x66, + ItemPacketTrade = 0x67, + ItemPacketCharInventory = 0x69, + ItemPacketSummonItem = 0x6A, + ItemPacketTributeItem = 0x6C, + ItemPacketMerchant = 0x64, + ItemPacketWorldContainer = 0x6B +}; +struct ItemPacket_Struct +{ +/*00*/ ItemPacketType PacketType; +/*04*/ char SerializedItem[1]; +/*xx*/ +}; + +struct BulkItemPacket_Struct +{ +/*00*/ char *SerializedItem; +/*xx*/ +}; + +struct Consume_Struct +{ +/*0000*/ int32 slot; +/*0004*/ int32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click +/*0008*/ int8 c_unknown1[4]; +/*0012*/ int8 type; // 0x01=Food 0x02=Water +/*0013*/ int8 unknown13[3]; +}; + + +struct MoveItem_Struct +{ +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +}; + +// +// from_slot/to_slot +// -1 - destroy +// 0 - cursor +// 1 - inventory +// 2 - bank +// 3 - trade +// 4 - shared bank +// +// cointype +// 0 - copeer +// 1 - silver +// 2 - gold +// 3 - platinum +// +static const uint32 COINTYPE_PP = 3; +static const uint32 COINTYPE_GP = 2; +static const uint32 COINTYPE_SP = 1; +static const uint32 COINTYPE_CP = 0; + +struct MoveCoin_Struct +{ + sint32 from_slot; + sint32 to_slot; + uint32 cointype1; + uint32 cointype2; + sint32 amount; +}; +struct TradeCoin_Struct{ + int32 trader; + int8 slot; + int16 unknown5; + int8 unknown7; + int32 amount; +}; +struct TradeMoneyUpdate_Struct{ + int32 trader; + int32 type; + int32 amount; +}; +/* +** Surname struct +** Size: 100 bytes +*/ +struct Surname_Struct +{ +/*0000*/ char name[64]; +/*0064*/ uint32 unknown0064; +/*0068*/ char lastname[32]; +/*0100*/ +}; + +struct GuildsListEntry_Struct { + char name[64]; +}; + +static const uint32 MAX_NUMBER_GUILDS = 1500; +struct GuildsList_Struct { + int8 head[64]; // First on guild list seems to be empty... + GuildsListEntry_Struct Guilds[MAX_NUMBER_GUILDS]; +}; + +struct GuildUpdate_Struct { + int32 guildID; + GuildsListEntry_Struct entry; +}; + +/* +** Money Loot +** Length: 22 Bytes +** OpCode: 5020 +*/ +struct moneyOnCorpseStruct { +/*0000*/ uint8 response; // 0 = someone else is, 1 = OK, 2 = not at this time +/*0001*/ uint8 unknown1; // = 0x5a +/*0002*/ uint8 unknown2; // = 0x40 +/*0003*/ uint8 unknown3; // = 0 +/*0004*/ uint32 platinum; // Platinum Pieces +/*0008*/ uint32 gold; // Gold Pieces + +/*0012*/ uint32 silver; // Silver Pieces +/*0016*/ uint32 copper; // Copper Pieces +}; + +//opcode = 0x5220 +// size 292 + + +struct LootingItem_Struct { +/*000*/ int32 lootee; +/*002*/ int32 looter; +/*004*/ int16 slot_id; +/*006*/ int8 unknown3[2]; +/*008*/ int32 auto_loot; +}; + +struct GuildManageStatus_Struct{ + int32 guild_id; + int32 oldrank; + int32 newrank; + char name[64]; +}; +// Guild invite, remove +struct GuildJoin_Struct{ +/*000*/ int32 guild_id; +/*004*/ int32 unknown04; +/*008*/ int32 level; +/*012*/ int32 class_; +/*016*/ int32 rank;//0 member, 1 officer, 2 leader +/*020*/ int32 zoneid; +/*024*/ int32 unknown24; +/*028*/ char name[64]; +/*092*/ +}; +struct GuildInviteAccept_Struct { + char inviter[64]; + char newmember[64]; + int32 response; + int32 guildeqid; +}; +struct GuildManageRemove_Struct { + int32 guildeqid; + char member[64]; +}; +struct GuildCommand_Struct { + char othername[64]; + char myname[64]; + int16 guildeqid; + int8 unknown[2]; // for guildinvite all 0's, for remove 0=0x56, 2=0x02 + int32 officer; +}; + +// 4244 bytes. Is not really an 'OnLevelMessage', it causes a popup box to display in the client +// Text looks like HTML. +struct OnLevelMessage_Struct { +/*0000*/ char Title[128]; +/*0128*/ char Text[4096]; +/*4224*/ uint32 Buttons; +/*4228*/ uint32 unknown4228; +/*4232*/ uint32 PopupID; +/*4236*/ uint32 unknown4236; +/*4240*/ uint32 unknown4240; +}; + +struct PopupResponse_Struct { +/*0000*/ int32 unknown0000; +/*0004*/ int32 popupid; +}; + +struct GuildManageBanker_Struct { + uint32 unknown0; + char myname[64]; + char member[64]; + uint32 enabled; +}; + +// Opcode OP_GMZoneRequest +// Size = 88 bytes +struct GMZoneRequest_Struct { +/*0000*/ char charname[64]; +/*0064*/ int32 zone_id; +/*0068*/ float x; +/*0072*/ float y; +/*0076*/ float z; +/*0080*/ char unknown0080[4]; +/*0084*/ int32 success; // 0 if command failed, 1 if succeeded? +/*0088*/ +// /*072*/ sint8 success; // =0 client->server, =1 server->client, -X=specific error +// /*073*/ int8 unknown0073[3]; // =0 ok, =ffffff error +}; + +struct GMSummon_Struct { +/* 0*/ char charname[64]; +/* 30*/ char gmname[64]; +/* 60*/ int32 success; +/* 61*/ int32 zoneID; +/*92*/ float y; +/*96*/ float x; +/*100*/ float z; +/*104*/ int32 unknown2; // E0 E0 56 00 +}; + +struct GMGoto_Struct { // x,y is swapped as compared to summon and makes sense as own packet +/* 0*/ char charname[64]; + +/* 64*/ char gmname[64]; +/* 128*/ int32 success; +/* 132*/ int32 zoneID; + +/*136*/ sint32 y; +/*140*/ sint32 x; +/*144*/ sint32 z; +/*148*/ int32 unknown2; // E0 E0 56 00 +}; + +struct GMLastName_Struct { + char name[64]; + char gmname[64]; + char lastname[64]; + int16 unknown[4]; // 0x00, 0x00 + // 0x01, 0x00 = Update the clients +}; + +//Combat Abilities +struct CombatAbility_Struct { + int32 m_target; //the ID of the target mob + int32 m_atk; + int32 m_skill; +}; + +struct DeleteItem_Struct { +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +}; + +//Instill Doubt +struct Instill_Doubt_Struct { + int8 i_id; + int8 ia_unknown; + int8 ib_unknown; + int8 ic_unknown; + int8 i_atk; + + int8 id_unknown; + int8 ie_unknown; + int8 if_unknown; + int8 i_type; + int8 ig_unknown; + int8 ih_unknown; + int8 ii_unknown; +}; + +struct GiveItem_Struct { + uint16 to_entity; + sint16 to_equipSlot; + uint16 from_entity; + sint16 from_equipSlot; +}; + +struct RandomReq_Struct { + int32 low; + int32 high; +}; + +/* solar: 9/23/03 reply to /random command; struct from Zaphod */ +struct RandomReply_Struct { +/* 00 */ int32 low; +/* 04 */ int32 high; +/* 08 */ int32 result; +/* 12 */ char name[64]; +/* 76 */ +}; + +struct LFG_Struct { +/* +Wrong size on OP_LFG. Got: 80, Expected: 68 + 0: 00 00 00 00 01 00 00 00 - 00 00 00 00 64 00 00 00 | ............d... + 16: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 48: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 64: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ +Wrong size on OP_LFG. Got: 80, Expected: 68 + 0: 00 00 00 00 01 00 00 00 - 3F 00 00 00 41 00 00 00 | ........?...A... + 16: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 48: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 64: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ +Wrong size on OP_LFG. Got: 80, Expected: 68 + 0: 00 00 00 00 01 00 00 00 - 3F 00 00 00 41 00 00 00 | ........?...A... + 16: 46 72 75 62 20 66 72 75 - 62 20 66 72 75 62 00 00 | Frub frub frub.. + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 48: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 64: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ +*/ +/*000*/ uint32 unknown000; +/*004*/ uint8 value; // 0x00 = off 0x01 = on +/*005*/ uint8 MatchFilter; +/*006*/ uint16 Unknown006; +/*008*/ uint32 FromLevel; +/*012*/ uint32 ToLevel; +/*016*/ char Comments[64]; +}; + +struct LFGGetMatchesRequest_Struct { +/*000*/ uint32 Unknown000; +/*004*/ uint32 FromLevel; +/*008*/ uint32 ToLevel; +/*012*/ uint32 Classes; +}; + +enum { LFPOff = 0, + LFPOn = 1, + LFPMemberUpdate = 255 // Internal use, not sent by client +}; + +struct LFP_Struct { +/*000*/ uint32 Unknown000; +/*004*/ uint8 Action; +/*005*/ uint8 MatchFilter; +/*006*/ uint16 Unknown006; +/*008*/ uint32 FromLevel; +/*012*/ uint32 ToLevel; +/*016*/ uint32 Classes; +/*020*/ char Comments[64]; +}; + +struct LFPGetMatchesRequest_Struct { +/*000*/ uint32 Unknown000; +/*004*/ uint32 FromLevel; +/*008*/ uint32 ToLevel; +}; + +/* +** LFG_Appearance_Struct +** Packet sent to clients to notify when someone in zone toggles LFG flag +** Size: 8 bytes +** Used in: OP_LFGAppearance +** +*/ +struct LFG_Appearance_Struct +{ +/*0000*/ uint32 spawn_id; // ID of the client +/*0004*/ uint8 lfg; // 1=LFG, 0=Not LFG +/*0005*/ char unknown0005[3]; // +/*0008*/ +}; + + +// EverQuest Time Information: +// 72 minutes per EQ Day +// 3 minutes per EQ Hour +// 6 seconds per EQ Tick (2 minutes EQ Time) +// 3 seconds per EQ Minute + +struct TimeOfDay_Struct { + int8 hour; + int8 minute; + int8 day; + int8 month; + int32 year; +}; + +// Darvik: shopkeeper structs +struct Merchant_Click_Struct { +/*000*/ int32 npcid; // Merchant NPC's entity id +/*004*/ int32 playerid; +/*008*/ int32 command; //1=open, 0=cancel/close +/*012*/ float rate; //cost multiplier, dosent work anymore +}; +/* +Unknowns: +0 is e7 from 01 to // MAYBE SLOT IN PURCHASE +1 is 03 +2 is 00 +3 is 00 +4 is ?? +5 is ?? +6 is 00 from a0 to +7 is 00 from 3f to */ +/* +0 is F6 to 01 +1 is CE CE +4A 4A +00 00 +00 E0 +00 CB +00 90 +00 3F +*/ + + + +struct Merchant_Sell_Struct { +/*000*/ int32 npcid; // Merchant NPC's entity id +/*004*/ int32 playerid; // Player's entity id +/*008*/ int32 itemslot; + int32 unknown12; +/*016*/ int8 quantity; // Already sold +/*017*/ int8 Unknown016[3]; +/*020*/ int32 price; +}; +struct Merchant_Purchase_Struct { +/*000*/ int32 npcid; // Merchant NPC's entity id +/*004*/ int32 itemslot; // Player's entity id +/*008*/ int32 quantity; +/*012*/ int32 price; +}; +struct Merchant_DelItem_Struct{ +/*000*/ int32 npcid; // Merchant NPC's entity id +/*004*/ int32 playerid; // Player's entity id +/*008*/ int32 itemslot; +/*012*/ uint32 unknown012; +}; +struct Adventure_Purchase_Struct { +/*000*/ int32 some_flag; //set to 1 generally... +/*000*/ int32 npcid; +/*004*/ int32 itemid; +/*008*/ int32 variable; +}; + +struct Adventure_Sell_Struct { +/*000*/ int32 unknown000; //0x01 +/*004*/ int32 npcid; +/*008*/ int32 slot; +/*012*/ int32 charges; +/*016*/ int32 sell_price; +}; + + +struct AdventurePoints_Update_Struct { +/*000*/ uint32 ldon_available_points; // Total available points +/*004*/ uint8 unknown004[36]; +/*040*/ uint8 unknown040[16]; +/*056*/ uint32 ldon_guk_points; // Earned Deepest Guk points +/*060*/ uint8 unknown060[16]; +/*076*/ uint32 ldon_mirugal_points; // Earned Mirugal' Menagerie points +/*080*/ uint8 unknown080[16]; +/*096*/ uint32 ldon_mistmoore_points; // Earned Mismoore Catacombs Points +/*100*/ uint8 unknown100[16]; +/*116*/ uint32 ldon_rujarkian_points; // Earned Rujarkian Hills points +/*120*/ uint8 unknown120[16]; +/*136*/ uint32 ldon_takish_points; // Earned Takish points +/*140*/ +}; + +struct AdventureFinish_Struct{ +/*000*/ uint32 win_lose;//Cofruben: 00 is a lose,01 is win. +/*004*/ uint32 points; +/*008*/ +}; +//OP_AdventureRequest +struct AdventureRequest_Struct{ +/*000*/ int32 risk;//1 normal,2 hard. +/*004*/ int32 entity_id; +/*008*/ int32 type; +/*012*/ +}; +struct AdventureRequestResponse_Struct{ +/*0000*/ int32 unknown000; +/*0004*/ char text[2048]; +/*2052*/ int32 timetoenter; +/*2056*/ int32 timeleft; +/*2060*/ int32 risk; +/*2064*/ float x; +/*2068*/ float y; +/*2072*/ float z; +/*2076*/ int32 showcompass; +/*2080*/ int32 unknown2080; +/*2084*/ +}; + +struct AdventureCountUpdate_Struct +{ +/*000*/ int32 current; +/*004*/ int32 total; +/*008*/ +}; + +struct AdventureStatsColumn_Struct +{ +/*000*/ int32 total; +/*004*/ int32 guk; +/*008*/ int32 mir; +/*012*/ int32 mmc; +/*016*/ int32 ruj; +/*020*/ int32 tak; +/*024*/ +}; + +struct AdventureStats_Struct +{ +/*000*/ AdventureStatsColumn_Struct success; +/*024*/ AdventureStatsColumn_Struct failure; +/*048*/ AdventureStatsColumn_Struct rank; +/*072*/ AdventureStatsColumn_Struct rank2; +/*096*/ +}; + +//this is mostly right but something is off that causes the client to crash sometimes +//I don't really care enough about the feature to work on it anymore though. +struct AdventureLeaderboardEntry_Struct +{ +/*004*/ char name[64]; +/*008*/ int32 success; +/*012*/ int32 failure; +/*016*/ +}; + +struct AdventureLeaderboard_Struct +{ +/*000*/ int32 unknown000; +/*004*/ int32 unknown004; +/*008*/ int32 success; +/*012*/ int32 failure; +/*016*/ int32 our_rank; +/*020*/ +}; + +/*struct Item_Shop_Struct { + uint16 merchantid; + int8 itemtype; + Item_Struct item; + int8 iss_unknown001[6]; +};*/ + +struct Illusion_Struct { //size: 256 - SoF +/*000*/ uint32 spawnid; +/*004*/ char charname[64]; // +/*068*/ uint16 race; // race +/*070*/ char unknown006[2]; +/*072*/ uint8 gender; +/*073*/ uint8 texture; +/*074*/ uint8 unknown008; // +/*075*/ uint8 unknown009; // +/*076*/ uint8 helmtexture; // Verified +/*077*/ uint8 unknown010; // +/*078*/ uint8 unknown011; // +/*079*/ uint8 unknown012; // +/*080*/ uint32 face; // Verified +/*084*/ uint8 hairstyle; // Verified +/*085*/ uint8 haircolor; // Verified +/*085*/ uint8 beard; // Verified +/*087*/ uint8 beardcolor; // Verified +/*088*/ uint32 drakkin_heritage; // Temp Placeholder until field is identified in SoF +/*092*/ uint32 drakkin_tattoo; // Temp Placeholder until field is identified in SoF +/*096*/ uint32 drakkin_details; // Temp Placeholder until field is identified in SoF +/*100*/ uint32 armor_tint; // Temp Placeholder until field is identified in SoF +/*104*/ uint8 eyecolor1; // Temp Placeholder until field is identified in SoF +/*105*/ uint8 eyecolor2; // Temp Placeholder until field is identified in SoF +/*106*/ uint8 unknown106[150]; //was uint8 unknown021[168]; +/*256*/ +}; + +struct Illusion_Struct_Old { +/*000*/ uint32 spawnid; + char charname[64]; +/**/ uint16 race; +/**/ char unknown006[2]; +/**/ uint8 gender; +/**/ uint8 texture; +/**/ uint8 helmtexture; +/**/ uint8 unknown011; +/**/ uint32 face; +/**/ char unknown020[88]; +/**/ +}; + +struct ZonePoint_Entry { +/*0000*/ int32 iterator; +/*0004*/ float y; +/*0008*/ float x; +/*0012*/ float z; +/*0016*/ float heading; +/*0020*/ int16 zoneid; +/*0022*/ int16 zoneinstance; // LDoN instance +}; + +struct ZonePoints { +/*0000*/ int32 count; +/*0004*/ struct ZonePoint_Entry *zpe; // Always add one extra to the end after all zonepoints +}; + +struct SkillUpdate_Struct { +/*00*/ uint32 skillId; +/*04*/ uint32 value; +/*08*/ +}; + +struct ZoneUnavail_Struct { + //This actually varies, but... + char zonename[16]; + sint16 unknown[4]; +}; + +struct GroupGeneric_Struct { + char name1[64]; + char name2[64]; +}; + +struct GroupCancel_Struct { + char name1[64]; + char name2[64]; + uint8 toggle; +}; + +struct GroupUpdate_Struct { +/*0000*/ int32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +}; + +struct GroupUpdate2_Struct { +/*0000*/ int32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[5][64]; +/*0388*/ char leadersname[64]; +/*0452*/ GroupLeadershipAA_Struct leader_aas; +/*0580*/ int8 unknown580[196]; +/*0766*/ uint32 NPCMarkerID; // EntityID of player delegated MarkNPC ability +/*0780*/ int8 unknown780[56]; +/*0836*/ +}; +struct GroupJoin_Struct { +/*0000*/ int32 action; +/*0004*/ char yourname[64]; +/*0068*/ char membername[64]; +/*0132*/ GroupLeadershipAA_Struct leader_aas; +/*0196*/ int8 unknown196[196]; +/*0392*/ uint32 NPCMarkerID; // EntityID of player delegated MarkNPC ability +/*0396*/ int8 unknown396[56]; +/*0452*/ +}; + +struct GroupFollow_Struct { // SoF Follow Struct +/*0000*/ char name1[64]; // inviter +/*0064*/ char name2[64]; // invitee +/*0128*/ int32 unknown0128; +/*0132*/ +}; + +struct FaceChange_Struct { +/*000*/ int8 haircolor; +/*001*/ int8 beardcolor; +/*002*/ int8 eyecolor1; // the eyecolors always seem to be the same, maybe left and right eye? +/*003*/ int8 eyecolor2; +/*004*/ int8 hairstyle; +/*005*/ int8 beard; +/*006*/ int8 face; +/*007*/ int32 drakkin_heritage; +/*011*/ int32 drakkin_tattoo; +/*015*/ int32 drakkin_details; +//there are only 10 faces for barbs changing woad just +//increase the face value by ten so if there were 8 woad +//designs then there would be 80 barb faces +}; + +/* +** Trade request from one client to another +** Used to initiate a trade +** Size: 8 bytes +** Used in: OP_TradeRequest +*/ +struct TradeRequest_Struct { +/*00*/ uint32 to_mob_id; +/*04*/ uint32 from_mob_id; +/*08*/ +}; + +struct TradeAccept_Struct { +/*00*/ uint32 from_mob_id; +/*04*/ uint32 unknown4; //seems to be garbage +/*08*/ +}; + +/* +** Cancel Trade struct +** Sent when a player cancels a trade +** Size: 8 bytes +** Used In: OP_CancelTrade +** +*/ +struct CancelTrade_Struct { +/*00*/ int32 fromid; +/*04*/ int32 action; +/*08*/ +}; + +struct PetitionUpdate_Struct { + int32 petnumber; // Petition Number + int32 color; // 0x00 = green, 0x01 = yellow, 0x02 = red + int32 status; + time_t senttime; // 4 has to be 0x1F + char accountid[32]; + char gmsenttoo[64]; + sint32 quetotal; + char charname[64]; +}; + +struct Petition_Struct { + int32 petnumber; + int32 urgency; + char accountid[32]; + char lastgm[32]; + int32 zone; + //char zone[32]; + char charname[64]; + int32 charlevel; + int32 charclass; + int32 charrace; + int32 unknown; + //time_t senttime; // Time? + int32 checkouts; + int32 unavail; + //int8 unknown5[4]; + time_t senttime; + int32 unknown2; + char petitiontext[1024]; + char gmtext[1024]; +}; + + +struct Who_All_Struct { // 76 length total +/*000*/ char whom[64]; +/*064*/ int32 wrace; // FF FF = no race + +/*068*/ int32 wclass; // FF FF = no class +/*072*/ int32 lvllow; // FF FF = no numbers +/*076*/ int32 lvlhigh; // FF FF = no numbers +/*080*/ int32 gmlookup; // FF FF = not doing /who all gm +/*084*/ int32 guildid; +/*088*/ int8 unknown076[64]; +/*152*/ int32 type; // New for SoF. 0 = /who 3 = /who all +/*156*/ +}; + +struct Stun_Struct { // 4 bytes total + int32 duration; // Duration of stun +}; + +struct AugmentItem_Struct { +/*00*/ sint16 container_slot; +/*02*/ char unknown02[2]; +/*04*/ sint32 augment_slot; +/*08*/ +}; + +// OP_Emote +struct Emote_Struct { +/*0000*/ int32 unknown01; +/*0004*/ char message[1024]; +/*1028*/ +}; + +// Inspect +struct Inspect_Struct { + int16 TargetID; + int16 PlayerID; +}; +//OP_InspectAnswer +struct InspectResponse_Struct{//Cofruben:need to send two of this for the inspect response. +/*000*/ int32 TargetID; +/*004*/ int32 playerid; +/*008*/ char itemnames[21][64]; +/*1352*/char unknown_zero[64];//fill with zero's. +/*1416*/int32 itemicons[21]; +/*1500*/int32 unknown_zero2; +/*1504*/char text[288]; +}; + +//OP_SetDataRate +struct SetDataRate_Struct { + float newdatarate; +}; + +//OP_SetServerFilter +struct SetServerFilter_Struct { + uint32 filters[29]; //see enum eqFilterType +}; + +//Op_SetServerFilterAck +struct SetServerFilterAck_Struct { + int8 blank[8]; +}; +struct IncreaseStat_Struct{ + /*0000*/ int8 unknown0; + /*0001*/ int8 str; + /*0002*/ int8 sta; + /*0003*/ int8 agi; + /*0004*/ int8 dex; + /*0005*/ int8 int_; + /*0006*/ int8 wis; + /*0007*/ int8 cha; + /*0008*/ int8 fire; + /*0009*/ int8 cold; + /*0010*/ int8 magic; + /*0011*/ int8 poison; + /*0012*/ int8 disease; + /*0013*/ char unknown13[116]; + /*0129*/ int8 str2; + /*0130*/ int8 sta2; + /*0131*/ int8 agi2; + /*0132*/ int8 dex2; + /*0133*/ int8 int_2; + /*0134*/ int8 wis2; + /*0135*/ int8 cha2; + /*0136*/ int8 fire2; + /*0137*/ int8 cold2; + /*0138*/ int8 magic2; + /*0139*/ int8 poison2; + /*0140*/ int8 disease2; +}; + +struct GMName_Struct { + char oldname[64]; + char gmname[64]; + char newname[64]; + int8 badname; + int8 unknown[3]; +}; + +struct GMDelCorpse_Struct { + char corpsename[64]; + char gmname[64]; + int8 unknown; +}; + +struct GMKick_Struct { + char name[64]; + char gmname[64]; + int8 unknown; +}; + + +struct GMKill_Struct { + char name[64]; + char gmname[64]; + int8 unknown; +}; + + +struct GMEmoteZone_Struct { + char text[512]; +}; + +// This is where the Text is sent to the client. +// Use ` as a newline character in the text. +// Variable length. +struct BookText_Struct { + uint8 window; // where to display the text (0xFF means new window) + uint8 type; //type: 0=scroll, 1=book, 2=item info.. prolly others. + uint32 invslot; // Only used in SoF and later clients. + char booktext[1]; // Variable Length +}; +// This is the request to read a book. +// This is just a "text file" on the server +// or in our case, the 'name' column in our books table. +struct BookRequest_Struct { + uint8 window; // where to display the text (0xFF means new window) + uint8 type; //type: 0=scroll, 1=book, 2=item info.. prolly others. + uint32 invslot; // Only used in Sof and later clients; + char txtfile[20]; +}; + +/* +** Object/Ground Spawn struct +** Used for Forges, Ovens, ground spawns, items dropped to ground, etc +** Size: 92 bytes +** OpCodes: OP_CreateObject +** Last Updated: Oct-17-2003 +** +*/ +struct Object_Struct { +/*00*/ uint32 linked_list_addr[2];// They are, get this, prev and next, ala linked list +/*08*/ uint16 unknown008[2]; // +/*12*/ uint32 drop_id; // Unique object id for zone +/*16*/ uint16 zone_id; // Redudant, but: Zone the object appears in +/*18*/ uint16 zone_instance; // +/*20*/ uint32 unknown020; // +/*24*/ uint32 unknown024; // +/*28*/ float heading; // heading +/*32*/ float z; // z coord +/*36*/ float x; // x coord +/*40*/ float y; // y coord +/*44*/ char object_name[20]; // Name of object, usually something like IT63_ACTORDEF +/*64*/ float unknown064; // seems like coords, not always valid, all 0 on most world objects +/*68*/ float unknown068; // seems like coords, not always valid, all 0 on most world objects +/*72*/ float unknown072; // seems like coords, not always valid, all 0 on most world objects +/*76*/ uint32 unknown076; // +/*80*/ uint32 object_type; // Type of object, not directly translated to OP_OpenObject +/*84*/ uint32 unknown084; //set to 0xFF +/*88*/ uint32 spawn_id; // Spawn Id of client interacting with object +/*92*/ +}; +// 01 = generic drop, 02 = armor, 19 = weapon +//[13:40] and 0xff seems to be indicative of the tradeskill/openable items that end up returning the old style item type in the OP_OpenObject + +/* +** Click Object Struct +** Client clicking on zone object (forge, groundspawn, etc) +** Size: 8 bytes +** Last Updated: Oct-17-2003 +** +*/ +struct ClickObject_Struct { +/*00*/ uint32 drop_id; +/*04*/ uint32 player_id; +/*08*/ +}; + +struct Shielding_Struct { + uint32 target_id; +}; + +/* +** Click Object Action Struct +** Response to client clicking on a World Container (ie, forge) +* also sent by the client when they close the container. +** +*/ +struct ClickObjectAction_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 type; // See object.h, "Object Types" +/*16*/ uint32 unknown16; // set to 0xA +/*20*/ uint32 icon; // Icon to display for tradeskill containers +/*24*/ uint32 unknown24; // +/*28*/ char object_name[64]; // Object name to display +/*92*/ +}; + +/* +** This is different now, mostly unknown +** +*/ +struct CloseContainer_Struct { +/*00*/ uint32 player_id; // Entity Id of player who clicked object +/*04*/ uint32 drop_id; // Zone-specified unique object identifier +/*08*/ uint32 open; // 1=opening, 0=closing +/*12*/ uint32 unknown12[12]; +}; + +/* +** Generic Door Struct +** Length: 52 Octets +** Used in: +** cDoorSpawnsStruct(f721) +** +*/ +struct Door_Struct +{ +/*0000*/ char name[32]; // Filename of Door // Was 10char long before... added the 6 in the next unknown to it: Daeken M. BlackBlade //changed both to 32: Trevius +/*0032*/ float yPos; // y loc +/*0036*/ float xPos; // x loc +/*0040*/ float zPos; // z loc +/*0044*/ float heading; +/*0048*/ int32 incline; // rotates the whole door +/*0052*/ int16 size; // 100 is normal, smaller number = smaller model +/*0054*/ int8 unknown0038[6]; +/*0060*/ uint8 doorId; // door's id # +/*0061*/ uint8 opentype; +/* + * Open types: + * 66 = PORT1414 (Qeynos) + * 55 = BBBOARD (Qeynos) + * 100 = QEYLAMP (Qeynos) + * 56 = CHEST1 (Qeynos) + * 5 = DOOR1 (Qeynos) + */ +/*0062*/ uint8 state_at_spawn; +/*0063*/ uint8 invert_state; // if this is 1, the door is normally open +/*0064*/ int32 door_param; +/*0068*/ uint8 unknown0052[12]; // mostly 0s, the last 3 bytes are something tho +/*0080*/ +}; + + + +struct DoorSpawns_Struct { + struct Door_Struct *doors; +}; + +/* + OP Code: Op_ClickDoor + Size: 16 + + 10/10/2003-Doodman Filled in struct +*/ +struct ClickDoor_Struct { +/*000*/ int32 doorid; +/*004*/ int8 picklockskill; +/*005*/ int8 unknown005[3]; +/*008*/ int32 item_id; +/*012*/ int16 player_id; +/*014*/ int8 unknown014[2]; +}; + +struct MoveDoor_Struct { + int8 doorid; + int8 action; +}; + + +struct BecomeNPC_Struct { + int32 id; + sint32 maxlevel; +}; + +struct Underworld_Struct { + float speed; + float y; + float x; + float z; +}; + +struct Resurrect_Struct { +/*000*/ int32 unknown000; +/*004*/ int16 zone_id; +/*006*/ int16 instance_id; +/*008*/ float y; +/*012*/ float x; +/*016*/ float z; +/*020*/ int32 unknown020; +/*024*/ char your_name[64]; +/*088*/ int32 unknown088; +/*092*/ char rezzer_name[64]; +/*156*/ int32 spellid; +/*160*/ char corpse_name[64]; +/*224*/ int32 action; +/* 228 */ +}; + +struct Translocate_Struct { +/*000*/ int32 ZoneID; +/*004*/ int32 SpellID; +/*008*/ int32 unknown008; //Heading ? +/*012*/ char Caster[64]; +/*076*/ float y; +/*080*/ float x; +/*084*/ float z; +/*088*/ uint32 Complete; +}; + +struct Sacrifice_Struct { +/*000*/ uint32 CasterID; +/*004*/ uint32 TargetID; +/*008*/ uint32 Confirm; +}; + +struct SetRunMode_Struct { + int8 mode; + int8 unknown[3]; +}; + +//EnvDamage is EnvDamage2 without a few bytes at the end. + +struct EnvDamage2_Struct { +/*0000*/ int32 id; +/*0004*/ int16 unknown4; +/*0006*/ int32 damage; +/*0010*/ int8 unknown10[12]; +/*0022*/ int8 dmgtype; //FA = Lava; FC = Falling +/*0023*/ int8 unknown2[4]; +/*0027*/ int16 constant; //Always FFFF +/*0029*/ int16 unknown29; +}; + +//Bazaar Stuff =D +// + +enum { + BazaarTrader_StartTraderMode = 1, + BazaarTrader_EndTraderMode = 2, + BazaarTrader_UpdatePrice = 3, + BazaarTrader_EndTransaction = 4, + BazaarSearchResults = 7, + BazaarWelcome = 9, + BazaarBuyItem = 10, + BazaarTrader_ShowItems = 11, + BazaarSearchDone = 12, + BazaarTrader_CustomerBrowsing = 13 +}; + +enum { + BazaarPriceChange_Fail = 0, + BazaarPriceChange_UpdatePrice = 1, + BazaarPriceChange_RemoveItem = 2, + BazaarPriceChange_AddItem = 3 +}; + +struct BazaarWindowStart_Struct { + int8 Action; + int8 Unknown001; + int16 Unknown002; +}; + + +struct BazaarWelcome_Struct { + BazaarWindowStart_Struct Beginning; + int32 Traders; + int32 Items; + int8 Unknown012[8]; +}; + +struct BazaarSearch_Struct { + BazaarWindowStart_Struct Beginning; + int32 TraderID; + int32 Class_; + int32 Race; + int32 ItemStat; + int32 Slot; + int32 Type; + char Name[64]; + int32 MinPrice; + int32 MaxPrice; + int32 Minlevel; + int32 MaxLlevel; +}; +struct BazaarInspect_Struct{ + int32 ItemID; + int32 Unknown004; + char Name[64]; +}; + +struct NewBazaarInspect_Struct { +/*000*/ BazaarWindowStart_Struct Beginning; +/*004*/ char Name[64]; +/*068*/ int32 Unknown068; +/*072*/ int32 Unknown072; +/*076*/ int32 Unknown076; +/*080*/ sint32 SerialNumber; +/*084*/ int32 Unknown084; +}; + +struct BazaarReturnDone_Struct{ + int32 Type; + int32 TraderID; + int32 Unknown008; + int32 Unknown012; + int32 Unknown016; +}; +struct BazaarSearchResults_Struct { + BazaarWindowStart_Struct Beginning; + int32 NumItems; + int32 ItemID; + int32 SellerID; + int32 Cost; + int32 ItemStat; + char Name[64]; +}; + +// Barter/Buyer +// +// +enum { + Barter_BuyerSearch = 0, + Barter_SellerSearch = 1, + Barter_BuyerModeOn = 2, + Barter_BuyerModeOff = 3, + Barter_BuyerItemUpdate = 5, + Barter_BuyerItemRemove = 6, + Barter_SellItem = 7, + Barter_SellerTransactionComplete = 8, + Barter_BuyerTransactionComplete = 9, + Barter_BuyerInspectBegin = 10, + Barter_BuyerInspectEnd = 11, + Barter_BuyerAppearance = 12, + Barter_BuyerInspectWindow = 13, + Barter_BarterItemInspect = 14, + Barter_SellerBrowsing = 15, + Barter_BuyerSearchResults = 16, + Barter_Welcome = 17, + Barter_WelcomeMessageUpdate = 19, + Barter_BuyerItemInspect = 21 +}; + +struct BuyerWelcomeMessageUpdate_Struct { +/*000*/ uint32 Action; +/*004*/ char WelcomeMessage[256]; +}; + +struct BuyerItemSearch_Struct { +/*000*/ uint32 Unknown000; +/*004*/ char SearchString[64]; +}; + +struct BuyerItemSearchResultEntry_Struct { +/*000*/ char ItemName[64]; +/*064*/ uint32 ItemID; +/*068*/ uint32 Unknown068; +/*072*/ uint32 Unknown072; +}; + +#define MAX_BUYER_ITEMSEARCH_RESULTS 200 + +struct BuyerItemSearchResults_Struct { + uint32 Action; + uint32 ResultCount; + BuyerItemSearchResultEntry_Struct Results[MAX_BUYER_ITEMSEARCH_RESULTS]; +}; + +struct BarterSearchRequest_Struct { + uint32 Action; + char SearchString[64]; + uint32 SearchID; +}; + +struct BuyerItemSearchLinkRequest_Struct { +/*000*/ uint32 Action; // 0x00000015 +/*004*/ uint32 ItemID; +/*008*/ uint32 Unknown008; +/*012*/ uint32 Unknown012; +}; + +struct BarterItemSearchLinkRequest_Struct { +/*000*/ uint32 Action; // 0x0000000E +/*004*/ uint32 SearcherID; +/*008*/ uint32 Unknown008; +/*012*/ uint32 Unknown012; +/*016*/ uint32 ItemID; +/*020*/ uint32 Unknown020; +}; + +struct BuyerInspectRequest_Struct { + uint32 Action; + uint32 BuyerID; + uint32 Approval; +}; + +struct BuyerBrowsing_Struct { + uint32 Action; + char PlayerName[64]; +}; + +struct BuyerRemoveItem_Struct { + uint32 Action; + uint32 BuySlot; +}; + +struct ServerSideFilters_Struct { +int8 clientattackfilters; // 0) No, 1) All (players) but self, 2) All (players) but group +int8 npcattackfilters; // 0) No, 1) Ignore NPC misses (all), 2) Ignore NPC Misses + Attacks (all but self), 3) Ignores NPC Misses + Attacks (all but group) +int8 clientcastfilters; // 0) No, 1) Ignore PC Casts (all), 2) Ignore PC Casts (not directed towards self) +int8 npccastfilters; // 0) No, 1) Ignore NPC Casts (all), 2) Ignore NPC Casts (not directed towards self) +}; + +/* +** Client requesting item statistics +** Size: 48 bytes +** Used In: OP_ItemLinkClick +** Last Updated: 2/15/2009 +** +*/ +struct ItemViewRequest_Struct { +/*000*/ uint32 item_id; +/*004*/ uint32 augments[5]; +/*024*/ uint32 link_hash; +/*028*/ uint32 unknown028; +/*032*/ char unknown032[12]; //probably includes loregroup & evolving info. see Client::MakeItemLink() in zone/inventory.cpp:469 +/*044*/ uint16 icon; +/*046*/ char unknown046[2]; +}; + +struct LDONItemViewRequest_Struct { + uint32 item_id; + uint8 unknown004[4]; + char item_name[64]; +}; + +/* + * Client to server packet + */ +struct PickPocket_Struct { +// Size 18 + uint32 to; + uint32 from; + uint16 myskill; + uint8 type; // -1 you are being picked, 0 failed , 1 = plat, 2 = gold, 3 = silver, 4 = copper, 5 = item + uint8 unknown1; // 0 for response, unknown for input + uint32 coin; + uint8 lastsix[2]; +}; +/* + * Server to client packet + */ + +enum { + PickPocketFailed = 0, + PickPocketPlatinum = 1, + PickPocketGold = 2, + PickPocketSilver = 3, + PickPocketCopper = 4, + PickPocketItem = 5 +}; + + +struct sPickPocket_Struct { + // Size 28 = coin/fail + uint32 to; + uint32 from; + uint32 myskill; + uint32 type; + uint32 coin; + char itemname[64]; +}; + +struct LogServer_Struct { +// Op_Code OP_LOGSERVER +/*000*/ uint32 unknown000; +/*004*/ uint8 enable_pvp; +/*005*/ uint8 unknown005; +/*006*/ uint8 unknown006; +/*007*/ uint8 unknown007; +/*008*/ uint8 enable_FV; +/*009*/ uint8 unknown009; +/*010*/ uint8 unknown010; +/*011*/ uint8 unknown011; +/*012*/ uint32 unknown012; // htonl(1) on live +/*016*/ uint32 unknown016; // htonl(1) on live +/*020*/ uint8 unknown020[12]; +/*032*/ char worldshortname[32]; +/*064*/ uint8 unknown064[32]; +/*096*/ char unknown096[16]; // 'pacman' on live +/*112*/ char unknown112[16]; // '64.37,148,36' on live +/*126*/ uint8 unknown128[48]; +/*176*/ uint32 unknown176; // htonl(0x00002695) +/*180*/ char unknown180[80]; // 'eqdataexceptions@mail.station.sony.com' on live +/*260*/ uint8 enable_petition_wnd; +/*261*/ uint8 enablevoicemacros; +/*262*/ uint8 enablemail; +/*263*/ uint8 disable_tutorial_go_home; +/*264*/ +}; + +struct ApproveWorld_Struct { +// Size 544 +// Op_Code OP_ApproveWorld + uint8 unknown544[544]; +}; + +struct ClientError_Struct +{ +/*00001*/ char type; +/*00001*/ char unknown0001[69]; +/*00069*/ char character_name[64]; +/*00134*/ char unknown134[192]; +/*00133*/ char message[31994]; +/*32136*/ +}; + +struct MobHealth +{ + /*0000*/ int8 hp; //health percent + /*0001*/ int16 id;//mobs id +}; + +struct Track_Struct { + int16 entityid; + int16 padding002; + float distance; +}; + +struct Tracking_Struct { + Track_Struct *Entrys; +}; + +/* +** ZoneServerInfo_Struct +** Zone server information +** Size: 130 bytes +** Used In: OP_ZoneServerInfo +** +*/ +struct ZoneServerInfo_Struct +{ +/*0000*/ char ip[128]; +/*0128*/ uint16 port; +}; + +struct WhoAllPlayer{ + int32 formatstring; + int32 pidstring; + char* name; + int32 rankstring; + char* guild; + int32 unknown80[2]; + int32 zonestring; + int32 zone; + int32 class_; + int32 level; + int32 race; + char* account; + int32 unknown100; +}; + +// The following four structs are the WhoAllPlayer struct above broken down +// for use in World ClientList::SendFriendsWho to accomodate the user of variable +// length strings within the struct above. + +struct WhoAllPlayerPart1 { + int32 FormatMSGID; + int32 PIDMSGID; + char Name[1];; +}; + +struct WhoAllPlayerPart2 { + int32 RankMSGID; + char Guild[1]; +}; + +struct WhoAllPlayerPart3 { + int32 Unknown80[2]; + int32 ZoneMSGID; + int32 Zone; + int32 Class_; + int32 Level; + int32 Race; + char Account[1]; +}; + +struct WhoAllPlayerPart4 { + int32 Unknown100; +}; + +struct WhoAllReturnStruct { +/*000*/ int32 id; +/*004*/ int32 playerineqstring; +/*008*/ char line[27]; +/*035*/ int8 unknown35; //0A +/*036*/ int32 unknown36;//0s +/*040*/ int32 playersinzonestring; +/*044*/ int32 unknown44[2]; //0s +/*052*/ int32 unknown52;//1 +/*056*/ int32 unknown56;//1 +/*060*/ int32 playercount;//1 + struct WhoAllPlayer *player; +}; + +struct Trader_Struct { +/*000*/ int32 Code; +/*004*/ int32 Unknown004; +/*008*/ uint64 Items[80]; +/*648*/ int32 ItemCost[80]; +}; + +struct ClickTrader_Struct { +/*000*/ int32 Code; +/*004*/ int32 Unknown004; +/*008*/ sint64 SerialNumber[80]; +/*648*/ int32 ItemCost[80]; +}; + +struct GetItems_Struct{ + int32 Items[80]; + sint32 SerialNumber[80]; + sint32 Charges[80]; +}; + +struct BecomeTrader_Struct{ + int32 ID; + int32 Code; +}; + +struct Trader_ShowItems_Struct{ +/*000*/ int32 Code; +/*004*/ int32 TraderID; +/*008*/ int32 Unknown08[3]; +}; + +struct TraderBuy_Struct{ +/*000*/ int32 Action; +/*004*/ int32 TraderID; +/*008*/ int32 ItemID; +/*012*/ int32 AlreadySold; +/*016*/ int32 Price; +/*020*/ int32 Quantity; +/*024*/ char ItemName[64]; +}; + +struct TraderItemUpdate_Struct{ + int32 Unknown000; + int32 TraderID; + int8 FromSlot; + int ToSlot; //7? + int16 Charges; +}; + +struct TraderPriceUpdate_Struct { +/*000*/ int32 Action; +/*004*/ int32 SubAction; +/*008*/ sint32 SerialNumber; +/*012*/ int32 Unknown012; +/*016*/ int32 NewPrice; +/*020*/ int32 Unknown016; +}; + +struct MoneyUpdate_Struct{ + sint32 platinum; + sint32 gold; + sint32 silver; + sint32 copper; +}; + +struct TraderDelItem_Struct{ + int32 Unknown000; + int32 TraderID; + int32 ItemID; + int32 Unknown012; +}; + +struct TraderClick_Struct{ +/*000*/ int32 TraderID; +/*004*/ int32 Unknown004; +/*008*/ int32 Unknown008; +/*012*/ int32 Approval; +}; + +struct FormattedMessage_Struct{ + int32 unknown0; + int32 string_id; + int32 type; + char *message; +}; +struct SimpleMessage_Struct{ + int32 string_id; + int32 color; + int32 unknown8; +}; + +struct GuildMemberUpdate_Struct { +/*00*/ int32 guild_id; +/*04*/ char member_name[64]; +/*68*/ int16 zone_id; +/*70*/ int16 instance_id; //speculated +/*72*/ int32 some_timestamp; //unix timestamp +/*76*/ +}; + +struct GuildMemberLevelUpdate_Struct { +/*00*/ uint32 guild_id; +/*04*/ char member_name[64]; +/*68*/ uint32 level; //not sure +}; + +struct Internal_GuildMemberEntry_Struct { +// char name[64]; //variable length + int32 level; //network byte order + int32 banker; //1=yes, 0=no, network byte order + int32 class_; //network byte order + int32 rank; //network byte order + int32 time_last_on; //network byte order + int32 tribute_enable; //network byte order + int32 total_tribute; //total guild tribute donated, network byte order + int32 last_tribute; //unix timestamp +// char public_note[1]; //variable length. + int16 zoneinstance; //network byte order + int16 zone_id; //network byte order +}; + +struct Internal_GuildMembers_Struct { //just for display purposes, this is not actually used in the message encoding. + char player_name[64]; //variable length. + int32 count; //network byte order + int32 name_length; //total length of all of the char names, excluding terminators + int32 note_length; //total length of all the public notes, excluding terminators + Internal_GuildMemberEntry_Struct *member; + /* + * followed by a set of `count` null terminated name strings + * and then a set of `count` null terminated public note strings + */ +}; + +struct GuildMOTD_Struct{ +/*0000*/ int32 unknown0; +/*0004*/ char name[64]; +/*0068*/ char setby_name[64]; +/*0132*/ int32 unknown132; +/*0136*/ char motd[512]; +}; + +struct GuildUpdate_PublicNote{ + int32 unknown0; + char name[64]; + char target[64]; + char note[1]; //variable length. +}; + +struct GuildDemoteStruct{ + char name[64]; + char target[64]; +}; + +struct GuildRemoveStruct{ + char target[64]; + char name[64]; + int32 unknown128; + int32 leaderstatus; //? +}; + +struct GuildMakeLeader{ + char name[64]; + char target[64]; +}; + +struct BugStruct{ +/*0000*/ char chartype[64]; +/*0064*/ char name[96]; +/*0160*/ char ui[128]; +/*0288*/ float x; +/*0292*/ float y; +/*0296*/ float z; +/*0300*/ float heading; +/*0304*/ int32 unknown304; +/*0308*/ char unknown308[160]; +/*0468*/ char target_name[64]; +/*0532*/ int32 type; +/*0536*/ char unknown536[2052]; +/*2584*/ char bug[2048]; +/*4632*/ char unknown4632[6]; +/*4638*/ char system_info[4094]; +}; +struct Make_Pet_Struct { //Simple struct for getting pet info + int8 level; + int8 class_; + int16 race; + int8 texture; + int8 pettype; + float size; + int8 type; + int32 min_dmg; + int32 max_dmg; +}; +struct Ground_Spawn{ + float max_x; + float max_y; + float min_x; + float min_y; + float max_z; + float heading; + char name[16]; + int32 item; + int32 max_allowed; + int32 respawntimer; +}; +struct Ground_Spawns { + struct Ground_Spawn spawn[50]; //Assigned max number to allow +}; +struct PetitionBug_Struct{ + int32 petition_number; + int32 unknown4; + char accountname[64]; + int32 zoneid; + char name[64]; + int32 level; + int32 class_; + int32 race; + int32 unknown152[3]; + int32 time; + int32 unknown168; + char text[1028]; +}; + +struct DyeStruct +{ + union + { + struct + { + struct Color_Struct head; + struct Color_Struct chest; + struct Color_Struct arms; + struct Color_Struct wrists; + struct Color_Struct hands; + struct Color_Struct legs; + struct Color_Struct feet; + struct Color_Struct primary; // you can't actually dye this + struct Color_Struct secondary; // or this + } + dyes; + struct Color_Struct dye[9]; + }; +}; + +struct ApproveZone_Struct { + char name[64]; + int32 zoneid; + int32 approve; +}; +struct ZoneInSendName_Struct { + int32 unknown0; + char name[64]; + char name2[64]; + int32 unknown132; +}; +struct ZoneInSendName_Struct2 { + int32 unknown0; + char name[64]; + int32 unknown68[145]; +}; + +static const uint32 MAX_TRIBUTE_TIERS = 10; + +struct StartTribute_Struct { + int32 client_id; + int32 tribute_master_id; + int32 response; +}; + +struct TributeLevel_Struct { + uint32 level; //backwards byte order! + int32 tribute_item_id; //backwards byte order! + int32 cost; //backwards byte order! +}; + +struct TributeAbility_Struct { + int32 tribute_id; //backwards byte order! + int32 tier_count; //backwards byte order! + TributeLevel_Struct tiers[MAX_TRIBUTE_TIERS]; + char *name; +}; + +struct GuildTributeAbility_Struct { + uint32 guild_id; + TributeAbility_Struct ability; +}; + +struct SelectTributeReq_Struct { + int32 client_id; //? maybe action ID? + uint32 tribute_id; + int32 unknown8; //seen E3 00 00 00 +}; + +struct SelectTributeReply_Struct { + int32 client_id; //echoed from request. + uint32 tribute_id; + char *desc; +}; + +struct TributeInfo_Struct { + int32 active; //0 == inactive, 1 == active + uint32 tributes[MAX_PLAYER_TRIBUTES]; //-1 == NONE + int32 tiers[MAX_PLAYER_TRIBUTES]; //all 00's + int32 tribute_master_id; +}; + +struct TributeItem_Struct { + int32 slot; + int32 quantity; + int32 tribute_master_id; + sint32 tribute_points; +}; + +struct TributePoint_Struct { + sint32 tribute_points; + int32 unknown04; + sint32 career_tribute_points; + int32 unknown12; +}; + +struct TributeMoney_Struct { + int32 platinum; + int32 tribute_master_id; + sint32 tribute_points; +}; + + +struct Split_Struct +{ + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; +}; + + +/* +** New Combine Struct +** Client requesting to perform a tradeskill combine +** Size: 4 bytes +** Used In: OP_TradeSkillCombine +** Last Updated: Oct-15-2003 +** +*/ +struct NewCombine_Struct { +/*00*/ sint16 container_slot; +/*02*/ char unknown02[2]; +/*04*/ +}; + + +//client requesting favorite recipies +struct TradeskillFavorites_Struct { + uint32 object_type; + uint32 some_id; + uint32 favorite_recipes[500]; +}; + +//search request +struct RecipesSearch_Struct { + uint32 object_type; //same as in favorites + uint32 some_id; //same as in favorites + uint32 mintrivial; + uint32 maxtrivial; + char query[56]; + uint32 unknown4; //is set to 00 03 00 00 + uint32 unknown5; //is set to 4C DD 12 00 +/*80*/ +}; + +//one sent for each item, from server in reply to favorites or search +struct RecipeReply_Struct { + uint32 object_type; + uint32 some_id; //same as in favorites + uint32 component_count; + uint32 recipe_id; + uint32 trivial; + char recipe_name[64]; +/*84*/ +}; + +//received and sent back as an ACK with different reply_code +struct RecipeAutoCombine_Struct { + uint32 object_type; + uint32 some_id; + uint32 unknown1; //echoed in reply + uint32 recipe_id; + uint32 reply_code; // 93 64 e1 00 (junk) in request + // 00 00 00 00 in successful reply + // f5 ff ff ff in 'you dont have all the stuff' reply +}; + +struct LevelAppearance_Struct { //Sends a little graphic on level up + int32 spawn_id; + int32 parm1; + int32 value1a; + int32 value1b; + int32 parm2; + int32 value2a; + int32 value2b; + int32 parm3; + int32 value3a; + int32 value3b; + int32 parm4; + int32 value4a; + int32 value4b; + int32 parm5; + int32 value5a; + int32 value5b; +/*64*/ +}; +struct MerchantList{ + uint32 id; + uint32 slot; + uint32 item; +}; +struct TempMerchantList{ + uint32 npcid; + uint32 slot; + uint32 item; + uint32 charges; //charges/quantity + uint32 origslot; +}; + + +struct FindPerson_Point { + float y; + float x; + float z; +}; + +struct FindPersonRequest_Struct { + uint32 npc_id; + FindPerson_Point client_pos; +}; + +//variable length packet of points +struct FindPersonResult_Struct { + FindPerson_Point dest; + FindPerson_Point *path; //last element must be the same as dest +}; + +struct MobRename_Struct { +/*000*/ char old_name[64]; +/*064*/ char old_name_again[64]; //not sure what the difference is +/*128*/ char new_name[64]; +/*192*/ uint32 unknown192; //set to 0 +/*196*/ uint32 unknown196; //set to 1 +/*200*/ +}; + +struct PlayMP3_Struct { + char filename[128]; +}; + +//this is for custom title display in the skill window +struct TitleEntry_Struct { + uint32 skill_id; + uint32 skill_value; + char title[1]; +}; + +struct Titles_Struct { + uint32 title_count; + TitleEntry_Struct *titles; +}; + +//this is for title selection by the client +struct TitleListEntry_Struct { + uint32 unknown0; //title ID + char prefix[1]; //variable length, null terminated + char postfix[1]; //variable length, null terminated +}; + +struct TitleList_Struct { + uint32 title_count; + TitleListEntry_Struct *titles; //list of title structs + //uint32 unknown_ending; seen 0x7265, 0 +}; + +struct SetTitle_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + uint32 title_id; +}; + +struct SetTitleReply_Struct { + uint32 is_suffix; //guessed: 0 = prefix, 1 = suffix + char title[32]; + uint32 entity_id; +}; + +#if 0 +// Old struct not used by Task System implementation but left for reference +struct TaskDescription_Struct { +/*000*/ uint32 activity_count; //not right. +/*004*/ uint32 taskid; +/*008*/ uint8 unk; +/*009*/ uint32 id3; +/*013*/ uint32 unknown13; +/*017*/ char name[1]; //variable length, 0 terminated +/*018*/ uint32 unknown18; +/*022*/ uint32 unknown22; +/*026*/ char desc[1]; //variable length, 0 terminated +/*027*/ uint32 reward_count; //not sure +/*031*/ uint32 unknown31; +/*035*/ uint32 unknown35; +/*039*/ uint16 unknown39; +/*041*/ char reward_link[1]; //variable length, 0 terminated +/*042*/ uint32 unknown43; //maybe crystal count? +}; +#endif + +struct TaskMemberList_Struct { +/*00*/ uint32 gopher_id; +/*04*/ uint32 unknown04; +/*08*/ uint32 member_count; //1 less than the number of members +/*12*/ char *list_pointer; +/* list is of the form: + char member_name[1] //null terminated string + uint8 task_leader //boolean flag +*/ +}; + +#if 0 +// Old structs not used by Task System implentation but left for reference +struct TaskActivity_Struct { +/*000*/ uint32 activity_count; //not right +/*004*/ uint32 id3; +/*008*/ uint32 taskid; +/*012*/ uint32 activity_id; +/*016*/ uint32 unknown016; +/*020*/ uint32 activity_type; +/*024*/ uint32 unknown024; +/*028*/ uint32 unknown28; +/*032*/ char mob_name[1]; //variable length, 0 terminated +/*033*/ char item_name[1]; //variable length, 0 terminated +/*034*/ uint32 goal_count; +/*038*/ uint32 unknown38; //0xFFFFFFFF +/*042*/ uint32 unknown42; //0xFFFFFFFF +/*046*/ uint32 unknown46; //saw 0x151,0x156 +/*050*/ uint32 unknown50; //saw 0x404,0 +/*054*/ char activity_name[1]; //variable length, 0 terminated... commonly empty +/*055*/ uint32 done_count; +/*059*/ uint32 unknown59; //=1 except on unknown and terminal activities? +/*063*/ +}; + +struct TaskHistoryEntry_Struct { + uint32 task_id; + char name[1]; + uint32 completed_time; +}; +struct TaskHistory_Struct { + uint32 completed_count; + TaskHistoryEntry_Struct entries[0]; +}; +#endif + +struct AcceptNewTask_Struct { + uint32 unknown00; + uint32 task_id; //set to 0 for 'decline' + uint32 task_master_id; //entity ID +}; + +//was all 0's from client, server replied with same op, all 0's +struct CancelTask_Struct { + uint32 SequenceNumber; + uint32 unknown4; // Only seen 0x00000002 +}; + +#if 0 +// old struct, not used by Task System implementation but left for reference. +struct AvaliableTask_Struct { + uint32 task_index; //no idea, seen 0x1 + uint32 task_master_id; //entity ID + uint32 task_id; + uint32 unknown012; + uint32 activity_count; //not sure, seen 2 + char desc[1]; //variable length, 0 terminated + uint32 reward_platinum;//not sure on these + uint32 reward_gold; + uint32 reward_silver; + uint32 reward_copper; + char some_name[1]; //variable length, 0 terminated + uint8 unknown1; + uint32 unknown2; //0xFFFFFFFF + uint32 unknown3; //0xFFFFFFFF + uint32 unknown4; //seen 0x16 + uint8 unknown5; +}; +#endif + + +// Many of the Task System packets contain variable length strings, as well as variable numbers +// of records, hence splitting them into multiple structs (header, middle, trailer) etc. +// +struct AvailableTaskHeader_Struct { + uint32 TaskCount; + uint32 unknown1; + uint32 TaskGiver; +}; + +struct AvailableTaskData1_Struct { + uint32 TaskID; + uint32 TimeLimit; + uint32 unknown2; +}; + +struct AvailableTaskData2_Struct { + uint32 unknown1,unknown2,unknown3,unknown4; +}; + +struct AvailableTaskTrailer_Struct { + uint32 ItemCount; + uint32 unknown1, unknown2; + uint32 StartZone; +}; + +struct TaskDescriptionHeader_Struct { + uint32 SequenceNumber; // The order the tasks appear in the journal. 0 for first task, 1 for second, etc. + uint32 TaskID; + uint32 unknown2; + uint32 unknown3; + uint8 unknown4; +}; + +struct TaskDescriptionData1_Struct { + uint32 Duration; + uint32 unknown2; + uint32 StartTime; +}; + +struct TaskDescriptionData2_Struct { + uint32 RewardCount; // ?? + uint32 unknown1; + uint32 unknown2; + uint16 unknown3; + //uint8 unknown4; +}; + +struct TaskDescriptionTrailer_Struct { + //uint16 unknown1; // 0x0012 + uint32 Points; +}; + +struct TaskActivityHeader_Struct { + uint32 TaskSequenceNumber; + uint32 unknown2; // Seen 0x00000002 + uint32 TaskID; + uint32 ActivityID; + uint32 unknown3; + uint32 ActivityType; + uint32 Optional; + uint32 unknown5; +}; + +struct TaskActivityData1_Struct { + uint32 GoalCount; + uint32 unknown1; // 0xffffffff + uint32 unknown2; // 0xffffffff + uint32 ZoneID; // seen 0x36 + uint32 unknown3; +}; + +struct TaskActivityTrailer_Struct { + uint32 DoneCount; + uint32 unknown1; // Seen 1 +}; + +// The Short_Struct is sent for tasks that are hidden and act as a placeholder +struct TaskActivityShort_Struct { + uint32 TaskSequenceNumber; + uint32 unknown2; // Seen 0x00000002 + uint32 TaskID; + uint32 ActivityID; + uint32 unknown3; + uint32 ActivityType; // 0xffffffff for the short packet + uint32 unknown4; +}; + +struct TaskActivityComplete_Struct { + uint32 TaskIndex; + uint32 unknown2; // 0x00000002 + uint32 unknown3; + uint32 ActivityID; + uint32 unknown4; // 0x00000001 + uint32 unknown5; // 0x00000001 +}; + +#if 0 +// This is a dupe of the CancelTask struct +struct TaskComplete_Struct { + uint32 unknown00; // 0x00000000 + uint32 unknown04; // 0x00000002 +}; +#endif + +struct TaskHistoryRequest_Struct { + uint32 TaskIndex; // This is the sequence the task was sent in the Completed Tasks packet. +}; + +struct TaskHistoryReplyHeader_Struct { + uint32 TaskID; + uint32 ActivityCount; +}; + +struct TaskHistoryReplyData1_Struct { + uint32 ActivityType; +}; + +struct TaskHistoryReplyData2_Struct { + uint32 GoalCount; + uint32 unknown04; // 0xffffffff + uint32 unknown08; // 0xffffffff + uint32 ZoneID; + uint32 unknown16; +}; + +enum { VoiceMacroTell = 1, VoiceMacroGroup = 2, VoiceMacroRaid = 3 }; + +struct VoiceMacroIn_Struct { +/*000*/ char Unknown000[64]; +/*064*/ uint32 Type; // 1 = Tell, 2 = Group, 3 = Raid +/*068*/ char Target[64]; +/*132*/ uint32 Unknown132; // Seems to be 0x0000000c always +/*136*/ uint32 MacroNumber; +}; + +struct VoiceMacroOut_Struct { +/*000*/ char From[64]; +/*064*/ uint32 Type; // 1 = Tell, 2 = Group, 3 = Raid +/*068*/ uint32 Unknown068; +/*072*/ uint32 Voice; +/*076*/ uint32 MacroNumber; +/*080*/ char Unknown080[60]; +}; + +struct BankerChange_Struct { + uint32 platinum; + uint32 gold; + uint32 silver; + uint32 copper; + uint32 platinum_bank; + uint32 gold_bank; + uint32 silver_bank; + uint32 copper_bank; +}; + +struct LeadershipExpUpdate_Struct { + /*0000*/ uint32 unknown0000; // All zeroes? + /*0004*/ uint32 group_leadership_exp; // Group leadership exp value + /*0008*/ uint32 group_leadership_points; // Unspent group points + /*0012*/ uint32 unknown0012; // Type? + /*0016*/ uint32 unknown0016; // All zeroes? + /*0020*/ uint32 raid_leadership_exp; // Raid leadership exp value + /*0024*/ uint32 raid_leadership_points; // Unspent raid points + /*0028*/ uint32 unknown0028; +}; + +struct UpdateLeadershipAA_Struct { +/*00*/ uint32 ability_id; +/*04*/ uint32 new_rank; +/*08*/ uint32 pointsleft; +/*12*/ +}; + +enum +{ + GroupLeadershipAbility_MarkNPC = 0 +}; + +struct DoGroupLeadershipAbility_Struct +{ +/*000*/ uint32 Ability; +/*000*/ uint32 Parameter; +}; + +struct DelegateAbility_Struct +{ +/*000*/ uint32 DelegateAbility; +/*004*/ uint32 MemberNumber; +/*008*/ uint32 Action; +/*012*/ uint32 Unknown012; +/*016*/ uint32 Unknown016; +/*020*/ uint32 EntityID; +/*024*/ uint32 Unknown024; +/*028*/ char Name[64]; +}; + +struct GroupUpdateLeaderAA_Struct +{ +/*000*/ char Unknown000[64]; +/*064*/ GroupLeadershipAA_Struct LeaderAAs; +/*128*/ char unknown128[128]; +}; + +struct MarkNPC_Struct +{ +/*00*/ uint32 TargetID; // Target EntityID +/*04*/ uint32 Number; // Number to mark them with (1, 2 or 3) +}; + +struct RaidGeneral_Struct { +/*00*/ uint32 action; //=10 +/*04*/ char player_name[64]; //should both be the player's name +/*04*/ char leader_name[64]; +/*132*/ uint32 parameter; +}; + +struct RaidAddMember_Struct { +/*000*/ RaidGeneral_Struct raidGen; //param = (group num-1); 0xFFFFFFFF = no group +/*136*/ uint8 _class; +/*137*/ uint8 level; +/*138*/ uint8 isGroupLeader; +/*139*/ uint8 flags[5]; //no idea if these are needed... +}; + + +struct RaidAdd_Struct { +/*000*/ uint32 action; //=0 +/*004*/ char player_name[64]; //should both be the player's name +/*068*/ char leader_name[64]; +/*132*/ uint8 _class; +/*133*/ uint8 level; +/*134*/ uint8 has_group; +/*135*/ uint8 unknown135; //seems to be 0x42 or 0 +}; + +struct RaidCreate_Struct { +/*00*/ uint32 action; //=8 +/*04*/ char leader_name[64]; +/*68*/ uint32 leader_id; +}; + +struct RaidMemberInfo_Struct { +/*00*/ uint8 group_number; +/*01*/ char member_name[1]; //dyanmic length, null terminated '\0' +/*00*/ uint8 unknown00; +/*01*/ uint8 _class; +/*02*/ uint8 level; +/*03*/ uint8 is_raid_leader; +/*04*/ uint8 is_group_leader; +/*05*/ uint8 main_tank; //not sure +/*06*/ uint8 unknown06[5]; //prolly more flags +}; + +struct RaidDetails_Struct { +/*000*/ uint32 action; //=6,20 +/*004*/ char leader_name[64]; +/*068*/ uint32 unknown68[4]; +/*084*/ LeadershipAA_Struct abilities; //ranks in backwards byte order +/*128*/ uint8 unknown128[142]; +/*354*/ uint32 leader_id; +}; + +struct RaidMembers_Struct { +/*000*/ RaidDetails_Struct details; +/*358*/ uint32 member_count; //including leader +/*362*/ RaidMemberInfo_Struct members[1]; +/*...*/ RaidMemberInfo_Struct empty; //seem to have an extra member with a 0 length name on the end +}; + +struct DynamicWall_Struct { +/*00*/ char name[32]; +/*32*/ float y; +/*36*/ float x; +/*40*/ float z; +/*44*/ uint32 something; +/*48*/ uint32 unknown48; //0 +/*52*/ uint32 one_hundred; //0x64 +/*56*/ uint32 unknown56; //0 +/*60*/ uint32 something2; +/*64*/ sint32 unknown64; //-1 +/*68*/ uint32 unknown68; //0 +/*72*/ uint32 unknown72; //0 +/*76*/ uint32 unknown76; //0x100 +/*80*/ +}; + +enum { //bandolier actions + BandolierCreate = 0, + BandolierRemove = 1, + BandolierSet = 2 +}; + +struct BandolierCreate_Struct { +/*00*/ uint32 action; //0 for create +/*04*/ uint8 number; +/*05*/ char name[32]; +/*37*/ uint16 unknown37; //seen 0x93FD +/*39*/ uint8 unknown39; //0 +}; + +struct BandolierDelete_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ int8 unknown05[35]; +}; + +struct BandolierSet_Struct { +/*00*/ uint32 action; +/*04*/ uint8 number; +/*05*/ int8 unknown05[35]; +}; + +struct Arrow_Struct { +/*000*/ uint32 type; //unsure on name, seems to be 0x1, dosent matter +/*005*/ int8 unknown004[12]; +/*016*/ float src_y; +/*020*/ float src_x; +/*024*/ float src_z; +/*028*/ int8 unknown028[12]; +/*040*/ float velocity; //4 is normal, 20 is quite fast +/*044*/ float launch_angle; //0-450ish, not sure the units, 140ish is straight +/*048*/ float tilt; //on the order of 125 +/*052*/ int8 unknown052[8]; +/*060*/ float arc; +/*064*/ int8 unknown064[12]; +/*076*/ uint32 source_id; +/*080*/ uint32 target_id; //entity ID +/*084*/ uint32 item_id; //1 to about 150ish +/*088*/ uint32 unknown088; //seen 125, dosent seem to change anything.. +/*092*/ uint32 unknown092; //seen 16, dosent seem to change anything +/*096*/ uint8 unknown096[2]; +/*098*/ uint8 skill; +/*099*/ uint8 item_type; +/*100*/ uint8 unknown100; +/*101*/ char model_name[16]; +/*117*/ int8 unknown117[19]; +}; + +//made a bunch of trivial structs for stuff for opcode finder to use +struct Consent_Struct { + char name[1]; //always at least a null +}; + +struct AdventureMerchant_Struct { + uint32 unknown_flag; //seems to be 1 + uint32 entity_id; +}; + +struct Save_Struct { + int8 unknown00[192]; +}; + +struct GMToggle_Struct { + int8 unknown0[64]; + uint32 toggle; +}; + +struct GroupInvite_Struct { + char invitee_name[64]; + char inviter_name[64]; +// int8 unknown128[65]; +}; + +struct BuffFadeMsg_Struct { + uint32 color; + char msg[1]; +}; + +struct UseAA_Struct { + int32 begin; + int32 ability; + int32 end; +}; + +struct AA_Ability { +/*00*/ int32 skill_id; +/*04*/ int32 base1; +/*08*/ int32 base2; +/*12*/ int32 slot; +}; + +struct SendAA_Struct { +/* EMU additions for internal use */ + char name[128]; + sint16 cost_inc; + +/*0000*/ int32 id; +/*0004*/ int32 unknown004; +/*0008*/ int32 hotkey_sid; +/*0012*/ int32 hotkey_sid2; +/*0016*/ int32 title_sid; +/*0020*/ int32 desc_sid; +/*0024*/ int32 class_type; +/*0028*/ int32 cost; +/*0032*/ int32 seq; +/*0036*/ int32 current_level; //1s, MQ2 calls this AARankRequired +/*0040*/ int32 prereq_skill; //is < 0, abs() is category # +/*0044*/ int32 prereq_minpoints; //min points in the prereq +/*0048*/ int32 type; +/*0052*/ int32 spellid; +/*0056*/ int32 spell_type; +/*0060*/ int32 spell_refresh; +/*0064*/ int16 classes; +/*0066*/ int16 berserker; //seems to be 1 if its a berserker ability +/*0068*/ int32 max_level; +/*0072*/ int32 last_id; +/*0076*/ int32 next_id; +/*0080*/ int32 cost2; +/*0084*/ int32 unknown80[2]; //0s +/*0088*/ int32 total_abilities; +/*0092*/ AA_Ability *abilities; +}; + +struct AA_Action { +/*00*/ int32 action; +/*04*/ int32 ability; +/*08*/ int32 unknown08; +/*12*/ int32 exp_value; +}; + +struct AA_Skills { //this should be removed and changed to AA_Array +/*00*/ int32 aa_skill; +/*04*/ int32 aa_value; +}; + +struct AAExpUpdate_Struct { +/*00*/ int32 unknown00; //seems to be a value from AA_Action.ability +/*04*/ int32 aapoints_unspent; +/*08*/ int8 aaxp_percent; //% of exp that goes to AAs +/*09*/ int8 unknown09[3]; //live dosent always zero these, so they arnt part of aaxp_percent +}; + + +struct AltAdvStats_Struct { +/*000*/ uint32 experience; +/*004*/ uint16 unspent; +/*006*/ uint16 unknown006; +/*008*/ int8 percentage; +/*009*/ int8 unknown009[3]; +}; + +struct PlayerAA_Struct { + AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct AATable_Struct { + AA_Skills aa_list[MAX_PP_AA_ARRAY]; +}; + +struct Weather_Struct { + uint32 val1; //generall 0x000000FF + uint32 type; //0x31=rain, 0x02=snow(i think), 0 = normal + uint32 mode; +}; + +struct ZoneInUnknown_Struct { + uint32 val1; + uint32 val2; + uint32 val3; +}; + +struct MobHealth_Struct { + uint16 entity_id; + uint8 hp; +}; + +struct AnnoyingZoneUnknown_Struct { + uint32 entity_id; + uint32 value; //always 4 +}; + +struct LoadSpellSet_Struct { + uint32 spell[MAX_PP_MEMSPELL]; // 0xFFFFFFFF if no action, slot number if to unmem starting at 0 + uint32 unknown; //there seems to be an extra field in this packet... +}; + +// This is the structure for OP_ZonePlayerToBind opcode. Discovered on Feb 9 2007 by FNW from packet logs for titanium client +// This field "zone_name" is text the Titanium client will display on player death +// it appears to be a variable length, null-terminated string +// In logs it has "Bind Location" text which shows up on Titanium client as .... +// "Return to Bind Location, please wait..." +// This can be used to send zone name instead.. On 6.2 client, this is ignored. +struct ZonePlayerToBind_Struct { +/*000*/ uint32 bind_zone_id; +/*004*/ float x; +/*008*/ float y; +/*012*/ float z; +/*016*/ float heading; +/*020*/ char zone_name[1]; +}; + +typedef struct { +/*000*/ uint32 bind_number; // Number of this bind in the iteration +/*004*/ uint32 bind_zone_id; // ID of the zone for this bind point or resurect point +/*008*/ float x; // X loc for this bind point +/*012*/ float y; // Y loc for this bind point +/*016*/ float z; // Z loc for this bind point +/*020*/ float heading; // Heading for this bind point +/*024*/ char bind_zone_name[1]; // Or "Bind Location" or "Resurrect" +/*000*/ uint8 validity; // 0 = valid choice, 1 = not a valid choice at this time (resurrection) +} RespawnOptions_Struct; + +struct RespawnWindow_Struct { +/*000*/ uint32 unknown000; // Seen 0 +/*004*/ uint32 time_remaining; // Total time before respawn in milliseconds +/*008*/ uint32 unknown008; // Seen 0 +/*012*/ uint32 total_binds; // Total Bind Point Options? - Seen 2 +/*016*/ RespawnOptions_Struct bind_points; +// First bind point is "Bind Location" and the last one is "Ressurect" +}; + +/** + * Shroud spawn. For others shrouding, this has their spawnId and + * spawnStruct. + * + * Length: 586 + * OpCode: OP_Shroud + */ +struct spawnShroudOther +{ +/*0000*/ uint32 spawnId; // Spawn Id of the shrouded player +/*0004*/ Spawn_Struct spawn; // Updated spawn struct for the player +/*0586*/ +}; + +struct ApplyPoison_Struct { + uint32 inventorySlot; + uint32 success; +}; + +struct ItemVerifyRequest_Struct { +/*000*/ sint32 slot; // Slot being Right Clicked +/*004*/ uint32 target; // Target Entity ID +/*008*/ +}; + +struct ItemVerifyReply_Struct { +/*000*/ sint32 slot; // Slot being Right Clicked +/*004*/ uint32 spell; // Spell ID to cast if different than item effect +/*008*/ uint32 target; // Target Entity ID +/*012*/ +}; + +/** + * Shroud yourself. For yourself shrouding, this has your spawnId, spawnStruct, + * bits of your charProfileStruct (no checksum, then charProfile up till + * but not including name), and an itemPlayerPacket for only items on the player + * and not the bank. + * + * Length: Variable + * OpCode: OP_Shroud + */ +#if 0 +struct spawnShroudSelf +{ +/*00000*/ uint32_t spawnId; // Spawn Id of you +/*00004*/ Spawn_Struct spawn; // Updated spawnStruct for you +//this is a sub-struct of PlayerProfile, which we havent broken out yet. +/*00586*/ playerProfileStruct profile; // Character profile for shrouded char +/*13522*/ uint8 items; // Items on the player +/*xxxxx*/ +}; +#endif + + +typedef struct { + char Name[64]; + uint16 Class; + uint16 Level; + uint16 Zone; + uint16 GuildID; +} GroupLFPMemberEntry; + +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 +}; + +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 +/*072*/ +}; + +struct ClearObject_Struct +{ +/*000*/ uint8 Clear; // If this is not set to non-zero there is a random chance of a client crash. +/*001*/ uint8 Unknown001[7]; +}; + +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 PVPStats_Struct +{ +/*0000*/ uint32 Kills; +/*0004*/ uint32 Deaths; +/*0008*/ uint32 PVPPointsAvailable; +/*0012*/ uint32 TotalPVPPoints; +/*0016*/ uint32 BestKillStreak; +/*0020*/ uint32 WorstDeathStreak; +/*0024*/ uint32 CurrentKillStreak; +/*0028*/ uint32 Infamy; +/*0032*/ uint32 Vitality; +/*0036*/ PVPStatsEntry_Struct LastDeath; +/*0124*/ PVPStatsEntry_Struct LastKill; +/*0212*/ PVPStatsEntry_Struct KillsLast24Hours[50]; +/*4612*/ +}; + +typedef struct +{ +/*000*/ char Name[64]; +/*064*/ uint32 Kills; +/*068*/ uint32 Deaths; +/*072*/ uint32 TotalPoints; +/*076*/ uint32 Infamy; +/*080*/ +} PVPLeaderBoardEntry_Struct; + +enum { PVPSortByKills = 0, PVPSortByPoints, PVPSortByInfamy }; + +struct PVPLeaderBoardRequest_Struct +{ +/*00*/ uint32 SortType; +/*04*/ +}; + +struct PVPLeaderBoard_Struct +{ +/*0000*/ uint32 Unknown0000; +/*0004*/ uint32 MyKills; +/*0008*/ uint32 MyTotalPoints; +/*0012*/ uint32 MyRank; +/*0016*/ uint32 MyDeaths; +/*0020*/ uint32 MyInfamy; +/*0024*/ PVPLeaderBoardEntry_Struct Entries[100]; +/*8024*/ +}; + +struct PVPLeaderBoardDetailsRequest_Struct +{ +/*00*/ uint32 Unknown00; +/*04*/ char Name[64]; +/*68*/ +}; + +struct PVPLeaderBoardDetailsReply_Struct +{ +/*000*/ char Name[64]; +/*064*/ int8 Unknown064[64]; +/*128*/ uint32 Level; +/*132*/ uint32 Race; +/*136*/ uint32 Class; +/*140*/ uint32 GuildID; +/*144*/ uint32 TotalAA; +/*148*/ uint32 Unknown148; +/*152*/ uint32 Kills; +/*156*/ uint32 Deaths; +/*160*/ uint32 Infamy; +/*164*/ uint32 Points; +/*168*/ +}; + +struct DisciplineTimer_Struct +{ +/*00*/ uint32 TimerID; +/*04*/ uint32 Duration; +/*08*/ uint32 Unknown08; +}; + +//old structures live here: +//#include "eq_old_structs.h" + +// Restore structure packing to default +#pragma pack() + +#endif diff --git a/utils/player_profile_set/player_profile_set/eqemu_string.h b/utils/player_profile_set/player_profile_set/eqemu_string.h new file mode 100644 index 000000000..ce8e44c92 --- /dev/null +++ b/utils/player_profile_set/player_profile_set/eqemu_string.h @@ -0,0 +1,21 @@ +#ifndef EQEMU_STRING_H +#define EQEMU_STRING_H + +#include +#include + +namespace EQEmuString +{ + +std::string ToUpper(std::string in_string) +{ + std::string out_string = in_string; + for(unsigned int i = 0; i < in_string.size(); ++i) + { + out_string[i] = std::toupper(in_string[i]); + } + return out_string; +} + +}; +#endif \ No newline at end of file diff --git a/utils/player_profile_set/player_profile_set/ini.cpp b/utils/player_profile_set/player_profile_set/ini.cpp new file mode 100644 index 000000000..d47fe3533 --- /dev/null +++ b/utils/player_profile_set/player_profile_set/ini.cpp @@ -0,0 +1,118 @@ +#include "ini.h" + +INIParser::INIParser(const char *filename) +{ + memset(file, 0, 128); + if(strlen(filename) > 128) + { + strncpy(file, filename, 128); + } + else + { + strncpy(file, filename, strlen(filename)); + } + Parse(); +} + +INIParser::~INIParser() +{ + +} + +void INIParser::Parse() +{ + FILE *fp; + if(fp = fopen(file, "r")) + { + char Option[255], Param[255]; + while(!feof(fp)) + { + ReadLine(fp, Option, Param); + AddOption(std::string(Option), std::string(Param)); + } + fclose(fp); + } +} + +void INIParser::ReadLine(FILE *fp, char *Option, char *Param) +{ + typedef enum ReadingState { ReadingOption, ReadingParameter }; + + ReadingState State = ReadingOption; + + int StrIndex = 0; + char ch; + + strcpy(Option, ""); + strcpy(Param, ""); + + while(true) { + ch = fgetc(fp); + if((ch=='#')&&(StrIndex==0)) { // Discard comment lines beginning with a hash + while((ch!=EOF)&&(ch!='\n')) + ch = fgetc(fp); + + continue; + } + if(ch=='\r') continue; + if((ch==EOF)||(ch=='\n')) { + switch(State) { + case ReadingOption: { + Option[StrIndex]='\0'; + break; + } + case ReadingParameter: { + Param[StrIndex] = '\0'; + break; + } + } + + break; + } + if(ch=='=') { + if(State==ReadingOption) { + Option[StrIndex] = '\0'; + State = ReadingParameter; + StrIndex = 0; + continue; + } + } + switch(State) { + case ReadingOption: { + Option[StrIndex++]=tolower(ch); + break; + } + case ReadingParameter: { + Param[StrIndex++]=ch; + break; + } + } + } + + if(!strcmp(Param,"true")) strcpy(Param,"1"); + if(!strcmp(Param,"false")) strcpy(Param,"0"); +} + +void INIParser::AddOption(std::string option, std::string param) +{ + for(unsigned int x = 0; x < options.size(); ++x) + { + if(options[x].option == option) + return; + } + + iniData d; + d.option = option; + d.param = param; + options.push_back(d); +} + +std::string INIParser::GetOption(std::string option) +{ + for(unsigned int x = 0; x < options.size(); ++x) + { + if(options[x].option == option) + return options[x].param; + } + return std::string("Not Found"); +} \ No newline at end of file diff --git a/utils/player_profile_set/player_profile_set/ini.h b/utils/player_profile_set/player_profile_set/ini.h new file mode 100644 index 000000000..54356c477 --- /dev/null +++ b/utils/player_profile_set/player_profile_set/ini.h @@ -0,0 +1,32 @@ +#ifndef INI__H +#define INI__H + +#include +#include +#include +#include + +struct iniData +{ + std::string option; + std::string param; +}; + +class INIParser +{ +public: + INIParser(const char *filename); + ~INIParser(); + void Parse(); + void ReadLine(FILE *fp, char *Option, char *Param); + + void AddOption(std::string option, std::string param); + std::string GetOption(std::string option); + + +protected: + char file[128]; + std::vector options; +}; + +#endif \ No newline at end of file diff --git a/utils/player_profile_set/player_profile_set/main.cpp b/utils/player_profile_set/player_profile_set/main.cpp new file mode 100644 index 000000000..368e3d83e --- /dev/null +++ b/utils/player_profile_set/player_profile_set/main.cpp @@ -0,0 +1,143 @@ +#include +#include +#include + +#include "eq_player_structs.h" +#include "database.h" +#include "ini.h" +#include "main.h" +#include "eqemu_string.h" + +EQEmuDatabase *emu_db; +std::vector player_list; + +//player_profile_set [SET/VIEW] [Field] [Name/*] +//ex: player_profile_set VIEW LDON_MMC Jess +int main(int argc, char* argv[]) +{ + INIParser *ini = new INIParser("database.ini"); + std::string host = ini->GetOption("host"); + std::string user = ini->GetOption("user"); + std::string pass = ini->GetOption("password"); + std::string datab = ini->GetOption("db"); + + if(argc != 5) + { + std::cout << "Usage: player_profile_set [SET/VIEW] [Field] [Value] [Name/*]" << std::endl; + std::cout << "Ex: player_profile_set VIEW LDON_MMC 0 Jess" << std::endl; + std::cout << "Would view the number of LDoN MMC points earned by Jess" << std::endl << std::endl; + std::cout << "Ex: player_profile_set SET LDON_TOTAL 0 *" << std::endl; + std::cout << "Would view the number of LDoN MMC points earned by All players to 0" << std::endl; + delete ini; + ini = 0; + exit(1); + } + + std::string program_action = argv[1]; + std::string program_field = argv[2]; + std::string program_value = argv[3]; + std::string player_name = argv[4]; + program_action = EQEmuString::ToUpper(program_action); + program_field = EQEmuString::ToUpper(program_field); + + emu_db = new EQEmuDatabase(host.c_str(), datab.c_str(), user.c_str(), pass.c_str()); + if(!emu_db->Connected()) + { + delete ini; + ini = 0; + delete emu_db; + emu_db = 0; + exit(1); + } + + if(player_name == "*") + emu_db->GetPlayers(); + else + emu_db->GetPlayer(player_name); + + std::vector::iterator iter = player_list.begin(); + + while(iter != player_list.end()) + { + player_entry pe = (*iter); + PlayerProfile_Struct *m_pp = (PlayerProfile_Struct*)pe.data; + + if(program_action == "SET") + { + m_pp->ldon_points_available = 0; + m_pp->ldon_points_guk = 0; + m_pp->ldon_points_mir = 0; + m_pp->ldon_points_mmc = 0; + m_pp->ldon_points_ruj = 0; + m_pp->ldon_points_tak = 0; + emu_db->StorePlayer(pe.id, pe.data); + } + else if(program_action == "VIEW") + { + std::cout << m_pp->name << std::endl; + std::cout << m_pp->ldon_points_available << std::endl; + std::cout << m_pp->ldon_points_guk << std::endl; + std::cout << m_pp->ldon_points_mir << std::endl; + std::cout << m_pp->ldon_points_mmc << std::endl; + std::cout << m_pp->ldon_points_ruj << std::endl; + std::cout << m_pp->ldon_points_tak << std::endl; + } + else + { + std::cout << "Unknown action specified" << std::endl; + } + delete[] pe.data; + pe.data = 0; + iter++; + } + player_list.clear(); + + delete ini; + ini = 0; + delete emu_db; + emu_db = 0; + + std::cout << "Press enter to exit..."; + std::cin.get(); + exit(0); +} + +std::string ConvertFieldToValue(PlayerProfile_Struct *m_pp, std::string field) +{ + if(!m_pp) + { + return std::string("Unable to convert field to value"); + } + + std::stringstream ss(std::stringstream::out | std::stringstream::in); + + if(field == "LASTNAME") + { + ss << m_pp->last_name; + return ss.str(); + } + + if(field == "GENDER") + { + ss << m_pp->gender; + return ss.str(); + } + + if(field == "RACE") + { + ss << m_pp->race; + return ss.str(); + } + + if(field == "CLASS") + { + ss << m_pp->class_; + return ss.str(); + } + + return std::string("Unable to convert field to value"); +} + +void ConvertValueToField(PlayerProfile_Struct *m_pp, std::string field, std::string value) +{ +} \ No newline at end of file diff --git a/utils/player_profile_set/player_profile_set/main.h b/utils/player_profile_set/player_profile_set/main.h new file mode 100644 index 000000000..df802ac22 --- /dev/null +++ b/utils/player_profile_set/player_profile_set/main.h @@ -0,0 +1,16 @@ +#ifndef EQ_MAIN_H +#define EQ_MAIN_H + +#include "types.h" +#include "eq_player_structs.h" + +struct player_entry +{ + int32 id; + char *data; +}; + +std::string ConvertFieldToValue(PlayerProfile_Struct *m_pp, std::string field); +void ConvertValueToField(PlayerProfile_Struct *m_pp, std::string field, std::string value); + +#endif \ No newline at end of file diff --git a/utils/player_profile_set/player_profile_set/player_profile_set.vcproj b/utils/player_profile_set/player_profile_set/player_profile_set.vcproj new file mode 100644 index 000000000..f96d1960e --- /dev/null +++ b/utils/player_profile_set/player_profile_set/player_profile_set.vcproj @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/utils/player_profile_set/player_profile_set/types.h b/utils/player_profile_set/player_profile_set/types.h new file mode 100644 index 000000000..07703ae0b --- /dev/null +++ b/utils/player_profile_set/player_profile_set/types.h @@ -0,0 +1,110 @@ +/* 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 TYPES_H +#define TYPES_H + +// TODO: If we require signed or unsigned we should the s and u types.. + +typedef unsigned char int8; +typedef unsigned char byte; +typedef unsigned short int16; +typedef unsigned int int32; + +typedef unsigned char uint8; +typedef signed char sint8; +typedef unsigned short uint16; +typedef signed short sint16; +typedef unsigned int uint32; +typedef signed int sint32; + +#ifdef WIN32 + #if defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 64 + typedef unsigned __int64 int64; + typedef unsigned __int64 uint64; + typedef signed __int64 sint64; + #else + #error __int64 not supported + #endif +#else +typedef unsigned long long int64; +typedef unsigned long long uint64; +typedef signed long long sint64; +//typedef __u64 int64; +//typedef __u64 uint64; +//typedef __s64 sint64; +#endif + +#ifndef __cplusplus +typedef enum { true, false } bool; +#endif + +typedef unsigned long ulong; +typedef unsigned short ushort; +typedef unsigned char uchar; + +typedef const char Const_char; //for perl XS + +#ifdef WIN32 + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + typedef void ThreadReturnType; +// #define THREAD_RETURN(x) return; + #define THREAD_RETURN(x) _endthread(); return; +#else + typedef void* ThreadReturnType; +// typedef int SOCKET; + #define THREAD_RETURN(x) return(x); +#endif + +#define safe_delete(d) if(d) { delete d; d=0; } +#define safe_delete_array(d) if(d) { delete[] d; d=0; } +#define L32(i) ((int32) i) +#define H32(i) ((int32) (i >> 32)) +#define L16(i) ((int16) i) + +#ifndef WIN32 +// More WIN32 compatability + typedef unsigned long DWORD; + typedef unsigned char BYTE; + typedef char CHAR; + typedef unsigned short WORD; + typedef float FLOAT; + typedef FLOAT *PFLOAT; + typedef BYTE *PBYTE,*LPBYTE; + typedef int *PINT,*LPINT; + typedef WORD *PWORD,*LPWORD; + typedef long *LPLONG, LONG; + typedef DWORD *PDWORD,*LPDWORD; + typedef int INT; + typedef unsigned int UINT,*PUINT,*LPUINT; +#endif + + +#ifdef WIN32 +#define DLLFUNC extern "C" __declspec(dllexport) +#else +#define DLLFUNC extern "C" +#endif + + + +#endif diff --git a/utils/scripts/Examples/ListIteration.pl b/utils/scripts/Examples/ListIteration.pl new file mode 100644 index 000000000..daec901b1 --- /dev/null +++ b/utils/scripts/Examples/ListIteration.pl @@ -0,0 +1,89 @@ +sub EVENT_SAY +{ + if($text=~/mob/i) + { + my @moblist = $entity_list->GetMobList(); + foreach $ent (@moblist) + { + $ent->Shout("I'm a mob!"); + } + } + + if($text=~/npc/i) + { + my @npclist = $entity_list->GetNPCList(); + foreach $ent (@npclist) + { + $ent->Shout("I'm a npc!"); + } + } + + if($text=~/client/i) + { + my @clientlist = $entity_list->GetClientList(); + foreach $ent (@clientlist) + { + $ent->Shout("I'm a client!"); + } + } + + if($text=~/corpse/i) + { + my @corpselist = $entity_list->GetCorpseList(); + my $index = 0; + my $index_two = 0; + foreach $ent (@corpselist) + { + if($ent->GetOwnerName() eq $client->GetName()) + { + $index_two++; + } + $index++; + } + quest::say("There are $index corpses in the zone and $index_two of them belong to you."); + } + + if($text=~/summon/i) + { + my @corpselist = $entity_list->GetCorpseList(); + foreach $ent (@corpselist) + { + if($ent->GetOwnerName() eq $client->GetName()) + { + $ent->Summon($client, 1773); + } + } + } + + if($text=~/rez/i) + { + my @corpselist = $entity_list->GetCorpseList(); + foreach $ent (@corpselist) + { + if($ent->GetOwnerName() eq $client->GetName()) + { + $ent->CastRezz(994, $npc); + return; + } + } + } +} + +sub EVENT_AGGRO_SAY +{ + if($text=~/hate/i) + { + my @hatelist = $npc->GetHateList(); + foreach $ent (@hatelist) + { + my $h_ent = $ent->GetEnt(); + my $h_dmg = $ent->GetDamage(); + my $h_hate = $ent->GetHate(); + if($h_ent) + { + my $h_ent_name = $h_ent->GetName(); + quest::say("$h_ent_name is on my hate list with $h_hate hate and $h_dmg damage."); + } + } + } +} diff --git a/utils/scripts/Examples/SpawnManipulation.pl b/utils/scripts/Examples/SpawnManipulation.pl new file mode 100644 index 000000000..a651b369a --- /dev/null +++ b/utils/scripts/Examples/SpawnManipulation.pl @@ -0,0 +1,17 @@ +sub EVENT_SAY { + if($text=~/despawn/i) + { + quest::disable_spawn2(10842); + quest::say("Yes sir!"); + } + elsif($text=~/enable/i) + { + quest::enable_spawn2(10842); + quest::say("Yes sir!"); + } + elsif($text=~/spawn/i) + { + quest::spawn_from_spawn2(10842); + quest::say("Yes sir!"); + } +} \ No newline at end of file diff --git a/utils/scripts/export_spells.pl b/utils/scripts/export_spells.pl new file mode 100644 index 000000000..4b5ca0bbd --- /dev/null +++ b/utils/scripts/export_spells.pl @@ -0,0 +1,87 @@ +#! /usr/bin/perl + +#use strict; +#use warnings; +use DBI; +use Getopt::Std; + +#get config info from eqemu_config.xml to connect to the db & point to spells_us.txt +getopts('c:t:i:s:oh'); +my $conf = "eqemu_config.xml"; #default +my $table = "spells_new"; #default +my $id = "id"; #default +my $spellf = "spells_us.txt"; #default + +if ($opt_h) { #help (-h) + printf "\nUsage: export_spells.pl [-c path] [-t table] [-i column] [-s path] [-o]\n"; + printf " -c path path/to/eqemu_config.xml. defaults to $conf\n"; + printf " -t table table to load the spells from. defaults to $table\n"; + printf " -i column name of the column in the database table to order by. defaults to $id\n"; + printf " -s path path/to/export/spells_us.txt. defaults to $spellf\n"; + printf " -o overwrite $spellf if it exists\n"; + printf "\n"; + exit; +} +if ($opt_c) {$conf = $opt_c;} #use config file from -c, if defined +if ($opt_t) {$table = $opt_t;} #use db table -t, if defined +if ($opt_i) {$id = $opt_i;} #use column name -i, if defined +if ($opt_s) {$spellf = $opt_s;} #use spells file from -s, if defined + +my $db = "eq"; +my $user = "eq"; +my $pass = "eq"; +my $host = "localhost"; +open(F, "<$conf") or die "Unable to open config: $conf\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; + } +} +if(!$db || !$user || !$pass || !$host) { + die "Invalid database info, missing one of: host, user, password, database\n"; +} + +#connect to the db before we waste time loading the spells file +my $source = "DBI:mysql:database=$db;host=$host"; +my $dbh = DBI->connect($source, $user, $pass) || die "Could not create db handle\n"; + +if ((-e $spellf) && !$opt_o) { #spell file already exists & we don't want to overwrite + die "'$spellf' already exists; Use -o if you want to overwrite\n"; +} + +#open spell file +open(SPELLS, ">$spellf") or die "Unable to open spell file for export: $spellf\n"; + +#parse through spells & write to spells_us.txt +my $sth = $dbh->prepare("SELECT * FROM $table ORDER BY $id ASC"); +$sth->execute(); #run the query on the db +my($numspells, $loadedspells, $highid); #define some variables, cause we're cool like that +while (my $val = $sth->fetch()) { + ++$numspells; + printf("Exporting \"%s\" (%d) \r", @$val[1], @$val[0]); # name (id) + my $line = join("^", @$val) . "\n"; #convert to delimited string, with a newline at the end + print SPELLS $line; #write to the file + $highid = @$val[0]; #set to current id +} + +close (SPELLS); #since we're done with it... + +#summary of results +print "Spells Exported: $numspells \n"; +print "Highest ID: $highid\n\n"; \ No newline at end of file diff --git a/utils/scripts/factionmod.pl b/utils/scripts/factionmod.pl new file mode 100644 index 000000000..c13f3fb78 --- /dev/null +++ b/utils/scripts/factionmod.pl @@ -0,0 +1,112 @@ +use DBI; + +my $db = peq; +my $user = eqemu; +my $pass = eqemu; +my $host = localhost; + +my $source = "DBI:mysql:database=$db;host=$host"; +my $dbh = DBI->connect($source, $user, $pass) || die "Could not create db handle\n"; + +my $select_query = "SELECT id,name,base,mod_c1,mod_c2,mod_c3,mod_c4,mod_c5,mod_c6,mod_c7,mod_c8,mod_c9,mod_c10,mod_c11,mod_c12,"; +$select_query .= "mod_c13,mod_c14,mod_c15,mod_c16,mod_r1,mod_r2,mod_r3,mod_r4,mod_r5,mod_r6,mod_r7,mod_r8,mod_r9,mod_r10,mod_r11,"; +$select_query .= "mod_r12,mod_r14,mod_r42,mod_r75,mod_r108,mod_r128,mod_r130,mod_r161,mod_r330,mod_r367,mod_r522,mod_d140,mod_d201,"; +$select_query .= "mod_d202,mod_d203,mod_d204,mod_d205,mod_d206,mod_d207,mod_d208,mod_d209,mod_d210,mod_d211,mod_d212,mod_d213,"; +$select_query .= "mod_d214,mod_d215,mod_d216 FROM faction_list"; + +my $count = 0; +my $sth = $dbh->prepare($select_query); +$sth->execute(); +while (my $ref = $sth->fetchrow_hashref()) { + for(my $i = 1; $i <= 16; $i++) { + my $field_name = "mod_c" . $i; + if($ref->{$field_name} != 0) { + my $mod_name = "c" . $i; + my $rsth = $dbh->prepare("REPLACE INTO `faction_list_mod` (`faction_id`,`mod`,`mod_name`) VALUES(?, ?, ?)"); + $rsth->execute($ref->{id}, $ref->{$field_name}, $mod_name); + $count++; + } + } + + for(my $i = 1; $i <= 12; $i++) { + my $field_name = "mod_r" . $i; + if($ref->{$field_name} != 0) { + my $mod_name = "r" . $i; + my $rsth = $dbh->prepare("REPLACE INTO `faction_list_mod` (`faction_id`,`mod`,`mod_name`) VALUES(?, ?, ?)"); + $rsth->execute($ref->{id}, $ref->{$field_name}, $mod_name); + $count++; + } + } + + if($ref->{"mod_r14"} != 0) { + my $rsth = $dbh->prepare("REPLACE INTO `faction_list_mod` (`faction_id`,`mod`,`mod_name`) VALUES(?, ?, ?)"); + $rsth->execute($ref->{id}, $ref->{"mod_r14"}, "r14"); + $count++; + } + + if($ref->{"mod_r42"} != 0) { + my $rsth = $dbh->prepare("REPLACE INTO `faction_list_mod` (`faction_id`,`mod`,`mod_name`) VALUES(?, ?, ?)"); + $rsth->execute($ref->{id}, $ref->{"mod_r42"}, "r42"); + $count++; + } + + if($ref->{"mod_r75"} != 0) { + my $rsth = $dbh->prepare("REPLACE INTO `faction_list_mod` (`faction_id`,`mod`,`mod_name`) VALUES(?, ?, ?)"); + $rsth->execute($ref->{id}, $ref->{"mod_r75"}, "r75"); + $count++; + } + + if($ref->{"mod_r108"} != 0) { + my $rsth = $dbh->prepare("REPLACE INTO `faction_list_mod` (`faction_id`,`mod`,`mod_name`) VALUES(?, ?, ?)"); + $rsth->execute($ref->{id}, $ref->{"mod_r108"}, "r108"); + $count++; + } + + if($ref->{"mod_r128"} != 0) { + my $rsth = $dbh->prepare("REPLACE INTO `faction_list_mod` (`faction_id`,`mod`,`mod_name`) VALUES(?, ?, ?)"); + $rsth->execute($ref->{id}, $ref->{"mod_r128"}, "r128"); + $count++; + } + + if($ref->{"mod_r130"} != 0) { + my $rsth = $dbh->prepare("REPLACE INTO `faction_list_mod` (`faction_id`,`mod`,`mod_name`) VALUES(?, ?, ?)"); + $rsth->execute($ref->{id}, $ref->{"mod_r130"}, "r130"); + $count++; + } + + if($ref->{"mod_r161"} != 0) { + my $rsth = $dbh->prepare("REPLACE INTO `faction_list_mod` (`faction_id`,`mod`,`mod_name`) VALUES(?, ?, ?)"); + $rsth->execute($ref->{id}, $ref->{"mod_r161"}, "r161"); + $count++; + } + + if($ref->{"mod_r330"} != 0) { + my $rsth = $dbh->prepare("REPLACE INTO `faction_list_mod` (`faction_id`,`mod`,`mod_name`) VALUES(?, ?, ?)"); + $rsth->execute($ref->{id}, $ref->{"mod_r330"}, "r330"); + $count++; + } + + if($ref->{"mod_r367"} != 0) { + my $rsth = $dbh->prepare("REPLACE INTO `faction_list_mod` (`faction_id`,`mod`,`mod_name`) VALUES(?, ?, ?)"); + $rsth->execute($ref->{id}, $ref->{"mod_r367"}, "r367"); + $count++; + } + + if($ref->{"mod_r522"} != 0) { + my $rsth = $dbh->prepare("REPLACE INTO `faction_list_mod` (`faction_id`,`mod`,`mod_name`) VALUES(?, ?, ?)"); + $rsth->execute($ref->{id}, $ref->{"mod_r522"}, "r522"); + $count++; + } + + for(my $i = 201; $i <= 216; $i++) { + my $field_name = "mod_d" . $i; + if($ref->{$field_name} != 0) { + my $mod_name = "d" . $i; + my $rsth = $dbh->prepare("REPLACE INTO `faction_list_mod` (`faction_id`,`mod`,`mod_name`) VALUES(?, ?, ?)"); + $rsth->execute($ref->{id}, $ref->{$field_name}, $mod_name); + $count++; + } + } +} + +print "$count entries created.\n"; \ No newline at end of file diff --git a/utils/scripts/import_showeq b/utils/scripts/import_showeq new file mode 100644 index 000000000..670daf592 --- /dev/null +++ b/utils/scripts/import_showeq @@ -0,0 +1,72 @@ +#!/usr/bin/perl + +my $date = $ARGV[0]; +my $opcodes_out = $ARGV[1]; + +my %opcodes = (); + +open(F, ") { + chomp; + next unless (/updated="$date"/); + if(/^.*id="([0-9a-fA-F]+)".*name="(OP_[^"]+)".*$/) { + $opcodes{$2} = $1; + } +} +close(F); +open(F, ") { + chomp; + next unless (/updated="$date"/); + if(/^.*id="([0-9a-fA-F]+)".*name="(OP_[^"]+)".*$/) { + $opcodes{$2} = $1; + } +} +close(F); + +my %fixes = ( +'OP_TributeInfo' => 'OP_SendTributes', +'OP_CancelInvite' => 'OP_GroupCancelInvite', +'OP_GMFind' => 'OP_FindPersonRequest', +'OP_CommonMessage' => 'OP_ChannelMessage', +'OP_FindResponse' => 'OP_FindPersonReply', +'OP_SpawnRename' => 'OP_MobRename', +'OP_SendZonePoints' => 'OP_SendZonepoints', +'OP_RequestZoneChange' => 'OP_RequestClientZoneChange', +'OP_Lockouts' => 'OP_LockoutTimerInfo', +'OP_GuildList' => 'OP_ZoneGuildList', +'OP_Action2' => 'OP_Damage' +); + +foreach my $seq (keys(%fixes)) { + if(defined($opcodes{$seq})) { + $opcodes{ $fixes{$seq} } = $opcodes{$seq}; + delete($opcodes{$seq}); + } +} + +open(F, "<$opcodes_out") || die "Unable to open $opcodes_out"; +while() { + chomp; + my $line = $_; + $line =~ s/# ShowEQ [0-9\/]+//g; + my $found = 0; + foreach my $op (keys(%opcodes)) { + if($line =~ /^$op=0x.*$/) { + print "$op=0x".$opcodes{$op}."\t\t\t# ShowEQ $date\n"; + delete($opcodes{$op}); + $found = 1; + last; + } + } + if(!$found) { + print "$line\n"; + } +} +close(F); + +foreach my $op (keys(%opcodes)) { + print "# Unmatched: $op=0x".$opcodes{$op}."\n"; +} + + diff --git a/utils/scripts/import_spells.pl b/utils/scripts/import_spells.pl new file mode 100644 index 000000000..832ccea4d --- /dev/null +++ b/utils/scripts/import_spells.pl @@ -0,0 +1,126 @@ +#! /usr/bin/perl + +#use strict; +#use warnings; +use DBI; +use Getopt::Std; + +#get config info from eqemu_config.xml to connect to the db & point to spells_us.txt +getopts('c:s:t:dh'); +my $conf = "eqemu_config.xml"; #default +my $spellf = "spells_us.txt"; #default +my $table = "spells_new"; #default + +if ($opt_h) { #help (-h) + printf "\nUsage: import_spells.pl [-c path] [-s path] [-t table] [-d]\n"; + printf " -c path path/to/eqemu_config.xml. defaults to $conf\n"; + printf " -s path path/to/spells_us.txt. defaults to $spellf\n"; + printf " -t table table to load the spells into. defaults to $table\n"; + printf " -d erase all spells from the database first\n\n"; + exit; +} +if ($opt_c) {$conf = $opt_c;} #use config file from -c, if defined +if ($opt_s) {$spellf = $opt_s;} #use spells file from -s, if defined +if ($opt_t) {$table = $opt_t;} #use db table -t, if defined + +my $db = "eq"; +my $user = "eq"; +my $pass = "eq"; +my $host = "localhost"; +open(F, "<$conf") or die "Unable to open config: $conf\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; + } +} +if(!$db || !$user || !$pass || !$host) { + die "Invalid database info, missing one of: host, user, password, database\n"; +} + +#connect to the db before we waste time loading the spells file +my $source1 = "DBI:mysql:database=information_schema;host=$host"; +my $source2 = "DBI:mysql:database=$db;host=$host"; + +#pull the column names from the db so we don't have to maintain a list. should also help if there are more columns in the db than the spells file +my $dbh = DBI->connect($source1, $user, $pass) || die "Could not create db handle\n"; +my $sth = $dbh->prepare("SELECT COLUMN_NAME FROM COLUMNS WHERE TABLE_SCHEMA='$db' AND TABLE_NAME='$table' ORDER BY ORDINAL_POSITION ASC"); +$sth->execute(); #run the query on the db +#create an array of the column names. i have a feeling there might be a more direct way to do it, but oh well +while (my $val = $sth->fetch()) { + push(@col_names, @$val[0]); # $column[#] +} + +#switch to the emu db +$dbh = DBI->connect($source2, $user, $pass) || die "Could not create db handle\n"; + +if ($opt_d) { #drop all data in the spells table + print "Are you sure you want to erase all spells from the '$table' table?\n"; + print "y/N ? "; + my $confirm = ; + if ($confirm=~/^y/i) { #as long as it begins with a Y, that's the important part + print "Deleting spells from the '$table' table... "; + $dbh->do("DELETE from $table"); + print "Done.\n\n"; + } else { + print "\nSkipping erasing spells from the '$table' table\n\n"; + } +} + +#open spell file +open(SPELLS, "<$spellf") or die "Unable to open spells: $spellf\n"; +#parse through spells +my($numspells, $loadedspells, $highid); #define some variables, cause we're cool like that +while() { + ++$numspells; + + chomp(); #get rid of those pesky new lines + s/'/\\'/g; #make query safe(r) by escaping quotes (\') + @s = split(/\^/); #separate by the ^ delimiter + + my @t_col_names = @col_names; #so we can safely pop any unneeded columns, but still have all of them the next time around + my $col_diff = abs(scalar(@t_col_names) - scalar(@s)); #we need to find out if the db or spells file has more columns, so we don't blow something up. this will get the amount of extra columns + #we're going to do this each loop, just in case someone screws up a line in the spell file, or starts messing with the table in the middle of the import + if (scalar(@s) < scalar(@t_col_names)) { + for (my $z = 1; $z <= $col_diff; $z++) { + pop(@t_col_names); + } + } elsif (scalar(@s) > scalar(@t_col_names)) { + for (my $z = 1; $z <= $col_diff; $z++) { + pop(@s); + } + } + + my $insert1 = join("`,`", @t_col_names); + my $insert2 = join("','", @s); #put everything in quotes & separate w/ commas for the query, plus the beginning & ending quotes. this doesn't print out to the console correctly (for me), but hopefully the query will be fine + my $insert = sprintf("REPLACE INTO %s (`%s`) VALUES ('%s')", $table, $insert1, $insert2); + + printf("Loading \"%s\" (%d) \r", $s[1], $s[0]); # name (id) + + my $i = $dbh->do($insert); #put these bad boys into the db + if ($i < 1) { #if the query didn't update anything into the db, or errored + printf("Error loading \"%s\" (%s) \n", $s[1], $s[0]); + } else { + $loadedspells++; #to compare db inserts to total spells + } + + $highid = $s[0]; #set to current id +} + +print "Spells Loaded: $loadedspells of $numspells \n"; +print "Highest ID: $highid\n\n"; \ No newline at end of file diff --git a/utils/scripts/load_13thfloor_items.pl b/utils/scripts/load_13thfloor_items.pl new file mode 100644 index 000000000..03fbfe5fb --- /dev/null +++ b/utils/scripts/load_13thfloor_items.pl @@ -0,0 +1,77 @@ +#! /usr/bin/perl + +use DBI; +use Getopt::Std; + +getopts('d:h'); +$conf = "eqemu_config.xml"; +if($opt_h) { + die "Usage: load_13thfloor_items.pl [-d path/to/eqemu_config.xml]\n"; +} +if($opt_d) { + $conf = $opt_d; +} + +$db = "eq"; +$user = "eq"; +$pass = "eq"; +$host = "localhost"; +open(F, "<$conf") or die "Unable to open config $conf\n"; +$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; + } +} +if(!$db || !$user || !$pass || !$host) { + die "Invalid db.ini, missing one of: host, user, password, database\n"; +} + +$source="DBI:mysql:database=$db;host=$host"; + +my $dbh = DBI->connect($source, $user, $pass) || die "Could not create db handle\n"; + +$_=; +chomp(); +s/'/\\'/g; +@fields=split("(? "itemuse" +); + +$insert="replace into items (".join(",",@fields).",source,updated) values ('"; +$insert=~s/UNK130/potionbeltslots/; +$insert=~s/UNK133/stackable/; + +#select(STDOUT); $|=1; +while() { + chomp(); + s/'/\\'/g; + @f=split("(?do($statement); + printf("Processing: %d %s \r",$f[4],$f[1]); + ++$count; +} +printf("Processed: %d items(s) \n",$count); + diff --git a/utils/scripts/ppreader.pl b/utils/scripts/ppreader.pl new file mode 100644 index 000000000..bfbc16b24 --- /dev/null +++ b/utils/scripts/ppreader.pl @@ -0,0 +1,226 @@ +#!/usr/bin/perl -W + +use strict; + +sub usage() { + print "Usage: ppreader.pl [-o|-n|-p|-r|-c] (old_pp) (old2_pp)\n"; + print " -o - print offsets for old struct\n"; + print " -n - print offsets for new struct\n"; + print " -p - print pp converter from old to new\n"; + print " -c - print pp converter between two old PPs\n"; + print " -r - read al alternative header and print offsets\n"; + exit(1); +} + +if($#ARGV < 0) { + usage(); +} + +my $mode = $ARGV[0]; + +my $temp = ".temp"; + + + +if($mode eq "-r") { + my $file = $ARGV[1]; + my $struct = $ARGV[2]; + print STDERR "Reading $struct from $file...\n"; + (my $old_order, my $old_fields, my $old_offsets) = readpp($struct, $file); + foreach my $f(@{$old_order}) { + my $off = $old_offsets->{$f}; + my $code = $old_fields->{$f}; + printf("/*%04lu*/%s\n", $off, $code); + } +} elsif($mode eq "-o") { + print STDERR "Reading old player profile...\n"; + my $old_struct = $ARGV[1]; + (my $old_order, my $old_fields, my $old_offsets) = readpp($old_struct, "../common/eq_old_structs.h"); + foreach my $f(@{$old_order}) { + my $off = $old_offsets->{$f}; + my $code = $old_fields->{$f}; + printf("/*%04lu*/%s\n", $off, $code); + } +} elsif($mode eq "-n") { + print STDERR "Reading new player profile...\n"; + (my $order, my $fields, my $offsets) = readpp("PlayerProfile_Struct", "../common/eq_packet_structs.h"); + foreach my $f(@{$order}) { + my $off = $offsets->{$f}; + my $code = $fields->{$f}; + printf("/*%04lu*/%s\n", $off, $code); + } +} elsif($mode eq "-p") { + if($#ARGV != 1) { + usage(); + } + my $old_struct = $ARGV[1]; + + print STDERR "Reading old player profile...\n"; + (my $old_order, my $old_fields, my $old_offsets) = readpp($old_struct, "../common/eq_old_structs.h"); + print STDERR "Reading new player profile...\n"; + (my $order, my $fields, my $offsets) = readpp("PlayerProfile_Struct", "../common/eq_packet_structs.h"); + + compare_pps($old_order, $old_fields, $old_offsets, + $order, $fields, $offsets); +} elsif($mode eq "-c") { + if($#ARGV != 2) { + usage(); + } + my $old_struct = $ARGV[1]; + my $old2_struct = $ARGV[2]; + + print STDERR "Reading old player profile...\n"; + (my $old_order, my $old_fields, my $old_offsets) = readpp($old_struct, "../common/eq_old_structs.h"); + print STDERR "Reading old2 player profile...\n"; + (my $order, my $fields, my $offsets) = readpp($old2_struct, "../common/eq_old_structs.h"); + + compare_pps($old_order, $old_fields, $old_offsets, + $order, $fields, $offsets); +} else { + print "Invalid mode specified."; + exit(1); +} + + +sub compare_pps { + (my $old_order, my $old_fields, my $old_offsets, my $order, my $fields, my $offsets) = @_; + + #will not catch order changes very well for now + + my $last_diff = 0; + my $first = $old_order->[0]; + my $taildrop = 0; + my $final = ""; + my $f; + foreach $f(@{$old_order}) { + if(!defined($offsets->{$f})) { + if($taildrop eq "") { + $taildrop = $f; + } + my $guess = $old_offsets->{$f} + $last_diff; +# print STDERR "field $f was lost in new profile, it could be at $guess.\n"; + next; + } + + my $diff = $offsets->{$f} - $old_offsets->{$f}; + + if($last_diff != $diff) { + #a change in deltas... print a rule for last block + my $tail = $f; + if($taildrop ne "") { + $tail = $taildrop; + } +# print "// delta change from $last_diff to $diff at field $f ($tail)\n"; + print "\t\tmemcpy(\&pp->$first, \&ops->$first, StructDist(ops, $first, $tail));\n"; + + $first = $f; + $last_diff = $diff; + $taildrop = ""; + + } else { + #another field with the same delta... + $final = $f; + $taildrop = ""; + next; + } + } + #finally do the last rule + my $tail = $f; + if($taildrop ne "") { + $tail = $taildrop; + } + print "\t\tmemcpy(\&pp->$first, \&ops->$first, StructDist(ops, $first, $tail));\n"; + +} + + + +sub readpp { + (my $sname, my $fname) = @_; + open(F, "<$fname") || die("Unable to open $fname\n"); + my @order = (); + my %fields = (); + my %offsets = (); + my $in = 0; + while() { + s/\r?\n//g; + if(/struct\s+$sname/) { + $in = 1; + } + next if(!$in); + + if($_ =~ /\}\s*;/) { + $in = 0; + last; + } + if($_ !~ /^\/\*[0-9]+\*\/(\s*)([^ \t]+)(\s+)([^ \t;]+);(.*)$/) { + print STDERR "Unable to parse line '$_'\n"; + next; + } + my $field = $4; + my $array = ""; + my $code = "$1$2$3$4;$5"; + if($field =~ /([^ \t]+)\[(.+)\]\[(.+)\]/) { + $field = $1."[0][0]"; + } elsif($field =~ /([^ \t]+)\[(.+)\]/) { + $field = $1."[0]"; + $array = $2; + } + $fields{$field} = $code; + push(@order, $field); + } + close(F); + + open(F, ">$temp.cpp") || die("Unable to open $temp.cpp"); + print F <<"EOC"; +#include "../common/types.h" +#include "$fname" +#include +#include +#include + + +int main() { + $sname p; + unsigned long start = (unsigned long) &p; +EOC + + foreach my $field(keys(%fields)) { + print F "\tprintf(\"$field,%lu\\n\", ((unsigned long) \&p.$field) - start);\n"; + } + print F "\n\treturn(0);\n}\n\n"; + close(F); + + if(system("g++ $temp.cpp -I. -o $temp") != 0) { + die("Error compiling $temp.cpp\n"); + } + + system("./$temp >$temp.out"); + open(F, "<$temp.out"); + while() { + chomp; + if($_ !~ /(.+),([0-9]+)/) { + print "Unable to read our own output '$_'\n"; + next; + } + if(!defined($fields{$1})) { + print "Read invalid field name '$1' from own output.\n"; + next; + } + $offsets{$1} = $2; + } + close(F); + +# unlink("$temp.cpp"); +# unlink("$temp.out"); +# unlink("$temp"); + + return(\@order, \%fields, \%offsets); +} + + + + + + + diff --git a/utils/scripts/schema.xml b/utils/scripts/schema.xml new file mode 100644 index 000000000..dcfa687be --- /dev/null +++ b/utils/scripts/schema.xml @@ -0,0 +1,5738 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
diff --git a/utils/scripts/serialize_items.pl b/utils/scripts/serialize_items.pl new file mode 100644 index 000000000..de850b2d5 --- /dev/null +++ b/utils/scripts/serialize_items.pl @@ -0,0 +1,81 @@ +#!/usr/bin/perl + +#use strict; +use DBI; +use Getopt::Std; + +getopts('i:d:h'); +$dbini = "db.ini"; +if($opt_h) { + die "Usage: serialize_items.pl [-i path/to/item_fieldlist.h] [-d path/to/db.ini]\n"; +} +$itemfields = "item_fieldlist.h"; +if($opt_d) { + $dbini = $opt_d; +} +if($opt_i) { + $itemfields = $opt_i; +} + +@fieldlist = (); +open(F, "<$itemfields") or die "Unable to open field list $itemfields\n"; +while() { + if(/F\((.+)\)/) { + push(@fieldlist, $1); + $sr=$#fieldlist if ($1 =~ /^sellrate$/i); + $id=$#fieldlist if ($1 =~ /^id$/i); + $name=$#fieldlist if ($1 =~ /^name$/i); + $lore=$#fieldlist if ($1 =~ /^lore$/i); + } +} +close(F); + +$db = ""; +$user = ""; +$pass = ""; +$host = ""; +open(F, "<$dbini") or die "Unable to open database config $dbini\n"; +while() { + s/\r//g; + if(/host\s*=\s*(.*)/) { + $host = $1; + } elsif(/user\s*=\s*(.*)/) { + $user = $1; + } elsif(/password\s*=\s*(.*)/) { + $pass = $1; + } elsif(/database\s*=\s*(.*)/) { + $db = $1; + } +} +if(!$db || !$user || !$pass || !$host) { + die "Invalid db.ini, missing one of: host, user, password, database\n"; +} + +$source="DBI:mysql:database=$db;host=$host"; + +my $dbh = DBI->connect($source, $user, $pass) || die "Could not create db handle\n"; + +select(STDOUT); $|=0; + +$sth = $dbh->prepare("select ".join(",",@fieldlist).",serialization from items"); +$sth->execute(); +while (my @data = $sth->fetchrow_array) { + $data[$sr]=sprintf("%.6f",$data[$sr]); + $orig_serialization=$data[$#data]; + $#data--; + + $data[$name]=~s/\|/\\|/g; + $data[$lore]=~s/\|/\\|/g; + $serialized=join('|',@data); + $serialized=~s/"/\\"/g; + + printf("Processing: %d %s",$data[$id],$data[$name]); + if ($serialized ne $orig_serialization) { + printf(" (UPDATED)\n"); + $dbh->do("update items set serialized=now(),serialization=".$dbh->quote($serialized)." where id=".$data[$id]); + } else { + print " \r"; + } +} + +print "\n"; diff --git a/utils/scripts/struct_fields.sh b/utils/scripts/struct_fields.sh new file mode 100644 index 000000000..56fe5f708 --- /dev/null +++ b/utils/scripts/struct_fields.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +sed 's#^.*[ \t]\([^ \t]*\);.*$#\tOUT(\1);#g' diff --git a/utils/scripts/throwpackets.pl b/utils/scripts/throwpackets.pl new file mode 100644 index 000000000..186966ff1 --- /dev/null +++ b/utils/scripts/throwpackets.pl @@ -0,0 +1,53 @@ +#copy this file into your server's dir +#include this file with: require "throwpackets.pl"; +# and add: +# command_add("throwfile", "[opcode name] [filename] - Send a file's hex contents as a packet", 250); +#to your commands_init to enable this + + +sub throwfile { + my $op = shift; + my $file = shift; + my $p = FileToPacket($op, $file); + if(!$p) { + $client->Message(13, "Unable to read file or parse contents."); + return; + } + $p->SendTo($client); + $client->Message(0, "Sent."); +} + +sub HexToPacket { + my $op = shift; + my $hex = shift; + my @lines = split(/\r?\n/, $hex); + my $body = ""; + my @pieces = (); + foreach my $l (@lines) { + if($l =~ /[0-9a-fA-Fx]+:\s*(.*)\s+\|/) { + $l = $1; + } + $l =~ s/\s+-\s+/ /g; + $body .= $l; + } + foreach my $p (split(/\s+/, $body)) { + push(@pieces, "0x$p"); + } + my $p = new PerlPacket($op); + $p->FromArray(\@pieces, $#pieces+1); + return($p); +} + +sub FileToPacket { + my $op = shift; + my $file = shift; + my $c = ""; + open(F, "<$file") || return(undef); + while() { + $c .= $_; + } + close(F); + return(HexToPacket($op, $c)); +} + + diff --git a/utils/sql/queryserv/2079_player_speech.sql b/utils/sql/queryserv/2079_player_speech.sql new file mode 100644 index 000000000..88816d6b9 --- /dev/null +++ b/utils/sql/queryserv/2079_player_speech.sql @@ -0,0 +1,14 @@ +-- ---------------------------- +-- Table structure for player_speech +-- ---------------------------- +CREATE TABLE `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=28 DEFAULT CHARSET=utf8; diff --git a/utils/sql/queryserv/2268_QueryServ.sql b/utils/sql/queryserv/2268_QueryServ.sql new file mode 100644 index 000000000..95b7232ac --- /dev/null +++ b/utils/sql/queryserv/2268_QueryServ.sql @@ -0,0 +1,22 @@ +SET FOREIGN_KEY_CHECKS=0; +-- ---------------------------- +-- 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=67 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; diff --git a/utils/sql/queryserv/2304_QueryServ.sql b/utils/sql/queryserv/2304_QueryServ.sql new file mode 100644 index 000000000..6f2c158c5 --- /dev/null +++ b/utils/sql/queryserv/2304_QueryServ.sql @@ -0,0 +1,41 @@ +SET FOREIGN_KEY_CHECKS=0; +-- ---------------------------- +-- Table structure for `qs_player_npc_kill_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_npc_kill_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/queryserv/2361_QueryServ.sql b/utils/sql/queryserv/2361_QueryServ.sql new file mode 100644 index 000000000..faa195a32 --- /dev/null +++ b/utils/sql/queryserv/2361_QueryServ.sql @@ -0,0 +1,141 @@ +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_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_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; \ No newline at end of file diff --git a/utils/sql/svn/1022_botadventuring.sql b/utils/sql/svn/1022_botadventuring.sql new file mode 100644 index 000000000..98a622d2a --- /dev/null +++ b/utils/sql/svn/1022_botadventuring.sql @@ -0,0 +1,30 @@ +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 $$ + +DELIMITER ; + +DROP VIEW IF EXISTS `vwGroups`; +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; \ No newline at end of file diff --git a/utils/sql/svn/1027_botactives.sql b/utils/sql/svn/1027_botactives.sql new file mode 100644 index 000000000..4875fbe3f --- /dev/null +++ b/utils/sql/svn/1027_botactives.sql @@ -0,0 +1,7 @@ +DROP TABLE IF EXISTS `botactives`; +CREATE TABLE `botactives` ( + `ActiveBotId` int(10) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`ActiveBotId`), + KEY `FK_botactives_1` (`ActiveBotId`), + CONSTRAINT `FK_botactives_1` FOREIGN KEY (`ActiveBotId`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; \ No newline at end of file diff --git a/utils/sql/svn/1030_botzoningsupport.sql b/utils/sql/svn/1030_botzoningsupport.sql new file mode 100644 index 000000000..e76c0d81c --- /dev/null +++ b/utils/sql/svn/1030_botzoningsupport.sql @@ -0,0 +1,3 @@ +DROP TABLE IF EXISTS `botactives`; + +ALTER TABLE `group_id` DROP PRIMARY KEY, ADD PRIMARY KEY USING BTREE(`groupid`, `charid`, `name`); \ No newline at end of file diff --git a/utils/sql/svn/1036_botbuffs.sql b/utils/sql/svn/1036_botbuffs.sql new file mode 100644 index 000000000..d31e51f5d --- /dev/null +++ b/utils/sql/svn/1036_botbuffs.sql @@ -0,0 +1,21 @@ +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', + `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; \ No newline at end of file diff --git a/utils/sql/svn/1038_botpetstatepersists.sql b/utils/sql/svn/1038_botpetstatepersists.sql new file mode 100644 index 000000000..c2bc09b09 --- /dev/null +++ b/utils/sql/svn/1038_botpetstatepersists.sql @@ -0,0 +1,36 @@ +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`) +) 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; \ No newline at end of file diff --git a/utils/sql/svn/1038_grouptablesuniquecolumndefinitions.sql b/utils/sql/svn/1038_grouptablesuniquecolumndefinitions.sql new file mode 100644 index 000000000..92a576396 --- /dev/null +++ b/utils/sql/svn/1038_grouptablesuniquecolumndefinitions.sql @@ -0,0 +1,2 @@ +ALTER TABLE `group_id` ADD UNIQUE INDEX `U_group_id_1`(`name`); +ALTER TABLE `group_leaders` ADD UNIQUE INDEX `U_group_leaders_1`(`leadername`); \ No newline at end of file diff --git a/utils/sql/svn/1039_botguilds.sql b/utils/sql/svn/1039_botguilds.sql new file mode 100644 index 000000000..17b23632d --- /dev/null +++ b/utils/sql/svn/1039_botguilds.sql @@ -0,0 +1,112 @@ +DROP VIEW IF EXISTS `vwGroups`; +DROP FUNCTION IF EXISTS `GetMobType`; + +DELIMITER $$ + +DROP FUNCTION IF EXISTS `GetMobTypeByName` $$ +CREATE FUNCTION `GetMobTypeByName` (mobname VARCHAR(64)) RETURNS CHAR(1) +BEGIN + DECLARE Result CHAR(1); + + SET Result = NULL; + + IF (select id from character_ where name = mobname) > 0 THEN + SET Result = 'C'; + ELSEIF (select BotID from bots where Name = mobname) > 0 THEN + SET Result = 'B'; + END IF; + + RETURN Result; +END $$ + +DELIMITER ; + +DROP VIEW IF EXISTS `vwGroups`; +CREATE VIEW `vwGroups` AS + select g.groupid as groupid, +GetMobTypeByName(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; + +DELIMITER $$ + +DROP FUNCTION IF EXISTS `GetMobTypeById` $$ +CREATE FUNCTION `GetMobTypeById` (mobid INTEGER UNSIGNED) RETURNS CHAR(1) +BEGIN + DECLARE Result CHAR(1); + + SET Result = NULL; + + IF (select id from character_ where id = mobid) > 0 THEN + SET Result = 'C'; + ELSEIF (select BotID from bots where BotID = mobid) > 0 THEN + SET Result = 'B'; + END IF; + + RETURN Result; +END $$ + +DELIMITER ; + +ALTER TABLE `bots` ADD COLUMN `LastZoneId` SMALLINT(6) NOT NULL DEFAULT 0 AFTER `TotalPlayTime`; + +DROP TABLE IF EXISTS `botguildmembers`; +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, + PRIMARY KEY (`char_id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +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 +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 +from botguildmembers as bm; + +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, +unix_timestamp(b.LastSpawnDate) as timelaston, +b.LastZoneId as zoneid +from bots as b; \ No newline at end of file diff --git a/utils/sql/svn/103_optional_chat_rules.sql b/utils/sql/svn/103_optional_chat_rules.sql new file mode 100644 index 000000000..7c7cc2fe5 --- /dev/null +++ b/utils/sql/svn/103_optional_chat_rules.sql @@ -0,0 +1,2 @@ +insert into rule_values values (0, 'Chat:ServerWideOOC', 'true'); +insert into rule_values values (0, 'Chat:ServerWideAuction', 'true'); diff --git a/utils/sql/svn/1040_DeprecatedBotRaidsSystems.sql b/utils/sql/svn/1040_DeprecatedBotRaidsSystems.sql new file mode 100644 index 000000000..452f1592a --- /dev/null +++ b/utils/sql/svn/1040_DeprecatedBotRaidsSystems.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS `botgroups`; \ No newline at end of file diff --git a/utils/sql/svn/104_traps.sql b/utils/sql/svn/104_traps.sql new file mode 100644 index 000000000..793d7eb2b --- /dev/null +++ b/utils/sql/svn/104_traps.sql @@ -0,0 +1,4 @@ +ALTER TABLE `traps` DROP `spawnchance`; +ALTER TABLE `traps` ADD `respawn_time` INT(11) UNSIGNED DEFAULT '60' NOT NULL AFTER `skill`; +ALTER TABLE `traps` ADD `level` MEDIUMINT(4) UNSIGNED DEFAULT '1' NOT NULL AFTER `skill`; +ALTER TABLE `traps` ADD `respawn_var` INT(11) UNSIGNED DEFAULT '0' NOT NULL AFTER `respawn_time`; diff --git a/utils/sql/svn/1057_titles.sql b/utils/sql/svn/1057_titles.sql new file mode 100644 index 000000000..e8f6b291b --- /dev/null +++ b/utils/sql/svn/1057_titles.sql @@ -0,0 +1,9 @@ +CREATE TABLE `player_titlesets` ( + `id` int(11) unsigned NOT NULL auto_increment, + `char_id` int(11) unsigned NOT NULL, + `title_set` int(11) unsigned NOT NULL, + PRIMARY KEY (`id`), + KEY `id` (`id`) +); + +alter table titles add column `title_set` int(11) NOT NULL default '0'; \ No newline at end of file diff --git a/utils/sql/svn/106_optional_proc_rules.sql b/utils/sql/svn/106_optional_proc_rules.sql new file mode 100644 index 000000000..e628e48b9 --- /dev/null +++ b/utils/sql/svn/106_optional_proc_rules.sql @@ -0,0 +1,5 @@ +insert into rule_values values (0, 'Combat:AdjustProcPerMinute', 'true'); +insert into rule_values values (0, 'Combat:AvgProcsPerMinute', '18.0'); +insert into rule_values values (0, 'Combat:ProcPerMinDexContrib', '0.075'); +insert into rule_values values (0, 'Combat:BaseProcChance', '0.035'); +insert into rule_values values (0, 'Combat:ProcDexDivideBy', '11000'); \ No newline at end of file diff --git a/utils/sql/svn/1077_botgroups.sql b/utils/sql/svn/1077_botgroups.sql new file mode 100644 index 000000000..553c5bbd5 --- /dev/null +++ b/utils/sql/svn/1077_botgroups.sql @@ -0,0 +1,35 @@ +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; \ No newline at end of file diff --git a/utils/sql/svn/1136_spell_globals.sql b/utils/sql/svn/1136_spell_globals.sql new file mode 100644 index 000000000..d64b48980 --- /dev/null +++ b/utils/sql/svn/1136_spell_globals.sql @@ -0,0 +1,10 @@ +DROP TABLE IF EXISTS `spell_globals`; +CREATE TABLE `spell_globals` ( + `spellid` int(11) NOT NULL, + `spell_name` varchar(64) NOT NULL default '', + `qglobal` varchar(65) NOT NULL default '', + `value` varchar(65) NOT NULL default '', + PRIMARY KEY (`spellid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +INSERT INTO `rule_values` VALUES ('1', 'Spells:EnableSpellGlobals', 'false', 'If true, spells check the spell_globals table against quest globals before allowing spells to scribe via quest::scribespell'); \ No newline at end of file diff --git a/utils/sql/svn/1144_optional_rule_return_nodrop.sql b/utils/sql/svn/1144_optional_rule_return_nodrop.sql new file mode 100644 index 000000000..06e0cf483 --- /dev/null +++ b/utils/sql/svn/1144_optional_rule_return_nodrop.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('1', 'NPC:ReturnNonQuestNoDropItems', 'false', 'Return NO DROP items on NPCs that do not have an EVENT_ITEM sub in their script'); \ No newline at end of file diff --git a/utils/sql/svn/1195_account_suspendeduntil.sql b/utils/sql/svn/1195_account_suspendeduntil.sql new file mode 100644 index 000000000..25fa0ce50 --- /dev/null +++ b/utils/sql/svn/1195_account_suspendeduntil.sql @@ -0,0 +1,2 @@ +ALTER TABLE `account` ADD `suspendeduntil` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00'; + diff --git a/utils/sql/svn/120_damageshieldtypes.sql b/utils/sql/svn/120_damageshieldtypes.sql new file mode 100644 index 000000000..c0691edca --- /dev/null +++ b/utils/sql/svn/120_damageshieldtypes.sql @@ -0,0 +1,401 @@ +-- +-- +-- type should be one of: +-- +-- 244 - YOU are rotting! / attacker beings to decay +-- 245 - YOU are chilled to the bone / attacker is chilled to the bone +-- 246 - YOU are freezing! / attacker is freezing +-- 247 - YOU are tormented! / attacker is tormented +-- 248 - YOU are burned! / attacker is burned +-- 249 - YOU are pierced by thorns / attacker is pierced by thorns +-- +-- Table structure for table `damageshieldtypes` +-- + +DROP TABLE IF EXISTS `damageshieldtypes`; +CREATE TABLE IF NOT EXISTS `damageshieldtypes` ( + `spellid` int(10) unsigned NOT NULL, + `type` tinyint(3) unsigned NOT NULL, + PRIMARY KEY (`spellid`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +-- +-- The types below are a guess and not guaranteed to be correct. +-- +-- Shield of Brambles +REPLACE into damageshieldtypes values(129,249); -- THORNS +-- Feedback +REPLACE into damageshieldtypes values(191,248); -- BURNT +-- Shield of Thistles +REPLACE into damageshieldtypes values(256,249); -- THORNS +-- Shield of Barbs +REPLACE into damageshieldtypes values(273,249); -- THORNS +-- Shield of Fire +REPLACE into damageshieldtypes values(332,248); -- BURNT +-- Shield of Thorns +REPLACE into damageshieldtypes values(356,249); -- THORNS +-- Banshee Aura +REPLACE into damageshieldtypes values(364,247); -- TORMENTED +-- O'Keils Radiation +REPLACE into damageshieldtypes values(378,248); -- BURNT +-- Shield of Flame +REPLACE into damageshieldtypes values(411,248); -- BURNT +-- Shield of Lava +REPLACE into damageshieldtypes values(412,248); -- BURNT +-- Shield of Spikes +REPLACE into damageshieldtypes values(432,249); -- THORNS +-- Inferno Shield +REPLACE into damageshieldtypes values(479,248); -- BURNT +-- Thistlecoat +REPLACE into damageshieldtypes values(515,249); -- THORNS +-- Barbcoat +REPLACE into damageshieldtypes values(516,249); -- THORNS +-- Bramblecoat +REPLACE into damageshieldtypes values(517,249); -- THORNS +-- Spikecoat +REPLACE into damageshieldtypes values(518,249); -- THORNS +-- Thorncoat +REPLACE into damageshieldtypes values(519,249); -- THORNS +-- Illusion: Fire Elemental +REPLACE into damageshieldtypes values(598,248); -- BURNT +-- Barrier of Combustion +REPLACE into damageshieldtypes values(680,248); -- BURNT +-- Psalm of Warmth +REPLACE into damageshieldtypes values(712,248); -- BURNT +-- Psalm of Cooling +REPLACE into damageshieldtypes values(713,246); -- FREEZING +-- Psalm of Vitality +REPLACE into damageshieldtypes values(715,245); -- CHILLED +-- Psalm of Purity +REPLACE into damageshieldtypes values(716,245); -- CHILLED +-- Chromatic Chaos +REPLACE into damageshieldtypes values(771,247); -- TORMENTED +-- AuraofElementalMastery +REPLACE into damageshieldtypes values(847,248); -- BURNT +-- FireElementalAura +REPLACE into damageshieldtypes values(927,248); -- BURNT +-- Vampire Aura +REPLACE into damageshieldtypes values(930,245); -- CHILLED +-- BarbedBones +REPLACE into damageshieldtypes values(932,249); -- THORNS +-- FrostElementalAura +REPLACE into damageshieldtypes values(953,246); -- FREEZING +-- Fwexar's Rage +REPLACE into damageshieldtypes values(1069,247); -- TORMENTED +-- Spiked Adornment +REPLACE into damageshieldtypes values(1143,249); -- THORNS +-- Bristling Armament +REPLACE into damageshieldtypes values(1249,249); -- THORNS +-- Scorching Skin +REPLACE into damageshieldtypes values(1251,248); -- BURNT +-- Kilva's Skin of Flame +REPLACE into damageshieldtypes values(1331,248); -- BURNT +-- Frostreaver's Blessing +REPLACE into damageshieldtypes values(1350,247); -- TORMENTED +-- Discordant Energy +REPLACE into damageshieldtypes values(1357,248); -- BURNT +-- Fiery Might +REPLACE into damageshieldtypes values(1367,248); -- BURNT +-- O'Keils Flickering Flame +REPLACE into damageshieldtypes values(1419,248); -- BURNT +-- Call of Earth +REPLACE into damageshieldtypes values(1462,249); -- THORNS +-- Bladecoat +REPLACE into damageshieldtypes values(1558,249); -- THORNS +-- Shield of Blades +REPLACE into damageshieldtypes values(1560,249); -- THORNS +-- Legacy of Thorn +REPLACE into damageshieldtypes values(1561,249); -- THORNS +-- Cadeau of Flame +REPLACE into damageshieldtypes values(1667,248); -- BURNT +-- Boon of Immolation +REPLACE into damageshieldtypes values(1668,248); -- BURNT +-- Aegis of Ro +REPLACE into damageshieldtypes values(1669,248); -- BURNT +-- Legacy of Spike +REPLACE into damageshieldtypes values(1727,249); -- THORNS +-- McVaxius` Rousing Rondo +REPLACE into damageshieldtypes values(1760,248); -- BURNT +-- Shield of the Winter +REPLACE into damageshieldtypes values(1795,246); -- FREEZING +-- Acting Fire Shield +REPLACE into damageshieldtypes values(1910,248); -- BURNT +-- Acting Poison Shield +REPLACE into damageshieldtypes values(1911,245); -- CHILLED +-- Acting Cold Shield +REPLACE into damageshieldtypes values(1912,246); -- FREEZING +-- Acting Disease Shield +REPLACE into damageshieldtypes values(1913,244); -- CHILLED +-- Shield of the Eighth +REPLACE into damageshieldtypes values(1963,247); -- TORMENTED +-- Thorny Shield +REPLACE into damageshieldtypes values(1975,249); -- THORNS +-- Aegis of Bathezid +REPLACE into damageshieldtypes values(2018,248); -- BURNT +-- Blessing of the Vah Shir +REPLACE into damageshieldtypes values(2067,247); -- TORMENTED +-- Ancient: Legacy of Blades +REPLACE into damageshieldtypes values(2125,249); -- THORNS +-- Shield of Darkness +REPLACE into damageshieldtypes values(2166,247); -- TORMENTED +-- Icicle Aura +REPLACE into damageshieldtypes values(2197,246); -- FREEZING +-- Tempest Guard +REPLACE into damageshieldtypes values(2316,248); -- BURNT +-- O'Keils Embers +REPLACE into damageshieldtypes values(2551,248); -- BURNT +-- Cloak of the Akheva +REPLACE into damageshieldtypes values(2580,245); -- CHILLED +-- Riftwind's Protection +REPLACE into damageshieldtypes values(2593,249); -- THORNS +-- Force of Nature +REPLACE into damageshieldtypes values(2595,249); -- THORNS +-- Warder's Protection +REPLACE into damageshieldtypes values(2600,249); -- THORNS +-- Unity of Fire +REPLACE into damageshieldtypes values(2673,248); -- BURNT +-- Sonic Feedback +REPLACE into damageshieldtypes values(2773,247); -- TORMENTED +-- Fire Elemental Form I +REPLACE into damageshieldtypes values(2795,248); -- BURNT +-- Fire Elemental Form II +REPLACE into damageshieldtypes values(2796,248); -- BURNT +-- Fire Elemental Form III +REPLACE into damageshieldtypes values(2797,248); -- BURNT +-- Golem Coat +REPLACE into damageshieldtypes values(2831,249); -- THORNS +-- Aura of Vinitras +REPLACE into damageshieldtypes values(2926,245); -- CHILLED +-- Smoldering Bulwark +REPLACE into damageshieldtypes values(2976,248); -- BURNT +-- Corona of Sol +REPLACE into damageshieldtypes values(3006,248); -- BURNT +-- Protection of the Wild +REPLACE into damageshieldtypes values(3039,249); -- THORNS +-- Shield of Eternal Flame +REPLACE into damageshieldtypes values(3056,248); -- BURNT +-- Portal Flames +REPLACE into damageshieldtypes values(3065,248); -- BURNT +-- Flameshield of Ro +REPLACE into damageshieldtypes values(3198,248); -- BURNT +-- Wrath of the Wild +REPLACE into damageshieldtypes values(3255,249); -- THORNS +-- Wrath of the Wild +REPLACE into damageshieldtypes values(3256,249); -- THORNS +-- Wrath of the Wild +REPLACE into damageshieldtypes values(3257,249); -- THORNS +-- Spirit of the Wood +REPLACE into damageshieldtypes values(3277,249); -- THORNS +-- Spirit of the Wood +REPLACE into damageshieldtypes values(3278,249); -- THORNS +-- Spirit of the Wood +REPLACE into damageshieldtypes values(3279,249); -- THORNS +-- Legacy of Bracken +REPLACE into damageshieldtypes values(3295,249); -- THORNS +-- Psalm of Veeshan +REPLACE into damageshieldtypes values(3368,249); -- THORNS +-- Warsong of Zek +REPLACE into damageshieldtypes values(3374,247); -- TORMENTED +-- Call of the Rathe +REPLACE into damageshieldtypes values(3419,247); -- TORMENTED +-- Shield of Bracken +REPLACE into damageshieldtypes values(3448,249); -- THORNS +-- Brackencoat +REPLACE into damageshieldtypes values(3450,249); -- THORNS +-- Maelstrom of Ro +REPLACE into damageshieldtypes values(3486,248); -- BURNT +-- Cloak of Luclin +REPLACE into damageshieldtypes values(3490,245); -- CHILLED +-- O`Keils Levity +REPLACE into damageshieldtypes values(3581,248); -- BURNT +-- Shield of Pain +REPLACE into damageshieldtypes values(3608,247); -- TORMENTED +-- Shield of Torment +REPLACE into damageshieldtypes values(3609,247); -- TORMENTED +-- Pyrokinetic Aura +REPLACE into damageshieldtypes values(3741,248); -- BURNT +-- Aquatic Aura +REPLACE into damageshieldtypes values(3743,247); -- TORMENTED +-- Caustic Aura +REPLACE into damageshieldtypes values(3745,248); -- BURNT +-- Barrier of Hatred +REPLACE into damageshieldtypes values(3766,245); -- CHILLED +-- Thistle Ward +REPLACE into damageshieldtypes values(3858,249); -- THORNS +-- Bramble Ward +REPLACE into damageshieldtypes values(3859,249); -- THORNS +-- Spike Ward +REPLACE into damageshieldtypes values(3860,249); -- THORNS +-- Vengeful Guard +REPLACE into damageshieldtypes values(3871,245); -- CHILLED +-- Aura of the Defender +REPLACE into damageshieldtypes values(3879,248); -- BURNT +-- Plagued Torment +REPLACE into damageshieldtypes values(3916,247); -- TORMENTED +-- Acid Aura +REPLACE into damageshieldtypes values(4115,248); -- BURNT +-- Shield of Pain I +REPLACE into damageshieldtypes values(4219,247); -- TORMENTED +-- Shield of Pain II +REPLACE into damageshieldtypes values(4220,247); -- TORMENTED +-- Shield of Pain III +REPLACE into damageshieldtypes values(4221,247); -- TORMENTED +-- Shield of Pain IV +REPLACE into damageshieldtypes values(4222,247); -- TORMENTED +-- Shield of Pain V +REPLACE into damageshieldtypes values(4223,247); -- TORMENTED +-- Shield of Pain VI +REPLACE into damageshieldtypes values(4224,247); -- TORMENTED +-- Shield of Pain VII +REPLACE into damageshieldtypes values(4225,247); -- TORMENTED +-- Shield of Pain VIII +REPLACE into damageshieldtypes values(4226,247); -- TORMENTED +-- Shield of Pain IX +REPLACE into damageshieldtypes values(4227,247); -- TORMENTED +-- Shield of Pain X +REPLACE into damageshieldtypes values(4228,247); -- TORMENTED +-- Icicle Shield +REPLACE into damageshieldtypes values(4289,246); -- FREEZING +-- Allure of Hatred +REPLACE into damageshieldtypes values(4441,247); -- TORMENTED +-- Petrad's Protection +REPLACE into damageshieldtypes values(4473,248); -- BURNT +-- Fire Elemental Form IV +REPLACE into damageshieldtypes values(4560,248); -- BURNT +-- Fire Elemental Form V +REPLACE into damageshieldtypes values(4561,248); -- BURNT +-- Aura of the Hunter +REPLACE into damageshieldtypes values(4663,249); -- THORNS +-- Blood Shield +REPLACE into damageshieldtypes values(4703,248); -- BURNT +-- Gleaming Armor +REPLACE into damageshieldtypes values(4715,248); -- BURNT +-- Bloodhound's Revenge +REPLACE into damageshieldtypes values(4731,248); -- BURNT +-- Haste of the Tunat'Muram +REPLACE into damageshieldtypes values(4740,249); -- THORNS +-- Protection of Discord I +REPLACE into damageshieldtypes values(4744,247); -- TORMENTED +-- Protection of Discord II +REPLACE into damageshieldtypes values(4745,247); -- TORMENTED +-- Protection of Discord III +REPLACE into damageshieldtypes values(4746,247); -- TORMENTED +-- Malevolent Retribution +REPLACE into damageshieldtypes values(4848,245); -- CHILLED +-- War March of the Mastruq +REPLACE into damageshieldtypes values(4871,247); -- TORMENTED +-- Arrow of Intensity +REPLACE into damageshieldtypes values(4999,248); -- BURNT +-- Frost Shield +REPLACE into damageshieldtypes values(5098,246); -- FREEZING +-- Frost Guard +REPLACE into damageshieldtypes values(5099,246); -- FREEZING +-- Basilisk Aura +REPLACE into damageshieldtypes values(5171,248); -- BURNT +-- Shield of Briar +REPLACE into damageshieldtypes values(5302,249); -- THORNS +-- Guard of the Earth +REPLACE into damageshieldtypes values(5305,249); -- THORNS +-- Briarcoat +REPLACE into damageshieldtypes values(5307,249); -- THORNS +-- Ward of the Hunter +REPLACE into damageshieldtypes values(5317,249); -- THORNS +-- Cloak of Discord +REPLACE into damageshieldtypes values(5339,248); -- BURNT +-- Nettle Shield +REPLACE into damageshieldtypes values(5358,249); -- THORNS +-- Nettlecoat +REPLACE into damageshieldtypes values(5362,249); -- THORNS +-- Circle of Nettles +REPLACE into damageshieldtypes values(5365,249); -- THORNS +-- War March of Muram +REPLACE into damageshieldtypes values(5376,247); -- TORMENTED +-- Fireskin +REPLACE into damageshieldtypes values(5466,248); -- BURNT +-- Circle of Fireskin +REPLACE into damageshieldtypes values(5488,248); -- BURNT +-- Pyrilen Skin +REPLACE into damageshieldtypes values(5492,248); -- BURNT +-- Jagged Spikes +REPLACE into damageshieldtypes values(5698,249); -- THORNS +-- Flickering Fire +REPLACE into damageshieldtypes values(5705,248); -- BURNT +-- Splinters +REPLACE into damageshieldtypes values(5802,249); -- THORNS +-- Electric Shock +REPLACE into damageshieldtypes values(5810,248); -- BURNT +-- Static Shield +REPLACE into damageshieldtypes values(5811,248); -- BURNT +-- Flamegore's Fire +REPLACE into damageshieldtypes values(5817,248); -- BURNT +-- Spirit of the Grove +REPLACE into damageshieldtypes values(5881,249); -- THORNS +-- Spirit of the Grove +REPLACE into damageshieldtypes values(5882,249); -- THORNS +-- Spirit of the Grove +REPLACE into damageshieldtypes values(5883,249); -- THORNS +-- Pyrilen Ember +REPLACE into damageshieldtypes values(5997,248); -- BURNT +-- Gelidran Sleet +REPLACE into damageshieldtypes values(6006,246); -- FREEZING +-- Ancient: Veil of Pyrilonus +REPLACE into damageshieldtypes values(6145,248); -- BURNT +-- Hungry Vines Recourse +REPLACE into damageshieldtypes values(6154,249); -- THORNS +-- Discordant Spikes +REPLACE into damageshieldtypes values(6160,249); -- THORNS +-- Hateful Guard +REPLACE into damageshieldtypes values(6373,247); -- TORMENTED +-- Malleable Ice +REPLACE into damageshieldtypes values(6558,246); -- FREEZING +-- Mass Illusion: Fire Elemental +REPLACE into damageshieldtypes values(6581,248); -- BURNT +-- Vishimtar's Aura +REPLACE into damageshieldtypes values(6643,245); -- CHILLED +-- Embrace of Life +REPLACE into damageshieldtypes values(6650,247); -- TORMENTED +-- Storm Guard +REPLACE into damageshieldtypes values(6769,247); -- TORMENTED +-- Razor Bones +REPLACE into damageshieldtypes values(6833,249); -- THORNS +-- Ithiasor's Aura +REPLACE into damageshieldtypes values(6887,245); -- CHILLED +-- Clan Aura +REPLACE into damageshieldtypes values(7082,247); -- TORMENTED +-- Orcish Bulwark +REPLACE into damageshieldtypes values(7129,247); -- TORMENTED +-- Fire Skin I +REPLACE into damageshieldtypes values(7589,248); -- BURNT +-- Fire Skin II +REPLACE into damageshieldtypes values(7590,248); -- BURNT +-- Fire Skin III +REPLACE into damageshieldtypes values(7591,248); -- BURNT +-- Fire Skin IV +REPLACE into damageshieldtypes values(7592,248); -- BURNT +-- Fire Skin V +REPLACE into damageshieldtypes values(7593,248); -- BURNT +-- Fire Skin VI +REPLACE into damageshieldtypes values(7594,248); -- BURNT +-- Fire Skin VII +REPLACE into damageshieldtypes values(7595,248); -- BURNT +-- Fire Skin VIII +REPLACE into damageshieldtypes values(7596,248); -- BURNT +-- Fire Skin IX +REPLACE into damageshieldtypes values(7597,248); -- BURNT +-- Fire Skin X +REPLACE into damageshieldtypes values(7598,248); -- BURNT +-- Root Rage +REPLACE into damageshieldtypes values(7702,247); -- TORMENTED +-- Last Stand +REPLACE into damageshieldtypes values(7715,247); -- TORMENTED +-- BloodBarbs +REPLACE into damageshieldtypes values(7747,249); -- THORNS +-- Reaper's Stance +REPLACE into damageshieldtypes values(8189,247); -- TORMENTED +-- Convergence of Spirits +REPLACE into damageshieldtypes values(8190,249); -- THORNS +-- Convergence of Spirits +REPLACE into damageshieldtypes values(8191,249); -- THORNS +-- Convergence of Spirits +REPLACE into damageshieldtypes values(8192,249); -- THORNS +-- Spiritfury +REPLACE into damageshieldtypes values(8246,247); -- TORMENTED diff --git a/utils/sql/svn/1259_npc_skill_types.sql b/utils/sql/svn/1259_npc_skill_types.sql new file mode 100644 index 000000000..d59e932f9 --- /dev/null +++ b/utils/sql/svn/1259_npc_skill_types.sql @@ -0,0 +1,2 @@ +ALTER TABLE `npc_types` ADD COLUMN `prim_melee_type` TINYINT(4) UNSIGNED NOT NULL DEFAULT 28 AFTER `d_meele_texture2`; +ALTER TABLE `npc_types` ADD COLUMN `sec_melee_type` TINYINT(4) UNSIGNED NOT NULL DEFAULT 28 AFTER `prim_melee_type`; diff --git a/utils/sql/svn/125_aggrozone.sql b/utils/sql/svn/125_aggrozone.sql new file mode 100644 index 000000000..088b1b7ee --- /dev/null +++ b/utils/sql/svn/125_aggrozone.sql @@ -0,0 +1 @@ +INSERT INTO commands VALUES ('aggrozone', 100, '[aggro] - Aggro every mob in the zone with X aggro. Default is 0. Not recommend if you\'re not invulnerable.'); \ No newline at end of file diff --git a/utils/sql/svn/127_optional_spell_rules.sql b/utils/sql/svn/127_optional_spell_rules.sql new file mode 100644 index 000000000..6645b8d20 --- /dev/null +++ b/utils/sql/svn/127_optional_spell_rules.sql @@ -0,0 +1,7 @@ +INSERT INTO rule_values VALUES + (0, "Spells:BaseCritChance", 0), + (0, "Spells:BaseCritRatio", 0), + (0, "Spells:WizCritLevel", 12), + (0, "Spells:WizCritChance", 7), + (0, "Spells:WizCritRatio", 15) +; diff --git a/utils/sql/svn/1280_bot_augs.sql b/utils/sql/svn/1280_bot_augs.sql new file mode 100644 index 000000000..091ced6e3 --- /dev/null +++ b/utils/sql/svn/1280_bot_augs.sql @@ -0,0 +1,18 @@ +-- Required +ALTER TABLE botinventory ADD COLUMN + (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); + +-- only required for updating existing bots +UPDATE botinventory SET charges=(select maxcharges from items WHERE id=itemid) WHERE (select maxcharges from items WHERE id=itemid)>=0; +UPDATE botinventory SET charges=255 WHERE (select maxcharges from items WHERE id=itemid)=(-1); +UPDATE botinventory SET charges=1 WHERE charges=0; +UPDATE botinventory SET color=(select color from items WHERE id=itemid) WHERE itemid=(SELECT id FROM items WHERE id=itemid); +UPDATE botinventory SET instnodrop=(select attuneable from items WHERE id=itemid) WHERE itemid=(SELECT id FROM items WHERE id=itemid); + diff --git a/utils/sql/svn/1290_optional_exp_loss_rule.sql b/utils/sql/svn/1290_optional_exp_loss_rule.sql new file mode 100644 index 000000000..fd57215a1 --- /dev/null +++ b/utils/sql/svn/1290_optional_exp_loss_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('1', 'Character:DeathExpLossMaxLevel', '255', 'Any level greater than this will no longer lose exp on death'); \ No newline at end of file diff --git a/utils/sql/svn/1293_guild_bank.sql b/utils/sql/svn/1293_guild_bank.sql new file mode 100644 index 000000000..80711e827 --- /dev/null +++ b/utils/sql/svn/1293_guild_bank.sql @@ -0,0 +1,15 @@ +CREATE TABLE `guild_bank` ( + `guildid` int(10) unsigned NOT NULL default '0', + `area` tinyint(3) unsigned NOT NULL default '0', + `slot` int(4) unsigned NOT NULL default '0', + `itemid` int(10) unsigned NOT NULL default '0', + `qty` int(10) unsigned NOT NULL default '0', + `donator` varchar(64) default NULL, + `permissions` tinyint(3) unsigned NOT NULL default '0', + `whofor` varchar(64) default NULL, + KEY `guildid` (`guildid`), + KEY `area` (`area`), + KEY `slot` (`slot`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +insert into rule_values values (1,'World:GuildBankZoneID','345', 'ID of zone the Guild Bank works in. Default 345, guildhall'); diff --git a/utils/sql/svn/129_optional_shared_plat_rule.sql b/utils/sql/svn/129_optional_shared_plat_rule.sql new file mode 100644 index 000000000..4eb669495 --- /dev/null +++ b/utils/sql/svn/129_optional_shared_plat_rule.sql @@ -0,0 +1 @@ +INSERT INTO rule_values VALUES (0, "Character:SharedBankPlat", false); \ No newline at end of file diff --git a/utils/sql/svn/131_optional_combat_rules.sql b/utils/sql/svn/131_optional_combat_rules.sql new file mode 100644 index 000000000..5087d9c28 --- /dev/null +++ b/utils/sql/svn/131_optional_combat_rules.sql @@ -0,0 +1,6 @@ +INSERT INTO rule_values VALUES + (0, "Spells:ResistPerLevelDiff", 85), + (0, "Combat:BaseHitChance", 54.0), + (0, "Combat:HitPerLevelDiff", 145), + (0, "Combat:AgiHitFactor", 0.015) +; \ No newline at end of file diff --git a/utils/sql/svn/133_task_repeatable.sql b/utils/sql/svn/133_task_repeatable.sql new file mode 100644 index 000000000..d607affa7 --- /dev/null +++ b/utils/sql/svn/133_task_repeatable.sql @@ -0,0 +1,2 @@ +ALTER TABLE `tasks` ADD `repeatable` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '1'; + diff --git a/utils/sql/svn/1379_loginserver_trusted_server.sql b/utils/sql/svn/1379_loginserver_trusted_server.sql new file mode 100644 index 000000000..7977f3d5c --- /dev/null +++ b/utils/sql/svn/1379_loginserver_trusted_server.sql @@ -0,0 +1 @@ +alter table tblWorldServerRegistration add ServerTrusted integer NOT NULL after ServerAdminID; diff --git a/utils/sql/svn/1392_recipe_learning.sql b/utils/sql/svn/1392_recipe_learning.sql new file mode 100644 index 000000000..68f784254 --- /dev/null +++ b/utils/sql/svn/1392_recipe_learning.sql @@ -0,0 +1,12 @@ +create table `char_recipe_list` ( + `char_id` int NOT NULL, + `recipe_id` int NOT NULL, + `madecount` int NOT NULL default 0, + primary key (`char_id`, `recipe_id`) +) Engine=InnoDB; + +alter table `tradeskill_recipe` add column `must_learn` tinyint not null default 0; + +insert into rule_values (ruleset_id, rule_name, rule_value, notes) values +(1, 'Skills:UseLimitTradeskillSearchSkillDiff', 'true', 'Enables the limit for the maximum difference between trivial and skill for recipe searches and favorites.'), +(1, 'Skills:MaxTradeskillSearchSkillDiff', '50', 'The maximum difference in skill between the trivial of an item and the skill of the player if the trivial is higher than the skill. Recipes that have not been learnt or made at least once via the Experiment mode will be removed from searches based on this criteria.'); \ No newline at end of file diff --git a/utils/sql/svn/1394_optional_rule_sod_hp_mana_end.sql b/utils/sql/svn/1394_optional_rule_sod_hp_mana_end.sql new file mode 100644 index 000000000..f77ab2c1a --- /dev/null +++ b/utils/sql/svn/1394_optional_rule_sod_hp_mana_end.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('1', 'Character:SoDClientUseSoDHPManaEnd', 'false', 'Setting this to true will allow SoD clients to use the SoD HP/Mana/End formulas and previous clients will use the old formulas'); \ No newline at end of file diff --git a/utils/sql/svn/1404_faction_list.sql b/utils/sql/svn/1404_faction_list.sql new file mode 100644 index 000000000..66fbd0627 --- /dev/null +++ b/utils/sql/svn/1404_faction_list.sql @@ -0,0 +1,7 @@ +ALTER TABLE `faction_list` ADD COLUMN `mod_r42` smallint(6) NOT NULL default '0' AFTER `mod_r14`; +ALTER TABLE `faction_list` ADD COLUMN `mod_r367` smallint(6) NOT NULL default '0' AFTER `mod_r330`; +ALTER TABLE `faction_list` ADD COLUMN `mod_r522` smallint(6) NOT NULL default '0' AFTER `mod_r367`; +UPDATE faction_list SET mod_r367 = mod_r60; +UPDATE faction_list SET mod_r42 = mod_r120; +ALTER TABLE `faction_list` DROP COLUMN `mod_r60`; +ALTER TABLE `faction_list` DROP COLUMN `mod_r120`; \ No newline at end of file diff --git a/utils/sql/svn/1410_optional_sod_aas_ht_and_loh.sql b/utils/sql/svn/1410_optional_sod_aas_ht_and_loh.sql new file mode 100644 index 000000000..7d377c7df --- /dev/null +++ b/utils/sql/svn/1410_optional_sod_aas_ht_and_loh.sql @@ -0,0 +1,40 @@ +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) VALUES +('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'), +('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'); + +INSERT INTO aa_actions (aaid, rank, reuse_time, spell_id, target, nonspell_action, nonspell_mana, nonspell_duration, redux_aa, redux_rate, redux_aa2, redux_rate2) VALUES +('7800', '0', '4320', '13531', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '1', '4320', '13532', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '2', '4320', '13533', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '3', '4320', '13534', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '4', '4320', '13535', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '5', '4320', '13536', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '6', '4320', '13537', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '7', '4320', '13538', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '8', '4320', '13539', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '9', '4320', '13540', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '10', '4320', '13541', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '11', '4320', '13542', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '12', '4320', '13543', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '13', '4320', '13544', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '14', '4320', '13545', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '15', '4320', '13562', '2', '0', '0', '0', '0', '0', '0', '0'), +('7800', '16', '4320', '13563', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '0', '4320', '13546', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '1', '4320', '13547', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '2', '4320', '13548', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '3', '4320', '13549', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '4', '4320', '13550', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '5', '4320', '13551', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '6', '4320', '13552', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '7', '4320', '13553', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '8', '4320', '13554', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '9', '4320', '13555', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '10', '4320', '13556', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '11', '4320', '13557', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '12', '4320', '13558', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '13', '4320', '13559', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '14', '4320', '13560', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '15', '4320', '13561', '2', '0', '0', '0', '0', '0', '0', '0'), +('7850', '16', '4320', '16028', '2', '0', '0', '0', '0', '0', '0', '0'); \ No newline at end of file diff --git a/utils/sql/svn/142_deathpeace_and_lifetap_aas.sql b/utils/sql/svn/142_deathpeace_and_lifetap_aas.sql new file mode 100644 index 000000000..59b596717 --- /dev/null +++ b/utils/sql/svn/142_deathpeace_and_lifetap_aas.sql @@ -0,0 +1,26 @@ +DELETE FROM `altadv_vars` WHERE `skill_id` = '844'; +DELETE FROM `altadv_vars` WHERE `skill_id` = '1319'; +DELETE FROM `altadv_vars` WHERE `skill_id` = '209'; +DELETE FROM `altadv_vars` WHERE `skill_id` = '1134'; +DELETE FROM `altadv_vars` WHERE `skill_id` = '1158'; + +UPDATE `altadv_vars` SET `prereq_skill`=`prereq_skill`+1 Where `prereq_skill`>=86; +UPDATE `altadv_vars` SET `prereq_skill`=`prereq_skill`+1 Where `prereq_skill`>=226; + +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) VALUES +(844, 'Advanced Theft of Life', 5, 2, 31455, 31456, 31453, 31454, 6, 4294967295, 202, 3, 0, 0, 2080, 0, 65, 0); + +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) VALUES +(1319, 'Soul Thief', 5, 3, 31459, 31460, 31457, 31458, 7, 4294967295, 227, 2, 0, 0, 2080, 0, 68, 0); + +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) VALUES +(209, 'Death Peace', 5, 1, 13738, 13739, 13736, 13737, 7, 4294967295, 0, 0, 0, 5, 2080, 0, 65, 0); + +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) VALUES +(1134, 'Blur of Axes', 3, 3, 31463, 31464, 31461, 31462, 5, 4294967295, 0, 0, 0, 0, 0, 1, 61, 1); + +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) VALUES +(1158, 'Vicious Frenzy', 4, 5, 31467, 31468, 31465, 31466, 7, 4294967295, 250, 3, 0, 0, 0, 1, 67, 0); + +INSERT INTO aa_actions (aaid, rank, reuse_time, spell_id, target, nonspell_action, nonspell_mana, nonspell_duration, redux_aa, redux_rate) VALUES +(209, 0, 5, 5919, 0, 0, 0, 0, 0, 0); \ No newline at end of file diff --git a/utils/sql/svn/1436_login_server_table_fix.sql b/utils/sql/svn/1436_login_server_table_fix.sql new file mode 100644 index 000000000..86fa0ac61 --- /dev/null +++ b/utils/sql/svn/1436_login_server_table_fix.sql @@ -0,0 +1 @@ +ALTER TABLE tblWorldServerRegistration MODIFY ServerTagDescription varchar(50) NOT NULL DEFAULT ''; diff --git a/utils/sql/svn/1446_allowrest_optional.sql b/utils/sql/svn/1446_allowrest_optional.sql new file mode 100644 index 000000000..c763bf293 --- /dev/null +++ b/utils/sql/svn/1446_allowrest_optional.sql @@ -0,0 +1,152 @@ +-- +-- IMPORTANT NOTE: +-- +-- If you source in this SQL, then you should export a custom spell file and distribute it to the players on your server, otherwise +-- the rest-state as displayed in the UI (SoF and SoD) may not reflect the rest-state as determined by the server. +-- +-- All the spells that follow are those that allow rest-state regen IN THE SoD CLIENT. +-- +-- If your server does not support accelerated rest-state regen, then you do not need this file. +-- +-- The first set of spell IDs are within the range of the Titanium spell file. +-- +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 110; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 111; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 112; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 676; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 677; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 678; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 1577; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 1578; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 1699; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 1702; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 1704; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 1772; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 2464; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 2468; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 2473; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 2475; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 3342; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 3387; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 3395; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 3650; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 5051; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 5573; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 5984; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 5985; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 5986; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7023; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7024; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7025; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7026; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7800; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7801; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7802; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7803; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7804; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7805; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7806; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7807; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7808; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7809; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7810; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7811; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7812; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7813; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7814; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7815; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7816; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7817; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7818; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7819; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7820; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7821; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7822; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7823; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 7824; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 8012; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 8023; +-- +-- Spells from SoF and SoD that have the same name in both clients. +-- +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 8963; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 9119; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 9120; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 9121; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 9980; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 9981; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 9982; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 10068; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 10069; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 10070; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 10608; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 10609; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 10610; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 10801; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 10825; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 10844; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 11656; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 11837; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 11848; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 11850; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 11882; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 11883; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 11884; + +-- Some of the following spells appear to have changed between SoF and SoD. You should not +-- execute the following if you are not using a spells_new table generated from the SOD spells_us.txt +-- without examining each spell to check if for your spells_new table, it should allow rest state. +-- +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 14488; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 14515; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 14516; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 14517; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 14659; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 14660; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 14661; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15074; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15329; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15330; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15331; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15414; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15437; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15454; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15489; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15503; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15551; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15552; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15553; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15557; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15558; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15559; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15563; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15564; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15565; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15569; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15570; +-- UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 15571; +-- +-- SoD only spells. +-- +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 16534; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 17607; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 18528; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 18573; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 18574; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 18575; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 18726; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 18727; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 18728; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 19206; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 19491; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 19492; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 19493; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 19594; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 19617; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 19618; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 19619; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 19638; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 19639; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 19640; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 19677; +UPDATE `spells_new` SET `allowrest` = 1 WHERE `id` = 19691; \ No newline at end of file diff --git a/utils/sql/svn/1446_allowrest_required.sql b/utils/sql/svn/1446_allowrest_required.sql new file mode 100644 index 000000000..4b5739a2c --- /dev/null +++ b/utils/sql/svn/1446_allowrest_required.sql @@ -0,0 +1 @@ +ALTER TABLE `spells_new` CHANGE `field212` `allowrest` INT( 11 ) NULL DEFAULT '0'; diff --git a/utils/sql/svn/1450_cvs.sql b/utils/sql/svn/1450_cvs.sql new file mode 100644 index 000000000..4e358db26 --- /dev/null +++ b/utils/sql/svn/1450_cvs.sql @@ -0,0 +1 @@ +INSERT INTO commands VALUES ('cvs', 200, ' - Summary of client versions currently online.'); diff --git a/utils/sql/svn/1451_guilds.sql b/utils/sql/svn/1451_guilds.sql new file mode 100644 index 000000000..5fad62365 --- /dev/null +++ b/utils/sql/svn/1451_guilds.sql @@ -0,0 +1,2 @@ +ALTER TABLE `guild_members` ADD `alt` TINYINT UNSIGNED NOT NULL DEFAULT '0' AFTER `public_note` ; +ALTER TABLE `guilds` ADD `channel` VARCHAR( 128 ) NOT NULL DEFAULT '', ADD `url` VARCHAR( 512 ) NOT NULL DEFAULT ''; diff --git a/utils/sql/svn/1498_instance_adventure.sql b/utils/sql/svn/1498_instance_adventure.sql new file mode 100644 index 000000000..8aa48797f --- /dev/null +++ b/utils/sql/svn/1498_instance_adventure.sql @@ -0,0 +1,8 @@ +ALTER TABLE zone DROP PRIMARY KEY, DROP INDEX zoneidnumber, ADD INDEX zoneidnumber (zoneidnumber), ADD INDEX zonename (short_name); +ALTER TABLE zone ADD ruleset INT UNSIGNED DEFAULT '0' NOT NULL AFTER weather; +ALTER TABLE zone ADD version TINYINT UNSIGNED DEFAULT '0' NOT NULL AFTER zoneidnumber; +ALTER TABLE adventure_template ADD graveyard_zone_id INT UNSIGNED DEFAULT '0' NOT NULL AFTER dest_h; +ALTER TABLE adventure_template ADD graveyard_x FLOAT DEFAULT '0' NOT NULL AFTER graveyard_zone_id; +ALTER TABLE adventure_template ADD graveyard_y FLOAT DEFAULT '0' NOT NULL AFTER graveyard_x; +ALTER TABLE adventure_template ADD graveyard_z FLOAT DEFAULT '0' NOT NULL AFTER graveyard_y; +ALTER TABLE adventure_template ADD graveyard_radius FLOAT UNSIGNED DEFAULT '0.0' NOT NULL AFTER graveyard_z; \ No newline at end of file diff --git a/utils/sql/svn/14_optional_merchantlist.sql b/utils/sql/svn/14_optional_merchantlist.sql new file mode 100644 index 000000000..9b496e1d8 --- /dev/null +++ b/utils/sql/svn/14_optional_merchantlist.sql @@ -0,0 +1 @@ +insert into rule_values values (0,'World:ClearTempMerchantlist','true'); diff --git a/utils/sql/svn/1510_global_instances.sql b/utils/sql/svn/1510_global_instances.sql new file mode 100644 index 000000000..a60ea4bb2 --- /dev/null +++ b/utils/sql/svn/1510_global_instances.sql @@ -0,0 +1,2 @@ +ALTER TABLE instance_lockout ADD never_expires TINYINT UNSIGNED DEFAULT '0' NOT NULL AFTER duration; +ALTER TABLE instance_lockout ADD is_global TINYINT UNSIGNED DEFAULT '0' NOT NULL AFTER version; \ No newline at end of file diff --git a/utils/sql/svn/1511_map_path_loading.sql b/utils/sql/svn/1511_map_path_loading.sql new file mode 100644 index 000000000..14fb49ca2 --- /dev/null +++ b/utils/sql/svn/1511_map_path_loading.sql @@ -0,0 +1 @@ +ALTER TABLE zone ADD map_file_name varchar(100) default NULL AFTER long_name; \ No newline at end of file diff --git a/utils/sql/svn/1513_zone_points.sql b/utils/sql/svn/1513_zone_points.sql new file mode 100644 index 000000000..4f0ac0180 --- /dev/null +++ b/utils/sql/svn/1513_zone_points.sql @@ -0,0 +1,7 @@ +ALTER TABLE `zone_points` ADD `version` int UNSIGNED default 0 NOT NULL AFTER `zone`; +ALTER TABLE `zone_points` ADD `target_instance` int UNSIGNED default 0 NOT NULL AFTER `target_zone_id`; +ALTER TABLE `zone_points` ADD `client_version_mask` int UNSIGNED default 4294967295 NOT NULL AFTER `buffer`; +ALTER TABLE `zone_points` DROP INDEX `NewIndex`, ADD INDEX `NewIndex` (`number`, `zone`); +ALTER TABLE `zone` ADD fog_density float default 0.0 NOT NULL AFTER fog_maxclip4; +ALTER TABLE `doors` ADD `dest_instance` int UNSIGNED default 0 NOT NULL AFTER `dest_zone`; +ALTER TABLE `doors` ADD `client_version_mask` int UNSIGNED default 4294967295 NOT NULL AFTER `buffer`; \ No newline at end of file diff --git a/utils/sql/svn/1519_zone_primary_key_id.sql b/utils/sql/svn/1519_zone_primary_key_id.sql new file mode 100644 index 000000000..54fd47971 --- /dev/null +++ b/utils/sql/svn/1519_zone_primary_key_id.sql @@ -0,0 +1 @@ +ALTER TABLE `zone` ADD `id` int(10) NOT NULL auto_increment PRIMARY KEY AFTER `short_name`; \ No newline at end of file diff --git a/utils/sql/svn/1542_items_table_cleanup.sql b/utils/sql/svn/1542_items_table_cleanup.sql new file mode 100644 index 000000000..da75a3939 --- /dev/null +++ b/utils/sql/svn/1542_items_table_cleanup.sql @@ -0,0 +1,32 @@ +-- This renames some unknown fields to known fields according to 13th floor data +-- +ALTER TABLE items CHANGE UNK061 elitematerial INT(11) DEFAULT 0 NOT NULL; +ALTER TABLE items CHANGE UNK098 ldonsellbackrate INT(11) DEFAULT 70 NOT NULL; +ALTER TABLE items CHANGE UNK129 scriptfileid INT(11) DEFAULT 0 NOT NULL; +ALTER TABLE items CHANGE UNK131 expendablearrow INT(11) DEFAULT 0 NOT NULL; +-- +-- The summonedflag field simply puts the Summoned Item note on newer summoned items, it has no real effect on gameplay +-- If you have summonedflag already set for custom items, you may not want to use the line below to copy UNK109 to summonedflag. +-- +UPDATE items SET summonedflag = UNK109; +-- +-- This drops multiple unknown fields that are not used and never will be +-- +ALTER TABLE items DROP UNK109; +ALTER TABLE items DROP unknown002; +ALTER TABLE items DROP unknown003; +ALTER TABLE items DROP unknown005; +ALTER TABLE items DROP unknown007; +ALTER TABLE items DROP unknown018; +ALTER TABLE items DROP unknown019; +ALTER TABLE items DROP unknown020; +ALTER TABLE items DROP unknown061; +ALTER TABLE items DROP unknown067; +ALTER TABLE items DROP unknown069; +ALTER TABLE items DROP unknown081; +ALTER TABLE items DROP unknown105; +ALTER TABLE items DROP unknown122; +ALTER TABLE items DROP unknown123; +ALTER TABLE items DROP unknown124; +ALTER TABLE items DROP unknown128; +ALTER TABLE items DROP unknown133; \ No newline at end of file diff --git a/utils/sql/svn/1548_nimbuseffect_required.sql b/utils/sql/svn/1548_nimbuseffect_required.sql new file mode 100644 index 000000000..e3800f88e --- /dev/null +++ b/utils/sql/svn/1548_nimbuseffect_required.sql @@ -0,0 +1 @@ +ALTER TABLE `spells_new` CHANGE `field193` `nimbuseffect` INT( 11 ) NULL DEFAULT '0'; diff --git a/utils/sql/svn/1562_instanced_spawnconditions.sql b/utils/sql/svn/1562_instanced_spawnconditions.sql new file mode 100644 index 000000000..7e8809048 --- /dev/null +++ b/utils/sql/svn/1562_instanced_spawnconditions.sql @@ -0,0 +1,8 @@ +CREATE TABLE `spawn_condition_values` ( + `id` int(10) unsigned NOT NULL, + `value` tinyint(3) unsigned default NULL, + `zone` varchar(64) NOT NULL, + `instance_id` int(10) unsigned NOT NULL, + UNIQUE KEY `instance` (`id`,`instance_id`,`zone`), + KEY `zoneinstance` (`zone`,`instance_id`) +) ENGINE=InnoDB; \ No newline at end of file diff --git a/utils/sql/svn/1586_waypoints_optional.sql b/utils/sql/svn/1586_waypoints_optional.sql new file mode 100644 index 000000000..82f7fb758 --- /dev/null +++ b/utils/sql/svn/1586_waypoints_optional.sql @@ -0,0 +1 @@ +UPDATE grid_entries SET heading = -1; \ No newline at end of file diff --git a/utils/sql/svn/158_optional_death_exp_loss.sql b/utils/sql/svn/158_optional_death_exp_loss.sql new file mode 100644 index 000000000..3ddeb678e --- /dev/null +++ b/utils/sql/svn/158_optional_death_exp_loss.sql @@ -0,0 +1 @@ +INSERT INTO rule_values VALUES (0, 'Character:DeathExpLossMultiplier', 3); diff --git a/utils/sql/svn/1610_tradeskill_required.sql b/utils/sql/svn/1610_tradeskill_required.sql new file mode 100644 index 000000000..5ce805bd1 --- /dev/null +++ b/utils/sql/svn/1610_tradeskill_required.sql @@ -0,0 +1 @@ +ALTER TABLE tradeskill_recipe ADD COLUMN quest TINYINT(1) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/utils/sql/svn/1618_zone.sql b/utils/sql/svn/1618_zone.sql new file mode 100644 index 000000000..55f9952e3 --- /dev/null +++ b/utils/sql/svn/1618_zone.sql @@ -0,0 +1,2 @@ +ALTER TABLE zone ADD suspendbuffs tinyint(1) unsigned NOT NULL DEFAULT 0; +UPDATE zone SET suspendbuffs = 1 WHERE short_name IN ('guildlobby', 'guildhall'); diff --git a/utils/sql/svn/1625_optional_rule_class_race_exp_bonus.sql b/utils/sql/svn/1625_optional_rule_class_race_exp_bonus.sql new file mode 100644 index 000000000..1fadabf65 --- /dev/null +++ b/utils/sql/svn/1625_optional_rule_class_race_exp_bonus.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('1', 'Character:UseRaceClassExpBonuses', 'true', 'Setting this to true will enable Class and Racial experience rate bonuses'); \ No newline at end of file diff --git a/utils/sql/svn/1672_optional_rules_respawn_window.sql b/utils/sql/svn/1672_optional_rules_respawn_window.sql new file mode 100644 index 000000000..09511738c --- /dev/null +++ b/utils/sql/svn/1672_optional_rules_respawn_window.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` VALUES ('1', 'Character:RespawnFromHover', 'true', 'Enable Respawn Window for SoF and later clients.'); +INSERT INTO `rule_values` VALUES ('1', 'Character:RespawnFromHoverTimer', 300, 'Respawn Window Timer in Seconds.'); diff --git a/utils/sql/svn/1679_optional_rules_blocked_buffs.sql b/utils/sql/svn/1679_optional_rules_blocked_buffs.sql new file mode 100644 index 000000000..f015cdf2b --- /dev/null +++ b/utils/sql/svn/1679_optional_rules_blocked_buffs.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:EnableBlockedBuffs', 'true', ''); + diff --git a/utils/sql/svn/1696_modify_zone_and_object_tables.sql b/utils/sql/svn/1696_modify_zone_and_object_tables.sql new file mode 100644 index 000000000..791c1845e --- /dev/null +++ b/utils/sql/svn/1696_modify_zone_and_object_tables.sql @@ -0,0 +1,19 @@ +ALTER TABLE object MODIFY objectname varchar(32); +ALTER TABLE zone MODIFY short_name varchar(32); +ALTER TABLE spawn2 MODIFY zone varchar(32); +ALTER TABLE doors MODIFY zone varchar(32); +ALTER TABLE zone_points MODIFY zone varchar(32); +ALTER TABLE zone_state_dump MODIFY zonename varchar(32); +ALTER TABLE launcher_zones MODIFY zone varchar(32); +ALTER TABLE fear_hints MODIFY zone varchar(32); +ALTER TABLE spawn_conditions MODIFY zone varchar(32); +ALTER TABLE spawn_events MODIFY zone varchar(32); +UPDATE zone SET short_name = "oceangreenvillage" WHERE short_name = "oceangreenvillag"; +UPDATE spawn2 SET zone = "oceangreenvillage" WHERE zone = "oceangreenvillag"; +UPDATE doors SET zone = "oceangreenvillage" WHERE zone = "oceangreenvillag"; +UPDATE zone_points SET zone = "oceangreenvillage" WHERE zone = "oceangreenvillag"; +UPDATE zone_state_dump SET zonename = "oceangreenvillage" WHERE zonename = "oceangreenvillag"; +UPDATE launcher_zones SET zone = "oceangreenvillage" WHERE zone = "oceangreenvillag"; +UPDATE fear_hints SET zone = "oceangreenvillage" WHERE zone = "oceangreenvillag"; +UPDATE spawn_conditions SET zone = "oceangreenvillage" WHERE zone = "oceangreenvillag"; +UPDATE spawn_events SET zone = "oceangreenvillage" WHERE zone = "oceangreenvillag"; \ No newline at end of file diff --git a/utils/sql/svn/1711_account_restricted_aa.sql b/utils/sql/svn/1711_account_restricted_aa.sql new file mode 100644 index 000000000..e97528103 --- /dev/null +++ b/utils/sql/svn/1711_account_restricted_aa.sql @@ -0,0 +1,3 @@ +ALTER TABLE `altadv_vars` ADD `account_time_required` INT UNSIGNED DEFAULT '0' NOT NULL AFTER `clientver`; +ALTER TABLE `account` ADD `time_creation` INT UNSIGNED DEFAULT '0' NOT NULL AFTER `suspendeduntil`; +UPDATE `account` SET `time_creation` = UNIX_TIMESTAMP() WHERE `time_creation` = 0; \ No newline at end of file diff --git a/utils/sql/svn/1717_optional_rule_bash_stun_chance.sql b/utils/sql/svn/1717_optional_rule_bash_stun_chance.sql new file mode 100644 index 000000000..7086f65f6 --- /dev/null +++ b/utils/sql/svn/1717_optional_rule_bash_stun_chance.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:NPCBashKickStunChance', '15', 'Percent chance that a bash/kick will stun'); + diff --git a/utils/sql/svn/1718_optional_rules_mod3s.sql b/utils/sql/svn/1718_optional_rules_mod3s.sql new file mode 100644 index 000000000..eb9d7ab06 --- /dev/null +++ b/utils/sql/svn/1718_optional_rules_mod3s.sql @@ -0,0 +1,9 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:ItemEnduranceRegenCap', '15', 'Endurance cap from items'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:ItemClairvoyanceCap', '250', 'Clairvoyance returns mana after a cast under certain circumstances'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:ItemHealAmtCap', '250', 'Heal Amt adds to heal spells based on their cast & recast time'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:ItemSpellDmgCap', '250', 'Spell Dmg adds to DD spells based on their cast & recast time'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:ItemDSMitigationCap', '50', 'Mitigates the effect of a damage shield'); + + + + diff --git a/utils/sql/svn/1719_optional_triggerOnCastAAs.sql b/utils/sql/svn/1719_optional_triggerOnCastAAs.sql new file mode 100644 index 000000000..3f24d8ba3 --- /dev/null +++ b/utils/sql/svn/1719_optional_triggerOnCastAAs.sql @@ -0,0 +1,49 @@ +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`) VALUES (1435, 'Gift of Mana', 3, 3, 4294967295, 4294967295, 1435, 1435, 7, 0, 83, 3, 0, 0, 31812, 0, 66, 3, 9, 4294967295, 3, 3, 1, 1435, 1, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1435, 1, 339, 8105, 2); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1435, 2, 134, 70, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1435, 3, 142, 65, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1435, 4, 137, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1436, 1, 339, 8105, 4); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1436, 2, 134, 70, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1436, 3, 142, 65, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1436, 4, 137, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1437, 1, 339, 8105, 6); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1437, 2, 134, 70, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1437, 3, 142, 65, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1437, 4, 137, 0, 0); +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`) VALUES (1486, 'Abundant Healing', 5, 5, 4294967295, 4294967295, 1486, 1486, 7, 0, 1086, 1, 0, 0, 1092, 0, 66, 0, 10, 4294967295, 2, 0, 1, 1486, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1486, 1, 339, 8165, 2); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1486, 2, 134, 75, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1486, 3, 142, 60, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1486, 4, 137, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1486, 5, 141, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1486, 6, 138, 1, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1487, 1, 339, 8166, 4); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1487, 2, 134, 75, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1487, 3, 142, 60, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1487, 4, 137, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1487, 5, 141, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1487, 6, 138, 1, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1488, 1, 339, 8167, 6); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1488, 2, 134, 75, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1488, 3, 142, 60, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1488, 4, 137, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1488, 5, 141, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1488, 6, 138, 1, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1489, 1, 339, 8168, 8); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1489, 2, 134, 75, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1489, 3, 142, 60, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1489, 4, 137, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1489, 5, 141, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1489, 6, 138, 1, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1490, 1, 339, 8169, 10); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1490, 2, 134, 75, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1490, 3, 142, 60, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1490, 4, 137, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1490, 5, 141, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1490, 6, 138, 1, 0); +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`) VALUES (1478, 'Pyromancy', 3, 3, 1478, 4294967295, 1478, 1478, 7, 8406, 0, 0, 99, 1, 4096, 0, 66, 0, 10, 4294967295, 3, 0, 1, 1478, 2, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (1478, 0, 1, 8406, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (1478, 1, 1, 8407, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (1478, 2, 1, 8408, 1, 0, 0, 0, 0, 0, 0, 0); + diff --git a/utils/sql/svn/1720_optional_sql_AAs.sql b/utils/sql/svn/1720_optional_sql_AAs.sql new file mode 100644 index 000000000..0c4227da1 --- /dev/null +++ b/utils/sql/svn/1720_optional_sql_AAs.sql @@ -0,0 +1,218 @@ +-- Update to Veteran AAs so they have the appropriate category tag +UPDATE `altadv_vars` SET `special_category`=5 WHERE `skill_id`=1371 LIMIT 1; +UPDATE `altadv_vars` SET `special_category`=5 WHERE `skill_id`=1372 LIMIT 1; +UPDATE `altadv_vars` SET `special_category`=5 WHERE `skill_id`=1373 LIMIT 1; +UPDATE `altadv_vars` SET `special_category`=5 WHERE `skill_id`=1374 LIMIT 1; +UPDATE `altadv_vars` SET `special_category`=5 WHERE `skill_id`=1375 LIMIT 1; +UPDATE `altadv_vars` SET `special_category`=5 WHERE `skill_id`=1376 LIMIT 1; +UPDATE `altadv_vars` SET `special_category`=5 WHERE `skill_id`=1377 LIMIT 1; +-- Demiplane Progression AAs +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`) VALUES (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); +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`) VALUES (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); +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`) VALUES (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); +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`) VALUES (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); +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`) VALUES (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); +-- Trials of Mata Muram AA +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`) VALUES (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); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1011, 1, 262, 8, 7); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1011, 2, 262, 8, 8); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1011, 3, 262, 8, 9); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1011, 4, 262, 8, 10); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1011, 5, 262, 8, 11); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1012, 1, 262, 16, 7); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1012, 2, 262, 16, 8); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1012, 3, 262, 16, 9); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1012, 4, 262, 16, 10); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1012, 5, 262, 16, 11); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1013, 1, 262, 24, 7); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1013, 2, 262, 24, 8); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1013, 3, 262, 24, 9); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1013, 4, 262, 24, 10); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1013, 5, 262, 24, 11); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1014, 1, 262, 32, 7); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1014, 2, 262, 32, 8); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1014, 3, 262, 32, 9); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1014, 4, 262, 32, 10); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1014, 5, 262, 32, 11); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1015, 1, 262, 40, 7); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1015, 2, 262, 40, 8); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1015, 3, 262, 40, 9); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1015, 4, 262, 40, 10); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1015, 5, 262, 40, 11); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1016, 1, 262, 50, 7); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1016, 2, 262, 50, 8); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1016, 3, 262, 50, 9); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1016, 4, 262, 50, 10); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1016, 5, 262, 50, 11); +-- Good DoN Progression AAs +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`) VALUES (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); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1361, 1, 262, 10, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1361, 2, 262, 10, 1); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1361, 3, 262, 10, 2); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1361, 4, 262, 10, 3); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1361, 5, 262, 10, 4); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1361, 6, 262, 10, 5); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1361, 7, 262, 10, 6); +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`) VALUES (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); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1362, 1, 69, 250, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1362, 2, 97, 250, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1362, 3, 190, 250, 0); +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`) VALUES (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); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1363, 1, 327, 1, 0); +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`) VALUES (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); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1364, 1, 273, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1364, 2, 274, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1364, 3, 294, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1364, 4, 319, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1364, 5, 169, 3, 0); +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`) VALUES (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); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1365, 1, 180, 3, 0); +-- Evil DoN Progression AAs +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`) VALUES (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); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1366, 1, 262, 10, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1366, 2, 262, 10, 1); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1366, 3, 262, 10, 2); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1366, 4, 262, 10, 3); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1366, 5, 262, 10, 4); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1366, 6, 262, 10, 5); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1366, 7, 262, 10, 6); +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`) VALUES (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); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1367, 1, 69, 250, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1367, 2, 97, 250, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1367, 3, 190, 250, 0); +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`) VALUES (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); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1368, 1, 327, 1, 0); +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`) VALUES (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); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1369, 1, 273, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1369, 2, 274, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1369, 3, 294, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1369, 4, 319, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1369, 5, 169, 3, 0); +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`) VALUES (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); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1370, 1, 180, 3, 0); +-- Drakkin Breath Weapon 1 +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`) VALUES (5150, 'Breath of Atathus', 0, 15, 5150, 5150, 5150, 5150, 10, 11112, 0, 0, 80, 300, 65534, 1, 0, 0, 523, 8, 4, 0, 1, 5150, 4, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 0, 300, 11112, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 1, 300, 11113, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 2, 300, 11114, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 3, 300, 11115, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 4, 300, 11116, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 5, 300, 11117, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 6, 300, 11118, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 7, 300, 11119, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 8, 300, 11120, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 9, 300, 11121, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 10, 300, 11122, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 11, 300, 11123, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 12, 300, 11124, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 13, 300, 11125, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5150, 14, 300, 11126, 1, 0, 0, 0, 0, 0, 0, 0); +-- Drakkin Breath Weapon 2 +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`) VALUES (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, 1, 5165, 4, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 0, 300, 11127, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 1, 300, 11128, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 2, 300, 11129, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 3, 300, 11130, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 4, 300, 11131, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 5, 300, 11132, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 6, 300, 11133, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 7, 300, 11134, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 8, 300, 11135, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 9, 300, 11136, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 10, 300, 11137, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 11, 300, 11138, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 12, 300, 11139, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 13, 300, 11140, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5165, 14, 300, 11141, 1, 0, 0, 0, 0, 0, 0, 0); +-- Drakkin Breath Weapon 3 +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`) VALUES (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, 1, 5180, 4, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 0, 300, 11142, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 1, 300, 11143, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 2, 300, 11144, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 3, 300, 11145, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 4, 300, 11146, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 5, 300, 11147, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 6, 300, 11148, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 7, 300, 11149, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 8, 300, 11150, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 9, 300, 11151, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 10, 300, 11152, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 11, 300, 11153, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 12, 300, 11154, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 13, 300, 11155, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5180, 14, 300, 11156, 1, 0, 0, 0, 0, 0, 0, 0); +-- Drakkin Breath Weapon 4 +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`) VALUES (5195, 'Breath of Venesh', 0, 15, 5195, 5195, 5195, 5195, 10, 11157, 0, 0, 80, 300, 65534, 1, 0, 0, 526, 8, 4, 0, 1, 5195, 4, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 0, 300, 11157, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 1, 300, 11158, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 2, 300, 11159, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 3, 300, 11160, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 4, 300, 11161, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 5, 300, 11162, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 6, 300, 11163, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 7, 300, 11164, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 8, 300, 11165, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 9, 300, 11166, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 10, 300, 11167, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 11, 300, 11168, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 12, 300, 11169, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 13, 300, 11170, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5195, 14, 300, 11171, 1, 0, 0, 0, 0, 0, 0, 0); +-- Drakkin Breath Weapon 5 +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`) VALUES (5210, 'Breath of Mysaphar', 0, 15, 5210, 5210, 5210, 5210, 10, 11172, 0, 0, 80, 300, 65534, 1, 0, 0, 527, 8, 4, 0, 1, 5210, 4, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 0, 300, 11172, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 1, 300, 11173, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 2, 300, 11174, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 3, 300, 11175, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 4, 300, 11176, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 5, 300, 11177, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 6, 300, 11178, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 7, 300, 11179, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 8, 300, 11180, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 9, 300, 11181, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 10, 300, 11182, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 11, 300, 11183, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 12, 300, 11184, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 13, 300, 11185, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5210, 14, 300, 11186, 1, 0, 0, 0, 0, 0, 0, 0); +-- Drakkin Breath Weapon 6 +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`) VALUES (5225, 'Breath of Keikolin', 0, 15, 5225, 5225, 5225, 5225, 10, 11187, 0, 0, 80, 300, 65534, 1, 0, 0, 528, 8, 4, 0, 1, 5225, 4, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 0, 300, 11187, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 1, 300, 11188, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 2, 300, 11189, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 3, 300, 11190, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 4, 300, 11191, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 5, 300, 11192, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 6, 300, 11193, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 7, 300, 11194, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 8, 300, 11195, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 9, 300, 11196, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 10, 300, 11197, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 11, 300, 11198, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 12, 300, 11199, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 13, 300, 11200, 1, 0, 0, 0, 0, 0, 0, 0); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (5225, 14, 300, 11201, 1, 0, 0, 0, 0, 0, 0, 0); +-- PoR Progression AA +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`) VALUES (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); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (1647, 0, 30, 8771, 1, 0, 0, 0, 0, 0, 0, 0); +-- Glyphs(Expendable) +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`) VALUES (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); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (4702, 0, 600, 9475, 1, 0, 0, 0, 0, 0, 0, 0); +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`) VALUES (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); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (4703, 0, 600, 12752, 1, 0, 0, 0, 0, 0, 0, 0); +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`) VALUES (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); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (4704, 0, 600, 9477, 1, 0, 0, 0, 0, 0, 0, 0); +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`) VALUES (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); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (4705, 0, 600, 9478, 1, 0, 0, 0, 0, 0, 0, 0); +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`) VALUES (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); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (4706, 0, 600, 9479, 1, 0, 0, 0, 0, 0, 0, 0); +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`) VALUES (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); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (4707, 0, 600, 12748, 1, 0, 0, 0, 0, 0, 0, 0); +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`) VALUES (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); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (4708, 0, 600, 12751, 5, 0, 0, 0, 0, 0, 0, 0); +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`) VALUES (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); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (4709, 0, 600, 12750, 1, 0, 0, 0, 0, 0, 0, 0); +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`) VALUES (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); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (4710, 0, 600, 12749, 1, 0, 0, 0, 0, 0, 0, 0); + + diff --git a/utils/sql/svn/1720_required_sql_AA_effects_update.sql b/utils/sql/svn/1720_required_sql_AA_effects_update.sql new file mode 100644 index 000000000..bf5426c36 --- /dev/null +++ b/utils/sql/svn/1720_required_sql_AA_effects_update.sql @@ -0,0 +1,82 @@ +ALTER TABLE `aa_effects` CHANGE COLUMN `base1` `base1` MEDIUMINT(8) NOT NULL DEFAULT '0' AFTER `effectid`, CHANGE COLUMN `base2` `base2` MEDIUMINT(8) NOT NULL DEFAULT '0' AFTER `base1`; +-- Ingenuity +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (625, 1, 294, 1, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (626, 1, 294, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (627, 1, 294, 3, 0); +-- Spell Casting Fury +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (92, 1, 294, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (93, 1, 294, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (94, 1, 294, 7, 0); +-- Spell Casting Fury Mastery +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (267, 1, 294, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (268, 1, 294, 5, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (269, 1, 294, 7, 0); +-- Fury of Magic +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (637, 1, 294, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (638, 1, 294, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (639, 1, 294, 6, 0); +-- Fury of Magic Mastery +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (640, 1, 294, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (641, 1, 294, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (642, 1, 294, 6, 0); +-- Fury of Magic Mastery +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (770, 1, 294, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (771, 1, 294, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (772, 1, 294, 6, 0); +-- Fury of Magic +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1107, 1, 294, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1108, 1, 294, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1109, 1, 294, 6, 0); +-- Advanced Fury of Magic Mastery +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (924, 1, 294, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (925, 1, 294, 4, 0); +-- Destructive Fury +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1210, 1, 155, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1211, 1, 155, 8, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1212, 1, 155, 16, 0); +-- Healing Adept +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (77, 1, 125, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (77, 2, 141, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (78, 1, 125, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (78, 2, 141, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (79, 1, 125, 6, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (79, 2, 141, 0, 0); +-- Advanced Healing Adept +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (434, 1, 125, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (435, 1, 125, 6, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (436, 1, 125, 9, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (434, 2, 141, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (435, 2, 141, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (436, 2, 141, 0, 0); +-- Healing Gift +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (80, 1, 274, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (80, 2, 141, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (81, 2, 141, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (81, 1, 274, 6, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (82, 1, 274, 10, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (82, 2, 141, 0, 0); +-- Advanced Healing Gift +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (437, 1, 274, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (437, 2, 141, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (438, 1, 274, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (438, 2, 141, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (439, 1, 274, 6, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (439, 2, 141, 0, 0); +-- Combat Fury +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (113, 1, 169, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (114, 1, 169, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (115, 1, 169, 7, 0); +-- Fury of the Ages +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (443, 1, 169, 1, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (444, 1, 169, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (445, 1, 169, 5, 0); +-- Critical Affliction +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (767, 1, 273, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (768, 1, 273, 6, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (769, 1, 273, 10, 0); +-- Improved Critical Affliction +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1099, 1, 273, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1100, 1, 273, 6, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1101, 1, 273, 10, 0); +-- Fading Memories +UPDATE `aa_actions` SET `nonspell_action`=16, `nonspell_mana`=900 WHERE `aaid`=630 AND `rank`=0 LIMIT 1; \ No newline at end of file diff --git a/utils/sql/svn/1721_optional_sql_drakkin_breath_update.sql b/utils/sql/svn/1721_optional_sql_drakkin_breath_update.sql new file mode 100644 index 000000000..40fafaf2c --- /dev/null +++ b/utils/sql/svn/1721_optional_sql_drakkin_breath_update.sql @@ -0,0 +1,6 @@ +UPDATE `altadv_vars` SET `aa_expansion`=523 WHERE `skill_id`=5150 LIMIT 1; +UPDATE `altadv_vars` SET `aa_expansion`=524 WHERE `skill_id`=5165 LIMIT 1; +UPDATE `altadv_vars` SET `aa_expansion`=525 WHERE `skill_id`=5180 LIMIT 1; +UPDATE `altadv_vars` SET `aa_expansion`=526 WHERE `skill_id`=5195 LIMIT 1; +UPDATE `altadv_vars` SET `aa_expansion`=527 WHERE `skill_id`=5210 LIMIT 1; +UPDATE `altadv_vars` SET `aa_expansion`=528 WHERE `skill_id`=5225 LIMIT 1; \ No newline at end of file diff --git a/utils/sql/svn/1721_required_sql_altadv_vars_update.sql b/utils/sql/svn/1721_required_sql_altadv_vars_update.sql new file mode 100644 index 000000000..55119afd1 --- /dev/null +++ b/utils/sql/svn/1721_required_sql_altadv_vars_update.sql @@ -0,0 +1 @@ +ALTER TABLE `altadv_vars` CHANGE COLUMN `aa_expansion` `aa_expansion` SMALLINT(3) UNSIGNED NOT NULL DEFAULT '3' AFTER `cost_inc`; \ No newline at end of file diff --git a/utils/sql/svn/1723_optional_sql_new_stats_window_rule.sql b/utils/sql/svn/1723_optional_sql_new_stats_window_rule.sql new file mode 100644 index 000000000..03a745225 --- /dev/null +++ b/utils/sql/svn/1723_optional_sql_new_stats_window_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:UseNewStatsWindow', 'true', 'New stats window shows so much it got the TSA seal of approval'); \ No newline at end of file diff --git a/utils/sql/svn/1723_required_sql_corruption.sql b/utils/sql/svn/1723_required_sql_corruption.sql new file mode 100644 index 000000000..c30bfadb3 --- /dev/null +++ b/utils/sql/svn/1723_required_sql_corruption.sql @@ -0,0 +1,3 @@ +ALTER TABLE `botbuffs` ADD COLUMN `CorruptionCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `CurseCounters`; +ALTER TABLE `bots` ADD COLUMN `Corrup` SMALLINT(5) NOT NULL DEFAULT '0' AFTER `PR`; +ALTER TABLE `npc_types` ADD COLUMN `Corrup` SMALLINT(5) NOT NULL DEFAULT '0' AFTER `PR`; \ No newline at end of file diff --git a/utils/sql/svn/1736_optional_sql_feral_swipe.sql b/utils/sql/svn/1736_optional_sql_feral_swipe.sql new file mode 100644 index 000000000..9c7d91060 --- /dev/null +++ b/utils/sql/svn/1736_optional_sql_feral_swipe.sql @@ -0,0 +1,2 @@ +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES (723, 0, 60, 4788, 2, 0, 0, 0, 0, 0, 0, 0); + diff --git a/utils/sql/svn/1737_required_sql_rule_and_aa_update.sql b/utils/sql/svn/1737_required_sql_rule_and_aa_update.sql new file mode 100644 index 000000000..a36decc49 --- /dev/null +++ b/utils/sql/svn/1737_required_sql_rule_and_aa_update.sql @@ -0,0 +1,12 @@ +UPDATE `rule_values` SET `rule_value`='6' WHERE `ruleset_id`=1 AND `rule_name`='Combat:BerserkBaseCritChance'; +UPDATE `rule_values` SET `rule_value`='0', `rule_name`='Combat:MeleeBaseCritChance' WHERE `rule_name`='Combat:BaseCritChance'; +UPDATE `rule_values` SET `rule_value`='3' WHERE `rule_name`='Combat:WarBerBaseCritChance'; +UPDATE `rule_values` SET `rule_value`='0' WHERE `rule_name`='Combat:ClientBaseCritChance'; + +UPDATE `aa_effects` SET `base2`=-1 WHERE `aaid`=113 LIMIT 1; +UPDATE `aa_effects` SET `base2`=-1 WHERE `aaid`=114 LIMIT 1; +UPDATE `aa_effects` SET `base2`=-1 WHERE `aaid`=115 LIMIT 1; + +UPDATE `aa_effects` SET `base2`=-1 WHERE `aaid`=443 LIMIT 1; +UPDATE `aa_effects` SET `base2`=-1 WHERE `aaid`=444 LIMIT 1; +UPDATE `aa_effects` SET `base2`=-1 WHERE `aaid`=445 LIMIT 1; \ No newline at end of file diff --git a/utils/sql/svn/1746_optional_sql_bot_manaregen.sql b/utils/sql/svn/1746_optional_sql_bot_manaregen.sql new file mode 100644 index 000000000..ca21b8ed5 --- /dev/null +++ b/utils/sql/svn/1746_optional_sql_bot_manaregen.sql @@ -0,0 +1 @@ +UPDATE rule_values SET rule_value = 1.0 WHERE rule_name = 'Bots:BotManaRegen'; \ No newline at end of file diff --git a/utils/sql/svn/1747_optional_HoT_zone_and_zonepoints.sql b/utils/sql/svn/1747_optional_HoT_zone_and_zonepoints.sql new file mode 100644 index 000000000..4c9bfbdd3 --- /dev/null +++ b/utils/sql/svn/1747_optional_HoT_zone_and_zonepoints.sql @@ -0,0 +1,111 @@ + +INSERT INTO `zone` (`short_name`, `id`, `file_name`, `long_name`, `map_file_name`, `safe_x`, `safe_y`, `safe_z`, `graveyard_id`, `min_level`, `min_status`, `zoneidnumber`, `version`, `timezone`, `maxclients`, `weather`, `ruleset`, `note`, `underworld`, `minclip`, `maxclip`, `fog_minclip`, `fog_maxclip`, `fog_blue`, `fog_red`, `fog_green`, `sky`, `ztype`, `zone_exp_multiplier`, `walkspeed`, `time_type`, `fog_red1`, `fog_green1`, `fog_blue1`, `fog_minclip1`, `fog_maxclip1`, `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`, `flag_needed`, `canbind`, `cancombat`, `canlevitate`, `castoutdoor`, `hotzone`, `insttype`, `shutdowndelay`, `peqzone`, `expansion`, `suspendbuffs`) VALUES +('thulelibrary', 5556, '', 'The Library', NULL, 0, 0, 0, 0, 0, 0, 704, 0, 0, 0, 1, 1, '', -90, 50, 1000, 0, 1000, 0, 0, 0, 1, 0, 1.00, 0.4, 4, 0, 0, 0, 450, 2000, 0, 0, 0, 0, 600, 0, 0, 0, 0, 600, 0, 0, 0, 0, 600, 0.33, '', 2, 1, 1, 1, 0, 0, 5000, 1, 8, 0), +('morellcastle', 5557, '', 'Morell''s Castle', NULL, -30, -219, -36, 0, 0, 0, 707, 0, 0, 0, 1, 0, NULL, -48, 50, 1000, 0, 1000, 0, 0, 0, 1, 0, 0.00, 0.4, 4, 0, 0, 0, 450, 450, 0, 0, 0, 0, 600, 0, 0, 0, 0, 600, 0, 0, 0, 0, 600, 0.33, '', 1, 1, 1, 1, 0, 0, 5000, 1, 0, 0), +('alkabormare', 5558, '', 'Al''Kabor''s Nightmare', NULL, 1104, -86, -14, 0, 0, 0, 709, 0, 0, 0, 1, 0, NULL, -1400, 50, 2400, 600, 2400, 220, 200, 200, 1, 255, 0.00, 0.4, 1, 0, 0, 0, 450, 450, 200, 200, 220, 600, 2400, 200, 200, 220, 600, 2400, 200, 200, 220, 600, 2400, 0.33, '', 1, 1, 1, 1, 0, 0, 5000, 1, 0, 0), +('fallen', 5559, '', 'Erudin Burning', NULL, 59, -15, 0, 0, 0, 0, 706, 0, 0, 0, 1, 0, NULL, -84, 50, 1050, 10, 1050, 220, 200, 200, 1, 255, 0.00, 0.4, 2, 0, 0, 0, 450, 450, 200, 200, 220, 10, 550, 200, 200, 220, 10, 550, 200, 200, 220, 10, 550, 0.33, '', 1, 1, 1, 1, 0, 0, 5000, 1, 0, 0), +('feerrott2', 5560, '', 'The Feerrott', NULL, 952.95, 1022.59, 40.83, 0, 0, 0, 700, 0, 0, 0, 1, 0, NULL, -192, 50, 1350, 10, 1350, 30, 60, 90, 1, 255, 0.00, 0.4, 1, 0, 0, 0, 450, 450, 60, 90, 30, 10, 350, 60, 90, 30, 10, 350, 60, 90, 30, 10, 350, 0.33, '', 1, 1, 1, 1, 0, 0, 5000, 1, 0, 0), +('housegarden', 5561, '', 'The Grounds', NULL, 102.16, -0.87, -28.89, 0, 0, 0, 703, 0, 0, 0, 1, 0, NULL, -300, 50, 2400, 500, 2400, 220, 200, 200, 1, 255, 0.00, 0.4, 1, 0, 0, 0, 450, 450, 200, 200, 220, 1000, 2400, 200, 200, 220, 1000, 2400, 200, 200, 220, 1000, 2400, 0.33, '', 1, 1, 1, 1, 0, 0, 5000, 1, 0, 0), +('miragulmare', 5562, '', 'Miragul''s Nightmare', NULL, -102, 36, -108, 0, 0, 0, 710, 0, 0, 0, 1, 0, NULL, -295, 50, 2400, 600, 2400, 220, 200, 200, 1, 255, 0.00, 0.4, 1, 0, 0, 0, 450, 450, 200, 200, 220, 600, 2400, 200, 200, 220, 600, 2400, 200, 200, 220, 600, 2400, 0.33, '', 1, 1, 1, 1, 0, 0, 5000, 1, 0, 0), +('somnium', 5563, '', 'Sanctum Somnium', NULL, -2, 195, 0, 0, 0, 0, 708, 0, 0, 0, 1, 0, NULL, -200, 50, 1000, 600, 2000, 0, 0, 0, 1, 0, 0.00, 0.4, 4, 0, 0, 0, 450, 450, 0, 0, 0, 600, 2000, 0, 0, 0, 600, 2000, 0, 0, 0, 600, 2000, 0.33, '', 1, 1, 1, 1, 0, 0, 5000, 1, 0, 0), +('thuledream', 5564, '', 'Fear Itself', NULL, 1282, -1139, 5, 0, 0, 0, 711, 0, 0, 0, 1, 0, NULL, -80, 50, 1400, 10, 1400, 10, 255, 50, 1, 255, 0.00, 0.4, 1, 0, 0, 0, 450, 450, 255, 50, 10, 10, 1400, 255, 50, 10, 10, 1400, 90, 20, 20, 10, 1400, 0.33, '', 1, 1, 1, 1, 0, 0, 5000, 1, 0, 0), +('thulehouse1', 5565, '', 'House of Thule', NULL, 0, -332, 4, 0, 0, 0, 701, 0, 0, 0, 1, 0, NULL, -300, 50, 2400, 350, 2400, 200, 230, 200, 1, 255, 0.00, 0.4, 2, 0, 0, 0, 450, 450, 230, 200, 200, 350, 2400, 230, 200, 200, 350, 2400, 230, 200, 200, 350, 2400, 0.33, '', 1, 1, 1, 1, 0, 0, 5000, 1, 0, 0), +('thulehouse2', 5566, '', 'House of Thule, Upper Floors', NULL, -91, 338, 64, 0, 0, 0, 702, 0, 0, 0, 1, 0, NULL, -98, 50, 2400, 350, 2400, 200, 230, 200, 1, 255, 0.00, 0.4, 2, 0, 0, 0, 450, 450, 230, 200, 200, 350, 2400, 230, 200, 200, 350, 2400, 230, 200, 200, 350, 2400, 0.33, '', 1, 1, 1, 1, 0, 0, 5000, 1, 0, 0), +('well', 5567, '', 'The Well', NULL, 0, 0, 52, 0, 0, 0, 705, 0, 0, 0, 1, 0, NULL, -113, 50, 1000, 100, 1000, 90, 50, 100, 1, 255, 0.00, 0.4, 0, 0, 0, 0, 450, 450, 50, 100, 90, 100, 700, 50, 100, 90, 100, 700, 50, 100, 90, 100, 700, 0.33, '', 1, 1, 1, 1, 0, 0, 5000, 1, 0, 0), +('neighborhood', 5568, '', 'Sunrise Hills', NULL, 2035, -2940, 6, 0, 0, 0, 712, 0, 0, 0, 1, 0, NULL, -250, 50, 6000, 1500, 6000, 200, 200, 200, 1, 255, 0.00, 0.4, 1, 0, 0, 0, 450, 450, 200, 200, 200, 1500, 6000, 200, 200, 200, 1500, 6000, 200, 200, 200, 1500, 6000, 0.33, '', 1, 1, 1, 1, 0, 0, 5000, 1, 0, 0); + +INSERT INTO `zone_points` (`id`, `zone`, `version`, `number`, `y`, `x`, `z`, `heading`, `target_y`, `target_x`, `target_z`, `target_heading`, `zoneinst`, `target_zone_id`, `target_instance`, `buffer`, `client_version_mask`) VALUES +(1912, 'morellcastle', 0, 0, 0, 0, 0, 0, 195.72, 0.11, -1.22, 257, 0, 708, 0, 0, 4294967295), +(1913, 'morellcastle', 0, 10, 0, 0, 0, 0, 524.77, -393.05, 12.05, 12, 0, 707, 0, 0, 4294967295), +(1914, 'morellcastle', 0, 11, 0, 0, 0, 0, 407.72, -6699.31, -5.15, 104, 0, 707, 0, 0, 4294967295), +(1915, 'morellcastle', 0, 30, 0, 0, 0, 0, 4660.26, -4260.59, -8.19, 127, 0, 707, 0, 0, 4294967295), +(1916, 'morellcastle', 0, 40, 0, 0, 0, 0, 870, -1095, 130, 127, 0, 702, 0, 0, 4294967295), +(1917, 'morellcastle', 0, 0, 0, 0, 0, 0, 195.72, 0.11, -1.22, 257, 0, 708, 0, 0, 4294967295), +(1918, 'morellcastle', 0, 10, 0, 0, 0, 0, 524.77, -393.05, 12.05, 12, 0, 707, 0, 0, 4294967295), +(1919, 'morellcastle', 0, 11, 0, 0, 0, 0, 407.72, -6699.31, -5.15, 104, 0, 707, 0, 0, 4294967295), +(1920, 'morellcastle', 0, 30, 0, 0, 0, 0, 4660.26, -4260.59, -8.19, 127, 0, 707, 0, 0, 4294967295), +(1921, 'morellcastle', 0, 40, 0, 0, 0, 0, 870, -1095, 130, 127, 0, 702, 0, 0, 4294967295), +(1922, 'alkabormare', 0, 11, 0, 0, 0, 0, 549, 802, 61, 383, 0, 702, 0, 0, 4294967295), +(1923, 'fallen', 0, 23, 0, 0, 0, 0, -1617, 19, -62, 127, 0, 706, 0, 0, 4294967295), +(1924, 'fallen', 0, 32, 0, 0, 0, 0, -1608, 131, 17, 510, 0, 706, 0, 0, 4294967295), +(1925, 'fallen', 0, 33, 0, 0, 0, 0, 474, -262, 16, 255, 0, 701, 0, 0, 4294967295), +(1926, 'housegarden', 0, 1, 0, 0, 0, 0, 777, -126, 16, 255, 0, 701, 0, 0, 4294967295), +(1927, 'housegarden', 0, 10, 0, 0, 0, 0, -5, 14, 170, 999999, 0, 705, 0, 0, 4294967295), +(1928, 'morellcastle', 0, 0, 0, 0, 0, 0, 195.72, 0.11, -1.22, 257, 0, 708, 0, 0, 4294967295), +(1929, 'morellcastle', 0, 10, 0, 0, 0, 0, 524.77, -393.05, 12.05, 12, 0, 707, 0, 0, 4294967295), +(1930, 'morellcastle', 0, 11, 0, 0, 0, 0, 407.72, -6699.31, -5.15, 104, 0, 707, 0, 0, 4294967295), +(1931, 'morellcastle', 0, 30, 0, 0, 0, 0, 4660.26, -4260.59, -8.19, 127, 0, 707, 0, 0, 4294967295), +(1932, 'morellcastle', 0, 40, 0, 0, 0, 0, 870, -1095, 130, 127, 0, 702, 0, 0, 4294967295), +(1933, 'thuledream', 0, 10, 0, 0, 0, 0, -335, -345, 64, 0, 0, 702, 0, 0, 4294967295), +(1934, 'thulehouse2', 0, 10, 0, 0, 0, 0, 302, 70, 64, 54, 0, 702, 0, 0, 4294967295), +(1935, 'thulehouse2', 0, 20, 0, 0, 0, 0, 302, 70, 64, 54, 0, 702, 0, 0, 4294967295), +(1936, 'thulehouse2', 0, 30, 0, 0, 0, 0, 302, 70, 64, 54, 0, 702, 0, 0, 4294967295), +(1937, 'thulehouse2', 0, 51, 0, 0, 0, 0, 13, -39, -107, 128, 0, 710, 0, 0, 4294967295), +(1938, 'thulehouse2', 0, 61, 0, 0, 0, 0, -50, 1146, -14, 440, 0, 709, 0, 0, 4294967295), +(1939, 'thulehouse2', 0, 72, 0, 0, 0, 0, -1329, 1036, 2, 82, 0, 711, 0, 0, 4294967295), +(1940, 'thulehouse2', 0, 82, 0, 0, 0, 0, 0, 0, -23, 498, 0, 708, 0, 0, 4294967295), +(1941, 'well', 0, 9, 0, 0, 0, 0, -943, -1642, -174, 495, 0, 703, 0, 0, 4294967295), +(1942, 'neighborhood', 0, 1, 0, 0, 0, 0, 936, 294, -19, 254, 0, 344, 0, 0, 4294967295), +(1943, 'neighborhood', 0, 10, 0, 0, 0, 0, 936, 294, -19, 254, 0, 344, 0, 0, 4294967295), +(1944, 'neighborhood', 0, 12, 0, 0, 0, 0, -542, 2089, -35, 374, 7969, 712, 0, 0, 4294967295), +(1945, 'neighborhood', 0, 13, 0, 0, 0, 0, 1252, 1497, 65, 119, 7969, 712, 0, 0, 4294967295), +(1946, 'neighborhood', 0, 14, 0, 0, 0, 0, 2586, 1642, 47, 67, 7969, 712, 0, 0, 4294967295), +(1947, 'neighborhood', 0, 15, 0, 0, 0, 0, 2778, 81, -60, 356, 7969, 712, 0, 0, 4294967295), +(1948, 'neighborhood', 0, 16, 0, 0, 0, 0, 719, -179, 50, 392, 7969, 712, 0, 0, 4294967295), +(1949, 'neighborhood', 0, 17, 0, 0, 0, 0, -787, 352, 5, 69, 7969, 712, 0, 0, 4294967295), +(1950, 'neighborhood', 0, 21, 0, 0, 0, 0, -2694, 2035, 6, 27, 7969, 712, 0, 0, 4294967295), +(1951, 'neighborhood', 0, 23, 0, 0, 0, 0, 1252, 1497, 65, 119, 7969, 712, 0, 0, 4294967295), +(1952, 'neighborhood', 0, 24, 0, 0, 0, 0, 2586, 1642, 47, 67, 7969, 712, 0, 0, 4294967295), +(1953, 'neighborhood', 0, 25, 0, 0, 0, 0, 2778, 81, -60, 356, 7969, 712, 0, 0, 4294967295), +(1954, 'neighborhood', 0, 26, 0, 0, 0, 0, 719, -179, 50, 392, 7969, 712, 0, 0, 4294967295), +(1955, 'neighborhood', 0, 27, 0, 0, 0, 0, -787, 352, 5, 69, 7969, 712, 0, 0, 4294967295), +(1956, 'neighborhood', 0, 31, 0, 0, 0, 0, -2694, 2035, 6, 27, 7969, 712, 0, 0, 4294967295), +(1957, 'neighborhood', 0, 32, 0, 0, 0, 0, -542, 2089, -35, 374, 7969, 712, 0, 0, 4294967295), +(1958, 'neighborhood', 0, 34, 0, 0, 0, 0, 2586, 1642, 47, 67, 7969, 712, 0, 0, 4294967295), +(1959, 'neighborhood', 0, 35, 0, 0, 0, 0, 2778, 81, -60, 356, 7969, 712, 0, 0, 4294967295), +(1960, 'neighborhood', 0, 36, 0, 0, 0, 0, 719, -179, 50, 392, 7969, 712, 0, 0, 4294967295), +(1961, 'neighborhood', 0, 37, 0, 0, 0, 0, -787, 352, 5, 69, 7969, 712, 0, 0, 4294967295), +(1962, 'neighborhood', 0, 41, 0, 0, 0, 0, -2694, 2035, 6, 27, 7969, 712, 0, 0, 4294967295), +(1963, 'neighborhood', 0, 42, 0, 0, 0, 0, -542, 2089, -35, 374, 7969, 712, 0, 0, 4294967295), +(1964, 'neighborhood', 0, 43, 0, 0, 0, 0, 1252, 1497, 65, 119, 7969, 712, 0, 0, 4294967295), +(1965, 'neighborhood', 0, 45, 0, 0, 0, 0, 2778, 81, -60, 356, 7969, 712, 0, 0, 4294967295), +(1966, 'neighborhood', 0, 46, 0, 0, 0, 0, 719, -179, 50, 392, 7969, 712, 0, 0, 4294967295), +(1967, 'neighborhood', 0, 47, 0, 0, 0, 0, -787, 352, 5, 69, 7969, 712, 0, 0, 4294967295), +(1968, 'neighborhood', 0, 51, 0, 0, 0, 0, -2694, 2035, 6, 27, 7969, 712, 0, 0, 4294967295), +(1969, 'neighborhood', 0, 52, 0, 0, 0, 0, -542, 2089, -35, 374, 7969, 712, 0, 0, 4294967295), +(1970, 'neighborhood', 0, 53, 0, 0, 0, 0, 1252, 1497, 65, 119, 7969, 712, 0, 0, 4294967295), +(1971, 'neighborhood', 0, 54, 0, 0, 0, 0, 2586, 1642, 47, 67, 7969, 712, 0, 0, 4294967295), +(1972, 'neighborhood', 0, 56, 0, 0, 0, 0, 719, -179, 50, 392, 7969, 712, 0, 0, 4294967295), +(1973, 'neighborhood', 0, 57, 0, 0, 0, 0, -787, 352, 5, 69, 7969, 712, 0, 0, 4294967295), +(1974, 'neighborhood', 0, 61, 0, 0, 0, 0, -2694, 2035, 6, 27, 7969, 712, 0, 0, 4294967295), +(1975, 'neighborhood', 0, 62, 0, 0, 0, 0, -542, 2089, -35, 374, 7969, 712, 0, 0, 4294967295), +(1976, 'neighborhood', 0, 63, 0, 0, 0, 0, 1252, 1497, 65, 119, 7969, 712, 0, 0, 4294967295), +(1977, 'neighborhood', 0, 64, 0, 0, 0, 0, 2586, 1642, 47, 67, 7969, 712, 0, 0, 4294967295), +(1978, 'neighborhood', 0, 65, 0, 0, 0, 0, 2778, 81, -60, 356, 7969, 712, 0, 0, 4294967295), +(1979, 'neighborhood', 0, 67, 0, 0, 0, 0, -787, 352, 5, 69, 7969, 712, 0, 0, 4294967295), +(1980, 'neighborhood', 0, 71, 0, 0, 0, 0, -2694, 2035, 6, 27, 7969, 712, 0, 0, 4294967295), +(1981, 'neighborhood', 0, 72, 0, 0, 0, 0, -542, 2089, -35, 374, 7969, 712, 0, 0, 4294967295), +(1982, 'neighborhood', 0, 73, 0, 0, 0, 0, 1252, 1497, 65, 119, 7969, 712, 0, 0, 4294967295), +(1983, 'neighborhood', 0, 74, 0, 0, 0, 0, 2586, 1642, 47, 67, 7969, 712, 0, 0, 4294967295), +(1984, 'neighborhood', 0, 75, 0, 0, 0, 0, 2778, 81, -60, 356, 7969, 712, 0, 0, 4294967295), +(1985, 'neighborhood', 0, 76, 0, 0, 0, 0, 719, -179, 50, 392, 7969, 712, 0, 0, 4294967295), +(1986, 'feerrott2', 0, 0, 0, 0, 0, 0, 59, -66, 4, 7.6, 0, 48, 0, 0, 4294967295), +(1987, 'feerrott2', 0, 15, 0, 0, 0, 0, -337, 0, 5, 0, 0, 701, 0, 0, 4294967295), +(1988, 'feerrott2', 0, 25, 0, 0, 0, 0, -844, 444, -157, 255, 0, 202, 0, 0, 4294967295), +(1989, 'feerrott2', 0, 30, 0, 0, 0, 0, -1223, 1583, 17, 455, 0, 413, 0, 0, 4294967295), +(1990, 'feerrott2', 0, 40, 0, 0, 0, 0, -385, -99, 1, 999, 0, 49, 0, 0, 4294967295), +(1991, 'feerrott2', 0, 100, 0, 0, 0, 0, 415, -3083, 2, 999, 0, 50, 0, 0, 4294967295), +(1992, 'miragulmare', 0, 31, 0, 0, 0, 0, 170, 899, 68, 383, 0, 702, 0, 0, 4294967295), +(1993, 'somnium', 0, 0, 0, 0, 0, 0, 635.7, -247.57, 180.39, 8, 0, 707, 0, 0, 4294967295), +(1994, 'somnium', 0, 10, 0, 0, 0, 0, 1075, -1936, 8.34, 326, 0, 708, 0, 0, 4294967295), +(1995, 'somnium', 0, 20, 0, 0, 0, 0, 10, 1630, 5, 132, 0, 708, 0, 0, 4294967295), +(1996, 'somnium', 0, 30, 0, 0, 0, 0, 11.21, 420.62, -24.74, 384, 0, 708, 0, 0, 4294967295), +(1997, 'somnium', 0, 100, 0, 0, 0, 0, 234.85, -43.8, -81.99, 129, 0, 708, 0, 0, 4294967295), +(1998, 'thulehouse1', 0, 10, 0, 0, 0, 0, 385, -75, 65, 292, 0, 701, 0, 0, 4294967295), +(1999, 'thulehouse1', 0, 19, 0, 0, 0, 0, 365, 88, 64, 201, 0, 702, 0, 0, 4294967295), +(2000, 'thulehouse1', 0, 20, 0, 0, 0, 0, 387, 76, 64, 216, 0, 701, 0, 0, 4294967295), +(2001, 'thulehouse1', 0, 21, 0, 0, 0, 0, 0, 112, -28, 382, 0, 703, 0, 0, 4294967295), +(2002, 'thulehouse1', 0, 40, 0, 0, 0, 0, -15, 59, 0, 60, 0, 706, 0, 0, 4294967295), +(2003, 'thulehouse1', 0, 41, 0, 0, 0, 0, -1393, 925, -2, 462, 0, 700, 0, 0, 4294967295), +(2004, 'thulehouse1', 0, 51, 0, 0, 0, 0, 0, 0, -1, 511, 0, 704, 0, 0, 4294967295), +(2005, 'thulelibrary', 0, 11, 0, 0, 0, 0, 582, 315, 16, 383, 0, 701, 0, 0, 4294967295); diff --git a/utils/sql/svn/1750_optional_sql_reflect_rule.sql b/utils/sql/svn/1750_optional_sql_reflect_rule.sql new file mode 100644 index 000000000..7155c9acc --- /dev/null +++ b/utils/sql/svn/1750_optional_sql_reflect_rule.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:ReflectType', '1', '0 = disabled, 1 = single target player spells only, 2 = all player spells, 3 = all single target spells, 4 = all spells'); + diff --git a/utils/sql/svn/1753_optional_haste_cap_rule.sql b/utils/sql/svn/1753_optional_haste_cap_rule.sql new file mode 100644 index 000000000..5a467f2b9 --- /dev/null +++ b/utils/sql/svn/1753_optional_haste_cap_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:HasteCap', '100', 'Haste cap for item haste + spell haste(not including overhaste)'); diff --git a/utils/sql/svn/1753_required_sql_healing_adept_aa.sql b/utils/sql/svn/1753_required_sql_healing_adept_aa.sql new file mode 100644 index 000000000..4b22edb9e --- /dev/null +++ b/utils/sql/svn/1753_required_sql_healing_adept_aa.sql @@ -0,0 +1,5 @@ +UPDATE `aa_effects` SET `base1`=5 WHERE `aaid`=78; +UPDATE `aa_effects` SET `base1`=10 WHERE `aaid`=79; +UPDATE `aa_effects` SET `base1`=13 WHERE `aaid`=434; +UPDATE `aa_effects` SET `base1`=16 WHERE `aaid`=435; +UPDATE `aa_effects` SET `base1`=19 WHERE `aaid`=436; \ No newline at end of file diff --git a/utils/sql/svn/1754_required_sql_healing_adept_aa_fix.sql b/utils/sql/svn/1754_required_sql_healing_adept_aa_fix.sql new file mode 100644 index 000000000..620a71a9a --- /dev/null +++ b/utils/sql/svn/1754_required_sql_healing_adept_aa_fix.sql @@ -0,0 +1,10 @@ +UPDATE `aa_effects` SET `base1`=5 WHERE `aaid`=78 && `effectid`=125; +UPDATE `aa_effects` SET `base1`=0 WHERE `aaid`=78 && `effectid`=141; +UPDATE `aa_effects` SET `base1`=10 WHERE `aaid`=79 && `effectid`=125; +UPDATE `aa_effects` SET `base1`=0 WHERE `aaid`=79 && `effectid`=141; +UPDATE `aa_effects` SET `base1`=13 WHERE `aaid`=434 && `effectid`=125; +UPDATE `aa_effects` SET `base1`=0 WHERE `aaid`=434 && `effectid`=141; +UPDATE `aa_effects` SET `base1`=16 WHERE `aaid`=435 && `effectid`=125; +UPDATE `aa_effects` SET `base1`=0 WHERE `aaid`=435 && `effectid`=141; +UPDATE `aa_effects` SET `base1`=19 WHERE `aaid`=436 && `effectid`=125; +UPDATE `aa_effects` SET `base1`=0 WHERE `aaid`=436 && `effectid`=141; \ No newline at end of file diff --git a/utils/sql/svn/1755_required_sql_fear_resist_aas.sql b/utils/sql/svn/1755_required_sql_fear_resist_aas.sql new file mode 100644 index 000000000..a1409f9e1 --- /dev/null +++ b/utils/sql/svn/1755_required_sql_fear_resist_aas.sql @@ -0,0 +1,5 @@ +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (116, 1, 181, 5, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (117, 1, 181, 15, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (118, 1, 181, 25, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (195, 1, 181, 100, 0); + diff --git a/utils/sql/svn/176_melody.sql b/utils/sql/svn/176_melody.sql new file mode 100644 index 000000000..0304c0dbd --- /dev/null +++ b/utils/sql/svn/176_melody.sql @@ -0,0 +1 @@ +INSERT INTO commands (command, access, description) VALUES ('melody', 0, 'A supplement for /melody until the OP code is found.') \ No newline at end of file diff --git a/utils/sql/svn/1784_optional_corpsedrag_rules.sql b/utils/sql/svn/1784_optional_corpsedrag_rules.sql new file mode 100644 index 000000000..858c75cf2 --- /dev/null +++ b/utils/sql/svn/1784_optional_corpsedrag_rules.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:MaxDraggedCorpses', '2', 'Maximum number of corpses that a player can /corpsedrag at once'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:DragCorpseDistance', '400.000000', 'If a player is using /corpsedrag and moving, the corpse will not move until the player exceeds this distance (NoRootNoZ)'); diff --git a/utils/sql/svn/1786_required_update_to_aas.sql b/utils/sql/svn/1786_required_update_to_aas.sql new file mode 100644 index 000000000..88d22741d --- /dev/null +++ b/utils/sql/svn/1786_required_update_to_aas.sql @@ -0,0 +1,20 @@ +-- Heightened Endurance AA +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (683, 1, 189, 1, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (684, 1, 189, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (685, 1, 189, 3, 0); +-- Convalescence AA +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (674, 1, 0, 1, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (675, 1, 0, 2, 0); +-- Healthy Aura AA +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1031, 1, 0, 1, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1032, 1, 0, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1033, 1, 0, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1034, 1, 0, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1035, 1, 0, 5, 0); +-- Expansive Mind AA +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1072, 1, 318, 1, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1073, 1, 318, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1074, 1, 318, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1075, 1, 318, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1076, 1, 318, 5, 0); + diff --git a/utils/sql/svn/1790_required_aa_required_level_cost.sql b/utils/sql/svn/1790_required_aa_required_level_cost.sql new file mode 100644 index 000000000..452710310 --- /dev/null +++ b/utils/sql/svn/1790_required_aa_required_level_cost.sql @@ -0,0 +1,54 @@ +-- +-- Table structure for table `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; + +-- +-- Dumping data for table `aa_required_level_cost` +-- + +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7800, 1, 0, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7801, 6, 0, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7802, 11, 0, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7803, 16, 0, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7804, 21, 0, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7805, 26, 0, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7806, 31, 0, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7807, 36, 0, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7808, 41, 0, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7809, 46, 0, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7810, 51, 3, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7811, 56, 4, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7812, 61, 5, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7813, 66, 6, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7814, 71, 7, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7815, 76, 8, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7816, 81, 8, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7817, 83, 9, 'Harm Touch'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7850, 5, 0, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7851, 11, 0, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7852, 16, 0, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7853, 21, 0, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7854, 26, 0, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7855, 31, 0, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7856, 36, 0, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7857, 41, 0, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7858, 46, 0, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7859, 51, 0, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7860, 56, 3, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7861, 61, 4, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7862, 66, 5, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7863, 71, 6, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7864, 76, 7, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7865, 81, 8, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (7866, 85, 9, 'Lay on Hands'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (593, 61, 2, 'Fervent Blessing'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (594, 63, 4, 'Fervent Blessing'); +INSERT INTO `aa_required_level_cost` (`skill_id`, `level`, `cost`, `description`) VALUES (595, 65, 6, 'Fervent Blessing'); diff --git a/utils/sql/svn/1793_resist_adjust.sql b/utils/sql/svn/1793_resist_adjust.sql new file mode 100644 index 000000000..66ce505ef --- /dev/null +++ b/utils/sql/svn/1793_resist_adjust.sql @@ -0,0 +1 @@ +ALTER TABLE `npc_spells_entries` ADD `resist_adjust` INT NULL AFTER `priority`; \ No newline at end of file diff --git a/utils/sql/svn/1799_optional_rest_regen_endurance_rule.sql b/utils/sql/svn/1799_optional_rest_regen_endurance_rule.sql new file mode 100644 index 000000000..85d10fb1b --- /dev/null +++ b/utils/sql/svn/1799_optional_rest_regen_endurance_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:RestRegenEndurance', 'false', 'Whether rest regen will affect endurance or not'); \ No newline at end of file diff --git a/utils/sql/svn/1802_required_doppelganger.sql b/utils/sql/svn/1802_required_doppelganger.sql new file mode 100644 index 000000000..5addba138 --- /dev/null +++ b/utils/sql/svn/1802_required_doppelganger.sql @@ -0,0 +1,7 @@ +INSERT INTO `npc_types` (`id`, `name`, `lastname`, `level`, `race`, `class`, `bodytype`, `hp`, `gender`, `texture`, `helmtexture`, `size`, `hp_regen_rate`, `mana_regen_rate`, `loottable_id`, `merchant_id`, `npc_spells_id`, `npc_faction_id`, `adventure_template_id`, `trap_template`, `mindmg`, `maxdmg`, `npcspecialattks`, `aggroradius`, `face`, `luclin_hairstyle`, `luclin_haircolor`, `luclin_eyecolor`, `luclin_eyecolor2`, `luclin_beardcolor`, `luclin_beard`, `drakkin_heritage`, `drakkin_tattoo`, `drakkin_details`, `armortint_id`, `armortint_red`, `armortint_green`, `armortint_blue`, `d_meele_texture1`, `d_meele_texture2`, `prim_melee_type`, `sec_melee_type`, `runspeed`, `MR`, `CR`, `DR`, `FR`, `PR`, `Corrup`, `see_invis`, `see_invis_undead`, `qglobal`, `AC`, `npc_aggro`, `spawn_limit`, `attack_speed`, `findable`, `STR`, `STA`, `DEX`, `AGI`, `_INT`, `WIS`, `CHA`, `see_hide`, `see_improved_hide`, `trackable`, `isbot`, `exclude`, `ATK`, `Accuracy`, `slow_mitigation`, `version`, `maxlevel`, `scalerate`, `private_corpse`, `unique_spawn_by_name`) VALUES (678, 'SwarmPetDG1', '', 1, 1, 14, 1, 1000, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 25, 'f', 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 28, 1.25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 100, 0, 0); +INSERT INTO `npc_types` (`id`, `name`, `lastname`, `level`, `race`, `class`, `bodytype`, `hp`, `gender`, `texture`, `helmtexture`, `size`, `hp_regen_rate`, `mana_regen_rate`, `loottable_id`, `merchant_id`, `npc_spells_id`, `npc_faction_id`, `adventure_template_id`, `trap_template`, `mindmg`, `maxdmg`, `npcspecialattks`, `aggroradius`, `face`, `luclin_hairstyle`, `luclin_haircolor`, `luclin_eyecolor`, `luclin_eyecolor2`, `luclin_beardcolor`, `luclin_beard`, `drakkin_heritage`, `drakkin_tattoo`, `drakkin_details`, `armortint_id`, `armortint_red`, `armortint_green`, `armortint_blue`, `d_meele_texture1`, `d_meele_texture2`, `prim_melee_type`, `sec_melee_type`, `runspeed`, `MR`, `CR`, `DR`, `FR`, `PR`, `Corrup`, `see_invis`, `see_invis_undead`, `qglobal`, `AC`, `npc_aggro`, `spawn_limit`, `attack_speed`, `findable`, `STR`, `STA`, `DEX`, `AGI`, `_INT`, `WIS`, `CHA`, `see_hide`, `see_improved_hide`, `trackable`, `isbot`, `exclude`, `ATK`, `Accuracy`, `slow_mitigation`, `version`, `maxlevel`, `scalerate`, `private_corpse`, `unique_spawn_by_name`) VALUES (679, 'SwarmPetDG2', '', 2, 1, 14, 1, 2000, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 25, 'f', 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 28, 1.25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 100, 0, 0); +INSERT INTO `npc_types` (`id`, `name`, `lastname`, `level`, `race`, `class`, `bodytype`, `hp`, `gender`, `texture`, `helmtexture`, `size`, `hp_regen_rate`, `mana_regen_rate`, `loottable_id`, `merchant_id`, `npc_spells_id`, `npc_faction_id`, `adventure_template_id`, `trap_template`, `mindmg`, `maxdmg`, `npcspecialattks`, `aggroradius`, `face`, `luclin_hairstyle`, `luclin_haircolor`, `luclin_eyecolor`, `luclin_eyecolor2`, `luclin_beardcolor`, `luclin_beard`, `drakkin_heritage`, `drakkin_tattoo`, `drakkin_details`, `armortint_id`, `armortint_red`, `armortint_green`, `armortint_blue`, `d_meele_texture1`, `d_meele_texture2`, `prim_melee_type`, `sec_melee_type`, `runspeed`, `MR`, `CR`, `DR`, `FR`, `PR`, `Corrup`, `see_invis`, `see_invis_undead`, `qglobal`, `AC`, `npc_aggro`, `spawn_limit`, `attack_speed`, `findable`, `STR`, `STA`, `DEX`, `AGI`, `_INT`, `WIS`, `CHA`, `see_hide`, `see_improved_hide`, `trackable`, `isbot`, `exclude`, `ATK`, `Accuracy`, `slow_mitigation`, `version`, `maxlevel`, `scalerate`, `private_corpse`, `unique_spawn_by_name`) VALUES (680, 'SwarmPetDG3', '', 3, 1, 14, 1, 3000, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 25, 'f', 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 28, 1.25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 100, 0, 0); + +INSERT INTO `pets` (`type`, `npcID`, `temp`) VALUES ('SwarmPetDG1', 678, 1); +INSERT INTO `pets` (`type`, `npcID`, `temp`) VALUES ('SwarmPetDG2', 679, 1); +INSERT INTO `pets` (`type`, `npcID`, `temp`) VALUES ('SwarmPetDG3', 680, 1); diff --git a/utils/sql/svn/1803_required_tasks_xpreward_signed.sql b/utils/sql/svn/1803_required_tasks_xpreward_signed.sql new file mode 100644 index 000000000..39516b643 --- /dev/null +++ b/utils/sql/svn/1803_required_tasks_xpreward_signed.sql @@ -0,0 +1,8 @@ +-- Convert Negative values to be less than the max signed value +UPDATE tasks SET xpreward = (xpreward - 2147483649) WHERE xpreward > 4294941695 AND xpreward < 4294967196; + +-- Change the xpreward field to be signed +ALTER TABLE tasks CHANGE `xpreward` `xpreward` INT(10) SIGNED NOT NULL DEFAULT '0'; + +-- Convert the previously converted values into actual negative values +UPDATE tasks SET xpreward = (xpreward - 2147483647) WHERE xpreward > 2147458047 AND xpreward < 2147483548; \ No newline at end of file diff --git a/utils/sql/svn/1804_required_ae_melee_updates.sql b/utils/sql/svn/1804_required_ae_melee_updates.sql new file mode 100644 index 000000000..85646fb65 --- /dev/null +++ b/utils/sql/svn/1804_required_ae_melee_updates.sql @@ -0,0 +1,11 @@ +-- Persistent Casting +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (692, 1, 229, 5, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (693, 1, 229, 12, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (694, 1, 229, 25, 0); +-- Destructive Force +UPDATE `aa_actions` SET `nonspell_action`=0 WHERE `aaid`=828 AND `rank`=0 LIMIT 1; +UPDATE `aa_actions` SET `nonspell_action`=0 WHERE `aaid`=828 AND `rank`=1 LIMIT 1; +UPDATE `aa_actions` SET `nonspell_action`=0 WHERE `aaid`=828 AND `rank`=2 LIMIT 1; +-- Rampage +UPDATE `aa_actions` SET `spell_id`=5233, `nonspell_action`=0 WHERE `aaid`=258 AND `rank`=0 LIMIT 1; +UPDATE `altadv_vars` SET `spellid`=5233 WHERE `skill_id`=258 LIMIT 1; \ No newline at end of file diff --git a/utils/sql/svn/1809_optional_rules.sql b/utils/sql/svn/1809_optional_rules.sql new file mode 100644 index 000000000..ce275f478 --- /dev/null +++ b/utils/sql/svn/1809_optional_rules.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:CorpseResTimeMS', '10800000', 'Time to res a corpse(ms)'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:VirusSpreadDistance', '30', 'The distance a viral spell will jump to its next victim'); diff --git a/utils/sql/svn/1813_required_doppelganger_npcid_change.sql b/utils/sql/svn/1813_required_doppelganger_npcid_change.sql new file mode 100644 index 000000000..e5322cdd5 --- /dev/null +++ b/utils/sql/svn/1813_required_doppelganger_npcid_change.sql @@ -0,0 +1,7 @@ +UPDATE `pets` SET `npcID`=682 WHERE `type`='SwarmPetDG1' LIMIT 1; +UPDATE `pets` SET `npcID`=683 WHERE `type`='SwarmPetDG2' LIMIT 1; +UPDATE `pets` SET `npcID`=684 WHERE `type`='SwarmPetDG3' LIMIT 1; + +UPDATE `npc_types` SET `id`=682 WHERE `id`=678 && `name`='SwarmPetDG1' LIMIT 1; +UPDATE `npc_types` SET `id`=683 WHERE `id`=679 && `name`='SwarmPetDG2' LIMIT 1; +UPDATE `npc_types` SET `id`=684 WHERE `id`=680 && `name`='SwarmPetDG3' LIMIT 1; \ No newline at end of file diff --git a/utils/sql/svn/1817_optional_npc_archery_bonus_rule.sql b/utils/sql/svn/1817_optional_npc_archery_bonus_rule.sql new file mode 100644 index 000000000..7afc7b358 --- /dev/null +++ b/utils/sql/svn/1817_optional_npc_archery_bonus_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:ArcheryNPCMultiplier', '1.0', 'Multiplied by the min and max hit to determine npcs ranged dmg'); diff --git a/utils/sql/svn/1823_optional_delay_death.sql b/utils/sql/svn/1823_optional_delay_death.sql new file mode 100644 index 000000000..a48540455 --- /dev/null +++ b/utils/sql/svn/1823_optional_delay_death.sql @@ -0,0 +1,6 @@ +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1026, 1, 328, 50, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1027, 1, 328, 100, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1028, 1, 328, 150, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1029, 1, 328, 200, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES (1030, 1, 328, 250, 0); + diff --git a/utils/sql/svn/1847_required_doors_dest_zone_size_32.sql b/utils/sql/svn/1847_required_doors_dest_zone_size_32.sql new file mode 100644 index 000000000..a6c926e3f --- /dev/null +++ b/utils/sql/svn/1847_required_doors_dest_zone_size_32.sql @@ -0,0 +1 @@ +ALTER TABLE doors CHANGE `dest_zone` `dest_zone` VARCHAR(32) DEFAULT 'NONE'; diff --git a/utils/sql/svn/1859_optional_item_casts_use_focus_rule.sql b/utils/sql/svn/1859_optional_item_casts_use_focus_rule.sql new file mode 100644 index 000000000..b6a02b696 --- /dev/null +++ b/utils/sql/svn/1859_optional_item_casts_use_focus_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:ItemCastsUseFocus', 'false', 'If true, this allows item clickies to use focuses that have limited max levels on them'); diff --git a/utils/sql/svn/1884_optional_bot_spells_update.sql b/utils/sql/svn/1884_optional_bot_spells_update.sql new file mode 100644 index 000000000..cf82bad5c --- /dev/null +++ b/utils/sql/svn/1884_optional_bot_spells_update.sql @@ -0,0 +1,55 @@ +-- Shaman Spells +UPDATE npc_spells_entries SET priority = 2, maxlevel = 61 WHERE spellid = (SELECT id FROM spells_new WHERE name ='Chloroblast') AND npc_spells_id = (SELECT id FROM npc_spells WHERE name = 'Shaman Bot'); + +REPLACE INTO npc_spells_entries (npc_spells_id, spellid, type, minlevel, maxlevel, priority) VALUES +-- HoTs +((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT id FROM spells_new WHERE name = 'Breath of Trushar'), 2, 65, 69, 1), +((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT id FROM spells_new WHERE name = 'Spiritual Serenity'), 2, 70, 255, 1), +-- Heal +((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT id FROM spells_new WHERE name = 'Kragg\'s Mending'), 2, 58, 61, 2); + + +-- Druid Spells +REPLACE INTO npc_spells_entries (npc_spells_id, spellid, type, minlevel, maxlevel, priority) VALUES +-- Heals +((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT id FROM spells_new WHERE name = 'Tunare\'s Renewal'), 2, 58, 63, 2), +((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT id FROM spells_new WHERE name = 'Karana\'s Renewal'), 2, 64, 255, 2); + + +-- Paladin Spells +UPDATE npc_spells_entries SET minlevel = 58, maxlevel = 64 WHERE spellid = (SELECT id FROM spells_new WHERE name ='Healing Wave of Prexus') AND npc_spells_id = (SELECT id FROM npc_spells WHERE name = 'Paladin Bot'); +UPDATE npc_spells_entries SET maxlevel = 61 WHERE spellid = (SELECT id FROM spells_new WHERE name ='Force of Akera') AND npc_spells_id = (SELECT id FROM npc_spells WHERE name = 'Paladin Bot'); +UPDATE npc_spells_entries SET maxlevel = 64 WHERE spellid = (SELECT id FROM spells_new WHERE name ='Force of Akilae') AND npc_spells_id = (SELECT id FROM npc_spells WHERE name = 'Paladin Bot'); +UPDATE npc_spells_entries SET maxlevel = 67 WHERE spellid = (SELECT id FROM spells_new WHERE name ='Quellious\' Word of Serenity') AND npc_spells_id = (SELECT id FROM npc_spells WHERE name = 'Paladin Bot'); +UPDATE npc_spells_entries SET maxlevel = 69 WHERE spellid = (SELECT id FROM spells_new WHERE name ='Force of Piety') AND npc_spells_id = (SELECT id FROM npc_spells WHERE name = 'Paladin Bot'); +UPDATE npc_spells_entries SET maxlevel = 255 WHERE spellid = (SELECT id FROM spells_new WHERE name ='Serene Command') AND npc_spells_id = (SELECT id FROM npc_spells WHERE name = 'Paladin Bot'); + +REPLACE INTO npc_spells_entries (npc_spells_id, spellid, type, minlevel, maxlevel, priority) VALUES +-- Group Heals +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT id FROM spells_new WHERE name = 'Wave of Life'), 2, 39, 54, 2), +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT id FROM spells_new WHERE name = 'Wave of Healing'), 2, 55, 57, 2), +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT id FROM spells_new WHERE name = 'Wave of Marr'), 2, 65, 69, 2), +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT id FROM spells_new WHERE name = 'Wave of Trushar'), 2, 65, 69, 2), +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT id FROM spells_new WHERE name = 'Wave of Piety'), 2, 70, 255, 2); + + +-- Cleric Spells +REPLACE INTO npc_spells_entries (npc_spells_id, spellid, type, minlevel, maxlevel, priority) VALUES +-- Fast Heals +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Remedy'), 2, 51, 60, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Supernal Remedy'), 2, 61, 65, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Pious Remedy'), 2, 66, 255, 2), +-- Regular Heals +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Divine Light'), 2, 53, 57, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Ethereal Light'), 2, 58, 62, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Supernal Light'), 2, 63, 64, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Holy Light'), 2, 65, 67, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Pious Light'), 2, 68, 69, 2), +-- Group Heals +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Word of Health'), 2, 30, 44, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Word of Healing'), 2, 45, 51, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Word of Vigor'), 2, 52, 56, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Word of Restoration'), 2, 57, 63, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Word of Redemption'), 2, 60, 255, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Word of Replenishment'), 2, 64, 68, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT id FROM spells_new WHERE name = 'Word of Vivification'), 2, 69, 255, 2); \ No newline at end of file diff --git a/utils/sql/svn/1885_optional_rules_fv_pvp_expansions.sql b/utils/sql/svn/1885_optional_rules_fv_pvp_expansions.sql new file mode 100644 index 000000000..1527d50c7 --- /dev/null +++ b/utils/sql/svn/1885_optional_rules_fv_pvp_expansions.sql @@ -0,0 +1,5 @@ +INSERT INTO `rule_values` (`ruleset_id`,`rule_name`,`rule_value`,`notes`) VALUES ('1','World:ExpansionSettings','16383','Expansion settings. Affects client features related to expansions.'); +INSERT INTO `rule_values` (`ruleset_id`,`rule_name`,`rule_value`,`notes`) VALUES ('1','World:PVPSettings','0','PVP Settings, affects clients\' attack state and Sony-hardcoded checks for PVP rules.'); +INSERT INTO `rule_values` (`ruleset_id`,`rule_name`,`rule_value`,`notes`) VALUES ('1','World:IsGMPetitionWindowEnabled','false','Enables the petition queue window on the client. This menu is accessable by pressing the G key ingame with the GM flag on.'); +INSERT INTO `rule_values` (`ruleset_id`,`rule_name`,`rule_value`,`notes`) VALUES ('1','World:FVNoDropFlag','0','Sets the FV ruleset to on or off. Enable with rule 2 for GM-trading only. Keep in mind this disables OOC chatter for GMs only if you choose that option.'); +INSERT INTO `rule_values` (`ruleset_id`,`rule_name`,`rule_value`,`notes`) VALUES ('1','Character:MinStatusForNoDropExemptions','80','Enables bypassing of no-drop flags if status is set to this value and FVNoDropFlag is set to 2.'); \ No newline at end of file diff --git a/utils/sql/svn/1889_optional_skill_cap_rule.sql b/utils/sql/svn/1889_optional_skill_cap_rule.sql new file mode 100644 index 000000000..0bdac69d5 --- /dev/null +++ b/utils/sql/svn/1889_optional_skill_cap_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`,`rule_name`,`rule_value`,`notes`) VALUES ('1','Character:SkillCapMaxLevel','-1','Sets the Max Level used for Skill Caps (from skill_caps table). Default of -1 makes it use MaxLevel rule value.'); diff --git a/utils/sql/svn/189_character_.sql b/utils/sql/svn/189_character_.sql new file mode 100644 index 000000000..e1401f4de --- /dev/null +++ b/utils/sql/svn/189_character_.sql @@ -0,0 +1,3 @@ +ALTER TABLE `character_` ADD `lfp` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '0'; +ALTER TABLE `character_` ADD `lfg` TINYINT( 1 ) UNSIGNED NOT NULL DEFAULT '0'; + diff --git a/utils/sql/svn/1908_required_npc_types_definitions.sql b/utils/sql/svn/1908_required_npc_types_definitions.sql new file mode 100644 index 000000000..b68bbf09e --- /dev/null +++ b/utils/sql/svn/1908_required_npc_types_definitions.sql @@ -0,0 +1,2 @@ +ALTER TABLE `npc_types` ADD COLUMN `attack_count` SMALLINT NOT NULL DEFAULT '-1' AFTER `maxdmg`; +ALTER TABLE `npc_types` ADD COLUMN `mana` INT(11) NOT NULL DEFAULT '0' AFTER `hp`; \ No newline at end of file diff --git a/utils/sql/svn/1926_optional_stat_cap.sql b/utils/sql/svn/1926_optional_stat_cap.sql new file mode 100644 index 000000000..449855984 --- /dev/null +++ b/utils/sql/svn/1926_optional_stat_cap.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`,`rule_name`,`rule_value`,`notes`) VALUES ('1','Character:StatCap','0','Sets the Max Statistics Cap for PCs. 0 = feature disabled'); \ No newline at end of file diff --git a/utils/sql/svn/1944_spawn2.sql b/utils/sql/svn/1944_spawn2.sql new file mode 100644 index 000000000..18d638b54 --- /dev/null +++ b/utils/sql/svn/1944_spawn2.sql @@ -0,0 +1 @@ +ALTER TABLE `spawn2` ADD COLUMN `animation` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `enabled`; diff --git a/utils/sql/svn/1946_doors.sql b/utils/sql/svn/1946_doors.sql new file mode 100644 index 000000000..aed68920b --- /dev/null +++ b/utils/sql/svn/1946_doors.sql @@ -0,0 +1,2 @@ +ALTER TABLE doors DROP INDEX DoorIndex; +ALTER TABLE doors ADD CONSTRAINT DoorIndex UNIQUE KEY (zone, doorid, version); diff --git a/utils/sql/svn/1960_optional_console_timeout_rule.sql b/utils/sql/svn/1960_optional_console_timeout_rule.sql new file mode 100644 index 000000000..29b5ebf21 --- /dev/null +++ b/utils/sql/svn/1960_optional_console_timeout_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Console:SessionTimeOut', '600000', 'This sets the default timeout time for Telnet sessions (MS)'); \ No newline at end of file diff --git a/utils/sql/svn/1962_optional_guild_creation_window_rules.sql b/utils/sql/svn/1962_optional_guild_creation_window_rules.sql new file mode 100644 index 000000000..c8a04b18a --- /dev/null +++ b/utils/sql/svn/1962_optional_guild_creation_window_rules.sql @@ -0,0 +1,5 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Guild:PlayerCreationAllowed', 'true', 'Allow players with Underfoot+ to create a guild via new UI window.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Guild:PlayerCreationLimit', '1', 'Allow players to create a guild using the window in Underfoot+'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Guild:PlayerCreationRequiredLevel', '0', 'Required level to use the UF+ window to create a new guild.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Guild:PlayerCreationRequiredStatus', '0', 'Required admin status to use UF+ window to create a new guild.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Guild:PlayerCreationRequiredTime', '0', 'Required Time Entitled on Account to be able to use UF+ window to create a new guild.'); diff --git a/utils/sql/svn/1963_optional_rule_live_like_focuses.sql b/utils/sql/svn/1963_optional_rule_live_like_focuses.sql new file mode 100644 index 000000000..f74f3576a --- /dev/null +++ b/utils/sql/svn/1963_optional_rule_live_like_focuses.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:LiveLikeFocusEffects', 'true', 'Makes certain healing, dmg and mana reduction focuses random like live'); \ No newline at end of file diff --git a/utils/sql/svn/1968_optional_enrage_rules.sql b/utils/sql/svn/1968_optional_enrage_rules.sql new file mode 100644 index 000000000..d438fa4b1 --- /dev/null +++ b/utils/sql/svn/1968_optional_enrage_rules.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'NPC:StartEnrageValue', '9', '% HP value that mobs will begin to enrage.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'NPC:LiveLikeEnrage', 'false', 'If enabled, will cause all non-player pets to lose the ability to enrage.'); \ No newline at end of file diff --git a/utils/sql/svn/196_trader.sql b/utils/sql/svn/196_trader.sql new file mode 100644 index 000000000..4430598fa --- /dev/null +++ b/utils/sql/svn/196_trader.sql @@ -0,0 +1,22 @@ +DROP TABLE IF EXISTS `trader`; +CREATE TABLE `trader` ( + `char_id` int(10) unsigned NOT NULL default '0', + `item_id` int(10) unsigned NOT NULL default '0', + `serialnumber` int(10) unsigned NOT NULL default '0', + `charges` int(11) NOT NULL default '0', + `item_cost` int(10) unsigned NOT NULL default '0', + `slot_id` tinyint(3) unsigned NOT NULL default '0', + PRIMARY KEY (`char_id`,`slot_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `trader_audit`; +CREATE TABLE `trader_audit` ( + `time` datetime NOT NULL, + `seller` varchar(64) NOT NULL, + `buyer` varchar(64) NOT NULL, + `itemname` varchar(64) NOT NULL, + `quantity` int(11) NOT NULL, + `totalcost` int(11) NOT NULL +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +UPDATE doors set opentype=155 where opentype=154 and zone='bazaar'; diff --git a/utils/sql/svn/1972_optional_extradmg_item_cap.sql b/utils/sql/svn/1972_optional_extradmg_item_cap.sql new file mode 100644 index 000000000..34498a0ca --- /dev/null +++ b/utils/sql/svn/1972_optional_extradmg_item_cap.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:ItemExtraDmgCap', '150', 'eg +bash, +frenzy dmg, etc'); \ No newline at end of file diff --git a/utils/sql/svn/1974_required_bot_spells_update.sql b/utils/sql/svn/1974_required_bot_spells_update.sql new file mode 100644 index 000000000..9a912140b --- /dev/null +++ b/utils/sql/svn/1974_required_bot_spells_update.sql @@ -0,0 +1,34 @@ +INSERT INTO `npc_spells_entries` (`id`, `npc_spells_id`, `spellid`, `type`, `minlevel`, `maxlevel`) VALUES (18145, 705, 190, 2048, 47, 55); +INSERT INTO `npc_spells_entries` (`id`, `npc_spells_id`, `spellid`, `type`, `minlevel`, `maxlevel`) VALUES (18146, 705, 292, 2048, 2, 55); +INSERT INTO `npc_spells_entries` (`id`, `npc_spells_id`, `spellid`, `type`, `minlevel`, `maxlevel`) VALUES (18147, 705, 187, 2048, 13, 55); +INSERT INTO `npc_spells_entries` (`id`, `npc_spells_id`, `spellid`, `type`, `minlevel`, `maxlevel`) VALUES (18148, 705, 188, 2048, 30, 55); +INSERT INTO `npc_spells_entries` (`id`, `npc_spells_id`, `spellid`, `type`, `minlevel`) VALUES (18150, 705, 1691, 2048, 54); +INSERT INTO `npc_spells_entries` (`id`, `npc_spells_id`, `spellid`, `type`, `minlevel`) VALUES (18151, 705, 1692, 2048, 59); +INSERT INTO `npc_spells_entries` (`id`, `npc_spells_id`, `spellid`, `type`, `minlevel`) VALUES (18152, 705, 2120, 2048, 60); +INSERT INTO `npc_spells_entries` (`id`, `npc_spells_id`, `spellid`, `type`, `minlevel`) VALUES (18153, 705, 3341, 2048, 61); +INSERT INTO `npc_spells_entries` (`id`, `npc_spells_id`, `spellid`, `type`, `minlevel`) VALUES (18154, 705, 3354, 2048, 63); +INSERT INTO `npc_spells_entries` (`id`, `npc_spells_id`, `spellid`, `type`, `minlevel`) VALUES (18155, 705, 3358, 2048, 64); +INSERT INTO `npc_spells_entries` (`id`, `npc_spells_id`, `spellid`, `type`, `minlevel`) VALUES (18156, 705, 5503, 2048, 67); +INSERT INTO `npc_spells_entries` (`id`, `npc_spells_id`, `spellid`, `type`, `minlevel`) VALUES (18157, 705, 8035, 2048, 68); +INSERT INTO `npc_spells_entries` (`id`, `npc_spells_id`, `spellid`, `type`, `minlevel`) VALUES (18158, 705, 5520, 2048, 69); + +UPDATE `npc_spells_entries` SET `priority`=1 WHERE `priority`=0 AND 'npc_spells_id' = 705; +UPDATE `npc_spells_entries` SET `priority`=8 WHERE `priority`=7 AND 'npc_spells_id' = 705; +UPDATE `npc_spells_entries` SET `priority`=7 WHERE `priority`=6 AND 'npc_spells_id' = 705; +UPDATE `npc_spells_entries` SET `priority`=6 WHERE `priority`=5 AND 'npc_spells_id' = 705; +UPDATE `npc_spells_entries` SET `priority`=5 WHERE `priority`=4 AND 'npc_spells_id' = 705; +UPDATE `npc_spells_entries` SET `priority`=4 WHERE `priority`=3 AND 'npc_spells_id' = 705; +UPDATE `npc_spells_entries` SET `priority`=3 WHERE `priority`=2 AND 'npc_spells_id' = 705; +UPDATE `npc_spells_entries` SET `priority`=2 WHERE `priority`=1 AND TYPE != 2048 AND 'npc_spells_id' = 705; + +UPDATE `npc_spells_entries` SET `maxlevel`=12 WHERE `id`=18146 LIMIT 1; +UPDATE `npc_spells_entries` SET `maxlevel`=29 WHERE `id`=18147 LIMIT 1; +UPDATE `npc_spells_entries` SET `maxlevel`=46 WHERE `id`=18148 LIMIT 1; +UPDATE `npc_spells_entries` SET `maxlevel`=58 WHERE `id`=18150 LIMIT 1; +UPDATE `npc_spells_entries` SET `maxlevel`=59 WHERE `id`=18151 LIMIT 1; +UPDATE `npc_spells_entries` SET `maxlevel`=60 WHERE `id`=18152 LIMIT 1; +UPDATE `npc_spells_entries` SET `maxlevel`=62 WHERE `id`=18153 LIMIT 1; +UPDATE `npc_spells_entries` SET `maxlevel`=63 WHERE `id`=18154 LIMIT 1; +UPDATE `npc_spells_entries` SET `maxlevel`=66 WHERE `id`=18155 LIMIT 1; +UPDATE `npc_spells_entries` SET `maxlevel`=67 WHERE `id`=18156 LIMIT 1; +UPDATE `npc_spells_entries` SET `maxlevel`=68 WHERE `id`=18157 LIMIT 1; diff --git a/utils/sql/svn/1977_underwater.sql b/utils/sql/svn/1977_underwater.sql new file mode 100644 index 000000000..ad15c41a2 --- /dev/null +++ b/utils/sql/svn/1977_underwater.sql @@ -0,0 +1 @@ +ALTER TABLE `npc_types` ADD COLUMN `underwater` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `unique_spawn_by_name`; diff --git a/utils/sql/svn/1998_optional_intoxication_and_looting_rules.sql b/utils/sql/svn/1998_optional_intoxication_and_looting_rules.sql new file mode 100644 index 000000000..e564bb416 --- /dev/null +++ b/utils/sql/svn/1998_optional_intoxication_and_looting_rules.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:CheckCursorEmptyWhenLooting', 'true', 'If true, a player cannot loot a corpse (player or NPC) with an item on their cursor'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:MaintainIntoxicationAcrossZones', 'true', 'If true, alcohol effects are maintained across zoning and logging out/in.'); diff --git a/utils/sql/svn/1_task_system.sql b/utils/sql/svn/1_task_system.sql new file mode 100644 index 000000000..1dcfba827 --- /dev/null +++ b/utils/sql/svn/1_task_system.sql @@ -0,0 +1,169 @@ + +DROP TABLE IF EXISTS `tasks`; +CREATE TABLE `tasks` ( + `id` int(11) unsigned NOT NULL, + `duration` int(11) unsigned NOT NULL, + `title` varchar(100) NOT NULL, + `description` varchar(2047) NOT NULL, + `reward` varchar(64) NOT NULL, + `rewardid` int(11) unsigned NOT NULL default '0', + `cashreward` int(11) unsigned NOT NULL default '0', + `xpreward` int(10) unsigned NOT NULL default '0', + `rewardmethod` tinyint(3) unsigned NOT NULL default '2', + `startzone` int(11) NOT NULL, + `stepped` tinyint(3) unsigned NOT NULL default '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS `activities`; +CREATE TABLE `activities` ( + `taskid` int(11) unsigned NOT NULL, + `activityid` int(11) unsigned NOT NULL default '0', + `step` int(11) unsigned NOT NULL default '0', + `activitytype` tinyint(3) unsigned NOT NULL default '0', + `text1` varchar(64) NOT NULL, + `text2` varchar(64) NOT NULL, + `text3` varchar(128) NOT NULL, + `goalid` int(11) unsigned NOT NULL default '0', + `goalmethod` int(10) unsigned NOT NULL default '0', + `goalcount` int(11) default '1', + `delivertonpc` int(11) unsigned NOT NULL default '0', + `zoneid` int(11) NOT NULL default '0', + `optional` tinyint(1) NOT NULL default '0', + PRIMARY KEY (`taskid`,`activityid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +INSERT INTO `activities` VALUES (1,0,1,1,'Guard Brookrock','Elven Bottle Of Wine','',36078,0,1,54124,54,0),(1,1,2,5,'Camp of Outsiders','','',1,0,1,0,54,0),(1,2,3,4,'Guard Brookrock','','',54124,0,1,0,54,0),(1,3,4,2,'Bandits','','',7,1,3,0,54,0),(1,4,5,4,'Guard Brookrock','','',54124,0,1,0,54,0),(1,5,6,5,'the entrance to Crushbone Citadel','','',2,0,1,0,54,0),(1,6,6,2,'Orcs','','',1,1,5,0,54,0),(1,7,7,4,'Guard Brookrock','','',54124,0,1,0,54,0),(1,8,8,4,'V\'Lynn Renloe','','',202291,0,1,0,202,0),(2,0,0,2,'Orcs','','',1,1,5,0,21,0),(2,1,0,3,'any creature','Rusty Items','',2,1,3,0,0,0),(2,2,0,5,'','','Locate the Antonica Spires in the Luclin Nexus',1,0,1,0,152,0),(3,0,0,7,'','Fish','',4,1,1,0,69,0),(3,1,0,8,'','Roots, berries, mushrooms or cherries','',3,1,1,0,69,0),(3,2,0,2,'','','Kill Pyzjn',4147,0,1,0,0,1),(4,0,0,11,'','','Locate the lair of Innoruuk',0,0,1,0,76,0),(4,1,0,11,'','','Find the lair of Cazic Thule',0,0,1,0,72,0),(5,0,0,3,'','','Recover the Ring of the Ancients',12268,0,1,0,0,0),(5,1,0,3,'','','Retrieve a Shadowed Rapier',7100,0,1,0,0,0),(5,2,2,1,'','','Return the Ring of the Ancients to Hasten Bootstrutter',12268,0,1,50320,50,0),(5,3,2,1,'','','Present the Shadowed Rapier to Hasten Bootstrutter',7100,0,1,50320,50,0),(5,4,2,100,'','','Give 3500 gold pieces to Hasten Bootstrutter',0,0,350000,50320,50,0),(6,0,0,6,'Class 1 Wood Point Arrows','Class 1 Wood Point Arrows','',8,1,10,0,0,0),(7,0,0,2,'Rats','','',9,1,10,0,4,0),(7,1,0,4,'Guard Philbin','','',4062,2,1,0,4,0),(7,2,0,2,'','','Kill 1 more rat please',9,1,1,0,4,0),(7,3,0,4,'Guard Philbin','','',4062,2,1,0,4,0),(8,0,0,2,'Bats','','',10,1,5,0,4,0),(8,1,0,4,'Guard Philbin','','',4062,2,1,0,4,0),(9,0,0,2,'Snakes','','',11,1,2,0,4,0),(9,1,0,4,'Guard Philbin','','',4062,2,1,0,4,0),(10,0,0,2,'Gnolls','','',12,1,3,0,4,0),(10,1,0,4,'Guard Philbin','','',4062,2,1,0,4,0),(11,0,0,2,'Skeletons','','',13,1,6,0,4,0),(11,1,0,4,'Guard Philbin','','',4062,2,1,0,4,0); + + +DROP TABLE IF EXISTS `character_tasks`; +CREATE TABLE `character_tasks` ( + `charid` int(11) unsigned NOT NULL, + `taskid` int(11) unsigned NOT NULL, + `slot` int(11) unsigned NOT NULL, + `acceptedtime` int(11) unsigned default NULL, + PRIMARY KEY (`charid`,`taskid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `character_activities`; +CREATE TABLE `character_activities` ( + `charid` int(11) unsigned NOT NULL, + `taskid` int(11) unsigned NOT NULL, + `activityid` int(11) unsigned NOT NULL default '0', + `donecount` int(11) unsigned NOT NULL default '0', + `completed` tinyint(1) default '0', + PRIMARY KEY (`charid`,`taskid`,`activityid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +DROP TABLE IF EXISTS `character_enabledtasks`; +CREATE TABLE `character_enabledtasks` ( + `charid` int(11) unsigned NOT NULL, + `taskid` int(11) unsigned NOT NULL, + PRIMARY KEY (`charid`,`taskid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS `completed_tasks`; +CREATE TABLE `completed_tasks` ( + `charid` int(11) unsigned NOT NULL, + `completedtime` int(11) unsigned NOT NULL, + `taskid` int(11) unsigned NOT NULL, + `activityid` int(11) NOT NULL, + PRIMARY KEY (`charid`,`completedtime`,`taskid`,`activityid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS `goallists`; +CREATE TABLE `goallists` ( + `listid` int(10) unsigned NOT NULL, + `entry` int(10) unsigned NOT NULL, + PRIMARY KEY (`listid`,`entry`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +INSERT INTO `goallists` VALUES (1,5001),(1,5002),(1,5003),(1,5078),(1,5079),(1,5080),(1,5082),(1,5083),(1,5084),(1,5085),(1,5089),(1,5108),(1,5109),(1,5112),(1,5114),(1,5130),(1,9129),(1,10000),(1,10159),(1,10166),(1,20028),(1,20033),(1,20036),(1,20048),(1,20171),(1,21005),(1,21012),(1,21026),(1,21038),(1,21039),(1,21096),(1,21097),(1,21102),(1,21107),(1,21115),(1,21118),(1,21119),(1,21121),(1,22005),(1,22022),(1,22031),(1,22052),(1,22053),(1,22055),(1,22142),(1,22143),(1,22149),(1,22155),(1,22165),(1,22171),(1,22172),(1,22179),(1,30033),(1,30034),(1,30042),(1,33033),(1,33034),(1,33036),(1,33037),(1,33038),(1,33064),(1,33108),(1,33113),(1,33123),(1,33124),(1,33156),(1,34015),(1,34105),(1,35007),(1,35008),(1,35009),(1,35011),(1,35034),(1,35039),(1,35042),(1,35043),(1,35044),(1,35052),(1,35091),(1,35092),(1,35116),(1,35129),(1,37014),(1,37015),(1,37017),(1,37018),(1,37026),(1,37032),(1,37035),(1,37040),(1,37069),(1,50027),(1,50028),(1,50033),(1,50034),(1,50065),(1,50070),(1,50211),(1,50259),(1,51088),(1,51089),(1,51090),(1,51110),(1,51111),(1,54001),(1,54003),(1,54004),(1,54008),(1,54015),(1,54016),(1,54019),(1,54027),(1,54037),(1,54039),(1,54185),(1,54187),(1,54188),(1,54190),(1,54193),(1,54194),(1,54195),(1,54200),(1,57001),(1,57003),(1,57004),(1,57015),(1,57020),(1,57040),(1,57041),(1,57056),(1,57085),(1,57090),(1,57136),(1,58000),(1,58001),(1,58002),(1,58003),(1,58004),(1,58005),(1,58006),(1,58008),(1,58009),(1,58011),(1,58012),(1,58013),(1,58015),(1,58016),(1,58024),(1,58025),(1,58026),(1,58027),(1,58029),(1,58033),(1,58034),(1,58035),(1,58036),(1,58037),(1,58038),(1,58039),(1,58040),(1,58041),(1,58042),(1,58043),(1,58045),(1,58047),(1,58053),(1,58054),(1,58055),(1,68055),(1,68056),(1,68168),(1,68169),(1,68190),(1,68200),(1,68223),(1,70010),(1,70011),(1,70042),(1,255702),(1,269011),(1,269012),(2,2310),(2,5013),(2,5014),(2,5015),(2,5016),(2,5019),(2,5020),(2,5021),(2,5022),(2,5023),(2,5024),(2,5025),(2,5040),(2,5072),(2,5074),(2,5076),(2,6011),(2,6013),(2,6014),(2,6015),(2,6016),(2,6838),(2,7007),(2,7008),(2,7009),(2,7010),(2,7790),(2,13346),(2,13970),(2,16253),(2,19943),(2,19950),(2,20036),(2,20176),(2,20187),(2,20198),(2,20258),(2,20297),(2,20414),(2,26800),(2,30980),(2,31992),(2,36750),(2,60084),(2,67531),(2,70482),(2,86875),(2,86961),(2,89615),(2,94100),(2,94114),(2,94128),(2,94170),(2,94212),(3,13045),(3,13046),(3,13047),(3,13048),(3,13106),(3,13419),(3,14905),(3,14969),(3,22745),(3,31485),(4,13019),(4,22745),(5,2300),(5,17981),(6,5500),(6,5507),(6,8003),(7,54035),(7,54036),(7,54207),(7,54235),(7,54250),(7,54254),(8,8500),(8,8572),(8,8644),(9,4007),(9,4009),(9,4013),(9,4024),(9,4036),(9,4043),(9,4078),(9,4080),(10,4002),(10,4009),(10,4011),(10,4012),(10,4014),(10,4025),(10,4039),(10,4042),(10,4043),(10,4050),(10,4051),(10,4074),(10,4075),(10,4143),(11,4017),(11,4018),(11,4023),(11,4044),(12,4006),(12,4015),(12,4021),(12,4028),(12,4029),(12,4033),(12,4079),(12,4096),(12,4152),(12,4191),(13,4010),(13,4022),(13,4045),(13,4047),(13,4091),(13,4092),(13,4094),(13,4101),(13,4108),(13,4109),(13,4110),(13,4112),(13,4114),(13,4115),(13,4116),(13,4119),(13,4120),(13,4122),(13,4125),(13,4128),(13,4129),(13,4137),(13,4139),(13,4145),(13,4146),(13,4148),(13,4150); + + +DROP TABLE IF EXISTS `proximities`; +CREATE TABLE `proximities` ( + `zoneid` int(10) unsigned NOT NULL, + `exploreid` int(10) unsigned NOT NULL, + `minx` float(14,6) NOT NULL, + `maxx` float(14,6) NOT NULL, + `miny` float(14,6) NOT NULL, + `maxy` float(14,6) NOT NULL, + `minz` float(14,6) NOT NULL, + `maxz` float(14,6) NOT NULL, + PRIMARY KEY (`zoneid`,`exploreid`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + + +DROP TABLE IF EXISTS `tasksets`; +CREATE TABLE `tasksets` ( + `id` int(11) unsigned NOT NULL, + `taskid` int(11) unsigned NOT NULL, + PRIMARY KEY (`id`,`taskid`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +INSERT INTO `tasksets` VALUES (1,7),(1,8),(1,9),(1,10),(1,11); + +drop table if exists `keyring`; +create table `keyring`( +`char_id` integer not null, +`item_id` integer not null +) engine=MyISAM DEFAULT CHARSET=latin1; + +insert into rule_values values (1,'TaskSystem:EnableTaskSystem','true'); +insert into rule_values values (1,'TaskSystem:PeriodicCheckTimer',5); +insert into rule_values values (1,'TaskSystem:RecordCompletedTasks','true'); +insert into rule_values values (1,'TaskSystem:RecordCompletedOptionalActivities','true'); +insert into rule_values values (1,'TaskSystem:KeepOneRecordPerCompletedTask','true'); +insert into rule_values values (1,'TaskSystem:EnableTaskProximity','true'); +insert into rule_values values (1,'World:EnableTutorialButton','true'); +insert into rule_values values (1,'World:EnableReturnHomeButton','true'); +insert into rule_values values (1,'World:MaxLevelForTutorial','10'); +insert into rule_values values (1,'World:MinOfflineTimeToReturnHome','21600'); +Insert into rule_values values (1, 'World:AddMaxClientsPerIP', -1); +Insert into rule_values values (1, 'World:AddMaxClientsStatus', -1); +INSERT INTO `activities` VALUES ('12', '0', '0', '2', 'giant thicket rats', '', '', '14', '1', '10', '0', '33', '0'); +INSERT INTO `activities` VALUES ('12', '1', '0', '3', 'any creature', 'rat whiskers', '', '13071', '0', '4', '0', '33', '0'); +INSERT INTO `activities` VALUES ('12', '2', '1', '4', 'Ace Slighthand', '', '', '0', '0', '1', '0', '19', '0'); +INSERT INTO `activities` VALUES ('12', '3', '2', '1', 'Swish Appletop', 'rat whiskers', '', '13071', '0', '4', '33145', '33', '0'); +INSERT INTO `goallists` VALUES ('14', '33027'); +INSERT INTO `goallists` VALUES ('14', '33039'); +INSERT INTO `goallists` VALUES ('14', '33055'); +INSERT INTO `tasks` VALUES ('12', '0', 'Extraordinary Rodents', '[1,2, Go to the Misty Thicket, kill 10 giant thicket rats and loot four rat whiskers.]', '', '0', '722', '100', '0', '19', '1'); +INSERT INTO variables VALUES ('curInstFlagNum', 2000, 'Determines what instance flag will be handed out next', '2008-09-05 04:46:47'); +ALTER TABLE `zone` ADD column `insttype` tinyint (1) zerofill unsigned NOT NULL default '0'; +ALTER table character_ ADD column `instZflagNum` int(10) unsigned NOT NULL default '0'; +ALTER table character_ ADD column `instZOrgID` int(11) NOT NULL default '0'; +INSERT INTO variables VALUES ('dfltInstZflag',1000, 'Used to determine if a zone is instanced, must be 1000 or greater', '2008-09-05 04:46:47'); + +ALTER TABLE `spawngroup` ADD `dist` FLOAT NOT NULL DEFAULT '0.0', +ADD `max_x` FLOAT NOT NULL DEFAULT '0.0', +ADD `min_x` FLOAT NOT NULL DEFAULT '0.0', +ADD `max_y` FLOAT NOT NULL DEFAULT '0.0', +ADD `min_y` FLOAT NOT NULL DEFAULT '0.0', +ADD `delay` INT NOT NULL DEFAULT '0'; + +CREATE TABLE `raid_details` ( + `raidid` int(4) NOT NULL, + `loottype` int(4) NOT NULL, + `locked` tinyint(1) NOT NULL, + PRIMARY KEY (`raidid`) +); + +CREATE TABLE `raid_members` ( + `raidid` int(4) NOT NULL, + `charid` int(4) NOT NULL, + `groupid` int(4) NOT NULL, + `_class` tinyint(4) NOT NULL, + `level` tinyint(4) NOT NULL, + `name` varchar(64) NOT NULL, + `isgroupleader` tinyint(1) NOT NULL, + `israidleader` tinyint(1) NOT NULL, + `islooter` tinyint(1) NOT NULL, + PRIMARY KEY (`charid`) +); diff --git a/utils/sql/svn/2004_charges_alt_currency.sql b/utils/sql/svn/2004_charges_alt_currency.sql new file mode 100644 index 000000000..dd278c841 --- /dev/null +++ b/utils/sql/svn/2004_charges_alt_currency.sql @@ -0,0 +1,21 @@ +CREATE TABLE `alternate_currency` ( + `id` INT(10) NOT NULL, + `item_id` INT(10) NOT NULL, + PRIMARY KEY (`id`) +); + +CREATE TABLE `character_alt_currency` ( + `char_id` INT(10) UNSIGNED NOT NULL, + `currency_id` INT(10) UNSIGNED NOT NULL, + `amount` INT(10) UNSIGNED NOT NULL, + PRIMARY KEY (`char_id`, `currency_id`) +) +ENGINE=InnoDB; + +ALTER TABLE `merchantlist` ADD COLUMN `faction_required` SMALLINT NOT NULL DEFAULT '-100' AFTER `item`, ADD COLUMN `level_required` TINYINT UNSIGNED NOT NULL DEFAULT '0' AFTER `faction_required`, ADD COLUMN `alt_currency_cost` SMALLINT UNSIGNED NOT NULL DEFAULT '0' AFTER `level_required`; +ALTER TABLE `npc_types` ADD COLUMN `alt_currency_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `merchant_id`; +ALTER TABLE `lootdrop_entries` CHANGE COLUMN `item_charges` `item_charges` SMALLINT(2) UNSIGNED NOT NULL DEFAULT '1' AFTER `item_id`; +ALTER TABLE `inventory` CHANGE COLUMN `charges` `charges` SMALLINT(3) UNSIGNED NULL DEFAULT '0' AFTER `itemid`; +ALTER TABLE `sharedbank` CHANGE COLUMN `charges` `charges` SMALLINT(3) UNSIGNED NULL DEFAULT '0' AFTER `itemid`; +ALTER TABLE `object` CHANGE COLUMN `charges` `charges` SMALLINT(3) UNSIGNED NOT NULL DEFAULT '0' AFTER `itemid`; +ALTER TABLE `object_contents` CHANGE COLUMN `charges` `charges` SMALLINT(3) NOT NULL DEFAULT '0' AFTER `itemid`; diff --git a/utils/sql/svn/2015_optional_specialization_training_rule.sql b/utils/sql/svn/2015_optional_specialization_training_rule.sql new file mode 100644 index 000000000..bf23fe22a --- /dev/null +++ b/utils/sql/svn/2015_optional_specialization_training_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Skills:MaxTrainSpecializations', '50', 'Max level a GM trainer will train casting specializations'); diff --git a/utils/sql/svn/2016_optional_rule_bot_aa_expansion.sql b/utils/sql/svn/2016_optional_rule_bot_aa_expansion.sql new file mode 100644 index 000000000..8dc6dd301 --- /dev/null +++ b/utils/sql/svn/2016_optional_rule_bot_aa_expansion.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Bots:BotAAExpansion', '8', 'The expansion through which bots will obtain AAs'); diff --git a/utils/sql/svn/2023_optional_mysqlcli.sql b/utils/sql/svn/2023_optional_mysqlcli.sql new file mode 100644 index 000000000..182e0901e --- /dev/null +++ b/utils/sql/svn/2023_optional_mysqlcli.sql @@ -0,0 +1 @@ +INSERT INTO commands VALUES ('mysql', 250, 'Mysql CLI, see \'help\' for options.'); diff --git a/utils/sql/svn/2024_optional_update_crystals.sql b/utils/sql/svn/2024_optional_update_crystals.sql new file mode 100644 index 000000000..91d72e9f4 --- /dev/null +++ b/utils/sql/svn/2024_optional_update_crystals.sql @@ -0,0 +1,2 @@ +UPDATE `items` SET `stackable`='1' WHERE `id`='40902'; +UPDATE `items` SET `stackable`='1' WHERE `id`='40903'; diff --git a/utils/sql/svn/2024_required_update.sql b/utils/sql/svn/2024_required_update.sql new file mode 100644 index 000000000..fb79dd27b --- /dev/null +++ b/utils/sql/svn/2024_required_update.sql @@ -0,0 +1,804 @@ +CREATE TABLE `char_create_combinations` ( + `allocation_id` INT(10) UNSIGNED NOT NULL, + `race` INT(10) UNSIGNED NOT NULL, + `class` INT(10) UNSIGNED NOT NULL, + `deity` INT(10) UNSIGNED NOT NULL, + `start_zone` INT(10) UNSIGNED NOT NULL, + `expansions_req` INT(10) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`race`, `class`, `deity`, `start_zone`) +); + +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '1', '201', '58', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '1', '204', '58', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '1', '207', '58', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '1', '206', '58', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '1', '211', '58', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '1', '396', '58', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '1', '212', '58', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '1', '208', '58', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '1', '396', '58', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '1', '201', '58', '45'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '1', '207', '58', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '1', '206', '58', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '1', '212', '58', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '1', '211', '58', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '1', '208', '58', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '1', '204', '58', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '1', '396', '58', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '1', '211', '58', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '2', '201', '48', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '2', '207', '48', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '2', '208', '48', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '2', '212', '48', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '2', '206', '48', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '2', '204', '48', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '2', '201', '48', '45'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '2', '206', '48', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '2', '207', '48', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '2', '204', '48', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '2', '212', '48', '2'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '2', '208', '48', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '3', '204', '54', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '3', '207', '54', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '3', '208', '54', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '3', '212', '54', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '3', '207', '54', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '3', '212', '54', '2'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '3', '208', '54', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '3', '204', '54', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '4', '207', '55', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '4', '215', '55', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '4', '207', '55', '3'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '4', '215', '55', '3'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '5', '201', '57', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '5', '206', '57', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '5', '201', '57', '45'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '5', '206', '57', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '6', '207', '49', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '6', '215', '49', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '6', '207', '49', '3'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '6', '215', '49', '3'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '7', '210', '52', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '7', '396', '52', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '7', '396', '52', '2'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '7', '210', '52', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '8', '202', '47', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '8', '207', '47', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '8', '209', '47', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '8', '211', '47', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '8', '213', '47', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '8', '215', '47', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '8', '396', '47', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '8', '216', '47', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '8', '214', '47', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '8', '212', '47', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '8', '210', '47', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '8', '208', '47', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '8', '204', '47', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '8', '205', '47', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '396', '47', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '205', '47', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '204', '47', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '207', '47', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '212', '47', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '209', '47', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '210', '47', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '213', '47', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '202', '47', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '211', '47', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '216', '47', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '215', '47', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '214', '47', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '208', '47', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '396', '47', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '209', '47', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '213', '47', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '211', '47', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '216', '47', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '215', '47', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '214', '47', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '202', '47', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '210', '47', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '8', '205', '47', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '9', '201', '56', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '9', '206', '56', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '9', '207', '56', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '9', '212', '56', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '9', '396', '56', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '9', '205', '56', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '9', '204', '56', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '9', '396', '56', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '9', '212', '56', '2'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '9', '204', '56', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '9', '207', '56', '2'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '9', '201', '56', '2'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '9', '206', '56', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '9', '205', '56', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '9', '396', '56', '2'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '9', '205', '56', '2'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '11', '201', '53', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '11', '206', '53', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '11', '201', '53', '45'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '11', '206', '53', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '12', '201', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '12', '204', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '12', '207', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '12', '206', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '12', '208', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '12', '213', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '12', '396', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '12', '212', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '12', '396', '51', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '12', '213', '51', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '12', '206', '51', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '12', '207', '51', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '12', '204', '51', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '12', '212', '51', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '12', '208', '51', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '12', '201', '51', '45'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '12', '396', '51', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '12', '213', '51', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '13', '201', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '13', '207', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '13', '208', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '13', '212', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '13', '396', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '13', '206', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '13', '204', '51', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '13', '396', '51', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '13', '212', '51', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '13', '208', '51', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '13', '201', '51', '45'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '13', '206', '51', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '13', '207', '51', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '13', '204', '51', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '13', '396', '51', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '14', '201', '50', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '14', '206', '50', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '14', '208', '50', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '14', '212', '50', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '14', '396', '50', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '14', '207', '50', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '1', '14', '204', '50', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '14', '396', '50', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '14', '208', '50', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '14', '212', '50', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '14', '204', '50', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '14', '201', '50', '45'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '14', '206', '50', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '14', '207', '50', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '1', '14', '396', '50', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '2', '1', '211', '4', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '2', '1', '214', '4', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '2', '1', '396', '4', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '2', '1', '396', '4', '29'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '2', '1', '214', '4', '29'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '2', '1', '211', '4', '29'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '2', '9', '205', '2', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '2', '9', '396', '2', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '2', '9', '214', '2', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '2', '9', '396', '2', '29'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '2', '9', '205', '2', '29'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '2', '9', '214', '2', '29'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '2', '10', '214', '3', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '2', '10', '214', '3', '29'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2052', '2', '15', '208', '0', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2052', '2', '15', '214', '0', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('4', '2', '15', '208', '0', '29'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('4', '2', '15', '214', '0', '29'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2112', '2', '16', '211', '1', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2112', '2', '16', '396', '1', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2112', '2', '16', '214', '1', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('64', '2', '16', '396', '1', '29'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('64', '2', '16', '211', '1', '29'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('64', '2', '16', '214', '1', '29'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '2', '203', '17', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '2', '210', '17', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '2', '209', '17', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '2', '203', '17', '75'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '2', '210', '17', '24'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '2', '209', '17', '24'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '3', '209', '21', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '3', '210', '21', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '3', '209', '21', '24'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '3', '210', '21', '24'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '5', '203', '22', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '5', '203', '22', '75'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '11', '203', '20', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '11', '203', '20', '75'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '12', '209', '19', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '12', '213', '19', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '12', '396', '19', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '12', '210', '19', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '12', '396', '19', '23'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '12', '209', '19', '23'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '12', '213', '19', '23'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '12', '210', '19', '23'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '13', '209', '19', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '13', '210', '19', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '13', '396', '19', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '13', '396', '19', '23'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '13', '209', '19', '23'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '13', '210', '19', '23'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '14', '209', '18', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '14', '210', '18', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '3', '14', '396', '18', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '14', '396', '18', '23'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '14', '210', '18', '23'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '3', '14', '209', '18', '23'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '1', '207', '85', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '1', '211', '85', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '1', '215', '85', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '1', '396', '85', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '1', '396', '85', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '1', '207', '85', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '1', '211', '85', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '1', '215', '85', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '4', '215', '83', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '4', '215', '83', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '6', '215', '82', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '6', '215', '82', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '8', '202', '81', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '8', '209', '81', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '8', '396', '81', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '8', '216', '81', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '8', '215', '81', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '8', '214', '81', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '8', '213', '81', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '8', '212', '81', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '8', '211', '81', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '8', '210', '81', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '8', '208', '81', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '8', '204', '81', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '8', '207', '81', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '8', '205', '81', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '8', '396', '81', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '8', '205', '81', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '8', '207', '81', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '8', '208', '81', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '8', '211', '81', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '8', '216', '81', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '8', '215', '81', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '8', '214', '81', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '8', '202', '81', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '8', '213', '81', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '8', '210', '81', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '8', '209', '81', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '8', '212', '81', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '8', '204', '81', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '9', '205', '84', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '9', '215', '84', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '9', '207', '84', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '4', '9', '396', '84', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '9', '396', '84', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '9', '215', '84', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '9', '207', '84', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '4', '9', '205', '84', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '2', '215', '43', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '2', '215', '43', '61'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '3', '215', '46', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '3', '215', '46', '61'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '12', '204', '45', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '12', '208', '45', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '12', '215', '45', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '12', '396', '45', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '12', '213', '45', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '12', '207', '45', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '12', '396', '45', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '12', '207', '45', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '12', '204', '45', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '12', '213', '45', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '12', '215', '45', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '12', '208', '45', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '13', '204', '45', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '13', '207', '45', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '13', '208', '45', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '13', '396', '45', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '13', '215', '45', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '13', '396', '45', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '13', '207', '45', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '13', '208', '45', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '13', '215', '45', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '13', '204', '45', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '14', '204', '44', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '14', '215', '44', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '14', '396', '44', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '14', '208', '44', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '5', '14', '207', '44', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '14', '396', '44', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '14', '207', '44', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '14', '204', '44', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '14', '215', '44', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '5', '14', '208', '44', '62'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '1', '206', '11', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '1', '211', '11', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '1', '396', '11', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '1', '396', '11', '41'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '1', '211', '11', '41'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '1', '206', '11', '41'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '2', '206', '5', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '2', '206', '5', '42'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '5', '206', '10', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '5', '206', '10', '42'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '9', '205', '9', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '9', '206', '9', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '9', '396', '9', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '9', '396', '9', '42'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '9', '205', '9', '42'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '9', '206', '9', '42'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '11', '206', '8', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '11', '206', '8', '42'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '12', '206', '7', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '12', '213', '7', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '12', '396', '7', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '12', '396', '7', '41'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '12', '206', '7', '41'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '12', '213', '7', '41'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '13', '206', '7', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '13', '396', '7', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '13', '396', '7', '41'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '13', '206', '7', '41'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '14', '206', '6', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '6', '14', '396', '6', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '14', '396', '6', '41'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '6', '14', '206', '6', '41'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '1', '201', '36', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '1', '212', '36', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '1', '396', '36', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '1', '215', '36', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '1', '214', '36', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '1', '211', '36', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '1', '204', '36', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '1', '207', '36', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '1', '208', '36', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '1', '209', '36', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '1', '206', '36', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '396', '36', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '208', '36', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '211', '36', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '215', '36', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '214', '36', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '209', '36', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '212', '36', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '204', '36', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '201', '36', '45'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '207', '36', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '206', '36', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '396', '36', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '209', '36', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '211', '36', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '214', '36', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '396', '36', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '211', '36', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '214', '36', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '1', '209', '36', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '3', '204', '33', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '3', '208', '33', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '3', '212', '33', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '3', '215', '33', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '3', '207', '33', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '3', '207', '33', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '3', '215', '33', '61'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '3', '212', '33', '2'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '3', '204', '33', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '3', '208', '33', '9'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '4', '207', '34', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '4', '215', '34', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '4', '207', '34', '3'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '4', '215', '34', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '4', '215', '34', '3'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '6', '207', '32', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '6', '215', '32', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '6', '207', '32', '3'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '6', '215', '32', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '6', '215', '32', '3'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '8', '202', '31', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '8', '210', '31', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '8', '212', '31', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '8', '396', '31', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '8', '216', '31', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '8', '215', '31', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '8', '214', '31', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '8', '213', '31', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '8', '211', '31', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '8', '209', '31', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '8', '204', '31', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '8', '205', '31', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '8', '208', '31', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '8', '207', '31', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '396', '31', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '212', '31', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '214', '31', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '202', '31', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '213', '31', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '210', '31', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '209', '31', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '205', '31', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '207', '31', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '204', '31', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '208', '31', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '211', '31', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '216', '31', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '215', '31', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '396', '31', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '209', '31', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '213', '31', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '214', '31', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '211', '31', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '216', '31', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '215', '31', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '202', '31', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '210', '31', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '205', '31', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '396', '31', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '211', '31', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '216', '31', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '215', '31', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '214', '31', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '202', '31', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '213', '31', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '210', '31', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '209', '31', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '8', '205', '31', '1'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '9', '201', '35', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '9', '205', '35', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '9', '204', '35', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '9', '212', '35', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '9', '396', '35', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '9', '215', '35', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '7', '9', '207', '35', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '9', '396', '35', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '9', '205', '35', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '9', '201', '35', '2'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '9', '207', '35', '2'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '9', '212', '35', '2'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '9', '215', '35', '54'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '9', '204', '35', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '9', '396', '35', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '9', '205', '35', '10'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '9', '396', '35', '2'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '7', '9', '205', '35', '2'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '8', '1', '202', '16', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '8', '1', '396', '16', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '8', '1', '396', '16', '60'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '8', '1', '202', '16', '60'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '8', '2', '202', '13', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '8', '2', '202', '13', '67'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '8', '3', '202', '14', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '8', '3', '202', '14', '67'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '8', '9', '202', '15', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '8', '9', '396', '15', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '8', '9', '205', '15', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '8', '9', '396', '15', '67'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '8', '9', '205', '15', '67'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '8', '9', '202', '15', '67'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2112', '8', '16', '202', '12', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2112', '8', '16', '205', '12', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2112', '8', '16', '396', '12', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('64', '8', '16', '396', '12', '67'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('64', '8', '16', '205', '12', '67'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('64', '8', '16', '202', '12', '67'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '9', '1', '203', '74', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '9', '1', '396', '74', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '9', '1', '211', '74', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '9', '1', '206', '74', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '9', '1', '396', '74', '52'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '9', '1', '206', '74', '52'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '9', '1', '211', '74', '52'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '9', '1', '203', '74', '52'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '9', '5', '203', '72', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '9', '5', '206', '72', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '9', '5', '203', '72', '52'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '9', '5', '206', '72', '52'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '9', '10', '203', '73', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '9', '10', '206', '73', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '9', '10', '203', '73', '52'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '9', '10', '206', '73', '52'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2052', '9', '15', '203', '70', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2052', '9', '15', '206', '70', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('4', '9', '15', '203', '70', '52'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('4', '9', '15', '206', '70', '52'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2112', '9', '16', '203', '71', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2112', '9', '16', '211', '71', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2112', '9', '16', '396', '71', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2112', '9', '16', '206', '71', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('64', '9', '16', '396', '71', '52'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('64', '9', '16', '211', '71', '52'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('64', '9', '16', '203', '71', '52'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('64', '9', '16', '206', '71', '52'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '10', '1', '203', '107', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '10', '1', '396', '107', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '10', '1', '211', '107', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '10', '1', '396', '107', '49'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '10', '1', '203', '107', '49'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '10', '1', '211', '107', '49'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '10', '5', '203', '67', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '10', '5', '211', '67', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '10', '5', '203', '67', '49'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '10', '5', '211', '67', '49'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '10', '10', '211', '68', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '10', '10', '211', '68', '49'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2052', '10', '15', '211', '65', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('4', '10', '15', '211', '65', '49'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2112', '10', '16', '203', '108', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2112', '10', '16', '396', '108', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2112', '10', '16', '211', '108', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('64', '10', '16', '396', '108', '49'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('64', '10', '16', '211', '108', '49'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('64', '10', '16', '203', '108', '49'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '11', '1', '202', '42', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '11', '1', '396', '42', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '11', '1', '211', '42', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '11', '1', '396', '42', '19'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '11', '1', '211', '42', '19'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '11', '1', '202', '42', '19'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '11', '2', '205', '37', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '11', '2', '205', '37', '19'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '11', '3', '207', '39', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '11', '3', '207', '39', '19'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '11', '4', '207', '40', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '11', '4', '207', '40', '19'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '11', '6', '207', '38', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '11', '6', '207', '38', '19'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '11', '9', '202', '41', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '11', '9', '205', '41', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '11', '9', '396', '41', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '11', '9', '396', '41', '19'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '11', '9', '205', '41', '19'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '11', '9', '202', '41', '19'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '1', '201', '30', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '1', '396', '30', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '1', '202', '30', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '1', '211', '30', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '1', '396', '30', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '1', '211', '30', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '1', '202', '30', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '1', '201', '30', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '2', '201', '23', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '2', '205', '23', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '2', '202', '23', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '2', '201', '23', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '2', '202', '23', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '2', '205', '23', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '3', '202', '27', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '3', '202', '27', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '5', '201', '29', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '5', '201', '29', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '9', '201', '28', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '9', '396', '28', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '9', '202', '28', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '9', '205', '28', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '9', '396', '28', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '9', '201', '28', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '9', '202', '28', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '9', '205', '28', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '11', '201', '26', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '11', '201', '26', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '12', '201', '25', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '12', '202', '25', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '12', '213', '25', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '12', '396', '25', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '12', '396', '25', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '12', '201', '25', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '12', '202', '25', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '12', '213', '25', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '13', '201', '25', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '13', '396', '25', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '13', '202', '25', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '13', '396', '25', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '13', '202', '25', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '13', '201', '25', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '14', '201', '24', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '14', '396', '24', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '12', '14', '202', '24', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '14', '396', '24', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '14', '201', '24', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('0', '12', '14', '202', '24', '55'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2059', '128', '1', '203', '64', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('1', '128', '1', '203', '64', '106'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2059', '128', '5', '203', '62', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('1', '128', '5', '203', '62', '106'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2059', '128', '7', '203', '60', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('1', '128', '7', '203', '60', '106'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2059', '128', '10', '203', '63', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('1', '128', '10', '203', '63', '106'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2059', '128', '11', '203', '61', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('1', '128', '11', '203', '61', '82'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2053', '128', '15', '203', '59', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('5', '128', '15', '203', '59', '106'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2052', '130', '1', '396', '80', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('4', '130', '1', '396', '80', '155'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2052', '130', '8', '396', '75', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('4', '130', '8', '396', '75', '155'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2052', '130', '9', '396', '78', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('4', '130', '9', '396', '78', '155'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2052', '130', '10', '396', '79', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('4', '130', '10', '396', '79', '155'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2052', '130', '15', '396', '76', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('4', '130', '15', '396', '76', '155'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2116', '130', '16', '396', '77', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('68', '130', '16', '396', '77', '155'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2064', '330', '1', '208', '92', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('16', '330', '1', '208', '92', '50'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2064', '330', '2', '206', '86', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2064', '330', '2', '208', '86', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('16', '330', '2', '206', '86', '50'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('16', '330', '2', '208', '86', '50'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2064', '330', '3', '208', '88', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('16', '330', '3', '208', '88', '50'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2064', '330', '5', '206', '90', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('16', '330', '5', '206', '90', '50'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2064', '330', '9', '206', '89', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('16', '330', '9', '206', '89', '50'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2064', '330', '10', '208', '91', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('16', '330', '10', '208', '91', '50'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2064', '330', '11', '206', '87', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('16', '330', '11', '206', '87', '50'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2064', '330', '12', '208', '93', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('16', '330', '12', '208', '93', '50'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '522', '1', '216', '94', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '522', '2', '216', '95', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '522', '3', '216', '96', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '522', '4', '216', '97', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '522', '5', '216', '98', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '522', '6', '216', '99', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '522', '7', '216', '100', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '522', '8', '216', '101', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '522', '9', '216', '102', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '522', '11', '216', '103', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '522', '12', '216', '104', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '522', '13', '216', '105', '394'); +INSERT INTO `char_create_combinations` (`expansions_req`, `race`, `class`, `deity`, `allocation_id`, `start_zone`) VALUES('2048', '522', '14', '216', '106', '394'); + + + +CREATE TABLE `char_create_point_allocations` ( + `id` INT(10) UNSIGNED NOT NULL, + `base_str` INT(10) UNSIGNED NOT NULL, + `base_sta` INT(10) UNSIGNED NOT NULL, + `base_dex` INT(10) UNSIGNED NOT NULL, + `base_agi` INT(10) UNSIGNED NOT NULL, + `base_int` INT(10) UNSIGNED NOT NULL, + `base_wis` INT(10) UNSIGNED NOT NULL, + `base_cha` INT(10) UNSIGNED NOT NULL, + `alloc_str` INT(10) UNSIGNED NOT NULL, + `alloc_sta` INT(10) UNSIGNED NOT NULL, + `alloc_dex` INT(10) UNSIGNED NOT NULL, + `alloc_agi` INT(10) UNSIGNED NOT NULL, + `alloc_int` INT(10) UNSIGNED NOT NULL, + `alloc_wis` INT(10) UNSIGNED NOT NULL, + `alloc_cha` INT(10) UNSIGNED NOT NULL, + PRIMARY KEY (`id`) +); + +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('0', '103', '70', '87', '105', '60', '80', '60', '0', '5', '5', '5', '0', '5', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('1', '113', '80', '82', '100', '60', '70', '55', '25', '0', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('2', '103', '80', '92', '95', '60', '70', '55', '25', '5', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('3', '103', '70', '82', '100', '60', '80', '60', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('4', '113', '70', '87', '105', '60', '70', '55', '0', '0', '0', '25', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('5', '65', '75', '90', '70', '99', '93', '60', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('6', '60', '75', '90', '65', '109', '83', '70', '0', '0', '0', '0', '5', '0', '25'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('7', '60', '75', '90', '75', '109', '83', '60', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('8', '60', '85', '90', '65', '109', '83', '60', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('9', '60', '85', '100', '65', '99', '83', '60', '25', '5', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('10', '70', '75', '90', '70', '109', '83', '65', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('11', '70', '75', '95', '75', '99', '83', '60', '0', '0', '0', '25', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('12', '100', '100', '70', '95', '60', '83', '45', '25', '0', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('13', '95', '90', '70', '95', '60', '93', '45', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('14', '100', '90', '70', '95', '60', '88', '55', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('15', '90', '100', '80', '90', '60', '83', '45', '25', '5', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('16', '100', '90', '75', '100', '60', '83', '45', '0', '0', '0', '25', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('17', '65', '70', '70', '75', '107', '93', '70', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('18', '60', '70', '70', '70', '117', '83', '80', '0', '0', '0', '0', '5', '0', '25'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('19', '60', '70', '70', '80', '117', '83', '70', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('20', '60', '80', '70', '70', '117', '83', '70', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('21', '70', '70', '70', '75', '107', '88', '80', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('22', '70', '70', '70', '75', '117', '83', '75', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('23', '65', '85', '85', '75', '98', '77', '60', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('24', '60', '85', '85', '70', '108', '67', '70', '0', '0', '0', '0', '5', '0', '25'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('25', '60', '85', '85', '80', '108', '67', '60', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('26', '60', '95', '85', '70', '108', '67', '60', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('27', '70', '85', '85', '75', '98', '72', '70', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('28', '60', '95', '95', '70', '98', '67', '60', '25', '5', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('29', '70', '85', '85', '75', '108', '67', '65', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('30', '70', '85', '90', '80', '98', '67', '60', '0', '0', '0', '25', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('31', '75', '95', '90', '70', '75', '60', '85', '0', '0', '0', '0', '0', '0', '25'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('32', '70', '85', '90', '80', '75', '70', '75', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('33', '80', '85', '90', '75', '75', '65', '85', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('34', '75', '85', '100', '80', '75', '65', '75', '0', '20', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('35', '70', '95', '100', '70', '75', '60', '75', '25', '5', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('36', '80', '85', '95', '80', '75', '60', '75', '0', '0', '0', '25', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('37', '75', '90', '95', '80', '67', '90', '50', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('38', '70', '90', '95', '85', '67', '90', '50', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('39', '80', '90', '95', '80', '67', '85', '60', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('40', '75', '90', '105', '85', '67', '85', '50', '0', '20', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('41', '70', '100', '105', '75', '67', '80', '50', '25', '5', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('42', '80', '90', '100', '85', '67', '80', '50', '0', '0', '0', '25', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('43', '60', '70', '85', '70', '92', '105', '80', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('44', '55', '70', '85', '65', '102', '95', '90', '0', '0', '0', '0', '5', '0', '25'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('45', '55', '70', '85', '75', '102', '95', '80', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('46', '65', '70', '85', '70', '92', '100', '90', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('47', '80', '85', '75', '75', '75', '75', '85', '0', '0', '0', '0', '0', '0', '25'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('48', '80', '75', '75', '80', '75', '85', '75', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('49', '75', '75', '75', '85', '75', '85', '75', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('50', '75', '75', '75', '75', '85', '75', '85', '0', '0', '0', '0', '5', '0', '25'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('51', '75', '75', '75', '85', '85', '75', '75', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('52', '80', '85', '85', '80', '75', '75', '75', '0', '0', '20', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('53', '75', '85', '75', '75', '85', '75', '75', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('54', '85', '75', '75', '80', '75', '80', '85', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('55', '80', '75', '85', '85', '75', '80', '75', '0', '20', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('56', '75', '85', '85', '75', '75', '75', '75', '25', '5', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('57', '85', '75', '75', '80', '85', '75', '80', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('58', '85', '75', '80', '85', '75', '75', '75', '0', '0', '0', '25', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('59', '70', '85', '95', '80', '75', '90', '60', '0', '5', '5', '5', '0', '5', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('60', '75', '95', '100', '75', '75', '80', '55', '0', '0', '20', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('61', '70', '95', '90', '70', '85', '80', '55', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('62', '80', '85', '90', '75', '85', '80', '60', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('63', '70', '85', '90', '75', '75', '90', '60', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('64', '80', '85', '95', '80', '75', '80', '55', '0', '0', '0', '25', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('65', '130', '70', '75', '132', '60', '77', '42', '0', '5', '5', '5', '0', '5', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('66', '140', '80', '70', '127', '60', '67', '37', '25', '0', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('67', '140', '70', '70', '127', '70', '67', '42', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('68', '130', '70', '70', '127', '60', '77', '42', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('69', '140', '70', '75', '132', '60', '67', '37', '0', '0', '0', '25', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('70', '108', '75', '88', '119', '52', '70', '45', '0', '5', '5', '5', '0', '5', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('71', '118', '85', '83', '114', '52', '60', '40', '25', '0', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('72', '118', '75', '83', '114', '62', '60', '45', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('73', '108', '75', '83', '114', '52', '70', '45', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('74', '118', '75', '88', '119', '52', '60', '40', '0', '0', '0', '25', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('75', '95', '80', '90', '75', '65', '70', '75', '0', '0', '0', '0', '0', '0', '25'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('76', '90', '70', '95', '85', '65', '80', '70', '0', '5', '5', '5', '0', '5', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('77', '100', '80', '90', '80', '65', '70', '65', '25', '0', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('78', '90', '80', '100', '75', '65', '70', '65', '25', '5', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('79', '90', '70', '90', '80', '65', '80', '70', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('80', '100', '70', '95', '85', '65', '70', '65', '0', '0', '0', '25', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('81', '70', '90', '95', '65', '75', '80', '85', '0', '0', '0', '0', '0', '0', '25'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('82', '65', '80', '95', '75', '75', '90', '75', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('83', '70', '80', '105', '75', '75', '85', '75', '0', '20', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('84', '65', '90', '105', '65', '75', '80', '75', '25', '5', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('85', '75', '80', '100', '75', '75', '80', '75', '0', '0', '0', '25', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('86', '75', '100', '100', '85', '75', '85', '50', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('87', '70', '110', '100', '80', '85', '75', '50', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('88', '80', '100', '100', '85', '75', '80', '60', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('89', '70', '110', '110', '80', '75', '75', '50', '25', '5', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('90', '80', '100', '100', '85', '85', '75', '55', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('91', '70', '100', '100', '85', '75', '85', '55', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('92', '80', '100', '105', '90', '75', '75', '50', '0', '0', '0', '25', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('93', '70', '100', '100', '90', '85', '75', '50', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('94', '80', '75', '90', '90', '85', '80', '75', '0', '0', '0', '25', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('95', '75', '75', '85', '85', '85', '90', '75', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('96', '80', '75', '85', '85', '85', '85', '85', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('97', '75', '75', '95', '90', '85', '85', '75', '0', '20', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('98', '80', '75', '85', '85', '95', '80', '80', '0', '0', '0', '20', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('99', '70', '75', '85', '90', '85', '90', '75', '0', '0', '0', '5', '0', '25', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('100', '75', '85', '95', '85', '85', '80', '75', '0', '0', '20', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('101', '75', '85', '85', '80', '85', '80', '85', '0', '0', '0', '0', '0', '0', '25'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('102', '70', '85', '95', '80', '85', '80', '75', '25', '5', '0', '0', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('103', '70', '85', '85', '80', '95', '80', '75', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('104', '70', '75', '85', '90', '95', '80', '75', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('105', '70', '75', '85', '90', '95', '80', '75', '0', '0', '0', '5', '25', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('106', '70', '75', '85', '80', '95', '80', '85', '0', '0', '0', '0', '5', '0', '25'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('107', '140', '70', '75', '132', '60', '67', '37', '7', '0', '0', '18', '0', '0', '0'); +INSERT INTO `char_create_point_allocations` (`id`, `base_str`, `base_dex`, `base_agi`, `base_sta`, `base_int`, `base_wis`, `base_cha`, `alloc_str`, `alloc_dex`, `alloc_agi`, `alloc_sta`, `alloc_int`, `alloc_wis`, `alloc_cha`) VALUES('108', '140', '80', '70', '127', '60', '67', '37', '10', '0', '0', '15', '0', '0', '0'); + + +CREATE TABLE `character_buffs` ( + `character_id` INT(10) UNSIGNED NOT NULL, + `slot_id` TINYINT(3) UNSIGNED NOT NULL, + `spell_id` SMALLINT(10) UNSIGNED NOT NULL, + `caster_level` TINYINT(3) UNSIGNED NOT NULL, + `caster_name` VARCHAR(64) NOT NULL, + `ticsremaining` INT(10) UNSIGNED NOT NULL, + `counters` INT(10) UNSIGNED NOT NULL, + `numhits` INT(10) UNSIGNED NOT NULL, + `melee_rune` INT(10) UNSIGNED NOT NULL, + `magic_rune` INT(10) UNSIGNED NOT NULL, + `persistent` TINYINT(3) UNSIGNED NOT NULL, + `death_save_chance` INT(10) UNSIGNED NOT NULL, + `death_save_aa_chance` INT(10) UNSIGNED NOT NULL, + PRIMARY KEY (`character_id`, `slot_id`), + INDEX `character_id` (`character_id`) +); + +ALTER TABLE `inventory` ADD COLUMN `custom_data` TEXT NULL AFTER `instnodrop`; +ALTER TABLE `sharedbank` ADD COLUMN `custom_data` TEXT NULL AFTER `augslot5`; \ No newline at end of file diff --git a/utils/sql/svn/2057_required_discovered_items.sql b/utils/sql/svn/2057_required_discovered_items.sql new file mode 100644 index 000000000..f69c86698 --- /dev/null +++ b/utils/sql/svn/2057_required_discovered_items.sql @@ -0,0 +1,6 @@ +CREATE TABLE `discovered_items` ( + `item_id` int(11) unsigned NOT NULL DEFAULT '0', + `char_name` varchar(64) NOT NULL DEFAULT '', + `discovered_date` int(11) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`item_id`) +); diff --git a/utils/sql/svn/2058_optional_rule_discovered_items.sql b/utils/sql/svn/2058_optional_rule_discovered_items.sql new file mode 100644 index 000000000..72fe01623 --- /dev/null +++ b/utils/sql/svn/2058_optional_rule_discovered_items.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:EnableDiscoveredItems', 'true', 'If enabled, it enables EVENT_DISCOVER_ITEM and also saves character names and timestamps'); diff --git a/utils/sql/svn/2062_required_version_changes.sql b/utils/sql/svn/2062_required_version_changes.sql new file mode 100644 index 000000000..9c7a309b7 --- /dev/null +++ b/utils/sql/svn/2062_required_version_changes.sql @@ -0,0 +1,7 @@ +ALTER TABLE `doors` +MODIFY COLUMN `version` smallint(5) NOT NULL DEFAULT 0 AFTER `zone`; +ALTER TABLE `object` +MODIFY COLUMN `version` smallint(5) NOT NULL DEFAULT 0 AFTER `zoneid`; +ALTER TABLE `ground_spawns` +MODIFY COLUMN `version` smallint(5) NOT NULL DEFAULT 0 AFTER `zoneid`; + diff --git a/utils/sql/svn/2069_required_pets.sql b/utils/sql/svn/2069_required_pets.sql new file mode 100644 index 000000000..b877cb5f2 --- /dev/null +++ b/utils/sql/svn/2069_required_pets.sql @@ -0,0 +1,57 @@ +ALTER TABLE `pets` DROP PRIMARY KEY; +ALTER TABLE `pets` ADD COLUMN `petpower` int NOT NULL default '0' AFTER `type`, ADD COLUMN `petcontrol` tinyint NOT NULL default '0', ADD COLUMN `petnaming` tinyint NOT NULL default '0', ADD COLUMN `monsterflag` tinyint NOT NULL default '0', ADD COLUMN `equipmentset` int NOT NULL default -1, ADD PRIMARY KEY (`type`, `petpower`); + +UPDATE `pets` SET `monsterflag` = 1 where `type` like 'MonsterSum%'; +UPDATE `pets` SET `petcontrol` = 2 where `type` like 'SumAir%' or `type` like 'SumEarth%' or `type` like 'SumFire%' or `type` like 'SumWater%' or `type` = 'SumMageMultiElement' or `type` like 'BLPet%' or `type` like 'skel_%' or `type` like 'MonsterSum%' or `type` = 'DruidPet' or `type` like 'SpiritWolf%' ; +UPDATE `pets` SET `petcontrol` = 1 where `type` like 'Animation%'; +UPDATE `pets` SET `petnaming` = 2 where `type` like 'BLPet%'; +UPDATE `pets` SET `petnaming` = 3 where `type` like 'SumAir%' or `type` like 'SumEarth%' or `type` like 'SumFire%' or `type` like 'SumWater%' or `type` = 'SumMageMultiElement' or `type` like 'Animation%' or `type` like 'skel_%' or `type` like 'MonsterSum%' or `type` = 'DruidPet' or `type` like 'SpiritWolf%'; +UPDATE `pets` SET `petnaming` = 1 where `type` rlike 'familiar'; + +/* Equipment sets can have a name to make it easier to manage them. */ +CREATE TABLE `pets_equipmentset` ( + `set_id` int NOT NULL PRIMARY KEY, + `setname` varchar(30) NOT NULL default '', + `nested_set` int NOT NULL default -1 +) ENGINE=MyISAM; + +CREATE TABLE `pets_equipmentset_entries` ( + `set_id` int NOT NULL, + `slot` int NOT NULL, + `item_id` int NOT NULL, + PRIMARY KEY (`set_id`, `slot`) +) ENGINE=MyISAM; + + +CREATE TABLE `character_pet_info` ( + `char_id` int NOT NULL, + `pet` int NOT NULL, + `petname` varchar(64) NOT NULL default '', + `petpower` int NOT NULL default 0, + `spell_id` int NOT NULL default 0, + `hp` int NOT NULL default 0, + `mana` int NOT NULL default 0, + PRIMARY KEY (`char_id`, `pet`) +) ENGINE=InnoDB; + +CREATE TABLE `character_pet_buffs` ( + `char_id` int NOT NULL, + `pet` int NOT NULL, + `slot` int NOT NULL, + `spell_id` int NOT NULL, + `caster_level` tinyint NOT NULL default 0, + `castername` varchar(64) NOT NULL default '', + `ticsremaining` int NOT NULL default 0, + `counters` int NOT NULL default 0, + `numhits` int NOT NULL default 0, + `rune` int NOT NULL default 0, + PRIMARY KEY (`char_id`, `pet`, `slot`) +) ENGINE=InnoDB; + +CREATE TABLE `character_pet_inventory` ( + `char_id` int NOT NULL, + `pet` int NOT NULL, + `slot` int NOT NULL, + `item_id` int NOT NULL, + PRIMARY KEY (`char_id`, `pet`, `slot`) +) ENGINE=InnoDB; \ No newline at end of file diff --git a/utils/sql/svn/2087_required_bots_hp_and_mana_and_spell_updates.sql b/utils/sql/svn/2087_required_bots_hp_and_mana_and_spell_updates.sql new file mode 100644 index 000000000..e58b1c82f --- /dev/null +++ b/utils/sql/svn/2087_required_bots_hp_and_mana_and_spell_updates.sql @@ -0,0 +1,224 @@ +ALTER TABLE `bots` ADD COLUMN `HP` INTEGER NOT NULL DEFAULT 0 AFTER `DrakkinDetails`; +ALTER TABLE `bots` ADD COLUMN `Mana` INTEGER NOT NULL DEFAULT 0 AFTER `HP`; + +-- Debuff Spells +REPLACE INTO npc_spells_entries (npc_spells_id, spellid, type, minlevel, maxlevel, priority) VALUES +((SELECT id FROM npc_spells WHERE name = 'Shadowknight Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Siphon Strength' AND sn.classes5 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 6, 51, 3), +((SELECT id FROM npc_spells WHERE name = 'Shadowknight Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scream of Hate' AND sn.classes5 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 15, 34, 3), +((SELECT id FROM npc_spells WHERE name = 'Shadowknight Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scream of Pain' AND sn.classes5 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 23, 49, 3), +((SELECT id FROM npc_spells WHERE name = 'Shadowknight Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Shroud of Hate' AND sn.classes5 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 35, 53, 3), +((SELECT id FROM npc_spells WHERE name = 'Shadowknight Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Shroud of Pain' AND sn.classes5 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 50, 55, 3), +((SELECT id FROM npc_spells WHERE name = 'Shadowknight Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Abduction of Strength' AND sn.classes5 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 52, 59, 3), +((SELECT id FROM npc_spells WHERE name = 'Shadowknight Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Torrent of Hate' AND sn.classes5 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 54, 64, 3), +((SELECT id FROM npc_spells WHERE name = 'Shadowknight Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Torrent of Pain' AND sn.classes5 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 56, 62, 3), +((SELECT id FROM npc_spells WHERE name = 'Shadowknight Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Torrent of Fatigue' AND sn.classes5 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 58, 65, 3), +((SELECT id FROM npc_spells WHERE name = 'Shadowknight Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Aura of Darkness' AND sn.classes5 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 61, 70, 2), + +((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Hand of Ro' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 61, 85, 2), +((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Sun\'s Corona' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 67, 73, 2), +((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Glacier Breath' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 67, 71, 2), + +((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Siphon Strength' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 1, 12, 3), +((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scent of Dusk' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 10, 20, 3), +((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scent of Shadow' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 21, 36, 3), +((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scent of Darkness' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 37, 51, 3), +((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scent of Terris' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 52, 67, 3), +((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Degeneration' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 52, 65, 3), +((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scent of Midnight' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 68, 71, 3), + +((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Malaise' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 22, 43, 2), +((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Malaisement' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 44, 50, 2), +((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Malosi' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 51, 57, 2), +((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Malosini' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 58, 59, 2), +((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Mala' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 60, 255, 1), +((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Malosinia' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 63, 70, 2), + +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Weaken' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 1, 3, 3), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Tashan' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 2, 17, 1), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Enfeeblement' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 4, 8, 3), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Ebbing Strength' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 9, 18, 3), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Disempower' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 16, 24, 3), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Tashani' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 18, 40, 1), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Feckless Might' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 19, 33, 4), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Listless Power' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 25, 39, 3), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Insipid Weakness' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 34, 41, 4), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Incapacitate' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 40, 52, 3), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Tashania' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 41, 56, 1), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Weakness' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 42, 52, 4), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cripple' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 53, 65, 3), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Tashanian' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 57, 60, 1), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Howl of Tashan' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 61, 71, 1), +((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Synapsis Spasm' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 66, 70, 3), + +((SELECT id FROM npc_spells WHERE name = 'Beastlord Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Listless Power' AND sn.classes15 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 44, 55, 2), +((SELECT id FROM npc_spells WHERE name = 'Beastlord Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Incapacitate' AND sn.classes15 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 56, 65, 2); + +-- Level > 70 +-- REPLACE INTO npc_spells_entries (npc_spells_id, spellid, type, minlevel, maxlevel, priority) VALUES +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Blistering Sunray' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 71, 75, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Blistering Sunray Rk. II' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 71, 75, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Blistering Sunray Rk. III' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 71, 75, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Torrid Sunray' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 76, 80, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Torrid Sunray Rk. II' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 76, 80, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Torrid Sunray Rk. III' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 76, 80, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Withering Sunray' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 81, 85, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Withering Sunray Rk. II' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 81, 85, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Withering Sunray Rk. III' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 81, 85, 2), + +-- ((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scent of Twilight' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 72, 76, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scent of Twilight Rk. II' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 72, 76, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scent of Twilight Rk. III' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 72, 76, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scent of Afterlife' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 77, 81, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scent of Afterlife Rk. II' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 77, 81, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scent of Afterlife Rk. III' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 77, 81, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scent of Gloom' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 82, 86, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scent of Gloom Rk. II' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 82, 86, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Necromancer Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Scent of Gloom Rk. III' AND sn.classes11 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 82, 86, 3), + +-- ((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Malosinise' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 71, 75, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Malosinise Rk. II' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 71, 75, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Malosinise Rk. III' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 71, 75, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Malosinise' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 76, 80, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Malosinise Rk. II' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 76, 80, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Malosinise Rk. III' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 76, 80, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Malosenea' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 81, 85, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Malosenea Rk. II' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 81, 85, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Magician Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Malosenea Rk. III' AND sn.classes13 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 81, 85, 2), + +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Fractured Consciousness' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 71, 75, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Fractured Consciousness Rk. II' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 71, 75, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Fractured Consciousness Rk. III' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 71, 75, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Echo of Tashan' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 72, 76, 1), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Echo of Tashan Rk. II' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 72, 76, 1), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Echo of Tashan Rk. III' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 72, 76, 1), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Shattered Consciousness' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 76, 80, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Shattered Consciousness Rk. II' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 76, 80, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Shattered Consciousness Rk. III' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 76, 80, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Din of Tashan' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 77, 81, 1), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Din of Tashan Rk. II' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 77, 81, 1), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Din of Tashan Rk. III' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 77, 81, 1), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Fragmented Consciousness' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 81, 85, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Fragmented Consciousness Rk. II' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 81, 85, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Fragmented Consciousness Rk. III' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 81, 85, 3), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Bark of Tashan' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 82, 86, 1), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Bark of Tashan Rk. II' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 82, 86, 1), +-- ((SELECT id FROM npc_spells WHERE name = 'Enchanter Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Bark of Tashan Rk. III' AND sn.classes14 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 16384, 82, 86, 1); + +-- Cure Spells +REPLACE INTO npc_spells_entries (npc_spells_id, spellid, type, minlevel, maxlevel, priority) VALUES +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Poison' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 1, 21, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Disease' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 4, 27, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Remove Minor Curse' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 8, 22, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Counteract Poison' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 22, 47, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Remove Lesser Curse' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 23, 37, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Counteract Disease' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 28, 50, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Remove Curse' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 38, 53, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Abolish Poison' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 48, 57, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Pure Blood' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 51, 83, 3), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Remove Greater Curse' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 54, 255, 2), +((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Antidote' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 58, 83, 2), + +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Poison' AND sn.classes3 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 5, 33, 2), +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Disease' AND sn.classes3 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 11, 55, 2), +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Remove Minor Curse' AND sn.classes3 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 19, 33, 2), +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Counteract Poison' AND sn.classes3 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 34, 61, 2), +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Remove Lesser Curse' AND sn.classes3 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 34, 44, 2), +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Remove Curse' AND sn.classes3 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 45, 59, 2), +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Counteract Disease' AND sn.classes3 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 56, 61, 2), +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Remove Greater Curse' AND sn.classes3 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 60, 255, 2), +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Crusader`s Touch' AND sn.classes3 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 62, 66, 2), +((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Crusader\'s Purity' AND sn.classes3 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 67, 80, 2), + +((SELECT id FROM npc_spells WHERE name = 'Ranger Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Poison' AND sn.classes4 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 13, 60, 2), +((SELECT id FROM npc_spells WHERE name = 'Ranger Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Disease' AND sn.classes4 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 22, 60, 2), +((SELECT id FROM npc_spells WHERE name = 'Ranger Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Counteract Poison' AND sn.classes4 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 61, 72, 2), +((SELECT id FROM npc_spells WHERE name = 'Ranger Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Counteract Disease' AND sn.classes4 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 61, 72, 2), + +((SELECT id FROM npc_spells WHERE name = 'Shadowknight Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Disease' AND sn.classes5 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 19, 255, 2), + +((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Disease' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 4, 27, 2), +((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Poison' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 5, 27, 2), +((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Counteract Poison' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 28, 51, 2), +((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Counteract Disease' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 28, 51, 2), +((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Pure Blood' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 52, 83, 2), + +((SELECT id FROM npc_spells WHERE name = 'Bard Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Aria of Asceticism' AND sn.classes8 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 45, 85, 2), +((SELECT id FROM npc_spells WHERE name = 'Bard Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Aria of Innocence' AND sn.classes8 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 52, 85, 2), + +((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Disease' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 1, 21, 2), +((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Poison' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 2, 25, 2), +((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Remove Minor Curse' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 9, 23, 2), +((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Counteract Disease' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 22, 47, 2), +((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Remove Lesser Curse' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 24, 37, 2), +((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Counteract Poison' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 26, 51, 2), +((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Remove Curse' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 38, 53, 2), +((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Abolish Disease' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 48, 51, 2), +((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Disinfecting Aura' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 52, 84, 2), +((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Blood of Nadox' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 52, 84, 2), +((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Remove Greater Curse' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 54, 255, 2), + +((SELECT id FROM npc_spells WHERE name = 'Beastlord Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Disease' AND sn.classes15 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 4, 44, 2), +((SELECT id FROM npc_spells WHERE name = 'Beastlord Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Poison' AND sn.classes15 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 13, 60, 2), +((SELECT id FROM npc_spells WHERE name = 'Beastlord Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Counteract Disease' AND sn.classes15 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 45, 62, 2), +((SELECT id FROM npc_spells WHERE name = 'Beastlord Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Counteract Poison' AND sn.classes15 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 61, 255, 2), +((SELECT id FROM npc_spells WHERE name = 'Beastlord Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Abolish Disease' AND sn.classes15 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 63, 255, 2); + + +-- Level > 70 +-- REPLACE INTO npc_spells_entries (npc_spells_id, spellid, type, minlevel, maxlevel, priority) VALUES +-- ((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Expunge Corruption' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 74, 78, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Expunge Corruption Rk. II' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 74, 78, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Expunge Corruption Rk. III' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 74, 78, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Abolish Corruption' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 255, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Abolish Corruption Rk. II' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 255, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Abolish Corruption Rk. III' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 255, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Purified Blood' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 255, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Purified Blood Rk. II' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 255, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Cleric Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Purified Blood Rk. III' AND sn.classes2 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 255, 2), + +-- ((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Devoted Purity' AND sn.classes3 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 81, 255, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Devoted Purity Rk. II' AND sn.classes3 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 81, 255, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Paladin Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Devoted Purity Rk. III' AND sn.classes3 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 81, 255, 2), + +-- ((SELECT id FROM npc_spells WHERE name = 'Ranger Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Potameid Salve' AND sn.classes4 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 73, 77, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Ranger Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Potameid Salve Rk. II' AND sn.classes4 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 73, 77, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Ranger Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Potameid Salve Rk. III' AND sn.classes4 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 73, 77, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Ranger Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Potameid Balm' AND sn.classes4 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 78, 82, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Ranger Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Potameid Balm Rk. II' AND sn.classes4 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 78, 82, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Ranger Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Potameid Balm Rk. III' AND sn.classes4 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 78, 82, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Ranger Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Burynai Balm' AND sn.classes4 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 83, 87, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Ranger Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Burynai Balm Rk. II' AND sn.classes4 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 83, 87, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Ranger Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Burynai Balm Rk. III' AND sn.classes4 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 83, 87, 2), + +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Corruption' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 71, 83, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Corruption Rk. II' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 71, 83, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Corruption Rk. III' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 71, 83, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Purified Blood' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 88, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Purified Blood Rk. II' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 88, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Druid Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Purified Blood Rk. III' AND sn.classes6 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 88, 2), + +-- ((SELECT id FROM npc_spells WHERE name = 'Bard Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Minohten\'s Purifying Panpipes' AND sn.classes8 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 74, 78, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Bard Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Minohten\'s Purifying Panpipes Rk. II' AND sn.classes8 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 74, 78, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Bard Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Minohten\'s Purifying Panpipes Rk. III' AND sn.classes8 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 74, 78, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Bard Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Minohten\'s Purifying Panpipes' AND sn.classes8 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 79, 83, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Bard Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Minohten\'s Purifying Panpipes Rk. II' AND sn.classes8 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 79, 83, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Bard Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Minohten\'s Purifying Panpipes Rk. III' AND sn.classes8 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 79, 83, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Bard Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Firiona\'s Blessed Clarinet' AND sn.classes8 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 85, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Bard Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Firiona\'s Blessed Clarinet Rk.II' AND sn.classes8 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 85, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Bard Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Firiona\'s Blessed Clarinet Rk.III' AND sn.classes8 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 85, 2), + +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Corruption' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 72, 73, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Corruption Rk. II' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 72, 73, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Cure Corruption Rk. III' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 72, 73, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Chant of the Napaea' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 74, 78, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Chant of the Napaea Rk. II' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 74, 78, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Chant of the Napaea Rk. III' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 74, 78, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Chant of the Darkvine' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 79, 83, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Chant of the Darkvine Rk. II' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 79, 83, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Chant of the Darkvine Rk. III' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 79, 83, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Chant of the Burynai' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 88, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Chant of the Burynai Rk. II' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 88, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Chant of the Burynai Rk. III' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 84, 88, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Blood of Avoling' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 85, 89, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Blood of Avoling Rk. II' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 85, 89, 2), +-- ((SELECT id FROM npc_spells WHERE name = 'Shaman Bot'), (SELECT sn.id FROM spells_new sn, items i WHERE sn.name = 'Blood of Avoling Rk. III' AND sn.classes10 < 254 AND sn.id = i.scrolleffect ORDER BY sn.id DESC LIMIT 1), 32768, 85, 89, 2); \ No newline at end of file diff --git a/utils/sql/svn/2098_required_zonepoint_version_changes.sql b/utils/sql/svn/2098_required_zonepoint_version_changes.sql new file mode 100644 index 000000000..50925b66d --- /dev/null +++ b/utils/sql/svn/2098_required_zonepoint_version_changes.sql @@ -0,0 +1 @@ +ALTER TABLE `zone_points` MODIFY COLUMN `version` int(11) NOT NULL DEFAULT 0 AFTER `zone`; diff --git a/utils/sql/svn/2099_required_discovered_items_account_status.sql b/utils/sql/svn/2099_required_discovered_items_account_status.sql new file mode 100644 index 000000000..fd9c75290 --- /dev/null +++ b/utils/sql/svn/2099_required_discovered_items_account_status.sql @@ -0,0 +1,2 @@ +ALTER TABLE `discovered_items` ADD COLUMN `account_status` int(11) NOT NULL DEFAULT '0' AFTER `discovered_date`; +UPDATE discovered_items, account, character_ SET discovered_items.account_status = account.status WHERE character_.account_id = account.id AND character_.name = discovered_items.char_name; diff --git a/utils/sql/svn/2104_required_group_roles.sql b/utils/sql/svn/2104_required_group_roles.sql new file mode 100644 index 000000000..7a0dbb7bf --- /dev/null +++ b/utils/sql/svn/2104_required_group_roles.sql @@ -0,0 +1,2 @@ +ALTER TABLE `group_leaders` ADD COLUMN `maintank` VARCHAR(64) NOT NULL DEFAULT '' AFTER `leadershipaa`; +ALTER TABLE `group_leaders` ADD COLUMN `puller` VARCHAR(64) NOT NULL DEFAULT '' AFTER `assist`; \ No newline at end of file diff --git a/utils/sql/svn/2107_required_bot_stances.sql b/utils/sql/svn/2107_required_bot_stances.sql new file mode 100644 index 000000000..e63a05bf2 --- /dev/null +++ b/utils/sql/svn/2107_required_bot_stances.sql @@ -0,0 +1,6 @@ +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) +); \ No newline at end of file diff --git a/utils/sql/svn/210_undyeme.sql b/utils/sql/svn/210_undyeme.sql new file mode 100644 index 000000000..a46269725 --- /dev/null +++ b/utils/sql/svn/210_undyeme.sql @@ -0,0 +1 @@ +INSERT INTO commands (command, access, description) VALUES ('undyeme', 0, 'Remove dye from all of your armor slots'); diff --git a/utils/sql/svn/2129_required_lfguild.sql b/utils/sql/svn/2129_required_lfguild.sql new file mode 100644 index 000000000..dc98dfa9b --- /dev/null +++ b/utils/sql/svn/2129_required_lfguild.sql @@ -0,0 +1,16 @@ +-- +-- Table structure for table `lfguild` +-- + +CREATE TABLE IF NOT EXISTS `lfguild` ( + `type` tinyint(3) unsigned NOT NULL DEFAULT '0', + `name` varchar(32) NOT NULL, + `comment` varchar(256) NOT NULL, + `fromlevel` tinyint(3) unsigned NOT NULL DEFAULT '0', + `tolevel` tinyint(3) unsigned NOT NULL DEFAULT '0', + `classes` int(10) unsigned NOT NULL DEFAULT '0', + `aacount` int(10) unsigned NOT NULL DEFAULT '0', + `timezone` int(10) unsigned NOT NULL DEFAULT '0', + `timeposted` int(10) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`type`,`name`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; diff --git a/utils/sql/svn/2133_required_faction_loot_despawn.sql b/utils/sql/svn/2133_required_faction_loot_despawn.sql new file mode 100644 index 000000000..375d72039 --- /dev/null +++ b/utils/sql/svn/2133_required_faction_loot_despawn.sql @@ -0,0 +1,27 @@ +alter table faction_values add column `temp` tinyint(3) not null default 0; +alter table npc_faction_entries add column `temp` tinyint(3) not null default 0; +alter table spawngroup add column `despawn` tinyint(3) not null default 0; +alter table spawngroup add column `despawn_timer` int(11) not null default 100; +alter table lootdrop_entries add column `minlevel` tinyint(3) not null default 0; +alter table lootdrop_entries add column `maxlevel` tinyint(3) not null default 127; + +DROP TABLE IF EXISTS `player_corpses_backup`; +CREATE TABLE `player_corpses_backup` ( + `id` int(11) unsigned NOT NULL default '0', + `charid` int(11) unsigned NOT NULL default '0', + `charname` varchar(64) NOT NULL default '', + `zoneid` smallint(5) NOT NULL default '0', + `instanceid` SMALLINT UNSIGNED DEFAULT '0' NOT NULL, + `x` float NOT NULL default '0', + `y` float NOT NULL default '0', + `z` float NOT NULL default '0', + `heading` float NOT NULL default '0', + `data` blob, + `timeofdeath` datetime NOT NULL default '0000-00-00 00:00:00', + `rezzed` tinyint(3) unsigned default '0', + `IsBurried` tinyint(3) NOT NULL default '0', + `WasAtGraveyard` tinyint(3) NOT NULL default '0' +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +INSERT INTO `rule_values` VALUES (1, 'World:DeleteStaleCorpeBackups', 'true', 'Deletes stale corpse backups older than 2 weeks.'); +INSERT INTO `rule_values` VALUES (1, 'Zone:UsePlayerCorpseBackups', 'true', 'Keeps backups of player corpses.'); \ No newline at end of file diff --git a/utils/sql/svn/2136_extended_targets.sql b/utils/sql/svn/2136_extended_targets.sql new file mode 100644 index 000000000..32a69b7b2 --- /dev/null +++ b/utils/sql/svn/2136_extended_targets.sql @@ -0,0 +1,2 @@ +ALTER TABLE `character_` ADD `xtargets` TINYINT UNSIGNED NOT NULL DEFAULT '5'; +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:EnableXTargetting', 'true', ''); diff --git a/utils/sql/svn/2142_emotes.sql b/utils/sql/svn/2142_emotes.sql new file mode 100644 index 000000000..c86e03922 --- /dev/null +++ b/utils/sql/svn/2142_emotes.sql @@ -0,0 +1,15 @@ +CREATE TABLE `npc_emotes` ( + `id` int(10) NOT NULL auto_increment, + `emoteid` int(10) unsigned NOT NULL default '0', + `event_` tinyint(3) NOT NULL default '0', + `type` tinyint(3) NOT NULL default '0', + `text` varchar(512) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `emoteid` (`emoteid`,`event_`) +) ENGINE=InnoDB AUTO_INCREMENT=579 DEFAULT CHARSET=latin1; + +alter table npc_types add column `emoteid` int(10) NOT NULL default '0'; + +insert into commands values ('emoteview', 80, 'Lists all of a NPCs loaded emotes'); +insert into commands values ('reloadview', 80, 'Reloads NPC emotes'); +insert into commands values ('emotesearch', 80, 'Searches loaded NPC emotes'); \ No newline at end of file diff --git a/utils/sql/svn/2154_optional_rule_spell_procs_resists_falloff.sql b/utils/sql/svn/2154_optional_rule_spell_procs_resists_falloff.sql new file mode 100644 index 000000000..b59582481 --- /dev/null +++ b/utils/sql/svn/2154_optional_rule_spell_procs_resists_falloff.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AvgSpellProcsPerMinute', '6.0', 'Default = 6.0 Determines proc rate of spells applied from sympathetic focus effect'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:ResistFalloff', '67', 'Default = 67 Max that level that will adjust our resist chance based on level modifiers'); diff --git a/utils/sql/svn/2156_optional_charm_break_rule.sql b/utils/sql/svn/2156_optional_charm_break_rule.sql new file mode 100644 index 000000000..7a9aad13f --- /dev/null +++ b/utils/sql/svn/2156_optional_charm_break_rule.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:CharismaEffectiveness', '10', 'Default 10 CHA = -1 resist mod. Deterimes how much resist modification charisma applies to charm/pacify checks.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:CharmBreakCheckChance', '25', 'Default 25. Determines percent chance for a charm break check to occur each buff tick.'); \ No newline at end of file diff --git a/utils/sql/svn/2159_optional_defensiveproc_rules.sql b/utils/sql/svn/2159_optional_defensiveproc_rules.sql new file mode 100644 index 000000000..14c7e30ac --- /dev/null +++ b/utils/sql/svn/2159_optional_defensiveproc_rules.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:AvgDefProcsPerMinute', '2', 'Default 2. Determines defensive procs per minute.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:DefProcPerMinAgiContrib', '0.075', 'Default 25. Determines how much agility effects proc rate.'); \ No newline at end of file diff --git a/utils/sql/svn/2164_require_bots_bottimers.sql b/utils/sql/svn/2164_require_bots_bottimers.sql new file mode 100644 index 000000000..9b42f531a --- /dev/null +++ b/utils/sql/svn/2164_require_bots_bottimers.sql @@ -0,0 +1,7 @@ +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) +) \ No newline at end of file diff --git a/utils/sql/svn/2171_optional_SpecialAttackACBonus_rule.sql b/utils/sql/svn/2171_optional_SpecialAttackACBonus_rule.sql new file mode 100644 index 000000000..0d46823ce --- /dev/null +++ b/utils/sql/svn/2171_optional_SpecialAttackACBonus_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:SpecialAttackACBonus', '15', '(Default=15) Percent amount of damage per AC gained for certain special attacks (damage = AC*SpecialAttackACBonus/100).'); diff --git a/utils/sql/svn/2176_optional_FrenzyBonus_rule.sql b/utils/sql/svn/2176_optional_FrenzyBonus_rule.sql new file mode 100644 index 000000000..5d39b80a2 --- /dev/null +++ b/utils/sql/svn/2176_optional_FrenzyBonus_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:FrenzyBonus', '0', 'Modify Frenzy skill damage by percent,'); diff --git a/utils/sql/svn/2176_optional_aa_expansion_SOF_fix.sql b/utils/sql/svn/2176_optional_aa_expansion_SOF_fix.sql new file mode 100644 index 000000000..81a09024f --- /dev/null +++ b/utils/sql/svn/2176_optional_aa_expansion_SOF_fix.sql @@ -0,0 +1,12 @@ +-- Corrects the values in aa_expansion field for SOF+ client NOTE: DODH AA will go in OOW on Titanium ATM +-- Lucin AA = 3 +UPDATE altadv_vars SET aa_expansion = 3 WHERE special_category = 4294967295 AND skill_id < 291; +-- POP AA = 4 +UPDATE altadv_vars SET aa_expansion = 4 WHERE special_category = 4294967295 AND skill_id >= 292 AND skill_id <= 652; +-- GOD AA = 7 +UPDATE altadv_vars SET aa_expansion = 7 WHERE special_category = 4294967295 AND skill_id >= 672 AND skill_id <= 931; +-- OOW AA = 8 +UPDATE altadv_vars SET aa_expansion = 8 WHERE special_category = 4294967295 AND skill_id >= 1158 AND skill_id <= 1344; +UPDATE altadv_vars SET aa_expansion = 8 WHERE special_category = 4294967295 AND skill_id >= 978 AND skill_id <= 1129; +-- DODH AA = 10 +UPDATE altadv_vars SET aa_expansion = 10 WHERE special_category = 4294967295 AND skill_id >= 1378 AND skill_id <= 1627; \ No newline at end of file diff --git a/utils/sql/svn/2176_required_aa_updates.sql b/utils/sql/svn/2176_required_aa_updates.sql new file mode 100644 index 000000000..d49d9b619 --- /dev/null +++ b/utils/sql/svn/2176_required_aa_updates.sql @@ -0,0 +1,282 @@ +-- Concentration +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1587, 1, 344, 50, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1588, 1, 344, 55, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1589, 1, 344, 60, 0); + +-- Stalwart Endurance +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 652, 1, 293, 25, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 653, 1, 293, 50, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 654, 1, 293, 75, 0); + +-- Dauntless Perseverance +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1586, 1, 293, 25, 0); + +-- First Aid +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 74, 1, 269, 10, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 75, 1, 269, 20, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 76, 1, 269, 30, 0); + +-- Bandage Wound +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 263, 1, 282, 10, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 264, 1, 282, 25, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 265, 1, 282, 50, 0); + +-- Mithaniel's Binding +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 537, 1, 282, 60, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 538, 1, 282, 70, 0); + +-- Field Dressing +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1611, 1, 282, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1612, 1, 282, 8, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1613, 1, 282, 12, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1614, 1, 282, 16, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1615, 1, 282, 20, 0); + +-- Combat Medic +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 5136, 1, 282, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 5137, 1, 282, 8, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 5138, 1, 282, 12, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 5139, 1, 282, 16, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 5140, 1, 282, 20, 0); + +-- Concentration +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1587, 1, 10, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1588, 1, 33, 0, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1589, 1, 50, 0, 0); + +-- Relentless Assault +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1621, 1, 177, 14, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1622, 1, 177, 19, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1623, 1, 177, 24, 0); + +-- Punishing Blade +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 599, 1, 266, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 600, 1, 266, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 601, 1, 266, 8, 0); + +-- Wicked Blade +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1536, 1, 266, 11, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1537, 1, 266, 14, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1538, 1, 266, 17, 0); + +-- Speed of the Knight +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 602, 1, 266, 2, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 603, 1, 266, 4, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 604, 1, 266, 8, 0); + +-- Innate See Invis +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1388, 2, 13, 1, 0); + +-- Selo's Enduring Cadence +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1627, 1, 271, 5, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1628, 1, 271, 10, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1629, 1, 271, 15, 0); + +-- Vicious Smash +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 855, 1, 220, 5, 10); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 856, 1, 220, 15, 10); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 857, 1, 220, 30, 10); + +-- Kick Mastery -- Updates added for redundancy since some of these may already exist. +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 820, 1, 220, 10, 26); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 820, 2, 220, 10, 30); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 820, 3, 220, 10, 38); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 821, 1, 220, 20, 26); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 821, 2, 220, 20, 30); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 821, 3, 220, 20, 38); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 822, 1, 220, 30, 26); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 822, 2, 220, 30, 30); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 822, 3, 220, 30, 38); +UPDATE `aa_effects` SET `effectid`=220, `base1`=10, `base2`=26 WHERE `aaid`=820 && `slot`=1; +UPDATE `aa_effects` SET `effectid`=220, `base1`=10, `base2`=30 WHERE `aaid`=820 && `slot`=2; +UPDATE `aa_effects` SET `effectid`=220, `base1`=10, `base2`=38 WHERE `aaid`=820 && `slot`=3; +UPDATE `aa_effects` SET `effectid`=220, `base1`=20, `base2`=26 WHERE `aaid`=821 && `slot`=1; +UPDATE `aa_effects` SET `effectid`=220, `base1`=20, `base2`=30 WHERE `aaid`=821 && `slot`=2; +UPDATE `aa_effects` SET `effectid`=220, `base1`=20, `base2`=38 WHERE `aaid`=821 && `slot`=3; +UPDATE `aa_effects` SET `effectid`=220, `base1`=30, `base2`=26 WHERE `aaid`=822 && `slot`=1; +UPDATE `aa_effects` SET `effectid`=220, `base1`=30, `base2`=30 WHERE `aaid`=822 && `slot`=2; +UPDATE `aa_effects` SET `effectid`=220, `base1`=30, `base2`=38 WHERE `aaid`=822 && `slot`=3; + +-- Strengthened Strike -- Updates added for redundancy since some of these may already exist. +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 915, 1, 220, 5, 10); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 915, 2, 220, 5, 30); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 916, 1, 220, 15, 10); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 916, 2, 220, 15, 30); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 917, 1, 220, 30, 10); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 917, 2, 220, 30, 30); +UPDATE `aa_effects` SET `effectid`=220, `base1`=5, `base2`=10 WHERE `aaid`=915 && `slot`=1; +UPDATE `aa_effects` SET `effectid`=220, `base1`=5, `base2`=30 WHERE `aaid`=915 && `slot`=2; + +-- Shield Block +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1287, 1, 320, 1, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1288, 2, 320, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1289, 3, 320, 5, 0); + +-- Blur of Axes +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1134, 1, 220, 32, 74); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1135, 1, 220, 64, 74); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1136, 1, 220, 96, 74); + +-- Vicious Frenzy +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1158, 1, 220, 128, 74); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1159, 1, 220, 192, 74); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1160, 1, 220, 224, 74); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1161, 1, 220, 256, 74); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1162, 1, 220, 288, 74); + +-- Triple Backstab +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 846, 1, 258, 5, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 847, 1, 258, 10, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 848, 1, 258, 15, 0); + +-- Flurry of Knives +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1301, 1, 258, 20, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1302, 1, 258, 25, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1303, 1, 258, 30, 0); + +-- Chaotic Stab +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 287, 1, 253, 1, 0); + +-- Endless Quiver +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 205, 1, 251, 100, 0); + +-- Archery Mastery +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 199, 1, 301, 30, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 200, 1, 301, 60, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 201, 1, 301, 100, 0); + +-- Throwing Mastery +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1131, 1, 185, 10, 51); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1132, 1, 185, 20, 51); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1133, 1, 185, 30, 51); + +-- Sinister Strikes +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 806, 1, 249, 1, 0); + +-- Heightened Awareness +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 823, 1, 222, 20, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 824, 1, 222, 25, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 825, 1, 222, 30, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 826, 1, 222, 35, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 827, 1, 222, 40, 0); + +-- Tactical Mastery +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 631, 1, 292, 15, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 632, 1, 292, 30, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 633, 1, 292, 45, 0); + +-- Ferocity +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 564, 1, 177, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 565, 1, 177, 6, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 566, 1, 177, 9, 0); + +-- Knight's Advantage +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 561, 1, 177, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 562, 1, 177, 6, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 563, 1, 177, 9, 0); + +-- Knight's Expertise +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1624, 1, 177, 14, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1625, 1, 177, 19, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1626, 1, 177, 24, 0); + +-- Bestial Frenzy +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 551, 1, 225, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 552, 1, 225, 6, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 553, 1, 225, 9, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 554, 1, 225, 12, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 555, 1, 225, 15, 0); + +-- Harmonious Attack +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 556, 1, 225, 3, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 557, 1, 225, 6, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 558, 1, 225, 9, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 559, 1, 225, 12, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 560, 1, 225, 15, 0); + +-- Weapon Affinity +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 686, 1, 200, 10, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 687, 1, 200, 20, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 688, 1, 200, 30, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 689, 1, 200, 40, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 690, 1, 200, 50, 0); + +-- Dragon Punch +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 273, 1, 288, 100, 21); + +-- Technique of Master Wu +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 611, 1, 283, 20, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 612, 1, 283, 40, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 613, 1, 283, 60, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 614, 1, 283, 80, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 615, 1, 283, 100, 0); + +-- Seized Opportunity +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 878, 1, 252, 10, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 879, 1, 252, 20, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 880, 1, 252, 30, 0); + +-- Forced Opening +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1539, 1, 252, 35, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1540, 1, 252, 40, 0); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 1541, 1, 252, 45, 0); + +-- Fleet of Foot +UPDATE `aa_effects` SET `effectid`=290, `base1`=5 WHERE `aaid`=628; +UPDATE `aa_effects` SET `effectid`=290, `base1`=15 WHERE `aaid`=629; + +-- Swift Journey +UPDATE `aa_effects` SET `effectid`=271, `base1`=7 WHERE `aaid`=672; +UPDATE `aa_effects` SET `effectid`=271, `base1`=14 WHERE `aaid`=673; + +-- Innate Run Speed +UPDATE `aa_effects` SET `effectid`=271, `base1`=8 WHERE `aaid`=62; +UPDATE `aa_effects` SET `effectid`=271, `base1`=14 WHERE `aaid`=63; +UPDATE `aa_effects` SET `effectid`=271, `base1`=21 WHERE `aaid`=64; + +-- Strikethrough +UPDATE `aa_effects` SET `effectid`=292, `base1`=5 WHERE `aaid`=807; +UPDATE `aa_effects` SET `effectid`=292, `base1`=10 WHERE `aaid`=808; +UPDATE `aa_effects` SET `effectid`=292, `base1`=15 WHERE `aaid`=809; + +-- altadv_vars +UPDATE `altadv_vars` SET `prereq_skill` = 263 WHERE `skill_id` = 537; +UPDATE `altadv_vars` SET `type` = 2 WHERE `skill_id` = 564; +UPDATE `altadv_vars` SET `prereq_skill` = 222, `prereq_minpoints` = 3 WHERE `skill_id` = 652; +UPDATE `altadv_vars` SET `hotkey_sid` = 4294967295, `hotkey_sid2` = 4294967295, `title_sid` = 683, `desc_sid` = 683, `spellid` = 4294967295 WHERE `skill_id` = 683; +UPDATE `altadv_vars` SET `hotkey_sid` = 4294967295, `hotkey_sid2` = 4294967295, `title_sid` = 807, `desc_sid` = 807, `spellid` = 4294967295 WHERE `skill_id` = 807; +UPDATE `altadv_vars` SET `hotkey_sid` = 4294967295, `hotkey_sid2` = 4294967295, `title_sid` = 820, `desc_sid` = 820, `spellid` = 4294967295, `sof_type` = 3 WHERE `skill_id` = 820; +UPDATE `altadv_vars` SET `hotkey_sid` = 4294967295, `hotkey_sid2` = 4294967295, `title_sid` = 823, `desc_sid` = 823, `spellid` = 4294967295, `sof_type` = 3 WHERE `skill_id` = 823; +UPDATE `altadv_vars` SET `hotkey_sid` = 4294967295, `hotkey_sid2` = 4294967295, `title_sid` = 915, `desc_sid` = 915, `spellid` = 4294967295, `type` = 6 WHERE `skill_id` = 915; +UPDATE `altadv_vars` SET `hotkey_sid` = 4294967295, `hotkey_sid2` = 4294967295, `title_sid` = 846, `desc_sid` = 846, `spellid` = 4294967295, `sof_type` = 3 WHERE `skill_id` = 846; +UPDATE `altadv_vars` SET `hotkey_sid` = 4294967295, `hotkey_sid2` = 4294967295, `title_sid` = 855, `desc_sid` = 855, `spellid` = 4294967295, `sof_type` = 3, `classes` = 40 WHERE `skill_id` = 855; +UPDATE `altadv_vars` SET `hotkey_sid` = 4294967295, `hotkey_sid2` = 4294967295, `title_sid` = 878, `desc_sid` = 878, `spellid` = 4294967295, `prereq_skill` = 0, `prereq_minpoints` = 0, `sof_type` = 3 WHERE `skill_id` = 878; +UPDATE `altadv_vars` SET `hotkey_sid` = 4294967295, `hotkey_sid2` = 4294967295, `title_sid` = 1134, `desc_sid` = 1134, `spellid` = 4294967295, `sof_type` = 3, `aa_expansion` = 4, `sof_next_skill` = 1134 WHERE `skill_id` = 1134; +UPDATE `altadv_vars` SET `hotkey_sid` = 4294967295, `hotkey_sid2` = 4294967295, `title_sid` = 1150, `desc_sid` = 1150, `spellid` = 4294967295 WHERE `skill_id` = 1150; +UPDATE `altadv_vars` SET `hotkey_sid` = 4294967295, `hotkey_sid2` = 4294967295, `title_sid` = 1131, `desc_sid` = 1131, `spellid` = 4294967295, `sof_type` = 3, `aa_expansion` = 3 WHERE `skill_id` = 1131; +UPDATE `altadv_vars` SET `hotkey_sid` = 4294967295, `hotkey_sid2` = 4294967295, `title_sid` = 1158, `desc_sid` = 1158, `spellid` = 4294967295, `prereq_skill` = 1134, `prereq_minpoints` = 3, `sof_type` = 3, `sof_next_skill` = 1134 WHERE `skill_id` = 1158; + +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`) VALUES ('1597', 'Call of Challenge', '9', '1', '1597', '1597', '1597', '1597', '7', '8271', '0', '0', '6', '10', '2', '0', '70', '0', '10', '4294967295', '3', '0', '70', '1597', '1', '0'); +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`) VALUES ('912', 'Warlord\'s Tenacity', '3', '3', '912', '912', '912', '912', '6', '4925', '0', '0', '4', '60', '2', '0', '65', '3', '7', '4294967295', '3', '0', '65', '912', '1', '0'); +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`) VALUES ('1583', 'Hastened Defiance', '5', '1', '4294967295', '4294967295', '1583', '1583', '7', '4294967295', '912', '3', '0', '0', '2', '0', '70', '0', '10', '4294967295', '3', '0', '70', '1583', '1', '0'); +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`) VALUES ('1586', 'Dauntless Perseverance', '12', '1', '4294967295', '4294967295', '1586', '1586', '7', '4294967295', '222', '3', '0', '0', '2', '0', '70', '0', '10', '4294967295', '3', '0', '70', '1586', '1', '0'); +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`) VALUES ('1611', 'Field Dressing', '3', '5', '4294967295', '4294967295', '1611', '1611', '7', '4294967295', '537', '2', '0', '0', '2', '1', '66', '0', '10', '4294967295', '3', '0', '66', '1417', '1', '0'); +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`) VALUES ('5136', 'Combat Medic', '3', '5', '4294967295', '4294967295', '4688', '4688', '3', '4294967295', '1611', '5', '0', '0', '2', '1', '71', '0', '12', '4294967295', '3', '0', '71', '4688', '4', '0'); +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`) VALUES ('1587', 'Concentration', '3', '3', '4294967295', '4294967295', '1587', '1587', '7', '4294967295', '0', '0', '0', '0', '642', '1', '66', '0', '10', '4294967295', '2', '0', '1', '1587', '1', '0'); +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`) VALUES ('1621', 'Relentless Assault', '7', '3', '4294967295', '4294967295', '1621', '1621', '7', '4294967295', '564', '3', '0', '0', '658', '1', '70', '0', '10', '4294967295', '2', '0', '70', '564', '1', '0'); +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`) VALUES ('1330', 'Resolute Defiance', '3', '3', '912', '912', '1330', '1330', '7', '4925', '912', '3', '4', '60', '2', '0', '70', '3', '8', '4294967295', '3', '0', '70', '912', '1', '0'); +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`) VALUES ('1536', 'Wicked Blade', '3', '3', '4294967295', '4294967295', '1536', '1536', '7', '4294967295', '599', '3', '0', '0', '146', '1', '70', '0', '10', '4294967295', '3', '0', '70', '599', '1', '0'); +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`) VALUES ('1388', 'Innate See Invis', '9', '1', '4294967295', '4294967295', '1388', '1388', '7', '0', '0', '0', '0', '0', '65534', '1', '70', '0', '10', '4294967295', '1', '0', '70', '1388', '1', '0'); +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`) VALUES ('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', '66', '1627', '1', '0'); +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`) VALUES ('1301', 'Flurry of Knives', '3', '3', '4294967295', '4294967295', '1301', '1301', '7', '4294967295', '846', '3', '0', '0', '512', '0', '66', '3', '8', '4294967295', '3', '0', '1', '846', '1', '0'); +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`) VALUES ('1539', 'Forced Opening', '3', '3', '4294967295', '4294967295', '1539', '1539', '7', '4294967295', '878', '3', '0', '0', '512', '0', '70', '0', '10', '4294967295', '3', '0', '1', '878', '1', '0'); +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`) VALUES ('1624', 'Knight\'s Expertise', '7', '3', '4294967295', '4294967295', '1624', '1624', '7', '4294967295', '561', '3', '0', '0', '40', '1', '70', '0', '10', '4294967295', '3', '0', '70', '561', '1', '0'); + +-- aa_actions +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES ('1597', '0', '10', '8271', '2', '0', '0', '0', '0', '0', '0', '0'); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES ('912', '0', '60', '4925', '1', '0', '0', '0', '1583', '10', '0', '0'); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES ('912', '1', '60', '4926', '1', '0', '0', '0', '1583', '10', '0', '0'); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES ('912', '2', '60', '4927', '1', '0', '0', '0', '1583', '10', '0', '0'); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES ('1330', '1', '60', '5936', '1', '0', '0', '0', '1583', '10', '0', '0'); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES ('1330', '2', '60', '5937', '1', '0', '0', '0', '1583', '10', '0', '0'); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES ('1330', '3', '60', '5938', '1', '0', '0', '0', '1583', '10', '0', '0'); diff --git a/utils/sql/svn/2178_required_aa_updates.sql b/utils/sql/svn/2178_required_aa_updates.sql new file mode 100644 index 000000000..a7a5e8c3c --- /dev/null +++ b/utils/sql/svn/2178_required_aa_updates.sql @@ -0,0 +1,148 @@ +-- Corrections to 2176_aa_updates.sql +-- Strengthed Strike +UPDATE `altadv_vars` SET `sof_type` = 3 ,`sof_next_skill` = 915 WHERE `skill_id` = 915; +-- Untamed Rage +UPDATE `altadv_vars` SET `sof_type` = 3, `hotkey_sid` = 1150, `hotkey_sid2` = 1150, `title_sid` = 1150, `desc_sid` = 1150, `spellid` = 5848 WHERE `skill_id` = 1150; + +-- New Updates + +-- Warcry +-- UPDATE `altadv_vars` SET `spellid` = 5229 WHERE `skill_id` = 260; +REPLACE 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`) VALUES ('260', 'Warcry', '3', '3', '13797', '4294967295', '13795', '13796', '3', '5229', '117', '3', '3', '2160', '2', '0', '59', '3', '3', '4294967295', '3', '0', '1', '260', '1', '0'); + +-- Veteran's Wrath (x4 each for different classes) +REPLACE 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`) VALUES ('1041', 'Veteran\'s Wrath', '3', '3', '4294967295', '4294967295', '30224', '30225', '7', '4294967295', '445', '3', '0', '0', '33192', '1', '67', '3', '8', '4294967295', '2', '0', '1', '1041', '1', '0'); +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`) VALUES ('1050', 'Veteran\'s Wrath', '3', '3', '4294967295', '4294967295', '1050', '1050', '7', '4294967295', '443', '3', '0', '0', '2', '1', '67', '3', '8', '4294967295', '2', '0', '1', '1050', '1', '0'); +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`) VALUES ('1044', 'Veteran\'s Wrath', '3', '3', '4294967295', '4294967295', '1044', '1044', '7', '4294967295', '443', '3', '0', '0', '512', '0', '67', '3', '8', '4294967295', '2', '0', '1', '1044', '1', '0'); +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`) VALUES ('1047', 'Veteran\'s Wrath', '3', '3', '4294967295', '4294967295', '1047', '1047', '7', '4294967295', '443', '3', '0', '0', '16', '0', '67', '3', '8', '4294967295', '2', '0', '1', '1047', '1', '0'); + +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1041', '1', '330', '25', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1041', '2', '330', '25', '1'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1041', '3', '330', '25', '2'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1041', '4', '330', '25', '3'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1041', '5', '330', '25', '28'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1041', '6', '330', '25', '36'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1042', '1', '330', '30', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1042', '2', '330', '30', '1'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1042', '3', '330', '30', '2'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1042', '4', '330', '30', '3'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1042', '5', '330', '30', '28'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1042', '6', '330', '30', '36'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1043', '1', '330', '35', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1043', '2', '330', '35', '1'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1043', '3', '330', '35', '2'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1043', '4', '330', '35', '3'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1043', '5', '330', '35', '28'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1043', '6', '330', '35', '36'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1044', '1', '330', '25', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1044', '2', '330', '25', '1'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1044', '3', '330', '25', '2'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1044', '4', '330', '25', '3'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1044', '5', '330', '25', '28'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1044', '6', '330', '25', '36'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1045', '1', '330', '30', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1045', '2', '330', '30', '1'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1045', '3', '330', '30', '2'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1045', '4', '330', '30', '3'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1045', '5', '330', '30', '28'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1045', '6', '330', '30', '36'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1046', '1', '330', '35', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1046', '2', '330', '35', '1'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1046', '3', '330', '35', '2'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1046', '4', '330', '35', '3'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1046', '5', '330', '35', '28'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1046', '6', '330', '35', '36'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1047', '1', '330', '25', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1047', '2', '330', '25', '1'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1047', '3', '330', '25', '2'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1047', '4', '330', '25', '3'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1047', '5', '330', '25', '28'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1047', '6', '330', '25', '36'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1048', '1', '330', '30', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1048', '2', '330', '30', '1'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1048', '3', '330', '30', '2'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1048', '4', '330', '30', '3'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1048', '5', '330', '30', '28'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1048', '6', '330', '30', '36'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1049', '1', '330', '35', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1049', '2', '330', '35', '1'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1049', '3', '330', '35', '2'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1049', '4', '330', '35', '3'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1049', '5', '330', '35', '28'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1049', '6', '330', '35', '36'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1050', '1', '330', '15', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1050', '2', '330', '15', '1'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1050', '3', '330', '15', '2'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1050', '4', '330', '15', '3'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1050', '5', '330', '15', '28'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1050', '6', '330', '15', '36'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1051', '1', '330', '20', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1051', '2', '330', '20', '1'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1051', '3', '330', '20', '2'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1051', '4', '330', '20', '3'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1051', '5', '330', '20', '28'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1051', '6', '330', '20', '36'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1052', '1', '330', '25', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1052', '2', '330', '25', '1'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1052', '3', '330', '25', '2'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1052', '4', '330', '25', '3'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1052', '5', '330', '25', '28'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1052', '6', '330', '25', '36'); + +-- CombatFury/Fury of the Ages [198% at Rank 3 + 3) +REPLACE 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`) VALUES ('443', 'Fury of the Ages', '3', '3', '4294967295', '4294967295', '5620', '5621', '5', '4294967295', '113', '3', '0', '0', '33722', '1', '62', '0', '4', '4294967295', '3', '0', '1', '113', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('113', '1', '169', '15', '-1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('114', '1', '169', '30', '-1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('115', '1', '169', '45', '-1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('443', '1', '169', '50', '-1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('444', '1', '169', '100', '-1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('445', '1', '169', '153', '-1'); + +-- Slay Undead/Vanquish Undead +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`) VALUES ('1524', 'Vanquish Undead', '3', '3', '4294967295', '4294967295', '1524', '1524', '3', '4294967295', '190', '3', '0', '0', '8', '0', '59', '3', '10', '4294967295', '3', '0', '1', '190', '1', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('190', '1', '219', '225', '680'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('191', '1', '219', '235', '725'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('192', '1', '219', '245', '770'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1524', '1', '219', '485', '815'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1525', '1', '219', '495', '860'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1526', '1', '219', '515', '905'); + +-- Elemental Fury +REPLACE 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`) VALUES ('790', 'Elemental Fury', '3', '5', '4294967295', '4294967295', '790', '790', '6', '0', '0', '0', '0', '0', '8192', '0', '65', '0', '7', '4294967295', '2', '0', '1', '790', '1', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('790', '1', '218', '1', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('791', '1', '218', '2', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('792', '1', '218', '3', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('793', '1', '218', '4', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('794', '1', '218', '5', '0'); + +-- Death's Fury +REPLACE 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`) VALUES ('834', 'Deaths Fury', '3', '5', '4294967295', '4294967295', '834', '834', '6', '0', '0', '0', '0', '0', '2048', '0', '65', '0', '7', '4294967295', '3', '0', '1', '834', '1', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('834', '1', '218', '1', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('835', '1', '218', '2', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('836', '1', '218', '3', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('837', '1', '218', '4', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('838', '1', '218', '5', '0'); + +-- Warder's Fury +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('724', '1', '218', '1', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('725', '1', '218', '2', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('726', '1', '218', '3', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('727', '1', '218', '4', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('728', '1', '218', '5', '0'); + +-- Compainion's Fury +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`) VALUES ('8201', 'Companion\'s Fury', '7', '3', '4294967295', '4294967295', '8201', '8201', '6', '4294967295', '0', '0', '0', '0', '60448', '0', '81', '0', '15', '4294967295', '2', '0', '1', '8201', '4', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8201', '1', '218', '1', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8202', '1', '218', '2', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8203', '1', '218', '3', '0'); + +-- Ancestral Aid +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`) VALUES ('1327', 'Ancestral Aid', '5', '3', '1327', '1327', '1327', '1327', '7', '5933', '0', '0', '2', '900', '1024', '0', '67', '0', '8', '4294967295', '3', '0', '1', '1327', '1', '0'); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES('1327', '0', '900', '5933', '2', '0', '0', '0', '0', '0', '0', '0'); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES ('1327', '1', '900', '5934', '2', '0', '0', '0', '0', '0', '0', '0'); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES ('1327', '2', '900', '5935', '2', '0', '0', '0', '0', '0', '0', '0'); + +-- Spirit Channeling +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`) VALUES ('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'); +INSERT INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES('1323', '0', '2160', '5932', '1', '0', '0', '0', '0', '0', '0', '0'); + diff --git a/utils/sql/svn/2183_optional_bot_xp_rule.sql b/utils/sql/svn/2183_optional_bot_xp_rule.sql new file mode 100644 index 000000000..60e483bf2 --- /dev/null +++ b/utils/sql/svn/2183_optional_bot_xp_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Bots:BotGroupXP', 'false', 'Determines whether client gets xp for bots outside their group.'); \ No newline at end of file diff --git a/utils/sql/svn/2185_optional_NPCFlurryChacne_rule b/utils/sql/svn/2185_optional_NPCFlurryChacne_rule new file mode 100644 index 000000000..5d39b80a2 --- /dev/null +++ b/utils/sql/svn/2185_optional_NPCFlurryChacne_rule @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:FrenzyBonus', '0', 'Modify Frenzy skill damage by percent,'); diff --git a/utils/sql/svn/2185_optional_NPCFlurryChacne_rule.sql b/utils/sql/svn/2185_optional_NPCFlurryChacne_rule.sql new file mode 100644 index 000000000..fc5cec311 --- /dev/null +++ b/utils/sql/svn/2185_optional_NPCFlurryChacne_rule.sql @@ -0,0 +1,290 @@ +-- Elemental Alacrity/Quickening Death/Warders Alacrity +REPLACE 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`) VALUES ('795', 'Elemental Alacrity', '5', '5', '4294967295', '4294967295', '795', '795', '6', '0', '0', '0', '0', '0', '8192', '0', '65', '0', '7', '4294967295', '3', '0', '1', '795', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('795', '1', '280', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('796', '1', '280', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('797', '1', '280', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('798', '1', '280', '8', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('799', '1', '280', '10', '0'); + +REPLACE 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`) VALUES ('839', 'Quickening of Death', '5', '5', '4294967295', '4294967295', '839', '839', '6', '0', '0', '0', '0', '0', '2048', '0', '65', '0', '7', '4294967295', '3', '0', '1', '839', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('839', '1', '280', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('840', '1', '280', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('841', '1', '280', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('842', '1', '280', '8', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('843', '1', '280', '10', '0'); + +REPLACE 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`) VALUES ('729', 'Warder\'s Alacrity', '3', '5', '4294967295', '4294967295', '9179', '9180', '6', '4294967295', '0', '0', '0', '0', '32768', '0', '65', '0', '7', '4294967295', '3', '0', '1', '729', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('729', '1', '280', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('730', '1', '280', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('731', '1', '280', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('732', '1', '280', '8', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('733', '1', '280', '10', '0'); + +-- Elemental Durability +REPLACE 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`) VALUES ('803', 'Elemental Durability', '3', '3', '4294967295', '4294967295', '803', '803', '6', '0', '0', '0', '0', '0', '8192', '0', '65', '3', '7', '4294967295', '3', '0', '1', '803', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('803', '1', '213', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('804', '1', '213', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('805', '1', '213', '10', '0'); + +-- Elemental Agility +REPLACE 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`) VALUES ('800', 'Elemental Agility', '3', '3', '4294967295', '4294967295', '800', '800', '6', '0', '0', '0', '0', '0', '8192', '0', '65', '3', '7', '4294967295', '3', '0', '1', '800', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('800', '1', '215', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('801', '1', '215', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('802', '1', '215', '10', '0'); + +-- Combat Agility/Lightning Reflexes/Reflexive Mastery/Precognition/CA-V +REPLACE 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`) VALUES ('125', 'Combat Agility', '2', '3', '4294967295', '4294967295', '13566', '13567', '2', '4294967295', '4294967295', '0', '0', '0', '65534', '1', '55', '2', '3', '4294967295', '2', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('125', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('126', '1', '172', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('127', '1', '172', '10', '0'); + +REPLACE 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`) VALUES ('449', 'Lightning Reflexes', '3', '5', '4294967295', '4294967295', '5636', '5637', '5', '4294967295', '126', '3', '0', '0', '65534', '1', '61', '0', '4', '4294967295', '2', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('449', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('450', '1', '172', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('451', '1', '172', '9', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('452', '1', '172', '12', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('453', '1', '172', '15', '0'); + +REPLACE 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`) VALUES ('1061', 'Reflexive Mastery', '5', '5', '4294967295', '4294967295', '30304', '30305', '7', '4294967295', '451', '5', '0', '0', '65534', '1', '66', '0', '8', '4294967295', '2', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1061', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1062', '1', '172', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1063', '1', '172', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1064', '1', '172', '8', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1065', '1', '172', '10', '0'); + +REPLACE 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`) VALUES ('1394', 'Precognition', '5', '5', '4294967295', '4294967295', '1394', '1394', '7', '4294967295', '1061', '5', '0', '0', '65534', '1', '70', '0', '10', '4294967295', '2', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1394', '1', '172', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1395', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1396', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1397', '1', '172', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1398', '1', '172', '5', '0'); + +REPLACE 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`) VALUES ('5519', 'Combat Agility V', '6', '5', '4294967295', '4294967295', '125', '125', '7', '4294967295', '1394', '5', '0', '0', '65534', '1', '76', '0', '14', '4294967295', '2', '0', '1', '125', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5519', '1', '172', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5520', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5521', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5522', '1', '172', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5523', '1', '172', '5', '0'); + +-- Physical Enchancement +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('279', '1', '214', '200', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('279', '2', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('279', '3', '172', '2', '0'); + +-- Natural Durability +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('107', '1', '214', '200', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('108', '1', '214', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('109', '1', '214', '1000', '0'); + +-- Precision of the Pathfinder +REPLACE 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`) VALUES ('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', '1', '864', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '1', '216', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '2', '216', '10', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '3', '216', '10', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '4', '216', '10', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '5', '216', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '6', '216', '10', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '7', '216', '10', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '8', '216', '10', '36'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '1', '216', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '2', '216', '20', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '3', '216', '20', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '4', '216', '20', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '5', '216', '20', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '6', '216', '20', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '7', '216', '20', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '8', '216', '20', '36'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '1', '216', '30', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '2', '216', '30', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '3', '216', '30', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '4', '216', '30', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '5', '216', '30', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '6', '216', '30', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '7', '216', '30', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '8', '216', '30', '36'); + +-- Scouts Efficiancy +REPLACE 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`) VALUES ('1290', 'Scout\'s Efficiency', '3', '3', '4294967295', '4294967295', '5519', '1290', '7', '4294967295', '864', '3', '0', '0', '16', '0', '70', '3', '8', '4294967295', '3', '0', '1', '864', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '1', '216', '40', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '2', '216', '40', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '3', '216', '40', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '4', '216', '40', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '5', '216', '40', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '6', '216', '40', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '7', '216', '40', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '8', '216', '40', '36'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '1', '216', '50', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '2', '216', '50', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '3', '216', '50', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '4', '216', '50', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '5', '216', '50', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '6', '216', '50', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '7', '216', '50', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '8', '216', '50', '36'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '1', '216', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '2', '216', '60', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '3', '216', '60', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '4', '216', '60', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '5', '216', '60', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '6', '216', '60', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '7', '216', '60', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '8', '216', '60', '36'); + +-- Dead Aim +REPLACE 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`) VALUES ('1140', 'Dead Aim', '3', '3', '4294967295', '4294967295', '1140', '1140', '5', '4294967295', '0', '0', '0', '0', '0', '1', '61', '0', '5', '4294967295', '3', '0', '1', '1140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1140', '1', '216', '10', '51'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1141', '1', '216', '20', '51'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1141', '1', '216', '30', '51'); + +-- Combat Stability/Innate Defense/Defensive Instincts/Thick Skin/CS V +REPLACE 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`) VALUES ('122', 'Combat Stability', '2', '3', '4294967295', '4294967295', '13564', '13565', '2', '4294967295', '4294967295', '0', '0', '0', '65534', '1', '55', '2', '3', '4294967295', '2', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('122', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('123', '1', '259', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('124', '1', '259', '10', '0'); + +REPLACE 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`) VALUES ('454', 'Innate Defense', '3', '5', '4294967295', '4294967295', '5638', '5639', '5', '4294967295', '122', '3', '0', '0', '65534', '1', '61', '0', '4', '4294967295', '2', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('454', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('455', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('456', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('457', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('458', '1', '259', '3', '0'); + +REPLACE 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`) VALUES ('1066', 'Defensive Instincts', '5', '5', '4294967295', '4294967295', '30324', '30325', '7', '4294967295', '456', '5', '0', '0', '65534', '1', '66', '0', '8', '4294967295', '2', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1066', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1067', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1068', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1069', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1070', '1', '259', '2', '0'); + +REPLACE 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`) VALUES ('1399', 'Thick Skin', '5', '5', '4294967295', '4294967295', '1399', '1399', '7', '4294967295', '1066', '5', '0', '0', '65534', '1', '70', '0', '10', '4294967295', '2', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1399', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1400', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1401', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1402', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1403', '1', '259', '2', '0'); + +REPLACE 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`) VALUES ('5524', 'Combat Stability V', '6', '5', '4294967295', '4294967295', '122', '122', '7', '4294967295', '1399', '5', '0', '0', '65534', '1', '76', '0', '14', '4294967295', '2', '0', '1', '122', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5524', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5525', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5526', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5527', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5528', '1', '259', '1', '0'); + +-- Double Riposte +REPLACE 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`) VALUES ('247', 'Double Riposte', '3', '3', '4294967295', '4294967295', '13775', '13776', '3', '4294967295', '4294967295', '0', '0', '0', '33466', '1', '59', '3', '3', '4294967295', '3', '0', '1', '247', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('247', '1', '224', '15', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('248', '1', '224', '35', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('249', '1', '224', '50', '0'); + +-- Flash of Steel +REPLACE 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`) VALUES ('504', 'Flash of Steel', '3', '3', '4294967295', '4294967295', '5622', '5623', '5', '4294967295', '249', '3', '0', '0', '33466', '1', '62', '0', '4', '4294967295', '3', '0', '1', '247', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('504', '1', '224', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('505', '1', '224', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('506', '1', '224', '80', '0'); + +-- Return Kick +REPLACE 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`) VALUES ('240', 'Return Kick', '3', '3', '4294967295', '4294967295', '13767', '13768', '3', '4294967295', '0', '0', '0', '0', '128', '0', '59', '3', '3', '4294967295', '3', '0', '1', '240', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('240', '1', '224', '20', '26'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('240', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('241', '1', '224', '40', '26'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('241', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('242', '1', '224', '60', '26'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('242', '2', '173', '3', '0'); + +-- Warlord's Return Kick +REPLACE 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`) VALUES ('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', '1', '6566', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6287', '1', '224', '20', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6287', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6288', '1', '224', '40', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6288', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6289', '1', '224', '60', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6289', '2', '173', '3', '0'); + +-- Beastlords Feral Kick +REPLACE 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`) VALUES ('7106', 'Beastlords Feral Kick', '6', '3', '4294967295', '4294967295', '7106', '7106', '3', '4294967295', '0', '0', '0', '0', '32768', '0', '73', '0', '14', '4294967295', '3', '0', '1', '7106', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7106', '1', '224', '20', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7106', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7107', '1', '224', '40', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7107', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7108', '1', '224', '60', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7108', '2', '173', '3', '0'); + +-- Hunter's Return Kick +REPLACE 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`) VALUES ('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', '1', '6558', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6269', '1', '224', '20', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6269', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6270', '1', '224', '40', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6270', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6271', '1', '224', '60', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6271', '2', '173', '3', '0'); + +-- Knight's Return Strike +REPLACE 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`) VALUES ('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', '1', '6557', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6266', '1', '224', '20', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6266', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6267', '1', '224', '40', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6267', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6268', '1', '224', '60', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6268', '2', '173', '3', '0'); + +-- Frenzied Defense +REPLACE 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`) VALUES ('1166', 'Frenzied Defense', '3', '3', '4294967295', '4294967295', '1143', '1143', '3', '4294967295', '0', '0', '0', '0', '0', '1', '65', '0', '15', '4294967295', '3', '0', '1', '1143', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1143', '1', '224', '20', '74'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1143', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1144', '1', '224', '40', '74'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1144', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1145', '1', '224', '60', '74'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1145', '2', '173', '3', '0'); + +-- Knave's Return Strike +REPLACE 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`) VALUES ('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', '1', '6560', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6275', '1', '224', '20', '8'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6275', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6276', '1', '224', '40', '8'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6276', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6277', '1', '224', '60', '8'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6277', '2', '173', '3', '0'); + +-- Flurry / Raging Flurry / Crazed Onslaught/ Flurry IV +REPLACE 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`) VALUES ('255', 'Flurry', '3', '3', '4294967295', '4294967295', '13785', '13786', '3', '4294967295', '113', '3', '0', '0', '2', '1', '59', '3', '3', '4294967295', '3', '0', '1', '255', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('255', '1', '279', '7', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('256', '1', '279', '11', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('257', '1', '279', '15', '0'); + +REPLACE 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`) VALUES ('542', 'Raging Flurry', '2', '3', '4294967295', '4294967295', '5644', '5645', '5', '4294967295', '255', '3', '0', '0', '2', '1', '63', '2', '4', '4294967295', '3', '0', '1', '255', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('542', '1', '279', '17', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('543', '1', '279', '19', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('544', '1', '279', '21', '0'); + +REPLACE 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`) VALUES ('1163', 'Crazed Onslaught', '5', '3', '4294967295', '4294967295', '116', '116', '7', '4294967295', '542', '3', '0', '0', '2', '1', '67', '0', '8', '4294967295', '3', '0', '1', '255', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1163', '1', '279', '23', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1164', '1', '279', '25', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1165', '1', '279', '27', '0'); + +REPLACE 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`) VALUES ('4795', 'Flurry IV', '5', '3', '4294967295', '4294967295', '255', '255', '7', '4294967295', '1163', '3', '0', '0', '2', '1', '71', '0', '12', '4294967295', '3', '0', '1', '255', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4795', '1', '279', '28', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4796', '1', '279', '29', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4797', '1', '279', '30', '0'); + +-- Rapid Strikes / Need to figure out ID / AA not implemented / AA not implemented +REPLACE 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`) VALUES ('815', 'Rapid Strikes', '4', '5', '4294967295', '4294967295', '815', '815', '6', '0', '0', '0', '0', '0', '128', '0', '65', '0', '7', '4294967295', '3', '0', '1', '815', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('815', '1', '279', '7', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('816', '1', '279', '11', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('817', '1', '279', '15', '0'); + +-- Lightning Strikes / ? / ? / ? +REPLACE 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`) VALUES ('1616', 'Lightning Strikes', '5', '3', '4294967295', '4294967295', '13785', '13786', '7', '4294967295', '0', '0', '0', '0', '16', '1', '66', '3', '10', '4294967295', '3', '0', '1', '1616', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1616', '1', '279', '7', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1617', '1', '279', '11', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1618', '1', '279', '15', '0'); + +-- Ambidexterity +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('198', '1', '276', '32', '0'); + +-- Misc display fix. +UPDATE `altadv_vars` SET `sof_type` = 2 WHERE `skill_id` = 443; +UPDATE `altadv_vars` SET `sof_type` = 3 WHERE `skill_id` = 790; diff --git a/utils/sql/svn/2185_optional_NPCFlurryChance_rule.sql b/utils/sql/svn/2185_optional_NPCFlurryChance_rule.sql new file mode 100644 index 000000000..059e88e3d --- /dev/null +++ b/utils/sql/svn/2185_optional_NPCFlurryChance_rule.sql @@ -0,0 +1 @@ +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:NPCFlurryChance', '20', '(Default=20) Chance for NPC to flurry if special attack F '); diff --git a/utils/sql/svn/2185_required_aa_updates b/utils/sql/svn/2185_required_aa_updates new file mode 100644 index 000000000..fc5cec311 --- /dev/null +++ b/utils/sql/svn/2185_required_aa_updates @@ -0,0 +1,290 @@ +-- Elemental Alacrity/Quickening Death/Warders Alacrity +REPLACE 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`) VALUES ('795', 'Elemental Alacrity', '5', '5', '4294967295', '4294967295', '795', '795', '6', '0', '0', '0', '0', '0', '8192', '0', '65', '0', '7', '4294967295', '3', '0', '1', '795', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('795', '1', '280', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('796', '1', '280', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('797', '1', '280', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('798', '1', '280', '8', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('799', '1', '280', '10', '0'); + +REPLACE 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`) VALUES ('839', 'Quickening of Death', '5', '5', '4294967295', '4294967295', '839', '839', '6', '0', '0', '0', '0', '0', '2048', '0', '65', '0', '7', '4294967295', '3', '0', '1', '839', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('839', '1', '280', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('840', '1', '280', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('841', '1', '280', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('842', '1', '280', '8', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('843', '1', '280', '10', '0'); + +REPLACE 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`) VALUES ('729', 'Warder\'s Alacrity', '3', '5', '4294967295', '4294967295', '9179', '9180', '6', '4294967295', '0', '0', '0', '0', '32768', '0', '65', '0', '7', '4294967295', '3', '0', '1', '729', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('729', '1', '280', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('730', '1', '280', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('731', '1', '280', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('732', '1', '280', '8', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('733', '1', '280', '10', '0'); + +-- Elemental Durability +REPLACE 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`) VALUES ('803', 'Elemental Durability', '3', '3', '4294967295', '4294967295', '803', '803', '6', '0', '0', '0', '0', '0', '8192', '0', '65', '3', '7', '4294967295', '3', '0', '1', '803', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('803', '1', '213', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('804', '1', '213', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('805', '1', '213', '10', '0'); + +-- Elemental Agility +REPLACE 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`) VALUES ('800', 'Elemental Agility', '3', '3', '4294967295', '4294967295', '800', '800', '6', '0', '0', '0', '0', '0', '8192', '0', '65', '3', '7', '4294967295', '3', '0', '1', '800', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('800', '1', '215', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('801', '1', '215', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('802', '1', '215', '10', '0'); + +-- Combat Agility/Lightning Reflexes/Reflexive Mastery/Precognition/CA-V +REPLACE 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`) VALUES ('125', 'Combat Agility', '2', '3', '4294967295', '4294967295', '13566', '13567', '2', '4294967295', '4294967295', '0', '0', '0', '65534', '1', '55', '2', '3', '4294967295', '2', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('125', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('126', '1', '172', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('127', '1', '172', '10', '0'); + +REPLACE 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`) VALUES ('449', 'Lightning Reflexes', '3', '5', '4294967295', '4294967295', '5636', '5637', '5', '4294967295', '126', '3', '0', '0', '65534', '1', '61', '0', '4', '4294967295', '2', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('449', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('450', '1', '172', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('451', '1', '172', '9', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('452', '1', '172', '12', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('453', '1', '172', '15', '0'); + +REPLACE 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`) VALUES ('1061', 'Reflexive Mastery', '5', '5', '4294967295', '4294967295', '30304', '30305', '7', '4294967295', '451', '5', '0', '0', '65534', '1', '66', '0', '8', '4294967295', '2', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1061', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1062', '1', '172', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1063', '1', '172', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1064', '1', '172', '8', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1065', '1', '172', '10', '0'); + +REPLACE 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`) VALUES ('1394', 'Precognition', '5', '5', '4294967295', '4294967295', '1394', '1394', '7', '4294967295', '1061', '5', '0', '0', '65534', '1', '70', '0', '10', '4294967295', '2', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1394', '1', '172', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1395', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1396', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1397', '1', '172', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1398', '1', '172', '5', '0'); + +REPLACE 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`) VALUES ('5519', 'Combat Agility V', '6', '5', '4294967295', '4294967295', '125', '125', '7', '4294967295', '1394', '5', '0', '0', '65534', '1', '76', '0', '14', '4294967295', '2', '0', '1', '125', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5519', '1', '172', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5520', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5521', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5522', '1', '172', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5523', '1', '172', '5', '0'); + +-- Physical Enchancement +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('279', '1', '214', '200', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('279', '2', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('279', '3', '172', '2', '0'); + +-- Natural Durability +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('107', '1', '214', '200', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('108', '1', '214', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('109', '1', '214', '1000', '0'); + +-- Precision of the Pathfinder +REPLACE 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`) VALUES ('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', '1', '864', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '1', '216', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '2', '216', '10', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '3', '216', '10', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '4', '216', '10', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '5', '216', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '6', '216', '10', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '7', '216', '10', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '8', '216', '10', '36'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '1', '216', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '2', '216', '20', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '3', '216', '20', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '4', '216', '20', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '5', '216', '20', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '6', '216', '20', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '7', '216', '20', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '8', '216', '20', '36'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '1', '216', '30', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '2', '216', '30', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '3', '216', '30', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '4', '216', '30', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '5', '216', '30', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '6', '216', '30', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '7', '216', '30', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '8', '216', '30', '36'); + +-- Scouts Efficiancy +REPLACE 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`) VALUES ('1290', 'Scout\'s Efficiency', '3', '3', '4294967295', '4294967295', '5519', '1290', '7', '4294967295', '864', '3', '0', '0', '16', '0', '70', '3', '8', '4294967295', '3', '0', '1', '864', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '1', '216', '40', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '2', '216', '40', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '3', '216', '40', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '4', '216', '40', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '5', '216', '40', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '6', '216', '40', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '7', '216', '40', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '8', '216', '40', '36'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '1', '216', '50', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '2', '216', '50', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '3', '216', '50', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '4', '216', '50', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '5', '216', '50', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '6', '216', '50', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '7', '216', '50', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '8', '216', '50', '36'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '1', '216', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '2', '216', '60', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '3', '216', '60', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '4', '216', '60', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '5', '216', '60', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '6', '216', '60', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '7', '216', '60', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '8', '216', '60', '36'); + +-- Dead Aim +REPLACE 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`) VALUES ('1140', 'Dead Aim', '3', '3', '4294967295', '4294967295', '1140', '1140', '5', '4294967295', '0', '0', '0', '0', '0', '1', '61', '0', '5', '4294967295', '3', '0', '1', '1140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1140', '1', '216', '10', '51'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1141', '1', '216', '20', '51'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1141', '1', '216', '30', '51'); + +-- Combat Stability/Innate Defense/Defensive Instincts/Thick Skin/CS V +REPLACE 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`) VALUES ('122', 'Combat Stability', '2', '3', '4294967295', '4294967295', '13564', '13565', '2', '4294967295', '4294967295', '0', '0', '0', '65534', '1', '55', '2', '3', '4294967295', '2', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('122', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('123', '1', '259', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('124', '1', '259', '10', '0'); + +REPLACE 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`) VALUES ('454', 'Innate Defense', '3', '5', '4294967295', '4294967295', '5638', '5639', '5', '4294967295', '122', '3', '0', '0', '65534', '1', '61', '0', '4', '4294967295', '2', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('454', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('455', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('456', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('457', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('458', '1', '259', '3', '0'); + +REPLACE 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`) VALUES ('1066', 'Defensive Instincts', '5', '5', '4294967295', '4294967295', '30324', '30325', '7', '4294967295', '456', '5', '0', '0', '65534', '1', '66', '0', '8', '4294967295', '2', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1066', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1067', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1068', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1069', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1070', '1', '259', '2', '0'); + +REPLACE 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`) VALUES ('1399', 'Thick Skin', '5', '5', '4294967295', '4294967295', '1399', '1399', '7', '4294967295', '1066', '5', '0', '0', '65534', '1', '70', '0', '10', '4294967295', '2', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1399', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1400', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1401', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1402', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1403', '1', '259', '2', '0'); + +REPLACE 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`) VALUES ('5524', 'Combat Stability V', '6', '5', '4294967295', '4294967295', '122', '122', '7', '4294967295', '1399', '5', '0', '0', '65534', '1', '76', '0', '14', '4294967295', '2', '0', '1', '122', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5524', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5525', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5526', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5527', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5528', '1', '259', '1', '0'); + +-- Double Riposte +REPLACE 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`) VALUES ('247', 'Double Riposte', '3', '3', '4294967295', '4294967295', '13775', '13776', '3', '4294967295', '4294967295', '0', '0', '0', '33466', '1', '59', '3', '3', '4294967295', '3', '0', '1', '247', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('247', '1', '224', '15', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('248', '1', '224', '35', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('249', '1', '224', '50', '0'); + +-- Flash of Steel +REPLACE 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`) VALUES ('504', 'Flash of Steel', '3', '3', '4294967295', '4294967295', '5622', '5623', '5', '4294967295', '249', '3', '0', '0', '33466', '1', '62', '0', '4', '4294967295', '3', '0', '1', '247', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('504', '1', '224', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('505', '1', '224', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('506', '1', '224', '80', '0'); + +-- Return Kick +REPLACE 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`) VALUES ('240', 'Return Kick', '3', '3', '4294967295', '4294967295', '13767', '13768', '3', '4294967295', '0', '0', '0', '0', '128', '0', '59', '3', '3', '4294967295', '3', '0', '1', '240', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('240', '1', '224', '20', '26'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('240', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('241', '1', '224', '40', '26'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('241', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('242', '1', '224', '60', '26'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('242', '2', '173', '3', '0'); + +-- Warlord's Return Kick +REPLACE 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`) VALUES ('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', '1', '6566', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6287', '1', '224', '20', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6287', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6288', '1', '224', '40', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6288', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6289', '1', '224', '60', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6289', '2', '173', '3', '0'); + +-- Beastlords Feral Kick +REPLACE 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`) VALUES ('7106', 'Beastlords Feral Kick', '6', '3', '4294967295', '4294967295', '7106', '7106', '3', '4294967295', '0', '0', '0', '0', '32768', '0', '73', '0', '14', '4294967295', '3', '0', '1', '7106', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7106', '1', '224', '20', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7106', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7107', '1', '224', '40', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7107', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7108', '1', '224', '60', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7108', '2', '173', '3', '0'); + +-- Hunter's Return Kick +REPLACE 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`) VALUES ('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', '1', '6558', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6269', '1', '224', '20', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6269', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6270', '1', '224', '40', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6270', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6271', '1', '224', '60', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6271', '2', '173', '3', '0'); + +-- Knight's Return Strike +REPLACE 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`) VALUES ('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', '1', '6557', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6266', '1', '224', '20', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6266', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6267', '1', '224', '40', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6267', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6268', '1', '224', '60', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6268', '2', '173', '3', '0'); + +-- Frenzied Defense +REPLACE 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`) VALUES ('1166', 'Frenzied Defense', '3', '3', '4294967295', '4294967295', '1143', '1143', '3', '4294967295', '0', '0', '0', '0', '0', '1', '65', '0', '15', '4294967295', '3', '0', '1', '1143', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1143', '1', '224', '20', '74'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1143', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1144', '1', '224', '40', '74'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1144', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1145', '1', '224', '60', '74'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1145', '2', '173', '3', '0'); + +-- Knave's Return Strike +REPLACE 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`) VALUES ('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', '1', '6560', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6275', '1', '224', '20', '8'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6275', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6276', '1', '224', '40', '8'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6276', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6277', '1', '224', '60', '8'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6277', '2', '173', '3', '0'); + +-- Flurry / Raging Flurry / Crazed Onslaught/ Flurry IV +REPLACE 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`) VALUES ('255', 'Flurry', '3', '3', '4294967295', '4294967295', '13785', '13786', '3', '4294967295', '113', '3', '0', '0', '2', '1', '59', '3', '3', '4294967295', '3', '0', '1', '255', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('255', '1', '279', '7', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('256', '1', '279', '11', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('257', '1', '279', '15', '0'); + +REPLACE 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`) VALUES ('542', 'Raging Flurry', '2', '3', '4294967295', '4294967295', '5644', '5645', '5', '4294967295', '255', '3', '0', '0', '2', '1', '63', '2', '4', '4294967295', '3', '0', '1', '255', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('542', '1', '279', '17', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('543', '1', '279', '19', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('544', '1', '279', '21', '0'); + +REPLACE 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`) VALUES ('1163', 'Crazed Onslaught', '5', '3', '4294967295', '4294967295', '116', '116', '7', '4294967295', '542', '3', '0', '0', '2', '1', '67', '0', '8', '4294967295', '3', '0', '1', '255', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1163', '1', '279', '23', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1164', '1', '279', '25', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1165', '1', '279', '27', '0'); + +REPLACE 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`) VALUES ('4795', 'Flurry IV', '5', '3', '4294967295', '4294967295', '255', '255', '7', '4294967295', '1163', '3', '0', '0', '2', '1', '71', '0', '12', '4294967295', '3', '0', '1', '255', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4795', '1', '279', '28', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4796', '1', '279', '29', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4797', '1', '279', '30', '0'); + +-- Rapid Strikes / Need to figure out ID / AA not implemented / AA not implemented +REPLACE 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`) VALUES ('815', 'Rapid Strikes', '4', '5', '4294967295', '4294967295', '815', '815', '6', '0', '0', '0', '0', '0', '128', '0', '65', '0', '7', '4294967295', '3', '0', '1', '815', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('815', '1', '279', '7', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('816', '1', '279', '11', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('817', '1', '279', '15', '0'); + +-- Lightning Strikes / ? / ? / ? +REPLACE 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`) VALUES ('1616', 'Lightning Strikes', '5', '3', '4294967295', '4294967295', '13785', '13786', '7', '4294967295', '0', '0', '0', '0', '16', '1', '66', '3', '10', '4294967295', '3', '0', '1', '1616', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1616', '1', '279', '7', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1617', '1', '279', '11', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1618', '1', '279', '15', '0'); + +-- Ambidexterity +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('198', '1', '276', '32', '0'); + +-- Misc display fix. +UPDATE `altadv_vars` SET `sof_type` = 2 WHERE `skill_id` = 443; +UPDATE `altadv_vars` SET `sof_type` = 3 WHERE `skill_id` = 790; diff --git a/utils/sql/svn/2185_required_aa_updates.sql b/utils/sql/svn/2185_required_aa_updates.sql new file mode 100644 index 000000000..fc5cec311 --- /dev/null +++ b/utils/sql/svn/2185_required_aa_updates.sql @@ -0,0 +1,290 @@ +-- Elemental Alacrity/Quickening Death/Warders Alacrity +REPLACE 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`) VALUES ('795', 'Elemental Alacrity', '5', '5', '4294967295', '4294967295', '795', '795', '6', '0', '0', '0', '0', '0', '8192', '0', '65', '0', '7', '4294967295', '3', '0', '1', '795', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('795', '1', '280', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('796', '1', '280', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('797', '1', '280', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('798', '1', '280', '8', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('799', '1', '280', '10', '0'); + +REPLACE 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`) VALUES ('839', 'Quickening of Death', '5', '5', '4294967295', '4294967295', '839', '839', '6', '0', '0', '0', '0', '0', '2048', '0', '65', '0', '7', '4294967295', '3', '0', '1', '839', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('839', '1', '280', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('840', '1', '280', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('841', '1', '280', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('842', '1', '280', '8', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('843', '1', '280', '10', '0'); + +REPLACE 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`) VALUES ('729', 'Warder\'s Alacrity', '3', '5', '4294967295', '4294967295', '9179', '9180', '6', '4294967295', '0', '0', '0', '0', '32768', '0', '65', '0', '7', '4294967295', '3', '0', '1', '729', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('729', '1', '280', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('730', '1', '280', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('731', '1', '280', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('732', '1', '280', '8', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('733', '1', '280', '10', '0'); + +-- Elemental Durability +REPLACE 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`) VALUES ('803', 'Elemental Durability', '3', '3', '4294967295', '4294967295', '803', '803', '6', '0', '0', '0', '0', '0', '8192', '0', '65', '3', '7', '4294967295', '3', '0', '1', '803', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('803', '1', '213', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('804', '1', '213', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('805', '1', '213', '10', '0'); + +-- Elemental Agility +REPLACE 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`) VALUES ('800', 'Elemental Agility', '3', '3', '4294967295', '4294967295', '800', '800', '6', '0', '0', '0', '0', '0', '8192', '0', '65', '3', '7', '4294967295', '3', '0', '1', '800', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('800', '1', '215', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('801', '1', '215', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('802', '1', '215', '10', '0'); + +-- Combat Agility/Lightning Reflexes/Reflexive Mastery/Precognition/CA-V +REPLACE 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`) VALUES ('125', 'Combat Agility', '2', '3', '4294967295', '4294967295', '13566', '13567', '2', '4294967295', '4294967295', '0', '0', '0', '65534', '1', '55', '2', '3', '4294967295', '2', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('125', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('126', '1', '172', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('127', '1', '172', '10', '0'); + +REPLACE 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`) VALUES ('449', 'Lightning Reflexes', '3', '5', '4294967295', '4294967295', '5636', '5637', '5', '4294967295', '126', '3', '0', '0', '65534', '1', '61', '0', '4', '4294967295', '2', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('449', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('450', '1', '172', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('451', '1', '172', '9', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('452', '1', '172', '12', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('453', '1', '172', '15', '0'); + +REPLACE 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`) VALUES ('1061', 'Reflexive Mastery', '5', '5', '4294967295', '4294967295', '30304', '30305', '7', '4294967295', '451', '5', '0', '0', '65534', '1', '66', '0', '8', '4294967295', '2', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1061', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1062', '1', '172', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1063', '1', '172', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1064', '1', '172', '8', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1065', '1', '172', '10', '0'); + +REPLACE 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`) VALUES ('1394', 'Precognition', '5', '5', '4294967295', '4294967295', '1394', '1394', '7', '4294967295', '1061', '5', '0', '0', '65534', '1', '70', '0', '10', '4294967295', '2', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1394', '1', '172', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1395', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1396', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1397', '1', '172', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1398', '1', '172', '5', '0'); + +REPLACE 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`) VALUES ('5519', 'Combat Agility V', '6', '5', '4294967295', '4294967295', '125', '125', '7', '4294967295', '1394', '5', '0', '0', '65534', '1', '76', '0', '14', '4294967295', '2', '0', '1', '125', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5519', '1', '172', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5520', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5521', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5522', '1', '172', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5523', '1', '172', '5', '0'); + +-- Physical Enchancement +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('279', '1', '214', '200', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('279', '2', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('279', '3', '172', '2', '0'); + +-- Natural Durability +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('107', '1', '214', '200', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('108', '1', '214', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('109', '1', '214', '1000', '0'); + +-- Precision of the Pathfinder +REPLACE 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`) VALUES ('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', '1', '864', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '1', '216', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '2', '216', '10', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '3', '216', '10', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '4', '216', '10', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '5', '216', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '6', '216', '10', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '7', '216', '10', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('864', '8', '216', '10', '36'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '1', '216', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '2', '216', '20', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '3', '216', '20', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '4', '216', '20', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '5', '216', '20', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '6', '216', '20', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '7', '216', '20', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('865', '8', '216', '20', '36'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '1', '216', '30', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '2', '216', '30', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '3', '216', '30', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '4', '216', '30', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '5', '216', '30', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '6', '216', '30', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '7', '216', '30', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('866', '8', '216', '30', '36'); + +-- Scouts Efficiancy +REPLACE 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`) VALUES ('1290', 'Scout\'s Efficiency', '3', '3', '4294967295', '4294967295', '5519', '1290', '7', '4294967295', '864', '3', '0', '0', '16', '0', '70', '3', '8', '4294967295', '3', '0', '1', '864', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '1', '216', '40', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '2', '216', '40', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '3', '216', '40', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '4', '216', '40', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '5', '216', '40', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '6', '216', '40', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '7', '216', '40', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1290', '8', '216', '40', '36'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '1', '216', '50', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '2', '216', '50', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '3', '216', '50', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '4', '216', '50', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '5', '216', '50', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '6', '216', '50', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '7', '216', '50', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1291', '8', '216', '50', '36'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '1', '216', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '2', '216', '60', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '3', '216', '60', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '4', '216', '60', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '5', '216', '60', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '6', '216', '60', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '7', '216', '60', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1292', '8', '216', '60', '36'); + +-- Dead Aim +REPLACE 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`) VALUES ('1140', 'Dead Aim', '3', '3', '4294967295', '4294967295', '1140', '1140', '5', '4294967295', '0', '0', '0', '0', '0', '1', '61', '0', '5', '4294967295', '3', '0', '1', '1140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1140', '1', '216', '10', '51'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1141', '1', '216', '20', '51'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1141', '1', '216', '30', '51'); + +-- Combat Stability/Innate Defense/Defensive Instincts/Thick Skin/CS V +REPLACE 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`) VALUES ('122', 'Combat Stability', '2', '3', '4294967295', '4294967295', '13564', '13565', '2', '4294967295', '4294967295', '0', '0', '0', '65534', '1', '55', '2', '3', '4294967295', '2', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('122', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('123', '1', '259', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('124', '1', '259', '10', '0'); + +REPLACE 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`) VALUES ('454', 'Innate Defense', '3', '5', '4294967295', '4294967295', '5638', '5639', '5', '4294967295', '122', '3', '0', '0', '65534', '1', '61', '0', '4', '4294967295', '2', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('454', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('455', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('456', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('457', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('458', '1', '259', '3', '0'); + +REPLACE 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`) VALUES ('1066', 'Defensive Instincts', '5', '5', '4294967295', '4294967295', '30324', '30325', '7', '4294967295', '456', '5', '0', '0', '65534', '1', '66', '0', '8', '4294967295', '2', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1066', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1067', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1068', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1069', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1070', '1', '259', '2', '0'); + +REPLACE 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`) VALUES ('1399', 'Thick Skin', '5', '5', '4294967295', '4294967295', '1399', '1399', '7', '4294967295', '1066', '5', '0', '0', '65534', '1', '70', '0', '10', '4294967295', '2', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1399', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1400', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1401', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1402', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1403', '1', '259', '2', '0'); + +REPLACE 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`) VALUES ('5524', 'Combat Stability V', '6', '5', '4294967295', '4294967295', '122', '122', '7', '4294967295', '1399', '5', '0', '0', '65534', '1', '76', '0', '14', '4294967295', '2', '0', '1', '122', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5524', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5525', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5526', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5527', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5528', '1', '259', '1', '0'); + +-- Double Riposte +REPLACE 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`) VALUES ('247', 'Double Riposte', '3', '3', '4294967295', '4294967295', '13775', '13776', '3', '4294967295', '4294967295', '0', '0', '0', '33466', '1', '59', '3', '3', '4294967295', '3', '0', '1', '247', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('247', '1', '224', '15', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('248', '1', '224', '35', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('249', '1', '224', '50', '0'); + +-- Flash of Steel +REPLACE 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`) VALUES ('504', 'Flash of Steel', '3', '3', '4294967295', '4294967295', '5622', '5623', '5', '4294967295', '249', '3', '0', '0', '33466', '1', '62', '0', '4', '4294967295', '3', '0', '1', '247', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('504', '1', '224', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('505', '1', '224', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('506', '1', '224', '80', '0'); + +-- Return Kick +REPLACE 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`) VALUES ('240', 'Return Kick', '3', '3', '4294967295', '4294967295', '13767', '13768', '3', '4294967295', '0', '0', '0', '0', '128', '0', '59', '3', '3', '4294967295', '3', '0', '1', '240', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('240', '1', '224', '20', '26'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('240', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('241', '1', '224', '40', '26'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('241', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('242', '1', '224', '60', '26'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('242', '2', '173', '3', '0'); + +-- Warlord's Return Kick +REPLACE 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`) VALUES ('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', '1', '6566', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6287', '1', '224', '20', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6287', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6288', '1', '224', '40', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6288', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6289', '1', '224', '60', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6289', '2', '173', '3', '0'); + +-- Beastlords Feral Kick +REPLACE 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`) VALUES ('7106', 'Beastlords Feral Kick', '6', '3', '4294967295', '4294967295', '7106', '7106', '3', '4294967295', '0', '0', '0', '0', '32768', '0', '73', '0', '14', '4294967295', '3', '0', '1', '7106', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7106', '1', '224', '20', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7106', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7107', '1', '224', '40', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7107', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7108', '1', '224', '60', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7108', '2', '173', '3', '0'); + +-- Hunter's Return Kick +REPLACE 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`) VALUES ('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', '1', '6558', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6269', '1', '224', '20', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6269', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6270', '1', '224', '40', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6270', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6271', '1', '224', '60', '30'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6271', '2', '173', '3', '0'); + +-- Knight's Return Strike +REPLACE 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`) VALUES ('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', '1', '6557', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6266', '1', '224', '20', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6266', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6267', '1', '224', '40', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6267', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6268', '1', '224', '60', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6268', '2', '173', '3', '0'); + +-- Frenzied Defense +REPLACE 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`) VALUES ('1166', 'Frenzied Defense', '3', '3', '4294967295', '4294967295', '1143', '1143', '3', '4294967295', '0', '0', '0', '0', '0', '1', '65', '0', '15', '4294967295', '3', '0', '1', '1143', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1143', '1', '224', '20', '74'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1143', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1144', '1', '224', '40', '74'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1144', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1145', '1', '224', '60', '74'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1145', '2', '173', '3', '0'); + +-- Knave's Return Strike +REPLACE 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`) VALUES ('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', '1', '6560', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6275', '1', '224', '20', '8'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6275', '2', '173', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6276', '1', '224', '40', '8'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6276', '2', '173', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6277', '1', '224', '60', '8'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6277', '2', '173', '3', '0'); + +-- Flurry / Raging Flurry / Crazed Onslaught/ Flurry IV +REPLACE 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`) VALUES ('255', 'Flurry', '3', '3', '4294967295', '4294967295', '13785', '13786', '3', '4294967295', '113', '3', '0', '0', '2', '1', '59', '3', '3', '4294967295', '3', '0', '1', '255', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('255', '1', '279', '7', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('256', '1', '279', '11', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('257', '1', '279', '15', '0'); + +REPLACE 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`) VALUES ('542', 'Raging Flurry', '2', '3', '4294967295', '4294967295', '5644', '5645', '5', '4294967295', '255', '3', '0', '0', '2', '1', '63', '2', '4', '4294967295', '3', '0', '1', '255', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('542', '1', '279', '17', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('543', '1', '279', '19', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('544', '1', '279', '21', '0'); + +REPLACE 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`) VALUES ('1163', 'Crazed Onslaught', '5', '3', '4294967295', '4294967295', '116', '116', '7', '4294967295', '542', '3', '0', '0', '2', '1', '67', '0', '8', '4294967295', '3', '0', '1', '255', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1163', '1', '279', '23', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1164', '1', '279', '25', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1165', '1', '279', '27', '0'); + +REPLACE 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`) VALUES ('4795', 'Flurry IV', '5', '3', '4294967295', '4294967295', '255', '255', '7', '4294967295', '1163', '3', '0', '0', '2', '1', '71', '0', '12', '4294967295', '3', '0', '1', '255', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4795', '1', '279', '28', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4796', '1', '279', '29', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4797', '1', '279', '30', '0'); + +-- Rapid Strikes / Need to figure out ID / AA not implemented / AA not implemented +REPLACE 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`) VALUES ('815', 'Rapid Strikes', '4', '5', '4294967295', '4294967295', '815', '815', '6', '0', '0', '0', '0', '0', '128', '0', '65', '0', '7', '4294967295', '3', '0', '1', '815', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('815', '1', '279', '7', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('816', '1', '279', '11', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('817', '1', '279', '15', '0'); + +-- Lightning Strikes / ? / ? / ? +REPLACE 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`) VALUES ('1616', 'Lightning Strikes', '5', '3', '4294967295', '4294967295', '13785', '13786', '7', '4294967295', '0', '0', '0', '0', '16', '1', '66', '3', '10', '4294967295', '3', '0', '1', '1616', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1616', '1', '279', '7', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1617', '1', '279', '11', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1618', '1', '279', '15', '0'); + +-- Ambidexterity +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('198', '1', '276', '32', '0'); + +-- Misc display fix. +UPDATE `altadv_vars` SET `sof_type` = 2 WHERE `skill_id` = 443; +UPDATE `altadv_vars` SET `sof_type` = 3 WHERE `skill_id` = 790; diff --git a/utils/sql/svn/2188_optional_miscspelleffect_rules b/utils/sql/svn/2188_optional_miscspelleffect_rules new file mode 100644 index 000000000..837cd9f53 --- /dev/null +++ b/utils/sql/svn/2188_optional_miscspelleffect_rules @@ -0,0 +1,4 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:MaxCastTimeReduction', '50', 'Max percent your spell cast time can be reduced by spell haste.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:RootBreakFromSpells', '20', 'Chance for root to break when cast on.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:DeathSaveCharismaMod', '3', 'Determines how much charisma effects chance of death save firing.(CHA*value/10)'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:DivineInterventionHeal', '8000', 'Divine intervention heal amount.'); diff --git a/utils/sql/svn/2188_optional_miscspelleffect_rules.sql b/utils/sql/svn/2188_optional_miscspelleffect_rules.sql new file mode 100644 index 000000000..837cd9f53 --- /dev/null +++ b/utils/sql/svn/2188_optional_miscspelleffect_rules.sql @@ -0,0 +1,4 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:MaxCastTimeReduction', '50', 'Max percent your spell cast time can be reduced by spell haste.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:RootBreakFromSpells', '20', 'Chance for root to break when cast on.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:DeathSaveCharismaMod', '3', 'Determines how much charisma effects chance of death save firing.(CHA*value/10)'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:DivineInterventionHeal', '8000', 'Divine intervention heal amount.'); diff --git a/utils/sql/svn/2188_required_aa_updates b/utils/sql/svn/2188_required_aa_updates new file mode 100644 index 000000000..20cbd9cda --- /dev/null +++ b/utils/sql/svn/2188_required_aa_updates @@ -0,0 +1,93 @@ +-- Touch of the Divine +REPLACE 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`) VALUES ('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', '1', '741', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('741', '1', '232', '2', '4544'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('742', '1', '232', '4', '4545'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('743', '1', '232', '6', '4546'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('744', '1', '232', '8', '4547'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('745', '1', '232', '10', '4548'); + +-- Unfailing Divinity +REPLACE 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`) VALUES ('577', 'Unfailing Divinity', '2', '3', '4294967295', '4294967295', '5660', '5661', '5', '4294967295', '0', '0', '0', '0', '4', '0', '61', '2', '4', '4294967295', '3', '0', '1', '577', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('577', '1', '277', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('578', '1', '277', '40', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('579', '1', '277', '60', '0'); + +-- Internal Metronome(5)/Channeling Focus(3) * These AA no longer exist on live. +REPLACE 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`) VALUES ('710', 'Internal Metronome', '5', '5', '4294967295', '4294967295', '710', '710', '6', '0', '96', '3', '0', '0', '256', '0', '65', '0', '7', '4294967295', '2', '0', '1', '710', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('95', '1', '235', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('96', '1', '235', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('97', '1', '235', '15', '0'); + +REPLACE 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`) VALUES ('95', 'Channeling Focus', '2', '3', '4294967295', '4294967295', '13546', '13547', '2', '4294967295', '4294967295', '0', '0', '0', '64892', '0', '55', '2', '3', '4294967295', '2', '0', '1', '95', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('710', '1', '235', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('711', '1', '235', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('712', '1', '235', '15', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('713', '1', '235', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('714', '1', '235', '25', '0'); + +-- Enchanted root/Viscid Root/Petrified root +REPLACE 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`) VALUES ('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'); +REPLACE 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`) VALUES ('567', 'Viscid Roots', '5', '1', '4294967295', '4294967295', '5664', '5665', '5', '4294967295', '144', '1', '0', '0', '64', '0', '63', '0', '4', '4294967295', '3', '0', '1', '567', '1', '0'); +REPLACE 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`) VALUES ('5061', 'Petrified Roots ', '5', '1', '4294967295', '4294967295', '567', '567', '7', '4294967295', '567', '1', '0', '0', '64', '0', '71', '0', '14', '4294967295', '3', '0', '1', '567', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('144', '1', '244', '50', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('567', '1', '244', '62', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5061', '1', '244', '73', '0'); + +-- Pet Affinity +REPLACE 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`) VALUES ('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'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('734', '1', '237', '1', '0'); + +-- Jamfest +REPLACE 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`) VALUES ('225', 'Jam Fest', '3', '3', '4294967295', '4294967295', '13750', '13751', '3', '4294967295', '4294967295', '0', '0', '0', '256', '0', '59', '3', '3', '4294967295', '3', '0', '1', '225', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('225', '1', '272', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('225', '1', '272', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('225', '1', '272', '3', '0'); + +-- Extended Notes/Sionachie's Crescendo +REPLACE 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`) VALUES ('270', 'Extended Notes', '3', '3', '4294967295', '4294967295', '13805', '13806', '3', '4294967295', '4294967295', '0', '0', '0', '256', '0', '59', '3', '3', '4294967295', '3', '0', '1', '270', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('270', '1', '270', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('271', '1', '270', '15', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('272', '1', '270', '25', '0'); + +REPLACE 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`) VALUES ('568', 'Sionachie\'s Crescendo', '2', '3', '4294967295', '4294967295', '5666', '5667', '5', '4294967295', '270', '3', '0', '0', '256', '0', '63', '0', '4', '4294967295', '3', '0', '1', '568', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('568', '1', '270', '30', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('569', '1', '270', '35', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('570', '1', '270', '40', '0'); + +-- Spell Casting Expertise/Mastery of the Past(Caster/Healer)/MoP II/MoP III/MoP IV +REPLACE 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`) VALUES ('101', 'Spell Casting Expertise', '2', '3', '4294967295', '4294967295', '13550', '13551', '2', '4294967295', '4294967295', '0', '0', '0', '31008', '0', '55', '2', '3', '4294967295', '2', '0', '1', '101', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('101', '1', '265', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('102', '1', '265', '35', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('103', '1', '265', '52', '0'); + +REPLACE 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`) VALUES ('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', '1', '446', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('446', '1', '265', '54', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('447', '1', '265', '56', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('448', '1', '265', '58', '0'); + +REPLACE 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`) VALUES ('735', 'Mastery of the Past', '3', '3', '4294967295', '4294967295', '9163', '9164', '6', '4294967295', '0', '0', '0', '0', '32784', '0', '62', '0', '7', '4294967295', '2', '0', '1', '735', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('735', '1', '265', '54', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('736', '1', '265', '56', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('737', '1', '265', '58', '0'); + +REPLACE 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`) VALUES ('7056', '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', '1', '735', '1', '0'); +REPLACE 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`) VALUES ('7059', 'Mastery of the Past III', '6', '3', '4294967295', '4294967295', '735', '735', '5', '4294967295', '7056', '3', '0', '0', '33884', '0', '71', '0', '14', '4294967295', '2', '0', '1', '735', '4', '0'); +REPLACE 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`) VALUES ('7066', 'Mastery of the Past IV', '8', '3', '4294967295', '4294967295', '735', '735', '5', '4294967295', '7059', '3', '0', '0', '33884', '0', '76', '0', '15', '4294967295', '2', '0', '1', '735', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7056', '1', '265', '62', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7057', '1', '265', '64', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7058', '1', '265', '66', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7059', '1', '265', '67', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7060', '1', '265', '68', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7061', '1', '265', '69', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7066', '1', '265', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7066', '1', '265', '71', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7066', '1', '265', '72', '0'); + +-- Misc fix from prior revision. +UPDATE `altadv_vars` SET `berserker` = 0 WHERE `skill_id` = 1624; +UPDATE `altadv_vars` SET `clientver` = 99 WHERE `skill_id` = 1143; +UPDATE `altadv_vars` SET `clientver` = 5 WHERE `aa_expansion` = 15; +REPLACE 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`) VALUES ('1166', 'Frenzied Defense', '3', '3', '4294967295', '4294967295', '1143', '1143', '3', '4294967295', '0', '0', '0', '0', '0', '1', '65', '0', '15', '4294967295', '3', '0', '1', '1143', '4', '0'); + diff --git a/utils/sql/svn/2188_required_aa_updates.sql b/utils/sql/svn/2188_required_aa_updates.sql new file mode 100644 index 000000000..20cbd9cda --- /dev/null +++ b/utils/sql/svn/2188_required_aa_updates.sql @@ -0,0 +1,93 @@ +-- Touch of the Divine +REPLACE 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`) VALUES ('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', '1', '741', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('741', '1', '232', '2', '4544'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('742', '1', '232', '4', '4545'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('743', '1', '232', '6', '4546'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('744', '1', '232', '8', '4547'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('745', '1', '232', '10', '4548'); + +-- Unfailing Divinity +REPLACE 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`) VALUES ('577', 'Unfailing Divinity', '2', '3', '4294967295', '4294967295', '5660', '5661', '5', '4294967295', '0', '0', '0', '0', '4', '0', '61', '2', '4', '4294967295', '3', '0', '1', '577', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('577', '1', '277', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('578', '1', '277', '40', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('579', '1', '277', '60', '0'); + +-- Internal Metronome(5)/Channeling Focus(3) * These AA no longer exist on live. +REPLACE 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`) VALUES ('710', 'Internal Metronome', '5', '5', '4294967295', '4294967295', '710', '710', '6', '0', '96', '3', '0', '0', '256', '0', '65', '0', '7', '4294967295', '2', '0', '1', '710', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('95', '1', '235', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('96', '1', '235', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('97', '1', '235', '15', '0'); + +REPLACE 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`) VALUES ('95', 'Channeling Focus', '2', '3', '4294967295', '4294967295', '13546', '13547', '2', '4294967295', '4294967295', '0', '0', '0', '64892', '0', '55', '2', '3', '4294967295', '2', '0', '1', '95', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('710', '1', '235', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('711', '1', '235', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('712', '1', '235', '15', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('713', '1', '235', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('714', '1', '235', '25', '0'); + +-- Enchanted root/Viscid Root/Petrified root +REPLACE 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`) VALUES ('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'); +REPLACE 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`) VALUES ('567', 'Viscid Roots', '5', '1', '4294967295', '4294967295', '5664', '5665', '5', '4294967295', '144', '1', '0', '0', '64', '0', '63', '0', '4', '4294967295', '3', '0', '1', '567', '1', '0'); +REPLACE 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`) VALUES ('5061', 'Petrified Roots ', '5', '1', '4294967295', '4294967295', '567', '567', '7', '4294967295', '567', '1', '0', '0', '64', '0', '71', '0', '14', '4294967295', '3', '0', '1', '567', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('144', '1', '244', '50', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('567', '1', '244', '62', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5061', '1', '244', '73', '0'); + +-- Pet Affinity +REPLACE 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`) VALUES ('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'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('734', '1', '237', '1', '0'); + +-- Jamfest +REPLACE 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`) VALUES ('225', 'Jam Fest', '3', '3', '4294967295', '4294967295', '13750', '13751', '3', '4294967295', '4294967295', '0', '0', '0', '256', '0', '59', '3', '3', '4294967295', '3', '0', '1', '225', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('225', '1', '272', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('225', '1', '272', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('225', '1', '272', '3', '0'); + +-- Extended Notes/Sionachie's Crescendo +REPLACE 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`) VALUES ('270', 'Extended Notes', '3', '3', '4294967295', '4294967295', '13805', '13806', '3', '4294967295', '4294967295', '0', '0', '0', '256', '0', '59', '3', '3', '4294967295', '3', '0', '1', '270', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('270', '1', '270', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('271', '1', '270', '15', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('272', '1', '270', '25', '0'); + +REPLACE 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`) VALUES ('568', 'Sionachie\'s Crescendo', '2', '3', '4294967295', '4294967295', '5666', '5667', '5', '4294967295', '270', '3', '0', '0', '256', '0', '63', '0', '4', '4294967295', '3', '0', '1', '568', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('568', '1', '270', '30', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('569', '1', '270', '35', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('570', '1', '270', '40', '0'); + +-- Spell Casting Expertise/Mastery of the Past(Caster/Healer)/MoP II/MoP III/MoP IV +REPLACE 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`) VALUES ('101', 'Spell Casting Expertise', '2', '3', '4294967295', '4294967295', '13550', '13551', '2', '4294967295', '4294967295', '0', '0', '0', '31008', '0', '55', '2', '3', '4294967295', '2', '0', '1', '101', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('101', '1', '265', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('102', '1', '265', '35', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('103', '1', '265', '52', '0'); + +REPLACE 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`) VALUES ('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', '1', '446', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('446', '1', '265', '54', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('447', '1', '265', '56', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('448', '1', '265', '58', '0'); + +REPLACE 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`) VALUES ('735', 'Mastery of the Past', '3', '3', '4294967295', '4294967295', '9163', '9164', '6', '4294967295', '0', '0', '0', '0', '32784', '0', '62', '0', '7', '4294967295', '2', '0', '1', '735', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('735', '1', '265', '54', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('736', '1', '265', '56', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('737', '1', '265', '58', '0'); + +REPLACE 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`) VALUES ('7056', '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', '1', '735', '1', '0'); +REPLACE 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`) VALUES ('7059', 'Mastery of the Past III', '6', '3', '4294967295', '4294967295', '735', '735', '5', '4294967295', '7056', '3', '0', '0', '33884', '0', '71', '0', '14', '4294967295', '2', '0', '1', '735', '4', '0'); +REPLACE 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`) VALUES ('7066', 'Mastery of the Past IV', '8', '3', '4294967295', '4294967295', '735', '735', '5', '4294967295', '7059', '3', '0', '0', '33884', '0', '76', '0', '15', '4294967295', '2', '0', '1', '735', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7056', '1', '265', '62', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7057', '1', '265', '64', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7058', '1', '265', '66', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7059', '1', '265', '67', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7060', '1', '265', '68', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7061', '1', '265', '69', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7066', '1', '265', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7066', '1', '265', '71', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7066', '1', '265', '72', '0'); + +-- Misc fix from prior revision. +UPDATE `altadv_vars` SET `berserker` = 0 WHERE `skill_id` = 1624; +UPDATE `altadv_vars` SET `clientver` = 99 WHERE `skill_id` = 1143; +UPDATE `altadv_vars` SET `clientver` = 5 WHERE `aa_expansion` = 15; +REPLACE 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`) VALUES ('1166', 'Frenzied Defense', '3', '3', '4294967295', '4294967295', '1143', '1143', '3', '4294967295', '0', '0', '0', '0', '0', '1', '65', '0', '15', '4294967295', '3', '0', '1', '1143', '4', '0'); + diff --git a/utils/sql/svn/2189_optional_taunt_rules b/utils/sql/svn/2189_optional_taunt_rules new file mode 100644 index 000000000..b80ede2d7 --- /dev/null +++ b/utils/sql/svn/2189_optional_taunt_rules @@ -0,0 +1,3 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:TauntOverLevel', '1', '(Default=1) Allows you to taunt NPC's over users level.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:TauntSkillFalloff', '0.33', '(Default=0.33) For every taunt skill point that's not maxed you lose this % chance to taunt.'); + diff --git a/utils/sql/svn/2189_optional_taunt_rules.sql b/utils/sql/svn/2189_optional_taunt_rules.sql new file mode 100644 index 000000000..b80ede2d7 --- /dev/null +++ b/utils/sql/svn/2189_optional_taunt_rules.sql @@ -0,0 +1,3 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:TauntOverLevel', '1', '(Default=1) Allows you to taunt NPC's over users level.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:TauntSkillFalloff', '0.33', '(Default=0.33) For every taunt skill point that's not maxed you lose this % chance to taunt.'); + diff --git a/utils/sql/svn/2195_required_sharedplatupdates.sql b/utils/sql/svn/2195_required_sharedplatupdates.sql new file mode 100644 index 000000000..4a99ad3c8 --- /dev/null +++ b/utils/sql/svn/2195_required_sharedplatupdates.sql @@ -0,0 +1,2 @@ +ALTER TABLE `account` +MODIFY COLUMN `sharedplat` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `charname`; \ No newline at end of file diff --git a/utils/sql/svn/2208_optional_EnableSoulAbrasionAA.sql b/utils/sql/svn/2208_optional_EnableSoulAbrasionAA.sql new file mode 100644 index 000000000..0916e0372 --- /dev/null +++ b/utils/sql/svn/2208_optional_EnableSoulAbrasionAA.sql @@ -0,0 +1,75 @@ +-- Updates your spell file to allow Soul Abrasion AA to properly function off 'spellgroup 99' +-- If your using an older spell dbase this is required! +UPDATE spells_new SET spellgroup = 99 WHERE id =822 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =1471 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =1966 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =2717 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =2718 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =3228 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =4908 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =4910 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =6037 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =6038 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =6039 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =6102 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =10254 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =10255 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =10256 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =11621 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =11622 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =11623 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =11940 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =11941 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =11942 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =12629 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =12630 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =12631 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =12632 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =12633 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =12634 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =15157 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =15158 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =15159 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =15205 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =15206 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =15207 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =16066 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =16067 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =16068 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =16069 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =16070 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =16071 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =16584 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =16585 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =16586 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =19310 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =19311 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =19312 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =19358 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =19359 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =19360 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =23569 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =23570 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =23571 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =25559 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =25560 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =25561 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =25562 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =25563 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =25564 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =25601 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =25602 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =25603 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =28636 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =28637 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =28638 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =28639 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =28640 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =28641 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =28684 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =28685 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =28686 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =32197 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =32198 AND spellgroup = 0 AND targettype = 13 ; +UPDATE spells_new SET spellgroup = 99 WHERE id =32199 AND spellgroup = 0 AND targettype = 13 ; + diff --git a/utils/sql/svn/2208_optional_aa_stacking_rule.sql b/utils/sql/svn/2208_optional_aa_stacking_rule.sql new file mode 100644 index 000000000..659cb82fe --- /dev/null +++ b/utils/sql/svn/2208_optional_aa_stacking_rule.sql @@ -0,0 +1 @@ +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'AA:Stacking', 'true', 'Enable stacking of AA within the same series on all clients'); diff --git a/utils/sql/svn/2208_required_aa_updates.sql b/utils/sql/svn/2208_required_aa_updates.sql new file mode 100644 index 000000000..02d26d833 --- /dev/null +++ b/utils/sql/svn/2208_required_aa_updates.sql @@ -0,0 +1,1891 @@ +-- Earthen AA line UF +REPLACE 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`) VALUES ('8263', 'Earthen Brawn', '5', '5', '4294967295', '4294967295', '8263', '8263', '1', '4294967295', '7357', '5', '0', '0', '65534', '1', '81', '0', '16', '4294967295', '1', '0', '10', '8263', '6', '0'); +REPLACE 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`) VALUES ('8268', 'Earthen Stability', '5', '5', '4294967295', '4294967295', '8268', '8268', '1', '4294967295', '7357', '5', '0', '0', '65534', '1', '81', '0', '16', '4294967295', '1', '0', '10', '8268', '6', '0'); +REPLACE 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`) VALUES ('8273', 'Earthen Alacrity', '5', '5', '4294967295', '4294967295', '8273', '8273', '1', '4294967295', '7357', '5', '0', '0', '65534', '1', '81', '0', '16', '4294967295', '1', '0', '10', '8273', '6', '0'); +REPLACE 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`) VALUES ('8278', 'Earthen Artistry', '5', '5', '4294967295', '4294967295', '8278', '8278', '1', '4294967295', '7357', '5', '0', '0', '65534', '1', '81', '0', '16', '4294967295', '1', '0', '10', '8278', '6', '0'); +REPLACE 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`) VALUES ('8283', 'Earthen Sagacity', '5', '5', '4294967295', '4294967295', '8283', '8283', '1', '4294967295', '7357', '5', '0', '0', '65534', '1', '81', '0', '16', '4294967295', '1', '0', '10', '8283', '6', '0'); +REPLACE 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`) VALUES ('8288', 'Earthen Brilliance', '5', '5', '4294967295', '4294967295', '8288', '8288', '1', '4294967295', '7357', '5', '0', '0', '65534', '1', '81', '0', '16', '4294967295', '1', '0', '10', '8288', '6', '0'); +REPLACE 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`) VALUES ('8293', 'Earthen Allure', '5', '5', '4294967295', '4294967295', '8293', '8293', '1', '4294967295', '7357', '5', '0', '0', '65534', '1', '81', '0', '16', '4294967295', '1', '0', '10', '8293', '6', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8263', '1', '262', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8264', '1', '262', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8265', '1', '262', '15', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8266', '1', '262', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8267', '1', '262', '25', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8268', '1', '262', '5', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8269', '1', '262', '10', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8270', '1', '262', '15', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8271', '1', '262', '20', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8272', '1', '262', '25', '1'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8273', '1', '262', '5', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8274', '1', '262', '10', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8275', '1', '262', '15', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8276', '1', '262', '20', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8277', '1', '262', '25', '2'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8278', '1', '262', '5', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8279', '1', '262', '10', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8280', '1', '262', '15', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8281', '1', '262', '20', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8282', '1', '262', '25', '3'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8283', '1', '262', '5', '4'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8284', '1', '262', '10', '4'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8285', '1', '262', '15', '4'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8286', '1', '262', '20', '4'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8287', '1', '262', '25', '4'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8288', '1', '262', '5', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8289', '1', '262', '10', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8290', '1', '262', '15', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8291', '1', '262', '20', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8292', '1', '262', '25', '5'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8293', '1', '262', '5', '6'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8294', '1', '262', '10', '6'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8295', '1', '262', '15', '6'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8296', '1', '262', '20', '6'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8297', '1', '262', '25', '6'); + +-- Planar Power +REPLACE 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`) VALUES ('4678', 'Planar Power III', '5', '5', '4294967295', '4294967295', '418', '418', '4', '4294967295', '1001', '5', '0', '0', '65534', '1', '71', '0', '12', '4294967295', '1', '0', '10', '418', '4', '0'); +REPLACE 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`) VALUES ('7547', 'Planar Power IV', '5', '5', '4294967295', '4294967295', '418', '418', '4', '4294967295', '4678', '5', '0', '0', '65534', '1', '81', '0', '15', '4294967295', '1', '0', '10', '418', '5', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4678', '1', '262', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4679', '1', '262', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4680', '1', '262', '15', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4681', '1', '262', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4682', '1', '262', '25', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4678', '2', '262', '5', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4679', '2', '262', '10', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4680', '2', '262', '15', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4681', '2', '262', '20', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4682', '2', '262', '25', '1'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4678', '3', '262', '5', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4679', '3', '262', '10', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4680', '3', '262', '15', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4681', '3', '262', '20', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4682', '3', '262', '25', '2'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4678', '4', '262', '5', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4679', '4', '262', '10', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4680', '4', '262', '15', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4681', '4', '262', '20', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4682', '4', '262', '25', '3'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4678', '5', '262', '5', '4'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4679', '5', '262', '10', '4'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4680', '5', '262', '15', '4'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4681', '5', '262', '20', '4'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4682', '5', '262', '25', '4'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4678', '6', '262', '5', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4679', '6', '262', '10', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4680', '6', '262', '15', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4681', '6', '262', '20', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4682', '6', '262', '25', '5'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4678', '7', '262', '5', '6'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4679', '7', '262', '10', '6'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4680', '7', '262', '15', '6'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4681', '7', '262', '20', '6'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4682', '7', '262', '25', '6'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7547', '1', '262', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7548', '1', '262', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7549', '1', '262', '15', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7550', '1', '262', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7551', '1', '262', '25', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7547', '2', '262', '5', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7548', '2', '262', '10', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7549', '2', '262', '15', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7550', '2', '262', '20', '1'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7551', '2', '262', '25', '1'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7547', '3', '262', '5', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7548', '3', '262', '10', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7549', '3', '262', '15', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7550', '3', '262', '20', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7551', '3', '262', '25', '2'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7547', '4', '262', '5', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7548', '4', '262', '10', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7549', '4', '262', '15', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7550', '4', '262', '20', '3'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7551', '4', '262', '25', '3'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7537', '5', '262', '5', '4'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7538', '5', '262', '10', '4'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7539', '5', '262', '15', '4'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7540', '5', '262', '20', '4'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7541', '5', '262', '25', '4'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7547', '6', '262', '5', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7548', '6', '262', '10', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7549', '6', '262', '15', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7550', '6', '262', '20', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7551', '6', '262', '25', '5'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7547', '7', '262', '5', '6'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7548', '7', '262', '10', '6'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7549', '7', '262', '15', '6'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7550', '7', '262', '20', '6'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7551', '7', '262', '25', '6'); + + +-- Critical Affliction +REPLACE 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`) VALUES ('767', 'Critical Affliction', '3', '3', '4294967295', '4294967295', '9191', '9192', '6', '4294967295', '0', '0', '0', '0', '36192', '0', '65', '3', '7', '4294967295', '2', '0', '1', '767', '1', '0'); +REPLACE 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`) VALUES ('1099', 'Improved Critical Affliction', '3', '3', '4294967295', '4294967295', '30456', '30457', '7', '4294967295', '767', '3', '0', '0', '36192', '0', '67', '3', '8', '4294967295', '2', '0', '1', '767', '1', '0'); +REPLACE 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`) VALUES ('12423', 'Critical Affliction III', '9', '3', '4294967295', '4294967295', '767', '767', '7', '4294967295', '1099', '3', '0', '0', '36192', '0', '81', '3', '16', '4294967295', '2', '0', '1', '767', '6', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('767', '1', '273', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('768', '1', '273', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('769', '1', '273', '9', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1099', '1', '273', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1100', '1', '273', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1101', '1', '273', '9', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12423', '1', '273', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12424', '1', '273', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12425', '1', '273', '9', '0'); + +-- Persistent Casting +REPLACE 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`) VALUES ('692', 'Persistent Casting', '3', '3', '4294967295', '4294967295', '9187', '9188', '6', '4294967295', '0', '0', '0', '0', '64892', '0', '55', '3', '7', '4294967295', '2', '0', '1', '692', '1', '0'); +REPLACE 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`) VALUES ('7647', 'Persistent Casting II', '6', '3', '4294967295', '4294967295', '692', '692', '6', '4294967295', '692', '3', '0', '0', '64892', '0', '81', '0', '15', '4294967295', '2', '0', '1', '692', '5', '0'); +REPLACE 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`) VALUES ('7670', 'Persistent Casting III', '7', '3', '4294967295', '4294967295', '692', '692', '6', '4294967295', '7647', '3', '0', '0', '64892', '0', '85', '2', '16', '4294967295', '2', '0', '1', '692', '6', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('692', '1', '229', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('693', '1', '229', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('694', '1', '229', '15', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7647', '1', '229', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7648', '1', '229', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7649', '1', '229', '15', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7670', '1', '229', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7671', '1', '229', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7672', '1', '229', '15', '0'); + +-- Cascading Life (Level 70 +5) +REPLACE 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`) VALUES ('8224', 'Cascade of Life', '5', '3', '4294967295', '4294967295', '8224', '8224', '7', '4294967295', '1486', '5', '0', '0', '1092', '0', '75', '0', '15', '4294967295', '2', '0', '1', '8224', '5', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '1', '339', '10', '16230'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '7', '134', '80', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8224', '15', '139', '-5403', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '1', '339', '10', '16231'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '4', '142', '65', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '7', '134', '85', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8225', '15', '139', '-5403', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '1', '339', '10', '16232'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '4', '142', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '7', '134', '90', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8226', '15', '139', '-5403', '0'); + +-- Total Domination +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('649', '1', '243', '15', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('650', '1', '243', '35', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('651', '1', '243', '50', '0'); + +-- Coat of Thistles +REPLACE 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`) VALUES ('867', 'Coat of Thistles', '5', '5', '4294967295', '4294967295', '867', '867', '6', '0', '0', '0', '0', '0', '16', '0', '65', '0', '7', '4294967295', '2', '0', '1', '867', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('867', '1', '59', '-3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('868', '1', '59', '-6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('869', '1', '59', '-9', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('870', '1', '59', '-12', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('871', '1', '59', '-15', '0'); + +-- Abundant Heal updates. +REPLACE 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`) VALUES ('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'); +REPLACE 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`) VALUES ('5529', 'Abundant Healing II', '7', '5', '4294967295', '4294967295', '1486', '1486', '7', '0', '1486', '5', '0', '0', '1092', '0', '76', '2', '14', '4294967295', '2', '0', '1', '1486', '4', '0'); +REPLACE 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`) VALUES ('7554', 'Abundant Healing III', '7', '5', '4294967295', '4294967295', '1486', '1486', '7', '0', '5529', '5', '0', '0', '1092', '0', '81', '2', '15', '4294967295', '2', '0', '1', '1486', '5', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '1', '339', '1', '8164'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1486', '16', '348', '10', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '1', '339', '3', '8165'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1487', '16', '348', '10', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '1', '339', '5', '8166'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1488', '16', '348', '10', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '1', '339', '7', '8167'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1489', '16', '348', '10', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '1', '339', '9', '8168'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1490', '16', '348', '10', '0'); + +-- AH II + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '1', '339', '19', '12684'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5529', '16', '348', '10', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '1', '339', '11', '12680'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5530', '16', '348', '10', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '1', '339', '13', '12681'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5531', '16', '348', '10', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '1', '339', '15', '12682'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '16', '348', '10', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '1', '339', '17', '12683'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5533', '16', '348', '10', '0'); + +-- AH3 + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '1', '339', '21', '16194'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7554', '16', '348', '10', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '1', '339', '23', '16195'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7555', '16', '348', '10', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '1', '339', '25', '16196'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7556', '16', '348', '10', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '1', '339', '27', '16417'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7557', '16', '348', '10', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '1', '339', '29', '16418'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '4', '142', '60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '5', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '6', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '7', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '8', '139', '-265', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '9', '139', '-754', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '10', '139', '-1332', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '11', '139', '-1572', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '12', '139', '-2749', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '13', '139', '-4979', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '14', '139', '-5418', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '15', '139', '-5403', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7558', '16', '348', '10', '0'); + +-- Destructive Fury updates. +REPLACE 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`) VALUES ('4755', 'Destructive Fury II', '6', '3', '4294967295', '4294967295', '1210', '1210', '7', '4294967295', '1210', '3', '0', '0', '27716', '0', '71', '1', '12', '4294967295', '2', '0', '1', '1210', '4', '0'); +REPLACE 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`) VALUES ('5557', 'Destructive Fury III', '7', '3', '4294967295', '4294967295', '1210', '1210', '7', '4294967295', '4755', '3', '0', '0', '27716', '0', '76', '2', '14', '4294967295', '2', '0', '1', '1210', '4', '0'); +REPLACE 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`) VALUES ('7582', 'Destructive Fury IV', '8', '3', '4294967295', '4294967295', '1210', '1210', '7', '4294967295', '5557', '3', '0', '0', '27716', '0', '81', '2', '15', '4294967295', '2', '0', '1', '1210', '5', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1210', '1', '294', '0', '107'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1211', '1', '294', '0', '116'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1212', '1', '294', '0', '125'); +-- 140max +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4755', '1', '294', '0', '105'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4756', '1', '294', '0', '110'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4757', '1', '294', '0', '115'); +-- 160max +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5557', '1', '294', '0', '110'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5558', '1', '294', '0', '115'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5559', '1', '294', '0', '120'); +-- 200 max +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7582', '1', '294', '0', '120'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7583', '1', '294', '0', '130'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7584', '1', '294', '0', '140'); + +-- Healing Gift updates. +REPLACE 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`) VALUES ('80', 'Healing Gift', '2', '3', '4294967295', '4294967295', '13536', '13537', '2', '4294967295', '4294967295', '0', '0', '0', '33884', '0', '55', '2', '3', '4294967295', '2', '0', '1', '80', '1', '0'); +REPLACE 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`) VALUES ('437', 'Advanced Healing Gift', '2', '3', '4294967295', '4294967295', '5580', '5581', '5', '4294967295', '80', '3', '0', '0', '33884', '0', '62', '2', '4', '4294967295', '2', '0', '1', '80', '1', '0'); +REPLACE 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`) VALUES ('1086', 'Healing Gift Mastery', '3', '3', '4294967295', '4294967295', '30404', '30405', '7', '4294967295', '437', '3', '0', '0', '33884', '0', '66', '3', '8', '4294967295', '2', '0', '1', '80', '1', '0'); +REPLACE 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`) VALUES ('4779', 'Healing Gift IV', '6', '3', '4294967295', '4294967295', '30404', '30405', '7', '4294967295', '1086', '3', '0', '0', '33884', '0', '71', '1', '12', '4294967295', '2', '0', '1', '80', '4', '0'); +REPLACE 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`) VALUES ('5592', 'Healing Gift V', '7', '3', '4294967295', '4294967295', '30404', '30405', '7', '4294967295', '4479', '3', '0', '0', '33884', '0', '76', '2', '14', '4294967295', '2', '0', '1', '80', '4', '0'); +REPLACE 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`) VALUES ('7590', 'Healing Gift VI', '7', '3', '4294967295', '4294967295', '30404', '30405', '7', '4294967295', '5592', '3', '0', '0', '33884', '0', '81', '2', '15', '4294967295', '2', '0', '1', '80', '5', '0'); +REPLACE 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`) VALUES ('12454', 'Healing Gift VII', '12', '3', '4294967295', '4294967295', '30404', '30405', '7', '4294967295', '7590', '0', '0', '0', '33884', '0', '85', '2', '16', '4294967295', '2', '0', '1', '80', '6', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4779', '1', '274', '26', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4780', '1', '274', '28', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4781', '1', '274', '30', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5592', '1', '274', '32', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5593', '1', '274', '33', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5594', '1', '274', '34', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7590', '1', '274', '35', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7591', '1', '274', '36', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7592', '1', '274', '37', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12452', '1', '274', '38', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12453', '1', '274', '39', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12454', '1', '274', '40', '0'); + +-- Gift of Mana/II/III/IV +REPLACE 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`) VALUES ('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'); +REPLACE 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`) VALUES ('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'); +REPLACE 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`) VALUES ('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'); +-- (1) +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '1', '339', '3', '8105'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '2', '142', '65', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '3', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '4', '134', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '5', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '6', '137', '0', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '7', '339', '3', '8105'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '8', '142', '65', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '9', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '10', '134', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '11', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '12', '137', '100', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '13', '339', '3', '8105'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '14', '142', '65', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '15', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '16', '134', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '17', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '18', '137', '79', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '19', '339', '3', '8105'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '20', '142', '65', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '21', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '22', '134', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '23', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1435', '24', '137', '149', '0'); +-- (2) +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '1', '339', '6', '8105'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '2', '142', '65', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '3', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '4', '134', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '5', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '6', '137', '0', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '7', '339', '6', '8105'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '8', '142', '65', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '9', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '10', '134', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '11', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '12', '137', '100', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '13', '339', '6', '8105'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '14', '142', '65', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '15', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '16', '134', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '17', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '18', '137', '79', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '19', '339', '6', '8105'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '20', '142', '65', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '21', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '22', '134', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '23', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1436', '24', '137', '147', '0'); +-- (3) +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '1', '339', '10', '8105'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '2', '142', '65', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '3', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '4', '134', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '5', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '6', '137', '0', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '7', '339', '10', '8105'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '8', '142', '65', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '9', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '10', '134', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '11', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '12', '137', '100', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '13', '339', '10', '8105'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '14', '142', '65', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '15', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '16', '134', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '17', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '18', '137', '79', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '19', '339', '10', '8105'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '20', '142', '65', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '21', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '22', '134', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '23', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1437', '24', '137', '147', '0'); +-- GoRM + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '1', '339', '10', '11404'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '2', '142', '71', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '3', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '4', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '5', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '6', '137', '0', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '7', '339', '10', '11404'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '8', '142', '71', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '9', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '10', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '11', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '12', '137', '100', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '13', '339', '10', '11404'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '14', '142', '71', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '15', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '16', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '17', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '18', '137', '79', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '19', '339', '10', '11404'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '20', '142', '71', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '21', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '22', '134', '75', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '23', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4773', '24', '137', '149', '0'); + +-- GoERM +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '1', '339', '10', '13199'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '2', '142', '76', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '3', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '4', '134', '80', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '5', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '6', '137', '0', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '7', '339', '10', '13199'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '8', '142', '76', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '9', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '10', '134', '80', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '11', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '12', '137', '100', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '13', '339', '10', '13199'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '14', '142', '76', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '15', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '16', '134', '80', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '17', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '18', '137', '79', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '19', '339', '10', '13199'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '20', '142', '76', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '21', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '22', '134', '80', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '23', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6517', '24', '137', '149', '0'); + +-- GoAERM +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '1', '339', '10', '13830'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '2', '142', '81', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '3', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '4', '134', '85', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '5', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '6', '137', '0', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '7', '339', '10', '13830'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '8', '142', '81', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '9', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '10', '134', '85', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '11', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '12', '137', '100', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '13', '339', '10', '13830'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '14', '142', '81', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '15', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '16', '134', '85', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '17', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '18', '137', '79', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '19', '339', '10', '13830'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '20', '142', '81', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '21', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '22', '134', '85', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '23', '348', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7621', '24', '137', '149', '0'); + + + +-- END WARRIOR General/Archetype + +-- Armor of Wisdom I+II - Warriors +REPLACE 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`) VALUES ('8235', 'Armor of Wisdom', '6', '5', '4294967295', '4294967295', '8235', '8235', '7', '4294967295', '0', '0', '0', '0', '2', '0', '81', '0', '15', '4294967295', '2', '0', '1', '8235', '5', '0'); +REPLACE 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`) VALUES ('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', '1', '8235', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8235', '1', '1', '29', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8236', '1', '1', '58', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8237', '1', '1', '87', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8238', '1', '1', '116', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8239', '1', '1', '145', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8305', '1', '1', '174', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8306', '1', '1', '203', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8307', '1', '1', '232', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8308', '1', '1', '261', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8309', '1', '1', '290', '0'); + +-- Finishing Blow I-VII +REPLACE 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`) VALUES ('119', 'Finishing Blow', '2', '3', '4294967295', '4294967295', '13562', '13563', '2', '4294967295', '0', '0', '0', '0', '33722', '1', '55', '2', '3', '4294967295', '2', '0', '1', '119', '1', '0'); +REPLACE 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`) VALUES ('440', 'Coup de Grace', '2', '3', '4294967295', '4294967295', '5618', '5619', '5', '4294967295', '119', '3', '0', '0', '33722', '1', '62', '0', '4', '4294967295', '2', '0', '1', '119', '1', '0'); +REPLACE 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`) VALUES ('1053', 'Deathblow', '3', '3', '4294967295', '4294967295', '30272', '30273', '7', '4294967295', '440', '3', '0', '0', '33722', '1', '66', '0', '8', '4294967295', '2', '0', '1', '119', '1', '0'); +REPLACE 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`) VALUES ('4721', 'Finishing Blow IV', '3', '3', '4294967295', '4294967295', '119', '119', '7', '4294967295', '1053', '3', '0', '0', '33722', '1', '71', '0', '12', '4294967295', '2', '0', '1', '119', '4', '0'); +REPLACE 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`) VALUES ('5554', 'Finishing Blow V', '7', '3', '4294967295', '4294967295', '119', '119', '7', '4294967295', '4721', '3', '0', '0', '33722', '1', '76', '2', '14', '4294967295', '2', '0', '1', '119', '4', '0'); +REPLACE 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`) VALUES ('7565', 'Finishing Blow VI', '7', '3', '4294967295', '4294967295', '119', '119', '7', '4294967295', '5554', '3', '0', '0', '33722', '1', '81', '2', '15', '4294967295', '2', '0', '1', '119', '4', '0'); +REPLACE 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`) VALUES ('6023', 'Finishing Blow VII', '12', '3', '4294967295', '4294967295', '119', '119', '7', '4294967295', '7565', '3', '0', '0', '33722', '1', '85', '0', '16', '4294967295', '2', '0', '1', '119', '6', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('199', '1', '278', '500', '32000'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('199', '2', '440', '50', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('200', '1', '278', '500', '32000'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('200', '2', '440', '52', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('201', '1', '278', '500', '32000'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('201', '2', '440', '54', '200'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('440', '1', '278', '550', '50800'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('440', '2', '440', '56', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('441', '1', '278', '550', '50800'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('441', '2', '440', '58', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('442', '1', '278', '550', '50800'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('442', '2', '440', '60', '200'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1053', '1', '278', '600', '65440'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1053', '2', '440', '61', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1054', '1', '278', '600', '65440'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1054', '2', '440', '63', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1055', '1', '278', '600', '65440'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1055', '2', '440', '65', '200'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4721', '1', '278', '670', '82600'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4721', '2', '440', '66', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4722', '1', '278', '670', '82600'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4722', '2', '440', '68', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4723', '1', '278', '670', '82600'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4723', '2', '440', '70', '200'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5554', '1', '278', '750', '102280'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5554', '2', '440', '71', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5555', '1', '278', '750', '102280'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5555', '2', '440', '73', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5556', '1', '278', '750', '102280'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5556', '2', '440', '75', '200'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7565', '1', '278', '900', '124480'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7565', '2', '440', '76', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7566', '1', '278', '900', '124480'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7566', '2', '440', '78', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7567', '1', '278', '900', '124480'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7567', '2', '440', '80', '200'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6023', '1', '278', '900', '132440'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6023', '2', '440', '82', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6024', '1', '278', '900', '149200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6024', '2', '440', '84', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6025', '1', '278', '900', '149200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6025', '2', '440', '86', '200'); + +-- Killing Spree I-III +REPLACE 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`) VALUES ('4739', 'Killing Spree', '3', '3', '4294967295', '4294967295', '4739', '4739', '7', '4294967295', '0', '0', '0', '0', '642', '1', '71', '3', '12', '4294967295', '2', '0', '1', '4739', '4', '0'); +REPLACE 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`) VALUES ('5562', 'Killing Spree II', '7', '3', '4294967295', '4294967295', '4739', '4739', '7', '4294967295', '4739', '3', '0', '0', '642', '1', '76', '2', '14', '4294967295', '2', '0', '1', '4739', '4', '0'); +REPLACE 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`) VALUES ('7573', 'Killing Spree III', '7', '3', '4294967295', '4294967295', '4739', '4739', '7', '4294967295', '5562', '3', '0', '0', '642', '1', '76', '2', '15', '4294967295', '2', '0', '1', '4739', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4739', '1', '360', '10', '11023'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4740', '1', '360', '20', '11023'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4741', '1', '360', '30', '11023'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5562', '1', '360', '40', '11023'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5563', '1', '360', '50', '11023'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5564', '1', '360', '60', '11023'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7573', '1', '360', '70', '11023'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7574', '1', '360', '80', '11023'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7575', '1', '360', '90', '11023'); + +-- Empowered Ingenuity +REPLACE 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`) VALUES ('8198', 'Empowered Ingenuity', '4', '3', '4294967295', '4294967295', '8198', '8198', '7', '4294967295', '625', '3', '0', '0', '642', '1', '65', '0', '15', '4294967295', '2', '0', '1', '8198', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8198', '1', '294', '0', '110'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8199', '1', '294', '0', '120'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8200', '1', '294', '0', '130'); + +-- Ingenuity II-III +REPLACE 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`) VALUES ('4733', 'Ingenuity II', '3', '3', '4294967295', '4294967295', '4733', '4733', '5', '4294967295', '625', '3', '0', '0', '642', '1', '71', '0', '12', '4294967295', '2', '0', '1', '625', '4', '0'); +REPLACE 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`) VALUES ('7641', 'Ingenuity III', '3', '3', '4294967295', '4294967295', '4733', '4733', '5', '4294967295', '4733', '3', '0', '0', '642', '1', '76', '0', '15', '4294967295', '2', '0', '1', '625', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('625', '1', '294', '3', '100'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('626', '1', '294', '6', '100'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('627', '1', '294', '9', '100'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4733', '1', '294', '11', '100'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4734', '1', '294', '13', '100'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4735', '1', '294', '15', '100'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7641', '1', '294', '17', '100'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7642', '1', '294', '19', '100'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7643', '1', '294', '21', '100'); + +-- Enhanced Aggression I-V +REPLACE 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`) VALUES ('1592', 'Enhanced Aggression', '5', '5', '4294967295', '4294967295', '1592', '1592', '7', '4294967295', '0', '0', '0', '0', '33722', '1', '70', '0', '10', '4294967295', '2', '0', '1', '1592', '1', '0'); +REPLACE 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`) VALUES ('4725', 'Enhanced Aggression II', '5', '5', '4294967295', '4294967295', '1592', '1592', '7', '4294967295', '1592', '5', '0', '0', '33722', '1', '71', '0', '12', '4294967295', '2', '0', '1', '1592', '4', '0'); +REPLACE 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`) VALUES ('5557', 'Enhanced Aggression III', '5', '5', '4294967295', '4294967295', '1592', '8232', '7', '4294967295', '4725', '5', '0', '0', '33722', '1', '76', '0', '14', '4294967295', '2', '0', '1', '1592', '4', '0'); +REPLACE 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`) VALUES ('7568', 'Enhanced Aggression IV', '5', '5', '4294967295', '4294967295', '1592', '1592', '7', '4294967295', '5557', '5', '0', '0', '33722', '1', '81', '0', '15', '4294967295', '2', '0', '1', '1592', '5', '0'); +REPLACE 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`) VALUES ('12439', 'Enhanced Aggression V', '5', '5', '4294967295', '4294967295', '1592', '1592', '7', '4294967295', '7568', '5', '0', '0', '33722', '1', '85', '0', '16', '4294967295', '2', '0', '1', '1592', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1592', '1', '341', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1593', '1', '341', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1594', '1', '341', '30', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1595', '1', '341', '40', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1596', '1', '341', '50', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4725', '1', '341', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4726', '1', '341', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4727', '1', '341', '30', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4728', '1', '341', '40', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4729', '1', '341', '50', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5557', '1', '341', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5558', '1', '341', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5559', '1', '341', '30', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5560', '1', '341', '40', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5561', '1', '341', '50', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7568', '1', '341', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7569', '1', '341', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7570', '1', '341', '30', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7571', '1', '341', '40', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7572', '1', '341', '50', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12439', '1', '341', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12440', '1', '341', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12441', '1', '341', '30', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12442', '1', '341', '40', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12443', '1', '341', '50', '0'); + +-- Extended Ingenuity I + II +REPLACE 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`) VALUES ('8232', 'Extended Ingenuity', '4', '5', '4294967295', '4294967295', '8232', '8232', '7', '4294967295', '0', '0', '0', '0', '642', '1', '65', '0', '15', '4294967295', '2', '0', '1', '8232', '5', '0'); +REPLACE 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`) VALUES ('8261', 'Extended Ingenuity', '4', '1', '4294967295', '4294967295', '8232', '8232', '7', '4294967295', '8232', '5', '0', '0', '642', '1', '80', '0', '16', '4294967295', '2', '0', '1', '8232', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8232', '1', '128', '5', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8232', '1', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8232', '1', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8232', '1', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8232', '1', '411', '66434', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8232', '1', '137', '-40', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8233', '1', '128', '15', '15'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8233', '1', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8233', '1', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8233', '1', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8233', '1', '411', '66434', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8233', '1', '137', '-40', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8234', '1', '128', '25', '25'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8234', '1', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8234', '1', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8234', '1', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8234', '1', '411', '66434', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8234', '1', '137', '-40', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8235', '1', '128', '35', '35'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8235', '1', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8235', '1', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8235', '1', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8235', '1', '411', '66434', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8235', '1', '137', '-40', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8236', '1', '128', '50', '50'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8236', '1', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8236', '1', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8236', '1', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8236', '1', '411', '66434', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8236', '1', '137', '-40', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8261', '1', '128', '65', '65'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8261', '1', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8261', '1', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8261', '1', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8261', '1', '411', '66434', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8261', '1', '137', '-40', '0'); + +-- Ageless Emenity +REPLACE 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`) VALUES ('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'); +REPLACE INTO `aa_actions` (`aaid`, `rank`, `reuse_time`, `spell_id`, `target`, `nonspell_action`, `nonspell_mana`, `nonspell_duration`, `redux_aa`, `redux_rate`, `redux_aa2`, `redux_rate2`) VALUES ('10367', '1', '360', '21751', '2', '0', '0', '0', '0', '0', '0', '0'); + +-- Punshing Blade III/IV [Data change for I+II] MAX: 25 +REPLACE 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`) VALUES ('5534', 'Punishing Blade III', '8', '3', '4294967295', '4294967295', '599', '599', '7', '4294967295', '1536', '3', '0', '0', '146', '1', '76', '2', '14', '4294967295', '2', '0', '1', '599', '4', '0'); +REPLACE 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`) VALUES ('7659', 'Punishing Blade IV', '8', '3', '4294967295', '4294967295', '599', '599', '7', '4294967295', '5534', '3', '0', '0', '146', '1', '81', '2', '15', '4294967295', '2', '0', '1', '599', '5', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( '599', '1', '266', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( '600', '1', '266', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( '601', '1', '266', '8', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( '1536', '1', '266', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( '1537', '1', '266', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( '1538', '1', '266', '9', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5534', '1', '266', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5532', '1', '266', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5536', '1', '266', '5', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7659', '1', '266', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7660', '1', '266', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7661', '1', '266', '3', '0'); + +-- Shielding Resistance +REPLACE 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`) VALUES ('1181', 'Shielding Resistance', '3', '5', '4294967295', '4294967295', '1181', '1181', '7', '4294967295', '0', '0', '0', '0', '33682', '0', '70', '0', '8', '4294967295', '2', '0', '1', '1181', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1181', '1', '305', '-20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1182', '1', '305', '-40', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1183', '1', '305', '-60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1184', '1', '305', '-80', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1185', '1', '305', '-100', '0'); + +-- Slipperly Attacks +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1093', '1', '304', '-20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1094', '1', '304', '-40', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1095', '1', '304', '-60', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1096', '1', '304', '-80', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1097', '1', '304', '-100', '0'); + +-- Hardy Endurance (level required 65 + 5) +REPLACE 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`) VALUES ('8215', 'Hard Endurance', '5', '5', '4294967295', '4294967295', '8215', '8215', '7', '4294967295', '0', '0', '0', '0', '642', '1', '65', '0', '15', '4294967295', '2', '0', '1', '8215', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8215', '1', '190', '50', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8216', '1', '190', '100', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8217', '1', '190', '150', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8218', '1', '190', '200', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8219', '1', '190', '250', '0'); + +-- Heightened Endurance II/III/IV +REPLACE 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`) VALUES ('1036', 'Fitness', '5', '3', '4294967295', '4294967295', '1036', '1036', '7', '4294967295', '683', '3', '0', '0', '642', '1', '66', '0', '8', '4294967295', '2', '0', '1', '683', '1', '0'); +REPLACE 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`) VALUES ('5130', 'Heightened Endurance III', '5', '5', '4294967295', '4294967295', '683', '683', '7', '4294967295', '1036', '3', '0', '0', '642', '1', '71', '0', '12', '4294967295', '2', '0', '1', '683', '1', '0'); +REPLACE 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`) VALUES ('6080', 'Heightened Endurance IV', '5', '2', '4294967295', '4294967295', '683', '683', '7', '4294967295', '5130', '5', '0', '0', '642', '1', '75', '0', '14', '4294967295', '2', '0', '1', '683', '4', '0'); +REPLACE 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`) VALUES ('7604', 'Heightened Endurance IV', '5', '2', '4294967295', '4294967295', '683', '683', '7', '4294967295', '6080', '2', '0', '0', '642', '1', '80', '0', '15', '4294967295', '2', '0', '1', '683', '5', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1036', '1', '189', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1037', '1', '189', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1039', '1', '189', '6', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5130', '1', '189', '7', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5131', '1', '189', '8', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5132', '1', '189', '9', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5133', '1', '189', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5134', '1', '189', '11', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6080', '1', '189', '12', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6081', '1', '189', '13', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7604', '1', '189', '14', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7605', '1', '189', '15', '0'); + +-- Shield Block II/III +REPLACE 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`) VALUES ('5516', 'Shield Block II', '8', '3', '4294967295', '4294967295', '1287', '1287', '7', '1287', '3', '0', '0', '0', '42', '0', '76', '1', '14', '4294967295', '2', '0', '1', '1287', '4', '0'); +REPLACE 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`) VALUES ('12469', 'Shield Block III', '7', '3', '4294967295', '4294967295', '1287', '1287', '7', '5516', '3', '0', '0', '0', '42', '0', '81', '2', '16', '4294967295', '2', '0', '1', '1287', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5516', '1', '320', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5517', '1', '320', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5518', '1', '320', '3', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12469', '1', '320', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12470', '1', '320', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12471', '1', '320', '3', '0'); + +-- Spell Casting Subtlty +REPLACE 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`) VALUES ('98', 'Spell Casting Subtlety', '2', '3', '4294967295', '4294967295', '13548', '13549', '2', '0', '0', '0', '0', '0', '30976', '0', '55', '2', '3', '4294967295', '2', '0', '1', '98', '1', '0'); +REPLACE 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`) VALUES ('738', 'Spell Casting Subtlety', '3', '3', '4294967295', '4294967295', '738', '738', '6', '0', '0', '0', '0', '0', '1092', '0', '65', '3', '7', '4294967295', '2', '0', '1', '738', '1', '0'); +REPLACE 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`) VALUES ('5317', 'Spell Casting Subtlety II', '4', '3', '4294967295', '4294967295', '738', '738', '6', '738', '3', '0', '0', '0', '1092', '0', '71', '0', '12', '4294967295', '2', '0', '1', '738', '4', '0'); +REPLACE 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`) VALUES ('5628', 'Spell Casting Subtlety III', '7', '3', '4294967295', '4294967295', '738', '738', '6', '5317', '3', '0', '0', '0', '1092', '0', '76', '2', '14', '4294967295', '2', '0', '1', '738', '4', '0'); +REPLACE 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`) VALUES ('7583', 'Spell Casting Subtlety IV', '7', '3', '4294967295', '4294967295', '738', '738', '6', '5628', '3', '0', '0', '0', '1092', '0', '81', '2', '15', '4294967295', '2', '0', '1', '738', '5', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('98', '1', '114', '-4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('99', '1', '114', '-12', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('100', '1', '114', '-20', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('738', '1', '114', '-4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('739', '1', '114', '-12', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('740', '1', '114', '-20', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5317', '1', '114', '-3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5318', '1', '114', '-6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5319', '1', '114', '-9', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5628', '1', '114', '-3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5629', '1', '114', '-4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5630', '1', '114', '-5', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7583', '1', '114', '-2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7584', '1', '114', '-4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7585', '1', '114', '-6', '0'); + +-- Theft of Life/Advanced ToL/Soul Thief +REPLACE 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`) VALUES ('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'); +REPLACE 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`) VALUES ('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'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('634', '1', '274', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('635', '1', '274', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('636', '1', '274', '10', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('844', '1', '274', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('845', '1', '274', '6', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1319', '1', '274', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1320', '1', '274', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1321', '1', '274', '6', '0'); + +-- Soul Abrasion *You may need to do optional SQL to adjust spell data tables for Soul Abrasion to work. +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('210', '1', '302', '50', '50'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('210', '2', '385', '99', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('211', '1', '302', '100', '100'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('211', '2', '385', '99', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('212', '1', '302', '200', '200'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('212', '2', '385', '99', '0'); + +-- Quick Damage +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('141', '1', '127', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('141', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('141', '3', '138', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('141', '4', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('141', '5', '143', '3000', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('142', '1', '127', '25', '25'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('142', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('142', '3', '138', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('142', '4', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('142', '5', '143', '3000', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('143', '1', '127', '50', '50'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('143', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('143', '3', '138', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('143', '4', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('143', '5', '143', '3000', '0'); + + +-- Quick Evacuation +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('137', '1', '127', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('137', '2', '137', '88', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('138', '1', '127', '25', '25'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('138', '2', '137', '88', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('139', '1', '127', '50', '50'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('139', '2', '137', '88', '0'); + +-- Quick Summoning +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('164', '1', '127', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('164', '2', '137', '32', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('164', '3', '127', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('164', '4', '137', '33', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('164', '5', '127', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('164', '6', '137', '82', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('164', '7', '127', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('164', '8', '137', '152', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('164', '9', '143', '3000', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('165', '1', '127', '25', '25'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('165', '2', '137', '32', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('165', '3', '127', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('165', '4', '137', '33', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('165', '5', '127', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('165', '6', '137', '82', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('165', '7', '127', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('165', '8', '137', '152', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('165', '9', '143', '3000', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('166', '1', '127', '50', '50'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('166', '2', '137', '32', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('166', '3', '127', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('166', '4', '137', '33', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('166', '5', '127', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('166', '6', '137', '82', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('166', '7', '127', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('166', '8', '137', '152', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('166', '9', '143', '3000', '0'); + +-- Quick Buff +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('147', '1', '127', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('147', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('147', '3', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('147', '4', '143', '3000', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('148', '1', '127', '25', '25'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('148', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('148', '3', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('148', '4', '143', '3000', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('149', '1', '127', '50', '50'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('149', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('149', '3', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('149', '4', '143', '3000', '0'); + +-- Spell Casting Deftness +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('104', '1', '127', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('104', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('104', '3', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('104', '4', '143', '3000', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('105', '1', '127', '25', '25'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('105', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('105', '3', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('105', '4', '143', '3000', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('106', '1', '127', '50', '50'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('106', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('106', '3', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('106', '4', '143', '3000', '0'); + +-- Healing Adept I-III -- Healing Adept IV +REPLACE 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`) VALUES ('12449', 'Healing Adept IV', '10', '3', '4294967295', '4294967295', '77', '77', '7', '4294967295', '1083', '3', '0', '0', '33884', '0', '81', '1', '16', '4294967295', '2', '0', '1', '77', '6', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('77', '1', '125', '2', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('77', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('77', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('77', '4', '139', '-6232', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('77', '5', '139', '-6264', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('77', '6', '125', '2', '2'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('77', '7', '137', '147', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('77', '8', '141', '1', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('78', '1', '125', '5', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('78', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('78', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('78', '4', '139', '-6232', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('78', '5', '139', '-6264', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('78', '6', '125', '5', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('78', '7', '137', '147', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('78', '8', '141', '1', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('79', '1', '125', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('79', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('79', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('79', '4', '139', '-6232', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('79', '5', '139', '-6264', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('79', '6', '125', '10', '10'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('79', '7', '137', '147', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('79', '8', '141', '1', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('434', '1', '125', '13', '13'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('434', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('434', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('434', '4', '139', '-6232', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('434', '5', '139', '-6264', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('434', '6', '125', '13', '13'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('434', '7', '137', '147', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('434', '8', '141', '1', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('435', '1', '125', '16', '16'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('435', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('435', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('435', '4', '139', '-6232', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('435', '5', '139', '-6264', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('435', '6', '125', '16', '16'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('435', '7', '137', '147', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('435', '8', '141', '1', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('436', '1', '125', '19', '19'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('436', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('436', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('436', '4', '139', '-6232', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('436', '5', '139', '-6264', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('436', '6', '125', '19', '19'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('436', '7', '137', '147', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('436', '8', '141', '1', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1083', '1', '125', '22', '13'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1083', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1083', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1083', '4', '139', '-6232', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1083', '5', '139', '-6264', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1083', '6', '125', '22', '22'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1083', '7', '137', '147', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1083', '8', '141', '1', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1084', '1', '125', '25', '16'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1084', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1084', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1084', '4', '139', '-6232', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1084', '5', '139', '-6264', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1084', '6', '125', '25', '25'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1084', '7', '137', '147', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1084', '8', '141', '1', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1085', '1', '125', '28', '19'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1085', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1085', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1085', '4', '139', '-6232', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1085', '5', '139', '-6264', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1085', '6', '125', '28', '28'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1085', '7', '137', '147', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1085', '8', '141', '1', '0'); +-- HA4 +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12449', '1', '125', '31', '31'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12449', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12449', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12449', '4', '139', '-6232', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12449', '5', '139', '-6264', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12449', '6', '125', '31', '31'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12449', '7', '137', '147', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12449', '8', '141', '1', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12450', '1', '125', '34', '34'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12450', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12450', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12450', '4', '139', '-6232', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12450', '5', '139', '-6264', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12450', '6', '125', '33', '33'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12450', '7', '137', '147', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12450', '8', '141', '1', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12451', '1', '125', '37', '37'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12451', '2', '137', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12451', '3', '141', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12451', '4', '139', '-6232', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12451', '5', '139', '-6264', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12451', '6', '125', '37', '37'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12451', '7', '137', '147', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12451', '8', '141', '1', '0'); + +-- Combat Agility/Lightning Reflexes/Reflexive Mastery/Precognition/CA-V +REPLACE 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`) VALUES ('125', 'Combat Agility', '2', '3', '4294967295', '4294967295', '13566', '13567', '2', '4294967295', '4294967295', '0', '0', '0', '65534', '1', '55', '2', '3', '4294967295', '1', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('125', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('126', '1', '172', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('127', '1', '172', '10', '0'); + +REPLACE 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`) VALUES ('449', 'Lightning Reflexes', '3', '5', '4294967295', '4294967295', '5636', '5637', '5', '4294967295', '125', '3', '125', '3', '65534', '1', '61', '0', '4', '4294967295', '1', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('449', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('450', '1', '172', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('451', '1', '172', '9', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('452', '1', '172', '12', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('453', '1', '172', '15', '0'); + +REPLACE 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`) VALUES ('1061', 'Reflexive Mastery', '5', '5', '4294967295', '4294967295', '30304', '30305', '7', '4294967295', '449', '5', '0', '0', '65534', '1', '66', '0', '8', '4294967295', '1', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1061', '1', '172', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1062', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1063', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1064', '1', '172', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1065', '1', '172', '7', '0'); + +REPLACE 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`) VALUES ('1394', 'Precognition', '5', '5', '4294967295', '4294967295', '1394', '1394', '7', '4294967295', '1061', '5', '0', '0', '65534', '1', '70', '0', '10', '4294967295', '1', '0', '1', '125', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1394', '1', '172', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1395', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1396', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1397', '1', '172', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1398', '1', '172', '7', '0'); + +REPLACE 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`) VALUES ('5519', 'Combat Agility V', '6', '5', '4294967295', '4294967295', '125', '125', '7', '4294967295', '1394', '5', '0', '0', '65534', '1', '76', '1', '14', '4294967295', '1', '0', '1', '125', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5519', '1', '172', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5520', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5521', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5522', '1', '172', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5523', '1', '172', '5', '0'); + +REPLACE 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`) VALUES ('7501', 'Combat Agility VI', '7', '5', '4294967295', '4294967295', '125', '125', '7', '4294967295', '5519', '5', '0', '0', '65534', '1', '81', '1', '15', '4294967295', '1', '0', '1', '125', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7501', '1', '172', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7502', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7503', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7504', '1', '172', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7505', '1', '172', '5', '0'); + +REPLACE 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`) VALUES ('12396', 'Combat Agility VII', '8', '5', '4294967295', '4294967295', '125', '125', '7', '4294967295', '7501', '5', '0', '0', '65534', '1', '85', '1', '16', '4294967295', '1', '0', '1', '125', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12396', '1', '172', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12397', '1', '172', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12398', '1', '172', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12399', '1', '172', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12400', '1', '172', '5', '0'); + + +-- Revision to CA/CS +-- Combat Stability/Innate Defense/Defensive Instincts/Thick Skin/CS V +REPLACE 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`) VALUES ('122', 'Combat Stability', '2', '3', '4294967295', '4294967295', '13564', '13565', '2', '4294967295', '4294967295', '0', '0', '0', '65534', '1', '55', '2', '3', '4294967295', '1', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('122', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('123', '1', '259', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('124', '1', '259', '10', '0'); + +REPLACE 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`) VALUES ('454', 'Innate Defense', '3', '5', '4294967295', '4294967295', '5638', '5639', '5', '4294967295', '122', '3', '122', '3', '65534', '1', '61', '0', '4', '4294967295', '1', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('454', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('455', '1', '259', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('456', '1', '259', '9', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('457', '1', '259', '12', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('458', '1', '259', '15', '0'); + +REPLACE 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`) VALUES ('1066', 'Defensive Instincts', '5', '5', '4294967295', '4294967295', '30324', '30325', '7', '4294967295', '454', '5', '0', '0', '65534', '1', '66', '0', '8', '4294967295', '1', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1066', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1067', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1068', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1069', '1', '259', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1070', '1', '259', '7', '0'); + +REPLACE 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`) VALUES ('1399', 'Thick Skin', '5', '5', '4294967295', '4294967295', '1399', '1399', '7', '4294967295', '1066', '5', '1066', '5', '65534', '1', '70', '0', '10', '4294967295', '1', '0', '1', '122', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1399', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1400', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1401', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1402', '1', '259', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1403', '1', '259', '7', '0'); + +REPLACE 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`) VALUES ('5524', 'Combat Stability V', '6', '5', '4294967295', '4294967295', '122', '122', '7', '4294967295', '1399', '5', '0', '0', '65534', '1', '76', '1', '14', '4294967295', '1', '0', '1', '122', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5524', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5525', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5526', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5527', '1', '259', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5528', '1', '259', '5', '0'); + +REPLACE 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`) VALUES ('7506', 'Combat Stability VI', '7', '5', '4294967295', '4294967295', '122', '122', '7', '4294967295', '5524', '5', '0', '0', '65534', '1', '81', '1', '15', '4294967295', '1', '0', '1', '122', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7506', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7507', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7508', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7509', '1', '259', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7510', '1', '259', '5', '0'); + +REPLACE 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`) VALUES ('12401', 'Combat Stability VII', '8', '5', '4294967295', '4294967295', '122', '122', '7', '4294967295', '7506', '5', '0', '0', '65534', '1', '85', '1', '16', '4294967295', '1', '0', '1', '122', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12401', '1', '259', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12402', '1', '259', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12403', '1', '259', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12404', '1', '259', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12405', '1', '259', '5', '0'); + +-- Delay Death upgrades +REPLACE 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`) VALUES ('1389', 'Delay Death II', '3', '5', '4294967295', '4294967295', '1206', '1206', '1', '4294967295', '1026', '5', '0', '0', '65534', '1', '70', '0', '10', '4294967295', '1', '0', '1', '1026', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1389', '1', '328', '50', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1390', '1', '328', '100', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1391', '1', '328', '150', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1392', '1', '328', '200', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1393', '1', '328', '250', '0'); + +REPLACE 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`) VALUES ('4683', 'Delay Death III', '3', '5', '4294967295', '4294967295', '1206', '1206', '1', '4294967295', '1389', '5', '0', '0', '65534', '1', '71', '0', '12', '4294967295', '1', '0', '1', '1026', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4683', '1', '328', '50', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4684', '1', '328', '100', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4685', '1', '328', '150', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4686', '1', '328', '200', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4687', '1', '328', '250', '0'); + +REPLACE 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`) VALUES ('6523', 'Delay Death IV', '3', '5', '4294967295', '4294967295', '1206', '1206', '1', '4294967295', '4683', '5', '0', '0', '65534', '1', '76', '0', '14', '4294967295', '1', '0', '1', '1026', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6523', '1', '328', '50', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6524', '1', '328', '100', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6525', '1', '328', '150', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6526', '1', '328', '200', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6527', '1', '328', '250', '0'); + +REPLACE 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`) VALUES ('7511', 'Delay Death V', '3', '5', '4294967295', '4294967295', '1206', '1206', '1', '4294967295', '6523', '5', '0', '0', '65534', '1', '81', '0', '15', '4294967295', '1', '0', '1', '1026', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7511', '1', '328', '50', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7512', '1', '328', '100', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7513', '1', '328', '150', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7514', '1', '328', '200', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7515', '1', '328', '250', '0'); + +-- Energetic Attunement +REPLACE 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`) VALUES ('1056', 'Energetic Attunement', '3', '5', '4294967295', '4294967295', '1206', '1206', '1', '4294967295', '0', '0', '0', '0', '65534', '1', '71', '0', '12', '4294967295', '1', '0', '1', '1056', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1056', '1', '317', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1057', '1', '317', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1058', '1', '317', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1059', '1', '317', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1060', '1', '317', '5', '0'); + +REPLACE 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`) VALUES ('6431', 'Energetic Attunement II', '3', '5', '4294967295', '4294967295', '1643', '1643', '1', '4294967295', '1056', '5', '0', '0', '65534', '1', '76', '0', '14', '4294967295', '1', '0', '1', '1056', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6431', '1', '317', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6432', '1', '317', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6433', '1', '317', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6434', '1', '317', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6435', '1', '317', '5', '0'); + +REPLACE 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`) VALUES ('7522', 'Energetic Attunement III', '3', '5', '4294967295', '4294967295', '1643', '1643', '1', '4294967295', '6431', '5', '0', '0', '65534', '1', '81', '0', '15', '4294967295', '1', '0', '1', '1056', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6431', '1', '317', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6432', '1', '317', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6433', '1', '317', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6434', '1', '317', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6435', '1', '317', '5', '0'); + +-- General Sturdiness +REPLACE 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`) VALUES ('6119', 'General Sturdiness', '6', '5', '4294967295', '4294967295', '6119', '6119', '1', '4294967295', '0', '0', '0', '0', '65534', '1', '76', '0', '14', '4294967295', '1', '0', '1', '6119', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6119', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6119', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6119', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6119', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6119', '1', '69', '500', '0'); + +REPLACE 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`) VALUES ('7526', 'General Sturdiness II', '6', '5', '4294967295', '4294967295', '6119', '6119', '1', '4294967295', '6119', '5', '0', '0', '65534', '1', '81', '0', '15', '4294967295', '1', '0', '1', '6119', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7526', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7526', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7526', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7526', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7526', '1', '69', '500', '0'); + +REPLACE 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`) VALUES ('12406', 'General Sturdiness III', '6', '5', '4294967295', '4294967295', '6119', '6119', '1', '4294967295', '7526', '5', '0', '0', '65534', '1', '81', '0', '16', '4294967295', '1', '0', '1', '6119', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12406', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12407', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12408', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12409', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12410', '1', '69', '500', '0'); + +-- SOD Discordant Defiance +REPLACE 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`) VALUES ('7516', 'Discordant Defiance II', '5', '5', '4294967295', '4294967295', '1006', '1006', '1', '4294967295', '1006', '5', '0', '0', '65534', '1', '71', '0', '15', '4294967295', '1', '0', '1', '1006', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7516', '1','262', '10', '7' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7516', '2','262', '10', '8' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7516', '3','262', '10', '9' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7516', '4','262', '10', '10' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7516', '5','262', '10', '11' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7517', '1','262', '20', '7' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7517', '2','262', '20', '8' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7517', '3','262', '20', '9' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7517', '4','262', '20', '10' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7517', '5','262', '20', '11' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7518', '1','262', '30', '7' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7518', '2','262', '30', '8' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7518', '3','262', '30', '9' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7518', '4','262', '30', '10' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7518', '5','262', '30', '11' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7519', '1','262', '40', '7' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7519', '2','262', '40', '8' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7519', '3','262', '40', '9' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7519', '4','262', '40', '10' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7519', '5','262', '40', '11' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7520', '1','262', '50', '7' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7520', '2','262', '50', '8' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7520', '3','262', '50', '9' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7520', '4','262', '50', '10' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7520', '5','262', '50', '11'); + +-- Mystical Attuning +REPLACE 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`) VALUES ('6521', 'Mystical Attuning II', '5', '2', '4294967295', '4294967295', '1021', '1021', '1', '4294967295', '1021', '5', '0', '0', '65534', '1', '75', '0', '14', '4294967295', '1', '0', '1', '1021', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6521', '1','327', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6522', '1','327', '2', '0'); + +REPLACE 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`) VALUES ('7539', 'Mystical Attuning III', '5', '5', '4294967295', '4294967295', '1021', '1021', '1', '4294967295', '6521', '2', '0', '0', '65534', '1', '80', '0', '16', '4294967295', '1', '0', '1', '1021', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7539', '1','327', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7540', '1','327', '2', '0' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7541', '1','327', '3', '0' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7542', '1','327', '4', '0' ); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7543', '1','327', '5', '0' ); + +-- Mystical Shielding +REPLACE 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`) VALUES ('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'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5045', '1','180', '5', '0'); + +-- Mental Fortitude +REPLACE 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`) VALUES ('8228', 'Mental Fortitude', '5', '3', '4294967295', '4294967295', '8828', '8828', '1', '4294967295', '0', '0', '0', '0', '65534', '1', '75', '0', '15', '4294967295', '1', '0', '1', '8228', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8228', '1', '378', '5', '22'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8229', '1', '378', '10', '22'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8230', '1', '378', '15', '22'); + +-- Fix Frenzied Defense +REPLACE 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`) VALUES ('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'); + +-- Fixes for incorrect ID's MoP II-V +REPLACE 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`) VALUES ('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', '12', '735', '1', '0'); +REPLACE 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`) VALUES ('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', '1', '735', '1', '0'); +REPLACE 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`) VALUES ('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', '1', '735', '4', '0'); +REPLACE 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`) VALUES ('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', '1', '735', '4', '0'); +REPLACE 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`) VALUES ('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', '1', '735', '5', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7050', '1', '265', '62', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7051', '1', '265', '64', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7052', '1', '265', '66', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7053', '1', '265', '67', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7054', '1', '265', '68', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7055', '1', '265', '69', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7063', '1', '265', '70', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7064', '1', '265', '71', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7065', '1', '265', '72', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7622', '1', '265', '74', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7623', '1', '265', '76', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7624', '1', '265', '79', '0'); + +-- Data adjustments for effects to be addititive. +-- TripleBS +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('846', '1', '258', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('847', '1', '258', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('848', '1', '258', '15', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1301', '1', '258', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1302', '1', '258', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1303', '1', '258', '15', '0'); + +-- FrontalBS +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('878', '1', '252', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('879', '1', '252', '20', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('880', '1', '252', '30', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1539', '1', '252', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1540', '1', '252', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1541', '1', '252', '15', '0'); + +-- DoubleAttack +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('564', '1', '177', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('565', '1', '177', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('566', '1', '177', '9', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('561', '1', '177', '3', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('562', '1', '177', '6', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('563', '1', '177', '9', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1624', '1', '177', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1625', '1', '177', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1626', '1', '177', '15', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1621', '1', '177', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1622', '1', '177', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1623', '1', '177', '15', '0'); + +-- Flurry +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('255', '1', '279', '7', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('256', '1', '279', '11', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('257', '1', '279', '15', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('542', '1', '279', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('543', '1', '279', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('544', '1', '279', '6', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1163', '1', '279', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1164', '1', '279', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1165', '1', '279', '6', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4795', '1', '279', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4796', '1', '279', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4797', '1', '279', '3', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('815', '1', '279', '7', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('816', '1', '279', '11', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('817', '1', '279', '15', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1616', '1', '279', '7', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1617', '1', '279', '11', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1618', '1', '279', '15', '0'); + +-- Bard song range +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('568', '1', '270', '5', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('569', '1', '270', '10', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('570', '1', '270', '15', '0'); + +-- Enhanced root +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('144', '1', '244', '50', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('567', '1', '244', '12', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5061', '1', '244', '11', '0'); + + +-- Fixes for Warlord Tenacity/Resolute Defiance +REPLACE 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`) VALUES ('912', 'Warlord\'s Tenacity', '3', '3', '912', '912', '912', '912', '6', '4925', '0', '0', '4', '3600', '2', '0', '65', '3', '7', '4294967295', '3', '0', '65', '912', '1', '0'); +REPLACE 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`) VALUES ('1330', 'Resolute Defiance', '3', '3', '912', '912', '1330', '1330', '7', '4925', '912', '3', '4', '3600', '2', '0', '70', '3', '8', '4294967295', '3', '0', '70', '912', '1', '0'); +UPDATE aa_actions SET reuse_time = 3600 WHERE aaid = 912; +UPDATE aa_actions SET reuse_time = 3600 WHERE aaid = 1330; + +-- Misc display fix. +UPDATE `altadv_vars` SET `sof_type` = 1 WHERE `sof_next_skill` = 122; +UPDATE `altadv_vars` SET `sof_type` = 1 WHERE `sof_next_skill` = 125; +UPDATE `altadv_vars` SET `sof_type` = 2 WHERE `sof_next_skill` = 599; +UPDATE `altadv_vars` SET `sof_type` = 2 WHERE `sof_next_skill` = 1536; +UPDATE `altadv_vars` SET `sof_type` = 2 WHERE `sof_next_skill` = 625; +UPDATE `altadv_vars` SET `sof_type` = 2 WHERE `sof_next_skill` = 1435; +UPDATE `altadv_vars` SET `sof_type` = 2 WHERE `sof_next_skill` = 1210; + +-- Remove redundant/error entry +DELETE FROM altadv_vars WHERE skill_id = 1143; +DELETE FROM altadv_vars WHERE skill_id = 7056; +DELETE FROM altadv_vars WHERE skill_id = 7059; +DELETE FROM altadv_vars WHERE skill_id = 7066; + +-- Misc pre req fixes +UPDATE `altadv_vars` SET `prereq_skill` = 676 WHERE `skill_id` = 978; +UPDATE `altadv_vars` SET `prereq_skill` = 267 WHERE `skill_id` = 92; +UPDATE `altadv_vars` SET `prereq_skill` = 533 WHERE `skill_id` = 155; +UPDATE `altadv_vars` SET `prereq_skill` = 213 WHERE `skill_id` = 700; +UPDATE `altadv_vars` SET `prereq_skill` = 230 WHERE `skill_id` = 539; +UPDATE `altadv_vars` SET `prereq_skill` = 640 WHERE `skill_id` = 924; +UPDATE `altadv_vars` SET `prereq_skill` = 288 WHERE `skill_id` = 1129; +UPDATE `altadv_vars` SET `prereq_skill` = 418 WHERE `skill_id` = 1001; +UPDATE `altadv_vars` SET `prereq_skill` = 589 WHERE `skill_id` = 893; +UPDATE `altadv_vars` SET `prereq_skill` = 619 WHERE `skill_id` = 721; +UPDATE `altadv_vars` SET `prereq_skill` = 637 WHERE `skill_id` = 770; +UPDATE `altadv_vars` SET `prereq_skill` = 1340 WHERE `skill_id` = 724; +UPDATE `altadv_vars` SET `prereq_skill` = 1341 WHERE `skill_id` = 729; + +UPDATE `altadv_vars` SET `sof_next_skill` = 1342 WHERE `skill_id` = 1342; + +-- Unneccessary data. +DELETE FROM aa_effects WHERE aaid=80 AND slot=2; +DELETE FROM aa_effects WHERE aaid=81 AND slot=2; +DELETE FROM aa_effects WHERE aaid=82 AND slot=2; + +-- Update all sof_max_level by finding the sum of all AA that share the same sof_next_skill + +CREATE TEMPORARY TABLE temp_table ( +sof_next_skill INT(11) DEFAULT 0 PRIMARY KEY, +sof_max_level INT(11) DEFAULT 0 +); + +INSERT INTO temp_table ( temp_table.sof_next_skill ) (SELECT altadv_vars.sof_next_skill FROM altadv_vars GROUP BY altadv_vars.sof_next_skill); + +UPDATE temp_table SET temp_table.sof_max_level = (SELECT SUM(altadv_vars.max_level) FROM altadv_vars WHERE altadv_vars.sof_next_skill = temp_table.sof_next_skill); + +UPDATE altadv_vars, temp_table SET altadv_vars.sof_max_level = temp_table.sof_max_level WHERE altadv_vars.sof_next_skill = temp_table.sof_next_skill; + +DROP TEMPORARY TABLE temp_table; + +-- Add new fields for AA stacking. + +ALTER TABLE `altadv_vars` +ADD COLUMN `sof_current_level` tinyint(3) unsigned NOT NULL default '0' AFTER `account_time_required`, +ADD COLUMN `sof_next_id` int(10) unsigned unsigned NOT NULL default '0' AFTER `sof_current_level`, +ADD COLUMN `level_inc` tinyint(3) unsigned NOT NULL default '0' AFTER `sof_next_id`; + +-- Populate new fields + +UPDATE altadv_vars SET level_inc = 5 WHERE skill_id = 8215; +UPDATE altadv_vars SET level_inc = 5 WHERE skill_id = 8228; +UPDATE altadv_vars SET level_inc = 5 WHERE skill_id = 8224; + +UPDATE altadv_vars SET level_inc = 1 WHERE aa_expansion = 12 AND class_type = 71 AND max_level = 5; +UPDATE altadv_vars SET level_inc = 2 WHERE aa_expansion = 12 AND class_type = 71 AND max_level = 3; + +UPDATE altadv_vars SET level_inc = 1 WHERE aa_expansion = 14 AND class_type = 76 AND max_level = 5; +UPDATE altadv_vars SET level_inc = 2 WHERE aa_expansion = 14 AND class_type = 76 AND max_level = 3; + +UPDATE altadv_vars SET level_inc = 1 WHERE aa_expansion = 15 AND class_type = 81 AND max_level = 5; +UPDATE altadv_vars SET level_inc = 2 WHERE aa_expansion = 15 AND class_type = 81 AND max_level = 3; + +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 42 ; +UPDATE altadv_vars SET sof_current_level = 1 WHERE skill_id = 130 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 263 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 267 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 292 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 302 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 312 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 322 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 332 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 342 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 352 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 362 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 372 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 382 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 392 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 402 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 434 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 437 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 440 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 443 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 449 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 454 ; +UPDATE altadv_vars SET sof_current_level = 1 WHERE skill_id = 531 ; +UPDATE altadv_vars SET sof_current_level = 1 WHERE skill_id = 533 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 539 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 542 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 672 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 674 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 676 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 721 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 924 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 978 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 1001 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 1031 ; +UPDATE altadv_vars SET sof_current_level = 6 WHERE skill_id = 1053 ; +UPDATE altadv_vars SET sof_current_level = 8 WHERE skill_id = 1061 ; +UPDATE altadv_vars SET sof_current_level = 8 WHERE skill_id = 1066 ; +UPDATE altadv_vars SET sof_current_level = 6 WHERE skill_id = 1083 ; +UPDATE altadv_vars SET sof_current_level = 6 WHERE skill_id = 1086 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 1099 ; +UPDATE altadv_vars SET sof_current_level = 1 WHERE skill_id = 1129 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 1340 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 1341 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 770 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 893 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 700 ; +UPDATE altadv_vars SET sof_current_level = 0 WHERE skill_id = 1287 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 1158 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 1621 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 1536 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 1624 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 1524 ; +UPDATE altadv_vars SET sof_current_level = 13 WHERE skill_id = 1394 ; +UPDATE altadv_vars SET sof_current_level = 18 WHERE skill_id = 5519 ; +UPDATE altadv_vars SET sof_current_level = 13 WHERE skill_id = 1399 ; +UPDATE altadv_vars SET sof_current_level = 18 WHERE skill_id = 5524 ; +UPDATE altadv_vars SET sof_current_level = 6 WHERE skill_id = 1163 ; +UPDATE altadv_vars SET sof_current_level = 9 WHERE skill_id = 4795 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 7050 ; +UPDATE altadv_vars SET sof_current_level = 6 WHERE skill_id = 7053 ; +UPDATE altadv_vars SET sof_current_level = 9 WHERE skill_id = 7063 ; +UPDATE altadv_vars SET sof_current_level = 12 WHERE skill_id = 7622 ; +UPDATE altadv_vars SET sof_current_level = 1 WHERE skill_id = 5061 ; +UPDATE altadv_vars SET sof_current_level = 23 WHERE skill_id = 7501 ; +UPDATE altadv_vars SET sof_current_level = 28 WHERE skill_id = 12396 ; +UPDATE altadv_vars SET sof_current_level = 23 WHERE skill_id = 7506 ; +UPDATE altadv_vars SET sof_current_level = 28 WHERE skill_id = 12401 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 1389 ; +UPDATE altadv_vars SET sof_current_level = 10 WHERE skill_id = 4683 ; +UPDATE altadv_vars SET sof_current_level = 15 WHERE skill_id = 6523 ; +UPDATE altadv_vars SET sof_current_level = 20 WHERE skill_id = 7511 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 6431 ; +UPDATE altadv_vars SET sof_current_level = 10 WHERE skill_id = 7522 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 7526 ; +UPDATE altadv_vars SET sof_current_level = 10 WHERE skill_id = 12406 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 7516 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 6521 ; +UPDATE altadv_vars SET sof_current_level = 7 WHERE skill_id = 7539 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 4733 ; +UPDATE altadv_vars SET sof_current_level = 6 WHERE skill_id = 7641 ; +UPDATE altadv_vars SET sof_current_level = 0 WHERE skill_id = 1592 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 4725 ; +UPDATE altadv_vars SET sof_current_level = 10 WHERE skill_id = 5557 ; +UPDATE altadv_vars SET sof_current_level = 15 WHERE skill_id = 7568 ; +UPDATE altadv_vars SET sof_current_level = 20 WHERE skill_id = 12439 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 8261 ; +UPDATE altadv_vars SET sof_current_level = 6 WHERE skill_id = 5534 ; +UPDATE altadv_vars SET sof_current_level = 9 WHERE skill_id = 7659 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 1036 ; +UPDATE altadv_vars SET sof_current_level = 6 WHERE skill_id = 5130 ; +UPDATE altadv_vars SET sof_current_level = 11 WHERE skill_id = 6080 ; +UPDATE altadv_vars SET sof_current_level = 13 WHERE skill_id = 7604 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 5516 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 5562 ; +UPDATE altadv_vars SET sof_current_level = 6 WHERE skill_id = 7573 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 5529 ; +UPDATE altadv_vars SET sof_current_level = 10 WHERE skill_id = 7554 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 4755 ; +UPDATE altadv_vars SET sof_current_level = 9 WHERE skill_id = 7582 ; +UPDATE altadv_vars SET sof_current_level = 9 WHERE skill_id = 4779 ; +UPDATE altadv_vars SET sof_current_level = 12 WHERE skill_id = 5592 ; +UPDATE altadv_vars SET sof_current_level = 15 WHERE skill_id = 7590 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 8305 ; +UPDATE altadv_vars SET sof_current_level = 9 WHERE skill_id = 4721 ; +UPDATE altadv_vars SET sof_current_level = 12 WHERE skill_id = 5554 ; +UPDATE altadv_vars SET sof_current_level = 18 WHERE skill_id = 6023 ; +UPDATE altadv_vars SET sof_current_level = 15 WHERE skill_id = 7565 ; +UPDATE altadv_vars SET sof_current_level = 6 WHERE skill_id = 12423 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 7647 ; +UPDATE altadv_vars SET sof_current_level = 6 WHERE skill_id = 7670 ; +UPDATE altadv_vars SET sof_current_level = 18 WHERE skill_id = 12454 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 5317 ; +UPDATE altadv_vars SET sof_current_level = 6 WHERE skill_id = 5628 ; +UPDATE altadv_vars SET sof_current_level = 9 WHERE skill_id = 7583 ; +UPDATE altadv_vars SET sof_current_level = 9 WHERE skill_id = 12449 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 504 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 1290 ; +UPDATE altadv_vars SET sof_current_level = 10 WHERE skill_id = 4678 ; +UPDATE altadv_vars SET sof_current_level = 15 WHERE skill_id = 7547 ; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 844 ; +UPDATE altadv_vars SET sof_current_level = 5 WHERE skill_id = 1319 ; +UPDATE altadv_vars SET sof_current_level = 6 WHERE skill_id = 12469 ; + +UPDATE altadv_vars SET sof_next_id = 292 WHERE skill_id = 2 ; +UPDATE altadv_vars SET sof_next_id = 302 WHERE skill_id = 7 ; +UPDATE altadv_vars SET sof_next_id = 312 WHERE skill_id = 12 ; +UPDATE altadv_vars SET sof_next_id = 322 WHERE skill_id = 17 ; +UPDATE altadv_vars SET sof_next_id = 332 WHERE skill_id = 22 ; +UPDATE altadv_vars SET sof_next_id = 342 WHERE skill_id = 27 ; +UPDATE altadv_vars SET sof_next_id = 352 WHERE skill_id = 32 ; +UPDATE altadv_vars SET sof_next_id = 362 WHERE skill_id = 37 ; +UPDATE altadv_vars SET sof_next_id = 372 WHERE skill_id = 42 ; +UPDATE altadv_vars SET sof_next_id = 382 WHERE skill_id = 47 ; +UPDATE altadv_vars SET sof_next_id = 392 WHERE skill_id = 52 ; +UPDATE altadv_vars SET sof_next_id = 402 WHERE skill_id = 57 ; +UPDATE altadv_vars SET sof_next_id = 672 WHERE skill_id = 62 ; +UPDATE altadv_vars SET sof_next_id = 674 WHERE skill_id = 65 ; +UPDATE altadv_vars SET sof_next_id = 676 WHERE skill_id = 71 ; +UPDATE altadv_vars SET sof_next_id = 263 WHERE skill_id = 74 ; +UPDATE altadv_vars SET sof_next_id = 434 WHERE skill_id = 77 ; +UPDATE altadv_vars SET sof_next_id = 437 WHERE skill_id = 80 ; +UPDATE altadv_vars SET sof_next_id = 267 WHERE skill_id = 92 ; +UPDATE altadv_vars SET sof_next_id = 1031 WHERE skill_id = 110 ; +UPDATE altadv_vars SET sof_next_id = 443 WHERE skill_id = 113 ; +UPDATE altadv_vars SET sof_next_id = 440 WHERE skill_id = 119 ; +UPDATE altadv_vars SET sof_next_id = 454 WHERE skill_id = 122 ; +UPDATE altadv_vars SET sof_next_id = 449 WHERE skill_id = 125 ; +UPDATE altadv_vars SET sof_next_id = 531 WHERE skill_id = 131 ; +UPDATE altadv_vars SET sof_next_id = 533 WHERE skill_id = 155 ; +UPDATE altadv_vars SET sof_next_id = 1524 WHERE skill_id = 190 ; +UPDATE altadv_vars SET sof_next_id = 700 WHERE skill_id = 213 ; +UPDATE altadv_vars SET sof_next_id = 539 WHERE skill_id = 230 ; +UPDATE altadv_vars SET sof_next_id = 542 WHERE skill_id = 255 ; +UPDATE altadv_vars SET sof_next_id = 1129 WHERE skill_id = 288 ; +UPDATE altadv_vars SET sof_next_id = 1001 WHERE skill_id = 418 ; +UPDATE altadv_vars SET sof_next_id = 1083 WHERE skill_id = 434 ; +UPDATE altadv_vars SET sof_next_id = 1086 WHERE skill_id = 437 ; +UPDATE altadv_vars SET sof_next_id = 1053 WHERE skill_id = 440 ; +UPDATE altadv_vars SET sof_next_id = 1061 WHERE skill_id = 449 ; +UPDATE altadv_vars SET sof_next_id = 1066 WHERE skill_id = 454 ; +UPDATE altadv_vars SET sof_next_id = 1163 WHERE skill_id = 542 ; +UPDATE altadv_vars SET sof_next_id = 1624 WHERE skill_id = 561 ; +UPDATE altadv_vars SET sof_next_id = 1621 WHERE skill_id = 564 ; +UPDATE altadv_vars SET sof_next_id = 5061 WHERE skill_id = 567 ; +UPDATE altadv_vars SET sof_next_id = 893 WHERE skill_id = 589 ; +UPDATE altadv_vars SET sof_next_id = 1536 WHERE skill_id = 599 ; +UPDATE altadv_vars SET sof_next_id = 721 WHERE skill_id = 619 ; +UPDATE altadv_vars SET sof_next_id = 4733 WHERE skill_id = 625 ; +UPDATE altadv_vars SET sof_next_id = 770 WHERE skill_id = 637 ; +UPDATE altadv_vars SET sof_next_id = 924 WHERE skill_id = 640 ; +UPDATE altadv_vars SET sof_next_id = 978 WHERE skill_id = 676 ; +UPDATE altadv_vars SET sof_next_id = 7647 WHERE skill_id = 692 ; +UPDATE altadv_vars SET sof_next_id = 1340 WHERE skill_id = 724 ; +UPDATE altadv_vars SET sof_next_id = 1341 WHERE skill_id = 729 ; +UPDATE altadv_vars SET sof_next_id = 7050 WHERE skill_id = 446 ; +UPDATE altadv_vars SET sof_next_id = 7050 WHERE skill_id = 735 ; +UPDATE altadv_vars SET sof_next_id = 1099 WHERE skill_id = 767 ; +UPDATE altadv_vars SET sof_next_id = 7516 WHERE skill_id = 1006 ; +UPDATE altadv_vars SET sof_next_id = 6521 WHERE skill_id = 1021 ; +UPDATE altadv_vars SET sof_next_id = 1389 WHERE skill_id = 1026 ; +UPDATE altadv_vars SET sof_next_id = 4721 WHERE skill_id = 1053 ; +UPDATE altadv_vars SET sof_next_id = 1394 WHERE skill_id = 1061 ; +UPDATE altadv_vars SET sof_next_id = 1399 WHERE skill_id = 1066 ; +UPDATE altadv_vars SET sof_next_id = 12449 WHERE skill_id = 1083 ; +UPDATE altadv_vars SET sof_next_id = 4779 WHERE skill_id = 1086 ; +UPDATE altadv_vars SET sof_next_id = 12423 WHERE skill_id = 1099 ; +UPDATE altadv_vars SET sof_next_id = 4755 WHERE skill_id = 1210 ; +UPDATE altadv_vars SET sof_next_id = 1036 WHERE skill_id = 683 ; +UPDATE altadv_vars SET sof_next_id = 5317 WHERE skill_id = 738 ; +UPDATE altadv_vars SET sof_next_id = 1158 WHERE skill_id = 1134 ; +UPDATE altadv_vars SET sof_next_id = 5516 WHERE skill_id = 1287 ; +UPDATE altadv_vars SET sof_next_id = 5529 WHERE skill_id = 1486 ; +UPDATE altadv_vars SET sof_next_id = 5534 WHERE skill_id = 1536 ; +UPDATE altadv_vars SET sof_next_id = 5519 WHERE skill_id = 1394 ; +UPDATE altadv_vars SET sof_next_id = 7501 WHERE skill_id = 5519 ; +UPDATE altadv_vars SET sof_next_id = 5524 WHERE skill_id = 1399 ; +UPDATE altadv_vars SET sof_next_id = 7506 WHERE skill_id = 5524 ; +UPDATE altadv_vars SET sof_next_id = 4795 WHERE skill_id = 1163 ; +UPDATE altadv_vars SET sof_next_id = 7053 WHERE skill_id = 7050 ; +UPDATE altadv_vars SET sof_next_id = 7063 WHERE skill_id = 7053 ; +UPDATE altadv_vars SET sof_next_id = 7622 WHERE skill_id = 7063 ; +UPDATE altadv_vars SET sof_next_id = 12396 WHERE skill_id = 7501 ; +UPDATE altadv_vars SET sof_next_id = 12401 WHERE skill_id = 7506 ; +UPDATE altadv_vars SET sof_next_id = 4683 WHERE skill_id = 1389 ; +UPDATE altadv_vars SET sof_next_id = 6523 WHERE skill_id = 4683 ; +UPDATE altadv_vars SET sof_next_id = 7511 WHERE skill_id = 6523 ; +UPDATE altadv_vars SET sof_next_id = 7522 WHERE skill_id = 6431 ; +UPDATE altadv_vars SET sof_next_id = 7526 WHERE skill_id = 6119 ; +UPDATE altadv_vars SET sof_next_id = 12406 WHERE skill_id = 7526 ; +UPDATE altadv_vars SET sof_next_id = 7539 WHERE skill_id = 6521 ; +UPDATE altadv_vars SET sof_next_id = 6431 WHERE skill_id = 1056 ; +UPDATE altadv_vars SET sof_next_id = 7641 WHERE skill_id = 4733 ; +UPDATE altadv_vars SET sof_next_id = 4725 WHERE skill_id = 1592 ; +UPDATE altadv_vars SET sof_next_id = 5557 WHERE skill_id = 4725 ; +UPDATE altadv_vars SET sof_next_id = 7568 WHERE skill_id = 5557 ; +UPDATE altadv_vars SET sof_next_id = 12439 WHERE skill_id = 7568 ; +UPDATE altadv_vars SET sof_next_id = 8621 WHERE skill_id = 8232 ; +UPDATE altadv_vars SET sof_next_id = 7659 WHERE skill_id = 5534 ; +UPDATE altadv_vars SET sof_next_id = 5130 WHERE skill_id = 1036 ; +UPDATE altadv_vars SET sof_next_id = 6080 WHERE skill_id = 5130 ; +UPDATE altadv_vars SET sof_next_id = 7604 WHERE skill_id = 6080 ; +UPDATE altadv_vars SET sof_next_id = 12469 WHERE skill_id = 5516 ; +UPDATE altadv_vars SET sof_next_id = 5562 WHERE skill_id = 4739 ; +UPDATE altadv_vars SET sof_next_id = 7573 WHERE skill_id = 5562 ; +UPDATE altadv_vars SET sof_next_id = 7554 WHERE skill_id = 5529; +UPDATE altadv_vars SET sof_next_id = 5557 WHERE skill_id = 4755 ; +UPDATE altadv_vars SET sof_next_id = 5592 WHERE skill_id = 4779 ; +UPDATE altadv_vars SET sof_next_id = 7590 WHERE skill_id = 5592 ; +UPDATE altadv_vars SET sof_next_id = 12454 WHERE skill_id = 7590 ; +UPDATE altadv_vars SET sof_next_id = 8305 WHERE skill_id = 8235 ; +UPDATE altadv_vars SET sof_next_id = 5554 WHERE skill_id = 4721 ; +UPDATE altadv_vars SET sof_next_id = 7565 WHERE skill_id = 5554 ; +UPDATE altadv_vars SET sof_next_id = 6023 WHERE skill_id = 7565 ; +UPDATE altadv_vars SET sof_next_id = 7670 WHERE skill_id = 7647 ; +UPDATE altadv_vars SET sof_next_id = 5628 WHERE skill_id = 5317 ; +UPDATE altadv_vars SET sof_next_id = 7583 WHERE skill_id = 5628 ; +UPDATE altadv_vars SET sof_next_id = 504 WHERE skill_id = 247 ; +UPDATE altadv_vars SET sof_next_id = 1290 WHERE skill_id = 684 ; +UPDATE altadv_vars SET sof_next_id = 7547 WHERE skill_id = 4678; + +UPDATE altadv_vars SET sof_next_id = 844 WHERE skill_id = 634 ; +UPDATE altadv_vars SET sof_next_id = 1319 WHERE skill_id = 844 ; +UPDATE altadv_vars SET sof_next_id = 4678 WHERE skill_id = 1001 ; + diff --git a/utils/sql/svn/2209_optional_additive_bonus_rule.sql b/utils/sql/svn/2209_optional_additive_bonus_rule.sql new file mode 100644 index 000000000..d8ff12e1c --- /dev/null +++ b/utils/sql/svn/2209_optional_additive_bonus_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AdditiveBonusValues', 'false', 'Allow certain bonuses to be calculated by adding together the value from each item, instead of taking the highest value. (ie Add together all Cleave Effects)'); \ No newline at end of file diff --git a/utils/sql/svn/2213_loot_changes.sql b/utils/sql/svn/2213_loot_changes.sql new file mode 100644 index 000000000..b783fcfdb --- /dev/null +++ b/utils/sql/svn/2213_loot_changes.sql @@ -0,0 +1,6 @@ +alter table loottable_entries add `droplimit` tinyint(2) unsigned NOT NULL default 0; +alter table loottable_entries add `mindrop` tinyint(2) unsigned NOT NULL default 0; +alter table lootdrop_entries change `chance` `chance` float not null default 1; +alter table lootdrop_entries add `multiplier` tinyint(2) unsigned NOT NULL default 1; +update loottable_entries set droplimit = 1 where probability != 100; +update loottable_entries set mindrop = multiplier, droplimit = multiplier, multiplier = 1 where probability = 100; diff --git a/utils/sql/svn/2214_faction_list_mod.sql b/utils/sql/svn/2214_faction_list_mod.sql new file mode 100644 index 000000000..ae5d32f24 --- /dev/null +++ b/utils/sql/svn/2214_faction_list_mod.sql @@ -0,0 +1,8 @@ +CREATE TABLE `faction_list_mod` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `faction_id` int(10) unsigned NOT NULL, + `mod` smallint(6) NOT NULL, + `mod_name` varchar(16) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `faction_id_mod_name` (`faction_id`,`mod_name`) +); \ No newline at end of file diff --git a/utils/sql/svn/2215_required_aa_updates.sql b/utils/sql/svn/2215_required_aa_updates.sql new file mode 100644 index 000000000..34604d8b7 --- /dev/null +++ b/utils/sql/svn/2215_required_aa_updates.sql @@ -0,0 +1,19 @@ +-- Spell casting fury fix +REPLACE 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 ('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'); + +REPLACE 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 ('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'); + +REPLACE 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 ('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'); + + +REPLACE 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 ('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'); + +REPLACE 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 ('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'); + +REPLACE 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 ('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'); + +-- Archery Mastery +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 199, 1, 301, 30, 0); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 200, 1, 301, 60, 0); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ( 201, 1, 301, 100, 0); + diff --git a/utils/sql/svn/222_buyer.sql b/utils/sql/svn/222_buyer.sql new file mode 100644 index 000000000..e666d2dee --- /dev/null +++ b/utils/sql/svn/222_buyer.sql @@ -0,0 +1,13 @@ +DROP TABLE IF EXISTS `buyer`; +CREATE TABLE `buyer` ( + `charid` int(11) NOT NULL, + `buyslot` int(11) NOT NULL, + `itemid` int(11) NOT NULL, + `itemname` varchar(65) NOT NULL, + `quantity` int(11) NOT NULL, + `price` int(11) NOT NULL, + PRIMARY KEY (`charid`,`buyslot`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +ALTER TABLE `trader_audit` ADD `trantype` TINYINT NOT NULL DEFAULT '0'; + diff --git a/utils/sql/svn/2243_optional_char_max_level_rule.sql b/utils/sql/svn/2243_optional_char_max_level_rule.sql new file mode 100644 index 000000000..99e74f2d7 --- /dev/null +++ b/utils/sql/svn/2243_optional_char_max_level_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:PerCharacterQglobalMaxLevel', 'false', 'Allows Character Max Level based on qglobal "CharMaxLevel'); \ No newline at end of file diff --git a/utils/sql/svn/2260_probability.sql b/utils/sql/svn/2260_probability.sql new file mode 100644 index 000000000..12f277a00 --- /dev/null +++ b/utils/sql/svn/2260_probability.sql @@ -0,0 +1,3 @@ +-- Not needed if you ran 2213_loot_changes.sql after 12/14/12 + +ALTER TABLE `loottable_entries` ADD `probability` FLOAT NOT NULL DEFAULT '100'; \ No newline at end of file diff --git a/utils/sql/svn/2262_required_pet_discipline_update.sql b/utils/sql/svn/2262_required_pet_discipline_update.sql new file mode 100644 index 000000000..d10350441 --- /dev/null +++ b/utils/sql/svn/2262_required_pet_discipline_update.sql @@ -0,0 +1,6 @@ +DELETE FROM `aa_effects` WHERE aaid = 288 or aaid = 1129 or aaid = 1130; +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '288', '1', '267', '1', '15'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '1129', '1', '267', '1', '0'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '1129', '2', '267', '1', '24'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '1130', '1', '267', '1', '21'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '1130', '2', '267', '1', '24'); \ No newline at end of file diff --git a/utils/sql/svn/2264_required_aa_updates.sql b/utils/sql/svn/2264_required_aa_updates.sql new file mode 100644 index 000000000..a7b4cd564 --- /dev/null +++ b/utils/sql/svn/2264_required_aa_updates.sql @@ -0,0 +1,40 @@ +-- update Improved Familiar to not require allegiant familiar to purchase +UPDATE `altadv_vars` SET `prereq_skill`=0 WHERE `skill_id`='155'; +-- Clear Any Prior Stuff on Animation Empathy -- +DELETE FROM `aa_effects` WHERE aaid = 580 or aaid = 581 or aaid = 582; +-- Animation Empathy Rank I -- +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '580', '1', '267', '1', '4'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '580', '2', '267', '1', '8'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '580', '3', '267', '1', '5'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '580', '4', '254', '0', '0'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '580', '5', '254', '0', '0'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '580', '6', '254', '0', '0'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '580', '7', '254', '0', '0'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '580', '8', '254', '0', '0'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '580', '9', '254', '0', '0'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '580', '10', '254', '0', '0'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '580', '11', '254', '0', '0'); +-- Animation Empathy Rank II -- +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '581', '1', '267', '1', '4'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '581', '2', '267', '1', '8'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '581', '3', '267', '1', '5'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '581', '4', '267', '1', '2'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '581', '5', '267', '1', '7'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '581', '6', '254', '0', '0'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '581', '7', '254', '0', '0'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '581', '8', '254', '0', '0'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '581', '9', '254', '0', '0'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '581', '10', '254', '0', '0'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '581', '11', '254', '0', '0'); +-- Animation Empathy Rank III -- +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '582', '1', '267', '1', '4'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '582', '2', '267', '1', '8'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '582', '3', '267', '1', '5'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '582', '4', '267', '1', '2'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '582', '5', '267', '1', '7'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '582', '6', '267', '1', '6'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '582', '7', '267', '1', '9'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '582', '8', '267', '1', '12'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '582', '9', '267', '1', '11'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '582', '10', '267', '1', '28'); +INSERT INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '582', '11', '267', '1', '1'); \ No newline at end of file diff --git a/utils/sql/svn/2268_required_updates.sql b/utils/sql/svn/2268_required_updates.sql new file mode 100644 index 000000000..96945da73 --- /dev/null +++ b/utils/sql/svn/2268_required_updates.sql @@ -0,0 +1,25 @@ +/* Akkadius */ + +-- Heap Corruption Fix -- +DELETE FROM `aa_effects` WHERE `id`=3346 LIMIT 1; + +-- QGlobal Changes -- +ALTER TABLE `quest_globals` +MODIFY COLUMN `value` varchar(128) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '?' AFTER `name`; + +ALTER TABLE `quest_globals` +DROP COLUMN `id`, +MODIFY COLUMN `charid` int(11) NOT NULL DEFAULT 0 FIRST , +DROP PRIMARY KEY, +ADD PRIMARY KEY (`charid`, `npcid`, `zoneid`, `name`); + +-- Add First Logon Status, required for EVENT_CONNECT in player.pl -- +ALTER TABLE `character_` +ADD COLUMN `firstlogon` tinyint(3) NOT NULL DEFAULT 0 AFTER `xtargets`; + +-- QueryServ Rules -- +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'QueryServ:PlayerLogNPCKills', 'false', ''); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'QueryServ:PlayerLogTrades', 'false', ''); + +-- Flow Perl Commands to EVENT_SAY in player.pl/global_player.pl -- +INSERT INTO `rule_values` VALUES ('1', 'Chat:FlowCommandstoPerl_EVENT_SAY', 'true', ''); \ No newline at end of file diff --git a/utils/sql/svn/226_account_limiting.sql b/utils/sql/svn/226_account_limiting.sql new file mode 100644 index 000000000..b2756e8b5 --- /dev/null +++ b/utils/sql/svn/226_account_limiting.sql @@ -0,0 +1,2 @@ +Insert into rule_values values (0, 'World:AccountSessionLimit', -1); +Insert into rule_values values (0, 'World:ExemptAccountLimitStatus', -1); \ No newline at end of file diff --git a/utils/sql/svn/2274_optional_rule_iplimitdisconnectall.sql b/utils/sql/svn/2274_optional_rule_iplimitdisconnectall.sql new file mode 100644 index 000000000..f358b363f --- /dev/null +++ b/utils/sql/svn/2274_optional_rule_iplimitdisconnectall.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'World:IPLimitDisconnectAll', 'false', 'Disconnect all current clients by IP if they go over the IP limit. This should allow people to quickly reconnect in the case of dead sessions waiting to timeout.'); diff --git a/utils/sql/svn/2278_optional_rule_targetableswarmpet.sql b/utils/sql/svn/2278_optional_rule_targetableswarmpet.sql new file mode 100644 index 000000000..b86f208c4 --- /dev/null +++ b/utils/sql/svn/2278_optional_rule_targetableswarmpet.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Pets:TargetableSwarmPet', 'false', 'Set true to allow swarm pets to not be targeted by clients.'); diff --git a/utils/sql/svn/2280_optional_rule_targetableswarmpet-rename.sql b/utils/sql/svn/2280_optional_rule_targetableswarmpet-rename.sql new file mode 100644 index 000000000..bf753b092 --- /dev/null +++ b/utils/sql/svn/2280_optional_rule_targetableswarmpet-rename.sql @@ -0,0 +1,2 @@ + +UPDATE `rule_values` SET `rule_name` = 'Pets:UnTargetableSwarmPet' WHERE `rule_name` = 'Pets:TargetableSwarmPet'; \ No newline at end of file diff --git a/utils/sql/svn/2283_required_npc_changes.sql b/utils/sql/svn/2283_required_npc_changes.sql new file mode 100644 index 000000000..e58e78422 --- /dev/null +++ b/utils/sql/svn/2283_required_npc_changes.sql @@ -0,0 +1,3 @@ +ALTER TABLE `npc_types` +ADD COLUMN `spellscale` float NOT NULL DEFAULT 100 AFTER `emoteid`, +ADD COLUMN `healscale` float NOT NULL DEFAULT 100 AFTER `spellscale`; \ No newline at end of file diff --git a/utils/sql/svn/2299_required_inspectmessage_fields.sql b/utils/sql/svn/2299_required_inspectmessage_fields.sql new file mode 100644 index 000000000..4b90c320f --- /dev/null +++ b/utils/sql/svn/2299_required_inspectmessage_fields.sql @@ -0,0 +1,2 @@ +ALTER TABLE `character_` ADD COLUMN `inspectmessage` VARCHAR(256) NOT NULL DEFAULT ''; +ALTER TABLE `bots` ADD COLUMN `BotInspectMessage` VARCHAR(256) NOT NULL DEFAULT ''; \ No newline at end of file diff --git a/utils/sql/svn/2300_optional_loot_changes.sql b/utils/sql/svn/2300_optional_loot_changes.sql new file mode 100644 index 000000000..79bed179e --- /dev/null +++ b/utils/sql/svn/2300_optional_loot_changes.sql @@ -0,0 +1,3 @@ +ALTER TABLE `lootdrop_entries` +MODIFY COLUMN `maxlevel` tinyint(3) UNSIGNED NOT NULL DEFAULT 255 AFTER `minlevel`; +UPDATE `lootdrop_entries` SET `maxlevel` = 255 WHERE `maxlevel` = 127; \ No newline at end of file diff --git a/utils/sql/svn/230_spells_table.sql b/utils/sql/svn/230_spells_table.sql new file mode 100644 index 000000000..4a06baeb0 --- /dev/null +++ b/utils/sql/svn/230_spells_table.sql @@ -0,0 +1,219 @@ +DROP TABLE IF EXISTS `spells_new`; +CREATE TABLE `spells_new` ( + `id` int(11) NOT NULL, + `name` varchar(64) default NULL, + `player_1` varchar(64) default 'BLUE_TRAIL', + `teleport_zone` varchar(64) default NULL, + `you_cast` varchar(120) default NULL, + `other_casts` varchar(120) default NULL, + `cast_on_you` varchar(120) default NULL, + `cast_on_other` varchar(120) default NULL, + `spell_fades` varchar(120) default NULL, + `range` int(11) NOT NULL default '100', + `aoerange` int(11) NOT NULL default '0', + `pushback` int(11) NOT NULL default '0', + `pushup` int(11) NOT NULL default '0', + `cast_time` int(11) NOT NULL default '0', + `recovery_time` int(11) NOT NULL default '0', + `recast_time` int(11) NOT NULL default '0', + `buffdurationformula` int(11) NOT NULL default '7', + `buffduration` int(11) NOT NULL default '65', + `AEDuration` int(11) NOT NULL default '0', + `mana` int(11) NOT NULL default '0', + `effect_base_value1` int(11) NOT NULL default '100', + `effect_base_value2` int(11) NOT NULL default '0', + `effect_base_value3` int(11) NOT NULL default '0', + `effect_base_value4` int(11) NOT NULL default '0', + `effect_base_value5` int(11) NOT NULL default '0', + `effect_base_value6` int(11) NOT NULL default '0', + `effect_base_value7` int(11) NOT NULL default '0', + `effect_base_value8` int(11) NOT NULL default '0', + `effect_base_value9` int(11) NOT NULL default '0', + `effect_base_value10` int(11) NOT NULL default '0', + `effect_base_value11` int(11) NOT NULL default '0', + `effect_base_value12` int(11) NOT NULL default '0', + `effect_limit_value1` int(11) NOT NULL default '0', + `effect_limit_value2` int(11) NOT NULL default '0', + `effect_limit_value3` int(11) NOT NULL default '0', + `effect_limit_value4` int(11) NOT NULL default '0', + `effect_limit_value5` int(11) NOT NULL default '0', + `effect_limit_value6` int(11) NOT NULL default '0', + `effect_limit_value7` int(11) NOT NULL default '0', + `effect_limit_value8` int(11) NOT NULL default '0', + `effect_limit_value9` int(11) NOT NULL default '0', + `effect_limit_value10` int(11) NOT NULL default '0', + `effect_limit_value11` int(11) NOT NULL default '0', + `effect_limit_value12` int(11) NOT NULL default '0', + `max1` int(11) NOT NULL default '0', + `max2` int(11) NOT NULL default '0', + `max3` int(11) NOT NULL default '0', + `max4` int(11) NOT NULL default '0', + `max5` int(11) NOT NULL default '0', + `max6` int(11) NOT NULL default '0', + `max7` int(11) NOT NULL default '0', + `max8` int(11) NOT NULL default '0', + `max9` int(11) NOT NULL default '0', + `max10` int(11) NOT NULL default '0', + `max11` int(11) NOT NULL default '0', + `max12` int(11) NOT NULL default '0', + `icon` int(11) NOT NULL default '0', + `memicon` int(11) NOT NULL default '0', + `components1` int(11) NOT NULL default '-1', + `components2` int(11) NOT NULL default '-1', + `components3` int(11) NOT NULL default '-1', + `components4` int(11) NOT NULL default '-1', + `component_counts1` int(11) NOT NULL default '1', + `component_counts2` int(11) NOT NULL default '1', + `component_counts3` int(11) NOT NULL default '1', + `component_counts4` int(11) NOT NULL default '1', + `NoexpendReagent1` int(11) NOT NULL default '-1', + `NoexpendReagent2` int(11) NOT NULL default '-1', + `NoexpendReagent3` int(11) NOT NULL default '-1', + `NoexpendReagent4` int(11) NOT NULL default '-1', + `formula1` int(11) NOT NULL default '100', + `formula2` int(11) NOT NULL default '100', + `formula3` int(11) NOT NULL default '100', + `formula4` int(11) NOT NULL default '100', + `formula5` int(11) NOT NULL default '100', + `formula6` int(11) NOT NULL default '100', + `formula7` int(11) NOT NULL default '100', + `formula8` int(11) NOT NULL default '100', + `formula9` int(11) NOT NULL default '100', + `formula10` int(11) NOT NULL default '100', + `formula11` int(11) NOT NULL default '100', + `formula12` int(11) NOT NULL default '100', + `LightType` int(11) NOT NULL default '0', + `goodEffect` int(11) NOT NULL default '0', + `Activated` int(11) NOT NULL default '0', + `resisttype` int(11) NOT NULL default '0', + `effectid1` int(11) NOT NULL default '254', + `effectid2` int(11) NOT NULL default '254', + `effectid3` int(11) NOT NULL default '254', + `effectid4` int(11) NOT NULL default '254', + `effectid5` int(11) NOT NULL default '254', + `effectid6` int(11) NOT NULL default '254', + `effectid7` int(11) NOT NULL default '254', + `effectid8` int(11) NOT NULL default '254', + `effectid9` int(11) NOT NULL default '254', + `effectid10` int(11) NOT NULL default '254', + `effectid11` int(11) NOT NULL default '254', + `effectid12` int(11) NOT NULL default '254', + `targettype` int(11) NOT NULL default '2', + `basediff` int(11) NOT NULL default '0', + `skill` int(11) NOT NULL default '98', + `zonetype` int(11) NOT NULL default '-1', + `EnvironmentType` int(11) NOT NULL default '0', + `TimeOfDay` int(11) NOT NULL default '0', + `classes1` int(11) NOT NULL default '255', + `classes2` int(11) NOT NULL default '255', + `classes3` int(11) NOT NULL default '255', + `classes4` int(11) NOT NULL default '255', + `classes5` int(11) NOT NULL default '255', + `classes6` int(11) NOT NULL default '255', + `classes7` int(11) NOT NULL default '255', + `classes8` int(11) NOT NULL default '255', + `classes9` int(11) NOT NULL default '255', + `classes10` int(11) NOT NULL default '255', + `classes11` int(11) NOT NULL default '255', + `classes12` int(11) NOT NULL default '255', + `classes13` int(11) NOT NULL default '255', + `classes14` int(11) NOT NULL default '255', + `classes15` int(11) NOT NULL default '255', + `classes16` int(11) NOT NULL default '255', + `CastingAnim` int(11) NOT NULL default '44', + `TargetAnim` int(11) NOT NULL default '13', + `TravelType` int(11) NOT NULL default '0', + `SpellAffectIndex` int(11) NOT NULL default '-1', + `field124` int(11) NOT NULL default '0', + `field125` int(11) NOT NULL default '0', + `deities1` int(11) NOT NULL default '0', + `deities2` int(11) NOT NULL default '0', + `deities3` int(11) NOT NULL default '0', + `deities4` int(11) NOT NULL default '0', + `deities5` int(11) NOT NULL default '0', + `deities6` int(11) NOT NULL default '0', + `deities7` int(11) NOT NULL default '0', + `deities8` int(11) NOT NULL default '0', + `deities9` int(11) NOT NULL default '0', + `deities10` int(11) NOT NULL default '0', + `deities11` int(11) NOT NULL default '0', + `deities12` int(12) NOT NULL default '0', + `deities13` int(11) NOT NULL default '0', + `deities14` int(11) NOT NULL default '0', + `deities15` int(11) NOT NULL default '0', + `deities16` int(11) NOT NULL default '0', + `field142` int(11) NOT NULL default '100', + `field143` int(11) NOT NULL default '0', + `new_icon` int(11) NOT NULL default '161', + `spellanim` int(11) NOT NULL default '0', + `uninterruptable` int(11) NOT NULL default '0', + `ResistDiff` int(11) NOT NULL default '-150', + `dot_stacking_exempt` int(11) NOT NULL default '0', + `deleteable` int(11) NOT NULL default '0', + `RecourseLink` int(11) NOT NULL default '0', + `field151` int(11) NOT NULL default '0', + `field152` int(11) NOT NULL default '0', + `field153` int(11) NOT NULL default '0', + `short_buff_box` int(11) NOT NULL default '-1', + `descnum` int(11) NOT NULL default '0', + `typedescnum` int(11) default NULL, + `effectdescnum` int(11) default NULL, + `field158` int(11) default NULL, + `field159` int(11) NOT NULL default '0', + `field160` int(11) NOT NULL default '0', + `field161` int(11) NOT NULL default '0', + `bonushate` int(11) NOT NULL default '0', + `field163` int(11) NOT NULL default '100', + `field164` int(11) NOT NULL default '-150', + `field165` int(11) NOT NULL default '0', + `EndurCost` int(11) NOT NULL default '0', + `EndurTimerIndex` int(11) NOT NULL default '0', + `field168` int(11) NOT NULL default '0', + `field169` int(11) NOT NULL default '0', + `field170` int(11) NOT NULL default '0', + `field171` int(11) NOT NULL default '0', + `field172` int(11) NOT NULL default '0', + `HateAdded` int(11) NOT NULL default '0', + `EndurUpkeep` int(11) NOT NULL default '0', + `field175` int(11) default NULL, + `numhits` int(11) NOT NULL default '0', + `pvpresistbase` int(11) NOT NULL default '-150', + `pvpresistcalc` int(11) NOT NULL default '100', + `pvpresistcap` int(11) NOT NULL default '-150', + `spell_category` int(11) NOT NULL default '-99', + `field181` int(11) NOT NULL default '7', + `field182` int(11) NOT NULL default '65', + `field183` int(11) NOT NULL default '0', + `field184` int(11) NOT NULL default '0', + `can_mgb` int(11) NOT NULL default '0', + `nodispell` int(11) NOT NULL default '-1', + `npc_category` int(11) NOT NULL default '0', + `npc_usefulness` int(11) NOT NULL default '0', + `field189` int(11) NOT NULL default '0', + `field190` int(11) NOT NULL default '0', + `field191` int(11) NOT NULL default '0', + `field192` int(11) NOT NULL default '0', + `field193` int(11) NOT NULL default '0', + `field194` int(11) NOT NULL default '0', + `field195` int(11) NOT NULL default '0', + `field196` int(11) NOT NULL default '0', + `field197` int(11) NOT NULL default '0', + `field198` int(11) NOT NULL default '0', + `field199` int(11) NOT NULL default '1', + `field200` int(11) NOT NULL default '0', + `field201` int(11) NOT NULL default '0', + `field202` int(11) NOT NULL default '0', + `field203` int(11) default NULL default '0', + `field204` int(11) default NULL default '0', + `field205` int(11) default NULL default '0', + `field206` int(11) default NULL default '-1', + `spellgroup` int(11) default NULL default '0', + `field208` int(11) default NULL default '0', + `field209` int(11) default NULL default '0', + `field210` int(11) default NULL default '1', + `field211` int(11) default NULL default '0', + `field212` int(11) default NULL default '0', + `field213` int(11) default NULL default '1', + `field214` int(11) default NULL default '1', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; diff --git a/utils/sql/svn/2340_required_maxbuffslotspet.sql b/utils/sql/svn/2340_required_maxbuffslotspet.sql new file mode 100644 index 000000000..925bef2e1 --- /dev/null +++ b/utils/sql/svn/2340_required_maxbuffslotspet.sql @@ -0,0 +1,5 @@ +/* Sorvani */ + +-- Max Buff Slots for Pets -- +INSERT INTO `rule_values` (`ruleset_id`,`rule_name`,`rule_value`,`notes`) + VALUES (1,'Spells:MaxTotalSlotsPET','25','do not set this higher than 25 until the player profile is removed from the blob'); \ No newline at end of file diff --git a/utils/sql/svn/235_horses_table.sql b/utils/sql/svn/235_horses_table.sql new file mode 100644 index 000000000..4731c70f4 --- /dev/null +++ b/utils/sql/svn/235_horses_table.sql @@ -0,0 +1,98 @@ +DROP TABLE IF EXISTS `horses`; +CREATE TABLE `horses` ( + `filename` varchar(32) NOT NULL, + `race` smallint(3) NOT NULL default '216', + `gender` tinyint(1) NOT NULL default '0', + `texture` tinyint(2) NOT NULL default '0', + `mountspeed` float(4,2) NOT NULL default '0.75', + `notes` varchar(64) default 'Notes', + PRIMARY KEY (`filename`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + + +INSERT INTO `horses` VALUES ('SumChimeraFast', '216', '0', '0', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumCragslither1Fast', '216', '1', '0', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumCragslither2Fast', '216', '1', '0', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumCragslither3Fast', '216', '1', '1', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseBlFast', '216', '0', '2', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseBlRun1', '216', '0', '2', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseBlRun2', '216', '0', '2', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseBlSlow1', '216', '0', '2', '0.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseBlSlow2', '216', '0', '0', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseBrFast', '216', '0', '0', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseBrRun1', '216', '0', '0', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseBrRun2', '216', '0', '0', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseBrSlow1', '216', '0', '0', '0.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseBrSlow2', '216', '0', '0', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseTaFast', '216', '0', '3', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseTaRun1', '216', '0', '3', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseTaRun2', '216', '0', '3', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseTaSlow1', '216', '0', '3', '0.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseTaSlow2', '216', '0', '3', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseWhFast', '216', '0', '1', '1.75', '2871 - Summon Horse SumHorseWhFast'); +INSERT INTO `horses` VALUES ('SumHorseWhRun1', '216', '0', '1', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseWhRun2', '216', '0', '1', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseWhSlow1', '216', '0', '1', '0.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumHorseWhSlow2', '216', '0', '1', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumKirin0Fast', '216', '1', '0', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumKirin2Fast', '216', '1', '1', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardBlkFast', '216', '1', '1', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardBlkRun1', '216', '1', '1', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardBlkRun2', '216', '1', '1', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardBlkSlow1', '216', '1', '1', '0.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardBlkSlow2', '216', '1', '1', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardGrnFast', '216', '1', '2', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardGrnRun1', '216', '1', '2', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardGrnRun2', '216', '1', '2', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardGrnSlow1', '216', '1', '2', '0.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardGrnSlow2', '216', '1', '2', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardRedFast', '216', '1', '3', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardRedRun1', '216', '1', '3', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardRedRun2', '216', '1', '3', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardRedSlow1', '216', '1', '3', '0.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardRedSlow2', '216', '1', '3', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardWhtFast', '216', '1', '0', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardWhtRun1', '216', '1', '0', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardWhtRun2', '216', '1', '0', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardWhtSlow1', '216', '1', '0', '0.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumLizardWhtSlow2', '216', '1', '0', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumNightmareFast', '216', '1', '0', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumPuma1Fast', '216', '1', '0', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumPuma3Fast', '216', '1', '0', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumRoboboar', '472', '2', '0', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumRoboboarFast', '472', '2', '0', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumRoboboarRun1', '472', '2', '0', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumRoboboarRun2', '472', '2', '0', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumRoboboarSlow2', '472', '2', '0', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumUnicornFast', '216', '0', '1', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseBlFast', '216', '0', '2', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseBlRun1', '216', '0', '2', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseBlRun2', '216', '0', '2', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseBlSlow1', '216', '0', '2', '0.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseBlSlow2', '216', '0', '2', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseBrFast', '216', '0', '0', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseBrRun1', '216', '0', '0', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseBrRun2', '216', '0', '0', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseBrSlow1', '216', '0', '0', '0.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseBrSlow2', '216', '0', '0', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseTaFast', '216', '0', '3', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseTaRun1', '216', '0', '3', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseTaRun2', '216', '0', '3', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseTaSlow1', '216', '0', '3', '0.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseTaSlow2', '216', '0', '3', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseWhFast', '216', '0', '1', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseWhRun1', '216', '0', '1', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseWhRun2', '216', '0', '1', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseWhSlow1', '216', '0', '1', '0.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumWarHorseWhSlow2', '216', '0', '1', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumWorgFastClaimDigital', '216', '1', '0', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumWorgFastClaimRetailBox', '216', '1', '0', '1.75', 'Notes'); +INSERT INTO `horses` VALUES ('SumWorgRun1ClaimDigital', '216', '1', '0', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumWorgRun1ClaimRetailBox', '216', '1', '0', '1.25', 'Notes'); +INSERT INTO `horses` VALUES ('SumWorgRun2ClaimDigital', '216', '1', '0', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumWorgRun2ClaimRetailBox', '216', '1', '0', '1.50', 'Notes'); +INSERT INTO `horses` VALUES ('SumWorgSlow2ClaimDigital', '216', '1', '0', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('SumWorgSlow2ClaimRetailBox', '216', '1', '0', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('TestHorseA', '216', '0', '0', '1.00', 'Notes'); +INSERT INTO `horses` VALUES ('TestWarHorseA', '216', '0', '0', '1.00', 'Notes'); + diff --git a/utils/sql/svn/2361_required_qs_rule_values.sql b/utils/sql/svn/2361_required_qs_rule_values.sql new file mode 100644 index 000000000..0e7a3f9ec --- /dev/null +++ b/utils/sql/svn/2361_required_qs_rule_values.sql @@ -0,0 +1,5 @@ +-- New Rule Values -- +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`) VALUES (1, 'QueryServ:MerchantLogTransactions', 'false'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`) VALUES (1, 'QueryServ:PlayerLogDeletes', 'false'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`) VALUES (1, 'QueryServ:PlayerLogHandins', 'false'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`) VALUES (1, 'QueryServ:PlayerLogMoves', 'false'); diff --git a/utils/sql/svn/2370_required_aa_updates.sql b/utils/sql/svn/2370_required_aa_updates.sql new file mode 100644 index 000000000..9a1a1d94c --- /dev/null +++ b/utils/sql/svn/2370_required_aa_updates.sql @@ -0,0 +1,21 @@ +-- Fix for Healing Gift AA line + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1086', '1', '274', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1087', '1', '274', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1088', '1', '274', '6', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4779', '1', '274', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4780', '1', '274', '4', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('4781', '1', '274', '6', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5592', '1', '274', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5593', '1', '274', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('5594', '1', '274', '3', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7590', '1', '274', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7591', '1', '274', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7592', '1', '274', '3', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12452', '1', '274', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12453', '1', '274', '2', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('12454', '1', '274', '3', '0'); \ No newline at end of file diff --git a/utils/sql/svn/2376_required_aa_updates.sql b/utils/sql/svn/2376_required_aa_updates.sql new file mode 100644 index 000000000..a5dee7c12 --- /dev/null +++ b/utils/sql/svn/2376_required_aa_updates.sql @@ -0,0 +1,53 @@ +-- MISC AA fixes mostly display related + +-- Fix innate cold on SOD +UPDATE altadv_vars SET sof_current_level = 0 WHERE skill_id = 42; + +-- Fix for innate run speed display +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 672; + +-- Fix for istrument master/singing +UPDATE altadv_vars SET sof_next_id = 700 WHERE skill_id = 213; +UPDATE altadv_vars SET sof_next_id = 701 WHERE skill_id = 275; +UPDATE altadv_vars SET sof_type = 3 WHERE skill_id = 700; +UPDATE altadv_vars SET sof_type = 3 WHERE skill_id = 701; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 700; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 701; + +UPDATE altadv_vars SET hotkey_sid = 4294967295 WHERE skill_id = 700; +UPDATE altadv_vars SET hotkey_sid = 4294967295 WHERE skill_id = 701; +UPDATE altadv_vars SET hotkey_sid2 = 4294967295 WHERE skill_id = 700; +UPDATE altadv_vars SET hotkey_sid2 = 4294967295 WHERE skill_id = 701; + +-- Fix for Wizards having beast AA Frenzy Spirit/Warders x +UPDATE altadv_vars SET sof_next_skill = 921 WHERE skill_id = 1340; +UPDATE altadv_vars SET sof_next_skill = 922 WHERE skill_id = 1341; +UPDATE altadv_vars SET sof_next_skill = 923 WHERE skill_id = 1342; +UPDATE altadv_vars SET sof_next_skill = 155 WHERE skill_id = 1344; + +-- Fix for spell casting reinforcement +UPDATE altadv_vars SET sof_max_level = 3 WHERE skill_id = 86; +UPDATE altadv_vars SET sof_max_level = 1 WHERE skill_id = 266; +UPDATE altadv_vars SET sof_next_skill = 266 WHERE skill_id = 266; + +-- General Sturdiness Fix +UPDATE altadv_vars SET sof_next_id = 7527 WHERE skill_id = 6119; +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6119', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6120', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6121', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6122', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('6123', '1', '69', '500', '0'); + +UPDATE altadv_vars SET skill_id = 7527 WHERE skill_id = 7526; +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7527', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7528', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7529', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7530', '1', '69', '500', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('7531', '1', '69', '500', '0'); + +-- Energetic Attument III -- +REPLACE INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '7522', '1', '317', '1', '0'); +REPLACE INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '7523', '1', '317', '1', '0'); +REPLACE INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '7524', '1', '317', '1', '0'); +REPLACE INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '7525', '1', '317', '1', '0'); +REPLACE INTO `aa_effects` (`id` ,`aaid` ,`slot` ,`effectid` ,`base1` ,`base2`) VALUES (NULL , '7526', '1', '317', '1', '0'); \ No newline at end of file diff --git a/utils/sql/svn/2380_optional_merc_data.sql b/utils/sql/svn/2380_optional_merc_data.sql new file mode 100644 index 000000000..907dc543c --- /dev/null +++ b/utils/sql/svn/2380_optional_merc_data.sql @@ -0,0 +1,4958 @@ +-- Drop tables to reload data +drop table IF EXISTS merc_merchant_entries; +drop table IF EXISTS merc_merchant_template_entries; +drop table IF EXISTS merc_merchant_templates; +drop table IF EXISTS merc_stance_entries; +drop table IF EXISTS merc_templates; +drop table IF EXISTS merc_npc_types; +drop table IF EXISTS merc_name_types; +drop table IF EXISTS merc_subtypes; +drop table IF EXISTS merc_types; + +-- What's displayed in merc merchant's dropdown (Apprentice Mercenaries (Dark Elf)) +create table merc_types +( + merc_type_id int UNSIGNED NOT NULL AUTO_INCREMENT, + race_id int UNSIGNED NOT NULL, + proficiency_id tinyint UNSIGNED NOT NULL, + dbstring varchar(12) NOT NULL, + clientversion int UNSIGNED NOT NULL , -- limits mercs by clientversion for available models + PRIMARY KEY (merc_type_id) +); + +-- other data relevant to mercs - could be in merc_templates, but is repeated (class & tier) +create table merc_subtypes +( + merc_subtype_id int UNSIGNED NOT NULL AUTO_INCREMENT, + class_id int UNSIGNED NOT NULL, + tier_id tinyint UNSIGNED NOT NULL, + confidence_id tinyint UNSIGNED NOT NULL, + PRIMARY KEY (merc_subtype_id) +); + +create table merc_name_types +( + name_type_id int UNSIGNED NOT NULL, + class_id int UNSIGNED NOT NULL, + prefix varchar(25) NOT NULL, + suffix varchar(25) NOT NULL, + PRIMARY KEY (name_type_id, class_id) +); + +-- mostly for reference so there's just not a random merc_npc_type_id thrown in +create table merc_npc_types +( + merc_npc_type_id int UNSIGNED NOT NULL AUTO_INCREMENT, + proficiency_id tinyint UNSIGNED NOT NULL, + tier_id tinyint UNSIGNED NOT NULL, + class_id int UNSIGNED NOT NULL, + name varchar(255) NULL, + PRIMARY KEY (merc_npc_type_id) +); + +-- ties together basic merc info to be displayed +create table merc_templates +( + merc_template_id int UNSIGNED NOT NULL AUTO_INCREMENT, + merc_type_id int UNSIGNED NOT NULL, + merc_subtype_id int UNSIGNED NOT NULL, + merc_npc_type_id int(11) UNSIGNED NOT NULL, + dbstring varchar(12) NOT NULL, -- could be determined on the fly, but would require a lot of string manipulation + name_type_id tinyint DEFAULT 0 NOT NULL, -- determines whether or not merc gets a name or 'a goblin mercenary' + clientversion int UNSIGNED NOT NULL , + PRIMARY KEY (merc_template_id), + KEY FK_merc_templates_1 (merc_type_id), + CONSTRAINT FK_merc_templates_1 FOREIGN KEY (merc_type_id) REFERENCES merc_types (merc_type_id), + KEY FK_merc_templates_2 (merc_subtype_id), + CONSTRAINT FK_merc_templates_2 FOREIGN KEY (merc_subtype_id) REFERENCES merc_subtypes (merc_subtype_id) +); + +-- lists stances available per class & proficiency (apprentice has passive and balanced, journeyman has more) +create table merc_stance_entries +( + merc_stance_entry_id int UNSIGNED NOT NULL AUTO_INCREMENT, + class_id int UNSIGNED NOT NULL, + proficiency_id tinyint UNSIGNED NOT NULL, + stance_id tinyint UNSIGNED NOT NULL, + isdefault bool NOT NULL, + PRIMARY KEY (merc_stance_entry_id) +); + +-- named record to link templates to, and assign to a merchant (a merchant may have more than one) +create table merc_merchant_templates +( + merc_merchant_template_id int UNSIGNED NOT NULL AUTO_INCREMENT, + name varchar(50) NOT NULL, + qglobal varchar(255), -- quest global - could be on merc_templates + PRIMARY KEY (merc_merchant_template_id) +); + +-- assigns templates to merchant named template list (similar to loottable & lootdrops) +create table merc_merchant_template_entries +( + merc_merchant_template_entry_id int UNSIGNED NOT NULL AUTO_INCREMENT, + merc_merchant_template_id int UNSIGNED NOT NULL, + merc_template_id int UNSIGNED NOT NULL, + PRIMARY KEY (merc_merchant_template_entry_id), + KEY FK_merc_merchant_template_entries_1 (merc_merchant_template_id), + CONSTRAINT FK_merc_merchant_template_entries_1 FOREIGN KEY (merc_merchant_template_id) REFERENCES merc_merchant_templates (merc_merchant_template_id), + KEY FK_merc_merchant_template_entries_2 (merc_template_id), + CONSTRAINT FK_merc_merchant_template_entries_2 FOREIGN KEY (merc_template_id) REFERENCES merc_templates (merc_template_id) +); + +-- links merc template lists to merchants (merchant_id references npc_types.id) +create table merc_merchant_entries +( + merc_merchant_entry_id int UNSIGNED NOT NULL AUTO_INCREMENT, + merc_merchant_template_id int UNSIGNED NOT NULL, + merchant_id int(11) UNSIGNED NOT NULL, + PRIMARY KEY (merc_merchant_entry_id), + KEY FK_merc_merchant_entries_1 (merc_merchant_template_id), + CONSTRAINT FK_merc_merchant_entries_1 FOREIGN KEY (merc_merchant_template_id) REFERENCES merc_merchant_templates (merc_merchant_template_id) +); + +-- PC Models +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 1, 1, '1000100', 4); -- Human Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 1, 2, '1000200', 4); -- Human Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 2, 1, '2000100', 4); -- Barbarian Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 2, 2, '2000200', 4); -- Barbarian Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 3, 1, '3000100', 4); -- Erudite Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 3, 2, '3000200', 4); -- Erudite Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 4, 1, '4000100', 4); -- Wood Elf Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 4, 2, '4000200', 4); -- Wood Elf Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 5, 1, '5000100', 4); -- High Elf Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 5, 2, '5000200', 4); -- High Elf Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 6, 1, '6000100', 4); -- Dark Elf Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 6, 2, '6000200', 4); -- Dark Elf Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 7, 1, '7000100', 4); -- Half Elf Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 7, 2, '7000200', 4); -- Half Elf Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 8, 1, '8000100', 4); -- Dwarf Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 8, 2, '8000200', 4); -- Dwarf Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 9, 1, '9000100', 4); -- Troll Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 9, 2, '9000200', 4); -- Troll Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 10, 1, '10000100', 4); -- Ogre Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 10, 2, '10000200', 4); -- Ogre Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 11, 1, '11000100', 4); -- Halfling Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 11, 2, '11000200', 4); -- Halfling Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 12, 1, '12000100', 4); -- Gnome Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 12, 2, '12000200', 4); -- Gnome Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 128, 1, '128000100', 4); -- Iksar Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 128, 2, '128000200', 4); -- Iksar Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 130, 1, '130000100', 4); -- Vah Shir Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 130, 2, '130000200', 4); -- Vah Shir Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 330, 1, '330000100', 4); -- Froglok Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 330, 2, '330000200', 4); -- Froglok Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 522, 1, '522000100', 4); -- Drakkin Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 522, 2, '522000200', 4); -- Drakkin Journeyman + +-- NPC Models +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 26, 1, '26000100', 4); -- Froglok Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 26, 2, '26000200', 4); -- Froglok Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 48, 1, '48000100', 4); -- Kobold Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 48, 2, '48000200', 4); -- Kobold Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 51, 1, '51000100', 4); -- Lizard Man Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 51, 2, '51000200', 4); -- Lizard Man Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 82, 1, '82000100', 4); -- Scarecrow Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 82, 2, '82000200', 4); -- Scarecrow Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 134, 1, '134000100', 4); -- Mosquito Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 134, 2, '134000200', 4); -- Mosquito Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 137, 1, '137000100', 4); -- Kunark Goblin Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 137, 2, '137000200', 4); -- Kunark Goblin Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 199, 1, '199000100', 4); -- ShikNar Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 199, 2, '199000200', 4); -- ShikNar Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 338, 1, '338000100', 4); -- Gnome Pirate Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 338, 2, '338000200', 4); -- Gnome Pirate Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 339, 1, '339000100', 4); -- Dark Elf Pirate Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 339, 2, '339000200', 4); -- Dark Elf Pirate Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 367, 1, '367000100', 4); -- Frozen Skeleton Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 367, 2, '367000200', 4); -- Frozen Skeleton Journeyman + +-- GoD Models +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 394, 1, '394000100', 4); -- Ikaav Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 394, 2, '394000200', 4); -- Ikaav Journeyman + +-- OoW Models +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 409, 1, '409000100', 4); -- Bazu Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 409, 2, '409000200', 4); -- Bazu Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 411, 1, '411000100', 4); -- Pyrilen Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 411, 2, '411000200', 4); -- Pyrilen Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 413, 1, '413000100', 4); -- Dragorn Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 413, 2, '413000200', 4); -- Dragorn Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 417, 1, '417000100', 4); -- Gelidran Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 417, 2, '417000200', 4); -- Gelidran Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 419, 1, '419000100', 4); -- Hadal Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 419, 2, '419000200', 4); -- Hadal Journeyman + +-- DoN/DoD Models +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 433, 1, '433000100', 4); -- Goblin Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 433, 2, '433000200', 4); -- Goblin Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 456, 1, '456000100', 4); -- Sporali Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 456, 2, '456000200', 4); -- Sporali Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 457, 1, '457000100', 4); -- Clockwork Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 457, 2, '457000200', 4); -- Clockwork Journeyman +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 458, 1, '458000100', 4); -- Orc Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 458, 2, '458000200', 4); -- Orc Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 461, 1, '461000100', 4); -- Drachnid Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 461, 2, '461000200', 4); -- Drachnid Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 471, 1, '471000100', 4); -- Zombie Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 471, 2, '471000200', 4); -- Zombie Journeyman + +-- PoR Models +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 487, 1, '487000100', 4); -- Banshee Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 487, 2, '487000200', 4); -- Banshee Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 488, 1, '488000100', 4); -- Banshee Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 488, 2, '488000200', 4); -- Banshee Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 495, 1, '495000100', 4); -- Skrykin Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 495, 2, '495000200', 4); -- Skrykin Journeyman + +-- TSS Models +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 520, 1, '520000100', 4); -- Bixie Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 520, 2, '520000200', 4); -- Bixie Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 524, 1, '524000100', 4); -- Gnoll Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 524, 2, '524000200', 4); -- Gnoll Journeyman + +-- TBS Models +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 562, 1, '562000100', 4); -- Kerran Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 562, 2, '562000200', 4); -- Kerran Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 564, 1, '564000100', 4); -- Siren Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 564, 2, '564000200', 4); -- Siren Journeyman + +-- SoF Models +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 568, 1, '568000100', 4); -- Brownie Apprentice +REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 568, 2, '568000200', 4); -- Brownie Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 574, 1, '574000100', 4); -- Minotaur Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 574, 2, '574000200', 4); -- Minotaur Journeyman + +-- SoD Models +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 610, 1, '610000100', 4); -- Sarnak Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 610, 2, '610000200', 4); -- Sarnak Journeyman + +-- UF Models +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 638, 1, '638000100', 5); -- Bellikos Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 638, 2, '638000200', 5); -- Bellikos Journeyman + +-- HoT Models +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 659, 1, '659000100', 6); -- Marionette Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 659, 2, '659000200', 6); -- Marionette Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 663, 1, '663000100', 6); -- Amygdalan Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 663, 2, '663000200', 6); -- Amygdalan Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 666, 1, '666000100', 6); -- Gingerbread Man Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 666, 2, '666000200', 6); -- Gingerbread Man Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 696, 1, '696000100', 6); -- Swinetor Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 696, 2, '696000200', 6); -- Swinetor Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 698, 1, '698000100', 6); -- Hadal Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 698, 2, '698000200', 6); -- Hadal Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 1458, 1, '1458000100', 6); -- Rallosian Orc Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 1458, 2, '1458000200', 6); -- Rallosian Orc Journeyman +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 36702, 1, '36702000100', 6); -- Frozen Skeleton Apprentice +-- REPLACE INTO merc_types ( race_id, proficiency_id, dbstring, clientversion ) VALUES ( 36702, 2, '36702000200', 6); -- Frozen Skeleton Journeyman + + + +-- merc SubTypes + +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 1, 1, 1); -- tank t1 low +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 1, 2, 2); -- tank t2 medium +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 1, 3, 2); -- tank t3 medium +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 1, 4, 2); -- tank t4 medium +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 1, 5, 3); -- tank t5 high + +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 2, 1, 1); -- healer t1 low +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 2, 2, 2); -- healer t2 medium +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 2, 3, 2); -- healer t3 medium +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 2, 4, 2); -- healer t4 medium +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 2, 5, 3); -- healer t5 high + +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 9, 1, 1); -- melee damage t1 low +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 9, 2, 2); -- melee damage t2 medium +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 9, 3, 2); -- melee damage t3 medium +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 9, 4, 2); -- melee damage t4 medium +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 9, 5, 3); -- melee damage t5 high + +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 12, 1, 1); -- caster damage t1 low +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 12, 2, 2); -- caster damage t2 medium +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 12, 3, 2); -- caster damage t3 medium +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 12, 4, 2); -- caster damage t4 medium +REPLACE INTO merc_subtypes ( class_id, tier_id, confidence_id ) VALUES ( 12, 5, 3); -- caster damage t5 high + + +REPLACE INTO merc_name_types ( name_type_id, class_id, prefix, suffix ) VALUES ( 1, 0, '', 'mercenary'); +REPLACE INTO merc_name_types ( name_type_id, class_id, prefix, suffix ) VALUES ( 2, 0, '', 'hireling'); +REPLACE INTO merc_name_types ( name_type_id, class_id, prefix, suffix ) VALUES ( 3, 0, 'hired', ''); +REPLACE INTO merc_name_types ( name_type_id, class_id, prefix, suffix ) VALUES ( 4, 1, '', 'defender'); +REPLACE INTO merc_name_types ( name_type_id, class_id, prefix, suffix ) VALUES ( 4, 2, '', 'soother'); +REPLACE INTO merc_name_types ( name_type_id, class_id, prefix, suffix ) VALUES ( 4, 4, '', 'fighter'); +REPLACE INTO merc_name_types ( name_type_id, class_id, prefix, suffix ) VALUES ( 4, 12, '', 'caster'); +REPLACE INTO merc_name_types ( name_type_id, class_id, prefix, suffix ) VALUES ( 5, 1, '', 'brawler'); +REPLACE INTO merc_name_types ( name_type_id, class_id, prefix, suffix ) VALUES ( 5, 2, '', 'mender'); +REPLACE INTO merc_name_types ( name_type_id, class_id, prefix, suffix ) VALUES ( 5, 4, '', ''); +REPLACE INTO merc_name_types ( name_type_id, class_id, prefix, suffix ) VALUES ( 5, 12, '', ''); +REPLACE INTO merc_name_types ( name_type_id, class_id, prefix, suffix ) VALUES ( 6, 2, '', 'acolyte'); +REPLACE INTO merc_name_types ( name_type_id, class_id, prefix, suffix ) VALUES ( 7, 1, '', 'nightlord'); +REPLACE INTO merc_name_types ( name_type_id, class_id, prefix, suffix ) VALUES ( 8, 2, '', 'mendicant'); + + +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 1, 1, 'Apprentice Tank - Tier I'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 2, 1, 'Apprentice Tank - Tier II'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 3, 1, 'Apprentice Tank - Tier III'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 4, 1, 'Apprentice Tank - Tier IV'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 5, 1, 'Apprentice Tank - Tier V'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 1, 1, 'Journeyman Tank - Tier I'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 2, 1, 'Journeyman Tank - Tier II'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 3, 1, 'Journeyman Tank - Tier III'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 4, 1, 'Journeyman Tank - Tier IV'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 5, 1, 'Journeyman Tank - Tier V'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 1, 2, 'Apprentice Healer - Tier I'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 2, 2, 'Apprentice Healer - Tier II'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 3, 2, 'Apprentice Healer - Tier III'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 4, 2, 'Apprentice Healer - Tier IV'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 5, 2, 'Apprentice Healer - Tier V'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 1, 2, 'Journeyman Healer - Tier I'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 2, 2, 'Journeyman Healer - Tier II'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 3, 2, 'Journeyman Healer - Tier III'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 4, 2, 'Journeyman Healer - Tier IV'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 5, 2, 'Journeyman Healer - Tier V'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 1, 9, 'Apprentice Melee DPS - Tier I'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 2, 9, 'Apprentice Melee DPS - Tier II'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 3, 9, 'Apprentice Melee DPS - Tier III'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 4, 9, 'Apprentice Melee DPS - Tier IV'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 5, 9, 'Apprentice Melee DPS - Tier V'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 1, 9, 'Journeyman Melee DPS - Tier I'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 2, 9, 'Journeyman Melee DPS - Tier II'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 3, 9, 'Journeyman Melee DPS - Tier III'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 4, 9, 'Journeyman Melee DPS - Tier IV'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 5, 9, 'Journeyman Melee DPS - Tier V'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 1, 12, 'Apprentice Caster DPS - Tier I'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 2, 12, 'Apprentice Caster DPS - Tier II'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 3, 12, 'Apprentice Caster DPS - Tier III'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 4, 12, 'Apprentice Caster DPS - Tier IV'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 1, 5, 12, 'Apprentice Caster DPS - Tier V'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 1, 12, 'Journeyman Caster DPS - Tier I'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 2, 12, 'Journeyman Caster DPS - Tier II'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 3, 12, 'Journeyman Caster DPS - Tier III'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 4, 12, 'Journeyman Caster DPS - Tier IV'); +REPLACE INTO merc_npc_types (proficiency_id, tier_id, class_id, name) VALUES ( 2, 5, 12, 'Journeyman Caster DPS - Tier V'); + + + +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '1010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '1010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '1010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '1010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '1010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '1020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '1020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '1020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '1020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '1020105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '1040101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '1040102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '1040103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '1040104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '1040105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '1120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '1120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '1120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '1120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '1120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '1010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '1010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '1010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '1010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '1010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '1020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '1020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '1020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '1020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '1020205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '1040201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '1040202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '1040203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '1040204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '1040205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '1120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '1120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '1120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '1120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '1120205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '2010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '2010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '2010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '2010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '2010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '2020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '2020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '2020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '2020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '2020105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '2040101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '2040102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '2040103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '2040104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '2040105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '2120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '2120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '2120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '2120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '2120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '2010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '2010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '2010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '2010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '2010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '2020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '2020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '2020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '2020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '2020205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '2040201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '2040202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '2040203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '2040204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '2040205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '2120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '2120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '2120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '2120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '2120205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '3010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '3010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '3010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '3010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '3010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '3020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '3020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '3020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '3020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '3020105', 0, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '3090101', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '3090102', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '3090103', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '3090104', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '3090105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '3120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '3120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '3120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '3120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '3120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '3010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '3010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '3010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '3010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '3010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '3020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '3020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '3020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '3020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '3020205', 0, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '3090201', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '3090202', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '3090203', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '3090204', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '3090205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '3120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '3120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '3120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '3120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '3120205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '4010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '4010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '4010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '4010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '4010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '4020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '4020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '4020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '4020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '4020105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '4040101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '4040102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '4040103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '4040104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '4040105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '4120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '4120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '4120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '4120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '4120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '4010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '4010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '4010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '4010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '4010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '4020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '4020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '4020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '4020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '4020205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '4040201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '4040202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '4040203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '4040204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '4040205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '4120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '4120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '4120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '4120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '4120205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '5010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '5010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '5010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '5010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '5010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '5020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '5020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '5020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '5020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '5020105', 0, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '5090101', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '5090102', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '5090103', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '5090104', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '5090105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '5120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '5120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '5120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '5120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '5120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '5010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '5010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '5010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '5010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '5010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '5020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '5020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '5020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '5020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '5020205', 0, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '5090201', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '5090202', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '5090203', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '5090204', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '5090205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '5120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '5120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '5120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '5120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '5120205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '6010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '6010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '6010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '6010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '6010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '6020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '6020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '6020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '6020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '6020105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '6040101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '6040102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '6040103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '6040104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '6040105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '6120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '6120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '6120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '6120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '6120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '6010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '6010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '6010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '6010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '6010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '6020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '6020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '6020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '6020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '6020205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '6040201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '6040202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '6040203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '6040204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '6040205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '6120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '6120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '6120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '6120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '6120205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '7010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '7010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '7010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '7010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '7010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '7020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '7020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '7020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '7020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '7020105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '7040101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '7040102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '7040103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '7040104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '7040105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '7120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '7120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '7120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '7120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '7120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '7010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '7010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '7010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '7010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '7010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '7020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '7020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '7020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '7020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '7020205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '7040201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '7040202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '7040203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '7040204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '7040205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '7120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '7120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '7120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '7120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '7120205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '8010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '8010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '8010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '8010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '8010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '8020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '8020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '8020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '8020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '8020105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '8040101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '8040102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '8040103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '8040104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '8040105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '8120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '8120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '8120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '8120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '8120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '8010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '8010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '8010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '8010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '8010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '8020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '8020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '8020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '8020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '8020205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '8040201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '8040202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '8040203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '8040204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '8040205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '8120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '8120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '8120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '8120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '8120205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '9010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '9010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '9010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '9010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '9010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '9020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '9020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '9020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '9020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '9020105', 0, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '9090101', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '9090102', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '9090103', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '9090104', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '9090105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '9120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '9120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '9120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '9120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '9120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '9010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '9010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '9010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '9010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '9010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '9020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '9020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '9020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '9020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '9020205', 0, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '9090201', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '9090202', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '9090203', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '9090204', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '9090205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '9120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '9120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '9120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '9120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '9120205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '10010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '10010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '10010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '10010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '10010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '10020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '10020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '10020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '10020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '10020105', 0, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '10090101', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '10090102', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '10090103', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '10090104', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '10090105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '10120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '10120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '10120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '10120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '10120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '10010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '10010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '10010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '10010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '10010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '10020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '10020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '10020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '10020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '10020205', 0, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '10090201', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '10090202', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '10090203', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '10090204', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '10090205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '10120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '10120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '10120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '10120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '10120205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '11010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '11010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '11010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '11010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '11010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '11020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '11020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '11020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '11020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '11020105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '11040101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '11040102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '11040103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '11040104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '11040105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '11120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '11120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '11120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '11120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '11120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '11010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '11010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '11010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '11010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '11010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '11020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '11020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '11020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '11020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '11020205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '11040201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '11040202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '11040203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '11040204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '11040205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '11120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '11120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '11120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '11120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '11120205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '12010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '12010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '12010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '12010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '12010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '12020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '12020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '12020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '12020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '12020105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '12040101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '12040102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '12040103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '12040104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '12040105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '12120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '12120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '12120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '12120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '12120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '12010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '12010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '12010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '12010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '12010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '12020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '12020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '12020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '12020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '12020205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '12040201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '12040202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '12040203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '12040204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '12040205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '12120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '12120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '12120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '12120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '12120205', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '26010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '26010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '26010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '26010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '26010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '26020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '26020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '26020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '26020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '26020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '26040101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '26040102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '26040103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '26040104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '26040105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '26120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '26120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '26120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '26120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '26120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '26010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '26010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '26010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '26010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '26010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '26020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '26020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '26020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '26020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '26020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '26040201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '26040202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '26040203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '26040204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '26040205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '26120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '26120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '26120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '26120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '26120205', 1, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '48010101', 1, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '48010102', 1, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '48010103', 1, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '48010104', 1, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '48010105', 1, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '48020101', 1, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '48020102', 1, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '48020103', 1, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '48020104', 1, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '48020105', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '48040101', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '48040102', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '48040103', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '48040104', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '48040105', 1, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '48120101', 1, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '48120102', 1, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '48120103', 1, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '48120104', 1, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '48120105', 1, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '48010201', 1, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '48010202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '48010203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '48010204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '48010205', 1, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '48020201', 1, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '48020202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '48020203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '48020204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '48020205', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '48040201', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '48040202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '48040203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '48040204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '48040205', 1, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '48120201', 1, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '48120202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '48120203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '48120204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '48120205', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '51010101', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '51010102', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '51010103', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '51010104', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '51010105', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '51020101', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '51020102', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '51020103', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '51020104', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '51020105', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '51040101', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '51040102', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '51040103', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '51040104', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '51040105', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '51120101', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '51120102', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '51120103', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '51120104', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '51120105', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '51010201', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '51010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '51010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '51010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '51010205', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '51020201', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '51020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '51020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '51020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '51020205', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '51040201', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '51040202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '51040203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '51040204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '51040205', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '51120201', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '51120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '51120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '51120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '51120205', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '82010101', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '82010102', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '82010103', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '82010104', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '82010105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '82020101', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '82020102', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '82020103', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '82020104', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '82020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '82040101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '82040102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '82040103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '82040104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '82040105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '82030101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '82030102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '82030103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '82030104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '82030105', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '82010201', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '82010202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '82010203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '82010204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '82010205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '82020201', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '82020202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '82020203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '82020204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '82020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '82040201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '82040202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '82040203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '82040204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '82040205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '82030201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '82030202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '82030203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '82030204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '82030205', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '128010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '128010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '128010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '128010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '128010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '128020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '128020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '128020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '128020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '128020105', 0, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '128090101', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '128090102', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '128090103', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '128090104', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '128090105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '128120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '128120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '128120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '128120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '128120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '128010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '128010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '128010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '128010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '128010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '128020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '128020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '128020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '128020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '128020205', 0, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '128090201', 0, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '128090202', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '128090203', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '128090204', 0, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '128090205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '128120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '128120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '128120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '128120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '128120205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '130010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '130010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '130010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '130010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '130010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '130020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '130020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '130020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '130020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '130020105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '130040101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '130040102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '130040103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '130040104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '130040105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '130120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '130120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '130120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '130120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '130120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '130010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '130010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '130010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '130010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '130010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '130020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '130020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '130020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '130020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '130020205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '130040201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '130040202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '130040203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '130040204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '130040205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '130120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '130120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '130120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '130120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '130120205', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '134010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '134010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '134010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '134010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '134010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '134020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '134020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '134020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '134020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '134020105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '134090101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '134090102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '134090103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '134090104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '134090105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '134120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '134120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '134120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '134120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '134120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '134010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '134010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '134010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '134010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '134010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '134020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '134020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '134020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '134020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '134020205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '134090201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '134090202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '134090203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '134090204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '134090205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '134120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '134120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '134120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '134120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '134120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '137010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '137010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '137010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '137010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '137010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '137020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '137020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '137020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '137020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '137020105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '137090101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '137090102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '137090103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '137090104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '137090105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '137120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '137120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '137120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '137120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '137120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '137010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '137010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '137010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '137010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '137010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '137020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '137020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '137020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '137020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '137020205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '137090201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '137090202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '137090203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '137090204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '137090205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '137120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '137120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '137120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '137120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '137120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '199010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '199010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '199010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '199010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '199010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '199020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '199020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '199020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '199020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '199020105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '199090101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '199090102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '199090103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '199090104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '199090105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '199120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '199120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '199120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '199120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '199120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '199010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '199010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '199010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '199010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '199010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '199020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '199020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '199020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '199020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '199020205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '199090201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '199090202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '199090203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '199090204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '199090205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '199120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '199120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '199120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '199120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '199120205', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '330010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '330010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '330010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '330010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '330010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '330020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '330020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '330020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '330020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '330020105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '330040101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '330040102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '330040103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '330040104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '330040105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '330120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '330120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '330120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '330120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '330120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '330010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '330010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '330010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '330010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '330010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '330020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '330020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '330020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '330020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '330020205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '330040201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '330040202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '330040203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '330040204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '330040205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '330120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '330120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '330120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '330120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '330120205', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '338010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '338010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '338010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '338010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '338010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '338020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '338020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '338020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '338020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '338020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '338090101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '338090102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '338090103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '338090104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '338090105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '338120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '338120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '338120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '338120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '338120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '338010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '338010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '338010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '338010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '338010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '338020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '338020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '338020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '338020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '338020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '338090201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '338090202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '338090203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '338090204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '338090205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '338120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '338120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '338120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '338120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '338120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '339010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '339010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '339010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '339010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '339010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '339020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '339020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '339020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '339020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '339020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '339040101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '339040102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '339040103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '339040104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '339040105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '339030101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '339030102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '339030103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '339030104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '339030105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '339010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '339010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '339010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '339010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '339010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '339020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '339020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '339020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '339020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '339020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '339040201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '339040202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '339040203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '339040204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '339040205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '339030201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '339030202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '339030203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '339030204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '339030205', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '367010101', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '367010102', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '367010103', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '367010104', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '367010105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '367020101', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '367020102', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '367020103', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '367020104', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '367020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '367040101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '367040102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '367040103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '367040104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '367040105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '367030101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '367030102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '367030103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '367030104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '367030105', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '367010201', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '367010202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '367010203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '367010204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '367010205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '367020201', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '367020202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '367020203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '367020204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '367020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '367040201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '367040202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '367040203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '367040204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '367040205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '367030201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '367030202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '367030203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '367030204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '367030205', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '394010101', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '394010102', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '394010103', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '394010104', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '394010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '394020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '394020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '394020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '394020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '394020105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '394090101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '394090102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '394090103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '394090104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '394090105', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '394120101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '394120102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '394120103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '394120104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '394120105', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '394010201', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '394010202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '394010203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '394010204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '394010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '394020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '394020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '394020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '394020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '394020205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '394090201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '394090202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '394090203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '394090204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '394090205', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '394120201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '394120202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '394120203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '394120204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '394120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '409010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '409010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '409010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '409010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '409010105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '409020101', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '409020102', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '409020103', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '409020104', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '409020105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '409090101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '409090102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '409090103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '409090104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '409090105', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '409120101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '409120102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '409120103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '409120104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '409120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '409010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '409010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '409010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '409010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '409010205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '409020201', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '409020202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '409020203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '409020204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '409020205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '409090201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '409090202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '409090203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '409090204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '409090205', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '409120201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '409120202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '409120203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '409120204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '409120205', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '411010101', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '411010102', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '411010103', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '411010104', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '411010105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '411020101', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '411020102', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '411020103', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '411020104', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '411020105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '411090101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '411090102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '411090103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '411090104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '411090105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '411120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '411120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '411120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '411120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '411120105', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '411010201', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '411010202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '411010203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '411010204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '411010205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '411020201', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '411020202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '411020203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '411020204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '411020205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '411090201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '411090202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '411090203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '411090204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '411090205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '411120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '411120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '411120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '411120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '411120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '413010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '413010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '413010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '413010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '413010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '413020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '413020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '413020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '413020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '413020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '413090101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '413090102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '413090103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '413090104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '413090105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '413120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '413120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '413120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '413120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '413120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '413010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '413010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '413010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '413010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '413010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '413020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '413020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '413020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '413020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '413020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '413090201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '413090202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '413090203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '413090204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '413090205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '413120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '413120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '413120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '413120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '413120205', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '417010101', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '417010102', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '417010103', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '417010104', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '417010105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '417020101', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '417020102', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '417020103', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '417020104', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '417020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '417090101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '417090102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '417090103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '417090104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '417090105', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '417120101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '417120102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '417120103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '417120104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '417120105', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '417010201', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '417010202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '417010203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '417010204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '417010205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '417020201', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '417020202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '417020203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '417020204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '417020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '417090201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '417090202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '417090203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '417090204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '417090205', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '417120201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '417120202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '417120203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '417120204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '417120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '419010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '419010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '419010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '419010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '419010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '419020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '419020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '419020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '419020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '419020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '419090101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '419090102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '419090103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '419090104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '419090105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '419120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '419120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '419120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '419120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '419120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '419010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '419010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '419010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '419010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '419010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '419020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '419020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '419020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '419020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '419020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '419090201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '419090202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '419090203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '419090204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '419090205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '419120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '419120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '419120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '419120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '419120205', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '433010101', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '433010102', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '433010103', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '433010104', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '433010105', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '433020101', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '433020102', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '433020103', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '433020104', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '433020105', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '433040101', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '433040102', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '433040103', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '433040104', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '433040105', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '433120101', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '433120102', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '433120103', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '433120104', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '433120105', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '433010201', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '433010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '433010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '433010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '433010205', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '433020201', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '433020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '433020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '433020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '433020205', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '433040201', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '433040202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '433040203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '433040204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '433040205', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '433120201', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '433120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '433120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '433120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '433120205', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '456010101', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '456010102', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '456010103', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '456010104', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '456010105', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '456020101', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '456020102', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '456020103', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '456020104', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '456020105', 1, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '456090101', 1, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '456090102', 1, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '456090103', 1, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '456090104', 1, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '456090105', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '456120101', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '456120102', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '456120103', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '456120104', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '456120105', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '456010201', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '456010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '456010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '456010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '456010205', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '456020201', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '456020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '456020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '456020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '456020205', 1, 4); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '456090201', 1, 5); +-- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '456090202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '456090203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '456090204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '456090205', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '456120201', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '456120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '456120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '456120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '456120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '457010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '457010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '457010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '457010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '457010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '457020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '457020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '457020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '457020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '457020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '457040101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '457040102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '457040103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '457040104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '457040105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '457120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '457120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '457120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '457120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '457120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '457010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '457010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '457010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '457010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '457010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '457020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '457020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '457020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '457020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '457020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '457040201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '457040202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '457040203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '457040204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '457040205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '457120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '457120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '457120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '457120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '457120205', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '458010101', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '458010102', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '458010103', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '458010104', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '458010105', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '458020101', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '458020102', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '458020103', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '458020104', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '458020105', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '458040101', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '458040102', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '458040103', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '458040104', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '458040105', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '458120101', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '458120102', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '458120103', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '458120104', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '458120105', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '458010201', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '458010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '458010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '458010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '458010205', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '458020201', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '458020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '458020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '458020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '458020205', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '458040201', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '458040202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '458040203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '458040204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '458040205', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '458120201', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '458120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '458120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '458120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '458120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '461010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '461010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '461010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '461010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '461010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '461020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '461020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '461020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '461020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '461020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '461040101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '461040102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '461040103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '461040104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '461040105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '461120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '461120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '461120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '461120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '461120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '461010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '461010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '461010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '461010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '461010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '461020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '461020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '461020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '461020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '461020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '461040201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '461040202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '461040203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '461040204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '461040205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '461120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '461120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '461120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '461120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '461120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '471010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '471010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '471010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '471010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '471010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '471020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '471020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '471020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '471020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '471020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '471090101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '471090102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '471090103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '471090104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '471090105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '471120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '471120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '471120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '471120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '471120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '471010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '471010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '471010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '471010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '471010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '471020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '471020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '471020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '471020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '471020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '471090201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '471090202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '471090203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '471090204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '471090205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '471120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '471120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '471120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '471120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '471120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '487010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '487010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '487010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '487010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '487010105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '487020101', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '487020102', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '487020103', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '487020104', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '487020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '487090101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '487090102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '487090103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '487090104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '487090105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '487120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '487120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '487120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '487120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '487120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '487010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '487010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '487010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '487010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '487010205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '487020201', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '487020202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '487020203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '487020204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '487020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '487090201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '487090202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '487090203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '487090204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '487090205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '487120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '487120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '487120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '487120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '487120205', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '488010101', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '488010102', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '488010103', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '488010104', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '488010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '488020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '488020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '488020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '488020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '488020105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '488090101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '488090102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '488090103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '488090104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '488090105', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '488120101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '488120102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '488120103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '488120104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '488120105', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '488010201', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '488010202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '488010203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '488010204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '488010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '488020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '488020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '488020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '488020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '488020205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '488090201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '488090202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '488090203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '488090204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '488090205', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '488120201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '488120202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '488120203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '488120204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '488120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '495010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '495010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '495010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '495010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '495010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '495020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '495020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '495020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '495020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '495020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '495040101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '495040102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '495040103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '495040104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '495040105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '495030101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '495030102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '495030103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '495030104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '495030105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '495010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '495010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '495010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '495010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '495010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '495020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '495020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '495020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '495020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '495020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '495040201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '495040202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '495040203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '495040204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '495040205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '495030201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '495030202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '495030203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '495030204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '495030205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '520010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '520010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '520010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '520010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '520010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '520020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '520020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '520020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '520020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '520020105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '520090101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '520090102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '520090103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '520090104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '520090105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '520120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '520120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '520120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '520120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '520120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '520010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '520010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '520010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '520010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '520010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '520020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '520020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '520020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '520020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '520020205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '520090201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '520090202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '520090203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '520090204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '520090205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '520120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '520120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '520120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '520120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '520120205', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '522010101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '522010102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '522010103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '522010104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '522010105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '522020101', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '522020102', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '522020103', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '522020104', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '522020105', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '522040101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '522040102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '522040103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '522040104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '522040105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '522120101', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '522120102', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '522120103', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '522120104', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '522120105', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '522010201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '522010202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '522010203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '522010204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '522010205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '522020201', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '522020202', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '522020203', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '522020204', 0, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '522020205', 0, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '522040201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '522040202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '522040203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '522040204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '522040205', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '522120201', 0, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '522120202', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '522120203', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '522120204', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '522120205', 0, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '524010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '524010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '524010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '524010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '524010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '524020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '524020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '524020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '524020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '524020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '524040101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '524040102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '524040103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '524040104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '524040105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '524120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '524120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '524120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '524120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '524120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '524010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '524010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '524010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '524010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '524010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '524020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '524020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '524020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '524020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '524020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '524040201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '524040202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '524040203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '524040204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '524040205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '524120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '524120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '524120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '524120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '524120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '562010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '562010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '562010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '562010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '562010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '562020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '562020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '562020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '562020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '562020105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '562090101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '562090102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '562090103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '562090104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '562090105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '562120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '562120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '562120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '562120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '562120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '562010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '562010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '562010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '562010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '562010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '562020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '562020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '562020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '562020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '562020205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '562090201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '562090202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '562090203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '562090204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '562090205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '562120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '562120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '562120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '562120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '562120205', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '564010101', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '564010102', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '564010103', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '564010104', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '564010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '564020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '564020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '564020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '564020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '564020105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '564090101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '564090102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '564090103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '564090104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '564090105', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '564120101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '564120102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '564120103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '564120104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '564120105', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '564010201', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '564010202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '564010203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '564010204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '564010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '564020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '564020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '564020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '564020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '564020205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '564090201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '564090202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '564090203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '564090204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '564090205', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '564120201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '564120202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '564120203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '564120204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '564120205', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '568010101', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '568010102', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '568010103', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '568010104', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '568010105', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '568020101', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '568020102', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '568020103', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '568020104', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '568020105', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '568040101', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '568040102', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '568040103', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '568040104', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '568040105', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '568120101', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '568120102', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '568120103', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '568120104', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '568120105', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '568010201', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '568010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '568010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '568010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '568010205', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '568020201', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '568020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '568020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '568020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '568020205', 1, 4); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '568040201', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '568040202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '568040203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '568040204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '568040205', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '568120201', 1, 5); +REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '568120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '568120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '568120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '568120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '574010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '574010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '574010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '574010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '574010105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '574020101', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '574020102', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '574020103', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '574020104', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '574020105', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '574090101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '574090102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '574090103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '574090104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '574090105', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '574120101', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '574120102', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '574120103', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '574120104', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '574120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '574010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '574010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '574010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '574010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '574010205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '574020201', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '574020202', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '574020203', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '574020204', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '574020205', 1, 4); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '574090201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '574090202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '574090203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '574090204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '574090205', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '574120201', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '574120202', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '574120203', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '574120204', 1, 5); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '574120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '610010101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '610010102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '610010103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '610010104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '610010105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '610020101', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '610020102', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '610020103', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '610020104', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '610020105', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '610090101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '610090102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '610090103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '610090104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '610090105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '610120101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '610120102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '610120103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '610120104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '610120105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '610010201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '610010202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '610010203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '610010204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '610010205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '610020201', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '610020202', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '610020203', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '610020204', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '610020205', 1, 4); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '610090201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '610090202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '610090203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '610090204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '610090205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '610120201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '610120202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '610120203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '610120204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '610120205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '638010101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '638010102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '638010103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '638010104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '638010105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '638020101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '638020102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '638020103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '638020104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '638020105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '638040101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '638040102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '638040103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '638040104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '638040105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '638030101', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '638030102', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '638030103', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '638030104', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '638030105', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '638010201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '638010202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '638010203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '638010204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '638010205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '638020201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '638020202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '638020203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '638020204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '638020205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '638040201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '638040202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '638040203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '638040204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '638040205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '638030201', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '638030202', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '638030203', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '638030204', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '638030205', 1, 5); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '659010101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '659010102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '659010103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '659010104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '659010105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '659020101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '659020102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '659020103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '659020104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '659020105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '659090101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '659090102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '659090103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '659090104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '659090105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '659120101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '659120102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '659120103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '659120104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '659120105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '659010201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '659010202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '659010203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '659010204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '659010205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '659020201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '659020202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '659020203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '659020204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '659020205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '659090201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '659090202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '659090203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '659090204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '659090205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '659120201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '659120202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '659120203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '659120204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '659120205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '663010101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '663010102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '663010103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '663010104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '663010105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '663020101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '663020102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '663020103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '663020104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '663020105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '663090101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '663090102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '663090103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '663090104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '663090105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '663120101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '663120102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '663120103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '663120104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '663120105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '663010201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '663010202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '663010203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '663010204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '663010205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '663020201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '663020202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '663020203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '663020204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '663020205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '663090201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '663090202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '663090203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '663090204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '663090205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '663120201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '663120202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '663120203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '663120204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '663120205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '666010101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '666010102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '666010103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '666010104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '666010105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '666020101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '666020102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '666020103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '666020104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '666020105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '666090101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '666090102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '666090103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '666090104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '666090105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '666120101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '666120102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '666120103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '666120104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '666120105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '666010201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '666010202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '666010203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '666010204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '666010205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '666020201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '666020202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '666020203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '666020204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '666020205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '666090201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '666090202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '666090203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '666090204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '666090205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '666120201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '666120202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '666120203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '666120204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '666120205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '696010101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '696010102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '696010103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '696010104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '696010105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '696020101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '696020102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '696020103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '696020104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '696020105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '696090101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '696090102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '696090103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '696090104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '696090105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '696120101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '696120102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '696120103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '696120104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '696120105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '696010201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '696010202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '696010203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '696010204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '696010205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '696020201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '696020202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '696020203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '696020204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '696020205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '696090201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '696090202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '696090203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '696090204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '696090205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '696120201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '696120202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '696120203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '696120204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '696120205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '698010101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '698010102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '698010103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '698010104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '698010105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '698020101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '698020102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '698020103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '698020104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '698020105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '698090101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '698090102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '698090103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '698090104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '698090105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '698120101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '698120102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '698120103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '698120104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '698120105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '698010201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '698010202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '698010203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '698010204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '698010205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '698020201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '698020202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '698020203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '698020204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '698020205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '698090201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '698090202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '698090203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '698090204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '698090205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '698120201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '698120202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '698120203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '698120204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '698120205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '1458010101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '1458010102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '1458010103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '1458010104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '1458010105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '1458020101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '1458020102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '1458020103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '1458020104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '1458020105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '1458090101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '1458090102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '1458090103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '1458090104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '1458090105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '1458120101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '1458120102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '1458120103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '1458120104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '1458120105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '1458010201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '1458010202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '1458010203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '1458010204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '1458010205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '1458020201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '1458020202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '1458020203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '1458020204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '1458020205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '1458090201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '1458090202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '1458090203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '1458090204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '1458090205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '1458120201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '1458120202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '1458120203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '1458120204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '1458120205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1 ), '36702010101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2 ), '36702010102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3 ), '36702010103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4 ), '36702010104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5 ), '36702010105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1 ), '36702020101', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2 ), '36702020102', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3 ), '36702020103', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4 ), '36702020104', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5 ), '36702020105', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1 ), '36702090101', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2 ), '36702090102', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3 ), '36702090103', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4 ), '36702090104', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5 ), '36702090105', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1 ), '36702120101', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2 ), '36702120102', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3 ), '36702120103', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4 ), '36702120104', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5 ), '36702120105', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1 ), '36702010201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2 ), '36702010202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3 ), '36702010203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4 ), '36702010204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5 ), '36702010205', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1 ), '36702020201', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2 ), '36702020202', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3 ), '36702020203', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4 ), '36702020204', 1, 6); +-- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5 ), '36702020205', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1 ), '36702090201', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2 ), '36702090202', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3 ), '36702090203', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4 ), '36702090204', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5 ), '36702090205', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1 ), '36702120201', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2 ), '36702120202', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3 ), '36702120203', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4 ), '36702120204', 1, 6); +-- -- -- REPLACE INTO merc_templates ( merc_type_id, merc_subtype_id, merc_npc_type_id, dbstring, name_type_id, clientversion ) VALUES ( (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ), (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ), ( SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5 ), '36702120205', 1, 6); + + +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Human Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Barbarian Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Erudite Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Wood Elf Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default High Elf Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Dark Elf Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Half Elf Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Dwarf Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Troll Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Ogre Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Halfling Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Gnome Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Froglok Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Kobold Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Lizard Man Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Scarecrow Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Iksar Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Vah Shir Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Mosquito Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Kunark Goblin Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default ShikNar Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Guktan Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Gnome Pirate Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Dark Elf Pirate Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Frozen Skeleton Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Ikaav Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Bazu Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Pyrilen Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Dragorn Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Gelidran Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Hadal Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Goblin Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Sporali Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Clockwork Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Orc Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Drachnid Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Zombie Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Banshee Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Banshee Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Skrykin Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Bixie Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Drakkin Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Gnoll Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Kerran Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Siren Mercenaries', ''); +REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Brownie Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Minotaur Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Sarnak Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Bellikos Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Marionette Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Amygdalan Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Gingerbread Man Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Swinetor Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Hadal Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Rallosian Orc Mercenaries', ''); +-- REPLACE INTO merc_merchant_templates ( Name, qglobal ) VALUES ( 'Default Frozen Skeleton Mercenaries', ''); + + +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 1, 1, 1, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 1, 1, 5, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 1, 2, 1, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 1, 2, 5, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 1, 2, 6, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 2, 1, 1, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 2, 1, 2, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 2, 2, 1, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 2, 2, 2, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 2, 2, 3, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 2, 2, 4, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 9, 1, 1, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 9, 1, 2, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 9, 2, 1, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 9, 2, 2, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 9, 2, 7, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 12, 1, 1, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 12, 1, 2, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 12, 2, 1, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 12, 2, 2, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 12, 2, 7, 0 ); +REPLACE INTO merc_stance_entries ( class_id, proficiency_id, stance_id, isdefault ) VALUES ( 12, 2, 9, 0 ); + +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 2 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 3 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 4 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 5 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 6 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 7 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 8 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 9 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 10 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 11 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 12 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 26 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 48 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 51 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Scarecrow Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 82 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 128 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 130 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Mosquito Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 134 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 137 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default ShikNar Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 199 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 330 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 338 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Pirate Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 339 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 367 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ikaav Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 394 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bazu Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 409 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Pyrilen Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 411 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dragorn Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 413 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gelidran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 417 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 419 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 433 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 456 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Clockwork Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 457 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drachnid Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 461 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Zombie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 471 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 487 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Banshee Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 488 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Skrykin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 495 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bixie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 520 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 522 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnoll Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 524 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kerran Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 562 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Siren Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 564 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 568 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Minotaur Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 574 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sarnak Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 610 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Bellikos Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 638 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Marionette Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 659 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Amygdalan Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 663 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gingerbread Man Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 666 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Swinetor Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 696 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Hadal Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 698 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Rallosian Orc Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 1458 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 1 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 1 AND tier_id = 5 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 1 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 2 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 3 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 4 LIMIT 1 ))); +-- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 2 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 9 AND tier_id = 5 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 1 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 2 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 3 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 4 LIMIT 1 ))); +-- -- -- REPLACE INTO merc_merchant_template_entries ( merc_merchant_template_id, merc_template_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Frozen Skeleton Mercenaries'), (SELECT merc_template_id FROM merc_templates WHERE merc_type_id = (SELECT merc_type_id FROM merc_types WHERE race_id = 36702 AND proficiency_id = 2 LIMIT 1 ) AND merc_subtype_ID = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = 12 AND tier_id = 5 LIMIT 1 ))); + + +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Caynd')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Caynd')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Barbarian Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Aderrawyn')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Aderrawyn')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Jarenth')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Jarenth')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Half Elf Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Rhilali')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Rhilali')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Bomawyn')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Bomawyn')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Erudite Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Adiasean')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kobold Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Adiasean')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Norerd')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Guktan Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Erohach')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Frilikin')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Gnome Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Asirav')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Voelath')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Voelath')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Vah Shir Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Helind')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Sporali Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Helind')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Rirak')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Rirak')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dwarf Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Cigosh')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Goblin Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Cigosh')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Jelab')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Jelab')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Jelab')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Iksar Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Frendad')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Frendad')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Kunark Goblin Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Frendad')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Onaut')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Onaut')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default High Elf Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Rhenwan')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Brownie Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Rhenwan')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Prau')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Wood Elf Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Shabado')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Kedirakith')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Dark Elf Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Onay')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Drendle')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Human Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Vaehan')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Aboekor')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Adew')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Daknot')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Daknot')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Ogre Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Rhorin')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Lizard Man Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Rhorin')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Ulaurd')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Ulaurd')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Ulaurd')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Troll Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Wedrijan')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Froglok Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Wedrijan')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Orc Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Wedrijan')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Jerbabi')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Halfling Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Guardian_Thaowyn')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Mercenary_Mdjai')); +REPLACE INTO merc_merchant_entries ( merc_merchant_template_id, merchant_id ) VALUES ( (SELECT merc_merchant_template_id FROM merc_merchant_templates WHERE name = 'Default Drakkin Mercenaries'), (SELECT ID FROM npc_types WHERE name = 'Mercenary_Akana')); \ No newline at end of file diff --git a/utils/sql/svn/2380_optional_merc_merchant_npctypes_update.sql b/utils/sql/svn/2380_optional_merc_merchant_npctypes_update.sql new file mode 100644 index 000000000..b64d13d6b --- /dev/null +++ b/utils/sql/svn/2380_optional_merc_merchant_npctypes_update.sql @@ -0,0 +1,128 @@ + -- delete grids +DELETE FROM grid +WHERE zoneid = 202 +AND id IN (SELECT DISTINCT gridid +FROM grid_entries +WHERE zoneid = 202 +AND gridid IN ( SELECT s2.pathgrid FROM spawn2 s2, spawngroup sg, spawnentry se, npc_types nt +WHERE nt.name IN ('Guardian_Aderrawyn', 'Guardian_Caynd', +'a_frightening_liaison', 'Guardian_Jarenth', 'Guardian_Rhilali', +'Guardian_Bomawyn', 'Guardian_Adiasean', 'Guardian_Norerd', 'Guardian_Erohach', +'Guardian_Frilikin', 'Guardian_Asirav', 'Guardian_Voelath', 'Guardian_Helind', +'Guardian_Frendad', 'Guardian_Rirak', 'Guardian_Jelab', 'Guardian_Cigosh', +'Frozen_Skeleton_of_the_Cursed', 'Guardian_Onaut', 'Guardian_Rhenwan', +'Guardian_Kedirakith', 'Guardian_Onay', 'Guardian_Drendle', 'Guardian_Vaehan', +'Guardian_Prau', 'Guardian_Shabado', 'Guardian_Daknot', 'Guardian_Aboekor', +'Guardian_Adew', 'Guardian_Rhorin', 'Facesmasher_Brog', 'Guardian_Ulaurd', +'Guardian_Wedrijan', 'Guardian_Jerbabi', 'Guardian_Thaowyn') +AND s2.spawngroupID = sg.id +AND s2.zone = 'poknowledge' +AND sg.ID = se.spawngroupID +AND se.npcID = nt.id)); + + -- delete grid entries +DELETE FROM grid_entries +WHERE zoneid = 202 +AND gridid IN ( SELECT s2.pathgrid FROM spawn2 s2, spawngroup sg, spawnentry se, npc_types nt +WHERE nt.name IN ('Guardian_Aderrawyn', 'Guardian_Caynd', +'a_frightening_liaison', 'Guardian_Jarenth', 'Guardian_Rhilali', +'Guardian_Bomawyn', 'Guardian_Adiasean', 'Guardian_Norerd', 'Guardian_Erohach', +'Guardian_Frilikin', 'Guardian_Asirav', 'Guardian_Voelath', 'Guardian_Helind', +'Guardian_Frendad', 'Guardian_Rirak', 'Guardian_Jelab', 'Guardian_Cigosh', +'Frozen_Skeleton_of_the_Cursed', 'Guardian_Onaut', 'Guardian_Rhenwan', +'Guardian_Kedirakith', 'Guardian_Onay', 'Guardian_Drendle', 'Guardian_Vaehan', +'Guardian_Prau', 'Guardian_Shabado', 'Guardian_Daknot', 'Guardian_Aboekor', +'Guardian_Adew', 'Guardian_Rhorin', 'Facesmasher_Brog', 'Guardian_Ulaurd', +'Guardian_Wedrijan', 'Guardian_Jerbabi', 'Guardian_Thaowyn') +AND s2.spawngroupID = sg.id +AND s2.zone = 'poknowledge' +AND sg.ID = se.spawngroupID +AND se.npcID = nt.id); + + -- remove spawn2 pathgrid +UPDATE spawn2 SET pathgrid = 0 +WHERE zone = 'poknowledge' +AND spawngroupID IN ( SELECT DISTINCT sg.id FROM spawngroup sg, spawnentry se, npc_types nt +WHERE nt.name IN ('Guardian_Aderrawyn', 'Guardian_Caynd', +'a_frightening_liaison', 'Guardian_Jarenth', 'Guardian_Rhilali', +'Guardian_Bomawyn', 'Guardian_Adiasean', 'Guardian_Norerd', 'Guardian_Erohach', +'Guardian_Frilikin', 'Guardian_Asirav', 'Guardian_Voelath', 'Guardian_Helind', +'Guardian_Frendad', 'Guardian_Rirak', 'Guardian_Jelab', 'Guardian_Cigosh', +'Frozen_Skeleton_of_the_Cursed', 'Guardian_Onaut', 'Guardian_Rhenwan', +'Guardian_Kedirakith', 'Guardian_Onay', 'Guardian_Drendle', 'Guardian_Vaehan', +'Guardian_Prau', 'Guardian_Shabado', 'Guardian_Daknot', 'Guardian_Aboekor', +'Guardian_Adew', 'Guardian_Rhorin', 'Facesmasher_Brog', 'Guardian_Ulaurd', +'Guardian_Wedrijan', 'Guardian_Jerbabi', 'Guardian_Thaowyn') +AND sg.ID = se.spawngroupID +AND se.npcID = nt.id); + +-- UPDATE merc merchant npc_types +UPDATE npc_types set texture = 3, helmtexture = 0, size = 7, gender = 1, race = 2, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1, lastname = 'Barbarian Mercenary Liaison' where name = 'Guardian_Aderrawyn'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 7, gender = 0, race = 2, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0, lastname = 'Barbarian Mercenary Liaison' where name = 'Guardian_Caynd'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 5.5, gender = 0, race = 7, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'Half Elf Mercenary Liaison' where name = 'Guardian_Jarenth'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 5.5, gender = 1, race = 7, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'Half Elf Mercenary Liaison' where name = 'Guardian_Rhilali'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 6, gender = 0, race = 3, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'Erudite Mercenary Liaison' where name = 'Guardian_Bomawyn'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 6, gender = 1, race = 3, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'Erudite Mercenary Liaison' where name = 'Guardian_Adiasean'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 5, gender = 0, race = 330, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'Froglok Mercenary Liaison' where name = 'Guardian_Norerd'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 5, gender = 1, race = 330, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'Froglok Mercenary Liaison' where name = 'Guardian_Erohach'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 3, gender = 0, race = 12, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'Gnome Mercenary Liaison' where name = 'Guardian_Frilikin'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 3, gender = 1, race = 12, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'Gnome Mercenary Liaison' where name = 'Guardian_Asirav'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 7, gender = 0, race = 130, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'Vah Shir Mercenary Liaison' where name = 'Guardian_Voelath'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 7, gender = 1, race = 130, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'Vah Shir Mercenary Liaison' where name = 'Guardian_Helind'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 6, gender = 1, race = 128, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'Iksar Mercenary Liaison' where name = 'Guardian_Frendad'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 4, gender = 1, race = 8, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'Dwarf Mercenary Liaison' where name = 'Guardian_Rirak'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 6, gender = 0, race = 128, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'Iksar Mercenary Liaison' where name = 'Guardian_Jelab'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 4, gender = 0, race = 8, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'Dwarf Mercenary Liaison' where name = 'Guardian_Cigosh'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 6, gender = 0, race = 5, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'High Elf Mercenary Liaison' where name = 'Guardian_Onaut'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 6, gender = 1, race = 5, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'High Elf Mercenary Liaison' where name = 'Guardian_Rhenwan'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 5, gender = 0, race = 6, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'Dark Elf Mercenary Liaison' where name = 'Guardian_Kedirakith'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 5, gender = 1, race = 6, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'Dark Elf Mercenary Liaison' where name = 'Guardian_Onay'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 6, gender = 1, race = 1, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'Human Mercenary Liaison' where name = 'Guardian_Drendle'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 6, gender = 0, race = 1, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'Human Mercenary Liaison' where name = 'Guardian_Vaehan'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 5, gender = 0, race = 4, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'Wood Elf Mercenary Liaison' where name = 'Guardian_Prau'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 5, gender = 1, race = 4, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'Wood Elf Mercenary Liaison' where name = 'Guardian_Shabado'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 9, gender = 1, race = 10, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'Ogre Mercenary Liaison' where name = 'Guardian_Daknot'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 6, gender = 0, race = 522, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'Drakkin Mercenary Liaison' where name = 'Guardian_Aboekor'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 5.7, gender = 1, race = 522, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'Drakkin Mercenary Liaison' where name = 'Guardian_Adew'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 9, gender = 0, race = 10, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'Ogre Mercenary Liaison' where name = 'Guardian_Rhorin'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 8, gender = 1, race = 9, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'Troll Mercenary Liaison' where name = 'Guardian_Ulaurd'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 8, gender = 0, race = 9, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'Troll Mercenary Liaison' where name = 'Guardian_Wedrijan'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 3.5, gender = 0, race = 11, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 0 , lastname = 'Halfling Mercenary Liaison' where name = 'Guardian_Jerbabi'; +UPDATE npc_types set texture = 3, helmtexture = 0, size = 3.5, gender = 1, race = 11, class = 71, level = 99, hp = 10791, face = 0, luclin_hairstyle = 0, luclin_haircolor = 0, luclin_eyecolor = 0, luclin_eyecolor2 = 0, luclin_beardcolor = 0, luclin_beard = 255, drakkin_heritage = 0, drakkin_tattoo = 0, drakkin_details = 0, armortint_red = 0, armortint_green = 0, armortint_blue = 0, d_meele_texture1 = 0, d_meele_texture2 = 0, findable = True, gender = 1 , lastname = 'Halfling Mercenary Liaison' where name = 'Guardian_Thaowyn'; + +-- UPDATE merc merchant spawn locations +UPDATE spawn2 SET x = -188, y = 216, z = -109.5, heading = 128 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Aderrawyn' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = -213, y = 216, z = -109.5, heading = 128 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Caynd' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = -0.375, y = -41.875, z = -155.125, heading = 189.5 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'a_frightening_liaison' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 83, y = 10, z = -80.5, heading = 192 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Jarenth' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 83, y = -9, z = -80.5, heading = 192 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Rhilali' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 170, y = 170, z = -123.875, heading = 192 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Bomawyn' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 170, y = 200, z = -123.875, heading = 192 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Adiasean' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 172, y = -173, z = -124.75, heading = 192 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Norerd' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 172, y = -203, z = -124.75, heading = 192 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Erohach' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 258, y = 533, z = -158, heading = 0 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Frilikin' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 288, y = 533, z = -158, heading = 0 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Asirav' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 444, y = 429, z = -123.5, heading = 192 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Voelath' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 444, y = 449, z = -123.5, heading = 192 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Helind' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 260, y = -533, z = -156.125, heading = 128 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Frendad' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 566, y = 25, z = 2.5, heading = 64 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Rirak' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 290, y = -533, z = -156.125, heading = 128 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Jelab' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 570, y = -25, z = 2.5, heading = 64 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Cigosh' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 234.875, y = -824.375, z = -156.125, heading = 215 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Frozen_Skeleton_of_the_Cursed' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 700, y = 533, z = -124.125, heading = 0 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Onaut' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 730, y = 533, z = -124.125, heading = 0 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Rhenwan' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 700, y = -533, z = -124.75, heading = 128 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Kedirakith' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 730, y = -533, z = -124.75, heading = 128 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Onay' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 904, y = 274, z = -1.375, heading = 128 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Drendle' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 948, y = 274, z = -1, heading = 128 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Vaehan' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 667, y = 842, z = -110.75, heading = 64 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Prau' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 667, y = 871, z = -110.75, heading = 64 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Shabado' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 1000, y = -223, z = 6, heading = 0 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Daknot' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 917, y = 648, z = -79.875, heading = 0 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Aboekor' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 937, y = 648, z = -80.125, heading = 0 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Adew' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 1050, y = -223, z = 6, heading = 0 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Rhorin' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 1070, y = -461, z = -122.875, heading = 0 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Facesmasher_Brog' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 1017, y = -636, z = -78.875, heading = 128 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Ulaurd' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 1035, y = -636, z = -78.875, heading = 128 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Wedrijan' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 1404, y = 68, z = -109.75, heading = 64 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Jerbabi' LIMIT 1) AND sg.id = se.spawngroupID); +UPDATE spawn2 SET x = 1404, y = -68, z = -109.75, heading = 64 WHERE spawngroupID = (SELECT sg.id FROM spawngroup sg, spawnentry se WHERE se.npcID = (SELECT ID FROM npc_types WHERE name = 'Guardian_Thaowyn' LIMIT 1) AND sg.id = se.spawngroupID); diff --git a/utils/sql/svn/2380_optional_merc_rules.sql b/utils/sql/svn/2380_optional_merc_rules.sql new file mode 100644 index 000000000..6bbce2baa --- /dev/null +++ b/utils/sql/svn/2380_optional_merc_rules.sql @@ -0,0 +1,8 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Mercs:AllowMercs', 'false', 'Turns mercs on for the server - will not load merc data if set to false.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Mercs:SuspendIntervalMS', '10000', 'Time interval for suspend command in milliseconds.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Mercs:UpkeepIntervalMS', '180000', 'Time interval for merc upkeep in milliseconds.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Mercs:SuspendIntervalS', '10', 'Time interval for suspend command in seconds.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Mercs:UpkeepIntervalS', '180', 'Time interval for merc upkeep in seconds.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Mercs:ScaleRate', '100', 'Allows scaling of merc stats vs livelike values.'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Mercs:AggroRadius', '100', 'Determines the distance from which a merc will aggro target(also used to determine the distance at which a healer merc will begin healing a group member)'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Mercs:AggroRadiusPuller', '25', 'Determines the distance from which a merc will aggro target, if they have the group role of puller (also used to determine the distance at which a healer merc will begin healing a group member, if they have the group role of puller)'); \ No newline at end of file diff --git a/utils/sql/svn/2383_required_group_ismerc.sql b/utils/sql/svn/2383_required_group_ismerc.sql new file mode 100644 index 000000000..3e9a6077f --- /dev/null +++ b/utils/sql/svn/2383_required_group_ismerc.sql @@ -0,0 +1 @@ +ALTER TABLE `group_id` ADD COLUMN `ismerc` tinyint(3) NOT NULL DEFAULT 0 AFTER `name`, DROP PRIMARY KEY, ADD PRIMARY KEY (`groupid`, `charid`, `ismerc`); \ No newline at end of file diff --git a/utils/sql/svn/2428_optional_levelbasedexpmods.sql b/utils/sql/svn/2428_optional_levelbasedexpmods.sql new file mode 100644 index 000000000..225a13dde --- /dev/null +++ b/utils/sql/svn/2428_optional_levelbasedexpmods.sql @@ -0,0 +1,117 @@ +INSERT INTO `rule_values` VALUES ('1', 'Zone:LevelBasedEXPMods', 'true', null); + +SET FOREIGN_KEY_CHECKS=0; +-- ---------------------------- +-- Table structure for `level_exp_mods` +-- ---------------------------- +DROP TABLE IF EXISTS `level_exp_mods`; +CREATE TABLE `level_exp_mods` ( + `level` int(11) NOT NULL DEFAULT '0', + `exp_mod` float DEFAULT NULL, + `aa_exp_mod` float DEFAULT NULL, + PRIMARY KEY (`level`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Records of level_exp_mods +-- ---------------------------- +INSERT INTO `level_exp_mods` VALUES ('1', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('2', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('3', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('4', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('5', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('6', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('7', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('8', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('9', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('10', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('11', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('12', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('13', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('14', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('15', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('16', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('17', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('18', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('19', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('20', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('21', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('22', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('23', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('24', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('25', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('26', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('27', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('28', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('29', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('30', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('31', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('32', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('33', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('34', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('35', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('36', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('37', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('38', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('39', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('40', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('41', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('42', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('43', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('44', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('45', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('46', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('47', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('48', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('49', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('50', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('51', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('52', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('53', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('54', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('55', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('56', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('57', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('58', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('59', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('60', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('61', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('62', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('63', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('64', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('65', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('66', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('67', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('68', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('69', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('70', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('71', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('72', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('73', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('74', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('75', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('76', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('77', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('78', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('79', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('80', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('81', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('82', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('83', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('84', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('85', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('86', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('87', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('88', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('89', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('90', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('91', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('92', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('93', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('94', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('95', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('96', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('97', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('98', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('99', '1', '1'); +INSERT INTO `level_exp_mods` VALUES ('100', '1', '1'); diff --git a/utils/sql/svn/243_spawn_timers.sql b/utils/sql/svn/243_spawn_timers.sql new file mode 100644 index 000000000..d6cffa167 --- /dev/null +++ b/utils/sql/svn/243_spawn_timers.sql @@ -0,0 +1,9 @@ +DROP TABLE IF EXISTS `respawn_times`; +CREATE TABLE `respawn_times` ( + `id` int(11) NOT NULL default '0', + `start` int(11) NOT NULL default '0', + `duration` int(11) NOT NULL default '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +ALTER TABLE `spawn2` DROP `timeleft`; \ No newline at end of file diff --git a/utils/sql/svn/2448_optional_stun_proc_aggro_rule.sql b/utils/sql/svn/2448_optional_stun_proc_aggro_rule.sql new file mode 100644 index 000000000..9e6576b31 --- /dev/null +++ b/utils/sql/svn/2448_optional_stun_proc_aggro_rule.sql @@ -0,0 +1,5 @@ +/* Sorvani */ + +-- Max Aggro from a Stun Proc -- +INSERT INTO `rule_values` (`ruleset_id`,`rule_name`,`rule_value`,`notes`) + VALUES (1,'Aggro:MaxStunProcAggro','400','Set to -1 for no limit. Maxmimum amount of aggro that a stun based proc will add.'); \ No newline at end of file diff --git a/utils/sql/svn/2471_required_aa_updates.sql b/utils/sql/svn/2471_required_aa_updates.sql new file mode 100644 index 000000000..71bda30c7 --- /dev/null +++ b/utils/sql/svn/2471_required_aa_updates.sql @@ -0,0 +1,34 @@ +-- MISC AA fixes mostly display related + +-- Fixed for siezed opporunity +UPDATE altadv_vars SET sof_next_id = 1539 WHERE skill_id = 878; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 1539; +UPDATE altadv_vars SET sof_type = 3 WHERE skill_id = 1539; + +-- Triple backstab +UPDATE altadv_vars SET sof_next_id = 1301 WHERE skill_id = 846; +UPDATE altadv_vars SET sof_current_level = 3 WHERE skill_id = 1301; +UPDATE altadv_vars SET sof_type = 3 WHERE skill_id = 1301; + +/* Prolonged Destruction prereq should be 931 Frenzied Devastation not 741 Touch of the Divine */ +UPDATE altadv_vars SET prereq_skill=931 WHERE skill_id=1337; +/* Ro's Greater Familiar prereq should be 921 Ro's Flaming Familiar not 724 BL Warder's Fury */ +UPDATE altadv_vars SET prereq_skill=921 WHERE skill_id=1340; +/* E'ci's Greater Familiar prereq should be 922 E'ci's Icy Familiar not 729 BL Warder's Alacrity */ +UPDATE altadv_vars SET prereq_skill=922 WHERE skill_id=1341; +/* Druzzil's Greater Familiar prereq should be 923 Druzzil's Mystical Familiar not 734 BL Pet Affinity */ +UPDATE altadv_vars SET prereq_skill=923 WHERE skill_id=1342; +/* Devoted prereq should be 533 Allegiant Familiar not 290 BL Frenzy of Spirit */ +UPDATE altadv_vars SET prereq_skill=533 WHERE skill_id=1344; + +-- Fix extend ingenunity +UPDATE altadv_vars SET sof_next_id = 8232 WHERE skill_id = 8261; + +-- Hasty Exit +UPDATE altadv_vars SET sof_next_id = 886 WHERE skill_id = 498; +UPDATE altadv_vars SET sof_current_level = 2 WHERE skill_id = 886; + +-- Pet Disc -- still semi bugged it will show 0/3 after buying first rank but when you buy it will go to 2/3 +UPDATE altadv_vars SET sof_type = 2 WHERE skill_id = 288; +UPDATE altadv_vars SET cost_inc = 2 WHERE skill_id = 1129; + diff --git a/utils/sql/svn/247_mail.sql b/utils/sql/svn/247_mail.sql new file mode 100644 index 000000000..a7fede7f8 --- /dev/null +++ b/utils/sql/svn/247_mail.sql @@ -0,0 +1,14 @@ +CREATE TABLE `mail` ( + `msgid` int(10) unsigned NOT NULL auto_increment, + `charid` int(10) unsigned NOT NULL, + `timestamp` int(11) NOT NULL default '0', + `from` varchar(100) NOT NULL, + `subject` varchar(200) NOT NULL, + `body` text NOT NULL, + `to` text NOT NULL, + `status` tinyint(4) NOT NULL, + PRIMARY KEY (`msgid`), + KEY `charid` (`charid`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +ALTER TABLE `character_` ADD `mailkey` CHAR( 16 ) NOT NULL default '0'; diff --git a/utils/sql/svn/2482_required_start_zones.sql b/utils/sql/svn/2482_required_start_zones.sql new file mode 100644 index 000000000..fa2f697fc --- /dev/null +++ b/utils/sql/svn/2482_required_start_zones.sql @@ -0,0 +1 @@ +alter table `start_zones` add column `heading` float not null default 0 AFTER `z`; \ No newline at end of file diff --git a/utils/sql/svn/249_chatchannels.sql b/utils/sql/svn/249_chatchannels.sql new file mode 100644 index 000000000..a1384c4b6 --- /dev/null +++ b/utils/sql/svn/249_chatchannels.sql @@ -0,0 +1,44 @@ +-- +-- Table structure for table `chatchannels` +-- + +CREATE TABLE `chatchannels` ( + `name` varchar(64) NOT NULL, + `owner` varchar(64) NOT NULL, + `password` varchar(64) NOT NULL, + `minstatus` int(5) NOT NULL default '0', + PRIMARY KEY (`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +-- +-- Dumping data for table `chatchannels` +-- + +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Newplayers', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('General', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Warrior', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Cleric', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Paladin', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Ranger', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Shadowknight', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Druid', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Monk', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Bard', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Rogue', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Shaman', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Necromancer', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Wizard', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Magician', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Enchanter', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Beastlord', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Berserker', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Antonica', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Odus', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Faydwer', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Kunark', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Velious', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Luclin', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Planes', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Brokenskull', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Taelosia', '*System*', '', 0); +INSERT INTO `chatchannels` (`name`, `owner`, `password`, `minstatus`) VALUES ('Kuua', '*System*', '', 0); diff --git a/utils/sql/svn/2504_required_aa_updates.sql b/utils/sql/svn/2504_required_aa_updates.sql new file mode 100644 index 000000000..8b2f1616c --- /dev/null +++ b/utils/sql/svn/2504_required_aa_updates.sql @@ -0,0 +1,32 @@ +-- Fix for Extended Ingenuity +UPDATE altadv_vars SET max_level = 3 WHERE skill_id = 8232; +UPDATE altadv_vars SET sof_max_level = 4 WHERE skill_id = 8232; +UPDATE altadv_vars SET sof_max_level = 4 WHERE skill_id = 8261; + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8232', '1', '128', '5', '5'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8232', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8232', '3', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8232', '4', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8232', '5', '411', '66434', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8232', '6', '137', '-40', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8233', '1', '128', '15', '15'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8233', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8233', '3', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8233', '4', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8233', '5', '411', '66434', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8233', '6', '137', '-40', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8234', '1', '128', '25', '25'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8234', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8234', '3', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8234', '4', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8234', '5', '411', '66434', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8234', '6', '137', '-40', '0'); + +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8261', '1', '128', '65', '65'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8261', '2', '138', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8261', '3', '140', '1', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8261', '4', '311', '0', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8261', '5', '411', '66434', '0'); +REPLACE INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('8261', '6', '137', '-40', '0'); diff --git a/utils/sql/svn/250_bot_spell_update.sql b/utils/sql/svn/250_bot_spell_update.sql new file mode 100644 index 000000000..635dd1c36 --- /dev/null +++ b/utils/sql/svn/250_bot_spell_update.sql @@ -0,0 +1,23 @@ + +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=278; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=425; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=428; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=4058; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=4055; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=235; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=42; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=426; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=3579; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=428; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=717; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=2605; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=2517; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=2894; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=80; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=261; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=255; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=894; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=2739; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=86; + +INSERT INTO npc_spells_entries SET npc_spells_id=707, spellid=425, type=8, minlevel=20, maxlevel=255, manacost=-1, recast_delay=-1, priority=0; diff --git a/utils/sql/svn/250_optional_bot_spell_update.sql b/utils/sql/svn/250_optional_bot_spell_update.sql new file mode 100644 index 000000000..b47d4b263 --- /dev/null +++ b/utils/sql/svn/250_optional_bot_spell_update.sql @@ -0,0 +1,12 @@ + +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=62; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=227; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=63; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=226; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=60; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=224; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=61; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=225; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=64; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=228; + diff --git a/utils/sql/svn/285_optional_bot_spell_update.sql b/utils/sql/svn/285_optional_bot_spell_update.sql new file mode 100644 index 000000000..bf75eada6 --- /dev/null +++ b/utils/sql/svn/285_optional_bot_spell_update.sql @@ -0,0 +1,7 @@ + +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=481; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=482; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=483; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=484; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=1689; +DELETE FROM npc_spells_entries where (npc_spells_id >=701 and npc_spells_id <=712) AND spellid=3343; diff --git a/utils/sql/svn/292_augslots.sql b/utils/sql/svn/292_augslots.sql new file mode 100644 index 000000000..7ec20c6e1 --- /dev/null +++ b/utils/sql/svn/292_augslots.sql @@ -0,0 +1,12 @@ +-- These will throw up warnings if you have an outdated schema. +alter table items drop column augslot1visible; +alter table items drop column augslot2visible; +alter table items drop column augslot3visible; +alter table items drop column augslot4visible; +alter table items drop column augslot5visible; + +alter table items change column `augslot1unk` `augslot1visible` tinyint(3); +alter table items change column `augslot2unk` `augslot2visible` tinyint(3); +alter table items change column `augslot3unk` `augslot3visible` tinyint(3); +alter table items change column `augslot4unk` `augslot4visible` tinyint(3); +alter table items change column `augslot5unk` `augslot5visible` tinyint(3); \ No newline at end of file diff --git a/utils/sql/svn/294_merchant_logging.sql b/utils/sql/svn/294_merchant_logging.sql new file mode 100644 index 000000000..148d64ccc --- /dev/null +++ b/utils/sql/svn/294_merchant_logging.sql @@ -0,0 +1,3 @@ +ALTER TABLE `eventlog` ADD `event_nid` INT NOT NULL DEFAULT '0'; +INSERT INTO `rule_values` VALUES (0, 'EventLog:RecordSellToMerchant', 'false'); +INSERT INTO `rule_values` VALUES (0, 'EventLog:RecordBuyFromMerchant', 'false'); diff --git a/utils/sql/svn/2_optional_maxclients.sql b/utils/sql/svn/2_optional_maxclients.sql new file mode 100644 index 000000000..4010986a3 --- /dev/null +++ b/utils/sql/svn/2_optional_maxclients.sql @@ -0,0 +1,2 @@ +Insert into rule_values values (0, 'World:AddMaxClientsPerIP', -1 ); +Insert into rule_values values (0, 'World:AddMaxClientsStatus', -1 ); diff --git a/utils/sql/svn/304_faction_list.sql b/utils/sql/svn/304_faction_list.sql new file mode 100644 index 000000000..e8ba7f199 --- /dev/null +++ b/utils/sql/svn/304_faction_list.sql @@ -0,0 +1,2 @@ +ALTER TABLE `faction_list` ADD COLUMN `mod_c16` smallint(6) NOT NULL default '0' AFTER `mod_c15`; +ALTER TABLE `faction_list` ADD COLUMN `mod_r330` smallint(6) NOT NULL default '0' AFTER `mod_r161`; \ No newline at end of file diff --git a/utils/sql/svn/326_aas.sql b/utils/sql/svn/326_aas.sql new file mode 100644 index 000000000..d1b28a18b --- /dev/null +++ b/utils/sql/svn/326_aas.sql @@ -0,0 +1,5 @@ +SET @row = 0; +UPDATE altadv_vars a SET prereq_skill = COALESCE((SELECT p.skill_id FROM (SELECT a2.skill_id, @row := @row + 1 AS prereq_index_num FROM altadv_vars a2) AS p WHERE p.prereq_index_num = a.prereq_skill), 0) WHERE prereq_skill < 1000000; +update altadv_vars set prereq_skill = 767 where skill_id = 1099; +update altadv_vars set prereq_skill = 844 where skill_id = 1319; +INSERT INTO `rule_values` VALUES (0, 'Combat:ChanceToHitDivideBy', '1250'); \ No newline at end of file diff --git a/utils/sql/svn/328_bot_management.sql b/utils/sql/svn/328_bot_management.sql new file mode 100644 index 000000000..939d2069c --- /dev/null +++ b/utils/sql/svn/328_bot_management.sql @@ -0,0 +1,10 @@ +DROP TABLE IF EXISTS `botleader`; +CREATE TABLE `botleader` ( + `botid` int(11) NOT NULL default '0', + `leaderid` int(11) default NULL, + `bot_name` text NOT NULL, + `zone_name` text NOT NULL, + PRIMARY KEY (`botid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +DELETE from rule_values where rule_name='EQOffline:BotCount'; + diff --git a/utils/sql/svn/328_optional_bot_management.sql b/utils/sql/svn/328_optional_bot_management.sql new file mode 100644 index 000000000..3ea443a61 --- /dev/null +++ b/utils/sql/svn/328_optional_bot_management.sql @@ -0,0 +1,23 @@ +delete from rule_values where rule_name like 'EQOffline%'; +INSERT INTO rule_values VALUES ('0', 'EQOffline:CreateBotCount', 150); +INSERT INTO rule_values VALUES ('0', 'EQOffline:SpawnBotCount', 71); +INSERT INTO rule_values VALUES ('0', 'EQOffline:BotQuest', 'true'); +delete from npc_types where name='Aediles_Thrall'; +delete from npc_types where name='Aspen'; +insert into npc_types ( id, name, lastname, level, race, class, bodytype, hp, gender, texture, helmtexture, size, hp_regen_rate, mana_regen_rate, loottable_id, merchant_id, npc_spells_id, npc_faction_id, mindmg, maxdmg, npcspecialattks, aggroradius, face, runspeed, MR, CR, DR, FR, PR, see_invis, see_invis_undead, qglobal, AC, spawn_limit, findable, STR, STA, DEX, AGI, _INT, WIS, CHA, see_hide, see_improved_hide, trackable, isbot) + VALUES (151070, 'Aediles_Thrall', '', 71, 8, 1, 1, 10000, 0, 0, 0, 5.0, 500, 0, 0, 0, 0, 0, 1, 1, '', 70, 255, 1.9, 0, 0, 0, 0, 0, 1, 1, 1, 5000, 0, 1, 192, 192, 192, 192, 192, 192, 192, 1, 1, 1, 0); +insert into npc_types ( id, name, lastname, level, race, class, bodytype, hp, gender, texture, helmtexture, size, hp_regen_rate, mana_regen_rate, loottable_id, merchant_id, npc_spells_id, npc_faction_id, mindmg, maxdmg, npcspecialattks, aggroradius, face, runspeed, MR, CR, DR, FR, PR, see_invis, see_invis_undead, qglobal, AC, spawn_limit, findable, STR, STA, DEX, AGI, _INT, WIS, CHA, see_hide, see_improved_hide, trackable, isbot) + VALUES (151071, 'Aspen', '', 5, 7, 1, 1, 10000, 0, 0, 0, 4.5, 500, 0, 0, 0, 0, 0, 1, 1, '', 70, 255, 1.9, 0, 0, 0, 0, 0, 1, 1, 1, 5000, 0, 1, 192, 192, 192, 192, 192, 192, 192, 1, 1, 1, 0); +delete from spawn2 where spawngroupid=49000 and zone='bazaar'; +delete from spawn2 where spawngroupid=49001 and zone='bazaar'; +insert into spawn2 (spawngroupID, zone, x, y, z, heading, respawntime) values (60000, 'bazaar', 119.385948, -402.999329, 33.257092, 224.250000, 1200); +insert into spawn2 (spawngroupID, zone, x, y, z, heading, respawntime) values (60001, 'bazaar', 116.750000, -400.375000, 33.257092, 89.875000, 1200); +delete from spawngroup where name='bazaar_Aediles_Thrall'; +delete from spawngroup where name='bazaar_Aspen'; +insert into spawngroup (id, name) values (60000, 'bazaar_Aediles_Thrall'); +insert into spawngroup (id, name) values (60001, 'bazaar_Aspen'); +delete from spawnentry where npcid=151070; +delete from spawnentry where npcid=151071; +insert into spawnentry (spawngroupID, npcID, chance) values (60000, 151070, 100); +insert into spawnentry (spawngroupID, npcID, chance) values (60001, 151071, 100); + diff --git a/utils/sql/svn/340_gm_ips.sql b/utils/sql/svn/340_gm_ips.sql new file mode 100644 index 000000000..d5e7f9cd4 --- /dev/null +++ b/utils/sql/svn/340_gm_ips.sql @@ -0,0 +1,8 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`) VALUES (0, 'World:GMAccountIPList', 'false'); + +CREATE TABLE IF NOT EXISTS `gm_ips` ( + `name` varchar(64) NOT NULL, + `account_id` int(11) NOT NULL, + `ip_address` varchar(15) NOT NULL, + UNIQUE KEY `account_id` (`account_id`,`ip_address`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; \ No newline at end of file diff --git a/utils/sql/svn/356_combat.sql b/utils/sql/svn/356_combat.sql new file mode 100644 index 000000000..f472ed637 --- /dev/null +++ b/utils/sql/svn/356_combat.sql @@ -0,0 +1,18 @@ +DELETE FROM `rule_values` WHERE rule_name = 'Combat:BaseHitChance'; +INSERT INTO `rule_values` VALUES (0, 'Combat:BaseHitChance', 69); +DELETE FROM `rule_values` WHERE rule_name = 'Combat:AgiHitFactor'; +INSERT INTO `rule_values` VALUES (0, 'Combat:AgiHitFactor', 0.01); +INSERT INTO `rule_values` VALUES (0, 'EventLog:RecordSellToMerchant', 'false'); +INSERT INTO `rule_values` VALUES (0, 'Combat:HitFalloffMinor', 5.0); +INSERT INTO `rule_values` VALUES (0, 'Combat:HitFalloffModerate', 7.0); +INSERT INTO `rule_values` VALUES (0, 'Combat:HitFalloffMajor', 50.0); +INSERT INTO `rule_values` VALUES (0, 'Combat:HitBonusPerLevel', 0.4); +INSERT INTO `rule_values` VALUES (0, 'Combat:WeaponSkillFalloff', 0.33); +INSERT INTO `rule_values` VALUES (0, 'Combat:ArcheryHitPenalty', 0.25); +INSERT INTO `rule_values` VALUES (0, 'Combat:MeleeHitChanceMod', 1.0); +INSERT INTO `rule_values` VALUES (0, 'Combat:PriestHitChanceMod', 0.85); +INSERT INTO `rule_values` VALUES (0, 'Combat:CasterHitChanceMod', 0.7); +INSERT INTO `rule_values` VALUES (0, 'Combat:HeavyAvoidChanceMod', 1.0); +INSERT INTO `rule_values` VALUES (0, 'Combat:ModerateAvoidChanceMod', 0.96); +INSERT INTO `rule_values` VALUES (0, 'Combat:LightAvoidChanceMod', 0.91); +INSERT INTO `rule_values` VALUES (0, 'Combat:UnarmoredAvoidChanceMod', 0.86); \ No newline at end of file diff --git a/utils/sql/svn/35_task_stepped.sql b/utils/sql/svn/35_task_stepped.sql new file mode 100644 index 000000000..696a5e331 --- /dev/null +++ b/utils/sql/svn/35_task_stepped.sql @@ -0,0 +1 @@ +ALTER TABLE `tasks` DROP `stepped` ; \ No newline at end of file diff --git a/utils/sql/svn/360_peqzone.sql b/utils/sql/svn/360_peqzone.sql new file mode 100644 index 000000000..7aeab16d0 --- /dev/null +++ b/utils/sql/svn/360_peqzone.sql @@ -0,0 +1,11 @@ +INSERT INTO `rule_values` VALUES ('1', 'Merchant:UsePriceMod', 'true'); +INSERT INTO `rule_values` VALUES ('1', 'Merchant:SellCostMod', '1.05'); +INSERT INTO `rule_values` VALUES ('1', 'Merchant:BuyCostMod', '0.95'); +INSERT INTO `rule_values` VALUES ('1', 'Merchant:PriceBonusPct', '4'); +INSERT INTO `rule_values` VALUES ('1', 'Merchant:PricePenaltyPct', '4'); +INSERT INTO `rule_values` VALUES ('1', 'Merchant:ChaBonusMod', '3.45'); +INSERT INTO `rule_values` VALUES ('1', 'Merchant:ChaPenaltyMod', '1.52'); + +update zone set peqzone = 0 where zoneidnumber in (26 ,39 ,187 ,188 ,71 ,162 ,76 ,186 ,105 ,124 ,89 ,128 ,189 ,108 ,158 ,200 ,201); +update zone set peqzone = 0 where zoneidnumber > 203 and zoneidnumber < 224; +update zone set peqzone = 0 where zoneidnumber > 228; \ No newline at end of file diff --git a/utils/sql/svn/364_ranged_dist_rule.sql b/utils/sql/svn/364_ranged_dist_rule.sql new file mode 100644 index 000000000..dc148e787 --- /dev/null +++ b/utils/sql/svn/364_ranged_dist_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('1', 'Combat:MinRangedAttackDist', '25'); diff --git a/utils/sql/svn/386_bot_save_raid.sql b/utils/sql/svn/386_bot_save_raid.sql new file mode 100644 index 000000000..5d029081d --- /dev/null +++ b/utils/sql/svn/386_bot_save_raid.sql @@ -0,0 +1,8 @@ +DROP TABLE IF EXISTS `botgroups`; +CREATE TABLE `botgroups` ( + `groupid` int(11) NOT NULL default '0', + `charid` int(11) NOT NULL default '0', + `botid` int(11) NOT NULL default '0', + `slot` int(11) NOT NULL default '0', + PRIMARY KEY (`botid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; diff --git a/utils/sql/svn/42_task_min_maxlevel.sql b/utils/sql/svn/42_task_min_maxlevel.sql new file mode 100644 index 000000000..e0c8015f5 --- /dev/null +++ b/utils/sql/svn/42_task_min_maxlevel.sql @@ -0,0 +1,2 @@ +ALTER TABLE `tasks` ADD `minlevel` TINYINT UNSIGNED NOT NULL DEFAULT '0', +ADD `maxlevel` TINYINT UNSIGNED NOT NULL DEFAULT '0'; diff --git a/utils/sql/svn/434_optional_rest_state_rules.sql b/utils/sql/svn/434_optional_rest_state_rules.sql new file mode 100644 index 000000000..032936a0d --- /dev/null +++ b/utils/sql/svn/434_optional_rest_state_rules.sql @@ -0,0 +1,3 @@ +INSERT INTO rule_values VALUES(0,'Character:RestRegenPercent',0); +INSERT INTO rule_values VALUES(0,'Character:RestRegenTimeToActivate',30); + diff --git a/utils/sql/svn/447_sof_startzone_rule.sql b/utils/sql/svn/447_sof_startzone_rule.sql new file mode 100644 index 000000000..e4b0465d7 --- /dev/null +++ b/utils/sql/svn/447_sof_startzone_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('1', 'World:SoFStartZoneID', '-1'); diff --git a/utils/sql/svn/463_altadv_vars.sql b/utils/sql/svn/463_altadv_vars.sql new file mode 100644 index 000000000..05b7b9e4f --- /dev/null +++ b/utils/sql/svn/463_altadv_vars.sql @@ -0,0 +1,116 @@ +# +# The spell_type field in the altadv_vars table is used by the client as a timer ID and is +# sent in OP_AAAction timer update packets. +# +# AAs on a shared timer should have the same spell_type. +# +# Clean out any existing timers using the previous method of numbering AA Timers. +# +delete from timers where type>=1000 and type<=2999; +# +# This updates the spell_type (Timer ID) for all existing entries that +# have a reuse timer. +# +# Most of the numbers have been collected from live, with a few tweaks where AAs have been removed/combined. +# +update altadv_vars set spell_type=1 where skill_id =128; +update altadv_vars set spell_type=2 where skill_id =129; +update altadv_vars set spell_type=3 where skill_id =130; +update altadv_vars set spell_type=4 where skill_id =131; +update altadv_vars set spell_type=5 where skill_id =132; +update altadv_vars set spell_type=6 where skill_id =133; +update altadv_vars set spell_type=7 where skill_id =136; +update altadv_vars set spell_type=3 where skill_id =140; +update altadv_vars set spell_type=15 where skill_id =145; +update altadv_vars set spell_type=2 where skill_id =146; +update altadv_vars set spell_type=3 where skill_id =153; +update altadv_vars set spell_type=7 where skill_id =154; +update altadv_vars set spell_type=8 where skill_id =155; +update altadv_vars set spell_type=2 where skill_id =156; +update altadv_vars set spell_type=3 where skill_id =162; +update altadv_vars set spell_type=2 where skill_id =163; +update altadv_vars set spell_type=14 where skill_id =167; +update altadv_vars set spell_type=4 where skill_id =168; +update altadv_vars set spell_type=4 where skill_id =171; +update altadv_vars set spell_type=4 where skill_id =174; +update altadv_vars set spell_type=4 where skill_id =177; +update altadv_vars set spell_type=5 where skill_id =181; +update altadv_vars set spell_type=4 where skill_id =183; +update altadv_vars set spell_type=5 where skill_id =184; +update altadv_vars set spell_type=6 where skill_id =185; +update altadv_vars set spell_type=7 where skill_id =186; +update altadv_vars set spell_type=8 where skill_id =187; +update altadv_vars set spell_type=9 where skill_id =188; +update altadv_vars set spell_type=3 where skill_id =193; +update altadv_vars set spell_type=0 where skill_id =194; +update altadv_vars set spell_type=2 where skill_id =197; +update altadv_vars set spell_type=0 where skill_id =206; +update altadv_vars set spell_type=2 where skill_id =207; +update altadv_vars set spell_type=2 where skill_id =208; +update altadv_vars set spell_type=1 where skill_id =233; +update altadv_vars set spell_type=1 where skill_id =243; +update altadv_vars set spell_type=12 where skill_id =254; +update altadv_vars set spell_type=1 where skill_id =258; +update altadv_vars set spell_type=2 where skill_id =259; +update altadv_vars set spell_type=3 where skill_id =260; +update altadv_vars set spell_type=4 where skill_id =274; +update altadv_vars set spell_type=3 where skill_id =289; +update altadv_vars set spell_type=4 where skill_id =290; +update altadv_vars set spell_type=5 where skill_id =291; +update altadv_vars set spell_type=8 where skill_id =459; +update altadv_vars set spell_type=9 where skill_id =507; +update altadv_vars set spell_type=4 where skill_id =510; +update altadv_vars set spell_type=4 where skill_id =513; +update altadv_vars set spell_type=5 where skill_id =516; +update altadv_vars set spell_type=1 where skill_id =517; +update altadv_vars set spell_type=6 where skill_id =520; +update altadv_vars set spell_type=9 where skill_id =523; +update altadv_vars set spell_type=0 where skill_id =526; +update altadv_vars set spell_type=5 where skill_id =528; +update altadv_vars set spell_type=4 where skill_id =531; +update altadv_vars set spell_type=8 where skill_id =533; +update altadv_vars set spell_type=4 where skill_id =534; +update altadv_vars set spell_type=3 where skill_id =545; +update altadv_vars set spell_type=5 where skill_id =548; +update altadv_vars set spell_type=2 where skill_id =592; +update altadv_vars set spell_type=7 where skill_id =616; +update altadv_vars set spell_type=6 where skill_id =619; +update altadv_vars set spell_type=7 where skill_id =630; +update altadv_vars set spell_type=0 where skill_id =643; +update altadv_vars set spell_type=4 where skill_id =645; +update altadv_vars set spell_type=7 where skill_id =718; +update altadv_vars set spell_type=6 where skill_id =721; +update altadv_vars set spell_type=6 where skill_id =723; +update altadv_vars set spell_type=8 where skill_id =921; +update altadv_vars set spell_type=8 where skill_id =922; +update altadv_vars set spell_type=8 where skill_id =923; +update altadv_vars set spell_type=12 where skill_id =926; +update altadv_vars set spell_type=13 where skill_id =931; +update altadv_vars set spell_type=20 where skill_id =1000; +update altadv_vars set spell_type=8 where skill_id =1119; +update altadv_vars set spell_type=5 where skill_id =1123; +update altadv_vars set spell_type=2 where skill_id =1126; +update altadv_vars set spell_type=10 where skill_id =1229; +update altadv_vars set spell_type=11 where skill_id =1334; +update altadv_vars set spell_type=13 where skill_id =1337; +update altadv_vars set spell_type=8 where skill_id =1340; +update altadv_vars set spell_type=8 where skill_id =1341; +update altadv_vars set spell_type=8 where skill_id =1342; +update altadv_vars set spell_type=9 where skill_id =1343; +update altadv_vars set spell_type=8 where skill_id =1344; +update altadv_vars set spell_type=2 where skill_id =828; +update altadv_vars set spell_type=10 where skill_id =831; +update altadv_vars set spell_type=5 where skill_id =872; +update altadv_vars set spell_type=5 where skill_id =875; +update altadv_vars set spell_type=5 where skill_id =860; +update altadv_vars set spell_type=6 where skill_id =863; +update altadv_vars set spell_type=5 where skill_id =900; +update altadv_vars set spell_type=10 where skill_id =746; +update altadv_vars set spell_type=13 where skill_id =749; +update altadv_vars set spell_type=6 where skill_id =757; +update altadv_vars set spell_type=5 where skill_id =1149; +update altadv_vars set spell_type=2 where skill_id =1150; +update altadv_vars set spell_type=5 where skill_id =773; +update altadv_vars set spell_type=8 where skill_id =785; +update altadv_vars set spell_type=2 where skill_id =702; +update altadv_vars set spell_type=12 where skill_id =209; diff --git a/utils/sql/svn/475_aa_actions.sql b/utils/sql/svn/475_aa_actions.sql new file mode 100644 index 000000000..cefc9d11f --- /dev/null +++ b/utils/sql/svn/475_aa_actions.sql @@ -0,0 +1,4 @@ +ALTER TABLE `aa_actions` ADD `redux_aa2` MEDIUMINT( 8 ) UNSIGNED NOT NULL DEFAULT '0', +ADD `redux_rate2` TINYINT( 4 ) NOT NULL DEFAULT '0'; +UPDATE `aa_actions` SET `redux_aa2` = '886', +`redux_rate2` = '10' WHERE `aa_actions`.`aaid` =243 AND `aa_actions`.`rank` =0 LIMIT 1 ; diff --git a/utils/sql/svn/500_spawn2_optimization.sql b/utils/sql/svn/500_spawn2_optimization.sql new file mode 100644 index 000000000..754afcc01 --- /dev/null +++ b/utils/sql/svn/500_spawn2_optimization.sql @@ -0,0 +1 @@ +ALTER TABLE `spawn2` DROP INDEX `ZoneGroup`, ADD INDEX `ZoneGroup` (`zone`) \ No newline at end of file diff --git a/utils/sql/svn/503_bugs.sql b/utils/sql/svn/503_bugs.sql new file mode 100644 index 000000000..84ccb53d7 --- /dev/null +++ b/utils/sql/svn/503_bugs.sql @@ -0,0 +1,17 @@ +DROP TABLE IF EXISTS `bugs`; +CREATE TABLE `bugs` ( + `id` int(11) unsigned NOT NULL auto_increment, + `zone` varchar(32) NOT NULL, + `name` varchar(64) NOT NULL, + `ui` varchar(128) NOT NULL, + `x` float NOT NULL default '0', + `y` float NOT NULL default '0', + `z` float NOT NULL default '0', + `type` varchar(64) NOT NULL, + `flag` tinyint(3) unsigned NOT NULL, + `target` varchar(64) default NULL, + `bug` varchar(1024) NOT NULL, + `date` date NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/utils/sql/svn/518_drakkin_npc_type_features.sql b/utils/sql/svn/518_drakkin_npc_type_features.sql new file mode 100644 index 000000000..f928d8547 --- /dev/null +++ b/utils/sql/svn/518_drakkin_npc_type_features.sql @@ -0,0 +1,3 @@ +ALTER TABLE `npc_types` ADD `drakkin_heritage` int(10) NOT NULL default '0' AFTER `luclin_beard`; +ALTER TABLE `npc_types` ADD `drakkin_tattoo` int(10) NOT NULL default '0' AFTER `drakkin_heritage`; +ALTER TABLE `npc_types` ADD `drakkin_details` int(10) NOT NULL default '0' AFTER `drakkin_tattoo`; \ No newline at end of file diff --git a/utils/sql/svn/524_rule_values_notes.sql b/utils/sql/svn/524_rule_values_notes.sql new file mode 100644 index 000000000..65a4326d4 --- /dev/null +++ b/utils/sql/svn/524_rule_values_notes.sql @@ -0,0 +1,208 @@ +ALTER TABLE `rule_values` ADD `notes` text NOT NULL AFTER `rule_value`; + + +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:MaxLevel'; +UPDATE `rule_values` SET notes = 'Sets the Max Level attainable via Experience' WHERE rule_name = 'Character:MaxExpLevel'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:DeathExpLossLevel'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:DeathItemLossLevel'; +UPDATE `rule_values` SET notes = 'Adjust how much exp is lost' WHERE rule_name = 'Character:DeathExpLossMultiplier'; +UPDATE `rule_values` SET notes = 'Adjust to use the above multiplier or to use code default.' WHERE rule_name = 'Character:UseDeathExpLossMult'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:CorpseDecayTimeMS'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:LeaveCorpses'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:LeaveNakedCorpses'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:ExpMultiplier'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:AAExpMultiplier'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:GroupExpMultiplier'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:RaidExpMultiplier'; +UPDATE `rule_values` SET notes = '0=disabled' WHERE rule_name = 'Character:AutosaveIntervalS'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:HPRegenMultiplier'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:ManaRegenMultiplier'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:EnduranceRegenMultiplier'; +UPDATE `rule_values` SET notes = 'item\'s hunger restored = this value * item\'s food level:100 = normal:50 = people eat 2x as fast:200 = people eat 2x as slow' WHERE rule_name = 'Character:ConsumptionMultiplier'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:HealOnLevel'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:FeignKillsPet'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:ItemManaRegenCap'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:ItemHealthRegenCap'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:ItemDamageShieldCap'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:ItemAccuracyCap'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:ItemAvoidanceCap'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:ItemCombatEffectsCap'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:ItemShieldingCap'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:ItemSpellShieldingCap'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:ItemDoTShieldingCap'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:ItemStunResistCap'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:ItemStrikethroughCap'; +UPDATE `rule_values` SET notes = 'skill ups are at 100%' WHERE rule_name = 'Character:SkillUpModifier'; +UPDATE `rule_values` SET notes = 'off by default to prevent duping for now' WHERE rule_name = 'Character:SharedBankPlat'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Character:BindAnywhere'; +UPDATE `rule_values` SET notes = 'Set to >0 to enable rest state bonus HP and mana regen.' WHERE rule_name = 'Character:RestRegenPercent'; +UPDATE `rule_values` SET notes = 'Time in seconds for rest state regen to kick in.' WHERE rule_name = 'Character:RestRegenTimeToActivate'; + +UPDATE `rule_values` SET notes = 'Max number of members allowed in a single guild' WHERE rule_name = 'Guild:MaxMembers'; +UPDATE `rule_values` SET notes = 'Highest skill level that tradeskills can be trained to from GM Trainers' WHERE rule_name = 'Skills:MaxTrainTradeskills'; +UPDATE `rule_values` SET notes = 'Range at which a pet will respond to attack commands' WHERE rule_name = 'Pets:AttackCommandRange'; +UPDATE `rule_values` SET notes = 'This setting overrides the minstatus setting in the zones table if set' WHERE rule_name = 'GM:MinStatusToZoneAnywhere'; + +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'World:ZoneAutobootTimeoutMS'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'World:ClientKeepaliveTimeoutMS'; +UPDATE `rule_values` SET notes = 'Toggle whether or not to check incoming client connections against the Banned_IPs table. Set this value to false to disable this feature.' WHERE rule_name = 'World:UseBannedIPsTable'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'World:EnableTutorialButton'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'World:EnableReturnHomeButton'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'World:MaxLevelForTutorial'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'World:TutorialZoneID'; +UPDATE `rule_values` SET notes = '21600 seconds is 6 Hours' WHERE rule_name = 'World:MinOfflineTimeToReturnHome'; +UPDATE `rule_values` SET notes = 'Maximum number of clients allowed to connect per IP address if account status is < AddMaxClientsStatus. Default value: -1 (feature disabled)' WHERE rule_name = 'World:MaxClientsPerIP'; +UPDATE `rule_values` SET notes = 'Exempt accounts from the MaxClientsPerIP and AddMaxClientsStatus rules:if their status is >= this value. Default value: -1 (feature disabled)' WHERE rule_name = 'World:ExemptMaxClientsStatus'; +UPDATE `rule_values` SET notes = 'Maximum number of clients allowed to connect per IP address if account status is < ExemptMaxClientsStatus. Default value: -1 (feature disabled)' WHERE rule_name = 'World:AddMaxClientsPerIP'; +UPDATE `rule_values` SET notes = 'Accounts with status >= this rule will be allowed to use the amount of accounts defined in the AddMaxClientsPerIP. Default value: -1 (feature disabled)' WHERE rule_name = 'World:AddMaxClientsStatus'; +UPDATE `rule_values` SET notes = 'cavedude: Clears temp merchant items when world boots.' WHERE rule_name = 'World:ClearTempMerchantlist'; +UPDATE `rule_values` SET notes = 'Max number of characters allowed on at once from a single account (-1 is disabled)' WHERE rule_name = 'World:AccountSessionLimit'; +UPDATE `rule_values` SET notes = 'Min status required to be exempt from multi-session per account limiting (-1 is disabled)' WHERE rule_name = 'World:ExemptAccountLimitStatus'; +UPDATE `rule_values` SET notes = 'Check ip list against GM Accounts:AntiHack GM Accounts.' WHERE rule_name = 'World:GMAccountIPList'; +UPDATE `rule_values` SET notes = 'Minimum GM status to check against AntiHack list' WHERE rule_name = 'World:MinGMAntiHackStatus'; +UPDATE `rule_values` SET notes = 'Sets the Starting Zone for SoF Clients separate from Titanium Clients (-1 is disabled)' WHERE rule_name = 'World:SoFStartZoneID'; + +UPDATE `rule_values` SET notes = 'ms between intervals of sending a position update to the entire zone.' WHERE rule_name = 'Zone: NPCGlobalPositionUpdateInterval'; +UPDATE `rule_values` SET notes = 'the time a client remains link dead on the server after a sudden disconnection' WHERE rule_name = 'Zone: ClientLinkdeadMS'; +UPDATE `rule_values` SET notes = 'ms time until a player corpse is moved to a zone\'s graveyard:if one is specified for the zone' WHERE rule_name = 'Zone: GraveyardTimeMS'; +UPDATE `rule_values` SET notes = ' enables or disables the shadowrest zone feature for player corpses. Default is turned off.' WHERE rule_name = 'Zone:EnableShadowrest'; +UPDATE `rule_values` SET notes = 'Required status level to exempt the MQWarpDetector. Set to -1 to disable this feature.' WHERE rule_name = 'Zone:MQWarpExemptStatus'; +UPDATE `rule_values` SET notes = 'Required status level to exempt the MQZoneDetector. Set to -1 to disable this feature.' WHERE rule_name = 'Zone:MQZoneExemptStatus'; +UPDATE `rule_values` SET notes = 'Required status level to exempt the MQGateDetector. Set to -1 to disable this feature.' WHERE rule_name = 'Zone:MQGateExemptStatus'; +UPDATE `rule_values` SET notes = 'Required status level to exempt the MGhostDetector. Set to -1 to disable this feature.' WHERE rule_name = 'Zone:MQGhostExemptStatus'; +UPDATE `rule_values` SET notes = 'Enable the MQWarp Detector. Set to False to disable this feature.' WHERE rule_name = 'Zone:EnableMQWarpDetector'; +UPDATE `rule_values` SET notes = 'Enable the MQZone Detector. Set to False to disable this feature.' WHERE rule_name = 'Zone:EnableMQZoneDetector'; +UPDATE `rule_values` SET notes = 'Enable the MQGate Detector. Set to False to disable this feature.' WHERE rule_name = 'Zone:EnableMQGateDetector'; +UPDATE `rule_values` SET notes = 'Enable the MQGhost Detector. Set to False to disable this feature.' WHERE rule_name = 'Zone:EnableMQGhostDetector'; +UPDATE `rule_values` SET notes = 'Distance a player must travel between client to server location updates before a warp is registered. 30 allows for beyond GM speed without lag.' WHERE rule_name = 'Zone:MQWarpDetectorDistance'; +UPDATE `rule_values` SET notes = 'Distance beyond the Zone:MQWarpDetectorDistance that a player must travel within the MQWarpThresholdTimer amount of time before tripping the MQWarp detector. Set to 0 to disable this feature.' WHERE rule_name = 'Zone:MQWarpLagThreshold'; +UPDATE `rule_values` SET notes = 'Amount of time before the warp_threshold resets to the Zone:MQWarpLagThreshold value. Default: 90000 (900 seconds/15 minutes). Set to -1 to disable this feature.' WHERE rule_name = 'Zone:MQWarpThresholdTimer'; +UPDATE `rule_values` SET notes = 'How long a dynamic zone stays loaded while empty' WHERE rule_name = 'Zone:AutoShutdownDelay'; + +UPDATE `rule_values` SET notes = 'increases zone boot times a bit to reduce hopping.' WHERE rule_name = 'Map:FixPathingZWhenLoading'; +UPDATE `rule_values` SET notes = 'alternative to `WhenLoading`:accomplishes the same thing but does it at each waypoint instead of once at boot time.' WHERE rule_name = 'Map:FixPathingZAtWaypoints'; +UPDATE `rule_values` SET notes = 'very CPU intensive:but helps hopping with widely spaced waypoints.' WHERE rule_name = 'Map:FixPathingZWhenMoving'; +UPDATE `rule_values` SET notes = 'try to repair Z coords in the SendTo routine as well.' WHERE rule_name = 'Map:FixPathingZOnSendTo'; +UPDATE `rule_values` SET notes = 'at runtime while pathing: max change in Z to allow the BestZ code to apply.' WHERE rule_name = 'Map:FixPathingZMaxDeltaMoving'; +UPDATE `rule_values` SET notes = 'at runtime at each waypoint: max change in Z to allow the BestZ code to apply.' WHERE rule_name = 'Map:FixPathingZMaxDeltaWaypoint'; +UPDATE `rule_values` SET notes = 'at runtime in SendTo: max change in Z to allow the BestZ code to apply.' WHERE rule_name = 'Map:FixPathingZMaxDeltaSendTo'; +UPDATE `rule_values` SET notes = 'while loading each waypoint: max change in Z to allow the BestZ code to apply.' WHERE rule_name = 'Map:FixPathingZMaxDeltaLoading'; + +UPDATE `rule_values` SET notes = 'Does not apply BestZ as waypoints are loaded if they are in water' WHERE rule_name = 'Watermap:CheckWaypointsInWaterWhenLoading'; +UPDATE `rule_values` SET notes = 'Check if a mob has moved into/out of water when at waypoints and sets flymode' WHERE rule_name = 'Watermap:CheckForWaterAtWaypoints'; +UPDATE `rule_values` SET notes = 'Checks if a mob has moved into/out of water each time it\'s loc is recalculated' WHERE rule_name = 'Watermap:CheckForWaterWhenMoving'; +UPDATE `rule_values` SET notes = 'Checks if a mob has moved into/out of water on SendTo' WHERE rule_name = 'Watermap:CheckForWaterOnSendTo'; +UPDATE `rule_values` SET notes = 'Only lets a player fish near water (if a water map exists for the zone)' WHERE rule_name = 'Watermap:CheckForWaterWhenFishing'; +UPDATE `rule_values` SET notes = 'How far in front of player water must be for fishing to work' WHERE rule_name = 'Watermap:FishingRodLength'; +UPDATE `rule_values` SET notes = 'If water is more than this far below the player:it is considered too far to fish' WHERE rule_name = 'Watermap:FishingLineLength'; + +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Spells:AutoResistDiff'; +UPDATE `rule_values` SET notes = 'chance to resist given no resists and same level' WHERE rule_name = 'Spells:ResistChance'; +UPDATE `rule_values` SET notes = 'multiplier:chance to resist = this * ResistAmount' WHERE rule_name = 'Spells:ResistMod'; +UPDATE `rule_values` SET notes = 'The chance when a spell is resisted that it will partial hit.' WHERE rule_name = 'Spells:PartialHitChance'; +UPDATE `rule_values` SET notes = 'The chance when a fear spell is resisted that it will partial hit.' WHERE rule_name = 'Spells:PartialHitChanceFear'; +UPDATE `rule_values` SET notes = 'base % chance that everyone has to crit a spell' WHERE rule_name = 'Spells:BaseCritChance'; +UPDATE `rule_values` SET notes = 'base % bonus to damage on a successful spell crit. 100 = 2x damage' WHERE rule_name = 'Spells:BaseCritRatio'; +UPDATE `rule_values` SET notes = 'level wizards first get spell crits' WHERE rule_name = 'Spells:WizCritLevel'; +UPDATE `rule_values` SET notes = 'wiz\'s crit chance:on top of BaseCritChance' WHERE rule_name = 'Spells:WizCritChance'; +UPDATE `rule_values` SET notes = 'wiz\'s crit bonus:on top of BaseCritRatio (should be 0 for Live-like)' WHERE rule_name = 'Spells:WizCritRatio'; +UPDATE `rule_values` SET notes = '8.5 resist per level difference.' WHERE rule_name = 'Spells:ResistPerLevelDiff'; +UPDATE `rule_values` SET notes = 'If not zero:time in seconds to accept a Translocate.' WHERE rule_name = 'Spells:TranslocateTimeLimit'; + +UPDATE `rule_values` SET notes = 'The base crit chance for non warriors:NOTE: This will apply to NPCs as well' WHERE rule_name = 'Combat:BaseCritChance'; +UPDATE `rule_values` SET notes = 'The base crit chance for warriors and berserkers:only applies to clients' WHERE rule_name = 'Combat:WarBerBaseCritChance'; +UPDATE `rule_values` SET notes = 'The bonus base crit chance you get when you\'re berserk' WHERE rule_name = 'Combat:BerserkBaseCritChance'; +UPDATE `rule_values` SET notes = 'The level that npcs can KICK/BASH' WHERE rule_name = 'Combat:NPCBashKickLevel'; +UPDATE `rule_values` SET notes = 'The base crit chance for all clients:this will stack with warrior\'s/zerker\'s crit chance.' WHERE rule_name = 'Combat:ClientBaseCritChance'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Combat:UseIntervalAC'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Combat:PetAttackMagicLevel'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Combat:EnableFearPathing'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Combat:FleeHPRatio'; +UPDATE `rule_values` SET notes = 'If false:mobs won\'t flee if other mobs are in combat with it.' WHERE rule_name = 'Combat:FleeIfNotAlone'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Combat:AdjustProcPerMinute'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Combat:AvgProcsPerMinute'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Combat:ProcPerMinDexContrib'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Combat:BaseProcChance'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Combat:ProcDexDivideBy'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Combat:BaseHitChance'; +UPDATE `rule_values` SET notes = 'hit will fall off up to 5% over the initial level range' WHERE rule_name = 'Combat:HitFalloffMinor'; +UPDATE `rule_values` SET notes = 'hit will fall off up to 7% over the three levels after the initial level range' WHERE rule_name = 'Combat:HitFalloffModerate'; +UPDATE `rule_values` SET notes = 'hit will fall off sharply if we\'re outside the minor and moderate range' WHERE rule_name = 'Combat:HitFalloffMajor'; +UPDATE `rule_values` SET notes = 'You gain this % of hit for every level you are above your target' WHERE rule_name = 'Combat:HitBonusPerLevel'; +UPDATE `rule_values` SET notes = 'For every weapon skill point that\'s not maxed you lose this % of hit' WHERE rule_name = 'Combat:WeaponSkillFalloff'; +UPDATE `rule_values` SET notes = 'Archery has a hit penalty to try to help balance it with the plethora of long term +hit modifiers for it' WHERE rule_name = 'Combat:ArcheryHitPenalty'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Combat:AgiHitFactor'; +UPDATE `rule_values` SET notes = 'Minimum Distance to use Ranged Attacks ' WHERE rule_name = 'Combat:MinRangedAttackDist'; + +UPDATE `rule_values` SET notes = 'level<55' WHERE rule_name = 'NPC:MinorNPCCorpseDecayTimeMS'; +UPDATE `rule_values` SET notes = 'level>=55' WHERE rule_name = 'NPC:MajorNPCCorpseDecayTimeMS'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'NPC:CorpseUnlockTimer'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'NPC:EmptyNPCCorpseDecayTimeMS'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'NPC:UseItemBonusesForNonPets'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'NPC:SayPauseTimeInSec'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'NPC:OOCRegen'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'NPC:BuffFriends'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'NPC:EnableNPCQuestJournal'; + +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Aggro:SmartAggroList'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Aggro:SittingAggroMod'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Aggro:MeleeRangeAggroMod'; +UPDATE `rule_values` SET notes = '0 will prefer our current target to any other, > 0 makes it harder for our npcs to switch targets.' WHERE rule_name = 'Aggro:CurrentTargetAggroMod'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Aggro:CriticallyWoundedAggroMod'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Aggro:SlowAggroMod'; +UPDATE `rule_values` SET notes = 'mez:blind:charm etc etc' WHERE rule_name = 'Aggro:IncapacitateAggroMod'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Aggro:MovementImpairAggroMod'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Aggro:SpellAggroMod'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Aggro:SongAggroMod'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Aggro:PetSpellAggroMod'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Aggro:StunAggroMod'; + +UPDATE `rule_values` SET notes = 'Globally enable or disable the Task system' WHERE rule_name = 'TaskSystem:EnableTaskSystem'; +UPDATE `rule_values` SET notes = 'Seconds between checks for failed tasks. Also used by the \'Touch\' activity' WHERE rule_name = 'TaskSystem:PeriodicCheckTimer'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'TaskSystem:RecordCompletedTasks'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'TaskSystem:RecordCompletedOptionalActivities'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'TaskSystem:KeepOneRecordPerCompletedTask'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'TaskSystem:EnableTaskProximity'; + +UPDATE `rule_values` SET notes = 'Adjust mana regen for bots:1 is fast and higher numbers slow it down 3 is about the same as players.' WHERE rule_name = 'EQOffline:BotManaRegen'; +UPDATE `rule_values` SET notes = 'Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat.' WHERE rule_name = 'EQOffline:BotFinishBuffing'; +UPDATE `rule_values` SET notes = 'Number of bots that each account can create' WHERE rule_name = 'EQOffline:CreateBotCount'; +UPDATE `rule_values` SET notes = 'Number of bots a character can have spawned at one time:You + 71 bots is a 12 group raid' WHERE rule_name = 'EQOffline:SpawnBotCount'; +UPDATE `rule_values` SET notes = 'Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit:see: /bazaar/Aediles_Thrall.pl' WHERE rule_name = 'EQOffline:BotQuest'; + +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Chat:ServerWideOOC'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Chat:ServerWideAuction'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Chat:EnableVoiceMacros'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Chat:EnableMailKeyIPVerification'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Chat:EnableAntiSpam'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Chat:MinStatusToBypassAntiSpam'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Chat:MinimumMessagesPerInterval'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Chat:MaximumMessagesPerInterval'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Chat:MaxMessagesBeforeKick'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Chat:IntervalDurationMS'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Chat:KarmaUpdateIntervalMS'; + +UPDATE `rule_values` SET notes = 'Use faction/charisma price modifiers.' WHERE rule_name = 'Merchant:UsePriceMod'; +UPDATE `rule_values` SET notes = 'Modifier for NPC sell price.' WHERE rule_name = 'Merchant:SellCostMod'; +UPDATE `rule_values` SET notes = 'Modifier for NPC buy price.' WHERE rule_name = 'Merchant:BuyCostMod'; +UPDATE `rule_values` SET notes = 'Determines maximum price bonus from having good faction/CHA. Value is a percent.' WHERE rule_name = 'Merchant:PriceBonusPct'; +UPDATE `rule_values` SET notes = 'Determines maximum price penalty from having bad faction/CHA. Value is a percent.' WHERE rule_name = 'Merchant:PricePenaltyPct'; +UPDATE `rule_values` SET notes = 'Determines CHA cap:from 104 CHA. 3.45 is 132 CHA at apprehensive. 0.34 is 400 CHA at apprehensive.' WHERE rule_name = 'Merchant:ChaBonusMod'; +UPDATE `rule_values` SET notes = 'Determines CHA bottom:up to 102 CHA. 1.52 is 37 CHA at apprehensive. 0.98 is 0 CHA at apprehensive.' WHERE rule_name = 'Merchant:ChaPenaltyMod'; + +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Bazaar:AuditTrail'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Bazaar:MaxSearchResults'; +UPDATE `rule_values` SET notes = 'notation' WHERE rule_name = 'Bazaar:EnableWarpToTrader'; +UPDATE `rule_values` SET notes = 'The max results returned in the /barter search' WHERE rule_name = 'Bazaar:MaxBarterSearchResults'; + +UPDATE `rule_values` SET notes = 'If false:client wont bring up the Mail window.' WHERE rule_name = 'Mail:EnableMailSystem'; +UPDATE `rule_values` SET notes = 'Time in seconds. 0 will delete all messages in the trash when the mailserver starts' WHERE rule_name = 'Mail:ExpireTrash'; +UPDATE `rule_values` SET notes = '1 Year. Set to -1 for never' WHERE rule_name = 'Mail:ExpireRead'; +UPDATE `rule_values` SET notes = '1 Year. Set to -1 for never' WHERE rule_name = 'Mail:ExpireUnread'; + +UPDATE `rule_values` SET notes = 'Required status to administer chat channels' WHERE rule_name = 'Channels:RequiredStatusAdmin'; +UPDATE `rule_values` SET notes = 'Required status to list all chat channels' WHERE rule_name = 'Channels:RequiredStatusListAll'; +UPDATE `rule_values` SET notes = 'Empty password protected channels will be deleted after this many minutes' WHERE rule_name = 'Channels:DeleteTimer'; + +UPDATE `rule_values` SET notes = 'Record sales from a player to an NPC merchant in eventlog table' WHERE rule_name = 'EventLog:RecordSellToMerchant'; +UPDATE `rule_values` SET notes = 'Record purchases by a player from an NPC merchant in eventlog table' WHERE rule_name = 'EventLog:RecordBuyFromMerchant'; diff --git a/utils/sql/svn/527_npc_armor_tint.sql b/utils/sql/svn/527_npc_armor_tint.sql new file mode 100644 index 000000000..881d346f2 --- /dev/null +++ b/utils/sql/svn/527_npc_armor_tint.sql @@ -0,0 +1,3 @@ +ALTER TABLE `npc_types` ADD COLUMN `armortint_red` TINYINT UNSIGNED NOT NULL DEFAULT 0 AFTER `drakkin_details`, +ADD COLUMN `armortint_green` TINYINT UNSIGNED NOT NULL DEFAULT 0 AFTER `armortint_red`, +ADD COLUMN `armortint_blue` TINYINT UNSIGNED NOT NULL DEFAULT 0 AFTER `armortint_green`; \ No newline at end of file diff --git a/utils/sql/svn/553_saylink_table.sql b/utils/sql/svn/553_saylink_table.sql new file mode 100644 index 000000000..add2c7e21 --- /dev/null +++ b/utils/sql/svn/553_saylink_table.sql @@ -0,0 +1,6 @@ +DROP TABLE IF EXISTS `saylink`; +CREATE TABLE `saylink` ( + `id` int(10) NOT NULL auto_increment, + `phrase` varchar(64) NOT NULL default '', + PRIMARY KEY (`id`) +) ENGINE=MyISAM AUTO_INCREMENT=10 DEFAULT CHARSET=latin1; \ No newline at end of file diff --git a/utils/sql/svn/55_zone_shutdowndeleay.sql b/utils/sql/svn/55_zone_shutdowndeleay.sql new file mode 100644 index 000000000..752e806a4 --- /dev/null +++ b/utils/sql/svn/55_zone_shutdowndeleay.sql @@ -0,0 +1,2 @@ +ALTER TABLE `zone` ADD column `shutdowndelay` bigint (16) unsigned NOT NULL default '5000'; +INSERT INTO rule_values VALUES (1,'Zone:AutoShutdownDelay', 5000); diff --git a/utils/sql/svn/564_nokeyring.sql b/utils/sql/svn/564_nokeyring.sql new file mode 100644 index 000000000..c17b5130d --- /dev/null +++ b/utils/sql/svn/564_nokeyring.sql @@ -0,0 +1 @@ +ALTER TABLE `doors` ADD COLUMN `nokeyring` TINYINT UNSIGNED NOT NULL DEFAULT 0 AFTER `keyitem`; diff --git a/utils/sql/svn/600_group_leadership.sql b/utils/sql/svn/600_group_leadership.sql new file mode 100644 index 000000000..551e78142 --- /dev/null +++ b/utils/sql/svn/600_group_leadership.sql @@ -0,0 +1,6 @@ +ALTER TABLE `group_leaders` ADD `assist` VARCHAR( 64 ) NOT NULL , +ADD `marknpc` VARCHAR( 64 ) NOT NULL DEFAULT '', +ADD `leadershipaa` TINYBLOB NOT NULL DEFAULT ''; + +INSERT INTO `rule_values` VALUES (1,'Character:KillsPerRaidLeadershipAA','50',''); +INSERT INTO `rule_values` VALUES (1,'Character:KillsPerGroupLeadershipAA','50',''); diff --git a/utils/sql/svn/612_instance_changes.sql b/utils/sql/svn/612_instance_changes.sql new file mode 100644 index 000000000..3bff88208 --- /dev/null +++ b/utils/sql/svn/612_instance_changes.sql @@ -0,0 +1,120 @@ +CREATE TABLE `instance_lockout` ( + `id` int(11) NOT NULL auto_increment, + `zone` int(11) unsigned NOT NULL default '0', + `version` tinyint(4) unsigned NOT NULL default '0', + `start_time` int(11) unsigned NOT NULL default '0', + `duration` int(11) unsigned NOT NULL default '0', + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`), + KEY `id_2` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `instance_lockout_player` ( + `id` int(11) unsigned NOT NULL default '0', + `charid` int(11) unsigned NOT NULL default '0', + PRIMARY KEY (`charid`,`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +ALTER TABLE `respawn_times` ADD `instance_id` SMALLINT DEFAULT '0' NOT NULL AFTER `duration`; +ALTER TABLE `respawn_times` DROP PRIMARY KEY, ADD PRIMARY KEY (`id`, `instance_id`); + +ALTER TABLE `character_` ADD `instanceid` SMALLINT UNSIGNED DEFAULT '0' NOT NULL AFTER `zoneid`; +ALTER TABLE `character_` DROP `instZflagNum`; +ALTER TABLE `character_` DROP `instZOrgID`; + +ALTER TABLE `spawn2` ADD `version` SMALLINT UNSIGNED DEFAULT '0' NOT NULL AFTER `zone`; + +ALTER TABLE `player_corpses` ADD `instanceid` SMALLINT UNSIGNED DEFAULT '0' NOT NULL AFTER `zoneid`; +ALTER TABLE `player_corpses` ADD INDEX `instanceid` (`instanceid`); + +ALTER TABLE `traps` ADD INDEX `zone` (`zone`); +ALTER TABLE `traps` ADD `version` SMALLINT UNSIGNED DEFAULT '0' NOT NULL AFTER `zone`; + +ALTER TABLE `ground_spawns` ADD INDEX `zone` (`zoneid`); +ALTER TABLE `ground_spawns` ADD `version` SMALLINT UNSIGNED DEFAULT '0' NOT NULL AFTER `zoneid`; + +ALTER TABLE `object` ADD INDEX `zone` (`zoneid`); +ALTER TABLE `object` ADD `version` SMALLINT UNSIGNED DEFAULT '0' NOT NULL AFTER `zoneid`; +ALTER TABLE `object` DROP `linked_list_addr_01`; +ALTER TABLE `object` DROP `linked_list_addr_02`; +ALTER TABLE `object` DROP `unknown88`; +DELETE FROM object WHERE object.type=1 AND object.itemid!=0; + +ALTER TABLE `doors` ADD `version` SMALLINT UNSIGNED DEFAULT '0' NOT NULL AFTER `zone`; +ALTER TABLE `doors` ADD `is_ldon_door` TINYINT UNSIGNED DEFAULT '0' NOT NULL AFTER `dest_heading`; + +ALTER TABLE `npc_types` ADD `adventure_template_id` INT UNSIGNED DEFAULT '0' NOT NULL AFTER `npc_faction_id`; + +CREATE TABLE `adventure_template` ( + `id` int(10) unsigned NOT NULL, + `zone` varchar(64) NOT NULL, + `zone_version` tinyint(3) unsigned NOT NULL default '0', + `is_hard` tinyint(3) unsigned NOT NULL default '0', + `is_raid` tinyint(3) unsigned NOT NULL default '0', + `min_level` tinyint(3) unsigned NOT NULL default '1', + `max_level` tinyint(3) unsigned NOT NULL default '65', + `type` tinyint(3) unsigned NOT NULL default '0', + `type_data` int(10) unsigned NOT NULL default '0', + `type_count` smallint(5) unsigned NOT NULL default '0', + `text` varchar(512) NOT NULL, + `duration` int(10) unsigned NOT NULL default '7200', + `zone_in_time` int(10) unsigned NOT NULL default '1800', + `win_points` smallint(5) unsigned NOT NULL default '0', + `lose_points` smallint(5) unsigned NOT NULL default '0', + `theme` tinyint(3) unsigned NOT NULL default '1', + `zone_in_zone_id` smallint(5) unsigned NOT NULL default '0', + `zone_in_x` float NOT NULL default '0', + `zone_in_y` float NOT NULL default '0', + `zone_in_object_id` smallint(4) NOT NULL default '0', + `dest_x` float NOT NULL default '0', + `dest_y` float NOT NULL default '0', + `dest_z` float NOT NULL default '0', + `dest_h` float NOT NULL default '0', + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`), + KEY `id_2` (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +CREATE TABLE `adventure_template_entry` ( + `id` int(10) unsigned NOT NULL, + `template_id` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`,`template_id`), + KEY `id` (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +CREATE TABLE `adventure_details` ( + `id` int(10) unsigned NOT NULL auto_increment, + `adventure_id` smallint(5) unsigned NOT NULL, + `instance_id` int(11) NOT NULL default '-1', + `count` smallint(5) unsigned NOT NULL default '0', + `assassinate_count` smallint(5) unsigned NOT NULL default '0', + `status` tinyint(3) unsigned NOT NULL default '0', + `time_created` int(10) unsigned NOT NULL default '0', + `time_zoned` int(10) unsigned NOT NULL default '0', + `time_completed` int(10) unsigned NOT NULL default '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `adventure_members` ( + `id` int(10) unsigned NOT NULL, + `charid` int(10) unsigned NOT NULL, + PRIMARY KEY (`charid`), + KEY `id` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `adventure_stats` ( + `player_id` int(10) unsigned NOT NULL, + `guk_wins` mediumint(8) unsigned NOT NULL default '0', + `mir_wins` mediumint(8) unsigned NOT NULL default '0', + `mmc_wins` mediumint(8) unsigned NOT NULL default '0', + `ruj_wins` mediumint(8) unsigned NOT NULL default '0', + `tak_wins` mediumint(8) unsigned NOT NULL default '0', + `guk_losses` mediumint(8) unsigned NOT NULL default '0', + `mir_losses` mediumint(8) unsigned NOT NULL default '0', + `mmc_losses` mediumint(8) unsigned NOT NULL default '0', + `ruj_losses` mediumint(8) unsigned NOT NULL default '0', + `tak_losses` mediumint(8) unsigned NOT NULL default '0', + PRIMARY KEY (`player_id`), + UNIQUE KEY `player_id` (`player_id`), + KEY `player_id_2` (`player_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/utils/sql/svn/615_adventure_assassination.sql b/utils/sql/svn/615_adventure_assassination.sql new file mode 100644 index 000000000..ef2fa8ea3 --- /dev/null +++ b/utils/sql/svn/615_adventure_assassination.sql @@ -0,0 +1,4 @@ +ALTER TABLE `adventure_template` ADD `assa_x` FLOAT DEFAULT '0' NOT NULL AFTER `type_count`; +ALTER TABLE `adventure_template` ADD `assa_y` FLOAT DEFAULT '0' NOT NULL AFTER `assa_x`; +ALTER TABLE `adventure_template` ADD `assa_z` FLOAT DEFAULT '0' NOT NULL AFTER `assa_y`; +ALTER TABLE `adventure_template` ADD `assa_h` FLOAT DEFAULT '0' NOT NULL AFTER `assa_z`; \ No newline at end of file diff --git a/utils/sql/svn/619_Adventure_Recruiter_Flavor.sql b/utils/sql/svn/619_Adventure_Recruiter_Flavor.sql new file mode 100644 index 000000000..6d69cb1d8 --- /dev/null +++ b/utils/sql/svn/619_Adventure_Recruiter_Flavor.sql @@ -0,0 +1,7 @@ +CREATE TABLE `adventure_template_entry_flavor` ( + `id` int(10) unsigned NOT NULL, + `text` varchar(512) character set utf8 collate utf8_bin NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`), + KEY `id_2` (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/utils/sql/svn/621_LDoNTraps.sql b/utils/sql/svn/621_LDoNTraps.sql new file mode 100644 index 000000000..cbc166d35 --- /dev/null +++ b/utils/sql/svn/621_LDoNTraps.sql @@ -0,0 +1,18 @@ +ALTER TABLE `npc_types` ADD `trap_template` INT UNSIGNED NOT NULL AFTER `adventure_template_id`; + +CREATE TABLE `ldon_trap_templates` ( + `id` int(10) unsigned NOT NULL, + `type` tinyint(3) unsigned NOT NULL default '1', + `spell_id` smallint(5) unsigned NOT NULL default '0', + `skill` smallint(5) unsigned NOT NULL default '0', + `locked` tinyint(3) unsigned NOT NULL default '0', + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +CREATE TABLE `ldon_trap_entries` ( + `id` int(10) unsigned NOT NULL, + `trap_id` int(10) unsigned NOT NULL default '0', + PRIMARY KEY (`id`,`trap_id`), + KEY `id` (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/utils/sql/svn/633_ucs.sql b/utils/sql/svn/633_ucs.sql new file mode 100644 index 000000000..d10cc2856 --- /dev/null +++ b/utils/sql/svn/633_ucs.sql @@ -0,0 +1,6 @@ +CREATE TABLE `friends` ( + `charid` int(10) unsigned NOT NULL, + `type` tinyint(1) unsigned NOT NULL default '1' COMMENT '1 = Friend, 0 = Ignore', + `name` varchar(64) NOT NULL, + PRIMARY KEY (`charid`,`type`,`name`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; diff --git a/utils/sql/svn/634_TrapTemplateDefaultValue.sql b/utils/sql/svn/634_TrapTemplateDefaultValue.sql new file mode 100644 index 000000000..c77f4cc67 --- /dev/null +++ b/utils/sql/svn/634_TrapTemplateDefaultValue.sql @@ -0,0 +1 @@ +alter table npc_types modify trap_template INTEGER(10) UNSIGNED default 0; \ No newline at end of file diff --git a/utils/sql/svn/643_BotsTable.sql b/utils/sql/svn/643_BotsTable.sql new file mode 100644 index 000000000..99a831699 --- /dev/null +++ b/utils/sql/svn/643_BotsTable.sql @@ -0,0 +1,46 @@ +DROP TABLE IF EXISTS bots; +CREATE TABLE IF NOT EXISTS bots ( + BotID integer unsigned NOT NULL auto_increment, + BotOwnerAccountID integer unsigned NOT NULL, + BotInventoryID integer unsigned NOT NULL default 0, + BotSpellsID integer unsigned NOT NULL default 0, + Name varchar(64) NOT NULL, + LastName varchar(32) default NULL, + /*ExpPoints integer unsigned NOT NULL default 0, + AAPoints integer unsigned NOT NULL default 0,*/ + BotLevel tinyint(2) unsigned NOT NULL default 0, + Race smallint(5) unsigned not NULL default 0, + Class tinyint(2) unsigned NOT NULL default 0, + BodyType int(11) default NULL, + HitPoints int(11) NOT NULL default 0, + Gender tinyint(2) unsigned NOT NULL default 0, + Size float NOT NULL default 0, + HitPointsRegenRate int(11) unsigned NOT NULL default 0, + ManaRegenRate int(11) unsigned NOT NULL default 0, + Face int(10) unsigned NOT NULL default 1, + LuclinHairStyle int(10) unsigned NOT NULL default 1, + LuclinHairColor int(10) unsigned NOT NULL default 1, + LuclinEyeColor int(10) unsigned NOT NULL default 1, + LuclinEyeColor2 int(10) unsigned NOT NULL default 1, + LuclinBeardColor int(10) unsigned NOT NULL default 1, + LuclinBeard int(10) unsigned NOT NULL default 0, + DrakkinHeritage int(10) unsigned NOT NULL default 0, + DrakkinTattoo int(10) unsigned NOT NULL default 0, + DrakkinDetails int(10) unsigned NOT NULL default 0, + RunSpeed float 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, + AC smallint(5) NOT NULL default 0, + STR mediumint(8) unsigned NOT NULL default 75, + STA mediumint(8) unsigned NOT NULL default 75, + DEX mediumint(8) unsigned NOT NULL default 75, + AGI mediumint(8) unsigned NOT NULL default 75, + _INT mediumint(8) unsigned NOT NULL default 80, + WIS mediumint(8) unsigned NOT NULL default 75, + CHA mediumint(8) unsigned NOT NULL default 75, + ATK mediumint(9) NOT NULL default 0, + PRIMARY KEY (BotID) +) ENGINE=InnoDB; \ No newline at end of file diff --git a/utils/sql/svn/646_archery_penalty_rule.sql b/utils/sql/svn/646_archery_penalty_rule.sql new file mode 100644 index 000000000..4c4cf57db --- /dev/null +++ b/utils/sql/svn/646_archery_penalty_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('1', 'Combat:ArcheryStationaryPenalty', '1.0','Damage Penalty for moving or rooted targets. 1 = 50% penalty (Default), 2 = no penalty, 0 = 100% penalty'); diff --git a/utils/sql/svn/665_heroic_resists.sql b/utils/sql/svn/665_heroic_resists.sql new file mode 100644 index 000000000..ee0cc4c64 --- /dev/null +++ b/utils/sql/svn/665_heroic_resists.sql @@ -0,0 +1,7 @@ +ALTER TABLE `items` +ADD COLUMN `heroic_pr` smallint(6) NOT NULL default '0' AFTER `heroic_cha`, +ADD COLUMN `heroic_dr` smallint(6) NOT NULL default '0' AFTER `heroic_pr`, +ADD COLUMN `heroic_fr` smallint(6) NOT NULL default '0' AFTER `heroic_dr`, +ADD COLUMN `heroic_cr` smallint(6) NOT NULL default '0' AFTER `heroic_fr`, +ADD COLUMN `heroic_mr` smallint(6) NOT NULL default '0' AFTER `heroic_cr`, +ADD COLUMN `heroic_svcorrup` smallint(6) NOT NULL default '0' AFTER `heroic_mr`; \ No newline at end of file diff --git a/utils/sql/svn/667_titles.sql b/utils/sql/svn/667_titles.sql new file mode 100644 index 000000000..2ad76d116 --- /dev/null +++ b/utils/sql/svn/667_titles.sql @@ -0,0 +1,114 @@ +DROP TABLE IF EXISTS `titles`; +CREATE TABLE `titles` ( + `id` int(10) unsigned NOT NULL auto_increment, + `skill_id` tinyint(3) NOT NULL default '-1', + `min_skill_value` mediumint(8) NOT NULL default '-1', + `max_skill_value` mediumint(8) NOT NULL default '-1', + `min_aa_points` mediumint(8) NOT NULL default '-1', + `max_aa_points` mediumint(8) NOT NULL default '-1', + `class` tinyint(4) NOT NULL default '-1', + `gender` tinyint(1) NOT NULL default '-1' COMMENT '-1 = either, 0 = male, 1 = female', + `char_id` int(11) NOT NULL default '-1', + `status` int(11) NOT NULL default '-1', + `item_id` int(11) NOT NULL default '-1', + `prefix` varchar(32) NOT NULL default '', + `suffix` varchar(32) NOT NULL default '', + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=94 ; + +-- +-- Dumping data for table `titles` +-- + +INSERT INTO `titles` (`id`, `skill_id`, `min_skill_value`, `max_skill_value`, `min_aa_points`, `max_aa_points`, `class`, `gender`, `char_id`, `status`, `item_id`, `prefix`, `suffix`) VALUES +(3, -1, -1, -1, -1, -1, -1, -1, -1, 50, -1, 'Guide', ''), +(4, -1, -1, -1, 6, 17, -1, 0, -1, -1, -1, 'Baron', ''), +(5, -1, -1, -1, 6, 17, -1, 1, -1, -1, -1, 'Baroness', ''), +(6, -1, -1, -1, -1, -1, -1, -1, -1, 180, -1, 'Developer', 'of The Grand Creation'), +(7, -1, -1, -1, 18, 41, 1, -1, -1, -1, -1, 'Veteran', ''), +(8, -1, -1, -1, 18, 41, 8, -1, -1, -1, -1, 'Veteran', ''), +(9, -1, -1, -1, 18, 41, 15, -1, -1, -1, -1, 'Venerable', ''), +(10, -1, -1, -1, 18, 41, 16, -1, -1, -1, -1, 'Veteran', ''), +(11, -1, -1, -1, 18, 41, 2, -1, -1, -1, -1, 'Venerable', ''), +(12, -1, -1, -1, 18, 41, 6, -1, -1, -1, -1, 'Venerable', ''), +(13, -1, -1, -1, 18, 41, 14, 0, -1, -1, -1, 'Master', ''), +(14, -1, -1, -1, 18, 41, 14, 1, -1, -1, -1, 'Mistress', ''), +(15, -1, -1, -1, 18, 41, 13, 0, -1, -1, -1, 'Master', ''), +(16, -1, -1, -1, 18, 41, 13, 1, -1, -1, -1, 'Mistress', ''), +(17, -1, -1, -1, 18, 41, 7, 0, -1, -1, -1, 'Brother', ''), +(18, -1, -1, -1, 18, 41, 7, 1, -1, -1, -1, 'Sister', ''), +(19, -1, -1, -1, 18, 41, 11, -1, -1, -1, -1, 'Sage', ''), +(20, -1, -1, -1, 18, 41, 3, 0, -1, -1, -1, 'Sir', ''), +(21, -1, -1, -1, 18, 41, 3, 1, -1, -1, -1, 'Lady', ''), +(22, -1, -1, -1, 18, 41, 4, -1, -1, -1, -1, 'Veteran', ''), +(23, -1, -1, -1, 18, 41, 9, -1, -1, -1, -1, 'Veteran', ''), +(24, -1, -1, -1, 18, 41, 5, 0, -1, -1, -1, 'Sir', ''), +(25, -1, -1, -1, 18, 41, 5, 1, -1, -1, -1, 'Lady', ''), +(26, -1, -1, -1, 18, 41, 10, -1, -1, -1, -1, 'Venerable', ''), +(27, -1, -1, -1, 18, 41, 12, 0, -1, -1, -1, 'Master', ''), +(28, -1, -1, -1, 18, 41, 12, 1, -1, -1, -1, 'Mistress', ''), +(29, -1, -1, -1, 42, -1, 8, 0, -1, -1, -1, 'Impresario', ''), +(30, -1, -1, -1, 42, -1, 8, 1, -1, -1, -1, 'Muse', ''), +(31, -1, -1, -1, 42, -1, 15, -1, -1, -1, -1, 'Elder', ''), +(32, -1, -1, -1, 42, -1, 16, -1, -1, -1, -1, 'Savage', ''), +(33, -1, -1, -1, 42, -1, 2, -1, -1, -1, -1, 'Exarch', ''), +(34, -1, -1, -1, 42, -1, 6, -1, -1, -1, -1, 'Elder', ''), +(35, -1, -1, -1, 42, -1, 14, -1, -1, -1, -1, 'Sage', ''), +(36, -1, -1, -1, 42, -1, 13, -1, -1, -1, -1, 'Sage', ''), +(37, -1, -1, -1, 42, -1, 7, -1, -1, -1, -1, 'Sensei', ''), +(38, -1, -1, -1, 42, -1, 11, -1, -1, -1, -1, 'Lich', ''), +(39, -1, -1, -1, 42, -1, 3, 0, -1, -1, -1, 'Duke', ''), +(40, -1, -1, -1, 42, -1, 3, 1, -1, -1, -1, 'Duchess', ''), +(41, -1, -1, -1, 42, -1, 4, 0, -1, -1, -1, 'Hunter', ''), +(42, -1, -1, -1, 42, -1, 4, 1, -1, -1, -1, 'Huntress', ''), +(43, -1, -1, -1, 42, -1, 9, -1, -1, -1, -1, 'Marauder', ''), +(44, -1, -1, -1, 42, -1, 5, 0, -1, -1, -1, 'Duke', ''), +(45, -1, -1, -1, 42, -1, 5, 1, -1, -1, -1, 'Duchess', ''), +(46, -1, -1, -1, 42, -1, 10, -1, -1, -1, -1, 'Elder', ''), +(47, -1, -1, -1, 42, -1, 1, -1, -1, -1, -1, 'Marshall', ''), +(48, -1, -1, -1, 42, -1, 12, -1, -1, -1, -1, 'Sage', ''), +(49, 60, 100, 199, -1, -1, -1, -1, -1, -1, -1, 'Apprentice Chef', ''), +(50, 60, 200, 249, -1, -1, -1, -1, -1, -1, -1, 'Journeyman Chef', ''), +(51, 60, 250, 299, -1, -1, -1, -1, -1, -1, -1, 'Expert Chef', ''), +(52, 60, 300, -1, -1, -1, -1, -1, -1, -1, -1, 'Master Chef', ''), +(53, 65, 100, 199, -1, -1, -1, -1, -1, -1, -1, 'Apprentice Brewer', ''), +(54, 65, 200, 249, -1, -1, -1, -1, -1, -1, -1, 'Journeyman Brewer', ''), +(55, 65, 250, 299, -1, -1, -1, -1, -1, -1, -1, 'Expert Brewer', ''), +(56, 65, 300, -1, -1, -1, -1, -1, -1, -1, -1, 'Master Brewer', ''), +(57, 55, 100, 199, -1, -1, -1, -1, -1, -1, -1, 'Apprentice Fisherman', ''), +(58, 55, 200, 249, -1, -1, -1, -1, -1, -1, -1, 'Journeyman Fisherman', ''), +(59, 55, 250, 299, -1, -1, -1, -1, -1, -1, -1, 'Expert Fisherman', ''), +(60, 55, 300, -1, -1, -1, -1, -1, -1, -1, -1, 'Master Fisherman', ''), +(61, 64, 100, 199, -1, -1, -1, -1, -1, -1, -1, 'Apprentice Fletcher', ''), +(62, 64, 200, 249, -1, -1, -1, -1, -1, -1, -1, 'Journeyman Fletcher', ''), +(63, 64, 250, 299, -1, -1, -1, -1, -1, -1, -1, 'Expert Fletcher', ''), +(64, 64, 300, -1, -1, -1, -1, -1, -1, -1, -1, 'Master Fletcher', ''), +(65, 68, 100, 199, -1, -1, -1, -1, -1, -1, -1, 'Apprentice Jeweler', ''), +(66, 68, 200, 249, -1, -1, -1, -1, -1, -1, -1, 'Journeyman Jeweler', ''), +(67, 68, 250, 299, -1, -1, -1, -1, -1, -1, -1, 'Expert Jeweler', ''), +(68, 68, 300, -1, -1, -1, -1, -1, -1, -1, -1, 'Master Jeweler', ''), +(69, 69, 100, 199, -1, -1, -1, -1, -1, -1, -1, 'Apprentice Potter', ''), +(70, 69, 200, 249, -1, -1, -1, -1, -1, -1, -1, 'Journeyman Potter', ''), +(71, 69, 250, 299, -1, -1, -1, -1, -1, -1, -1, 'Expert Potter', ''), +(72, 69, 300, -1, -1, -1, -1, -1, -1, -1, -1, 'Master Potter', ''), +(73, 63, 100, 199, -1, -1, -1, -1, -1, -1, -1, 'Apprentice Smith', ''), +(74, 63, 200, 249, -1, -1, -1, -1, -1, -1, -1, 'Journeyman Smith', ''), +(75, 63, 250, 299, -1, -1, -1, -1, -1, -1, -1, 'Expert Smith', ''), +(76, 63, 300, -1, -1, -1, -1, -1, -1, -1, -1, 'Master Smith', ''), +(77, 59, 100, 199, -1, -1, 10, -1, -1, -1, -1, 'Apprentice Alchemist', ''), +(78, 59, 200, 249, -1, -1, 10, -1, -1, -1, -1, 'Journeyman Alchemist', ''), +(79, 59, 250, 299, -1, -1, 10, -1, -1, -1, -1, 'Expert Alchemist', ''), +(80, 59, 300, -1, -1, -1, 10, -1, -1, -1, -1, 'Master Alchemist', ''), +(81, 56, 100, 199, -1, -1, 9, -1, -1, -1, -1, 'Apprentice Poisoncrafter', ''), +(82, 56, 200, 249, -1, -1, 9, -1, -1, -1, -1, 'Journeyman Poisoncrafter', ''), +(83, 56, 250, 299, -1, -1, 9, -1, -1, -1, -1, 'Expert Poisoncrafter', ''), +(84, 56, 300, -1, -1, -1, 9, -1, -1, -1, -1, 'Master Poisoncrafter', ''), +(85, 58, 100, 199, -1, -1, -1, -1, -1, -1, -1, 'Apprentice Researcher', ''), +(86, 58, 200, 249, -1, -1, -1, -1, -1, -1, -1, 'Journeyman Researcher', ''), +(87, 58, 250, 299, -1, -1, -1, -1, -1, -1, -1, 'Expert Researcher', ''), +(88, 58, 300, -1, -1, -1, -1, -1, -1, -1, -1, 'Master Researcher', ''), +(89, 57, 100, 199, -1, -1, -1, -1, -1, -1, -1, 'Apprentice Tinker', ''), +(90, 57, 200, 249, -1, -1, -1, -1, -1, -1, -1, 'Journeyman Tinker', ''), +(91, 57, 250, 299, -1, -1, -1, -1, -1, -1, -1, 'Expert Tinker', ''), +(92, 57, 300, -1, -1, -1, -1, -1, -1, -1, -1, 'Master Tinker', ''), +(93, -1, -1, -1, -1, -1, 4, -1, -1, -1, 20487, 'Windcaller', ''); diff --git a/utils/sql/svn/687_aa_table_changes.sql b/utils/sql/svn/687_aa_table_changes.sql new file mode 100644 index 000000000..5fc49a279 --- /dev/null +++ b/utils/sql/svn/687_aa_table_changes.sql @@ -0,0 +1,8 @@ +ALTER TABLE `altadv_vars` +ADD COLUMN `aa_expansion` tinyint(3) unsigned NOT NULL default '3' AFTER `cost_inc`, +ADD COLUMN `special_category` int(10) unsigned NOT NULL default '4294967295' AFTER `aa_expansion`, +ADD COLUMN `sof_type` tinyint(3) unsigned NOT NULL default '1' AFTER `special_category`, +ADD COLUMN `sof_cost_inc` tinyint(3) NOT NULL default '0' AFTER `sof_type`, +ADD COLUMN `sof_max_level` tinyint(3) unsigned NOT NULL default '1' AFTER `sof_cost_inc`, +ADD COLUMN `sof_next_skill` int(10) unsigned NOT NULL default '0' AFTER `sof_max_level`, +ADD COLUMN `clientver` tinyint(3) unsigned NOT NULL default '1' AFTER `sof_next_skill`; \ No newline at end of file diff --git a/utils/sql/svn/68_optional_character_maxexplevel.sql b/utils/sql/svn/68_optional_character_maxexplevel.sql new file mode 100644 index 000000000..632b76a73 --- /dev/null +++ b/utils/sql/svn/68_optional_character_maxexplevel.sql @@ -0,0 +1 @@ +Insert into rule_values values (0, 'Character:MaxExpLevel', 0); \ No newline at end of file diff --git a/utils/sql/svn/699_peqzone_rule.sql b/utils/sql/svn/699_peqzone_rule.sql new file mode 100644 index 000000000..aa1d9433f --- /dev/null +++ b/utils/sql/svn/699_peqzone_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('1', 'Zone:PEQZoneReuseTime', '300', 'Amount of time, in seconds, before you can reuse the #peqzone command'); \ No newline at end of file diff --git a/utils/sql/svn/702_aashieldblock_tint_table.sql b/utils/sql/svn/702_aashieldblock_tint_table.sql new file mode 100644 index 000000000..be998176a --- /dev/null +++ b/utils/sql/svn/702_aashieldblock_tint_table.sql @@ -0,0 +1,38 @@ +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) VALUES (1287, 'Shield Block', 3, 3, 4294967295, 4294967295, 1287, +1287, 7, 0, 0, 0, 0, 0, 42, 0, 67, 3, 7, 4294967295, 2, 3, 3, 0, 1); + +ALTER TABLE `npc_types` ADD COLUMN `armortint_id` INTEGER UNSIGNED NOT NULL DEFAULT 0 AFTER `drakkin_details`; + +CREATE TABLE `npc_types_tint` ( + `id` int unsigned NOT NULL DEFAULT '0', + `red1h` tinyint unsigned NOT NULL DEFAULT '0', + `grn1h` tinyint unsigned NOT NULL DEFAULT '0', + `blu1h` tinyint unsigned NOT NULL DEFAULT '0', + `red2c` tinyint unsigned NOT NULL DEFAULT '0', + `grn2c` tinyint unsigned NOT NULL DEFAULT '0', + `blu2c` tinyint unsigned NOT NULL DEFAULT '0', + `red3a` tinyint unsigned NOT NULL DEFAULT '0', + `grn3a` tinyint unsigned NOT NULL DEFAULT '0', + `blu3a` tinyint unsigned NOT NULL DEFAULT '0', + `red4b` tinyint unsigned NOT NULL DEFAULT '0', + `grn4b` tinyint unsigned NOT NULL DEFAULT '0', + `blu4b` tinyint unsigned NOT NULL DEFAULT '0', + `red5g` tinyint unsigned NOT NULL DEFAULT '0', + `grn5g` tinyint unsigned NOT NULL DEFAULT '0', + `blu5g` tinyint unsigned NOT NULL DEFAULT '0', + `red6l` tinyint unsigned NOT NULL DEFAULT '0', + `grn6l` tinyint unsigned NOT NULL DEFAULT '0', + `blu6l` tinyint unsigned NOT NULL DEFAULT '0', + `red7f` tinyint unsigned NOT NULL DEFAULT '0', + `grn7f` tinyint unsigned NOT NULL DEFAULT '0', + `blu7f` tinyint unsigned NOT NULL DEFAULT '0', + `red8x` tinyint unsigned NOT NULL DEFAULT '0', + `grn8x` tinyint unsigned NOT NULL DEFAULT '0', + `blu8x` tinyint unsigned NOT NULL DEFAULT '0', + `red9x` tinyint unsigned NOT NULL DEFAULT '0', + `grn9x` tinyint unsigned NOT NULL DEFAULT '0', + `blu9x` tinyint unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC; \ No newline at end of file diff --git a/utils/sql/svn/703_peqzone_rule.sql b/utils/sql/svn/703_peqzone_rule.sql new file mode 100644 index 000000000..3f84a0639 --- /dev/null +++ b/utils/sql/svn/703_peqzone_rule.sql @@ -0,0 +1,4 @@ +INSERT INTO `rule_values` VALUES ('1', 'Zone:UsePEQZoneDebuffs', 'true', 'Will determine if #peqzone will debuff players or not when used.'); +REPLACE INTO `rule_values` VALUES ('1', 'Zone:PEQZoneReuseTime', '900', 'Amount of time, in seconds, before you can reuse the #peqzone command'); +INSERT INTO `rule_values` VALUES ('1', 'Zone:PEQZoneDebuff1', '4454', 'First debuff casted by #peqzone Default is Cursed Keepers Blight.'); +INSERT INTO `rule_values` VALUES ('1', 'Zone:PEQZoneDebuff2', '2209', 'Second debuff casted by #peqzone Default is Tendrils of Apathy.'); \ No newline at end of file diff --git a/utils/sql/svn/704_rules.sql b/utils/sql/svn/704_rules.sql new file mode 100644 index 000000000..4b901461e --- /dev/null +++ b/utils/sql/svn/704_rules.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` VALUES ('1', 'NPC:LastFightingDelayMovingMin', '10000','Minimum time (in ms) before mob goes home after all aggro loss'); +INSERT INTO `rule_values` VALUES ('1', 'NPC:LastFightingDelayMovingMax', '20000','Maximum time (in ms) before mob goes home after all aggro loss'); \ No newline at end of file diff --git a/utils/sql/svn/710_tint_set_naming.sql b/utils/sql/svn/710_tint_set_naming.sql new file mode 100644 index 000000000..7f1961838 --- /dev/null +++ b/utils/sql/svn/710_tint_set_naming.sql @@ -0,0 +1 @@ +ALTER TABLE `npc_types_tint` ADD `tint_set_name` text NOT NULL AFTER `id`; diff --git a/utils/sql/svn/721_pathing_rules.sql b/utils/sql/svn/721_pathing_rules.sql new file mode 100644 index 000000000..759128f47 --- /dev/null +++ b/utils/sql/svn/721_pathing_rules.sql @@ -0,0 +1,20 @@ +INSERT INTO `rule_values` VALUES (1, 'Pathing:Aggro', 'true', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:AggroReturnToGrid', 'true', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:CandidateNodeRangeXY', '400.000000', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:CandidateNodeRangeZ', '10.0000000', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:CullNodesFromEnd', '1', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:CullNodesFromStart', '1', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:Fear', 'true', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:Find', 'true', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:Guard', 'true', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:LOSCheckFrequency', '1000', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:MaxNodesLeftForLOSCheck', '4', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:MinDistanceForLOSCheckLong', '1000000.00', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:MinDistanceForLOSCheckShort', '40000.0000', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:MinNodesTraversedForLOSCheck', '3', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:RouteUpdateFrequencyLong', '5000', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:RouteUpdateFrequencyNodeCount', '5', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:RouteUpdateFrequencyShort', '1000', ''); +INSERT INTO `rule_values` VALUES (1, 'Pathing:ZDiffThreshold', '10.0000000', ''); +INSERT INTO `rule_values` VALUES (1, 'Map:FindBestZHeightAdjust', '1', ''); +INSERT INTO `rule_values` VALUES (1, 'Map:UseClosestZ', 'false', ''); diff --git a/utils/sql/svn/730_smart_delay_moving.sql b/utils/sql/svn/730_smart_delay_moving.sql new file mode 100644 index 000000000..d120d454b --- /dev/null +++ b/utils/sql/svn/730_smart_delay_moving.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('1', 'NPC:SmartLastFightingDelayMoving', 'true', 'When true, mobs that started going home previously will do so again immediately if still on FD hate list'); diff --git a/utils/sql/svn/731_rule_assist_notarget_self.sql b/utils/sql/svn/731_rule_assist_notarget_self.sql new file mode 100644 index 000000000..b7bb6cf42 --- /dev/null +++ b/utils/sql/svn/731_rule_assist_notarget_self.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('1', 'Combat:AssistNoTargetSelf', 'true','When assisting a target without a target: true = target self, false = leave target as was before assist (this is the behavior on live)'); diff --git a/utils/sql/svn/732_sacrifice_rules.sql b/utils/sql/svn/732_sacrifice_rules.sql new file mode 100644 index 000000000..efb0a5ba1 --- /dev/null +++ b/utils/sql/svn/732_sacrifice_rules.sql @@ -0,0 +1,3 @@ +INSERT INTO `rule_values` VALUES ('1', 'Spells:SacrificeMinLevel', '46', 'First level Sacrifice will work on'); +INSERT INTO `rule_values` VALUES ('1', 'Spells:SacrificeMaxLevel', '69', 'Last level Sacrifice will work on'); +INSERT INTO `rule_values` VALUES ('1', 'Spells:SacrificeItemID', '9963', 'Item ID of the item Sacrifice will return. Defaults to an Essence Emerald.'); \ No newline at end of file diff --git a/utils/sql/svn/745_slow_mitigation.sql b/utils/sql/svn/745_slow_mitigation.sql new file mode 100644 index 000000000..39db93bdf --- /dev/null +++ b/utils/sql/svn/745_slow_mitigation.sql @@ -0,0 +1 @@ +ALTER TABLE `npc_types` ADD COLUMN `slow_mitigation` FLOAT UNSIGNED NOT NULL DEFAULT 0 AFTER `Accuracy`; diff --git a/utils/sql/svn/754_archery_base_damage_rule.sql b/utils/sql/svn/754_archery_base_damage_rule.sql new file mode 100644 index 000000000..bbf71807e --- /dev/null +++ b/utils/sql/svn/754_archery_base_damage_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('1', 'Combat:ArcheryBaseDamageBonus', '1', 'Modifier to Base Archery Damage .5 = 50%, 1 = 100%, 2 = 200%'); diff --git a/utils/sql/svn/755_sof_altadv_vars_updates.sql b/utils/sql/svn/755_sof_altadv_vars_updates.sql new file mode 100644 index 000000000..f947dafc4 --- /dev/null +++ b/utils/sql/svn/755_sof_altadv_vars_updates.sql @@ -0,0 +1,63 @@ +UPDATE altadv_vars SET sof_next_skill = skill_id; +UPDATE altadv_vars SET aa_expansion = type; + +UPDATE altadv_vars SET sof_type = 1 WHERE classes = 65534 AND type < 8; +UPDATE altadv_vars SET sof_type = type WHERE type < 4 AND classes < 65534; +UPDATE altadv_vars SET sof_type = 1 WHERE type = 4; +UPDATE altadv_vars SET sof_type = 3 WHERE type = 5 AND classes < 65534; +UPDATE altadv_vars SET sof_type = 2 WHERE type = 6 AND classes < 65534; +UPDATE altadv_vars SET sof_type = 2 WHERE type = 7 AND classes < 65534; +UPDATE altadv_vars SET sof_type = 4 WHERE type = 8; + +UPDATE altadv_vars SET special_category = 6 WHERE skill_id = 244; +UPDATE altadv_vars SET special_category = 6 WHERE skill_id = 412; +UPDATE altadv_vars SET special_category = 6 WHERE skill_id = 979; +UPDATE altadv_vars SET special_category = 6 WHERE skill_id = 982; +UPDATE altadv_vars SET special_category = 6 WHERE skill_id = 985; +UPDATE altadv_vars SET special_category = 6 WHERE skill_id = 988; +UPDATE altadv_vars SET special_category = 6 WHERE skill_id = 991; +UPDATE altadv_vars SET special_category = 6 WHERE skill_id = 994; +UPDATE altadv_vars SET special_category = 6 WHERE skill_id = 997; + +UPDATE altadv_vars SET sof_next_skill = prereq_skill WHERE prereq_skill > 0 AND prereq_skill < 74; + +UPDATE altadv_vars SET sof_next_skill = 74 WHERE skill_id = 263; +UPDATE altadv_vars SET sof_next_skill = 77 WHERE skill_id = 434; +UPDATE altadv_vars SET sof_next_skill = 80 WHERE skill_id = 437; +UPDATE altadv_vars SET sof_next_skill = 86 WHERE skill_id = 266; +UPDATE altadv_vars SET sof_next_skill = 92 WHERE skill_id = 267; +UPDATE altadv_vars SET sof_next_skill = 101 WHERE skill_id = 446; +UPDATE altadv_vars SET sof_next_skill = 113 WHERE skill_id = 443; +UPDATE altadv_vars SET sof_next_skill = 119 WHERE skill_id = 440; +UPDATE altadv_vars SET sof_next_skill = 122 WHERE skill_id = 454; +UPDATE altadv_vars SET sof_next_skill = 125 WHERE skill_id = 449; +UPDATE altadv_vars SET sof_next_skill = 213 WHERE skill_id = 700; +UPDATE altadv_vars SET sof_next_skill = 247 WHERE skill_id = 504; +UPDATE altadv_vars SET sof_next_skill = 255 WHERE skill_id = 542; +UPDATE altadv_vars SET sof_next_skill = 267 WHERE skill_id = 640; +UPDATE altadv_vars SET sof_next_skill = 275 WHERE skill_id = 701; +UPDATE altadv_vars SET sof_next_skill = 288 WHERE skill_id = 1129; +UPDATE altadv_vars SET sof_next_skill = 418 WHERE skill_id = 1001; +UPDATE altadv_vars SET sof_next_skill = 77 WHERE skill_id = 1083; +UPDATE altadv_vars SET sof_next_skill = 80 WHERE skill_id = 1086; +UPDATE altadv_vars SET sof_next_skill = 119 WHERE skill_id = 1053; +UPDATE altadv_vars SET sof_next_skill = 125 WHERE skill_id = 1061; +UPDATE altadv_vars SET sof_next_skill = 122 WHERE skill_id = 1066; +UPDATE altadv_vars SET sof_next_skill = 498 WHERE skill_id = 886; +UPDATE altadv_vars SET sof_next_skill = 528 WHERE skill_id = 900; +UPDATE altadv_vars SET sof_next_skill = 589 WHERE skill_id = 893; +UPDATE altadv_vars SET sof_next_skill = 110 WHERE skill_id = 1031; +UPDATE altadv_vars SET sof_next_skill = 71 WHERE skill_id = 978; +UPDATE altadv_vars SET sof_next_skill = 155 WHERE skill_id = 533; +UPDATE altadv_vars SET sof_next_skill = 267 WHERE skill_id = 924; +UPDATE altadv_vars SET sof_next_skill = 619 WHERE skill_id = 721; +UPDATE altadv_vars SET sof_next_skill = 724 WHERE skill_id = 1340; +UPDATE altadv_vars SET sof_next_skill = 729 WHERE skill_id = 1341; +UPDATE altadv_vars SET sof_next_skill = 734 WHERE skill_id = 1342; +UPDATE altadv_vars SET sof_next_skill = 290 WHERE skill_id = 1344; +UPDATE altadv_vars SET sof_next_skill = 230 WHERE skill_id = 539; +UPDATE altadv_vars SET sof_next_skill = 767 WHERE skill_id = 1099; +UPDATE altadv_vars SET sof_next_skill = 637 WHERE skill_id = 770; +UPDATE altadv_vars SET sof_next_skill = 131 WHERE skill_id = 531; + +UPDATE altadv_vars SET prereq_skill = 1134 WHERE skill_id = 1158; diff --git a/utils/sql/svn/773_monk_rules.sql b/utils/sql/svn/773_monk_rules.sql new file mode 100644 index 000000000..6ebfccf42 --- /dev/null +++ b/utils/sql/svn/773_monk_rules.sql @@ -0,0 +1,6 @@ +UPDATE `rule_values` SET rule_value='10' WHERE rule_name='Combat:MonkDamageTableBonus'; +UPDATE `rule_values` SET rule_value='25' WHERE rule_name='Combat:FlyingKickBonus'; +UPDATE `rule_values` SET rule_value='20' WHERE rule_name='Combat:DragonPunchBonus'; +UPDATE `rule_values` SET rule_value='15' WHERE rule_name='Combat:EagleStrikeBonus'; +UPDATE `rule_values` SET rule_value='10' WHERE rule_name='Combat:TigerClawBonus'; +UPDATE `rule_values` SET rule_value='5' WHERE rule_name='Combat:RoundKickBonus'; \ No newline at end of file diff --git a/utils/sql/svn/853_optional_rule_aaexp.sql b/utils/sql/svn/853_optional_rule_aaexp.sql new file mode 100644 index 000000000..9fc7dc007 --- /dev/null +++ b/utils/sql/svn/853_optional_rule_aaexp.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('1', 'AA:ExpPerPoint', '23976503', 'Amount of exp per AA. Is the same as the amount of exp to go from level 51 to level 52.'); \ No newline at end of file diff --git a/utils/sql/svn/858_optional_rule_ip_limit_by_status.sql b/utils/sql/svn/858_optional_rule_ip_limit_by_status.sql new file mode 100644 index 000000000..fc1838c76 --- /dev/null +++ b/utils/sql/svn/858_optional_rule_ip_limit_by_status.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('1', 'World:MaxClientsSetByStatus', 'false', 'If True, IP Limiting will be set to the status on the account as long as the status is > MaxClientsPerIP'); diff --git a/utils/sql/svn/892_optional_bots_table_mod.sql b/utils/sql/svn/892_optional_bots_table_mod.sql new file mode 100644 index 000000000..b571ee36e --- /dev/null +++ b/utils/sql/svn/892_optional_bots_table_mod.sql @@ -0,0 +1,29 @@ +alter table bots +drop column BodyType, +drop column HitPoints, +drop column HitPointsRegenRate, +drop column ManaRegenRate, +drop column RunSpeed, +add column BotCreateDate timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, +add column LastSpawnDate datetime NOT NULL DEFAULT '0000-00-00 00:00:00', +add column TotalPlayTime time NOT NULL DEFAULT '00:00:00', +modify column Race smallint(5) NOT NULL DEFAULT 0, +modify column Class tinyint(2) NOT NULL DEFAULT 0, +modify column Gender tinyint(2) NOT NULL DEFAULT 0, +modify column Face int(10) NOT NULL DEFAULT 1, +modify column LuclinHairStyle int(10) NOT NULL DEFAULT 1, +modify column LuclinHairColor int(10) NOT NULL DEFAULT 1, +modify column LuclinEyeColor int(10) NOT NULL DEFAULT 1, +modify column LuclinEyeColor2 int(10) NOT NULL DEFAULT 1, +modify column LuclinBeardColor int(10) NOT NULL DEFAULT 1, +modify column LuclinBeard int(10) NOT NULL DEFAULT 0, +modify column DrakkinHeritage int(10) NOT NULL DEFAULT 0, +modify column DrakkinTattoo int(10) NOT NULL DEFAULT 0, +modify column DrakkinDetails int(10) NOT NULL DEFAULT 0, +modify column STR mediumint(8) NOT NULL DEFAULT 75, +modify column STA mediumint(8) NOT NULL DEFAULT 75, +modify column DEX mediumint(8) NOT NULL DEFAULT 75, +modify column AGI mediumint(8) NOT NULL DEFAULT 75, +modify column _INT mediumint(8) NOT NULL DEFAULT 80, +modify column WIS mediumint(8) NOT NULL DEFAULT 75, +modify column CHA mediumint(8) NOT NULL DEFAULT 75; \ No newline at end of file diff --git a/utils/sql/svn/893_optional_bots_table_mod.sql b/utils/sql/svn/893_optional_bots_table_mod.sql new file mode 100644 index 000000000..add7a249e --- /dev/null +++ b/utils/sql/svn/893_optional_bots_table_mod.sql @@ -0,0 +1,3 @@ +alter table bots +drop column TotalPlayTime, +add column TotalPlayTime int unsigned NOT NULL DEFAULT 0; \ No newline at end of file diff --git a/utils/sql/svn/898_npc_maxlevel_scalerate.sql b/utils/sql/svn/898_npc_maxlevel_scalerate.sql new file mode 100644 index 000000000..7b0deabcc --- /dev/null +++ b/utils/sql/svn/898_npc_maxlevel_scalerate.sql @@ -0,0 +1,2 @@ +ALTER TABLE `npc_types` ADD COLUMN `maxlevel` tinyint(3) NOT NULL DEFAULT 0; +ALTER TABLE `npc_types` ADD COLUMN `scalerate` int NOT NULL DEFAULT 100; diff --git a/utils/sql/svn/902_optional_rule_snareflee.sql b/utils/sql/svn/902_optional_rule_snareflee.sql new file mode 100644 index 000000000..3488519cf --- /dev/null +++ b/utils/sql/svn/902_optional_rule_snareflee.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` VALUES ('1', 'Combat:FleeSnareHPRatio', '11', 'HP at which snare will halt movement of a fleeing NPC.'); \ No newline at end of file diff --git a/utils/sql/svn/923_spawn2_enabled.sql b/utils/sql/svn/923_spawn2_enabled.sql new file mode 100644 index 000000000..1bc027c6d --- /dev/null +++ b/utils/sql/svn/923_spawn2_enabled.sql @@ -0,0 +1 @@ +ALTER TABLE `spawn2` ADD `enabled` TINYINT UNSIGNED DEFAULT '1' NOT NULL AFTER `cond_value`; \ No newline at end of file diff --git a/utils/sql/svn/962_hot_zone.sql b/utils/sql/svn/962_hot_zone.sql new file mode 100644 index 000000000..58eb3f7d4 --- /dev/null +++ b/utils/sql/svn/962_hot_zone.sql @@ -0,0 +1 @@ +ALTER TABLE `zone` ADD `hotzone` TINYINT UNSIGNED DEFAULT '0' NOT NULL AFTER `castoutdoor`; \ No newline at end of file diff --git a/utils/sql/svn/964_reports.sql b/utils/sql/svn/964_reports.sql new file mode 100644 index 000000000..4764065b8 --- /dev/null +++ b/utils/sql/svn/964_reports.sql @@ -0,0 +1,8 @@ +CREATE TABLE `reports` ( + `id` int(10) unsigned NOT NULL auto_increment, + `name` varchar(64) default NULL, + `reported` varchar(64) default NULL, + `reported_text` text, + PRIMARY KEY (`id`), + UNIQUE KEY `id` (`id`) +) ENGINE=InnoDB; \ No newline at end of file diff --git a/utils/sql/svn/971_veteran_rewards.sql b/utils/sql/svn/971_veteran_rewards.sql new file mode 100644 index 000000000..68b83aa60 --- /dev/null +++ b/utils/sql/svn/971_veteran_rewards.sql @@ -0,0 +1,17 @@ +CREATE TABLE `veteran_reward_templates` ( + `claim_id` int(10) unsigned NOT NULL, + `name` varchar(64) NOT NULL, + `item_id` int(10) unsigned NOT NULL, + `charges` smallint(5) unsigned NOT NULL, + `reward_slot` tinyint(3) unsigned NOT NULL, + UNIQUE KEY `claim_reward` (`claim_id`,`reward_slot`), + KEY `claim_id` (`claim_id`) +) ENGINE=MyISAM; + +CREATE TABLE `account_rewards` ( + `account_id` int(10) unsigned NOT NULL, + `reward_id` int(10) unsigned NOT NULL, + `amount` int(10) unsigned NOT NULL, + UNIQUE KEY `account_reward` (`account_id`,`reward_id`), + KEY `account_id` (`account_id`) +) ENGINE=InnoDB; \ No newline at end of file diff --git a/utils/sql/svn/977_raid_npc_private_corpses.sql b/utils/sql/svn/977_raid_npc_private_corpses.sql new file mode 100644 index 000000000..ed89ba176 --- /dev/null +++ b/utils/sql/svn/977_raid_npc_private_corpses.sql @@ -0,0 +1 @@ +ALTER TABLE `npc_types` ADD `private_corpse` TINYINT UNSIGNED DEFAULT '0' NOT NULL AFTER `scalerate`; \ No newline at end of file diff --git a/utils/sql/svn/979_unique_spawn_by_name.sql b/utils/sql/svn/979_unique_spawn_by_name.sql new file mode 100644 index 000000000..16ef44cca --- /dev/null +++ b/utils/sql/svn/979_unique_spawn_by_name.sql @@ -0,0 +1 @@ +ALTER TABLE `npc_types` ADD `unique_spawn_by_name` TINYINT UNSIGNED DEFAULT '0' NOT NULL AFTER `private_corpse`; \ No newline at end of file diff --git a/utils/sql/svn/980_account_ip.sql b/utils/sql/svn/980_account_ip.sql new file mode 100644 index 000000000..ff965b117 --- /dev/null +++ b/utils/sql/svn/980_account_ip.sql @@ -0,0 +1,7 @@ +CREATE TABLE `account_ip` ( + `accid` int(11) NOT NULL default '0', + `ip` varchar(32) NOT NULL default '', + `count` int(11) NOT NULL default 1, + `lastused` timestamp NOT NULL default CURRENT_TIMESTAMP, + UNIQUE KEY `ip` (`accid`,`ip`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; \ No newline at end of file diff --git a/utils/sql/svn/bots.sql b/utils/sql/svn/bots.sql new file mode 100644 index 000000000..849298da4 --- /dev/null +++ b/utils/sql/svn/bots.sql @@ -0,0 +1,280 @@ +DROP TABLE IF EXISTS `botgroupmembers`; +DROP TABLE IF EXISTS `botgroup`; +DROP TABLE IF EXISTS `botbuffs`; +DROP TABLE IF EXISTS `botpetinventory`; +DROP TABLE IF EXISTS `botpetbuffs`; +DROP TABLE IF EXISTS `botpets`; +DROP TABLE IF EXISTS `botguildmembers`; +DROP TABLE IF EXISTS `botinventory`; +DROP TABLE IF EXISTS `bots`; + +CREATE TABLE IF NOT EXISTS `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', + `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', + `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', + PRIMARY KEY (`BotID`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS botinventory ( + `BotInventoryID` int(10) unsigned NOT NULL AUTO_INCREMENT, + `BotID` int(10) unsigned NOT NULL DEFAULT '0', + `SlotID` int(11) NOT NULL DEFAULT '0', + `ItemID` int(10) unsigned NOT NULL DEFAULT '0', + `charges` tinyint(3) unsigned DEFAULT '0', + `color` int(10) 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; + +delete from rule_values where rule_name like 'Bots%' and ruleset_id = 1; + +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.'); + +DELIMITER $$ + +DROP FUNCTION IF EXISTS `GetMobTypeByName` $$ +CREATE FUNCTION `GetMobTypeByName` (mobname VARCHAR(64)) RETURNS CHAR(1) +BEGIN + DECLARE Result CHAR(1); + + SET Result = NULL; + + IF (select id from character_ where name = mobname) > 0 THEN + SET Result = 'C'; + ELSEIF (select BotID from bots where Name = mobname) > 0 THEN + SET Result = 'B'; + END IF; + + RETURN Result; +END $$ + +DELIMITER ; + +DELIMITER $$ + +DROP FUNCTION IF EXISTS `GetMobTypeById` $$ +CREATE FUNCTION `GetMobTypeById` (mobid INTEGER UNSIGNED) RETURNS CHAR(1) +BEGIN + DECLARE Result CHAR(1); + + SET Result = NULL; + + IF (select id from character_ where id = mobid) > 0 THEN + SET Result = 'C'; + ELSEIF (select BotID from bots where BotID = mobid) > 0 THEN + SET Result = 'B'; + END IF; + + RETURN Result; +END $$ + +DELIMITER ; + +DROP VIEW IF EXISTS `vwGroups`; +CREATE VIEW `vwGroups` AS + select g.groupid as groupid, +GetMobTypeByName(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; + +ALTER TABLE `group_id` DROP PRIMARY KEY, ADD PRIMARY KEY USING BTREE(`groupid`, `charid`, `name`); +ALTER TABLE `guild_members` DROP PRIMARY KEY; +ALTER TABLE `group_id` ADD UNIQUE INDEX `U_group_id_1`(`name`); +ALTER TABLE `group_leaders` ADD UNIQUE INDEX `U_group_leaders_1`(`leadername`); + +CREATE TABLE IF NOT EXISTS `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', + `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; + +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`) +) ENGINE=InnoDB; + +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; + +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; + +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, + PRIMARY KEY (`char_id`) +) ENGINE=InnoDB; + +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, +0 as alt +from botguildmembers as bm; + +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; + +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; \ No newline at end of file diff --git a/utils/sql/svn/botsconvert.sql b/utils/sql/svn/botsconvert.sql new file mode 100644 index 000000000..fb6e0177e --- /dev/null +++ b/utils/sql/svn/botsconvert.sql @@ -0,0 +1,16 @@ +RENAME TABLE botinventory TO oldbotinventory; + +ALTER TABLE bots ADD COLUMN (oldnpcid int(11) NOT NULL default 0); + +SOURCE bots.sql; + +INSERT INTO bots SELECT NULL, bo.botleadercharacterid, nt.npc_spells_id, nt.name, nt.lastname, nt.level, nt.race, nt.class, nt.bodytype, nt.hp, nt.gender, nt.size, nt.hp_regen_rate, nt.mana_regen_rate, nt.face, nt.luclin_hairstyle, nt.luclin_haircolor, nt.luclin_eyecolor, nt.luclin_eyecolor2, nt.luclin_beardcolor, nt.luclin_beard, nt.drakkin_heritage, nt.drakkin_tattoo, nt.drakkin_details, nt.runspeed, nt.MR, nt.CR, nt.DR, nt.FR, nt.PR, nt.AC, nt.STR, nt.STA, nt.DEX, nt.AGI, nt._INT, nt.WIS, nt.CHA, nt.ATK, nt.id FROM botsowners bo, npc_types nt WHERE nt.isbot = 1 AND nt.id = bo.botnpctypeid; + +INSERT INTO botinventory SELECT NULL, b.BotID, i.botslotid, i.itemid FROM bots b, oldbotinventory i WHERE b.oldnpcid = i.npctypeid; + +ALTER TABLE bots DROP COLUMN oldnpcid; + +DROP TABLE IF EXISTS oldbotinventory; +DROP TABLE IF EXISTS botsleaders; + +DELETE FROM npc_types WHERE isbot = 1; diff --git a/utils/sql/svn/mercs.sql b/utils/sql/svn/mercs.sql new file mode 100644 index 000000000..a3ffed789 --- /dev/null +++ b/utils/sql/svn/mercs.sql @@ -0,0 +1,3929 @@ +-- Drop tables to reload data +DROP VIEW IF EXISTS vwMercNpcTypes; +DROP TABLE IF EXISTS merc_spell_list_entries; +DROP TABLE IF EXISTS merc_spell_lists; +DROP TABLE IF EXISTS merc_armorinfo; +DROP TABLE IF EXISTS merc_weaponinfo; +DROP TABLE IF EXISTS merc_stats; + +CREATE TABLE merc_stats ( + merc_npc_type_id int(11) unsigned NOT NULL, + clientlevel tinyint(2) unsigned NOT NULL default '1', + level tinyint(2) unsigned NOT NULL default '1', + hp int(11) NOT NULL default '1', + mana int(11) NOT NULL default '0', + AC smallint(5) NOT NULL default '1', + ATK mediumint(9) NOT NULL default '1', + STR mediumint(8) unsigned NOT NULL default '75', + STA mediumint(8) unsigned NOT NULL default '75', + DEX mediumint(8) unsigned NOT NULL default '75', + AGI mediumint(8) unsigned NOT NULL default '75', + _INT mediumint(8) unsigned NOT NULL default '80', + WIS mediumint(8) unsigned NOT NULL default '80', + CHA mediumint(8) unsigned NOT NULL default '75', + MR smallint(5) NOT NULL default '15', + CR smallint(5) NOT NULL default '15', + DR smallint(5) NOT NULL default '15', + FR smallint(5) NOT NULL default '15', + PR smallint(5) NOT NULL default '15', + Corrup smallint(5) NOT NULL default '15', + mindmg int(10) unsigned NOT NULL default '1', + maxdmg int(10) unsigned NOT NULL default '1', + attack_count smallint(6) NOT NULL default '0', + attack_speed tinyint(3) NOT NULL default '0', + specialattks varchar(36) NOT NULL default '', + Accuracy mediumint(9) NOT NULL default '0', + hp_regen_rate int(11) unsigned NOT NULL default '1', + mana_regen_rate int(11) unsigned NOT NULL default '1', + runspeed float NOT NULL default '0', + spellscale float NOT NULL default '100', + healscale float NOT NULL default '100', + PRIMARY KEY (merc_npc_type_id, clientlevel) +); + +CREATE TABLE merc_armorinfo ( + id int(11) NOT NULL auto_increment, + merc_npc_type_id int(11) unsigned NOT NULL, + minlevel tinyint(2) unsigned NOT NULL default '1', + maxlevel tinyint(2) unsigned NOT NULL default '255', + texture tinyint(2) unsigned NOT NULL default '0', + helmtexture tinyint(2) unsigned NOT NULL default '0', + armortint_id int(10) unsigned NOT NULL default '0', + armortint_red tinyint(3) unsigned NOT NULL default '0', + armortint_green tinyint(3) unsigned NOT NULL default '0', + armortint_blue tinyint(3) unsigned NOT NULL default '0', + PRIMARY KEY (id) +); + +CREATE TABLE merc_weaponinfo ( + id int(11) NOT NULL auto_increment, + merc_npc_type_id int(11) NOT NULL, + minlevel tinyint(2) unsigned NOT NULL default '0', + maxlevel tinyint(2) unsigned NOT NULL default '0', + d_meele_texture1 int(10) unsigned NOT NULL default '0', + d_meele_texture2 int(10) unsigned NOT NULL default '0', + prim_melee_type tinyint(4) unsigned NOT NULL default '28', + sec_melee_type tinyint(4) unsigned NOT NULL default '28', + PRIMARY KEY (id) +); + +create table merc_spell_lists +( + merc_spell_list_id int UNSIGNED NOT NULL AUTO_INCREMENT, + class_id int UNSIGNED NOT NULL, + proficiency_id tinyint UNSIGNED NOT NULL, + name varchar(50) NOT NULL, + PRIMARY KEY (merc_spell_list_id) +); + +create table merc_spell_list_entries +( + merc_spell_list_entry_id int UNSIGNED NOT NULL AUTO_INCREMENT, + merc_spell_list_id int UNSIGNED NOT NULL, + spell_id int UNSIGNED NOT NULL, + spell_type tinyint UNSIGNED NOT NULL default '0', + stance_id tinyint UNSIGNED NOT NULL default '0', + minlevel tinyint UNSIGNED NOT NULL default '1', + maxlevel tinyint UNSIGNED NOT NULL default '255', + slot tinyint NOT NULL default '-1', + procChance tinyint UNSIGNED NOT NULL default '0', + PRIMARY KEY (merc_spell_list_entry_id), + KEY FK_merc_spell_lists_1 (merc_spell_list_id), + CONSTRAINT FK_merc_spell_lists_1 FOREIGN KEY (merc_spell_list_id) REFERENCES merc_spell_lists (merc_spell_list_id) +); + +CREATE VIEW vwMercNpcTypes AS +SELECT + ms.merc_npc_type_id, + '' AS name, + ms.clientlevel, + ms.level, + mtyp.race_id, + mstyp.class_id, + ms.hp, + ms.mana, + 0 AS gender, + mai.texture, + mai.helmtexture, + ms.attack_speed, + ms.STR, + ms.STA, + ms.DEX, + ms.AGI, + ms._INT, + ms.WIS, + ms.CHA, + ms.MR, + ms.CR, + ms.DR, + ms.FR, + ms.PR, + ms.Corrup, + ms.mindmg, + ms.maxdmg, + ms.attack_count, + ms.specialattks AS npcspecialattks, + mwi.d_meele_texture1, + mwi.d_meele_texture2, + mwi.prim_melee_type, + mwi.sec_melee_type, + ms.runspeed, + ms.hp_regen_rate, + ms.mana_regen_rate, + 1 AS bodytype, + mai.armortint_id, + mai.armortint_red, + mai.armortint_green, + mai.armortint_blue, + ms.AC, + ms.ATK, + ms.Accuracy, + ms.spellscale, + ms.healscale + FROM merc_stats ms + INNER JOIN merc_armorinfo mai + ON ms.merc_npc_type_id = mai.merc_npc_type_id + AND mai.minlevel <= ms.level AND mai.maxlevel >= ms.level + INNER JOIN merc_weaponinfo mwi + ON ms.merc_npc_type_id = mwi.merc_npc_type_id + AND mwi.minlevel <= ms.level AND mwi.maxlevel >= ms.level + INNER JOIN merc_templates mtem + ON mtem.merc_npc_type_id = ms.merc_npc_type_id + INNER JOIN merc_types mtyp + ON mtem.merc_type_id = mtyp.merc_type_id + INNER JOIN merc_subtypes mstyp + ON mtem.merc_subtype_id = mstyp.merc_subtype_id; + + +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 1, 1, 50, 0, 35, 28, 66, 66, 66, 66, 75, 75, 66, 15, 15, 15, 15, 15, 15, 3, 10, 1, -18, '', 0, 13, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 1, 1, 55, 0, 36, 29, 67, 67, 67, 67, 77, 77, 67, 18, 18, 18, 18, 18, 18, 3, 12, 1, -18, '', 0, 15, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 1, 1, 60, 0, 38, 29, 68, 68, 68, 68, 79, 79, 68, 21, 21, 21, 21, 21, 21, 4, 15, 1, -18, '', 0, 17, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 1, 1, 65, 0, 39, 30, 69, 69, 69, 69, 81, 81, 69, 24, 24, 24, 24, 24, 24, 4, 18, 1, -18, '', 0, 19, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 1, 1, 70, 0, 41, 30, 70, 70, 70, 70, 83, 83, 70, 27, 27, 27, 27, 27, 27, 5, 21, 1, -18, '', 0, 21, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 1, 1, 85, 0, 43, 30, 71, 71, 71, 71, 95, 95, 71, 31, 31, 31, 31, 31, 31, 4, 17, 1, -18, '', 0, 23, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 1, 1, 90, 0, 44, 31, 72, 72, 72, 72, 97, 97, 72, 34, 34, 34, 34, 34, 34, 5, 19, 1, -18, '', 0, 25, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 1, 1, 95, 0, 46, 31, 73, 73, 73, 73, 99, 99, 73, 37, 37, 37, 37, 37, 37, 5, 22, 1, -18, '', 0, 27, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 1, 1, 100, 0, 47, 32, 74, 74, 74, 74, 101, 101, 74, 40, 40, 40, 40, 40, 40, 6, 25, 1, -18, '', 0, 29, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 1, 1, 105, 0, 49, 32, 75, 75, 75, 75, 103, 103, 75, 43, 43, 43, 43, 43, 43, 6, 28, 1, -18, '', 0, 31, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 1, 1, 43, 29, 27, 17, 56, 56, 56, 56, 106, 106, 56, 15, 15, 15, 15, 15, 15, 1, 8, 1, -18, '', 0, 8, 5, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 1, 1, 46, 32, 28, 17, 57, 57, 57, 57, 109, 109, 57, 18, 18, 18, 18, 18, 18, 1, 10, 1, -18, '', 0, 9, 6, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 1, 1, 49, 35, 29, 18, 58, 58, 58, 58, 112, 112, 58, 21, 21, 21, 21, 21, 21, 2, 11, 1, -18, '', 0, 10, 7, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 1, 1, 52, 38, 30, 18, 59, 59, 59, 59, 115, 115, 59, 24, 24, 24, 24, 24, 24, 2, 13, 1, -18, '', 0, 11, 8, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 1, 1, 55, 41, 31, 18, 60, 60, 60, 60, 118, 118, 60, 27, 27, 27, 27, 27, 27, 2, 14, 1, -18, '', 0, 12, 9, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 1, 1, 73, 54, 33, 18, 61, 61, 61, 61, 131, 131, 61, 31, 31, 31, 31, 31, 31, 2, 13, 1, -18, '', 0, 14, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 1, 1, 76, 57, 34, 18, 62, 62, 62, 62, 134, 134, 62, 34, 34, 34, 34, 34, 34, 2, 14, 1, -18, '', 0, 15, 9, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 1, 1, 79, 60, 35, 19, 63, 63, 63, 63, 137, 137, 63, 37, 37, 37, 37, 37, 37, 2, 15, 1, -18, '', 0, 16, 10, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 1, 1, 82, 63, 36, 19, 64, 64, 64, 64, 140, 140, 64, 40, 40, 40, 40, 40, 40, 3, 17, 1, -18, '', 0, 17, 11, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 1, 1, 85, 66, 37, 19, 65, 65, 65, 65, 143, 143, 65, 43, 43, 43, 43, 43, 43, 3, 18, 1, -18, '', 0, 18, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 1, 1, 44, 0, 20, 28, 66, 66, 66, 66, 75, 75, 66, 15, 15, 15, 15, 15, 15, 3, 9, 1, -18, '', 0, 11, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 1, 1, 48, 0, 21, 29, 67, 67, 67, 67, 77, 77, 67, 18, 18, 18, 18, 18, 18, 3, 11, 1, -18, '', 0, 13, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 1, 1, 52, 0, 21, 29, 68, 68, 68, 68, 79, 79, 68, 21, 21, 21, 21, 21, 21, 4, 14, 1, -18, '', 0, 15, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 1, 1, 56, 0, 22, 30, 69, 69, 69, 69, 81, 81, 69, 24, 24, 24, 24, 24, 24, 4, 17, 1, -18, '', 0, 17, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 1, 1, 60, 0, 22, 30, 70, 70, 70, 70, 83, 83, 70, 27, 27, 27, 27, 27, 27, 5, 19, 1, -18, '', 0, 19, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 1, 1, 74, 0, 25, 31, 71, 71, 71, 71, 95, 95, 71, 31, 31, 31, 31, 31, 31, 4, 15, 1, -18, '', 0, 19, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 1, 1, 78, 0, 26, 32, 72, 72, 72, 72, 97, 97, 72, 34, 34, 34, 34, 34, 34, 5, 18, 1, -18, '', 0, 21, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 1, 1, 82, 0, 26, 32, 73, 73, 73, 73, 99, 99, 73, 37, 37, 37, 37, 37, 37, 5, 20, 1, -18, '', 0, 23, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 1, 1, 86, 0, 27, 33, 74, 74, 74, 74, 101, 101, 74, 40, 40, 40, 40, 40, 40, 6, 23, 1, -18, '', 0, 25, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 1, 1, 90, 0, 27, 33, 75, 75, 75, 75, 103, 103, 75, 43, 43, 43, 43, 43, 43, 6, 26, 1, -18, '', 0, 27, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 1, 1, 37, 29, 13, 8, 56, 56, 56, 56, 106, 106, 56, 15, 15, 15, 15, 15, 15, 1, 8, 1, -18, '', 0, 7, 5, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 1, 1, 39, 32, 13, 8, 57, 57, 57, 57, 109, 109, 57, 18, 18, 18, 18, 18, 18, 1, 10, 1, -18, '', 0, 8, 6, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 1, 1, 41, 35, 14, 8, 58, 58, 58, 58, 112, 112, 58, 21, 21, 21, 21, 21, 21, 2, 11, 1, -18, '', 0, 9, 7, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 1, 1, 43, 38, 14, 9, 59, 59, 59, 59, 115, 115, 59, 24, 24, 24, 24, 24, 24, 2, 13, 1, -18, '', 0, 10, 8, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 1, 1, 45, 41, 14, 9, 60, 60, 60, 60, 118, 118, 60, 27, 27, 27, 27, 27, 27, 2, 14, 1, -18, '', 0, 11, 9, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 1, 1, 62, 54, 16, 9, 61, 61, 61, 61, 131, 131, 61, 31, 31, 31, 31, 31, 31, 2, 13, 1, -18, '', 0, 12, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 1, 1, 64, 57, 16, 9, 62, 62, 62, 62, 134, 134, 62, 34, 34, 34, 34, 34, 34, 2, 14, 1, -18, '', 0, 13, 9, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 1, 1, 66, 60, 17, 9, 63, 63, 63, 63, 137, 137, 63, 37, 37, 37, 37, 37, 37, 2, 15, 1, -18, '', 0, 14, 10, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 1, 1, 68, 63, 17, 10, 64, 64, 64, 64, 140, 140, 64, 40, 40, 40, 40, 40, 40, 3, 17, 1, -18, '', 0, 15, 11, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 1, 1, 70, 66, 17, 10, 65, 65, 65, 65, 143, 143, 65, 43, 43, 43, 43, 43, 43, 3, 18, 1, -18, '', 0, 16, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 2, 2, 90, 0, 69, 56, 67, 67, 67, 67, 78, 78, 67, 17, 17, 17, 17, 17, 17, 3, 11, 1, -18, '', 0, 13, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 2, 2, 100, 0, 72, 57, 68, 68, 68, 68, 80, 80, 68, 20, 20, 20, 20, 20, 20, 4, 14, 1, -18, '', 0, 15, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 2, 2, 110, 0, 75, 58, 69, 69, 69, 69, 82, 82, 69, 23, 23, 23, 23, 23, 23, 4, 17, 1, -18, '', 0, 17, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 2, 2, 120, 0, 78, 59, 70, 70, 70, 70, 84, 84, 70, 26, 26, 26, 26, 26, 26, 5, 19, 1, -18, '', 0, 19, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 2, 2, 130, 0, 81, 60, 71, 71, 71, 71, 86, 86, 71, 29, 29, 29, 29, 29, 29, 5, 22, 1, -18, '', 0, 21, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 2, 2, 160, 0, 85, 61, 73, 73, 73, 73, 98, 98, 73, 35, 35, 35, 35, 35, 35, 4, 18, 1, -18, '', 0, 23, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 2, 2, 170, 0, 88, 62, 74, 74, 74, 74, 100, 100, 74, 38, 38, 38, 38, 38, 38, 5, 21, 1, -18, '', 0, 25, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 2, 2, 180, 0, 91, 63, 75, 75, 75, 75, 102, 102, 75, 41, 41, 41, 41, 41, 41, 5, 24, 1, -18, '', 0, 27, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 2, 2, 190, 0, 94, 64, 76, 76, 76, 76, 104, 104, 76, 44, 44, 44, 44, 44, 44, 6, 26, 1, -18, '', 0, 29, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 2, 2, 200, 0, 97, 65, 77, 77, 77, 77, 106, 106, 77, 47, 47, 47, 47, 47, 47, 6, 29, 1, -18, '', 0, 31, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 2, 2, 76, 59, 54, 33, 57, 57, 57, 57, 110, 110, 57, 17, 17, 17, 17, 17, 17, 1, 10, 1, -18, '', 0, 8, 5, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 2, 2, 82, 65, 56, 34, 58, 58, 58, 58, 113, 113, 58, 20, 20, 20, 20, 20, 20, 2, 11, 1, -18, '', 0, 9, 6, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 2, 2, 88, 71, 58, 35, 59, 59, 59, 59, 116, 116, 59, 23, 23, 23, 23, 23, 23, 2, 13, 1, -18, '', 0, 10, 7, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 2, 2, 94, 77, 60, 35, 60, 60, 60, 60, 119, 119, 60, 26, 26, 26, 26, 26, 26, 2, 14, 1, -18, '', 0, 11, 8, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 2, 2, 100, 83, 62, 36, 61, 61, 61, 61, 122, 122, 61, 29, 29, 29, 29, 29, 29, 2, 15, 1, -18, '', 0, 12, 9, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 2, 2, 136, 109, 66, 36, 63, 63, 63, 63, 135, 135, 63, 35, 35, 35, 35, 35, 35, 2, 14, 1, -18, '', 0, 14, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 2, 2, 142, 115, 68, 37, 64, 64, 64, 64, 138, 138, 64, 38, 38, 38, 38, 38, 38, 2, 15, 1, -18, '', 0, 15, 9, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 2, 2, 148, 121, 70, 38, 65, 65, 65, 65, 141, 141, 65, 41, 41, 41, 41, 41, 41, 3, 17, 1, -18, '', 0, 16, 10, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 2, 2, 154, 127, 72, 38, 66, 66, 66, 66, 144, 144, 66, 44, 44, 44, 44, 44, 44, 3, 18, 1, -18, '', 0, 17, 11, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 2, 2, 160, 133, 74, 39, 67, 67, 67, 67, 147, 147, 67, 47, 47, 47, 47, 47, 47, 3, 20, 1, -18, '', 0, 18, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 2, 2, 78, 0, 41, 57, 67, 67, 67, 67, 78, 78, 67, 17, 17, 17, 17, 17, 17, 3, 10, 1, -18, '', 0, 11, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 2, 2, 86, 0, 42, 58, 68, 68, 68, 68, 80, 80, 68, 20, 20, 20, 20, 20, 20, 4, 13, 1, -18, '', 0, 13, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 2, 2, 94, 0, 43, 59, 69, 69, 69, 69, 82, 82, 69, 23, 23, 23, 23, 23, 23, 4, 15, 1, -18, '', 0, 15, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 2, 2, 102, 0, 44, 60, 70, 70, 70, 70, 84, 84, 70, 26, 26, 26, 26, 26, 26, 5, 18, 1, -18, '', 0, 17, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 2, 2, 110, 0, 45, 61, 71, 71, 71, 71, 86, 86, 71, 29, 29, 29, 29, 29, 29, 5, 20, 1, -18, '', 0, 19, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 2, 2, 138, 0, 51, 63, 73, 73, 73, 73, 98, 98, 73, 35, 35, 35, 35, 35, 35, 4, 17, 1, -18, '', 0, 19, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 2, 2, 146, 0, 52, 64, 74, 74, 74, 74, 100, 100, 74, 38, 38, 38, 38, 38, 38, 5, 19, 1, -18, '', 0, 21, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 2, 2, 154, 0, 53, 65, 75, 75, 75, 75, 102, 102, 75, 41, 41, 41, 41, 41, 41, 5, 22, 1, -18, '', 0, 23, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 2, 2, 162, 0, 54, 66, 76, 76, 76, 76, 104, 104, 76, 44, 44, 44, 44, 44, 44, 6, 24, 1, -18, '', 0, 25, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 2, 2, 170, 0, 55, 67, 77, 77, 77, 77, 106, 106, 77, 47, 47, 47, 47, 47, 47, 6, 27, 1, -18, '', 0, 27, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 2, 2, 64, 59, 26, 16, 57, 57, 57, 57, 110, 110, 57, 17, 17, 17, 17, 17, 17, 1, 10, 1, -18, '', 0, 7, 5, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 2, 2, 68, 65, 27, 17, 58, 58, 58, 58, 113, 113, 58, 20, 20, 20, 20, 20, 20, 2, 11, 1, -18, '', 0, 8, 6, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 2, 2, 72, 71, 28, 17, 59, 59, 59, 59, 116, 116, 59, 23, 23, 23, 23, 23, 23, 2, 13, 1, -18, '', 0, 9, 7, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 2, 2, 76, 77, 28, 18, 60, 60, 60, 60, 119, 119, 60, 26, 26, 26, 26, 26, 26, 2, 14, 1, -18, '', 0, 10, 8, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 2, 2, 80, 83, 29, 18, 61, 61, 61, 61, 122, 122, 61, 29, 29, 29, 29, 29, 29, 2, 15, 1, -18, '', 0, 11, 9, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 2, 2, 114, 109, 32, 18, 63, 63, 63, 63, 135, 135, 63, 35, 35, 35, 35, 35, 35, 2, 14, 1, -18, '', 0, 12, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 2, 2, 118, 115, 33, 19, 64, 64, 64, 64, 138, 138, 64, 38, 38, 38, 38, 38, 38, 2, 15, 1, -18, '', 0, 13, 9, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 2, 2, 122, 121, 34, 19, 65, 65, 65, 65, 141, 141, 65, 41, 41, 41, 41, 41, 41, 3, 17, 1, -18, '', 0, 14, 10, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 2, 2, 126, 127, 34, 20, 66, 66, 66, 66, 144, 144, 66, 44, 44, 44, 44, 44, 44, 3, 18, 1, -18, '', 0, 15, 11, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 2, 2, 130, 133, 35, 20, 67, 67, 67, 67, 147, 147, 67, 47, 47, 47, 47, 47, 47, 3, 20, 1, -18, '', 0, 16, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 3, 3, 130, 0, 104, 84, 67, 67, 67, 67, 81, 81, 67, 19, 19, 19, 19, 19, 19, 3, 12, 1, -18, '', 0, 14, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 3, 3, 145, 0, 108, 86, 68, 68, 68, 68, 83, 83, 68, 22, 22, 22, 22, 22, 22, 4, 15, 1, -18, '', 0, 16, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 3, 3, 160, 0, 113, 87, 69, 69, 69, 69, 85, 85, 69, 25, 25, 25, 25, 25, 25, 4, 18, 1, -18, '', 0, 18, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 3, 3, 175, 0, 117, 89, 70, 70, 70, 70, 87, 87, 70, 28, 28, 28, 28, 28, 28, 5, 21, 1, -18, '', 0, 20, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 3, 3, 190, 0, 122, 90, 71, 71, 71, 71, 89, 89, 71, 31, 31, 31, 31, 31, 31, 5, 24, 1, -18, '', 0, 22, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 3, 3, 235, 0, 128, 91, 73, 73, 73, 73, 101, 101, 73, 37, 37, 37, 37, 37, 37, 5, 19, 1, -18, '', 0, 24, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 3, 3, 250, 0, 132, 93, 74, 74, 74, 74, 103, 103, 74, 40, 40, 40, 40, 40, 40, 5, 22, 1, -18, '', 0, 26, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 3, 3, 265, 0, 137, 94, 75, 75, 75, 75, 105, 105, 75, 43, 43, 43, 43, 43, 43, 6, 25, 1, -18, '', 0, 28, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 3, 3, 280, 0, 141, 96, 76, 76, 76, 76, 107, 107, 76, 46, 46, 46, 46, 46, 46, 6, 28, 1, -18, '', 0, 30, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 3, 3, 295, 0, 146, 97, 77, 77, 77, 77, 109, 109, 77, 49, 49, 49, 49, 49, 49, 7, 31, 1, -18, '', 0, 32, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 3, 3, 109, 91, 81, 51, 57, 57, 57, 57, 113, 113, 57, 19, 19, 19, 19, 19, 19, 2, 11, 1, -18, '', 0, 8, 5, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 3, 3, 118, 100, 84, 52, 58, 58, 58, 58, 116, 116, 58, 22, 22, 22, 22, 22, 22, 2, 13, 1, -18, '', 0, 9, 6, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 3, 3, 127, 109, 87, 53, 59, 59, 59, 59, 119, 119, 59, 25, 25, 25, 25, 25, 25, 2, 14, 1, -18, '', 0, 10, 7, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 3, 3, 136, 118, 90, 54, 60, 60, 60, 60, 122, 122, 60, 28, 28, 28, 28, 28, 28, 2, 15, 1, -18, '', 0, 11, 8, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 3, 3, 145, 127, 93, 55, 61, 61, 61, 61, 125, 125, 61, 31, 31, 31, 31, 31, 31, 3, 17, 1, -18, '', 0, 12, 9, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 3, 3, 199, 166, 99, 55, 63, 63, 63, 63, 138, 138, 63, 37, 37, 37, 37, 37, 37, 2, 15, 1, -18, '', 0, 14, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 3, 3, 208, 175, 102, 56, 64, 64, 64, 64, 141, 141, 64, 40, 40, 40, 40, 40, 40, 3, 17, 1, -18, '', 0, 15, 9, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 3, 3, 217, 184, 105, 57, 65, 65, 65, 65, 144, 144, 65, 43, 43, 43, 43, 43, 43, 3, 18, 1, -18, '', 0, 16, 10, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 3, 3, 226, 193, 108, 58, 66, 66, 66, 66, 147, 147, 66, 46, 46, 46, 46, 46, 46, 3, 20, 1, -18, '', 0, 17, 11, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 3, 3, 235, 202, 111, 59, 67, 67, 67, 67, 150, 150, 67, 49, 49, 49, 49, 49, 49, 3, 21, 1, -18, '', 0, 18, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 3, 3, 112, 0, 61, 85, 67, 67, 67, 67, 81, 81, 67, 19, 19, 19, 19, 19, 19, 3, 11, 1, -18, '', 0, 12, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 3, 3, 124, 0, 63, 87, 68, 68, 68, 68, 83, 83, 68, 22, 22, 22, 22, 22, 22, 4, 14, 1, -18, '', 0, 14, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 3, 3, 136, 0, 64, 88, 69, 69, 69, 69, 85, 85, 69, 25, 25, 25, 25, 25, 25, 4, 17, 1, -18, '', 0, 16, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 3, 3, 148, 0, 66, 90, 70, 70, 70, 70, 87, 87, 70, 28, 28, 28, 28, 28, 28, 5, 19, 1, -18, '', 0, 18, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 3, 3, 160, 0, 67, 91, 71, 71, 71, 71, 89, 89, 71, 31, 31, 31, 31, 31, 31, 5, 22, 1, -18, '', 0, 20, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 3, 3, 202, 0, 76, 94, 73, 73, 73, 73, 101, 101, 73, 37, 37, 37, 37, 37, 37, 5, 18, 1, -18, '', 0, 20, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 3, 3, 214, 0, 78, 96, 74, 74, 74, 74, 103, 103, 74, 40, 40, 40, 40, 40, 40, 5, 20, 1, -18, '', 0, 22, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 3, 3, 226, 0, 79, 97, 75, 75, 75, 75, 105, 105, 75, 43, 43, 43, 43, 43, 43, 6, 23, 1, -18, '', 0, 24, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 3, 3, 238, 0, 81, 99, 76, 76, 76, 76, 107, 107, 76, 46, 46, 46, 46, 46, 46, 6, 26, 1, -18, '', 0, 26, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 3, 3, 250, 0, 82, 100, 77, 77, 77, 77, 109, 109, 77, 49, 49, 49, 49, 49, 49, 7, 28, 1, -18, '', 0, 28, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 3, 3, 91, 91, 40, 24, 57, 57, 57, 57, 113, 113, 57, 19, 19, 19, 19, 19, 19, 2, 11, 1, -18, '', 0, 7, 5, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 3, 3, 97, 100, 41, 25, 58, 58, 58, 58, 116, 116, 58, 22, 22, 22, 22, 22, 22, 2, 13, 1, -18, '', 0, 8, 6, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 3, 3, 103, 109, 42, 26, 59, 59, 59, 59, 119, 119, 59, 25, 25, 25, 25, 25, 25, 2, 14, 1, -18, '', 0, 9, 7, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 3, 3, 109, 118, 43, 27, 60, 60, 60, 60, 122, 122, 60, 28, 28, 28, 28, 28, 28, 2, 15, 1, -18, '', 0, 10, 8, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 3, 3, 115, 127, 44, 27, 61, 61, 61, 61, 125, 125, 61, 31, 31, 31, 31, 31, 31, 3, 17, 1, -18, '', 0, 11, 9, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 3, 3, 166, 166, 49, 27, 63, 63, 63, 63, 138, 138, 63, 37, 37, 37, 37, 37, 37, 2, 15, 1, -18, '', 0, 12, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 3, 3, 172, 175, 50, 28, 64, 64, 64, 64, 141, 141, 64, 40, 40, 40, 40, 40, 40, 3, 17, 1, -18, '', 0, 13, 9, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 3, 3, 178, 184, 51, 29, 65, 65, 65, 65, 144, 144, 65, 43, 43, 43, 43, 43, 43, 3, 18, 1, -18, '', 0, 14, 10, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 3, 3, 184, 193, 52, 30, 66, 66, 66, 66, 147, 147, 66, 46, 46, 46, 46, 46, 46, 3, 20, 1, -18, '', 0, 15, 11, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 3, 3, 190, 202, 53, 30, 67, 67, 67, 67, 150, 150, 67, 49, 49, 49, 49, 49, 49, 3, 21, 1, -18, '', 0, 16, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 4, 4, 170, 0, 138, 112, 68, 68, 68, 68, 84, 84, 68, 22, 22, 22, 22, 22, 22, 4, 14, 1, -18, '', 0, 14, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 4, 4, 190, 0, 144, 114, 69, 69, 69, 69, 86, 86, 69, 26, 26, 26, 26, 26, 26, 4, 17, 1, -18, '', 0, 16, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 4, 4, 210, 0, 150, 116, 70, 70, 70, 70, 88, 88, 70, 30, 30, 30, 30, 30, 30, 5, 19, 1, -18, '', 0, 18, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 4, 4, 230, 0, 156, 118, 71, 71, 71, 71, 90, 90, 71, 34, 34, 34, 34, 34, 34, 5, 22, 1, -18, '', 0, 20, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 4, 4, 250, 0, 162, 120, 72, 72, 72, 72, 92, 92, 72, 38, 38, 38, 38, 38, 38, 6, 25, 1, -18, '', 0, 22, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 4, 4, 310, 0, 170, 122, 75, 75, 75, 75, 104, 104, 75, 40, 40, 40, 40, 40, 40, 5, 21, 1, -18, '', 0, 24, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 4, 4, 330, 0, 176, 124, 76, 76, 76, 76, 106, 106, 76, 44, 44, 44, 44, 44, 44, 5, 24, 1, -18, '', 0, 26, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 4, 4, 350, 0, 182, 126, 77, 77, 77, 77, 108, 108, 77, 48, 48, 48, 48, 48, 48, 6, 26, 1, -18, '', 0, 28, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 4, 4, 370, 0, 188, 128, 78, 78, 78, 78, 110, 110, 78, 52, 52, 52, 52, 52, 52, 6, 29, 1, -18, '', 0, 30, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 4, 4, 390, 0, 194, 130, 79, 79, 79, 79, 112, 112, 79, 56, 56, 56, 56, 56, 56, 7, 32, 1, -18, '', 0, 32, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 4, 4, 142, 125, 108, 67, 58, 58, 58, 58, 117, 117, 58, 22, 22, 22, 22, 22, 22, 2, 13, 1, -18, '', 0, 9, 5, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 4, 4, 154, 137, 112, 68, 59, 59, 59, 59, 120, 120, 59, 26, 26, 26, 26, 26, 26, 2, 14, 1, -18, '', 0, 10, 6, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 4, 4, 166, 149, 116, 70, 60, 60, 60, 60, 123, 123, 60, 30, 30, 30, 30, 30, 30, 2, 15, 1, -18, '', 0, 11, 7, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 4, 4, 178, 161, 120, 71, 61, 61, 61, 61, 126, 126, 61, 34, 34, 34, 34, 34, 34, 3, 17, 1, -18, '', 0, 12, 8, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 4, 4, 190, 173, 124, 72, 62, 62, 62, 62, 129, 129, 62, 38, 38, 38, 38, 38, 38, 3, 18, 1, -18, '', 0, 13, 9, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 4, 4, 262, 225, 132, 73, 65, 65, 65, 65, 142, 142, 65, 40, 40, 40, 40, 40, 40, 3, 17, 1, -18, '', 0, 15, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 4, 4, 274, 237, 136, 74, 66, 66, 66, 66, 145, 145, 66, 44, 44, 44, 44, 44, 44, 3, 18, 1, -18, '', 0, 16, 9, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 4, 4, 286, 249, 140, 76, 67, 67, 67, 67, 148, 148, 67, 48, 48, 48, 48, 48, 48, 3, 20, 1, -18, '', 0, 17, 10, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 4, 4, 298, 261, 144, 77, 68, 68, 68, 68, 151, 151, 68, 52, 52, 52, 52, 52, 52, 3, 21, 1, -18, '', 0, 18, 11, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 4, 4, 310, 273, 148, 78, 69, 69, 69, 69, 154, 154, 69, 56, 56, 56, 56, 56, 56, 4, 22, 1, -18, '', 0, 19, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 4, 4, 146, 0, 82, 114, 68, 68, 68, 68, 84, 84, 68, 22, 22, 22, 22, 22, 22, 4, 13, 1, -18, '', 0, 12, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 4, 4, 162, 0, 84, 116, 69, 69, 69, 69, 86, 86, 69, 26, 26, 26, 26, 26, 26, 4, 15, 1, -18, '', 0, 14, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 4, 4, 178, 0, 86, 118, 70, 70, 70, 70, 88, 88, 70, 30, 30, 30, 30, 30, 30, 5, 18, 1, -18, '', 0, 16, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 4, 4, 194, 0, 88, 120, 71, 71, 71, 71, 90, 90, 71, 34, 34, 34, 34, 34, 34, 5, 20, 1, -18, '', 0, 18, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 4, 4, 210, 0, 90, 122, 72, 72, 72, 72, 92, 92, 72, 38, 38, 38, 38, 38, 38, 6, 23, 1, -18, '', 0, 20, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 4, 4, 266, 0, 102, 126, 75, 75, 75, 75, 104, 104, 75, 40, 40, 40, 40, 40, 40, 5, 19, 1, -18, '', 0, 20, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 4, 4, 282, 0, 104, 128, 76, 76, 76, 76, 106, 106, 76, 44, 44, 44, 44, 44, 44, 5, 22, 1, -18, '', 0, 22, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 4, 4, 298, 0, 106, 130, 77, 77, 77, 77, 108, 108, 77, 48, 48, 48, 48, 48, 48, 6, 24, 1, -18, '', 0, 24, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 4, 4, 314, 0, 108, 132, 78, 78, 78, 78, 110, 110, 78, 52, 52, 52, 52, 52, 52, 6, 27, 1, -18, '', 0, 26, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 4, 4, 330, 0, 110, 134, 79, 79, 79, 79, 112, 112, 79, 56, 56, 56, 56, 56, 56, 7, 30, 1, -18, '', 0, 28, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 4, 4, 118, 125, 53, 33, 58, 58, 58, 58, 117, 117, 58, 22, 22, 22, 22, 22, 22, 2, 13, 1, -18, '', 0, 8, 5, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 4, 4, 126, 137, 54, 34, 59, 59, 59, 59, 120, 120, 59, 26, 26, 26, 26, 26, 26, 2, 14, 1, -18, '', 0, 9, 6, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 4, 4, 134, 149, 56, 35, 60, 60, 60, 60, 123, 123, 60, 30, 30, 30, 30, 30, 30, 2, 15, 1, -18, '', 0, 10, 7, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 4, 4, 142, 161, 57, 36, 61, 61, 61, 61, 126, 126, 61, 34, 34, 34, 34, 34, 34, 3, 17, 1, -18, '', 0, 11, 8, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 4, 4, 150, 173, 58, 37, 62, 62, 62, 62, 129, 129, 62, 38, 38, 38, 38, 38, 38, 3, 18, 1, -18, '', 0, 12, 9, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 4, 4, 218, 225, 65, 37, 65, 65, 65, 65, 142, 142, 65, 40, 40, 40, 40, 40, 40, 3, 17, 1, -18, '', 0, 13, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 4, 4, 226, 237, 66, 38, 66, 66, 66, 66, 145, 145, 66, 44, 44, 44, 44, 44, 44, 3, 18, 1, -18, '', 0, 14, 9, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 4, 4, 234, 249, 68, 39, 67, 67, 67, 67, 148, 148, 67, 48, 48, 48, 48, 48, 48, 3, 20, 1, -18, '', 0, 15, 10, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 4, 4, 242, 261, 69, 40, 68, 68, 68, 68, 151, 151, 68, 52, 52, 52, 52, 52, 52, 3, 21, 1, -18, '', 0, 16, 11, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 4, 4, 250, 273, 70, 41, 69, 69, 69, 69, 154, 154, 69, 56, 56, 56, 56, 56, 56, 4, 22, 1, -18, '', 0, 17, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 5, 5, 211, 0, 173, 140, 69, 69, 69, 69, 87, 87, 69, 24, 24, 24, 24, 24, 24, 4, 15, 1, -18, '', 0, 14, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 5, 5, 236, 0, 180, 143, 70, 70, 70, 70, 89, 89, 70, 28, 28, 28, 28, 28, 28, 4, 18, 1, -18, '', 0, 16, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 5, 5, 261, 0, 188, 145, 71, 71, 71, 71, 91, 91, 71, 32, 32, 32, 32, 32, 32, 5, 21, 1, -18, '', 0, 18, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 5, 5, 286, 0, 195, 148, 72, 72, 72, 72, 93, 93, 72, 36, 36, 36, 36, 36, 36, 5, 24, 1, -18, '', 0, 20, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 5, 5, 311, 0, 203, 150, 73, 73, 73, 73, 95, 95, 73, 40, 40, 40, 40, 40, 40, 6, 26, 1, -18, '', 0, 22, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 5, 5, 386, 0, 213, 152, 76, 76, 76, 76, 107, 107, 76, 44, 44, 44, 44, 44, 44, 5, 22, 1, -18, '', 0, 24, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 5, 5, 411, 0, 220, 155, 77, 77, 77, 77, 109, 109, 77, 48, 48, 48, 48, 48, 48, 6, 25, 1, -18, '', 0, 26, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 5, 5, 436, 0, 228, 157, 78, 78, 78, 78, 111, 111, 78, 52, 52, 52, 52, 52, 52, 6, 28, 1, -18, '', 0, 28, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 5, 5, 461, 0, 235, 160, 79, 79, 79, 79, 113, 113, 79, 56, 56, 56, 56, 56, 56, 7, 31, 1, -18, '', 0, 30, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 5, 5, 486, 0, 243, 162, 80, 80, 80, 80, 115, 115, 80, 60, 60, 60, 60, 60, 60, 7, 33, 1, -18, '', 0, 32, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 5, 5, 176, 160, 135, 84, 59, 59, 59, 59, 120, 120, 59, 24, 24, 24, 24, 24, 24, 2, 14, 1, -18, '', 0, 9, 6, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 5, 5, 191, 175, 140, 86, 60, 60, 60, 60, 123, 123, 60, 28, 28, 28, 28, 28, 28, 2, 15, 1, -18, '', 0, 10, 7, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 5, 5, 206, 190, 145, 88, 61, 61, 61, 61, 126, 126, 61, 32, 32, 32, 32, 32, 32, 3, 17, 1, -18, '', 0, 11, 8, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 5, 5, 221, 205, 150, 89, 62, 62, 62, 62, 129, 129, 62, 36, 36, 36, 36, 36, 36, 3, 18, 1, -18, '', 0, 12, 9, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 5, 5, 236, 220, 155, 91, 63, 63, 63, 63, 132, 132, 63, 40, 40, 40, 40, 40, 40, 3, 20, 1, -18, '', 0, 13, 10, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 5, 5, 326, 285, 165, 91, 66, 66, 66, 66, 145, 145, 66, 44, 44, 44, 44, 44, 44, 3, 18, 1, -18, '', 0, 15, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 5, 5, 341, 300, 170, 93, 67, 67, 67, 67, 148, 148, 67, 48, 48, 48, 48, 48, 48, 3, 20, 1, -18, '', 0, 16, 10, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 5, 5, 356, 315, 175, 95, 68, 68, 68, 68, 151, 151, 68, 52, 52, 52, 52, 52, 52, 3, 21, 1, -18, '', 0, 17, 11, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 5, 5, 371, 330, 180, 96, 69, 69, 69, 69, 154, 154, 69, 56, 56, 56, 56, 56, 56, 4, 22, 1, -18, '', 0, 18, 12, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 5, 5, 386, 345, 185, 98, 70, 70, 70, 70, 157, 157, 70, 60, 60, 60, 60, 60, 60, 4, 24, 1, -18, '', 0, 19, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 5, 5, 181, 0, 102, 142, 69, 69, 69, 69, 87, 87, 69, 24, 24, 24, 24, 24, 24, 4, 14, 1, -18, '', 0, 12, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 5, 5, 201, 0, 105, 145, 70, 70, 70, 70, 89, 89, 70, 28, 28, 28, 28, 28, 28, 4, 17, 1, -18, '', 0, 14, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 5, 5, 221, 0, 107, 147, 71, 71, 71, 71, 91, 91, 71, 32, 32, 32, 32, 32, 32, 5, 19, 1, -18, '', 0, 16, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 5, 5, 241, 0, 110, 150, 72, 72, 72, 72, 93, 93, 72, 36, 36, 36, 36, 36, 36, 5, 22, 1, -18, '', 0, 18, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 5, 5, 261, 0, 112, 152, 73, 73, 73, 73, 95, 95, 73, 40, 40, 40, 40, 40, 40, 6, 24, 1, -18, '', 0, 20, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 5, 5, 331, 0, 127, 157, 76, 76, 76, 76, 107, 107, 76, 44, 44, 44, 44, 44, 44, 5, 20, 1, -18, '', 0, 20, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 5, 5, 351, 0, 130, 160, 77, 77, 77, 77, 109, 109, 77, 48, 48, 48, 48, 48, 48, 6, 23, 1, -18, '', 0, 22, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 5, 5, 371, 0, 132, 162, 78, 78, 78, 78, 111, 111, 78, 52, 52, 52, 52, 52, 52, 6, 26, 1, -18, '', 0, 24, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 5, 5, 391, 0, 135, 165, 79, 79, 79, 79, 113, 113, 79, 56, 56, 56, 56, 56, 56, 7, 28, 1, -18, '', 0, 26, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 5, 5, 411, 0, 137, 167, 80, 80, 80, 80, 115, 115, 80, 60, 60, 60, 60, 60, 60, 7, 31, 1, -18, '', 0, 28, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 5, 5, 146, 160, 66, 41, 59, 59, 59, 59, 120, 120, 59, 24, 24, 24, 24, 24, 24, 2, 14, 1, -18, '', 0, 8, 6, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 5, 5, 156, 175, 68, 42, 60, 60, 60, 60, 123, 123, 60, 28, 28, 28, 28, 28, 28, 2, 15, 1, -18, '', 0, 9, 7, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 5, 5, 166, 190, 70, 43, 61, 61, 61, 61, 126, 126, 61, 32, 32, 32, 32, 32, 32, 3, 17, 1, -18, '', 0, 10, 8, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 5, 5, 176, 205, 71, 45, 62, 62, 62, 62, 129, 129, 62, 36, 36, 36, 36, 36, 36, 3, 18, 1, -18, '', 0, 11, 9, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 5, 5, 186, 220, 73, 46, 63, 63, 63, 63, 132, 132, 63, 40, 40, 40, 40, 40, 40, 3, 20, 1, -18, '', 0, 12, 10, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 5, 5, 271, 285, 81, 46, 66, 66, 66, 66, 145, 145, 66, 44, 44, 44, 44, 44, 44, 3, 18, 1, -18, '', 0, 13, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 5, 5, 281, 300, 83, 47, 67, 67, 67, 67, 148, 148, 67, 48, 48, 48, 48, 48, 48, 3, 20, 1, -18, '', 0, 14, 10, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 5, 5, 291, 315, 85, 48, 68, 68, 68, 68, 151, 151, 68, 52, 52, 52, 52, 52, 52, 3, 21, 1, -18, '', 0, 15, 11, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 5, 5, 301, 330, 86, 50, 69, 69, 69, 69, 154, 154, 69, 56, 56, 56, 56, 56, 56, 4, 22, 1, -18, '', 0, 16, 12, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 5, 5, 311, 345, 88, 51, 70, 70, 70, 70, 157, 157, 70, 60, 60, 60, 60, 60, 60, 4, 24, 1, -18, '', 0, 17, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 6, 6, 253, 0, 207, 168, 70, 70, 70, 70, 90, 90, 70, 26, 26, 26, 26, 26, 26, 4, 17, 1, -18, '', 0, 15, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 6, 6, 283, 0, 216, 171, 71, 71, 71, 71, 92, 92, 71, 30, 30, 30, 30, 30, 30, 5, 19, 1, -18, '', 0, 17, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 6, 6, 313, 0, 225, 174, 72, 72, 72, 72, 94, 94, 72, 34, 34, 34, 34, 34, 34, 5, 22, 1, -18, '', 0, 19, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 6, 6, 343, 0, 234, 177, 73, 73, 73, 73, 96, 96, 73, 38, 38, 38, 38, 38, 38, 6, 25, 1, -18, '', 0, 21, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 6, 6, 373, 0, 243, 180, 74, 74, 74, 74, 98, 98, 74, 42, 42, 42, 42, 42, 42, 6, 28, 1, -18, '', 0, 23, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 6, 6, 463, 0, 255, 183, 78, 78, 78, 78, 110, 110, 78, 46, 46, 46, 46, 46, 46, 5, 24, 1, -18, '', 0, 25, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 6, 6, 493, 0, 264, 186, 79, 79, 79, 79, 112, 112, 79, 50, 50, 50, 50, 50, 50, 6, 26, 1, -18, '', 0, 27, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 6, 6, 523, 0, 273, 189, 80, 80, 80, 80, 114, 114, 80, 54, 54, 54, 54, 54, 54, 6, 29, 1, -18, '', 0, 29, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 6, 6, 553, 0, 282, 192, 81, 81, 81, 81, 116, 116, 81, 58, 58, 58, 58, 58, 58, 7, 32, 1, -18, '', 0, 31, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 6, 6, 583, 0, 291, 195, 82, 82, 82, 82, 118, 118, 82, 62, 62, 62, 62, 62, 62, 7, 35, 1, -18, '', 0, 33, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 6, 6, 210, 197, 162, 101, 60, 60, 60, 60, 124, 124, 60, 26, 26, 26, 26, 26, 26, 2, 15, 1, -18, '', 0, 9, 6, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 6, 6, 228, 215, 168, 103, 61, 61, 61, 61, 127, 127, 61, 30, 30, 30, 30, 30, 30, 3, 17, 1, -18, '', 0, 10, 7, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 6, 6, 246, 233, 174, 105, 62, 62, 62, 62, 130, 130, 62, 34, 34, 34, 34, 34, 34, 3, 18, 1, -18, '', 0, 11, 8, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 6, 6, 264, 251, 180, 107, 63, 63, 63, 63, 133, 133, 63, 38, 38, 38, 38, 38, 38, 3, 20, 1, -18, '', 0, 12, 9, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 6, 6, 282, 269, 186, 109, 64, 64, 64, 64, 136, 136, 64, 42, 42, 42, 42, 42, 42, 3, 21, 1, -18, '', 0, 13, 10, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 6, 6, 390, 347, 198, 110, 68, 68, 68, 68, 149, 149, 68, 46, 46, 46, 46, 46, 46, 3, 20, 1, -18, '', 0, 15, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 6, 6, 408, 365, 204, 112, 69, 69, 69, 69, 152, 152, 69, 50, 50, 50, 50, 50, 50, 3, 21, 1, -18, '', 0, 16, 10, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 6, 6, 426, 383, 210, 114, 70, 70, 70, 70, 155, 155, 70, 54, 54, 54, 54, 54, 54, 4, 22, 1, -18, '', 0, 17, 11, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 6, 6, 444, 401, 216, 116, 71, 71, 71, 71, 158, 158, 71, 58, 58, 58, 58, 58, 58, 4, 24, 1, -18, '', 0, 18, 12, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 6, 6, 462, 419, 222, 118, 72, 72, 72, 72, 161, 161, 72, 62, 62, 62, 62, 62, 62, 4, 25, 1, -18, '', 0, 19, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 6, 6, 216, 0, 123, 171, 70, 70, 70, 70, 90, 90, 70, 26, 26, 26, 26, 26, 26, 4, 15, 1, -18, '', 0, 13, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 6, 6, 240, 0, 126, 174, 71, 71, 71, 71, 92, 92, 71, 30, 30, 30, 30, 30, 30, 5, 18, 1, -18, '', 0, 15, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 6, 6, 264, 0, 129, 177, 72, 72, 72, 72, 94, 94, 72, 34, 34, 34, 34, 34, 34, 5, 20, 1, -18, '', 0, 17, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 6, 6, 288, 0, 132, 180, 73, 73, 73, 73, 96, 96, 73, 38, 38, 38, 38, 38, 38, 6, 23, 1, -18, '', 0, 19, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 6, 6, 312, 0, 135, 183, 74, 74, 74, 74, 98, 98, 74, 42, 42, 42, 42, 42, 42, 6, 26, 1, -18, '', 0, 21, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 6, 6, 396, 0, 153, 189, 78, 78, 78, 78, 110, 110, 78, 46, 46, 46, 46, 46, 46, 5, 22, 1, -18, '', 0, 21, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 6, 6, 420, 0, 156, 192, 79, 79, 79, 79, 112, 112, 79, 50, 50, 50, 50, 50, 50, 6, 24, 1, -18, '', 0, 23, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 6, 6, 444, 0, 159, 195, 80, 80, 80, 80, 114, 114, 80, 54, 54, 54, 54, 54, 54, 6, 27, 1, -18, '', 0, 25, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 6, 6, 468, 0, 162, 198, 81, 81, 81, 81, 116, 116, 81, 58, 58, 58, 58, 58, 58, 7, 30, 1, -18, '', 0, 27, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 6, 6, 492, 0, 165, 201, 82, 82, 82, 82, 118, 118, 82, 62, 62, 62, 62, 62, 62, 7, 32, 1, -18, '', 0, 29, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 6, 6, 174, 197, 80, 49, 60, 60, 60, 60, 124, 124, 60, 26, 26, 26, 26, 26, 26, 2, 15, 1, -18, '', 0, 8, 6, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 6, 6, 186, 215, 82, 51, 61, 61, 61, 61, 127, 127, 61, 30, 30, 30, 30, 30, 30, 3, 17, 1, -18, '', 0, 9, 7, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 6, 6, 198, 233, 84, 52, 62, 62, 62, 62, 130, 130, 62, 34, 34, 34, 34, 34, 34, 3, 18, 1, -18, '', 0, 10, 8, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 6, 6, 210, 251, 86, 54, 63, 63, 63, 63, 133, 133, 63, 38, 38, 38, 38, 38, 38, 3, 20, 1, -18, '', 0, 11, 9, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 6, 6, 222, 269, 88, 55, 64, 64, 64, 64, 136, 136, 64, 42, 42, 42, 42, 42, 42, 3, 21, 1, -18, '', 0, 12, 10, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 6, 6, 324, 347, 98, 55, 68, 68, 68, 68, 149, 149, 68, 46, 46, 46, 46, 46, 46, 3, 20, 1, -18, '', 0, 13, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 6, 6, 336, 365, 100, 57, 69, 69, 69, 69, 152, 152, 69, 50, 50, 50, 50, 50, 50, 3, 21, 1, -18, '', 0, 14, 10, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 6, 6, 348, 383, 102, 58, 70, 70, 70, 70, 155, 155, 70, 54, 54, 54, 54, 54, 54, 4, 22, 1, -18, '', 0, 15, 11, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 6, 6, 360, 401, 104, 60, 71, 71, 71, 71, 158, 158, 71, 58, 58, 58, 58, 58, 58, 4, 24, 1, -18, '', 0, 16, 12, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 6, 6, 372, 419, 106, 61, 72, 72, 72, 72, 161, 161, 72, 62, 62, 62, 62, 62, 62, 4, 25, 1, -18, '', 0, 17, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 7, 7, 294, 0, 242, 196, 71, 71, 71, 71, 93, 93, 71, 28, 28, 28, 28, 28, 28, 4, 18, 1, -18, '', 0, 15, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 7, 7, 329, 0, 252, 200, 72, 72, 72, 72, 95, 95, 72, 32, 32, 32, 32, 32, 32, 5, 21, 1, -18, '', 0, 17, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 7, 7, 364, 0, 263, 203, 73, 73, 73, 73, 97, 97, 73, 36, 36, 36, 36, 36, 36, 5, 24, 1, -18, '', 0, 19, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 7, 7, 399, 0, 273, 207, 74, 74, 74, 74, 99, 99, 74, 40, 40, 40, 40, 40, 40, 6, 26, 1, -18, '', 0, 21, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 7, 7, 434, 0, 284, 210, 75, 75, 75, 75, 101, 101, 75, 44, 44, 44, 44, 44, 44, 6, 29, 1, -18, '', 0, 23, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 7, 7, 539, 0, 298, 213, 79, 79, 79, 79, 113, 113, 79, 48, 48, 48, 48, 48, 48, 6, 25, 1, -18, '', 0, 25, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 7, 7, 574, 0, 308, 217, 80, 80, 80, 80, 115, 115, 80, 52, 52, 52, 52, 52, 52, 6, 28, 1, -18, '', 0, 27, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 7, 7, 609, 0, 319, 220, 81, 81, 81, 81, 117, 117, 81, 56, 56, 56, 56, 56, 56, 7, 31, 1, -18, '', 0, 29, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 7, 7, 644, 0, 329, 224, 82, 82, 82, 82, 119, 119, 82, 60, 60, 60, 60, 60, 60, 7, 33, 1, -18, '', 0, 31, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 7, 7, 679, 0, 340, 227, 83, 83, 83, 83, 121, 121, 83, 64, 64, 64, 64, 64, 64, 8, 36, 1, -18, '', 0, 33, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 7, 7, 245, 235, 189, 118, 61, 61, 61, 61, 127, 127, 61, 28, 28, 28, 28, 28, 28, 3, 17, 1, -18, '', 0, 9, 6, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 7, 7, 266, 256, 196, 120, 62, 62, 62, 62, 130, 130, 62, 32, 32, 32, 32, 32, 32, 3, 18, 1, -18, '', 0, 10, 7, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 7, 7, 287, 277, 203, 123, 63, 63, 63, 63, 133, 133, 63, 36, 36, 36, 36, 36, 36, 3, 20, 1, -18, '', 0, 11, 8, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 7, 7, 308, 298, 210, 125, 64, 64, 64, 64, 136, 136, 64, 40, 40, 40, 40, 40, 40, 3, 21, 1, -18, '', 0, 12, 9, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 7, 7, 329, 319, 217, 127, 65, 65, 65, 65, 139, 139, 65, 44, 44, 44, 44, 44, 44, 4, 22, 1, -18, '', 0, 13, 10, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 7, 7, 455, 410, 231, 128, 69, 69, 69, 69, 152, 152, 69, 48, 48, 48, 48, 48, 48, 3, 21, 1, -18, '', 0, 15, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 7, 7, 476, 431, 238, 130, 70, 70, 70, 70, 155, 155, 70, 52, 52, 52, 52, 52, 52, 4, 22, 1, -18, '', 0, 16, 10, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 7, 7, 497, 452, 245, 133, 71, 71, 71, 71, 158, 158, 71, 56, 56, 56, 56, 56, 56, 4, 24, 1, -18, '', 0, 17, 11, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 7, 7, 518, 473, 252, 135, 72, 72, 72, 72, 161, 161, 72, 60, 60, 60, 60, 60, 60, 4, 25, 1, -18, '', 0, 18, 12, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 7, 7, 539, 494, 259, 137, 73, 73, 73, 73, 164, 164, 73, 64, 64, 64, 64, 64, 64, 4, 27, 1, -18, '', 0, 19, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 7, 7, 252, 0, 143, 199, 71, 71, 71, 71, 93, 93, 71, 28, 28, 28, 28, 28, 28, 4, 17, 1, -18, '', 0, 13, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 7, 7, 280, 0, 147, 203, 72, 72, 72, 72, 95, 95, 72, 32, 32, 32, 32, 32, 32, 5, 19, 1, -18, '', 0, 15, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 7, 7, 308, 0, 150, 206, 73, 73, 73, 73, 97, 97, 73, 36, 36, 36, 36, 36, 36, 5, 22, 1, -18, '', 0, 17, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 7, 7, 336, 0, 154, 210, 74, 74, 74, 74, 99, 99, 74, 40, 40, 40, 40, 40, 40, 6, 24, 1, -18, '', 0, 19, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 7, 7, 364, 0, 157, 213, 75, 75, 75, 75, 101, 101, 75, 44, 44, 44, 44, 44, 44, 6, 27, 1, -18, '', 0, 21, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 7, 7, 462, 0, 178, 220, 79, 79, 79, 79, 113, 113, 79, 48, 48, 48, 48, 48, 48, 6, 23, 1, -18, '', 0, 21, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 7, 7, 490, 0, 182, 224, 80, 80, 80, 80, 115, 115, 80, 52, 52, 52, 52, 52, 52, 6, 26, 1, -18, '', 0, 23, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 7, 7, 518, 0, 185, 227, 81, 81, 81, 81, 117, 117, 81, 56, 56, 56, 56, 56, 56, 7, 28, 1, -18, '', 0, 25, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 7, 7, 546, 0, 189, 231, 82, 82, 82, 82, 119, 119, 82, 60, 60, 60, 60, 60, 60, 7, 31, 1, -18, '', 0, 27, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 7, 7, 574, 0, 192, 234, 83, 83, 83, 83, 121, 121, 83, 64, 64, 64, 64, 64, 64, 8, 33, 1, -18, '', 0, 29, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 7, 7, 202, 235, 93, 57, 61, 61, 61, 61, 127, 127, 61, 28, 28, 28, 28, 28, 28, 3, 17, 1, -18, '', 0, 8, 6, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 7, 7, 216, 256, 95, 59, 62, 62, 62, 62, 130, 130, 62, 32, 32, 32, 32, 32, 32, 3, 18, 1, -18, '', 0, 9, 7, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 7, 7, 230, 277, 98, 61, 63, 63, 63, 63, 133, 133, 63, 36, 36, 36, 36, 36, 36, 3, 20, 1, -18, '', 0, 10, 8, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 7, 7, 244, 298, 100, 63, 64, 64, 64, 64, 136, 136, 64, 40, 40, 40, 40, 40, 40, 3, 21, 1, -18, '', 0, 11, 9, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 7, 7, 258, 319, 102, 64, 65, 65, 65, 65, 139, 139, 65, 44, 44, 44, 44, 44, 44, 4, 22, 1, -18, '', 0, 12, 10, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 7, 7, 377, 410, 114, 64, 69, 69, 69, 69, 152, 152, 69, 48, 48, 48, 48, 48, 48, 3, 21, 1, -18, '', 0, 13, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 7, 7, 391, 431, 116, 66, 70, 70, 70, 70, 155, 155, 70, 52, 52, 52, 52, 52, 52, 4, 22, 1, -18, '', 0, 14, 10, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 7, 7, 405, 452, 119, 68, 71, 71, 71, 71, 158, 158, 71, 56, 56, 56, 56, 56, 56, 4, 24, 1, -18, '', 0, 15, 11, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 7, 7, 419, 473, 121, 70, 72, 72, 72, 72, 161, 161, 72, 60, 60, 60, 60, 60, 60, 4, 25, 1, -18, '', 0, 16, 12, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 7, 7, 433, 494, 123, 71, 73, 73, 73, 73, 164, 164, 73, 64, 64, 64, 64, 64, 64, 4, 27, 1, -18, '', 0, 17, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 8, 8, 337, 0, 276, 224, 73, 73, 73, 73, 96, 96, 73, 31, 31, 31, 31, 31, 31, 5, 19, 1, -18, '', 0, 15, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 8, 8, 377, 0, 288, 228, 74, 74, 74, 74, 98, 98, 74, 36, 36, 36, 36, 36, 36, 5, 22, 1, -18, '', 0, 17, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 8, 8, 417, 0, 300, 232, 75, 75, 75, 75, 100, 100, 75, 41, 41, 41, 41, 41, 41, 6, 25, 1, -18, '', 0, 19, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 8, 8, 457, 0, 312, 236, 76, 76, 76, 76, 102, 102, 76, 46, 46, 46, 46, 46, 46, 6, 28, 1, -18, '', 0, 21, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 8, 8, 497, 0, 324, 240, 77, 77, 77, 77, 104, 104, 77, 51, 51, 51, 51, 51, 51, 7, 31, 1, -18, '', 0, 23, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 8, 8, 617, 0, 340, 244, 82, 82, 82, 82, 116, 116, 82, 53, 53, 53, 53, 53, 53, 6, 26, 1, -18, '', 0, 25, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 8, 8, 657, 0, 352, 248, 83, 83, 83, 83, 118, 118, 83, 58, 58, 58, 58, 58, 58, 6, 29, 1, -18, '', 0, 27, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 8, 8, 697, 0, 364, 252, 84, 84, 84, 84, 120, 120, 84, 63, 63, 63, 63, 63, 63, 7, 32, 1, -18, '', 0, 29, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 8, 8, 737, 0, 376, 256, 85, 85, 85, 85, 122, 122, 85, 68, 68, 68, 68, 68, 68, 7, 35, 1, -18, '', 0, 31, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 8, 8, 777, 0, 388, 260, 86, 86, 86, 86, 124, 124, 86, 73, 73, 73, 73, 73, 73, 8, 38, 1, -18, '', 0, 33, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 8, 8, 280, 275, 216, 134, 62, 62, 62, 62, 131, 131, 62, 31, 31, 31, 31, 31, 31, 3, 18, 1, -18, '', 0, 10, 6, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 8, 8, 304, 299, 224, 137, 63, 63, 63, 63, 134, 134, 63, 36, 36, 36, 36, 36, 36, 3, 20, 1, -18, '', 0, 11, 7, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 8, 8, 328, 323, 232, 140, 64, 64, 64, 64, 137, 137, 64, 41, 41, 41, 41, 41, 41, 3, 21, 1, -18, '', 0, 12, 8, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 8, 8, 352, 347, 240, 142, 65, 65, 65, 65, 140, 140, 65, 46, 46, 46, 46, 46, 46, 4, 22, 1, -18, '', 0, 13, 9, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 8, 8, 376, 371, 248, 145, 66, 66, 66, 66, 143, 143, 66, 51, 51, 51, 51, 51, 51, 4, 24, 1, -18, '', 0, 14, 10, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 8, 8, 520, 475, 264, 146, 71, 71, 71, 71, 156, 156, 71, 53, 53, 53, 53, 53, 53, 4, 22, 1, -18, '', 0, 16, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 8, 8, 544, 499, 272, 149, 72, 72, 72, 72, 159, 159, 72, 58, 58, 58, 58, 58, 58, 4, 24, 1, -18, '', 0, 17, 10, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 8, 8, 568, 523, 280, 152, 73, 73, 73, 73, 162, 162, 73, 63, 63, 63, 63, 63, 63, 4, 25, 1, -18, '', 0, 18, 11, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 8, 8, 592, 547, 288, 154, 74, 74, 74, 74, 165, 165, 74, 68, 68, 68, 68, 68, 68, 4, 27, 1, -18, '', 0, 19, 12, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 8, 8, 616, 571, 296, 157, 75, 75, 75, 75, 168, 168, 75, 73, 73, 73, 73, 73, 73, 5, 28, 1, -18, '', 0, 20, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 8, 8, 288, 0, 164, 228, 73, 73, 73, 73, 96, 96, 73, 31, 31, 31, 31, 31, 31, 5, 18, 1, -18, '', 0, 13, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 8, 8, 320, 0, 168, 232, 74, 74, 74, 74, 98, 98, 74, 36, 36, 36, 36, 36, 36, 5, 20, 1, -18, '', 0, 15, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 8, 8, 352, 0, 172, 236, 75, 75, 75, 75, 100, 100, 75, 41, 41, 41, 41, 41, 41, 6, 23, 1, -18, '', 0, 17, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 8, 8, 384, 0, 176, 240, 76, 76, 76, 76, 102, 102, 76, 46, 46, 46, 46, 46, 46, 6, 26, 1, -18, '', 0, 19, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 8, 8, 416, 0, 180, 244, 77, 77, 77, 77, 104, 104, 77, 51, 51, 51, 51, 51, 51, 7, 28, 1, -18, '', 0, 21, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 8, 8, 528, 0, 204, 252, 82, 82, 82, 82, 116, 116, 82, 53, 53, 53, 53, 53, 53, 6, 24, 1, -18, '', 0, 21, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 8, 8, 560, 0, 208, 256, 83, 83, 83, 83, 118, 118, 83, 58, 58, 58, 58, 58, 58, 6, 27, 1, -18, '', 0, 23, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 8, 8, 592, 0, 212, 260, 84, 84, 84, 84, 120, 120, 84, 63, 63, 63, 63, 63, 63, 7, 30, 1, -18, '', 0, 25, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 8, 8, 624, 0, 216, 264, 85, 85, 85, 85, 122, 122, 85, 68, 68, 68, 68, 68, 68, 7, 32, 1, -18, '', 0, 27, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 8, 8, 656, 0, 220, 268, 86, 86, 86, 86, 124, 124, 86, 73, 73, 73, 73, 73, 73, 8, 35, 1, -18, '', 0, 29, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 8, 8, 231, 275, 106, 66, 62, 62, 62, 62, 131, 131, 62, 31, 31, 31, 31, 31, 31, 3, 18, 1, -18, '', 0, 9, 6, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 8, 8, 247, 299, 109, 68, 63, 63, 63, 63, 134, 134, 63, 36, 36, 36, 36, 36, 36, 3, 20, 1, -18, '', 0, 10, 7, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 8, 8, 263, 323, 112, 70, 64, 64, 64, 64, 137, 137, 64, 41, 41, 41, 41, 41, 41, 3, 21, 1, -18, '', 0, 11, 8, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 8, 8, 279, 347, 114, 72, 65, 65, 65, 65, 140, 140, 65, 46, 46, 46, 46, 46, 46, 4, 22, 1, -18, '', 0, 12, 9, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 8, 8, 295, 371, 117, 74, 66, 66, 66, 66, 143, 143, 66, 51, 51, 51, 51, 51, 51, 4, 24, 1, -18, '', 0, 13, 10, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 8, 8, 431, 475, 130, 74, 71, 71, 71, 71, 156, 156, 71, 53, 53, 53, 53, 53, 53, 4, 22, 1, -18, '', 0, 14, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 8, 8, 447, 499, 133, 76, 72, 72, 72, 72, 159, 159, 72, 58, 58, 58, 58, 58, 58, 4, 24, 1, -18, '', 0, 15, 10, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 8, 8, 463, 523, 136, 78, 73, 73, 73, 73, 162, 162, 73, 63, 63, 63, 63, 63, 63, 4, 25, 1, -18, '', 0, 16, 11, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 8, 8, 479, 547, 138, 80, 74, 74, 74, 74, 165, 165, 74, 68, 68, 68, 68, 68, 68, 4, 27, 1, -18, '', 0, 17, 12, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 8, 8, 495, 571, 141, 82, 75, 75, 75, 75, 168, 168, 75, 73, 73, 73, 73, 73, 73, 5, 28, 1, -18, '', 0, 18, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 9, 9, 380, 0, 311, 252, 74, 74, 74, 74, 99, 99, 74, 33, 33, 33, 33, 33, 33, 5, 21, 1, -18, '', 0, 16, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 9, 9, 425, 0, 324, 257, 75, 75, 75, 75, 101, 101, 75, 38, 38, 38, 38, 38, 38, 5, 24, 1, -18, '', 0, 18, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 9, 9, 470, 0, 338, 261, 76, 76, 76, 76, 103, 103, 76, 43, 43, 43, 43, 43, 43, 6, 26, 1, -18, '', 0, 20, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 9, 9, 515, 0, 351, 266, 77, 77, 77, 77, 105, 105, 77, 48, 48, 48, 48, 48, 48, 6, 29, 1, -18, '', 0, 22, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 9, 9, 560, 0, 365, 270, 78, 78, 78, 78, 107, 107, 78, 53, 53, 53, 53, 53, 53, 7, 32, 1, -18, '', 0, 24, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 9, 9, 695, 0, 383, 274, 83, 83, 83, 83, 119, 119, 83, 55, 55, 55, 55, 55, 55, 6, 28, 1, -18, '', 0, 26, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 9, 9, 740, 0, 396, 279, 84, 84, 84, 84, 121, 121, 84, 60, 60, 60, 60, 60, 60, 7, 31, 1, -18, '', 0, 28, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 9, 9, 785, 0, 410, 283, 85, 85, 85, 85, 123, 123, 85, 65, 65, 65, 65, 65, 65, 7, 33, 1, -18, '', 0, 30, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 9, 9, 830, 0, 423, 288, 86, 86, 86, 86, 125, 125, 86, 70, 70, 70, 70, 70, 70, 8, 36, 1, -18, '', 0, 32, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 9, 9, 875, 0, 437, 292, 87, 87, 87, 87, 127, 127, 87, 75, 75, 75, 75, 75, 75, 8, 39, 1, -18, '', 0, 34, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 9, 9, 315, 317, 243, 152, 63, 63, 63, 63, 134, 134, 63, 33, 33, 33, 33, 33, 33, 3, 20, 1, -18, '', 0, 10, 6, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 9, 9, 342, 344, 252, 155, 64, 64, 64, 64, 137, 137, 64, 38, 38, 38, 38, 38, 38, 3, 21, 1, -18, '', 0, 11, 7, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 9, 9, 369, 371, 261, 158, 65, 65, 65, 65, 140, 140, 65, 43, 43, 43, 43, 43, 43, 4, 22, 1, -18, '', 0, 12, 8, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 9, 9, 396, 398, 270, 161, 66, 66, 66, 66, 143, 143, 66, 48, 48, 48, 48, 48, 48, 4, 24, 1, -18, '', 0, 13, 9, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 9, 9, 423, 425, 279, 164, 67, 67, 67, 67, 146, 146, 67, 53, 53, 53, 53, 53, 53, 4, 25, 1, -18, '', 0, 14, 10, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 9, 9, 585, 542, 297, 165, 72, 72, 72, 72, 159, 159, 72, 55, 55, 55, 55, 55, 55, 4, 24, 1, -18, '', 0, 16, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 9, 9, 612, 569, 306, 168, 73, 73, 73, 73, 162, 162, 73, 60, 60, 60, 60, 60, 60, 4, 25, 1, -18, '', 0, 17, 10, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 9, 9, 639, 596, 315, 171, 74, 74, 74, 74, 165, 165, 74, 65, 65, 65, 65, 65, 65, 4, 27, 1, -18, '', 0, 18, 11, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 9, 9, 666, 623, 324, 174, 75, 75, 75, 75, 168, 168, 75, 70, 70, 70, 70, 70, 70, 5, 28, 1, -18, '', 0, 19, 12, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 9, 9, 693, 650, 333, 177, 76, 76, 76, 76, 171, 171, 76, 75, 75, 75, 75, 75, 75, 5, 29, 1, -18, '', 0, 20, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 9, 9, 325, 0, 184, 256, 74, 74, 74, 74, 99, 99, 74, 33, 33, 33, 33, 33, 33, 5, 19, 1, -18, '', 0, 14, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 9, 9, 361, 0, 189, 261, 75, 75, 75, 75, 101, 101, 75, 38, 38, 38, 38, 38, 38, 5, 22, 1, -18, '', 0, 16, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 9, 9, 397, 0, 193, 265, 76, 76, 76, 76, 103, 103, 76, 43, 43, 43, 43, 43, 43, 6, 24, 1, -18, '', 0, 18, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 9, 9, 433, 0, 198, 270, 77, 77, 77, 77, 105, 105, 77, 48, 48, 48, 48, 48, 48, 6, 27, 1, -18, '', 0, 20, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 9, 9, 469, 0, 202, 274, 78, 78, 78, 78, 107, 107, 78, 53, 53, 53, 53, 53, 53, 7, 30, 1, -18, '', 0, 22, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 9, 9, 595, 0, 229, 283, 83, 83, 83, 83, 119, 119, 83, 55, 55, 55, 55, 55, 55, 6, 26, 1, -18, '', 0, 22, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 9, 9, 631, 0, 234, 288, 84, 84, 84, 84, 121, 121, 84, 60, 60, 60, 60, 60, 60, 7, 28, 1, -18, '', 0, 24, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 9, 9, 667, 0, 238, 292, 85, 85, 85, 85, 123, 123, 85, 65, 65, 65, 65, 65, 65, 7, 31, 1, -18, '', 0, 26, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 9, 9, 703, 0, 243, 297, 86, 86, 86, 86, 125, 125, 86, 70, 70, 70, 70, 70, 70, 8, 33, 1, -18, '', 0, 28, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 9, 9, 739, 0, 247, 301, 87, 87, 87, 87, 127, 127, 87, 75, 75, 75, 75, 75, 75, 8, 36, 1, -18, '', 0, 30, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 9, 9, 261, 317, 120, 74, 63, 63, 63, 63, 134, 134, 63, 33, 33, 33, 33, 33, 33, 3, 20, 1, -18, '', 0, 9, 6, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 9, 9, 279, 344, 123, 76, 64, 64, 64, 64, 137, 137, 64, 38, 38, 38, 38, 38, 38, 3, 21, 1, -18, '', 0, 10, 7, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 9, 9, 297, 371, 126, 78, 65, 65, 65, 65, 140, 140, 65, 43, 43, 43, 43, 43, 43, 4, 22, 1, -18, '', 0, 11, 8, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 9, 9, 315, 398, 129, 81, 66, 66, 66, 66, 143, 143, 66, 48, 48, 48, 48, 48, 48, 4, 24, 1, -18, '', 0, 12, 9, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 9, 9, 333, 425, 132, 83, 67, 67, 67, 67, 146, 146, 67, 53, 53, 53, 53, 53, 53, 4, 25, 1, -18, '', 0, 13, 10, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 9, 9, 486, 542, 147, 83, 72, 72, 72, 72, 159, 159, 72, 55, 55, 55, 55, 55, 55, 4, 24, 1, -18, '', 0, 14, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 9, 9, 504, 569, 150, 85, 73, 73, 73, 73, 162, 162, 73, 60, 60, 60, 60, 60, 60, 4, 25, 1, -18, '', 0, 15, 10, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 9, 9, 522, 596, 153, 87, 74, 74, 74, 74, 165, 165, 74, 65, 65, 65, 65, 65, 65, 4, 27, 1, -18, '', 0, 16, 11, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 9, 9, 540, 623, 156, 90, 75, 75, 75, 75, 168, 168, 75, 70, 70, 70, 70, 70, 70, 5, 28, 1, -18, '', 0, 17, 12, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 9, 9, 558, 650, 159, 92, 76, 76, 76, 76, 171, 171, 76, 75, 75, 75, 75, 75, 75, 5, 29, 1, -18, '', 0, 18, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 10, 10, 424, 0, 345, 280, 77, 77, 77, 77, 102, 102, 77, 35, 35, 35, 35, 35, 35, 5, 22, 1, -18, '', 0, 16, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 10, 10, 474, 0, 360, 285, 79, 79, 79, 79, 104, 104, 79, 40, 40, 40, 40, 40, 40, 6, 25, 1, -18, '', 0, 18, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 10, 10, 524, 0, 375, 290, 81, 81, 81, 81, 106, 106, 81, 45, 45, 45, 45, 45, 45, 6, 28, 1, -18, '', 0, 20, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 10, 10, 574, 0, 390, 295, 83, 83, 83, 83, 108, 108, 83, 50, 50, 50, 50, 50, 50, 7, 31, 1, -18, '', 0, 22, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 10, 10, 624, 0, 405, 300, 85, 85, 85, 85, 110, 110, 85, 55, 55, 55, 55, 55, 55, 7, 33, 1, -18, '', 0, 24, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 10, 10, 774, 0, 425, 305, 87, 87, 87, 87, 122, 122, 87, 57, 57, 57, 57, 57, 57, 6, 29, 1, -18, '', 0, 26, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 10, 10, 824, 0, 440, 310, 89, 89, 89, 89, 124, 124, 89, 62, 62, 62, 62, 62, 62, 7, 32, 1, -18, '', 0, 28, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 10, 10, 874, 0, 455, 315, 91, 91, 91, 91, 126, 126, 91, 67, 67, 67, 67, 67, 67, 7, 35, 1, -18, '', 0, 30, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 10, 10, 924, 0, 470, 320, 93, 93, 93, 93, 128, 128, 93, 72, 72, 72, 72, 72, 72, 8, 38, 1, -18, '', 0, 32, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 10, 10, 974, 0, 485, 325, 95, 95, 95, 95, 130, 130, 95, 77, 77, 77, 77, 77, 77, 8, 40, 1, -18, '', 0, 34, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 10, 10, 351, 360, 270, 168, 66, 66, 66, 66, 138, 138, 66, 35, 35, 35, 35, 35, 35, 3, 21, 1, -18, '', 0, 10, 7, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 10, 10, 381, 390, 280, 171, 68, 68, 68, 68, 141, 141, 68, 40, 40, 40, 40, 40, 40, 4, 22, 1, -18, '', 0, 11, 8, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 10, 10, 411, 420, 290, 175, 70, 70, 70, 70, 144, 144, 70, 45, 45, 45, 45, 45, 45, 4, 24, 1, -18, '', 0, 12, 9, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 10, 10, 441, 450, 300, 178, 72, 72, 72, 72, 147, 147, 72, 50, 50, 50, 50, 50, 50, 4, 25, 1, -18, '', 0, 13, 10, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 10, 10, 471, 480, 310, 181, 74, 74, 74, 74, 150, 150, 74, 55, 55, 55, 55, 55, 55, 4, 27, 1, -18, '', 0, 14, 11, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 10, 10, 651, 610, 330, 183, 76, 76, 76, 76, 163, 163, 76, 57, 57, 57, 57, 57, 57, 4, 25, 1, -18, '', 0, 16, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 10, 10, 681, 640, 340, 186, 78, 78, 78, 78, 166, 166, 78, 62, 62, 62, 62, 62, 62, 4, 27, 1, -18, '', 0, 17, 11, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 10, 10, 711, 670, 350, 190, 80, 80, 80, 80, 169, 169, 80, 67, 67, 67, 67, 67, 67, 5, 28, 1, -18, '', 0, 18, 12, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 10, 10, 741, 700, 360, 193, 82, 82, 82, 82, 172, 172, 82, 72, 72, 72, 72, 72, 72, 5, 29, 1, -18, '', 0, 19, 13, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 10, 10, 771, 730, 370, 196, 84, 84, 84, 84, 175, 175, 84, 77, 77, 77, 77, 77, 77, 5, 31, 1, -18, '', 0, 20, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 10, 10, 362, 0, 205, 285, 77, 77, 77, 77, 102, 102, 77, 35, 35, 35, 35, 35, 35, 5, 20, 1, -18, '', 0, 14, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 10, 10, 402, 0, 210, 290, 79, 79, 79, 79, 104, 104, 79, 40, 40, 40, 40, 40, 40, 6, 23, 1, -18, '', 0, 16, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 10, 10, 442, 0, 215, 295, 81, 81, 81, 81, 106, 106, 81, 45, 45, 45, 45, 45, 45, 6, 26, 1, -18, '', 0, 18, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 10, 10, 482, 0, 220, 300, 83, 83, 83, 83, 108, 108, 83, 50, 50, 50, 50, 50, 50, 7, 28, 1, -18, '', 0, 20, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 10, 10, 522, 0, 225, 305, 85, 85, 85, 85, 110, 110, 85, 55, 55, 55, 55, 55, 55, 7, 31, 1, -18, '', 0, 22, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 10, 10, 662, 0, 255, 315, 87, 87, 87, 87, 122, 122, 87, 57, 57, 57, 57, 57, 57, 6, 27, 1, -18, '', 0, 22, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 10, 10, 702, 0, 260, 320, 89, 89, 89, 89, 124, 124, 89, 62, 62, 62, 62, 62, 62, 7, 30, 1, -18, '', 0, 24, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 10, 10, 742, 0, 265, 325, 91, 91, 91, 91, 126, 126, 91, 67, 67, 67, 67, 67, 67, 7, 32, 1, -18, '', 0, 26, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 10, 10, 782, 0, 270, 330, 93, 93, 93, 93, 128, 128, 93, 72, 72, 72, 72, 72, 72, 8, 35, 1, -18, '', 0, 28, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 10, 10, 822, 0, 275, 335, 95, 95, 95, 95, 130, 130, 95, 77, 77, 77, 77, 77, 77, 8, 37, 1, -18, '', 0, 30, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 10, 10, 291, 360, 133, 82, 66, 66, 66, 66, 138, 138, 66, 35, 35, 35, 35, 35, 35, 3, 21, 1, -18, '', 0, 9, 7, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 10, 10, 311, 390, 136, 85, 68, 68, 68, 68, 141, 141, 68, 40, 40, 40, 40, 40, 40, 4, 22, 1, -18, '', 0, 10, 8, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 10, 10, 331, 420, 140, 87, 70, 70, 70, 70, 144, 144, 70, 45, 45, 45, 45, 45, 45, 4, 24, 1, -18, '', 0, 11, 9, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 10, 10, 351, 450, 143, 90, 72, 72, 72, 72, 147, 147, 72, 50, 50, 50, 50, 50, 50, 4, 25, 1, -18, '', 0, 12, 10, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 10, 10, 371, 480, 146, 92, 74, 74, 74, 74, 150, 150, 74, 55, 55, 55, 55, 55, 55, 4, 27, 1, -18, '', 0, 13, 11, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 10, 10, 541, 610, 163, 92, 76, 76, 76, 76, 163, 163, 76, 57, 57, 57, 57, 57, 57, 4, 25, 1, -18, '', 0, 14, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 10, 10, 561, 640, 166, 95, 78, 78, 78, 78, 166, 166, 78, 62, 62, 62, 62, 62, 62, 4, 27, 1, -18, '', 0, 15, 11, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 10, 10, 581, 670, 170, 97, 80, 80, 80, 80, 169, 169, 80, 67, 67, 67, 67, 67, 67, 5, 28, 1, -18, '', 0, 16, 12, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 10, 10, 601, 700, 173, 100, 82, 82, 82, 82, 172, 172, 82, 72, 72, 72, 72, 72, 72, 5, 29, 1, -18, '', 0, 17, 13, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 10, 10, 621, 730, 176, 102, 84, 84, 84, 84, 175, 175, 84, 77, 77, 77, 77, 77, 77, 5, 31, 1, -18, '', 0, 18, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 11, 11, 469, 0, 380, 308, 78, 78, 78, 78, 105, 105, 78, 37, 37, 37, 37, 37, 37, 5, 24, 1, -18, '', 0, 16, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 11, 11, 524, 0, 396, 314, 80, 80, 80, 80, 107, 107, 80, 42, 42, 42, 42, 42, 42, 6, 26, 1, -18, '', 0, 18, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 11, 11, 579, 0, 413, 319, 82, 82, 82, 82, 109, 109, 82, 47, 47, 47, 47, 47, 47, 6, 29, 1, -18, '', 0, 20, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 11, 11, 634, 0, 429, 325, 84, 84, 84, 84, 111, 111, 84, 52, 52, 52, 52, 52, 52, 7, 32, 1, -18, '', 0, 22, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 11, 11, 689, 0, 446, 330, 86, 86, 86, 86, 113, 113, 86, 57, 57, 57, 57, 57, 57, 7, 35, 1, -18, '', 0, 24, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 11, 11, 854, 0, 468, 335, 88, 88, 88, 88, 125, 125, 88, 61, 61, 61, 61, 61, 61, 7, 31, 1, -18, '', 0, 26, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 11, 11, 909, 0, 484, 341, 90, 90, 90, 90, 127, 127, 90, 66, 66, 66, 66, 66, 66, 7, 33, 1, -18, '', 0, 28, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 11, 11, 964, 0, 501, 346, 92, 92, 92, 92, 129, 129, 92, 71, 71, 71, 71, 71, 71, 8, 36, 1, -18, '', 0, 30, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 11, 11, 1019, 0, 517, 352, 94, 94, 94, 94, 131, 131, 94, 76, 76, 76, 76, 76, 76, 8, 39, 1, -18, '', 0, 32, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 11, 11, 1074, 0, 534, 357, 96, 96, 96, 96, 133, 133, 96, 81, 81, 81, 81, 81, 81, 9, 42, 1, -18, '', 0, 34, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 11, 11, 388, 405, 297, 185, 67, 67, 67, 67, 141, 141, 67, 37, 37, 37, 37, 37, 37, 4, 22, 1, -18, '', 0, 10, 7, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 11, 11, 421, 438, 308, 189, 69, 69, 69, 69, 144, 144, 69, 42, 42, 42, 42, 42, 42, 4, 24, 1, -18, '', 0, 11, 8, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 11, 11, 454, 471, 319, 193, 71, 71, 71, 71, 147, 147, 71, 47, 47, 47, 47, 47, 47, 4, 25, 1, -18, '', 0, 12, 9, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 11, 11, 487, 504, 330, 196, 73, 73, 73, 73, 150, 150, 73, 52, 52, 52, 52, 52, 52, 4, 27, 1, -18, '', 0, 13, 10, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 11, 11, 520, 537, 341, 200, 75, 75, 75, 75, 153, 153, 75, 57, 57, 57, 57, 57, 57, 5, 28, 1, -18, '', 0, 14, 11, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 11, 11, 718, 680, 363, 201, 77, 77, 77, 77, 166, 166, 77, 61, 61, 61, 61, 61, 61, 4, 27, 1, -18, '', 0, 16, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 11, 11, 751, 713, 374, 205, 79, 79, 79, 79, 169, 169, 79, 66, 66, 66, 66, 66, 66, 5, 28, 1, -18, '', 0, 17, 11, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 11, 11, 784, 746, 385, 209, 81, 81, 81, 81, 172, 172, 81, 71, 71, 71, 71, 71, 71, 5, 29, 1, -18, '', 0, 18, 12, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 11, 11, 817, 779, 396, 212, 83, 83, 83, 83, 175, 175, 83, 76, 76, 76, 76, 76, 76, 5, 31, 1, -18, '', 0, 19, 13, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 11, 11, 850, 812, 407, 216, 85, 85, 85, 85, 178, 178, 85, 81, 81, 81, 81, 81, 81, 5, 32, 1, -18, '', 0, 20, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 11, 11, 400, 0, 225, 313, 78, 78, 78, 78, 105, 105, 78, 37, 37, 37, 37, 37, 37, 5, 22, 1, -18, '', 0, 14, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 11, 11, 444, 0, 231, 319, 80, 80, 80, 80, 107, 107, 80, 42, 42, 42, 42, 42, 42, 6, 24, 1, -18, '', 0, 16, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 11, 11, 488, 0, 236, 324, 82, 82, 82, 82, 109, 109, 82, 47, 47, 47, 47, 47, 47, 6, 27, 1, -18, '', 0, 18, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 11, 11, 532, 0, 242, 330, 84, 84, 84, 84, 111, 111, 84, 52, 52, 52, 52, 52, 52, 7, 30, 1, -18, '', 0, 20, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 11, 11, 576, 0, 247, 335, 86, 86, 86, 86, 113, 113, 86, 57, 57, 57, 57, 57, 57, 7, 32, 1, -18, '', 0, 22, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 11, 11, 730, 0, 280, 346, 88, 88, 88, 88, 125, 125, 88, 61, 61, 61, 61, 61, 61, 7, 28, 1, -18, '', 0, 22, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 11, 11, 774, 0, 286, 352, 90, 90, 90, 90, 127, 127, 90, 66, 66, 66, 66, 66, 66, 7, 31, 1, -18, '', 0, 24, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 11, 11, 818, 0, 291, 357, 92, 92, 92, 92, 129, 129, 92, 71, 71, 71, 71, 71, 71, 8, 33, 1, -18, '', 0, 26, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 11, 11, 862, 0, 297, 363, 94, 94, 94, 94, 131, 131, 94, 76, 76, 76, 76, 76, 76, 8, 36, 1, -18, '', 0, 28, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 11, 11, 906, 0, 302, 368, 96, 96, 96, 96, 133, 133, 96, 81, 81, 81, 81, 81, 81, 9, 39, 1, -18, '', 0, 30, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 11, 11, 321, 405, 146, 90, 67, 67, 67, 67, 141, 141, 67, 37, 37, 37, 37, 37, 37, 4, 22, 1, -18, '', 0, 9, 7, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 11, 11, 343, 438, 150, 93, 69, 69, 69, 69, 144, 144, 69, 42, 42, 42, 42, 42, 42, 4, 24, 1, -18, '', 0, 10, 8, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 11, 11, 365, 471, 154, 96, 71, 71, 71, 71, 147, 147, 71, 47, 47, 47, 47, 47, 47, 4, 25, 1, -18, '', 0, 11, 9, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 11, 11, 387, 504, 157, 99, 73, 73, 73, 73, 150, 150, 73, 52, 52, 52, 52, 52, 52, 4, 27, 1, -18, '', 0, 12, 10, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 11, 11, 409, 537, 161, 101, 75, 75, 75, 75, 153, 153, 75, 57, 57, 57, 57, 57, 57, 5, 28, 1, -18, '', 0, 13, 11, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 11, 11, 596, 680, 179, 101, 77, 77, 77, 77, 166, 166, 77, 61, 61, 61, 61, 61, 61, 4, 27, 1, -18, '', 0, 14, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 11, 11, 618, 713, 183, 104, 79, 79, 79, 79, 169, 169, 79, 66, 66, 66, 66, 66, 66, 5, 28, 1, -18, '', 0, 15, 11, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 11, 11, 640, 746, 187, 107, 81, 81, 81, 81, 172, 172, 81, 71, 71, 71, 71, 71, 71, 5, 29, 1, -18, '', 0, 16, 12, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 11, 11, 662, 779, 190, 110, 83, 83, 83, 83, 175, 175, 83, 76, 76, 76, 76, 76, 76, 5, 31, 1, -18, '', 0, 17, 13, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 11, 11, 684, 812, 194, 112, 85, 85, 85, 85, 178, 178, 85, 81, 81, 81, 81, 81, 81, 5, 32, 1, -18, '', 0, 18, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 12, 12, 514, 0, 414, 336, 80, 80, 80, 80, 108, 108, 80, 40, 40, 40, 40, 40, 40, 6, 25, 1, -18, '', 0, 17, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 12, 12, 574, 0, 432, 342, 82, 82, 82, 82, 110, 110, 82, 46, 46, 46, 46, 46, 46, 6, 28, 1, -18, '', 0, 19, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 12, 12, 634, 0, 450, 348, 84, 84, 84, 84, 112, 112, 84, 52, 52, 52, 52, 52, 52, 7, 31, 1, -18, '', 0, 21, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 12, 12, 694, 0, 468, 354, 86, 86, 86, 86, 114, 114, 86, 58, 58, 58, 58, 58, 58, 7, 33, 1, -18, '', 0, 23, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 12, 12, 754, 0, 486, 360, 88, 88, 88, 88, 116, 116, 88, 64, 64, 64, 64, 64, 64, 8, 36, 1, -18, '', 0, 25, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 12, 12, 934, 0, 510, 366, 91, 91, 91, 91, 128, 128, 91, 64, 64, 64, 64, 64, 64, 7, 32, 1, -18, '', 0, 27, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 12, 12, 994, 0, 528, 372, 93, 93, 93, 93, 130, 130, 93, 70, 70, 70, 70, 70, 70, 7, 35, 1, -18, '', 0, 29, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 12, 12, 1054, 0, 546, 378, 95, 95, 95, 95, 132, 132, 95, 76, 76, 76, 76, 76, 76, 8, 38, 1, -18, '', 0, 31, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 12, 12, 1114, 0, 564, 384, 97, 97, 97, 97, 134, 134, 97, 82, 82, 82, 82, 82, 82, 8, 40, 1, -18, '', 0, 33, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 12, 12, 1174, 0, 582, 390, 99, 99, 99, 99, 136, 136, 99, 88, 88, 88, 88, 88, 88, 9, 43, 1, -18, '', 0, 35, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 12, 12, 426, 451, 324, 202, 69, 69, 69, 69, 145, 145, 69, 40, 40, 40, 40, 40, 40, 4, 24, 1, -18, '', 0, 11, 7, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 12, 12, 462, 487, 336, 206, 71, 71, 71, 71, 148, 148, 71, 46, 46, 46, 46, 46, 46, 4, 25, 1, -18, '', 0, 12, 8, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 12, 12, 498, 523, 348, 210, 73, 73, 73, 73, 151, 151, 73, 52, 52, 52, 52, 52, 52, 4, 27, 1, -18, '', 0, 13, 9, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 12, 12, 534, 559, 360, 214, 75, 75, 75, 75, 154, 154, 75, 58, 58, 58, 58, 58, 58, 5, 28, 1, -18, '', 0, 14, 10, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 12, 12, 570, 595, 372, 218, 77, 77, 77, 77, 157, 157, 77, 64, 64, 64, 64, 64, 64, 5, 29, 1, -18, '', 0, 15, 11, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 12, 12, 786, 751, 396, 220, 80, 80, 80, 80, 170, 170, 80, 64, 64, 64, 64, 64, 64, 5, 28, 1, -18, '', 0, 17, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 12, 12, 822, 787, 408, 224, 82, 82, 82, 82, 173, 173, 82, 70, 70, 70, 70, 70, 70, 5, 29, 1, -18, '', 0, 18, 11, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 12, 12, 858, 823, 420, 228, 84, 84, 84, 84, 176, 176, 84, 76, 76, 76, 76, 76, 76, 5, 31, 1, -18, '', 0, 19, 12, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 12, 12, 894, 859, 432, 232, 86, 86, 86, 86, 179, 179, 86, 82, 82, 82, 82, 82, 82, 5, 32, 1, -18, '', 0, 20, 13, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 12, 12, 930, 895, 444, 236, 88, 88, 88, 88, 182, 182, 88, 88, 88, 88, 88, 88, 88, 6, 34, 1, -18, '', 0, 21, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 12, 12, 439, 0, 246, 342, 80, 80, 80, 80, 108, 108, 80, 40, 40, 40, 40, 40, 40, 6, 23, 1, -18, '', 0, 15, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 12, 12, 487, 0, 252, 348, 82, 82, 82, 82, 110, 110, 82, 46, 46, 46, 46, 46, 46, 6, 26, 1, -18, '', 0, 17, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 12, 12, 535, 0, 258, 354, 84, 84, 84, 84, 112, 112, 84, 52, 52, 52, 52, 52, 52, 7, 28, 1, -18, '', 0, 19, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 12, 12, 583, 0, 264, 360, 86, 86, 86, 86, 114, 114, 86, 58, 58, 58, 58, 58, 58, 7, 31, 1, -18, '', 0, 21, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 12, 12, 631, 0, 270, 366, 88, 88, 88, 88, 116, 116, 88, 64, 64, 64, 64, 64, 64, 8, 33, 1, -18, '', 0, 23, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 12, 12, 799, 0, 306, 378, 91, 91, 91, 91, 128, 128, 91, 64, 64, 64, 64, 64, 64, 7, 30, 1, -18, '', 0, 23, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 12, 12, 847, 0, 312, 384, 93, 93, 93, 93, 130, 130, 93, 70, 70, 70, 70, 70, 70, 7, 32, 1, -18, '', 0, 25, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 12, 12, 895, 0, 318, 390, 95, 95, 95, 95, 132, 132, 95, 76, 76, 76, 76, 76, 76, 8, 35, 1, -18, '', 0, 27, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 12, 12, 943, 0, 324, 396, 97, 97, 97, 97, 134, 134, 97, 82, 82, 82, 82, 82, 82, 8, 37, 1, -18, '', 0, 29, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 12, 12, 991, 0, 330, 402, 99, 99, 99, 99, 136, 136, 99, 88, 88, 88, 88, 88, 88, 9, 40, 1, -18, '', 0, 31, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 12, 12, 353, 451, 160, 99, 69, 69, 69, 69, 145, 145, 69, 40, 40, 40, 40, 40, 40, 4, 24, 1, -18, '', 0, 10, 7, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 12, 12, 377, 487, 164, 102, 71, 71, 71, 71, 148, 148, 71, 46, 46, 46, 46, 46, 46, 4, 25, 1, -18, '', 0, 11, 8, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 12, 12, 401, 523, 168, 105, 73, 73, 73, 73, 151, 151, 73, 52, 52, 52, 52, 52, 52, 4, 27, 1, -18, '', 0, 12, 9, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 12, 12, 425, 559, 172, 108, 75, 75, 75, 75, 154, 154, 75, 58, 58, 58, 58, 58, 58, 5, 28, 1, -18, '', 0, 13, 10, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 12, 12, 449, 595, 176, 111, 77, 77, 77, 77, 157, 157, 77, 64, 64, 64, 64, 64, 64, 5, 29, 1, -18, '', 0, 14, 11, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 12, 12, 653, 751, 196, 111, 80, 80, 80, 80, 170, 170, 80, 64, 64, 64, 64, 64, 64, 5, 28, 1, -18, '', 0, 15, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 12, 12, 677, 787, 200, 114, 82, 82, 82, 82, 173, 173, 82, 70, 70, 70, 70, 70, 70, 5, 29, 1, -18, '', 0, 16, 11, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 12, 12, 701, 823, 204, 117, 84, 84, 84, 84, 176, 176, 84, 76, 76, 76, 76, 76, 76, 5, 31, 1, -18, '', 0, 17, 12, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 12, 12, 725, 859, 208, 120, 86, 86, 86, 86, 179, 179, 86, 82, 82, 82, 82, 82, 82, 5, 32, 1, -18, '', 0, 18, 13, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 12, 12, 749, 895, 212, 123, 88, 88, 88, 88, 182, 182, 88, 88, 88, 88, 88, 88, 88, 6, 34, 1, -18, '', 0, 19, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 13, 13, 561, 0, 449, 364, 81, 81, 81, 81, 111, 111, 81, 42, 42, 42, 42, 42, 42, 6, 26, 1, -18, '', 0, 17, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 13, 13, 626, 0, 468, 371, 83, 83, 83, 83, 113, 113, 83, 48, 48, 48, 48, 48, 48, 6, 29, 1, -18, '', 0, 19, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 13, 13, 691, 0, 488, 377, 85, 85, 85, 85, 115, 115, 85, 54, 54, 54, 54, 54, 54, 7, 32, 1, -18, '', 0, 21, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 13, 13, 756, 0, 507, 384, 87, 87, 87, 87, 117, 117, 87, 60, 60, 60, 60, 60, 60, 7, 35, 1, -18, '', 0, 23, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 13, 13, 821, 0, 527, 390, 89, 89, 89, 89, 119, 119, 89, 66, 66, 66, 66, 66, 66, 8, 38, 1, -18, '', 0, 25, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 13, 13, 1016, 0, 553, 396, 92, 92, 92, 92, 131, 131, 92, 66, 66, 66, 66, 66, 66, 7, 33, 1, -18, '', 0, 27, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 13, 13, 1081, 0, 572, 403, 94, 94, 94, 94, 133, 133, 94, 72, 72, 72, 72, 72, 72, 8, 36, 1, -18, '', 0, 29, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 13, 13, 1146, 0, 592, 409, 96, 96, 96, 96, 135, 135, 96, 78, 78, 78, 78, 78, 78, 8, 39, 1, -18, '', 0, 31, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 13, 13, 1211, 0, 611, 416, 98, 98, 98, 98, 137, 137, 98, 84, 84, 84, 84, 84, 84, 9, 42, 1, -18, '', 0, 33, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 13, 13, 1276, 0, 631, 422, 100, 100, 100, 100, 139, 139, 100, 90, 90, 90, 90, 90, 90, 9, 45, 1, -18, '', 0, 35, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 13, 13, 464, 499, 351, 219, 70, 70, 70, 70, 148, 148, 70, 42, 42, 42, 42, 42, 42, 4, 25, 1, -18, '', 0, 11, 7, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 13, 13, 503, 538, 364, 223, 72, 72, 72, 72, 151, 151, 72, 48, 48, 48, 48, 48, 48, 4, 27, 1, -18, '', 0, 12, 8, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 13, 13, 542, 577, 377, 228, 74, 74, 74, 74, 154, 154, 74, 54, 54, 54, 54, 54, 54, 5, 28, 1, -18, '', 0, 13, 9, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 13, 13, 581, 616, 390, 232, 76, 76, 76, 76, 157, 157, 76, 60, 60, 60, 60, 60, 60, 5, 29, 1, -18, '', 0, 14, 10, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 13, 13, 620, 655, 403, 236, 78, 78, 78, 78, 160, 160, 78, 66, 66, 66, 66, 66, 66, 5, 31, 1, -18, '', 0, 15, 11, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 13, 13, 854, 824, 429, 238, 81, 81, 81, 81, 173, 173, 81, 66, 66, 66, 66, 66, 66, 5, 29, 1, -18, '', 0, 17, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 13, 13, 893, 863, 442, 242, 83, 83, 83, 83, 176, 176, 83, 72, 72, 72, 72, 72, 72, 5, 31, 1, -18, '', 0, 18, 11, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 13, 13, 932, 902, 455, 247, 85, 85, 85, 85, 179, 179, 85, 78, 78, 78, 78, 78, 78, 5, 32, 1, -18, '', 0, 19, 12, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 13, 13, 971, 941, 468, 251, 87, 87, 87, 87, 182, 182, 87, 84, 84, 84, 84, 84, 84, 6, 34, 1, -18, '', 0, 20, 13, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 13, 13, 1010, 980, 481, 255, 89, 89, 89, 89, 185, 185, 89, 90, 90, 90, 90, 90, 90, 6, 35, 1, -18, '', 0, 21, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 13, 13, 479, 0, 266, 370, 81, 81, 81, 81, 111, 111, 81, 42, 42, 42, 42, 42, 42, 6, 24, 1, -18, '', 0, 15, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 13, 13, 531, 0, 273, 377, 83, 83, 83, 83, 113, 113, 83, 48, 48, 48, 48, 48, 48, 6, 27, 1, -18, '', 0, 17, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 13, 13, 583, 0, 279, 383, 85, 85, 85, 85, 115, 115, 85, 54, 54, 54, 54, 54, 54, 7, 30, 1, -18, '', 0, 19, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 13, 13, 635, 0, 286, 390, 87, 87, 87, 87, 117, 117, 87, 60, 60, 60, 60, 60, 60, 7, 32, 1, -18, '', 0, 21, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 13, 13, 687, 0, 292, 396, 89, 89, 89, 89, 119, 119, 89, 66, 66, 66, 66, 66, 66, 8, 35, 1, -18, '', 0, 23, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 13, 13, 869, 0, 331, 409, 92, 92, 92, 92, 131, 131, 92, 66, 66, 66, 66, 66, 66, 7, 31, 1, -18, '', 0, 23, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 13, 13, 921, 0, 338, 416, 94, 94, 94, 94, 133, 133, 94, 72, 72, 72, 72, 72, 72, 8, 33, 1, -18, '', 0, 25, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 13, 13, 973, 0, 344, 422, 96, 96, 96, 96, 135, 135, 96, 78, 78, 78, 78, 78, 78, 8, 36, 1, -18, '', 0, 27, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 13, 13, 1025, 0, 351, 429, 98, 98, 98, 98, 137, 137, 98, 84, 84, 84, 84, 84, 84, 9, 39, 1, -18, '', 0, 29, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 13, 13, 1077, 0, 357, 435, 100, 100, 100, 100, 139, 139, 100, 90, 90, 90, 90, 90, 90, 9, 41, 1, -18, '', 0, 31, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 13, 13, 385, 499, 173, 107, 70, 70, 70, 70, 148, 148, 70, 42, 42, 42, 42, 42, 42, 4, 25, 1, -18, '', 0, 10, 7, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 13, 13, 411, 538, 177, 110, 72, 72, 72, 72, 151, 151, 72, 48, 48, 48, 48, 48, 48, 4, 27, 1, -18, '', 0, 11, 8, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 13, 13, 437, 577, 182, 113, 74, 74, 74, 74, 154, 154, 74, 54, 54, 54, 54, 54, 54, 5, 28, 1, -18, '', 0, 12, 9, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 13, 13, 463, 616, 186, 117, 76, 76, 76, 76, 157, 157, 76, 60, 60, 60, 60, 60, 60, 5, 29, 1, -18, '', 0, 13, 10, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 13, 13, 489, 655, 190, 120, 78, 78, 78, 78, 160, 160, 78, 66, 66, 66, 66, 66, 66, 5, 31, 1, -18, '', 0, 14, 11, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 13, 13, 710, 824, 212, 120, 81, 81, 81, 81, 173, 173, 81, 66, 66, 66, 66, 66, 66, 5, 29, 1, -18, '', 0, 15, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 13, 13, 736, 863, 216, 123, 83, 83, 83, 83, 176, 176, 83, 72, 72, 72, 72, 72, 72, 5, 31, 1, -18, '', 0, 16, 11, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 13, 13, 762, 902, 221, 126, 85, 85, 85, 85, 179, 179, 85, 78, 78, 78, 78, 78, 78, 5, 32, 1, -18, '', 0, 17, 12, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 13, 13, 788, 941, 225, 130, 87, 87, 87, 87, 182, 182, 87, 84, 84, 84, 84, 84, 84, 6, 34, 1, -18, '', 0, 18, 13, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 13, 13, 814, 980, 229, 133, 89, 89, 89, 89, 185, 185, 89, 90, 90, 90, 90, 90, 90, 6, 35, 1, -18, '', 0, 19, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 14, 14, 609, 0, 483, 392, 83, 83, 83, 83, 114, 114, 83, 44, 44, 44, 44, 44, 44, 6, 28, 1, -18, '', 0, 17, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 14, 14, 679, 0, 504, 399, 85, 85, 85, 85, 116, 116, 85, 50, 50, 50, 50, 50, 50, 7, 31, 1, -18, '', 0, 19, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 14, 14, 749, 0, 525, 406, 87, 87, 87, 87, 118, 118, 87, 56, 56, 56, 56, 56, 56, 7, 33, 1, -18, '', 0, 21, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 14, 14, 819, 0, 546, 413, 89, 89, 89, 89, 120, 120, 89, 62, 62, 62, 62, 62, 62, 8, 36, 1, -18, '', 0, 23, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 14, 14, 889, 0, 567, 420, 91, 91, 91, 91, 122, 122, 91, 68, 68, 68, 68, 68, 68, 8, 39, 1, -18, '', 0, 25, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 14, 14, 1099, 0, 595, 427, 95, 95, 95, 95, 134, 134, 95, 70, 70, 70, 70, 70, 70, 7, 35, 1, -18, '', 0, 27, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 14, 14, 1169, 0, 616, 434, 97, 97, 97, 97, 136, 136, 97, 76, 76, 76, 76, 76, 76, 8, 38, 1, -18, '', 0, 29, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 14, 14, 1239, 0, 637, 441, 99, 99, 99, 99, 138, 138, 99, 82, 82, 82, 82, 82, 82, 8, 40, 1, -18, '', 0, 31, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 14, 14, 1309, 0, 658, 448, 101, 101, 101, 101, 140, 140, 101, 88, 88, 88, 88, 88, 88, 9, 43, 1, -18, '', 0, 33, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 14, 14, 1379, 0, 679, 455, 103, 103, 103, 103, 142, 142, 103, 94, 94, 94, 94, 94, 94, 9, 46, 1, -18, '', 0, 35, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 14, 14, 504, 549, 378, 235, 72, 72, 72, 72, 152, 152, 72, 44, 44, 44, 44, 44, 44, 4, 27, 1, -18, '', 0, 11, 7, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 14, 14, 546, 591, 392, 240, 74, 74, 74, 74, 155, 155, 74, 50, 50, 50, 50, 50, 50, 5, 28, 1, -18, '', 0, 12, 8, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 14, 14, 588, 633, 406, 245, 76, 76, 76, 76, 158, 158, 76, 56, 56, 56, 56, 56, 56, 5, 29, 1, -18, '', 0, 13, 9, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 14, 14, 630, 675, 420, 249, 78, 78, 78, 78, 161, 161, 78, 62, 62, 62, 62, 62, 62, 5, 31, 1, -18, '', 0, 14, 10, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 14, 14, 672, 717, 434, 254, 80, 80, 80, 80, 164, 164, 80, 68, 68, 68, 68, 68, 68, 5, 32, 1, -18, '', 0, 15, 11, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 14, 14, 924, 899, 462, 256, 84, 84, 84, 84, 177, 177, 84, 70, 70, 70, 70, 70, 70, 5, 31, 1, -18, '', 0, 17, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 14, 14, 966, 941, 476, 261, 86, 86, 86, 86, 180, 180, 86, 76, 76, 76, 76, 76, 76, 5, 32, 1, -18, '', 0, 18, 11, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 14, 14, 1008, 983, 490, 266, 88, 88, 88, 88, 183, 183, 88, 82, 82, 82, 82, 82, 82, 6, 34, 1, -18, '', 0, 19, 12, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 14, 14, 1050, 1025, 504, 270, 90, 90, 90, 90, 186, 186, 90, 88, 88, 88, 88, 88, 88, 6, 35, 1, -18, '', 0, 20, 13, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 14, 14, 1092, 1067, 518, 275, 92, 92, 92, 92, 189, 189, 92, 94, 94, 94, 94, 94, 94, 6, 36, 1, -18, '', 0, 21, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 14, 14, 520, 0, 287, 399, 83, 83, 83, 83, 114, 114, 83, 44, 44, 44, 44, 44, 44, 6, 26, 1, -18, '', 0, 15, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 14, 14, 576, 0, 294, 406, 85, 85, 85, 85, 116, 116, 85, 50, 50, 50, 50, 50, 50, 7, 28, 1, -18, '', 0, 17, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 14, 14, 632, 0, 301, 413, 87, 87, 87, 87, 118, 118, 87, 56, 56, 56, 56, 56, 56, 7, 31, 1, -18, '', 0, 19, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 14, 14, 688, 0, 308, 420, 89, 89, 89, 89, 120, 120, 89, 62, 62, 62, 62, 62, 62, 8, 33, 1, -18, '', 0, 21, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 14, 14, 744, 0, 315, 427, 91, 91, 91, 91, 122, 122, 91, 68, 68, 68, 68, 68, 68, 8, 36, 1, -18, '', 0, 23, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 14, 14, 940, 0, 357, 441, 95, 95, 95, 95, 134, 134, 95, 70, 70, 70, 70, 70, 70, 7, 32, 1, -18, '', 0, 23, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 14, 14, 996, 0, 364, 448, 97, 97, 97, 97, 136, 136, 97, 76, 76, 76, 76, 76, 76, 8, 35, 1, -18, '', 0, 25, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 14, 14, 1052, 0, 371, 455, 99, 99, 99, 99, 138, 138, 99, 82, 82, 82, 82, 82, 82, 8, 37, 1, -18, '', 0, 27, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 14, 14, 1108, 0, 378, 462, 101, 101, 101, 101, 140, 140, 101, 88, 88, 88, 88, 88, 88, 9, 40, 1, -18, '', 0, 29, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 14, 14, 1164, 0, 385, 469, 103, 103, 103, 103, 142, 142, 103, 94, 94, 94, 94, 94, 94, 9, 43, 1, -18, '', 0, 31, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 14, 14, 418, 549, 186, 115, 72, 72, 72, 72, 152, 152, 72, 44, 44, 44, 44, 44, 44, 4, 27, 1, -18, '', 0, 10, 7, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 14, 14, 446, 591, 191, 119, 74, 74, 74, 74, 155, 155, 74, 50, 50, 50, 50, 50, 50, 5, 28, 1, -18, '', 0, 11, 8, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 14, 14, 474, 633, 196, 122, 76, 76, 76, 76, 158, 158, 76, 56, 56, 56, 56, 56, 56, 5, 29, 1, -18, '', 0, 12, 9, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 14, 14, 502, 675, 200, 126, 78, 78, 78, 78, 161, 161, 78, 62, 62, 62, 62, 62, 62, 5, 31, 1, -18, '', 0, 13, 10, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 14, 14, 530, 717, 205, 129, 80, 80, 80, 80, 164, 164, 80, 68, 68, 68, 68, 68, 68, 5, 32, 1, -18, '', 0, 14, 11, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 14, 14, 768, 899, 228, 129, 84, 84, 84, 84, 177, 177, 84, 70, 70, 70, 70, 70, 70, 5, 31, 1, -18, '', 0, 15, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 14, 14, 796, 941, 233, 133, 86, 86, 86, 86, 180, 180, 86, 76, 76, 76, 76, 76, 76, 5, 32, 1, -18, '', 0, 16, 11, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 14, 14, 824, 983, 238, 136, 88, 88, 88, 88, 183, 183, 88, 82, 82, 82, 82, 82, 82, 6, 34, 1, -18, '', 0, 17, 12, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 14, 14, 852, 1025, 242, 140, 90, 90, 90, 90, 186, 186, 90, 88, 88, 88, 88, 88, 88, 6, 35, 1, -18, '', 0, 18, 13, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 14, 14, 880, 1067, 247, 143, 92, 92, 92, 92, 189, 189, 92, 94, 94, 94, 94, 94, 94, 6, 36, 1, -18, '', 0, 19, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 15, 15, 658, 0, 518, 420, 85, 85, 85, 85, 117, 117, 85, 46, 46, 46, 46, 46, 46, 6, 29, 1, -18, '', 0, 18, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 15, 15, 733, 0, 540, 428, 87, 87, 87, 87, 119, 119, 87, 52, 52, 52, 52, 52, 52, 7, 32, 1, -18, '', 0, 20, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 15, 15, 808, 0, 563, 435, 89, 89, 89, 89, 121, 121, 89, 58, 58, 58, 58, 58, 58, 7, 35, 1, -18, '', 0, 22, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 15, 15, 883, 0, 585, 443, 91, 91, 91, 91, 123, 123, 91, 64, 64, 64, 64, 64, 64, 8, 38, 1, -18, '', 0, 24, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 15, 15, 958, 0, 608, 450, 93, 93, 93, 93, 125, 125, 93, 70, 70, 70, 70, 70, 70, 8, 40, 1, -18, '', 0, 26, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 15, 15, 1183, 0, 638, 457, 97, 97, 97, 97, 137, 137, 97, 72, 72, 72, 72, 72, 72, 8, 36, 1, -18, '', 0, 28, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 15, 15, 1258, 0, 660, 465, 99, 99, 99, 99, 139, 139, 99, 78, 78, 78, 78, 78, 78, 8, 39, 1, -18, '', 0, 30, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 15, 15, 1333, 0, 683, 472, 101, 101, 101, 101, 141, 141, 101, 84, 84, 84, 84, 84, 84, 9, 42, 1, -18, '', 0, 32, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 15, 15, 1408, 0, 705, 480, 103, 103, 103, 103, 143, 143, 103, 90, 90, 90, 90, 90, 90, 9, 45, 1, -18, '', 0, 34, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 15, 15, 1483, 0, 728, 487, 105, 105, 105, 105, 145, 145, 105, 96, 96, 96, 96, 96, 96, 10, 47, 1, -18, '', 0, 36, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 15, 15, 544, 600, 405, 253, 74, 74, 74, 74, 155, 155, 74, 46, 46, 46, 46, 46, 46, 5, 28, 1, -18, '', 0, 11, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 15, 15, 589, 645, 420, 258, 76, 76, 76, 76, 158, 158, 76, 52, 52, 52, 52, 52, 52, 5, 29, 1, -18, '', 0, 12, 9, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 15, 15, 634, 690, 435, 263, 78, 78, 78, 78, 161, 161, 78, 58, 58, 58, 58, 58, 58, 5, 31, 1, -18, '', 0, 13, 10, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 15, 15, 679, 735, 450, 268, 80, 80, 80, 80, 164, 164, 80, 64, 64, 64, 64, 64, 64, 5, 32, 1, -18, '', 0, 14, 11, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 15, 15, 724, 780, 465, 273, 82, 82, 82, 82, 167, 167, 82, 70, 70, 70, 70, 70, 70, 6, 34, 1, -18, '', 0, 15, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 15, 15, 994, 975, 495, 275, 86, 86, 86, 86, 180, 180, 86, 72, 72, 72, 72, 72, 72, 5, 32, 1, -18, '', 0, 17, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 15, 15, 1039, 1020, 510, 280, 88, 88, 88, 88, 183, 183, 88, 78, 78, 78, 78, 78, 78, 6, 34, 1, -18, '', 0, 18, 12, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 15, 15, 1084, 1065, 525, 285, 90, 90, 90, 90, 186, 186, 90, 84, 84, 84, 84, 84, 84, 6, 35, 1, -18, '', 0, 19, 13, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 15, 15, 1129, 1110, 540, 290, 92, 92, 92, 92, 189, 189, 92, 90, 90, 90, 90, 90, 90, 6, 36, 1, -18, '', 0, 20, 14, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 15, 15, 1174, 1155, 555, 295, 94, 94, 94, 94, 192, 192, 94, 96, 96, 96, 96, 96, 96, 6, 38, 1, -18, '', 0, 21, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 15, 15, 562, 0, 307, 427, 85, 85, 85, 85, 117, 117, 85, 46, 46, 46, 46, 46, 46, 6, 27, 1, -18, '', 0, 16, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 15, 15, 622, 0, 315, 435, 87, 87, 87, 87, 119, 119, 87, 52, 52, 52, 52, 52, 52, 7, 30, 1, -18, '', 0, 18, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 15, 15, 682, 0, 322, 442, 89, 89, 89, 89, 121, 121, 89, 58, 58, 58, 58, 58, 58, 7, 32, 1, -18, '', 0, 20, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 15, 15, 742, 0, 330, 450, 91, 91, 91, 91, 123, 123, 91, 64, 64, 64, 64, 64, 64, 8, 35, 1, -18, '', 0, 22, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 15, 15, 802, 0, 337, 457, 93, 93, 93, 93, 125, 125, 93, 70, 70, 70, 70, 70, 70, 8, 37, 1, -18, '', 0, 24, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 15, 15, 1012, 0, 382, 472, 97, 97, 97, 97, 137, 137, 97, 72, 72, 72, 72, 72, 72, 8, 33, 1, -18, '', 0, 24, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 15, 15, 1072, 0, 390, 480, 99, 99, 99, 99, 139, 139, 99, 78, 78, 78, 78, 78, 78, 8, 36, 1, -18, '', 0, 26, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 15, 15, 1132, 0, 397, 487, 101, 101, 101, 101, 141, 141, 101, 84, 84, 84, 84, 84, 84, 9, 39, 1, -18, '', 0, 28, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 15, 15, 1192, 0, 405, 495, 103, 103, 103, 103, 143, 143, 103, 90, 90, 90, 90, 90, 90, 9, 41, 1, -18, '', 0, 30, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 15, 15, 1252, 0, 412, 502, 105, 105, 105, 105, 145, 145, 105, 96, 96, 96, 96, 96, 96, 10, 44, 1, -18, '', 0, 32, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 15, 15, 452, 600, 200, 123, 73, 73, 73, 73, 155, 155, 73, 46, 46, 46, 46, 46, 46, 5, 28, 1, -18, '', 0, 10, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 15, 15, 482, 645, 205, 127, 75, 75, 75, 75, 158, 158, 75, 52, 52, 52, 52, 52, 52, 5, 29, 1, -18, '', 0, 11, 9, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 15, 15, 512, 690, 210, 131, 77, 77, 77, 77, 161, 161, 77, 58, 58, 58, 58, 58, 58, 5, 31, 1, -18, '', 0, 12, 10, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 15, 15, 542, 735, 215, 135, 79, 79, 79, 79, 164, 164, 79, 64, 64, 64, 64, 64, 64, 5, 32, 1, -18, '', 0, 13, 11, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 15, 15, 572, 780, 220, 138, 81, 81, 81, 81, 167, 167, 81, 70, 70, 70, 70, 70, 70, 6, 34, 1, -18, '', 0, 14, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 15, 15, 827, 975, 245, 138, 85, 85, 85, 85, 180, 180, 85, 72, 72, 72, 72, 72, 72, 5, 32, 1, -18, '', 0, 15, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 15, 15, 857, 1020, 250, 142, 87, 87, 87, 87, 183, 183, 87, 78, 78, 78, 78, 78, 78, 6, 34, 1, -18, '', 0, 16, 12, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 15, 15, 887, 1065, 255, 146, 89, 89, 89, 89, 186, 186, 89, 84, 84, 84, 84, 84, 84, 6, 35, 1, -18, '', 0, 17, 13, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 15, 15, 917, 1110, 260, 150, 91, 91, 91, 91, 189, 189, 91, 90, 90, 90, 90, 90, 90, 6, 36, 1, -18, '', 0, 18, 14, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 15, 15, 947, 1155, 265, 153, 93, 93, 93, 93, 192, 192, 93, 96, 96, 96, 96, 96, 96, 6, 38, 1, -18, '', 0, 19, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 16, 16, 708, 0, 552, 448, 87, 87, 87, 87, 120, 120, 87, 49, 49, 49, 49, 49, 49, 7, 31, 1, -18, '', 0, 18, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 16, 16, 788, 0, 576, 456, 89, 89, 89, 89, 122, 122, 89, 56, 56, 56, 56, 56, 56, 7, 33, 1, -18, '', 0, 20, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 16, 16, 868, 0, 600, 464, 91, 91, 91, 91, 124, 124, 91, 63, 63, 63, 63, 63, 63, 8, 36, 1, -18, '', 0, 22, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 16, 16, 948, 0, 624, 472, 93, 93, 93, 93, 126, 126, 93, 70, 70, 70, 70, 70, 70, 8, 39, 1, -18, '', 0, 24, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 16, 16, 1028, 0, 648, 480, 95, 95, 95, 95, 128, 128, 95, 77, 77, 77, 77, 77, 77, 9, 42, 1, -18, '', 0, 26, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 16, 16, 1268, 0, 680, 488, 100, 100, 100, 100, 140, 140, 100, 75, 75, 75, 75, 75, 75, 8, 38, 1, -18, '', 0, 28, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 16, 16, 1348, 0, 704, 496, 102, 102, 102, 102, 142, 142, 102, 82, 82, 82, 82, 82, 82, 8, 40, 1, -18, '', 0, 30, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 16, 16, 1428, 0, 728, 504, 104, 104, 104, 104, 144, 144, 104, 89, 89, 89, 89, 89, 89, 9, 43, 1, -18, '', 0, 32, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 16, 16, 1508, 0, 752, 512, 106, 106, 106, 106, 146, 146, 106, 96, 96, 96, 96, 96, 96, 9, 46, 1, -18, '', 0, 34, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 16, 16, 1588, 0, 776, 520, 108, 108, 108, 108, 148, 148, 108, 103, 103, 103, 103, 103, 103, 10, 49, 1, -18, '', 0, 36, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 16, 16, 586, 653, 432, 269, 76, 76, 76, 76, 159, 159, 76, 49, 49, 49, 49, 49, 49, 5, 29, 1, -18, '', 0, 12, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 16, 16, 634, 701, 448, 274, 78, 78, 78, 78, 162, 162, 78, 56, 56, 56, 56, 56, 56, 5, 31, 1, -18, '', 0, 13, 9, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 16, 16, 682, 749, 464, 280, 80, 80, 80, 80, 165, 165, 80, 63, 63, 63, 63, 63, 63, 5, 32, 1, -18, '', 0, 14, 10, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 16, 16, 730, 797, 480, 285, 82, 82, 82, 82, 168, 168, 82, 70, 70, 70, 70, 70, 70, 6, 34, 1, -18, '', 0, 15, 11, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 16, 16, 778, 845, 496, 290, 84, 84, 84, 84, 171, 171, 84, 77, 77, 77, 77, 77, 77, 6, 35, 1, -18, '', 0, 16, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 16, 16, 1066, 1053, 528, 293, 89, 89, 89, 89, 184, 184, 89, 75, 75, 75, 75, 75, 75, 6, 34, 1, -18, '', 0, 18, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 16, 16, 1114, 1101, 544, 298, 91, 91, 91, 91, 187, 187, 91, 82, 82, 82, 82, 82, 82, 6, 35, 1, -18, '', 0, 19, 12, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 16, 16, 1162, 1149, 560, 304, 93, 93, 93, 93, 190, 190, 93, 89, 89, 89, 89, 89, 89, 6, 36, 1, -18, '', 0, 20, 13, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 16, 16, 1210, 1197, 576, 309, 95, 95, 95, 95, 193, 193, 95, 96, 96, 96, 96, 96, 96, 6, 38, 1, -18, '', 0, 21, 14, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 16, 16, 1258, 1245, 592, 314, 97, 97, 97, 97, 196, 196, 97, 103, 103, 103, 103, 103, 103, 7, 39, 1, -18, '', 0, 22, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 16, 16, 605, 0, 328, 456, 87, 87, 87, 87, 120, 120, 87, 49, 49, 49, 49, 49, 49, 7, 28, 1, -18, '', 0, 16, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 16, 16, 669, 0, 336, 464, 89, 89, 89, 89, 122, 122, 89, 56, 56, 56, 56, 56, 56, 7, 31, 1, -18, '', 0, 18, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 16, 16, 733, 0, 344, 472, 91, 91, 91, 91, 124, 124, 91, 63, 63, 63, 63, 63, 63, 8, 33, 1, -18, '', 0, 20, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 16, 16, 797, 0, 352, 480, 93, 93, 93, 93, 126, 126, 93, 70, 70, 70, 70, 70, 70, 8, 36, 1, -18, '', 0, 22, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 16, 16, 861, 0, 360, 488, 95, 95, 95, 95, 128, 128, 95, 77, 77, 77, 77, 77, 77, 9, 39, 1, -18, '', 0, 24, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 16, 16, 1085, 0, 408, 504, 100, 100, 100, 100, 140, 140, 100, 75, 75, 75, 75, 75, 75, 8, 35, 1, -18, '', 0, 24, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 16, 16, 1149, 0, 416, 512, 102, 102, 102, 102, 142, 142, 102, 82, 82, 82, 82, 82, 82, 8, 37, 1, -18, '', 0, 26, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 16, 16, 1213, 0, 424, 520, 104, 104, 104, 104, 144, 144, 104, 89, 89, 89, 89, 89, 89, 9, 40, 1, -18, '', 0, 28, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 16, 16, 1277, 0, 432, 528, 106, 106, 106, 106, 146, 146, 106, 96, 96, 96, 96, 96, 96, 9, 43, 1, -18, '', 0, 30, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 16, 16, 1341, 0, 440, 536, 108, 108, 108, 108, 148, 148, 108, 103, 103, 103, 103, 103, 103, 10, 45, 1, -18, '', 0, 32, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 16, 16, 487, 653, 213, 132, 76, 76, 76, 76, 159, 159, 76, 49, 49, 49, 49, 49, 49, 5, 29, 1, -18, '', 0, 11, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 16, 16, 519, 701, 218, 136, 78, 78, 78, 78, 162, 162, 78, 56, 56, 56, 56, 56, 56, 5, 31, 1, -18, '', 0, 12, 9, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 16, 16, 551, 749, 224, 140, 80, 80, 80, 80, 165, 165, 80, 63, 63, 63, 63, 63, 63, 5, 32, 1, -18, '', 0, 13, 10, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 16, 16, 583, 797, 229, 144, 82, 82, 82, 82, 168, 168, 82, 70, 70, 70, 70, 70, 70, 6, 34, 1, -18, '', 0, 14, 11, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 16, 16, 615, 845, 234, 148, 84, 84, 84, 84, 171, 171, 84, 77, 77, 77, 77, 77, 77, 6, 35, 1, -18, '', 0, 15, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 16, 16, 887, 1053, 261, 148, 89, 89, 89, 89, 184, 184, 89, 75, 75, 75, 75, 75, 75, 6, 34, 1, -18, '', 0, 16, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 16, 16, 919, 1101, 266, 152, 91, 91, 91, 91, 187, 187, 91, 82, 82, 82, 82, 82, 82, 6, 35, 1, -18, '', 0, 17, 12, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 16, 16, 951, 1149, 272, 156, 93, 93, 93, 93, 190, 190, 93, 89, 89, 89, 89, 89, 89, 6, 36, 1, -18, '', 0, 18, 13, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 16, 16, 983, 1197, 277, 160, 95, 95, 95, 95, 193, 193, 95, 96, 96, 96, 96, 96, 96, 6, 38, 1, -18, '', 0, 19, 14, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 16, 16, 1015, 1245, 282, 164, 97, 97, 97, 97, 196, 196, 97, 103, 103, 103, 103, 103, 103, 7, 39, 1, -18, '', 0, 20, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 17, 17, 760, 0, 587, 476, 89, 89, 89, 89, 123, 123, 89, 51, 51, 51, 51, 51, 51, 7, 32, 2, -18, '', 0, 18, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 17, 17, 845, 0, 612, 485, 91, 91, 91, 91, 125, 125, 91, 58, 58, 58, 58, 58, 58, 7, 35, 2, -18, '', 0, 20, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 17, 17, 930, 0, 638, 493, 93, 93, 93, 93, 127, 127, 93, 65, 65, 65, 65, 65, 65, 8, 38, 2, -18, '', 0, 22, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 17, 17, 1015, 0, 663, 502, 95, 95, 95, 95, 129, 129, 95, 72, 72, 72, 72, 72, 72, 8, 40, 2, -18, '', 0, 24, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 17, 17, 1100, 0, 689, 510, 97, 97, 97, 97, 131, 131, 97, 79, 79, 79, 79, 79, 79, 9, 43, 2, -18, '', 0, 26, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 17, 17, 1355, 0, 723, 518, 102, 102, 102, 102, 143, 143, 102, 79, 79, 79, 79, 79, 79, 8, 39, 2, -18, '', 0, 28, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 17, 17, 1440, 0, 748, 527, 104, 104, 104, 104, 145, 145, 104, 86, 86, 86, 86, 86, 86, 9, 42, 2, -18, '', 0, 30, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 17, 17, 1525, 0, 774, 535, 106, 106, 106, 106, 147, 147, 106, 93, 93, 93, 93, 93, 93, 9, 45, 2, -18, '', 0, 32, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 17, 17, 1610, 0, 799, 544, 108, 108, 108, 108, 149, 149, 108, 100, 100, 100, 100, 100, 100, 10, 47, 2, -18, '', 0, 34, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 17, 17, 1695, 0, 825, 552, 110, 110, 110, 110, 151, 151, 110, 107, 107, 107, 107, 107, 107, 10, 50, 2, -18, '', 0, 36, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 17, 17, 628, 707, 459, 286, 78, 78, 78, 78, 162, 162, 78, 51, 51, 51, 51, 51, 51, 5, 31, 1, -18, '', 0, 12, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 17, 17, 679, 758, 476, 292, 80, 80, 80, 80, 165, 165, 80, 58, 58, 58, 58, 58, 58, 5, 32, 1, -18, '', 0, 13, 9, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 17, 17, 730, 809, 493, 298, 82, 82, 82, 82, 168, 168, 82, 65, 65, 65, 65, 65, 65, 6, 34, 1, -18, '', 0, 14, 10, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 17, 17, 781, 860, 510, 303, 84, 84, 84, 84, 171, 171, 84, 72, 72, 72, 72, 72, 72, 6, 35, 1, -18, '', 0, 15, 11, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 17, 17, 832, 911, 527, 309, 86, 86, 86, 86, 174, 174, 86, 79, 79, 79, 79, 79, 79, 6, 36, 1, -18, '', 0, 16, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 17, 17, 1138, 1132, 561, 311, 91, 91, 91, 91, 187, 187, 91, 79, 79, 79, 79, 79, 79, 6, 35, 1, -18, '', 0, 18, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 17, 17, 1189, 1183, 578, 317, 93, 93, 93, 93, 190, 190, 93, 86, 86, 86, 86, 86, 86, 6, 36, 1, -18, '', 0, 19, 12, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 17, 17, 1240, 1234, 595, 323, 95, 95, 95, 95, 193, 193, 95, 93, 93, 93, 93, 93, 93, 6, 38, 1, -18, '', 0, 20, 13, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 17, 17, 1291, 1285, 612, 328, 97, 97, 97, 97, 196, 196, 97, 100, 100, 100, 100, 100, 100, 7, 39, 1, -18, '', 0, 21, 14, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 17, 17, 1342, 1336, 629, 334, 99, 99, 99, 99, 199, 199, 99, 107, 107, 107, 107, 107, 107, 7, 41, 1, -18, '', 0, 22, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 17, 17, 649, 0, 348, 484, 89, 89, 89, 89, 123, 123, 89, 51, 51, 51, 51, 51, 51, 7, 30, 2, -18, '', 0, 16, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 17, 17, 717, 0, 357, 493, 91, 91, 91, 91, 125, 125, 91, 58, 58, 58, 58, 58, 58, 7, 32, 2, -18, '', 0, 18, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 17, 17, 785, 0, 365, 501, 93, 93, 93, 93, 127, 127, 93, 65, 65, 65, 65, 65, 65, 8, 35, 2, -18, '', 0, 20, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 17, 17, 853, 0, 374, 510, 95, 95, 95, 95, 129, 129, 95, 72, 72, 72, 72, 72, 72, 8, 37, 2, -18, '', 0, 22, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 17, 17, 921, 0, 382, 518, 97, 97, 97, 97, 131, 131, 97, 79, 79, 79, 79, 79, 79, 9, 40, 2, -18, '', 0, 24, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 17, 17, 1159, 0, 433, 535, 102, 102, 102, 102, 143, 143, 102, 79, 79, 79, 79, 79, 79, 8, 36, 2, -18, '', 0, 24, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 17, 17, 1227, 0, 442, 544, 104, 104, 104, 104, 145, 145, 104, 86, 86, 86, 86, 86, 86, 9, 39, 2, -18, '', 0, 26, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 17, 17, 1295, 0, 450, 552, 106, 106, 106, 106, 147, 147, 106, 93, 93, 93, 93, 93, 93, 9, 41, 2, -18, '', 0, 28, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 17, 17, 1363, 0, 459, 561, 108, 108, 108, 108, 149, 149, 108, 100, 100, 100, 100, 100, 100, 10, 44, 2, -18, '', 0, 30, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 17, 17, 1431, 0, 467, 569, 110, 110, 110, 110, 151, 151, 110, 107, 107, 107, 107, 107, 107, 10, 46, 2, -18, '', 0, 32, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 17, 17, 523, 707, 226, 140, 77, 77, 77, 77, 162, 162, 77, 51, 51, 51, 51, 51, 51, 5, 31, 1, -18, '', 0, 11, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 17, 17, 557, 758, 232, 144, 79, 79, 79, 79, 165, 165, 79, 58, 58, 58, 58, 58, 58, 5, 32, 1, -18, '', 0, 12, 9, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 17, 17, 591, 809, 238, 148, 81, 81, 81, 81, 168, 168, 81, 65, 65, 65, 65, 65, 65, 6, 34, 1, -18, '', 0, 13, 10, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 17, 17, 625, 860, 243, 153, 83, 83, 83, 83, 171, 171, 83, 72, 72, 72, 72, 72, 72, 6, 35, 1, -18, '', 0, 14, 11, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 17, 17, 659, 911, 249, 157, 85, 85, 85, 85, 174, 174, 85, 79, 79, 79, 79, 79, 79, 6, 36, 1, -18, '', 0, 15, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 17, 17, 948, 1132, 277, 157, 90, 90, 90, 90, 187, 187, 90, 79, 79, 79, 79, 79, 79, 6, 35, 1, -18, '', 0, 16, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 17, 17, 982, 1183, 283, 161, 92, 92, 92, 92, 190, 190, 92, 86, 86, 86, 86, 86, 86, 6, 36, 1, -18, '', 0, 17, 12, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 17, 17, 1016, 1234, 289, 165, 94, 94, 94, 94, 193, 193, 94, 93, 93, 93, 93, 93, 93, 6, 38, 1, -18, '', 0, 18, 13, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 17, 17, 1050, 1285, 294, 170, 96, 96, 96, 96, 196, 196, 96, 100, 100, 100, 100, 100, 100, 7, 39, 1, -18, '', 0, 19, 14, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 17, 17, 1084, 1336, 300, 174, 98, 98, 98, 98, 199, 199, 98, 107, 107, 107, 107, 107, 107, 7, 41, 1, -18, '', 0, 20, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 18, 18, 813, 0, 621, 504, 92, 92, 92, 92, 126, 126, 92, 53, 53, 53, 53, 53, 53, 7, 33, 2, -18, '', 0, 19, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 18, 18, 903, 0, 648, 513, 94, 94, 94, 94, 128, 128, 94, 60, 60, 60, 60, 60, 60, 8, 36, 2, -18, '', 0, 21, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 18, 18, 993, 0, 675, 522, 96, 96, 96, 96, 130, 130, 96, 67, 67, 67, 67, 67, 67, 8, 39, 2, -18, '', 0, 23, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 18, 18, 1083, 0, 702, 531, 98, 98, 98, 98, 132, 132, 98, 74, 74, 74, 74, 74, 74, 9, 42, 2, -18, '', 0, 25, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 18, 18, 1173, 0, 729, 540, 100, 100, 100, 100, 134, 134, 100, 81, 81, 81, 81, 81, 81, 9, 45, 2, -18, '', 0, 27, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 18, 18, 1443, 0, 765, 549, 106, 106, 106, 106, 146, 146, 106, 81, 81, 81, 81, 81, 81, 8, 40, 2, -18, '', 0, 29, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 18, 18, 1533, 0, 792, 558, 108, 108, 108, 108, 148, 148, 108, 88, 88, 88, 88, 88, 88, 9, 43, 2, -18, '', 0, 31, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 18, 18, 1623, 0, 819, 567, 110, 110, 110, 110, 150, 150, 110, 95, 95, 95, 95, 95, 95, 9, 46, 2, -18, '', 0, 33, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 18, 18, 1713, 0, 846, 576, 112, 112, 112, 112, 152, 152, 112, 102, 102, 102, 102, 102, 102, 10, 49, 2, -18, '', 0, 35, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 18, 18, 1803, 0, 873, 585, 114, 114, 114, 114, 154, 154, 114, 109, 109, 109, 109, 109, 109, 10, 52, 2, -18, '', 0, 37, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 18, 18, 672, 763, 486, 303, 80, 80, 80, 80, 166, 166, 80, 53, 53, 53, 53, 53, 53, 5, 32, 1, -18, '', 0, 12, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 18, 18, 726, 817, 504, 309, 82, 82, 82, 82, 169, 169, 82, 60, 60, 60, 60, 60, 60, 6, 34, 1, -18, '', 0, 13, 9, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 18, 18, 780, 871, 522, 315, 84, 84, 84, 84, 172, 172, 84, 67, 67, 67, 67, 67, 67, 6, 35, 1, -18, '', 0, 14, 10, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 18, 18, 834, 925, 540, 321, 86, 86, 86, 86, 175, 175, 86, 74, 74, 74, 74, 74, 74, 6, 36, 1, -18, '', 0, 15, 11, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 18, 18, 888, 979, 558, 327, 88, 88, 88, 88, 178, 178, 88, 81, 81, 81, 81, 81, 81, 6, 38, 1, -18, '', 0, 16, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 18, 18, 1212, 1213, 594, 330, 94, 94, 94, 94, 191, 191, 94, 81, 81, 81, 81, 81, 81, 6, 36, 1, -18, '', 0, 18, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 18, 18, 1266, 1267, 612, 336, 96, 96, 96, 96, 194, 194, 96, 88, 88, 88, 88, 88, 88, 6, 38, 1, -18, '', 0, 19, 12, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 18, 18, 1320, 1321, 630, 342, 98, 98, 98, 98, 197, 197, 98, 95, 95, 95, 95, 95, 95, 7, 39, 1, -18, '', 0, 20, 13, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 18, 18, 1374, 1375, 648, 348, 100, 100, 100, 100, 200, 200, 100, 102, 102, 102, 102, 102, 102, 7, 41, 1, -18, '', 0, 21, 14, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 18, 18, 1428, 1429, 666, 354, 102, 102, 102, 102, 203, 203, 102, 109, 109, 109, 109, 109, 109, 7, 42, 1, -18, '', 0, 22, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 18, 18, 694, 0, 369, 513, 92, 92, 92, 92, 126, 126, 92, 53, 53, 53, 53, 53, 53, 7, 31, 2, -18, '', 0, 17, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 18, 18, 766, 0, 378, 522, 94, 94, 94, 94, 128, 128, 94, 60, 60, 60, 60, 60, 60, 8, 33, 2, -18, '', 0, 19, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 18, 18, 838, 0, 387, 531, 96, 96, 96, 96, 130, 130, 96, 67, 67, 67, 67, 67, 67, 8, 36, 2, -18, '', 0, 21, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 18, 18, 910, 0, 396, 540, 98, 98, 98, 98, 132, 132, 98, 74, 74, 74, 74, 74, 74, 9, 39, 2, -18, '', 0, 23, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 18, 18, 982, 0, 405, 549, 100, 100, 100, 100, 134, 134, 100, 81, 81, 81, 81, 81, 81, 9, 41, 2, -18, '', 0, 25, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 18, 18, 1234, 0, 459, 567, 106, 106, 106, 106, 146, 146, 106, 81, 81, 81, 81, 81, 81, 8, 37, 2, -18, '', 0, 25, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 18, 18, 1306, 0, 468, 576, 108, 108, 108, 108, 148, 148, 108, 88, 88, 88, 88, 88, 88, 9, 40, 2, -18, '', 0, 27, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 18, 18, 1378, 0, 477, 585, 110, 110, 110, 110, 150, 150, 110, 95, 95, 95, 95, 95, 95, 9, 43, 2, -18, '', 0, 29, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 18, 18, 1450, 0, 486, 594, 112, 112, 112, 112, 152, 152, 112, 102, 102, 102, 102, 102, 102, 10, 45, 2, -18, '', 0, 31, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 18, 18, 1522, 0, 495, 603, 114, 114, 114, 114, 154, 154, 114, 109, 109, 109, 109, 109, 109, 10, 48, 2, -18, '', 0, 33, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 18, 18, 560, 763, 240, 148, 80, 80, 80, 80, 166, 166, 80, 53, 53, 53, 53, 53, 53, 5, 32, 1, -18, '', 0, 11, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 18, 18, 596, 817, 246, 153, 82, 82, 82, 82, 169, 169, 82, 60, 60, 60, 60, 60, 60, 6, 34, 1, -18, '', 0, 12, 9, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 18, 18, 632, 871, 252, 157, 84, 84, 84, 84, 172, 172, 84, 67, 67, 67, 67, 67, 67, 6, 35, 1, -18, '', 0, 13, 10, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 18, 18, 668, 925, 258, 162, 86, 86, 86, 86, 175, 175, 86, 74, 74, 74, 74, 74, 74, 6, 36, 1, -18, '', 0, 14, 11, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 18, 18, 704, 979, 264, 166, 88, 88, 88, 88, 178, 178, 88, 81, 81, 81, 81, 81, 81, 6, 38, 1, -18, '', 0, 15, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 18, 18, 1010, 1213, 294, 166, 94, 94, 94, 94, 191, 191, 94, 81, 81, 81, 81, 81, 81, 6, 36, 1, -18, '', 0, 16, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 18, 18, 1046, 1267, 300, 171, 96, 96, 96, 96, 194, 194, 96, 88, 88, 88, 88, 88, 88, 6, 38, 1, -18, '', 0, 17, 12, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 18, 18, 1082, 1321, 306, 175, 98, 98, 98, 98, 197, 197, 98, 95, 95, 95, 95, 95, 95, 7, 39, 1, -18, '', 0, 18, 13, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 18, 18, 1118, 1375, 312, 180, 100, 100, 100, 100, 200, 200, 100, 102, 102, 102, 102, 102, 102, 7, 41, 1, -18, '', 0, 19, 14, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 18, 18, 1154, 1429, 318, 184, 102, 102, 102, 102, 203, 203, 102, 109, 109, 109, 109, 109, 109, 7, 42, 1, -18, '', 0, 20, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 19, 19, 867, 0, 656, 532, 94, 94, 94, 94, 129, 129, 94, 55, 55, 55, 55, 55, 55, 7, 35, 2, -18, '', 0, 19, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 19, 19, 962, 0, 684, 542, 96, 96, 96, 96, 131, 131, 96, 62, 62, 62, 62, 62, 62, 8, 38, 2, -18, '', 0, 21, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 19, 19, 1057, 0, 713, 551, 98, 98, 98, 98, 133, 133, 98, 69, 69, 69, 69, 69, 69, 8, 40, 2, -18, '', 0, 23, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 19, 19, 1152, 0, 741, 561, 100, 100, 100, 100, 135, 135, 100, 76, 76, 76, 76, 76, 76, 9, 43, 2, -18, '', 0, 25, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 19, 19, 1247, 0, 770, 570, 102, 102, 102, 102, 137, 137, 102, 83, 83, 83, 83, 83, 83, 9, 46, 2, -18, '', 0, 27, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 19, 19, 1532, 0, 808, 579, 108, 108, 108, 108, 149, 149, 108, 83, 83, 83, 83, 83, 83, 9, 42, 2, -18, '', 0, 29, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 19, 19, 1627, 0, 836, 589, 110, 110, 110, 110, 151, 151, 110, 90, 90, 90, 90, 90, 90, 9, 45, 2, -18, '', 0, 31, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 19, 19, 1722, 0, 865, 598, 112, 112, 112, 112, 153, 153, 112, 97, 97, 97, 97, 97, 97, 10, 47, 2, -18, '', 0, 33, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 19, 19, 1817, 0, 893, 608, 114, 114, 114, 114, 155, 155, 114, 104, 104, 104, 104, 104, 104, 10, 50, 2, -18, '', 0, 35, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 19, 19, 1912, 0, 922, 617, 116, 116, 116, 116, 157, 157, 116, 111, 111, 111, 111, 111, 111, 11, 53, 2, -18, '', 0, 37, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 19, 19, 717, 821, 513, 320, 82, 82, 82, 82, 169, 169, 82, 55, 55, 55, 55, 55, 55, 6, 34, 1, -18, '', 0, 12, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 19, 19, 774, 878, 532, 326, 84, 84, 84, 84, 172, 172, 84, 62, 62, 62, 62, 62, 62, 6, 35, 1, -18, '', 0, 13, 9, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 19, 19, 831, 935, 551, 333, 86, 86, 86, 86, 175, 175, 86, 69, 69, 69, 69, 69, 69, 6, 36, 1, -18, '', 0, 14, 10, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 19, 19, 888, 992, 570, 339, 88, 88, 88, 88, 178, 178, 88, 76, 76, 76, 76, 76, 76, 6, 38, 1, -18, '', 0, 15, 11, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 19, 19, 945, 1049, 589, 345, 90, 90, 90, 90, 181, 181, 90, 83, 83, 83, 83, 83, 83, 7, 39, 1, -18, '', 0, 16, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 19, 19, 1287, 1296, 627, 348, 96, 96, 96, 96, 194, 194, 96, 83, 83, 83, 83, 83, 83, 6, 38, 1, -18, '', 0, 18, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 19, 19, 1344, 1353, 646, 354, 98, 98, 98, 98, 197, 197, 98, 90, 90, 90, 90, 90, 90, 7, 39, 1, -18, '', 0, 19, 12, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 19, 19, 1401, 1410, 665, 361, 100, 100, 100, 100, 200, 200, 100, 97, 97, 97, 97, 97, 97, 7, 41, 1, -18, '', 0, 20, 13, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 19, 19, 1458, 1467, 684, 367, 102, 102, 102, 102, 203, 203, 102, 104, 104, 104, 104, 104, 104, 7, 42, 1, -18, '', 0, 21, 14, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 19, 19, 1515, 1524, 703, 373, 104, 104, 104, 104, 206, 206, 104, 111, 111, 111, 111, 111, 111, 7, 43, 1, -18, '', 0, 22, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 19, 19, 741, 0, 389, 541, 94, 94, 94, 94, 129, 129, 94, 55, 55, 55, 55, 55, 55, 7, 32, 2, -18, '', 0, 17, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 19, 19, 817, 0, 399, 551, 96, 96, 96, 96, 131, 131, 96, 62, 62, 62, 62, 62, 62, 8, 35, 2, -18, '', 0, 19, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 19, 19, 893, 0, 408, 560, 98, 98, 98, 98, 133, 133, 98, 69, 69, 69, 69, 69, 69, 8, 37, 2, -18, '', 0, 21, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 19, 19, 969, 0, 418, 570, 100, 100, 100, 100, 135, 135, 100, 76, 76, 76, 76, 76, 76, 9, 40, 2, -18, '', 0, 23, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 19, 19, 1045, 0, 427, 579, 102, 102, 102, 102, 137, 137, 102, 83, 83, 83, 83, 83, 83, 9, 43, 2, -18, '', 0, 25, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 19, 19, 1311, 0, 484, 598, 108, 108, 108, 108, 149, 149, 108, 83, 83, 83, 83, 83, 83, 9, 39, 2, -18, '', 0, 25, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 19, 19, 1387, 0, 494, 608, 110, 110, 110, 110, 151, 151, 110, 90, 90, 90, 90, 90, 90, 9, 41, 2, -18, '', 0, 27, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 19, 19, 1463, 0, 503, 617, 112, 112, 112, 112, 153, 153, 112, 97, 97, 97, 97, 97, 97, 10, 44, 2, -18, '', 0, 29, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 19, 19, 1539, 0, 513, 627, 114, 114, 114, 114, 155, 155, 114, 104, 104, 104, 104, 104, 104, 10, 46, 2, -18, '', 0, 31, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 19, 19, 1615, 0, 522, 636, 116, 116, 116, 116, 157, 157, 116, 111, 111, 111, 111, 111, 111, 11, 49, 2, -18, '', 0, 33, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 19, 19, 599, 821, 253, 156, 81, 81, 81, 81, 169, 169, 81, 55, 55, 55, 55, 55, 55, 6, 34, 1, -18, '', 0, 11, 8, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 19, 19, 637, 878, 259, 161, 83, 83, 83, 83, 172, 172, 83, 62, 62, 62, 62, 62, 62, 6, 35, 1, -18, '', 0, 12, 9, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 19, 19, 675, 935, 266, 166, 85, 85, 85, 85, 175, 175, 85, 69, 69, 69, 69, 69, 69, 6, 36, 1, -18, '', 0, 13, 10, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 19, 19, 713, 992, 272, 171, 87, 87, 87, 87, 178, 178, 87, 76, 76, 76, 76, 76, 76, 6, 38, 1, -18, '', 0, 14, 11, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 19, 19, 751, 1049, 278, 175, 89, 89, 89, 89, 181, 181, 89, 83, 83, 83, 83, 83, 83, 7, 39, 1, -18, '', 0, 15, 12, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 19, 19, 1074, 1296, 310, 175, 95, 95, 95, 95, 194, 194, 95, 83, 83, 83, 83, 83, 83, 6, 38, 1, -18, '', 0, 16, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 19, 19, 1112, 1353, 316, 180, 97, 97, 97, 97, 197, 197, 97, 90, 90, 90, 90, 90, 90, 7, 39, 1, -18, '', 0, 17, 12, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 19, 19, 1150, 1410, 323, 185, 99, 99, 99, 99, 200, 200, 99, 97, 97, 97, 97, 97, 97, 7, 41, 1, -18, '', 0, 18, 13, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 19, 19, 1188, 1467, 329, 190, 101, 101, 101, 101, 203, 203, 101, 104, 104, 104, 104, 104, 104, 7, 42, 1, -18, '', 0, 19, 14, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 19, 19, 1226, 1524, 335, 194, 103, 103, 103, 103, 206, 206, 103, 111, 111, 111, 111, 111, 111, 7, 43, 1, -18, '', 0, 20, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 20, 20, 924, 0, 690, 560, 98, 98, 98, 98, 132, 132, 98, 58, 58, 58, 58, 58, 58, 8, 36, 2, -18, '', 0, 19, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 20, 20, 1024, 0, 720, 570, 101, 101, 101, 101, 134, 134, 101, 66, 66, 66, 66, 66, 66, 8, 39, 2, -18, '', 0, 21, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 20, 20, 1124, 0, 750, 580, 104, 104, 104, 104, 136, 136, 104, 74, 74, 74, 74, 74, 74, 9, 42, 2, -18, '', 0, 23, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 20, 20, 1224, 0, 780, 590, 107, 107, 107, 107, 138, 138, 107, 82, 82, 82, 82, 82, 82, 9, 45, 2, -18, '', 0, 25, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 20, 20, 1324, 0, 810, 600, 110, 110, 110, 110, 140, 140, 110, 90, 90, 90, 90, 90, 90, 10, 47, 2, -18, '', 0, 27, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 20, 20, 1624, 0, 850, 610, 113, 113, 113, 113, 152, 152, 113, 88, 88, 88, 88, 88, 88, 9, 43, 2, -18, '', 0, 29, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 20, 20, 1724, 0, 880, 620, 116, 116, 116, 116, 154, 154, 116, 96, 96, 96, 96, 96, 96, 9, 46, 2, -18, '', 0, 31, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 20, 20, 1824, 0, 910, 630, 119, 119, 119, 119, 156, 156, 119, 104, 104, 104, 104, 104, 104, 10, 49, 2, -18, '', 0, 33, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 20, 20, 1924, 0, 940, 640, 122, 122, 122, 122, 158, 158, 122, 112, 112, 112, 112, 112, 112, 10, 52, 2, -18, '', 0, 35, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 20, 20, 2024, 0, 970, 650, 125, 125, 125, 125, 160, 160, 125, 120, 120, 120, 120, 120, 120, 11, 54, 2, -18, '', 0, 37, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 20, 20, 764, 880, 540, 336, 86, 86, 86, 86, 173, 173, 86, 58, 58, 58, 58, 58, 58, 6, 35, 1, -18, '', 0, 13, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 20, 20, 824, 940, 560, 343, 89, 89, 89, 89, 176, 176, 89, 66, 66, 66, 66, 66, 66, 6, 36, 1, -18, '', 0, 14, 10, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 20, 20, 884, 1000, 580, 350, 92, 92, 92, 92, 179, 179, 92, 74, 74, 74, 74, 74, 74, 6, 38, 1, -18, '', 0, 15, 11, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 20, 20, 944, 1060, 600, 356, 95, 95, 95, 95, 182, 182, 95, 82, 82, 82, 82, 82, 82, 7, 39, 1, -18, '', 0, 16, 12, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 20, 20, 1004, 1120, 620, 363, 98, 98, 98, 98, 185, 185, 98, 90, 90, 90, 90, 90, 90, 7, 41, 1, -18, '', 0, 17, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 20, 20, 1364, 1380, 660, 366, 101, 101, 101, 101, 198, 198, 101, 88, 88, 88, 88, 88, 88, 7, 39, 1, -18, '', 0, 19, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 20, 20, 1424, 1440, 680, 373, 104, 104, 104, 104, 201, 201, 104, 96, 96, 96, 96, 96, 96, 7, 41, 1, -18, '', 0, 20, 13, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 20, 20, 1484, 1500, 700, 380, 107, 107, 107, 107, 204, 204, 107, 104, 104, 104, 104, 104, 104, 7, 42, 1, -18, '', 0, 21, 14, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 20, 20, 1544, 1560, 720, 386, 110, 110, 110, 110, 207, 207, 110, 112, 112, 112, 112, 112, 112, 7, 43, 1, -18, '', 0, 22, 15, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 20, 20, 1604, 1620, 740, 393, 113, 113, 113, 113, 210, 210, 113, 120, 120, 120, 120, 120, 120, 8, 45, 1, -18, '', 0, 23, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 20, 20, 790, 0, 410, 570, 98, 98, 98, 98, 132, 132, 98, 58, 58, 58, 58, 58, 58, 8, 33, 2, -18, '', 0, 17, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 20, 20, 870, 0, 420, 580, 101, 101, 101, 101, 134, 134, 101, 66, 66, 66, 66, 66, 66, 8, 36, 2, -18, '', 0, 19, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 20, 20, 950, 0, 430, 590, 104, 104, 104, 104, 136, 136, 104, 74, 74, 74, 74, 74, 74, 9, 39, 2, -18, '', 0, 21, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 20, 20, 1030, 0, 440, 600, 107, 107, 107, 107, 138, 138, 107, 82, 82, 82, 82, 82, 82, 9, 41, 2, -18, '', 0, 23, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 20, 20, 1110, 0, 450, 610, 110, 110, 110, 110, 140, 140, 110, 90, 90, 90, 90, 90, 90, 10, 44, 2, -18, '', 0, 25, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 20, 20, 1390, 0, 510, 630, 113, 113, 113, 113, 152, 152, 113, 88, 88, 88, 88, 88, 88, 9, 40, 2, -18, '', 0, 25, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 20, 20, 1470, 0, 520, 640, 116, 116, 116, 116, 154, 154, 116, 96, 96, 96, 96, 96, 96, 9, 43, 2, -18, '', 0, 27, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 20, 20, 1550, 0, 530, 650, 119, 119, 119, 119, 156, 156, 119, 104, 104, 104, 104, 104, 104, 10, 45, 2, -18, '', 0, 29, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 20, 20, 1630, 0, 540, 660, 122, 122, 122, 122, 158, 158, 122, 112, 112, 112, 112, 112, 112, 10, 48, 2, -18, '', 0, 31, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 20, 20, 1710, 0, 550, 670, 125, 125, 125, 125, 160, 160, 125, 120, 120, 120, 120, 120, 120, 11, 50, 2, -18, '', 0, 33, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 20, 20, 638, 880, 266, 165, 85, 85, 85, 85, 173, 173, 85, 58, 58, 58, 58, 58, 58, 6, 35, 1, -18, '', 0, 12, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 20, 20, 678, 940, 273, 170, 88, 88, 88, 88, 176, 176, 88, 66, 66, 66, 66, 66, 66, 6, 36, 1, -18, '', 0, 13, 10, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 20, 20, 718, 1000, 280, 175, 91, 91, 91, 91, 179, 179, 91, 74, 74, 74, 74, 74, 74, 6, 38, 1, -18, '', 0, 14, 11, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 20, 20, 758, 1060, 286, 180, 94, 94, 94, 94, 182, 182, 94, 82, 82, 82, 82, 82, 82, 7, 39, 1, -18, '', 0, 15, 12, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 20, 20, 798, 1120, 293, 185, 97, 97, 97, 97, 185, 185, 97, 90, 90, 90, 90, 90, 90, 7, 41, 1, -18, '', 0, 16, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 20, 20, 1138, 1380, 326, 185, 100, 100, 100, 100, 198, 198, 100, 88, 88, 88, 88, 88, 88, 7, 39, 1, -18, '', 0, 17, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 20, 20, 1178, 1440, 333, 190, 103, 103, 103, 103, 201, 201, 103, 96, 96, 96, 96, 96, 96, 7, 41, 1, -18, '', 0, 18, 13, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 20, 20, 1218, 1500, 340, 195, 106, 106, 106, 106, 204, 204, 106, 104, 104, 104, 104, 104, 104, 7, 42, 1, -18, '', 0, 19, 14, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 20, 20, 1258, 1560, 346, 200, 109, 109, 109, 109, 207, 207, 109, 112, 112, 112, 112, 112, 112, 7, 43, 1, -18, '', 0, 20, 15, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 20, 20, 1298, 1620, 353, 205, 112, 112, 112, 112, 210, 210, 112, 120, 120, 120, 120, 120, 120, 8, 45, 1, -18, '', 0, 21, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 21, 21, 982, 0, 725, 588, 100, 100, 100, 100, 135, 135, 100, 60, 60, 60, 60, 60, 60, 8, 38, 2, -18, '', 0, 20, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 21, 21, 1087, 0, 756, 599, 103, 103, 103, 103, 137, 137, 103, 68, 68, 68, 68, 68, 68, 8, 40, 2, -18, '', 0, 22, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 21, 21, 1192, 0, 788, 609, 106, 106, 106, 106, 139, 139, 106, 76, 76, 76, 76, 76, 76, 9, 43, 2, -18, '', 0, 24, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 21, 21, 1297, 0, 819, 620, 109, 109, 109, 109, 141, 141, 109, 84, 84, 84, 84, 84, 84, 9, 46, 2, -18, '', 0, 26, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 21, 21, 1402, 0, 851, 630, 112, 112, 112, 112, 143, 143, 112, 92, 92, 92, 92, 92, 92, 10, 49, 2, -18, '', 0, 28, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 21, 21, 1717, 0, 893, 640, 115, 115, 115, 115, 155, 155, 115, 90, 90, 90, 90, 90, 90, 9, 45, 2, -18, '', 0, 30, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 21, 21, 1822, 0, 924, 651, 118, 118, 118, 118, 157, 157, 118, 98, 98, 98, 98, 98, 98, 10, 47, 2, -18, '', 0, 32, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 21, 21, 1927, 0, 956, 661, 121, 121, 121, 121, 159, 159, 121, 106, 106, 106, 106, 106, 106, 10, 50, 2, -18, '', 0, 34, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 21, 21, 2032, 0, 987, 672, 124, 124, 124, 124, 161, 161, 124, 114, 114, 114, 114, 114, 114, 11, 53, 2, -18, '', 0, 36, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 21, 21, 2137, 0, 1019, 682, 127, 127, 127, 127, 163, 163, 127, 122, 122, 122, 122, 122, 122, 11, 56, 2, -18, '', 0, 38, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 21, 21, 811, 941, 567, 354, 88, 88, 88, 88, 176, 176, 88, 60, 60, 60, 60, 60, 60, 6, 36, 1, -18, '', 0, 13, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 21, 21, 874, 1004, 588, 361, 91, 91, 91, 91, 179, 179, 91, 68, 68, 68, 68, 68, 68, 6, 38, 1, -18, '', 0, 14, 10, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 21, 21, 937, 1067, 609, 368, 94, 94, 94, 94, 182, 182, 94, 76, 76, 76, 76, 76, 76, 7, 39, 1, -18, '', 0, 15, 11, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 21, 21, 1000, 1130, 630, 375, 97, 97, 97, 97, 185, 185, 97, 84, 84, 84, 84, 84, 84, 7, 41, 1, -18, '', 0, 16, 12, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 21, 21, 1063, 1193, 651, 382, 100, 100, 100, 100, 188, 188, 100, 92, 92, 92, 92, 92, 92, 7, 42, 1, -18, '', 0, 17, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 21, 21, 1441, 1466, 693, 385, 103, 103, 103, 103, 201, 201, 103, 90, 90, 90, 90, 90, 90, 7, 41, 1, -18, '', 0, 19, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 21, 21, 1504, 1529, 714, 392, 106, 106, 106, 106, 204, 204, 106, 98, 98, 98, 98, 98, 98, 7, 42, 1, -18, '', 0, 20, 13, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 21, 21, 1567, 1592, 735, 399, 109, 109, 109, 109, 207, 207, 109, 106, 106, 106, 106, 106, 106, 7, 43, 1, -18, '', 0, 21, 14, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 21, 21, 1630, 1655, 756, 406, 112, 112, 112, 112, 210, 210, 112, 114, 114, 114, 114, 114, 114, 8, 45, 1, -18, '', 0, 22, 15, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 21, 21, 1693, 1718, 777, 413, 115, 115, 115, 115, 213, 213, 115, 122, 122, 122, 122, 122, 122, 8, 46, 1, -18, '', 0, 23, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 21, 21, 839, 0, 430, 598, 100, 100, 100, 100, 135, 135, 100, 60, 60, 60, 60, 60, 60, 8, 35, 2, -18, '', 0, 18, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 21, 21, 923, 0, 441, 609, 103, 103, 103, 103, 137, 137, 103, 68, 68, 68, 68, 68, 68, 8, 37, 2, -18, '', 0, 20, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 21, 21, 1007, 0, 451, 619, 106, 106, 106, 106, 139, 139, 106, 76, 76, 76, 76, 76, 76, 9, 40, 2, -18, '', 0, 22, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 21, 21, 1091, 0, 462, 630, 109, 109, 109, 109, 141, 141, 109, 84, 84, 84, 84, 84, 84, 9, 43, 2, -18, '', 0, 24, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 21, 21, 1175, 0, 472, 640, 112, 112, 112, 112, 143, 143, 112, 92, 92, 92, 92, 92, 92, 10, 45, 2, -18, '', 0, 26, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 21, 21, 1469, 0, 535, 661, 115, 115, 115, 115, 155, 155, 115, 90, 90, 90, 90, 90, 90, 9, 41, 2, -18, '', 0, 26, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 21, 21, 1553, 0, 546, 672, 118, 118, 118, 118, 157, 157, 118, 98, 98, 98, 98, 98, 98, 10, 44, 2, -18, '', 0, 28, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 21, 21, 1637, 0, 556, 682, 121, 121, 121, 121, 159, 159, 121, 106, 106, 106, 106, 106, 106, 10, 46, 2, -18, '', 0, 30, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 21, 21, 1721, 0, 567, 693, 124, 124, 124, 124, 161, 161, 124, 114, 114, 114, 114, 114, 114, 11, 49, 2, -18, '', 0, 32, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 21, 21, 1805, 0, 577, 703, 127, 127, 127, 127, 163, 163, 127, 122, 122, 122, 122, 122, 122, 11, 52, 2, -18, '', 0, 34, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 21, 21, 679, 941, 280, 173, 87, 87, 87, 87, 176, 176, 87, 60, 60, 60, 60, 60, 60, 6, 36, 1, -18, '', 0, 12, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 21, 21, 721, 1004, 287, 178, 90, 90, 90, 90, 179, 179, 90, 68, 68, 68, 68, 68, 68, 6, 38, 1, -18, '', 0, 13, 10, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 21, 21, 763, 1067, 294, 183, 93, 93, 93, 93, 182, 182, 93, 76, 76, 76, 76, 76, 76, 7, 39, 1, -18, '', 0, 14, 11, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 21, 21, 805, 1130, 301, 189, 96, 96, 96, 96, 185, 185, 96, 84, 84, 84, 84, 84, 84, 7, 41, 1, -18, '', 0, 15, 12, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 21, 21, 847, 1193, 308, 194, 99, 99, 99, 99, 188, 188, 99, 92, 92, 92, 92, 92, 92, 7, 42, 1, -18, '', 0, 16, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 21, 21, 1204, 1466, 343, 194, 102, 102, 102, 102, 201, 201, 102, 90, 90, 90, 90, 90, 90, 7, 41, 1, -18, '', 0, 17, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 21, 21, 1246, 1529, 350, 199, 105, 105, 105, 105, 204, 204, 105, 98, 98, 98, 98, 98, 98, 7, 42, 1, -18, '', 0, 18, 13, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 21, 21, 1288, 1592, 357, 204, 108, 108, 108, 108, 207, 207, 108, 106, 106, 106, 106, 106, 106, 7, 43, 1, -18, '', 0, 19, 14, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 21, 21, 1330, 1655, 364, 210, 111, 111, 111, 111, 210, 210, 111, 114, 114, 114, 114, 114, 114, 8, 45, 1, -18, '', 0, 20, 15, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 21, 21, 1372, 1718, 371, 215, 114, 114, 114, 114, 213, 213, 114, 122, 122, 122, 122, 122, 122, 8, 46, 1, -18, '', 0, 21, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 22, 22, 1042, 0, 759, 616, 103, 103, 103, 103, 138, 138, 103, 62, 62, 62, 62, 62, 62, 8, 39, 2, -18, '', 0, 20, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 22, 22, 1152, 0, 792, 627, 106, 106, 106, 106, 140, 140, 106, 70, 70, 70, 70, 70, 70, 9, 42, 2, -18, '', 0, 22, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 22, 22, 1262, 0, 825, 638, 109, 109, 109, 109, 142, 142, 109, 78, 78, 78, 78, 78, 78, 9, 45, 2, -18, '', 0, 24, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 22, 22, 1372, 0, 858, 649, 112, 112, 112, 112, 144, 144, 112, 86, 86, 86, 86, 86, 86, 10, 47, 2, -18, '', 0, 26, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 22, 22, 1482, 0, 891, 660, 115, 115, 115, 115, 146, 146, 115, 94, 94, 94, 94, 94, 94, 10, 50, 2, -18, '', 0, 28, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 22, 22, 1812, 0, 935, 671, 119, 119, 119, 119, 158, 158, 119, 92, 92, 92, 92, 92, 92, 9, 46, 2, -18, '', 0, 30, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 22, 22, 1922, 0, 968, 682, 122, 122, 122, 122, 160, 160, 122, 100, 100, 100, 100, 100, 100, 10, 49, 2, -18, '', 0, 32, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 22, 22, 2032, 0, 1001, 693, 125, 125, 125, 125, 162, 162, 125, 108, 108, 108, 108, 108, 108, 10, 52, 2, -18, '', 0, 34, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 22, 22, 2142, 0, 1034, 704, 128, 128, 128, 128, 164, 164, 128, 116, 116, 116, 116, 116, 116, 11, 54, 2, -18, '', 0, 36, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 22, 22, 2252, 0, 1067, 715, 131, 131, 131, 131, 166, 166, 131, 124, 124, 124, 124, 124, 124, 11, 57, 2, -18, '', 0, 38, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 22, 22, 861, 1003, 594, 370, 91, 91, 91, 91, 180, 180, 91, 62, 62, 62, 62, 62, 62, 6, 38, 1, -18, '', 0, 13, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 22, 22, 927, 1069, 616, 377, 94, 94, 94, 94, 183, 183, 94, 70, 70, 70, 70, 70, 70, 7, 39, 1, -18, '', 0, 14, 10, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 22, 22, 993, 1135, 638, 385, 97, 97, 97, 97, 186, 186, 97, 78, 78, 78, 78, 78, 78, 7, 41, 1, -18, '', 0, 15, 11, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 22, 22, 1059, 1201, 660, 392, 100, 100, 100, 100, 189, 189, 100, 86, 86, 86, 86, 86, 86, 7, 42, 1, -18, '', 0, 16, 12, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 22, 22, 1125, 1267, 682, 399, 103, 103, 103, 103, 192, 192, 103, 94, 94, 94, 94, 94, 94, 7, 43, 1, -18, '', 0, 17, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 22, 22, 1521, 1553, 726, 403, 107, 107, 107, 107, 205, 205, 107, 92, 92, 92, 92, 92, 92, 7, 42, 1, -18, '', 0, 19, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 22, 22, 1587, 1619, 748, 410, 110, 110, 110, 110, 208, 208, 110, 100, 100, 100, 100, 100, 100, 7, 43, 1, -18, '', 0, 20, 13, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 22, 22, 1653, 1685, 770, 418, 113, 113, 113, 113, 211, 211, 113, 108, 108, 108, 108, 108, 108, 8, 45, 1, -18, '', 0, 21, 14, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 22, 22, 1719, 1751, 792, 425, 116, 116, 116, 116, 214, 214, 116, 116, 116, 116, 116, 116, 116, 8, 46, 1, -18, '', 0, 22, 15, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 22, 22, 1785, 1817, 814, 432, 119, 119, 119, 119, 217, 217, 119, 124, 124, 124, 124, 124, 124, 8, 48, 1, -18, '', 0, 23, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 22, 22, 891, 0, 451, 627, 103, 103, 103, 103, 138, 138, 103, 62, 62, 62, 62, 62, 62, 8, 36, 2, -18, '', 0, 18, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 22, 22, 979, 0, 462, 638, 106, 106, 106, 106, 140, 140, 106, 70, 70, 70, 70, 70, 70, 9, 39, 2, -18, '', 0, 20, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 22, 22, 1067, 0, 473, 649, 109, 109, 109, 109, 142, 142, 109, 78, 78, 78, 78, 78, 78, 9, 41, 2, -18, '', 0, 22, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 22, 22, 1155, 0, 484, 660, 112, 112, 112, 112, 144, 144, 112, 86, 86, 86, 86, 86, 86, 10, 44, 2, -18, '', 0, 24, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 22, 22, 1243, 0, 495, 671, 115, 115, 115, 115, 146, 146, 115, 94, 94, 94, 94, 94, 94, 10, 46, 2, -18, '', 0, 26, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 22, 22, 1551, 0, 561, 693, 119, 119, 119, 119, 158, 158, 119, 92, 92, 92, 92, 92, 92, 9, 43, 2, -18, '', 0, 26, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 22, 22, 1639, 0, 572, 704, 122, 122, 122, 122, 160, 160, 122, 100, 100, 100, 100, 100, 100, 10, 45, 2, -18, '', 0, 28, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 22, 22, 1727, 0, 583, 715, 125, 125, 125, 125, 162, 162, 125, 108, 108, 108, 108, 108, 108, 10, 48, 2, -18, '', 0, 30, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 22, 22, 1815, 0, 594, 726, 128, 128, 128, 128, 164, 164, 128, 116, 116, 116, 116, 116, 116, 11, 50, 2, -18, '', 0, 32, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 22, 22, 1903, 0, 605, 737, 131, 131, 131, 131, 166, 166, 131, 124, 124, 124, 124, 124, 124, 11, 53, 2, -18, '', 0, 34, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 22, 22, 722, 1003, 293, 181, 90, 90, 90, 90, 180, 180, 90, 62, 62, 62, 62, 62, 62, 6, 38, 1, -18, '', 0, 12, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 22, 22, 766, 1069, 300, 187, 93, 93, 93, 93, 183, 183, 93, 70, 70, 70, 70, 70, 70, 7, 39, 1, -18, '', 0, 13, 10, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 22, 22, 810, 1135, 308, 192, 96, 96, 96, 96, 186, 186, 96, 78, 78, 78, 78, 78, 78, 7, 41, 1, -18, '', 0, 14, 11, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 22, 22, 854, 1201, 315, 198, 99, 99, 99, 99, 189, 189, 99, 86, 86, 86, 86, 86, 86, 7, 42, 1, -18, '', 0, 15, 12, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 22, 22, 898, 1267, 322, 203, 102, 102, 102, 102, 192, 192, 102, 94, 94, 94, 94, 94, 94, 7, 43, 1, -18, '', 0, 16, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 22, 22, 1272, 1553, 359, 203, 106, 106, 106, 106, 205, 205, 106, 92, 92, 92, 92, 92, 92, 7, 42, 1, -18, '', 0, 17, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 22, 22, 1316, 1619, 366, 209, 109, 109, 109, 109, 208, 208, 109, 100, 100, 100, 100, 100, 100, 7, 43, 1, -18, '', 0, 18, 13, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 22, 22, 1360, 1685, 374, 214, 112, 112, 112, 112, 211, 211, 112, 108, 108, 108, 108, 108, 108, 8, 45, 1, -18, '', 0, 19, 14, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 22, 22, 1404, 1751, 381, 220, 115, 115, 115, 115, 214, 214, 115, 116, 116, 116, 116, 116, 116, 8, 46, 1, -18, '', 0, 20, 15, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 22, 22, 1448, 1817, 388, 225, 118, 118, 118, 118, 217, 217, 118, 124, 124, 124, 124, 124, 124, 8, 48, 1, -18, '', 0, 21, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 23, 23, 1103, 0, 794, 644, 105, 105, 105, 105, 141, 141, 105, 64, 64, 64, 64, 64, 64, 8, 40, 2, -18, '', 0, 20, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 23, 23, 1218, 0, 828, 656, 108, 108, 108, 108, 143, 143, 108, 72, 72, 72, 72, 72, 72, 9, 43, 2, -18, '', 0, 22, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 23, 23, 1333, 0, 863, 667, 111, 111, 111, 111, 145, 145, 111, 80, 80, 80, 80, 80, 80, 9, 46, 2, -18, '', 0, 24, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 23, 23, 1448, 0, 897, 679, 114, 114, 114, 114, 147, 147, 114, 88, 88, 88, 88, 88, 88, 10, 49, 2, -18, '', 0, 26, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 23, 23, 1563, 0, 932, 690, 117, 117, 117, 117, 149, 149, 117, 96, 96, 96, 96, 96, 96, 10, 52, 2, -18, '', 0, 28, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 23, 23, 1908, 0, 978, 701, 121, 121, 121, 121, 161, 161, 121, 96, 96, 96, 96, 96, 96, 10, 47, 2, -18, '', 0, 30, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 23, 23, 2023, 0, 1012, 713, 124, 124, 124, 124, 163, 163, 124, 104, 104, 104, 104, 104, 104, 10, 50, 2, -18, '', 0, 32, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 23, 23, 2138, 0, 1047, 724, 127, 127, 127, 127, 165, 165, 127, 112, 112, 112, 112, 112, 112, 11, 53, 2, -18, '', 0, 34, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 23, 23, 2253, 0, 1081, 736, 130, 130, 130, 130, 167, 167, 130, 120, 120, 120, 120, 120, 120, 11, 56, 2, -18, '', 0, 36, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 23, 23, 2368, 0, 1116, 747, 133, 133, 133, 133, 169, 169, 133, 128, 128, 128, 128, 128, 128, 12, 59, 2, -18, '', 0, 38, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 23, 23, 912, 1067, 621, 387, 93, 93, 93, 93, 183, 183, 93, 64, 64, 64, 64, 64, 64, 7, 39, 1, -18, '', 0, 13, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 23, 23, 981, 1136, 644, 395, 96, 96, 96, 96, 186, 186, 96, 72, 72, 72, 72, 72, 72, 7, 41, 1, -18, '', 0, 14, 10, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 23, 23, 1050, 1205, 667, 403, 99, 99, 99, 99, 189, 189, 99, 80, 80, 80, 80, 80, 80, 7, 42, 1, -18, '', 0, 15, 11, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 23, 23, 1119, 1274, 690, 410, 102, 102, 102, 102, 192, 192, 102, 88, 88, 88, 88, 88, 88, 7, 43, 1, -18, '', 0, 16, 12, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 23, 23, 1188, 1343, 713, 418, 105, 105, 105, 105, 195, 195, 105, 96, 96, 96, 96, 96, 96, 8, 45, 1, -18, '', 0, 17, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 23, 23, 1602, 1642, 759, 421, 109, 109, 109, 109, 208, 208, 109, 96, 96, 96, 96, 96, 96, 7, 43, 1, -18, '', 0, 19, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 23, 23, 1671, 1711, 782, 429, 112, 112, 112, 112, 211, 211, 112, 104, 104, 104, 104, 104, 104, 8, 45, 1, -18, '', 0, 20, 13, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 23, 23, 1740, 1780, 805, 437, 115, 115, 115, 115, 214, 214, 115, 112, 112, 112, 112, 112, 112, 8, 46, 1, -18, '', 0, 21, 14, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 23, 23, 1809, 1849, 828, 444, 118, 118, 118, 118, 217, 217, 118, 120, 120, 120, 120, 120, 120, 8, 48, 1, -18, '', 0, 22, 15, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 23, 23, 1878, 1918, 851, 452, 121, 121, 121, 121, 220, 220, 121, 128, 128, 128, 128, 128, 128, 8, 49, 1, -18, '', 0, 23, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 23, 23, 944, 0, 471, 655, 105, 105, 105, 105, 141, 141, 105, 64, 64, 64, 64, 64, 64, 8, 37, 2, -18, '', 0, 18, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 23, 23, 1036, 0, 483, 667, 108, 108, 108, 108, 143, 143, 108, 72, 72, 72, 72, 72, 72, 9, 40, 2, -18, '', 0, 20, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 23, 23, 1128, 0, 494, 678, 111, 111, 111, 111, 145, 145, 111, 80, 80, 80, 80, 80, 80, 9, 43, 2, -18, '', 0, 22, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 23, 23, 1220, 0, 506, 690, 114, 114, 114, 114, 147, 147, 114, 88, 88, 88, 88, 88, 88, 10, 45, 2, -18, '', 0, 24, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 23, 23, 1312, 0, 517, 701, 117, 117, 117, 117, 149, 149, 117, 96, 96, 96, 96, 96, 96, 10, 48, 2, -18, '', 0, 26, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 23, 23, 1634, 0, 586, 724, 121, 121, 121, 121, 161, 161, 121, 96, 96, 96, 96, 96, 96, 10, 44, 2, -18, '', 0, 26, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 23, 23, 1726, 0, 598, 736, 124, 124, 124, 124, 163, 163, 124, 104, 104, 104, 104, 104, 104, 10, 46, 2, -18, '', 0, 28, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 23, 23, 1818, 0, 609, 747, 127, 127, 127, 127, 165, 165, 127, 112, 112, 112, 112, 112, 112, 11, 49, 2, -18, '', 0, 30, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 23, 23, 1910, 0, 621, 759, 130, 130, 130, 130, 167, 167, 130, 120, 120, 120, 120, 120, 120, 11, 52, 2, -18, '', 0, 32, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 23, 23, 2002, 0, 632, 770, 133, 133, 133, 133, 169, 169, 133, 128, 128, 128, 128, 128, 128, 12, 54, 2, -18, '', 0, 34, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 23, 23, 766, 1067, 306, 189, 92, 92, 92, 92, 183, 183, 92, 64, 64, 64, 64, 64, 64, 7, 39, 1, -18, '', 0, 12, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 23, 23, 812, 1136, 314, 195, 95, 95, 95, 95, 186, 186, 95, 72, 72, 72, 72, 72, 72, 7, 41, 1, -18, '', 0, 13, 10, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 23, 23, 858, 1205, 322, 201, 98, 98, 98, 98, 189, 189, 98, 80, 80, 80, 80, 80, 80, 7, 42, 1, -18, '', 0, 14, 11, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 23, 23, 904, 1274, 329, 207, 101, 101, 101, 101, 192, 192, 101, 88, 88, 88, 88, 88, 88, 7, 43, 1, -18, '', 0, 15, 12, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 23, 23, 950, 1343, 337, 212, 104, 104, 104, 104, 195, 195, 104, 96, 96, 96, 96, 96, 96, 8, 45, 1, -18, '', 0, 16, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 23, 23, 1341, 1642, 375, 212, 108, 108, 108, 108, 208, 208, 108, 96, 96, 96, 96, 96, 96, 7, 43, 1, -18, '', 0, 17, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 23, 23, 1387, 1711, 383, 218, 111, 111, 111, 111, 211, 211, 111, 104, 104, 104, 104, 104, 104, 8, 45, 1, -18, '', 0, 18, 13, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 23, 23, 1433, 1780, 391, 224, 114, 114, 114, 114, 214, 214, 114, 112, 112, 112, 112, 112, 112, 8, 46, 1, -18, '', 0, 19, 14, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 23, 23, 1479, 1849, 398, 230, 117, 117, 117, 117, 217, 217, 117, 120, 120, 120, 120, 120, 120, 8, 48, 1, -18, '', 0, 20, 15, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 23, 23, 1525, 1918, 406, 235, 120, 120, 120, 120, 220, 220, 120, 128, 128, 128, 128, 128, 128, 8, 49, 1, -18, '', 0, 21, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 24, 24, 1167, 0, 828, 672, 108, 108, 108, 108, 144, 144, 108, 67, 67, 67, 67, 67, 67, 9, 42, 2, -18, '', 0, 21, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 24, 24, 1287, 0, 864, 684, 111, 111, 111, 111, 146, 146, 111, 76, 76, 76, 76, 76, 76, 9, 45, 2, -18, '', 0, 23, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 24, 24, 1407, 0, 900, 696, 114, 114, 114, 114, 148, 148, 114, 85, 85, 85, 85, 85, 85, 10, 47, 2, -18, '', 0, 25, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 24, 24, 1527, 0, 936, 708, 117, 117, 117, 117, 150, 150, 117, 94, 94, 94, 94, 94, 94, 10, 50, 2, -18, '', 0, 27, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 24, 24, 1647, 0, 972, 720, 120, 120, 120, 120, 152, 152, 120, 103, 103, 103, 103, 103, 103, 11, 53, 2, -18, '', 0, 29, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 24, 24, 2007, 0, 1020, 732, 125, 125, 125, 125, 164, 164, 125, 99, 99, 99, 99, 99, 99, 10, 49, 2, -18, '', 0, 31, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 24, 24, 2127, 0, 1056, 744, 128, 128, 128, 128, 166, 166, 128, 108, 108, 108, 108, 108, 108, 10, 52, 2, -18, '', 0, 33, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 24, 24, 2247, 0, 1092, 756, 131, 131, 131, 131, 168, 168, 131, 117, 117, 117, 117, 117, 117, 11, 54, 2, -18, '', 0, 35, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 24, 24, 2367, 0, 1128, 768, 134, 134, 134, 134, 170, 170, 134, 126, 126, 126, 126, 126, 126, 11, 57, 2, -18, '', 0, 37, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 24, 24, 2487, 0, 1164, 780, 137, 137, 137, 137, 172, 172, 137, 135, 135, 135, 135, 135, 135, 12, 60, 2, -18, '', 0, 39, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 24, 24, 964, 1133, 648, 404, 96, 96, 96, 96, 187, 187, 96, 67, 67, 67, 67, 67, 67, 7, 41, 1, -18, '', 0, 14, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 24, 24, 1036, 1205, 672, 412, 99, 99, 99, 99, 190, 190, 99, 76, 76, 76, 76, 76, 76, 7, 42, 1, -18, '', 0, 15, 10, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 24, 24, 1108, 1277, 696, 420, 102, 102, 102, 102, 193, 193, 102, 85, 85, 85, 85, 85, 85, 7, 43, 1, -18, '', 0, 16, 11, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 24, 24, 1180, 1349, 720, 428, 105, 105, 105, 105, 196, 196, 105, 94, 94, 94, 94, 94, 94, 8, 45, 1, -18, '', 0, 17, 12, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 24, 24, 1252, 1421, 744, 436, 108, 108, 108, 108, 199, 199, 108, 103, 103, 103, 103, 103, 103, 8, 46, 1, -18, '', 0, 18, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 24, 24, 1684, 1733, 792, 440, 113, 113, 113, 113, 212, 212, 113, 99, 99, 99, 99, 99, 99, 8, 45, 1, -18, '', 0, 20, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 24, 24, 1756, 1805, 816, 448, 116, 116, 116, 116, 215, 215, 116, 108, 108, 108, 108, 108, 108, 8, 46, 1, -18, '', 0, 21, 13, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 24, 24, 1828, 1877, 840, 456, 119, 119, 119, 119, 218, 218, 119, 117, 117, 117, 117, 117, 117, 8, 48, 1, -18, '', 0, 22, 14, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 24, 24, 1900, 1949, 864, 464, 122, 122, 122, 122, 221, 221, 122, 126, 126, 126, 126, 126, 126, 8, 49, 1, -18, '', 0, 23, 15, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 24, 24, 1972, 2021, 888, 472, 125, 125, 125, 125, 224, 224, 125, 135, 135, 135, 135, 135, 135, 9, 50, 1, -18, '', 0, 24, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 24, 24, 998, 0, 492, 684, 108, 108, 108, 108, 144, 144, 108, 67, 67, 67, 67, 67, 67, 9, 39, 2, -18, '', 0, 19, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 24, 24, 1094, 0, 504, 696, 111, 111, 111, 111, 146, 146, 111, 76, 76, 76, 76, 76, 76, 9, 41, 2, -18, '', 0, 21, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 24, 24, 1190, 0, 516, 708, 114, 114, 114, 114, 148, 148, 114, 85, 85, 85, 85, 85, 85, 10, 44, 2, -18, '', 0, 23, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 24, 24, 1286, 0, 528, 720, 117, 117, 117, 117, 150, 150, 117, 94, 94, 94, 94, 94, 94, 10, 46, 2, -18, '', 0, 25, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 24, 24, 1382, 0, 540, 732, 120, 120, 120, 120, 152, 152, 120, 103, 103, 103, 103, 103, 103, 11, 49, 2, -18, '', 0, 27, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 24, 24, 1718, 0, 612, 756, 125, 125, 125, 125, 164, 164, 125, 99, 99, 99, 99, 99, 99, 10, 45, 2, -18, '', 0, 27, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 24, 24, 1814, 0, 624, 768, 128, 128, 128, 128, 166, 166, 128, 108, 108, 108, 108, 108, 108, 10, 48, 2, -18, '', 0, 29, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 24, 24, 1910, 0, 636, 780, 131, 131, 131, 131, 168, 168, 131, 117, 117, 117, 117, 117, 117, 11, 50, 2, -18, '', 0, 31, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 24, 24, 2006, 0, 648, 792, 134, 134, 134, 134, 170, 170, 134, 126, 126, 126, 126, 126, 126, 11, 53, 2, -18, '', 0, 33, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 24, 24, 2102, 0, 660, 804, 137, 137, 137, 137, 172, 172, 137, 135, 135, 135, 135, 135, 135, 12, 56, 2, -18, '', 0, 35, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 24, 24, 811, 1133, 320, 198, 95, 95, 95, 95, 187, 187, 95, 67, 67, 67, 67, 67, 67, 7, 41, 1, -18, '', 0, 13, 9, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 24, 24, 859, 1205, 328, 204, 98, 98, 98, 98, 190, 190, 98, 76, 76, 76, 76, 76, 76, 7, 42, 1, -18, '', 0, 14, 10, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 24, 24, 907, 1277, 336, 210, 101, 101, 101, 101, 193, 193, 101, 85, 85, 85, 85, 85, 85, 7, 43, 1, -18, '', 0, 15, 11, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 24, 24, 955, 1349, 344, 216, 104, 104, 104, 104, 196, 196, 104, 94, 94, 94, 94, 94, 94, 8, 45, 1, -18, '', 0, 16, 12, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 24, 24, 1003, 1421, 352, 222, 107, 107, 107, 107, 199, 199, 107, 103, 103, 103, 103, 103, 103, 8, 46, 1, -18, '', 0, 17, 13, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 24, 24, 1411, 1733, 392, 222, 112, 112, 112, 112, 212, 212, 112, 99, 99, 99, 99, 99, 99, 8, 45, 1, -18, '', 0, 18, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 24, 24, 1459, 1805, 400, 228, 115, 115, 115, 115, 215, 215, 115, 108, 108, 108, 108, 108, 108, 8, 46, 1, -18, '', 0, 19, 13, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 24, 24, 1507, 1877, 408, 234, 118, 118, 118, 118, 218, 218, 118, 117, 117, 117, 117, 117, 117, 8, 48, 1, -18, '', 0, 20, 14, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 24, 24, 1555, 1949, 416, 240, 121, 121, 121, 121, 221, 221, 121, 126, 126, 126, 126, 126, 126, 8, 49, 1, -18, '', 0, 21, 15, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 24, 24, 1603, 2021, 424, 246, 124, 124, 124, 124, 224, 224, 124, 135, 135, 135, 135, 135, 135, 9, 50, 1, -18, '', 0, 22, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 25, 25, 1233, 0, 863, 700, 111, 111, 111, 111, 147, 147, 111, 69, 69, 69, 69, 69, 69, 9, 43, 2, -18, '', 0, 21, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 25, 25, 1358, 0, 900, 713, 114, 114, 114, 114, 149, 149, 114, 78, 78, 78, 78, 78, 78, 9, 46, 2, -18, '', 0, 23, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 25, 25, 1483, 0, 938, 725, 117, 117, 117, 117, 151, 151, 117, 87, 87, 87, 87, 87, 87, 10, 49, 2, -18, '', 0, 25, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 25, 25, 1608, 0, 975, 738, 120, 120, 120, 120, 153, 153, 120, 96, 96, 96, 96, 96, 96, 10, 52, 2, -18, '', 0, 27, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 25, 25, 1733, 0, 1013, 750, 123, 123, 123, 123, 155, 155, 123, 105, 105, 105, 105, 105, 105, 11, 54, 2, -18, '', 0, 29, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 25, 25, 2108, 0, 1063, 762, 128, 128, 128, 128, 167, 167, 128, 101, 101, 101, 101, 101, 101, 10, 50, 2, -18, '', 0, 31, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 25, 25, 2233, 0, 1100, 775, 131, 131, 131, 131, 169, 169, 131, 110, 110, 110, 110, 110, 110, 11, 53, 2, -18, '', 0, 33, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 25, 25, 2358, 0, 1138, 787, 134, 134, 134, 134, 171, 171, 134, 119, 119, 119, 119, 119, 119, 11, 56, 2, -18, '', 0, 35, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 25, 25, 2483, 0, 1175, 800, 137, 137, 137, 137, 173, 173, 137, 128, 128, 128, 128, 128, 128, 12, 59, 2, -18, '', 0, 37, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 25, 25, 2608, 0, 1213, 812, 140, 140, 140, 140, 175, 175, 140, 137, 137, 137, 137, 137, 137, 12, 61, 2, -18, '', 0, 39, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 25, 25, 1018, 1200, 675, 421, 98, 98, 98, 98, 190, 190, 98, 69, 69, 69, 69, 69, 69, 7, 42, 1, -18, '', 0, 14, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 25, 25, 1093, 1275, 700, 429, 101, 101, 101, 101, 193, 193, 101, 78, 78, 78, 78, 78, 78, 7, 43, 1, -18, '', 0, 15, 11, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 25, 25, 1168, 1350, 725, 438, 104, 104, 104, 104, 196, 196, 104, 87, 87, 87, 87, 87, 87, 8, 45, 1, -18, '', 0, 16, 12, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 25, 25, 1243, 1425, 750, 446, 107, 107, 107, 107, 199, 199, 107, 96, 96, 96, 96, 96, 96, 8, 46, 1, -18, '', 0, 17, 13, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 25, 25, 1318, 1500, 775, 454, 110, 110, 110, 110, 202, 202, 110, 105, 105, 105, 105, 105, 105, 8, 48, 1, -18, '', 0, 18, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 25, 25, 1768, 1825, 825, 458, 115, 115, 115, 115, 215, 215, 115, 101, 101, 101, 101, 101, 101, 8, 46, 1, -18, '', 0, 20, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 25, 25, 1843, 1900, 850, 466, 118, 118, 118, 118, 218, 218, 118, 110, 110, 110, 110, 110, 110, 8, 48, 1, -18, '', 0, 21, 14, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 25, 25, 1918, 1975, 875, 475, 121, 121, 121, 121, 221, 221, 121, 119, 119, 119, 119, 119, 119, 8, 49, 1, -18, '', 0, 22, 15, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 25, 25, 1993, 2050, 900, 483, 124, 124, 124, 124, 224, 224, 124, 128, 128, 128, 128, 128, 128, 9, 50, 1, -18, '', 0, 23, 16, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 25, 25, 2068, 2125, 925, 491, 127, 127, 127, 127, 227, 227, 127, 137, 137, 137, 137, 137, 137, 9, 52, 1, -18, '', 0, 24, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 25, 25, 1055, 0, 512, 712, 111, 111, 111, 111, 147, 147, 111, 69, 69, 69, 69, 69, 69, 9, 40, 2, -18, '', 0, 19, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 25, 25, 1155, 0, 525, 725, 114, 114, 114, 114, 149, 149, 114, 78, 78, 78, 78, 78, 78, 9, 43, 2, -18, '', 0, 21, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 25, 25, 1255, 0, 537, 737, 117, 117, 117, 117, 151, 151, 117, 87, 87, 87, 87, 87, 87, 10, 45, 2, -18, '', 0, 23, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 25, 25, 1355, 0, 550, 750, 120, 120, 120, 120, 153, 153, 120, 96, 96, 96, 96, 96, 96, 10, 48, 2, -18, '', 0, 25, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 25, 25, 1455, 0, 562, 762, 123, 123, 123, 123, 155, 155, 123, 105, 105, 105, 105, 105, 105, 11, 50, 2, -18, '', 0, 27, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 25, 25, 1805, 0, 637, 787, 128, 128, 128, 128, 167, 167, 128, 101, 101, 101, 101, 101, 101, 10, 46, 2, -18, '', 0, 27, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 25, 25, 1905, 0, 650, 800, 131, 131, 131, 131, 169, 169, 131, 110, 110, 110, 110, 110, 110, 11, 49, 2, -18, '', 0, 29, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 25, 25, 2005, 0, 662, 812, 134, 134, 134, 134, 171, 171, 134, 119, 119, 119, 119, 119, 119, 11, 52, 2, -18, '', 0, 31, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 25, 25, 2105, 0, 675, 825, 137, 137, 137, 137, 173, 173, 137, 128, 128, 128, 128, 128, 128, 12, 54, 2, -18, '', 0, 33, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 25, 25, 2205, 0, 687, 837, 140, 140, 140, 140, 175, 175, 140, 137, 137, 137, 137, 137, 137, 12, 57, 2, -18, '', 0, 35, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 25, 25, 858, 1200, 333, 206, 97, 97, 97, 97, 190, 190, 97, 69, 69, 69, 69, 69, 69, 7, 42, 1, -18, '', 0, 13, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 25, 25, 908, 1275, 341, 212, 100, 100, 100, 100, 193, 193, 100, 78, 78, 78, 78, 78, 78, 7, 43, 1, -18, '', 0, 14, 11, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 25, 25, 958, 1350, 350, 218, 103, 103, 103, 103, 196, 196, 103, 87, 87, 87, 87, 87, 87, 8, 45, 1, -18, '', 0, 15, 12, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 25, 25, 1008, 1425, 358, 225, 106, 106, 106, 106, 199, 199, 106, 96, 96, 96, 96, 96, 96, 8, 46, 1, -18, '', 0, 16, 13, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 25, 25, 1058, 1500, 366, 231, 109, 109, 109, 109, 202, 202, 109, 105, 105, 105, 105, 105, 105, 8, 48, 1, -18, '', 0, 17, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 25, 25, 1483, 1825, 408, 231, 114, 114, 114, 114, 215, 215, 114, 101, 101, 101, 101, 101, 101, 8, 46, 1, -18, '', 0, 18, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 25, 25, 1533, 1900, 416, 237, 117, 117, 117, 117, 218, 218, 117, 110, 110, 110, 110, 110, 110, 8, 48, 1, -18, '', 0, 19, 14, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 25, 25, 1583, 1975, 425, 243, 120, 120, 120, 120, 221, 221, 120, 119, 119, 119, 119, 119, 119, 8, 49, 1, -18, '', 0, 20, 15, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 25, 25, 1633, 2050, 433, 250, 123, 123, 123, 123, 224, 224, 123, 128, 128, 128, 128, 128, 128, 9, 50, 1, -18, '', 0, 21, 16, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 25, 25, 1683, 2125, 441, 256, 126, 126, 126, 126, 227, 227, 126, 137, 137, 137, 137, 137, 137, 9, 52, 1, -18, '', 0, 22, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 26, 26, 1301, 0, 897, 728, 114, 114, 114, 114, 150, 150, 114, 71, 71, 71, 71, 71, 71, 9, 45, 2, -18, '', 0, 21, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 26, 26, 1431, 0, 936, 741, 117, 117, 117, 117, 152, 152, 117, 80, 80, 80, 80, 80, 80, 10, 47, 2, -18, '', 0, 23, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 26, 26, 1561, 0, 975, 754, 120, 120, 120, 120, 154, 154, 120, 89, 89, 89, 89, 89, 89, 10, 50, 2, -18, '', 0, 25, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 26, 26, 1691, 0, 1014, 767, 123, 123, 123, 123, 156, 156, 123, 98, 98, 98, 98, 98, 98, 11, 53, 2, -18, '', 0, 27, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 26, 26, 1821, 0, 1053, 780, 126, 126, 126, 126, 158, 158, 126, 107, 107, 107, 107, 107, 107, 11, 56, 2, -18, '', 0, 29, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 26, 26, 2211, 0, 1105, 793, 132, 132, 132, 132, 170, 170, 132, 105, 105, 105, 105, 105, 105, 10, 52, 2, -18, '', 0, 31, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 26, 26, 2341, 0, 1144, 806, 135, 135, 135, 135, 172, 172, 135, 114, 114, 114, 114, 114, 114, 11, 54, 2, -18, '', 0, 33, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 26, 26, 2471, 0, 1183, 819, 138, 138, 138, 138, 174, 174, 138, 123, 123, 123, 123, 123, 123, 11, 57, 2, -18, '', 0, 35, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 26, 26, 2601, 0, 1222, 832, 141, 141, 141, 141, 176, 176, 141, 132, 132, 132, 132, 132, 132, 12, 60, 2, -18, '', 0, 37, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 26, 26, 2731, 0, 1261, 845, 144, 144, 144, 144, 178, 178, 144, 141, 141, 141, 141, 141, 141, 12, 63, 2, -18, '', 0, 39, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 26, 26, 1074, 1269, 702, 437, 101, 101, 101, 101, 194, 194, 101, 71, 71, 71, 71, 71, 71, 7, 43, 1, -18, '', 0, 14, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 26, 26, 1152, 1347, 728, 446, 104, 104, 104, 104, 197, 197, 104, 80, 80, 80, 80, 80, 80, 8, 45, 1, -18, '', 0, 15, 11, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 26, 26, 1230, 1425, 754, 455, 107, 107, 107, 107, 200, 200, 107, 89, 89, 89, 89, 89, 89, 8, 46, 1, -18, '', 0, 16, 12, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 26, 26, 1308, 1503, 780, 463, 110, 110, 110, 110, 203, 203, 110, 98, 98, 98, 98, 98, 98, 8, 48, 1, -18, '', 0, 17, 13, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 26, 26, 1386, 1581, 806, 472, 113, 113, 113, 113, 206, 206, 113, 107, 107, 107, 107, 107, 107, 8, 49, 1, -18, '', 0, 18, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 26, 26, 1854, 1919, 858, 476, 119, 119, 119, 119, 219, 219, 119, 105, 105, 105, 105, 105, 105, 8, 48, 1, -18, '', 0, 20, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 26, 26, 1932, 1997, 884, 485, 122, 122, 122, 122, 222, 222, 122, 114, 114, 114, 114, 114, 114, 8, 49, 1, -18, '', 0, 21, 14, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 26, 26, 2010, 2075, 910, 494, 125, 125, 125, 125, 225, 225, 125, 123, 123, 123, 123, 123, 123, 9, 50, 1, -18, '', 0, 22, 15, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 26, 26, 2088, 2153, 936, 502, 128, 128, 128, 128, 228, 228, 128, 132, 132, 132, 132, 132, 132, 9, 52, 1, -18, '', 0, 23, 16, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 26, 26, 2166, 2231, 962, 511, 131, 131, 131, 131, 231, 231, 131, 141, 141, 141, 141, 141, 141, 9, 53, 1, -18, '', 0, 24, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 26, 26, 1113, 0, 533, 741, 114, 114, 114, 114, 150, 150, 114, 71, 71, 71, 71, 71, 71, 9, 41, 2, -18, '', 0, 19, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 26, 26, 1217, 0, 546, 754, 117, 117, 117, 117, 152, 152, 117, 80, 80, 80, 80, 80, 80, 10, 44, 2, -18, '', 0, 21, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 26, 26, 1321, 0, 559, 767, 120, 120, 120, 120, 154, 154, 120, 89, 89, 89, 89, 89, 89, 10, 46, 2, -18, '', 0, 23, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 26, 26, 1425, 0, 572, 780, 123, 123, 123, 123, 156, 156, 123, 98, 98, 98, 98, 98, 98, 11, 49, 2, -18, '', 0, 25, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 26, 26, 1529, 0, 585, 793, 126, 126, 126, 126, 158, 158, 126, 107, 107, 107, 107, 107, 107, 11, 52, 2, -18, '', 0, 27, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 26, 26, 1893, 0, 663, 819, 132, 132, 132, 132, 170, 170, 132, 105, 105, 105, 105, 105, 105, 10, 48, 2, -18, '', 0, 27, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 26, 26, 1997, 0, 676, 832, 135, 135, 135, 135, 172, 172, 135, 114, 114, 114, 114, 114, 114, 11, 50, 2, -18, '', 0, 29, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 26, 26, 2101, 0, 689, 845, 138, 138, 138, 138, 174, 174, 138, 123, 123, 123, 123, 123, 123, 11, 53, 2, -18, '', 0, 31, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 26, 26, 2205, 0, 702, 858, 141, 141, 141, 141, 176, 176, 141, 132, 132, 132, 132, 132, 132, 12, 56, 2, -18, '', 0, 33, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 26, 26, 2309, 0, 715, 871, 144, 144, 144, 144, 178, 178, 144, 141, 141, 141, 141, 141, 141, 12, 58, 2, -18, '', 0, 35, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 26, 26, 907, 1269, 346, 214, 100, 100, 100, 100, 194, 194, 100, 71, 71, 71, 71, 71, 71, 7, 43, 1, -18, '', 0, 13, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 26, 26, 959, 1347, 355, 221, 103, 103, 103, 103, 197, 197, 103, 80, 80, 80, 80, 80, 80, 8, 45, 1, -18, '', 0, 14, 11, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 26, 26, 1011, 1425, 364, 227, 106, 106, 106, 106, 200, 200, 106, 89, 89, 89, 89, 89, 89, 8, 46, 1, -18, '', 0, 15, 12, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 26, 26, 1063, 1503, 372, 234, 109, 109, 109, 109, 203, 203, 109, 98, 98, 98, 98, 98, 98, 8, 48, 1, -18, '', 0, 16, 13, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 26, 26, 1115, 1581, 381, 240, 112, 112, 112, 112, 206, 206, 112, 107, 107, 107, 107, 107, 107, 8, 49, 1, -18, '', 0, 17, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 26, 26, 1557, 1919, 424, 240, 118, 118, 118, 118, 219, 219, 118, 105, 105, 105, 105, 105, 105, 8, 48, 1, -18, '', 0, 18, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 26, 26, 1609, 1997, 433, 247, 121, 121, 121, 121, 222, 222, 121, 114, 114, 114, 114, 114, 114, 8, 49, 1, -18, '', 0, 19, 14, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 26, 26, 1661, 2075, 442, 253, 124, 124, 124, 124, 225, 225, 124, 123, 123, 123, 123, 123, 123, 9, 50, 1, -18, '', 0, 20, 15, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 26, 26, 1713, 2153, 450, 260, 127, 127, 127, 127, 228, 228, 127, 132, 132, 132, 132, 132, 132, 9, 52, 1, -18, '', 0, 21, 16, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 26, 26, 1765, 2231, 459, 266, 130, 130, 130, 130, 231, 231, 130, 141, 141, 141, 141, 141, 141, 9, 53, 1, -18, '', 0, 22, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 27, 27, 1371, 0, 932, 756, 117, 117, 117, 117, 153, 153, 117, 73, 73, 73, 73, 73, 73, 9, 46, 2, -18, '', 0, 22, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 27, 27, 1506, 0, 972, 770, 120, 120, 120, 120, 155, 155, 120, 82, 82, 82, 82, 82, 82, 10, 49, 2, -18, '', 0, 24, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 27, 27, 1641, 0, 1013, 783, 123, 123, 123, 123, 157, 157, 123, 91, 91, 91, 91, 91, 91, 10, 52, 2, -18, '', 0, 26, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 27, 27, 1776, 0, 1053, 797, 126, 126, 126, 126, 159, 159, 126, 100, 100, 100, 100, 100, 100, 11, 54, 2, -18, '', 0, 28, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 27, 27, 1911, 0, 1094, 810, 129, 129, 129, 129, 161, 161, 129, 109, 109, 109, 109, 109, 109, 11, 57, 2, -18, '', 0, 30, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 27, 27, 2316, 0, 1148, 823, 135, 135, 135, 135, 173, 173, 135, 107, 107, 107, 107, 107, 107, 11, 53, 2, -18, '', 0, 32, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 27, 27, 2451, 0, 1188, 837, 138, 138, 138, 138, 175, 175, 138, 116, 116, 116, 116, 116, 116, 11, 56, 2, -18, '', 0, 34, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 27, 27, 2586, 0, 1229, 850, 141, 141, 141, 141, 177, 177, 141, 125, 125, 125, 125, 125, 125, 12, 59, 2, -18, '', 0, 36, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 27, 27, 2721, 0, 1269, 864, 144, 144, 144, 144, 179, 179, 144, 134, 134, 134, 134, 134, 134, 12, 61, 2, -18, '', 0, 38, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 27, 27, 2856, 0, 1310, 877, 147, 147, 147, 147, 181, 181, 147, 143, 143, 143, 143, 143, 143, 13, 64, 2, -18, '', 0, 40, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 27, 27, 1132, 1339, 729, 455, 104, 104, 104, 104, 197, 197, 104, 73, 73, 73, 73, 73, 73, 8, 45, 1, -18, '', 0, 14, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 27, 27, 1213, 1420, 756, 464, 107, 107, 107, 107, 200, 200, 107, 82, 82, 82, 82, 82, 82, 8, 46, 1, -18, '', 0, 15, 11, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 27, 27, 1294, 1501, 783, 473, 110, 110, 110, 110, 203, 203, 110, 91, 91, 91, 91, 91, 91, 8, 48, 1, -18, '', 0, 16, 12, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 27, 27, 1375, 1582, 810, 482, 113, 113, 113, 113, 206, 206, 113, 100, 100, 100, 100, 100, 100, 8, 49, 1, -18, '', 0, 17, 13, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 27, 27, 1456, 1663, 837, 491, 116, 116, 116, 116, 209, 209, 116, 109, 109, 109, 109, 109, 109, 9, 50, 1, -18, '', 0, 18, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 27, 27, 1942, 2014, 891, 495, 122, 122, 122, 122, 222, 222, 122, 107, 107, 107, 107, 107, 107, 8, 49, 1, -18, '', 0, 20, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 27, 27, 2023, 2095, 918, 504, 125, 125, 125, 125, 225, 225, 125, 116, 116, 116, 116, 116, 116, 9, 50, 1, -18, '', 0, 21, 14, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 27, 27, 2104, 2176, 945, 513, 128, 128, 128, 128, 228, 228, 128, 125, 125, 125, 125, 125, 125, 9, 52, 1, -18, '', 0, 22, 15, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 27, 27, 2185, 2257, 972, 522, 131, 131, 131, 131, 231, 231, 131, 134, 134, 134, 134, 134, 134, 9, 53, 1, -18, '', 0, 23, 16, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 27, 27, 2266, 2338, 999, 531, 134, 134, 134, 134, 234, 234, 134, 143, 143, 143, 143, 143, 143, 9, 55, 1, -18, '', 0, 24, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 27, 27, 1174, 0, 553, 769, 117, 117, 117, 117, 153, 153, 117, 73, 73, 73, 73, 73, 73, 9, 43, 2, -18, '', 0, 20, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 27, 27, 1282, 0, 567, 783, 120, 120, 120, 120, 155, 155, 120, 82, 82, 82, 82, 82, 82, 10, 45, 2, -18, '', 0, 22, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 27, 27, 1390, 0, 580, 796, 123, 123, 123, 123, 157, 157, 123, 91, 91, 91, 91, 91, 91, 10, 48, 2, -18, '', 0, 24, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 27, 27, 1498, 0, 594, 810, 126, 126, 126, 126, 159, 159, 126, 100, 100, 100, 100, 100, 100, 11, 50, 2, -18, '', 0, 26, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 27, 27, 1606, 0, 607, 823, 129, 129, 129, 129, 161, 161, 129, 109, 109, 109, 109, 109, 109, 11, 53, 2, -18, '', 0, 28, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 27, 27, 1984, 0, 688, 850, 135, 135, 135, 135, 173, 173, 135, 107, 107, 107, 107, 107, 107, 11, 49, 2, -18, '', 0, 28, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 27, 27, 2092, 0, 702, 864, 138, 138, 138, 138, 175, 175, 138, 116, 116, 116, 116, 116, 116, 11, 52, 2, -18, '', 0, 30, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 27, 27, 2200, 0, 715, 877, 141, 141, 141, 141, 177, 177, 141, 125, 125, 125, 125, 125, 125, 12, 54, 2, -18, '', 0, 32, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 27, 27, 2308, 0, 729, 891, 144, 144, 144, 144, 179, 179, 144, 134, 134, 134, 134, 134, 134, 12, 57, 2, -18, '', 0, 34, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 27, 27, 2416, 0, 742, 904, 147, 147, 147, 147, 181, 181, 147, 143, 143, 143, 143, 143, 143, 13, 59, 2, -18, '', 0, 36, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 27, 27, 957, 1339, 360, 222, 102, 102, 102, 102, 197, 197, 102, 73, 73, 73, 73, 73, 73, 8, 45, 1, -18, '', 0, 13, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 27, 27, 1011, 1420, 369, 229, 105, 105, 105, 105, 200, 200, 105, 82, 82, 82, 82, 82, 82, 8, 46, 1, -18, '', 0, 14, 11, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 27, 27, 1065, 1501, 378, 236, 108, 108, 108, 108, 203, 203, 108, 91, 91, 91, 91, 91, 91, 8, 48, 1, -18, '', 0, 15, 12, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 27, 27, 1119, 1582, 387, 243, 111, 111, 111, 111, 206, 206, 111, 100, 100, 100, 100, 100, 100, 8, 49, 1, -18, '', 0, 16, 13, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 27, 27, 1173, 1663, 396, 249, 114, 114, 114, 114, 209, 209, 114, 109, 109, 109, 109, 109, 109, 9, 50, 1, -18, '', 0, 17, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 27, 27, 1632, 2014, 441, 249, 120, 120, 120, 120, 222, 222, 120, 107, 107, 107, 107, 107, 107, 8, 49, 1, -18, '', 0, 18, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 27, 27, 1686, 2095, 450, 256, 123, 123, 123, 123, 225, 225, 123, 116, 116, 116, 116, 116, 116, 9, 50, 1, -18, '', 0, 19, 14, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 27, 27, 1740, 2176, 459, 263, 126, 126, 126, 126, 228, 228, 126, 125, 125, 125, 125, 125, 125, 9, 52, 1, -18, '', 0, 20, 15, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 27, 27, 1794, 2257, 468, 270, 129, 129, 129, 129, 231, 231, 129, 134, 134, 134, 134, 134, 134, 9, 53, 1, -18, '', 0, 21, 16, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 27, 27, 1848, 2338, 477, 276, 132, 132, 132, 132, 234, 234, 132, 143, 143, 143, 143, 143, 143, 9, 55, 1, -18, '', 0, 22, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 28, 28, 1443, 0, 966, 784, 121, 121, 121, 121, 156, 156, 121, 76, 76, 76, 76, 76, 76, 10, 47, 2, -18, '', 0, 22, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 28, 28, 1583, 0, 1008, 798, 124, 124, 124, 124, 158, 158, 124, 86, 86, 86, 86, 86, 86, 10, 50, 2, -18, '', 0, 24, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 28, 28, 1723, 0, 1050, 812, 127, 127, 127, 127, 160, 160, 127, 96, 96, 96, 96, 96, 96, 11, 53, 2, -18, '', 0, 26, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 28, 28, 1863, 0, 1092, 826, 130, 130, 130, 130, 162, 162, 130, 106, 106, 106, 106, 106, 106, 11, 56, 2, -18, '', 0, 28, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 28, 28, 2003, 0, 1134, 840, 133, 133, 133, 133, 164, 164, 133, 116, 116, 116, 116, 116, 116, 12, 59, 2, -18, '', 0, 30, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 28, 28, 2423, 0, 1190, 854, 140, 140, 140, 140, 176, 176, 140, 110, 110, 110, 110, 110, 110, 11, 54, 2, -18, '', 0, 32, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 28, 28, 2563, 0, 1232, 868, 143, 143, 143, 143, 178, 178, 143, 120, 120, 120, 120, 120, 120, 11, 57, 2, -18, '', 0, 34, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 28, 28, 2703, 0, 1274, 882, 146, 146, 146, 146, 180, 180, 146, 130, 130, 130, 130, 130, 130, 12, 60, 2, -18, '', 0, 36, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 28, 28, 2843, 0, 1316, 896, 149, 149, 149, 149, 182, 182, 149, 140, 140, 140, 140, 140, 140, 12, 63, 2, -18, '', 0, 38, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 28, 28, 2983, 0, 1358, 910, 152, 152, 152, 152, 184, 184, 152, 150, 150, 150, 150, 150, 150, 13, 66, 2, -18, '', 0, 40, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 28, 28, 1192, 1411, 756, 471, 107, 107, 107, 107, 201, 201, 107, 76, 76, 76, 76, 76, 76, 8, 46, 1, -18, '', 0, 15, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 28, 28, 1276, 1495, 784, 480, 110, 110, 110, 110, 204, 204, 110, 86, 86, 86, 86, 86, 86, 8, 48, 1, -18, '', 0, 16, 11, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 28, 28, 1360, 1579, 812, 490, 113, 113, 113, 113, 207, 207, 113, 96, 96, 96, 96, 96, 96, 8, 49, 1, -18, '', 0, 17, 12, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 28, 28, 1444, 1663, 840, 499, 116, 116, 116, 116, 210, 210, 116, 106, 106, 106, 106, 106, 106, 9, 50, 1, -18, '', 0, 18, 13, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 28, 28, 1528, 1747, 868, 508, 119, 119, 119, 119, 213, 213, 119, 116, 116, 116, 116, 116, 116, 9, 52, 1, -18, '', 0, 19, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 28, 28, 2032, 2111, 924, 513, 126, 126, 126, 126, 226, 226, 126, 110, 110, 110, 110, 110, 110, 9, 50, 1, -18, '', 0, 21, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 28, 28, 2116, 2195, 952, 522, 129, 129, 129, 129, 229, 229, 129, 120, 120, 120, 120, 120, 120, 9, 52, 1, -18, '', 0, 22, 14, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 28, 28, 2200, 2279, 980, 532, 132, 132, 132, 132, 232, 232, 132, 130, 130, 130, 130, 130, 130, 9, 53, 1, -18, '', 0, 23, 15, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 28, 28, 2284, 2363, 1008, 541, 135, 135, 135, 135, 235, 235, 135, 140, 140, 140, 140, 140, 140, 9, 55, 1, -18, '', 0, 24, 16, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 28, 28, 2368, 2447, 1036, 550, 138, 138, 138, 138, 238, 238, 138, 150, 150, 150, 150, 150, 150, 10, 56, 1, -18, '', 0, 25, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 28, 28, 1236, 0, 574, 798, 121, 121, 121, 121, 156, 156, 121, 76, 76, 76, 76, 76, 76, 10, 44, 2, -18, '', 0, 20, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 28, 28, 1348, 0, 588, 812, 124, 124, 124, 124, 158, 158, 124, 86, 86, 86, 86, 86, 86, 10, 46, 2, -18, '', 0, 22, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 28, 28, 1460, 0, 602, 826, 127, 127, 127, 127, 160, 160, 127, 96, 96, 96, 96, 96, 96, 11, 49, 2, -18, '', 0, 24, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 28, 28, 1572, 0, 616, 840, 130, 130, 130, 130, 162, 162, 130, 106, 106, 106, 106, 106, 106, 11, 52, 2, -18, '', 0, 26, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 28, 28, 1684, 0, 630, 854, 133, 133, 133, 133, 164, 164, 133, 116, 116, 116, 116, 116, 116, 12, 54, 2, -18, '', 0, 28, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 28, 28, 2076, 0, 714, 882, 140, 140, 140, 140, 176, 176, 140, 110, 110, 110, 110, 110, 110, 11, 50, 2, -18, '', 0, 28, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 28, 28, 2188, 0, 728, 896, 143, 143, 143, 143, 178, 178, 143, 120, 120, 120, 120, 120, 120, 11, 53, 2, -18, '', 0, 30, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 28, 28, 2300, 0, 742, 910, 146, 146, 146, 146, 180, 180, 146, 130, 130, 130, 130, 130, 130, 12, 56, 2, -18, '', 0, 32, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 28, 28, 2412, 0, 756, 924, 149, 149, 149, 149, 182, 182, 149, 140, 140, 140, 140, 140, 140, 12, 58, 2, -18, '', 0, 34, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 28, 28, 2524, 0, 770, 938, 152, 152, 152, 152, 184, 184, 152, 150, 150, 150, 150, 150, 150, 13, 61, 2, -18, '', 0, 36, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 28, 28, 1009, 1411, 373, 231, 106, 106, 106, 106, 201, 201, 106, 76, 76, 76, 76, 76, 76, 8, 46, 1, -18, '', 0, 14, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 28, 28, 1065, 1495, 382, 238, 109, 109, 109, 109, 204, 204, 109, 86, 86, 86, 86, 86, 86, 8, 48, 1, -18, '', 0, 15, 11, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 28, 28, 1121, 1579, 392, 245, 112, 112, 112, 112, 207, 207, 112, 96, 96, 96, 96, 96, 96, 8, 49, 1, -18, '', 0, 16, 12, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 28, 28, 1177, 1663, 401, 252, 115, 115, 115, 115, 210, 210, 115, 106, 106, 106, 106, 106, 106, 9, 50, 1, -18, '', 0, 17, 13, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 28, 28, 1233, 1747, 410, 259, 118, 118, 118, 118, 213, 213, 118, 116, 116, 116, 116, 116, 116, 9, 52, 1, -18, '', 0, 18, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 28, 28, 1709, 2111, 457, 259, 125, 125, 125, 125, 226, 226, 125, 110, 110, 110, 110, 110, 110, 9, 50, 1, -18, '', 0, 19, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 28, 28, 1765, 2195, 466, 266, 128, 128, 128, 128, 229, 229, 128, 120, 120, 120, 120, 120, 120, 9, 52, 1, -18, '', 0, 20, 14, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 28, 28, 1821, 2279, 476, 273, 131, 131, 131, 131, 232, 232, 131, 130, 130, 130, 130, 130, 130, 9, 53, 1, -18, '', 0, 21, 15, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 28, 28, 1877, 2363, 485, 280, 134, 134, 134, 134, 235, 235, 134, 140, 140, 140, 140, 140, 140, 9, 55, 1, -18, '', 0, 22, 16, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 28, 28, 1933, 2447, 494, 287, 137, 137, 137, 137, 238, 238, 137, 150, 150, 150, 150, 150, 150, 10, 56, 1, -18, '', 0, 23, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 29, 29, 1518, 0, 1001, 812, 124, 124, 124, 124, 159, 159, 124, 78, 78, 78, 78, 78, 78, 10, 49, 2, -18, '', 0, 22, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 29, 29, 1663, 0, 1044, 827, 127, 127, 127, 127, 161, 161, 127, 88, 88, 88, 88, 88, 88, 10, 52, 2, -18, '', 0, 24, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 29, 29, 1808, 0, 1088, 841, 130, 130, 130, 130, 163, 163, 130, 98, 98, 98, 98, 98, 98, 11, 54, 2, -18, '', 0, 26, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 29, 29, 1953, 0, 1131, 856, 133, 133, 133, 133, 165, 165, 133, 108, 108, 108, 108, 108, 108, 11, 57, 2, -18, '', 0, 28, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 29, 29, 2098, 0, 1175, 870, 136, 136, 136, 136, 167, 167, 136, 118, 118, 118, 118, 118, 118, 12, 60, 2, -18, '', 0, 30, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 29, 29, 2533, 0, 1233, 884, 143, 143, 143, 143, 179, 179, 143, 114, 114, 114, 114, 114, 114, 11, 56, 2, -18, '', 0, 32, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 29, 29, 2678, 0, 1276, 899, 146, 146, 146, 146, 181, 181, 146, 124, 124, 124, 124, 124, 124, 12, 59, 2, -18, '', 0, 34, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 29, 29, 2823, 0, 1320, 913, 149, 149, 149, 149, 183, 183, 149, 134, 134, 134, 134, 134, 134, 12, 61, 2, -18, '', 0, 36, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 29, 29, 2968, 0, 1363, 928, 152, 152, 152, 152, 185, 185, 152, 144, 144, 144, 144, 144, 144, 13, 64, 2, -18, '', 0, 38, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 29, 29, 3113, 0, 1407, 942, 155, 155, 155, 155, 187, 187, 155, 154, 154, 154, 154, 154, 154, 13, 67, 2, -18, '', 0, 40, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 29, 29, 1253, 1485, 783, 488, 110, 110, 110, 110, 204, 204, 110, 78, 78, 78, 78, 78, 78, 8, 48, 1, -18, '', 0, 15, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 29, 29, 1340, 1572, 812, 498, 113, 113, 113, 113, 207, 207, 113, 88, 88, 88, 88, 88, 88, 8, 49, 1, -18, '', 0, 16, 11, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 29, 29, 1427, 1659, 841, 508, 116, 116, 116, 116, 210, 210, 116, 98, 98, 98, 98, 98, 98, 9, 50, 1, -18, '', 0, 17, 12, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 29, 29, 1514, 1746, 870, 517, 119, 119, 119, 119, 213, 213, 119, 108, 108, 108, 108, 108, 108, 9, 52, 1, -18, '', 0, 18, 13, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 29, 29, 1601, 1833, 899, 527, 122, 122, 122, 122, 216, 216, 122, 118, 118, 118, 118, 118, 118, 9, 53, 1, -18, '', 0, 19, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 29, 29, 2123, 2210, 957, 531, 129, 129, 129, 129, 229, 229, 129, 114, 114, 114, 114, 114, 114, 9, 52, 1, -18, '', 0, 21, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 29, 29, 2210, 2297, 986, 541, 132, 132, 132, 132, 232, 232, 132, 124, 124, 124, 124, 124, 124, 9, 53, 1, -18, '', 0, 22, 14, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 29, 29, 2297, 2384, 1015, 551, 135, 135, 135, 135, 235, 235, 135, 134, 134, 134, 134, 134, 134, 9, 55, 1, -18, '', 0, 23, 15, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 29, 29, 2384, 2471, 1044, 560, 138, 138, 138, 138, 238, 238, 138, 144, 144, 144, 144, 144, 144, 10, 56, 1, -18, '', 0, 24, 16, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 29, 29, 2471, 2558, 1073, 570, 141, 141, 141, 141, 241, 241, 141, 154, 154, 154, 154, 154, 154, 10, 57, 1, -18, '', 0, 25, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 29, 29, 1300, 0, 594, 826, 124, 124, 124, 124, 159, 159, 124, 78, 78, 78, 78, 78, 78, 10, 45, 2, -18, '', 0, 20, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 29, 29, 1416, 0, 609, 841, 127, 127, 127, 127, 161, 161, 127, 88, 88, 88, 88, 88, 88, 10, 48, 2, -18, '', 0, 22, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 29, 29, 1532, 0, 623, 855, 130, 130, 130, 130, 163, 163, 130, 98, 98, 98, 98, 98, 98, 11, 50, 2, -18, '', 0, 24, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 29, 29, 1648, 0, 638, 870, 133, 133, 133, 133, 165, 165, 133, 108, 108, 108, 108, 108, 108, 11, 53, 2, -18, '', 0, 26, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 29, 29, 1764, 0, 652, 884, 136, 136, 136, 136, 167, 167, 136, 118, 118, 118, 118, 118, 118, 12, 56, 2, -18, '', 0, 28, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 29, 29, 2170, 0, 739, 913, 143, 143, 143, 143, 179, 179, 143, 114, 114, 114, 114, 114, 114, 11, 52, 2, -18, '', 0, 28, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 29, 29, 2286, 0, 754, 928, 146, 146, 146, 146, 181, 181, 146, 124, 124, 124, 124, 124, 124, 12, 54, 2, -18, '', 0, 30, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 29, 29, 2402, 0, 768, 942, 149, 149, 149, 149, 183, 183, 149, 134, 134, 134, 134, 134, 134, 12, 57, 2, -18, '', 0, 32, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 29, 29, 2518, 0, 783, 957, 152, 152, 152, 152, 185, 185, 152, 144, 144, 144, 144, 144, 144, 13, 59, 2, -18, '', 0, 34, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 29, 29, 2634, 0, 797, 971, 155, 155, 155, 155, 187, 187, 155, 154, 154, 154, 154, 154, 154, 13, 62, 2, -18, '', 0, 36, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 29, 29, 1063, 1485, 386, 239, 108, 108, 108, 108, 204, 204, 108, 78, 78, 78, 78, 78, 78, 8, 48, 1, -18, '', 0, 14, 10, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 29, 29, 1121, 1572, 396, 246, 111, 111, 111, 111, 207, 207, 111, 88, 88, 88, 88, 88, 88, 8, 49, 1, -18, '', 0, 15, 11, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 29, 29, 1179, 1659, 406, 253, 114, 114, 114, 114, 210, 210, 114, 98, 98, 98, 98, 98, 98, 9, 50, 1, -18, '', 0, 16, 12, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 29, 29, 1237, 1746, 415, 261, 117, 117, 117, 117, 213, 213, 117, 108, 108, 108, 108, 108, 108, 9, 52, 1, -18, '', 0, 17, 13, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 29, 29, 1295, 1833, 425, 268, 120, 120, 120, 120, 216, 216, 120, 118, 118, 118, 118, 118, 118, 9, 53, 1, -18, '', 0, 18, 14, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 29, 29, 1788, 2210, 473, 268, 127, 127, 127, 127, 229, 229, 127, 114, 114, 114, 114, 114, 114, 9, 52, 1, -18, '', 0, 19, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 29, 29, 1846, 2297, 483, 275, 130, 130, 130, 130, 232, 232, 130, 124, 124, 124, 124, 124, 124, 9, 53, 1, -18, '', 0, 20, 14, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 29, 29, 1904, 2384, 493, 282, 133, 133, 133, 133, 235, 235, 133, 134, 134, 134, 134, 134, 134, 9, 55, 1, -18, '', 0, 21, 15, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 29, 29, 1962, 2471, 502, 290, 136, 136, 136, 136, 238, 238, 136, 144, 144, 144, 144, 144, 144, 10, 56, 1, -18, '', 0, 22, 16, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 29, 29, 2020, 2558, 512, 297, 139, 139, 139, 139, 241, 241, 139, 154, 154, 154, 154, 154, 154, 10, 57, 1, -18, '', 0, 23, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 30, 30, 1595, 0, 1035, 840, 129, 129, 129, 129, 162, 162, 129, 80, 80, 80, 80, 80, 80, 10, 50, 2, -18, '', 0, 23, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 30, 30, 1745, 0, 1080, 855, 133, 133, 133, 133, 164, 164, 133, 90, 90, 90, 90, 90, 90, 11, 53, 2, -18, '', 0, 25, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 30, 30, 1895, 0, 1125, 870, 137, 137, 137, 137, 166, 166, 137, 100, 100, 100, 100, 100, 100, 11, 56, 2, -18, '', 0, 27, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 30, 30, 2045, 0, 1170, 885, 141, 141, 141, 141, 168, 168, 141, 110, 110, 110, 110, 110, 110, 12, 59, 2, -18, '', 0, 29, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 30, 30, 2195, 0, 1215, 900, 145, 145, 145, 145, 170, 170, 145, 120, 120, 120, 120, 120, 120, 12, 61, 2, -18, '', 0, 31, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 30, 30, 2645, 0, 1275, 915, 149, 149, 149, 149, 182, 182, 149, 116, 116, 116, 116, 116, 116, 11, 57, 2, -18, '', 0, 33, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 30, 30, 2795, 0, 1320, 930, 153, 153, 153, 153, 184, 184, 153, 126, 126, 126, 126, 126, 126, 12, 60, 2, -18, '', 0, 35, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 30, 30, 2945, 0, 1365, 945, 157, 157, 157, 157, 186, 186, 157, 136, 136, 136, 136, 136, 136, 12, 63, 2, -18, '', 0, 37, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 30, 30, 3095, 0, 1410, 960, 161, 161, 161, 161, 188, 188, 161, 146, 146, 146, 146, 146, 146, 13, 66, 2, -18, '', 0, 39, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 30, 30, 3245, 0, 1455, 975, 165, 165, 165, 165, 190, 190, 165, 156, 156, 156, 156, 156, 156, 13, 68, 2, -18, '', 0, 41, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 30, 30, 1317, 1560, 810, 505, 114, 114, 114, 114, 208, 208, 114, 80, 80, 80, 80, 80, 80, 8, 49, 1, -18, '', 0, 15, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 30, 30, 1407, 1650, 840, 515, 118, 118, 118, 118, 211, 211, 118, 90, 90, 90, 90, 90, 90, 9, 50, 1, -18, '', 0, 16, 12, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 30, 30, 1497, 1740, 870, 525, 122, 122, 122, 122, 214, 214, 122, 100, 100, 100, 100, 100, 100, 9, 52, 1, -18, '', 0, 17, 13, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 30, 30, 1587, 1830, 900, 535, 126, 126, 126, 126, 217, 217, 126, 110, 110, 110, 110, 110, 110, 9, 53, 1, -18, '', 0, 18, 14, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 30, 30, 1677, 1920, 930, 545, 130, 130, 130, 130, 220, 220, 130, 120, 120, 120, 120, 120, 120, 9, 55, 1, -18, '', 0, 19, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 30, 30, 2217, 2310, 990, 550, 134, 134, 134, 134, 233, 233, 134, 116, 116, 116, 116, 116, 116, 9, 53, 1, -18, '', 0, 21, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 30, 30, 2307, 2400, 1020, 560, 138, 138, 138, 138, 236, 236, 138, 126, 126, 126, 126, 126, 126, 9, 55, 1, -18, '', 0, 22, 15, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 30, 30, 2397, 2490, 1050, 570, 142, 142, 142, 142, 239, 239, 142, 136, 136, 136, 136, 136, 136, 10, 56, 1, -18, '', 0, 23, 16, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 30, 30, 2487, 2580, 1080, 580, 146, 146, 146, 146, 242, 242, 146, 146, 146, 146, 146, 146, 146, 10, 57, 1, -18, '', 0, 24, 17, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 30, 30, 2577, 2670, 1110, 590, 150, 150, 150, 150, 245, 245, 150, 156, 156, 156, 156, 156, 156, 10, 59, 1, -18, '', 0, 25, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 30, 30, 1367, 0, 615, 855, 129, 129, 129, 129, 162, 162, 129, 80, 80, 80, 80, 80, 80, 10, 46, 2, -18, '', 0, 21, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 30, 30, 1487, 0, 630, 870, 133, 133, 133, 133, 164, 164, 133, 90, 90, 90, 90, 90, 90, 11, 49, 2, -18, '', 0, 23, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 30, 30, 1607, 0, 645, 885, 137, 137, 137, 137, 166, 166, 137, 100, 100, 100, 100, 100, 100, 11, 52, 2, -18, '', 0, 25, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 30, 30, 1727, 0, 660, 900, 141, 141, 141, 141, 168, 168, 141, 110, 110, 110, 110, 110, 110, 12, 54, 2, -18, '', 0, 27, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 30, 30, 1847, 0, 675, 915, 145, 145, 145, 145, 170, 170, 145, 120, 120, 120, 120, 120, 120, 12, 57, 2, -18, '', 0, 29, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 30, 30, 2267, 0, 765, 945, 149, 149, 149, 149, 182, 182, 149, 116, 116, 116, 116, 116, 116, 11, 53, 2, -18, '', 0, 29, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 30, 30, 2387, 0, 780, 960, 153, 153, 153, 153, 184, 184, 153, 126, 126, 126, 126, 126, 126, 12, 56, 2, -18, '', 0, 31, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 30, 30, 2507, 0, 795, 975, 157, 157, 157, 157, 186, 186, 157, 136, 136, 136, 136, 136, 136, 12, 58, 2, -18, '', 0, 33, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 30, 30, 2627, 0, 810, 990, 161, 161, 161, 161, 188, 188, 161, 146, 146, 146, 146, 146, 146, 13, 61, 2, -18, '', 0, 35, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 30, 30, 2747, 0, 825, 1005, 165, 165, 165, 165, 190, 190, 165, 156, 156, 156, 156, 156, 156, 13, 63, 2, -18, '', 0, 37, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 30, 30, 1120, 1560, 400, 247, 113, 113, 113, 113, 208, 208, 113, 80, 80, 80, 80, 80, 80, 8, 49, 1, -18, '', 0, 14, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 30, 30, 1180, 1650, 410, 255, 117, 117, 117, 117, 211, 211, 117, 90, 90, 90, 90, 90, 90, 9, 50, 1, -18, '', 0, 15, 12, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 30, 30, 1240, 1740, 420, 262, 121, 121, 121, 121, 214, 214, 121, 100, 100, 100, 100, 100, 100, 9, 52, 1, -18, '', 0, 16, 13, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 30, 30, 1300, 1830, 430, 270, 125, 125, 125, 125, 217, 217, 125, 110, 110, 110, 110, 110, 110, 9, 53, 1, -18, '', 0, 17, 14, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 30, 30, 1360, 1920, 440, 277, 129, 129, 129, 129, 220, 220, 129, 120, 120, 120, 120, 120, 120, 9, 55, 1, -18, '', 0, 18, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 30, 30, 1870, 2310, 490, 277, 133, 133, 133, 133, 233, 233, 133, 116, 116, 116, 116, 116, 116, 9, 53, 1, -18, '', 0, 19, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 30, 30, 1930, 2400, 500, 285, 137, 137, 137, 137, 236, 236, 137, 126, 126, 126, 126, 126, 126, 9, 55, 1, -18, '', 0, 20, 15, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 30, 30, 1990, 2490, 510, 292, 141, 141, 141, 141, 239, 239, 141, 136, 136, 136, 136, 136, 136, 10, 56, 1, -18, '', 0, 21, 16, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 30, 30, 2050, 2580, 520, 300, 145, 145, 145, 145, 242, 242, 145, 146, 146, 146, 146, 146, 146, 10, 57, 1, -18, '', 0, 22, 17, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 30, 30, 2110, 2670, 530, 307, 149, 149, 149, 149, 245, 245, 149, 156, 156, 156, 156, 156, 156, 10, 59, 1, -18, '', 0, 23, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 31, 31, 1675, 0, 1070, 868, 132, 132, 132, 132, 165, 165, 132, 82, 82, 82, 82, 82, 82, 10, 52, 2, -18, '', 0, 23, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 31, 31, 1830, 0, 1116, 884, 136, 136, 136, 136, 167, 167, 136, 92, 92, 92, 92, 92, 92, 11, 54, 2, -18, '', 0, 25, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 31, 31, 1985, 0, 1163, 899, 140, 140, 140, 140, 169, 169, 140, 102, 102, 102, 102, 102, 102, 11, 57, 2, -18, '', 0, 27, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 31, 31, 2140, 0, 1209, 915, 144, 144, 144, 144, 171, 171, 144, 112, 112, 112, 112, 112, 112, 12, 60, 2, -18, '', 0, 29, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 31, 31, 2295, 0, 1256, 930, 148, 148, 148, 148, 173, 173, 148, 122, 122, 122, 122, 122, 122, 12, 63, 2, -18, '', 0, 31, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 31, 31, 2760, 0, 1318, 945, 152, 152, 152, 152, 185, 185, 152, 118, 118, 118, 118, 118, 118, 12, 59, 2, -18, '', 0, 33, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 31, 31, 2915, 0, 1364, 961, 156, 156, 156, 156, 187, 187, 156, 128, 128, 128, 128, 128, 128, 12, 61, 2, -18, '', 0, 35, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 31, 31, 3070, 0, 1411, 976, 160, 160, 160, 160, 189, 189, 160, 138, 138, 138, 138, 138, 138, 13, 64, 2, -18, '', 0, 37, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 31, 31, 3225, 0, 1457, 992, 164, 164, 164, 164, 191, 191, 164, 148, 148, 148, 148, 148, 148, 13, 67, 2, -18, '', 0, 39, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 31, 31, 3380, 0, 1504, 1007, 168, 168, 168, 168, 193, 193, 168, 158, 158, 158, 158, 158, 158, 14, 70, 2, -18, '', 0, 41, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 31, 31, 1383, 1637, 837, 522, 117, 117, 117, 117, 211, 211, 117, 82, 82, 82, 82, 82, 82, 9, 50, 1, -18, '', 0, 15, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 31, 31, 1476, 1730, 868, 532, 121, 121, 121, 121, 214, 214, 121, 92, 92, 92, 92, 92, 92, 9, 52, 1, -18, '', 0, 16, 12, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 31, 31, 1569, 1823, 899, 543, 125, 125, 125, 125, 217, 217, 125, 102, 102, 102, 102, 102, 102, 9, 53, 1, -18, '', 0, 17, 13, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 31, 31, 1662, 1916, 930, 553, 129, 129, 129, 129, 220, 220, 129, 112, 112, 112, 112, 112, 112, 9, 55, 1, -18, '', 0, 18, 14, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 31, 31, 1755, 2009, 961, 563, 133, 133, 133, 133, 223, 223, 133, 122, 122, 122, 122, 122, 122, 10, 56, 1, -18, '', 0, 19, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 31, 31, 2313, 2412, 1023, 568, 137, 137, 137, 137, 236, 236, 137, 118, 118, 118, 118, 118, 118, 9, 55, 1, -18, '', 0, 21, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 31, 31, 2406, 2505, 1054, 578, 141, 141, 141, 141, 239, 239, 141, 128, 128, 128, 128, 128, 128, 10, 56, 1, -18, '', 0, 22, 15, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 31, 31, 2499, 2598, 1085, 589, 145, 145, 145, 145, 242, 242, 145, 138, 138, 138, 138, 138, 138, 10, 57, 1, -18, '', 0, 23, 16, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 31, 31, 2592, 2691, 1116, 599, 149, 149, 149, 149, 245, 245, 149, 148, 148, 148, 148, 148, 148, 10, 59, 1, -18, '', 0, 24, 17, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 31, 31, 2685, 2784, 1147, 609, 153, 153, 153, 153, 248, 248, 153, 158, 158, 158, 158, 158, 158, 10, 60, 1, -18, '', 0, 25, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 31, 31, 1436, 0, 635, 883, 132, 132, 132, 132, 165, 165, 132, 82, 82, 82, 82, 82, 82, 10, 48, 2, -18, '', 0, 21, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 31, 31, 1560, 0, 651, 899, 136, 136, 136, 136, 167, 167, 136, 92, 92, 92, 92, 92, 92, 11, 50, 2, -18, '', 0, 23, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 31, 31, 1684, 0, 666, 914, 140, 140, 140, 140, 169, 169, 140, 102, 102, 102, 102, 102, 102, 11, 53, 2, -18, '', 0, 25, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 31, 31, 1808, 0, 682, 930, 144, 144, 144, 144, 171, 171, 144, 112, 112, 112, 112, 112, 112, 12, 56, 2, -18, '', 0, 27, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 31, 31, 1932, 0, 697, 945, 148, 148, 148, 148, 173, 173, 148, 122, 122, 122, 122, 122, 122, 12, 58, 2, -18, '', 0, 29, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 31, 31, 2366, 0, 790, 976, 152, 152, 152, 152, 185, 185, 152, 118, 118, 118, 118, 118, 118, 12, 54, 2, -18, '', 0, 29, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 31, 31, 2490, 0, 806, 992, 156, 156, 156, 156, 187, 187, 156, 128, 128, 128, 128, 128, 128, 12, 57, 2, -18, '', 0, 31, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 31, 31, 2614, 0, 821, 1007, 160, 160, 160, 160, 189, 189, 160, 138, 138, 138, 138, 138, 138, 13, 59, 2, -18, '', 0, 33, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 31, 31, 2738, 0, 837, 1023, 164, 164, 164, 164, 191, 191, 164, 148, 148, 148, 148, 148, 148, 13, 62, 2, -18, '', 0, 35, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 31, 31, 2862, 0, 852, 1038, 168, 168, 168, 168, 193, 193, 168, 158, 158, 158, 158, 158, 158, 14, 65, 2, -18, '', 0, 37, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 31, 31, 1178, 1637, 413, 255, 115, 115, 115, 115, 211, 211, 115, 82, 82, 82, 82, 82, 82, 9, 50, 1, -18, '', 0, 14, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 31, 31, 1240, 1730, 423, 263, 119, 119, 119, 119, 214, 214, 119, 92, 92, 92, 92, 92, 92, 9, 52, 1, -18, '', 0, 15, 12, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 31, 31, 1302, 1823, 434, 271, 123, 123, 123, 123, 217, 217, 123, 102, 102, 102, 102, 102, 102, 9, 53, 1, -18, '', 0, 16, 13, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 31, 31, 1364, 1916, 444, 279, 127, 127, 127, 127, 220, 220, 127, 112, 112, 112, 112, 112, 112, 9, 55, 1, -18, '', 0, 17, 14, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 31, 31, 1426, 2009, 454, 286, 131, 131, 131, 131, 223, 223, 131, 122, 122, 122, 122, 122, 122, 10, 56, 1, -18, '', 0, 18, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 31, 31, 1953, 2412, 506, 286, 135, 135, 135, 135, 236, 236, 135, 118, 118, 118, 118, 118, 118, 9, 55, 1, -18, '', 0, 19, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 31, 31, 2015, 2505, 516, 294, 139, 139, 139, 139, 239, 239, 139, 128, 128, 128, 128, 128, 128, 10, 56, 1, -18, '', 0, 20, 15, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 31, 31, 2077, 2598, 527, 302, 143, 143, 143, 143, 242, 242, 143, 138, 138, 138, 138, 138, 138, 10, 57, 1, -18, '', 0, 21, 16, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 31, 31, 2139, 2691, 537, 310, 147, 147, 147, 147, 245, 245, 147, 148, 148, 148, 148, 148, 148, 10, 59, 1, -18, '', 0, 22, 17, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 31, 31, 2201, 2784, 547, 317, 151, 151, 151, 151, 248, 248, 151, 158, 158, 158, 158, 158, 158, 10, 60, 1, -18, '', 0, 23, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 32, 32, 1758, 0, 1104, 896, 136, 136, 136, 136, 168, 168, 136, 85, 85, 85, 85, 85, 85, 11, 53, 2, -18, '', 0, 23, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 32, 32, 1918, 0, 1152, 912, 140, 140, 140, 140, 170, 170, 140, 96, 96, 96, 96, 96, 96, 11, 56, 2, -18, '', 0, 25, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 32, 32, 2078, 0, 1200, 928, 144, 144, 144, 144, 172, 172, 144, 107, 107, 107, 107, 107, 107, 12, 59, 2, -18, '', 0, 27, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 32, 32, 2238, 0, 1248, 944, 148, 148, 148, 148, 174, 174, 148, 118, 118, 118, 118, 118, 118, 12, 61, 2, -18, '', 0, 29, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 32, 32, 2398, 0, 1296, 960, 152, 152, 152, 152, 176, 176, 152, 129, 129, 129, 129, 129, 129, 13, 64, 2, -18, '', 0, 31, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 32, 32, 2878, 0, 1360, 976, 157, 157, 157, 157, 188, 188, 157, 123, 123, 123, 123, 123, 123, 12, 60, 2, -18, '', 0, 33, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 32, 32, 3038, 0, 1408, 992, 161, 161, 161, 161, 190, 190, 161, 134, 134, 134, 134, 134, 134, 12, 63, 2, -18, '', 0, 35, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 32, 32, 3198, 0, 1456, 1008, 165, 165, 165, 165, 192, 192, 165, 145, 145, 145, 145, 145, 145, 13, 66, 2, -18, '', 0, 37, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 32, 32, 3358, 0, 1504, 1024, 169, 169, 169, 169, 194, 194, 169, 156, 156, 156, 156, 156, 156, 13, 68, 2, -18, '', 0, 39, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 32, 32, 3518, 0, 1552, 1040, 173, 173, 173, 173, 196, 196, 173, 167, 167, 167, 167, 167, 167, 14, 71, 2, -18, '', 0, 41, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 32, 32, 1451, 1715, 864, 538, 121, 121, 121, 121, 215, 215, 121, 85, 85, 85, 85, 85, 85, 9, 52, 1, -18, '', 0, 16, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 32, 32, 1547, 1811, 896, 549, 125, 125, 125, 125, 218, 218, 125, 96, 96, 96, 96, 96, 96, 9, 53, 1, -18, '', 0, 17, 12, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 32, 32, 1643, 1907, 928, 560, 129, 129, 129, 129, 221, 221, 129, 107, 107, 107, 107, 107, 107, 9, 55, 1, -18, '', 0, 18, 13, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 32, 32, 1739, 2003, 960, 570, 133, 133, 133, 133, 224, 224, 133, 118, 118, 118, 118, 118, 118, 10, 56, 1, -18, '', 0, 19, 14, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 32, 32, 1835, 2099, 992, 581, 137, 137, 137, 137, 227, 227, 137, 129, 129, 129, 129, 129, 129, 10, 57, 1, -18, '', 0, 20, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 32, 32, 2411, 2515, 1056, 586, 142, 142, 142, 142, 240, 240, 142, 123, 123, 123, 123, 123, 123, 10, 56, 1, -18, '', 0, 22, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 32, 32, 2507, 2611, 1088, 597, 146, 146, 146, 146, 243, 243, 146, 134, 134, 134, 134, 134, 134, 10, 57, 1, -18, '', 0, 23, 15, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 32, 32, 2603, 2707, 1120, 608, 150, 150, 150, 150, 246, 246, 150, 145, 145, 145, 145, 145, 145, 10, 59, 1, -18, '', 0, 24, 16, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 32, 32, 2699, 2803, 1152, 618, 154, 154, 154, 154, 249, 249, 154, 156, 156, 156, 156, 156, 156, 10, 60, 1, -18, '', 0, 25, 17, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 32, 32, 2795, 2899, 1184, 629, 158, 158, 158, 158, 252, 252, 158, 167, 167, 167, 167, 167, 167, 11, 62, 1, -18, '', 0, 26, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 32, 32, 1507, 0, 656, 912, 136, 136, 136, 136, 168, 168, 136, 85, 85, 85, 85, 85, 85, 11, 49, 2, -18, '', 0, 21, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 32, 32, 1635, 0, 672, 928, 140, 140, 140, 140, 170, 170, 140, 96, 96, 96, 96, 96, 96, 11, 52, 2, -18, '', 0, 23, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 32, 32, 1763, 0, 688, 944, 144, 144, 144, 144, 172, 172, 144, 107, 107, 107, 107, 107, 107, 12, 54, 2, -18, '', 0, 25, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 32, 32, 1891, 0, 704, 960, 148, 148, 148, 148, 174, 174, 148, 118, 118, 118, 118, 118, 118, 12, 57, 2, -18, '', 0, 27, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 32, 32, 2019, 0, 720, 976, 152, 152, 152, 152, 176, 176, 152, 129, 129, 129, 129, 129, 129, 13, 59, 2, -18, '', 0, 29, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 32, 32, 2467, 0, 816, 1008, 157, 157, 157, 157, 188, 188, 157, 123, 123, 123, 123, 123, 123, 12, 56, 2, -18, '', 0, 29, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 32, 32, 2595, 0, 832, 1024, 161, 161, 161, 161, 190, 190, 161, 134, 134, 134, 134, 134, 134, 12, 58, 2, -18, '', 0, 31, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 32, 32, 2723, 0, 848, 1040, 165, 165, 165, 165, 192, 192, 165, 145, 145, 145, 145, 145, 145, 13, 61, 2, -18, '', 0, 33, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 32, 32, 2851, 0, 864, 1056, 169, 169, 169, 169, 194, 194, 169, 156, 156, 156, 156, 156, 156, 13, 63, 2, -18, '', 0, 35, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 32, 32, 2979, 0, 880, 1072, 173, 173, 173, 173, 196, 196, 173, 167, 167, 167, 167, 167, 167, 14, 66, 2, -18, '', 0, 37, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 32, 32, 1238, 1715, 426, 264, 119, 119, 119, 119, 215, 215, 119, 85, 85, 85, 85, 85, 85, 9, 52, 1, -18, '', 0, 15, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 32, 32, 1302, 1811, 437, 272, 123, 123, 123, 123, 218, 218, 123, 96, 96, 96, 96, 96, 96, 9, 53, 1, -18, '', 0, 16, 12, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 32, 32, 1366, 1907, 448, 280, 127, 127, 127, 127, 221, 221, 127, 107, 107, 107, 107, 107, 107, 9, 55, 1, -18, '', 0, 17, 13, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 32, 32, 1430, 2003, 458, 288, 131, 131, 131, 131, 224, 224, 131, 118, 118, 118, 118, 118, 118, 10, 56, 1, -18, '', 0, 18, 14, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 32, 32, 1494, 2099, 469, 296, 135, 135, 135, 135, 227, 227, 135, 129, 129, 129, 129, 129, 129, 10, 57, 1, -18, '', 0, 19, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 32, 32, 2038, 2515, 522, 296, 140, 140, 140, 140, 240, 240, 140, 123, 123, 123, 123, 123, 123, 10, 56, 1, -18, '', 0, 20, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 32, 32, 2102, 2611, 533, 304, 144, 144, 144, 144, 243, 243, 144, 134, 134, 134, 134, 134, 134, 10, 57, 1, -18, '', 0, 21, 15, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 32, 32, 2166, 2707, 544, 312, 148, 148, 148, 148, 246, 246, 148, 145, 145, 145, 145, 145, 145, 10, 59, 1, -18, '', 0, 22, 16, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 32, 32, 2230, 2803, 554, 320, 152, 152, 152, 152, 249, 249, 152, 156, 156, 156, 156, 156, 156, 10, 60, 1, -18, '', 0, 23, 17, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 32, 32, 2294, 2899, 565, 328, 156, 156, 156, 156, 252, 252, 156, 167, 167, 167, 167, 167, 167, 11, 62, 1, -18, '', 0, 24, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 33, 33, 1843, 0, 1139, 924, 139, 139, 139, 139, 171, 171, 139, 87, 87, 87, 87, 87, 87, 11, 54, 2, -18, '', 0, 24, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 33, 33, 2008, 0, 1188, 941, 143, 143, 143, 143, 173, 173, 143, 98, 98, 98, 98, 98, 98, 11, 57, 2, -18, '', 0, 26, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 33, 33, 2173, 0, 1238, 957, 147, 147, 147, 147, 175, 175, 147, 109, 109, 109, 109, 109, 109, 12, 60, 2, -18, '', 0, 28, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 33, 33, 2338, 0, 1287, 974, 151, 151, 151, 151, 177, 177, 151, 120, 120, 120, 120, 120, 120, 12, 63, 2, -18, '', 0, 30, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 33, 33, 2503, 0, 1337, 990, 155, 155, 155, 155, 179, 179, 155, 131, 131, 131, 131, 131, 131, 13, 66, 2, -18, '', 0, 32, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 33, 33, 2998, 0, 1403, 1006, 160, 160, 160, 160, 191, 191, 160, 125, 125, 125, 125, 125, 125, 12, 61, 2, -18, '', 0, 34, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 33, 33, 3163, 0, 1452, 1023, 164, 164, 164, 164, 193, 193, 164, 136, 136, 136, 136, 136, 136, 13, 64, 2, -18, '', 0, 36, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 33, 33, 3328, 0, 1502, 1039, 168, 168, 168, 168, 195, 195, 168, 147, 147, 147, 147, 147, 147, 13, 67, 2, -18, '', 0, 38, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 33, 33, 3493, 0, 1551, 1056, 172, 172, 172, 172, 197, 197, 172, 158, 158, 158, 158, 158, 158, 14, 70, 2, -18, '', 0, 40, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 33, 33, 3658, 0, 1601, 1072, 176, 176, 176, 176, 199, 199, 176, 169, 169, 169, 169, 169, 169, 14, 73, 2, -18, '', 0, 42, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 33, 33, 1521, 1795, 891, 556, 124, 124, 124, 124, 218, 218, 124, 87, 87, 87, 87, 87, 87, 9, 53, 1, -18, '', 0, 16, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 33, 33, 1620, 1894, 924, 567, 128, 128, 128, 128, 221, 221, 128, 98, 98, 98, 98, 98, 98, 9, 55, 1, -18, '', 0, 17, 12, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 33, 33, 1719, 1993, 957, 578, 132, 132, 132, 132, 224, 224, 132, 109, 109, 109, 109, 109, 109, 10, 56, 1, -18, '', 0, 18, 13, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 33, 33, 1818, 2092, 990, 589, 136, 136, 136, 136, 227, 227, 136, 120, 120, 120, 120, 120, 120, 10, 57, 1, -18, '', 0, 19, 14, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 33, 33, 1917, 2191, 1023, 600, 140, 140, 140, 140, 230, 230, 140, 131, 131, 131, 131, 131, 131, 10, 59, 1, -18, '', 0, 20, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 33, 33, 2511, 2620, 1089, 605, 145, 145, 145, 145, 243, 243, 145, 125, 125, 125, 125, 125, 125, 10, 57, 1, -18, '', 0, 22, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 33, 33, 2610, 2719, 1122, 616, 149, 149, 149, 149, 246, 246, 149, 136, 136, 136, 136, 136, 136, 10, 59, 1, -18, '', 0, 23, 15, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 33, 33, 2709, 2818, 1155, 627, 153, 153, 153, 153, 249, 249, 153, 147, 147, 147, 147, 147, 147, 10, 60, 1, -18, '', 0, 24, 16, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 33, 33, 2808, 2917, 1188, 638, 157, 157, 157, 157, 252, 252, 157, 158, 158, 158, 158, 158, 158, 11, 62, 1, -18, '', 0, 25, 17, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 33, 33, 2907, 3016, 1221, 649, 161, 161, 161, 161, 255, 255, 161, 169, 169, 169, 169, 169, 169, 11, 63, 1, -18, '', 0, 26, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 33, 33, 1581, 0, 676, 940, 139, 139, 139, 139, 171, 171, 139, 87, 87, 87, 87, 87, 87, 11, 50, 2, -18, '', 0, 22, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 33, 33, 1713, 0, 693, 957, 143, 143, 143, 143, 173, 173, 143, 98, 98, 98, 98, 98, 98, 11, 53, 2, -18, '', 0, 24, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 33, 33, 1845, 0, 709, 973, 147, 147, 147, 147, 175, 175, 147, 109, 109, 109, 109, 109, 109, 12, 56, 2, -18, '', 0, 26, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 33, 33, 1977, 0, 726, 990, 151, 151, 151, 151, 177, 177, 151, 120, 120, 120, 120, 120, 120, 12, 58, 2, -18, '', 0, 28, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 33, 33, 2109, 0, 742, 1006, 155, 155, 155, 155, 179, 179, 155, 131, 131, 131, 131, 131, 131, 13, 61, 2, -18, '', 0, 30, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 33, 33, 2571, 0, 841, 1039, 160, 160, 160, 160, 191, 191, 160, 125, 125, 125, 125, 125, 125, 12, 57, 2, -18, '', 0, 30, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 33, 33, 2703, 0, 858, 1056, 164, 164, 164, 164, 193, 193, 164, 136, 136, 136, 136, 136, 136, 13, 59, 2, -18, '', 0, 32, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 33, 33, 2835, 0, 874, 1072, 168, 168, 168, 168, 195, 195, 168, 147, 147, 147, 147, 147, 147, 13, 62, 2, -18, '', 0, 34, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 33, 33, 2967, 0, 891, 1089, 172, 172, 172, 172, 197, 197, 172, 158, 158, 158, 158, 158, 158, 14, 65, 2, -18, '', 0, 36, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 33, 33, 3099, 0, 907, 1105, 176, 176, 176, 176, 199, 199, 176, 169, 169, 169, 169, 169, 169, 14, 67, 2, -18, '', 0, 38, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 33, 33, 1300, 1795, 440, 272, 122, 122, 122, 122, 218, 218, 122, 87, 87, 87, 87, 87, 87, 9, 53, 1, -18, '', 0, 15, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 33, 33, 1366, 1894, 451, 280, 126, 126, 126, 126, 221, 221, 126, 98, 98, 98, 98, 98, 98, 9, 55, 1, -18, '', 0, 16, 12, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 33, 33, 1432, 1993, 462, 288, 130, 130, 130, 130, 224, 224, 130, 109, 109, 109, 109, 109, 109, 10, 56, 1, -18, '', 0, 17, 13, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 33, 33, 1498, 2092, 473, 297, 134, 134, 134, 134, 227, 227, 134, 120, 120, 120, 120, 120, 120, 10, 57, 1, -18, '', 0, 18, 14, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 33, 33, 1564, 2191, 484, 305, 138, 138, 138, 138, 230, 230, 138, 131, 131, 131, 131, 131, 131, 10, 59, 1, -18, '', 0, 19, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 33, 33, 2125, 2620, 539, 305, 143, 143, 143, 143, 243, 243, 143, 125, 125, 125, 125, 125, 125, 10, 57, 1, -18, '', 0, 20, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 33, 33, 2191, 2719, 550, 313, 147, 147, 147, 147, 246, 246, 147, 136, 136, 136, 136, 136, 136, 10, 59, 1, -18, '', 0, 21, 15, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 33, 33, 2257, 2818, 561, 321, 151, 151, 151, 151, 249, 249, 151, 147, 147, 147, 147, 147, 147, 10, 60, 1, -18, '', 0, 22, 16, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 33, 33, 2323, 2917, 572, 330, 155, 155, 155, 155, 252, 252, 155, 158, 158, 158, 158, 158, 158, 11, 62, 1, -18, '', 0, 23, 17, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 33, 33, 2389, 3016, 583, 338, 159, 159, 159, 159, 255, 255, 159, 169, 169, 169, 169, 169, 169, 11, 63, 1, -18, '', 0, 24, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 34, 34, 1931, 0, 1173, 952, 143, 143, 143, 143, 174, 174, 143, 89, 89, 89, 89, 89, 89, 11, 56, 2, -18, '', 0, 24, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 34, 34, 2101, 0, 1224, 969, 147, 147, 147, 147, 176, 176, 147, 100, 100, 100, 100, 100, 100, 12, 59, 2, -18, '', 0, 26, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 34, 34, 2271, 0, 1275, 986, 151, 151, 151, 151, 178, 178, 151, 111, 111, 111, 111, 111, 111, 12, 61, 2, -18, '', 0, 28, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 34, 34, 2441, 0, 1326, 1003, 155, 155, 155, 155, 180, 180, 155, 122, 122, 122, 122, 122, 122, 13, 64, 2, -18, '', 0, 30, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 34, 34, 2611, 0, 1377, 1020, 159, 159, 159, 159, 182, 182, 159, 133, 133, 133, 133, 133, 133, 13, 67, 2, -18, '', 0, 32, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 34, 34, 3121, 0, 1445, 1037, 165, 165, 165, 165, 194, 194, 165, 127, 127, 127, 127, 127, 127, 12, 63, 2, -18, '', 0, 34, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 34, 34, 3291, 0, 1496, 1054, 169, 169, 169, 169, 196, 196, 169, 138, 138, 138, 138, 138, 138, 13, 66, 2, -18, '', 0, 36, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 34, 34, 3461, 0, 1547, 1071, 173, 173, 173, 173, 198, 198, 173, 149, 149, 149, 149, 149, 149, 13, 68, 2, -18, '', 0, 38, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 34, 34, 3631, 0, 1598, 1088, 177, 177, 177, 177, 200, 200, 177, 160, 160, 160, 160, 160, 160, 14, 71, 2, -18, '', 0, 40, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 34, 34, 3801, 0, 1649, 1105, 181, 181, 181, 181, 202, 202, 181, 171, 171, 171, 171, 171, 171, 14, 74, 2, -18, '', 0, 42, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 34, 34, 1594, 1877, 918, 572, 128, 128, 128, 128, 222, 222, 128, 89, 89, 89, 89, 89, 89, 9, 55, 1, -18, '', 0, 16, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 34, 34, 1696, 1979, 952, 583, 132, 132, 132, 132, 225, 225, 132, 100, 100, 100, 100, 100, 100, 10, 56, 1, -18, '', 0, 17, 12, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 34, 34, 1798, 2081, 986, 595, 136, 136, 136, 136, 228, 228, 136, 111, 111, 111, 111, 111, 111, 10, 57, 1, -18, '', 0, 18, 13, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 34, 34, 1900, 2183, 1020, 606, 140, 140, 140, 140, 231, 231, 140, 122, 122, 122, 122, 122, 122, 10, 59, 1, -18, '', 0, 19, 14, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 34, 34, 2002, 2285, 1054, 617, 144, 144, 144, 144, 234, 234, 144, 133, 133, 133, 133, 133, 133, 10, 60, 1, -18, '', 0, 20, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 34, 34, 2614, 2727, 1122, 623, 150, 150, 150, 150, 247, 247, 150, 127, 127, 127, 127, 127, 127, 10, 59, 1, -18, '', 0, 22, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 34, 34, 2716, 2829, 1156, 634, 154, 154, 154, 154, 250, 250, 154, 138, 138, 138, 138, 138, 138, 10, 60, 1, -18, '', 0, 23, 15, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 34, 34, 2818, 2931, 1190, 646, 158, 158, 158, 158, 253, 253, 158, 149, 149, 149, 149, 149, 149, 11, 62, 1, -18, '', 0, 24, 16, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 34, 34, 2920, 3033, 1224, 657, 162, 162, 162, 162, 256, 256, 162, 160, 160, 160, 160, 160, 160, 11, 63, 1, -18, '', 0, 25, 17, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 34, 34, 3022, 3135, 1258, 668, 166, 166, 166, 166, 259, 259, 166, 171, 171, 171, 171, 171, 171, 11, 64, 1, -18, '', 0, 26, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 34, 34, 1657, 0, 697, 969, 143, 143, 143, 143, 174, 174, 143, 89, 89, 89, 89, 89, 89, 11, 52, 2, -18, '', 0, 22, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 34, 34, 1793, 0, 714, 986, 147, 147, 147, 147, 176, 176, 147, 100, 100, 100, 100, 100, 100, 12, 54, 2, -18, '', 0, 24, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 34, 34, 1929, 0, 731, 1003, 151, 151, 151, 151, 178, 178, 151, 111, 111, 111, 111, 111, 111, 12, 57, 2, -18, '', 0, 26, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 34, 34, 2065, 0, 748, 1020, 155, 155, 155, 155, 180, 180, 155, 122, 122, 122, 122, 122, 122, 13, 59, 2, -18, '', 0, 28, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 34, 34, 2201, 0, 765, 1037, 159, 159, 159, 159, 182, 182, 159, 133, 133, 133, 133, 133, 133, 13, 62, 2, -18, '', 0, 30, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 34, 34, 2677, 0, 867, 1071, 165, 165, 165, 165, 194, 194, 165, 127, 127, 127, 127, 127, 127, 12, 58, 2, -18, '', 0, 30, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 34, 34, 2813, 0, 884, 1088, 169, 169, 169, 169, 196, 196, 169, 138, 138, 138, 138, 138, 138, 13, 61, 2, -18, '', 0, 32, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 34, 34, 2949, 0, 901, 1105, 173, 173, 173, 173, 198, 198, 173, 149, 149, 149, 149, 149, 149, 13, 63, 2, -18, '', 0, 34, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 34, 34, 3085, 0, 918, 1122, 177, 177, 177, 177, 200, 200, 177, 160, 160, 160, 160, 160, 160, 14, 66, 2, -18, '', 0, 36, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 34, 34, 3221, 0, 935, 1139, 181, 181, 181, 181, 202, 202, 181, 171, 171, 171, 171, 171, 171, 14, 69, 2, -18, '', 0, 38, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 34, 34, 1364, 1877, 453, 280, 126, 126, 126, 126, 222, 222, 126, 89, 89, 89, 89, 89, 89, 9, 55, 1, -18, '', 0, 15, 11, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 34, 34, 1432, 1979, 464, 289, 130, 130, 130, 130, 225, 225, 130, 100, 100, 100, 100, 100, 100, 10, 56, 1, -18, '', 0, 16, 12, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 34, 34, 1500, 2081, 476, 297, 134, 134, 134, 134, 228, 228, 134, 111, 111, 111, 111, 111, 111, 10, 57, 1, -18, '', 0, 17, 13, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 34, 34, 1568, 2183, 487, 306, 138, 138, 138, 138, 231, 231, 138, 122, 122, 122, 122, 122, 122, 10, 59, 1, -18, '', 0, 18, 14, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 34, 34, 1636, 2285, 498, 314, 142, 142, 142, 142, 234, 234, 142, 133, 133, 133, 133, 133, 133, 10, 60, 1, -18, '', 0, 19, 15, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 34, 34, 2214, 2727, 555, 314, 148, 148, 148, 148, 247, 247, 148, 127, 127, 127, 127, 127, 127, 10, 59, 1, -18, '', 0, 20, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 34, 34, 2282, 2829, 566, 323, 152, 152, 152, 152, 250, 250, 152, 138, 138, 138, 138, 138, 138, 10, 60, 1, -18, '', 0, 21, 15, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 34, 34, 2350, 2931, 578, 331, 156, 156, 156, 156, 253, 253, 156, 149, 149, 149, 149, 149, 149, 11, 62, 1, -18, '', 0, 22, 16, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 34, 34, 2418, 3033, 589, 340, 160, 160, 160, 160, 256, 256, 160, 160, 160, 160, 160, 160, 160, 11, 63, 1, -18, '', 0, 23, 17, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 34, 34, 2486, 3135, 600, 348, 164, 164, 164, 164, 259, 259, 164, 171, 171, 171, 171, 171, 171, 11, 64, 1, -18, '', 0, 24, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 35, 35, 2022, 0, 1208, 980, 147, 147, 147, 147, 177, 177, 147, 91, 91, 91, 91, 91, 91, 11, 57, 2, -18, '', 0, 24, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 35, 35, 2197, 0, 1260, 998, 151, 151, 151, 151, 179, 179, 151, 102, 102, 102, 102, 102, 102, 12, 60, 2, -18, '', 0, 26, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 35, 35, 2372, 0, 1313, 1015, 155, 155, 155, 155, 181, 181, 155, 113, 113, 113, 113, 113, 113, 12, 63, 2, -18, '', 0, 28, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 35, 35, 2547, 0, 1365, 1033, 159, 159, 159, 159, 183, 183, 159, 124, 124, 124, 124, 124, 124, 13, 66, 2, -18, '', 0, 30, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 35, 35, 2722, 0, 1418, 1050, 163, 163, 163, 163, 185, 185, 163, 135, 135, 135, 135, 135, 135, 13, 68, 2, -18, '', 0, 32, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 35, 35, 3247, 0, 1488, 1067, 169, 169, 169, 169, 197, 197, 169, 131, 131, 131, 131, 131, 131, 13, 64, 2, -18, '', 0, 34, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 35, 35, 3422, 0, 1540, 1085, 173, 173, 173, 173, 199, 199, 173, 142, 142, 142, 142, 142, 142, 13, 67, 2, -18, '', 0, 36, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 35, 35, 3597, 0, 1593, 1102, 177, 177, 177, 177, 201, 201, 177, 153, 153, 153, 153, 153, 153, 14, 70, 2, -18, '', 0, 38, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 35, 35, 3772, 0, 1645, 1120, 181, 181, 181, 181, 203, 203, 181, 164, 164, 164, 164, 164, 164, 14, 73, 2, -18, '', 0, 40, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 35, 35, 3947, 0, 1698, 1137, 185, 185, 185, 185, 205, 205, 185, 175, 175, 175, 175, 175, 175, 15, 75, 2, -18, '', 0, 42, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 35, 35, 1669, 1960, 945, 589, 131, 131, 131, 131, 225, 225, 131, 91, 91, 91, 91, 91, 91, 10, 56, 1, -18, '', 0, 16, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 35, 35, 1774, 2065, 980, 601, 135, 135, 135, 135, 228, 228, 135, 102, 102, 102, 102, 102, 102, 10, 57, 1, -18, '', 0, 17, 13, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 35, 35, 1879, 2170, 1015, 613, 139, 139, 139, 139, 231, 231, 139, 113, 113, 113, 113, 113, 113, 10, 59, 1, -18, '', 0, 18, 14, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 35, 35, 1984, 2275, 1050, 624, 143, 143, 143, 143, 234, 234, 143, 124, 124, 124, 124, 124, 124, 10, 60, 1, -18, '', 0, 19, 15, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 35, 35, 2089, 2380, 1085, 636, 147, 147, 147, 147, 237, 237, 147, 135, 135, 135, 135, 135, 135, 11, 62, 1, -18, '', 0, 20, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 35, 35, 2719, 2835, 1155, 641, 153, 153, 153, 153, 250, 250, 153, 131, 131, 131, 131, 131, 131, 10, 60, 1, -18, '', 0, 22, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 35, 35, 2824, 2940, 1190, 653, 157, 157, 157, 157, 253, 253, 157, 142, 142, 142, 142, 142, 142, 11, 62, 1, -18, '', 0, 23, 16, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 35, 35, 2929, 3045, 1225, 665, 161, 161, 161, 161, 256, 256, 161, 153, 153, 153, 153, 153, 153, 11, 63, 1, -18, '', 0, 24, 17, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 35, 35, 3034, 3150, 1260, 676, 165, 165, 165, 165, 259, 259, 165, 164, 164, 164, 164, 164, 164, 11, 64, 1, -18, '', 0, 25, 18, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 35, 35, 3139, 3255, 1295, 688, 169, 169, 169, 169, 262, 262, 169, 175, 175, 175, 175, 175, 175, 11, 66, 1, -18, '', 0, 26, 19, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 35, 35, 1735, 0, 717, 997, 147, 147, 147, 147, 177, 177, 147, 91, 91, 91, 91, 91, 91, 11, 53, 2, -18, '', 0, 22, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 35, 35, 1875, 0, 735, 1015, 151, 151, 151, 151, 179, 179, 151, 102, 102, 102, 102, 102, 102, 12, 56, 2, -18, '', 0, 24, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 35, 35, 2015, 0, 752, 1032, 155, 155, 155, 155, 181, 181, 155, 113, 113, 113, 113, 113, 113, 12, 58, 2, -18, '', 0, 26, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 35, 35, 2155, 0, 770, 1050, 159, 159, 159, 159, 183, 183, 159, 124, 124, 124, 124, 124, 124, 13, 61, 2, -18, '', 0, 28, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 35, 35, 2295, 0, 787, 1067, 163, 163, 163, 163, 185, 185, 163, 135, 135, 135, 135, 135, 135, 13, 63, 2, -18, '', 0, 30, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 35, 35, 2785, 0, 892, 1102, 169, 169, 169, 169, 197, 197, 169, 131, 131, 131, 131, 131, 131, 13, 59, 2, -18, '', 0, 30, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 35, 35, 2925, 0, 910, 1120, 173, 173, 173, 173, 199, 199, 173, 142, 142, 142, 142, 142, 142, 13, 62, 2, -18, '', 0, 32, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 35, 35, 3065, 0, 927, 1137, 177, 177, 177, 177, 201, 201, 177, 153, 153, 153, 153, 153, 153, 14, 65, 2, -18, '', 0, 34, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 35, 35, 3205, 0, 945, 1155, 181, 181, 181, 181, 203, 203, 181, 164, 164, 164, 164, 164, 164, 14, 67, 2, -18, '', 0, 36, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 35, 35, 3345, 0, 962, 1172, 185, 185, 185, 185, 205, 205, 185, 175, 175, 175, 175, 175, 175, 15, 70, 2, -18, '', 0, 38, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 35, 35, 1431, 1960, 466, 288, 129, 129, 129, 129, 225, 225, 129, 91, 91, 91, 91, 91, 91, 10, 56, 1, -18, '', 0, 15, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 35, 35, 1501, 2065, 478, 297, 133, 133, 133, 133, 228, 228, 133, 102, 102, 102, 102, 102, 102, 10, 57, 1, -18, '', 0, 16, 13, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 35, 35, 1571, 2170, 490, 306, 137, 137, 137, 137, 231, 231, 137, 113, 113, 113, 113, 113, 113, 10, 59, 1, -18, '', 0, 17, 14, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 35, 35, 1641, 2275, 501, 315, 141, 141, 141, 141, 234, 234, 141, 124, 124, 124, 124, 124, 124, 10, 60, 1, -18, '', 0, 18, 15, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 35, 35, 1711, 2380, 513, 323, 145, 145, 145, 145, 237, 237, 145, 135, 135, 135, 135, 135, 135, 11, 62, 1, -18, '', 0, 19, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 35, 35, 2306, 2835, 571, 323, 151, 151, 151, 151, 250, 250, 151, 131, 131, 131, 131, 131, 131, 10, 60, 1, -18, '', 0, 20, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 35, 35, 2376, 2940, 583, 332, 155, 155, 155, 155, 253, 253, 155, 142, 142, 142, 142, 142, 142, 11, 62, 1, -18, '', 0, 21, 16, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 35, 35, 2446, 3045, 595, 341, 159, 159, 159, 159, 256, 256, 159, 153, 153, 153, 153, 153, 153, 11, 63, 1, -18, '', 0, 22, 17, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 35, 35, 2516, 3150, 606, 350, 163, 163, 163, 163, 259, 259, 163, 164, 164, 164, 164, 164, 164, 11, 64, 1, -18, '', 0, 23, 18, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 35, 35, 2586, 3255, 618, 358, 167, 167, 167, 167, 262, 262, 167, 175, 175, 175, 175, 175, 175, 11, 66, 1, -18, '', 0, 24, 19, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 36, 36, 2116, 0, 1242, 1008, 151, 151, 151, 151, 180, 180, 151, 94, 94, 94, 94, 94, 94, 12, 59, 2, -18, '', 0, 25, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 36, 36, 2296, 0, 1296, 1026, 155, 155, 155, 155, 182, 182, 155, 106, 106, 106, 106, 106, 106, 12, 61, 2, -18, '', 0, 27, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 36, 36, 2476, 0, 1350, 1044, 159, 159, 159, 159, 184, 184, 159, 118, 118, 118, 118, 118, 118, 13, 64, 2, -18, '', 0, 29, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 36, 36, 2656, 0, 1404, 1062, 163, 163, 163, 163, 186, 186, 163, 130, 130, 130, 130, 130, 130, 13, 67, 2, -18, '', 0, 31, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 36, 36, 2836, 0, 1458, 1080, 167, 167, 167, 167, 188, 188, 167, 142, 142, 142, 142, 142, 142, 14, 70, 2, -18, '', 0, 33, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 36, 36, 3376, 0, 1530, 1098, 174, 174, 174, 174, 200, 200, 174, 134, 134, 134, 134, 134, 134, 13, 66, 2, -18, '', 0, 35, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 36, 36, 3556, 0, 1584, 1116, 178, 178, 178, 178, 202, 202, 178, 146, 146, 146, 146, 146, 146, 13, 68, 2, -18, '', 0, 37, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 36, 36, 3736, 0, 1638, 1134, 182, 182, 182, 182, 204, 204, 182, 158, 158, 158, 158, 158, 158, 14, 71, 2, -18, '', 0, 39, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 36, 36, 3916, 0, 1692, 1152, 186, 186, 186, 186, 206, 206, 186, 170, 170, 170, 170, 170, 170, 14, 74, 2, -18, '', 0, 41, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 36, 36, 4096, 0, 1746, 1170, 190, 190, 190, 190, 208, 208, 190, 182, 182, 182, 182, 182, 182, 15, 77, 2, -18, '', 0, 43, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 36, 36, 1746, 2045, 972, 606, 135, 135, 135, 135, 229, 229, 135, 94, 94, 94, 94, 94, 94, 10, 57, 1, -18, '', 0, 17, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 36, 36, 1854, 2153, 1008, 618, 139, 139, 139, 139, 232, 232, 139, 106, 106, 106, 106, 106, 106, 10, 59, 1, -18, '', 0, 18, 13, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 36, 36, 1962, 2261, 1044, 630, 143, 143, 143, 143, 235, 235, 143, 118, 118, 118, 118, 118, 118, 10, 60, 1, -18, '', 0, 19, 14, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 36, 36, 2070, 2369, 1080, 642, 147, 147, 147, 147, 238, 238, 147, 130, 130, 130, 130, 130, 130, 11, 62, 1, -18, '', 0, 20, 15, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 36, 36, 2178, 2477, 1116, 654, 151, 151, 151, 151, 241, 241, 151, 142, 142, 142, 142, 142, 142, 11, 63, 1, -18, '', 0, 21, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 36, 36, 2826, 2945, 1188, 660, 158, 158, 158, 158, 254, 254, 158, 134, 134, 134, 134, 134, 134, 11, 62, 1, -18, '', 0, 23, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 36, 36, 2934, 3053, 1224, 672, 162, 162, 162, 162, 257, 257, 162, 146, 146, 146, 146, 146, 146, 11, 63, 1, -18, '', 0, 24, 16, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 36, 36, 3042, 3161, 1260, 684, 166, 166, 166, 166, 260, 260, 166, 158, 158, 158, 158, 158, 158, 11, 64, 1, -18, '', 0, 25, 17, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 36, 36, 3150, 3269, 1296, 696, 170, 170, 170, 170, 263, 263, 170, 170, 170, 170, 170, 170, 170, 11, 66, 1, -18, '', 0, 26, 18, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 36, 36, 3258, 3377, 1332, 708, 174, 174, 174, 174, 266, 266, 174, 182, 182, 182, 182, 182, 182, 12, 67, 1, -18, '', 0, 27, 19, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 36, 36, 1817, 0, 738, 1026, 151, 151, 151, 151, 180, 180, 151, 94, 94, 94, 94, 94, 94, 12, 54, 2, -18, '', 0, 23, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 36, 36, 1961, 0, 756, 1044, 155, 155, 155, 155, 182, 182, 155, 106, 106, 106, 106, 106, 106, 12, 57, 2, -18, '', 0, 25, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 36, 36, 2105, 0, 774, 1062, 159, 159, 159, 159, 184, 184, 159, 118, 118, 118, 118, 118, 118, 13, 59, 2, -18, '', 0, 27, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 36, 36, 2249, 0, 792, 1080, 163, 163, 163, 163, 186, 186, 163, 130, 130, 130, 130, 130, 130, 13, 62, 2, -18, '', 0, 29, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 36, 36, 2393, 0, 810, 1098, 167, 167, 167, 167, 188, 188, 167, 142, 142, 142, 142, 142, 142, 14, 65, 2, -18, '', 0, 31, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 36, 36, 2897, 0, 918, 1134, 174, 174, 174, 174, 200, 200, 174, 134, 134, 134, 134, 134, 134, 13, 61, 2, -18, '', 0, 31, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 36, 36, 3041, 0, 936, 1152, 178, 178, 178, 178, 202, 202, 178, 146, 146, 146, 146, 146, 146, 13, 63, 2, -18, '', 0, 33, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 36, 36, 3185, 0, 954, 1170, 182, 182, 182, 182, 204, 204, 182, 158, 158, 158, 158, 158, 158, 14, 66, 2, -18, '', 0, 35, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 36, 36, 3329, 0, 972, 1188, 186, 186, 186, 186, 206, 206, 186, 170, 170, 170, 170, 170, 170, 14, 69, 2, -18, '', 0, 37, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 36, 36, 3473, 0, 990, 1206, 190, 190, 190, 190, 208, 208, 190, 182, 182, 182, 182, 182, 182, 15, 71, 2, -18, '', 0, 39, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 36, 36, 1500, 2045, 480, 297, 133, 133, 133, 133, 229, 229, 133, 94, 94, 94, 94, 94, 94, 10, 57, 1, -18, '', 0, 16, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 36, 36, 1572, 2153, 492, 306, 137, 137, 137, 137, 232, 232, 137, 106, 106, 106, 106, 106, 106, 10, 59, 1, -18, '', 0, 17, 13, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 36, 36, 1644, 2261, 504, 315, 141, 141, 141, 141, 235, 235, 141, 118, 118, 118, 118, 118, 118, 10, 60, 1, -18, '', 0, 18, 14, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 36, 36, 1716, 2369, 516, 324, 145, 145, 145, 145, 238, 238, 145, 130, 130, 130, 130, 130, 130, 11, 62, 1, -18, '', 0, 19, 15, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 36, 36, 1788, 2477, 528, 333, 149, 149, 149, 149, 241, 241, 149, 142, 142, 142, 142, 142, 142, 11, 63, 1, -18, '', 0, 20, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 36, 36, 2400, 2945, 588, 333, 156, 156, 156, 156, 254, 254, 156, 134, 134, 134, 134, 134, 134, 11, 62, 1, -18, '', 0, 21, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 36, 36, 2472, 3053, 600, 342, 160, 160, 160, 160, 257, 257, 160, 146, 146, 146, 146, 146, 146, 11, 63, 1, -18, '', 0, 22, 16, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 36, 36, 2544, 3161, 612, 351, 164, 164, 164, 164, 260, 260, 164, 158, 158, 158, 158, 158, 158, 11, 64, 1, -18, '', 0, 23, 17, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 36, 36, 2616, 3269, 624, 360, 168, 168, 168, 168, 263, 263, 168, 170, 170, 170, 170, 170, 170, 11, 66, 1, -18, '', 0, 24, 18, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 36, 36, 2688, 3377, 636, 369, 172, 172, 172, 172, 266, 266, 172, 182, 182, 182, 182, 182, 182, 12, 67, 1, -18, '', 0, 25, 19, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 37, 37, 2213, 0, 1277, 1036, 155, 155, 155, 155, 183, 183, 155, 96, 96, 96, 96, 96, 96, 12, 60, 2, -18, '', 0, 25, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 37, 37, 2398, 0, 1332, 1055, 159, 159, 159, 159, 185, 185, 159, 108, 108, 108, 108, 108, 108, 12, 63, 2, -18, '', 0, 27, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 37, 37, 2583, 0, 1388, 1073, 163, 163, 163, 163, 187, 187, 163, 120, 120, 120, 120, 120, 120, 13, 66, 2, -18, '', 0, 29, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 37, 37, 2768, 0, 1443, 1092, 167, 167, 167, 167, 189, 189, 167, 132, 132, 132, 132, 132, 132, 13, 68, 2, -18, '', 0, 31, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 37, 37, 2953, 0, 1499, 1110, 171, 171, 171, 171, 191, 191, 171, 144, 144, 144, 144, 144, 144, 14, 71, 2, -18, '', 0, 33, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 37, 37, 3508, 0, 1573, 1128, 178, 178, 178, 178, 203, 203, 178, 136, 136, 136, 136, 136, 136, 13, 67, 2, -18, '', 0, 35, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 37, 37, 3693, 0, 1628, 1147, 182, 182, 182, 182, 205, 205, 182, 148, 148, 148, 148, 148, 148, 14, 70, 2, -18, '', 0, 37, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 37, 37, 3878, 0, 1684, 1165, 186, 186, 186, 186, 207, 207, 186, 160, 160, 160, 160, 160, 160, 14, 73, 2, -18, '', 0, 39, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 37, 37, 4063, 0, 1739, 1184, 190, 190, 190, 190, 209, 209, 190, 172, 172, 172, 172, 172, 172, 15, 75, 2, -18, '', 0, 41, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 37, 37, 4248, 0, 1795, 1202, 194, 194, 194, 194, 211, 211, 194, 184, 184, 184, 184, 184, 184, 15, 78, 2, -18, '', 0, 43, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 37, 37, 1826, 2131, 999, 623, 139, 139, 139, 139, 232, 232, 139, 96, 96, 96, 96, 96, 96, 10, 59, 1, -18, '', 0, 17, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 37, 37, 1937, 2242, 1036, 635, 143, 143, 143, 143, 235, 235, 143, 108, 108, 108, 108, 108, 108, 10, 60, 1, -18, '', 0, 18, 13, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 37, 37, 2048, 2353, 1073, 648, 147, 147, 147, 147, 238, 238, 147, 120, 120, 120, 120, 120, 120, 11, 62, 1, -18, '', 0, 19, 14, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 37, 37, 2159, 2464, 1110, 660, 151, 151, 151, 151, 241, 241, 151, 132, 132, 132, 132, 132, 132, 11, 63, 1, -18, '', 0, 20, 15, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 37, 37, 2270, 2575, 1147, 672, 155, 155, 155, 155, 244, 244, 155, 144, 144, 144, 144, 144, 144, 11, 64, 1, -18, '', 0, 21, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 37, 37, 2936, 3056, 1221, 678, 162, 162, 162, 162, 257, 257, 162, 136, 136, 136, 136, 136, 136, 11, 63, 1, -18, '', 0, 23, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 37, 37, 3047, 3167, 1258, 690, 166, 166, 166, 166, 260, 260, 166, 148, 148, 148, 148, 148, 148, 11, 64, 1, -18, '', 0, 24, 16, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 37, 37, 3158, 3278, 1295, 703, 170, 170, 170, 170, 263, 263, 170, 160, 160, 160, 160, 160, 160, 11, 66, 1, -18, '', 0, 25, 17, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 37, 37, 3269, 3389, 1332, 715, 174, 174, 174, 174, 266, 266, 174, 172, 172, 172, 172, 172, 172, 12, 67, 1, -18, '', 0, 26, 18, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 37, 37, 3380, 3500, 1369, 727, 178, 178, 178, 178, 269, 269, 178, 184, 184, 184, 184, 184, 184, 12, 69, 1, -18, '', 0, 27, 19, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 37, 37, 1901, 0, 758, 1054, 155, 155, 155, 155, 183, 183, 155, 96, 96, 96, 96, 96, 96, 12, 56, 2, -18, '', 0, 23, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 37, 37, 2049, 0, 777, 1073, 159, 159, 159, 159, 185, 185, 159, 108, 108, 108, 108, 108, 108, 12, 58, 2, -18, '', 0, 25, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 37, 37, 2197, 0, 795, 1091, 163, 163, 163, 163, 187, 187, 163, 120, 120, 120, 120, 120, 120, 13, 61, 2, -18, '', 0, 27, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 37, 37, 2345, 0, 814, 1110, 167, 167, 167, 167, 189, 189, 167, 132, 132, 132, 132, 132, 132, 13, 63, 2, -18, '', 0, 29, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 37, 37, 2493, 0, 832, 1128, 171, 171, 171, 171, 191, 191, 171, 144, 144, 144, 144, 144, 144, 14, 66, 2, -18, '', 0, 31, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 37, 37, 3011, 0, 943, 1165, 178, 178, 178, 178, 203, 203, 178, 136, 136, 136, 136, 136, 136, 13, 62, 2, -18, '', 0, 31, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 37, 37, 3159, 0, 962, 1184, 182, 182, 182, 182, 205, 205, 182, 148, 148, 148, 148, 148, 148, 14, 65, 2, -18, '', 0, 33, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 37, 37, 3307, 0, 980, 1202, 186, 186, 186, 186, 207, 207, 186, 160, 160, 160, 160, 160, 160, 14, 67, 2, -18, '', 0, 35, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 37, 37, 3455, 0, 999, 1221, 190, 190, 190, 190, 209, 209, 190, 172, 172, 172, 172, 172, 172, 15, 70, 2, -18, '', 0, 37, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 37, 37, 3603, 0, 1017, 1239, 194, 194, 194, 194, 211, 211, 194, 184, 184, 184, 184, 184, 184, 15, 72, 2, -18, '', 0, 39, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 37, 37, 1571, 2131, 493, 305, 136, 136, 136, 136, 232, 232, 136, 96, 96, 96, 96, 96, 96, 10, 59, 1, -18, '', 0, 16, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 37, 37, 1645, 2242, 505, 314, 140, 140, 140, 140, 235, 235, 140, 108, 108, 108, 108, 108, 108, 10, 60, 1, -18, '', 0, 17, 13, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 37, 37, 1719, 2353, 518, 323, 144, 144, 144, 144, 238, 238, 144, 120, 120, 120, 120, 120, 120, 11, 62, 1, -18, '', 0, 18, 14, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 37, 37, 1793, 2464, 530, 333, 148, 148, 148, 148, 241, 241, 148, 132, 132, 132, 132, 132, 132, 11, 63, 1, -18, '', 0, 19, 15, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 37, 37, 1867, 2575, 542, 342, 152, 152, 152, 152, 244, 244, 152, 144, 144, 144, 144, 144, 144, 11, 64, 1, -18, '', 0, 20, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 37, 37, 2496, 3056, 604, 342, 159, 159, 159, 159, 257, 257, 159, 136, 136, 136, 136, 136, 136, 11, 63, 1, -18, '', 0, 21, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 37, 37, 2570, 3167, 616, 351, 163, 163, 163, 163, 260, 260, 163, 148, 148, 148, 148, 148, 148, 11, 64, 1, -18, '', 0, 22, 16, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 37, 37, 2644, 3278, 629, 360, 167, 167, 167, 167, 263, 263, 167, 160, 160, 160, 160, 160, 160, 11, 66, 1, -18, '', 0, 23, 17, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 37, 37, 2718, 3389, 641, 370, 171, 171, 171, 171, 266, 266, 171, 172, 172, 172, 172, 172, 172, 12, 67, 1, -18, '', 0, 24, 18, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 37, 37, 2792, 3500, 653, 379, 175, 175, 175, 175, 269, 269, 175, 184, 184, 184, 184, 184, 184, 12, 69, 1, -18, '', 0, 25, 19, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 38, 38, 2313, 0, 1311, 1064, 160, 160, 160, 160, 186, 186, 160, 98, 98, 98, 98, 98, 98, 12, 61, 2, -18, '', 0, 25, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 38, 38, 2503, 0, 1368, 1083, 164, 164, 164, 164, 188, 188, 164, 110, 110, 110, 110, 110, 110, 13, 64, 2, -18, '', 0, 27, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 38, 38, 2693, 0, 1425, 1102, 168, 168, 168, 168, 190, 190, 168, 122, 122, 122, 122, 122, 122, 13, 67, 2, -18, '', 0, 29, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 38, 38, 2883, 0, 1482, 1121, 172, 172, 172, 172, 192, 192, 172, 134, 134, 134, 134, 134, 134, 14, 70, 2, -18, '', 0, 31, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 38, 38, 3073, 0, 1539, 1140, 176, 176, 176, 176, 194, 194, 176, 146, 146, 146, 146, 146, 146, 14, 73, 2, -18, '', 0, 33, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 38, 38, 3643, 0, 1615, 1159, 184, 184, 184, 184, 206, 206, 184, 140, 140, 140, 140, 140, 140, 13, 68, 2, -18, '', 0, 35, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 38, 38, 3833, 0, 1672, 1178, 188, 188, 188, 188, 208, 208, 188, 152, 152, 152, 152, 152, 152, 14, 71, 2, -18, '', 0, 37, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 38, 38, 4023, 0, 1729, 1197, 192, 192, 192, 192, 210, 210, 192, 164, 164, 164, 164, 164, 164, 14, 74, 2, -18, '', 0, 39, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 38, 38, 4213, 0, 1786, 1216, 196, 196, 196, 196, 212, 212, 196, 176, 176, 176, 176, 176, 176, 15, 77, 2, -18, '', 0, 41, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 38, 38, 4403, 0, 1843, 1235, 200, 200, 200, 200, 214, 214, 200, 188, 188, 188, 188, 188, 188, 15, 80, 2, -18, '', 0, 43, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 38, 38, 1909, 2219, 1026, 639, 143, 143, 143, 143, 236, 236, 143, 98, 98, 98, 98, 98, 98, 10, 60, 1, -18, '', 0, 17, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 38, 38, 2023, 2333, 1064, 652, 147, 147, 147, 147, 239, 239, 147, 110, 110, 110, 110, 110, 110, 11, 62, 1, -18, '', 0, 18, 13, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 38, 38, 2137, 2447, 1102, 665, 151, 151, 151, 151, 242, 242, 151, 122, 122, 122, 122, 122, 122, 11, 63, 1, -18, '', 0, 19, 14, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 38, 38, 2251, 2561, 1140, 677, 155, 155, 155, 155, 245, 245, 155, 134, 134, 134, 134, 134, 134, 11, 64, 1, -18, '', 0, 20, 15, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 38, 38, 2365, 2675, 1178, 690, 159, 159, 159, 159, 248, 248, 159, 146, 146, 146, 146, 146, 146, 11, 66, 1, -18, '', 0, 21, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 38, 38, 3049, 3169, 1254, 696, 167, 167, 167, 167, 261, 261, 167, 140, 140, 140, 140, 140, 140, 11, 64, 1, -18, '', 0, 23, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 38, 38, 3163, 3283, 1292, 709, 171, 171, 171, 171, 264, 264, 171, 152, 152, 152, 152, 152, 152, 11, 66, 1, -18, '', 0, 24, 16, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 38, 38, 3277, 3397, 1330, 722, 175, 175, 175, 175, 267, 267, 175, 164, 164, 164, 164, 164, 164, 12, 67, 1, -18, '', 0, 25, 17, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 38, 38, 3391, 3511, 1368, 734, 179, 179, 179, 179, 270, 270, 179, 176, 176, 176, 176, 176, 176, 12, 69, 1, -18, '', 0, 26, 18, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 38, 38, 3505, 3625, 1406, 747, 183, 183, 183, 183, 273, 273, 183, 188, 188, 188, 188, 188, 188, 12, 70, 1, -18, '', 0, 27, 19, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 38, 38, 1987, 0, 779, 1083, 160, 160, 160, 160, 186, 186, 160, 98, 98, 98, 98, 98, 98, 12, 57, 2, -18, '', 0, 23, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 38, 38, 2139, 0, 798, 1102, 164, 164, 164, 164, 188, 188, 164, 110, 110, 110, 110, 110, 110, 13, 59, 2, -18, '', 0, 25, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 38, 38, 2291, 0, 817, 1121, 168, 168, 168, 168, 190, 190, 168, 122, 122, 122, 122, 122, 122, 13, 62, 2, -18, '', 0, 27, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 38, 38, 2443, 0, 836, 1140, 172, 172, 172, 172, 192, 192, 172, 134, 134, 134, 134, 134, 134, 14, 65, 2, -18, '', 0, 29, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 38, 38, 2595, 0, 855, 1159, 176, 176, 176, 176, 194, 194, 176, 146, 146, 146, 146, 146, 146, 14, 67, 2, -18, '', 0, 31, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 38, 38, 3127, 0, 969, 1197, 184, 184, 184, 184, 206, 206, 184, 140, 140, 140, 140, 140, 140, 13, 63, 2, -18, '', 0, 31, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 38, 38, 3279, 0, 988, 1216, 188, 188, 188, 188, 208, 208, 188, 152, 152, 152, 152, 152, 152, 14, 66, 2, -18, '', 0, 33, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 38, 38, 3431, 0, 1007, 1235, 192, 192, 192, 192, 210, 210, 192, 164, 164, 164, 164, 164, 164, 14, 69, 2, -18, '', 0, 35, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 38, 38, 3583, 0, 1026, 1254, 196, 196, 196, 196, 212, 212, 196, 176, 176, 176, 176, 176, 176, 15, 71, 2, -18, '', 0, 37, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 38, 38, 3735, 0, 1045, 1273, 200, 200, 200, 200, 214, 214, 200, 188, 188, 188, 188, 188, 188, 15, 74, 2, -18, '', 0, 39, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 38, 38, 1645, 2219, 506, 313, 140, 140, 140, 140, 236, 236, 140, 98, 98, 98, 98, 98, 98, 10, 60, 1, -18, '', 0, 16, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 38, 38, 1721, 2333, 519, 323, 144, 144, 144, 144, 239, 239, 144, 110, 110, 110, 110, 110, 110, 11, 62, 1, -18, '', 0, 17, 13, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 38, 38, 1797, 2447, 532, 332, 148, 148, 148, 148, 242, 242, 148, 122, 122, 122, 122, 122, 122, 11, 63, 1, -18, '', 0, 18, 14, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 38, 38, 1873, 2561, 544, 342, 152, 152, 152, 152, 245, 245, 152, 134, 134, 134, 134, 134, 134, 11, 64, 1, -18, '', 0, 19, 15, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 38, 38, 1949, 2675, 557, 351, 156, 156, 156, 156, 248, 248, 156, 146, 146, 146, 146, 146, 146, 11, 66, 1, -18, '', 0, 20, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 38, 38, 2595, 3169, 620, 351, 164, 164, 164, 164, 261, 261, 164, 140, 140, 140, 140, 140, 140, 11, 64, 1, -18, '', 0, 21, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 38, 38, 2671, 3283, 633, 361, 168, 168, 168, 168, 264, 264, 168, 152, 152, 152, 152, 152, 152, 11, 66, 1, -18, '', 0, 22, 16, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 38, 38, 2747, 3397, 646, 370, 172, 172, 172, 172, 267, 267, 172, 164, 164, 164, 164, 164, 164, 12, 67, 1, -18, '', 0, 23, 17, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 38, 38, 2823, 3511, 658, 380, 176, 176, 176, 176, 270, 270, 176, 176, 176, 176, 176, 176, 176, 12, 69, 1, -18, '', 0, 24, 18, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 38, 38, 2899, 3625, 671, 389, 180, 180, 180, 180, 273, 273, 180, 188, 188, 188, 188, 188, 188, 12, 70, 1, -18, '', 0, 25, 19, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 39, 39, 2417, 0, 1346, 1092, 164, 164, 164, 164, 189, 189, 164, 100, 100, 100, 100, 100, 100, 12, 63, 2, -18, '', 0, 26, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 39, 39, 2612, 0, 1404, 1112, 168, 168, 168, 168, 191, 191, 168, 112, 112, 112, 112, 112, 112, 13, 66, 2, -18, '', 0, 28, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 39, 39, 2807, 0, 1463, 1131, 172, 172, 172, 172, 193, 193, 172, 124, 124, 124, 124, 124, 124, 13, 68, 2, -18, '', 0, 30, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 39, 39, 3002, 0, 1521, 1151, 176, 176, 176, 176, 195, 195, 176, 136, 136, 136, 136, 136, 136, 14, 71, 2, -18, '', 0, 32, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 39, 39, 3197, 0, 1580, 1170, 180, 180, 180, 180, 197, 197, 180, 148, 148, 148, 148, 148, 148, 14, 74, 2, -18, '', 0, 34, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 39, 39, 3782, 0, 1658, 1189, 188, 188, 188, 188, 209, 209, 188, 142, 142, 142, 142, 142, 142, 14, 70, 2, -18, '', 0, 36, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 39, 39, 3977, 0, 1716, 1209, 192, 192, 192, 192, 211, 211, 192, 154, 154, 154, 154, 154, 154, 14, 73, 2, -18, '', 0, 38, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 39, 39, 4172, 0, 1775, 1228, 196, 196, 196, 196, 213, 213, 196, 166, 166, 166, 166, 166, 166, 15, 75, 2, -18, '', 0, 40, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 39, 39, 4367, 0, 1833, 1248, 200, 200, 200, 200, 215, 215, 200, 178, 178, 178, 178, 178, 178, 15, 78, 2, -18, '', 0, 42, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 39, 39, 4562, 0, 1892, 1267, 204, 204, 204, 204, 217, 217, 204, 190, 190, 190, 190, 190, 190, 16, 81, 2, -18, '', 0, 44, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 39, 39, 1994, 2309, 1053, 657, 147, 147, 147, 147, 239, 239, 147, 100, 100, 100, 100, 100, 100, 11, 62, 1, -18, '', 0, 17, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 39, 39, 2111, 2426, 1092, 670, 151, 151, 151, 151, 242, 242, 151, 112, 112, 112, 112, 112, 112, 11, 63, 1, -18, '', 0, 18, 13, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 39, 39, 2228, 2543, 1131, 683, 155, 155, 155, 155, 245, 245, 155, 124, 124, 124, 124, 124, 124, 11, 64, 1, -18, '', 0, 19, 14, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 39, 39, 2345, 2660, 1170, 696, 159, 159, 159, 159, 248, 248, 159, 136, 136, 136, 136, 136, 136, 11, 66, 1, -18, '', 0, 20, 15, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 39, 39, 2462, 2777, 1209, 709, 163, 163, 163, 163, 251, 251, 163, 148, 148, 148, 148, 148, 148, 12, 67, 1, -18, '', 0, 21, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 39, 39, 3164, 3284, 1287, 715, 171, 171, 171, 171, 264, 264, 171, 142, 142, 142, 142, 142, 142, 11, 66, 1, -18, '', 0, 23, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 39, 39, 3281, 3401, 1326, 728, 175, 175, 175, 175, 267, 267, 175, 154, 154, 154, 154, 154, 154, 12, 67, 1, -18, '', 0, 24, 16, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 39, 39, 3398, 3518, 1365, 741, 179, 179, 179, 179, 270, 270, 179, 166, 166, 166, 166, 166, 166, 12, 69, 1, -18, '', 0, 25, 17, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 39, 39, 3515, 3635, 1404, 754, 183, 183, 183, 183, 273, 273, 183, 178, 178, 178, 178, 178, 178, 12, 70, 1, -18, '', 0, 26, 18, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 39, 39, 3632, 3752, 1443, 767, 187, 187, 187, 187, 276, 276, 187, 190, 190, 190, 190, 190, 190, 12, 71, 1, -18, '', 0, 27, 19, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 39, 39, 2077, 0, 799, 1111, 164, 164, 164, 164, 189, 189, 164, 100, 100, 100, 100, 100, 100, 12, 58, 2, -18, '', 0, 24, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 39, 39, 2233, 0, 819, 1131, 168, 168, 168, 168, 191, 191, 168, 112, 112, 112, 112, 112, 112, 13, 61, 2, -18, '', 0, 26, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 39, 39, 2389, 0, 838, 1150, 172, 172, 172, 172, 193, 193, 172, 124, 124, 124, 124, 124, 124, 13, 63, 2, -18, '', 0, 28, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 39, 39, 2545, 0, 858, 1170, 176, 176, 176, 176, 195, 195, 176, 136, 136, 136, 136, 136, 136, 14, 66, 2, -18, '', 0, 30, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 39, 39, 2701, 0, 877, 1189, 180, 180, 180, 180, 197, 197, 180, 148, 148, 148, 148, 148, 148, 14, 69, 2, -18, '', 0, 32, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 39, 39, 3247, 0, 994, 1228, 188, 188, 188, 188, 209, 209, 188, 142, 142, 142, 142, 142, 142, 14, 65, 2, -18, '', 0, 32, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 39, 39, 3403, 0, 1014, 1248, 192, 192, 192, 192, 211, 211, 192, 154, 154, 154, 154, 154, 154, 14, 67, 2, -18, '', 0, 34, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 39, 39, 3559, 0, 1033, 1267, 196, 196, 196, 196, 213, 213, 196, 166, 166, 166, 166, 166, 166, 15, 70, 2, -18, '', 0, 36, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 39, 39, 3715, 0, 1053, 1287, 200, 200, 200, 200, 215, 215, 200, 178, 178, 178, 178, 178, 178, 15, 72, 2, -18, '', 0, 38, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 39, 39, 3871, 0, 1072, 1306, 204, 204, 204, 204, 217, 217, 204, 190, 190, 190, 190, 190, 190, 16, 75, 2, -18, '', 0, 40, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 39, 39, 1722, 2309, 520, 321, 144, 144, 144, 144, 239, 239, 144, 100, 100, 100, 100, 100, 100, 11, 62, 1, -18, '', 0, 16, 12, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 39, 39, 1800, 2426, 533, 331, 148, 148, 148, 148, 242, 242, 148, 112, 112, 112, 112, 112, 112, 11, 63, 1, -18, '', 0, 17, 13, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 39, 39, 1878, 2543, 546, 341, 152, 152, 152, 152, 245, 245, 152, 124, 124, 124, 124, 124, 124, 11, 64, 1, -18, '', 0, 18, 14, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 39, 39, 1956, 2660, 559, 351, 156, 156, 156, 156, 248, 248, 156, 136, 136, 136, 136, 136, 136, 11, 66, 1, -18, '', 0, 19, 15, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 39, 39, 2034, 2777, 572, 360, 160, 160, 160, 160, 251, 251, 160, 148, 148, 148, 148, 148, 148, 12, 67, 1, -18, '', 0, 20, 16, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 39, 39, 2697, 3284, 637, 360, 168, 168, 168, 168, 264, 264, 168, 142, 142, 142, 142, 142, 142, 11, 66, 1, -18, '', 0, 21, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 39, 39, 2775, 3401, 650, 370, 172, 172, 172, 172, 267, 267, 172, 154, 154, 154, 154, 154, 154, 12, 67, 1, -18, '', 0, 22, 16, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 39, 39, 2853, 3518, 663, 380, 176, 176, 176, 176, 270, 270, 176, 166, 166, 166, 166, 166, 166, 12, 69, 1, -18, '', 0, 23, 17, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 39, 39, 2931, 3635, 676, 390, 180, 180, 180, 180, 273, 273, 180, 178, 178, 178, 178, 178, 178, 12, 70, 1, -18, '', 0, 24, 18, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 39, 39, 3009, 3752, 689, 399, 184, 184, 184, 184, 276, 276, 184, 190, 190, 190, 190, 190, 190, 12, 71, 1, -18, '', 0, 25, 19, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 40, 40, 2524, 0, 1380, 1120, 170, 170, 170, 170, 192, 192, 170, 103, 103, 103, 103, 103, 103, 13, 64, 2, -18, '', 0, 26, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 40, 40, 2724, 0, 1440, 1140, 175, 175, 175, 175, 194, 194, 175, 116, 116, 116, 116, 116, 116, 13, 67, 2, -18, '', 0, 28, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 40, 40, 2924, 0, 1500, 1160, 180, 180, 180, 180, 196, 196, 180, 129, 129, 129, 129, 129, 129, 14, 70, 2, -18, '', 0, 30, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 40, 40, 3124, 0, 1560, 1180, 185, 185, 185, 185, 198, 198, 185, 142, 142, 142, 142, 142, 142, 14, 73, 2, -18, '', 0, 32, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 40, 40, 3324, 0, 1620, 1200, 190, 190, 190, 190, 200, 200, 190, 155, 155, 155, 155, 155, 155, 15, 75, 2, -18, '', 0, 34, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 40, 40, 3924, 0, 1700, 1220, 195, 195, 195, 195, 212, 212, 195, 145, 145, 145, 145, 145, 145, 14, 71, 2, -18, '', 0, 36, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 40, 40, 4124, 0, 1760, 1240, 200, 200, 200, 200, 214, 214, 200, 158, 158, 158, 158, 158, 158, 14, 74, 2, -18, '', 0, 38, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 40, 40, 4324, 0, 1820, 1260, 205, 205, 205, 205, 216, 216, 205, 171, 171, 171, 171, 171, 171, 15, 77, 2, -18, '', 0, 40, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 40, 40, 4524, 0, 1880, 1280, 210, 210, 210, 210, 218, 218, 210, 184, 184, 184, 184, 184, 184, 15, 80, 2, -18, '', 0, 42, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 40, 40, 4724, 0, 1940, 1300, 215, 215, 215, 215, 220, 220, 215, 197, 197, 197, 197, 197, 197, 16, 82, 2, -18, '', 0, 44, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 40, 40, 2082, 2400, 1080, 673, 152, 152, 152, 152, 243, 243, 152, 103, 103, 103, 103, 103, 103, 11, 63, 1, -18, '', 0, 18, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 40, 40, 2202, 2520, 1120, 686, 157, 157, 157, 157, 246, 246, 157, 116, 116, 116, 116, 116, 116, 11, 64, 1, -18, '', 0, 19, 14, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 40, 40, 2322, 2640, 1160, 700, 162, 162, 162, 162, 249, 249, 162, 129, 129, 129, 129, 129, 129, 11, 66, 1, -18, '', 0, 20, 15, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 40, 40, 2442, 2760, 1200, 713, 167, 167, 167, 167, 252, 252, 167, 142, 142, 142, 142, 142, 142, 12, 67, 1, -18, '', 0, 21, 16, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 40, 40, 2562, 2880, 1240, 726, 172, 172, 172, 172, 255, 255, 172, 155, 155, 155, 155, 155, 155, 12, 69, 1, -18, '', 0, 22, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 40, 40, 3282, 3400, 1320, 733, 177, 177, 177, 177, 268, 268, 177, 145, 145, 145, 145, 145, 145, 12, 67, 1, -18, '', 0, 24, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 40, 40, 3402, 3520, 1360, 746, 182, 182, 182, 182, 271, 271, 182, 158, 158, 158, 158, 158, 158, 12, 69, 1, -18, '', 0, 25, 17, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 40, 40, 3522, 3640, 1400, 760, 187, 187, 187, 187, 274, 274, 187, 171, 171, 171, 171, 171, 171, 12, 70, 1, -18, '', 0, 26, 18, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 40, 40, 3642, 3760, 1440, 773, 192, 192, 192, 192, 277, 277, 192, 184, 184, 184, 184, 184, 184, 12, 71, 1, -18, '', 0, 27, 19, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 40, 40, 3762, 3880, 1480, 786, 197, 197, 197, 197, 280, 280, 197, 197, 197, 197, 197, 197, 197, 13, 73, 1, -18, '', 0, 28, 20, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 40, 40, 2170, 0, 820, 1140, 170, 170, 170, 170, 192, 192, 170, 103, 103, 103, 103, 103, 103, 13, 59, 2, -18, '', 0, 24, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 40, 40, 2330, 0, 840, 1160, 175, 175, 175, 175, 194, 194, 175, 116, 116, 116, 116, 116, 116, 13, 62, 2, -18, '', 0, 26, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 40, 40, 2490, 0, 860, 1180, 180, 180, 180, 180, 196, 196, 180, 129, 129, 129, 129, 129, 129, 14, 65, 2, -18, '', 0, 28, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 40, 40, 2650, 0, 880, 1200, 185, 185, 185, 185, 198, 198, 185, 142, 142, 142, 142, 142, 142, 14, 67, 2, -18, '', 0, 30, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 40, 40, 2810, 0, 900, 1220, 190, 190, 190, 190, 200, 200, 190, 155, 155, 155, 155, 155, 155, 15, 70, 2, -18, '', 0, 32, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 40, 40, 3370, 0, 1020, 1260, 195, 195, 195, 195, 212, 212, 195, 145, 145, 145, 145, 145, 145, 14, 66, 2, -18, '', 0, 32, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 40, 40, 3530, 0, 1040, 1280, 200, 200, 200, 200, 214, 214, 200, 158, 158, 158, 158, 158, 158, 14, 69, 2, -18, '', 0, 34, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 40, 40, 3690, 0, 1060, 1300, 205, 205, 205, 205, 216, 216, 205, 171, 171, 171, 171, 171, 171, 15, 71, 2, -18, '', 0, 36, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 40, 40, 3850, 0, 1080, 1320, 210, 210, 210, 210, 218, 218, 210, 184, 184, 184, 184, 184, 184, 15, 74, 2, -18, '', 0, 38, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 40, 40, 4010, 0, 1100, 1340, 215, 215, 215, 215, 220, 220, 215, 197, 197, 197, 197, 197, 197, 16, 76, 2, -18, '', 0, 40, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 40, 40, 1801, 2400, 533, 330, 149, 149, 149, 149, 243, 243, 149, 103, 103, 103, 103, 103, 103, 11, 63, 1, -18, '', 0, 17, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 40, 40, 1881, 2520, 546, 340, 154, 154, 154, 154, 246, 246, 154, 116, 116, 116, 116, 116, 116, 11, 64, 1, -18, '', 0, 18, 14, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 40, 40, 1961, 2640, 560, 350, 159, 159, 159, 159, 249, 249, 159, 129, 129, 129, 129, 129, 129, 11, 66, 1, -18, '', 0, 19, 15, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 40, 40, 2041, 2760, 573, 360, 164, 164, 164, 164, 252, 252, 164, 142, 142, 142, 142, 142, 142, 12, 67, 1, -18, '', 0, 20, 16, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 40, 40, 2121, 2880, 586, 370, 169, 169, 169, 169, 255, 255, 169, 155, 155, 155, 155, 155, 155, 12, 69, 1, -18, '', 0, 21, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 40, 40, 2801, 3400, 653, 370, 174, 174, 174, 174, 268, 268, 174, 145, 145, 145, 145, 145, 145, 12, 67, 1, -18, '', 0, 22, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 40, 40, 2881, 3520, 666, 380, 179, 179, 179, 179, 271, 271, 179, 158, 158, 158, 158, 158, 158, 12, 69, 1, -18, '', 0, 23, 17, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 40, 40, 2961, 3640, 680, 390, 184, 184, 184, 184, 274, 274, 184, 171, 171, 171, 171, 171, 171, 12, 70, 1, -18, '', 0, 24, 18, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 40, 40, 3041, 3760, 693, 400, 189, 189, 189, 189, 277, 277, 189, 184, 184, 184, 184, 184, 184, 12, 71, 1, -18, '', 0, 25, 19, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 40, 40, 3121, 3880, 706, 410, 194, 194, 194, 194, 280, 280, 194, 197, 197, 197, 197, 197, 197, 13, 73, 1, -18, '', 0, 26, 20, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 41, 41, 2634, 0, 1415, 1148, 174, 174, 174, 174, 195, 195, 174, 105, 105, 105, 105, 105, 105, 13, 66, 2, -18, '', 0, 26, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 41, 41, 2839, 0, 1476, 1169, 179, 179, 179, 179, 197, 197, 179, 118, 118, 118, 118, 118, 118, 13, 68, 2, -18, '', 0, 28, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 41, 41, 3044, 0, 1538, 1189, 184, 184, 184, 184, 199, 199, 184, 131, 131, 131, 131, 131, 131, 14, 71, 2, -18, '', 0, 30, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 41, 41, 3249, 0, 1599, 1210, 189, 189, 189, 189, 201, 201, 189, 144, 144, 144, 144, 144, 144, 14, 74, 2, -18, '', 0, 32, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 41, 41, 3454, 0, 1661, 1230, 194, 194, 194, 194, 203, 203, 194, 157, 157, 157, 157, 157, 157, 15, 77, 2, -18, '', 0, 34, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 41, 41, 4069, 0, 1743, 1250, 199, 199, 199, 199, 215, 215, 199, 149, 149, 149, 149, 149, 149, 14, 73, 2, -18, '', 0, 36, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 41, 41, 4274, 0, 1804, 1271, 204, 204, 204, 204, 217, 217, 204, 162, 162, 162, 162, 162, 162, 15, 75, 2, -18, '', 0, 38, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 41, 41, 4479, 0, 1866, 1291, 209, 209, 209, 209, 219, 219, 209, 175, 175, 175, 175, 175, 175, 15, 78, 2, -18, '', 0, 40, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 41, 41, 4684, 0, 1927, 1312, 214, 214, 214, 214, 221, 221, 214, 188, 188, 188, 188, 188, 188, 16, 81, 2, -18, '', 0, 42, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 41, 41, 4889, 0, 1989, 1332, 219, 219, 219, 219, 223, 223, 219, 201, 201, 201, 201, 201, 201, 16, 84, 2, -18, '', 0, 44, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 41, 41, 2173, 2493, 1107, 690, 156, 156, 156, 156, 246, 246, 156, 105, 105, 105, 105, 105, 105, 11, 64, 1, -18, '', 0, 18, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 41, 41, 2296, 2616, 1148, 704, 161, 161, 161, 161, 249, 249, 161, 118, 118, 118, 118, 118, 118, 11, 66, 1, -18, '', 0, 19, 14, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 41, 41, 2419, 2739, 1189, 718, 166, 166, 166, 166, 252, 252, 166, 131, 131, 131, 131, 131, 131, 12, 67, 1, -18, '', 0, 20, 15, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 41, 41, 2542, 2862, 1230, 731, 171, 171, 171, 171, 255, 255, 171, 144, 144, 144, 144, 144, 144, 12, 69, 1, -18, '', 0, 21, 16, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 41, 41, 2665, 2985, 1271, 745, 176, 176, 176, 176, 258, 258, 176, 157, 157, 157, 157, 157, 157, 12, 70, 1, -18, '', 0, 22, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 41, 41, 3403, 3518, 1353, 751, 181, 181, 181, 181, 271, 271, 181, 149, 149, 149, 149, 149, 149, 12, 69, 1, -18, '', 0, 24, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 41, 41, 3526, 3641, 1394, 765, 186, 186, 186, 186, 274, 274, 186, 162, 162, 162, 162, 162, 162, 12, 70, 1, -18, '', 0, 25, 17, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 41, 41, 3649, 3764, 1435, 779, 191, 191, 191, 191, 277, 277, 191, 175, 175, 175, 175, 175, 175, 12, 71, 1, -18, '', 0, 26, 18, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 41, 41, 3772, 3887, 1476, 792, 196, 196, 196, 196, 280, 280, 196, 188, 188, 188, 188, 188, 188, 13, 73, 1, -18, '', 0, 27, 19, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 41, 41, 3895, 4010, 1517, 806, 201, 201, 201, 201, 283, 283, 201, 201, 201, 201, 201, 201, 201, 13, 74, 1, -18, '', 0, 28, 20, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 41, 41, 2265, 0, 840, 1168, 174, 174, 174, 174, 195, 195, 174, 105, 105, 105, 105, 105, 105, 13, 61, 2, -18, '', 0, 24, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 41, 41, 2429, 0, 861, 1189, 179, 179, 179, 179, 197, 197, 179, 118, 118, 118, 118, 118, 118, 13, 63, 2, -18, '', 0, 26, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 41, 41, 2593, 0, 881, 1209, 184, 184, 184, 184, 199, 199, 184, 131, 131, 131, 131, 131, 131, 14, 66, 2, -18, '', 0, 28, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 41, 41, 2757, 0, 902, 1230, 189, 189, 189, 189, 201, 201, 189, 144, 144, 144, 144, 144, 144, 14, 69, 2, -18, '', 0, 30, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 41, 41, 2921, 0, 922, 1250, 194, 194, 194, 194, 203, 203, 194, 157, 157, 157, 157, 157, 157, 15, 71, 2, -18, '', 0, 32, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 41, 41, 3495, 0, 1045, 1291, 199, 199, 199, 199, 215, 215, 199, 149, 149, 149, 149, 149, 149, 14, 67, 2, -18, '', 0, 32, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 41, 41, 3659, 0, 1066, 1312, 204, 204, 204, 204, 217, 217, 204, 162, 162, 162, 162, 162, 162, 15, 70, 2, -18, '', 0, 34, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 41, 41, 3823, 0, 1086, 1332, 209, 209, 209, 209, 219, 219, 209, 175, 175, 175, 175, 175, 175, 15, 72, 2, -18, '', 0, 36, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 41, 41, 3987, 0, 1107, 1353, 214, 214, 214, 214, 221, 221, 214, 188, 188, 188, 188, 188, 188, 16, 75, 2, -18, '', 0, 38, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 41, 41, 4151, 0, 1127, 1373, 219, 219, 219, 219, 223, 223, 219, 201, 201, 201, 201, 201, 201, 16, 78, 2, -18, '', 0, 40, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 41, 41, 1882, 2493, 546, 338, 153, 153, 153, 153, 246, 246, 153, 105, 105, 105, 105, 105, 105, 11, 64, 1, -18, '', 0, 17, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 41, 41, 1964, 2616, 560, 348, 158, 158, 158, 158, 249, 249, 158, 118, 118, 118, 118, 118, 118, 11, 66, 1, -18, '', 0, 18, 14, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 41, 41, 2046, 2739, 574, 358, 163, 163, 163, 163, 252, 252, 163, 131, 131, 131, 131, 131, 131, 12, 67, 1, -18, '', 0, 19, 15, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 41, 41, 2128, 2862, 587, 369, 168, 168, 168, 168, 255, 255, 168, 144, 144, 144, 144, 144, 144, 12, 69, 1, -18, '', 0, 20, 16, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 41, 41, 2210, 2985, 601, 379, 173, 173, 173, 173, 258, 258, 173, 157, 157, 157, 157, 157, 157, 12, 70, 1, -18, '', 0, 21, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 41, 41, 2907, 3518, 669, 379, 178, 178, 178, 178, 271, 271, 178, 149, 149, 149, 149, 149, 149, 12, 69, 1, -18, '', 0, 22, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 41, 41, 2989, 3641, 683, 389, 183, 183, 183, 183, 274, 274, 183, 162, 162, 162, 162, 162, 162, 12, 70, 1, -18, '', 0, 23, 17, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 41, 41, 3071, 3764, 697, 399, 188, 188, 188, 188, 277, 277, 188, 175, 175, 175, 175, 175, 175, 12, 71, 1, -18, '', 0, 24, 18, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 41, 41, 3153, 3887, 710, 410, 193, 193, 193, 193, 280, 280, 193, 188, 188, 188, 188, 188, 188, 13, 73, 1, -18, '', 0, 25, 19, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 41, 41, 3235, 4010, 724, 420, 198, 198, 198, 198, 283, 283, 198, 201, 201, 201, 201, 201, 201, 13, 74, 1, -18, '', 0, 26, 20, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 42, 42, 2748, 0, 1449, 1176, 179, 179, 179, 179, 198, 198, 179, 107, 107, 107, 107, 107, 107, 13, 67, 2, -18, '', 0, 27, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 42, 42, 2958, 0, 1512, 1197, 184, 184, 184, 184, 200, 200, 184, 120, 120, 120, 120, 120, 120, 14, 70, 2, -18, '', 0, 29, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 42, 42, 3168, 0, 1575, 1218, 189, 189, 189, 189, 202, 202, 189, 133, 133, 133, 133, 133, 133, 14, 73, 2, -18, '', 0, 31, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 42, 42, 3378, 0, 1638, 1239, 194, 194, 194, 194, 204, 204, 194, 146, 146, 146, 146, 146, 146, 15, 75, 2, -18, '', 0, 33, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 42, 42, 3588, 0, 1701, 1260, 199, 199, 199, 199, 206, 206, 199, 159, 159, 159, 159, 159, 159, 15, 78, 2, -18, '', 0, 35, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 42, 42, 4218, 0, 1785, 1281, 205, 205, 205, 205, 218, 218, 205, 151, 151, 151, 151, 151, 151, 14, 74, 2, -18, '', 0, 37, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 42, 42, 4428, 0, 1848, 1302, 210, 210, 210, 210, 220, 220, 210, 164, 164, 164, 164, 164, 164, 15, 77, 2, -18, '', 0, 39, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 42, 42, 4638, 0, 1911, 1323, 215, 215, 215, 215, 222, 222, 215, 177, 177, 177, 177, 177, 177, 15, 80, 2, -18, '', 0, 41, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 42, 42, 4848, 0, 1974, 1344, 220, 220, 220, 220, 224, 224, 220, 190, 190, 190, 190, 190, 190, 16, 82, 2, -18, '', 0, 43, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 42, 42, 5058, 0, 2037, 1365, 225, 225, 225, 225, 226, 226, 225, 203, 203, 203, 203, 203, 203, 16, 85, 2, -18, '', 0, 45, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 42, 42, 2267, 2587, 1134, 707, 161, 161, 161, 161, 250, 250, 161, 107, 107, 107, 107, 107, 107, 11, 66, 1, -18, '', 0, 18, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 42, 42, 2393, 2713, 1176, 721, 166, 166, 166, 166, 253, 253, 166, 120, 120, 120, 120, 120, 120, 12, 67, 1, -18, '', 0, 19, 14, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 42, 42, 2519, 2839, 1218, 735, 171, 171, 171, 171, 256, 256, 171, 133, 133, 133, 133, 133, 133, 12, 69, 1, -18, '', 0, 20, 15, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 42, 42, 2645, 2965, 1260, 749, 176, 176, 176, 176, 259, 259, 176, 146, 146, 146, 146, 146, 146, 12, 70, 1, -18, '', 0, 21, 16, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 42, 42, 2771, 3091, 1302, 763, 181, 181, 181, 181, 262, 262, 181, 159, 159, 159, 159, 159, 159, 12, 71, 1, -18, '', 0, 22, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 42, 42, 3527, 3637, 1386, 770, 187, 187, 187, 187, 275, 275, 187, 151, 151, 151, 151, 151, 151, 12, 70, 1, -18, '', 0, 24, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 42, 42, 3653, 3763, 1428, 784, 192, 192, 192, 192, 278, 278, 192, 164, 164, 164, 164, 164, 164, 12, 71, 1, -18, '', 0, 25, 17, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 42, 42, 3779, 3889, 1470, 798, 197, 197, 197, 197, 281, 281, 197, 177, 177, 177, 177, 177, 177, 13, 73, 1, -18, '', 0, 26, 18, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 42, 42, 3905, 4015, 1512, 812, 202, 202, 202, 202, 284, 284, 202, 190, 190, 190, 190, 190, 190, 13, 74, 1, -18, '', 0, 27, 19, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 42, 42, 4031, 4141, 1554, 826, 207, 207, 207, 207, 287, 287, 207, 203, 203, 203, 203, 203, 203, 13, 76, 1, -18, '', 0, 28, 20, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 42, 42, 2364, 0, 861, 1197, 179, 179, 179, 179, 198, 198, 179, 107, 107, 107, 107, 107, 107, 13, 62, 2, -18, '', 0, 25, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 42, 42, 2532, 0, 882, 1218, 184, 184, 184, 184, 200, 200, 184, 120, 120, 120, 120, 120, 120, 14, 65, 2, -18, '', 0, 27, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 42, 42, 2700, 0, 903, 1239, 189, 189, 189, 189, 202, 202, 189, 133, 133, 133, 133, 133, 133, 14, 67, 2, -18, '', 0, 29, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 42, 42, 2868, 0, 924, 1260, 194, 194, 194, 194, 204, 204, 194, 146, 146, 146, 146, 146, 146, 15, 70, 2, -18, '', 0, 31, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 42, 42, 3036, 0, 945, 1281, 199, 199, 199, 199, 206, 206, 199, 159, 159, 159, 159, 159, 159, 15, 72, 2, -18, '', 0, 33, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 42, 42, 3624, 0, 1071, 1323, 205, 205, 205, 205, 218, 218, 205, 151, 151, 151, 151, 151, 151, 14, 69, 2, -18, '', 0, 33, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 42, 42, 3792, 0, 1092, 1344, 210, 210, 210, 210, 220, 220, 210, 164, 164, 164, 164, 164, 164, 15, 71, 2, -18, '', 0, 35, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 42, 42, 3960, 0, 1113, 1365, 215, 215, 215, 215, 222, 222, 215, 177, 177, 177, 177, 177, 177, 15, 74, 2, -18, '', 0, 37, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 42, 42, 4128, 0, 1134, 1386, 220, 220, 220, 220, 224, 224, 220, 190, 190, 190, 190, 190, 190, 16, 76, 2, -18, '', 0, 39, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 42, 42, 4296, 0, 1155, 1407, 225, 225, 225, 225, 226, 226, 225, 203, 203, 203, 203, 203, 203, 16, 79, 2, -18, '', 0, 41, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 42, 42, 1967, 2587, 560, 346, 157, 157, 157, 157, 250, 250, 157, 107, 107, 107, 107, 107, 107, 11, 66, 1, -18, '', 0, 17, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 42, 42, 2051, 2713, 574, 357, 162, 162, 162, 162, 253, 253, 162, 120, 120, 120, 120, 120, 120, 12, 67, 1, -18, '', 0, 18, 14, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 42, 42, 2135, 2839, 588, 367, 167, 167, 167, 167, 256, 256, 167, 133, 133, 133, 133, 133, 133, 12, 69, 1, -18, '', 0, 19, 15, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 42, 42, 2219, 2965, 602, 378, 172, 172, 172, 172, 259, 259, 172, 146, 146, 146, 146, 146, 146, 12, 70, 1, -18, '', 0, 20, 16, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 42, 42, 2303, 3091, 616, 388, 177, 177, 177, 177, 262, 262, 177, 159, 159, 159, 159, 159, 159, 12, 71, 1, -18, '', 0, 21, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 42, 42, 3017, 3637, 686, 388, 183, 183, 183, 183, 275, 275, 183, 151, 151, 151, 151, 151, 151, 12, 70, 1, -18, '', 0, 22, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 42, 42, 3101, 3763, 700, 399, 188, 188, 188, 188, 278, 278, 188, 164, 164, 164, 164, 164, 164, 12, 71, 1, -18, '', 0, 23, 17, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 42, 42, 3185, 3889, 714, 409, 193, 193, 193, 193, 281, 281, 193, 177, 177, 177, 177, 177, 177, 13, 73, 1, -18, '', 0, 24, 18, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 42, 42, 3269, 4015, 728, 420, 198, 198, 198, 198, 284, 284, 198, 190, 190, 190, 190, 190, 190, 13, 74, 1, -18, '', 0, 25, 19, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 42, 42, 3353, 4141, 742, 430, 203, 203, 203, 203, 287, 287, 203, 203, 203, 203, 203, 203, 203, 13, 76, 1, -18, '', 0, 26, 20, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 43, 43, 2865, 0, 1484, 1204, 183, 183, 183, 183, 201, 201, 183, 109, 109, 109, 109, 109, 109, 13, 68, 2, -18, '', 0, 27, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 43, 43, 3080, 0, 1548, 1226, 188, 188, 188, 188, 203, 203, 188, 122, 122, 122, 122, 122, 122, 14, 71, 2, -18, '', 0, 29, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 43, 43, 3295, 0, 1613, 1247, 193, 193, 193, 193, 205, 205, 193, 135, 135, 135, 135, 135, 135, 14, 74, 2, -18, '', 0, 31, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 43, 43, 3510, 0, 1677, 1269, 198, 198, 198, 198, 207, 207, 198, 148, 148, 148, 148, 148, 148, 15, 77, 2, -18, '', 0, 33, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 43, 43, 3725, 0, 1742, 1290, 203, 203, 203, 203, 209, 209, 203, 161, 161, 161, 161, 161, 161, 15, 80, 2, -18, '', 0, 35, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 43, 43, 4370, 0, 1828, 1311, 209, 209, 209, 209, 221, 221, 209, 153, 153, 153, 153, 153, 153, 15, 75, 2, -18, '', 0, 37, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 43, 43, 4585, 0, 1892, 1333, 214, 214, 214, 214, 223, 223, 214, 166, 166, 166, 166, 166, 166, 15, 78, 2, -18, '', 0, 39, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 43, 43, 4800, 0, 1957, 1354, 219, 219, 219, 219, 225, 225, 219, 179, 179, 179, 179, 179, 179, 16, 81, 2, -18, '', 0, 41, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 43, 43, 5015, 0, 2021, 1376, 224, 224, 224, 224, 227, 227, 224, 192, 192, 192, 192, 192, 192, 16, 84, 2, -18, '', 0, 43, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 43, 43, 5230, 0, 2086, 1397, 229, 229, 229, 229, 229, 229, 229, 205, 205, 205, 205, 205, 205, 17, 87, 2, -18, '', 0, 45, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 43, 43, 2364, 2683, 1161, 724, 165, 165, 165, 165, 253, 253, 165, 109, 109, 109, 109, 109, 109, 12, 67, 1, -18, '', 0, 18, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 43, 43, 2493, 2812, 1204, 738, 170, 170, 170, 170, 256, 256, 170, 122, 122, 122, 122, 122, 122, 12, 69, 1, -18, '', 0, 19, 14, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 43, 43, 2622, 2941, 1247, 753, 175, 175, 175, 175, 259, 259, 175, 135, 135, 135, 135, 135, 135, 12, 70, 1, -18, '', 0, 20, 15, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 43, 43, 2751, 3070, 1290, 767, 180, 180, 180, 180, 262, 262, 180, 148, 148, 148, 148, 148, 148, 12, 71, 1, -18, '', 0, 21, 16, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 43, 43, 2880, 3199, 1333, 781, 185, 185, 185, 185, 265, 265, 185, 161, 161, 161, 161, 161, 161, 13, 73, 1, -18, '', 0, 22, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 43, 43, 3654, 3758, 1419, 788, 191, 191, 191, 191, 278, 278, 191, 153, 153, 153, 153, 153, 153, 12, 71, 1, -18, '', 0, 24, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 43, 43, 3783, 3887, 1462, 802, 196, 196, 196, 196, 281, 281, 196, 166, 166, 166, 166, 166, 166, 13, 73, 1, -18, '', 0, 25, 17, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 43, 43, 3912, 4016, 1505, 817, 201, 201, 201, 201, 284, 284, 201, 179, 179, 179, 179, 179, 179, 13, 74, 1, -18, '', 0, 26, 18, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 43, 43, 4041, 4145, 1548, 831, 206, 206, 206, 206, 287, 287, 206, 192, 192, 192, 192, 192, 192, 13, 76, 1, -18, '', 0, 27, 19, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 43, 43, 4170, 4274, 1591, 845, 211, 211, 211, 211, 290, 290, 211, 205, 205, 205, 205, 205, 205, 13, 77, 1, -18, '', 0, 28, 20, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 43, 43, 2465, 0, 881, 1225, 183, 183, 183, 183, 201, 201, 183, 109, 109, 109, 109, 109, 109, 13, 63, 2, -18, '', 0, 25, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 43, 43, 2637, 0, 903, 1247, 188, 188, 188, 188, 203, 203, 188, 122, 122, 122, 122, 122, 122, 14, 66, 2, -18, '', 0, 27, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 43, 43, 2809, 0, 924, 1268, 193, 193, 193, 193, 205, 205, 193, 135, 135, 135, 135, 135, 135, 14, 69, 2, -18, '', 0, 29, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 43, 43, 2981, 0, 946, 1290, 198, 198, 198, 198, 207, 207, 198, 148, 148, 148, 148, 148, 148, 15, 71, 2, -18, '', 0, 31, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 43, 43, 3153, 0, 967, 1311, 203, 203, 203, 203, 209, 209, 203, 161, 161, 161, 161, 161, 161, 15, 74, 2, -18, '', 0, 33, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 43, 43, 3755, 0, 1096, 1354, 209, 209, 209, 209, 221, 221, 209, 153, 153, 153, 153, 153, 153, 15, 70, 2, -18, '', 0, 33, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 43, 43, 3927, 0, 1118, 1376, 214, 214, 214, 214, 223, 223, 214, 166, 166, 166, 166, 166, 166, 15, 72, 2, -18, '', 0, 35, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 43, 43, 4099, 0, 1139, 1397, 219, 219, 219, 219, 225, 225, 219, 179, 179, 179, 179, 179, 179, 16, 75, 2, -18, '', 0, 37, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 43, 43, 4271, 0, 1161, 1419, 224, 224, 224, 224, 227, 227, 224, 192, 192, 192, 192, 192, 192, 16, 78, 2, -18, '', 0, 39, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 43, 43, 4443, 0, 1182, 1440, 229, 229, 229, 229, 229, 229, 229, 205, 205, 205, 205, 205, 205, 17, 80, 2, -18, '', 0, 41, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 43, 43, 2054, 2683, 573, 354, 161, 161, 161, 161, 253, 253, 161, 109, 109, 109, 109, 109, 109, 12, 67, 1, -18, '', 0, 17, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 43, 43, 2140, 2812, 587, 365, 166, 166, 166, 166, 256, 256, 166, 122, 122, 122, 122, 122, 122, 12, 69, 1, -18, '', 0, 18, 14, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 43, 43, 2226, 2941, 602, 376, 171, 171, 171, 171, 259, 259, 171, 135, 135, 135, 135, 135, 135, 12, 70, 1, -18, '', 0, 19, 15, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 43, 43, 2312, 3070, 616, 387, 176, 176, 176, 176, 262, 262, 176, 148, 148, 148, 148, 148, 148, 12, 71, 1, -18, '', 0, 20, 16, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 43, 43, 2398, 3199, 630, 397, 181, 181, 181, 181, 265, 265, 181, 161, 161, 161, 161, 161, 161, 13, 73, 1, -18, '', 0, 21, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 43, 43, 3129, 3758, 702, 397, 187, 187, 187, 187, 278, 278, 187, 153, 153, 153, 153, 153, 153, 12, 71, 1, -18, '', 0, 22, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 43, 43, 3215, 3887, 716, 408, 192, 192, 192, 192, 281, 281, 192, 166, 166, 166, 166, 166, 166, 13, 73, 1, -18, '', 0, 23, 17, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 43, 43, 3301, 4016, 731, 419, 197, 197, 197, 197, 284, 284, 197, 179, 179, 179, 179, 179, 179, 13, 74, 1, -18, '', 0, 24, 18, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 43, 43, 3387, 4145, 745, 430, 202, 202, 202, 202, 287, 287, 202, 192, 192, 192, 192, 192, 192, 13, 76, 1, -18, '', 0, 25, 19, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 43, 43, 3473, 4274, 759, 440, 207, 207, 207, 207, 290, 290, 207, 205, 205, 205, 205, 205, 205, 13, 77, 1, -18, '', 0, 26, 20, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 44, 44, 2986, 0, 1518, 1232, 188, 188, 188, 188, 204, 204, 188, 112, 112, 112, 112, 112, 112, 14, 70, 2, -18, '', 0, 27, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 44, 44, 3206, 0, 1584, 1254, 193, 193, 193, 193, 206, 206, 193, 126, 126, 126, 126, 126, 126, 14, 73, 2, -18, '', 0, 29, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 44, 44, 3426, 0, 1650, 1276, 198, 198, 198, 198, 208, 208, 198, 140, 140, 140, 140, 140, 140, 15, 75, 2, -18, '', 0, 31, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 44, 44, 3646, 0, 1716, 1298, 203, 203, 203, 203, 210, 210, 203, 154, 154, 154, 154, 154, 154, 15, 78, 2, -18, '', 0, 33, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 44, 44, 3866, 0, 1782, 1320, 208, 208, 208, 208, 212, 212, 208, 168, 168, 168, 168, 168, 168, 16, 81, 2, -18, '', 0, 35, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 44, 44, 4526, 0, 1870, 1342, 215, 215, 215, 215, 224, 224, 215, 158, 158, 158, 158, 158, 158, 15, 77, 2, -18, '', 0, 37, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 44, 44, 4746, 0, 1936, 1364, 220, 220, 220, 220, 226, 226, 220, 172, 172, 172, 172, 172, 172, 15, 80, 2, -18, '', 0, 39, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 44, 44, 4966, 0, 2002, 1386, 225, 225, 225, 225, 228, 228, 225, 186, 186, 186, 186, 186, 186, 16, 82, 2, -18, '', 0, 41, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 44, 44, 5186, 0, 2068, 1408, 230, 230, 230, 230, 230, 230, 230, 200, 200, 200, 200, 200, 200, 16, 85, 2, -18, '', 0, 43, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 44, 44, 5406, 0, 2134, 1430, 235, 235, 235, 235, 232, 232, 235, 214, 214, 214, 214, 214, 214, 17, 88, 2, -18, '', 0, 45, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 44, 44, 2464, 2781, 1188, 740, 170, 170, 170, 170, 257, 257, 170, 112, 112, 112, 112, 112, 112, 12, 69, 1, -18, '', 0, 19, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 44, 44, 2596, 2913, 1232, 755, 175, 175, 175, 175, 260, 260, 175, 126, 126, 126, 126, 126, 126, 12, 70, 1, -18, '', 0, 20, 14, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 44, 44, 2728, 3045, 1276, 770, 180, 180, 180, 180, 263, 263, 180, 140, 140, 140, 140, 140, 140, 12, 71, 1, -18, '', 0, 21, 15, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 44, 44, 2860, 3177, 1320, 784, 185, 185, 185, 185, 266, 266, 185, 154, 154, 154, 154, 154, 154, 13, 73, 1, -18, '', 0, 22, 16, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 44, 44, 2992, 3309, 1364, 799, 190, 190, 190, 190, 269, 269, 190, 168, 168, 168, 168, 168, 168, 13, 74, 1, -18, '', 0, 23, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 44, 44, 3784, 3881, 1452, 806, 197, 197, 197, 197, 282, 282, 197, 158, 158, 158, 158, 158, 158, 13, 73, 1, -18, '', 0, 25, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 44, 44, 3916, 4013, 1496, 821, 202, 202, 202, 202, 285, 285, 202, 172, 172, 172, 172, 172, 172, 13, 74, 1, -18, '', 0, 26, 17, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 44, 44, 4048, 4145, 1540, 836, 207, 207, 207, 207, 288, 288, 207, 186, 186, 186, 186, 186, 186, 13, 76, 1, -18, '', 0, 27, 18, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 44, 44, 4180, 4277, 1584, 850, 212, 212, 212, 212, 291, 291, 212, 200, 200, 200, 200, 200, 200, 13, 77, 1, -18, '', 0, 28, 19, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 44, 44, 4312, 4409, 1628, 865, 217, 217, 217, 217, 294, 294, 217, 214, 214, 214, 214, 214, 214, 14, 78, 1, -18, '', 0, 29, 20, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 44, 44, 2570, 0, 902, 1254, 188, 188, 188, 188, 204, 204, 188, 112, 112, 112, 112, 112, 112, 14, 65, 2, -18, '', 0, 25, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 44, 44, 2746, 0, 924, 1276, 193, 193, 193, 193, 206, 206, 193, 126, 126, 126, 126, 126, 126, 14, 67, 2, -18, '', 0, 27, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 44, 44, 2922, 0, 946, 1298, 198, 198, 198, 198, 208, 208, 198, 140, 140, 140, 140, 140, 140, 15, 70, 2, -18, '', 0, 29, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 44, 44, 3098, 0, 968, 1320, 203, 203, 203, 203, 210, 210, 203, 154, 154, 154, 154, 154, 154, 15, 72, 2, -18, '', 0, 31, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 44, 44, 3274, 0, 990, 1342, 208, 208, 208, 208, 212, 212, 208, 168, 168, 168, 168, 168, 168, 16, 75, 2, -18, '', 0, 33, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 44, 44, 3890, 0, 1122, 1386, 215, 215, 215, 215, 224, 224, 215, 158, 158, 158, 158, 158, 158, 15, 71, 2, -18, '', 0, 33, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 44, 44, 4066, 0, 1144, 1408, 220, 220, 220, 220, 226, 226, 220, 172, 172, 172, 172, 172, 172, 15, 74, 2, -18, '', 0, 35, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 44, 44, 4242, 0, 1166, 1430, 225, 225, 225, 225, 228, 228, 225, 186, 186, 186, 186, 186, 186, 16, 76, 2, -18, '', 0, 37, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 44, 44, 4418, 0, 1188, 1452, 230, 230, 230, 230, 230, 230, 230, 200, 200, 200, 200, 200, 200, 16, 79, 2, -18, '', 0, 39, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 44, 44, 4594, 0, 1210, 1474, 235, 235, 235, 235, 232, 232, 235, 214, 214, 214, 214, 214, 214, 17, 82, 2, -18, '', 0, 41, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 44, 44, 2144, 2781, 586, 363, 166, 166, 166, 166, 257, 257, 166, 112, 112, 112, 112, 112, 112, 12, 69, 1, -18, '', 0, 18, 13, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 44, 44, 2232, 2913, 601, 374, 171, 171, 171, 171, 260, 260, 171, 126, 126, 126, 126, 126, 126, 12, 70, 1, -18, '', 0, 19, 14, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 44, 44, 2320, 3045, 616, 385, 176, 176, 176, 176, 263, 263, 176, 140, 140, 140, 140, 140, 140, 12, 71, 1, -18, '', 0, 20, 15, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 44, 44, 2408, 3177, 630, 396, 181, 181, 181, 181, 266, 266, 181, 154, 154, 154, 154, 154, 154, 13, 73, 1, -18, '', 0, 21, 16, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 44, 44, 2496, 3309, 645, 407, 186, 186, 186, 186, 269, 269, 186, 168, 168, 168, 168, 168, 168, 13, 74, 1, -18, '', 0, 22, 17, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 44, 44, 3244, 3881, 718, 407, 193, 193, 193, 193, 282, 282, 193, 158, 158, 158, 158, 158, 158, 13, 73, 1, -18, '', 0, 23, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 44, 44, 3332, 4013, 733, 418, 198, 198, 198, 198, 285, 285, 198, 172, 172, 172, 172, 172, 172, 13, 74, 1, -18, '', 0, 24, 17, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 44, 44, 3420, 4145, 748, 429, 203, 203, 203, 203, 288, 288, 203, 186, 186, 186, 186, 186, 186, 13, 76, 1, -18, '', 0, 25, 18, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 44, 44, 3508, 4277, 762, 440, 208, 208, 208, 208, 291, 291, 208, 200, 200, 200, 200, 200, 200, 13, 77, 1, -18, '', 0, 26, 19, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 44, 44, 3596, 4409, 777, 451, 213, 213, 213, 213, 294, 294, 213, 214, 214, 214, 214, 214, 214, 14, 78, 1, -18, '', 0, 27, 20, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 45, 45, 3111, 0, 1553, 1260, 193, 193, 193, 193, 207, 207, 193, 114, 114, 114, 114, 114, 114, 14, 71, 2, -18, '', 0, 28, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 45, 45, 3336, 0, 1620, 1283, 198, 198, 198, 198, 209, 209, 198, 128, 128, 128, 128, 128, 128, 14, 74, 2, -18, '', 0, 30, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 45, 45, 3561, 0, 1688, 1305, 203, 203, 203, 203, 211, 211, 203, 142, 142, 142, 142, 142, 142, 15, 77, 2, -18, '', 0, 32, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 45, 45, 3786, 0, 1755, 1328, 208, 208, 208, 208, 213, 213, 208, 156, 156, 156, 156, 156, 156, 15, 80, 2, -18, '', 0, 34, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 45, 45, 4011, 0, 1823, 1350, 213, 213, 213, 213, 215, 215, 213, 170, 170, 170, 170, 170, 170, 16, 82, 2, -18, '', 0, 36, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 45, 45, 4686, 0, 1913, 1372, 220, 220, 220, 220, 227, 227, 220, 160, 160, 160, 160, 160, 160, 15, 78, 2, -18, '', 0, 38, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 45, 45, 4911, 0, 1980, 1395, 225, 225, 225, 225, 229, 229, 225, 174, 174, 174, 174, 174, 174, 16, 81, 2, -18, '', 0, 40, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 45, 45, 5136, 0, 2048, 1417, 230, 230, 230, 230, 231, 231, 230, 188, 188, 188, 188, 188, 188, 16, 84, 2, -18, '', 0, 42, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 45, 45, 5361, 0, 2115, 1440, 235, 235, 235, 235, 233, 233, 235, 202, 202, 202, 202, 202, 202, 17, 87, 2, -18, '', 0, 44, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 45, 45, 5586, 0, 2183, 1462, 240, 240, 240, 240, 235, 235, 240, 216, 216, 216, 216, 216, 216, 17, 89, 2, -18, '', 0, 46, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 45, 45, 2567, 2880, 1215, 758, 174, 174, 174, 174, 260, 260, 174, 114, 114, 114, 114, 114, 114, 12, 70, 1, -18, '', 0, 19, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 45, 45, 2702, 3015, 1260, 773, 179, 179, 179, 179, 263, 263, 179, 128, 128, 128, 128, 128, 128, 12, 71, 1, -18, '', 0, 20, 15, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 45, 45, 2837, 3150, 1305, 788, 184, 184, 184, 184, 266, 266, 184, 142, 142, 142, 142, 142, 142, 13, 73, 1, -18, '', 0, 21, 16, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 45, 45, 2972, 3285, 1350, 803, 189, 189, 189, 189, 269, 269, 189, 156, 156, 156, 156, 156, 156, 13, 74, 1, -18, '', 0, 22, 17, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 45, 45, 3107, 3420, 1395, 818, 194, 194, 194, 194, 272, 272, 194, 170, 170, 170, 170, 170, 170, 13, 76, 1, -18, '', 0, 23, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 45, 45, 3917, 4005, 1485, 825, 201, 201, 201, 201, 285, 285, 201, 160, 160, 160, 160, 160, 160, 13, 74, 1, -18, '', 0, 25, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 45, 45, 4052, 4140, 1530, 840, 206, 206, 206, 206, 288, 288, 206, 174, 174, 174, 174, 174, 174, 13, 76, 1, -18, '', 0, 26, 18, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 45, 45, 4187, 4275, 1575, 855, 211, 211, 211, 211, 291, 291, 211, 188, 188, 188, 188, 188, 188, 13, 77, 1, -18, '', 0, 27, 19, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 45, 45, 4322, 4410, 1620, 870, 216, 216, 216, 216, 294, 294, 216, 202, 202, 202, 202, 202, 202, 14, 78, 1, -18, '', 0, 28, 20, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 45, 45, 4457, 4545, 1665, 885, 221, 221, 221, 221, 297, 297, 221, 216, 216, 216, 216, 216, 216, 14, 80, 1, -18, '', 0, 29, 21, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 45, 45, 2679, 0, 922, 1282, 193, 193, 193, 193, 207, 207, 193, 114, 114, 114, 114, 114, 114, 14, 66, 2, -18, '', 0, 26, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 45, 45, 2859, 0, 945, 1305, 198, 198, 198, 198, 209, 209, 198, 128, 128, 128, 128, 128, 128, 14, 69, 2, -18, '', 0, 28, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 45, 45, 3039, 0, 967, 1327, 203, 203, 203, 203, 211, 211, 203, 142, 142, 142, 142, 142, 142, 15, 71, 2, -18, '', 0, 30, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 45, 45, 3219, 0, 990, 1350, 208, 208, 208, 208, 213, 213, 208, 156, 156, 156, 156, 156, 156, 15, 74, 2, -18, '', 0, 32, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 45, 45, 3399, 0, 1012, 1372, 213, 213, 213, 213, 215, 215, 213, 170, 170, 170, 170, 170, 170, 16, 76, 2, -18, '', 0, 34, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 45, 45, 4029, 0, 1147, 1417, 220, 220, 220, 220, 227, 227, 220, 160, 160, 160, 160, 160, 160, 15, 72, 2, -18, '', 0, 34, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 45, 45, 4209, 0, 1170, 1440, 225, 225, 225, 225, 229, 229, 225, 174, 174, 174, 174, 174, 174, 16, 75, 2, -18, '', 0, 36, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 45, 45, 4389, 0, 1192, 1462, 230, 230, 230, 230, 231, 231, 230, 188, 188, 188, 188, 188, 188, 16, 78, 2, -18, '', 0, 38, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 45, 45, 4569, 0, 1215, 1485, 235, 235, 235, 235, 233, 233, 235, 202, 202, 202, 202, 202, 202, 17, 80, 2, -18, '', 0, 40, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 45, 45, 4749, 0, 1237, 1507, 240, 240, 240, 240, 235, 235, 240, 216, 216, 216, 216, 216, 216, 17, 83, 2, -18, '', 0, 42, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 45, 45, 2237, 2880, 600, 371, 170, 170, 170, 170, 260, 260, 170, 114, 114, 114, 114, 114, 114, 12, 70, 1, -18, '', 0, 18, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 45, 45, 2327, 3015, 615, 382, 175, 175, 175, 175, 263, 263, 175, 128, 128, 128, 128, 128, 128, 12, 71, 1, -18, '', 0, 19, 15, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 45, 45, 2417, 3150, 630, 393, 180, 180, 180, 180, 266, 266, 180, 142, 142, 142, 142, 142, 142, 13, 73, 1, -18, '', 0, 20, 16, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 45, 45, 2507, 3285, 645, 405, 185, 185, 185, 185, 269, 269, 185, 156, 156, 156, 156, 156, 156, 13, 74, 1, -18, '', 0, 21, 17, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 45, 45, 2597, 3420, 660, 416, 190, 190, 190, 190, 272, 272, 190, 170, 170, 170, 170, 170, 170, 13, 76, 1, -18, '', 0, 22, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 45, 45, 3362, 4005, 735, 416, 197, 197, 197, 197, 285, 285, 197, 160, 160, 160, 160, 160, 160, 13, 74, 1, -18, '', 0, 23, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 45, 45, 3452, 4140, 750, 427, 202, 202, 202, 202, 288, 288, 202, 174, 174, 174, 174, 174, 174, 13, 76, 1, -18, '', 0, 24, 18, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 45, 45, 3542, 4275, 765, 438, 207, 207, 207, 207, 291, 291, 207, 188, 188, 188, 188, 188, 188, 13, 77, 1, -18, '', 0, 25, 19, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 45, 45, 3632, 4410, 780, 450, 212, 212, 212, 212, 294, 294, 212, 202, 202, 202, 202, 202, 202, 14, 78, 1, -18, '', 0, 26, 20, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 45, 45, 3722, 4545, 795, 461, 217, 217, 217, 217, 297, 297, 217, 216, 216, 216, 216, 216, 216, 14, 80, 1, -18, '', 0, 27, 21, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 46, 46, 3240, 0, 1587, 1288, 198, 198, 198, 198, 210, 210, 198, 116, 116, 116, 116, 116, 116, 14, 73, 2, -18, '', 0, 28, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 46, 46, 3470, 0, 1656, 1311, 203, 203, 203, 203, 212, 212, 203, 130, 130, 130, 130, 130, 130, 15, 75, 2, -18, '', 0, 30, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 46, 46, 3700, 0, 1725, 1334, 208, 208, 208, 208, 214, 214, 208, 144, 144, 144, 144, 144, 144, 15, 78, 2, -18, '', 0, 32, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 46, 46, 3930, 0, 1794, 1357, 213, 213, 213, 213, 216, 216, 213, 158, 158, 158, 158, 158, 158, 16, 81, 2, -18, '', 0, 34, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 46, 46, 4160, 0, 1863, 1380, 218, 218, 218, 218, 218, 218, 218, 172, 172, 172, 172, 172, 172, 16, 84, 2, -18, '', 0, 36, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 46, 46, 4850, 0, 1955, 1403, 226, 226, 226, 226, 230, 230, 226, 162, 162, 162, 162, 162, 162, 15, 80, 2, -18, '', 0, 38, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 46, 46, 5080, 0, 2024, 1426, 231, 231, 231, 231, 232, 232, 231, 176, 176, 176, 176, 176, 176, 16, 82, 2, -18, '', 0, 40, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 46, 46, 5310, 0, 2093, 1449, 236, 236, 236, 236, 234, 234, 236, 190, 190, 190, 190, 190, 190, 16, 85, 2, -18, '', 0, 42, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 46, 46, 5540, 0, 2162, 1472, 241, 241, 241, 241, 236, 236, 241, 204, 204, 204, 204, 204, 204, 17, 88, 2, -18, '', 0, 44, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 46, 46, 5770, 0, 2231, 1495, 246, 246, 246, 246, 238, 238, 246, 218, 218, 218, 218, 218, 218, 17, 91, 2, -18, '', 0, 46, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 46, 46, 2673, 2981, 1242, 774, 179, 179, 179, 179, 264, 264, 179, 116, 116, 116, 116, 116, 116, 12, 71, 1, -18, '', 0, 19, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 46, 46, 2811, 3119, 1288, 789, 184, 184, 184, 184, 267, 267, 184, 130, 130, 130, 130, 130, 130, 13, 73, 1, -18, '', 0, 20, 15, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 46, 46, 2949, 3257, 1334, 805, 189, 189, 189, 189, 270, 270, 189, 144, 144, 144, 144, 144, 144, 13, 74, 1, -18, '', 0, 21, 16, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 46, 46, 3087, 3395, 1380, 820, 194, 194, 194, 194, 273, 273, 194, 158, 158, 158, 158, 158, 158, 13, 76, 1, -18, '', 0, 22, 17, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 46, 46, 3225, 3533, 1426, 835, 199, 199, 199, 199, 276, 276, 199, 172, 172, 172, 172, 172, 172, 13, 77, 1, -18, '', 0, 23, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 46, 46, 4053, 4131, 1518, 843, 207, 207, 207, 207, 289, 289, 207, 162, 162, 162, 162, 162, 162, 13, 76, 1, -18, '', 0, 25, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 46, 46, 4191, 4269, 1564, 858, 212, 212, 212, 212, 292, 292, 212, 176, 176, 176, 176, 176, 176, 13, 77, 1, -18, '', 0, 26, 18, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 46, 46, 4329, 4407, 1610, 874, 217, 217, 217, 217, 295, 295, 217, 190, 190, 190, 190, 190, 190, 14, 78, 1, -18, '', 0, 27, 19, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 46, 46, 4467, 4545, 1656, 889, 222, 222, 222, 222, 298, 298, 222, 204, 204, 204, 204, 204, 204, 14, 80, 1, -18, '', 0, 28, 20, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 46, 46, 4605, 4683, 1702, 904, 227, 227, 227, 227, 301, 301, 227, 218, 218, 218, 218, 218, 218, 14, 81, 1, -18, '', 0, 29, 21, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 46, 46, 2790, 0, 943, 1311, 198, 198, 198, 198, 210, 210, 198, 116, 116, 116, 116, 116, 116, 14, 67, 2, -18, '', 0, 26, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 46, 46, 2974, 0, 966, 1334, 203, 203, 203, 203, 212, 212, 203, 130, 130, 130, 130, 130, 130, 15, 70, 2, -18, '', 0, 28, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 46, 46, 3158, 0, 989, 1357, 208, 208, 208, 208, 214, 214, 208, 144, 144, 144, 144, 144, 144, 15, 72, 2, -18, '', 0, 30, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 46, 46, 3342, 0, 1012, 1380, 213, 213, 213, 213, 216, 216, 213, 158, 158, 158, 158, 158, 158, 16, 75, 2, -18, '', 0, 32, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 46, 46, 3526, 0, 1035, 1403, 218, 218, 218, 218, 218, 218, 218, 172, 172, 172, 172, 172, 172, 16, 78, 2, -18, '', 0, 34, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 46, 46, 4170, 0, 1173, 1449, 226, 226, 226, 226, 230, 230, 226, 162, 162, 162, 162, 162, 162, 15, 74, 2, -18, '', 0, 34, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 46, 46, 4354, 0, 1196, 1472, 231, 231, 231, 231, 232, 232, 231, 176, 176, 176, 176, 176, 176, 16, 76, 2, -18, '', 0, 36, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 46, 46, 4538, 0, 1219, 1495, 236, 236, 236, 236, 234, 234, 236, 190, 190, 190, 190, 190, 190, 16, 79, 2, -18, '', 0, 38, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 46, 46, 4722, 0, 1242, 1518, 241, 241, 241, 241, 236, 236, 241, 204, 204, 204, 204, 204, 204, 17, 82, 2, -18, '', 0, 40, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 46, 46, 4906, 0, 1265, 1541, 246, 246, 246, 246, 238, 238, 246, 218, 218, 218, 218, 218, 218, 17, 84, 2, -18, '', 0, 42, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 46, 46, 2333, 2981, 613, 379, 175, 175, 175, 175, 264, 264, 175, 116, 116, 116, 116, 116, 116, 12, 71, 1, -18, '', 0, 18, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 46, 46, 2425, 3119, 628, 391, 180, 180, 180, 180, 267, 267, 180, 130, 130, 130, 130, 130, 130, 13, 73, 1, -18, '', 0, 19, 15, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 46, 46, 2517, 3257, 644, 402, 185, 185, 185, 185, 270, 270, 185, 144, 144, 144, 144, 144, 144, 13, 74, 1, -18, '', 0, 20, 16, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 46, 46, 2609, 3395, 659, 414, 190, 190, 190, 190, 273, 273, 190, 158, 158, 158, 158, 158, 158, 13, 76, 1, -18, '', 0, 21, 17, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 46, 46, 2701, 3533, 674, 425, 195, 195, 195, 195, 276, 276, 195, 172, 172, 172, 172, 172, 172, 13, 77, 1, -18, '', 0, 22, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 46, 46, 3483, 4131, 751, 425, 203, 203, 203, 203, 289, 289, 203, 162, 162, 162, 162, 162, 162, 13, 76, 1, -18, '', 0, 23, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 46, 46, 3575, 4269, 766, 437, 208, 208, 208, 208, 292, 292, 208, 176, 176, 176, 176, 176, 176, 13, 77, 1, -18, '', 0, 24, 18, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 46, 46, 3667, 4407, 782, 448, 213, 213, 213, 213, 295, 295, 213, 190, 190, 190, 190, 190, 190, 14, 78, 1, -18, '', 0, 25, 19, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 46, 46, 3759, 4545, 797, 460, 218, 218, 218, 218, 298, 298, 218, 204, 204, 204, 204, 204, 204, 14, 80, 1, -18, '', 0, 26, 20, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 46, 46, 3851, 4683, 812, 471, 223, 223, 223, 223, 301, 301, 223, 218, 218, 218, 218, 218, 218, 14, 81, 1, -18, '', 0, 27, 21, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 47, 47, 3373, 0, 1622, 1316, 203, 203, 203, 203, 213, 213, 203, 118, 118, 118, 118, 118, 118, 14, 74, 3, -18, '', 0, 28, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 47, 47, 3608, 0, 1692, 1340, 208, 208, 208, 208, 215, 215, 208, 132, 132, 132, 132, 132, 132, 15, 77, 3, -18, '', 0, 30, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 47, 47, 3843, 0, 1763, 1363, 213, 213, 213, 213, 217, 217, 213, 146, 146, 146, 146, 146, 146, 15, 80, 3, -18, '', 0, 32, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 47, 47, 4078, 0, 1833, 1387, 218, 218, 218, 218, 219, 219, 218, 160, 160, 160, 160, 160, 160, 16, 82, 3, -18, '', 0, 34, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 47, 47, 4313, 0, 1904, 1410, 223, 223, 223, 223, 221, 221, 223, 174, 174, 174, 174, 174, 174, 16, 85, 3, -18, '', 0, 36, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 47, 47, 5018, 0, 1998, 1433, 231, 231, 231, 231, 233, 233, 231, 166, 166, 166, 166, 166, 166, 16, 81, 3, -18, '', 0, 38, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 47, 47, 5253, 0, 2068, 1457, 236, 236, 236, 236, 235, 235, 236, 180, 180, 180, 180, 180, 180, 16, 84, 3, -18, '', 0, 40, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 47, 47, 5488, 0, 2139, 1480, 241, 241, 241, 241, 237, 237, 241, 194, 194, 194, 194, 194, 194, 17, 87, 3, -18, '', 0, 42, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 47, 47, 5723, 0, 2209, 1504, 246, 246, 246, 246, 239, 239, 246, 208, 208, 208, 208, 208, 208, 17, 89, 3, -18, '', 0, 44, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 47, 47, 5958, 0, 2280, 1527, 251, 251, 251, 251, 241, 241, 251, 222, 222, 222, 222, 222, 222, 18, 92, 3, -18, '', 0, 46, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 47, 47, 2782, 3083, 1269, 791, 183, 183, 183, 183, 267, 267, 183, 118, 118, 118, 118, 118, 118, 13, 73, 1, -18, '', 0, 19, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 47, 47, 2923, 3224, 1316, 807, 188, 188, 188, 188, 270, 270, 188, 132, 132, 132, 132, 132, 132, 13, 74, 1, -18, '', 0, 20, 15, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 47, 47, 3064, 3365, 1363, 823, 193, 193, 193, 193, 273, 273, 193, 146, 146, 146, 146, 146, 146, 13, 76, 1, -18, '', 0, 21, 16, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 47, 47, 3205, 3506, 1410, 838, 198, 198, 198, 198, 276, 276, 198, 160, 160, 160, 160, 160, 160, 13, 77, 1, -18, '', 0, 22, 17, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 47, 47, 3346, 3647, 1457, 854, 203, 203, 203, 203, 279, 279, 203, 174, 174, 174, 174, 174, 174, 14, 78, 1, -18, '', 0, 23, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 47, 47, 4192, 4258, 1551, 861, 211, 211, 211, 211, 292, 292, 211, 166, 166, 166, 166, 166, 166, 13, 77, 1, -18, '', 0, 25, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 47, 47, 4333, 4399, 1598, 877, 216, 216, 216, 216, 295, 295, 216, 180, 180, 180, 180, 180, 180, 14, 78, 1, -18, '', 0, 26, 18, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 47, 47, 4474, 4540, 1645, 893, 221, 221, 221, 221, 298, 298, 221, 194, 194, 194, 194, 194, 194, 14, 80, 1, -18, '', 0, 27, 19, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 47, 47, 4615, 4681, 1692, 908, 226, 226, 226, 226, 301, 301, 226, 208, 208, 208, 208, 208, 208, 14, 81, 1, -18, '', 0, 28, 20, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 47, 47, 4756, 4822, 1739, 924, 231, 231, 231, 231, 304, 304, 231, 222, 222, 222, 222, 222, 222, 14, 83, 1, -18, '', 0, 29, 21, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 47, 47, 2905, 0, 963, 1339, 203, 203, 203, 203, 213, 213, 203, 118, 118, 118, 118, 118, 118, 14, 69, 3, -18, '', 0, 26, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 47, 47, 3093, 0, 987, 1363, 208, 208, 208, 208, 215, 215, 208, 132, 132, 132, 132, 132, 132, 15, 71, 3, -18, '', 0, 28, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 47, 47, 3281, 0, 1010, 1386, 213, 213, 213, 213, 217, 217, 213, 146, 146, 146, 146, 146, 146, 15, 74, 3, -18, '', 0, 30, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 47, 47, 3469, 0, 1034, 1410, 218, 218, 218, 218, 219, 219, 218, 160, 160, 160, 160, 160, 160, 16, 76, 3, -18, '', 0, 32, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 47, 47, 3657, 0, 1057, 1433, 223, 223, 223, 223, 221, 221, 223, 174, 174, 174, 174, 174, 174, 16, 79, 3, -18, '', 0, 34, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 47, 47, 4315, 0, 1198, 1480, 231, 231, 231, 231, 233, 233, 231, 166, 166, 166, 166, 166, 166, 16, 75, 3, -18, '', 0, 34, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 47, 47, 4503, 0, 1222, 1504, 236, 236, 236, 236, 235, 235, 236, 180, 180, 180, 180, 180, 180, 16, 78, 3, -18, '', 0, 36, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 47, 47, 4691, 0, 1245, 1527, 241, 241, 241, 241, 237, 237, 241, 194, 194, 194, 194, 194, 194, 17, 80, 3, -18, '', 0, 38, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 47, 47, 4879, 0, 1269, 1551, 246, 246, 246, 246, 239, 239, 246, 208, 208, 208, 208, 208, 208, 17, 83, 3, -18, '', 0, 40, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 47, 47, 5067, 0, 1292, 1574, 251, 251, 251, 251, 241, 241, 251, 222, 222, 222, 222, 222, 222, 18, 85, 3, -18, '', 0, 42, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 47, 47, 2432, 3083, 626, 387, 179, 179, 179, 179, 267, 267, 179, 118, 118, 118, 118, 118, 118, 13, 73, 1, -18, '', 0, 18, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 47, 47, 2526, 3224, 642, 399, 184, 184, 184, 184, 270, 270, 184, 132, 132, 132, 132, 132, 132, 13, 74, 1, -18, '', 0, 19, 15, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 47, 47, 2620, 3365, 658, 411, 189, 189, 189, 189, 273, 273, 189, 146, 146, 146, 146, 146, 146, 13, 76, 1, -18, '', 0, 20, 16, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 47, 47, 2714, 3506, 673, 423, 194, 194, 194, 194, 276, 276, 194, 160, 160, 160, 160, 160, 160, 13, 77, 1, -18, '', 0, 21, 17, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 47, 47, 2808, 3647, 689, 434, 199, 199, 199, 199, 279, 279, 199, 174, 174, 174, 174, 174, 174, 14, 78, 1, -18, '', 0, 22, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 47, 47, 3607, 4258, 767, 434, 207, 207, 207, 207, 292, 292, 207, 166, 166, 166, 166, 166, 166, 13, 77, 1, -18, '', 0, 23, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 47, 47, 3701, 4399, 783, 446, 212, 212, 212, 212, 295, 295, 212, 180, 180, 180, 180, 180, 180, 14, 78, 1, -18, '', 0, 24, 18, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 47, 47, 3795, 4540, 799, 458, 217, 217, 217, 217, 298, 298, 217, 194, 194, 194, 194, 194, 194, 14, 80, 1, -18, '', 0, 25, 19, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 47, 47, 3889, 4681, 814, 470, 222, 222, 222, 222, 301, 301, 222, 208, 208, 208, 208, 208, 208, 14, 81, 1, -18, '', 0, 26, 20, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 47, 47, 3983, 4822, 830, 481, 227, 227, 227, 227, 304, 304, 227, 222, 222, 222, 222, 222, 222, 14, 83, 1, -18, '', 0, 27, 21, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 48, 48, 3509, 0, 1656, 1344, 209, 209, 209, 209, 216, 216, 209, 121, 121, 121, 121, 121, 121, 15, 75, 3, -18, '', 0, 29, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 48, 48, 3749, 0, 1728, 1368, 214, 214, 214, 214, 218, 218, 214, 136, 136, 136, 136, 136, 136, 15, 78, 3, -18, '', 0, 31, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 48, 48, 3989, 0, 1800, 1392, 219, 219, 219, 219, 220, 220, 219, 151, 151, 151, 151, 151, 151, 16, 81, 3, -18, '', 0, 33, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 48, 48, 4229, 0, 1872, 1416, 224, 224, 224, 224, 222, 222, 224, 166, 166, 166, 166, 166, 166, 16, 84, 3, -18, '', 0, 35, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 48, 48, 4469, 0, 1944, 1440, 229, 229, 229, 229, 224, 224, 229, 181, 181, 181, 181, 181, 181, 17, 87, 3, -18, '', 0, 37, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 48, 48, 5189, 0, 2040, 1464, 238, 238, 238, 238, 236, 236, 238, 169, 169, 169, 169, 169, 169, 16, 82, 3, -18, '', 0, 39, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 48, 48, 5429, 0, 2112, 1488, 243, 243, 243, 243, 238, 238, 243, 184, 184, 184, 184, 184, 184, 16, 85, 3, -18, '', 0, 41, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 48, 48, 5669, 0, 2184, 1512, 248, 248, 248, 248, 240, 240, 248, 199, 199, 199, 199, 199, 199, 17, 88, 3, -18, '', 0, 43, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 48, 48, 5909, 0, 2256, 1536, 253, 253, 253, 253, 242, 242, 253, 214, 214, 214, 214, 214, 214, 17, 91, 3, -18, '', 0, 45, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 48, 48, 6149, 0, 2328, 1560, 258, 258, 258, 258, 244, 244, 258, 229, 229, 229, 229, 229, 229, 18, 94, 3, -18, '', 0, 47, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 48, 48, 2895, 3187, 1296, 808, 188, 188, 188, 188, 271, 271, 188, 121, 121, 121, 121, 121, 121, 13, 74, 1, -18, '', 0, 20, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 48, 48, 3039, 3331, 1344, 824, 193, 193, 193, 193, 274, 274, 193, 136, 136, 136, 136, 136, 136, 13, 76, 1, -18, '', 0, 21, 15, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 48, 48, 3183, 3475, 1392, 840, 198, 198, 198, 198, 277, 277, 198, 151, 151, 151, 151, 151, 151, 13, 77, 1, -18, '', 0, 22, 16, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 48, 48, 3327, 3619, 1440, 856, 203, 203, 203, 203, 280, 280, 203, 166, 166, 166, 166, 166, 166, 14, 78, 1, -18, '', 0, 23, 17, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 48, 48, 3471, 3763, 1488, 872, 208, 208, 208, 208, 283, 283, 208, 181, 181, 181, 181, 181, 181, 14, 80, 1, -18, '', 0, 24, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 48, 48, 4335, 4387, 1584, 880, 217, 217, 217, 217, 296, 296, 217, 169, 169, 169, 169, 169, 169, 14, 78, 1, -18, '', 0, 26, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 48, 48, 4479, 4531, 1632, 896, 222, 222, 222, 222, 299, 299, 222, 184, 184, 184, 184, 184, 184, 14, 80, 1, -18, '', 0, 27, 18, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 48, 48, 4623, 4675, 1680, 912, 227, 227, 227, 227, 302, 302, 227, 199, 199, 199, 199, 199, 199, 14, 81, 1, -18, '', 0, 28, 19, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 48, 48, 4767, 4819, 1728, 928, 232, 232, 232, 232, 305, 305, 232, 214, 214, 214, 214, 214, 214, 14, 83, 1, -18, '', 0, 29, 20, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 48, 48, 4911, 4963, 1776, 944, 237, 237, 237, 237, 308, 308, 237, 229, 229, 229, 229, 229, 229, 15, 84, 1, -18, '', 0, 30, 21, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 48, 48, 3024, 0, 984, 1368, 209, 209, 209, 209, 216, 216, 209, 121, 121, 121, 121, 121, 121, 15, 70, 3, -18, '', 0, 27, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 48, 48, 3216, 0, 1008, 1392, 214, 214, 214, 214, 218, 218, 214, 136, 136, 136, 136, 136, 136, 15, 72, 3, -18, '', 0, 29, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 48, 48, 3408, 0, 1032, 1416, 219, 219, 219, 219, 220, 220, 219, 151, 151, 151, 151, 151, 151, 16, 75, 3, -18, '', 0, 31, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 48, 48, 3600, 0, 1056, 1440, 224, 224, 224, 224, 222, 222, 224, 166, 166, 166, 166, 166, 166, 16, 78, 3, -18, '', 0, 33, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 48, 48, 3792, 0, 1080, 1464, 229, 229, 229, 229, 224, 224, 229, 181, 181, 181, 181, 181, 181, 17, 80, 3, -18, '', 0, 35, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 48, 48, 4464, 0, 1224, 1512, 238, 238, 238, 238, 236, 236, 238, 169, 169, 169, 169, 169, 169, 16, 76, 3, -18, '', 0, 35, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 48, 48, 4656, 0, 1248, 1536, 243, 243, 243, 243, 238, 238, 243, 184, 184, 184, 184, 184, 184, 16, 79, 3, -18, '', 0, 37, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 48, 48, 4848, 0, 1272, 1560, 248, 248, 248, 248, 240, 240, 248, 199, 199, 199, 199, 199, 199, 17, 82, 3, -18, '', 0, 39, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 48, 48, 5040, 0, 1296, 1584, 253, 253, 253, 253, 242, 242, 253, 214, 214, 214, 214, 214, 214, 17, 84, 3, -18, '', 0, 41, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 48, 48, 5232, 0, 1320, 1608, 258, 258, 258, 258, 244, 244, 258, 229, 229, 229, 229, 229, 229, 18, 87, 3, -18, '', 0, 43, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 48, 48, 2534, 3187, 640, 396, 184, 184, 184, 184, 271, 271, 184, 121, 121, 121, 121, 121, 121, 13, 74, 1, -18, '', 0, 19, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 48, 48, 2630, 3331, 656, 408, 189, 189, 189, 189, 274, 274, 189, 136, 136, 136, 136, 136, 136, 13, 76, 1, -18, '', 0, 20, 15, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 48, 48, 2726, 3475, 672, 420, 194, 194, 194, 194, 277, 277, 194, 151, 151, 151, 151, 151, 151, 13, 77, 1, -18, '', 0, 21, 16, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 48, 48, 2822, 3619, 688, 432, 199, 199, 199, 199, 280, 280, 199, 166, 166, 166, 166, 166, 166, 14, 78, 1, -18, '', 0, 22, 17, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 48, 48, 2918, 3763, 704, 444, 204, 204, 204, 204, 283, 283, 204, 181, 181, 181, 181, 181, 181, 14, 80, 1, -18, '', 0, 23, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 48, 48, 3734, 4387, 784, 444, 213, 213, 213, 213, 296, 296, 213, 169, 169, 169, 169, 169, 169, 14, 78, 1, -18, '', 0, 24, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 48, 48, 3830, 4531, 800, 456, 218, 218, 218, 218, 299, 299, 218, 184, 184, 184, 184, 184, 184, 14, 80, 1, -18, '', 0, 25, 18, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 48, 48, 3926, 4675, 816, 468, 223, 223, 223, 223, 302, 302, 223, 199, 199, 199, 199, 199, 199, 14, 81, 1, -18, '', 0, 26, 19, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 48, 48, 4022, 4819, 832, 480, 228, 228, 228, 228, 305, 305, 228, 214, 214, 214, 214, 214, 214, 14, 83, 1, -18, '', 0, 27, 20, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 48, 48, 4118, 4963, 848, 492, 233, 233, 233, 233, 308, 308, 233, 229, 229, 229, 229, 229, 229, 15, 84, 1, -18, '', 0, 28, 21, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 49, 49, 3650, 0, 1691, 1372, 214, 214, 214, 214, 219, 219, 214, 123, 123, 123, 123, 123, 123, 15, 77, 3, -18, '', 0, 29, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 49, 49, 3895, 0, 1764, 1397, 219, 219, 219, 219, 221, 221, 219, 138, 138, 138, 138, 138, 138, 15, 80, 3, -18, '', 0, 31, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 49, 49, 4140, 0, 1838, 1421, 224, 224, 224, 224, 223, 223, 224, 153, 153, 153, 153, 153, 153, 16, 82, 3, -18, '', 0, 33, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 49, 49, 4385, 0, 1911, 1446, 229, 229, 229, 229, 225, 225, 229, 168, 168, 168, 168, 168, 168, 16, 85, 3, -18, '', 0, 35, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 49, 49, 4630, 0, 1985, 1470, 234, 234, 234, 234, 227, 227, 234, 183, 183, 183, 183, 183, 183, 17, 88, 3, -18, '', 0, 37, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 49, 49, 5365, 0, 2083, 1494, 243, 243, 243, 243, 239, 239, 243, 171, 171, 171, 171, 171, 171, 16, 84, 3, -18, '', 0, 39, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 49, 49, 5610, 0, 2156, 1519, 248, 248, 248, 248, 241, 241, 248, 186, 186, 186, 186, 186, 186, 17, 87, 3, -18, '', 0, 41, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 49, 49, 5855, 0, 2230, 1543, 253, 253, 253, 253, 243, 243, 253, 201, 201, 201, 201, 201, 201, 17, 89, 3, -18, '', 0, 43, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 49, 49, 6100, 0, 2303, 1568, 258, 258, 258, 258, 245, 245, 258, 216, 216, 216, 216, 216, 216, 18, 92, 3, -18, '', 0, 45, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 49, 49, 6345, 0, 2377, 1592, 263, 263, 263, 263, 247, 247, 263, 231, 231, 231, 231, 231, 231, 18, 95, 3, -18, '', 0, 47, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 49, 49, 3011, 3293, 1323, 825, 193, 193, 193, 193, 274, 274, 193, 123, 123, 123, 123, 123, 123, 13, 76, 1, -18, '', 0, 20, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 49, 49, 3158, 3440, 1372, 841, 198, 198, 198, 198, 277, 277, 198, 138, 138, 138, 138, 138, 138, 13, 77, 1, -18, '', 0, 21, 15, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 49, 49, 3305, 3587, 1421, 858, 203, 203, 203, 203, 280, 280, 203, 153, 153, 153, 153, 153, 153, 14, 78, 1, -18, '', 0, 22, 16, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 49, 49, 3452, 3734, 1470, 874, 208, 208, 208, 208, 283, 283, 208, 168, 168, 168, 168, 168, 168, 14, 80, 1, -18, '', 0, 23, 17, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 49, 49, 3599, 3881, 1519, 890, 213, 213, 213, 213, 286, 286, 213, 183, 183, 183, 183, 183, 183, 14, 81, 1, -18, '', 0, 24, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 49, 49, 4481, 4518, 1617, 898, 222, 222, 222, 222, 299, 299, 222, 171, 171, 171, 171, 171, 171, 14, 80, 1, -18, '', 0, 26, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 49, 49, 4628, 4665, 1666, 914, 227, 227, 227, 227, 302, 302, 227, 186, 186, 186, 186, 186, 186, 14, 81, 1, -18, '', 0, 27, 18, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 49, 49, 4775, 4812, 1715, 931, 232, 232, 232, 232, 305, 305, 232, 201, 201, 201, 201, 201, 201, 14, 83, 1, -18, '', 0, 28, 19, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 49, 49, 4922, 4959, 1764, 947, 237, 237, 237, 237, 308, 308, 237, 216, 216, 216, 216, 216, 216, 15, 84, 1, -18, '', 0, 29, 20, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 49, 49, 5069, 5106, 1813, 963, 242, 242, 242, 242, 311, 311, 242, 231, 231, 231, 231, 231, 231, 15, 85, 1, -18, '', 0, 30, 21, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 49, 49, 3146, 0, 1004, 1396, 214, 214, 214, 214, 219, 219, 214, 123, 123, 123, 123, 123, 123, 15, 71, 3, -18, '', 0, 27, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 49, 49, 3342, 0, 1029, 1421, 219, 219, 219, 219, 221, 221, 219, 138, 138, 138, 138, 138, 138, 15, 74, 3, -18, '', 0, 29, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 49, 49, 3538, 0, 1053, 1445, 224, 224, 224, 224, 223, 223, 224, 153, 153, 153, 153, 153, 153, 16, 76, 3, -18, '', 0, 31, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 49, 49, 3734, 0, 1078, 1470, 229, 229, 229, 229, 225, 225, 229, 168, 168, 168, 168, 168, 168, 16, 79, 3, -18, '', 0, 33, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 49, 49, 3930, 0, 1102, 1494, 234, 234, 234, 234, 227, 227, 234, 183, 183, 183, 183, 183, 183, 17, 82, 3, -18, '', 0, 35, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 49, 49, 4616, 0, 1249, 1543, 243, 243, 243, 243, 239, 239, 243, 171, 171, 171, 171, 171, 171, 16, 78, 3, -18, '', 0, 35, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 49, 49, 4812, 0, 1274, 1568, 248, 248, 248, 248, 241, 241, 248, 186, 186, 186, 186, 186, 186, 17, 80, 3, -18, '', 0, 37, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 49, 49, 5008, 0, 1298, 1592, 253, 253, 253, 253, 243, 243, 253, 201, 201, 201, 201, 201, 201, 17, 83, 3, -18, '', 0, 39, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 49, 49, 5204, 0, 1323, 1617, 258, 258, 258, 258, 245, 245, 258, 216, 216, 216, 216, 216, 216, 18, 85, 3, -18, '', 0, 41, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 49, 49, 5400, 0, 1347, 1641, 263, 263, 263, 263, 247, 247, 263, 231, 231, 231, 231, 231, 231, 18, 88, 3, -18, '', 0, 43, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 49, 49, 2640, 3293, 653, 404, 188, 188, 188, 188, 274, 274, 188, 123, 123, 123, 123, 123, 123, 13, 76, 1, -18, '', 0, 19, 14, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 49, 49, 2738, 3440, 669, 416, 193, 193, 193, 193, 277, 277, 193, 138, 138, 138, 138, 138, 138, 13, 77, 1, -18, '', 0, 20, 15, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 49, 49, 2836, 3587, 686, 428, 198, 198, 198, 198, 280, 280, 198, 153, 153, 153, 153, 153, 153, 14, 78, 1, -18, '', 0, 21, 16, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 49, 49, 2934, 3734, 702, 441, 203, 203, 203, 203, 283, 283, 203, 168, 168, 168, 168, 168, 168, 14, 80, 1, -18, '', 0, 22, 17, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 49, 49, 3032, 3881, 718, 453, 208, 208, 208, 208, 286, 286, 208, 183, 183, 183, 183, 183, 183, 14, 81, 1, -18, '', 0, 23, 18, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 49, 49, 3865, 4518, 800, 453, 217, 217, 217, 217, 299, 299, 217, 171, 171, 171, 171, 171, 171, 14, 80, 1, -18, '', 0, 24, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 49, 49, 3963, 4665, 816, 465, 222, 222, 222, 222, 302, 302, 222, 186, 186, 186, 186, 186, 186, 14, 81, 1, -18, '', 0, 25, 18, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 49, 49, 4061, 4812, 833, 477, 227, 227, 227, 227, 305, 305, 227, 201, 201, 201, 201, 201, 201, 14, 83, 1, -18, '', 0, 26, 19, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 49, 49, 4159, 4959, 849, 490, 232, 232, 232, 232, 308, 308, 232, 216, 216, 216, 216, 216, 216, 15, 84, 1, -18, '', 0, 27, 20, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 49, 49, 4257, 5106, 865, 502, 237, 237, 237, 237, 311, 311, 237, 231, 231, 231, 231, 231, 231, 15, 85, 1, -18, '', 0, 28, 21, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 50, 50, 3795, 0, 1725, 1400, 221, 221, 221, 221, 222, 222, 221, 125, 125, 125, 125, 125, 125, 15, 78, 3, -18, '', 0, 29, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 50, 50, 4045, 0, 1800, 1425, 227, 227, 227, 227, 224, 224, 227, 140, 140, 140, 140, 140, 140, 16, 81, 3, -18, '', 0, 31, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 50, 50, 4295, 0, 1875, 1450, 233, 233, 233, 233, 226, 226, 233, 155, 155, 155, 155, 155, 155, 16, 84, 3, -18, '', 0, 33, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 50, 50, 4545, 0, 1950, 1475, 239, 239, 239, 239, 228, 228, 239, 170, 170, 170, 170, 170, 170, 17, 87, 3, -18, '', 0, 35, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 50, 50, 4795, 0, 2025, 1500, 245, 245, 245, 245, 230, 230, 245, 185, 185, 185, 185, 185, 185, 17, 89, 3, -18, '', 0, 37, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 50, 50, 5545, 0, 2125, 1525, 251, 251, 251, 251, 242, 242, 251, 175, 175, 175, 175, 175, 175, 16, 85, 3, -18, '', 0, 39, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 50, 50, 5795, 0, 2200, 1550, 257, 257, 257, 257, 244, 244, 257, 190, 190, 190, 190, 190, 190, 17, 88, 3, -18, '', 0, 41, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 50, 50, 6045, 0, 2275, 1575, 263, 263, 263, 263, 246, 246, 263, 205, 205, 205, 205, 205, 205, 17, 91, 3, -18, '', 0, 43, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 50, 50, 6295, 0, 2350, 1600, 269, 269, 269, 269, 248, 248, 269, 220, 220, 220, 220, 220, 220, 18, 94, 3, -18, '', 0, 45, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 50, 50, 6545, 0, 2425, 1625, 275, 275, 275, 275, 250, 250, 275, 235, 235, 235, 235, 235, 235, 18, 96, 3, -18, '', 0, 47, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 50, 50, 3130, 3400, 1350, 841, 199, 199, 199, 199, 278, 278, 199, 125, 125, 125, 125, 125, 125, 13, 77, 1, -18, '', 0, 20, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 50, 50, 3280, 3550, 1400, 858, 205, 205, 205, 205, 281, 281, 205, 140, 140, 140, 140, 140, 140, 14, 78, 1, -18, '', 0, 21, 16, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 50, 50, 3430, 3700, 1450, 875, 211, 211, 211, 211, 284, 284, 211, 155, 155, 155, 155, 155, 155, 14, 80, 1, -18, '', 0, 22, 17, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 50, 50, 3580, 3850, 1500, 891, 217, 217, 217, 217, 287, 287, 217, 170, 170, 170, 170, 170, 170, 14, 81, 1, -18, '', 0, 23, 18, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 50, 50, 3730, 4000, 1550, 908, 223, 223, 223, 223, 290, 290, 223, 185, 185, 185, 185, 185, 185, 14, 83, 1, -18, '', 0, 24, 19, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 50, 50, 4630, 4650, 1650, 916, 229, 229, 229, 229, 303, 303, 229, 175, 175, 175, 175, 175, 175, 14, 81, 1, -18, '', 0, 26, 18, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 50, 50, 4780, 4800, 1700, 933, 235, 235, 235, 235, 306, 306, 235, 190, 190, 190, 190, 190, 190, 14, 83, 1, -18, '', 0, 27, 19, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 50, 50, 4930, 4950, 1750, 950, 241, 241, 241, 241, 309, 309, 241, 205, 205, 205, 205, 205, 205, 15, 84, 1, -18, '', 0, 28, 20, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 50, 50, 5080, 5100, 1800, 966, 247, 247, 247, 247, 312, 312, 247, 220, 220, 220, 220, 220, 220, 15, 85, 1, -18, '', 0, 29, 21, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 50, 50, 5230, 5250, 1850, 983, 253, 253, 253, 253, 315, 315, 253, 235, 235, 235, 235, 235, 235, 15, 87, 1, -18, '', 0, 30, 22, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 50, 50, 3272, 0, 1025, 1425, 221, 221, 221, 221, 222, 222, 221, 125, 125, 125, 125, 125, 125, 15, 72, 3, -18, '', 0, 27, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 50, 50, 3472, 0, 1050, 1450, 227, 227, 227, 227, 224, 224, 227, 140, 140, 140, 140, 140, 140, 16, 75, 3, -18, '', 0, 29, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 50, 50, 3672, 0, 1075, 1475, 233, 233, 233, 233, 226, 226, 233, 155, 155, 155, 155, 155, 155, 16, 78, 3, -18, '', 0, 31, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 50, 50, 3872, 0, 1100, 1500, 239, 239, 239, 239, 228, 228, 239, 170, 170, 170, 170, 170, 170, 17, 80, 3, -18, '', 0, 33, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 50, 50, 4072, 0, 1125, 1525, 245, 245, 245, 245, 230, 230, 245, 185, 185, 185, 185, 185, 185, 17, 83, 3, -18, '', 0, 35, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 50, 50, 4772, 0, 1275, 1575, 251, 251, 251, 251, 242, 242, 251, 175, 175, 175, 175, 175, 175, 16, 79, 3, -18, '', 0, 35, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 50, 50, 4972, 0, 1300, 1600, 257, 257, 257, 257, 244, 244, 257, 190, 190, 190, 190, 190, 190, 17, 82, 3, -18, '', 0, 37, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 50, 50, 5172, 0, 1325, 1625, 263, 263, 263, 263, 246, 246, 263, 205, 205, 205, 205, 205, 205, 17, 84, 3, -18, '', 0, 39, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 50, 50, 5372, 0, 1350, 1650, 269, 269, 269, 269, 248, 248, 269, 220, 220, 220, 220, 220, 220, 18, 87, 3, -18, '', 0, 41, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 50, 50, 5572, 0, 1375, 1675, 275, 275, 275, 275, 250, 250, 275, 235, 235, 235, 235, 235, 235, 18, 89, 3, -18, '', 0, 43, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 50, 50, 2748, 3400, 666, 412, 194, 194, 194, 194, 278, 278, 194, 125, 125, 125, 125, 125, 125, 13, 77, 1, -18, '', 0, 19, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 50, 50, 2848, 3550, 683, 425, 200, 200, 200, 200, 281, 281, 200, 140, 140, 140, 140, 140, 140, 14, 78, 1, -18, '', 0, 20, 16, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 50, 50, 2948, 3700, 700, 437, 206, 206, 206, 206, 284, 284, 206, 155, 155, 155, 155, 155, 155, 14, 80, 1, -18, '', 0, 21, 17, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 50, 50, 3048, 3850, 716, 450, 212, 212, 212, 212, 287, 287, 212, 170, 170, 170, 170, 170, 170, 14, 81, 1, -18, '', 0, 22, 18, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 50, 50, 3148, 4000, 733, 462, 218, 218, 218, 218, 290, 290, 218, 185, 185, 185, 185, 185, 185, 14, 83, 1, -18, '', 0, 23, 19, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 50, 50, 3998, 4650, 816, 462, 224, 224, 224, 224, 303, 303, 224, 175, 175, 175, 175, 175, 175, 14, 81, 1, -18, '', 0, 24, 18, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 50, 50, 4098, 4800, 833, 475, 230, 230, 230, 230, 306, 306, 230, 190, 190, 190, 190, 190, 190, 14, 83, 1, -18, '', 0, 25, 19, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 50, 50, 4198, 4950, 850, 487, 236, 236, 236, 236, 309, 309, 236, 205, 205, 205, 205, 205, 205, 15, 84, 1, -18, '', 0, 26, 20, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 50, 50, 4298, 5100, 866, 500, 242, 242, 242, 242, 312, 312, 242, 220, 220, 220, 220, 220, 220, 15, 85, 1, -18, '', 0, 27, 21, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 50, 50, 4398, 5250, 883, 512, 248, 248, 248, 248, 315, 315, 248, 235, 235, 235, 235, 235, 235, 15, 87, 1, -18, '', 0, 28, 22, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 51, 51, 3945, 0, 1760, 1428, 226, 226, 226, 226, 225, 225, 226, 127, 127, 127, 127, 127, 127, 15, 80, 3, -18, '', 0, 30, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 51, 51, 4200, 0, 1836, 1454, 232, 232, 232, 232, 227, 227, 232, 142, 142, 142, 142, 142, 142, 16, 82, 3, -18, '', 0, 32, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 51, 51, 4455, 0, 1913, 1479, 238, 238, 238, 238, 229, 229, 238, 157, 157, 157, 157, 157, 157, 16, 85, 3, -18, '', 0, 34, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 51, 51, 4710, 0, 1989, 1505, 244, 244, 244, 244, 231, 231, 244, 172, 172, 172, 172, 172, 172, 17, 88, 3, -18, '', 0, 36, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 51, 51, 4965, 0, 2066, 1530, 250, 250, 250, 250, 233, 233, 250, 187, 187, 187, 187, 187, 187, 17, 91, 3, -18, '', 0, 38, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 51, 51, 5730, 0, 2168, 1555, 256, 256, 256, 256, 245, 245, 256, 177, 177, 177, 177, 177, 177, 17, 87, 3, -18, '', 0, 40, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 51, 51, 5985, 0, 2244, 1581, 262, 262, 262, 262, 247, 247, 262, 192, 192, 192, 192, 192, 192, 17, 89, 3, -18, '', 0, 42, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 51, 51, 6240, 0, 2321, 1606, 268, 268, 268, 268, 249, 249, 268, 207, 207, 207, 207, 207, 207, 18, 92, 3, -18, '', 0, 44, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 51, 51, 6495, 0, 2397, 1632, 274, 274, 274, 274, 251, 251, 274, 222, 222, 222, 222, 222, 222, 18, 95, 3, -18, '', 0, 46, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 51, 51, 6750, 0, 2474, 1657, 280, 280, 280, 280, 253, 253, 280, 237, 237, 237, 237, 237, 237, 19, 98, 3, -18, '', 0, 48, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 51, 51, 3253, 3509, 1377, 859, 204, 204, 204, 204, 281, 281, 204, 127, 127, 127, 127, 127, 127, 14, 78, 1, -18, '', 0, 20, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 51, 51, 3406, 3662, 1428, 876, 210, 210, 210, 210, 284, 284, 210, 142, 142, 142, 142, 142, 142, 14, 80, 1, -18, '', 0, 21, 16, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 51, 51, 3559, 3815, 1479, 893, 216, 216, 216, 216, 287, 287, 216, 157, 157, 157, 157, 157, 157, 14, 81, 1, -18, '', 0, 22, 17, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 51, 51, 3712, 3968, 1530, 910, 222, 222, 222, 222, 290, 290, 222, 172, 172, 172, 172, 172, 172, 14, 83, 1, -18, '', 0, 23, 18, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 51, 51, 3865, 4121, 1581, 927, 228, 228, 228, 228, 293, 293, 228, 187, 187, 187, 187, 187, 187, 15, 84, 1, -18, '', 0, 24, 19, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 51, 51, 4783, 4784, 1683, 935, 234, 234, 234, 234, 306, 306, 234, 177, 177, 177, 177, 177, 177, 14, 83, 1, -18, '', 0, 26, 18, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 51, 51, 4936, 4937, 1734, 952, 240, 240, 240, 240, 309, 309, 240, 192, 192, 192, 192, 192, 192, 15, 84, 1, -18, '', 0, 27, 19, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 51, 51, 5089, 5090, 1785, 969, 246, 246, 246, 246, 312, 312, 246, 207, 207, 207, 207, 207, 207, 15, 85, 1, -18, '', 0, 28, 20, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 51, 51, 5242, 5243, 1836, 986, 252, 252, 252, 252, 315, 315, 252, 222, 222, 222, 222, 222, 222, 15, 87, 1, -18, '', 0, 29, 21, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 51, 51, 5395, 5396, 1887, 1003, 258, 258, 258, 258, 318, 318, 258, 237, 237, 237, 237, 237, 237, 15, 88, 1, -18, '', 0, 30, 22, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 51, 51, 3402, 0, 1045, 1453, 226, 226, 226, 226, 225, 225, 226, 127, 127, 127, 127, 127, 127, 15, 74, 3, -18, '', 0, 28, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 51, 51, 3606, 0, 1071, 1479, 232, 232, 232, 232, 227, 227, 232, 142, 142, 142, 142, 142, 142, 16, 76, 3, -18, '', 0, 30, 0, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 51, 51, 3810, 0, 1096, 1504, 238, 238, 238, 238, 229, 229, 238, 157, 157, 157, 157, 157, 157, 16, 79, 3, -18, '', 0, 32, 0, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 51, 51, 4014, 0, 1122, 1530, 244, 244, 244, 244, 231, 231, 244, 172, 172, 172, 172, 172, 172, 17, 82, 3, -18, '', 0, 34, 0, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 51, 51, 4218, 0, 1147, 1555, 250, 250, 250, 250, 233, 233, 250, 187, 187, 187, 187, 187, 187, 17, 84, 3, -18, '', 0, 36, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 51, 51, 4932, 0, 1300, 1606, 256, 256, 256, 256, 245, 245, 256, 177, 177, 177, 177, 177, 177, 17, 80, 3, -18, '', 0, 36, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 51, 51, 5136, 0, 1326, 1632, 262, 262, 262, 262, 247, 247, 262, 192, 192, 192, 192, 192, 192, 17, 83, 3, -18, '', 0, 38, 0, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 51, 51, 5340, 0, 1351, 1657, 268, 268, 268, 268, 249, 249, 268, 207, 207, 207, 207, 207, 207, 18, 85, 3, -18, '', 0, 40, 0, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 51, 51, 5544, 0, 1377, 1683, 274, 274, 274, 274, 251, 251, 274, 222, 222, 222, 222, 222, 222, 18, 88, 3, -18, '', 0, 42, 0, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 51, 51, 5748, 0, 1402, 1708, 280, 280, 280, 280, 253, 253, 280, 237, 237, 237, 237, 237, 237, 19, 91, 3, -18, '', 0, 44, 0, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 51, 51, 2860, 3509, 680, 420, 199, 199, 199, 199, 281, 281, 199, 127, 127, 127, 127, 127, 127, 14, 78, 1, -18, '', 0, 19, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 51, 51, 2962, 3662, 697, 433, 205, 205, 205, 205, 284, 284, 205, 142, 142, 142, 142, 142, 142, 14, 80, 1, -18, '', 0, 20, 16, 1.25, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 51, 51, 3064, 3815, 714, 446, 211, 211, 211, 211, 287, 287, 211, 157, 157, 157, 157, 157, 157, 14, 81, 1, -18, '', 0, 21, 17, 1.25, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 51, 51, 3166, 3968, 731, 459, 217, 217, 217, 217, 290, 290, 217, 172, 172, 172, 172, 172, 172, 14, 83, 1, -18, '', 0, 22, 18, 1.25, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 51, 51, 3268, 4121, 748, 471, 223, 223, 223, 223, 293, 293, 223, 187, 187, 187, 187, 187, 187, 15, 84, 1, -18, '', 0, 23, 19, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 51, 51, 4135, 4784, 833, 471, 229, 229, 229, 229, 306, 306, 229, 177, 177, 177, 177, 177, 177, 14, 83, 1, -18, '', 0, 24, 18, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 51, 51, 4237, 4937, 850, 484, 235, 235, 235, 235, 309, 309, 235, 192, 192, 192, 192, 192, 192, 15, 84, 1, -18, '', 0, 25, 19, 1.25, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 51, 51, 4339, 5090, 867, 497, 241, 241, 241, 241, 312, 312, 241, 207, 207, 207, 207, 207, 207, 15, 85, 1, -18, '', 0, 26, 20, 1.25, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 51, 51, 4441, 5243, 884, 510, 247, 247, 247, 247, 315, 315, 247, 222, 222, 222, 222, 222, 222, 15, 87, 1, -18, '', 0, 27, 21, 1.25, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 51, 51, 4543, 5396, 901, 522, 253, 253, 253, 253, 318, 318, 253, 237, 237, 237, 237, 237, 237, 15, 88, 1, -18, '', 0, 28, 22, 1.25, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 52, 52, 4098, 0, 1794, 1456, 232, 232, 232, 232, 228, 228, 232, 130, 130, 130, 130, 130, 130, 16, 81, 3, -18, '', 0, 30, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 52, 52, 4358, 0, 1872, 1482, 238, 238, 238, 238, 230, 230, 238, 146, 146, 146, 146, 146, 146, 16, 84, 3, -18, '', 0, 32, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 52, 52, 4618, 0, 1950, 1508, 244, 244, 244, 244, 232, 232, 244, 162, 162, 162, 162, 162, 162, 17, 87, 3, -18, '', 0, 34, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 52, 52, 4878, 0, 2028, 1534, 250, 250, 250, 250, 234, 234, 250, 178, 178, 178, 178, 178, 178, 17, 89, 3, -18, '', 0, 36, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 52, 52, 5138, 0, 2106, 1560, 256, 256, 256, 256, 236, 236, 256, 194, 194, 194, 194, 194, 194, 18, 92, 3, -18, '', 0, 38, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 52, 52, 5918, 0, 2210, 1586, 263, 263, 263, 263, 248, 248, 263, 180, 180, 180, 180, 180, 180, 17, 88, 3, -18, '', 0, 40, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 52, 52, 6178, 0, 2288, 1612, 269, 269, 269, 269, 250, 250, 269, 196, 196, 196, 196, 196, 196, 17, 91, 3, -18, '', 0, 42, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 52, 52, 6438, 0, 2366, 1638, 275, 275, 275, 275, 252, 252, 275, 212, 212, 212, 212, 212, 212, 18, 94, 3, -18, '', 0, 44, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 52, 52, 6698, 0, 2444, 1664, 281, 281, 281, 281, 254, 254, 281, 228, 228, 228, 228, 228, 228, 18, 96, 3, -18, '', 0, 46, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 52, 52, 6958, 0, 2522, 1690, 287, 287, 287, 287, 256, 256, 287, 244, 244, 244, 244, 244, 244, 19, 99, 3, -18, '', 0, 48, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 52, 52, 3380, 3619, 1404, 875, 209, 209, 209, 209, 285, 285, 209, 130, 130, 130, 130, 130, 130, 14, 80, 1, -18, '', 0, 21, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 52, 52, 3536, 3775, 1456, 892, 215, 215, 215, 215, 288, 288, 215, 146, 146, 146, 146, 146, 146, 14, 81, 1, -18, '', 0, 22, 16, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 52, 52, 3692, 3931, 1508, 910, 221, 221, 221, 221, 291, 291, 221, 162, 162, 162, 162, 162, 162, 14, 83, 1, -18, '', 0, 23, 17, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 52, 52, 3848, 4087, 1560, 927, 227, 227, 227, 227, 294, 294, 227, 178, 178, 178, 178, 178, 178, 15, 84, 1, -18, '', 0, 24, 18, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 52, 52, 4004, 4243, 1612, 944, 233, 233, 233, 233, 297, 297, 233, 194, 194, 194, 194, 194, 194, 15, 85, 1, -18, '', 0, 25, 19, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 52, 52, 4940, 4919, 1716, 953, 240, 240, 240, 240, 310, 310, 240, 180, 180, 180, 180, 180, 180, 15, 84, 1, -18, '', 0, 27, 18, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 52, 52, 5096, 5075, 1768, 970, 246, 246, 246, 246, 313, 313, 246, 196, 196, 196, 196, 196, 196, 15, 85, 1, -18, '', 0, 28, 19, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 52, 52, 5252, 5231, 1820, 988, 252, 252, 252, 252, 316, 316, 252, 212, 212, 212, 212, 212, 212, 15, 87, 1, -18, '', 0, 29, 20, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 52, 52, 5408, 5387, 1872, 1005, 258, 258, 258, 258, 319, 319, 258, 228, 228, 228, 228, 228, 228, 15, 88, 1, -18, '', 0, 30, 21, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 52, 52, 5564, 5543, 1924, 1022, 264, 264, 264, 264, 322, 322, 264, 244, 244, 244, 244, 244, 244, 16, 90, 1, -18, '', 0, 31, 22, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 52, 52, 3535, 0, 1066, 1482, 232, 232, 232, 232, 228, 228, 232, 130, 130, 130, 130, 130, 130, 16, 75, 3, -18, '', 0, 28, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 52, 52, 3743, 0, 1092, 1508, 238, 238, 238, 238, 230, 230, 238, 146, 146, 146, 146, 146, 146, 16, 78, 3, -18, '', 0, 30, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 52, 52, 3951, 0, 1118, 1534, 244, 244, 244, 244, 232, 232, 244, 162, 162, 162, 162, 162, 162, 17, 80, 3, -18, '', 0, 32, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 52, 52, 4159, 0, 1144, 1560, 250, 250, 250, 250, 234, 234, 250, 178, 178, 178, 178, 178, 178, 17, 83, 3, -18, '', 0, 34, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 52, 52, 4367, 0, 1170, 1586, 256, 256, 256, 256, 236, 236, 256, 194, 194, 194, 194, 194, 194, 18, 85, 3, -18, '', 0, 36, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 52, 52, 5095, 0, 1326, 1638, 263, 263, 263, 263, 248, 248, 263, 180, 180, 180, 180, 180, 180, 17, 82, 3, -18, '', 0, 36, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 52, 52, 5303, 0, 1352, 1664, 269, 269, 269, 269, 250, 250, 269, 196, 196, 196, 196, 196, 196, 17, 84, 3, -18, '', 0, 38, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 52, 52, 5511, 0, 1378, 1690, 275, 275, 275, 275, 252, 252, 275, 212, 212, 212, 212, 212, 212, 18, 87, 3, -18, '', 0, 40, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 52, 52, 5719, 0, 1404, 1716, 281, 281, 281, 281, 254, 254, 281, 228, 228, 228, 228, 228, 228, 18, 89, 3, -18, '', 0, 42, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 52, 52, 5927, 0, 1430, 1742, 287, 287, 287, 287, 256, 256, 287, 244, 244, 244, 244, 244, 244, 19, 92, 3, -18, '', 0, 44, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 52, 52, 2976, 3619, 693, 429, 204, 204, 204, 204, 285, 285, 204, 130, 130, 130, 130, 130, 130, 14, 80, 1, -18, '', 0, 20, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 52, 52, 3080, 3775, 710, 442, 210, 210, 210, 210, 288, 288, 210, 146, 146, 146, 146, 146, 146, 14, 81, 1, -18, '', 0, 21, 16, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 52, 52, 3184, 3931, 728, 455, 216, 216, 216, 216, 291, 291, 216, 162, 162, 162, 162, 162, 162, 14, 83, 1, -18, '', 0, 22, 17, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 52, 52, 3288, 4087, 745, 468, 222, 222, 222, 222, 294, 294, 222, 178, 178, 178, 178, 178, 178, 15, 84, 1, -18, '', 0, 23, 18, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 52, 52, 3392, 4243, 762, 481, 228, 228, 228, 228, 297, 297, 228, 194, 194, 194, 194, 194, 194, 15, 85, 1, -18, '', 0, 24, 19, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 52, 52, 4276, 4919, 849, 481, 235, 235, 235, 235, 310, 310, 235, 180, 180, 180, 180, 180, 180, 15, 84, 1, -18, '', 0, 25, 18, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 52, 52, 4380, 5075, 866, 494, 241, 241, 241, 241, 313, 313, 241, 196, 196, 196, 196, 196, 196, 15, 85, 1, -18, '', 0, 26, 19, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 52, 52, 4484, 5231, 884, 507, 247, 247, 247, 247, 316, 316, 247, 212, 212, 212, 212, 212, 212, 15, 87, 1, -18, '', 0, 27, 20, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 52, 52, 4588, 5387, 901, 520, 253, 253, 253, 253, 319, 319, 253, 228, 228, 228, 228, 228, 228, 15, 88, 1, -18, '', 0, 28, 21, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 52, 52, 4692, 5543, 918, 533, 259, 259, 259, 259, 322, 322, 259, 244, 244, 244, 244, 244, 244, 16, 90, 1, -18, '', 0, 29, 22, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 53, 53, 4256, 0, 1829, 1484, 237, 237, 237, 237, 231, 231, 237, 132, 132, 132, 132, 132, 132, 16, 82, 3, -18, '', 0, 30, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 53, 53, 4521, 0, 1908, 1511, 243, 243, 243, 243, 233, 233, 243, 148, 148, 148, 148, 148, 148, 16, 85, 3, -18, '', 0, 32, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 53, 53, 4786, 0, 1988, 1537, 249, 249, 249, 249, 235, 235, 249, 164, 164, 164, 164, 164, 164, 17, 88, 3, -18, '', 0, 34, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 53, 53, 5051, 0, 2067, 1564, 255, 255, 255, 255, 237, 237, 255, 180, 180, 180, 180, 180, 180, 17, 91, 3, -18, '', 0, 36, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 53, 53, 5316, 0, 2147, 1590, 261, 261, 261, 261, 239, 239, 261, 196, 196, 196, 196, 196, 196, 18, 94, 3, -18, '', 0, 38, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 53, 53, 6111, 0, 2253, 1616, 268, 268, 268, 268, 251, 251, 268, 184, 184, 184, 184, 184, 184, 17, 89, 3, -18, '', 0, 40, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 53, 53, 6376, 0, 2332, 1643, 274, 274, 274, 274, 253, 253, 274, 200, 200, 200, 200, 200, 200, 18, 92, 3, -18, '', 0, 42, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 53, 53, 6641, 0, 2412, 1669, 280, 280, 280, 280, 255, 255, 280, 216, 216, 216, 216, 216, 216, 18, 95, 3, -18, '', 0, 44, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 53, 53, 6906, 0, 2491, 1696, 286, 286, 286, 286, 257, 257, 286, 232, 232, 232, 232, 232, 232, 19, 98, 3, -18, '', 0, 46, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 53, 53, 7171, 0, 2571, 1722, 292, 292, 292, 292, 259, 259, 292, 248, 248, 248, 248, 248, 248, 19, 101, 3, -18, '', 0, 48, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 53, 53, 3510, 3731, 1431, 892, 214, 214, 214, 214, 288, 288, 214, 132, 132, 132, 132, 132, 132, 14, 81, 1, -18, '', 0, 21, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 53, 53, 3669, 3890, 1484, 910, 220, 220, 220, 220, 291, 291, 220, 148, 148, 148, 148, 148, 148, 14, 83, 1, -18, '', 0, 22, 16, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 53, 53, 3828, 4049, 1537, 928, 226, 226, 226, 226, 294, 294, 226, 164, 164, 164, 164, 164, 164, 15, 84, 1, -18, '', 0, 23, 17, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 53, 53, 3987, 4208, 1590, 945, 232, 232, 232, 232, 297, 297, 232, 180, 180, 180, 180, 180, 180, 15, 85, 1, -18, '', 0, 24, 18, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 53, 53, 4146, 4367, 1643, 963, 238, 238, 238, 238, 300, 300, 238, 196, 196, 196, 196, 196, 196, 15, 87, 1, -18, '', 0, 25, 19, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 53, 53, 5100, 5056, 1749, 971, 245, 245, 245, 245, 313, 313, 245, 184, 184, 184, 184, 184, 184, 15, 85, 1, -18, '', 0, 27, 18, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 53, 53, 5259, 5215, 1802, 989, 251, 251, 251, 251, 316, 316, 251, 200, 200, 200, 200, 200, 200, 15, 87, 1, -18, '', 0, 28, 19, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 53, 53, 5418, 5374, 1855, 1007, 257, 257, 257, 257, 319, 319, 257, 216, 216, 216, 216, 216, 216, 15, 88, 1, -18, '', 0, 29, 20, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 53, 53, 5577, 5533, 1908, 1024, 263, 263, 263, 263, 322, 322, 263, 232, 232, 232, 232, 232, 232, 16, 90, 1, -18, '', 0, 30, 21, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 53, 53, 5736, 5692, 1961, 1042, 269, 269, 269, 269, 325, 325, 269, 248, 248, 248, 248, 248, 248, 16, 91, 1, -18, '', 0, 31, 22, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 53, 53, 3672, 0, 1086, 1510, 237, 237, 237, 237, 231, 231, 237, 132, 132, 132, 132, 132, 132, 16, 76, 3, -18, '', 0, 28, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 53, 53, 3884, 0, 1113, 1537, 243, 243, 243, 243, 233, 233, 243, 148, 148, 148, 148, 148, 148, 16, 79, 3, -18, '', 0, 30, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 53, 53, 4096, 0, 1139, 1563, 249, 249, 249, 249, 235, 235, 249, 164, 164, 164, 164, 164, 164, 17, 82, 3, -18, '', 0, 32, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 53, 53, 4308, 0, 1166, 1590, 255, 255, 255, 255, 237, 237, 255, 180, 180, 180, 180, 180, 180, 17, 84, 3, -18, '', 0, 34, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 53, 53, 4520, 0, 1192, 1616, 261, 261, 261, 261, 239, 239, 261, 196, 196, 196, 196, 196, 196, 18, 87, 3, -18, '', 0, 36, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 53, 53, 5262, 0, 1351, 1669, 268, 268, 268, 268, 251, 251, 268, 184, 184, 184, 184, 184, 184, 17, 83, 3, -18, '', 0, 36, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 53, 53, 5474, 0, 1378, 1696, 274, 274, 274, 274, 253, 253, 274, 200, 200, 200, 200, 200, 200, 18, 85, 3, -18, '', 0, 38, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 53, 53, 5686, 0, 1404, 1722, 280, 280, 280, 280, 255, 255, 280, 216, 216, 216, 216, 216, 216, 18, 88, 3, -18, '', 0, 40, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 53, 53, 5898, 0, 1431, 1749, 286, 286, 286, 286, 257, 257, 286, 232, 232, 232, 232, 232, 232, 19, 91, 3, -18, '', 0, 42, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 53, 53, 6110, 0, 1457, 1775, 292, 292, 292, 292, 259, 259, 292, 248, 248, 248, 248, 248, 248, 19, 93, 3, -18, '', 0, 44, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 53, 53, 3095, 3731, 706, 437, 209, 209, 209, 209, 288, 288, 209, 132, 132, 132, 132, 132, 132, 14, 81, 1, -18, '', 0, 20, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 53, 53, 3201, 3890, 724, 450, 215, 215, 215, 215, 291, 291, 215, 148, 148, 148, 148, 148, 148, 14, 83, 1, -18, '', 0, 21, 16, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 53, 53, 3307, 4049, 742, 463, 221, 221, 221, 221, 294, 294, 221, 164, 164, 164, 164, 164, 164, 15, 84, 1, -18, '', 0, 22, 17, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 53, 53, 3413, 4208, 759, 477, 227, 227, 227, 227, 297, 297, 227, 180, 180, 180, 180, 180, 180, 15, 85, 1, -18, '', 0, 23, 18, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 53, 53, 3519, 4367, 777, 490, 233, 233, 233, 233, 300, 300, 233, 196, 196, 196, 196, 196, 196, 15, 87, 1, -18, '', 0, 24, 19, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 53, 53, 4420, 5056, 865, 490, 240, 240, 240, 240, 313, 313, 240, 184, 184, 184, 184, 184, 184, 15, 85, 1, -18, '', 0, 25, 18, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 53, 53, 4526, 5215, 883, 503, 246, 246, 246, 246, 316, 316, 246, 200, 200, 200, 200, 200, 200, 15, 87, 1, -18, '', 0, 26, 19, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 53, 53, 4632, 5374, 901, 516, 252, 252, 252, 252, 319, 319, 252, 216, 216, 216, 216, 216, 216, 15, 88, 1, -18, '', 0, 27, 20, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 53, 53, 4738, 5533, 918, 530, 258, 258, 258, 258, 322, 322, 258, 232, 232, 232, 232, 232, 232, 16, 90, 1, -18, '', 0, 28, 21, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 53, 53, 4844, 5692, 936, 543, 264, 264, 264, 264, 325, 325, 264, 248, 248, 248, 248, 248, 248, 16, 91, 1, -18, '', 0, 29, 22, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 54, 54, 4419, 0, 1863, 1512, 243, 243, 243, 243, 234, 234, 243, 134, 134, 134, 134, 134, 134, 16, 84, 3, -18, '', 0, 31, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 54, 54, 4689, 0, 1944, 1539, 249, 249, 249, 249, 236, 236, 249, 150, 150, 150, 150, 150, 150, 17, 87, 3, -18, '', 0, 33, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 54, 54, 4959, 0, 2025, 1566, 255, 255, 255, 255, 238, 238, 255, 166, 166, 166, 166, 166, 166, 17, 89, 3, -18, '', 0, 35, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 54, 54, 5229, 0, 2106, 1593, 261, 261, 261, 261, 240, 240, 261, 182, 182, 182, 182, 182, 182, 18, 92, 3, -18, '', 0, 37, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 54, 54, 5499, 0, 2187, 1620, 267, 267, 267, 267, 242, 242, 267, 198, 198, 198, 198, 198, 198, 18, 95, 3, -18, '', 0, 39, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 54, 54, 6309, 0, 2295, 1647, 275, 275, 275, 275, 254, 254, 275, 186, 186, 186, 186, 186, 186, 17, 91, 3, -18, '', 0, 41, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 54, 54, 6579, 0, 2376, 1674, 281, 281, 281, 281, 256, 256, 281, 202, 202, 202, 202, 202, 202, 18, 94, 3, -18, '', 0, 43, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 54, 54, 6849, 0, 2457, 1701, 287, 287, 287, 287, 258, 258, 287, 218, 218, 218, 218, 218, 218, 18, 96, 3, -18, '', 0, 45, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 54, 54, 7119, 0, 2538, 1728, 293, 293, 293, 293, 260, 260, 293, 234, 234, 234, 234, 234, 234, 19, 99, 3, -18, '', 0, 47, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 54, 54, 7389, 0, 2619, 1755, 299, 299, 299, 299, 262, 262, 299, 250, 250, 250, 250, 250, 250, 19, 102, 3, -18, '', 0, 49, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 54, 54, 3644, 3845, 1458, 909, 220, 220, 220, 220, 292, 292, 220, 134, 134, 134, 134, 134, 134, 14, 83, 1, -18, '', 0, 21, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 54, 54, 3806, 4007, 1512, 927, 226, 226, 226, 226, 295, 295, 226, 150, 150, 150, 150, 150, 150, 15, 84, 1, -18, '', 0, 22, 16, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 54, 54, 3968, 4169, 1566, 945, 232, 232, 232, 232, 298, 298, 232, 166, 166, 166, 166, 166, 166, 15, 85, 1, -18, '', 0, 23, 17, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 54, 54, 4130, 4331, 1620, 963, 238, 238, 238, 238, 301, 301, 238, 182, 182, 182, 182, 182, 182, 15, 87, 1, -18, '', 0, 24, 18, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 54, 54, 4292, 4493, 1674, 981, 244, 244, 244, 244, 304, 304, 244, 198, 198, 198, 198, 198, 198, 15, 88, 1, -18, '', 0, 25, 19, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 54, 54, 5264, 5195, 1782, 990, 252, 252, 252, 252, 317, 317, 252, 186, 186, 186, 186, 186, 186, 15, 87, 1, -18, '', 0, 27, 18, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 54, 54, 5426, 5357, 1836, 1008, 258, 258, 258, 258, 320, 320, 258, 202, 202, 202, 202, 202, 202, 15, 88, 1, -18, '', 0, 28, 19, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 54, 54, 5588, 5519, 1890, 1026, 264, 264, 264, 264, 323, 323, 264, 218, 218, 218, 218, 218, 218, 16, 90, 1, -18, '', 0, 29, 20, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 54, 54, 5750, 5681, 1944, 1044, 270, 270, 270, 270, 326, 326, 270, 234, 234, 234, 234, 234, 234, 16, 91, 1, -18, '', 0, 30, 21, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 54, 54, 5912, 5843, 1998, 1062, 276, 276, 276, 276, 329, 329, 276, 250, 250, 250, 250, 250, 250, 16, 92, 1, -18, '', 0, 31, 22, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 54, 54, 3814, 0, 1107, 1539, 243, 243, 243, 243, 234, 234, 243, 134, 134, 134, 134, 134, 134, 16, 78, 3, -18, '', 0, 29, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 54, 54, 4030, 0, 1134, 1566, 249, 249, 249, 249, 236, 236, 249, 150, 150, 150, 150, 150, 150, 17, 80, 3, -18, '', 0, 31, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 54, 54, 4246, 0, 1161, 1593, 255, 255, 255, 255, 238, 238, 255, 166, 166, 166, 166, 166, 166, 17, 83, 3, -18, '', 0, 33, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 54, 54, 4462, 0, 1188, 1620, 261, 261, 261, 261, 240, 240, 261, 182, 182, 182, 182, 182, 182, 18, 85, 3, -18, '', 0, 35, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 54, 54, 4678, 0, 1215, 1647, 267, 267, 267, 267, 242, 242, 267, 198, 198, 198, 198, 198, 198, 18, 88, 3, -18, '', 0, 37, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 54, 54, 5434, 0, 1377, 1701, 275, 275, 275, 275, 254, 254, 275, 186, 186, 186, 186, 186, 186, 17, 84, 3, -18, '', 0, 37, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 54, 54, 5650, 0, 1404, 1728, 281, 281, 281, 281, 256, 256, 281, 202, 202, 202, 202, 202, 202, 18, 87, 3, -18, '', 0, 39, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 54, 54, 5866, 0, 1431, 1755, 287, 287, 287, 287, 258, 258, 287, 218, 218, 218, 218, 218, 218, 18, 89, 3, -18, '', 0, 41, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 54, 54, 6082, 0, 1458, 1782, 293, 293, 293, 293, 260, 260, 293, 234, 234, 234, 234, 234, 234, 19, 92, 3, -18, '', 0, 43, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 54, 54, 6298, 0, 1485, 1809, 299, 299, 299, 299, 262, 262, 299, 250, 250, 250, 250, 250, 250, 19, 95, 3, -18, '', 0, 45, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 54, 54, 3217, 3845, 720, 445, 214, 214, 214, 214, 292, 292, 214, 134, 134, 134, 134, 134, 134, 14, 83, 1, -18, '', 0, 20, 15, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 54, 54, 3325, 4007, 738, 459, 220, 220, 220, 220, 295, 295, 220, 150, 150, 150, 150, 150, 150, 15, 84, 1, -18, '', 0, 21, 16, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 54, 54, 3433, 4169, 756, 472, 226, 226, 226, 226, 298, 298, 226, 166, 166, 166, 166, 166, 166, 15, 85, 1, -18, '', 0, 22, 17, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 54, 54, 3541, 4331, 774, 486, 232, 232, 232, 232, 301, 301, 232, 182, 182, 182, 182, 182, 182, 15, 87, 1, -18, '', 0, 23, 18, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 54, 54, 3649, 4493, 792, 499, 238, 238, 238, 238, 304, 304, 238, 198, 198, 198, 198, 198, 198, 15, 88, 1, -18, '', 0, 24, 19, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 54, 54, 4567, 5195, 882, 499, 246, 246, 246, 246, 317, 317, 246, 186, 186, 186, 186, 186, 186, 15, 87, 1, -18, '', 0, 25, 18, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 54, 54, 4675, 5357, 900, 513, 252, 252, 252, 252, 320, 320, 252, 202, 202, 202, 202, 202, 202, 15, 88, 1, -18, '', 0, 26, 19, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 54, 54, 4783, 5519, 918, 526, 258, 258, 258, 258, 323, 323, 258, 218, 218, 218, 218, 218, 218, 16, 90, 1, -18, '', 0, 27, 20, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 54, 54, 4891, 5681, 936, 540, 264, 264, 264, 264, 326, 326, 264, 234, 234, 234, 234, 234, 234, 16, 91, 1, -18, '', 0, 28, 21, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 54, 54, 4999, 5843, 954, 553, 270, 270, 270, 270, 329, 329, 270, 250, 250, 250, 250, 250, 250, 16, 92, 1, -18, '', 0, 29, 22, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 55, 55, 4586, 0, 1898, 1540, 249, 249, 249, 249, 237, 237, 249, 136, 136, 136, 136, 136, 136, 16, 85, 3, -18, '', 0, 31, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 55, 55, 4861, 0, 1980, 1568, 255, 255, 255, 255, 239, 239, 255, 152, 152, 152, 152, 152, 152, 17, 88, 3, -18, '', 0, 33, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 55, 55, 5136, 0, 2063, 1595, 261, 261, 261, 261, 241, 241, 261, 168, 168, 168, 168, 168, 168, 17, 91, 3, -18, '', 0, 35, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 55, 55, 5411, 0, 2145, 1623, 267, 267, 267, 267, 243, 243, 267, 184, 184, 184, 184, 184, 184, 18, 94, 3, -18, '', 0, 37, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 55, 55, 5686, 0, 2228, 1650, 273, 273, 273, 273, 245, 245, 273, 200, 200, 200, 200, 200, 200, 18, 96, 3, -18, '', 0, 39, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 55, 55, 6511, 0, 2338, 1677, 281, 281, 281, 281, 257, 257, 281, 188, 188, 188, 188, 188, 188, 18, 92, 3, -18, '', 0, 41, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 55, 55, 6786, 0, 2420, 1705, 287, 287, 287, 287, 259, 259, 287, 204, 204, 204, 204, 204, 204, 18, 95, 3, -18, '', 0, 43, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 55, 55, 7061, 0, 2503, 1732, 293, 293, 293, 293, 261, 261, 293, 220, 220, 220, 220, 220, 220, 19, 98, 3, -18, '', 0, 45, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 55, 55, 7336, 0, 2585, 1760, 299, 299, 299, 299, 263, 263, 299, 236, 236, 236, 236, 236, 236, 19, 101, 3, -18, '', 0, 47, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 55, 55, 7611, 0, 2668, 1787, 305, 305, 305, 305, 265, 265, 305, 252, 252, 252, 252, 252, 252, 20, 103, 3, -18, '', 0, 49, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 55, 55, 3782, 3960, 1485, 926, 225, 225, 225, 225, 295, 295, 225, 136, 136, 136, 136, 136, 136, 15, 84, 1, -18, '', 0, 21, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 55, 55, 3947, 4125, 1540, 944, 231, 231, 231, 231, 298, 298, 231, 152, 152, 152, 152, 152, 152, 15, 85, 1, -18, '', 0, 22, 17, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 55, 55, 4112, 4290, 1595, 963, 237, 237, 237, 237, 301, 301, 237, 168, 168, 168, 168, 168, 168, 15, 87, 1, -18, '', 0, 23, 18, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 55, 55, 4277, 4455, 1650, 981, 243, 243, 243, 243, 304, 304, 243, 184, 184, 184, 184, 184, 184, 15, 88, 1, -18, '', 0, 24, 19, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 55, 55, 4442, 4620, 1705, 999, 249, 249, 249, 249, 307, 307, 249, 200, 200, 200, 200, 200, 200, 16, 90, 1, -18, '', 0, 25, 20, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 55, 55, 5432, 5335, 1815, 1008, 257, 257, 257, 257, 320, 320, 257, 188, 188, 188, 188, 188, 188, 15, 88, 1, -18, '', 0, 27, 19, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 55, 55, 5597, 5500, 1870, 1026, 263, 263, 263, 263, 323, 323, 263, 204, 204, 204, 204, 204, 204, 16, 90, 1, -18, '', 0, 28, 20, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 55, 55, 5762, 5665, 1925, 1045, 269, 269, 269, 269, 326, 326, 269, 220, 220, 220, 220, 220, 220, 16, 91, 1, -18, '', 0, 29, 21, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 55, 55, 5927, 5830, 1980, 1063, 275, 275, 275, 275, 329, 329, 275, 236, 236, 236, 236, 236, 236, 16, 92, 1, -18, '', 0, 30, 22, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 55, 55, 6092, 5995, 2035, 1081, 281, 281, 281, 281, 332, 332, 281, 252, 252, 252, 252, 252, 252, 16, 94, 1, -18, '', 0, 31, 23, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 55, 55, 3959, 0, 1127, 1567, 249, 249, 249, 249, 237, 237, 249, 136, 136, 136, 136, 136, 136, 16, 79, 3, -18, '', 0, 29, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 55, 55, 4179, 0, 1155, 1595, 255, 255, 255, 255, 239, 239, 255, 152, 152, 152, 152, 152, 152, 17, 82, 3, -18, '', 0, 31, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 55, 55, 4399, 0, 1182, 1622, 261, 261, 261, 261, 241, 241, 261, 168, 168, 168, 168, 168, 168, 17, 84, 3, -18, '', 0, 33, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 55, 55, 4619, 0, 1210, 1650, 267, 267, 267, 267, 243, 243, 267, 184, 184, 184, 184, 184, 184, 18, 87, 3, -18, '', 0, 35, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 55, 55, 4839, 0, 1237, 1677, 273, 273, 273, 273, 245, 245, 273, 200, 200, 200, 200, 200, 200, 18, 89, 3, -18, '', 0, 37, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 55, 55, 5609, 0, 1402, 1732, 281, 281, 281, 281, 257, 257, 281, 188, 188, 188, 188, 188, 188, 18, 85, 3, -18, '', 0, 37, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 55, 55, 5829, 0, 1430, 1760, 287, 287, 287, 287, 259, 259, 287, 204, 204, 204, 204, 204, 204, 18, 88, 3, -18, '', 0, 39, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 55, 55, 6049, 0, 1457, 1787, 293, 293, 293, 293, 261, 261, 293, 220, 220, 220, 220, 220, 220, 19, 91, 3, -18, '', 0, 41, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 55, 55, 6269, 0, 1485, 1815, 299, 299, 299, 299, 263, 263, 299, 236, 236, 236, 236, 236, 236, 19, 93, 3, -18, '', 0, 43, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 55, 55, 6489, 0, 1512, 1842, 305, 305, 305, 305, 265, 265, 305, 252, 252, 252, 252, 252, 252, 20, 96, 3, -18, '', 0, 45, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 55, 55, 3343, 3960, 733, 453, 219, 219, 219, 219, 295, 295, 219, 136, 136, 136, 136, 136, 136, 15, 84, 1, -18, '', 0, 20, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 55, 55, 3453, 4125, 751, 467, 225, 225, 225, 225, 298, 298, 225, 152, 152, 152, 152, 152, 152, 15, 85, 1, -18, '', 0, 21, 17, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 55, 55, 3563, 4290, 770, 481, 231, 231, 231, 231, 301, 301, 231, 168, 168, 168, 168, 168, 168, 15, 87, 1, -18, '', 0, 22, 18, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 55, 55, 3673, 4455, 788, 495, 237, 237, 237, 237, 304, 304, 237, 184, 184, 184, 184, 184, 184, 15, 88, 1, -18, '', 0, 23, 19, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 55, 55, 3783, 4620, 806, 508, 243, 243, 243, 243, 307, 307, 243, 200, 200, 200, 200, 200, 200, 16, 90, 1, -18, '', 0, 24, 20, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 55, 55, 4718, 5335, 898, 508, 251, 251, 251, 251, 320, 320, 251, 188, 188, 188, 188, 188, 188, 15, 88, 1, -18, '', 0, 25, 19, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 55, 55, 4828, 5500, 916, 522, 257, 257, 257, 257, 323, 323, 257, 204, 204, 204, 204, 204, 204, 16, 90, 1, -18, '', 0, 26, 20, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 55, 55, 4938, 5665, 935, 536, 263, 263, 263, 263, 326, 326, 263, 220, 220, 220, 220, 220, 220, 16, 91, 1, -18, '', 0, 27, 21, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 55, 55, 5048, 5830, 953, 550, 269, 269, 269, 269, 329, 329, 269, 236, 236, 236, 236, 236, 236, 16, 92, 1, -18, '', 0, 28, 22, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 55, 55, 5158, 5995, 971, 563, 275, 275, 275, 275, 332, 332, 275, 252, 252, 252, 252, 252, 252, 16, 94, 1, -18, '', 0, 29, 23, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 56, 56, 4758, 0, 1932, 1568, 255, 255, 255, 255, 240, 240, 255, 139, 139, 139, 139, 139, 139, 17, 87, 3, -18, '', 0, 31, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 56, 56, 5038, 0, 2016, 1596, 261, 261, 261, 261, 242, 242, 261, 156, 156, 156, 156, 156, 156, 17, 89, 3, -18, '', 0, 33, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 56, 56, 5318, 0, 2100, 1624, 267, 267, 267, 267, 244, 244, 267, 173, 173, 173, 173, 173, 173, 18, 92, 3, -18, '', 0, 35, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 56, 56, 5598, 0, 2184, 1652, 273, 273, 273, 273, 246, 246, 273, 190, 190, 190, 190, 190, 190, 18, 95, 3, -18, '', 0, 37, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 56, 56, 5878, 0, 2268, 1680, 279, 279, 279, 279, 248, 248, 279, 207, 207, 207, 207, 207, 207, 19, 98, 3, -18, '', 0, 39, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 56, 56, 6718, 0, 2380, 1708, 288, 288, 288, 288, 260, 260, 288, 193, 193, 193, 193, 193, 193, 18, 94, 3, -18, '', 0, 41, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 56, 56, 6998, 0, 2464, 1736, 294, 294, 294, 294, 262, 262, 294, 210, 210, 210, 210, 210, 210, 18, 96, 3, -18, '', 0, 43, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 56, 56, 7278, 0, 2548, 1764, 300, 300, 300, 300, 264, 264, 300, 227, 227, 227, 227, 227, 227, 19, 99, 3, -18, '', 0, 45, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 56, 56, 7558, 0, 2632, 1792, 306, 306, 306, 306, 266, 266, 306, 244, 244, 244, 244, 244, 244, 19, 102, 3, -18, '', 0, 47, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 56, 56, 7838, 0, 2716, 1820, 312, 312, 312, 312, 268, 268, 312, 261, 261, 261, 261, 261, 261, 20, 105, 3, -18, '', 0, 49, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 56, 56, 3924, 4077, 1512, 942, 231, 231, 231, 231, 299, 299, 231, 139, 139, 139, 139, 139, 139, 15, 85, 1, -18, '', 0, 22, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 56, 56, 4092, 4245, 1568, 961, 237, 237, 237, 237, 302, 302, 237, 156, 156, 156, 156, 156, 156, 15, 87, 1, -18, '', 0, 23, 17, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 56, 56, 4260, 4413, 1624, 980, 243, 243, 243, 243, 305, 305, 243, 173, 173, 173, 173, 173, 173, 15, 88, 1, -18, '', 0, 24, 18, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 56, 56, 4428, 4581, 1680, 998, 249, 249, 249, 249, 308, 308, 249, 190, 190, 190, 190, 190, 190, 16, 90, 1, -18, '', 0, 25, 19, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 56, 56, 4596, 4749, 1736, 1017, 255, 255, 255, 255, 311, 311, 255, 207, 207, 207, 207, 207, 207, 16, 91, 1, -18, '', 0, 26, 20, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 56, 56, 5604, 5477, 1848, 1026, 264, 264, 264, 264, 324, 324, 264, 193, 193, 193, 193, 193, 193, 16, 90, 1, -18, '', 0, 28, 19, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 56, 56, 5772, 5645, 1904, 1045, 270, 270, 270, 270, 327, 327, 270, 210, 210, 210, 210, 210, 210, 16, 91, 1, -18, '', 0, 29, 20, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 56, 56, 5940, 5813, 1960, 1064, 276, 276, 276, 276, 330, 330, 276, 227, 227, 227, 227, 227, 227, 16, 92, 1, -18, '', 0, 30, 21, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 56, 56, 6108, 5981, 2016, 1082, 282, 282, 282, 282, 333, 333, 282, 244, 244, 244, 244, 244, 244, 16, 94, 1, -18, '', 0, 31, 22, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 56, 56, 6276, 6149, 2072, 1101, 288, 288, 288, 288, 336, 336, 288, 261, 261, 261, 261, 261, 261, 17, 95, 1, -18, '', 0, 32, 23, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 56, 56, 4109, 0, 1148, 1596, 255, 255, 255, 255, 240, 240, 255, 139, 139, 139, 139, 139, 139, 17, 80, 3, -18, '', 0, 29, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 56, 56, 4333, 0, 1176, 1624, 261, 261, 261, 261, 242, 242, 261, 156, 156, 156, 156, 156, 156, 17, 83, 3, -18, '', 0, 31, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 56, 56, 4557, 0, 1204, 1652, 267, 267, 267, 267, 244, 244, 267, 173, 173, 173, 173, 173, 173, 18, 85, 3, -18, '', 0, 33, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 56, 56, 4781, 0, 1232, 1680, 273, 273, 273, 273, 246, 246, 273, 190, 190, 190, 190, 190, 190, 18, 88, 3, -18, '', 0, 35, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 56, 56, 5005, 0, 1260, 1708, 279, 279, 279, 279, 248, 248, 279, 207, 207, 207, 207, 207, 207, 19, 91, 3, -18, '', 0, 37, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 56, 56, 5789, 0, 1428, 1764, 288, 288, 288, 288, 260, 260, 288, 193, 193, 193, 193, 193, 193, 18, 87, 3, -18, '', 0, 37, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 56, 56, 6013, 0, 1456, 1792, 294, 294, 294, 294, 262, 262, 294, 210, 210, 210, 210, 210, 210, 18, 89, 3, -18, '', 0, 39, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 56, 56, 6237, 0, 1484, 1820, 300, 300, 300, 300, 264, 264, 300, 227, 227, 227, 227, 227, 227, 19, 92, 3, -18, '', 0, 41, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 56, 56, 6461, 0, 1512, 1848, 306, 306, 306, 306, 266, 266, 306, 244, 244, 244, 244, 244, 244, 19, 95, 3, -18, '', 0, 43, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 56, 56, 6685, 0, 1540, 1876, 312, 312, 312, 312, 268, 268, 312, 261, 261, 261, 261, 261, 261, 20, 97, 3, -18, '', 0, 45, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 56, 56, 3473, 4077, 746, 462, 225, 225, 225, 225, 299, 299, 225, 139, 139, 139, 139, 139, 139, 15, 85, 1, -18, '', 0, 21, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 56, 56, 3585, 4245, 765, 476, 231, 231, 231, 231, 302, 302, 231, 156, 156, 156, 156, 156, 156, 15, 87, 1, -18, '', 0, 22, 17, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 56, 56, 3697, 4413, 784, 490, 237, 237, 237, 237, 305, 305, 237, 173, 173, 173, 173, 173, 173, 15, 88, 1, -18, '', 0, 23, 18, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 56, 56, 3809, 4581, 802, 504, 243, 243, 243, 243, 308, 308, 243, 190, 190, 190, 190, 190, 190, 16, 90, 1, -18, '', 0, 24, 19, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 56, 56, 3921, 4749, 821, 518, 249, 249, 249, 249, 311, 311, 249, 207, 207, 207, 207, 207, 207, 16, 91, 1, -18, '', 0, 25, 20, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 56, 56, 4873, 5477, 914, 518, 258, 258, 258, 258, 324, 324, 258, 193, 193, 193, 193, 193, 193, 16, 90, 1, -18, '', 0, 26, 19, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 56, 56, 4985, 5645, 933, 532, 264, 264, 264, 264, 327, 327, 264, 210, 210, 210, 210, 210, 210, 16, 91, 1, -18, '', 0, 27, 20, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 56, 56, 5097, 5813, 952, 546, 270, 270, 270, 270, 330, 330, 270, 227, 227, 227, 227, 227, 227, 16, 92, 1, -18, '', 0, 28, 21, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 56, 56, 5209, 5981, 970, 560, 276, 276, 276, 276, 333, 333, 276, 244, 244, 244, 244, 244, 244, 16, 94, 1, -18, '', 0, 29, 22, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 56, 56, 5321, 6149, 989, 574, 282, 282, 282, 282, 336, 336, 282, 261, 261, 261, 261, 261, 261, 17, 95, 1, -18, '', 0, 30, 23, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 57, 57, 4935, 0, 1967, 1596, 261, 261, 261, 261, 243, 243, 261, 141, 141, 141, 141, 141, 141, 17, 88, 3, -18, '', 0, 32, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 57, 57, 5220, 0, 2052, 1625, 267, 267, 267, 267, 245, 245, 267, 158, 158, 158, 158, 158, 158, 17, 91, 3, -18, '', 0, 34, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 57, 57, 5505, 0, 2138, 1653, 273, 273, 273, 273, 247, 247, 273, 175, 175, 175, 175, 175, 175, 18, 94, 3, -18, '', 0, 36, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 57, 57, 5790, 0, 2223, 1682, 279, 279, 279, 279, 249, 249, 279, 192, 192, 192, 192, 192, 192, 18, 96, 3, -18, '', 0, 38, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 57, 57, 6075, 0, 2309, 1710, 285, 285, 285, 285, 251, 251, 285, 209, 209, 209, 209, 209, 209, 19, 99, 3, -18, '', 0, 40, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 57, 57, 6930, 0, 2423, 1738, 294, 294, 294, 294, 263, 263, 294, 195, 195, 195, 195, 195, 195, 18, 95, 3, -18, '', 0, 42, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 57, 57, 7215, 0, 2508, 1767, 300, 300, 300, 300, 265, 265, 300, 212, 212, 212, 212, 212, 212, 19, 98, 3, -18, '', 0, 44, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 57, 57, 7500, 0, 2594, 1795, 306, 306, 306, 306, 267, 267, 306, 229, 229, 229, 229, 229, 229, 19, 101, 3, -18, '', 0, 46, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 57, 57, 7785, 0, 2679, 1824, 312, 312, 312, 312, 269, 269, 312, 246, 246, 246, 246, 246, 246, 20, 103, 3, -18, '', 0, 48, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 57, 57, 8070, 0, 2765, 1852, 318, 318, 318, 318, 271, 271, 318, 263, 263, 263, 263, 263, 263, 20, 106, 3, -18, '', 0, 50, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 57, 57, 4069, 4195, 1539, 960, 236, 236, 236, 236, 302, 302, 236, 141, 141, 141, 141, 141, 141, 15, 87, 1, -18, '', 0, 22, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 57, 57, 4240, 4366, 1596, 979, 242, 242, 242, 242, 305, 305, 242, 158, 158, 158, 158, 158, 158, 15, 88, 1, -18, '', 0, 23, 17, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 57, 57, 4411, 4537, 1653, 998, 248, 248, 248, 248, 308, 308, 248, 175, 175, 175, 175, 175, 175, 16, 90, 1, -18, '', 0, 24, 18, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 57, 57, 4582, 4708, 1710, 1017, 254, 254, 254, 254, 311, 311, 254, 192, 192, 192, 192, 192, 192, 16, 91, 1, -18, '', 0, 25, 19, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 57, 57, 4753, 4879, 1767, 1036, 260, 260, 260, 260, 314, 314, 260, 209, 209, 209, 209, 209, 209, 16, 92, 1, -18, '', 0, 26, 20, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 57, 57, 5779, 5620, 1881, 1045, 269, 269, 269, 269, 327, 327, 269, 195, 195, 195, 195, 195, 195, 16, 91, 1, -18, '', 0, 28, 19, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 57, 57, 5950, 5791, 1938, 1064, 275, 275, 275, 275, 330, 330, 275, 212, 212, 212, 212, 212, 212, 16, 92, 1, -18, '', 0, 29, 20, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 57, 57, 6121, 5962, 1995, 1083, 281, 281, 281, 281, 333, 333, 281, 229, 229, 229, 229, 229, 229, 16, 94, 1, -18, '', 0, 30, 21, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 57, 57, 6292, 6133, 2052, 1102, 287, 287, 287, 287, 336, 336, 287, 246, 246, 246, 246, 246, 246, 17, 95, 1, -18, '', 0, 31, 22, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 57, 57, 6463, 6304, 2109, 1121, 293, 293, 293, 293, 339, 339, 293, 263, 263, 263, 263, 263, 263, 17, 97, 1, -18, '', 0, 32, 23, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 57, 57, 4262, 0, 1168, 1624, 261, 261, 261, 261, 243, 243, 261, 141, 141, 141, 141, 141, 141, 17, 82, 3, -18, '', 0, 30, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 57, 57, 4490, 0, 1197, 1653, 267, 267, 267, 267, 245, 245, 267, 158, 158, 158, 158, 158, 158, 17, 84, 3, -18, '', 0, 32, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 57, 57, 4718, 0, 1225, 1681, 273, 273, 273, 273, 247, 247, 273, 175, 175, 175, 175, 175, 175, 18, 87, 3, -18, '', 0, 34, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 57, 57, 4946, 0, 1254, 1710, 279, 279, 279, 279, 249, 249, 279, 192, 192, 192, 192, 192, 192, 18, 89, 3, -18, '', 0, 36, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 57, 57, 5174, 0, 1282, 1738, 285, 285, 285, 285, 251, 251, 285, 209, 209, 209, 209, 209, 209, 19, 92, 3, -18, '', 0, 38, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 57, 57, 5972, 0, 1453, 1795, 294, 294, 294, 294, 263, 263, 294, 195, 195, 195, 195, 195, 195, 18, 88, 3, -18, '', 0, 38, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 57, 57, 6200, 0, 1482, 1824, 300, 300, 300, 300, 265, 265, 300, 212, 212, 212, 212, 212, 212, 19, 91, 3, -18, '', 0, 40, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 57, 57, 6428, 0, 1510, 1852, 306, 306, 306, 306, 267, 267, 306, 229, 229, 229, 229, 229, 229, 19, 93, 3, -18, '', 0, 42, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 57, 57, 6656, 0, 1539, 1881, 312, 312, 312, 312, 269, 269, 312, 246, 246, 246, 246, 246, 246, 20, 96, 3, -18, '', 0, 44, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 57, 57, 6884, 0, 1567, 1909, 318, 318, 318, 318, 271, 271, 318, 263, 263, 263, 263, 263, 263, 20, 98, 3, -18, '', 0, 46, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 57, 57, 3606, 4195, 760, 470, 230, 230, 230, 230, 302, 302, 230, 141, 141, 141, 141, 141, 141, 15, 87, 1, -18, '', 0, 21, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 57, 57, 3720, 4366, 779, 484, 236, 236, 236, 236, 305, 305, 236, 158, 158, 158, 158, 158, 158, 15, 88, 1, -18, '', 0, 22, 17, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 57, 57, 3834, 4537, 798, 498, 242, 242, 242, 242, 308, 308, 242, 175, 175, 175, 175, 175, 175, 16, 90, 1, -18, '', 0, 23, 18, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 57, 57, 3948, 4708, 817, 513, 248, 248, 248, 248, 311, 311, 248, 192, 192, 192, 192, 192, 192, 16, 91, 1, -18, '', 0, 24, 19, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 57, 57, 4062, 4879, 836, 527, 254, 254, 254, 254, 314, 314, 254, 209, 209, 209, 209, 209, 209, 16, 92, 1, -18, '', 0, 25, 20, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 57, 57, 5031, 5620, 931, 527, 263, 263, 263, 263, 327, 327, 263, 195, 195, 195, 195, 195, 195, 16, 91, 1, -18, '', 0, 26, 19, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 57, 57, 5145, 5791, 950, 541, 269, 269, 269, 269, 330, 330, 269, 212, 212, 212, 212, 212, 212, 16, 92, 1, -18, '', 0, 27, 20, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 57, 57, 5259, 5962, 969, 555, 275, 275, 275, 275, 333, 333, 275, 229, 229, 229, 229, 229, 229, 16, 94, 1, -18, '', 0, 28, 21, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 57, 57, 5373, 6133, 988, 570, 281, 281, 281, 281, 336, 336, 281, 246, 246, 246, 246, 246, 246, 17, 95, 1, -18, '', 0, 29, 22, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 57, 57, 5487, 6304, 1007, 584, 287, 287, 287, 287, 339, 339, 287, 263, 263, 263, 263, 263, 263, 17, 97, 1, -18, '', 0, 30, 23, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 58, 58, 5117, 0, 2001, 1624, 268, 268, 268, 268, 246, 246, 268, 143, 143, 143, 143, 143, 143, 17, 89, 3, -18, '', 0, 32, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 58, 58, 5407, 0, 2088, 1653, 274, 274, 274, 274, 248, 248, 274, 160, 160, 160, 160, 160, 160, 18, 92, 3, -18, '', 0, 34, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 58, 58, 5697, 0, 2175, 1682, 280, 280, 280, 280, 250, 250, 280, 177, 177, 177, 177, 177, 177, 18, 95, 3, -18, '', 0, 36, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 58, 58, 5987, 0, 2262, 1711, 286, 286, 286, 286, 252, 252, 286, 194, 194, 194, 194, 194, 194, 19, 98, 3, -18, '', 0, 38, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 58, 58, 6277, 0, 2349, 1740, 292, 292, 292, 292, 254, 254, 292, 211, 211, 211, 211, 211, 211, 19, 101, 3, -18, '', 0, 40, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 58, 58, 7147, 0, 2465, 1769, 302, 302, 302, 302, 266, 266, 302, 197, 197, 197, 197, 197, 197, 18, 96, 3, -18, '', 0, 42, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 58, 58, 7437, 0, 2552, 1798, 308, 308, 308, 308, 268, 268, 308, 214, 214, 214, 214, 214, 214, 19, 99, 3, -18, '', 0, 44, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 58, 58, 7727, 0, 2639, 1827, 314, 314, 314, 314, 270, 270, 314, 231, 231, 231, 231, 231, 231, 19, 102, 3, -18, '', 0, 46, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 58, 58, 8017, 0, 2726, 1856, 320, 320, 320, 320, 272, 272, 320, 248, 248, 248, 248, 248, 248, 20, 105, 3, -18, '', 0, 48, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 58, 58, 8307, 0, 2813, 1885, 326, 326, 326, 326, 274, 274, 326, 265, 265, 265, 265, 265, 265, 20, 108, 3, -18, '', 0, 50, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 58, 58, 4219, 4315, 1566, 976, 242, 242, 242, 242, 306, 306, 242, 143, 143, 143, 143, 143, 143, 15, 88, 1, -18, '', 0, 22, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 58, 58, 4393, 4489, 1624, 995, 248, 248, 248, 248, 309, 309, 248, 160, 160, 160, 160, 160, 160, 16, 90, 1, -18, '', 0, 23, 17, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 58, 58, 4567, 4663, 1682, 1015, 254, 254, 254, 254, 312, 312, 254, 177, 177, 177, 177, 177, 177, 16, 91, 1, -18, '', 0, 24, 18, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 58, 58, 4741, 4837, 1740, 1034, 260, 260, 260, 260, 315, 315, 260, 194, 194, 194, 194, 194, 194, 16, 92, 1, -18, '', 0, 25, 19, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 58, 58, 4915, 5011, 1798, 1053, 266, 266, 266, 266, 318, 318, 266, 211, 211, 211, 211, 211, 211, 16, 94, 1, -18, '', 0, 26, 20, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 58, 58, 5959, 5765, 1914, 1063, 276, 276, 276, 276, 331, 331, 276, 197, 197, 197, 197, 197, 197, 16, 92, 1, -18, '', 0, 28, 19, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 58, 58, 6133, 5939, 1972, 1082, 282, 282, 282, 282, 334, 334, 282, 214, 214, 214, 214, 214, 214, 16, 94, 1, -18, '', 0, 29, 20, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 58, 58, 6307, 6113, 2030, 1102, 288, 288, 288, 288, 337, 337, 288, 231, 231, 231, 231, 231, 231, 17, 95, 1, -18, '', 0, 30, 21, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 58, 58, 6481, 6287, 2088, 1121, 294, 294, 294, 294, 340, 340, 294, 248, 248, 248, 248, 248, 248, 17, 97, 1, -18, '', 0, 31, 22, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 58, 58, 6655, 6461, 2146, 1140, 300, 300, 300, 300, 343, 343, 300, 265, 265, 265, 265, 265, 265, 17, 98, 1, -18, '', 0, 32, 23, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 58, 58, 4420, 0, 1189, 1653, 268, 268, 268, 268, 246, 246, 268, 143, 143, 143, 143, 143, 143, 17, 83, 3, -18, '', 0, 30, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 58, 58, 4652, 0, 1218, 1682, 274, 274, 274, 274, 248, 248, 274, 160, 160, 160, 160, 160, 160, 18, 85, 3, -18, '', 0, 32, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 58, 58, 4884, 0, 1247, 1711, 280, 280, 280, 280, 250, 250, 280, 177, 177, 177, 177, 177, 177, 18, 88, 3, -18, '', 0, 34, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 58, 58, 5116, 0, 1276, 1740, 286, 286, 286, 286, 252, 252, 286, 194, 194, 194, 194, 194, 194, 19, 91, 3, -18, '', 0, 36, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 58, 58, 5348, 0, 1305, 1769, 292, 292, 292, 292, 254, 254, 292, 211, 211, 211, 211, 211, 211, 19, 93, 3, -18, '', 0, 38, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 58, 58, 6160, 0, 1479, 1827, 302, 302, 302, 302, 266, 266, 302, 197, 197, 197, 197, 197, 197, 18, 89, 3, -18, '', 0, 38, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 58, 58, 6392, 0, 1508, 1856, 308, 308, 308, 308, 268, 268, 308, 214, 214, 214, 214, 214, 214, 19, 92, 3, -18, '', 0, 40, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 58, 58, 6624, 0, 1537, 1885, 314, 314, 314, 314, 270, 270, 314, 231, 231, 231, 231, 231, 231, 19, 95, 3, -18, '', 0, 42, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 58, 58, 6856, 0, 1566, 1914, 320, 320, 320, 320, 272, 272, 320, 248, 248, 248, 248, 248, 248, 20, 97, 3, -18, '', 0, 44, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 58, 58, 7088, 0, 1595, 1943, 326, 326, 326, 326, 274, 274, 326, 265, 265, 265, 265, 265, 265, 20, 100, 3, -18, '', 0, 46, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 58, 58, 3743, 4315, 773, 478, 236, 236, 236, 236, 306, 306, 236, 143, 143, 143, 143, 143, 143, 15, 88, 1, -18, '', 0, 21, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 58, 58, 3859, 4489, 792, 493, 242, 242, 242, 242, 309, 309, 242, 160, 160, 160, 160, 160, 160, 16, 90, 1, -18, '', 0, 22, 17, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 58, 58, 3975, 4663, 812, 507, 248, 248, 248, 248, 312, 312, 248, 177, 177, 177, 177, 177, 177, 16, 91, 1, -18, '', 0, 23, 18, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 58, 58, 4091, 4837, 831, 522, 254, 254, 254, 254, 315, 315, 254, 194, 194, 194, 194, 194, 194, 16, 92, 1, -18, '', 0, 24, 19, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 58, 58, 4207, 5011, 850, 536, 260, 260, 260, 260, 318, 318, 260, 211, 211, 211, 211, 211, 211, 16, 94, 1, -18, '', 0, 25, 20, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 58, 58, 5193, 5765, 947, 536, 270, 270, 270, 270, 331, 331, 270, 197, 197, 197, 197, 197, 197, 16, 92, 1, -18, '', 0, 26, 19, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 58, 58, 5309, 5939, 966, 551, 276, 276, 276, 276, 334, 334, 276, 214, 214, 214, 214, 214, 214, 16, 94, 1, -18, '', 0, 27, 20, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 58, 58, 5425, 6113, 986, 565, 282, 282, 282, 282, 337, 337, 282, 231, 231, 231, 231, 231, 231, 17, 95, 1, -18, '', 0, 28, 21, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 58, 58, 5541, 6287, 1005, 580, 288, 288, 288, 288, 340, 340, 288, 248, 248, 248, 248, 248, 248, 17, 97, 1, -18, '', 0, 29, 22, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 58, 58, 5657, 6461, 1024, 594, 294, 294, 294, 294, 343, 343, 294, 265, 265, 265, 265, 265, 265, 17, 98, 1, -18, '', 0, 30, 23, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 59, 59, 5303, 0, 2036, 1652, 274, 274, 274, 274, 249, 249, 274, 145, 145, 145, 145, 145, 145, 17, 91, 3, -18, '', 0, 32, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 59, 59, 5598, 0, 2124, 1682, 280, 280, 280, 280, 251, 251, 280, 162, 162, 162, 162, 162, 162, 18, 94, 3, -18, '', 0, 34, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 59, 59, 5893, 0, 2213, 1711, 286, 286, 286, 286, 253, 253, 286, 179, 179, 179, 179, 179, 179, 18, 96, 3, -18, '', 0, 36, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 59, 59, 6188, 0, 2301, 1741, 292, 292, 292, 292, 255, 255, 292, 196, 196, 196, 196, 196, 196, 19, 99, 3, -18, '', 0, 38, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 59, 59, 6483, 0, 2390, 1770, 298, 298, 298, 298, 257, 257, 298, 213, 213, 213, 213, 213, 213, 19, 102, 3, -18, '', 0, 40, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 59, 59, 7368, 0, 2508, 1799, 308, 308, 308, 308, 269, 269, 308, 201, 201, 201, 201, 201, 201, 19, 98, 3, -18, '', 0, 42, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 59, 59, 7663, 0, 2596, 1829, 314, 314, 314, 314, 271, 271, 314, 218, 218, 218, 218, 218, 218, 19, 101, 3, -18, '', 0, 44, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 59, 59, 7958, 0, 2685, 1858, 320, 320, 320, 320, 273, 273, 320, 235, 235, 235, 235, 235, 235, 20, 103, 3, -18, '', 0, 46, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 59, 59, 8253, 0, 2773, 1888, 326, 326, 326, 326, 275, 275, 326, 252, 252, 252, 252, 252, 252, 20, 106, 3, -18, '', 0, 48, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 59, 59, 8548, 0, 2862, 1917, 332, 332, 332, 332, 277, 277, 332, 269, 269, 269, 269, 269, 269, 21, 109, 3, -18, '', 0, 50, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 59, 59, 4373, 4437, 1593, 993, 248, 248, 248, 248, 309, 309, 248, 145, 145, 145, 145, 145, 145, 16, 90, 1, -18, '', 0, 22, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 59, 59, 4550, 4614, 1652, 1013, 254, 254, 254, 254, 312, 312, 254, 162, 162, 162, 162, 162, 162, 16, 91, 1, -18, '', 0, 23, 17, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 59, 59, 4727, 4791, 1711, 1033, 260, 260, 260, 260, 315, 315, 260, 179, 179, 179, 179, 179, 179, 16, 92, 1, -18, '', 0, 24, 18, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 59, 59, 4904, 4968, 1770, 1052, 266, 266, 266, 266, 318, 318, 266, 196, 196, 196, 196, 196, 196, 16, 94, 1, -18, '', 0, 25, 19, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 59, 59, 5081, 5145, 1829, 1072, 272, 272, 272, 272, 321, 321, 272, 213, 213, 213, 213, 213, 213, 17, 95, 1, -18, '', 0, 26, 20, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 59, 59, 6143, 5912, 1947, 1081, 282, 282, 282, 282, 334, 334, 282, 201, 201, 201, 201, 201, 201, 16, 94, 1, -18, '', 0, 28, 19, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 59, 59, 6320, 6089, 2006, 1101, 288, 288, 288, 288, 337, 337, 288, 218, 218, 218, 218, 218, 218, 17, 95, 1, -18, '', 0, 29, 20, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 59, 59, 6497, 6266, 2065, 1121, 294, 294, 294, 294, 340, 340, 294, 235, 235, 235, 235, 235, 235, 17, 97, 1, -18, '', 0, 30, 21, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 59, 59, 6674, 6443, 2124, 1140, 300, 300, 300, 300, 343, 343, 300, 252, 252, 252, 252, 252, 252, 17, 98, 1, -18, '', 0, 31, 22, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 59, 59, 6851, 6620, 2183, 1160, 306, 306, 306, 306, 346, 346, 306, 269, 269, 269, 269, 269, 269, 17, 99, 1, -18, '', 0, 32, 23, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 59, 59, 4583, 0, 1209, 1681, 274, 274, 274, 274, 249, 249, 274, 145, 145, 145, 145, 145, 145, 17, 84, 3, -18, '', 0, 30, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 59, 59, 4819, 0, 1239, 1711, 280, 280, 280, 280, 251, 251, 280, 162, 162, 162, 162, 162, 162, 18, 87, 3, -18, '', 0, 32, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 59, 59, 5055, 0, 1268, 1740, 286, 286, 286, 286, 253, 253, 286, 179, 179, 179, 179, 179, 179, 18, 89, 3, -18, '', 0, 34, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 59, 59, 5291, 0, 1298, 1770, 292, 292, 292, 292, 255, 255, 292, 196, 196, 196, 196, 196, 196, 19, 92, 3, -18, '', 0, 36, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 59, 59, 5527, 0, 1327, 1799, 298, 298, 298, 298, 257, 257, 298, 213, 213, 213, 213, 213, 213, 19, 95, 3, -18, '', 0, 38, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 59, 59, 6353, 0, 1504, 1858, 308, 308, 308, 308, 269, 269, 308, 201, 201, 201, 201, 201, 201, 19, 91, 3, -18, '', 0, 38, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 59, 59, 6589, 0, 1534, 1888, 314, 314, 314, 314, 271, 271, 314, 218, 218, 218, 218, 218, 218, 19, 93, 3, -18, '', 0, 40, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 59, 59, 6825, 0, 1563, 1917, 320, 320, 320, 320, 273, 273, 320, 235, 235, 235, 235, 235, 235, 20, 96, 3, -18, '', 0, 42, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 59, 59, 7061, 0, 1593, 1947, 326, 326, 326, 326, 275, 275, 326, 252, 252, 252, 252, 252, 252, 20, 98, 3, -18, '', 0, 44, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 59, 59, 7297, 0, 1622, 1976, 332, 332, 332, 332, 277, 277, 332, 269, 269, 269, 269, 269, 269, 21, 101, 3, -18, '', 0, 46, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 59, 59, 3884, 4437, 786, 486, 241, 241, 241, 241, 309, 309, 241, 145, 145, 145, 145, 145, 145, 16, 90, 1, -18, '', 0, 21, 16, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 59, 59, 4002, 4614, 806, 501, 247, 247, 247, 247, 312, 312, 247, 162, 162, 162, 162, 162, 162, 16, 91, 1, -18, '', 0, 22, 17, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 59, 59, 4120, 4791, 826, 516, 253, 253, 253, 253, 315, 315, 253, 179, 179, 179, 179, 179, 179, 16, 92, 1, -18, '', 0, 23, 18, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 59, 59, 4238, 4968, 845, 531, 259, 259, 259, 259, 318, 318, 259, 196, 196, 196, 196, 196, 196, 16, 94, 1, -18, '', 0, 24, 19, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 59, 59, 4356, 5145, 865, 545, 265, 265, 265, 265, 321, 321, 265, 213, 213, 213, 213, 213, 213, 17, 95, 1, -18, '', 0, 25, 20, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 59, 59, 5359, 5912, 963, 545, 275, 275, 275, 275, 334, 334, 275, 201, 201, 201, 201, 201, 201, 16, 94, 1, -18, '', 0, 26, 19, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 59, 59, 5477, 6089, 983, 560, 281, 281, 281, 281, 337, 337, 281, 218, 218, 218, 218, 218, 218, 17, 95, 1, -18, '', 0, 27, 20, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 59, 59, 5595, 6266, 1003, 575, 287, 287, 287, 287, 340, 340, 287, 235, 235, 235, 235, 235, 235, 17, 97, 1, -18, '', 0, 28, 21, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 59, 59, 5713, 6443, 1022, 590, 293, 293, 293, 293, 343, 343, 293, 252, 252, 252, 252, 252, 252, 17, 98, 1, -18, '', 0, 29, 22, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 59, 59, 5831, 6620, 1042, 604, 299, 299, 299, 299, 346, 346, 299, 269, 269, 269, 269, 269, 269, 17, 99, 1, -18, '', 0, 30, 23, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 60, 60, 5495, 0, 2070, 1680, 282, 282, 282, 282, 252, 252, 282, 148, 148, 148, 148, 148, 148, 18, 92, 3, -18, '', 0, 33, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 60, 60, 5795, 0, 2160, 1710, 289, 289, 289, 289, 254, 254, 289, 166, 166, 166, 166, 166, 166, 18, 95, 3, -18, '', 0, 35, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 60, 60, 6095, 0, 2250, 1740, 296, 296, 296, 296, 256, 256, 296, 184, 184, 184, 184, 184, 184, 19, 98, 3, -18, '', 0, 37, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 60, 60, 6395, 0, 2340, 1770, 303, 303, 303, 303, 258, 258, 303, 202, 202, 202, 202, 202, 202, 19, 101, 3, -18, '', 0, 39, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 60, 60, 6695, 0, 2430, 1800, 310, 310, 310, 310, 260, 260, 310, 220, 220, 220, 220, 220, 220, 20, 103, 3, -18, '', 0, 41, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 60, 60, 7595, 0, 2550, 1830, 317, 317, 317, 317, 272, 272, 317, 204, 204, 204, 204, 204, 204, 19, 99, 3, -18, '', 0, 43, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 60, 60, 7895, 0, 2640, 1860, 324, 324, 324, 324, 274, 274, 324, 222, 222, 222, 222, 222, 222, 19, 102, 3, -18, '', 0, 45, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 60, 60, 8195, 0, 2730, 1890, 331, 331, 331, 331, 276, 276, 331, 240, 240, 240, 240, 240, 240, 20, 105, 3, -18, '', 0, 47, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 60, 60, 8495, 0, 2820, 1920, 338, 338, 338, 338, 278, 278, 338, 258, 258, 258, 258, 258, 258, 20, 108, 3, -18, '', 0, 49, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 60, 60, 8795, 0, 2910, 1950, 345, 345, 345, 345, 280, 280, 345, 276, 276, 276, 276, 276, 276, 21, 110, 3, -18, '', 0, 51, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 60, 60, 4531, 4560, 1620, 1010, 255, 255, 255, 255, 313, 313, 255, 148, 148, 148, 148, 148, 148, 16, 91, 1, -18, '', 0, 23, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 60, 60, 4711, 4740, 1680, 1030, 262, 262, 262, 262, 316, 316, 262, 166, 166, 166, 166, 166, 166, 16, 92, 1, -18, '', 0, 24, 18, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 60, 60, 4891, 4920, 1740, 1050, 269, 269, 269, 269, 319, 319, 269, 184, 184, 184, 184, 184, 184, 16, 94, 1, -18, '', 0, 25, 19, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 60, 60, 5071, 5100, 1800, 1070, 276, 276, 276, 276, 322, 322, 276, 202, 202, 202, 202, 202, 202, 17, 95, 1, -18, '', 0, 26, 20, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 60, 60, 5251, 5280, 1860, 1090, 283, 283, 283, 283, 325, 325, 283, 220, 220, 220, 220, 220, 220, 17, 97, 1, -18, '', 0, 27, 21, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 60, 60, 6331, 6060, 1980, 1100, 290, 290, 290, 290, 338, 338, 290, 204, 204, 204, 204, 204, 204, 17, 95, 1, -18, '', 0, 29, 20, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 60, 60, 6511, 6240, 2040, 1120, 297, 297, 297, 297, 341, 341, 297, 222, 222, 222, 222, 222, 222, 17, 97, 1, -18, '', 0, 30, 21, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 60, 60, 6691, 6420, 2100, 1140, 304, 304, 304, 304, 344, 344, 304, 240, 240, 240, 240, 240, 240, 17, 98, 1, -18, '', 0, 31, 22, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 60, 60, 6871, 6600, 2160, 1160, 311, 311, 311, 311, 347, 347, 311, 258, 258, 258, 258, 258, 258, 17, 99, 1, -18, '', 0, 32, 23, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 60, 60, 7051, 6780, 2220, 1180, 318, 318, 318, 318, 350, 350, 318, 276, 276, 276, 276, 276, 276, 18, 101, 1, -18, '', 0, 33, 24, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 60, 60, 4750, 0, 1230, 1710, 282, 282, 282, 282, 252, 252, 282, 148, 148, 148, 148, 148, 148, 18, 85, 3, -18, '', 0, 31, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 60, 60, 4990, 0, 1260, 1740, 289, 289, 289, 289, 254, 254, 289, 166, 166, 166, 166, 166, 166, 18, 88, 3, -18, '', 0, 33, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 60, 60, 5230, 0, 1290, 1770, 296, 296, 296, 296, 256, 256, 296, 184, 184, 184, 184, 184, 184, 19, 91, 3, -18, '', 0, 35, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 60, 60, 5470, 0, 1320, 1800, 303, 303, 303, 303, 258, 258, 303, 202, 202, 202, 202, 202, 202, 19, 93, 3, -18, '', 0, 37, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 60, 60, 5710, 0, 1350, 1830, 310, 310, 310, 310, 260, 260, 310, 220, 220, 220, 220, 220, 220, 20, 96, 3, -18, '', 0, 39, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 60, 60, 6550, 0, 1530, 1890, 317, 317, 317, 317, 272, 272, 317, 204, 204, 204, 204, 204, 204, 19, 92, 3, -18, '', 0, 39, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 60, 60, 6790, 0, 1560, 1920, 324, 324, 324, 324, 274, 274, 324, 222, 222, 222, 222, 222, 222, 19, 95, 3, -18, '', 0, 41, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 60, 60, 7030, 0, 1590, 1950, 331, 331, 331, 331, 276, 276, 331, 240, 240, 240, 240, 240, 240, 20, 97, 3, -18, '', 0, 43, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 60, 60, 7270, 0, 1620, 1980, 338, 338, 338, 338, 278, 278, 338, 258, 258, 258, 258, 258, 258, 20, 100, 3, -18, '', 0, 45, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 60, 60, 7510, 0, 1650, 2010, 345, 345, 345, 345, 280, 280, 345, 276, 276, 276, 276, 276, 276, 21, 102, 3, -18, '', 0, 47, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 60, 60, 4030, 4560, 800, 495, 248, 248, 248, 248, 313, 313, 248, 148, 148, 148, 148, 148, 148, 16, 91, 1, -18, '', 0, 22, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 60, 60, 4150, 4740, 820, 510, 255, 255, 255, 255, 316, 316, 255, 166, 166, 166, 166, 166, 166, 16, 92, 1, -18, '', 0, 23, 18, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 60, 60, 4270, 4920, 840, 525, 262, 262, 262, 262, 319, 319, 262, 184, 184, 184, 184, 184, 184, 16, 94, 1, -18, '', 0, 24, 19, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 60, 60, 4390, 5100, 860, 540, 269, 269, 269, 269, 322, 322, 269, 202, 202, 202, 202, 202, 202, 17, 95, 1, -18, '', 0, 25, 20, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 60, 60, 4510, 5280, 880, 555, 276, 276, 276, 276, 325, 325, 276, 220, 220, 220, 220, 220, 220, 17, 97, 1, -18, '', 0, 26, 21, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 60, 60, 5530, 6060, 980, 555, 283, 283, 283, 283, 338, 338, 283, 204, 204, 204, 204, 204, 204, 17, 95, 1, -18, '', 0, 27, 20, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 60, 60, 5650, 6240, 1000, 570, 290, 290, 290, 290, 341, 341, 290, 222, 222, 222, 222, 222, 222, 17, 97, 1, -18, '', 0, 28, 21, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 60, 60, 5770, 6420, 1020, 585, 297, 297, 297, 297, 344, 344, 297, 240, 240, 240, 240, 240, 240, 17, 98, 1, -18, '', 0, 29, 22, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 60, 60, 5890, 6600, 1040, 600, 304, 304, 304, 304, 347, 347, 304, 258, 258, 258, 258, 258, 258, 17, 99, 1, -18, '', 0, 30, 23, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 60, 60, 6010, 6780, 1060, 615, 311, 311, 311, 311, 350, 350, 311, 276, 276, 276, 276, 276, 276, 18, 101, 1, -18, '', 0, 31, 24, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 61, 61, 5692, 0, 2105, 1708, 288, 288, 288, 288, 255, 255, 288, 150, 150, 150, 150, 150, 150, 21, 109, 4, -20, '', 0, 33, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 61, 61, 5997, 0, 2196, 1739, 295, 295, 295, 295, 257, 257, 295, 168, 168, 168, 168, 168, 168, 21, 111, 4, -21, '', 0, 35, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 61, 61, 6302, 0, 2288, 1769, 302, 302, 302, 302, 259, 259, 302, 186, 186, 186, 186, 186, 186, 22, 114, 4, -22, '', 0, 37, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 61, 61, 6607, 0, 2379, 1800, 309, 309, 309, 309, 261, 261, 309, 204, 204, 204, 204, 204, 204, 22, 117, 4, -23, '', 0, 39, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 61, 61, 6912, 0, 2471, 1830, 316, 316, 316, 316, 263, 263, 316, 222, 222, 222, 222, 222, 222, 23, 120, 4, -24, '', 0, 41, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 61, 61, 7827, 0, 2593, 1860, 323, 323, 323, 323, 275, 275, 323, 206, 206, 206, 206, 206, 206, 22, 116, 4, -25, '', 0, 43, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 61, 61, 8132, 0, 2684, 1891, 330, 330, 330, 330, 277, 277, 330, 224, 224, 224, 224, 224, 224, 23, 118, 4, -26, '', 0, 45, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 61, 61, 8437, 0, 2776, 1921, 337, 337, 337, 337, 279, 279, 337, 242, 242, 242, 242, 242, 242, 23, 121, 4, -27, '', 0, 47, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 61, 61, 8742, 0, 2867, 1952, 344, 344, 344, 344, 281, 281, 344, 260, 260, 260, 260, 260, 260, 24, 124, 4, -28, '', 0, 49, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 61, 61, 9047, 0, 2959, 1982, 351, 351, 351, 351, 283, 283, 351, 278, 278, 278, 278, 278, 278, 24, 127, 4, -29, '', 0, 51, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 61, 61, 4693, 4685, 1647, 1027, 261, 261, 261, 261, 316, 316, 261, 150, 150, 150, 150, 150, 150, 16, 92, 1, -18, '', 0, 23, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 61, 61, 4876, 4868, 1708, 1047, 268, 268, 268, 268, 319, 319, 268, 168, 168, 168, 168, 168, 168, 16, 94, 1, -18, '', 0, 24, 18, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 61, 61, 5059, 5051, 1769, 1068, 275, 275, 275, 275, 322, 322, 275, 186, 186, 186, 186, 186, 186, 17, 95, 1, -18, '', 0, 25, 19, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 61, 61, 5242, 5234, 1830, 1088, 282, 282, 282, 282, 325, 325, 282, 204, 204, 204, 204, 204, 204, 17, 97, 1, -18, '', 0, 26, 20, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 61, 61, 5425, 5417, 1891, 1108, 289, 289, 289, 289, 328, 328, 289, 222, 222, 222, 222, 222, 222, 17, 98, 1, -18, '', 0, 27, 21, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 61, 61, 6523, 6210, 2013, 1118, 296, 296, 296, 296, 341, 341, 296, 206, 206, 206, 206, 206, 206, 17, 97, 1, -18, '', 0, 29, 20, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 61, 61, 6706, 6393, 2074, 1138, 303, 303, 303, 303, 344, 344, 303, 224, 224, 224, 224, 224, 224, 17, 98, 1, -18, '', 0, 30, 21, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 61, 61, 6889, 6576, 2135, 1159, 310, 310, 310, 310, 347, 347, 310, 242, 242, 242, 242, 242, 242, 17, 99, 1, -18, '', 0, 31, 22, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 61, 61, 7072, 6759, 2196, 1179, 317, 317, 317, 317, 350, 350, 317, 260, 260, 260, 260, 260, 260, 18, 101, 1, -18, '', 0, 32, 23, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 61, 61, 7255, 6942, 2257, 1199, 324, 324, 324, 324, 353, 353, 324, 278, 278, 278, 278, 278, 278, 18, 102, 1, -18, '', 0, 33, 24, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 61, 61, 4921, 0, 1250, 1738, 288, 288, 288, 288, 255, 255, 288, 150, 150, 150, 150, 150, 150, 21, 102, 4, -20, '', 0, 31, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 61, 61, 5165, 0, 1281, 1769, 295, 295, 295, 295, 257, 257, 295, 168, 168, 168, 168, 168, 168, 21, 104, 4, -21, '', 0, 33, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 61, 61, 5409, 0, 1311, 1799, 302, 302, 302, 302, 259, 259, 302, 186, 186, 186, 186, 186, 186, 22, 107, 4, -22, '', 0, 35, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 61, 61, 5653, 0, 1342, 1830, 309, 309, 309, 309, 261, 261, 309, 204, 204, 204, 204, 204, 204, 22, 110, 4, -23, '', 0, 37, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 61, 61, 5897, 0, 1372, 1860, 316, 316, 316, 316, 263, 263, 316, 222, 222, 222, 222, 222, 222, 23, 112, 4, -24, '', 0, 39, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 61, 61, 6751, 0, 1555, 1921, 323, 323, 323, 323, 275, 275, 323, 206, 206, 206, 206, 206, 206, 22, 108, 4, -25, '', 0, 39, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 61, 61, 6995, 0, 1586, 1952, 330, 330, 330, 330, 277, 277, 330, 224, 224, 224, 224, 224, 224, 23, 111, 4, -26, '', 0, 41, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 61, 61, 7239, 0, 1616, 1982, 337, 337, 337, 337, 279, 279, 337, 242, 242, 242, 242, 242, 242, 23, 113, 4, -27, '', 0, 43, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 61, 61, 7483, 0, 1647, 2013, 344, 344, 344, 344, 281, 281, 344, 260, 260, 260, 260, 260, 260, 24, 116, 4, -28, '', 0, 45, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 61, 61, 7727, 0, 1677, 2043, 351, 351, 351, 351, 283, 283, 351, 278, 278, 278, 278, 278, 278, 24, 119, 4, -29, '', 0, 47, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 61, 61, 4179, 4685, 813, 503, 253, 253, 253, 253, 316, 316, 253, 150, 150, 150, 150, 150, 150, 16, 92, 1, -18, '', 0, 22, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 61, 61, 4301, 4868, 833, 518, 260, 260, 260, 260, 319, 319, 260, 168, 168, 168, 168, 168, 168, 16, 94, 1, -18, '', 0, 23, 18, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 61, 61, 4423, 5051, 854, 533, 267, 267, 267, 267, 322, 322, 267, 186, 186, 186, 186, 186, 186, 17, 95, 1, -18, '', 0, 24, 19, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 61, 61, 4545, 5234, 874, 549, 274, 274, 274, 274, 325, 325, 274, 204, 204, 204, 204, 204, 204, 17, 97, 1, -18, '', 0, 25, 20, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 61, 61, 4667, 5417, 894, 564, 281, 281, 281, 281, 328, 328, 281, 222, 222, 222, 222, 222, 222, 17, 98, 1, -18, '', 0, 26, 21, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 61, 61, 5704, 6210, 996, 564, 288, 288, 288, 288, 341, 341, 288, 206, 206, 206, 206, 206, 206, 17, 97, 1, -18, '', 0, 27, 20, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 61, 61, 5826, 6393, 1016, 579, 295, 295, 295, 295, 344, 344, 295, 224, 224, 224, 224, 224, 224, 17, 98, 1, -18, '', 0, 28, 21, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 61, 61, 5948, 6576, 1037, 594, 302, 302, 302, 302, 347, 347, 302, 242, 242, 242, 242, 242, 242, 17, 99, 1, -18, '', 0, 29, 22, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 61, 61, 6070, 6759, 1057, 610, 309, 309, 309, 309, 350, 350, 309, 260, 260, 260, 260, 260, 260, 18, 101, 1, -18, '', 0, 30, 23, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 61, 61, 6192, 6942, 1077, 625, 316, 316, 316, 316, 353, 353, 316, 278, 278, 278, 278, 278, 278, 18, 102, 1, -18, '', 0, 31, 24, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 62, 62, 5894, 0, 2139, 1736, 295, 295, 295, 295, 258, 258, 295, 152, 152, 152, 152, 152, 152, 24, 125, 4, -21, '', 0, 33, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 62, 62, 6204, 0, 2232, 1767, 302, 302, 302, 302, 260, 260, 302, 170, 170, 170, 170, 170, 170, 25, 128, 4, -22, '', 0, 35, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 62, 62, 6514, 0, 2325, 1798, 309, 309, 309, 309, 262, 262, 309, 188, 188, 188, 188, 188, 188, 25, 131, 4, -23, '', 0, 37, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 62, 62, 6824, 0, 2418, 1829, 316, 316, 316, 316, 264, 264, 316, 206, 206, 206, 206, 206, 206, 26, 133, 4, -24, '', 0, 39, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 62, 62, 7134, 0, 2511, 1860, 323, 323, 323, 323, 266, 266, 323, 224, 224, 224, 224, 224, 224, 26, 136, 4, -25, '', 0, 41, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 62, 62, 8064, 0, 2635, 1891, 331, 331, 331, 331, 278, 278, 331, 210, 210, 210, 210, 210, 210, 25, 132, 4, -26, '', 0, 43, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 62, 62, 8374, 0, 2728, 1922, 338, 338, 338, 338, 280, 280, 338, 228, 228, 228, 228, 228, 228, 26, 135, 4, -27, '', 0, 45, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 62, 62, 8684, 0, 2821, 1953, 345, 345, 345, 345, 282, 282, 345, 246, 246, 246, 246, 246, 246, 26, 138, 4, -28, '', 0, 47, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 62, 62, 8994, 0, 2914, 1984, 352, 352, 352, 352, 284, 284, 352, 264, 264, 264, 264, 264, 264, 27, 140, 4, -29, '', 0, 49, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 62, 62, 9304, 0, 3007, 2015, 359, 359, 359, 359, 286, 286, 359, 282, 282, 282, 282, 282, 282, 27, 143, 4, -30, '', 0, 51, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 62, 62, 4859, 4811, 1674, 1043, 267, 267, 267, 267, 320, 320, 267, 152, 152, 152, 152, 152, 152, 16, 94, 1, -18, '', 0, 23, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 62, 62, 5045, 4997, 1736, 1064, 274, 274, 274, 274, 323, 323, 274, 170, 170, 170, 170, 170, 170, 17, 95, 1, -18, '', 0, 24, 18, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 62, 62, 5231, 5183, 1798, 1085, 281, 281, 281, 281, 326, 326, 281, 188, 188, 188, 188, 188, 188, 17, 97, 1, -18, '', 0, 25, 19, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 62, 62, 5417, 5369, 1860, 1105, 288, 288, 288, 288, 329, 329, 288, 206, 206, 206, 206, 206, 206, 17, 98, 1, -18, '', 0, 26, 20, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 62, 62, 5603, 5555, 1922, 1126, 295, 295, 295, 295, 332, 332, 295, 224, 224, 224, 224, 224, 224, 17, 99, 1, -18, '', 0, 27, 21, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 62, 62, 6719, 6361, 2046, 1136, 303, 303, 303, 303, 345, 345, 303, 210, 210, 210, 210, 210, 210, 17, 98, 1, -18, '', 0, 29, 20, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 62, 62, 6905, 6547, 2108, 1157, 310, 310, 310, 310, 348, 348, 310, 228, 228, 228, 228, 228, 228, 17, 99, 1, -18, '', 0, 30, 21, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 62, 62, 7091, 6733, 2170, 1178, 317, 317, 317, 317, 351, 351, 317, 246, 246, 246, 246, 246, 246, 18, 101, 1, -18, '', 0, 31, 22, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 62, 62, 7277, 6919, 2232, 1198, 324, 324, 324, 324, 354, 354, 324, 264, 264, 264, 264, 264, 264, 18, 102, 1, -18, '', 0, 32, 23, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 62, 62, 7463, 7105, 2294, 1219, 331, 331, 331, 331, 357, 357, 331, 282, 282, 282, 282, 282, 282, 18, 104, 1, -18, '', 0, 33, 24, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 62, 62, 5097, 0, 1271, 1767, 295, 295, 295, 295, 258, 258, 295, 152, 152, 152, 152, 152, 152, 24, 118, 4, -21, '', 0, 31, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 62, 62, 5345, 0, 1302, 1798, 302, 302, 302, 302, 260, 260, 302, 170, 170, 170, 170, 170, 170, 25, 121, 4, -22, '', 0, 33, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 62, 62, 5593, 0, 1333, 1829, 309, 309, 309, 309, 262, 262, 309, 188, 188, 188, 188, 188, 188, 25, 123, 4, -23, '', 0, 35, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 62, 62, 5841, 0, 1364, 1860, 316, 316, 316, 316, 264, 264, 316, 206, 206, 206, 206, 206, 206, 26, 126, 4, -24, '', 0, 37, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 62, 62, 6089, 0, 1395, 1891, 323, 323, 323, 323, 266, 266, 323, 224, 224, 224, 224, 224, 224, 26, 128, 4, -25, '', 0, 39, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 62, 62, 6957, 0, 1581, 1953, 331, 331, 331, 331, 278, 278, 331, 210, 210, 210, 210, 210, 210, 25, 125, 4, -26, '', 0, 39, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 62, 62, 7205, 0, 1612, 1984, 338, 338, 338, 338, 280, 280, 338, 228, 228, 228, 228, 228, 228, 26, 127, 4, -27, '', 0, 41, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 62, 62, 7453, 0, 1643, 2015, 345, 345, 345, 345, 282, 282, 345, 246, 246, 246, 246, 246, 246, 26, 130, 4, -28, '', 0, 43, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 62, 62, 7701, 0, 1674, 2046, 352, 352, 352, 352, 284, 284, 352, 264, 264, 264, 264, 264, 264, 27, 132, 4, -29, '', 0, 45, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 62, 62, 7949, 0, 1705, 2077, 359, 359, 359, 359, 286, 286, 359, 282, 282, 282, 282, 282, 282, 27, 135, 4, -30, '', 0, 47, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 62, 62, 4332, 4811, 826, 511, 260, 260, 260, 260, 320, 320, 260, 152, 152, 152, 152, 152, 152, 16, 94, 1, -18, '', 0, 22, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 62, 62, 4456, 4997, 847, 527, 267, 267, 267, 267, 323, 323, 267, 170, 170, 170, 170, 170, 170, 17, 95, 1, -18, '', 0, 23, 18, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 62, 62, 4580, 5183, 868, 542, 274, 274, 274, 274, 326, 326, 274, 188, 188, 188, 188, 188, 188, 17, 97, 1, -18, '', 0, 24, 19, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 62, 62, 4704, 5369, 888, 558, 281, 281, 281, 281, 329, 329, 281, 206, 206, 206, 206, 206, 206, 17, 98, 1, -18, '', 0, 25, 20, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 62, 62, 4828, 5555, 909, 573, 288, 288, 288, 288, 332, 332, 288, 224, 224, 224, 224, 224, 224, 17, 99, 1, -18, '', 0, 26, 21, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 62, 62, 5882, 6361, 1012, 573, 296, 296, 296, 296, 345, 345, 296, 210, 210, 210, 210, 210, 210, 17, 98, 1, -18, '', 0, 27, 20, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 62, 62, 6006, 6547, 1033, 589, 303, 303, 303, 303, 348, 348, 303, 228, 228, 228, 228, 228, 228, 17, 99, 1, -18, '', 0, 28, 21, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 62, 62, 6130, 6733, 1054, 604, 310, 310, 310, 310, 351, 351, 310, 246, 246, 246, 246, 246, 246, 18, 101, 1, -18, '', 0, 29, 22, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 62, 62, 6254, 6919, 1074, 620, 317, 317, 317, 317, 354, 354, 317, 264, 264, 264, 264, 264, 264, 18, 102, 1, -18, '', 0, 30, 23, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 62, 62, 6378, 7105, 1095, 635, 324, 324, 324, 324, 357, 357, 324, 282, 282, 282, 282, 282, 282, 18, 104, 1, -18, '', 0, 31, 24, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 63, 63, 6102, 0, 2174, 1764, 301, 301, 301, 301, 261, 261, 301, 154, 154, 154, 154, 154, 154, 27, 141, 4, -21, '', 0, 34, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 63, 63, 6417, 0, 2268, 1796, 308, 308, 308, 308, 263, 263, 308, 172, 172, 172, 172, 172, 172, 28, 144, 4, -22, '', 0, 36, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 63, 63, 6732, 0, 2363, 1827, 315, 315, 315, 315, 265, 265, 315, 190, 190, 190, 190, 190, 190, 28, 147, 4, -23, '', 0, 38, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 63, 63, 7047, 0, 2457, 1859, 322, 322, 322, 322, 267, 267, 322, 208, 208, 208, 208, 208, 208, 29, 150, 4, -24, '', 0, 40, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 63, 63, 7362, 0, 2552, 1890, 329, 329, 329, 329, 269, 269, 329, 226, 226, 226, 226, 226, 226, 29, 153, 4, -25, '', 0, 42, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 63, 63, 8307, 0, 2678, 1921, 337, 337, 337, 337, 281, 281, 337, 212, 212, 212, 212, 212, 212, 29, 148, 4, -26, '', 0, 44, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 63, 63, 8622, 0, 2772, 1953, 344, 344, 344, 344, 283, 283, 344, 230, 230, 230, 230, 230, 230, 29, 151, 4, -27, '', 0, 46, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 63, 63, 8937, 0, 2867, 1984, 351, 351, 351, 351, 285, 285, 351, 248, 248, 248, 248, 248, 248, 30, 154, 4, -28, '', 0, 48, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 63, 63, 9252, 0, 2961, 2016, 358, 358, 358, 358, 287, 287, 358, 266, 266, 266, 266, 266, 266, 30, 157, 4, -29, '', 0, 50, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 63, 63, 9567, 0, 3056, 2047, 365, 365, 365, 365, 289, 289, 365, 284, 284, 284, 284, 284, 284, 31, 160, 4, -30, '', 0, 52, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 63, 63, 5030, 4939, 1701, 1061, 273, 273, 273, 273, 323, 323, 273, 154, 154, 154, 154, 154, 154, 17, 95, 1, -18, '', 0, 23, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 63, 63, 5219, 5128, 1764, 1082, 280, 280, 280, 280, 326, 326, 280, 172, 172, 172, 172, 172, 172, 17, 97, 1, -18, '', 0, 24, 18, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 63, 63, 5408, 5317, 1827, 1103, 287, 287, 287, 287, 329, 329, 287, 190, 190, 190, 190, 190, 190, 17, 98, 1, -18, '', 0, 25, 19, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 63, 63, 5597, 5506, 1890, 1124, 294, 294, 294, 294, 332, 332, 294, 208, 208, 208, 208, 208, 208, 17, 99, 1, -18, '', 0, 26, 20, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 63, 63, 5786, 5695, 1953, 1145, 301, 301, 301, 301, 335, 335, 301, 226, 226, 226, 226, 226, 226, 18, 101, 1, -18, '', 0, 27, 21, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 63, 63, 6920, 6514, 2079, 1155, 309, 309, 309, 309, 348, 348, 309, 212, 212, 212, 212, 212, 212, 17, 99, 1, -18, '', 0, 29, 20, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 63, 63, 7109, 6703, 2142, 1176, 316, 316, 316, 316, 351, 351, 316, 230, 230, 230, 230, 230, 230, 18, 101, 1, -18, '', 0, 30, 21, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 63, 63, 7298, 6892, 2205, 1197, 323, 323, 323, 323, 354, 354, 323, 248, 248, 248, 248, 248, 248, 18, 102, 1, -18, '', 0, 31, 22, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 63, 63, 7487, 7081, 2268, 1218, 330, 330, 330, 330, 357, 357, 330, 266, 266, 266, 266, 266, 266, 18, 104, 1, -18, '', 0, 32, 23, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 63, 63, 7676, 7270, 2331, 1239, 337, 337, 337, 337, 360, 360, 337, 284, 284, 284, 284, 284, 284, 18, 105, 1, -18, '', 0, 33, 24, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 63, 63, 5277, 0, 1291, 1795, 301, 301, 301, 301, 261, 261, 301, 154, 154, 154, 154, 154, 154, 27, 134, 4, -21, '', 0, 32, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 63, 63, 5529, 0, 1323, 1827, 308, 308, 308, 308, 263, 263, 308, 172, 172, 172, 172, 172, 172, 28, 137, 4, -22, '', 0, 34, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 63, 63, 5781, 0, 1354, 1858, 315, 315, 315, 315, 265, 265, 315, 190, 190, 190, 190, 190, 190, 28, 140, 4, -23, '', 0, 36, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 63, 63, 6033, 0, 1386, 1890, 322, 322, 322, 322, 267, 267, 322, 208, 208, 208, 208, 208, 208, 29, 142, 4, -24, '', 0, 38, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 63, 63, 6285, 0, 1417, 1921, 329, 329, 329, 329, 269, 269, 329, 226, 226, 226, 226, 226, 226, 29, 145, 4, -25, '', 0, 40, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 63, 63, 7167, 0, 1606, 1984, 337, 337, 337, 337, 281, 281, 337, 212, 212, 212, 212, 212, 212, 29, 141, 4, -26, '', 0, 40, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 63, 63, 7419, 0, 1638, 2016, 344, 344, 344, 344, 283, 283, 344, 230, 230, 230, 230, 230, 230, 29, 143, 4, -27, '', 0, 42, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 63, 63, 7671, 0, 1669, 2047, 351, 351, 351, 351, 285, 285, 351, 248, 248, 248, 248, 248, 248, 30, 146, 4, -28, '', 0, 44, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 63, 63, 7923, 0, 1701, 2079, 358, 358, 358, 358, 287, 287, 358, 266, 266, 266, 266, 266, 266, 30, 149, 4, -29, '', 0, 46, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 63, 63, 8175, 0, 1732, 2110, 365, 365, 365, 365, 289, 289, 365, 284, 284, 284, 284, 284, 284, 31, 151, 4, -30, '', 0, 48, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 63, 63, 4489, 4939, 840, 519, 265, 265, 265, 265, 323, 323, 265, 154, 154, 154, 154, 154, 154, 17, 95, 1, -18, '', 0, 22, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 63, 63, 4615, 5128, 861, 535, 272, 272, 272, 272, 326, 326, 272, 172, 172, 172, 172, 172, 172, 17, 97, 1, -18, '', 0, 23, 18, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 63, 63, 4741, 5317, 882, 551, 279, 279, 279, 279, 329, 329, 279, 190, 190, 190, 190, 190, 190, 17, 98, 1, -18, '', 0, 24, 19, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 63, 63, 4867, 5506, 903, 567, 286, 286, 286, 286, 332, 332, 286, 208, 208, 208, 208, 208, 208, 17, 99, 1, -18, '', 0, 25, 20, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 63, 63, 4993, 5695, 924, 582, 293, 293, 293, 293, 335, 335, 293, 226, 226, 226, 226, 226, 226, 18, 101, 1, -18, '', 0, 26, 21, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 63, 63, 6064, 6514, 1029, 582, 301, 301, 301, 301, 348, 348, 301, 212, 212, 212, 212, 212, 212, 17, 99, 1, -18, '', 0, 27, 20, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 63, 63, 6190, 6703, 1050, 598, 308, 308, 308, 308, 351, 351, 308, 230, 230, 230, 230, 230, 230, 18, 101, 1, -18, '', 0, 28, 21, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 63, 63, 6316, 6892, 1071, 614, 315, 315, 315, 315, 354, 354, 315, 248, 248, 248, 248, 248, 248, 18, 102, 1, -18, '', 0, 29, 22, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 63, 63, 6442, 7081, 1092, 630, 322, 322, 322, 322, 357, 357, 322, 266, 266, 266, 266, 266, 266, 18, 104, 1, -18, '', 0, 30, 23, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 63, 63, 6568, 7270, 1113, 645, 329, 329, 329, 329, 360, 360, 329, 284, 284, 284, 284, 284, 284, 18, 105, 1, -18, '', 0, 31, 24, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 64, 64, 6314, 0, 2208, 1792, 308, 308, 308, 308, 264, 264, 308, 157, 157, 157, 157, 157, 157, 31, 158, 4, -22, '', 0, 34, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 64, 64, 6634, 0, 2304, 1824, 315, 315, 315, 315, 266, 266, 315, 176, 176, 176, 176, 176, 176, 31, 161, 4, -23, '', 0, 36, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 64, 64, 6954, 0, 2400, 1856, 322, 322, 322, 322, 268, 268, 322, 195, 195, 195, 195, 195, 195, 32, 163, 4, -24, '', 0, 38, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 64, 64, 7274, 0, 2496, 1888, 329, 329, 329, 329, 270, 270, 329, 214, 214, 214, 214, 214, 214, 32, 166, 4, -25, '', 0, 40, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 64, 64, 7594, 0, 2592, 1920, 336, 336, 336, 336, 272, 272, 336, 233, 233, 233, 233, 233, 233, 33, 169, 4, -26, '', 0, 42, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 64, 64, 8554, 0, 2720, 1952, 345, 345, 345, 345, 284, 284, 345, 215, 215, 215, 215, 215, 215, 32, 165, 4, -27, '', 0, 44, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 64, 64, 8874, 0, 2816, 1984, 352, 352, 352, 352, 286, 286, 352, 234, 234, 234, 234, 234, 234, 32, 168, 4, -28, '', 0, 46, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 64, 64, 9194, 0, 2912, 2016, 359, 359, 359, 359, 288, 288, 359, 253, 253, 253, 253, 253, 253, 33, 170, 4, -29, '', 0, 48, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 64, 64, 9514, 0, 3008, 2048, 366, 366, 366, 366, 290, 290, 366, 272, 272, 272, 272, 272, 272, 33, 173, 4, -30, '', 0, 50, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 64, 64, 9834, 0, 3104, 2080, 373, 373, 373, 373, 292, 292, 373, 291, 291, 291, 291, 291, 291, 34, 176, 4, -31, '', 0, 52, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 64, 64, 5206, 5069, 1728, 1077, 280, 280, 280, 280, 327, 327, 280, 157, 157, 157, 157, 157, 157, 17, 97, 1, -18, '', 0, 24, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 64, 64, 5398, 5261, 1792, 1098, 287, 287, 287, 287, 330, 330, 287, 176, 176, 176, 176, 176, 176, 17, 98, 1, -18, '', 0, 25, 18, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 64, 64, 5590, 5453, 1856, 1120, 294, 294, 294, 294, 333, 333, 294, 195, 195, 195, 195, 195, 195, 17, 99, 1, -18, '', 0, 26, 19, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 64, 64, 5782, 5645, 1920, 1141, 301, 301, 301, 301, 336, 336, 301, 214, 214, 214, 214, 214, 214, 18, 101, 1, -18, '', 0, 27, 20, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 64, 64, 5974, 5837, 1984, 1162, 308, 308, 308, 308, 339, 339, 308, 233, 233, 233, 233, 233, 233, 18, 102, 1, -18, '', 0, 28, 21, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 64, 64, 7126, 6669, 2112, 1173, 317, 317, 317, 317, 352, 352, 317, 215, 215, 215, 215, 215, 215, 18, 101, 1, -18, '', 0, 30, 20, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 64, 64, 7318, 6861, 2176, 1194, 324, 324, 324, 324, 355, 355, 324, 234, 234, 234, 234, 234, 234, 18, 102, 1, -18, '', 0, 31, 21, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 64, 64, 7510, 7053, 2240, 1216, 331, 331, 331, 331, 358, 358, 331, 253, 253, 253, 253, 253, 253, 18, 104, 1, -18, '', 0, 32, 22, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 64, 64, 7702, 7245, 2304, 1237, 338, 338, 338, 338, 361, 361, 338, 272, 272, 272, 272, 272, 272, 18, 105, 1, -18, '', 0, 33, 23, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 64, 64, 7894, 7437, 2368, 1258, 345, 345, 345, 345, 364, 364, 345, 291, 291, 291, 291, 291, 291, 19, 106, 1, -18, '', 0, 34, 24, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 64, 64, 5462, 0, 1312, 1824, 308, 308, 308, 308, 264, 264, 308, 157, 157, 157, 157, 157, 157, 31, 151, 4, -22, '', 0, 32, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 64, 64, 5718, 0, 1344, 1856, 315, 315, 315, 315, 266, 266, 315, 176, 176, 176, 176, 176, 176, 31, 153, 4, -23, '', 0, 34, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 64, 64, 5974, 0, 1376, 1888, 322, 322, 322, 322, 268, 268, 322, 195, 195, 195, 195, 195, 195, 32, 156, 4, -24, '', 0, 36, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 64, 64, 6230, 0, 1408, 1920, 329, 329, 329, 329, 270, 270, 329, 214, 214, 214, 214, 214, 214, 32, 158, 4, -25, '', 0, 38, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 64, 64, 6486, 0, 1440, 1952, 336, 336, 336, 336, 272, 272, 336, 233, 233, 233, 233, 233, 233, 33, 161, 4, -26, '', 0, 40, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 64, 64, 7382, 0, 1632, 2016, 345, 345, 345, 345, 284, 284, 345, 215, 215, 215, 215, 215, 215, 32, 157, 4, -27, '', 0, 40, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 64, 64, 7638, 0, 1664, 2048, 352, 352, 352, 352, 286, 286, 352, 234, 234, 234, 234, 234, 234, 32, 160, 4, -28, '', 0, 42, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 64, 64, 7894, 0, 1696, 2080, 359, 359, 359, 359, 288, 288, 359, 253, 253, 253, 253, 253, 253, 33, 162, 4, -29, '', 0, 44, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 64, 64, 8150, 0, 1728, 2112, 366, 366, 366, 366, 290, 290, 366, 272, 272, 272, 272, 272, 272, 33, 165, 4, -30, '', 0, 46, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 64, 64, 8406, 0, 1760, 2144, 373, 373, 373, 373, 292, 292, 373, 291, 291, 291, 291, 291, 291, 34, 168, 4, -31, '', 0, 48, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 64, 64, 4650, 5069, 853, 528, 272, 272, 272, 272, 327, 327, 272, 157, 157, 157, 157, 157, 157, 17, 97, 1, -18, '', 0, 23, 17, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 64, 64, 4778, 5261, 874, 544, 279, 279, 279, 279, 330, 330, 279, 176, 176, 176, 176, 176, 176, 17, 98, 1, -18, '', 0, 24, 18, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 64, 64, 4906, 5453, 896, 560, 286, 286, 286, 286, 333, 333, 286, 195, 195, 195, 195, 195, 195, 17, 99, 1, -18, '', 0, 25, 19, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 64, 64, 5034, 5645, 917, 576, 293, 293, 293, 293, 336, 336, 293, 214, 214, 214, 214, 214, 214, 18, 101, 1, -18, '', 0, 26, 20, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 64, 64, 5162, 5837, 938, 592, 300, 300, 300, 300, 339, 339, 300, 233, 233, 233, 233, 233, 233, 18, 102, 1, -18, '', 0, 27, 21, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 64, 64, 6250, 6669, 1045, 592, 309, 309, 309, 309, 352, 352, 309, 215, 215, 215, 215, 215, 215, 18, 101, 1, -18, '', 0, 28, 20, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 64, 64, 6378, 6861, 1066, 608, 316, 316, 316, 316, 355, 355, 316, 234, 234, 234, 234, 234, 234, 18, 102, 1, -18, '', 0, 29, 21, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 64, 64, 6506, 7053, 1088, 624, 323, 323, 323, 323, 358, 358, 323, 253, 253, 253, 253, 253, 253, 18, 104, 1, -18, '', 0, 30, 22, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 64, 64, 6634, 7245, 1109, 640, 330, 330, 330, 330, 361, 361, 330, 272, 272, 272, 272, 272, 272, 18, 105, 1, -18, '', 0, 31, 23, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 64, 64, 6762, 7437, 1130, 656, 337, 337, 337, 337, 364, 364, 337, 291, 291, 291, 291, 291, 291, 19, 106, 1, -18, '', 0, 32, 24, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 65, 65, 6533, 0, 2243, 1820, 315, 315, 315, 315, 267, 267, 315, 159, 159, 159, 159, 159, 159, 34, 174, 4, -22, '', 0, 34, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 65, 65, 6858, 0, 2340, 1853, 322, 322, 322, 322, 269, 269, 322, 178, 178, 178, 178, 178, 178, 34, 177, 4, -23, '', 0, 36, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 65, 65, 7183, 0, 2438, 1885, 329, 329, 329, 329, 271, 271, 329, 197, 197, 197, 197, 197, 197, 35, 180, 4, -24, '', 0, 38, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 65, 65, 7508, 0, 2535, 1918, 336, 336, 336, 336, 273, 273, 336, 216, 216, 216, 216, 216, 216, 35, 183, 4, -25, '', 0, 40, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 65, 65, 7833, 0, 2633, 1950, 343, 343, 343, 343, 275, 275, 343, 235, 235, 235, 235, 235, 235, 36, 185, 4, -26, '', 0, 42, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 65, 65, 8808, 0, 2763, 1982, 352, 352, 352, 352, 287, 287, 352, 219, 219, 219, 219, 219, 219, 35, 181, 4, -27, '', 0, 44, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 65, 65, 9133, 0, 2860, 2015, 359, 359, 359, 359, 289, 289, 359, 238, 238, 238, 238, 238, 238, 36, 184, 4, -28, '', 0, 46, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 65, 65, 9458, 0, 2958, 2047, 366, 366, 366, 366, 291, 291, 366, 257, 257, 257, 257, 257, 257, 36, 187, 4, -29, '', 0, 48, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 65, 65, 9783, 0, 3055, 2080, 373, 373, 373, 373, 293, 293, 373, 276, 276, 276, 276, 276, 276, 37, 190, 4, -30, '', 0, 50, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 65, 65, 10108, 0, 3153, 2112, 380, 380, 380, 380, 295, 295, 380, 295, 295, 295, 295, 295, 295, 37, 192, 4, -31, '', 0, 52, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 65, 65, 5385, 5200, 1755, 1094, 286, 286, 286, 286, 330, 330, 286, 159, 159, 159, 159, 159, 159, 17, 98, 1, -18, '', 0, 24, 18, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 65, 65, 5580, 5395, 1820, 1116, 293, 293, 293, 293, 333, 333, 293, 178, 178, 178, 178, 178, 178, 17, 99, 1, -18, '', 0, 25, 19, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 65, 65, 5775, 5590, 1885, 1138, 300, 300, 300, 300, 336, 336, 300, 197, 197, 197, 197, 197, 197, 18, 101, 1, -18, '', 0, 26, 20, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 65, 65, 5970, 5785, 1950, 1159, 307, 307, 307, 307, 339, 339, 307, 216, 216, 216, 216, 216, 216, 18, 102, 1, -18, '', 0, 27, 21, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 65, 65, 6165, 5980, 2015, 1181, 314, 314, 314, 314, 342, 342, 314, 235, 235, 235, 235, 235, 235, 18, 104, 1, -18, '', 0, 28, 22, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 65, 65, 7335, 6825, 2145, 1191, 323, 323, 323, 323, 355, 355, 323, 219, 219, 219, 219, 219, 219, 18, 102, 1, -18, '', 0, 30, 21, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 65, 65, 7530, 7020, 2210, 1213, 330, 330, 330, 330, 358, 358, 330, 238, 238, 238, 238, 238, 238, 18, 104, 1, -18, '', 0, 31, 22, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 65, 65, 7725, 7215, 2275, 1235, 337, 337, 337, 337, 361, 361, 337, 257, 257, 257, 257, 257, 257, 18, 105, 1, -18, '', 0, 32, 23, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 65, 65, 7920, 7410, 2340, 1256, 344, 344, 344, 344, 364, 364, 344, 276, 276, 276, 276, 276, 276, 19, 106, 1, -18, '', 0, 33, 24, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 65, 65, 8115, 7605, 2405, 1278, 351, 351, 351, 351, 367, 367, 351, 295, 295, 295, 295, 295, 295, 19, 108, 1, -18, '', 0, 34, 25, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 65, 65, 5652, 0, 1332, 1852, 315, 315, 315, 315, 267, 267, 315, 159, 159, 159, 159, 159, 159, 34, 167, 4, -22, '', 0, 32, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 65, 65, 5912, 0, 1365, 1885, 322, 322, 322, 322, 269, 269, 322, 178, 178, 178, 178, 178, 178, 34, 170, 4, -23, '', 0, 34, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 65, 65, 6172, 0, 1397, 1917, 329, 329, 329, 329, 271, 271, 329, 197, 197, 197, 197, 197, 197, 35, 172, 4, -24, '', 0, 36, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 65, 65, 6432, 0, 1430, 1950, 336, 336, 336, 336, 273, 273, 336, 216, 216, 216, 216, 216, 216, 35, 175, 4, -25, '', 0, 38, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 65, 65, 6692, 0, 1462, 1982, 343, 343, 343, 343, 275, 275, 343, 235, 235, 235, 235, 235, 235, 36, 177, 4, -26, '', 0, 40, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 65, 65, 7602, 0, 1657, 2047, 352, 352, 352, 352, 287, 287, 352, 219, 219, 219, 219, 219, 219, 35, 173, 4, -27, '', 0, 40, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 65, 65, 7862, 0, 1690, 2080, 359, 359, 359, 359, 289, 289, 359, 238, 238, 238, 238, 238, 238, 36, 176, 4, -28, '', 0, 42, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 65, 65, 8122, 0, 1722, 2112, 366, 366, 366, 366, 291, 291, 366, 257, 257, 257, 257, 257, 257, 36, 179, 4, -29, '', 0, 44, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 65, 65, 8382, 0, 1755, 2145, 373, 373, 373, 373, 293, 293, 373, 276, 276, 276, 276, 276, 276, 37, 181, 4, -30, '', 0, 46, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 65, 65, 8642, 0, 1787, 2177, 380, 380, 380, 380, 295, 295, 380, 295, 295, 295, 295, 295, 295, 37, 184, 4, -31, '', 0, 48, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 65, 65, 4816, 5200, 866, 536, 277, 277, 277, 277, 330, 330, 277, 159, 159, 159, 159, 159, 159, 17, 98, 1, -18, '', 0, 23, 18, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 65, 65, 4946, 5395, 888, 552, 284, 284, 284, 284, 333, 333, 284, 178, 178, 178, 178, 178, 178, 17, 99, 1, -18, '', 0, 24, 19, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 65, 65, 5076, 5590, 910, 568, 291, 291, 291, 291, 336, 336, 291, 197, 197, 197, 197, 197, 197, 18, 101, 1, -18, '', 0, 25, 20, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 65, 65, 5206, 5785, 931, 585, 298, 298, 298, 298, 339, 339, 298, 216, 216, 216, 216, 216, 216, 18, 102, 1, -18, '', 0, 26, 21, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 65, 65, 5336, 5980, 953, 601, 305, 305, 305, 305, 342, 342, 305, 235, 235, 235, 235, 235, 235, 18, 104, 1, -18, '', 0, 27, 22, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 65, 65, 6441, 6825, 1061, 601, 314, 314, 314, 314, 355, 355, 314, 219, 219, 219, 219, 219, 219, 18, 102, 1, -18, '', 0, 28, 21, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 65, 65, 6571, 7020, 1083, 617, 321, 321, 321, 321, 358, 358, 321, 238, 238, 238, 238, 238, 238, 18, 104, 1, -18, '', 0, 29, 22, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 65, 65, 6701, 7215, 1105, 633, 328, 328, 328, 328, 361, 361, 328, 257, 257, 257, 257, 257, 257, 18, 105, 1, -18, '', 0, 30, 23, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 65, 65, 6831, 7410, 1126, 650, 335, 335, 335, 335, 364, 364, 335, 276, 276, 276, 276, 276, 276, 19, 106, 1, -18, '', 0, 31, 24, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 65, 65, 6961, 7605, 1148, 666, 342, 342, 342, 342, 367, 367, 342, 295, 295, 295, 295, 295, 295, 19, 108, 1, -18, '', 0, 32, 25, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 66, 66, 6757, 0, 2277, 1848, 352, 352, 352, 352, 300, 300, 352, 161, 161, 161, 161, 161, 161, 37, 191, 4, -23, '', 0, 35, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 66, 66, 7087, 0, 2376, 1881, 359, 359, 359, 359, 302, 302, 359, 180, 180, 180, 180, 180, 180, 38, 193, 4, -24, '', 0, 37, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 66, 66, 7417, 0, 2475, 1914, 366, 366, 366, 366, 304, 304, 366, 199, 199, 199, 199, 199, 199, 38, 196, 4, -25, '', 0, 39, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 66, 66, 7747, 0, 2574, 1947, 373, 373, 373, 373, 306, 306, 373, 218, 218, 218, 218, 218, 218, 39, 199, 4, -26, '', 0, 41, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 66, 66, 8077, 0, 2673, 1980, 380, 380, 380, 380, 308, 308, 380, 237, 237, 237, 237, 237, 237, 39, 202, 4, -27, '', 0, 43, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 66, 66, 9067, 0, 2805, 2013, 390, 390, 390, 390, 320, 320, 390, 221, 221, 221, 221, 221, 221, 38, 198, 4, -28, '', 0, 45, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 66, 66, 9397, 0, 2904, 2046, 397, 397, 397, 397, 322, 322, 397, 240, 240, 240, 240, 240, 240, 39, 200, 4, -29, '', 0, 47, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 66, 66, 9727, 0, 3003, 2079, 404, 404, 404, 404, 324, 324, 404, 259, 259, 259, 259, 259, 259, 39, 203, 4, -30, '', 0, 49, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 66, 66, 10057, 0, 3102, 2112, 411, 411, 411, 411, 326, 326, 411, 278, 278, 278, 278, 278, 278, 40, 206, 4, -31, '', 0, 51, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 66, 66, 10387, 0, 3201, 2145, 418, 418, 418, 418, 328, 328, 418, 297, 297, 297, 297, 297, 297, 40, 209, 4, -32, '', 0, 53, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 66, 66, 5570, 5333, 1782, 1111, 323, 323, 323, 323, 364, 364, 323, 161, 161, 161, 161, 161, 161, 17, 99, 1, -18, '', 0, 24, 18, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 66, 66, 5768, 5531, 1848, 1133, 330, 330, 330, 330, 367, 367, 330, 180, 180, 180, 180, 180, 180, 18, 101, 1, -18, '', 0, 25, 19, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 66, 66, 5966, 5729, 1914, 1155, 337, 337, 337, 337, 370, 370, 337, 199, 199, 199, 199, 199, 199, 18, 102, 1, -18, '', 0, 26, 20, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 66, 66, 6164, 5927, 1980, 1177, 344, 344, 344, 344, 373, 373, 344, 218, 218, 218, 218, 218, 218, 18, 104, 1, -18, '', 0, 27, 21, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 66, 66, 6362, 6125, 2046, 1199, 351, 351, 351, 351, 376, 376, 351, 237, 237, 237, 237, 237, 237, 18, 105, 1, -18, '', 0, 28, 22, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 66, 66, 7550, 6983, 2178, 1210, 361, 361, 361, 361, 389, 389, 361, 221, 221, 221, 221, 221, 221, 18, 104, 1, -18, '', 0, 30, 21, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 66, 66, 7748, 7181, 2244, 1232, 368, 368, 368, 368, 392, 392, 368, 240, 240, 240, 240, 240, 240, 18, 105, 1, -18, '', 0, 31, 22, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 66, 66, 7946, 7379, 2310, 1254, 375, 375, 375, 375, 395, 395, 375, 259, 259, 259, 259, 259, 259, 19, 106, 1, -18, '', 0, 32, 23, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 66, 66, 8144, 7577, 2376, 1276, 382, 382, 382, 382, 398, 398, 382, 278, 278, 278, 278, 278, 278, 19, 108, 1, -18, '', 0, 33, 24, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 66, 66, 8342, 7775, 2442, 1298, 389, 389, 389, 389, 401, 401, 389, 297, 297, 297, 297, 297, 297, 19, 109, 1, -18, '', 0, 34, 25, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 66, 66, 5847, 0, 1353, 1881, 352, 352, 352, 352, 300, 300, 352, 161, 161, 161, 161, 161, 161, 37, 183, 4, -23, '', 0, 33, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 66, 66, 6111, 0, 1386, 1914, 359, 359, 359, 359, 302, 302, 359, 180, 180, 180, 180, 180, 180, 38, 186, 4, -24, '', 0, 35, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 66, 66, 6375, 0, 1419, 1947, 366, 366, 366, 366, 304, 304, 366, 199, 199, 199, 199, 199, 199, 38, 188, 4, -25, '', 0, 37, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 66, 66, 6639, 0, 1452, 1980, 373, 373, 373, 373, 306, 306, 373, 218, 218, 218, 218, 218, 218, 39, 191, 4, -26, '', 0, 39, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 66, 66, 6903, 0, 1485, 2013, 380, 380, 380, 380, 308, 308, 380, 237, 237, 237, 237, 237, 237, 39, 194, 4, -27, '', 0, 41, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 66, 66, 7827, 0, 1683, 2079, 390, 390, 390, 390, 320, 320, 390, 221, 221, 221, 221, 221, 221, 38, 190, 4, -28, '', 0, 41, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 66, 66, 8091, 0, 1716, 2112, 397, 397, 397, 397, 322, 322, 397, 240, 240, 240, 240, 240, 240, 39, 192, 4, -29, '', 0, 43, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 66, 66, 8355, 0, 1749, 2145, 404, 404, 404, 404, 324, 324, 404, 259, 259, 259, 259, 259, 259, 39, 195, 4, -30, '', 0, 45, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 66, 66, 8619, 0, 1782, 2178, 411, 411, 411, 411, 326, 326, 411, 278, 278, 278, 278, 278, 278, 40, 198, 4, -31, '', 0, 47, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 66, 66, 8883, 0, 1815, 2211, 418, 418, 418, 418, 328, 328, 418, 297, 297, 297, 297, 297, 297, 40, 200, 4, -32, '', 0, 49, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 66, 66, 4986, 5333, 880, 544, 314, 314, 314, 314, 364, 364, 314, 161, 161, 161, 161, 161, 161, 17, 99, 1, -18, '', 0, 23, 18, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 66, 66, 5118, 5531, 902, 561, 321, 321, 321, 321, 367, 367, 321, 180, 180, 180, 180, 180, 180, 18, 101, 1, -18, '', 0, 24, 19, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 66, 66, 5250, 5729, 924, 577, 328, 328, 328, 328, 370, 370, 328, 199, 199, 199, 199, 199, 199, 18, 102, 1, -18, '', 0, 25, 20, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 66, 66, 5382, 5927, 946, 594, 335, 335, 335, 335, 373, 373, 335, 218, 218, 218, 218, 218, 218, 18, 104, 1, -18, '', 0, 26, 21, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 66, 66, 5514, 6125, 968, 610, 342, 342, 342, 342, 376, 376, 342, 237, 237, 237, 237, 237, 237, 18, 105, 1, -18, '', 0, 27, 22, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 66, 66, 6636, 6983, 1078, 610, 352, 352, 352, 352, 389, 389, 352, 221, 221, 221, 221, 221, 221, 18, 104, 1, -18, '', 0, 28, 21, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 66, 66, 6768, 7181, 1100, 627, 359, 359, 359, 359, 392, 392, 359, 240, 240, 240, 240, 240, 240, 18, 105, 1, -18, '', 0, 29, 22, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 66, 66, 6900, 7379, 1122, 643, 366, 366, 366, 366, 395, 395, 366, 259, 259, 259, 259, 259, 259, 19, 106, 1, -18, '', 0, 30, 23, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 66, 66, 7032, 7577, 1144, 660, 373, 373, 373, 373, 398, 398, 373, 278, 278, 278, 278, 278, 278, 19, 108, 1, -18, '', 0, 31, 24, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 66, 66, 7164, 7775, 1166, 676, 380, 380, 380, 380, 401, 401, 380, 297, 297, 297, 297, 297, 297, 19, 109, 1, -18, '', 0, 32, 25, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 67, 67, 6986, 0, 2312, 1876, 364, 364, 364, 364, 308, 308, 364, 163, 163, 163, 163, 163, 163, 40, 207, 4, -23, '', 0, 35, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 67, 67, 7321, 0, 2412, 1910, 371, 371, 371, 371, 310, 310, 371, 182, 182, 182, 182, 182, 182, 41, 210, 4, -24, '', 0, 37, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 67, 67, 7656, 0, 2513, 1943, 378, 378, 378, 378, 312, 312, 378, 201, 201, 201, 201, 201, 201, 41, 213, 4, -25, '', 0, 39, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 67, 67, 7991, 0, 2613, 1977, 385, 385, 385, 385, 314, 314, 385, 220, 220, 220, 220, 220, 220, 42, 215, 4, -26, '', 0, 41, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 67, 67, 8326, 0, 2714, 2010, 392, 392, 392, 392, 316, 316, 392, 239, 239, 239, 239, 239, 239, 42, 218, 4, -27, '', 0, 43, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 67, 67, 9331, 0, 2848, 2043, 402, 402, 402, 402, 328, 328, 402, 223, 223, 223, 223, 223, 223, 42, 214, 4, -28, '', 0, 45, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 67, 67, 9666, 0, 2948, 2077, 409, 409, 409, 409, 330, 330, 409, 242, 242, 242, 242, 242, 242, 42, 217, 4, -29, '', 0, 47, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 67, 67, 10001, 0, 3049, 2110, 416, 416, 416, 416, 332, 332, 416, 261, 261, 261, 261, 261, 261, 43, 220, 4, -30, '', 0, 49, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 67, 67, 10336, 0, 3149, 2144, 423, 423, 423, 423, 334, 334, 423, 280, 280, 280, 280, 280, 280, 43, 222, 4, -31, '', 0, 51, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 67, 67, 10671, 0, 3250, 2177, 430, 430, 430, 430, 336, 336, 430, 299, 299, 299, 299, 299, 299, 44, 225, 4, -32, '', 0, 53, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 67, 67, 5759, 5467, 1809, 1128, 334, 334, 334, 334, 372, 372, 334, 163, 163, 163, 163, 163, 163, 18, 101, 1, -18, '', 0, 24, 18, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 67, 67, 5960, 5668, 1876, 1150, 341, 341, 341, 341, 375, 375, 341, 182, 182, 182, 182, 182, 182, 18, 102, 1, -18, '', 0, 25, 19, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 67, 67, 6161, 5869, 1943, 1173, 348, 348, 348, 348, 378, 378, 348, 201, 201, 201, 201, 201, 201, 18, 104, 1, -18, '', 0, 26, 20, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 67, 67, 6362, 6070, 2010, 1195, 355, 355, 355, 355, 381, 381, 355, 220, 220, 220, 220, 220, 220, 18, 105, 1, -18, '', 0, 27, 21, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 67, 67, 6563, 6271, 2077, 1217, 362, 362, 362, 362, 384, 384, 362, 239, 239, 239, 239, 239, 239, 19, 106, 1, -18, '', 0, 28, 22, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 67, 67, 7769, 7142, 2211, 1228, 372, 372, 372, 372, 397, 397, 372, 223, 223, 223, 223, 223, 223, 18, 105, 1, -18, '', 0, 30, 21, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 67, 67, 7970, 7343, 2278, 1250, 379, 379, 379, 379, 400, 400, 379, 242, 242, 242, 242, 242, 242, 19, 106, 1, -18, '', 0, 31, 22, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 67, 67, 8171, 7544, 2345, 1273, 386, 386, 386, 386, 403, 403, 386, 261, 261, 261, 261, 261, 261, 19, 108, 1, -18, '', 0, 32, 23, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 67, 67, 8372, 7745, 2412, 1295, 393, 393, 393, 393, 406, 406, 393, 280, 280, 280, 280, 280, 280, 19, 109, 1, -18, '', 0, 33, 24, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 67, 67, 8573, 7946, 2479, 1317, 400, 400, 400, 400, 409, 409, 400, 299, 299, 299, 299, 299, 299, 19, 111, 1, -18, '', 0, 34, 25, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 67, 67, 6047, 0, 1373, 1909, 364, 364, 364, 364, 308, 308, 364, 163, 163, 163, 163, 163, 163, 40, 200, 4, -23, '', 0, 33, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 67, 67, 6315, 0, 1407, 1943, 371, 371, 371, 371, 310, 310, 371, 182, 182, 182, 182, 182, 182, 41, 202, 4, -24, '', 0, 35, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 67, 67, 6583, 0, 1440, 1976, 378, 378, 378, 378, 312, 312, 378, 201, 201, 201, 201, 201, 201, 41, 205, 4, -25, '', 0, 37, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 67, 67, 6851, 0, 1474, 2010, 385, 385, 385, 385, 314, 314, 385, 220, 220, 220, 220, 220, 220, 42, 207, 4, -26, '', 0, 39, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 67, 67, 7119, 0, 1507, 2043, 392, 392, 392, 392, 316, 316, 392, 239, 239, 239, 239, 239, 239, 42, 210, 4, -27, '', 0, 41, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 67, 67, 8057, 0, 1708, 2110, 402, 402, 402, 402, 328, 328, 402, 223, 223, 223, 223, 223, 223, 42, 206, 4, -28, '', 0, 41, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 67, 67, 8325, 0, 1742, 2144, 409, 409, 409, 409, 330, 330, 409, 242, 242, 242, 242, 242, 242, 42, 209, 4, -29, '', 0, 43, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 67, 67, 8593, 0, 1775, 2177, 416, 416, 416, 416, 332, 332, 416, 261, 261, 261, 261, 261, 261, 43, 211, 4, -30, '', 0, 45, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 67, 67, 8861, 0, 1809, 2211, 423, 423, 423, 423, 334, 334, 423, 280, 280, 280, 280, 280, 280, 43, 214, 4, -31, '', 0, 47, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 67, 67, 9129, 0, 1842, 2244, 430, 430, 430, 430, 336, 336, 430, 299, 299, 299, 299, 299, 299, 44, 216, 4, -32, '', 0, 49, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 67, 67, 5160, 5467, 893, 552, 325, 325, 325, 325, 372, 372, 325, 163, 163, 163, 163, 163, 163, 18, 101, 1, -18, '', 0, 23, 18, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 67, 67, 5294, 5668, 915, 569, 332, 332, 332, 332, 375, 375, 332, 182, 182, 182, 182, 182, 182, 18, 102, 1, -18, '', 0, 24, 19, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 67, 67, 5428, 5869, 938, 586, 339, 339, 339, 339, 378, 378, 339, 201, 201, 201, 201, 201, 201, 18, 104, 1, -18, '', 0, 25, 20, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 67, 67, 5562, 6070, 960, 603, 346, 346, 346, 346, 381, 381, 346, 220, 220, 220, 220, 220, 220, 18, 105, 1, -18, '', 0, 26, 21, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 67, 67, 5696, 6271, 982, 619, 353, 353, 353, 353, 384, 384, 353, 239, 239, 239, 239, 239, 239, 19, 106, 1, -18, '', 0, 27, 22, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 67, 67, 6835, 7142, 1094, 619, 363, 363, 363, 363, 397, 397, 363, 223, 223, 223, 223, 223, 223, 18, 105, 1, -18, '', 0, 28, 21, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 67, 67, 6969, 7343, 1116, 636, 370, 370, 370, 370, 400, 400, 370, 242, 242, 242, 242, 242, 242, 19, 106, 1, -18, '', 0, 29, 22, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 67, 67, 7103, 7544, 1139, 653, 377, 377, 377, 377, 403, 403, 377, 261, 261, 261, 261, 261, 261, 19, 108, 1, -18, '', 0, 30, 23, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 67, 67, 7237, 7745, 1161, 670, 384, 384, 384, 384, 406, 406, 384, 280, 280, 280, 280, 280, 280, 19, 109, 1, -18, '', 0, 31, 24, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 67, 67, 7371, 7946, 1183, 686, 391, 391, 391, 391, 409, 409, 391, 299, 299, 299, 299, 299, 299, 19, 111, 1, -18, '', 0, 32, 25, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 68, 68, 7221, 0, 2346, 1904, 377, 377, 377, 377, 316, 316, 377, 166, 166, 166, 166, 166, 166, 44, 223, 4, -24, '', 0, 35, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 68, 68, 7561, 0, 2448, 1938, 384, 384, 384, 384, 318, 318, 384, 186, 186, 186, 186, 186, 186, 44, 226, 4, -25, '', 0, 37, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 68, 68, 7901, 0, 2550, 1972, 391, 391, 391, 391, 320, 320, 391, 206, 206, 206, 206, 206, 206, 45, 229, 4, -26, '', 0, 39, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 68, 68, 8241, 0, 2652, 2006, 398, 398, 398, 398, 322, 322, 398, 226, 226, 226, 226, 226, 226, 45, 232, 4, -27, '', 0, 41, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 68, 68, 8581, 0, 2754, 2040, 405, 405, 405, 405, 324, 324, 405, 246, 246, 246, 246, 246, 246, 46, 235, 4, -28, '', 0, 43, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 68, 68, 9601, 0, 2890, 2074, 416, 416, 416, 416, 336, 336, 416, 228, 228, 228, 228, 228, 228, 45, 230, 4, -29, '', 0, 45, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 68, 68, 9941, 0, 2992, 2108, 423, 423, 423, 423, 338, 338, 423, 248, 248, 248, 248, 248, 248, 45, 233, 4, -30, '', 0, 47, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 68, 68, 10281, 0, 3094, 2142, 430, 430, 430, 430, 340, 340, 430, 268, 268, 268, 268, 268, 268, 46, 236, 4, -31, '', 0, 49, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 68, 68, 10621, 0, 3196, 2176, 437, 437, 437, 437, 342, 342, 437, 288, 288, 288, 288, 288, 288, 46, 239, 4, -32, '', 0, 51, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 68, 68, 10961, 0, 3298, 2210, 444, 444, 444, 444, 344, 344, 444, 308, 308, 308, 308, 308, 308, 47, 242, 4, -33, '', 0, 53, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 68, 68, 5953, 5603, 1836, 1144, 346, 346, 346, 346, 381, 381, 346, 166, 166, 166, 166, 166, 166, 18, 102, 1, -18, '', 0, 25, 18, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 68, 68, 6157, 5807, 1904, 1167, 353, 353, 353, 353, 384, 384, 353, 186, 186, 186, 186, 186, 186, 18, 104, 1, -18, '', 0, 26, 19, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 68, 68, 6361, 6011, 1972, 1190, 360, 360, 360, 360, 387, 387, 360, 206, 206, 206, 206, 206, 206, 18, 105, 1, -18, '', 0, 27, 20, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 68, 68, 6565, 6215, 2040, 1212, 367, 367, 367, 367, 390, 390, 367, 226, 226, 226, 226, 226, 226, 19, 106, 1, -18, '', 0, 28, 21, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 68, 68, 6769, 6419, 2108, 1235, 374, 374, 374, 374, 393, 393, 374, 246, 246, 246, 246, 246, 246, 19, 108, 1, -18, '', 0, 29, 22, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 68, 68, 7993, 7303, 2244, 1246, 385, 385, 385, 385, 406, 406, 385, 228, 228, 228, 228, 228, 228, 19, 106, 1, -18, '', 0, 31, 21, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 68, 68, 8197, 7507, 2312, 1269, 392, 392, 392, 392, 409, 409, 392, 248, 248, 248, 248, 248, 248, 19, 108, 1, -18, '', 0, 32, 22, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 68, 68, 8401, 7711, 2380, 1292, 399, 399, 399, 399, 412, 412, 399, 268, 268, 268, 268, 268, 268, 19, 109, 1, -18, '', 0, 33, 23, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 68, 68, 8605, 7915, 2448, 1314, 406, 406, 406, 406, 415, 415, 406, 288, 288, 288, 288, 288, 288, 19, 111, 1, -18, '', 0, 34, 24, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 68, 68, 8809, 8119, 2516, 1337, 413, 413, 413, 413, 418, 418, 413, 308, 308, 308, 308, 308, 308, 20, 112, 1, -18, '', 0, 35, 25, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 68, 68, 6252, 0, 1394, 1938, 377, 377, 377, 377, 316, 316, 377, 166, 166, 166, 166, 166, 166, 44, 216, 4, -24, '', 0, 33, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 68, 68, 6524, 0, 1428, 1972, 384, 384, 384, 384, 318, 318, 384, 186, 186, 186, 186, 186, 186, 44, 218, 4, -25, '', 0, 35, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 68, 68, 6796, 0, 1462, 2006, 391, 391, 391, 391, 320, 320, 391, 206, 206, 206, 206, 206, 206, 45, 221, 4, -26, '', 0, 37, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 68, 68, 7068, 0, 1496, 2040, 398, 398, 398, 398, 322, 322, 398, 226, 226, 226, 226, 226, 226, 45, 224, 4, -27, '', 0, 39, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 68, 68, 7340, 0, 1530, 2074, 405, 405, 405, 405, 324, 324, 405, 246, 246, 246, 246, 246, 246, 46, 226, 4, -28, '', 0, 41, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 68, 68, 8292, 0, 1734, 2142, 416, 416, 416, 416, 336, 336, 416, 228, 228, 228, 228, 228, 228, 45, 222, 4, -29, '', 0, 41, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 68, 68, 8564, 0, 1768, 2176, 423, 423, 423, 423, 338, 338, 423, 248, 248, 248, 248, 248, 248, 45, 225, 4, -30, '', 0, 43, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 68, 68, 8836, 0, 1802, 2210, 430, 430, 430, 430, 340, 340, 430, 268, 268, 268, 268, 268, 268, 46, 228, 4, -31, '', 0, 45, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 68, 68, 9108, 0, 1836, 2244, 437, 437, 437, 437, 342, 342, 437, 288, 288, 288, 288, 288, 288, 46, 230, 4, -32, '', 0, 47, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 68, 68, 9380, 0, 1870, 2278, 444, 444, 444, 444, 344, 344, 444, 308, 308, 308, 308, 308, 308, 47, 233, 4, -33, '', 0, 49, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 68, 68, 5339, 5603, 906, 561, 337, 337, 337, 337, 381, 381, 337, 166, 166, 166, 166, 166, 166, 18, 102, 1, -18, '', 0, 24, 18, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 68, 68, 5475, 5807, 929, 578, 344, 344, 344, 344, 384, 384, 344, 186, 186, 186, 186, 186, 186, 18, 104, 1, -18, '', 0, 25, 19, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 68, 68, 5611, 6011, 952, 595, 351, 351, 351, 351, 387, 387, 351, 206, 206, 206, 206, 206, 206, 18, 105, 1, -18, '', 0, 26, 20, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 68, 68, 5747, 6215, 974, 612, 358, 358, 358, 358, 390, 390, 358, 226, 226, 226, 226, 226, 226, 19, 106, 1, -18, '', 0, 27, 21, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 68, 68, 5883, 6419, 997, 629, 365, 365, 365, 365, 393, 393, 365, 246, 246, 246, 246, 246, 246, 19, 108, 1, -18, '', 0, 28, 22, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 68, 68, 7039, 7303, 1110, 629, 376, 376, 376, 376, 406, 406, 376, 228, 228, 228, 228, 228, 228, 19, 106, 1, -18, '', 0, 29, 21, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 68, 68, 7175, 7507, 1133, 646, 383, 383, 383, 383, 409, 409, 383, 248, 248, 248, 248, 248, 248, 19, 108, 1, -18, '', 0, 30, 22, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 68, 68, 7311, 7711, 1156, 663, 390, 390, 390, 390, 412, 412, 390, 268, 268, 268, 268, 268, 268, 19, 109, 1, -18, '', 0, 31, 23, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 68, 68, 7447, 7915, 1178, 680, 397, 397, 397, 397, 415, 415, 397, 288, 288, 288, 288, 288, 288, 19, 111, 1, -18, '', 0, 32, 24, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 68, 68, 7583, 8119, 1201, 697, 404, 404, 404, 404, 418, 418, 404, 308, 308, 308, 308, 308, 308, 20, 112, 1, -18, '', 0, 33, 25, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 69, 69, 7462, 0, 2381, 1932, 389, 389, 389, 389, 324, 324, 389, 168, 168, 168, 168, 168, 168, 47, 240, 4, -24, '', 0, 36, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 69, 69, 7807, 0, 2484, 1967, 396, 396, 396, 396, 326, 326, 396, 188, 188, 188, 188, 188, 188, 47, 243, 4, -25, '', 0, 38, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 69, 69, 8152, 0, 2588, 2001, 403, 403, 403, 403, 328, 328, 403, 208, 208, 208, 208, 208, 208, 48, 245, 4, -26, '', 0, 40, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 69, 69, 8497, 0, 2691, 2036, 410, 410, 410, 410, 330, 330, 410, 228, 228, 228, 228, 228, 228, 48, 248, 4, -27, '', 0, 42, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 69, 69, 8842, 0, 2795, 2070, 417, 417, 417, 417, 332, 332, 417, 248, 248, 248, 248, 248, 248, 49, 251, 4, -28, '', 0, 44, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 69, 69, 9877, 0, 2933, 2104, 428, 428, 428, 428, 344, 344, 428, 230, 230, 230, 230, 230, 230, 48, 247, 4, -29, '', 0, 46, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 69, 69, 10222, 0, 3036, 2139, 435, 435, 435, 435, 346, 346, 435, 250, 250, 250, 250, 250, 250, 49, 250, 4, -30, '', 0, 48, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 69, 69, 10567, 0, 3140, 2173, 442, 442, 442, 442, 348, 348, 442, 270, 270, 270, 270, 270, 270, 49, 252, 4, -31, '', 0, 50, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 69, 69, 10912, 0, 3243, 2208, 449, 449, 449, 449, 350, 350, 449, 290, 290, 290, 290, 290, 290, 50, 255, 4, -32, '', 0, 52, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 69, 69, 11257, 0, 3347, 2242, 456, 456, 456, 456, 352, 352, 456, 310, 310, 310, 310, 310, 310, 50, 258, 4, -33, '', 0, 54, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 69, 69, 6151, 5741, 1863, 1162, 357, 357, 357, 357, 389, 389, 357, 168, 168, 168, 168, 168, 168, 18, 104, 1, -18, '', 0, 25, 18, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 69, 69, 6358, 5948, 1932, 1185, 364, 364, 364, 364, 392, 392, 364, 188, 188, 188, 188, 188, 188, 18, 105, 1, -18, '', 0, 26, 19, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 69, 69, 6565, 6155, 2001, 1208, 371, 371, 371, 371, 395, 395, 371, 208, 208, 208, 208, 208, 208, 19, 106, 1, -18, '', 0, 27, 20, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 69, 69, 6772, 6362, 2070, 1231, 378, 378, 378, 378, 398, 398, 378, 228, 228, 228, 228, 228, 228, 19, 108, 1, -18, '', 0, 28, 21, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 69, 69, 6979, 6569, 2139, 1254, 385, 385, 385, 385, 401, 401, 385, 248, 248, 248, 248, 248, 248, 19, 109, 1, -18, '', 0, 29, 22, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 69, 69, 8221, 7466, 2277, 1265, 396, 396, 396, 396, 414, 414, 396, 230, 230, 230, 230, 230, 230, 19, 108, 1, -18, '', 0, 31, 21, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 69, 69, 8428, 7673, 2346, 1288, 403, 403, 403, 403, 417, 417, 403, 250, 250, 250, 250, 250, 250, 19, 109, 1, -18, '', 0, 32, 22, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 69, 69, 8635, 7880, 2415, 1311, 410, 410, 410, 410, 420, 420, 410, 270, 270, 270, 270, 270, 270, 19, 111, 1, -18, '', 0, 33, 23, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 69, 69, 8842, 8087, 2484, 1334, 417, 417, 417, 417, 423, 423, 417, 290, 290, 290, 290, 290, 290, 20, 112, 1, -18, '', 0, 34, 24, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 69, 69, 9049, 8294, 2553, 1357, 424, 424, 424, 424, 426, 426, 424, 310, 310, 310, 310, 310, 310, 20, 113, 1, -18, '', 0, 35, 25, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 69, 69, 6462, 0, 1414, 1966, 389, 389, 389, 389, 324, 324, 389, 168, 168, 168, 168, 168, 168, 47, 232, 4, -24, '', 0, 34, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 69, 69, 6738, 0, 1449, 2001, 396, 396, 396, 396, 326, 326, 396, 188, 188, 188, 188, 188, 188, 47, 235, 4, -25, '', 0, 36, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 69, 69, 7014, 0, 1483, 2035, 403, 403, 403, 403, 328, 328, 403, 208, 208, 208, 208, 208, 208, 48, 237, 4, -26, '', 0, 38, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 69, 69, 7290, 0, 1518, 2070, 410, 410, 410, 410, 330, 330, 410, 228, 228, 228, 228, 228, 228, 48, 240, 4, -27, '', 0, 40, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 69, 69, 7566, 0, 1552, 2104, 417, 417, 417, 417, 332, 332, 417, 248, 248, 248, 248, 248, 248, 49, 243, 4, -28, '', 0, 42, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 69, 69, 8532, 0, 1759, 2173, 428, 428, 428, 428, 344, 344, 428, 230, 230, 230, 230, 230, 230, 48, 239, 4, -29, '', 0, 42, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 69, 69, 8808, 0, 1794, 2208, 435, 435, 435, 435, 346, 346, 435, 250, 250, 250, 250, 250, 250, 49, 241, 4, -30, '', 0, 44, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 69, 69, 9084, 0, 1828, 2242, 442, 442, 442, 442, 348, 348, 442, 270, 270, 270, 270, 270, 270, 49, 244, 4, -31, '', 0, 46, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 69, 69, 9360, 0, 1863, 2277, 449, 449, 449, 449, 350, 350, 449, 290, 290, 290, 290, 290, 290, 50, 246, 4, -32, '', 0, 48, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 69, 69, 9636, 0, 1897, 2311, 456, 456, 456, 456, 352, 352, 456, 310, 310, 310, 310, 310, 310, 50, 249, 4, -33, '', 0, 50, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 69, 69, 5523, 5741, 920, 569, 348, 348, 348, 348, 389, 389, 348, 168, 168, 168, 168, 168, 168, 18, 104, 1, -18, '', 0, 24, 18, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 69, 69, 5661, 5948, 943, 586, 355, 355, 355, 355, 392, 392, 355, 188, 188, 188, 188, 188, 188, 18, 105, 1, -18, '', 0, 25, 19, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 69, 69, 5799, 6155, 966, 603, 362, 362, 362, 362, 395, 395, 362, 208, 208, 208, 208, 208, 208, 19, 106, 1, -18, '', 0, 26, 20, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 69, 69, 5937, 6362, 989, 621, 369, 369, 369, 369, 398, 398, 369, 228, 228, 228, 228, 228, 228, 19, 108, 1, -18, '', 0, 27, 21, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 69, 69, 6075, 6569, 1012, 638, 376, 376, 376, 376, 401, 401, 376, 248, 248, 248, 248, 248, 248, 19, 109, 1, -18, '', 0, 28, 22, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 69, 69, 7248, 7466, 1127, 638, 387, 387, 387, 387, 414, 414, 387, 230, 230, 230, 230, 230, 230, 19, 108, 1, -18, '', 0, 29, 21, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 69, 69, 7386, 7673, 1150, 655, 394, 394, 394, 394, 417, 417, 394, 250, 250, 250, 250, 250, 250, 19, 109, 1, -18, '', 0, 30, 22, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 69, 69, 7524, 7880, 1173, 672, 401, 401, 401, 401, 420, 420, 401, 270, 270, 270, 270, 270, 270, 19, 111, 1, -18, '', 0, 31, 23, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 69, 69, 7662, 8087, 1196, 690, 408, 408, 408, 408, 423, 423, 408, 290, 290, 290, 290, 290, 290, 20, 112, 1, -18, '', 0, 32, 24, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 69, 69, 7800, 8294, 1219, 707, 415, 415, 415, 415, 426, 426, 415, 310, 310, 310, 310, 310, 310, 20, 113, 1, -18, '', 0, 33, 25, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 70, 70, 7710, 0, 2415, 1960, 403, 403, 403, 403, 332, 332, 403, 170, 170, 170, 170, 170, 170, 50, 256, 4, -25, '', 0, 36, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 70, 70, 8060, 0, 2520, 1995, 411, 411, 411, 411, 334, 334, 411, 190, 190, 190, 190, 190, 190, 51, 259, 4, -26, '', 0, 38, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 70, 70, 8410, 0, 2625, 2030, 419, 419, 419, 419, 336, 336, 419, 210, 210, 210, 210, 210, 210, 51, 262, 4, -27, '', 0, 40, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 70, 70, 8760, 0, 2730, 2065, 427, 427, 427, 427, 338, 338, 427, 230, 230, 230, 230, 230, 230, 52, 265, 4, -28, '', 0, 42, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 70, 70, 9110, 0, 2835, 2100, 435, 435, 435, 435, 340, 340, 435, 250, 250, 250, 250, 250, 250, 52, 267, 4, -29, '', 0, 44, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 70, 70, 10160, 0, 2975, 2135, 443, 443, 443, 443, 352, 352, 443, 232, 232, 232, 232, 232, 232, 51, 263, 4, -30, '', 0, 46, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 70, 70, 10510, 0, 3080, 2170, 451, 451, 451, 451, 354, 354, 451, 252, 252, 252, 252, 252, 252, 52, 266, 4, -31, '', 0, 48, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 70, 70, 10860, 0, 3185, 2205, 459, 459, 459, 459, 356, 356, 459, 272, 272, 272, 272, 272, 272, 52, 269, 4, -32, '', 0, 50, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 70, 70, 11210, 0, 3290, 2240, 467, 467, 467, 467, 358, 358, 467, 292, 292, 292, 292, 292, 292, 53, 272, 4, -33, '', 0, 52, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 70, 70, 11560, 0, 3395, 2275, 475, 475, 475, 475, 360, 360, 475, 312, 312, 312, 312, 312, 312, 53, 274, 4, -34, '', 0, 54, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 70, 70, 6355, 5880, 1890, 1178, 370, 370, 370, 370, 398, 398, 370, 170, 170, 170, 170, 170, 170, 18, 105, 1, -18, '', 0, 25, 19, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 70, 70, 6565, 6090, 1960, 1201, 378, 378, 378, 378, 401, 401, 378, 190, 190, 190, 190, 190, 190, 19, 106, 1, -18, '', 0, 26, 20, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 70, 70, 6775, 6300, 2030, 1225, 386, 386, 386, 386, 404, 404, 386, 210, 210, 210, 210, 210, 210, 19, 108, 1, -18, '', 0, 27, 21, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 70, 70, 6985, 6510, 2100, 1248, 394, 394, 394, 394, 407, 407, 394, 230, 230, 230, 230, 230, 230, 19, 109, 1, -18, '', 0, 28, 22, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 70, 70, 7195, 6720, 2170, 1271, 402, 402, 402, 402, 410, 410, 402, 250, 250, 250, 250, 250, 250, 19, 111, 1, -18, '', 0, 29, 23, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 70, 70, 8455, 7630, 2310, 1283, 410, 410, 410, 410, 423, 423, 410, 232, 232, 232, 232, 232, 232, 19, 109, 1, -18, '', 0, 31, 22, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 70, 70, 8665, 7840, 2380, 1306, 418, 418, 418, 418, 426, 426, 418, 252, 252, 252, 252, 252, 252, 19, 111, 1, -18, '', 0, 32, 23, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 70, 70, 8875, 8050, 2450, 1330, 426, 426, 426, 426, 429, 429, 426, 272, 272, 272, 272, 272, 272, 20, 112, 1, -18, '', 0, 33, 24, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 70, 70, 9085, 8260, 2520, 1353, 434, 434, 434, 434, 432, 432, 434, 292, 292, 292, 292, 292, 292, 20, 113, 1, -18, '', 0, 34, 25, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 70, 70, 9295, 8470, 2590, 1376, 442, 442, 442, 442, 435, 435, 442, 312, 312, 312, 312, 312, 312, 20, 115, 1, -18, '', 0, 35, 26, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 70, 70, 6677, 0, 1435, 1995, 403, 403, 403, 403, 332, 332, 403, 170, 170, 170, 170, 170, 170, 50, 248, 4, -25, '', 0, 34, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 70, 70, 6957, 0, 1470, 2030, 411, 411, 411, 411, 334, 334, 411, 190, 190, 190, 190, 190, 190, 51, 251, 4, -26, '', 0, 36, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 70, 70, 7237, 0, 1505, 2065, 419, 419, 419, 419, 336, 336, 419, 210, 210, 210, 210, 210, 210, 51, 254, 4, -27, '', 0, 38, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 70, 70, 7517, 0, 1540, 2100, 427, 427, 427, 427, 338, 338, 427, 230, 230, 230, 230, 230, 230, 52, 256, 4, -28, '', 0, 40, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 70, 70, 7797, 0, 1575, 2135, 435, 435, 435, 435, 340, 340, 435, 250, 250, 250, 250, 250, 250, 52, 259, 4, -29, '', 0, 42, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 70, 70, 8777, 0, 1785, 2205, 443, 443, 443, 443, 352, 352, 443, 232, 232, 232, 232, 232, 232, 51, 255, 4, -30, '', 0, 42, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 70, 70, 9057, 0, 1820, 2240, 451, 451, 451, 451, 354, 354, 451, 252, 252, 252, 252, 252, 252, 52, 258, 4, -31, '', 0, 44, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 70, 70, 9337, 0, 1855, 2275, 459, 459, 459, 459, 356, 356, 459, 272, 272, 272, 272, 272, 272, 52, 260, 4, -32, '', 0, 46, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 70, 70, 9617, 0, 1890, 2310, 467, 467, 467, 467, 358, 358, 467, 292, 292, 292, 292, 292, 292, 53, 263, 4, -33, '', 0, 48, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 70, 70, 9897, 0, 1925, 2345, 475, 475, 475, 475, 360, 360, 475, 312, 312, 312, 312, 312, 312, 53, 265, 4, -34, '', 0, 50, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 70, 70, 5711, 5880, 933, 577, 361, 361, 361, 361, 398, 398, 361, 170, 170, 170, 170, 170, 170, 18, 105, 1, -18, '', 0, 24, 19, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 70, 70, 5851, 6090, 956, 595, 369, 369, 369, 369, 401, 401, 369, 190, 190, 190, 190, 190, 190, 19, 106, 1, -18, '', 0, 25, 20, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 70, 70, 5991, 6300, 980, 612, 377, 377, 377, 377, 404, 404, 377, 210, 210, 210, 210, 210, 210, 19, 108, 1, -18, '', 0, 26, 21, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 70, 70, 6131, 6510, 1003, 630, 385, 385, 385, 385, 407, 407, 385, 230, 230, 230, 230, 230, 230, 19, 109, 1, -18, '', 0, 27, 22, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 70, 70, 6271, 6720, 1026, 647, 393, 393, 393, 393, 410, 410, 393, 250, 250, 250, 250, 250, 250, 19, 111, 1, -18, '', 0, 28, 23, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 70, 70, 7461, 7630, 1143, 647, 401, 401, 401, 401, 423, 423, 401, 232, 232, 232, 232, 232, 232, 19, 109, 1, -18, '', 0, 29, 22, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 70, 70, 7601, 7840, 1166, 665, 409, 409, 409, 409, 426, 426, 409, 252, 252, 252, 252, 252, 252, 19, 111, 1, -18, '', 0, 30, 23, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 70, 70, 7741, 8050, 1190, 682, 417, 417, 417, 417, 429, 429, 417, 272, 272, 272, 272, 272, 272, 20, 112, 1, -18, '', 0, 31, 24, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 70, 70, 7881, 8260, 1213, 700, 425, 425, 425, 425, 432, 432, 425, 292, 292, 292, 292, 292, 292, 20, 113, 1, -18, '', 0, 32, 25, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 70, 70, 8021, 8470, 1236, 717, 433, 433, 433, 433, 435, 435, 433, 312, 312, 312, 312, 312, 312, 20, 115, 1, -18, '', 0, 33, 26, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 71, 71, 7963, 0, 2450, 1988, 415, 415, 415, 415, 340, 340, 415, 172, 172, 172, 172, 172, 172, 53, 273, 4, -25, '', 0, 36, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 71, 71, 8318, 0, 2556, 2024, 423, 423, 423, 423, 342, 342, 423, 192, 192, 192, 192, 192, 192, 54, 275, 4, -26, '', 0, 38, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 71, 71, 8673, 0, 2663, 2059, 431, 431, 431, 431, 344, 344, 431, 212, 212, 212, 212, 212, 212, 54, 278, 4, -27, '', 0, 40, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 71, 71, 9028, 0, 2769, 2095, 439, 439, 439, 439, 346, 346, 439, 232, 232, 232, 232, 232, 232, 55, 281, 4, -28, '', 0, 42, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 71, 71, 9383, 0, 2876, 2130, 447, 447, 447, 447, 348, 348, 447, 252, 252, 252, 252, 252, 252, 55, 284, 4, -29, '', 0, 44, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 71, 71, 10448, 0, 3018, 2165, 455, 455, 455, 455, 360, 360, 455, 236, 236, 236, 236, 236, 236, 55, 280, 4, -30, '', 0, 46, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 71, 71, 10803, 0, 3124, 2201, 463, 463, 463, 463, 362, 362, 463, 256, 256, 256, 256, 256, 256, 55, 282, 4, -31, '', 0, 48, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 71, 71, 11158, 0, 3231, 2236, 471, 471, 471, 471, 364, 364, 471, 276, 276, 276, 276, 276, 276, 56, 285, 4, -32, '', 0, 50, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 71, 71, 11513, 0, 3337, 2272, 479, 479, 479, 479, 366, 366, 479, 296, 296, 296, 296, 296, 296, 56, 288, 4, -33, '', 0, 52, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 71, 71, 11868, 0, 3444, 2307, 487, 487, 487, 487, 368, 368, 487, 316, 316, 316, 316, 316, 316, 57, 291, 4, -34, '', 0, 54, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 71, 71, 6563, 6021, 1917, 1195, 382, 382, 382, 382, 406, 406, 382, 172, 172, 172, 172, 172, 172, 19, 106, 1, -18, '', 0, 25, 19, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 71, 71, 6776, 6234, 1988, 1219, 390, 390, 390, 390, 409, 409, 390, 192, 192, 192, 192, 192, 192, 19, 108, 1, -18, '', 0, 26, 20, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 71, 71, 6989, 6447, 2059, 1243, 398, 398, 398, 398, 412, 412, 398, 212, 212, 212, 212, 212, 212, 19, 109, 1, -18, '', 0, 27, 21, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 71, 71, 7202, 6660, 2130, 1266, 406, 406, 406, 406, 415, 415, 406, 232, 232, 232, 232, 232, 232, 19, 111, 1, -18, '', 0, 28, 22, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 71, 71, 7415, 6873, 2201, 1290, 414, 414, 414, 414, 418, 418, 414, 252, 252, 252, 252, 252, 252, 20, 112, 1, -18, '', 0, 29, 23, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 71, 71, 8693, 7796, 2343, 1301, 422, 422, 422, 422, 431, 431, 422, 236, 236, 236, 236, 236, 236, 19, 111, 1, -18, '', 0, 31, 22, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 71, 71, 8906, 8009, 2414, 1325, 430, 430, 430, 430, 434, 434, 430, 256, 256, 256, 256, 256, 256, 20, 112, 1, -18, '', 0, 32, 23, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 71, 71, 9119, 8222, 2485, 1349, 438, 438, 438, 438, 437, 437, 438, 276, 276, 276, 276, 276, 276, 20, 113, 1, -18, '', 0, 33, 24, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 71, 71, 9332, 8435, 2556, 1372, 446, 446, 446, 446, 440, 440, 446, 296, 296, 296, 296, 296, 296, 20, 115, 1, -18, '', 0, 34, 25, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 71, 71, 9545, 8648, 2627, 1396, 454, 454, 454, 454, 443, 443, 454, 316, 316, 316, 316, 316, 316, 20, 116, 1, -18, '', 0, 35, 26, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 71, 71, 6897, 0, 1455, 2023, 415, 415, 415, 415, 340, 340, 415, 172, 172, 172, 172, 172, 172, 53, 265, 4, -25, '', 0, 34, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 71, 71, 7181, 0, 1491, 2059, 423, 423, 423, 423, 342, 342, 423, 192, 192, 192, 192, 192, 192, 54, 267, 4, -26, '', 0, 36, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 71, 71, 7465, 0, 1526, 2094, 431, 431, 431, 431, 344, 344, 431, 212, 212, 212, 212, 212, 212, 54, 270, 4, -27, '', 0, 38, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 71, 71, 7749, 0, 1562, 2130, 439, 439, 439, 439, 346, 346, 439, 232, 232, 232, 232, 232, 232, 55, 273, 4, -28, '', 0, 40, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 71, 71, 8033, 0, 1597, 2165, 447, 447, 447, 447, 348, 348, 447, 252, 252, 252, 252, 252, 252, 55, 275, 4, -29, '', 0, 42, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 71, 71, 9027, 0, 1810, 2236, 455, 455, 455, 455, 360, 360, 455, 236, 236, 236, 236, 236, 236, 55, 271, 4, -30, '', 0, 42, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 71, 71, 9311, 0, 1846, 2272, 463, 463, 463, 463, 362, 362, 463, 256, 256, 256, 256, 256, 256, 55, 274, 4, -31, '', 0, 44, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 71, 71, 9595, 0, 1881, 2307, 471, 471, 471, 471, 364, 364, 471, 276, 276, 276, 276, 276, 276, 56, 276, 4, -32, '', 0, 46, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 71, 71, 9879, 0, 1917, 2343, 479, 479, 479, 479, 366, 366, 479, 296, 296, 296, 296, 296, 296, 56, 279, 4, -33, '', 0, 48, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 71, 71, 10163, 0, 1952, 2378, 487, 487, 487, 487, 368, 368, 487, 316, 316, 316, 316, 316, 316, 57, 282, 4, -34, '', 0, 50, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 71, 71, 5903, 6021, 946, 585, 372, 372, 372, 372, 406, 406, 372, 172, 172, 172, 172, 172, 172, 19, 106, 1, -18, '', 0, 24, 19, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 71, 71, 6045, 6234, 970, 603, 380, 380, 380, 380, 409, 409, 380, 192, 192, 192, 192, 192, 192, 19, 108, 1, -18, '', 0, 25, 20, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 71, 71, 6187, 6447, 994, 621, 388, 388, 388, 388, 412, 412, 388, 212, 212, 212, 212, 212, 212, 19, 109, 1, -18, '', 0, 26, 21, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 71, 71, 6329, 6660, 1017, 639, 396, 396, 396, 396, 415, 415, 396, 232, 232, 232, 232, 232, 232, 19, 111, 1, -18, '', 0, 27, 22, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 71, 71, 6471, 6873, 1041, 656, 404, 404, 404, 404, 418, 418, 404, 252, 252, 252, 252, 252, 252, 20, 112, 1, -18, '', 0, 28, 23, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 71, 71, 7678, 7796, 1159, 656, 412, 412, 412, 412, 431, 431, 412, 236, 236, 236, 236, 236, 236, 19, 111, 1, -18, '', 0, 29, 22, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 71, 71, 7820, 8009, 1183, 674, 420, 420, 420, 420, 434, 434, 420, 256, 256, 256, 256, 256, 256, 20, 112, 1, -18, '', 0, 30, 23, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 71, 71, 7962, 8222, 1207, 692, 428, 428, 428, 428, 437, 437, 428, 276, 276, 276, 276, 276, 276, 20, 113, 1, -18, '', 0, 31, 24, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 71, 71, 8104, 8435, 1230, 710, 436, 436, 436, 436, 440, 440, 436, 296, 296, 296, 296, 296, 296, 20, 115, 1, -18, '', 0, 32, 25, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 71, 71, 8246, 8648, 1254, 727, 444, 444, 444, 444, 443, 443, 444, 316, 316, 316, 316, 316, 316, 20, 116, 1, -18, '', 0, 33, 26, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 72, 72, 8222, 0, 2484, 2016, 428, 428, 428, 428, 348, 348, 428, 175, 175, 175, 175, 175, 175, 57, 289, 4, -26, '', 0, 37, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 72, 72, 8582, 0, 2592, 2052, 436, 436, 436, 436, 350, 350, 436, 196, 196, 196, 196, 196, 196, 57, 292, 4, -27, '', 0, 39, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 72, 72, 8942, 0, 2700, 2088, 444, 444, 444, 444, 352, 352, 444, 217, 217, 217, 217, 217, 217, 58, 295, 4, -28, '', 0, 41, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 72, 72, 9302, 0, 2808, 2124, 452, 452, 452, 452, 354, 354, 452, 238, 238, 238, 238, 238, 238, 58, 297, 4, -29, '', 0, 43, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 72, 72, 9662, 0, 2916, 2160, 460, 460, 460, 460, 356, 356, 460, 259, 259, 259, 259, 259, 259, 59, 300, 4, -30, '', 0, 45, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 72, 72, 10742, 0, 3060, 2196, 469, 469, 469, 469, 368, 368, 469, 239, 239, 239, 239, 239, 239, 58, 296, 4, -31, '', 0, 47, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 72, 72, 11102, 0, 3168, 2232, 477, 477, 477, 477, 370, 370, 477, 260, 260, 260, 260, 260, 260, 58, 299, 4, -32, '', 0, 49, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 72, 72, 11462, 0, 3276, 2268, 485, 485, 485, 485, 372, 372, 485, 281, 281, 281, 281, 281, 281, 59, 302, 4, -33, '', 0, 51, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 72, 72, 11822, 0, 3384, 2304, 493, 493, 493, 493, 374, 374, 493, 302, 302, 302, 302, 302, 302, 59, 304, 4, -34, '', 0, 53, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 72, 72, 12182, 0, 3492, 2340, 501, 501, 501, 501, 376, 376, 501, 323, 323, 323, 323, 323, 323, 60, 307, 4, -35, '', 0, 55, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 72, 72, 6777, 6163, 1944, 1212, 394, 394, 394, 394, 415, 415, 394, 175, 175, 175, 175, 175, 175, 19, 108, 1, -18, '', 0, 26, 19, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 72, 72, 6993, 6379, 2016, 1236, 402, 402, 402, 402, 418, 418, 402, 196, 196, 196, 196, 196, 196, 19, 109, 1, -18, '', 0, 27, 20, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 72, 72, 7209, 6595, 2088, 1260, 410, 410, 410, 410, 421, 421, 410, 217, 217, 217, 217, 217, 217, 19, 111, 1, -18, '', 0, 28, 21, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 72, 72, 7425, 6811, 2160, 1284, 418, 418, 418, 418, 424, 424, 418, 238, 238, 238, 238, 238, 238, 20, 112, 1, -18, '', 0, 29, 22, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 72, 72, 7641, 7027, 2232, 1308, 426, 426, 426, 426, 427, 427, 426, 259, 259, 259, 259, 259, 259, 20, 113, 1, -18, '', 0, 30, 23, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 72, 72, 8937, 7963, 2376, 1320, 435, 435, 435, 435, 440, 440, 435, 239, 239, 239, 239, 239, 239, 20, 112, 1, -18, '', 0, 32, 22, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 72, 72, 9153, 8179, 2448, 1344, 443, 443, 443, 443, 443, 443, 443, 260, 260, 260, 260, 260, 260, 20, 113, 1, -18, '', 0, 33, 23, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 72, 72, 9369, 8395, 2520, 1368, 451, 451, 451, 451, 446, 446, 451, 281, 281, 281, 281, 281, 281, 20, 115, 1, -18, '', 0, 34, 24, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 72, 72, 9585, 8611, 2592, 1392, 459, 459, 459, 459, 449, 449, 459, 302, 302, 302, 302, 302, 302, 20, 116, 1, -18, '', 0, 35, 25, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 72, 72, 9801, 8827, 2664, 1416, 467, 467, 467, 467, 452, 452, 467, 323, 323, 323, 323, 323, 323, 21, 118, 1, -18, '', 0, 36, 26, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 72, 72, 7123, 0, 1476, 2052, 428, 428, 428, 428, 348, 348, 428, 175, 175, 175, 175, 175, 175, 57, 281, 4, -26, '', 0, 35, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 72, 72, 7411, 0, 1512, 2088, 436, 436, 436, 436, 350, 350, 436, 196, 196, 196, 196, 196, 196, 57, 284, 4, -27, '', 0, 37, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 72, 72, 7699, 0, 1548, 2124, 444, 444, 444, 444, 352, 352, 444, 217, 217, 217, 217, 217, 217, 58, 286, 4, -28, '', 0, 39, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 72, 72, 7987, 0, 1584, 2160, 452, 452, 452, 452, 354, 354, 452, 238, 238, 238, 238, 238, 238, 58, 289, 4, -29, '', 0, 41, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 72, 72, 8275, 0, 1620, 2196, 460, 460, 460, 460, 356, 356, 460, 259, 259, 259, 259, 259, 259, 59, 291, 4, -30, '', 0, 43, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 72, 72, 9283, 0, 1836, 2268, 469, 469, 469, 469, 368, 368, 469, 239, 239, 239, 239, 239, 239, 58, 288, 4, -31, '', 0, 43, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 72, 72, 9571, 0, 1872, 2304, 477, 477, 477, 477, 370, 370, 477, 260, 260, 260, 260, 260, 260, 58, 290, 4, -32, '', 0, 45, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 72, 72, 9859, 0, 1908, 2340, 485, 485, 485, 485, 372, 372, 485, 281, 281, 281, 281, 281, 281, 59, 293, 4, -33, '', 0, 47, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 72, 72, 10147, 0, 1944, 2376, 493, 493, 493, 493, 374, 374, 493, 302, 302, 302, 302, 302, 302, 59, 295, 4, -34, '', 0, 49, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 72, 72, 10435, 0, 1980, 2412, 501, 501, 501, 501, 376, 376, 501, 323, 323, 323, 323, 323, 323, 60, 298, 4, -35, '', 0, 51, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 72, 72, 6101, 6163, 960, 594, 384, 384, 384, 384, 415, 415, 384, 175, 175, 175, 175, 175, 175, 19, 108, 1, -18, '', 0, 25, 19, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 72, 72, 6245, 6379, 984, 612, 392, 392, 392, 392, 418, 418, 392, 196, 196, 196, 196, 196, 196, 19, 109, 1, -18, '', 0, 26, 20, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 72, 72, 6389, 6595, 1008, 630, 400, 400, 400, 400, 421, 421, 400, 217, 217, 217, 217, 217, 217, 19, 111, 1, -18, '', 0, 27, 21, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 72, 72, 6533, 6811, 1032, 648, 408, 408, 408, 408, 424, 424, 408, 238, 238, 238, 238, 238, 238, 20, 112, 1, -18, '', 0, 28, 22, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 72, 72, 6677, 7027, 1056, 666, 416, 416, 416, 416, 427, 427, 416, 259, 259, 259, 259, 259, 259, 20, 113, 1, -18, '', 0, 29, 23, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 72, 72, 7901, 7963, 1176, 666, 425, 425, 425, 425, 440, 440, 425, 239, 239, 239, 239, 239, 239, 20, 112, 1, -18, '', 0, 30, 22, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 72, 72, 8045, 8179, 1200, 684, 433, 433, 433, 433, 443, 443, 433, 260, 260, 260, 260, 260, 260, 20, 113, 1, -18, '', 0, 31, 23, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 72, 72, 8189, 8395, 1224, 702, 441, 441, 441, 441, 446, 446, 441, 281, 281, 281, 281, 281, 281, 20, 115, 1, -18, '', 0, 32, 24, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 72, 72, 8333, 8611, 1248, 720, 449, 449, 449, 449, 449, 449, 449, 302, 302, 302, 302, 302, 302, 20, 116, 1, -18, '', 0, 33, 25, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 72, 72, 8477, 8827, 1272, 738, 457, 457, 457, 457, 452, 452, 457, 323, 323, 323, 323, 323, 323, 21, 118, 1, -18, '', 0, 34, 26, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 73, 73, 8487, 0, 2519, 2044, 440, 440, 440, 440, 356, 356, 440, 177, 177, 177, 177, 177, 177, 60, 305, 4, -26, '', 0, 37, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 73, 73, 8852, 0, 2628, 2081, 448, 448, 448, 448, 358, 358, 448, 198, 198, 198, 198, 198, 198, 60, 308, 4, -27, '', 0, 39, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 73, 73, 9217, 0, 2738, 2117, 456, 456, 456, 456, 360, 360, 456, 219, 219, 219, 219, 219, 219, 61, 311, 4, -28, '', 0, 41, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 73, 73, 9582, 0, 2847, 2154, 464, 464, 464, 464, 362, 362, 464, 240, 240, 240, 240, 240, 240, 61, 314, 4, -29, '', 0, 43, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 73, 73, 9947, 0, 2957, 2190, 472, 472, 472, 472, 364, 364, 472, 261, 261, 261, 261, 261, 261, 62, 317, 4, -30, '', 0, 45, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 73, 73, 11042, 0, 3103, 2226, 481, 481, 481, 481, 376, 376, 481, 241, 241, 241, 241, 241, 241, 61, 312, 4, -31, '', 0, 47, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 73, 73, 11407, 0, 3212, 2263, 489, 489, 489, 489, 378, 378, 489, 262, 262, 262, 262, 262, 262, 62, 315, 4, -32, '', 0, 49, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 73, 73, 11772, 0, 3322, 2299, 497, 497, 497, 497, 380, 380, 497, 283, 283, 283, 283, 283, 283, 62, 318, 4, -33, '', 0, 51, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 73, 73, 12137, 0, 3431, 2336, 505, 505, 505, 505, 382, 382, 505, 304, 304, 304, 304, 304, 304, 63, 321, 4, -34, '', 0, 53, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 73, 73, 12502, 0, 3541, 2372, 513, 513, 513, 513, 384, 384, 513, 325, 325, 325, 325, 325, 325, 63, 324, 4, -35, '', 0, 55, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 73, 73, 6995, 6307, 1971, 1229, 406, 406, 406, 406, 423, 423, 406, 177, 177, 177, 177, 177, 177, 19, 109, 1, -18, '', 0, 26, 19, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 73, 73, 7214, 6526, 2044, 1253, 414, 414, 414, 414, 426, 426, 414, 198, 198, 198, 198, 198, 198, 19, 111, 1, -18, '', 0, 27, 20, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 73, 73, 7433, 6745, 2117, 1278, 422, 422, 422, 422, 429, 429, 422, 219, 219, 219, 219, 219, 219, 20, 112, 1, -18, '', 0, 28, 21, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 73, 73, 7652, 6964, 2190, 1302, 430, 430, 430, 430, 432, 432, 430, 240, 240, 240, 240, 240, 240, 20, 113, 1, -18, '', 0, 29, 22, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 73, 73, 7871, 7183, 2263, 1326, 438, 438, 438, 438, 435, 435, 438, 261, 261, 261, 261, 261, 261, 20, 115, 1, -18, '', 0, 30, 23, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 73, 73, 9185, 8132, 2409, 1338, 447, 447, 447, 447, 448, 448, 447, 241, 241, 241, 241, 241, 241, 20, 113, 1, -18, '', 0, 32, 22, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 73, 73, 9404, 8351, 2482, 1362, 455, 455, 455, 455, 451, 451, 455, 262, 262, 262, 262, 262, 262, 20, 115, 1, -18, '', 0, 33, 23, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 73, 73, 9623, 8570, 2555, 1387, 463, 463, 463, 463, 454, 454, 463, 283, 283, 283, 283, 283, 283, 20, 116, 1, -18, '', 0, 34, 24, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 73, 73, 9842, 8789, 2628, 1411, 471, 471, 471, 471, 457, 457, 471, 304, 304, 304, 304, 304, 304, 21, 118, 1, -18, '', 0, 35, 25, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 73, 73, 10061, 9008, 2701, 1435, 479, 479, 479, 479, 460, 460, 479, 325, 325, 325, 325, 325, 325, 21, 119, 1, -18, '', 0, 36, 26, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 73, 73, 7354, 0, 1496, 2080, 440, 440, 440, 440, 356, 356, 440, 177, 177, 177, 177, 177, 177, 60, 297, 4, -26, '', 0, 35, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 73, 73, 7646, 0, 1533, 2117, 448, 448, 448, 448, 358, 358, 448, 198, 198, 198, 198, 198, 198, 60, 300, 4, -27, '', 0, 37, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 73, 73, 7938, 0, 1569, 2153, 456, 456, 456, 456, 360, 360, 456, 219, 219, 219, 219, 219, 219, 61, 303, 4, -28, '', 0, 39, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 73, 73, 8230, 0, 1606, 2190, 464, 464, 464, 464, 362, 362, 464, 240, 240, 240, 240, 240, 240, 61, 305, 4, -29, '', 0, 41, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 73, 73, 8522, 0, 1642, 2226, 472, 472, 472, 472, 364, 364, 472, 261, 261, 261, 261, 261, 261, 62, 308, 4, -30, '', 0, 43, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 73, 73, 9544, 0, 1861, 2299, 481, 481, 481, 481, 376, 376, 481, 241, 241, 241, 241, 241, 241, 61, 304, 4, -31, '', 0, 43, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 73, 73, 9836, 0, 1898, 2336, 489, 489, 489, 489, 378, 378, 489, 262, 262, 262, 262, 262, 262, 62, 306, 4, -32, '', 0, 45, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 73, 73, 10128, 0, 1934, 2372, 497, 497, 497, 497, 380, 380, 497, 283, 283, 283, 283, 283, 283, 62, 309, 4, -33, '', 0, 47, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 73, 73, 10420, 0, 1971, 2409, 505, 505, 505, 505, 382, 382, 505, 304, 304, 304, 304, 304, 304, 63, 312, 4, -34, '', 0, 49, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 73, 73, 10712, 0, 2007, 2445, 513, 513, 513, 513, 384, 384, 513, 325, 325, 325, 325, 325, 325, 63, 314, 4, -35, '', 0, 51, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 73, 73, 6303, 6307, 973, 602, 395, 395, 395, 395, 423, 423, 395, 177, 177, 177, 177, 177, 177, 19, 109, 1, -18, '', 0, 25, 19, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 73, 73, 6449, 6526, 997, 620, 403, 403, 403, 403, 426, 426, 403, 198, 198, 198, 198, 198, 198, 19, 111, 1, -18, '', 0, 26, 20, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 73, 73, 6595, 6745, 1022, 638, 411, 411, 411, 411, 429, 429, 411, 219, 219, 219, 219, 219, 219, 20, 112, 1, -18, '', 0, 27, 21, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 73, 73, 6741, 6964, 1046, 657, 419, 419, 419, 419, 432, 432, 419, 240, 240, 240, 240, 240, 240, 20, 113, 1, -18, '', 0, 28, 22, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 73, 73, 6887, 7183, 1070, 675, 427, 427, 427, 427, 435, 435, 427, 261, 261, 261, 261, 261, 261, 20, 115, 1, -18, '', 0, 29, 23, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 73, 73, 8128, 8132, 1192, 675, 436, 436, 436, 436, 448, 448, 436, 241, 241, 241, 241, 241, 241, 20, 113, 1, -18, '', 0, 30, 22, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 73, 73, 8274, 8351, 1216, 693, 444, 444, 444, 444, 451, 451, 444, 262, 262, 262, 262, 262, 262, 20, 115, 1, -18, '', 0, 31, 23, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 73, 73, 8420, 8570, 1241, 711, 452, 452, 452, 452, 454, 454, 452, 283, 283, 283, 283, 283, 283, 20, 116, 1, -18, '', 0, 32, 24, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 73, 73, 8566, 8789, 1265, 730, 460, 460, 460, 460, 457, 457, 460, 304, 304, 304, 304, 304, 304, 21, 118, 1, -18, '', 0, 33, 25, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 73, 73, 8712, 9008, 1289, 748, 468, 468, 468, 468, 460, 460, 468, 325, 325, 325, 325, 325, 325, 21, 119, 1, -18, '', 0, 34, 26, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 74, 74, 8758, 0, 2553, 2072, 453, 453, 453, 453, 364, 364, 453, 179, 179, 179, 179, 179, 179, 63, 322, 4, -27, '', 0, 37, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 74, 74, 9128, 0, 2664, 2109, 461, 461, 461, 461, 366, 366, 461, 200, 200, 200, 200, 200, 200, 64, 325, 4, -28, '', 0, 39, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 74, 74, 9498, 0, 2775, 2146, 469, 469, 469, 469, 368, 368, 469, 221, 221, 221, 221, 221, 221, 64, 327, 4, -29, '', 0, 41, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 74, 74, 9868, 0, 2886, 2183, 477, 477, 477, 477, 370, 370, 477, 242, 242, 242, 242, 242, 242, 65, 330, 4, -30, '', 0, 43, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 74, 74, 10238, 0, 2997, 2220, 485, 485, 485, 485, 372, 372, 485, 263, 263, 263, 263, 263, 263, 65, 333, 4, -31, '', 0, 45, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 74, 74, 11348, 0, 3145, 2257, 495, 495, 495, 495, 384, 384, 495, 245, 245, 245, 245, 245, 245, 64, 329, 4, -32, '', 0, 47, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 74, 74, 11718, 0, 3256, 2294, 503, 503, 503, 503, 386, 386, 503, 266, 266, 266, 266, 266, 266, 65, 332, 4, -33, '', 0, 49, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 74, 74, 12088, 0, 3367, 2331, 511, 511, 511, 511, 388, 388, 511, 287, 287, 287, 287, 287, 287, 65, 334, 4, -34, '', 0, 51, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 74, 74, 12458, 0, 3478, 2368, 519, 519, 519, 519, 390, 390, 519, 308, 308, 308, 308, 308, 308, 66, 337, 4, -35, '', 0, 53, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 74, 74, 12828, 0, 3589, 2405, 527, 527, 527, 527, 392, 392, 527, 329, 329, 329, 329, 329, 329, 66, 340, 4, -36, '', 0, 55, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 74, 74, 7219, 6453, 1998, 1245, 418, 418, 418, 418, 432, 432, 418, 179, 179, 179, 179, 179, 179, 19, 111, 1, -18, '', 0, 26, 19, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 74, 74, 7441, 6675, 2072, 1270, 426, 426, 426, 426, 435, 435, 426, 200, 200, 200, 200, 200, 200, 20, 112, 1, -18, '', 0, 27, 20, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 74, 74, 7663, 6897, 2146, 1295, 434, 434, 434, 434, 438, 438, 434, 221, 221, 221, 221, 221, 221, 20, 113, 1, -18, '', 0, 28, 21, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 74, 74, 7885, 7119, 2220, 1319, 442, 442, 442, 442, 441, 441, 442, 242, 242, 242, 242, 242, 242, 20, 115, 1, -18, '', 0, 29, 22, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 74, 74, 8107, 7341, 2294, 1344, 450, 450, 450, 450, 444, 444, 450, 263, 263, 263, 263, 263, 263, 20, 116, 1, -18, '', 0, 30, 23, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 74, 74, 9439, 8303, 2442, 1356, 460, 460, 460, 460, 457, 457, 460, 245, 245, 245, 245, 245, 245, 20, 115, 1, -18, '', 0, 32, 22, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 74, 74, 9661, 8525, 2516, 1381, 468, 468, 468, 468, 460, 460, 468, 266, 266, 266, 266, 266, 266, 20, 116, 1, -18, '', 0, 33, 23, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 74, 74, 9883, 8747, 2590, 1406, 476, 476, 476, 476, 463, 463, 476, 287, 287, 287, 287, 287, 287, 21, 118, 1, -18, '', 0, 34, 24, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 74, 74, 10105, 8969, 2664, 1430, 484, 484, 484, 484, 466, 466, 484, 308, 308, 308, 308, 308, 308, 21, 119, 1, -18, '', 0, 35, 25, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 74, 74, 10327, 9191, 2738, 1455, 492, 492, 492, 492, 469, 469, 492, 329, 329, 329, 329, 329, 329, 21, 120, 1, -18, '', 0, 36, 26, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 74, 74, 7591, 0, 1517, 2109, 453, 453, 453, 453, 364, 364, 453, 179, 179, 179, 179, 179, 179, 63, 314, 4, -27, '', 0, 35, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 74, 74, 7887, 0, 1554, 2146, 461, 461, 461, 461, 366, 366, 461, 200, 200, 200, 200, 200, 200, 64, 316, 4, -28, '', 0, 37, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 74, 74, 8183, 0, 1591, 2183, 469, 469, 469, 469, 368, 368, 469, 221, 221, 221, 221, 221, 221, 64, 319, 4, -29, '', 0, 39, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 74, 74, 8479, 0, 1628, 2220, 477, 477, 477, 477, 370, 370, 477, 242, 242, 242, 242, 242, 242, 65, 321, 4, -30, '', 0, 41, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 74, 74, 8775, 0, 1665, 2257, 485, 485, 485, 485, 372, 372, 485, 263, 263, 263, 263, 263, 263, 65, 324, 4, -31, '', 0, 43, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 74, 74, 9811, 0, 1887, 2331, 495, 495, 495, 495, 384, 384, 495, 245, 245, 245, 245, 245, 245, 64, 320, 4, -32, '', 0, 43, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 74, 74, 10107, 0, 1924, 2368, 503, 503, 503, 503, 386, 386, 503, 266, 266, 266, 266, 266, 266, 65, 323, 4, -33, '', 0, 45, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 74, 74, 10403, 0, 1961, 2405, 511, 511, 511, 511, 388, 388, 511, 287, 287, 287, 287, 287, 287, 65, 325, 4, -34, '', 0, 47, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 74, 74, 10699, 0, 1998, 2442, 519, 519, 519, 519, 390, 390, 519, 308, 308, 308, 308, 308, 308, 66, 328, 4, -35, '', 0, 49, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 74, 74, 10995, 0, 2035, 2479, 527, 527, 527, 527, 392, 392, 527, 329, 329, 329, 329, 329, 329, 66, 331, 4, -36, '', 0, 51, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 74, 74, 6510, 6453, 986, 610, 408, 408, 408, 408, 432, 432, 408, 179, 179, 179, 179, 179, 179, 19, 111, 1, -18, '', 0, 25, 19, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 74, 74, 6658, 6675, 1011, 629, 416, 416, 416, 416, 435, 435, 416, 200, 200, 200, 200, 200, 200, 20, 112, 1, -18, '', 0, 26, 20, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 74, 74, 6806, 6897, 1036, 647, 424, 424, 424, 424, 438, 438, 424, 221, 221, 221, 221, 221, 221, 20, 113, 1, -18, '', 0, 27, 21, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 74, 74, 6954, 7119, 1060, 666, 432, 432, 432, 432, 441, 441, 432, 242, 242, 242, 242, 242, 242, 20, 115, 1, -18, '', 0, 28, 22, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 74, 74, 7102, 7341, 1085, 684, 440, 440, 440, 440, 444, 444, 440, 263, 263, 263, 263, 263, 263, 20, 116, 1, -18, '', 0, 29, 23, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 74, 74, 8360, 8303, 1208, 684, 450, 450, 450, 450, 457, 457, 450, 245, 245, 245, 245, 245, 245, 20, 115, 1, -18, '', 0, 30, 22, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 74, 74, 8508, 8525, 1233, 703, 458, 458, 458, 458, 460, 460, 458, 266, 266, 266, 266, 266, 266, 20, 116, 1, -18, '', 0, 31, 23, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 74, 74, 8656, 8747, 1258, 721, 466, 466, 466, 466, 463, 463, 466, 287, 287, 287, 287, 287, 287, 21, 118, 1, -18, '', 0, 32, 24, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 74, 74, 8804, 8969, 1282, 740, 474, 474, 474, 474, 466, 466, 474, 308, 308, 308, 308, 308, 308, 21, 119, 1, -18, '', 0, 33, 25, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 74, 74, 8952, 9191, 1307, 758, 482, 482, 482, 482, 469, 469, 482, 329, 329, 329, 329, 329, 329, 21, 120, 1, -18, '', 0, 34, 26, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 75, 75, 9036, 0, 2588, 2100, 466, 466, 466, 466, 372, 372, 466, 181, 181, 181, 181, 181, 181, 66, 338, 4, -27, '', 0, 38, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 75, 75, 9411, 0, 2700, 2138, 474, 474, 474, 474, 374, 374, 474, 202, 202, 202, 202, 202, 202, 67, 341, 4, -28, '', 0, 40, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 75, 75, 9786, 0, 2813, 2175, 482, 482, 482, 482, 376, 376, 482, 223, 223, 223, 223, 223, 223, 67, 344, 4, -29, '', 0, 42, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 75, 75, 10161, 0, 2925, 2213, 490, 490, 490, 490, 378, 378, 490, 244, 244, 244, 244, 244, 244, 68, 347, 4, -30, '', 0, 44, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 75, 75, 10536, 0, 3038, 2250, 498, 498, 498, 498, 380, 380, 498, 265, 265, 265, 265, 265, 265, 68, 349, 4, -31, '', 0, 46, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 75, 75, 11661, 0, 3188, 2287, 508, 508, 508, 508, 392, 392, 508, 247, 247, 247, 247, 247, 247, 68, 345, 4, -32, '', 0, 48, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 75, 75, 12036, 0, 3300, 2325, 516, 516, 516, 516, 394, 394, 516, 268, 268, 268, 268, 268, 268, 68, 348, 4, -33, '', 0, 50, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 75, 75, 12411, 0, 3413, 2362, 524, 524, 524, 524, 396, 396, 524, 289, 289, 289, 289, 289, 289, 69, 351, 4, -34, '', 0, 52, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 75, 75, 12786, 0, 3525, 2400, 532, 532, 532, 532, 398, 398, 532, 310, 310, 310, 310, 310, 310, 69, 354, 4, -35, '', 0, 54, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 75, 75, 13161, 0, 3638, 2437, 540, 540, 540, 540, 400, 400, 540, 331, 331, 331, 331, 331, 331, 70, 356, 4, -36, '', 0, 56, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 75, 75, 7448, 6600, 2025, 1263, 430, 430, 430, 430, 440, 440, 430, 181, 181, 181, 181, 181, 181, 20, 112, 1, -18, '', 0, 26, 20, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 75, 75, 7673, 6825, 2100, 1288, 438, 438, 438, 438, 443, 443, 438, 202, 202, 202, 202, 202, 202, 20, 113, 1, -18, '', 0, 27, 21, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 75, 75, 7898, 7050, 2175, 1313, 446, 446, 446, 446, 446, 446, 446, 223, 223, 223, 223, 223, 223, 20, 115, 1, -18, '', 0, 28, 22, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 75, 75, 8123, 7275, 2250, 1338, 454, 454, 454, 454, 449, 449, 454, 244, 244, 244, 244, 244, 244, 20, 116, 1, -18, '', 0, 29, 23, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 75, 75, 8348, 7500, 2325, 1363, 462, 462, 462, 462, 452, 452, 462, 265, 265, 265, 265, 265, 265, 21, 118, 1, -18, '', 0, 30, 24, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 75, 75, 9698, 8475, 2475, 1375, 472, 472, 472, 472, 465, 465, 472, 247, 247, 247, 247, 247, 247, 20, 116, 1, -18, '', 0, 32, 23, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 75, 75, 9923, 8700, 2550, 1400, 480, 480, 480, 480, 468, 468, 480, 268, 268, 268, 268, 268, 268, 21, 118, 1, -18, '', 0, 33, 24, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 75, 75, 10148, 8925, 2625, 1425, 488, 488, 488, 488, 471, 471, 488, 289, 289, 289, 289, 289, 289, 21, 119, 1, -18, '', 0, 34, 25, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 75, 75, 10373, 9150, 2700, 1450, 496, 496, 496, 496, 474, 474, 496, 310, 310, 310, 310, 310, 310, 21, 120, 1, -18, '', 0, 35, 26, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 75, 75, 10598, 9375, 2775, 1475, 504, 504, 504, 504, 477, 477, 504, 331, 331, 331, 331, 331, 331, 21, 122, 1, -18, '', 0, 36, 27, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 75, 75, 7833, 0, 1537, 2137, 466, 466, 466, 466, 372, 372, 466, 181, 181, 181, 181, 181, 181, 66, 330, 4, -27, '', 0, 36, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 75, 75, 8133, 0, 1575, 2175, 474, 474, 474, 474, 374, 374, 474, 202, 202, 202, 202, 202, 202, 67, 333, 4, -28, '', 0, 38, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 75, 75, 8433, 0, 1612, 2212, 482, 482, 482, 482, 376, 376, 482, 223, 223, 223, 223, 223, 223, 67, 335, 4, -29, '', 0, 40, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 75, 75, 8733, 0, 1650, 2250, 490, 490, 490, 490, 378, 378, 490, 244, 244, 244, 244, 244, 244, 68, 338, 4, -30, '', 0, 42, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 75, 75, 9033, 0, 1687, 2287, 498, 498, 498, 498, 380, 380, 498, 265, 265, 265, 265, 265, 265, 68, 340, 4, -31, '', 0, 44, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 75, 75, 10083, 0, 1912, 2362, 508, 508, 508, 508, 392, 392, 508, 247, 247, 247, 247, 247, 247, 68, 336, 4, -32, '', 0, 44, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 75, 75, 10383, 0, 1950, 2400, 516, 516, 516, 516, 394, 394, 516, 268, 268, 268, 268, 268, 268, 68, 339, 4, -33, '', 0, 46, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 75, 75, 10683, 0, 1987, 2437, 524, 524, 524, 524, 396, 396, 524, 289, 289, 289, 289, 289, 289, 69, 342, 4, -34, '', 0, 48, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 75, 75, 10983, 0, 2025, 2475, 532, 532, 532, 532, 398, 398, 532, 310, 310, 310, 310, 310, 310, 69, 344, 4, -35, '', 0, 50, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 75, 75, 11283, 0, 2062, 2512, 540, 540, 540, 540, 400, 400, 540, 331, 331, 331, 331, 331, 331, 70, 347, 4, -36, '', 0, 52, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 75, 75, 6722, 6600, 1000, 618, 419, 419, 419, 419, 440, 440, 419, 181, 181, 181, 181, 181, 181, 20, 112, 1, -18, '', 0, 25, 20, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 75, 75, 6872, 6825, 1025, 637, 427, 427, 427, 427, 443, 443, 427, 202, 202, 202, 202, 202, 202, 20, 113, 1, -18, '', 0, 26, 21, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 75, 75, 7022, 7050, 1050, 656, 435, 435, 435, 435, 446, 446, 435, 223, 223, 223, 223, 223, 223, 20, 115, 1, -18, '', 0, 27, 22, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 75, 75, 7172, 7275, 1075, 675, 443, 443, 443, 443, 449, 449, 443, 244, 244, 244, 244, 244, 244, 20, 116, 1, -18, '', 0, 28, 23, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 75, 75, 7322, 7500, 1100, 693, 451, 451, 451, 451, 452, 452, 451, 265, 265, 265, 265, 265, 265, 21, 118, 1, -18, '', 0, 29, 24, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 75, 75, 8597, 8475, 1225, 693, 461, 461, 461, 461, 465, 465, 461, 247, 247, 247, 247, 247, 247, 20, 116, 1, -18, '', 0, 30, 23, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 75, 75, 8747, 8700, 1250, 712, 469, 469, 469, 469, 468, 468, 469, 268, 268, 268, 268, 268, 268, 21, 118, 1, -18, '', 0, 31, 24, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 75, 75, 8897, 8925, 1275, 731, 477, 477, 477, 477, 471, 471, 477, 289, 289, 289, 289, 289, 289, 21, 119, 1, -18, '', 0, 32, 25, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 75, 75, 9047, 9150, 1300, 750, 485, 485, 485, 485, 474, 474, 485, 310, 310, 310, 310, 310, 310, 21, 120, 1, -18, '', 0, 33, 26, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 75, 75, 9197, 9375, 1325, 768, 493, 493, 493, 493, 477, 477, 493, 331, 331, 331, 331, 331, 331, 21, 122, 1, -18, '', 0, 34, 27, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 76, 76, 9321, 0, 2622, 2128, 479, 479, 479, 479, 380, 380, 479, 184, 184, 184, 184, 184, 184, 70, 355, 4, -28, '', 0, 38, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 76, 76, 9701, 0, 2736, 2166, 487, 487, 487, 487, 382, 382, 487, 206, 206, 206, 206, 206, 206, 70, 357, 4, -29, '', 0, 40, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 76, 76, 10081, 0, 2850, 2204, 495, 495, 495, 495, 384, 384, 495, 228, 228, 228, 228, 228, 228, 71, 360, 4, -30, '', 0, 42, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 76, 76, 10461, 0, 2964, 2242, 503, 503, 503, 503, 386, 386, 503, 250, 250, 250, 250, 250, 250, 71, 363, 4, -31, '', 0, 44, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 76, 76, 10841, 0, 3078, 2280, 511, 511, 511, 511, 388, 388, 511, 272, 272, 272, 272, 272, 272, 72, 366, 4, -32, '', 0, 46, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 76, 76, 11981, 0, 3230, 2318, 522, 522, 522, 522, 400, 400, 522, 250, 250, 250, 250, 250, 250, 71, 362, 4, -33, '', 0, 48, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 76, 76, 12361, 0, 3344, 2356, 530, 530, 530, 530, 402, 402, 530, 272, 272, 272, 272, 272, 272, 71, 364, 4, -34, '', 0, 50, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 76, 76, 12741, 0, 3458, 2394, 538, 538, 538, 538, 404, 404, 538, 294, 294, 294, 294, 294, 294, 72, 367, 4, -35, '', 0, 52, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 76, 76, 13121, 0, 3572, 2432, 546, 546, 546, 546, 406, 406, 546, 316, 316, 316, 316, 316, 316, 72, 370, 4, -36, '', 0, 54, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 76, 76, 13501, 0, 3686, 2470, 554, 554, 554, 554, 408, 408, 554, 338, 338, 338, 338, 338, 338, 73, 373, 4, -37, '', 0, 56, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 76, 76, 7682, 6749, 2052, 1279, 443, 443, 443, 443, 449, 449, 443, 184, 184, 184, 184, 184, 184, 20, 113, 1, -18, '', 0, 27, 20, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 76, 76, 7910, 6977, 2128, 1304, 451, 451, 451, 451, 452, 452, 451, 206, 206, 206, 206, 206, 206, 20, 115, 1, -18, '', 0, 28, 21, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 76, 76, 8138, 7205, 2204, 1330, 459, 459, 459, 459, 455, 455, 459, 228, 228, 228, 228, 228, 228, 20, 116, 1, -18, '', 0, 29, 22, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 76, 76, 8366, 7433, 2280, 1355, 467, 467, 467, 467, 458, 458, 467, 250, 250, 250, 250, 250, 250, 21, 118, 1, -18, '', 0, 30, 23, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 76, 76, 8594, 7661, 2356, 1380, 475, 475, 475, 475, 461, 461, 475, 272, 272, 272, 272, 272, 272, 21, 119, 1, -18, '', 0, 31, 24, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 76, 76, 9962, 8649, 2508, 1393, 486, 486, 486, 486, 474, 474, 486, 250, 250, 250, 250, 250, 250, 21, 118, 1, -18, '', 0, 33, 23, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 76, 76, 10190, 8877, 2584, 1418, 494, 494, 494, 494, 477, 477, 494, 272, 272, 272, 272, 272, 272, 21, 119, 1, -18, '', 0, 34, 24, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 76, 76, 10418, 9105, 2660, 1444, 502, 502, 502, 502, 480, 480, 502, 294, 294, 294, 294, 294, 294, 21, 120, 1, -18, '', 0, 35, 25, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 76, 76, 10646, 9333, 2736, 1469, 510, 510, 510, 510, 483, 483, 510, 316, 316, 316, 316, 316, 316, 21, 122, 1, -18, '', 0, 36, 26, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 76, 76, 10874, 9561, 2812, 1494, 518, 518, 518, 518, 486, 486, 518, 338, 338, 338, 338, 338, 338, 22, 123, 1, -18, '', 0, 37, 27, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 76, 76, 8081, 0, 1558, 2166, 479, 479, 479, 479, 380, 380, 479, 184, 184, 184, 184, 184, 184, 70, 346, 4, -28, '', 0, 36, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 76, 76, 8385, 0, 1596, 2204, 487, 487, 487, 487, 382, 382, 487, 206, 206, 206, 206, 206, 206, 70, 349, 4, -29, '', 0, 38, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 76, 76, 8689, 0, 1634, 2242, 495, 495, 495, 495, 384, 384, 495, 228, 228, 228, 228, 228, 228, 71, 351, 4, -30, '', 0, 40, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 76, 76, 8993, 0, 1672, 2280, 503, 503, 503, 503, 386, 386, 503, 250, 250, 250, 250, 250, 250, 71, 354, 4, -31, '', 0, 42, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 76, 76, 9297, 0, 1710, 2318, 511, 511, 511, 511, 388, 388, 511, 272, 272, 272, 272, 272, 272, 72, 357, 4, -32, '', 0, 44, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 76, 76, 10361, 0, 1938, 2394, 522, 522, 522, 522, 400, 400, 522, 250, 250, 250, 250, 250, 250, 71, 353, 4, -33, '', 0, 44, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 76, 76, 10665, 0, 1976, 2432, 530, 530, 530, 530, 402, 402, 530, 272, 272, 272, 272, 272, 272, 71, 355, 4, -34, '', 0, 46, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 76, 76, 10969, 0, 2014, 2470, 538, 538, 538, 538, 404, 404, 538, 294, 294, 294, 294, 294, 294, 72, 358, 4, -35, '', 0, 48, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 76, 76, 11273, 0, 2052, 2508, 546, 546, 546, 546, 406, 406, 546, 316, 316, 316, 316, 316, 316, 72, 361, 4, -36, '', 0, 50, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 76, 76, 11577, 0, 2090, 2546, 554, 554, 554, 554, 408, 408, 554, 338, 338, 338, 338, 338, 338, 73, 363, 4, -37, '', 0, 52, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 76, 76, 6939, 6749, 1013, 627, 432, 432, 432, 432, 449, 449, 432, 184, 184, 184, 184, 184, 184, 20, 113, 1, -18, '', 0, 26, 20, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 76, 76, 7091, 6977, 1038, 646, 440, 440, 440, 440, 452, 452, 440, 206, 206, 206, 206, 206, 206, 20, 115, 1, -18, '', 0, 27, 21, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 76, 76, 7243, 7205, 1064, 665, 448, 448, 448, 448, 455, 455, 448, 228, 228, 228, 228, 228, 228, 20, 116, 1, -18, '', 0, 28, 22, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 76, 76, 7395, 7433, 1089, 684, 456, 456, 456, 456, 458, 458, 456, 250, 250, 250, 250, 250, 250, 21, 118, 1, -18, '', 0, 29, 23, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 76, 76, 7547, 7661, 1114, 703, 464, 464, 464, 464, 461, 461, 464, 272, 272, 272, 272, 272, 272, 21, 119, 1, -18, '', 0, 30, 24, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 76, 76, 8839, 8649, 1241, 703, 475, 475, 475, 475, 474, 474, 475, 250, 250, 250, 250, 250, 250, 21, 118, 1, -18, '', 0, 31, 23, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 76, 76, 8991, 8877, 1266, 722, 483, 483, 483, 483, 477, 477, 483, 272, 272, 272, 272, 272, 272, 21, 119, 1, -18, '', 0, 32, 24, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 76, 76, 9143, 9105, 1292, 741, 491, 491, 491, 491, 480, 480, 491, 294, 294, 294, 294, 294, 294, 21, 120, 1, -18, '', 0, 33, 25, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 76, 76, 9295, 9333, 1317, 760, 499, 499, 499, 499, 483, 483, 499, 316, 316, 316, 316, 316, 316, 21, 122, 1, -18, '', 0, 34, 26, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 76, 76, 9447, 9561, 1342, 779, 507, 507, 507, 507, 486, 486, 507, 338, 338, 338, 338, 338, 338, 22, 123, 1, -18, '', 0, 35, 27, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 77, 77, 9611, 0, 2657, 2156, 492, 492, 492, 492, 388, 388, 492, 186, 186, 186, 186, 186, 186, 73, 371, 4, -28, '', 0, 38, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 77, 77, 9996, 0, 2772, 2195, 500, 500, 500, 500, 390, 390, 500, 208, 208, 208, 208, 208, 208, 73, 374, 4, -29, '', 0, 40, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 77, 77, 10381, 0, 2888, 2233, 508, 508, 508, 508, 392, 392, 508, 230, 230, 230, 230, 230, 230, 74, 377, 4, -30, '', 0, 42, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 77, 77, 10766, 0, 3003, 2272, 516, 516, 516, 516, 394, 394, 516, 252, 252, 252, 252, 252, 252, 74, 379, 4, -31, '', 0, 44, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 77, 77, 11151, 0, 3119, 2310, 524, 524, 524, 524, 396, 396, 524, 274, 274, 274, 274, 274, 274, 75, 382, 4, -32, '', 0, 46, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 77, 77, 12306, 0, 3273, 2348, 535, 535, 535, 535, 408, 408, 535, 254, 254, 254, 254, 254, 254, 74, 378, 4, -33, '', 0, 48, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 77, 77, 12691, 0, 3388, 2387, 543, 543, 543, 543, 410, 410, 543, 276, 276, 276, 276, 276, 276, 75, 381, 4, -34, '', 0, 50, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 77, 77, 13076, 0, 3504, 2425, 551, 551, 551, 551, 412, 412, 551, 298, 298, 298, 298, 298, 298, 75, 384, 4, -35, '', 0, 52, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 77, 77, 13461, 0, 3619, 2464, 559, 559, 559, 559, 414, 414, 559, 320, 320, 320, 320, 320, 320, 76, 386, 4, -36, '', 0, 54, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 77, 77, 13846, 0, 3735, 2502, 567, 567, 567, 567, 416, 416, 567, 342, 342, 342, 342, 342, 342, 76, 389, 4, -37, '', 0, 56, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 77, 77, 7921, 6899, 2079, 1296, 455, 455, 455, 455, 457, 457, 455, 186, 186, 186, 186, 186, 186, 20, 115, 1, -18, '', 0, 27, 20, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 77, 77, 8152, 7130, 2156, 1322, 463, 463, 463, 463, 460, 460, 463, 208, 208, 208, 208, 208, 208, 20, 116, 1, -18, '', 0, 28, 21, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 77, 77, 8383, 7361, 2233, 1348, 471, 471, 471, 471, 463, 463, 471, 230, 230, 230, 230, 230, 230, 21, 118, 1, -18, '', 0, 29, 22, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 77, 77, 8614, 7592, 2310, 1373, 479, 479, 479, 479, 466, 466, 479, 252, 252, 252, 252, 252, 252, 21, 119, 1, -18, '', 0, 30, 23, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 77, 77, 8845, 7823, 2387, 1399, 487, 487, 487, 487, 469, 469, 487, 274, 274, 274, 274, 274, 274, 21, 120, 1, -18, '', 0, 31, 24, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 77, 77, 10231, 8824, 2541, 1411, 498, 498, 498, 498, 482, 482, 498, 254, 254, 254, 254, 254, 254, 21, 119, 1, -18, '', 0, 33, 23, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 77, 77, 10462, 9055, 2618, 1437, 506, 506, 506, 506, 485, 485, 506, 276, 276, 276, 276, 276, 276, 21, 120, 1, -18, '', 0, 34, 24, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 77, 77, 10693, 9286, 2695, 1463, 514, 514, 514, 514, 488, 488, 514, 298, 298, 298, 298, 298, 298, 21, 122, 1, -18, '', 0, 35, 25, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 77, 77, 10924, 9517, 2772, 1488, 522, 522, 522, 522, 491, 491, 522, 320, 320, 320, 320, 320, 320, 22, 123, 1, -18, '', 0, 36, 26, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 77, 77, 11155, 9748, 2849, 1514, 530, 530, 530, 530, 494, 494, 530, 342, 342, 342, 342, 342, 342, 22, 125, 1, -18, '', 0, 37, 27, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 77, 77, 8334, 0, 1578, 2194, 492, 492, 492, 492, 388, 388, 492, 186, 186, 186, 186, 186, 186, 73, 363, 4, -28, '', 0, 36, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 77, 77, 8642, 0, 1617, 2233, 500, 500, 500, 500, 390, 390, 500, 208, 208, 208, 208, 208, 208, 73, 365, 4, -29, '', 0, 38, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 77, 77, 8950, 0, 1655, 2271, 508, 508, 508, 508, 392, 392, 508, 230, 230, 230, 230, 230, 230, 74, 368, 4, -30, '', 0, 40, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 77, 77, 9258, 0, 1694, 2310, 516, 516, 516, 516, 394, 394, 516, 252, 252, 252, 252, 252, 252, 74, 370, 4, -31, '', 0, 42, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 77, 77, 9566, 0, 1732, 2348, 524, 524, 524, 524, 396, 396, 524, 274, 274, 274, 274, 274, 274, 75, 373, 4, -32, '', 0, 44, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 77, 77, 10644, 0, 1963, 2425, 535, 535, 535, 535, 408, 408, 535, 254, 254, 254, 254, 254, 254, 74, 369, 4, -33, '', 0, 44, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 77, 77, 10952, 0, 2002, 2464, 543, 543, 543, 543, 410, 410, 543, 276, 276, 276, 276, 276, 276, 75, 372, 4, -34, '', 0, 46, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 77, 77, 11260, 0, 2040, 2502, 551, 551, 551, 551, 412, 412, 551, 298, 298, 298, 298, 298, 298, 75, 374, 4, -35, '', 0, 48, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 77, 77, 11568, 0, 2079, 2541, 559, 559, 559, 559, 414, 414, 559, 320, 320, 320, 320, 320, 320, 76, 377, 4, -36, '', 0, 50, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 77, 77, 11876, 0, 2117, 2579, 567, 567, 567, 567, 416, 416, 567, 342, 342, 342, 342, 342, 342, 76, 379, 4, -37, '', 0, 52, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 77, 77, 7161, 6899, 1026, 635, 443, 443, 443, 443, 457, 457, 443, 186, 186, 186, 186, 186, 186, 20, 115, 1, -18, '', 0, 26, 20, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 77, 77, 7315, 7130, 1052, 654, 451, 451, 451, 451, 460, 460, 451, 208, 208, 208, 208, 208, 208, 20, 116, 1, -18, '', 0, 27, 21, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 77, 77, 7469, 7361, 1078, 673, 459, 459, 459, 459, 463, 463, 459, 230, 230, 230, 230, 230, 230, 21, 118, 1, -18, '', 0, 28, 22, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 77, 77, 7623, 7592, 1103, 693, 467, 467, 467, 467, 466, 466, 467, 252, 252, 252, 252, 252, 252, 21, 119, 1, -18, '', 0, 29, 23, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 77, 77, 7777, 7823, 1129, 712, 475, 475, 475, 475, 469, 469, 475, 274, 274, 274, 274, 274, 274, 21, 120, 1, -18, '', 0, 30, 24, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 77, 77, 9086, 8824, 1257, 712, 486, 486, 486, 486, 482, 482, 486, 254, 254, 254, 254, 254, 254, 21, 119, 1, -18, '', 0, 31, 23, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 77, 77, 9240, 9055, 1283, 731, 494, 494, 494, 494, 485, 485, 494, 276, 276, 276, 276, 276, 276, 21, 120, 1, -18, '', 0, 32, 24, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 77, 77, 9394, 9286, 1309, 750, 502, 502, 502, 502, 488, 488, 502, 298, 298, 298, 298, 298, 298, 21, 122, 1, -18, '', 0, 33, 25, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 77, 77, 9548, 9517, 1334, 770, 510, 510, 510, 510, 491, 491, 510, 320, 320, 320, 320, 320, 320, 22, 123, 1, -18, '', 0, 34, 26, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 77, 77, 9702, 9748, 1360, 789, 518, 518, 518, 518, 494, 494, 518, 342, 342, 342, 342, 342, 342, 22, 125, 1, -18, '', 0, 35, 27, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 78, 78, 9909, 0, 2691, 2184, 506, 506, 506, 506, 396, 396, 506, 188, 188, 188, 188, 188, 188, 76, 387, 4, -29, '', 0, 39, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 78, 78, 10299, 0, 2808, 2223, 514, 514, 514, 514, 398, 398, 514, 210, 210, 210, 210, 210, 210, 77, 390, 4, -30, '', 0, 41, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 78, 78, 10689, 0, 2925, 2262, 522, 522, 522, 522, 400, 400, 522, 232, 232, 232, 232, 232, 232, 77, 393, 4, -31, '', 0, 43, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 78, 78, 11079, 0, 3042, 2301, 530, 530, 530, 530, 402, 402, 530, 254, 254, 254, 254, 254, 254, 78, 396, 4, -32, '', 0, 45, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 78, 78, 11469, 0, 3159, 2340, 538, 538, 538, 538, 404, 404, 538, 276, 276, 276, 276, 276, 276, 78, 399, 4, -33, '', 0, 47, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 78, 78, 12639, 0, 3315, 2379, 550, 550, 550, 550, 416, 416, 550, 256, 256, 256, 256, 256, 256, 77, 394, 4, -34, '', 0, 49, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 78, 78, 13029, 0, 3432, 2418, 558, 558, 558, 558, 418, 418, 558, 278, 278, 278, 278, 278, 278, 78, 397, 4, -35, '', 0, 51, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 78, 78, 13419, 0, 3549, 2457, 566, 566, 566, 566, 420, 420, 566, 300, 300, 300, 300, 300, 300, 78, 400, 4, -36, '', 0, 53, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 78, 78, 13809, 0, 3666, 2496, 574, 574, 574, 574, 422, 422, 574, 322, 322, 322, 322, 322, 322, 79, 403, 4, -37, '', 0, 55, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 78, 78, 14199, 0, 3783, 2535, 582, 582, 582, 582, 424, 424, 582, 344, 344, 344, 344, 344, 344, 79, 406, 4, -38, '', 0, 57, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 78, 78, 8166, 7051, 2106, 1313, 468, 468, 468, 468, 466, 466, 468, 188, 188, 188, 188, 188, 188, 20, 116, 1, -18, '', 0, 27, 20, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 78, 78, 8400, 7285, 2184, 1339, 476, 476, 476, 476, 469, 469, 476, 210, 210, 210, 210, 210, 210, 21, 118, 1, -18, '', 0, 28, 21, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 78, 78, 8634, 7519, 2262, 1365, 484, 484, 484, 484, 472, 472, 484, 232, 232, 232, 232, 232, 232, 21, 119, 1, -18, '', 0, 29, 22, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 78, 78, 8868, 7753, 2340, 1391, 492, 492, 492, 492, 475, 475, 492, 254, 254, 254, 254, 254, 254, 21, 120, 1, -18, '', 0, 30, 23, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 78, 78, 9102, 7987, 2418, 1417, 500, 500, 500, 500, 478, 478, 500, 276, 276, 276, 276, 276, 276, 21, 122, 1, -18, '', 0, 31, 24, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 78, 78, 10506, 9001, 2574, 1430, 512, 512, 512, 512, 491, 491, 512, 256, 256, 256, 256, 256, 256, 21, 120, 1, -18, '', 0, 33, 23, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 78, 78, 10740, 9235, 2652, 1456, 520, 520, 520, 520, 494, 494, 520, 278, 278, 278, 278, 278, 278, 21, 122, 1, -18, '', 0, 34, 24, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 78, 78, 10974, 9469, 2730, 1482, 528, 528, 528, 528, 497, 497, 528, 300, 300, 300, 300, 300, 300, 22, 123, 1, -18, '', 0, 35, 25, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 78, 78, 11208, 9703, 2808, 1508, 536, 536, 536, 536, 500, 500, 536, 322, 322, 322, 322, 322, 322, 22, 125, 1, -18, '', 0, 36, 26, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 78, 78, 11442, 9937, 2886, 1534, 544, 544, 544, 544, 503, 503, 544, 344, 344, 344, 344, 344, 344, 22, 126, 1, -18, '', 0, 37, 27, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 78, 78, 8593, 0, 1599, 2223, 506, 506, 506, 506, 396, 396, 506, 188, 188, 188, 188, 188, 188, 76, 379, 4, -29, '', 0, 37, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 78, 78, 8905, 0, 1638, 2262, 514, 514, 514, 514, 398, 398, 514, 210, 210, 210, 210, 210, 210, 77, 381, 4, -30, '', 0, 39, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 78, 78, 9217, 0, 1677, 2301, 522, 522, 522, 522, 400, 400, 522, 232, 232, 232, 232, 232, 232, 77, 384, 4, -31, '', 0, 41, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 78, 78, 9529, 0, 1716, 2340, 530, 530, 530, 530, 402, 402, 530, 254, 254, 254, 254, 254, 254, 78, 387, 4, -32, '', 0, 43, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 78, 78, 9841, 0, 1755, 2379, 538, 538, 538, 538, 404, 404, 538, 276, 276, 276, 276, 276, 276, 78, 389, 4, -33, '', 0, 45, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 78, 78, 10933, 0, 1989, 2457, 550, 550, 550, 550, 416, 416, 550, 256, 256, 256, 256, 256, 256, 77, 385, 4, -34, '', 0, 45, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 78, 78, 11245, 0, 2028, 2496, 558, 558, 558, 558, 418, 418, 558, 278, 278, 278, 278, 278, 278, 78, 388, 4, -35, '', 0, 47, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 78, 78, 11557, 0, 2067, 2535, 566, 566, 566, 566, 420, 420, 566, 300, 300, 300, 300, 300, 300, 78, 391, 4, -36, '', 0, 49, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 78, 78, 11869, 0, 2106, 2574, 574, 574, 574, 574, 422, 422, 574, 322, 322, 322, 322, 322, 322, 79, 393, 4, -37, '', 0, 51, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 78, 78, 12181, 0, 2145, 2613, 582, 582, 582, 582, 424, 424, 582, 344, 344, 344, 344, 344, 344, 79, 396, 4, -38, '', 0, 53, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 78, 78, 7388, 7051, 1040, 643, 456, 456, 456, 456, 466, 466, 456, 188, 188, 188, 188, 188, 188, 20, 116, 1, -18, '', 0, 26, 20, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 78, 78, 7544, 7285, 1066, 663, 464, 464, 464, 464, 469, 469, 464, 210, 210, 210, 210, 210, 210, 21, 118, 1, -18, '', 0, 27, 21, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 78, 78, 7700, 7519, 1092, 682, 472, 472, 472, 472, 472, 472, 472, 232, 232, 232, 232, 232, 232, 21, 119, 1, -18, '', 0, 28, 22, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 78, 78, 7856, 7753, 1118, 702, 480, 480, 480, 480, 475, 475, 480, 254, 254, 254, 254, 254, 254, 21, 120, 1, -18, '', 0, 29, 23, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 78, 78, 8012, 7987, 1144, 721, 488, 488, 488, 488, 478, 478, 488, 276, 276, 276, 276, 276, 276, 21, 122, 1, -18, '', 0, 30, 24, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 78, 78, 9338, 9001, 1274, 721, 500, 500, 500, 500, 491, 491, 500, 256, 256, 256, 256, 256, 256, 21, 120, 1, -18, '', 0, 31, 23, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 78, 78, 9494, 9235, 1300, 741, 508, 508, 508, 508, 494, 494, 508, 278, 278, 278, 278, 278, 278, 21, 122, 1, -18, '', 0, 32, 24, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 78, 78, 9650, 9469, 1326, 760, 516, 516, 516, 516, 497, 497, 516, 300, 300, 300, 300, 300, 300, 22, 123, 1, -18, '', 0, 33, 25, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 78, 78, 9806, 9703, 1352, 780, 524, 524, 524, 524, 500, 500, 524, 322, 322, 322, 322, 322, 322, 22, 125, 1, -18, '', 0, 34, 26, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 78, 78, 9962, 9937, 1378, 799, 532, 532, 532, 532, 503, 503, 532, 344, 344, 344, 344, 344, 344, 22, 126, 1, -18, '', 0, 35, 27, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 79, 79, 10213, 0, 2726, 2212, 519, 519, 519, 519, 404, 404, 519, 190, 190, 190, 190, 190, 190, 79, 404, 4, -29, '', 0, 39, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 79, 79, 10608, 0, 2844, 2252, 527, 527, 527, 527, 406, 406, 527, 212, 212, 212, 212, 212, 212, 80, 407, 4, -30, '', 0, 41, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 79, 79, 11003, 0, 2963, 2291, 535, 535, 535, 535, 408, 408, 535, 234, 234, 234, 234, 234, 234, 80, 409, 4, -31, '', 0, 43, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 79, 79, 11398, 0, 3081, 2331, 543, 543, 543, 543, 410, 410, 543, 256, 256, 256, 256, 256, 256, 81, 412, 4, -32, '', 0, 45, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 79, 79, 11793, 0, 3200, 2370, 551, 551, 551, 551, 412, 412, 551, 278, 278, 278, 278, 278, 278, 81, 415, 4, -33, '', 0, 47, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 79, 79, 12978, 0, 3358, 2409, 563, 563, 563, 563, 424, 424, 563, 258, 258, 258, 258, 258, 258, 81, 411, 4, -34, '', 0, 49, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 79, 79, 13373, 0, 3476, 2449, 571, 571, 571, 571, 426, 426, 571, 280, 280, 280, 280, 280, 280, 81, 414, 4, -35, '', 0, 51, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 79, 79, 13768, 0, 3595, 2488, 579, 579, 579, 579, 428, 428, 579, 302, 302, 302, 302, 302, 302, 82, 416, 4, -36, '', 0, 53, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 79, 79, 14163, 0, 3713, 2528, 587, 587, 587, 587, 430, 430, 587, 324, 324, 324, 324, 324, 324, 82, 419, 4, -37, '', 0, 55, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 79, 79, 14558, 0, 3832, 2567, 595, 595, 595, 595, 432, 432, 595, 346, 346, 346, 346, 346, 346, 83, 422, 4, -38, '', 0, 57, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 79, 79, 8417, 7205, 2133, 1330, 480, 480, 480, 480, 474, 474, 480, 190, 190, 190, 190, 190, 190, 21, 118, 1, -18, '', 0, 27, 20, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 79, 79, 8654, 7442, 2212, 1356, 488, 488, 488, 488, 477, 477, 488, 212, 212, 212, 212, 212, 212, 21, 119, 1, -18, '', 0, 28, 21, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 79, 79, 8891, 7679, 2291, 1383, 496, 496, 496, 496, 480, 480, 496, 234, 234, 234, 234, 234, 234, 21, 120, 1, -18, '', 0, 29, 22, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 79, 79, 9128, 7916, 2370, 1409, 504, 504, 504, 504, 483, 483, 504, 256, 256, 256, 256, 256, 256, 21, 122, 1, -18, '', 0, 30, 23, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 79, 79, 9365, 8153, 2449, 1435, 512, 512, 512, 512, 486, 486, 512, 278, 278, 278, 278, 278, 278, 22, 123, 1, -18, '', 0, 31, 24, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 79, 79, 10787, 9180, 2607, 1448, 524, 524, 524, 524, 499, 499, 524, 258, 258, 258, 258, 258, 258, 21, 122, 1, -18, '', 0, 33, 23, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 79, 79, 11024, 9417, 2686, 1474, 532, 532, 532, 532, 502, 502, 532, 280, 280, 280, 280, 280, 280, 22, 123, 1, -18, '', 0, 34, 24, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 79, 79, 11261, 9654, 2765, 1501, 540, 540, 540, 540, 505, 505, 540, 302, 302, 302, 302, 302, 302, 22, 125, 1, -18, '', 0, 35, 25, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 79, 79, 11498, 9891, 2844, 1527, 548, 548, 548, 548, 508, 508, 548, 324, 324, 324, 324, 324, 324, 22, 126, 1, -18, '', 0, 36, 26, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 79, 79, 11735, 10128, 2923, 1553, 556, 556, 556, 556, 511, 511, 556, 346, 346, 346, 346, 346, 346, 22, 127, 1, -18, '', 0, 37, 27, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 79, 79, 8858, 0, 1619, 2251, 519, 519, 519, 519, 404, 404, 519, 190, 190, 190, 190, 190, 190, 79, 395, 4, -29, '', 0, 37, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 79, 79, 9174, 0, 1659, 2291, 527, 527, 527, 527, 406, 406, 527, 212, 212, 212, 212, 212, 212, 80, 398, 4, -30, '', 0, 39, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 79, 79, 9490, 0, 1698, 2330, 535, 535, 535, 535, 408, 408, 535, 234, 234, 234, 234, 234, 234, 80, 400, 4, -31, '', 0, 41, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 79, 79, 9806, 0, 1738, 2370, 543, 543, 543, 543, 410, 410, 543, 256, 256, 256, 256, 256, 256, 81, 403, 4, -32, '', 0, 43, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 79, 79, 10122, 0, 1777, 2409, 551, 551, 551, 551, 412, 412, 551, 278, 278, 278, 278, 278, 278, 81, 406, 4, -33, '', 0, 45, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 79, 79, 11228, 0, 2014, 2488, 563, 563, 563, 563, 424, 424, 563, 258, 258, 258, 258, 258, 258, 81, 402, 4, -34, '', 0, 45, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 79, 79, 11544, 0, 2054, 2528, 571, 571, 571, 571, 426, 426, 571, 280, 280, 280, 280, 280, 280, 81, 404, 4, -35, '', 0, 47, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 79, 79, 11860, 0, 2093, 2567, 579, 579, 579, 579, 428, 428, 579, 302, 302, 302, 302, 302, 302, 82, 407, 4, -36, '', 0, 49, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 79, 79, 12176, 0, 2133, 2607, 587, 587, 587, 587, 430, 430, 587, 324, 324, 324, 324, 324, 324, 82, 409, 4, -37, '', 0, 51, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 79, 79, 12492, 0, 2172, 2646, 595, 595, 595, 595, 432, 432, 595, 346, 346, 346, 346, 346, 346, 83, 412, 4, -38, '', 0, 53, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 79, 79, 7621, 7205, 1053, 651, 468, 468, 468, 468, 474, 474, 468, 190, 190, 190, 190, 190, 190, 21, 118, 1, -18, '', 0, 26, 20, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 79, 79, 7779, 7442, 1079, 671, 476, 476, 476, 476, 477, 477, 476, 212, 212, 212, 212, 212, 212, 21, 119, 1, -18, '', 0, 27, 21, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 79, 79, 7937, 7679, 1106, 691, 484, 484, 484, 484, 480, 480, 484, 234, 234, 234, 234, 234, 234, 21, 120, 1, -18, '', 0, 28, 22, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 79, 79, 8095, 7916, 1132, 711, 492, 492, 492, 492, 483, 483, 492, 256, 256, 256, 256, 256, 256, 21, 122, 1, -18, '', 0, 29, 23, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 79, 79, 8253, 8153, 1158, 730, 500, 500, 500, 500, 486, 486, 500, 278, 278, 278, 278, 278, 278, 22, 123, 1, -18, '', 0, 30, 24, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 79, 79, 9596, 9180, 1290, 730, 512, 512, 512, 512, 499, 499, 512, 258, 258, 258, 258, 258, 258, 21, 122, 1, -18, '', 0, 31, 23, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 79, 79, 9754, 9417, 1316, 750, 520, 520, 520, 520, 502, 502, 520, 280, 280, 280, 280, 280, 280, 22, 123, 1, -18, '', 0, 32, 24, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 79, 79, 9912, 9654, 1343, 770, 528, 528, 528, 528, 505, 505, 528, 302, 302, 302, 302, 302, 302, 22, 125, 1, -18, '', 0, 33, 25, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 79, 79, 10070, 9891, 1369, 790, 536, 536, 536, 536, 508, 508, 536, 324, 324, 324, 324, 324, 324, 22, 126, 1, -18, '', 0, 34, 26, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 79, 79, 10228, 10128, 1395, 809, 544, 544, 544, 544, 511, 511, 544, 346, 346, 346, 346, 346, 346, 22, 127, 1, -18, '', 0, 35, 27, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 80, 80, 10524, 0, 2760, 2240, 534, 534, 534, 534, 412, 412, 534, 193, 193, 193, 193, 193, 193, 83, 420, 4, -30, '', 0, 39, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 80, 80, 10924, 0, 2880, 2280, 543, 543, 543, 543, 414, 414, 543, 216, 216, 216, 216, 216, 216, 83, 423, 4, -31, '', 0, 41, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 80, 80, 11324, 0, 3000, 2320, 552, 552, 552, 552, 416, 416, 552, 239, 239, 239, 239, 239, 239, 84, 426, 4, -32, '', 0, 43, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 80, 80, 11724, 0, 3120, 2360, 561, 561, 561, 561, 418, 418, 561, 262, 262, 262, 262, 262, 262, 84, 429, 4, -33, '', 0, 45, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 80, 80, 12124, 0, 3240, 2400, 570, 570, 570, 570, 420, 420, 570, 285, 285, 285, 285, 285, 285, 85, 431, 4, -34, '', 0, 47, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 80, 80, 13324, 0, 3400, 2440, 579, 579, 579, 579, 432, 432, 579, 263, 263, 263, 263, 263, 263, 84, 427, 4, -35, '', 0, 49, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 80, 80, 13724, 0, 3520, 2480, 588, 588, 588, 588, 434, 434, 588, 286, 286, 286, 286, 286, 286, 84, 430, 4, -36, '', 0, 51, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 80, 80, 14124, 0, 3640, 2520, 597, 597, 597, 597, 436, 436, 597, 309, 309, 309, 309, 309, 309, 85, 433, 4, -37, '', 0, 53, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 80, 80, 14524, 0, 3760, 2560, 606, 606, 606, 606, 438, 438, 606, 332, 332, 332, 332, 332, 332, 85, 436, 4, -38, '', 0, 55, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 80, 80, 14924, 0, 3880, 2600, 615, 615, 615, 615, 440, 440, 615, 355, 355, 355, 355, 355, 355, 86, 438, 4, -39, '', 0, 57, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 80, 80, 8673, 7360, 2160, 1346, 494, 494, 494, 494, 483, 483, 494, 193, 193, 193, 193, 193, 193, 21, 119, 1, -18, '', 0, 28, 21, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 80, 80, 8913, 7600, 2240, 1373, 503, 503, 503, 503, 486, 486, 503, 216, 216, 216, 216, 216, 216, 21, 120, 1, -18, '', 0, 29, 22, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 80, 80, 9153, 7840, 2320, 1400, 512, 512, 512, 512, 489, 489, 512, 239, 239, 239, 239, 239, 239, 21, 122, 1, -18, '', 0, 30, 23, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 80, 80, 9393, 8080, 2400, 1426, 521, 521, 521, 521, 492, 492, 521, 262, 262, 262, 262, 262, 262, 22, 123, 1, -18, '', 0, 31, 24, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 80, 80, 9633, 8320, 2480, 1453, 530, 530, 530, 530, 495, 495, 530, 285, 285, 285, 285, 285, 285, 22, 125, 1, -18, '', 0, 32, 25, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 80, 80, 11073, 9360, 2640, 1466, 539, 539, 539, 539, 508, 508, 539, 263, 263, 263, 263, 263, 263, 22, 123, 1, -18, '', 0, 34, 24, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 80, 80, 11313, 9600, 2720, 1493, 548, 548, 548, 548, 511, 511, 548, 286, 286, 286, 286, 286, 286, 22, 125, 1, -18, '', 0, 35, 25, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 80, 80, 11553, 9840, 2800, 1520, 557, 557, 557, 557, 514, 514, 557, 309, 309, 309, 309, 309, 309, 22, 126, 1, -18, '', 0, 36, 26, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 80, 80, 11793, 10080, 2880, 1546, 566, 566, 566, 566, 517, 517, 566, 332, 332, 332, 332, 332, 332, 22, 127, 1, -18, '', 0, 37, 27, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 80, 80, 12033, 10320, 2960, 1573, 575, 575, 575, 575, 520, 520, 575, 355, 355, 355, 355, 355, 355, 23, 129, 1, -18, '', 0, 38, 28, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 80, 80, 9130, 0, 1640, 2280, 534, 534, 534, 534, 412, 412, 534, 193, 193, 193, 193, 193, 193, 83, 411, 4, -34, '', 0, 37, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 80, 80, 9450, 0, 1680, 2320, 543, 543, 543, 543, 414, 414, 543, 216, 216, 216, 216, 216, 216, 83, 414, 4, -35, '', 0, 39, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 80, 80, 9770, 0, 1720, 2360, 552, 552, 552, 552, 416, 416, 552, 239, 239, 239, 239, 239, 239, 84, 417, 4, -36, '', 0, 41, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 80, 80, 10090, 0, 1760, 2400, 561, 561, 561, 561, 418, 418, 561, 262, 262, 262, 262, 262, 262, 84, 419, 4, -37, '', 0, 43, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 80, 80, 10410, 0, 1800, 2440, 570, 570, 570, 570, 420, 420, 570, 285, 285, 285, 285, 285, 285, 85, 422, 4, -38, '', 0, 45, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 80, 80, 11530, 0, 2040, 2520, 579, 579, 579, 579, 432, 432, 579, 263, 263, 263, 263, 263, 263, 84, 418, 4, -39, '', 0, 45, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 80, 80, 11850, 0, 2080, 2560, 588, 588, 588, 588, 434, 434, 588, 286, 286, 286, 286, 286, 286, 84, 421, 4, -40, '', 0, 47, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 80, 80, 12170, 0, 2120, 2600, 597, 597, 597, 597, 436, 436, 597, 309, 309, 309, 309, 309, 309, 85, 423, 4, -41, '', 0, 49, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 80, 80, 12490, 0, 2160, 2640, 606, 606, 606, 606, 438, 438, 606, 332, 332, 332, 332, 332, 332, 85, 426, 4, -42, '', 0, 51, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 80, 80, 12810, 0, 2200, 2680, 615, 615, 615, 615, 440, 440, 615, 355, 355, 355, 355, 355, 355, 86, 428, 4, -43, '', 0, 53, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 80, 80, 7858, 7360, 1066, 660, 482, 482, 482, 482, 483, 483, 482, 193, 193, 193, 193, 193, 193, 21, 119, 1, -18, '', 0, 27, 21, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 80, 80, 8018, 7600, 1093, 680, 491, 491, 491, 491, 486, 486, 491, 216, 216, 216, 216, 216, 216, 21, 120, 1, -18, '', 0, 28, 22, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 80, 80, 8178, 7840, 1120, 700, 500, 500, 500, 500, 489, 489, 500, 239, 239, 239, 239, 239, 239, 21, 122, 1, -18, '', 0, 29, 23, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 80, 80, 8338, 8080, 1146, 720, 509, 509, 509, 509, 492, 492, 509, 262, 262, 262, 262, 262, 262, 22, 123, 1, -18, '', 0, 30, 24, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 80, 80, 8498, 8320, 1173, 740, 518, 518, 518, 518, 495, 495, 518, 285, 285, 285, 285, 285, 285, 22, 125, 1, -18, '', 0, 31, 25, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 80, 80, 9858, 9360, 1306, 740, 527, 527, 527, 527, 508, 508, 527, 263, 263, 263, 263, 263, 263, 22, 123, 1, -18, '', 0, 32, 24, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 80, 80, 10018, 9600, 1333, 760, 536, 536, 536, 536, 511, 511, 536, 286, 286, 286, 286, 286, 286, 22, 125, 1, -18, '', 0, 33, 25, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 80, 80, 10178, 9840, 1360, 780, 545, 545, 545, 545, 514, 514, 545, 309, 309, 309, 309, 309, 309, 22, 126, 1, -18, '', 0, 34, 26, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 80, 80, 10338, 10080, 1386, 800, 554, 554, 554, 554, 517, 517, 554, 332, 332, 332, 332, 332, 332, 22, 127, 1, -18, '', 0, 35, 27, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 80, 80, 10498, 10320, 1413, 820, 563, 563, 563, 563, 520, 520, 563, 355, 355, 355, 355, 355, 355, 23, 129, 1, -18, '', 0, 36, 28, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 81, 81, 10842, 0, 2795, 2268, 547, 547, 547, 547, 420, 420, 547, 195, 195, 195, 195, 195, 195, 86, 437, 4, -30, '', 0, 40, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 81, 81, 11247, 0, 2916, 2309, 556, 556, 556, 556, 422, 422, 556, 218, 218, 218, 218, 218, 218, 86, 439, 4, -31, '', 0, 42, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 81, 81, 11652, 0, 3038, 2349, 565, 565, 565, 565, 424, 424, 565, 241, 241, 241, 241, 241, 241, 87, 442, 4, -32, '', 0, 44, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 81, 81, 12057, 0, 3159, 2390, 574, 574, 574, 574, 426, 426, 574, 264, 264, 264, 264, 264, 264, 87, 445, 4, -33, '', 0, 46, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 81, 81, 12462, 0, 3281, 2430, 583, 583, 583, 583, 428, 428, 583, 287, 287, 287, 287, 287, 287, 88, 448, 4, -34, '', 0, 48, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 81, 81, 13677, 0, 3443, 2470, 592, 592, 592, 592, 440, 440, 592, 265, 265, 265, 265, 265, 265, 87, 444, 4, -35, '', 0, 50, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 81, 81, 14082, 0, 3564, 2511, 601, 601, 601, 601, 442, 442, 601, 288, 288, 288, 288, 288, 288, 88, 446, 4, -36, '', 0, 52, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 81, 81, 14487, 0, 3686, 2551, 610, 610, 610, 610, 444, 444, 610, 311, 311, 311, 311, 311, 311, 88, 449, 4, -37, '', 0, 54, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 81, 81, 14892, 0, 3807, 2592, 619, 619, 619, 619, 446, 446, 619, 334, 334, 334, 334, 334, 334, 89, 452, 4, -38, '', 0, 56, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 81, 81, 15297, 0, 3929, 2632, 628, 628, 628, 628, 448, 448, 628, 357, 357, 357, 357, 357, 357, 89, 455, 4, -39, '', 0, 58, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 81, 81, 8935, 7517, 2187, 1364, 507, 507, 507, 507, 491, 491, 507, 195, 195, 195, 195, 195, 195, 21, 120, 1, -18, '', 0, 28, 21, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 81, 81, 9178, 7760, 2268, 1391, 516, 516, 516, 516, 494, 494, 516, 218, 218, 218, 218, 218, 218, 21, 122, 1, -18, '', 0, 29, 22, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 81, 81, 9421, 8003, 2349, 1418, 525, 525, 525, 525, 497, 497, 525, 241, 241, 241, 241, 241, 241, 22, 123, 1, -18, '', 0, 30, 23, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 81, 81, 9664, 8246, 2430, 1445, 534, 534, 534, 534, 500, 500, 534, 264, 264, 264, 264, 264, 264, 22, 125, 1, -18, '', 0, 31, 24, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 81, 81, 9907, 8489, 2511, 1472, 543, 543, 543, 543, 503, 503, 543, 287, 287, 287, 287, 287, 287, 22, 126, 1, -18, '', 0, 32, 25, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 81, 81, 11365, 9542, 2673, 1485, 552, 552, 552, 552, 516, 516, 552, 265, 265, 265, 265, 265, 265, 22, 125, 1, -18, '', 0, 34, 24, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 81, 81, 11608, 9785, 2754, 1512, 561, 561, 561, 561, 519, 519, 561, 288, 288, 288, 288, 288, 288, 22, 126, 1, -18, '', 0, 35, 25, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 81, 81, 11851, 10028, 2835, 1539, 570, 570, 570, 570, 522, 522, 570, 311, 311, 311, 311, 311, 311, 22, 127, 1, -18, '', 0, 36, 26, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 81, 81, 12094, 10271, 2916, 1566, 579, 579, 579, 579, 525, 525, 579, 334, 334, 334, 334, 334, 334, 23, 129, 1, -18, '', 0, 37, 27, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 81, 81, 12337, 10514, 2997, 1593, 588, 588, 588, 588, 528, 528, 588, 357, 357, 357, 357, 357, 357, 23, 130, 1, -18, '', 0, 38, 28, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 81, 81, 9407, 0, 1660, 2308, 547, 547, 547, 547, 420, 420, 547, 195, 195, 195, 195, 195, 195, 86, 428, 4, -34, '', 0, 38, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 81, 81, 9731, 0, 1701, 2349, 556, 556, 556, 556, 422, 422, 556, 218, 218, 218, 218, 218, 218, 86, 430, 4, -35, '', 0, 40, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 81, 81, 10055, 0, 1741, 2389, 565, 565, 565, 565, 424, 424, 565, 241, 241, 241, 241, 241, 241, 87, 433, 4, -36, '', 0, 42, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 81, 81, 10379, 0, 1782, 2430, 574, 574, 574, 574, 426, 426, 574, 264, 264, 264, 264, 264, 264, 87, 436, 4, -37, '', 0, 44, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 81, 81, 10703, 0, 1822, 2470, 583, 583, 583, 583, 428, 428, 583, 287, 287, 287, 287, 287, 287, 88, 438, 4, -38, '', 0, 46, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 81, 81, 11837, 0, 2065, 2551, 592, 592, 592, 592, 440, 440, 592, 265, 265, 265, 265, 265, 265, 87, 434, 4, -39, '', 0, 46, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 81, 81, 12161, 0, 2106, 2592, 601, 601, 601, 601, 442, 442, 601, 288, 288, 288, 288, 288, 288, 88, 437, 4, -40, '', 0, 48, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 81, 81, 12485, 0, 2146, 2632, 610, 610, 610, 610, 444, 444, 610, 311, 311, 311, 311, 311, 311, 88, 439, 4, -41, '', 0, 50, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 81, 81, 12809, 0, 2187, 2673, 619, 619, 619, 619, 446, 446, 619, 334, 334, 334, 334, 334, 334, 89, 442, 4, -42, '', 0, 52, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 81, 81, 13133, 0, 2227, 2713, 628, 628, 628, 628, 448, 448, 628, 357, 357, 357, 357, 357, 357, 89, 445, 4, -43, '', 0, 54, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 81, 81, 8101, 7517, 1080, 668, 494, 494, 494, 494, 491, 491, 494, 195, 195, 195, 195, 195, 195, 21, 120, 1, -18, '', 0, 27, 21, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 81, 81, 8263, 7760, 1107, 688, 503, 503, 503, 503, 494, 494, 503, 218, 218, 218, 218, 218, 218, 21, 122, 1, -18, '', 0, 28, 22, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 81, 81, 8425, 8003, 1134, 708, 512, 512, 512, 512, 497, 497, 512, 241, 241, 241, 241, 241, 241, 22, 123, 1, -18, '', 0, 29, 23, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 81, 81, 8587, 8246, 1161, 729, 521, 521, 521, 521, 500, 500, 521, 264, 264, 264, 264, 264, 264, 22, 125, 1, -18, '', 0, 30, 24, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 81, 81, 8749, 8489, 1188, 749, 530, 530, 530, 530, 503, 503, 530, 287, 287, 287, 287, 287, 287, 22, 126, 1, -18, '', 0, 31, 25, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 81, 81, 10126, 9542, 1323, 749, 539, 539, 539, 539, 516, 516, 539, 265, 265, 265, 265, 265, 265, 22, 125, 1, -18, '', 0, 32, 24, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 81, 81, 10288, 9785, 1350, 769, 548, 548, 548, 548, 519, 519, 548, 288, 288, 288, 288, 288, 288, 22, 126, 1, -18, '', 0, 33, 25, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 81, 81, 10450, 10028, 1377, 789, 557, 557, 557, 557, 522, 522, 557, 311, 311, 311, 311, 311, 311, 22, 127, 1, -18, '', 0, 34, 26, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 81, 81, 10612, 10271, 1404, 810, 566, 566, 566, 566, 525, 525, 566, 334, 334, 334, 334, 334, 334, 23, 129, 1, -18, '', 0, 35, 27, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 81, 81, 10774, 10514, 1431, 830, 575, 575, 575, 575, 528, 528, 575, 357, 357, 357, 357, 357, 357, 23, 130, 1, -18, '', 0, 36, 28, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 82, 82, 11166, 0, 2829, 2296, 561, 561, 561, 561, 428, 428, 561, 197, 197, 197, 197, 197, 197, 89, 453, 4, -31, '', 0, 40, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 82, 82, 11576, 0, 2952, 2337, 570, 570, 570, 570, 430, 430, 570, 220, 220, 220, 220, 220, 220, 90, 456, 4, -32, '', 0, 42, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 82, 82, 11986, 0, 3075, 2378, 579, 579, 579, 579, 432, 432, 579, 243, 243, 243, 243, 243, 243, 90, 459, 4, -33, '', 0, 44, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 82, 82, 12396, 0, 3198, 2419, 588, 588, 588, 588, 434, 434, 588, 266, 266, 266, 266, 266, 266, 91, 461, 4, -34, '', 0, 46, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 82, 82, 12806, 0, 3321, 2460, 597, 597, 597, 597, 436, 436, 597, 289, 289, 289, 289, 289, 289, 91, 464, 4, -35, '', 0, 48, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 82, 82, 14036, 0, 3485, 2501, 607, 607, 607, 607, 448, 448, 607, 267, 267, 267, 267, 267, 267, 90, 460, 4, -36, '', 0, 50, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 82, 82, 14446, 0, 3608, 2542, 616, 616, 616, 616, 450, 450, 616, 290, 290, 290, 290, 290, 290, 91, 463, 4, -37, '', 0, 52, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 82, 82, 14856, 0, 3731, 2583, 625, 625, 625, 625, 452, 452, 625, 313, 313, 313, 313, 313, 313, 91, 466, 4, -38, '', 0, 54, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 82, 82, 15266, 0, 3854, 2624, 634, 634, 634, 634, 454, 454, 634, 336, 336, 336, 336, 336, 336, 92, 468, 4, -39, '', 0, 56, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 82, 82, 15676, 0, 3977, 2665, 643, 643, 643, 643, 456, 456, 643, 359, 359, 359, 359, 359, 359, 92, 471, 4, -40, '', 0, 58, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 82, 82, 9202, 7675, 2214, 1380, 520, 520, 520, 520, 500, 500, 520, 197, 197, 197, 197, 197, 197, 21, 122, 1, -18, '', 0, 28, 21, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 82, 82, 9448, 7921, 2296, 1407, 529, 529, 529, 529, 503, 503, 529, 220, 220, 220, 220, 220, 220, 22, 123, 1, -18, '', 0, 29, 22, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 82, 82, 9694, 8167, 2378, 1435, 538, 538, 538, 538, 506, 506, 538, 243, 243, 243, 243, 243, 243, 22, 125, 1, -18, '', 0, 30, 23, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 82, 82, 9940, 8413, 2460, 1462, 547, 547, 547, 547, 509, 509, 547, 266, 266, 266, 266, 266, 266, 22, 126, 1, -18, '', 0, 31, 24, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 82, 82, 10186, 8659, 2542, 1489, 556, 556, 556, 556, 512, 512, 556, 289, 289, 289, 289, 289, 289, 22, 127, 1, -18, '', 0, 32, 25, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 82, 82, 11662, 9725, 2706, 1503, 566, 566, 566, 566, 525, 525, 566, 267, 267, 267, 267, 267, 267, 22, 126, 1, -18, '', 0, 34, 24, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 82, 82, 11908, 9971, 2788, 1530, 575, 575, 575, 575, 528, 528, 575, 290, 290, 290, 290, 290, 290, 22, 127, 1, -18, '', 0, 35, 25, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 82, 82, 12154, 10217, 2870, 1558, 584, 584, 584, 584, 531, 531, 584, 313, 313, 313, 313, 313, 313, 23, 129, 1, -18, '', 0, 36, 26, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 82, 82, 12400, 10463, 2952, 1585, 593, 593, 593, 593, 534, 534, 593, 336, 336, 336, 336, 336, 336, 23, 130, 1, -18, '', 0, 37, 27, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 82, 82, 12646, 10709, 3034, 1612, 602, 602, 602, 602, 537, 537, 602, 359, 359, 359, 359, 359, 359, 23, 132, 1, -18, '', 0, 38, 28, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 82, 82, 9690, 0, 1681, 2337, 561, 561, 561, 561, 428, 428, 561, 197, 197, 197, 197, 197, 197, 89, 444, 4, -34, '', 0, 38, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 82, 82, 10018, 0, 1722, 2378, 570, 570, 570, 570, 430, 430, 570, 220, 220, 220, 220, 220, 220, 90, 447, 4, -35, '', 0, 40, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 82, 82, 10346, 0, 1763, 2419, 579, 579, 579, 579, 432, 432, 579, 243, 243, 243, 243, 243, 243, 90, 449, 4, -36, '', 0, 42, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 82, 82, 10674, 0, 1804, 2460, 588, 588, 588, 588, 434, 434, 588, 266, 266, 266, 266, 266, 266, 91, 452, 4, -37, '', 0, 44, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 82, 82, 11002, 0, 1845, 2501, 597, 597, 597, 597, 436, 436, 597, 289, 289, 289, 289, 289, 289, 91, 454, 4, -38, '', 0, 46, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 82, 82, 12150, 0, 2091, 2583, 607, 607, 607, 607, 448, 448, 607, 267, 267, 267, 267, 267, 267, 90, 451, 4, -39, '', 0, 46, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 82, 82, 12478, 0, 2132, 2624, 616, 616, 616, 616, 450, 450, 616, 290, 290, 290, 290, 290, 290, 91, 453, 4, -40, '', 0, 48, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 82, 82, 12806, 0, 2173, 2665, 625, 625, 625, 625, 452, 452, 625, 313, 313, 313, 313, 313, 313, 91, 456, 4, -41, '', 0, 50, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 82, 82, 13134, 0, 2214, 2706, 634, 634, 634, 634, 454, 454, 634, 336, 336, 336, 336, 336, 336, 92, 458, 4, -42, '', 0, 52, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 82, 82, 13462, 0, 2255, 2747, 643, 643, 643, 643, 456, 456, 643, 359, 359, 359, 359, 359, 359, 92, 461, 4, -43, '', 0, 54, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 82, 82, 8350, 7675, 1093, 676, 507, 507, 507, 507, 500, 500, 507, 197, 197, 197, 197, 197, 197, 21, 122, 1, -18, '', 0, 27, 21, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 82, 82, 8514, 7921, 1120, 697, 516, 516, 516, 516, 503, 503, 516, 220, 220, 220, 220, 220, 220, 22, 123, 1, -18, '', 0, 28, 22, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 82, 82, 8678, 8167, 1148, 717, 525, 525, 525, 525, 506, 506, 525, 243, 243, 243, 243, 243, 243, 22, 125, 1, -18, '', 0, 29, 23, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 82, 82, 8842, 8413, 1175, 738, 534, 534, 534, 534, 509, 509, 534, 266, 266, 266, 266, 266, 266, 22, 126, 1, -18, '', 0, 30, 24, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 82, 82, 9006, 8659, 1202, 758, 543, 543, 543, 543, 512, 512, 543, 289, 289, 289, 289, 289, 289, 22, 127, 1, -18, '', 0, 31, 25, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 82, 82, 10400, 9725, 1339, 758, 553, 553, 553, 553, 525, 525, 553, 267, 267, 267, 267, 267, 267, 22, 126, 1, -18, '', 0, 32, 24, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 82, 82, 10564, 9971, 1366, 779, 562, 562, 562, 562, 528, 528, 562, 290, 290, 290, 290, 290, 290, 22, 127, 1, -18, '', 0, 33, 25, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 82, 82, 10728, 10217, 1394, 799, 571, 571, 571, 571, 531, 531, 571, 313, 313, 313, 313, 313, 313, 23, 129, 1, -18, '', 0, 34, 26, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 82, 82, 10892, 10463, 1421, 820, 580, 580, 580, 580, 534, 534, 580, 336, 336, 336, 336, 336, 336, 23, 130, 1, -18, '', 0, 35, 27, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 82, 82, 11056, 10709, 1448, 840, 589, 589, 589, 589, 537, 537, 589, 359, 359, 359, 359, 359, 359, 23, 132, 1, -18, '', 0, 36, 28, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 83, 83, 11498, 0, 2864, 2324, 574, 574, 574, 574, 436, 436, 574, 199, 199, 199, 199, 199, 199, 92, 469, 4, -31, '', 0, 40, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 83, 83, 11913, 0, 2988, 2366, 583, 583, 583, 583, 438, 438, 583, 222, 222, 222, 222, 222, 222, 93, 472, 4, -32, '', 0, 42, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 83, 83, 12328, 0, 3113, 2407, 592, 592, 592, 592, 440, 440, 592, 245, 245, 245, 245, 245, 245, 93, 475, 4, -33, '', 0, 44, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 83, 83, 12743, 0, 3237, 2449, 601, 601, 601, 601, 442, 442, 601, 268, 268, 268, 268, 268, 268, 94, 478, 4, -34, '', 0, 46, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 83, 83, 13158, 0, 3362, 2490, 610, 610, 610, 610, 444, 444, 610, 291, 291, 291, 291, 291, 291, 94, 481, 4, -35, '', 0, 48, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 83, 83, 14403, 0, 3528, 2531, 620, 620, 620, 620, 456, 456, 620, 271, 271, 271, 271, 271, 271, 94, 476, 4, -36, '', 0, 50, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 83, 83, 14818, 0, 3652, 2573, 629, 629, 629, 629, 458, 458, 629, 294, 294, 294, 294, 294, 294, 94, 479, 4, -37, '', 0, 52, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 83, 83, 15233, 0, 3777, 2614, 638, 638, 638, 638, 460, 460, 638, 317, 317, 317, 317, 317, 317, 95, 482, 4, -38, '', 0, 54, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 83, 83, 15648, 0, 3901, 2656, 647, 647, 647, 647, 462, 462, 647, 340, 340, 340, 340, 340, 340, 95, 485, 4, -39, '', 0, 56, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 83, 83, 16063, 0, 4026, 2697, 656, 656, 656, 656, 464, 464, 656, 363, 363, 363, 363, 363, 363, 96, 488, 4, -40, '', 0, 58, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 83, 83, 9475, 7835, 2241, 1397, 533, 533, 533, 533, 508, 508, 533, 199, 199, 199, 199, 199, 199, 22, 123, 1, -18, '', 0, 28, 21, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 83, 83, 9724, 8084, 2324, 1425, 542, 542, 542, 542, 511, 511, 542, 222, 222, 222, 222, 222, 222, 22, 125, 1, -18, '', 0, 29, 22, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 83, 83, 9973, 8333, 2407, 1453, 551, 551, 551, 551, 514, 514, 551, 245, 245, 245, 245, 245, 245, 22, 126, 1, -18, '', 0, 30, 23, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 83, 83, 10222, 8582, 2490, 1480, 560, 560, 560, 560, 517, 517, 560, 268, 268, 268, 268, 268, 268, 22, 127, 1, -18, '', 0, 31, 24, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 83, 83, 10471, 8831, 2573, 1508, 569, 569, 569, 569, 520, 520, 569, 291, 291, 291, 291, 291, 291, 23, 129, 1, -18, '', 0, 32, 25, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 83, 83, 11965, 9910, 2739, 1521, 579, 579, 579, 579, 533, 533, 579, 271, 271, 271, 271, 271, 271, 22, 127, 1, -18, '', 0, 34, 24, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 83, 83, 12214, 10159, 2822, 1549, 588, 588, 588, 588, 536, 536, 588, 294, 294, 294, 294, 294, 294, 23, 129, 1, -18, '', 0, 35, 25, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 83, 83, 12463, 10408, 2905, 1577, 597, 597, 597, 597, 539, 539, 597, 317, 317, 317, 317, 317, 317, 23, 130, 1, -18, '', 0, 36, 26, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 83, 83, 12712, 10657, 2988, 1604, 606, 606, 606, 606, 542, 542, 606, 340, 340, 340, 340, 340, 340, 23, 132, 1, -18, '', 0, 37, 27, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 83, 83, 12961, 10906, 3071, 1632, 615, 615, 615, 615, 545, 545, 615, 363, 363, 363, 363, 363, 363, 23, 133, 1, -18, '', 0, 38, 28, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 83, 83, 9979, 0, 1701, 2365, 574, 574, 574, 574, 436, 436, 574, 199, 199, 199, 199, 199, 199, 92, 460, 4, -34, '', 0, 38, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 83, 83, 10311, 0, 1743, 2407, 583, 583, 583, 583, 438, 438, 583, 222, 222, 222, 222, 222, 222, 93, 463, 4, -35, '', 0, 40, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 83, 83, 10643, 0, 1784, 2448, 592, 592, 592, 592, 440, 440, 592, 245, 245, 245, 245, 245, 245, 93, 466, 4, -36, '', 0, 42, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 83, 83, 10975, 0, 1826, 2490, 601, 601, 601, 601, 442, 442, 601, 268, 268, 268, 268, 268, 268, 94, 468, 4, -37, '', 0, 44, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 83, 83, 11307, 0, 1867, 2531, 610, 610, 610, 610, 444, 444, 610, 291, 291, 291, 291, 291, 291, 94, 471, 4, -38, '', 0, 46, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 83, 83, 12469, 0, 2116, 2614, 620, 620, 620, 620, 456, 456, 620, 271, 271, 271, 271, 271, 271, 94, 467, 4, -39, '', 0, 46, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 83, 83, 12801, 0, 2158, 2656, 629, 629, 629, 629, 458, 458, 629, 294, 294, 294, 294, 294, 294, 94, 469, 4, -40, '', 0, 48, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 83, 83, 13133, 0, 2199, 2697, 638, 638, 638, 638, 460, 460, 638, 317, 317, 317, 317, 317, 317, 95, 472, 4, -41, '', 0, 50, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 83, 83, 13465, 0, 2241, 2739, 647, 647, 647, 647, 462, 462, 647, 340, 340, 340, 340, 340, 340, 95, 475, 4, -42, '', 0, 52, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 83, 83, 13797, 0, 2282, 2780, 656, 656, 656, 656, 464, 464, 656, 363, 363, 363, 363, 363, 363, 96, 477, 4, -43, '', 0, 54, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 83, 83, 8604, 7835, 1106, 684, 519, 519, 519, 519, 508, 508, 519, 199, 199, 199, 199, 199, 199, 22, 123, 1, -18, '', 0, 27, 21, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 83, 83, 8770, 8084, 1134, 705, 528, 528, 528, 528, 511, 511, 528, 222, 222, 222, 222, 222, 222, 22, 125, 1, -18, '', 0, 28, 22, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 83, 83, 8936, 8333, 1162, 726, 537, 537, 537, 537, 514, 514, 537, 245, 245, 245, 245, 245, 245, 22, 126, 1, -18, '', 0, 29, 23, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 83, 83, 9102, 8582, 1189, 747, 546, 546, 546, 546, 517, 517, 546, 268, 268, 268, 268, 268, 268, 22, 127, 1, -18, '', 0, 30, 24, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 83, 83, 9268, 8831, 1217, 767, 555, 555, 555, 555, 520, 520, 555, 291, 291, 291, 291, 291, 291, 23, 129, 1, -18, '', 0, 31, 25, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 83, 83, 10679, 9910, 1355, 767, 565, 565, 565, 565, 533, 533, 565, 271, 271, 271, 271, 271, 271, 22, 127, 1, -18, '', 0, 32, 24, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 83, 83, 10845, 10159, 1383, 788, 574, 574, 574, 574, 536, 536, 574, 294, 294, 294, 294, 294, 294, 23, 129, 1, -18, '', 0, 33, 25, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 83, 83, 11011, 10408, 1411, 809, 583, 583, 583, 583, 539, 539, 583, 317, 317, 317, 317, 317, 317, 23, 130, 1, -18, '', 0, 34, 26, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 83, 83, 11177, 10657, 1438, 830, 592, 592, 592, 592, 542, 542, 592, 340, 340, 340, 340, 340, 340, 23, 132, 1, -18, '', 0, 35, 27, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 83, 83, 11343, 10906, 1466, 850, 601, 601, 601, 601, 545, 545, 601, 363, 363, 363, 363, 363, 363, 23, 133, 1, -18, '', 0, 36, 28, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 84, 84, 11837, 0, 2898, 2352, 588, 588, 588, 588, 444, 444, 588, 202, 202, 202, 202, 202, 202, 96, 486, 4, -32, '', 0, 41, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 84, 84, 12257, 0, 3024, 2394, 597, 597, 597, 597, 446, 446, 597, 226, 226, 226, 226, 226, 226, 96, 489, 4, -33, '', 0, 43, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 84, 84, 12677, 0, 3150, 2436, 606, 606, 606, 606, 448, 448, 606, 250, 250, 250, 250, 250, 250, 97, 491, 4, -34, '', 0, 45, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 84, 84, 13097, 0, 3276, 2478, 615, 615, 615, 615, 450, 450, 615, 274, 274, 274, 274, 274, 274, 97, 494, 4, -35, '', 0, 47, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 84, 84, 13517, 0, 3402, 2520, 624, 624, 624, 624, 452, 452, 624, 298, 298, 298, 298, 298, 298, 98, 497, 4, -36, '', 0, 49, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 84, 84, 14777, 0, 3570, 2562, 635, 635, 635, 635, 464, 464, 635, 274, 274, 274, 274, 274, 274, 97, 493, 4, -37, '', 0, 51, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 84, 84, 15197, 0, 3696, 2604, 644, 644, 644, 644, 466, 466, 644, 298, 298, 298, 298, 298, 298, 97, 496, 4, -38, '', 0, 53, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 84, 84, 15617, 0, 3822, 2646, 653, 653, 653, 653, 468, 468, 653, 322, 322, 322, 322, 322, 322, 98, 498, 4, -39, '', 0, 55, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 84, 84, 16037, 0, 3948, 2688, 662, 662, 662, 662, 470, 470, 662, 346, 346, 346, 346, 346, 346, 98, 501, 4, -40, '', 0, 57, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 84, 84, 16457, 0, 4074, 2730, 671, 671, 671, 671, 472, 472, 671, 370, 370, 370, 370, 370, 370, 99, 504, 4, -41, '', 0, 59, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 84, 84, 9754, 7997, 2268, 1414, 546, 546, 546, 546, 517, 517, 546, 202, 202, 202, 202, 202, 202, 22, 125, 1, -18, '', 0, 29, 21, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 84, 84, 10006, 8249, 2352, 1442, 555, 555, 555, 555, 520, 520, 555, 226, 226, 226, 226, 226, 226, 22, 126, 1, -18, '', 0, 30, 22, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 84, 84, 10258, 8501, 2436, 1470, 564, 564, 564, 564, 523, 523, 564, 250, 250, 250, 250, 250, 250, 22, 127, 1, -18, '', 0, 31, 23, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 84, 84, 10510, 8753, 2520, 1498, 573, 573, 573, 573, 526, 526, 573, 274, 274, 274, 274, 274, 274, 23, 129, 1, -18, '', 0, 32, 24, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 84, 84, 10762, 9005, 2604, 1526, 582, 582, 582, 582, 529, 529, 582, 298, 298, 298, 298, 298, 298, 23, 130, 1, -18, '', 0, 33, 25, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 84, 84, 12274, 10097, 2772, 1540, 593, 593, 593, 593, 542, 542, 593, 274, 274, 274, 274, 274, 274, 23, 129, 1, -18, '', 0, 35, 24, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 84, 84, 12526, 10349, 2856, 1568, 602, 602, 602, 602, 545, 545, 602, 298, 298, 298, 298, 298, 298, 23, 130, 1, -18, '', 0, 36, 25, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 84, 84, 12778, 10601, 2940, 1596, 611, 611, 611, 611, 548, 548, 611, 322, 322, 322, 322, 322, 322, 23, 132, 1, -18, '', 0, 37, 26, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 84, 84, 13030, 10853, 3024, 1624, 620, 620, 620, 620, 551, 551, 620, 346, 346, 346, 346, 346, 346, 23, 133, 1, -18, '', 0, 38, 27, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 84, 84, 13282, 11105, 3108, 1652, 629, 629, 629, 629, 554, 554, 629, 370, 370, 370, 370, 370, 370, 24, 134, 1, -18, '', 0, 39, 28, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 84, 84, 10274, 0, 1722, 2394, 588, 588, 588, 588, 444, 444, 588, 202, 202, 202, 202, 202, 202, 96, 477, 4, -34, '', 0, 39, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 84, 84, 10610, 0, 1764, 2436, 597, 597, 597, 597, 446, 446, 597, 226, 226, 226, 226, 226, 226, 96, 479, 4, -35, '', 0, 41, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 84, 84, 10946, 0, 1806, 2478, 606, 606, 606, 606, 448, 448, 606, 250, 250, 250, 250, 250, 250, 97, 482, 4, -36, '', 0, 43, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 84, 84, 11282, 0, 1848, 2520, 615, 615, 615, 615, 450, 450, 615, 274, 274, 274, 274, 274, 274, 97, 484, 4, -37, '', 0, 45, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 84, 84, 11618, 0, 1890, 2562, 624, 624, 624, 624, 452, 452, 624, 298, 298, 298, 298, 298, 298, 98, 487, 4, -38, '', 0, 47, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 84, 84, 12794, 0, 2142, 2646, 635, 635, 635, 635, 464, 464, 635, 274, 274, 274, 274, 274, 274, 97, 483, 4, -39, '', 0, 47, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 84, 84, 13130, 0, 2184, 2688, 644, 644, 644, 644, 466, 466, 644, 298, 298, 298, 298, 298, 298, 97, 486, 4, -40, '', 0, 49, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 84, 84, 13466, 0, 2226, 2730, 653, 653, 653, 653, 468, 468, 653, 322, 322, 322, 322, 322, 322, 98, 488, 4, -41, '', 0, 51, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 84, 84, 13802, 0, 2268, 2772, 662, 662, 662, 662, 470, 470, 662, 346, 346, 346, 346, 346, 346, 98, 491, 4, -42, '', 0, 53, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 84, 84, 14138, 0, 2310, 2814, 671, 671, 671, 671, 472, 472, 671, 370, 370, 370, 370, 370, 370, 99, 494, 4, -43, '', 0, 55, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 84, 84, 8863, 7997, 1120, 693, 532, 532, 532, 532, 517, 517, 532, 202, 202, 202, 202, 202, 202, 22, 125, 1, -18, '', 0, 28, 21, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 84, 84, 9031, 8249, 1148, 714, 541, 541, 541, 541, 520, 520, 541, 226, 226, 226, 226, 226, 226, 22, 126, 1, -18, '', 0, 29, 22, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 84, 84, 9199, 8501, 1176, 735, 550, 550, 550, 550, 523, 523, 550, 250, 250, 250, 250, 250, 250, 22, 127, 1, -18, '', 0, 30, 23, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 84, 84, 9367, 8753, 1204, 756, 559, 559, 559, 559, 526, 526, 559, 274, 274, 274, 274, 274, 274, 23, 129, 1, -18, '', 0, 31, 24, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 84, 84, 9535, 9005, 1232, 777, 568, 568, 568, 568, 529, 529, 568, 298, 298, 298, 298, 298, 298, 23, 130, 1, -18, '', 0, 32, 25, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 84, 84, 10963, 10097, 1372, 777, 579, 579, 579, 579, 542, 542, 579, 274, 274, 274, 274, 274, 274, 23, 129, 1, -18, '', 0, 33, 24, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 84, 84, 11131, 10349, 1400, 798, 588, 588, 588, 588, 545, 545, 588, 298, 298, 298, 298, 298, 298, 23, 130, 1, -18, '', 0, 34, 25, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 84, 84, 11299, 10601, 1428, 819, 597, 597, 597, 597, 548, 548, 597, 322, 322, 322, 322, 322, 322, 23, 132, 1, -18, '', 0, 35, 26, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 84, 84, 11467, 10853, 1456, 840, 606, 606, 606, 606, 551, 551, 606, 346, 346, 346, 346, 346, 346, 23, 133, 1, -18, '', 0, 36, 27, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 84, 84, 11635, 11105, 1484, 861, 615, 615, 615, 615, 554, 554, 615, 370, 370, 370, 370, 370, 370, 24, 134, 1, -18, '', 0, 37, 28, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 85, 85, 12183, 0, 2933, 2380, 602, 602, 602, 602, 452, 452, 602, 204, 204, 204, 204, 204, 204, 99, 502, 4, -34, '', 0, 41, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 85, 85, 12608, 0, 3060, 2423, 611, 611, 611, 611, 454, 454, 611, 228, 228, 228, 228, 228, 228, 99, 505, 4, -35, '', 0, 43, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 85, 85, 13033, 0, 3188, 2465, 620, 620, 620, 620, 456, 456, 620, 252, 252, 252, 252, 252, 252, 100, 508, 4, -36, '', 0, 45, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 85, 85, 13458, 0, 3315, 2508, 629, 629, 629, 629, 458, 458, 629, 276, 276, 276, 276, 276, 276, 100, 511, 4, -37, '', 0, 47, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 85, 85, 13883, 0, 3443, 2550, 638, 638, 638, 638, 460, 460, 638, 300, 300, 300, 300, 300, 300, 101, 513, 4, -38, '', 0, 49, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 85, 85, 15158, 0, 3613, 2592, 649, 649, 649, 649, 472, 472, 649, 276, 276, 276, 276, 276, 276, 100, 509, 4, -39, '', 0, 51, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 85, 85, 15583, 0, 3740, 2635, 658, 658, 658, 658, 474, 474, 658, 300, 300, 300, 300, 300, 300, 101, 512, 4, -40, '', 0, 53, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 85, 85, 16008, 0, 3868, 2677, 667, 667, 667, 667, 476, 476, 667, 324, 324, 324, 324, 324, 324, 101, 515, 4, -41, '', 0, 55, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 85, 85, 16433, 0, 3995, 2720, 676, 676, 676, 676, 478, 478, 676, 348, 348, 348, 348, 348, 348, 102, 518, 4, -42, '', 0, 57, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 85, 85, 16858, 0, 4123, 2762, 685, 685, 685, 685, 480, 480, 685, 372, 372, 372, 372, 372, 372, 102, 520, 4, -43, '', 0, 59, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 85, 85, 10040, 8160, 2295, 1431, 559, 559, 559, 559, 525, 525, 559, 204, 204, 204, 204, 204, 204, 22, 126, 1, -18, '', 0, 29, 22, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 85, 85, 10295, 8415, 2380, 1459, 568, 568, 568, 568, 528, 528, 568, 228, 228, 228, 228, 228, 228, 22, 127, 1, -18, '', 0, 30, 23, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 85, 85, 10550, 8670, 2465, 1488, 577, 577, 577, 577, 531, 531, 577, 252, 252, 252, 252, 252, 252, 23, 129, 1, -18, '', 0, 31, 24, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 85, 85, 10805, 8925, 2550, 1516, 586, 586, 586, 586, 534, 534, 586, 276, 276, 276, 276, 276, 276, 23, 130, 1, -18, '', 0, 32, 25, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 85, 85, 11060, 9180, 2635, 1544, 595, 595, 595, 595, 537, 537, 595, 300, 300, 300, 300, 300, 300, 23, 132, 1, -18, '', 0, 33, 26, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 85, 85, 12590, 10285, 2805, 1558, 606, 606, 606, 606, 550, 550, 606, 276, 276, 276, 276, 276, 276, 23, 130, 1, -18, '', 0, 35, 25, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 85, 85, 12845, 10540, 2890, 1586, 615, 615, 615, 615, 553, 553, 615, 300, 300, 300, 300, 300, 300, 23, 132, 1, -18, '', 0, 36, 26, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 85, 85, 13100, 10795, 2975, 1615, 624, 624, 624, 624, 556, 556, 624, 324, 324, 324, 324, 324, 324, 23, 133, 1, -18, '', 0, 37, 27, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 85, 85, 13355, 11050, 3060, 1643, 633, 633, 633, 633, 559, 559, 633, 348, 348, 348, 348, 348, 348, 24, 134, 1, -18, '', 0, 38, 28, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 85, 85, 13610, 11305, 3145, 1671, 642, 642, 642, 642, 562, 562, 642, 372, 372, 372, 372, 372, 372, 24, 136, 1, -18, '', 0, 39, 29, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 85, 85, 10576, 0, 1742, 2422, 602, 602, 602, 602, 452, 452, 602, 204, 204, 204, 204, 204, 204, 99, 493, 4, -34, '', 0, 39, 0, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 85, 85, 10916, 0, 1785, 2465, 611, 611, 611, 611, 454, 454, 611, 228, 228, 228, 228, 228, 228, 99, 496, 4, -35, '', 0, 41, 0, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 85, 85, 11256, 0, 1827, 2507, 620, 620, 620, 620, 456, 456, 620, 252, 252, 252, 252, 252, 252, 100, 498, 4, -36, '', 0, 43, 0, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 85, 85, 11596, 0, 1870, 2550, 629, 629, 629, 629, 458, 458, 629, 276, 276, 276, 276, 276, 276, 100, 501, 4, -37, '', 0, 45, 0, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 85, 85, 11936, 0, 1912, 2592, 638, 638, 638, 638, 460, 460, 638, 300, 300, 300, 300, 300, 300, 101, 503, 4, -38, '', 0, 47, 0, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 85, 85, 13126, 0, 2167, 2677, 649, 649, 649, 649, 472, 472, 649, 276, 276, 276, 276, 276, 276, 100, 499, 4, -39, '', 0, 47, 0, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 85, 85, 13466, 0, 2210, 2720, 658, 658, 658, 658, 474, 474, 658, 300, 300, 300, 300, 300, 300, 101, 502, 4, -40, '', 0, 49, 0, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 85, 85, 13806, 0, 2252, 2762, 667, 667, 667, 667, 476, 476, 667, 324, 324, 324, 324, 324, 324, 101, 505, 4, -41, '', 0, 51, 0, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 85, 85, 14146, 0, 2295, 2805, 676, 676, 676, 676, 478, 478, 676, 348, 348, 348, 348, 348, 348, 102, 507, 4, -42, '', 0, 53, 0, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 85, 85, 14486, 0, 2337, 2847, 685, 685, 685, 685, 480, 480, 685, 372, 372, 372, 372, 372, 372, 102, 510, 4, -43, '', 0, 55, 0, 1.79, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 85, 85, 9128, 8160, 1133, 701, 545, 545, 545, 545, 525, 525, 545, 204, 204, 204, 204, 204, 204, 22, 126, 1, -18, '', 0, 28, 22, 1.25, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 85, 85, 9298, 8415, 1161, 722, 554, 554, 554, 554, 528, 528, 554, 228, 228, 228, 228, 228, 228, 22, 127, 1, -18, '', 0, 29, 23, 1.31, 126, 136); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 85, 85, 9468, 8670, 1190, 743, 563, 563, 563, 563, 531, 531, 563, 252, 252, 252, 252, 252, 252, 23, 129, 1, -18, '', 0, 30, 24, 1.37, 134, 152); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 85, 85, 9638, 8925, 1218, 765, 572, 572, 572, 572, 534, 534, 572, 276, 276, 276, 276, 276, 276, 23, 130, 1, -18, '', 0, 31, 25, 1.43, 142, 168); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 85, 85, 9808, 9180, 1246, 786, 581, 581, 581, 581, 537, 537, 581, 300, 300, 300, 300, 300, 300, 23, 132, 1, -18, '', 0, 32, 26, 1.49, 150, 184); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 85, 85, 11253, 10285, 1388, 786, 592, 592, 592, 592, 550, 550, 592, 276, 276, 276, 276, 276, 276, 23, 130, 1, -18, '', 0, 33, 25, 1.55, 118, 120); +REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 85, 85, 11423, 10540, 1416, 807, 601, 601, 601, 601, 553, 553, 601, 300, 300, 300, 300, 300, 300, 23, 132, 1, -18, '', 0, 34, 26, 1.61, 126, 136); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 85, 85, 11593, 10795, 1445, 828, 610, 610, 610, 610, 556, 556, 610, 324, 324, 324, 324, 324, 324, 23, 133, 1, -18, '', 0, 35, 27, 1.67, 134, 152); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 85, 85, 11763, 11050, 1473, 850, 619, 619, 619, 619, 559, 559, 619, 348, 348, 348, 348, 348, 348, 24, 134, 1, -18, '', 0, 36, 28, 1.73, 142, 168); + -- REPLACE INTO merc_stats ( merc_npc_type_id, clientlevel, level, hp, mana, AC, ATK, STR, STA, DEX, AGI, _INT, WIS, CHA, MR, CR, DR, FR, PR, Corrup, mindmg, maxdmg, attack_count, attack_speed, specialattks, Accuracy, hp_regen_rate, mana_regen_rate, runspeed, spellscale, healscale ) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 85, 85, 11933, 11305, 1501, 871, 628, 628, 628, 628, 562, 562, 628, 372, 372, 372, 372, 372, 372, 24, 136, 1, -18, '', 0, 37, 29, 1.79, 150, 184); + + +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 1, 12, 10649, 208, 1, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 13, 255, 10649, 10649, 1, 1); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 1, 12, 10649, 208, 1, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 13, 255, 10649, 10649, 1, 1); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 1, 12, 10649, 208, 1, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 13, 255, 10649, 10649, 1, 1); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 1, 12, 10649, 208, 1, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 13, 255, 10649, 10649, 1, 1); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 1, 12, 10649, 208, 1, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 13, 255, 10649, 10649, 1, 1); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 1, 12, 10649, 208, 1, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 13, 255, 10649, 10649, 1, 1); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 1, 12, 10649, 208, 1, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 13, 255, 10649, 10649, 1, 1); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 1, 12, 10649, 208, 1, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 13, 255, 10649, 10649, 1, 1); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 1, 12, 10649, 208, 1, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 13, 255, 10649, 10649, 1, 1); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 1, 12, 10649, 208, 1, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 13, 255, 10649, 10649, 1, 1); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 1, 255, 10608, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 1, 255, 10608, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 1, 255, 10608, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 1, 255, 10608, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 1, 255, 10608, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 1, 255, 10608, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 1, 255, 10608, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 1, 255, 10608, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 1, 255, 10608, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 1, 255, 10608, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 1, 12, 10650, 208, 36, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 13, 255, 10650, 10650, 36, 36); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 1, 12, 10650, 208, 36, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 13, 255, 10650, 10650, 36, 36); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 1, 12, 10650, 208, 36, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 13, 255, 10650, 10650, 36, 36); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 1, 12, 10650, 208, 36, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 13, 255, 10650, 10650, 36, 36); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 1, 12, 10650, 208, 36, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 13, 255, 10650, 10650, 36, 36); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 1, 12, 10650, 208, 36, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 13, 255, 10650, 10650, 36, 36); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 1, 12, 10650, 208, 36, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 13, 255, 10650, 10650, 36, 36); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 1, 12, 10650, 208, 36, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 13, 255, 10650, 10650, 36, 36); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 1, 12, 10650, 208, 36, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 13, 255, 10650, 10650, 36, 36); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 1, 12, 10650, 208, 36, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 13, 255, 10650, 10650, 36, 36); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 1, 255, 10681, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 1, 255, 10681, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 1, 255, 10681, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 1, 255, 10681, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 1, 255, 10681, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 1, 255, 10681, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 1, 255, 10681, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 1, 255, 10681, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 1, 255, 10681, 209, 0, 28); +REPLACE INTO merc_weaponinfo (merc_npc_type_id, minlevel, maxlevel, d_meele_texture1, d_meele_texture2, prim_melee_type, sec_melee_type) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 1, 255, 10681, 209, 0, 28); + + +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 1), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 2), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 3), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 4), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 1 AND tier_id = 5), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 1), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 2), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 3), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 4), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 1 AND proficiency_id = 2 AND tier_id = 5), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 1), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 2), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 3), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 4), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 1 AND tier_id = 5), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 1), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 2), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 3), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 4), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 2 AND proficiency_id = 2 AND tier_id = 5), 1, 255, 3, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 1), 1, 255, 2, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 2), 1, 255, 2, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 3), 1, 255, 2, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 4), 1, 255, 2, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 1 AND tier_id = 5), 1, 255, 2, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 1), 1, 255, 2, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 2), 1, 255, 2, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 3), 1, 255, 2, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 4), 1, 255, 2, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 9 AND proficiency_id = 2 AND tier_id = 5), 1, 255, 2, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 1), 1, 255, 10, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 2), 1, 255, 10, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 3), 1, 255, 10, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 4), 1, 255, 10, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 1 AND tier_id = 5), 1, 255, 10, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 1), 1, 255, 10, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 2), 1, 255, 10, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 3), 1, 255, 10, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 4), 1, 255, 10, 0, 0, 0, 0, 0); +REPLACE INTO merc_armorinfo (merc_npc_type_id, minlevel, maxlevel, texture, helmtexture, armortint_id, armortint_red, armortint_green, armortint_blue) VALUES ( (SELECT merc_npc_type_id FROM merc_npc_types WHERE class_id = 12 AND proficiency_id = 2 AND tier_id = 5), 1, 255, 10, 0, 0, 0, 0, 0); + +REPLACE INTO merc_spell_lists (class_id, proficiency_id, name) VALUES (1, 1, 'Apprentice Tank Disciplines'); +REPLACE INTO merc_spell_lists (class_id, proficiency_id, name) VALUES (1, 2, 'Journeyman Tank Disciplines'); +-- REPLACE INTO merc_spell_lists (class_id, proficiency_id, name) VALUES (1, 3, 'Master Tank Disciplines'); +REPLACE INTO merc_spell_lists (class_id, proficiency_id, name) VALUES (2, 1, 'Apprentice Healer Spells'); +REPLACE INTO merc_spell_lists (class_id, proficiency_id, name) VALUES (2, 2, 'Journeyman Healer Spells'); +-- REPLACE INTO merc_spell_lists (class_id, proficiency_id, name) VALUES (2, 3, 'Master Healer Spells'); +REPLACE INTO merc_spell_lists (class_id, proficiency_id, name) VALUES (9, 1, 'Apprentice Melee DPS Disciplines'); +REPLACE INTO merc_spell_lists (class_id, proficiency_id, name) VALUES (9, 2, 'Journeyman Melee DPS Disciplines'); +-- REPLACE INTO merc_spell_lists (class_id, proficiency_id, name) VALUES (9, 3, 'Master Melee DPS Disciplines'); +REPLACE INTO merc_spell_lists (class_id, proficiency_id, name) VALUES (12, 1, 'Apprentice Caster DPS Spells'); +REPLACE INTO merc_spell_lists (class_id, proficiency_id, name) VALUES (12, 2, 'Journeyman Caster DPS Spells'); +-- REPLACE INTO merc_spell_lists (class_id, proficiency_id, name) VALUES (11, 3, 'Master Caster DPS Spells'); + +REPLACE INTO merc_spell_list_entries (merc_spell_list_id, spell_id, spell_type, stance_id, minlevel, maxlevel, slot, procChance) VALUES + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Mercenary Taunt' ORDER BY id DESC LIMIT 1), 1, 0, 1, 255, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Provoke' ORDER BY id DESC LIMIT 1), 1, 0, 20, 51, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Bellow' ORDER BY id DESC LIMIT 1), 1, 0, 52, 55, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Berate' ORDER BY id DESC LIMIT 1), 1, 0, 56, 62, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Mercenary Area Taunt' ORDER BY id DESC LIMIT 1), 1, 0, 59, 255, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Incite' ORDER BY id DESC LIMIT 1), 1, 0, 63, 64, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Bellow of the Mastruq' ORDER BY id DESC LIMIT 1), 1, 0, 65, 69, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Bazu Bellow' ORDER BY id DESC LIMIT 1), 1, 0, 69, 80, 3, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Mock' ORDER BY id DESC LIMIT 1), 1, 0, 70, 74, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Scowl' ORDER BY id DESC LIMIT 1), 1, 0, 75, 79, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Sneer' ORDER BY id DESC LIMIT 1), 1, 0, 80, 84, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Bazu Bluster' ORDER BY id DESC LIMIT 1), 1, 0, 81, 85, 3, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Jeer' ORDER BY id DESC LIMIT 1), 1, 0, 85, 89, 2, 0 ); + +REPLACE INTO merc_spell_list_entries (merc_spell_list_id, spell_id, spell_type, stance_id, minlevel, maxlevel, slot, procChance) VALUES + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Mercenary Taunt' ORDER BY id DESC LIMIT 1), 1, 0, 1, 255, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Provoke' ORDER BY id DESC LIMIT 1), 1, 0, 20, 51, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Bellow' ORDER BY id DESC LIMIT 1), 1, 0, 52, 55, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Berate' ORDER BY id DESC LIMIT 1), 1, 0, 56, 62, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Mercenary Area Taunt' ORDER BY id DESC LIMIT 1), 1, 0, 59, 255, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Incite' ORDER BY id DESC LIMIT 1), 1, 0, 63, 64, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Ancient: Chaos Cry' ORDER BY id DESC LIMIT 1), 1, 0, 65, 69, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Bazu Bellow' ORDER BY id DESC LIMIT 1), 1, 0, 69, 80, 3, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Mock' ORDER BY id DESC LIMIT 1), 1, 0, 70, 74, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Scowl Rk. II' ORDER BY id DESC LIMIT 1), 1, 0, 75, 79, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Sneer Rk. II' ORDER BY id DESC LIMIT 1), 1, 0, 80, 84, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Bazu Bluster Rk. II' ORDER BY id DESC LIMIT 1), 1, 0, 81, 85, 3, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 1 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Jeer Rk. II' ORDER BY id DESC LIMIT 1), 1, 0, 85, 89, 2, 0 ); + + +REPLACE INTO merc_spell_list_entries (merc_spell_list_id, spell_id, spell_type, stance_id, minlevel, maxlevel, slot, procChance) VALUES + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Minor Healing' ORDER BY id DESC LIMIT 1), 2, 0, 1, 3, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Courage' ORDER BY id DESC LIMIT 1), 8, 0, 1, 6, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Light Healing' ORDER BY id DESC LIMIT 1), 2, 0, 4, 9, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Center' ORDER BY id DESC LIMIT 1), 8, 0, 7, 21, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Healing' ORDER BY id DESC LIMIT 1), 2, 0, 10, 19, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Symbol of Transal' ORDER BY id DESC LIMIT 1), 8, 0, 11, 20, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Blessing of Piety' ORDER BY id DESC LIMIT 1), 8, 0, 15, 34, 3, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Daring' ORDER BY id DESC LIMIT 1), 8, 0, 17, 20, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Celestial Remedy' ORDER BY id DESC LIMIT 1), 2, 0, 19, 28, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Greater Healing' ORDER BY id DESC LIMIT 1), 2, 0, 20, 29, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Ward of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 20, 39, 4, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Symbol of Ryltan' ORDER BY id DESC LIMIT 1), 8, 0, 21, 30, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Bravery' ORDER BY id DESC LIMIT 1), 8, 0, 22, 31, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Celestial Health' ORDER BY id DESC LIMIT 1), 2, 0, 29, 43, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Word of Health' ORDER BY id DESC LIMIT 1), 2, 0, 30, 44, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Superior Healing' ORDER BY id DESC LIMIT 1), 2, 0, 30, 52, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Symbol of Pinzarn' ORDER BY id DESC LIMIT 1), 8, 0, 31, 40, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Valor' ORDER BY id DESC LIMIT 1), 8, 0, 32, 39, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Blessing of Faith' ORDER BY id DESC LIMIT 1), 8, 0, 35, 61, 3, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Complete Healing' ORDER BY id DESC LIMIT 1), 2, 0, 39, 75, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Temperance' ORDER BY id DESC LIMIT 1), 8, 0, 40, 59, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Guard of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 40, 53, 4, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Symbol of Naltron' ORDER BY id DESC LIMIT 1), 8, 0, 41, 53, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Celestial Healing' ORDER BY id DESC LIMIT 1), 2, 0, 44, 58, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Word of Healing' ORDER BY id DESC LIMIT 1), 2, 0, 45, 51, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Blessing of Temperance' ORDER BY id DESC LIMIT 1), 8, 0, 45, 59, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Remedy' ORDER BY id DESC LIMIT 1), 2, 0, 51, 58, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Word of Vigor' ORDER BY id DESC LIMIT 1), 2, 0, 52, 56, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Divine Light' ORDER BY id DESC LIMIT 1), 2, 0, 53, 57, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Symbol of Marzin' ORDER BY id DESC LIMIT 1), 8, 0, 54, 60, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Protection of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 54, 61, 4, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Word of Restoration' ORDER BY id DESC LIMIT 1), 2, 0, 57, 63, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Naltron\'s Mark' ORDER BY id DESC LIMIT 1), 8, 0, 58, 59, 6, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Ethereal Light' ORDER BY id DESC LIMIT 1), 2, 0, 58, 62, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Celestial Elixir' ORDER BY id DESC LIMIT 1), 2, 0, 59, 61, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Ethereal Remedy' ORDER BY id DESC LIMIT 1), 8, 0, 59, 60, 6, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Marzin\'s Mark' ORDER BY id DESC LIMIT 1), 8, 0, 60, 62, 6, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Aegolism' ORDER BY id DESC LIMIT 1), 8, 0, 60, 61, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Blessing of Aegolism' ORDER BY id DESC LIMIT 1), 8, 0, 60, 61, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Ancient: Gift of Aegolism' ORDER BY id DESC LIMIT 1), 8, 0, 60, 61, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Ethereal Elixir' ORDER BY id DESC LIMIT 1), 2, 0, 60, 64, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Word of Redemption' ORDER BY id DESC LIMIT 1), 2, 0, 60, 75, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Supernal Remedy' ORDER BY id DESC LIMIT 1), 2, 0, 61, 65, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Symbol of Kazad' ORDER BY id DESC LIMIT 1), 2, 0, 61, 65, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Supernal Elixir' ORDER BY id DESC LIMIT 1), 2, 0, 62, 64, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Virtue' ORDER BY id DESC LIMIT 1), 8, 0, 62, 66, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Blessing of Reverence' ORDER BY id DESC LIMIT 1), 8, 0, 62, 66, 3, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Bulwark of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 62, 66, 4, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Kazad`s Mark' ORDER BY id DESC LIMIT 1), 2, 0, 63, 69, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Supernal Light' ORDER BY id DESC LIMIT 1), 2, 0, 63, 64, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Word of Replenishment' ORDER BY id DESC LIMIT 1), 2, 0, 64, 68, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Aura of Reverence' ORDER BY id DESC LIMIT 1), 8, 0, 64, 66, 3, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Hand of Virtue' ORDER BY id DESC LIMIT 1), 8, 0, 65, 66, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Holy Elixir' ORDER BY id DESC LIMIT 1), 2, 0, 65, 66, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Holy Light' ORDER BY id DESC LIMIT 1), 2, 0, 65, 67, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Symbol of Balikor' ORDER BY id DESC LIMIT 1), 2, 0, 66, 71, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Pious Remedy' ORDER BY id DESC LIMIT 1), 2, 0, 66, 255, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Conviction' ORDER BY id DESC LIMIT 1), 8, 0, 67, 71, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Pious Elixir' ORDER BY id DESC LIMIT 1), 2, 0, 67, 255, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Blessing of Devotion' ORDER BY id DESC LIMIT 1), 8, 0, 67, 70, 3, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Panoply of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 67, 72, 4, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Pious Light' ORDER BY id DESC LIMIT 1), 2, 0, 68, 72, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Aura of Devotion' ORDER BY id DESC LIMIT 1), 8, 0, 69, 255, 7, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Word of Vivification' ORDER BY id DESC LIMIT 1), 2, 0, 69, 79, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Hand of Conviction' ORDER BY id DESC LIMIT 1), 8, 0, 70, 71, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Balikor\'s Mark' ORDER BY id DESC LIMIT 1), 8, 0, 70, 74, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Elixir of Divinity' ORDER BY id DESC LIMIT 1), 2, 0, 70, 74, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Symbol of Elushar' ORDER BY id DESC LIMIT 1), 8, 0, 71, 75, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Sacred Remedy' ORDER BY id DESC LIMIT 1), 2, 0, 71, 75, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Blessing of Purpose' ORDER BY id DESC LIMIT 1), 8, 0, 71, 0, 3, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Tenacity' ORDER BY id DESC LIMIT 1), 8, 0, 72, 76, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Sacred Elixir' ORDER BY id DESC LIMIT 1), 2, 0, 72, 76, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Aura of Purpose' ORDER BY id DESC LIMIT 1), 8, 0, 72, 75, 3, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Sacred Light' ORDER BY id DESC LIMIT 1), 2, 0, 73, 77, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Promised Renewal' ORDER BY id DESC LIMIT 1), 2, 0, 73, 77, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Aegis of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 73, 77, 4, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Hand of Tenacity' ORDER BY id DESC LIMIT 1), 8, 0, 75, 76, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Elushar\'s Mark' ORDER BY id DESC LIMIT 1), 8, 0, 75, 79, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Elixir of Redemption' ORDER BY id DESC LIMIT 1), 2, 0, 75, 79, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Rallied Aegis of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 75, 77, 4, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Solemn Remedy' ORDER BY id DESC LIMIT 1), 2, 0, 76, 80, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Symbol of Kaerra' ORDER BY id DESC LIMIT 1), 8, 0, 76, 80, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Blessing of Resolve' ORDER BY id DESC LIMIT 1), 8, 0, 76, 80, 3, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Temerity' ORDER BY id DESC LIMIT 1), 8, 0, 77, 81, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Solemn Elixir' ORDER BY id DESC LIMIT 1), 2, 0, 77, 81, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Eleventh-Hour' ORDER BY id DESC LIMIT 1), 2, 0, 77, 81, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Aura of Resolve' ORDER BY id DESC LIMIT 1), 8, 0, 77, 80, 3, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Solemn Light' ORDER BY id DESC LIMIT 1), 2, 0, 78, 82, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Promised Restoration' ORDER BY id DESC LIMIT 1), 2, 0, 78, 82, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Shield of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 78, 82, 4, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Hand of Temerity' ORDER BY id DESC LIMIT 1), 8, 0, 80, 81, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Kaerra\'s Mark' ORDER BY id DESC LIMIT 1), 8, 0, 80, 84, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Elixir of Atonement' ORDER BY id DESC LIMIT 1), 2, 0, 80, 84, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Frantic Renewal' ORDER BY id DESC LIMIT 1), 2, 0, 80, 84, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Rallied Shield of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 80, 82, 4, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Symbol of Darianna' ORDER BY id DESC LIMIT 1), 8, 0, 81, 85, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Devout Remedy' ORDER BY id DESC LIMIT 1), 2, 0, 81, 85, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Blessing of Loyalty' ORDER BY id DESC LIMIT 1), 8, 0, 81, 85, 3, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Gallantry' ORDER BY id DESC LIMIT 1), 8, 0, 82, 86, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Devout Elixir' ORDER BY id DESC LIMIT 1), 2, 0, 82, 86, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Twelfth Night' ORDER BY id DESC LIMIT 1), 2, 0, 82, 86, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Aura of Loyalty' ORDER BY id DESC LIMIT 1), 8, 0, 82, 85, 3, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Devout Light' ORDER BY id DESC LIMIT 1), 2, 0, 83, 87, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Promised Recuperation' ORDER BY id DESC LIMIT 1), 2, 0, 83, 87, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Palladium of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 83, 87, 4, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Hand of Gallantry' ORDER BY id DESC LIMIT 1), 8, 0, 85, 86, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Darianna\'s Mark' ORDER BY id DESC LIMIT 1), 8, 0, 85, 89, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Frenetic Renewal' ORDER BY id DESC LIMIT 1), 2, 0, 85, 89, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 1), (SELECT id FROM spells_new WHERE name = 'Rallied Palladium of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 85, 87, 4, 0); + + +REPLACE INTO merc_spell_list_entries (merc_spell_list_id, spell_id, spell_type, stance_id, minlevel, maxlevel, slot, procChance) VALUES + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Minor Healing' ORDER BY id DESC LIMIT 1), 2, 0, 1, 3, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Courage' ORDER BY id DESC LIMIT 1), 8, 0, 1, 6, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Light Healing' ORDER BY id DESC LIMIT 1), 2, 0, 4, 9, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Center' ORDER BY id DESC LIMIT 1), 8, 0, 7, 21, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Healing' ORDER BY id DESC LIMIT 1), 2, 0, 10, 19, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Symbol of Transal' ORDER BY id DESC LIMIT 1), 8, 0, 11, 20, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Blessing of Piety' ORDER BY id DESC LIMIT 1), 8, 0, 15, 34, 3, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Daring' ORDER BY id DESC LIMIT 1), 8, 0, 17, 20, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Celestial Remedy' ORDER BY id DESC LIMIT 1), 2, 0, 19, 28, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Greater Healing' ORDER BY id DESC LIMIT 1), 2, 0, 20, 29, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Ward of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 20, 39, 4, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Symbol of Ryltan' ORDER BY id DESC LIMIT 1), 8, 0, 21, 30, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Bravery' ORDER BY id DESC LIMIT 1), 8, 0, 22, 31, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Celestial Health' ORDER BY id DESC LIMIT 1), 2, 0, 29, 43, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Word of Health' ORDER BY id DESC LIMIT 1), 2, 0, 30, 44, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Superior Healing' ORDER BY id DESC LIMIT 1), 2, 0, 30, 52, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Symbol of Pinzarn' ORDER BY id DESC LIMIT 1), 8, 0, 31, 40, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Valor' ORDER BY id DESC LIMIT 1), 8, 0, 32, 39, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Blessing of Faith' ORDER BY id DESC LIMIT 1), 8, 0, 35, 61, 3, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Complete Healing' ORDER BY id DESC LIMIT 1), 2, 0, 39, 75, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Temperance' ORDER BY id DESC LIMIT 1), 8, 0, 40, 59, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Guard of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 40, 53, 4, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Symbol of Naltron' ORDER BY id DESC LIMIT 1), 8, 0, 41, 53, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Celestial Healing' ORDER BY id DESC LIMIT 1), 2, 0, 44, 58, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Word of Healing' ORDER BY id DESC LIMIT 1), 2, 0, 45, 51, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Blessing of Temperance' ORDER BY id DESC LIMIT 1), 8, 0, 45, 59, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Remedy' ORDER BY id DESC LIMIT 1), 2, 0, 51, 58, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Word of Vigor' ORDER BY id DESC LIMIT 1), 2, 0, 52, 56, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Divine Light' ORDER BY id DESC LIMIT 1), 2, 0, 53, 57, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Symbol of Marzin' ORDER BY id DESC LIMIT 1), 8, 0, 54, 60, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Protection of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 54, 61, 4, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Word of Restoration' ORDER BY id DESC LIMIT 1), 2, 0, 57, 63, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Naltron\'s Mark' ORDER BY id DESC LIMIT 1), 8, 0, 58, 59, 6, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Ethereal Light' ORDER BY id DESC LIMIT 1), 2, 0, 58, 62, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Celestial Elixir' ORDER BY id DESC LIMIT 1), 2, 0, 59, 61, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Ethereal Remedy' ORDER BY id DESC LIMIT 1), 8, 0, 59, 60, 6, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Marzin\'s Mark' ORDER BY id DESC LIMIT 1), 8, 0, 60, 62, 6, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Aegolism' ORDER BY id DESC LIMIT 1), 8, 0, 60, 61, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Blessing of Aegolism' ORDER BY id DESC LIMIT 1), 8, 0, 60, 61, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Ancient: Gift of Aegolism' ORDER BY id DESC LIMIT 1), 8, 0, 60, 61, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Ethereal Elixir' ORDER BY id DESC LIMIT 1), 2, 0, 60, 64, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Word of Redemption' ORDER BY id DESC LIMIT 1), 2, 0, 60, 75, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Supernal Remedy' ORDER BY id DESC LIMIT 1), 2, 0, 61, 65, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Symbol of Kazad' ORDER BY id DESC LIMIT 1), 2, 0, 61, 65, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Supernal Elixir' ORDER BY id DESC LIMIT 1), 2, 0, 62, 64, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Virtue' ORDER BY id DESC LIMIT 1), 8, 0, 62, 66, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Blessing of Reverence' ORDER BY id DESC LIMIT 1), 8, 0, 62, 66, 3, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Bulwark of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 62, 66, 4, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Kazad`s Mark' ORDER BY id DESC LIMIT 1), 2, 0, 63, 69, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Supernal Light' ORDER BY id DESC LIMIT 1), 2, 0, 63, 64, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Word of Replenishment' ORDER BY id DESC LIMIT 1), 2, 0, 64, 68, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Aura of Reverence' ORDER BY id DESC LIMIT 1), 8, 0, 64, 66, 3, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Hand of Virtue' ORDER BY id DESC LIMIT 1), 8, 0, 65, 66, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Holy Elixir' ORDER BY id DESC LIMIT 1), 2, 0, 65, 66, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Holy Light' ORDER BY id DESC LIMIT 1), 2, 0, 65, 67, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Symbol of Balikor' ORDER BY id DESC LIMIT 1), 2, 0, 66, 71, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Pious Remedy' ORDER BY id DESC LIMIT 1), 2, 0, 66, 255, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Conviction' ORDER BY id DESC LIMIT 1), 8, 0, 67, 71, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Pious Elixir' ORDER BY id DESC LIMIT 1), 2, 0, 67, 255, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Blessing of Devotion' ORDER BY id DESC LIMIT 1), 8, 0, 67, 70, 3, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Panoply of Vie' ORDER BY id DESC LIMIT 1), 8, 0, 67, 72, 4, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Pious Light' ORDER BY id DESC LIMIT 1), 2, 0, 68, 69, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Aura of Devotion' ORDER BY id DESC LIMIT 1), 8, 0, 69, 255, 7, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Word of Vivification' ORDER BY id DESC LIMIT 1), 2, 0, 69, 79, 2, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Hand of Conviction' ORDER BY id DESC LIMIT 1), 8, 0, 70, 71, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Balikor\'s Mark' ORDER BY id DESC LIMIT 1), 8, 0, 70, 74, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Elixir of Divinity' ORDER BY id DESC LIMIT 1), 2, 0, 70, 74, 1, 0 ), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Ancient: Hallowed Light' ORDER BY id DESC LIMIT 1), 2, 0, 70, 72, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Symbol of Elushar Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 71, 75, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Sacred Remedy Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 71, 75, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Blessing of Purpose Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 71, 0, 3, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Tenacity Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 72, 76, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Sacred Elixir Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 72, 76, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Aura of Purpose Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 72, 75, 3, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Sacred Light Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 73, 77, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Promised Renewal Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 73, 77, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Aegis of Vie Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 73, 77, 4, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Hand of Tenacity Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 75, 76, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Elushar\'s Mark Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 75, 79, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Elixir of Redemption Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 75, 79, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Rallied Aegis of Vie Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 75, 77, 4, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Solemn Remedy Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 76, 80, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Symbol of Kaerra Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 76, 80, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Blessing of Resolve Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 76, 80, 3, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Temerity Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 77, 81, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Solemn Elixir Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 77, 81, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Eleventh-Hour Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 77, 81, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Aura of Resolve Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 77, 80, 3, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Solemn Light Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 78, 82, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Promised Restoration Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 78, 82, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Shield of Vie Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 78, 82, 4, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Hand of Temerity Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 80, 81, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Kaerra\'s Mark Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 80, 84, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Elixir of Atonement Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 80, 84, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Frantic Renewal Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 80, 84, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Rallied Shield of Vie Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 80, 82, 4, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Symbol of Darianna Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 81, 85, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Devout Remedy Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 81, 85, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Blessing of Loyalty Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 81, 85, 3, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Gallantry Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 82, 86, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Devout Elixir Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 82, 86, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Twelfth Night Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 82, 86, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Aura of Loyalty Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 82, 85, 3, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Devout Light Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 83, 87, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Promised Recuperation Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 83, 87, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Palladium of Vie Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 83, 87, 4, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Hand of Gallantry Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 85, 86, 1, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Darianna\'s Mark Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 85, 89, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Frenetic Renewal Rk. II' ORDER BY id DESC LIMIT 1), 2, 0, 85, 89, 2, 0), + ((SELECT merc_spell_list_id FROM merc_spell_lists WHERE class_id = 2 AND proficiency_id = 2), (SELECT id FROM spells_new WHERE name = 'Rallied Palladium of Vie Rk. II' ORDER BY id DESC LIMIT 1), 8, 0, 85, 87, 4, 0); diff --git a/world/Adventure.cpp b/world/Adventure.cpp new file mode 100644 index 000000000..98a480073 --- /dev/null +++ b/world/Adventure.cpp @@ -0,0 +1,438 @@ +#include "../common/debug.h" +#include "../common/servertalk.h" +#include "../common/extprofile.h" +#include "../common/rulesys.h" +#include "../common/MiscFunctions.h" +#include "Adventure.h" +#include "AdventureManager.h" +#include "worlddb.h" +#include "zonelist.h" +#include "clientlist.h" +#include "cliententry.h" + +extern ZSList zoneserver_list; +extern ClientList client_list; +extern AdventureManager adventure_manager; + +Adventure::Adventure(AdventureTemplate *t) +{ + adventure_template = t; + status = AS_WaitingForZoneIn; + current_timer = new Timer(1000 * t->zone_in_time); + count = 0; + assassination_count = 0; + instance_id = 0; +} + +Adventure::Adventure(AdventureTemplate *t, int count, int assassination_count, AdventureStatus status, uint16 instance_id, uint32 time_left) +{ + adventure_template = t; + this->count = count; + this->assassination_count = assassination_count; + this->status = status; + this->instance_id = instance_id; + + if(status == AS_Finished) + { + database.SetInstanceDuration(instance_id, time_left); + } + else + { + database.SetInstanceDuration(instance_id, time_left + 60); + } + + current_timer = new Timer(1000 * time_left); +} + +Adventure::~Adventure() +{ + safe_delete(current_timer); +} + +void Adventure::AddPlayer(string character_name, bool add_client_to_instance) +{ + if(!PlayerExists(character_name)) + { + int client_id = database.GetCharacterID(character_name.c_str()); + if(add_client_to_instance) + { + database.AddClientToInstance(instance_id, client_id); + } + players.push_back(character_name); + } +} + +void Adventure::RemovePlayer(string character_name) +{ + list::iterator iter = players.begin(); + while(iter != players.end()) + { + if((*iter).compare(character_name) == 0) + { + database.RemoveClientFromInstance(instance_id, database.GetCharacterID(character_name.c_str())); + players.erase(iter); + return; + } + iter++; + } +} + +bool Adventure::PlayerExists(string character_name) +{ + list::iterator iter = players.begin(); + while(iter != players.end()) + { + if(character_name.compare((*iter)) == 0) + { + return true; + } + iter++; + } + return false; +} + +bool Adventure::IsActive() +{ + return (status != AS_Finished); +} + +bool Adventure::Process() +{ + if(players.size() == 0) + { + return false; + } + + if(current_timer->Check()) + { + //Timer wore out while waiting for zone in. + if(status == AS_WaitingForZoneIn) + { + MoveCorpsesToGraveyard(); + database.DeleteInstance(instance_id); + return false; + } + else if(status == AS_WaitingForPrimaryEndTime) + { + //Do partial failure: send a message to the clients that they can only get a certain amount of points. + SendAdventureMessage(13, "You failed to complete your adventure in time. Complete your adventure goal within 30 minutes to " + "receive a lesser reward. This adventure will end in 30 minutes and your party will be ejected from the dungeon."); + SetStatus(AS_WaitingForSecondaryEndTime); + } + else + { + if(count < GetTemplate()->type_count) + { + Finished(AWS_Lose); + } + + MoveCorpsesToGraveyard(); + database.DeleteInstance(instance_id); + return false; + } + } + return true; +} + +bool Adventure::CreateInstance() +{ + uint32 zone_id = database.GetZoneID(adventure_template->zone); + if(!zone_id) + { + return false; + } + + uint16 id = 0; + if(!database.GetUnusedInstanceID(id)) + { + return false; + } + + if(!database.CreateInstance(id, zone_id, adventure_template->zone_version, adventure_template->zone_in_time + 60)) + { + return false; + } + + instance_id = id; + return true; +} + +void Adventure::SetStatus(AdventureStatus new_status) +{ + if(new_status == AS_WaitingForPrimaryEndTime) + { + status = new_status; + safe_delete(current_timer); + current_timer = new Timer(adventure_template->duration * 1000); + database.SetInstanceDuration(instance_id, adventure_template->duration + 60); + ServerPacket *pack = new ServerPacket(ServerOP_InstanceUpdateTime, sizeof(ServerInstanceUpdateTime_Struct)); + ServerInstanceUpdateTime_Struct *ut = (ServerInstanceUpdateTime_Struct*)pack->pBuffer; + ut->instance_id = instance_id; + ut->new_duration = adventure_template->duration + 60; + + pack->Deflate(); + zoneserver_list.SendPacket(0, instance_id, pack); + safe_delete(pack); + } + else if(new_status == AS_WaitingForSecondaryEndTime) + { + status = new_status; + safe_delete(current_timer); + current_timer = new Timer(1800000); + database.SetInstanceDuration(instance_id, 1860); + ServerPacket *pack = new ServerPacket(ServerOP_InstanceUpdateTime, sizeof(ServerInstanceUpdateTime_Struct)); + ServerInstanceUpdateTime_Struct *ut = (ServerInstanceUpdateTime_Struct*)pack->pBuffer; + ut->instance_id = instance_id; + ut->new_duration = 1860; + + pack->Deflate(); + zoneserver_list.SendPacket(0, instance_id, pack); + safe_delete(pack); + } + else if(new_status == AS_Finished) + { + status = new_status; + safe_delete(current_timer); + current_timer = new Timer(1800000); + database.SetInstanceDuration(instance_id, 1800); + ServerPacket *pack = new ServerPacket(ServerOP_InstanceUpdateTime, sizeof(ServerInstanceUpdateTime_Struct)); + ServerInstanceUpdateTime_Struct *ut = (ServerInstanceUpdateTime_Struct*)pack->pBuffer; + ut->instance_id = instance_id; + ut->new_duration = 1860; + + pack->Deflate(); + zoneserver_list.SendPacket(0, instance_id, pack); + safe_delete(pack); + } + else + { + return; + } + + list::iterator iter = players.begin(); + while(iter != players.end()) + { + adventure_manager.GetAdventureData((*iter).c_str()); + iter++; + } +} + +void Adventure::SendAdventureMessage(uint32 type, const char *msg) +{ + ServerPacket *pack = new ServerPacket(ServerOP_EmoteMessage, sizeof(ServerEmoteMessage_Struct) + strlen(msg) + 1); + ServerEmoteMessage_Struct *sms = (ServerEmoteMessage_Struct*)pack->pBuffer; + sms->type = type; + strcpy(sms->message, msg); + list::iterator iter = players.begin(); + while(iter != players.end()) + { + ClientListEntry *current = client_list.FindCharacter((*iter).c_str()); + if(current) + { + strcpy(sms->to, (*iter).c_str()); + zoneserver_list.SendPacket(current->zone(), current->instance(), pack); + } + iter++; + } + delete pack; +} + +void Adventure::IncrementCount() +{ + const AdventureTemplate *at = GetTemplate(); + if(count >= at->type_count) + { + return; + } + + if(status == AS_WaitingForPrimaryEndTime) + { + count++; + if(count == at->type_count) + { + SetStatus(AS_Finished); + Finished(AWS_Win); + } + } + else if(status == AS_WaitingForSecondaryEndTime) + { + count++; + if(count == at->type_count) + { + SetStatus(AS_Finished); + Finished(AWS_SecondPlace); + } + } +} + +void Adventure::IncrementAssassinationCount() +{ + if(count >= RuleI(Adventure, NumberKillsForBossSpawn)) + { + return; + } + + assassination_count++; +} + +void Adventure::Finished(AdventureWinStatus ws) +{ + list::iterator iter = players.begin(); + while(iter != players.end()) + { + ClientListEntry *current = client_list.FindCharacter((*iter).c_str()); + if(current) + { + if(current->Online() == CLE_Status_InZone) + { + //We can send our packets only. + ServerPacket *pack = new ServerPacket(ServerOP_AdventureFinish, sizeof(ServerAdventureFinish_Struct)); + ServerAdventureFinish_Struct *af = (ServerAdventureFinish_Struct*)pack->pBuffer; + strcpy(af->player, (*iter).c_str()); + af->theme = GetTemplate()->theme; + if(ws == AWS_Win) + { + af->win = true; + af->points = GetTemplate()->win_points; + } + else if(ws == AWS_SecondPlace) + { + af->win = true; + af->points = GetTemplate()->lose_points; + } + else + { + af->win = false; + af->points = 0; + } + pack->Deflate(); + zoneserver_list.SendPacket(current->zone(), current->instance(), pack); + database.UpdateAdventureStatsEntry(database.GetCharacterID((*iter).c_str()), GetTemplate()->theme, (ws != AWS_Lose) ? true : false); + delete pack; + } + else + { + AdventureFinishEvent afe; + afe.name = (*iter); + if(ws == AWS_Win) + { + afe.theme = GetTemplate()->theme; + afe.points = GetTemplate()->win_points; + afe.win = true; + } + else if(ws == AWS_SecondPlace) + { + afe.theme = GetTemplate()->theme; + afe.points = GetTemplate()->lose_points; + afe.win = true; + } + else + { + afe.win = false; + afe.points = 0; + } + adventure_manager.AddFinishedEvent(afe); + database.UpdateAdventureStatsEntry(database.GetCharacterID((*iter).c_str()), GetTemplate()->theme, (ws != AWS_Lose) ? true : false); + } + } + else + { + AdventureFinishEvent afe; + afe.name = (*iter); + if(ws == AWS_Win) + { + afe.theme = GetTemplate()->theme; + afe.points = GetTemplate()->win_points; + afe.win = true; + } + else if(ws == AWS_SecondPlace) + { + afe.theme = GetTemplate()->theme; + afe.points = GetTemplate()->lose_points; + afe.win = true; + } + else + { + afe.win = false; + afe.points = 0; + } + adventure_manager.AddFinishedEvent(afe); + database.UpdateAdventureStatsEntry(database.GetCharacterID((*iter).c_str()), GetTemplate()->theme, (ws != AWS_Lose) ? true : false); + } + iter++; + } + adventure_manager.GetAdventureData(this); +} + +void Adventure::MoveCorpsesToGraveyard() +{ + if(GetTemplate()->graveyard_zone_id == 0) + { + return; + } + + list dbid_list; + 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); + } + + list::iterator iter = dbid_list.begin(); + while(iter != dbid_list.end()) + { + 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++; + } + + iter = dbid_list.begin(); + list::iterator c_iter = charid_list.begin(); + while(iter != dbid_list.end()) + { + ServerPacket* pack = new ServerPacket(ServerOP_DepopAllPlayersCorpses, sizeof(ServerDepopAllPlayersCorpses_Struct)); + ServerDepopAllPlayersCorpses_Struct *dpc = (ServerDepopAllPlayersCorpses_Struct*)pack->pBuffer; + dpc->CharacterID = (*c_iter); + dpc->InstanceID = 0; + dpc->ZoneID = GetTemplate()->graveyard_zone_id; + zoneserver_list.SendPacket(0, GetInstanceID(), pack); + delete pack; + + pack = new ServerPacket(ServerOP_SpawnPlayerCorpse, sizeof(SpawnPlayerCorpse_Struct)); + SpawnPlayerCorpse_Struct* spc = (SpawnPlayerCorpse_Struct*)pack->pBuffer; + spc->player_corpse_id = (*iter); + spc->zone_id = GetTemplate()->graveyard_zone_id; + + zoneserver_list.SendPacket(spc->zone_id, 0, pack); + delete pack; + iter++; + c_iter++; + } +} \ No newline at end of file diff --git a/world/Adventure.h b/world/Adventure.h new file mode 100644 index 000000000..ebb8d2b83 --- /dev/null +++ b/world/Adventure.h @@ -0,0 +1,103 @@ +#ifndef ADVENTURE_H +#define ADVENTURE_H + +#include "../common/debug.h" +#include "../common/types.h" +#include "../common/timer.h" +#include "AdventureTemplate.h" +#include +#include +#include + +using namespace std; + +enum AdventureStatus +{ + AS_WaitingForZoneIn, + AS_WaitingForPrimaryEndTime, + AS_WaitingForSecondaryEndTime, + AS_Finished, +}; + +enum AdventureWinStatus +{ + AWS_Win, + AWS_SecondPlace, + AWS_Lose +}; + +struct AdventureZones +{ + string zone; + int version; +}; + +struct AdventureZoneIn +{ + int zone_id; + int door_id; +}; + +struct AdventureFinishEvent +{ + string name; + bool win; + int points; + int theme; +}; + +struct LeaderboardInfo +{ + string name; + uint32 wins; + uint32 guk_wins; + uint32 mir_wins; + uint32 mmc_wins; + uint32 ruj_wins; + uint32 tak_wins; + uint32 losses; + uint32 guk_losses; + uint32 mir_losses; + uint32 mmc_losses; + uint32 ruj_losses; + uint32 tak_losses; +}; + +class Adventure +{ +public: + Adventure(AdventureTemplate *t); + Adventure(AdventureTemplate *t, int count, int assassination_count, AdventureStatus status, uint16 instance_id, uint32 time_left); + ~Adventure(); + bool Process(); + bool IsActive(); + void AddPlayer(string character_name, bool add_client_to_instance = true); + void RemovePlayer(string character_name); + bool PlayerExists(string character_name); + bool CreateInstance(); + void IncrementCount(); + void IncrementAssassinationCount(); + void Finished(AdventureWinStatus ws); + void SetStatus(AdventureStatus new_status); + void SendAdventureMessage(uint32 type, const char *msg); + void MoveCorpsesToGraveyard(); + + uint16 GetInstanceID() const { return instance_id; } + const AdventureTemplate *GetTemplate() const { return adventure_template; } + AdventureStatus GetStatus() const { return status; } + list GetPlayers() { return players; } + int GetCount() const { return count; } + int GetAssassinationCount() const { return assassination_count; } + uint32 GetRemainingTime() const { if(current_timer) { return (current_timer->GetRemainingTime() / 1000); } else { return 0; } } +protected: + int id; + int count; + int assassination_count; + AdventureTemplate *adventure_template; + AdventureStatus status; + list players; + Timer *current_timer; + int instance_id; +}; + +#endif diff --git a/world/AdventureManager.cpp b/world/AdventureManager.cpp new file mode 100644 index 000000000..b415b83f8 --- /dev/null +++ b/world/AdventureManager.cpp @@ -0,0 +1,2278 @@ +#include "../common/debug.h" +#include "../common/MiscFunctions.h" +#include "../common/servertalk.h" +#include "../common/rulesys.h" +#include "Adventure.h" +#include "AdventureManager.h" +#include "worlddb.h" +#include "zonelist.h" +#include "clientlist.h" +#include "cliententry.h" +#include +#include + +extern ZSList zoneserver_list; +extern ClientList client_list; + +AdventureManager::AdventureManager() +{ + process_timer = new Timer(500); + save_timer = new Timer(90000); + leaderboard_info_timer = new Timer(180000); +} + +AdventureManager::~AdventureManager() +{ + safe_delete(process_timer); + safe_delete(save_timer); + safe_delete(leaderboard_info_timer); +} + +void AdventureManager::Process() +{ + if(process_timer->Check()) + { + list::iterator iter = adventure_list.begin(); + while(iter != adventure_list.end()) + { + if(!(*iter)->Process()) + { + Adventure *adv = (*iter); + iter = adventure_list.erase(iter); + GetAdventureData(adv); + delete adv; + continue; + } + iter++; + } + } + + if(leaderboard_info_timer->Check()) + { + LoadLeaderboardInfo(); + } + + if(save_timer->Check()) + { + Save(); + } +} + +void AdventureManager::CalculateAdventureRequestReply(const char *data) +{ + ServerAdventureRequest_Struct *sar = (ServerAdventureRequest_Struct*)data; + ClientListEntry *leader = client_list.FindCharacter(sar->leader); + if(!leader) + { + return; + } + + /** + * This block checks to see if we actually have any adventures for the requested theme. + */ + map >::iterator adv_list_iter = adventure_entries.find(sar->template_id); + if(adv_list_iter == adventure_entries.end()) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureRequestDeny, sizeof(ServerAdventureRequestDeny_Struct)); + ServerAdventureRequestDeny_Struct *deny = (ServerAdventureRequestDeny_Struct*)pack->pBuffer; + strcpy(deny->leader, sar->leader); + strcpy(deny->reason, "There are currently no adventures set for this theme."); + pack->Deflate(); + zoneserver_list.SendPacket(leader->zone(), leader->instance(), pack); + delete pack; + return; + } + + /** + * This block checks to see if our requested group has anyone with an "Active" adventure + * Active being in progress, finished adventures that are still waiting to expire do not count + * Though they will count against you for which new adventure you can get. + */ + list::iterator iter = adventure_list.begin(); + while(iter != adventure_list.end()) + { + Adventure* current = (*iter); + if(current->IsActive()) + { + for(int i = 0; i < sar->member_count; ++i) + { + if(current->PlayerExists((data + sizeof(ServerAdventureRequest_Struct) + (64 * i)))) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureRequestDeny, sizeof(ServerAdventureRequestDeny_Struct)); + ServerAdventureRequestDeny_Struct *deny = (ServerAdventureRequestDeny_Struct*)pack->pBuffer; + strcpy(deny->leader, sar->leader); + + stringstream ss(stringstream::out | stringstream::in); + ss << (data + sizeof(ServerAdventureRequest_Struct) + (64 * i)) << " is already apart of an active adventure."; + + strcpy(deny->reason, ss.str().c_str()); + pack->Deflate(); + zoneserver_list.SendPacket(leader->zone(), leader->instance(), pack); + delete pack; + return; + } + } + } + iter++; + } + + /** + * Now we need to get every available adventure for our selected theme and exclude ones we can't use. + * ie. the ones that would cause overlap issues for new adventures with the old unexpired adventures. + */ + list excluded_zones; + list excluded_zone_ins; + for(int i = 0; i < sar->member_count; ++i) + { + int finished_adventures_count; + Adventure **finished_adventures = GetFinishedAdventures((data + sizeof(ServerAdventureRequest_Struct) + (64 * i)), finished_adventures_count); + for(int i = 0; i < finished_adventures_count; ++i) + { + if(!IsInExcludedZoneList(excluded_zones, finished_adventures[i]->GetTemplate()->zone, finished_adventures[i]->GetTemplate()->zone_version)) + { + AdventureZones adv; + adv.zone = finished_adventures[i]->GetTemplate()->zone; + adv.version = finished_adventures[i]->GetTemplate()->zone_version; + excluded_zones.push_back(adv); + } + if(!IsInExcludedZoneInList(excluded_zone_ins, finished_adventures[i]->GetTemplate()->zone_in_zone_id, + finished_adventures[i]->GetTemplate()->zone_in_object_id)) + { + AdventureZoneIn adv; + adv.door_id = finished_adventures[i]->GetTemplate()->zone_in_object_id; + adv.zone_id = finished_adventures[i]->GetTemplate()->zone_in_zone_id; + excluded_zone_ins.push_back(adv); + } + } + safe_delete_array(finished_adventures); + } + + list eligible_adventures = adventure_entries[sar->template_id]; + /** + * Remove zones from eligible zones based on their difficulty and type. + * ie only use difficult zones for difficult, collect for collect, etc. + */ + list::iterator ea_iter = eligible_adventures.begin(); + while(ea_iter != eligible_adventures.end()) + { + if((*ea_iter)->is_hard != ((sar->risk == 2) ? true : false)) + { + ea_iter = eligible_adventures.erase(ea_iter); + continue; + } + + if(sar->type != 0 && sar->type != (*ea_iter)->type) + { + ea_iter = eligible_adventures.erase(ea_iter); + continue; + } + ea_iter++; + } + + /** + * Get levels for this group. + */ + int valid_count = 0; + int avg_level = 0; + int min_level = 40000; + int max_level = 0; + + for(int i = 0; i < sar->member_count; ++i) + { + ClientListEntry *current = client_list.FindCharacter((data + sizeof(ServerAdventureRequest_Struct) + (64 * i))); + if(current) + { + int lvl = current->level(); + if(lvl != 0) + { + avg_level += lvl; + valid_count++; + if(lvl < min_level) + { + min_level = lvl; + } + if(lvl > max_level) + { + max_level = lvl; + } + } + else + { + if(database.GetCharacterLevel((data + sizeof(ServerAdventureRequest_Struct) + (64 * i)), lvl)) + { + avg_level += lvl; + valid_count++; + if(lvl < min_level) + { + min_level = lvl; + } + if(lvl > max_level) + { + max_level = lvl; + } + } + } + } + else + { + int lvl = 0; + if(database.GetCharacterLevel((data + sizeof(ServerAdventureRequest_Struct) + (64 * i)), lvl)) + { + avg_level += lvl; + valid_count++; + if(lvl < min_level) + { + min_level = lvl; + } + if(lvl > max_level) + { + max_level = lvl; + } + } + } + } + + if(valid_count == 0) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureRequestDeny, sizeof(ServerAdventureRequestDeny_Struct)); + ServerAdventureRequestDeny_Struct *deny = (ServerAdventureRequestDeny_Struct*)pack->pBuffer; + strcpy(deny->leader, sar->leader); + strcpy(deny->reason, "The number of found players for this adventure was zero."); + pack->Deflate(); + zoneserver_list.SendPacket(leader->zone(), leader->instance(), pack); + delete pack; + return; + } + + avg_level = avg_level / valid_count; + + if(max_level - min_level > RuleI(Adventure, MaxLevelRange)) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureRequestDeny, sizeof(ServerAdventureRequestDeny_Struct)); + ServerAdventureRequestDeny_Struct *deny = (ServerAdventureRequestDeny_Struct*)pack->pBuffer; + strcpy(deny->leader, sar->leader); + + stringstream ss(stringstream::out | stringstream::in); + ss << "The maximum level range for this adventure is " << RuleI(Adventure, MaxLevelRange); + ss << " but the level range calculated was " << (max_level - min_level) << "."; + strcpy(deny->reason, ss.str().c_str()); + pack->Deflate(); + zoneserver_list.SendPacket(leader->zone(), leader->instance(), pack); + delete pack; + return; + } + + /** + * Remove the zones from our eligible zones based on the exclusion above + */ + list::iterator ez_iter = excluded_zones.begin(); + while(ez_iter != excluded_zones.end()) + { + list::iterator ea_iter = eligible_adventures.begin(); + while(ea_iter != eligible_adventures.end()) + { + if((*ez_iter).zone.compare((*ea_iter)->zone) == 0 && (*ez_iter).version == (*ea_iter)->zone_version) + { + ea_iter = eligible_adventures.erase(ea_iter); + continue; + } + ea_iter++; + } + ez_iter++; + } + + list::iterator ezi_iter = excluded_zone_ins.begin(); + while(ezi_iter != excluded_zone_ins.end()) + { + list::iterator ea_iter = eligible_adventures.begin(); + while(ea_iter != eligible_adventures.end()) + { + if((*ezi_iter).zone_id == (*ea_iter)->zone_in_zone_id && (*ezi_iter).door_id == (*ea_iter)->zone_in_object_id) + { + ea_iter = eligible_adventures.erase(ea_iter); + continue; + } + ea_iter++; + } + ezi_iter++; + } + + /** + * Remove Zones based on level + */ + ea_iter = eligible_adventures.begin(); + while(ea_iter != eligible_adventures.end()) + { + if((*ea_iter)->min_level > avg_level) + { + ea_iter = eligible_adventures.erase(ea_iter); + continue; + } + + if((*ea_iter)->max_level < avg_level) + { + ea_iter = eligible_adventures.erase(ea_iter); + continue; + } + ea_iter++; + } + + if(eligible_adventures.size() > 0) + { + ea_iter = eligible_adventures.begin(); + int c_index = MakeRandomInt(0, (eligible_adventures.size()-1)); + for(int i = 0; i < c_index; ++i) + { + ea_iter++; + } + ServerPacket *pack = new ServerPacket(ServerOP_AdventureRequestAccept, sizeof(ServerAdventureRequestAccept_Struct) + (sar->member_count * 64)); + ServerAdventureRequestAccept_Struct *sra = (ServerAdventureRequestAccept_Struct*)pack->pBuffer; + strcpy(sra->leader, sar->leader); + strcpy(sra->text, (*ea_iter)->text); + sra->theme = sar->template_id; + sra->id = (*ea_iter)->id; + sra->member_count = sar->member_count; + memcpy((pack->pBuffer + sizeof(ServerAdventureRequestAccept_Struct)), (data + sizeof(ServerAdventureRequest_Struct)), (sar->member_count * 64)); + pack->Deflate(); + zoneserver_list.SendPacket(leader->zone(), leader->instance(), pack); + delete pack; + return; + } + else + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureRequestDeny, sizeof(ServerAdventureRequestDeny_Struct)); + ServerAdventureRequestDeny_Struct *deny = (ServerAdventureRequestDeny_Struct*)pack->pBuffer; + strcpy(deny->leader, sar->leader); + strcpy(deny->reason, "The number of adventures returned was zero."); + pack->Deflate(); + zoneserver_list.SendPacket(leader->zone(), leader->instance(), pack); + delete pack; + return; + } +} + +void AdventureManager::TryAdventureCreate(const char *data) +{ + ServerAdventureRequestCreate_Struct *src = (ServerAdventureRequestCreate_Struct*)data; + ClientListEntry *leader = client_list.FindCharacter(src->leader); + if(!leader) + { + return; + } + + AdventureTemplate *adv_template = GetAdventureTemplate(src->theme, src->id); + if(!adv_template) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureCreateDeny, 64); + strcpy((char*)pack->pBuffer, src->leader); + pack->Deflate(); + zoneserver_list.SendPacket(leader->zone(), leader->instance(), pack); + delete pack; + return; + } + + Adventure *adv = new Adventure(adv_template); + if(!adv->CreateInstance()) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureCreateDeny, 64); + strcpy((char*)pack->pBuffer, src->leader); + pack->Deflate(); + zoneserver_list.SendPacket(leader->zone(), leader->instance(), pack); + delete pack; + delete adv; + return; + } + + for(int i = 0; i < src->member_count; ++i) + { + Adventure *a = GetActiveAdventure((data + sizeof(ServerAdventureRequestCreate_Struct) + (64 * i))); + if(a) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureCreateDeny, 64); + strcpy((char*)pack->pBuffer, src->leader); + pack->Deflate(); + zoneserver_list.SendPacket(leader->zone(), leader->instance(), pack); + delete pack; + delete adv; + return; + } + + adv->AddPlayer((data + sizeof(ServerAdventureRequestCreate_Struct) + (64 * i))); + } + + //Need to send adventure data to zone server for each client. + for(int i = 0; i < src->member_count; ++i) + { + + ClientListEntry *player = client_list.FindCharacter((data + sizeof(ServerAdventureRequestCreate_Struct) + (64 * i))); + if(player) + { + int f_count = 0; + Adventure** finished_adventures = GetFinishedAdventures((data + sizeof(ServerAdventureRequestCreate_Struct) + (64 * i)), f_count); + ServerPacket *pack = new ServerPacket(ServerOP_AdventureData, sizeof(ServerSendAdventureData_Struct) + + (sizeof(ServerFinishedAdventures_Struct) * f_count)); + ServerSendAdventureData_Struct *sca = (ServerSendAdventureData_Struct*)pack->pBuffer; + + strcpy(sca->player, (data + sizeof(ServerAdventureRequestCreate_Struct) + (64 * i))); + strcpy(sca->text, adv_template->text); + sca->time_left = adv_template->zone_in_time; + sca->time_to_enter = adv_template->zone_in_time; + sca->risk = adv_template->is_hard ? 1 : 0; + sca->x = adv_template->zone_in_x; + sca->y = adv_template->zone_in_y; + sca->zone_in_id = adv_template->zone_in_zone_id; + sca->zone_in_object = adv_template->zone_in_object_id; + sca->instance_id = adv->GetInstanceID(); + sca->count = 0; + sca->total = adv_template->type_count; + sca->finished_adventures = f_count; + for(int f = 0; f < f_count; ++f) + { + ServerFinishedAdventures_Struct *sfa = (ServerFinishedAdventures_Struct*)(pack->pBuffer + + sizeof(ServerSendAdventureData_Struct) + + (sizeof(ServerFinishedAdventures_Struct) * f)); + sfa->zone_in_id = finished_adventures[f]->GetTemplate()->zone_in_zone_id; + sfa->zone_in_object = finished_adventures[f]->GetTemplate()->zone_in_object_id; + } + + pack->Deflate(); + zoneserver_list.SendPacket(player->zone(), player->instance(), pack); + safe_delete_array(finished_adventures); + delete pack; + } + } + + adventure_list.push_back(adv); +} + +void AdventureManager::GetAdventureData(Adventure *adv) +{ + list player_list = adv->GetPlayers(); + list::iterator iter = player_list.begin(); + while(iter != player_list.end()) + { + GetAdventureData((*iter).c_str()); + iter++; + } +} + +void AdventureManager::GetAdventureData(const char *name) +{ + ClientListEntry *player = client_list.FindCharacter(name); + if(player) + { + int f_count = 0; + Adventure** finished_adventures = GetFinishedAdventures(name, f_count); + Adventure *current = GetActiveAdventure(name); + ServerPacket *pack = new ServerPacket(ServerOP_AdventureData, sizeof(ServerSendAdventureData_Struct) + + (sizeof(ServerFinishedAdventures_Struct) * f_count)); + ServerSendAdventureData_Struct *sca = (ServerSendAdventureData_Struct*)pack->pBuffer; + + if(current) + { + const AdventureTemplate *adv_template = current->GetTemplate(); + strcpy(sca->player, name); + strcpy(sca->text, adv_template->text); + sca->risk = adv_template->is_hard ? 1 : 0; + sca->x = adv_template->zone_in_x; + sca->y = adv_template->zone_in_y; + sca->zone_in_id = adv_template->zone_in_zone_id; + sca->zone_in_object = adv_template->zone_in_object_id; + sca->count = current->GetCount(); + sca->total = adv_template->type_count; + + sca->time_left = current->GetRemainingTime(); + if(current->GetStatus() == AS_WaitingForZoneIn) + { + sca->time_to_enter = sca->time_left; + } + } + else + { + //We have no mission and no finished missions + //Delete our stuff and return instead of sending a blank packet. + if(f_count == 0) + { + delete pack; + ServerPacket *pack = new ServerPacket(ServerOP_AdventureDataClear, 64); + strcpy((char*)pack->pBuffer, name); + pack->Deflate(); + zoneserver_list.SendPacket(player->zone(), player->instance(), pack); + + delete pack; + delete[] finished_adventures; + return; + } + } + + sca->finished_adventures = f_count; + for(int i = 0; i < f_count; ++i) + { + ServerFinishedAdventures_Struct *sfa = (ServerFinishedAdventures_Struct*)(pack->pBuffer + + sizeof(ServerSendAdventureData_Struct) + + (sizeof(ServerFinishedAdventures_Struct) * i)); + sfa->zone_in_id = finished_adventures[i]->GetTemplate()->zone_in_zone_id; + sfa->zone_in_object = finished_adventures[i]->GetTemplate()->zone_in_object_id; + } + + pack->Deflate(); + zoneserver_list.SendPacket(player->zone(), player->instance(), pack); + safe_delete_array(finished_adventures); + delete pack; + delete[] finished_adventures; + } +} + +bool AdventureManager::IsInExcludedZoneList(list excluded_zones, string zone_name, int version) +{ + list::iterator iter = excluded_zones.begin(); + while(iter != excluded_zones.end()) + { + if(((*iter).zone.compare(zone_name) == 0) && ((*iter).version == version)) + { + return true; + } + iter++; + } + return false; +} + +bool AdventureManager::IsInExcludedZoneInList(list excluded_zone_ins, int zone_id, int door_object) +{ + list::iterator iter = excluded_zone_ins.begin(); + while(iter != excluded_zone_ins.end()) + { + if(((*iter).zone_id == zone_id) && ((*iter).door_id == door_object)) + { + return true; + } + iter++; + } + return false; +} + +Adventure **AdventureManager::GetFinishedAdventures(const char *player, int &count) +{ + Adventure **ret = NULL; + count = 0; + + list::iterator iter = adventure_list.begin(); + while(iter != adventure_list.end()) + { + if((*iter)->PlayerExists(player)) + { + if(!(*iter)->IsActive()) + { + if(ret) + { + Adventure **t = new Adventure*[count + 1]; + for(int i = 0; i < count; i++) + { + t[i] = ret[i]; + } + t[count] = (*iter); + delete[] ret; + ret = t; + } + else + { + ret = new Adventure*[1]; + ret[0] = (*iter); + } + count++; + } + } + iter++; + } + return ret; +} + +Adventure *AdventureManager::GetActiveAdventure(const char *player) +{ + list::iterator iter = adventure_list.begin(); + while(iter != adventure_list.end()) + { + if((*iter)->PlayerExists(player) && (*iter)->IsActive()) + { + return (*iter); + } + iter++; + } + return NULL; +} + +AdventureTemplate *AdventureManager::GetAdventureTemplate(int theme, int id) +{ + map >::iterator iter = adventure_entries.find(theme); + if(iter == adventure_entries.end()) + { + return NULL; + } + + list::iterator l_iter = (*iter).second.begin(); + while(l_iter != (*iter).second.end()) + { + if((*l_iter)->id == id) + { + return (*l_iter); + } + l_iter++; + } + return NULL; +} + +AdventureTemplate *AdventureManager::GetAdventureTemplate(int id) +{ + map::iterator iter = adventure_templates.find(id); + if(iter == adventure_templates.end()) + { + return NULL; + } + + return iter->second; +} + +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, " + "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); + return false; + } + return false; +} + +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)) + { + while((row = mysql_fetch_row(result))) + { + int id = atoi(row[0]); + int template_id = atoi(row[1]); + AdventureTemplate* tid = NULL; + + map::iterator t_iter = adventure_templates.find(template_id); + if(t_iter == adventure_templates.end()) + { + continue; + } + else + { + tid = adventure_templates[template_id]; + } + + list temp; + 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); + return false; + } + return false; +} + +void AdventureManager::PlayerClickedDoor(const char *player, int zone_id, int door_id) +{ + list::iterator iter = adventure_list.begin(); + while(iter != adventure_list.end()) + { + const AdventureTemplate *t = (*iter)->GetTemplate(); + if(t->zone_in_zone_id == zone_id && t->zone_in_object_id == door_id) + { + if((*iter)->PlayerExists(player)) + { + ClientListEntry *pc = client_list.FindCharacter(player); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureClickDoorReply, sizeof(ServerPlayerClickedAdventureDoorReply_Struct)); + ServerPlayerClickedAdventureDoorReply_Struct *sr = (ServerPlayerClickedAdventureDoorReply_Struct*)pack->pBuffer; + strcpy(sr->player, player); + sr->zone_id = database.GetZoneID(t->zone); + sr->instance_id = (*iter)->GetInstanceID(); + sr->x = t->dest_x; + sr->y = t->dest_y; + sr->z = t->dest_z; + sr->h = t->dest_h; + if((*iter)->GetStatus() == AS_WaitingForZoneIn) + { + (*iter)->SetStatus(AS_WaitingForPrimaryEndTime); + } + + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + safe_delete(pack); + } + return; + } + } + iter++; + } + + ClientListEntry *pc = client_list.FindCharacter(player); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureClickDoorError, 64); + strcpy((char*)pack->pBuffer, player); + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + safe_delete(pack); + } +} + +void AdventureManager::LeaveAdventure(const char *name) +{ + ClientListEntry *pc = client_list.FindCharacter(name); + if(pc) + { + Adventure *current = GetActiveAdventure(name); + if(current) + { + if(pc->instance() != 0 && pc->instance() == current->GetInstanceID()) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaveDeny, 64); + strcpy((char*)pack->pBuffer, name); + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + safe_delete(pack); + } + else + { + if(current->GetStatus() != AS_WaitingForZoneIn) + { + database.UpdateAdventureStatsEntry(database.GetCharacterID(name), current->GetTemplate()->theme, false); + } + + current->RemovePlayer(name); + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaveReply, 64); + strcpy((char*)pack->pBuffer, name); + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + safe_delete(pack); + } + } + else + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaveReply, 64); + strcpy((char*)pack->pBuffer, name); + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + safe_delete(pack); + } + } +} + +void AdventureManager::IncrementCount(uint16 instance_id) +{ + list::iterator iter = adventure_list.begin(); + Adventure *current = NULL; + while(iter != adventure_list.end()) + { + if((*iter)->GetInstanceID() == instance_id) + { + current = (*iter); + break; + } + iter++; + } + + if(current) + { + current->IncrementCount(); + list slist = current->GetPlayers(); + list::iterator siter = slist.begin(); + ServerPacket *pack = new ServerPacket(ServerOP_AdventureCountUpdate, sizeof(ServerAdventureCountUpdate_Struct)); + ServerAdventureCountUpdate_Struct *ac = (ServerAdventureCountUpdate_Struct*)pack->pBuffer; + ac->count = current->GetCount(); + ac->total = current->GetTemplate()->type_count; + + while(siter != slist.end()) + { + ClientListEntry *pc = client_list.FindCharacter((*siter).c_str()); + if(pc) + { + memset(ac->player, 0, 64); + strcpy(ac->player, (*siter).c_str()); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + } + siter++; + } + + delete pack; + } +} + +void AdventureManager::IncrementAssassinationCount(uint16 instance_id) +{ + list::iterator iter = adventure_list.begin(); + Adventure *current = NULL; + while(iter != adventure_list.end()) + { + if((*iter)->GetInstanceID() == instance_id) + { + current = (*iter); + break; + } + iter++; + } + + if(current) + { + current->IncrementAssassinationCount(); + } +} + + +void AdventureManager::GetZoneData(uint16 instance_id) +{ + list::iterator iter = adventure_list.begin(); + Adventure *current = NULL; + while(iter != adventure_list.end()) + { + if((*iter)->GetInstanceID() == instance_id) + { + current = (*iter); + break; + } + iter++; + } + + if(current) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureZoneData, sizeof(ServerZoneAdventureDataReply_Struct)); + ServerZoneAdventureDataReply_Struct *zd = (ServerZoneAdventureDataReply_Struct*)pack->pBuffer; + + const AdventureTemplate* temp = current->GetTemplate(); + zd->instance_id = instance_id; + zd->count = current->GetCount(); + zd->total = temp->type_count; + zd->type = temp->type; + zd->data_id = temp->type_data; + zd->assa_count = current->GetAssassinationCount(); + zd->assa_x = temp->assa_x; + zd->assa_y = temp->assa_y; + zd->assa_z = temp->assa_z; + zd->assa_h = temp->assa_h; + zd->dest_x = temp->dest_x; + zd->dest_y = temp->dest_y; + zd->dest_z = temp->dest_z; + zd->dest_h = temp->dest_h; + zoneserver_list.SendPacket(0, instance_id, pack); + delete pack; + } +} + +// Sort the leaderboard by wins. +bool pred_sort_by_win_count(const LeaderboardInfo &lbi1, const LeaderboardInfo &lbi2) +{ + return lbi1.wins > lbi2.wins; +} + +// Sort the leaderboard by win percentage. +bool pred_sort_by_win_percentage(const LeaderboardInfo &lbi1, const LeaderboardInfo &lbi2) +{ + int per1 = 10000; + int per2 = 10000; + if(lbi1.wins + lbi1.losses != 0) + { + per1 = (lbi1.wins * 10000 / (lbi1.wins + lbi1.losses)); + } + + if(lbi2.wins + lbi2.losses != 0) + { + per1 = (lbi2.wins * 10000 / (lbi2.wins + lbi2.losses)); + } + return per1 > per2; +} + +// Sort the leaderboard by wins(guk). +bool pred_sort_by_win_count_guk(const LeaderboardInfo &lbi1, const LeaderboardInfo &lbi2) +{ + return lbi1.guk_wins > lbi2.guk_wins; +} + +// Sort the leaderboard by win percentage(guk). +bool pred_sort_by_win_percentage_guk(const LeaderboardInfo &lbi1, const LeaderboardInfo &lbi2) +{ + int per1 = 10000; + int per2 = 10000; + if(lbi1.guk_wins + lbi1.guk_losses != 0) + { + per1 = (lbi1.guk_wins * 10000 / (lbi1.guk_wins + lbi1.guk_losses)); + } + + if(lbi2.guk_wins + lbi2.guk_losses != 0) + { + per1 = (lbi2.guk_wins * 10000 / (lbi2.guk_wins + lbi2.guk_losses)); + } + return per1 > per2; +} + +// Sort the leaderboard by wins(mir). +bool pred_sort_by_win_count_mir(const LeaderboardInfo &lbi1, const LeaderboardInfo &lbi2) +{ + return lbi1.mir_wins > lbi2.mir_wins; +} + +// Sort the leaderboard by win percentage(mir). +bool pred_sort_by_win_percentage_mir(const LeaderboardInfo &lbi1, const LeaderboardInfo &lbi2) +{ + int per1 = 10000; + int per2 = 10000; + if(lbi1.mir_wins + lbi1.mir_losses != 0) + { + per1 = (lbi1.mir_wins * 10000 / (lbi1.mir_wins + lbi1.mir_losses)); + } + + if(lbi2.mir_wins + lbi2.mir_losses != 0) + { + per1 = (lbi2.mir_wins * 10000 / (lbi2.mir_wins + lbi2.mir_losses)); + } + return per1 > per2; +} + +// Sort the leaderboard by wins(mmc). +bool pred_sort_by_win_count_mmc(const LeaderboardInfo &lbi1, const LeaderboardInfo &lbi2) +{ + return lbi1.mmc_wins > lbi2.mmc_wins; +} + +// Sort the leaderboard by win percentage(mmc). +bool pred_sort_by_win_percentage_mmc(const LeaderboardInfo &lbi1, const LeaderboardInfo &lbi2) +{ + int per1 = 10000; + int per2 = 10000; + if(lbi1.mmc_wins + lbi1.mmc_losses != 0) + { + per1 = (lbi1.mmc_wins * 10000 / (lbi1.mmc_wins + lbi1.mmc_losses)); + } + + if(lbi2.mmc_wins + lbi2.mmc_losses != 0) + { + per1 = (lbi2.mmc_wins * 10000 / (lbi2.mmc_wins + lbi2.mmc_losses)); + } + return per1 > per2; +} + +// Sort the leaderboard by wins(ruj). +bool pred_sort_by_win_count_ruj(const LeaderboardInfo &lbi1, const LeaderboardInfo &lbi2) +{ + return lbi1.ruj_wins > lbi2.ruj_wins; +} + +// Sort the leaderboard by win percentage(ruj). +bool pred_sort_by_win_percentage_ruj(const LeaderboardInfo &lbi1, const LeaderboardInfo &lbi2) +{ + int per1 = 10000; + int per2 = 10000; + if(lbi1.ruj_wins + lbi1.ruj_losses != 0) + { + per1 = (lbi1.ruj_wins * 10000 / (lbi1.ruj_wins + lbi1.ruj_losses)); + } + + if(lbi2.ruj_wins + lbi2.ruj_losses != 0) + { + per1 = (lbi2.ruj_wins * 10000 / (lbi2.ruj_wins + lbi2.ruj_losses)); + } + return per1 > per2; +} + +// Sort the leaderboard by wins(tak). +bool pred_sort_by_win_count_tak(const LeaderboardInfo &lbi1, const LeaderboardInfo &lbi2) +{ + return lbi1.tak_wins > lbi2.tak_wins; +} + +// Sort the leaderboard by win percentage(tak). +bool pred_sort_by_win_percentage_tak(const LeaderboardInfo &lbi1, const LeaderboardInfo &lbi2) +{ + int per1 = 10000; + int per2 = 10000; + if(lbi1.tak_wins + lbi1.tak_losses != 0) + { + per1 = (lbi1.tak_wins * 10000 / (lbi1.tak_wins + lbi1.tak_losses)); + } + + if(lbi2.tak_wins + lbi2.tak_losses != 0) + { + per1 = (lbi2.tak_wins * 10000 / (lbi2.tak_wins + lbi2.tak_losses)); + } + return per1 > per2; +} + +void AdventureManager::LoadLeaderboardInfo() +{ + leaderboard_info_wins.clear(); + leaderboard_info_percentage.clear(); + leaderboard_info_wins_guk.clear(); + leaderboard_info_percentage_guk.clear(); + leaderboard_info_wins_mir.clear(); + leaderboard_info_percentage_mir.clear(); + leaderboard_info_wins_mmc.clear(); + leaderboard_info_percentage_mmc.clear(); + leaderboard_info_wins_ruj.clear(); + 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); + return; + } + else + { + LogFile->write(EQEMuLog::Error, "Error in AdventureManager:::GetLeaderboardInfo: %s (%s)", query, errbuf); + safe_delete_array(query); + return; + } + return; +}; + +void AdventureManager::DoLeaderboardRequest(const char* player, uint8 type) +{ + switch(type) + { + case 1: + if(!leaderboard_sorted_wins) + { + leaderboard_info_wins.sort(pred_sort_by_win_count); + leaderboard_sorted_wins = true; + } + DoLeaderboardRequestWins(player); + break; + case 2: + if(!leaderboard_sorted_percentage) + { + leaderboard_info_percentage.sort(pred_sort_by_win_percentage); + leaderboard_sorted_percentage = true; + } + DoLeaderboardRequestPercentage(player); + break; + case 3: + if(!leaderboard_sorted_wins_guk) + { + leaderboard_info_wins_guk.sort(pred_sort_by_win_count_guk); + leaderboard_sorted_wins_guk = true; + } + DoLeaderboardRequestWinsGuk(player); + break; + case 4: + if(!leaderboard_sorted_percentage_guk) + { + leaderboard_info_percentage_guk.sort(pred_sort_by_win_percentage_guk); + leaderboard_sorted_percentage_guk = true; + } + DoLeaderboardRequestPercentageGuk(player); + break; + case 5: + if(!leaderboard_sorted_wins_mir) + { + leaderboard_info_wins_mir.sort(pred_sort_by_win_count_mir); + leaderboard_sorted_wins_mir = true; + } + DoLeaderboardRequestWinsMir(player); + break; + case 6: + if(!leaderboard_sorted_percentage_mir) + { + leaderboard_info_percentage_mir.sort(pred_sort_by_win_percentage_mir); + leaderboard_sorted_percentage_mir = true; + } + DoLeaderboardRequestPercentageMir(player); + break; + case 7: + if(!leaderboard_sorted_wins_mmc) + { + leaderboard_info_wins_mmc.sort(pred_sort_by_win_count_mmc); + leaderboard_sorted_wins_mmc = true; + } + DoLeaderboardRequestWinsMmc(player); + break; + case 8: + if(!leaderboard_sorted_percentage_mmc) + { + leaderboard_info_percentage_mmc.sort(pred_sort_by_win_percentage_mmc); + leaderboard_sorted_percentage_mmc = true; + } + DoLeaderboardRequestPercentageMmc(player); + break; + case 9: + if(!leaderboard_sorted_wins_ruj) + { + leaderboard_info_wins_ruj.sort(pred_sort_by_win_count_ruj); + leaderboard_sorted_wins_ruj = true; + } + DoLeaderboardRequestWinsRuj(player); + break; + case 10: + if(!leaderboard_sorted_percentage_ruj) + { + leaderboard_info_percentage_ruj.sort(pred_sort_by_win_percentage_ruj); + leaderboard_sorted_percentage_ruj = true; + } + DoLeaderboardRequestPercentageRuj(player); + break; + case 11: + if(!leaderboard_sorted_wins_tak) + { + leaderboard_info_wins_tak.sort(pred_sort_by_win_count_tak); + leaderboard_sorted_wins_tak = true; + } + DoLeaderboardRequestWinsTak(player); + break; + case 12: + if(!leaderboard_sorted_percentage_tak) + { + leaderboard_info_percentage_tak.sort(pred_sort_by_win_percentage_tak); + leaderboard_sorted_percentage_tak = true; + } + DoLeaderboardRequestPercentageTak(player); + break; + } +} + +void AdventureManager::DoLeaderboardRequestWins(const char* player) +{ + ClientListEntry *pc = client_list.FindCharacter(player); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, 64 + sizeof(AdventureLeaderboard_Struct)); + AdventureLeaderboard_Struct *al = (AdventureLeaderboard_Struct*)(pack->pBuffer + 64); + strcpy((char*)pack->pBuffer, player); + + int place = -1; + int our_successes; + int our_failures; + int i = 0; + list::iterator iter = leaderboard_info_wins.begin(); + while(i < 100 && iter != leaderboard_info_wins.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.wins; + our_failures = li.losses; + } + + al->entries[i].success = li.wins; + al->entries[i].failure = li.losses; + strcpy(al->entries[i].name, li.name.c_str()); + i++; + iter++; + } + + if(place == -1 && iter != leaderboard_info_wins.end()) + { + while(iter != leaderboard_info_wins.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.wins; + our_failures = li.losses; + break; + } + i++; + iter++; + } + } + + if(place == -1) + { + al->our_rank = leaderboard_info_wins.size() + 1; + al->success = 0; + al->failure = 0; + } + else + { + al->our_rank = place; + al->success = our_successes; + al->failure = our_failures; + } + + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + delete pack; + } +} + +void AdventureManager::DoLeaderboardRequestPercentage(const char* player) +{ + ClientListEntry *pc = client_list.FindCharacter(player); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, 64 + sizeof(AdventureLeaderboard_Struct)); + AdventureLeaderboard_Struct *al = (AdventureLeaderboard_Struct*)(pack->pBuffer + 64); + strcpy((char*)pack->pBuffer, player); + + int place = -1; + int our_successes; + int our_failures; + int i = 0; + list::iterator iter = leaderboard_info_percentage.begin(); + while(i < 100 && iter != leaderboard_info_percentage.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.wins; + our_failures = li.losses; + } + + al->entries[i].success = li.wins; + al->entries[i].failure = li.losses; + strcpy(al->entries[i].name, li.name.c_str()); + i++; + iter++; + } + + if(place == -1 && iter != leaderboard_info_percentage.end()) + { + while(iter != leaderboard_info_percentage.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.wins; + our_failures = li.losses; + break; + } + i++; + iter++; + } + } + + if(place == -1) + { + al->our_rank = leaderboard_info_percentage.size() + 1; + al->success = 0; + al->failure = 0; + } + else + { + al->our_rank = place; + al->success = our_successes; + al->failure = our_failures; + } + + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + delete pack; + } +} + +void AdventureManager::DoLeaderboardRequestWinsGuk(const char* player) +{ + ClientListEntry *pc = client_list.FindCharacter(player); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, 64 + sizeof(AdventureLeaderboard_Struct)); + AdventureLeaderboard_Struct *al = (AdventureLeaderboard_Struct*)(pack->pBuffer + 64); + strcpy((char*)pack->pBuffer, player); + + int place = -1; + int our_successes; + int our_failures; + int i = 0; + list::iterator iter = leaderboard_info_wins_guk.begin(); + while(i < 100 && iter != leaderboard_info_wins_guk.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.guk_wins; + our_failures = li.guk_losses; + } + + al->entries[i].success = li.guk_wins; + al->entries[i].failure = li.guk_losses; + strcpy(al->entries[i].name, li.name.c_str()); + i++; + iter++; + } + + if(place == -1 && iter != leaderboard_info_wins_guk.end()) + { + while(iter != leaderboard_info_wins_guk.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.guk_wins; + our_failures = li.guk_losses; + break; + } + i++; + iter++; + } + } + + if(place == -1) + { + al->our_rank = leaderboard_info_wins_guk.size() + 1; + al->success = 0; + al->failure = 0; + } + else + { + al->our_rank = place; + al->success = our_successes; + al->failure = our_failures; + } + + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + delete pack; + } +} + +void AdventureManager::DoLeaderboardRequestPercentageGuk(const char* player) +{ + ClientListEntry *pc = client_list.FindCharacter(player); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, 64 + sizeof(AdventureLeaderboard_Struct)); + AdventureLeaderboard_Struct *al = (AdventureLeaderboard_Struct*)(pack->pBuffer + 64); + strcpy((char*)pack->pBuffer, player); + + int place = -1; + int our_successes; + int our_failures; + int i = 0; + list::iterator iter = leaderboard_info_percentage_guk.begin(); + while(i < 100 && iter != leaderboard_info_percentage_guk.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.guk_wins; + our_failures = li.guk_losses; + } + + al->entries[i].success = li.guk_wins; + al->entries[i].failure = li.guk_losses; + strcpy(al->entries[i].name, li.name.c_str()); + i++; + iter++; + } + + if(place == -1 && iter != leaderboard_info_percentage_guk.end()) + { + while(iter != leaderboard_info_percentage_guk.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.guk_wins; + our_failures = li.guk_losses; + break; + } + i++; + iter++; + } + } + + if(place == -1) + { + al->our_rank = leaderboard_info_percentage_guk.size() + 1; + al->success = 0; + al->failure = 0; + } + else + { + al->our_rank = place; + al->success = our_successes; + al->failure = our_failures; + } + + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + delete pack; + } +} + +void AdventureManager::DoLeaderboardRequestWinsMir(const char* player) +{ + ClientListEntry *pc = client_list.FindCharacter(player); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, 64 + sizeof(AdventureLeaderboard_Struct)); + AdventureLeaderboard_Struct *al = (AdventureLeaderboard_Struct*)(pack->pBuffer + 64); + strcpy((char*)pack->pBuffer, player); + + int place = -1; + int our_successes; + int our_failures; + int i = 0; + list::iterator iter = leaderboard_info_wins_mir.begin(); + while(i < 100 && iter != leaderboard_info_wins_mir.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.mir_wins; + our_failures = li.mir_losses; + } + + al->entries[i].success = li.mir_wins; + al->entries[i].failure = li.mir_losses; + strcpy(al->entries[i].name, li.name.c_str()); + i++; + iter++; + } + + if(place == -1 && iter != leaderboard_info_wins_mir.end()) + { + while(iter != leaderboard_info_wins_mir.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.mir_wins; + our_failures = li.mir_losses; + break; + } + i++; + iter++; + } + } + + if(place == -1) + { + al->our_rank = leaderboard_info_wins_mir.size() + 1; + al->success = 0; + al->failure = 0; + } + else + { + al->our_rank = place; + al->success = our_successes; + al->failure = our_failures; + } + + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + delete pack; + } +} + +void AdventureManager::DoLeaderboardRequestPercentageMir(const char* player) +{ + ClientListEntry *pc = client_list.FindCharacter(player); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, 64 + sizeof(AdventureLeaderboard_Struct)); + AdventureLeaderboard_Struct *al = (AdventureLeaderboard_Struct*)(pack->pBuffer + 64); + strcpy((char*)pack->pBuffer, player); + + int place = -1; + int our_successes; + int our_failures; + int i = 0; + list::iterator iter = leaderboard_info_percentage_mir.begin(); + while(i < 100 && iter != leaderboard_info_percentage_mir.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.mir_wins; + our_failures = li.mir_losses; + } + + al->entries[i].success = li.mir_wins; + al->entries[i].failure = li.mir_losses; + strcpy(al->entries[i].name, li.name.c_str()); + i++; + iter++; + } + + if(place == -1 && iter != leaderboard_info_percentage_mir.end()) + { + while(iter != leaderboard_info_percentage_mir.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.mir_wins; + our_failures = li.mir_losses; + break; + } + i++; + iter++; + } + } + + if(place == -1) + { + al->our_rank = leaderboard_info_percentage_mir.size() + 1; + al->success = 0; + al->failure = 0; + } + else + { + al->our_rank = place; + al->success = our_successes; + al->failure = our_failures; + } + + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + delete pack; + } +} + +void AdventureManager::DoLeaderboardRequestWinsMmc(const char* player) +{ + ClientListEntry *pc = client_list.FindCharacter(player); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, 64 + sizeof(AdventureLeaderboard_Struct)); + AdventureLeaderboard_Struct *al = (AdventureLeaderboard_Struct*)(pack->pBuffer + 64); + strcpy((char*)pack->pBuffer, player); + + int place = -1; + int our_successes; + int our_failures; + int i = 0; + list::iterator iter = leaderboard_info_wins_mmc.begin(); + while(i < 100 && iter != leaderboard_info_wins_mmc.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.mmc_wins; + our_failures = li.mmc_losses; + } + + al->entries[i].success = li.mmc_wins; + al->entries[i].failure = li.mmc_losses; + strcpy(al->entries[i].name, li.name.c_str()); + i++; + iter++; + } + + if(place == -1 && iter != leaderboard_info_wins_mmc.end()) + { + while(iter != leaderboard_info_wins_mmc.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.mmc_wins; + our_failures = li.mmc_losses; + break; + } + i++; + iter++; + } + } + + if(place == -1) + { + al->our_rank = leaderboard_info_wins_mmc.size() + 1; + al->success = 0; + al->failure = 0; + } + else + { + al->our_rank = place; + al->success = our_successes; + al->failure = our_failures; + } + + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + delete pack; + } +} + +void AdventureManager::DoLeaderboardRequestPercentageMmc(const char* player) +{ + ClientListEntry *pc = client_list.FindCharacter(player); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, 64 + sizeof(AdventureLeaderboard_Struct)); + AdventureLeaderboard_Struct *al = (AdventureLeaderboard_Struct*)(pack->pBuffer + 64); + strcpy((char*)pack->pBuffer, player); + + int place = -1; + int our_successes; + int our_failures; + int i = 0; + list::iterator iter = leaderboard_info_percentage_mmc.begin(); + while(i < 100 && iter != leaderboard_info_percentage_mmc.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.mmc_wins; + our_failures = li.mmc_losses; + } + + al->entries[i].success = li.mmc_wins; + al->entries[i].failure = li.mmc_losses; + strcpy(al->entries[i].name, li.name.c_str()); + i++; + iter++; + } + + if(place == -1 && iter != leaderboard_info_percentage_mmc.end()) + { + while(iter != leaderboard_info_percentage_mmc.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.mmc_wins; + our_failures = li.mmc_losses; + break; + } + i++; + iter++; + } + } + + if(place == -1) + { + al->our_rank = leaderboard_info_percentage_mmc.size() + 1; + al->success = 0; + al->failure = 0; + } + else + { + al->our_rank = place; + al->success = our_successes; + al->failure = our_failures; + } + + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + delete pack; + } +} + +void AdventureManager::DoLeaderboardRequestWinsRuj(const char* player) +{ + ClientListEntry *pc = client_list.FindCharacter(player); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, 64 + sizeof(AdventureLeaderboard_Struct)); + AdventureLeaderboard_Struct *al = (AdventureLeaderboard_Struct*)(pack->pBuffer + 64); + strcpy((char*)pack->pBuffer, player); + + int place = -1; + int our_successes; + int our_failures; + int i = 0; + list::iterator iter = leaderboard_info_wins_ruj.begin(); + while(i < 100 && iter != leaderboard_info_wins_ruj.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.ruj_wins; + our_failures = li.ruj_losses; + } + + al->entries[i].success = li.ruj_wins; + al->entries[i].failure = li.ruj_losses; + strcpy(al->entries[i].name, li.name.c_str()); + i++; + iter++; + } + + if(place == -1 && iter != leaderboard_info_wins_ruj.end()) + { + while(iter != leaderboard_info_wins_ruj.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.ruj_wins; + our_failures = li.ruj_losses; + break; + } + i++; + iter++; + } + } + + if(place == -1) + { + al->our_rank = leaderboard_info_wins_ruj.size() + 1; + al->success = 0; + al->failure = 0; + } + else + { + al->our_rank = place; + al->success = our_successes; + al->failure = our_failures; + } + + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + delete pack; + } +} + +void AdventureManager::DoLeaderboardRequestPercentageRuj(const char* player) +{ + ClientListEntry *pc = client_list.FindCharacter(player); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, 64 + sizeof(AdventureLeaderboard_Struct)); + AdventureLeaderboard_Struct *al = (AdventureLeaderboard_Struct*)(pack->pBuffer + 64); + strcpy((char*)pack->pBuffer, player); + + int place = -1; + int our_successes; + int our_failures; + int i = 0; + list::iterator iter = leaderboard_info_percentage_ruj.begin(); + while(i < 100 && iter != leaderboard_info_percentage_ruj.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.ruj_wins; + our_failures = li.ruj_losses; + } + + al->entries[i].success = li.ruj_wins; + al->entries[i].failure = li.ruj_losses; + strcpy(al->entries[i].name, li.name.c_str()); + i++; + iter++; + } + + if(place == -1 && iter != leaderboard_info_percentage_ruj.end()) + { + while(iter != leaderboard_info_percentage_ruj.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.ruj_wins; + our_failures = li.ruj_losses; + break; + } + i++; + iter++; + } + } + + if(place == -1) + { + al->our_rank = leaderboard_info_percentage_ruj.size() + 1; + al->success = 0; + al->failure = 0; + } + else + { + al->our_rank = place; + al->success = our_successes; + al->failure = our_failures; + } + + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + delete pack; + } +} + +void AdventureManager::DoLeaderboardRequestWinsTak(const char* player) +{ + ClientListEntry *pc = client_list.FindCharacter(player); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, 64 + sizeof(AdventureLeaderboard_Struct)); + AdventureLeaderboard_Struct *al = (AdventureLeaderboard_Struct*)(pack->pBuffer + 64); + strcpy((char*)pack->pBuffer, player); + + int place = -1; + int our_successes; + int our_failures; + int i = 0; + list::iterator iter = leaderboard_info_wins_ruj.begin(); + while(i < 100 && iter != leaderboard_info_wins_ruj.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.tak_wins; + our_failures = li.tak_losses; + } + + al->entries[i].success = li.tak_wins; + al->entries[i].failure = li.tak_losses; + strcpy(al->entries[i].name, li.name.c_str()); + i++; + iter++; + } + + if(place == -1 && iter != leaderboard_info_wins_ruj.end()) + { + while(iter != leaderboard_info_wins_ruj.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.tak_wins; + our_failures = li.tak_losses; + break; + } + i++; + iter++; + } + } + + if(place == -1) + { + al->our_rank = leaderboard_info_wins_ruj.size() + 1; + al->success = 0; + al->failure = 0; + } + else + { + al->our_rank = place; + al->success = our_successes; + al->failure = our_failures; + } + + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + delete pack; + } +} + +void AdventureManager::DoLeaderboardRequestPercentageTak(const char* player) +{ + ClientListEntry *pc = client_list.FindCharacter(player); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, 64 + sizeof(AdventureLeaderboard_Struct)); + AdventureLeaderboard_Struct *al = (AdventureLeaderboard_Struct*)(pack->pBuffer + 64); + strcpy((char*)pack->pBuffer, player); + + int place = -1; + int our_successes; + int our_failures; + int i = 0; + list::iterator iter = leaderboard_info_percentage_tak.begin(); + while(i < 100 && iter != leaderboard_info_percentage_tak.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.tak_wins; + our_failures = li.tak_losses; + } + + al->entries[i].success = li.tak_wins; + al->entries[i].failure = li.tak_losses; + strcpy(al->entries[i].name, li.name.c_str()); + i++; + iter++; + } + + if(place == -1 && iter != leaderboard_info_percentage_tak.end()) + { + while(iter != leaderboard_info_percentage_tak.end()) + { + LeaderboardInfo li = (*iter); + if(li.name.compare(player) == 0) + { + place = i; + our_successes = li.tak_wins; + our_failures = li.tak_losses; + break; + } + i++; + iter++; + } + } + + if(place == -1) + { + al->our_rank = leaderboard_info_percentage_tak.size() + 1; + al->success = 0; + al->failure = 0; + } + else + { + al->our_rank = place; + al->success = our_successes; + al->failure = our_failures; + } + + pack->Deflate(); + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + delete pack; + } +} + +bool AdventureManager::PopFinishedEvent(const char *name, AdventureFinishEvent &fe) +{ + list::iterator iter = finished_list.begin(); + while(iter != finished_list.end()) + { + if((*iter).name.compare(name) == 0) + { + fe.name = (*iter).name; + fe.points = (*iter).points; + fe.theme = (*iter).theme; + fe.win = (*iter).win; + finished_list.erase(iter); + Save(); + return true; + } + iter++; + } + return false; +} + +void AdventureManager::SendAdventureFinish(AdventureFinishEvent fe) +{ + ClientListEntry *pc = client_list.FindCharacter(fe.name.c_str()); + if(pc) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureFinish, sizeof(ServerAdventureFinish_Struct)); + ServerAdventureFinish_Struct *af = (ServerAdventureFinish_Struct*)pack->pBuffer; + strcpy(af->player, fe.name.c_str()); + af->theme = fe.theme; + af->win = fe.win; + af->points = fe.points; + + zoneserver_list.SendPacket(pc->zone(), pc->instance(), pack); + delete pack; + } +} + +void AdventureManager::Save() +{ + //disabled for now + return; + + stringstream ss(stringstream::in | stringstream::out); + + int number_of_elements = adventure_list.size(); + ss.write((const char*)&number_of_elements, sizeof(int)); + + char null_term = 0; + list::iterator a_iter = adventure_list.begin(); + while(a_iter != adventure_list.end()) + { + int cur = (*a_iter)->GetCount(); + ss.write((const char*)&cur, sizeof(int)); + + cur = (*a_iter)->GetAssassinationCount(); + ss.write((const char*)&cur, sizeof(int)); + + cur = (*a_iter)->GetTemplate()->id; + ss.write((const char*)&cur, sizeof(int)); + + cur = (int)(*a_iter)->GetStatus(); + ss.write((const char*)&cur, sizeof(int)); + + cur = (*a_iter)->GetInstanceID(); + ss.write((const char*)&cur, sizeof(int)); + + cur = (*a_iter)->GetRemainingTime(); + ss.write((const char*)&cur, sizeof(int)); + + list players = (*a_iter)->GetPlayers(); + cur = players.size(); + ss.write((const char*)&cur, sizeof(int)); + + list::iterator s_iter = players.begin(); + while(s_iter != players.end()) + { + ss.write((const char*)(*s_iter).c_str(), (*s_iter).size()); + ss.write((const char*)&null_term, sizeof(char)); + s_iter++; + } + + a_iter++; + } + + number_of_elements = finished_list.size(); + ss.write((const char*)&number_of_elements, sizeof(int)); + list::iterator f_iter = finished_list.begin(); + while(f_iter != finished_list.end()) + { + ss.write((const char*)&(*f_iter).win, sizeof(bool)); + ss.write((const char*)&(*f_iter).points, sizeof(int)); + ss.write((const char*)&(*f_iter).theme, sizeof(int)); + ss.write((const char*)(*f_iter).name.c_str(), (*f_iter).name.size()); + ss.write((const char*)&null_term, sizeof(char)); + f_iter++; + } + + FILE *f = fopen("adventure_state.dat", "w"); + if(f) + { + fwrite(ss.str().c_str(), ss.str().size(), 1, f); + fclose(f); + } +} + +void AdventureManager::Load() +{ + //disabled for now + return; + + char *data = NULL; + FILE *f = fopen("adventure_state.dat", "r"); + if(f) + { + fseek(f, 0, SEEK_END); + long length = ftell(f); + if(length > 0) + { + data = new char[length]; + fseek(f, 0, SEEK_SET); + fread(data, length, 1, f); + } + fclose(f); + } + + if(data) + { + char *ptr = data; + + int number_of_adventures = *((int*)ptr); + ptr += sizeof(int); + + for(int i = 0; i < number_of_adventures; ++i) + { + int count = *((int*)ptr); + ptr += sizeof(int); + + int a_count = *((int*)ptr); + ptr += sizeof(int); + + int template_id = *((int*)ptr); + ptr += sizeof(int); + + int status = *((int*)ptr); + ptr += sizeof(int); + + int instance_id = *((int*)ptr); + ptr += sizeof(int); + + int rem_time = *((int*)ptr); + ptr += sizeof(int); + + int num_players = *((int*)ptr); + ptr += sizeof(int); + + AdventureTemplate *t = GetAdventureTemplate(template_id); + if(t) + { + Adventure *adv = new Adventure(t, count, a_count, (AdventureStatus)status, instance_id, rem_time); + for(int j = 0; j < num_players; ++j) + { + adv->AddPlayer((const char*)ptr, false); + ptr += strlen((const char*)ptr); + ptr += 1; + } + adventure_list.push_back(adv); + } + else + { + for(int j = 0; j < num_players; ++j) + { + ptr += strlen((const char*)ptr); + ptr += 1; + } + } + } + + int number_of_finished = *((int*)ptr); + ptr += sizeof(int); + + for(int k = 0; k < number_of_finished; ++k) + { + AdventureFinishEvent afe; + afe.win = *((bool*)ptr); + ptr += sizeof(bool); + + afe.points = *((int*)ptr); + ptr += sizeof(int); + + afe.theme = *((int*)ptr); + ptr += sizeof(int); + + afe.name = (const char*)ptr; + ptr += strlen((const char*)ptr); + ptr += 1; + + finished_list.push_back(afe); + } + + safe_delete_array(data); + } +} + diff --git a/world/AdventureManager.h b/world/AdventureManager.h new file mode 100644 index 000000000..716458f3e --- /dev/null +++ b/world/AdventureManager.h @@ -0,0 +1,95 @@ +#ifndef ADVENTURE_MANAGER_H +#define ADVENTURE_MANAGER_H + +#include "../common/debug.h" +#include "../common/types.h" +#include "../common/timer.h" +#include "Adventure.h" +#include "AdventureTemplate.h" +#include +#include + +using namespace std; + +class AdventureManager +{ +public: + AdventureManager(); + ~AdventureManager(); + + void Process(); + + bool LoadAdventureTemplates(); + bool LoadAdventureEntries(); + void LoadLeaderboardInfo(); + + void CalculateAdventureRequestReply(const char *data); + void PlayerClickedDoor(const char *player, int zone_id, int door_id); + void TryAdventureCreate(const char *data); + void GetAdventureData(Adventure *adv); + void GetAdventureData(const char *name); + void LeaveAdventure(const char *name); + void IncrementCount(uint16 instance_id); + void IncrementAssassinationCount(uint16 instance_id); + void DoLeaderboardRequest(const char* player, uint8 type); + void SendAdventureFinish(AdventureFinishEvent fe); + void AddFinishedEvent(AdventureFinishEvent fe) { finished_list.push_back(fe); Save(); } + bool PopFinishedEvent(const char *name, AdventureFinishEvent &fe); + void Save(); + void Load(); + + Adventure **GetFinishedAdventures(const char *player, int &count); + Adventure *GetActiveAdventure(const char *player); + AdventureTemplate *GetAdventureTemplate(int theme, int id); + AdventureTemplate *GetAdventureTemplate(int id); + void GetZoneData(uint16 instance_id); +protected: + bool IsInExcludedZoneList(list excluded_zones, string zone_name, int version); + bool IsInExcludedZoneInList(list excluded_zone_ins, int zone_id, int door_object); + void DoLeaderboardRequestWins(const char* player); + void DoLeaderboardRequestPercentage(const char* player); + void DoLeaderboardRequestWinsGuk(const char* player); + void DoLeaderboardRequestPercentageGuk(const char* player); + void DoLeaderboardRequestWinsMir(const char* player); + void DoLeaderboardRequestPercentageMir(const char* player); + void DoLeaderboardRequestWinsMmc(const char* player); + void DoLeaderboardRequestPercentageMmc(const char* player); + void DoLeaderboardRequestWinsRuj(const char* player); + void DoLeaderboardRequestPercentageRuj(const char* player); + void DoLeaderboardRequestWinsTak(const char* player); + void DoLeaderboardRequestPercentageTak(const char* player); + + map adventure_templates; + map > adventure_entries; + list adventure_list; + list finished_list; + list leaderboard_info_wins; + list leaderboard_info_percentage; + list leaderboard_info_wins_guk; + list leaderboard_info_percentage_guk; + list leaderboard_info_wins_mir; + list leaderboard_info_percentage_mir; + list leaderboard_info_wins_mmc; + list leaderboard_info_percentage_mmc; + list leaderboard_info_wins_ruj; + list leaderboard_info_percentage_ruj; + list leaderboard_info_wins_tak; + list leaderboard_info_percentage_tak; + bool leaderboard_sorted_wins; + bool leaderboard_sorted_percentage; + bool leaderboard_sorted_wins_guk; + bool leaderboard_sorted_percentage_guk; + bool leaderboard_sorted_wins_mir; + bool leaderboard_sorted_percentage_mir; + bool leaderboard_sorted_wins_mmc; + bool leaderboard_sorted_percentage_mmc; + bool leaderboard_sorted_wins_ruj; + bool leaderboard_sorted_percentage_ruj; + bool leaderboard_sorted_wins_tak; + bool leaderboard_sorted_percentage_tak; + Timer *process_timer; + Timer *save_timer; + Timer *leaderboard_info_timer; +}; + +#endif \ No newline at end of file diff --git a/world/AdventureTemplate.h b/world/AdventureTemplate.h new file mode 100644 index 000000000..86fc3d0f5 --- /dev/null +++ b/world/AdventureTemplate.h @@ -0,0 +1,47 @@ +#ifndef ADVENTURE_TEMPLATE_H +#define ADVENTURE_TEMPLATE_H + +#include "../common/debug.h" +#include "../common/types.h" + +#pragma pack(1) + +struct AdventureTemplate +{ + uint32 id; + char zone[64]; + uint32 zone_version; + bool is_hard; + int32 min_level; + int32 max_level; + uint8 type; + uint32 type_data; + uint16 type_count; + float assa_x; + float assa_y; + float assa_z; + float assa_h; + char text[1024]; + uint32 duration; + uint32 zone_in_time; + int32 win_points; + int32 lose_points; + uint8 theme; + uint16 zone_in_zone_id; + float zone_in_x; + float zone_in_y; + uint16 zone_in_object_id; + float dest_x; + float dest_y; + float dest_z; + float dest_h; + int graveyard_zone_id; + float graveyard_x; + float graveyard_y; + float graveyard_z; + float graveyard_radius; +}; + +#pragma pack() + +#endif diff --git a/world/CMakeLists.txt b/world/CMakeLists.txt new file mode 100644 index 000000000..f36ba87fa --- /dev/null +++ b/world/CMakeLists.txt @@ -0,0 +1,91 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +SET(world_sources + Adventure.cpp + AdventureManager.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 + lfplist.cpp + LoginServer.cpp + LoginServerList.cpp + net.cpp + perl_EQLConfig.cpp + perl_EQW.cpp + perl_HTTPRequest.cpp + queryserv.cpp + ucs.cpp + wguild_mgr.cpp + world_logsys.cpp + WorldConfig.cpp + worlddb.cpp + zonelist.cpp + zoneserver.cpp +) + +SET(world_headers + Adventure.h + AdventureManager.h + AdventureTemplate.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 + lfplist.h + LoginServer.h + LoginServerList.h + net.h + queryserv.h + SoFCharCreateData.h + ucs.h + wguild_mgr.h + WorldConfig.h + worlddb.h + WorldTCPConnection.h + zonelist.h + zoneserver.h +) + +ADD_EXECUTABLE(world ${world_sources} ${world_headers}) + +ADD_DEFINITIONS(-DWORLD) + +TARGET_LINK_LIBRARIES(world Common ${PERL_LIBRARY} debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE}) + +IF(MSVC) + + SET_TARGET_PROPERTIES(world PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF") + TARGET_LINK_LIBRARIES(world "Ws2_32.lib") +ENDIF(MSVC) + +IF(MINGW) + TARGET_LINK_LIBRARIES(world "WS2_32") +ENDIF(MINGW) + +IF(UNIX) + TARGET_LINK_LIBRARIES(world "dl") + TARGET_LINK_LIBRARIES(world "z") + TARGET_LINK_LIBRARIES(world "m") + TARGET_LINK_LIBRARIES(world "rt") + TARGET_LINK_LIBRARIES(world "pthread") + ADD_DEFINITIONS(-fPIC) +ENDIF(UNIX) + +SET(EXECUTABLE_OUTPUT_PATH ../Bin) diff --git a/world/EQLConfig.cpp b/world/EQLConfig.cpp new file mode 100644 index 000000000..7c94085bf --- /dev/null +++ b/world/EQLConfig.cpp @@ -0,0 +1,384 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "EQLConfig.h" +#include "worlddb.h" +#include "LauncherLink.h" +#include "LauncherList.h" +#include "../common/MiscFunctions.h" +#include +#include + +extern LauncherList launcher_list; + +EQLConfig::EQLConfig(const char *launcher_name) +: m_name(launcher_name) +{ + LoadSettings(); +} + +void EQLConfig::LoadSettings() { + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + 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); + + 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); + return false; + } + safe_delete_array(query); + + return(new EQLConfig(name)); +} + +void EQLConfig::GetZones(std::vector &result) { + map::iterator cur, end; + cur = m_zones.begin(); + end = m_zones.end(); + for(; cur != end; cur++) { + result.push_back(cur->second); + } +} + +vector EQLConfig::ListZones() { + LauncherLink *ll = launcher_list.Get(m_name.c_str()); + vector res; + if(ll == NULL) { + //if the launcher isnt connected, use the list from the database. + map::iterator cur, end; + cur = m_zones.begin(); + end = m_zones.end(); + for(; cur != end; cur++) { + res.push_back(cur->first); + } + } else { + //otherwise, use the zone list from the launcher link. + ll->GetZoneList(res); + } + return(res); +} + +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); + 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); + return; + } + safe_delete_array(query); +} + +bool EQLConfig::IsConnected() const { + LauncherLink *ll = launcher_list.Get(m_name.c_str()); + return(ll != NULL); +} + +void EQLConfig::RestartZone(Const_char *zone_ref) { + LauncherLink *ll = launcher_list.Get(m_name.c_str()); + if(ll == NULL) + return; + ll->RestartZone(zone_ref); +} + +void EQLConfig::StopZone(Const_char *zone_ref) { + LauncherLink *ll = launcher_list.Get(m_name.c_str()); + if(ll == NULL) + return; + ll->StopZone(zone_ref); +} + +void EQLConfig::StartZone(Const_char *zone_ref) { + LauncherLink *ll = launcher_list.Get(m_name.c_str()); + if(ll == NULL) + return; + ll->StartZone(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); + + //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'; + char zonebuf[32]; + 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); + return false; + } + safe_delete_array(query); + + //update our internal state. + LauncherZone lz; + lz.name = short_name; + lz.port = port; + m_zones[lz.name] = lz; + + //if the launcher is connected, update it. + LauncherLink *ll = launcher_list.Get(m_name.c_str()); + if(ll != NULL) { + ll->BootZone(short_name, port); + } + + 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); + + //check internal state + map::iterator res; + res = m_zones.find(short_name); + if(res == m_zones.end()) { + //not found. + LogFile->write(EQEMuLog::Error, "Update for unknown zone %s", short_name); + 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'; + char zonebuf[32]; + 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); + return false; + } + safe_delete_array(query); + + + //update internal state + res->second.port = port; + + //if the launcher is connected, update it. + LauncherLink *ll = launcher_list.Get(m_name.c_str()); + if(ll != NULL) { + ll->RestartZone(short_name); + } + + return(true); +} + +bool EQLConfig::DeleteStaticZone(Const_char *short_name) { + //check internal state + map::iterator res; + res = m_zones.find(short_name); + if(res == m_zones.end()) { + //not found. + LogFile->write(EQEMuLog::Error, "Update for unknown zone %s", short_name); + 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'; + char zonebuf[32]; + 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); + return false; + } + safe_delete_array(query); + + //internal update. + m_zones.erase(res); + + //if the launcher is connected, update it. + LauncherLink *ll = launcher_list.Get(m_name.c_str()); + if(ll != NULL) { + ll->StopZone(short_name); + } + + return true; +} + +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); + return false; + } + safe_delete_array(query); + + //update in-memory version. + m_dynamics = count; + + //if the launcher is connected, update it. + LauncherLink *ll = launcher_list.Get(m_name.c_str()); + if(ll != NULL) { + ll->BootDynamics(count); + } + + return(false); +} + +int EQLConfig::GetDynamicCount() const { + return(m_dynamics); +} + +map EQLConfig::GetZoneDetails(Const_char *zone_ref) { + map res; + + LauncherLink *ll = launcher_list.Get(m_name.c_str()); + if(ll == NULL) { + res["name"] = zone_ref; + res["up"] = "0"; + res["starts"] = "0"; + res["port"] = "0"; + } else { + ll->GetZoneDetails(zone_ref, res); + } + + return(res); +} + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/world/EQLConfig.h b/world/EQLConfig.h new file mode 100644 index 000000000..faf679142 --- /dev/null +++ b/world/EQLConfig.h @@ -0,0 +1,82 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 EQLCONFIG_H_ +#define EQLCONFIG_H_ + +#include "../common/types.h" +#include "worlddb.h" +#include +#include + +using namespace std; + +class LauncherLink; + +typedef struct { + std::string name; + uint16 port; +} LauncherZone; + +//a class exported to perl representing a launcher's in-DB config +class EQLConfig { +public: + EQLConfig(const char *launcher_name); + + void LoadSettings(); + static EQLConfig *CreateLauncher(const char *name, uint8 dynamic_count); + + void GetZones(std::vector &result); + +//BEGIN PERL EXPORT + Const_char * GetName() const { return(m_name.c_str()); } + int GetStaticCount() const { return(m_zones.size()); } + bool IsConnected() const; //is this launcher connected to world + + void DeleteLauncher(); //kill this launcher and all its zones. + + void RestartZone(Const_char *zone_ref); + void StopZone(Const_char *zone_ref); + void StartZone(Const_char *zone_ref); + + bool BootStaticZone(Const_char *short_name, uint16 port); + bool ChangeStaticZone(Const_char *short_name, uint16 port); + bool DeleteStaticZone(Const_char *short_name); + + bool SetDynamicCount(int count); + int GetDynamicCount() const; + + vector ListZones(); //returns an array of zone refs + map GetZoneDetails(Const_char *zone_ref); +//END PERL EXPORT + +protected: + const string m_name; + + uint8 m_dynamics; + + map m_zones; //static zones. +}; + + + + + + +#endif /*EQLCONFIG_H_*/ + + diff --git a/world/EQW.cpp b/world/EQW.cpp new file mode 100644 index 000000000..2092b8ef8 --- /dev/null +++ b/world/EQW.cpp @@ -0,0 +1,474 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 +*/ + +#ifdef EMBPERL + +#include "../common/debug.h" +#include "EQW.h" +#include "EQWParser.h" +#include "WorldConfig.h" +#include "../common/races.h" +#include "../common/classes.h" +#include "../common/misc.h" +#include "../common/MiscFunctions.h" +#include "zoneserver.h" +#include "zonelist.h" +#include "clientlist.h" +#include "cliententry.h" +#include "LoginServer.h" +#include "LoginServerList.h" +#include "worlddb.h" +#include "client.h" +#include "LauncherList.h" +#include "LauncherLink.h" +#include "wguild_mgr.h" + +#include + +using namespace std; + +extern ZSList zoneserver_list; +extern ClientList client_list; +extern uint32 numzones; +extern LoginServerList loginserverlist; +extern LauncherList launcher_list; +extern volatile bool RunLoops; + + + + +EQW EQW::s_EQW; + +//IO Capture routine +XS(XS_EQWIO_PRINT); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQWIO_PRINT) +{ + dXSARGS; + if (items < 2) + return; + + int r; + for(r = 1; r < items; r++) { + char *str = SvPV_nolen(ST(r)); + EQW::Singleton()->AppendOutput(str); + } + + XSRETURN_EMPTY; +} + +EQW::EQW() { +} + +void EQW::AppendOutput(const char *str) { + m_outputBuffer += str; +// _log(WORLD__EQW, "Append %d chars, yeilding result of length %d", strlen(str), m_outputBuffer.length()); +} + +const std::string &EQW::GetOutput() const { +// _log(WORLD__EQW, "Getting, length %d", m_outputBuffer.length()); + return(m_outputBuffer); +} + +void EQW::LockWorld() { + WorldConfig::LockWorld(); + if (loginserverlist.Connected()) { + loginserverlist.SendStatus(); + } +} + +void EQW::UnlockWorld() { + WorldConfig::UnlockWorld(); + if (loginserverlist.Connected()) { + loginserverlist.SendStatus(); + } +} + +Const_char *EQW::GetConfig(Const_char *var_name) { + m_returnBuffer = WorldConfig::get()->GetByName(var_name); + return(m_returnBuffer.c_str()); +} + +bool EQW::LSConnected() { + return(loginserverlist.Connected()); +} + +int EQW::CountZones() { + return(zoneserver_list.GetZoneCount()); +} + +//returns an array of zone_refs (opaque) +vector EQW::ListBootedZones() { + vector res; + + vector zones; + zoneserver_list.GetZoneIDList(zones); + + vector::iterator cur, end; + cur = zones.begin(); + end = zones.end(); + for(; cur != end; cur++) { + res.push_back(itoa(*cur)); + } + + return(res); +} + +map EQW::GetZoneDetails(Const_char *zone_ref) { + map res; + + ZoneServer *zs = zoneserver_list.FindByID(atoi(zone_ref)); + if(zs == NULL) { + res["error"] = "Invalid zone."; + return(res); + } + + res["type"] = zs->IsStaticZone()?"static":"dynamic"; + res["zone_id"] = itoa(zs->GetZoneID()); + res["launch_name"] = zs->GetLaunchName(); + res["launched_name"] = zs->GetLaunchedName(); + res["short_name"] = zs->GetZoneName(); + res["long_name"] = zs->GetZoneLongName(); + res["port"] = itoa(zs->GetCPort()); + res["player_count"] = itoa(zs->NumPlayers()); + + //this isnt gunna work for dynamic zones... + res["launcher"] = ""; + if(zs->GetZoneID() != 0) { + LauncherLink *ll = launcher_list.FindByZone(zs->GetLaunchName()); + if(ll != NULL) + res["launcher"] = ll->GetName(); + } + + return(res); +} + +int EQW::CountPlayers() { + return(client_list.GetClientCount()); +} + +//returns an array of character names in the zone (empty=all zones) +vector EQW::ListPlayers(Const_char *zone_name) { + vector res; + + vector list; + client_list.GetClients(zone_name, list); + + vector::iterator cur, end; + cur = list.begin(); + end = list.end(); + for(; cur != end; cur++) { + res.push_back((*cur)->name()); + } + return(res); +} + +map EQW::GetPlayerDetails(Const_char *char_name) { + map res; + + ClientListEntry *cle = client_list.FindCharacter(char_name); + if(cle == NULL) { + res["error"] = "1"; + return(res); + } + + res["character"] = cle->name(); + res["account"] = cle->AccountName(); + res["account_id"] = itoa(cle->AccountID()); + res["location_short"] = cle->zone()?database.GetZoneName(cle->zone()):"No Zone"; + res["location_long"] = res["location_short"]; + res["location_id"] = itoa(cle->zone()); + res["ip"] = long2ip(cle->GetIP()); + res["level"] = itoa(cle->level()); + res["race"] = GetRaceName(cle->race()); + res["race_id"] = itoa(cle->race()); + res["class"] = GetEQClassName(cle->class_()); + res["class_id"] = itoa(cle->class_()); + res["guild_id"] = itoa(cle->GuildID()); + res["guild"] = guild_mgr.GetGuildName(cle->GuildID()); + res["status"] = itoa(cle->Admin()); +// res["patch"] = cle->DescribePatch(); + + return(res); +} + +int EQW::CountLaunchers(bool active_only) { + if(active_only) + return(launcher_list.GetLauncherCount()); + + vector it(EQW::ListLaunchers()); + return(it.size()); +} +/* +vector EQW::ListActiveLaunchers() { + vector launchers; + launcher_list.GetLauncherNameList(launchers); + return(launchers); +}*/ + +vector EQW::ListLaunchers() { +// vector list; +// database.GetLauncherList(list); + vector launchers; + launcher_list.GetLauncherNameList(launchers); + return(launchers); + +/* if(list.empty()) { + return(launchers); + } else if(launchers.empty()) { + return(list); + } + + //union the two lists. + vector::iterator curo, endo, curi, endi; + curo = list.begin(); + endo = list.end(); + for(; curo != endo; curo++) { + bool found = false; + curi = launchers.begin(); + endi = launchers.end(); + for(; curi != endi; curi++) { + if(*curo == *curi) { + found = true; + break; + } + } + if(found) + break; + launchers.push_back(*curo); + } + return(launchers);*/ +} + +EQLConfig * EQW::GetLauncher(Const_char *launcher_name) { + return(launcher_list.GetConfig(launcher_name)); +} + +void EQW::CreateLauncher(Const_char *launcher_name, int dynamic_count) { + launcher_list.CreateLauncher(launcher_name, dynamic_count); +} + +void EQW::LSReconnect() { + #ifdef _WINDOWS + _beginthread(AutoInitLoginServer, 0, NULL); + #else + pthread_t thread; + pthread_create(&thread, NULL, &AutoInitLoginServer, NULL); + #endif + RunLoops = true; + _log(WORLD__CONSOLE,"Login Server Reconnect manually restarted by Web Tool"); +} + +/*EQLConfig * EQW::FindLauncher(Const_char *zone_ref) { + return(NULL); +}*/ + +/* +map EQW::GetLaunchersDetails(Const_char *launcher_name) { + map res; + + LauncherLink *ll = launcher_list.Get(launcher_name); + if(ll == NULL) { + res["name"] = launcher_name; + res["ip"] = "Not Connected"; + res["id"] = "0"; + res["zone_count"] = "0"; + res["connected"] = "no"; + return(res); + } else { + res["name"] = ll->GetName(); + res["ip"] = long2ip(ll->GetIP()); + res["id"] = itoa(ll->GetID()); + res["zone_count"] = itoa(ll->CountZones()); + res["connected"] = "yes"; + } + + return(res); +} + +vector EQW::ListLauncherZones(Const_char *launcher_name) { + vector list; + LauncherLink *ll = launcher_list.Get(launcher_name); + if(ll != NULL) { + ll->GetZoneList(list); + } + return(list); +} + +map EQW::GetLauncherZoneDetails(Const_char *launcher_name, Const_char *zone_ref) { + map res; + LauncherLink *ll = launcher_list.Get(launcher_name); + if(ll != NULL) { + ll->GetZoneDetails(zone_ref, res); + } else { + res["error"] = "Launcher Not Found"; + } + return(res); +} + +void EQW::CreateLauncher(Const_char *launcher_name, int dynamic_count) { +} + +bool EQW::BootStaticZone(Const_char *launcher_name, Const_char *short_name) { + return(false); +} + +bool EQW::DeleteStaticZone(Const_char *launcher_name, Const_char *short_name) { + return(false); +} + +bool EQW::SetDynamicCount(Const_char *launcher_name, int count) { + return(false); +} + +int EQW::GetDynamicCount(Const_char *launcher_name) { + return(0); +} +*/ + +uint32 EQW::CreateGuild(const char* name, uint32 leader_char_id) { + uint32 id = guild_mgr.CreateGuild(name, leader_char_id); + if(id != GUILD_NONE) + client_list.UpdateClientGuild(leader_char_id, id); + return(id); +} + +bool EQW::DeleteGuild(uint32 guild_id) { + return(guild_mgr.DeleteGuild(guild_id)); +} + +bool EQW::RenameGuild(uint32 guild_id, const char* name) { + return(guild_mgr.RenameGuild(guild_id, name)); +} + +bool EQW::SetGuildMOTD(uint32 guild_id, const char* motd, const char *setter) { + return(guild_mgr.SetGuildMOTD(guild_id, motd, setter)); +} + +bool EQW::SetGuildLeader(uint32 guild_id, uint32 leader_char_id) { + return(guild_mgr.SetGuildLeader(guild_id, leader_char_id)); +} + +bool EQW::SetGuild(uint32 charid, uint32 guild_id, uint8 rank) { + client_list.UpdateClientGuild(charid, guild_id); + return(guild_mgr.SetGuild(charid, guild_id, rank)); +} + +bool EQW::SetGuildRank(uint32 charid, uint8 rank) { + return(guild_mgr.SetGuildRank(charid, rank)); +} + +bool EQW::SetBankerFlag(uint32 charid, bool is_banker) { + return(guild_mgr.SetBankerFlag(charid, is_banker)); +} + +bool EQW::SetTributeFlag(uint32 charid, bool enabled) { + return(guild_mgr.SetTributeFlag(charid, enabled)); +} + +bool EQW::SetPublicNote(uint32 charid, const char *note) { + return(guild_mgr.SetPublicNote(charid, 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; +} + +vector EQW::ListBugs(uint32 offset) { + 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); + return res; +} + +map EQW::GetBugDetails(Const_char *id) { + 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); + return res; +} + +void EQW::ResolveBug(const char *id) { + vector res; + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + MYSQL_ROW row; + if(database.RunQuery(query, MakeAnyLenString(&query, "UPDATE bugs SET status=1 WHERE id=%s", id), errbuf)) { + safe_delete_array(query); + } + safe_delete_array(query); +} + + +#endif //EMBPERL + + + + + + + + + + + + + + + + diff --git a/world/EQW.h b/world/EQW.h new file mode 100644 index 000000000..4580a3998 --- /dev/null +++ b/world/EQW.h @@ -0,0 +1,93 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 EQW_H_ +#define EQW_H_ + +#include +#include +#include +#include "../common/types.h" +using namespace std; + +class EQLConfig; + +//this is the main object exported to perl. +class EQW { + EQW(); +public: + static EQW *Singleton() { return(&s_EQW); } + + void AppendOutput(const char *str); + const std::string &GetOutput() const; + void ClearOutput() { m_outputBuffer = ""; } + +//BEGIN PERL EXPORT + //NOTE: you must have a space after the * of a return value + Const_char * GetConfig(Const_char *var_name); + void LockWorld(); + void UnlockWorld(); + + bool LSConnected(); + void LSReconnect(); + + int CountZones(); + vector ListBootedZones(); //returns an array of zone_refs (opaque) + map GetZoneDetails(Const_char *zone_ref); //returns a hash ref of details + + int CountPlayers(); + vector ListPlayers(Const_char *zone_name = ""); //returns an array of player refs (opaque) + map GetPlayerDetails(Const_char *player_ref); //returns a hash ref of details + + int CountLaunchers(bool active_only); +// vector ListActiveLaunchers(); //returns an array of launcher names + vector ListLaunchers(); //returns an array of launcher names + EQLConfig * GetLauncher(Const_char *launcher_name); //returns the EQLConfig object for the specified launcher. + void CreateLauncher(Const_char *launcher_name, int dynamic_count); +// EQLConfig * FindLauncher(Const_char *zone_ref); + + //Guild routines, mostly wrappers around guild_mgr + uint32 CreateGuild(const char* name, uint32 leader_char_id); + bool DeleteGuild(uint32 guild_id); + bool RenameGuild(uint32 guild_id, const char* name); + bool SetGuildMOTD(uint32 guild_id, const char* motd, const char *setter); + bool SetGuildLeader(uint32 guild_id, uint32 leader_char_id); + bool SetGuild(uint32 charid, uint32 guild_id, uint8 rank); + bool SetGuildRank(uint32 charid, uint8 rank); + bool SetBankerFlag(uint32 charid, bool is_banker); + bool SetTributeFlag(uint32 charid, bool enabled); + bool SetPublicNote(uint32 charid, const char *note); + + //bugs + int CountBugs(); + vector ListBugs(uint32 offset); //returns an array of zone_refs (opaque) + map GetBugDetails(const char *id); + void ResolveBug(const char *id); + +//END PERL EXPORT + +protected: + std::string m_outputBuffer; + std::string m_returnBuffer; + + bool m_worldLocked; + +private: + static EQW s_EQW; +}; + +#endif /*EQW_H_*/ diff --git a/world/EQWHTTPHandler.cpp b/world/EQWHTTPHandler.cpp new file mode 100644 index 000000000..4212981d6 --- /dev/null +++ b/world/EQWHTTPHandler.cpp @@ -0,0 +1,340 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "EQWHTTPHandler.h" +#include "../common/SocketLib/Base64.h" +#include "EQWParser.h" +#include "EQW.h" +#include "HTTPRequest.h" +#include "../common/logsys.h" +#include "worlddb.h" +#include "console.h" + +using namespace std; + +Mime EQWHTTPHandler::s_mime; +#ifdef EMBPERL +EQWParser *EQWHTTPHandler::s_parser = NULL; +#endif +const int EQWHTTPHandler::READ_BUFFER_LEN = 1024; //for page IO, was a static const member, but VC6 got mad. + +EQWHTTPHandler::EQWHTTPHandler(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort) +: HttpdSocket(ID,in_socket,irIP,irPort), + m_closeOnFinish(false) +{ +} + +EQWHTTPHandler::~EQWHTTPHandler() { + +} + +#ifdef EMBPERL +EQWParser *EQWHTTPHandler::GetParser() { + if(s_parser == NULL) { + EQW::Singleton()->ClearOutput(); + s_parser = new EQWParser(); + const string &res = EQW::Singleton()->GetOutput(); + if(!res.empty()) { + printf("EQWParser Init output:\n%s\n\n", res.c_str()); + EQW::Singleton()->ClearOutput(); + } + } + return(s_parser); +} +#endif + +/*void EQWHTTPHandler::OnWrite() { + HttpdSocket::OnWrite(); + if(m_closeOnFinish && GetOutputLength() == 0) { +// printf("CLOSING\n"); + Close(); + } +}*/ + + +void EQWHTTPHandler::Exec() { + m_sentHeaders = false; + m_responseCode = "200"; +// printf("Request: %s, %s, %s, %s.\n", GetMethod().c_str(), GetUrl().c_str(), GetUri().c_str(), GetQueryString().c_str()); + + SetHttpVersion("HTTP/1.0"); + AddResponseHeader("Connection", "close"); + + if(GetUri().find("..") != string::npos) { + SendResponse("403", "Forbidden"); + printf("%s is forbidden.\n", GetUri().c_str()); + return; + } + + if(!CheckAuth()) { + AddResponseHeader("Content-type", "text/plain"); + AddResponseHeader("WWW-Authenticate", "Basic realm=\"EQEmulator\""); + SendResponse("401", "Authorization Required"); + SendString("Gotta Authenticate."); + } else { + string::size_type start = GetUri().find_first_not_of('/'); + string page; + if(start != string::npos) + page = GetUri().substr(start); + else + page = "index.html"; + SendPage(page); + } +/* if (!Detach()) { + printf("Unable to detach...\n"); + } + if(GetOutputLength() > 0) { + //we cannot close yet + m_closeOnFinish = true; + } else { + Close(); + }*/ + Free(); //the "app" side (us) is done with this connection too... + Disconnect(); +} + +void EQWHTTPHandler::OnHeader(const std::string& key,const std::string& value) { + HttpdSocket::OnHeader(key, value); + + if (!strcasecmp(key.c_str(),"Authorization")) { + if(strncasecmp(value.c_str(), "Basic ", 6)) { + printf("Invalid auth type. Expected Basic: %s\n", value.c_str()); + return; + } + + std::string dec; + Base64::decode(value.c_str() + 6, dec); + + std::string::size_type cpos; + cpos = dec.find_first_of(':'); + if(cpos == string::npos) { + printf("Invalid auth string: %s\n", dec.c_str()); + return; + } + + m_username = dec.substr(0, cpos); + m_password = dec.substr(cpos+1); + } +} + +//we should prolly cache login info here... if we load a fresh page, we could be checking +//their auth dozens of times rather quickly... +bool EQWHTTPHandler::CheckAuth() const { + if(m_username.length() < 1) + return(false); + + int16 status = 0; + uint32 acctid = database.CheckLogin(m_username.c_str(), m_password.c_str(), &status); + if(acctid == 0) { + _log(WORLD__HTTP_ERR, "Login autentication failed for %s with '%s'", m_username.c_str(), m_password.c_str()); + return(false); + } + if(status < httpLoginStatus) { + _log(WORLD__HTTP_ERR, "Login of %s failed: status too low.", m_username.c_str()); + return(false); + } + + return(true); +} + +void EQWHTTPHandler::SendPage(const std::string &file) { + + string path = "templates/"; + path += file; + + FILE *f = fopen(path.c_str(), "rb"); + if(f == NULL) { + SendResponse("404", "Not Found"); + SendString("Not found."); + printf("%s not found.\n", file.c_str()); + return; + } + + string type = s_mime.GetMimeFromFilename(file); + AddResponseHeader("Content-type", type); + + bool process = false; +#ifdef EMBPERL + if(type == "text/html") + process = true; + else { + //not processing, send headers right away +#endif + SendResponse("200", "OK"); +#ifdef EMBPERL + } +#endif + + + char *buffer = new char[READ_BUFFER_LEN+1]; + size_t len; + string to_process; + while((len = fread(buffer, 1, READ_BUFFER_LEN, f)) > 0) { + buffer[len] = '\0'; + if(process) + to_process += buffer; + else + SendBuf(buffer, len); + } + delete[] buffer; + fclose(f); +#ifdef EMBPERL + if(process) { + //convert the base form into a useful perl exportable form + HTTPRequest req(this, GetHttpForm()); + GetParser()->SetHTTPRequest("testing", &req); + + //parse out the page and potentially pass some stuff on to perl. + ProcessAndSend(to_process); + + //clear out the form, just in case (since it gets destroyed next) + GetParser()->SetHTTPRequest("testing", NULL); + } +#endif +} + +bool EQWHTTPHandler::LoadMimeTypes(const char *filename) { + return(s_mime.LoadMimeFile(filename)); +} + +#ifdef EMBPERL +void EQWHTTPHandler::ProcessAndSend(const string &str) { + string::size_type len = str.length(); + string::size_type start = 0; + string::size_type pos, end; + + while((pos = str.find("", pos+2); + if(end == string::npos) { + //terminal ?> not found... should issue a warning or something... + string scriptBody = str.substr(pos+2); + ProcessScript(scriptBody); + start = len; + break; + } else { + //script only consumes some of this buffer... + string scriptBody = str.substr(pos+2, end-pos-2); + ProcessScript(scriptBody); + start = end + 2; + } + } + + //send whatever is left over + if(start != len) + ProcessText(str.c_str() + start, len-start); +} + +void EQWHTTPHandler::ProcessScript(const std::string &script_body) { + const char *script = script_body.c_str(); + if(strcmp("perl", script) == 0) + script += 4; //allow EQW_eval("testing", script_body.c_str()); + const string &res = EQW::Singleton()->GetOutput(); + if(!res.empty()) { + ProcessText(res.c_str(), res.length()); + EQW::Singleton()->ClearOutput(); + } +} + +void EQWHTTPHandler::ProcessText(const char *txt, int len) { + if(!m_sentHeaders) { + SendResponse(m_responseCode, "OK"); + m_sentHeaders = true; + } + SendBuf(txt, len); +} +#endif + + +EQWHTTPServer::EQWHTTPServer() +: m_port(0) +{ +} + +void EQWHTTPServer::CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort) { + EQWHTTPHandler *conn = new EQWHTTPHandler(ID, in_socket, irIP, irPort); + AddConnection(conn); +} + +void EQWHTTPServer::Stop() { + _log(WORLD__HTTP, "Requesting that HTTP Service stop."); + m_running = false; + Close(); +} + +bool EQWHTTPServer::Start(uint16 port, const char *mime_file) { + if(m_running) { + _log(WORLD__HTTP_ERR, "HTTP Service is already running on port %d", m_port); + return(false); + } + + //load up our nice mime types + if(!EQWHTTPHandler::LoadMimeTypes(mime_file)) { + _log(WORLD__HTTP_ERR, "Failed to load mime types from '%s'", mime_file); + return(false); + } else { + _log(WORLD__HTTP, "Loaded mime types from %s", mime_file); + } + + //fire up the server thread + char errbuf[TCPServer_ErrorBufferSize]; + if(!Open(port, errbuf)) { + _log(WORLD__HTTP_ERR, "Unable to bind to port %d for HTTP service: %s", port, errbuf); + return(false); + } + + m_running = true; + m_port = port; + + /* + +#ifdef _WINDOWS + _beginthread(ThreadProc, 0, this); +#else + pthread_create(&m_thread, NULL, ThreadProc, this); +#endif*/ + + return(true); +} + +/* +void EQWHTTPServer::Run() { + _log(WORLD__HTTP, "HTTP Processing thread started on port %d", m_port); + do { +#warning DELETE THIS IF YOU DONT USE IT + Sleep(10); + } while(m_running); + _log(WORLD__HTTP, "HTTP Processing thread terminating on port %d", m_port); +} + +ThreadReturnType EQWHTTPServer::ThreadProc(void *data) { + ((EQWHTTPServer *) data)->Run(); + THREAD_RETURN(NULL); +}*/ + + + diff --git a/world/EQWHTTPHandler.h b/world/EQWHTTPHandler.h new file mode 100644 index 000000000..cf6a2f508 --- /dev/null +++ b/world/EQWHTTPHandler.h @@ -0,0 +1,101 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 EQWHTTPHandler_H +#define EQWHTTPHandler_H + +#include "../common/TCPServer.h" +#include "../common/TCPConnection.h" +#include "../common/SocketLib/HttpdSocket.h" +#include "../common/SocketLib/Mime.h" +#include "../common/types.h" + +class EQWParser; + +class EQWHTTPHandler : public HttpdSocket { + static const int READ_BUFFER_LEN; +public: + EQWHTTPHandler(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort); + virtual ~EQWHTTPHandler(); + + void SetResponseCode(const char *code) { m_responseCode = code; } + + //HttpdSocket interface: + virtual void Exec(); + virtual void OnHeader(const std::string& key,const std::string& value); + + static bool LoadMimeTypes(const char *filename); +protected: + bool CheckAuth() const; + void SendPage(const std::string &file); + + //credentials + std::string m_username; + std::string m_password; + + bool m_closeOnFinish; + std::string m_responseCode; + bool m_sentHeaders; + + + //our mime type manager + static Mime s_mime; + + +#ifdef EMBPERL + void ProcessAndSend(const std::string &entire_html_page); + void ProcessScript(const std::string &script_body); + void ProcessText(const char *txt, int len); + + static EQWParser *GetParser(); + +private: + static EQWParser *s_parser; +#endif +}; + +class EQWHTTPServer : protected TCPServer { +public: + EQWHTTPServer(); + + bool Start(uint16 port, const char *mime_file); + void Stop(); + + +protected: + volatile bool m_running; + uint16 m_port; + + virtual void CreateNewConnection(uint32 ID, SOCKET in_socket, uint32 irIP, uint16 irPort); + +/* //I decided to put this into its own thread so that the HTTP pages + //cannot block the main world server's operation. + static ThreadReturnType ThreadProc(void* tmp); + void Run(); + + + +#ifndef WIN32 + pthread_t m_thread; +#endif*/ +}; + +#endif + + + + diff --git a/world/EQWParser.cpp b/world/EQWParser.cpp new file mode 100644 index 000000000..b6bc63ac3 --- /dev/null +++ b/world/EQWParser.cpp @@ -0,0 +1,352 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 +*/ + +//a lot of this is copied from embperl.cpp, but I didnt feel like factoring the common stuff out + +#ifdef EMBPERL + +#include "../common/debug.h" +#include "EQWParser.h" +#include "EQW.h" +#include "../common/EQDB.h" +#include "../common/logsys.h" +#include "worlddb.h" + +using namespace std; + +#ifndef GvCV_set +#define GvCV_set(gv,cv) (GvCV(gv) = (cv)) +#endif + + +XS(XS_EQWIO_PRINT); + +//so embedded scripts can use xs extensions (ala 'use socket;') +EXTERN_C void boot_DynaLoader(pTHX_ CV* cv); +EXTERN_C XS(boot_EQW); +EXTERN_C XS(boot_EQDB); +EXTERN_C XS(boot_EQDBRes); +EXTERN_C XS(boot_HTTPRequest); +EXTERN_C XS(boot_EQLConfig); + +EXTERN_C void xs_init(pTHX) +{ + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = '\0'; + + char buf[128]; //shouldent have any function names longer than this. + + //add the strcpy stuff to get rid of const warnings.... + + newXS(strcpy(buf, "DynaLoader::boot_DynaLoader"), boot_DynaLoader, file); + newXS(strcpy(buf, "EQW::boot_EQW"), boot_EQW, file); + newXS(strcpy(buf, "EQDB::boot_EQDB"), boot_EQDB, file); + newXS(strcpy(buf, "EQDBRes::boot_EQDBRes"), boot_EQDBRes, file); + newXS(strcpy(buf, "HTTPRequest::boot_HTTPRequest"), boot_HTTPRequest, file); + newXS(strcpy(buf, "EQLConfig::boot_EQLConfig"), boot_EQLConfig, file); + newXS(strcpy(buf, "EQWIO::PRINT"), XS_EQWIO_PRINT, file); +} + +EQWParser::EQWParser() { + //setup perl... + my_perl = perl_alloc(); + _empty_sv = newSV(0); + if(!my_perl) + _log(WORLD__PERL_ERR, "Error: perl_alloc failed!"); + else + DoInit(); +} + +void EQWParser::DoInit() { + const char *argv_eqemu[] = { "", + "-w", "-W", + "-e", "0;", NULL }; + + int argc = 5; + + char **argv = (char **)argv_eqemu; + char **env = { NULL }; + + PL_perl_destruct_level = 1; + + perl_construct(my_perl); + + PERL_SYS_INIT3(&argc, &argv, &env); + + perl_parse(my_perl, xs_init, argc, argv, env); + + perl_run(my_perl); + + //a little routine we use a lot. + eval_pv("sub my_eval {eval $_[0];}", TRUE); //dies on error + + //ruin the perl exit and command: + eval_pv("sub my_exit {}",TRUE); + eval_pv("sub my_sleep {}",TRUE); + if(gv_stashpv("CORE::GLOBAL", FALSE)) { + GV *exitgp = gv_fetchpv("CORE::GLOBAL::exit", TRUE, SVt_PVCV); + GvCV_set(exitgp, perl_get_cv("my_exit", TRUE)); //dies on error + GvIMPORTED_CV_on(exitgp); + GV *sleepgp = gv_fetchpv("CORE::GLOBAL::sleep", TRUE, SVt_PVCV); + GvCV_set(sleepgp, perl_get_cv("my_sleep", TRUE)); //dies on error + GvIMPORTED_CV_on(sleepgp); + } + + //setup eval_file + eval_pv( + "our %Cache;" + "use Symbol qw(delete_package);" + "sub eval_file {" + "my($package, $filename) = @_;" + "$filename=~s/\'//g;" + "if(! -r $filename) { print \"Unable to read perl file '$filename'\\n\"; return; }" + "my $mtime = -M $filename;" + "if(defined $Cache{$package}{mtime}&&$Cache{$package}{mtime} <= $mtime && !($package eq 'plugin')){" + " return;" + "} else {" + //we 'my' $filename,$mtime,$package,$sub to prevent them from changing our state up here. + " eval(\"package $package; my(\\$filename,\\$mtime,\\$package,\\$sub); \\$isloaded = 1; require '$filename'; \");" + "}" + "}" + ,FALSE); + + //make a tie-able class to capture IO and get it where it needs to go + eval_pv( + "package EQWIO; " +// "&boot_EQEmuIO;" + "sub TIEHANDLE { my $me = bless {}, $_[0]; $me->PRINT('Creating '.$me); return($me); } " + "sub WRITE { } " + "sub PRINTF { my $me = shift; my $fmt = shift; $me->PRINT(sprintf($fmt, @_)); } " + "sub CLOSE { my $me = shift; $me->PRINT('Closing '.$me); } " + "sub DESTROY { my $me = shift; $me->PRINT('Destroying '.$me); } " +//this ties us for all packages + "package MAIN;" + " if(tied *STDOUT) { untie(*STDOUT); }" + " if(tied *STDERR) { untie(*STDERR); }" + " tie *STDOUT, 'EQWIO';" + " tie *STDERR, 'EQWIO';" + ,FALSE); + + eval_pv( + "package world; " + ,FALSE + ); + + //make sure the EQW pointer is set up in this package + EQW *curc = EQW::Singleton(); + SV *l = get_sv("world::EQW", true); + if(curc != NULL) { + sv_setref_pv(l, "EQW", curc); + } else { + //clear out the value, mainly to get rid of blessedness + sv_setsv(l, _empty_sv); + } + + //make sure the EQDB pointer is set up in this package + EQDB::SetMySQL(database.getMySQL()); + EQDB *curc_db = EQDB::Singleton(); + SV *l_db = get_sv("world::EQDB", true); + if(curc_db != NULL) { + sv_setref_pv(l_db, "EQDB", curc_db); + } else { + //clear out the value, mainly to get rid of blessedness + sv_setsv(l_db, _empty_sv); + } + + //load up EQW + eval_pv( + "package EQW;" + "&boot_EQW;" //load our EQW XS + "package EQDB;" + "&boot_EQDB;" //load our EQW XS + "package EQDBRes;" + "&boot_EQDBRes;" //load our EQW XS + "package HTTPRequest;" + "&boot_HTTPRequest;" //load our HTTPRequest XS + "package EQLConfig;" + "&boot_EQLConfig;" //load our EQLConfig XS + , FALSE ); + + +#ifdef EMBPERL_PLUGIN + _log(WORLD__PERL, "Loading worldui perl plugins."); + string err; + if(!eval_file("world", "worldui.pl", err)) { + _log(WORLD__PERL_ERR, "Warning - world.pl: %s", err.c_str()); + } + + eval_pv( + "package world; " + "if(opendir(D,'worldui')) { " + " my @d = readdir(D);" + " closedir(D);" + " foreach(@d){ " + " next unless(/\\.pl$); " + " require 'templates/'.$_;" + " }" + "}" + ,FALSE); +#endif //EMBPERL_PLUGIN +} + +EQWParser::~EQWParser() { + //removed to try to stop perl from exploding on reload, we'll see +/* eval_pv( + "package quest;" + " untie *STDOUT;" + " untie *STDERR;" + ,FALSE); +*/ + perl_free(my_perl); +} + +bool EQWParser::eval_file(const char * packagename, const char * filename, std::string &error) +{ + std::vector args; + args.push_back(packagename); + args.push_back(filename); + return(dosub("eval_file", args, error)); +} + +bool EQWParser::dosub(const char * subname, const std::vector &args, string &error, int mode) { + bool err = false; + dSP; /* initialize stack pointer */ + ENTER; /* everything created after here */ + SAVETMPS; /* ...is a temporary variable. */ + PUSHMARK(SP); /* remember the stack pointer */ + if(args.size() > 0) + { + for(std::vector::const_iterator i = args.begin(); i != args.end(); ++i) + {/* push the arguments onto the perl stack */ + XPUSHs(sv_2mortal(newSVpv(i->c_str(), i->length()))); + } + } + PUTBACK; /* make local stack pointer global */ + call_pv(subname, mode); /*eval our code*/ + SPAGAIN; /* refresh stack pointer */ + if(SvTRUE(ERRSV)) { + err = true; + } + FREETMPS; /* free temp values */ + LEAVE; /* ...and the XPUSHed "mortal" args.*/ + + if(err) { + error = "Perl runtime error: "; + error += SvPVX(ERRSV); + return(false); + } + return(true); +} + +bool EQWParser::eval(const char * code, string &error) { + std::vector arg; + arg.push_back(code); + return(dosub("my_eval", arg, error, G_SCALAR|G_DISCARD|G_EVAL|G_KEEPERR)); +} + + +void EQWParser::EQW_eval(const char *pkg, const char *code) { + char namebuf[64]; + + snprintf(namebuf, 64, "package %s;", pkg); + eval_pv(namebuf, FALSE); + + //make sure the EQW pointer is set up + EQW *curc = EQW::Singleton(); + snprintf(namebuf, 64, "EQW"); +// snprintf(namebuf, 64, "%s::EQW", pkg); + SV *l = get_sv(namebuf, true); + if(curc != NULL) { + sv_setref_pv(l, "EQW", curc); + } else { + //clear out the value, mainly to get rid of blessedness + sv_setsv(l, _empty_sv); + } + //make sure the EQDB pointer is set up + EQDB *curc_db = EQDB::Singleton(); + snprintf(namebuf, 64, "EQDB"); +// snprintf(namebuf, 64, "%s::EQW", pkg); + SV *l_db = get_sv(namebuf, true); + if(curc_db != NULL) { + sv_setref_pv(l_db, "EQDB", curc_db); + } else { + //clear out the value, mainly to get rid of blessedness + sv_setsv(l_db, _empty_sv); + } + + string err; + if(!eval(code, err)) { + EQW::Singleton()->AppendOutput(err.c_str()); + } +} + +void EQWParser::SetHTTPRequest(const char *pkg, HTTPRequest *it) { + char namebuf[64]; + + snprintf(namebuf, 64, "package %s;", pkg); + eval_pv(namebuf, FALSE); + + snprintf(namebuf, 64, "request"); +// snprintf(namebuf, 64, "%s::EQW", pkg); + SV *l = get_sv(namebuf, true); + if(it != NULL) { + sv_setref_pv(l, "HTTPRequest", it); + } else { + //clear out the value, mainly to get rid of blessedness + sv_setsv(l, _empty_sv); + } + +} +/* +$editors = array(); +$editors["merchant"] = new MerchantEditor(); +#... for other editors + +if(defined($editors[$editor])) { + $edit = $editors[$editor]; + $edit->dispatch($action); +} + +class MerchantEditor extends BaseEditor { + MerchantEditor() { + $this->RegisterAction(0, "get_merchantlist", "merchant/merchant.tmpl.php", "no"); + $this->RegisterAction(1, "get_merchantlist", "merchant/merchant.edit.tmpl.php", "no"); + } +} + + +function dispatch() { + my $dispatcher = $this->_dispatchers[$action]; + $body = new Template($dispatcher["template"]); + my $proc = $dispatcher["proc"]; + $vars = $this->$proc(); + if($dispatcher["guestmode"] == "no") { + check_authorization(); + } + if ($vars) { + foreach ($vars as $key=>$value) { + $body->set($key, $value); + } + } +} + +*/ + + +#endif //EMBPERL diff --git a/world/EQWParser.h b/world/EQWParser.h new file mode 100644 index 000000000..777cbb700 --- /dev/null +++ b/world/EQWParser.h @@ -0,0 +1,79 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 EQWPARSER_H_ +#define EQWPARSER_H_ + + + +#ifdef EMBPERL + +#include +#include +#include +#include +#include + +#include "../common/useperl.h" + +class HTTPRequest; + +class EQWParser { +public: + EQWParser(); + ~EQWParser(); + + void EQW_eval(const char *pkg, const char *code); + void SetHTTPRequest(const char *pkg, HTTPRequest *it); + + //put an integer into a perl varable + void seti(const char *varname, int val) const { + SV *t = get_sv(varname, true); + sv_setiv(t, val); + } + //put a real into a perl varable + void setd(const char *varname, float val) const { + SV *t = get_sv(varname, true); + sv_setnv(t, val); + } + //put a string into a perl varable + void setstr(const char *varname, const char *val) const { + SV *t = get_sv(varname, true); + sv_setpv(t, val); + } + +protected: + void DoInit(); + bool eval(const char * code, std::string &error); + bool dosub(const char * subname, const std::vector &args, std::string &error, int mode = G_SCALAR|G_DISCARD|G_EVAL); + bool eval_file(const char * packagename, const char * filename, std::string &error); + + //the embedded interpreter + PerlInterpreter * my_perl; + SV *_empty_sv; +}; +#endif //EMBPERL + + + + + + + + + +#endif /*EQWPARSER_H_*/ diff --git a/world/HTTPRequest.cpp b/world/HTTPRequest.cpp new file mode 100644 index 000000000..b68dab7da --- /dev/null +++ b/world/HTTPRequest.cpp @@ -0,0 +1,93 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "HTTPRequest.h" +#include "EQWHTTPHandler.h" +#include "../common/EQDB.h" +#include "../common/SocketLib/HttpdForm.h" +#include + +using namespace std; + +HTTPRequest::HTTPRequest(EQWHTTPHandler *h, HttpdForm *form) +: m_handler(h) +{ + string name, value; + if(form->getfirst(name, value)) { + m_values[name] = value; + while(form->getnext(name, value)) + m_values[name] = value; + } +} + +const char *HTTPRequest::getEscaped(const char *name, const char *default_value) const { + return(EQDB::Singleton()->escape_string(get(name, default_value))); +} + +const char *HTTPRequest::get(const char *name, const char *default_value) const { + std::map::const_iterator res; + res = m_values.find(name); + if(res == m_values.end()) + return(default_value); + return(res->second.c_str()); +} + +map HTTPRequest::get_all() const { + return m_values; +} + +int HTTPRequest::getInt(const char *name, int default_value) const { + std::map::const_iterator res; + res = m_values.find(name); + if(res == m_values.end()) + return(default_value); + return(atoi(res->second.c_str())); +} + +float HTTPRequest::getFloat(const char *name, float default_value) const { + std::map::const_iterator res; + res = m_values.find(name); + if(res == m_values.end()) + return(default_value); + return(atof(res->second.c_str())); +} + +void HTTPRequest::header(Const_char *name, Const_char *value) { + m_handler->AddResponseHeader(name, value); +} + +void HTTPRequest::SetResponseCode(Const_char *code) { + m_handler->SetResponseCode(code); +} + +void HTTPRequest::redirect(Const_char *URL) { + header("Location", URL); + SetResponseCode("302"); +} + + + + + + + + + + + + diff --git a/world/HTTPRequest.h b/world/HTTPRequest.h new file mode 100644 index 000000000..41a7af383 --- /dev/null +++ b/world/HTTPRequest.h @@ -0,0 +1,64 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 HTTPREQUEST_H_ +#define HTTPREQUEST_H_ + +#include "../common/types.h" +#include +#include + +//this object acts as a friendlier interface to the HttpdForm object (perl exportable) +//which does more effecient lookups + + +class HttpdForm; +class EQWHTTPHandler; + +using namespace std; + +class HTTPRequest { +public: + HTTPRequest(EQWHTTPHandler *h, HttpdForm *form); + +//BEGIN PERL EXPORT + + + Const_char * get(Const_char *name, Const_char *default_value = "") const; + int getInt(Const_char *name, int default_value = 0) const; + float getFloat(Const_char *name, float default_value = 0.0) const; + + //returns a database-safe string + Const_char * getEscaped(Const_char *name, Const_char *default_value = "") const; + + map get_all() const; + + void redirect(Const_char *URL); + void SetResponseCode(Const_char *code); + void header(Const_char *name, Const_char *value); +//END PERL EXPORT + +protected: + EQWHTTPHandler *const m_handler; + std::map m_values; +}; + + +#endif /*HTTPREQUEST_H_*/ + + + diff --git a/world/LauncherLink.cpp b/world/LauncherLink.cpp new file mode 100644 index 000000000..f89d1bbb7 --- /dev/null +++ b/world/LauncherLink.cpp @@ -0,0 +1,368 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "LauncherLink.h" +#include "LauncherList.h" +#include "WorldConfig.h" +#include "../common/logsys.h" +#include "../common/md5.h" +#include "../common/packet_dump.h" +#include "../common/servertalk.h" +#include "../common/EmuTCPConnection.h" +#include "worlddb.h" +#include "EQLConfig.h" + +#include +#include +using namespace std; + +extern LauncherList launcher_list; + + +LauncherLink::LauncherLink(int id, EmuTCPConnection *c) +: ID(id), + tcpc(c), + authenticated(false), + m_name(""), + m_bootTimer(2000) +{ + m_dynamicCount = 0; + m_bootTimer.Disable(); +} + +LauncherLink::~LauncherLink() { + tcpc->Free(); +} + +bool LauncherLink::Process() { + if (!tcpc->Connected()) + return false; + + if(m_bootTimer.Check(false)) { + //force a boot on any zone which isnt running. + std::map::iterator cur, end; + cur = m_states.begin(); + end = m_states.end(); + for(; cur != end; cur++) { + if(!cur->second.up) { + StartZone(cur->first.c_str()); + } + } + m_bootTimer.Disable(); + } + + ServerPacket *pack = 0; + while((pack = tcpc->PopPacket())) { + _hex(WORLD__ZONE_TRACE,pack->pBuffer,pack->size); + if (!authenticated) { + if (WorldConfig::get()->SharedKey.length() > 0) { + if (pack->opcode == ServerOP_ZAAuth && pack->size == 16) { + uint8 tmppass[16]; + MD5::Generate((const uchar*) WorldConfig::get()->SharedKey.c_str(), WorldConfig::get()->SharedKey.length(), tmppass); + if (memcmp(pack->pBuffer, tmppass, 16) == 0) + authenticated = true; + else { + struct in_addr in; + in.s_addr = GetIP(); + _log(WORLD__LAUNCH_ERR, "Launcher authorization failed."); + ServerPacket* pack = new ServerPacket(ServerOP_ZAAuthFailed); + SendPacket(pack); + delete pack; + Disconnect(); + return false; + } + } + else { + struct in_addr in; + in.s_addr = GetIP(); + _log(WORLD__LAUNCH_ERR, "Launcher authorization failed."); + ServerPacket* pack = new ServerPacket(ServerOP_ZAAuthFailed); + SendPacket(pack); + delete pack; + Disconnect(); + return false; + } + } + else + { + _log(WORLD__LAUNCH,"**WARNING** You have not configured a world shared key in your config file. You should add a STRING element to your element to prevent unauthroized zone access."); + authenticated = true; + } + delete pack; + continue; + } + switch(pack->opcode) { + case 0: + break; + case ServerOP_KeepAlive: { + // ignore this + break; + } + case ServerOP_ZAAuth: { + _log(WORLD__LAUNCH, "Got authentication from %s when they are already authenticated.", m_name.c_str()); + break; + } + case ServerOP_LauncherConnectInfo: { + const LauncherConnectInfo *it = (const LauncherConnectInfo *) pack->pBuffer; + if(HasName()) { + _log(WORLD__LAUNCH_ERR, "Launcher '%s' received an additional connect packet with name '%s'. Ignoring.", m_name.c_str(), it->name); + break; + } + m_name = it->name; + + EQLConfig *config = launcher_list.GetConfig(m_name.c_str()); + if(config == NULL) { + _log(WORLD__LAUNCH, "Unknown launcher '%s' connected. Disconnecting.", it->name); + Disconnect(); + break; + } + + _log(WORLD__LAUNCH, "Launcher Identified itself as '%s'. Loading zone list.", it->name); + + std::vector result; + //database.GetLauncherZones(it->name, result); + config->GetZones(result); + + std::vector::iterator cur, end; + cur = result.begin(); + end = result.end(); + ZoneState zs; + for(; cur != end; cur++) { + zs.port = cur->port; + zs.up = false; + zs.starts = 0; + _log(WORLD__LAUNCH_TRACE, "%s: Loaded zone '%s' on port %d", m_name.c_str(), cur->name.c_str(), zs.port); + m_states[cur->name] = zs; + } + + //now we add all the dynamics. + BootDynamics(config->GetDynamicCount()); + + m_bootTimer.Start(); + + break; + } + case ServerOP_LauncherZoneStatus: { + const LauncherZoneStatus *it = (const LauncherZoneStatus *) pack->pBuffer; + std::map::iterator res; + res = m_states.find(it->short_name); + if(res == m_states.end()) { + _log(WORLD__LAUNCH_ERR, "%s: reported state for zone %s which it does not have.", m_name.c_str(), it->short_name); + break; + } + _log(WORLD__LAUNCH, "%s: %s reported state %s (%d starts)", m_name.c_str(), it->short_name, it->running?"STARTED":"STOPPED", it->start_count); + res->second.up = it->running; + res->second.starts = it->start_count; + break; + } + default: + { + _log(WORLD__LAUNCH_ERR, "Unknown ServerOPcode from launcher 0x%04x, size %d",pack->opcode,pack->size); + DumpPacket(pack->pBuffer, pack->size); + break; + } + } + + delete pack; + } + return(true); +} + +bool LauncherLink::ContainsZone(const char *short_name) const { + return(m_states.find(short_name) != m_states.end()); + + /* + * std::map::const_iterator cur, end; + cur = m_states.begin(); + end = m_states.end(); + for(; cur != end; cur++) { + if( + }*/ +} + +void LauncherLink::BootZone(const char *short_name, uint16 port) { + ZoneState zs; + zs.port = port; + zs.up = false; + zs.starts = 0; + _log(WORLD__LAUNCH_TRACE, "%s: Loaded zone '%s' on port %d", m_name.c_str(), short_name, zs.port); + m_states[short_name] = zs; + + StartZone(short_name); +} + +void LauncherLink::StartZone(const char *short_name) { + ServerPacket* pack = new ServerPacket(ServerOP_LauncherZoneRequest, sizeof(LauncherZoneRequest)); + LauncherZoneRequest* s = (LauncherZoneRequest *) pack->pBuffer; + + strn0cpy(s->short_name, short_name, 32); + s->command = ZR_Start; + + SendPacket(pack); + delete pack; +} + +void LauncherLink::RestartZone(const char *short_name) { + ServerPacket* pack = new ServerPacket(ServerOP_LauncherZoneRequest, sizeof(LauncherZoneRequest)); + LauncherZoneRequest* s = (LauncherZoneRequest *) pack->pBuffer; + + strn0cpy(s->short_name, short_name, 32); + s->command = ZR_Restart; + + SendPacket(pack); + delete pack; +} + +void LauncherLink::StopZone(const char *short_name) { + ServerPacket* pack = new ServerPacket(ServerOP_LauncherZoneRequest, sizeof(LauncherZoneRequest)); + LauncherZoneRequest* s = (LauncherZoneRequest *) pack->pBuffer; + + strn0cpy(s->short_name, short_name, 32); + s->command = ZR_Stop; + + SendPacket(pack); + delete pack; +} + +void LauncherLink::BootDynamics(uint8 new_count) { + if(m_dynamicCount == new_count) + return; + + ZoneState zs; + if(m_dynamicCount < new_count) { + //we are booting more dynamics. + + zs.port = 0; + zs.up = false; + zs.starts = 0; + + int r; + char nbuf[20]; + uint8 index; + //"for each zone we need to boot" + for(r = m_dynamicCount; r < new_count; r++) { + //find an idle ID + for(index = m_dynamicCount+1; index < 255; index++) { + sprintf(nbuf, "dynamic_%02d", index); + if(m_states.find(nbuf) != m_states.end()) + continue; + m_states[nbuf] = zs; + StartZone(nbuf); + break; + } + } + m_dynamicCount = new_count; + } else if(new_count == 0) { + //kill all zones... + std::map::iterator cur, end; + cur = m_states.begin(); + end = m_states.end(); + for(; cur != end; cur++) { + StopZone(cur->first.c_str()); + } + } else { + //need to get rid of some zones... + + //quick and dirty way to do this.. should do better (like looking for idle zones) + int found = 0; + std::map::iterator cur, end; + cur = m_states.begin(); + end = m_states.end(); + for(; cur != end; cur++) { + if(cur->first.find("dynamic_") == 0) { + if(found >= new_count) { + //this zone exceeds the number of allowed booted zones. + StopZone(cur->first.c_str()); + } else { + found++; + } + } + } + + m_dynamicCount = new_count; + } + +} + + +void LauncherLink::GetZoneList(std::vector &l) { + std::map::iterator cur, end; + cur = m_states.begin(); + end = m_states.end(); + for(; cur != end; cur++) { + l.push_back(cur->first.c_str()); + } +} + +void LauncherLink::GetZoneDetails(const char *short_name, std::map &res) { + res.clear(); + + std::map::iterator r; + r = m_states.find(short_name); + if(r == m_states.end()) { + res["error"] = "Zone Not Found"; + res["name"] = short_name; + res["up"] = "0"; + res["starts"] = "0"; + res["port"] = "0"; + } else { + res["name"] = r->first; + res["up"] = r->second.up?"1":"0"; + res["starts"] = itoa(r->second.starts); + res["port"] = itoa(r->second.port); + } +} + +void LauncherLink::Shutdown() { + ServerPacket* pack = new ServerPacket(ServerOP_ShutdownAll); + SendPacket(pack); + delete pack; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/world/LauncherLink.h b/world/LauncherLink.h new file mode 100644 index 000000000..04749b95d --- /dev/null +++ b/world/LauncherLink.h @@ -0,0 +1,81 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 LAUNCHERLINK_H_ +#define LAUNCHERLINK_H_ + +#include "../common/EmuTCPConnection.h" +#include "../common/timer.h" +#include +#include +#include + +class ServerPacket; + +class LauncherLink { +public: + LauncherLink(int id, EmuTCPConnection *tcpc); + ~LauncherLink(); + + bool Process(); + bool SendPacket(ServerPacket* pack) { return tcpc->SendPacket(pack); } +// bool SendPacket(TCPConnection::TCPNetPacket_Struct* tnps) { return tcpc->SendPacket(tnps); } + + int GetID() const { return(ID); } + void Disconnect() { tcpc->Disconnect(); } + + inline bool HasName() const { return(m_name.length() > 0); } + inline uint32 GetIP() const { return tcpc->GetrIP(); } + inline uint16 GetPort() const { return tcpc->GetrPort(); } + inline const char * GetName() const { return(m_name.c_str()); } + inline int CountZones() const { return(m_states.size()); } + + bool ContainsZone(const char *short_name) const; + + //commands + void Shutdown(); + void BootZone(const char *short_name, uint16 port); + void StartZone(const char *short_name); + void RestartZone(const char *short_name); + void StopZone(const char *short_name); + void BootDynamics(uint8 new_total); + + void GetZoneList(std::vector &list); + void GetZoneDetails(const char *short_name, std::map &result); + +protected: + const int ID; + EmuTCPConnection*const tcpc; + bool authenticated; + std::string m_name; + Timer m_bootTimer; + + uint8 m_dynamicCount; + + typedef struct { + bool up; + uint32 starts; //number of times this zone has started + uint16 port; //the port this zone wants to use (0=pick one) + } ZoneState; + std::map m_states; +}; + + + + + +#endif /*LAUNCHERLINK_H_*/ diff --git a/world/LauncherList.cpp b/world/LauncherList.cpp new file mode 100644 index 000000000..79f513946 --- /dev/null +++ b/world/LauncherList.cpp @@ -0,0 +1,222 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "LauncherList.h" +#include "LauncherLink.h" +#include "../common/logsys.h" +#include "EQLConfig.h" + +using namespace std; + +LauncherList::LauncherList() +: nextID(1) +{ +} + +LauncherList::~LauncherList() { + vector::iterator cur, end; + cur = m_pendingLaunchers.begin(); + end = m_pendingLaunchers.end(); + for(; cur != end; cur++) { + delete *cur; + } + + map::iterator curc, endc; + curc = m_configs.begin(); + endc = m_configs.end(); + for(; curc != endc; curc++) { + delete curc->second; + } + + map::iterator curl, endl; + curl = m_launchers.begin(); + endl = m_launchers.end(); + for(; curl != endl; curl++) { + delete curl->second; + } +} + +void LauncherList::Process() { + //process pending launchers.. + vector::iterator cur, end; + cur = m_pendingLaunchers.begin(); + while(cur != m_pendingLaunchers.end()) { + LauncherLink *l = *cur; +//printf("ProcP %d: %p\n", l->GetID(), l); + if(!l->Process()) { + //launcher has died before it identified itself. + _log(WORLD__LAUNCH, "Removing pending launcher %d", l->GetID()); + cur = m_pendingLaunchers.erase(cur); + delete l; + } else if(l->HasName()) { + //launcher has identified itself now. + //remove ourself from the pending list + cur = m_pendingLaunchers.erase(cur); + string name = l->GetName(); + //kill off anybody else using our name. + map::iterator res; + res = m_launchers.find(name); + if(res != m_launchers.end()) { + _log(WORLD__LAUNCH, "Ghosting launcher %s", name.c_str()); + delete res->second; + } + _log(WORLD__LAUNCH, "Removing pending launcher %d. Adding %s to active list.", l->GetID(), name.c_str()); + //put the launcher in the list. + m_launchers[name] = l; + } else { + cur++; + } + } + + //process active launchers. + map::iterator curl, tmp; + curl = m_launchers.begin(); + while(curl != m_launchers.end()) { + LauncherLink *l = curl->second; +//printf("Proc %s(%d): %p\n", l->GetName(), l->GetID(), l); + if(!l->Process()) { + //launcher has died before it identified itself. + _log(WORLD__LAUNCH, "Removing launcher %s (%d)", l->GetName(), l->GetID()); + tmp = curl; + curl++; + m_launchers.erase(tmp); + delete l; + } else { + curl++; + } + } +} + +LauncherLink *LauncherList::Get(const char *name) { + map::iterator res; + res = m_launchers.find(name); + if(res == m_launchers.end()) + return(NULL); + return(res->second); +/* string goal(name); + + vector::iterator cur, end; + cur = m_launchers.begin(); + end = m_launchers.end(); + for(; cur != end; cur++) { + if(goal == (*cur)->GetName()) + return(*cur); + } + return(NULL);*/ +} + +LauncherLink *LauncherList::FindByZone(const char *short_name) { + map::iterator cur, end; + cur = m_launchers.begin(); + end = m_launchers.end(); + for(; cur != end; cur++) { + if(cur->second->ContainsZone(short_name)) + return(cur->second); + } + return(NULL); +} + +void LauncherList::Add(EmuTCPConnection *conn) { + LauncherLink *it = new LauncherLink(nextID++, conn); + _log(WORLD__LAUNCH, "Adding pending launcher %d", it->GetID()); + m_pendingLaunchers.push_back(it); +} + + +int LauncherList::GetLauncherCount() { + return(m_launchers.size()); +} + +void LauncherList::GetLauncherNameList(std::vector &res) { + map::iterator cur, end; + cur = m_configs.begin(); + end = m_configs.end(); + for(; cur != end; cur++) { + res.push_back(cur->first); + } +} + +void LauncherList::LoadList() { + vector launchers; + + database.GetLauncherList(launchers); + + vector::iterator cur, end; + cur = launchers.begin(); + end = launchers.end(); + for(; cur != end; cur++) { + m_configs[*cur] = new EQLConfig(cur->c_str()); + } +} + +EQLConfig *LauncherList::GetConfig(const char *name) { + map::iterator res; + res = m_configs.find(name); + if(res == m_configs.end()) { + return(NULL); + } + return(res->second); +} + +void LauncherList::CreateLauncher(const char *name, uint8 dynamic_count) { + m_configs[name] = EQLConfig::CreateLauncher(name, dynamic_count); +} + +void LauncherList::Remove(const char *name) { + map::iterator resc; + resc = m_configs.find(name); + if(resc != m_configs.end()) { + delete resc->second; + m_configs.erase(resc); + } + + map::iterator resl; + resl = m_launchers.find(name); + if(resl != m_launchers.end()) { + resl->second->Disconnect(); + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/world/LauncherList.h b/world/LauncherList.h new file mode 100644 index 000000000..61e95555d --- /dev/null +++ b/world/LauncherList.h @@ -0,0 +1,67 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 LAUNCHERLIST_H_ +#define LAUNCHERLIST_H_ + +#include "../common/types.h" +#include +#include +#include + +class LauncherLink; +class EmuTCPConnection; +class EQLConfig; + +class LauncherList { +public: + LauncherList(); + ~LauncherList(); + + void Process(); + + void LoadList(); + EQLConfig *GetConfig(const char *name); + void CreateLauncher(const char *name, uint8 dynamic_count); + void Remove(const char *name); + + void Add(EmuTCPConnection *conn); + LauncherLink *Get(const char *name); + LauncherLink *FindByZone(const char *short_name); + + int GetLauncherCount(); + void GetLauncherNameList(std::vector &list); + +protected: + std::map m_configs; //we own these objects + std::map m_launchers; //we own these objects +// std::map m_configs; //we own these objects + std::vector m_pendingLaunchers; //we own these objects, have not yet identified themself + int nextID; +}; + + + + + + + + + + + +#endif /*LAUNCHERLIST_H_*/ diff --git a/world/LoginServer.cpp b/world/LoginServer.cpp new file mode 100644 index 000000000..6d8e61752 --- /dev/null +++ b/world/LoginServer.cpp @@ -0,0 +1,339 @@ +/* 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 "../common/debug.h" +#include +using namespace std; +#include +#include +#include +using namespace std; +#include +#include "../common/version.h" + +#ifdef _WINDOWS + #include + #include + #include + + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#else // Pyro: fix for linux + #include +#ifdef FREEBSD //Timothy Whitman - January 7, 2003 + #include +#endif + #include + #include + #include + #include + #include + + #include "../common/unix.h" + + #define SOCKET_ERROR -1 + #define INVALID_SOCKET -1 + extern int errno; +#endif + +#define IGNORE_LS_FATAL_ERROR + +#include "../common/servertalk.h" +#include "LoginServer.h" +#include "LoginServerList.h" +#include "../common/eq_packet_structs.h" +#include "../common/packet_dump.h" +#include "../common/MiscFunctions.h" +#include "zoneserver.h" +#include "worlddb.h" +#include "zonelist.h" +#include "clientlist.h" +#include "WorldConfig.h" + +extern ZSList zoneserver_list; +extern ClientList client_list; +extern uint32 numzones; +extern uint32 numplayers; +extern volatile bool RunLoops; + +LoginServer::LoginServer(const char* iAddress, uint16 iPort, const char* Account, const char* Password) +: statusupdate_timer(LoginServer_StatusUpdateInterval) +{ + strn0cpy(LoginServerAddress,iAddress,256); + LoginServerPort = iPort; + strn0cpy(LoginAccount,Account,31); + strn0cpy(LoginPassword,Password,31); + CanAccountUpdate = false; + tcpc = new EmuTCPConnection(true); + tcpc->SetPacketMode(EmuTCPConnection::packetModeLogin); +} + +LoginServer::~LoginServer() { + delete tcpc; +} + +bool LoginServer::Process() { + const WorldConfig *Config=WorldConfig::get(); + + if (statusupdate_timer.Check()) { + this->SendStatus(); + } + + /************ Get all packets from packet manager out queue and process them ************/ + ServerPacket *pack = 0; + while((pack = tcpc->PopPacket())) + { + _log(WORLD__LS_TRACE,"Recevied ServerPacket from LS OpCode 0x04x",pack->opcode); + _hex(WORLD__LS_TRACE,pack->pBuffer,pack->size); + + switch(pack->opcode) { + case 0: + break; + case ServerOP_KeepAlive: { + // ignore this + break; + } + case ServerOP_UsertoWorldReq: { + UsertoWorldRequest_Struct* utwr = (UsertoWorldRequest_Struct*) pack->pBuffer; + uint32 id = database.GetAccountIDFromLSID(utwr->lsaccountid); + int16 status = database.CheckStatus(id); + + ServerPacket* outpack = new ServerPacket; + outpack->opcode = ServerOP_UsertoWorldResp; + outpack->size = sizeof(UsertoWorldResponse_Struct); + outpack->pBuffer = new uchar[outpack->size]; + memset(outpack->pBuffer, 0, outpack->size); + UsertoWorldResponse_Struct* utwrs = (UsertoWorldResponse_Struct*) outpack->pBuffer; + utwrs->lsaccountid = utwr->lsaccountid; + utwrs->ToID = utwr->FromID; + + if(Config->Locked == true) + { + if((status == 0 || status < 100) && (status != -2 || status != -1)) + utwrs->response = 0; + if(status >= 100) + utwrs->response = 1; + } + else { + utwrs->response = 1; + } + + int32 x = Config->MaxClients; + if( (int32)numplayers >= x && x != -1 && x != 255 && status < 80) + utwrs->response = -3; + + if(status == -1) + utwrs->response = -1; + if(status == -2) + utwrs->response = -2; + + utwrs->worldid = utwr->worldid; + SendPacket(outpack); + delete outpack; + break; + } + case ServerOP_LSClientAuth: { + ServerLSClientAuth* slsca = (ServerLSClientAuth*) pack->pBuffer; + + if (RuleI(World, AccountSessionLimit) >= 0) { + // Enforce the limit on the number of characters on the same account that can be + // online at the same time. + client_list.EnforceSessionLimit(slsca->lsaccount_id); + } + + client_list.CLEAdd(slsca->lsaccount_id, slsca->name, slsca->key, slsca->worldadmin, slsca->ip, slsca->local); + break; + } + case ServerOP_LSFatalError: { +#ifndef IGNORE_LS_FATAL_ERROR + WorldConfig::DisableLoginserver(); + _log(WORLD__LS_ERR, "Login server responded with FatalError. Disabling reconnect."); +#else + _log(WORLD__LS_ERR, "Login server responded with FatalError."); +#endif + if (pack->size > 1) { + _log(WORLD__LS_ERR, " %s",pack->pBuffer); + } + break; + } + case ServerOP_SystemwideMessage: { + ServerSystemwideMessage* swm = (ServerSystemwideMessage*) pack->pBuffer; + zoneserver_list.SendEmoteMessageRaw(0, 0, 0, swm->type, swm->message); + break; + } + case ServerOP_LSRemoteAddr: { + if (!Config->WorldAddress.length()) { + WorldConfig::SetWorldAddress((char *)pack->pBuffer); + _log(WORLD__LS, "Loginserver provided %s as world address",pack->pBuffer); + } + break; + } + case ServerOP_LSAccountUpdate: { + _log(WORLD__LS, "Received ServerOP_LSAccountUpdate packet from loginserver"); + CanAccountUpdate = true; + break; + } + default: + { + _log(WORLD__LS_ERR, "Unknown LSOpCode: 0x%04x size=%d",(int)pack->opcode,pack->size); +DumpPacket(pack->pBuffer, pack->size); + break; + } + } + + delete pack; + } + + return true; +} + +bool LoginServer::InitLoginServer() { + if(Connected() == false) { + if(ConnectReady()) { + _log(WORLD__LS, "Connecting to login server: %s:%d",LoginServerAddress,LoginServerPort); + Connect(); + } else { + _log(WORLD__LS_ERR, "Not connected but not ready to connect, this is bad: %s:%d", + LoginServerAddress,LoginServerPort); + } + } + return true; +} + +bool LoginServer::Connect() { + char tmp[25]; + if(database.GetVariable("loginType",tmp,sizeof(tmp)) && strcasecmp(tmp,"MinILogin") == 0){ + minilogin = true; + _log(WORLD__LS, "Setting World to MiniLogin Server type"); + } + else + minilogin = false; + + if (minilogin && WorldConfig::get()->WorldAddress.length()==0) { + _log(WORLD__LS_ERR, "**** For minilogin to work, you need to set the
element in the section."); + return false; + } + + char errbuf[TCPConnection_ErrorBufferSize]; + if ((LoginServerIP = ResolveIP(LoginServerAddress, errbuf)) == 0) { + _log(WORLD__LS_ERR, "Unable to resolve '%s' to an IP.",LoginServerAddress); + return false; + } + + if (LoginServerIP == 0 || LoginServerPort == 0) { + _log(WORLD__LS_ERR, "Connect info incomplete, cannot connect: %s:%d",LoginServerAddress,LoginServerPort); + return false; + } + + if (tcpc->ConnectIP(LoginServerIP, LoginServerPort, errbuf)) { + _log(WORLD__LS, "Connected to Loginserver: %s:%d",LoginServerAddress,LoginServerPort); + if (minilogin) + SendInfo(); + else + SendNewInfo(); + SendStatus(); + zoneserver_list.SendLSZones(); + return true; + } + else { + _log(WORLD__LS_ERR, "Could not connect to login server: %s:%d %s",LoginServerAddress,LoginServerPort,errbuf); + return false; + } +} +void LoginServer::SendInfo() { + const WorldConfig *Config=WorldConfig::get(); + + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_LSInfo; + pack->size = sizeof(ServerLSInfo_Struct); + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer, 0, pack->size); + ServerLSInfo_Struct* lsi = (ServerLSInfo_Struct*) pack->pBuffer; + strcpy(lsi->protocolversion, EQEMU_PROTOCOL_VERSION); + strcpy(lsi->serverversion, CURRENT_VERSION); + strcpy(lsi->name, Config->LongName.c_str()); + strcpy(lsi->account, LoginAccount); + strcpy(lsi->password, LoginPassword); + strcpy(lsi->address, Config->WorldAddress.c_str()); + SendPacket(pack); + delete pack; +} + +void LoginServer::SendNewInfo() { + uint16 port; + const WorldConfig *Config=WorldConfig::get(); + + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_NewLSInfo; + pack->size = sizeof(ServerNewLSInfo_Struct); + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer, 0, pack->size); + ServerNewLSInfo_Struct* lsi = (ServerNewLSInfo_Struct*) pack->pBuffer; + strcpy(lsi->protocolversion, EQEMU_PROTOCOL_VERSION); + strcpy(lsi->serverversion, CURRENT_VERSION); + strcpy(lsi->name, Config->LongName.c_str()); + strcpy(lsi->shortname, Config->ShortName.c_str()); + strcpy(lsi->account, LoginAccount); + strcpy(lsi->password, LoginPassword); + if (Config->WorldAddress.length()) + strcpy(lsi->remote_address, Config->WorldAddress.c_str()); + if (Config->LocalAddress.length()) + strcpy(lsi->local_address, Config->LocalAddress.c_str()); + else { + tcpc->GetSockName(lsi->local_address,&port); + WorldConfig::SetLocalAddress(lsi->local_address); + } + SendPacket(pack); + delete pack; +} + +void LoginServer::SendStatus() { + statusupdate_timer.Start(); + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_LSStatus; + pack->size = sizeof(ServerLSStatus_Struct); + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer, 0, pack->size); + ServerLSStatus_Struct* lss = (ServerLSStatus_Struct*) pack->pBuffer; + + if (WorldConfig::get()->Locked) + lss->status = -2; + else if (numzones <= 0) + lss->status = -2; + else + lss->status = numplayers; + + lss->num_zones = numzones; + lss->num_players = numplayers; + SendPacket(pack); + delete pack; +} + +void LoginServer::SendAccountUpdate(ServerPacket* pack) { + ServerLSAccountUpdate_Struct* s = (ServerLSAccountUpdate_Struct *) pack->pBuffer; + if(CanUpdate()) { + _log(WORLD__LS, "Sending ServerOP_LSAccountUpdate packet to loginserver: %s:%d",LoginServerAddress,LoginServerPort); + strn0cpy(s->worldaccount, LoginAccount, 30); + strn0cpy(s->worldpassword, LoginPassword, 30); + SendPacket(pack); + } +} + diff --git a/world/LoginServer.h b/world/LoginServer.h new file mode 100644 index 000000000..807874cb9 --- /dev/null +++ b/world/LoginServer.h @@ -0,0 +1,62 @@ +/* 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 LOGINSERVER_H +#define LOGINSERVER_H + +#include "../common/servertalk.h" +#include "../common/linked_list.h" +#include "../common/timer.h" +#include "../common/queue.h" +#include "../common/eq_packet_structs.h" +#include "../common/Mutex.h" +#include "../common/EmuTCPConnection.h" + +class LoginServer{ +public: + LoginServer(const char*, uint16, const char*, const char*); + ~LoginServer(); + + bool InitLoginServer(); + + bool Process(); + bool Connect(); + + void SendInfo(); + void SendNewInfo(); + void SendStatus(); + + void SendPacket(ServerPacket* pack) { tcpc->SendPacket(pack); } + void SendAccountUpdate(ServerPacket* pack); + bool ConnectReady() { return tcpc->ConnectReady(); } + bool Connected() { return tcpc->Connected(); } + bool MiniLogin() { return minilogin; } + bool CanUpdate() { return CanAccountUpdate; } + +private: + bool minilogin; + EmuTCPConnection* tcpc; + char LoginServerAddress[256]; + uint32 LoginServerIP; + uint16 LoginServerPort; + char LoginAccount[32]; + char LoginPassword[32]; + bool CanAccountUpdate; + + Timer statusupdate_timer; +}; +#endif diff --git a/world/LoginServerList.cpp b/world/LoginServerList.cpp new file mode 100644 index 000000000..1b133a597 --- /dev/null +++ b/world/LoginServerList.cpp @@ -0,0 +1,197 @@ +/* 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 "../common/debug.h" +#include +using namespace std; +#include +#include +#include +using namespace std; +#include +#include "../common/version.h" + +#define IGNORE_LS_FATAL_ERROR + +#include "../common/servertalk.h" +#include "LoginServer.h" +#include "LoginServerList.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" + +extern ZSList zoneserver_list; +extern LoginServerList loginserverlist; +extern ClientList client_list; +extern uint32 numzones; +extern uint32 numplayers; +extern volatile bool RunLoops; + +LoginServerList::LoginServerList() { +} + +LoginServerList::~LoginServerList() { +} + +void LoginServerList::Add(const char* iAddress, uint16 iPort, const char* Account, const char* Password) +{ + LoginServer* loginserver = new LoginServer(iAddress, iPort, Account, Password); + list.Insert(loginserver); +} + +bool LoginServerList::Process() { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()){ + iterator.GetData()->Process(); + iterator.Advance(); + } + return true; +} + +#ifdef _WINDOWS + void AutoInitLoginServer(void *tmp) { +#else + void *AutoInitLoginServer(void *tmp) { +#endif + loginserverlist.InitLoginServer(); +#ifndef WIN32 + return 0; +#endif +} + +void LoginServerList::InitLoginServer() { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()){ + iterator.GetData()->InitLoginServer(); + iterator.Advance(); + } +} + +bool LoginServerList::SendInfo() { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()){ + iterator.GetData()->SendInfo(); + iterator.Advance(); + } + return true; +} + +bool LoginServerList::SendNewInfo() { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()){ + iterator.GetData()->SendNewInfo(); + iterator.Advance(); + } + return true; +} + +bool LoginServerList::SendStatus() { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()){ + iterator.GetData()->SendStatus(); + iterator.Advance(); + } + return true; +} + +bool LoginServerList::SendPacket(ServerPacket* pack) { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()){ + iterator.GetData()->SendPacket(pack); + iterator.Advance(); + } + return true; +} + +bool LoginServerList::SendAccountUpdate(ServerPacket* pack) { + LinkedListIterator iterator(list); + + _log(WORLD__LS, "Requested to send ServerOP_LSAccountUpdate packet to all loginservers"); + iterator.Reset(); + while(iterator.MoreElements()){ + if(iterator.GetData()->CanUpdate()) { + iterator.GetData()->SendAccountUpdate(pack); + } + iterator.Advance(); + } + return true; +} + +bool LoginServerList::Connected() { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()){ + if(iterator.GetData()->Connected()) + return true; + iterator.Advance(); + } + return false; +} + +bool LoginServerList::AllConnected() { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()){ + if(iterator.GetData()->Connected() == false) + return false; + iterator.Advance(); + } + return true; +} + +bool LoginServerList::MiniLogin() { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()){ + if(iterator.GetData()->MiniLogin()) + return true; + iterator.Advance(); + } + return false; +} + +bool LoginServerList::CanUpdate() { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()){ + if(iterator.GetData()->CanUpdate()) + return true; + iterator.Advance(); + } + return false; +} + diff --git a/world/LoginServerList.h b/world/LoginServerList.h new file mode 100644 index 000000000..f6af8a8a2 --- /dev/null +++ b/world/LoginServerList.h @@ -0,0 +1,48 @@ +#ifndef LOGINSERVERLIST_H_ +#define LOGINSERVERLIST_H_ + +#include "../common/servertalk.h" +#include "../common/linked_list.h" +#include "../common/timer.h" +#include "../common/queue.h" +#include "../common/eq_packet_structs.h" +#include "../common/Mutex.h" +#include "../common/EmuTCPConnection.h" + +#ifdef _WINDOWS + void AutoInitLoginServer(void *tmp); +#else + void *AutoInitLoginServer(void *tmp); +#endif + +class LoginServer; + +class LoginServerList{ +public: + LoginServerList(); + ~LoginServerList(); + + void Add(const char*, uint16, const char*, const char*); + void InitLoginServer(); + + bool Process(); + + bool SendInfo(); + bool SendNewInfo(); + bool SendStatus(); + + bool SendPacket(ServerPacket *pack); + bool SendAccountUpdate(ServerPacket *pack); + bool Connected(); + bool AllConnected(); + bool MiniLogin(); + bool CanUpdate(); + +protected: + LinkedList list; +}; + + + + +#endif /*LOGINSERVERLIST_H_*/ diff --git a/world/SoFCharCreateData.h b/world/SoFCharCreateData.h new file mode 100644 index 000000000..7f8deafdd --- /dev/null +++ b/world/SoFCharCreateData.h @@ -0,0 +1,31 @@ +#ifndef SOFCHARCREATEDATA_H +#define SOFCHARCREATEDATA_H + +#pragma pack(1) + +struct RaceClassAllocation { + unsigned int Index; + unsigned int BaseStats[7]; + unsigned int DefaultPointAllocation[7]; +}; + +struct RaceClassCombos { + unsigned int ExpansionRequired; + unsigned int Race; + unsigned int Class; + unsigned int Deity; + unsigned int AllocationIndex; + unsigned int Zone; +}; + +/*struct SoFCCData { + unsigned char Unknown; + unsigned int RaceClassStatEntryCount; + SoFCCRaceClassData RCData[109]; + unsigned int Unknown2; + SoFCCStartZoneData StartZoneData[641]; +}; +*/ +#pragma pack() + +#endif diff --git a/world/WorldConfig.cpp b/world/WorldConfig.cpp new file mode 100644 index 000000000..571084328 --- /dev/null +++ b/world/WorldConfig.cpp @@ -0,0 +1,54 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "WorldConfig.h" + +WorldConfig *WorldConfig::_world_config = NULL; + + +string WorldConfig::GetByName(const string &var_name) const { + if(var_name == "UpdateStats") + return(UpdateStats?"true":"false"); + if(var_name == "LoginDisabled") + return(LoginDisabled?"true":"false"); + return(EQEmuConfig::GetByName(var_name)); +} + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/world/WorldConfig.h b/world/WorldConfig.h new file mode 100644 index 000000000..6b97ccb18 --- /dev/null +++ b/world/WorldConfig.h @@ -0,0 +1,74 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 __WorldConfig_H +#define __WorldConfig_H + +#include "../common/EQEmuConfig.h" + +class WorldConfig : public EQEmuConfig { +public: + virtual string GetByName(const string &var_name) const; + + bool UpdateStats; + bool LoginDisabled; + +private: + + static WorldConfig *_world_config; + + WorldConfig() : EQEmuConfig() { + LoginDisabled=false; + UpdateStats=true; + } + +public: + + // Produce a const singleton + static const WorldConfig *get() { + if (_world_config == NULL) + LoadConfig(); + return(_world_config); + } + + // Load the config + static bool LoadConfig() { + if (_world_config != NULL) + delete _world_config; + _world_config=new WorldConfig; + _config=_world_config; + + return _config->ParseFile(EQEmuConfig::ConfigFile.c_str(),"server"); + } + + // Accessors for the static private object + static void LockWorld() { if (_world_config) _world_config->Locked=true; } + static void UnlockWorld() { if (_world_config) _world_config->Locked=false; } + + static void DisableStats() { if (_world_config) _world_config->UpdateStats=false; } + static void EnableStats() { if (_world_config) _world_config->UpdateStats=true; } + + static void DisableLoginserver() { if (_world_config) _world_config->LoginDisabled=true; } + static void EnableLoginserver() { if (_world_config) _world_config->LoginDisabled=false; } + + static void SetWorldAddress(string addr) { if (_world_config) _world_config->WorldAddress=addr; } + static void SetLocalAddress(string addr) { if (_world_config) _world_config->LocalAddress=addr; } + + void Dump() const; +}; + +#endif diff --git a/world/WorldTCPConnection.h b/world/WorldTCPConnection.h new file mode 100644 index 000000000..d01207814 --- /dev/null +++ b/world/WorldTCPConnection.h @@ -0,0 +1,35 @@ +/* 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 WorldTCPCONNECTION_H +#define WorldTCPCONNECTION_H + +#include "../common/types.h" + +class WorldTCPConnection +{ +public: + WorldTCPConnection() { } + virtual ~WorldTCPConnection() { } + virtual void SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...) { } + virtual void SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message) { } + + virtual inline bool IsConsole() { return false; } + virtual inline bool IsZoneServer() { return false; } +}; + +#endif diff --git a/world/client.cpp b/world/client.cpp new file mode 100644 index 000000000..fa83ecf98 --- /dev/null +++ b/world/client.cpp @@ -0,0 +1,2021 @@ +#include "../common/debug.h" +#include "../common/EQPacket.h" +#include "../common/EQStreamIntf.h" +#include "../common/misc.h" +#include +using namespace std; +#include +using namespace std; +#include +#include +#include +#include +#include + +//FatherNitwit: uncomment to enable my IP based authentication hack +//#define IPBASED_AUTH_HACK + +// Disgrace: for windows compile +#ifdef _WINDOWS + #include + #include + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#else + #include +#ifdef FREEBSD //Timothy Whitman - January 7, 2003 + #include +#endif + #include + #include + #include +#endif + + +#include "client.h" +#include "../common/emu_opcodes.h" +#include "../common/eq_packet_structs.h" +#include "../common/packet_dump.h" +#include "../common/EQStreamIntf.h" +#include "worlddb.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/MiscFunctions.h" +#include "WorldConfig.h" +#include "LoginServer.h" +#include "LoginServerList.h" +#include "zoneserver.h" +#include "zonelist.h" +#include "clientlist.h" +#include "wguild_mgr.h" +#include "../common/rulesys.h" +#include "SoFCharCreateData.h" + +std::vector character_create_allocations; +std::vector character_create_race_class_combos; + +extern ZSList zoneserver_list; +extern LoginServerList loginserverlist; +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)), + connect(1000), + eqs(ieqs) +{ + // Live does not send datarate as of 3/11/2005 + //eqs->SetDataRate(7); + ip = eqs->GetRemoteIP(); + port = ntohs(eqs->GetRemotePort()); + + autobootup_timeout.Disable(); + connect.Disable(); + seencharsel = false; + cle = 0; + zoneID = 0; + char_name[0] = 0; + charid = 0; + pwaitingforbootup = 0; + StartInTutorial = false; + ClientVersionBit = 0; + numclients++; + + string StreamDescription = eqs->Describe(); + + if(StreamDescription == "Patch Titanium") + { + ClientVersionBit = BIT_Titanium; + } + else if(StreamDescription == "Patch 6.2") + { + ClientVersionBit = BIT_Client62; + } + else if(StreamDescription == "Patch SoF") + { + ClientVersionBit = BIT_SoF; + } + else if(StreamDescription == "Patch SoD") + { + ClientVersionBit = BIT_SoD; + } + else if(StreamDescription == "Patch Underfoot") + { + ClientVersionBit = BIT_Underfoot; + } + else if(StreamDescription == "Patch RoF") + { + ClientVersionBit = BIT_RoF; + } +} + +Client::~Client() { + if (RunLoops && cle && zoneID == 0) + cle->SetOnline(CLE_Status_Offline); + + numclients--; + + //let the stream factory know were done with this stream + eqs->Close(); + eqs->ReleaseFromUse(); +} + +void Client::SendLogServer() +{ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogServer, sizeof(LogServer_Struct)); + LogServer_Struct *l=(LogServer_Struct *)outapp->pBuffer; + const char *wsn=WorldConfig::get()->ShortName.c_str(); + memcpy(l->worldshortname,wsn,strlen(wsn)); + + if(RuleB(Mail, EnableMailSystem)) + l->enablemail = 1; + + if(RuleB(Chat, EnableVoiceMacros)) + l->enablevoicemacros = 1; + + l->enable_pvp = (RuleI(World, PVPSettings)); + + if(RuleB(World, IsGMPetitionWindowEnabled)) + l->enable_petition_wnd = 1; + + if(RuleI(World, FVNoDropFlag) == 1 || RuleI(World, FVNoDropFlag) == 2 && GetAdmin() > RuleI(Character, MinStatusForNoDropExemptions)) + l->enable_FV = 1; + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendEnterWorld(string name) +{ +char char_name[32]= { 0 }; + if (pZoning && database.GetLiveChar(GetAccountID(), char_name)) { + if(database.GetAccountIDByChar(char_name) != GetAccountID()) { + eqs->Close(); + return; + } else { + clog(WORLD__CLIENT,"Telling client to continue session."); + } + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_EnterWorld, strlen(char_name)+1); + memcpy(outapp->pBuffer,char_name,strlen(char_name)+1); + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendExpansionInfo() { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ExpansionInfo, sizeof(ExpansionInfo_Struct)); + ExpansionInfo_Struct *eis = (ExpansionInfo_Struct*)outapp->pBuffer; + eis->Expansions = (RuleI(World, ExpansionSettings)); + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendCharInfo() { + if (cle) { + cle->SetOnline(CLE_Status_CharSelect); + } + + if (ClientVersionBit & BIT_RoFAndLater) + { + // Can make max char per account into a rule - New to VoA + SendMaxCharCreate(10); + SendMembership(); + SendMembershipSettings(); + } + + seencharsel = true; + + + // Send OP_SendCharInfo + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct)); + CharacterSelect_Struct* cs = (CharacterSelect_Struct*)outapp->pBuffer; + + database.GetCharSelectInfo(GetAccountID(), cs); + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendMaxCharCreate(int max_chars) { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendMaxCharacters, sizeof(MaxCharacters_Struct)); + MaxCharacters_Struct* mc = (MaxCharacters_Struct*)outapp->pBuffer; + + mc->max_chars = max_chars; + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendMembership() { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendMembership, sizeof(Membership_Struct)); + Membership_Struct* mc = (Membership_Struct*)outapp->pBuffer; + + /* + The remaining entry fields probably hold more membership restriction data that needs to be identified. + Here is a possible list based on the EQ Website membership comparison list: + 1. Spell Ranks Allowed + 2. Prestige Items Usable + 3. In-Game Mail Service (send/recieve) + 4. Parcel Delivery (send/recieve) + 5. Loyalty Rewards Per-Week (30, 60, Max) + 6. Mercenary Tiers (Apprentice 1-2, Apprentice All Tiers, All Tiers) + 7. Neighborhood House + 8. Active Journal Quests (Tasks?) (10, 15, Max) + 9. Guild Function (join, join and create) + 10. Broker System (restricted, unlimited) + 11. Voice Chat + 12. Chat Ability + 13. Progression Server + 14. Customer Support + 15. In-Game Popup Advertising + That is 15 possible fields, and there are 15 unknowns in the struct...Coincidence? + */ + + mc->membership = 2; //Hardcode to gold for now. We don't use anything else. + mc->races = 0x1ffff; // Available Races (4110 for silver) + mc->classes = 0x1ffff; // Available Classes (4614 for silver) - Was 0x101ffff + mc->entrysize = 21; // Number of membership setting entries below + mc->entries[0] = 0xffffffff; // Max AA Restriction + mc->entries[1] = 0xffffffff; // Max Level Restriction + mc->entries[2] = 0xffffffff; // Max Char Slots per Account (not used by client?) + mc->entries[3] = 0xffffffff; // 1 for Silver + mc->entries[4] = 8; // Main Inventory Size (0xffffffff on Live for Gold, but limitting to 8 until 10 is supported) + mc->entries[5] = 0xffffffff; // Max Platinum per level + mc->entries[6] = 1; // 0 for Silver + mc->entries[7] = 1; // 0 for Silver + mc->entries[8] = 1; // 1 for Silver + mc->entries[9] = 0xffffffff; // Unknown - Maybe Loyalty Points every 12 hours? 60 per week for Silver + mc->entries[10] = 1; // 1 for Silver + mc->entries[11] = 0xffffffff; // Shared Bank Slots + mc->entries[12] = 0xffffffff; // Unknown - Maybe Max Active Tasks? + mc->entries[13] = 1; // 1 for Silver + mc->entries[14] = 1; // 0 for Silver + mc->entries[15] = 1; // 0 for Silver + mc->entries[16] = 1; // 1 for Silver + mc->entries[17] = 1; // 0 for Silver + mc->entries[18] = 1; // 0 for Silver + mc->entries[19] = 0xffffffff; // 0 for Silver + mc->entries[20] = 0xffffffff; // 0 for Silver + mc->exit_url_length = 0; + //mc->exit_url = 0; // Used on Live: "http://www.everquest.com/free-to-play/exit-silver" + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendMembershipSettings() { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendMembershipDetails, sizeof(Membership_Details_Struct)); + Membership_Details_Struct* mds = (Membership_Details_Struct*)outapp->pBuffer; + + mds->membership_setting_count = 66; + uint32 gold_settings[22] = {-1,-1,-1,-1,-1,-1,1,1,1,-1,1,-1,-1,1,1,1,1,1,1,-1,-1,0}; + uint32 entry_count = 0; + for (int setting_id=0; setting_id < 22; setting_id++) + { + for (int setting_index=0; setting_index < 3; setting_index++) + { + + mds->settings[entry_count].setting_index = setting_index; + mds->settings[entry_count].setting_id = setting_id; + mds->settings[entry_count].setting_value = gold_settings[setting_id]; + entry_count++; + } + } + + mds->race_entry_count = 15; + mds->class_entry_count = 15; + + uint32 cur_purchase_id = 90287; + uint32 cur_purchase_id2 = 90301; + uint32 cur_bitwise_value = 1; + for (int entry_id=0; entry_id < 15; entry_id++) + { + if (entry_id == 0) + { + mds->membership_races[entry_id].purchase_id = 1; + mds->membership_races[entry_id].bitwise_entry = 0x1ffff; + mds->membership_classes[entry_id].purchase_id = 1; + mds->membership_classes[entry_id].bitwise_entry = 0x1ffff; + } + else + { + mds->membership_races[entry_id].purchase_id = cur_purchase_id; + + if (entry_id < 3) + { + mds->membership_classes[entry_id].purchase_id = cur_purchase_id; + } + else + { + mds->membership_classes[entry_id].purchase_id = cur_purchase_id2; + cur_purchase_id2++; + } + + if (entry_id == 1) + { + mds->membership_races[entry_id].bitwise_entry = 4110; + mds->membership_classes[entry_id].bitwise_entry = 4614; + } + else if (entry_id == 2) + { + mds->membership_races[entry_id].bitwise_entry = 4110; + mds->membership_classes[entry_id].bitwise_entry = 4614; + } + else + { + if (entry_id == 12) + { + // Live Skips 4096 + cur_bitwise_value *= 2; + } + mds->membership_races[entry_id].bitwise_entry = cur_bitwise_value; + mds->membership_classes[entry_id].bitwise_entry = cur_bitwise_value; + } + cur_purchase_id++; + } + cur_bitwise_value *= 2; + } + mds->exit_url_length = 0; // Live uses 42 + //strcpy(eq->exit_url, "http://www.everquest.com/free-to-play/exit"); + mds->exit_url_length2 = 0; // Live uses 49 + //strcpy(eq->exit_url2, "http://www.everquest.com/free-to-play/exit-silver"); + + /* + Account Access Level Settings + + ID - Free Silver Gold - Possible Setting + 00 - 250 1000 -1 - Max AA Restriction + 01 - -1 -1 -1 - Max Level Restriction + 02 - 2 4 -1 - Max Char Slots per Account + 03 - 1 1 -1 - Max Spell Rank + 04 - 4 6 -1 - Main Inventory Size + 05 - 100 500 -1 - Max Platinum per level + 06 - 0 0 1 - Send Mail? + 07 - 0 0 1 - Send Parcels? + 08 - 1 1 1 - Voice Chat Unlimited? + 09 - 2 5 -1 - Mercenary Tiers + 10 - 0 1 1 - Create Guilds? + 11 - 0 0 -1 - Shared Bank Slots + 12 - 9 14 -1 - Max Journal Quests - 1 + 13 - 0 1 1 - Neighborhood-House Allowed? + 14 - 0 0 1 - Prestige Enabled? + 15 - 0 0 1 - Broker System Unlimited? + 16 - 0 1 1 - Chat UnRestricted? + 17 - 0 0 1 - Progression Server Access? + 18 - 0 0 1 - Full Customer Support? + 19 - 0 0 -1 - 0 for Silver + 20 - 0 0 -1 - 0 for Silver + 21 - 0 0 0 - Unknown 0 + */ + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendPostEnterWorld() { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_PostEnterWorld, 1); + outapp->size=0; + QueuePacket(outapp); + safe_delete(outapp); +} + +bool Client::HandlePacket(const EQApplicationPacket *app) { + const WorldConfig *Config=WorldConfig::get(); + EmuOpcode opcode = app->GetOpcode(); + + clog(WORLD__CLIENT_TRACE,"Recevied EQApplicationPacket"); + _pkt(WORLD__CLIENT_TRACE,app); + + bool ret = true; + + if (!eqs->CheckState(ESTABLISHED)) { + clog(WORLD__CLIENT,"Client disconnected (net inactive on send)"); + return false; + } + + // Voidd: Anti-GM Account hack, Checks source ip against valid GM Account IP Addresses + if (RuleB(World, GMAccountIPList) && this->GetAdmin() >= (RuleI(World, MinGMAntiHackStatus))) { + if(!database.CheckGMIPs(long2ip(this->GetIP()).c_str(), this->GetAccountID())) { + clog(WORLD__CLIENT,"GM Account not permited from source address %s and accountid %i", long2ip(this->GetIP()).c_str(), this->GetAccountID()); + eqs->Close(); + } + } + + if (GetAccountID() == 0 && opcode != OP_SendLoginInfo) { + // Got a packet other than OP_SendLoginInfo when not logged in + clog(WORLD__CLIENT_ERR,"Expecting OP_SendLoginInfo, got %s", OpcodeNames[opcode]); + return false; + } + else if (opcode == OP_AckPacket) { + return true; + } + + switch(opcode) + { + case OP_CrashDump: + break; + case OP_SendLoginInfo: + { + if (app->size != sizeof(LoginInfo_Struct)) { + ret = false; + break; + } + + LoginInfo_Struct *li=(LoginInfo_Struct *)app->pBuffer; + + // Quagmire - max len for name is 18, pass 15 + char name[19] = {0}; + char password[16] = {0}; + strn0cpy(name, (char*)li->login_info,18); + strn0cpy(password, (char*)&(li->login_info[strlen(name)+1]), 15); + + if (strlen(password) <= 1) { + // TODO: Find out how to tell the client wrong username/password + clog(WORLD__CLIENT_ERR,"Login without a password"); + ret = false; + break; + } + + pZoning=(li->zoning==1); + +#ifdef IPBASED_AUTH_HACK + struct in_addr tmpip; + tmpip.s_addr = ip; +#endif + uint32 id=0; + bool minilogin = loginserverlist.MiniLogin(); + if(minilogin){ + struct in_addr miniip; + miniip.s_addr = ip; + id = database.GetMiniLoginAccount(inet_ntoa(miniip)); + } + else if(strncasecmp(name, "LS#", 3) == 0) + id=atoi(&name[3]); + else + id=atoi(name); +#ifdef IPBASED_AUTH_HACK + if ((cle = zoneserver_list.CheckAuth(inet_ntoa(tmpip), password))) +#else + if (loginserverlist.Connected() == false && !pZoning) { + clog(WORLD__CLIENT_ERR,"Error: Login server login while not connected to login server."); + ret = false; + break; + } + if ((minilogin && (cle = client_list.CheckAuth(id,password,ip))) || (cle = client_list.CheckAuth(id, password))) +#endif + { + if (cle->AccountID() == 0 || (!minilogin && cle->LSID()==0)) { + clog(WORLD__CLIENT_ERR,"ID is 0. Is this server connected to minilogin?"); + if(!minilogin) + clog(WORLD__CLIENT_ERR,"If so you forget the minilogin variable..."); + else + clog(WORLD__CLIENT_ERR,"Could not find a minilogin account, verify ip address logging into minilogin is the same that is in your account table."); + ret = false; + break; + } + + cle->SetOnline(); + + clog(WORLD__CLIENT,"Logged in. Mode=%s",pZoning ? "(Zoning)" : "(CharSel)"); + + if(minilogin){ + WorldConfig::DisableStats(); + clog(WORLD__CLIENT,"MiniLogin Account #%d",cle->AccountID()); + } + else { + clog(WORLD__CLIENT,"LS Account #%d",cle->LSID()); + } + if(Config->UpdateStats){ + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_LSPlayerJoinWorld; + pack->size = sizeof(ServerLSPlayerJoinWorld_Struct); + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer,0,pack->size); + ServerLSPlayerJoinWorld_Struct* join =(ServerLSPlayerJoinWorld_Struct*)pack->pBuffer; + strcpy(join->key,GetLSKey()); + join->lsaccount_id = GetLSID(); + loginserverlist.SendPacket(pack); + safe_delete(pack); + } + + if (!pZoning) + SendGuildList(); + SendLogServer(); + SendApproveWorld(); + SendEnterWorld(cle->name()); + SendPostEnterWorld(); + if (!pZoning) { + SendExpansionInfo(); + SendCharInfo(); + database.LoginIP(cle->AccountID(), long2ip(GetIP()).c_str()); + } + + } + else { + // TODO: Find out how to tell the client wrong username/password + clog(WORLD__CLIENT_ERR,"Bad/Expired session key '%s'",name); + ret = false; + break; + } + + if (!cle) + break; + cle->SetIP(GetIP()); + break; + } + case OP_ApproveName: //Name approval + { + if (GetAccountID() == 0) { + clog(WORLD__CLIENT_ERR,"Name approval request with no logged in account"); + ret = false; + break; + } + snprintf(char_name, 64, "%s", (char*)app->pBuffer); + 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)); + + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket; + outapp->SetOpcode(OP_ApproveName); + outapp->pBuffer = new uchar[1]; + outapp->size = 1; + bool valid; + 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; + } + else if (database.ReserveName(GetAccountID(), char_name)) { + valid = true; + } + else { + valid = false; + } + outapp->pBuffer[0] = valid? 1 : 0; + QueuePacket(outapp); + safe_delete(outapp); + + if(!valid) { + memset(char_name, 0, sizeof(char_name)); + } + + break; + } + case OP_RandomNameGenerator: + { + // creates up to a 10 char name + char vowels[18]="aeiouyaeiouaeioe"; + char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd"; + char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr"; + int rndnum=MakeRandomInt(0, 75),n=1; + bool dlc=false; + 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" + rndname[0]=paircons[rndnum]; + rndname[1]=paircons[rndnum+1]; + n=2; + } + else if (rndnum>16) + { + rndnum-=17; + rndname[0]=cons[rndnum]; + } + else + { + rndname[0]=vowels[rndnum]; + vwl=true; + } + int namlen=MakeRandomInt(5, 10); + 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" + rndnum=MakeRandomInt(0, 7)*2; + } + else + { // 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 + i+=1; + } + else + { // select a single cons + rndname[i]=cons[rndnum]; + } + } + else + { // 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 + { + rndname[i+1]=rndname[i]; + dbl=true; + i+=1; + } + } + } + + rndname[0]=toupper(rndname[0]); + NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer; + memset(ngs->name,0,64); + strcpy(ngs->name,rndname); + +// char names[8][64] = { "How", "About", "You", "Think", "Of", "Your", "Own", "Name" }; +// //Could have parts of the random name in this struct and they compile together +// NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer; +// strncpy(ngs->name,"Notcreated",64); + + QueuePacket(app); + break; + + } + case OP_CharacterCreateRequest: { + // New OpCode in SoF + uint32 allocs = character_create_allocations.size(); + uint32 combos = character_create_race_class_combos.size(); + uint32 len = sizeof(RaceClassAllocation) * allocs; + len += sizeof(RaceClassCombos) * combos; + len += sizeof(uint8); + len += sizeof(uint32); + len += sizeof(uint32); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_CharacterCreateRequest, len); + unsigned char *ptr = outapp->pBuffer; + *((uint8*)ptr) = 0; + ptr += sizeof(uint8); + + *((uint32*)ptr) = allocs; + ptr += sizeof(uint32); + + for(int i = 0; i < allocs; ++i) { + RaceClassAllocation *alc = (RaceClassAllocation*)ptr; + + alc->Index = character_create_allocations[i].Index; + for(int j = 0; j < 7; ++j) { + alc->BaseStats[j] = character_create_allocations[i].BaseStats[j]; + alc->DefaultPointAllocation[j] = character_create_allocations[i].DefaultPointAllocation[j]; + } + ptr += sizeof(RaceClassAllocation); + } + + *((uint32*)ptr) = combos; + ptr += sizeof(uint32); + for(int i = 0; i < combos; ++i) { + RaceClassCombos *cmb = (RaceClassCombos*)ptr; + cmb->ExpansionRequired = character_create_race_class_combos[i].ExpansionRequired; + cmb->Race = character_create_race_class_combos[i].Race; + cmb->Class = character_create_race_class_combos[i].Class; + cmb->Deity = character_create_race_class_combos[i].Deity; + cmb->AllocationIndex = character_create_race_class_combos[i].AllocationIndex; + cmb->Zone = character_create_race_class_combos[i].Zone; + ptr += sizeof(RaceClassCombos); + } + + QueuePacket(outapp); + safe_delete(outapp); + break; + } + + case OP_CharacterCreate: //Char create + { + if (GetAccountID() == 0) + { + clog(WORLD__CLIENT_ERR,"Account ID not set; unable to create character."); + ret = false; + break; + } + 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); + break; + } + + CharCreate_Struct *cc = (CharCreate_Struct*)app->pBuffer; + if(OPCharCreate(char_name, cc) == false) + { + database.DeleteCharacter(char_name); + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApproveName, 1); + outapp->pBuffer[0] = 0; + QueuePacket(outapp); + safe_delete(outapp); + } + else + SendCharInfo(); + break; + } + case OP_EnterWorld: // Enter world + { + if (GetAccountID() == 0) { + clog(WORLD__CLIENT_ERR,"Enter world with no logged in account"); + eqs->Close(); + break; + } + if(GetAdmin() < 0) + { + clog(WORLD__CLIENT,"Account banned or suspended."); + eqs->Close(); + break; + } + + if (RuleI(World, MaxClientsPerIP) >= 0) { + client_list.GetCLEIP(this->GetIP()); //Check current CLE Entry IPs against incoming connection + } + + EnterWorld_Struct *ew=(EnterWorld_Struct *)app->pBuffer; + strn0cpy(char_name, ew->name, 64); + + EQApplicationPacket *outapp; + uint32 tmpaccid = 0; + charid = database.GetCharacterInfo(char_name, &tmpaccid, &zoneID, &instanceID); + if (charid == 0 || tmpaccid != GetAccountID()) { + clog(WORLD__CLIENT_ERR,"Could not get CharInfo for '%s'",char_name); + eqs->Close(); + break; + } + + // Make sure this account owns this character + if (tmpaccid != GetAccountID()) { + clog(WORLD__CLIENT_ERR,"This account does not own the character named '%s'",char_name); + eqs->Close(); + break; + } + + if(!pZoning && ew->return_home) + { + CharacterSelect_Struct* cs = new CharacterSelect_Struct; + memset(cs, 0, sizeof(CharacterSelect_Struct)); + database.GetCharSelectInfo(GetAccountID(), cs); + bool home_enabled = false; + + for(int x = 0; x < 10; ++x) + { + if(strcasecmp(cs->name[x], char_name) == 0) + { + if(cs->gohome[x] == 1) + { + home_enabled = true; + break; + } + } + } + safe_delete(cs); + + if(home_enabled) + { + zoneID = database.MoveCharacterToBind(charid,4); + } + 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(); + break; + } + } + + if(!pZoning && (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial))) { + CharacterSelect_Struct* cs = new CharacterSelect_Struct; + memset(cs, 0, sizeof(CharacterSelect_Struct)); + database.GetCharSelectInfo(GetAccountID(), cs); + bool tutorial_enabled = false; + + for(int x = 0; x < 10; ++x) + { + if(strcasecmp(cs->name[x], char_name) == 0) + { + if(cs->tutorial[x] == 1) + { + tutorial_enabled = true; + break; + } + } + } + safe_delete(cs); + + if(tutorial_enabled) + { + zoneID = RuleI(World, TutorialZoneID); + database.MoveCharacterToZone(charid, database.GetZoneName(zoneID)); + } + else + { + clog(WORLD__CLIENT_ERR,"'%s' is trying to go to tutorial but are not allowed...",char_name); + database.SetHackerFlag(GetAccountName(), char_name, "MQTutorial: player tried to enter the tutorial without having tutorial enabled for this character."); + eqs->Close(); + break; + } + } + + if (zoneID == 0 || !database.GetZoneName(zoneID)) { + // This is to save people in an invalid zone, once it's removed from the DB + database.MoveCharacterToZone(charid, "arena"); + clog(WORLD__CLIENT_ERR, "Zone not found in database zone_id=%i, moveing char to arena character:%s", zoneID, char_name); + } + + if(instanceID > 0) + { + if(!database.VerifyInstanceAlive(instanceID, GetCharID())) + { + zoneID = database.MoveCharacterToBind(charid); + instanceID = 0; + } + else + { + if(!database.VerifyZoneInstance(zoneID, instanceID)) + { + zoneID = database.MoveCharacterToBind(charid); + instanceID = 0; + } + } + } + + if(!pZoning) { + database.SetGroupID(char_name, 0, charid); + 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){ + 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); + QueuePacket(outapp3); + safe_delete(outapp3); + } + } + } + + outapp = new EQApplicationPacket(OP_MOTD); + char tmp[500] = {0}; + if (database.GetVariable("MOTD", tmp, 500)) { + outapp->size = strlen(tmp)+1; + outapp->pBuffer = new uchar[outapp->size]; + memset(outapp->pBuffer,0,outapp->size); + strcpy((char*)outapp->pBuffer, tmp); + + } else { + // Null Message of the Day. :) + outapp->size = 1; + outapp->pBuffer = new uchar[outapp->size]; + outapp->pBuffer[0] = 0; + } + QueuePacket(outapp); + safe_delete(outapp); + + int MailKey = MakeRandomInt(1, INT_MAX); + + database.SetMailKey(charid, GetIP(), MailKey); + + char ConnectionType; + + if(ClientVersionBit & BIT_UnderfootAndLater) + ConnectionType = 'U'; + else if(ClientVersionBit & BIT_SoFAndLater) + ConnectionType = 'S'; + else + ConnectionType = 'C'; + + EQApplicationPacket *outapp2 = new EQApplicationPacket(OP_SetChatServer); + char buffer[112]; + sprintf(buffer,"%s,%i,%s.%s,%c%08X", + Config->ChatHost.c_str(), + Config->ChatPort, + Config->ShortName.c_str(), + this->GetCharName(), ConnectionType, MailKey + ); + outapp2->size=strlen(buffer)+1; + outapp2->pBuffer = new uchar[outapp2->size]; + memcpy(outapp2->pBuffer,buffer,outapp2->size); + QueuePacket(outapp2); + safe_delete(outapp2); + + outapp2 = new EQApplicationPacket(OP_SetChatServer2); + + if(ClientVersionBit & BIT_TitaniumAndEarlier) + ConnectionType = 'M'; + + sprintf(buffer,"%s,%i,%s.%s,%c%08X", + Config->MailHost.c_str(), + Config->MailPort, + Config->ShortName.c_str(), + this->GetCharName(), ConnectionType, MailKey + ); + outapp2->size=strlen(buffer)+1; + outapp2->pBuffer = new uchar[outapp2->size]; + memcpy(outapp2->pBuffer,buffer,outapp2->size); + QueuePacket(outapp2); + safe_delete(outapp2); + + EnterWorld(); + break; + } + case OP_LoginComplete:{ + break; + } + case OP_DeleteCharacter: { + uint32 char_acct_id = database.GetAccountIDByChar((char*)app->pBuffer); + if(char_acct_id == GetAccountID()) + { + clog(WORLD__CLIENT,"Delete character: %s",app->pBuffer); + database.DeleteCharacter((char *)app->pBuffer); + SendCharInfo(); + } + break; + } + case OP_ApproveWorld: + { + break; + } + case OP_WorldClientReady:{ + break; + } + case OP_World_Client_CRC1: + case OP_World_Client_CRC2: { + // There is no obvious entry in the CC struct to indicate that the 'Start Tutorial button + // is selected when a character is created. I have observed that in this case, OP_EnterWorld is sent + // before OP_World_Client_CRC1. Therefore, if we receive OP_World_Client_CRC1 before OP_EnterWorld, + // then 'Start Tutorial' was not chosen. + StartInTutorial = false; + break; + } + case OP_WearChange: { // User has selected a different character + break; + } + case OP_WorldComplete: { + eqs->Close(); + break; + } + case OP_LoginUnknown1: + case OP_LoginUnknown2: + break; + + case OP_ZoneChange: + // HoT sends this to world while zoning and wants it echoed back. + if(ClientVersionBit & BIT_RoFAndLater) + { + QueuePacket(app); + } + break; + + + default: { + clog(WORLD__CLIENT_ERR,"Received unknown EQApplicationPacket"); + _pkt(WORLD__CLIENT_ERR,app); + break; + } + } + return ret; +} + +bool Client::Process() { + bool ret = true; + //bool sendguilds = true; + sockaddr_in to; + + memset((char *) &to, 0, sizeof(to)); + to.sin_family = AF_INET; + to.sin_port = port; + to.sin_addr.s_addr = ip; + + if (autobootup_timeout.Check()) { + clog(WORLD__CLIENT_ERR, "Zone bootup timer expired, bootup failed or too slow."); + ZoneUnavail(); + } + if(connect.Check()){ + SendGuildList();// Send OPCode: OP_GuildsList + SendApproveWorld(); + connect.Disable(); + } + if (CLE_keepalive_timer.Check()) { + if (cle) + cle->KeepAlive(); + } + + /************ Get all packets from packet manager out queue and process them ************/ + EQApplicationPacket *app = 0; + while(ret && (app = (EQApplicationPacket *)eqs->PopPacket())) { + ret = HandlePacket(app); + + delete app; + } + + if (!eqs->CheckState(ESTABLISHED)) { + if(WorldConfig::get()->UpdateStats){ + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_LSPlayerLeftWorld; + pack->size = sizeof(ServerLSPlayerLeftWorld_Struct); + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer,0,pack->size); + ServerLSPlayerLeftWorld_Struct* logout =(ServerLSPlayerLeftWorld_Struct*)pack->pBuffer; + strcpy(logout->key,GetLSKey()); + logout->lsaccount_id = GetLSID(); + loginserverlist.SendPacket(pack); + safe_delete(pack); + } + clog(WORLD__CLIENT,"Client disconnected (not active in process)"); + return false; + } + + return ret; +} + +void Client::EnterWorld(bool TryBootup) { + if (zoneID == 0) + return; + + ZoneServer* zs = NULL; + if(instanceID > 0) + { + if(database.VerifyInstanceAlive(instanceID, GetCharID())) + { + if(database.VerifyZoneInstance(zoneID, instanceID)) + { + zs = zoneserver_list.FindByInstanceID(instanceID); + } + else + { + instanceID = 0; + zs = NULL; + database.MoveCharacterToBind(GetCharID()); + ZoneUnavail(); + return; + } + } + else + { + instanceID = 0; + zs = NULL; + database.MoveCharacterToBind(GetCharID()); + ZoneUnavail(); + return; + } + } + else + zs = zoneserver_list.FindByZoneID(zoneID); + + + const char *zone_name=database.GetZoneName(zoneID, true); + if (zs) { + // warn the world we're comming, so it knows not to shutdown + zs->IncommingClient(this); + } + else { + if (TryBootup) { + clog(WORLD__CLIENT,"Attempting autobootup of %s (%d:%d)",zone_name,zoneID,instanceID); + autobootup_timeout.Start(); + pwaitingforbootup = zoneserver_list.TriggerBootup(zoneID, instanceID); + if (pwaitingforbootup == 0) { + clog(WORLD__CLIENT_ERR,"No zoneserver available to boot up."); + ZoneUnavail(); + } + return; + } + else { + clog(WORLD__CLIENT_ERR,"Requested zone %s is no running.",zone_name); + ZoneUnavail(); + return; + } + } + pwaitingforbootup = 0; + + cle->SetChar(charid, char_name); + database.UpdateLiveChar(char_name, GetAccountID()); + clog(WORLD__CLIENT,"%s %s (%d:%d)",seencharsel ? "Entering zone" : "Zoning to",zone_name,zoneID,instanceID); +// database.SetAuthentication(account_id, char_name, zone_name, ip); + + if (seencharsel) { + if (GetAdmin() < 80 && zoneserver_list.IsZoneLocked(zoneID)) { + clog(WORLD__CLIENT_ERR,"Enter world failed. Zone is locked."); + ZoneUnavail(); + return; + } + + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_AcceptWorldEntrance; + pack->size = sizeof(WorldToZone_Struct); + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer, 0, pack->size); + WorldToZone_Struct* wtz = (WorldToZone_Struct*) pack->pBuffer; + wtz->account_id = GetAccountID(); + wtz->response = 0; + zs->SendPacket(pack); + delete pack; + } + else { // if they havent seen character select screen, we can assume this is a zone + // to zone movement, which should be preauthorized before they leave the previous zone + Clearance(1); + } +} + +void Client::Clearance(int8 response) +{ + ZoneServer* zs = NULL; + if(instanceID > 0) + { + zs = zoneserver_list.FindByInstanceID(instanceID); + } + else + { + zs = zoneserver_list.FindByZoneID(zoneID); + } + + if(zs == 0 || response == -1 || response == 0) + { + if (zs == 0) + { + clog(WORLD__CLIENT_ERR,"Unable to find zoneserver in Client::Clearance!!"); + } else { + clog(WORLD__CLIENT_ERR, "Invalid response %d in Client::Clearance", response); + } + + ZoneUnavail(); + return; + } + + EQApplicationPacket* outapp; + + if (zs->GetCAddress() == NULL) { + clog(WORLD__CLIENT_ERR, "Unable to do zs->GetCAddress() in Client::Clearance!!"); + ZoneUnavail(); + return; + } + + if (zoneID == 0) { + clog(WORLD__CLIENT_ERR, "zoneID is NULL in Client::Clearance!!"); + ZoneUnavail(); + return; + } + + const char* zonename = database.GetZoneName(zoneID); + if (zonename == 0) { + clog(WORLD__CLIENT_ERR, "zonename is NULL in Client::Clearance!!"); + ZoneUnavail(); + return; + } + + // @bp This is the chat server + /* + char packetData[] = "64.37.148.34.9876,MyServer,Testchar,23cd2c95"; + outapp = new EQApplicationPacket(OP_0x0282, sizeof(packetData)); + strcpy((char*)outapp->pBuffer, packetData); + QueuePacket(outapp); + delete outapp; + */ + + // Send zone server IP data + outapp = new EQApplicationPacket(OP_ZoneServerInfo, sizeof(ZoneServerInfo_Struct)); + ZoneServerInfo_Struct* zsi = (ZoneServerInfo_Struct*)outapp->pBuffer; + const char *zs_addr=zs->GetCAddress(); + if (!zs_addr[0]) { + if (cle->IsLocalClient()) { + struct in_addr in; + in.s_addr = zs->GetIP(); + zs_addr=inet_ntoa(in); + if (!strcmp(zs_addr,"127.0.0.1")) + zs_addr=WorldConfig::get()->LocalAddress.c_str(); + } else { + zs_addr=WorldConfig::get()->WorldAddress.c_str(); + } + } + strcpy(zsi->ip, zs_addr); + zsi->port =zs->GetCPort(); + clog(WORLD__CLIENT,"Sending client to zone %s (%d:%d) at %s:%d",zonename,zoneID,instanceID,zsi->ip,zsi->port); + QueuePacket(outapp); + safe_delete(outapp); + + if (cle) + cle->SetOnline(CLE_Status_Zoning); +} + +void Client::ZoneUnavail() { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZoneUnavail, sizeof(ZoneUnavail_Struct)); + ZoneUnavail_Struct* ua = (ZoneUnavail_Struct*)outapp->pBuffer; + const char* zonename = database.GetZoneName(zoneID); + if (zonename) + strcpy(ua->zonename, zonename); + QueuePacket(outapp); + delete outapp; + + zoneID = 0; + pwaitingforbootup = 0; + autobootup_timeout.Disable(); +} + +bool Client::GenPassKey(char* key) { + char* passKey=NULL; + *passKey += ((char)('A'+((int)MakeRandomInt(0, 25)))); + *passKey += ((char)('A'+((int)MakeRandomInt(0, 25)))); + memcpy(key, passKey, strlen(passKey)); + return true; +} + +void Client::QueuePacket(const EQApplicationPacket* app, bool ack_req) { + clog(WORLD__CLIENT_TRACE, "Sending EQApplicationPacket OpCode 0x%04x",app->GetOpcode()); + _pkt(WORLD__CLIENT_TRACE, app); + + ack_req = true; // It's broke right now, dont delete this line till fix it. =P + eqs->QueuePacket(app, ack_req); +} + +void Client::SendGuildList() { + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_GuildsList); + + //ask the guild manager to build us a nice guild list packet + outapp->pBuffer = guild_mgr.MakeGuildList("", outapp->size); + if(outapp->pBuffer == NULL) { + clog(GUILDS__ERROR, "Unable to make guild list!"); + return; + } + + clog(GUILDS__OUT_PACKETS, "Sending OP_GuildsList of length %d", outapp->size); +// _pkt(GUILDS__OUT_PACKET_TRACE, outapp); + + eqs->FastQueuePacket((EQApplicationPacket **)&outapp); +} + +// @merth: I have no idea what this struct is for, so it's hardcoded for now +void Client::SendApproveWorld() +{ + EQApplicationPacket* outapp; + + // Send OPCode: OP_ApproveWorld, size: 544 + outapp = new EQApplicationPacket(OP_ApproveWorld, sizeof(ApproveWorld_Struct)); + ApproveWorld_Struct* aw = (ApproveWorld_Struct*)outapp->pBuffer; + uchar foo[] = { +//0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x95,0x5E,0x30,0xA5,0xCA,0xD4,0xEA,0xF5, +//0xCB,0x14,0xFC,0xF7,0x78,0xE2,0x73,0x15,0x90,0x17,0xCE,0x7A,0xEB,0xEC,0x3C,0x34, +//0x5C,0x6D,0x10,0x05,0xFC,0xEA,0xED,0x19,0xC5,0x0D,0x7A,0x82,0x17,0xCC,0xCC,0x71, +//0x56,0x38,0xDF,0x78,0x8D,0xE6,0x44,0xD3,0x6F,0xDB,0xE3,0xCF,0x21,0x30,0x75,0x2F, +//0xCD,0xDC,0xE9,0xB4,0xA4,0x4E,0x58,0xDE,0xEE,0x54,0xDD,0x87,0xDA,0xE9,0xC6,0xC8, +//0x02,0xDD,0xC4,0xFD,0x94,0x36,0x32,0xAD,0x1B,0x39,0x0F,0x00,0x00,0x00,0x00,0x00, + +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x37,0x87,0x13,0xbe,0xc8,0xa7,0x77,0xcb, +0x27,0xed,0xe1,0xe6,0x5d,0x1c,0xaa,0xd3,0x3c,0x26,0x3b,0x6d,0x8c,0xdb,0x36,0x8d, +0x91,0x72,0xf5,0xbb,0xe0,0x5c,0x50,0x6f,0x09,0x6d,0xc9,0x1e,0xe7,0x2e,0xf4,0x38, +0x1b,0x5e,0xa8,0xc2,0xfe,0xb4,0x18,0x4a,0xf7,0x72,0x85,0x13,0xf5,0x63,0x6c,0x16, +0x69,0xf4,0xe0,0x17,0xff,0x87,0x11,0xf3,0x2b,0xb7,0x73,0x04,0x37,0xca,0xd5,0x77, +0xf8,0x03,0x20,0x0a,0x56,0x8b,0xfb,0x35,0xff,0x59,0x00,0x00,0x00,0x00,0x00,0x00, + +//0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0x42,0x69,0x2a,0x87,0xdd,0x04,0x3d, +//0x7f,0xb1,0xb3,0xbb,0xde,0xd5,0x5f,0xfc,0x1f,0xb3,0x25,0x94,0x16,0xd5,0xf3,0x97, +//0x43,0xdf,0xb9,0x69,0x68,0xdf,0x2b,0x64,0x98,0xf5,0x44,0xbe,0x38,0x65,0xef,0xff, +//0x36,0x89,0x90,0xcf,0x26,0xbb,0x9f,0x76,0xd5,0xaf,0x6d,0xf2,0x08,0xbe,0xce,0xd8, +//0x3e,0x4b,0x53,0x8a,0xf3,0x44,0x7c,0x19,0x49,0x5d,0x97,0x99,0xd8,0x8b,0xee,0x10, +//0x1a,0x7d,0xb7,0x8b,0x49,0x9b,0x40,0x8c,0xea,0x49,0x09,0x00,0x00,0x00,0x00,0x00, +// +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x15,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x53,0xC3,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00 +}; + memcpy(aw->unknown544, foo, sizeof(foo)); + QueuePacket(outapp); + safe_delete(outapp); +} + +bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) +{ + PlayerProfile_Struct pp; + ExtendedProfile_Struct ext; + Inventory inv; + time_t bday = time(NULL); + char startzone[50]={0}; + uint32 i; + struct in_addr in; + + + 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", + 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); + + // 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)) + { + 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 + 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_; + pp.gender = cc->gender; + pp.deity = cc->deity; + pp.STR = cc->STR; + pp.STA = cc->STA; + pp.AGI = cc->AGI; + pp.DEX = cc->DEX; + pp.WIS = cc->WIS; + pp.INT = cc->INT; + pp.CHA = cc->CHA; + pp.face = cc->face; + pp.eyecolor1 = cc->eyecolor1; + pp.eyecolor2 = cc->eyecolor2; + pp.hairstyle = cc->hairstyle; + pp.haircolor = cc->haircolor; + pp.beard = cc->beard; + pp.beardcolor = cc->beardcolor; + pp.drakkin_heritage = cc->drakkin_heritage; + pp.drakkin_tattoo = cc->drakkin_tattoo; + pp.drakkin_details = cc->drakkin_details; + pp.birthday = bday; + pp.lastlogin = bday; + 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 + pp.skills[SENSE_HEADING] = 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++) + pp.spell_book[i] = 0xFFFFFFFF; + + for(i = 0; i < MAX_PP_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. + 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) + 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,"Found 'startzone' variable setting: %s", startzone); + pp.zone_id = database.GetZoneID(startzone); + 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 + { + if(ClientVersionBit & BIT_TitaniumAndEarlier) + database.GetStartZone(&pp, cc); + else + database.GetStartZoneSoF(&pp, cc); + } + } + + 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 starting city location to the initial bind point + pp.binds[4] = pp.binds[0]; + + + 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 + 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)) + { + clog(WORLD__CLIENT_ERR,"Character creation failed: %s", pp.name); + return false; + } + else + { + 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; + + _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; + } + } + } + + if(!found) { + _log(WORLD__CLIENT_ERR, "Could not find class/race/deity/start_zone combination"); + return false; + } + + uint32 max_stats = 0; + uint32 allocs = character_create_allocations.size(); + RaceClassAllocation allocation; + found = false; + for(int i = 0; i < combos; ++i) { + if(character_create_allocations[i].Index == class_combo.AllocationIndex) { + allocation = character_create_allocations[i]; + found = true; + break; + } + } + + if(!found) { + _log(WORLD__CLIENT_ERR, "Could not find starting stats for selected character combo, cannot verify stats"); + return false; + } + + max_stats = allocation.DefaultPointAllocation[0] + + allocation.DefaultPointAllocation[1] + + allocation.DefaultPointAllocation[2] + + allocation.DefaultPointAllocation[3] + + allocation.DefaultPointAllocation[4] + + allocation.DefaultPointAllocation[5] + + allocation.DefaultPointAllocation[6]; + + 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]) { + _log(WORLD__CLIENT_ERR, "Dexterity out of range"); + return false; + } + + 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]) { + _log(WORLD__CLIENT_ERR, "Stamina out of range"); + return false; + } + + 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]) { + _log(WORLD__CLIENT_ERR, "Wisdom out of range"); + return false; + } + + if(cc->CHA > allocation.BaseStats[6] + max_stats || cc->CHA < allocation.BaseStats[6]) { + _log(WORLD__CLIENT_ERR, "Charisma out of range"); + return false; + } + + uint32 current_stats = 0; + current_stats += cc->STR - allocation.BaseStats[0]; + current_stats += cc->DEX - allocation.BaseStats[1]; + current_stats += cc->AGI - allocation.BaseStats[2]; + current_stats += cc->STA - allocation.BaseStats[3]; + 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) { + _log(WORLD__CLIENT_ERR, "Current Stats > Maximum Stats"); + return false; + } + + return true; +} + +bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc) +{ + uint32 bSTR, bSTA, bAGI, bDEX, bWIS, bINT, bCHA, bTOTAL, cTOTAL, stat_points; //these are all uint32 in CharCreate_Struct, so we'll make them uint32 here to make the compiler shut up + int classtemp, racetemp; + int Charerrors = 0; + + +// solar: if this is increased you'll have to add a column to the classrace +// table below +#define _TABLE_RACES 16 + + static const int BaseRace[_TABLE_RACES][7] = + { /* STR STA AGI DEX WIS INT CHR */ + { /*Human*/ 75, 75, 75, 75, 75, 75, 75}, + { /*Barbarian*/ 103, 95, 82, 70, 70, 60, 55}, + { /*Erudite*/ 60, 70, 70, 70, 83, 107, 70}, + { /*Wood Elf*/ 65, 65, 95, 80, 80, 75, 75}, + { /*High Elf*/ 55, 65, 85, 70, 95, 92, 80}, + { /*Dark Elf*/ 60, 65, 90, 75, 83, 99, 60}, + { /*Half Elf*/ 70, 70, 90, 85, 60, 75, 75}, + { /*Dwarf*/ 90, 90, 70, 90, 83, 60, 45}, + { /*Troll*/ 108, 109, 83, 75, 60, 52, 40}, + { /*Ogre*/ 130, 122, 70, 70, 67, 60, 37}, + { /*Halfling*/ 70, 75, 95, 90, 80, 67, 50}, + { /*Gnome*/ 60, 70, 85, 85, 67, 98, 60}, + { /*Iksar*/ 70, 70, 90, 85, 80, 75, 55}, + { /*Vah Shir*/ 90, 75, 90, 70, 70, 65, 65}, + { /*Froglok*/ 70, 80, 100, 100, 75, 75, 50}, + { /*Drakkin*/ 70, 80, 85, 75, 80, 85, 75} + }; + + static const int BaseClass[PLAYER_CLASS_COUNT][8] = + { /* STR STA AGI DEX WIS INT CHR ADD*/ + { /*Warrior*/ 10, 10, 5, 0, 0, 0, 0, 25}, + { /*Cleric*/ 5, 5, 0, 0, 10, 0, 0, 30}, + { /*Paladin*/ 10, 5, 0, 0, 5, 0, 10, 20}, + { /*Ranger*/ 5, 10, 10, 0, 5, 0, 0, 20}, + { /*ShadowKnight*/ 10, 5, 0, 0, 0, 10, 5, 20}, + { /*Druid*/ 0, 10, 0, 0, 10, 0, 0, 30}, + { /*Monk*/ 5, 5, 10, 10, 0, 0, 0, 20}, + { /*Bard*/ 5, 0, 0, 10, 0, 0, 10, 25}, + { /*Rouge*/ 0, 0, 10, 10, 0, 0, 0, 30}, + { /*Shaman*/ 0, 5, 0, 0, 10, 0, 5, 30}, + { /*Necromancer*/ 0, 0, 0, 10, 0, 10, 0, 30}, + { /*Wizard*/ 0, 10, 0, 0, 0, 10, 0, 30}, + { /*Magician*/ 0, 10, 0, 0, 0, 10, 0, 30}, + { /*Enchanter*/ 0, 0, 0, 0, 0, 10, 10, 30}, + { /*Beastlord*/ 0, 10, 5, 0, 10, 0, 5, 20}, + { /*Berserker*/ 10, 5, 0, 10, 0, 0, 0, 25} + }; + + static const bool ClassRaceLookupTable[PLAYER_CLASS_COUNT][_TABLE_RACES]= + { /*Human Barbarian Erudite Woodelf Highelf Darkelf Halfelf Dwarf Troll Ogre Halfling Gnome Iksar Vahshir Froglok Drakkin*/ + { /*Warrior*/ true, true, false, true, false, true, true, true, true, true, true, true, true, true, true, true}, + { /*Cleric*/ true, false, true, false, true, true, true, true, false, false, true, true, false, false, true, true}, + { /*Paladin*/ true, false, true, false, true, false, true, true, false, false, true, true, false, false, true, true}, + { /*Ranger*/ true, false, false, true, false, false, true, false, false, false, true, false, false, false, false, true}, + { /*ShadowKnight*/ true, false, true, false, false, true, false, false, true, true, false, true, true, false, true, true}, + { /*Druid*/ true, false, false, true, false, false, true, false, false, false, true, false, false, false, false, true}, + { /*Monk*/ true, false, false, false, false, false, false, false, false, false, false, false, true, false, false, true}, + { /*Bard*/ true, false, false, true, false, false, true, false, false, false, false, false, false, true, false, true}, + { /*Rogue*/ true, true, false, true, false, true, true, true, false, false, true, true, false, true, true, true}, + { /*Shaman*/ false, true, false, false, false, false, false, false, true, true, false, false, true, true, true, false}, + { /*Necromancer*/ true, false, true, false, false, true, false, false, false, false, false, true, true, false, true, true}, + { /*Wizard*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, true, true}, + { /*Magician*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, false, true}, + { /*Enchanter*/ true, false, true, false, true, true, false, false, false, false, false, true, false, false, false, true}, + { /*Beastlord*/ false, true, false, false, false, false, false, false, true, true, false, false, true, true, false, false}, + { /*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; + + _log(WORLD__CLIENT,"Validating char creation info..."); + + classtemp = cc->class_ - 1; + racetemp = cc->race - 1; + // these have non sequential race numbers so they need to be mapped + if (cc->race == FROGLOK) racetemp = 14; + if (cc->race == VAHSHIR) racetemp = 13; + if (cc->race == IKSAR) racetemp = 12; + if (cc->race == DRAKKIN) racetemp = 15; + + // if out of range looking it up in the table would crash stuff + // so we return from these + if(classtemp >= PLAYER_CLASS_COUNT) + { + _log(WORLD__CLIENT_ERR," class is out of range"); + return false; + } + 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? + { + _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 + return false; + } + + // solar: add up the base values for this class/race + // this is what they start with, and they have stat_points more + // that can distributed + bSTR = BaseClass[classtemp][0] + BaseRace[racetemp][0]; + bSTA = BaseClass[classtemp][1] + BaseRace[racetemp][1]; + bAGI = BaseClass[classtemp][2] + BaseRace[racetemp][2]; + bDEX = BaseClass[classtemp][3] + BaseRace[racetemp][3]; + bWIS = BaseClass[classtemp][4] + BaseRace[racetemp][4]; + bINT = BaseClass[classtemp][5] + BaseRace[racetemp][5]; + bCHA = BaseClass[classtemp][6] + BaseRace[racetemp][6]; + stat_points = BaseClass[classtemp][7]; + bTOTAL = bSTR + bSTA + bAGI + bDEX + bWIS + bINT + bCHA; + cTOTAL = cc->STR + cc->STA + cc->AGI + cc->DEX + cc->WIS + cc->INT + cc->CHA; + + // solar: the first check makes sure the total is exactly what was expected. + // this will catch all the stat cheating, but there's still the issue + // of reducing CHA or INT or something, to use for STR, so we check + // that none are lower than the base or higher than base + stat_points + // 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) + { + _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) + { + _log(WORLD__CLIENT_ERR," stat STR is out of range"); + Charerrors++; + } + 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) + { + _log(WORLD__CLIENT_ERR," stat AGI is out of range"); + Charerrors++; + } + 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) + { + _log(WORLD__CLIENT_ERR," stat WIS is out of range"); + Charerrors++; + } + 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) + { + _log(WORLD__CLIENT_ERR," stat CHA is out of range"); + Charerrors++; + } + + /*TODO: Check for deity/class/race.. it'd be nice, but probably of any real use to hack(faction, deity based items are all I can think of) + I am NOT writing those tables - kathgar*/ + + _log(WORLD__CLIENT,"Found %d errors in character creation request", Charerrors); + + return Charerrors == 0; +} + +void Client::SetClassStartingSkills( PlayerProfile_Struct *pp ) +{ + for(uint32 i = 0; i <= HIGHEST_SKILL; ++i) { + if(pp->skills[i] == 0) { + if(i >= SPECIALIZE_ABJURE && i <= SPECIALIZE_EVOCATION) { + continue; + } + + if(i == MAKE_POISON || + i == TINKERING || + i == RESEARCH || + i == ALCHEMY || + i == BAKING || + i == TAILORING || + i == BLACKSMITHING || + i == FLETCHING || + i == BREWING || + i == POTTERY || + i == JEWELRY_MAKING || + i == BEGGING) { + continue; + } + + pp->skills[i] = database.GetSkillCap(pp->class_, (SkillType)i, 1); + } + } +} + +void Client::SetRaceStartingSkills( PlayerProfile_Struct *pp ) +{ + switch( pp->race ) + { + case BARBARIAN: + case DWARF: + case ERUDITE: + case HALF_ELF: + case HIGH_ELF: + case HUMAN: + case OGRE: + case TROLL: + case DRAKKIN: //Drakkin are supposed to get a starting AA Skill + { + // No Race Specific Skills + break; + } + case DARK_ELF: + { + pp->skills[HIDE] = 50; + break; + } + case FROGLOK: + { + pp->skills[SWIMMING] = 125; + break; + } + case GNOME: + { + pp->skills[TINKERING] = 50; + break; + } + case HALFLING: + { + pp->skills[HIDE] = 50; + pp->skills[SNEAK] = 50; + break; + } + case IKSAR: + { + pp->skills[FORAGE] = 50; + pp->skills[SWIMMING] = 100; + break; + } + case WOOD_ELF: + { + pp->skills[FORAGE] = 50; + pp->skills[HIDE] = 50; + break; + } + case VAHSHIR: + { + pp->skills[SAFE_FALL] = 50; + pp->skills[SNEAK] = 50; + break; + } + } +} + +void Client::SetRacialLanguages( PlayerProfile_Struct *pp ) +{ + switch( pp->race ) + { + case BARBARIAN: + { + pp->languages[LANG_COMMON_TONGUE] = 100; + pp->languages[LANG_BARBARIAN] = 100; + break; + } + case DARK_ELF: + { + pp->languages[LANG_COMMON_TONGUE] = 100; + pp->languages[LANG_DARK_ELVISH] = 100; + pp->languages[LANG_DARK_SPEECH] = 100; + pp->languages[LANG_ELDER_ELVISH] = 100; + pp->languages[LANG_ELVISH] = 25; + break; + } + case DWARF: + { + pp->languages[LANG_COMMON_TONGUE] = 100; + pp->languages[LANG_DWARVISH] = 100; + pp->languages[LANG_GNOMISH] = 25; + break; + } + case ERUDITE: + { + pp->languages[LANG_COMMON_TONGUE] = 100; + pp->languages[LANG_ERUDIAN] = 100; + break; + } + case FROGLOK: + { + pp->languages[LANG_COMMON_TONGUE] = 100; + pp->languages[LANG_FROGLOK] = 100; + pp->languages[LANG_TROLL] = 25; + break; + } + case GNOME: + { + pp->languages[LANG_COMMON_TONGUE] = 100; + pp->languages[LANG_DWARVISH] = 25; + pp->languages[LANG_GNOMISH] = 100; + break; + } + case HALF_ELF: + { + pp->languages[LANG_COMMON_TONGUE] = 100; + pp->languages[LANG_ELVISH] = 100; + break; + } + case HALFLING: + { + pp->languages[LANG_COMMON_TONGUE] = 100; + pp->languages[LANG_HALFLING] = 100; + break; + } + case HIGH_ELF: + { + pp->languages[LANG_COMMON_TONGUE] = 100; + pp->languages[LANG_DARK_ELVISH] = 25; + pp->languages[LANG_ELDER_ELVISH] = 25; + pp->languages[LANG_ELVISH] = 100; + break; + } + case HUMAN: + { + pp->languages[LANG_COMMON_TONGUE] = 100; + break; + } + case IKSAR: + { + pp->languages[LANG_COMMON_TONGUE] = 95; + pp->languages[LANG_DARK_SPEECH] = 100; + pp->languages[LANG_LIZARDMAN] = 100; + break; + } + case OGRE: + { + pp->languages[LANG_COMMON_TONGUE] = 95; + pp->languages[LANG_DARK_SPEECH] = 100; + pp->languages[LANG_OGRE] = 100; + break; + } + case TROLL: + { + pp->languages[LANG_COMMON_TONGUE] = 95; + pp->languages[LANG_DARK_SPEECH] = 100; + pp->languages[LANG_TROLL] = 100; + break; + } + case WOOD_ELF: + { + pp->languages[LANG_COMMON_TONGUE] = 100; + pp->languages[LANG_ELVISH] = 100; + break; + } + case VAHSHIR: + { + pp->languages[LANG_COMMON_TONGUE] = 100; + pp->languages[LANG_COMBINE_TONGUE] = 100; + pp->languages[LANG_ERUDIAN] = 25; + pp->languages[LANG_VAH_SHIR] = 100; + break; + } + case DRAKKIN: + { + pp->languages[LANG_COMMON_TONGUE] = 100; + pp->languages[LANG_ELDER_DRAGON] = 100; + pp->languages[LANG_DRAGON] = 100; + break; + } + } +} diff --git a/world/client.h b/world/client.h new file mode 100644 index 000000000..ea8f1a27c --- /dev/null +++ b/world/client.h @@ -0,0 +1,108 @@ +/* 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 CLIENT_H +#define CLIENT_H + +#include + +//#include "../common/EQStream.h" +#include "../common/linked_list.h" +#include "../common/timer.h" +//#include "zoneserver.h" +#include "../common/logsys.h" +#include "../common/eq_packet_structs.h" +#include "cliententry.h" + +#define CLIENT_TIMEOUT 30000 + +class EQApplicationPacket; +class EQStreamInterface; + +class Client { +public: + Client(EQStreamInterface* ieqs); + ~Client(); + + bool Process(); + void ReceiveData(uchar* buf, int len); + void SendCharInfo(); + void SendMaxCharCreate(int max_chars); + void SendMembership(); + void SendMembershipSettings(); + void EnterWorld(bool TryBootup = true); + void ZoneUnavail(); + void QueuePacket(const EQApplicationPacket* app, bool ack_req = true); + void Clearance(int8 response); + void SendGuildList(); + void SendEnterWorld(std::string name); + void SendExpansionInfo(); + void SendLogServer(); + void SendApproveWorld(); + void SendPostEnterWorld(); + bool GenPassKey(char* key); + + inline uint32 GetIP() { return ip; } + inline uint16 GetPort() { return port; } + inline uint32 GetZoneID() { return zoneID; } + inline uint32 GetInstanceID() { return instanceID; } + inline uint32 WaitingForBootup() { return pwaitingforbootup; } + inline const char * GetAccountName() { if (cle) { return cle->AccountName(); } return "NOCLE"; } + inline int16 GetAdmin() { if (cle) { return cle->Admin(); } return 0; } + inline uint32 GetAccountID() { if (cle) { return cle->AccountID(); } return 0; } + inline uint32 GetWID() { if (cle) { return cle->GetID(); } return 0; } + inline uint32 GetLSID() { if (cle) { return cle->LSID(); } return 0; } + inline const char* GetLSKey() { if (cle) { return cle->GetLSKey(); } return "NOKEY"; } + inline uint32 GetCharID() { return charid; } + inline const char* GetCharName() { return char_name; } + inline ClientListEntry* GetCLE() { return cle; } + inline void SetCLE(ClientListEntry* iCLE) { cle = iCLE; } +private: + + uint32 ip; + uint16 port; + uint32 charid; + char char_name[64]; + uint32 zoneID; + uint32 instanceID; + bool pZoning; + Timer autobootup_timeout; + uint32 pwaitingforbootup; + + bool StartInTutorial; + uint32 ClientVersionBit; + bool OPCharCreate(char *name, CharCreate_Struct *cc); + + void SetClassStartingSkills( PlayerProfile_Struct *pp ); + void SetRaceStartingSkills( PlayerProfile_Struct *pp ); + void SetRacialLanguages( PlayerProfile_Struct *pp ); + + ClientListEntry* cle; + Timer CLE_keepalive_timer; + Timer connect; + bool firstlogin; + bool seencharsel; + bool realfirstlogin; + bool HandlePacket(const EQApplicationPacket *app); + EQStreamInterface* const eqs; +}; + + +bool CheckCharCreateInfoSoF(CharCreate_Struct *cc); +bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc); + +#endif diff --git a/world/cliententry.cpp b/world/cliententry.cpp new file mode 100644 index 000000000..0ea413cc1 --- /dev/null +++ b/world/cliententry.cpp @@ -0,0 +1,326 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 "cliententry.h" +#include "clientlist.h" +#include "LoginServer.h" +#include "LoginServerList.h" +#include "worlddb.h" +#include "zoneserver.h" +#include "WorldConfig.h" +#include "../common/guilds.h" + +extern uint32 numplayers; +extern LoginServerList loginserverlist; +extern ClientList client_list; +extern volatile bool RunLoops; + +ClientListEntry::ClientListEntry(uint32 in_id, uint32 iLSID, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin, uint32 ip, uint8 local) +: id(in_id) +{ + ClearVars(true); + + pIP = ip; + pLSID = iLSID; + if(iLSID > 0) + paccountid = database.GetAccountIDFromLSID(iLSID, paccountname, &padmin); + strn0cpy(plsname, iLoginName, sizeof(plsname)); + strn0cpy(plskey, iLoginKey, sizeof(plskey)); + pworldadmin = iWorldAdmin; + plocal=(local==1); + + pinstance = 0; +} + +ClientListEntry::ClientListEntry(uint32 in_id, uint32 iAccID, const char* iAccName, MD5& iMD5Pass, int16 iAdmin) +: id(in_id) +{ + ClearVars(true); + + pIP = 0; + pLSID = 0; + pworldadmin = 0; + + paccountid = iAccID; + strn0cpy(paccountname, iAccName, sizeof(paccountname)); + pMD5Pass = iMD5Pass; + padmin = iAdmin; + + pinstance = 0; +} + +ClientListEntry::ClientListEntry(uint32 in_id, ZoneServer* iZS, ServerClientList_Struct* scl, int8 iOnline) +: id(in_id) +{ + ClearVars(true); + + pIP = 0; + pLSID = scl->LSAccountID; + strn0cpy(plsname, scl->name, sizeof(plsname)); + strn0cpy(plskey, scl->lskey, sizeof(plskey)); + pworldadmin = 0; + + paccountid = scl->AccountID; + strn0cpy(paccountname, scl->AccountName, sizeof(paccountname)); + padmin = scl->Admin; + + pinstance = 0; + + if (iOnline >= CLE_Status_Zoning) + Update(iZS, scl, iOnline); + else + SetOnline(iOnline); +} + +ClientListEntry::~ClientListEntry() { + if (RunLoops) { + Camp(); // updates zoneserver's numplayers + client_list.RemoveCLEReferances(this); + } +} + +void ClientListEntry::SetChar(uint32 iCharID, const char* iCharName) { + pcharid = iCharID; + strn0cpy(pname, iCharName, sizeof(pname)); +} + +void ClientListEntry::SetOnline(ZoneServer* iZS, int8 iOnline) { + if (iZS == this->Server()) + SetOnline(iOnline); +} + +void ClientListEntry::SetOnline(int8 iOnline) { + if (iOnline >= CLE_Status_Online && pOnline < CLE_Status_Online) + numplayers++; + else if (iOnline < CLE_Status_Online && pOnline >= CLE_Status_Online) { + numplayers--; + } + if (iOnline != CLE_Status_Online || pOnline < CLE_Status_Online) + pOnline = iOnline; + if (iOnline < CLE_Status_Zoning) + Camp(); + if (pOnline >= CLE_Status_Online) + stale = 0; +} +void ClientListEntry::LSUpdate(ZoneServer* iZS){ + if(WorldConfig::get()->UpdateStats){ + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_LSZoneInfo; + pack->size = sizeof(ZoneInfo_Struct); + pack->pBuffer = new uchar[pack->size]; + ZoneInfo_Struct* zone =(ZoneInfo_Struct*)pack->pBuffer; + zone->count=iZS->NumPlayers(); + zone->zone = iZS->GetZoneID(); + zone->zone_wid = iZS->GetID(); + loginserverlist.SendPacket(pack); + safe_delete(pack); + } +} +void ClientListEntry::LSZoneChange(ZoneToZone_Struct* ztz){ + if(WorldConfig::get()->UpdateStats){ + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_LSPlayerZoneChange; + pack->size = sizeof(ServerLSPlayerZoneChange_Struct); + pack->pBuffer = new uchar[pack->size]; + ServerLSPlayerZoneChange_Struct* zonechange =(ServerLSPlayerZoneChange_Struct*)pack->pBuffer; + zonechange->lsaccount_id = LSID(); + zonechange->from = ztz->current_zone_id; + zonechange->to = ztz->requested_zone_id; + loginserverlist.SendPacket(pack); + safe_delete(pack); + } +} +void ClientListEntry::Update(ZoneServer* iZS, ServerClientList_Struct* scl, int8 iOnline) { + if (pzoneserver != iZS) { + if (pzoneserver){ + pzoneserver->RemovePlayer(); + LSUpdate(pzoneserver); + } + if (iZS){ + iZS->AddPlayer(); + LSUpdate(iZS); + } + } + pzoneserver = iZS; + pzone = scl->zone; + pinstance = scl->instance_id; + pcharid = scl->charid; + + strcpy(pname, scl->name); + if (paccountid == 0) { + paccountid = scl->AccountID; + strcpy(paccountname, scl->AccountName); + strcpy(plsname, scl->AccountName); + pIP = scl->IP; + pLSID = scl->LSAccountID; + strn0cpy(plskey, scl->lskey, sizeof(plskey)); + } + padmin = scl->Admin; + plevel = scl->level; + pclass_ = scl->class_; + prace = scl->race; + panon = scl->anon; + ptellsoff = scl->tellsoff; + pguild_id = scl->guild_id; + pLFG = scl->LFG; + gm = scl->gm; + pClientVersion = scl->ClientVersion; + + // Fields from the LFG Window + if((scl->LFGFromLevel != 0) && (scl->LFGToLevel != 0)) { + pLFGFromLevel = scl->LFGFromLevel; + pLFGToLevel = scl->LFGToLevel; + pLFGMatchFilter = scl->LFGMatchFilter; + memcpy(pLFGComments, scl->LFGComments, sizeof(pLFGComments)); + } + + SetOnline(iOnline); +} + +void ClientListEntry::LeavingZone(ZoneServer* iZS, int8 iOnline) { + if (iZS != 0 && iZS != pzoneserver) + return; + SetOnline(iOnline); + + if (pzoneserver){ + pzoneserver->RemovePlayer(); + LSUpdate(pzoneserver); + } + pzoneserver = 0; + pzone = 0; +} + +void ClientListEntry::ClearVars(bool iAll) { + if (iAll) { + pOnline = CLE_Status_Never; + stale = 0; + + pLSID = 0; + memset(plsname, 0, sizeof(plsname)); + memset(plskey, 0, sizeof(plskey)); + pworldadmin = 0; + + paccountid = 0; + memset(paccountname, 0, sizeof(paccountname)); + padmin = 0; + } + pzoneserver = 0; + pzone = 0; + pcharid = 0; + memset(pname, 0, sizeof(pname)); + plevel = 0; + pclass_ = 0; + prace = 0; + panon = 0; + ptellsoff = 0; + pguild_id = GUILD_NONE; + pLFG = 0; + gm = 0; + pClientVersion = 0; +} + +void ClientListEntry::Camp(ZoneServer* iZS) { + if (iZS != 0 && iZS != pzoneserver) + return; + if (pzoneserver){ + pzoneserver->RemovePlayer(); + LSUpdate(pzoneserver); + } + + ClearVars(); + + stale = 0; +} + +bool ClientListEntry::CheckStale() { + stale++; + if (stale >= 3) { + if (pOnline > CLE_Status_Offline) + SetOnline(CLE_Status_Offline); + else + return true; + } + return false; +} + +bool ClientListEntry::CheckAuth(uint32 iLSID, const char* iKey) { +// if (LSID() == iLSID && strncmp(plskey, iKey,10) == 0) { + if (strncmp(plskey, iKey,10) == 0) { + if (paccountid == 0 && LSID()>0) { + int16 tmpStatus = WorldConfig::get()->DefaultStatus; + paccountid = database.CreateAccount(plsname, 0, tmpStatus, LSID()); + if (!paccountid) { + _log(WORLD__CLIENTLIST_ERR,"Error adding local account for LS login: '%s', duplicate name?" ,plsname); + return false; + } + strn0cpy(paccountname, plsname, sizeof(paccountname)); + padmin = tmpStatus; + } + char lsworldadmin[15] = "0"; + database.GetVariable("honorlsworldadmin", lsworldadmin, sizeof(lsworldadmin)); + if (atoi(lsworldadmin) == 1 && pworldadmin != 0 && (padmin < pworldadmin || padmin == 0)) + padmin = pworldadmin; + return true; + } + return false; +} + +bool ClientListEntry::CheckAuth(const char* iName, MD5& iMD5Password) { + if (LSAccountID() == 0 && strcmp(paccountname, iName) == 0 && pMD5Pass == iMD5Password) + return true; + return false; +} + +bool ClientListEntry::CheckAuth(uint32 id, const char* iKey, uint32 ip) { + if (pIP==ip && strncmp(plskey, iKey,10) == 0){ + paccountid = id; + database.GetAccountFromID(id,paccountname,&padmin); + return true; + } + return false; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/world/cliententry.h b/world/cliententry.h new file mode 100644 index 000000000..f6a63a1b3 --- /dev/null +++ b/world/cliententry.h @@ -0,0 +1,126 @@ +#ifndef CLIENTENTRY_H_ +#define CLIENTENTRY_H_ + +#include "../common/types.h" +#include "../common/md5.h" +//#include "../common/eq_packet_structs.h" +#include "../common/servertalk.h" + + +#define CLE_Status_Never -1 +#define CLE_Status_Offline 0 +#define CLE_Status_Online 1 // Will not overwrite more specific online status +#define CLE_Status_CharSelect 2 +#define CLE_Status_Zoning 3 +#define CLE_Status_InZone 4 + +class ZoneServer; +struct ServerClientList_Struct; + +class ClientListEntry { +public: + ClientListEntry(uint32 id, uint32 iLSID, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = 0, uint32 ip = 0, uint8 local=0); + ClientListEntry(uint32 id, uint32 iAccID, const char* iAccName, MD5& iMD5Pass, int16 iAdmin = 0); + ClientListEntry(uint32 id, ZoneServer* iZS, ServerClientList_Struct* scl, int8 iOnline); + ~ClientListEntry(); + bool CheckStale(); + void Update(ZoneServer* zoneserver, ServerClientList_Struct* scl, int8 iOnline = CLE_Status_InZone); + void LSUpdate(ZoneServer* zoneserver); + void LSZoneChange(ZoneToZone_Struct* ztz); + bool CheckAuth(uint32 iLSID, const char* key); + bool CheckAuth(const char* iName, MD5& iMD5Password); + bool CheckAuth(uint32 id, const char* key, uint32 ip); + void SetOnline(ZoneServer* iZS, int8 iOnline); + void SetOnline(int8 iOnline = CLE_Status_Online); + void SetChar(uint32 iCharID, const char* iCharName); + inline int8 Online() { return pOnline; } + inline const uint32 GetID() const { return id; } + inline const uint32 GetIP() const { return pIP; } + inline void SetIP(const uint32& iIP) { pIP = iIP; } + inline void KeepAlive() { stale = 0; } + inline uint8 GetStaleCounter() const { return stale; } + void LeavingZone(ZoneServer* iZS = 0, int8 iOnline = CLE_Status_Offline); + void Camp(ZoneServer* iZS = 0); + + // Login Server stuff + inline uint32 LSID() const { return pLSID; } + inline uint32 LSAccountID() const { return pLSID; } + inline const char* LSName() const { return plsname; } + inline int16 WorldAdmin() const { return pworldadmin; } + inline const char* GetLSKey() const { return plskey; } + + // Account stuff + inline uint32 AccountID() const { return paccountid; } + inline const char* AccountName() const { return paccountname; } + inline int16 Admin() const { return padmin; } + inline void SetAdmin(uint16 iAdmin) { padmin = iAdmin; } + + // Character info + inline ZoneServer* Server() const { return pzoneserver; } + inline void ClearServer() { pzoneserver = 0; } + inline uint32 CharID() const { return pcharid; } + inline const char* name() const { return pname; } + inline uint32 zone() const { return pzone; } + inline uint16 instance() const { return pinstance; } + inline uint8 level() const { return plevel; } + inline uint8 class_() const { return pclass_; } + inline uint16 race() const { return prace; } + inline uint8 Anon() { return panon; } + inline uint8 TellsOff() const { return ptellsoff; } + inline uint32 GuildID() const { return pguild_id; } + inline void SetGuild(uint32 guild_id) { pguild_id = guild_id; } + inline bool LFG() const { return pLFG; } + inline uint8 GetGM() const { return gm; } + inline void SetGM(uint8 igm) { gm = igm; } + inline void SetZone(uint32 zone) { pzone = zone; } + inline bool IsLocalClient() const { return plocal; } + inline uint8 GetLFGFromLevel() const { return pLFGFromLevel; } + inline uint8 GetLFGToLevel() const { return pLFGToLevel; } + inline bool GetLFGMatchFilter() const { return pLFGMatchFilter; } + inline const char* GetLFGComments() const { return pLFGComments; } + inline uint8 GetClientVersion() { return pClientVersion; } + +private: + void ClearVars(bool iAll = false); + + const uint32 id; + uint32 pIP; + int8 pOnline; + uint8 stale; + + // Login Server stuff + uint32 pLSID; + char plsname[32]; + char plskey[16]; + int16 pworldadmin; // Login server's suggested admin status setting + bool plocal; + + // Account stuff + uint32 paccountid; + char paccountname[32]; + MD5 pMD5Pass; + int16 padmin; + + // Character info + ZoneServer* pzoneserver; + uint32 pzone; + uint16 pinstance; + uint32 pcharid; + char pname[64]; + uint8 plevel; + uint8 pclass_; + uint16 prace; + uint8 panon; + uint8 ptellsoff; + uint32 pguild_id; + bool pLFG; + uint8 gm; + uint8 pClientVersion; + uint8 pLFGFromLevel; + uint8 pLFGToLevel; + bool pLFGMatchFilter; + char pLFGComments[64]; +}; + + +#endif /*CLIENTENTRY_H_*/ diff --git a/world/clientlist.cpp b/world/clientlist.cpp new file mode 100644 index 000000000..0728745e7 --- /dev/null +++ b/world/clientlist.cpp @@ -0,0 +1,1362 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 "clientlist.h" +#include "zoneserver.h" +#include "zonelist.h" +#include "client.h" +#include "console.h" +#include "worlddb.h" +#include "../common/guilds.h" +#include "../common/races.h" +#include "../common/classes.h" +#include "../common/packet_dump.h" +#include "wguild_mgr.h" + +#include +using namespace std; + +extern ConsoleList console_list; +extern ZSList zoneserver_list; +uint32 numplayers = 0; //this really wants to be a member variable of ClientList... + + +ClientList::ClientList() +: CLStale_timer(45000) +{ + NextCLEID = 1; +} + +ClientList::~ClientList() { +} + + +void ClientList::Process() { + + if (CLStale_timer.Check()) + CLCheckStale(); + + + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (!iterator.GetData()->Process()) { + struct in_addr in; + in.s_addr = iterator.GetData()->GetIP(); + _log(WORLD__CLIENTLIST,"Removing client from %s:%d", inet_ntoa(in), iterator.GetData()->GetPort()); +//the client destructor should take care of this. +// iterator.GetData()->Free(); + iterator.RemoveCurrent(); + } + else + iterator.Advance(); + } +} + + +void ClientList::CLERemoveZSRef(ZoneServer* iZS) { + LinkedListIterator iterator(clientlist); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->Server() == iZS) { + iterator.GetData()->ClearServer(); // calling this before LeavingZone() makes CLE not update the number of players in a zone + iterator.GetData()->LeavingZone(); + } + iterator.Advance(); + } +} + +ClientListEntry* ClientList::GetCLE(uint32 iID) { + LinkedListIterator iterator(clientlist); + + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetID() == iID) { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} + +//Account Limiting Code to limit the number of characters allowed on from a single account at once. +void ClientList::EnforceSessionLimit(uint32 iLSAccountID) { + + ClientListEntry* ClientEntry = 0; + + LinkedListIterator iterator(clientlist, BACKWARD); + + int CharacterCount = 0; + + iterator.Reset(); + + while(iterator.MoreElements()) { + + ClientEntry = iterator.GetData(); + + if ((ClientEntry->LSAccountID() == iLSAccountID) && + ((ClientEntry->Admin() <= (RuleI(World, ExemptAccountLimitStatus))) || (RuleI(World, ExemptAccountLimitStatus) < 0))) { + + CharacterCount++; + + if (CharacterCount >= (RuleI(World, AccountSessionLimit))){ + // If we have a char name, they are in a zone, so send a kick to the zone server + if(strlen(ClientEntry->name())) { + + ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; + strcpy(skp->adminname, "SessionLimit"); + strcpy(skp->name, ClientEntry->name()); + skp->adminrank = 255; + zoneserver_list.SendPacket(pack); + safe_delete(pack); + } + + ClientEntry->SetOnline(CLE_Status_Offline); + + iterator.RemoveCurrent(); + + continue; + } + } + iterator.Advance(); + } +} + + +//Check current CLE Entry IPs against incoming connection + +void ClientList::GetCLEIP(uint32 iIP) { + + ClientListEntry* countCLEIPs = 0; + LinkedListIterator iterator(clientlist); + + int IPInstances = 0; + iterator.Reset(); + + while(iterator.MoreElements()) { + + countCLEIPs = iterator.GetData(); + + // If the IP matches, and the connection admin status is below the exempt status, + // or exempt status is less than 0 (no-one is exempt) + if ((countCLEIPs->GetIP() == iIP) && + ((countCLEIPs->Admin() < (RuleI(World, ExemptMaxClientsStatus))) || + (RuleI(World, ExemptMaxClientsStatus) < 0))) { + + // Increment the occurences of this IP address + IPInstances++; + + // If the number of connections exceeds the lower limit + if (IPInstances > (RuleI(World, MaxClientsPerIP))) { + + // If MaxClientsSetByStatus is set to True, override other IP Limit Rules + if (RuleB(World, MaxClientsSetByStatus)) { + + // The IP Limit is set by the status of the account if status > MaxClientsPerIP + if (IPInstances > countCLEIPs->Admin()) { + + if(RuleB(World, IPLimitDisconnectAll)) { + DisconnectByIP(iIP); + return; + } else { + // Remove the connection + countCLEIPs->SetOnline(CLE_Status_Offline); + iterator.RemoveCurrent(); + continue; + } + } + } + // Else if the Admin status of the connection is not eligible for the higher limit, + // or there is no higher limit (AddMaxClientStatus<0) + else if ((countCLEIPs->Admin() < (RuleI(World, AddMaxClientsStatus)) || + (RuleI(World, AddMaxClientsStatus) < 0))) { + + if(RuleB(World, IPLimitDisconnectAll)) { + DisconnectByIP(iIP); + return; + } else { + // Remove the connection + countCLEIPs->SetOnline(CLE_Status_Offline); + iterator.RemoveCurrent(); + continue; + } + } + // else they are eligible for the higher limit, but if they exceed that + else if (IPInstances > RuleI(World, AddMaxClientsPerIP)) { + + if(RuleB(World, IPLimitDisconnectAll)) { + DisconnectByIP(iIP); + return; + } else { + // Remove the connection + countCLEIPs->SetOnline(CLE_Status_Offline); + iterator.RemoveCurrent(); + continue; + } + } + } + } + iterator.Advance(); + } +} + +void ClientList::DisconnectByIP(uint32 iIP) { + ClientListEntry* countCLEIPs = 0; + LinkedListIterator iterator(clientlist); + iterator.Reset(); + + while(iterator.MoreElements()) { + countCLEIPs = iterator.GetData(); + if ((countCLEIPs->GetIP() == iIP)) { + if(strlen(countCLEIPs->name())) { + ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; + strcpy(skp->adminname, "SessionLimit"); + strcpy(skp->name, countCLEIPs->name()); + skp->adminrank = 255; + zoneserver_list.SendPacket(pack); + safe_delete(pack); + } + countCLEIPs->SetOnline(CLE_Status_Offline); + iterator.RemoveCurrent(); + } + iterator.Advance(); + } +} + +ClientListEntry* ClientList::FindCharacter(const char* name) { + LinkedListIterator iterator(clientlist); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (strcasecmp(iterator.GetData()->name(), name) == 0) { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} + + +ClientListEntry* ClientList::FindCLEByAccountID(uint32 iAccID) { + LinkedListIterator iterator(clientlist); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->AccountID() == iAccID) { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} + +ClientListEntry* ClientList::FindCLEByCharacterID(uint32 iCharID) { + LinkedListIterator iterator(clientlist); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->CharID() == iCharID) { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} + +void ClientList::SendCLEList(const int16& admin, const char* to, WorldTCPConnection* connection, const char* iName) { + LinkedListIterator iterator(clientlist); + char* output = 0; + uint32 outsize = 0, outlen = 0; + int x = 0, y = 0; + int namestrlen = iName == 0 ? 0 : strlen(iName); + bool addnewline = false; + char newline[3]; + if (connection->IsConsole()) + strcpy(newline, "\r\n"); + else + strcpy(newline, "^"); + + iterator.Reset(); + while(iterator.MoreElements()) { + ClientListEntry* cle = iterator.GetData(); + if (admin >= cle->Admin() && (iName == 0 || namestrlen == 0 || strncasecmp(cle->name(), iName, namestrlen) == 0 || strncasecmp(cle->AccountName(), iName, namestrlen) == 0 || strncasecmp(cle->LSName(), iName, namestrlen) == 0)) { + struct in_addr in; + in.s_addr = cle->GetIP(); + if (addnewline) { + AppendAnyLenString(&output, &outsize, &outlen, newline); + } + AppendAnyLenString(&output, &outsize, &outlen, "ID: %i Acc# %i AccName: %s IP: %s", cle->GetID(), cle->AccountID(), cle->AccountName(), inet_ntoa(in)); + AppendAnyLenString(&output, &outsize, &outlen, "%s Stale: %i Online: %i Admin: %i", newline, cle->GetStaleCounter(), cle->Online(), cle->Admin()); + if (cle->LSID()) + AppendAnyLenString(&output, &outsize, &outlen, "%s LSID: %i LSName: %s WorldAdmin: %i", newline, cle->LSID(), cle->LSName(), cle->WorldAdmin()); + if (cle->CharID()) + AppendAnyLenString(&output, &outsize, &outlen, "%s CharID: %i CharName: %s Zone: %s (%i)", newline, cle->CharID(), cle->name(), database.GetZoneName(cle->zone()), cle->zone()); + if (outlen >= 3072) { + connection->SendEmoteMessageRaw(to, 0, 0, 10, output); + safe_delete(output); + outsize = 0; + outlen = 0; + addnewline = false; + } + else + addnewline = true; + y++; + } + iterator.Advance(); + x++; + } + AppendAnyLenString(&output, &outsize, &outlen, "%s%i CLEs in memory. %i CLEs listed. numplayers = %i.", newline, x, y, numplayers); + connection->SendEmoteMessageRaw(to, 0, 0, 10, output); + safe_delete(output); +} + + +void ClientList::CLEAdd(uint32 iLSID, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin, uint32 ip, uint8 local) { + ClientListEntry* tmp = new ClientListEntry(GetNextCLEID(), iLSID, iLoginName, iLoginKey, iWorldAdmin, ip, local); + + clientlist.Append(tmp); +} + +void ClientList::CLCheckStale() { + LinkedListIterator iterator(clientlist); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->CheckStale()) { + iterator.RemoveCurrent(); + } + else + iterator.Advance(); + } +} + +void ClientList::ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl) { + LinkedListIterator iterator(clientlist); + ClientListEntry* cle; + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetID() == scl->wid) { + cle = iterator.GetData(); + if (scl->remove == 2){ + cle->LeavingZone(zoneserver, CLE_Status_Offline); + } + else if (scl->remove == 1) + cle->LeavingZone(zoneserver, CLE_Status_Zoning); + else + cle->Update(zoneserver, scl); + return; + } + iterator.Advance(); + } + if (scl->remove == 2) + cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status_Online); + else if (scl->remove == 1) + cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status_Zoning); + else + cle = new ClientListEntry(GetNextCLEID(), zoneserver, scl, CLE_Status_InZone); + clientlist.Insert(cle); + zoneserver->ChangeWID(scl->charid, cle->GetID()); +} + +void ClientList::CLEKeepAlive(uint32 numupdates, uint32* wid) { + LinkedListIterator iterator(clientlist); + uint32 i; + + iterator.Reset(); + while(iterator.MoreElements()) { + for (i=0; iGetID()) + iterator.GetData()->KeepAlive(); + } + iterator.Advance(); + } +} + + +ClientListEntry* ClientList::CheckAuth(uint32 id, const char* iKey, uint32 ip ) { + LinkedListIterator iterator(clientlist); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->CheckAuth(id, iKey, ip)) + return iterator.GetData(); + iterator.Advance(); + } + return 0; +} +ClientListEntry* ClientList::CheckAuth(uint32 iLSID, const char* iKey) { + LinkedListIterator iterator(clientlist); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->CheckAuth(iLSID, iKey)) + return iterator.GetData(); + iterator.Advance(); + } + return 0; +} + +ClientListEntry* ClientList::CheckAuth(const char* iName, const char* iPassword) { + LinkedListIterator iterator(clientlist); + MD5 tmpMD5(iPassword); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->CheckAuth(iName, tmpMD5)) + return iterator.GetData(); + iterator.Advance(); + } + int16 tmpadmin; + + _log(WORLD__ZONELIST,"Login with '%s' and '%s'", iName, iPassword); + + uint32 accid = database.CheckLogin(iName, iPassword, &tmpadmin); + if (accid) { + ClientListEntry* tmp = new ClientListEntry(GetNextCLEID(), accid, iName, tmpMD5, tmpadmin); + clientlist.Append(tmp); + return tmp; + } + return 0; +} + +void ClientList::SendOnlineGuildMembers(uint32 FromID, uint32 GuildID) +{ + int PacketLength = 8; + + uint32 Count = 0; + ClientListEntry* from = this->FindCLEByCharacterID(FromID); + + if(!from) + { + _log(WORLD__CLIENT_ERR,"Invalid client. FromID=%i GuildID=%i", FromID, GuildID); + return; + } + + LinkedListIterator Iterator(clientlist); + + Iterator.Reset(); + + while(Iterator.MoreElements()) + { + ClientListEntry* CLE = Iterator.GetData(); + + if(CLE && (CLE->GuildID() == GuildID)) + { + PacketLength += (strlen(CLE->name()) + 5); + ++Count; + } + + Iterator.Advance(); + + } + + Iterator.Reset(); + + ServerPacket* pack = new ServerPacket(ServerOP_OnlineGuildMembersResponse, PacketLength); + + char *Buffer = (char *)pack->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, FromID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, Count); + + while(Iterator.MoreElements()) + { + ClientListEntry* CLE = Iterator.GetData(); + + if(CLE && (CLE->GuildID() == GuildID)) + { + VARSTRUCT_ENCODE_STRING(Buffer, CLE->name()); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, CLE->zone()); + } + + Iterator.Advance(); + } + zoneserver_list.SendPacket(from->zone(), from->instance(), pack); + safe_delete(pack); +} + + +void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_Struct* whom, WorldTCPConnection* connection) { + try{ + LinkedListIterator iterator(clientlist); + LinkedListIterator countclients(clientlist); + ClientListEntry* cle = 0; + ClientListEntry* countcle = 0; + //char tmpgm[25] = ""; + //char accinfo[150] = ""; + char line[300] = ""; + //char tmpguild[50] = ""; + //char LFG[10] = ""; + //uint32 x = 0; + int whomlen = 0; + if (whom) { + whomlen = strlen(whom->whom); + if(whom->wrace == 0x001A) // 0x001A is the old Froglok race number and is sent by the client for /who all froglok + whom->wrace = FROGLOK; // This is what EQEmu uses for the Froglok Race number. + } + + char* output = 0; + uint32 outsize = 0, outlen = 0; + uint32 totalusers=0; + uint32 totallength=0; + AppendAnyLenString(&output, &outsize, &outlen, "Players on server:"); + if (connection->IsConsole()) + AppendAnyLenString(&output, &outsize, &outlen, "\r\n"); + else + AppendAnyLenString(&output, &outsize, &outlen, "\n"); + countclients.Reset(); + while(countclients.MoreElements()){ + countcle = countclients.GetData(); + const char* tmpZone = database.GetZoneName(countcle->zone()); + if ( + (countcle->Online() >= CLE_Status_Zoning) && + (!countcle->GetGM() || countcle->Anon() != 1 || admin >= countcle->Admin()) && + (whom == 0 || ( + ((countcle->Admin() >= 80 && countcle->GetGM()) || whom->gmlookup == 0xFFFF) && + (whom->lvllow == 0xFFFF || (countcle->level() >= whom->lvllow && countcle->level() <= whom->lvlhigh && (countcle->Anon()==0 || admin > countcle->Admin()))) && + (whom->wclass == 0xFFFF || (countcle->class_() == whom->wclass && (countcle->Anon()==0 || admin > countcle->Admin()))) && + (whom->wrace == 0xFFFF || (countcle->race() == whom->wrace && (countcle->Anon()==0 || admin > countcle->Admin()))) && + (whomlen == 0 || ( + (tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) || + strncasecmp(countcle->name(),whom->whom, whomlen) == 0 || + (strncasecmp(guild_mgr.GetGuildName(countcle->GuildID()), whom->whom, whomlen) == 0) || + (admin >= 100 && strncasecmp(countcle->AccountName(), whom->whom, whomlen) == 0) + )) + )) +) { + if((countcle->Anon()>0 && admin>=countcle->Admin() && admin>0) || countcle->Anon()==0 ){ + totalusers++; + if(totalusers<=20 || admin>=100) + totallength=totallength+strlen(countcle->name())+strlen(countcle->AccountName())+strlen(guild_mgr.GetGuildName(countcle->GuildID()))+5; + } + else if((countcle->Anon()>0 && admin<=countcle->Admin()) || countcle->Anon()==0 && !countcle->GetGM()){ + totalusers++; + if(totalusers<=20 || admin>=100) + totallength=totallength+strlen(countcle->name())+strlen(guild_mgr.GetGuildName(countcle->GuildID()))+5; + } + } + countclients.Advance(); + } + uint32 plid=fromid; + uint32 playerineqstring=5001; + const char line2[]="---------------------------"; + uint8 unknown35=0x0A; + uint32 unknown36=0; + uint32 playersinzonestring=5028; + if(totalusers>20 && admin<100){ + totalusers=20; + playersinzonestring=5033; + } + else if(totalusers>1) + playersinzonestring=5036; + uint32 unknown44[2]; + unknown44[0]=0; + unknown44[1]=0; + uint32 unknown52=totalusers; + uint32 unknown56=1; + ServerPacket* pack2 = new ServerPacket(ServerOP_WhoAllReply,64+totallength+(49*totalusers)); + memset(pack2->pBuffer,0,pack2->size); + uchar *buffer=pack2->pBuffer; + uchar *bufptr=buffer; + //memset(buffer,0,pack2->size); + memcpy(bufptr,&plid, sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&playerineqstring, sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&line2, strlen(line2)); + bufptr+=strlen(line2); + memcpy(bufptr,&unknown35, sizeof(uint8)); + bufptr+=sizeof(uint8); + memcpy(bufptr,&unknown36, sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&playersinzonestring, sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&unknown44[0], sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&unknown44[1], sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&unknown52, sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&unknown56, sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&totalusers, sizeof(uint32)); + bufptr+=sizeof(uint32); + + iterator.Reset(); + int idx=-1; + while(iterator.MoreElements()) { + cle = iterator.GetData(); + + const char* tmpZone = database.GetZoneName(cle->zone()); + if ( + (cle->Online() >= CLE_Status_Zoning) && + (!cle->GetGM() || cle->Anon() != 1 || admin >= cle->Admin()) && + (whom == 0 || ( + ((cle->Admin() >= 80 && cle->GetGM()) || whom->gmlookup == 0xFFFF) && + (whom->lvllow == 0xFFFF || (cle->level() >= whom->lvllow && cle->level() <= whom->lvlhigh && (cle->Anon()==0 || admin>cle->Admin()))) && + (whom->wclass == 0xFFFF || (cle->class_() == whom->wclass && (cle->Anon()==0 || admin>cle->Admin()))) && + (whom->wrace == 0xFFFF || (cle->race() == whom->wrace && (cle->Anon()==0 || admin>cle->Admin()))) && + (whomlen == 0 || ( + (tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) || + strncasecmp(cle->name(),whom->whom, whomlen) == 0 || + (strncasecmp(guild_mgr.GetGuildName(cle->GuildID()), whom->whom, whomlen) == 0) || + (admin >= 100 && strncasecmp(cle->AccountName(), whom->whom, whomlen) == 0) + )) + )) +) { + line[0] = 0; + uint32 rankstring=0xFFFFFFFF; + if((cle->Anon()==1 && cle->GetGM() && cle->Admin()>admin) || (idx>=20 && admin<100)){ //hide gms that are anon from lesser gms and normal players, cut off at 20 + rankstring=0; + iterator.Advance(); + continue; + } else if (cle->GetGM()) { + if (cle->Admin() >=250) + rankstring=5021; + else if (cle->Admin() >= 200) + rankstring=5020; + else if (cle->Admin() >= 180) + rankstring=5019; + else if (cle->Admin() >= 170) + rankstring=5018; + else if (cle->Admin() >= 160) + rankstring=5017; + else if (cle->Admin() >= 150) + rankstring=5016; + else if (cle->Admin() >= 100) + rankstring=5015; + else if (cle->Admin() >= 95) + rankstring=5014; + else if (cle->Admin() >= 90) + rankstring=5013; + else if (cle->Admin() >= 85) + rankstring=5012; + else if (cle->Admin() >= 81) + rankstring=5011; + else if (cle->Admin() >= 80) + rankstring=5010; + else if (cle->Admin() >= 50) + rankstring=5009; + else if (cle->Admin() >= 20) + rankstring=5008; + else if (cle->Admin() >= 10) + rankstring=5007; + } + idx++; + char guildbuffer[67]={0}; + if (cle->GuildID() != GUILD_NONE && cle->GuildID()>0) + sprintf(guildbuffer,"<%s>", guild_mgr.GetGuildName(cle->GuildID())); + uint32 formatstring=5025; + if(cle->Anon()==1 && (adminAdmin() || admin==0)) + formatstring=5024; + else if(cle->Anon()==1 && admin>=cle->Admin() && admin>0) + formatstring=5022; + else if(cle->Anon()==2 && (adminAdmin() || admin==0)) + formatstring=5023;//display guild + else if(cle->Anon()==2 && admin>=cle->Admin() && admin>0) + formatstring=5022;//display everything + + //war* wars2 = (war*)pack2->pBuffer; + + uint32 plclass_=0; + uint32 pllevel=0; + uint32 pidstring=0xFFFFFFFF;//5003; + uint32 plrace=0; + uint32 zonestring=0xFFFFFFFF; + uint32 plzone=0; + uint32 unknown80[2]; + if(cle->Anon()==0 || (admin>=cle->Admin() && admin>0)){ + plclass_=cle->class_(); + pllevel=cle->level(); + if(admin>=100) + pidstring=5003; + plrace=cle->race(); + zonestring=5006; + plzone=cle->zone(); + } + + + if(admin>=cle->Admin() && admin>0) + unknown80[0]=cle->Admin(); + else + unknown80[0]=0xFFFFFFFF; + unknown80[1]=0xFFFFFFFF;//1035 + + + //char plstatus[20]={0}; + //sprintf(plstatus, "Status %i",cle->Admin()); + char plname[64]={0}; + strcpy(plname,cle->name()); + + char placcount[30]={0}; + if(admin>=cle->Admin() && admin>0) + strcpy(placcount,cle->AccountName()); + + memcpy(bufptr,&formatstring, sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&pidstring, sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&plname, strlen(plname)+1); + bufptr+=strlen(plname)+1; + memcpy(bufptr,&rankstring, sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&guildbuffer, strlen(guildbuffer)+1); + bufptr+=strlen(guildbuffer)+1; + memcpy(bufptr,&unknown80[0], sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&unknown80[1], sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&zonestring, sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&plzone, sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&plclass_, sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&pllevel, sizeof(uint32)); + bufptr+=sizeof(uint32); + memcpy(bufptr,&plrace, sizeof(uint32)); + bufptr+=sizeof(uint32); + uint32 ending=0; + memcpy(bufptr,&placcount, strlen(placcount)+1); + bufptr+=strlen(placcount)+1; + ending=207; + memcpy(bufptr,&ending, sizeof(uint32)); + bufptr+=sizeof(uint32); + } + iterator.Advance(); + } + pack2->Deflate(); + //zoneserver_list.SendPacket(pack2); // NO NO NO WHY WOULD YOU SEND IT TO EVERY ZONE SERVER?!? + SendPacket(to,pack2); + safe_delete(pack2); + safe_delete(output); + } + catch(...){ + _log(WORLD__ZONELIST_ERR,"Unknown error in world's SendWhoAll (probably mem error), ignoring..."); + return; + } +} + +void ClientList::SendFriendsWho(ServerFriendsWho_Struct *FriendsWho, WorldTCPConnection* connection) { + + vector FriendsCLEs; + FriendsCLEs.reserve(100); + + char Friend_[65]; + + char *FriendsPointer = FriendsWho->FriendsString; + + // FriendsString is a comma delimited list of names. + + char *Seperator = NULL; + + Seperator = strchr(FriendsPointer, ','); + if(!Seperator) Seperator = strchr(FriendsPointer, '\0'); + + uint32 TotalLength=0; + + while(Seperator != NULL) { + + if((Seperator - FriendsPointer) > 64) return; + + strncpy(Friend_, FriendsPointer, Seperator - FriendsPointer); + Friend_[Seperator - FriendsPointer] = 0; + + ClientListEntry* CLE = FindCharacter(Friend_); + if(CLE && CLE->name() && (CLE->Online() >= CLE_Status_Zoning) && !(CLE->GetGM() && CLE->Anon())) { + FriendsCLEs.push_back(CLE); + TotalLength += strlen(CLE->name()); + int GuildNameLength = strlen(guild_mgr.GetGuildName(CLE->GuildID())); + if(GuildNameLength>0) + TotalLength += (GuildNameLength + 2); + } + + if(Seperator[0] == '\0') break; + + FriendsPointer = Seperator + 1; + Seperator = strchr(FriendsPointer, ','); + if(!Seperator) Seperator = strchr(FriendsPointer, '\0'); + } + + + try{ + ClientListEntry* cle; + int FriendsOnline = FriendsCLEs.size(); + int PacketLength = sizeof(WhoAllReturnStruct) + (47 * FriendsOnline) + TotalLength; + ServerPacket* pack2 = new ServerPacket(ServerOP_WhoAllReply, PacketLength); + memset(pack2->pBuffer,0,pack2->size); + uchar *buffer=pack2->pBuffer; + uchar *bufptr=buffer; + + WhoAllReturnStruct *WARS = (WhoAllReturnStruct *)bufptr; + + WARS->id = FriendsWho->FromID; + WARS->playerineqstring = 0xffffffff; + strcpy(WARS->line, ""); + WARS->unknown35 = 0x0a; + WARS->unknown36 = 0x00; + + if(FriendsCLEs.size() == 1) + WARS->playersinzonestring = 5028; // 5028 There is %1 player in EverQuest. + else + WARS->playersinzonestring = 5036; // 5036 There are %1 players in EverQuest. + + WARS->unknown44[0] = 0; + WARS->unknown44[1] = 0; + WARS->unknown52 = FriendsOnline; + WARS->unknown56 = 1; + WARS->playercount = FriendsOnline; + + bufptr+=sizeof(WhoAllReturnStruct); + + for(int CLEEntry = 0; CLEEntry < FriendsOnline; CLEEntry++) { + + cle = FriendsCLEs[CLEEntry]; + + char GuildName[67]={0}; + if (cle->GuildID() != GUILD_NONE && cle->GuildID()>0) + sprintf(GuildName,"<%s>", guild_mgr.GetGuildName(cle->GuildID())); + uint32 FormatMSGID=5025; // 5025 %T1[%2 %3] %4 (%5) %6 %7 %8 %9 + if(cle->Anon()==1) + FormatMSGID=5024; // 5024 %T1[ANONYMOUS] %2 %3 + else if(cle->Anon()==2) + FormatMSGID=5023; // 5023 %T1[ANONYMOUS] %2 %3 %4 + + uint32 PlayerClass=0; + uint32 PlayerLevel=0; + uint32 PlayerRace=0; + uint32 ZoneMSGID=0xffffffff; + uint32 PlayerZone=0; + + if(cle->Anon()==0) { + PlayerClass=cle->class_(); + PlayerLevel=cle->level(); + PlayerRace=cle->race(); + ZoneMSGID=5006; // 5006 ZONE: %1 + PlayerZone=cle->zone(); + } + + char PlayerName[64]={0}; + strcpy(PlayerName,cle->name()); + + WhoAllPlayerPart1* WAPP1 = (WhoAllPlayerPart1*)bufptr; + + WAPP1->FormatMSGID = FormatMSGID; + WAPP1->PIDMSGID = 0xffffffff; + strcpy(WAPP1->Name, PlayerName); + + bufptr += sizeof(WhoAllPlayerPart1) + strlen(PlayerName); + WhoAllPlayerPart2* WAPP2 = (WhoAllPlayerPart2*)bufptr; + + WAPP2->RankMSGID = 0xffffffff; + strcpy(WAPP2->Guild, GuildName); + + bufptr += sizeof(WhoAllPlayerPart2) + strlen(GuildName); + WhoAllPlayerPart3* WAPP3 = (WhoAllPlayerPart3*)bufptr; + + WAPP3->Unknown80[0] = 0xffffffff; + WAPP3->Unknown80[1] = 0xffffffff; + WAPP3->ZoneMSGID = ZoneMSGID; + WAPP3->Zone = PlayerZone; + WAPP3->Class_ = PlayerClass; + WAPP3->Level = PlayerLevel; + WAPP3->Race = PlayerRace; + WAPP3->Account[0] = 0; + + bufptr += sizeof(WhoAllPlayerPart3); + + WhoAllPlayerPart4* WAPP4 = (WhoAllPlayerPart4*)bufptr; + WAPP4->Unknown100 = 207; + + bufptr += sizeof(WhoAllPlayerPart4); + + } + pack2->Deflate(); + SendPacket(FriendsWho->FromName,pack2); + safe_delete(pack2); + } + catch(...){ + _log(WORLD__ZONELIST_ERR,"Unknown error in world's SendFriendsWho (probably mem error), ignoring..."); + return; + } +} + +void ClientList::SendLFGMatches(ServerLFGMatchesRequest_Struct *smrs) { + + // Send back matches when someone searches player's Looking For A Group. + + LinkedListIterator Iterator(clientlist); + ClientListEntry* CLE = 0; + int Matches = 0; + + Iterator.Reset(); + + // We run the ClientList twice. The first time is to determine how big the outgoing packet needs to be. + while(Iterator.MoreElements()) { + CLE = Iterator.GetData(); + if(CLE->LFG()) { + unsigned int BitMask = 1 << CLE->class_(); + // First we check that the player meets the level and class criteria of the person + // doing the search. + if((CLE->level() >= smrs->FromLevel) && (CLE->level() <= smrs->ToLevel) && + (BitMask & smrs->Classes)) + // Then we check if if the player doing the search meets the level criteria specified + // by the player who is LFG. + // + // GetLFGMatchFilter returns the setting of the 'Only players who match my posted filters + // can query me' checkbox. + // + // FromLevel and ToLevel are the settings of the 'Want group levels:' boxes. + if(!CLE->GetLFGMatchFilter() || ((smrs->QuerierLevel >= CLE->GetLFGFromLevel()) && + (smrs->QuerierLevel <= CLE->GetLFGToLevel()))) + Matches++; + } + Iterator.Advance(); + } + ServerPacket* Pack = new ServerPacket(ServerOP_LFGMatches, (sizeof(ServerLFGMatchesResponse_Struct) * Matches) + 4); + + char *Buf = (char *)Pack->pBuffer; + // FromID is the Entity ID of the player doing the search. + VARSTRUCT_ENCODE_TYPE(uint32, Buf, smrs->FromID); + + ServerLFGMatchesResponse_Struct* Buffer = (ServerLFGMatchesResponse_Struct*)Buf; + + Iterator.Reset(); + + if(Matches) { + while(Iterator.MoreElements() && (Matches > 0)) { + CLE = Iterator.GetData(); + if(CLE->LFG()) { + unsigned int BitMask = 1 << CLE->class_(); + if((CLE->level() >= smrs->FromLevel) && (CLE->level() <= smrs->ToLevel) && + (BitMask & smrs->Classes)) { + Matches--; + strcpy(Buffer->Name, CLE->name()); + Buffer->Class_ = CLE->class_(); + Buffer->Level = CLE->level(); + Buffer->Zone = CLE->zone(); + // If the LFG player is anon, level and class are still displayed, but + // zone shows as UNAVAILABLE. + Buffer->Anon = (CLE->Anon() != 0); + // The client can filter on Guildname + Buffer->GuildID = CLE->GuildID(); + strcpy(Buffer->Comments, CLE->GetLFGComments()); + Buffer++; + } + } + Iterator.Advance(); + } + Pack->Deflate(); + } + SendPacket(smrs->FromName,Pack); + safe_delete(Pack); +} + +void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* whom, WorldTCPConnection* connection) { + LinkedListIterator iterator(clientlist); + ClientListEntry* cle = 0; + char tmpgm[25] = ""; + char accinfo[150] = ""; + char line[300] = ""; + char tmpguild[50] = ""; + char LFG[10] = ""; + uint32 x = 0; + int whomlen = 0; + if (whom) + whomlen = strlen(whom->whom); + + char* output = 0; + uint32 outsize = 0, outlen = 0; + AppendAnyLenString(&output, &outsize, &outlen, "Players on server:"); + if (connection->IsConsole()) + AppendAnyLenString(&output, &outsize, &outlen, "\r\n"); + else + AppendAnyLenString(&output, &outsize, &outlen, "\n"); + iterator.Reset(); + while(iterator.MoreElements()) { + cle = iterator.GetData(); + const char* tmpZone = database.GetZoneName(cle->zone()); + if ( + (cle->Online() >= CLE_Status_Zoning) + && (whom == 0 || ( + ((cle->Admin() >= 80 && cle->GetGM()) || whom->gmlookup == 0xFFFF) && + (whom->lvllow == 0xFFFF || (cle->level() >= whom->lvllow && cle->level() <= whom->lvlhigh)) && + (whom->wclass == 0xFFFF || cle->class_() == whom->wclass) && + (whom->wrace == 0xFFFF || cle->race() == whom->wrace) && + (whomlen == 0 || ( + (tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) || + strncasecmp(cle->name(),whom->whom, whomlen) == 0 || + (strncasecmp(guild_mgr.GetGuildName(cle->GuildID()), whom->whom, whomlen) == 0) || + (admin >= 100 && strncasecmp(cle->AccountName(), whom->whom, whomlen) == 0) + )) + )) +) { + line[0] = 0; +// MYRA - use new (5.x) Status labels in who for telnet connection + if (cle->Admin() >=250) + strcpy(tmpgm, "* GM-Impossible * "); + else if (cle->Admin() >= 200) + strcpy(tmpgm, "* GM-Mgmt * "); + else if (cle->Admin() >= 180) + strcpy(tmpgm, "* GM-Coder * "); + else if (cle->Admin() >= 170) + strcpy(tmpgm, "* GM-Areas * "); + else if (cle->Admin() >= 160) + strcpy(tmpgm, "* QuestMaster * "); + else if (cle->Admin() >= 150) + strcpy(tmpgm, "* GM-Lead Admin * "); + else if (cle->Admin() >= 100) + strcpy(tmpgm, "* GM-Admin * "); + else if (cle->Admin() >= 95) + strcpy(tmpgm, "* GM-Staff * "); + else if (cle->Admin() >= 90) + strcpy(tmpgm, "* EQ Support * "); + else if (cle->Admin() >= 85) + strcpy(tmpgm, "* GM-Tester * "); + else if (cle->Admin() >= 81) + strcpy(tmpgm, "* Senior Guide * "); + else if (cle->Admin() >= 80) + strcpy(tmpgm, "* QuestTroupe * "); + else if (cle->Admin() >= 50) + strcpy(tmpgm, "* Guide * "); + else if (cle->Admin() >= 20) + strcpy(tmpgm, "* Apprentice Guide * "); + else if (cle->Admin() >= 10) + strcpy(tmpgm, "* Steward * "); + else + tmpgm[0] = 0; +// end Myra + + if (guild_mgr.GuildExists(cle->GuildID())) { + snprintf(tmpguild, 36, " <%s>", guild_mgr.GetGuildName(cle->GuildID())); + } else + tmpguild[0] = 0; + + if (cle->LFG()) + strcpy(LFG, " LFG"); + else + LFG[0] = 0; + + if (admin >= 150 && admin >= cle->Admin()) { + sprintf(accinfo, " AccID: %i AccName: %s LSID: %i Status: %i", cle->AccountID(), cle->AccountName(), cle->LSAccountID(), cle->Admin()); + } + else + accinfo[0] = 0; + + if (cle->Anon() == 2) { // Roleplay + if (admin >= 100 && admin >= cle->Admin()) + sprintf(line, " %s[RolePlay %i %s] %s (%s)%s zone: %s%s%s", tmpgm, cle->level(), GetEQClassName(cle->class_(),cle->level()), cle->name(), GetRaceName(cle->race()), tmpguild, tmpZone, LFG, accinfo); + else if (cle->Admin() >= 80 && admin < 80 && cle->GetGM()) { + iterator.Advance(); + continue; + } + else + sprintf(line, " %s[ANONYMOUS] %s%s%s%s", tmpgm, cle->name(), tmpguild, LFG, accinfo); + } + else if (cle->Anon() == 1) { // Anon + if (admin >= 100 && admin >= cle->Admin()) + sprintf(line, " %s[ANON %i %s] %s (%s)%s zone: %s%s%s", tmpgm, cle->level(), GetEQClassName(cle->class_(),cle->level()), cle->name(), GetRaceName(cle->race()), tmpguild, tmpZone, LFG, accinfo); + else if (cle->Admin() >= 80 && cle->GetGM()) { + iterator.Advance(); + continue; + } + else + sprintf(line, " %s[ANONYMOUS] %s%s%s", tmpgm, cle->name(), LFG, accinfo); + } + else + sprintf(line, " %s[%i %s] %s (%s)%s zone: %s%s%s", tmpgm, cle->level(), GetEQClassName(cle->class_(),cle->level()), cle->name(), GetRaceName(cle->race()), tmpguild, tmpZone, LFG, accinfo); + + AppendAnyLenString(&output, &outsize, &outlen, line); + if (outlen >= 3584) { + connection->SendEmoteMessageRaw(to, 0, 0, 10, output); + safe_delete(output); + outsize = 0; + outlen = 0; + } + else { + if (connection->IsConsole()) + AppendAnyLenString(&output, &outsize, &outlen, "\r\n"); + else + AppendAnyLenString(&output, &outsize, &outlen, "\n"); + } + x++; + if (x >= 20 && admin < 80) + break; + } + iterator.Advance(); + } + + if (x >= 20 && admin < 80) + AppendAnyLenString(&output, &outsize, &outlen, "too many results...20 players shown"); + else + AppendAnyLenString(&output, &outsize, &outlen, "%i players online", x); + if (admin >= 150 && (whom == 0 || whom->gmlookup != 0xFFFF)) { + if (connection->IsConsole()) + AppendAnyLenString(&output, &outsize, &outlen, "\r\n"); + else + AppendAnyLenString(&output, &outsize, &outlen, "\n"); + console_list.SendConsoleWho(connection, to, admin, &output, &outsize, &outlen); + } + if (output) + connection->SendEmoteMessageRaw(to, 0, 0, 10, output); + safe_delete(output); +} + +void ClientList::Add(Client* client) { + list.Insert(client); +} + +Client* ClientList::FindByAccountID(uint32 account_id) { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) { + _log(WORLD__CLIENTLIST, "ClientList[0x%08x]::FindByAccountID(%p) iterator.GetData()[%p]", this, account_id, iterator.GetData()); + if (iterator.GetData()->GetAccountID() == account_id) { + Client* tmp = iterator.GetData(); + return tmp; + } + iterator.Advance(); + } + return 0; +} + +Client* ClientList::FindByName(char* charname) { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) { + _log(WORLD__CLIENTLIST, "ClientList[0x%08x]::FindByName(\"%s\") iterator.GetData()[%p]", this, charname, iterator.GetData()); + if (iterator.GetData()->GetCharName() == charname) { + Client* tmp = iterator.GetData(); + return tmp; + } + iterator.Advance(); + } + return 0; +} + +Client* ClientList::Get(uint32 ip, uint16 port) { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetIP() == ip && iterator.GetData()->GetPort() == port) + { + Client* tmp = iterator.GetData(); + return tmp; + } + iterator.Advance(); + } + return 0; +} + +void ClientList::ZoneBootup(ZoneServer* zs) { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->WaitingForBootup()) { + if (iterator.GetData()->GetZoneID() == zs->GetZoneID() + && iterator.GetData()->GetInstanceID() == zs->GetInstanceID()) { + iterator.GetData()->EnterWorld(false); + } + else if (iterator.GetData()->WaitingForBootup() == zs->GetID()) { + iterator.GetData()->ZoneUnavail(); + } + } + iterator.Advance(); + } +} + +void ClientList::RemoveCLEReferances(ClientListEntry* cle) { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetCLE() == cle) { + iterator.GetData()->SetCLE(0); + } + iterator.Advance(); + } +} + + +bool ClientList::SendPacket(const char* to, ServerPacket* pack) { + if (to == 0 || to[0] == 0) { + zoneserver_list.SendPacket(pack); + return true; + } + else if (to[0] == '*') { + // Cant send a packet to a console.... + return false; + } + else { + ClientListEntry* cle = FindCharacter(to); + if (cle != NULL) { + if (cle->Server() != NULL) { + cle->Server()->SendPacket(pack); + return true; + } + return false; + } else { + ZoneServer* zs = zoneserver_list.FindByName(to); + if (zs != NULL) { + zs->SendPacket(pack); + return true; + } + return false; + } + } + return false; +} + +void ClientList::SendGuildPacket(uint32 guild_id, ServerPacket* pack) { + set zone_ids; + + LinkedListIterator iterator(clientlist); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GuildID() == guild_id) { + zone_ids.insert(iterator.GetData()->zone()); + } + iterator.Advance(); + } + + //now we know all the zones, send it to each one... this is kinda a shitty way to do this + //since its basically O(n^2) + set::iterator cur, end; + cur = zone_ids.begin(); + end = zone_ids.end(); + for(; cur != end; cur++) { + zoneserver_list.SendPacket(*cur, pack); + } +} + +void ClientList::UpdateClientGuild(uint32 char_id, uint32 guild_id) { + LinkedListIterator iterator(clientlist); + + iterator.Reset(); + while(iterator.MoreElements()) { + ClientListEntry *cle = iterator.GetData(); + if (cle->CharID() == char_id) { + cle->SetGuild(guild_id); + } + iterator.Advance(); + } +} + + + +int ClientList::GetClientCount() { + return(numplayers); +} + +void ClientList::GetClients(const char *zone_name, vector &res) { + LinkedListIterator iterator(clientlist); + iterator.Reset(); + + if(zone_name[0] == '\0') { + while(iterator.MoreElements()) { + ClientListEntry* tmp = iterator.GetData(); + res.push_back(tmp); + iterator.Advance(); + } + } else { + uint32 zoneid = database.GetZoneID(zone_name); + while(iterator.MoreElements()) { + ClientListEntry* tmp = iterator.GetData(); + if(tmp->zone() == zoneid) + res.push_back(tmp); + iterator.Advance(); + } + } +} + +void ClientList::SendClientVersionSummary(const char *Name) +{ + uint32 Client62Count = 0; + uint32 ClientTitaniumCount = 0; + uint32 ClientSoFCount = 0; + uint32 ClientSoDCount = 0; + uint32 ClientUnderfootCount = 0; + uint32 ClientRoFCount = 0; + + LinkedListIterator Iterator(clientlist); + + Iterator.Reset(); + + while(Iterator.MoreElements()) + { + ClientListEntry* CLE = Iterator.GetData(); + + if(CLE && CLE->zone()) + { + switch(CLE->GetClientVersion()) + { + case 1: + { + ++Client62Count; + break; + } + case 2: + { + ++ClientTitaniumCount; + break; + } + case 3: + { + ++ClientSoFCount; + break; + } + case 4: + { + ++ClientSoDCount; + break; + } + case 5: + { + ++ClientUnderfootCount; + break; + } + case 6: + { + ++ClientRoFCount; + break; + } + default: + break; + } + } + + Iterator.Advance(); + + } + + zoneserver_list.SendEmoteMessage(Name, 0, 0, 13, "There are %i 6.2, %i Titanium, %i SoF, %i SoD, %i UF, %i RoF clients currently connected.", + Client62Count, ClientTitaniumCount, ClientSoFCount, ClientSoDCount, ClientUnderfootCount, ClientRoFCount); +} diff --git a/world/clientlist.h b/world/clientlist.h new file mode 100644 index 000000000..11dae6a79 --- /dev/null +++ b/world/clientlist.h @@ -0,0 +1,85 @@ +#ifndef CLIENTLIST_H_ +#define CLIENTLIST_H_ + +#include "../common/eq_packet_structs.h" +#include "../common/linked_list.h" +#include "../common/timer.h" +#include "../common/rulesys.h" +#include "../common/servertalk.h" +#include +#include + +class Client; +class ZoneServer; +class WorldTCPConnection; +class ClientListEntry; +class ServerPacket; +struct ServerClientList_Struct; + +class ClientList { +public: + ClientList(); + ~ClientList(); + + void Process(); + + //from old ClientList + void Add(Client* client); + Client* Get(uint32 ip, uint16 port); + Client* FindByAccountID(uint32 account_id); + Client* FindByName(char* charname); + + void ZoneBootup(ZoneServer* zs); + void RemoveCLEReferances(ClientListEntry* cle); + + + //from ZSList + + void SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_Struct* whom, WorldTCPConnection* connection); + void SendFriendsWho(ServerFriendsWho_Struct *FriendsWho, WorldTCPConnection* connection); + void SendOnlineGuildMembers(uint32 FromID, uint32 GuildID); + void SendClientVersionSummary(const char *Name); + void SendLFGMatches(ServerLFGMatchesRequest_Struct *LFGMatchesRequest); + void ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* whom, WorldTCPConnection* connection); + void SendCLEList(const int16& admin, const char* to, WorldTCPConnection* connection, const char* iName = 0); + + bool SendPacket(const char* to, ServerPacket* pack); + void SendGuildPacket(uint32 guild_id, ServerPacket* pack); + + void ClientUpdate(ZoneServer* zoneserver, ServerClientList_Struct* scl); + void CLERemoveZSRef(ZoneServer* iZS); + ClientListEntry* CheckAuth(uint32 iLSID, const char* iKey); + ClientListEntry* CheckAuth(const char* iName, const char* iPassword); + ClientListEntry* CheckAuth(uint32 id, const char* iKey, uint32 ip); + ClientListEntry* FindCharacter(const char* name); + ClientListEntry* FindCLEByAccountID(uint32 iAccID); + ClientListEntry* FindCLEByCharacterID(uint32 iCharID); + ClientListEntry* GetCLE(uint32 iID); + void GetCLEIP(uint32 iIP); + void DisconnectByIP(uint32 iIP); + void EnforceSessionLimit(uint32 iLSAccountID); + void CLCheckStale(); + void CLEKeepAlive(uint32 numupdates, uint32* wid); + void CLEAdd(uint32 iLSID, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = 0, uint32 ip = 0, uint8 local=0); + void UpdateClientGuild(uint32 char_id, uint32 guild_id); + + int GetClientCount(); + void GetClients(const char *zone_name, std::vector &into); + +protected: + inline uint32 GetNextCLEID() { return NextCLEID++; } + + //this is the list of people actively connected to zone + LinkedList list; + + //this is the list of people in any zone, not nescesarily connected to world + Timer CLStale_timer; + uint32 NextCLEID; + LinkedList clientlist; + +}; + + + + +#endif /*CLIENTLIST_H_*/ diff --git a/world/console.cpp b/world/console.cpp new file mode 100644 index 000000000..a1b12aec6 --- /dev/null +++ b/world/console.cpp @@ -0,0 +1,843 @@ +/* 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 "../common/debug.h" +#include +using namespace std; +#include +#include +#include +#include + + +#include "../common/version.h" +#include "console.h" +#include "zoneserver.h" +#include "worlddb.h" +#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/serverinfo.h" +#include "../common/md5.h" +#include "../common/opcodemgr.h" +#include "../common/rulesys.h" +#include "../common/ruletypes.h" +#include "WorldConfig.h" +#include "zoneserver.h" +#include "zonelist.h" +#include "clientlist.h" +#include "LauncherList.h" +#include "ucs.h" +#include "queryserv.h" + +#ifdef _WINDOWS + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#endif + +extern ZSList zoneserver_list; +extern uint32 numzones; +extern LoginServerList loginserverlist; +extern ClientList client_list; +extern LauncherList launcher_list; +extern UCSConnection UCSLink; +extern QueryServConnection QSLink; +extern volatile bool RunLoops; + +ConsoleList console_list; + +Console::Console(EmuTCPConnection* itcpc) +: WorldTCPConnection(), + timeout_timer(RuleI(Console, SessionTimeOut)), + prompt_timer(1000) +{ + tcpc = itcpc; + tcpc->SetEcho(true); + state = 0; + paccountid = 0; + memset(paccountname, 0, sizeof(paccountname)); + admin = 0; + pAcceptMessages = false; +} + +Console::~Console() { + if (tcpc) + tcpc->Free(); +} + +void Console::Die() { + state = CONSOLE_STATE_CLOSED; + struct in_addr in; + in.s_addr = GetIP(); + _log(WORLD__CONSOLE,"Removing console from %s:%d",inet_ntoa(in),GetPort()); + tcpc->Disconnect(); +} + +bool Console::SendChannelMessage(const ServerChannelMessage_Struct* scm) { + if (!pAcceptMessages) + return false; + switch (scm->chan_num) { + if(RuleB(Chat, ServerWideAuction)){ + case 4: { + SendMessage(1, "%s auctions, '%s'", scm->from, scm->message); + break; + } + } + if(RuleB(Chat, ServerWideOOC)){ + case 5: { + SendMessage(1, "%s says ooc, '%s'", scm->from, scm->message); + break; + } + } + case 6: { + SendMessage(1, "%s BROADCASTS, '%s'", scm->from, scm->message); + break; + } + case 7: { + 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; + strcpy(scm2->deliverto, scm2->from); + scm2->noreply = true; + client_list.SendPacket(scm->from, pack); + safe_delete(pack); + break; + } + case 11: { + SendMessage(1, "%s GMSAYS, '%s'", scm->from, scm->message); + break; + } + default: { + return false; + } + } + return true; +} + +bool Console::SendEmoteMessage(uint32 type, const char* message, ...) { + if (!message) + return false; + if (!pAcceptMessages) + return false; + va_list argptr; + char buffer[1024]; + + va_start(argptr, message); + vsnprintf(buffer, sizeof(buffer), message, argptr); + va_end(argptr); + + SendMessage(1, message); + return true; +} + +bool Console::SendEmoteMessageRaw(uint32 type, const char* message) { + if (!message) + return false; + if (!pAcceptMessages) + return false; + SendMessage(1, message); + return true; +} + +void Console::SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...) { + if (!message) + return; + if (to_guilddbid != 0 || to_minstatus > Admin()) + return; + va_list argptr; + char buffer[1024]; + + va_start(argptr, message); + vsnprintf(buffer, sizeof(buffer), message, argptr); + va_end(argptr); + + SendEmoteMessageRaw(to, to_guilddbid, to_minstatus, type, buffer); +} + +void Console::SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message) { + if (!message) + return; + if (to_guilddbid != 0 || to_minstatus > Admin()) + return; + SendMessage(1, message); +} + +void Console::SendMessage(uint8 newline, const char* message, ...) { + if (!message) + return; + char* buffer = 0; + uint32 bufsize = 1500; + if (message) + bufsize += strlen(message); + buffer = new char[bufsize]; + memset(buffer, 0, bufsize); + if (message != 0) { + va_list argptr; + + va_start(argptr, message); + vsnprintf(buffer, bufsize - 512, message, argptr); + va_end(argptr); + } + + if (newline) { + char outbuf[3]; + outbuf[0] = 13; + outbuf[1] = 10; + outbuf[2] = 0; + for (int i=0; i < newline; i++) + strcat(buffer, outbuf); + } + tcpc->Send((uchar*) buffer, strlen(buffer)); + safe_delete_array(buffer); +} + +bool Console::Process() { + if (state == CONSOLE_STATE_CLOSED) + return false; + + if (!tcpc->Connected()) { + struct in_addr in; + in.s_addr = GetIP(); + _log(WORLD__CONSOLE,"Removing console (!tcpc->Connected) from %s:%d",inet_ntoa(in),GetPort()); + return false; + } + //if we have not gotten the special markers after this timer, send login prompt + if(prompt_timer.Check()) { + prompt_timer.Disable(); + if(tcpc->GetMode() == EmuTCPConnection::modeConsole) + tcpc->Send((const uchar*) "Username: ", strlen("Username: ")); + } + + if (timeout_timer.Check()) { + SendMessage(1, 0); + SendMessage(1, "Timeout, disconnecting..."); + struct in_addr in; + in.s_addr = GetIP(); + _log(WORLD__CONSOLE,"TCP connection timeout from %s:%d",inet_ntoa(in),GetPort()); + return false; + } + + if (tcpc->GetMode() == EmuTCPConnection::modePacket) { + struct in_addr in; + in.s_addr = GetIP(); + if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeZone) { + ZoneServer* zs = new ZoneServer(tcpc); + _log(WORLD__CONSOLE,"New zoneserver #%d from %s:%d", zs->GetID(), inet_ntoa(in), GetPort()); + zoneserver_list.Add(zs); + numzones++; + tcpc = 0; + } else if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeLauncher) { + _log(WORLD__CONSOLE,"New launcher from %s:%d", inet_ntoa(in), GetPort()); + launcher_list.Add(tcpc); + tcpc = 0; + } else if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeUCS) + { + _log(WORLD__CONSOLE,"New UCS Connection from %s:%d", inet_ntoa(in), GetPort()); + UCSLink.SetConnection(tcpc); + tcpc = 0; + } + else if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeQueryServ) + { + _log(WORLD__CONSOLE,"New QS Connection from %s:%d", inet_ntoa(in), GetPort()); + QSLink.SetConnection(tcpc); + tcpc = 0; + } else { + _log(WORLD__CONSOLE,"Unsupported packet mode from %s:%d", inet_ntoa(in), GetPort()); + } + return false; + } + char* command = 0; + while ((command = tcpc->PopLine())) { + timeout_timer.Start(); + ProcessCommand(command); + delete command; + } + return true; +} + +void ConsoleList::Add(Console* con) { + list.Insert(con); +} + +void ConsoleList::Process() { + LinkedListIterator iterator(list); + iterator.Reset(); + + while(iterator.MoreElements()) { + if (!iterator.GetData()->Process()) + iterator.RemoveCurrent(); + else + iterator.Advance(); + } +} + +void ConsoleList::KillAll() { + LinkedListIterator iterator(list); + iterator.Reset(); + + while(iterator.MoreElements()) { + iterator.GetData()->Die(); + iterator.RemoveCurrent(); + } +} + +void ConsoleList::SendConsoleWho(WorldTCPConnection* connection, const char* to, int16 admin, char** output, uint32* outsize, uint32* outlen) { + LinkedListIterator iterator(list); + iterator.Reset(); + struct in_addr in; + int x = 0; + + while(iterator.MoreElements()) { + in.s_addr = iterator.GetData()->GetIP(); + if (admin >= iterator.GetData()->Admin()) + AppendAnyLenString(output, outsize, outlen, " Console: %s:%i AccID: %i AccName: %s", inet_ntoa(in), iterator.GetData()->GetPort(), iterator.GetData()->AccountID(), iterator.GetData()->AccountName()); + else + AppendAnyLenString(output, outsize, outlen, " Console: AccID: %i AccName: %s", iterator.GetData()->AccountID(), iterator.GetData()->AccountName()); + if (*outlen >= 3584) { + connection->SendEmoteMessageRaw(to, 0, 0, 10, *output); + safe_delete(*output); + *outsize = 0; + *outlen = 0; + } + else { + if (connection->IsConsole()) + AppendAnyLenString(output, outsize, outlen, "\r\n"); + else + AppendAnyLenString(output, outsize, outlen, "\n"); + } + x++; + iterator.Advance(); + } + AppendAnyLenString(output, outsize, outlen, "%i consoles connected", x); +} + +void ConsoleList::SendChannelMessage(const ServerChannelMessage_Struct* scm) { + LinkedListIterator iterator(list); + iterator.Reset(); + + while(iterator.MoreElements()) { + iterator.GetData()->SendChannelMessage(scm); + iterator.Advance(); + } +} + +void ConsoleList::SendEmoteMessage(uint32 type, const char* message, ...) { + va_list argptr; + char buffer[1024]; + + va_start(argptr, message); + vsnprintf(buffer, sizeof(buffer), message, argptr); + va_end(argptr); + + SendEmoteMessageRaw(type, buffer); +} + +void ConsoleList::SendEmoteMessageRaw(uint32 type, const char* message) { + LinkedListIterator iterator(list); + iterator.Reset(); + + while(iterator.MoreElements()) { + iterator.GetData()->SendEmoteMessageRaw(type, message); + iterator.Advance(); + } +} + +Console* ConsoleList::FindByAccountName(const char* accname) { + LinkedListIterator iterator(list); + iterator.Reset(); + + while(iterator.MoreElements()) { + if (strcasecmp(iterator.GetData()->AccountName(), accname) == 0) + return iterator.GetData(); + + iterator.Advance(); + } + return 0; +} + +void Console::ProcessCommand(const char* command) { + switch(state) + { + case CONSOLE_STATE_USERNAME: + { + if (strlen(command) >= 16) { + SendMessage(1, 0); + SendMessage(2, "Username buffer overflow."); + SendMessage(1, "Bye Bye."); + state = CONSOLE_STATE_CLOSED; + return; + } + strcpy(paccountname, command); + state = CONSOLE_STATE_PASSWORD; + SendMessage(0, "Password: "); + tcpc->SetEcho(false); + break; + } + case CONSOLE_STATE_PASSWORD: + { + if (strlen(command) >= 16) { + SendMessage(1, 0); + SendMessage(2, "Password buffer overflow."); + SendMessage(1, "Bye Bye."); + state = CONSOLE_STATE_CLOSED; + return; + } + paccountid = database.CheckLogin(paccountname,command); + if (paccountid == 0) { + SendMessage(1, 0); + SendMessage(2, "Login failed."); + SendMessage(1, "Bye Bye."); + state = CONSOLE_STATE_CLOSED; + return; + } + database.GetAccountName(paccountid, paccountname); // fixes case and stuff + admin = database.CheckStatus(paccountid); + if (!(admin >= consoleLoginStatus)) { + SendMessage(1, 0); + SendMessage(2, "Access denied."); + SendMessage(1, "Bye Bye."); + state = CONSOLE_STATE_CLOSED; + return; + } + _log(WORLD__CONSOLE,"TCP console authenticated: Username=%s, Admin=%d",paccountname,admin); + SendMessage(1, 0); + SendMessage(2, "Login accepted."); + state = CONSOLE_STATE_CONNECTED; + tcpc->SetEcho(true); + SendPrompt(); + break; + } + case CONSOLE_STATE_CONNECTED: { + _log(WORLD__CONSOLE,"TCP command: %s: \"%s\"",paccountname,command); + Seperator sep(command); + if (strcasecmp(sep.arg[0], "help") == 0 || strcmp(sep.arg[0], "?") == 0) { + SendMessage(1, " whoami"); + SendMessage(1, " who"); + SendMessage(1, " zonestatus"); + SendMessage(1, " uptime [zoneID#]"); + SendMessage(1, " emote [zonename or charname or world] [type] [message]"); + SendMessage(1, " echo [on/off]"); + SendMessage(1, " acceptmessages [on/off]"); + SendMessage(1, " tell [name] [message]"); + SendMessage(1, " broadcast [message]"); + SendMessage(1, " gmsay [message]"); + SendMessage(1, " ooc [message]"); + SendMessage(1, " auction [message]"); + if (admin >= consoleKickStatus) + SendMessage(1, " kick [charname]"); + if (admin >= consoleLockStatus) + SendMessage(1, " lock/unlock"); + if (admin >= consoleZoneStatus) { + SendMessage(1, " zoneshutdown [zonename or ZoneServerID]"); + SendMessage(1, " zonebootup [ZoneServerID] [zonename]"); + SendMessage(1, " zonelock [list|lock|unlock] [zonename]"); + } + if (admin >= consoleFlagStatus) + SendMessage(1, " flag [status] [accountname]"); + if (admin >= consolePassStatus) + SendMessage(1, " setpass [accountname] [newpass]"); + if (admin >= consoleWorldStatus) { + SendMessage(1, " version"); + SendMessage(1, " worldshutdown"); + } + if (admin >= 201) { + SendMessage(1, " IPLookup [name]"); + } + if (admin >= 100) { + SendMessage(1, " LSReconnect"); + SendMessage(1, " signalcharbyname charname ID"); + } + } + else if (strcasecmp(sep.arg[0], "ping") == 0) { + // do nothing + } + else if (strcasecmp(sep.arg[0], "signalcharbyname") == 0) { + SendMessage(1, "Signal Sent to %s with ID %i", (char*) sep.arg[1], atoi(sep.arg[2])); + uint32 message_len = strlen((char*) sep.arg[1]) + 1; + ServerPacket* pack = new ServerPacket(ServerOP_CZSignalClientByName, sizeof(CZClientSignalByName_Struct) + message_len); + CZClientSignalByName_Struct* CZSC = (CZClientSignalByName_Struct*) pack->pBuffer; + strn0cpy(CZSC->Name, (char*) sep.arg[1], 64); + CZSC->data = atoi(sep.arg[2]); + zoneserver_list.SendPacket(pack); + safe_delete(pack); + } + else if (strcasecmp(sep.arg[0], "setpass") == 0 && admin >= consolePassStatus) { + if (sep.argnum != 2) + SendMessage(1, "Format: setpass accountname password"); + else { + + int16 tmpstatus = 0; + uint32 tmpid = database.GetAccountIDByName(sep.arg[1], &tmpstatus); + if (!tmpid) + SendMessage(1, "Error: Account not found"); + else if (tmpstatus > admin) + SendMessage(1, "Cannot change password: Account's status is higher than yours"); + else if (database.SetLocalPassword(tmpid, sep.arg[2])) + SendMessage(1, "Password changed."); + else + SendMessage(1, "Error changing password."); + } + } + else if (strcasecmp(sep.arg[0], "uptime") == 0) { + if (sep.IsNumber(1) && atoi(sep.arg[1]) > 0) { + ServerPacket* pack = new ServerPacket(ServerOP_Uptime, sizeof(ServerUptime_Struct)); + ServerUptime_Struct* sus = (ServerUptime_Struct*) pack->pBuffer; + snprintf(sus->adminname, sizeof(sus->adminname), "*%s", this->GetName()); + sus->zoneserverid = atoi(sep.arg[1]); + ZoneServer* zs = zoneserver_list.FindByID(sus->zoneserverid); + if (zs) + zs->SendPacket(pack); + else + SendMessage(1, "Zoneserver not found."); + delete pack; + } + else { + ZSList::ShowUpTime(this); + } + } + else if (strcasecmp(sep.arg[0], "md5") == 0) { + uint8 md5[16]; + MD5::Generate((const uchar*) sep.argplus[1], strlen(sep.argplus[1]), md5); + SendMessage(1, "MD5: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", md5[0], md5[1], md5[2], md5[3], md5[4], md5[5], md5[6], md5[7], md5[8], md5[9], md5[10], md5[11], md5[12], md5[13], md5[14], md5[15]); + } + else if (strcasecmp(sep.arg[0], "whoami") == 0) { + SendMessage(1, "You are logged in as '%s'", this->AccountName()); + SendMessage(1, "You are known as '*%s'", this->AccountName()); + SendMessage(1, "AccessLevel: %d", this->Admin()); + } + else if (strcasecmp(sep.arg[0], "echo") == 0) { + if (strcasecmp(sep.arg[1], "on") == 0) + tcpc->SetEcho(true); + else if (strcasecmp(sep.arg[1], "off") == 0) { + if (pAcceptMessages) + SendMessage(1, "Echo can not be turned off while acceptmessages is on"); + else + tcpc->SetEcho(false); + } + else + SendMessage(1, "Usage: echo [on/off]"); + } + else if (strcasecmp(sep.arg[0], "acceptmessages") == 0) { + if (strcasecmp(sep.arg[1], "on") == 0) + if (tcpc->GetEcho()) + SendMessage(1, "AcceptMessages can not be turned on while echo is on"); + else + pAcceptMessages = true; + else if (strcasecmp(sep.arg[1], "off") == 0) + pAcceptMessages = false; + else + SendMessage(1, "Usage: acceptmessages [on/off]"); + } + else if (strcasecmp(sep.arg[0], "tell") == 0) { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + zoneserver_list.SendChannelMessage(tmpname, sep.arg[1], 7, 0, sep.argplus[2]); + } + else if (strcasecmp(sep.arg[0], "broadcast") == 0) { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + zoneserver_list.SendChannelMessage(tmpname, 0, 6, 0, sep.argplus[1]); + } + else if (strcasecmp(sep.arg[0], "ooc") == 0) { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + zoneserver_list.SendChannelMessage(tmpname, 0, 5, 0, sep.argplus[1]); + } + else if (strcasecmp(sep.arg[0], "auction") == 0) { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + zoneserver_list.SendChannelMessage(tmpname, 0, 4, 0, sep.argplus[1]); + } + else if (strcasecmp(sep.arg[0], "gmsay") == 0 || strcasecmp(sep.arg[0], "pr") == 0) { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + zoneserver_list.SendChannelMessage(tmpname, 0, 11, 0, sep.argplus[1]); + } + else if (strcasecmp(sep.arg[0], "emote") == 0) { + if (strcasecmp(sep.arg[1], "world") == 0) + zoneserver_list.SendEmoteMessageRaw(0, 0, 0, atoi(sep.arg[2]), sep.argplus[3]); + else { + ZoneServer* zs = zoneserver_list.FindByName(sep.arg[1]); + if (zs != 0) + zs->SendEmoteMessageRaw(0, 0, 0, atoi(sep.arg[2]), sep.argplus[3]); + else + zoneserver_list.SendEmoteMessageRaw(sep.arg[1], 0, 0, atoi(sep.arg[2]), sep.argplus[3]); + } + } + else if (strcasecmp(sep.arg[0], "movechar") == 0) { + if(sep.arg[1][0]==0 || sep.arg[2][0] == 0) + SendMessage(1, "Usage: movechar [charactername] [zonename]"); + else { + if (!database.GetZoneID(sep.arg[2])) + SendMessage(1, "Error: Zone '%s' not found", sep.arg[2]); + else if (!database.CheckUsedName((char*) sep.arg[1])) { + if (!database.MoveCharacterToZone((char*) sep.arg[1], (char*) sep.arg[2])) + SendMessage(1, "Character Move Failed!"); + else + SendMessage(1, "Character has been moved."); + } + else + SendMessage(1, "Character Does Not Exist"); + } + } + else if (strcasecmp(sep.arg[0], "flag") == 0 && this->Admin() >= consoleFlagStatus) { +// SCORPIOUS2K - reversed parameter order for flag + if(sep.arg[2][0]==0 || !sep.IsNumber(1)) + SendMessage(1, "Usage: flag [status] [accountname]"); + else + { + if (atoi(sep.arg[1]) > this->Admin()) + SendMessage(1, "You cannot set people's status to higher than your own"); + else if (atoi(sep.arg[1]) < 0 && this->Admin() < consoleFlagStatus) + SendMessage(1, "You have too low of status to change flags"); + else if (!database.SetAccountStatus(sep.arg[2], atoi(sep.arg[1]))) + SendMessage(1, "Unable to flag account!"); + else + SendMessage(1, "Account Flaged"); + } + } + else if (strcasecmp(sep.arg[0], "kick") == 0 && admin >= consoleKickStatus) { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_KickPlayer; + pack->size = sizeof(ServerKickPlayer_Struct); + pack->pBuffer = new uchar[pack->size]; + ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; + strcpy(skp->adminname, tmpname); + strcpy(skp->name, sep.arg[1]); + skp->adminrank = this->Admin(); + zoneserver_list.SendPacket(pack); + delete pack; + } + else if (strcasecmp(sep.arg[0], "who") == 0) { + Who_All_Struct* whom = new Who_All_Struct; + memset(whom, 0, sizeof(Who_All_Struct)); + whom->lvllow = 0xFFFF; + whom->lvlhigh = 0xFFFF; + whom->wclass = 0xFFFF; + whom->wrace = 0xFFFF; + whom->gmlookup = 0xFFFF; + for (int i=1; i<=sep.argnum; i++) { + if (strcasecmp(sep.arg[i], "gm") == 0) + whom->gmlookup = 1; + else if (sep.IsNumber(i)) { + if (whom->lvllow == 0xFFFF) { + whom->lvllow = atoi(sep.arg[i]); + whom->lvlhigh = whom->lvllow; + } + else if (atoi(sep.arg[i]) > int(whom->lvllow)) + whom->lvlhigh = atoi(sep.arg[i]); + else + whom->lvllow = atoi(sep.arg[i]); + } + else + strn0cpy(whom->whom, sep.arg[i], sizeof(whom->whom)); + } + client_list.ConsoleSendWhoAll(0, admin, whom, this); + delete whom; + } + else if (strcasecmp(sep.arg[0], "zonestatus") == 0) { + zoneserver_list.SendZoneStatus(0, admin, this); + } + else if (strcasecmp(sep.arg[0], "exit") == 0 || strcasecmp(sep.arg[0], "quit") == 0) { + SendMessage(1, "Bye Bye."); + state = CONSOLE_STATE_CLOSED; + } + else if (strcasecmp(sep.arg[0], "zoneshutdown") == 0 && admin >= consoleZoneStatus) { + if (sep.arg[1][0] == 0) { + SendMessage(1, "Usage: zoneshutdown zoneshortname"); + } else { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + + ServerPacket* pack = new ServerPacket; + pack->size = sizeof(ServerZoneStateChange_struct); + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer, 0, sizeof(ServerZoneStateChange_struct)); + ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer; + pack->opcode = ServerOP_ZoneShutdown; + strcpy(s->adminname, tmpname); + if (sep.arg[1][0] >= '0' && sep.arg[1][0] <= '9') + s->ZoneServerID = atoi(sep.arg[1]); + else + s->zoneid = database.GetZoneID(sep.arg[1]); + + ZoneServer* zs = 0; + if (s->ZoneServerID != 0) + zs = zoneserver_list.FindByID(s->ZoneServerID); + else if (s->zoneid != 0) + zs = zoneserver_list.FindByName(database.GetZoneName(s->zoneid)); + else + SendMessage(1, "Error: ZoneShutdown: neither ID nor name specified"); + + if (zs == 0) + SendMessage(1, "Error: ZoneShutdown: zoneserver not found"); + else + zs->SendPacket(pack); + + delete pack; + } + } + else if (strcasecmp(sep.arg[0], "zonebootup") == 0 && admin >= consoleZoneStatus) { + if (sep.arg[2][0] == 0 || !sep.IsNumber(1)) { + SendMessage(1, "Usage: zonebootup ZoneServerID# zoneshortname"); + } else { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + + _log(WORLD__CONSOLE,"Console ZoneBootup: %s, %s, %s",tmpname,sep.arg[2],sep.arg[1]); + zoneserver_list.SOPZoneBootup(tmpname, atoi(sep.arg[1]), sep.arg[2], (bool) (strcasecmp(sep.arg[3], "static") == 0)); + } + } + else if (strcasecmp(sep.arg[0], "worldshutdown") == 0 && admin >= consoleWorldStatus) { + ServerPacket* pack = new ServerPacket(ServerOP_ShutdownAll); + zoneserver_list.SendPacket(pack); + delete pack; + SendMessage(1, "Sending shutdown packet... goodbye."); + CatchSignal(0); + } + else if (strcasecmp(sep.arg[0], "lock") == 0 && admin >= consoleLockStatus) { + WorldConfig::LockWorld(); + if (loginserverlist.Connected()) { + loginserverlist.SendStatus(); + SendMessage(1, "World locked."); + } + else { + SendMessage(1, "World locked, but login server not connected."); + } + } + else if (strcasecmp(sep.arg[0], "unlock") == 0 && admin >= consoleLockStatus) { + WorldConfig::UnlockWorld(); + if (loginserverlist.Connected()) { + loginserverlist.SendStatus(); + SendMessage(1, "World unlocked."); + } + else { + SendMessage(1, "World unlocked, but login server not connected."); + } + } + else if (strcasecmp(sep.arg[0], "version") == 0 && admin >= consoleWorldStatus) { + SendMessage(1, "Current version information."); + SendMessage(1, " %s", CURRENT_WORLD_VERSION); + SendMessage(1, " Compiled on: %s at %s", COMPILE_DATE, COMPILE_TIME); + SendMessage(1, " Last modified on: %s", LAST_MODIFIED); + } + else if (strcasecmp(sep.arg[0], "serverinfo") == 0 && admin >= 200) { + if (strcasecmp(sep.arg[1], "os") == 0) { + #ifdef _WINDOWS + GetOS(); + char intbuffer [sizeof(unsigned long)]; + SendMessage(1, "Operating system information."); + SendMessage(1, " %s", Ver_name); + SendMessage(1, " Build number: %s", ultoa(Ver_build, intbuffer, 10)); + SendMessage(1, " Minor version: %s", ultoa(Ver_min, intbuffer, 10)); + SendMessage(1, " Major version: %s", ultoa(Ver_maj, intbuffer, 10)); + SendMessage(1, " Platform Id: %s", ultoa(Ver_pid, intbuffer, 10)); + #else + char os_string[100]; + SendMessage(1, "Operating system information."); + SendMessage(1, " %s", GetOS(os_string)); + #endif + } + else { + SendMessage(1, "Usage: Serverinfo [type]"); + SendMessage(1, " OS - Operating system version information."); + } + } + else if (strcasecmp(sep.arg[0], "IPLookup") == 0 && admin >= 201) { + client_list.SendCLEList(admin, 0, this, sep.argplus[1]); + } + else if (strcasecmp(sep.arg[0], "LSReconnect") == 0 && admin >= 100) { + #ifdef _WINDOWS + _beginthread(AutoInitLoginServer, 0, NULL); + #else + pthread_t thread; + pthread_create(&thread, NULL, &AutoInitLoginServer, NULL); + #endif + RunLoops = true; + SendMessage(1, " Login Server Reconnect manually restarted by Console"); + _log(WORLD__CONSOLE,"Login Server Reconnect manually restarted by Console"); + } + else if (strcasecmp(sep.arg[0], "zonelock") == 0 && admin >= consoleZoneStatus) { + if (strcasecmp(sep.arg[1], "list") == 0) { + zoneserver_list.ListLockedZones(0, this); + } + else if (strcasecmp(sep.arg[1], "lock") == 0 && admin >= 101) { + uint16 tmp = database.GetZoneID(sep.arg[2]); + if (tmp) { + if (zoneserver_list.SetLockedZone(tmp, true)) + zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone locked: %s", database.GetZoneName(tmp)); + else + SendMessage(1, "Failed to change lock"); + } + else + SendMessage(1, "Usage: #zonelock lock [zonename]"); + } + else if (strcasecmp(sep.arg[1], "unlock") == 0 && admin >= 101) { + uint16 tmp = database.GetZoneID(sep.arg[2]); + if (tmp) { + if (zoneserver_list.SetLockedZone(tmp, false)) + zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone unlocked: %s", database.GetZoneName(tmp)); + else + SendMessage(1, "Failed to change lock"); + } + else + SendMessage(1, "Usage: #zonelock unlock [zonename]"); + } + else { + SendMessage(1, "#zonelock sub-commands"); + SendMessage(1, " list"); + if (admin >= 101) { + SendMessage(1, " lock [zonename]"); + SendMessage(1, " unlock [zonename]"); + } + } + } + else { + SendMessage(1, "Command unknown."); + } + if (state == CONSOLE_STATE_CONNECTED) + SendPrompt(); + break; + } + default: { + break; + } + } +} + +void Console::SendPrompt() { + if (tcpc->GetEcho()) + SendMessage(0, "%s> ", paccountname); +} diff --git a/world/console.h b/world/console.h new file mode 100644 index 000000000..2a595e598 --- /dev/null +++ b/world/console.h @@ -0,0 +1,108 @@ +/* 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 CONSOLE_H +#define CONSOLE_H + +enum { + consoleLoginStatus = 50, //ability to log in, basic commands. + httpLoginStatus = 100, //can log into the HTTP interface + consoleFlagStatus = 200, //flag + consoleKickStatus = 150, //kick + consoleLockStatus = 150, //world lock/unlock + consoleZoneStatus = 150, //zone up/down/lock + consolePassStatus = 200, //change password + consoleWorldStatus = 200, //world shutdown + consoleOpcodesStatus = 250 +}; + +#define CONSOLE_STATE_USERNAME 0 +#define CONSOLE_STATE_PASSWORD 1 +#define CONSOLE_STATE_CONNECTED 2 +#define CONSOLE_STATE_CLOSED 3 + +#include "../common/linked_list.h" +#include "../common/timer.h" +#include "../common/queue.h" +#include "../common/EmuTCPConnection.h" +#include "WorldTCPConnection.h" +#include "../common/Mutex.h" + +struct ServerChannelMessage_Struct; + +class Console : public WorldTCPConnection { +public: + Console(EmuTCPConnection* itcpc); + virtual ~Console(); + virtual inline bool IsConsole() { return true; } + + bool Process(); + void Send(const char* message); + int16 Admin() { return admin; } + uint32 GetIP() { return tcpc->GetrIP(); } + uint16 GetPort() { return tcpc->GetrPort(); } + void ProcessCommand(const char* command); + void Die(); + + bool SendChannelMessage(const ServerChannelMessage_Struct* scm); + bool SendEmoteMessage(uint32 type, const char* message, ...); + bool SendEmoteMessageRaw(uint32 type, const char* message); + void SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...); + void SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message); + void SendMessage(uint8 newline, const char* message, ...); + + const char* GetName() { return paccountname; } + const char* AccountName() { return paccountname; } + uint32 AccountID() { return paccountid; } +private: + EmuTCPConnection* tcpc; + + Timer timeout_timer; + Timer prompt_timer; + + void SendPrompt(); + + uint32 paccountid; + char paccountname[30]; + bool pAcceptMessages; + + uint8 state; + + int16 admin; + uchar textbuf[1024]; + int bufindex; +}; + +class ConsoleList +{ +public: + ConsoleList() {} + ~ConsoleList() {} + + void Add(Console* con); + void Process(); + void KillAll(); + + void SendChannelMessage(const ServerChannelMessage_Struct* scm); + void SendConsoleWho(WorldTCPConnection* connection, const char* to, int16 admin, char** output, uint32* outsize, uint32* outlen); + void SendEmoteMessage(uint32 type, const char* message, ...); + void SendEmoteMessageRaw(uint32 type, const char* message); + Console* FindByAccountName(const char* accname); +private: + LinkedList list; +}; +#endif diff --git a/world/lfplist.cpp b/world/lfplist.cpp new file mode 100644 index 000000000..da1c43a1c --- /dev/null +++ b/world/lfplist.cpp @@ -0,0 +1,279 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2008 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 "lfplist.h" +#include "cliententry.h" +#include "clientlist.h" +#include "zoneserver.h" +#include "zonelist.h" +#include "../common/logsys.h" +#include "../common/MiscFunctions.h" + +extern ClientList client_list; +extern ZSList zoneserver_list; + +GroupLFP::GroupLFP(uint32 inLeaderID) { + + LeaderID = inLeaderID; + for(unsigned int i=0; iAction can be 1 or 255. + // If it is 255, we update the members only, not the level/class filters. + // + if(Update->Action != 255) { + MatchFilter = Update->MatchFilter; + FromLevel = Update->FromLevel; + ToLevel = Update->ToLevel; + Classes = Update->Classes; + strcpy(Comments, Update->Comments); + } + + for(unsigned int i=0; iMembers[i].Name); + // If we were passed the class/level and zone information, use that. + if(Update->Members[i].Class && Update->Members[i].Level && Update->Members[i].Zone) { + Members[i].Class = Update->Members[i].Class; + Members[i].Level = Update->Members[i].Level; + Members[i].Zone = Update->Members[i].Zone; + Members[i].GuildID = Update->Members[i].GuildID; + } + // Otherwise try and find the information ourselves. + else { + CLE = client_list.FindCharacter(Members[i].Name); + if(CLE) { + Members[i].Class = CLE->class_(); + Members[i].Level = CLE->level(); + Members[i].Zone = CLE->zone(); + Members[i].GuildID = CLE->GuildID(); + } + else { + Members[i].Class = 0; + Members[i].Level = 0; + Members[i].Zone = 0; + Members[i].GuildID = 0xFFFF; + } + } + } +} + +void GroupLFP::RemoveMember(int Index) { + + Members[Index].Name[0] = '\0'; +} + +GroupLFPList::GroupLFPList() : LFPStaleTimer(60000) { + +} + +void GroupLFPList::Process() { + + // Once a minute, check for clients in a LFP group who are no longer connected, and remove them. + // If the client that posted the LFP group has gone, remove the entire LFP entry. + // + // We also update the level, class and zone for each member of the group. Their class will usually + // never change, but if the LFP group is posted while a member is zoning, it will initially be + // 'Unknown Class', so we will fill it in here. + + if(!LFPStaleTimer.Check()) + return; + + GroupLFP* Group; + + LinkedListIterator Iterator(LFPGroupList); + Iterator.Reset(); + + while(Iterator.MoreElements()) { + Group = Iterator.GetData(); + int MemberCount = 0; + if(Group) { + GroupLFPMemberEntry* GroupMembers = Group->Members; + if(!GroupMembers) { + Iterator.Advance(); + continue; + } + for(unsigned int i=0; iRemoveMember(i); + } + else { + // If the level/class/zone information we have is valid, update + // the player's information. + // + if(CLE->level() > 0) + GroupMembers[i].Level = CLE->level(); + + if(CLE->class_() > 0) + GroupMembers[i].Class = CLE->class_(); + + if(CLE->zone() > 0) + GroupMembers[i].Zone = CLE->zone(); + + MemberCount++; + } + } + if(MemberCount == 0) { + // If the leader or all the members are not online, remove the entry. + Iterator.RemoveCurrent(); + continue; + } + } + Iterator.Advance(); + } +} + +void GroupLFPList::RemoveGroup(ServerLFPUpdate_Struct *Update) { + + GroupLFP* Group; + + LinkedListIterator Iterator(LFPGroupList); + Iterator.Reset(); + + while(Iterator.MoreElements()) { + Group = Iterator.GetData(); + if(Group && (Group->GetID() == Update->LeaderID)) { + Iterator.RemoveCurrent(); + return; + } + Iterator.Advance(); + } +} + +void GroupLFPList::UpdateGroup(ServerLFPUpdate_Struct *Update) { + + GroupLFP* Group; + + LinkedListIterator Iterator(LFPGroupList); + Iterator.Reset(); + + while(Iterator.MoreElements()) { + Group = Iterator.GetData(); + if(Group && (Group->GetID() == Update->LeaderID)) { + Group->SetDetails(Update); + return; + } + Iterator.Advance(); + } + + Group = new GroupLFP(Update->LeaderID); + + if(Group) { + Group->SetDetails(Update); + LFPGroupList.Append(Group); + } + +} + +void GroupLFPList::SendLFPMatches(ServerLFPMatchesRequest_Struct* smrs) { + + int Matches = 0; + GroupLFP* Group; + + LinkedListIterator Iterator(LFPGroupList); + Iterator.Reset(); + + while(Iterator.MoreElements()) { + Group = Iterator.GetData(); + Iterator.Advance(); + + if(Group) { + // Just check if the leader is within the requested level range. + if((Group->Members[0].Level < smrs->FromLevel) || (Group->Members[0].Level > smrs->ToLevel)) + continue; + + // If the Player putting up the LFP request specified MatchFilter = true, then anyone + // searching for groups LFP must meet the specified criteria to see this group. + if(Group->MatchFilter) { + unsigned int BitMask = 1 << smrs->QuerierClass; + if(!(BitMask & Group->Classes)) continue; + if(!((smrs->QuerierLevel >= Group->FromLevel) && (smrs->QuerierLevel <= Group->ToLevel))) continue; + } + Matches++; + } + } + + ServerPacket* Pack = new ServerPacket(ServerOP_LFPMatches, (sizeof(ServerLFPMatchesResponse_Struct) * Matches) + 4); + + char *Buf = (char *)Pack->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buf, smrs->FromID); + + ServerLFPMatchesResponse_Struct* Buffer = (ServerLFPMatchesResponse_Struct*)Buf; + + Iterator.Reset(); + + if(Matches) { + while(Iterator.MoreElements() && (Matches > 0)) { + Group = Iterator.GetData(); + Iterator.Advance(); + + if(Group) { + if((Group->Members[0].Level < smrs->FromLevel) || (Group->Members[0].Level > smrs->ToLevel)) + continue; + + if(Group->MatchFilter) { + unsigned int BitMask = 1 << smrs->QuerierClass; + if(!(BitMask & Group->Classes)) continue; + if(!((smrs->QuerierLevel >= Group->FromLevel) && (smrs->QuerierLevel <= Group->ToLevel))) continue; + } + Buffer->FromLevel = Group->FromLevel; + Buffer->ToLevel = Group->ToLevel; + Buffer->Classes = Group->Classes; + memcpy(Buffer->Members, Group->Members, 64 * MAX_GROUP_MEMBERS); + strcpy(Buffer->Comments, Group->Comments); + Matches--; + Buffer++; + } + } + Pack->Deflate(); + } + + ClientListEntry* CLE = client_list.FindCharacter(smrs->FromName); + if (CLE != NULL) { + if (CLE->Server() != NULL) + CLE->Server()->SendPacket(Pack); + } + else { + ZoneServer* zs = zoneserver_list.FindByName(smrs->FromName); + if (zs != NULL) + zs->SendPacket(Pack); + } + safe_delete(Pack); +} + diff --git a/world/lfplist.h b/world/lfplist.h new file mode 100644 index 000000000..a829603ca --- /dev/null +++ b/world/lfplist.h @@ -0,0 +1,66 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2008 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 LFPENTRY_H +#define LFPENTRY_H + +#include "../common/eq_packet_structs.h" +#include "../common/servertalk.h" +#include "../common/linked_list.h" +#include "../common/timer.h" + +class GroupLFP { + +public: + GroupLFP(uint32 LeaderID); + + void SetDetails(ServerLFPUpdate_Struct *Update); + inline GroupLFPMemberEntry* GetMembers() { return Members; } + inline uint32 GetID() { return LeaderID; } + void RemoveMember(int Index); + + friend class GroupLFPList; + +private: + uint32 LeaderID; + uint8 MatchFilter; + uint32 FromLevel; + uint32 ToLevel; + uint32 Classes; + char Comments[64]; + GroupLFPMemberEntry Members[MAX_GROUP_MEMBERS]; + +}; + +class GroupLFPList { + +public: + GroupLFPList(); + void UpdateGroup(ServerLFPUpdate_Struct *Update); + void RemoveGroup(ServerLFPUpdate_Struct *Update); + void SendLFPMatches(ServerLFPMatchesRequest_Struct* smrs); + void Process(); + +private: + LinkedList LFPGroupList; + Timer LFPStaleTimer; + + +}; + +#endif diff --git a/world/net.cpp b/world/net.cpp new file mode 100644 index 000000000..3ca7490bb --- /dev/null +++ b/world/net.cpp @@ -0,0 +1,568 @@ +/* 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 "../common/debug.h" + +#include +using namespace std; +#include +#include +#include + +#include + +#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/seperator.h" +#include "../common/version.h" +#include "../common/eqtime.h" +#include "../common/timeoutmgr.h" +#include "../common/EQEMuError.h" +#include "../common/opcodemgr.h" +#include "../common/guilds.h" +#include "../common/EQStreamIdent.h" +//#include "../common/patches/Client62.h" +#include "../common/rulesys.h" +#include "../common/platform.h" +#include "../common/crash.h" +#ifdef _WINDOWS + #include + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp + #include +#else + #include + #include "../common/unix.h" + #include + #include + #include + #include + #ifndef FREEBSD + union semun { + int val; + struct semid_ds *buf; + ushort *array; + struct seminfo *__buf; + void *__pad; + }; + #endif + +#endif + +#include "../common/EMuShareMem.h" +extern LoadEMuShareMemDLL EMuShareMemDLL; + +/* +Zone only right now. +#ifdef EQPROFILE +#ifdef COMMON_PROFILE +CommonProfiler _cp; +#endif +#endif*/ + +#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 "zoneserver.h" +#include "zonelist.h" +#include "clientlist.h" +#include "LauncherList.h" +#include "wguild_mgr.h" +#include "lfplist.h" +#include "AdventureManager.h" +#include "ucs.h" +#include "queryserv.h" + +TimeoutManager timeout_manager; +EQStreamFactory eqsf(WorldStream,9000); +EmuTCPServer tcps; +ClientList client_list; +GroupLFPList LFPGroupList; +ZSList zoneserver_list; +LoginServerList loginserverlist; +EQWHTTPServer http_server; +UCSConnection UCSLink; +QueryServConnection QSLink; +LauncherList launcher_list; +AdventureManager adventure_manager; +DBAsync *dbasync = NULL; +RuleManager *rules = new RuleManager(); +volatile bool RunLoops = true; +uint32 numclients = 0; +uint32 numzones = 0; +bool holdzones = false; + + +extern ConsoleList console_list; + +void CatchSignal(int sig_num); + +int main(int argc, char** argv) { + RegisterExecutablePlatform(ExePlatformWorld); + set_exception_handler(); + + // Load server configuration + _log(WORLD__INIT, "Loading server configuration.."); + if (!WorldConfig::LoadConfig()) { + _log(WORLD__INIT_ERR, "Loading server configuration failed."); + return(1); + } + const WorldConfig *Config=WorldConfig::get(); + + if(!load_log_settings(Config->LogSettingsFile.c_str())) + _log(WORLD__INIT, "Warning: Unable to read %s", Config->LogSettingsFile.c_str()); + else + _log(WORLD__INIT, "Log settings loaded from %s", Config->LogSettingsFile.c_str()); + + + _log(WORLD__INIT, "CURRENT_WORLD_VERSION:%s", CURRENT_WORLD_VERSION); + + #ifdef _DEBUG + _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); + #endif + + if (signal(SIGINT, CatchSignal) == SIG_ERR) { + _log(WORLD__INIT_ERR, "Could not set signal handler"); + return 0; + } + if (signal(SIGTERM, CatchSignal) == SIG_ERR) { + _log(WORLD__INIT_ERR, "Could not set signal handler"); + return 0; + } + #ifndef WIN32 + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { + _log(WORLD__INIT_ERR, "Could not set signal handler"); + return 0; + } + #endif + + // add login server config to list + if (Config->LoginCount == 0) { + if (Config->LoginHost.length()) { + loginserverlist.Add(Config->LoginHost.c_str(), Config->LoginPort, Config->LoginAccount.c_str(), Config->LoginPassword.c_str()); + _log(WORLD__INIT, "Added loginserver %s:%i", Config->LoginHost.c_str(), Config->LoginPort); + } + } else { + LinkedList loginlist=Config->loginlist; + LinkedListIterator iterator(loginlist); + iterator.Reset(); + while(iterator.MoreElements()) { + loginserverlist.Add(iterator.GetData()->LoginHost.c_str(), iterator.GetData()->LoginPort, iterator.GetData()->LoginAccount.c_str(), iterator.GetData()->LoginPassword.c_str()); + _log(WORLD__INIT, "Added loginserver %s:%i", iterator.GetData()->LoginHost.c_str(), iterator.GetData()->LoginPort); + iterator.Advance(); + } + } + + _log(WORLD__INIT, "Connecting to MySQL..."); + if (!database.Connect( + Config->DatabaseHost.c_str(), + Config->DatabaseUsername.c_str(), + Config->DatabasePassword.c_str(), + Config->DatabaseDB.c_str(), + Config->DatabasePort)) { + _log(WORLD__INIT_ERR, "Cannot continue without a database connection."); + return(1); + } + dbasync = new DBAsync(&database); + guild_mgr.SetDatabase(&database); + + if (argc >= 2) { + char tmp[2]; + if (strcasecmp(argv[1], "help") == 0 || strcasecmp(argv[1], "?") == 0 || strcasecmp(argv[1], "/?") == 0 || strcasecmp(argv[1], "-?") == 0 || strcasecmp(argv[1], "-h") == 0 || strcasecmp(argv[1], "-help") == 0) { + cout << "Worldserver command line commands:" << endl; + cout << "adduser username password flag - adds a user account" << endl; + cout << "flag username flag - sets GM flag on the account" << endl; + cout << "startzone zoneshortname - sets the starting zone" << endl; + cout << "-holdzones - reboots lost zones" << endl; + return 0; + } + else if (strcasecmp(argv[1], "-holdzones") == 0) { + cout << "Reboot Zones mode ON" << endl; + holdzones = true; + } + else if (database.GetVariable("disablecommandline", tmp, 2)) { + if (strlen(tmp) == 1) { + if (tmp[0] == '1') { + cout << "Command line disabled in database... exiting" << endl; + return 0; + } + } + } + else if (strcasecmp(argv[1], "adduser") == 0) { + if (argc == 5) { + if (Seperator::IsNumber(argv[4])) { + if (atoi(argv[4]) >= 0 && atoi(argv[4]) <= 255) { + if (database.CreateAccount(argv[2], argv[3], atoi(argv[4])) == 0) + cout << "database.CreateAccount failed." << endl; + else + cout << "Account created: Username='" << argv[2] << "', Password='" << argv[3] << "', status=" << argv[4] << endl; + return 0; + } + } + } + cout << "Usage: world adduser username password flag" << endl; + cout << "flag = 0, 1 or 2" << endl; + return 0; + } + 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]))) + cout << "Account flagged: Username='" << argv[2] << "', status=" << argv[3] << endl; + else + cout << "database.SetAccountStatus failed." << endl; + return 0; + } + } + } + cout << "Usage: world flag username flag" << endl; + cout << "flag = 0-200" << endl; + return 0; + } + else if (strcasecmp(argv[1], "startzone") == 0) { + if (argc == 3) { + if (strlen(argv[2]) < 3) { + cout << "Error: zone name too short" << endl; + } + else if (strlen(argv[2]) > 15) { + cout << "Error: zone name too long" << endl; + } + else { + if (database.SetVariable("startzone", argv[2])) + cout << "Starting zone changed: '" << argv[2] << "'" << endl; + else + cout << "database.SetVariable failed." << endl; + } + return 0; + } + cout << "Usage: world startzone zoneshortname" << endl; + return 0; + } + else { + cout << "Error, unknown command line option" << endl; + return 0; + } + } + + if(Config->WorldHTTPEnabled) { + _log(WORLD__INIT, "Starting HTTP world service..."); + http_server.Start(Config->WorldHTTPPort, Config->WorldHTTPMimeFile.c_str()); + } else { + _log(WORLD__INIT, "HTTP world service disabled."); + } + + _log(WORLD__INIT, "Loading variables.."); + database.LoadVariables(); + _log(WORLD__INIT, "Loading zones.."); + database.LoadZoneNames(); + _log(WORLD__INIT, "Clearing groups.."); + database.ClearGroup(); + _log(WORLD__INIT, "Clearing raids.."); + database.ClearRaid(); + database.ClearRaidDetails(); + _log(WORLD__INIT, "Loading items.."); + if (!database.LoadItems()) { + _log(WORLD__INIT_ERR, "Error: Could not load item data. But ignoring"); + } + _log(WORLD__INIT, "Loading guilds.."); + guild_mgr.LoadGuilds(); + //rules: + { + char tmp[64]; + if (database.GetVariable("RuleSet", tmp, sizeof(tmp)-1)) { + _log(WORLD__INIT, "Loading rule set '%s'", tmp); + if(!rules->LoadRules(&database, tmp)) { + _log(WORLD__INIT_ERR, "Failed to load ruleset '%s', falling back to defaults.", tmp); + } + } else { + if(!rules->LoadRules(&database, "default")) { + _log(WORLD__INIT, "No rule set configured, using default rules"); + } else { + _log(WORLD__INIT, "Loaded default rule set 'default'", tmp); + } + } + } + if(RuleB(World, ClearTempMerchantlist)){ + _log(WORLD__INIT, "Clearing temporary merchant lists.."); + database.ClearMerchantTemp(); + } + _log(WORLD__INIT, "Loading EQ time of day.."); + if (!zoneserver_list.worldclock.loadFile(Config->EQTimeFile.c_str())) + _log(WORLD__INIT_ERR, "Unable to load %s", Config->EQTimeFile.c_str()); + _log(WORLD__INIT, "Loading launcher list.."); + launcher_list.LoadList(); + + char tmp[20]; + tmp[0] = '\0'; + database.GetVariable("holdzones",tmp, 20); + if ((strcasecmp(tmp, "1") == 0)) { + holdzones = true; + } + _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()) + { + _log(WORLD__INIT_ERR, "Unable to load adventure templates."); + } + + if(!adventure_manager.LoadAdventureEntries()) + { + _log(WORLD__INIT_ERR, "Unable to load adventure templates."); + } + + adventure_manager.Load(); + adventure_manager.LoadLeaderboardInfo(); + + _log(WORLD__INIT, "Purging expired instances"); + database.PurgeExpiredInstances(); + Timer PurgeInstanceTimer(450000); + PurgeInstanceTimer.Start(450000); + + _log(WORLD__INIT, "Loading char create info..."); + database.LoadCharacterCreateAllocations(); + database.LoadCharacterCreateCombos(); + + char errbuf[TCPConnection_ErrorBufferSize]; + if (tcps.Open(Config->WorldTCPPort, errbuf)) { + _log(WORLD__INIT,"Zone (TCP) listener started."); + } else { + _log(WORLD__INIT_ERR,"Failed to start zone (TCP) listener on port %d:",Config->WorldTCPPort); + _log(WORLD__INIT_ERR," %s",errbuf); + return 1; + } + if (eqsf.Open()) { + _log(WORLD__INIT,"Client (UDP) listener started."); + } else { + _log(WORLD__INIT_ERR,"Failed to start client (UDP) listener (port 9000)"); + return 1; + } + + //register all the patches we have avaliable with the stream identifier. + EQStreamIdentifier stream_identifier; + RegisterAllPatches(stream_identifier); + zoneserver_list.shutdowntimer = new Timer(60000); + zoneserver_list.shutdowntimer->Disable(); + zoneserver_list.reminder = new Timer(20000); + zoneserver_list.reminder->Disable(); + Timer InterserverTimer(INTERSERVER_TIMER); // does MySQL pings and auto-reconnect + InterserverTimer.Trigger(); + uint8 ReconnectCounter = 100; + EQStream* eqs; + EmuTCPConnection* tcpc; + EQStreamInterface *eqsi; + + while(RunLoops) { + Timer::SetCurrentTime(); + + //check the factory for any new incoming streams. + while ((eqs = eqsf.Pop())) { + //pull the stream out of the factory and give it to the stream identifier + //which will figure out what patch they are running, and set up the dynamic + //structures and opcodes for that patch. + struct in_addr in; + in.s_addr = eqs->GetRemoteIP(); + _log(WORLD__CLIENT, "New connection from %s:%d", inet_ntoa(in),ntohs(eqs->GetRemotePort())); + stream_identifier.AddStream(eqs); //takes the stream + } + + //give the stream identifier a chance to do its work.... + stream_identifier.Process(); + + //check the stream identifier for any now-identified streams + while((eqsi = stream_identifier.PopIdentified())) { + //now that we know what patch they are running, start up their client object + struct in_addr in; + in.s_addr = eqsi->GetRemoteIP(); + if (RuleB(World, UseBannedIPsTable)){ //Lieka: Check to see if we have the responsibility for blocking IPs. + _log(WORLD__CLIENT, "Checking inbound connection %s against BannedIPs table", inet_ntoa(in)); + if (!database.CheckBannedIPs(inet_ntoa(in))){ //Lieka: Check inbound IP against banned IP table. + _log(WORLD__CLIENT, "Connection %s PASSED banned IPs check. Processing connection.", inet_ntoa(in)); + Client* client = new Client(eqsi); + // @merth: client->zoneattempt=0; + client_list.Add(client); + } else { + _log(WORLD__CLIENT, "Connection from %s FAILED banned IPs check. Closing connection.", inet_ntoa(in)); + eqsi->Close(); //Lieka: If the inbound IP is on the banned table, close the EQStream. + } + } + if (!RuleB(World, UseBannedIPsTable)){ + _log(WORLD__CLIENT, "New connection from %s:%d, processing connection", inet_ntoa(in), ntohs(eqsi->GetRemotePort())); + Client* client = new Client(eqsi); + // @merth: client->zoneattempt=0; + client_list.Add(client); + } + } + + client_list.Process(); + + while ((tcpc = tcps.NewQueuePop())) { + struct in_addr in; + in.s_addr = tcpc->GetrIP(); + _log(WORLD__ZONE, "New TCP connection from %s:%d", inet_ntoa(in),tcpc->GetrPort()); + console_list.Add(new Console(tcpc)); + } + + if(PurgeInstanceTimer.Check()) + { + database.PurgeExpiredInstances(); + } + + //check for timeouts in other threads + timeout_manager.CheckTimeouts(); + + loginserverlist.Process(); + + console_list.Process(); + + zoneserver_list.Process(); + + launcher_list.Process(); + + UCSLink.Process(); + + QSLink.Process(); + + LFPGroupList.Process(); + + adventure_manager.Process(); + + if (InterserverTimer.Check()) { + InterserverTimer.Start(); + database.ping(); + AsyncLoadVariables(dbasync, &database); + ReconnectCounter++; + if (ReconnectCounter >= 12) { // only create thread to reconnect every 10 minutes. previously we were creating a new thread every 10 seconds + ReconnectCounter = 0; + if (loginserverlist.AllConnected() == false) { +#ifdef _WINDOWS + _beginthread(AutoInitLoginServer, 0, NULL); +#else + pthread_t thread; + pthread_create(&thread, NULL, &AutoInitLoginServer, NULL); +#endif + } + } + } + if (numclients == 0) { + Sleep(50); + continue; + } + Sleep(20); + } + _log(WORLD__SHUTDOWN,"World main loop completed."); + _log(WORLD__SHUTDOWN,"Shutting down console connections (if any)."); + console_list.KillAll(); + _log(WORLD__SHUTDOWN,"Shutting down zone connections (if any)."); + zoneserver_list.KillAll(); + _log(WORLD__SHUTDOWN,"Zone (TCP) listener stopped."); + tcps.Close(); + _log(WORLD__SHUTDOWN,"Client (UDP) listener stopped."); + eqsf.Close(); + _log(WORLD__SHUTDOWN,"Signaling HTTP service to stop..."); + http_server.Stop(); +#if 0 +#if defined(SHAREMEM) && !defined(WIN32) + for (int ipc_files = 0; ipc_files <= 4; ipc_files++) { + key_t share_key; + switch (ipc_files) { + // Item + case 0: share_key = ftok(".", 'I'); break; + // Npctype + case 1: share_key = ftok(".", 'N'); break; + // Door + case 2: share_key = ftok(".", 'D'); break; + // Spell + case 3: share_key = ftok(".", 'S'); break; + // Faction + case 4: share_key = ftok(".", 'F'); break; + // ERROR Fatal + default: cerr<<"Opps!"<EQTimeFile.c_str())==false) + _log(WORLD__SHUTDOWN,"Failed to save time file."); + RunLoops = false; +} + +void UpdateWindowTitle(char* iNewTitle) { +#ifdef _WINDOWS + char tmp[500]; + if (iNewTitle) { + snprintf(tmp, sizeof(tmp), "World: %s", iNewTitle); + } + else { + snprintf(tmp, sizeof(tmp), "World"); + } + SetConsoleTitle(tmp); +#endif +} + diff --git a/world/net.h b/world/net.h new file mode 100644 index 000000000..492c03a9c --- /dev/null +++ b/world/net.h @@ -0,0 +1,88 @@ +/* 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 WIN32 + #include + #include + #include + #include + #include + #include + #include +#else + #include + #include + #include + #include +#endif + +void CatchSignal(int sig_num); +void UpdateWindowTitle(char* iNewTitle); + +#define EQ_WORLD_PORT 9000 //mandated by the client +#define LOGIN_PORT 5997 + +class NetConnection +{ +public: + NetConnection() { + world_locked = false; + for (int i=0; i<5; i++) { + memset(loginaddress[i], 0, sizeof(loginaddress[i])); + loginport[i] = LOGIN_PORT; + } + memset(worldname, 0, sizeof(worldname)); + memset(worldshortname, 0, sizeof(worldshortname)); + memset(worldaccount, 0, sizeof(worldaccount)); + memset(worldpassword, 0, sizeof(worldpassword)); + memset(worldaddress, 0, sizeof(worldaddress)); + memset(chataddress, 0, sizeof(chataddress)); + DEFAULTSTATUS=0; + LoginServerInfo = 0;//ReadLoginINI(); + UpdateStats = false; + } + ~NetConnection() { } + + bool ReadLoginINI(); + bool LoginServerInfo; + bool UpdateStats; + char* GetLoginInfo(uint16* oPort); + inline char* GetLoginAddress(uint8 i) { return loginaddress[i]; } + inline uint16 GetLoginPort(uint8 i) { return loginport[i]; } + inline char* GetWorldName() { return worldname; } + inline char* GetWorldShortName() { return worldshortname; } + inline char* GetWorldAccount() { return worldaccount; } + inline char* GetWorldPassword() { return worldpassword; } + inline char* GetWorldAddress() { return worldaddress; } + inline uint8 GetDefaultStatus() { return DEFAULTSTATUS; } + inline char* GetChatAddress() { return chataddress; } + uint16 GetChatPort() { return chatport; } + bool world_locked; +private: + int listening_socket; + char loginaddress[5][255]; + uint16 loginport[5]; + uint16 chatport; + char worldname[201]; + char worldshortname[31]; + char worldaccount[31]; + char worldpassword[31]; + char worldaddress[255]; + char chataddress[255]; + uint8 DEFAULTSTATUS; + +}; diff --git a/world/perl_EQLConfig.cpp b/world/perl_EQLConfig.cpp new file mode 100644 index 000000000..26d35f951 --- /dev/null +++ b/world/perl_EQLConfig.cpp @@ -0,0 +1,492 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of tmp. Do not edit this file, edit tmp instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 +*/ + +typedef const char Const_char; + +#ifdef EMBPERL +#include "../common/debug.h" +#include "EQWParser.h" +#include "EQLConfig.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +XS(XS_EQLConfig_GetName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQLConfig_GetName) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQLConfig::GetName(THIS)"); + { + EQLConfig * THIS; + Const_char * RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EQLConfig")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQLConfig *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQLConfig"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetName(); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_EQLConfig_GetStaticCount); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQLConfig_GetStaticCount) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQLConfig::GetStaticCount(THIS)"); + { + EQLConfig * THIS; + int RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EQLConfig")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQLConfig *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQLConfig"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetStaticCount(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQLConfig_IsConnected); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQLConfig_IsConnected) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQLConfig::IsConnected(THIS)"); + { + EQLConfig * THIS; + bool RETVAL; + + if (sv_derived_from(ST(0), "EQLConfig")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQLConfig *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQLConfig"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsConnected(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQLConfig_DeleteLauncher); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQLConfig_DeleteLauncher) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQLConfig::DeleteLauncher(THIS)"); + { + EQLConfig * THIS; + + if (sv_derived_from(ST(0), "EQLConfig")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQLConfig *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQLConfig"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->DeleteLauncher(); + } + XSRETURN_EMPTY; +} + +XS(XS_EQLConfig_RestartZone); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQLConfig_RestartZone) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQLConfig::RestartZone(THIS, zone_ref)"); + { + EQLConfig * THIS; + Const_char * zone_ref = (Const_char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EQLConfig")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQLConfig *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQLConfig"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RestartZone(zone_ref); + } + XSRETURN_EMPTY; +} + +XS(XS_EQLConfig_StopZone); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQLConfig_StopZone) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQLConfig::StopZone(THIS, zone_ref)"); + { + EQLConfig * THIS; + Const_char * zone_ref = (Const_char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EQLConfig")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQLConfig *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQLConfig"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->StopZone(zone_ref); + } + XSRETURN_EMPTY; +} + +XS(XS_EQLConfig_StartZone); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQLConfig_StartZone) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQLConfig::StartZone(THIS, zone_ref)"); + { + EQLConfig * THIS; + Const_char * zone_ref = (Const_char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EQLConfig")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQLConfig *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQLConfig"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->StartZone(zone_ref); + } + XSRETURN_EMPTY; +} + +XS(XS_EQLConfig_BootStaticZone); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQLConfig_BootStaticZone) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: EQLConfig::BootStaticZone(THIS, short_name, port)"); + { + EQLConfig * THIS; + bool RETVAL; + Const_char * short_name = (Const_char *)SvPV_nolen(ST(1)); + uint16 port = (uint16)SvUV(ST(2)); + + if (sv_derived_from(ST(0), "EQLConfig")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQLConfig *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQLConfig"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->BootStaticZone(short_name, port); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQLConfig_ChangeStaticZone); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQLConfig_ChangeStaticZone) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: EQLConfig::ChangeStaticZone(THIS, short_name, port)"); + { + EQLConfig * THIS; + bool RETVAL; + Const_char * short_name = (Const_char *)SvPV_nolen(ST(1)); + uint16 port = (uint16)SvUV(ST(2)); + + if (sv_derived_from(ST(0), "EQLConfig")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQLConfig *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQLConfig"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->ChangeStaticZone(short_name, port); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQLConfig_DeleteStaticZone); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQLConfig_DeleteStaticZone) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQLConfig::DeleteStaticZone(THIS, short_name)"); + { + EQLConfig * THIS; + bool RETVAL; + Const_char * short_name = (Const_char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EQLConfig")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQLConfig *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQLConfig"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->DeleteStaticZone(short_name); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQLConfig_SetDynamicCount); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQLConfig_SetDynamicCount) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQLConfig::SetDynamicCount(THIS, count)"); + { + EQLConfig * THIS; + bool RETVAL; + int count = (int)SvIV(ST(1)); + + if (sv_derived_from(ST(0), "EQLConfig")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQLConfig *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQLConfig"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->SetDynamicCount(count); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQLConfig_GetDynamicCount); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQLConfig_GetDynamicCount) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQLConfig::GetDynamicCount(THIS)"); + { + EQLConfig * THIS; + int RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EQLConfig")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQLConfig *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQLConfig"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDynamicCount(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQLConfig_ListZones); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQLConfig_ListZones) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQLConfig::ListZones(THIS)"); + { + EQLConfig * THIS; + vector RETVAL; + + if (sv_derived_from(ST(0), "EQLConfig")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQLConfig *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQLConfig"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->ListZones(); + ST(0) = sv_newmortal(); + { + U32 ix_RETVAL; + /* pop crap off the stack we dont really want */ + POPs; + POPs; + /* grow the stack to the number of elements being returned */ + EXTEND(SP, RETVAL.size()); + for (ix_RETVAL = 0; ix_RETVAL < RETVAL.size(); ix_RETVAL++) { + const string &it = RETVAL[ix_RETVAL]; + ST(ix_RETVAL) = sv_newmortal(); + sv_setpvn(ST(ix_RETVAL), it.c_str(), it.length()); + } + /* hackish, but im over it. The normal xsubpp return will be right below this */ + XSRETURN(RETVAL.size()); + } + } + XSRETURN(1); +} + +XS(XS_EQLConfig_GetZoneDetails); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQLConfig_GetZoneDetails) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQLConfig::GetZoneDetails(THIS, zone_ref)"); + { + EQLConfig * THIS; + map RETVAL; + Const_char * zone_ref = (Const_char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EQLConfig")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQLConfig *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQLConfig"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetZoneDetails(zone_ref); + ST(0) = sv_newmortal(); + if (RETVAL.begin()!=RETVAL.end()) + { + //NOTE: we are leaking the original ST(0) right now + HV *hv = newHV(); + sv_2mortal((SV*)hv); + ST(0) = newRV((SV*)hv); + + map::const_iterator cur, end; + cur = RETVAL.begin(); + end = RETVAL.end(); + for(; cur != end; cur++) { + /* get the element from the hash, creating if needed (will be needed) */ + SV**ele = hv_fetch(hv, cur->first.c_str(), cur->first.length(), TRUE); + if(ele == NULL) { + Perl_croak(aTHX_ "Unable to create a hash element for RETVAL"); + break; + } + /* put our string in the SV associated with this element in the hash */ + sv_setpvn(*ele, cur->second.c_str(), cur->second.length()); + } + } + + + + + + + + + + + + + + + + + + } + XSRETURN(1); +} + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_EQLConfig); /* prototype to pass -Wmissing-prototypes */ +XS(boot_EQLConfig) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "GetName"), XS_EQLConfig_GetName, file, "$"); + newXSproto(strcpy(buf, "GetStaticCount"), XS_EQLConfig_GetStaticCount, file, "$"); + newXSproto(strcpy(buf, "IsConnected"), XS_EQLConfig_IsConnected, file, "$"); + newXSproto(strcpy(buf, "DeleteLauncher"), XS_EQLConfig_DeleteLauncher, file, "$"); + newXSproto(strcpy(buf, "RestartZone"), XS_EQLConfig_RestartZone, file, "$$"); + newXSproto(strcpy(buf, "StopZone"), XS_EQLConfig_StopZone, file, "$$"); + newXSproto(strcpy(buf, "StartZone"), XS_EQLConfig_StartZone, file, "$$"); + newXSproto(strcpy(buf, "BootStaticZone"), XS_EQLConfig_BootStaticZone, file, "$$$"); + newXSproto(strcpy(buf, "ChangeStaticZone"), XS_EQLConfig_ChangeStaticZone, file, "$$$"); + newXSproto(strcpy(buf, "DeleteStaticZone"), XS_EQLConfig_DeleteStaticZone, file, "$$"); + newXSproto(strcpy(buf, "SetDynamicCount"), XS_EQLConfig_SetDynamicCount, file, "$$"); + newXSproto(strcpy(buf, "GetDynamicCount"), XS_EQLConfig_GetDynamicCount, file, "$"); + newXSproto(strcpy(buf, "ListZones"), XS_EQLConfig_ListZones, file, "$"); + newXSproto(strcpy(buf, "GetZoneDetails"), XS_EQLConfig_GetZoneDetails, file, "$$"); + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES + diff --git a/world/perl_EQW.cpp b/world/perl_EQW.cpp new file mode 100644 index 000000000..c43bd8615 --- /dev/null +++ b/world/perl_EQW.cpp @@ -0,0 +1,1023 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of tmp. Do not edit this file, edit tmp instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 +*/ + +typedef const char Const_char; + +#ifdef EMBPERL +#include "../common/debug.h" +#include "EQWParser.h" +#include "EQW.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +XS(XS_EQW_GetConfig); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_GetConfig) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQW::GetConfig(THIS, var_name)"); + { + EQW * THIS; + Const_char * RETVAL; + dXSTARG; + Const_char * var_name = (Const_char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetConfig(var_name); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_EQW_LockWorld); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_LockWorld) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQW::LockWorld(THIS)"); + { + EQW * THIS; + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->LockWorld(); + } + XSRETURN_EMPTY; +} + +XS(XS_EQW_UnlockWorld); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_UnlockWorld) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQW::UnlockWorld(THIS)"); + { + EQW * THIS; + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->UnlockWorld(); + } + XSRETURN_EMPTY; +} + +XS(XS_EQW_LSConnected); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_LSConnected) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQW::LSConnected(THIS)"); + { + EQW * THIS; + bool RETVAL; + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->LSConnected(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQW_LSReconnect); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_LSReconnect) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQW::LSReconnect(THIS)"); + { + EQW * THIS; + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->LSReconnect(); + } + XSRETURN_EMPTY; +} + +XS(XS_EQW_CountZones); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_CountZones) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQW::CountZones(THIS)"); + { + EQW * THIS; + int RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CountZones(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQW_ListBootedZones); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_ListBootedZones) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQW::ListBootedZones(THIS)"); + { + EQW * THIS; + vector RETVAL; + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->ListBootedZones(); + ST(0) = sv_newmortal(); + { + U32 ix_RETVAL; + /* pop crap off the stack we dont really want */ + POPs; + POPs; + /* grow the stack to the number of elements being returned */ + EXTEND(SP, RETVAL.size()); + for (ix_RETVAL = 0; ix_RETVAL < RETVAL.size(); ix_RETVAL++) { + const string &it = RETVAL[ix_RETVAL]; + ST(ix_RETVAL) = sv_newmortal(); + sv_setpvn(ST(ix_RETVAL), it.c_str(), it.length()); + } + /* hackish, but im over it. The normal xsubpp return will be right below this */ + XSRETURN(RETVAL.size()); + } + } + XSRETURN(1); +} + +XS(XS_EQW_GetZoneDetails); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_GetZoneDetails) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQW::GetZoneDetails(THIS, zone_ref)"); + { + EQW * THIS; + map RETVAL; + Const_char * zone_ref = (Const_char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetZoneDetails(zone_ref); + ST(0) = sv_newmortal(); + if (RETVAL.begin()!=RETVAL.end()) + { + //NOTE: we are leaking the original ST(0) right now + HV *hv = newHV(); + sv_2mortal((SV*)hv); + ST(0) = newRV((SV*)hv); + + map::const_iterator cur, end; + cur = RETVAL.begin(); + end = RETVAL.end(); + for(; cur != end; cur++) { + /* get the element from the hash, creating if needed (will be needed) */ + SV**ele = hv_fetch(hv, cur->first.c_str(), cur->first.length(), TRUE); + if(ele == NULL) { + Perl_croak(aTHX_ "Unable to create a hash element for RETVAL"); + break; + } + /* put our string in the SV associated with this element in the hash */ + sv_setpvn(*ele, cur->second.c_str(), cur->second.length()); + } + } + + + + + + + + + + + + + + + + + + } + XSRETURN(1); +} + +XS(XS_EQW_CountPlayers); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_CountPlayers) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQW::CountPlayers(THIS)"); + { + EQW * THIS; + int RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CountPlayers(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQW_ListPlayers); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_ListPlayers) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: EQW::ListPlayers(THIS, zone_name= \"\")"); + { + EQW * THIS; + vector RETVAL; + Const_char * zone_name; + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 2) + zone_name = ""; + else { + zone_name = (Const_char *)SvPV_nolen(ST(1)); + } + + RETVAL = THIS->ListPlayers(zone_name); + ST(0) = sv_newmortal(); + { + U32 ix_RETVAL; + /* pop crap off the stack we dont really want */ + POPs; + POPs; + /* grow the stack to the number of elements being returned */ + EXTEND(SP, RETVAL.size()); + for (ix_RETVAL = 0; ix_RETVAL < RETVAL.size(); ix_RETVAL++) { + const string &it = RETVAL[ix_RETVAL]; + ST(ix_RETVAL) = sv_newmortal(); + sv_setpvn(ST(ix_RETVAL), it.c_str(), it.length()); + } + /* hackish, but im over it. The normal xsubpp return will be right below this */ + XSRETURN(RETVAL.size()); + } + } + XSRETURN(1); +} + +XS(XS_EQW_GetPlayerDetails); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_GetPlayerDetails) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQW::GetPlayerDetails(THIS, player_ref)"); + { + EQW * THIS; + map RETVAL; + Const_char * player_ref = (Const_char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetPlayerDetails(player_ref); + ST(0) = sv_newmortal(); + if (RETVAL.begin()!=RETVAL.end()) + { + //NOTE: we are leaking the original ST(0) right now + HV *hv = newHV(); + sv_2mortal((SV*)hv); + ST(0) = newRV((SV*)hv); + + map::const_iterator cur, end; + cur = RETVAL.begin(); + end = RETVAL.end(); + for(; cur != end; cur++) { + /* get the element from the hash, creating if needed (will be needed) */ + SV**ele = hv_fetch(hv, cur->first.c_str(), cur->first.length(), TRUE); + if(ele == NULL) { + Perl_croak(aTHX_ "Unable to create a hash element for RETVAL"); + break; + } + /* put our string in the SV associated with this element in the hash */ + sv_setpvn(*ele, cur->second.c_str(), cur->second.length()); + } + } + + + + + + + + + + + + + + + + + + } + XSRETURN(1); +} + +XS(XS_EQW_CountLaunchers); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_CountLaunchers) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQW::CountLaunchers(THIS, active_only)"); + { + EQW * THIS; + int RETVAL; + dXSTARG; + bool active_only = (bool)SvTRUE(ST(1)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CountLaunchers(active_only); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQW_ListLaunchers); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_ListLaunchers) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQW::ListLaunchers(THIS)"); + { + EQW * THIS; + vector RETVAL; + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->ListLaunchers(); + ST(0) = sv_newmortal(); + { + U32 ix_RETVAL; + /* pop crap off the stack we dont really want */ + POPs; + POPs; + /* grow the stack to the number of elements being returned */ + EXTEND(SP, RETVAL.size()); + for (ix_RETVAL = 0; ix_RETVAL < RETVAL.size(); ix_RETVAL++) { + const string &it = RETVAL[ix_RETVAL]; + ST(ix_RETVAL) = sv_newmortal(); + sv_setpvn(ST(ix_RETVAL), it.c_str(), it.length()); + } + /* hackish, but im over it. The normal xsubpp return will be right below this */ + XSRETURN(RETVAL.size()); + } + } + XSRETURN(1); +} + +XS(XS_EQW_GetLauncher); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_GetLauncher) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQW::GetLauncher(THIS, launcher_name)"); + { + EQW * THIS; + EQLConfig * RETVAL; + Const_char * launcher_name = (Const_char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetLauncher(launcher_name); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "EQLConfig", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQW_CreateLauncher); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_CreateLauncher) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: EQW::CreateLauncher(THIS, launcher_name, dynamic_count)"); + { + EQW * THIS; + Const_char * launcher_name = (Const_char *)SvPV_nolen(ST(1)); + int dynamic_count = (int)SvIV(ST(2)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->CreateLauncher(launcher_name, dynamic_count); + } + XSRETURN_EMPTY; +} + +XS(XS_EQW_CreateGuild); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_CreateGuild) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: EQW::CreateGuild(THIS, name, leader_char_id)"); + { + EQW * THIS; + uint32 RETVAL; + dXSTARG; + char* name = (char *)SvPV_nolen(ST(1)); + uint32 leader_char_id = (uint32)SvUV(ST(2)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CreateGuild(name, leader_char_id); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQW_DeleteGuild); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_DeleteGuild) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQW::DeleteGuild(THIS, guild_id)"); + { + EQW * THIS; + bool RETVAL; + uint32 guild_id = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->DeleteGuild(guild_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQW_RenameGuild); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_RenameGuild) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: EQW::RenameGuild(THIS, guild_id, name)"); + { + EQW * THIS; + bool RETVAL; + uint32 guild_id = (uint32)SvUV(ST(1)); + char* name = (char *)SvPV_nolen(ST(2)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->RenameGuild(guild_id, name); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQW_SetGuildMOTD); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_SetGuildMOTD) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: EQW::SetGuildMOTD(THIS, guild_id, motd, setter)"); + { + EQW * THIS; + bool RETVAL; + uint32 guild_id = (uint32)SvUV(ST(1)); + char* motd = (char *)SvPV_nolen(ST(2)); + char * setter = (char *)SvPV_nolen(ST(3)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->SetGuildMOTD(guild_id, motd, setter); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQW_SetGuildLeader); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_SetGuildLeader) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: EQW::SetGuildLeader(THIS, guild_id, leader_char_id)"); + { + EQW * THIS; + bool RETVAL; + uint32 guild_id = (uint32)SvUV(ST(1)); + uint32 leader_char_id = (uint32)SvUV(ST(2)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->SetGuildLeader(guild_id, leader_char_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQW_SetGuild); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_SetGuild) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: EQW::SetGuild(THIS, charid, guild_id, rank)"); + { + EQW * THIS; + bool RETVAL; + uint32 charid = (uint32)SvUV(ST(1)); + uint32 guild_id = (uint32)SvUV(ST(2)); + uint8 rank = (uint8)SvUV(ST(3)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->SetGuild(charid, guild_id, rank); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQW_SetGuildRank); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_SetGuildRank) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: EQW::SetGuildRank(THIS, charid, rank)"); + { + EQW * THIS; + bool RETVAL; + uint32 charid = (uint32)SvUV(ST(1)); + uint8 rank = (uint8)SvUV(ST(2)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->SetGuildRank(charid, rank); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQW_SetBankerFlag); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_SetBankerFlag) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: EQW::SetBankerFlag(THIS, charid, is_banker)"); + { + EQW * THIS; + bool RETVAL; + uint32 charid = (uint32)SvUV(ST(1)); + bool is_banker = (bool)SvTRUE(ST(2)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->SetBankerFlag(charid, is_banker); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQW_SetTributeFlag); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_SetTributeFlag) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: EQW::SetTributeFlag(THIS, charid, enabled)"); + { + EQW * THIS; + bool RETVAL; + uint32 charid = (uint32)SvUV(ST(1)); + bool enabled = (bool)SvTRUE(ST(2)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->SetTributeFlag(charid, enabled); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQW_SetPublicNote); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_SetPublicNote) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: EQW::SetPublicNote(THIS, charid, note)"); + { + EQW * THIS; + bool RETVAL; + uint32 charid = (uint32)SvUV(ST(1)); + char * note = (char *)SvPV_nolen(ST(2)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->SetPublicNote(charid, note); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EQW_CountBugs); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_CountBugs) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EQW::CountBugs(THIS)"); + { + EQW * THIS; + int RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CountBugs(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EQW_ListBugs); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_ListBugs) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQW::ListBugs(THIS, id)"); + { + EQW * THIS; + uint32 id = (uint32)SvUV(ST(1)); + vector RETVAL; + + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->ListBugs(id); + ST(0) = sv_newmortal(); + { + U32 ix_RETVAL; + /* pop crap off the stack we dont really want */ + POPs; + POPs; + /* grow the stack to the number of elements being returned */ + EXTEND(SP, RETVAL.size()); + for (ix_RETVAL = 0; ix_RETVAL < RETVAL.size(); ix_RETVAL++) { + const string &it = RETVAL[ix_RETVAL]; + ST(ix_RETVAL) = sv_newmortal(); + sv_setpvn(ST(ix_RETVAL), it.c_str(), it.length()); + } + /* hackish, but im over it. The normal xsubpp return will be right below this */ + XSRETURN(RETVAL.size()); + } + } + XSRETURN(1); +} + +XS(XS_EQW_GetBugDetails); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_GetBugDetails) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQW::GetBugDetails(THIS, bug_ref)"); + { + EQW * THIS; + map RETVAL; + Const_char * bug_ref = (Const_char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetBugDetails(bug_ref); + ST(0) = sv_newmortal(); + if (RETVAL.begin()!=RETVAL.end()) + { + //NOTE: we are leaking the original ST(0) right now + HV *hv = newHV(); + sv_2mortal((SV*)hv); + ST(0) = newRV((SV*)hv); + + map::const_iterator cur, end; + cur = RETVAL.begin(); + end = RETVAL.end(); + for(; cur != end; cur++) { + /* get the element from the hash, creating if needed (will be needed) */ + SV**ele = hv_fetch(hv, cur->first.c_str(), cur->first.length(), TRUE); + if(ele == NULL) { + Perl_croak(aTHX_ "Unable to create a hash element for RETVAL"); + break; + } + /* put our string in the SV associated with this element in the hash */ + sv_setpvn(*ele, cur->second.c_str(), cur->second.length()); + } + } + } + XSRETURN(1); +} + +XS(XS_EQW_ResolveBug); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQW_ResolveBug) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EQW::ResolveBug(THIS, id)"); + { + EQW * THIS; + const char *id = (const char*)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EQW")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EQW *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EQW"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->ResolveBug(id); + } + XSRETURN_EMPTY; +} + + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_EQW); /* prototype to pass -Wmissing-prototypes */ +XS(boot_EQW) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "GetConfig"), XS_EQW_GetConfig, file, "$$"); + newXSproto(strcpy(buf, "LockWorld"), XS_EQW_LockWorld, file, "$"); + newXSproto(strcpy(buf, "UnlockWorld"), XS_EQW_UnlockWorld, file, "$"); + newXSproto(strcpy(buf, "LSConnected"), XS_EQW_LSConnected, file, "$"); + newXSproto(strcpy(buf, "LSReconnect"), XS_EQW_LSReconnect, file, "$"); + newXSproto(strcpy(buf, "CountZones"), XS_EQW_CountZones, file, "$"); + newXSproto(strcpy(buf, "ListBootedZones"), XS_EQW_ListBootedZones, file, "$"); + newXSproto(strcpy(buf, "GetZoneDetails"), XS_EQW_GetZoneDetails, file, "$$"); + newXSproto(strcpy(buf, "CountPlayers"), XS_EQW_CountPlayers, file, "$"); + newXSproto(strcpy(buf, "ListPlayers"), XS_EQW_ListPlayers, file, "$;$"); + newXSproto(strcpy(buf, "GetPlayerDetails"), XS_EQW_GetPlayerDetails, file, "$$"); + newXSproto(strcpy(buf, "CountLaunchers"), XS_EQW_CountLaunchers, file, "$$"); + newXSproto(strcpy(buf, "ListLaunchers"), XS_EQW_ListLaunchers, file, "$"); + newXSproto(strcpy(buf, "GetLauncher"), XS_EQW_GetLauncher, file, "$$"); + newXSproto(strcpy(buf, "CreateLauncher"), XS_EQW_CreateLauncher, file, "$$$"); + newXSproto(strcpy(buf, "CreateGuild"), XS_EQW_CreateGuild, file, "$$$"); + newXSproto(strcpy(buf, "DeleteGuild"), XS_EQW_DeleteGuild, file, "$$"); + newXSproto(strcpy(buf, "RenameGuild"), XS_EQW_RenameGuild, file, "$$$"); + newXSproto(strcpy(buf, "SetGuildMOTD"), XS_EQW_SetGuildMOTD, file, "$$$$"); + newXSproto(strcpy(buf, "SetGuildLeader"), XS_EQW_SetGuildLeader, file, "$$$"); + newXSproto(strcpy(buf, "SetGuild"), XS_EQW_SetGuild, file, "$$$$"); + newXSproto(strcpy(buf, "SetGuildRank"), XS_EQW_SetGuildRank, file, "$$$"); + newXSproto(strcpy(buf, "SetBankerFlag"), XS_EQW_SetBankerFlag, file, "$$$"); + newXSproto(strcpy(buf, "SetTributeFlag"), XS_EQW_SetTributeFlag, file, "$$$"); + newXSproto(strcpy(buf, "SetPublicNote"), XS_EQW_SetPublicNote, file, "$$$"); + newXSproto(strcpy(buf, "CountBugs"), XS_EQW_CountBugs, file, "$"); + newXSproto(strcpy(buf, "ListBugs"), XS_EQW_ListBugs, file, "$$"); + newXSproto(strcpy(buf, "GetBugDetails"), XS_EQW_GetBugDetails, file, "$$"); + newXSproto(strcpy(buf, "ResolveBug"), XS_EQW_ResolveBug, file, "$$"); + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES + diff --git a/world/perl_HTTPRequest.cpp b/world/perl_HTTPRequest.cpp new file mode 100644 index 000000000..49704be17 --- /dev/null +++ b/world/perl_HTTPRequest.cpp @@ -0,0 +1,345 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of tmp. Do not edit this file, edit tmp instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 +*/ + +typedef const char Const_char; + +#ifdef EMBPERL +#include "../common/debug.h" +#include "EQWParser.h" +#include "HTTPRequest.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +XS(XS_HTTPRequest_get); /* prototype to pass -Wmissing-prototypes */ +XS(XS_HTTPRequest_get) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: HTTPRequest::get(THIS, name, default_value= \"\")"); + { + HTTPRequest * THIS; + Const_char * RETVAL; + dXSTARG; + Const_char * name = (Const_char *)SvPV_nolen(ST(1)); + Const_char * default_value; + + if (sv_derived_from(ST(0), "HTTPRequest")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(HTTPRequest *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type HTTPRequest"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 3) + default_value = ""; + else { + default_value = (Const_char *)SvPV_nolen(ST(2)); + } + + RETVAL = THIS->get(name, default_value); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_HTTPRequest_getInt); /* prototype to pass -Wmissing-prototypes */ +XS(XS_HTTPRequest_getInt) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: HTTPRequest::getInt(THIS, name, default_value= 0)"); + { + HTTPRequest * THIS; + int RETVAL; + dXSTARG; + Const_char * name = (Const_char *)SvPV_nolen(ST(1)); + int default_value; + + if (sv_derived_from(ST(0), "HTTPRequest")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(HTTPRequest *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type HTTPRequest"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 3) + default_value = 0; + else { + default_value = (int)SvIV(ST(2)); + } + + RETVAL = THIS->getInt(name, default_value); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_HTTPRequest_getFloat); /* prototype to pass -Wmissing-prototypes */ +XS(XS_HTTPRequest_getFloat) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: HTTPRequest::getFloat(THIS, name, default_value= 0.0)"); + { + HTTPRequest * THIS; + float RETVAL; + dXSTARG; + Const_char * name = (Const_char *)SvPV_nolen(ST(1)); + float default_value; + + if (sv_derived_from(ST(0), "HTTPRequest")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(HTTPRequest *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type HTTPRequest"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 3) + default_value = 0.0; + else { + default_value = (float)SvNV(ST(2)); + } + + RETVAL = THIS->getFloat(name, default_value); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_HTTPRequest_getEscaped); /* prototype to pass -Wmissing-prototypes */ +XS(XS_HTTPRequest_getEscaped) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: HTTPRequest::getEscaped(THIS, name, default_value= \"\")"); + { + HTTPRequest * THIS; + Const_char * RETVAL; + dXSTARG; + Const_char * name = (Const_char *)SvPV_nolen(ST(1)); + Const_char * default_value; + + if (sv_derived_from(ST(0), "HTTPRequest")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(HTTPRequest *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type HTTPRequest"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 3) + default_value = ""; + else { + default_value = (Const_char *)SvPV_nolen(ST(2)); + } + + RETVAL = THIS->getEscaped(name, default_value); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_HTTPRequest_get_all); /* prototype to pass -Wmissing-prototypes */ +XS(XS_HTTPRequest_get_all) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: HTTPRequest::get_all(THIS)"); + { + HTTPRequest * THIS; + map RETVAL; + + if (sv_derived_from(ST(0), "HTTPRequest")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(HTTPRequest *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type HTTPRequest"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->get_all(); + ST(0) = sv_newmortal(); + if (RETVAL.begin()!=RETVAL.end()) + { + //NOTE: we are leaking the original ST(0) right now + HV *hv = newHV(); + sv_2mortal((SV*)hv); + ST(0) = newRV((SV*)hv); + + map::const_iterator cur, end; + cur = RETVAL.begin(); + end = RETVAL.end(); + for(; cur != end; cur++) { + /* get the element from the hash, creating if needed (will be needed) */ + SV**ele = hv_fetch(hv, cur->first.c_str(), cur->first.length(), TRUE); + if(ele == NULL) { + Perl_croak(aTHX_ "Unable to create a hash element for RETVAL"); + break; + } + /* put our string in the SV associated with this element in the hash */ + sv_setpvn(*ele, cur->second.c_str(), cur->second.length()); + } + } + + + + + + + + + + + + + + + + + + } + XSRETURN(1); +} + +XS(XS_HTTPRequest_redirect); /* prototype to pass -Wmissing-prototypes */ +XS(XS_HTTPRequest_redirect) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: HTTPRequest::redirect(THIS, URL)"); + { + HTTPRequest * THIS; + Const_char * URL = (Const_char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "HTTPRequest")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(HTTPRequest *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type HTTPRequest"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->redirect(URL); + } + XSRETURN_EMPTY; +} + +XS(XS_HTTPRequest_SetResponseCode); /* prototype to pass -Wmissing-prototypes */ +XS(XS_HTTPRequest_SetResponseCode) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: HTTPRequest::SetResponseCode(THIS, code)"); + { + HTTPRequest * THIS; + Const_char * code = (Const_char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "HTTPRequest")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(HTTPRequest *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type HTTPRequest"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetResponseCode(code); + } + XSRETURN_EMPTY; +} + +XS(XS_HTTPRequest_header); /* prototype to pass -Wmissing-prototypes */ +XS(XS_HTTPRequest_header) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: HTTPRequest::header(THIS, name, value)"); + { + HTTPRequest * THIS; + Const_char * name = (Const_char *)SvPV_nolen(ST(1)); + Const_char * value = (Const_char *)SvPV_nolen(ST(2)); + + if (sv_derived_from(ST(0), "HTTPRequest")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(HTTPRequest *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type HTTPRequest"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->header(name, value); + } + XSRETURN_EMPTY; +} + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_HTTPRequest); /* prototype to pass -Wmissing-prototypes */ +XS(boot_HTTPRequest) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "get"), XS_HTTPRequest_get, file, "$$;$"); + newXSproto(strcpy(buf, "getInt"), XS_HTTPRequest_getInt, file, "$$;$"); + newXSproto(strcpy(buf, "getFloat"), XS_HTTPRequest_getFloat, file, "$$;$"); + newXSproto(strcpy(buf, "getEscaped"), XS_HTTPRequest_getEscaped, file, "$$;$"); + newXSproto(strcpy(buf, "get_all"), XS_HTTPRequest_get_all, file, "$"); + newXSproto(strcpy(buf, "redirect"), XS_HTTPRequest_redirect, file, "$$"); + newXSproto(strcpy(buf, "SetResponseCode"), XS_HTTPRequest_SetResponseCode, file, "$$"); + newXSproto(strcpy(buf, "header"), XS_HTTPRequest_header, file, "$$$"); + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES + diff --git a/world/queryserv.cpp b/world/queryserv.cpp new file mode 100644 index 000000000..256c2c8b1 --- /dev/null +++ b/world/queryserv.cpp @@ -0,0 +1,133 @@ +#include "../common/debug.h" +#include "queryserv.h" +#include "WorldConfig.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/packet_dump.h" + +extern ClientList client_list; +extern ZSList zoneserver_list; + +QueryServConnection::QueryServConnection() +{ + Stream = 0; + authenticated = false; +} + +void QueryServConnection::SetConnection(EmuTCPConnection *inStream) +{ + if(Stream) + { + _log(QUERYSERV__ERROR, "Incoming QueryServ Connection while we were already connected to a QueryServ."); + Stream->Disconnect(); + } + + Stream = inStream; + + authenticated = false; +} + +bool QueryServConnection::Process() +{ + if (!Stream || !Stream->Connected()) + return false; + + ServerPacket *pack = 0; + + while((pack = Stream->PopPacket())) + { + if (!authenticated) + { + if (WorldConfig::get()->SharedKey.length() > 0) + { + if (pack->opcode == ServerOP_ZAAuth && pack->size == 16) + { + uint8 tmppass[16]; + + MD5::Generate((const uchar*) WorldConfig::get()->SharedKey.c_str(), WorldConfig::get()->SharedKey.length(), tmppass); + + if (memcmp(pack->pBuffer, tmppass, 16) == 0) + authenticated = true; + else + { + struct in_addr in; + in.s_addr = GetIP(); + _log(QUERYSERV__ERROR, "QueryServ authorization failed."); + ServerPacket* pack = new ServerPacket(ServerOP_ZAAuthFailed); + SendPacket(pack); + delete pack; + Disconnect(); + return false; + } + } + else + { + struct in_addr in; + in.s_addr = GetIP(); + _log(QUERYSERV__ERROR, "QueryServ authorization failed."); + ServerPacket* pack = new ServerPacket(ServerOP_ZAAuthFailed); + SendPacket(pack); + delete pack; + Disconnect(); + return false; + } + } + else + { + _log(QUERYSERV__ERROR,"**WARNING** You have not configured a world shared key in your config file. You should add a STRING element to your element to prevent unauthroized zone access."); + authenticated = true; + } + delete pack; + continue; + } + switch(pack->opcode) + { + case 0: + break; + + case ServerOP_KeepAlive: + { + // ignore this + break; + } + case ServerOP_ZAAuth: + { + _log(QUERYSERV__ERROR, "Got authentication from QueryServ when they are already authenticated."); + break; + } + case ServerOP_QueryServGeneric: + { + uint32 ZoneID = pack->ReadUInt32(); + uint16 InstanceID = pack->ReadUInt32(); + zoneserver_list.SendPacket(ZoneID, InstanceID, pack); + break; + } + case ServerOP_LFGuildUpdate: + { + zoneserver_list.SendPacket(pack); + break; + } + default: + { + _log(QUERYSERV__ERROR, "Unknown ServerOPcode from QueryServ 0x%04x, size %d", pack->opcode, pack->size); + DumpPacket(pack->pBuffer, pack->size); + break; + } + } + + delete pack; + } + return(true); +} + +bool QueryServConnection::SendPacket(ServerPacket* pack) +{ + if(!Stream) + return false; + + return Stream->SendPacket(pack); +} diff --git a/world/queryserv.h b/world/queryserv.h new file mode 100644 index 000000000..df6dde7f8 --- /dev/null +++ b/world/queryserv.h @@ -0,0 +1,23 @@ +#ifndef QueryServ_H +#define QueryServ_H + +#include "../common/types.h" +#include "../common/EmuTCPConnection.h" +#include "../common/servertalk.h" + +class QueryServConnection +{ +public: + QueryServConnection(); + void SetConnection(EmuTCPConnection *inStream); + bool Process(); + bool SendPacket(ServerPacket* pack); + void Disconnect() { if(Stream) Stream->Disconnect(); } + void SendMessage(const char *From, const char *Message); +private: + inline uint32 GetIP() const { return Stream ? Stream->GetrIP() : 0; } + EmuTCPConnection *Stream; + bool authenticated; +}; + +#endif /*QueryServ_H_*/ diff --git a/world/ucs.cpp b/world/ucs.cpp new file mode 100644 index 000000000..0e73c8dd6 --- /dev/null +++ b/world/ucs.cpp @@ -0,0 +1,129 @@ +#include "../common/debug.h" +#include "ucs.h" +#include "WorldConfig.h" +#include "../common/logsys.h" +#include "../common/logtypes.h" +#include "../common/md5.h" +#include "../common/EmuTCPConnection.h" +#include "../common/packet_dump.h" + +UCSConnection::UCSConnection() +{ + Stream = 0; + authenticated = false; +} + +void UCSConnection::SetConnection(EmuTCPConnection *inStream) +{ + if(Stream) + { + _log(UCS__ERROR, "Incoming UCS Connection while we were already connected to a UCS."); + Stream->Disconnect(); + } + + Stream = inStream; + + authenticated = false; +} + +bool UCSConnection::Process() +{ + if (!Stream || !Stream->Connected()) + return false; + + ServerPacket *pack = 0; + + while((pack = Stream->PopPacket())) + { + if (!authenticated) + { + if (WorldConfig::get()->SharedKey.length() > 0) + { + if (pack->opcode == ServerOP_ZAAuth && pack->size == 16) + { + uint8 tmppass[16]; + + MD5::Generate((const uchar*) WorldConfig::get()->SharedKey.c_str(), WorldConfig::get()->SharedKey.length(), tmppass); + + if (memcmp(pack->pBuffer, tmppass, 16) == 0) + authenticated = true; + else + { + struct in_addr in; + in.s_addr = GetIP(); + _log(UCS__ERROR, "UCS authorization failed."); + ServerPacket* pack = new ServerPacket(ServerOP_ZAAuthFailed); + SendPacket(pack); + delete pack; + Disconnect(); + return false; + } + } + else + { + struct in_addr in; + in.s_addr = GetIP(); + _log(UCS__ERROR, "UCS authorization failed."); + ServerPacket* pack = new ServerPacket(ServerOP_ZAAuthFailed); + SendPacket(pack); + delete pack; + Disconnect(); + return false; + } + } + else + { + _log(UCS__ERROR,"**WARNING** You have not configured a world shared key in your config file. You should add a STRING element to your element to prevent unauthroized zone access."); + authenticated = true; + } + delete pack; + continue; + } + switch(pack->opcode) + { + case 0: + break; + + case ServerOP_KeepAlive: + { + // ignore this + break; + } + case ServerOP_ZAAuth: + { + _log(UCS__ERROR, "Got authentication from UCS when they are already authenticated."); + break; + } + default: + { + _log(UCS__ERROR, "Unknown ServerOPcode from UCS 0x%04x, size %d", pack->opcode, pack->size); + DumpPacket(pack->pBuffer, pack->size); + break; + } + } + + delete pack; + } + return(true); +} + +bool UCSConnection::SendPacket(ServerPacket* pack) +{ + if(!Stream) + return false; + + return Stream->SendPacket(pack); +} + +void UCSConnection::SendMessage(const char *From, const char *Message) +{ + ServerPacket* pack = new ServerPacket(ServerOP_UCSMessage, strlen(From) + strlen(Message) + 2); + + char *Buffer = (char *)pack->pBuffer; + + VARSTRUCT_ENCODE_STRING(Buffer, From); + VARSTRUCT_ENCODE_STRING(Buffer, Message); + + SendPacket(pack); + safe_delete(pack); +} diff --git a/world/ucs.h b/world/ucs.h new file mode 100644 index 000000000..a65d82e3d --- /dev/null +++ b/world/ucs.h @@ -0,0 +1,23 @@ +#ifndef UCS_H +#define UCS_H + +#include "../common/types.h" +#include "../common/EmuTCPConnection.h" +#include "../common/servertalk.h" + +class UCSConnection +{ +public: + UCSConnection(); + void SetConnection(EmuTCPConnection *inStream); + bool Process(); + bool SendPacket(ServerPacket* pack); + void Disconnect() { if(Stream) Stream->Disconnect(); } + void SendMessage(const char *From, const char *Message); +private: + inline uint32 GetIP() const { return Stream ? Stream->GetrIP() : 0; } + EmuTCPConnection *Stream; + bool authenticated; +}; + +#endif /*UCS_H_*/ diff --git a/world/wguild_mgr.cpp b/world/wguild_mgr.cpp new file mode 100644 index 000000000..eba8451f6 --- /dev/null +++ b/world/wguild_mgr.cpp @@ -0,0 +1,172 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "wguild_mgr.h" +#include "../common/servertalk.h" +#include "clientlist.h" +#include "zonelist.h" + + +extern ClientList client_list; +extern ZSList zoneserver_list; + + + +WorldGuildManager guild_mgr; + + + +void WorldGuildManager::SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation) { + _log(GUILDS__REFRESH, "Broadcasting guild refresh for %d, changes: name=%d, motd=%d, rank=d, relation=%d", guild_id, name, motd, rank, relation); + ServerPacket* pack = new ServerPacket(ServerOP_RefreshGuild, sizeof(ServerGuildRefresh_Struct)); + ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer; + s->guild_id = guild_id; + s->name_change = name; + s->motd_change = motd; + s->rank_change = rank; + s->relation_change = relation; + zoneserver_list.SendPacket(pack); + safe_delete(pack); +} + +void WorldGuildManager::SendCharRefresh(uint32 old_guild_id, uint32 guild_id, uint32 charid) { + _log(GUILDS__REFRESH, "Broadcasting char refresh for %d from guild %d to world", charid, guild_id); + ServerPacket* pack = new ServerPacket(ServerOP_GuildCharRefresh, sizeof(ServerGuildCharRefresh_Struct)); + ServerGuildCharRefresh_Struct *s = (ServerGuildCharRefresh_Struct *) pack->pBuffer; + s->guild_id = guild_id; + s->old_guild_id = old_guild_id; + s->char_id = charid; + zoneserver_list.SendPacket(pack); + safe_delete(pack); +} + +void WorldGuildManager::SendGuildDelete(uint32 guild_id) { + _log(GUILDS__REFRESH, "Broadcasting guild delete for guild %d to world", guild_id); + ServerPacket* pack = new ServerPacket(ServerOP_DeleteGuild, sizeof(ServerGuildID_Struct)); + ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer; + s->guild_id = guild_id; + zoneserver_list.SendPacket(pack); + safe_delete(pack); +} + +void WorldGuildManager::ProcessZonePacket(ServerPacket *pack) { + switch(pack->opcode) { + + case ServerOP_RefreshGuild: { + if(pack->size != sizeof(ServerGuildRefresh_Struct)) { + _log(GUILDS__ERROR, "Received ServerOP_RefreshGuild of incorrect size %d, expected %d", pack->size, sizeof(ServerGuildRefresh_Struct)); + return; + } + ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer; + _log(GUILDS__REFRESH, "Received and broadcasting guild refresh for %d, changes: name=%d, motd=%d, rank=d, relation=%d", s->guild_id, s->name_change, s->motd_change, s->rank_change, s->relation_change); + + //broadcast this packet to all zones. + zoneserver_list.SendPacket(pack); + + //preform a local refresh. + if(!RefreshGuild(s->guild_id)) { + _log(GUILDS__ERROR, "Unable to preform local refresh on guild %d", s->guild_id); + //can we do anything? + } + + break; + } + + case ServerOP_GuildCharRefresh: { + if(pack->size != sizeof(ServerGuildCharRefresh_Struct)) { + _log(GUILDS__ERROR, "Received ServerOP_RefreshGuild of incorrect size %d, expected %d", pack->size, sizeof(ServerGuildCharRefresh_Struct)); + return; + } + ServerGuildCharRefresh_Struct *s = (ServerGuildCharRefresh_Struct *) pack->pBuffer; + _log(GUILDS__REFRESH, "Received and broadcasting guild member refresh for char %d to all zones with members of guild %d", s->char_id, s->guild_id); + + //preform the local update + client_list.UpdateClientGuild(s->char_id, s->guild_id); + + //broadcast this update to any zone with a member in this guild. + //client_list.SendGuildPacket(s->guild_id, pack); + //because im sick of this not working, sending it to all zones, just spends a bit more bandwidth. + zoneserver_list.SendPacket(pack); + + break; + } + + case ServerOP_DeleteGuild: { + if(pack->size != sizeof(ServerGuildID_Struct)) { + _log(GUILDS__ERROR, "Received ServerOP_DeleteGuild of incorrect size %d, expected %d", pack->size, sizeof(ServerGuildID_Struct)); + return; + } + ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer; + _log(GUILDS__REFRESH, "Received and broadcasting guild delete for guild %d", s->guild_id); + + //broadcast this packet to all zones. + zoneserver_list.SendPacket(pack); + + //preform a local refresh. + if(!LocalDeleteGuild(s->guild_id)) { + _log(GUILDS__ERROR, "Unable to preform local delete on guild %d", s->guild_id); + //can we do anything? + } + + break; + } + + case ServerOP_GuildMemberUpdate: { + if(pack->size != sizeof(ServerGuildMemberUpdate_Struct)) + { + _log(GUILDS__ERROR, "Received ServerOP_GuildMemberUpdate of incorrect size %d, expected %d", pack->size, sizeof(ServerGuildMemberUpdate_Struct)); + return; + } + + zoneserver_list.SendPacket(pack); + + break; + } + + default: + _log(GUILDS__ERROR, "Unknown packet 0x%x received from zone??", pack->opcode); + break; + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/world/wguild_mgr.h b/world/wguild_mgr.h new file mode 100644 index 000000000..d5200d82d --- /dev/null +++ b/world/wguild_mgr.h @@ -0,0 +1,31 @@ +#ifndef GUILD_MGR_H_ +#define GUILD_MGR_H_ + +#include "../common/types.h" +#include "../common/guild_base.h" + +class Client; +class ServerPacket; + +class WorldGuildManager : public BaseGuildManager { +public: + + //called by zoneserver when it receives a guild message from zone. + void ProcessZonePacket(ServerPacket *pack); + + uint8 *MakeGuildMembers(uint32 guild_id, const char *prefix_name, uint32 &length); //make a guild member list packet, returns ownership of the buffer. + +protected: + virtual void SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation); + virtual void SendCharRefresh(uint32 old_guild_id, uint32 guild_id, uint32 charid); + virtual void SendRankUpdate(uint32 CharID) { return; } + virtual void SendGuildDelete(uint32 guild_id); + + //map m_tribute; //map from guild ID to current tribute ammount +}; + +extern WorldGuildManager guild_mgr; + + +#endif /*GUILD_MGR_H_*/ + diff --git a/world/world_logsys.cpp b/world/world_logsys.cpp new file mode 100644 index 000000000..d3934e126 --- /dev/null +++ b/world/world_logsys.cpp @@ -0,0 +1,58 @@ + +#include "../common/debug.h" +#include "../common/logsys.h" +#include "zoneserver.h" +#include "client.h" +#include +#include + + +void log_message_clientVA(LogType type, Client *who, const char *fmt, va_list args) { + char prefix_buffer[256]; + snprintf(prefix_buffer, 255, "[%s] %s: ", log_type_info[type].name, who->GetAccountName()); + prefix_buffer[255] = '\0'; + + LogFile->writePVA(EQEMuLog::Debug, prefix_buffer, fmt, args); +} + +void log_message_client(LogType type, Client *who, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + log_message_clientVA(type, who, fmt, args); + va_end(args); +} + +void log_message_zoneVA(LogType type, ZoneServer *who, const char *fmt, va_list args) { + + char prefix_buffer[256]; + char zone_tag[65]; + const char *zone_name=who->GetZoneName(); + if (*zone_name==0) + snprintf(zone_tag,64,"[%d]", who->GetID()); + else + snprintf(zone_tag,64,"[%d] [%s]",who->GetID(),zone_name); + + snprintf(prefix_buffer, 255, "[%s] %s ", log_type_info[type].name, zone_tag); + prefix_buffer[255] = '\0'; + + LogFile->writePVA(EQEMuLog::Debug, prefix_buffer, fmt, args); +} + +void log_message_zone(LogType type, ZoneServer *who, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + log_message_zoneVA(type, who, fmt, args); + va_end(args); +} + + + + + + + + + + + + diff --git a/world/worlddb.cpp b/world/worlddb.cpp new file mode 100644 index 000000000..4de6b1e3c --- /dev/null +++ b/world/worlddb.cpp @@ -0,0 +1,650 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "worlddb.h" +//#include "../common/Item.h" +#include "../common/MiscFunctions.h" +#include "../common/eq_packet_structs.h" +#include "../common/Item.h" +#include "../common/dbasync.h" +#include "../common/rulesys.h" +#include +#include +#include +#include "SoFCharCreateData.h" + +using namespace std; +WorldDatabase database; +extern std::vector character_create_allocations; +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; + Inventory *inv; + + for (int i=0; i<10; i++) { + strcpy(cs->name[i], ""); + cs->zone[i] = 0; + cs->level[i] = 0; + cs->tutorial[i] = 0; + cs->gohome[i] = 0; + } + + int char_num = 0; + unsigned long* lengths; + + // 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]); + + // 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, EnableReturnHomeButton)) { + int now = time(NULL); + if((now - pp->lastlogin) >= RuleI(World, MinOfflineTimeToReturnHome)) + cs->gohome[char_num] = 1; + } + + + // 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==MATERIAL_PRIMARY) || (material==MATERIAL_SECONDARY)) + { + if(strlen(item->GetItem()->IDFile) > 2) { + uint32 idfile=atoi(&item->GetItem()->IDFile[2]); + if (material==MATERIAL_PRIMARY) + 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 + { + cout << "Got a bogus character (" << row[0] << ") Ignoring!!!" << endl; + cout << "PP length ="< 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; + } + safe_delete_array(query); + + return pp.binds[bindnum].zoneId; +} + +bool WorldDatabase::GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row = 0; + int rows; + + if(!in_pp || !in_cc) + return false; + + in_pp->x = in_pp->y = in_pp->z = in_pp->heading = in_pp->zone_id = 0; + in_pp->binds[0].x = in_pp->binds[0].y = in_pp->binds[0].z = in_pp->binds[0].zoneId = 0; + + 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::Status, "Start zone query: %s\n", query); + safe_delete_array(query); + + 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"); + switch(in_cc->start_zone) + { + case 0: + { + in_pp->zone_id = 24; // erudnext + in_pp->binds[0].zoneId = 38; // tox + break; + } + case 1: + { + in_pp->zone_id =2; // qeynos2 + in_pp->binds[0].zoneId = 2; // qeynos2 + break; + } + case 2: + { + in_pp->zone_id =29; // halas + in_pp->binds[0].zoneId = 30; // everfrost + break; + } + case 3: + { + in_pp->zone_id =19; // rivervale + in_pp->binds[0].zoneId = 20; // kithicor + break; + } + case 4: + { + in_pp->zone_id =9; // freportw + in_pp->binds[0].zoneId = 9; // freportw + break; + } + case 5: + { + in_pp->zone_id =40; // neriaka + in_pp->binds[0].zoneId = 25; // nektulos + break; + } + case 6: + { + in_pp->zone_id =52; // gukta + in_pp->binds[0].zoneId = 46; // innothule + break; + } + case 7: + { + in_pp->zone_id =49; // oggok + in_pp->binds[0].zoneId = 47; // feerrott + break; + } + case 8: + { + in_pp->zone_id =60; // kaladima + in_pp->binds[0].zoneId = 68; // butcher + break; + } + case 9: + { + in_pp->zone_id =54; // gfaydark + in_pp->binds[0].zoneId = 54; // gfaydark + break; + } + case 10: + { + in_pp->zone_id =61; // felwithea + in_pp->binds[0].zoneId = 54; // gfaydark + break; + } + case 11: + { + in_pp->zone_id =55; // akanon + in_pp->binds[0].zoneId = 56; // steamfont + break; + } + case 12: + { + in_pp->zone_id =82; // cabwest + in_pp->binds[0].zoneId = 78; // fieldofbone + break; + } + case 13: + { + in_pp->zone_id =155; // sharvahl + in_pp->binds[0].zoneId = 155; // sharvahl + break; + } + } + } + + if(in_pp->x == 0 && in_pp->y == 0 && in_pp->z == 0) + database.GetSafePoints(in_pp->zone_id, 0, &in_pp->x, &in_pp->y, &in_pp->z); + + 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. + // + // 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; + + 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: %s\n", query); + _log(WORLD__CLIENT_TRACE, "SoF Start zone query: %s\n", query); + safe_delete_array(query); + + 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(in_cc->start_zone == RuleI(World, TutorialZoneID)) + in_pp->zone_id = in_cc->start_zone; + else { + in_pp->x = in_pp->binds[0].x = -51; + in_pp->y = in_pp->binds[0].y = -20; + in_pp->z = in_pp->binds[0].z = 0.79; + in_pp->zone_id = in_pp->binds[0].zoneId = 394; // Crescent Reach. + } + + } + + if(in_pp->x == 0 && in_pp->y == 0 && in_pp->z == 0) + database.GetSafePoints(in_pp->zone_id, 0, &in_pp->x, &in_pp->y, &in_pp->z); + + 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); +} + +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) + sprintf(MailKeyString, "%08X%08X", IPAddress, 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); + +} + +bool WorldDatabase::GetCharacterLevel(const char *name, int &level) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + 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; +} + +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; + } + + return true; +} + +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; + } + + return true; +} + + + diff --git a/world/worlddb.h b/world/worlddb.h new file mode 100644 index 000000000..ffa5af953 --- /dev/null +++ b/world/worlddb.h @@ -0,0 +1,50 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 WORLDDB_H_ +#define WORLDDB_H_ + +#include "../common/shareddb.h" +#include "../common/ZoneNumbers.h" + +struct PlayerProfile_Struct; +struct CharCreate_Struct; +struct CharacterSelect_Struct; + + +class WorldDatabase : public SharedDatabase { +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*); + int MoveCharacterToBind(int CharID, uint8 bindnum = 0); + + void GetLauncherList(std::vector &result); + void SetMailKey(int CharID, int IPAddress, int MailKey); + bool GetCharacterLevel(const char *name, int &level); + + bool LoadCharacterCreateAllocations(); + bool LoadCharacterCreateCombos(); +protected: + +}; + +extern WorldDatabase database; + + +#endif /*WORLDDB_H_*/ diff --git a/world/zonelist.cpp b/world/zonelist.cpp new file mode 100644 index 000000000..03b6567de --- /dev/null +++ b/world/zonelist.cpp @@ -0,0 +1,730 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 "zonelist.h" +#include "zoneserver.h" +#include "WorldTCPConnection.h" +#include "worlddb.h" +#include "console.h" +#include "WorldConfig.h" +#include "../common/servertalk.h" +#include "../common/MiscFunctions.h" + +extern uint32 numzones; +extern bool holdzones; +extern ConsoleList console_list; + + + +ZSList::ZSList() +{ + NextID = 1; + CurGroupID = 1; + LastAllocatedPort=0; + memset(pLockedZones, 0, sizeof(pLockedZones)); +} + +ZSList::~ZSList() { +} + +void ZSList::ShowUpTime(WorldTCPConnection* con, const char* adminname) { + uint32 ms = Timer::GetCurrentTime(); + uint32 d = ms / 86400000; + ms -= d * 86400000; + uint32 h = ms / 3600000; + ms -= h * 3600000; + uint32 m = ms / 60000; + ms -= m * 60000; + uint32 s = ms / 1000; + if (d) + con->SendEmoteMessage(adminname, 0, 0, 0, "Worldserver Uptime: %02id %02ih %02im %02is", d, h, m, s); + else if (h) + con->SendEmoteMessage(adminname, 0, 0, 0, "Worldserver Uptime: %02ih %02im %02is", h, m, s); + else + con->SendEmoteMessage(adminname, 0, 0, 0, "Worldserver Uptime: %02im %02is", m, s); +} + +void ZSList::Add(ZoneServer* zoneserver) { + list.Insert(zoneserver); + zoneserver->SendGroupIDs(); //send its initial set of group ids +} + +void ZSList::KillAll() { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) { + iterator.GetData()->Disconnect(); + iterator.RemoveCurrent(); + numzones--; + } +} + +void ZSList::Process() { + + if(shutdowntimer && shutdowntimer->Check()){ + _log(WORLD__ZONELIST, "Shutdown timer has expired. Telling all zones to shut down and exiting. (fake sigint)"); + ServerPacket* pack2 = new ServerPacket; + pack2->opcode = ServerOP_ShutdownAll; + pack2->size=0; + SendPacket(pack2); + safe_delete(pack2); + 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); + } + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (!iterator.GetData()->Process()) { + ZoneServer* zs = iterator.GetData(); + struct in_addr in; + in.s_addr = zs->GetIP(); + _log(WORLD__ZONELIST,"Removing zoneserver #%d at %s:%d",zs->GetID(),zs->GetCAddress(),zs->GetCPort()); + zs->LSShutDownUpdate(zs->GetZoneID()); + if (holdzones){ + _log(WORLD__ZONELIST,"Hold Zones mode is ON - rebooting lost zone"); + if(!zs->IsStaticZone()) + RebootZone(inet_ntoa(in),zs->GetCPort(),zs->GetCAddress(),zs->GetID()); + else + RebootZone(inet_ntoa(in),zs->GetCPort(),zs->GetCAddress(),zs->GetID(),database.GetZoneID(zs->GetZoneName())); + } + + iterator.RemoveCurrent(); + numzones--; + } + else { + iterator.Advance(); + } + } +} + +bool ZSList::SendPacket(ServerPacket* pack) { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) { + iterator.GetData()->SendPacket(pack); + iterator.Advance(); + } + return true; +} + +bool ZSList::SendPacket(uint32 ZoneID, ServerPacket* pack) { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetZoneID() == ZoneID) { + ZoneServer* tmp = iterator.GetData(); + return(tmp->SendPacket(pack)); + } + iterator.Advance(); + } + return(false); +} + +bool ZSList::SendPacket(uint32 ZoneID, uint16 instanceID, ServerPacket* pack) { + LinkedListIterator iterator(list); + + iterator.Reset(); + if(instanceID != 0) + { + while(iterator.MoreElements()) { + if(iterator.GetData()->GetInstanceID() == instanceID) { + ZoneServer* tmp = iterator.GetData(); + return(tmp->SendPacket(pack)); + } + iterator.Advance(); + } + } + else + { + while(iterator.MoreElements()) { + if (iterator.GetData()->GetZoneID() == ZoneID + && iterator.GetData()->GetInstanceID() == 0) { + ZoneServer* tmp = iterator.GetData(); + return(tmp->SendPacket(pack)); + } + iterator.Advance(); + } + } + return(false); +} + +ZoneServer* ZSList::FindByName(const char* zonename) { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (strcasecmp(iterator.GetData()->GetZoneName(), zonename) == 0) { + ZoneServer* tmp = iterator.GetData(); + return tmp; + } + iterator.Advance(); + } + return 0; +} + +ZoneServer* ZSList::FindByID(uint32 ZoneID) { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetID() == ZoneID) { + ZoneServer* tmp = iterator.GetData(); + return tmp; + } + iterator.Advance(); + } + return 0; +} + +ZoneServer* ZSList::FindByZoneID(uint32 ZoneID) { + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) + { + ZoneServer* tmp = iterator.GetData(); + if (tmp->GetZoneID() == ZoneID && tmp->GetInstanceID() == 0) { + return tmp; + } + iterator.Advance(); + } + return 0; +} + +ZoneServer* ZSList::FindByPort(uint16 port) { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetCPort() == port) { + ZoneServer* tmp = iterator.GetData(); + return tmp; + } + iterator.Advance(); + } + return 0; +} + +ZoneServer* ZSList::FindByInstanceID(uint32 InstanceID) +{ + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetInstanceID() == InstanceID) { + ZoneServer* tmp = iterator.GetData(); + return tmp; + } + iterator.Advance(); + } + return 0; +} + +bool ZSList::SetLockedZone(uint16 iZoneID, bool iLock) { + for (int i=0; iSendEmoteMessageRaw(to, 0, 0, 0, database.GetZoneName(pLockedZones[i], true)); + x++; + } + } + connection->SendEmoteMessage(to, 0, 0, 0, "%i zones locked.", x); +} + +void ZSList::SendZoneStatus(const char* to, int16 admin, WorldTCPConnection* connection) { + LinkedListIterator iterator(list); + struct in_addr in; + + iterator.Reset(); + char locked[4]; + if (WorldConfig::get()->Locked == true) + strcpy(locked, "Yes"); + else + strcpy(locked, "No"); + + char* output = 0; + uint32 outsize = 0, outlen = 0; + if (connection->IsConsole()) + AppendAnyLenString(&output, &outsize, &outlen, "World Locked: %s\r\n", locked); + else + AppendAnyLenString(&output, &outsize, &outlen, "World Locked: %s^", locked); + if (connection->IsConsole()) + AppendAnyLenString(&output, &outsize, &outlen, "Zoneservers online:\r\n"); + else + AppendAnyLenString(&output, &outsize, &outlen, "Zoneservers online:^"); +// connection->SendEmoteMessage(to, 0, 0, 0, "World Locked: %s", locked); +// connection->SendEmoteMessage(to, 0, 0, 0, "Zoneservers online:"); + int v=0, w=0, x=0, y=0, z=0; + char tmpStatic[2] = { 0, 0 }, tmpZone[64]; + memset(tmpZone, 0, sizeof(tmpZone)); + ZoneServer* zs = 0; + while(iterator.MoreElements()) { + zs = iterator.GetData(); + in.s_addr = zs->GetIP(); + + if(zs->IsStaticZone()) + z++; + else if (zs->GetZoneID() != 0) + w++; + else if(zs->GetZoneID() == 0 && !zs->IsBootingUp()) + v++; + + if (zs->IsStaticZone()) + tmpStatic[0] = 'S'; + else + tmpStatic[0] = ' '; + + if (admin >= 150) { + if (zs->GetZoneID()) + snprintf(tmpZone, sizeof(tmpZone), "%s (%i)", zs->GetZoneName(), zs->GetZoneID()); + else if (zs->IsBootingUp()) + strcpy(tmpZone, "..."); + else + tmpZone[0] = 0; + + AppendAnyLenString(&output, &outsize, &outlen, " #%-3i %s %15s:%-5i %2i %s:%i %s", zs->GetID(), tmpStatic, inet_ntoa(in), zs->GetPort(), zs->NumPlayers(), zs->GetCAddress(), zs->GetCPort(), tmpZone); + if (outlen >= 3584) { + connection->SendEmoteMessageRaw(to, 0, 0, 10, output); + safe_delete(output); + outsize = 0; + outlen = 0; + } + else { + if (connection->IsConsole()) + AppendAnyLenString(&output, &outsize, &outlen, "\r\n"); + else + AppendAnyLenString(&output, &outsize, &outlen, "^"); + } + x++; + } + else if (zs->GetZoneID() != 0) { + if (zs->GetZoneID()) + strcpy(tmpZone, zs->GetZoneName()); + else + tmpZone[0] = 0; + AppendAnyLenString(&output, &outsize, &outlen, " #%i %s %s", zs->GetID(), tmpStatic, tmpZone); + if (outlen >= 3584) { + connection->SendEmoteMessageRaw(to, 0, 0, 10, output); + safe_delete(output); + outsize = 0; + outlen = 0; + } + else { + if (connection->IsConsole()) + AppendAnyLenString(&output, &outsize, &outlen, "\r\n"); + else + AppendAnyLenString(&output, &outsize, &outlen, "^"); + } + x++; + } + y++; + iterator.Advance(); + } + if (connection->IsConsole()) + AppendAnyLenString(&output, &outsize, &outlen, "%i servers listed. %i servers online.\r\n", x, y); + else + AppendAnyLenString(&output, &outsize, &outlen, "%i servers listed. %i servers online.^", x, y); + AppendAnyLenString(&output, &outsize, &outlen, "%i zones are static zones, %i zones are booted zones, %i zones available.",z,w,v); +// connection->SendEmoteMessage(to, 0, 0, "%i servers listed. %i servers online.", x, y); +// connection->SendEmoteMessage(to,0,0,"%i zones are static zones, %i zones are booted zones, %i zones available.",z,w,v); + if (output) + connection->SendEmoteMessageRaw(to, 0, 0, 10, output); + safe_delete(output); +} + +void ZSList::SendChannelMessage(const char* from, const char* to, uint8 chan_num, uint8 language, const char* message, ...) { + if (!message) + return; + va_list argptr; + char buffer[1024]; + + va_start(argptr, message); + vsnprintf(buffer, sizeof(buffer), message, argptr); + va_end(argptr); + + SendChannelMessageRaw(from, to, chan_num, language, buffer); +} + +void ZSList::SendChannelMessageRaw(const char* from, const char* to, uint8 chan_num, uint8 language, const char* message) { + if (!message) + return; + ServerPacket* pack = new ServerPacket; + + pack->opcode = ServerOP_ChannelMessage; + pack->size = sizeof(ServerChannelMessage_Struct)+strlen(message)+1; + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer, 0, pack->size); + ServerChannelMessage_Struct* scm = (ServerChannelMessage_Struct*) pack->pBuffer; + if (from == 0) { + strcpy(scm->from, "WServer"); + scm->noreply = true; + } + else if (from[0] == 0) { + strcpy(scm->from, "WServer"); + scm->noreply = true; + } + else + strcpy(scm->from, from); + if (to != 0) { + strcpy((char *) scm->to, to); + strcpy((char *) scm->deliverto, to); + } + else { + scm->to[0] = 0; + scm->deliverto[0] = 0; + } + + scm->language = language; + scm->chan_num = chan_num; + strcpy(&scm->message[0], message); + if (scm->chan_num == 5 || scm->chan_num == 6 || scm->chan_num == 11) { + console_list.SendChannelMessage(scm); + } + pack->Deflate(); + SendPacket(pack); + delete pack; +} + + +void ZSList::SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...) { + if (!message) + return; + va_list argptr; + char buffer[1024]; + + va_start(argptr, message); + vsnprintf(buffer, sizeof(buffer), message, argptr); + va_end(argptr); + + SendEmoteMessageRaw(to, to_guilddbid, to_minstatus, type, buffer); +} + +void ZSList::SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message) { + if (!message) + return; + ServerPacket* pack = new ServerPacket; + + pack->opcode = ServerOP_EmoteMessage; + pack->size = sizeof(ServerEmoteMessage_Struct)+strlen(message)+1; + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer, 0, pack->size); + ServerEmoteMessage_Struct* sem = (ServerEmoteMessage_Struct*) pack->pBuffer; + + if (to) { + if (to[0] == '*') { + Console* con = console_list.FindByAccountName(&to[1]); + if (con) + con->SendEmoteMessageRaw(to, to_guilddbid, to_minstatus, type, message); + delete pack; + return; + } + strcpy((char *) sem->to, to); + } + else { + sem->to[0] = 0; + } + + sem->guilddbid = to_guilddbid; + sem->minstatus = to_minstatus; + sem->type = type; + strcpy(&sem->message[0], message); + char tempto[64]={0}; + if(to) + strn0cpy(tempto,to,64); + pack->Deflate(); + if (tempto[0] == 0) { + SendPacket(pack); + if (to_guilddbid == 0) + console_list.SendEmoteMessageRaw(type, message); + } + else { + ZoneServer* zs = FindByName(to); + + if (zs != 0) + zs->SendPacket(pack); + else + SendPacket(pack); + } + delete pack; +} + +void ZSList::SendTimeSync() { + ServerPacket* pack = new ServerPacket(ServerOP_SyncWorldTime, sizeof(eqTimeOfDay)); + eqTimeOfDay* tod = (eqTimeOfDay*) pack->pBuffer; + tod->start_eqtime=worldclock.getStartEQTime(); + tod->start_realtime=worldclock.getStartRealTime(); + SendPacket(pack); + delete pack; +} + +void ZSList::NextGroupIDs(uint32 &start, uint32 &end) { + start = CurGroupID; + CurGroupID += 1000; //hand them out 1000 at a time... + if(CurGroupID < start) { //handle overflow + start = 1; + CurGroupID = 1001; + } + end = CurGroupID - 1; +} + +void ZSList::SOPZoneBootup(const char* adminname, uint32 ZoneServerID, const char* zonename, bool iMakeStatic) { + ZoneServer* zs = 0; + ZoneServer* zs2 = 0; + uint32 zoneid; + if (!(zoneid = database.GetZoneID(zonename))) + SendEmoteMessage(adminname, 0, 0, 0, "Error: SOP_ZoneBootup: zone '%s' not found in 'zone' table. Typo protection=ON.", zonename); + else { + if (ZoneServerID != 0) + zs = FindByID(ZoneServerID); + else + SendEmoteMessage(adminname, 0, 0, 0, "Error: SOP_ZoneBootup: ServerID must be specified"); + + if (zs == 0) + SendEmoteMessage(adminname, 0, 0, 0, "Error: SOP_ZoneBootup: zoneserver not found"); + else { + zs2 = FindByName(zonename); + if (zs2 != 0) + SendEmoteMessage(adminname, 0, 0, 0, "Error: SOP_ZoneBootup: zone '%s' already being hosted by ZoneServer #%i", zonename, zs2->GetID()); + else { + zs->TriggerBootup(zoneid, 0, adminname, iMakeStatic); + } + } + } +} + +void ZSList::RebootZone(const char* ip1,uint16 port,const char* ip2, uint32 skipid, uint32 zoneid){ +// get random zone + LinkedListIterator iterator(list); + uint32 x = 0; + iterator.Reset(); + while(iterator.MoreElements()) { + x++; + iterator.Advance(); + } + if (x == 0) + return; + ZoneServer** tmp = new ZoneServer*[x]; + uint32 y = 0; + iterator.Reset(); + while(iterator.MoreElements()) { + if (!strcmp(iterator.GetData()->GetCAddress(),ip2) && !iterator.GetData()->IsBootingUp() && iterator.GetData()->GetID() != skipid) { + tmp[y++] = iterator.GetData(); + } + iterator.Advance(); + } + if (y == 0) { + safe_delete(tmp); + return; + } + uint32 z = MakeRandomInt(0, y-1); + + ServerPacket* pack = new ServerPacket(ServerOP_ZoneReboot, sizeof(ServerZoneReboot_Struct)); + ServerZoneReboot_Struct* s = (ServerZoneReboot_Struct*) pack->pBuffer; +// strcpy(s->ip1,ip1); + strcpy(s->ip2,ip2); + s->port = port; + s->zoneid = zoneid; + if(zoneid != 0) + _log(WORLD__ZONELIST,"Rebooting static zone with the ID of: %i",zoneid); + tmp[z]->SendPacket(pack); + delete pack; + safe_delete_array(tmp); +} + +uint16 ZSList::GetAvailableZonePort() +{ + const WorldConfig *Config=WorldConfig::get(); + int i; + uint16 port=0; + + if (LastAllocatedPort==0) + i=Config->ZonePortLow; + else + i=LastAllocatedPort+1; + + while(i!=LastAllocatedPort && port==0) { + if (i>Config->ZonePortHigh) + i=Config->ZonePortLow; + + if (!FindByPort(i)) { + port=i; + break; + } + i++; + } + LastAllocatedPort=port; + + return port; +} + +uint32 ZSList::TriggerBootup(uint32 iZoneID, uint32 iInstanceID) { + if(iInstanceID > 0) + { + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) { + if(iterator.GetData()->GetInstanceID() == iInstanceID) + { + return iterator.GetData()->GetID(); + } + iterator.Advance(); + } + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetZoneID() == 0 && !iterator.GetData()->IsBootingUp()) { + ZoneServer* zone=iterator.GetData(); + zone->TriggerBootup(iZoneID, iInstanceID); + return zone->GetID(); + } + iterator.Advance(); + } + return 0; + } + else + { + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) { + if(iterator.GetData()->GetZoneID() == iZoneID && iterator.GetData()->GetInstanceID() == 0) + { + return iterator.GetData()->GetID(); + } + iterator.Advance(); + } + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetZoneID() == 0 && !iterator.GetData()->IsBootingUp()) { + ZoneServer* zone=iterator.GetData(); + zone->TriggerBootup(iZoneID); + return zone->GetID(); + } + iterator.Advance(); + } + return 0; + } + /*Old Random boot zones use this if your server is distributed across computers. + LinkedListIterator iterator(list); + + srand(time(NULL)); + uint32 x = 0; + iterator.Reset(); + while(iterator.MoreElements()) { + x++; + iterator.Advance(); + } + if (x == 0) { + return 0; + } + + ZoneServer** tmp = new ZoneServer*[x]; + uint32 y = 0; + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetZoneID() == 0 && !iterator.GetData()->IsBootingUp()) { + tmp[y++] = iterator.GetData(); + } + iterator.Advance(); + } + if (y == 0) { + safe_delete(tmp); + return 0; + } + + uint32 z = rand() % y; + + tmp[z]->TriggerBootup(iZoneID); + uint32 ret = tmp[z]->GetID(); + safe_delete(tmp); + return ret; + */ +} + +void ZSList::SendLSZones(){ + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) { + ZoneServer* zs = iterator.GetData(); + zs->LSBootUpdate(zs->GetZoneID(),true); + iterator.Advance(); + } +} + +int ZSList::GetZoneCount() { + return(numzones); +} + +void ZSList::GetZoneIDList(vector &zones) { + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) { + ZoneServer* zs = iterator.GetData(); + zones.push_back(zs->GetID()); + iterator.Advance(); + } + +} + + + + + + + + + + + + diff --git a/world/zonelist.h b/world/zonelist.h new file mode 100644 index 000000000..cd8b36943 --- /dev/null +++ b/world/zonelist.h @@ -0,0 +1,82 @@ +#ifndef ZONELIST_H_ +#define ZONELIST_H_ + +#include "../common/types.h" +#include "../common/eqtime.h" +#include "../common/timer.h" +#include "../common/linked_list.h" +#include + +class WorldTCPConnection; +class ServerPacket; +class ZoneServer; + +class ZSList +{ +public: + enum { MaxLockedZones = 10 }; + + static void ShowUpTime(WorldTCPConnection* con, const char* adminname = 0); + + ZSList(); + ~ZSList(); + ZoneServer* FindByName(const char* zonename); + ZoneServer* FindByID(uint32 ZoneID); + ZoneServer* FindByZoneID(uint32 ZoneID); + ZoneServer* FindByPort(uint16 port); + ZoneServer* FindByInstanceID(uint32 InstanceID); + + void SendChannelMessage(const char* from, const char* to, uint8 chan_num, uint8 language, const char* message, ...); + void SendChannelMessageRaw(const char* from, const char* to, uint8 chan_num, uint8 language, const char* message); + void SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...); + void SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message); + + void SendZoneStatus(const char* to, int16 admin, WorldTCPConnection* connection); + + void SendTimeSync(); + void Add(ZoneServer* zoneserver); + void Process(); + void KillAll(); + bool SendPacket(ServerPacket* pack); + bool SendPacket(uint32 zoneid, ServerPacket* pack); + bool SendPacket(uint32 zoneid, uint16 instanceid, ServerPacket* pack); + inline uint32 GetNextID() { return NextID++; } + void RebootZone(const char* ip1,uint16 port, const char* ip2, uint32 skipid, uint32 zoneid = 0); + uint32 TriggerBootup(uint32 iZoneID, uint32 iInstanceID = 0); + void SOPZoneBootup(const char* adminname, uint32 ZoneServerID, const char* zonename, bool iMakeStatic = false); + EQTime worldclock; + bool SetLockedZone(uint16 iZoneID, bool iLock); + bool IsZoneLocked(uint16 iZoneID); + void ListLockedZones(const char* to, WorldTCPConnection* connection); + Timer* shutdowntimer; + Timer* reminder; + void NextGroupIDs(uint32 &start, uint32 &end); + void SendLSZones(); + uint16 GetAvailableZonePort(); + + int GetZoneCount(); + void GetZoneIDList(std::vector &zones); + +protected: + uint32 NextID; + LinkedList list; + uint16 pLockedZones[MaxLockedZones]; + uint32 CurGroupID; + uint16 LastAllocatedPort; + + +}; + + + + + + + + + + + + + +#endif /*ZONELIST_H_*/ diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp new file mode 100644 index 000000000..bab704fb3 --- /dev/null +++ b/world/zoneserver.cpp @@ -0,0 +1,1431 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 "zoneserver.h" +#include "clientlist.h" +#include "LoginServer.h" +#include "LoginServerList.h" +#include "zonelist.h" +#include "worlddb.h" +#include "console.h" +#include "client.h" +#include "../common/md5.h" +#include "WorldConfig.h" +#include "../common/guilds.h" +#include "../common/packet_dump.h" +#include "../common/misc.h" +#include "../common/MiscFunctions.h" +#include "cliententry.h" +#include "wguild_mgr.h" +#include "lfplist.h" +#include "AdventureManager.h" +#include "ucs.h" +#include "queryserv.h" + +extern ClientList client_list; +extern GroupLFPList LFPGroupList; +extern ZSList zoneserver_list; +extern ConsoleList console_list; +extern LoginServerList loginserverlist; +extern volatile bool RunLoops; +extern AdventureManager adventure_manager; +extern UCSConnection UCSLink; +extern QueryServConnection QSLink; + +ZoneServer::ZoneServer(EmuTCPConnection* itcpc) +: WorldTCPConnection(), tcpc(itcpc), ls_zboot(5000) { + ID = zoneserver_list.GetNextID(); + memset(zone_name, 0, sizeof(zone_name)); + memset(compiled, 0, sizeof(compiled)); + zoneID = 0; + instanceID = 0; + + memset(clientaddress, 0, sizeof(clientaddress)); + clientport = 0; + BootingUp = false; + authenticated = false; + staticzone = false; + pNumPlayers = 0; +} + +ZoneServer::~ZoneServer() { + if (RunLoops) + client_list.CLERemoveZSRef(this); + tcpc->Free(); +} + +bool ZoneServer::SetZone(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { + BootingUp = false; + + const char* zn = MakeLowerString(database.GetZoneName(iZoneID)); + char* longname; + + if (iZoneID) + zlog(WORLD__ZONE,"Setting to '%s' (%d:%d)%s",(zn) ? zn : "",iZoneID, iInstanceID, + iStaticZone ? " (Static)" : ""); + + zoneID = iZoneID; + instanceID = iInstanceID; + if(iZoneID!=0) + oldZoneID = iZoneID; + if (zoneID == 0) { + client_list.CLERemoveZSRef(this); + pNumPlayers = 0; + LSSleepUpdate(GetPrevZoneID()); + } + + staticzone = iStaticZone; + + if (zn) + { + strn0cpy(zone_name, zn, sizeof(zone_name)); + if( database.GetZoneLongName( (char*)zone_name, &longname, NULL, NULL, NULL, NULL, NULL, NULL ) ) + { + strn0cpy(long_name, longname, sizeof(long_name)); + safe_delete_array( longname ); + } + else + strcpy(long_name, ""); + } + else + { + strcpy(zone_name, ""); + strcpy(long_name, ""); + } + + client_list.ZoneBootup(this); + ls_zboot.Start(); + + return true; +} + +void ZoneServer::LSShutDownUpdate(uint32 zoneid){ + if(WorldConfig::get()->UpdateStats){ + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_LSZoneShutdown; + pack->size = sizeof(ZoneShutdown_Struct); + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer,0,pack->size); + ZoneShutdown_Struct* zsd =(ZoneShutdown_Struct*)pack->pBuffer; + if(zoneid==0) + zsd->zone = GetPrevZoneID(); + else + zsd->zone = zoneid; + zsd->zone_wid = GetID(); + loginserverlist.SendPacket(pack); + safe_delete(pack); + } +} +void ZoneServer::LSBootUpdate(uint32 zoneid, uint32 instanceid, bool startup){ + if(WorldConfig::get()->UpdateStats){ + ServerPacket* pack = new ServerPacket; + if(startup) + pack->opcode = ServerOP_LSZoneStart; + else + pack->opcode = ServerOP_LSZoneBoot; + pack->size = sizeof(ZoneBoot_Struct); + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer,0,pack->size); + ZoneBoot_Struct* bootup =(ZoneBoot_Struct*)pack->pBuffer; + if(startup) + strcpy(bootup->compile_time,GetCompileTime()); + bootup->zone = zoneid; + bootup->zone_wid = GetID(); + bootup->instance = instanceid; + loginserverlist.SendPacket(pack); + safe_delete(pack); + } +} + +void ZoneServer::LSSleepUpdate(uint32 zoneid){ + if(WorldConfig::get()->UpdateStats){ + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_LSZoneSleep; + pack->size = sizeof(ServerLSZoneSleep_Struct); + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer,0,pack->size); + ServerLSZoneSleep_Struct* sleep =(ServerLSZoneSleep_Struct*)pack->pBuffer; + sleep->zone = zoneid; + sleep->zone_wid = GetID(); + loginserverlist.SendPacket(pack); + safe_delete(pack); + } +} + +bool ZoneServer::Process() { + if (!tcpc->Connected()) + return false; + if(ls_zboot.Check()){ + LSBootUpdate(GetZoneID(), true); + ls_zboot.Disable(); + } + ServerPacket *pack = 0; + while((pack = tcpc->PopPacket())) { + _hex(WORLD__ZONE_TRACE,pack->pBuffer,pack->size); + if (!authenticated) { + if (WorldConfig::get()->SharedKey.length() > 0) { + if (pack->opcode == ServerOP_ZAAuth && pack->size == 16) { + uint8 tmppass[16]; + MD5::Generate((const uchar*) WorldConfig::get()->SharedKey.c_str(), WorldConfig::get()->SharedKey.length(), tmppass); + if (memcmp(pack->pBuffer, tmppass, 16) == 0) + authenticated = true; + else { + struct in_addr in; + in.s_addr = GetIP(); + zlog(WORLD__ZONE_ERR,"Zone authorization failed."); + ServerPacket* pack = new ServerPacket(ServerOP_ZAAuthFailed); + SendPacket(pack); + delete pack; + Disconnect(); + return false; + } + } + else { + struct in_addr in; + in.s_addr = GetIP(); + zlog(WORLD__ZONE_ERR,"Zone authorization failed."); + ServerPacket* pack = new ServerPacket(ServerOP_ZAAuthFailed); + SendPacket(pack); + delete pack; + Disconnect(); + return false; + } + } + else + { + _log(WORLD__ZONE,"**WARNING** You have not configured a world shared key in your config file. You should add a STRING element to your element to prevent unauthroized zone access."); + authenticated = true; + } + } + switch(pack->opcode) { + case 0: + break; + case ServerOP_KeepAlive: { + // ignore this + break; + } + case ServerOP_ZAAuth: { + break; + } + case ServerOP_LSZoneBoot:{ + if(pack->size==sizeof(ZoneBoot_Struct)){ + ZoneBoot_Struct* zbs= (ZoneBoot_Struct*)pack->pBuffer; + SetCompile(zbs->compile_time); + } + break; + } + case ServerOP_GroupInvite: { + if(pack->size != sizeof(GroupInvite_Struct)) + break; + + GroupInvite_Struct* gis = (GroupInvite_Struct*) pack->pBuffer; + + client_list.SendPacket(gis->invitee_name, pack); + break; + } + case ServerOP_GroupFollow: { + if(pack->size != sizeof(ServerGroupFollow_Struct)) + break; + + ServerGroupFollow_Struct *sgfs = (ServerGroupFollow_Struct *) pack->pBuffer; + + client_list.SendPacket(sgfs->gf.name1, pack); + break; + } + case ServerOP_GroupFollowAck: { + if(pack->size != sizeof(ServerGroupFollowAck_Struct)) + break; + + ServerGroupFollowAck_Struct *sgfas = (ServerGroupFollowAck_Struct *) pack->pBuffer; + + client_list.SendPacket(sgfas->Name, pack); + break; + } + case ServerOP_GroupCancelInvite: { + if(pack->size != sizeof(GroupCancel_Struct)) + break; + + GroupCancel_Struct *gcs = (GroupCancel_Struct *) pack->pBuffer; + + client_list.SendPacket(gcs->name1, pack); + break; + } + case ServerOP_GroupIDReq: { + SendGroupIDs(); + break; + } + case ServerOP_GroupLeave: { + if(pack->size != sizeof(ServerGroupLeave_Struct)) + break; + zoneserver_list.SendPacket(pack); //bounce it to all zones + break; + } + + case ServerOP_GroupJoin: { + if(pack->size != sizeof(ServerGroupJoin_Struct)) + break; + zoneserver_list.SendPacket(pack); //bounce it to all zones + break; + } + + case ServerOP_ForceGroupUpdate: { + if(pack->size != sizeof(ServerForceGroupUpdate_Struct)) + break; + zoneserver_list.SendPacket(pack); //bounce it to all zones + break; + } + + case ServerOP_OOZGroupMessage: { + zoneserver_list.SendPacket(pack); //bounce it to all zones + break; + } + + case ServerOP_DisbandGroup: { + if(pack->size != sizeof(ServerDisbandGroup_Struct)) + break; + zoneserver_list.SendPacket(pack); //bounce it to all zones + break; + } + + case ServerOP_RaidAdd:{ + if(pack->size != sizeof(ServerRaidGeneralAction_Struct)) + break; + + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_RaidRemove: { + if(pack->size != sizeof(ServerRaidGeneralAction_Struct)) + break; + + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_RaidDisband: { + if(pack->size != sizeof(ServerRaidGeneralAction_Struct)) + break; + + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_RaidLockFlag: { + if(pack->size != sizeof(ServerRaidGeneralAction_Struct)) + break; + + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_RaidChangeGroup: { + if(pack->size != sizeof(ServerRaidGeneralAction_Struct)) + break; + + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_UpdateGroup: { + if(pack->size != sizeof(ServerRaidGeneralAction_Struct)) + break; + + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_RaidGroupDisband: { + if(pack->size != sizeof(ServerRaidGeneralAction_Struct)) + break; + + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_RaidGroupAdd: { + if(pack->size != sizeof(ServerRaidGroupAction_Struct)) + break; + + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_RaidGroupRemove: { + if(pack->size != sizeof(ServerRaidGroupAction_Struct)) + break; + + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_RaidGroupSay: { + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_RaidSay: { + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_RaidGroupLeader: { + if(pack->size != sizeof(ServerRaidGeneralAction_Struct)) + break; + + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_RaidLeader: { + if(pack->size != sizeof(ServerRaidGeneralAction_Struct)) + break; + + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_DetailsChange: { + if(pack->size != sizeof(ServerRaidGeneralAction_Struct)) + break; + + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_SpawnCondition: { + if(pack->size != sizeof(ServerSpawnCondition_Struct)) + break; + //bounce the packet to the correct zone server, if its up + ServerSpawnCondition_Struct* ssc = (ServerSpawnCondition_Struct*)pack->pBuffer; + zoneserver_list.SendPacket(ssc->zoneID, ssc->instanceID, pack); + break; + } + case ServerOP_SpawnEvent: { + if(pack->size != sizeof(ServerSpawnEvent_Struct)) + break; + //bounce the packet to the correct zone server, if its up + ServerSpawnEvent_Struct* sse = (ServerSpawnEvent_Struct*)pack->pBuffer; + zoneserver_list.SendPacket(sse->zoneID, 0, pack); + break; + } + case ServerOP_ChannelMessage: { + ServerChannelMessage_Struct* scm = (ServerChannelMessage_Struct*) pack->pBuffer; + if(scm->chan_num == 20) + { + UCSLink.SendMessage(scm->from, scm->message); + break; + } + if (scm->chan_num == 7 || scm->chan_num == 14) { + if (scm->deliverto[0] == '*') { + 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); + break; + } + ClientListEntry* cle = client_list.FindCharacter(scm->deliverto); + if (cle == 0 || cle->Online() < CLE_Status_Zoning || (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) { + if (!scm->noreply) + zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); + } + else if (cle->Online() == CLE_Status_Zoning) { + if (!scm->noreply) { + 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); + } + // 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) + zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not contactable at this time'", scm->to, scm->to); + } + else + cle->Server()->SendPacket(pack); + } + else { + if (scm->chan_num == 5 || scm->chan_num == 6 || scm->chan_num == 11) { + console_list.SendChannelMessage(scm); + } + zoneserver_list.SendPacket(pack); + } + break; + } + case ServerOP_EmoteMessage: { + ServerEmoteMessage_Struct* sem = (ServerEmoteMessage_Struct*) pack->pBuffer; + zoneserver_list.SendEmoteMessageRaw(sem->to, sem->guilddbid, sem->minstatus, sem->type, sem->message); + break; + } + case ServerOP_VoiceMacro: { + + ServerVoiceMacro_Struct* svm = (ServerVoiceMacro_Struct*) pack->pBuffer; + + if(svm->Type == VoiceMacroTell) { + + ClientListEntry* cle = client_list.FindCharacter(svm->To); + + if (!cle || (cle->Online() < CLE_Status_Zoning) || !cle->Server()) { + + zoneserver_list.SendEmoteMessage(svm->From, 0, 0, 0, "'%s is not online at this time'", svm->To); + + break; + } + + cle->Server()->SendPacket(pack); + } + else + zoneserver_list.SendPacket(pack); + + break; + } + + case ServerOP_RezzPlayerAccept: { + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_RezzPlayer: { + + RezzPlayer_Struct* sRezz = (RezzPlayer_Struct*) pack->pBuffer; + if (zoneserver_list.SendPacket(pack)){ + zlog(WORLD__ZONE,"Sent Rez packet for %s",sRezz->rez.your_name); + } + else { + zlog(WORLD__ZONE,"Could not send Rez packet for %s",sRezz->rez.your_name); + } + break; + } + case ServerOP_RezzPlayerReject: + { + char *Recipient = (char *)pack->pBuffer; + client_list.SendPacket(Recipient, pack); + break; + } + + case ServerOP_MultiLineMsg: { + ServerMultiLineMsg_Struct* mlm = (ServerMultiLineMsg_Struct*) pack->pBuffer; + client_list.SendPacket(mlm->to, pack); + break; + } + case ServerOP_SetZone: { + if(pack->size != sizeof(SetZone_Struct)) + break; + + SetZone_Struct* szs = (SetZone_Struct*) pack->pBuffer; + if (szs->zoneid != 0) { + if(database.GetZoneName(szs->zoneid)) + SetZone(szs->zoneid, szs->instanceid, szs->staticzone); + else + SetZone(0); + } + else + SetZone(0); + + break; + } + case ServerOP_SetConnectInfo: { + if (pack->size != sizeof(ServerConnectInfo)) + break; + ServerConnectInfo* sci = (ServerConnectInfo*) pack->pBuffer; + + if (!sci->port) { + clientport=zoneserver_list.GetAvailableZonePort(); + + ServerPacket p(ServerOP_SetConnectInfo, sizeof(ServerConnectInfo)); + memset(p.pBuffer,0,sizeof(ServerConnectInfo)); + ServerConnectInfo* sci = (ServerConnectInfo*) p.pBuffer; + sci->port = clientport; + SendPacket(&p); + zlog(WORLD__ZONE,"Auto zone port configuration. Telling zone to use port %d",clientport); + } else { + clientport=sci->port; + zlog(WORLD__ZONE,"Zone specified port %d, must be a previously allocated zone reconnecting.",clientport); + } + + } + case ServerOP_SetLaunchName: { + if(pack->size != sizeof(LaunchName_Struct)) + break; + const LaunchName_Struct* ln = (const LaunchName_Struct*)pack->pBuffer; + launcher_name = ln->launcher_name; + launched_name = ln->zone_name; + zlog(WORLD__ZONE, "Zone started with name %s by launcher %s", launched_name.c_str(), launcher_name.c_str()); + break; + } + case ServerOP_ShutdownAll: { + if(pack->size==0){ + zoneserver_list.SendPacket(pack); + zoneserver_list.Process(); + CatchSignal(2); + } + else{ + WorldShutDown_Struct* wsd=(WorldShutDown_Struct*)pack->pBuffer; + if(wsd->time==0 && wsd->interval==0 && zoneserver_list.shutdowntimer->Enabled()){ + zoneserver_list.shutdowntimer->Disable(); + zoneserver_list.reminder->Disable(); + } + else{ + zoneserver_list.shutdowntimer->SetTimer(wsd->time); + zoneserver_list.reminder->SetTimer(wsd->interval-1000); + zoneserver_list.reminder->SetAtTrigger(wsd->interval); + zoneserver_list.shutdowntimer->Start(); + zoneserver_list.reminder->Start(); + } + } + break; + } + case ServerOP_ZoneShutdown: { + ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer; + ZoneServer* zs = 0; + if (s->ZoneServerID != 0) + zs = zoneserver_list.FindByID(s->ZoneServerID); + else if (s->zoneid != 0) + zs = zoneserver_list.FindByName(database.GetZoneName(s->zoneid)); + else + zoneserver_list.SendEmoteMessage(s->adminname, 0, 0, 0, "Error: SOP_ZoneShutdown: neither ID nor name specified"); + + if (zs == 0) + zoneserver_list.SendEmoteMessage(s->adminname, 0, 0, 0, "Error: SOP_ZoneShutdown: zoneserver not found"); + else + zs->SendPacket(pack); + break; + } + case ServerOP_ZoneBootup: { + ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer; + zoneserver_list.SOPZoneBootup(s->adminname, s->ZoneServerID, database.GetZoneName(s->zoneid), s->makestatic); + break; + } + case ServerOP_ZoneStatus: { + if (pack->size >= 1) + zoneserver_list.SendZoneStatus((char *) &pack->pBuffer[1], (uint8) pack->pBuffer[0], this); + break; + + } + case ServerOP_AcceptWorldEntrance: { + if(pack->size != sizeof(WorldToZone_Struct)) + break; + + WorldToZone_Struct* wtz = (WorldToZone_Struct*) pack->pBuffer; + Client* client = 0; + client = client_list.FindByAccountID(wtz->account_id); + if(client != 0) + 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. + // + + + if(pack->size != sizeof(ZoneToZone_Struct)) + break; + ZoneToZone_Struct* ztz = (ZoneToZone_Struct*) pack->pBuffer; + ClientListEntry* client = NULL; + if(WorldConfig::get()->UpdateStats) + client = client_list.FindCharacter(ztz->name); + + 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 + { + 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) + ) + { + ztz->response = 0; + SendPacket(pack); + break; + } + + ZoneServer *ingress_server = NULL; + 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); + + } + + if(ingress_server) // found a zone already running + { + _log(WORLD__ZONE,"Found a zone already booted for %s\n", ztz->name); + ztz->response = 1; + } + else // need to boot one + { + 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); + // bootup successful, ready to rock + ztz->response = 1; + ingress_server = zoneserver_list.FindByID(server_id); + } + 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; + } + } + 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 + } + } + else // this is response from the ingress server, route it back to the egress server + { + zlog(WORLD__ZONE,"Processing ZTZ for ingress to zone for client %s\n", ztz->name); + ZoneServer *egress_server = NULL; + if(ztz->current_instance_id > 0) + { + egress_server = zoneserver_list.FindByInstanceID(ztz->current_instance_id); + } + else + { + egress_server = zoneserver_list.FindByZoneID(ztz->current_zone_id); + } + + if(egress_server) + { + egress_server->SendPacket(pack); + } + } + + break; + } + case ServerOP_ClientList: { + if (pack->size != sizeof(ServerClientList_Struct)) { + zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_ClientList. Got: %d, Expected: %d",pack->size,sizeof(ServerClientList_Struct)); + break; + } + client_list.ClientUpdate(this, (ServerClientList_Struct*) pack->pBuffer); + break; + } + case ServerOP_ClientListKA: { + ServerClientListKeepAlive_Struct* sclka = (ServerClientListKeepAlive_Struct*) pack->pBuffer; + if (pack->size < 4 || pack->size != 4 + (4 * sclka->numupdates)) { + zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_ClientListKA. Got: %d, Expected: %d",pack->size, (4 + (4 * sclka->numupdates))); + break; + } + client_list.CLEKeepAlive(sclka->numupdates, sclka->wid); + break; + } + case ServerOP_Who: { + ServerWhoAll_Struct* whoall = (ServerWhoAll_Struct*) pack->pBuffer; + Who_All_Struct* whom = new Who_All_Struct; + memset(whom,0,sizeof(Who_All_Struct)); + whom->gmlookup = whoall->gmlookup; + whom->lvllow = whoall->lvllow; + whom->lvlhigh = whoall->lvlhigh; + whom->wclass = whoall->wclass; + whom->wrace = whoall->wrace; + strcpy(whom->whom,whoall->whom); + client_list.SendWhoAll(whoall->fromid,whoall->from, whoall->admin, whom, this); + delete whom; + break; + } + 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: + { + ServerRequestClientVersionSummary_Struct *srcvss = (ServerRequestClientVersionSummary_Struct*) pack->pBuffer; + client_list.SendClientVersionSummary(srcvss->Name); + break; + } + case ServerOP_ReloadRules: + { + zoneserver_list.SendPacket(pack); + rules->LoadRules(&database, "default"); + break; + } + case ServerOP_ReloadRulesWorld: + { + rules->LoadRules(&database, "default"); + break; + } + case ServerOP_CameraShake: + { + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_FriendsWho: { + ServerFriendsWho_Struct* FriendsWho = (ServerFriendsWho_Struct*) pack->pBuffer; + client_list.SendFriendsWho(FriendsWho, this); + break; + } + case ServerOP_LFGMatches: { + ServerLFGMatchesRequest_Struct* smrs = (ServerLFGMatchesRequest_Struct*) pack->pBuffer; + client_list.SendLFGMatches(smrs); + break; + } + case ServerOP_LFPMatches: { + ServerLFPMatchesRequest_Struct* smrs = (ServerLFPMatchesRequest_Struct*) pack->pBuffer; + LFPGroupList.SendLFPMatches(smrs); + break; + } + case ServerOP_LFPUpdate: { + ServerLFPUpdate_Struct* sus = (ServerLFPUpdate_Struct*) pack->pBuffer; + if(sus->Action) + LFPGroupList.UpdateGroup(sus); + else + LFPGroupList.RemoveGroup(sus); + break; + } + case ServerOP_ZonePlayer: { + //ServerZonePlayer_Struct* szp = (ServerZonePlayer_Struct*) pack->pBuffer; + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_KickPlayer: { + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_KillPlayer: { + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_GuildRankUpdate: + { + zoneserver_list.SendPacket(pack); + break; + } + //these opcodes get processed by the guild manager. + case ServerOP_RefreshGuild: + case ServerOP_DeleteGuild: + case ServerOP_GuildCharRefresh: + case ServerOP_GuildMemberUpdate: { + guild_mgr.ProcessZonePacket(pack); + break; + } + + case ServerOP_FlagUpdate: { + ClientListEntry* cle = client_list.FindCLEByAccountID(*((uint32*) pack->pBuffer)); + if (cle) + cle->SetAdmin(*((int16*) &pack->pBuffer[4])); + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_GMGoto: { + if (pack->size != sizeof(ServerGMGoto_Struct)) { + zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_GMGoto. Got: %d, Expected: %d",pack->size,sizeof(ServerGMGoto_Struct)); + break; + } + ServerGMGoto_Struct* gmg = (ServerGMGoto_Struct*) pack->pBuffer; + ClientListEntry* cle = client_list.FindCharacter(gmg->gotoname); + if (cle != 0) { + if (cle->Server() == 0) + this->SendEmoteMessage(gmg->myname, 0, 0, 13, "Error: Cannot identify %s's zoneserver.", gmg->gotoname); + else if (cle->Anon() == 1 && cle->Admin() > gmg->admin) // no snooping for anon GMs + this->SendEmoteMessage(gmg->myname, 0, 0, 13, "Error: %s not found", gmg->gotoname); + else + cle->Server()->SendPacket(pack); + } + else { + this->SendEmoteMessage(gmg->myname, 0, 0, 13, "Error: %s not found", gmg->gotoname); + } + break; + } + case ServerOP_Lock: { + if (pack->size != sizeof(ServerLock_Struct)) { + zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_Lock. Got: %d, Expected: %d",pack->size,sizeof(ServerLock_Struct)); + break; + } + ServerLock_Struct* slock = (ServerLock_Struct*) pack->pBuffer; + if (slock->mode >= 1) + WorldConfig::LockWorld(); + else + WorldConfig::UnlockWorld(); + if (loginserverlist.Connected()) { + loginserverlist.SendStatus(); + if (slock->mode >= 1) + this->SendEmoteMessage(slock->myname, 0, 0, 13, "World locked"); + else + this->SendEmoteMessage(slock->myname, 0, 0, 13, "World unlocked"); + } + else { + if (slock->mode >= 1) + this->SendEmoteMessage(slock->myname, 0, 0, 13, "World locked, but login server not connected."); + else + this->SendEmoteMessage(slock->myname, 0, 0, 13, "World unlocked, but login server not conencted."); + } + break; + } + case ServerOP_Motd: { + if (pack->size != sizeof(ServerMotd_Struct)) { + zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_Motd. Got: %d, Expected: %d",pack->size,sizeof(ServerMotd_Struct)); + break; + } + ServerMotd_Struct* smotd = (ServerMotd_Struct*) pack->pBuffer; + database.SetVariable("MOTD",smotd->motd); + //this->SendEmoteMessage(smotd->myname, 0, 0, 13, "Updated Motd."); + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_Uptime: { + if (pack->size != sizeof(ServerUptime_Struct)) { + zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_Uptime. Got: %d, Expected: %d",pack->size,sizeof(ServerUptime_Struct)); + break; + } + ServerUptime_Struct* sus = (ServerUptime_Struct*) pack->pBuffer; + if (sus->zoneserverid == 0) { + ZSList::ShowUpTime(this, sus->adminname); + } + else { + ZoneServer* zs = zoneserver_list.FindByID(sus->zoneserverid); + if (zs) + zs->SendPacket(pack); + } + break; + } + case ServerOP_Petition: { + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_GetWorldTime: { + zlog(WORLD__ZONE,"Broadcasting a world time update"); + ServerPacket* pack = new ServerPacket; + + pack->opcode = ServerOP_SyncWorldTime; + pack->size = sizeof(eqTimeOfDay); + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer, 0, pack->size); + eqTimeOfDay* tod = (eqTimeOfDay*) pack->pBuffer; + tod->start_eqtime=zoneserver_list.worldclock.getStartEQTime(); + tod->start_realtime=zoneserver_list.worldclock.getStartRealTime(); + SendPacket(pack); + delete pack; + break; + } + case ServerOP_SetWorldTime: { + zlog(WORLD__ZONE,"Received SetWorldTime"); + eqTimeOfDay* newtime = (eqTimeOfDay*) pack->pBuffer; + zoneserver_list.worldclock.setEQTimeOfDay(newtime->start_eqtime, newtime->start_realtime); + zlog(WORLD__ZONE,"New time = %d-%d-%d %d:%d (%d)\n", newtime->start_eqtime.year, newtime->start_eqtime.month, (int)newtime->start_eqtime.day, (int)newtime->start_eqtime.hour, (int)newtime->start_eqtime.minute, (int)newtime->start_realtime); + zoneserver_list.worldclock.saveFile(WorldConfig::get()->EQTimeFile.c_str()); + zoneserver_list.SendTimeSync(); + break; + } + case ServerOP_IPLookup: { + if (pack->size < sizeof(ServerGenericWorldQuery_Struct)) { + zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_IPLookup. Got: %d, Expected (at least): %d",pack->size,sizeof(ServerGenericWorldQuery_Struct)); + break; + } + ServerGenericWorldQuery_Struct* sgwq = (ServerGenericWorldQuery_Struct*) pack->pBuffer; + if (pack->size == sizeof(ServerGenericWorldQuery_Struct)) + client_list.SendCLEList(sgwq->admin, sgwq->from, this); + else + client_list.SendCLEList(sgwq->admin, sgwq->from, this, sgwq->query); + break; + } + case ServerOP_LockZone: { + if (pack->size < sizeof(ServerLockZone_Struct)) { + zlog(WORLD__ZONE_ERR,"Wrong size on ServerOP_LockZone. Got: %d, Expected: %d",pack->size,sizeof(ServerLockZone_Struct)); + break; + } + ServerLockZone_Struct* s = (ServerLockZone_Struct*) pack->pBuffer; + switch (s->op) { + case 0: + zoneserver_list.ListLockedZones(s->adminname, this); + break; + case 1: + if (zoneserver_list.SetLockedZone(s->zoneID, true)) + zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone locked: %s", database.GetZoneName(s->zoneID)); + else + this->SendEmoteMessageRaw(s->adminname, 0, 0, 0, "Failed to change lock"); + break; + case 2: + if (zoneserver_list.SetLockedZone(s->zoneID, false)) + zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone unlocked: %s", database.GetZoneName(s->zoneID)); + else + this->SendEmoteMessageRaw(s->adminname, 0, 0, 0, "Failed to change lock"); + break; + } + break; + } + case ServerOP_ItemStatus: { + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_OOCMute: { + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_Revoke: { + RevokeStruct* rev = (RevokeStruct*)pack->pBuffer; + ClientListEntry* cle = client_list.FindCharacter(rev->name); + if (cle != 0 && cle->Server() != 0) + { + cle->Server()->SendPacket(pack); + } + break; + } + case ServerOP_SpawnPlayerCorpse: { + SpawnPlayerCorpse_Struct* s = (SpawnPlayerCorpse_Struct*)pack->pBuffer; + ZoneServer* zs = zoneserver_list.FindByZoneID(s->zone_id); + if(zs) { + if (zs->SendPacket(pack)) { + zlog(WORLD__ZONE,"Sent request to spawn player corpse id %i in zone %u.",s->player_corpse_id, s->zone_id); + } + else { + zlog(WORLD__ZONE_ERR,"Could not send request to spawn player corpse id %i in zone %u.",s->player_corpse_id, s->zone_id); + } + } + break; + } + case ServerOP_Consent: { + // Message string id's likely to be used here are: + // CONSENT_YOURSELF = 399 + // CONSENT_INVALID_NAME = 397 + // TARGET_NOT_FOUND = 101 + ZoneServer* zs; + ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; + ClientListEntry* cle = client_list.FindCharacter(s->grantname); + if(cle) { + if(cle->instance() != 0) + { + zs = zoneserver_list.FindByInstanceID(cle->instance()); + if(zs) { + if(zs->SendPacket(pack)) { + zlog(WORLD__ZONE, "Sent consent packet from player %s to player %s in zone %u.", s->ownername, s->grantname, cle->instance()); + } + else { + zlog(WORLD__ZONE_ERR, "Unable to locate zone record for instance id %u in zoneserver list for ServerOP_Consent operation.", s->instance_id); + } + } + else + { + delete pack; + pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); + ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; + strcpy(scs->grantname, s->grantname); + strcpy(scs->ownername, s->ownername); + scs->permission = s->permission; + scs->zone_id = s->zone_id; + scs->instance_id = s->instance_id; + scs->message_string_id = 101; + zs = zoneserver_list.FindByInstanceID(s->instance_id); + if(zs) { + if(!zs->SendPacket(pack)) + zlog(WORLD__ZONE_ERR, "Unable to send consent response back to player %s in instance %u.", s->ownername, zs->GetInstanceID()); + } + else { + zlog(WORLD__ZONE_ERR, "Unable to locate zone record for instance id %u in zoneserver list for ServerOP_Consent_Response operation.", s->instance_id); + } + } + } + else + { + zs = zoneserver_list.FindByZoneID(cle->zone()); + if(zs) { + if(zs->SendPacket(pack)) { + zlog(WORLD__ZONE, "Sent consent packet from player %s to player %s in zone %u.", s->ownername, s->grantname, cle->zone()); + } + else { + zlog(WORLD__ZONE_ERR, "Unable to locate zone record for zone id %u in zoneserver list for ServerOP_Consent operation.", s->zone_id); + } + } + else { + // send target not found back to requester + delete pack; + pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); + ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; + strcpy(scs->grantname, s->grantname); + strcpy(scs->ownername, s->ownername); + scs->permission = s->permission; + scs->zone_id = s->zone_id; + scs->message_string_id = 101; + zs = zoneserver_list.FindByZoneID(s->zone_id); + if(zs) { + if(!zs->SendPacket(pack)) + zlog(WORLD__ZONE_ERR, "Unable to send consent response back to player %s in zone %s.", s->ownername, zs->GetZoneName()); + } + else { + zlog(WORLD__ZONE_ERR, "Unable to locate zone record for zone id %u in zoneserver list for ServerOP_Consent_Response operation.", s->zone_id); + } + } + } + } + else { + // send target not found back to requester + delete pack; + pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); + ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; + strcpy(scs->grantname, s->grantname); + strcpy(scs->ownername, s->ownername); + scs->permission = s->permission; + scs->zone_id = s->zone_id; + scs->message_string_id = 397; + zs = zoneserver_list.FindByZoneID(s->zone_id); + if(zs) { + if(!zs->SendPacket(pack)) + zlog(WORLD__ZONE_ERR, "Unable to send consent response back to player %s in zone %s.", s->ownername, zs->GetZoneName()); + } + else { + zlog(WORLD__ZONE_ERR, "Unable to locate zone record for zone id %u in zoneserver list for ServerOP_Consent_Response operation.", s->zone_id); + } + } + break; + } + case ServerOP_Consent_Response: { + // Message string id's likely to be used here are: + // CONSENT_YOURSELF = 399 + // CONSENT_INVALID_NAME = 397 + // TARGET_NOT_FOUND = 101 + ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; + if(s->instance_id != 0) + { + ZoneServer* zs = zoneserver_list.FindByInstanceID(s->instance_id); + if(zs) { + if(!zs->SendPacket(pack)) + zlog(WORLD__ZONE_ERR, "Unable to send consent response back to player %s in instance %u.", s->ownername, zs->GetInstanceID()); + } + else { + zlog(WORLD__ZONE_ERR, "Unable to locate zone record for instance id %u in zoneserver list for ServerOP_Consent_Response operation.", s->instance_id); + } + } + else + { + ZoneServer* zs = zoneserver_list.FindByZoneID(s->zone_id); + if(zs) { + if(!zs->SendPacket(pack)) + zlog(WORLD__ZONE_ERR, "Unable to send consent response back to player %s in zone %s.", s->ownername, zs->GetZoneName()); + } + else { + zlog(WORLD__ZONE_ERR, "Unable to locate zone record for zone id %u in zoneserver list for ServerOP_Consent_Response operation.", s->zone_id); + } + } + break; + } + + case ServerOP_InstanceUpdateTime : + { + ServerInstanceUpdateTime_Struct *iut = (ServerInstanceUpdateTime_Struct*)pack->pBuffer; + ZoneServer *zm = zoneserver_list.FindByInstanceID(iut->instance_id); + if(zm) + { + zm->SendPacket(pack); + } + break; + } + case ServerOP_QGlobalUpdate: + { + if(pack->size != sizeof(ServerQGlobalUpdate_Struct)) + { + break; + } + + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_QGlobalDelete: + { + if(pack->size != sizeof(ServerQGlobalDelete_Struct)) + { + break; + } + + zoneserver_list.SendPacket(pack); + break; + } + + case ServerOP_AdventureRequest: + { + adventure_manager.CalculateAdventureRequestReply((const char*)pack->pBuffer); + break; + } + + case ServerOP_AdventureRequestCreate: + { + adventure_manager.TryAdventureCreate((const char*)pack->pBuffer); + break; + } + + case ServerOP_AdventureDataRequest: + { + AdventureFinishEvent fe; + while(adventure_manager.PopFinishedEvent((const char*)pack->pBuffer, fe)) + { + adventure_manager.SendAdventureFinish(fe); + } + adventure_manager.GetAdventureData((const char*)pack->pBuffer); + break; + } + + case ServerOP_AdventureClickDoor: + { + ServerPlayerClickedAdventureDoor_Struct *pcad = (ServerPlayerClickedAdventureDoor_Struct*)pack->pBuffer; + adventure_manager.PlayerClickedDoor(pcad->player, pcad->zone_id, pcad->id); + break; + } + + case ServerOP_AdventureLeave: + { + adventure_manager.LeaveAdventure((const char*)pack->pBuffer); + break; + } + + case ServerOP_AdventureCountUpdate: + { + ServerAdventureCount_Struct *sc = (ServerAdventureCount_Struct*)pack->pBuffer; + adventure_manager.IncrementCount(sc->instance_id); + break; + } + + case ServerOP_AdventureAssaCountUpdate: + { + adventure_manager.IncrementAssassinationCount(*((uint16*)pack->pBuffer)); + break; + } + + case ServerOP_AdventureZoneData: + { + adventure_manager.GetZoneData(*((uint16*)pack->pBuffer)); + break; + } + + case ServerOP_AdventureLeaderboard: + { + ServerLeaderboardRequest_Struct *lr = (ServerLeaderboardRequest_Struct*)pack->pBuffer; + adventure_manager.DoLeaderboardRequest(lr->player, lr->type); + break; + } + + case ServerOP_LSAccountUpdate: + { + zlog(WORLD__ZONE, "Received ServerOP_LSAccountUpdate packet from zone"); + loginserverlist.SendAccountUpdate(pack); + break; + } + + case ServerOP_UCSMailMessage: + { + UCSLink.SendPacket(pack); + break; + } + + case ServerOP_QueryServGeneric: + case ServerOP_Speech: + case ServerOP_QSPlayerLogTrades: + { + QSLink.SendPacket(pack); + break; + } + case ServerOP_QSPlayerLogHandins: + { + QSLink.SendPacket(pack); + break; + } + case ServerOP_QSPlayerLogNPCKills: + { + QSLink.SendPacket(pack); + break; + } + case ServerOP_QSPlayerLogDeletes: + { + QSLink.SendPacket(pack); + break; + } + case ServerOP_QSPlayerLogMoves: + { + QSLink.SendPacket(pack); + break; + } + case ServerOP_QSMerchantLogTransactions: + { + QSLink.SendPacket(pack); + break; + } + case ServerOP_CZSignalClientByName: + case ServerOP_CZMessagePlayer: + case ServerOP_CZSignalClient: + { + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_DepopAllPlayersCorpses: + case ServerOP_DepopPlayerCorpse: + case ServerOP_ReloadTitles: + case ServerOP_SpawnStatusChange: + case ServerOP_ReloadTasks: + case ServerOP_ReloadWorld: + case ServerOP_UpdateSpawn: + { + zoneserver_list.SendPacket(pack); + break; + } + default: + { + zlog(WORLD__ZONE_ERR,"Unknown ServerOPcode from zone 0x%04x, size %d",pack->opcode,pack->size); + DumpPacket(pack->pBuffer, pack->size); + break; + } + } + + delete pack; + } + return true; +} + +void ZoneServer::SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...) { + if (!message) + return; + va_list argptr; + char buffer[1024]; + + va_start(argptr, message); + vsnprintf(buffer, sizeof(buffer), message, argptr); + va_end(argptr); + SendEmoteMessageRaw(to, to_guilddbid, to_minstatus, type, buffer); +} + +void ZoneServer::SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message) { + if (!message) + return; + ServerPacket* pack = new ServerPacket; + + pack->opcode = ServerOP_EmoteMessage; + pack->size = sizeof(ServerEmoteMessage_Struct)+strlen(message)+1; + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer, 0, pack->size); + ServerEmoteMessage_Struct* sem = (ServerEmoteMessage_Struct*) pack->pBuffer; + + if (to != 0) { + strcpy((char *) sem->to, to); + } + else { + sem->to[0] = 0; + } + + sem->guilddbid = to_guilddbid; + sem->minstatus = to_minstatus; + sem->type = type; + strcpy(&sem->message[0], message); + + pack->Deflate(); + SendPacket(pack); + delete pack; +} + +void ZoneServer::SendGroupIDs() { + ServerPacket* pack = new ServerPacket(ServerOP_GroupIDReply, sizeof(ServerGroupIDReply_Struct)); + ServerGroupIDReply_Struct* sgi = (ServerGroupIDReply_Struct*)pack->pBuffer; + zoneserver_list.NextGroupIDs(sgi->start, sgi->end); + SendPacket(pack); + delete pack; +} + +void ZoneServer::ChangeWID(uint32 iCharID, uint32 iWID) { + ServerPacket* pack = new ServerPacket(ServerOP_ChangeWID, sizeof(ServerChangeWID_Struct)); + ServerChangeWID_Struct* scw = (ServerChangeWID_Struct*) pack->pBuffer; + scw->charid = iCharID; + scw->newwid = iWID; + zoneserver_list.SendPacket(pack); + delete pack; +} + + +void ZoneServer::TriggerBootup(uint32 iZoneID, uint32 iInstanceID, const char* adminname, bool iMakeStatic) { + BootingUp = true; + zoneID = iZoneID; + instanceID = iInstanceID; + + ServerPacket* pack = new ServerPacket(ServerOP_ZoneBootup, sizeof(ServerZoneStateChange_struct)); + ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer; + s->ZoneServerID = ID; + if (adminname != 0) + strcpy(s->adminname, adminname); + + if (iZoneID == 0) + s->zoneid = this->GetZoneID(); + else + s->zoneid = iZoneID; + + s->instanceid = iInstanceID; + s->makestatic = iMakeStatic; + SendPacket(pack); + delete pack; + LSBootUpdate(iZoneID, iInstanceID); +} + +void ZoneServer::IncommingClient(Client* client) { + BootingUp = true; + ServerPacket* pack = new ServerPacket(ServerOP_ZoneIncClient, sizeof(ServerZoneIncommingClient_Struct)); + ServerZoneIncommingClient_Struct* s = (ServerZoneIncommingClient_Struct*) pack->pBuffer; + s->zoneid = GetZoneID(); + s->instanceid = GetInstanceID(); + s->wid = client->GetWID(); + s->ip = client->GetIP(); + s->accid = client->GetAccountID(); + s->admin = client->GetAdmin(); + s->charid = client->GetCharID(); + if (client->GetCLE()) + s->tellsoff = client->GetCLE()->TellsOff(); + strn0cpy(s->charname, client->GetCharName(), sizeof(s->charname)); + strn0cpy(s->lskey, client->GetLSKey(), sizeof(s->lskey)); + SendPacket(pack); + delete pack; +} + + diff --git a/world/zoneserver.h b/world/zoneserver.h new file mode 100644 index 000000000..95da895ac --- /dev/null +++ b/world/zoneserver.h @@ -0,0 +1,93 @@ +/* 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 ZONESERVER_H +#define ZONESERVER_H + +#include "WorldTCPConnection.h" +#include "../common/EmuTCPConnection.h" +#include +#include + +class Client; +class ServerPacket; + + +class ZoneServer : public WorldTCPConnection { +public: + ZoneServer(EmuTCPConnection* itcpc); + ~ZoneServer(); + virtual inline bool IsZoneServer() { return true; } + + bool Process(); + bool SendPacket(ServerPacket* pack) { return tcpc->SendPacket(pack); } + void SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...); + void SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message); + bool SetZone(uint32 iZoneID, uint32 iInstanceID = 0, bool iStaticZone = false); + void TriggerBootup(uint32 iZoneID = 0, uint32 iInstanceID = 0, const char* iAdminName = 0, bool iMakeStatic = false); + void Disconnect() { tcpc->Disconnect(); } + void IncommingClient(Client* client); + void LSBootUpdate(uint32 zoneid, uint32 iInstanceID = 0, bool startup = false); + void LSSleepUpdate(uint32 zoneid); + void LSShutDownUpdate(uint32 zoneid); + uint32 GetPrevZoneID() { return oldZoneID; } + void ChangeWID(uint32 iCharID, uint32 iWID); + void SendGroupIDs(); + + inline const char* GetZoneName() const { return zone_name; } + inline const char* GetZoneLongName() const { return long_name; } + const char* GetCompileTime() const{ return compiled; } + void SetCompile(char* in_compile){ strcpy(compiled,in_compile); } + inline uint32 GetZoneID() const { return zoneID; } + inline uint32 GetIP() const { return tcpc->GetrIP(); } + inline uint16 GetPort() const { return tcpc->GetrPort(); } + inline const char* GetCAddress() const { return clientaddress; } + inline uint16 GetCPort() const { return clientport; } + inline uint32 GetID() const { return ID; } + inline bool IsBootingUp() const { return BootingUp; } + inline bool IsStaticZone() const{ return staticzone; } + inline uint32 NumPlayers() const { return pNumPlayers; } + inline void AddPlayer() { pNumPlayers++; } + inline void RemovePlayer() { pNumPlayers--; } + inline const char * GetLaunchName() const { return(launcher_name.c_str()); } + inline const char * GetLaunchedName() const { return(launched_name.c_str()); } + + inline uint32 GetInstanceID() { return instanceID; } + inline void SetInstanceID(uint32 i) { instanceID = i; } +private: + EmuTCPConnection* const tcpc; + + uint32 ID; + char clientaddress[250]; + uint16 clientport; + bool BootingUp; + bool staticzone; + bool authenticated; + uint32 pNumPlayers; + char compiled[25]; + char zone_name[32]; + char long_name[256]; + uint32 zoneID; + uint32 oldZoneID; + Timer ls_zboot; + uint32 instanceID; //instance ids contain a zone id, and a zone version + std::string launcher_name; //the launcher which started us + std::string launched_name; //the name of the zone we launched. +}; + + +#endif diff --git a/zone/AA.cpp b/zone/AA.cpp new file mode 100644 index 000000000..c3c1b9217 --- /dev/null +++ b/zone/AA.cpp @@ -0,0 +1,1968 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2004 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 +*/ + +// Test 1 + +#include "../common/debug.h" +#include "AA.h" +#include "mob.h" +#include "client.h" +#include "groups.h" +#include "raids.h" +#include "spdat.h" +#include "object.h" +#include "doors.h" +#include "beacon.h" +#include "PlayerCorpse.h" +#include "titles.h" +#include "../common/races.h" +#include "../common/classes.h" +#include "../common/eq_packet_structs.h" +#include "../common/packet_dump.h" +#include "../common/MiscFunctions.h" +#include "../common/logsys.h" +#include "zonedb.h" +#include "StringIDs.h" +#if !defined(NEW_LoadSPDat) && !defined(DB_LoadSPDat) + extern SPDat_Spell_Struct spells[SPDAT_RECORDS]; +#endif + +//static data arrays, really not big enough to warrant shared mem. +AA_DBAction AA_Actions[aaHighestID][MAX_AA_ACTION_RANKS]; //[aaid][rank] +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); + + if(!aa2) + { + for(int i = 1;i < MAX_AA_ACTION_RANKS; ++i) + { + int a = activate - i; + + if(a <= 0) + break; + + aa2 = zone->FindAA(a); + + if(aa2 != NULL) + break; + } + } + + if(aa2) + return aa2->spell_type; + + return 0; +} + +int Client::CalcAAReuseTimer(const AA_DBAction *caa) { + + if(!caa) + return 0; + + int ReuseTime = caa->reuse_time; + + if(ReuseTime > 0) + { + int ReductionPercentage; + + if(caa->redux_aa > 0 && caa->redux_aa < aaHighestID) + { + ReductionPercentage = GetAA(caa->redux_aa) * caa->redux_rate; + + if(caa->redux_aa2 > 0 && caa->redux_aa2 < aaHighestID) + ReductionPercentage += (GetAA(caa->redux_aa2) * caa->redux_rate2); + + ReuseTime = caa->reuse_time * (100 - ReductionPercentage) / 100; + } + + } + return ReuseTime; +} + +void Client::ActivateAA(aaID activate){ + if(activate < 0 || activate >= aaHighestID) + return; + if(IsStunned() || IsFeared() || IsMezzed() || IsSilenced() || IsPet() || IsSitting() || GetFeigned()) + return; + + int AATimerID = GetAATimerID(activate); + + SendAA_Struct* aa2 = NULL; + aaID aaid = activate; + uint8 activate_val = GetAA(activate); + //this wasn't taking into acct multi tiered act talents before... + if(activate_val == 0){ + aa2 = zone->FindAA(activate); + if(!aa2){ + int i; + int a; + for(i=1;iFindAA(a); + if(aa2 != NULL) + break; + } + } + if(aa2){ + aaid = (aaID) aa2->id; + activate_val = GetAA(aa2->id); + } + } + + if (activate_val == 0){ + return; + } + + if(aa2) + { + if(aa2->account_time_required) + { + if((Timer::GetTimeSeconds() + account_creation) < aa2->account_time_required) + { + return; + } + } + } + + if(!p_timers.Expired(&database, AATimerID + pTimerAAStart)) + { + uint32 aaremain = p_timers.GetRemainingTime(AATimerID + pTimerAAStart); + uint32 aaremain_hr = aaremain / (60 * 60); + uint32 aaremain_min = (aaremain / 60) % 60; + uint32 aaremain_sec = aaremain % 60; + + if(aa2) { + if (aaremain_hr >= 1) //1 hour or more + Message(13, "You can use the ability %s again in %u hour(s) %u minute(s) %u seconds", + aa2->name, aaremain_hr, aaremain_min, aaremain_sec); + else //less than an hour + Message(13, "You can use the ability %s again in %u minute(s) %u seconds", + aa2->name, aaremain_min, aaremain_sec); + } else { + if (aaremain_hr >= 1) //1 hour or more + Message(13, "You can use this ability again in %u hour(s) %u minute(s) %u seconds", + aaremain_hr, aaremain_min, aaremain_sec); + else //less than an hour + Message(13, "You can use this ability again in %u minute(s) %u seconds", + aaremain_min, aaremain_sec); + } + return; + } + + if(activate_val > MAX_AA_ACTION_RANKS) + activate_val = MAX_AA_ACTION_RANKS; + activate_val--; //to get array index. + + //get our current node, now that the indices are well bounded + const AA_DBAction *caa = &AA_Actions[aaid][activate_val]; + + if((aaid == aaImprovedHarmTouch || aaid == aaLeechTouch) && !p_timers.Expired(&database, pTimerHarmTouch)){ + Message(13,"Ability recovery time not yet met."); + return; + } + + //everything should be configured out now + + uint16 target_id = 0; + + //figure out our target + switch(caa->target) { + case aaTargetUser: + case aaTargetGroup: + target_id = GetID(); + break; + case aaTargetCurrent: + case aaTargetCurrentGroup: + if(GetTarget() == NULL) { + Message_StringID(MT_DefaultText, AA_NO_TARGET); //You must first select a target for this ability! + p_timers.Clear(&database, AATimerID + pTimerAAStart); + return; + } + target_id = GetTarget()->GetID(); + break; + case aaTargetPet: + if(GetPet() == NULL) { + Message(0, "A pet is required for this skill."); + return; + } + target_id = GetPetID(); + break; + } + + //handle non-spell action + if(caa->action != aaActionNone) { + if(caa->mana_cost > 0) { + if(GetMana() < caa->mana_cost) { + Message(0, "Not enough mana to use this skill."); + return; + } + SetMana(GetMana() - caa->mana_cost); + } + HandleAAAction(aaid); + if(caa->reuse_time > 0) + { + uint32 timer_base = CalcAAReuseTimer(caa); + if(activate == aaImprovedHarmTouch || activate == aaLeechTouch) + { + p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); + } + p_timers.Start(AATimerID + pTimerAAStart, timer_base); + time_t timestamp = time(NULL); + SendAATimer(AATimerID, static_cast(timestamp), static_cast(timestamp)); + } + } + + //cast the spell, if we have one + if(caa->spell_id > 0 && caa->spell_id < SPDAT_RECORDS) { + + if(caa->reuse_time > 0) + { + uint32 timer_base = CalcAAReuseTimer(caa); + if(activate == aaImprovedHarmTouch || activate == aaLeechTouch) + { + p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); + } + + if(!CastSpell(caa->spell_id, target_id, 10, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) + return; + } + else + { + if(!CastSpell(caa->spell_id, target_id)) + return; + } + } + // Check if AA is expendable + 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(); + SendAA(activate); + SendAATable(); + } +} + +void Client::HandleAAAction(aaID activate) { + if(activate < 0 || activate >= aaHighestID) + return; + + uint8 activate_val = GetAA(activate); + + if (activate_val == 0) + return; + + if(activate_val > MAX_AA_ACTION_RANKS) + activate_val = MAX_AA_ACTION_RANKS; + activate_val--; //to get array index. + + //get our current node, now that the indices are well bounded + const AA_DBAction *caa = &AA_Actions[activate][activate_val]; + + uint16 timer_id = 0; + uint16 timer_duration = caa->duration; + aaTargetType target = aaTargetUser; + + uint16 spell_id = SPELL_UNKNOWN; //gets cast at the end if not still unknown + + switch(caa->action) { + case aaActionAETaunt: + entity_list.AETaunt(this); + break; + + case aaActionMassBuff: + EnableAAEffect(aaEffectMassGroupBuff, 3600); + Message_StringID(MT_Disciplines, MGB_STRING); //The next group buff you cast will hit all targets in range. + break; + + case aaActionFlamingArrows: + //toggle it + if(CheckAAEffect(aaEffectFlamingArrows)) + EnableAAEffect(aaEffectFlamingArrows); + else + DisableAAEffect(aaEffectFlamingArrows); + break; + + case aaActionFrostArrows: + if(CheckAAEffect(aaEffectFrostArrows)) + EnableAAEffect(aaEffectFrostArrows); + else + DisableAAEffect(aaEffectFrostArrows); + break; + + case aaActionRampage: + EnableAAEffect(aaEffectRampage, 10); + break; + + case aaActionSharedHealth: + if(CheckAAEffect(aaEffectSharedHealth)) + EnableAAEffect(aaEffectSharedHealth); + else + DisableAAEffect(aaEffectSharedHealth); + break; + + case aaActionCelestialRegen: { + //special because spell_id depends on a different AA + switch (GetAA(aaCelestialRenewal)) { + case 1: + spell_id = 3250; + break; + case 2: + spell_id = 3251; + break; + default: + spell_id = 2740; + break; + } + target = aaTargetCurrent; + break; + } + + case aaActionDireCharm: { + //special because spell_id depends on class + switch (GetClass()) + { + case DRUID: + spell_id = 2760; //2644? + break; + case NECROMANCER: + spell_id = 2759; //2643? + break; + case ENCHANTER: + spell_id = 2761; //2642? + break; + } + target = aaTargetCurrent; + break; + } + + case aaActionImprovedFamiliar: { + //Spell IDs might be wrong... + if (GetAA(aaAllegiantFamiliar)) + spell_id = 3264; //1994? + else + spell_id = 2758; //2155? + break; + } + + case aaActionActOfValor: + if(GetTarget() != NULL) { + int curhp = GetTarget()->GetHP(); + target = aaTargetCurrent; + GetTarget()->HealDamage(curhp, this); + Death(this,0,SPELL_UNKNOWN,HAND_TO_HAND); + } + break; + + case aaActionSuspendedMinion: + if (GetPet()) { + target = aaTargetPet; + switch (GetAA(aaSuspendedMinion)) { + case 1: + spell_id = 3248; + break; + case 2: + spell_id = 3249; + break; + } + //do we really need to cast a spell? + + Message(0,"You call your pet to your side."); + GetPet()->WipeHateList(); + GetPet()->GMMove(GetX(),GetY(),GetZ()); + if (activate_val > 1) + entity_list.ClearFeignAggro(GetPet()); + } else { + Message(0,"You have no pet to call."); + } + break; + + // seveian 2008-09-23 + case aaActionProjectIllusion: + EnableAAEffect(aaEffectProjectIllusion, 3600); + Message(10, "The power of your next illusion spell will flow to your grouped target in your place."); + break; + + + case aaActionEscape: + Escape(); + break; + + case aaBeastialAlignment: + switch(GetBaseRace()) { + case BARBARIAN: + spell_id = AA_Choose3(activate_val, 4521, 4522, 4523); + break; + case TROLL: + spell_id = AA_Choose3(activate_val, 4524, 4525, 4526); + break; + case OGRE: + spell_id = AA_Choose3(activate_val, 4527, 4527, 4529); + break; + case IKSAR: + spell_id = AA_Choose3(activate_val, 4530, 4531, 4532); + break; + case VAHSHIR: + spell_id = AA_Choose3(activate_val, 4533, 4534, 4535); + break; + } + + case aaActionLeechTouch: + target = aaTargetCurrent; + spell_id = SPELL_HARM_TOUCH2; + EnableAAEffect(aaEffectLeechTouch, 1000); + break; + + case aaActionFadingMemories: + entity_list.RemoveFromTargets(this, true); + SetInvisible(1); + break; + + default: + LogFile->write(EQEMuLog::Error, "Unknown AA nonspell action type %d", caa->action); + return; + } + + + uint16 target_id = 0; + //figure out our target + switch(target) { + case aaTargetUser: + case aaTargetGroup: + target_id = GetID(); + break; + case aaTargetCurrent: + case aaTargetCurrentGroup: + if(GetTarget() == NULL) { + Message_StringID(MT_DefaultText, AA_NO_TARGET); //You must first select a target for this ability! + p_timers.Clear(&database, timer_id + pTimerAAEffectStart); + return; + } + target_id = GetTarget()->GetID(); + break; + case aaTargetPet: + if(GetPet() == NULL) { + Message(0, "A pet is required for this skill."); + return; + } + target_id = GetPetID(); + break; + } + + //cast the spell, if we have one + if(spell_id > 0 && spell_id < SPDAT_RECORDS) { + //I dont know when we need to mem and when we do not, if ever... + //MemorizeSpell(8, spell_id, 3); + CastSpell(spell_id, target_id); + } + + //handle the duration timer if we have one. + if(timer_id > 0 && timer_duration > 0) { + p_timers.Start(pTimerAAEffectStart + timer_id, timer_duration); + } +} + + +//Originally written by Branks +//functionality rewritten by Father Nitwit +void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, uint32 duration_override) { + + //It might not be a bad idea to put these into the database, eventually.. + + //Dook- swarms and wards + + PetRecord record; + if(!database.GetPetEntry(spells[spell_id].teleport_zone, &record)) + { + LogFile->write(EQEMuLog::Error, "Unknown swarm pet spell id: %d, check pets table", spell_id); + Message(13, "Unable to find data for pet %s", spells[spell_id].teleport_zone); + return; + } + + AA_SwarmPet pet; + pet.count = 1; + pet.duration = 1; + + for(int x = 0; x < 12; x++) + { + if(spells[spell_id].effectid[x] == SE_TemporaryPets) + { + pet.count = spells[spell_id].base[x]; + pet.duration = spells[spell_id].max[x]; + } + } + + if(IsClient()) + pet.duration += (CastToClient()->GetFocusEffect(focusSwarmPetDuration, spell_id) / 1000); + + pet.npc_id = record.npc_type; + + NPCType *made_npc = NULL; + + const NPCType *npc_type = database.GetNPCType(pet.npc_id); + if(npc_type == NULL) { + //log write + LogFile->write(EQEMuLog::Error, "Unknown npc type for swarm pet spell id: %d", spell_id); + Message(0,"Unable to find pet!"); + return; + } + + if(name_override != NULL) { + //we have to make a custom NPC type for this name change + made_npc = new NPCType; + memcpy(made_npc, npc_type, sizeof(NPCType)); + strcpy(made_npc->name, name_override); + npc_type = made_npc; + } + + int summon_count = 0; + summon_count = pet.count; + + if(summon_count > MAX_SWARM_PETS) + summon_count = MAX_SWARM_PETS; + + 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) { + int pet_duration = pet.duration; + if(duration_override > 0) + pet_duration = duration_override; + + //this is a little messy, but the only way to do it right + //it would be possible to optimize out this copy for the last pet, but oh well + NPCType *npc_dup = NULL; + if(made_npc != NULL) { + npc_dup = new NPCType; + memcpy(npc_dup, made_npc, sizeof(NPCType)); + } + + NPC* npca = new NPC( + (npc_dup!=NULL)?npc_dup:npc_type, //make sure we give the NPC the correct data pointer + 0, + GetX()+swarm_pet_x[summon_count], GetY()+swarm_pet_y[summon_count], + GetZ(), GetHeading(), FlyMode3); + + if((spell_id == 6882) || (spell_id == 6884)) + npca->SetFollowID(GetID()); + + if(!npca->GetSwarmInfo()){ + AA_SwarmPetInfo* nSI = new AA_SwarmPetInfo; + npca->SetSwarmInfo(nSI); + npca->GetSwarmInfo()->duration = new Timer(pet_duration*1000); + } + else{ + npca->GetSwarmInfo()->duration->Start(pet_duration*1000); + } + + //removing this prevents the pet from attacking + npca->GetSwarmInfo()->owner_id = GetID(); + + //give the pets somebody to "love" + if(targ != NULL){ + npca->AddToHateList(targ, 1000, 1000); + npca->GetSwarmInfo()->target = targ->GetID(); + } + + //we allocated a new NPC type object, give the NPC ownership of that memory + if(npc_dup != NULL) + npca->GiveNPCTypeData(npc_dup); + + entity_list.AddNPC(npca, true, true); + summon_count--; + } + + //the target of these swarm pets will take offense to being cast on... + if(targ != NULL) + targ->AddToHateList(this, 1, 0); +} + +void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_override, uint32 duration_override, bool followme) { + + AA_SwarmPet pet; + pet.count = 1; + pet.duration = 1; + + pet.npc_id = typesid; + + NPCType *made_npc = NULL; + + const NPCType *npc_type = database.GetNPCType(typesid); + if(npc_type == NULL) { + //log write + LogFile->write(EQEMuLog::Error, "Unknown npc type for swarm pet type id: %d", typesid); + Message(0,"Unable to find pet!"); + return; + } + + if(name_override != NULL) { + //we have to make a custom NPC type for this name change + made_npc = new NPCType; + memcpy(made_npc, npc_type, sizeof(NPCType)); + strcpy(made_npc->name, name_override); + npc_type = made_npc; + } + + int summon_count = 0; + summon_count = pet.count; + + if(summon_count > MAX_SWARM_PETS) + summon_count = MAX_SWARM_PETS; + + 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) { + int pet_duration = pet.duration; + if(duration_override > 0) + pet_duration = duration_override; + + //this is a little messy, but the only way to do it right + //it would be possible to optimize out this copy for the last pet, but oh well + NPCType *npc_dup = NULL; + if(made_npc != NULL) { + npc_dup = new NPCType; + memcpy(npc_dup, made_npc, sizeof(NPCType)); + } + + NPC* npca = new NPC( + (npc_dup!=NULL)?npc_dup:npc_type, //make sure we give the NPC the correct data pointer + 0, + GetX()+swarm_pet_x[summon_count], GetY()+swarm_pet_y[summon_count], + GetZ(), GetHeading(), FlyMode3); + + if(!npca->GetSwarmInfo()){ + AA_SwarmPetInfo* nSI = new AA_SwarmPetInfo; + npca->SetSwarmInfo(nSI); + npca->GetSwarmInfo()->duration = new Timer(pet_duration*1000); + } + else{ + npca->GetSwarmInfo()->duration->Start(pet_duration*1000); + } + + //removing this prevents the pet from attacking + npca->GetSwarmInfo()->owner_id = GetID(); + + //give the pets somebody to "love" + if(targ != NULL){ + npca->AddToHateList(targ, 1000, 1000); + npca->GetSwarmInfo()->target = targ->GetID(); + } + + //we allocated a new NPC type object, give the NPC ownership of that memory + if(npc_dup != NULL) + npca->GiveNPCTypeData(npc_dup); + + entity_list.AddNPC(npca, true, true); + summon_count--; + } +} + +void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration) +{ + Corpse *CorpseToUse = NULL; + CorpseToUse = entity_list.GetClosestCorpse(this, NULL); + + if(!CorpseToUse) + return; + + //assuming we have pets in our table; we take the first pet as a base type. + const NPCType *base_type = database.GetNPCType(500); + NPCType *make_npc = new NPCType; + memcpy(make_npc, base_type, sizeof(NPCType)); + + //combat stats + make_npc->AC = ((GetLevel() * 7) + 550); + make_npc->ATK = GetLevel(); + make_npc->max_dmg = (GetLevel() * 4) + 2; + make_npc->min_dmg = 1; + + //base stats + make_npc->cur_hp = (GetLevel() * 55); + make_npc->max_hp = (GetLevel() * 55); + make_npc->STR = 85 + (GetLevel() * 3); + make_npc->STA = 85 + (GetLevel() * 3); + make_npc->DEX = 85 + (GetLevel() * 3); + make_npc->AGI = 85 + (GetLevel() * 3); + make_npc->INT = 85 + (GetLevel() * 3); + make_npc->WIS = 85 + (GetLevel() * 3); + make_npc->CHA = 85 + (GetLevel() * 3); + make_npc->MR = 25; + make_npc->FR = 25; + make_npc->CR = 25; + make_npc->DR = 25; + make_npc->PR = 25; + + //level class and gender + make_npc->level = GetLevel(); + make_npc->class_ = CorpseToUse->class_; + make_npc->race = CorpseToUse->race; + make_npc->gender = CorpseToUse->gender; + make_npc->loottable_id = 0; + //name + char NewName[64]; + sprintf(NewName, "%s`s Animated Corpse", GetCleanName()); + strcpy(make_npc->name, NewName); + + //appearance + make_npc->beard = CorpseToUse->beard; + make_npc->beardcolor = CorpseToUse->beardcolor; + make_npc->eyecolor1 = CorpseToUse->eyecolor1; + make_npc->eyecolor2 = CorpseToUse->eyecolor2; + make_npc->haircolor = CorpseToUse->haircolor; + make_npc->hairstyle = CorpseToUse->hairstyle; + make_npc->helmtexture = CorpseToUse->helmtexture; + make_npc->luclinface = CorpseToUse->luclinface; + make_npc->size = CorpseToUse->size; + make_npc->texture = CorpseToUse->texture; + + //cast stuff.. based off of PEQ's if you want to change + //it you'll have to mod this code, but most likely + //most people will be using PEQ style for the first + //part of their spell list; can't think of any smooth + //way to do this + //some basic combat mods here too since it's convienent + switch(CorpseToUse->class_) + { + case CLERIC: + make_npc->npc_spells_id = 1; + break; + case WIZARD: + make_npc->npc_spells_id = 2; + break; + case NECROMANCER: + make_npc->npc_spells_id = 3; + break; + case MAGICIAN: + make_npc->npc_spells_id = 4; + break; + case ENCHANTER: + make_npc->npc_spells_id = 5; + break; + case SHAMAN: + make_npc->npc_spells_id = 6; + break; + case DRUID: + make_npc->npc_spells_id = 7; + break; + case PALADIN: + make_npc->npc_attacks[0] = 'T'; + make_npc->cur_hp = make_npc->cur_hp * 150 / 100; + make_npc->max_hp = make_npc->max_hp * 150 / 100; + make_npc->npc_spells_id = 8; + break; + case SHADOWKNIGHT: + make_npc->npc_attacks[0] = 'T'; + make_npc->cur_hp = make_npc->cur_hp * 150 / 100; + make_npc->max_hp = make_npc->max_hp * 150 / 100; + make_npc->npc_spells_id = 9; + break; + case RANGER: + make_npc->npc_attacks[0] = 'Q'; + make_npc->cur_hp = make_npc->cur_hp * 135 / 100; + make_npc->max_hp = make_npc->max_hp * 135 / 100; + make_npc->npc_spells_id = 10; + break; + case BARD: + make_npc->npc_attacks[0] = 'T'; + make_npc->cur_hp = make_npc->cur_hp * 110 / 100; + make_npc->max_hp = make_npc->max_hp * 110 / 100; + make_npc->npc_spells_id = 11; + break; + case BEASTLORD: + make_npc->npc_attacks[0] = 'Q'; + make_npc->cur_hp = make_npc->cur_hp * 110 / 100; + make_npc->max_hp = make_npc->max_hp * 110 / 100; + make_npc->npc_spells_id = 12; + break; + case ROGUE: + make_npc->npc_attacks[0] = 'Q'; + make_npc->max_dmg = make_npc->max_dmg * 150 /100; + make_npc->cur_hp = make_npc->cur_hp * 110 / 100; + make_npc->max_hp = make_npc->max_hp * 110 / 100; + break; + case MONK: + make_npc->npc_attacks[0] = 'Q'; + make_npc->max_dmg = make_npc->max_dmg * 150 /100; + make_npc->cur_hp = make_npc->cur_hp * 135 / 100; + make_npc->max_hp = make_npc->max_hp * 135 / 100; + break; + case WARRIOR: + case BERSERKER: + make_npc->npc_attacks[0] = 'Q'; + make_npc->max_dmg = make_npc->max_dmg * 150 /100; + make_npc->cur_hp = make_npc->cur_hp * 175 / 100; + make_npc->max_hp = make_npc->max_hp * 175 / 100; + break; + default: + make_npc->npc_spells_id = 0; + break; + } + + make_npc->loottable_id = 0; + make_npc->merchanttype = 0; + 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()){ + AA_SwarmPetInfo* nSI = new AA_SwarmPetInfo; + npca->SetSwarmInfo(nSI); + npca->GetSwarmInfo()->duration = new Timer(duration*1000); + } + else{ + npca->GetSwarmInfo()->duration->Start(duration*1000); + } + + npca->GetSwarmInfo()->owner_id = GetID(); + + //give the pet somebody to "love" + if(target != NULL){ + npca->AddToHateList(target, 100000); + npca->GetSwarmInfo()->target = target->GetID(); + } + + //gear stuff, need to make sure there's + //no situation where this stuff can be duped + for(int x = 0; x < 21; x++) + { + uint32 sitem = 0; + sitem = CorpseToUse->GetWornItem(x); + if(sitem){ + const Item_Struct * itm = database.GetItem(sitem); + npca->AddLootDrop(itm, &npca->itemlist, 1, 1, 127, true, true); + } + } + + //we allocated a new NPC type object, give the NPC ownership of that memory + if(make_npc != NULL) + npca->GiveNPCTypeData(make_npc); + + entity_list.AddNPC(npca, true, true); + + //the target of these swarm pets will take offense to being cast on... + if(target != NULL) + target->AddToHateList(this, 1, 0); +} + +//turn on an AA effect +//duration == 0 means no time limit, used for one-shot deals, etc.. +void Client::EnableAAEffect(aaEffectType type, uint32 duration) { + if(type > 32) + return; //for now, special logic needed. + m_epp.aa_effects |= 1 << (type-1); + + if(duration > 0) { + p_timers.Start(pTimerAAEffectStart + type, duration); + } else { + p_timers.Clear(&database, pTimerAAEffectStart + type); + } +} + +void Client::DisableAAEffect(aaEffectType type) { + if(type > 32) + return; //for now, special logic needed. + uint32 bit = 1 << (type-1); + if(m_epp.aa_effects & bit) { + m_epp.aa_effects ^= bit; + } + p_timers.Clear(&database, pTimerAAEffectStart + type); +} + +/* +By default an AA effect is a one shot deal, unless +a duration timer is set. +*/ +bool Client::CheckAAEffect(aaEffectType type) { + if(type > 32) + return(false); //for now, special logic needed. + if(m_epp.aa_effects & (1 << (type-1))) { //is effect enabled? + //has our timer expired? + if(p_timers.Expired(&database, pTimerAAEffectStart + type)) { + DisableAAEffect(type); + return(false); + } + return(true); + } + return(false); +} + +void Client::SendAAStats() { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AAExpUpdate, sizeof(AltAdvStats_Struct)); + AltAdvStats_Struct *aps = (AltAdvStats_Struct *)outapp->pBuffer; + aps->experience = m_pp.expAA; + aps->experience = (uint32)(((float)330.0f * (float)m_pp.expAA) / (float)max_AAXP); + aps->unspent = m_pp.aapoints; + aps->percentage = m_epp.perAA; + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::BuyAA(AA_Action* action) +{ + mlog(AA__MESSAGE, "Starting to buy AA %d", action->ability); + + //find the AA information from the database + SendAA_Struct* aa2 = zone->FindAA(action->ability); + if(!aa2) { + //hunt for a lower level... + int i; + int a; + for(i=1;iability - i; + if(a <= 0) + break; + mlog(AA__MESSAGE, "Could not find AA %d, trying potential parent %d", action->ability, a); + aa2 = zone->FindAA(a); + if(aa2 != NULL) + break; + } + } + if(aa2 == NULL) + return; //invalid ability... + + if(aa2->special_category == 1 || aa2->special_category == 2) + return; // Not purchasable progression style AAs + + if(aa2->special_category == 8 && aa2->cost == 0) + return; // Not purchasable racial AAs(set a cost to make them purchasable) + + uint32 cur_level = GetAA(aa2->id); + if((aa2->id + cur_level) != action->ability) { //got invalid AA + mlog(AA__ERROR, "Unable to find or match AA %d (found %d + lvl %d)", action->ability, aa2->id, cur_level); + return; + } + + if(aa2->account_time_required) + { + if((Timer::GetTimeSeconds() - account_creation) < aa2->account_time_required) + { + return; + } + } + + uint32 real_cost; + std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(action->ability); + + 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); + + mlog(AA__MESSAGE, "Set AA %d to level %d", aa2->id, cur_level+1); + + m_pp.aapoints -= real_cost; + + Save(); + if ((RuleB(AA, Stacking) && (GetClientVersionBit() >= 4) && (aa2->hotkey_sid == 4294967295)) + && ((aa2->max_level == (cur_level+1)) && aa2->sof_next_id)){ + SendAA(aa2->id); + SendAA(aa2->sof_next_id); + } + else + SendAA(aa2->id); + + 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"); + + + SendAAStats(); + + CalcBonuses(); + if(title_manager.IsNewAATitleAvailable(m_pp.aapoints_spent, GetBaseClass())) + NotifyNewTitlesAvailable(); + } +} + +void Client::SendAATimer(uint32 ability, uint32 begin, uint32 end) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AAAction,sizeof(UseAA_Struct)); + UseAA_Struct* uaaout = (UseAA_Struct*)outapp->pBuffer; + uaaout->ability = ability; + uaaout->begin = begin; + uaaout->end = end; + QueuePacket(outapp); + safe_delete(outapp); +} + +//sends all AA timers. +void Client::SendAATimers() { + //we dont use SendAATimer because theres no reason to allocate the EQApplicationPacket every time + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AAAction,sizeof(UseAA_Struct)); + UseAA_Struct* uaaout = (UseAA_Struct*)outapp->pBuffer; + + PTimerList::iterator c,e; + c = p_timers.begin(); + e = p_timers.end(); + for(; c != e; c++) { + PersistentTimer *cur = c->second; + if(cur->GetType() < pTimerAAStart || cur->GetType() > pTimerAAEnd) + continue; //not an AA timer + //send timer + uaaout->begin = cur->GetStartTime(); + uaaout->end = static_cast(time(NULL)); + uaaout->ability = cur->GetType() - pTimerAAStart; // uuaaout->ability is really a shared timer number + QueuePacket(outapp); + } + + safe_delete(outapp); +} + +void Client::SendAATable() { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RespondAA, sizeof(AATable_Struct)); + + AATable_Struct* aa2 = (AATable_Struct *)outapp->pBuffer; + aa2->aa_spent = GetAAPointsSpent(); + + uint32 i; + for(i=0;i < MAX_PP_AA_ARRAY;i++){ + aa2->aa_list[i].aa_skill = aa[i]->AA; + aa2->aa_list[i].aa_value = aa[i]->value; + aa2->aa_list[i].unknown08 = 0; + } + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendPreviousAA(uint32 id, int seq){ + uint32 value=0; + SendAA_Struct* saa2 = NULL; + if(id==0) + saa2 = zone->GetAABySequence(seq); + else + saa2 = zone->FindAA(id); + if(!saa2) + return; + int size=sizeof(SendAA_Struct)+sizeof(AA_Ability)*saa2->total_abilities; + uchar* buffer = new uchar[size]; + SendAA_Struct* saa=(SendAA_Struct*)buffer; + value = GetAA(saa2->id); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAATable); + outapp->size=size; + outapp->pBuffer=(uchar*)saa; + value--; + memcpy(saa,saa2,size); + + if(value>0){ + if(saa->spellid==0) + saa->spellid=0xFFFFFFFF; + saa->id+=value; + saa->next_id=saa->id+1; + if(value==1) + saa->last_id=saa2->id; + else + saa->last_id=saa->id-1; + saa->current_level=value+1; + saa->cost2 = 0; //cost 2 is what the client uses to calc how many points we've spent, so we have to add up the points in order + for(uint32 i = 0; i < (value+1); i++) { + saa->cost2 += saa->cost + (saa->cost_inc * i); + } + } + + database.FillAAEffects(saa); + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendAA(uint32 id, int seq) { + + uint32 value=0; + SendAA_Struct* saa2 = NULL; + SendAA_Struct* qaa = NULL; + SendAA_Struct* saa_pp = NULL; + bool IsBaseLevel = true; + bool aa_stack = false; + + if(id==0) + saa2 = zone->GetAABySequence(seq); + else + saa2 = zone->FindAA(id); + if(!saa2) + return; + + uint16 classes = saa2->classes; + if(!(classes & (1 << GetClass())) && (GetClass()!=BERSERKER || saa2->berserker==0)){ + return; + } + + if(saa2->account_time_required) + { + if((Timer::GetTimeSeconds() - account_creation) < saa2->account_time_required) + { + return; + } + } + + // Hide Quest/Progression AAs unless player has been granted the first level using $client->IncrementAA(skill_id). + if (saa2->special_category == 1 || saa2->special_category == 2 ) { + if(GetAA(saa2->id) == 0) + return; + // For Quest line AA(demiplane AEs) where only 1 is visible at a time, check to make sure only the highest level obtained is shown + if(saa2->aa_expansion > 0) { + qaa = zone->FindAA(saa2->id+1); + if(qaa && (saa2->aa_expansion == qaa->aa_expansion) && GetAA(qaa->id) > 0) + return; + } + } + +/* Beginning of Shroud AAs, these categories are for Passive and Active Shroud AAs + Eventually with a toggle we could have it show player list or shroud list + if (saa2->special_category == 3 || saa2->special_category == 4) + return; +*/ + // Check for racial/Drakkin blood line AAs + if (saa2->special_category == 8) + { + uint32 client_race = this->GetBaseRace(); + + // Drakkin Bloodlines + if (saa2->aa_expansion > 522) + { + if (client_race != 522) + return; // Check for Drakkin Race + + int heritage = this->GetDrakkinHeritage() + 523; // 523 = Drakkin Race(522) + Bloodline + + if (heritage != saa2->aa_expansion) + return; + } + // Racial AAs + else if (client_race != saa2->aa_expansion) + { + return; + } + } + + /* + AA stacking on SoF+ clients. + + 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 + 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. + + How stacking works: + Utilizes two new fields: sof_next_id (which is the next id in the series), sof_current_level (ranks the AA's as the current level) + 1) If no AA's purchased only display the base levels of each AA series. + 2) When you purchase an AA and its rank is maxed it sends the packet for the completed AA, and the packet + for the next aa in the series. The previous tier is removed from your window, and the new AA is displayed. + 3) When you zone/buy your player profile will be checked and determine what AA can be displayed base on what you have already. + */ + + if (RuleB(AA, Stacking) && (GetClientVersionBit() >= 4) && (saa2->hotkey_sid == 4294967295)) + aa_stack = true; + + if (aa_stack){ + uint32 aa_AA = 0; + uint32 aa_value = 0; + for (int i = 0; i < MAX_PP_AA_ARRAY; i++) { + if (aa[i]) { + aa_AA = aa[i]->AA; + aa_value = aa[i]->value; + + if (aa_AA){ + + if (aa_value > 0) + aa_AA -= aa_value-1; + + saa_pp = zone->FindAA(aa_AA); + + if (saa_pp){ + + if (saa_pp->sof_next_skill == saa2->sof_next_skill){ + + if (saa_pp->id == saa2->id) + break; //You already have this in the player profile. + else if ((saa_pp->sof_current_level < saa2->sof_current_level) && (aa_value < saa_pp->max_level)) + return; //DISABLE DISPLAY HIGHER - You have not reached max level yet of your current AA. + else if ((saa_pp->sof_current_level < saa2->sof_current_level) && (aa_value == saa_pp->max_level) && (saa_pp->sof_next_id == saa2->id)) + IsBaseLevel = false; //ALLOW DISPLAY HIGHER + } + } + } + } + } + } + + //Hide higher tiers of multi tiered AA's if the base level is not fully purchased. + if (aa_stack && IsBaseLevel && saa2->sof_current_level > 0) + return; + + int size=sizeof(SendAA_Struct)+sizeof(AA_Ability)*saa2->total_abilities; + uchar* buffer = new uchar[size]; + SendAA_Struct* saa=(SendAA_Struct*)buffer; + memcpy(saa,saa2,size); + + if(saa->spellid==0) + saa->spellid=0xFFFFFFFF; + + value=GetAA(saa->id); + uint32 orig_val = value; + + if(value && saa->id){ + + if(value < saa->max_level){ + saa->id+=value; + saa->next_id=saa->id+1; + value++; + } + + else if (aa_stack && saa->sof_next_id){ + saa->id+=value-1; + saa->next_id=saa->sof_next_id; + + //Prevent removal of previous AA from window if next AA belongs to a higher client version. + SendAA_Struct* saa_next = NULL; + saa_next = zone->FindAA(saa->sof_next_id); + if (saa_next && + (((GetClientVersionBit() == 4) && (saa_next->clientver > 4)) + || ((GetClientVersionBit() == 8) && (saa_next->clientver > 5)) + || ((GetClientVersionBit() == 16) && (saa_next->clientver > 6)))){ + saa->next_id=0xFFFFFFFF; + } + } + + else{ + saa->id+=value-1; + saa->next_id=0xFFFFFFFF; + } + + uint32 current_level_mod = 0; + if (aa_stack) + current_level_mod = saa->sof_current_level; + + saa->last_id=saa->id-1; + saa->current_level=value+(current_level_mod); + saa->cost = saa2->cost + (saa2->cost_inc*(value-1)); + saa->cost2 = 0; + for(uint32 i = 0; i < value; i++) { + saa->cost2 += saa2->cost + (saa2->cost_inc * i); + } + saa->class_type = saa2->class_type + (saa2->level_inc*(value-1)); + } + + if (aa_stack){ + + if (saa->sof_current_level > 1 && value == 0) + saa->current_level = saa->sof_current_level+1; + + saa->max_level = saa->sof_max_level; + } + + database.FillAAEffects(saa); + + if(value > 0) + { + const AA_DBAction *caa = &AA_Actions[saa->id][value - 1]; + + if(caa && caa->reuse_time > 0) + saa->spell_refresh = CalcAAReuseTimer(caa); + } + + //You can now use the level_inc field in the altadv_vars table to accomplish this, though still needed + //for special cases like LOH/HT due to inability to implement correct stacking of AA's that use hotkeys. + std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(saa->id); + + if(RequiredLevel != AARequiredLevelAndCost.end()) + { + saa->class_type = RequiredLevel->second.Level; + saa->cost = RequiredLevel->second.Cost; + } + + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAATable); + outapp->size=size; + outapp->pBuffer=(uchar*)saa; + if(id==0 && value && (orig_val < saa->max_level)) //send previous AA only on zone in + SendPreviousAA(id, seq); + + QueuePacket(outapp); + safe_delete(outapp); + //will outapp delete the buffer for us even though it didnt make it? --- Yes, it should +} + +void Client::SendAAList(){ + int total = zone->GetTotalAAs(); + for(int i=0;i < total;i++){ + SendAA(0,i); + } +} + +uint32 Client::GetAA(uint32 aa_id) const { + map::const_iterator res; + res = aa_points.find(aa_id); + if(res != aa_points.end()) { + return(res->second); + } + return(0); +} + +bool Client::SetAA(uint32 aa_id, uint32 new_value) { + aa_points[aa_id] = new_value; + uint32 cur; + for(cur=0;cur < MAX_PP_AA_ARRAY;cur++){ + if((aa[cur]->value > 1) && ((aa[cur]->AA - aa[cur]->value + 1)== aa_id)){ + aa[cur]->value = new_value; + if(new_value > 0) + aa[cur]->AA++; + else + aa[cur]->AA = 0; + return true; + } + else if((aa[cur]->value == 1) && (aa[cur]->AA == aa_id)){ + aa[cur]->value = new_value; + if(new_value > 0) + aa[cur]->AA++; + else + aa[cur]->AA = 0; + return true; + } + else if(aa[cur]->AA==0){ //end of list + aa[cur]->AA = aa_id; + aa[cur]->value = new_value; + return true; + } + } + return false; +} + +SendAA_Struct* Zone::FindAA(uint32 id) { + return aas_send[id]; +} + +void Zone::LoadAAs() { + LogFile->write(EQEMuLog::Status, "Loading AA information..."); + totalAAs = database.CountAAs(); + if(totalAAs == 0) { + LogFile->write(EQEMuLog::Error, "Failed to load AAs!"); + aas = NULL; + return; + } + aas = new SendAA_Struct *[totalAAs]; + + database.LoadAAs(aas); + + int i; + for(i=0; i < totalAAs; i++){ + SendAA_Struct* aa = aas[i]; + aas_send[aa->id] = aa; + } + + //load AA Effects into aa_effects + LogFile->write(EQEMuLog::Status, "Loading AA Effects..."); + if (database.LoadAAEffects2()) + LogFile->write(EQEMuLog::Status, "Loaded %d AA Effects.", aa_effects.size()); + else + LogFile->write(EQEMuLog::Error, "Failed to load AA Effects!"); +} + +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))!= NULL) { + 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); + return false; + } + safe_delete_array(query); + return true; +} +void Client::ResetAA(){ + uint32 i; + for(i=0;iAA = 0; + aa[i]->value = 0; + } + map::iterator itr; + for(itr=aa_points.begin();itr!=aa_points.end();itr++) + aa_points[itr->first] = 0; + + for(int i = 0; i < _maxLeaderAA; ++i) + m_pp.leader_abilities.ranks[i] = 0; + + m_pp.group_leadership_points = 0; + m_pp.raid_leadership_points = 0; + m_pp.group_leadership_exp = 0; + m_pp.raid_leadership_exp = 0; +} + +int Client::GroupLeadershipAAHealthEnhancement() +{ + Group *g = GetGroup(); + + if(!g || (g->GroupCount() < 3)) + return 0; + + switch(g->GetLeadershipAA(groupAAHealthEnhancement)) + { + case 0: + return 0; + case 1: + return 30; + case 2: + return 60; + case 3: + return 100; + } + + return 0; +} + +int Client::GroupLeadershipAAManaEnhancement() +{ + Group *g = GetGroup(); + + if(!g || (g->GroupCount() < 3)) + return 0; + + switch(g->GetLeadershipAA(groupAAManaEnhancement)) + { + case 0: + return 0; + case 1: + return 30; + case 2: + return 60; + case 3: + return 100; + } + + return 0; +} + +int Client::GroupLeadershipAAHealthRegeneration() +{ + Group *g = GetGroup(); + + if(!g || (g->GroupCount() < 3)) + return 0; + + switch(g->GetLeadershipAA(groupAAHealthRegeneration)) + { + case 0: + return 0; + case 1: + return 4; + case 2: + return 6; + case 3: + return 8; + } + + return 0; +} + +int Client::GroupLeadershipAAOffenseEnhancement() +{ + Group *g = GetGroup(); + + if(!g || (g->GroupCount() < 3)) + return 0; + + 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; + } + return 0; +} + +void Client::InspectBuffs(Client* Inspector, int Rank) +{ + if(!Inspector || (Rank == 0)) return; + + 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 = NULL; + 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); + } + } + } + } +} + +//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); + return false; + } + + return true; +} + +//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; +} + +//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 +uint8 Zone::GetTotalAALevels(uint32 skill_id) { + size_t sz = aa_effects[skill_id].size(); + return sz >= 255 ? 255 : static_cast(sz); +} + +/* +Every AA can send the client effects, which are purely for client side effects. +Essentially it's like being able to attach a very simple version of a spell to +Any given AA, it has 4 fields: +skill_id = spell effect id +slot = ID slot, doesn't appear to have any impact on stacking like real spells, just needs to be unique. +base1 = the base field of a spell +base2 = base field 2 of a spell, most AAs do not utilize this +example: + skill_id = SE_STA + slot = 1 + base1 = 15 + This would if you filled the abilities struct with this make the client show if it had + that AA an additional 15 stamina on the client's stats +*/ +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))!=NULL) { + 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++; + } + 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))!=NULL) + count = atoi(row[0]); + mysql_free_result(result); + } else { + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::CountAAs query '%s': %s", query, errbuf); + } + safe_delete_array(query); + return count; +} + +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))!=NULL){ + count = atoi(row[0]); + } + mysql_free_result(result); + } else { + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::CountAALevels query '%s': %s", query, errbuf); + } + safe_delete_array(query); + return count; +} + +uint32 ZoneDatabase::GetSizeAA(){ + int size=CountAAs()*sizeof(SendAA_Struct); + if(size>0) + size+=CountAAEffects()*sizeof(AA_Ability); + return size; +} + +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))!=NULL) { + skill=atoi(row[0]); + load[ndx] = GetAASkillVars(skill); + load[ndx]->seq = ndx+1; + ndx++; + } + mysql_free_result(result); + } else { + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAs query '%s': %s", query, errbuf); + } + safe_delete_array(query); + + AARequiredLevelAndCost.clear(); + + 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))!=NULL) + { + 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); + + safe_delete_array(query); +} + +SendAA_Struct* ZoneDatabase::GetAASkillVars(uint32 skill_id) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + SendAA_Struct* sendaa = NULL; + 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; + + 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); + + buffer = new uchar[totalsize]; + memset(buffer,0,totalsize); + sendaa = (SendAA_Struct*)buffer; + + row = mysql_fetch_row(result); + + //ATOI IS NOT UNISGNED 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])); + } + 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; +} + +void Client::DurationRampage(uint32 duration) +{ + if(duration) { + m_epp.aa_effects |= 1 << (aaEffectRampage-1); + p_timers.Start(pTimerAAEffectStart + aaEffectRampage, duration); + } +} + +AA_SwarmPetInfo::AA_SwarmPetInfo() +{ + target = 0; + owner_id = 0; + duration = NULL; +} + +AA_SwarmPetInfo::~AA_SwarmPetInfo() +{ + target = 0; + owner_id = 0; + safe_delete(duration); +} + +Mob *AA_SwarmPetInfo::GetOwner() +{ + return entity_list.GetMobID(owner_id); +} \ No newline at end of file diff --git a/zone/AA.h b/zone/AA.h new file mode 100644 index 000000000..cb3a5f889 --- /dev/null +++ b/zone/AA.h @@ -0,0 +1,2169 @@ + +#ifndef AA_H +#define AA_H + +#include "../common/eq_packet_structs.h" + +#define MANA_BURN 664 + + +#include +using namespace std; + + +#define MAX_SWARM_PETS 12 //this can change as long as you make more coords (swarm_pet_x/swarm_pet_y) + +//this might be missing some, and some might not be used... +typedef enum { //AA Targeting Constants + aaTargetUser = 1, + aaTargetCurrent = 2, //use current target + aaTargetGroup = 3, //target group of user + aaTargetCurrentGroup = 4, //target group of current target + aaTargetPet = 5 //target the user's pet +} aaTargetType; + + +typedef enum { + aaActionNone = 0, + aaActionAETaunt = 1, + aaActionMassBuff = 2, + aaActionFlamingArrows = 3, + aaActionFrostArrows = 4, + aaActionRampage = 5, + aaActionSharedHealth = 6, + aaActionCelestialRegen = 7, + aaActionDireCharm = 8, + aaActionImprovedFamiliar = 9, + aaActionActOfValor = 10, + aaActionSuspendedMinion = 11, + aaActionEscape = 12, + aaActionBeastialAlignment = 13, + aaActionLeechTouch = 14, + aaActionProjectIllusion = 15, + aaActionFadingMemories = 16 +} aaNonspellAction; + +//use these for AAs which dont cast spells, yet need effects +//if this list grows beyond 32, more work is needed in *AAEffect +typedef enum { //AA Effect IDs + aaEffectMassGroupBuff = 1, + aaEffectRampage, + aaEffectSharedHealth, + aaEffectFlamingArrows, + aaEffectFrostArrows, + aaEffectWarcry, + aaEffectLeechTouch, + aaEffectProjectIllusion // seveian 2008-09-23 +} aaEffectType; + + +enum { //leadership AA indexes + groupAAMarkNPC = 0, + groupAANPCHealth, + groupAADelegateMainAssist, + groupAADelegateMarkNPC, + groupAA4, + groupAA5, + groupAAInspectBuffs, + groupAA7, + groupAASpellAwareness, + groupAAOffenseEnhancement, + groupAAManaEnhancement, + groupAAHealthEnhancement, + groupAAHealthRegeneration, + groupAAFindPathToPC, + groupAAHealthOfTargetsTarget, + groupAA15, + + raidAAMarkNPC, //0x10, things assume this is the first raid ability + raidAANPCHealth, + raidAADelegateMainAssist, + raidAADelegateMarkNPC, + raidAA4, + raidAA5, + raidAA6, + raidAASpellAwareness, + raidAAOffenseEnhancement, + raidAAManaEnhancement, + raidAAHealthEnhancement, + raidAAHealthRegeneration, + raidAAFindPathToPC, + raidAAHealthOfTargetsTarget, + raidAA14, + raidAA15, + + _maxLeaderAA //=32 +}; + +#define MAX_LEADERSHIP_TIERS 6 +//each progression should be 0 terminated to mark it as the end. +static const uint8 LeadershipAACosts[_maxLeaderAA][MAX_LEADERSHIP_TIERS] = { +{ 1, 2, 3, 0, 0, 0 }, //groupAAMarkNPC +{ 2, 0, 0, 0, 0, 0 }, //groupAANPCHealth +{ 4, 0, 0, 0, 0, 0 }, //groupAADelegateMainAssist - Have seen DelegateMainAssist come in with two different codes. +{ 4, 0, 0, 0, 0, 0 }, //groupAADelegateMainAssist +{ 4, 0, 0, 0, 0, 0 }, //groupAADelegateMarkNPC +{ 0, 0, 0, 0, 0, 0 }, //groupAA5 +{ 4, 6, 0, 0, 0, 0 }, //groupAAInspectBuffs +{ 0, 0, 0, 0, 0, 0 }, //groupAA7 +{ 6, 0, 0, 0, 0, 0 }, //groupAASpellAwareness +{ 4, 5, 6, 7, 8, 0 }, //groupAAOffenseEnhancement +{ 4, 6, 8, 0, 0, 0 }, //groupAAManaEnhancement +{ 4, 6, 8, 0, 0, 0 }, //groupAAHealthEnhancement +{ 4, 6, 8, 0, 0, 0 }, //groupAAHealthRegeneration +{ 4, 0, 0, 0, 0, 0 }, //groupAAFindPathToPC +{ 7, 0, 0, 0, 0, 0 }, //groupAAHealthOfTargetsTarget +{ 0, 0, 0, 0, 0, 0 }, //groupAA15 + +{ 5, 6, 7, 0, 0, 0 }, //raidAAMarkNPC //0x10 +{ 4, 0, 0, 0, 0, 0 }, //raidAANPCHealth +{ 6, 7, 8, 0, 0, 0 }, //raidAADelegateMainAssist +{ 6, 6, 6, 0, 0, 0 }, //raidAADelegateMarkNPC +{ 6, 6, 6, 0, 0, 0 }, //raidAADelegateMarkNPC (works for SoD and Titanium) +{ 0, 0, 0, 0, 0, 0 }, //raidAA5 +{ 0, 0, 0, 0, 0, 0 }, //raidAA6 +{ 8, 0, 0, 0, 0, 0 }, //raidAASpellAwareness +{ 6, 7, 8, 9, 10, 0 }, //raidAAOffenseEnhancement +{ 6, 8, 10, 0, 0, 0 }, //raidAAManaEnhancement +{ 6, 8, 10, 0, 0, 0 }, //raidAAHealthEnhancement +{ 6, 8, 10, 0, 0, 0 }, //raidAAHealthRegeneration +{ 5, 0, 0, 0, 0, 0 }, //raidAAFindPathToPC +{ 9, 0, 0, 0, 0, 0 }, //raidAAHealthOfTargetsTarget +{ 0, 0, 0, 0, 0, 0 }, //raidAA14 +{ 0, 0, 0, 0, 0, 0 }, //raidAA15 +}; + +/* +typedef enum { //AA IDs + aaNone = 0, + aaInnateStrength = 2, //works + aaInnateStamina = 7, //works + aaInnateAgility = 12, //works + //aaCompleteHeal = 13,/ //not implemented, but is in dbstr_us.txt + aaInnateDexterity = 17, //works + aaInnateIntelligence = 22, //works + aaInnateWisdom = 27, //works + aaInnateCharisma = 32, //works + aaInnateFireProtection = 37, //works + aaInnateColdProtection = 42, //works + aaInnateMagicProtection = 47, //works + aaInnatePoisonProtection = 52, //works + aaInnateDiseaseProtection = 57, //works + aaInnateRunSpeed = 62, //works + aaInnateRegeneration = 65, //works + aaInnateMetabolism = 68, + aaInnateLungCapacity = 71, //handled by client + aaFirstAid = 74, //untested + aaHealingAdept = 77, //untested + aaHealingGift = 80, //untested + aaSpellCastingMastery = 83, //untested + aaSpellCastingReinforcement = 86, //untested + aaMentalClarity = 89, + aaSpellCastingFury = 92, //untested + aaChanellingFocus = 95, + aaSpellCastingSubtlety = 98, //untested + aaSpellCastingExpertise = 101, //untested + aaSpellCastingDeftness = 104, //untested + aaNaturalDurability = 107, //works + aaNaturalHealing = 110, //untested + aaCombatFury = 113, //untested + aaFearResistance = 116, //untested + aaFinishingBlow = 119, //untested + aaCombatStability = 122, + aaCombatAgility = 125, + aaMassGroupBuff = 128, //untested + aaDivineResurrection = 129, //DB + aaInnateInvisToUndead = 130, //DB + aaCelestialRegeneration = 131, //untested + aaBestowDivineAura = 132, //DB + aaTurnUndead = 133, //DB + aaPurifySoul = 136, //DB + aaQuickEvacuation = 137, //untested + aaExodus = 140, //untested + aaQuickDamage = 141, //untested + aaEnhancedRoot = 144, + aaDireCharm = 145, //untested + aaCannibalization = 146, //DB + aaQuickBuff = 147, //untested + aaAlchemyMastery = 150, + aaRabidBear = 153, //DB + aaManaBurn = 154, //DB + aaImprovedFamiliar = 155, //untested, implemented? + aaNexusGate = 156, //DB + aaUnknown54 = 157, + aaPermanentIllusion = 158, + aaJewelCraftMastery = 159, + aaGatherMana = 162, //DB + aaMendCompanion = 163, //DB + aaQuickSummoning = 164, //untested + aaFrenziedBurnout = 167, //DB + aaElementalFormFire = 168, //DB + aaElementalFormWater = 171, //DB + aaElementalFormEarth = 174, //DB + aaElementalFormAir = 177, //DB + aaImprovedReclaimEnergy = 180, //untested + aaTurnSummoned = 181, //DB + aaElementalPact = 182, //DB + aaLifeBurn = 183, //DB + aaDeadMesmerization = 184, //DB + aaFearstorm = 185, //DB + aaFleshToBone = 186, //DB + aaCallToCorpse = 187, //DB + aaDivineStun = 188, //DB + aaImprovedLayOnHands = 189, + aaSlayUndead = 190, + aaActOfValor = 193, //DB + aaHolySteed = 194, //DB + aaFearless = 195, + aa2HandBash = 196, //works. handled by client? + aaInnateCamouflage = 197, //DB + aaAmbidexterity = 198, //untested + aaArcheryMastery = 199, //untested + aaFletchingMastery = 202, //removed from db? + aaEndlessQuiver = 205, //untested + aaUnholySteed = 206, //DB + aaImprovedHarmTouch = 207, //untested + aaLeechTouch = 208, //DB + aaDeathPeace = 209, + aaSoulAbrasion = 210, //untested + aaInstrumentMastery = 213, //untested + aaUnknown91 = 216, //not used + aaUnknown92 = 219, //not used + aaUnknown93 = 222, //not used + aaJamFest = 225, + aaUnknown95 = 228, + aaSonicCall = 229, + aaCriticalMend = 230, //untested + aaPurifyBody = 233, //DB + aaChainCombo = 234, + aaRapidFeign = 237, //works + aaReturnKick = 240, + aaEscape = 243, //DB + aaPoisonMastery = 244, + aaDoubleRiposte = 247, //untested + aaQuickHide = 250, + aaQuickThrow = 253, //corrected from dbstr_us.txt + aaPurgePoison = 254, //DB + aaFlurry = 255, //untested + aaRampage = 258, //untested + aaAreaTaunt = 259, //untested + aaWarcry = 260, //DB + aaBandageWound = 263, //untested + aaSpellCastingReinforcementMastery = 266, //untested + aaSpellCastingFuryMastery = 267, //untested + aaExtendedNotes = 270, //untested + aaDragonPunch = 273, + aaStrongRoot = 274, //DB + aaSingingMastery = 275, //untested + aaBodyAndMindRejuvenation = 278, //added + aaPhysicalEnhancement = 279, //untested + aaAdvTrapNegotiation = 280, //untested + aaAcrobatics = 283, //untested + aaScribbleNotes = 286, + aaChaoticStab = 287, //untested + aaPetDiscipline = 288, //added + aaHobbleofSpirits = 289, //DB + aaFrenzyofSpirit = 290, //DB + aaParagonofSpirit = 291, //DB + aaAdvancedInnateStrength = 292, //works + aaAdvancedInnateStamina = 302, //works + aaAdvancedInnateAgility = 312, //works + aaAdvancedInnateDexterity = 322, //works + aaAdvancedInnateIntelligence = 332, //works + aaAdvancedInnateWisdom = 342, //works + aaAdvancedInnateCharisma = 352, //works + aaWardingofSolusek = 362, //works + aaBlessingofEci = 372, //works + aaMarrsProtection = 382, //works + aaShroudofTheFaceless = 392, //works + aaBertoxxulousGift = 402, //works + aaNewTanaanCraftingMastery = 412, + aaPlanarPower = 418, //untested + aaPlanarDurability = 423, //added + aaInnateEnlightenment = 426, //added + aaAdvancedSpellCastingMastery = 431,//untested + aaAdvancedHealingAdept = 434, //untested + aaAdvancedHealingGift = 437, //untested + aaCoupdeGrace = 440, //added + aaFuryoftheAges = 443, //added + aaMasteryofthePast = 446, //untested + aaLightningReflexes = 449, //added + aaInnateDefense = 454, //added + aaRadiantCure = 459, //DB + aaHastenedDivinity = 462, //DB + aaHastenedTurning = 465, //DB + aaHastenedPurificationofSoul = 468, //DB + aaHastenedGathering = 471, //DB + aaHastenedRabidity = 474, //DB + aaHastenedExodus = 477, //DB + aaHastenedRoot = 480, //DB + aaHastenedMending = 483, //DB + aaHastenedBanishment = 486, //DB + aaHastenedInstigation = 489, //DB, maybe + aaFuriousRampage = 492, //DB + aaHastenedPurificationoftheBody = 495,//DB + aaHastyExit = 498, //DB + aaHastenedPurification = 501, //DB + aaFlashofSteel = 504, + aaDivineArbitration = 507, //DB + aaWrathoftheWild = 510, //DB + aaVirulentParalysis = 513, //DB + aaHarvestofDruzzil = 516, //DB + aaEldritchRune = 517, //DB + aaServantofRo = 520, //DB + aaWaketheDead = 523, //DB + aaSuspendedMinion = 526, //untested + aaSpiritCall = 528, //DB + aaCelestialRenewal = 531, //DB + aaAllegiantFamiliar = 533, + aaHandofPiety = 534, //DB + aaMithanielsBinding = 537, //untested + aaMendingoftheTranquil = 539, + aaRagingFlurry = 542, + aaGuardianoftheForest = 545, //DB + aaSpiritoftheWood = 548, //DB + aaBestialFrenzy = 551, //untested + aaHarmoniousAttack = 556, //untested + aaKnightsAdvantage = 561, + aaFerocity = 564, + aaViscidRoots = 567, + aaSionachiesCrescendo = 568, //untested + aaAyonaesTutelage = 571, + aaFeignedMinion = 574, + aaUnfailingDivinity = 577, + aaAnimationEmpathy = 580, // Implemented + aaRushtoJudgement = 583, + aaLivingShield = 586, + aaConsumptionoftheSoul = 589, //untested + aaBoastfulBellow = 592, //DB + aaFervrentBlessing = 593, //untested + aaTouchoftheWicked = 596, //untested + aaPunishingBlade = 599, + aaSpeedoftheKnight = 602, + aaShroudofStealth = 605, + aaNimbleEvasion = 606, + aaTechniqueofMasterWu = 611, + aaHostoftheElements = 616, //DB + aaCallofXuzl = 619, //DB + aaHastenedStealth = 622, + aaIngenuity = 625, + aaFleetofFoot = 628, + aaFadingMemories = 630, + aaTacticalMastery = 631, + aaTheftofLife = 634, + aaFuryofMagic = 637, + aaFuryofMagicMastery2 = 640, //whats the difference? + aaProjectIllusion = 643, + aaHeadshot = 644, //added + aaEntrap = 645, //DB + aaUnholyTouch = 646, //untested + aaTotalDomination = 649, // Implemented + aaStalwartEndurance = 652, //implemented as bonus + aaQuickSummoning2 = 655, //whats the difference? + aaMentalClarity2 = 658, //whats the difference? + aaInnateRegeneration2 = 661, //whats the difference? + aaManaBurn2 = 664, //whats the difference? + aaExtendedNotes2 = 665, //not implemented - later expansions replaced Extended Notes with this. + aaSionachiesCrescendo2 = 668, //not implemented - later expansions replaced Sionachies Crescendo with this. + aaImprovedReclaimEnergy2 = 671, //whats the difference? untetsed + aaSwiftJourney = 672, //implemented as bonus + aaConvalescence = 674, //added 9/26/08 + aaLastingBreath = 676, //handled by client + aaPackrat = 678, //added 9/29/08 + aaHeightenedEndurance = 683, + aaWeaponAffinity = 686, //implemented + aaSecondaryForte = 691, + aaPersistantCasting = 692, + aaTuneofPursuance = 695, + aaImprovedInstrumentMastery = 700, + aaImprovedSingingMastery =701, + aaExultantBellowing = 702, + aaEchoofTaelosia = 707, + aaInternalMetronome = 710, //In 2006 this AA was removed. + aaPiousSupplication = 715, + aaBeastialAlignment = 718, //untested + aaWrathofXuzl = 721, + aaFeralSwipe = 723, //DB? + aaWardersFury = 724, + aaWardersAlacrity = 729, + aaPetAffinity = 734, // Implemented + aaMasteryofthePast2 = 735, //whats the difference? + aaSpellCastingSubtlety2 = 738, //whats the difference? + aaTouchoftheDivine = 741, + aaDivineAvatar = 746, //DB + aaExquisiteBenediction = 749, //DB + aaQuickenedCuring = 754, + aaNaturesBoon = 757, //DB + aaAdvancedTracking = 762, + aaCriticalAffliction = 767, + aaFuryofMagicMastery = 770, //whats the difference? + aaDoppelganger = 773, + aaEnchancedForgetfulness = 776, + aaMesmerizationMastery = 781, + aaQuickMassGroupBuff = 782, + aaSharedHealth = 785, + aaElementalFury = 790, + aaElementalAlacrity = 795, + aaElementalAgility = 800, + aaElementalDurability = 803, + aaSinisterStrikes = 806, + aaStrikethrough = 807, + aaStonewall = 810, + aaRapidStrikes = 815, + aaKickMastery = 820, + aaHightenedAwareness = 823, + aaDestructiveForce = 828, //DB + aaSwarmofDecay = 831, //DB + aaDeathsFury = 834, + aaQuickeningofDeath = 839, + aaAdvancedTheftofLife = 844, + aaTripleBackstab = 846, + aaHastenedPiety = 849, + aaImmobilizingBash = 852, + aaViciousSmash = 855, + aaRadiantCure2 = 860, //whats the difference? + aaPurification = 863, + aaPrecisionofthePathfinder = 864, + aaCoatofThistles = 867, + aaFlamingArrows = 872, //untested + aaFrostArrows = 875, //untested + aaSeizedOpportunity = 878, + aaTrapCircumvention = 881, + aaImprovedHastyExit = 886, + aaVirulentVenom = 888, + aaImprovedConsumptionofSoul = 893, + aaIntenseHatred = 895, + aaAdvancedSpiritCall = 900, + aaCalloftheAncients = 902, //DB + aaSturdiness = 907, + aaWarlordsTenacity = 912, //DB + aaStrengthenedStrike = 915, + aaExtendedShielding = 918, + aaRosFlamingFamiliar = 921, //DB + aaEcisIcyFamiliar = 922, //DB + aaDruzzilsMysticalFamiliar = 923, //DB + aaAdvancedFuryofMagicMastery = 924, //added 9/29/08 + aaWardofDestruction = 926, //DB + aaFrenziedDevastation = 931, //DB + aaCombatFury2 = 934, //whats the difference? + aaCombatFury3 = 937, //whats the difference? + aaCombatFury4 = 940, //whats the difference? + aaFuryoftheAges2 = 943, //whats the difference? + aaFuryoftheAges3 = 946, //whats the difference? + aaFuryoftheAges4 = 949, //whats the difference? + aaPlanarDurability2 = 952, //whats the difference? + aaInnateEnlightenment2 = 955, //whats the difference? + aaDireCharm2 = 960, //whats the difference? + aaDireCharm3 = 961, //whats the difference? + aaTouchoftheDivine2 = 962, //whats the difference? + aaTouchofDecay = 967, + aaCalloftheAncients2 = 970, //whats the difference? + aaImprovedVision = 975, + aaEternalBreath = 978, //handled by client + aaBlacksmithingMastery = 979, //added 9/29/08 + aaBakingMastery = 982, //added 9/29/08 + aaBrewingMastery = 985, //added 9/29/08 + aaFletchingMastery2 = 988, //added 9/29/08 + aaPotteryMastery = 991, //added 9/29/08 + aaTailoringMastery = 994, //added 9/29/08 + aaSalvage = 997, + aaOrigin = 1000, //spell + aaChaoticPotential = 1001, //added + aaDiscordantDefiance = 1006, //added 9/29/08 + aaTrialsofMataMuram = 1011, + aaMysticalAttuning = 1021, + aaDelayDeath = 1026, + aaHealthyAura = 1031, + aaFitness = 1036, + aaVeteransWrath = 1041, //added 9/29/08 + aaVeteransWrath2 = 1044, //whats the difference? + aaVeteransWrath3 = 1047, //whats the difference? + aaVeteransWrath4 = 1050, //whats the difference? + aaDeathblow = 1053, + aaReflexiveMastery = 1061, + aaDefensiveInstincts = 1066, + aaMnemonicRetention = 1071, //Implemented + aaExpansiveMind = 1072, //added 9/29/08 + aaSleightofHand = 1077, + aaSleightofHand2 = 1080, //whats the difference? + aaHealingAdeptMastery = 1083, + aaHealingGiftMastery = 1086, + aaArcaneTongues = 1089, + aaMasterofDisguise = 1092, + aaSlipperyAttacks = 1093, + aaImprovedCriticalAffliction = 1099, + aaFortifiedBellowing = 1102, + aaFuryofMagic2 = 1107, //whats the difference? + aaDanceofBlades = 1110, + aaShieldofNotes = 1116, + aaRoarofThunder = 1119, + aaPersistentMinion = 1122, + aaPerfectionofSpirit = 1123, + aaReplentishCompanion = 1126, + aaAdvancedPetDiscipline = 1129, + aaThrowingMastery = 1131, + aaBlurofAxes = 1134, + aaHastenedWarCry = 1137, + aaDeadAim = 1140, + aaFrenziedDefense = 1143, + aaTirelessSprint = 1146, + aaDesperation = 1149, + aaUntamedRage = 1150, + aaEchoingCries = 1155, + aaViciousFrenzy = 1158, + aaCrazedOnslaught = 1163, + aaOverwhelmingAttack = 1172, + aaFuriousRage = 1175, + aaBloodPact = 1178, + aaShieldingResistance = 1181, + aaHealingBoon = 1186, + aaResplendentCure = 1189, + aaCelestialHammer = 1192, + aaDivineRetribution = 1195, + aaCelestialRejuvination = 1203, + aaFerventBenediction = 1206, + aaSanctuary = 1209, + aaDestructiveFury = 1210, //added 9/29/08 + aaDestructiveFury2 = 1213, //whats the difference? + aaBoonoftheForest = 1222, + aaSpiritoftheGrove = 1225, + aaCalloftheWild = 1228, + aaSecondaryRecall = 1229, + aaNaturesBounty = 1230, + aaStasis = 1233, + aaColorShock = 1239, + aaMindOverMatter = 1242, + aaSoothingWords = 1245, + aaElementalSwarm = 1248, + aaHeartofFlames = 1251, + aaHeartofVapor = 1252, + aaHeartofIce = 1253, + aaHeartofStone = 1254, + aaImitateDeath = 1255, + aaCripplingStrike = 1256, + aaStunningKick = 1259, + aaEyeGouge = 1262, + aaIronKicks = 1265, + aaStyleoftheMimic = 1268, + aaDeathPeace2 = 1272, //whats the difference? + aaArmyoftheDead = 1274, + aaCelestialStun = 1277, + aaHandofDevotion = 1278, + aaSteadfastWill = 1284, + aaShieldBlock = 1287, + aaScoutsEfficiency = 1290, + aaGuardianoftheGlade = 1293, + aaTrackingMastery = 1296, + aaFlurryofKnives = 1301, + aaPrecision = 1304, + aaNervesofSteel = 1307, + aaTouchoftheCursed = 1313, + aaSpiritualCorrosion = 1316, + aaSoulThief = 1319, + aaSpiritualChanneling = 1323, + aaBoonoftheAncients = 1324, + aaAncestralAid = 1327, + aaResoluteDefiance = 1330, + aaPresstheAttack = 1333, + aaMindCrash = 1334, + aaProlongedDestruction = 1337, + aaRosGreaterFamiliar = 1340, + aaEcisGreaterFamiliar = 1341, + aaDruzzilsGreaterFamiliar = 1342, + aaTeleportBind = 1343, + aaDevotedFamiliar = 1344, + aaAuspiceoftheHunter = 1345, + aaSavageSpirit = 1348, + aaPresstheAttack2 = 1351, //whats the difference? + aaCripplingStrike2 = 1352, //whats the difference? + aaStunningKick2 = 1353, //whats the difference? + aaEyeGouge2 = 1358, //whats the difference? + + //Dragons of Norrath + //good info here: http://www.eqthieves.com/exp-don-progression.htm and here: http://everquest.allakhazam.com/db/guides.html?guide=811 + aaGiftoftheDarkReign = 1361, //from dbstr_us.txt + aaTenacityoftheDarkReign = 1362, //from dbstr_us.txt + aaEmbraceoftheDarkReign = 1363, //from dbstr_us.txt + aaPoweroftheDarkReign = 1364, //from dbstr_us.txt + aaFervoroftheDarkReign = 1365, //from dbstr_us.txt + aaGiftoftheKeepers = 1366, //from dbstr_us.txt + aaValoroftheKeepers = 1367, //from dbstr_us.txt + aaEmbraceoftheKeepers = 1368, //from dbstr_us.txt + aaPoweroftheKeepers = 1369, //from dbstr_us.txt + aaSanctityoftheKeepers = 1370, //from dbstr_us.txt + + //Veteran AAs + aaLessonoftheDevoted = 1371, //from dbstr_us.txt + aaInfusionoftheFaithful = 1372, //from dbstr_us.txt + aaChaoticJester = 1373, //from dbstr_us.txt + aaExpedientRecovery = 1374, //from dbstr_us.txt + aaSteadfastServant = 1375, //from dbstr_us.txt + aaStaunchRecovery = 1376, //from dbstr_us.txt + aaIntensityoftheResolute = 1377, //from dbstr_us.txt + + //Depths of Darkhollow + + //the following 5 look to be used as flags for completion of the Blood Raids for access to the Demiplane of Blood + //quest info here: http://everquest.allakhazam.com/db/quest.html?quest=3582 + //"You must also complete the five Blood Raids in any order: The Council of Nine, Emperor Draygun, Bloodeye, Matriarch Shyra, Sendaii, the Hive Queen" + //"The AA's you receive are: Curse of Blood (1/5), Affliction of Blood (2/5), Torment of Blood (3/5), Temptation of Blood (4/5), Invitation of Blood (5/5)." + aaCurseofBlood = 1378, //from dbstr_us.txt + aaAfflictionofBlood = 1379, //from dbstr_us.txt + aaTormentofBlood = 1380, //from dbstr_us.txt + aaTemptationofBlood = 1381, //from dbstr_us.txt + aaInvitationofBlood = 1382, //from dbstr_us.txt + + aaTurnUndead2 = 1383, //from dbstr_us.txt, Class AA changed in DoD + aaWrackUndead = 1386, //from dbstr_us.txt, PoP Class AA changed in DoD + aaEradicateUndead = 1387, //from dbstr_us.txt + aaInnateSeeInvis = 1388, //from dbstr_us.txt + aaProlongedMortality = 1389, //from dbstr_us.txt + aaPrecognition = 1394, //from dbstr_us.txt + aaThickSkin = 1399, //from dbstr_us.txt + aaSilentCasting = 1404, //from dbstr_us.txt + aaSilentCasting2 = 1409, //from dbstr_us.txt + aaHastenedMindCrash = 1414, //from dbstr_us.txt + aaFieldDressing = 1417, //from dbstr_us.txt + aaBandageWounds = 1420, //from dbstr_us.txt + aaCascadingRage = 1425, //from dbstr_us.txt + aaElementalFerocity = 1430, //from dbstr_us.txt + aaGiftofMana = 1435, //from dbstr_us.txt + aaRuneofShadows = 1440, //from dbstr_us.txt + aaChannelingMastery = 1445, //from dbstr_us.txt + aaConservation = 1453, //from dbstr_us.txt + aaCryofBattle = 1458, //from dbstr_us.txt + aaWardofPurity = 1459, //from dbstr_us.txt + aaTurnSummoned2 = 1462, //from dbstr_us.txt + aaWrackSummoned = 1465, //from dbstr_us.txt + aaEradicateSummoned = 1466, //from dbstr_us.txt + aaWardersSavagery = 1467, //from dbstr_us.txt + aaShackleofSpirits = 1470, //from dbstr_us.txt + aaHastenedThunder = 1471, //from dbstr_us.txt + aaTranslocationalAnchor = 1474, //from dbstr_us.txt + aaStealthyGetaway = 1477, //from dbstr_us.txt + aaPyromancy = 1478, //from dbstr_us.txt + aaMasteryofFury = 1483, //from dbstr_us.txt + aaAbundantHealing = 1486, //from dbstr_us.txt + aaGreaterAvatar = 1491, //from dbstr_us.txt + aaSharedCamouflage = 1494, //from dbstr_us.txt + aaConvergenceofSpirits = 1495, //from dbstr_us.txt + aaNaturesGuardian = 1498, //from dbstr_us.txt + aaEdictofCommand = 1501, //from dbstr_us.txt + aaExtendedBurnout = 1504, //from dbstr_us.txt + aaGuardianofRo = 1507, //from dbstr_us.txt + aaBloodMagic = 1510, //from dbstr_us.txt + aaGraverobbing = 1511, //from dbstr_us.txt + aaAfflictionMastery = 1514, //from dbstr_us.txt + aaGreaterRabidBear = 1517, //from dbstr_us.txt + aaAncestralGuard = 1520, //from dbstr_us.txt + aaCloakofLight = 1523, //from dbstr_us.txt + aaVanquishUndead = 1524, //from dbstr_us.txt + aaCloakofShadows = 1527, //from dbstr_us.txt + aaWillfulDeath = 1528, //from dbstr_us.txt + aaSwiftBlade = 1533, //from dbstr_us.txt + aaWickedBlade = 1536, //from dbstr_us.txt + aaForcedOpening = 1539, //from dbstr_us.txt + aaAppraisal = 1542, //from dbstr_us.txt + aaPreciseStrikes = 1543, //from dbstr_us.txt + aaHastenedDeath = 1546, //from dbstr_us.txt + aaUnflinchingResolve = 1549, //from dbstr_us.txt + aaWeightlessSteps = 1552, //from dbstr_us.txt + aaHastenedBlades = 1555, //from dbstr_us.txt + aaImprovedHarmoniousAttack = 1563, //from dbstr_us.txt + aaImprovedBestialFrenzy = 1566, //from dbstr_us.txt + aaSongofStone = 1569, //from dbstr_us.txt + aaDeepSleep = 1572, //from dbstr_us.txt + aaCompanionsGift = 1577, //from dbstr_us.txt + aaHastenedDefiance = 1583, //from dbstr_us.txt + aaDauntlessPerseverance = 1586, //from dbstr_us.txt + aaConcentration = 1587, //from dbstr_us.txt + aaEnhancedAggression = 1592, //from dbstr_us.txt + aaCallofChallenge = 1597, //from dbstr_us.txt + aaCacophony = 1598, //from dbstr_us.txt + aaImprovedHeadshot = 1601, //from dbstr_us.txt + aaAnatomy = 1604, //from dbstr_us.txt + aaFetterofSpirits = 1607, //from dbstr_us.txt + aaTrickShot = 1608, //from dbstr_us.txt + aaLightningStrikes = 1616, //from dbstr_us.txt + aaRelentlessAssault = 1621, //from dbstr_us.txt + aaKnightsExpertise = 1624, //from dbstr_us.txt + aaSelosEnduringCadence = 1627, //from dbstr_us.txt + aaHarmTouch = 7800, //from dbstr_us.txt + aaLayonHands = 7850, //from dbstr_us.txt + aaLayonHandsRank16 = 7866, + + aaHighestID //this should always be last, and should always + //follow the highest AA ID +} aaID; +*/ + + +typedef enum { //AA IDs + aaNone =0, + aaInnateStrength =2,//implemented as bonus + aaInnateStamina =7,//implemented as bonus + aaInnateAgility =12,//implemented as bonus + /*aaCompleteHeal =13,*///not implemented, but is in dbstr_us.txt + aaInnateDexterity =17,//implemented as bonus + aaInnateIntelligence =22,//implemented as bonus + aaInnateWisdom =27,//implemented as bonus + aaInnateCharisma =32,//implemented as bonus + aaInnateFireProtection =37,//implemented as bonus + aaInnateColdProtection =42,//implemented as bonus + aaInnateMagicProtection =47,//implemented as bonus + aaInnatePoisonProtection =52,//implemented as bonus + aaInnateDiseaseProtection =57,//implemented as bonus + aaInnateRunSpeed =62,//implemented as bonus + aaInnateRegeneration =65,//implemented as bonus + aaInnateMetabolism =68, + aaInnateLungCapacity =71,//handled by client + aaFirstAid =74,//implemented as bonus + aaHealingAdept =77,//implemented as bonus-focus + aaHealingGift =80,//implemented as bonus + aaSpellCastingMastery =83,//untested + aaSpellCastingReinforcement =86,//untested + aaMentalClarity =89,//implemented as bonus + aaSpellCastingFury =92,//implemented as bonus + aaChanellingFocus =95,//implemented as bonus *Live AA effect removed in 2006 + aaSpellCastingSubtlety =98,//untested + aaSpellCastingExpertise =101,//untested + aaSpellCastingDeftness =104,//implemented as bonus-focus + aaNaturalDurability =107,//implemented as bonus + aaNaturalHealing =110,//implemented as bonus + aaCombatFury =113,//implemented as bonus + aaFearResistance =116,//untested + aaFinishingBlow =119,//untested + aaCombatStability =122,//implemented as bonus + aaCombatAgility =125,//implemented as bonus + aaMassGroupBuff =128,//untested + aaDivineResurrection =129,//DB + aaInnateInvisToUndead =130,//DB + aaCelestialRegeneration =131,//untested + aaBestowDivineAura =132,//DB + aaTurnUndead =133,//DB + aaPurifySoul =136,//DB + aaQuickEvacuation =137,//implemented as bonus-focus + aaExodus =140,//untested + aaQuickDamage =141,//implemented as bonus-focus + aaEnhancedRoot =144,//implemented as bonus + aaDireCharm =145,//untested + aaCannibalization =146,//DB + aaQuickBuff =147,//implemented as bonus-focus + aaAlchemyMastery =150, + aaRabidBear =153,//DB + aaManaBurn =154,//DB + aaImprovedFamiliar =155,//untested, implemented? + aaNexusGate =156,//DB + aaUnknown54 =157, + aaPermanentIllusion =158, + aaJewelCraftMastery =159, + aaGatherMana =162,//DB + aaMendCompanion =163,//DB + aaQuickSummoning =164,//implemented as bonus-focus + aaFrenziedBurnout =167,//DB + aaElementalFormFire =168,//DB + aaElementalFormWater =171,//DB + aaElementalFormEarth =174,//DB + aaElementalFormAir =177,//DB + aaImprovedReclaimEnergy =180,//untested + aaTurnSummoned =181,//DB + aaElementalPact =182,//DB + aaLifeBurn =183,//DB + aaDeadMesmerization =184,//DB + aaFearstorm =185,//DB + aaFleshToBone =186,//DB + aaCallToCorpse =187,//DB + aaDivineStun =188,//DB + aaImprovedLayOnHands =189, + aaSlayUndead =190,//implemented as bonus + aaActOfValor =193,//DB + aaHolySteed =194,//DB + aaFearless =195, + aa2HandBash =196,//works. handled by client? + aaInnateCamouflage =197,//DB + aaAmbidexterity =198,//implemented as bonus + aaArcheryMastery =199,//implemented as bonus + aaFletchingMastery =202,//removed from db? + aaEndlessQuiver =205,//implemented as bonus + aaUnholySteed =206,//DB + aaImprovedHarmTouch =207,//untested + aaLeechTouch =208,//DB + aaDeathPeace =209, + aaSoulAbrasion =210,//implemented as bonus-focus + aaInstrumentMastery =213,//untested + aaUnknown91 =216,//not used + aaUnknown92 =219,//not used + aaUnknown93 =222,//not used + aaJamFest =225,//implemented as bonus + aaUnknown95 =228, + aaSonicCall =229, + aaCriticalMend =230,//untested + aaPurifyBody =233,//DB + aaChainCombo =234, + aaRapidFeign =237,//works + aaReturnKick =240,//implemented as bonus + aaEscape =243,//DB + aaPoisonMastery =244, + aaDoubleRiposte =247,//implemented as bonus + aaQuickHide =250, + aaQuickThrow =253,//corrected from dbstr_us.txt + aaPurgePoison =254,//DB + aaFlurry =255,//implemented as bonus + aaRampage =258,//untested + aaAreaTaunt =259,//untested + aaWarcry =260,//DB + aaBandageWound =263,//implemented as bonus + aaSpellCastingReinforcementMastery =266,//untested + aaSpellCastingFuryMastery =267,//untested + aaExtendedNotes =270,//implemented as bonus + aaDragonPunch =273,//implemented as bonus + aaStrongRoot =274,//DB + aaSingingMastery =275,//untested + aaBodyAndMindRejuvenation =278,//added + aaPhysicalEnhancement =279,//implemented as bonus + aaAdvTrapNegotiation =280,//untested + aaAcrobatics =283,//untested + aaScribbleNotes =286, + aaChaoticStab =287,//implemented as bonus + aaPetDiscipline =288,//added + aaHobbleofSpirits =289,//DB + aaFrenzyofSpirit =290,//DB + aaParagonofSpirit =291,//DB + aaAdvancedInnateStrength =292,//implemented as bonus + aaAdvancedInnateStamina =302,//implemented as bonus + aaAdvancedInnateAgility =312,//implemented as bonus + aaAdvancedInnateDexterity =322,//implemented as bonus + aaAdvancedInnateIntelligence =332,//implemented as bonus + aaAdvancedInnateWisdom =342,//implemented as bonus + aaAdvancedInnateCharisma =352,//implemented as bonus + aaWardingofSolusek =362,//implemented as bonus + aaBlessingofEci =372,//implemented as bonus + aaMarrsProtection =382,//implemented as bonus + aaShroudofTheFaceless =392,//implemented as bonus + aaBertoxxulousGift =402,//implemented as bonus + aaNewTanaanCraftingMastery =412, + aaPlanarPower =418,//untested + aaPlanarDurability =423,//added + aaInnateEnlightenment =426,//added + aaAdvancedSpellCastingMastery =431,//untested + aaAdvancedHealingAdept =434,//untested + aaAdvancedHealingGift =437,//untested + aaCoupdeGrace =440,//added + aaFuryoftheAges =443,//implemented as bonus + aaMasteryofthePast =446,//implemented as bonus + aaLightningReflexes =449,//implemented as bonus + aaInnateDefense =454,//implemented as bonus + aaRadiantCure =459,//DB + aaHastenedDivinity =462,//DB + aaHastenedTurning =465,//DB + aaHastenedPurificationofSoul =468,//DB + aaHastenedGathering =471,//DB + aaHastenedRabidity =474,//DB + aaHastenedExodus =477,//DB + aaHastenedRoot =480,//DB + aaHastenedMending =483,//DB + aaHastenedBanishment =486,//DB + aaHastenedInstigation =489,//DB, maybe + aaFuriousRampage =492,//DB + aaHastenedPurificationoftheBody =495,//DB + aaHastyExit =498,//DB + aaHastenedPurification =501,//DB + aaFlashofSteel =504,//implemented as bonus + aaDivineArbitration =507,//DB + aaWrathoftheWild =510,//DB + aaVirulentParalysis =513,//DB + aaHarvestofDruzzil =516,//DB + aaEldritchRune =517,//DB + aaServantofRo =520,//DB + aaWaketheDead =523,//DB + aaSuspendedMinion =526,//untested + aaSpiritCall =528,//DB + aaCelestialRenewal =531,//DB + aaAllegiantFamiliar =533, + aaHandofPiety =534,//DB + aaMithanielsBinding =537,//implemented as bonus + aaMendingoftheTranquil =539, + aaRagingFlurry =542,//implemented as bonus + aaGuardianoftheForest =545,//DB + aaSpiritoftheWood =548,//DB + aaBestialFrenzy =551,//implemented as bonus + aaHarmoniousAttack =556,//implemented as bonus + aaKnightsAdvantage =561,//implemented as bonus + aaFerocity =564,//implemented as bonus + aaViscidRoots =567, + aaSionachiesCrescendo =568,//implemented as bonus + aaAyonaesTutelage =571, + aaFeignedMinion =574, + aaUnfailingDivinity =577, + aaAnimationEmpathy =580,// Implemented + aaRushtoJudgement =583, + aaLivingShield =586, + aaConsumptionoftheSoul =589,//untested + aaBoastfulBellow =592,//DB + aaFervrentBlessing =593,//untested + aaTouchoftheWicked =596,//untested + aaPunishingBlade =599,//implemented as bonus + aaSpeedoftheKnight =602,//implemented as bonus + aaShroudofStealth =605, + aaNimbleEvasion =606, + aaTechniqueofMasterWu =611, + aaHostoftheElements =616,//DB + aaCallofXuzl =619,//DB + aaHastenedStealth =622, + aaIngenuity =625, + aaFleetofFoot =628,//implemented as bonus + aaFadingMemories =630, + aaTacticalMastery =631,//implemented as bonus + aaTheftofLife =634,//implemented as bonus-focus + aaFuryofMagic =637, + aaFuryofMagicMastery2 =640,//whats the difference? + aaProjectIllusion =643, + aaHeadshot =644,//added + aaEntrap =645,//DB + aaUnholyTouch =646,//untested + aaTotalDomination =649,// Implemented + aaStalwartEndurance =652,//implemented as bonus + aaQuickSummoning2 =655,//*not implemented - Liva AA that replaces prior version in later exp. + aaMentalClarity2 =658,//whats the difference? + aaInnateRegeneration2 =661,//whats the difference? + aaManaBurn2 =664,//whats the difference? + aaExtendedNotes2 =665,//*not implemented - Live AA that replaces prior version in later exp. + aaSionachiesCrescendo2 =668,//*not implemented - Live AA that replaces prior version in later exp. + aaImprovedReclaimEnergy2 =671,//whats the difference? untetsed + aaSwiftJourney =672,//implemented as bonus + aaConvalescence =674,//added 9/26/08 + aaLastingBreath =676,//handled by client + aaPackrat =678,//added 9/29/08 + aaHeightenedEndurance =683, + aaWeaponAffinity =686,//implemented as bonus + aaSecondaryForte =691, + aaPersistantCasting =692, + aaTuneofPursuance =695, + aaImprovedInstrumentMastery =700, + aaImprovedSingingMastery =701, + aaExultantBellowing =702, + aaEchoofTaelosia =707, + aaInternalMetronome =710,//implemented as bonus *Live AA removed in 2006 + aaPiousSupplication =715, + aaBeastialAlignment =718,//untested + aaWrathofXuzl =721, + aaFeralSwipe =723,//DB? + aaWardersFury =724,//implemented as bonus + aaWardersAlacrity =729,//implemented as bonus + aaPetAffinity =734,//implemented as bonus + aaMasteryofthePast2 =735,//implemented as bonus [Different classes] + aaSpellCastingSubtlety2 =738,//whats the difference? + aaTouchoftheDivine =741, + aaDivineAvatar =746,//DB + aaExquisiteBenediction =749,//DB + aaQuickenedCuring =754, + aaNaturesBoon =757,//DB + aaAdvancedTracking =762, + aaCriticalAffliction =767, + aaFuryofMagicMastery =770,//whats the difference? + aaDoppelganger =773, + aaEnchancedForgetfulness =776, + aaMesmerizationMastery =781, + aaQuickMassGroupBuff =782, + aaSharedHealth =785, + aaElementalFury =790,//implemented as bonus + aaElementalAlacrity =795,//implemented as bonus + aaElementalAgility =800,//implemented as bonus + aaElementalDurability =803,//implemented as bonus + aaSinisterStrikes =806,//implemented as bonus + aaStrikethrough =807,//implemented as bonus + aaStonewall =810, + aaRapidStrikes =815,//implemented as bonus + aaKickMastery =820,//implemented as bonus + aaHightenedAwareness =823, + aaDestructiveForce =828,//DB + aaSwarmofDecay =831,//DB + aaDeathsFury =834, + aaQuickeningofDeath =839,//implemented as bonus + aaAdvancedTheftofLife =844,//implemented as bonus-focus + aaTripleBackstab =846,//implemented as bonus + aaHastenedPiety =849, + aaImmobilizingBash =852, + aaViciousSmash =855,//implemented as bonus + aaRadiantCure2 =860,//whats the difference? + aaPurification =863, + aaPrecisionofthePathfinder =864,//implemented as bonus + aaCoatofThistles =867, + aaFlamingArrows =872,//untested + aaFrostArrows =875,//untested + aaSeizedOpportunity =878, + aaTrapCircumvention =881, + aaImprovedHastyExit =886, + aaVirulentVenom =888, + aaImprovedConsumptionofSoul =893, + aaIntenseHatred =895, + aaAdvancedSpiritCall =900, + aaCalloftheAncients =902,//DB + aaSturdiness =907, + aaWarlordsTenacity =912,//implemented as effect + aaStrengthenedStrike =915,//implemented as bonus + aaExtendedShielding =918, + aaRosFlamingFamiliar =921,//DB + aaEcisIcyFamiliar =922,//DB + aaDruzzilsMysticalFamiliar =923,//DB + aaAdvancedFuryofMagicMastery =924,//added 9/29/08 + aaWardofDestruction =926,//DB + aaFrenziedDevastation =931,//DB + aaCombatFury2 =934,//implemented as bonus + aaCombatFury3 =937,//implemented as bonus + aaCombatFury4 =940,//implemented as bonus + aaFuryoftheAges2 =943,//implemented as bonus + aaFuryoftheAges3 =946,//implemented as bonus + aaFuryoftheAges4 =949,//implemented as bonus + aaPlanarDurability2 =952,//whats the difference? + aaInnateEnlightenment2 =955,//whats the difference? + aaDireCharm2 =960,//whats the difference? + aaDireCharm3 =961,//whats the difference? + aaTouchoftheDivine2 =962,//whats the difference? + aaTouchofDecay =967, + aaCalloftheAncients2 =970,//whats the difference? + aaImprovedVision =975, + aaEternalBreath =978,//handled by client + aaBlacksmithingMastery =979,//added 9/29/08 + aaBakingMastery =982,//added 9/29/08 + aaBrewingMastery =985,//added 9/29/08 + aaFletchingMastery2 =988,//added 9/29/08 + aaPotteryMastery =991,//added 9/29/08 + aaTailoringMastery =994,//added 9/29/08 + aaSalvage =997, + aaOrigin =1000,//spell + aaChaoticPotential =1001,//added + aaDiscordantDefiance =1006,//implemented as bonus + aaTrialsofMataMuram =1011, + aaMysticalAttuning =1021, + aaDelayDeath =1026,//implemented as bonus + aaHealthyAura =1031, + aaFitness =1036,//implemented as bonus + aaVeteransWrath =1041,//implemented as bonus [Different classes/values on each Veteran's Wrath] + aaVeteransWrath2 =1044,//implemented as bonus + aaVeteransWrath3 =1047,//implemented as bonus + aaVeteransWrath4 =1050,//implemented as bonus + aaDeathblow =1053,//implemented as bonus + aaReflexiveMastery =1061,//implemented as bonus + aaDefensiveInstincts =1066,//implemented as bonus + aaMnemonicRetention =1071,//Implemented + aaExpansiveMind =1072,//added 9/29/08 + aaSleightofHand =1077, + aaSleightofHand2 =1080,//whats the difference? + aaHealingAdeptMastery =1083, + aaHealingGiftMastery =1086, + aaArcaneTongues =1089, + aaMasterofDisguise =1092, + aaSlipperyAttacks =1093, + aaImprovedCriticalAffliction =1099, + aaFortifiedBellowing =1102, + aaFuryofMagic2 =1107,//whats the difference? + aaDanceofBlades =1110, + aaShieldofNotes =1116, + aaRoarofThunder =1119, + aaPersistentMinion =1122, + aaPerfectionofSpirit =1123, + aaReplentishCompanion =1126, + aaAdvancedPetDiscipline =1129, + aaThrowingMastery =1131,//implemented as bonus + aaBlurofAxes =1134,//implemented as bonus + aaHastenedWarCry =1137, + aaDeadAim =1140,//implemented as bonus + aaFrenziedDefense =1143,//*not implemented - duplicate + aaTirelessSprint =1146, + aaDesperation =1149, + aaUntamedRage =1150, + aaEchoingCries =1155, + aaViciousFrenzy =1158, + aaCrazedOnslaught =1163, + aaFrenziedDefense2 =1166,//implemented as bonus + aaOverwhelmingAttack =1172, + aaFuriousRage =1175, + aaBloodPact =1178, + aaShieldingResistance =1181,////implemented as bonus + aaHealingBoon =1186, + aaResplendentCure =1189, + aaCelestialHammer =1192, + aaDivineRetribution =1195, + aaCelestialRejuvination =1203, + aaFerventBenediction =1206, + aaSanctuary =1209, + aaDestructiveFury =1210,//implemented as bonus - added 9/29/08 + aaDestructiveFury2 =1213,//*not implemented [classes changed] - replaces id 1210 + aaBoonoftheForest =1222, + aaSpiritoftheGrove =1225, + aaCalloftheWild =1228, + aaSecondaryRecall =1229, + aaNaturesBounty =1230, + aaStasis =1233, + aaColorShock =1239, + aaMindOverMatter =1242, + aaSoothingWords =1245, + aaElementalSwarm =1248, + aaHeartofFlames =1251, + aaHeartofVapor =1252, + aaHeartofIce =1253, + aaHeartofStone =1254, + aaImitateDeath =1255, + aaCripplingStrike =1256, + aaStunningKick =1259, + aaEyeGouge =1262, + aaIronKicks =1265, + aaStyleoftheMimic =1268, + aaDeathPeace2 =1272,//whats the difference? + aaArmyoftheDead =1274, + aaCelestialStun =1277, + aaHandofDevotion =1278, + aaSteadfastWill =1284, + aaShieldBlock =1287,//implemented as bonus + aaScoutsEfficiency =1290,//implemented as bonus + aaGuardianoftheGlade =1293, + aaTrackingMastery =1296, + aaFlurryofKnives =1301,//implemented as bonus + aaPrecision =1304, + aaNervesofSteel =1307, + aaTouchoftheCursed =1313, + aaSpiritualCorrosion =1316, + aaSoulThief =1319,//implemented as bonus-focus + aaSpiritualChanneling =1323,//implemented as effect + aaBoonoftheAncients =1324, + aaAncestralAid =1327,//implemented as effect + aaResoluteDefiance =1330,//implemented as effect + aaPresstheAttack =1333, + aaMindCrash =1334, + aaProlongedDestruction =1337, + aaRosGreaterFamiliar =1340, + aaEcisGreaterFamiliar =1341, + aaDruzzilsGreaterFamiliar =1342, + aaTeleportBind =1343, + aaDevotedFamiliar =1344, + aaAuspiceoftheHunter =1345, + aaSavageSpirit =1348, + aaPresstheAttack2 =1351,//whats the difference? + aaCripplingStrike2 =1352,//whats the difference? + aaStunningKick2 =1353,//whats the difference? + aaEyeGouge2 =1358,//whats the difference? + + + aaGiftoftheDarkReign =1361,//from dbstr_us.txt + aaTenacityoftheDarkReign =1362,//from dbstr_us.txt + aaEmbraceoftheDarkReign =1363,//from dbstr_us.txt + aaPoweroftheDarkReign =1364,//from dbstr_us.txt + aaFervoroftheDarkReign =1365,//from dbstr_us.txt + aaGiftoftheKeepers =1366,//from dbstr_us.txt + aaValoroftheKeepers =1367,//from dbstr_us.txt + aaEmbraceoftheKeepers =1368,//from dbstr_us.txt + aaPoweroftheKeepers =1369,//from dbstr_us.txt + aaSanctityoftheKeepers =1370,//from dbstr_us.txt + + + aaLessonoftheDevoted =1371,//from dbstr_us.txt + aaInfusionoftheFaithful =1372,//from dbstr_us.txt + aaChaoticJester =1373,//from dbstr_us.txt + aaExpedientRecovery =1374,//from dbstr_us.txt + aaSteadfastServant =1375,//from dbstr_us.txt + aaStaunchRecovery =1376,//from dbstr_us.txt + aaIntensityoftheResolute =1377,//from dbstr_us.txt + + + aaCurseofBlood =1378,//from dbstr_us.txt + aaAfflictionofBlood =1379,//from dbstr_us.txt + aaTormentofBlood =1380,//from dbstr_us.txt + aaTemptationofBlood =1381,//from dbstr_us.txt + aaInvitationofBlood =1382,//from dbstr_us.txt + + aaTurnUndead2 =1383,//from dbstr_us.txt, Class AA changed in DoD + aaWrackUndead =1386,//from dbstr_us.txt, PoP Class AA changed in DoD + aaEradicateUndead =1387,//from dbstr_us.txt + aaInnateSeeInvis =1388,//implemented as bonus + aaProlongedMortality =1389,//from dbstr_us.txt + aaPrecognition =1394,//implemented as bonus + aaThickSkin =1399,//implemented as bonus + aaSilentCasting =1404,//from dbstr_us.txt + aaSilentCasting2 =1409,//from dbstr_us.txt + aaHastenedMindCrash =1414,//from dbstr_us.txt + aaFieldDressing =1417,//implemented as bonus + aaBandageWounds =1420,//implemented as bonus + aaCascadingRage =1425,//from dbstr_us.txt + aaElementalFerocity =1430,//from dbstr_us.txt + aaGiftofMana =1435,//implemented as bonus + aaRuneofShadows =1440,//from dbstr_us.txt + aaChannelingMastery =1445,//from dbstr_us.txt + aaConservation =1453,//from dbstr_us.txt + aaCryofBattle =1458,//from dbstr_us.txt + aaWardofPurity =1459,//from dbstr_us.txt + aaTurnSummoned2 =1462,//from dbstr_us.txt + aaWrackSummoned =1465,//from dbstr_us.txt + aaEradicateSummoned =1466,//from dbstr_us.txt + aaWardersSavagery =1467,//from dbstr_us.txt + aaShackleofSpirits =1470,//from dbstr_us.txt + aaHastenedThunder =1471,//from dbstr_us.txt + aaTranslocationalAnchor =1474,//from dbstr_us.txt + aaStealthyGetaway =1477,//from dbstr_us.txt + aaPyromancy =1478,//from dbstr_us.txt + aaMasteryofFury =1483,//from dbstr_us.txt + aaAbundantHealing =1486,//from dbstr_us.txt + aaGreaterAvatar =1491,//from dbstr_us.txt + aaSharedCamouflage =1494,//from dbstr_us.txt + aaConvergenceofSpirits =1495,//from dbstr_us.txt + aaNaturesGuardian =1498,//from dbstr_us.txt + aaEdictofCommand =1501,//from dbstr_us.txt + aaExtendedBurnout =1504,//from dbstr_us.txt + aaGuardianofRo =1507,//from dbstr_us.txt + aaBloodMagic =1510,//from dbstr_us.txt + aaGraverobbing =1511,//from dbstr_us.txt + aaAfflictionMastery =1514,//from dbstr_us.txt + aaGreaterRabidBear =1517,//from dbstr_us.txt + aaAncestralGuard =1520,//from dbstr_us.txt + aaCloakofLight =1523,//from dbstr_us.txt + aaVanquishUndead =1524,//from dbstr_us.txt + aaCloakofShadows =1527,//from dbstr_us.txt + aaWillfulDeath =1528,//from dbstr_us.txt + aaSwiftBlade =1533,//implemented as bonus + aaWickedBlade =1536,//implemented as bonus + aaForcedOpening =1539,//from dbstr_us.txt + aaAppraisal =1542,//from dbstr_us.txt + aaPreciseStrikes =1543,//from dbstr_us.txt + aaHastenedDeath =1546,//from dbstr_us.txt + aaUnflinchingResolve =1549,//from dbstr_us.txt + aaWeightlessSteps =1552,//from dbstr_us.txt + aaHastenedBlades =1555,//from dbstr_us.txt + aaImprovedHarmoniousAttack =1563,//from dbstr_us.txt + aaImprovedBestialFrenzy =1566,//from dbstr_us.txt + aaSongofStone =1569,//from dbstr_us.txt + aaDeepSleep =1572,//from dbstr_us.txt + aaCompanionsGift =1577,//from dbstr_us.txt + aaHastenedDefiance =1583,//implemented as redux + aaDauntlessPerseverance =1586,//implemented as bonus + aaConcentration =1587,//implemented as bonus + aaEnhancedAggression =1592,//from dbstr_us.txt + aaCallofChallenge =1597,//implemented as effect + aaCacophony =1598,//from dbstr_us.txt + aaImprovedHeadshot =1601,//from dbstr_us.txt + aaAnatomy =1604,//from dbstr_us.txt + aaFetterofSpirits =1607,//from dbstr_us.txt + aaTrickShot =1608,//from dbstr_us.txt + aaLightningStrikes =1616,//implemented as bonus + aaRelentlessAssault =1621,//implemented as bonus + aaKnightsExpertise =1624,//implemented as bonus + aaSelosEnduringCadence =1627,//implemented as bonus + + + aaThroneofHeroes =4655, + aaTinkeringMastery =4672, + aaCombatMedic =4688,//implemented as bonus + aaQuickDraw =4698, + aaBattleReady =4699, + aaGlyphofDragonScales =4702,//implemented as effect + aaGlyphofArcaneSecrets =4704,//implemented as effect + aaGlyphofDraconicPotential =4705,//implemented as effect + aaGlyphofDestruction =4706,//implemented as effect + aaKillingSpree =4739,//implemented as effect + aaHoldtheLine =4742, + aaBloodTithe =4761, + aaSleightofHand3 =4767, + aaGiftofRadiantMana =4773, + aaSavageAssault =4795,//Flurry + aaOverbear =4798, + aaHastenedMend =4801, + aaPrecisionofAxes =4809, + aaDeathMask =4819, + aaGrapplingStrike =4836, + aaShieldSpecialist =4844, + aaMarkoftheMageHunter =4849, + aaHamstring =4850, + aaUncannyResilience =4854, + + aaBlindingFury =4857, + aaBattleLeap =4860, + aaSoulSeeker =4861, + aaHolyLight =4880, + aaSurreality =4887, + aaManaDraw =4890, + aaNightmareStasis =4894, + aaFireCore =4903, + aaVaporCore =4906, + aaIceCore =4909, + aaStoneCore =4912, + aaVolatileManaBlaze =4915, + aaGreaterBloodTithe =4924, + aaGatheringDusk =4927, + aaVeilofMindshadow =4931, + aaSanguineMindCrystal =4934, + aaAzureMindCrystal =4935, + aaArcaneWhisper =4938, + aaDimensionalInstability =4943, + aaCryomancy =4944, + aaSurvivalist =5000, + aaProtectionoftheSpiritWolf =5007, + aaTasteofBlood =5015, + aaHymnoftheLastStand =5017, + aaBladedSong =5020, + aaTwistedShank =5021, + aaDirtyFighting =5022, + aaLigamentSlice =5025, + aaTumble =5028, + aaSiphonSoul =5032, + aaUnflinchingWill =5035, + aaPetrifiedRoots =5061, + aaHastenedSanctuary =5083, + aaMortalCoil =5085, + aaArmoroftheInquisitor =5095, + aaHandofDisruption =5098, + aaSpiritoftheWhiteWolf =5105, + aaPactoftheWolf =5109, + aaDoppelgangersBeckon =5127, + aaBreathofAtathus =5150, + aaBreathofDratonra =5165, + aaBreathofOshvir =5180, + aaBreathofVenesh =5195, + aaBreathofMysaphar =5210, + aaBreathofKeikolin =5225, + aaHuntersFury =5248, + aaUnionofSpirits =5251, + aaShieldBlock2 =5263, + aaDeathsWrath =5264, + aaSummonersBeckon =5269, + aaShiftingElements =5276, + aaArcaneOverkill =5295, + aaFuneralDirge =5298, + aaFierceEye =5717, + aaPreciseBlow =5776, + aaForceofDisruption =5984, + aaPunchMastery =6020, + aaHastenedDestructiveForce =6023, + aaCompanionsDurability =6051, + aaGeneralSturdiness =6119, + aaRapidDefiance =6136, + aaPactoftheWurine =6218, + aaProtectionofDirewood =6232, + aaEnhancedDamageShield =6257, + aaArcomancy =6290, + aaBlessingofResurrection =6299, + aaRakesDeadlyAim =6322, + aaRoguesFury =6325, + aaEnvenomedBlades =6328, + aaCompanionofNecessity =6333, + aaRakesPowerfulAim =6334, + aaHastenedCacophony =6337, + aaHastenedFuneralDirge =6340, + aaMastersHastenedCombination =6343, + aaHastenedSilentCasting =6346, + aaHastenedSilentCasting2 =6349, + aaHastenedTrueshot =6355, + aaOutridersAccuracy =6370, + aaDestructiveCascade =6375, + aaCompanionsRelocation =6379, + aaFocusedParagonofSpirits =6380, + aaCompanionsAgility =6383, + aaMaestrosConcentration =6386, + aaBlessingofLife =6395, + aaPlanarDurability3 =6422, + aaHolyRoot =6436, + aaHastenedRoarofThunder =6442, + aaQuickenedSuspendMinion =6445, + aaQuickenedSummonAxes =6452, + aaCallHither =6455, + aaFortifiedSurvival =6458, + aaFortifiedIntervention =6461, + aaPlanarDurability4 =6467, + aaFuriousLeap =6499, + aaQuickenedHarvestofDruzzil =6503, + aaChatteringBones =6508, + aaWarlordsDeadlyAim =6511, + aaQuickenedCalloftheWild =6514, + aaGiftofExquisiteRadiantMana =6517, + aaHastenedOrigin =6528, + aaDirgeoftheSleepwalker =6533, + aaQuickTime =6534, + aaSteadyHands =6535, + aaSelosSonata =6536, + aaCompanionsBlessing =6537, + aaHastenedBestialAlignment =6538, + aaFortifyCompanion =6539, + aaBurstofPower =6540, + aaPactoftheWurine2 =6541, + aaRecklessAbandon =6542, + aaGiftofResurrection =6543, + aaTouchoftheDivine3 =6544, + aaHastenedCalloftheWild =6545, + aaHuntersAttackPower =6546, + aaHastenedExodus2 =6547, + aaClingingRoot =6548, + aaVielofInvisibility =6549, + aaCriticalAffliction2 =6550, + aaLessenedPresence =6551, + aaManaOverburn =6552, + aaHostoftheElements2 =6553, + aaEnhancedDamageShield2 =6554, + aaHastenedPurifyBody =6555, + aaWrathofLife =6556, + aaKnightsReturnStrike =6557, + aaHuntersReturnKick =6558, + aaHastenedLigamentSlice =6559, + aaKnavesReturnStrike =6560, + aaStormStrike =6561, + aaSilentPresence =6562, + aaTurgursSwarm =6563, + aaInfusedbyRage =6564, + aaGutPunch =6565, + aaWarlordsReturnKick =6566, + aaArcomancy2 =6567, + aaCallofXuzl2 =6568, + aaBETAONLYDestructiveForceTest =6600, + aaRecourseofLife =6601, + aaVehementRage =6607, + aaKneeStrike =6610, + aaHastenedFortitudeDiscipline =6611, + aaHastenedFuriousDiscipline =6614, + aaWarlordsBravery =6617, + aaDeathsRevenge =6630, + aaHarmshield =6635, + aaDestructiveFury3 =6636, + aaVoiceofThule =6639, + aaExplosionofHatred =6640, + aaCascadingTheftofLife =6641, + aaHateStep =6644, + aaViciousBiteofChaos =6645, + aaEncroachingDarkness =6646, + aaSleightofHand4 =6663, + aaEnduringVision =6664, + aaEnduringVision2 =6666, + + aaCunningDisguiseHuman =6671, + aaCunningDisguiseHalfElf =6672, + aaCunningDisguiseBarbarian =6673, + aaCunningDisguiseErudite =6674, + aaCunningDisguiseTroll =6675, + aaCunningDisguiseGoblin =6676, + aaQuickCannibalize =6690, + aaTigirsInsectSwarm =6691, + aaDampenResistance =6692, + aaHastenedDampenResistance =6697, + aaHastenedDampenResistance2 =6698, + aaSpiritWalk =6702, + aaHastenedVirulentParalysis =6703, + aaLanguidBite =6706, + aaQuickenedBloodofNadox =6709, + aaHastenedSpiritCall =6712, + aaHarmoniousArrow =6750, + aaHastenedWeaponShield =6751, + aaOutridersAttack =6754, + aaGroupGuardianoftheForest =6755, + aaPackHunt =6758, + aaKeenBlade =6761, + aaOutridersEvasion =6764, + aaRangedFinesse =6765, + aaGroupCloakofLight =6790, + aaHealingLight =6791, + aaHalttheDead =6794, + aaDirectedForceofDisruption =6795, + aaRiseofBones =6815, + aaWhisperwind =6818, + aaHastenedBloodMagic =6819, + aaOverpowerUndead =6822, + aaHastenedSwarmofDecay =6823, + aaGiftoftheGrave =6828, + aaHastenedSongofStone =6870, + aaStompingLeap =6930, + aaJuggernautSurge =6931, + aaDistractionAttack =6932, + aaHastenedSavageSpirit =6935, + aaDyingBlow =6938, + aaHastenedDistractionAttack =6941, + aaNaturalInvisibility =6970, + aaAttackoftheWarders =6971, + aaHastenedFeralAttacks =6974, + aaHastenedFocusedParagon =6977, + aaHastenedParagon =6980, + aaGroupBestialAlignment =6983, + aaBiteoftheAsp =6984, + aaRavensClaw =6985, + aaGorillaSmash =6986, + aaAuroriaMastery =6987, + aaHastenedGetaway =7005, + aaForaging =7062, + aaPerfectedInvisibility =7069, + aaHastenedDivineAvatar =7100, + aaHastenedPurification2 =7103, + aaBeastlordsFeralKick =7106, + aaGiftofAmazingExquisiteRadiantMana =7621, + aaHastenedWrathoftheWild =7664, + aaGroupShrink =7669, + aaEnchantDwerium =7732, + aaMassEnchantDwerium =7734, + aaEnchantPalladium =7735, + aaEnchantPalladiumTrio =7736, + aaMassEnchantPalladium =7737, + aaGreaterMassEnchantPalladium =7738, + aaEnchantTemporite =7739, + aaMassEnchantTemporite =7740, + + aaLayonHandsRank16 =7866, //Duplicate ID with aaExtendedAstralProjection! + + aaHarmTouch =7800,//First 10 AA cost 0 per rank + aaHarmToch2 =7810,//Next 6+ AA cost 3 +1inc per rank + aaLayonHands =7850,//First 10 AA cost 0 per rank + aaLayonHands2 =7860,//Next 6+ AA cost 3 +1inc per rank + //aaExtendedAstralProjection =7866, + aaNeshikasBlink =7869, + aaZanFisWhistle =7872, + aaFivePointPalm =7875, + aaTheftofEssence =7900, + aaMalosinete =7903, + aaHastenedTurnUndead =7940, + aaCascadingDivineAura =7943, + aaGroupPurifySoul =7944, + aaQuickenedRenewal =7945, + aaSanctifiedBlessing =7948, + aaFocusedCelestialRegeneration =7951, + aaMasteryofNature =7980, + aaHastenedNaturesGuardian =7983, + aaSpiritoftheBlackWolf =7986, + aaHastenedLycanSoul =7989, + aaSelfStasis =8030, + aaHastenedVeilofMindshadow =8031, + aaPhantasmalOpponent =8034, + aaHastenedEdictofCommand =8035, + aaFogofMemories =8038, + aaBiteofTashani =8039, + aaExtendedIngenuity =8040, + aaFuryofDruzzil =8060, + aaFuryofEci =8063, + aaFuryofRo =8066, + aaFortifiedEntanglement =8069, + aaForceofWill =8072, + aaAtolsShackles =8075, + aaHastenedManaburn =8076, + aaHastenedCallofXuzl =8082, + aaCompanionsAlacrity =8190, + aaImprovedIntimidation =8193, + aaPerfectedLevitation =8194, + aaHastenedFortifyCompanion =8195, + aaEmpoweredIngenuity =8198, + aaCompanionsFury =8201, + aaQuickenedRadiantCure =8204, + aaQuickenedRadiantCure2 =8207, + aaMentalStamina =8210, + aaHardyEndurance =8215, + aaGroupPerfectInvisibility =8220, + aaFocusofArcanum =8221, + aaGroupPerfectedInvisibilitytoUndead =8222, + aaSpellCastingReinforcementMastery2 =8223, + aaCascadeofLife =8224, + aaSummonCompanion =8227, + aaMentalFortitude =8228, + aaGate =8231, + aaExtendedIngenuity2 =8232, + aaArmorofWisdom1 =8235, + aaArmorofWisdom2 =8240, + aaArmorofWisdom3 =8245, + aaArmorofWisdom4 =8250, + aaArmorofWisdom5 =8255, + + + aaSpellCastingReinforcementArtistry =8262, + aaEarthenBrawn =8263, + aaEarthenStability =8268, + aaEarthenAlacrity =8273, + aaEarthenArtistry =8278, + aaEarthenSagacity =8283, + aaEarthenBrilliance =8288, + aaEarthenAllure =8293, + aaResplendentGlory =8300, + aaRageofRallosZek =8303, + aaEnhancedAreaTaunt =8312, + aaFluidMarch =8314, + aaHastenedSelosKick =8317, + aaHastenedBellow =8319, + aaBelltoneMind =8322, + aaSubtleBlows =8325, + aaLitheBody =8330, + aaEnhancedThiefsEyes =8331, + aaExtendedLanguidBite =8332, + aaQuickenedMalosinete =8335, + aaDrapeofShadows =8341, + aaHostintheShell =8342, + aaHastenedManaDraw =8347, + aaHastenedMezmerization =8350, + aaDreamlikeMight =8351, + aaDreamlikeFortitude =8361, + aaDreamlikeSwiftness =8371, + aaDreamlikeCunning =8381, + aaDreamlikeSapience =8391, + aaArmorRend =8400, + aaFundamentofIntellect =9100, + aaSongwriting =9101, + aaFundamentofIntellect2 =9109, + aaHybridResearch =9111, + aaFundamentofIntellect3 =9118, + aaWrittenPrayer =9121, + aaFundamentofIntellect4 =9127, + aaFundamentofWisdom =9136, + aaFundamentofWisdom2 =9145, + aaFundamentofWisdom3 =9154, + aaFundamentofPower =9163, + aaFundamentofPower2 =9172, + aaFundamentofPower3 =9181, + aaFundamentofPower4 =9190, + aaFundamentofPower5 =9199, + aaFundamentofCombat =9208, + aaFundamentofCombat2 =9217, + aaFundamentofCombat3 =9226, + aaFundamentofCombat4 =9235, + aaFundamentFirstSpireofArcanum =9300, + aaFundamentSecondSpireofArcanum =9303, + aaFundamentThirdSpireofArcanum =9306, + aaFundamentFirstSpireoftheSensei =9309, + aaFundamentSecondSpireoftheSensei =9312, + aaFundamentThirdSpireoftheSensei =9315, + aaFundamentFirstSpireoftheElements =9318, + aaFundamentSecondSpireoftheElements =9321, + aaFundamentThirdSpireoftheElements =9324, + aaFundamentFirstSpireofEnchantment =9327, + aaFundamentSecondSpireofEnchantment =9330, + aaFundamentThirdSpireofEnchantment =9333, + aaFundamentFirstSpireofNecromancy =9336, + aaFundamentSecondSpireofNecromancy =9339, + aaFundamentThirdSpireofNecromancy =9342, + aaFundamentFirstSpireoftheWarlord =9345, + aaFundamentSecondSpireoftheWarlord =9348, + aaFundamentThirdSpireoftheWarlord =9351, + aaFundamentFirstSpireoftheRake =9354, + aaFundamentSecondSpireoftheRake =9357, + aaFundamentThirdSpireoftheRake =9360, + aaFundamentFirstSpireoftheMinstrels =9363, + aaFundamentSecondSpireoftheMinstrels =9366, + aaFundamentThirdSpireoftheMinstrels =9369, + aaFundamentFirstSpireoftheSavageLord =9372, + aaFundamentSecondSpireoftheSavageLord =9375, + aaFundamentThirdSpireoftheSavageLord =9378, + aaFundamentFirstSpireofHoliness =9381, + aaFundamentSecondSpireofHoliness =9384, + aaFundamentThirdSpireofHoliness =9387, + aaFundamentFirstSpireoftheReavers =9390, + aaFundamentSecondSpireoftheReavers =9393, + aaFundamentThirdSpireoftheReavers =9396, + aaFundamentFirstSpireofthePathfinders =9399, + aaFundamentSecondSpireofthePathfinders =9402, + aaFundamentThirdSpireofthePathfinders =9405, + aaFundamentFirstSpireofDivinity =9408, + aaFundamentSecondSpireofDivinity =9411, + aaFundamentThirdSpireofDivinity =9414, + aaFundamentFirstSpireofNature =9417, + aaFundamentSecondSpireofNature =9420, + aaFundamentThirdSpireofNature =9423, + aaFundamentFirstSpireofAncestors =9426, + aaFundamentSecondSpireofAncestors =9429, + aaFundamentThirdSpireofAncestors =9432, + aaFundamentFirstSpireofSavagery =9435, + aaFundamentSecondSpireofSavagery =9438, + aaFundamentThirdSpireofSavagery =9441, + aaHastenedDirgeoftheSleepwalker =10329, + aaVaingloriousShout =10330, + aaLyreLeap =10331, + aaDominationMastery =10332, + aaLyricalPrankster =10333, + aaSelosKick =10336, + aaATuneStuckInYourHead =10339, + aaStonefoot =10340, + aaHastenedStunningKick =10343, + aaInfusionofThunder =10346, + aaChainsofPurity =10348, + aaProjectionofFury =10351, + aaProjectionofDoom =10352, + aaProjectionofPiety =10353, + aaGiftofLife =10354, + aaBlessingofLight =10355, + aaHastenedDivineIntervention =10358, + aaImprovedAtone =10364, + aaAgelessEnmity =10367, + aaCoverTracks =10368, + aaSpellCastingSubtlety3 =10370, + aaImprovedNaturalInvisibility =10373, + aaProtectionoftheWarder =10374, + aaNaturesSalve =10377, + aaFocusofAnimus =10380, + aaImbuedFerocity =10387, + aaNaturesReprieve =10388, + aaExtendedTrickery =10389, + aaAgelessEnmity2 =10392, + aaShacklesofTunare =10393, + aaBeaconoftheRighteous =10394, + aaBobbingCorpse =10395, + aaGroupSpiritoftheWhiteWolf =10396, + aaGroupSpiritoftheBlackWolf =10397, + aaSelfPreservation =10400, + aaHastenedFrenzy =10401, + aaExtendedHavoc =10404, + aaHastenedDeflectionDiscipline =10405, + aaRogueTripleAttackSkillupTest =10410, + aaHastenedHostoftheElements =10413, + aaEnhancedWallofWind =10421, + aaHandofRo =10424, + aaFixationofRo =10425, + aaPeacefulSpiritoftheWood =10426, + aaPeacefulConvergenceofSpirits =10427, + aaQuickenedArmyoftheDead =10434, + aaHealingFrenzy =10450, + aaOverpoweringStrikes =10453, + aaQuickenedBlessingofRessurection =10456, + aaHastenedAtonement =10459, + aaImprovedSanctuary =10462, + aaBlessingofSanctuary =10463, + aaHastenedCelestialHammer =10464, + aaQuickenedTurnUndead =10470, + aaSpiritofEagle =10500, + aaFlightofEagles =10501, + aaEgress =10502, + aaSpiritsofNature =10503, + aaWallofWind =10506, + aaHastenedSpiritoftheWood =10511, + aaHastenedConvergenceofSpirits =10514, + aaHastenedProjectionofFury =10519, + aaHastenedGutPunch =10522, + aaHastenedFirstSpireoftheWarlord =10527, + aaHastenedSecondSpireoftheWarlord =10532, + aaHastenedThirdSpireoftheWarlord =10537, + aaWillingCovenant =10542, + aaWarlordsResurgence =10545, + aaWarlordsFury =10546, + aaSurgeoftheVanquisher =10547, + aaImprovedShieldSpecialist =10548, + aaBeamofSlumber =10550, + aaPhantasmicReflex =10551, + aaHastenedSelfStasis =10558, + aaForcefulBanishment =10561, + aaHastenedBlastofAnger =10579, + aaHastenedFuriousLeap =10582, + aaHastenedGrapplingStrike =10588, + aaForceofElements =10600, + aaAspectofZomm =10601, + aaExtendedSharedHealth =10610, + aaFuriousRefrain =10627, + aaAgileFeet =10650, + aaHastenedDefensivePoses =10653, + aaExtendedImpenetrableDiscipline =10656, + aaHastenedDestructiveForce2 =10657, + aaMendingofBodyandSoul =10666, + aaLevant =10700, + aaReluctantBenevolence =10701, + aaValorousRage =10711, + aaHastenedGroupGuardianoftheForest =10714, + aaHastenedOutridersAttack =10719, + aaHastenedProtectionoftheSpiritWolf =10722, + aaHastenedImbuedFerocity =10727, + aaHastenedHarmoniousArrow =10730, + aaHastenedEntrap =10733, + aaPoisonArrows =10736, + aaBestowDivineAura2 =10752, + aaBlessingofPurification =10753, + aaSensetheDead =10754, + aaBlessingofRo =10789, + aaExtendedWildGrowth =10792, + aaHastenedCalloftheWild2 =10797, + aaHastenedAuspiceoftheHunter =10800, + aaClenchedJaw =10803, + aaScoutsMasteryofFire =10806, + aaScoutsMasteryofIce =10809, + aaScoutsMasteryofMagic =10812, + aaScoutsMasteryofSlashing =10815, + aaScoutsMasteryofPiercing =10818, + aaScoutsMasteryofBluntWeapons =10821, + aaMassiveStrike =10850, + aaStrikethrough2 =10853, + aaHatesAttraction =10900, + aaFeignedMinion2 =10903, + aaHastenedSummonRemains =10909, + aaVisageofDeath =10912, + aaCascadingTheftofLife2 =10915, + aaExtendedSloth =10950, + aaHastenedAncestralAid =10951, + aaHastenedUnionofSpirits =10954, + aaGroupShrink2 =10957, + aaInconspicuousTotem =10958, + aaExtendedPestilence =10959, + aaGroupSpiritWalk =10960, + aaHastenedTaunt =11000, + aaExtendedShieldReflect =11003, + aaExtendedCommandingVoice =11004, + aaHastenedLeap =11007, + aaRefundTest =11017, + aaHastenedDestruction =11050, + aaNetherstep =11055, + aaBeamofDisplacement =11056, + aaTranslocate =11057, + aaTeleport =11058, + aaPlayingPossum =11073, + aaCatlikeReflexes =11074, + aaHastenedBiteoftheAsp =11077, + aaHastenedGorillaSmash =11078, + aaHastenedRavensClaw =11079, + aaChameleonStrike =11080, + aaBearPatriarch =11081, + aaTwoHandsNoMercy =11085, + aaHastenedCryofBattle =11088, + aaTwinproc =12416, + aaTacticalMastery2 =12419, + aaGroupPerfectedLevitation =12422, + aaDoubleRiposte2 =12429, + aaHastenedForcefulRejuvenation =12430, + aaHastenedCompanionsBlessing =12478, + aaMysticalEcho =12489, + aaHastenedJoltingKicks =12500, + aaHastenedHatesAttraction =12582, + aaHastenedFrenziedStabbing =12600, + aaExtendedFrenziedStabbingDiscipline =12603, + aaSpeedoftheScoundrel =12606, + aaHastenedPinpoint =12607, + aaHastenedTwistedChanceDiscipline =12615, + aaVileEfficacy =12634, + aaImprovedDeathPeace =12635, + aaEyesWideOpen =12636, + aaCommunionoftheCheetah =12638, + aaExtendedConvergenceofSpirit =12645, + aaExtendedSpiritoftheWood =12646, + aaSpiritoftheBear =12651, + aaTwinheal =12652, + aaNaturesBlessing =12655, + aaNaturesFury =12661, + aaHastenedStormStrike =12664, + aaExtendedImpenetrableBarrier =12678, + aaExtendedHeelofKanji =12688, + aaExtendedScaledfist =12691, + aaFistsofSteel =12706, + aaExtendedDeftdance =12709, + aaHastenedDeftdance =12710, + aaHastenedLyreLeap =12713, + aaHastenedQuickTime =12716, + aaExtendedQuickTime =12719, + aaExtendedFierceEye =12720, + aaHastenedFierceEye =12721, + aaHastenedDanceofBlades =12727, + aaResoundingDirge =12737, + aaFreeze =12752, + aaShock =12753, + aaQuickenedArmyoftheDead2 =12763, + aaLRKBolt =12764, + aaExoblast =12765, + aaMercurialTorment =12766, + aaPestilentParalysis =12770, + aaHastenedDivineCompanionAura =12773, + aaEmbalmersCarapace =12778, + aaHastenedEncroachingDarkness =12779, + aaExpandingDarkness =12782, + aaAegisofKildrukaun =12785, + aaBestialBloodrage =12804, + aaCompanionsSacrifice =12807, + aaExtendedFeralgia =12816, + aaHastenedProtectiveSpirit =12819, + aaHastenedEmpathicFury =12822, + aaWardersGemination =12825, + aaQuickenedParagonofSpirit =12831, + aaWardersGift =12837, + aaGelidRending =12846, + aaQuickenedNaturesSalve =12849, + aaImprovedWardofDestruction =12852, + aaAbscond =12864, + aaAtolsUnresistableShackles =12865, + aaDimensionalShield =12866, + aaImprovedSustainedDestruction =12867, + aaHastenedHarvestofDruzzil =12881, + aaSurvivaloftheFelicitous =12884, + aaConcussiveIntuition =12885, + aaEcisIcyBlessing =12886, + aaRosFieryBlessing =12887, + aaDruzzilsMysticalBlessing =12888, + aaKerafyrmsFavor =12889, + aaKerafyrmsFavor2 =12890, + aaKerafyrmsPrismaticFamiliar =12892, + aaImprovedTwincast =12893, + aaHastenedPhantasmalOpponent =12894, + aaHastenedFirstSpireofEnchantment =12902, + aaHastenedSecondSpireofEnchantment =12907, + aaHastenedThirdSpireofEnchantment =12912, + aaProfoundVisage =12920, + aaBeguilersBanishment =12923, + aaExtendedPacification =12926, + aaCalculatedInsanity =12931, + aaCripplingAurora =12937, + aaMentalContortion =12938, + aaGlyphSpray =12939, + aaEtherealYield =12940, + aaDiminutiveCompanion =12941, + aaElementalSpellFury =12960, + aaSmallModulationShard =12963, + aaMediumModulationShard =12964, + aaLargeModulationShard =12965, + aaHastenedMalosinete =12968, + aaShieldoftheElements =12971, + aaExtendedMalosinete =12977, + aaExtendedPestilence2 =12988, + aaArmorofAncestralSpirits =12989, + aaGroupPactoftheWolf =12992, + aaHastenedInconspicuousTotem =13001, + aaPurifiedSpirits =13004, + aaGroupSpiritWalk2 =13008, + aaGroupSilentPresence =13009, + aaHastenedCannibalization =13010, + aaHastenedSpiritChanneling =13013, + aaVengefulSpirits =13017, + aaCripplingApparition =13020, + aaHastenedSelfPreservation =13055, + aaBindingAxe =13065, + aaAgonyofAbsolution =13066, + aaHastenedAbsolution =13067, + aaGiftofDreamlikeExquisiteRadiantMana =13090, + aaLingeringDeath =13096, + aaMarrsSalvation =13100, + aaBlessingoftheFaithful =13101, + aaUnbridledStrikeofFear =13108, + aaHastenedForcefulRejuvenation2 =13129, + aaMovingMountains =13130, + aaQuickenedTerrors =13132, + aaBattleFrenzy =13134, + aaQuickenedDivineAvatar =13137, + aaQuickenedSilentCasting =13143, + aaQuickenedSilentCasting2 =13146, + aaBalefireBurst =13164, + aaExplosionofSpite =13165, + aaHastenedExplosionofHatred =13166, + aaHowloftheWarlord =13169, + aaLeapofFaith =13202, + aaHastenedTuneInYourHead =13204, + aaBloodfury =13224, + aaScentofTerris =13225, + aaDrearyDeeds =13252, + aaEnchantFeymetal =13260, + aaMassEnchantFeymetal =13261, + aaPoke =13268, + aaReplicate =13269, + aaInsight =13270, + aaEnchantAlaranMetal =13271, + aaMassEnchantAlaranMetal =13272, + aaLifetap =13274, + aaGiftofAscendantExquisiteRadiantMana =13294, + aaDivineGuardian =13385, + aaDivinePeace =13388, + aaYaulp =13389, + aaHastenedPurifiedSpirits =13416, + aaSwarmofFireflies =13419, + aaHastenedProjectionofFury2 =13441, + aaBladeGuardian =13444, + aaHastenedPossum =13449, + aaHastenedWardersGift =13463, + aaHastenedCompanionsSacrifice =13474, + aaCheetahsPounce =13483, + aaBloodlust =13484, + aaPrimalFury =13485, + aaShaurisSonoriousClouding =13527, + aaSonicDisplacement =13528, + aaLureoftheSirensSong =13529, + aaGlacialArrow =13549, + aaConvergenceofSpirits2 =13556, + aaHastenedOutridersAccuracy =13565, + aaHastenedOutridersEvasion =13568, + aaGraspofSylvanSpirits =13571, + aaBloodthirstyBlade =13616, + aaEnlightenedFocusofArcanum =13646, + aaExtendedSilentCasting =13667, + aaFuryofKerafyrm =13670, + aaHallowedSteed =13673, + aaWickedSteed =13674, + aaEmbalmersCarapace2 =13678, + aaMirroredPestilence =13684, + aaEmbraceTheDecay =13687, + aaQuickenedScentofTerris =13689, + aaFrenzyoftheDead =13693, + aaElementalUnion =13695, + aaCalculatedInsanity2 =13729, + aaHastenedCalculatedInsanity =13753, + aaHastenedMentalContortion =13758, + aaHastenedCripplingAurora =13764, + aaGlyphofIndeterminableReward =13788, + aaRoguesFury2 =13792, + aaPoisonousPerfection =13801, + aaDistantStrike =13844, + aaWarCryoftheBraxi =13872, + aaHastenedJuggernautSurge =13873, + aaHastenedResilience =13878, + aaHastenedBloodPact =13881, + aaHastenedFivePointPalm =13889, + aaPhalanxofOne =13900, + aaHastenedFlashofAnger =13905, + aaHastenedBazuRoar =13908, + aaHastenedScowl =13911, + aaHastenedMarkoftheMageHunter =13917, + aaVeiledStrength =13933, + aaVeiledFortitude =13943, + aaVeiledRapidity =13953, + aaVeiledCunning =13963, + aaVeiledAcumen =13973, + aaVeiledBrilliance =13983, + aaVeiledAllure =13993, + aaRestorationofLife =14010, + aaHastenedBeaconoftheRighteous =14011, + aaKnowledgeofAlaranCulture =14017, + aaKnowledgeofAlaranCultureAdvanced =14018, + aaBraceForImpact =14019, + aaMercilessBlade =14026, + aaCombatantsPact =14029, + aaWarlordsResolve =14032, + aaHastenedWarlordsBravery =14037, + aaHastenedWarlordsTenacity =14040, + aaHastenedLeechTouch =14046, + aaBonyGraspofDeath =14051, + aaThoughtLeech =14052, + aaHastenedLeechcurseDiscipline =14056, + aaHastenedUnholyAuraDiscipline =14059, + aaHastenedHarmshield =14062, + aaHastenedProjectionofDoom =14065, + aaHastenedProjectionofPiety =14068, + aaShieldofBrilliance =14071, + aaShieldofBrilliance2 =14072, + aaHastenedSanctificationDiscipline =14076, + aaSpeedoftheSavior =14080, + aaDivineCall =14081, + aaHastenedLeapofFaith =14085, + aaHastenedMarrsSalvation =14088, + aaHastenedArmoroftheInquisitor =14091, + aaQuickenedStuns =14100, + aaExtendedOutridersAttack =14115, + aaEtheriumBlades =14129, + aaHastenedAssassinationDisciplines =14135, + aaCunningDisguiseShissar =14139, + aaScoutsMasteryofPiercing2 =14141, + aaExtendedEnvenomedBlades =14144, + aaHastenedSpeedFocus =14148, + aaHeelofBrithrax =14151, + aaPressurePoints =14157, + aaHastenedZanFisWhistle =14160, + aaExtendedZanFisWhistle =14163, + aaHastenedThunder2 =14166, + aaHastenedThousandBlades =14169, + aaHastenedTuneStuckInYourHead =14176, + aaExtendedDanceofBlades =14179, + aaExtendedThousandBlades =14180, + aaAllegrettoofBattle =14181, + aaVivaceofConflict =14186, + aaNoteworthyDisguiseDrake =14192, + aaBulwarkofBlades =14193, + aaDecapitation =14200, + aaHastenedBerserkingDisciplines =14203, + aaQuietMiracle =14206, + aaRepeltheWicked =14207, + aaBeaconofLife =14208, + aaBlessedChains =14209, + aaHastenedFocusedCelestialRegeneration =14213, + aaShrink =14224, + aaQuickenedSpiritCalling =14225, + aaQuickenedCalloftheWild2 =14228, + aaSpiritualRebuke =14231, + aaPathosis =14232, + aaPreincarnation =14233, + aaSpiritualBlessing =14234, + aaHastenedNaturesFury =14241, + aaWrathoftheForestWalker =14244, + aaGiftofSylvanSpirits =14249, + aaExtendedVinelashCascade =14254, + aaExtendedSpiritoftheBear =14259, + aaVeiloftheUnderbrush =14262, + aaParalyticSpores =14264, + aaHiddenCommunionoftheCheetah =14265, + aaSteadfastResolve =14275, + aaExtendedGroupBestialAlignment =14278, + aaGroupPerfectedInvisibilitytoUndead2 =14281, + aaPerfectedInvisibilitytoUndead =14282, + aaQuickenedStasis =14283, + aaExtendedDrearyDeeds =14286, + aaQuickenedFrenziedBurnout =14289, + aaQuickenedHostoftheElements =14292, + aaHastenedCompanionsRelocation =14295, + aaModulationSpecialist =14298, + aaStaffBlock =14301, + aaHastenedDrapeofShadows =14304, + aaVirulentTalon =14307, + aaHastenedPestilentParalysis =14308, + aaHastenedMercurialTorment =14311, + aaHastenedWhisperwind =14314, + aaQuickenedScentofTerris2 =14316, + aaQuickenedDeathBloom =14318, + aaDeathsMalaise =14321, + aaDyingGrasp =14322, + aaAHoleInSpace =14323, + aaHastenedForceofWill =14328, + aaHastenedImprovedTwincast =14331, + aaHastenedNightmareStasis =14341, + aaScintillatingBeam =14346, + aaConvergence =14358, + aaGiftofDeathlyResolve =14359, + aaFuneralPyre =14360, + aaHastenedEldritchRune =14364, + aaHerosBarracks =14367, + aaHerosBarracks2 =14368, + aaHerosBarracks3 =14369, + aaHerosBarracks4 =14370, + aaSummonTomeoftheHerosJourney =14371, + + aaHighestID //this should always be last, and should always + //follow the highest AA ID +} aaID; + + +//Structure representing the database's AA actions +struct AA_DBAction { + uint32 reuse_time; //in seconds + uint16 spell_id; //spell to cast, SPELL_UNKNOWN=no spell + aaTargetType target; //from aaTargetType + aaNonspellAction action; //non-spell action to take + uint16 mana_cost; //mana the NON-SPELL action costs + uint16 duration; //duration of NON-SPELL effect, 0=N/A + aaID redux_aa; //AA which reduces reuse time + int32 redux_rate; //%/point in redux_aa reduction in reuse time + aaID redux_aa2; //AA which reduces reuse time + int32 redux_rate2; //%/point in redux_aa reduction in reuse time +}; + +//Structure representing the database's swarm pet configs +struct AA_SwarmPet { + uint8 count; //number to summon + uint32 npc_id; //id from npc_types to represent it. + uint16 duration; //how long they last, in seconds +}; + +struct AALevelCost_Struct +{ + uint32 Level; + uint32 Cost; +}; + +//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 map AA_SwarmPets; //key=spell_id + +#define AA_Choose3(val, v1, v2, v3) (val==1?v1:(val==2?v2:v3)) + +extern mapaas_send; +extern std::map > aa_effects; +extern std::map AARequiredLevelAndCost; + +enum { //values of AA_Action.action + aaActionActivate = 0, + aaActionSetEXP = 1, + aaActionDisableEXP = 2, + aaActionBuy = 3 +}; + +class Timer; +class AA_SwarmPetInfo { +public: + AA_SwarmPetInfo(); + ~AA_SwarmPetInfo(); + Mob * GetOwner(); + Timer *duration; + uint32 target; //the target ID + uint32 owner_id; +}; + +#endif diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt new file mode 100644 index 000000000..40ac4f901 --- /dev/null +++ b/zone/CMakeLists.txt @@ -0,0 +1,185 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +SET(zone_sources + AA.cpp + aggro.cpp + attack.cpp + beacon.cpp + bonuses.cpp + bot.cpp + botspellsai.cpp + client.cpp + client_logs.cpp + client_mods.cpp + client_packet.cpp + client_process.cpp + command.cpp + doors.cpp + effects.cpp + embparser.cpp + embperl.cpp + embxs.cpp + entity.cpp + exp.cpp + faction.cpp + fearpath.cpp + forage.cpp + groups.cpp + guild.cpp + guild_mgr.cpp + hate_list.cpp + horse.cpp + inventory.cpp + loottables.cpp + Map.cpp + merc.cpp + mob.cpp + MobAI.cpp + net.cpp + npc.cpp + NpcAI.cpp + Object.cpp + parser.cpp + pathing.cpp + perl_client.cpp + perl_doors.cpp + perl_entity.cpp + perl_groups.cpp + perl_hateentry.cpp + perl_mob.cpp + perl_npc.cpp + perl_object.cpp + perl_perlpacket.cpp + perl_PlayerCorpse.cpp + perl_questitem.cpp + perl_raids.cpp + perlpacket.cpp + perlparser.cpp + petitions.cpp + pets.cpp + PlayerCorpse.cpp + QGlobals.cpp + questmgr.cpp + QuestParserCollection.cpp + raids.cpp + spawn2.cpp + spawn2.h + spawngroup.cpp + spdat.cpp + special_attacks.cpp + spell_effects.cpp + spells.cpp + tasks.cpp + titles.cpp + tradeskills.cpp + trading.cpp + trap.cpp + tribute.cpp + updatemgr.cpp + watermap.cpp + waypoints.cpp + worldserver.cpp + zone.cpp + zone_logsys.cpp + zone_profile.cpp + ZoneConfig.cpp + zonedb.cpp + zonedbasync.cpp + zoning.cpp +) + +SET(zone_headers + AA.h + basic_functions.h + beacon.h + blah.h + bot.h + botStructs.h + client.h + client_logs.h + client_packet.h + command.h + common.h + doors.h + embparser.h + embperl.h + embxs.h + entity.h + errmsg.h + event_codes.h + faction.h + features.h + forage.h + groups.h + guild_mgr.h + hate_list.h + horse.h + loottable.h + map.h + masterentity.h + maxskill.h + message.h + merc.h + mob.h + net.h + npc.h + NpcAI.h + object.h + parser.h + pathing.h + perlpacket.h + perlparser.h + petitions.h + pets.h + PlayerCorpse.h + QGlobals.h + QuestInterface.h + questmgr.h + QuestParserCollection.h + raid.h + raids.h + skills.h + spawn2.cpp + spawn2.h + spawngroup.h + spdat.h + StringIDs.h + tasks.h + titles.h + trap.h + updatemgr.h + watermap.h + worldserver.h + zone.h + zone_profile.h + ZoneConfig.h + zonedb.h + zonedbasync.h + zonedump.h +) + +ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers}) + +ADD_DEFINITIONS(-DZONE) + +TARGET_LINK_LIBRARIES(zone Common ${PERL_LIBRARY} debug ${MySQL_LIBRARY_DEBUG} optimized ${MySQL_LIBRARY_RELEASE}) + +IF(MSVC) + SET_TARGET_PROPERTIES(zone PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF") + TARGET_LINK_LIBRARIES(zone "Ws2_32.lib") +ENDIF(MSVC) + +IF(MINGW) + TARGET_LINK_LIBRARIES(zone "WS2_32") +ENDIF(MINGW) + +IF(UNIX) + TARGET_LINK_LIBRARIES(zone "dl") + TARGET_LINK_LIBRARIES(zone "z") + TARGET_LINK_LIBRARIES(zone "m") + TARGET_LINK_LIBRARIES(zone "rt") + TARGET_LINK_LIBRARIES(zone "pthread") + ADD_DEFINITIONS(-fPIC) +ENDIF(UNIX) + +SET(EXECUTABLE_OUTPUT_PATH ../Bin) diff --git a/zone/Map.cpp b/zone/Map.cpp new file mode 100644 index 000000000..55d39dfca --- /dev/null +++ b/zone/Map.cpp @@ -0,0 +1,1029 @@ +/* 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 "../common/debug.h" +#include "../common/MiscFunctions.h" +#include +#include +#include +#include + +#ifndef WIN32 +//comment this out if your worried about zone boot times and your not using valgrind +#define SLOW_AND_CRAPPY_MAKES_VALGRIND_HAPPY +#endif + +#include "zone_profile.h" +#include "map.h" +#include "zone.h" +#ifdef _WINDOWS +#define snprintf _snprintf +#endif + +extern Zone* zone; + +//Do we believe the normals from the map file? +//you want this enabled if it dosent break things. +//#define TRUST_MAPFILE_NORMALS + +//#define OPTIMIZE_QT_LOOKUPS +#define EPS 0.002f //acceptable error + +//#define DEBUG_SEEK 1 +//#define DEBUG_BEST_Z 1 + +/* + of note: + it is possible to get a null node in a valid region if it + does not have any triangles in it. + this will dictate bahaviour on getting a null node + TODO: listen to the above + */ + +//quick functions to clean up vertex code. +#define Vmin3(o, a, b, c) ((a.ob.o)? (a.o>c.o?a.o:c.o) : (b.o>c.o?b.o:c.o)) +#define ABS(x) ((x)<0?-(x):(x)) + +Map* Map::LoadMapfile(const char* in_zonename, const char *directory) { + FILE *fp; + char zBuf[64]; + char cWork[256]; + Map* ret = 0; + + //have to convert to lower because the short names im getting + //are not all lower anymore, copy since strlwr edits the str. + strn0cpy(zBuf, in_zonename, 64); + + if(directory == NULL) + directory = MAP_DIR; + snprintf(cWork, 250, "%s/%s.map", directory, strlwr(zBuf)); + + if ((fp = fopen( cWork, "rb" ))) { + ret = new Map(); + if(ret != NULL) { + ret->loadMap(fp); + printf("Map %s loaded.\n", cWork); + } else { + printf("Map %s loading failed.\n", cWork); + } + fclose(fp); + } + else { + printf("Map %s not found.\n", cWork); + } + return ret; +} + +Map::Map() { + _minz = FLT_MAX; + _minx = FLT_MAX; + _miny = FLT_MAX; + _maxz = FLT_MIN; + _maxx = FLT_MIN; + _maxy = FLT_MIN; + + m_Faces = 0; + m_Nodes = 0; + m_FaceLists = 0; + mFinalFaces = NULL; + mNodes = NULL; + mFaceLists = NULL; +} + +bool Map::loadMap(FILE *fp) { +#ifndef INVERSEXY +#warning Map files do not work without inverted XY + return(false); +#endif + + mapHeader head; + if(fread(&head, sizeof(head), 1, fp) != 1) { + //map read error. + return(false); + } + if(head.version != MAP_VERSION) { + //invalid version... if there really are multiple versions, + //a conversion routine could be possible. + printf("Invalid map version 0x%lx\n", (unsigned long)head.version); + return(false); + } + + printf("Map header: %lu faces, %u nodes, %lu facelists\n", (unsigned long)head.face_count, (unsigned int)head.node_count, (unsigned long)head.facelist_count); + + m_Faces = head.face_count; + m_Nodes = head.node_count; + m_FaceLists = head.facelist_count; + + /* fread(&m_Vertex, 4, 1, fp); + fread(&m_Faces , 4, 1, fp);*/ +// mFinalVertex = new VERTEX[m_Vertex]; + mFinalFaces = new FACE [m_Faces]; + mNodes = new NODE[m_Nodes]; + mFaceLists = new uint32[m_FaceLists]; + +// fread(mFinalVertex, m_Vertex, sizeof(VERTEX), fp); + + //this was changed to this loop from the single read because valgrind was + //hanging on this read otherwise... I dont pretend to understand it. +#ifdef SLOW_AND_CRAPPY_MAKES_VALGRIND_HAPPY + uint32 r; + for(r = 0; r < m_Faces; r++) { + if(fread(mFinalFaces+r, sizeof(FACE), 1, fp) != 1) { + printf("Unable to read %lu faces from map file, got %lu.\n", (unsigned long)m_Faces, (unsigned long)r); + return(false); + } + } +#else + uint32 count; + if((count = static_cast(fread(mFinalFaces, sizeof(FACE), m_Faces , fp))) != m_Faces) { + printf("Unable to read %lu face bytes from map file, got %lu.\n", (unsigned long)m_Faces, (unsigned long)count); + return(false); + } +#endif + +#ifdef SLOW_AND_CRAPPY_MAKES_VALGRIND_HAPPY + for(r = 0; r < m_Nodes; r++) { + if(fread(mNodes+r, sizeof(NODE), 1, fp) != 1) { + printf("Unable to read %lu nodes from map file, got %lu.\n", (unsigned long)m_Nodes, (unsigned long)r); + return(false); + } + } +#else + if(fread(mNodes, sizeof(NODE), m_Nodes, fp) != m_Nodes) { + printf("Unable to read %lu nodes from map file.\n", (unsigned long)m_Nodes); + return(false); + } +#endif + +#ifdef SLOW_AND_CRAPPY_MAKES_VALGRIND_HAPPY + for(r = 0; r < m_FaceLists; r++) { + if(fread(mFaceLists+r, sizeof(uint32), 1, fp) != 1) { + printf("Unable to read %lu face lists from map file, got %lu.\n", (unsigned long)m_FaceLists, (unsigned long)r); + return(false); + } + } +#else + if(fread(mFaceLists, sizeof(uint32), m_FaceLists, fp) != m_FaceLists) { + printf("Unable to read %lu face lists from map file.\n", (unsigned long)m_FaceLists); + return(false); + } +#endif + + +/* mRoot = new NODE(); + RecLoadNode(mRoot, fp );*/ + + uint32 i; + float v; + for(i = 0; i < m_Faces; i++) { + v = Vmax3(x, mFinalFaces[i].a, mFinalFaces[i].b, mFinalFaces[i].c); + if(v > _maxx) + _maxx = v; + v = Vmin3(x, mFinalFaces[i].a, mFinalFaces[i].b, mFinalFaces[i].c); + if(v < _minx) + _minx = v; + v = Vmax3(y, mFinalFaces[i].a, mFinalFaces[i].b, mFinalFaces[i].c); + if(v > _maxy) + _maxy = v; + v = Vmin3(y, mFinalFaces[i].a, mFinalFaces[i].b, mFinalFaces[i].c); + if(v < _miny) + _miny = v; + v = Vmax3(z, mFinalFaces[i].a, mFinalFaces[i].b, mFinalFaces[i].c); + if(v > _maxz) + _maxz = v; + v = Vmin3(z, mFinalFaces[i].a, mFinalFaces[i].b, mFinalFaces[i].c); + if(v < _minz) + _minz = v; + } + printf("Loaded map: %lu vertices, %lu faces\n", (unsigned long)m_Faces*3, (unsigned long)m_Faces); + printf("Map BB: (%.2f -> %.2f, %.2f -> %.2f, %.2f -> %.2f)\n", _minx, _maxx, _miny, _maxy, _minz, _maxz); + return(true); +} + +Map::~Map() { +// safe_delete_array(mFinalVertex); + safe_delete_array(mFinalFaces); + safe_delete_array(mNodes); + safe_delete_array(mFaceLists); +// RecFreeNode( mRoot ); +} + + +NodeRef Map::SeekNode( NodeRef node_r, float x, float y ) const { + if(node_r == NODE_NONE || node_r >= m_Nodes) { + return(NODE_NONE); + } + PNODE _node = &mNodes[node_r]; +#ifdef DEBUG_SEEK +printf("Seeking node for %u:(%.2f, %.2f) with root 0x%x.\n", node_r, x, y, _node); + +printf(" Current Box: (%.2f -> %.2f, %.2f -> %.2f)\n", _node->minx, _node->maxx, _node->miny, _node->maxy); +#endif + if( x>= _node->minx && x<= _node->maxx && y>= _node->miny && y<= _node->maxy ) { + if( _node->flags & nodeFinal ) { +#ifdef DEBUG_SEEK +printf("Seeking node for %u:(%.2f, %.2f) with root 0x%x.\n", node_r, x, y, _node); +printf(" Final Node: (%.2f -> %.2f, %.2f -> %.2f)\n", _node->minx, _node->maxx, _node->miny, _node->maxy); +fflush(stdout); +printf(" Final node found with %d faces.\n", _node->faces.count); +/*printf(" Faces:\n"); +unsigned long *cfl = mFaceLists + _node->faces.offset; +unsigned long m; +for(m = 0; m < _node->faces.count; m++) { + FACE *c = &mFinalFaces[ *cfl ]; + printf(" %lu (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n", + *cfl, c->a.x, c->a.y, c->a.z, + c->b.x, c->b.y, c->b.z, + c->c.x, c->c.y, c->c.z); + cfl++; +}*/ +#endif + return node_r; + } +#ifdef DEBUG_SEEK +printf(" Kids: %u, %u, %u, %u\n", _node->nodes[0], _node->nodes[1], _node->nodes[2], _node->nodes[3]); + +printf(" Contained In Box: (%.2f -> %.2f, %.2f -> %.2f)\n", _node->minx, _node->maxx, _node->miny, _node->maxy); + +/*printf(" Node found has children.\n"); +if(_node->node1 != NULL) { + printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", + _node->node1->minx, _node->node1->maxx, _node->node1->miny, _node->node1->maxy); +} +if(_node->node2 != NULL) { + printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", + _node->node2->minx, _node->node2->maxx, _node->node2->miny, _node->node2->maxy); +} +if(_node->node3 != NULL) { + printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", + _node->node3->minx, _node->node3->maxx, _node->node3->miny, _node->node3->maxy); +} +if(_node->node4 != NULL) { + printf("\tNode: (%.2f -> %.2f, %.2f -> %.2f)\n", + _node->node4->minx, _node->node4->maxx, _node->node4->miny, _node->node4->maxy); +}*/ +#endif + //NOTE: could precalc these and store them in node headers + + NodeRef tmp = NODE_NONE; +#ifdef OPTIMIZE_QT_LOOKUPS + float midx = _node->minx + (_node->maxx - _node->minx) * 0.5; + float midy = _node->miny + (_node->maxy - _node->miny) * 0.5; + //follow ordering rules from map.h... + if(x < midx) { + if(y < midy) { //quad 3 + if(_node->nodes[2] != NODE_NONE && _node->nodes[2] != node_r) + tmp = SeekNode( _node->nodes[2], x, y ); + } else { //quad 2 + if(_node->nodes[2] != NODE_NONE && _node->nodes[1] != node_r) + tmp = SeekNode( _node->nodes[1], x, y ); + } + } else { + if(y < midy) { //quad 4 + if(_node->nodes[2] != NODE_NONE && _node->nodes[3] != node_r) + tmp = SeekNode( _node->nodes[3], x, y ); + } else { //quad 1 + if(_node->nodes[2] != NODE_NONE && _node->nodes[0] != node_r) + tmp = SeekNode( _node->nodes[0], x, y ); + } + } + if( tmp != NODE_NONE ) return tmp; +#else + if(_node->nodes[0] == node_r) return(NODE_NONE); //prevent infinite recursion + tmp = SeekNode( _node->nodes[0], x, y ); + if( tmp != NODE_NONE ) return tmp; + if(_node->nodes[1] == node_r) return(NODE_NONE); //prevent infinite recursion + tmp = SeekNode( _node->nodes[1], x, y ); + if( tmp != NODE_NONE ) return tmp; + if(_node->nodes[2] == node_r) return(NODE_NONE); //prevent infinite recursion + tmp = SeekNode( _node->nodes[2], x, y ); + if( tmp != NODE_NONE ) return tmp; + if(_node->nodes[3] == node_r) return(NODE_NONE); //prevent infinite recursion + tmp = SeekNode( _node->nodes[3], x, y ); + if( tmp != NODE_NONE ) return tmp; +#endif + + } +#ifdef DEBUG_SEEK +printf(" No node found.\n"); +#endif + return(NODE_NONE); +} + +// maybe precalc edges. +int* Map::SeekFace( NodeRef node_r, float x, float y ) { + if( node_r == NODE_NONE || node_r >= m_Nodes) { + return(NULL); + } + const PNODE _node = &mNodes[node_r]; + if(!(_node->flags & nodeFinal)) { + return(NULL); //not a final node... could find the proper node... + } + + +//printf("Seeking face for (%.2f, %.2f) with root 0x%x.\n", x, y, _node); + float dx,dy; + float nx,ny; + int *face = mCandFaces; + unsigned long i; + for( i=0;i<_node->faces.count;i++ ) { + const FACE &cf = mFinalFaces[ mFaceLists[_node->faces.offset + i] ]; + const VERTEX &v1 = cf.a; + const VERTEX &v2 = cf.b; + const VERTEX &v3 = cf.c; + + dx = v2.x - v1.x; dy = v2.y - v1.y; + nx = x - v1.x; ny = y - v1.y; + if( dx*ny - dy*nx >0.0f ) continue; + + dx = v3.x - v2.x; dy = v3.y - v2.y; + nx = x - v2.x; ny = y - v2.y; + if( dx*ny - dy*nx >0.0f ) continue; + + dx = v1.x - v3.x; dy = v1.y - v3.y; + nx = x - v3.x; ny = y - v3.y; + if( dx*ny - dy*nx >0.0f ) continue; + + *face++ = mFaceLists[_node->faces.offset + i]; + } + *face = -1; + return mCandFaces; +} + +// can be op? +float Map::GetFaceHeight( int _idx, float x, float y ) const { + const PFACE pface = &mFinalFaces[ _idx ]; + return ( pface->nd - x * pface->nx - y * pface->ny ) / pface->nz; +} + +//FatherNitwit's LOS code... +//Algorithm stolen from internet +//p1=start of segment +//p2=end of segment + +bool Map::LineIntersectsZone(VERTEX start, VERTEX end, float step_mag, VERTEX *result, FACE **on) const +{ + _ZP(Map_LineIntersectsZone); + // Cast a ray from start to end, checking for collisions in each node between the two points. + // + float stepx, stepy, stepz, curx, cury, curz; + + curx = start.x; + cury = start.y; + curz = start.z; + + VERTEX cur = start; + + stepx = end.x - start.x; + stepy = end.y - start.y; + stepz = end.z - start.z; + + if((stepx == 0) && (stepy == 0) && (stepz == 0)) + return false; + + float factor = sqrt(stepx*stepx + stepy*stepy + stepz*stepz); + + stepx = (stepx/factor)*step_mag; + stepy = (stepy/factor)*step_mag; + stepz = (stepz/factor)*step_mag; + + NodeRef cnode, lnode, finalnode; + lnode = NODE_NONE; //last node visited + + cnode = SeekNode(GetRoot(), start.x, start.y); + finalnode = SeekNode(GetRoot(), end.x, end.y); + if(cnode == finalnode) + return LineIntersectsNode(cnode, start, end, result, on); + + do { + + stepx = (float)end.x - curx; + stepy = (float)end.y - cury; + stepz = (float)end.z - curz; + + factor = sqrt(stepx*stepx + stepy*stepy + stepz*stepz); + + stepx = (stepx/factor)*step_mag; + stepy = (stepy/factor)*step_mag; + stepz = (stepz/factor)*step_mag; + + cnode = SeekNode(GetRoot(), curx, cury); + if(cnode != lnode) + { + lnode = cnode; + + if(cnode == NODE_NONE) + return false; + + if(LineIntersectsNode(cnode, start, end, result, on)) + return(true); + + if(cnode == finalnode) + return false; + } + curx += stepx; + cury += stepy; + curz += stepz; + + cur.x = curx; + cur.y = cury; + cur.z = curz; + + if(ABS(curx - end.x) < step_mag) cur.x = end.x; + if(ABS(cury - end.y) < step_mag) cur.y = end.y; + if(ABS(curz - end.z) < step_mag) cur.z = end.z; + + } while(cur.x != end.x || cur.y != end.y || cur.z != end.z); + + return false; +} + +bool Map::LocWithinNode( NodeRef node_r, float x, float y ) const { + if( node_r == NODE_NONE || node_r >= m_Nodes) { + return(false); + } + const PNODE _node = &mNodes[node_r]; + //this function exists so nobody outside of MAP needs to know + //how the NODE sturcture works + return( x>= _node->minx && x<= _node->maxx && y>= _node->miny && y<= _node->maxy ); +} + +bool Map::LineIntersectsNode( NodeRef node_r, VERTEX p1, VERTEX p2, VERTEX *result, FACE **on) const { + _ZP(Map_LineIntersectsNode); + if( node_r == NODE_NONE || node_r >= m_Nodes) { + return(true); //can see through empty nodes, just allow LOS on error... + } + const PNODE _node = &mNodes[node_r]; + if(!(_node->flags & nodeFinal)) { + return(true); //not a final node... not sure best action + } + + unsigned long i; + + PFACE cur; + const uint32 *cfl = mFaceLists + _node->faces.offset; + + for(i = 0; i < _node->faces.count; i++) { + if(*cfl > m_Faces) + continue; //watch for invalid lists, they seem to happen + cur = &mFinalFaces[ *cfl ]; + if(LineIntersectsFace(cur,p1, p2, result)) { + if(on != NULL) + *on = cur; + return(true); + } + cfl++; + } + +//printf("Checked %ld faces and found none in the way.\n", i); + + return(false); +} + + +float Map::FindBestZ( NodeRef node_r, VERTEX p1, VERTEX *result, FACE **on) const { + _ZP(Map_FindBestZ); + + p1.z += RuleI(Map, FindBestZHeightAdjust); + + if(RuleB(Map, UseClosestZ)) + return FindClosestZ(p1); + + if(node_r == GetRoot()) { + node_r = SeekNode(node_r, p1.x, p1.y); + } + if( node_r == NODE_NONE || node_r >= m_Nodes) { + return(BEST_Z_INVALID); + } + const PNODE _node = &mNodes[node_r]; + if(!(_node->flags & nodeFinal)) { + return(BEST_Z_INVALID); //not a final node... could find the proper node... + } + + VERTEX tmp_result; //dummy placeholder if they do not ask for a result. + if(result == NULL) + result = &tmp_result; + + VERTEX p2(p1); + p2.z = BEST_Z_INVALID; + + float best_z = BEST_Z_INVALID; + int zAttempt; + + unsigned long i; + + PFACE cur; + + // If we don't find a bestZ on the first attempt, we try again from a position CurrentZ + 10 higher + // This is in case the pathing between waypoints temporarily sends the NPC below ground level. + // + for(zAttempt=1; zAttempt<=2; zAttempt++) { + + const uint32 *cfl = mFaceLists + _node->faces.offset; + +#ifdef DEBUG_BEST_Z +printf("Start finding best Z...\n"); +#endif + for(i = 0; i < _node->faces.count; i++) { + if(*cfl > m_Faces) + continue; //watch for invalid lists, they seem to happen, e.g. in eastwastes.map + + cur = &mFinalFaces[ *cfl ]; +//printf("Intersecting with face %lu\n", *cfl); + if(LineIntersectsFace(cur, p1, p2, result)) { +#ifdef DEBUG_BEST_Z + printf(" %lu (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n", + *cfl, cur->a.x, cur->a.y, cur->a.z, + cur->b.x, cur->b.y, cur->b.z, + cur->c.x, cur->c.y, cur->c.z); + printf("Found a z: %.2f\n", result->z); +#endif + if (result->z > best_z) { + if(on != NULL) + *on = cur; + best_z = result->z; + } + } + cfl++; + } + + if(best_z != BEST_Z_INVALID) return best_z; + + p1.z = p1.z + 10 ; // If we can't find a best Z, the NPC is probably just under the world. Try again from 10 units higher up. + } + +#ifdef DEBUG_BEST_Z +fflush(stdout); +printf("Best Z found: %.2f\n", best_z); +#endif + return best_z; +} + + +bool Map::LineIntersectsFace( PFACE cface, VERTEX p1, VERTEX p2, VERTEX *result) const { + if( cface == NULL ) { + return(false); //cant intersect a face we dont have... i guess + } + + const VERTEX &pa = cface->a; + const VERTEX &pb = cface->b; + const VERTEX &pc = cface->c; + + //quick bounding box checks + float tbb; + + tbb = Vmin3(x, pa, pb, pc); + if(p1.x < tbb && p2.x < tbb) + return(false); + tbb = Vmin3(y, pa, pb, pc); + if(p1.y < tbb && p2.y < tbb) + return(false); + tbb = Vmin3(z, pa, pb, pc); + if(p1.z < tbb && p2.z < tbb) + return(false); + + tbb = Vmax3(x, pa, pb, pc); + if(p1.x > tbb && p2.x > tbb) + return(false); + tbb = Vmax3(y, pa, pb, pc); + if(p1.y > tbb && p2.y > tbb) + return(false); + tbb = Vmax3(z, pa, pb, pc); + if(p1.z > tbb && p2.z > tbb) + return(false); + + //begin attempt 2 +//#define RTOD 57.2957795 //radians to degrees constant. + + float d; + float denom,mu; + VERTEX n, intersect; + +// FACE *thisface = &mFinalFaces[ _node->pfaces[ i ] ]; + + VERTEX *p = &intersect; + if(result != NULL) + p = result; + + // Calculate the parameters for the plane + //recalculate from points +#ifndef TRUST_MAPFILE_NORMALS + n.x = (pb.y - pa.y)*(pc.z - pa.z) - (pb.z - pa.z)*(pc.y - pa.y); + n.y = (pb.z - pa.z)*(pc.x - pa.x) - (pb.x - pa.x)*(pc.z - pa.z); + n.z = (pb.x - pa.x)*(pc.y - pa.y) - (pb.y - pa.y)*(pc.x - pa.x); + Normalize(&n); + d = - n.x * pa.x - n.y * pa.y - n.z * pa.z; +#else + //use precaled data from .map file + n.x = cface->nx; + n.y = cface->ny; + n.z = cface->nz; + d = cface->nd; +#endif + + //try inverting the normals... + n.x = -n.x; + n.y = -n.y; + n.z = -n.z; + d = - n.x * pa.x - n.y * pa.y - n.z * pa.z; //recalc + + + // Calculate the position on the line that intersects the plane + denom = n.x * (p2.x - p1.x) + n.y * (p2.y - p1.y) + n.z * (p2.z - p1.z); + if (ABS(denom) < EPS) // Line and plane don't intersect + return(false); + mu = - (d + n.x * p1.x + n.y * p1.y + n.z * p1.z) / denom; + if (mu < 0 || mu > 1) // Intersection not along line segment + return(false); + p->x = p1.x + mu * (p2.x - p1.x); + p->y = p1.y + mu * (p2.y - p1.y); + p->z = p1.z + mu * (p2.z - p1.z); + + +/* //old method, slow as hell due to acos(), but it works well + + float a1,a2,a3; + float total; + VERTEX pa1,pa2,pa3; + + pa1.x = pa.x - p->x; + pa1.y = pa.y - p->y; + pa1.z = pa.z - p->z; + Normalize(&pa1); + pa2.x = pb.x - p->x; + pa2.y = pb.y - p->y; + pa2.z = pb.z - p->z; + Normalize(&pa2); + pa3.x = pc.x - p->x; + pa3.y = pc.y - p->y; + pa3.z = pc.z - p->z; + Normalize(&pa3); + a1 = pa1.x*pa2.x + pa1.y*pa2.y + pa1.z*pa2.z; + a2 = pa2.x*pa3.x + pa2.y*pa3.y + pa2.z*pa3.z; + a3 = pa3.x*pa1.x + pa3.y*pa1.y + pa3.z*pa1.z; + +//holy hell these 3 acos are slow, we need to rewrite this... +// total = (acos(a1) + acos(a2) + acos(a3)); +// if (ABS(total - 2*M_PI) > EPS) + total = (acos(a1) + acos(a2) + acos(a3)) * 57.2957795; + if (ABS(total - 360) > EPS) + return(false); + + return(true); +*/ + +/* + //yet another failed method, project triangle and point into + //2 space based on largest component of the normal + //and check the triangle there. + float tx, ty, tz; + if(n.x < 0) + tx = -n.x; + else + tx = n.x; + if(n.y < 0) + ty = -n.y; + else + ty = n.y; + if(n.z < 0) + tz = -n.z; + else + tz = n.z; + + VERTEX pa2, pb2, pc2; + if(tx < ty) { + //keep x + if(tz < ty) { + //keep z, drop y + pa2.x = pa.x; pa2.y = pa.z; + pb2.x = pb.x; pb2.y = pb.z; + pc2.x = pc.x; pc2.y = pc.z; + } else { + //keep y, drop z... + pa2.x = pa.x; pa2.y = pa.y; + pb2.x = pb.x; pb2.y = pb.y; + pc2.x = pc.x; pc2.y = pc.y; + } + } else { + //keep y + if(tz < tx) { + //keep z, drop x + pa2.x = pa.x; pa2.y = pa.z; + pb2.x = pb.x; pb2.y = pb.z; + pc2.x = pc.x; pc2.y = pc.z; + } else { + //keep y, drop z... + pa2.x = pa.x; pa2.y = pa.y; + pb2.x = pb.x; pb2.y = pb.y; + pc2.x = pc.x; pc2.y = pc.y; + } + } + + // Determine whether or not the intersection point is bounded by pa,pb,pc +#define Sign(p1, p2, p3) \ + ((p1->x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1->y - p3.y)) + bool b1, b2, b3; + + b1 = Sign(p, pa2, pb2) < 0.0f; + b2 = Sign(p, pb2, pc2) < 0.0f; + b3 = Sign(p, pc2, pa2) < 0.0f; + + return ((b1 == b2) && (b2 == b3)); +*/ + +/* //not working well, seems to block LOS a lot + + //a new check based on barycentric coordinates, stolen from + //http://www.flipcode.com/cgi-bin/fcmsg.cgi?thread_show=7766 + float xcp, ycp, xab, yab, xac, yac; + float divb; + VERTEX barycoords; + + xcp = p->x - pc.x; + ycp = p->z - pc.z; + if( xcp == 0.f && ycp == 0.f ) + return(true); + + xab = pb.x - pa.x; + yab = pb.z - pa.z; + divb = xab * ycp - yab * xcp; + if( divb == 0.f ) + return(false); + + xac = pc.x - pa.x; + yac = pc.z - pa.z; + + barycoords.y = ( -yac * xcp + xac * ycp ) / divb; + if( barycoords.y < -EPS || barycoords.y > (1+EPS) ) + return(false); // small error tolerance + if( barycoords.y < 0.f ) + barycoords.y = 0.f; + if( barycoords.y > 1.f ) + barycoords.y = 1.f; + +// barycoords.x = 1.f - barycoords.y; + + if( xcp != 0.f ) { + float div = -xac + barycoords.y * xab; + if( div == 0.f ) + return(false); // flat triangle + barycoords.z = 1.f - xcp / div ; + } else { + float div = -yac + barycoords.y * yab; + if( div == 0.f ) + return(false); // flat triangle + barycoords.z = 1.f - ycp / div ; + } + + if( barycoords.z < -EPS || barycoords.z > (1+EPS) ) + return(false); +// if( barycoords.z < 0.f ) +// barycoords.z = 0.f; +// if( barycoords.z > 1.f ) +// barycoords.z = 1.f; + +// barycoords.x *= 1.f - barycoords.z; +// barycoords.y *= 1.f - barycoords.z; + + return(true); +*/ + +/* + Yet another method adapted from this code: + Vec3 pa1 = pa - p; + Vec3 pa2 = pb - p; + float d = pa1.cross(pa2).dot(n); + if (d < 0) return false; + Vec3 pa3 = pb - p; + d = pa2.cross(pa3).dot(n); + if (d < 0) return false; + d = pa3.cross(pa1).dot(n); + if (d < 0) return false; + return true; +*/ + + //in practice, this seems to actually take longer + //than the arc cosine method above... + n.x = -n.x; + n.y = -n.y; + n.z = -n.z; + VERTEX pa1,pa2,pa3, tmp; + float t; + + //pa1 = pa - p + pa1.x = pa.x - p->x; + pa1.y = pa.y - p->y; + pa1.z = pa.z - p->z; + + //pa2 = pb - p + pa2.x = pb.x - p->x; + pa2.y = pb.y - p->y; + pa2.z = pb.z - p->z; + + //tmp = pa1 cross pa2 + tmp.x = pa1.y * pa2.z - pa1.z * pa2.y; + tmp.y = pa1.z * pa2.x - pa1.x * pa2.z; + tmp.z = pa1.x * pa2.y - pa1.y * pa2.x; + + //t = tmp dot n + t = tmp.x * n.x + tmp.y * n.y + tmp.z * n.z; + if(t < 0) + return(false); +//printf("t = %f\n", t); + + //pa3 = pb - p + pa3.x = pc.x - p->x; + pa3.y = pc.y - p->y; + pa3.z = pc.z - p->z; + + //tmp = pa2 cross pa3 + tmp.x = pa2.y * pa3.z - pa2.z * pa3.y; + tmp.y = pa2.z * pa3.x - pa2.x * pa3.z; + tmp.z = pa2.x * pa3.y - pa2.y * pa3.x; + + //t = tmp dot n + t = tmp.x * n.x + tmp.y * n.y + tmp.z * n.z; + if(t < 0) + return(false); +//printf("t = %f\n", t); + + //tmp = pa3 cross pa1 + tmp.x = pa3.y * pa1.z - pa3.z * pa1.y; + tmp.y = pa3.z * pa1.x - pa3.x * pa1.z; + tmp.z = pa3.x * pa1.y - pa3.y * pa1.x; + + //t = tmp dot n + t = tmp.x * n.x + tmp.y * n.y + tmp.z * n.z; + if(t < 0) + return(false); +//printf("t = %f\n", t); + + return(true); +} + +void Map::Normalize(VERTEX *p) { + float len = sqrtf(p->x*p->x + p->y*p->y + p->z*p->z); + p->x /= len; + p->y /= len; + p->z /= len; +} + +bool Map::LineIntersectsZoneNoZLeaps(VERTEX start, VERTEX end, float step_mag, VERTEX *result, FACE **on) { + float z = -999999; + VERTEX step; + VERTEX cur; + cur.x = start.x; + cur.y = start.y; + cur.z = start.z; + + step.x = end.x - start.x; + step.y = end.y - start.y; + step.z = end.z - start.z; + float factor = step_mag / sqrt(step.x*step.x + step.y*step.y + step.z*step.z); + + step.x *= factor; + step.y *= factor; + step.z *= factor; + + int steps = 0; + + if (step.x > 0 && step.x < 0.001f) + step.x = 0.001f; + if (step.y > 0 && step.y < 0.001f) + step.y = 0.001f; + if (step.z > 0 && step.z < 0.001f) + step.z = 0.001f; + if (step.x < 0 && step.x > -0.001f) + step.x = -0.001f; + if (step.y < 0 && step.y > -0.001f) + step.y = -0.001f; + if (step.z < 0 && step.z > -0.001f) + step.z = -0.001f; + + NodeRef cnode, lnode; + lnode = 0; + //while we are not past end + //always do this once, even if start == end. + while(cur.x != end.x || cur.y != end.y || cur.z != end.z) + { + steps++; + cnode = SeekNode(GetRoot(), cur.x, cur.y); + if (cnode == NODE_NONE) + { + return(true); + } + VERTEX me; + me.x = cur.x; + me.y = cur.y; + me.z = cur.z; + VERTEX hit; + FACE *onhit; + float best_z = zone->zonemap->FindBestZ(cnode, me, &hit, &onhit); + float diff = ABS(best_z-z); +// diff *= sign(diff); + if (z == -999999 || best_z == -999999 || diff < 12.0) + z = best_z; + else + return(true); + //look at current location + if(cnode != NODE_NONE && cnode != lnode) { + if(LineIntersectsNode(cnode, start, end, result, on)) + { + return(true); + } + lnode = cnode; + } + + //move 1 step + if (cur.x != end.x) + cur.x += step.x; + if (cur.y != end.y) + cur.y += step.y; + if (cur.z != end.z) + cur.z += step.z; + + //watch for end conditions + if ( (cur.x > end.x && end.x >= start.x) || (cur.x < end.x && end.x <= start.x) || (step.x == 0) ) { + cur.x = end.x; + } + if ( (cur.y > end.y && end.y >= start.y) || (cur.y < end.y && end.y <= start.y) || (step.y == 0) ) { + cur.y = end.y; + } + if ( (cur.z > end.z && end.z >= start.z) || (cur.z < end.z && end.z < start.z) || (step.z == 0) ) { + cur.z = end.z; + } + } + + //walked entire line and didnt run into anything... + return(false); +} + +float Map::FindClosestZ(VERTEX p ) const +{ + // Unlike FindBestZ, this method finds the closest Z value above or below the specified point. + // + std::list ZSet; + + NodeRef NodeR = SeekNode(MAP_ROOT_NODE, p.x, p.y); + + if( NodeR == NODE_NONE || NodeR >= m_Nodes) + return 0; + + PNODE CurrentNode = &mNodes[NodeR]; + + if(!(CurrentNode->flags & nodeFinal)) + return 0; + + VERTEX p1(p), p2(p), Result; + + p1.z = 999999; + + p2.z = BEST_Z_INVALID; + + const uint32 *CurrentFaceList = mFaceLists + CurrentNode->faces.offset; + + for(unsigned long i = 0; i < CurrentNode->faces.count; ++i) + { + if(*CurrentFaceList > m_Faces) + continue; + + PFACE CurrentFace = &mFinalFaces[ *CurrentFaceList ]; + + if(CurrentFace->nz > 0 && LineIntersectsFace(CurrentFace, p1, p2, &Result)) + ZSet.push_back(Result.z); + + CurrentFaceList++; + + } + if(ZSet.size() == 0) + return 0; + + if(ZSet.size() == 1) + return ZSet.front(); + + float ClosestZ = -999999; + + for(list::iterator Iterator = ZSet.begin(); Iterator != ZSet.end(); ++Iterator) + { + if(ABS(p.z - (*Iterator)) < ABS(p.z - ClosestZ)) + ClosestZ = (*Iterator); + } + + return ClosestZ; +} + diff --git a/zone/MobAI.cpp b/zone/MobAI.cpp new file mode 100644 index 000000000..eeaad192a --- /dev/null +++ b/zone/MobAI.cpp @@ -0,0 +1,2419 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2004 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 "../common/debug.h" +#include +using namespace std; +#include +using namespace std; +#include +#include +#include +#include "npc.h" +#include "masterentity.h" +#include "NpcAI.h" +#include "map.h" +#include "../common/moremath.h" +#include "parser.h" +#include "StringIDs.h" +#include "../common/MiscFunctions.h" +#include "../common/rulesys.h" +#include "features.h" +#include "QuestParserCollection.h" +#include "watermap.h" + +#if !defined(NEW_LoadSPDat) && !defined(DB_LoadSPDat) + extern SPDat_Spell_Struct spells[SPDAT_RECORDS]; +#endif + +extern EntityList entity_list; + +extern Zone *zone; + +#ifdef _EQDEBUG + #define MobAI_DEBUG_Spells -1 +#else + #define MobAI_DEBUG_Spells -1 +#endif +#define ABS(x) ((x)<0?-(x):(x)) + +//NOTE: do NOT pass in beneficial and detrimental spell types into the same call here! +bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { + _ZP(Mob_AICastSpell); +// Faction isnt checked here, it's assumed you wouldnt pass a spell type you wouldnt want casted on the mob + if (!tar) + return false; + + if (IsNoCast()) + return false; + + if(AI_HasSpells() == false) + return false; + + if (iChance < 100) { + if (MakeRandomInt(0, 100) >= iChance) + return false; + } + + float dist2; + + if (iSpellTypes & SpellType_Escape) { + dist2 = 0; //DistNoRoot(*this); //WTF was up with this... + } else + dist2 = DistNoRoot(*tar); + + bool checked_los = false; //we do not check LOS until we are absolutely sure we need to, and we only do it once. + + float manaR = GetManaRatio(); + for (int i = static_cast(AIspells.size()) - 1; i >= 0; i--) { + if (AIspells[i].spellid <= 0 || AIspells[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + //return false; + continue; + } + if (iSpellTypes & AIspells[i].type) { + // manacost has special values, -1 is no mana cost, -2 is instant cast (no mana) + int32 mana_cost = AIspells[i].manacost; + if (mana_cost == -1) + mana_cost = spells[AIspells[i].spellid].mana; + else if (mana_cost == -2) + mana_cost = 0; + if ( + (( + (spells[AIspells[i].spellid].targettype==ST_AECaster || spells[AIspells[i].spellid].targettype==ST_AEBard) + && dist2 <= spells[AIspells[i].spellid].aoerange*spells[AIspells[i].spellid].aoerange + ) || + dist2 <= spells[AIspells[i].spellid].range*spells[AIspells[i].spellid].range + ) + && (mana_cost <= GetMana() || GetMana() == GetMaxMana()) + && (AIspells[i].time_cancast+(MakeRandomInt(0, 4))) <= Timer::GetCurrentTime() //break up the spelling casting over a period of time. + ) { + +#if MobAI_DEBUG_Spells >= 21 + cout << "Mob::AICastSpell: Casting: spellid=" << AIspells[i].spellid + << ", tar=" << tar->GetName() + << ", dist2[" << dist2 << "]<=" << spells[AIspells[i].spellid].range *spells[AIspells[i].spellid].range + << ", mana_cost[" << mana_cost << "]<=" << GetMana() + << ", cancast[" << AIspells[i].time_cancast << "]<=" << Timer::GetCurrentTime() + << ", type=" << AIspells[i].type << endl; +#endif + + switch (AIspells[i].type) { + case SpellType_Heal: { + if ( + (spells[AIspells[i].spellid].targettype == ST_Target || tar == this) + && tar->DontHealMeBefore() < Timer::GetCurrentTime() + && !(tar->IsPet() && tar->GetOwner()->IsClient()) //no buffing PC's pets + ) { + uint8 hpr = (uint8)tar->GetHPRatio(); + + if(hpr <= 35 || (!IsEngaged() && hpr <= 50) || (tar->IsClient() && hpr <= 99)) { + uint32 tempTime = 0; + AIDoSpellCast(i, tar, mana_cost, &tempTime); + tar->SetDontHealMeBefore(tempTime); + return true; + } + } + break; + } + case SpellType_Root: { + if ( + !tar->IsRooted() + && dist2 >= 900 + && MakeRandomInt(0, 99) < 50 + && tar->DontRootMeBefore() < Timer::GetCurrentTime() + && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 + ) { + if(!checked_los) { + if(!CheckLosFN(tar)) + return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + checked_los = true; + } + uint32 tempTime = 0; + AIDoSpellCast(i, tar, mana_cost, &tempTime); + tar->SetDontRootMeBefore(tempTime); + return true; + } + break; + } + case SpellType_Buff: { + if ( + (spells[AIspells[i].spellid].targettype == ST_Target || tar == this) + && tar->DontBuffMeBefore() < Timer::GetCurrentTime() + && !tar->IsImmuneToSpell(AIspells[i].spellid, this) + && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 + && !(tar->IsPet() && tar->GetOwner()->IsClient() && this != tar) //no buffing PC's pets, but they can buff themself + ) + { + if(!checked_los) { + if(!CheckLosFN(tar)) + return(false); + checked_los = true; + } + uint32 tempTime = 0; + AIDoSpellCast(i, tar, mana_cost, &tempTime); + tar->SetDontBuffMeBefore(tempTime); + return true; + } + break; + } + + case SpellType_InCombatBuff: { + if(MakeRandomInt(0,100) < 50) + { + AIDoSpellCast(i, tar, mana_cost); + return true; + } + break; + } + + case SpellType_Escape: { + if (GetHPRatio() <= 5 ) + { + AIDoSpellCast(i, tar, mana_cost); + return true; + } + break; + } + case SpellType_Slow: + case SpellType_Debuff: + case SpellType_Nuke: { + if ( + manaR >= 10 && MakeRandomInt(0, 99) < 70 + && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 + ) { + if(!checked_los) { + if(!CheckLosFN(tar)) + return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + checked_los = true; + } + AIDoSpellCast(i, tar, mana_cost); + return true; + } + break; + } + case SpellType_Dispel: { + if(MakeRandomInt(0, 100) < 15) + { + if(!checked_los) { + if(!CheckLosFN(tar)) + return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + checked_los = true; + } + if(tar->CountDispellableBuffs() > 0) + { + AIDoSpellCast(i, tar, mana_cost); + return true; + } + } + break; + } + case SpellType_Mez: { + if(MakeRandomInt(0, 99) < 20) + { + Mob * mezTar = NULL; + mezTar = entity_list.GetTargetForMez(this); + + if(mezTar && mezTar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0) + { + AIDoSpellCast(i, mezTar, mana_cost); + return true; + } + } + break; + } + + case SpellType_Charm: + { + if(MakeRandomInt(0, 99) < 20) + { + Mob * chrmTar = GetHateRandom(); + if(chrmTar && chrmTar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0) + { + AIDoSpellCast(i, chrmTar, mana_cost); + return true; + } + break; + } + } + + case SpellType_Pet: { + //keep mobs from recasting pets when they have them. + if (!IsPet() && !GetPetID() && MakeRandomInt(0, 99) < 25) { + AIDoSpellCast(i, tar, mana_cost); + return true; + } + break; + } + case SpellType_Lifetap: { + if ( GetHPRatio() <= 95 + && MakeRandomInt(0, 99) < 50 + && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 + ) { + if(!checked_los) { + if(!CheckLosFN(tar)) + return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + checked_los = true; + } + AIDoSpellCast(i, tar, mana_cost); + return true; + } + break; + } + case SpellType_Snare: { + if ( + !tar->IsRooted() + && MakeRandomInt(0, 99) < 50 + && tar->DontSnareMeBefore() < Timer::GetCurrentTime() + && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 + ) { + if(!checked_los) { + if(!CheckLosFN(tar)) + return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + checked_los = true; + } + uint32 tempTime = 0; + AIDoSpellCast(i, tar, mana_cost, &tempTime); + tar->SetDontSnareMeBefore(tempTime); + return true; + } + break; + } + case SpellType_DOT: { + if ( + MakeRandomInt(0, 99) < 60 + && tar->DontDotMeBefore() < Timer::GetCurrentTime() + && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 + ) { + if(!checked_los) { + if(!CheckLosFN(tar)) + return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + checked_los = true; + } + uint32 tempTime = 0; + AIDoSpellCast(i, tar, mana_cost, &tempTime); + tar->SetDontDotMeBefore(tempTime); + return true; + } + break; + } + default: { + cout<<"Error: Unknown spell type in AICastSpell. caster:"<GetName()<<" type:"<= 21 + else { + cout << "Mob::AICastSpell: NotCasting: spellid=" << AIspells[i].spellid << ", tar=" << tar->GetName() << ", dist2[" << dist2 << "]<=" << spells[AIspells[i].spellid].range*spells[AIspells[i].spellid].range << ", mana_cost[" << mana_cost << "]<=" << GetMana() << ", cancast[" << AIspells[i].time_cancast << "]<=" << Timer::GetCurrentTime() << endl; + } +#endif + } + } + return false; +} + +bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore) { +#if MobAI_DEBUG_Spells >= 1 + cout << "Mob::AIDoSpellCast: spellid=" << AIspells[i].spellid << ", tar=" << tar->GetName() << ", mana=" << mana_cost << ", Name: " << spells[AIspells[i].spellid].name << endl; +#endif + casting_spell_AIindex = i; + + //stop moving if were casting a spell and were not a bard... + if(!IsBardSong(AIspells[i].spellid)) { + SetRunAnimSpeed(0); + SendPosition(); + SetMoving(false); + } + + return CastSpell(AIspells[i].spellid, tar->GetID(), 1, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0, &(AIspells[i].resist_adjust)); +} + +bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint16 iSpellTypes) { + _ZP(EntityList_AICheckCloseBeneficialSpells); + + if((iSpellTypes&SpellTypes_Detrimental) != 0) { + //according to live, you can buff and heal through walls... + //now with PCs, this only applies if you can TARGET the target, but + // according to Rogean, Live NPCs will just cast through walls/floors, no problem.. + // + // This check was put in to address an idle-mob CPU issue + _log(AI__ERROR, "Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); + return(false); + } + + if(!caster) + return false; + + if(caster->AI_HasSpells() == false) + return false; + + if(caster->SpecAttacks[NPC_NO_BUFFHEAL_FRIENDS]) + return false; + + if (iChance < 100) { + uint8 tmp = MakeRandomInt(0, 99); + if (tmp >= iChance) + return false; + } + if (caster->GetPrimaryFaction() == 0 ) + return(false); // well, if we dont have a faction set, we're gonna be indiff to everybody + + float iRange2 = iRange*iRange; + + float t1, t2, t3; + + + //Only iterate through NPCs + LinkedListIterator iterator(npc_list); + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) { + NPC* mob = iterator.GetData(); + + //Since >90% of mobs will always be out of range, try to + //catch them with simple bounding box checks first. These + //checks are about 6X faster than DistNoRoot on my athlon 1Ghz + t1 = mob->GetX() - caster->GetX(); + t2 = mob->GetY() - caster->GetY(); + t3 = mob->GetZ() - caster->GetZ(); + //cheap ABS() + if(t1 < 0) + t1 = 0 - t1; + if(t2 < 0) + t2 = 0 - t2; + if(t3 < 0) + t3 = 0 - t3; + if ( t1 > iRange + || t2 > iRange + || t3 > iRange + || mob->DistNoRoot(*caster) > iRange2 + //this call should seem backwards: + || !mob->CheckLosFN(caster) + || mob->GetReverseFactionCon(caster) >= FACTION_KINDLY + ) { + continue; + } + + //since we assume these are beneficial spells, which do not + //require LOS, we just go for it. + // we have a winner! + if((iSpellTypes & SpellType_Buff) && !RuleB(NPC, BuffFriends)){ + if (mob != caster) + iSpellTypes = SpellType_Heal; + } + + if (caster->AICastSpell(mob, 100, iSpellTypes)) + return true; + } + return false; +} + +void Mob::AI_Init() { + pAIControlled = false; + AIthink_timer = 0; + AIwalking_timer = 0; + AImovement_timer = 0; + AItarget_check_timer = 0; + AIfeignremember_timer = NULL; + AIscanarea_timer = 0; + minLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMin); + maxLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMax); + + pDontHealMeBefore = 0; + pDontBuffMeBefore = 0; + pDontDotMeBefore = 0; + pDontRootMeBefore = 0; + pDontSnareMeBefore = 0; + pDontCureMeBefore = 0; +} + +void NPC::AI_Init() { + Mob::AI_Init(); + + AIautocastspell_timer = 0; + casting_spell_AIindex = static_cast(AIspells.size()); + + roambox_max_x = 0; + roambox_max_y = 0; + roambox_min_x = 0; + roambox_min_y = 0; + roambox_distance = 0; + roambox_movingto_x = 0; + roambox_movingto_y = 0; + roambox_delay = 2500; +} + +void Client::AI_Init() { + Mob::AI_Init(); + minLastFightingDelayMoving = CLIENT_LD_TIMEOUT; + maxLastFightingDelayMoving = CLIENT_LD_TIMEOUT; +} + +void Mob::AI_Start(uint32 iMoveDelay) { + if (pAIControlled) + return; + + if (iMoveDelay) + pLastFightingDelayMoving = Timer::GetCurrentTime() + iMoveDelay; + else + pLastFightingDelayMoving = 0; + + pAIControlled = true; + AIthink_timer = new Timer(AIthink_duration); + AIthink_timer->Trigger(); + AIwalking_timer = new Timer(0); + AImovement_timer = new Timer(AImovement_duration); + AItarget_check_timer = new Timer(AItarget_check_duration); + AIfeignremember_timer = new Timer(AIfeignremember_delay); + AIscanarea_timer = new Timer(AIscanarea_delay); +#ifdef REVERSE_AGGRO + if(IsNPC() && !CastToNPC()->WillAggroNPCs()) + AIscanarea_timer->Disable(); +#endif + + if (GetAggroRange() == 0) + pAggroRange = 70; + if (GetAssistRange() == 0) + pAssistRange = 70; + hate_list.Wipe(); + + delta_heading = 0; + delta_x = 0; + delta_y = 0; + delta_z = 0; + pRunAnimSpeed = 0; + pLastChange = Timer::GetCurrentTime(); +} + +void Client::AI_Start(uint32 iMoveDelay) { + Mob::AI_Start(iMoveDelay); + + if (!pAIControlled) + return; + + pClientSideTarget = GetTarget() ? GetTarget()->GetID() : 0; + SendAppearancePacket(AT_Anim, ANIM_FREEZE); // this freezes the client + SendAppearancePacket(AT_Linkdead, 1); // Sending LD packet so *LD* appears by the player name when charmed/feared -Kasai + SetAttackTimer(); + SetFeigned(false); +} + +void NPC::AI_Start(uint32 iMoveDelay) { + Mob::AI_Start(iMoveDelay); + if (!pAIControlled) + return; + + if (AIspells.size() == 0) { + AIautocastspell_timer = new Timer(1000); + AIautocastspell_timer->Disable(); + } else { + AIautocastspell_timer = new Timer(750); + AIautocastspell_timer->Start(RandomTimer(0, 15000), false); + } + + if (NPCTypedata) { + AI_AddNPCSpells(NPCTypedata->npc_spells_id); + NPCSpecialAttacks(NPCTypedata->npc_attacks,0); + } + + SendTo(GetX(), GetY(), GetZ()); + SetChanged(); + SaveGuardSpot(); +} + +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(AIscanarea_timer); + safe_delete(AIfeignremember_timer); + hate_list.Wipe(); +} + +void NPC::AI_Stop() { + Waypoints.clear(); + safe_delete(AIautocastspell_timer); +} + +void Client::AI_Stop() { + Mob::AI_Stop(); + this->Message_StringID(13,PLAYER_REGAIN); + + EQApplicationPacket *app = new EQApplicationPacket(OP_Charm, sizeof(Charm_Struct)); + Charm_Struct *ps = (Charm_Struct*)app->pBuffer; + ps->owner_id = 0; + ps->pet_id = this->GetID(); + ps->command = 0; + entity_list.QueueClients(this, app); + safe_delete(app); + + SetTarget(entity_list.GetMob(pClientSideTarget)); + SendAppearancePacket(AT_Anim, GetAppearanceValue(GetAppearance())); + SendAppearancePacket(AT_Linkdead, 0); // Removing LD packet so *LD* no longer appears by the player name when charmed/feared -Kasai + if (!auto_attack) { + attack_timer.Disable(); + attack_dw_timer.Disable(); + } + if (IsLD()) + { + Save(); + Disconnect(); + } +} + +//todo: expand the logic here to cover: +//redundant debuffs +//buffing owner +//certain types of det spells that need special behavior. +void Client::AI_SpellCast() +{ + if(!charm_cast_timer.Check()) + return; + + Mob *targ = GetTarget(); + if(!targ) + return; + + float dist = DistNoRootNoZ(*targ); + + std::vector valid_spells; + std::vector slots; + + for(uint32 x = 0; x < 9; ++x) + { + uint32 current_spell = m_pp.mem_spells[x]; + if(!IsValidSpell(current_spell)) + continue; + + if(IsBeneficialSpell(current_spell)) + { + continue; + } + + if(dist > spells[current_spell].range*spells[current_spell].range) + { + continue; + } + + if(GetMana() < spells[current_spell].mana) + { + continue; + } + + if(IsEffectInSpell(current_spell, SE_Charm)) + { + continue; + } + + if(!GetPTimers().Expired(&database, pTimerSpellStart + current_spell, false)) + { + continue; + } + + if(targ->CanBuffStack(current_spell, GetLevel(), true) < 0) + { + continue; + } + + //bard songs cause trouble atm + if(IsBardSong(current_spell)) + continue; + + valid_spells.push_back(current_spell); + slots.push_back(x); + } + + uint32 spell_to_cast = 0xFFFFFFFF; + uint32 slot_to_use = 10; + if(valid_spells.size() == 1) + { + spell_to_cast = valid_spells[0]; + slot_to_use = slots[0]; + } + else if(valid_spells.size() == 0) + { + return; + } + else + { + uint32 idx = MakeRandomInt(0, (valid_spells.size()-1)); + spell_to_cast = valid_spells[idx]; + slot_to_use = slots[idx]; + } + + if(IsMezSpell(spell_to_cast) || IsFearSpell(spell_to_cast)) + { + Mob *tar = entity_list.GetTargetForMez(this); + if(!tar) + { + tar = GetTarget(); + if(tar && IsFearSpell(spell_to_cast)) + { + if(!IsBardSong(spell_to_cast)) + { + SetRunAnimSpeed(0); + SendPosition(); + SetMoving(false); + } + CastSpell(spell_to_cast, tar->GetID(), slot_to_use); + return; + } + } + } + else + { + Mob *tar = GetTarget(); + if(tar) + { + if(!IsBardSong(spell_to_cast)) + { + SetRunAnimSpeed(0); + SendPosition(); + SetMoving(false); + } + CastSpell(spell_to_cast, tar->GetID(), slot_to_use); + return; + } + } + + +} + +void Client::AI_Process() +{ + if (!IsAIControlled()) + return; + + if (!(AIthink_timer->Check() || attack_timer.Check(false))) + return; + + if (IsCasting()) + return; + + bool engaged = IsEngaged(); + + Mob *ow = GetOwner(); + if(!engaged) + { + if(ow) + { + if(ow->IsEngaged()) + { + Mob *tar = ow->GetTarget(); + if(tar) + { + AddToHateList(tar, 1, 0, false); + } + } + } + } + + if(!ow) + { + if(!IsFeared() && !IsLD()) + { + BuffFadeByEffect(SE_Charm); + return; + } + } + + if(RuleB(Combat, EnableFearPathing)){ + if(curfp) { + if(IsRooted()) { + //make sure everybody knows were not moving, for appearance sake + if(IsMoving()) + { + if(GetTarget()) + SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); + SetRunAnimSpeed(0); + SendPosition(); + SetMoving(false); + moved=false; + } + //continue on to attack code, ensuring that we execute the engaged code + engaged = true; + } else { + if(AImovement_timer->Check()) { + animation = GetRunspeed() * 21; + // Check if we have reached the last fear point + if((ABS(GetX()-fear_walkto_x) < 0.1) && (ABS(GetY()-fear_walkto_y) <0.1)) { + // Calculate a new point to run to + CalculateNewFearpoint(); + } + if(!RuleB(Pathing, Fear) || !zone->pathing) + CalculateNewPosition2(fear_walkto_x, fear_walkto_y, fear_walkto_z, GetFearSpeed(), true); + else + { + bool WaypointChanged, NodeReached; + + VERTEX Goal = UpdatePath(fear_walkto_x, fear_walkto_y, fear_walkto_z, + GetFearSpeed(), WaypointChanged, NodeReached); + + if(WaypointChanged) + tar_ndx = 20; + + CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetFearSpeed()); + } + } + return; + } + } + } + + if (engaged) + { + if (IsRooted()) + SetTarget(hate_list.GetClosest(this)); + else + { + if(AItarget_check_timer->Check()) + { + SetTarget(hate_list.GetTop(this)); + } + } + + if (!GetTarget()) + return; + + if (GetTarget()->IsCorpse()) { + RemoveFromHateList(this); + return; + } + + if(DivineAura()) + return; + + bool is_combat_range = CombatRange(GetTarget()); + + if(is_combat_range) { + if(charm_class_attacks_timer.Check()) { + DoClassAttacks(GetTarget()); + } + + if (AImovement_timer->Check()) { + SetRunAnimSpeed(0); + } + if(IsMoving()) { + SetMoving(false); + moved=false; + SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); + SendPosition(); + tar_ndx =0; + } + + if(GetTarget() && !IsStunned() && !IsMezzed() && !GetFeigned()) { + if(attack_timer.Check()) { + Attack(GetTarget(), 13); + if(GetTarget()) { + if(CheckDoubleAttack()) { + Attack(GetTarget(), 13); + if(GetTarget()) { + bool triple_attack_success = false; + if((((GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER) + && GetLevel() >= 60) || SpecAttacks[SPECATK_TRIPLE]) + && CheckDoubleAttack(true)) + { + Attack(GetTarget(), 13, true); + triple_attack_success = true; + } + + if(GetTarget()) + { + //Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack). + int16 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; + + if (flurrychance) + { + if(MakeRandomInt(0, 100) < flurrychance) + { + Message_StringID(MT_NPCFlurry, 128); + Attack(GetTarget(), 13, false); + Attack(GetTarget(), 13, false); + } + } + + int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; + + if (ExtraAttackChanceBonus && GetTarget()) { + ItemInst *wpn = GetInv().GetItem(SLOT_PRIMARY); + if(wpn){ + if(wpn->GetItem()->ItemType == ItemType2HS || + wpn->GetItem()->ItemType == ItemType2HB || + wpn->GetItem()->ItemType == ItemType2HPierce ) + { + if(MakeRandomInt(0, 100) < ExtraAttackChanceBonus) + { + Attack(GetTarget(), 13, false); + } + } + } + } + + if (GetClass() == WARRIOR || GetClass() == BERSERKER) + { + if(!dead && !berserk && this->GetHPRatio() < 30) + { + entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_START, GetName()); + berserk = true; + } + else if (berserk && this->GetHPRatio() > 30) + { + entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_END, GetName()); + berserk = false; + } + } + } + } + } + } + } + } + + if(CanThisClassDualWield() && attack_dw_timer.Check()) + { + if(GetTarget()) + { + float DualWieldProbability = 0.0f; + + int16 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity; + DualWieldProbability = (GetSkill(DUAL_WIELD) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max + int16 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance; + DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f; + + if(MakeRandomFloat(0.0, 1.0) < DualWieldProbability) + { + Attack(GetTarget(), 14); + if(CheckDoubleAttack()) + { + Attack(GetTarget(), 14); + } + + } + } + } + } + else + { + if(!IsRooted()) + { + animation = 21 * GetRunspeed(); + if(!RuleB(Pathing, Aggro) || !zone->pathing) + CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed()); + else + { + bool WaypointChanged, NodeReached; + VERTEX Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), + GetRunspeed(), WaypointChanged, NodeReached); + + if(WaypointChanged) + tar_ndx = 20; + + CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed()); + } + } + else if(IsMoving()) + { + SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); + SetRunAnimSpeed(0); + SendPosition(); + SetMoving(false); + moved=false; + } + } + AI_SpellCast(); + } + else + { + if(AIfeignremember_timer->Check()) { + std::set::iterator RememberedCharID, tmp; + RememberedCharID=feign_memory_list.begin(); + bool got_one = false; + while(RememberedCharID != feign_memory_list.end()) { + Client* remember_client = entity_list.GetClientByCharID(*RememberedCharID); + if(remember_client == NULL) { + //they are gone now... + tmp = RememberedCharID; + RememberedCharID++; + feign_memory_list.erase(tmp); + } else if (!remember_client->GetFeigned()) { + AddToHateList(remember_client->CastToMob(),1); + tmp = RememberedCharID; + RememberedCharID++; + feign_memory_list.erase(tmp); + got_one = true; + break; + } else { + //they are still feigned, carry on... + RememberedCharID++; + } + } + } + + if(IsPet()) + { + Mob* owner = GetOwner(); + if(owner == NULL) + return; + + float dist = DistNoRoot(*owner); + if (dist >= 100) + { + float speed = dist >= 225 ? GetRunspeed() : GetWalkspeed(); + animation = 21 * speed; + CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed); + } + else + { + SetHeading(owner->GetHeading()); + if(moved) + { + moved=false; + SetMoving(false); + SendPosition(); + SetRunAnimSpeed(0); + } + } + } + } +} + +void Mob::AI_Process() { + _ZP(Mob_AI_Process); + + if (!IsAIControlled()) + return; + + if (!(AIthink_timer->Check() || attack_timer.Check(false))) + return; + + if (IsCasting()) + return; + + bool engaged = IsEngaged(); + bool doranged = false; + + // Begin: Additions for Wiz Fear Code + // + if(RuleB(Combat, EnableFearPathing)){ + if(curfp) { + if(IsRooted()) { + //make sure everybody knows were not moving, for appearance sake + if(IsMoving()) + { + if(target) + SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY())); + SetRunAnimSpeed(0); + SendPosition(); + SetMoving(false); + moved=false; + } + //continue on to attack code, ensuring that we execute the engaged code + engaged = true; + } else { + if(AImovement_timer->Check()) { + // Check if we have reached the last fear point + if((ABS(GetX()-fear_walkto_x) < 0.1) && (ABS(GetY()-fear_walkto_y) <0.1)) { + // Calculate a new point to run to + CalculateNewFearpoint(); + } + if(!RuleB(Pathing, Fear) || !zone->pathing) + CalculateNewPosition2(fear_walkto_x, fear_walkto_y, fear_walkto_z, GetFearSpeed(), true); + else + { + bool WaypointChanged, NodeReached; + + VERTEX Goal = UpdatePath(fear_walkto_x, fear_walkto_y, fear_walkto_z, + GetFearSpeed(), WaypointChanged, NodeReached); + + if(WaypointChanged) + tar_ndx = 20; + + CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetFearSpeed()); + } + } + return; + } + } + } + + // trigger EVENT_SIGNAL if required + if(IsNPC()) { + CastToNPC()->CheckSignal(); + } + + if (engaged) + { + _ZP(Mob_AI_Process_engaged); + if (IsRooted()) + SetTarget(hate_list.GetClosest(this)); + else + { + if(AItarget_check_timer->Check()) + { + if (IsFocused()) { + if (!target) { + SetTarget(hate_list.GetTop(this)); + } + } else { + SetTarget(hate_list.GetTop(this)); + } + + } + } + + if (!target) + return; + + if (target->IsCorpse()) + { + RemoveFromHateList(this); + return; + } + + if(DivineAura()) + return; + + if(SpecAttacks[TETHER] || SpecAttacks[LEASH]) { + if(DistNoRootNoZ(CastToNPC()->GetSpawnPointX(), CastToNPC()->GetSpawnPointY()) > pAggroRange*pAggroRange) { + GMMove(CastToNPC()->GetSpawnPointX(), CastToNPC()->GetSpawnPointY(), CastToNPC()->GetSpawnPointZ(), CastToNPC()->GetSpawnPointH()); + if(SpecAttacks[LEASH]) { + SetHP(GetMaxHP()); + BuffFadeAll(); + WipeHateList(); + return; + } + } + } + + if (GetHPRatio() < (RuleI(NPC, StartEnrageValue)+1) && + (!RuleB(NPC, LiveLikeEnrage) || + (RuleB(NPC, LiveLikeEnrage) && + ((IsPet() && !IsCharmed() && GetOwner() && GetOwner()->IsClient()) || + (CastToNPC()->GetSwarmOwner() && entity_list.GetMob(CastToNPC()->GetSwarmOwner())->IsClient()))))) + { + StartEnrage(); + } + + bool is_combat_range = CombatRange(target); + + if (is_combat_range) + { + if (AImovement_timer->Check()) + { + SetRunAnimSpeed(0); + } + if(IsMoving()) + { + SetMoving(false); + moved=false; + SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY())); + SendPosition(); + tar_ndx =0; + } + + //casting checked above... + if(target && !IsStunned() && !IsMezzed() && GetAppearance() != eaDead && !IsMeleeDisabled()) { + + //we should check to see if they die mid-attacks, previous + //crap of checking target for null was not gunna cut it + + //try main hand first + if(attack_timer.Check()) { + if(IsNPC()) { + int16 n_atk = CastToNPC()->GetNumberOfAttacks(); + if(n_atk <= 1) { + Attack(target, 13); + } else { + for(int i = 0; i < n_atk; ++i) { + Attack(target, 13); + } + } + } else { + Attack(target, 13); + } + + if (target) + { + //we use this random value in three comparisons with different + //thresholds, and if its truely random, then this should work + //out reasonably and will save us compute resources. + int32 RandRoll = MakeRandomInt(0, 99); + if (CanThisClassDoubleAttack() + //check double attack, this is NOT the same rules that clients use... + && RandRoll < (GetLevel() + NPCDualAttackModifier)) + { + if (Attack(target, 13)) + { + // lets see if we can do a triple attack with the main hand + //pets are excluded from triple and quads... + if (SpecAttacks[SPECATK_TRIPLE] + && !IsPet() && RandRoll < (GetLevel()+NPCTripleAttackModifier)) + { + if (Attack(target, 13)) + { // now lets check the quad attack + if (SpecAttacks[SPECATK_QUAD] + && RandRoll < (GetLevel() + NPCQuadAttackModifier)) + { + Attack(target, 13); + } + } + } + } + } + } + + if (SpecAttacks[SPECATK_FLURRY]) { + + uint8 npc_flurry = RuleI(Combat, NPCFlurryChance); + if (GetFlurryChance()) + npc_flurry = GetFlurryChance(); + + if (MakeRandomInt(0, 99) < npc_flurry) + Flurry(); + } + + if (IsPet()) { + + Mob *owner = GetOwner(); + + if (owner){ + int16 flurry_chance = owner->aabonuses.PetFlurry + owner->spellbonuses.PetFlurry + owner->itembonuses.PetFlurry; + + if (flurry_chance && (MakeRandomInt(0, 99) < flurry_chance)) + Flurry(); + } + } + + if (SpecAttacks[SPECATK_RAMPAGE]) + { + //simply based off dex for now, probably a better calc + if(MakeRandomInt(0, 100) < ((int)(GetDEX() / ((GetLevel() * 0.760) + 10.0)) + 5)) + Rampage(); + } + + if (SpecAttacks[SPECATK_AREA_RAMPAGE]) + { + + //simply based off dex for now, probably a better calc + if(MakeRandomInt(0, 100) < ((int)(GetDEX() / ((GetLevel() * 0.760) + 10.0)) + 5)) + AreaRampage(); + } + } + + //now off hand + if (attack_dw_timer.Check() && CanThisClassDualWield()) + { + int myclass = GetClass(); + //can only dual wield without a weapon if your a monk + if(SpecAttacks[SPECATK_INNATE_DW] || (GetEquipment(MATERIAL_SECONDARY) != 0 && GetLevel() > 29) || myclass == MONK || myclass == MONKGM) { + float DualWieldProbability = (GetSkill(DUAL_WIELD) + GetLevel()) / 400.0f; + if(MakeRandomFloat(0.0, 1.0) < DualWieldProbability) + { + Attack(target, 14); + if (CanThisClassDoubleAttack()) + { + int32 RandRoll = MakeRandomInt(0, 99); + if (RandRoll < (GetLevel() + 20)) + { + if (Attack(target, 14)); + } + } + } + } + } + + //now special attacks (kick, etc) + if(IsNPC()) + CastToNPC()->DoClassAttacks(target); + } + AI_EngagedCastCheck(); + } //end is within combat range + else { + //we cannot reach our target... + //underwater stuff only works with water maps in the zone! + if(IsNPC() && CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) { + if(!zone->watermap->InLiquid(target->GetX(), target->GetY(), target->GetZ())) { + Mob *tar = hate_list.GetTop(this); + if(tar == target) { + WipeHateList(); + Heal(); + BuffFadeAll(); + AIwalking_timer->Start(100); + pLastFightingDelayMoving = Timer::GetCurrentTime(); + return; + } else if(tar != NULL) { + SetTarget(tar); + return; + } + } + } + + // See if we can summon the mob to us + if (!HateSummon()) + { + //could not summon them, check ranged... + if(SpecAttacks[SPECATK_RANGED_ATK]) + doranged = true; + + // Now pursue + // TODO: Check here for another person on hate list with close hate value + if(AI_PursueCastCheck()){ + //we did something, so do not process movement. + } + else if (AImovement_timer->Check()) + { + if(!IsRooted()) { + mlog(AI__WAYPOINTS, "Pursuing %s while engaged.", target->GetName()); + if(!RuleB(Pathing, Aggro) || !zone->pathing) + CalculateNewPosition2(target->GetX(), target->GetY(), target->GetZ(), GetRunspeed()); + else + { + bool WaypointChanged, NodeReached; + + VERTEX Goal = UpdatePath(target->GetX(), target->GetY(), target->GetZ(), + GetRunspeed(), WaypointChanged, NodeReached); + + if(WaypointChanged) + tar_ndx = 20; + + CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed()); + } + + } + else if(IsMoving()) { + SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY())); + SetRunAnimSpeed(0); + SendPosition(); + SetMoving(false); + moved=false; + + } + } + } + } + } + else + { + if(AIfeignremember_timer->Check()) { + // EverHood - 6/14/06 + // Improved Feign Death Memory + // check to see if any of our previous feigned targets have gotten up. + std::set::iterator RememberedCharID, tmp; + RememberedCharID=feign_memory_list.begin(); + bool got_one = false; + while(RememberedCharID != feign_memory_list.end()) { + Client* remember_client = entity_list.GetClientByCharID(*RememberedCharID); + if(remember_client == NULL) { + //they are gone now... + tmp = RememberedCharID; + RememberedCharID++; + feign_memory_list.erase(tmp); + } else if (!remember_client->GetFeigned()) { + AddToHateList(remember_client->CastToMob(),1); + tmp = RememberedCharID; + RememberedCharID++; + feign_memory_list.erase(tmp); + got_one = true; + break; + } else { + //they are still feigned, carry on... + RememberedCharID++; + } + } + } + if (AI_IdleCastCheck()) + { + //we processed a spell action, so do nothing else. + } + else if (AIscanarea_timer->Check()) + { + /* + * This is where NPCs look around to see if they want to attack anybody. + * + * if REVERSE_AGGRO is enabled, then this timer is disabled unless they + * have the npc_aggro flag on them, and aggro against clients is checked + * by the clients. + * + */ + _ZP(Mob_AI_Process_scanarea); + + Mob* tmptar = entity_list.AICheckCloseAggro(this, GetAggroRange(), GetAssistRange()); + if (tmptar) + AddToHateList(tmptar); + } + else if (AImovement_timer->Check() && !IsRooted()) + { + _ZP(Mob_AI_Process_move); + SetRunAnimSpeed(0); + if (IsPet()) + { + _ZP(Mob_AI_Process_pet); + // we're a pet, do as we're told + switch (pStandingPetOrder) + { + case SPO_Follow: + { + + Mob* owner = GetOwner(); + if(owner == NULL) + break; + + //if(owner->IsClient()) + // printf("Pet start pos: (%f, %f, %f)\n", GetX(), GetY(), GetZ()); + + float dist = DistNoRoot(*owner); + if (dist >= 400) + { + float speed = GetWalkspeed(); + if (dist >= 5625) + speed = GetRunspeed(); + CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed); + } + else + { + if(moved) + { + moved=false; + SetMoving(false); + SendPosition(); + } + } + + /* + //fix up Z + float zdiff = GetZ() - owner->GetZ(); + if(zdiff < 0) + zdiff = 0 - zdiff; + if(zdiff > 2.0f) { + SendTo(GetX(), GetY(), owner->GetZ()); + SendPosition(); + } + + if(owner->IsClient()) + printf("Pet pos: (%f, %f, %f)\n", GetX(), GetY(), GetZ()); + */ + + break; + } + case SPO_Sit: + { + SetAppearance(eaSitting, false); + break; + } + case SPO_Guard: + { + //only NPCs can guard stuff. (forced by where the guard movement code is in the AI) + if(IsNPC()) { + CastToNPC()->NextGuardPosition(); + } + break; + } + } + } + else if (GetFollowID()) + { + Mob* follow = entity_list.GetMob(GetFollowID()); + if (!follow) SetFollowID(0); + else + { + float dist2 = DistNoRoot(*follow); + int followdist = GetFollowDistance(); + + if (dist2 >= followdist) // Default follow distance is 100 + { + float speed = GetWalkspeed(); + if (dist2 >= followdist + 150) + speed = GetRunspeed(); + CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed); + } + else + { + if(moved) + { + SendPosition(); + moved=false; + SetMoving(false); + } + } + } + } + else //not a pet, and not following somebody... + { + // dont move till a bit after you last fought + if (pLastFightingDelayMoving < Timer::GetCurrentTime()) + { + if (this->IsClient()) + { + // LD timer expired, drop out of world + if (this->CastToClient()->IsLD()) + this->CastToClient()->Disconnect(); + return; + } + + if(IsNPC()) + { + if(RuleB(NPC, SmartLastFightingDelayMoving) && !feign_memory_list.empty()) + { + minLastFightingDelayMoving = 0; + maxLastFightingDelayMoving = 0; + } + CastToNPC()->AI_DoMovement(); + } + } + + } + } // else if (AImovement_timer->Check()) + } + + //Do Ranged attack here + if(doranged) + { + RangedAttack(target); + } +} + +void NPC::AI_DoMovement() { + float walksp = GetMovespeed(); + if(walksp <= 0.0f) + return; //this is idle movement at walk speed, and we are unable to walk right now. + + if (roambox_distance > 0) { + _ZP(Mob_AI_Process_roambox); + if ( + roambox_movingto_x > roambox_max_x + || roambox_movingto_x < roambox_min_x + || roambox_movingto_y > roambox_max_y + || roambox_movingto_y < roambox_min_y + ) + { + float movedist = roambox_distance*roambox_distance; + float movex = MakeRandomFloat(0, movedist); + float movey = movedist - movex; + movex = sqrtf(movex); + movey = sqrtf(movey); + movex *= MakeRandomInt(0, 1) ? 1 : -1; + movey *= MakeRandomInt(0, 1) ? 1 : -1; + roambox_movingto_x = GetX() + movex; + roambox_movingto_y = GetY() + movey; + if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x) + roambox_movingto_x -= movex * 2; + if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y) + roambox_movingto_y -= movey * 2; + if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x) + roambox_movingto_x = roambox_max_x; + if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y) + roambox_movingto_y = roambox_max_y; + } + + mlog(AI__WAYPOINTS, "Roam Box: d=%.3f (%.3f->%.3f,%.3f->%.3f): Go To (%.3f,%.3f)", + roambox_distance, roambox_min_x, roambox_max_x, roambox_min_y, roambox_max_y, roambox_movingto_x, roambox_movingto_y); + if (!CalculateNewPosition2(roambox_movingto_x, roambox_movingto_y, GetZ(), walksp, true)) + { + roambox_movingto_x = roambox_max_x + 1; // force update + pLastFightingDelayMoving = Timer::GetCurrentTime() + RandomTimer(roambox_delay, roambox_delay + 5000); + SetMoving(false); + SendPosition(); // makes mobs stop clientside + } + } + else if (roamer) + { + _ZP(Mob_AI_Process_roamer); + if (AIwalking_timer->Check()) + { + movetimercompleted=true; + AIwalking_timer->Disable(); + } + + + int16 gridno = CastToNPC()->GetGrid(); + + if (gridno > 0 || cur_wp==-2) { + if (movetimercompleted==true) { // time to pause at wp is over + if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) { + CastToNPC()->Depop(true); //depop and resart spawn timer + } + else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { + CastToNPC()->Depop(false);//depop without spawn timer + } + else { + movetimercompleted=false; + + mlog(QUESTS__PATHING, "We are departing waypoint %d.", cur_wp); + + //if we were under quest control (with no grid), we are done now.. + if(cur_wp == -2) { + mlog(QUESTS__PATHING, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode."); + roamer = false; + cur_wp = 0; + } + + if(GetAppearance() != eaStanding) + SetAppearance(eaStanding, false); + + entity_list.OpenDoorsNear(CastToNPC()); + + if(!DistractedFromGrid) { + //kick off event_waypoint depart + char temp[16]; + sprintf(temp, "%d", cur_wp); + parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), NULL, temp, 0); + + //setup our next waypoint, if we are still on our normal grid + //remember that the quest event above could have done anything it wanted with our grid + if(gridno > 0) { + CastToNPC()->CalculateNewWaypoint(); + } + } + else { + DistractedFromGrid = false; + } + } + } // endif (movetimercompleted==true) + else if (!(AIwalking_timer->Enabled())) + { // currently moving + if (cur_wp_x == GetX() && cur_wp_y == GetY()) + { // are we there yet? then stop + mlog(AI__WAYPOINTS, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid()); + SetWaypointPause(); + if(GetAppearance() != eaStanding) + SetAppearance(eaStanding, false); + SetMoving(false); + if (cur_wp_heading >= 0.0) { + SetHeading(cur_wp_heading); + } + SendPosition(); + + //kick off event_waypoint arrive + char temp[16]; + sprintf(temp, "%d", cur_wp); + parse->EventNPC(EVENT_WAYPOINT_ARRIVE, CastToNPC(), NULL, temp, 0); + + // wipe feign memory since we reached our first waypoint + if(cur_wp == 1) + ClearFeignMemory(); + } + else + { // not at waypoint yet, so keep moving + if(!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0)) + CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, walksp, true); + else + { + bool WaypointChanged; + bool NodeReached; + VERTEX Goal = UpdatePath(cur_wp_x, cur_wp_y, cur_wp_z, walksp, WaypointChanged, NodeReached); + if(WaypointChanged) + tar_ndx = 20; + + if(NodeReached) + entity_list.OpenDoorsNear(CastToNPC()); + + CalculateNewPosition2(Goal.x, Goal.y, Goal.z, walksp, true); + } + + } + } + } // endif (gridno > 0) +// handle new quest grid command processing + else if (gridno < 0) + { // this mob is under quest control + if (movetimercompleted==true) + { // time to pause has ended + SetGrid( 0 - GetGrid()); // revert to AI control + mlog(QUESTS__PATHING, "Quest pathing is finished. Resuming on grid %d", GetGrid()); + + if(GetAppearance() != eaStanding) + SetAppearance(eaStanding, false); + + CalculateNewWaypoint(); + } + } + + } + else if (IsGuarding()) + { + _ZP(Mob_AI_Process_guard); + + bool CP2Moved; + if(!RuleB(Pathing, Guard) || !zone->pathing) + CP2Moved = CalculateNewPosition2(guard_x, guard_y, guard_z, walksp); + else + { + if(!((x_pos == guard_x) && (y_pos == guard_y) && (z_pos == guard_z))) + { + bool WaypointChanged, NodeReached; + VERTEX Goal = UpdatePath(guard_x, guard_y, guard_z, walksp, WaypointChanged, NodeReached); + if(WaypointChanged) + tar_ndx = 20; + + if(NodeReached) + entity_list.OpenDoorsNear(CastToNPC()); + + CP2Moved = CalculateNewPosition2(Goal.x, Goal.y, Goal.z, walksp); + } + else + CP2Moved = false; + + } + if (!CP2Moved) + { + if(moved) { + mlog(AI__WAYPOINTS, "Reached guard point (%.3f,%.3f,%.3f)", guard_x, guard_y, guard_z); + ClearFeignMemory(); + moved=false; + SetMoving(false); + if (GetTarget() == NULL || DistNoRoot(*GetTarget()) >= 5*5 ) + { + SetHeading(guard_heading); + } else { + FaceTarget(GetTarget()); + } + SendPosition(); + SetAppearance(GetGuardPointAnim()); + } + } + } +} + +// Note: Mob that caused this may not get added to the hate list until after this function call completes +void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) { + if (!IsAIControlled()) + return; + + if(GetAppearance() != eaStanding) + { + SetAppearance(eaStanding); + } + + if (iYellForHelp) { + if(IsPet()) { + GetOwner()->AI_Event_Engaged(attacker, iYellForHelp); + } else { + entity_list.AIYellForHelp(this, attacker); + } + } + + if(IsNPC()) + { + if(CastToNPC()->GetGrid() > 0) + { + DistractedFromGrid = true; + } + if(attacker && !attacker->IsCorpse()) + { + //Because sometimes the AIYellForHelp triggers another engaged and then immediately a not engaged + //if the target dies before it goes off + if(attacker->GetHP() > 0) + { + if(!CastToNPC()->GetCombatEvent() && GetHP() > 0) + { + parse->EventNPC(EVENT_COMBAT, CastToNPC(), attacker, "1", 0); + uint16 emoteid = CastToNPC()->GetNPCEmoteID(); + if(emoteid != 0) + CastToNPC()->DoNPCEmote(ENTERCOMBAT,emoteid); + CastToNPC()->SetCombatEvent(true); + } + } + } + } +} + +// Note: Hate list may not be actually clear until after this function call completes +void Mob::AI_Event_NoLongerEngaged() { + if (!IsAIControlled()) + return; + this->AIwalking_timer->Start(RandomTimer(3000,20000)); + pLastFightingDelayMoving = Timer::GetCurrentTime(); + if (minLastFightingDelayMoving == maxLastFightingDelayMoving) + pLastFightingDelayMoving += minLastFightingDelayMoving; + else + pLastFightingDelayMoving += MakeRandomInt(minLastFightingDelayMoving, maxLastFightingDelayMoving); + // EverHood - 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); + SetMoving(false); + SendPosition(); + } + ClearRampage(); + + if(IsNPC()) + { + if(CastToNPC()->GetCombatEvent() && GetHP() > 0) + { + uint16 emoteid = CastToNPC()->GetNPCEmoteID(); + parse->EventNPC(EVENT_COMBAT, CastToNPC(), NULL, "0", 0); + if(emoteid != 0) + CastToNPC()->DoNPCEmote(LEAVECOMBAT,emoteid); + CastToNPC()->SetCombatEvent(false); + } + } +} + +//this gets called from InterruptSpell() for failure or SpellFinished() for success +void NPC::AI_Event_SpellCastFinished(bool iCastSucceeded, uint8 slot) { + if (slot == 1) { + uint32 recovery_time = 0; + if (iCastSucceeded) { + if (casting_spell_AIindex < AIspells.size()) { + recovery_time += spells[AIspells[casting_spell_AIindex].spellid].recovery_time; + if (AIspells[casting_spell_AIindex].recast_delay >= 0) + { + if (AIspells[casting_spell_AIindex].recast_delay < 10000) + AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + (AIspells[casting_spell_AIindex].recast_delay*1000); + } + else + AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + spells[AIspells[casting_spell_AIindex].spellid].recast_time; + } + if (recovery_time < AIautocastspell_timer->GetSetAtTrigger()) + recovery_time = AIautocastspell_timer->GetSetAtTrigger(); + AIautocastspell_timer->Start(recovery_time, false); + } + else + AIautocastspell_timer->Start(800, false); + casting_spell_AIindex = AIspells.size(); + } +} + + +bool NPC::AI_EngagedCastCheck() { + if (AIautocastspell_timer->Check(false)) { + _ZP(Mob_AI_Process_engaged_cast); + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. + + mlog(AI__SPELLS, "Engaged autocast check triggered. Trying to cast healing spells then maybe offensive spells."); + + // try casting a heal or gate + if (!AICastSpell(this, 100, SpellType_Heal | SpellType_Escape | SpellType_InCombatBuff)) { + // try casting a heal on nearby + if (!entity_list.AICheckCloseBeneficialSpells(this, 25, MobAISpellRange, SpellType_Heal)) { + //nobody to heal, try some detrimental spells. + if(!AICastSpell(GetTarget(), 20, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff)) { + //no spell to cast, try again soon. + AIautocastspell_timer->Start(RandomTimer(500, 1000), false); + } + } + } + return(true); + } + + return(false); +} + +bool NPC::AI_PursueCastCheck() { + if (AIautocastspell_timer->Check(false)) { + _ZP(Mob_AI_Process_pursue_cast); + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. + + mlog(AI__SPELLS, "Engaged (pursuing) autocast check triggered. Trying to cast offensive spells."); + if(!AICastSpell(GetTarget(), 90, SpellType_Root | SpellType_Nuke | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff)) { + //no spell cast, try again soon. + AIautocastspell_timer->Start(RandomTimer(500, 2000), false); + } //else, spell casting finishing will reset the timer. + return(true); + } + return(false); +} + +bool NPC::AI_IdleCastCheck() { + if (AIautocastspell_timer->Check(false)) { + _ZP(Mob_AI_Process_autocast); +#if MobAI_DEBUG_Spells >= 25 + cout << "Non-Engaged autocast check triggered: " << this->GetName() << endl; +#endif + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. + if (!AICastSpell(this, 100, SpellType_Heal | SpellType_Buff | SpellType_Pet)) { + if(!entity_list.AICheckCloseBeneficialSpells(this, 33, MobAISpellRange, SpellType_Heal | SpellType_Buff)) { + //if we didnt cast any spells, our autocast timer just resets to the + //last duration it was set to... try to put up a more reasonable timer... + AIautocastspell_timer->Start(RandomTimer(1000, 5000), false); + } //else, spell casting finishing will reset the timer. + } //else, spell casting finishing will reset the timer. + return(true); + } + return(false); +} + +void Mob::StartEnrage() +{ + // dont continue if already enraged + if (bEnraged) + return; + if (SpecAttackTimers[SPECATK_ENRAGE] && !SpecAttackTimers[SPECATK_ENRAGE]->Check()) + return; + // see if NPC has possibility to enrage + if (!SpecAttacks[SPECATK_ENRAGE]) + return; + // check if timer exists (should be true at all times) + if (SpecAttackTimers[SPECATK_ENRAGE]) + { + safe_delete(SpecAttackTimers[SPECATK_ENRAGE]); + SpecAttackTimers[SPECATK_ENRAGE] = NULL; + } + + if (!SpecAttackTimers[SPECATK_ENRAGE]) + { + SpecAttackTimers[SPECATK_ENRAGE] = new Timer(EnragedDurationTimer); + } + // start the timer. need to call IsEnraged frequently since we dont have callback timers :-/ + SpecAttackTimers[SPECATK_ENRAGE]->Start(); + bEnraged = true; + entity_list.MessageClose_StringID(this, true, 200, MT_NPCEnrage, NPC_ENRAGE_START, GetCleanName()); +} + +void Mob::ProcessEnrage(){ + if(IsEnraged()){ + if(SpecAttackTimers[SPECATK_ENRAGE] && SpecAttackTimers[SPECATK_ENRAGE]->Check()){ + entity_list.MessageClose_StringID(this, true, 200, MT_NPCEnrage, NPC_ENRAGE_END, GetCleanName()); + SpecAttackTimers[SPECATK_ENRAGE]->Start(EnragedTimer); + bEnraged = false; + } + } +} + +bool Mob::IsEnraged() +{ + return bEnraged; +} + +bool Mob::Flurry() +{ + // this is wrong, flurry is extra attacks on the current target + Mob *target = GetTarget(); + if (target) { + if (!IsPet()) { + entity_list.MessageClose_StringID(this, true, 200, MT_NPCFlurry, NPC_FLURRY, GetCleanName(), target->GetCleanName()); + } else { + entity_list.MessageClose_StringID(this, true, 200, MT_PetFlurry, NPC_FLURRY, GetCleanName(), target->GetCleanName()); + } + for (int i = 0; i < RuleI(Combat, MaxFlurryHits); i++) + Attack(target); + } + return true; +} + +bool Mob::AddRampage(Mob *mob) +{ + if(!mob) + return false; + + if (!SpecAttacks[SPECATK_RAMPAGE]) + return false; + + for (int i = 0; i < RampageArray.size(); i++) + { + // if name is already on the list dont add it again + if (strcasecmp(mob->GetName(), RampageArray[i].c_str()) == 0) + return false; + } + std::string r_name = mob->GetName(); + RampageArray.push_back(r_name); + return true; +} + +void Mob::ClearRampage(){ + RampageArray.clear(); +} + +bool Mob::Rampage() +{ + int index_hit = 0; + if (!IsPet()) { + entity_list.MessageClose_StringID(this, true, 200, MT_NPCRampage, NPC_RAMPAGE, GetCleanName()); + } else { + entity_list.MessageClose_StringID(this, true, 200, MT_PetFlurry, NPC_RAMPAGE, GetCleanName()); + } + for (int i = 0; i < RampageArray.size(); i++) + { + if(index_hit >= RuleI(Combat, MaxRampageTargets)) + break; + // range is important + Mob *m_target = entity_list.GetMob(RampageArray[i].c_str()); + if(m_target) + { + if(m_target == GetTarget()) + continue; + if (CombatRange(m_target)) + { + Attack(m_target); + index_hit++; + } + } + } + if(index_hit < RuleI(Combat, MaxRampageTargets)) + Attack(GetTarget()); + return true; +} + +void Mob::AreaRampage() +{ + int index_hit = 0; + if (!IsPet()) { // do not know every pet AA so thought it safer to add this + entity_list.MessageClose_StringID(this, true, 200, MT_NPCRampage, AE_RAMPAGE, GetCleanName()); + } else { + entity_list.MessageClose_StringID(this, true, 200, MT_PetFlurry, AE_RAMPAGE, GetCleanName()); + } + index_hit = hate_list.AreaRampage(this, GetTarget()); + + if(index_hit == 0) + Attack(GetTarget()); +} + +uint32 Mob::GetLevelCon(uint8 mylevel, uint8 iOtherLevel) { + int16 diff = iOtherLevel - mylevel; + uint32 conlevel=0; + + if (diff == 0) + return CON_WHITE; + else if (diff >= 1 && diff <= 2) + return CON_YELLOW; + else if (diff >= 3) + return CON_RED; + + if (mylevel <= 8) + { + if (diff <= -4) + conlevel = CON_GREEN; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 9) + { + if (diff <= -6) + conlevel = CON_GREEN; + else if (diff <= -4) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 13) + { + if (diff <= -7) + conlevel = CON_GREEN; + else if (diff <= -5) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 15) + { + if (diff <= -7) + conlevel = CON_GREEN; + else if (diff <= -5) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 17) + { + if (diff <= -8) + conlevel = CON_GREEN; + else if (diff <= -6) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 21) + { + if (diff <= -9) + conlevel = CON_GREEN; + else if (diff <= -7) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 25) + { + if (diff <= -10) + conlevel = CON_GREEN; + else if (diff <= -8) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 29) + { + if (diff <= -11) + conlevel = CON_GREEN; + else if (diff <= -9) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 31) + { + if (diff <= -12) + conlevel = CON_GREEN; + else if (diff <= -9) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 33) + { + if (diff <= -13) + conlevel = CON_GREEN; + else if (diff <= -10) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 37) + { + if (diff <= -14) + conlevel = CON_GREEN; + else if (diff <= -11) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 41) + { + if (diff <= -16) + conlevel = CON_GREEN; + else if (diff <= -12) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 45) + { + if (diff <= -17) + conlevel = CON_GREEN; + else if (diff <= -13) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 49) + { + if (diff <= -18) + conlevel = CON_GREEN; + else if (diff <= -14) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 53) + { + if (diff <= -19) + conlevel = CON_GREEN; + else if (diff <= -15) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else if (mylevel <= 55) + { + if (diff <= -20) + conlevel = CON_GREEN; + else if (diff <= -15) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + else + { + if (diff <= -21) + conlevel = CON_GREEN; + else if (diff <= -16) + conlevel = CON_LIGHTBLUE; + else + conlevel = CON_BLUE; + } + return conlevel; +} + +void NPC::CheckSignal() { + if (!signal_q.empty()) { + int signal_id = signal_q.front(); + signal_q.pop_front(); + char buf[32]; + snprintf(buf, 31, "%d", signal_id); + buf[31] = '\0'; + parse->EventNPC(EVENT_SIGNAL, this, NULL, buf, 0); + } +} + + + +/* +alter table npc_types drop column usedspells; +alter table npc_types add column npc_spells_id int(11) unsigned not null default 0 after merchant_id; +Create Table npc_spells ( + id int(11) unsigned not null auto_increment primary key, + name tinytext, + parent_list int(11) unsigned not null default 0, + attack_proc smallint(5) not null default -1, + proc_chance tinyint(3) not null default 3 + ); +create table npc_spells_entries ( + id int(11) unsigned not null auto_increment primary key, + npc_spells_id int(11) not null, + spellid smallint(5) not null default 0, + type smallint(5) unsigned not null default 0, + minlevel tinyint(3) unsigned not null default 0, + maxlevel tinyint(3) unsigned not null default 255, + manacost smallint(5) not null default '-1', + recast_delay int(11) not null default '-1', + priority smallint(5) not null default 0, + index npc_spells_id (npc_spells_id) + ); +*/ + +bool IsSpellInList(DBnpcspells_Struct* spell_list, int16 iSpellID); +bool Compare_AI_Spells(AISpells_Struct i, AISpells_Struct j); + +bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) { + // ok, this function should load the list, and the parent list then shove them into the struct and sort + npc_spells_id = iDBSpellsID; + AIspells.clear(); + if (iDBSpellsID == 0) { + AIautocastspell_timer->Disable(); + return false; + } + DBnpcspells_Struct* spell_list = database.GetNPCSpells(iDBSpellsID); + if (!spell_list) { + AIautocastspell_timer->Disable(); + return false; + } + DBnpcspells_Struct* parentlist = database.GetNPCSpells(spell_list->parent_list); + uint32 i; +#if MobAI_DEBUG_Spells >= 10 + cout << "Loading NPCSpells onto " << this->GetName() << ": dbspellsid=" << iDBSpellsID; + if (spell_list) { + cout << " (found, " << spell_list->numentries << "), parentlist=" << spell_list->parent_list; + if (spell_list->parent_list) { + if (parentlist) { + cout << " (found, " << parentlist->numentries << ")"; + } + else + cout << " (not found)"; + } + } + else + cout << " (not found)"; + cout << endl; +#endif + int16 attack_proc_spell = -1; + int8 proc_chance = 3; + if (parentlist) { + attack_proc_spell = parentlist->attack_proc; + proc_chance = parentlist->proc_chance; + for (i=0; inumentries; i++) { + if (GetLevel() >= parentlist->entries[i].minlevel && GetLevel() <= parentlist->entries[i].maxlevel && parentlist->entries[i].spellid > 0) { + if (!IsSpellInList(spell_list, parentlist->entries[i].spellid)) + { + AddSpellToNPCList(parentlist->entries[i].priority, + parentlist->entries[i].spellid, parentlist->entries[i].type, + parentlist->entries[i].manacost, parentlist->entries[i].recast_delay, + parentlist->entries[i].resist_adjust); + } + } + } + } + if (spell_list->attack_proc >= 0) { + attack_proc_spell = spell_list->attack_proc; + proc_chance = spell_list->proc_chance; + } + for (i=0; inumentries; i++) { + if (GetLevel() >= spell_list->entries[i].minlevel && GetLevel() <= spell_list->entries[i].maxlevel && spell_list->entries[i].spellid > 0) { + AddSpellToNPCList(spell_list->entries[i].priority, + spell_list->entries[i].spellid, spell_list->entries[i].type, + spell_list->entries[i].manacost, spell_list->entries[i].recast_delay, + spell_list->entries[i].resist_adjust); + } + } + std::sort(AIspells.begin(), AIspells.end(), Compare_AI_Spells); + + if (attack_proc_spell > 0) + AddProcToWeapon(attack_proc_spell, true, proc_chance); + + if (AIspells.size() == 0) + AIautocastspell_timer->Disable(); + else + AIautocastspell_timer->Trigger(); + return true; +} + +bool IsSpellInList(DBnpcspells_Struct* spell_list, int16 iSpellID) { + for (uint32 i=0; i < spell_list->numentries; i++) { + if (spell_list->entries[i].spellid == iSpellID) + return true; + } + return false; +} + +bool Compare_AI_Spells(AISpells_Struct i, AISpells_Struct j) +{ + return(i.priority > j.priority); +} + +// adds a spell to the list, taking into account priority and resorting list as needed. +void NPC::AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint16 iType, + int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust) +{ + + if(!IsValidSpell(iSpellID)) + return; + + HasAISpell = true; + AISpells_Struct t; + + t.priority = iPriority; + t.spellid = iSpellID; + t.type = iType; + t.manacost = iManaCost; + t.recast_delay = iRecastDelay; + t.time_cancast = 0; + t.resist_adjust = iResistAdjust; + + AIspells.push_back(t); +} + +void NPC::RemoveSpellFromNPCList(int16 spell_id) +{ + std::vector::iterator iter = AIspells.begin(); + while(iter != AIspells.end()) + { + if((*iter).spellid == spell_id) + { + iter = AIspells.erase(iter); + continue; + } + iter++; + } +} + +DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) { + if (iDBSpellsID == 0) + return 0; + if (!npc_spells_cache) { + npc_spells_maxid = GetMaxNPCSpellsID(); + npc_spells_cache = new DBnpcspells_Struct*[npc_spells_maxid+1]; + npc_spells_loadtried = new bool[npc_spells_maxid+1]; + for (uint32 i=0; i<=npc_spells_maxid; i++) { + npc_spells_cache[i] = 0; + npc_spells_loadtried[i] = false; + } + } + if (iDBSpellsID > npc_spells_maxid) + return 0; + 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 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]); + int16 tmpattack_proc = atoi(row[2]); + uint8 tmpproc_chance = atoi(row[3]); + 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]->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 { + cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return 0; + } + } + else { + mysql_free_result(result); + } + } + else { + cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return 0; + } + + return 0; + } + return 0; +} + +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 { + cerr << "Error in GetMaxNPCSpellsID query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return 0; + } + + return 0; +} + diff --git a/zone/NpcAI.cpp b/zone/NpcAI.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/zone/NpcAI.h b/zone/NpcAI.h new file mode 100644 index 000000000..2f08d633a --- /dev/null +++ b/zone/NpcAI.h @@ -0,0 +1,41 @@ +/* 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 NPCAI_H +#define NPCAI_H +#include "../common/types.h" + + + +/* +Even con color is wrong and so is the message but i can't seem to find it with a for loop. +Seems more like a bitwise comparison client side.. + con->level = 2; // GREEN + con->level = 18; // LIGHT BLUE + con->level = 4; // BLUE + con->level = 20; // EVEN + con->level = 15; // YELLOW + con->level = 13; // RED +*/ +#define DEFAULT_AGGRORADIUS 70 +#define DEFAULT_FRENYRADIUS 70 +#define MAX_SHIELDRADIUS 20 + +uint32 GetLevelCon(uint8 PlayerLevel, uint8 NPCLevel); + +#endif diff --git a/zone/Object.cpp b/zone/Object.cpp new file mode 100644 index 000000000..6fb91ba75 --- /dev/null +++ b/zone/Object.cpp @@ -0,0 +1,950 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 +#include + +#include "masterentity.h" +#include "zonedb.h" +#include "../common/packet_functions.h" +#include "../common/packet_dump.h" +#include "../common/MiscFunctions.h" +#include "features.h" +#include "StringIDs.h" +using namespace std; + +#include "QuestParserCollection.h" + +const char DEFAULT_OBJECT_NAME[] = "IT63_ACTORDEF"; +const char DEFAULT_OBJECT_NAME_SUFFIX[] = "_ACTORDEF"; + + +extern Zone* zone; +extern EntityList entity_list; + +// Loading object from database +Object::Object(uint32 id, uint32 type, uint32 icon, const Object_Struct& object, const ItemInst* inst) + : respawn_timer(0), decay_timer(300000) +{ + + user = NULL; + last_user = NULL; + + // Initialize members + m_id = id; + m_type = type; + m_icon = icon; + m_inuse = false; + m_inst = NULL; + m_ground_spawn=false; + // Copy object data + memcpy(&m_data, &object, sizeof(Object_Struct)); + if (inst) { + m_inst = inst->Clone(); + decay_timer.Start(); + } else { + decay_timer.Disable(); + } + respawn_timer.Disable(); + + // Set drop_id to zero - it will be set when added to zone with SetID() + m_data.drop_id = 0; +} + +//creating a re-ocurring ground spawn. +Object::Object(const ItemInst* inst, char* name,float max_x,float min_x,float max_y,float min_y,float z,float heading,uint32 respawntimer) + : respawn_timer(respawntimer), decay_timer(300000) +{ + + user = NULL; + last_user = NULL; + m_max_x=max_x; + m_max_y=max_y; + m_min_x=min_x; + m_min_y=min_y; + m_id = 0; + m_inst = (inst) ? inst->Clone() : NULL; + m_type = OT_DROPPEDITEM; + m_icon = 0; + m_inuse = false; + m_ground_spawn = true; + decay_timer.Disable(); + // Set as much struct data as we can + memset(&m_data, 0, sizeof(Object_Struct)); + m_data.heading = heading; + //printf("Spawning object %s at %f,%f,%f\n",name,m_data.x,m_data.y,m_data.z); + m_data.z = z; + m_data.zone_id = zone->GetZoneID(); + respawn_timer.Disable(); + strcpy(m_data.object_name, name); + RandomSpawn(false); + + // Hardcoded portion for unknown members + m_data.unknown024 = 0x7f001194; + m_data.unknown076 = 0x0000d5fe; + m_data.unknown084 = 0xFFFFFFFF; +} + +// Loading object from client dropping item on ground +Object::Object(Client* client, const ItemInst* inst) + : respawn_timer(0), decay_timer(300000) +{ + user = NULL; + last_user = NULL; + + // Initialize members + m_id = 0; + m_inst = (inst) ? inst->Clone() : NULL; + m_type = OT_DROPPEDITEM; + m_icon = 0; + m_inuse = false; + m_ground_spawn = false; + // Set as much struct data as we can + memset(&m_data, 0, sizeof(Object_Struct)); + m_data.heading = client->GetHeading(); + m_data.x = client->GetX(); + m_data.y = client->GetY(); + m_data.z = client->GetZ(); + m_data.zone_id = zone->GetZoneID(); + + decay_timer.Start(); + respawn_timer.Disable(); + + // Hardcoded portion for unknown members + m_data.unknown024 = 0x7f001194; + m_data.unknown076 = 0x0000d5fe; + m_data.unknown084 = 0xFFFFFFFF; + + // Set object name + if (inst) { + const Item_Struct* item = inst->GetItem(); + if (item && item->IDFile) { + if (strlen(item->IDFile) == 0) { + strcpy(m_data.object_name, DEFAULT_OBJECT_NAME); + } + else { + // Object name is idfile + _ACTORDEF + uint32 len_idfile = strlen(inst->GetItem()->IDFile); + uint32 len_copy = sizeof(m_data.object_name) - len_idfile - 1; + if (len_copy > sizeof(DEFAULT_OBJECT_NAME_SUFFIX)) { + len_copy = sizeof(DEFAULT_OBJECT_NAME_SUFFIX); + } + + memcpy(&m_data.object_name[0], inst->GetItem()->IDFile, len_idfile); + memcpy(&m_data.object_name[len_idfile], DEFAULT_OBJECT_NAME_SUFFIX, len_copy); + } + } + else { + strcpy(m_data.object_name, DEFAULT_OBJECT_NAME); + } + } +} + +Object::Object(const ItemInst *inst, float x, float y, float z, float heading, uint32 decay_time) + : respawn_timer(0), decay_timer(decay_time) +{ + user = NULL; + last_user = NULL; + + // Initialize members + m_id = 0; + m_inst = (inst) ? inst->Clone() : NULL; + m_type = OT_DROPPEDITEM; + m_icon = 0; + m_inuse = false; + m_ground_spawn = false; + // Set as much struct data as we can + memset(&m_data, 0, sizeof(Object_Struct)); + m_data.heading = heading; + m_data.x = x; + m_data.y = y; + m_data.z = z; + m_data.zone_id = zone->GetZoneID(); + + if (decay_time) + decay_timer.Start(); + + respawn_timer.Disable(); + + // Hardcoded portion for unknown members + m_data.unknown024 = 0x7f001194; + m_data.unknown076 = 0x0000d5fe; + m_data.unknown084 = 0xFFFFFFFF; + + // Set object name + if (inst) { + const Item_Struct* item = inst->GetItem(); + if (item && item->IDFile) { + if (strlen(item->IDFile) == 0) { + strcpy(m_data.object_name, DEFAULT_OBJECT_NAME); + } + else { + // Object name is idfile + _ACTORDEF + uint32 len_idfile = strlen(inst->GetItem()->IDFile); + uint32 len_copy = sizeof(m_data.object_name) - len_idfile - 1; + if (len_copy > sizeof(DEFAULT_OBJECT_NAME_SUFFIX)) { + len_copy = sizeof(DEFAULT_OBJECT_NAME_SUFFIX); + } + + memcpy(&m_data.object_name[0], inst->GetItem()->IDFile, len_idfile); + memcpy(&m_data.object_name[len_idfile], DEFAULT_OBJECT_NAME_SUFFIX, len_copy); + } + } + else { + strcpy(m_data.object_name, DEFAULT_OBJECT_NAME); + } + } +} + +Object::Object(const char *model, float x, float y, float z, float heading, uint8 type, uint32 decay_time) + : respawn_timer(0), decay_timer(decay_time) +{ + user = NULL; + last_user = NULL; + ItemInst* inst = NULL; + inst = new ItemInst(ItemUseWorldContainer); + + // Initialize members + m_id = 0; + m_inst = (inst) ? inst->Clone() : NULL; + m_type = type; + m_icon = 0; + m_inuse = false; + m_ground_spawn = false; + // Set as much struct data as we can + memset(&m_data, 0, sizeof(Object_Struct)); + m_data.heading = heading; + m_data.x = x; + m_data.y = y; + m_data.z = z; + m_data.zone_id = zone->GetZoneID(); + + if (decay_time) + decay_timer.Start(); + + respawn_timer.Disable(); + + //Hardcoded portion for unknown members + m_data.unknown024 = 0x7f001194; + m_data.unknown076 = 0x0000d5fe; + m_data.unknown084 = 0xFFFFFFFF; + + if(model) + strcpy(m_data.object_name, model); + else + strcpy(m_data.object_name, "IT64_ACTORDEF"); //default object name if model isn't specified for some unknown reason +} + +Object::~Object() +{ + safe_delete(m_inst); + if(user != NULL) { + user->SetTradeskillObject(NULL); + } +} + +void Object::SetID(uint16 set_id) +{ + // Invoke base class + Entity::SetID(set_id); + + // Store new id as drop_id + m_data.drop_id = (uint32)this->GetID(); +} + +// Reset state of object back to zero +void Object::ResetState() +{ + safe_delete(m_inst); + + m_id = 0; + m_type = 0; + m_icon = 0; + memset(&m_data, 0, sizeof(Object_Struct)); +} + +bool Object::Save() +{ + if (m_id) { + // Update existing + database.UpdateObject(m_id, m_type, m_icon, m_data, m_inst); + } + else { + // Doesn't yet exist, add now + m_id = database.AddObject(m_type, m_icon, m_data, m_inst); + } + + return true; +} + +uint16 Object::VarSave() +{ + if (m_id) { + // Update existing + database.UpdateObject(m_id, m_type, m_icon, m_data, m_inst); + } + else { + // Doesn't yet exist, add now + m_id = database.AddObject(m_type, m_icon, m_data, m_inst); + } + return m_id; +} + +// Remove object from database +void Object::Delete(bool reset_state) +{ + if (m_id != 0) { + database.DeleteObject(m_id); + } + + if (reset_state) { + ResetState(); + } +} + +// Add item to object (only logical for world tradeskill containers +void Object::PutItem(uint8 index, const ItemInst* inst) +{ + if (index > 9) { + LogFile->write(EQEMuLog::Error, "Object::PutItem: Invalid index specified (%i)", index); + return; + } + + if (m_inst && m_inst->IsType(ItemClassContainer)) { + if (inst) { + m_inst->PutItem(index, *inst); + } + else { + m_inst->DeleteItem(index); + } + database.SaveWorldContainer(zone->GetZoneID(),m_id,m_inst); + // This is _highly_ inefficient, but for now it will work: Save entire object to database + Save(); + } +} + +void Object::Close() { + m_inuse = false; + if(user != NULL) + { + last_user = user; + // put any remaining items from the world container back into the player's inventory to avoid item loss + // if they close the container without removing all items + ItemInst* container = this->m_inst; + if(container != NULL) + { + for (uint8 i = 0; i < MAX_ITEMS_PER_BAG; i++) + { + ItemInst* inst = container->PopItem(i); + if(inst != NULL) + { + user->MoveItemToInventory(inst, true); + } + } + } + + user->SetTradeskillObject(NULL); + } + user = NULL; +} + +// Remove item from container +void Object::DeleteItem(uint8 index) +{ + if (m_inst && m_inst->IsType(ItemClassContainer)) { + m_inst->DeleteItem(index); + + // This is _highly_ inefficient, but for now it will work: Save entire object to database + Save(); + } +} + +// Pop item out of container +ItemInst* Object::PopItem(uint8 index) +{ + ItemInst* inst = NULL; + + if (m_inst && m_inst->IsType(ItemClassContainer)) { + inst = m_inst->PopItem(index); + + // This is _highly_ inefficient, but for now it will work: Save entire object to database + Save(); + } + + return inst; +} + +void Object::CreateSpawnPacket(EQApplicationPacket* app) +{ + app->SetOpcode(OP_GroundSpawn); + app->pBuffer = new uchar[sizeof(Object_Struct)]; + app->size = sizeof(Object_Struct); + memcpy(app->pBuffer, &m_data, sizeof(Object_Struct)); +} + +void Object::CreateDeSpawnPacket(EQApplicationPacket* app) +{ + app->SetOpcode(OP_ClickObject); + app->pBuffer = new uchar[sizeof(ClickObject_Struct)]; + app->size = sizeof(ClickObject_Struct); + memset(app->pBuffer, 0, sizeof(ClickObject_Struct)); + ClickObject_Struct* co = (ClickObject_Struct*) app->pBuffer; + co->drop_id = m_data.drop_id; + co->player_id = 0; +} + +bool Object::Process(){ + if(m_type == OT_DROPPEDITEM && decay_timer.Enabled() && decay_timer.Check()) { + // Send click to all clients (removes entity on client) + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClickObject, sizeof(ClickObject_Struct)); + ClickObject_Struct* click_object = (ClickObject_Struct*)outapp->pBuffer; + click_object->drop_id = GetID(); + entity_list.QueueClients(NULL, outapp, false); + safe_delete(outapp); + + // Remove object + database.DeleteObject(m_id); + return false; + } + + if(m_ground_spawn && respawn_timer.Check()){ + RandomSpawn(true); + } + return true; +} + +void Object::RandomSpawn(bool send_packet) { + if(!m_ground_spawn) + return; + + m_data.x = MakeRandomFloat(m_min_x, m_max_x); + m_data.y = MakeRandomFloat(m_min_y, m_max_y); + respawn_timer.Disable(); + + if(send_packet) { + EQApplicationPacket app; + CreateSpawnPacket(&app); + entity_list.QueueClients(NULL, &app, true); + } +} + +bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) +{ + if(m_ground_spawn){//This is a Cool Groundspawn + respawn_timer.Start(); + } + if (m_type == OT_DROPPEDITEM) { + bool cursordelete = false; + if (m_inst && sender) { + // if there is a lore conflict, delete the offending item from the server inventory + // the client updates itself and takes care of sending "duplicate lore item" messages + if(sender->CheckLoreConflict(m_inst->GetItem())) { + int16 loreslot = sender->GetInv().HasItem(m_inst->GetItem()->ID, 0, invWhereBank); + if(loreslot != SLOT_INVALID) // if the duplicate is in the bank, delete it. + sender->DeleteItemInInventory(loreslot); + else + cursordelete = true; // otherwise, we delete the new one + } + + char buf[10]; + snprintf(buf, 9, "%u", m_inst->GetItem()->ID); + buf[9] = '\0'; + parse->EventPlayer(EVENT_PLAYER_PICKUP, sender, buf, 0); + + // Transfer item to client + sender->PutItemInInventory(SLOT_CURSOR, *m_inst, false); + sender->SendItemPacket(SLOT_CURSOR, m_inst, ItemPacketTrade); + + if(cursordelete) // delete the item if it's a duplicate lore. We have to do this because the client expects the item packet + sender->DeleteItemInInventory(SLOT_CURSOR); + + if(!m_ground_spawn) + safe_delete(m_inst); + + // No longer using a tradeskill object + sender->SetTradeskillObject(NULL); + user = NULL; + } + + // Send click to all clients (removes entity on client) + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClickObject, sizeof(ClickObject_Struct)); + memcpy(outapp->pBuffer, click_object, sizeof(ClickObject_Struct)); + entity_list.QueueClients(NULL, outapp, false); + safe_delete(outapp); + + // Remove object + database.DeleteObject(m_id); + if(!m_ground_spawn) + entity_list.RemoveEntity(this->GetID()); + } else { + // Tradeskill item + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClickObjectAction, sizeof(ClickObjectAction_Struct)); + ClickObjectAction_Struct* coa = (ClickObjectAction_Struct*)outapp->pBuffer; + + //TODO: there is prolly a better way to do this. + //if this is not the main user, send them a close and a message + if(user == NULL || user == sender) + coa->open = 0x01; + else { + coa->open = 0x00; + //sender->Message(13, "Somebody is allready using that container."); + } + m_inuse = true; + coa->type = m_type; + coa->unknown16 = 0x0a; + + coa->drop_id = click_object->drop_id; + coa->player_id = click_object->player_id; + coa->icon = m_icon; + + if(sender->IsLooting()) + { + coa->open = 0x00; + user = sender; + } + + sender->QueuePacket(outapp); + safe_delete(outapp); + + //if the object allready had a user, we are done + if(user != NULL) + return(false); + + // Starting to use this object + sender->SetTradeskillObject(this); + + user = sender; + + // Send items inside of container + + if (m_inst && m_inst->IsType(ItemClassContainer)) { + + //Clear out no-drop and no-rent items first if different player opens it + if(user != last_user) + m_inst->ClearByFlags(byFlagSet, byFlagSet); + + EQApplicationPacket* outapp=new EQApplicationPacket(OP_ClientReady,0); + sender->QueuePacket(outapp); + safe_delete(outapp); + for (uint8 i=0; i<10; i++) { + const ItemInst* inst = m_inst->GetItem(i); + if (inst) { + //sender->GetInv().PutItem(i+4000,inst); + sender->SendItemPacket(i, inst, ItemPacketWorldContainer); + } + } + } + } + + return true; +} + +// 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; + + if (inst && inst->GetItem()) { + item_id = inst->GetItem()->ID; + charges = inst->GetCharges(); + } + + // SQL Escape object_name + uint32 len = strlen(object.object_name) * 2 + 1; + 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, NULL, NULL, &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); + } + } + + safe_delete_array(object_name); + safe_delete_array(query); + 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; + + if (inst && inst->GetItem()) { + item_id = inst->GetItem()->ID; + charges = inst->GetCharges(); + } + + // SQL Escape object_name + uint32 len = strlen(object.object_name) * 2 + 1; + 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); + } + } + + safe_delete_array(object_name); + safe_delete_array(query); +} +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 { + cerr << "Error in LoadGroundSpawns query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } + 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); + } + //else { + // Delete contained items, if any + // DeleteWorldContainer(id); + //} + + safe_delete_array(query); +} + +uint32 Object::GetDBID() +{ + return this->m_id; +} + +uint32 Object::GetType() +{ + return this->m_type; +} + +void Object::SetType(uint32 type) +{ + this->m_type = type; + this->m_data.object_type = type; +} + +uint32 Object::GetIcon() +{ + return this->m_icon; +} + +float Object::GetX() +{ + return this->m_data.x; +} + +float Object::GetY() +{ + return this->m_data.y; +} + + +float Object::GetZ() +{ + return this->m_data.z; +} + +float Object::GetHeadingData() +{ + return this->m_data.heading; +} + +void Object::SetX(float pos) +{ + this->m_data.x = pos; + + 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::SetY(float pos) +{ + this->m_data.y = pos; + + 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::Depop() +{ + EQApplicationPacket* app = new EQApplicationPacket(); + this->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + entity_list.RemoveObject(this->GetID()); +} + +void Object::Repop() +{ + 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::SetZ(float pos) +{ + this->m_data.z = pos; + + 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::SetModelName(const char* modelname) +{ + strn0cpy(m_data.object_name, modelname, sizeof(m_data.object_name)); // 32 is the max for chars in object_name, this should be safe + 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); +} + +const char* Object::GetModelName() +{ + return this->m_data.object_name; +} + +void Object::SetIcon(uint32 icon) +{ + this->m_icon = icon; +} + +uint32 Object::GetItemID() +{ + if (this->m_inst == 0) + { + return 0; + } + + const Item_Struct* item = this->m_inst->GetItem(); + + if (item == 0) + { + return 0; + } + + return item->ID; +} + +void Object::SetItemID(uint32 itemid) +{ + safe_delete(this->m_inst); + + if (itemid) + { + this->m_inst = database.CreateItem(itemid); + } +} + +void Object::GetObjectData(Object_Struct* Data) +{ + if (Data) + { + memcpy(Data, &this->m_data, sizeof(this->m_data)); + } +} + +void Object::SetObjectData(Object_Struct* Data) +{ + if (Data) + { + memcpy(&this->m_data, Data, sizeof(this->m_data)); + } +} + +void Object::GetLocation(float* x, float* y, float* z) +{ + if (x) + { + *x = this->m_data.x; + } + + if (y) + { + *y = this->m_data.y; + } + + if (z) + { + *z = this->m_data.z; + } +} + +void Object::SetLocation(float x, float y, float z) +{ + this->m_data.x = x; + this->m_data.y = y; + this->m_data.z = z; + 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::GetHeading(float* heading) +{ + if (heading) + { + *heading = this->m_data.heading; + } +} + +void Object::SetHeading(float heading) +{ + this->m_data.heading = heading; + 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::SetEntityVariable(const char *id, const char *m_var) +{ + std::string n_m_var = m_var; + o_EntityVariables[id] = n_m_var; +} + +const char* Object::GetEntityVariable(const char *id) +{ + if(!id) + return NULL; + + std::map::iterator iter = o_EntityVariables.find(id); + if(iter != o_EntityVariables.end()) + { + return iter->second.c_str(); + } + return NULL; +} + +bool Object::EntityVariableExists(const char * id) +{ + if(!id) + return false; + + std::map::iterator iter = o_EntityVariables.find(id); + if(iter != o_EntityVariables.end()) + { + return true; + } + return false; +} diff --git a/zone/PlayerCorpse.cpp b/zone/PlayerCorpse.cpp new file mode 100644 index 000000000..82da1a827 --- /dev/null +++ b/zone/PlayerCorpse.cpp @@ -0,0 +1,2085 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 +*/ +/* +New class for handeling corpses and everything associated with them. +Child of the Mob class. +-Quagmire +*/ +#include "../common/debug.h" +#include +#include +#include +#include +#include +using namespace std; +#ifdef _WINDOWS +#define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +#include "masterentity.h" +#include "../common/packet_functions.h" +#include "../common/crc32.h" +#include "StringIDs.h" +#include "worldserver.h" +#include "../common/rulesys.h" +#include "QuestParserCollection.h" + +extern EntityList entity_list; +extern Zone* zone; +extern WorldServer worldserver; +extern npcDecayTimes_Struct npcCorpseDecayTimes[100]; + +void Corpse::SendEndLootErrorPacket(Client* client) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_LootComplete, 0); + client->QueuePacket(outapp); + safe_delete(outapp); +} + +void Corpse::SendLootReqErrorPacket(Client* client, uint8 response) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_MoneyOnCorpse, sizeof(moneyOnCorpseStruct)); + moneyOnCorpseStruct* d = (moneyOnCorpseStruct*) outapp->pBuffer; + d->response = response; + d->unknown1 = 0x5a; + d->unknown2 = 0x40; + client->QueuePacket(outapp); + 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; + + 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; + } + } + + 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; + } + 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; + + switch(dbpc->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, 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(); + + // 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; + } +} + +// 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), + corpse_decay_timer(in_decaytime), + corpse_res_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; + if (in_itemlist) { + itemlist = *in_itemlist; + in_itemlist->clear(); + } + + 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()); + 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); + break; + } + } + if(IsEmpty()) + { + corpse_decay_timer.SetTimer(RuleI(NPC,EmptyNPCCorpseDecayTimeMS)+1000); + } + + if(in_npc->HasPrivateCorpse()) + { + corpse_delay_timer.SetTimer(corpse_decay_timer.GetRemainingTime() + 1000); + } + + // Added By Hogie -- End + for (int i=0; irezzexp = 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_decay_timer(RuleI(Character, CorpseDecayTimeMS)), + corpse_res_timer(RuleI(Character, CorpseResTimeMS)), + corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), + corpse_graveyard_timer(RuleI(Zone, GraveyardTimeMS)), + loot_cooldown_timer(10) +{ + int i; + PlayerProfile_Struct *pp = &client->GetPP(); + ItemInst *item; + + if(!zone->HasGraveyard()) { + corpse_graveyard_timer.Disable(); + } + + memset(item_tint, 0, sizeof(item_tint)); + for (i=0; iCharacterID(); + dbid = 0; + p_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 + become_npc = false; + + SetPKItem(0); + + 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.) + if (!RuleB(Character, RespawnFromHover) || client->GetClientVersion() < EQClientSoF) { + SetCash(pp->copper, pp->silver, pp->gold, pp->platinum); + pp->copper = 0; + pp->silver = 0; + pp->gold = 0; + pp->platinum = 0; + } + + // get their tints + memcpy(item_tint, &client->GetPP().item_tint, sizeof(item_tint)); + + // solar: TODO soulbound items need not be added to corpse, but they need + // to go into the regular slots on the player, out of bags + + // worn + inventory + cursor + std::list removed_list; + bool cursor = false; + for(i = 0; i <= 30; i++) + { + if(i == 21 && client->GetClientVersion() >= EQClientSoF) { + item = client->GetInv().GetItem(9999); + if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent)) { + std::list slot_list = MoveItemToCorpse(client, item, 9999); + removed_list.merge(slot_list); + } + + } + + item = client->GetInv().GetItem(i); + if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent)) { + std::list slot_list = MoveItemToCorpse(client, item, i); + removed_list.merge(slot_list); + } + } + + // cursor queue // (change to first client that supports 'death hover' mode, if not SoF.) + if (!RuleB(Character, RespawnFromHover) || client->GetClientVersion() < EQClientSoF) { + + // bumped starting assignment to 8001 because any in-memory 'slot 8000' item was moved above as 'slot 30' + // 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++) { + item = *it; + 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; + } + } + } + + 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) { + first = false; + } else { + ss << " OR "; + } + ss << "slotid=" << (*iter); + iter++; + } + ss << ")"; + database.RunQuery(ss.str().c_str(), ss.str().length()); + } + + if(cursor) { // all cursor items should be on corpse (client < SoF or RespawnFromHover = false) + while(!client->GetInv().CursorEmpty()) + client->DeleteItemInInventory(SLOT_CURSOR, 0, false, false); + } + else { // only visible cursor made it to corpse (client >= Sof and RespawnFromHover = true) + std::list::const_iterator start = client->GetInv().cursor_begin(); + std::list::const_iterator finish = client->GetInv().cursor_end(); + database.SaveCursor(client->CharacterID(), start, finish); + } + + client->CalcBonuses(); // will only affect offline profile viewing of dead characters..unneeded overhead + client->Save(); + } //end "not leaving naked corpses" + + Rezzed(false); + Save(); +} + +// solar: helper function for client corpse constructor +std::list Corpse::MoveItemToCorpse(Client *client, ItemInst *item, int16 equipslot) +{ + int bagindex; + int16 interior_slot; + ItemInst *interior_item; + std::list returnlist; + + AddItem(item->GetItem()->ID, item->GetCharges(), equipslot, item->GetAugmentItemID(0), item->GetAugmentItemID(1), item->GetAugmentItemID(2), item->GetAugmentItemID(3), item->GetAugmentItemID(4)); + 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 >= 22 && equipslot <=30))) // Limit the bag check to inventory and cursor slots. + { + for(bagindex = 0; bagindex <= 9; 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) + { + 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); + } + } + } + client->DeleteItemInInventory(equipslot, 0, true, false); + 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), + corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)), + corpse_res_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) + 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; + itemlist = *in_itemlist; + in_itemlist->clear(); + + strcpy(orgname, 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]; + memset(dbpc, 0, tmpsize); + dbpc->itemcount = tmp; + dbpc->size = this->size; + dbpc->locked = pLocked; + 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->class_ = class_; + dbpc->gender = gender; + dbpc->deity = deity; + dbpc->level = level; + dbpc->texture = this->texture; + dbpc->helmtexture = this->helmtexture; + dbpc->exp = rezzexp; + + memcpy(dbpc->item_tint, item_tint, sizeof(dbpc->item_tint)); + dbpc->haircolor = haircolor; + dbpc->beardcolor = beardcolor; + dbpc->eyecolor2 = eyecolor1; + dbpc->hairstyle = hairstyle; + dbpc->face = luclinface; + dbpc->beard = beard; + dbpc->drakkin_heritage = drakkin_heritage; + dbpc->drakkin_tattoo = drakkin_tattoo; + dbpc->drakkin_details = drakkin_details; + + uint32 x = 0; + ItemList::iterator cur,end; + cur = itemlist.begin(); + end = itemlist.end(); + for(; cur != end; cur++) { + ServerLootItem_Struct* item = *cur; + memcpy((char*) &dbpc->items[x++], (char*) item, sizeof(player_lootitem::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); + } + else + dbid = database.UpdatePlayerCorpse(dbid, charid, orgname, zone->GetZoneID(), zone->GetInstanceID(), (uchar*) dbpc, tmpsize, x_pos, y_pos, z_pos, heading,Rezzed()); + safe_delete_array(dbpc); + if (dbid == 0) { + cout << "Error: Failed to save player corpse '" << this->GetName() << "'" << endl; + return false; + } + return true; +} + +void Corpse::Delete() { + if (IsPlayerCorpse() && dbid != 0) + database.DeletePlayerCorpse(dbid); + dbid = 0; + + p_depop = true; +} + +void Corpse::Bury() { + if (IsPlayerCorpse() && dbid != 0) + database.BuryPlayerCorpse(dbid); + dbid = 0; + + p_depop = true; +} + +void Corpse::Depop() { + if (IsNPCCorpse()) + p_depop = true; +} + +void Corpse::DepopCorpse() { + p_depop = true; +} + +uint32 Corpse::CountItems() { + return itemlist.size(); +} + +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; + 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; + itemlist.push_back(item); +} + +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) + { + sitem = *cur; + break; + } + } + + if (sitem && bag_item_data && Inventory::SupportsContainers(sitem->equipSlot)) + { + int16 bagstart = Inventory::CalcSlotId(sitem->equipSlot, 0); + + cur = itemlist.begin(); + end = itemlist.end(); + for(; cur != end; cur++) { + sitem2 = *cur; + if(sitem2->equipSlot >= bagstart && sitem2->equipSlot < bagstart + 10) + { + bag_item_data[sitem2->equipSlot - bagstart] = sitem2; + } + } + } + + return sitem; +} + +uint32 Corpse::GetWornItem(int16 equipSlot) const { + ItemList::const_iterator cur,end; + cur = itemlist.begin(); + end = itemlist.end(); + for(; cur != end; cur++) { + ServerLootItem_Struct* item = *cur; + if (item->equipSlot == equipSlot) + { + return item->item_id; + } + } + + return 0; +} + +void Corpse::RemoveItem(uint16 lootslot) +{ + + if (lootslot == 0xFFFF) + return; + + ItemList::iterator cur,end; + cur = itemlist.begin(); + end = itemlist.end(); + for(; cur != end; cur++) { + ServerLootItem_Struct* sitem = *cur; + if (sitem->lootslot == lootslot) + { + RemoveItem(sitem); + return; + } + } +} + +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; + itemlist.erase(cur); + + material = Inventory::CalcMaterialFromSlot(sitem->equipSlot); + if(material != 0xFF) + SendWearChange(material); + + safe_delete(sitem); + + return; + } + } +} + +void Corpse::SetCash(uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_platinum) { + this->copper = in_copper; + this->silver = in_silver; + this->gold = in_gold; + this->platinum = in_platinum; + pIsChanged = true; +} + +void Corpse::RemoveCash() { + this->copper = 0; + this->silver = 0; + this->gold = 0; + this->platinum = 0; + pIsChanged = 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) + return false; + + if(corpse_delay_timer.Check()) + { + for (int i=0; iHasGraveyard()) { + Save(); + p_depop = true; + database.GraveyardPlayerCorpse(dbid, 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->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_graveyard_timer.Disable(); + return false; + } + /* + if(corpse_res_timer.Check()) { + can_rez = false; + corpse_res_timer.Disable(); + } + */ + if(corpse_decay_timer.Check()) { + if(!RuleB(Zone, EnableShadowrest)) + Delete(); + else { + if(database.BuryPlayerCorpse(dbid)) { + Save(); + p_depop = true; + dbid = 0; + LogFile->write(EQEMuLog::Debug, "Tagged %s player corpse has burried.", this->GetName()); + } + else + { + LogFile->write(EQEMuLog::Error, "Unable to bury %s player corpse.", this->GetName()); + return true; + } + } + corpse_decay_timer.Disable(); + return false; + } + + return true; +} + +void Corpse::SetDecayTimer(uint32 decaytime) { + if (decaytime == 0) + corpse_decay_timer.Trigger(); + else + corpse_decay_timer.Start(decaytime); +} + +bool Corpse::CanMobLoot(int charid) { + uint8 z=0; + for(int i=0; i= MAX_LOOTERS) + return; + if(them == NULL || !them->IsClient()) + return; + + 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) { + SendLootReqErrorPacket(client, 0); + return; + } + + if(IsPlayerCorpse() && dbid == 0) { + // SendLootReqErrorPacket(client, 0); + client->Message(13, "Warning: Corpse's dbid = 0! Corpse will not survive zone shutdown!"); + cout << "Error: PlayerCorpse::MakeLootRequestPackets: dbid = 0!" << endl; + // return; + } + + if(pLocked && client->Admin() < 100) { + SendLootReqErrorPacket(client, 0); + client->Message(13, "Error: Corpse locked by GM."); + return; + } + + if(BeingLootedBy == 0) { BeingLootedBy = 0xFFFFFFFF; } + + if(this->BeingLootedBy != 0xFFFFFFFF) { + // lets double check.... + Entity* looter = entity_list.GetID(this->BeingLootedBy); + if(looter == 0) { this->BeingLootedBy = 0xFFFFFFFF; } + } + + uint8 tCanLoot = 1; + bool lootcoin = false; + if(database.GetVariable("LootCoin", tmp, 9)) { lootcoin = (atoi(tmp) == 1); } + + if(this->BeingLootedBy != 0xFFFFFFFF && this->BeingLootedBy != client->GetID()) { + SendLootReqErrorPacket(client, 0); + tCanLoot = 0; + } + 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(tCanLoot >= 2 || (tCanLoot == 1 && client->Admin() >= 100 && client->GetGM())) { + this->BeingLootedBy = 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); } + } + } + + if(!IsPlayerCorpse() && client->IsGrouped() && client->AutoSplitEnabled() && client->GetGroup()) { + d->copper = 0; + d->silver = 0; + d->gold = 0; + d->platinum = 0; + Group *cgroup = client->GetGroup(); + cgroup->SplitMoney(GetCopper(), GetSilver(), GetGold(), GetPlatinum(), client); + } + else { + d->copper = this->GetCopper(); + d->silver = this->GetSilver(); + d->gold = this->GetGold(); + d->platinum = this->GetPlatinum(); + client->AddMoneyToPP(GetCopper(), GetSilver(), GetGold(), GetPlatinum(), false); + } + + RemoveCash(); + Save(); + client->Save(); + } + + outapp->priority = 6; + client->QueuePacket(outapp); + safe_delete(outapp); + if(tCanLoot == 5) { + int pkitem = GetPKItem(); + const Item_Struct* item = database.GetItem(pkitem); + ItemInst* inst = database.CreateItem(item, item->MaxCharges); + if(inst) { + client->SendItemPacket(22, inst, ItemPacketLoot); + safe_delete(inst); + } + else { client->Message(13, "Could not find item number %i to send!!", GetPKItem()); } + + client->QueuePacket(app); + return; + } + + int i = 0; + const Item_Struct* item = 0; + ItemList::iterator cur,end; + cur = itemlist.begin(); + end = itemlist.end(); + + uint8 containercount = 0; + int corpselootlimit; + + if(client->GetClientVersion() >= EQClientRoF) { corpselootlimit = 34; } + else if(client->GetClientVersion() >= EQClientSoF) { corpselootlimit = 32; } + else if(client->GetClientVersion() == EQClientTitanium) { corpselootlimit = 31; } + else { corpselootlimit = 30; } + + for(; cur != end; cur++) { + ServerLootItem_Struct* item_data = *cur; + item_data->lootslot = 0xFFFF; + + // 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 <= 30 || item_data->equipSlot == 9999 || tCanLoot>=3 || + (item_data->equipSlot >= 8000 && item_data->equipSlot <= 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); + if(inst) { + client->SendItemPacket(i + 22, inst, ItemPacketLoot); // 22 is the corpse inventory start offset for Ti(EMu) + safe_delete(inst); + } + + item_data->lootslot = i; + } + } + + i++; + } + } + + if(IsPlayerCorpse() && (charid == 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."); + client->Message(0, "(%s contains %i additional %s.)", GetName(), (i - corpselootlimit), (i - corpselootlimit) == 1 ? "item" : "items"); + } + + 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()); + + 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"); + client->Message(0, "Inaccessable Corpse Item: %s", item->Name); + } + } + } + } + + // Disgrace: Client seems to require that we send the packet back... + client->QueuePacket(app); + + // This is required for the 'Loot All' feature to work for SoD clients. I expect it is to tell the client that the + // server has now sent all the items on the corpse. + 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. + client->QueuePacket(app); + + if(!loot_cooldown_timer.Check()) + { + SendEndLootErrorPacket(client); + 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()) + { + client->Message(13, "You may not loot an item while you have an item on your cursor."); + SendEndLootErrorPacket(client); + return; + } + + LootingItem_Struct* lootitem = (LootingItem_Struct*)app->pBuffer; + + if (this->BeingLootedBy != 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)) { + client->Message(13, "Error: This is a player corpse and you dont own it."); + SendEndLootErrorPacket(client); + return; + } + if (pLocked && 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){ + client->Message(13, "Error: You cannot loot any more items from this corpse."); + SendEndLootErrorPacket(client); + BeingLootedBy = 0xFFFFFFFF; + return; + } + const Item_Struct* item = 0; + ItemInst *inst = 0; + ServerLootItem_Struct* item_data = NULL, *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) + item_data = GetItem(lootitem->slot_id - 22); //dont allow them to loot entire bags of items as pvp reward + else + item_data = GetItem(lootitem->slot_id - 22, bag_item_data); + + if (GetPKItem()<=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 + inst = database.CreateItem(item); + } + + if (client && inst) + { + + if (client->CheckLoreConflict(item)) + { + client->Message_StringID(0,LOOT_LORE_ERROR); + SendEndLootErrorPacket(client); + BeingLootedBy = 0; + delete inst; + return; + } + + if(inst->IsAugmented()) + { + for(int i=0; iGetAugment(i); + if(itm) + { + if(client->CheckLoreConflict(itm->GetItem())) + { + client->Message_StringID(0,LOOT_LORE_ERROR); + SendEndLootErrorPacket(client); + BeingLootedBy = 0; + delete inst; + return; + } + } + } + } + + char buf[88]; + char corpse_name[64]; + strcpy(corpse_name, orgname); + snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(), EntityList::RemoveNumbers(corpse_name)); + buf[87] = '\0'; + parse->EventPlayer(EVENT_LOOT, client, buf, 0); + + 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) + { + ServerZoneAdventureDataReply_Struct *ad = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; + 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)) + client->PutLootInInventory(SLOT_CURSOR, *inst, bag_item_data); + } + else + { + client->PutLootInInventory(SLOT_CURSOR, *inst, bag_item_data); + } + // 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=0; i < 10; i++) + { + if (bag_item_data[i]) + { + RemoveItem(bag_item_data[i]); + } + } + } + + if(GetPKItem()!=-1) + SetPKItem(0); + + //now send messages to all interested parties + + //creates a link for the item + char *link = 0, *link2 = 0; //just like a db query :-) + client->MakeItemLink(link2, inst); + MakeAnyLenString(&link, "%c" "%s" "%s" "%c", + 0x12, + link2, + item->Name, + 0x12); + safe_delete_array(link2); + + client->Message_StringID(MT_LootMessages, LOOTED_MESSAGE, link); + if(!IsPlayerCorpse()) { + Group *g = client->GetGroup(); + if(g != NULL) { + g->GroupMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE, client->GetName(), link); + } else { + Raid *r = client->GetRaid(); + if(r != NULL) { + r->RaidMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE, client->GetName(), link); + } + } + } + safe_delete_array(link); + } + else + { + SendEndLootErrorPacket(client); + safe_delete(inst); + return; + } + + if (IsPlayerCorpse()) + client->SendItemLink(inst); + else + client->SendItemLink(inst, true); + + safe_delete(inst); +} + +void Corpse::EndLoot(Client* client, const EQApplicationPacket* app) { + EQApplicationPacket* outapp = new EQApplicationPacket; + outapp->SetOpcode(OP_LootComplete); + outapp->size = 0; + client->QueuePacket(outapp); + safe_delete(outapp); + + //client->Save(); //inventory operations auto-commit + this->BeingLootedBy = 0xFFFFFFFF; + if (this->IsEmpty()) + Delete(); + else + Save(); +} + +void Corpse::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) +{ + Mob::FillSpawnStruct(ns, ForWho); + + ns->spawn.max_hp = 120; + + if (IsPlayerCorpse()) + ns->spawn.NPC = 3; + else + ns->spawn.NPC = 2; +} + +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); + + ItemList::iterator cur,end; + cur = itemlist.begin(); + end = itemlist.end(); + + int corpselootlimit; + + if (to->GetClientVersion() >= EQClientSoF) { corpselootlimit = 32; } + else if (to->GetClientVersion() == EQClientTitanium) { corpselootlimit = 31; } + else { corpselootlimit = 30; } + + for(; cur != end; cur++) { + ServerLootItem_Struct* sitem = *cur; + + if (IsPlayerCorpse()) { + if (sitem->equipSlot >= 251 && sitem->equipSlot <= 340) + sitem->lootslot = 0xFFFF; + else + x < corpselootlimit ? sitem->lootslot = x : sitem->lootslot = 0xFFFF; + + 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); + else + to->Message((sitem->lootslot == 0xFFFF), "Error: 0x%04x", sitem->item_id); + + if (sitem->lootslot != 0xFFFF) + x++; + + y++; + } + else { + sitem->lootslot=y; + const Item_Struct* item = database.GetItem(sitem->item_id); + + if (item) + to->Message(0, "LootSlot: %i Item: %s (%d), Count: %i", sitem->lootslot, item->Name, item->ID, sitem->charges); + else + to->Message(0, "Error: 0x%04x", sitem->item_id); + + y++; + } + } + + if (IsPlayerCorpse()) { + to->Message(0, "%i visible %s (%i total) on %s (DBID: %i).", x, x==1?"item":"items", y, this->GetName(), this->GetDBID()); + } + else { + to->Message(0, "%i %s on %s.", y, y==1?"item":"items", this->GetName()); + } +} + +bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) +{ + uint32 dist2 = 10000; // pow(100, 2); + if (!spell) { + if (this->GetCharID() == client->CharacterID()) { + if (IsLocked() && client->Admin() < 100) { + client->Message(13, "That corpse is locked by a GM."); + return false; + } + if (!CheckDistance || (DistNoRootNoZ(*client) <= dist2)) + { + GMMove(client->GetX(), client->GetY(), client->GetZ()); + pIsChanged = true; + } + else + { + client->Message(0, "Corpse is too far away."); + return false; + } + } + else + { + 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)) + { + GMMove(client->GetX(), client->GetY(), client->GetZ()); + pIsChanged = true; + } + else + { + client->Message(0, "Corpse is too far away."); + return false; + } + consented = true; + } + } + if(!consented) + { + client->Message(0, "You do not have permission to move this corpse."); + return false; + } + } + } + else { + GMMove(client->GetX(), client->GetY(), client->GetZ()); + pIsChanged = true; + } + Save(); + return true; +} + +void Corpse::CompleteRezz(){ + rezzexp = 0; + pIsChanged = true; + this->Save(); +} + +void Corpse::Spawn() { + EQApplicationPacket* app = new EQApplicationPacket; + this->CreateSpawnPacket(app, this); + entity_list.QueueClients(this, app); + 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); + cerr << "Error1 in DeleteGraveyard query " << errbuf << endl; + return false; + } + + if (affected_rows == 0) { + cerr << "Error2 in DeleteGraveyard query: affected_rows = 0" << 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); + cerr << "Error3 in DeleteGraveyard query " << errbuf << endl; + return false; + } + safe_delete_array(query); + + if (affected_rows == 0) { + cerr << "Error4 in DeleteGraveyard query: affected_rows = 0" << 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); + cerr << "Error1 in AddGraveyardIDToZone query " << errbuf << endl; + return 0; + } + safe_delete_array(query); + + if (affected_rows == 0) { + cerr << "Error2 in AddGraveyardIDToZone query: affected_rows = 0" << 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); + cerr << "Error1 in NewGraveyardRecord query " << errbuf << endl; + return 0; + } + safe_delete_array(query); + + if (affected_rows == 0) { + cerr << "Error2 in NewGraveyardRecord query: affected_rows = 0" << endl; + return 0; + } + + if(new_graveyard_id <= 0) { + cerr << "Error3 in NewGraveyardRecord query: new_graveyard_id <= 0" << 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); + cerr << "Error1 in GraveyardPlayerCorpse query " << errbuf << endl; + return 0; + } + safe_delete_array(query); + + if (affected_rows == 0) { + cerr << "Error2 in GraveyardPlayerCorpse query: affected_rows = 0" << 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); + cerr << "Error1 in UpdatePlayerCorpse query " << errbuf << endl; + return 0; + } + safe_delete_array(query); + + if (affected_rows == 0) { + cerr << "Error2 in UpdatePlayerCorpse query: affected_rows = 0" << endl; + return 0; + } + if(rezzed){ + if (!RunQuery(query, MakeAnyLenString(&query, "update player_corpses set rezzed = 1 WHERE id=%d",dbid), errbuf)) { + safe_delete_array(query); + cerr << "Error in UpdatePlayerCorpse/Rezzed query: " << errbuf << endl; + } + } + 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); + cerr << "Error1 in CreatePlayerCorpse query " << errbuf << endl; + return 0; + } + safe_delete_array(query); + + if (affected_rows == 0) { + cerr << "Error2 in CreatePlayerCorpse query: affected_rows = 0" << endl; + return 0; + } + + if (last_insert_id == 0) { + cerr << "Error3 in CreatePlayerCorpse query: last_insert_id = 0" << 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 + cerr << "Error in CreatePlayerCorpseBackup query: affected_rows != 1" << endl; + } + else + cerr << "Error in CreatePlayerCorpseBackup query " << errbuf << endl; + } + safe_delete_array(query); + } + else { + cerr << "Error in CreatePlayerCorpseBackup: dbid = 0" << 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 { + cerr << "Error in GetPlayerBurriedCorpseCount query '" << query << "' " << errbuf << 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 { + cerr << "Error in GetPlayerCorpseCount query '" << query << "' " << errbuf << 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 { + cerr << "Error in SummonBurriedPlayerCorpse query '" << query << "' " << errbuf << 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 + cerr << "Error2 in UnburyPlayerCorpse query: affected_rows NOT EQUAL to 1, as expected." << endl; + } + else + cerr << "Error1 in UnburyPlayerCorpse query " << errbuf << 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 { + cerr << "Error in LoadPlayerCorpse query '" << query << "' " << errbuf << endl; + 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 { + cerr << "Error in LoadPlayerCorpses query '" << query << "' " << errbuf << endl; + 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 { + cerr << "Error in GetFirstCorpseID query '" << query << "' " << errbuf << 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)) { + cerr << "Error in BuryPlayerCorpse query '" << query << "' " << errbuf << 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)) { + cerr << "Error in BuryPlayerCorpse query '" << query << "' " << errbuf << 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)) { + cerr << "Error in DeletePlayerCorpse query '" << query << "' " << errbuf << 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 > 8) + { + return 0; + } + + invslot = Inventory::CalcSlotFromMaterial(material_slot); + if(invslot == -1) + return 0; + + return GetWornItem(invslot); +} + +uint32 Corpse::GetEquipmentColor(uint8 material_slot) const { + const Item_Struct *item; + + if(material_slot > 8) + { + return 0; + } + + item = database.GetItem(GetEquipment(material_slot)); + if(item != 0) + { + return item_tint[material_slot].rgb.use_tint ? + item_tint[material_slot].color : + item->Color; + } + + return 0; +} + +void Corpse::AddLooter(Mob* who) +{ + for (int i=0; iCastToClient()->CharacterID(); + break; + } + } +} + +void Corpse::LoadPlayerCorpseDecayTime(uint32 dbid){ + if(!dbid) + 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); + } + else + safe_delete_array(query); +} + +/* +void Corpse::CastRezz(uint16 spellid, Mob* Caster){ + if(Rezzed()){ + if(Caster && Caster->IsClient()) + Caster->Message(13,"This character has already been resurrected."); + return; + } + + 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/PlayerCorpse.h b/zone/PlayerCorpse.h new file mode 100644 index 000000000..e5fbfc7c4 --- /dev/null +++ b/zone/PlayerCorpse.h @@ -0,0 +1,142 @@ +/* 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 CORPSE_H +#define CORPSE_H + +#include "mob.h" + +class Client; +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); + + 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(); + + //abstract virtual function implementations requird by base abstract class + virtual void Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillType attack_skill) { return; } + virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false) { return; } + virtual bool Attack(Mob* other, int Hand = 13, bool FromRiposte = false, bool IsStrikethrough = true, bool IsFromSpell = false) { return false; } + virtual bool HasRaid() { return false; } + virtual bool HasGroup() { return false; } + virtual Raid* GetRaid() { return 0; } + virtual Group* GetGroup() { return 0; } + + 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); + void RemoveItem(uint16 lootslot); + void RemoveItem(ServerLootItem_Struct* item_data); + 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); + 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(); + + char orgname[64]; + uint32 GetEquipment(uint8 material_slot) const; // returns item id + uint32 GetEquipmentColor(uint8 material_slot) const; + inline int GetRezzExp() { return rezzexp; } + +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; + uint32 silver; + uint32 gold; + uint32 platinum; + bool p_depop; + uint32 BeingLootedBy; + uint32 rezzexp; + bool rez; + bool can_rez; + 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; + Timer corpse_graveyard_timer; + Timer loot_cooldown_timer; + Color_Struct item_tint[9]; +}; + +#endif diff --git a/zone/QGlobals.cpp b/zone/QGlobals.cpp new file mode 100644 index 000000000..67bba76f0 --- /dev/null +++ b/zone/QGlobals.cpp @@ -0,0 +1,138 @@ +#include "../common/debug.h" +#include "../common/MiscFunctions.h" +#include "QGlobals.h" +#include "zonedb.h" + +void QGlobalCache::AddGlobal(uint32 id, QGlobal global) +{ + global.id = id; + qGlobalBucket.push_back(global); +} + +void QGlobalCache::RemoveGlobal(std::string name, uint32 npcID, uint32 charID, uint32 zoneID) +{ + std::list::iterator iter = qGlobalBucket.begin(); + while(iter != qGlobalBucket.end()) + { + if(name.compare((*iter).name) == 0) + { + if((npcID == (*iter).npc_id || (*iter).npc_id == 0) && (charID == (*iter).char_id || (*iter).char_id == 0) && (zoneID == (*iter).zone_id || (*iter).zone_id == 0)) + { + qGlobalBucket.erase(iter); + return; + } + } + ++iter; + } +} + +void QGlobalCache::Combine(std::list &cacheA, std::list cacheB, uint32 npcID, uint32 charID, uint32 zoneID) +{ + std::list::iterator iter = cacheB.begin(); + while(iter != cacheB.end()) + { + QGlobal cur = (*iter); + + if((cur.npc_id == npcID || cur.npc_id == 0) && (cur.char_id == charID || cur.char_id == 0) && (cur.zone_id == zoneID || cur.zone_id == 0)) + { + if(Timer::GetTimeSeconds() < cur.expdate) + { + cacheA.push_back(cur); + } + } + ++iter; + } +} + +void QGlobalCache::PurgeExpiredGlobals() +{ + if(!qGlobalBucket.size()) + return; + + std::list::iterator iter = qGlobalBucket.begin(); + while(iter != qGlobalBucket.end()) + { + QGlobal cur = (*iter); + if(Timer::GetTimeSeconds() > cur.expdate) + { + iter = qGlobalBucket.erase(iter); + continue; + } + ++iter; + } +} + +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); +} + +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); +} + +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); +} +void QGlobalCache::LoadByGlobalContext() +{ + 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 = 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); +} diff --git a/zone/QGlobals.h b/zone/QGlobals.h new file mode 100644 index 000000000..f449b4fb8 --- /dev/null +++ b/zone/QGlobals.h @@ -0,0 +1,42 @@ +#ifndef __QGLOBALS__H +#define __QGLOBALS__H + +#include +#include +#include +#include "../common/timer.h" + +struct QGlobal +{ + QGlobal() { } + QGlobal(std::string g_name, uint32 c_id, uint32 n_id, uint32 z_id, std::string n_value, uint32 expire_date) + : name(g_name), char_id(c_id), npc_id(n_id), zone_id(z_id), value(n_value), expdate(expire_date) { id = 0; } + std::string name; + uint32 char_id; + uint32 npc_id; + uint32 zone_id; + std::string value; + uint32 expdate; + uint32 id; +}; + +class QGlobalCache +{ +public: + void AddGlobal(uint32 id, QGlobal global); + void RemoveGlobal(std::string name, uint32 npcID, uint32 charID, uint32 zoneID); + std::list GetBucket() { return qGlobalBucket; } + + //assumes cacheA is already a valid or empty list and doesn't check for valid items. + static void Combine(std::list &cacheA, std::list cacheB, uint32 npcID, uint32 charID, uint32 zoneID); + + void PurgeExpiredGlobals(); + void LoadByNPCID(uint32 npcID); //npc + void LoadByCharID(uint32 charID); //client + void LoadByZoneID(uint32 zoneID); //zone + void LoadByGlobalContext(); //zone +protected: + std::list qGlobalBucket; +}; + +#endif diff --git a/zone/Quest.cpp b/zone/Quest.cpp new file mode 100644 index 000000000..7128b9950 --- /dev/null +++ b/zone/Quest.cpp @@ -0,0 +1,101 @@ +/* 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 "../common/debug.h" +#include +#include +#include +#include + +// Disgrace: for windows compile +#ifdef WIN32 + #include + #define snprintf _snprintf +#else + #include "../common/unix.h" +#endif + +#include "Quest.h" + +pquest_entry Quest::m_pQuests; +int Quest::m_nQuests; + +Quest::Quest() +{ + m_pQuests = NULL; + m_nQuests = 0; +} + +Quest::~Quest() +{ + for( int i=0;i +#include +#include + +extern Zone* zone; + +QuestParserCollection::QuestParserCollection() { + _player_quest_status = QuestUnloaded; + _global_player_quest_status = QuestUnloaded; +} + +QuestParserCollection::~QuestParserCollection() { +} + +void QuestParserCollection::RegisterQuestInterface(QuestInterface *qi, std::string ext) { + _interfaces[qi->GetIdentifier()] = qi; + _extensions[qi->GetIdentifier()] = ext; + _load_precedence.push_back(qi); +} + +void QuestParserCollection::AddVar(std::string name, std::string val) { + std::list::iterator iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + (*iter)->AddVar(name, val); + iter++; + } +} + +void QuestParserCollection::ReloadQuests(bool reset_timers) { + _npc_quest_status.clear(); + _player_quest_status = QuestUnloaded; + _global_player_quest_status = QuestUnloaded; + _global_npc_quest_status = QuestUnloaded; + _spell_quest_status.clear(); + _item_quest_status.clear(); + std::list::iterator iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + (*iter)->ReloadQuests(reset_timers); + iter++; + } +} + +bool QuestParserCollection::HasQuestSub(uint32 npcid, const char *subname) { + std::map::iterator iter = _npc_quest_status.find(npcid); + if(_global_npc_quest_status == QuestUnloaded){ + QuestInterface *qi = GetQIByGlobalNPCQuest(); + if(qi) { + _global_npc_quest_status = qi->GetIdentifier(); + return qi->HasGlobalQuestSub(subname); + } + } + if(iter != _npc_quest_status.end()) { + //loaded or failed to load + if(iter->second != QuestFailedToLoad) { + std::map::iterator qiter = _interfaces.find(iter->second); + return qiter->second->HasQuestSub(npcid, subname) || qiter->second->HasGlobalQuestSub(subname); + } + } else { + QuestInterface *qi = GetQIByGlobalNPCQuest(); + if(qi) { + _global_npc_quest_status = qi->GetIdentifier(); + } + + qi = GetQIByNPCQuest(npcid); + if(qi) { + _npc_quest_status[npcid] = qi->GetIdentifier(); + return qi->HasQuestSub(npcid, subname) || qi->HasGlobalQuestSub(subname); + } else { + _npc_quest_status[npcid] = QuestFailedToLoad; + } + } + return false; +} + +bool QuestParserCollection::PlayerHasQuestSub(const char *subname) { + if(_player_quest_status == QuestUnloaded) { + QuestInterface *qi = GetQIByGlobalPlayerQuest(); + if(qi) { + _global_player_quest_status = qi->GetIdentifier(); + } + + qi = GetQIByPlayerQuest(); + if(qi) { + _player_quest_status = qi->GetIdentifier(); + return qi->PlayerHasQuestSub(subname) || qi->GlobalPlayerHasQuestSub(subname); + } + } else if(_player_quest_status != QuestFailedToLoad) { + std::map::iterator iter = _interfaces.find(_player_quest_status); + return iter->second->PlayerHasQuestSub(subname) || iter->second->GlobalPlayerHasQuestSub(subname); + } + return false; +} + +bool QuestParserCollection::SpellHasQuestSub(uint32 spell_id, const char *subname) { + std::map::iterator iter = _spell_quest_status.find(spell_id); + if(iter != _spell_quest_status.end()) { + //loaded or failed to load + if(iter->second != QuestFailedToLoad) { + std::map::iterator qiter = _interfaces.find(iter->second); + return qiter->second->SpellHasQuestSub(spell_id, subname); + } + } else { + QuestInterface *qi = GetQIBySpellQuest(spell_id); + if(qi) { + _spell_quest_status[spell_id] = qi->GetIdentifier(); + return qi->SpellHasQuestSub(spell_id, subname); + } else { + _spell_quest_status[spell_id] = QuestFailedToLoad; + } + } + return false; +} + +bool QuestParserCollection::ItemHasQuestSub(ItemInst *itm, const char *subname) { + std::string item_script; + if(strcmp("EVENT_SCALE_CALC", subname) == 0 || strcmp("EVENT_ITEM_ENTERZONE", subname) == 0) { + item_script = itm->GetItem()->CharmFile; + } else if(strcmp("EVENT_ITEM_CLICK", subname) == 0 || strcmp("EVENT_ITEM_CLICK_CAST", subname) == 0) { + item_script = "script_"; + item_script += itoa(itm->GetItem()->ScriptFileID); + } else { + item_script = itoa(itm->GetItem()->ID); + } + + std::map::iterator iter = _item_quest_status.find(item_script); + if(iter != _item_quest_status.end()) { + //loaded or failed to load + if(iter->second != QuestFailedToLoad) { + std::map::iterator qiter = _interfaces.find(iter->second); + return qiter->second->ItemHasQuestSub(itm, subname); + } + } else { + QuestInterface *qi = GetQIByItemQuest(item_script); + if(qi) { + _item_quest_status[item_script] = qi->GetIdentifier(); + return qi->ItemHasQuestSub(itm, subname); + } else { + _item_quest_status[item_script] = QuestFailedToLoad; + } + } + return false; +} + +void QuestParserCollection::EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data) { + std::map::iterator iter = _npc_quest_status.find(npc->GetNPCTypeID()); + if(iter != _npc_quest_status.end()) { + //loaded or failed to load + if(iter->second != QuestFailedToLoad) { + std::map::iterator qiter = _interfaces.find(iter->second); + qiter->second->EventNPC(evt, npc, init, data, extra_data); + } + } else { + QuestInterface *qi = GetQIByNPCQuest(npc->GetNPCTypeID()); + if(qi) { + _npc_quest_status[npc->GetNPCTypeID()] = qi->GetIdentifier(); + qi->EventNPC(evt, npc, init, data, extra_data); + } else { + _npc_quest_status[npc->GetNPCTypeID()] = QuestFailedToLoad; + } + } + + // K, lets also parse templates/global_npc.pl + if(_global_npc_quest_status != QuestUnloaded) { + QuestInterface *qi = GetQIByGlobalNPCQuest(); + if(qi) { + _global_npc_quest_status = qi->GetIdentifier(); + qi->EventGlobalNPC(evt, npc, init, data, extra_data); + } + } else { + if(_global_npc_quest_status != QuestFailedToLoad) { + std::map::iterator iter = _interfaces.find(_global_npc_quest_status); + if(iter != _interfaces.end()) + iter->second->EventGlobalNPC(evt, npc, init, data, extra_data); + } + } +} + +void QuestParserCollection::EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data) { + if(_player_quest_status == QuestUnloaded) { + QuestInterface *qi = GetQIByGlobalPlayerQuest(); + if(qi) { + _global_player_quest_status = qi->GetIdentifier(); + qi->EventGlobalPlayer(evt, client, data, extra_data); + } + + qi = GetQIByPlayerQuest(); + if(qi) { + _player_quest_status = qi->GetIdentifier(); + qi->EventPlayer(evt, client, data, extra_data); + } + + } else { + if(_global_player_quest_status != QuestFailedToLoad) { + std::map::iterator iter = _interfaces.find(_global_player_quest_status); + if(iter != _interfaces.end()) + iter->second->EventGlobalPlayer(evt, client, data, extra_data); + } + if(_player_quest_status != QuestFailedToLoad) { + std::map::iterator iter = _interfaces.find(_player_quest_status); + iter->second->EventPlayer(evt, client, data, extra_data); + } + } +} + +void QuestParserCollection::EventItem(QuestEventID evt, Client *client, ItemInst *item, uint32 objid, uint32 extra_data) { + std::string item_script; + if(evt == EVENT_SCALE_CALC || evt == EVENT_ITEM_ENTERZONE) { + item_script = item->GetItem()->CharmFile; + } else if(evt == EVENT_ITEM_CLICK || evt == EVENT_ITEM_CLICK_CAST) { + item_script = "script_"; + item_script += itoa(item->GetItem()->ScriptFileID); + } else { + item_script = itoa(item->GetItem()->ID); + } + + std::map::iterator iter = _item_quest_status.find(item_script); + if(iter != _item_quest_status.end()) { + //loaded or failed to load + if(iter->second != QuestFailedToLoad) { + std::map::iterator qiter = _interfaces.find(iter->second); + qiter->second->EventItem(evt, client, item, objid, extra_data); + } + } else { + QuestInterface *qi = GetQIByItemQuest(item_script); + if(qi) { + _item_quest_status[item_script] = qi->GetIdentifier(); + qi->EventItem(evt, client, item, objid, extra_data); + } else { + _item_quest_status[item_script] = QuestFailedToLoad; + } + } +} + +void QuestParserCollection::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data) { + std::map::iterator iter = _spell_quest_status.find(spell_id); + if(iter != _spell_quest_status.end()) { + //loaded or failed to load + if(iter->second != QuestFailedToLoad) { + std::map::iterator qiter = _interfaces.find(iter->second); + qiter->second->EventSpell(evt, npc, client, spell_id, extra_data); + } + } else { + QuestInterface *qi = GetQIBySpellQuest(spell_id); + if(qi) { + _spell_quest_status[spell_id] = qi->GetIdentifier(); + qi->EventSpell(evt, npc, client, spell_id, extra_data); + } else { + _spell_quest_status[spell_id] = QuestFailedToLoad; + } + } +} + +QuestInterface *QuestParserCollection::GetQIByNPCQuest(uint32 npcid) { + //first look for /quests/zone/npcid.ext (precedence) + std::string filename = "quests/"; + filename += zone->GetShortName(); + filename += "/"; + filename += itoa(npcid); + std::string tmp; + FILE *f = NULL; + + std::list::iterator iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + tmp = filename; + std::map::iterator ext = _extensions.find((*iter)->GetIdentifier()); + tmp += "."; + tmp += ext->second; + f = fopen(tmp.c_str(), "r"); + if(f) { + fclose(f); + return (*iter); + } + + iter++; + } + + //second look for /quests/zone/npcname.ext (precedence) + const NPCType *npc_type = database.GetNPCType(npcid); + if(!npc_type) { + return NULL; + } + std::string npc_name = npc_type->name; + int sz = static_cast(npc_name.length()); + for(int i = 0; i < sz; ++i) { + if(npc_name[i] == '`') { + npc_name[i] = '-'; + } + } + + filename = "quests/"; + filename += zone->GetShortName(); + filename += "/"; + filename += npc_name; + + iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + tmp = filename; + std::map::iterator ext = _extensions.find((*iter)->GetIdentifier()); + tmp += "."; + tmp += ext->second; + f = fopen(tmp.c_str(), "r"); + if(f) { + fclose(f); + return (*iter); + } + + iter++; + } + + //third look for /quests/zone/default.ext (precedence) + filename = "quests/"; + filename += zone->GetShortName(); + filename += "/"; + filename += "default"; + iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + tmp = filename; + std::map::iterator ext = _extensions.find((*iter)->GetIdentifier()); + tmp += "."; + tmp += ext->second; + f = fopen(tmp.c_str(), "r"); + if(f) { + fclose(f); + return (*iter); + } + + iter++; + } + + //fourth look for /quests/templates/npcid.ext (precedence) + filename = "quests/"; + filename += QUEST_TEMPLATES_DIRECTORY; + filename += "/"; + filename += itoa(npcid); + iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + tmp = filename; + std::map::iterator ext = _extensions.find((*iter)->GetIdentifier()); + tmp += "."; + tmp += ext->second; + f = fopen(tmp.c_str(), "r"); + if(f) { + fclose(f); + return (*iter); + } + + iter++; + } + + //fifth look for /quests/templates/npcname.ext (precedence) + filename = "quests/"; + filename += QUEST_TEMPLATES_DIRECTORY; + filename += "/"; + filename += npc_name; + iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + tmp = filename; + std::map::iterator ext = _extensions.find((*iter)->GetIdentifier()); + tmp += "."; + tmp += ext->second; + f = fopen(tmp.c_str(), "r"); + if(f) { + fclose(f); + return (*iter); + } + + iter++; + } + + //fifth look for /quests/templates/default.ext (precedence) + filename = "quests/"; + filename += QUEST_TEMPLATES_DIRECTORY; + filename += "/"; + filename += "default"; + iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + tmp = filename; + std::map::iterator ext = _extensions.find((*iter)->GetIdentifier()); + tmp += "."; + tmp += ext->second; + f = fopen(tmp.c_str(), "r"); + if(f) { + fclose(f); + return (*iter); + } + + iter++; + } + + return NULL; +} + +QuestInterface *QuestParserCollection::GetQIByPlayerQuest() { + + if(!zone) + return NULL; + + //first look for /quests/zone/player_v[instance_version].ext (precedence) + std::string filename = "quests/"; + filename += zone->GetShortName(); + filename += "/"; + filename += "player_v"; + filename += itoa(zone->GetInstanceVersion()); + std::string tmp; + FILE *f = NULL; + + std::list::iterator iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + tmp = filename; + std::map::iterator ext = _extensions.find((*iter)->GetIdentifier()); + tmp += "."; + tmp += ext->second; + f = fopen(tmp.c_str(), "r"); + if(f) { + fclose(f); + return (*iter); + } + + iter++; + } + + //second look for /quests/zone/player.ext (precedence) + filename = "quests/"; + filename += zone->GetShortName(); + filename += "/"; + filename += "player"; + + iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + tmp = filename; + std::map::iterator ext = _extensions.find((*iter)->GetIdentifier()); + tmp += "."; + tmp += ext->second; + f = fopen(tmp.c_str(), "r"); + if(f) { + fclose(f); + return (*iter); + } + + iter++; + } + + //third look for /quests/templates/player.ext (precedence) + filename = "quests/"; + filename += QUEST_TEMPLATES_DIRECTORY; + filename += "/"; + filename += "player"; + iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + tmp = filename; + std::map::iterator ext = _extensions.find((*iter)->GetIdentifier()); + tmp += "."; + tmp += ext->second; + f = fopen(tmp.c_str(), "r"); + if(f) { + fclose(f); + return (*iter); + } + + iter++; + } + + return NULL; +} + +QuestInterface *QuestParserCollection::GetQIByGlobalNPCQuest(){ + // simply look for templates/global_npc.pl + std::string filename = "quests/"; + filename += QUEST_TEMPLATES_DIRECTORY; + filename += "/"; + filename += "global_npc"; + std::string tmp; + FILE *f = NULL; + + std::list::iterator iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + tmp = filename; + std::map::iterator ext = _extensions.find((*iter)->GetIdentifier()); + tmp += "."; + tmp += ext->second; + f = fopen(tmp.c_str(), "r"); + if(f) { + fclose(f); + return (*iter); + } + + iter++; + } + + return NULL; +} + +QuestInterface *QuestParserCollection::GetQIByGlobalPlayerQuest() { + //first look for /quests/templates/player.ext (precedence) + std::string filename = "quests/"; + filename += QUEST_TEMPLATES_DIRECTORY; + filename += "/"; + filename += "global_player"; + std::string tmp; + FILE *f = NULL; + + std::list::iterator iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + tmp = filename; + std::map::iterator ext = _extensions.find((*iter)->GetIdentifier()); + tmp += "."; + tmp += ext->second; + f = fopen(tmp.c_str(), "r"); + if(f) { + fclose(f); + return (*iter); + } + + iter++; + } + + return NULL; +} + +QuestInterface *QuestParserCollection::GetQIBySpellQuest(uint32 spell_id) { + //first look for /quests/spells/spell_id.ext (precedence) + std::string filename = "quests/spells/"; + filename += itoa(spell_id); + std::string tmp; + FILE *f = NULL; + + std::list::iterator iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + tmp = filename; + std::map::iterator ext = _extensions.find((*iter)->GetIdentifier()); + tmp += "."; + tmp += ext->second; + f = fopen(tmp.c_str(), "r"); + if(f) { + fclose(f); + return (*iter); + } + + iter++; + } + + return NULL; +} + +QuestInterface *QuestParserCollection::GetQIByItemQuest(std::string item_script) { + //first look for /quests/items/item_script.ext (precedence) + std::string filename = "quests/items/"; + filename += item_script; + std::string tmp; + FILE *f = NULL; + + std::list::iterator iter = _load_precedence.begin(); + while(iter != _load_precedence.end()) { + tmp = filename; + std::map::iterator ext = _extensions.find((*iter)->GetIdentifier()); + tmp += "."; + tmp += ext->second; + f = fopen(tmp.c_str(), "r"); + if(f) { + fclose(f); + return (*iter); + } + + iter++; + } + + return NULL; +} \ No newline at end of file diff --git a/zone/QuestParserCollection.h b/zone/QuestParserCollection.h new file mode 100644 index 000000000..640a1557b --- /dev/null +++ b/zone/QuestParserCollection.h @@ -0,0 +1,62 @@ +#ifndef _EQE_QUESTPARSERCOLLECTION_H +#define _EQE_QUESTPARSERCOLLECTION_H + +#include "../common/types.h" +#include +#include +#include +#include + +#include "masterentity.h" +#include "../common/Item.h" +#include "QuestInterface.h" + +#define QuestFailedToLoad 0xFFFFFFFF +#define QuestUnloaded 0x00 + +class QuestParserCollection { +public: + QuestParserCollection(); + ~QuestParserCollection(); + + void RegisterQuestInterface(QuestInterface *qi, std::string ext); + + void AddVar(std::string name, std::string val); + void ReloadQuests(bool reset_timers = true); + + bool HasQuestSub(uint32 npcid, const char *subname); + bool PlayerHasQuestSub(const char *subname); + bool SpellHasQuestSub(uint32 spell_id, const char *subname); + bool ItemHasQuestSub(ItemInst *itm, const char *subname); + + void EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data); + void EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data); + void EventItem(QuestEventID evt, Client *client, ItemInst *item, uint32 objid, uint32 extra_data); + void EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data); + +private: + QuestInterface *GetQIByNPCQuest(uint32 npcid); + QuestInterface *GetQIByGlobalNPCQuest(); + QuestInterface *GetQIByPlayerQuest(); + QuestInterface *GetQIByGlobalPlayerQuest(); + QuestInterface *GetQIBySpellQuest(uint32 spell_id); + QuestInterface *GetQIByItemQuest(std::string item_script); + + std::map _interfaces; + std::map _extensions; + std::list _load_precedence; + + //0x00 = Unloaded + //0xFFFFFFFF = Failed to Load + std::map _npc_quest_status; + uint32 _global_npc_quest_status; + uint32 _player_quest_status; + uint32 _global_player_quest_status; + std::map _spell_quest_status; + std::map _item_quest_status; +}; + +extern QuestParserCollection *parse; + +#endif + diff --git a/zone/StringIDs.h b/zone/StringIDs.h new file mode 100644 index 000000000..b0959b39a --- /dev/null +++ b/zone/StringIDs.h @@ -0,0 +1,334 @@ +#ifndef STRING_IDS +#define STRING_IDS + +//These strings are loaded from eqstr_us.txt, but may vary between client versions. Maybe we could make this an enum that's dependent on the client version? +#define GENERIC_9_STRINGS 1 //%1 %2 %3 %4 %5 %6 %7 %8 %9 +#define TARGET_OUT_OF_RANGE 100 //Your target is out of range, get closer! +#define TARGET_NOT_FOUND 101 //Target player not found. +#define CANNOT_BIND 105 //You cannot form an affinity with this area. Try a city. +#define SPELL_DOES_NOT_WORK_HERE 106 //This spell does not work here. +#define SPELL_DOES_NOT_WORK_PLANE 107 //This spell does not work on this plane. +#define CANT_SEE_TARGET 108 //You cannot see your target. +#define MGB_STRING 113 //The next group buff you cast will hit all targets in range. +#define TARGET_TOO_FAR 124 //Your target is too far away, get closer! +#define PROC_TOOLOW 126 //Your will is not sufficient to command this weapon. +#define PROC_PETTOOLOW 127 //Your pet's will is not sufficient to command its weapon. +#define DOORS_LOCKED 130 //It's locked and you're not holding the key. +#define DOORS_CANT_PICK 131 //This lock cannot be picked. +#define DOORS_INSUFFICIENT_SKILL 132 //You are not sufficiently skilled to pick this lock. +#define DOORS_GM 133 //You opened the locked door with your magic GM key. +#define ITEMS_INSUFFICIENT_LEVEL 136 //You are not sufficient level to use this item. +#define GAIN_XP 138 //You gain experience!! +#define GAIN_GROUPXP 139 //You gain party experience!! +#define BOW_DOUBLE_DAMAGE 143 //Your bow shot did double dmg. +#define FORAGE_GRUBS 150 //You have scrounged up some fishing grubs. +#define FORAGE_WATER 151 //You have scrounged up some water. +#define FORAGE_FOOD 152 //You have scrounged up some food. +#define FORAGE_DRINK 153 //You have scrounged up some drink. +#define FORAGE_NOEAT 154 //You have scrounged up something that doesn't look edible. +#define FORAGE_FAILED 155 //You fail to locate any food nearby. +#define ALREADY_FISHING 156 //You are already fishing! +#define FISHING_NO_POLE 160 //You can't fish without a fishing pole, go buy one. +#define FISHING_EQUIP_POLE 161 //You need to put your fishing pole in your primary hand. +#define FISHING_NO_BAIT 162 //You can't fish without fishing bait, go buy some. +#define FISHING_CAST 163 //You cast your line. +#define NOT_SCARING 164 //You're not scaring anyone. +#define FISHING_STOP 165 //You stop fishing and go on your way. +#define FISHING_LAND 166 //Trying to catch land sharks perhaps? +#define FISHING_LAVA 167 //Trying to catch a fire elemental or something? +#define FISHING_FAILED 168 //You didn't catch anything. +#define FISHING_POLE_BROKE 169 //Your fishing pole broke! +#define FISHING_SUCCESS 170 //You caught, something... +#define FISHING_SPILL_BEER 171 //You spill your beer while bringing in your line. +#define FISHING_LOST_BAIT 172 //You lost your bait! +#define SPELL_FIZZLE 173 //Your spell fizzles! +#define MUST_EQUIP_ITEM 179 //You cannot use this item unless it is equipped. +#define MISS_NOTE 180 //You miss a note, bringing your song to a close! +#define CANNOT_USE_ITEM 181 //Your race, class, or deity cannot use this item. +#define ITEM_OUT_OF_CHARGES 182 //Item is out of charges. +#define TARGET_NO_MANA 191 //Your target has no mana to affect +#define TARGET_GROUP_MEMBER 196 //You must first target a group member. +#define INSUFFICIENT_MANA 199 //Insufficient Mana to cast this spell! +#define SAC_TOO_LOW 203 //This being is not a worthy sacrifice. +#define SAC_TOO_HIGH 204 //This being is too powerful to be a sacrifice. +#define CANNOT_SAC_SELF 205 //You cannot sacrifice yourself. +#define SILENCED_STRING 207 //You *CANNOT* cast spells, you have been silenced! +#define CANNOT_AFFECT_PC 210 //That spell can not affect this target PC. +#define SPELL_NEED_TAR 214 //You must first select a target for this spell! +#define ONLY_ON_CORPSES 221 //This spell only works on corpses. +#define CANT_DRAIN_SELF 224 //You can't drain yourself! +#define CORPSE_NOT_VALID 230 //This corpse is not valid. +#define CORPSE_TOO_OLD 231 //This player cannot be resurrected. The corpse is too old. +#define CAST_OUTDOORS 234 //You can only cast this spell in the outdoors. +#define SPELL_RECAST 236 //Spell recast time not yet met. +#define SPELL_RECOVERY 237 //Spell recovery time not yet met. +#define CANNOT_MEZ 239 //Your target cannot be mesmerized. +#define CANNOT_MEZ_WITH_SPELL 240 //Your target cannot be mesmerized (with this spell). +#define IMMUNE_STUN 241 //Your target is immune to the stun portion of this effect. +#define IMMUNE_ATKSPEED 242 //Your target is immune to changes in its attack speed. +#define IMMUNE_FEAR 243 //Your target is immune to fear spells. +#define IMMUNE_MOVEMENT 244 //Your target is immune to changes in its run speed. +#define ONLY_ONE_PET 246 //You cannot have more than one pet at a time. +#define CANNOT_CHARM_YET 248 //Your target is too high of a level for your charm spell. +#define CANNOT_AFFECT_NPC 251 //That spell can not affect this target NPC. +#define SUSPEND_MINION_HAS_AGGRO 256 //Your pet is the focus of something's attention. +#define NO_PET 255 //You do not have a pet. +#define CORPSE_CANT_SENSE 262 //You cannot sense any corpses for this PC in this zone. +#define SPELL_NO_HOLD 263 //Your spell did not take hold. +#define CANNOT_CHARM 267 //This NPC cannot be charmed. +#define SPELL_NO_EFFECT 268 //Your target looks unaffected. +#define NO_INSTRUMENT_SKILL 269 //Stick to singing until you learn to play this instrument. +#define REGAIN_AND_CONTINUE 270 //You regain your concentration and continue your casting. +#define SPELL_WOULDNT_HOLD 271 //Your spell would not have taken hold on your target. +#define MISSING_SPELL_COMP 272 //You are missing some required spell components. +#define INVIS_BEGIN_BREAK 275 //You feel yourself starting to appear. +#define DISCIPLINE_CONLOST 278 //You lose the concentration to remain in your fighting discipline. +#define REZ_REGAIN 289 //You regain some experience from resurrection. +#define DUP_LORE 290 //Duplicate lore items are not allowed. +#define TGB_ON 293 //Target other group buff is *ON*. +#define TGB_OFF 294 //Target other group buff is *OFF*. +#define LDON_SENSE_TRAP1 306 //You do not Sense any traps. +#define TRADESKILL_NOCOMBINE 334 //You cannot combine these items in this container type! +#define TRADESKILL_FAILED 336 //You lacked the skills to fashion the items together. +#define TRADESKILL_TRIVIAL 338 //You can no longer advance your skill from making this item. +#define TRADESKILL_SUCCEED 339 //You have fashioned the items together to create something new! +#define MEND_CRITICAL 349 //You magically mend your wounds and heal considerable damage. +#define MEND_SUCCESS 350 //You mend your wounds and heal some damage. +#define MEND_WORSEN 351 //You have worsened your wounds! +#define MEND_FAIL 352 //You have failed to mend your wounds. +#define LDON_SENSE_TRAP2 367 //You have not detected any traps. +#define LOOT_LORE_ERROR 371 //You cannot loot this Lore Item. You already have one. +#define PICK_LORE 379 //You cannot pick up a lore item you already possess. +#define CONSENT_DENIED 390 //You do not have consent to summon that corpse. +#define DISCIPLINE_RDY 393 //You are ready to use a new discipline now. +#define CONSENT_INVALID_NAME 397 //Not a valid consent name. +#define CONSENT_NPC 398 //You cannot consent NPC\'s. +#define CONSENT_YOURSELF 399 //You cannot consent yourself. +#define SONG_NEEDS_DRUM 405 //You need to play a percussion instrument for this song +#define SONG_NEEDS_WIND 406 //You need to play a wind instrument for this song +#define SONG_NEEDS_STRINGS 407 //You need to play a stringed instrument for this song +#define SONG_NEEDS_BRASS 408 //You need to play a brass instrument for this song +#define AA_GAIN_ABILITY 410 //You have gained the ability "%T1" at a cost of %2 ability %T3. +#define AA_IMPROVE 411 //You have improved %T1 %2 at a cost of %3 ability %T4. +#define AA_REUSE_MSG 413 //You can use the ability %B1(1) again in %2 hour(s) %3 minute(s) %4 seconds. +#define AA_REUSE_MSG2 414 //You can use the ability %B1(1) again in %2 minute(s) %3 seconds. +#define YOU_HEALED 419 //%1 has healed you for %2 points of damage. +#define BEGINS_TO_GLOW 422 //Your %1 begins to glow. +#define ALREADY_INVIS 423 //%1 tries to cast an invisibility spell on you, but you are already invisible. +#define YOU_ARE_PROTECTED 424 //%1 tries to cast a spell on you, but you are protected. +#define TARGET_RESISTED 425 //Your target resisted the %1 spell. +#define YOU_RESIST 426 //You resist the %1 spell! +#define SUMMONING_CORPSE 429 //Summoning your corpse. +#define SUMMONING_CORPSE_OTHER 430 //Summoning %1's corpse. +#define MISSING_SPELL_COMP_ITEM 433 //You are missing %1. +#define OTHER_HIT_NONMELEE 434 //%1 was hit by non-melee for %2 points of damage. +#define SPELL_WORN_OFF_OF 436 //Your %1 spell has worn off of %2. +#define SPELL_WORN_OFF 437 //Your %1 spell has worn off. +#define INTERRUPT_SPELL 439 //Your spell is interrupted. +#define LOSE_LEVEL 442 //You LOST a level! You are now level %1! +#define GAIN_ABILITY_POINT 446 //You have gained an ability point! You now have %1 ability point%2. +#define GAIN_LEVEL 447 //You have gained a level! Welcome to level %1! +#define LANG_SKILL_IMPROVED 449 //Your language skills have improved. +#define OTHER_LOOTED_MESSAGE 466 //--%1 has looted a %2-- +#define LOOTED_MESSAGE 467 //--You have looted a %1-- +#define FACTION_WORST 469 //Your faction standing with %1 could not possibly get any worse. +#define FACTION_WORSE 470 //Your faction standing with %1 got worse. +#define FACTION_BEST 471 //Your faction standing with %1 could not possibly get any better. +#define FACTION_BETTER 472 //Your faction standing with %1 got better. +#define PET_REPORT_HP 488 //I have %1 percent of my hit points left. +#define CORPSE_DECAY1 495 //This corpse will decay in %1 minute(s) %2 seconds. +#define DISC_LEVEL_ERROR 503 //You must be a level %1 ... to use this discipline. +#define DISCIPLINE_CANUSEIN 504 //You can use a new discipline in %1 minutes %2 seconds. +#define PVP_ON 552 //You are now player kill and follow the ways of Discord. +#define GENERIC_STRINGID_SAY 554 //%1 says '%T2' +#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 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) +#define CRITICAL_HIT 1023 //%1 scores a critical hit! (%2) +#define RESISTS_URGE 1025 //%1 resists their urge to flee. +#define BERSERK_START 1027 //%1 goes into a berserker frenzy! +#define DEATH_PACT 1028 //%1's death pact has been benevolently fulfilled! +#define DIVINE_INTERVENTION 1029 //%1 has been rescued by divine intervention! +#define BERSERK_END 1030 //%1 is no longer berserk. +#define GATES 1031 //%1 Gates. +#define GENERIC_SAY 1032 //%1 says '%2' +#define OTHER_REGAIN_CAST 1033 //%1 regains concentration and continues casting. +#define GENERIC_SHOUT 1034 //%1 shouts '%2' +#define GENERIC_EMOTE 1036 //%1 %2 +#define NPC_ENRAGE_START 1042 //%1 has become ENRAGED. +#define NPC_ENRAGE_END 1043 //%1 is no longer enraged. +#define NPC_RAMPAGE 1044 //%1 goes on a RAMPAGE! +#define NPC_FLURRY 1045 //%1 executes a FLURRY of attacks on %2! +#define DISCIPLINE_FEARLESS 1076 //%1 becomes fearless. +#define DUEL_FINISHED 1088 //dont know text +#define EATING_MESSAGE 1091 //Chomp, chomp, chomp... %1 takes a bite from a %2. +#define DRINKING_MESSAGE 1093 //Glug, glug, glug... %1 takes a drink from a %2. +#define SUCCESSFUL_TAUNT 1095 //I'll teach you to interfere with me %3. +#define PET_SIT_STRING 1130 //Changing position, Master. +#define PET_CALMING 1131 //Sorry, Master..calming down. +#define PET_FOLLOWING 1132 //Following you, Master. +#define PET_GUARDME_STRING 1133 //Guarding you, Master. +#define PET_GUARDINGLIFE 1134 //Guarding with my life..oh splendid one. +#define PET_GETLOST_STRING 1135 //As you wish, oh great one. +#define PET_LEADERIS 1136 //My leader is %3. +#define I_FOLLOW_NOONE 1137 //I follow no one. +#define MERCHANT_BUSY 1143 //I'm sorry, I am busy right now. +#define MERCHANT_GREETING 1144 //Welcome to my shop, %3. +#define MERCHANT_HANDY_ITEM1 1145 //Hello there, %3. How about a nice %4? +#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 AA_POINT 1197 //point +#define AA_POINTS 1215 //points +#define SPELL_FIZZLE_OTHER 1218 //%1's spell fizzles! +#define MISSED_NOTE_OTHER 1219 //A missed note brings %1's song to a close! +#define CORPSE_DECAY_NOW 1227 //This corpse is waiting to expire. +#define SURNAME_REJECTED 1374 //Your new surname was rejected. Please try a different name. +#define DUEL_DECLINE 1383 //%1 has declined your challenge to duel to the death. +#define DUEL_ACCEPTED 1384 //%1 has already accepted a duel with someone else. +#define DUEL_CONSIDERING 1385 //%1 is considering a duel with someone else. +#define PLAYER_REGAIN 1394 //You have control of yourself again. +#define REZZ_ALREADY_PENDING 1379 //You were unable to restore the corpse to life, but you may have success with a later attempt. +#define IN_USE 1406 //Someone else is using that. Try again later. +#define DUEL_FLED 1408 //%1 has defeated %2 in a duel to the death! %3 has fled like a cowardly dog! +#define MEMBER_OF_YOUR_GUILD 1429 +#define OFFICER_OF_YOUR_GUILD 1430 +#define LEADER_OF_YOUR_GUILD 1431 +#define RECEIVED_PLATINUM 1452 //You receive %1 Platinum from %2. +#define RECEIVED_GOLD 1453 //You receive %1 Gold from %2. +#define RECEIVED_SILVER 1454 //You receive %1 Silver from %2. +#define RECEIVED_COPPER 1455 //You receive %1 Copper from %2. +#define STRING_FEIGNFAILED 1456 //%1 has fallen to the ground. +#define DOORS_SUCCESSFUL_PICK 1457 //You successfully picked the lock. +#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 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. +#define SUSPEND_MINION_FIGHTING 3270 //Your pet must be at peace, first. +#define TRADESKILL_MISSING_ITEM 3455 //You are missing a %1. +#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 CORPSEDRAG_LIMIT 4061 //You are already dragging as much as you can! +#define CORPSEDRAG_ALREADY 4062 //You are already dragging %1. +#define CORPSEDRAG_SOMEONE_ELSE 4063 //Someone else is dragging %1. +#define CORPSEDRAG_BEGIN 4064 //You begin to drag %1. +#define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses. +#define CORPSEDRAG_STOP 4066 //You stop dragging the corpse. +#define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters. +#define PETITION_NO_DELETE 5053 //You do not have a petition in the queue. +#define PETITION_DELETED 5054 //Your petition was successfully deleted. +#define GAIN_RAIDEXP 5085 //You gained raid experience! +#define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure. +#define PET_ATTACKING 5501 //%1 tells you, 'Attacking %2 Master.' +#define FATAL_BOW_SHOT 5745 //%1 performs a FATAL BOW SHOT!! +#define MELEE_SILENCE 5806 //You *CANNOT* use this melee ability, you are suffering from amnesia! +#define DISCIPLINE_REUSE_MSG 5807 //You can use the ability %1 again in %2 hour(s) %3 minute(s) %4 seconds. +#define DISCIPLINE_REUSE_MSG2 5808 //You can use the ability %1 again in %2 minute(s) %3 seconds. +#define FAILED_TAUNT 5811 //You have failed to taunt your target. +#define AA_NO_TARGET 5825 //You must first select a target for this ability! +#define GUILD_BANK_CANNOT_DEPOSIT 6097 // Cannot deposit this item. Containers must be empty, and only one of each LORE and no NO TRADE or TEMPORARY items may be deposited. +#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 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 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! +#define LDON_HAVE_NOT_DISARMED 7555 //You have not disarmed %1. +#define LDON_ACCIDENT_SETOFF2 7556 //You accidentally set off the trap! +#define LDON_CERTAIN_TRAP 7557 //You are certain that %1 is trapped. +#define LDON_CERTAIN_NOT_TRAP 7558 //You are certain that %1 is not trapped. +#define LDON_CANT_DETERMINE_TRAP 7559 //You are unable to determine if %1 is trapped. +#define LDON_PICKLOCK_SUCCESS 7560 //You have successfully picked %1! +#define LDON_PICKLOCK_FAILURE 7561 //You have failed to pick %1. +#define LDON_STILL_LOCKED 7562 //You cannot open %1, it is locked. +#define LDON_BASH_CHEST 7563 //%1 try to %2 %3, but do no damage. +#define DOORS_NO_PICK 7564 //You must have a lock pick in your inventory to do this. +#define LDON_NO_LOCKPICK 7564 //You must have a lock pick in your inventory to do this. +#define LDON_WAS_NOT_LOCKED 7565 //%1 was not locked. +#define LDON_WAS_NOT_TRAPPED 7566 //%1 was not trapped +#define GAIN_GROUP_LEADERSHIP_POINT 8585 // +#define GAIN_RAID_LEADERSHIP_POINT 8589 // +#define MAX_GROUP_LEADERSHIP_POINTS 8584 // +#define MAX_RAID_LEADERSHIP_POINTS 8591 // +#define LEADERSHIP_EXP_ON 8653 // +#define LEADERSHIP_EXP_OFF 8654 // +#define CURRENT_SPELL_EFFECTS 8757 //%1's current spell effects: +#define GAIN_GROUP_LEADERSHIP_EXP 8788 // +#define GAIN_RAID_LEADERSHIP_EXP 8789 // +#define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining) +#define YOU_HEAL 9068 //You have healed %1 for %2 points of damage. +#define OTHER_HIT_DOT 9072 //%1 has taken %2 damage from your %3. +#define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage. +#define SHAKE_OFF_STUN 9077 +#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 AE_RAMPAGE 11015 //%1 goes on a WILD RAMPAGE! +#define FACE_ACCEPTED 12028 //Facial features accepted. +#define SPELL_LEVEL_TO_LOW 12048 //You will have to achieve level %1 before you can scribe the %2. +#define ATTACKFAILED 12158 //%1 try to %2 %3, but %4! +#define HIT_STRING 12183 //hit +#define CRUSH_STRING 12191 //crush +#define PIERCE_STRING 12193 //pierce +#define KICK_STRING 12195 //kick +#define STRIKE_STRING 12197 //strike +#define BACKSTAB_STRING 12199 //backstab +#define BASH_STRING 12201 //bash +#define GUILD_NOT_MEMBER 12242 //You are not a member of any guild. +#define MEMBER_OF_X_GUILD 12256 +#define OFFICER_OF_X_GUILD 12257 +#define LEADER_OF_X_GUILD 12258 +#define NOT_IN_A_GUILD 12259 +#define TARGET_PLAYER_FOR_GUILD_STATUS 12260 +#define GROUP_INVITEE_NOT_FOUND 12268 //You must target a player or use /invite to invite someone to your group. +#define GROUP_INVITEE_SELF 12270 //12270 You cannot invite yourself. +#define NOT_IN_CONTROL 12368 //You do not have control of yourself right now. +#define ALREADY_CASTING 12442 //You are already casting a spell! +#define SENSE_CORPSE_NOT_NAME 12446 //You don't sense any corpses of that name. +#define SENSE_CORPSE_NONE 12447 //You don't sense any corpses. +#define NOT_HOLDING_ITEM 12452 //You are not holding an item! +#define SENSE_UNDEAD 12471 //You sense undead in this direction. +#define SENSE_ANIMAL 12472 //You sense an animal in this direction. +#define SENSE_SUMMONED 12473 //You sense a summoned being in this direction. +#define SENSE_NOTHING 12474 //You don't sense anything. +#define LDON_SENSE_TRAP3 12476 //You don't sense any traps. +#define INTERRUPT_SPELL_OTHER 12478 //%1's casting is interrupted! +#define YOU_HIT_NONMELEE 12481 //You were hit by non-melee for %1 damage. +#define TRACK_LOST_TARGET 12681 //You have lost your tracking target. +#define TRACK_STRAIGHT_AHEAD 12676 +#define TRACK_AHEAD_AND_TO 12677 +#define TRACK_TO_THE 12678 +#define TRACK_BEHIND_AND_TO 12679 +#define TRACK_BEHIND_YOU 12680 +#define BEAM_SMILE 12501 //%1 beams a smile at %2 +#define SONG_ENDS_ABRUPTLY 12686 //Your song ends abruptly. +#define SONG_ENDS 12687 //Your song ends. +#define SONG_ENDS_OTHER 12688 //%1's song ends. +#define SONG_ENDS_ABRUPTLY_OTHER 12689 //%1's song ends abruptly. +#define DIVINE_AURA_NO_ATK 12695 //You can't attack while invulnerable! +#define TRY_ATTACKING_SOMEONE 12696 //Try attacking someone other than yourself, it's more productive. +#define BACKSTAB_WEAPON 12874 //You need a piercing weapon as your primary weapon in order to backstab +#define MORE_SKILLED_THAN_I 12931 //%1 tells you, 'You are more skilled than I! What could I possibly teach you?' +#define SURNAME_EXISTS 12939 //You already have a surname. Operation failed. +#define SURNAME_LEVEL 12940 //You can only submit a surname upon reaching the 20th level. Operation failed. +#define SURNAME_TOO_LONG 12942 //Surname must be less than 20 characters in length. +#define REPORT_ONCE 12945 //You may only submit a report once per time that you zone. Thank you. +#define NOW_INVISIBLE 12950 //%1 is now Invisible. +#define NOW_VISIBLE 12951 //%1 is now Visible. +#define GUILD_NOT_MEMBER2 12966 //You are not in a guild. +#define DISC_LEVEL_USE_ERROR 13004 //You are not sufficient level to use this discipline. +#define TOGGLE_ON 13172 //Asking server to turn ON your incoming tells. +#define TOGGLE_OFF 13173 //Asking server to turn OFF all incoming tells for you. +#define DUEL_INPROGRESS 13251 //You have already accepted a duel with someone else cowardly dog. +#define GENERIC_MISS 15041 //%1 missed %2 + +#endif diff --git a/zone/ZoneConfig.cpp b/zone/ZoneConfig.cpp new file mode 100644 index 000000000..13681c0cc --- /dev/null +++ b/zone/ZoneConfig.cpp @@ -0,0 +1,22 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "ZoneConfig.h" + +ZoneConfig *ZoneConfig::_zone_config = NULL; + diff --git a/zone/ZoneConfig.h b/zone/ZoneConfig.h new file mode 100644 index 000000000..91d50c841 --- /dev/null +++ b/zone/ZoneConfig.h @@ -0,0 +1,61 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 __ZoneConfig_H +#define __ZoneConfig_H + +#include "../common/EQEmuConfig.h" + +class ZoneConfig : public EQEmuConfig { + public: + uint16 ZonePort; + string ZoneAddress; + + private: + + static ZoneConfig *_zone_config; + + ZoneConfig() : EQEmuConfig() { + ZonePort=0; + } + + public: + + // Produce a const singleton + static const ZoneConfig *get() { + if (_zone_config == NULL) + LoadConfig(); + return(_zone_config); + } + + // Load the config + static bool LoadConfig() { + if (_zone_config != NULL) + delete _zone_config; + _zone_config=new ZoneConfig; + _config=_zone_config; + + return _config->ParseFile(EQEmuConfig::ConfigFile.c_str(),"server"); + } + + // Accessors for the static private object + static void SetZonePort(uint16 port) { if (_zone_config) _zone_config->ZonePort=port; } + + void Dump() const; +}; + +#endif diff --git a/zone/aggro.cpp b/zone/aggro.cpp new file mode 100644 index 000000000..422dd591f --- /dev/null +++ b/zone/aggro.cpp @@ -0,0 +1,1480 @@ +/* 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 "../common/debug.h" +#include +#include +#include "masterentity.h" +#include "faction.h" +#include "map.h" +#include "spdat.h" +#include "../common/skills.h" +#include "../common/MiscFunctions.h" +#include "../common/rulesys.h" +#include "StringIDs.h" +#include + +extern Zone* zone; +//#define LOSDEBUG 6 + +//look around a client for things which might aggro the client. +void EntityList::CheckClientAggro(Client *around) { + _ZP(EntityList_CheckClientAggro); + + LinkedListIterator iterator(mob_list); + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) { + _ZP(EntityList_CheckClientAggro_Loop); + Mob* mob = iterator.GetData(); + if(mob->IsClient()) //also ensures that mob != around + continue; + + if(mob->CheckWillAggro(around)) { + if(mob->IsEngaged()) + { + mob->AddToHateList(around); + } + else + { + mob->AddToHateList(around, mob->GetLevel()); + } + } + } +} + +void EntityList::DescribeAggro(Client *towho, NPC *from_who, float d, bool verbose) { + float d2 = d*d; + + towho->Message(0, "Describing aggro for %s", from_who->GetName()); + + bool engaged = from_who->IsEngaged(); + if(engaged) { + Mob *top = from_who->GetHateTop(); + towho->Message(0, ".. I am currently fighting with %s", top == NULL?"(NULL)":top->GetName()); + } + bool check_npcs = from_who->WillAggroNPCs(); + + if(verbose) { + char namebuf[256]; + + int my_primary = from_who->GetPrimaryFaction(); + Mob *own = from_who->GetOwner(); + if(own != NULL) + my_primary = own->GetPrimaryFaction(); + + if(my_primary == 0) { + strcpy(namebuf, "(No faction)"); + } else if(my_primary < 0) { + strcpy(namebuf, "(Special faction)"); + } else { + if(!database.GetFactionName(my_primary, namebuf, sizeof(namebuf))) + strcpy(namebuf, "(Unknown)"); + } + towho->Message(0, ".. I am on faction %s (%d)\n", namebuf, my_primary); + } + + LinkedListIterator iterator(mob_list); + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) { + Mob* mob = iterator.GetData(); + if(mob->IsClient()) //also ensures that mob != around + continue; + + if(mob->DistNoRoot(*from_who) > d2) + continue; + + if(engaged) { + uint32 amm = from_who->GetHateAmount(mob); + if(amm == 0) { + towho->Message(0, "... %s is not on my hate list.", mob->GetName()); + } else { + towho->Message(0, "... %s is on my hate list with value %lu", mob->GetName(), (unsigned long)amm); + } + } else if(!check_npcs && mob->IsNPC()) { + towho->Message(0, "... %s is an NPC and my npc_aggro is disabled.", mob->GetName()); + } else { + from_who->DescribeAggro(towho, mob, verbose); + } + } +} + +void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) { + //this logic is duplicated from below, try to keep it up to date. + float iAggroRange = GetAggroRange(); + + float t1, t2, t3; + t1 = mob->GetX() - GetX(); + t2 = mob->GetY() - GetY(); + t3 = mob->GetZ() - GetZ(); + //Cheap ABS() + if(t1 < 0) + t1 = 0 - t1; + if(t2 < 0) + t2 = 0 - t2; + if(t3 < 0) + t3 = 0 - t3; + if( ( t1 > iAggroRange) + || ( t2 > iAggroRange) + || ( t3 > iAggroRange) ) { + towho->Message(0, "...%s is out of range (fast). distances (%.3f,%.3f,%.3f), range %.3f", mob->GetName(), + t1, t2, t3, iAggroRange); + return; + } + + if(mob->IsInvisible(this)) { + towho->Message(0, "...%s is invisible to me. ", mob->GetName()); + return; + } + if((mob->IsClient() && + (!mob->CastToClient()->Connected() + || mob->CastToClient()->IsLD() + || mob->CastToClient()->IsBecomeNPC() + || mob->CastToClient()->GetGM() + ) + )) + { + towho->Message(0, "...%s is my owner. ", mob->GetName()); + return; + } + + + if(mob == GetOwner()) { + towho->Message(0, "...%s a GM or is not connected. ", mob->GetName()); + return; + } + + float dist2 = mob->DistNoRoot(*this); + float iAggroRange2 = iAggroRange*iAggroRange; + if( dist2 > iAggroRange2 ) { + towho->Message(0, "...%s is out of range. %.3f > %.3f ", mob->GetName(), + dist2, iAggroRange2); + return; + } + + if(GetINT() > 75 && mob->GetLevelCon(GetLevel()) == CON_GREEN ) { + towho->Message(0, "...%s is red to me (basically)", mob->GetName(), + dist2, iAggroRange2); + return; + } + + if(verbose) { + int my_primary = GetPrimaryFaction(); + int mob_primary = mob->GetPrimaryFaction(); + Mob *own = GetOwner(); + if(own != NULL) + my_primary = own->GetPrimaryFaction(); + own = mob->GetOwner(); + if(mob_primary > 0 && own != NULL) + mob_primary = own->GetPrimaryFaction(); + + if(mob_primary == 0) { + towho->Message(0, "...%s has no primary faction", mob->GetName()); + } else if(mob_primary < 0) { + towho->Message(0, "...%s is on special faction %d", mob->GetName(), mob_primary); + } else { + char namebuf[256]; + if(!database.GetFactionName(mob_primary, namebuf, sizeof(namebuf))) + strcpy(namebuf, "(Unknown)"); + list::iterator cur,end; + cur = faction_list.begin(); + end = faction_list.end(); + bool res = false; + for(; cur != end; cur++) { + struct NPCFaction* fac = *cur; + if ((int32)fac->factionID == mob_primary) { + if (fac->npc_value > 0) { + towho->Message(0, "...%s is on ALLY faction %s (%d) with %d", mob->GetName(), namebuf, mob_primary, fac->npc_value); + res = true; + break; + } else if (fac->npc_value < 0) { + towho->Message(0, "...%s is on ENEMY faction %s (%d) with %d", mob->GetName(), namebuf, mob_primary, fac->npc_value); + res = true; + break; + } else { + towho->Message(0, "...%s is on NEUTRAL faction %s (%d) with 0", mob->GetName(), namebuf, mob_primary); + res = true; + break; + } + } + } + if(!res) { + towho->Message(0, "...%s is on faction %s (%d), which I have no entry for.", mob->GetName(), namebuf, mob_primary); + } + } + } + + FACTION_VALUE fv = mob->GetReverseFactionCon(this); + + if(!( + fv == FACTION_SCOWLS + || + (mob->GetPrimaryFaction() != GetPrimaryFaction() && mob->GetPrimaryFaction() == -4 && GetOwner() == NULL) + || + fv == FACTION_THREATENLY + )) { + towho->Message(0, "...%s faction not low enough. value='%s'", mob->GetName(), FactionValueToString(fv)); + return; + } + if(fv == FACTION_THREATENLY) { + towho->Message(0, "...%s threatening to me, so they only have a %d chance per check of attacking.", mob->GetName()); + } + + if(!CheckLosFN(mob)) { + towho->Message(0, "...%s is out of sight.", mob->GetName()); + } + + towho->Message(0, "...%s meets all conditions, I should be attacking them.", mob->GetName()); +} + +/* + If you change this function, you should update the above function + to keep the #aggro command accurate. +*/ +bool Mob::CheckWillAggro(Mob *mob) { + if(!mob) + return false; + _ZP(Mob_CheckWillAggro); + + //sometimes if a client has some lag while zoning into a dangerous place while either invis or a GM + //they will aggro mobs even though it's supposed to be impossible, to lets make sure we've finished connecting + if(mob->IsClient() && !mob->CastToClient()->ClientFinishedLoading()) + return false; + + Mob *ownr = mob->GetOwner(); + if(ownr && ownr->IsClient() && !ownr->CastToClient()->ClientFinishedLoading()) + return false; + + float iAggroRange = GetAggroRange(); + + // Check If it's invisible and if we can see invis + // Check if it's a client, and that the client is connected and not linkdead, + // and that the client isn't Playing an NPC, with thier gm flag on + // Check if it's not a Interactive NPC + // Trumpcard: The 1st 3 checks are low cost calcs to filter out unnessecary distance checks. Leave them at the beginning, they are the most likely occurence. + // Image: I moved this up by itself above faction and distance checks because if one of these return true, theres no reason to go through the other information + + float t1, t2, t3; + t1 = mob->GetX() - GetX(); + t2 = mob->GetY() - GetY(); + t3 = mob->GetZ() - GetZ(); + //Cheap ABS() + if(t1 < 0) + t1 = 0 - t1; + if(t2 < 0) + t2 = 0 - t2; + if(t3 < 0) + t3 = 0 - t3; + if( ( t1 > iAggroRange) + || ( t2 > iAggroRange) + || ( t3 > iAggroRange) + ||(mob->IsInvisible(this)) + || (mob->IsClient() && + (!mob->CastToClient()->Connected() + || mob->CastToClient()->IsLD() + || mob->CastToClient()->IsBecomeNPC() + || mob->CastToClient()->GetGM() + ) + )) + { + return(false); + } + + //im not sure I understand this.. + //if I have an owner and it is not this mob, then I cannot + //aggro this mob...??? + //changed to be 'if I have an owner and this is it' + if(mob == GetOwner()) { + return(false); + } + + float dist2 = mob->DistNoRoot(*this); + float iAggroRange2 = iAggroRange*iAggroRange; + + if( dist2 > iAggroRange2 ) { + // Skip it, out of range + return(false); + } + + //Image: Get their current target and faction value now that its required + //this function call should seem backwards + FACTION_VALUE fv = mob->GetReverseFactionCon(this); + + // Make sure they're still in the zone + // Are they in range? + // Are they kos? + // Are we stupid or are they green + // and they don't have their gm flag on + int heroicCHA_mod = mob->itembonuses.HeroicCHA/25; // 800 Heroic CHA cap + if(heroicCHA_mod > THREATENLY_ARRGO_CHANCE) + heroicCHA_mod = THREATENLY_ARRGO_CHANCE; + if + ( + //old InZone check taken care of above by !mob->CastToClient()->Connected() + ( + ( GetINT() <= 75 ) + ||( mob->IsClient() && mob->CastToClient()->IsSitting() ) + ||( mob->GetLevelCon(GetLevel()) != CON_GREEN ) + + ) + && + ( + ( + fv == FACTION_SCOWLS + || + (mob->GetPrimaryFaction() != GetPrimaryFaction() && mob->GetPrimaryFaction() == -4 && GetOwner() == NULL) + || + ( + fv == FACTION_THREATENLY + && MakeRandomInt(0,99) < THREATENLY_ARRGO_CHANCE - heroicCHA_mod + ) + ) + ) + ) + { + //FatherNiwtit: make sure we can see them. last since it is very expensive + if(CheckLosFN(mob)) { + + // Aggro + #if EQDEBUG>=6 + LogFile->write(EQEMuLog::Debug, "Check aggro for %s target %s.", GetName(), mob->GetName()); + #endif + return(true); + } + } +#if EQDEBUG >= 6 + printf("Is In zone?:%d\n", mob->InZone()); + printf("Dist^2: %f\n", dist2); + printf("Range^2: %f\n", iAggroRange2); + printf("Faction: %d\n", fv); + printf("Int: %d\n", GetINT()); + printf("Con: %d\n", GetLevelCon(mob->GetLevel())); +#endif + return(false); +} + +Mob* EntityList::AICheckCloseAggro(Mob* sender, float iAggroRange, float iAssistRange) { + if (!sender || !sender->IsNPC()) + return(NULL); + _ZP(EntityList_AICheckCloseAggro); + +#ifdef REVERSE_AGGRO + //with reverse aggro, npc->client is checked elsewhere, no need to check again + LinkedListIterator iterator(npc_list); +#else + LinkedListIterator iterator(mob_list); +#endif + iterator.Reset(); + //float distZ; + while(iterator.MoreElements()) { + Mob* mob = iterator.GetData(); + + if(sender->CheckWillAggro(mob)) { + return(mob); + } + + iterator.Advance(); + } + //LogFile->write(EQEMuLog::Debug, "Check aggro for %s no target.", sender->GetName()); + return(NULL); +} + +int EntityList::GetHatedCount(Mob *attacker, Mob *exclude) { + + // Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker + + if(!attacker) return 0; + + int Count = 0; + + LinkedListIterator iterator(npc_list); + + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) { + + NPC* mob = iterator.GetData(); + + if(!mob || (mob == exclude)) continue; + + if(!mob->IsEngaged()) continue; + + if(mob->IsFeared() || mob->IsMezzed()) continue; + + if(attacker->GetLevelCon(mob->GetLevel()) == CON_GREEN) continue; + + if(!mob->CheckAggro(attacker)) continue; + + float AggroRange = mob->GetAggroRange(); + + // Square it because we will be using DistNoRoot + + AggroRange = AggroRange * AggroRange; + + if(mob->DistNoRoot(*attacker) > AggroRange) continue; + + Count++; + + } + + return Count; + +} + +void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { + _ZP(EntityList_AIYellForHelp); + if(!sender || !attacker) + return; + if (sender->GetPrimaryFaction() == 0 ) + return; // well, if we dont have a faction set, we're gonna be indiff to everybody + + LinkedListIterator iterator(npc_list); + + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) { + NPC* mob = iterator.GetData(); + if(!mob){ + continue; + } + float r = mob->GetAssistRange(); + r = r * r; + + if ( + mob != sender + && mob != attacker +// && !mob->IsCorpse() +// && mob->IsAIControlled() + && mob->GetPrimaryFaction() != 0 + && mob->DistNoRoot(*sender) <= r + && !mob->IsEngaged() + && ((!mob->IsPet()) || (mob->IsPet() && mob->GetOwner() && !mob->GetOwner()->IsClient())) + // If we're a pet we don't react to any calls for help if our owner is a client + ) + { + //if they are in range, make sure we are not green... + //then jump in if they are our friend + if(attacker->GetLevelCon(mob->GetLevel()) != CON_GREEN) + { + bool useprimfaction = false; + if(mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction()) + { + const NPCFactionList *cf = database.GetNPCFactionEntry(mob->GetNPCFactionID()); + if(cf){ + if(cf->assistprimaryfaction != 0) + useprimfaction = true; + } + } + + if(useprimfaction || sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE ) + { + //attacking someone on same faction, or a friend + //Father Nitwit: make sure we can see them. + if(mob->CheckLosFN(sender)) { +#if (EQDEBUG>=5) + LogFile->write(EQEMuLog::Debug, "AIYellForHelp(\"%s\",\"%s\") %s attacking %s Dist %f Z %f", + sender->GetName(), attacker->GetName(), mob->GetName(), attacker->GetName(), mob->DistNoRoot(*sender), fabs(sender->GetZ()+mob->GetZ())); +#endif + mob->AddToHateList(attacker, 1, 0, false); + } + } + } + } + } +} + +/* +solar: returns false if attack should not be allowed +I try to list every type of conflict that's possible here, so it's easy +to see how the decision is made. Yea, it could be condensed and made +faster, but I'm doing it this way to make it readable and easy to modify +*/ + +bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack) +{ + + Mob *mob1, *mob2, *tempmob; + Client *c1, *c2, *becomenpc; +// NPC *npc1, *npc2; + int reverse; + + if(!zone->CanDoCombat()) + return false; + + // some special cases + if(!target) + return false; + + if(this == target) // you can attack yourself + return true; + + if(target->SpecAttacks[NO_HARM_FROM_CLIENT]){ + return false; + } + + // can't damage own pet (applies to everthing) + Mob *target_owner = target->GetOwner(); + Mob *our_owner = GetOwner(); + if(target_owner && target_owner == this) + return false; + else if(our_owner && our_owner == target) + return false; + + //cannot hurt untargetable mobs + bodyType bt = target->GetBodyType(); + + if(bt == BT_NoTarget || bt == BT_NoTarget2) { + if (RuleB(Pets, UnTargetableSwarmPet)) { + if (target->IsNPC()) { + if (!target->CastToNPC()->GetSwarmOwner()) { + return(false); + } + } else { + return(false); + } + } else { + return(false); + } + } + + if(!isSpellAttack) + { + if(GetClass() == LDON_TREASURE) + { + return false; + } + } + + // solar: the format here is a matrix of mob type vs mob type. + // redundant ones are omitted and the reverse is tried if it falls through. + + // first figure out if we're pets. we always look at the master's flags. + // no need to compare pets to anything + mob1 = our_owner ? our_owner : this; + mob2 = target_owner ? target_owner : target; + + reverse = 0; + do + { + if(_CLIENT(mob1)) + { + if(_CLIENT(mob2)) // client vs client + { + c1 = mob1->CastToClient(); + c2 = mob2->CastToClient(); + + if // if both are pvp they can fight + ( + c1->GetPVP() && + c2->GetPVP() + ) + return true; + else if // if they're dueling they can go at it + ( + c1->IsDueling() && + c2->IsDueling() && + c1->GetDuelTarget() == c2->GetID() && + c2->GetDuelTarget() == c1->GetID() + ) + return true; + else + return false; + } + else if(_NPC(mob2)) // client vs npc + { + return true; + } + else if(_BECOMENPC(mob2)) // client vs becomenpc + { + c1 = mob1->CastToClient(); + becomenpc = mob2->CastToClient(); + + if(c1->GetLevel() > becomenpc->GetBecomeNPCLevel()) + return false; + else + return true; + } + else if(_CLIENTCORPSE(mob2)) // client vs client corpse + { + return false; + } + else if(_NPCCORPSE(mob2)) // client vs npc corpse + { + return false; + } + } + else if(_NPC(mob1)) + { + if(_NPC(mob2)) // npc vs npc + { +/* +this says that an NPC can NEVER attack a faction ally... +this is stupid... somebody else should check this rule if they want to +enforce it, this just says 'can they possibly fight based on their +type', in which case, the answer is yes. +*/ +/* npc1 = mob1->CastToNPC(); + npc2 = mob2->CastToNPC(); + if + ( + npc1->GetPrimaryFaction() != 0 && + npc2->GetPrimaryFaction() != 0 && + ( + npc1->GetPrimaryFaction() == npc2->GetPrimaryFaction() || + npc1->IsFactionListAlly(npc2->GetPrimaryFaction()) + ) + ) + return false; + else +*/ + return true; + } + else if(_BECOMENPC(mob2)) // npc vs becomenpc + { + return true; + } + else if(_CLIENTCORPSE(mob2)) // npc vs client corpse + { + return false; + } + else if(_NPCCORPSE(mob2)) // npc vs npc corpse + { + return false; + } + } + else if(_BECOMENPC(mob1)) + { + if(_BECOMENPC(mob2)) // becomenpc vs becomenpc + { + return true; + } + else if(_CLIENTCORPSE(mob2)) // becomenpc vs client corpse + { + return false; + } + else if(_NPCCORPSE(mob2)) // becomenpc vs npc corpse + { + return false; + } + } + else if(_CLIENTCORPSE(mob1)) + { + if(_CLIENTCORPSE(mob2)) // client corpse vs client corpse + { + return false; + } + else if(_NPCCORPSE(mob2)) // client corpse vs npc corpse + { + return false; + } + } + else if(_NPCCORPSE(mob1)) + { + if(_NPCCORPSE(mob2)) // npc corpse vs npc corpse + { + return false; + } + } + +#ifdef BOTS + bool HasRuleDefined = false; + bool IsBotAttackAllowed = false; + IsBotAttackAllowed = Bot::IsBotAttackAllowed(mob1, mob2, HasRuleDefined); + if(HasRuleDefined) + return IsBotAttackAllowed; +#endif //BOTS + + // we fell through, now we swap the 2 mobs and run through again once more + tempmob = mob1; + mob1 = mob2; + mob2 = tempmob; + } + while( reverse++ == 0 ); + + LogFile->write(EQEMuLog::Debug, "Mob::IsAttackAllowed: don't have a rule for this - %s vs %s\n", this->GetName(), target->GetName()); + return false; +} + + +// solar: this is to check if non detrimental things are allowed to be done +// to the target. clients cannot affect npcs and vice versa, and clients +// cannot affect other clients that are not of the same pvp flag as them. +// also goes for their pets +bool Mob::IsBeneficialAllowed(Mob *target) +{ + Mob *mob1, *mob2, *tempmob; + Client *c1, *c2; + int reverse; + + if(!target) + return false; + + if (target->GetAllowBeneficial()) + return true; + + // solar: see IsAttackAllowed for notes + + // first figure out if we're pets. we always look at the master's flags. + // no need to compare pets to anything + mob1 = this->GetOwnerID() ? this->GetOwner() : this; + mob2 = target->GetOwnerID() ? target->GetOwner() : target; + + // if it's self target or our own pet it's ok + if(mob1 == mob2) + return true; + + reverse = 0; + do + { + if(_CLIENT(mob1)) + { + if(_CLIENT(mob2)) // client to client + { + c1 = mob1->CastToClient(); + c2 = mob2->CastToClient(); + + if(c1->GetPVP() == c2->GetPVP()) + return true; + else if // if they're dueling they can heal each other too + ( + c1->IsDueling() && + c2->IsDueling() && + c1->GetDuelTarget() == c2->GetID() && + c2->GetDuelTarget() == c1->GetID() + ) + return true; + else + return false; + } + else if(_NPC(mob2)) // client to npc + { + /* fall through and swap positions */ + } + else if(_BECOMENPC(mob2)) // client to becomenpc + { + return false; + } + else if(_CLIENTCORPSE(mob2)) // client to client corpse + { + return true; + } + else if(_NPCCORPSE(mob2)) // client to npc corpse + { + return false; + } +#ifdef BOTS + else if(mob2->IsBot()) + return true; +#endif + } + else if(_NPC(mob1)) + { + if(_CLIENT(mob2)) + { + return false; + } + if(_NPC(mob2)) // npc to npc + { + return true; + } + else if(_BECOMENPC(mob2)) // npc to becomenpc + { + return true; + } + else if(_CLIENTCORPSE(mob2)) // npc to client corpse + { + return false; + } + else if(_NPCCORPSE(mob2)) // npc to npc corpse + { + return false; + } + } + else if(_BECOMENPC(mob1)) + { + if(_BECOMENPC(mob2)) // becomenpc to becomenpc + { + return true; + } + else if(_CLIENTCORPSE(mob2)) // becomenpc to client corpse + { + return false; + } + else if(_NPCCORPSE(mob2)) // becomenpc to npc corpse + { + return false; + } + } + else if(_CLIENTCORPSE(mob1)) + { + if(_CLIENTCORPSE(mob2)) // client corpse to client corpse + { + return false; + } + else if(_NPCCORPSE(mob2)) // client corpse to npc corpse + { + return false; + } + } + else if(_NPCCORPSE(mob1)) + { + if(_NPCCORPSE(mob2)) // npc corpse to npc corpse + { + return false; + } + } + + // we fell through, now we swap the 2 mobs and run through again once more + tempmob = mob1; + mob1 = mob2; + mob2 = tempmob; + } + while( reverse++ == 0 ); + + LogFile->write(EQEMuLog::Debug, "Mob::IsBeneficialAllowed: don't have a rule for this - %s to %s\n", this->GetName(), target->GetName()); + return false; +} + +bool Mob::CombatRange(Mob* other) +{ + if(!other) + return(false); + + float size_mod = GetSize(); + float other_size_mod = other->GetSize(); + + if(GetRace() == 49 || GetRace() == 158 || GetRace() == 196) //For races with a fixed size + size_mod = 60.0f; + else if (size_mod < 6.0) + size_mod = 8.0f; + + if(other->GetRace() == 49 || other->GetRace() == 158 || other->GetRace() == 196) //For races with a fixed size + other_size_mod = 60.0f; + else if (other_size_mod < 6.0) + other_size_mod = 8.0f; + + if (other_size_mod > size_mod) + { + size_mod = other_size_mod; + } + + // this could still use some work, but for now it's an improvement.... + + if (size_mod > 29) + size_mod *= size_mod; + else if (size_mod > 19) + size_mod *= size_mod * 2; + else + size_mod *= size_mod * 4; + + + // prevention of ridiculously sized hit boxes + if (size_mod > 10000) + size_mod = size_mod / 7; + + if (DistNoRoot(*other) <= size_mod) + { + return true; + } + return false; +} + +//Old LOS function, prolly not used anymore +//Not removed because I havent looked it over to see if anything +//useful is in here before we delete it. +bool Mob::CheckLos(Mob* other) { + if (zone->zonemap == 0) + { + return true; + } + float tmp_x = GetX(); + float tmp_y = GetY(); + float tmp_z = GetZ(); + float trg_x = other->GetX(); + float trg_y = other->GetY(); + float trg_z = other->GetZ(); + float perwalk_x = 0.5; + float perwalk_y = 0.5; + float perwalk_z = 0.5; + float dist_x = tmp_x - trg_x; + if (dist_x < 0) + dist_x *= -1; + float dist_y = tmp_y - trg_y; + if (dist_y < 0) + dist_y *= -1; + float dist_z = tmp_z - trg_z; + if (dist_z < 0) + dist_z *= -1; + if (dist_x < dist_y && dist_z < dist_y) + { + perwalk_x /= (dist_y/dist_x); + perwalk_z /= (dist_y/dist_z); + } + else if (dist_y < dist_x && dist_z < dist_x) + { + perwalk_y /= (dist_x/dist_y); + perwalk_z /= (dist_x/dist_z); + } + else if (dist_x < dist_z && dist_y < dist_z) + { + perwalk_x /= (dist_z/dist_x); + perwalk_y /= (dist_z/dist_y); + } + float steps = (dist_x/perwalk_x + dist_y/perwalk_y + dist_z/perwalk_z)*10; //Just a safety check to prevent endless loops. + while (steps > 0) { + steps--; + if (tmp_x < trg_x) + { + if (tmp_x + perwalk_x < trg_x) + tmp_x += perwalk_x; + else + tmp_x = trg_x; + } + if (tmp_y < trg_y) + { + if (tmp_y + perwalk_y < trg_y) + tmp_y += perwalk_y; + else + tmp_y = trg_y; + } + if (tmp_z < trg_z) + { + if (tmp_z + perwalk_z < trg_z) + tmp_z += perwalk_z; + else + tmp_z = trg_z; + } + if (tmp_x > trg_x) + { + if (tmp_x - perwalk_x > trg_x) + tmp_x -= perwalk_x; + else + tmp_x = trg_x; + } + if (tmp_y > trg_y) + { + if (tmp_y - perwalk_y > trg_y) + tmp_y -= perwalk_y; + else + tmp_y = trg_y; + } + if (tmp_z > trg_z) + { + if (tmp_z - perwalk_z > trg_z) + tmp_z -= perwalk_z; + else + tmp_z = trg_z; + } + if (tmp_y == trg_y && tmp_x == trg_x && tmp_z == trg_z) + { + return true; + } + +//I believe this is contributing to breaking mob spawns when a map is loaded +// NodeRef pnode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), tmp_x, tmp_y ); + NodeRef pnode = NODE_NONE; + if (pnode != NODE_NONE) + { + const int *iface = zone->zonemap->SeekFace( pnode, tmp_x, tmp_y ); + if (*iface == -1) { + return false; + } + float temp_z = 0; + float best_z = 999999; + while(*iface != -1) + { + temp_z = zone->zonemap->GetFaceHeight( *iface, x_pos, y_pos ); +//UMM.. OMG... sqrtf(pow(x, 2)) == x.... retards + float best_dist = sqrtf((float)(pow(best_z-tmp_z, 2))); + float tmp_dist = sqrtf((float)(pow(tmp_z-tmp_z, 2))); + if (tmp_dist < best_dist) + { + best_z = temp_z; + } + iface++; + } +/* solar: our aggro code isn't using this right now, just spells, so i'm + taking out the +-10 check for now to make it work right on hills + if (best_z - 10 > trg_z || best_z + 10 < trg_z) + { + return false; + } +*/ + } + } + return true; +} + + +//Father Nitwit's LOS code +bool Mob::CheckLosFN(Mob* other) { + bool Result = false; + + if(other) + Result = CheckLosFN(other->GetX(), other->GetY(), other->GetZ(), other->GetSize()); + + return Result; +} + +bool Mob::CheckLosFN(float posX, float posY, float posZ, float mobSize) { + if(zone->zonemap == NULL) { + //not sure what the best return is on error + //should make this a database variable, but im lazy today +#ifdef LOS_DEFAULT_CAN_SEE + return(true); +#else + return(false); +#endif + } + _ZP(Mob_CheckLosFN); + + VERTEX myloc; + VERTEX oloc; + +#define LOS_DEFAULT_HEIGHT 6.0f + + myloc.x = GetX(); + myloc.y = GetY(); + myloc.z = GetZ() + (GetSize()==0.0?LOS_DEFAULT_HEIGHT:GetSize())/2 * HEAD_POSITION; + + oloc.x = posX; + oloc.y = posY; + oloc.z = posZ + (mobSize==0.0?LOS_DEFAULT_HEIGHT:mobSize)/2 * SEE_POSITION; + +#if LOSDEBUG>=5 + LogFile->write(EQEMuLog::Debug, "LOS from (%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f) sizes: (%.2f, %.2f)", myloc.x, myloc.y, myloc.z, oloc.x, oloc.y, oloc.z, GetSize(), mobSize); +#endif + + FACE *onhit; + NodeRef mynode; + NodeRef onode; + + VERTEX hit; + //see if anything in our node is in the way + mynode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), myloc.x, myloc.y); + if(mynode != NODE_NONE) { + if(zone->zonemap->LineIntersectsNode(mynode, myloc, oloc, &hit, &onhit)) { +#if LOSDEBUG>=5 + LogFile->write(EQEMuLog::Debug, "Check LOS for %s target position, cannot see.", GetName()); + LogFile->write(EQEMuLog::Debug, "\tPoly: (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n", + onhit->a.x, onhit->a.y, onhit->a.z, + onhit->b.x, onhit->b.y, onhit->b.z, + onhit->c.x, onhit->c.y, onhit->c.z); +#endif + return(false); + } + } +#if LOSDEBUG>=5 + else { + LogFile->write(EQEMuLog::Debug, "WTF, I have no node, what am I standing on??? (%.2f, %.2f).", myloc.x, myloc.y); + } +#endif + + //see if they are in a different node. + //if so, see if anything in their node is blocking me. + if(! zone->zonemap->LocWithinNode(mynode, oloc.x, oloc.y)) { + onode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), oloc.x, oloc.y); + if(onode != NODE_NONE && onode != mynode) { + if(zone->zonemap->LineIntersectsNode(onode, myloc, oloc, &hit, &onhit)) { +#if LOSDEBUG>=5 + LogFile->write(EQEMuLog::Debug, "Check LOS for %s target position, cannot see (2).", GetName()); + LogFile->write(EQEMuLog::Debug, "\tPoly: (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n", + onhit->a.x, onhit->a.y, onhit->a.z, + onhit->b.x, onhit->b.y, onhit->b.z, + onhit->c.x, onhit->c.y, onhit->c.z); +#endif + return(false); + } + } +#if LOSDEBUG>=5 + else if(onode == NODE_NONE) { + LogFile->write(EQEMuLog::Debug, "WTF, They have no node, what are they standing on??? (%.2f, %.2f).", myloc.x, myloc.y); + } +#endif + } + + /* + if(zone->zonemap->LineIntersectsZone(myloc, oloc, CHECK_LOS_STEP, &onhit)) { +#if LOSDEBUG>=5 + LogFile->write(EQEMuLog::Debug, "Check LOS for %s target %s, cannot see.", GetName(), other->GetName() ); + LogFile->write(EQEMuLog::Debug, "\tPoly: (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n", + onhit->a.x, onhit->a.y, onhit->a.z, + onhit->b.x, onhit->b.y, onhit->b.z, + onhit->c.x, onhit->c.y, onhit->c.z); +#endif + return(false); + }*/ + +#if LOSDEBUG>=5 + LogFile->write(EQEMuLog::Debug, "Check LOS for %s target position, CAN SEE.", GetName()); +#endif + + return(true); +} + +//offensive spell aggro +int32 Mob::CheckAggroAmount(uint16 spellid, bool isproc) { + uint16 spell_id = spellid; + int32 AggroAmount = 0; + int32 nonModifiedAggro = 0; + uint16 slevel = GetLevel(); + + for (int o = 0; o < EFFECT_COUNT; o++) { + switch(spells[spell_id].effectid[o]) { + case SE_CurrentHPOnce: + case SE_CurrentHP:{ + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], this->GetLevel(), spell_id); + if(val < 0) + AggroAmount -= val; + break; + } + case SE_MovementSpeed: { + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], this->GetLevel(), spell_id); + if (val < 0) + { + AggroAmount += (2 + ((slevel * slevel) / 8)); + break; + } + break; + } + case SE_AttackSpeed: + case SE_AttackSpeed2: + case SE_AttackSpeed3:{ + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], this->GetLevel(), spell_id); + if (val < 100) + { + AggroAmount += (5 + ((slevel * slevel) / 5)); + } + break; + } + case SE_Stun: { + int val = (5 + ((slevel * slevel) / 6)); + if (isproc && RuleI(Aggro,MaxStunProcAggro) > -1 && (val > RuleI(Aggro,MaxStunProcAggro))) + val = RuleI(Aggro,MaxStunProcAggro); + AggroAmount += val; + break; + } + case SE_Blind: { + AggroAmount += (5 + ((slevel * slevel) / 6)); + break; + } + case SE_Mez: { + AggroAmount += (5 + ((slevel * slevel) / 5)); + break; + } + case SE_Charm: { + AggroAmount += (5 + ((slevel * slevel) / 5)); + break; + } + case SE_Root: { + AggroAmount += (2 + ((slevel * slevel) / 8)); + break; + } + case SE_Fear: { + AggroAmount += (5 + ((slevel * slevel) / 6)); + break; + } + case SE_ATK: + case SE_ACv2: + case SE_ArmorClass: { + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], this->GetLevel(), spell_id); + if (val < 0) + { + AggroAmount -= val*2; + } + break; + } + case SE_ResistMagic: + case SE_ResistFire: + case SE_ResistCold: + case SE_ResistPoison: + case SE_ResistDisease:{ + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], this->GetLevel(), spell_id); + if (val < 0) + { + AggroAmount -= val*3; + } + break; + } + case SE_ResistAll:{ + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], this->GetLevel(), spell_id); + if (val < 0) + { + AggroAmount -= val*6; + } + break; + } + case SE_STR: + case SE_STA: + case SE_DEX: + case SE_AGI: + case SE_INT: + case SE_WIS: + case SE_CHA:{ + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], this->GetLevel(), spell_id); + if (val < 0) + { + AggroAmount -= val*2; + } + break; + } + case SE_AllStats:{ + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], this->GetLevel(), spell_id); + if (val < 0) + { + AggroAmount -= val*6; + } + break; + } + case SE_BardAEDot:{ + AggroAmount += slevel*2; + break; + } + case SE_SpinTarget:{ + AggroAmount += (5 + ((slevel * slevel) / 5)); + break; + } + case SE_Amnesia: + case SE_Silence:{ + AggroAmount += slevel*2; + break; + } + case SE_Destroy:{ + AggroAmount += slevel*2; + break; + } + case SE_Harmony: + case SE_CastingLevel: + case SE_MeleeMitigation: + case SE_CriticalHitChance: + case SE_AvoidMeleeChance: + case SE_RiposteChance: + case SE_DodgeChance: + case SE_ParryChance: + case SE_DualWieldChance: + case SE_DoubleAttackChance: + case SE_MeleeSkillCheck: + case SE_HitChance: + case SE_DamageModifier: + case SE_MinDamageModifier: + case SE_IncreaseBlockChance: + case SE_Accuracy: + case SE_DamageShield: + case SE_SpellDamageShield: + case SE_ReverseDS:{ + AggroAmount += slevel*2; + break; + } + case SE_CurrentMana: + case SE_ManaRegen_v2: + case SE_ManaPool: + case SE_CurrentEndurance:{ + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], this->GetLevel(), spell_id); + if (val < 0) + { + AggroAmount -= val*2; + } + break; + } + case SE_CancelMagic: + case SE_DispelDetrimental:{ + AggroAmount += slevel; + break; + } + case SE_ReduceHate: + case SE_Calm:{ + int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], this->GetLevel(), spell_id); + nonModifiedAggro = val; + break; + } + } + } + + if(IsAEDurationSpell(spell_id)) + { + AggroAmount /= 2; + } + + if(spells[spell_id].HateAdded > 0) + { + AggroAmount = spells[spell_id].HateAdded; + } + + if (IsBardSong(spell_id)) + AggroAmount = AggroAmount * RuleI(Aggro, SongAggroMod) / 100; + if (GetOwner() && IsPet()) + AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100; + + if(AggroAmount > 0) + { + + int HateMod = RuleI(Aggro, SpellAggroMod); + + if(IsClient()) + { + HateMod += CastToClient()->GetFocusEffect(focusSpellHateMod, spell_id); + } + + //Live AA - Spell casting subtlety + HateMod += aabonuses.hatemod + spellbonuses.hatemod + itembonuses.hatemod; + + AggroAmount = (AggroAmount * HateMod) / 100; + + //made up number probably scales a bit differently on live but it seems like it will be close enough + //every time you cast on live you get a certain amount of "this is a spell" aggro + //confirmed by EQ devs to be 100 exactly at level 85. From their wording it doesn't seem like it's affected + //by hate modifiers either. + //AggroAmount += (slevel*slevel/72); + // Saved so I can reimplement it; + // this should only be on the spell to aggro the npc not every spell + + } + + + + AggroAmount += spells[spell_id].bonushate + nonModifiedAggro; + return AggroAmount; +} + +//healing and buffing aggro +int32 Mob::CheckHealAggroAmount(uint16 spellid, uint32 heal_possible) { + uint16 spell_id = spellid; + int32 AggroAmount = 0; + + for (int o = 0; o < EFFECT_COUNT; o++) { + switch(spells[spell_id].effectid[o]) { + case SE_CurrentHP: { + AggroAmount += spells[spell_id].mana; + break; + } + case SE_Rune: { + AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[0], spells[spell_id].base[0], spells[spell_id].max[o], this->GetLevel(), spellid) * 2; + break; + } + case SE_HealOverTime:{ + AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], this->GetLevel(), spell_id); + break; + } + default:{ + break; + } + } + } + if (IsBardSong(spell_id)) + AggroAmount = AggroAmount * RuleI(Aggro, SongAggroMod) / 100; + if (GetOwner() && IsPet()) + AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100; + + if(AggroAmount > 0) + { + int HateMod = RuleI(Aggro, SpellAggroMod); + + if(IsClient()) + { + HateMod += CastToClient()->GetFocusEffect(focusSpellHateMod, spell_id); + } + + //Live AA - Spell casting subtlety + HateMod += aabonuses.hatemod + spellbonuses.hatemod + itembonuses.hatemod; + + AggroAmount = (AggroAmount * HateMod) / 100; + + //made up number probably scales a bit differently on live but it seems like it will be close enough + //every time you cast on live you get a certain amount of "this is a spell" aggro + //confirmed by EQ devs to be 100 exactly at level 85. From their wording it doesn't seem like it's affected + //by hate modifiers either. + //AggroAmount += (slevel*slevel/72); // Moved Below + + + } + + + if(AggroAmount < 0) + return 0; + else + return AggroAmount; +} + +void Mob::AddFeignMemory(Client* attacker) { + if(feign_memory_list.empty() && AIfeignremember_timer != NULL) + AIfeignremember_timer->Start(AIfeignremember_delay); + feign_memory_list.insert(attacker->CharacterID()); +} + +void Mob::RemoveFromFeignMemory(Client* attacker) { + feign_memory_list.erase(attacker->CharacterID()); + if(feign_memory_list.empty() && AIfeignremember_timer != NULL) + AIfeignremember_timer->Disable(); + if(feign_memory_list.empty()) + { + minLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMin); + maxLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMax); + if(AIfeignremember_timer != NULL) + AIfeignremember_timer->Disable(); + } +} + +void Mob::ClearFeignMemory() { + feign_memory_list.clear(); + minLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMin); + maxLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMax); + if(AIfeignremember_timer != NULL) + AIfeignremember_timer->Disable(); +} + +bool Mob::PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id) { + + if(!caster) return false; + + if(spells[spell_id].ResistDiff <= -600) + return true; + + //Applies additional Charisma bonus to resist rate + float resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster,0,0,1); + + if(IsCharmSpell(spell_id)) { + + if (spells[spell_id].field209 == -1) //If charm spell has this set(-1), it can not break till end of duration. + return true; + + //1: The mob has a default 25% chance of being allowed a resistance check against the charm. + if (MakeRandomInt(0, 100) > RuleI(Spells, CharmBreakCheckChance)) + return true; + + //2: The mob makes a resistance check against the charm + if (resist_check == 100) + return true; + + else + { + if (caster->IsClient()) + { + //3: At maxed ability, Total Domination has a 50% chance of preventing the charm break that otherwise would have occurred. + uint16 TotalDominationBonus = caster->aabonuses.CharmBreakChance + caster->spellbonuses.CharmBreakChance + caster->itembonuses.CharmBreakChance; + + if (MakeRandomInt(0, 100) < TotalDominationBonus) + return true; + + } + } + } + + else + { + // Assume this is a harmony/pacify spell + if (resist_check == 100) + return true; + } + + return false; +} + + + diff --git a/zone/attack.cpp b/zone/attack.cpp new file mode 100644 index 000000000..5e3acb085 --- /dev/null +++ b/zone/attack.cpp @@ -0,0 +1,4334 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2002 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 +*/ + +#if EQDEBUG >= 5 +//#define ATTACK_DEBUG 20 +#endif + +#include "../common/debug.h" +#include +#include +#include +#include +#include +using namespace std; +#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/skills.h" +#include "spdat.h" +#include "zone.h" +#include "StringIDs.h" +#include "../common/MiscFunctions.h" +#include "../common/rulesys.h" +#include "QuestParserCollection.h" +#include "watermap.h" +#include "worldserver.h" +extern WorldServer worldserver; + +#ifdef _WINDOWS +#define snprintf _snprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +extern EntityList entity_list; +#if !defined(NEW_LoadSPDat) && !defined(DB_LoadSPDat) + extern SPDat_Spell_Struct spells[SPDAT_RECORDS]; +#endif + +extern Zone* zone; + +bool Mob::AttackAnimation(SkillType &skillinuse, int Hand, const ItemInst* weapon) +{ + // Determine animation + int type = 0; + if (weapon && weapon->IsType(ItemClassCommon)) { + const Item_Struct* item = weapon->GetItem(); +#if EQDEBUG >= 11 + LogFile->write(EQEMuLog::Debug, "Weapon skill:%i", item->ItemType); +#endif + switch (item->ItemType) + { + case ItemType1HS: // 1H Slashing + { + skillinuse = _1H_SLASHING; + type = anim1HWeapon; + break; + } + case ItemType2HS: // 2H Slashing + { + skillinuse = _2H_SLASHING; + type = anim2HSlashing; + break; + } + case ItemTypePierce: // Piercing + { + skillinuse = PIERCING; + type = animPiercing; + break; + } + case ItemType1HB: // 1H Blunt + { + skillinuse = _1H_BLUNT; + type = anim1HWeapon; + break; + } + case ItemType2HB: // 2H Blunt + { + skillinuse = _2H_BLUNT; + type = anim2HWeapon; + break; + } + case ItemType2HPierce: // 2H Piercing + { + skillinuse = PIERCING; + type = anim2HWeapon; + break; + } + case ItemTypeHand2Hand: + { + skillinuse = HAND_TO_HAND; + type = animHand2Hand; + break; + } + default: + { + skillinuse = HAND_TO_HAND; + type = animHand2Hand; + break; + } + }// switch + } + else if(IsNPC()) { + + switch (skillinuse) + { + case _1H_SLASHING: // 1H Slashing + { + type = anim1HWeapon; + break; + } + case _2H_SLASHING: // 2H Slashing + { + type = anim2HSlashing; + break; + } + case PIERCING: // Piercing + { + type = animPiercing; + break; + } + case _1H_BLUNT: // 1H Blunt + { + type = anim1HWeapon; + break; + } + case _2H_BLUNT: // 2H Blunt + { + type = anim2HWeapon; + break; + } + case 99: // 2H Piercing + { + type = anim2HWeapon; + break; + } + case HAND_TO_HAND: + { + type = animHand2Hand; + break; + } + default: + { + type = animHand2Hand; + break; + } + }// switch + } + else { + skillinuse = HAND_TO_HAND; + type = animHand2Hand; + } + + // If we're attacking with the secondary hand, play the dual wield anim + if (Hand == 14) // DW anim + type = animDualWield; + + DoAnim(type); + return true; +} + +// called when a mob is attacked, does the checks to see if it's a hit +// and does other mitigation checks. 'this' is the mob being attacked. +bool Mob::CheckHitChance(Mob* other, SkillType skillinuse, int Hand, int16 chance_mod) +{ +/*/ + //Reworked a lot of this code to achieve better balance at higher levels. + //The old code basically meant that any in high level (50+) combat, + //both parties always had 95% chance to hit the other one. +/*/ + //If chance bonus set in spell data for Skill Attacks is 10k allow to hit without calculations. + if (chance_mod == 10000) + return true; + + Mob *attacker=other; + Mob *defender=this; + float chancetohit = RuleR(Combat, BaseHitChance); + + if(attacker->IsNPC() && !attacker->IsPet()) + chancetohit += RuleR(Combat, NPCBonusHitChance); + +#if ATTACK_DEBUG>=11 + LogFile->write(EQEMuLog::Debug, "CheckHitChance(%s) attacked by %s", defender->GetName(), attacker->GetName()); +#endif + mlog(COMBAT__TOHIT,"CheckHitChance(%s) attacked by %s", defender->GetName(), attacker->GetName()); + + bool pvpmode = false; + if(IsClient() && other->IsClient()) + pvpmode = true; + + float bonus; + + //////////////////////////////////////////////////////// + // To hit calcs go here + //////////////////////////////////////////////////////// + + uint8 attacker_level = attacker->GetLevel() ? attacker->GetLevel() : 1; + uint8 defender_level = defender->GetLevel() ? defender->GetLevel() : 1; + + //Calculate the level difference + + mlog(COMBAT__TOHIT, "Chance to hit before level diff calc %.2f", chancetohit); + double level_difference = attacker_level - defender_level; + double range = defender->GetLevel(); + range = ((range / 4) + 3); + + if(level_difference < 0) + { + if(level_difference >= -range) + { + chancetohit += (level_difference / range) * RuleR(Combat,HitFalloffMinor); //5 + } + else if (level_difference >= -(range+3.0)) + { + chancetohit -= RuleR(Combat,HitFalloffMinor); + chancetohit += ((level_difference+range) / (3.0)) * RuleR(Combat,HitFalloffModerate); //7 + } + else + { + chancetohit -= (RuleR(Combat,HitFalloffMinor) + RuleR(Combat,HitFalloffModerate)); + chancetohit += ((level_difference+range+3.0)/12.0) * RuleR(Combat,HitFalloffMajor); //50 + } + } + else + { + chancetohit += (RuleR(Combat,HitBonusPerLevel) * level_difference); + } + + mlog(COMBAT__TOHIT, "Chance to hit after level diff calc %.2f", chancetohit); + + chancetohit -= ((float)defender->GetAGI() * RuleR(Combat, AgiHitFactor)); + + mlog(COMBAT__TOHIT, "Chance to hit after agil calc %.2f", chancetohit); + + if(attacker->IsClient()) + { + chancetohit -= (RuleR(Combat,WeaponSkillFalloff) * (attacker->CastToClient()->MaxSkill(skillinuse) - attacker->GetSkill(skillinuse))); + mlog(COMBAT__TOHIT, "Chance to hit after weapon falloff calc (attack) %.2f", chancetohit); + } + + if(defender->IsClient()) + { + chancetohit += (RuleR(Combat,WeaponSkillFalloff) * (defender->CastToClient()->MaxSkill(DEFENSE) - defender->GetSkill(DEFENSE))); + mlog(COMBAT__TOHIT, "Chance to hit after weapon falloff calc (defense) %.2f", chancetohit); + } + + //I dont think this is 100% correct, but at least it does something... + if(attacker->spellbonuses.MeleeSkillCheckSkill == skillinuse || attacker->spellbonuses.MeleeSkillCheckSkill == 255) { + chancetohit += attacker->spellbonuses.MeleeSkillCheck; + mlog(COMBAT__TOHIT, "Applied spell melee skill bonus %d, yeilding %.2f", attacker->spellbonuses.MeleeSkillCheck, chancetohit); + } + if(attacker->itembonuses.MeleeSkillCheckSkill == skillinuse || attacker->itembonuses.MeleeSkillCheckSkill == 255) { + chancetohit += attacker->itembonuses.MeleeSkillCheck; + mlog(COMBAT__TOHIT, "Applied item melee skill bonus %d, yeilding %.2f", attacker->spellbonuses.MeleeSkillCheck, chancetohit); + } + + //subtract off avoidance by the defender. (Live AA - Combat Agility) + bonus = defender->spellbonuses.AvoidMeleeChance + defender->itembonuses.AvoidMeleeChance + (defender->aabonuses.AvoidMeleeChance * 10); + + //AA Live - Elemental Agility + if (IsPet()) { + Mob *owner = defender->GetOwner(); + if (!owner)return false; + bonus += (owner->aabonuses.PetAvoidance + owner->spellbonuses.PetAvoidance + owner->itembonuses.PetAvoidance)*10; + } + + if(bonus > 0) { + chancetohit -= ((bonus * chancetohit) / 1000); + mlog(COMBAT__TOHIT, "Applied avoidance chance %.2f/10, yeilding %.2f", bonus, chancetohit); + if (defender->spellbonuses.AvoidMeleeChance) + defender->CheckHitsRemaining(0, false, false,SE_AvoidMeleeChance); + } + + if(attacker->IsNPC()) + chancetohit += (chancetohit * attacker->CastToNPC()->GetAccuracyRating() / 1000); + + mlog(COMBAT__TOHIT, "Chance to hit after accuracy rating calc %.2f", chancetohit); + + float hitBonus = 0; + + /* + Kayen: Unknown if the HitChance and Accuracy effect's should modify 'chancetohit' + cumulatively or successively. For now all hitBonuses are cumulative. + */ + + hitBonus += attacker->itembonuses.HitChanceEffect[skillinuse] + + attacker->spellbonuses.HitChanceEffect[skillinuse]+ + attacker->itembonuses.HitChanceEffect[HIGHEST_SKILL+1] + + attacker->spellbonuses.HitChanceEffect[HIGHEST_SKILL+1]; + + //Accuracy = Spell Effect , HitChance = 'Accuracy' from Item Effect + //Only AA derived accuracy can be skill limited. ie (Precision of the Pathfinder, Dead Aim) + hitBonus += (attacker->itembonuses.Accuracy[HIGHEST_SKILL+1] + + attacker->spellbonuses.Accuracy[HIGHEST_SKILL+1] + + attacker->aabonuses.Accuracy[HIGHEST_SKILL+1] + + attacker->aabonuses.Accuracy[skillinuse] + + attacker->itembonuses.HitChance) / 15.0f; + + hitBonus += chance_mod; //Modifier applied from casted/disc skill attacks. + + chancetohit += ((chancetohit * hitBonus) / 100.0f); + + if(skillinuse == ARCHERY) + chancetohit -= (chancetohit * RuleR(Combat, ArcheryHitPenalty)) / 100.0f; + + // Chance to hit; Max 95%, Min 30% + if(chancetohit > 1000) { + //if chance to hit is crazy high, that means a discipline is in use, and let it stay there + } + else if(chancetohit > 95) { + chancetohit = 95; + } + else if(chancetohit < 5) { + chancetohit = 5; + } + + //I dont know the best way to handle a garunteed hit discipline being used + //agains a garunteed riposte (for example) discipline... for now, garunteed hit wins + + + #if EQDEBUG>=11 + LogFile->write(EQEMuLog::Debug, "3 FINAL calculated chance to hit is: %5.2f", chancetohit); + #endif + + // + // Did we hit? + // + + float tohit_roll = MakeRandomFloat(0, 100); + + mlog(COMBAT__TOHIT, "Final hit chance: %.2f%%. Hit roll %.2f", chancetohit, tohit_roll); + + return(tohit_roll <= chancetohit); +} + + +bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) +{ + /* solar: called when a mob is attacked, does the checks to see if it's a hit + * and does other mitigation checks. 'this' is the mob being attacked. + * + * special return values: + * -1 - block + * -2 - parry + * -3 - riposte + * -4 - dodge + * + */ + float skill; + float bonus; + float RollTable[4] = {0,0,0,0}; + float roll; + Mob *attacker=other; + Mob *defender=this; + + //garunteed hit + bool ghit = false; + if((attacker->spellbonuses.MeleeSkillCheck + attacker->itembonuses.MeleeSkillCheck) > 500) + ghit = true; + + ////////////////////////////////////////////////////////// + // make enrage same as riposte + ///////////////////////////////////////////////////////// + if (IsEnraged() && !other->BehindMob(this, other->GetX(), other->GetY())) { + damage = -3; + mlog(COMBAT__DAMAGE, "I am enraged, riposting frontal attack."); + } + + ///////////////////////////////////////////////////////// + // riposte + ///////////////////////////////////////////////////////// + float riposte_chance = 0.0f; + if (CanRiposte && damage > 0 && CanThisClassRiposte() && !other->BehindMob(this, other->GetX(), other->GetY())) + { + riposte_chance = (100.0f + (float)defender->aabonuses.RiposteChance + (float)defender->spellbonuses.RiposteChance + (float)defender->itembonuses.RiposteChance) / 100.0f; + skill = GetSkill(RIPOSTE); + if (IsClient()) { + CastToClient()->CheckIncreaseSkill(RIPOSTE, other, -10); + } + + if (!ghit) { //if they are not using a garunteed hit discipline + bonus = 2.0 + skill/60.0 + (GetDEX()/200); + bonus *= riposte_chance; + RollTable[0] = bonus + (itembonuses.HeroicDEX / 25); // 25 heroic = 1%, applies to ripo, parry, block + } + } + + /////////////////////////////////////////////////////// + // block + /////////////////////////////////////////////////////// + + bool bBlockFromRear = false; + bool bShieldBlockFromRear = false; + + if (this->IsClient()) { + int aaChance = 0; + + // a successful roll on this does not mean a successful block is forthcoming. only that a chance to block + // from a direction other than the rear is granted. + + //Live AA - HightenedAwareness + int BlockBehindChance = aabonuses.BlockBehind + spellbonuses.BlockBehind + itembonuses.BlockBehind; + + if (BlockBehindChance && (BlockBehindChance > MakeRandomInt(1, 100))){ + bBlockFromRear = true; + + if (spellbonuses.BlockBehind || itembonuses.BlockBehind) + bShieldBlockFromRear = true; //This bonus should allow a chance to Shield Block from behind. + } + } + + float block_chance = 0.0f; + if (damage > 0 && CanThisClassBlock() && (!other->BehindMob(this, other->GetX(), other->GetY()) || bBlockFromRear)) { + block_chance = (100.0f + (float)spellbonuses.IncreaseBlockChance + (float)itembonuses.IncreaseBlockChance) / 100.0f; + skill = CastToClient()->GetSkill(BLOCKSKILL); + if (IsClient()) { + CastToClient()->CheckIncreaseSkill(BLOCKSKILL, other, -10); + } + + if (!ghit) { //if they are not using a garunteed hit discipline + bonus = 2.0 + skill/35.0 + (GetDEX()/200); + RollTable[1] = RollTable[0] + (bonus * block_chance); + } + } + else{ + RollTable[1] = RollTable[0]; + } + + if(damage > 0 && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock) + && (!other->BehindMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { + bool equiped = CastToClient()->m_inv.GetItem(14); + if(equiped) { + uint8 shield = CastToClient()->m_inv.GetItem(14)->GetItem()->ItemType; + float bonusShieldBlock = 0.0f; + if(shield == ItemTypeShield) { + + //Live AA - Shield Block + bonusShieldBlock = aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock; + RollTable[1] += bonusShieldBlock; + } + } + } + + if(damage > 0 && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock) + && (!other->BehindMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { + bool equiped2 = CastToClient()->m_inv.GetItem(13); + if(equiped2) { + uint8 TwoHandBlunt = CastToClient()->m_inv.GetItem(13)->GetItem()->ItemType; + float bonusStaffBlock = 0.0f; + if(TwoHandBlunt == ItemType2HB) { + + bonusStaffBlock = aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock; + RollTable[1] += bonusStaffBlock; + } + } + } + + ////////////////////////////////////////////////////// + // parry + ////////////////////////////////////////////////////// + float parry_chance = 0.0f; + if (damage > 0 && CanThisClassParry() && !other->BehindMob(this, other->GetX(), other->GetY())) + { + parry_chance = (100.0f + (float)defender->spellbonuses.ParryChance + (float)defender->itembonuses.ParryChance) / 100.0f; + skill = CastToClient()->GetSkill(PARRY); + if (IsClient()) { + CastToClient()->CheckIncreaseSkill(PARRY, other, -10); + } + + if (!ghit) { //if they are not using a garunteed hit discipline + bonus = 2.0 + skill/60.0 + (GetDEX()/200); + bonus *= parry_chance; + RollTable[2] = RollTable[1] + bonus; + } + } + else{ + RollTable[2] = RollTable[1]; + } + + //////////////////////////////////////////////////////// + // dodge + //////////////////////////////////////////////////////// + float dodge_chance = 0.0f; + if (damage > 0 && CanThisClassDodge() && !other->BehindMob(this, other->GetX(), other->GetY())) + { + dodge_chance = (100.0f + (float)defender->spellbonuses.DodgeChance + (float)defender->itembonuses.DodgeChance) / 100.0f; + skill = CastToClient()->GetSkill(DODGE); + if (IsClient()) { + CastToClient()->CheckIncreaseSkill(DODGE, other, -10); + } + + if (!ghit) { //if they are not using a garunteed hit discipline + bonus = 2.0 + skill/60.0 + (GetAGI()/200); + bonus *= dodge_chance; + RollTable[3] = RollTable[2] + bonus - (itembonuses.HeroicDEX / 25) + (itembonuses.HeroicAGI / 25); + } + } + else{ + RollTable[3] = RollTable[2]; + } + + if(damage > 0){ + roll = MakeRandomFloat(0,100); + if(roll <= RollTable[0]){ + damage = -3; + } + else if(roll <= RollTable[1]){ + damage = -1; + } + else if(roll <= RollTable[2]){ + damage = -2; + } + else if(roll <= RollTable[3]){ + damage = -4; + } + } + + mlog(COMBAT__DAMAGE, "Final damage after all avoidances: %d", damage); + + if (damage < 0) + return true; + return false; +} + +void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit) +{ + if(damage <= 0) + return; + + Mob* defender = this; + float aa_mit = 0; + + aa_mit = (aabonuses.CombatStability + itembonuses.CombatStability + spellbonuses.CombatStability)/100.0f; + + if(RuleB(Combat, UseIntervalAC)) + { + float softcap = 0.0; + float mitigation_rating = 0.0; + float attack_rating = 0.0; + int shield_ac = 0; + int armor = 0; + float weight = 0.0; + if(IsClient()) + { + armor = CastToClient()->GetRawACNoShield(shield_ac); + weight = (CastToClient()->CalcCurrentWeight() / 10.0); + } + else if(IsNPC()) + { + armor = spellbonuses.AC + itembonuses.AC + (CastToNPC()->GetRawAC() / RuleR(Combat, NPCACFactor)) + 1; + } + + if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER) + { + softcap = RuleI(Combat, ClothACSoftcap); + } + else if(GetClass() == MONK && weight <= 15.0) + { + softcap = RuleI(Combat, MonkACSoftcap); + } + else if(GetClass() == DRUID || GetClass() == BEASTLORD || GetClass() == MONK) + { + softcap = RuleI(Combat, LeatherACSoftcap); + } + else if(GetClass() == SHAMAN || GetClass() == ROGUE || GetClass() == BERSERKER || GetClass() == RANGER) + { + softcap = RuleI(Combat, ChainACSoftcap); + } + else + { + softcap = RuleI(Combat, PlateACSoftcap); + } + + softcap += shield_ac; + armor += shield_ac; + softcap += (softcap * (aa_mit * RuleR(Combat, AAMitigationACFactor))); + if(armor > softcap) + { + int softcap_armor = armor - softcap; + if(GetClass() == WARRIOR) + { + softcap_armor = softcap_armor * RuleR(Combat, WarriorACSoftcapReturn); + } + else if(GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || (GetClass() == MONK && weight <= 15.0)) + { + softcap_armor = softcap_armor * RuleR(Combat, KnightACSoftcapReturn); + } + else if(GetClass() == CLERIC || GetClass() == BARD || GetClass() == BERSERKER || GetClass() == ROGUE || GetClass() == SHAMAN || GetClass() == MONK) + { + softcap_armor = softcap_armor * RuleR(Combat, LowPlateChainACSoftcapReturn); + } + else if(GetClass() == RANGER || GetClass() == BEASTLORD) + { + softcap_armor = softcap_armor * RuleR(Combat, LowChainLeatherACSoftcapReturn); + } + else if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER || GetClass() == DRUID) + { + softcap_armor = softcap_armor * RuleR(Combat, CasterACSoftcapReturn); + } + else + { + softcap_armor = softcap_armor * RuleR(Combat, MiscACSoftcapReturn); + } + armor = softcap + softcap_armor; + } + + mitigation_rating = 0.0; + if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER) + { + mitigation_rating = ((GetSkill(DEFENSE) + itembonuses.HeroicAGI/10) / 4.0) + armor + 1; + } + else + { + mitigation_rating = ((GetSkill(DEFENSE) + itembonuses.HeroicAGI/10) / 3.0) + (armor * 1.333333) + 1; + } + mitigation_rating *= 0.847; + + if(attacker->IsClient()) + { + attack_rating = (attacker->CastToClient()->CalcATK() + ((attacker->GetSTR()-66) * 0.9) + (attacker->GetSkill(OFFENSE)*1.345)); + } + else + { + attack_rating = (attacker->GetATK() + (attacker->GetSkill(OFFENSE)*1.345) + ((attacker->GetSTR()-66) * 0.9)); + } + + float d = 10.0; + float mit_roll = MakeRandomFloat(0, mitigation_rating); + float atk_roll = MakeRandomFloat(0, attack_rating); + + if(atk_roll > mit_roll) + { + float a_diff = (atk_roll - mit_roll); + float thac0 = attack_rating * RuleR(Combat, ACthac0Factor); + float thac0cap = ((attacker->GetLevel() * 9) + 20); + if(thac0 > thac0cap) + { + thac0 = thac0cap; + } + d -= 10.0 * (a_diff / thac0); + } + else if(mit_roll > atk_roll) + { + float m_diff = (mit_roll - atk_roll); + float thac20 = mitigation_rating * RuleR(Combat, ACthac20Factor); + float thac20cap = ((defender->GetLevel() * 9) + 20); + if(thac20 > thac20cap) + { + thac20 = thac20cap; + } + d += 10 * (m_diff / thac20); + } + + if(d < 0.0) + { + d = 0.0; + } + + if(d > 20) + { + d = 20.0; + } + + float interval = (damage - minhit) / 20.0; + damage = damage - ((int)d * interval); + } + else{ + //////////////////////////////////////////////////////// + // Scorpious2k: Include AC in the calculation + // use serverop variables to set values + int myac = GetAC(); + if (damage > 0 && myac > 0) { + int acfail=1000; + char tmp[10]; + + if (database.GetVariable("ACfail", tmp, 9)) { + acfail = (int) (atof(tmp) * 100); + if (acfail>100) acfail=100; + } + + if (acfail<=0 || MakeRandomInt(0, 100)>acfail) { + float acreduction=1; + int acrandom=300; + if (database.GetVariable("ACreduction", tmp, 9)) + { + acreduction=atof(tmp); + if (acreduction>100) acreduction=100; + } + + if (database.GetVariable("ACrandom", tmp, 9)) + { + acrandom = (int) ((atof(tmp)+1) * 100); + if (acrandom>10100) acrandom=10100; + } + + if (acreduction>0) { + damage -= (int) (GetAC() * acreduction/100.0f); + } + if (acrandom>0) { + damage -= (myac * MakeRandomInt(0, acrandom) / 10000); + } + if (damage<1) damage=1; + mlog(COMBAT__DAMAGE, "AC Damage Reduction: fail chance %d%%. Failed. Reduction %.3f%%, random %d. Resulting damage %d.", acfail, acreduction, acrandom, damage); + } else { + mlog(COMBAT__DAMAGE, "AC Damage Reduction: fail chance %d%%. Did not fail.", acfail); + } + } + + damage -= (aa_mit * damage); + + if(damage != 0 && damage < minhit) + damage = minhit; + } + + //reduce the damage from shielding item and aa based on the min dmg + //spells offer pure mitigation + damage -= (minhit * defender->itembonuses.MeleeMitigation / 100); + damage -= (damage * defender->spellbonuses.MeleeMitigation / 100); + + if(damage < 0) + damage = 0; +} + +//Returns the weapon damage against the input mob +//if we cannot hit the mob with the current weapon we will get a value less than or equal to zero +//Else we know we can hit. +//GetWeaponDamage(mob*, const Item_Struct*) is intended to be used for mobs or any other situation where we do not have a client inventory item +//GetWeaponDamage(mob*, const ItemInst*) is intended to be used for situations where we have a client inventory item +int Mob::GetWeaponDamage(Mob *against, const Item_Struct *weapon_item) { + _ZP(Mob_GetWeaponDamageA); + int dmg = 0; + int banedmg = 0; + + //can't hit invulnerable stuff with weapons. + if(against->GetInvul() || against->SpecAttacks[IMMUNE_MELEE]){ + return 0; + } + + //check to see if our weapons or fists are magical. + if(against->SpecAttacks[IMMUNE_MELEE_NONMAGICAL]){ + if(weapon_item){ + if(weapon_item->Magic){ + dmg = weapon_item->Damage; + + //this is more for non weapon items, ex: boots for kick + //they don't have a dmg but we should be able to hit magical + dmg = dmg <= 0 ? 1 : dmg; + } + else + return 0; + } + else{ + if((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30){ + dmg = GetMonkHandToHandDamage(); + } + else if(GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)){ + //pets wouldn't actually use this but... + //it gives us an idea if we can hit due to the dual nature of this function + dmg = 1; + } + else if(SpecAttacks[SPECATK_MAGICAL]) + { + dmg = 1; + } + else + return 0; + } + } + else{ + if(weapon_item){ + dmg = weapon_item->Damage; + + dmg = dmg <= 0 ? 1 : dmg; + } + else{ + if(GetClass() == MONK || GetClass() == BEASTLORD){ + dmg = GetMonkHandToHandDamage(); + } + else{ + dmg = 1; + } + } + } + + int eledmg = 0; + if(!against->SpecAttacks[IMMUNE_MAGIC]){ + if(weapon_item && weapon_item->ElemDmgAmt){ + //we don't check resist for npcs here + eledmg = weapon_item->ElemDmgAmt; + dmg += eledmg; + } + } + + if(against->SpecAttacks[IMMUNE_MELEE_EXCEPT_BANE]){ + if(weapon_item){ + if(weapon_item->BaneDmgBody == against->GetBodyType()){ + banedmg += weapon_item->BaneDmgAmt; + } + + if(weapon_item->BaneDmgRace == against->GetRace()){ + banedmg += weapon_item->BaneDmgRaceAmt; + } + } + + if(!eledmg && !banedmg){ + if(!SpecAttacks[SPECATK_BANE]) + return 0; + else + return 1; + } + else + dmg += banedmg; + } + else{ + if(weapon_item){ + if(weapon_item->BaneDmgBody == against->GetBodyType()){ + banedmg += weapon_item->BaneDmgAmt; + } + + if(weapon_item->BaneDmgRace == against->GetRace()){ + banedmg += weapon_item->BaneDmgRaceAmt; + } + } + + dmg += (banedmg + eledmg); + } + + if(dmg <= 0){ + return 0; + } + else + return dmg; +} + +int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate) +{ + _ZP(Mob_GetWeaponDamageB); + int dmg = 0; + int banedmg = 0; + + if(!against || against->GetInvul() || against->SpecAttacks[IMMUNE_MELEE]){ + return 0; + } + + //check for items being illegally attained + if(weapon_item){ + const Item_Struct *mWeaponItem = weapon_item->GetItem(); + if(mWeaponItem){ + if(mWeaponItem->ReqLevel > GetLevel()){ + return 0; + } + + if(!weapon_item->IsEquipable(GetBaseRace(), GetClass())){ + return 0; + } + } + else{ + return 0; + } + } + + if(against->SpecAttacks[IMMUNE_MELEE_NONMAGICAL]){ + if(weapon_item){ + // check to see if the weapon is magic + bool MagicWeapon = false; + if(weapon_item->GetItem() && weapon_item->GetItem()->Magic) + MagicWeapon = true; + else { + if(spellbonuses.MagicWeapon || itembonuses.MagicWeapon) + MagicWeapon = true; + } + + if(MagicWeapon) { + + if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ + dmg = CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->Damage); + } + else{ + dmg = weapon_item->GetItem()->Damage; + } + + for(int x = 0; x < 5; x++){ + if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ + dmg += weapon_item->GetAugment(x)->GetItem()->Damage; + if (hate) *hate += weapon_item->GetAugment(x)->GetItem()->Damage + weapon_item->GetAugment(x)->GetItem()->ElemDmgAmt; + } + } + dmg = dmg <= 0 ? 1 : dmg; + } + else + return 0; + } + else{ + if((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30){ + dmg = GetMonkHandToHandDamage(); + if (hate) *hate += dmg; + } + else if(GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)){ //pets wouldn't actually use this but... + dmg = 1; //it gives us an idea if we can hit + } + else if(SpecAttacks[SPECATK_MAGICAL]){ + dmg = 1; + } + else + return 0; + } + } + else{ + if(weapon_item){ + if(weapon_item->GetItem()){ + + if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ + dmg = CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->Damage); + } + else{ + dmg = weapon_item->GetItem()->Damage; + } + + for(int x = 0; x < 5; x++){ + if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ + dmg += weapon_item->GetAugment(x)->GetItem()->Damage; + if (hate) *hate += weapon_item->GetAugment(x)->GetItem()->Damage + weapon_item->GetAugment(x)->GetItem()->ElemDmgAmt; + } + } + dmg = dmg <= 0 ? 1 : dmg; + } + } + else{ + if(GetClass() == MONK || GetClass() == BEASTLORD){ + dmg = GetMonkHandToHandDamage(); + if (hate) *hate += dmg; + } + else{ + dmg = 1; + } + } + } + + int eledmg = 0; + if(!against->SpecAttacks[IMMUNE_MAGIC]){ + if(weapon_item && weapon_item->GetItem() && weapon_item->GetItem()->ElemDmgAmt){ + if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ + eledmg = CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->ElemDmgAmt); + } + else{ + eledmg = weapon_item->GetItem()->ElemDmgAmt; + } + + if(eledmg) + { + eledmg = (eledmg * against->ResistSpell(weapon_item->GetItem()->ElemDmgType, 0, this) / 100); + } + } + + if(weapon_item){ + for(int x = 0; x < 5; x++){ + if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ + if(weapon_item->GetAugment(x)->GetItem()->ElemDmgAmt) + eledmg += (weapon_item->GetAugment(x)->GetItem()->ElemDmgAmt * against->ResistSpell(weapon_item->GetAugment(x)->GetItem()->ElemDmgType, 0, this) / 100); + } + } + } + } + + if(against->SpecAttacks[IMMUNE_MELEE_EXCEPT_BANE]){ + if(weapon_item && weapon_item->GetItem()){ + if(weapon_item->GetItem()->BaneDmgBody == against->GetBodyType()){ + if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ + banedmg += CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->BaneDmgAmt); + } + else{ + banedmg += weapon_item->GetItem()->BaneDmgAmt; + } + } + + if(weapon_item->GetItem()->BaneDmgRace == against->GetRace()){ + if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ + banedmg += CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->BaneDmgRaceAmt); + } + else{ + banedmg += weapon_item->GetItem()->BaneDmgRaceAmt; + } + } + + for(int x = 0; x < 5; x++){ + if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ + if(weapon_item->GetAugment(x)->GetItem()->BaneDmgBody == against->GetBodyType()){ + banedmg += weapon_item->GetAugment(x)->GetItem()->BaneDmgAmt; + } + + if(weapon_item->GetAugment(x)->GetItem()->BaneDmgRace == against->GetRace()){ + banedmg += weapon_item->GetAugment(x)->GetItem()->BaneDmgRaceAmt; + } + } + } + } + + if(!eledmg && !banedmg) + { + if(!SpecAttacks[SPECATK_BANE]) + return 0; + else + return 1; + } + else { + dmg += (banedmg + eledmg); + if (hate) *hate += banedmg; + } + } + else{ + if(weapon_item && weapon_item->GetItem()){ + if(weapon_item->GetItem()->BaneDmgBody == against->GetBodyType()){ + if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ + banedmg += CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->BaneDmgAmt); + } + else{ + banedmg += weapon_item->GetItem()->BaneDmgAmt; + } + } + + if(weapon_item->GetItem()->BaneDmgRace == against->GetRace()){ + if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ + banedmg += CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->BaneDmgRaceAmt); + } + else{ + banedmg += weapon_item->GetItem()->BaneDmgRaceAmt; + } + } + + for(int x = 0; x < 5; x++){ + if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ + if(weapon_item->GetAugment(x)->GetItem()->BaneDmgBody == against->GetBodyType()){ + banedmg += weapon_item->GetAugment(x)->GetItem()->BaneDmgAmt; + } + + if(weapon_item->GetAugment(x)->GetItem()->BaneDmgRace == against->GetRace()){ + banedmg += weapon_item->GetAugment(x)->GetItem()->BaneDmgRaceAmt; + } + } + } + } + dmg += (banedmg + eledmg); + if (hate) *hate += banedmg; + } + + if(dmg <= 0){ + return 0; + } + else + return dmg; +} + +//note: throughout this method, setting `damage` to a negative is a way to +//stop the attack calculations +// IsFromSpell added to allow spell effects to use Attack. (Mainly for the Rampage AA right now.) +bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell) +{ + + _ZP(Client_Attack); + + if (!other) { + SetTarget(NULL); + LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Client::Attack() for evaluation!"); + return false; + } + + if(!GetTarget()) + SetTarget(other); + + mlog(COMBAT__ATTACKS, "Attacking %s with hand %d %s", other?other->GetName():"(NULL)", Hand, bRiposte?"(this is a riposte)":""); + + //SetAttackTimer(); + if ( + (IsCasting() && GetClass() != BARD && !IsFromSpell) + || other == NULL + || ((IsClient() && CastToClient()->dead) || (other->IsClient() && other->CastToClient()->dead)) + || (GetHP() < 0) + || (!IsAttackAllowed(other)) + ) { + mlog(COMBAT__ATTACKS, "Attack canceled, invalid circumstances."); + return false; // Only bards can attack while casting + } + if(DivineAura() && !GetGM()) {//cant attack while invulnerable unless your a gm + mlog(COMBAT__ATTACKS, "Attack canceled, Divine Aura is in effect."); + Message_StringID(MT_DefaultText, DIVINE_AURA_NO_ATK); //You can't attack while invulnerable! + return false; + } + + if (GetFeigned()) + return false; // Rogean: How can you attack while feigned? Moved up from Aggro Code. + + + ItemInst* weapon; + if (Hand == 14){ // Kaiyodo - Pick weapon from the attacking hand + weapon = GetInv().GetItem(SLOT_SECONDARY); + OffHandAtk(true); + } + else{ + weapon = GetInv().GetItem(SLOT_PRIMARY); + OffHandAtk(false); + } + + if(weapon != NULL) { + if (!weapon->IsWeapon()) { + mlog(COMBAT__ATTACKS, "Attack canceled, Item %s (%d) is not a weapon.", weapon->GetItem()->Name, weapon->GetID()); + return(false); + } + mlog(COMBAT__ATTACKS, "Attacking with weapon: %s (%d)", weapon->GetItem()->Name, weapon->GetID()); + } else { + mlog(COMBAT__ATTACKS, "Attacking without a weapon."); + } + + // calculate attack_skill and skillinuse depending on hand and weapon + // also send Packet to near clients + SkillType skillinuse; + AttackAnimation(skillinuse, Hand, weapon); + mlog(COMBAT__ATTACKS, "Attacking with %s in slot %d using skill %d", weapon?weapon->GetItem()->Name:"Fist", Hand, skillinuse); + + /// Now figure out damage + int damage = 0; + uint8 mylevel = GetLevel() ? GetLevel() : 1; + uint32 hate = 0; + if (weapon) hate = weapon->GetItem()->Damage + weapon->GetItem()->ElemDmgAmt; + int weapon_damage = GetWeaponDamage(other, weapon, &hate); + if (hate == 0 && weapon_damage > 1) hate = weapon_damage; + + //if weapon damage > 0 then we know we can hit the target with this weapon + //otherwise we cannot and we set the damage to -5 later on + if(weapon_damage > 0){ + + //Berserker Berserk damage bonus + if(berserk && GetClass() == BERSERKER){ + int bonus = 3 + GetLevel()/10; //unverified + weapon_damage = weapon_damage * (100+bonus) / 100; + mlog(COMBAT__DAMAGE, "Berserker damage bonus increases DMG to %d", weapon_damage); + } + + //try a finishing blow.. if successful end the attack + if(TryFinishingBlow(other, skillinuse)) + return (true); + + int min_hit = 1; + int max_hit = (2*weapon_damage*GetDamageTable(skillinuse)) / 100; + + if(GetLevel() < 10 && max_hit > 20) + max_hit = (RuleI(Combat, HitCapPre10)); + else if(GetLevel() < 20 && max_hit > 40) + max_hit = (RuleI(Combat, HitCapPre20)); + + CheckIncreaseSkill(skillinuse, other, -15); + CheckIncreaseSkill(OFFENSE, other, -15); + + + // *************************************************************** + // *** Calculate the damage bonus, if applicable, for this hit *** + // *************************************************************** + +#ifndef EQEMU_NO_WEAPON_DAMAGE_BONUS + + // If you include the preprocessor directive "#define EQEMU_NO_WEAPON_DAMAGE_BONUS", that indicates that you do not + // want damage bonuses added to weapon damage at all. This feature was requested by ChaosSlayer on the EQEmu Forums. + // + // This is not recommended for normal usage, as the damage bonus represents a non-trivial component of the DPS output + // of weapons wielded by higher-level melee characters (especially for two-handed weapons). + + int ucDamageBonus = 0; + + if( Hand == 13 && GetLevel() >= 28 && IsWarriorClass() ) + { + // Damage bonuses apply only to hits from the main hand (Hand == 13) by characters level 28 and above + // who belong to a melee class. If we're here, then all of these conditions apply. + + ucDamageBonus = GetWeaponDamageBonus( weapon ? weapon->GetItem() : (const Item_Struct*) NULL ); + + min_hit += (int) ucDamageBonus; + max_hit += (int) ucDamageBonus; + hate += ucDamageBonus; + } +#endif + //Live AA - Sinister Strikes *Adds weapon damage bonus to offhand weapon. + if (Hand==14) { + if (aabonuses.SecondaryDmgInc || itembonuses.SecondaryDmgInc || spellbonuses.SecondaryDmgInc){ + + ucDamageBonus = GetWeaponDamageBonus( weapon ? weapon->GetItem() : (const Item_Struct*) NULL ); + + min_hit += (int) ucDamageBonus; + max_hit += (int) ucDamageBonus; + hate += ucDamageBonus; + } + } + + min_hit += min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100; + + if(max_hit < min_hit) + max_hit = min_hit; + + if(RuleB(Combat, UseIntervalAC)) + damage = max_hit; + else + damage = MakeRandomInt(min_hit, max_hit); + + 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); + + //check to see if we hit.. + if(!other->CheckHitChance(this, skillinuse, Hand)) { + mlog(COMBAT__ATTACKS, "Attack missed. Damage set to 0."); + damage = 0; + } else { //we hit, try to avoid it + other->AvoidDamage(this, damage); + other->MeleeMitigation(this, damage, min_hit); + if(damage > 0) { + ApplyMeleeDamageBonus(skillinuse, damage); + damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse); + TryCriticalHit(other, skillinuse, damage); + } + mlog(COMBAT__DAMAGE, "Final damage after all reductions: %d", damage); + } + + //riposte + bool slippery_attack = false; // Part of hack to allow riposte to become a miss, but still allow a Strikethrough chance (like on Live) + if (damage == -3) { + if (bRiposte) return false; + else { + if (Hand == 14) {// 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; + OffhandRiposteFail *= -1; //Live uses a negative value for this. + + if (OffhandRiposteFail && + (OffhandRiposteFail > 99 || (MakeRandomInt(0, 100) < OffhandRiposteFail))) { + damage = 0; // Counts as a miss + slippery_attack = true; + } else + DoRiposte(other); + if (IsDead()) return false; + } + else + DoRiposte(other); + if (IsDead()) return false; + } + } + + 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; + + if(bonusStrikeThrough && (MakeRandomInt(0, 100) < bonusStrikeThrough)) { + Message_StringID(MT_StrikeThrough, STRIKETHROUGH_STRING); // You strike through your opponents defenses! + Attack(other, Hand, false, true); // Strikethrough only gives another attempted hit + return false; + } + } + } + else{ + damage = -5; + } + + + // Hate Generation is on a per swing basis, regardless of a hit, miss, or block, its always the same. + // If we are this far, this means we are atleast making a swing. + if (!bRiposte) // Ripostes never generate any aggro. + other->AddToHateList(this, hate); + + /////////////////////////////////////////////////////////// + ////// Send Attack Damage + /////////////////////////////////////////////////////////// + other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); + + if (IsDead()) return false; + + if(damage > 0 && (spellbonuses.MeleeLifetap || itembonuses.MeleeLifetap)) + { + int lifetap_amt = spellbonuses.MeleeLifetap + itembonuses.MeleeLifetap; + if(lifetap_amt > 100) + lifetap_amt = 100; + + lifetap_amt = damage * lifetap_amt / 100; + + mlog(COMBAT__DAMAGE, "Melee lifetap healing for %d damage.", damage); + //heal self for damage done.. + HealDamage(lifetap_amt); + + if (spellbonuses.MeleeLifetap) + CheckHitsRemaining(0, false,false, SE_MeleeLifetap); + } + + //break invis when you attack + if(invisible) { + mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack."); + BuffFadeByEffect(SE_Invisibility); + BuffFadeByEffect(SE_Invisibility2); + invisible = false; + } + if(invisible_undead) { + mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack."); + BuffFadeByEffect(SE_InvisVsUndead); + BuffFadeByEffect(SE_InvisVsUndead2); + invisible_undead = false; + } + if(invisible_animals){ + mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack."); + 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); + } + + if(GetTarget()) + TriggerDefensiveProcs(weapon, other, Hand, damage); + + if (damage > 0) + return true; + + else + return false; +} + +//used by complete heal and #heal +void Mob::Heal() +{ + SetMaxHP(); + SendHPUpdate(); +} + +void Client::Damage(Mob* other, int32 damage, uint16 spell_id, SkillType attack_skill, bool avoidable, int8 buffslot, bool iBuffTic) +{ + if(dead || IsCorpse()) + return; + + if(spell_id==0) + spell_id = SPELL_UNKNOWN; + + if(spell_id!=0 && spell_id != SPELL_UNKNOWN && other && damage > 0) + { + if(other->IsNPC() && !other->IsPet()) + { + float npcspellscale = other->CastToNPC()->GetSpellScale(); + damage = ((float)damage * npcspellscale) / (float)100; + } + } + + // cut all PVP spell damage to 2/3 -solar + // EverHood - 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; + if(attack_skill == ARCHERY) + PvPMitigation = 80; + else + PvPMitigation = 67; + damage = (damage * PvPMitigation) / 100; + } + + if(!ClientFinishedLoading()) + damage = -5; + + //do a majority of the work... + CommonDamage(other, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic); + + if (damage > 0) { + + if (spell_id == SPELL_UNKNOWN) + CheckIncreaseSkill(DEFENSE, other, -15); + } +} + +void Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_skill) +{ + if(!ClientFinishedLoading()) + return; + + if(dead) + return; //cant die more than once... + + int exploss; + + 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 + // + uint8 killed_level = GetLevel(); + if(!spell) spell = SPELL_UNKNOWN; + + SendLogoutPackets(); + + //make our become corpse packet, and queue to ourself before OP_Death. + EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct)); + BecomeCorpse_Struct* bc = (BecomeCorpse_Struct*)app2.pBuffer; + bc->spawn_id = GetID(); + bc->x = GetX(); + bc->y = GetY(); + bc->z = GetZ(); + QueuePacket(&app2); + + // make death packet + EQApplicationPacket app(OP_Death, sizeof(Death_Struct)); + Death_Struct* d = (Death_Struct*)app.pBuffer; + d->spawn_id = GetID(); + d->killer_id = killerMob ? killerMob->GetID() : 0; + d->corpseid=GetID(); + d->bindzoneid = m_pp.binds[0].zoneId; + d->spell_id = spell == SPELL_UNKNOWN ? 0xffffffff : spell; + d->attack_skill = spell != SPELL_UNKNOWN ? 0xe7 : attack_skill; + d->damage = damage; + app.priority = 6; + entity_list.QueueClients(this, &app); + + // + // #2: figure out things that affect the player dying and mark them dead + // + + InterruptSpell(); + SetPet(0); + SetHorseId(0); + dead = true; + + + if (killerMob != NULL) + { + if (killerMob->IsNPC()) { + parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0); + uint16 emoteid = killerMob->CastToNPC()->GetNPCEmoteID(); + if(emoteid != 0) + killerMob->CastToNPC()->DoNPCEmote(KILLEDPC,emoteid); + killerMob->TrySpellOnKill(killed_level,spell); + } + + if(killerMob->IsClient() && (IsDueling() || killerMob->CastToClient()->IsDueling())) { + SetDueling(false); + SetDuelTarget(0); + if (killerMob->IsClient() && killerMob->CastToClient()->IsDueling() && killerMob->CastToClient()->GetDuelTarget() == GetID()) + { + //if duel opponent killed us... + killerMob->CastToClient()->SetDueling(false); + killerMob->CastToClient()->SetDuelTarget(0); + entity_list.DuelMessage(killerMob,this,false); + } else { + //otherwise, we just died, end the duel. + Mob* who = entity_list.GetMob(GetDuelTarget()); + if(who && who->IsClient()) { + who->CastToClient()->SetDueling(false); + who->CastToClient()->SetDuelTarget(0); + } + } + } + } + + entity_list.RemoveFromTargets(this); + hate_list.RemoveEnt(this); + + + //remove ourself from all proximities + ClearAllProximities(); + + // + // #3: exp loss and corpse generation + // + + // figure out if they should lose exp + if(RuleB(Character, UseDeathExpLossMult)){ + float GetNum [] = {0.005f,0.015f,0.025f,0.035f,0.045f,0.055f,0.065f,0.075f,0.085f,0.095f,0.110f }; + int Num = RuleI(Character, DeathExpLossMultiplier); + if((Num < 0) || (Num > 10)) + Num = 3; + float loss = GetNum[Num]; + exploss=(int)((float)GetEXP() * (loss)); //loose % of total XP pending rule (choose 0-10) + } + + if(!RuleB(Character, UseDeathExpLossMult)){ + exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000); + } + + if( (GetLevel() < RuleI(Character, DeathExpLossLevel)) || (GetLevel() > RuleI(Character, DeathExpLossMaxLevel)) || IsBecomeNPC() ) + { + exploss = 0; + } + else if( killerMob ) + { + if( killerMob->IsClient() ) + { + exploss = 0; + } + else if( killerMob->GetOwner() && killerMob->GetOwner()->IsClient() ) + { + exploss = 0; + } + } + + if(spell != SPELL_UNKNOWN) + { + uint32 buff_count = GetMaxTotalSlots(); + for(uint16 buffIt = 0; buffIt < buff_count; buffIt++) + { + if(buffs[buffIt].spellid == spell && buffs[buffIt].client) + { + exploss = 0; // no exp loss for pvp dot + break; + } + } + } + + bool LeftCorpse = false; + + // now we apply the exp loss, unmem their spells, and make a corpse + // unless they're a GM (or less than lvl 10 + if(!GetGM()) + { + if(exploss > 0) { + int32 newexp = GetEXP(); + if(exploss > newexp) { + //lost more than we have... wtf.. + newexp = 1; + } else { + newexp -= exploss; + } + SetEXP(newexp, GetAAXP()); + //m_epp.perAA = 0; //reset to no AA exp on death. + } + + //this generates a lot of 'updates' to the client that the client does not need + BuffFadeAll(); + if((GetClientVersionBit() & BIT_SoFAndLater) && RuleB(Character, RespawnFromHover)) + UnmemSpellAll(true); + else + UnmemSpellAll(false); + + if(RuleB(Character, LeaveCorpses) && GetLevel() >= RuleI(Character, DeathItemLossLevel) || RuleB(Character, LeaveNakedCorpses)) + { + // creating the corpse takes the cash/items off the player too + Corpse *new_corpse = new Corpse(this, exploss); + + char tmp[20]; + database.GetVariable("ServerType", tmp, 9); + if(atoi(tmp)==1 && killerMob != NULL && killerMob->IsClient()){ + char tmp2[10] = {0}; + database.GetVariable("PvPreward", tmp, 9); + int reward = atoi(tmp); + if(reward==3){ + database.GetVariable("PvPitem", tmp2, 9); + int pvpitem = atoi(tmp2); + if(pvpitem>0 && pvpitem<200000) + new_corpse->SetPKItem(pvpitem); + } + else if(reward==2) + new_corpse->SetPKItem(-1); + else if(reward==1) + new_corpse->SetPKItem(1); + else + new_corpse->SetPKItem(0); + if(killerMob->CastToClient()->isgrouped) { + Group* group = entity_list.GetGroupByClient(killerMob->CastToClient()); + if(group != 0) + { + for(int i=0;i<6;i++) + { + if(group->members[i] != NULL) + { + new_corpse->AllowMobLoot(group->members[i],i); + } + } + } + } + } + + entity_list.AddCorpse(new_corpse, GetID()); + SetID(0); + + //send the become corpse packet to everybody else in the zone. + entity_list.QueueClients(this, &app2, true); + + LeftCorpse = true; + } + +// if(!IsLD())//Todo: make it so an LDed client leaves corpse if its enabled +// MakeCorpse(exploss); + } else { + BuffFadeDetrimental(); + } + + + +#if 0 // solar: commenting this out for now TODO reimplement becomenpc stuff + if (IsBecomeNPC() == true) + { + if (killerMob != NULL && killerMob->IsClient()) { + if (killerMob->CastToClient()->isgrouped && entity_list.GetGroupByMob(killerMob) != 0) + entity_list.GetGroupByMob(killerMob->CastToClient())->SplitExp((uint32)(level*level*75*3.5f), this); + + else + killerMob->CastToClient()->AddEXP((uint32)(level*level*75*3.5f)); // Pyro: Comment this if NPC death crashes zone + //hate_list.DoFactionHits(GetNPCFactionID()); + } + + Corpse* corpse = new Corpse(this->CastToClient(), 0); + entity_list.AddCorpse(corpse, this->GetID()); + this->SetID(0); + if(killerMob->GetOwner() != 0 && killerMob->GetOwner()->IsClient()) + killerMob = killerMob->GetOwner(); + if(killerMob != 0 && killerMob->IsClient()) { + corpse->AllowMobLoot(killerMob, 0); + if(killerMob->CastToClient()->isgrouped) { + Group* group = entity_list.GetGroupByClient(killerMob->CastToClient()); + if(group != 0) { + for(int i=0; i < MAX_GROUP_MEMBERS; i++) { // Doesnt work right, needs work + if(group->members[i] != NULL) { + corpse->AllowMobLoot(group->members[i],i); + } + } + } + } + } + } +#endif + + + // + // 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 + // + + if(LeftCorpse && (GetClientVersionBit() & BIT_SoFAndLater) && RuleB(Character, RespawnFromHover)) + { + ClearDraggedCorpses(); + + RespawnFromHoverTimer.Start(RuleI(Character, RespawnFromHoverTimer) * 1000); + + SendRespawnBinds(); + } + else + { + if(isgrouped) + { + Group *g = GetGroup(); + if(g) + g->MemberZoned(this); + } + + Raid* r = entity_list.GetRaidByClient(this); + + if(r) + r->MemberZoned(this); + + 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(); + + GoToDeath(); + } +} + +bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell) // Kaiyodo - base function has changed prototype, need to update overloaded version +{ + _ZP(NPC_Attack); + int damage = 0; + + if (!other) { + SetTarget(NULL); + LogFile->write(EQEMuLog::Error, "A null Mob object was passed to NPC::Attack() for evaluation!"); + return false; + } + + if(DivineAura()) + return(false); + + if(!GetTarget()) + SetTarget(other); + + //Check that we can attack before we calc heading and face our target + if (!IsAttackAllowed(other)) { + if (this->GetOwnerID()) + entity_list.MessageClose(this, 1, 200, 10, "%s says, 'That is not a legal target master.'", this->GetCleanName()); + if(other) { + RemoveFromHateList(other); + mlog(COMBAT__ATTACKS, "I am not allowed to attack %s", other->GetName()); + } + return false; + } + + FaceTarget(GetTarget()); + + SkillType skillinuse = HAND_TO_HAND; + if (Hand == 13) { + skillinuse = static_cast(GetPrimSkill()); + OffHandAtk(false); + } + if (Hand == 14) { + skillinuse = static_cast(GetSecSkill()); + OffHandAtk(true); + } + + //figure out what weapon they are using, if any + const Item_Struct* weapon = NULL; + if (Hand == 13 && equipment[SLOT_PRIMARY] > 0) + weapon = database.GetItem(equipment[SLOT_PRIMARY]); + else if (equipment[SLOT_SECONDARY]) + weapon = database.GetItem(equipment[SLOT_SECONDARY]); + + //We dont factor much from the weapon into the attack. + //Just the skill type so it doesn't look silly using punching animations and stuff while wielding weapons + if(weapon) { + mlog(COMBAT__ATTACKS, "Attacking with weapon: %s (%d) (too bad im not using it for much)", weapon->Name, weapon->ID); + + if(Hand == 14 && weapon->ItemType == ItemTypeShield){ + mlog(COMBAT__ATTACKS, "Attack with shield canceled."); + return false; + } + + switch(weapon->ItemType){ + case ItemType1HS: + skillinuse = _1H_SLASHING; + break; + case ItemType2HS: + skillinuse = _2H_SLASHING; + break; + case ItemTypePierce: + case ItemType2HPierce: + skillinuse = PIERCING; + break; + case ItemType1HB: + skillinuse = _1H_BLUNT; + break; + case ItemType2HB: + skillinuse = _2H_BLUNT; + break; + case ItemTypeBow: + skillinuse = ARCHERY; + break; + case ItemTypeThrowing: + case ItemTypeThrowingv2: + skillinuse = THROWING; + break; + default: + skillinuse = HAND_TO_HAND; + break; + } + } + + int weapon_damage = GetWeaponDamage(other, weapon); + + //do attack animation regardless of whether or not we can hit below + int16 charges = 0; + ItemInst weapon_inst(weapon, charges); + AttackAnimation(skillinuse, Hand, &weapon_inst); + + //Work-around for there being no 2HP skill - We use 99 for the 2HB animation and 36 for pierce messages + if(skillinuse == 99) + skillinuse = static_cast(36); + + //basically "if not immune" then do the attack + if((weapon_damage) > 0) { + + //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; + if(weapon){ + if(weapon->BaneDmgBody == other->GetBodyType()){ + eleBane += weapon->BaneDmgAmt; + } + + if(weapon->BaneDmgRace == other->GetRace()){ + eleBane += weapon->BaneDmgRaceAmt; + } + + if(weapon->ElemDmgAmt){ + eleBane += (weapon->ElemDmgAmt * other->ResistSpell(weapon->ElemDmgType, 0, this) / 100); + } + } + + if(!RuleB(NPC, UseItemBonusesForNonPets)){ + if(!GetOwner()){ + eleBane = 0; + } + } + + uint8 otherlevel = other->GetLevel(); + uint8 mylevel = this->GetLevel(); + + otherlevel = otherlevel ? otherlevel : 1; + mylevel = mylevel ? mylevel : 1; + + //instead of calcing damage in floats lets just go straight to ints + if(RuleB(Combat, UseIntervalAC)) + damage = (max_dmg+eleBane); + else + damage = MakeRandomInt((min_dmg+eleBane),(max_dmg+eleBane)); + + + //check if we're hitting above our max or below it. + if((min_dmg+eleBane) != 0 && damage < (min_dmg+eleBane)) { + mlog(COMBAT__DAMAGE, "Damage (%d) is below min (%d). Setting to min.", damage, (min_dmg+eleBane)); + damage = (min_dmg+eleBane); + } + if((max_dmg+eleBane) != 0 && damage > (max_dmg+eleBane)) { + mlog(COMBAT__DAMAGE, "Damage (%d) is above max (%d). Setting to max.", damage, (max_dmg+eleBane)); + damage = (max_dmg+eleBane); + } + + int32 hate = damage; + if(IsPet()) + { + hate = hate * 100 / GetDamageTable(skillinuse); + } + //THIS IS WHERE WE CHECK TO SEE IF WE HIT: + if(other->IsClient() && other->CastToClient()->IsSitting()) { + mlog(COMBAT__DAMAGE, "Client %s is sitting. Hitting for max damage (%d).", other->GetName(), (max_dmg+eleBane)); + damage = (max_dmg+eleBane); + damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse); + + mlog(COMBAT__HITS, "Generating hate %d towards %s", hate, GetName()); + // now add done damage to the hate list + other->AddToHateList(this, hate); + } else { + if(!other->CheckHitChance(this, skillinuse, Hand)) { + damage = 0; //miss + } else { //hit, check for damage avoidance + other->AvoidDamage(this, damage); + other->MeleeMitigation(this, damage, min_dmg+eleBane); + if(damage > 0) { + ApplyMeleeDamageBonus(skillinuse, damage); + damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse); + TryCriticalHit(other, skillinuse, damage); + } + mlog(COMBAT__HITS, "Generating hate %d towards %s", hate, GetName()); + // now add done damage to the hate list + if(damage > 0) + { + other->AddToHateList(this, hate); + } + else + other->AddToHateList(this, 0); + } + } + + mlog(COMBAT__DAMAGE, "Final damage against %s: %d", other->GetName(), damage); + + if(other->IsClient() && IsPet() && GetOwner()->IsClient()) { + //pets do half damage to clients in pvp + damage=damage/2; + } + } + else + damage = -5; + + //cant riposte a riposte + if (bRiposte && damage == -3) { + mlog(COMBAT__DAMAGE, "Riposte of riposte canceled."); + return false; + } + + int16 DeathHP = 0; + DeathHP = other->GetDelayDeath() * -1; + + if(GetHP() > 0 && other->GetHP() >= DeathHP) { + other->Damage(this, damage, SPELL_UNKNOWN, skillinuse, false); // Not avoidable client already had thier chance to Avoid + } else + return false; + + if (HasDied()) //killed by damage shield ect + return false; + + //break invis when you attack + if(invisible) { + mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack."); + BuffFadeByEffect(SE_Invisibility); + BuffFadeByEffect(SE_Invisibility2); + invisible = false; + } + if(invisible_undead) { + mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack."); + BuffFadeByEffect(SE_InvisVsUndead); + BuffFadeByEffect(SE_InvisVsUndead2); + invisible_undead = false; + } + if(invisible_animals){ + mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack."); + BuffFadeByEffect(SE_InvisVsAnimals); + invisible_animals = false; + } + + if(hidden || improved_hidden) + { + 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); + } + + + hidden = false; + improved_hidden = false; + + //I doubt this works... + if (!GetTarget()) + return true; //We killed them + + if( !bRiposte && other->GetHP() > 0 ) { + TryWeaponProc(weapon, other, Hand); //no weapon + } + + TriggerDefensiveProcs(NULL, other, Hand, damage); + + // now check ripostes + if (damage == -3) { // riposting + DoRiposte(other); + } + + if (damage > 0) + return true; + + else + return false; +} + +void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, SkillType attack_skill, bool avoidable, int8 buffslot, bool iBuffTic) { + if(spell_id==0) + spell_id = SPELL_UNKNOWN; + + //handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds + if(attacked_timer.Check()) + { + mlog(COMBAT__HITS, "Triggering EVENT_ATTACK due to attack by %s", other->GetName()); + parse->EventNPC(EVENT_ATTACK, this, other, "", 0); + } + attacked_timer.Start(CombatEventTimer_expire); + + if (!IsEngaged()) + zone->AddAggroMob(); + + if(GetClass() == LDON_TREASURE) + { + if(IsLDoNLocked() && GetLDoNLockedSkill() != LDoNTypeMechanical) + { + damage = -5; + } + else + { + if(IsLDoNTrapped()) + { + Message_StringID(13, LDON_ACCIDENT_SETOFF2); + SpellFinished(GetLDoNTrapSpellID(), other, 10, 0, -1, spells[GetLDoNTrapSpellID()].ResistDiff, false); + SetLDoNTrapSpellID(0); + SetLDoNTrapped(false); + SetLDoNTrapDetected(false); + } + } + } + + //do a majority of the work... + CommonDamage(other, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic); + + if(damage > 0) { + //see if we are gunna start fleeing + if(!IsPet()) CheckFlee(); + } +} + +void NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_skill) { + _ZP(NPC_Death); + mlog(COMBAT__HITS, "Fatal blow dealt by %s with %d damage, spell %d, skill %d", killerMob->GetName(), damage, spell, attack_skill); + + if (this->IsEngaged()) + { + zone->DelAggroMob(); +#if EQDEBUG >= 11 + LogFile->write(EQEMuLog::Debug,"NPC::Death() Mobs currently Aggro %i", zone->MobsAggroCount()); +#endif + } + SetHP(0); + SetPet(0); + Mob* killer = GetHateDamageTop(this); + + entity_list.RemoveFromTargets(this, p_depop); + + if(p_depop == true) + return; + + BuffFadeAll(); + uint8 killed_level = GetLevel(); + + EQApplicationPacket* app= new EQApplicationPacket(OP_Death,sizeof(Death_Struct)); + Death_Struct* d = (Death_Struct*)app->pBuffer; + d->spawn_id = GetID(); + d->killer_id = killerMob ? killerMob->GetID() : 0; +// d->unknown12 = 1; + d->bindzoneid = 0; + d->spell_id = spell == SPELL_UNKNOWN ? 0xffffffff : spell; + d->attack_skill = SkillDamageTypes[attack_skill]; + d->damage = damage; + app->priority = 6; + entity_list.QueueClients(killerMob, app, false); + + if(respawn2) { + respawn2->DeathReset(); + } + + if (killerMob) { + if(GetClass() != LDON_TREASURE) + hate_list.Add(killerMob, damage); + } + + safe_delete(app); + + Mob *give_exp = hate_list.GetDamageTop(this); + + if(give_exp == NULL) + give_exp = killer; + + if(give_exp && give_exp->HasOwner()) { + + bool ownerInGroup = false; + if((give_exp->HasGroup() && give_exp->GetGroup()->IsGroupMember(give_exp->GetUltimateOwner())) + || (give_exp->IsPet() && (give_exp->GetOwner()->IsClient() + || ( give_exp->GetOwner()->HasGroup() && give_exp->GetOwner()->GetGroup()->IsGroupMember(give_exp->GetOwner()->GetUltimateOwner()))))) + ownerInGroup = true; + + give_exp = give_exp->GetUltimateOwner(); + +#ifdef BOTS + if(!RuleB(Bots, BotGroupXP) && !ownerInGroup) { + give_exp = NULL; + } +#endif //BOTS + } + + int PlayerCount = 0; // QueryServ Player Counting + + Client *give_exp_client = NULL; + if(give_exp && give_exp->IsClient()) + give_exp_client = give_exp->CastToClient(); + + bool IsLdonTreasure = (this->GetClass() == LDON_TREASURE); + if (give_exp_client && !IsCorpse() && MerchantType == 0) + { + Group *kg = entity_list.GetGroupByClient(give_exp_client); + Raid *kr = entity_list.GetRaidByClient(give_exp_client); + + if(kr) + { + if(!IsLdonTreasure) { + kr->SplitExp((EXP_FORMULA), 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 != NULL) { // If Group Member is Client + parse->EventNPC(EVENT_KILLED_MERIT, this, kr->members[i].member, "killed", 0); + if(RuleB(TaskSystem, EnableTaskSystem)) + kr->members[i].member->UpdateTasksOnKill(GetNPCTypeID()); + PlayerCount++; + } + } + + // QueryServ Logging - Raid Kills + if(RuleB(QueryServ, PlayerLogNPCKills)){ + ServerPacket* pack = new ServerPacket(ServerOP_QSPlayerLogNPCKills, sizeof(QSPlayerLogNPCKill_Struct) + (sizeof(QSPlayerLogNPCKillsPlayers_Struct) * PlayerCount)); + PlayerCount = 0; + QSPlayerLogNPCKill_Struct* QS = (QSPlayerLogNPCKill_Struct*) pack->pBuffer; + QS->s1.NPCID = this->GetNPCTypeID(); + 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 != NULL) { // If Group Member is Client + Client *c = kr->members[i].member; + QS->Chars[PlayerCount].char_id = c->CharacterID(); + PlayerCount++; + } + } + worldserver.SendPacket(pack); // Send Packet to World + safe_delete(pack); + } + // End QueryServ Logging + + } + else if (give_exp_client->IsGrouped() && kg != NULL) + { + if(!IsLdonTreasure) { + kg->SplitExp((EXP_FORMULA), this); + if(killerMob && (kg->IsGroupMember(killerMob->GetName()) || kg->IsGroupMember(killerMob->GetUltimateOwner()->GetName()))) + killerMob->TrySpellOnKill(killed_level,spell); + } + /* Send the EVENT_KILLED_MERIT event and update kill tasks + * for all group members */ + for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (kg->members[i] != NULL && kg->members[i]->IsClient()) { // If Group Member is Client + Client *c = kg->members[i]->CastToClient(); + parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0); + if(RuleB(TaskSystem, EnableTaskSystem)) + c->UpdateTasksOnKill(GetNPCTypeID()); + + PlayerCount++; + } + } + + // QueryServ Logging - Group Kills + if(RuleB(QueryServ, PlayerLogNPCKills)){ + ServerPacket* pack = new ServerPacket(ServerOP_QSPlayerLogNPCKills, sizeof(QSPlayerLogNPCKill_Struct) + (sizeof(QSPlayerLogNPCKillsPlayers_Struct) * PlayerCount)); + PlayerCount = 0; + QSPlayerLogNPCKill_Struct* QS = (QSPlayerLogNPCKill_Struct*) pack->pBuffer; + QS->s1.NPCID = this->GetNPCTypeID(); + QS->s1.ZoneID = this->GetZoneID(); + QS->s1.Type = 1; // Group Fight + for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (kg->members[i] != NULL && kg->members[i]->IsClient()) { // If Group Member is Client + Client *c = kg->members[i]->CastToClient(); + QS->Chars[PlayerCount].char_id = c->CharacterID(); + PlayerCount++; + } + } + worldserver.SendPacket(pack); // Send Packet to World + safe_delete(pack); + } + // End QueryServ Logging + } + else + { + if(!IsLdonTreasure) { + int conlevel = give_exp->GetLevelCon(GetLevel()); + if (conlevel != CON_GREEN) + { + if(GetOwner() && GetOwner()->IsClient()){ + } + else { + give_exp_client->AddEXP((EXP_FORMULA), conlevel); // Pyro: Comment this if NPC death crashes zone + if(killerMob && (killerMob->GetID() == give_exp_client->GetID() || killerMob->GetUltimateOwner()->GetID() == give_exp_client->GetID())) + killerMob->TrySpellOnKill(killed_level,spell); + } + } + } + /* Send the EVENT_KILLED_MERIT event */ + parse->EventNPC(EVENT_KILLED_MERIT, this, give_exp_client, "killed", 0); + if(RuleB(TaskSystem, EnableTaskSystem)) + give_exp_client->UpdateTasksOnKill(GetNPCTypeID()); + + // QueryServ Logging - Solo + if(RuleB(QueryServ, PlayerLogNPCKills)){ + ServerPacket* pack = new ServerPacket(ServerOP_QSPlayerLogNPCKills, sizeof(QSPlayerLogNPCKill_Struct) + (sizeof(QSPlayerLogNPCKillsPlayers_Struct) * 1)); + QSPlayerLogNPCKill_Struct* QS = (QSPlayerLogNPCKill_Struct*) pack->pBuffer; + QS->s1.NPCID = this->GetNPCTypeID(); + QS->s1.ZoneID = this->GetZoneID(); + QS->s1.Type = 0; // Solo Fight + Client *c = give_exp_client; + QS->Chars[0].char_id = c->CharacterID(); + PlayerCount++; + worldserver.SendPacket(pack); // Send Packet to World + safe_delete(pack); + } + // End QueryServ Logging + } + } + + //do faction hits even if we are a merchant, so long as a player killed us + if(give_exp_client) + hate_list.DoFactionHits(GetNPCFactionID()); + + if (!HasOwner() && !IsMerc() && class_ != MERCHANT && class_ != ADVENTUREMERCHANT && !GetSwarmInfo() + && MerchantType == 0 && killer && (killer->IsClient() || (killer->HasOwner() && killer->GetUltimateOwner()->IsClient()) || + (killer->IsNPC() && killer->CastToNPC()->GetSwarmInfo() && killer->CastToNPC()->GetSwarmInfo()->GetOwner() && killer->CastToNPC()->GetSwarmInfo()->GetOwner()->IsClient()))) + { + if(killer != 0) + { + if(killer->GetOwner() != 0 && killer->GetOwner()->IsClient()) + killer = killer->GetOwner(); + + if(!killer->CastToClient()->GetGM() && killer->IsClient()) + this->CheckMinMaxLevel(killer); + } + entity_list.RemoveFromAutoXTargets(this); + uint16 emoteid = this->GetNPCEmoteID(); + Corpse* corpse = new Corpse(this, &itemlist, GetNPCTypeID(), &NPCTypedata,level>54?RuleI(NPC,MajorNPCCorpseDecayTimeMS):RuleI(NPC,MinorNPCCorpseDecayTimeMS)); + entity_list.LimitRemoveNPC(this); + entity_list.AddCorpse(corpse, this->GetID()); + + entity_list.UnMarkNPC(GetID()); + entity_list.RemoveNPC(GetID()); + this->SetID(0); + if(killer != 0 && emoteid != 0) + corpse->CastToNPC()->DoNPCEmote(AFTERDEATH, emoteid); + if(killer != 0 && killer->IsClient()) { + corpse->AllowMobLoot(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] != NULL) { + corpse->AllowMobLoot(group->members[i],i); + } + } + } + } + else if(killer->IsRaidGrouped()){ + Raid* r = entity_list.GetRaidByClient(killer->CastToClient()); + if(r){ + int i = 0; + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + switch(r->GetLootType()) + { + case 0: + case 1: + if(r->members[x].member && r->members[x].IsRaidLeader){ + corpse->AllowMobLoot(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); + i++; + } + else if(r->members[x].member && r->members[x].IsGroupLeader){ + corpse->AllowMobLoot(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); + i++; + } + break; + case 4: + if(r->members[x].member) + { + corpse->AllowMobLoot(r->members[x].member, i); + i++; + } + break; + } + } + } + } + } + + if(zone && zone->adv_data) + { + ServerZoneAdventureDataReply_Struct *sr = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; + if(sr->type == Adventure_Kill) + { + zone->DoAdventureCountIncrease(); + } + else if(sr->type == Adventure_Assassinate) + { + if(sr->data_id == GetNPCTypeID()) + { + zone->DoAdventureCountIncrease(); + } + else + { + zone->DoAdventureAssassinationCountIncrease(); + } + } + } + } + else + entity_list.RemoveFromXTargets(this); + + // Parse quests even if we're killed by an NPC + if(killerMob) { + Mob *oos = killerMob->GetOwnerOrSelf(); + parse->EventNPC(EVENT_DEATH, this, oos, "", 0); + uint16 emoteid = this->GetNPCEmoteID(); + if(emoteid != 0) + this->DoNPCEmote(ONDEATH,emoteid); + if(oos->IsNPC()) + { + parse->EventNPC(EVENT_NPC_SLAY, oos->CastToNPC(), this, "", 0); + uint16 emoteid = oos->CastToNPC()->GetNPCEmoteID(); + if(emoteid != 0) + oos->CastToNPC()->DoNPCEmote(KILLEDNPC,emoteid); + killerMob->TrySpellOnKill(killed_level,spell); + } + } + + this->WipeHateList(); + p_depop = true; + if(killerMob && killerMob->GetTarget() == this) //we can kill things without having them targeted + killerMob->SetTarget(NULL); //via AE effects and such.. + + entity_list.UpdateFindableNPCState(this, true); +} + +void Mob::AddToHateList(Mob* other, int32 hate, int32 damage, bool iYellForHelp, bool bFrenzy, bool iBuffTic) { + assert(other != NULL); + if (other == this) + return; + + if(damage < 0){ + hate = 1; + } + + bool wasengaged = IsEngaged(); + Mob* owner = other->GetOwner(); + Mob* mypet = this->GetPet(); + Mob* myowner = this->GetOwner(); + Mob* targetmob = this->GetTarget(); + + if(other){ + AddRampage(other); + int hatemod = 100 + other->spellbonuses.hatemod + other->itembonuses.hatemod + other->aabonuses.hatemod; + if(hatemod < 1) + hatemod = 1; + hate = ((hate * (hatemod))/100); + } + + if(IsPet() && GetOwner() && GetOwner()->GetAA(aaPetDiscipline) && IsHeld() && !IsFocused()) { //ignore aggro if hold and !focus + return; + } + + if(IsPet() && GetOwner() && GetOwner()->GetAA(aaPetDiscipline) && IsHeld() && GetOwner()->GetAA(aaAdvancedPetDiscipline) >= 1 && IsFocused()) { + if (!targetmob) + return; + } + + if(IsClient() && !IsAIControlled()) + return; + + if(IsFamiliar() || SpecAttacks[IMMUNE_AGGRO]) + return; + + if (other == myowner) + return; + + if(other->SpecAttacks[IMMUNE_AGGRO_ON]) + return; + + if(SpecAttacks[NPC_TUNNELVISION]) { + Mob *top = GetTarget(); + if(top && top != other) { + hate *= RuleR(Aggro, TunnelVisionAggroMod); + } + } + + if(IsNPC() && CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) { + if(!zone->watermap->InLiquid(other->GetX(), other->GetY(), other->GetZ())) { + return; + } + } + // first add self + + // The damage on the hate list is used to award XP to the killer. This check is to prevent Killstealing. + // e.g. Mob has 5000 hit points, Player A melees it down to 500 hp, Player B executes a headshot (10000 damage). + // If we add 10000 damage, Player B would get the kill credit, so we only award damage credit to player B of the + // amount of HP the mob had left. + // + if(damage > GetHP()) + damage = GetHP(); + + hate_list.Add(other, hate, damage, bFrenzy, !iBuffTic); + + if(other->IsClient()) + other->CastToClient()->AddAutoXTarget(this); + +#ifdef BOTS + // if other is a bot, add the bots client to the hate list + if(other->IsBot()) { + if(other->CastToBot()->GetBotOwner() && other->CastToBot()->GetBotOwner()->CastToClient()->GetFeigned()) { + AddFeignMemory(other->CastToBot()->GetBotOwner()->CastToClient()); + } + else { + if(!hate_list.IsOnHateList(other->CastToBot()->GetBotOwner())) + hate_list.Add(other->CastToBot()->GetBotOwner(), 0, 0, false, true); + } + } +#endif //BOTS + + + // if other is a merc, add the merc client to the hate list + if(other->IsMerc()) { + if(other->CastToMerc()->GetMercOwner() && other->CastToMerc()->GetMercOwner()->CastToClient()->GetFeigned()) { + AddFeignMemory(other->CastToMerc()->GetMercOwner()->CastToClient()); + } + else { + if(!hate_list.IsOnHateList(other->CastToMerc()->GetMercOwner())) + hate_list.Add(other->CastToMerc()->GetMercOwner(), 0, 0, false, true); + } + } //MERC + + // then add pet owner if there's one + if (owner) { // Other is a pet, add him and it + // EverHood 6/12/06 + // Can't add a feigned owner to hate list + if(owner->IsClient() && owner->CastToClient()->GetFeigned()) { + //they avoid hate due to feign death... + } else { + // cb:2007-08-17 + // owner must get on list, but he's not actually gained any hate yet + if(!owner->SpecAttacks[IMMUNE_AGGRO]) + { + hate_list.Add(owner, 0, 0, false, !iBuffTic); + if(owner->IsClient()) + owner->CastToClient()->AddAutoXTarget(this); + } + } + } + + if (mypet && (!(GetAA(aaPetDiscipline) && mypet->IsHeld()))) { // I have a pet, add other to it + if(!mypet->IsFamiliar() && !mypet->SpecAttacks[IMMUNE_AGGRO]) + mypet->hate_list.Add(other, 0, 0, bFrenzy); + } else if (myowner) { // I am a pet, add other to owner if it's NPC/LD + if (myowner->IsAIControlled() && !myowner->SpecAttacks[IMMUNE_AGGRO]) + myowner->hate_list.Add(other, 0, 0, bFrenzy); + } + if (!wasengaged) { + if(IsNPC() && other->IsClient() && other->CastToClient()) + parse->EventNPC(EVENT_AGGRO, this->CastToNPC(), other, "", 0); + AI_Event_Engaged(other, iYellForHelp); + adverrorinfo = 8293; + } +} + +// solar: this is called from Damage() when 'this' is attacked by 'other. +// 'this' is the one being attacked +// 'other' is the attacker +// a damage shield causes damage (or healing) to whoever attacks the wearer +// a reverse ds causes damage to the wearer whenever it attack someone +// given this, a reverse ds must be checked each time the wearer is attacking +// and not when they're attacked +//a damage shield on a spell is a negative value but on an item it's a positive value so add the spell value and subtract the item value to get the end ds value +void Mob::DamageShield(Mob* attacker, bool spell_ds) { + + if(!attacker || this == attacker) + return; + + int DS = 0; + int rev_ds = 0; + uint16 spellid = 0; + + if(!spell_ds) + { + DS = spellbonuses.DamageShield; + rev_ds = attacker->spellbonuses.ReverseDamageShield; + + if(spellbonuses.DamageShieldSpellID != 0 && spellbonuses.DamageShieldSpellID != SPELL_UNKNOWN) + spellid = spellbonuses.DamageShieldSpellID; + } + else { + DS = spellbonuses.SpellDamageShield; + rev_ds = 0; + // This ID returns "you are burned", seemed most appropriate for spell DS + spellid = 2166; + } + + if(DS == 0 && rev_ds == 0) + return; + + mlog(COMBAT__HITS, "Applying Damage Shield of value %d to %s", DS, attacker->GetName()); + + //invert DS... spells yield negative values for a true damage shield + if(DS < 0) { + if(!spell_ds) { + + DS += aabonuses.DamageShield; //Live AA - coat of thistles. (negative value) + DS -= itembonuses.DamageShield; //+Damage Shield should only work when you already have a DS spell + + //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 + + attacker->spellbonuses.DSMitigationOffHand + + attacker->aabonuses.DSMitigationOffHand; + DS -= DS*mitigation/100; + } + DS -= DS * attacker->itembonuses.DSMitigation / 100; + } + attacker->Damage(this, -DS, spellid, ABJURE/*hackish*/, false); + //we can assume there is a spell now + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); + CombatDamage_Struct* cds = (CombatDamage_Struct*)outapp->pBuffer; + cds->target = attacker->GetID(); + cds->source = GetID(); + cds->type = spellbonuses.DamageShieldType; + cds->spellid = 0x0; + cds->damage = DS; + entity_list.QueueCloseClients(this, outapp); + safe_delete(outapp); + } else if (DS > 0 && !spell_ds) { + //we are healing the attacker... + attacker->HealDamage(DS); + //TODO: send a packet??? + } + + //Reverse DS + //this is basically a DS, but the spell is on the attacker, not the attackee + //if we've gotten to this point, we know we know "attacker" hit "this" (us) for damage & we aren't invulnerable + uint16 rev_ds_spell_id = SPELL_UNKNOWN; + + if(spellbonuses.ReverseDamageShieldSpellID != 0 && spellbonuses.ReverseDamageShieldSpellID != SPELL_UNKNOWN) + rev_ds_spell_id = spellbonuses.ReverseDamageShieldSpellID; + + if(rev_ds < 0) { + mlog(COMBAT__HITS, "Applying Reverse Damage Shield of value %d to %s", rev_ds, attacker->GetName()); + attacker->Damage(this, -rev_ds, rev_ds_spell_id, ABJURE/*hackish*/, false); //"this" (us) will get the hate, etc. not sure how this works on Live, but it'll works for now, and tanks will love us for this + //do we need to send a damage packet here also? + /* + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); + CombatDamage_Struct* cds = (CombatDamage_Struct*)outapp->pBuffer; + cds->target = attacker->GetID(); + cds->source = GetID(); + cds->type = attacker->spellbonuses.ReverseDamageShieldType; + cds->spellid = 0x0; + cds->damage = rev_ds; + entity_list.QueueCloseClients(this, outapp); + safe_delete(outapp); + */ + } +} + +uint8 Mob::GetWeaponDamageBonus( const Item_Struct *Weapon ) +{ + _ZP(Mob_GetWeaponDamageBonus); + // This function calculates and returns the damage bonus for the weapon identified by the parameter "Weapon". + // Modified 9/21/2008 by Cantus + + + // Assert: This function should only be called for hits by the mainhand, as damage bonuses apply only to the + // weapon in the primary slot. Be sure to check that Hand == 13 before calling. + + // Assert: The caller should ensure that Weapon is actually a weapon before calling this function. + // The ItemInst::IsWeapon() method can be used to quickly determine this. + + // Assert: This function should not be called if the player's level is below 28, as damage bonuses do not begin + // to apply until level 28. + + // Assert: This function should not be called unless the player is a melee class, as casters do not receive a damage bonus. + + + if( Weapon == NULL || Weapon->ItemType == ItemType1HS || Weapon->ItemType == ItemType1HB || Weapon->ItemType == ItemTypeHand2Hand || Weapon->ItemType == ItemTypePierce ) + { + // The weapon in the player's main (primary) hand is a one-handed weapon, or there is no item equipped at all. + // + // According to player posts on Allakhazam, 1H damage bonuses apply to bare fists (nothing equipped in the mainhand, + // as indicated by Weapon == NULL). + // + // The following formula returns the correct damage bonus for all 1H weapons: + + return (uint8) ((GetLevel() - 25) / 3); + } + + // If we've gotten to this point, the weapon in the mainhand is a two-handed weapon. + // Calculating damage bonuses for 2H weapons is more complicated, as it's based on PC level AND the delay of the weapon. + // The formula to calculate 2H bonuses is HIDEOUS. It's a huge conglomeration of ternary operators and multiple operations. + // + // The following is a hybrid approach. In cases where the Level and Delay merit a formula that does not use many operators, + // the formula is used. In other cases, lookup tables are used for speed. + // Though the following code may look bloated and ridiculous, it's actually a very efficient way of calculating these bonuses. + + // Player Level is used several times in the code below, so save it into a variable. + // If GetLevel() were an ordinary function, this would DEFINITELY make sense, as it'd cut back on all of the function calling + // overhead involved with multiple calls to GetLevel(). But in this case, GetLevel() is a simple, inline accessor method. + // So it probably doesn't matter. If anyone knows for certain that there is no overhead involved with calling GetLevel(), + // as I suspect, then please feel free to delete the following line, and replace all occurences of "ucPlayerLevel" with "GetLevel()". + uint8 ucPlayerLevel = (uint8) GetLevel(); + + + // The following may look cleaner, and would certainly be easier to understand, if it was + // a simple 53x150 cell matrix. + // + // However, that would occupy 7,950 Bytes of memory (7.76 KB), and would likely result + // in "thrashing the cache" when performing lookups. + // + // Initially, I thought the best approach would be to reverse-engineer the formula used by + // Sony/Verant to calculate these 2H weapon damage bonuses. But the more than Reno and I + // worked on figuring out this formula, the more we're concluded that the formula itself ugly + // (that is, it contains so many operations and conditionals that it's fairly CPU intensive). + // Because of that, we're decided that, in most cases, a lookup table is the most efficient way + // to calculate these damage bonuses. + // + // The code below is a hybrid between a pure formulaic approach and a pure, brute-force + // lookup table. In cases where a formula is the best bet, I use a formula. In other places + // where a formula would be ugly, I use a lookup table in the interests of speed. + + + if( Weapon->Delay <= 27 ) + { + // Damage Bonuses for all 2H weapons with delays of 27 or less are identical. + // They are the same as the damage bonus would be for a corresponding 1H weapon, plus one. + // This formula applies to all levels 28-80, and will probably continue to apply if + + // the level cap on Live ever is increased beyond 80. + + return (ucPlayerLevel - 22) / 3; + } + + + if( ucPlayerLevel == 65 && Weapon->Delay <= 59 ) + { + // Consider these two facts: + // * Level 65 is the maximum level on many EQ Emu servers. + // * If you listed the levels of all characters logged on to a server, odds are that the number you'll + // see most frequently is level 65. That is, there are more level 65 toons than any other single level. + // + // Therefore, if we can optimize this function for level 65 toons, we're speeding up the server! + // + // With that goal in mind, I create an array of Damage Bonuses for level 65 characters wielding 2H weapons with + // delays between 28 and 59 (inclusive). I suspect that this one small lookup array will therefore handle + // many of the calls to this function. + + static const uint8 ucLevel65DamageBonusesForDelays28to59[] = {35, 35, 36, 36, 37, 37, 38, 38, 39, 39, 40, 40, 42, 42, 42, 45, 45, 47, 48, 49, 49, 51, 51, 52, 53, 54, 54, 56, 56, 57, 58, 59}; + + return ucLevel65DamageBonusesForDelays28to59[Weapon->Delay-28]; + } + + + if( ucPlayerLevel > 65 ) + { + if( ucPlayerLevel > 80 ) + { + // As level 80 is currently the highest achievable level on Live, we only include + // damage bonus information up to this level. + // + // If there is a custom EQEmu server that allows players to level beyond 80, the + // damage bonus for their 2H weapons will simply not increase beyond their damage + // bonus at level 80. + + ucPlayerLevel = 80; + } + + // Lucy does not list a chart of damage bonuses for players levels 66+, + // so my original version of this function just applied the level 65 damage + // bonus for level 66+ toons. That sucked for higher level toons, as their + // 2H weapons stopped ramping up in DPS as they leveled past 65. + // + // Thanks to the efforts of two guys, this is no longer the case: + // + // Janusd (Zetrakyl) ran a nifty query against the PEQ item database to list + // the name of an example 2H weapon that represents each possible unique 2H delay. + // + // Romai then wrote an excellent script to automatically look up each of those + // weapons, open the Lucy item page associated with it, and iterate through all + // levels in the range 66 - 80. He saved the damage bonus for that weapon for + // each level, and that forms the basis of the lookup tables below. + + if( Weapon->Delay <= 59 ) + { + static const uint8 ucDelay28to59Levels66to80[32][15]= + { + /* Level: */ + /* 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 */ + + {36, 37, 38, 39, 41, 42, 43, 44, 45, 47, 49, 49, 49, 50, 53}, /* Delay = 28 */ + {36, 38, 38, 39, 42, 43, 43, 45, 46, 48, 49, 50, 51, 52, 54}, /* Delay = 29 */ + {37, 38, 39, 40, 43, 43, 44, 46, 47, 48, 50, 51, 52, 53, 55}, /* Delay = 30 */ + {37, 39, 40, 40, 43, 44, 45, 46, 47, 49, 51, 52, 52, 52, 54}, /* Delay = 31 */ + {38, 39, 40, 41, 44, 45, 45, 47, 48, 48, 50, 52, 53, 55, 57}, /* Delay = 32 */ + {38, 40, 41, 41, 44, 45, 46, 48, 49, 50, 52, 53, 54, 56, 58}, /* Delay = 33 */ + {39, 40, 41, 42, 45, 46, 47, 48, 49, 51, 53, 54, 55, 57, 58}, /* Delay = 34 */ + {39, 41, 42, 43, 46, 46, 47, 49, 50, 52, 54, 55, 56, 57, 59}, /* Delay = 35 */ + {40, 41, 42, 43, 46, 47, 48, 50, 51, 53, 55, 55, 56, 58, 60}, /* Delay = 36 */ + {40, 42, 43, 44, 47, 48, 49, 50, 51, 53, 55, 56, 57, 59, 61}, /* Delay = 37 */ + {41, 42, 43, 44, 47, 48, 49, 51, 52, 54, 56, 57, 58, 60, 62}, /* Delay = 38 */ + {41, 43, 44, 45, 48, 49, 50, 52, 53, 55, 57, 58, 59, 61, 63}, /* Delay = 39 */ + {43, 45, 46, 47, 50, 51, 52, 54, 55, 57, 59, 60, 61, 63, 65}, /* Delay = 40 */ + {43, 45, 46, 47, 50, 51, 52, 54, 55, 57, 59, 60, 61, 63, 65}, /* Delay = 41 */ + {44, 46, 47, 48, 51, 52, 53, 55, 56, 58, 60, 61, 62, 64, 66}, /* Delay = 42 */ + {46, 48, 49, 50, 53, 54, 55, 58, 59, 61, 63, 64, 65, 67, 69}, /* Delay = 43 */ + {47, 49, 50, 51, 54, 55, 56, 58, 59, 61, 64, 65, 66, 68, 70}, /* Delay = 44 */ + {48, 50, 51, 52, 56, 57, 58, 60, 61, 63, 65, 66, 68, 70, 72}, /* Delay = 45 */ + {50, 52, 53, 54, 57, 58, 59, 62, 63, 65, 67, 68, 69, 71, 74}, /* Delay = 46 */ + {50, 52, 53, 55, 58, 59, 60, 62, 63, 66, 68, 69, 70, 72, 74}, /* Delay = 47 */ + {51, 53, 54, 55, 58, 60, 61, 63, 64, 66, 69, 69, 71, 73, 75}, /* Delay = 48 */ + {52, 54, 55, 57, 60, 61, 62, 65, 66, 68, 70, 71, 73, 75, 77}, /* Delay = 49 */ + {53, 55, 56, 57, 61, 62, 63, 65, 67, 69, 71, 72, 74, 76, 78}, /* Delay = 50 */ + {53, 55, 57, 58, 61, 62, 64, 66, 67, 69, 72, 73, 74, 77, 79}, /* Delay = 51 */ + {55, 57, 58, 59, 63, 64, 65, 68, 69, 71, 74, 75, 76, 78, 81}, /* Delay = 52 */ + {57, 55, 59, 60, 63, 65, 66, 68, 70, 72, 74, 76, 77, 79, 82}, /* Delay = 53 */ + {56, 58, 59, 61, 64, 65, 67, 69, 70, 73, 75, 76, 78, 80, 82}, /* Delay = 54 */ + {57, 59, 61, 62, 66, 67, 68, 71, 72, 74, 77, 78, 80, 82, 84}, /* Delay = 55 */ + {58, 60, 61, 63, 66, 68, 69, 71, 73, 75, 78, 79, 80, 83, 85}, /* Delay = 56 */ + + /* Important Note: Janusd's search for 2H weapons did not find */ + /* any 2H weapon with a delay of 57. Therefore the values below */ + /* are interpolated, not exact! */ + {59, 61, 62, 64, 67, 69, 70, 72, 74, 76, 77, 78, 81, 84, 86}, /* Delay = 57 INTERPOLATED */ + + {60, 62, 63, 65, 68, 70, 71, 74, 75, 78, 80, 81, 83, 85, 88}, /* Delay = 58 */ + + /* Important Note: Janusd's search for 2H weapons did not find */ + /* any 2H weapon with a delay of 59. Therefore the values below */ + /* are interpolated, not exact! */ + {60, 62, 64, 65, 69, 70, 72, 74, 76, 78, 81, 82, 84, 86, 89}, /* Delay = 59 INTERPOLATED */ + }; + + return ucDelay28to59Levels66to80[Weapon->Delay-28][ucPlayerLevel-66]; + } + else + { + // Delay is 60+ + + const static uint8 ucDelayOver59Levels66to80[6][15] = + { + /* Level: */ + /* 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 */ + + {61, 63, 65, 66, 70, 71, 73, 75, 77, 79, 82, 83, 85, 87, 90}, /* Delay = 60 */ + {65, 68, 69, 71, 75, 76, 78, 80, 82, 85, 87, 89, 91, 93, 96}, /* Delay = 65 */ + + /* Important Note: Currently, the only 2H weapon with a delay */ + /* of 66 is not player equippable (it's None/None). So I'm */ + /* leaving it commented out to keep this table smaller. */ + //{66, 68, 70, 71, 75, 77, 78, 81, 83, 85, 88, 90, 91, 94, 97}, /* Delay = 66 */ + + {70, 72, 74, 76, 80, 81, 83, 86, 88, 88, 90, 95, 97, 99, 102}, /* Delay = 70 */ + {82, 85, 87, 89, 89, 94, 98, 101, 103, 106, 109, 111, 114, 117, 120}, /* Delay = 85 */ + {90, 93, 96, 98, 103, 105, 107, 111, 113, 116, 120, 122, 125, 128, 131}, /* Delay = 95 */ + + /* Important Note: Currently, the only 2H weapons with delay */ + /* 100 are GM-only items purchased from vendors in Sunset Home */ + /* (cshome). Because they are highly unlikely to be used in */ + /* combat, I'm commenting it out to keep the table smaller. */ + //{95, 98, 101, 103, 108, 110, 113, 116, 119, 122, 126, 128, 131, 134, 138},/* Delay = 100 */ + + {136, 140, 144, 148, 154, 157, 161, 166, 170, 174, 179, 183, 187, 191, 196} /* Delay = 150 */ + }; + + if( Weapon->Delay < 65 ) + { + return ucDelayOver59Levels66to80[0][ucPlayerLevel-66]; + } + else if( Weapon->Delay < 70 ) + { + return ucDelayOver59Levels66to80[1][ucPlayerLevel-66]; + } + else if( Weapon->Delay < 85 ) + { + return ucDelayOver59Levels66to80[2][ucPlayerLevel-66]; + } + else if( Weapon->Delay < 95 ) + { + return ucDelayOver59Levels66to80[3][ucPlayerLevel-66]; + } + else if( Weapon->Delay < 150 ) + { + return ucDelayOver59Levels66to80[4][ucPlayerLevel-66]; + } + else + { + return ucDelayOver59Levels66to80[5][ucPlayerLevel-66]; + } + } + } + + + // If we've gotten to this point in the function without hitting a return statement, + // we know that the character's level is between 28 and 65, and that the 2H weapon's + // delay is 28 or higher. + + // The Damage Bonus values returned by this function (in the level 28-65 range) are + // based on a table of 2H Weapon Damage Bonuses provided by Lucy at the following address: + // http://lucy.allakhazam.com/dmgbonus.html + + if( Weapon->Delay <= 39 ) + { + if( ucPlayerLevel <= 53) + { + // The Damage Bonus for all 2H weapons with delays between 28 and 39 (inclusive) is the same for players level 53 and below... + static const uint8 ucDelay28to39LevelUnder54[] = {1, 1, 2, 3, 3, 3, 4, 5, 5, 6, 6, 6, 8, 8, 8, 9, 9, 10, 11, 11, 11, 12, 13, 14, 16, 17}; + + // As a note: The following formula accurately calculates damage bonuses for 2H weapons with delays in the range 28-39 (inclusive) + // for characters levels 28-50 (inclusive): + // return ( (ucPlayerLevel - 22) / 3 ) + ( (ucPlayerLevel - 25) / 5 ); + // + // However, the small lookup array used above is actually much faster. So we'll just use it instead of the formula + // + // (Thanks to Reno for helping figure out the above formula!) + + return ucDelay28to39LevelUnder54[ucPlayerLevel-28]; + } + else + { + // Use a matrix to look up the damage bonus for 2H weapons with delays between 28 and 39 wielded by characters level 54 and above. + static const uint8 ucDelay28to39Level54to64[12][11] = + { + /* Level: */ + /* 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 */ + + {17, 21, 21, 23, 25, 26, 28, 30, 31, 31, 33}, /* Delay = 28 */ + {17, 21, 22, 23, 25, 26, 29, 30, 31, 32, 34}, /* Delay = 29 */ + {18, 21, 22, 23, 25, 27, 29, 31, 32, 32, 34}, /* Delay = 30 */ + {18, 21, 22, 23, 25, 27, 29, 31, 32, 33, 34}, /* Delay = 31 */ + {18, 21, 22, 24, 26, 27, 30, 32, 32, 33, 35}, /* Delay = 32 */ + {18, 21, 22, 24, 26, 27, 30, 32, 33, 34, 35}, /* Delay = 33 */ + {18, 22, 22, 24, 26, 28, 30, 32, 33, 34, 36}, /* Delay = 34 */ + {18, 22, 23, 24, 26, 28, 31, 33, 34, 34, 36}, /* Delay = 35 */ + {18, 22, 23, 25, 27, 28, 31, 33, 34, 35, 37}, /* Delay = 36 */ + {18, 22, 23, 25, 27, 29, 31, 33, 34, 35, 37}, /* Delay = 37 */ + {18, 22, 23, 25, 27, 29, 32, 34, 35, 36, 38}, /* Delay = 38 */ + {18, 22, 23, 25, 27, 29, 32, 34, 35, 36, 38} /* Delay = 39 */ + }; + + return ucDelay28to39Level54to64[Weapon->Delay-28][ucPlayerLevel-54]; + } + } + else if( Weapon->Delay <= 59 ) + { + if( ucPlayerLevel <= 52 ) + { + if( Weapon->Delay <= 45 ) + { + static const uint8 ucDelay40to45Levels28to52[6][25] = + { + /* Level: */ + /* 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52 */ + + {2, 2, 3, 4, 4, 4, 5, 6, 6, 7, 7, 7, 9, 9, 9, 10, 10, 11, 12, 12, 12, 13, 14, 16, 18}, /* Delay = 40 */ + {2, 2, 3, 4, 4, 4, 5, 6, 6, 7, 7, 7, 9, 9, 9, 10, 10, 11, 12, 12, 12, 13, 14, 16, 18}, /* Delay = 41 */ + {2, 2, 3, 4, 4, 4, 5, 6, 6, 7, 7, 7, 9, 9, 9, 10, 10, 11, 12, 12, 12, 13, 14, 16, 18}, /* Delay = 42 */ + {4, 4, 5, 6, 6, 6, 7, 8, 8, 9, 9, 9, 11, 11, 11, 12, 12, 13, 14, 14, 14, 15, 16, 18, 20}, /* Delay = 43 */ + {4, 4, 5, 6, 6, 6, 7, 8, 8, 9, 9, 9, 11, 11, 11, 12, 12, 13, 14, 14, 14, 15, 16, 18, 20}, /* Delay = 44 */ + {5, 5, 6, 7, 7, 7, 8, 9, 9, 10, 10, 10, 12, 12, 12, 13, 13, 14, 15, 15, 15, 16, 17, 19, 21} /* Delay = 45 */ + }; + + return ucDelay40to45Levels28to52[Weapon->Delay-40][ucPlayerLevel-28]; + } + else + { + static const uint8 ucDelay46Levels28to52[] = {6, 6, 7, 8, 8, 8, 9, 10, 10, 11, 11, 11, 13, 13, 13, 14, 14, 15, 16, 16, 16, 17, 18, 20, 22}; + + return ucDelay46Levels28to52[ucPlayerLevel-28] + ((Weapon->Delay-46) / 3); + } + } + else + { + // Player is in the level range 53 - 64 + + // Calculating damage bonus for 2H weapons with a delay between 40 and 59 (inclusive) involves, unforunately, a brute-force matrix lookup. + static const uint8 ucDelay40to59Levels53to64[20][37] = + { + /* Level: */ + /* 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 */ + + {19, 20, 24, 25, 27, 29, 31, 34, 36, 37, 38, 40}, /* Delay = 40 */ + {19, 20, 24, 25, 27, 29, 31, 34, 36, 37, 38, 40}, /* Delay = 41 */ + {19, 20, 24, 25, 27, 29, 31, 34, 36, 37, 38, 40}, /* Delay = 42 */ + {21, 22, 26, 27, 29, 31, 33, 37, 39, 40, 41, 43}, /* Delay = 43 */ + {21, 22, 26, 27, 29, 32, 34, 37, 39, 40, 41, 43}, /* Delay = 44 */ + {22, 23, 27, 28, 31, 33, 35, 38, 40, 42, 43, 45}, /* Delay = 45 */ + {23, 24, 28, 30, 32, 34, 36, 40, 42, 43, 44, 46}, /* Delay = 46 */ + {23, 24, 29, 30, 32, 34, 37, 40, 42, 43, 44, 47}, /* Delay = 47 */ + {23, 24, 29, 30, 32, 35, 37, 40, 43, 44, 45, 47}, /* Delay = 48 */ + {24, 25, 30, 31, 34, 36, 38, 42, 44, 45, 46, 49}, /* Delay = 49 */ + {24, 26, 30, 31, 34, 36, 39, 42, 44, 46, 47, 49}, /* Delay = 50 */ + {24, 26, 30, 31, 34, 36, 39, 42, 45, 46, 47, 49}, /* Delay = 51 */ + {25, 27, 31, 33, 35, 38, 40, 44, 46, 47, 49, 51}, /* Delay = 52 */ + {25, 27, 31, 33, 35, 38, 40, 44, 46, 48, 49, 51}, /* Delay = 53 */ + {26, 27, 32, 33, 36, 38, 41, 44, 47, 48, 49, 52}, /* Delay = 54 */ + {27, 28, 33, 34, 37, 39, 42, 46, 48, 50, 51, 53}, /* Delay = 55 */ + {27, 28, 33, 34, 37, 40, 42, 46, 49, 50, 51, 54}, /* Delay = 56 */ + {27, 28, 33, 34, 37, 40, 43, 46, 49, 50, 52, 54}, /* Delay = 57 */ + {28, 29, 34, 36, 39, 41, 44, 48, 50, 52, 53, 56}, /* Delay = 58 */ + {28, 29, 34, 36, 39, 41, 44, 48, 51, 52, 54, 56} /* Delay = 59 */ + }; + + return ucDelay40to59Levels53to64[Weapon->Delay-40][ucPlayerLevel-53]; + } + } + else + { + // The following table allows us to look up Damage Bonuses for weapons with delays greater than or equal to 60. + // + // There aren't a lot of 2H weapons with a delay greater than 60. In fact, both a database and Lucy search run by janusd confirm + // that the only unique 2H delays greater than 60 are: 65, 70, 85, 95, and 150. + // + // To be fair, there are also weapons with delays of 66 and 100. But they are either not equippable (None/None), or are + // only available to GMs from merchants in Sunset Home (cshome). In order to keep this table "lean and mean", I will not + // include the values for delays 66 and 100. If they ever are wielded, the 66 delay weapon will use the 65 delay bonuses, + // and the 100 delay weapon will use the 95 delay bonuses. So it's not a big deal. + // + // Still, if someone in the future decides that they do want to include them, here are the tables for these two delays: + // + // {12, 12, 13, 14, 14, 14, 15, 16, 16, 17, 17, 17, 19, 19, 19, 20, 20, 21, 22, 22, 22, 23, 24, 26, 29, 30, 32, 37, 39, 42, 45, 48, 53, 55, 57, 59, 61, 64} /* Delay = 66 */ + // {24, 24, 25, 26, 26, 26, 27, 28, 28, 29, 29, 29, 31, 31, 31, 32, 32, 33, 34, 34, 34, 35, 36, 39, 43, 45, 48, 55, 57, 62, 66, 71, 77, 80, 83, 85, 89, 92} /* Delay = 100 */ + // + // In case there are 2H weapons added in the future with delays other than those listed above (and until the damage bonuses + // associated with that new delay are added to this function), this function is designed to do the following: + // + // For weapons with delays in the range 60-64, use the Damage Bonus that would apply to a 2H weapon with delay 60. + // For weapons with delays in the range 65-69, use the Damage Bonus that would apply to a 2H weapon with delay 65 + // For weapons with delays in the range 70-84, use the Damage Bonus that would apply to a 2H weapon with delay 70. + // For weapons with delays in the range 85-94, use the Damage Bonus that would apply to a 2H weapon with delay 85. + // For weapons with delays in the range 95-149, use the Damage Bonus that would apply to a 2H weapon with delay 95. + // For weapons with delays 150 or higher, use the Damage Bonus that would apply to a 2H weapon with delay 150. + + static const uint8 ucDelayOver59Levels28to65[6][38] = + { + /* Level: */ + /* 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64. 65 */ + + {10, 10, 11, 12, 12, 12, 13, 14, 14, 15, 15, 15, 17, 17, 17, 18, 18, 19, 20, 20, 20, 21, 22, 24, 27, 28, 30, 35, 36, 39, 42, 45, 49, 51, 53, 54, 57, 59}, /* Delay = 60 */ + {12, 12, 13, 14, 14, 14, 15, 16, 16, 17, 17, 17, 19, 19, 19, 20, 20, 21, 22, 22, 22, 23, 24, 26, 29, 30, 32, 37, 39, 42, 45, 48, 52, 55, 57, 58, 61, 63}, /* Delay = 65 */ + {14, 14, 15, 16, 16, 16, 17, 18, 18, 19, 19, 19, 21, 21, 21, 22, 22, 23, 24, 24, 24, 25, 26, 28, 31, 33, 35, 40, 42, 45, 48, 52, 56, 59, 61, 62, 65, 68}, /* Delay = 70 */ + {19, 19, 20, 21, 21, 21, 22, 23, 23, 24, 24, 24, 26, 26, 26, 27, 27, 28, 29, 29, 29, 30, 31, 34, 37, 39, 41, 47, 49, 54, 57, 61, 66, 69, 72, 74, 77, 80}, /* Delay = 85 */ + {22, 22, 23, 24, 24, 24, 25, 26, 26, 27, 27, 27, 29, 29, 29, 30, 30, 31, 32, 32, 32, 33, 34, 37, 40, 43, 45, 52, 54, 59, 62, 67, 73, 76, 79, 81, 84, 88}, /* Delay = 95 */ + {40, 40, 41, 42, 42, 42, 43, 44, 44, 45, 45, 45, 47, 47, 47, 48, 48, 49, 50, 50, 50, 51, 52, 56, 61, 65, 69, 78, 82, 89, 94, 102, 110, 115, 119, 122, 127, 132} /* Delay = 150 */ + }; + + if( Weapon->Delay < 65 ) + { + return ucDelayOver59Levels28to65[0][ucPlayerLevel-28]; + } + else if( Weapon->Delay < 70 ) + { + return ucDelayOver59Levels28to65[1][ucPlayerLevel-28]; + } + else if( Weapon->Delay < 85 ) + { + return ucDelayOver59Levels28to65[2][ucPlayerLevel-28]; + } + else if( Weapon->Delay < 95 ) + { + return ucDelayOver59Levels28to65[3][ucPlayerLevel-28]; + } + else if( Weapon->Delay < 150 ) + { + return ucDelayOver59Levels28to65[4][ucPlayerLevel-28]; + } + else + { + return ucDelayOver59Levels28to65[5][ucPlayerLevel-28]; + } + } +} + +int Mob::GetMonkHandToHandDamage(void) +{ + // Kaiyodo - Determine a monk's fist damage. Table data from www.monkly-business.com + // saved as static array - this should speed this function up considerably + static int damage[66] = { + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 + 99, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 9, 9, 9, 9, 9,10,10,10,10,10,11,11,11,11,11, + 12,12,12,12,12,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14, + 14,14,15,15,15,15 }; + + // Have a look to see if we have epic fists on + + if (IsClient() && CastToClient()->GetItemIDAt(12) == 10652) + return(9); + else + { + int Level = GetLevel(); + if (Level > 65) + return(19); + else + return damage[Level]; + } +} + +int Mob::GetMonkHandToHandDelay(void) +{ + // Kaiyodo - Determine a monk's fist delay. Table data from www.monkly-business.com + // saved as static array - this should speed this function up considerably + static int delayshuman[66] = { + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 + 99,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, + 36,36,36,36,36,35,35,35,35,35,34,34,34,34,34,33,33,33,33,33, + 32,32,32,32,32,31,31,31,31,31,30,30,30,29,29,29,28,28,28,27, + 26,24,22,20,20,20 }; + static int delaysiksar[66] = { + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 + 99,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, + 36,36,36,36,36,36,36,36,36,36,35,35,35,35,35,34,34,34,34,34, + 33,33,33,33,33,32,32,32,32,32,31,31,31,30,30,30,29,29,29,28, + 27,24,22,20,20,20 }; + + // Have a look to see if we have epic fists on + if (IsClient() && CastToClient()->GetItemIDAt(12) == 10652) + return(16); + else + { + int Level = GetLevel(); + if (GetRace() == HUMAN) + { + if (Level > 65) + return(24); + else + return delayshuman[Level]; + } + else //heko: iksar table + { + if (Level > 65) + return(25); + else + return delaysiksar[Level]; + } + } +} + +int32 Mob::ReduceDamage(int32 damage) +{ + if(damage <= 0) + return damage; + + int32 slot = -1; + + if (spellbonuses.NegateAttacks[0]){ + slot = spellbonuses.NegateAttacks[1]; + if(slot >= 0) { + if(CheckHitsRemaining(slot, false, true)) + return -6; + } + } + + if (spellbonuses.MitigateMeleeRune[0]){ + slot = spellbonuses.MitigateMeleeRune[1]; + if(slot >= 0) + { + int damage_to_reduce = damage * spellbonuses.MitigateMeleeRune[0] / 100; + if(damage_to_reduce > buffs[slot].melee_rune) + { + mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" + " damage remaining, fading buff.", damage_to_reduce, buffs[slot].melee_rune); + damage -= damage_to_reduce; + if(!TryFadeEffect(slot)) + BuffFadeBySlot(slot); + UpdateRuneFlags(); + } + else + { + mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" + " damage remaining.", damage_to_reduce, buffs[slot].melee_rune); + buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce); + damage -= damage_to_reduce; + if (!CheckHitsRemaining(slot)) + UpdateRuneFlags(); + } + } + } + + if(damage < 1) + return -6; + + if (HasRune()) + damage = RuneAbsorb(damage, SE_Rune); + + if(damage < 1) + return -6; + + if (spellbonuses.ManaAbsorbPercentDamage[0]){ + slot = spellbonuses.ManaAbsorbPercentDamage[1]; + if(GetMana() > damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100) { + damage -= (damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100); + SetMana(GetMana() - damage); + CheckHitsRemaining(slot); + } + } + + return(damage); +} + +int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTic, Mob* attacker) +{ + if(damage <= 0) + return damage; + + int32 slot = -1; + + // See if we block the spell outright first + if (spellbonuses.NegateAttacks[0]){ + slot = spellbonuses.NegateAttacks[1]; + if(slot >= 0) { + if(CheckHitsRemaining(slot, false, true)) + return 0; + } + } + + // If this is a DoT, use DoT Shielding... + if(iBuffTic) + damage -= (damage * itembonuses.DoTShielding / 100); + + // This must be a DD then so lets apply Spell Shielding and runes. + else + { + // Reduce damage by the Spell Shielding first so that the runes don't take the raw damage. + damage -= (damage * itembonuses.SpellShield / 100); + + // Do runes now. + if (spellbonuses.MitigateSpellRune[0]){ + slot = spellbonuses.MitigateSpellRune[1]; + if(slot >= 0) + { + int damage_to_reduce = damage * spellbonuses.MitigateSpellRune[0] / 100; + if(damage_to_reduce > buffs[slot].magic_rune) + { + mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateSpellDamage %d damage negated, %d" + " damage remaining, fading buff.", damage_to_reduce, buffs[slot].magic_rune); + damage -= damage_to_reduce; + if(!TryFadeEffect(slot)) + BuffFadeBySlot(slot); + UpdateRuneFlags(); + } + else + { + mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" + " damage remaining.", damage_to_reduce, buffs[slot].magic_rune); + buffs[slot].magic_rune = (buffs[slot].magic_rune - damage_to_reduce); + damage -= damage_to_reduce; + if (!CheckHitsRemaining(slot)) + UpdateRuneFlags(); + } + } + } + + if(damage < 1) + return 0; + + + if (HasSpellRune()) + damage = RuneAbsorb(damage, SE_AbsorbMagicAtt); + + if(damage < 1) + return 0; + + if (spellbonuses.ManaAbsorbPercentDamage[0]){ + slot = spellbonuses.ManaAbsorbPercentDamage[1]; + if(GetMana() > damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100) { + damage -= (damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100); + SetMana(GetMana() - damage); + CheckHitsRemaining(slot); + } + } + } + return damage; +} + +bool Mob::HasProcs() const +{ + for (int i = 0; i < MAX_PROCS; i++) + if (PermaProcs[i].spellID != SPELL_UNKNOWN || SpellProcs[i].spellID != SPELL_UNKNOWN) + return true; + return false; +} + +bool Mob::HasDefensiveProcs() const +{ + for (int i = 0; i < MAX_PROCS; i++) + if (DefensiveProcs[i].spellID != SPELL_UNKNOWN) + return true; + return false; +} + +bool Mob::HasSkillProcs() const +{ + for (int i = 0; i < MAX_PROCS; i++) + if (SkillProcs[i].spellID != SPELL_UNKNOWN) + return true; + return false; +} + +bool Mob::HasRangedProcs() const +{ + for (int i = 0; i < MAX_PROCS; i++) + if (RangedProcs[i].spellID != SPELL_UNKNOWN) + return true; + return false; +} + +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; + + if(!HasSkill(DOUBLE_ATTACK) && !bonusGiveDA) + return false; + + float chance = 0.0f; + + uint16 skill = GetSkill(DOUBLE_ATTACK); + + int16 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) + chance = (float(skill+GetLevel()) * (float(100.0f+bonusDA+bonusGiveDA) /100.0f)) /500.0f; + else + chance = (float(bonusGiveDA) * (float(100.0f+bonusDA)/100.0f) ) /100.0f; + + //Live now uses a static Triple Attack skill (lv 46 = 2% lv 60 = 20%) - We do not have this skill on EMU ATM. + //A reasonable forumla would then be TA = 20% * chance + //AA's can also give triple attack skill over cap. (ie Burst of Power) NOTE: Skill ID in spell data is 76 (Triple Attack) + //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; + chance *= 0.2f; //Baseline chance is 20% of your double attack chance. + chance *= float(100.0f+triple_bonus)/100.0f; //Apply modifiers. + } + + if((MakeRandomFloat(0, 1) < chance)) + return true; + + return false; +} + +void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, const SkillType skill_used, bool &avoidable, const int8 buffslot, const bool iBuffTic) { + // This method is called with skill_used=ABJURE for Damage Shield damage. + bool FromDamageShield = (skill_used == ABJURE); + + mlog(COMBAT__HITS, "Applying damage %d done by %s with skill %d and spell %d, avoidable? %s, is %sa buff tic in slot %d", + damage, attacker?attacker->GetName():"NOBODY", skill_used, spell_id, avoidable?"yes":"no", iBuffTic?"":"not ", buffslot); + + if (GetInvul() || DivineAura()) { + mlog(COMBAT__DAMAGE, "Avoiding %d damage due to invulnerability.", damage); + damage = -5; + } + + if( spell_id != SPELL_UNKNOWN || attacker == NULL ) + avoidable = false; + + // only apply DS if physical damage (no spell damage) + // damage shield calls this function with spell_id set, so its unavoidable + if (attacker && damage > 0 && spell_id == SPELL_UNKNOWN && skill_used != ARCHERY && skill_used != THROWING) { + DamageShield(attacker); + + if (spellbonuses.DamageShield) + CheckHitsRemaining(0, false, false, SE_DamageShield); + } + + if(attacker){ + if(attacker->IsClient()){ + if(!RuleB(Combat, EXPFromDmgShield)) { + // Damage shield damage shouldn't count towards who gets EXP + if(!attacker->CastToClient()->GetFeigned() && !FromDamageShield) + AddToHateList(attacker, 0, damage, true, false, iBuffTic); + } + else { + if(!attacker->CastToClient()->GetFeigned()) + AddToHateList(attacker, 0, damage, true, false, iBuffTic); + } + } + else + AddToHateList(attacker, 0, damage, true, false, iBuffTic); + } + + if(damage > 0) { + //if there is some damage being done and theres an attacker involved + if(attacker) { + if(spell_id == SPELL_HARM_TOUCH2 && attacker->IsClient() && attacker->CastToClient()->CheckAAEffect(aaEffectLeechTouch)){ + int healed = damage; + healed = attacker->GetActSpellHealing(spell_id, healed); + attacker->HealDamage(healed); + entity_list.MessageClose(this, true, 300, MT_Emote, "%s beams a smile at %s", attacker->GetCleanName(), this->GetCleanName() ); + attacker->CastToClient()->DisableAAEffect(aaEffectLeechTouch); + } + + // if spell is lifetap add hp to the caster + if (spell_id != SPELL_UNKNOWN && IsLifetapSpell( spell_id )) { + int healed = damage; + + healed = attacker->GetActSpellHealing(spell_id, healed); + mlog(COMBAT__DAMAGE, "Applying lifetap heal of %d to %s", healed, attacker->GetName()); + attacker->HealDamage(healed); + + //we used to do a message to the client, but its gone now. + // emote goes with every one ... even npcs + entity_list.MessageClose(this, true, 300, MT_Emote, "%s beams a smile at %s", attacker->GetCleanName(), this->GetCleanName() ); + } + } //end `if there is some damage being done and theres anattacker person involved` + + Mob *pet = GetPet(); + if (pet && !pet->IsFamiliar() && !pet->SpecAttacks[IMMUNE_AGGRO] && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse()) + { + if (!pet->IsHeld()) { + mlog(PETS__AGGRO, "Sending pet %s into battle due to attack.", pet->GetName()); + pet->AddToHateList(attacker, 1); + pet->SetTarget(attacker); + Message_StringID(10, PET_ATTACKING, pet->GetCleanName(), attacker->GetCleanName()); + } + } + + //see if any runes want to reduce this damage + if(spell_id == SPELL_UNKNOWN) { + damage = ReduceDamage(damage); + mlog(COMBAT__HITS, "Melee Damage reduced to %d", damage); + } else { + int32 origdmg = damage; + damage = AffectMagicalDamage(damage, spell_id, iBuffTic, attacker); + if (origdmg != damage && attacker && attacker->IsClient()) { + if(attacker->CastToClient()->GetFilter(FILTER_DAMAGESHIELD) != FilterHide) + attacker->Message(15, "The Spellshield absorbed %d of %d points of damage", origdmg - damage, origdmg); + } + if (damage == 0 && attacker && origdmg != damage && IsClient()) { + //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()); + } + + } + + + if(IsClient() && CastToClient()->sneaking){ + CastToClient()->sneaking = false; + SendAppearancePacket(AT_Sneak, 0); + } + if(attacker && attacker->IsClient() && attacker->CastToClient()->sneaking){ + attacker->CastToClient()->sneaking = false; + attacker->SendAppearancePacket(AT_Sneak, 0); + } + //final damage has been determined. + + /* + //check for death conditions + if(IsClient()) { + if((GetHP()) <= -10) { + Death(attacker, damage, spell_id, skill_used); + return; + } + } else { + if (damage >= GetHP()) { + //killed... + SetHP(-100); + Death(attacker, damage, spell_id, skill_used); + return; + } + } + */ + + SetHP(GetHP() - damage); + + if(HasDied()) { + bool IsSaved = false; + + if(TryDivineSave()) + IsSaved = true; + + if(!IsSaved && !TrySpellOnDeath()) { + SetHP(-500); + + if(attacker && attacker->IsClient() && (spell_id != SPELL_UNKNOWN) && damage>0) { + char val1[20]={0}; + entity_list.MessageClose_StringID(this, false, 100, MT_NonMelee, HIT_NON_MELEE, attacker->GetCleanName(), GetCleanName(),ConvertArray(damage,val1)); + } + + Death(attacker, damage, spell_id, skill_used); + return; + } + } + + else{ + if(GetHPRatio() < 16) + TryDeathSave(); + } + + //fade mez if we are mezzed + if (IsMezzed()) { + mlog(COMBAT__HITS, "Breaking mez due to attack."); + BuffFadeByEffect(SE_Mez); + } + + //check stun chances if bashing + if (damage > 0 && ((skill_used == BASH || skill_used == KICK) && attacker)) + { + // NPCs can stun with their bash/kick as soon as they recieve it. + // Clients can stun mobs under level 56 with their bash/kick when they get level 55 or greater. + if((attacker->IsNPC()) || (attacker->IsClient() && attacker->GetLevel() >= 55 && GetLevel() < 56)) + { + if (MakeRandomInt(0,99) < (RuleI(Character, NPCBashKickStunChance)) || attacker->IsClient()) + { + int stun_resist = itembonuses.StunResist+spellbonuses.StunResist; + int frontal_stun_resist = itembonuses.FrontalStunResist+spellbonuses.FrontalStunResist; + + if(IsClient()){ + stun_resist += aabonuses.StunResist; + frontal_stun_resist += aabonuses.FrontalStunResist; + } + + if( (GetBaseRace() == OGRE && IsClient() || + (frontal_stun_resist && ((frontal_stun_resist >= 100) || (MakeRandomInt(0,100) <= frontal_stun_resist)))) + && !attacker->BehindMob(this, attacker->GetX(), attacker->GetY())) + { + mlog(COMBAT__HITS, "Stun Resisted. Ogres are immune to frontal melee stuns."); + } + else + { + if(stun_resist <= 0 || MakeRandomInt(0,99) >= stun_resist) + { + mlog(COMBAT__HITS, "Stunned. We had %d percent resist chance."); + Stun(0); + } + else + { + if(IsClient()) + Message_StringID(MT_Stun, SHAKE_OFF_STUN); + + mlog(COMBAT__HITS, "Stun Resisted. We had %dpercent resist chance."); + } + } + } + } + } + + if(spell_id != SPELL_UNKNOWN && !iBuffTic) { + //see if root will break + if (IsRooted() && !FromDamageShield) { // neotoyko: only spells cancel root + + /*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443 + The Viscid Roots AA does the following: Reduces the chance for root to break by X percent. + There is no distinction of any kind between the caster inflicted damage, or anyone + else's damage. There is also no distinction between Direct and DOT damage in the root code. + There is however, a provision that if the damage inflicted is greater than 500 per hit, the + chance to break root is increased. My guess is when this code was put in place, the devs at + the time couldn't imagine DOT damage getting that high. + */ + int BreakChance = RuleI(Spells, RootBreakFromSpells); + BreakChance -= BreakChance*rooted_mod/100; + + if (BreakChance < 1) + BreakChance = 1; + + if (MakeRandomInt(0, 99) < BreakChance) { + mlog(COMBAT__HITS, "Spell broke root! BreakChance percent chance"); + BuffFadeByEffect(SE_Root, buffslot); // buff slot is passed through so a root w/ dam doesnt cancel itself + } else { + mlog(COMBAT__HITS, "Spell did not break root. BreakChance percent chance"); + } + } + } + else if(spell_id == SPELL_UNKNOWN) + { + //increment chances of interrupting + if(IsCasting()) { //shouldnt interrupt on regular spell damage + attacked_count++; + mlog(COMBAT__HITS, "Melee attack while casting. Attack count %d", attacked_count); + } + } + + //send an HP update if we are hurt + if(GetHP() < GetMaxHP()) + SendHPUpdate(); + } //end `if damage was done` + + //send damage packet... + if(!iBuffTic) { //buff ticks do not send damage, instead they just call SendHPUpdate(), which is done below + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); + CombatDamage_Struct* a = (CombatDamage_Struct*)outapp->pBuffer; + a->target = GetID(); + if (attacker == NULL) + a->source = 0; + else if (attacker->IsClient() && attacker->CastToClient()->GMHideMe()) + a->source = 0; + else + a->source = attacker->GetID(); + a->type = SkillDamageTypes[skill_used]; // was 0x1c + a->damage = damage; +// if (attack_skill != 231) +// a->spellid = SPELL_UNKNOWN; +// else + a->spellid = spell_id; + + //Note: if players can become pets, they will not receive damage messages of their own + //this was done to simplify the code here (since we can only effectively skip one mob on queue) + eqFilterType filter; + Mob *skip = attacker; + if(attacker && attacker->GetOwnerID()) { + //attacker is a pet, let pet owners see their pet's damage + Mob* owner = attacker->GetOwner(); + if (owner && owner->IsClient()) { + if (((spell_id != SPELL_UNKNOWN) || (FromDamageShield)) && damage>0) { + //special crap for spell damage, looks hackish to me + char val1[20]={0}; + owner->Message_StringID(MT_NonMelee,OTHER_HIT_NONMELEE,GetCleanName(),ConvertArray(damage,val1)); + } else { + if(damage > 0) { + if(spell_id != SPELL_UNKNOWN) + filter = iBuffTic ? FilterDOT : FilterSpellDamage; + else + filter = FILTER_MYPETHITS; + } else if(damage == -5) + filter = FilterNone; //cant filter invulnerable + else + filter = FILTER_MYPETMISSES; + + if(!FromDamageShield) + owner->CastToClient()->QueuePacket(outapp,true,CLIENT_CONNECTED,filter); + } + } + skip = owner; + } else { + //attacker is not a pet, send to the attacker + + //if the attacker is a client, try them with the correct filter + if(attacker && attacker->IsClient()) { + if (((spell_id != SPELL_UNKNOWN)||(FromDamageShield)) && damage>0) { + //special crap for spell damage, looks hackish to me + char val1[20]={0}; + if (FromDamageShield) + { + if(!attacker->CastToClient()->GetFilter(FilterDamageShields) == FilterHide) + { + attacker->Message_StringID(MT_DS,OTHER_HIT_NONMELEE,GetCleanName(),ConvertArray(damage,val1)); + } + } + else + entity_list.MessageClose_StringID(this, true, 100, MT_NonMelee,HIT_NON_MELEE,attacker->GetCleanName(),GetCleanName(),ConvertArray(damage,val1)); + } else { + if(damage > 0) { + if(spell_id != SPELL_UNKNOWN) + filter = iBuffTic ? FilterDOT : FilterSpellDamage; + else + filter = FilterNone; //cant filter our own hits + } else if(damage == -5) + filter = FilterNone; //cant filter invulnerable + else + filter = FILTER_MYMISSES; + + attacker->CastToClient()->QueuePacket(outapp, true, CLIENT_CONNECTED, filter); + } + } + skip = attacker; + } + + //send damage to all clients around except the specified skip mob (attacker or the attacker's owner) and ourself + if(damage > 0) { + if(spell_id != SPELL_UNKNOWN) + filter = iBuffTic ? FilterDOT : FilterSpellDamage; + else + filter = FILTER_OTHERHITS; + } else if(damage == -5) + filter = FilterNone; //cant filter invulnerable + else + filter = FILTER_OTHERMISSES; + //make attacker (the attacker) send the packet so we can skip them and the owner + //this call will send the packet to `this` as well (using the wrong filter) (will not happen until PC charm works) +//LogFile->write(EQEMuLog::Debug, "Queue damage to all except %s with filter %d (%d), type %d", skip->GetName(), filter, IsClient()?CastToClient()->GetFilter(filter):-1, a->type); + // + // If this is Damage Shield damage, the correct OP_Damage packets will be sent from Mob::DamageShield, so + // we don't send them here. + if(!FromDamageShield) { + entity_list.QueueCloseClients(this, outapp, true, 200, skip, true, filter); + //send the damage to ourself if we are a client + if(IsClient()) { + //I dont think any filters apply to damage affecting us + CastToClient()->QueuePacket(outapp); + } + } + + safe_delete(outapp); + } else { + //else, it is a buff tic... + // Everhood - 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 + if(attacker->CastToClient()->GetFilter(FilterDOT) != FilterHide) { + attacker->Message_StringID(MT_DoTDamage, OTHER_HIT_DOT, GetCleanName(),itoa(damage),spells[spell_id].name); + } + } + } //end packet sending +} + + +void Mob::HealDamage(uint32 amount, Mob* caster) { + uint32 maxhp = GetMaxHP(); + uint32 curhp = GetHP(); + uint32 acthealed = 0; + + if(caster && amount > 0) + { + if(caster->IsNPC() && !caster->IsPet()) + { + float npchealscale = caster->CastToNPC()->GetHealScale(); + amount = ((float)amount * npchealscale) / (float)100; + } + } + + + if(amount > (maxhp - curhp)) + acthealed = (maxhp - curhp); + else + acthealed = amount; + + char *TempString = NULL; + + MakeAnyLenString(&TempString, "%d", acthealed); + + if(acthealed > 100){ + if(caster){ + Message_StringID(MT_NonMelee, YOU_HEALED, caster->GetCleanName(), TempString); + if(caster != this){ + caster->Message_StringID(MT_NonMelee, YOU_HEAL, GetCleanName(), TempString); + } + } + else{ + Message(MT_NonMelee, "You have been healed for %d points of damage.", acthealed); + } + } + + if (curhp < maxhp) { + if ((curhp+amount)>maxhp) + curhp=maxhp; + else + curhp+=amount; + SetHP(curhp); + + SendHPUpdate(); + } + safe_delete_array(TempString); +} + + + +//proc chance includes proc bonus +float Mob::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) { + int mydex = GetDEX(); + float AABonus = 0; + ProcBonus = 0; + ProcChance = 0; + + if (aabonuses.ProcChance) + AABonus = float(aabonuses.ProcChance) / 100.0f; + + switch(hand){ + case 13: + weapon_speed = attack_timer.GetDuration(); + break; + case 14: + weapon_speed = attack_dw_timer.GetDuration(); + break; + case 11: + weapon_speed = ranged_timer.GetDuration(); + break; + } + + + //calculate the weapon speed in ms, so we can use the rule to compare against. + + if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance + weapon_speed = RuleI(Combat, MinHastedDelay); + + ProcBonus += (float(itembonuses.ProcChance + spellbonuses.ProcChance) / 1000.0f + AABonus); + + if(RuleB(Combat, AdjustProcPerMinute) == true) + { + ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms + ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib) / 100.0f; + ProcChance = ProcChance + (ProcChance * ProcBonus); + } + else + { + ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy); + ProcChance = ProcChance + (ProcChance * ProcBonus); + } + + mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); + return ProcChance; +} + +float Mob::GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) { + int myagi = GetAGI(); + ProcBonus = 0; + ProcChance = 0; + + switch(hand){ + case 13: + weapon_speed = attack_timer.GetDuration(); + break; + case 14: + weapon_speed = attack_dw_timer.GetDuration(); + break; + case 11: + return 0; + break; + } + + /* + 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; + */ + + //calculate the weapon speed in ms, so we can use the rule to compare against. + //weapon_speed = ((int)(weapon_speed*(100.0f+attack_speed)*PermaHaste)); + if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance + weapon_speed = RuleI(Combat, MinHastedDelay); + + ProcChance = ((float)weapon_speed * RuleR(Combat, AvgDefProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms + ProcBonus += float(myagi) * RuleR(Combat, DefProcPerMinAgiContrib) / 100.0f; + ProcChance = ProcChance + (ProcChance * ProcBonus); + + mlog(COMBAT__PROCS, "Defensive Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); + return ProcChance; +} + +void Mob::TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand, int damage) { + + if (!on) { + SetTarget(NULL); + LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Mob::TryDefensiveProc for evaluation!"); + return; + } + + bool bSkillProc = HasSkillProcs(); + bool bDefensiveProc = HasDefensiveProcs(); + + if (!bDefensiveProc && !bSkillProc) + return; + + if (!bDefensiveProc && (bSkillProc && damage >= 0)) + return; + + float ProcChance, ProcBonus; + if(weapon!=NULL) + on->GetDefensiveProcChances(ProcBonus, ProcChance, weapon->GetItem()->Delay, hand); + else + on->GetDefensiveProcChances(ProcBonus, ProcChance); + if(hand != 13) + ProcChance /= 2; + + if (bDefensiveProc){ + for (int i = 0; i < MAX_PROCS; i++) { + if (DefensiveProcs[i].spellID != SPELL_UNKNOWN) { + int chance = ProcChance * (DefensiveProcs[i].chance); + if ((MakeRandomInt(0, 100) < chance)) { + ExecWeaponProc(DefensiveProcs[i].spellID, on); + CheckHitsRemaining(0, false, false, 0, DefensiveProcs[i].base_spellID); + } + } + } + } + + if (bSkillProc && damage < 0){ + + if (damage == -1) + TrySkillProc(on, BLOCKSKILL, ProcChance); + + if (damage == -2) + TrySkillProc(on, PARRY, ProcChance); + + if (damage == -3) + TrySkillProc(on, RIPOSTE, ProcChance); + + if (damage == -4) + TrySkillProc(on, DODGE, ProcChance); + } +} + +void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) { + _ZP(Mob_TryWeaponProcA); + if(!on) { + SetTarget(NULL); + LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Mob::TryWeaponProc for evaluation!"); + return; + } + + if(!weapon_g) { + TryWeaponProc((const Item_Struct*) NULL, on, hand); + return; + } + + if(!weapon_g->IsType(ItemClassCommon)) { + TryWeaponProc((const Item_Struct*) NULL, on, hand); + return; + } + + //do main procs + TryWeaponProc(weapon_g->GetItem(), on, hand); + + + //we have to calculate these again, oh well + int ourlevel = GetLevel(); + float ProcChance, ProcBonus; + GetProcChances(ProcBonus, ProcChance, weapon_g->GetItem()->Delay, hand); + if(hand != 13) + { + ProcChance /= 2; + } + + //do augment procs + int r; + for(r = 0; r < MAX_AUGMENT_SLOTS; r++) { + const ItemInst* aug_i = weapon_g->GetAugment(r); + if(!aug_i) + continue; + const Item_Struct* aug = aug_i->GetItem(); + if(!aug) + continue; + + if (aug->Proc.Type == ET_CombatProc) { + ProcChance = ProcChance*(100+aug->ProcRate)/100; + if (MakeRandomFloat(0, 1) < ProcChance) { + if(aug->Proc.Level > ourlevel) { + Mob * own = GetOwner(); + if(own != NULL) { + own->Message_StringID(13,PROC_PETTOOLOW); + } else { + Message_StringID(13,PROC_TOOLOW); + } + } else { + ExecWeaponProc(aug->Proc.Effect, on); + } + } + } + } +} + +void Mob::TryWeaponProc(const Item_Struct* weapon, Mob *on, uint16 hand) { + _ZP(Mob_TryWeaponProcB); + uint16 skillinuse = 28; + int ourlevel = GetLevel(); + float ProcChance, ProcBonus; + if(weapon!=NULL) + GetProcChances(ProcBonus, ProcChance, weapon->Delay, hand); + else + GetProcChances(ProcBonus, ProcChance); + + if(hand != 13) //Is Archery intended to proc at 50% rate? + ProcChance /= 2; + + //give weapon a chance to proc first. + if(weapon != NULL) { + skillinuse = GetSkillByItemType(weapon->ItemType); + if (weapon->Proc.Type == ET_CombatProc) { + float WPC = ProcChance*(100.0f+(float)weapon->ProcRate)/100.0f; + if (MakeRandomFloat(0, 1) <= WPC) { // 255 dex = 0.084 chance of proc. No idea what this number should be really. + if(weapon->Proc.Level > ourlevel) { + mlog(COMBAT__PROCS, "Tried to proc (%s), but our level (%d) is lower than required (%d)", weapon->Name, ourlevel, weapon->Proc.Level); + Mob * own = GetOwner(); + if(own != NULL) { + own->Message_StringID(13,PROC_PETTOOLOW); + } else { + Message_StringID(13,PROC_TOOLOW); + } + } else { + mlog(COMBAT__PROCS, "Attacking weapon (%s) successfully procing spell %d (%.2f percent chance)", weapon->Name, weapon->Proc.Effect, ProcChance*100); + ExecWeaponProc(weapon->Proc.Effect, on); + } + } else { + mlog(COMBAT__PROCS, "Attacking weapon (%s) did no proc (%.2f percent chance).", weapon->Name, ProcChance*100); + } + } + } + + if(ProcBonus == -1) { + LogFile->write(EQEMuLog::Error, "ProcBonus was -1 value!"); + return; + } + + bool bRangedAttack = false; + if (weapon != NULL) { + if (weapon->ItemType == ItemTypeBow || weapon->ItemType == ItemTypeThrowing || weapon->ItemType == ItemTypeThrowingv2) { + bRangedAttack = true; + } + } + + bool isRanged = false; + if(weapon) + { + if(weapon->ItemType == ItemTypeArrow || + weapon->ItemType == ItemTypeThrowing || + weapon->ItemType == ItemTypeThrowingv2 || + weapon->ItemType == ItemTypeBow) + { + isRanged = true; + } + } + + uint32 i; + for(i = 0; i < MAX_PROCS; i++) { + if (PermaProcs[i].spellID != SPELL_UNKNOWN) { + if(MakeRandomInt(0, 100) < PermaProcs[i].chance) { + mlog(COMBAT__PROCS, "Permanent proc %d procing spell %d (%d percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance); + ExecWeaponProc(PermaProcs[i].spellID, on); + } else { + mlog(COMBAT__PROCS, "Permanent proc %d failed to proc %d (%d percent chance)", i, PermaProcs[i].spellID, PermaProcs[i].chance); + } + } + + if(!isRanged) + { + if(IsPet() && hand != 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets) + { + //Maybe implement this later if pets are ever given dual procs? + } + else + { + int chance = ProcChance * (SpellProcs[i].chance); + if(MakeRandomInt(0, 100) < chance) { + mlog(COMBAT__PROCS, "Spell proc %d procing spell %d (%d percent chance)", i, SpellProcs[i].spellID, chance); + ExecWeaponProc(SpellProcs[i].spellID, on); + } else { + mlog(COMBAT__PROCS, "Spell proc %d failed to proc %d (%d percent chance)", i, SpellProcs[i].spellID, chance); + } + } + } + if (bRangedAttack) { + int chance = ProcChance * RangedProcs[i].chance; + if(MakeRandomInt(0, 100) < chance) { + mlog(COMBAT__PROCS, "Ranged proc %d procing spell %d", i, RangedProcs[i].spellID, RangedProcs[i].chance); + ExecWeaponProc(RangedProcs[i].spellID, on); + CheckHitsRemaining(0, false, false, 0, RangedProcs[i].base_spellID); + } else { + mlog(COMBAT__PROCS, "Ranged proc %d failed to proc %d", i, RangedProcs[i].spellID, RangedProcs[i].chance); + } + } + } + + if (HasSkillProcs()) + TrySkillProc(on, skillinuse, ProcChance); +} + +void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage) +{ + if(damage < 1) + return; + + //Allows pets to perform critical hits. + //Each rank adds an additional 1% chance for any melee hit (primary, secondary, kick, bash, etc) to critical, + //dealing up to 63% more damage. http://www.magecompendium.com/aa-short-library.html + + Mob *owner = NULL; + float critChance = 0.0f; + critChance += RuleI(Combat, MeleeBaseCritChance); + uint16 critMod = 163; + + if (damage < 1) //We can't critical hit if we don't hit. + return; + + if (!IsPet()) + return; + + owner = GetOwner(); + + if (!owner) + return; + + int16 CritPetChance = owner->aabonuses.PetCriticalHit + owner->itembonuses.PetCriticalHit + owner->spellbonuses.PetCriticalHit; + int16 CritChanceBonus = GetCriticalChanceBonus(skill); + + if (CritPetChance || critChance) { + + //For pets use PetCriticalHit for base chance, pets do not innately critical with without it + //even if buffed with a CritChanceBonus effects. + critChance += CritPetChance; + critChance += critChance*CritChanceBonus/100.0f; + } + + if(critChance > 0){ + + critChance /= 100; + + if(MakeRandomFloat(0, 1) < critChance) + { + critMod += GetCritDmgMob(skill) * 2; // To account for base crit mod being 200 not 100 + damage = (damage * critMod) / 100; + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRITICAL_HIT, GetCleanName(), itoa(damage)); + } + } +} + +void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage) +{ + if(damage < 1) + return; + + // decided to branch this into it's own function since it's going to be duplicating a lot of the + // code in here, but could lead to some confusion otherwise + if (IsPet() && GetOwner()->IsClient()) { + TryPetCriticalHit(defender,skill,damage); + return; + } + +#ifdef BOTS + if (this->IsPet() && this->GetOwner()->IsBot()) { + this->TryPetCriticalHit(defender,skill,damage); + return; + } +#endif //BOTS + + + float critChance = 0.0f; + + //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 (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); + return; + } + } + } + + //2: Try Melee Critical + + //Base critical rate for all classes is dervived from DEX stat, this rate is then augmented + //by item,spell and AA bonuses allowing you a chance to critical hit. If the following rules + //are defined you will have an innate chance to hit at Level 1 regardless of bonuses. + //Warning: Do not define these rules if you want live like critical hits. + critChance += RuleI(Combat, MeleeBaseCritChance); + + if(IsClient()) + critChance += RuleI(Combat, ClientBaseCritChance); + + bool IsBerserk = false; + if(((GetClass() == WARRIOR || GetClass() == BERSERKER) && GetLevel() >= 12 && IsClient())) + { + if(CastToClient()->berserk){ + critChance += RuleI(Combat, BerserkBaseCritChance); + IsBerserk = true; + } + + else + critChance += RuleI(Combat, WarBerBaseCritChance); + } + + if(skill == ARCHERY && GetClass() == RANGER && GetSkill(ARCHERY) >= 65) + critChance += 6; + + if(skill == THROWING && GetClass() == ROGUE && GetSkill(THROWING) >= 65) + critChance += 6; + + int CritChanceBonus = GetCriticalChanceBonus(skill); + + if (CritChanceBonus || critChance) { + + //Get Base CritChance from Dex. (200 = ~1.6%, 255 = ~2.0%, 355 = ~2.20%) Fall off rate > 255 + //http://giline.versus.jp/shiden/su.htm , http://giline.versus.jp/shiden/damage_e.htm + if (GetDEX() <= 255) + critChance += (float(GetDEX()) / 125.0f); + else if (GetDEX() > 255) + critChance += (float(GetDEX()-255)/ 500.0f) + 2.0f; + critChance += critChance*(float)CritChanceBonus /100.0f; + } + + if(critChance > 0){ + + critChance /= 100; + + if(MakeRandomFloat(0, 1) < critChance) + { + uint16 critMod = 200; + bool crip_success = false; + int16 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 + //have a 200% Chance to Critical Blow effect, therefore you have a 20% Chance to Critical Blow. + if (CripplingBlowChance){ + critChance *= float(CripplingBlowChance)/100.0f; + + if(MakeRandomFloat(0, 1) < critChance){ + critMod = 400; + crip_success = true; + } + } + + critMod += GetCritDmgMob(skill) * 2; // To account for base crit mod being 200 not 100 + damage = damage * critMod / 100; + + if(IsBerserk || crip_success) + { + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRIPPLING_BLOW, GetCleanName(), itoa(damage)); + // Crippling blows also have a chance to stun + //Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a staggers message. + if (defender->GetLevel() <= 55 && !defender->SpecAttacks[IMMUNE_STUN]){ + defender->Emote("staggers."); + defender->Stun(0); + } + } + + else + { + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRITICAL_HIT, GetCleanName(), itoa(damage)); + } + } + } +} + + +bool Mob::TryFinishingBlow(Mob *defender, SkillType skillinuse) +{ + + if (!defender) + return false; + + if (aabonuses.FinishingBlow[1] && !defender->IsClient() && defender->GetHPRatio() < 10){ + + uint32 chance = aabonuses.FinishingBlow[0]/10; //500 = 5% chance. + uint32 damage = aabonuses.FinishingBlow[1]; + uint16 levelreq = aabonuses.FinishingBlowLvl[0]; + + if(defender->GetLevel() <= levelreq && (chance >= MakeRandomInt(0, 1000))){ + mlog(COMBAT__ATTACKS, "Landed a finishing blow: levelreq at %d, other level %d", levelreq , defender->GetLevel()); + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FINISHING_BLOW, GetName()); + defender->Damage(this, damage, SPELL_UNKNOWN, skillinuse); + return true; + } + else + { + mlog(COMBAT__ATTACKS, "FAILED a finishing blow: levelreq at %d, other level %d", levelreq , defender->GetLevel()); + return false; + } + } + return false; +} + +void Mob::DoRiposte(Mob* defender) { + mlog(COMBAT__ATTACKS, "Preforming a riposte"); + + if (!defender) + return; + + defender->Attack(this, SLOT_PRIMARY, true); + if (HasDied()) return; + + int16 DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[0] + + defender->spellbonuses.GiveDoubleRiposte[0] + + defender->itembonuses.GiveDoubleRiposte[0]; + + //Live AA - Double Riposte + if(DoubleRipChance && (DoubleRipChance >= MakeRandomInt(0, 100))) { + mlog(COMBAT__ATTACKS, "Preforming a double riposed (%d percent chance)", DoubleRipChance); + defender->Attack(this, SLOT_PRIMARY, true); + if (HasDied()) return; + } + + //Double Riposte effect, allows for a chance to do RIPOSTE with a skill specfic special attack (ie Return Kick). + //Coded narrowly: Limit to one per client. Limit AA only. [1 = Skill Attack Chance, 2 = Skill] + DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[1]; + + if(DoubleRipChance && (DoubleRipChance >= MakeRandomInt(0, 100))) { + mlog(COMBAT__ATTACKS, "Preforming a return SPECIAL ATTACK (%d percent chance)", DoubleRipChance); + + if (defender->GetClass() == MONK) + defender->MonkSpecialAttack(this, defender->aabonuses.GiveDoubleRiposte[2]); + else if (defender->IsClient()) + defender->CastToClient()->DoClassAttacks(this,defender->aabonuses.GiveDoubleRiposte[2], true); + } +} + +void Mob::ApplyMeleeDamageBonus(uint16 skill, int32 &damage){ + + if(!RuleB(Combat, UseIntervalAC)){ + if(IsNPC()){ //across the board NPC damage bonuses. + //only account for STR here, assume their base STR was factored into their DB damages + int dmgbonusmod = 0; + dmgbonusmod += (100*(itembonuses.STR + spellbonuses.STR))/3; + dmgbonusmod += (100*(spellbonuses.ATK + itembonuses.ATK))/5; + mlog(COMBAT__DAMAGE, "Damage bonus: %d percent from ATK and STR bonuses.", (dmgbonusmod/100)); + damage += (damage*dmgbonusmod/10000); + } + } + + damage += damage * GetMeleeDamageMod_SE(skill) / 100; + + //Rogue sneak attack disciplines make use of this, they are active for one hit + if (spellbonuses.HitChanceEffect[HIGHEST_SKILL+1] || spellbonuses.HitChanceEffect[skill]) + CheckHitsRemaining(0, false, false, SE_HitChance,0,true,skill); +} + +bool Mob::HasDied() { + bool Result = false; + int16 hp_below = 0; + + hp_below = (GetDelayDeath() * -1); + + if((GetHP()) <= (hp_below)) + Result = true; + + return Result; +} + +uint16 Mob::GetDamageTable(SkillType skillinuse) +{ + if(GetLevel() <= 51) + { + uint16 ret_table = 0; + int str_over_75 = 0; + if(GetSTR() > 75) + str_over_75 = GetSTR() - 75; + if(str_over_75 > 255) + ret_table = (GetSkill(skillinuse)+255)/2; + else + ret_table = (GetSkill(skillinuse)+str_over_75)/2; + + if(ret_table < 100) + return 100; + + return ret_table; + } + else if(GetLevel() >= 90) + { + if(GetClass() == MONK) + return 379; + else + return 345; + } + else + { + uint16 dmg_table[] = { + 275, 275, 275, 275, 275, + 280, 280, 280, 280, 285, + 285, 285, 290, 290, 295, + 295, 300, 300, 300, 305, + 305, 305, 310, 310, 315, + 315, 320, 320, 320, 325, + 325, 325, 330, 330, 335, + 335, 340, 340, 340, + }; + if(GetClass() == MONK) + return (dmg_table[GetLevel()-51]*(100+RuleI(Combat,MonkDamageTableBonus))/100); + else + return dmg_table[GetLevel()-51]; + } +} + +void Mob::TrySkillProc(Mob *on, uint16 skill, float chance) +{ + + if (!on) { + SetTarget(NULL); + LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Mob::TrySkillProc for evaluation!"); + return; + } + + for (int i = 0; i < MAX_PROCS; i++) { + if (SkillProcs[i].spellID != SPELL_UNKNOWN){ + if (PassLimitToSkill(SkillProcs[i].base_spellID,skill)){ + int ProcChance = chance * (float)SkillProcs[i].chance; + if ((MakeRandomInt(0, 100) < ProcChance)) { + ExecWeaponProc(SkillProcs[i].spellID, on); + CheckHitsRemaining(0, false, false, 0, SkillProcs[i].base_spellID); + } + } + } + } +} + +int32 Mob::RuneAbsorb(int32 damage, uint16 type) +{ + uint32 buff_max = GetMaxTotalSlots(); + if (type == SE_Rune){ + for(uint32 slot = 0; slot < buff_max; slot++) { + if((buffs[slot].spellid != SPELL_UNKNOWN) && (buffs[slot].melee_rune) && IsEffectInSpell(buffs[slot].spellid, type)){ + uint32 melee_rune_left = buffs[slot].melee_rune; + if(melee_rune_left >= damage) + { + melee_rune_left -= damage; + buffs[slot].melee_rune = melee_rune_left; + return -6; + } + + else + { + if(melee_rune_left > 0) + damage -= melee_rune_left; + if(!TryFadeEffect(slot)) + BuffFadeBySlot(slot); + UpdateRuneFlags(); + continue; + } + } + } + return damage; + } + + + else{ + for(uint32 slot = 0; slot < buff_max; slot++) { + if((buffs[slot].spellid != SPELL_UNKNOWN) && (buffs[slot].magic_rune) && IsEffectInSpell(buffs[slot].spellid, type)){ + uint32 magic_rune_left = buffs[slot].magic_rune; + if(magic_rune_left >= damage) + { + magic_rune_left -= damage; + buffs[slot].magic_rune = magic_rune_left; + return 0; + } + + else + { + if(magic_rune_left > 0) + damage -= magic_rune_left; + if(!TryFadeEffect(slot)) + BuffFadeBySlot(slot); + UpdateRuneFlags(); + continue; + } + } + } + return damage; + } +} \ No newline at end of file diff --git a/zone/basic_functions.h b/zone/basic_functions.h new file mode 100644 index 000000000..8185b612e --- /dev/null +++ b/zone/basic_functions.h @@ -0,0 +1,178 @@ +const char *notin = "~.? ,)=!&|<>\""; + +const char * charIn = "`~1234567890-=!@#$%^&*()_+qwertyuiop[]asdfghjkl;'zxcvbnm,./QWERTYUIOP|ASDFGHJKL:ZXCVBNM<>?\"{}"; +const char * charIn2 = "`~1234567890-=!@#$%^&*()_+qwertyuiop[]asdfghjkl;'zxcvbnm,./QWERTYUIOP|ASDFGHJKL:ZXCVBNM<>?\" "; + +char com_list[512]; + +/*char * gettok(const char * string, int chara, int pos) { + static char * pch; + static char temp[100]; + static char ty[100]; + memset(temp, 0, sizeof(temp)); + memset(ty, 0, sizeof(ty)); + ty[0] = chara; + strn0cpy(temp, string, sizeof(temp)); + pch = strtok(temp,ty); + for (int i=0; i < pos; i++) { + if (pos == i) break; + pch = strtok(NULL,ty); + } + return pch; +}*/ + + +int calc(char * stuff) { + int result = 0; + int i = 0; + int a = 0; + static char temp[100]; + int op = 0; + memset(temp,0x0,100); + //char heh[100]; + while(*stuff) + { + a++; + + if (*stuff >= '0' && *stuff <= '9') { + if (op) { + switch ( op ) { + case 0: + break; + case '+': + result += atoi(stuff); + memset(temp,0x0,100); + i = 0; + break; + case '-': + result -= atoi(stuff); + memset(temp,0x0,100); + i = 0; + break; + case '*': + result *= atoi(stuff); + memset(temp,0x0,100); + i = 0; + break; + case '/': + result /= atoi(stuff); + memset(temp,0x0,100); + i = 0; + break; + } + } + temp[i] = *stuff; + } + else if (*stuff == '+' || *stuff == '-' || *stuff == '/' || *stuff == '*') { + op = *stuff; + if (!result) result = atoi(temp); + memset(temp,0x0,100); + i=0; + } + i++; + stuff++; + } + return result; +} + +char * postring (char * string, int pos) { + static char temp[1024]; + memset(temp, 0, sizeof(temp)); + int p=0; + int tmpStringLen = strlen(string); + for (int i=pos; i < tmpStringLen; i++) { + temp[p] = string[i]; + p++; + } + return temp; +} + +int g_id ( char * string ) +{ + char temp[100]; + int i=0; + while (*string) + { + if (*string >= '0' && *string <= '9') temp[i++] = *string++; + else string++; + } + return atoi(temp); +} + +int GetArgs(char * string) +{ +#if 1 + char temp[255]; + char c_name[255]; + int params = 0; + //char *buffer = new char[512]; // this is never deleted, causes zone to crash + char *buffer = com_list; + memset(temp,0x0,255); + //memset(buffer,0x0,512); + //#ifdef WIN32 + //strcpy(buffer,com_list); + //#else + //strncpy(buffer,com_list,sizeof(buffer)-1); + //#endif + int i=0; + while (*buffer) + { + if (*buffer != '|' && *buffer != ' ') temp[i++] = *buffer++; + else buffer++; + if (*buffer == '|') { + params = atoi(temp); + if (!strcmp(c_name,string)) { + return params; + } + memset(temp,0x0,255); + i=0; + } + if (*buffer == ' ') { + memset(c_name,0x0,255); + strcpy(c_name,temp); + memset(temp,0x0,255); + i=0; + } + } + return 0; +#else + if (strstr(string,"if")) { return 0; } + if (strstr(string,"sfollow")) { return 0; } + if (strstr(string,"save")) { return 0; } + if (strstr(string,"while")) { return 0; } + if (strstr(string,"break")) { return 1; } + if (strstr(string,"follow")) { return 1; } + if (strstr(string,"setallskill")) { return 1; } + if (strstr(string,"me")) { return 1; } + if (strstr(string,"flagcheck")) { return 1; } + if (strstr(string,"echo")) { return 1; } + if (strstr(string,"summonitem")) { return 1; } + if (strstr(string,"say")) { return 1; } + if (strstr(string,"emote")) { return 1; } + if (strstr(string,"shout")) { return 1; } + if (strstr(string,"depop")) { return 1; } + if (strstr(string,"cumflag")) { return 1; } + if (strstr(string,"exp")) { return 1; } + if (strstr(string,"level")) { return 1; } + if (strstr(string,"safemove")) { return 1; } + if (strstr(string,"rain")) { return 1; } + if (strstr(string,"snow")) { return 1; } + if (strstr(string,"pvp")) { return 1; } + if (strstr(string,"doanim")) { return 1; } + if (strstr(string,"dbspawnadd")) { return 2; } + if (strstr(string,"castspell")) { return 2; } + if (strstr(string,"flagcheck")) { return 2; } + if (strstr(string,"addskill")) { return 2; } + if (strstr(string,"write")) { return 2; } + if (strstr(string,"settarget")) { return 2; } + if (strstr(string,"givecash")) { return 4; } + if (strstr(string,"movepc")) { return 4; } + if (strstr(string,"spawn")) { return 6; } + return 0; +#endif // 0 +} + +Client* gClient = 0; + +uint32 Line_Number = 0; + diff --git a/zone/beacon.cpp b/zone/beacon.cpp new file mode 100644 index 000000000..6825a727a --- /dev/null +++ b/zone/beacon.cpp @@ -0,0 +1,135 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 +*/ + +/* + +solar: Beacon class, extends Mob. Used for AE rain spells to have a mob +target to center around. + +*/ + +#include "../common/debug.h" +#ifdef _WINDOWS +#define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +#include "masterentity.h" +#include "spdat.h" + +extern EntityList entity_list; +extern Zone* zone; + +// solar: if lifetime is 0 this is a permanent beacon.. not sure if that'll be +// useful for anything +Beacon::Beacon(Mob *at_mob, int lifetime) +:Mob +( + NULL, NULL, 0, 0, 0, INVISIBLE_MAN, 0, BT_NoTarget, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +), + remove_timer(lifetime), + spell_timer(0) +{ + remove_timer.Disable(); + spell_timer.Disable(); + remove_me = false; + spell_id = 0xFFFF; + resist_adjust = 0; + spell_iterations = 0; + caster_id = 0; + + // copy location + x_pos = at_mob->GetX(); + y_pos = at_mob->GetY(); + z_pos = at_mob->GetZ(); + heading = at_mob->GetHeading(); + + if(lifetime) + { + remove_timer.Start(); + } +#ifdef SOLAR + entity_list.Message(0, 0, "Beacon being created at %0.2f %0.2f %0.2f heading %0.2f lifetime %d", GetX(), GetY(), GetZ(), GetHeading(), lifetime); +#endif +} + +Beacon::~Beacon() +{ +#ifdef SOLAR + entity_list.Message(0, 0, "Beacon %d being removed at %0.2f %0.2f %0.2f heading %0.2f", GetID(), GetX(), GetY(), GetZ(), GetHeading()); +#endif +} + +bool Beacon::Process() +{ + if(remove_me) + { + return false; + } + + if + ( + spell_timer.Enabled() && + spell_timer.Check() && + IsValidSpell(spell_id) + ) + { + Mob *caster = entity_list.GetMob(caster_id); + if(caster && spell_iterations--) + { + bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()); //NPC AE spells do not affect the NPC caster + entity_list.AESpell(caster, this, spell_id, affect_caster, resist_adjust); + } + else + { + // spell is done casting, or caster disappeared + spell_id = 0xFFFF; + spell_iterations = 0; + spell_timer.Disable(); + caster_id = 0; + } + } + + if(remove_timer.Enabled() && remove_timer.Check()) + { + return false; + } + + return true; +} + +void Beacon::AELocationSpell(Mob *caster, uint16 cast_spell_id, int16 resist_adjust) +{ + if(!IsValidSpell(cast_spell_id) || !caster) + return; + + caster_id = caster->GetID(); + spell_id = cast_spell_id; + this->resist_adjust = resist_adjust; + spell_iterations = spells[spell_id].AEDuration / 2500; + spell_iterations = spell_iterations < 1 ? 1 : spell_iterations; // at least 1 + spell_timer.Start(2500); + spell_timer.Trigger(); +} + + diff --git a/zone/beacon.h b/zone/beacon.h new file mode 100644 index 000000000..db605ebff --- /dev/null +++ b/zone/beacon.h @@ -0,0 +1,60 @@ +/* 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 BEACON_H +#define BEACON_H + +#include "entity.h" +#include "mob.h" +#include "../common/types.h" +#include "../common/timer.h" + +class Beacon : public Mob +{ +public: + Beacon(Mob *at_mob, int lifetime); + ~Beacon(); + + //abstract virtual function implementations requird by base abstract class + virtual void Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillType attack_skill) { return; } + virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false) { return; } + virtual bool Attack(Mob* other, int Hand = 13, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false) { return false; } + virtual bool HasRaid() { return false; } + virtual bool HasGroup() { return false; } + virtual Raid* GetRaid() { return 0; } + virtual Group* GetGroup() { return 0; } + + bool IsBeacon() const { return true; } + bool Process(); + virtual void Depop(bool not_used = true) { remove_me = true; } + void AELocationSpell(Mob *caster, uint16 cast_spell_id, int16 resist_adjust); + +protected: + Timer remove_timer; + bool remove_me; + + uint16 spell_id; + int16 resist_adjust; + int spell_iterations; + Timer spell_timer; + + uint16 caster_id; +private: +}; + +#endif diff --git a/zone/blah.h b/zone/blah.h new file mode 100644 index 000000000..1e0275818 --- /dev/null +++ b/zone/blah.h @@ -0,0 +1,9 @@ +#endif +#ifndef Client #include "client.h" +#endif +#include "Object.h" +#include "groups.h" +#include "doors.h" + + +//This file looks important \ No newline at end of file diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp new file mode 100644 index 000000000..8eb561b0d --- /dev/null +++ b/zone/bonuses.cpp @@ -0,0 +1,3475 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2004 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 "../common/debug.h" +#include "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/rulesys.h" +#include "QuestParserCollection.h" +#include +#include +#ifndef WIN32 +#include +#include "../common/unix.h" +#endif + +#include "StringIDs.h" + +void Mob::CalcBonuses() +{ + CalcSpellBonuses(&spellbonuses); + CalcMaxHP(); + CalcMaxMana(); + SetAttackTimer(); + + rooted = FindType(SE_Root); +} + +void NPC::CalcBonuses() +{ + Mob::CalcBonuses(); + memset(&aabonuses, 0, sizeof(StatBonuses)); + + if(RuleB(NPC, UseItemBonusesForNonPets)){ + memset(&itembonuses, 0, sizeof(StatBonuses)); + CalcItemBonuses(&itembonuses); + } + else{ + if(GetOwner()){ + memset(&itembonuses, 0, sizeof(StatBonuses)); + CalcItemBonuses(&itembonuses); + } + } + + // This has to happen last, so we actually take the item bonuses into account. + Mob::CalcBonuses(); +} + +void Client::CalcBonuses() +{ + _ZP(Client_CalcBonuses); + memset(&itembonuses, 0, sizeof(StatBonuses)); + CalcItemBonuses(&itembonuses); + CalcEdibleBonuses(&itembonuses); + + CalcSpellBonuses(&spellbonuses); + + _log(AA__BONUSES, "Calculating AA Bonuses for %s.", this->GetCleanName()); + CalcAABonuses(&aabonuses); //we're not quite ready for this + _log(AA__BONUSES, "Finished calculating AA Bonuses for %s.", this->GetCleanName()); + + RecalcWeight(); + + CalcAC(); + CalcATK(); + CalcHaste(); + + CalcSTR(); + CalcSTA(); + CalcDEX(); + CalcAGI(); + CalcINT(); + CalcWIS(); + CalcCHA(); + + CalcMR(); + CalcFR(); + CalcDR(); + CalcPR(); + CalcCR(); + CalcCorrup(); + + CalcMaxHP(); + CalcMaxMana(); + CalcMaxEndurance(); + + rooted = FindType(SE_Root); + + XPRate = 100 + spellbonuses.XPRateMod; +} + +int Client::CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat) +{ + if( (reclevel > 0) && (level < reclevel) ) + { + int32 statmod = (level * 10000 / reclevel) * basestat; + + if( statmod < 0 ) + { + statmod -= 5000; + return (statmod/10000); + } + else + { + statmod += 5000; + return (statmod/10000); + } + } + + return 0; +} + +void Client::CalcItemBonuses(StatBonuses* newbon) { + //memset assumed to be done by caller. + + // Clear item faction mods + ClearItemFactionBonuses(); + + unsigned int i; + //should not include 21 (SLOT_AMMO) + for (i=0; i<21; i++) { + const ItemInst* inst = m_inv[i]; + if(inst == 0) + continue; + AddItemBonuses(inst, newbon); + } + + //Power Source Slot + if (GetClientVersion() >= EQClientSoF) + { + const ItemInst* inst = m_inv[9999]; + if(inst) + AddItemBonuses(inst, newbon); + } + + //tribute items + for (i = 0; i < MAX_PLAYER_TRIBUTES; i++) { + const ItemInst* inst = m_inv[TRIBUTE_SLOT_START + i]; + if(inst == 0) + continue; + AddItemBonuses(inst, newbon, false, true); + } + // Caps + if(newbon->HPRegen > CalcHPRegenCap()) + newbon->HPRegen = CalcHPRegenCap(); + + if(newbon->ManaRegen > CalcManaRegenCap()) + newbon->ManaRegen = CalcManaRegenCap(); + + if(newbon->EnduranceRegen > CalcEnduranceRegenCap()) + newbon->EnduranceRegen = CalcEnduranceRegenCap(); + + SetAttackTimer(); +} + +void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug, bool isTribute) { + if(!inst || !inst->IsType(ItemClassCommon)) + { + return; + } + + if(inst->GetAugmentType()==0 && isAug == true) + { + return; + } + + const Item_Struct *item = inst->GetItem(); + + if(!isTribute && !inst->IsEquipable(GetBaseRace(),GetClass())) + { + if(item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink) + return; + } + + if(GetLevel() < item->ReqLevel) + { + return; + } + + if(GetLevel() >= item->RecLevel) + { + newbon->AC += item->AC; + newbon->HP += item->HP; + newbon->Mana += item->Mana; + newbon->Endurance += item->Endur; + newbon->STR += (item->AStr + item->HeroicStr); + newbon->STA += (item->ASta + item->HeroicSta); + newbon->DEX += (item->ADex + item->HeroicDex); + newbon->AGI += (item->AAgi + item->HeroicAgi); + newbon->INT += (item->AInt + item->HeroicInt); + newbon->WIS += (item->AWis + item->HeroicWis); + newbon->CHA += (item->ACha + item->HeroicCha); + + newbon->MR += (item->MR + item->HeroicMR); + newbon->FR += (item->FR + item->HeroicFR); + newbon->CR += (item->CR + item->HeroicCR); + newbon->PR += (item->PR + item->HeroicPR); + newbon->DR += (item->DR + item->HeroicDR); + newbon->Corrup += (item->SVCorruption + item->HeroicSVCorrup); + + newbon->STRCapMod += item->HeroicStr; + newbon->STACapMod += item->HeroicSta; + newbon->DEXCapMod += item->HeroicDex; + newbon->AGICapMod += item->HeroicAgi; + newbon->INTCapMod += item->HeroicInt; + newbon->WISCapMod += item->HeroicWis; + newbon->CHACapMod += item->HeroicCha; + newbon->MRCapMod += item->HeroicMR; + newbon->CRCapMod += item->HeroicFR; + newbon->FRCapMod += item->HeroicCR; + newbon->PRCapMod += item->HeroicPR; + newbon->DRCapMod += item->HeroicDR; + newbon->CorrupCapMod += item->HeroicSVCorrup; + + newbon->HeroicSTR += item->HeroicStr; + newbon->HeroicSTA += item->HeroicSta; + newbon->HeroicDEX += item->HeroicDex; + newbon->HeroicAGI += item->HeroicAgi; + newbon->HeroicINT += item->HeroicInt; + newbon->HeroicWIS += item->HeroicWis; + newbon->HeroicCHA += item->HeroicCha; + newbon->HeroicMR += item->HeroicMR; + newbon->HeroicFR += item->HeroicFR; + newbon->HeroicCR += item->HeroicCR; + newbon->HeroicPR += item->HeroicPR; + newbon->HeroicDR += item->HeroicDR; + newbon->HeroicCorrup += item->HeroicSVCorrup; + + } + else + { + int lvl = GetLevel(); + int reclvl = item->RecLevel; + + newbon->AC += CalcRecommendedLevelBonus( lvl, reclvl, item->AC ); + newbon->HP += CalcRecommendedLevelBonus( lvl, reclvl, item->HP ); + newbon->Mana += CalcRecommendedLevelBonus( lvl, reclvl, item->Mana ); + newbon->Endurance += CalcRecommendedLevelBonus( lvl, reclvl, item->Endur ); + newbon->STR += CalcRecommendedLevelBonus( lvl, reclvl, (item->AStr + item->HeroicStr) ); + newbon->STA += CalcRecommendedLevelBonus( lvl, reclvl, (item->ASta + item->HeroicSta) ); + newbon->DEX += CalcRecommendedLevelBonus( lvl, reclvl, (item->ADex + item->HeroicDex) ); + newbon->AGI += CalcRecommendedLevelBonus( lvl, reclvl, (item->AAgi + item->HeroicAgi) ); + newbon->INT += CalcRecommendedLevelBonus( lvl, reclvl, (item->AInt + item->HeroicInt) ); + newbon->WIS += CalcRecommendedLevelBonus( lvl, reclvl, (item->AWis + item->HeroicWis) ); + newbon->CHA += CalcRecommendedLevelBonus( lvl, reclvl, (item->ACha + item->HeroicCha) ); + + newbon->MR += CalcRecommendedLevelBonus( lvl, reclvl, (item->MR + item->HeroicMR) ); + newbon->FR += CalcRecommendedLevelBonus( lvl, reclvl, (item->FR + item->HeroicFR) ); + newbon->CR += CalcRecommendedLevelBonus( lvl, reclvl, (item->CR + item->HeroicCR) ); + newbon->PR += CalcRecommendedLevelBonus( lvl, reclvl, (item->PR + item->HeroicPR) ); + newbon->DR += CalcRecommendedLevelBonus( lvl, reclvl, (item->DR + item->HeroicDR) ); + newbon->Corrup += CalcRecommendedLevelBonus( lvl, reclvl, (item->SVCorruption + item->HeroicSVCorrup) ); + + newbon->STRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicStr ); + newbon->STACapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSta ); + newbon->DEXCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDex ); + newbon->AGICapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicAgi ); + newbon->INTCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicInt ); + newbon->WISCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicWis ); + newbon->CHACapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCha ); + newbon->MRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicMR ); + newbon->CRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicFR ); + newbon->FRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCR ); + newbon->PRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicPR ); + newbon->DRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDR ); + newbon->CorrupCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSVCorrup ); + + newbon->HeroicSTR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicStr ); + newbon->HeroicSTA += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSta ); + newbon->HeroicDEX += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDex ); + newbon->HeroicAGI += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicAgi ); + newbon->HeroicINT += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicInt ); + newbon->HeroicWIS += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicWis ); + newbon->HeroicCHA += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCha ); + newbon->HeroicMR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicMR ); + newbon->HeroicFR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicFR ); + newbon->HeroicCR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCR ); + newbon->HeroicPR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicPR ); + newbon->HeroicDR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDR ); + newbon->HeroicCorrup += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSVCorrup ); + } + + //FatherNitwit: New style haste, shields, and regens + if(newbon->haste < (int16)item->Haste) { + newbon->haste = item->Haste; + } + if(item->Regen > 0) + newbon->HPRegen += item->Regen; + + if(item->ManaRegen > 0) + newbon->ManaRegen += item->ManaRegen; + + if(item->EnduranceRegen > 0) + newbon->EnduranceRegen += item->EnduranceRegen; + + if(item->Attack > 0) { + + int cap = RuleI(Character, ItemATKCap); + cap += itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap; + + if((newbon->ATK + item->Attack) > cap) + newbon->ATK = RuleI(Character, ItemATKCap); + else + newbon->ATK += item->Attack; + } + if(item->DamageShield > 0) { + if((newbon->DamageShield + item->DamageShield) > RuleI(Character, ItemDamageShieldCap)) + newbon->DamageShield = RuleI(Character, ItemDamageShieldCap); + else + newbon->DamageShield += item->DamageShield; + } + if(item->SpellShield > 0) { + if((newbon->SpellShield + item->SpellShield) > RuleI(Character, ItemSpellShieldingCap)) + newbon->SpellShield = RuleI(Character, ItemSpellShieldingCap); + else + newbon->SpellShield += item->SpellShield; + } + if(item->Shielding > 0) { + if((newbon->MeleeMitigation + item->Shielding) > RuleI(Character, ItemShieldingCap)) + newbon->MeleeMitigation = RuleI(Character, ItemShieldingCap); + else + newbon->MeleeMitigation += item->Shielding; + } + if(item->StunResist > 0) { + if((newbon->StunResist + item->StunResist) > RuleI(Character, ItemStunResistCap)) + newbon->StunResist = RuleI(Character, ItemStunResistCap); + else + newbon->StunResist += item->StunResist; + } + if(item->StrikeThrough > 0) { + if((newbon->StrikeThrough + item->StrikeThrough) > RuleI(Character, ItemStrikethroughCap)) + newbon->StrikeThrough = RuleI(Character, ItemStrikethroughCap); + else + newbon->StrikeThrough += item->StrikeThrough; + } + if(item->Avoidance > 0) { + if((newbon->AvoidMeleeChance + item->Avoidance) > RuleI(Character, ItemAvoidanceCap)) + newbon->AvoidMeleeChance = RuleI(Character, ItemAvoidanceCap); + else + newbon->AvoidMeleeChance += item->Avoidance; + } + if(item->Accuracy > 0) { + if((newbon->HitChance + item->Accuracy) > RuleI(Character, ItemAccuracyCap)) + newbon->HitChance = RuleI(Character, ItemAccuracyCap); + else + newbon->HitChance += item->Accuracy; + } + if(item->CombatEffects > 0) { + if((newbon->ProcChance + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap)) + newbon->ProcChance = RuleI(Character, ItemCombatEffectsCap); + else + newbon->ProcChance += item->CombatEffects; + } + if(item->DotShielding > 0) { + if((newbon->DoTShielding + item->DotShielding) > RuleI(Character, ItemDoTShieldingCap)) + newbon->DoTShielding = RuleI(Character, ItemDoTShieldingCap); + else + newbon->DoTShielding += item->DotShielding; + } + + if(item->HealAmt > 0) { + if((newbon->HealAmt + item->HealAmt) > RuleI(Character, ItemHealAmtCap)) + newbon->HealAmt = RuleI(Character, ItemHealAmtCap); + else + newbon->HealAmt += item->HealAmt; + } + if(item->SpellDmg > 0) { + if((newbon->SpellDmg + item->SpellDmg) > RuleI(Character, ItemSpellDmgCap)) + newbon->SpellDmg = RuleI(Character, ItemSpellDmgCap); + else + newbon->SpellDmg += item->SpellDmg; + } + if(item->Clairvoyance > 0) { + if((newbon->Clairvoyance + item->Clairvoyance) > RuleI(Character, ItemClairvoyanceCap)) + newbon->Clairvoyance = RuleI(Character, ItemClairvoyanceCap); + else + newbon->Clairvoyance += item->Clairvoyance; + } + + if(item->DSMitigation > 0) { + if((newbon->DSMitigation + item->DSMitigation) > RuleI(Character, ItemDSMitigationCap)) + newbon->DSMitigation = RuleI(Character, ItemDSMitigationCap); + else + newbon->DSMitigation += item->DSMitigation; + } + if (item->Worn.Effect>0 && (item->Worn.Type == ET_WornEffect)) { // latent effects + ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, true); + } + + if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects + ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0, true); + } + + switch(item->BardType) + { + case 51: /* All (e.g. Singing Short Sword) */ + { + if(item->BardValue > newbon->singingMod) + newbon->singingMod = item->BardValue; + if(item->BardValue > newbon->brassMod) + newbon->brassMod = item->BardValue; + if(item->BardValue > newbon->stringedMod) + newbon->stringedMod = item->BardValue; + if(item->BardValue > newbon->percussionMod) + newbon->percussionMod = item->BardValue; + if(item->BardValue > newbon->windMod) + newbon->windMod = item->BardValue; + break; + } + case 50: /* Singing */ + { + if(item->BardValue > newbon->singingMod) + newbon->singingMod = item->BardValue; + break; + } + case 23: /* Wind */ + { + if(item->BardValue > newbon->windMod) + newbon->windMod = item->BardValue; + break; + } + case 24: /* stringed */ + { + if(item->BardValue > newbon->stringedMod) + newbon->stringedMod = item->BardValue; + break; + } + case 25: /* brass */ + { + if(item->BardValue > newbon->brassMod) + newbon->brassMod = item->BardValue; + break; + } + case 26: /* Percussion */ + { + if(item->BardValue > newbon->percussionMod) + newbon->percussionMod = item->BardValue; + break; + } + } + + if (item->SkillModValue != 0 && item->SkillModType <= HIGHEST_SKILL){ + if ((item->SkillModValue > 0 && newbon->skillmod[item->SkillModType] < item->SkillModValue) || + (item->SkillModValue < 0 && newbon->skillmod[item->SkillModType] > item->SkillModValue)) + { + newbon->skillmod[item->SkillModType] = item->SkillModValue; + } + } + + // Add Item Faction Mods + if (item->FactionMod1) + { + if (item->FactionAmt1 > 0 && item->FactionAmt1 > GetItemFactionBonus(item->FactionMod1)) + { + AddItemFactionBonus(item->FactionMod1, item->FactionAmt1); + } + else if (item->FactionAmt1 < 0 && item->FactionAmt1 < GetItemFactionBonus(item->FactionMod1)) + { + AddItemFactionBonus(item->FactionMod1, item->FactionAmt1); + } + } + if (item->FactionMod2) + { + if (item->FactionAmt2 > 0 && item->FactionAmt2 > GetItemFactionBonus(item->FactionMod2)) + { + AddItemFactionBonus(item->FactionMod2, item->FactionAmt2); + } + else if (item->FactionAmt2 < 0 && item->FactionAmt2 < GetItemFactionBonus(item->FactionMod2)) + { + AddItemFactionBonus(item->FactionMod2, item->FactionAmt2); + } + } + if (item->FactionMod3) + { + if (item->FactionAmt3 > 0 && item->FactionAmt3 > GetItemFactionBonus(item->FactionMod3)) + { + AddItemFactionBonus(item->FactionMod3, item->FactionAmt3); + } + else if (item->FactionAmt3 < 0 && item->FactionAmt3 < GetItemFactionBonus(item->FactionMod3)) + { + AddItemFactionBonus(item->FactionMod3, item->FactionAmt3); + } + } + if (item->FactionMod4) + { + if (item->FactionAmt4 > 0 && item->FactionAmt4 > GetItemFactionBonus(item->FactionMod4)) + { + AddItemFactionBonus(item->FactionMod4, item->FactionAmt4); + } + else if (item->FactionAmt4 < 0 && item->FactionAmt4 < GetItemFactionBonus(item->FactionMod4)) + { + AddItemFactionBonus(item->FactionMod4, item->FactionAmt4); + } + } + + if (item->ExtraDmgSkill != 0 && item->ExtraDmgSkill <= HIGHEST_SKILL) { + if((newbon->SkillDamageAmount[item->ExtraDmgSkill] + item->ExtraDmgAmt) > RuleI(Character, ItemExtraDmgCap)) + newbon->SkillDamageAmount[item->ExtraDmgSkill] = RuleI(Character, ItemExtraDmgCap); + else + newbon->SkillDamageAmount[item->ExtraDmgSkill] += item->ExtraDmgAmt; + } + + if (!isAug) + { + int i; + for(i = 0; i < MAX_AUGMENT_SLOTS; i++) { + AddItemBonuses(inst->GetAugment(i),newbon,true); + } + } + +} + +void Client::CalcEdibleBonuses(StatBonuses* newbon) { +#if EQDEBUG >= 11 + cout<<"Client::CalcEdibleBonuses(StatBonuses* newbon)"<GetItem() && inst->IsType(ItemClassCommon)) { + const Item_Struct *item=inst->GetItem(); + if (item->ItemType == ItemTypeFood && !food) + food = true; + else if (item->ItemType == ItemTypeDrink && !drink) + drink = true; + else + continue; + AddItemBonuses(inst, newbon); + } + } + for (i = 251; i <= 330; i++) + { + if (food && drink) + break; + const ItemInst* inst = GetInv().GetItem(i); + if (inst && inst->GetItem() && inst->IsType(ItemClassCommon)) { + const Item_Struct *item=inst->GetItem(); + if (item->ItemType == ItemTypeFood && !food) + food = true; + else if (item->ItemType == ItemTypeDrink && !drink) + drink = true; + else + continue; + AddItemBonuses(inst, newbon); + } + } +} + +void Client::CalcAABonuses(StatBonuses* newbon) { + memset(newbon, 0, sizeof(StatBonuses)); //start fresh + + int i; + uint32 slots = 0; + uint32 aa_AA = 0; + uint32 aa_value = 0; + for (i = 0; i < MAX_PP_AA_ARRAY; i++) { //iterate through all of the client's AAs + if (this->aa[i]) { // make sure aa exists or we'll crash zone + aa_AA = this->aa[i]->AA; //same as aaid from the aa_effects table + aa_value = this->aa[i]->value; //how many points in it + if (aa_AA > 0 || aa_value > 0) { //do we have the AA? if 1 of the 2 is set, we can assume we do + //slots = database.GetTotalAALevels(aa_AA); //find out how many effects from aa_effects table + slots = zone->GetTotalAALevels(aa_AA); //find out how many effects from aa_effects, which is loaded into memory + if (slots > 0) //and does it have any effects? may be able to put this above, not sure if it runs on each iteration + ApplyAABonuses(aa_AA, slots, newbon); //add the bonuses + } + } + } +} + + +//A lot of the normal spell functions (IsBlankSpellEffect, etc) are set for just spells (in zone/spdat.h). For now, we'll just put them directly into the code and comment with the corresponding normal function +//Maybe we'll fix it later? :-D +void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) +{ + if(slots == 0) //sanity check. why bother if no slots to fill? + return; + + //from AA_Ability struct + uint32 effect = 0; + int32 base1 = 0; + int32 base2 = 0; //only really used for SE_RaiseStatCap & SE_ReduceSkillTimer in aa_effects table + uint32 slot = 0; + + std::map >::const_iterator find_iter = aa_effects.find(aaid); + if(find_iter == aa_effects.end()) + { + return; + } + + for (map::const_iterator iter = aa_effects[aaid].begin(); iter != aa_effects[aaid].end(); ++iter) { + effect = iter->second.skill_id; + base1 = iter->second.base1; + base2 = iter->second.base2; + slot = iter->second.slot; + + //we default to 0 (SE_CurrentHP) for the effect, so if there aren't any base1/2 values, we'll just skip it + if (effect == 0 && base1 == 0 && base2 == 0) + continue; + + //IsBlankSpellEffect() + if (effect == SE_Blank || (effect == SE_CHA && base1 == 0) || effect == SE_StackingCommand_Block || effect == SE_StackingCommand_Overwrite) + continue; + + _log(AA__BONUSES, "Applying Effect %d from AA %u in slot %d (base1: %d, base2: %d) on %s", effect, aaid, slot, base1, base2, this->GetCleanName()); + + uint8 focus = IsFocusEffect(0, 0, true,effect); + if (focus) + { + newbon->FocusEffects[focus] = effect; + continue; + } + + switch (effect) + { + //Note: AA effects that use accuracy are skill limited, while spell effect is not. + case SE_Accuracy: + if ((base2 == -1) && (newbon->Accuracy[HIGHEST_SKILL+1] < base1)) + newbon->Accuracy[HIGHEST_SKILL+1] = base1; + else if (newbon->Accuracy[base2] < base1) + newbon->Accuracy[base2] += base1; + break; + case SE_CurrentHP: //regens + newbon->HPRegen += base1; + break; + case SE_CurrentEndurance: + newbon->EnduranceRegen += base1; + break; + case SE_MovementSpeed: + newbon->movementspeed += base1; //should we let these stack? + /*if (base1 > newbon->movementspeed) //or should we use a total value? + newbon->movementspeed = base1;*/ + break; + case SE_STR: + newbon->STR += base1; + break; + case SE_DEX: + newbon->DEX += base1; + break; + case SE_AGI: + newbon->AGI += base1; + break; + case SE_STA: + newbon->STA += base1; + break; + case SE_INT: + newbon->INT += base1; + break; + case SE_WIS: + newbon->WIS += base1; + break; + case SE_CHA: + newbon->CHA += base1; + break; + case SE_WaterBreathing: + //handled by client + break; + case SE_CurrentMana: + newbon->ManaRegen += base1; + break; + case SE_ItemManaRegenCapIncrease: + newbon->ItemManaRegenCap += base1; + break; + case SE_ResistFire: + newbon->FR += base1; + break; + case SE_ResistCold: + newbon->CR += base1; + break; + case SE_ResistPoison: + newbon->PR += base1; + break; + case SE_ResistDisease: + newbon->DR += base1; + break; + case SE_ResistMagic: + newbon->MR += base1; + break; + case SE_ResistCorruption: + newbon->Corrup += base1; + break; + case SE_IncreaseSpellHaste: + break; + case SE_IncreaseRange: + break; + case SE_MaxHPChange: + newbon->MaxHP += base1; + break; + case SE_Packrat: + newbon->Packrat += base1; + break; + case SE_TwoHandBash: + break; + case SE_SetBreathLevel: + break; + case SE_RaiseStatCap: + switch(base2) + { + //are these #define'd somewhere? + case 0: //str + newbon->STRCapMod += base1; + break; + case 1: //sta + newbon->STACapMod += base1; + break; + case 2: //agi + newbon->AGICapMod += base1; + break; + case 3: //dex + newbon->DEXCapMod += base1; + break; + case 4: //wis + newbon->WISCapMod += base1; + break; + case 5: //int + newbon->INTCapMod += base1; + break; + case 6: //cha + newbon->CHACapMod += base1; + break; + case 7: //mr + newbon->MRCapMod += base1; + break; + case 8: //cr + newbon->CRCapMod += base1; + break; + case 9: //fr + newbon->FRCapMod += base1; + break; + case 10: //pr + newbon->PRCapMod += base1; + break; + case 11: //dr + newbon->DRCapMod += base1; + break; + case 12: //corruption + newbon->CorrupCapMod += base1; + break; + } + break; + case SE_PetDiscipline2: + break; + case SE_SpellSlotIncrease: + break; + case SE_MysticalAttune: + newbon->BuffSlotIncrease += base1; + break; + case SE_TotalHP: + newbon->HP += base1; + break; + case SE_StunResist: + newbon->StunResist += base1; + break; + case SE_SpellCritChance: + newbon->CriticalSpellChance += base1; + break; + case SE_SpellCritDmgIncrease: + newbon->SpellCritDmgIncrease += base1; + break; + case SE_DotCritDmgIncrease: + newbon->DotCritDmgIncrease += base1; + break; + case SE_ResistSpellChance: + newbon->ResistSpellChance += base1; + break; + case SE_CriticalHealChance: + newbon->CriticalHealChance += base1; + break; + case SE_CriticalHealOverTime: + newbon->CriticalHealOverTime += base1; + break; + case SE_CriticalDoTChance: + newbon->CriticalDoTChance += base1; + break; + case SE_ReduceSkillTimer: + newbon->SkillReuseTime[base2] += base1; + break; + case SE_Fearless: + newbon->Fearless = true; + break; + case SE_PersistantCasting: + newbon->PersistantCasting += base1; + break; + case SE_DelayDeath: + newbon->DelayDeath += base1; + break; + case SE_FrontalStunResist: + newbon->FrontalStunResist += base1; + break; + case SE_ImprovedBindWound: + newbon->BindWound += base1; + break; + case SE_MaxBindWound: + newbon->MaxBindWound += base1; + break; + case SE_ExtraAttackChance: + newbon->ExtraAttackChance += base1; + break; + case SE_SeeInvis: + newbon->SeeInvis = base1; + break; + case SE_BaseMovementSpeed: + newbon->BaseMovementSpeed += base1; + break; + case SE_IncreaseRunSpeedCap: + newbon->IncreaseRunSpeedCap += base1; + break; + case SE_ConsumeProjectile: + newbon->ConsumeProjectile += base1; + break; + case SE_ArcheryDamageModifier: + newbon->ArcheryDamageModifier += base1; + break; + case SE_DamageShield: + newbon->DamageShield += base1; + break; + case SE_CharmBreakChance: + newbon->CharmBreakChance += base1; + break; + case SE_OffhandRiposteFail: + newbon->OffhandRiposteFail += base1; + break; + case SE_ItemAttackCapIncrease: + newbon->ItemATKCap += base1; + break; + case SE_GivePetGroupTarget: + newbon->GivePetGroupTarget = true; + break; + case SE_ItemHPRegenCapIncrease: + newbon->ItemHPRegenCap = +base1; + break; + case SE_Ambidexterity: + newbon->Ambidexterity += base1; + break; + case SE_PetMaxHP: + newbon->PetMaxHP += base1; + break; + case SE_AvoidMeleeChance: + newbon->AvoidMeleeChance += base1; + break; + case SE_CombatStability: + newbon->CombatStability += base1; + break; + case SE_PetCriticalHit: + newbon->PetCriticalHit += base1; + break; + case SE_PetAvoidance: + newbon->PetAvoidance += base1; + break; + case SE_ShieldBlock: + newbon->ShieldBlock += base1; + break; + case SE_SecondaryDmgInc: + newbon->SecondaryDmgInc = true; + break; + case SE_ChangeAggro: + newbon->hatemod += base1; + break; + case SE_EndurancePool: + newbon->Endurance += base1; + break; + case SE_ChannelChanceItems: + newbon->ChannelChanceItems += base1; + break; + case SE_ChannelChanceSpells: + newbon->ChannelChanceSpells += base1; + break; + case SE_DoubleSpecialAttack: + newbon->DoubleSpecialAttack += base1; + break; + case SE_TripleBackstab: + newbon->TripleBackstab += base1; + break; + case SE_FrontalBackstabMinDmg: + newbon->FrontalBackstabMinDmg = true; + break; + case SE_FrontalBackstabChance: + newbon->FrontalBackstabChance += base1; + break; + case SE_BlockBehind: + newbon->BlockBehind += base1; + break; + case SE_StrikeThrough2: + newbon->StrikeThrough += base1; + break; + case SE_DoubleAttackChance: + newbon->DoubleAttackChance += base1; + break; + case SE_GiveDoubleAttack: + newbon->GiveDoubleAttack += base1; + break; + case SE_ProcChance: + newbon->ProcChance += base1; + break; + case SE_RiposteChance: + newbon->RiposteChance += base1; + break; + case SE_Flurry: + newbon->FlurryChance += base1; + break; + case SE_PetFlurry: + newbon->PetFlurry = base1; + break; + case SE_BardSongRange: + newbon->SongRange += base1; + break; + case SE_RootBreakChance: + newbon->RootBreakChance += base1; + break; + case SE_UnfailingDivinity: + newbon->UnfailingDivinity += base1; + break; + case SE_CrippBlowChance: + newbon->CrippBlowChance += base1; + break; + + case SE_SpellOnKill: + for(int i = 0; i < MAX_SPELL_TRIGGER*3; i+=3) + { + if(!newbon->SpellOnKill[i] || ((newbon->SpellOnKill[i] == base2) && (newbon->SpellOnKill[i+1] < base1))) + { + //base1 = chance, base2 = SpellID to be triggered, base3 = min npc level + newbon->SpellOnKill[i] = base2; + newbon->SpellOnKill[i+1] = base1; + + if (GetLevel() > 15) + newbon->SpellOnKill[i+2] = GetLevel() - 15; //AA specifiy "non-trivial" + else + newbon->SpellOnKill[i+2] = 0; + + break; + } + } + break; + + case SE_SpellOnDeath: + for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) + { + if(!newbon->SpellOnDeath[i]) + { + // base1 = SpellID to be triggered, base2 = chance to fire + newbon->SpellOnDeath[i] = base1; + newbon->SpellOnDeath[i+1] = base2; + break; + } + } + break; + + case SE_TriggerOnCast: + + for(int i = 0; i < MAX_SPELL_TRIGGER; i++) + { + if (newbon->SpellTriggers[i] == aaid) + break; + + if(!newbon->SpellTriggers[i]) + { + //Save the 'aaid' of each triggerable effect to an array + newbon->SpellTriggers[i] = aaid; + break; + } + } + break; + + case SE_CriticalHitChance: + { + if(base2 == -1) + newbon->CriticalHitChance[HIGHEST_SKILL+1] += base1; + else + newbon->CriticalHitChance[base2] += base1; + } + break; + + case SE_CriticalDamageMob: + { + // base1 = effect value, base2 = skill restrictions(-1 for all) + if(base2 == -1) + newbon->CritDmgMob[HIGHEST_SKILL+1] += base1; + else + newbon->CritDmgMob[base2] += base1; + break; + } + + case SE_CriticalSpellChance: + { + newbon->CriticalSpellChance += base1; + + if (base2 > 100) + newbon->SpellCritDmgIncrease += (base2 - 100); + + break; + } + + case SE_ResistFearChance: + { + if(base1 == 100) // If we reach 100% in a single spell/item then we should be immune to negative fear resist effects until our immunity is over + newbon->Fearless = true; + + newbon->ResistFearChance += base1; // these should stack + break; + } + + case SE_SkillDamageAmount: + { + if(base2 == -1) + newbon->SkillDamageAmount[HIGHEST_SKILL+1] += base1; + else + newbon->SkillDamageAmount[base2] += base1; + break; + } + + case SE_SpecialAttackKBProc: + { + //You can only have one of these per client. [AA Dragon Punch] + newbon->SpecialAttackKBProc[0] = base1; //Chance base 100 = 25% proc rate + newbon->SpecialAttackKBProc[1] = base2; //Skill to KB Proc Off + break; + } + + case SE_DamageModifier: + { + if(base2 == -1) + newbon->DamageModifier[HIGHEST_SKILL+1] += base1; + else + newbon->DamageModifier[base2] += base1; + break; + } + + case SE_SlayUndead: + { + if(newbon->SlayUndead[1] < base1) + newbon->SlayUndead[0] = base1; // Rate + newbon->SlayUndead[1] = base2; // Damage Modifier + break; + } + + case SE_GiveDoubleRiposte: + { + //0=Regular Riposte 1=Skill Attack Riposte 2=Skill + if(base2 == 0){ + if(newbon->GiveDoubleRiposte[0] < base1) + newbon->GiveDoubleRiposte[0] = base1; + } + //Only for special attacks. + else if(base2 > 0 && (newbon->GiveDoubleRiposte[1] < base1)){ + newbon->GiveDoubleRiposte[1] = base1; + newbon->GiveDoubleRiposte[2] = base2; + } + + break; + } + + //Kayen: Not sure best way to implement this yet. + //Physically raises skill cap ie if 55/55 it will raise to 55/60 + case SE_RaiseSkillCap: + { + if(newbon->RaiseSkillCap[0] < base1){ + newbon->RaiseSkillCap[0] = base1; //value + newbon->RaiseSkillCap[1] = base2; //skill + } + break; + } + + case SE_MasteryofPast: + { + if(newbon->MasteryofPast < base1) + newbon->MasteryofPast = base1; + break; + } + + case SE_CastingLevel2: + case SE_CastingLevel: + { + newbon->effective_casting_level += base1; + break; + } + + + case SE_DivineSave: + { + if(newbon->DivineSaveChance[0] < base1) + { + newbon->DivineSaveChance[0] = base1; + newbon->DivineSaveChance[1] = base2; + } + break; + } + + case SE_SpellEffectResistChance: + { + for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2) + { + if(!newbon->SEResist[e] || ((newbon->SEResist[e] = base2) && (newbon->SEResist[e+1] < base1)) ){ + newbon->SEResist[e] = base2; + newbon->SEResist[e+1] = base1; + break; + } + } + break; + } + + case SE_MitigateDamageShield: + { + if (base1 < 0) + base1 = base1*(-1); + + newbon->DSMitigationOffHand += base1; + break; + } + + case SE_FinishingBlow: + { + + //base1 = chance, base2 = damage + if (newbon->FinishingBlow[1] < base2){ + newbon->FinishingBlow[0] = base1; + newbon->FinishingBlow[1] = base2; + } + break; + } + + case SE_FinishingBlowLvl: + { + //base1 = level, base2 = ??? (Set to 200 in AA data, possible proc rate mod?) + if (newbon->FinishingBlowLvl[0] < base1){ + newbon->FinishingBlowLvl[0] = base1; + newbon->FinishingBlowLvl[1] = base2; + } + break; + } + } + } +} + +void Mob::CalcSpellBonuses(StatBonuses* newbon) +{ + int i; + + memset(newbon, 0, sizeof(StatBonuses)); + newbon->AggroRange = -1; + newbon->AssistRange = -1; + + uint32 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); + } + + //Removes the spell bonuses that are effected by a 'negate' debuff. + if (spellbonuses.NegateEffects){ + for(i = 0; i < buff_count; i++) { + if( (buffs[i].spellid != SPELL_UNKNOWN) && (IsEffectInSpell(buffs[i].spellid, SE_NegateSpellEffect)) ) + NegateSpellsBonuses(buffs[i].spellid); + } + } + //this prolly suffer from roundoff error slightly... + newbon->AC = newbon->AC * 10 / 34; //ratio determined impirically from client. +} + +void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterId, bool item_bonus, uint32 ticsremaining, int buffslot) +{ + int i, effect_value; + Mob *caster = NULL; + + if(!IsValidSpell(spell_id)) + return; + + if(casterId > 0) + caster = entity_list.GetMob(casterId); + + for (i = 0; i < EFFECT_COUNT; i++) + { + if(IsBlankSpellEffect(spell_id, i)) + continue; + + uint8 focus = IsFocusEffect(spell_id, i); + if (focus) + { + newbon->FocusEffects[focus] = spells[spell_id].effectid[i]; + continue; + } + + effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining); + + switch (spells[spell_id].effectid[i]) + { + case SE_CurrentHP: //regens + if(effect_value > 0) { + newbon->HPRegen += effect_value; + } + break; + + case SE_CurrentEndurance: + newbon->EnduranceRegen += effect_value; + break; + + case SE_ChangeFrenzyRad: + { + // redundant to have level check here + if(newbon->AggroRange == -1 || effect_value < newbon->AggroRange) + { + newbon->AggroRange = effect_value; + } + break; + } + + case SE_Harmony: + { + // neotokyo: 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; + } + break; + } + + case SE_AttackSpeed: + { + if ((effect_value - 100) > 0) { // Haste + if (newbon->haste < 0) break; // Slowed - Don't apply haste + if ((effect_value - 100) > newbon->haste) { + newbon->haste = effect_value - 100; + } + } + else if ((effect_value - 100) < 0) { // Slow + //Slow Mitigation works by taking the amount that would be slowed, and adding a multiplied version of the difference. + int real_slow_value = (100 - effect_value) * -1; + if (slow_mitigation){ + int new_effect_value = SlowMitigation(false,caster,real_slow_value); + if (new_effect_value < newbon->haste) { + newbon->haste = new_effect_value; + SlowMitigation(true,caster); + } + } + else { + if (real_slow_value < newbon->haste) + newbon->haste = real_slow_value; + } + } + break; + } + + case SE_AttackSpeed2: + { + if ((effect_value - 100) > 0) { // Haste V2 - Stacks with V1 but does not Overcap + if ((effect_value - 100) > newbon->hastetype2) { + newbon->hastetype2 = effect_value - 100; + } + } + break; + } + + case SE_AttackSpeed3: + { + if (effect_value > 0) { // Haste V3 - Stacks and Overcaps + if (effect_value > newbon->hastetype3) { + newbon->hastetype3 = effect_value; + } + } + break; + } + + case SE_AttackSpeed4: + { + if (effect_value > 0) { + if (slow_mitigation){ + int new_effect_value = SlowMitigation(false,caster,effect_value); + if (new_effect_value > newbon->inhibitmelee) { + newbon->inhibitmelee = new_effect_value; + SlowMitigation(true,caster); + } + } + else if (effect_value > newbon->inhibitmelee) { + newbon->inhibitmelee = effect_value; + } + } + break; + } + + case SE_TotalHP: + { + newbon->HP += effect_value; + break; + } + + case SE_ManaRegen_v2: + case SE_CurrentMana: + { + newbon->ManaRegen += effect_value; + break; + } + + case SE_ManaPool: + { + newbon->Mana += effect_value; + break; + } + + case SE_Stamina: + { + newbon->EnduranceReduction += effect_value; + break; + } + + case SE_ACv2: + case SE_ArmorClass: + { + newbon->AC += effect_value; + break; + } + + case SE_ATK: + { + newbon->ATK += effect_value; + break; + } + + case SE_STR: + { + newbon->STR += effect_value; + break; + } + + case SE_DEX: + { + newbon->DEX += effect_value; + break; + } + + case SE_AGI: + { + newbon->AGI += effect_value; + break; + } + + case SE_STA: + { + newbon->STA += effect_value; + break; + } + + case SE_INT: + { + newbon->INT += effect_value; + break; + } + + case SE_WIS: + { + newbon->WIS += effect_value; + break; + } + + case SE_CHA: + { + if (spells[spell_id].base[i] != 0) { + newbon->CHA += effect_value; + } + break; + } + + case SE_AllStats: + { + newbon->STR += effect_value; + newbon->DEX += effect_value; + newbon->AGI += effect_value; + newbon->STA += effect_value; + newbon->INT += effect_value; + newbon->WIS += effect_value; + newbon->CHA += effect_value; + break; + } + + case SE_ResistFire: + { + newbon->FR += effect_value; + break; + } + + case SE_ResistCold: + { + newbon->CR += effect_value; + break; + } + + case SE_ResistPoison: + { + newbon->PR += effect_value; + break; + } + + case SE_ResistDisease: + { + newbon->DR += effect_value; + break; + } + + case SE_ResistMagic: + { + newbon->MR += effect_value; + break; + } + + case SE_ResistAll: + { + newbon->MR += effect_value; + newbon->DR += effect_value; + newbon->PR += effect_value; + newbon->CR += effect_value; + newbon->FR += effect_value; + break; + } + + case SE_ResistCorruption: + { + newbon->Corrup += effect_value; + break; + } + + case SE_RaiseStatCap: + { + switch(spells[spell_id].base2[i]) + { + //are these #define'd somewhere? + case 0: //str + newbon->STRCapMod += effect_value; + break; + case 1: //sta + newbon->STACapMod += effect_value; + break; + case 2: //agi + newbon->AGICapMod += effect_value; + break; + case 3: //dex + newbon->DEXCapMod += effect_value; + break; + case 4: //wis + newbon->WISCapMod += effect_value; + break; + case 5: //int + newbon->INTCapMod += effect_value; + break; + case 6: //cha + newbon->CHACapMod += effect_value; + break; + case 7: //mr + newbon->MRCapMod += effect_value; + break; + case 8: //cr + newbon->CRCapMod += effect_value; + break; + case 9: //fr + newbon->FRCapMod += effect_value; + break; + case 10: //pr + newbon->PRCapMod += effect_value; + break; + case 11: //dr + newbon->DRCapMod += effect_value; + break; + case 12: // corruption + newbon->CorrupCapMod += effect_value; + break; + } + break; + } + + case SE_CastingLevel2: + case SE_CastingLevel: // Brilliance of Ro + { + newbon->effective_casting_level += effect_value; + break; + } + + case SE_MovementSpeed: + newbon->movementspeed += effect_value; + break; + + case SE_SpellDamageShield: + newbon->SpellDamageShield += effect_value; + break; + + case SE_DamageShield: + { + newbon->DamageShield += effect_value; + newbon->DamageShieldSpellID = spell_id; + newbon->DamageShieldType = GetDamageShieldType(spell_id); + break; + } + + case SE_ReverseDS: + { + newbon->ReverseDamageShield += effect_value; + newbon->ReverseDamageShieldSpellID = spell_id; + newbon->ReverseDamageShieldType = GetDamageShieldType(spell_id); + break; + } + + case SE_Reflect: + newbon->reflect_chance += effect_value; + break; + + case SE_SingingSkill: + { + if(effect_value > newbon->singingMod) + newbon->singingMod = effect_value; + break; + } + + case SE_ChangeAggro: + newbon->hatemod += effect_value; + break; + + case SE_MeleeMitigation: + //for some reason... this value is negative for increased mitigation + newbon->MeleeMitigation -= effect_value; + break; + + case SE_CriticalHitChance: + { + + if (RuleB(Spells, AdditiveBonusValues) && item_bonus) { + if(spells[spell_id].base2[i] == -1) + newbon->CriticalHitChance[HIGHEST_SKILL+1] += effect_value; + else + newbon->CriticalHitChance[spells[spell_id].base2[i]] += effect_value; + } + + else if(effect_value < 0) { + + if(spells[spell_id].base2[i] == -1 && newbon->CriticalHitChance[HIGHEST_SKILL+1] > effect_value) + newbon->CriticalHitChance[HIGHEST_SKILL+1] = effect_value; + else if(spells[spell_id].base2[i] != -1 && newbon->CriticalHitChance[spells[spell_id].base2[i]] > effect_value) + newbon->CriticalHitChance[spells[spell_id].base2[i]] = effect_value; + } + + + else if(spells[spell_id].base2[i] == -1 && newbon->CriticalHitChance[HIGHEST_SKILL+1] < effect_value) + newbon->CriticalHitChance[HIGHEST_SKILL+1] = effect_value; + else if(spells[spell_id].base2[i] != -1 && newbon->CriticalHitChance[spells[spell_id].base2[i]] < effect_value) + newbon->CriticalHitChance[spells[spell_id].base2[i]] = effect_value; + break; + } + + case SE_CrippBlowChance: + { + if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + newbon->CrippBlowChance += effect_value; + + else if((effect_value < 0) && (newbon->CrippBlowChance > effect_value)) + newbon->CrippBlowChance = effect_value; + + else if(newbon->CrippBlowChance < effect_value) + newbon->CrippBlowChance = effect_value; + + break; + } + + case SE_AvoidMeleeChance: + { + //multiplier is to be compatible with item effects, watching for overflow too + effect_value = effect_value<3000? effect_value * 10 : 30000; + if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + newbon->AvoidMeleeChance += effect_value; + + else if((effect_value < 0) && (newbon->AvoidMeleeChance > effect_value)) + newbon->AvoidMeleeChance = effect_value; + + else if(newbon->AvoidMeleeChance < effect_value) + newbon->AvoidMeleeChance = effect_value; + break; + } + + case SE_RiposteChance: + { + if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + newbon->RiposteChance += effect_value; + + else if((effect_value < 0) && (newbon->RiposteChance > effect_value)) + newbon->RiposteChance = effect_value; + + else if(newbon->RiposteChance < effect_value) + newbon->RiposteChance = effect_value; + break; + } + + case SE_DodgeChance: + { + if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + newbon->DodgeChance += effect_value; + + else if((effect_value < 0) && (newbon->DodgeChance > effect_value)) + newbon->DodgeChance = effect_value; + + if(newbon->DodgeChance < effect_value) + newbon->DodgeChance = effect_value; + break; + } + + case SE_ParryChance: + { + if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + newbon->ParryChance += effect_value; + + else if((effect_value < 0) && (newbon->ParryChance > effect_value)) + newbon->ParryChance = effect_value; + + if(newbon->ParryChance < effect_value) + newbon->ParryChance = effect_value; + break; + } + + case SE_DualWieldChance: + { + if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + newbon->DualWieldChance += effect_value; + + else if((effect_value < 0) && (newbon->DualWieldChance > effect_value)) + newbon->DualWieldChance = effect_value; + + if(newbon->DualWieldChance < effect_value) + newbon->DualWieldChance = effect_value; + break; + } + + case SE_DoubleAttackChance: + { + + if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + newbon->DoubleAttackChance += effect_value; + + else if((effect_value < 0) && (newbon->DoubleAttackChance > effect_value)) + newbon->DoubleAttackChance = effect_value; + + if(newbon->DoubleAttackChance < effect_value) + newbon->DoubleAttackChance = effect_value; + break; + } + + case SE_TripleAttackChance: + { + + if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + newbon->TripleAttackChance += effect_value; + + else if((effect_value < 0) && (newbon->TripleAttackChance > effect_value)) + newbon->TripleAttackChance = effect_value; + + if(newbon->TripleAttackChance < effect_value) + newbon->TripleAttackChance = effect_value; + break; + } + + case SE_MeleeLifetap: + { + if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + newbon->MeleeLifetap += spells[spell_id].base[i]; + + else if((effect_value < 0) && (newbon->MeleeLifetap > effect_value)) + newbon->MeleeLifetap = spells[spell_id].base[i]; + + if(newbon->MeleeLifetap < spells[spell_id].base[i]) + newbon->MeleeLifetap = spells[spell_id].base[i]; + break; + } + + case SE_AllInstrumentMod: + { + if(effect_value > newbon->singingMod) + newbon->singingMod = effect_value; + if(effect_value > newbon->brassMod) + newbon->brassMod = effect_value; + if(effect_value > newbon->percussionMod) + newbon->percussionMod = effect_value; + if(effect_value > newbon->windMod) + newbon->windMod = effect_value; + if(effect_value > newbon->stringedMod) + newbon->stringedMod = effect_value; + break; + } + + case SE_ResistSpellChance: + newbon->ResistSpellChance += effect_value; + break; + + case SE_ResistFearChance: + { + if(effect_value == 100) // If we reach 100% in a single spell/item then we should be immune to negative fear resist effects until our immunity is over + newbon->Fearless = true; + + newbon->ResistFearChance += effect_value; // these should stack + break; + } + + case SE_Fearless: + newbon->Fearless = true; + break; + + case SE_HundredHands: + { + if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + newbon->HundredHands += spells[spell_id].base[i]; + + if (effect_value > 0 && effect_value > newbon->HundredHands) + newbon->HundredHands = effect_value; //Increase Weapon Delay + else if (effect_value < 0 && effect_value < newbon->HundredHands) + newbon->HundredHands = effect_value; //Decrease Weapon Delay + break; + } + + case SE_MeleeSkillCheck: + { + if(newbon->MeleeSkillCheck < effect_value) { + newbon->MeleeSkillCheck = effect_value; + newbon->MeleeSkillCheckSkill = spells[spell_id].base2[i]==-1?255:spells[spell_id].base2[i]; + } + break; + } + + case SE_HitChance: + { + + if (RuleB(Spells, AdditiveBonusValues) && item_bonus){ + if(spells[spell_id].base2[i] == -1) + newbon->HitChanceEffect[HIGHEST_SKILL+1] += effect_value; + else + newbon->HitChanceEffect[spells[spell_id].base2[i]] += effect_value; + } + + else if(spells[spell_id].base2[i] == -1){ + + if ((effect_value < 0) && (newbon->HitChanceEffect[HIGHEST_SKILL+1] > effect_value)) + newbon->HitChanceEffect[HIGHEST_SKILL+1] = effect_value; + + else if (!newbon->HitChanceEffect[HIGHEST_SKILL+1] || + ((newbon->HitChanceEffect[HIGHEST_SKILL+1] > 0) && (newbon->HitChanceEffect[HIGHEST_SKILL+1] < effect_value))) + newbon->HitChanceEffect[HIGHEST_SKILL+1] = effect_value; + } + + else { + + if ((effect_value < 0) && (newbon->HitChanceEffect[spells[spell_id].base2[i]] > effect_value)) + newbon->HitChanceEffect[spells[spell_id].base2[i]] = effect_value; + + else if (!newbon->HitChanceEffect[spells[spell_id].base2[i]] || + ((newbon->HitChanceEffect[spells[spell_id].base2[i]] > 0) && (newbon->HitChanceEffect[spells[spell_id].base2[i]] < effect_value))) + newbon->HitChanceEffect[spells[spell_id].base2[i]] = effect_value; + } + + break; + + } + + case SE_DamageModifier: + { + if(spells[spell_id].base2[i] == -1) + newbon->DamageModifier[HIGHEST_SKILL+1] += effect_value; + else + newbon->DamageModifier[spells[spell_id].base2[i]] += effect_value; + break; + } + + case SE_MinDamageModifier: + { + if(spells[spell_id].base2[i] == -1) + newbon->MinDamageModifier[HIGHEST_SKILL+1] += effect_value; + else + newbon->MinDamageModifier[spells[spell_id].base2[i]] += effect_value; + break; + } + + case SE_StunResist: + { + if(newbon->StunResist < effect_value) + newbon->StunResist = effect_value; + break; + } + + case SE_ProcChance: + { + //multiplier is to be compatible with item effects,watching for overflow too + effect_value = effect_value<3000? effect_value : 3000; + + if (RuleB(Spells, AdditiveBonusValues) && item_bonus) + newbon->ProcChance += effect_value; + + else if((effect_value < 0) && (newbon->DoubleAttackChance > effect_value)) + newbon->ProcChance = effect_value; + + if(newbon->ProcChance < effect_value) + newbon->ProcChance = effect_value; + + break; + } + + case SE_ExtraAttackChance: + newbon->ExtraAttackChance += effect_value; + break; + + case SE_PercentXPIncrease: + { + if(newbon->XPRateMod < effect_value) + newbon->XPRateMod = effect_value; + break; + } + + case SE_DeathSave: + { + if(newbon->DeathSave[0] < effect_value) + { + newbon->DeathSave[0] = effect_value; //1='Partial' 2='Full' + newbon->DeathSave[1] = buffslot; + //These are used in later expansion spell effects. + newbon->DeathSave[2] = spells[spell_id].base2[i];//Min level for HealAmt + newbon->DeathSave[3] = spells[spell_id].max[i];//HealAmt + } + break; + } + + case SE_DivineSave: + { + if (RuleB(Spells, AdditiveBonusValues) && item_bonus) { + newbon->DivineSaveChance[0] += effect_value; + newbon->DivineSaveChance[1] = 0; + } + + else if(newbon->DivineSaveChance[0] < effect_value) + { + newbon->DivineSaveChance[0] = effect_value; + newbon->DivineSaveChance[1] = spells[spell_id].base2[i]; + //SetDeathSaveChance(true); + } + break; + } + + case SE_Flurry: + newbon->FlurryChance += effect_value; + break; + + case SE_Accuracy: + { + if ((effect_value < 0) && (newbon->Accuracy[HIGHEST_SKILL+1] > effect_value)) + newbon->Accuracy[HIGHEST_SKILL+1] = effect_value; + + else if (!newbon->Accuracy[HIGHEST_SKILL+1] || + ((newbon->Accuracy[HIGHEST_SKILL+1] > 0) && (newbon->Accuracy[HIGHEST_SKILL+1] < effect_value))) + newbon->Accuracy[HIGHEST_SKILL+1] = effect_value; + break; + } + + case SE_MaxHPChange: + newbon->MaxHPChange += effect_value; + break; + + case SE_EndurancePool: + newbon->Endurance += effect_value; + break; + + case SE_HealRate: + newbon->HealRate += effect_value; + break; + + case SE_SkillDamageTaken: + { + if(spells[spell_id].base2[i] == -1) + newbon->SkillDmgTaken[HIGHEST_SKILL+1] += effect_value; + else + newbon->SkillDmgTaken[spells[spell_id].base2[i]] += effect_value; + break; + } + + case SE_TriggerOnCast: + { + for(int e = 0; e < MAX_SPELL_TRIGGER; e++) + { + if(!newbon->SpellTriggers[e]) + { + newbon->SpellTriggers[e] = spell_id; + break; + } + } + break; + } + + case SE_SpellCritChance: + newbon->CriticalSpellChance += effect_value; + break; + + case SE_CriticalSpellChance: + { + newbon->CriticalSpellChance += effect_value; + if (spells[spell_id].base2[i] > 100) + newbon->SpellCritDmgIncrease += (spells[spell_id].base2[i] - 100); + break; + } + + case SE_SpellCritDmgIncrease: + newbon->SpellCritDmgIncrease += effect_value; + break; + + case SE_DotCritDmgIncrease: + newbon->DotCritDmgIncrease += effect_value; + break; + + case SE_CriticalHealChance2: + case SE_CriticalHealChance: + newbon->CriticalHealChance += effect_value; + break; + + case SE_CriticalHealOverTime2: + case SE_CriticalHealOverTime: + newbon->CriticalHealOverTime += effect_value; + break; + + case SE_MitigateDamageShield: + { + if (effect_value < 0) + effect_value = effect_value*-1; + + newbon->DSMitigationOffHand += effect_value; + break; + } + + case SE_CriticalDoTChance: + newbon->CriticalDoTChance += effect_value; + break; + + case SE_SpellOnKill: + { + for(int e = 0; e < MAX_SPELL_TRIGGER*3; e+=3) + { + if(!newbon->SpellOnKill[e]) + { + // Base2 = Spell to fire | Base1 = % chance | Base3 = min level + newbon->SpellOnKill[e] = spells[spell_id].base2[i]; + newbon->SpellOnKill[e+1] = effect_value; + newbon->SpellOnKill[e+2] = spells[spell_id].max[i]; + break; + } + } + break; + } + + case SE_SpellOnDeath: + { + for(int e = 0; e < MAX_SPELL_TRIGGER; e+=2) + { + if(!newbon->SpellOnDeath[e]) + { + // Base2 = Spell to fire | Base1 = % chance + newbon->SpellOnDeath[e] = spells[spell_id].base2[i]; + newbon->SpellOnDeath[e+1] = effect_value; + break; + } + } + break; + } + + case SE_CriticalDamageMob: + { + if(spells[spell_id].base2[i] == -1) + newbon->CritDmgMob[HIGHEST_SKILL+1] += effect_value; + else + newbon->CritDmgMob[spells[spell_id].base2[i]] += effect_value; + break; + } + + case SE_ReduceSkillTimer: + { + if(newbon->SkillReuseTime[spells[spell_id].base2[i]] < effect_value) + newbon->SkillReuseTime[spells[spell_id].base2[i]] = effect_value; + break; + } + + case SE_SkillDamageAmount: + { + if(spells[spell_id].base2[i] == -1) + newbon->SkillDamageAmount[HIGHEST_SKILL+1] += effect_value; + else + newbon->SkillDamageAmount[spells[spell_id].base2[i]] += effect_value; + break; + } + + case SE_GravityEffect: + newbon->GravityEffect = 1; + break; + + case SE_AntiGate: + newbon->AntiGate = true; + break; + + case SE_MagicWeapon: + newbon->MagicWeapon = true; + break; + + case SE_IncreaseBlockChance: + newbon->IncreaseBlockChance += effect_value; + break; + + case SE_PersistantCasting: + newbon->PersistantCasting += effect_value; + break; + + case SE_LimitHPPercent: + { + if(newbon->HPPercCap != 0 && newbon->HPPercCap > effect_value) + newbon->HPPercCap = effect_value; + else if(newbon->HPPercCap == 0) + newbon->HPPercCap = effect_value; + + break; + } + case SE_LimitManaPercent: + { + if(newbon->ManaPercCap != 0 && newbon->ManaPercCap > effect_value) + newbon->ManaPercCap = effect_value; + else if(newbon->ManaPercCap == 0) + newbon->ManaPercCap = effect_value; + + break; + } + case SE_LimitEndPercent: + { + if(newbon->EndPercCap != 0 && newbon->EndPercCap > effect_value) + newbon->EndPercCap = effect_value; + else if(newbon->EndPercCap == 0) + newbon->EndPercCap = effect_value; + + break; + } + + case SE_BlockNextSpellFocus: + newbon->BlockNextSpell = true; + break; + + case SE_NegateSpellEffect: + newbon->NegateEffects = true; + break; + + case SE_ImmuneFleeing: + newbon->ImmuneToFlee = true; + break; + + case SE_DelayDeath: + newbon->DelayDeath += effect_value; + break; + + case SE_SpellProcChance: + newbon->SpellProcChance += effect_value; + break; + + case SE_CharmBreakChance: + newbon->CharmBreakChance += effect_value; + break; + + case SE_BardSongRange: + newbon->SongRange += effect_value; + break; + + case SE_HPToMana: + { + //Lower the ratio the more favorable + if((!newbon->HPToManaConvert) || (newbon->HPToManaConvert >= effect_value)) + newbon->HPToManaConvert = spells[spell_id].base[i]; + break; + } + + case SE_SkillDamageAmount2: + { + if(spells[spell_id].base2[i] == -1) + newbon->SkillDamageAmount2[HIGHEST_SKILL+1] += effect_value; + else + newbon->SkillDamageAmount2[spells[spell_id].base2[i]] += effect_value; + break; + } + + case SE_NegateAttacks: + { + if (!newbon->NegateAttacks[0]){ + newbon->NegateAttacks[0] = 1; + newbon->NegateAttacks[1] = buffslot; + } + break; + } + + case SE_MitigateMeleeDamage: + { + if (newbon->MitigateMeleeRune[0] < effect_value){ + newbon->MitigateMeleeRune[0] = effect_value; + newbon->MitigateMeleeRune[1] = buffslot; + } + break; + } + + case SE_MitigateSpellDamage: + { + if (newbon->MitigateSpellRune[0] < effect_value){ + newbon->MitigateSpellRune[0] = effect_value; + newbon->MitigateSpellRune[1] = buffslot; + } + break; + } + + case SE_ManaAbsorbPercentDamage: + { + if (newbon->ManaAbsorbPercentDamage[0] < effect_value){ + newbon->ManaAbsorbPercentDamage[0] = effect_value; + newbon->ManaAbsorbPercentDamage[1] = buffslot; + } + break; + } + + case SE_ShieldBlock: + newbon->ShieldBlock += effect_value; + break; + + case SE_BlockBehind: + newbon->BlockBehind += effect_value; + break; + + case SE_Fear: + newbon->IsFeared = true; + break; + + //AA bonuses - implemented broadly into spell/item effects + case SE_FrontalStunResist: + newbon->FrontalStunResist += effect_value; + break; + + case SE_ImprovedBindWound: + newbon->BindWound += effect_value; + break; + + case SE_MaxBindWound: + newbon->MaxBindWound += effect_value; + break; + + case SE_BaseMovementSpeed: + newbon->BaseMovementSpeed += effect_value; + break; + + case SE_IncreaseRunSpeedCap: + newbon->IncreaseRunSpeedCap += effect_value; + break; + + case SE_DoubleSpecialAttack: + newbon->DoubleSpecialAttack += effect_value; + break; + + case SE_TripleBackstab: + newbon->TripleBackstab += effect_value; + break; + + case SE_FrontalBackstabMinDmg: + newbon->FrontalBackstabMinDmg = true; + break; + + case SE_FrontalBackstabChance: + newbon->FrontalBackstabChance += effect_value; + break; + + case SE_ConsumeProjectile: + newbon->ConsumeProjectile += effect_value; + break; + + case SE_ArcheryDamageModifier: + newbon->ArcheryDamageModifier += effect_value; + break; + + case SE_SecondaryDmgInc: + newbon->SecondaryDmgInc = true; + break; + + case SE_StrikeThrough2: + newbon->StrikeThrough += effect_value; + break; + + case SE_GiveDoubleAttack: + newbon->GiveDoubleAttack += effect_value; + break; + + case SE_PetCriticalHit: + newbon->PetCriticalHit += effect_value; + break; + + case SE_CombatStability: + newbon->CombatStability += effect_value; + break; + + case SE_PetAvoidance: + newbon->PetAvoidance += effect_value; + break; + + case SE_Ambidexterity: + newbon->Ambidexterity += effect_value; + break; + + case SE_PetMaxHP: + newbon->PetMaxHP += effect_value; + break; + + case SE_PetFlurry: + newbon->PetFlurry += effect_value; + break; + + case SE_GivePetGroupTarget: + newbon->GivePetGroupTarget = true; + break; + + case SE_RootBreakChance: + newbon->RootBreakChance += effect_value; + break; + + case SE_ChannelChanceItems: + newbon->ChannelChanceItems += effect_value; + break; + + case SE_ChannelChanceSpells: + newbon->ChannelChanceSpells += effect_value; + break; + + case SE_UnfailingDivinity: + newbon->UnfailingDivinity += effect_value; + break; + + + case SE_ItemHPRegenCapIncrease: + newbon->ItemHPRegenCap += effect_value; + break; + + case SE_OffhandRiposteFail: + newbon->OffhandRiposteFail += effect_value; + break; + + case SE_ItemAttackCapIncrease: + newbon->ItemATKCap += effect_value; + break; + + case SE_TwoHandBluntBlock: + newbon->TwoHandBluntBlock += effect_value; + break; + + case SE_SpellEffectResistChance: + { + for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2) + { + if(!newbon->SEResist[e] && + ((newbon->SEResist[e] = spells[spell_id].base2[i]) && (newbon->SEResist[e+1] < effect_value)) ){ + newbon->SEResist[e] = spells[spell_id].base2[i]; + newbon->SEResist[e+1] = effect_value; + break; + } + } + break; + } + + case SE_MasteryofPast: + { + if(newbon->MasteryofPast < effect_value) + newbon->MasteryofPast = effect_value; + break; + } + + case SE_GiveDoubleRiposte: + { + //Only allow for regular double riposte chance. + if(newbon->GiveDoubleRiposte[spells[spell_id].base2[i]] == 0){ + if(newbon->GiveDoubleRiposte[0] < effect_value) + newbon->GiveDoubleRiposte[0] = effect_value; + } + break; + } + + case SE_SlayUndead: + { + if(newbon->SlayUndead[1] < effect_value) + newbon->SlayUndead[0] = effect_value; // Rate + newbon->SlayUndead[1] = spells[spell_id].base2[i]; // Damage Modifier + break; + } + + + } + } +} + +void NPC::CalcItemBonuses(StatBonuses *newbon) +{ + if(newbon){ + + for(int i = 0; i < MAX_WORN_INVENTORY; i++){ + const Item_Struct *cur = database.GetItem(equipment[i]); + if(cur){ + //basic stats + newbon->AC += cur->AC; + newbon->HP += cur->HP; + newbon->Mana += cur->Mana; + newbon->Endurance += cur->Endur; + newbon->STR += (cur->AStr + cur->HeroicStr); + newbon->STA += (cur->ASta + cur->HeroicSta); + newbon->DEX += (cur->ADex + cur->HeroicDex); + newbon->AGI += (cur->AAgi + cur->HeroicAgi); + newbon->INT += (cur->AInt + cur->HeroicInt); + newbon->WIS += (cur->AWis + cur->HeroicWis); + newbon->CHA += (cur->ACha + cur->HeroicCha); + newbon->MR += (cur->MR + cur->HeroicMR); + newbon->FR += (cur->FR + cur->HeroicFR); + newbon->CR += (cur->CR + cur->HeroicCR); + newbon->PR += (cur->PR + cur->HeroicPR); + newbon->DR += (cur->DR + cur->HeroicDR); + newbon->Corrup += (cur->SVCorruption + cur->HeroicSVCorrup); + + //more complex stats + if(cur->Regen > 0) { + newbon->HPRegen += cur->Regen; + } + if(cur->ManaRegen > 0) { + newbon->ManaRegen += cur->ManaRegen; + } + if(cur->Attack > 0) { + newbon->ATK += cur->Attack; + } + if(cur->DamageShield > 0) { + newbon->DamageShield += cur->DamageShield; + } + if(cur->SpellShield > 0) { + newbon->SpellDamageShield += cur->SpellShield; + } + if(cur->Shielding > 0) { + newbon->MeleeMitigation += cur->Shielding; + } + if(cur->StunResist > 0) { + newbon->StunResist += cur->StunResist; + } + if(cur->StrikeThrough > 0) { + newbon->StrikeThrough += cur->StrikeThrough; + } + if(cur->Avoidance > 0) { + newbon->AvoidMeleeChance += cur->Avoidance; + } + if(cur->Accuracy > 0) { + newbon->HitChance += cur->Accuracy; + } + if(cur->CombatEffects > 0) { + newbon->ProcChance += cur->CombatEffects; + } + if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent effects + ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon); + } + if (cur->Haste > newbon->haste) + newbon->haste = cur->Haste; + } + } + + } +} + +void Client::CalcItemScale(bool login) +{ + bool changed = false; + + if(CalcItemScale(0, 21, login)) + changed = true; + + if(CalcItemScale(22, 30, login)) + changed = true; + + if(CalcItemScale(251, 341, login)) + changed = true; + + if(CalcItemScale(400, 405, login)) + changed = true; + + //Power Source Slot + if (GetClientVersion() >= EQClientSoF) + { + if(CalcItemScale(9999, 10000, login)) + changed = true; + } + + if(changed) + { + CalcBonuses(); + } +} + +bool Client::CalcItemScale(uint32 slot_x, uint32 slot_y, bool login) +{ + bool changed = false; + int i; + for (i = slot_x; i < slot_y; i++) { + const ItemInst* inst = m_inv[i]; + if(inst == 0) + continue; + + bool update_slot = false; + if(inst->IsScaling()) + { + EvoItemInst* e_inst = (EvoItemInst*)inst; + uint16 oldexp = e_inst->GetExp(); + + if(login) { + parse->EventItem(EVENT_ITEM_ENTERZONE, this, e_inst, e_inst->GetID(), 0); + } + parse->EventItem(EVENT_SCALE_CALC, this, e_inst, e_inst->GetID(), 0); + + if (e_inst->GetExp() != oldexp) { // if the scaling factor changed, rescale the item and update the client + e_inst->ScaleItem(); + changed = true; + update_slot = true; + } + } + + //iterate all augments + for(int x = 0; x < MAX_AUGMENT_SLOTS; ++x) + { + ItemInst * a_inst = inst->GetAugment(x); + if(!a_inst) + continue; + + if(a_inst->IsScaling()) + { + EvoItemInst* e_inst = (EvoItemInst*)a_inst; + uint16 oldexp = e_inst->GetExp(); + + if(login) { + parse->EventItem(EVENT_ITEM_ENTERZONE, this, e_inst, e_inst->GetID(), 0); + } + parse->EventItem(EVENT_SCALE_CALC, this, e_inst, e_inst->GetID(), 0); + + if (e_inst->GetExp() != oldexp) + { + e_inst->ScaleItem(); + changed = true; + update_slot = true; + } + } + } + + if(update_slot) + { + SendItemPacket(i, inst, ItemPacketCharmUpdate); + } + } + return changed; +} + +uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_effect) +{ + uint16 effect = 0; + + if (!AA) + effect = spells[spell_id].effectid[effect_index]; + else + effect = aa_effect; + + switch (effect) + { + case SE_ImprovedDamage: + return focusImprovedDamage; + case SE_ImprovedHeal: + return focusImprovedHeal; + case SE_ReduceManaCost: + return focusManaCost; + case SE_IncreaseSpellHaste: + return focusSpellHaste; + case SE_IncreaseSpellDuration: + return focusSpellDuration; + case SE_SpellDurationIncByTic: + return focusSpellDurByTic; + case SE_SwarmPetDuration: + return focusSwarmPetDuration; + case SE_IncreaseRange: + return focusRange; + case SE_ReduceReagentCost: + return focusReagentCost; + case SE_PetPowerIncrease: + return focusPetPower; + case SE_SpellResistReduction: + return focusResistRate; + case SE_SpellHateMod: + return focusSpellHateMod; + case SE_ReduceReuseTimer: + return focusReduceRecastTime; + case SE_TriggerOnCast: + //return focusTriggerOnCast; + return 0; //This is calculated as an actual bonus + case SE_SpellVulnerability: + return focusSpellVulnerability; + case SE_BlockNextSpellFocus: + return focusBlockNextSpell; + case SE_Twincast: + return focusTwincast; + case SE_SympatheticProc: + return focusSympatheticProc; + case SE_SpellDamage: + return focusSpellDamage; + case SE_FF_Damage_Amount: + return focusFF_Damage_Amount; + case SE_ImprovedDamage2: + return focusImprovedDamage2; + case SE_Empathy: + return focusAdditionalDamage; + case SE_HealRate2: + return focusHealRate; + case SE_IncreaseSpellPower: + return focusSpellEffectiveness; + case SE_IncreaseNumHits: + return focusIncreaseNumHits; + case SE_CriticalHealRate: + return focusCriticalHealRate; + case SE_AdditionalHeal2: + return focusAdditionalHeal2; + case SE_AdditionalHeal: + return focusAdditionalHeal; + } + return 0; +} + +void Mob::NegateSpellsBonuses(uint16 spell_id) +{ + if(!IsValidSpell(spell_id)) + return; + + int effect_value = 0; + + for (int i = 0; i < EFFECT_COUNT; i++) + { + if (spells[spell_id].effectid[i] == SE_NegateSpellEffect){ + + //Negate focus effects + for(int e = 0; e < HIGHEST_FOCUS+1; e++) + { + if (spellbonuses.FocusEffects[e] == spells[spell_id].base2[i]) + { + spellbonuses.FocusEffects[e] = effect_value; + continue; + } + } + + //Negate bonuses + switch (spells[spell_id].base2[i]) + { + case SE_CurrentHP: + if(spells[spell_id].base[i] == 1) { + spellbonuses.HPRegen = effect_value; + aabonuses.HPRegen = effect_value; + itembonuses.HPRegen = effect_value; + } + break; + + case SE_CurrentEndurance: + spellbonuses.EnduranceRegen = effect_value; + aabonuses.EnduranceRegen = effect_value; + itembonuses.EnduranceRegen = effect_value; + break; + + case SE_ChangeFrenzyRad: + spellbonuses.AggroRange = effect_value; + aabonuses.AggroRange = effect_value; + itembonuses.AggroRange = effect_value; + break; + + case SE_Harmony: + spellbonuses.AssistRange = effect_value; + aabonuses.AssistRange = effect_value; + itembonuses.AssistRange = effect_value; + break; + + case SE_AttackSpeed: + spellbonuses.haste = effect_value; + aabonuses.haste = effect_value; + itembonuses.haste = effect_value; + break; + + case SE_AttackSpeed2: + spellbonuses.hastetype2 = effect_value; + aabonuses.hastetype2 = effect_value; + itembonuses.hastetype2 = effect_value; + break; + + case SE_AttackSpeed3: + { + if (effect_value > 0) { + spellbonuses.hastetype3 = effect_value; + aabonuses.hastetype3 = effect_value; + itembonuses.hastetype3 = effect_value; + + } + break; + } + + case SE_AttackSpeed4: + spellbonuses.inhibitmelee = effect_value; + aabonuses.inhibitmelee = effect_value; + itembonuses.inhibitmelee = effect_value; + break; + + case SE_TotalHP: + spellbonuses.HP = effect_value; + aabonuses.HP = effect_value; + itembonuses.HP = effect_value; + break; + + case SE_ManaRegen_v2: + case SE_CurrentMana: + spellbonuses.ManaRegen = effect_value; + aabonuses.ManaRegen = effect_value; + itembonuses.ManaRegen = effect_value; + break; + + case SE_ManaPool: + spellbonuses.Mana = effect_value; + itembonuses.Mana = effect_value; + aabonuses.Mana = effect_value; + break; + + case SE_Stamina: + spellbonuses.EnduranceReduction = effect_value; + itembonuses.EnduranceReduction = effect_value; + aabonuses.EnduranceReduction = effect_value; + break; + + case SE_ACv2: + case SE_ArmorClass: + spellbonuses.AC = effect_value; + aabonuses.AC = effect_value; + itembonuses.AC = effect_value; + break; + + case SE_ATK: + spellbonuses.ATK = effect_value; + aabonuses.ATK = effect_value; + itembonuses.ATK = effect_value; + break; + + case SE_STR: + spellbonuses.STR = effect_value; + itembonuses.STR = effect_value; + aabonuses.STR = effect_value; + break; + + case SE_DEX: + spellbonuses.DEX = effect_value; + aabonuses.DEX = effect_value; + itembonuses.DEX = effect_value; + break; + + case SE_AGI: + itembonuses.AGI = effect_value; + aabonuses.AGI = effect_value; + spellbonuses.AGI = effect_value; + break; + + case SE_STA: + spellbonuses.STA = effect_value; + itembonuses.STA = effect_value; + aabonuses.STA = effect_value; + break; + + case SE_INT: + spellbonuses.INT = effect_value; + aabonuses.INT = effect_value; + itembonuses.INT = effect_value; + break; + + case SE_WIS: + spellbonuses.WIS = effect_value; + aabonuses.WIS = effect_value; + itembonuses.WIS = effect_value; + break; + + case SE_CHA: + itembonuses.CHA = effect_value; + spellbonuses.CHA = effect_value; + aabonuses.CHA = effect_value; + break; + + case SE_AllStats: + { + spellbonuses.STR = effect_value; + spellbonuses.DEX = effect_value; + spellbonuses.AGI = effect_value; + spellbonuses.STA = effect_value; + spellbonuses.INT = effect_value; + spellbonuses.WIS = effect_value; + spellbonuses.CHA = effect_value; + + itembonuses.STR = effect_value; + itembonuses.DEX = effect_value; + itembonuses.AGI = effect_value; + itembonuses.STA = effect_value; + itembonuses.INT = effect_value; + itembonuses.WIS = effect_value; + itembonuses.CHA = effect_value; + + aabonuses.STR = effect_value; + aabonuses.DEX = effect_value; + aabonuses.AGI = effect_value; + aabonuses.STA = effect_value; + aabonuses.INT = effect_value; + aabonuses.WIS = effect_value; + aabonuses.CHA = effect_value; + break; + } + + case SE_ResistFire: + spellbonuses.FR = effect_value; + itembonuses.FR = effect_value; + aabonuses.FR = effect_value; + break; + + case SE_ResistCold: + spellbonuses.CR = effect_value; + aabonuses.CR = effect_value; + itembonuses.CR = effect_value; + break; + + case SE_ResistPoison: + spellbonuses.PR = effect_value; + aabonuses.PR = effect_value; + itembonuses.PR = effect_value; + break; + + case SE_ResistDisease: + spellbonuses.DR = effect_value; + itembonuses.DR = effect_value; + aabonuses.DR = effect_value; + break; + + case SE_ResistMagic: + spellbonuses.MR = effect_value; + aabonuses.MR = effect_value; + itembonuses.MR = effect_value; + break; + + case SE_ResistAll: + { + spellbonuses.MR = effect_value; + spellbonuses.DR = effect_value; + spellbonuses.PR = effect_value; + spellbonuses.CR = effect_value; + spellbonuses.FR = effect_value; + + aabonuses.MR = effect_value; + aabonuses.DR = effect_value; + aabonuses.PR = effect_value; + aabonuses.CR = effect_value; + aabonuses.FR = effect_value; + + itembonuses.MR = effect_value; + itembonuses.DR = effect_value; + itembonuses.PR = effect_value; + itembonuses.CR = effect_value; + itembonuses.FR = effect_value; + break; + } + + case SE_ResistCorruption: + spellbonuses.Corrup = effect_value; + itembonuses.Corrup = effect_value; + aabonuses.Corrup = effect_value; + break; + + case SE_CastingLevel2: + case SE_CastingLevel: // Brilliance of Ro + spellbonuses.effective_casting_level = effect_value; + aabonuses.effective_casting_level = effect_value; + itembonuses.effective_casting_level = effect_value; + break; + + + case SE_MovementSpeed: + spellbonuses.movementspeed = effect_value; + aabonuses.movementspeed = effect_value; + itembonuses.movementspeed = effect_value; + break; + + case SE_SpellDamageShield: + spellbonuses.SpellDamageShield = effect_value; + aabonuses.SpellDamageShield = effect_value; + itembonuses.SpellDamageShield = effect_value; + break; + + case SE_DamageShield: + spellbonuses.DamageShield = effect_value; + aabonuses.DamageShield = effect_value; + itembonuses.DamageShield = effect_value; + break; + + case SE_ReverseDS: + spellbonuses.ReverseDamageShield = effect_value; + aabonuses.ReverseDamageShield = effect_value; + itembonuses.ReverseDamageShield = effect_value; + break; + + case SE_Reflect: + spellbonuses.reflect_chance = effect_value; + aabonuses.reflect_chance = effect_value; + itembonuses.reflect_chance = effect_value; + break; + + case SE_SingingSkill: + spellbonuses.singingMod = effect_value; + itembonuses.singingMod = effect_value; + aabonuses.singingMod = effect_value; + break; + + case SE_ChangeAggro: + spellbonuses.hatemod = effect_value; + itembonuses.hatemod = effect_value; + aabonuses.hatemod = effect_value; + break; + + case SE_MeleeMitigation: + spellbonuses.MeleeMitigation = effect_value; + itembonuses.MeleeMitigation = effect_value; + aabonuses.MeleeMitigation = effect_value; + break; + + case SE_CriticalHitChance: + { + for(int e = 0; e < HIGHEST_SKILL+1; e++) + { + spellbonuses.CriticalHitChance[e] = effect_value; + aabonuses.CriticalHitChance[e] = effect_value; + itembonuses.CriticalHitChance[e] = effect_value; + } + } + + case SE_CrippBlowChance: + spellbonuses.CrippBlowChance = effect_value; + aabonuses.CrippBlowChance = effect_value; + itembonuses.CrippBlowChance = effect_value; + break; + + case SE_AvoidMeleeChance: + spellbonuses.AvoidMeleeChance = effect_value; + aabonuses.AvoidMeleeChance = effect_value; + itembonuses.AvoidMeleeChance = effect_value; + break; + + case SE_RiposteChance: + spellbonuses.RiposteChance = effect_value; + aabonuses.RiposteChance = effect_value; + itembonuses.RiposteChance = effect_value; + break; + + case SE_DodgeChance: + spellbonuses.DodgeChance = effect_value; + aabonuses.DodgeChance = effect_value; + itembonuses.DodgeChance = effect_value; + break; + + case SE_ParryChance: + spellbonuses.ParryChance = effect_value; + aabonuses.ParryChance = effect_value; + itembonuses.ParryChance = effect_value; + break; + + case SE_DualWieldChance: + spellbonuses.DualWieldChance = effect_value; + aabonuses.DualWieldChance = effect_value; + itembonuses.DualWieldChance = effect_value; + break; + + case SE_DoubleAttackChance: + spellbonuses.DoubleAttackChance = effect_value; + aabonuses.DoubleAttackChance = effect_value; + itembonuses.DoubleAttackChance = effect_value; + break; + + case SE_TripleAttackChance: + spellbonuses.TripleAttackChance = effect_value; + aabonuses.TripleAttackChance = effect_value; + itembonuses.TripleAttackChance = effect_value; + break; + + case SE_MeleeLifetap: + spellbonuses.MeleeLifetap = effect_value; + aabonuses.MeleeLifetap = effect_value; + itembonuses.MeleeLifetap = effect_value; + break; + + case SE_AllInstrumentMod: + { + spellbonuses.singingMod = effect_value; + spellbonuses.brassMod = effect_value; + spellbonuses.percussionMod = effect_value; + spellbonuses.windMod = effect_value; + spellbonuses.stringedMod = effect_value; + + itembonuses.singingMod = effect_value; + itembonuses.brassMod = effect_value; + itembonuses.percussionMod = effect_value; + itembonuses.windMod = effect_value; + itembonuses.stringedMod = effect_value; + + aabonuses.singingMod = effect_value; + aabonuses.brassMod = effect_value; + aabonuses.percussionMod = effect_value; + aabonuses.windMod = effect_value; + aabonuses.stringedMod = effect_value; + break; + } + + case SE_ResistSpellChance: + spellbonuses.ResistSpellChance = effect_value; + aabonuses.ResistSpellChance = effect_value; + itembonuses.ResistSpellChance = effect_value; + break; + + case SE_ResistFearChance: + spellbonuses.Fearless = false; + spellbonuses.ResistFearChance = effect_value; + aabonuses.ResistFearChance = effect_value; + itembonuses.ResistFearChance = effect_value; + break; + + case SE_Fearless: + spellbonuses.Fearless = false; + aabonuses.Fearless = false; + itembonuses.Fearless = false; + break; + + case SE_HundredHands: + spellbonuses.HundredHands = effect_value; + aabonuses.HundredHands = effect_value; + itembonuses.HundredHands = effect_value; + break; + + case SE_MeleeSkillCheck: + { + spellbonuses.MeleeSkillCheck = effect_value; + spellbonuses.MeleeSkillCheckSkill = effect_value; + break; + } + + case SE_HitChance: + { + for(int e = 0; e < HIGHEST_SKILL+1; e++) + { + spellbonuses.HitChanceEffect[e] = effect_value; + aabonuses.HitChanceEffect[e] = effect_value; + itembonuses.HitChanceEffect[e] = effect_value; + } + break; + } + + case SE_DamageModifier: + { + for(int e = 0; e < HIGHEST_SKILL+1; e++) + { + spellbonuses.DamageModifier[e] = effect_value; + aabonuses.DamageModifier[e] = effect_value; + itembonuses.DamageModifier[e] = effect_value; + } + break; + } + + case SE_MinDamageModifier: + { + for(int e = 0; e < HIGHEST_SKILL+1; e++) + { + spellbonuses.MinDamageModifier[e] = effect_value; + aabonuses.MinDamageModifier[e] = effect_value; + itembonuses.MinDamageModifier[e] = effect_value; + } + break; + } + + case SE_StunResist: + spellbonuses.StunResist = effect_value; + aabonuses.StunResist = effect_value; + itembonuses.StunResist = effect_value; + break; + + case SE_ProcChance: + spellbonuses.ProcChance = effect_value; + aabonuses.ProcChance = effect_value; + itembonuses.ProcChance = effect_value; + break; + + case SE_ExtraAttackChance: + spellbonuses.ExtraAttackChance = effect_value; + aabonuses.ExtraAttackChance = effect_value; + itembonuses.ExtraAttackChance = effect_value; + break; + + case SE_PercentXPIncrease: + spellbonuses.XPRateMod = effect_value; + aabonuses.XPRateMod = effect_value; + itembonuses.XPRateMod = effect_value; + break; + + case SE_Flurry: + spellbonuses.FlurryChance = effect_value; + aabonuses.FlurryChance = effect_value; + itembonuses.FlurryChance = effect_value; + break; + + case SE_Accuracy: + { + spellbonuses.Accuracy[HIGHEST_SKILL+1] = effect_value; + itembonuses.Accuracy[HIGHEST_SKILL+1] = effect_value; + + for(int e = 0; e < HIGHEST_SKILL+1; e++) + { + aabonuses.Accuracy[e] = effect_value; + } + break; + } + + case SE_MaxHPChange: + spellbonuses.MaxHPChange = effect_value; + aabonuses.MaxHPChange = effect_value; + itembonuses.MaxHPChange = effect_value; + break; + + case SE_EndurancePool: + spellbonuses.Endurance = effect_value; + aabonuses.Endurance = effect_value; + itembonuses.Endurance = effect_value; + break; + + case SE_HealRate: + spellbonuses.HealRate = effect_value; + aabonuses.HealRate = effect_value; + itembonuses.HealRate = effect_value; + break; + + case SE_SkillDamageTaken: + { + for(int e = 0; e < HIGHEST_SKILL+1; e++) + { + spellbonuses.SkillDmgTaken[e] = effect_value; + aabonuses.SkillDmgTaken[e] = effect_value; + itembonuses.SkillDmgTaken[e] = effect_value; + + } + break; + } + + case SE_TriggerOnCast: + { + for(int e = 0; e < MAX_SPELL_TRIGGER; e++) + { + spellbonuses.SpellTriggers[e] = effect_value; + aabonuses.SpellTriggers[e] = effect_value; + itembonuses.SpellTriggers[e] = effect_value; + } + break; + } + + case SE_SpellCritChance: + spellbonuses.CriticalSpellChance = effect_value; + aabonuses.CriticalSpellChance = effect_value; + itembonuses.CriticalSpellChance = effect_value; + break; + + case SE_CriticalSpellChance: + spellbonuses.CriticalSpellChance = effect_value; + spellbonuses.SpellCritDmgIncrease = effect_value; + aabonuses.CriticalSpellChance = effect_value; + aabonuses.SpellCritDmgIncrease = effect_value; + itembonuses.CriticalSpellChance = effect_value; + itembonuses.SpellCritDmgIncrease = effect_value; + break; + + case SE_SpellCritDmgIncrease: + spellbonuses.SpellCritDmgIncrease = effect_value; + aabonuses.SpellCritDmgIncrease = effect_value; + itembonuses.SpellCritDmgIncrease = effect_value; + break; + + case SE_DotCritDmgIncrease: + spellbonuses.DotCritDmgIncrease = effect_value; + aabonuses.DotCritDmgIncrease = effect_value; + itembonuses.DotCritDmgIncrease = effect_value; + break; + + case SE_CriticalHealChance2: + case SE_CriticalHealChance: + spellbonuses.CriticalHealChance = effect_value; + aabonuses.CriticalHealChance = effect_value; + itembonuses.CriticalHealChance = effect_value; + break; + + case SE_CriticalHealOverTime2: + case SE_CriticalHealOverTime: + spellbonuses.CriticalHealOverTime = effect_value; + aabonuses.CriticalHealOverTime = effect_value; + itembonuses.CriticalHealOverTime = effect_value; + break; + + case SE_MitigateDamageShield: + spellbonuses.DSMitigationOffHand = effect_value; + itembonuses.DSMitigationOffHand = effect_value; + aabonuses.DSMitigationOffHand = effect_value; + break; + + case SE_CriticalDoTChance: + spellbonuses.CriticalDoTChance = effect_value; + aabonuses.CriticalDoTChance = effect_value; + itembonuses.CriticalDoTChance = effect_value; + break; + + case SE_SpellOnKill: + { + for(int e = 0; e < MAX_SPELL_TRIGGER*3; e=3) + { + spellbonuses.SpellOnKill[e] = effect_value; + spellbonuses.SpellOnKill[e+1] = effect_value; + spellbonuses.SpellOnKill[e+2] = effect_value; + + aabonuses.SpellOnKill[e] = effect_value; + aabonuses.SpellOnKill[e+1] = effect_value; + aabonuses.SpellOnKill[e+2] = effect_value; + + itembonuses.SpellOnKill[e] = effect_value; + itembonuses.SpellOnKill[e+1] = effect_value; + itembonuses.SpellOnKill[e+2] = effect_value; + } + break; + } + + /* + case SE_SpellOnDeath: + { + for(int e = 0; e < MAX_SPELL_TRIGGER; e=2) + { + spellbonuses.SpellOnDeath[e] = SPELL_UNKNOWN; + spellbonuses.SpellOnDeath[e+1] = effect_value; + } + break; + } + */ + + case SE_CriticalDamageMob: + { + for(int e = 0; e < HIGHEST_SKILL+1; e++) + { + spellbonuses.CritDmgMob[e] = effect_value; + aabonuses.CritDmgMob[e] = effect_value; + itembonuses.CritDmgMob[e] = effect_value; + } + break; + } + + case SE_SkillDamageAmount: + { + for(int e = 0; e < HIGHEST_SKILL+1; e++) + { + spellbonuses.SkillDamageAmount[e] = effect_value; + aabonuses.SkillDamageAmount[e] = effect_value; + itembonuses.SkillDamageAmount[e] = effect_value; + } + break; + } + + case SE_IncreaseBlockChance: + spellbonuses.IncreaseBlockChance = effect_value; + aabonuses.IncreaseBlockChance = effect_value; + itembonuses.IncreaseBlockChance = effect_value; + break; + + case SE_PersistantCasting: + spellbonuses.PersistantCasting = effect_value; + itembonuses.PersistantCasting = effect_value; + aabonuses.PersistantCasting = effect_value; + break; + + case SE_ImmuneFleeing: + spellbonuses.ImmuneToFlee = false; + break; + + case SE_DelayDeath: + spellbonuses.DelayDeath = effect_value; + aabonuses.DelayDeath = effect_value; + itembonuses.DelayDeath = effect_value; + break; + + case SE_SpellProcChance: + spellbonuses.SpellProcChance = effect_value; + itembonuses.SpellProcChance = effect_value; + aabonuses.SpellProcChance = effect_value; + break; + + case SE_CharmBreakChance: + spellbonuses.CharmBreakChance = effect_value; + aabonuses.CharmBreakChance = effect_value; + itembonuses.CharmBreakChance = effect_value; + break; + + case SE_BardSongRange: + spellbonuses.SongRange = effect_value; + aabonuses.SongRange = effect_value; + itembonuses.SongRange = effect_value; + break; + + case SE_SkillDamageAmount2: + { + for(int e = 0; e < HIGHEST_SKILL+1; e++) + { + spellbonuses.SkillDamageAmount2[e] = effect_value; + aabonuses.SkillDamageAmount2[e] = effect_value; + itembonuses.SkillDamageAmount2[e] = effect_value; + } + break; + } + + case SE_NegateAttacks: + spellbonuses.NegateAttacks[0] = effect_value; + spellbonuses.NegateAttacks[1] = effect_value; + break; + + case SE_MitigateMeleeDamage: + spellbonuses.MitigateMeleeRune[0] = effect_value; + spellbonuses.MitigateMeleeRune[1] = -1; + break; + + case SE_MitigateSpellDamage: + spellbonuses.MitigateSpellRune[0] = effect_value; + spellbonuses.MitigateSpellRune[1] = -1; + break; + + case SE_ManaAbsorbPercentDamage: + spellbonuses.ManaAbsorbPercentDamage[0] = effect_value; + spellbonuses.ManaAbsorbPercentDamage[1] = -1; + break; + + case SE_ShieldBlock: + spellbonuses.ShieldBlock = effect_value; + aabonuses.ShieldBlock = effect_value; + itembonuses.ShieldBlock = effect_value; + + case SE_BlockBehind: + spellbonuses.BlockBehind = effect_value; + aabonuses.BlockBehind = effect_value; + itembonuses.BlockBehind = effect_value; + break; + + case SE_Fear: + spellbonuses.IsFeared = false; + break; + + case SE_FrontalStunResist: + spellbonuses.FrontalStunResist = effect_value; + aabonuses.FrontalStunResist = effect_value; + itembonuses.FrontalStunResist = effect_value; + break; + + case SE_ImprovedBindWound: + aabonuses.BindWound = effect_value; + itembonuses.BindWound = effect_value; + spellbonuses.BindWound = effect_value; + break; + + case SE_MaxBindWound: + spellbonuses.MaxBindWound = effect_value; + aabonuses.MaxBindWound = effect_value; + itembonuses.MaxBindWound = effect_value; + break; + + case SE_BaseMovementSpeed: + spellbonuses.BaseMovementSpeed = effect_value; + aabonuses.BaseMovementSpeed = effect_value; + itembonuses.BaseMovementSpeed = effect_value; + break; + + case SE_IncreaseRunSpeedCap: + itembonuses.IncreaseRunSpeedCap = effect_value; + aabonuses.IncreaseRunSpeedCap = effect_value; + spellbonuses.IncreaseRunSpeedCap = effect_value; + break; + + case SE_DoubleSpecialAttack: + spellbonuses.DoubleSpecialAttack = effect_value; + aabonuses.DoubleSpecialAttack = effect_value; + itembonuses.DoubleSpecialAttack = effect_value; + break; + + case SE_TripleBackstab: + spellbonuses.TripleBackstab = effect_value; + aabonuses.TripleBackstab = effect_value; + itembonuses.TripleBackstab = effect_value; + break; + + case SE_FrontalBackstabMinDmg: + spellbonuses.FrontalBackstabMinDmg = false; + break; + + case SE_FrontalBackstabChance: + spellbonuses.FrontalBackstabChance = effect_value; + aabonuses.FrontalBackstabChance = effect_value; + itembonuses.FrontalBackstabChance = effect_value; + break; + + case SE_ConsumeProjectile: + spellbonuses.ConsumeProjectile = effect_value; + aabonuses.ConsumeProjectile = effect_value; + itembonuses.ConsumeProjectile = effect_value; + break; + + case SE_ArcheryDamageModifier: + spellbonuses.ArcheryDamageModifier = effect_value; + aabonuses.ArcheryDamageModifier = effect_value; + itembonuses.ArcheryDamageModifier = effect_value; + break; + + case SE_SecondaryDmgInc: + spellbonuses.SecondaryDmgInc = false; + aabonuses.SecondaryDmgInc = false; + itembonuses.SecondaryDmgInc = false; + break; + + case SE_StrikeThrough2: + spellbonuses.StrikeThrough = effect_value; + aabonuses.StrikeThrough = effect_value; + itembonuses.StrikeThrough = effect_value; + break; + + case SE_GiveDoubleAttack: + spellbonuses.GiveDoubleAttack = effect_value; + aabonuses.GiveDoubleAttack = effect_value; + itembonuses.GiveDoubleAttack = effect_value; + break; + + case SE_PetCriticalHit: + spellbonuses.PetCriticalHit = effect_value; + aabonuses.PetCriticalHit = effect_value; + itembonuses.PetCriticalHit = effect_value; + break; + + case SE_CombatStability: + spellbonuses.CombatStability = effect_value; + aabonuses.CombatStability = effect_value; + itembonuses.CombatStability = effect_value; + break; + + case SE_PetAvoidance: + spellbonuses.PetAvoidance = effect_value; + aabonuses.PetAvoidance = effect_value; + itembonuses.PetAvoidance = effect_value; + break; + + case SE_Ambidexterity: + spellbonuses.Ambidexterity = effect_value; + aabonuses.Ambidexterity = effect_value; + itembonuses.Ambidexterity = effect_value; + break; + + case SE_PetMaxHP: + spellbonuses.PetMaxHP = effect_value; + aabonuses.PetMaxHP = effect_value; + itembonuses.PetMaxHP = effect_value; + break; + + case SE_PetFlurry: + spellbonuses.PetFlurry = effect_value; + aabonuses.PetFlurry = effect_value; + itembonuses.PetFlurry = effect_value; + break; + + case SE_GivePetGroupTarget: + spellbonuses.GivePetGroupTarget = false; + aabonuses.GivePetGroupTarget = false; + itembonuses.GivePetGroupTarget = false; + break; + + case SE_RootBreakChance: + spellbonuses.RootBreakChance = effect_value; + aabonuses.RootBreakChance = effect_value; + itembonuses.RootBreakChance = effect_value; + break; + + case SE_ChannelChanceItems: + spellbonuses.ChannelChanceItems = effect_value; + aabonuses.ChannelChanceItems = effect_value; + itembonuses.ChannelChanceItems = effect_value; + break; + + case SE_ChannelChanceSpells: + spellbonuses.ChannelChanceSpells = effect_value; + aabonuses.ChannelChanceSpells = effect_value; + itembonuses.ChannelChanceSpells = effect_value; + break; + + case SE_UnfailingDivinity: + spellbonuses.UnfailingDivinity = effect_value; + aabonuses.UnfailingDivinity = effect_value; + itembonuses.UnfailingDivinity = effect_value; + break; + + case SE_ItemHPRegenCapIncrease: + spellbonuses.ItemHPRegenCap = effect_value; + aabonuses.ItemHPRegenCap = effect_value; + itembonuses.ItemHPRegenCap = effect_value; + break; + + case SE_OffhandRiposteFail: + spellbonuses.OffhandRiposteFail = effect_value; + aabonuses.OffhandRiposteFail = effect_value; + itembonuses.OffhandRiposteFail = effect_value; + break; + + case SE_ItemAttackCapIncrease: + aabonuses.ItemATKCap = effect_value; + itembonuses.ItemATKCap = effect_value; + spellbonuses.ItemATKCap = effect_value; + break; + + case SE_SpellEffectResistChance: + { + for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2) + { + spellbonuses.SEResist[e] = effect_value; + spellbonuses.SEResist[e+1] = effect_value; + } + break; + } + + case SE_MasteryofPast: + spellbonuses.MasteryofPast = effect_value; + aabonuses.MasteryofPast = effect_value; + itembonuses.MasteryofPast = effect_value; + break; + + case SE_GiveDoubleRiposte: + spellbonuses.GiveDoubleRiposte[0] = effect_value; + itembonuses.GiveDoubleRiposte[0] = effect_value; + aabonuses.GiveDoubleRiposte[0] = effect_value; + break; + + case SE_SlayUndead: + spellbonuses.SlayUndead[0] = effect_value; + spellbonuses.SlayUndead[1] = effect_value; + itembonuses.SlayUndead[0] = effect_value; + itembonuses.SlayUndead[1] = effect_value; + aabonuses.SlayUndead[0] = effect_value; + aabonuses.SlayUndead[1] = effect_value; + break; + + } + } + } +} \ No newline at end of file diff --git a/zone/bot.cpp b/zone/bot.cpp new file mode 100644 index 000000000..774fb3f73 --- /dev/null +++ b/zone/bot.cpp @@ -0,0 +1,17491 @@ +#ifdef BOTS + +#include "bot.h" +#include "object.h" +#include "doors.h" +#include "QuestParserCollection.h" + +extern volatile bool ZoneLoaded; + +// This constructor is used during the bot create command +Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, 0, 0, 0, 0, 0, 0, false), rest_timer(1) { + if(botOwner) { + this->SetBotOwner(botOwner); + this->_botOwnerCharacterID = botOwner->CharacterID(); + } + else { + this->SetBotOwner(0); + this->_botOwnerCharacterID = 0; + } + + _guildRank = 0; + _guildId = 0; + + _lastTotalPlayTime = 0; + _startTotalPlayTime = time(&_startTotalPlayTime); + _lastZoneId = 0; + + _baseMR = npcTypeData.MR; + _baseCR = npcTypeData.CR; + _baseDR = npcTypeData.DR; + _baseFR = npcTypeData.FR; + _basePR = npcTypeData.PR; + _baseCorrup = npcTypeData.Corrup; + _baseAC = npcTypeData.AC; + _baseSTR = npcTypeData.STR; + _baseSTA = npcTypeData.STA; + _baseDEX = npcTypeData.DEX; + _baseAGI = npcTypeData.AGI; + _baseINT = npcTypeData.INT; + _baseWIS = npcTypeData.WIS; + _baseCHA = npcTypeData.CHA; + _baseATK = npcTypeData.ATK; + _baseRace = npcTypeData.race; + _baseGender = npcTypeData.gender; + RestRegenHP = 0; + RestRegenMana = 0; + RestRegenEndurance = 0; + + SetBotID(0); + SetBotSpellID(0); + SetSpawnStatus(false); + SetBotArcher(false); + SetBotCharmer(false); + SetPetChooser(false); + SetRangerAutoWeaponSelect(false); + SetHasBeenSummoned(false); + SetTaunting(GetClass() == WARRIOR); + SetDefaultBotStance(); + SetInHealRotation(false); + SetHealRotationActive(false); + SetHasHealedThisCycle(false); + ClearHealRotationLeader(); + ClearHealRotationTargets(); + ClearHealRotationMembers(); + SetHealRotationNextHealTime(0); + SetHealRotationTimer(0); + SetNumHealRotationMembers(0); + CalcChanceToCast(); + rest_timer.Disable(); + + SetFollowDistance(184); + + // Do this once and only in this constructor + GenerateAppearance(); + + GenerateBaseStats(); + GenerateArmorClass(); + + // Calculate HitPoints Last As It Uses Base Stats + cur_hp = GenerateBaseHitPoints(); + cur_mana = GenerateBaseManaPoints(); + cur_end = CalcBaseEndurance(); + + hp_regen = CalcHPRegen(); + mana_regen = CalcManaRegen(); + end_regen = CalcEnduranceRegen(); + + for (int i = 0; i < MaxTimer; i++) { + timers[i] = 0; + } + + for(int i = 0; i < MaxHealRotationTargets; i++) { + _healRotationTargets[i] = 0; + } + + strcpy(this->name, this->GetCleanName()); +} + +// This constructor is used when the bot is loaded out of the database +Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType npcTypeData) : NPC(&npcTypeData, 0, 0, 0, 0, 0, 0, false), rest_timer(1) { + this->_botOwnerCharacterID = botOwnerCharacterID; + + if(this->_botOwnerCharacterID > 0) { + this->SetBotOwner(entity_list.GetClientByCharID(this->_botOwnerCharacterID)); + } + + _guildRank = 0; + _guildId = 0; + + _lastTotalPlayTime = totalPlayTime; + _startTotalPlayTime = time(&_startTotalPlayTime); + _lastZoneId = lastZoneId; + berserk = false; + + _baseMR = npcTypeData.MR; + _baseCR = npcTypeData.CR; + _baseDR = npcTypeData.DR; + _baseFR = npcTypeData.FR; + _basePR = npcTypeData.PR; + _baseCorrup = npcTypeData.Corrup; + _baseAC = npcTypeData.AC; + _baseSTR = npcTypeData.STR; + _baseSTA = npcTypeData.STA; + _baseDEX = npcTypeData.DEX; + _baseAGI = npcTypeData.AGI; + _baseINT = npcTypeData.INT; + _baseWIS = npcTypeData.WIS; + _baseCHA = npcTypeData.CHA; + _baseATK = npcTypeData.ATK; + _baseRace = npcTypeData.race; + _baseGender = npcTypeData.gender; + cur_hp = npcTypeData.cur_hp; + cur_mana = npcTypeData.Mana; + + RestRegenHP = 0; + RestRegenMana = 0; + RestRegenEndurance = 0; + + SetBotID(botID); + SetBotSpellID(botSpellsID); + SetSpawnStatus(false); + SetBotArcher(false); + SetBotCharmer(false); + SetPetChooser(false); + SetRangerAutoWeaponSelect(false); + SetHasBeenSummoned(false); + LoadStance(); + SetTaunting((GetClass() == WARRIOR || GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) && (GetBotStance() == BotStanceAggressive)); + SetGroupMessagesOn(GetClass() == CLERIC || GetClass() == DRUID || GetClass() == SHAMAN || GetClass() == ENCHANTER); + SetInHealRotation(false); + SetHealRotationActive(false); + SetHasHealedThisCycle(false); + SetHealRotationUseFastHeals(false); + ClearHealRotationLeader(); + ClearHealRotationTargets(); + ClearHealRotationMembers(); + SetHealRotationNextHealTime(0); + SetHealRotationTimer(0); + SetNumHealRotationMembers(0); + CalcChanceToCast(); + rest_timer.Disable(); + + SetFollowDistance(184); + + strcpy(this->name, this->GetCleanName()); + + database.GetBotInspectMessage(this->GetBotID(), &_botInspectMessage); + + LoadGuildMembership(&_guildId, &_guildRank, &_guildName); + + std::string TempErrorMessage; + + EquipBot(&TempErrorMessage); + + if(!TempErrorMessage.empty()) { + // TODO: log error message to zone error log + if(GetBotOwner()) + GetBotOwner()->Message(13, TempErrorMessage.c_str()); + } + + for (int i = 0; i < MaxTimer; i++) { + timers[i] = 0; + } + + for(int i = 0; i < MaxHealRotationTargets; i++) { + _healRotationTargets[i] = 0; + } + + GenerateBaseStats(); + + LoadTimers(); + LoadAAs(); + LoadBuffs(); + + CalcBotStats(false); + + hp_regen = CalcHPRegen(); + mana_regen = CalcManaRegen(); + end_regen = CalcEnduranceRegen(); + + if(cur_hp > max_hp) + cur_hp = max_hp; + if(cur_hp <= 0) { + SetHP(max_hp/5); + SetMana(0); + BuffFadeAll(); + SpellOnTarget(756, this); // Rezz effects + } + if(cur_mana > max_mana) + cur_mana = max_mana; + cur_end = max_end; +} + +Bot::~Bot() { + AI_Stop(); + + if(HasGroup()) + Bot::RemoveBotFromGroup(this, GetGroup()); + + if(HasPet()) + GetPet()->Depop(); + + entity_list.RemoveBot(GetID()); +} + +void Bot::SetBotID(uint32 botID) { + this->_botID = botID; + this->npctype_id = botID; +} + +void Bot::SetBotSpellID(uint32 newSpellID) { + this->npc_spells_id = newSpellID; +} + +uint32 Bot::GetBotArcheryRange() { + uint32 result = 0; + + ItemInst* rangeItem = GetBotItem(SLOT_RANGE); + + if(!rangeItem) + return 0; + + const Item_Struct* botweapon = rangeItem->GetItem(); + + uint32 archeryMaterial; + uint32 archeryColor; + uint32 archeryBowID; + uint32 archeryAmmoID; + + if(botweapon && botweapon->ItemType == ItemTypeBow) { + uint32 range = 0; + + archeryMaterial = atoi(botweapon->IDFile + 2); + archeryBowID = botweapon->ID; + archeryColor = botweapon->Color; + range =+ botweapon->Range; + + rangeItem = GetBotItem(SLOT_AMMO); + if(rangeItem) + botweapon = rangeItem->GetItem(); + + if(!botweapon || (botweapon->ItemType != ItemTypeArrow)) { + return 0; + } + + range += botweapon->Range; + + archeryAmmoID = botweapon->ID; + + result = range; + } + + return result; +} + +void Bot::ChangeBotArcherWeapons(bool isArcher) { + if((GetClass()==WARRIOR) || (GetClass()==PALADIN) || (GetClass()==RANGER) + || (GetClass()==SHADOWKNIGHT) || (GetClass()==ROGUE)) + { + if(!isArcher) { + BotAddEquipItem(SLOT_PRIMARY, GetBotItemBySlot(SLOT_PRIMARY)); + BotAddEquipItem(SLOT_SECONDARY, GetBotItemBySlot(SLOT_SECONDARY)); + //archerbot->SendWearChange(MATERIAL_PRIMARY); + //archerbot->SendWearChange(MATERIAL_SECONDARY); + SetAttackTimer(); + Say("My blade is ready."); + } + else { + //archerbot->SendWearChange(MATERIAL_PRIMARY); + //archerbot->SendWearChange(MATERIAL_SECONDARY); + BotRemoveEquipItem(SLOT_PRIMARY); + BotRemoveEquipItem(SLOT_SECONDARY); + //archerbot->SendBotArcheryWearChange(MATERIAL_PRIMARY, archeryMaterial, archeryColor); + BotAddEquipItem(SLOT_AMMO, GetBotItemBySlot(SLOT_AMMO)); + BotAddEquipItem(SLOT_SECONDARY, GetBotItemBySlot(SLOT_RANGE)); + SetAttackTimer(); + Say("My bow is true and ready."); + } + } + else { + Say("I don't know how to use a bow."); + } +} + +void Bot::Sit() { + if(IsMoving()) { + moved = false; + // SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); + SendPosition(); + SetMoving(false); + tar_ndx = 0; + } + + SetAppearance(eaSitting); +} + +void Bot::Stand() { + SetAppearance(eaStanding); +} + +bool Bot::IsSitting() { + bool result = false; + + if(GetAppearance() == eaSitting && !IsMoving()) + result = true; + + return result; +} + +bool Bot::IsStanding() { + bool result = false; + + if(GetAppearance() == eaStanding) + result = true; + + 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 BotNPCType; + int CopyLength = 0; + + CopyLength = botName.copy(BotNPCType.name, 63); + BotNPCType.name[CopyLength] = '\0'; + CopyLength = 0; + + CopyLength = botLastName.copy(BotNPCType.lastname, 69); + BotNPCType.lastname[CopyLength] = '\0'; + CopyLength = 0; + + BotNPCType.npc_spells_id = botSpellsID; + BotNPCType.level = botLevel; + BotNPCType.race = botRace; + BotNPCType.class_ = botClass; + BotNPCType.gender = gender; + BotNPCType.size = size; + BotNPCType.luclinface = face; + BotNPCType.hairstyle = hairStyle; + BotNPCType.haircolor = hairColor; + BotNPCType.eyecolor1 = eyeColor; + BotNPCType.eyecolor2 = eyeColor2; + BotNPCType.beardcolor = beardColor; + BotNPCType.beard = beard; + BotNPCType.drakkin_heritage = drakkinHeritage; + BotNPCType.drakkin_tattoo = drakkinTattoo; + BotNPCType.drakkin_details = drakkinDetails; + BotNPCType.cur_hp = hp; + BotNPCType.Mana = mana; + BotNPCType.MR = mr; + BotNPCType.CR = cr; + BotNPCType.DR = dr; + BotNPCType.FR = fr; + BotNPCType.PR = pr; + BotNPCType.Corrup = corrup; + BotNPCType.AC = ac; + BotNPCType.STR = str; + BotNPCType.STA = sta; + BotNPCType.DEX = dex; + BotNPCType.AGI = agi; + BotNPCType.INT = _int; + BotNPCType.WIS = wis; + BotNPCType.CHA = cha; + BotNPCType.ATK = attack; + + BotNPCType.npc_id = 0; + BotNPCType.texture = 0; + BotNPCType.d_meele_texture1 = 0; + BotNPCType.d_meele_texture2 = 0; + BotNPCType.qglobal = false; + BotNPCType.attack_speed = 0; + BotNPCType.runspeed = 1.25; + BotNPCType.bodytype = 1; + BotNPCType.findable = 0; + BotNPCType.hp_regen = 1; + BotNPCType.mana_regen = 1; + BotNPCType.maxlevel = botLevel; + + return BotNPCType; +} + +NPCType Bot::CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender) { + NPCType Result; + int CopyLength = 0; + + CopyLength = botName.copy(Result.name, 63); + Result.name[CopyLength] = '\0'; + CopyLength = 0; + + CopyLength = botLastName.copy(Result.lastname, 69); + Result.lastname[CopyLength] = '\0'; + CopyLength = 0; + + Result.level = botLevel; + Result.race = botRace; + Result.class_ = botClass; + Result.gender = gender; + + // default values + Result.maxlevel = botLevel; + Result.size = 6.0; + Result.npc_id = 0; + Result.cur_hp = 0; + Result.drakkin_details = 0; + Result.drakkin_heritage = 0; + Result.drakkin_tattoo = 0; + Result.runspeed = 1.25; + Result.bodytype = 1; + Result.findable = 0; + Result.hp_regen = 1; + Result.mana_regen = 1; + Result.texture = 0; + Result.d_meele_texture1 = 0; + Result.d_meele_texture2 = 0; + Result.qglobal = false; + Result.npc_spells_id = 0; + Result.attack_speed = 0; + Result.STR = 75; + Result.STA = 75; + Result.DEX = 75; + Result.AGI = 75; + Result.WIS = 75; + Result.INT = 75; + Result.CHA = 75; + Result.ATK = 75; + Result.MR = 25; + Result.FR = 25; + Result.DR = 15; + Result.PR = 15; + Result.CR = 25; + Result.Corrup = 15; + Result.AC = 12; + + return Result; +} + +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; + + switch(this->GetClass()) { + case 1: // Warrior + Strength += 10; + Stamina += 20; + Agility += 10; + Dexterity += 10; + Attack += 12; + break; + case 2: // Cleric + BotSpellID = 701; + Strength += 5; + Stamina += 5; + Agility += 10; + Wisdom += 30; + Attack += 8; + break; + case 3: // Paladin + BotSpellID = 708; + Strength += 15; + Stamina += 5; + Wisdom += 15; + Charisma += 10; + Dexterity += 5; + Attack += 17; + break; + case 4: // Ranger + BotSpellID = 710; + Strength += 15; + Stamina += 10; + Agility += 10; + Wisdom += 15; + Attack += 17; + break; + case 5: // Shadowknight + BotSpellID = 709; + Strength += 10; + Stamina += 15; + Intelligence += 20; + Charisma += 5; + Attack += 17; + break; + case 6: // Druid + BotSpellID = 707; + Stamina += 15; + Wisdom += 35; + Attack += 5; + break; + case 7: // Monk + Strength += 5; + Stamina += 15; + Agility += 15; + Dexterity += 15; + Attack += 17; + break; + case 8: // Bard + BotSpellID = 711; + Strength += 15; + Dexterity += 10; + Charisma += 15; + Intelligence += 10; + Attack += 17; + break; + case 9: // Rogue + Strength += 10; + Stamina += 20; + Agility += 10; + Dexterity += 10; + Attack += 12; + break; + case 10: // Shaman + BotSpellID = 706; + Stamina += 10; + Wisdom += 30; + Charisma += 10; + Attack += 28; + break; + case 11: // Necromancer + BotSpellID = 703; + Dexterity += 10; + Agility += 10; + Intelligence += 30; + Attack += 5; + break; + case 12: // Wizard + BotSpellID = 702; + Stamina += 20; + Intelligence += 30; + Attack += 5; + break; + case 13: // Magician + BotSpellID = 704; + Stamina += 20; + Intelligence += 30; + Attack += 5; + break; + case 14: // Enchanter + BotSpellID = 705; + Intelligence += 25; + Charisma += 25; + Attack += 5; + break; + case 15: // Beastlord + BotSpellID = 712; + Stamina += 10; + Agility += 10; + Dexterity += 5; + Wisdom += 20; + Charisma += 5; + Attack += 31; + break; + case 16: // Berserker + Strength += 10; + Stamina += 15; + Dexterity += 15; + Agility += 10; + Attack += 25; + break; + } + + float BotSize = GetSize(); + + switch(this->GetRace()) { + case 1: // Humans have no race bonus + break; + case 2: // Barbarian + Strength += 28; + Stamina += 20; + Agility += 7; + Dexterity -= 5; + Wisdom -= 5; + Intelligence -= 10; + Charisma -= 20; + BotSize = 7.0; + ColdResist += 10; + break; + case 3: // Erudite + Strength -= 15; + Stamina -= 5; + Agility -= 5; + Dexterity -= 5; + Wisdom += 8; + Intelligence += 32; + Charisma -= 5; + MagicResist += 5; + DiseaseResist -= 5; + break; + case 4: // Wood Elf + Strength -= 10; + Stamina -= 10; + Agility += 20; + Dexterity += 5; + Wisdom += 5; + BotSize = 5.0; + break; + case 5: // High Elf + Strength -= 20; + Stamina -= 10; + Agility += 10; + Dexterity -= 5; + Wisdom += 20; + Intelligence += 12; + Charisma += 5; + break; + case 6: // Dark Elf + Strength -= 15; + Stamina -= 10; + Agility += 15; + Wisdom += 8; + Intelligence += 24; + Charisma -= 15; + BotSize = 5.0; + break; + case 7: // Half Elf + Strength -= 5; + Stamina -= 5; + Agility += 15; + Dexterity += 10; + Wisdom -= 15; + BotSize = 5.5; + break; + case 8: // Dwarf + Strength += 15; + Stamina += 15; + Agility -= 5; + Dexterity += 15; + Wisdom += 8; + Intelligence -= 15; + Charisma -= 30; + BotSize = 4.0; + MagicResist -= 5; + PoisonResist += 5; + break; + case 9: // Troll + Strength += 33; + Stamina += 34; + Agility += 8; + Wisdom -= 15; + Intelligence -= 23; + Charisma -= 35; + BotSize = 8.0; + FireResist -= 20; + break; + case 10: // Ogre + Strength += 55; + Stamina += 77; + Agility -= 5; + Dexterity -= 5; + Wisdom -= 8; + Intelligence -= 15; + Charisma -= 38; + BotSize = 9.0; + break; + case 11: // Halfling + Strength -= 5; + Agility += 20; + Dexterity += 15; + Wisdom += 5; + Intelligence -= 8; + Charisma -= 25; + BotSize = 3.5; + PoisonResist += 5; + DiseaseResist += 5; + break; + case 12: // Gnome + Strength -= 15; + Stamina -= 5; + Agility += 10; + Dexterity += 10; + Wisdom -= 8; + Intelligence += 23; + Charisma -= 15; + BotSize = 3.0; + break; + case 128: // Iksar + Strength -= 5; + Stamina -= 5; + Agility += 15; + Dexterity += 10; + Wisdom += 5; + Charisma -= 20; + MagicResist -= 5; + FireResist -= 5; + break; + case 130: // Vah Shir + Strength += 15; + Agility += 15; + Dexterity -= 5; + Wisdom -= 5; + Intelligence -= 10; + Charisma -= 10; + BotSize = 7.0; + MagicResist -= 5; + FireResist -= 5; + break; + case 330: // Froglok + Strength -= 5; + Stamina += 5; + Agility += 25; + Dexterity += 25; + Charisma -= 25; + BotSize = 5.0; + MagicResist -= 5; + FireResist -= 5; + break; + case 522: // Drakkin + Strength -= 5; + Stamina += 5; + Agility += 10; + Intelligence += 10; + Wisdom += 5; + BotSize = 5.0; + PoisonResist += 2; + DiseaseResist += 2; + MagicResist += 2; + FireResist += 2; + ColdResist += 2; + break; + } + + this->STR = Strength; + this->STA = Stamina; + this->DEX = Dexterity; + this->AGI = Agility; + this->WIS = Wisdom; + this->INT = Intelligence; + this->CHA = Charisma; + this->ATK = Attack; + this->MR = MagicResist; + this->FR = FireResist; + this->DR = DiseaseResist; + this->PR = PoisonResist; + this->CR = ColdResist; + this->Corrup = CorruptionResist; + SetBotSpellID(BotSpellID); + this->size = BotSize; +} + +void Bot::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; + +} + +int16 Bot::acmod() +{ + int agility = GetAGI(); + int level = GetLevel(); + if(agility < 1 || level < 1) + return 0; + + if(agility <= 74) + { + if(agility == 1) + return -24; + else if(agility <=3) + return -23; + else if(agility == 4) + return -22; + else if(agility <=6) + return -21; + else if(agility <=8) + return -20; + else if(agility == 9) + return -19; + else if(agility <=11) + return -18; + else if(agility == 12) + return -17; + else if(agility <=14) + return -16; + else if(agility <=16) + return -15; + else if(agility == 17) + return -14; + else if(agility <=19) + return -13; + else if(agility == 20) + return -12; + else if(agility <=22) + return -11; + else if(agility <=24) + return -10; + else if(agility == 25) + return -9; + else if(agility <=27) + return -8; + else if(agility == 28) + return -7; + else if(agility <=30) + return -6; + else if(agility <=32) + return -5; + else if(agility == 33) + return -4; + else if(agility <=35) + return -3; + else if(agility == 36) + return -2; + else if(agility <=38) + return -1; + else if(agility <=65) + return 0; + else if(agility <=70) + return 1; + else if(agility <=74) + return 5; + } + else if(agility <= 137) + { + if(agility == 75) + { + if(level <= 6) + return 9; + else if(level <= 19) + return 23; + else if(level <= 39) + return 33; + else + return 39; + } + else if(agility >= 76 && agility <= 79) + { + if(level <= 6) + return 10; + else if(level <= 19) + return 23; + else if(level <= 39) + return 33; + else + return 40; + } + else if(agility == 80) + { + if(level <= 6) + return 11; + else if(level <= 19) + return 24; + else if(level <= 39) + return 34; + else + return 41; + } + else if(agility >= 81 && agility <= 85) + { + if(level <= 6) + return 12; + else if(level <= 19) + return 25; + else if(level <= 39) + return 35; + else + return 42; + } + else if(agility >= 86 && agility <= 90) + { + if(level <= 6) + return 12; + else if(level <= 19) + return 26; + else if(level <= 39) + return 36; + else + return 42; + } + else if(agility >= 91 && agility <= 95) + { + if(level <= 6) + return 13; + else if(level <= 19) + return 26; + else if(level <= 39) + return 36; + else + return 43; + } + else if(agility >= 96 && agility <= 99){ + if(level <= 6) + return 14; + else if(level <= 19) + return 27; + else if(level <= 39) + return 37; + else + return 44; + } + else if(agility == 100 && level >= 7) + { + if(level <= 19) + return 28; + else if (level <= 39) + return 38; + else + return 45; + } + else if(level <= 6) + { + return 15; + } + //level is >6 + else if(agility >= 101 && agility <= 105) + { + if(level <= 19) + return 29; + else if(level <= 39) + return 39;// not verified + else + return 45; + } + else if(agility >= 106 && agility <= 110) + { + if(level <= 19) + return 29; + else if(level <= 39) + return 39;// not verified + else + return 46; + } + else if(agility >= 111 && agility <= 115) + { + if(level <= 19) + return 30; + else if(level <= 39) + return 40;// not verified + else + return 47; + } + else if(agility >= 116 && agility <= 119) + { + if(level <= 19) + return 31; + else if(level <= 39) + return 41; + else + return 47; + } + else if(level <= 19) + { + return 32; + } + //level is > 19 + else if(agility == 120) + { + if(level <= 39) + return 42; + else + return 48; + } + else if(agility <= 125) + { + if(level <= 39) + return 42; + else + return 49; + } + else if(agility <= 135) + { + if(level <= 39) + return 42; + else + return 50; + } + else { + if(level <= 39) + return 42; + else + return 51; + } + } + else if(agility <= 300) + { + if(level <= 6) { + if(agility <= 139) + return(21); + else if(agility == 140) + return(22); + else if(agility <= 145) + return(23); + else if(agility <= 150) + return(23); + else if(agility <= 155) + return(24); + else if(agility <= 159) + return(25); + else if(agility == 160) + return(26); + else if(agility <= 165) + return(26); + else if(agility <= 170) + return(27); + else if(agility <= 175) + return(28); + else if(agility <= 179) + return(28); + else if(agility == 180) + return(29); + else if(agility <= 185) + return(30); + else if(agility <= 190) + return(31); + else if(agility <= 195) + return(31); + else if(agility <= 199) + return(32); + else if(agility <= 219) + return(33); + else if(agility <= 239) + return(34); + else + return(35); + } + else if(level <= 19) + { + if(agility <= 139) + return(34); + else if(agility == 140) + return(35); + else if(agility <= 145) + return(36); + else if(agility <= 150) + return(37); + else if(agility <= 155) + return(37); + else if(agility <= 159) + return(38); + else if(agility == 160) + return(39); + else if(agility <= 165) + return(40); + else if(agility <= 170) + return(40); + else if(agility <= 175) + return(41); + else if(agility <= 179) + return(42); + else if(agility == 180) + return(43); + else if(agility <= 185) + return(43); + else if(agility <= 190) + return(44); + else if(agility <= 195) + return(45); + else if(agility <= 199) + return(45); + else if(agility <= 219) + return(46); + else if(agility <= 239) + return(47); + else + return(48); + } + else if(level <= 39) + { + if(agility <= 139) + return(44); + else if(agility == 140) + return(45); + else if(agility <= 145) + return(46); + else if(agility <= 150) + return(47); + else if(agility <= 155) + return(47); + else if(agility <= 159) + return(48); + else if(agility == 160) + return(49); + else if(agility <= 165) + return(50); + else if(agility <= 170) + return(50); + else if(agility <= 175) + return(51); + else if(agility <= 179) + return(52); + else if(agility == 180) + return(53); + else if(agility <= 185) + return(53); + else if(agility <= 190) + return(54); + else if(agility <= 195) + return(55); + else if(agility <= 199) + return(55); + else if(agility <= 219) + return(56); + else if(agility <= 239) + return(57); + else + return(58); + } + else + { //lvl >= 40 + if(agility <= 139) + return(51); + else if(agility == 140) + return(52); + else if(agility <= 145) + return(53); + else if(agility <= 150) + return(53); + else if(agility <= 155) + return(54); + else if(agility <= 159) + return(55); + else if(agility == 160) + return(56); + else if(agility <= 165) + return(56); + else if(agility <= 170) + return(57); + else if(agility <= 175) + return(58); + else if(agility <= 179) + return(58); + else if(agility == 180) + return(59); + else if(agility <= 185) + return(60); + else if(agility <= 190) + return(61); + else if(agility <= 195) + return(61); + else if(agility <= 199) + return(62); + else if(agility <= 219) + return(63); + else if(agility <= 239) + return(64); + else + return(65); + } + } + else + { + //seems about 21 agil per extra AC pt over 300... + return (65 + ((agility-300) / 21)); + } +#if EQDEBUG >= 11 + LogFile->write(EQEMuLog::Error, "Error in Bot::acmod(): Agility: %i, Level: %i",agility,level); +#endif + return 0; +} + +void Bot::GenerateArmorClass() +{ + /// new formula + int avoidance = 0; + avoidance = (acmod() + ((GetSkill(DEFENSE)*16)/9)); + if(avoidance < 0) + avoidance = 0; + + int mitigation = 0; + if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER) + { + mitigation = GetSkill(DEFENSE)/4 + (itembonuses.AC+1); + mitigation -= 4; + } + else + { + mitigation = GetSkill(DEFENSE)/3 + ((itembonuses.AC*4)/3); + if(GetClass() == MONK) + mitigation += GetLevel() * 13/10; //the 13/10 might be wrong, but it is close... + } + int displayed = 0; + displayed += ((avoidance+mitigation)*1000)/847; //natural AC + + //Iksar AC, untested + if(GetRace() == IKSAR) + { + displayed += 12; + int iksarlevel = GetLevel(); + iksarlevel -= 10; + if(iksarlevel > 25) + iksarlevel = 25; + if(iksarlevel > 0) + displayed += iksarlevel * 12 / 10; + } + + //spell AC bonuses are added directly to natural total + displayed += spellbonuses.AC; + + this->AC = displayed; +} + +uint16 Bot::GetPrimarySkillValue() +{ + SkillType skill = HIGHEST_SKILL; //because NULL == 0, which is 1H Slashing, & we want it to return 0 from GetSkill + bool equiped = m_inv.GetItem(SLOT_PRIMARY); + + if(!equiped) + { + skill = HAND_TO_HAND; + } + else + { + uint8 type = m_inv.GetItem(SLOT_PRIMARY)->GetItem()->ItemType; //is this the best way to do this? + switch(type) + { + case ItemType1HS: // 1H Slashing + { + skill = _1H_SLASHING; + break; + } + case ItemType2HS: // 2H Slashing + { + skill = _2H_SLASHING; + break; + } + case ItemTypePierce: // Piercing + { + skill = PIERCING; + break; + } + case ItemType1HB: // 1H Blunt + { + skill = _1H_BLUNT; + break; + } + case ItemType2HB: // 2H Blunt + { + skill = _2H_BLUNT; + break; + } + case ItemType2HPierce: // 2H Piercing + { + skill = PIERCING; + break; + } + case ItemTypeHand2Hand: // Hand to Hand + { + skill = HAND_TO_HAND; + break; + } + default: // All other types default to Hand to Hand + { + skill = HAND_TO_HAND; + break; + } + } + } + + return GetSkill(skill); +} + +uint16 Bot::MaxSkill(SkillType skillid, uint16 class_, uint16 level) const { + return(database.GetSkillCap(class_, skillid, level)); +} + +uint16 Bot::GetTotalATK() +{ + uint16 AttackRating = 0; + uint16 WornCap = itembonuses.ATK; + + if(IsBot()) { + AttackRating = ((WornCap * 1.342) + (GetSkill(OFFENSE) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69)); + AttackRating += aabonuses.ATK + GroupLeadershipAAOffenseEnhancement(); + + if (AttackRating < 10) + AttackRating = 10; + } + else + AttackRating = GetATK(); + + AttackRating += spellbonuses.ATK; + + return AttackRating; +} + +uint16 Bot::GetATKRating() +{ + uint16 AttackRating = 0; + if(IsBot()) { + AttackRating = (GetSkill(OFFENSE) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69); + + if (AttackRating < 10) + AttackRating = 10; + } + return AttackRating; +} + +int32 Bot::GenerateBaseHitPoints() +{ + // Calc Base Hit Points + int new_base_hp = 0; + uint16 lm = GetClassLevelFactor(); + uint16 Post255; + uint16 NormalSTA = GetSTA(); + + if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) + { + float SoDPost255; + + if(((NormalSTA - 255) / 2) > 0) + SoDPost255 = ((NormalSTA - 255) / 2); + else + SoDPost255 = 0; + + int hp_factor = GetClassHPFactor(); + + if(level < 41) + { + new_base_hp = (5 + (GetLevel() * hp_factor / 12) + ((NormalSTA - SoDPost255) * GetLevel() * hp_factor / 3600)); + } + else if(level < 81) + { + new_base_hp = (5 + (40 * hp_factor / 12) + ((GetLevel() - 40) * hp_factor / 6) + + ((NormalSTA - SoDPost255) * hp_factor / 90) + + ((NormalSTA - SoDPost255) * (GetLevel() - 40) * hp_factor / 1800)); + } + else + { + new_base_hp = (5 + (80 * hp_factor / 8) + ((GetLevel() - 80) * hp_factor / 10) + + ((NormalSTA - SoDPost255) * hp_factor / 90) + + ((NormalSTA - SoDPost255) * hp_factor / 45)); + } + } + else + { + if((NormalSTA-255)/2 > 0) + Post255 = (NormalSTA-255)/2; + else + Post255 = 0; + + new_base_hp = (5)+(GetLevel()*lm/10) + (((NormalSTA-Post255)*GetLevel()*lm/3000)) + ((Post255*1)*lm/6000); + } + this->base_hp = new_base_hp; + + return new_base_hp; +} + +void Bot::GenerateAABonuses(StatBonuses* newbon) { + // General AA bonus + uint8 botClass = GetClass(); + uint8 botLevel = GetLevel(); + + memset(newbon, 0, sizeof(StatBonuses)); //start fresh + + if(botLevel >= 51) { + //level 51 = 1 AA level + + int i; + int totalAAs = database.CountAAs(); + uint32 slots = 0; + uint32 aa_AA = 0; + uint32 aa_value = 0; + for (i = 0; i < totalAAs; i++) { //iterate through all of the client's AAs + std::map::iterator aa = botAAs.find(i); + if(aa != botAAs.end()) { // make sure aa exists or we'll crash zone + aa_AA = aa->second.aa_id; //same as aaid from the aa_effects table + aa_value = aa->second.total_levels; //how many points in it + if (aa_AA > 0 || aa_value > 0) { //do we have the AA? if 1 of the 2 is set, we can assume we do + //slots = database.GetTotalAALevels(aa_AA); //find out how many effects from aa_effects table + slots = zone->GetTotalAALevels(aa_AA); //find out how many effects from aa_effects, which is loaded into memory + if (slots > 0) //and does it have any effects? may be able to put this above, not sure if it runs on each iteration + ApplyAABonuses(aa_AA + aa_value -1, slots, newbon); //add the bonuses + } + } + } + } +} + +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 + + 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); + 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); + + if(!database.RunQuery(Query, length, TempErrorMessageBuffer, &DatasetResult)) { + errorMessage = std::string(TempErrorMessageBuffer); + } + else { + int totalAAs = database.CountAAs(); + + 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()) { + LogFile->write(EQEMuLog::Error, "Error in Bot::LoadAAs()"); + } +} + +uint32 Bot::GetAA(uint32 aa_id) { + + std::map::const_iterator find_iter = botAAs.find(aa_id); + int aaLevel = 0; + + if(find_iter != botAAs.end()) { + aaLevel = find_iter->second.total_levels; + } + + return aaLevel; +} + +//current with Client::ApplyAABonuses 9/26/12 +void Bot::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) +{ + if(slots == 0) //sanity check. why bother if no slots to fill? + return; + + //from AA_Ability struct + uint32 effect = 0; + int32 base1 = 0; + int32 base2 = 0; //only really used for SE_RaiseStatCap & SE_ReduceSkillTimer in aa_effects table + uint32 slot = 0; + + std::map >::const_iterator find_iter = aa_effects.find(aaid); + if(find_iter == aa_effects.end()) + { + return; + } + + for (map::const_iterator iter = aa_effects[aaid].begin(); iter != aa_effects[aaid].end(); ++iter) { + effect = iter->second.skill_id; + base1 = iter->second.base1; + base2 = iter->second.base2; + slot = iter->second.slot; + + //we default to 0 (SE_CurrentHP) for the effect, so if there aren't any base1/2 values, we'll just skip it + if (effect == 0 && base1 == 0 && base2 == 0) + continue; + + //IsBlankSpellEffect() + if (effect == SE_Blank || (effect == SE_CHA && base1 == 0) || effect == SE_StackingCommand_Block || effect == SE_StackingCommand_Overwrite) + continue; + + _log(AA__BONUSES, "Applying Effect %d from AA %u in slot %d (base1: %d, base2: %d) on %s", effect, aaid, slot, base1, base2, this->GetCleanName()); + + uint8 focus = IsFocusEffect(0, 0, true,effect); + if (focus) + { + newbon->FocusEffects[focus] = effect; + continue; + } + + switch (effect) + { + //Note: AA effects that use accuracy are skill limited, while spell effect is not. + case SE_Accuracy: + if ((base2 == -1) && (newbon->Accuracy[HIGHEST_SKILL+1] < base1)) + newbon->Accuracy[HIGHEST_SKILL+1] = base1; + else if (newbon->Accuracy[base2] < base1) + newbon->Accuracy[base2] += base1; + break; + case SE_CurrentHP: //regens + newbon->HPRegen += base1; + break; + case SE_CurrentEndurance: + newbon->EnduranceRegen += base1; + break; + case SE_MovementSpeed: + newbon->movementspeed += base1; //should we let these stack? + /*if (base1 > newbon->movementspeed) //or should we use a total value? + newbon->movementspeed = base1;*/ + break; + case SE_STR: + newbon->STR += base1; + break; + case SE_DEX: + newbon->DEX += base1; + break; + case SE_AGI: + newbon->AGI += base1; + break; + case SE_STA: + newbon->STA += base1; + break; + case SE_INT: + newbon->INT += base1; + break; + case SE_WIS: + newbon->WIS += base1; + break; + case SE_CHA: + newbon->CHA += base1; + break; + case SE_WaterBreathing: + //handled by client + break; + case SE_CurrentMana: + newbon->ManaRegen += base1; + break; + case SE_ItemManaRegenCapIncrease: + newbon->ItemManaRegenCap += base1; + break; + case SE_ResistFire: + newbon->FR += base1; + break; + case SE_ResistCold: + newbon->CR += base1; + break; + case SE_ResistPoison: + newbon->PR += base1; + break; + case SE_ResistDisease: + newbon->DR += base1; + break; + case SE_ResistMagic: + newbon->MR += base1; + break; + case SE_ResistCorruption: + newbon->Corrup += base1; + break; + case SE_IncreaseSpellHaste: + break; + case SE_IncreaseRange: + break; + case SE_MaxHPChange: + newbon->MaxHP += base1; + break; + case SE_Packrat: + newbon->Packrat += base1; + break; + case SE_TwoHandBash: + break; + case SE_SetBreathLevel: + break; + case SE_RaiseStatCap: + switch(base2) + { + //are these #define'd somewhere? + case 0: //str + newbon->STRCapMod += base1; + break; + case 1: //sta + newbon->STACapMod += base1; + break; + case 2: //agi + newbon->AGICapMod += base1; + break; + case 3: //dex + newbon->DEXCapMod += base1; + break; + case 4: //wis + newbon->WISCapMod += base1; + break; + case 5: //int + newbon->INTCapMod += base1; + break; + case 6: //cha + newbon->CHACapMod += base1; + break; + case 7: //mr + newbon->MRCapMod += base1; + break; + case 8: //cr + newbon->CRCapMod += base1; + break; + case 9: //fr + newbon->FRCapMod += base1; + break; + case 10: //pr + newbon->PRCapMod += base1; + break; + case 11: //dr + newbon->DRCapMod += base1; + break; + case 12: //corruption + newbon->CorrupCapMod += base1; + break; + } + break; + case SE_PetDiscipline2: + break; + case SE_SpellSlotIncrease: + break; + case SE_MysticalAttune: + newbon->BuffSlotIncrease += base1; + break; + case SE_TotalHP: + newbon->HP += base1; + break; + case SE_StunResist: + newbon->StunResist += base1; + break; + case SE_SpellCritChance: + newbon->CriticalSpellChance += base1; + break; + case SE_SpellCritDmgIncrease: + newbon->SpellCritDmgIncrease += base1; + break; + case SE_DotCritDmgIncrease: + newbon->DotCritDmgIncrease += base1; + break; + case SE_ResistSpellChance: + newbon->ResistSpellChance += base1; + break; + case SE_CriticalHealChance: + newbon->CriticalHealChance += base1; + break; + case SE_CriticalHealOverTime: + newbon->CriticalHealOverTime += base1; + break; + case SE_CriticalDoTChance: + newbon->CriticalDoTChance += base1; + break; + case SE_ReduceSkillTimer: + newbon->SkillReuseTime[base2] += base1; + break; + case SE_Fearless: + newbon->Fearless = true; + break; + case SE_PersistantCasting: + newbon->PersistantCasting += base1; + break; + case SE_DelayDeath: + newbon->DelayDeath += base1; + break; + case SE_FrontalStunResist: + newbon->FrontalStunResist += base1; + break; + case SE_ImprovedBindWound: + newbon->BindWound += base1; + break; + case SE_MaxBindWound: + newbon->MaxBindWound += base1; + break; + case SE_ExtraAttackChance: + newbon->ExtraAttackChance += base1; + break; + case SE_SeeInvis: + newbon->SeeInvis = base1; + break; + case SE_BaseMovementSpeed: + newbon->BaseMovementSpeed += base1; + break; + case SE_IncreaseRunSpeedCap: + newbon->IncreaseRunSpeedCap += base1; + break; + case SE_ConsumeProjectile: + newbon->ConsumeProjectile += base1; + break; + case SE_ArcheryDamageModifier: + newbon->ArcheryDamageModifier += base1; + break; + case SE_DamageShield: + newbon->DamageShield += base1; + break; + case SE_CharmBreakChance: + newbon->CharmBreakChance += base1; + break; + case SE_OffhandRiposteFail: + newbon->OffhandRiposteFail += base1; + break; + case SE_ItemAttackCapIncrease: + newbon->ItemATKCap += base1; + break; + case SE_GivePetGroupTarget: + newbon->GivePetGroupTarget = true; + break; + case SE_ItemHPRegenCapIncrease: + newbon->ItemHPRegenCap = +base1; + break; + case SE_Ambidexterity: + newbon->Ambidexterity += base1; + break; + case SE_PetMaxHP: + newbon->PetMaxHP += base1; + break; + case SE_AvoidMeleeChance: + newbon->AvoidMeleeChance += base1; + break; + case SE_CombatStability: + newbon->CombatStability += base1; + break; + case SE_PetCriticalHit: + newbon->PetCriticalHit += base1; + break; + case SE_PetAvoidance: + newbon->PetAvoidance += base1; + break; + case SE_ShieldBlock: + newbon->ShieldBlock += base1; + break; + case SE_SecondaryDmgInc: + newbon->SecondaryDmgInc = true; + break; + case SE_ChangeAggro: + newbon->hatemod += base1; + break; + case SE_EndurancePool: + newbon->Endurance += base1; + break; + case SE_ChannelChanceItems: + newbon->ChannelChanceItems += base1; + break; + case SE_ChannelChanceSpells: + newbon->ChannelChanceSpells += base1; + break; + case SE_DoubleSpecialAttack: + newbon->DoubleSpecialAttack += base1; + break; + case SE_TripleBackstab: + newbon->TripleBackstab += base1; + break; + case SE_FrontalBackstabMinDmg: + newbon->FrontalBackstabMinDmg = true; + break; + case SE_FrontalBackstabChance: + newbon->FrontalBackstabChance += base1; + break; + case SE_BlockBehind: + newbon->BlockBehind += base1; + break; + case SE_StrikeThrough2: + newbon->StrikeThrough += base1; + break; + case SE_DoubleAttackChance: + newbon->DoubleAttackChance += base1; + break; + case SE_GiveDoubleAttack: + newbon->GiveDoubleAttack += base1; + break; + case SE_ProcChance: + newbon->ProcChance += base1; + break; + case SE_RiposteChance: + newbon->RiposteChance += base1; + break; + case SE_Flurry: + newbon->FlurryChance += base1; + break; + case SE_PetFlurry: + newbon->PetFlurry = base1; + break; + case SE_BardSongRange: + newbon->SongRange += base1; + break; + case SE_RootBreakChance: + newbon->RootBreakChance += base1; + break; + case SE_UnfailingDivinity: + newbon->UnfailingDivinity += base1; + break; + + case SE_SpellOnKill: + for(int i = 0; i < MAX_SPELL_TRIGGER*3; i+=3) + { + if(!newbon->SpellOnKill[i] || ((newbon->SpellOnKill[i] == base2) && (newbon->SpellOnKill[i+1] < base1))) + { + //base1 = chance, base2 = SpellID to be triggered, base3 = min npc level + newbon->SpellOnKill[i] = base2; + newbon->SpellOnKill[i+1] = base1; + + if (GetLevel() > 15) + newbon->SpellOnKill[i+2] = GetLevel() - 15; //AA specifiy "non-trivial" + else + newbon->SpellOnKill[i+2] = 0; + + break; + } + } + break; + + case SE_SpellOnDeath: + for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) + { + if(!newbon->SpellOnDeath[i]) + { + // base1 = SpellID to be triggered, base2 = chance to fire + newbon->SpellOnDeath[i] = base1; + newbon->SpellOnDeath[i+1] = base2; + break; + } + } + break; + + case SE_TriggerOnCast: + + for(int i = 0; i < MAX_SPELL_TRIGGER; i++) + { + if (newbon->SpellTriggers[i] == aaid) + break; + + if(!newbon->SpellTriggers[i]) + { + //Save the 'aaid' of each triggerable effect to an array + newbon->SpellTriggers[i] = aaid; + break; + } + } + break; + + case SE_CriticalHitChance: + { + if(base2 == -1) + newbon->CriticalHitChance[HIGHEST_SKILL+1] += base1; + else + newbon->CriticalHitChance[base2] += base1; + } + break; + + case SE_CriticalDamageMob: + { + // base1 = effect value, base2 = skill restrictions(-1 for all) + if(base2 == -1) + newbon->CritDmgMob[HIGHEST_SKILL+1] += base1; + else + newbon->CritDmgMob[base2] += base1; + break; + } + + case SE_CriticalSpellChance: + { + newbon->CriticalSpellChance += base1; + + if (base2 > 100) + newbon->SpellCritDmgIncrease += (base2 - 100); + + break; + } + + case SE_ResistFearChance: + { + if(base1 == 100) // If we reach 100% in a single spell/item then we should be immune to negative fear resist effects until our immunity is over + newbon->Fearless = true; + + newbon->ResistFearChance += base1; // these should stack + break; + } + + case SE_SkillDamageAmount: + { + if(base2 == -1) + newbon->SkillDamageAmount[HIGHEST_SKILL+1] += base1; + else + newbon->SkillDamageAmount[base2] += base1; + break; + } + + case SE_SpecialAttackKBProc: + { + //You can only have one of these per client. [AA Dragon Punch] + newbon->SpecialAttackKBProc[0] = base1; //Chance base 100 = 25% proc rate + newbon->SpecialAttackKBProc[1] = base2; //Skill to KB Proc Off + break; + } + + case SE_DamageModifier: + { + if(base2 == -1) + newbon->DamageModifier[HIGHEST_SKILL+1] += base1; + else + newbon->DamageModifier[base2] += base1; + break; + } + + case SE_SlayUndead: + { + if(newbon->SlayUndead[1] < base1) + newbon->SlayUndead[0] = base1; // Rate + newbon->SlayUndead[1] = base2; // Damage Modifier + break; + } + + case SE_GiveDoubleRiposte: + { + //0=Regular Riposte 1=Skill Attack Riposte 2=Skill + if(base2 == 0){ + if(newbon->GiveDoubleRiposte[0] < base1) + newbon->GiveDoubleRiposte[0] = base1; + } + //Only for special attacks. + else if(base2 > 0 && (newbon->GiveDoubleRiposte[1] < base1)){ + newbon->GiveDoubleRiposte[1] = base1; + newbon->GiveDoubleRiposte[2] = base2; + } + + break; + } + + //Kayen: Not sure best way to implement this yet. + //Physically raises skill cap ie if 55/55 it will raise to 55/60 + case SE_RaiseSkillCap: + { + if(newbon->RaiseSkillCap[0] < base1){ + newbon->RaiseSkillCap[0] = base1; //value + newbon->RaiseSkillCap[1] = base2; //skill + } + break; + } + + case SE_MasteryofPast: + { + if(newbon->MasteryofPast < base1) + newbon->MasteryofPast = base1; + break; + } + + case SE_CastingLevel2: + case SE_CastingLevel: + { + newbon->effective_casting_level += base1; + break; + } + + + case SE_DivineSave: + { + if(newbon->DivineSaveChance[0] < base1) + { + newbon->DivineSaveChance[0] = base1; + newbon->DivineSaveChance[1] = base2; + } + break; + } + + case SE_SpellEffectResistChance: + { + for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2) + { + if(!newbon->SEResist[e] || ((newbon->SEResist[e] = base2) && (newbon->SEResist[e+1] < base1)) ){ + newbon->SEResist[e] = base2; + newbon->SEResist[e+1] = base1; + break; + } + } + break; + } + + case SE_MitigateDamageShield: + { + if (base1 < 0) + base1 = base1*(-1); + + newbon->DSMitigationOffHand += base1; + break; + } + + case SE_FinishingBlow: + { + + //base1 = chance, base2 = damage + if (newbon->FinishingBlow[1] < base2){ + newbon->FinishingBlow[0] = base1; + newbon->FinishingBlow[1] = base2; + } + break; + } + + case SE_FinishingBlowLvl: + { + //base1 = level, base2 = ??? (Set to 200 in AA data, possible proc rate mod?) + if (newbon->FinishingBlowLvl[0] < base1){ + newbon->FinishingBlowLvl[0] = base1; + newbon->FinishingBlowLvl[1] = base2; + } + break; + } + } + } +} + +bool Bot::IsValidRaceClassCombo() { + bool Result = false; + + switch(GetRace()) { + case 1: // Human + switch(GetClass()) { + case 1: // Warrior + case 2: // Cleric + case 3: // Paladin + case 4: // Ranger + case 5: // Shadowknight + case 6: // Druid + case 7: // Monk + case 8: // Bard + case 9: // Rogue + case 11: // Necromancer + case 12: // Wizard + case 13: // Magician + case 14: // Enchanter + Result = true; + break; + } + break; + case 2: // Barbarian + switch(GetClass()) { + case 1: // Warrior + case 9: // Rogue + case 10: // Shaman + case 15: // Beastlord + case 16: // Berserker + Result = true; + break; + } + break; + case 3: // Erudite + switch(GetClass()) { + case 2: // Cleric + case 3: // Paladin + case 5: // Shadowknight + case 11: // Necromancer + case 12: // Wizard + case 13: // Magician + case 14: // Enchanter + Result = true; + break; + } + break; + case 4: // Wood Elf + switch(GetClass()) { + case 1: // Warrior + case 4: // Ranger + case 6: // Druid + case 8: // Bard + case 9: // Rogue + Result = true; + break; + } + break; + case 5: // High Elf + switch(GetClass()) { + case 2: // Cleric + case 3: // Paladin + case 12: // Wizard + case 13: // Magician + case 14: // Enchanter + Result = true; + break; + } + break; + case 6: // Dark Elf + switch(GetClass()) { + case 1: // Warrior + case 2: // Cleric + case 5: // Shadowknight + case 9: // Rogue + case 11: // Necromancer + case 12: // Wizard + case 13: // Magician + case 14: // Enchanter + Result = true; + break; + } + break; + case 7: // Half Elf + switch(GetClass()) { + case 1: // Warrior + case 3: // Paladin + case 4: // Ranger + case 6: // Druid + case 8: // Bard + case 9: // Rogue + Result = true; + break; + } + break; + case 8: // Dwarf + switch(GetClass()) { + case 1: // Warrior + case 2: // Cleric + case 3: // Paladin + case 9: // Rogue + case 16: // Berserker + Result = true; + break; + } + break; + case 9: // Troll + switch(GetClass()) { + case 1: // Warrior + case 5: // Shadowknight + case 10: // Shaman + case 15: // Beastlord + case 16: // Berserker + Result = true; + break; + } + break; + case 10: // Ogre + switch(GetClass()) { + case 1: // Warrior + case 5: // Shadowknight + case 10: // Shaman + case 15: // Beastlord + case 16: // Berserker + Result = true; + break; + } + break; + case 11: // Halfling + switch(GetClass()) { + case 1: // Warrior + case 2: // Cleric + case 3: // Paladin + case 4: // Ranger + case 6: // Druid + case 9: // Rogue + Result = true; + break; + } + break; + case 12: // Gnome + switch(GetClass()) { + case 1: // Warrior + case 2: // Cleric + case 3: // Paladin + case 5: // Shadowknight + case 9: // Rogue + case 11: // Necromancer + case 12: // Wizard + case 13: // Magician + case 14: // Enchanter + Result = true; + break; + } + break; + case 128: // Iksar + switch(GetClass()) { + case 1: // Warrior + case 5: // Shadowknight + case 7: // Monk + case 10: // Shaman + case 11: // Necromancer + case 15: // Beastlord + Result = true; + break; + } + break; + case 130: // Vah Shir + switch(GetClass()) { + case 1: // Warrior + case 8: // Bard + case 9: // Rogue + case 10: // Shaman + case 15: // Beastlord + case 16: // Berserker + Result = true; + break; + } + break; + case 330: // Froglok + switch(GetClass()) { + case 1: // Warrior + case 2: // Cleric + case 3: // Paladin + case 5: // Shadowknight + case 9: // Rogue + case 10: // Shaman + case 11: // Necromancer + case 12: // Wizard + Result = true; + break; + } + break; + case 522: // Drakkin + switch(GetClass()) { + case 1: // Warrior + case 2: // Cleric + case 3: // Paladin + case 4: // Ranger + case 5: // Shadowknight + case 6: // Druid + case 7: // Monk + case 8: // Bard + case 9: // Rogue + case 11: // Necromancer + case 12: // Wizard + case 13: // Magician + case 14: // Enchanter + Result = true; + break; + } + break; + } + + return Result; +} + +bool Bot::IsValidName() { + bool Result = false; + std::string TempBotName = std::string(this->GetCleanName()); + + for(int iCounter = 0; iCounter < TempBotName.length(); iCounter++) { + if(isalpha(TempBotName[iCounter]) || TempBotName[iCounter] == '_') { + Result = true; + } + } + + return Result; +} + +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(!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; + + while(DataRow = mysql_fetch_row(DatasetResult)) { + ExistingNameCount = atoi(DataRow[0]); + break; + } + + if(ExistingNameCount == 0) + Result = true; + + mysql_free_result(DatasetResult); + } + + safe_delete(Query); + } + + return Result; +} + +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); + } + 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(); + SavePet(); + SaveStance(); + SaveTimers(); + } + + return Result; +} + +// Returns the current total play time for the bot +uint32 Bot::GetTotalPlayTime() { + uint32 Result = 0; + + double TempTotalPlayTime = 0; + + time_t currentTime = time(¤tTime); + + TempTotalPlayTime = difftime(currentTime, _startTotalPlayTime); + + TempTotalPlayTime += _lastTotalPlayTime; + + Result = (uint32)TempTotalPlayTime; + + return Result; +} + +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; + } + } + + int IsPersistent = 0; + + if(buffs[BuffCount].persistant_buff) + IsPersistent = 1; + else + IsPersistent = 0; + + if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botbuffs (BotId, SpellId, CasterLevel, DurationFormula, " + "TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, " + "DeathSaveSuccessChance, CasterAARank, Persistent) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u);", + 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].deathSaveSuccessChance, + buffs[BuffCount].deathsaveCasterAARank, IsPersistent), 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; + + if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, DeathSaveSuccessChance, CasterAARank, Persistent FROM botbuffs WHERE BotId = %u", GetBotID()), 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].deathSaveSuccessChance = atoi(DataRow[11]); + buffs[BuffCount].deathsaveCasterAARank = atoi(DataRow[12]); + buffs[BuffCount].casterid = 0; + + bool IsPersistent = false; + + if(atoi(DataRow[13])) + IsPersistent = true; + + buffs[BuffCount].persistant_buff = IsPersistent; + + BuffCount++; + } + + 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; + } + + mysql_free_result(DatasetResult); + } + + safe_delete(Query); + + if(!errorMessage.empty()) { + // TODO: Record this error message to zone error log + } + + return Result; +} + +void Bot::LoadPet() { + uint32 PetSaveId = GetPetSaveId(); + + if(PetSaveId > 0 && !GetPet() && PetSaveId <= SPDAT_RECORDS) { + std::string petName; + uint16 petMana = 0; + uint16 petHitPoints = 0; + uint32 botPetId = 0; + + LoadPetStats(&petName, &petMana, &petHitPoints, &botPetId, PetSaveId); + + MakePet(botPetId, spells[botPetId].teleport_zone, petName.c_str()); + + if(GetPet() && GetPet()->IsNPC()) { + NPC *pet = GetPet()->CastToNPC(); + SpellBuff_Struct petBuffs[BUFF_COUNT]; + uint32 petItems[MAX_WORN_INVENTORY]; + + LoadPetBuffs(petBuffs, PetSaveId); + LoadPetItems(petItems, PetSaveId); + + pet->SetPetState(petBuffs, petItems); + pet->CalcBonuses(); + pet->SetHP(petHitPoints); + pet->SetMana(petMana); + } + + DeletePetStats(PetSaveId); + } +} + +void Bot::LoadPetStats(std::string* petName, 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; + + bool statsLoaded = false; + + 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; + } + + mysql_free_result(DatasetResult); + + statsLoaded = true; + } + + safe_delete(Query); + Query = 0; + + if(!errorMessage.empty()) { + // TODO: Record this error message to zone error log + } + } +} + +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; + + bool BuffsLoaded = false; + + 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; + + petBuffs[BuffCount].spellid = atoi(DataRow[0]); + petBuffs[BuffCount].level = atoi(DataRow[1]); + petBuffs[BuffCount].duration = atoi(DataRow[2]); + + BuffCount++; + } + + 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 + } + } +} + +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; + + bool itemsLoaded = false; + + if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT ItemId FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer, &DatasetResult)) { + errorMessage = std::string(TempErrorMessageBuffer); + } + else { + int ItemCount = 0; + + while(DataRow = mysql_fetch_row(DatasetResult)) { + if(ItemCount == MAX_WORN_INVENTORY) + break; + + petItems[ItemCount] = atoi(DataRow[0]); + + ItemCount++; + } + + mysql_free_result(DatasetResult); + + 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() { + if(GetPet() && !GetPet()->IsFamiliar() && GetPet()->CastToNPC()->GetPetSpellID() /*&& !dead*/) { + NPC *pet = GetPet()->CastToNPC(); + uint16 petMana = pet->GetMana(); + uint16 petHitPoints = pet->GetHP(); + uint32 botPetId = pet->CastToNPC()->GetPetSpellID(); + char* tempPetName = new char[64]; + SpellBuff_Struct petBuffs[BUFF_COUNT]; + uint32 petItems[MAX_WORN_INVENTORY]; + + pet->GetPetState(petBuffs, petItems, tempPetName); + + uint32 existingBotPetSaveId = GetPetSaveId(); + + if(existingBotPetSaveId > 0) { + // Remove any existing pet buffs + DeletePetBuffs(existingBotPetSaveId); + + // Remove any existing pet items + DeletePetItems(existingBotPetSaveId); + } + + // Save pet stats and get a new bot pet save id + uint32 botPetSaveId = SavePetStats(std::string(tempPetName), petMana, petHitPoints, botPetId); + + // Save pet buffs + SavePetBuffs(petBuffs, botPetSaveId); + + // Save pet items + SavePetItems(petItems, botPetSaveId); + + if(tempPetName) + safe_delete_array(tempPetName); + } +} + +uint32 Bot::SavePetStats(std::string petName, uint16 petMana, uint16 petHitPoints, uint32 botPetId) { + uint32 Result = 0; + + std::string errorMessage; + char* Query = 0; + char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + + 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; +} + +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; + + 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; + } + } + + BuffCount++; + } + + 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; + + while(ItemCount < MAX_WORN_INVENTORY) { + 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++; + } + } + + ItemCount++; + } + + if(!errorMessage.empty()) { + // TODO: Record this error message to zone error log + } + } +} + +void Bot::DeletePetBuffs(uint32 botPetSaveId) { + if(botPetSaveId > 0) { + std::string errorMessage; + char* Query = 0; + char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + + if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { + errorMessage = std::string(TempErrorMessageBuffer); + } + + 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(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { + errorMessage = std::string(TempErrorMessageBuffer); + } + + 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(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE from botpets where BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { + errorMessage = std::string(TempErrorMessageBuffer); + } + + 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()) { + LogFile->write(EQEMuLog::Error, "Error in Bot::LoadStance()"); + } + + if(loaded) + SetBotStance((BotStanceType)Result); + else + SetDefaultBotStance(); +} + +void Bot::SaveStance() { + if(_baseBotStance != _botStance) { + std::string errorMessage; + char* Query = 0; + char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + + 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; + } + + 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()) { + LogFile->write(EQEMuLog::Error, "Error in Bot::LoadTimers()"); + } +} + +void Bot::SaveTimers() { + std::string errorMessage; + char* Query = 0; + char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + + if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM bottimers WHERE BotID = %u;", GetBotID()), TempErrorMessageBuffer)) { + errorMessage = std::string(TempErrorMessageBuffer); + safe_delete(Query); + Query = 0; + } + + 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()) { + LogFile->write(EQEMuLog::Error, "Error in Bot::SaveTimers()"); + } +} + +bool Bot::Process() +{ + _ZP(Bot_Process); + + if(IsStunned() && stunned_timer.Check()) + { + this->stunned = false; + this->stunned_timer.Disable(); + } + + if(!GetBotOwner()) + return false; + + if (GetDepop()) + { + _botOwner = 0; + _botOwnerCharacterID = 0; + _previousTarget = 0; + return false; + } + + SpellProcess(); + + if(tic_timer.Check()) + { + //6 seconds, or whatever the rule is set to has passed, send this position to everyone to avoid ghosting + if(!IsMoving() && !IsEngaged()) + { + SendPosition(); + if(IsSitting()) + { + if(!rest_timer.Enabled()) + { + rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000); + } + } + } + + BuffProcess(); + + CalcRestState(); + + if(curfp) + ProcessFlee(); + + if(GetHP() < GetMaxHP()) + SetHP(GetHP() + CalcHPRegen() + RestRegenHP); + + if(GetMana() < GetMaxMana()) + SetMana(GetMana() + CalcManaRegen() + RestRegenMana); + + CalcATK(); + if(GetEndurance() < GetMaxEndurance()) + SetEndurance(GetEndurance() + CalcEnduranceRegen() + RestRegenEndurance); + } + + if (sendhpupdate_timer.Check()) { + SendHPUpdate(); + + if(HasPet()) + GetPet()->SendHPUpdate(); + } + + if(GetAppearance() == eaDead && GetHP() > 0) + SetAppearance(eaStanding); + + if (IsStunned() || IsMezzed()) + return true; + + //Handle assists... + /*if(assist_timer.Check() && !Charmed() && GetTarget() != NULL) { + entity_list.AIYellForHelp(this, GetTarget()); + }*/ + + // Bot AI + AI_Process(); + + // Bot Pet AI + if(HasPet()) + PetAIProcess(); + + return true; +} + +void Bot::SpellProcess() +{ + // check the rapid recast prevention timer + if(spellend_timer.Check(false)) + { + NPC::SpellProcess(); + + if(GetClass() == BARD) { + if (casting_spell_id != 0) + casting_spell_id = 0; + } + } +} + +void Bot::BotMeditate(bool isSitting) { + if(isSitting) { + // If the bot is a caster has less than 99% mana while its not engaged, he needs to sit to meditate + if(GetManaRatio() < 99.0f) + { + if(!IsSitting()) + Sit(); + } + else + { + if(IsSitting()) + Stand(); + } + } + else + { + if(IsSitting()) + Stand(); + } + if(IsSitting()) + { + if(!rest_timer.Enabled()) + { + rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000); + } + } + else + { + rest_timer.Disable(); + } +} + +void Bot::BotRangedAttack(Mob* other) { + //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())) { + mlog(COMBAT__RANGED, "Bot Archery attack canceled. Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); + Message(0, "Error: Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); + return; + } + + ItemInst* rangedItem = GetBotItem(SLOT_RANGE); + const Item_Struct* RangeWeapon = 0; + if(rangedItem) + RangeWeapon = rangedItem->GetItem(); + + ItemInst* ammoItem = GetBotItem(SLOT_AMMO); + const Item_Struct* Ammo = 0; + if(ammoItem) + Ammo = ammoItem->GetItem(); + + if(!RangeWeapon || !Ammo) + return; + + mlog(COMBAT__RANGED, "Shooting %s with bow %s (%d) and arrow %s (%d)", other->GetCleanName(), RangeWeapon->Name, RangeWeapon->ID, Ammo->Name, Ammo->ID); + + if(!IsAttackAllowed(other) || + IsCasting() || + DivineAura() || + IsStunned() || + IsMezzed() || + (GetAppearance() == eaDead)) + { + return; + } + + SendItemAnimation(other, Ammo, ARCHERY); + + DoArcheryAttackDmg(GetTarget(), rangedItem, ammoItem); + + //break invis when you attack + if(invisible) { + mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack."); + BuffFadeByEffect(SE_Invisibility); + BuffFadeByEffect(SE_Invisibility2); + invisible = false; + } + if(invisible_undead) { + mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack."); + BuffFadeByEffect(SE_InvisVsUndead); + BuffFadeByEffect(SE_InvisVsUndead2); + invisible_undead = false; + } + if(invisible_animals){ + mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack."); + 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); + } +} + +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; + + // If you don't have the double attack skill, return + if(!GetSkill(DOUBLE_ATTACK) && !(GetClass() == BARD || GetClass() == BEASTLORD)) + return false; + + // You start with no chance of double attacking + float chance = 0.0f; + + uint16 skill = GetSkill(DOUBLE_ATTACK); + + int16 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) + chance = (float(skill+GetLevel()) * (float(100.0f+bonusDA+bonusGiveDA) /100.0f)) /500.0f; + else + chance = (float(bonusGiveDA) * (float(100.0f+bonusDA)/100.0f) ) /100.0f; + + //Live now uses a static Triple Attack skill (lv 46 = 2% lv 60 = 20%) - We do not have this skill on EMU ATM. + //A reasonable forumla would then be TA = 20% * chance + //AA's can also give triple attack skill over cap. (ie Burst of Power) NOTE: Skill ID in spell data is 76 (Triple Attack) + //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; + chance *= 0.2f; //Baseline chance is 20% of your double attack chance. + chance *= float(100.0f+triple_bonus)/100.0f; //Apply modifiers. + } + + if((MakeRandomFloat(0, 1) < chance)) + return true; + + return false; +} + +void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillType skillinuse, int16 chance_mod, int16 focus, bool CanRiposte) +{ + 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. + if (skillinuse == BEGGING) + skillinuse = OFFENSE; + + int damage = 0; + uint32 hate = 0; + int Hand = 13; + if (hate == 0 && weapon_damage > 1) hate = weapon_damage; + + if(weapon_damage > 0){ + + if(GetClass() == BERSERKER){ + int bonus = 3 + GetLevel()/10; + weapon_damage = weapon_damage * (100+bonus) / 100; + } + + int32 min_hit = 1; + int32 max_hit = (2*weapon_damage*GetDamageTable(skillinuse)) / 100; + + if(GetLevel() >= 28 && IsWarriorClass() ) + { + int ucDamageBonus = GetWeaponDamageBonus((const Item_Struct*) NULL ); + + min_hit += (int) ucDamageBonus; + max_hit += (int) ucDamageBonus; + hate += ucDamageBonus; + } + + ApplySpecialAttackMod(skillinuse, max_hit, min_hit); + + min_hit += min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100; + + if(max_hit < min_hit) + max_hit = min_hit; + + if(RuleB(Combat, UseIntervalAC)) + damage = max_hit; + else + damage = MakeRandomInt(min_hit, max_hit); + + if(!other->CheckHitChance(this, skillinuse, Hand, chance_mod)) { + damage = 0; + } else { + other->AvoidDamage(this, damage, CanRiposte); + other->MeleeMitigation(this, damage, min_hit); + if(damage > 0) { + damage += damage*focus/100; + ApplyMeleeDamageBonus(skillinuse, damage); + damage += other->GetAdditionalDamage(this, 0, true, skillinuse); + damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse); + TryCriticalHit(other, skillinuse, damage); + } + } + + if (damage == -3) { + DoRiposte(other); + if (HasDied()) + return; + } + } + + else + damage = -5; + + if(skillinuse == BASH){ + const ItemInst* inst = GetBotItem(SLOT_SECONDARY); + const Item_Struct* botweapon = 0; + if(inst) + botweapon = inst->GetItem(); + if(botweapon) { + if(botweapon->ItemType == ItemTypeShield) { + hate += botweapon->AC; + } + hate = hate * (100 + GetFuriousBash(botweapon->Focus.Effect)) / 100; + } + } + + other->AddToHateList(this, hate); + + bool CanSkillProc = true; + if (skillinuse == OFFENSE){ //Hack to allow damage to display. + skillinuse = TIGER_CLAW; //'strike' your opponent - Arbitrary choice for message. + CanSkillProc = false; //Disable skill procs + } + + other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); + + if (HasDied()) + return; + + if((skillinuse == DRAGON_PUNCH) && GetAA(aaDragonPunch) && MakeRandomInt(0, 99) < 25){ + SpellFinished(904, other, 10, 0, -1, spells[904].ResistDiff); + other->Stun(100); + } + + if (CanSkillProc && HasSkillProcs()){ + float chance = 10.0f*RuleR(Combat, AvgProcsPerMinute)/60000.0f; + TrySkillProc(other, skillinuse, chance); + } +} + +void Bot::ApplySpecialAttackMod(SkillType skill, int32 &dmg, int32 &mindmg) { + + int item_slot = -1; + //1: Apply bonus from AC (BOOT/SHIELD/HANDS) est. 40AC=6dmg + + switch (skill){ + + case FLYING_KICK: + case ROUND_KICK: + case KICK: + item_slot = SLOT_FEET; + break; + + case BASH: + item_slot = SLOT_SECONDARY; + break; + + case DRAGON_PUNCH: + case EAGLE_STRIKE: + case TIGER_CLAW: + item_slot = SLOT_HANDS; + break; + } + + if (item_slot >= 0){ + const ItemInst* inst = GetBotItem(item_slot); + const Item_Struct* botweapon = 0; + if(inst) + botweapon = inst->GetItem(); + if(botweapon) + dmg += botweapon->AC * (RuleI(Combat, SpecialAttackACBonus))/100; + } +} + +bool Bot::CanDoSpecialAttack(Mob *other) +{ + //Make sure everything is valid before doing any attacks. + if (!other) { + SetTarget(NULL); + return false; + } + + if(!GetTarget()) + SetTarget(other); + + if ((other == NULL || ((GetAppearance() == eaDead) || (other->IsClient() && other->CastToClient()->IsDead())) + || HasDied() || (!IsAttackAllowed(other)))) { + return false; + } + + if(other->GetInvul() || other->SpecAttacks[IMMUNE_MELEE]) + return false; + + return true; +} + +void Bot::SetTarget(Mob* mob) { + if(mob != this) { + if(mob != GetTarget()) + _previousTarget = GetTarget(); + + NPC::SetTarget(mob); + } +} + +float Bot::GetMaxMeleeRangeToTarget(Mob* target) { + float result = 0; + + if(target) { + float size_mod = GetSize(); + float other_size_mod = target->GetSize(); + + if(GetRace() == 49 || GetRace() == 158 || GetRace() == 196) //For races with a fixed size + size_mod = 60.0f; + else if (size_mod < 6.0) + size_mod = 8.0f; + + if(target->GetRace() == 49 || target->GetRace() == 158 || target->GetRace() == 196) //For races with a fixed size + other_size_mod = 60.0f; + else if (other_size_mod < 6.0) + other_size_mod = 8.0f; + + if (other_size_mod > size_mod) { + size_mod = other_size_mod; + } + + // this could still use some work, but for now it's an improvement.... + + if (size_mod > 29) + size_mod *= size_mod; + else if (size_mod > 19) + size_mod *= size_mod * 2; + else + size_mod *= size_mod * 4; + + // prevention of ridiculously sized hit boxes + if (size_mod > 10000) + size_mod = size_mod / 7; + + result = size_mod; + } + + return result; +} + +// AI Processing for the Bot object +void Bot::AI_Process() { + _ZP(Mob_BOT_Process); + + if(!IsAIControlled()) + return; + + uint8 botClass = GetClass(); + uint8 botLevel = GetLevel(); + + if(IsCasting() && (botClass != BARD)) + return; + + // A bot wont start its AI if not grouped + if(!GetBotOwner() || !IsGrouped()) { + return; + } + + if(GetAppearance() == eaDead) + return; + + Mob* BotOwner = GetBotOwner(); + + // The bots need an owner + if(!BotOwner) + return; + + try { + if(BotOwner->CastToClient()->IsDead()) { + SetTarget(0); + SetBotOwner(0); + return; + } + } + catch(...) { + SetTarget(0); + SetBotOwner(0); + return; + } + + if(GetHealRotationActive() && GetHealRotationTarget() && !GetHasHealedThisCycle() && GetHealRotationNextHealTime() < Timer::GetCurrentTime()) { + if(AIHealRotation(GetHealRotationTarget(), GetHealRotationUseFastHeals())) { + SetHasHealedThisCycle(true); + NotifyNextHealRotationMember(); + } + else { + NotifyNextHealRotationMember(true); + } + } + + if(GetHasBeenSummoned()) { + if(IsBotCaster() || IsBotArcher()) { + if (AImovement_timer->Check()) { + if(!GetTarget() || (IsBotCaster() && !IsBotCasterCombatRange(GetTarget())) || (IsBotArcher() && IsArcheryRange(GetTarget())) || (DistNoRootNoZ(GetPreSummonX(), GetPreSummonY()) < 10)) { + if(GetTarget()) + FaceTarget(GetTarget()); + SetHasBeenSummoned(false); + } + else if(!IsRooted()) { + if(GetTarget() && GetTarget()->GetHateTop() && GetTarget()->GetHateTop() != this) + { + mlog(AI__WAYPOINTS, "Returning to location prior to being summoned."); + CalculateNewPosition2(GetPreSummonX(), GetPreSummonY(), GetPreSummonZ(), GetRunspeed()); + SetHeading(CalculateHeadingToTarget(GetPreSummonX(), GetPreSummonY())); + return; + } + } + + if(IsMoving()) + SendPosUpdate(); + else + SendPosition(); + } + } + else { + if(GetTarget()) + FaceTarget(GetTarget()); + SetHasBeenSummoned(false); + } + + return; + } + + if(!IsEngaged()) { + if(GetFollowID()) { + if(BotOwner && BotOwner->CastToClient()->AutoAttackEnabled() && BotOwner->GetTarget() && + BotOwner->GetTarget()->IsNPC() && BotOwner->GetTarget()->GetHateAmount(BotOwner)) { + AddToHateList(BotOwner->GetTarget(), 1); + + if(HasPet()) + GetPet()->AddToHateList(BotOwner->GetTarget(), 1); + } + else { + Group* g = GetGroup(); + + if(g) { + for(int counter = 0; counter < g->GroupCount(); counter++) { + if(g->members[counter]) { + if(g->members[counter]->IsEngaged() && g->members[counter]->GetTarget()) { + AddToHateList(g->members[counter]->GetTarget(), 1); + + if(HasPet()) + GetPet()->AddToHateList(g->members[counter]->GetTarget(), 1); + + break; + } + } + } + } + } + } + } + + if(IsEngaged()) + { + _ZP(Mob_BOT_Process_IsEngaged); + + if(rest_timer.Enabled()) + rest_timer.Disable(); + + if(IsRooted()) + SetTarget(hate_list.GetClosest(this)); + else + SetTarget(hate_list.GetTop(this)); + + if(!GetTarget()) + return; + + if(HasPet()) + GetPet()->SetTarget(GetTarget()); + + if(!IsSitting()) + FaceTarget(GetTarget()); + + if(DivineAura()) + return; + + // Let's check if we have a los with our target. + // If we don't, our hate_list is wiped. + // Else, it was causing the bot to aggro behind wall etc... causing massive trains. + if(!CheckLosFN(GetTarget()) || GetTarget()->IsMezzed() || !IsAttackAllowed(GetTarget())) { + WipeHateList(); + + if(IsMoving()) { + SetHeading(0); + SetRunAnimSpeed(0); + + if(moved) { + moved = false; + SendPosition(); + SetMoving(false); + } + } + + return; + } + + bool atCombatRange = false; + + float meleeDistance = GetMaxMeleeRangeToTarget(GetTarget()); + + if(botClass == SHADOWKNIGHT || botClass == PALADIN || botClass == WARRIOR) { + meleeDistance = meleeDistance * .30; + } + else { + meleeDistance *= (float)MakeRandomFloat(.50, .85); + } + + bool atArcheryRange = IsArcheryRange(GetTarget()); + + if(GetRangerAutoWeaponSelect()) { + bool changeWeapons = false; + + if(atArcheryRange && !IsBotArcher()) { + SetBotArcher(true); + changeWeapons = true; + } + else if(!atArcheryRange && IsBotArcher()) { + SetBotArcher(false); + changeWeapons = true; + } + + if(changeWeapons) + ChangeBotArcherWeapons(IsBotArcher()); + } + + if(IsBotArcher() && atArcheryRange) { + if(IsMoving()) { + SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); + SetRunAnimSpeed(0); + + if(moved) { + moved = false; + SendPosition(); + SetMoving(false); + } + } + + atCombatRange = true; + } + else if(IsBotCaster() && GetLevel() > 12) { + if(IsBotCasterCombatRange(GetTarget())) + atCombatRange = true; + } + else if(DistNoRoot(*GetTarget()) <= meleeDistance) { + atCombatRange = true; + } + + if(atCombatRange) { + if(IsMoving()) { + SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); + SetRunAnimSpeed(0); + + if(moved) { + moved = false; + SendPosition(); + SetMoving(false); + } + } + + 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)) { + CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); + return; + } + } + 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) { + CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); + return; + } + } + + if(IsMoving()) + SendPosUpdate(); + else + SendPosition(); + } + + if(IsBotArcher() && ranged_timer.Check(false)) { + if(GetTarget()->GetHPRatio() <= 99.0f) + BotRangedAttack(GetTarget()); + } + else if(!IsBotArcher() && (!(IsBotCaster() && GetLevel() > 12)) && 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; + + if(GetBotStance() == BotStancePassive) + return; + + // First, special attack per class (kick, backstab etc..) + DoClassAttacks(GetTarget()); + + //try main hand first + if(attack_timer.Check()) { + Attack(GetTarget(), SLOT_PRIMARY); + + bool tripleSuccess = false; + + if(BotOwner && GetTarget() && CanThisClassDoubleAttack()) { + + if(BotOwner && CheckBotDoubleAttack()) { + Attack(GetTarget(), SLOT_PRIMARY, true); + } + + if(BotOwner && GetTarget() && SpecAttacks[SPECATK_TRIPLE] && CheckBotDoubleAttack(true)) { + tripleSuccess = true; + Attack(GetTarget(), SLOT_PRIMARY, true); + } + + //quad attack, does this belong here?? + if(BotOwner && GetTarget() && SpecAttacks[SPECATK_QUAD] && CheckBotDoubleAttack(true)) { + Attack(GetTarget(), SLOT_PRIMARY, true); + } + } + + //Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack). + int16 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; + + if (GetTarget() && flurrychance) + { + if(MakeRandomInt(0, 100) < flurrychance) + { + Message_StringID(MT_NPCFlurry, 128); + Attack(GetTarget(), SLOT_PRIMARY, false); + Attack(GetTarget(), SLOT_PRIMARY, false); + } + } + + int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; + + if (GetTarget() && ExtraAttackChanceBonus) { + ItemInst *wpn = GetBotItem(SLOT_PRIMARY); + if(wpn){ + if(wpn->GetItem()->ItemType == ItemType2HS || + wpn->GetItem()->ItemType == ItemType2HB || + wpn->GetItem()->ItemType == ItemType2HPierce ) + { + if(MakeRandomInt(0, 100) < ExtraAttackChanceBonus) + { + Attack(GetTarget(), SLOT_PRIMARY, false); + } + } + } + } + } + + 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; + } + } + + //now off hand + if(GetTarget() && attack_dw_timer.Check() && CanThisClassDualWield()) { + const ItemInst* instweapon = GetBotItem(SLOT_SECONDARY); + const Item_Struct* weapon = 0; + //can only dual wield without a weapon if you're a monk + if(instweapon || (botClass == MONK)) { + if(instweapon) + weapon = instweapon->GetItem(); + + int weapontype = NULL; + bool bIsFist = true; + + if(weapon) { + weapontype = weapon->ItemType; + bIsFist = false; + } + + if(bIsFist || ((weapontype != ItemType2HS) && (weapontype != ItemType2HPierce) && (weapontype != ItemType2HB))) { + float DualWieldProbability = 0.0f; + + int16 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity; + DualWieldProbability = (GetSkill(DUAL_WIELD) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max + int16 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance; + DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f; + + float random = MakeRandomFloat(0, 1); + + if (random < DualWieldProbability){ // Max 78% of DW + + Attack(GetTarget(), SLOT_SECONDARY); // Single attack with offhand + + ItemInst *wpn = GetBotItem(SLOT_SECONDARY); + TryWeaponProc(wpn, GetTarget(), SLOT_SECONDARY); + + if( CanThisClassDoubleAttack() && CheckBotDoubleAttack()) { + if(GetTarget() && GetTarget()->GetHP() > -10) + Attack(GetTarget(), SLOT_SECONDARY); // 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 + if(GetBotStance() != BotStancePassive) + 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()) { + if(GetBotStance() == BotStancePassive) + return; + + if(AI_EngagedCastCheck()) { + BotMeditate(false); + } + else if(GetArchetype() == ARCHETYPE_CASTER) + BotMeditate(true); + } + } // end IsEngaged() + else { + // Not engaged in combat + SetTarget(0); + + if(!IsMoving() && AIthink_timer->Check() && !spellend_timer.Enabled()) { + if(GetBotStance() != BotStancePassive) { + if(!AI_IdleCastCheck() && !IsCasting()) + BotMeditate(true); + } + else { + BotMeditate(true); + } + + } + + if(AImovement_timer->Check()) { + if(GetFollowID()) { + Mob* follow = entity_list.GetMob(GetFollowID()); + + if(follow) { + float dist = DistNoRoot(*follow); + float speed = follow->GetRunspeed(); + + if(dist < GetFollowDistance() + 1000) + speed = follow->GetWalkspeed(); + + SetRunAnimSpeed(0); + + if(dist > GetFollowDistance()) { + CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed); + if(rest_timer.Enabled()) + rest_timer.Disable(); + return; + } + else + { + if(moved) + { + moved=false; + SendPosition(); + SetMoving(false); + } + } + } + } + } + } +} + +// AI Processing for a Bot object's pet +void Bot::PetAIProcess() { + _ZP(Bot_PET_Process); + + if( !HasPet() || !GetPet() || !GetPet()->IsNPC()) + return; + + Mob* BotOwner = this->GetBotOwner(); + NPC* botPet = this->GetPet()->CastToNPC(); + + if(!botPet->GetOwner() || !botPet->GetID() || !botPet->GetOwnerID()) { + Kill(); + return; + } + + if (!botPet->IsAIControlled()) + return; + + if(botPet->GetAttackTimer().Check(false)) + return; + + if (botPet->IsCasting()) + return; + + // Return if the owner of the bot pet isnt a bot. + if (!botPet->GetOwner()->IsBot()) + return; + + if (IsEngaged()) { + _ZP(Bot_PET_Process_IsEngaged); + + if (botPet->IsRooted()) + botPet->SetTarget(hate_list.GetClosest(botPet)); + else + botPet->SetTarget(hate_list.GetTop(botPet)); + + // Let's check if we have a los with our target. + // If we don't, our hate_list is wiped. + // It causes some cpu stress but without it, it was causing the bot/pet to aggro behind wall, floor etc... + if(!botPet->CheckLosFN(botPet->GetTarget()) || botPet->GetTarget()->IsMezzed() || !botPet->IsAttackAllowed(GetTarget())) { + botPet->WipeHateList(); + botPet->SetTarget(botPet->GetOwner()); + + return; + } + + botPet->FaceTarget(botPet->GetTarget()); + + // Lets see if we can let the main tank build a little aggro + /*if(GetBotRaidID()) { + BotRaids *br = entity_list.GetBotRaidByMob(GetOwner()); + if(br) { + if(br->GetBotMainTank() && (br->GetBotMainTank() != this)) { + if(br->GetBotMainTarget() && (br->GetBotMainTarget()->GetHateAmount(br->GetBotMainTank()) < 5000)) { + if(GetTarget() == br->GetBotMainTarget()) { + return; + } + } + } + } + }*/ + + bool is_combat_range = botPet->CombatRange(botPet->GetTarget()); + + // Ok, we're engaged, each class type has a special AI + // Only melee class will go to melee. Casters and healers will stay behind, following the leader by default. + // I should probably make the casters staying in place so they can cast.. + + // Ok, we 're a melee or any other class lvl<12. Yes, because after it becomes hard to go in melee for casters.. even for bots.. + if( is_combat_range ) { + botPet->GetAIMovementTimer()->Check(); + + if(botPet->IsMoving()) { + botPet->SetRunAnimSpeed(0); + botPet->SetHeading(botPet->GetTarget()->GetHeading()); + if(moved) { + moved=false; + botPet->SendPosition(); + botPet->SetMoving(false); + } + } + + if(!botPet->IsMoving()) { + float newX = 0; + float newY = 0; + float newZ = 0; + bool petHasAggro = false; + + if(botPet->GetTarget() && botPet->GetTarget()->GetHateTop() && botPet->GetTarget()->GetHateTop() == botPet) { + petHasAggro = true; + } + + if(botPet->GetClass() == ROGUE && !petHasAggro && !botPet->BehindMob(botPet->GetTarget(), botPet->GetX(), botPet->GetY())) { + // Move the rogue to behind the mob + if(botPet->PlotPositionAroundTarget(botPet->GetTarget(), newX, newY, newZ)) { + botPet->CalculateNewPosition2(newX, newY, newZ, botPet->GetRunspeed()); + return; + } + } + else if(GetTarget() == botPet->GetTarget() && !petHasAggro && !botPet->BehindMob(botPet->GetTarget(), botPet->GetX(), botPet->GetY())) { + // If the bot owner and the bot are fighting the same mob, then move the pet to the rear arc of the mob + if(botPet->PlotPositionAroundTarget(botPet->GetTarget(), newX, newY, newZ)) { + botPet->CalculateNewPosition2(newX, newY, newZ, botPet->GetRunspeed()); + return; + } + } + else if(botPet->DistNoRootNoZ(*botPet->GetTarget()) < botPet->GetTarget()->GetSize()) { + // Let's try to adjust our melee range so we don't appear to be bunched up + bool isBehindMob = false; + bool moveBehindMob = false; + + if(botPet->BehindMob(botPet->GetTarget(), botPet->GetX(), botPet->GetY())) + isBehindMob = true; + + if (!isBehindMob && !petHasAggro) + moveBehindMob = true; + + if(botPet->PlotPositionAroundTarget(botPet->GetTarget(), newX, newY, newZ, moveBehindMob)) { + botPet->CalculateNewPosition2(newX, newY, newZ, botPet->GetRunspeed()); + return; + } + } + } + + // we can't fight if we don't have a target, are stun/mezzed or dead.. + if(botPet->GetTarget() && !botPet->IsStunned() && !botPet->IsMezzed() && (botPet->GetAppearance() != eaDead)) { + // check the delay on the attack + if(botPet->GetAttackTimer().Check()) { + // Stop attacking while we are on a front arc and the target is enraged + if(!botPet->BehindMob(botPet->GetTarget(), botPet->GetX(), botPet->GetY()) && botPet->GetTarget()->IsEnraged()) + return; + + if(botPet->Attack(GetTarget(), SLOT_PRIMARY)) // try the main hand + if (botPet->GetTarget()) // Do we still have a target? + { + // We're a pet so we re able to dual attack + int32 RandRoll = MakeRandomInt(0, 99); + if (botPet->CanThisClassDoubleAttack() && (RandRoll < (botPet->GetLevel() + NPCDualAttackModifier))) + { + if(botPet->Attack(botPet->GetTarget(), 13)) + {} + } + } + + if (botPet->GetOwner()->IsBot()) { + int aa_chance = 0; + int aa_skill = 0; + // Magician AA + aa_skill += botPet->GetOwner()->GetAA(aaElementalAlacrity); + // Necromancer AA + aa_skill += botPet->GetOwner()->GetAA(aaQuickeningofDeath); + // Beastlord AA + aa_skill += botPet->GetOwner()->GetAA(aaWardersAlacrity); + + if(aa_skill >= 1) { + aa_chance += (aa_skill > 5 ? 5 : aa_skill) * 4; + } + if(aa_skill >= 6) { + aa_chance += (aa_skill-5 > 3 ? 3 : aa_skill-5) * 7; + } + if(aa_skill >= 9) { + aa_chance += (aa_skill-8 > 3 ? 3 : aa_skill-8) * 3; + } + if(aa_skill >= 12) { + aa_chance += (aa_skill - 11) * 1; + } + + //aa_chance += botPet->GetOwner()->GetAA(aaCompanionsAlacrity) * 3; + + if (MakeRandomInt(1, 100) < aa_chance) + Flurry(); + } + + // Ok now, let's check pet's offhand. + if (botPet->GetAttackDWTimer().Check() && botPet->GetOwnerID() && botPet->GetOwner() && ((botPet->GetOwner()->GetClass() == MAGICIAN) || (botPet->GetOwner()->GetClass() == NECROMANCER) || (botPet->GetOwner()->GetClass() == SHADOWKNIGHT) || (botPet->GetOwner()->GetClass() == BEASTLORD))) + { + if(botPet->GetOwner()->GetLevel() >= 24) + { + float DualWieldProbability = (botPet->GetSkill(DUAL_WIELD) + botPet->GetLevel()) / 400.0f; + DualWieldProbability -= MakeRandomFloat(0, 1); + if(DualWieldProbability < 0){ + botPet->Attack(botPet->GetTarget(), 14); + if (botPet->CanThisClassDoubleAttack()) + { + int32 RandRoll = MakeRandomInt(0, 99); + if (RandRoll < (botPet->GetLevel() + 20)) + { + botPet->Attack(botPet->GetTarget(), 14); + } + } + } + } + } + if(!botPet->GetOwner()) + return; + + // Special attack + botPet->DoClassAttacks(botPet->GetTarget()); + } + // See if the pet can cast any spell + botPet->AI_EngagedCastCheck(); + } + }// end of the combat in range + else{ + // Now, if we cannot reach our target + if (!botPet->HateSummon()) + { + if(botPet->GetTarget() && botPet->AI_PursueCastCheck()) + {} + else if (botPet->GetTarget() && botPet->GetAIMovementTimer()->Check()) + { + botPet->SetRunAnimSpeed(0); + if(!botPet->IsRooted()) { + mlog(AI__WAYPOINTS, "Pursuing %s while engaged.", botPet->GetTarget()->GetCleanName()); + botPet->CalculateNewPosition2(botPet->GetTarget()->GetX(), botPet->GetTarget()->GetY(), botPet->GetTarget()->GetZ(), botPet->GetOwner()->GetRunspeed()); + return; + } + else { + botPet->SetHeading(botPet->GetTarget()->GetHeading()); + if(moved) { + moved=false; + botPet->SendPosition(); + botPet->SetMoving(false); + } + } + } + } + } + } + else{ + // Franck: EQoffline + // Ok if we're not engaged, what's happening.. + if(botPet->GetTarget() != botPet->GetOwner()) { + botPet->SetTarget(botPet->GetOwner()); + } + + if(!IsMoving()) { + botPet->AI_IdleCastCheck(); + } + + if(botPet->GetAIMovementTimer()->Check()) { + switch(pStandingPetOrder) { + case SPO_Follow: + { + float dist = botPet->DistNoRoot(*botPet->GetTarget()); + botPet->SetRunAnimSpeed(0); + if(dist > 184) { + botPet->CalculateNewPosition2(botPet->GetTarget()->GetX(), botPet->GetTarget()->GetY(), botPet->GetTarget()->GetZ(), botPet->GetTarget()->GetRunspeed()); + return; + } + else { + botPet->SetHeading(botPet->GetTarget()->GetHeading()); + if(moved) { + moved=false; + botPet->SendPosition(); + botPet->SetMoving(false); + } + } + } + break; + case SPO_Sit: + botPet->SetAppearance(eaSitting); + break; + case SPO_Guard: + botPet->NextGuardPosition(); + break; + } + } + } +} + +void Bot::Depop() { + WipeHateList(); + + entity_list.RemoveFromHateLists(this); + + if(HasGroup()) + Bot::RemoveBotFromGroup(this, GetGroup()); + + if(HasPet()) { + GetPet()->Depop(); + } + + _botOwner = 0; + _botOwnerCharacterID = 0; + _previousTarget = 0; + + NPC::Depop(false); +} + +bool Bot::DeleteBot(std::string* errorMessage) { + bool Result = false; + int TempCounter = 0; + + if(this->GetBotID() > 0) { + char* Query = 0; + char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + + // 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 botinventory WHERE botid = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { + *errorMessage = std::string(TempErrorMessageBuffer); + } + else + TempCounter++; + + if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botbuffs WHERE botid = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { + *errorMessage = std::string(TempErrorMessageBuffer); + } + else + TempCounter++; + + if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botstances WHERE BotID = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { + *errorMessage = std::string(TempErrorMessageBuffer); + } + else + TempCounter++; + + 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; +} + +void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) { + if(GetBotID() > 0 && _botOwnerCharacterID > 0 && botCharacterOwner && botCharacterOwner->CharacterID() == _botOwnerCharacterID) { + // Rename the bot name to make sure that Mob::GetName() matches Mob::GetCleanName() so we dont have a bot named "Jesuschrist001" + strcpy(name, GetCleanName()); + + // Get the zone id this bot spawned in + _lastZoneId = GetZoneID(); + + this->helmtexture = 0xFF; + this->texture = 0xFF; + + if(this->Save()) + this->GetBotOwner()->CastToClient()->Message(0, "%s saved.", this->GetCleanName()); + else + this->GetBotOwner()->CastToClient()->Message(13, "%s save failed!", this->GetCleanName()); + + // Spawn the bot at the bow owner's loc + this->x_pos = botCharacterOwner->GetX(); + this->y_pos = botCharacterOwner->GetY(); + this->z_pos = botCharacterOwner->GetZ(); + + // Make the bot look at the bot owner + FaceTarget(botCharacterOwner); + + // Level the bot to the same level as the bot owner + //this->SetLevel(botCharacterOwner->GetLevel()); + + entity_list.AddBot(this, true, true); + + // Load pet + LoadPet(); + + this->SendPosition(); + + /* // fillspawnstruct now properly handles this -U + uint32 itemID = 0; + uint8 materialFromSlot = 0xFF; + for(int i=0; i<22; ++i) { + itemID = GetBotItemBySlot(i); + if(itemID != 0) { + materialFromSlot = Inventory::CalcMaterialFromSlot(i); + if(materialFromSlot != 0xFF) { + this->SendWearChange(materialFromSlot); + } + } + }*/ + } +} + +// 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[5] = { 0, 0, 0, 0, 0 }; + + if(this->GetBotID() > 0 && slotID >= 0 && itemID > 0) { + if (inst && inst->IsType(ItemClassCommon)) { + for(int i=0; i<5; ++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); + } + + safe_delete_array(query); + } +} + +// 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); + } +} + +// 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(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[5]; + 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; + + ItemInst* inst = database.CreateItem(item_id, charges, aug[0], aug[1], aug[2], aug[3], aug[4]); + if(inst) { + int16 put_slot_id = SLOT_INVALID; + if(instnodrop || ((slot_id >= 0) && (slot_id <= 21) && 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); + + // Save ptr to item in inventory + if (put_slot_id == SLOT_INVALID) { + 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); + + 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 >= 0) { + char* query = 0; + MYSQL_RES* DatasetResult; + MYSQL_ROW DataRow; + + 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]); + } + + mysql_free_result(DatasetResult); + } + + safe_delete_array(query); + } + + return Result; +} + +// 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(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]); + } + + mysql_free_result(DatasetResult); + } + else + *errorMessage = std::string(errbuf); + + safe_delete_array(query); + } + + return Result; +} + +bool Bot::MesmerizeTarget(Mob* target) { + bool Result = false; + + if(target) { + int mezid = 0; + int mezlevel = GetLevel(); + + if(mezlevel >= 69) { + mezid = 5520; + } + else if(mezlevel == 68) { + mezid = 8035; + } + else if(mezlevel == 67) { + mezid = 5503; + } + else if(mezlevel >= 64) { + mezid = 3358; + } + else if(mezlevel == 63) { + mezid = 3354; + } + else if(mezlevel >= 61) { + mezid = 3341; + } + else if(mezlevel == 60) { + mezid = 2120; + } + else if(mezlevel == 59) { + mezid = 1692; + } + else if(mezlevel >= 54) { + mezid = 1691; + } + else if(mezlevel >= 47) { + mezid = 190; + } + else if(mezlevel >= 30) { + mezid = 188; + } + else if(mezlevel >= 13) { + mezid = 187; + } + else if(mezlevel >= 2) { + mezid = 292; + } + if(mezid > 0) { + uint32 DontRootMeBeforeTime = 0; + CastSpell(mezid, target->GetID(), 1, -1, -1, &DontRootMeBeforeTime); + target->SetDontRootMeBefore(DontRootMeBeforeTime); + Result = true; + } + } + + return Result; +} + +void Bot::SetLevel(uint8 in_level, bool command) { + if(in_level > 0) { + Mob::SetLevel(in_level, command); + } +} + +void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { + if(ns) { + Mob::FillSpawnStruct(ns, ForWho); + + ns->spawn.afk = 0; + ns->spawn.lfg = 0; + ns->spawn.anon = 0; + ns->spawn.gm = 0; + if(IsInAGuild()) + ns->spawn.guildID = GuildID(); + else + ns->spawn.guildID = 0xFFFFFFFF; // 0xFFFFFFFF = NO GUILD, 0 = Unknown Guild + ns->spawn.is_npc = 0; // 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 = 0; // 0=player,1=npc,2=pc corpse,3=npc corpse + + ns->spawn.helm = 0xFF; + ns->spawn.equip_chest2 = 0xFF; + + const Item_Struct* item = 0; + const ItemInst* inst = 0; + + uint32 spawnedbotid = 0; + spawnedbotid = this->GetBotID(); + + 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); + } + } + + 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); + } + } + } +} + +uint32 Bot::GetBotIDByBotName(std::string botName) { + uint32 Result = 0; + + if(!botName.empty()) { + char* Query = 0; + char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + MYSQL_RES* DatasetResult; + MYSQL_ROW DataRow; + std::string errorMessage; + + 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; + } + + mysql_free_result(DatasetResult); + } + + safe_delete_array(Query); + + if(!errorMessage.empty()) { + // TODO: Log this error to zone error log + } + } + + return Result; +} + +Bot* Bot::LoadBot(uint32 botID, std::string* errorMessage) { + Bot* Result = 0; + + if(botID > 0) { + char* Query = 0; + char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + MYSQL_RES* DatasetResult; + MYSQL_ROW DataRow; + + 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; + } + + mysql_free_result(DatasetResult); + } + + safe_delete_array(Query); + } + + return Result; +} + +std::list Bot::GetGroupedBotsByGroupId(uint32 groupId, std::string* errorMessage) { + std::list Result; + + if(groupId > 0) { + char* Query = 0; + char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + MYSQL_RES* DatasetResult; + MYSQL_ROW DataRow; + + 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); + } + + return Result; +} + +// Load and spawn all zoned bots by bot owner character +void Bot::LoadAndSpawnAllZonedBots(Client* botOwner) { + if(botOwner) { + if(botOwner->HasGroup()) { + Group* g = botOwner->GetGroup(); + if(g) { + uint32 TempGroupId = g->GetID(); + std::string errorMessage; + std::list ActiveBots = Bot::GetGroupedBotsByGroupId(botOwner->GetGroup()->GetID(), &errorMessage); + + if(errorMessage.empty() && !ActiveBots.empty()) { + for(list::iterator itr = ActiveBots.begin(); itr != ActiveBots.end(); itr++) { + Bot* activeBot = Bot::LoadBot(*itr, &errorMessage); + + if(!errorMessage.empty()) { + safe_delete(activeBot); + break; + } + + if(activeBot) { + activeBot->Spawn(botOwner, &errorMessage); + + g->UpdatePlayer(activeBot); + + if(g->GetLeader()) + activeBot->SetFollowID(g->GetLeader()->GetID()); + } + + if(activeBot && !botOwner->HasGroup()) + database.SetGroupID(activeBot->GetCleanName(), 0, activeBot->GetBotID()); + } + } + + // Catch all condition for error messages destined for the zone error log + if(!errorMessage.empty()) { + // TODO: Log this error message to zone error log + } + } + } + } +} + +// Returns TRUE if there is atleast 1 bot in the specified group +bool Bot::GroupHasBot(Group* group) { + bool Result = false; + + if(group) { + for(int Counter = 0; Counter < MAX_GROUP_MEMBERS; Counter++) { + if (group->members[Counter] == NULL) { + continue; + } + + if(group->members[Counter]->IsBot()) { + Result = true; + break; + } + } + } + + return Result; +} + +std::list Bot::GetBotList(uint32 botOwnerCharacterID, std::string* errorMessage) { + std::list Result; + + if(botOwnerCharacterID > 0) { + char* Query = 0; + char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + MYSQL_RES* DatasetResult; + MYSQL_ROW DataRow; + + 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); + } + + return Result; +} + +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; + + 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(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]); + + Result.push_back(TempSpawnedBotsList); + } + } + + mysql_free_result(DatasetResult); + } + + safe_delete_array(Query); + } + + return Result; +} + +void Bot::SaveBotGroup(Group* botGroup, std::string botGroupName, std::string* errorMessage) { + if(botGroup && !botGroupName.empty()) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + Mob* tempGroupLeader = botGroup->GetLeader(); + + if(tempGroupLeader->IsBot()) { + 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]; + + if(tempBot && tempBot->IsBot()) { + uint32 botGroupMemberBotId = tempBot->CastToBot()->GetBotID(); + + safe_delete_array(query); + + if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT into botgroupmembers (BotGroupId, BotId) values (%u, %u)", botGroupId, botGroupMemberBotId), errbuf)) { + *errorMessage = std::string(errbuf); + } + } + } + } + } + + 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(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); + + if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM botgroup WHERE BotGroupId = %u", botGroupId), errbuf)) { + *errorMessage = std::string(errbuf); + } + } + + safe_delete_array(query); + } + } +} + +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; + + if(!botGroupName.empty()) { + uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); + + 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); + + if(RowCount > 0) { + for(int iCounter = 0; iCounter < RowCount; iCounter++) { + DataRow = mysql_fetch_row(DatasetResult); + + if(DataRow) { + BotGroup tempBotGroup; + tempBotGroup.BotGroupID = botGroupId; + tempBotGroup.BotID = atoi(DataRow[0]); + + Result.push_back(tempBotGroup); + } + } + } + + mysql_free_result(DatasetResult); + } + + safe_delete_array(Query); + } + } + + return Result; +} + +std::list Bot::GetBotGroupListByBotOwnerCharacterId(uint32 botOwnerCharacterId, std::string* errorMessage) { + std::list result; + + if(botOwnerCharacterId > 0) { + char ErrBuf[MYSQL_ERRMSG_SIZE]; + char* Query = 0; + MYSQL_RES* DatasetResult; + MYSQL_ROW DataRow; + + 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); + + if(RowCount > 0) { + for(int iCounter = 0; iCounter < RowCount; iCounter++) { + DataRow = mysql_fetch_row(DatasetResult); + + if(DataRow) { + BotGroupList botGroupList; + botGroupList.BotGroupName = std::string(DataRow[0]); + botGroupList.BotGroupLeaderName = std::string(DataRow[1]); + + result.push_back(botGroupList); + } + } + } + + mysql_free_result(DatasetResult); + } + + safe_delete_array(Query); + } + + return result; +} + +bool Bot::DoesBotGroupNameExist(std::string botGroupName) { + bool result = false; + + if(!botGroupName.empty()) { + char* Query = 0; + MYSQL_RES* DatasetResult; + MYSQL_ROW DataRow; + + if(database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupId from vwBotGroups where BotGroupName = '%s'", botGroupName.c_str()), 0, &DatasetResult)) { + uint32 RowCount = mysql_num_rows(DatasetResult); + + if(RowCount > 0) { + for(int iCounter = 0; iCounter < RowCount; iCounter++) { + DataRow = mysql_fetch_row(DatasetResult); + + if(DataRow) { + uint32 tempBotGroupId = atoi(DataRow[0]); + std::string tempBotGroupName = std::string(DataRow[1]); + + if(botGroupName == tempBotGroupName) { + result = tempBotGroupId; + break; + } + } + } + } + + mysql_free_result(DatasetResult); + } + + safe_delete_array(Query); + } + + return result; +} + +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(!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); + + if(RowCount > 0) { + for(int iCounter = 0; iCounter < RowCount; iCounter++) { + DataRow = mysql_fetch_row(DatasetResult); + + if(DataRow) { + uint32 tempBotGroupId = atoi(DataRow[0]); + std::string tempBotGroupName = std::string(DataRow[1]); + + if(botGroupName == tempBotGroupName) { + result = tempBotGroupId; + break; + } + } + } + } + + mysql_free_result(DatasetResult); + } + + safe_delete_array(Query); + } + + return result; +} + +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(!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); + + 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; +} + +uint32 Bot::GetBotGroupLeaderIdByBotGroupName(std::string botGroupName) { + uint32 result = 0; + + if(!botGroupName.empty()) { + char* Query = 0; + MYSQL_RES* DatasetResult; + MYSQL_ROW DataRow; + + if(database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupLeaderBotId from vwBotGroups where BotGroupName = '%s'", botGroupName.c_str()), 0, &DatasetResult)) { + uint32 RowCount = mysql_num_rows(DatasetResult); + + 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; +} + +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(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]); + } + + mysql_free_result(DatasetResult); + } + else + *errorMessage = std::string(ErrBuf); + + safe_delete_array(Query); + } + + return Result; +} + +uint32 Bot::SpawnedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage) { + uint32 Result = 0; + + if(botOwnerCharacterID > 0) { + std::list SpawnedBots = entity_list.GetBotsByBotOwnerCharacterID(botOwnerCharacterID); + + Result = SpawnedBots.size(); + } + + return Result; +} + +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(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]); + } + + mysql_free_result(DatasetResult); + } + else + *errorMessage = std::string(ErrBuf); + + safe_delete_array(Query); + } + + return Result; +} + +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(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]); + } + + mysql_free_result(DatasetResult); + } + else + *errorMessage = std::string(ErrBuf); + + safe_delete_array(Query); + } + + return Result; +} + +std::string Bot::ClassIdToString(uint16 classId) { + std::string Result; + + if(classId > 0 && classId < 17) { + switch(classId) { + case 1: + Result = std::string("Warrior"); + break; + case 2: + Result = std::string("Cleric"); + break; + case 3: + Result = std::string("Paladin"); + break; + case 4: + Result = std::string("Ranger"); + break; + case 5: + Result = std::string("Shadowknight"); + break; + case 6: + Result = std::string("Druid"); + break; + case 7: + Result = std::string("Monk"); + break; + case 8: + Result = std::string("Bard"); + break; + case 9: + Result = std::string("Rogue"); + break; + case 10: + Result = std::string("Shaman"); + break; + case 11: + Result = std::string("Necromancer"); + break; + case 12: + Result = std::string("Wizard"); + break; + case 13: + Result = std::string("Magician"); + break; + case 14: + Result = std::string("Enchanter"); + break; + case 15: + Result = std::string("Beastlord"); + break; + case 16: + Result = std::string("Berserker"); + break; + } + } + + return Result; +} + +std::string Bot::RaceIdToString(uint16 raceId) { + std::string Result; + + if(raceId > 0) { + switch(raceId) { + case 1: + Result = std::string("Human"); + break; + case 2: + Result = std::string("Barbarian"); + break; + case 3: + Result = std::string("Erudite"); + break; + case 4: + Result = std::string("Wood Elf"); + break; + case 5: + Result = std::string("High Elf"); + break; + case 6: + Result = std::string("Dark Elf"); + break; + case 7: + Result = std::string("Half Elf"); + break; + case 8: + Result = std::string("Dwarf"); + break; + case 9: + Result = std::string("Troll"); + break; + case 10: + Result = std::string("Ogre"); + break; + case 11: + Result = std::string("Halfling"); + break; + case 12: + Result = std::string("Gnome"); + break; + case 128: + Result = std::string("Iksar"); + break; + case 130: + Result == std::string("Vah Shir"); + break; + case 330: + Result = std::string("Froglok"); + break; + case 522: + Result = std::string("Drakkin"); + break; + } + } + + return Result; +} + +void Bot::SendBotArcheryWearChange(uint8 material_slot, uint32 material, uint32 color) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct)); + WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer; + + wc->spawn_id = GetID(); + wc->material = material; + wc->color.color = color; + wc->wear_slot_id = material_slot; + + entity_list.QueueClients(this, outapp); + safe_delete(outapp); +} + +// Returns the item id that is in the bot inventory collection for the specified slot. +ItemInst* Bot::GetBotItem(uint32 slotID) { + ItemInst* item = m_inv.GetItem(slotID); + if(item){ + return item; + } + + return NULL; +} + +// Adds the specified item it bot to the NPC equipment array and to the bot inventory collection. +void Bot::BotAddEquipItem(int slot, uint32 id) { + if(slot > 0 && id > 0) { + uint8 materialFromSlot = Inventory::CalcMaterialFromSlot(slot); + + if(materialFromSlot != 0xFF) { + equipment[slot] = id; // npc has more than just material slots. Valid material should mean valid inventory index + SendWearChange(materialFromSlot); + } + } +} + +// Erases the specified item from bot the NPC equipment array and from the bot inventory collection. +void Bot::BotRemoveEquipItem(int slot) { + if(slot > 0) { + uint8 materialFromSlot = Inventory::CalcMaterialFromSlot(slot); + + if(materialFromSlot != 0xFF) { + equipment[slot] = 0; // npc has more than just material slots. Valid material should mean valid inventory index + SendWearChange(materialFromSlot); + if(materialFromSlot == MATERIAL_CHEST) + SendWearChange(MATERIAL_ARMS); + } + } +} + +void Bot::BotTradeSwapItem(Client* client, int16 lootSlot, const ItemInst* inst, const ItemInst* inst_swap, uint32 equipableSlots, std::string* errorMessage, bool swap) { + + if(!errorMessage->empty()) + return; + + client->PushItemOnCursor(*inst_swap, true); + + // Remove the item from the bot and from the bot's database records + RemoveBotItemBySlot(lootSlot, errorMessage); + + if(!errorMessage->empty()) + return; + + this->BotRemoveEquipItem(lootSlot); + + if(swap) { + BotTradeAddItem(inst->GetItem()->ID, inst, inst->GetCharges(), equipableSlots, lootSlot, errorMessage); + + if(!errorMessage->empty()) + return; + } +} + +void Bot::BotTradeAddItem(uint32 id, const ItemInst* inst, int16 charges, uint32 equipableSlots, uint16 lootSlot, std::string* errorMessage, bool addToDb) { + if(addToDb) { + this->SetBotItemInSlot(lootSlot, id, inst, errorMessage); + if(!errorMessage->empty()) + return; + m_inv.PutItem(lootSlot, *inst); + } + + this->BotAddEquipItem(lootSlot, id); +} + +bool Bot::Bot_Command_Resist(int resisttype, int level) { + int resistid = 0; + switch(resisttype) { + case 1: // Poison Cleric + if(level >= 30) { + resistid = 62; + } + else if(level >= 6) { + resistid = 227; + } + break; + case 2: // Disease Cleric + if(level >= 36) { + resistid = 63; + } + else if(level >= 11) { + resistid = 226; + } + break; + case 3: // Fire Cleric + if(level >= 33) { + resistid = 60; + } + else if(level >= 8) { + resistid = 224; + } + break; + case 4: // Cold Cleric + if(level >= 38) { + resistid = 61; + } + else if(level >= 13) { + resistid = 225; + } + break; + case 5: // Magic Cleric + if(level >= 43) { + resistid = 64; + } + else if(level >= 16) { + resistid = 228; + } + break; + case 6: // Magic Enchanter + if(level >= 37) { + resistid = 64; + } + else if(level >= 17) { + resistid = 228; + } + break; + case 7: // Poison Druid + if(level >= 44) { + resistid = 62; + } + else if(level >= 19) { + resistid = 227; + } + break; + case 8: // Disease Druid + if(level >= 44) { + resistid = 63; + } + else if(level >= 19) { + resistid = 226; + } + break; + case 9: // Fire Druid + if(level >= 20) { + resistid = 60; + } + else if(level >= 1) { + resistid = 224; + } + break; + case 10: // Cold Druid + if(level >= 30) { + resistid = 61; + } + else if(level >= 9) { + resistid = 225; + } + break; + case 11: // Magic Druid + if(level >= 49) { + resistid = 64; + } + else if(level >= 34) { + resistid = 228; + } + break; + case 12: // Poison Shaman + if(level >= 35) { + resistid = 62; + } + else if(level >= 20) { + resistid = 227; + } + break; + case 13: // Disease Shaman + if(level >= 30) { + resistid = 63; + } + else if(level >= 8) { + resistid = 226; + } + break; + case 14: // Fire Shaman + if(level >= 27) { + resistid = 60; + } + else if(level >= 5) { + resistid = 224; + } + break; + case 15: // Cold Shaman + if(level >= 24) { + resistid = 61; + } + else if(level >= 1) { + resistid = 225; + } + break; + case 16: // Magic Shaman + if(level >= 43) { + resistid = 64; + } + else if(level >= 19) { + resistid = 228; + } + break; + } + + if(resistid > 0) { + Group* g = GetGroup(); + if(g) { + for(int k=0; kmembers[k]) { + SpellOnTarget(resistid, g->members[k]); + } + } + return true; + } + } + + return false; +} + +bool Bot::RemoveBotFromGroup(Bot* bot, Group* group) { + bool Result = false; + + if(bot && group) { + if(bot->HasGroup()) { + if(!group->IsLeader(bot)) { + bot->SetFollowID(0); + + if(group->DelMember(bot)) + database.SetGroupID(bot->GetCleanName(), 0, bot->GetBotID()); + + if(group->GroupCount() <= 1 && ZoneLoaded) + group->DisbandGroup(); + } + else { + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(!group->members[i]) + continue; + + group->members[i]->SetFollowID(0); + } + + group->DisbandGroup(); + database.SetGroupID(bot->GetCleanName(), 0, bot->GetBotID()); + } + + Result = true; + } + } + + return Result; +} + +bool Bot::AddBotToGroup(Bot* bot, Group* group) { + bool Result = false; + + if(bot && group) { + if(!bot->HasGroup()) { + // Add bot to this group + if(group->AddMember(bot)) { + if(group->GetLeader()) { + bot->SetFollowID(group->GetLeader()->GetID()); + + // Need to send this only once when a group is formed with a bot so the client knows it is also the group leader + if(group->GroupCount() == 2 && group->GetLeader()->IsClient()) { + group->UpdateGroupAAs(); + Mob *TempLeader = group->GetLeader(); + group->SendUpdate(groupActUpdate, TempLeader); + } + } + + Result = true; + } + } + } + + return Result; +} + +bool Bot::BotGroupCreate(std::string botGroupLeaderName) { + bool Result = false; + + if(!botGroupLeaderName.empty()) { + Bot* botGroupLeader = entity_list.GetBotByBotName(botGroupLeaderName); + + if(botGroupLeader) + Result = BotGroupCreate(botGroupLeader); + } + + return Result; +} + +bool Bot::BotGroupCreate(Bot* botGroupLeader) { + bool Result = false; + + if(botGroupLeader && !botGroupLeader->HasGroup()) { + Group* newGroup = new Group(botGroupLeader); + + if(newGroup) { + entity_list.AddGroup(newGroup); + database.SetGroupID(botGroupLeader->GetName(), newGroup->GetID(), botGroupLeader->GetBotID()); + database.SetGroupLeaderName(newGroup->GetID(), botGroupLeader->GetName()); + + botGroupLeader->SetFollowID(botGroupLeader->GetBotOwner()->GetID()); + + Result = true; + } + } + + return Result; +} + +bool Bot::Bot_Command_CharmTarget(int charmtype, Mob *target) { + int charmid = 0; + int charmlevel = GetLevel(); + if(target) { + switch(charmtype) { + case 1: // Enchanter + if((charmlevel >= 64) && (charmlevel <= 75)) { + charmid = 3355; + } + else if((charmlevel >= 62) && (charmlevel <= 63)) { + charmid = 3347; + } + else if((charmlevel >= 60) && (charmlevel <= 61)) { + charmid = 1707; + } + else if((charmlevel >= 53) && (charmlevel <= 59)) { + charmid = 1705; + } + else if((charmlevel >= 37) && (charmlevel <= 52)) { + charmid = 183; + } + else if((charmlevel >= 23) && (charmlevel <= 36)) { + charmid = 182; + } + else if((charmlevel >= 11) && (charmlevel <= 22)) { + charmid = 300; + } + break; + case 2: // Necromancer + if((charmlevel >= 60) && (charmlevel <= 75)) { + charmid = 1629; + } + else if((charmlevel >=47) && (charmlevel <= 59)) { + charmid = 198; + } + else if((charmlevel >= 31) && (charmlevel <= 46)) { + charmid = 197; + } + else if((charmlevel >= 18) && (charmlevel <= 30)) { + charmid = 196; + } + break; + case 3: // Druid + if((charmlevel >= 63) && (charmlevel <= 75)) { + charmid = 3445; + } + else if((charmlevel >= 55) && (charmlevel <= 62)) { + charmid = 1556; + } + else if((charmlevel >= 52) && (charmlevel <= 54)) { + charmid = 1553; + } + else if((charmlevel >= 43) && (charmlevel <= 51)) { + charmid = 142; + } + else if((charmlevel >= 33) && (charmlevel <= 42)) { + charmid = 141; + } + else if((charmlevel >= 23) && (charmlevel <= 32)) { + charmid = 260; + } + else if((charmlevel >= 13) && (charmlevel <= 22)) { + charmid = 242; + } + break; + } + if(charmid > 0) { + uint32 DontRootMeBeforeTime = 0; + CastSpell(charmid, target->GetID(), 1, -1, -1, &DontRootMeBeforeTime); + target->SetDontRootMeBefore(DontRootMeBeforeTime); + return true; + } + } + return false; +} + +bool Bot::Bot_Command_DireTarget(int diretype, Mob *target) { + int direid = 0; + int direlevel = GetLevel(); + if(target) { + switch(diretype) { + case 1: // Enchanter + if(direlevel >= 65) { + direid = 5874; + } + else if(direlevel >= 55) { + direid = 2761; + } + break; + case 2: // Necromancer + if(direlevel >= 65) { + direid = 5876; + } + else if(direlevel >= 55) { + direid = 2759; + } + break; + case 3: // Druid + if(direlevel >= 65) { + direid = 5875; + } + else if(direlevel >= 55) { + direid = 2760; + } + break; + } + if(direid > 0) { + uint32 DontRootMeBeforeTime = 0; + CastSpell(direid, target->GetID(), 1, -1, -1, &DontRootMeBeforeTime); + target->SetDontRootMeBefore(DontRootMeBeforeTime); + return true; + } + } + return false; +} + +bool Bot::Bot_Command_CalmTarget(Mob *target) { + if(target) { + int calmid = 0; + int calmlevel = GetLevel(); + if((calmlevel >= 67) && (calmlevel <= 75)) { + calmid = 5274; + } + else if((calmlevel >= 62) && (calmlevel <= 66)) { + calmid = 3197; + } + else if((calmlevel >= 35) && (calmlevel <= 61)) { + calmid = 45; + } + else if((calmlevel >= 18) && (calmlevel <= 34)) { + calmid = 47; + } + else if((calmlevel >= 6) && (calmlevel <= 17)) { + calmid = 501; + } + else if((calmlevel >= 1) && (calmlevel <= 5)) { + calmid = 208; + } + if(calmid > 0) { + uint32 DontRootMeBeforeTime = 0; + CastSpell(calmid, target->GetID(), 1, -1, -1, &DontRootMeBeforeTime); + target->SetDontRootMeBefore(DontRootMeBeforeTime); + return true; + } + } + return false; +} + +bool Bot::Bot_Command_RezzTarget(Mob *target) { + if(target) { + int rezid = 0; + int rezlevel = GetLevel(); + if(rezlevel >= 56) { + rezid = 1524; + } + else if(rezlevel >= 47) { + rezid = 392; + } + else if(rezlevel >= 42) { + rezid = 2172; + } + else if(rezlevel >= 37) { + rezid = 388; + } + else if(rezlevel >= 32) { + rezid = 2171; + } + else if(rezlevel >= 27) { + rezid = 391; + } + else if(rezlevel >= 22) { + rezid = 2170; + } + else if(rezlevel >= 18) { + rezid = 2169; + } + if(rezid > 0) { + uint32 DontRootMeBeforeTime = 0; + CastSpell(rezid, target->GetID(), 1, -1, -1, &DontRootMeBeforeTime); + target->SetDontRootMeBefore(DontRootMeBeforeTime); + return true; + } + } + return false; +} + +bool Bot::Bot_Command_Cure(int curetype, int level) { + int cureid = 0; + switch(curetype) { + case 1: // Poison + if(level >= 58) { + cureid = 1525; + } + else if(level >= 48) { + cureid = 97; + } + else if(level >= 22) { + cureid = 95; + } + else if(level >= 1) { + cureid = 203; + } + break; + case 2: // Disease + if(level >= 51) { + cureid = 3693; + } + else if(level >= 28) { + cureid = 96; + } + else if(level >= 4) { + cureid = 213; + } + break; + case 3: // Curse + if(level >= 54) { + cureid = 2880; + } + else if(level >= 38) { + cureid = 2946; + } + else if(level >= 23) { + cureid = 4057; + } + else if(level >= 8) { + cureid = 4056; + } + break; + case 4: // Blindness + if(level >= 3) { + cureid = 212; + } + break; + } + + if(cureid > 0) { + Group* g = GetGroup(); + if(g) { + for(int k=0; kmembers[k]) { + SpellOnTarget(cureid, g->members[k]); + } + } + return true; + } + } + + return false; +} + +// Completes a trade with a client bot owner +void Bot::FinishTrade(Client* client, BotTradeType tradeType) { + if(client && !client->GetTradeskillObject() && (client->trade->state != Trading)) { + if(tradeType == BotTradeClientNormal) { + // Items being traded are found in the normal trade window used to trade between a Client and a Client or NPC + // Items in this mode are found in slot ids 3000 thru 3003 + PerformTradeWithClient(3000, 3007, client); + } + else if(tradeType == BotTradeClientNoDropNoTrade) { + // Items being traded are found on the Client's cursor slot, slot id 30. This item can be either a single item or it can be a bag. + // If it is a bag, then we have to search for items in slots 331 thru 340 + PerformTradeWithClient(SLOT_CURSOR, SLOT_CURSOR, client); + + // TODO: Add logic here to test if the item in SLOT_CURSOR is a container type, if it is then we need to call the following: + // PerformTradeWithClient(331, 340, client); + } + } +} + +// Perfoms the actual trade action with a client bot owner +void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* client) { + if(client) { + // TODO: Figure out what the actual max slot id is + const int MAX_SLOT_ID = 3179; + uint32 items[MAX_SLOT_ID] = {0}; + uint8 charges[MAX_SLOT_ID] = {0}; + bool botCanWear[MAX_SLOT_ID] = {0}; + + for(int16 i=beginSlotID; i<=endSlotID; ++i) { + bool BotCanWear = false; + bool UpdateClient = false; + bool already_returned = false; + + Inventory& clientInventory = client->GetInv(); + const ItemInst* inst = clientInventory[i]; + if(inst) { + items[i] = inst->GetItem()->ID; + charges[i] = inst->GetCharges(); + } + + if(i == SLOT_CURSOR) + UpdateClient = true; + + //EQoffline: will give the items to the bots and change the bot stats + if(inst && (GetBotOwner() == client->CastToMob()) && !IsEngaged()) { + std::string TempErrorMessage; + const Item_Struct* mWeaponItem = inst->GetItem(); + bool failedLoreCheck = false; + for(int m=0; mGetAugment(m); + if(itm) + { + if(CheckLoreConflict(itm->GetItem())) { + failedLoreCheck = true; + } + } + } + if(CheckLoreConflict(mWeaponItem)) { + failedLoreCheck = true; + } + if(failedLoreCheck) { + Message_StringID(0, DUP_LORE); + } + if(!failedLoreCheck && mWeaponItem && inst->IsEquipable(GetBaseRace(), GetClass()) && (GetLevel() >= mWeaponItem->ReqLevel)) { + BotCanWear = true; + botCanWear[i] = BotCanWear; + ItemInst* swap_item = NULL; + + const char* equipped[22] = {"Charm", "Left Ear", "Head", "Face", "Right Ear", "Neck", "Shoulders", "Arms", "Back", + "Left Wrist", "Right Wrist", "Range", "Hands", "Primary Hand", "Secondary Hand", + "Left Finger", "Right Finger", "Chest", "Legs", "Feet", "Waist", "Ammo" }; + bool success = false; + int how_many_slots = 0; + for(int j=0; j<22; ++j) { + if((mWeaponItem->Slots & (1 << j))) { + how_many_slots++; + if(!GetBotItem(j)) { + if(j == SLOT_PRIMARY) { + if((mWeaponItem->ItemType == ItemType2HS) || (mWeaponItem->ItemType == ItemType2HB) || (mWeaponItem->ItemType == ItemType2HPierce)) { + if(GetBotItem(SLOT_SECONDARY)) { + if(mWeaponItem && (mWeaponItem->ItemType == ItemType2HS) || (mWeaponItem->ItemType == ItemType2HB) || (mWeaponItem->ItemType == ItemType2HPierce)) { + if(client->CheckLoreConflict(GetBotItem(SLOT_SECONDARY)->GetItem())) { + failedLoreCheck = true; + } + } + else { + ItemInst* remove_item = GetBotItem(SLOT_SECONDARY); + BotTradeSwapItem(client, SLOT_SECONDARY, 0, remove_item, remove_item->GetItem()->Slots, &TempErrorMessage, false); + } + } + } + if(!failedLoreCheck) { + BotTradeAddItem(mWeaponItem->ID, inst, inst->GetCharges(), mWeaponItem->Slots, j, &TempErrorMessage); + success = true; + } + break; + } + else if(j == SLOT_SECONDARY) { + if(inst->IsWeapon()) { + if(CanThisClassDualWield()) { + BotTradeAddItem(mWeaponItem->ID, inst, inst->GetCharges(), mWeaponItem->Slots, j, &TempErrorMessage); + success = true; + } + else { + Say("I can't Dual Wield yet."); + --how_many_slots; + } + } + else { + BotTradeAddItem(mWeaponItem->ID, inst, inst->GetCharges(), mWeaponItem->Slots, j, &TempErrorMessage); + success = true; + } + if(success) { + if(GetBotItem(SLOT_PRIMARY)) { + ItemInst* remove_item = GetBotItem(SLOT_PRIMARY); + if((remove_item->GetItem()->ItemType == ItemType2HS) || (remove_item->GetItem()->ItemType == ItemType2HB) || (remove_item->GetItem()->ItemType == ItemType2HPierce)) { + BotTradeSwapItem(client, SLOT_PRIMARY, 0, remove_item, remove_item->GetItem()->Slots, &TempErrorMessage, false); + } + } + break; + } + } + else { + BotTradeAddItem(mWeaponItem->ID, inst, inst->GetCharges(), mWeaponItem->Slots, j, &TempErrorMessage); + success = true; + break; + } + } + } + } + if(!success) { + for(int j=0; j<22; ++j) { + if((mWeaponItem->Slots & (1 << j))) { + swap_item = GetBotItem(j); + failedLoreCheck = false; + for(int k=0; kGetAugment(k); + if(itm) + { + if(client->CheckLoreConflict(itm->GetItem())) { + failedLoreCheck = true; + } + } + } + if(client->CheckLoreConflict(swap_item->GetItem())) { + failedLoreCheck = true; + } + if(!failedLoreCheck) { + if(j == SLOT_PRIMARY) { + if((mWeaponItem->ItemType == ItemType2HS) || (mWeaponItem->ItemType == ItemType2HB) || (mWeaponItem->ItemType == ItemType2HPierce)) { + if(GetBotItem(SLOT_SECONDARY)) { + if(client->CheckLoreConflict(GetBotItem(SLOT_SECONDARY)->GetItem())) { + failedLoreCheck = true; + } + else { + ItemInst* remove_item = GetBotItem(SLOT_SECONDARY); + BotTradeSwapItem(client, SLOT_SECONDARY, 0, remove_item, remove_item->GetItem()->Slots, &TempErrorMessage, false); + } + } + } + if(!failedLoreCheck) { + BotTradeSwapItem(client, SLOT_PRIMARY, inst, swap_item, mWeaponItem->Slots, &TempErrorMessage); + success = true; + } + break; + } + else if(j == SLOT_SECONDARY) { + if(inst->IsWeapon()) { + if(CanThisClassDualWield()) { + BotTradeSwapItem(client, SLOT_SECONDARY, inst, swap_item, mWeaponItem->Slots, &TempErrorMessage); + success = true; + } + else { + botCanWear[i] = false; + Say("I can't Dual Wield yet."); + } + } + else { + BotTradeSwapItem(client, SLOT_SECONDARY, inst, swap_item, mWeaponItem->Slots, &TempErrorMessage); + success = true; + } + if(success && GetBotItem(SLOT_PRIMARY)) { + ItemInst* remove_item = GetBotItem(SLOT_PRIMARY); + if((remove_item->GetItem()->ItemType == ItemType2HS) || (remove_item->GetItem()->ItemType == ItemType2HB) || (remove_item->GetItem()->ItemType == ItemType2HPierce)) { + BotTradeSwapItem(client, SLOT_PRIMARY, 0, remove_item, remove_item->GetItem()->Slots, &TempErrorMessage, false); + } + } + break; + } + else { + BotTradeSwapItem(client, j, inst, swap_item, mWeaponItem->Slots, &TempErrorMessage); + success = true; + break; + } + } + else { + botCanWear[i] = false; + Message_StringID(0, PICK_LORE); + break; + } + } + } + } + if(success) { + if(how_many_slots > 1) { + Message(300, "If you want this item in a different slot, use #bot inventory remove to clear the spot."); + } + CalcBotStats(); + } + } + } + if(inst) { + if(!botCanWear[i]) { + client->PushItemOnCursor(*inst, true); + } + client->DeleteItemInInventory(i, 0, UpdateClient); + } + } + + const Item_Struct* item2 = 0; + for(int y=beginSlotID; y<=endSlotID; ++y) { + item2 = database.GetItem(items[y]); + if(item2) { + if(botCanWear[y]) { + Say("Thank you for the %s, %s.", item2->Name, client->GetName()); + } + else { + Say("I can't use this %s!", item2->Name); + } + } + } + } +} + +void Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, SkillType attack_skill) { + NPC::Death(killerMob, damage, spell_id, attack_skill); + + Save(); + + Mob *give_exp = hate_list.GetDamageTop(this); + Client *give_exp_client = NULL; + + if(give_exp && give_exp->IsClient()) + give_exp_client = give_exp->CastToClient(); + + bool IsLdonTreasure = (this->GetClass() == LDON_TREASURE); + + //if (give_exp_client && !IsCorpse() && MerchantType == 0) + //{ + // Group *kg = entity_list.GetGroupByClient(give_exp_client); + // Raid *kr = entity_list.GetRaidByClient(give_exp_client); + + // if(!kr && give_exp_client->IsClient() && give_exp_client->GetBotRaidID() > 0) { + // BotRaids *br = entity_list.GetBotRaidByMob(give_exp_client->CastToMob()); + // if(br) { + // if(!IsLdonTreasure) + // br->SplitExp((EXP_FORMULA), this); + + // if(br->GetBotMainTarget() == this) + // br->SetBotMainTarget(NULL); + + // /* Send the EVENT_KILLED_MERIT event for all raid members */ + // if(br->BotRaidGroups[0]) { + // for(int j=0; jBotRaidGroups[0]->members[j] && br->BotRaidGroups[0]->members[j]->IsClient()) { + // parse->Event(EVENT_KILLED_MERIT, GetNPCTypeID(), "killed", this, br->BotRaidGroups[0]->members[j]); + // if(RuleB(TaskSystem, EnableTaskSystem)) { + // br->BotRaidGroups[0]->members[j]->CastToClient()->UpdateTasksOnKill(GetNPCTypeID()); + // } + // } + // } + // } + // } + // } + //} + + //corpse->Depop(); + if(entity_list.GetCorpseByID(GetID())) + entity_list.GetCorpseByID(GetID())->Depop(); + + Group *g = GetGroup(); + if(g) { + for(int i=0; imembers[i]) { + if(g->members[i] == this) { + // If the leader dies, make the next bot the leader + // and reset all bots followid + if(g->IsLeader(g->members[i])) { + if(g->members[i+1]) { + g->SetLeader(g->members[i+1]); + g->members[i+1]->SetFollowID(g->members[i]->GetFollowID()); + for(int j=0; jmembers[j] && (g->members[j] != g->members[i+1])) { + g->members[j]->SetFollowID(g->members[i+1]->GetID()); + } + } + } + } + + // delete from group data + RemoveBotFromGroup(this, g); + + // if group members exist below this one, move + // them all up one slot in the group list + int j = i+1; + for(; jmembers[j]) { + g->members[j-1] = g->members[j]; + strcpy(g->membername[j-1], g->members[j]->GetCleanName()); + g->membername[j][0] = '\0'; + memset(g->membername[j], 0, 64); + g->members[j] = NULL; + } + } + + // update the client group + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); + GroupJoin_Struct* gu = (GroupJoin_Struct*)outapp->pBuffer; + gu->action = groupActLeave; + strcpy(gu->membername, GetCleanName()); + if(g) { + for(int k=0; kmembers[k] && g->members[k]->IsClient()) + g->members[k]->CastToClient()->QueuePacket(outapp); + } + } + safe_delete(outapp); + + // now that's done, lets see if all we have left is the client + // and we can clean up the clients raid group and group + /*if(GetBotRaidID()) { + BotRaids* br = entity_list.GetBotRaidByMob(this); + if(br) { + if(this == br->botmaintank) { + br->botmaintank = NULL; + } + if(this == br->botsecondtank) { + br->botsecondtank = NULL; + } + } + if(g->GroupCount() == 0) { + uint32 gid = g->GetID(); + if(br) { + br->RemoveEmptyBotGroup(); + } + entity_list.RemoveGroup(gid); + } + if(br && (br->RaidBotGroupsCount() == 1)) { + br->RemoveClientGroup(br->GetRaidBotLeader()); + } + if(br && (br->RaidBotGroupsCount() == 0)) { + br->DisbandBotRaid(); + } + }*/ + } + } + } + } + + if(GetInHealRotation()) { + GetHealRotationLeader()->RemoveHealRotationMember(this); + } + + entity_list.RemoveBot(this->GetID()); +} + +void Bot::Damage(Mob *from, int32 damage, uint16 spell_id, SkillType attack_skill, bool avoidable, int8 buffslot, bool iBuffTic) { + if(spell_id==0) + spell_id = SPELL_UNKNOWN; + + //handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds + if(attacked_timer.Check()) { + mlog(COMBAT__HITS, "Triggering EVENT_ATTACK due to attack by %s", from->GetName()); + parse->EventNPC(EVENT_ATTACK, this, from, "", 0); + } + + attacked_timer.Start(CombatEventTimer_expire); + + // TODO: A bot doesnt call this, right? + /*if (!IsEngaged()) + zone->AddAggroMob();*/ + + // if spell is lifetap add hp to the caster + if (spell_id != SPELL_UNKNOWN && IsLifetapSpell(spell_id)) { + int healed = GetActSpellHealing(spell_id, damage); + mlog(COMBAT__DAMAGE, "Applying lifetap heal of %d to %s", healed, GetCleanName()); + HealDamage(healed); + entity_list.MessageClose(this, true, 300, MT_Spells, "%s beams a smile at %s", GetCleanName(), from->GetCleanName() ); + } + + CommonDamage(from, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic); + + if(GetHP() < 0) { + if(IsCasting()) + InterruptSpell(); + SetAppearance(eaDead); + } + + SendHPUpdate(); + + // Aggro the bot's group members + if(IsGrouped()) + { + Group *g = GetGroup(); + if(g) + { + for(int i=0; imembers[i] && g->members[i]->IsBot() && from && !g->members[i]->CheckAggro(from)) + { + g->members[i]->AddToHateList(from, 1); + } + } + } + } +} + +void Bot::AddToHateList(Mob* other, int32 hate, int32 damage, bool iYellForHelp, bool bFrenzy, bool iBuffTic) +{ + Mob::AddToHateList(other, hate, damage, iYellForHelp, bFrenzy, iBuffTic); +} + +bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, bool IsFromSpell) +{ + _ZP(Bot_Attack); + + if (!other) { + SetTarget(NULL); + LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Bot::Attack for evaluation!"); + return false; + } + + if(!GetTarget() || GetTarget() != other) + SetTarget(other); + + mlog(COMBAT__ATTACKS, "Attacking %s with hand %d %s", other?other->GetCleanName():"(NULL)", Hand, FromRiposte?"(this is a riposte)":""); + + if ((IsCasting() && (GetClass() != BARD) && !IsFromSpell) || + other == NULL || + (GetHP() < 0) || + (GetAppearance() == eaDead) || + (!IsAttackAllowed(other))) + { + if(this->GetOwnerID()) + entity_list.MessageClose(this, 1, 200, 10, "%s says, '%s is not a legal target master.'", this->GetCleanName(), this->GetTarget()->GetCleanName()); + if(other) { + RemoveFromHateList(other); + mlog(COMBAT__ATTACKS, "I am not allowed to attack %s", other->GetCleanName()); + } + return false; + } + + if(DivineAura()) {//cant attack while invulnerable + mlog(COMBAT__ATTACKS, "Attack canceled, Divine Aura is in effect."); + return false; + } + + // TODO: Uncomment this block after solved the bug that is assigning a null value to GetTarget() for bots while in combat. Appears to happen at random, but frequently. + /*if(HasGroup() && _previousTarget != GetTarget()) { + std::ostringstream attackMessage; + attackMessage << "Attacking " << other->GetCleanName() << "."; + + GetGroup()->GroupMessage(this, 0, 100, attackMessage.str().c_str()); + }*/ + + FaceTarget(GetTarget()); + + ItemInst* weapon = NULL; + if(Hand == SLOT_PRIMARY) { + weapon = GetBotItem(SLOT_PRIMARY); + OffHandAtk(false); + } + if(Hand == SLOT_SECONDARY) { + weapon = GetBotItem(SLOT_SECONDARY); + OffHandAtk(true); + } + + if(weapon != NULL) { + if (!weapon->IsWeapon()) { + mlog(COMBAT__ATTACKS, "Attack canceled, Item %s (%d) is not a weapon.", weapon->GetItem()->Name, weapon->GetID()); + return(false); + } + mlog(COMBAT__ATTACKS, "Attacking with weapon: %s (%d)", weapon->GetItem()->Name, weapon->GetID()); + } else { + mlog(COMBAT__ATTACKS, "Attacking without a weapon."); + } + + // calculate attack_skill and skillinuse depending on hand and weapon + // also send Packet to near clients + SkillType skillinuse; + AttackAnimation(skillinuse, Hand, weapon); + mlog(COMBAT__ATTACKS, "Attacking with %s in slot %d using skill %d", weapon?weapon->GetItem()->Name:"Fist", Hand, skillinuse); + + /// Now figure out damage + int damage = 0; + uint8 mylevel = GetLevel() ? GetLevel() : 1; + uint32 hate = 0; + if (weapon) hate = weapon->GetItem()->Damage + weapon->GetItem()->ElemDmgAmt; + int weapon_damage = GetWeaponDamage(other, weapon, &hate); + if (hate == 0 && weapon_damage > 1) hate = weapon_damage; + + //if weapon damage > 0 then we know we can hit the target with this weapon + //otherwise we cannot and we set the damage to -5 later on + if(weapon_damage > 0){ + + //Berserker Berserk damage bonus + if(berserk && (GetClass() == BERSERKER)){ + int bonus = 3 + GetLevel()/10; //unverified + weapon_damage = weapon_damage * (100+bonus) / 100; + mlog(COMBAT__DAMAGE, "Berserker damage bonus increases DMG to %d", weapon_damage); + } + + //try a finishing blow.. if successful end the attack + if(TryFinishingBlow(other, skillinuse)) { + return (true); + } + + //damage formula needs some work + int min_hit = 1; + int max_hit = (2*weapon_damage*GetDamageTable(skillinuse)) / 100; + + if(GetLevel() < 10 && max_hit > 20) + max_hit = (RuleI(Combat, HitCapPre10)); + else if(GetLevel() < 20 && max_hit > 40) + max_hit = (RuleI(Combat, HitCapPre20)); + + // *************************************************************** + // *** Calculate the damage bonus, if applicable, for this hit *** + // *************************************************************** + +#ifndef EQEMU_NO_WEAPON_DAMAGE_BONUS + + // If you include the preprocessor directive "#define EQEMU_NO_WEAPON_DAMAGE_BONUS", that indicates that you do not + // want damage bonuses added to weapon damage at all. This feature was requested by ChaosSlayer on the EQEmu Forums. + // + // This is not recommended for normal usage, as the damage bonus represents a non-trivial component of the DPS output + // of weapons wielded by higher-level melee characters (especially for two-handed weapons). + + int ucDamageBonus = 0; + + if( Hand == SLOT_PRIMARY && GetLevel() >= 28 && IsWarriorClass() ) + { + // Damage bonuses apply only to hits from the main hand (Hand == 13) by characters level 28 and above + // who belong to a melee class. If we're here, then all of these conditions apply. + + ucDamageBonus = GetWeaponDamageBonus( weapon ? weapon->GetItem() : (const Item_Struct*) NULL ); + + min_hit += (int) ucDamageBonus; + max_hit += (int) ucDamageBonus; + hate += ucDamageBonus; + } +#endif + //Live AA - Sinister Strikes *Adds weapon damage bonus to offhand weapon. + if (Hand==SLOT_SECONDARY) { + if (aabonuses.SecondaryDmgInc || itembonuses.SecondaryDmgInc || spellbonuses.SecondaryDmgInc){ + + ucDamageBonus = GetWeaponDamageBonus( weapon ? weapon->GetItem() : (const Item_Struct*) NULL ); + + min_hit += (int) ucDamageBonus; + max_hit += (int) ucDamageBonus; + hate += ucDamageBonus; + } + } + + min_hit = min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100; + + if(max_hit < min_hit) + max_hit = min_hit; + + if(RuleB(Combat, UseIntervalAC)) + damage = max_hit; + else + damage = MakeRandomInt(min_hit, max_hit); + + 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, GetLevel()); + + //check to see if we hit.. + if(!other->CheckHitChance(other, skillinuse, Hand)) { + mlog(COMBAT__ATTACKS, "Attack missed. Damage set to 0."); + damage = 0; + other->AddToHateList(this, 0); + } else { //we hit, try to avoid it + other->AvoidDamage(this, damage); + other->MeleeMitigation(this, damage, min_hit); + if(damage > 0) { + ApplyMeleeDamageBonus(skillinuse, damage); + damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse); + TryCriticalHit(other, skillinuse, damage); + mlog(COMBAT__HITS, "Generating hate %d towards %s", hate, GetCleanName()); + // now add done damage to the hate list + //other->AddToHateList(this, hate); + } + else + other->AddToHateList(this, 0); + mlog(COMBAT__DAMAGE, "Final damage after all reductions: %d", damage); + } + + //riposte + bool slippery_attack = false; // Part of hack to allow riposte to become a miss, but still allow a Strikethrough chance (like on Live) + if (damage == -3) { + if (FromRiposte) return false; + else { + if (Hand == SLOT_SECONDARY) {// 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; + OffhandRiposteFail *= -1; //Live uses a negative value for this. + + if (OffhandRiposteFail && + (OffhandRiposteFail > 99 || (MakeRandomInt(0, 100) < OffhandRiposteFail))) { + damage = 0; // Counts as a miss + slippery_attack = true; + } else + DoRiposte(other); + if (GetHP() < 0) return false; + } + else + DoRiposte(other); + if (GetHP() < 0) return false; + } + } + + 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; + + if(bonusStrikeThrough && (MakeRandomInt(0, 100) < bonusStrikeThrough)) { + Message_StringID(MT_StrikeThrough, STRIKETHROUGH_STRING); // You strike through your opponents defenses! + Attack(other, Hand, false, true); // Strikethrough only gives another attempted hit + return false; + } + } + } + else{ + damage = -5; + } + + // Hate Generation is on a per swing basis, regardless of a hit, miss, or block, its always the same. + // If we are this far, this means we are atleast making a swing. + if (!FromRiposte) {// Ripostes never generate any aggro. + other->AddToHateList(this, hate); + } + + /////////////////////////////////////////////////////////// + ////// Send Attack Damage + /////////////////////////////////////////////////////////// + other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); + + if (GetHP() < 0) return false; + + if(damage > 0 && (spellbonuses.MeleeLifetap || itembonuses.MeleeLifetap)) + { + int lifetap_amt = spellbonuses.MeleeLifetap + itembonuses.MeleeLifetap; + if(lifetap_amt > 100) + lifetap_amt = 100; + + lifetap_amt = damage * lifetap_amt / 100; + + mlog(COMBAT__DAMAGE, "Melee lifetap healing for %d damage.", damage); + //heal self for damage done.. + HealDamage(lifetap_amt); + + if (spellbonuses.MeleeLifetap) + CheckHitsRemaining(0, false,false, SE_MeleeLifetap); + } + + //break invis when you attack + if(invisible) { + mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack."); + BuffFadeByEffect(SE_Invisibility); + BuffFadeByEffect(SE_Invisibility2); + invisible = false; + } + if(invisible_undead) { + mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack."); + BuffFadeByEffect(SE_InvisVsUndead); + BuffFadeByEffect(SE_InvisVsUndead2); + invisible_undead = false; + } + if(invisible_animals){ + mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack."); + 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); + } + + if(GetTarget()) + TriggerDefensiveProcs(weapon, other, Hand, damage); + + if (damage > 0) + return true; + + else + return false; +} + +int16 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id) +{ + const SPDat_Spell_Struct &spell = spells[spell_id]; + + int16 value = 0; + int lvlModifier = 100; + int spell_level = 0; + int lvldiff = 0; + bool LimitSpellSkill = false; + bool SpellSkill_Found = false; + uint32 effect = 0; + int32 base1 = 0; + int32 base2 = 0; + uint32 slot = 0; + + bool LimitFound = false; + int FocusCount = 0; + + std::map >::const_iterator find_iter = aa_effects.find(aa_ID); + if(find_iter == aa_effects.end()) + { + return 0; + } + + for (map::const_iterator iter = aa_effects[aa_ID].begin(); iter != aa_effects[aa_ID].end(); ++iter) + { + effect = iter->second.skill_id; + base1 = iter->second.base1; + base2 = iter->second.base2; + slot = iter->second.slot; + + //AA Foci's can contain multiple focus effects within the same AA. + //To handle this we will not automatically return zero if a limit is found. + //Instead if limit is found and multiple effects, we will reset the limit check + //when the next valid focus effect is found. + if (IsFocusEffect(0, 0, true,effect) || (effect == SE_TriggerOnCast)){ + FocusCount++; + //If limit found on prior check next, else end loop. + if (FocusCount > 1){ + if (LimitFound){ + value = 0; + LimitFound = false; + } + + else{ + break; + } + } + } + + + switch (effect) + { + case SE_Blank: + break; + + //Handle Focus Limits + case SE_LimitResist: + if(base1) + { + if(spell.resisttype != base1) + LimitFound = true; + } + break; + case SE_LimitInstant: + if(spell.buffduration) + LimitFound = true; + break; + case SE_LimitMaxLevel: + spell_level = spell.classes[(GetClass()%16) - 1]; + lvldiff = spell_level - base1; + //every level over cap reduces the effect by base2 percent unless from a clicky when ItemCastsUseFocus is true + if(lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)) + { + if(base2 > 0) + { + lvlModifier -= base2*lvldiff; + if(lvlModifier < 1) + LimitFound = true; + } + else { + LimitFound = true; + } + } + break; + case SE_LimitMinLevel: + if((spell.classes[(GetClass()%16) - 1]) < base1) + LimitFound = true; + break; + case SE_LimitCastTime: + if (spell.cast_time < base1) + LimitFound = true; + break; + case SE_LimitSpell: + // Exclude spell(any but this) + if(base1 < 0) { + if (spell_id == (base1*-1)) + LimitFound = true; + } + else { + // Include Spell(only this) + if (spell_id != base1) + LimitFound = true; + } + break; + case SE_LimitMinDur: + if (base1 > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) + LimitFound = true; + break; + case SE_LimitEffect: + // Exclude effect(any but this) + if(base1 < 0) { + if(IsEffectInSpell(spell_id,(base1*-1))) + LimitFound = true; + } + else { + // Include effect(only this) + if(!IsEffectInSpell(spell_id,base1)) + LimitFound = true; + } + break; + case SE_LimitSpellType: + switch(base1) + { + case 0: + if (!IsDetrimentalSpell(spell_id)) + LimitFound = true; + break; + case 1: + if (!IsBeneficialSpell(spell_id)) + LimitFound = true; + break; + } + break; + + case SE_LimitManaCost: + if(spell.mana < base1) + LimitFound = true; + break; + + case SE_LimitTarget: + // Exclude + if(base1 < 0){ + if(-base1 == spell.targettype) + LimitFound = true; + } + // Include + else { + if(base1 != spell.targettype) + LimitFound = true; + } + break; + + case SE_CombatSkills: + // 1 is for disciplines only + if(base1 == 1 && !IsDiscipline(spell_id)) + LimitFound = true; + // 0 is spells only + else if(base1 == 0 && IsDiscipline(spell_id)) + LimitFound = true; + break; + + case SE_LimitSpellGroup: + if(base1 > 0 && base1 != spell.spellgroup) + LimitFound = true; + else if(base1 < 0 && base1 == spell.spellgroup) + LimitFound = true; + break; + + + case SE_LimitSpellSkill: + LimitSpellSkill = true; + if(base1 == spell.skill) + SpellSkill_Found = true; + break; + + case SE_LimitExcludeSkill:{ + int16 spell_skill = spell.skill * -1; + if(base1 == spell_skill) + LimitFound = true; + break; + } + + case SE_LimitClass: + //Do not use this limit more then once per spell. If multiple class, treat value like items would. + if (!PassLimitClass(base1, GetClass())) + LimitFound = true; + break; + + + //Handle Focus Effects + case SE_ImprovedDamage: + if (type == focusImprovedDamage && base1 > value) + value = base1; + break; + + case SE_ImprovedHeal: + if (type == focusImprovedHeal && base1 > value) + value = base1; + break; + + case SE_ReduceManaCost: + if (type == focusManaCost ) + value = base1; + break; + + case SE_IncreaseSpellHaste: + if (type == focusSpellHaste && base1 > value) + value = base1; + break; + + case SE_IncreaseSpellDuration: + if (type == focusSpellDuration && base1 > value) + value = base1; + break; + + case SE_SpellDurationIncByTic: + if (type == focusSpellDurByTic && base1 > value) + value = base1; + break; + + case SE_SwarmPetDuration: + if (type == focusSwarmPetDuration && base1 > value) + value = base1; + break; + + case SE_IncreaseRange: + if (type == focusRange && base1 > value) + value = base1; + break; + + case SE_ReduceReagentCost: + if (type == focusReagentCost && base1 > value) + value = base1; + break; + + case SE_PetPowerIncrease: + if (type == focusPetPower && base1 > value) + value = base1; + break; + + case SE_SpellResistReduction: + if (type == focusResistRate && base1 > value) + value = base1; + break; + + case SE_SpellHateMod: + if (type == focusSpellHateMod) + { + if(value != 0) + { + if(value > 0) + { + if(base1 > value) + { + value = base1; + } + } + else + { + if(base1 < value) + { + value = base1; + } + } + } + else + value = base1; + } + break; + + case SE_ReduceReuseTimer: + { + if(type == focusReduceRecastTime) + value = base1 / 1000; + + break; + } + + case SE_TriggerOnCast: + { + if(type == focusTriggerOnCast) + { + if(MakeRandomInt(0, 100) <= base1){ + value = base2; + } + + else{ + value = 0; + LimitFound = true; + } + } + break; + } + case SE_SpellVulnerability: + { + if(type == focusSpellVulnerability) + { + value = base1; + } + break; + } + case SE_BlockNextSpellFocus: + { + if(type == focusBlockNextSpell) + { + if(MakeRandomInt(1, 100) <= base1) + value = 1; + } + break; + } + case SE_Twincast: + { + if(type == focusTwincast) + { + value = base1; + } + break; + } + + /* + case SE_SympatheticProc: + { + if(type == focusSympatheticProc) + { + float ProcChance, ProcBonus; + int16 ProcRateMod = base1; //Baseline is 100 for most Sympathetic foci + int32 cast_time = GetActSpellCasttime(spell_id, spells[spell_id].cast_time); + GetSympatheticProcChances(ProcBonus, ProcChance, cast_time, ProcRateMod); + + if(MakeRandomFloat(0, 1) <= ProcChance) + value = focus_id; + + else + value = 0; + } + break; + } + */ + case SE_SpellDamage: + { + if(type == focusSpellDamage) + value = base1; + + break; + } + + case SE_FF_Damage_Amount: + { + if(type == focusFF_Damage_Amount) + value = base1; + + break; + } + + case SE_Empathy: + { + if(type == focusAdditionalDamage) + value = base1; + + break; + } + + case SE_CriticalHealRate: + { + if (type == focusCriticalHealRate) + value = base1; + + break; + } + + case SE_AdditionalHeal: + { + if(type == focusAdditionalHeal) + value = base1; + + break; + } + + case SE_AdditionalHeal2: + { + if(type == focusAdditionalHeal2) + value = base1; + + break; + } + + case SE_HealRate2: + { + if(type == focusHealRate) + value = base1; + + break; + } + + case SE_IncreaseSpellPower: + { + if (type == focusSpellEffectiveness) + value = base1; + + break; + } + case SE_ImprovedDamage2: + { + if(type == focusImprovedDamage2) + value = base1; + + break; + } + + case SE_IncreaseNumHits: + { + if(type == focusIncreaseNumHits) + value = base1; + + break; + } + + //Check for spell skill limits. + if ((LimitSpellSkill) && (!SpellSkill_Found)) + return 0; + + } + } + + if (LimitFound){ + return 0; + } + + return(value*lvlModifier/100); +} + +int16 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { + if (IsBardSong(spell_id) && bottype != BotfocusSpellEffectiveness) + return 0; + + int16 realTotal = 0; + int16 realTotal2 = 0; + int16 realTotal3 = 0; + bool rand_effectiveness = false; + + //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages + //In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance + if((bottype == BotfocusManaCost || bottype == BotfocusImprovedHeal || bottype == BotfocusImprovedDamage) + && RuleB(Spells, LiveLikeFocusEffects)) + { + rand_effectiveness = true; + } + + //Check if item focus effect exists for the client. + if (itembonuses.FocusEffects[bottype]){ + + const Item_Struct* TempItem = 0; + const Item_Struct* UsedItem = 0; + const ItemInst* TempInst = 0; + uint16 UsedFocusID = 0; + int16 Total = 0; + int16 focus_max = 0; + int16 focus_max_real = 0; + + //item focus + for(int x=0; x<=21; x++) + { + TempItem = NULL; + ItemInst* ins = GetBotItem(x); + if (!ins) + continue; + TempItem = ins->GetItem(); + if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) { + if(rand_effectiveness) { + focus_max = CalcBotFocusEffect(bottype, TempItem->Focus.Effect, spell_id, true); + if (focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) { + focus_max_real = focus_max; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } else if (focus_max < 0 && focus_max < focus_max_real) { + focus_max_real = focus_max; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } + } + else { + Total = CalcBotFocusEffect(bottype, TempItem->Focus.Effect, spell_id); + if (Total > 0 && realTotal >= 0 && Total > realTotal) { + realTotal = Total; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } else if (Total < 0 && Total < realTotal) { + realTotal = Total; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } + } + } + + for(int y = 0; y < MAX_AUGMENT_SLOTS; ++y) + { + ItemInst *aug = NULL; + aug = ins->GetAugment(y); + if(aug) + { + const Item_Struct* TempItemAug = aug->GetItem(); + if (TempItemAug && TempItemAug->Focus.Effect > 0 && TempItemAug->Focus.Effect != SPELL_UNKNOWN) { + if(rand_effectiveness) { + focus_max = CalcBotFocusEffect(bottype, TempItemAug->Focus.Effect, spell_id, true); + if (focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) { + focus_max_real = focus_max; + UsedItem = TempItem; + UsedFocusID = TempItemAug->Focus.Effect; + } else if (focus_max < 0 && focus_max < focus_max_real) { + focus_max_real = focus_max; + UsedItem = TempItem; + UsedFocusID = TempItemAug->Focus.Effect; + } + } + else { + Total = CalcBotFocusEffect(bottype, TempItemAug->Focus.Effect, spell_id); + if (Total > 0 && realTotal >= 0 && Total > realTotal) { + realTotal = Total; + UsedItem = TempItem; + UsedFocusID = TempItemAug->Focus.Effect; + } else if (Total < 0 && Total < realTotal) { + realTotal = Total; + UsedItem = TempItem; + UsedFocusID = TempItemAug->Focus.Effect; + } + } + } + } + } + } + + if(UsedItem && rand_effectiveness && focus_max_real != 0) + realTotal = CalcBotFocusEffect(bottype, UsedFocusID, spell_id); + } + + //Check if spell focus effect exists for the client. + if (spellbonuses.FocusEffects[bottype]){ + + //Spell Focus + int16 Total2 = 0; + int16 focus_max2 = 0; + int16 focus_max_real2 = 0; + + int buff_tracker = -1; + int buff_slot = 0; + uint16 focusspellid = 0; + uint16 focusspell_tracker = 0; + uint32 buff_max = GetMaxTotalSlots(); + for (buff_slot = 0; buff_slot < buff_max; buff_slot++) { + focusspellid = buffs[buff_slot].spellid; + if (focusspellid == 0 || focusspellid >= SPDAT_RECORDS) + continue; + + if(rand_effectiveness) { + focus_max2 = CalcBotFocusEffect(bottype, focusspellid, spell_id, true); + if (focus_max2 > 0 && focus_max_real2 >= 0 && focus_max2 > focus_max_real2) { + focus_max_real2 = focus_max2; + buff_tracker = buff_slot; + focusspell_tracker = focusspellid; + } else if (focus_max2 < 0 && focus_max2 < focus_max_real2) { + focus_max_real2 = focus_max2; + buff_tracker = buff_slot; + focusspell_tracker = focusspellid; + } + } + else { + Total2 = CalcBotFocusEffect(bottype, focusspellid, spell_id); + if (Total2 > 0 && realTotal2 >= 0 && Total2 > realTotal2) { + realTotal2 = Total2; + buff_tracker = buff_slot; + focusspell_tracker = focusspellid; + } else if (Total2 < 0 && Total2 < realTotal2) { + realTotal2 = Total2; + buff_tracker = buff_slot; + focusspell_tracker = focusspellid; + } + } + } + + if(focusspell_tracker && rand_effectiveness && focus_max_real2 != 0) + realTotal2 = CalcBotFocusEffect(bottype, focusspell_tracker, spell_id); + + // For effects like gift of mana that only fire once, save the spellid into an array that consists of all available buff slots. + if(buff_tracker >= 0 && buffs[buff_tracker].numhits > 0) { + m_spellHitsLeft[buff_tracker] = focusspell_tracker; + } + } + + // AA Focus + if (aabonuses.FocusEffects[bottype]){ + + int totalAAs = database.CountAAs(); + int16 Total3 = 0; + uint32 slots = 0; + uint32 aa_AA = 0; + uint32 aa_value = 0; + + for (int i = 0; i < totalAAs; i++) { //iterate through all of the client's AAs + std::map::iterator aa = botAAs.find(i); + if(aa != botAAs.end()) { // make sure aa exists or we'll crash zone + aa_AA = aa->second.aa_id; //same as aaid from the aa_effects table + aa_value = aa->second.total_levels; //how many points in it + if (aa_AA < 1 || aa_value < 1) + continue; + + Total3 = CalcBotAAFocus(bottype, aa_AA, spell_id); + if (Total3 > 0 && realTotal3 >= 0 && Total3 > realTotal3) { + realTotal3 = Total3; + } + else if (Total3 < 0 && Total3 < realTotal3) { + realTotal3 = Total3; + } + } + } + } + + if(bottype == BotfocusReagentCost && IsSummonPetSpell(spell_id) && GetAA(aaElementalPact)) + return 100; + + if(bottype == BotfocusReagentCost && (IsEffectInSpell(spell_id, SE_SummonItem) || IsSacrificeSpell(spell_id))){ + return 0; + //Summon Spells that require reagents are typically imbue type spells, enchant metal, sacrifice and shouldn't be affected + //by reagent conservation for obvious reasons. + } + + return realTotal + realTotal2; +} + +int16 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; + int lvlModifier = 100; + int spell_level = 0; + int lvldiff = 0; + bool LimitSpellSkill = false; + bool SpellSkill_Found = false; + + for (int i = 0; i < EFFECT_COUNT; i++) { + switch (focus_spell.effectid[i]) { + case SE_Blank: + break; + //check limits + + case SE_LimitResist:{ + if(focus_spell.base[i]){ + if(spell.resisttype != focus_spell.base[i]) + return(0); + } + break; + } + case SE_LimitInstant:{ + if(spell.buffduration) + return(0); + break; + } + + case SE_LimitMaxLevel:{ + if (IsNPC()) + break; + spell_level = spell.classes[(GetClass()%16) - 1]; + lvldiff = spell_level - focus_spell.base[i]; + //every level over cap reduces the effect by focus_spell.base2[i] percent unless from a clicky when ItemCastsUseFocus is true + if(lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)) + { + if(focus_spell.base2[i] > 0) + { + lvlModifier -= focus_spell.base2[i]*lvldiff; + if(lvlModifier < 1) + return 0; + } + else + { + return 0; + } + } + break; + } + + case SE_LimitMinLevel: + if (IsNPC()) + break; + if (spell.classes[(GetClass()%16) - 1] < focus_spell.base[i]) + return(0); + break; + + case SE_LimitCastTime: + if (spells[spell_id].cast_time < (uint16)focus_spell.base[i]) + return(0); + break; + + case SE_LimitSpell: + if(focus_spell.base[i] < 0) { //exclude spell + if (spell_id == (focus_spell.base[i]*-1)) + return(0); + } else { + //this makes the assumption that only one spell can be explicitly included... + if (spell_id != focus_spell.base[i]) + return(0); + } + break; + + case SE_LimitMinDur: + if (focus_spell.base[i] > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) + return(0); + break; + + case SE_LimitEffect: + if(focus_spell.base[i] < 0){ + if(IsEffectInSpell(spell_id,focus_spell.base[i])){ //we limit this effect, can't have + return 0; + } + } + else{ + if(focus_spell.base[i] == SE_SummonPet) //summoning haste special case + { //must have one of the three pet effects to qualify + if(!IsEffectInSpell(spell_id, SE_SummonPet) && + !IsEffectInSpell(spell_id, SE_NecPet) && + !IsEffectInSpell(spell_id, SE_SummonBSTPet)) + { + return 0; + } + } + else if(!IsEffectInSpell(spell_id,focus_spell.base[i])){ //we limit this effect, must have + return 0; + } + } + break; + + + case SE_LimitSpellType: + switch( focus_spell.base[i] ) + { + case 0: + if (!IsDetrimentalSpell(spell_id)) + return 0; + break; + case 1: + if (!IsBeneficialSpell(spell_id)) + return 0; + break; + default: + LogFile->write(EQEMuLog::Normal, "CalcFocusEffect: unknown limit spelltype %d", focus_spell.base[i]); + } + break; + + case SE_LimitManaCost: + if(spell.mana < focus_spell.base[i]) + return 0; + break; + + case SE_LimitTarget: + // Exclude + if((focus_spell.base[i] < 0) && -focus_spell.base[i] == spell.targettype) + return 0; + // Include + else if (focus_spell.base[i] > 0 && focus_spell.base[i] != spell.targettype) + return 0; + + break; + + case SE_CombatSkills: + // 1 is for disciplines only + if(focus_spell.base[i] == 1 && !IsDiscipline(spell_id)) + return 0; + // 0 is for spells only + else if(focus_spell.base[i] == 0 && IsDiscipline(spell_id)) + return 0; + break; + + case SE_LimitSpellGroup: + if(focus_spell.base[i] > 0 && focus_spell.base[i] != spell.spellgroup) + return 0; + else if(focus_spell.base[i] < 0 && focus_spell.base[i] == spell.spellgroup) + return 0; + break; + + case SE_LimitSpellSkill: + LimitSpellSkill = true; + if(focus_spell.base[i] == spell.skill) + SpellSkill_Found = true; + break; + + case SE_LimitExcludeSkill:{ + int16 spell_skill = spell.skill * -1; + if(focus_spell.base[i] == spell_skill) + return 0; + break; + } + + case SE_LimitClass: + //Do not use this limit more then once per spell. If multiple class, treat value like items would. + if (!PassLimitClass(focus_spell.base[i], GetClass())) + return 0; + break; + + //handle effects + case SE_ImprovedDamage: + // No Spell used this, its handled by different spell effect IDs. + if (bottype == BotfocusImprovedDamage) { + // This is used to determine which focus should be used for the random calculation + if(best_focus) { + // If the spell contains a value in the base2 field then that is the max value + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + // If the spell does not contain a base2 value, then its a straight non random value + else { + value = focus_spell.base[i]; + } + } + // Actual focus calculation starts here + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = MakeRandomInt(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + case SE_ImprovedHeal: + if (bottype == BotfocusImprovedHeal) { + if(best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + else { + value = focus_spell.base[i]; + } + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = MakeRandomInt(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + case SE_ReduceManaCost: + if (bottype == BotfocusManaCost) { + if(best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + else { + value = focus_spell.base[i]; + } + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = MakeRandomInt(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + + case SE_IncreaseSpellHaste: + if (bottype == BotfocusSpellHaste && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_IncreaseSpellDuration: + if (bottype == BotfocusSpellDuration && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_SpellDurationIncByTic: + if (bottype == BotfocusSpellDurByTic && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_SwarmPetDuration: + if (bottype == BotfocusSwarmPetDuration && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_IncreaseRange: + if (bottype == BotfocusRange && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_ReduceReagentCost: + if (bottype == BotfocusReagentCost && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_PetPowerIncrease: + if (bottype == BotfocusPetPower && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_SpellResistReduction: + if (bottype == BotfocusResistRate && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_SpellHateMod: + if (bottype == BotfocusSpellHateMod) + { + if(value != 0) + { + if(value > 0) + { + if(focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + } + else + { + if(focus_spell.base[i] < value) + { + value = focus_spell.base[i]; + } + } + } + else + value = focus_spell.base[i]; + } + break; + + case SE_ReduceReuseTimer: + { + if(bottype == BotfocusReduceRecastTime) + value = focus_spell.base[i] / 1000; + + break; + } + + case SE_TriggerOnCast: + { + if(bottype == BotfocusTriggerOnCast) + + if(MakeRandomInt(0, 100) <= focus_spell.base[i]) + value = focus_spell.base2[i]; + + else + value = 0; + + break; + } + case SE_SpellVulnerability: + { + if(bottype == BotfocusSpellVulnerability) + { + value = focus_spell.base[i]; + } + break; + } + case SE_BlockNextSpellFocus: + { + if(bottype == BotfocusBlockNextSpell) + { + if(MakeRandomInt(1, 100) <= focus_spell.base[i]) + value = 1; + } + break; + } + case SE_Twincast: + { + if(bottype == BotfocusTwincast) + { + value = focus_spell.base[i]; + } + break; + } + case SE_SympatheticProc: + { + if(bottype == BotfocusSympatheticProc) + { + float ProcChance, ProcBonus; + int16 ProcRateMod = focus_spell.base[i]; //Baseline is 100 for most Sympathetic foci + int32 cast_time = GetActSpellCasttime(spell_id, spells[spell_id].cast_time); + GetSympatheticProcChances(ProcBonus, ProcChance, cast_time, ProcRateMod); + + if(MakeRandomFloat(0, 1) <= ProcChance) + value = focus_id; + + else + value = 0; + } + break; + } + case SE_SpellDamage: + { + if(bottype == BotfocusSpellDamage) + value = focus_spell.base[i]; + + break; + } + + case SE_FF_Damage_Amount: + { + if(bottype == BotfocusFF_Damage_Amount) + value = focus_spell.base[i]; + + break; + } + + case SE_Empathy: + { + if(bottype == BotfocusAdditionalDamage) + value = focus_spell.base[i]; + + break; + } + + case SE_CriticalHealRate: + { + if (bottype == BotfocusCriticalHealRate) + value = focus_spell.base[i]; + + break; + } + + case SE_AdditionalHeal: + { + if(bottype == BotfocusAdditionalHeal) + value = focus_spell.base[i]; + + break; + } + + case SE_AdditionalHeal2: + { + if(bottype == BotfocusAdditionalHeal2) + value = focus_spell.base[i]; + + break; + } + + case SE_HealRate2: + { + if(bottype == BotfocusHealRate) + value = focus_spell.base[i]; + + break; + } + + case SE_IncreaseSpellPower: + { + if (bottype == BotfocusSpellEffectiveness) + value = focus_spell.base[i]; + + break; + } + case SE_ImprovedDamage2: + { + if(bottype == BotfocusImprovedDamage2) + value = focus_spell.base[i]; + + break; + } + + case SE_IncreaseNumHits: + { + if(bottype == BotfocusIncreaseNumHits) + value = focus_spell.base[i]; + + break; + } +#if EQDEBUG >= 6 + //this spits up a lot of garbage when calculating spell focuses + //since they have all kinds of extra effects on them. + default: + LogFile->write(EQEMuLog::Normal, "CalcFocusEffect: unknown effectid %d", focus_spell.effectid[i]); +#endif + } + } + //Check for spell skill limits. + if ((LimitSpellSkill) && (!SpellSkill_Found)) + return 0; + + return(value*lvlModifier/100); +} + +//proc chance includes proc bonus +float Bot::GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) { + int mydex = GetDEX(); + float AABonus = 0; + ProcBonus = 0; + ProcChance = 0; + + if (aabonuses.ProcChance) + AABonus = float(aabonuses.ProcChance) / 100.0f; + + switch(hand){ + case SLOT_PRIMARY: + weapon_speed = attack_timer.GetDuration(); + break; + case SLOT_SECONDARY: + weapon_speed = attack_dw_timer.GetDuration(); + break; + case SLOT_RANGE: + weapon_speed = ranged_timer.GetDuration(); + break; + } + + + //calculate the weapon speed in ms, so we can use the rule to compare against. + + if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance + weapon_speed = RuleI(Combat, MinHastedDelay); + + ProcBonus += (float(itembonuses.ProcChance + spellbonuses.ProcChance) / 1000.0f + AABonus); + + if(RuleB(Combat, AdjustProcPerMinute) == true) + { + ProcChance = ((float)weapon_speed * RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms + ProcBonus += float(mydex) * RuleR(Combat, ProcPerMinDexContrib) / 100.0f; + ProcChance = ProcChance + (ProcChance * ProcBonus); + } + else + { + ProcChance = RuleR(Combat, BaseProcChance) + float(mydex) / RuleR(Combat, ProcDexDivideBy); + ProcChance = ProcChance + (ProcChance * ProcBonus); + } + + mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); + return ProcChance; +} + +bool Bot::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) +{ + /* solar: called when a mob is attacked, does the checks to see if it's a hit + * and does other mitigation checks. 'this' is the mob being attacked. + * + * special return values: + * -1 - block + * -2 - parry + * -3 - riposte + * -4 - dodge + * + */ + if(GetAppearance() == eaDead) + return false; + + float skill = 0; + float bonus = 0; + float RollTable[4] = {0,0,0,0}; + float roll = 0; + Mob *attacker=other; + Mob *defender=this; + + //garunteed hit + bool ghit = false; + if((attacker->GetSpellBonuses().MeleeSkillCheck + attacker->GetItemBonuses().MeleeSkillCheck) > 500) + ghit = true; + + ////////////////////////////////////////////////////////// + // make enrage same as riposte + ///////////////////////////////////////////////////////// + if (IsEnraged() && !other->BehindMob(this, other->GetX(), other->GetY())) { + damage = -3; + mlog(COMBAT__DAMAGE, "I am enraged, riposting frontal attack."); + } + + ///////////////////////////////////////////////////////// + // riposte + ///////////////////////////////////////////////////////// + float riposte_chance = 0.0f; + if (CanRiposte && damage > 0 && CanThisClassRiposte() && !other->BehindMob(this, other->GetX(), other->GetY())) + { + riposte_chance = (100.0f + (float)defender->GetAABonuses().RiposteChance + (float)defender->GetSpellBonuses().RiposteChance + (float)defender->GetItemBonuses().RiposteChance) / 100.0f; + skill = GetSkill(RIPOSTE); + + if (!ghit) { //if they are not using a garunteed hit discipline + bonus = 2.0 + skill/60.0 + (GetDEX()/200); + bonus *= riposte_chance; + RollTable[0] = bonus + (itembonuses.HeroicDEX / 25); // 25 heroic = 1%, applies to ripo, parry, block + } + } + + /////////////////////////////////////////////////////// + // block + /////////////////////////////////////////////////////// + + bool bBlockFromRear = false; + bool bShieldBlockFromRear = false; + + if (this->IsBot()) { + int aaChance = 0; + + // a successful roll on this does not mean a successful block is forthcoming. only that a chance to block + // from a direction other than the rear is granted. + + //Live AA - HightenedAwareness + int BlockBehindChance = aabonuses.BlockBehind + spellbonuses.BlockBehind + itembonuses.BlockBehind; + + if (BlockBehindChance && (BlockBehindChance > MakeRandomInt(1, 100))){ + bBlockFromRear = true; + + if (spellbonuses.BlockBehind || itembonuses.BlockBehind) + bShieldBlockFromRear = true; //This bonus should allow a chance to Shield Block from behind. + } + } + + float block_chance = 0.0f; + if (damage > 0 && CanThisClassBlock() && (!other->BehindMob(this, other->GetX(), other->GetY()) || bBlockFromRear)) { + block_chance = (100.0f + (float)spellbonuses.IncreaseBlockChance + (float)itembonuses.IncreaseBlockChance) / 100.0f; + skill = GetSkill(BLOCKSKILL); + + if (!ghit) { //if they are not using a garunteed hit discipline + bonus = 2.0 + skill/35.0 + (GetDEX()/200); + RollTable[1] = RollTable[0] + (bonus * block_chance) - riposte_chance; + block_chance *= bonus; // set this so we can remove it from the parry calcs + } + } + else{ + RollTable[1] = RollTable[0]; + } + + if(damage > 0 && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock) + && (!other->BehindMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { + bool equiped = GetBotItem(SLOT_SECONDARY); + if(equiped) { + uint8 shield = GetBotItem(SLOT_SECONDARY)->GetItem()->ItemType; + float bonusShieldBlock = 0.0f; + if(shield == ItemTypeShield) { + + //Live AA - Shield Block + bonusShieldBlock = aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock; + RollTable[1] = RollTable[0] + bonusShieldBlock; + } + } + } + + if(damage > 0 && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock) + && (!other->BehindMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { + bool equiped2 = GetBotItem(SLOT_PRIMARY); + if(equiped2) { + uint8 TwoHandBlunt = GetBotItem(SLOT_PRIMARY)->GetItem()->ItemType; + float bonusStaffBlock = 0.0f; + if(TwoHandBlunt == ItemType2HB) { + + bonusStaffBlock = aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock; + RollTable[1] = RollTable[0] + bonusStaffBlock; + } + } + } + + ////////////////////////////////////////////////////// + // parry + ////////////////////////////////////////////////////// + float parry_chance = 0.0f; + if (damage > 0 && CanThisClassParry() && !other->BehindMob(this, other->GetX(), other->GetY())) + { + parry_chance = (100.0f + (float)defender->GetSpellBonuses().ParryChance + (float)defender->GetItemBonuses().ParryChance) / 100.0f; + skill = GetSkill(PARRY); + + if (!ghit) { //if they are not using a garunteed hit discipline + bonus = 2.0 + skill/60.0 + (GetDEX()/200); + bonus *= parry_chance; + RollTable[2] = RollTable[1] + bonus - block_chance; + } + } + else{ + RollTable[2] = RollTable[1] - block_chance; + } + + //////////////////////////////////////////////////////// + // dodge + //////////////////////////////////////////////////////// + float dodge_chance = 0.0f; + if (damage > 0 && CanThisClassDodge() && !other->BehindMob(this, other->GetX(), other->GetY())) + { + dodge_chance = (100.0f + (float)defender->GetSpellBonuses().DodgeChance + (float)defender->GetItemBonuses().DodgeChance) / 100.0f; + skill = GetSkill(DODGE); + + if (!ghit) { //if they are not using a garunteed hit discipline + bonus = 2.0 + skill/60.0 + (GetAGI()/200); + bonus *= dodge_chance; + RollTable[3] = RollTable[2] + bonus - (itembonuses.HeroicDEX / 25) + (itembonuses.HeroicAGI / 25) - parry_chance; // Remove the dex as it doesnt count for dodge + } + } + else{ + RollTable[3] = RollTable[2] - (itembonuses.HeroicDEX / 25) + (itembonuses.HeroicAGI / 25) - parry_chance; + } + + if(damage > 0) + { + roll = MakeRandomFloat(0,100); + if(roll <= RollTable[0]){ + damage = -3; + } + else if(roll <= RollTable[1]){ + damage = -1; + } + else if(roll <= RollTable[2]){ + damage = -2; + } + else if(roll <= RollTable[3]){ + damage = -4; + } + } + + mlog(COMBAT__DAMAGE, "Final damage after all avoidances: %d", damage); + + if (damage < 0) + return true; + return false; +} + +int Bot::GetMonkHandToHandDamage(void) +{ + // Kaiyodo - Determine a monk's fist damage. Table data from www.monkly-business.com + // saved as static array - this should speed this function up considerably + static int damage[66] = { + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 + 99, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 9, 9, 9, 9, 9,10,10,10,10,10,11,11,11,11,11, + 12,12,12,12,12,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14, + 14,14,15,15,15,15 }; + + // Have a look to see if we have epic fists on + + uint32 botWeaponId = INVALID_ID; + botWeaponId = CastToNPC()->GetEquipment(MATERIAL_HANDS); + if(botWeaponId == 10652) { //Monk Epic ID + return 9; + } + else + { + int Level = GetLevel(); + if(Level > 65) + return 19; + else + return damage[Level]; + } + + int Level = GetLevel(); + if (Level > 65) + return(19); + else + return damage[Level]; +} + +void Bot::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage) +{ + bool slayUndeadCrit = false; + + if(damage < 1) //We can't critical hit if we don't hit. + return; + + float critChance = 0.0f; + + //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 (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); + return; + } + } + } + + //2: Try Melee Critical + + //Base critical rate for all classes is dervived from DEX stat, this rate is then augmented + //by item,spell and AA bonuses allowing you a chance to critical hit. If the following rules + //are defined you will have an innate chance to hit at Level 1 regardless of bonuses. + //Warning: Do not define these rules if you want live like critical hits. + critChance += RuleI(Combat, MeleeBaseCritChance); + + critChance += RuleI(Combat, ClientBaseCritChance); + + if(((GetClass() == WARRIOR || GetClass() == BERSERKER) && GetLevel() >= 12)) + { + if((GetHPRatio() < 30) && GetClass() == BERSERKER){ + critChance += RuleI(Combat, BerserkBaseCritChance); + berserk = true; + } + else + critChance += RuleI(Combat, WarBerBaseCritChance); + } + + if(skill == ARCHERY && GetClass() == RANGER && GetSkill(ARCHERY) >= 65) + critChance += 6; + + if(skill == THROWING && GetClass() == ROGUE && GetSkill(THROWING) >= 65) + critChance += 6; + + int CritChanceBonus = GetCriticalChanceBonus(skill); + + if (CritChanceBonus || critChance) { + + //Get Base CritChance from Dex. (200 = ~1.6%, 255 = ~2.0%, 355 = ~2.20%) Fall off rate > 255 + //http://giline.versus.jp/shiden/su.htm , http://giline.versus.jp/shiden/damage_e.htm + if (GetDEX() <= 255) + critChance += (float(GetDEX()) / 125.0f); + else if (GetDEX() > 255) + critChance += (float(GetDEX()-255)/ 500.0f) + 2.0f; + critChance += critChance*(float)CritChanceBonus /100.0f; + } + + if(critChance > 0){ + + critChance /= 100; + + if(MakeRandomFloat(0, 1) < critChance) + { + uint16 critMod = 200; + bool crip_success = false; + int16 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 + //have a 200% Chance to Critical Blow effect, therefore you have a 20% Chance to Critical Blow. + if (CripplingBlowChance){ + critChance *= float(CripplingBlowChance)/100.0f; + + if(MakeRandomFloat(0, 1) < critChance){ + critMod = 400; + crip_success = true; + } + } + + critMod += GetCritDmgMob(skill) * 2; // To account for base crit mod being 200 not 100 + damage = damage * critMod / 100; + + if(berserk || crip_success) + { + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRIPPLING_BLOW, GetCleanName(), itoa(damage)); + // Crippling blows also have a chance to stun + //Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a staggers message. + if (defender->GetLevel() <= 55 && !defender->SpecAttacks[IMMUNE_STUN]){ + defender->Emote("staggers."); + defender->Stun(0); + } + } + + else + { + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, CRITICAL_HIT, GetCleanName(), itoa(damage)); + } + } + } +} + +bool Bot::TryFinishingBlow(Mob *defender, SkillType skillinuse) +{ + if (!defender) + return false; + + if (aabonuses.FinishingBlow[1] && !defender->IsClient() && defender->GetHPRatio() < 10){ + + uint32 chance = aabonuses.FinishingBlow[0]/10; //500 = 5% chance. + uint32 damage = aabonuses.FinishingBlow[1]; + uint16 levelreq = aabonuses.FinishingBlowLvl[0]; + + if(defender->GetLevel() <= levelreq && (chance >= MakeRandomInt(0, 1000))){ + mlog(COMBAT__ATTACKS, "Landed a finishing blow: levelreq at %d, other level %d", levelreq , defender->GetLevel()); + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FINISHING_BLOW, GetName()); + defender->Damage(this, damage, SPELL_UNKNOWN, skillinuse); + return true; + } + else + { + mlog(COMBAT__ATTACKS, "FAILED a finishing blow: levelreq at %d, other level %d", levelreq , defender->GetLevel()); + return false; + } + } + return false; +} + +void Bot::DoRiposte(Mob* defender) { + mlog(COMBAT__ATTACKS, "Preforming a riposte"); + + if (!defender) + return; + + defender->Attack(this, SLOT_PRIMARY, true); + + //double riposte + int16 DoubleRipChance = defender->GetAABonuses().GiveDoubleRiposte[0] + + defender->GetSpellBonuses().GiveDoubleRiposte[0] + + defender->GetItemBonuses().GiveDoubleRiposte[0]; + + if(DoubleRipChance && (DoubleRipChance >= MakeRandomInt(0, 100))) { + mlog(COMBAT__ATTACKS, "Preforming a double riposte (%d percent chance)", DoubleRipChance); + + defender->Attack(this, SLOT_PRIMARY, true); + } + + //Double Riposte effect, allows for a chance to do RIPOSTE with a skill specfic special attack (ie Return Kick). + //Coded narrowly: Limit to one per client. Limit AA only. [1 = Skill Attack Chance, 2 = Skill] + DoubleRipChance = defender->GetAABonuses().GiveDoubleRiposte[1]; + + if(DoubleRipChance && (DoubleRipChance >= MakeRandomInt(0, 100))) { + if (defender->GetClass() == MONK) + defender->MonkSpecialAttack(this, defender->GetAABonuses().GiveDoubleRiposte[2]); + else if (defender->IsBot()) + defender->CastToClient()->DoClassAttacks(this,defender->GetAABonuses().GiveDoubleRiposte[2], true); + } +} + +void Bot::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit) +{ + if(damage <= 0) + return; + + Mob* defender = this; + float aa_mit = 0; + + aa_mit = (aabonuses.CombatStability + itembonuses.CombatStability + spellbonuses.CombatStability)/100.0f; + + if(RuleB(Combat, UseIntervalAC)) + { + float softcap = 0.0; + float mitigation_rating = 0.0; + float attack_rating = 0.0; + int shield_ac = 0; + int armor; + float weight = 0.0; + if(IsBot()) + { + armor = GetRawACNoShield(shield_ac); + weight = (CalcCurrentWeight() / 10.0); + } + + if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER) + { + softcap = RuleI(Combat, ClothACSoftcap); + } + else if(GetClass() == MONK && weight <= 15.0) + { + softcap = RuleI(Combat, MonkACSoftcap); + } + else if(GetClass() == DRUID || GetClass() == BEASTLORD || GetClass() == MONK) + { + softcap = RuleI(Combat, LeatherACSoftcap); + } + else if(GetClass() == SHAMAN || GetClass() == ROGUE || GetClass() == BERSERKER || GetClass() == RANGER) + { + softcap = RuleI(Combat, ChainACSoftcap); + } + else + { + softcap = RuleI(Combat, PlateACSoftcap); + } + + softcap += shield_ac; + armor += shield_ac; + softcap += (softcap * (aa_mit * RuleR(Combat, AAMitigationACFactor))); + if(armor > softcap) + { + int softcap_armor = armor - softcap; + if(GetClass() == WARRIOR) + { + softcap_armor = softcap_armor * RuleR(Combat, WarriorACSoftcapReturn); + } + else if(GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || (GetClass() == MONK && weight <= 15.0)) + { + softcap_armor = softcap_armor * RuleR(Combat, KnightACSoftcapReturn); + } + else if(GetClass() == CLERIC || GetClass() == BARD || GetClass() == BERSERKER || GetClass() == ROGUE || GetClass() == SHAMAN || GetClass() == MONK) + { + softcap_armor = softcap_armor * RuleR(Combat, LowPlateChainACSoftcapReturn); + } + else if(GetClass() == RANGER || GetClass() == BEASTLORD) + { + softcap_armor = softcap_armor * RuleR(Combat, LowChainLeatherACSoftcapReturn); + } + else if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER || GetClass() == DRUID) + { + softcap_armor = softcap_armor * RuleR(Combat, CasterACSoftcapReturn); + } + else + { + softcap_armor = softcap_armor * RuleR(Combat, MiscACSoftcapReturn); + } + armor = softcap + softcap_armor; + } + + mitigation_rating = 0.0; + if(GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == NECROMANCER || GetClass() == ENCHANTER) + { + mitigation_rating = ((GetSkill(DEFENSE) + itembonuses.HeroicAGI/10) / 4.0) + armor + 1; + } + else + { + mitigation_rating = ((GetSkill(DEFENSE) + itembonuses.HeroicAGI/10) / 3.0) + (armor * 1.333333) + 1; + } + mitigation_rating *= 0.847; + + if(attacker->IsClient()) + { + attack_rating = (attacker->CastToClient()->GetATK() + ((attacker->GetSTR()-66) * 0.9) + (attacker->GetSkill(OFFENSE)*1.345)); + } + else if(attacker->IsBot()) + { + attack_rating = (attacker->CastToBot()->CalcATK() + ((attacker->GetSTR()-66) * 0.9) + (attacker->GetSkill(OFFENSE)*1.345)); + } + else + { + attack_rating = (attacker->GetATK() + (attacker->GetSkill(OFFENSE)*1.345) + ((attacker->GetSTR()-66) * 0.9)); + } + + float d = 10.0; + float mit_roll = MakeRandomFloat(0, mitigation_rating); + float atk_roll = MakeRandomFloat(0, attack_rating); + + if(atk_roll > mit_roll) + { + float a_diff = (atk_roll - mit_roll); + float thac0 = attack_rating * RuleR(Combat, ACthac0Factor); + float thac0cap = ((attacker->GetLevel() * 9) + 20); + if(thac0 > thac0cap) + { + thac0 = thac0cap; + } + d -= 10.0 * (a_diff / thac0); + } + else if(mit_roll > atk_roll) + { + float m_diff = (mit_roll - atk_roll); + float thac20 = mitigation_rating * RuleR(Combat, ACthac20Factor); + float thac20cap = ((defender->GetLevel() * 9) + 20); + if(thac20 > thac20cap) + { + thac20 = thac20cap; + } + d += 10 * (m_diff / thac20); + } + + if(d < 0.0) + { + d = 0.0; + } + + if(d > 20) + { + d = 20.0; + } + + float interval = (damage - minhit) / 20.0; + damage = damage - ((int)d * interval); + } + else{ + //////////////////////////////////////////////////////// + // Scorpious2k: Include AC in the calculation + // use serverop variables to set values + int myac = GetAC(); + if (damage > 0 && myac > 0) { + int acfail=1000; + char tmp[10]; + + if (database.GetVariable("ACfail", tmp, 9)) { + acfail = (int) (atof(tmp) * 100); + if (acfail>100) acfail=100; + } + + if (acfail<=0 || MakeRandomInt(0, 100)>acfail) { + float acreduction=1; + int acrandom=300; + if (database.GetVariable("ACreduction", tmp, 9)) + { + acreduction=atof(tmp); + if (acreduction>100) acreduction=100; + } + + if (database.GetVariable("ACrandom", tmp, 9)) + { + acrandom = (int) ((atof(tmp)+1) * 100); + if (acrandom>10100) acrandom=10100; + } + + if (acreduction>0) { + damage -= (int) (GetAC() * acreduction/100.0f); + } + if (acrandom>0) { + damage -= (myac * MakeRandomInt(0, acrandom) / 10000); + } + if (damage<1) damage=1; + mlog(COMBAT__DAMAGE, "AC Damage Reduction: fail chance %d%%. Failed. Reduction %.3f%%, random %d. Resulting damage %d.", acfail, acreduction, acrandom, damage); + } else { + mlog(COMBAT__DAMAGE, "AC Damage Reduction: fail chance %d%%. Did not fail.", acfail); + } + } + + damage -= (aa_mit * damage); + + if(damage != 0 && damage < minhit) + damage = minhit; + } + + + //reduce the damage from shielding item and aa based on the min dmg + //spells offer pure mitigation + damage -= (minhit * itembonuses.MeleeMitigation / 100); + damage -= (damage * spellbonuses.MeleeMitigation / 100); + + if(damage < 0) + damage = 0; + + mlog(COMBAT__DAMAGE, "Applied %d percent mitigation, remaining damage %d", aa_mit, damage); +} + +void Bot::DoSpecialAttackDamage(Mob *who, SkillType skill, int32 max_damage, int32 min_damage, int32 hate_override,int ReuseTime, bool HitChance) { + //this really should go through the same code as normal melee damage to + //pick up all the special behavior there + + int32 hate = max_damage; + if(hate_override > -1) + hate = hate_override; + + if(skill == BASH) { + const ItemInst* inst = GetBotItem(SLOT_SECONDARY); + const Item_Struct* botweapon = 0; + if(inst) + botweapon = inst->GetItem(); + if(botweapon) { + if(botweapon->ItemType == ItemTypeShield) { + hate += botweapon->AC; + } + hate = hate * (100 + GetFuriousBash(botweapon->Focus.Effect)) / 100; + } + } + + min_damage += min_damage * GetMeleeMinDamageMod_SE(skill) / 100; + + if(HitChance && !who->CheckHitChance(this, skill, SLOT_PRIMARY)) + max_damage = 0; + + else{ + bool CanRiposte = true; + if(skill == THROWING && skill == ARCHERY) + CanRiposte = false; + + who->AvoidDamage(this, max_damage, CanRiposte); + who->MeleeMitigation(this, max_damage, min_damage); + + if(max_damage > 0) { + ApplyMeleeDamageBonus(skill, max_damage); + max_damage += who->GetAdditionalDamage(this, 0, true, skill); + max_damage += (itembonuses.HeroicSTR / 10) + (max_damage * who->GetSkillDmgTaken(skill) / 100) + GetSkillDmgAmt(skill); + TryCriticalHit(who, skill, max_damage); + } + } + + if(max_damage >= 0) //You should probably get aggro no matter what, but unclear why it was set like this. + who->AddToHateList(this, hate); + + who->Damage(this, max_damage, SPELL_UNKNOWN, skill, false); + + if(!GetTarget())return; + if (HasDied()) return; + + //[AA Dragon Punch] value[0] = 100 for 25%, chance value[1] = skill + if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skill){ + int kb_chance = 25; + kb_chance += kb_chance*(100-aabonuses.SpecialAttackKBProc[0])/100; + + if (MakeRandomInt(0, 99) < kb_chance) + SpellFinished(904, who, 10, 0, -1, spells[904].ResistDiff); + //who->Stun(100); Kayen: This effect does not stun on live, it only moves the NPC. + } + + if (HasSkillProcs()){ + float chance = (float)ReuseTime*RuleR(Combat, AvgProcsPerMinute)/60000.0f; + TrySkillProc(who, skill, chance); + } + + if(max_damage == -3 && !(who->GetHP() <= 0)) + DoRiposte(who); +} + +void Bot::TryBackstab(Mob *other, int ReuseTime) { + if(!other) + return; + + bool bIsBehind = false; + bool bCanFrontalBS = false; + + const ItemInst* inst = GetBotItem(SLOT_PRIMARY); + const Item_Struct* botpiercer = NULL; + if(inst) + botpiercer = inst->GetItem(); + if(!botpiercer || (botpiercer->ItemType != ItemTypePierce)) { + Say("I can't backstab with this weapon!"); + return; + } + + //Live AA - Triple Backstab + int tripleChance = itembonuses.TripleBackstab + spellbonuses.TripleBackstab + aabonuses.TripleBackstab; + + if (BehindMob(other, GetX(), GetY())) { + bIsBehind = true; + } + else { + //Live AA - Seized Opportunity + int FrontalBSChance = itembonuses.FrontalBackstabChance + spellbonuses.FrontalBackstabChance + aabonuses.FrontalBackstabChance; + + if (FrontalBSChance && (FrontalBSChance > MakeRandomInt(0, 100))) + bCanFrontalBS = true; + } + + if (bIsBehind || bCanFrontalBS){ // Bot is behind other OR can do Frontal Backstab + + // solar - chance to assassinate + int chance = 10 + (GetDEX()/10) + (itembonuses.HeroicDEX/10); //18.5% chance at 85 dex 40% chance at 300 dex + if( + level >= 60 && // bot is 60 or higher + other->GetLevel() <= 45 && // mob 45 or under + !other->CastToNPC()->IsEngaged() && // not aggro + other->GetHP()<=32000 + && other->IsNPC() + && MakeRandomFloat(0, 99) < chance // chance + ) { + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, ASSASSINATES, GetName()); + RogueAssassinate(other); + } + else { + RogueBackstab(other); + if (level > 54) { + float DoubleAttackProbability = (GetSkill(DOUBLE_ATTACK) + GetLevel()) / 500.0f; // 62.4 max + // Check for double attack with main hand assuming maxed DA Skill (MS) + + if(MakeRandomFloat(0, 1) < DoubleAttackProbability) // Max 62.4 % chance of DA + { + if(other->GetHP() > 0) + RogueBackstab(other,false,ReuseTime); + + if (tripleChance && other->GetHP() > 0 && tripleChance > MakeRandomInt(0, 100)) + RogueBackstab(other,false,ReuseTime); + } + } + } + } + //Live AA - Chaotic Backstab + else if(aabonuses.FrontalBackstabMinDmg || itembonuses.FrontalBackstabMinDmg || spellbonuses.FrontalBackstabMinDmg) { + + //we can stab from any angle, we do min damage though. + RogueBackstab(other, true); + if (level > 54) { + float DoubleAttackProbability = (GetSkill(DOUBLE_ATTACK) + GetLevel()) / 500.0f; // 62.4 max + // Check for double attack with main hand assuming maxed DA Skill (MS) + if(MakeRandomFloat(0, 1) < DoubleAttackProbability) // Max 62.4 % chance of DA + if(other->GetHP() > 0) + RogueBackstab(other,true, ReuseTime); + + if (tripleChance && other->GetHP() > 0 && tripleChance > MakeRandomInt(0, 100)) + RogueBackstab(other,false,ReuseTime); + } + } + else { //We do a single regular attack if we attack from the front without chaotic stab + Attack(other, 13); + } +} + +//heko: backstab +void Bot::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) +{ + int32 ndamage = 0; + int32 max_hit = 0; + int32 min_hit = 0; + int32 hate = 0; + int32 primaryweapondamage = 0; + int32 backstab_dmg = 0; + + ItemInst* botweaponInst = GetBotItem(SLOT_PRIMARY); + if(botweaponInst) { + primaryweapondamage = GetWeaponDamage(other, botweaponInst); + backstab_dmg = botweaponInst->GetItem()->BackstabDmg; + for(int i = 0; i < MAX_AUGMENT_SLOTS; ++i) + { + ItemInst *aug = botweaponInst->GetAugment(i); + if(aug) + { + backstab_dmg += aug->GetItem()->BackstabDmg; + } + } + } + else + { + primaryweapondamage = (GetLevel()/7)+1; // fallback incase it's a npc without a weapon, 2 dmg at 10, 10 dmg at 65 + backstab_dmg = primaryweapondamage; + } + + if(primaryweapondamage > 0){ + if(level > 25){ + max_hit = (((2*backstab_dmg) * GetDamageTable(BACKSTAB) / 100) * 10 * GetSkill(BACKSTAB) / 355) + ((level-25)/3) + 1; + hate = 20 * backstab_dmg * GetSkill(BACKSTAB) / 355; + } + else{ + max_hit = (((2*backstab_dmg) * GetDamageTable(BACKSTAB) / 100) * 10 * GetSkill(BACKSTAB) / 355) + 1; + hate = 20 * backstab_dmg * GetSkill(BACKSTAB) / 355; + } + + // determine minimum hits + if (level < 51) + { + min_hit = (level*15/10); + } + 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; + } + + if(!other->CheckHitChance(this, BACKSTAB, 0)) { + ndamage = 0; + } + else{ + if(min_damage){ + ndamage = min_hit; + } + else + { + if (max_hit < min_hit) + max_hit = min_hit; + + if(RuleB(Combat, UseIntervalAC)) + ndamage = max_hit; + else + ndamage = MakeRandomInt(min_hit, max_hit); + + } + } + } + else{ + ndamage = -5; + } + + DoSpecialAttackDamage(other, BACKSTAB, ndamage, min_hit, hate, ReuseTime); + DoAnim(animPiercing); +} + +void Bot::RogueAssassinate(Mob* other) +{ + ItemInst* botweaponInst = GetBotItem(SLOT_PRIMARY); + if(botweaponInst) { + if(GetWeaponDamage(other, botweaponInst)) { + other->Damage(this, 32000, SPELL_UNKNOWN, BACKSTAB); + } + else { + other->Damage(this, -5, SPELL_UNKNOWN, BACKSTAB); + } + } + + DoAnim(animPiercing); //piercing animation +} + +void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { + if(!target) + return; + + if(spellend_timer.Enabled() || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0) + return; + + if(!IsAttackAllowed(target)) + return; + + bool taunt_time = taunt_timer.Check(); + bool ca_time = classattack_timer.Check(false); + bool ka_time = knightattack_timer.Check(false); + + //only check attack allowed if we are going to do something + if((taunt_time || ca_time || ka_time) && !IsAttackAllowed(target)) + return; + + if(ka_time){ + int knightreuse = 1000; //lets give it a small cooldown actually. + switch(GetClass()){ + case SHADOWKNIGHT: case SHADOWKNIGHTGM:{ + CastSpell(SPELL_NPC_HARM_TOUCH, target->GetID()); + knightreuse = HarmTouchReuseTime * 1000; + break; + } + case PALADIN: case PALADINGM:{ + if(GetHPRatio() < 20) { + CastSpell(SPELL_LAY_ON_HANDS, GetID()); + knightreuse = LayOnHandsReuseTime * 1000; + } else { + knightreuse = 2000; //Check again in two seconds. + } + break; + } + } + knightattack_timer.Start(knightreuse); + } + + //general stuff, for all classes.... + //only gets used when their primary ability get used too + + //franck-add: EQoffline. Warrior bots must taunt the target. + if(taunting && target && target->IsNPC() && taunt_time ) { + //Only taunt if we are not top on target's hate list + //This ensures we have taunt available to regain aggro if needed + if(GetTarget() && GetTarget()->GetHateTop() && GetTarget()->GetHateTop() != this) { + Say("Taunting %s", target->GetCleanName()); + Taunt(target->CastToNPC(), false); + taunt_timer.Start(TauntReuseTime * 1000); + } + } + + if(!ca_time) + return; + + float HasteModifier = 0; + if(GetHaste() >= 0){ + HasteModifier = 10000 / (100 + GetHaste()); + } + else { + HasteModifier = (100 - GetHaste()); + } + int32 dmg = 0; + + uint16 skill_to_use = -1; + + int level = GetLevel(); + int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will + bool did_attack = false; + + switch(GetClass()) + { + case WARRIOR: + if(level >= RuleI(Combat, NPCBashKickLevel)){ + bool canBash = false; + if((GetRace() == OGRE || GetRace() == TROLL || GetRace() == BARBARIAN) // Racial Slam + || (m_inv.GetItem(SLOT_SECONDARY) && m_inv.GetItem(SLOT_SECONDARY)->GetItem()->ItemType == ItemTypeShield) //Using Shield + || (m_inv.GetItem(SLOT_PRIMARY) && (m_inv.GetItem(SLOT_PRIMARY)->GetItem()->ItemType == ItemType2HS + || m_inv.GetItem(SLOT_PRIMARY)->GetItem()->ItemType == ItemType2HB + || m_inv.GetItem(SLOT_PRIMARY)->GetItem()->ItemType == ItemType2HPierce) + && GetAA(aa2HandBash) >= 1)) { //Using 2 hand weapon, but has AA 2 Hand Bash + canBash = true; + } + + if(!canBash || 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. + skill_to_use = KICK; + } + else { + skill_to_use = BASH; + } + } + case RANGER: + case BEASTLORD: + skill_to_use = KICK; + break; + case BERSERKER: + skill_to_use = FRENZY; + break; + case CLERIC: + case SHADOWKNIGHT: + case PALADIN: + if(level >= RuleI(Combat, NPCBashKickLevel)){ + if((GetRace() == OGRE || GetRace() == TROLL || GetRace() == BARBARIAN) // Racial Slam + || (m_inv.GetItem(SLOT_SECONDARY) && m_inv.GetItem(SLOT_SECONDARY)->GetItem()->ItemType == ItemTypeShield) //Using Shield + || (m_inv.GetItem(SLOT_PRIMARY) && (m_inv.GetItem(SLOT_PRIMARY)->GetItem()->ItemType == ItemType2HS + || m_inv.GetItem(SLOT_PRIMARY)->GetItem()->ItemType == ItemType2HB + || m_inv.GetItem(SLOT_PRIMARY)->GetItem()->ItemType == ItemType2HPierce) + && GetAA(aa2HandBash) >= 1)) { //Using 2 hand weapon, but has AA 2 Hand Bash + skill_to_use = BASH; + } + } + break; + case MONK: + if(GetLevel() >= 30) + { + skill_to_use = FLYING_KICK; + } + else if(GetLevel() >= 25) + { + skill_to_use = DRAGON_PUNCH; + } + else if(GetLevel() >= 20) + { + skill_to_use = EAGLE_STRIKE; + } + else if(GetLevel() >= 10) + { + skill_to_use = TIGER_CLAW; + } + else if(GetLevel() >= 5) + { + skill_to_use = ROUND_KICK; + } + else + { + skill_to_use = KICK; + } + break; + case ROGUE: + skill_to_use = BACKSTAB; + break; + } + + if(skill_to_use == -1) + return; + + + if(skill_to_use == BASH) + { + if (target!=this) + { + DoAnim(animTailRake); + + if(GetWeaponDamage(target, GetBotItem(SLOT_SECONDARY)) <= 0 && + GetWeaponDamage(target, GetBotItem(SLOT_SHOULDER)) <= 0){ + dmg = -5; + } + else{ + if(!target->CheckHitChance(this, BASH, 0)) { + dmg = 0; + } + else{ + if(RuleB(Combat, UseIntervalAC)) + dmg = GetBashDamage(); + else + dmg = MakeRandomInt(1, GetBashDamage()); + + } + } + + reuse = BashReuseTime * 1000; + //reuse = (reuse*HasteModifier)/100; + + DoSpecialAttackDamage(target, BASH, dmg, 1,-1,reuse); + + did_attack = true; + + if(reuse > 0 && !IsRiposte) + { + //p_timers.Start(pTimerCombatAbility, reuse); + } + } + } + + if(skill_to_use == FRENZY) + { + int AtkRounds = 3; + int skillmod = 0; + + if(MaxSkill(FRENZY) > 0) + skillmod = 100*GetSkill(FRENZY)/MaxSkill(FRENZY); + + int32 max_dmg = (26 + ((((GetLevel()-6) * 2)*skillmod)/100)) * ((100+RuleI(Combat, FrenzyBonus))/100); + int32 min_dmg = 0; + DoAnim(anim2HSlashing); + + if (GetLevel() < 51) + min_dmg = 1; + else + min_dmg = GetLevel()*8/10; + + if (min_dmg > max_dmg) + max_dmg = min_dmg; + + reuse = FrenzyReuseTime * 1000; + //reuse = (reuse * HasteModifier)/100; + + did_attack = true; + + //Live parses show around 55% Triple 35% Double 10% Single, you will always get first hit. + while(AtkRounds > 0) { + + if (GetTarget() && (AtkRounds == 1 || MakeRandomInt(0,100) < 75)){ + DoSpecialAttackDamage(GetTarget(), FRENZY, max_dmg, min_dmg, max_dmg , reuse, true); + } + AtkRounds--; + } + + if(reuse > 0 && !IsRiposte) { + //p_timers.Start(pTimerCombatAbility, reuse); + } + } + + if(skill_to_use == KICK) + { + if(target!=this) + { + DoAnim(animKick); + + if(GetWeaponDamage(target, GetBotItem(SLOT_FEET)) <= 0){ + dmg = -5; + } + else{ + if(!target->CheckHitChance(this, KICK, 0)) { + dmg = 0; + } + else{ + if(RuleB(Combat, UseIntervalAC)) + dmg = GetKickDamage(); + else + dmg = MakeRandomInt(1, GetKickDamage()); + } + } + + reuse = KickReuseTime * 1000; + + DoSpecialAttackDamage(target, KICK, dmg, 1,-1, reuse); + + did_attack = true; + } + } + + if(skill_to_use == FLYING_KICK || + skill_to_use == DRAGON_PUNCH || + skill_to_use == EAGLE_STRIKE || + skill_to_use == TIGER_CLAW || + skill_to_use == ROUND_KICK) + { + reuse = MonkSpecialAttack(target, skill_to_use) - 1; + MonkSpecialAttack(target, skill_to_use); + + //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] = { FLYING_KICK, DRAGON_PUNCH, EAGLE_STRIKE, TIGER_CLAW, ROUND_KICK }; + MonkSpecialAttack(target, MonkSPA[MakeRandomInt(0,4)]); + + int TripleChance = 25; + + if (bDoubleSpecialAttack > 100) + TripleChance += TripleChance*(100-bDoubleSpecialAttack)/100; + + if(TripleChance > MakeRandomInt(0,100)) { + MonkSpecialAttack(target, MonkSPA[MakeRandomInt(0,4)]); + } + } + + reuse *= 1000; + did_attack = true; + } + + if(skill_to_use == BACKSTAB) + { + reuse = BackstabReuseTime * 1000; + did_attack = true; + + if (IsRiposte) + reuse=0; + + TryBackstab(target,reuse); + } + + classattack_timer.Start(reuse*HasteModifier/100); +} + +bool Bot::TryHeadShot(Mob* defender, SkillType skillInUse) { + bool Result = false; + + if(defender && (defender->GetBodyType() == BT_Humanoid) && (skillInUse == ARCHERY) && (GetClass() == RANGER) && (GetLevel() >= 62)) { + int defenderLevel = defender->GetLevel(); + int rangerLevel = GetLevel(); + // Bot Ranger Headshot AA through level 85(Underfoot) + if( GetAA(aaHeadshot) && ((defenderLevel - 46) <= GetAA(aaHeadshot) * 2) ) { + // WildcardX: These chance formula's below are arbitrary. If someone has a better formula that is more + // consistent with live, feel free to update these. + float AttackerChance = 0.20f + ((float)(rangerLevel - 51) * 0.005f); + float DefenderChance = (float)MakeRandomFloat(0.00f, 1.00f); + if(AttackerChance > DefenderChance) { + mlog(COMBAT__ATTACKS, "Landed a headshot: Attacker chance was %f and Defender chance was %f.", AttackerChance, DefenderChance); + // WildcardX: At the time I wrote this, there wasnt a string id for something like HEADSHOT_BLOW + //entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FINISHING_BLOW, GetName()); + entity_list.MessageClose(this, false, 200, MT_CritMelee, "%s has scored a leathal HEADSHOT!", GetName()); + defender->Damage(this, (defender->GetMaxHP()+50), SPELL_UNKNOWN, skillInUse); + Result = true; + } + else { + mlog(COMBAT__ATTACKS, "FAILED a headshot: Attacker chance was %f and Defender chance was %f.", AttackerChance, DefenderChance); + } + } + } + + return Result; +} + +//offensive spell aggro +int32 Bot::CheckAggroAmount(uint16 spellid) { + int32 AggroAmount = Mob::CheckAggroAmount(spellid); + + int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid); + AggroAmount = (AggroAmount * (100+focusAggro) / 100); + + return AggroAmount; +} + +int32 Bot::CheckHealAggroAmount(uint16 spellid, uint32 heal_possible) { + int32 AggroAmount = Mob::CheckHealAggroAmount(spellid, heal_possible); + + int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid); + + AggroAmount = (AggroAmount * (100 + focusAggro) / 100); + + return AggroAmount; +} + +void Bot::MakePet(uint16 spell_id, const char* pettype, const char *petname) { + Mob::MakePet(spell_id, pettype, petname); +} + +void Bot::AI_Stop() { + NPC::AI_Stop(); + Mob::AI_Stop(); +} + +//this is called with 'this' as the mob being looked at, and +//iOther the mob who is doing the looking. It should figure out +//what iOther thinks about 'this' +FACTION_VALUE Bot::GetReverseFactionCon(Mob* iOther) { +#if FACTIONS_DEBUG >= 20 + LogFile->write(EQEMuLog::Debug, "called N $s::GetReverseFactionCon(%s)", GetName(), iOther->GetName()); +#endif + + _ZP(Bot_GetReverseFactionCon); + + if(iOther->IsBot()) { + return FACTION_ALLY; + } + + return NPC::GetReverseFactionCon(iOther); +} + +Mob* Bot::GetOwnerOrSelf() { + Mob* Result = 0; + + if(this->GetBotOwner()) + Result = GetBotOwner(); + else + Result = this; + + return Result; +} + +Mob* Bot::GetOwner() { + Mob* Result = 0; + + Result = GetBotOwner(); + + if(!Result) { + this->SetBotOwner(0); + } + + return Result; +} + +bool Bot::IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined) +{ + bool Result = false; + + if(attacker && target) + { + if(attacker->IsClient() && target->IsBot() && attacker->CastToClient()->GetPVP() && target->CastToBot()->GetBotOwner()->CastToClient()->GetPVP()) + { + hasRuleDefined = true; + Result = true; + } + else if(attacker->IsClient() && target->IsBot()) + { + hasRuleDefined = true; + Result = false; + } + else if(attacker->IsBot() && target->IsNPC()) + { + hasRuleDefined = true; + Result = true; + } + else if(attacker->IsBot() && !target->IsNPC()) + { + hasRuleDefined = true; + Result = false; + } + else if(attacker->IsPet() && attacker->IsFamiliar()) + { + hasRuleDefined = true; + Result = false; + } + else if(attacker->IsBot() && attacker->CastToBot()->GetBotOwner() && attacker->CastToBot()->GetBotOwner()->CastToClient()->GetPVP()) + { + if(target->IsBot() && target->GetOwner() && target->GetOwner()->CastToClient()->GetPVP()) + { + // my target is a bot and it's owner is pvp + hasRuleDefined = true; + + if(target->GetOwner() == attacker->GetOwner()) + { + // no attacking if my target's owner is my owner + Result = false; + } + else + { + Result = true; + } + } + else if(target->IsClient() && target->CastToClient()->GetPVP()) + { + // my target is a player and it's pvp + hasRuleDefined = true; + + if(target == attacker->GetOwner()) + { + // my target cannot be my owner + Result = false; + } + else + { + Result = true; + } + } + else if(target->IsNPC()) + { + hasRuleDefined = true; + Result = true; + } + else if(!target->IsNPC()) + { + hasRuleDefined = true; + Result = false; + } + } + } + + return Result; +} + +void Bot::EquipBot(std::string* errorMessage) { + GetBotItems(errorMessage, m_inv); + + const ItemInst* inst = 0; + const Item_Struct* item = 0; + for(int i=0; i<=21; ++i) { + inst = GetBotItem(i); + if(inst) { + item = inst->GetItem(); + BotTradeAddItem(inst->GetID(), inst, inst->GetCharges(), item->Slots, i, errorMessage, false); + if(!errorMessage->empty()) + return; + } + } +} + +//// This method is meant to be called by zone or client methods to clean up objects when a client camps, goes LD, zones out or something like that. +//void Bot::DestroyBotRaidObjects(Client* client) { +// if(client) { +// if(client->GetBotRaidID() > 0) { +// BotRaids* br = entity_list.GetBotRaidByMob(client); +// if(br) { +// br->RemoveRaidBots(); +// br = NULL; +// } +// } +// +// //BotOrderCampAll(client); +// } +//} + +// Orders all the bots owned by the specified client bot owner to camp out of the game +void Bot::BotOrderCampAll(Client* c) { + if(c) { + std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); + + for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); botListItr++) { + (*botListItr)->Camp(); + } + } +} + +void Bot::ProcessBotOwnerRefDelete(Mob* botOwner) { + if(botOwner) { + if(botOwner->IsClient()) { + std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(botOwner->CastToClient()->CharacterID()); + + if(!BotList.empty()) { + for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); botListItr++) { + Bot* tempBot = *botListItr; + + if(tempBot) { + tempBot->SetTarget(0); + tempBot->SetBotOwner(0); + } + } + } + } + } +} + +void Bot::ProcessGuildInvite(Client* guildOfficer, Bot* botToGuild) { + if(guildOfficer && botToGuild) { + // Bots can be only guild member rank + if(!botToGuild->IsInAGuild()) { + //they are not in this or any other guild, this is an invite + if (!guild_mgr.CheckPermission(guildOfficer->GuildID(), guildOfficer->GuildRank(), GUILD_INVITE)) { + guildOfficer->Message(13, "You dont have permission to invite."); + return; + } + + // mlog(GUILDS__ACTIONS, "Inviting %s (%d) into guild %s (%d)", botToGuild->GetName(), botToGuild->GetBotID(), guild_mgr.GetGuildName(client->GuildID()), client->GuildID()); + + SetBotGuildMembership(botToGuild->GetBotID(), guildOfficer->GuildID(), GUILD_MEMBER); + + //_log(GUILDS__REFRESH, "Sending char refresh for BOT %s from guild %d to world", botToGuild->GetName(), guildOfficer->GuildID(); + + ServerPacket* pack = new ServerPacket(ServerOP_GuildCharRefresh, sizeof(ServerGuildCharRefresh_Struct)); + ServerGuildCharRefresh_Struct *s = (ServerGuildCharRefresh_Struct *) pack->pBuffer; + s->guild_id = guildOfficer->GuildID(); + s->old_guild_id = GUILD_NONE; + s->char_id = botToGuild->GetBotID(); + worldserver.SendPacket(pack); + safe_delete(pack); + + } else { + //they are in some other guild + guildOfficer->Message(13, "Player is in a guild."); + return; + } + } +} + +bool Bot::ProcessGuildRemoval(Client* guildOfficer, std::string botName) { + bool Result = false; + + if(guildOfficer && !botName.empty()) { + Bot* botToUnGuild = entity_list.GetBotByBotName(botName); + if(botToUnGuild) { + SetBotGuildMembership(botToUnGuild->GetBotID(), 0, 0); + Result = true; + } + else { + uint32 botId = GetBotIDByBotName(botName); + + if(botId > 0) { + // Bot is camped or in another zone + SetBotGuildMembership(botId, 0, 0); + Result = true; + } + } + + if(Result) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildManageRemove, sizeof(GuildManageRemove_Struct)); + GuildManageRemove_Struct* gm = (GuildManageRemove_Struct*) outapp->pBuffer; + gm->guildeqid = guildOfficer->GuildID(); + strcpy(gm->member, botName.c_str()); + guildOfficer->Message(0, "%s successfully removed from your guild.", botName.c_str()); + entity_list.QueueClientsGuild(guildOfficer, outapp, false, gm->guildeqid); + safe_delete(outapp); + } + } + + return Result; +} + +void Bot::SetBotGuildMembership(uint32 botId, uint32 guildid, uint8 rank) { + if(botId > 0) { + std::string errorMessage; + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + 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); + } + } + + safe_delete_array(query); + + if(!errorMessage.empty()) { + // TODO: Log this error message to the zone error log + } + } +} + +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; + } + + mysql_free_result(DatasetResult); + } + + safe_delete(Query); + + if(!errorMessage.empty()) { + // TODO: Record this error message to zone error log + } + } +} + +int32 Bot::CalcMaxMana() { + switch(GetCasterClass()) + { + case 'I': + case 'W': + { + max_mana = (GenerateBaseManaPoints() + 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(cur_mana > max_mana) { + cur_mana = max_mana; + } + else if(max_mana < 0) { + max_mana = 0; + } + + return max_mana; +} + +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; + + //default value for attack timer in case they have + //an invalid weapon equipped: + attack_timer.SetAtTrigger(4000, true); + + Timer* TimerToUse = NULL; + const Item_Struct* PrimaryWeapon = NULL; + + for (int i=SLOT_RANGE; i<=SLOT_SECONDARY; i++) { + + //pick a timer + if (i == SLOT_PRIMARY) + TimerToUse = &attack_timer; + else if (i == SLOT_RANGE) + TimerToUse = &ranged_timer; + else if(i == SLOT_SECONDARY) + TimerToUse = &attack_dw_timer; + else //invalid slot (hands will always hit this) + continue; + + const Item_Struct* ItemToUse = NULL; + ItemInst* ci = GetBotItem(i); + if(ci) + ItemToUse = ci->GetItem(); + + //special offhand stuff + if(i == SLOT_SECONDARY) { + //if we have a 2H weapon in our main hand, no dual + if(PrimaryWeapon != NULL) { + if( PrimaryWeapon->ItemClass == ItemClassCommon + && (PrimaryWeapon->ItemType == ItemType2HS + || PrimaryWeapon->ItemType == ItemType2HB + || PrimaryWeapon->ItemType == ItemType2HPierce)) { + attack_dw_timer.Disable(); + continue; + } + } + + //clients must have the skill to use it... + if(!GetSkill(DUAL_WIELD)) { + attack_dw_timer.Disable(); + continue; + } + } + + //see if we have a valid weapon + if(ItemToUse != NULL) { + //check type and damage/delay + if(ItemToUse->ItemClass != ItemClassCommon + || ItemToUse->Damage == 0 + || ItemToUse->Delay == 0) { + //no weapon + ItemToUse = NULL; + } + // Check to see if skill is valid + else if((ItemToUse->ItemType > ItemTypeThrowing) && (ItemToUse->ItemType != ItemTypeHand2Hand) && (ItemToUse->ItemType != ItemType2HPierce)) { + //no weapon + ItemToUse = NULL; + } + } + + int16 DelayMod = itembonuses.HundredHands + spellbonuses.HundredHands; + if (DelayMod < -99) + DelayMod = -99; + + //if we have no weapon.. + if (ItemToUse == NULL) { + //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 == ItemTypeThrowing)) + { + /*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); + } + + if(i == SLOT_PRIMARY) + PrimaryWeapon = ItemToUse; + } +} + +int32 Bot::Additional_SpellDmg(uint16 spell_id, bool bufftick) +{ + int32 spell_dmg = 0; + spell_dmg += GetBotFocusEffect(BotfocusFF_Damage_Amount, spell_id); + spell_dmg += GetBotFocusEffect(BotfocusSpellDamage, spell_id); + + //For DOTs you need to apply the damage over the duration of the dot to each tick (this is how live did it) + if (bufftick){ + int duration = CalcBuffDuration(this, this, spell_id); + if (duration > 0) + return spell_dmg /= duration; + else + return 0; + } + return spell_dmg; +} + +int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value) { + // Important variables: + // value: the actual damage after resists, passed from Mob::SpellEffect + // modifier: modifier to damage (from spells & focus effects?) + // ratio: % of the modifier to apply (from AAs & natural bonus?) + // chance: critital chance % + + int32 modifier = 100; + int16 spell_dmg = 0; + + //Dunno if this makes sense: + if (spells[spell_id].resisttype > 0) + modifier += GetBotFocusEffect((BotfocusType)(0-spells[spell_id].resisttype), spell_id); + + + int tt = spells[spell_id].targettype; + if (tt == ST_UndeadAE || tt == ST_Undead || tt == ST_Summoned) { + //undead/summoned spells + modifier += GetBotFocusEffect(BotfocusImprovedUndeadDamage, spell_id); + } else { + //damage spells. + modifier += GetBotFocusEffect(BotfocusImprovedDamage, spell_id); + modifier += GetBotFocusEffect(BotfocusSpellEffectiveness, spell_id); + modifier += GetBotFocusEffect(BotfocusImprovedDamage2, spell_id); + } + + // 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 ) { + if (this->GetLevel() > 40) + value -= (this->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 + } + + //these spell IDs could be wrong + if (spell_id == SPELL_LEECH_TOUCH) { //leech touch + value -= GetAA(aaConsumptionoftheSoul) * 200; //Consumption of the Soul + value -= GetAA(aaImprovedConsumptionofSoul) * 200; //Improved Consumption of the Soul + } + + //spell crits, dont make sense if cast on self. + if(tt != ST_Self) { + // item SpellDmg bonus + // Formula = SpellDmg * (casttime + recastime) / 7; Cant trigger off spell less than 5 levels below and cant cause more dmg than the spell itself. + if(this->itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) { + spell_dmg = this->itembonuses.SpellDmg * (spells[spell_id].cast_time + spells[spell_id].recast_time) / 7000; + if(spell_dmg > -value) + spell_dmg = -value; + } + + // Spell-based SpellDmg adds directly but it restricted by focuses. + spell_dmg += Additional_SpellDmg(spell_id); + + int chance = RuleI(Spells, BaseCritChance); + int32 ratio = RuleI(Spells, BaseCritRatio); + + chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; + ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease; + + if(GetClass() == WIZARD) { + if (GetLevel() >= RuleI(Spells, WizCritLevel)) { + chance += RuleI(Spells, WizCritChance); + ratio += RuleI(Spells, WizCritRatio); + } + if(aabonuses.SpellCritDmgIncrease > 0) // wizards get an additional bonus + ratio += aabonuses.SpellCritDmgIncrease * 1.5; //108%, 115%, 124%, close to Graffe's 207%, 215%, & 225% + } + + //Improved Harm Touch is a guaranteed crit if you have at least one level of SCF. + if (spell_id == SPELL_IMP_HARM_TOUCH) { + if ( (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0) ) + chance = 100; + } + + /* + //Handled in aa_effects will focus spells from 'spellgroup=99'. (SK life tap from buff procs) + //If you are using an older spell file table (Pre SOF)... + //Use SQL optional_EnableSoulAbrasionAA to update your spells table to properly use the effect. + //If you do not want to update your table then you may want to enable this. + if(tt == ST_Tap) { + if(spells[spell_id].classes[SHADOWKNIGHT-1] >= 254 && spell_id != SPELL_LEECH_TOUCH){ + if(ratio < 100) //chance increase and ratio are made up, not confirmed + ratio = 100; + + switch (GetAA(aaSoulAbrasion)) + { + case 1: + modifier += 100; + break; + case 2: + modifier += 200; + break; + case 3: + modifier += 300; + break; + } + } + } + */ + + if (chance > 0) { + mlog(SPELLS__CRITS, "Attempting spell crit. Spell: %s (%d), Value: %d, Modifier: %d, Chance: %d, Ratio: %d", spells[spell_id].name, spell_id, value, modifier, chance, ratio); + if(MakeRandomInt(0,100) <= chance) { + modifier += modifier*ratio/100; + spell_dmg *= 2; + mlog(SPELLS__CRITS, "Spell crit successful. Final damage modifier: %d, Final Damage: %d", modifier, (value * modifier / 100) - spell_dmg); + entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s delivers a critical blast! (%d)", GetName(), (-value * modifier / 100) + spell_dmg); + } else + mlog(SPELLS__CRITS, "Spell crit failed. Final Damage Modifier: %d, Final Damage: %d", modifier, (value * modifier / 100) - spell_dmg); + } + } + + return ((value * modifier / 100) - spell_dmg); +} + +int32 Bot::Additional_Heal(uint16 spell_id) +{ + int32 heal_amt = 0; + + heal_amt += GetBotFocusEffect(BotfocusAdditionalHeal, spell_id); + heal_amt += GetBotFocusEffect(BotfocusAdditionalHeal2, spell_id); + + if (heal_amt){ + int duration = CalcBuffDuration(this, this, spell_id); + if (duration > 0) + return heal_amt /= duration; + } + + return heal_amt; +} + +int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value) { + int32 modifier = 100; + int16 heal_amt = 0; + modifier += GetBotFocusEffect(BotfocusImprovedHeal, spell_id); + modifier += GetBotFocusEffect(BotfocusSpellEffectiveness, spell_id); + heal_amt += Additional_Heal(spell_id); + int chance = 0; + + if(spells[spell_id].buffduration < 1) { + uint8 botlevel = GetLevel(); + uint8 botclass = GetClass(); + // Formula = HealAmt * (casttime + recastime) / 7; Cant trigger off spell less than 5 levels below and cant heal more than the spell itself. + if(this->itembonuses.HealAmt && spells[spell_id].classes[(botclass%16) - 1] >= botlevel - 5) { + heal_amt = this->itembonuses.HealAmt * (spells[spell_id].cast_time + spells[spell_id].recast_time) / 7000; + if(heal_amt > value) + heal_amt = value; + } + + // Check for buffs that affect the healrate of the target and critical heal rate of target + if(GetTarget()) { + value += value * GetHealRate(spell_id) / 100; + chance += GetCriticalHealRate(spell_id); + } + + //Live AA - Healing Gift, Theft of Life + chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + + if(MakeRandomInt(0,99) < chance) { + entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s performs an exceptional heal! (%d)", GetName(), ((value * modifier / 50) + heal_amt*2)); + return ((value * modifier / 50) + heal_amt*2); + } + else{ + return ((value * modifier / 100) + heal_amt); + } + } + // Hots + else { + chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + if(MakeRandomInt(0,99) < chance) + return ((value * modifier / 50) + heal_amt*2); + } + + return ((value * modifier / 100) + heal_amt); +} + +int32 Bot::GetActSpellCasttime(uint16 spell_id, int32 casttime) { + int32 cast_reducer = 0; + cast_reducer += GetBotFocusEffect(BotfocusSpellHaste, spell_id); + + uint8 botlevel = GetLevel(); + uint8 botclass = GetClass(); + + if (botlevel >= 51 && casttime >= 3000 && !BeneficialSpell(spell_id) + && (botclass == SHADOWKNIGHT || botclass == RANGER + || botclass == PALADIN || botclass == BEASTLORD )) + cast_reducer += (GetLevel()-50)*3; + + if((casttime >= 4000) && BeneficialSpell(spell_id) && ((CalcBuffDuration(this,this,spell_id)-1) > 0)) { + switch (GetAA(aaSpellCastingDeftness)) { + case 1: + cast_reducer += 5; + break; + case 2: + cast_reducer += 10; + break; + case 3: + cast_reducer += 25; + break; + } + + switch (GetAA(aaQuickBuff)) { + case 1: + cast_reducer += 10; + break; + case 2: + cast_reducer += 25; + break; + case 3: + cast_reducer += 50; + break; + } + } + + if(IsSummonSpell(spell_id)) { + switch (GetAA(aaQuickSummoning)) { + case 1: + cast_reducer += 10; + break; + case 2: + cast_reducer += 25; + break; + case 3: + cast_reducer += 50; + break; + } + } + + if(IsEvacSpell(spell_id)) { + switch (GetAA(aaQuickEvacuation)) { + case 1: + cast_reducer += 10; + break; + case 2: + cast_reducer += 25; + break; + case 3: + cast_reducer += 50; + break; + } + } + + if(IsDamageSpell(spell_id) && spells[spell_id].cast_time >= 4000) { + switch (GetAA(aaQuickDamage)) { + case 1: + cast_reducer += 2; + break; + case 2: + cast_reducer += 5; + break; + case 3: + cast_reducer += 10; + break; + } + } + + if (cast_reducer > RuleI(Spells, MaxCastTimeReduction)) + cast_reducer = RuleI(Spells, MaxCastTimeReduction); + + casttime = (casttime*(100 - cast_reducer)/100); + + return casttime; +} + +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; + // Doesnt generate mana, so best case is a free spell + if(mana_back > cost) + mana_back = cost; + + cost -= mana_back; + } + + // This formula was derived from the following resource: + // http://www.eqsummoners.com/eq1/specialization-library.html + // WildcardX + float PercentManaReduction = 0; + float SpecializeSkill = GetSpecializeSkillValue(spell_id); + int SuccessChance = MakeRandomInt(0, 100); + + float bonus = 1.0; + switch(GetAA(aaSpellCastingMastery)) + { + case 1: + bonus += 0.05; + break; + case 2: + bonus += 0.15; + break; + case 3: + bonus += 0.30; + break; + } + + bonus += 0.05 * GetAA(aaAdvancedSpellCastingMastery); + + if(SuccessChance <= (SpecializeSkill * 0.3 * bonus)) + { + PercentManaReduction = 1 + 0.05 * SpecializeSkill; + switch(GetAA(aaSpellCastingMastery)) + { + case 1: + PercentManaReduction += 2.5; + break; + case 2: + PercentManaReduction += 5.0; + break; + case 3: + PercentManaReduction += 10.0; + break; + } + + switch(GetAA(aaAdvancedSpellCastingMastery)) + { + case 1: + PercentManaReduction += 2.5; + break; + case 2: + PercentManaReduction += 5.0; + break; + case 3: + PercentManaReduction += 10.0; + break; + } + } + + int16 focus_redux = GetBotFocusEffect(BotfocusManaCost, spell_id); + + if(focus_redux > 0) + { + PercentManaReduction += MakeRandomFloat(1, (double)focus_redux); + } + + cost -= (cost * (PercentManaReduction / 100)); + + // Gift of Mana - reduces spell cost to 1 mana + if(focus_redux >= 100) { + uint32 buff_max = GetMaxTotalSlots(); + for (int buffSlot = 0; buffSlot < buff_max; buffSlot++) { + if (buffs[buffSlot].spellid == 0 || buffs[buffSlot].spellid >= SPDAT_RECORDS) + continue; + + if(IsEffectInSpell(buffs[buffSlot].spellid, SE_ReduceManaCost)) { + if(CalcFocusEffect(focusManaCost, buffs[buffSlot].spellid, spell_id) == 100) + cost = 1; + } + } + } + + if(cost < 0) + cost = 0; + + return cost; +} + +float Bot::GetActSpellRange(uint16 spell_id, float range) { + float extrange = 100; + extrange += GetBotFocusEffect(BotfocusRange, spell_id); + return (range * extrange) / 100; +} + +int32 Bot::GetActSpellDuration(uint16 spell_id, int32 duration) { + int increase = 100; + increase += GetBotFocusEffect(BotfocusSpellDuration, spell_id); + int tic_inc = 0; + tic_inc = GetBotFocusEffect(BotfocusSpellDurByTic, spell_id); + + if(IsBeneficialSpell(spell_id)) + { + switch (GetAA(aaSpellCastingReinforcement)) { + case 1: + increase += 5; + break; + case 2: + increase += 15; + break; + case 3: + increase += 30; + if (GetAA(aaSpellCastingReinforcementMastery) == 1) + increase += 20; + break; + } + + if (GetAA(aaSpellCastingReinforcementMastery)) + increase += 20; + } + + if(IsMezSpell(spell_id)) { + tic_inc += GetAA(aaMesmerizationMastery); + } + + return (((duration * increase) / 100) + tic_inc); +} + +float Bot::GetAOERange(uint16 spell_id) { + float range; + + range = spells[spell_id].aoerange; + if(range == 0) //for TGB spells, they prolly do not have an aoe range + range = spells[spell_id].range; + if(range == 0) + range = 10; //something.... + + if(IsBardSong(spell_id) && IsBeneficialSpell(spell_id)) { + //Live AA - Extended Notes, SionachiesCrescendo + float song_bonus = aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange; + range += range*song_bonus /100.0f; + } + + range = GetActSpellRange(spell_id, range); + + return range; +} + +bool Bot::SpellEffect(Mob* caster, uint16 spell_id, float partial) { + bool Result = false; + + Result = Mob::SpellEffect(caster, spell_id, partial); + + // Franck-add: If healed/doted, a bot must show its new HP to its leader + if(IsGrouped()) { + Group *g = GetGroup(); + if(g) { + EQApplicationPacket hp_app; + CreateHPPacket(&hp_app); + for(int i=0; imembers[i] && g->members[i]->IsClient()) { + g->members[i]->CastToClient()->QueuePacket(&hp_app); + } + } + } + } + + return Result; +} + +void Bot::DoBuffTic(uint16 spell_id, uint32 ticsremaining, uint8 caster_level, Mob* caster) { + Mob::DoBuffTic(spell_id, ticsremaining, caster_level, caster); +} + +bool Bot::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot, int16 *resist_adjust) { + bool Result = false; + + if(zone && !zone->IsSpellBlocked(spell_id, GetX(), GetY(), GetZ())) { + _ZP(Bot_CastSpell); + + mlog(SPELLS__CASTING, "CastSpell called for spell %s (%d) on entity %d, slot %d, time %d, mana %d, from item slot %d", + spells[spell_id].name, spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot); + + if(casting_spell_id == spell_id) + ZeroCastingVars(); + + if(GetClass() != BARD) { + if(!IsValidSpell(spell_id) || casting_spell_id || delaytimer || spellend_timer.Enabled() || IsStunned() || IsFeared() || IsMezzed() || (IsSilenced() && !IsDiscipline(spell_id)) || (IsAmnesiad() && IsDiscipline(spell_id))) { + mlog(SPELLS__CASTING_ERR, "Spell casting canceled: not able to cast now. Valid? %d, casting %d, waiting? %d, spellend? %d, stunned? %d, feared? %d, mezed? %d, silenced? %d", + IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled(), IsStunned(), IsFeared(), IsMezzed(), IsSilenced() ); + if(IsSilenced() && !IsDiscipline(spell_id)) + Message_StringID(13, SILENCED_STRING); + if(IsAmnesiad() && IsDiscipline(spell_id)) + Message_StringID(13, MELEE_SILENCE); + if(casting_spell_id) + AI_Event_SpellCastFinished(false, casting_spell_slot); + return(false); + } + } + + if(IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()){ + Message_StringID(13, SPELL_WOULDNT_HOLD); + if(casting_spell_id) + AI_Event_SpellCastFinished(false, casting_spell_slot); + return(false); + } + + //cannot cast under deivne aura + if(DivineAura()) { + mlog(SPELLS__CASTING_ERR, "Spell casting canceled: cannot cast while Divine Aura is in effect."); + InterruptSpell(173, 0x121, false); + return(false); + } + + // check for fizzle + // note that CheckFizzle itself doesn't let NPCs fizzle, + // but this code allows for it. + if(slot < MAX_PP_MEMSPELL && !CheckFizzle(spell_id)) + { + int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE; + InterruptSpell(fizzle_msg, 0x121, spell_id); + + uint32 use_mana = ((spells[spell_id].mana) / 4); + mlog(SPELLS__CASTING_ERR, "Spell casting canceled: fizzled. %d mana has been consumed", use_mana); + + // fizzle 1/4 the mana away + SetMana(GetMana() - use_mana); + return(false); + } + + if (HasActiveSong()) { + mlog(SPELLS__BARDS, "Casting a new spell/song while singing a song. Killing old song %d.", bardsong); + //Note: this does NOT tell the client + //_StopSong(); + bardsong = 0; + bardsong_target_id = 0; + bardsong_slot = 0; + bardsong_timer.Disable(); + } + + Result = DoCastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot); + } + + return Result; +} + +bool Bot::SpellOnTarget(uint16 spell_id, Mob* spelltar) { + bool Result = false; + + if(!IsValidSpell(spell_id)) + return false; + + if(spelltar) { + if(spelltar->IsBot() && (spells[spell_id].targettype == ST_GroupTeleport)) { + // So I made this check because teleporting a group of bots tended to crash the zone + // It seems several group spells also show up as ST_GroupTeleport for some + // reason so I now have to check by spell id. These appear to be Group v1 spells and + // Heal over Time spells. + switch(spell_id) { + // Paladin + case 3577: // Wave of Life + case 4065: // Blessing of Austerity + case 1455: // Wave of Healing + case 2589: // Healing Wave of Prexus + case 3427: // Wave of Marr + case 3683: // Ethereal Cleansing + case 1283: // Celestial Cleansing + case 3485: // Supernal Cleansing + case 5293: // Pious Cleansing + case 4893: // Wave of Trushar + case 5295: // Jeron's Mark + case 5296: // Wave of Piety + // Bard + case 4085: // Forpar's Aria of Affliction + case 4083: // Rizlona's Embers + case 4086: // Forpar's Psalm of Pain + case 4084: // Rizlona's Fire + case 6734: // Song of the Storm + case 3651: // Wind of Marr + case 4087: // Forpar's Verse of Venom + case 3362: // Rizlona's Call of Flame + case 4112: // Call of the Muse + case 4872: // Echo of the Trusik + case 4873: // Dark Echo + case 5377: // Cantata of Life + case 5380: // Yelhun's Mystic Call + case 5382: // Eriki's Psalm of Power + case 6666: // Storm Blade + case 5388: // Ancient Call of Power + // Cleric + case 134: // Word of Health + case 136: // Word of Healing + case 1520: // Word of Vigor + case 1521: // Word of Restoration + case 1523: // Word of Redemption + case 3471: // Word of Replenishment + case 5270: // Word of Vivification + case 2502: // Celestial Remedy + case 2175: // Celestial Health + case 1444: // Celestial Healing + case 1522: // Celestial Elixir + case 2180: // Etherial Elixir + case 3047: // Kazad's Mark + case 3475: // Supernal Elixir + case 4053: // Blessing of Temperance + case 4108: // Aura of Reverence + case 4882: // Holy Elixir + case 5259: // Pious Elixir + case 5272: // Aura of Devotion + case 5277: // Balikor's Mark + // Enchanter + case 5517: // Circle of Alendar + case 6671: // Rune of Rikkukin + case 6739: // Rune of the Scale + // Shaman + case 2521: // Talisman of the Beast + case 4055: // Pack Shrew + case 3842: // Blood of Nadox + case 5417: // Champion + // Druid + case 4058: // Feral Pack + case 2520: // Natures Recovery + break; + default: + return false; + } + } + + //Franck-add: can't detrimental spell on bots and bots can't detriment on you or the others bots + if(((IsDetrimentalSpell(spell_id) && spelltar->IsBot()) || (IsDetrimentalSpell(spell_id) && spelltar->IsClient())) && !IsResurrectionEffects(spell_id)) + return false; + + if(spelltar->IsPet()) { + for(int i=0; iGetZoneID() == 202) && !(this == caster)) { + Result = Mob::IsImmuneToSpell(spell_id, caster); + + if(!Result) { + if(caster->IsBot()) { + if(spells[spell_id].targettype == ST_Undead) { + if((GetBodyType() != BT_SummonedUndead) && (GetBodyType() != BT_Undead) && (GetBodyType() != BT_Vampire)) { + mlog(SPELLS__RESISTS, "Bot's target is not an undead."); + return true; + } + } + if(spells[spell_id].targettype == ST_Summoned) { + if((GetBodyType() != BT_SummonedUndead) + && (GetBodyType() != BT_Summoned) + && (GetBodyType() != BT_Summoned2) + && (GetBodyType() != BT_Summoned3) + ) { + mlog(SPELLS__RESISTS, "Bot's target is not a summoned creature."); + return true; + } + } + } + + mlog(SPELLS__RESISTS, "No bot immunities to spell %d found.", spell_id); + } + } + + return Result; +} + +bool Bot::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction) { + bool Result = false; + + SpellTargetType targetType = spells[spell_id].targettype; + + + // This is so PoK NPC Necro/Shd can create essence emeralds for pc's from perl scripts + if(targetType == ST_GroupClientAndPet) { + if(((spell_id == 1768) && (zone->GetZoneID() == 202)) || (!IsDetrimentalSpell(spell_id))) { + CastAction = SingleTarget; + return true; + } + } + + Result = Mob::DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction); + + return Result; +} + +bool Bot::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot) { + bool Result = false; + + if(GetClass() == BARD) { + // Bard bots casting time is interrupting thier melee + cast_time = 0; + } + + Result = Mob::DoCastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot); + + if(oSpellWillFinish) { + const SPDat_Spell_Struct &spell = spells[spell_id]; + *oSpellWillFinish = Timer::GetCurrentTime() + ((spell.recast_time > 20000) ? 10000 : spell.recast_time); + } + + return Result; +} + +int32 Bot::GenerateBaseManaPoints() +{ + // Now, we need to calc the base mana. + int32 bot_mana = 0; + int32 WisInt = 0; + int32 MindLesserFactor, MindFactor; + int wisint_mana = 0; + int base_mana = 0; + int ConvertedWisInt = 0; + + switch(GetCasterClass()) + { + case 'I': + WisInt = INT; + if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { + if(WisInt > 100) { + ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); + if(WisInt > 201) { + ConvertedWisInt -= ((WisInt - 201) * 5 / 4); + } + } + else { + ConvertedWisInt = WisInt; + } + if(GetLevel() < 41) { + wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000); + base_mana = (GetLevel() * 15); + } + else if(GetLevel() < 81) { + wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100)); + base_mana = (600 + ((GetLevel() - 40) * 30)); + } + else { + wisint_mana = (9 * ConvertedWisInt); + base_mana = (1800 + ((GetLevel() - 80) * 18)); + } + bot_mana = base_mana + wisint_mana; + } + else { + if((( WisInt - 199 ) / 2) > 0) { + MindLesserFactor = ( WisInt - 199 ) / 2; + } + else { + MindLesserFactor = 0; + } + MindFactor = WisInt - MindLesserFactor; + if(WisInt > 100) { + bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40); + } + else { + bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100); + } + } + break; + + case 'W': + WisInt = WIS; + if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { + if(WisInt > 100) { + ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); + if(WisInt > 201) { + ConvertedWisInt -= ((WisInt - 201) * 5 / 4); + } + } + else { + ConvertedWisInt = WisInt; + } + if(GetLevel() < 41) { + wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000); + base_mana = (GetLevel() * 15); + } + else if(GetLevel() < 81) { + wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100)); + base_mana = (600 + ((GetLevel() - 40) * 30)); + } + else { + wisint_mana = (9 * ConvertedWisInt); + base_mana = (1800 + ((GetLevel() - 80) * 18)); + } + bot_mana = base_mana + wisint_mana; + } + else { + if((( WisInt - 199 ) / 2) > 0) { + MindLesserFactor = ( WisInt - 199 ) / 2; + } + else { + MindLesserFactor = 0; + } + MindFactor = WisInt - MindLesserFactor; + if(WisInt > 100) { + bot_mana = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40); + } + else { + bot_mana = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100); + } + } + break; + + default: + bot_mana = 0; + break; + } + + max_mana = bot_mana; + + return bot_mana; +} + +void Bot::GenerateSpecialAttacks() +{ + // Special Attacks + if(((GetClass() == MONK) || (GetClass() == WARRIOR) || (GetClass() == RANGER) || (GetClass() == BERSERKER)) && (GetLevel() >= 60)) { + SpecAttacks[SPECATK_TRIPLE] = true; + } +} + +bool Bot::DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool& stopLogic) { + if(GetClass() == BARD) { + if(!ApplyNextBardPulse(bardsong, this, bardsong_slot)) { + InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong); + } + stopLogic = true; + } + + return true; +} + +bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool& stopLogic) { + if(spellTarget) { + if(IsGrouped() && (spellTarget->IsBot() || spellTarget->IsClient()) && RuleB(Bots, BotGroupBuffing)) { + //NPC *bot = this->CastToNPC(); + bool noGroupSpell = false; + uint16 thespell = spell_id; + + for(int i=0; i < AIspells.size(); i++) { + int j = BotGetSpells(i); + int spelltype = BotGetSpellType(i); + bool spellequal = (j == thespell); + bool spelltypeequal = ((spelltype == 2) || (spelltype == 16) || (spelltype == 32)); + bool spelltypetargetequal = ((spelltype == 8) && (spells[thespell].targettype == ST_Self)); + bool spelltypeclassequal = ((spelltype == 1024) && (GetClass() == SHAMAN)); + bool slotequal = (slot == USE_ITEM_SPELL_SLOT); + + // if it's a targeted heal or escape spell or pet spell or it's self only buff or self buff weapon proc, we only want to cast it once + if(spellequal || slotequal) { + if((spelltypeequal || spelltypetargetequal) || spelltypeclassequal || slotequal) { + // Don't let the Shaman canni themselves to death + if(((spells[thespell].effectid[0] == 0) && (spells[thespell].base[0] < 0)) && + (spellTarget->GetHP() < ((spells[thespell].base[0] * (-1)) + 100))) { + return false; + } + + SpellOnTarget(thespell, spellTarget); + noGroupSpell = true; + stopLogic = true; + } + } + } + + if(!noGroupSpell) { + Group *g = GetGroup(); + if(g) { + for(int i=0; imembers[i]) { + if((g->members[i]->GetClass() == NECROMANCER) && + (IsEffectInSpell(thespell, SE_AbsorbMagicAtt) || IsEffectInSpell(thespell, SE_Rune))) { + // don't cast this on necro's, their health to mana + // spell eats up the rune spell and it just keeps + // getting recast over and over + } + else + { + SpellOnTarget(thespell, g->members[i]); + } + if(g->members[i] && g->members[i]->GetPetID()) { + SpellOnTarget(thespell, g->members[i]->GetPet()); + } + } + } + SetMana(GetMana() - (GetActSpellCost(thespell, spells[thespell].mana) * (g->GroupCount() - 1))); + } + } + + stopLogic = true; + } + } + + return true; +} + +bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool& stopLogic) { + bool isMainGroupMGB = false; + + //if(GetBotRaidID() > 0) { + // BotRaids *br = entity_list.GetBotRaidByMob(this); + // if(br) { + // for(int n=0; nBotRaidGroups[0] && (br->BotRaidGroups[0]->members[n] == this)) { + // if(GetLevel() >= 59) // MGB AA + // isMainGroupMGB = true; + // break; + // } + // } + // } + //} + + if(isMainGroupMGB && (GetClass() != BARD)) { + Say("MGB %s", spells[spell_id].name); + SpellOnTarget(spell_id, this); + entity_list.AESpell(this, this, spell_id, true); + } + else { + Group *g = GetGroup(); + if(g) { + for(int i=0; imembers[i]) { + SpellOnTarget(spell_id, g->members[i]); + if(g->members[i] && g->members[i]->GetPetID()) { + SpellOnTarget(spell_id, g->members[i]->GetPet()); + } + } + } + } + } + + stopLogic = true; + + return true; +} + +void Bot::CalcBonuses() { + GenerateBaseStats(); + CalcItemBonuses(); + CalcSpellBonuses(&spellbonuses); + GenerateAABonuses(&aabonuses); + SetAttackTimer(); + + CalcATK(); + CalcSTR(); + CalcSTA(); + CalcDEX(); + CalcAGI(); + CalcINT(); + CalcWIS(); + CalcCHA(); + + CalcMR(); + CalcFR(); + CalcDR(); + CalcPR(); + CalcCR(); + CalcCorrup(); + + GenerateArmorClass(); + + CalcMaxHP(); + CalcMaxMana(); + CalcMaxEndurance(); + hp_regen = CalcHPRegen(); + mana_regen = CalcManaRegen(); + end_regen = CalcEnduranceRegen(); +} + +int32 Bot::CalcHPRegenCap(){ + int level = GetLevel(); + int32 hpregen_cap = 0; + hpregen_cap = RuleI(Character, ItemHealthRegenCap) + itembonuses.HeroicSTA/25; + + hpregen_cap += aabonuses.ItemHPRegenCap + spellbonuses.ItemHPRegenCap + itembonuses.ItemHPRegenCap; + + return (hpregen_cap * RuleI(Character, HPRegenMultiplier) / 100); +} + +int32 Bot::CalcManaRegenCap(){ + int32 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap; + switch(GetCasterClass()) + { + case 'I': + cap += (itembonuses.HeroicINT / 25); + break; + case 'W': + cap += (itembonuses.HeroicWIS / 25); + break; + } + + return (cap * RuleI(Character, ManaRegenMultiplier) / 100); +} + +// Return max stat value for level +int16 Bot::GetMaxStat() { + int level = GetLevel(); + int16 base = 0; + + if (level < 61) { + base = 255; + } + else if (GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= EQClientSoF) { + base = 255 + 5 * (level - 60); + } + else if (level < 71) { + base = 255 + 5 * (level - 60); + } + else { + base = 330; + } + + return(base); +} + +int16 Bot::GetMaxResist() { + int level = GetLevel(); + + int16 base = 500; + + if(level > 60) + base += ((level - 60) * 5); + + return base; +} + +int16 Bot::GetMaxSTR() { + return GetMaxStat() + + itembonuses.STRCapMod + + spellbonuses.STRCapMod + + aabonuses.STRCapMod; +} +int16 Bot::GetMaxSTA() { + return GetMaxStat() + + itembonuses.STACapMod + + spellbonuses.STACapMod + + aabonuses.STACapMod; +} +int16 Bot::GetMaxDEX() { + return GetMaxStat() + + itembonuses.DEXCapMod + + spellbonuses.DEXCapMod + + aabonuses.DEXCapMod; +} +int16 Bot::GetMaxAGI() { + return GetMaxStat() + + itembonuses.AGICapMod + + spellbonuses.AGICapMod + + aabonuses.AGICapMod; +} +int16 Bot::GetMaxINT() { + return GetMaxStat() + + itembonuses.INTCapMod + + spellbonuses.INTCapMod + + aabonuses.INTCapMod; +} +int16 Bot::GetMaxWIS() { + return GetMaxStat() + + itembonuses.WISCapMod + + spellbonuses.WISCapMod + + aabonuses.WISCapMod; +} +int16 Bot::GetMaxCHA() { + return GetMaxStat() + + itembonuses.CHACapMod + + spellbonuses.CHACapMod + + aabonuses.CHACapMod; +} +int16 Bot::GetMaxMR() { + return GetMaxResist() + + itembonuses.MRCapMod + + spellbonuses.MRCapMod + + aabonuses.MRCapMod; +} +int16 Bot::GetMaxPR() { + return GetMaxResist() + + itembonuses.PRCapMod + + spellbonuses.PRCapMod + + aabonuses.PRCapMod; +} +int16 Bot::GetMaxDR() { + return GetMaxResist() + + itembonuses.DRCapMod + + spellbonuses.DRCapMod + + aabonuses.DRCapMod; +} +int16 Bot::GetMaxCR() { + return GetMaxResist() + + itembonuses.CRCapMod + + spellbonuses.CRCapMod + + aabonuses.CRCapMod; +} +int16 Bot::GetMaxFR() { + return GetMaxResist() + + itembonuses.FRCapMod + + spellbonuses.FRCapMod + + aabonuses.FRCapMod; +} +int16 Bot::GetMaxCorrup() { + return GetMaxResist() + + itembonuses.CorrupCapMod + + spellbonuses.CorrupCapMod + + aabonuses.CorrupCapMod; +} + +int16 Bot::CalcSTR() { + int16 val = STR + itembonuses.STR + spellbonuses.STR; + + int16 mod = aabonuses.STR; + + if(val>255 && GetLevel() <= 60) + val = 255; + STR = val + mod; + + if(STR < 1) + STR = 1; + + int m = GetMaxSTR(); + if(STR > m) + STR = m; + + return(STR); +} + +int16 Bot::CalcSTA() { + int16 val = STA + itembonuses.STA + spellbonuses.STA; + + int16 mod = aabonuses.STA; + + if(val>255 && GetLevel() <= 60) + val = 255; + STA = val + mod; + + if(STA < 1) + STA = 1; + + int m = GetMaxSTA(); + if(STA > m) + STA = m; + + return(STA); +} + +int16 Bot::CalcAGI() { + int16 val = AGI + itembonuses.AGI + spellbonuses.AGI; + int16 mod = aabonuses.AGI; + + if(val>255 && GetLevel() <= 60) + val = 255; + + AGI = val + mod; + + if(AGI < 1) + AGI = 1; + + int m = GetMaxAGI(); + if(AGI > m) + AGI = m; + + return(AGI); +} + +int16 Bot::CalcDEX() { + int16 val = DEX + itembonuses.DEX + spellbonuses.DEX; + + int16 mod = aabonuses.DEX; + + if(val>255 && GetLevel() <= 60) + val = 255; + DEX = val + mod; + + if(DEX < 1) + DEX = 1; + + int m = GetMaxDEX(); + if(DEX > m) + DEX = m; + + return(DEX); +} + +int16 Bot::CalcINT() { + int16 val = INT + itembonuses.INT + spellbonuses.INT; + + int16 mod = aabonuses.INT; + + if(val>255 && GetLevel() <= 60) + val = 255; + INT = val + mod; + + if(INT < 1) + INT = 1; + int m = GetMaxINT(); + if(INT > m) + INT = m; + + return(INT); +} + +int16 Bot::CalcWIS() { + int16 val = WIS + itembonuses.WIS + spellbonuses.WIS; + + int16 mod = aabonuses.WIS; + + if(val>255 && GetLevel() <= 60) + val = 255; + WIS = val + mod; + + if(WIS < 1) + WIS = 1; + + int m = GetMaxWIS(); + if(WIS > m) + WIS = m; + + return(WIS); +} + +int16 Bot::CalcCHA() { + int16 val = CHA + itembonuses.CHA + spellbonuses.CHA; + + int16 mod = aabonuses.CHA; + + if(val>255 && GetLevel() <= 60) + val = 255; + CHA = val + mod; + + if(CHA < 1) + CHA = 1; + + int m = GetMaxCHA(); + if(CHA > m) + CHA = m; + + return(CHA); +} + +//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() +{ + MR += itembonuses.MR + spellbonuses.MR + aabonuses.MR; + + if(GetClass() == WARRIOR) + MR += GetLevel() / 2; + + if(MR < 1) + MR = 1; + + if(MR > GetMaxMR()) + MR = GetMaxMR(); + + return(MR); +} + +int16 Bot::CalcFR() +{ + int c = GetClass(); + if(c == RANGER) { + FR += 4; + + int l = GetLevel(); + if(l > 49) + FR += l - 49; + } + + FR += itembonuses.FR + spellbonuses.FR + aabonuses.FR; + + if(FR < 1) + FR = 1; + + if(FR > GetMaxFR()) + FR = GetMaxFR(); + + return(FR); +} + +int16 Bot::CalcDR() +{ + int c = GetClass(); + if(c == PALADIN) { + DR += 8; + + int l = GetLevel(); + if(l > 49) + DR += l - 49; + + } else if(c == SHADOWKNIGHT) { + DR += 4; + + int l = GetLevel(); + if(l > 49) + DR += l - 49; + } + + DR += itembonuses.DR + spellbonuses.DR + aabonuses.DR; + + if(DR < 1) + DR = 1; + + if(DR > GetMaxDR()) + DR = GetMaxDR(); + + return(DR); +} + +int16 Bot::CalcPR() +{ + int c = GetClass(); + if(c == ROGUE) { + PR += 8; + + int l = GetLevel(); + if(l > 49) + PR += l - 49; + + } else if(c == SHADOWKNIGHT) { + PR += 4; + + int l = GetLevel(); + if(l > 49) + PR += l - 49; + } + + PR += itembonuses.PR + spellbonuses.PR + aabonuses.PR; + + if(PR < 1) + PR = 1; + + if(PR > GetMaxPR()) + PR = GetMaxPR(); + + return(PR); +} + +int16 Bot::CalcCR() +{ + int c = GetClass(); + if(c == RANGER) { + CR += 4; + + int l = GetLevel(); + if(l > 49) + CR += l - 49; + } + + CR += itembonuses.CR + spellbonuses.CR + aabonuses.CR; + + if(CR < 1) + CR = 1; + + if(CR > GetMaxCR()) + CR = GetMaxCR(); + + return(CR); +} + +int16 Bot::CalcCorrup() +{ + Corrup = Corrup + itembonuses.Corrup + spellbonuses.Corrup + aabonuses.Corrup; + + if(Corrup > GetMaxCorrup()) + Corrup = GetMaxCorrup(); + + return(Corrup); +} + +int16 Bot::CalcATK() { + ATK = itembonuses.ATK + spellbonuses.ATK + aabonuses.ATK + GroupLeadershipAAOffenseEnhancement(); + return(ATK); +} + +void Bot::CalcRestState() { + + // This method calculates rest state HP and mana regeneration. + // The bot must have been out of combat for RuleI(Character, RestRegenTimeToActivate) seconds, + // must be sitting down, and must not have any detrimental spells affecting them. + // + if(!RuleI(Character, RestRegenPercent)) + return; + + RestRegenHP = RestRegenMana = RestRegenEndurance = 0; + + if(IsEngaged() || !IsSitting()) + return; + + if(!rest_timer.Check(false)) + return; + + uint32 buff_count = GetMaxTotalSlots(); + for (unsigned int j = 0; j < buff_count; j++) { + if(buffs[j].spellid != SPELL_UNKNOWN) { + if(IsDetrimentalSpell(buffs[j].spellid) && (buffs[j].ticsremaining > 0)) + if(!DetrimentalSpellAllowsRest(buffs[j].spellid)) + return; + } + } + + RestRegenHP = (GetMaxHP() * RuleI(Character, RestRegenPercent) / 100); + + RestRegenMana = (GetMaxMana() * RuleI(Character, RestRegenPercent) / 100); + + if(RuleB(Character, RestRegenEndurance)) + RestRegenEndurance = (GetMaxEndurance() * RuleI(Character, RestRegenPercent) / 100); +} + +int32 Bot::LevelRegen() +{ + int level = GetLevel(); + bool bonus = GetRaceBitmask(_baseRace) & RuleI(Character, BaseHPRegenBonusRaces); + uint8 multiplier1 = bonus ? 2 : 1; + int32 hp = 0; + + //these calculations should match up with the info from Monkly Business, which was last updated ~05/2008: http://www.monkly-business.net/index.php?pageid=abilities + if (level < 51) { + if (IsSitting()) { + if (level < 20) + hp += 2 * multiplier1; + else if (level < 50) + hp += 3 * multiplier1; + else //level == 50 + hp += 4 * multiplier1; + } + else //feigned or standing + hp += 1 * multiplier1; + } + //there may be an easier way to calculate this next part, but I don't know what it is + else { //level >= 51 + int32 tmp = 0; + float multiplier2 = 1; + if (level < 56) { + tmp = 2; + if (bonus) + multiplier2 = 3; + } + else if (level < 60) { + tmp = 3; + if (bonus) + multiplier2 = 3.34; + } + else if (level < 61) { + tmp = 4; + if (bonus) + multiplier2 = 3; + } + else if (level < 63) { + tmp = 5; + if (bonus) + multiplier2 = 2.8; + } + else if (level < 65) { + tmp = 6; + if (bonus) + multiplier2 = 2.67; + } + else { //level >= 65 + tmp = 7; + if (bonus) + multiplier2 = 2.58; + } + + hp += int32(float(tmp) * multiplier2); + } + + return hp; +} + +int32 Bot::CalcHPRegen() { + int32 regen = LevelRegen() + itembonuses.HPRegen + spellbonuses.HPRegen; + regen += aabonuses.HPRegen + GroupLeadershipAAHealthRegeneration(); + + regen = (regen * RuleI(Character, HPRegenMultiplier)) / 100; + return regen; +} + +int32 Bot::CalcManaRegen() +{ + uint8 level = GetLevel(); + uint8 botclass = GetClass(); + int32 regen = 0; + //this should be changed so we dont med while camping, etc... + if (IsSitting()) + { + BuffFadeBySitModifier(); + if(botclass != WARRIOR && botclass != MONK && botclass != ROGUE && botclass != BERSERKER) { + regen = (((GetSkill(MEDITATE) / 10) + (level - (level / 4))) / 4) + 4; + regen += spellbonuses.ManaRegen + itembonuses.ManaRegen; + } + else + regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen; + } + else { + regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen; + } + + if(GetCasterClass() == 'I') + regen += (itembonuses.HeroicINT / 25); + else if(GetCasterClass() == 'W') + regen += (itembonuses.HeroicWIS / 25); + else + regen = 0; + + regen += aabonuses.ManaRegen; + + regen = (regen * RuleI(Character, ManaRegenMultiplier)) / 100; + + float mana_regen_rate = RuleR(Bots, BotManaRegen); + if(mana_regen_rate < 0.0f) + mana_regen_rate = 0.0f; + + regen = regen * mana_regen_rate; // 90% of people wouldnt guess that manaregen would decrease the larger the number they input, this makes more sense + + return regen; +} + +// This is for calculating Base HPs + STA bonus for SoD or later clients. +uint32 Bot::GetClassHPFactor() { + + int factor; + + // Note: Base HP factor under level 41 is equal to factor / 12, and from level 41 to 80 is factor / 6. + // Base HP over level 80 is factor / 10 + // HP per STA point per level is factor / 30 for level 80+ + // HP per STA under level 40 is the level 80 HP Per STA / 120, and for over 40 it is / 60. + + switch(GetClass()) + { + case DRUID: + case ENCHANTER: + case NECROMANCER: + case MAGICIAN: + case WIZARD: + factor = 240; + break; + case BEASTLORD: + case BERSERKER: + case MONK: + case ROGUE: + case SHAMAN: + factor = 255; + break; + case BARD: + case CLERIC: + factor = 264; + break; + case SHADOWKNIGHT: + case PALADIN: + factor = 288; + break; + case RANGER: + factor = 276; + break; + case WARRIOR: + factor = 300; + break; + default: + factor = 240; + break; + } + return factor; +} + +int32 Bot::CalcMaxHP() { + int32 bot_hp = 0; + uint32 nd = 10000; + + bot_hp += GenerateBaseHitPoints() + itembonuses.HP; + + nd += aabonuses.MaxHP; //Natural Durability, Physical Enhancement, Planar Durability + + bot_hp = (float)bot_hp * (float)nd / (float)10000; //this is to fix the HP-above-495k issue + bot_hp += spellbonuses.HP + aabonuses.HP; + + bot_hp += GroupLeadershipAAHealthEnhancement(); + + bot_hp += bot_hp * (spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000; + max_hp = bot_hp; + + if (cur_hp > max_hp) + cur_hp = max_hp; + + int hp_perc_cap = spellbonuses.HPPercCap; + if(hp_perc_cap) { + int curHP_cap = (max_hp * hp_perc_cap) / 100; + if (cur_hp > curHP_cap) + cur_hp = curHP_cap; + } + + return max_hp; +} + +int32 Bot::CalcMaxEndurance() +{ + max_end = CalcBaseEndurance() + spellbonuses.Endurance + itembonuses.Endurance; + + if (max_end < 0) { + max_end = 0; + } + + if (cur_end > max_end) { + cur_end = max_end; + } + + int end_perc_cap = spellbonuses.EndPercCap; + if(end_perc_cap) { + int curEnd_cap = (max_end * end_perc_cap) / 100; + if (cur_end > curEnd_cap) + cur_end = curEnd_cap; + } + + return max_end; +} + +int32 Bot::CalcBaseEndurance() +{ + int32 base_end = 0; + int32 base_endurance = 0; + int32 ConvertedStats = 0; + int32 sta_end = 0; + int Stats = 0; + + if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { + int HeroicStats = 0; + + Stats = ((GetSTR() + GetSTA() + GetDEX() + GetAGI()) / 4); + HeroicStats = ((GetHeroicSTR() + GetHeroicSTA() + GetHeroicDEX() + GetHeroicAGI()) / 4); + + if (Stats > 100) { + ConvertedStats = (((Stats - 100) * 5 / 2) + 100); + if (Stats > 201) { + ConvertedStats -= ((Stats - 201) * 5 / 4); + } + } + else { + ConvertedStats = Stats; + } + + if (GetLevel() < 41) { + sta_end = (GetLevel() * 75 * ConvertedStats / 1000); + base_endurance = (GetLevel() * 15); + } + else if (GetLevel() < 81) { + sta_end = ((3 * ConvertedStats) + ((GetLevel() - 40) * 15 * ConvertedStats / 100)); + base_endurance = (600 + ((GetLevel() - 40) * 30)); + } + else { + sta_end = (9 * ConvertedStats); + base_endurance = (1800 + ((GetLevel() - 80) * 18)); + } + base_end = (base_endurance + sta_end + (HeroicStats * 10)); + } + else + { + Stats = GetSTR()+GetSTA()+GetDEX()+GetAGI(); + int LevelBase = GetLevel() * 15; + + int at_most_800 = Stats; + if(at_most_800 > 800) + at_most_800 = 800; + + int Bonus400to800 = 0; + int HalfBonus400to800 = 0; + int Bonus800plus = 0; + int HalfBonus800plus = 0; + + int BonusUpto800 = int( at_most_800 / 4 ) ; + if(Stats > 400) { + Bonus400to800 = int( (at_most_800 - 400) / 4 ); + HalfBonus400to800 = int( max( ( at_most_800 - 400 ), 0 ) / 8 ); + + if(Stats > 800) { + Bonus800plus = int( (Stats - 800) / 8 ) * 2; + HalfBonus800plus = int( (Stats - 800) / 16 ); + } + } + int bonus_sum = BonusUpto800 + Bonus400to800 + HalfBonus400to800 + Bonus800plus + HalfBonus800plus; + + base_end = LevelBase; + + //take all of the sums from above, then multiply by level*0.075 + base_end += ( bonus_sum * 3 * GetLevel() ) / 40; + } + return base_end; +} + +int32 Bot::CalcEnduranceRegen() { + int32 regen = int32(GetLevel() * 4 / 10) + 2; + regen += spellbonuses.EnduranceRegen + itembonuses.EnduranceRegen; + + return (regen * RuleI(Character, EnduranceRegenMultiplier) / 100); +} + +int32 Bot::CalcEnduranceRegenCap() { + int cap = (RuleI(Character, ItemEnduranceRegenCap) + itembonuses.HeroicSTR/25 + itembonuses.HeroicDEX/25 + itembonuses.HeroicAGI/25 + itembonuses.HeroicSTA/25); + + return (cap * RuleI(Character, EnduranceRegenMultiplier) / 100); +} + +void Bot::SetEndurance(int32 newEnd) +{ + /*Endurance can't be less than 0 or greater than max*/ + if(newEnd < 0) + newEnd = 0; + else if(newEnd > GetMaxEndurance()){ + newEnd = GetMaxEndurance(); + } + + cur_end = newEnd; +} + +void Bot::DoEnduranceUpkeep() { + int upkeep_sum = 0; + + int cost_redux = spellbonuses.EnduranceReduction + itembonuses.EnduranceReduction; + + 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) { + if(cost_redux > 0) { + if(upkeep <= cost_redux) + continue; //reduced to 0 + upkeep -= cost_redux; + } + if((upkeep+upkeep_sum) > GetEndurance()) { + //they do not have enough to keep this one going. + BuffFadeBySlot(buffs_i); + } else { + upkeep_sum += upkeep; + } + } + } + } + + if(upkeep_sum != 0) + SetEndurance(GetEndurance() - upkeep_sum); +} + +void Bot::Camp(bool databaseSave) { + Sit(); + + if(IsGrouped()) { + RemoveBotFromGroup(this, GetGroup()); + } + + if(GetInHealRotation()) { + GetHealRotationLeader()->RemoveHealRotationMember(this); + } + + if(databaseSave) + Save(); + + Depop(); +} + +void Bot::Zone() { + if(HasGroup()) { + GetGroup()->MemberZoned(this); + } + + Save(); + Depop(); +} + +bool Bot::IsArcheryRange(Mob *target) { + bool result = false; + + if(target) { + float range = GetBotArcheryRange() + 5.0; //Fudge it a little, client will let you hit something at 0 0 0 when you are at 205 0 0 + + range *= range; + + float targetDistance = DistNoRootNoZ(*target); + + float minRuleDistance = RuleI(Combat, MinRangedAttackDist) * RuleI(Combat, MinRangedAttackDist); + + if((targetDistance > range) || (targetDistance < minRuleDistance)) + result = false; + else + result = true; + } + + return result; +} + +bool Bot::IsBotCasterCombatRange(Mob *target) { + bool result = false; + + if(target) { + float range = BotAISpellRange; + + range *= range; + + // half the max so the bot doesn't always stop at max range to allow combat movement + range *= .5; + + float targetDistance = DistNoRootNoZ(*target); + + if(targetDistance > range) + result = false; + else + result = true; + } + + return result; +} + +bool Bot::IsGroupPrimaryHealer() { + bool result = false; + uint8 botclass = GetClass(); + + if(HasGroup()) { + Group *g = GetGroup(); + + switch(botclass) + { + case CLERIC: + { + result = true; + break; + } + case DRUID: + { + result = GroupHasClericClass(g) ? false : true; + break; + } + case SHAMAN: + { + result = (GroupHasClericClass(g) || GroupHasDruidClass(g)) ? false : true; + break; + } + case PALADIN: + case RANGER: + case BEASTLORD: + { + result = GroupHasPriestClass(g) ? false : true; + break; + } + default: + { + result = false; + break; + } + } + } + + return result; +} + +bool Bot::IsGroupPrimarySlower() { + bool result = false; + uint8 botclass = GetClass(); + + if(HasGroup()) { + Group *g = GetGroup(); + + switch(botclass) + { + case SHAMAN: + { + result = true; + break; + } + case ENCHANTER: + { + result = GroupHasShamanClass(g) ? false : true; + break; + } + case BEASTLORD: + { + result = (GroupHasShamanClass(g) || GroupHasEnchanterClass(g)) ? false : true; + break; + } + default: + { + result = false; + break; + } + } + } + + return result; +} + +bool Bot::CanHeal() { + bool result = false; + + if(!AI_HasSpells()) + return false; + + BotSpell botSpell; + botSpell.SpellId = 0; + botSpell.SpellIndex = 0; + botSpell.ManaCost = 0; + + botSpell = GetFirstBotSpellBySpellType(this, SpellType_Heal); + + if(botSpell.SpellId != 0){ + result = true; + } + + /*if(GetFirstBotSpellBySpellType(this, SpellType_Heal)){ + result = true; + }*/ + + return result; +} + +bool Bot::CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ) { + // 2.5625 is the inverse of 0.3902439. The only difference is in implementation. + // NOTE: You can not change just one of the constants below. They are the same number, just expressed inversly of each other. + // const float clientOverServerRatio = 2.5625f; + const float serverOverClientRatio = 0.3902439f; + + // Use this block if using 2.5625 as the ratio. + // const int clientAnimationMovementRateTypeMultiple = 8; + + // WildcardX: These are valid rates and observations based on painstaking testing of the client response to these values + // + // + // 0 * 8 = 0 : No Movement + // 1 * 8 = 8 : Death Walk + // 2 * 8 = 16 : Slow Walk + // 3 * 8 = 24 : Normal Walk + // 4 * 8 = 32 : Jog + // 5 * 8 = 40 : Normal Run + // 6 * 8 = 48 : Faster Run + // 7 * 8 = 56 : Even Faster Run + // 8 * 8 = 64 : Fastest Yet Run (Bard Song Speed?) + // 9 * 8 = 72 : Faster Fastest Yet Run + // 10 * 8 = 80 : .... you get the idea, this is pretty fast + // 11 * 8 = 88 : .... warp speed anyone? + // 12 * 8 = 96 : .... transwarp drive was invented by gnomes in Norrath + // 13 * 8 = 104 : ... who needs warp drives when you can just displace through time and space? + // + // + // You get the idea here with these... These seem to be "benchmark values" of animation movement and how fast + // the client thinks the Mob is moving so it can make it all look seemless between updates from the server. + // This chart is scalable by the client so you can pass an animation rate of 50 and get a "faster run" but not quite a "even faster run" + + // Convert the Bot movement rate to a value the client understands based on the chart above + // Use this block if using 2.5625 as the ratio. + // speed *= clientMovementRateTypeMultiple; + + + // This sets the movement animation rate with the client + // Use this block if using 2.5625 as the ratio. + // pRunAnimSpeed = speed; + pRunAnimSpeed = ((serverOverClientRatio * 10.0f) * speed) * 10.0f; + + // Now convert our "speed" from the value necessary for the client to animate the correct movement type rate to the server side speed + // Use this block if using 2.5625 as the ratio. + // speed *= serverOverClientRatio; + speed = pRunAnimSpeed / serverOverClientRatio; + + return MakeNewPositionAndSendUpdate(x, y, z, speed, checkZ); +} + +// Orders all bots in the specified group to follow their group leader. +void Bot::BotGroupOrderFollow(Group* group, Client* client) { + if(group && client) { + Mob* groupLeader = group->GetLeader(); + + if(groupLeader) { + for(int i = 0; i< MAX_GROUP_MEMBERS; i++) { + if(group->members[i] && group->members[i]->IsBot()) { + Bot* botGroupMember = group->members[i]->CastToBot(); + + if(botGroupMember && botGroupMember->GetBotOwnerCharacterID() == client->CharacterID()) { + if(group->IsLeader(botGroupMember) && botGroupMember->GetBotOwner()) { + botGroupMember->SetFollowID(botGroupMember->GetBotOwner()->GetID()); + if(botGroupMember->GetBotOwner()) + botGroupMember->Say("Following %s.", botGroupMember->GetBotOwner()->GetName()); + } + else { + botGroupMember->SetFollowID(groupLeader->GetID()); + botGroupMember->Say("Following %s.", groupLeader->GetCleanName()); + } + + botGroupMember->WipeHateList(); + + if(botGroupMember->HasPet() && botGroupMember->GetPet()) { + botGroupMember->GetPet()->WipeHateList(); + } + } + } + } + } + } +} + +// Orders all bots in the specified group to guard their current location. +void Bot::BotGroupOrderGuard(Group* group, Client* client) { + if(group && client) { + for(int i = 0; i< MAX_GROUP_MEMBERS; i++) { + if(group->members[i] && group->members[i]->IsBot()) { + Bot* botGroupMember = group->members[i]->CastToBot(); + + if(botGroupMember && botGroupMember->GetBotOwnerCharacterID() == client->CharacterID()) { + botGroupMember->SetFollowID(0); + botGroupMember->Say("Guarding here."); + + botGroupMember->WipeHateList(); + + if(botGroupMember->HasPet() && botGroupMember->GetPet()) { + botGroupMember->GetPet()->WipeHateList(); + } + } + } + } + } +} + +// Orders all bots in the specified group to attack their group leader's target. +void Bot::BotGroupOrderAttack(Group* group, Mob* target, Client* client) { + if(group && target) { + Mob* groupLeader = group->GetLeader(); + + if(groupLeader) { + for(int i=0; i < MAX_GROUP_MEMBERS; i++) { + if(group->members[i] && group->members[i]->IsBot()) { + Bot* botGroupMember = group->members[i]->CastToBot(); + + if(botGroupMember->GetBotOwnerCharacterID() == client->CharacterID()) { + botGroupMember->WipeHateList(); + botGroupMember->AddToHateList(target, 1); + + if(botGroupMember->HasPet() && botGroupMember->GetPet()) { + botGroupMember->GetPet()->WipeHateList(); + botGroupMember->GetPet()->AddToHateList(target, 1); + } + } + } + } + } + } +} + +// Summons all bot group members to ther owners location. +void Bot::BotGroupSummon(Group* group, Client* client) { + if(group) { + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(group->members[i] && group->members[i]->IsBot()) { + Bot* botMember = group->members[i]->CastToBot(); + + if(botMember->GetBotOwnerCharacterID() == client->CharacterID()) { + botMember->SetTarget(botMember->GetBotOwner()); + botMember->WipeHateList(); + botMember->Warp(botMember->GetBotOwner()->GetX(), botMember->GetBotOwner()->GetY(), botMember->GetBotOwner()->GetZ()); + + if(botMember->HasPet() && botMember->GetPet()) { + botMember->GetPet()->SetTarget(botMember); + botMember->GetPet()->WipeHateList(); + botMember->GetPet()->Warp(botMember->GetBotOwner()->GetX(), botMember->GetBotOwner()->GetY(), botMember->GetBotOwner()->GetZ()); + } + } + } + } + } +} + +// Finds a bot in the entitity list by bot owner character id and the bot first name +Bot* Bot::GetBotByBotClientOwnerAndBotName(Client* c, std::string botName) { + Bot* Result = 0; + + if(c) { + std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); + + if(!BotList.empty()) { + for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); botListItr++) { + if(std::string((*botListItr)->GetCleanName()) == botName) { + Result = (*botListItr); + break; + } + } + } + } + + return Result; +} + +// Processes a group invite from a Client for a Bot character. +void Bot::ProcessBotGroupInvite(Client* c, std::string botName) { + if(c) { + Bot* invitedBot = GetBotByBotClientOwnerAndBotName(c, botName); + + if(invitedBot && !invitedBot->HasGroup()) { + if(!c->IsGrouped()) { + Group *g = new Group(c); + if(AddBotToGroup(invitedBot, g)) { + entity_list.AddGroup(g); + database.SetGroupLeaderName(g->GetID(), c->GetName()); + g->SaveGroupLeaderAA(); + database.SetGroupID(c->GetName(), g->GetID(), c->CharacterID()); + database.SetGroupID(invitedBot->GetCleanName(), g->GetID(), invitedBot->GetBotID()); + } + } + else { + AddBotToGroup(invitedBot, c->GetGroup()); + database.SetGroupID(invitedBot->GetCleanName(), c->GetGroup()->GetID(), invitedBot->GetBotID()); + } + + /*if(c->GetBotRaidID() > 0) + invitedBot->SetBotRaidID(c->GetBotRaidID());*/ + } + // TODO: if there is a bot but the bot is already in a group, do we send an group invitation cancel message back to the client? + } +} + +// Processes a group disband request from a Client for a Bot. +void Bot::ProcessBotGroupDisband(Client* c, std::string botName) { + if(c) { + Bot* tempBot = 0; + + if(botName.empty()) + tempBot = GetFirstBotInGroup(c->GetGroup()); + else + tempBot = GetBotByBotClientOwnerAndBotName(c, botName); + + RemoveBotFromGroup(tempBot, c->GetGroup()); + } +} + +// Handles all client zone change event +void Bot::ProcessClientZoneChange(Client* botOwner) { + if(botOwner) { + std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(botOwner->CharacterID()); + + for(list::iterator itr = BotList.begin(); itr != BotList.end(); itr++) { + Bot* tempBot = *itr; + + if(tempBot) { + if(tempBot->HasGroup()) { + Group* g = tempBot->GetGroup(); + if(g && g->GetLeader()) { + Mob* tempGroupLeader = tempBot->GetGroup()->GetLeader(); + if(tempGroupLeader && tempGroupLeader->IsClient()) { + if(tempBot->GetBotOwnerCharacterID() == tempGroupLeader->CastToClient()->CharacterID()) + tempBot->Zone(); + else + tempBot->Camp(); + } + } + else + tempBot->Camp(); + } + else + tempBot->Camp(); + } + } + } +} + +// Finds and returns the first Bot object found in specified group +Bot* Bot::GetFirstBotInGroup(Group* group) { + Bot* Result = 0; + + if(group) { + for(int Counter = 0; Counter < MAX_GROUP_MEMBERS; Counter++) { + if (group->members[Counter] == NULL) { + continue; + } + + if(group->members[Counter]->IsBot()) { + Result = group->members[Counter]->CastToBot(); + break; + } + } + } + + return Result; +} + +// Processes a client request to inspect a bot's equipment. +void Bot::ProcessBotInspectionRequest(Bot* inspectedBot, Client* client) { + if(inspectedBot && client) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_InspectAnswer, sizeof(InspectResponse_Struct)); + InspectResponse_Struct* insr = (InspectResponse_Struct*) outapp->pBuffer; + insr->TargetID = inspectedBot->GetNPCTypeID(); + insr->playerid = inspectedBot->GetID(); + + const Item_Struct* item = 0; + const ItemInst* inst = 0; + + // Modded to display power source items (will only show up on SoF+ client inspect windows though.) + // I don't think bots are currently coded to use them..but, you'll have to use '#bot inventory list' + // to see them on a Titanium client when/if they are activated. -U + for(int16 L = 0; L <= 20; L++) { + inst = inspectedBot->GetBotItem(L); + + if(inst) { + item = inst->GetItem(); + if(item) { + strcpy(insr->itemnames[L], item->Name); + insr->itemicons[L] = item->Icon; + } + else + insr->itemicons[L] = 0xFFFFFFFF; + } + } + + inst = inspectedBot->GetBotItem(9999); + + if(inst) { + item = inst->GetItem(); + if(item) { + strcpy(insr->itemnames[21], item->Name); + insr->itemicons[21] = item->Icon; + } + else + insr->itemicons[21] = 0xFFFFFFFF; + } + + inst = inspectedBot->GetBotItem(21); + + if(inst) { + item = inst->GetItem(); + if(item) { + strcpy(insr->itemnames[22], item->Name); + insr->itemicons[22] = item->Icon; + } + else + insr->itemicons[22] = 0xFFFFFFFF; + } + + strcpy(insr->text, inspectedBot->GetInspectMessage().text); + + client->QueuePacket(outapp); // Send answer to requester + } +} + +void Bot::CalcItemBonuses() +{ + memset(&itembonuses, 0, sizeof(StatBonuses)); + const Item_Struct* itemtmp = 0; + + for(int i=0; i<=21; ++i) { + const ItemInst* item = GetBotItem(i); + if(item) { + for(int j=0; j<=4; ++j) { + const ItemInst* aug = item->GetAugment(j); + if(aug) { + itemtmp = aug->GetItem(); + if(itemtmp->AC != 0) + itembonuses.AC += itemtmp->AC; + if(itemtmp->HP != 0) + itembonuses.HP += itemtmp->HP; + if(itemtmp->Mana != 0) + itembonuses.Mana += itemtmp->Mana; + if(itemtmp->Endur != 0) + itembonuses.Endurance += itemtmp->Endur; + if(itemtmp->AStr != 0) + itembonuses.STR += itemtmp->AStr; + if(itemtmp->ASta != 0) + itembonuses.STA += itemtmp->ASta; + if(itemtmp->ADex != 0) + itembonuses.DEX += itemtmp->ADex; + if(itemtmp->AAgi != 0) + itembonuses.AGI += itemtmp->AAgi; + if(itemtmp->AInt != 0) + itembonuses.INT += itemtmp->AInt; + if(itemtmp->AWis != 0) + itembonuses.WIS += itemtmp->AWis; + if(itemtmp->ACha != 0) + itembonuses.CHA += itemtmp->ACha; + if(itemtmp->MR != 0) + itembonuses.MR += itemtmp->MR; + if(itemtmp->FR != 0) + itembonuses.FR += itemtmp->FR; + if(itemtmp->CR != 0) + itembonuses.CR += itemtmp->CR; + if(itemtmp->PR != 0) + itembonuses.PR += itemtmp->PR; + if(itemtmp->DR != 0) + itembonuses.DR += itemtmp->DR; + if(itemtmp->SVCorruption != 0) + itembonuses.Corrup += itemtmp->SVCorruption; + if(itemtmp->Regen != 0) + itembonuses.HPRegen += itemtmp->Regen; + if(itemtmp->ManaRegen != 0) + itembonuses.ManaRegen += itemtmp->ManaRegen; + if(itemtmp->Attack != 0) + itembonuses.ATK += itemtmp->Attack; + if(itemtmp->DamageShield != 0) + itembonuses.DamageShield += itemtmp->DamageShield; + if(itemtmp->SpellShield != 0) + itembonuses.SpellDamageShield += itemtmp->SpellShield; + if(itemtmp->Shielding != 0) + itembonuses.MeleeMitigation += itemtmp->Shielding; + if(itemtmp->StunResist != 0) + itembonuses.StunResist += itemtmp->StunResist; + if(itemtmp->StrikeThrough != 0) + itembonuses.StrikeThrough += itemtmp->StrikeThrough; + if(itemtmp->Avoidance != 0) + itembonuses.AvoidMeleeChance += itemtmp->Avoidance; + if(itemtmp->Accuracy != 0) + itembonuses.HitChance += itemtmp->Accuracy; + if(itemtmp->CombatEffects != 0) + itembonuses.ProcChance += itemtmp->CombatEffects; + if(itemtmp->Haste != 0) + if(itembonuses.haste < itemtmp->Haste) + itembonuses.haste = itemtmp->Haste; + if ((itemtmp->Worn.Effect != 0) && (itemtmp->Worn.Type == ET_WornEffect)) { // latent effects + ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses); + } + } + } + itemtmp = item->GetItem(); + if(itemtmp->AC != 0) + itembonuses.AC += itemtmp->AC; + if(itemtmp->HP != 0) + itembonuses.HP += itemtmp->HP; + if(itemtmp->Mana != 0) + itembonuses.Mana += itemtmp->Mana; + if(itemtmp->Endur != 0) + itembonuses.Endurance += itemtmp->Endur; + if(itemtmp->AStr != 0) + itembonuses.STR += itemtmp->AStr; + if(itemtmp->ASta != 0) + itembonuses.STA += itemtmp->ASta; + if(itemtmp->ADex != 0) + itembonuses.DEX += itemtmp->ADex; + if(itemtmp->AAgi != 0) + itembonuses.AGI += itemtmp->AAgi; + if(itemtmp->AInt != 0) + itembonuses.INT += itemtmp->AInt; + if(itemtmp->AWis != 0) + itembonuses.WIS += itemtmp->AWis; + if(itemtmp->ACha != 0) + itembonuses.CHA += itemtmp->ACha; + if(itemtmp->MR != 0) + itembonuses.MR += itemtmp->MR; + if(itemtmp->FR != 0) + itembonuses.FR += itemtmp->FR; + if(itemtmp->CR != 0) + itembonuses.CR += itemtmp->CR; + if(itemtmp->PR != 0) + itembonuses.PR += itemtmp->PR; + if(itemtmp->DR != 0) + itembonuses.DR += itemtmp->DR; + if(itemtmp->SVCorruption != 0) + itembonuses.Corrup += itemtmp->SVCorruption; + if(itemtmp->Regen != 0) + itembonuses.HPRegen += itemtmp->Regen; + if(itemtmp->ManaRegen != 0) + itembonuses.ManaRegen += itemtmp->ManaRegen; + if(itemtmp->Attack != 0) + itembonuses.ATK += itemtmp->Attack; + if(itemtmp->DamageShield != 0) + itembonuses.DamageShield += itemtmp->DamageShield; + if(itemtmp->SpellShield != 0) + itembonuses.SpellDamageShield += itemtmp->SpellShield; + if(itemtmp->Shielding != 0) + itembonuses.MeleeMitigation += itemtmp->Shielding; + if(itemtmp->StunResist != 0) + itembonuses.StunResist += itemtmp->StunResist; + if(itemtmp->StrikeThrough != 0) + itembonuses.StrikeThrough += itemtmp->StrikeThrough; + if(itemtmp->Avoidance != 0) + itembonuses.AvoidMeleeChance += itemtmp->Avoidance; + if(itemtmp->Accuracy != 0) + itembonuses.HitChance += itemtmp->Accuracy; + if(itemtmp->CombatEffects != 0) + itembonuses.ProcChance += itemtmp->CombatEffects; + if(itemtmp->Haste != 0) + if(itembonuses.haste < itemtmp->Haste) + itembonuses.haste = itemtmp->Haste; + if ((itemtmp->Worn.Effect != 0) && (itemtmp->Worn.Type == ET_WornEffect)) { // latent effects + ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses); + } + } + } + + if(itembonuses.HPRegen > CalcHPRegenCap()) + itembonuses.HPRegen = CalcHPRegenCap(); + + if(itembonuses.ManaRegen > CalcManaRegenCap()) + itembonuses.ManaRegen = CalcManaRegenCap(); +} + +// This method is intended to call all necessary methods to do all bot stat calculations, including spell buffs, equipment, AA bonsues, etc. +void Bot::CalcBotStats(bool showtext) { + if(!GetBotOwner()) + return; + + if(showtext) { + GetBotOwner()->Message(15, "Bot updating..."); + } + + if(!IsValidRaceClassCombo()) { + GetBotOwner()->Message(15, "A %s - %s bot was detected. Is this Race/Class combination allowed?.", GetRaceName(GetRace()), GetEQClassName(GetClass(), GetLevel())); + GetBotOwner()->Message(15, "Previous Bots Code releases did not check Race/Class combinations during create."); + GetBotOwner()->Message(15, "Unless you are experiencing heavy lag, you should delete and remake this bot."); + } + + if(GetBotOwner()->GetLevel() != GetLevel()) + SetLevel(GetBotOwner()->GetLevel()); + + GenerateSpecialAttacks(); + + if(showtext) { + GetBotOwner()->Message(15, "Base stats:"); + GetBotOwner()->Message(15, "Level: %i HP: %i AC: %i Mana: %i STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetLevel(), base_hp, AC, max_mana, STR, STA, DEX, AGI, INT, WIS, CHA); + GetBotOwner()->Message(15, "Resists-- Magic: %i, Poison: %i, Fire: %i, Cold: %i, Disease: %i, Corruption: %i.",MR,PR,FR,CR,DR,Corrup); + } + + /*if(this->Save()) + this->GetBotOwner()->CastToClient()->Message(0, "%s saved.", this->GetCleanName()); + else + this->GetBotOwner()->CastToClient()->Message(13, "%s save failed!", this->GetCleanName());*/ + + CalcBonuses(); + + AI_AddNPCSpells(this->GetBotSpellID()); + + if(showtext) { + GetBotOwner()->Message(15, "I'm updated."); + GetBotOwner()->Message(15, "Level: %i HP: %i AC: %i Mana: %i STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetLevel(), max_hp, GetAC(), max_mana, GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA()); + GetBotOwner()->Message(15, "Resists-- Magic: %i, Poison: %i, Fire: %i, Cold: %i, Disease: %i, Corruption: %i.",GetMR(),GetPR(),GetFR(),GetCR(),GetDR(),GetCorrup()); + } +} + +bool Bot::CheckLoreConflict(const Item_Struct* item) { + if (!item) + return false; + if (!(item->LoreFlag)) + return false; + + if (item->LoreGroup == -1) // Standard lore items; look everywhere except the shared bank, return the result + return (m_inv.HasItem(item->ID, 0, invWhereWorn) != SLOT_INVALID); + + //If the item has a lore group, we check for other items with the same group and return the result + return (m_inv.HasItemByLoreGroup(item->LoreGroup, invWhereWorn) != SLOT_INVALID); +} + +bool Bot::GroupHasClass(Group* group, uint8 classId) { + bool result = false; + + if(group) { + for(int counter = 0; counter < MAX_GROUP_MEMBERS; counter++) { + if(group->members[counter] && group->members[counter]->GetClass() & classId) { + result = true; + break; + } + } + } + + return result; +} + +void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { + // All bot command processing occurs here now instead of in command.cpp + + // TODO: Log any possible error messages as most of these will be MySQL error messages. + std::string TempErrorMessage; + + if(sep->arg[1][0] == '\0') { + c->Message(13, "Bad argument, type #bot help"); + return; + } + if(!strcasecmp( sep->arg[1], "help") && !strcasecmp( sep->arg[2], "\0")){ + c->Message(0, "List of commands availables for bots :"); + c->Message(0, "#bot help - show this"); + c->Message(0, "#bot create [name] [class (id)] [race (id)] [model (male/female)] - create a permanent bot. See #bot help create."); + c->Message(0, "#bot help create - show all the race/class id. (make it easier to create bots)"); + c->Message(0, "#bot delete - completely destroy forever the targeted bot and all its items."); + c->Message(0, "#bot list [all/class(1-16)] - list your bots all or by class. Classes: 1(Warrior), 2(Cleric), 3(Paladin), 4(Ranger), 5(Sk), 6(Druid), 7(Monk), 8(Bard), 9(Rogue), 10(Shaman), 11(Necro), 12(Wiz), 13(Mag), 14(Ench), 15(Beast), 16(Bersek)"); + c->Message(0, "#bot spawn [bot name] - spawn a bot from it's name (use list to see all the bots). "); + c->Message(0, "#bot inventory list - show the inventory (and the slots IDs) of the targetted bot."); + c->Message(0, "#bot inventory remove [slotid] - remove the item at the given slot in the inventory of the targetted bot."); + c->Message(0, "#bot update - you must type that command once you gain a level."); + c->Message(0, "#bot summon - It will summon your targeted bot to you."); + c->Message(0, "#bot ai mez - If you're grouped with an enchanter, he will mez your target."); + c->Message(0, "#bot picklock - You must have a targeted rogue bot in your group and be right on the door."); + c->Message(0, "#bot cure [poison|disease|curse|blindness] Cleric has most options"); + c->Message(0, "#bot bindme - You must have a Cleric in your group to get Bind Affinity cast on you."); + c->Message(0, "#bot track - look at mobs in the zone (ranger has options)"); + c->Message(0, "#bot target calm - attempts to pacify your target mob."); + c->Message(0, "#bot evac - transports your pc group to safe location in the current zone. bots are lost"); + c->Message(0, "#bot resurrectme - Your bot Cleric will rez you."); + c->Message(0, "#bot corpse summon - Necromancers summon corpse."); + c->Message(0, "#bot lore - cast Identify on the item on your mouse pointer."); + c->Message(0, "#bot sow - Bot sow on you (Druid has options)"); + c->Message(0, "#bot invis - Bot invisiblity (must have proper class in group)"); + c->Message(0, "#bot levitate - Bot levitation (must have proper class in group)"); + c->Message(0, "#bot resist - Bot resist buffs (must have proper class in group)"); + c->Message(0, "#bot runeme - Enchanter Bot cast Rune spell on you"); + c->Message(0, "#bot shrink - Shaman or Beastlord will shrink target"); + c->Message(0, "#bot endureb - Bot enduring breath (must have proper class in group)"); + c->Message(0, "#bot charm - (must have proper class in group)"); + c->Message(0, "#bot dire charm - (must have proper class in group)"); + c->Message(0, "#bot pet remove - (remove pet before charm)"); + c->Message(0, "#bot gate - you need a Druid or Wizard in your group)"); + c->Message(0, "#bot archery - Toggle Archery Skilled bots between using a Bow or using Melee weapons."); + c->Message(0, "#bot magepet [earth|water|air|fire|monster] - Select the pet type you want your Mage bot to use."); + c->Message(0, "#bot giveitem - Gives your targetted bot the item you have on your cursor."); + c->Message(0, "#bot augmentitem - Allows you to augment items for other classes. You must have the Augmentation Sealer window filled."); + c->Message(0, "#bot camp - Tells your bot to camp out of the game."); + c->Message(0, "#bot group help - Displays the commands available to manage any BOTs in your group."); + c->Message(0, "#bot botgroup help - Displays the commands available to manage BOT ONLY groups."); + c->Message(0, "#bot mana [ | all] - Displays a mana report for all your spawned bots."); + c->Message(0, "#bot setfollowdistance ### - sets target bots follow distance to ### (ie 30 or 250)."); + c->Message(0, "#bot [hair|haircolor|beard|beardcolor|face|eyes|heritage|tattoo|details ] - Change your BOTs appearance."); + c->Message(0, "#bot armorcolor - #bot help armorcolor for info"); + c->Message(0, "#bot taunt [on|off] - Turns taunt on/off for targeted bot"); + c->Message(0, "#bot stance [name] [stance (id)|list] - Sets/lists stance for named bot (Passive = 0, Balanced = 1, Efficient = 2, Reactive = 3, Aggressive = 4, Burn = 5, BurnAE = 6)"); + c->Message(0, "#bot groupmessages [on|off] [bot name|all] - Turns group messages on/off for named bot/all bots."); + c->Message(0, "#bot defensive [bot name] - Causes warrior or knight bot to use defensive discipline / buff."); + c->Message(0, "#bot healrotation help - Displays the commands available to manage BOT heal rotations."); + // TODO: + // c->Message(0, "#bot illusion - Enchanter Bot cast an illusion buff spell on you or your target."); + c->Message(0, "#bot pull [] [target] - Bot Pulling Target NPC's"); + c->Message(0, "#bot setinspectmessage - Copies your inspect message to a targeted bot that you own"); + return; + } + + // pull + if(!strcasecmp(sep->arg[1], "pull")) { + Mob *target = c->GetTarget(); + if(target == NULL || target == c || target->IsBot() || (target->IsPet() && target->GetOwner()->IsBot())) + { + c->Message(15, "You must select a monster"); + return; + } + + if(c->IsGrouped()) + { + bool haspuller = false; + Group *g = c->GetGroup(); + for(int i=0; imembers[i] && g->members[i]->IsBot() && !strcasecmp(g->members[i]->GetName() , sep->arg[2])) + { + haspuller = true; + Mob *puller = g->members[i]; + if (puller->CastToBot()->IsArcheryRange(target)) + { + puller->Say("Trying to Pull %s \n", target->GetCleanName()); + puller->CastToBot()->BotRangedAttack(target); + } + else { + puller->Say("Out of Range %s \n", target->GetCleanName()); + } + } + } + if(!haspuller) { + c->Message(15, "You must have an Puller in your group."); + } + } + return; + } + + // added Bot follow distance - SetFollowDistance + if(!strcasecmp(sep->arg[1], "setfollowdistance")) { + if((c->GetTarget() == NULL) || (c->GetTarget() == c) || (!c->GetTarget()->IsBot()) || (c->GetTarget()->CastToBot()->GetBotOwner() != c)) { + c->Message(15, "You must target a bot you own!"); + } + else { + uint32 BotFollowDistance = atoi(sep->arg[2]); + c->GetTarget()->SetFollowDistance(BotFollowDistance); + + } + + return; + } + + //bot armor colors + if(!strcasecmp(sep->arg[1], "armorcolor")) { + if(c->GetTarget() && c->GetTarget()->IsBot() && (c->GetTarget()->CastToBot()->GetBotOwner() == c)) { + + if(sep->arg[2][0] == '\0' || sep->arg[3][0] == '\0' || sep->arg[4][0] == '\0' || sep->arg[5][0] == '\0') { + c->Message(0, "Usage: #bot armorcolor [slot] [red] [green] [blue] - use #bot help armorcolor for info"); + return; + } + + 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; + + 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); + } + } + else { + c->Message(15, "You must target a bot you own to do this."); + } + return; + } + // Help for coloring bot armor + 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)"); + c->Message(0, "------------------------------------------------------------------"); + c->Message(0, "Color: [red] [green] [blue] (enter a number from 0-255 for each"); + c->Message(0, "------------------------------------------------------------------"); + c->Message(0, "Example: #bot armorcolor 17 0 255 0 - this would make the chest bright green"); + return; + } + + 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; + } + + if(!strcasecmp(sep->arg[1], "giveitem")) { + if(c->GetTarget() && c->GetTarget()->IsBot() && (c->GetTarget()->CastToBot()->GetBotOwner() == c)) { + // Its a bot targetted and this client is the bots owner + Bot* targetedBot = c->GetTarget()->CastToBot(); + if(targetedBot) + targetedBot->FinishTrade(c, BotTradeClientNoDropNoTrade); + } + else { + c->Message(15, "You must target a bot you own to do this."); + } + + return; + } + + if(!strcasecmp(sep->arg[1], "camp")) { + if(!strcasecmp(sep->arg[2], "all")) { + // Camp out all bots owned by this bot owner + BotOrderCampAll(c); + } + else { + // Camp only the targetted bot + if(c->GetTarget() && c->GetTarget()->IsBot() && (c->GetTarget()->CastToBot()->GetBotOwner()->CastToClient() == c)) { + Bot* targetedBot = c->GetTarget()->CastToBot(); + if(targetedBot) + targetedBot->Camp(); + } + else + c->Message(15, "You must target a bot you own to do this."); + } + + return; + } + + if(!strcasecmp(sep->arg[1], "create")) { + if(sep->arg[2][0] == '\0' || sep->arg[3][0] == '\0' || sep->arg[4][0] == '\0' || sep->arg[5][0] == '\0' || sep->arg[6][0] != '\0') { + c->Message(0, "Usage: #bot create [name] [class(id)] [race(id)] [gender (male/female)]"); + return; + } + else if(strcasecmp(sep->arg[3],"1") && strcasecmp(sep->arg[3],"2") && strcasecmp(sep->arg[3],"3") && strcasecmp(sep->arg[3],"4") && strcasecmp(sep->arg[3],"5") && strcasecmp(sep->arg[3],"6") && strcasecmp(sep->arg[3],"7") && strcasecmp(sep->arg[3],"8") && strcasecmp(sep->arg[3],"9") && strcasecmp(sep->arg[3],"10") && strcasecmp(sep->arg[3],"11") && strcasecmp(sep->arg[3],"12") && strcasecmp(sep->arg[3],"13") && strcasecmp(sep->arg[3],"14") && strcasecmp(sep->arg[3],"15") && strcasecmp(sep->arg[3],"16")) { + c->Message(0, "Usage: #bot create [name] [class(id)] [race(id)] [gender (male/female)]"); + return; + } + else if(strcasecmp(sep->arg[4],"1") && strcasecmp(sep->arg[4],"2") && strcasecmp(sep->arg[4],"3") && strcasecmp(sep->arg[4],"4") && strcasecmp(sep->arg[4],"5") && strcasecmp(sep->arg[4],"6") && strcasecmp(sep->arg[4],"7") && strcasecmp(sep->arg[4],"8") && strcasecmp(sep->arg[4],"9") && strcasecmp(sep->arg[4],"10") && strcasecmp(sep->arg[4],"11") && strcasecmp(sep->arg[4],"12") && strcasecmp(sep->arg[4],"330") && strcasecmp(sep->arg[4],"128") && strcasecmp(sep->arg[4],"130") && strcasecmp(sep->arg[4],"522")) { + c->Message(0, "Usage: #bot create [name] [class(1-16)] [race(1-12,128,130,330,522)] [gender (male/female)]"); + return; + } + else if(strcasecmp(sep->arg[5],"male") && strcasecmp(sep->arg[5],"female")) { + c->Message(0, "Usage: #bot create [name] [class(1-16)] [race(1-12,128,130,330,522)] [gender (male/female)]"); + return; + } + + uint32 MaxBotCreate = RuleI(Bots, CreateBotCount); + if(CreatedBotCount(c->CharacterID(), &TempErrorMessage) >= MaxBotCreate) { + c->Message(0, "You cannot create more than %i bots.", MaxBotCreate); + return; + } + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + int gender = 0; + if(!strcasecmp(sep->arg[5], "female")) + gender = 1; + + NPCType DefaultNPCTypeStruct = CreateDefaultNPCTypeStructForBot(std::string(sep->arg[2]), std::string(), c->GetLevel(), atoi(sep->arg[4]), atoi(sep->arg[3]), gender); + Bot* NewBot = new Bot(DefaultNPCTypeStruct, c); + + if(NewBot) { + if(!NewBot->IsValidRaceClassCombo()) { + c->Message(0, "That Race/Class combination cannot be created."); + return; + } + + if(!NewBot->IsValidName()) { + c->Message(0, "%s has invalid characters. You can use only the A-Z, a-z and _ characters in a bot name.", NewBot->GetCleanName()); + return; + } + + if(!NewBot->IsBotNameAvailable(&TempErrorMessage)) { + c->Message(0, "The name %s is already being used. Please choose a different name.", NewBot->GetCleanName()); + return; + } + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + // Now that all validation is complete, we can save our newly created bot + if(!NewBot->Save()) + c->Message(0, "Unable to save %s as a bot.", NewBot->GetCleanName()); + else + c->Message(0, "%s saved as bot %u.", NewBot->GetCleanName(), NewBot->GetBotID()); + } + else { + // TODO: Log error message here + } + + // Bot creation is complete + return; + } + + if(!strcasecmp(sep->arg[1], "help") && !strcasecmp(sep->arg[2], "create") ){ + c->Message(0, "Classes: 1(Warrior), 2(Cleric), 3(Paladin), 4(Ranger), 5(Sk), 6(Druid), 7(Monk), 8(Bard), 9(Rogue), 10(Shaman), 11(Necro), 12(Wiz), 13(Mag), 14(Ench), 15(Beast), 16(Bersek)"); + c->Message(0, "------------------------------------------------------------------"); + c->Message(0, "Races: 1(Human), 2(Barb), 3(Erudit), 4(Wood elf), 5(High elf), 6(Dark elf), 7(Half elf), 8(Dwarf), 9(Troll), 10(Ogre), 11(Halfling), 12(Gnome), 128(Iksar), 130(Vah shir), 330(Froglok), 522(Drakkin)"); + c->Message(0, "------------------------------------------------------------------"); + c->Message(0, "Usage: #bot create [name] [class(1-16)] [race(1-12,128,130,330,522)] [gender(male/female)]"); + c->Message(0, "Example: #bot create Sneaky 9 6 male"); + return; + } + + if(!strcasecmp(sep->arg[1], "delete") ) { + if((c->GetTarget() == NULL) || !c->GetTarget()->IsBot()) + { + c->Message(15, "You must target a bot!"); + return; + } + else if(c->GetTarget()->CastToBot()->GetBotOwnerCharacterID() != c->CharacterID()) + { + c->Message(15, "You can't delete a bot that you don't own."); + return; + } + + if(c->GetTarget()->IsBot()) { + Bot* BotTargeted = c->GetTarget()->CastToBot(); + + if(BotTargeted) { + BotTargeted->DeleteBot(&TempErrorMessage); + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + BotTargeted->Camp(false); + } + } + + return; + } + + if(!strcasecmp(sep->arg[1], "list")) { + bool listAll = true; + int iClass = atoi(sep->arg[2]); + + if(iClass > 0 && iClass < 17) + listAll = false; + + std::list AvailableBots = GetBotList(c->CharacterID(), &TempErrorMessage); + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + if(!AvailableBots.empty()) { + for(std::list::iterator TempAvailableBotsList = AvailableBots.begin(); TempAvailableBotsList != AvailableBots.end(); TempAvailableBotsList++) { + if(!listAll && TempAvailableBotsList->BotClass != iClass) + continue; + + c->Message(0, "Name: %s -- Class: %s -- Level: %u -- Race: %s", TempAvailableBotsList->BotName, ClassIdToString(TempAvailableBotsList->BotClass).c_str(), TempAvailableBotsList->BotLevel, RaceIdToString(TempAvailableBotsList->BotRace).c_str()); + } + } + else { + c->Message(0, "You have no bots created. Use the #bot create command to create a bot."); + } + } + + if(!strcasecmp(sep->arg[1], "mana")) { + bool listAll = false; + Bot* bot = 0; + + if(sep->argnum == 2) { + if(std::string(sep->arg[2]).compare("all") == 0) + listAll = true; + else { + string botName = std::string(sep->arg[2]); + + Bot* tempBot = entity_list.GetBotByBotName(botName); + + if(tempBot && tempBot->GetBotOwner() == c) { + bot = tempBot; + } + } + } + else { + if(c->GetTarget() && c->GetTarget()->IsBot()) + bot = c->GetTarget()->CastToBot(); + } + + if(bot && !listAll) { + // Specific bot only + if(bot->GetClass() != WARRIOR && bot->GetClass() != MONK && bot->GetClass() != BARD && bot->GetClass() != BERSERKER && bot->GetClass() != ROGUE) + c->Message(0, "Name: %s -- Class: %s -- Mana: %3.1f%%", bot->GetCleanName(), ClassIdToString(bot->GetClass()).c_str(), bot->GetManaRatio()); + } + else { + // List all + std::list spawnedBots = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); + + if(!spawnedBots.empty()) { + for(std::list::iterator botsListItr = spawnedBots.begin(); botsListItr != spawnedBots.end(); botsListItr++) { + Bot* tempBot = *botsListItr; + if(tempBot) { + if(tempBot->GetClass() != WARRIOR && tempBot->GetClass() != MONK && tempBot->GetClass() != BARD && tempBot->GetClass() != BERSERKER && tempBot->GetClass() != ROGUE) + c->Message(0, "Name: %s -- Class: %s -- Mana: %3.1f%%", tempBot->GetCleanName(), ClassIdToString(tempBot->GetClass()).c_str(), tempBot->GetManaRatio()); + } + } + } + else { + c->Message(0, "You have no spawned bots in this zone."); + } + } + + return; + } + + if(!strcasecmp(sep->arg[1], "spawn") ) { + uint32 botId = GetBotIDByBotName(std::string(sep->arg[2])); + + if(GetBotOwnerCharacterID(botId, &TempErrorMessage) != c->CharacterID()) { + c->Message(0, "You can't spawn a bot that you don't own."); + return; + } + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + if(c->GetFeigned()) { + c->Message(0, "You can't summon bots while you are feigned."); + return; + } + + /*if(c->GetBotRaidID() > 0) { + BotRaids *br = entity_list.GetBotRaidByMob(c->CastToMob()); + if(br) { + if(br->GetBotRaidAggro()) { + c->Message(15, "You can't summon bots while you are engaged."); + return; + } + } + }*/ + + if(c->IsGrouped()) { + Group *g = entity_list.GetGroupByClient(c); + for (int i=0; imembers[i] && !g->members[i]->qglobal && (g->members[i]->GetAppearance() != eaDead) + && (g->members[i]->IsEngaged() || (g->members[i]->IsClient() && g->members[i]->CastToClient()->GetAggroCount()))) { + c->Message(0, "You can't summon bots while you are engaged."); + return; + } + if(g && g->members[i] && g->members[i]->qglobal) { + return; + } + } + } + else { + if(c->GetAggroCount() > 0) { + c->Message(0, "You can't spawn bots while you are engaged."); + return; + } + } + + Mob* TempBotMob = entity_list.GetMobByBotID(botId); + + if(TempBotMob) { + c->Message(0, "This bot is already in the zone."); + return; + } + + int spawnedBotCount = SpawnedBotCount(c->CharacterID(), &TempErrorMessage); + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + if(RuleB(Bots, BotQuest) && !c->GetGM()) { + const int allowedBots = AllowedBotSpawns(c->CharacterID(), &TempErrorMessage); + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + if(allowedBots == 0) { + c->Message(0, "You cannot spawn any bots."); + return; + } + + if(spawnedBotCount >= allowedBots) { + c->Message(0, "You cannot spawn more than %i bots.", spawnedBotCount); + return; + } + + } + + if(spawnedBotCount >= RuleI(Bots, SpawnBotCount) && !c->GetGM()) { + c->Message(0, "You cannot spawn more than %i bots.", spawnedBotCount); + return; + } + + Bot* TempBot = LoadBot(botId, &TempErrorMessage); + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + safe_delete(TempBot); + return; + } + + if(TempBot) { + // We have a bot loaded from the database + TempBot->Spawn(c, &TempErrorMessage); + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + safe_delete(TempBot); + return; + } + + TempBot->CastToMob()->Say("I am ready for battle."); + } + else { + // We did not find a bot for the specified bot id from the database + c->Message(0, "BotID: %i not found", atoi(sep->arg[2])); + } + + return; + } + + if(!strcasecmp(sep->arg[1], "archery")) { + if((c->GetTarget() == NULL) || (c->GetTarget() == c) || !c->GetTarget()->IsBot()) { + c->Message(15, "You must target a bot!"); + return; + } + + Bot* archerBot = c->GetTarget()->CastToBot(); + + if(archerBot) { + if(archerBot->IsBotArcher()) + archerBot->SetBotArcher(false); + else + archerBot->SetBotArcher(true); + + archerBot->ChangeBotArcherWeapons(archerBot->IsBotArcher()); + + if(archerBot->GetClass() == RANGER && archerBot->GetLevel() >= 61) + archerBot->SetRangerAutoWeaponSelect(archerBot->IsBotArcher()); + } + + return; + } + + if(!strcasecmp(sep->arg[1], "picklock")) { + if((c->GetTarget() == NULL) || (c->GetTarget() == c) || !c->GetTarget()->IsBot() || (c->GetTarget()->GetClass() != ROGUE)) { + c->Message(15, "You must target a rogue bot!"); + } + else { + entity_list.BotPickLock(c->GetTarget()->CastToBot()); + } + + return; + } + + if(!strcasecmp(sep->arg[1], "summon")) { + if((c->GetTarget() == NULL) || (c->GetTarget() == c) || !c->GetTarget()->IsBot() || c->GetTarget()->IsPet()) + { + c->Message(15, "You must target a bot!"); + } + else if(c->GetTarget()->IsMob() && !c->GetTarget()->IsPet()) + { + Mob *b = c->GetTarget(); + if(b) + { + // Is our target "botable" ? + if(!b->IsBot()){ + c->Message(15, "You must target a bot!"); + } + else if((b->CastToBot()->GetBotOwnerCharacterID() != c->CharacterID())) + { + b->Say("You can only summon your own bots."); + } + else + { + b->SetTarget(c->CastToMob()); + b->Warp(c->GetX(), c->GetY(), c->GetZ()); + } + } + } + + return; + } + + if(!strcasecmp(sep->arg[1], "inventory") && !strcasecmp(sep->arg[2], "list")) { + if(c->GetTarget() != NULL) { + if(c->GetTarget()->IsBot() && c->GetTarget()->CastToBot()->GetBotOwnerCharacterID() == c->CharacterID()) { + Mob* b = c->GetTarget(); + int x = c->GetTarget()->CastToBot()->GetBotItemsCount(&TempErrorMessage); + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + const char* equipped[22] = {"Charm", "Left Ear", "Head", "Face", "Right Ear", "Neck", "Shoulders", "Arms", "Back", + "Left Wrist", "Right Wrist", "Range", "Hands", "Primary Hand", "Secondary Hand", + "Left Finger", "Right Finger", "Chest", "Legs", "Feet", "Waist", "Ammo" }; + const ItemInst* item1 = NULL; + const Item_Struct* item2 = NULL; + bool is2Hweapon = false; + for(int i=0; i<22; ++i) + { + if((i == 14) && is2Hweapon) { + continue; + } + + item1 = b->CastToBot()->GetBotItem(i); + if(item1) + item2 = item1->GetItem(); + else + item2 = NULL; + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + if(item2 == 0) { + c->Message(15, "I need something for my %s (Item %i)", equipped[i], i); + continue; + } + if((i == 13) && ((item2->ItemType == ItemType2HS) || (item2->ItemType == ItemType2HB) || (item2->ItemType == ItemType2HPierce))) { + is2Hweapon = true; + } + + char* itemLink = 0; + if((i == 0) || (i == 11) || (i == 13) || (i == 14) || (i == 21)) { + if (c->GetClientVersion() >= EQClientSoF) + { + MakeAnyLenString(&itemLink, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X", + 0, + item2->ID, + item1->GetAugmentItemID(0), + item1->GetAugmentItemID(1), + item1->GetAugmentItemID(2), + item1->GetAugmentItemID(3), + item1->GetAugmentItemID(4), + 0, + 0, + 0, + 0, + 0 + ); + c->Message(15, "Using %c%s%s%c in my %s (Item %i)", 0x12, itemLink, item2->Name, 0x12, equipped[i], i); + } + else + { + MakeAnyLenString(&itemLink, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%08X", + 0, + item2->ID, + item1->GetAugmentItemID(0), + item1->GetAugmentItemID(1), + item1->GetAugmentItemID(2), + item1->GetAugmentItemID(3), + item1->GetAugmentItemID(4), + 0, + 0, + 0, + 0); + c->Message(15, "Using %c%s%s%c in my %s (Item %i)", 0x12, itemLink, item2->Name, 0x12, equipped[i], i); + } + } + else { + if (c->GetClientVersion() >= EQClientSoF) + { + MakeAnyLenString(&itemLink, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X", + 0, + item2->ID, + item1->GetAugmentItemID(0), + item1->GetAugmentItemID(1), + item1->GetAugmentItemID(2), + item1->GetAugmentItemID(3), + item1->GetAugmentItemID(4), + 0, + 0, + 0, + 0, + 0 + ); + c->Message(15, "Using %c%s%s%c in my %s (Item %i)", 0x12, itemLink, item2->Name, 0x12, equipped[i], i); + } + else + { + MakeAnyLenString(&itemLink, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%08X", + 0, + item2->ID, + item1->GetAugmentItemID(0), + item1->GetAugmentItemID(1), + item1->GetAugmentItemID(2), + item1->GetAugmentItemID(3), + item1->GetAugmentItemID(4), + 0, + 0, + 0, + 0); + c->Message(15, "Using %c%s%s%c in my %s (Item %i)", 0x12, itemLink, item2->Name, 0x12, equipped[i], i); + } + } + } + } + else { + c->Message(15, "You must group your bot first."); + } + } + else { + c->Message(15, "You must target a bot first."); + } + return; + } + + if(!strcasecmp(sep->arg[1], "inventory") && !strcasecmp(sep->arg[2], "remove")) { + if((c->GetTarget() == NULL) || (sep->arg[3] == '\0') || !c->GetTarget()->IsBot()) + { + c->Message(15, "Usage: #bot inventory remove [slotid] (You must have a bot targetted) "); + return; + } + else if(c->GetTarget()->IsBot() && c->GetTarget()->CastToBot()->GetBotOwnerCharacterID() == c->CharacterID()) + { + if(c->GetTradeskillObject() || (c->trade->state == Trading)) + return; + + int slotId = atoi(sep->arg[3]); + if(slotId > 21 || slotId < 0) { + c->Message(15, "A bot has 21 slots in its inventory, please choose a slot between 0 and 21."); + return; + } + const char* equipped[22] = {"Charm", "Left Ear", "Head", "Face", "Right Ear", "Neck", "Shoulders", "Arms", "Back", + "Left Wrist", "Right Wrist", "Range", "Hands", "Primary Hand", "Secondary Hand", + "Left Finger", "Right Finger", "Chest", "Legs", "Feet", "Waist", "Ammo" }; + + const Item_Struct* itm = NULL; + const ItemInst* itminst = c->GetTarget()->CastToBot()->GetBotItem(slotId); + if(itminst) + itm = itminst->GetItem(); + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + // Don't allow the player to remove a lore item they already possess and cause a crash + bool failedLoreCheck = false; + if(itminst) { + for(int m=0; mGetAugment(m); + if(itma) + { + if(c->CheckLoreConflict(itma->GetItem())) { + failedLoreCheck = true; + } + } + } + if(c->CheckLoreConflict(itm)) { + failedLoreCheck = true; + } + } + if(!failedLoreCheck) { + if(itm) { + c->PushItemOnCursor(*itminst, true); + Bot *gearbot = c->GetTarget()->CastToBot(); + if((slotId == SLOT_RANGE)||(slotId == SLOT_AMMO)||(slotId == SLOT_PRIMARY)||(slotId == SLOT_SECONDARY)) { + gearbot->SetBotArcher(false); + } + gearbot->RemoveBotItemBySlot(slotId, &TempErrorMessage); + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + gearbot->BotRemoveEquipItem(slotId); + gearbot->CalcBotStats(); + switch(slotId) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 8: + case 9: + case 10: + case 11: + case 13: + case 14: + case 15: + case 16: + case 17: + case 20: + case 21: + gearbot->Say("My %s is now unequipped.", equipped[slotId]); + break; + case 6: + case 7: + case 12: + case 18: + case 19: + gearbot->Say("My %s are now unequipped.", equipped[slotId]); + break; + default: + break; + } + } + else { + switch(slotId) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 8: + case 9: + case 10: + case 11: + case 13: + case 14: + case 15: + case 16: + case 17: + case 20: + case 21: + c->GetTarget()->Say("My %s is already unequipped.", equipped[slotId]); + break; + case 6: + case 7: + case 12: + case 18: + case 19: + c->GetTarget()->Say("My %s are already unequipped.", equipped[slotId]); + break; + default: + break; + } + } + } + else { + c->Message_StringID(0, PICK_LORE); + } + } + return; + } + + if(!strcasecmp(sep->arg[1], "update")) { + // Congdar: add IsEngaged check for exploit to keep bots alive by repeatedly using #bot update. + if((c->GetTarget() != NULL) && c->GetTarget()->IsBot()) { + if(c->GetLevel() <= c->GetTarget()->GetLevel()) { + c->Message(15, "This bot has already been updated."); + return; + } + + if(c->IsGrouped()) + { + Group *g = entity_list.GetGroupByClient(c); + for (int i=0; imembers[i] && g->members[i]->IsEngaged()) + { + c->Message(15, "You can't update bots while you are engaged."); + return; + } + } + } + + if((c->GetTarget()->CastToBot()->GetBotOwner() == c->CastToMob()) && !c->GetFeigned()) { + Bot* bot = c->GetTarget()->CastToBot(); + //bot->SetLevel(c->GetLevel()); + bot->SetPetChooser(false); + bot->CalcBotStats(); + } + else { + if(c->GetFeigned()) { + c->Message(15, "You cannot update bots while feigned."); + } + else { + c->Message(15, "You must target your bot first"); + } + } + } + else { + c->Message(15, "You must target a bot first"); + } + + return; + } + + //Bind + if(!strcasecmp(sep->arg[1], "bindme")) { + Mob *binder = NULL; + bool hasbinder = false; + if(c->IsGrouped()) + { + Group *g = c->GetGroup(); + if(g) { + for(int i=0; imembers[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == CLERIC)) + { + hasbinder = true; + binder = g->members[i]; + } + } + if(!hasbinder) { + c->Message(15, "You must have a Cleric in your group."); + } + } + } + if(hasbinder) { + binder->Say("Attempting to bind you %s.", c->GetName()); + binder->CastToNPC()->CastSpell(35, c->GetID(), 1, -1, -1); + } + return; + } + + // Rune + if(!strcasecmp(sep->arg[1], "runeme")) { + Mob *runeer = NULL; + bool hasruneer = false; + if(c->IsGrouped()) + { + Group *g = c->GetGroup(); + if(g) { + for(int i=0; imembers[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == ENCHANTER)) + { + hasruneer = true; + runeer = g->members[i]; + } + } + if(!hasruneer) { + c->Message(15, "You must have an Enchanter in your group."); + } + } + } + if(hasruneer) { + if (c->GetLevel() <= 12) { + runeer->Say("I need to be level 13 or higher for this..."); + } + else if ((c->GetLevel() >= 13) && (c->GetLevel() <= 21)) { + runeer->Say("Casting Rune I..."); + runeer->CastSpell(481, c->GetID(), 1, -1, -1); + } + else if ((c->GetLevel() >= 22) && (c->GetLevel() <= 32)) { + runeer->Say("Casting Rune II..."); + runeer->CastSpell(482, c->GetID(), 1, -1, -1); + } + else if ((c->GetLevel() >= 33) && (c->GetLevel() <= 39)) { + runeer->Say("Casting Rune III..."); + runeer->CastSpell(483, c->GetID(), 1, -1, -1); + } + else if ((c->GetLevel() >= 40) && (c->GetLevel() <= 51)) { + runeer->Say("Casting Rune IV..."); + runeer->CastSpell(484, c->GetID(), 1, -1, -1); + } + else if ((c->GetLevel() >= 52) && (c->GetLevel() <= 60)) { + runeer->Say("Casting Rune V..."); + runeer->CastSpell(1689, c->GetID(), 1, -1, -1); + } + else if (c->GetLevel() >= 61){ + runeer->Say("Casting Rune of Zebuxoruk..."); + runeer->CastSpell(3343, c->GetID(), 1, -1, -1); + } + } + return; + } + + //Tracking + if(!strcasecmp(sep->arg[1], "track") && c->IsGrouped()) { + Mob *Tracker; + uint32 TrackerClass = 0; + + Group *g = c->GetGroup(); + if(g) { + for(int i=0; imembers[i] && g->members[i]->IsBot()) { + switch(g->members[i]->GetClass()) { + case RANGER: + Tracker = g->members[i]; + TrackerClass = RANGER; + break; + case DRUID: + // Unless we have a ranger, druid is next best. + if(TrackerClass != RANGER) { + Tracker = g->members[i]; + TrackerClass = DRUID; + } + break; + case BARD: + // If we haven't found a tracker yet, use bard. + if(TrackerClass == 0) { + Tracker = g->members[i]; + TrackerClass = BARD; + } + break; + default: + break; + } + } + } + + int Level = (c->GetLevel()); + int RangeR = (Level*80); //Ranger + int RangeD = (Level*30); //Druid + int RangeB = (Level*20); //Bard + switch(TrackerClass) { + case RANGER: + if(!strcasecmp(sep->arg[2], "all")) { + Tracker->Say("Tracking everything", c->GetName()); + entity_list.ShowSpawnWindow(c, RangeR, false); + } + else if(!strcasecmp(sep->arg[2], "rare")) { + Tracker->Say("Selective tracking", c->GetName()); + entity_list.ShowSpawnWindow(c, RangeR, true); + } + else if(!strcasecmp(sep->arg[2], "near")) { + Tracker->Say("Tracking mobs nearby", c->GetName()); + entity_list.ShowSpawnWindow(c, RangeD, false); + } + else + Tracker->Say("You want to [track all], [track near], or [track rare]?", c->GetName()); + + break; + + case BARD: + + if(TrackerClass != RANGER) + Tracker->Say("Tracking up", c->GetName()); + entity_list.ShowSpawnWindow(c, RangeB, false); + break; + + case DRUID: + + if(TrackerClass = BARD) + Tracker->Say("Tracking up", c->GetName()); + entity_list.ShowSpawnWindow(c, RangeD, false); + break; + + default: + c->Message(15, "You must have a Ranger, Druid, or Bard in your group."); + break; + } + } + } + + //Cure + if ((!strcasecmp(sep->arg[1], "cure")) && (c->IsGrouped())) { + Mob *Curer; + uint32 CurerClass = 0; + Group *g = c->GetGroup(); + if(g) { + for(int i=0; imembers[i] && g->members[i]->IsBot()) { + switch(g->members[i]->GetClass()) { + case CLERIC: + Curer = g->members[i]; + CurerClass = CLERIC; + break; + case SHAMAN: + if(CurerClass != CLERIC){ + Curer = g->members[i]; + CurerClass = SHAMAN; + } + case DRUID: + if (CurerClass == 0){ + Curer = g->members[i]; + CurerClass = DRUID; + } + break; + break; + default: + break; + } + } + } + switch(CurerClass) { + case CLERIC: + if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 1)) { + Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->Bot_Command_Cure(1, Curer->GetLevel()); + } + else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 4)) { + Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->Bot_Command_Cure(2, Curer->GetLevel()); + } + else if(!strcasecmp(sep->arg[2], "curse") && (c->GetLevel() >= 8)) { + Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->Bot_Command_Cure(3, Curer->GetLevel()); + } + else if(!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() >= 3)) { + Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->Bot_Command_Cure(4, Curer->GetLevel()); + } + else if (!strcasecmp(sep->arg[2], "curse") && (c->GetLevel() <= 8) + || !strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() <= 3) + || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 4) + || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 1)) { + Curer->Say("I don't have the needed level yet", sep->arg[2]); + } + else + Curer->Say("Do you want [cure poison], [cure disease], [cure curse], or [cure blindness]?", c->GetName()); + + break; + + case SHAMAN: + if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 2)) { + Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->Bot_Command_Cure(1, Curer->GetLevel()); + } + else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 1)) { + Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->Bot_Command_Cure(2, Curer->GetLevel()); + } + else if(!strcasecmp(sep->arg[2], "curse")) { + Curer->Say("I don't have that spell", sep->arg[2]); + } + else if(!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() >= 7)) { + Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->Bot_Command_Cure(4, Curer->GetLevel()); + } + else if (!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() <= 7) + || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 1) + || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 2)) { + Curer->Say("I don't have the needed level yet", sep->arg[2]); + } + else + Curer->Say("Do you want [cure poison], [cure disease], or [cure blindness]?", c->GetName()); + + break; + + case DRUID: + + if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 5)) { + Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->Bot_Command_Cure(1, Curer->GetLevel()); + } + else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 4)) { + Curer->Say("Trying to cure us of %s.", sep->arg[2]); + Curer->CastToBot()->Bot_Command_Cure(2, Curer->GetLevel()); + } + else if(!strcasecmp(sep->arg[2], "curse")) { // Fire level 1 + Curer->Say("I don't have that spell", sep->arg[2]); + } + else if(!strcasecmp(sep->arg[2], "blindness") && (c->GetLevel() >= 13)) { + Curer->Say("I don't have that spell", sep->arg[2]); + } + else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 4) + || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 5)) { + Curer->Say("I don't have the needed level yet", sep->arg[2]) ; + } + else + Curer->Say("Do you want [cure poison], or [cure disease]?", c->GetName()); + + break; + + default: + c->Message(15, "You must have a Cleric, Shaman, or Druid in your group."); + break; + } + } + } + + //Mez + if(!strcasecmp(sep->arg[1], "ai") && !strcasecmp(sep->arg[2], "mez")) + { + Mob *target = c->GetTarget(); + if(target == NULL || target == c || target->IsBot() || (target->IsPet() && target->GetOwner()->IsBot())) + { + c->Message(15, "You must select a monster"); + return; + } + + if(c->IsGrouped()) + { + bool hasmezzer = false; + Group *g = c->GetGroup(); + for(int i=0; imembers[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == ENCHANTER)) + { + hasmezzer = true; + Mob *mezzer = g->members[i]; + mezzer->Say("Trying to mez %s \n", target->GetCleanName()); + mezzer->CastToBot()->MesmerizeTarget(target); + } + } + if(!hasmezzer) { + c->Message(15, "You must have an Enchanter in your group."); + } + } + return; + } + + //Lore (Identify item) + if(!strcasecmp(sep->arg[1], "lore")) { + if(c->IsGrouped()) + { + bool hascaster = false; + Group *g = c->GetGroup(); + for(int i=0; imembers[i] && g->members[i]->IsBot()) { + uint8 casterlevel = g->members[i]->GetLevel(); + switch(g->members[i]->GetClass()) { + case ENCHANTER: + if(casterlevel >= 15) { + hascaster = true; + } + break; + case WIZARD: + if(casterlevel >= 14) { + hascaster = true; + } + break; + case NECROMANCER: + if(casterlevel >= 17) { + hascaster = true; + } + break; + case MAGICIAN: + if(casterlevel >= 13) { + hascaster = true; + } + break; + default: + break; + } + if(hascaster) { + g->members[i]->Say("Trying to Identify your item..."); + g->members[i]->CastSpell(305, c->GetID(), 1, -1, -1); + break; + } + } + } + if(!hascaster) { + c->Message(15, "You don't see anyone in your group that can cast Identify."); + } + } + else { + c->Message(15, "You don't see anyone in your group that can cast Identify."); + } + return; + } + + //Resurrect + if(!strcasecmp(sep->arg[1], "resurrectme")) + { + Mob *target = c->GetTarget(); + if(target == NULL || !target->IsCorpse()) + { + c->Message(15, "You must select a corpse"); + return; + } + + if(c->IsGrouped()) + { + bool hasrezzer = false; + Group *g = c->GetGroup(); + for(int i=0; imembers[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == CLERIC)) + { + hasrezzer = true; + Mob *rezzer = g->members[i]; + rezzer->Say("Trying to rez %s", target->GetCleanName()); + rezzer->CastToBot()->Bot_Command_RezzTarget(target); + break; + } + } + if(!hasrezzer) { + c->Message(15, "You must have a Cleric in your group."); + } + } + else { + c->Message(15, "You must have a Cleric in your group."); + } + return; + } + + if(!strcasecmp(sep->arg[1], "magepet")) + { + if(c->GetTarget() && c->GetTarget()->IsBot() && (c->GetTarget()->GetClass() == MAGICIAN)) + { + if(c->GetTarget()->CastToBot()->GetBotOwnerCharacterID() == c->CharacterID()) + { + int botlevel = c->GetTarget()->GetLevel(); + c->GetTarget()->CastToBot()->SetPetChooser(true); + if(botlevel == 1) + { + c->GetTarget()->Say("I don't have any pets yet."); + return; + } + if(!strcasecmp(sep->arg[2], "water")) + { + c->GetTarget()->CastToBot()->SetPetChooserID(0); + } + else if(!strcasecmp(sep->arg[2], "fire")) + { + if(botlevel < 3) + { + c->GetTarget()->Say("I don't have that pet yet."); + return; + } + else + { + c->GetTarget()->CastToBot()->SetPetChooserID(1); + } + } + else if(!strcasecmp(sep->arg[2], "air")) + { + if(botlevel < 4) + { + c->GetTarget()->Say("I don't have that pet yet."); + return; + } + else + { + c->GetTarget()->CastToBot()->SetPetChooserID(2); + } + } + else if(!strcasecmp(sep->arg[2], "earth")) + { + if(botlevel < 5) + { + c->GetTarget()->Say("I don't have that pet yet."); + return; + } + else + { + c->GetTarget()->CastToBot()->SetPetChooserID(3); + } + } + else if(!strcasecmp(sep->arg[2], "monster")) + { + if(botlevel < 30) + { + c->GetTarget()->Say("I don't have that pet yet."); + return; + } + else + { + c->GetTarget()->CastToBot()->SetPetChooserID(4); + } + } + if(c->GetTarget()->GetPet()) + { + // cast reclaim energy + uint16 id = c->GetTarget()->GetPetID(); + c->GetTarget()->SetPetID(0); + c->GetTarget()->CastSpell(331, id); + } + } + } + else + { + c->Message(15, "You must target your Magician bot."); + } + return; + } + + //Summon Corpse + if(!strcasecmp(sep->arg[1], "corpse") && !strcasecmp(sep->arg[2], "summon")) { + if(c->GetTarget() == NULL) { + c->Message(15, "You must select player with his corpse in the zone."); + return; + } + if(c->IsGrouped()) { + bool hassummoner = false; + Mob *t = c->GetTarget(); + Group *g = c->GetGroup(); + int summonerlevel = 0; + for(int i=0; imembers[i] && g->members[i]->IsBot() && ((g->members[i]->GetClass() == NECROMANCER)||(g->members[i]->GetClass() == SHADOWKNIGHT))) { + hassummoner = true; + summonerlevel = g->members[i]->GetLevel(); + g->members[i]->InterruptSpell(); + if(!t->IsClient()) { + g->members[i]->Say("You have to target a player with a corpse in the zone"); + return; + } + else { + g->members[i]->SetTarget(t); + + if(summonerlevel < 12) { + g->members[i]->Say("I don't have that spell yet."); + } + else if((summonerlevel > 11) && (summonerlevel < 35)) { + g->members[i]->Say("Attempting to summon %s\'s corpse.", t->GetCleanName()); + g->members[i]->CastSpell(2213, t->GetID(), 1, -1, -1); + return; + } + else if((summonerlevel > 34) && (summonerlevel < 71)) { + g->members[i]->Say("Attempting to summon %s\'s corpse.", t->GetCleanName()); + g->members[i]->CastSpell(3, t->GetID(), 1, -1, -1); + return; + } + else if(summonerlevel > 70) { + g->members[i]->Say("Attempting to summon %s\'s corpse.", t->GetCleanName()); + g->members[i]->CastSpell(10042, t->GetID(), 1, -1, -1); + return; + } + } + } + } + if (!hassummoner) { + c->Message(15, "You must have a Necromancer or Shadowknight in your group."); + } + return; + } + } + + //Pacify + if(!strcasecmp(sep->arg[1], "target") && !strcasecmp(sep->arg[2], "calm")) + { + Mob *target = c->GetTarget(); + + if(target == NULL || target->IsClient() || target->IsBot() || target->IsPet() && target->GetOwner()->IsBot()) + c->Message(15, "You must select a monster"); + else { + if(c->IsGrouped()) { + Group *g = c->GetGroup(); + + for(int i=0; imembers[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == ENCHANTER)) { + Bot *pacer = g->members[i]->CastToBot(); + pacer->Say("Trying to pacify %s \n", target->GetCleanName()); + + if(pacer->Bot_Command_CalmTarget(target)) { + if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_Calm)) + //if(pacer->IsPacified(target)) + c->Message(0, "I have successfully pacified %s.", target->GetCleanName()); + return; + /*else + c->Message(0, "I failed to pacify %s.", target->GetCleanName());*/ + } + else + c->Message(0, "I failed to pacify %s.", target->GetCleanName()); + } + // seperated cleric and chanter so chanter is primary + if(g && g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == CLERIC) && (GroupHasEnchanterClass(g) == false)) { + Bot *pacer = g->members[i]->CastToBot(); + pacer->Say("Trying to pacify %s \n", target->GetCleanName()); + + if(pacer->Bot_Command_CalmTarget(target)) { + if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_Calm)) + //if(pacer->IsPacified(target)) + c->Message(0, "I have successfully pacified %s.", target->GetCleanName()); + return; + /*else + c->Message(0, "I failed to pacify %s.", target->GetCleanName());*/ + } + else + c->Message(0, "I failed to pacify %s.", target->GetCleanName()); + } + /*else + c->Message(15, "You must have an Enchanter or Cleric in your group.");*/ + } + } + } + + return; + } + + //Charm + if(!strcasecmp(sep->arg[1], "charm")) + { + Mob *target = c->GetTarget(); + if(target == NULL || target->IsClient() || target->IsBot() || (target->IsPet() && target->GetOwner()->IsBot())) + { + c->Message(15, "You must select a monster"); + return; + } + uint32 DBtype = c->GetTarget()->GetBodyType(); + Mob *Charmer; + uint32 CharmerClass = 0; + Group *g = c->GetGroup(); + if(g) { + for(int i=0; imembers[i] && g->members[i]->IsBot()) { + switch(g->members[i]->GetClass()) { + case ENCHANTER: + Charmer = g->members[i]; + CharmerClass = ENCHANTER; + break; + case NECROMANCER: + if(CharmerClass != ENCHANTER){ + Charmer = g->members[i]; + CharmerClass = NECROMANCER; + } + case DRUID: + if (CharmerClass == 0){ + Charmer = g->members[i]; + CharmerClass = DRUID; + } + break; + break; + default: + break; + } + } + } + switch(CharmerClass) { + case ENCHANTER: + if (c->GetLevel() >= 11) { + Charmer->Say("Trying to charm %s \n", target->GetCleanName(), sep->arg[2]); + Charmer->CastToBot()->Bot_Command_CharmTarget (1,target); + } + else if (c->GetLevel() <= 10){ + Charmer->Say("I don't have the needed level yet", sep->arg[2]); + } + else + Charmer->Say("Mob level is too high or can't be charmed", c->GetName()); + break; + + case NECROMANCER: + if ((c->GetLevel() >= 18) && (DBtype == 3)) { + Charmer->Say("Trying to Charm %s \n", target->GetCleanName(), sep->arg[2]); + Charmer->CastToBot()->Bot_Command_CharmTarget (2,target); + } + else if (c->GetLevel() <= 17){ + Charmer->Say("I don't have the needed level yet", sep->arg[2]); + } + else + Charmer->Say("Mob Is not undead...", c->GetName()); + break; + + case DRUID: + if ((c->GetLevel() >= 13) && (DBtype == 21)) { + Charmer->Say("Trying to charm %s \n", target->GetCleanName(), sep->arg[2]); + Charmer->CastToBot()->Bot_Command_CharmTarget (3,target); + } + else if (c->GetLevel() <= 12){ + Charmer->Say("I don't have the needed level yet", sep->arg[2]); + } + else + Charmer->Say("Mob is not an animal...", c->GetName()); + break; + + default: + c->Message(15, "You must have an Enchanter, Necromancer or Druid in your group."); + break; + } + } + } + + // Remove Bot's Pet + if(!strcasecmp(sep->arg[1], "pet") && !strcasecmp(sep->arg[2], "remove")) { + if(c->GetTarget() != NULL) { + if (c->IsGrouped() && c->GetTarget()->IsBot() && (c->GetTarget()->CastToBot()->GetBotOwner() == c) && + ((c->GetTarget()->GetClass() == NECROMANCER) || (c->GetTarget()->GetClass() == ENCHANTER) || (c->GetTarget()->GetClass() == DRUID))) { + if(c->GetTarget()->CastToBot()->IsBotCharmer()) { + c->GetTarget()->CastToBot()->SetBotCharmer(false); + c->GetTarget()->Say("Using a summoned pet."); + } + else { + if(c->GetTarget()->GetPet()) + { + c->GetTarget()->GetPet()->Say_StringID(PET_GETLOST_STRING); + // c->GetTarget()->GetPet()->Kill(); + c->GetTarget()->GetPet()->Depop(false); + c->GetTarget()->SetPetID(0); + } + c->GetTarget()->CastToBot()->SetBotCharmer(true); + c->GetTarget()->Say("Available for Dire Charm command."); + } + } + else { + c->Message(15, "You must target your Enchanter, Necromancer, or Druid bot."); + } + } + else { + c->Message(15, "You must target an Enchanter, Necromancer, or Druid bot."); + } + return; + } + + //Dire Charm + if(!strcasecmp(sep->arg[1], "Dire") && !strcasecmp(sep->arg[2], "Charm")) + { + Mob *target = c->GetTarget(); + if(target == NULL || target->IsClient() || target->IsBot() || (target->IsPet() && target->GetOwner()->IsBot())) + { + c->Message(15, "You must select a monster"); + return; + } + uint32 DBtype = c->GetTarget()->GetBodyType(); + Mob *Direr; + uint32 DirerClass = 0; + Group *g = c->GetGroup(); + if(g) { + for(int i=0; imembers[i] && g->members[i]->IsBot()) { + switch(g->members[i]->GetClass()) { + case ENCHANTER: + Direr = g->members[i]; + DirerClass = ENCHANTER; + break; + case NECROMANCER: + if(DirerClass != ENCHANTER){ + Direr = g->members[i]; + DirerClass = NECROMANCER; + } + case DRUID: + if (DirerClass == 0){ + Direr = g->members[i]; + DirerClass = DRUID; + } + break; + break; + default: + break; + } + } + } + switch(DirerClass) { + case ENCHANTER: + if (c->GetLevel() >= 55) { + Direr->Say("Trying to dire charm %s \n", target->GetCleanName(), sep->arg[2]); + Direr->CastToBot()->Bot_Command_DireTarget (1,target); + } + else if (c->GetLevel() <= 55){ + Direr->Say("I don't have the needed level yet", sep->arg[2]); + } + else + Direr->Say("Mob level is too high or can't be charmed", c->GetName()); + break; + + case NECROMANCER: + if ((c->GetLevel() >= 55) && (DBtype == 3)) { + Direr->Say("Trying to dire charm %s \n", target->GetCleanName(), sep->arg[2]); + Direr->CastToBot()->Bot_Command_DireTarget (2,target); + } + else if (c->GetLevel() <= 55){ + Direr->Say("I don't have the needed level yet", sep->arg[2]); + } + else + Direr->Say("Mob Is not undead...", c->GetName()); + break; + + case DRUID: + if ((c->GetLevel() >= 55) && (DBtype == 21)) { + Direr->Say("Trying to dire charm %s \n", target->GetCleanName(), sep->arg[2]); + Direr->CastToBot()->Bot_Command_DireTarget (3,target); + } + else if (c->GetLevel() <= 55){ + Direr->Say("I don't have the needed level yet", sep->arg[2]); + } + else + Direr->Say("Mob is not an animal...", c->GetName()); + break; + + default: + c->Message(15, "You must have an Enchanter, Necromancer or Druid in your group."); + break; + } + } + } + + // Evacuate + if(!strcasecmp(sep->arg[1], "evac")) { + Mob *evac = NULL; + bool hasevac = false; + if(c->IsGrouped()) + { + Group *g = c->GetGroup(); + if(g) { + for(int i=0; imembers[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == DRUID)) + || (g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == WIZARD))) + { + hasevac = true; + evac = g->members[i]; + } + } + if(!hasevac) { + c->Message(15, "You must have a Druid in your group."); + } + } + } + if((hasevac) && (c->GetLevel() >= 18)) { + evac->Say("Attempting to Evac you %s.", c->GetName()); + evac->CastToClient()->CastSpell(2183, c->GetID(), 1, -1, -1); + } + else if((hasevac) && (c->GetLevel() <= 17)) { + evac->Say("I'm not level 18 yet.", c->GetName()); + } + return; + } + + // Sow + if ((!strcasecmp(sep->arg[1], "sow")) && (c->IsGrouped())) { + Mob *Sower; + uint32 SowerClass = 0; + Group *g = c->GetGroup(); + if(g) { + for(int i=0; imembers[i] && g->members[i]->IsBot()) { + switch(g->members[i]->GetClass()) { + case DRUID: + Sower = g->members[i]; + SowerClass = DRUID; + break; + case SHAMAN: + if (SowerClass != DRUID){ + Sower = g->members[i]; + SowerClass = SHAMAN; + } + break; + case RANGER: + if (SowerClass == 0){ + Sower = g->members[i]; + SowerClass = RANGER; + } + break; + case BEASTLORD: + if (SowerClass == 0){ + Sower = g->members[i]; + SowerClass = BEASTLORD; + } + break; + default: + break; + } + } + } + switch(SowerClass) { + case DRUID: + if ((!strcasecmp(sep->arg[2], "regular")) && (zone->CanCastOutdoor()) && (c->GetLevel() >= 10) ) { + Sower->Say("Casting sow..."); + Sower->CastSpell(278, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "regular")) && (zone->CanCastOutdoor()) && (c->GetLevel() <= 10) ) { + Sower->Say("I'm not level 10 yet."); + } + else if ((!strcasecmp(sep->arg[2], "wolf")) && zone->CanCastOutdoor() && (c->GetLevel() >= 20)) { + Sower->Say("Casting group wolf..."); + Sower->CastSpell(428, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "wolf")) && (c->GetLevel() <= 20)) { + Sower->Say("I'm not level 20 yet."); + } + else if ((!strcasecmp(sep->arg[2], "feral")) && (c->GetLevel() >= 50)) { + Sower->Say("Casting Feral Pack..."); + Sower->CastSpell(4058, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "feral")) && (c->GetLevel() <= 50)) { + Sower->Say("I'm not level 50 yet."); + } + else if ((!strcasecmp(sep->arg[2], "shrew")) && (c->GetLevel() >= 35)) { + Sower->Say("Casting Pack Shrew..."); + Sower->CastSpell(4055, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "wolf")) && (c->GetLevel() <= 35)) { + Sower->Say("I'm not level 35 yet."); + } + else if ((!zone->CanCastOutdoor()) && (!strcasecmp(sep->arg[2], "regular")) || + (!zone->CanCastOutdoor()) && (!strcasecmp(sep->arg[2], "wolf"))) { + Sower->Say("I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher,", c->GetName()); + } + else if (!zone->CanCastOutdoor()) { + Sower->Say("I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher,", c->GetName()); + } + else if (zone->CanCastOutdoor()) { + Sower->Say("Do you want [sow regular] or [sow wolf]?", c->GetName()); + } + else if (!zone->CanCastOutdoor()) { + Sower->Say("I can't cast this spell indoors, try [sow shrew] if you're 35 or higher, or [sow feral] if you're 50 or higher,", c->GetName()); + } + break; + + case SHAMAN: + + if ((zone->CanCastOutdoor()) && (c->GetLevel() >= 9)) { + Sower->Say("Casting SoW..."); + Sower->CastToClient()->CastSpell(278, c->GetID(), 1, -1, -1); + } + else if (!zone->CanCastOutdoor()) { + Sower->Say("I can't cast this spell indoors", c->GetName()); + } + else if (c->GetLevel() <= 9) { + Sower->Say("I'm not level 9 yet."); + } + break; + + case RANGER: + + if ((zone->CanCastOutdoor()) && (c->GetLevel() >= 28)){ + Sower->Say("Casting SoW..."); + Sower->CastToClient()->CastSpell(278, c->GetID(), 1, -1, -1); + } + else if (!zone->CanCastOutdoor()) { + Sower->Say("I can't cast this spell indoors", c->GetName()); + } + else if (c->GetLevel() <= 28) { + Sower->Say("I'm not level 28 yet."); + } + break; + + case BEASTLORD: + + if((zone->CanCastOutdoor()) && (c->GetLevel() >= 24)) { + Sower->Say("Casting SoW..."); + Sower->CastToClient()->CastSpell(278, c->GetID(), 1, -1, -1); + } + else if (!zone->CanCastOutdoor()) { + Sower->Say("I can't cast this spell indoors", c->GetName()); + } + else if (c->GetLevel() <= 24) { + Sower->Say("I'm not level 24 yet."); + } + break; + + + default: + c->Message(15, "You must have a Druid, Shaman, Ranger, or Beastlord in your group."); + break; + } + } + } + + // Shrink + if ((!strcasecmp(sep->arg[1], "shrink")) && (c->IsGrouped())) { + Mob *Shrinker; + uint32 ShrinkerClass = 0; + Group *g = c->GetGroup(); + Mob *target = c->GetTarget(); + + if(target == NULL || (!target->IsClient() && (c->GetTarget()->CastToBot()->GetBotOwner() != c))) + c->Message(15, "You must select a player or bot you own"); + + else if(g) { + for(int i=0; imembers[i] && g->members[i]->IsBot()) { + switch(g->members[i]->GetClass()) { + case SHAMAN: + Shrinker = g->members[i]; + ShrinkerClass = SHAMAN; + break; + case BEASTLORD: + if (ShrinkerClass != SHAMAN){ + Shrinker = g->members[i]; + ShrinkerClass = BEASTLORD; + } + break; + default: + break; + } + } + } + switch(ShrinkerClass) { + case SHAMAN: + + if (c->GetLevel() >= 15) { + Shrinker->Say("Casting Shrink..."); + Shrinker->CastToBot()->SpellOnTarget(345, target); + } + else if (c->GetLevel() <= 14) { + Shrinker->Say("I'm not level 15 yet."); + } + break; + + case BEASTLORD: + + if (c->GetLevel() >= 23) { + Shrinker->Say("Casting Shrink..."); + Shrinker->CastToBot()->SpellOnTarget(345, target); + } + else if (c->GetLevel() <= 22) { + Shrinker->Say("I'm not level 23 yet."); + } + break; + + default: + c->Message(15, "You must have a Shaman or Beastlord in your group."); + break; + } + } + } + + // Gate + if ((!strcasecmp(sep->arg[1], "gate")) && (c->IsGrouped())) { + Mob *Gater; + uint32 GaterClass = 0; + Group *g = c->GetGroup(); + if(g) { + for(int i=0; imembers[i] && g->members[i]->IsBot()) { + switch(g->members[i]->GetClass()) { + case DRUID: + Gater = g->members[i]; + GaterClass = DRUID; + break; + case WIZARD: + if (GaterClass == 0){ + Gater = g->members[i]; + GaterClass = WIZARD; + } + break; + default: + break; + } + } + } + switch(GaterClass) { + case DRUID: + if ((!strcasecmp(sep->arg[2], "karana")) && (c->GetLevel() >= 25) ) { + Gater->Say("Casting Circle of Karana..."); + Gater->CastSpell(550, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "commons")) && (c->GetLevel() >= 27)) { + Gater->Say("Casting Circle of Commons..."); + Gater->CastSpell(551, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "tox")) && (c->GetLevel() >= 25)) { + Gater->Say("Casting Circle of Toxxulia..."); + Gater->CastSpell(552, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "butcher")) && (c->GetLevel() >= 25)) { + Gater->Say("Casting Circle of Butcherblock..."); + Gater->CastSpell(553, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "lava")) && (c->GetLevel() >= 30)) { + Gater->Say("Casting Circle of Lavastorm..."); + Gater->CastSpell(554, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "ro")) && (c->GetLevel() >= 32)) { + Gater->Say("Casting Circle of Ro..."); + Gater->CastSpell(555, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "feerrott")) && (c->GetLevel() >= 32)) { + Gater->Say("Casting Circle of feerrott..."); + Gater->CastSpell(556, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "steamfont")) && (c->GetLevel() >= 31)) { + Gater->Say("Casting Circle of Steamfont..."); + Gater->CastSpell(557, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "misty")) && (c->GetLevel() >= 36)) { + Gater->Say("Casting Circle of Misty..."); + Gater->CastSpell(558, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "wakening")) && (c->GetLevel() >= 40)) { + Gater->Say("Casting Circle of Wakening Lands..."); + Gater->CastSpell(1398, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "iceclad")) && (c->GetLevel() >= 32)) { + Gater->Say("Casting Circle of Iceclad Ocean..."); + Gater->CastSpell(1434, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "divide")) && (c->GetLevel() >= 36)) { + Gater->Say("Casting Circle of The Great Divide..."); + Gater->CastSpell(1438, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "cobalt")) && (c->GetLevel() >= 42)) { + Gater->Say("Casting Circle of Cobalt Scar..."); + Gater->CastSpell(1440, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "combines")) && (c->GetLevel() >= 33)) { + Gater->Say("Casting Circle of The Combines..."); + Gater->CastSpell(1517, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "surefall")) && (c->GetLevel() >= 26)) { + Gater->Say("Casting Circle of Surefall Glade..."); + Gater->CastSpell(2020, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "grimling")) && (c->GetLevel() >= 29)) { + Gater->Say("Casting Circle of Grimling Forest..."); + Gater->CastSpell(2419, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "twilight")) && (c->GetLevel() >= 33)) { + Gater->Say("Casting Circle of Twilight..."); + Gater->CastSpell(2424, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "dawnshroud")) && (c->GetLevel() >= 37)) { + Gater->Say("Casting Circle of Dawnshroud..."); + Gater->CastSpell(2429, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "nexus")) && (c->GetLevel() >= 26)) { + Gater->Say("Casting Circle of The Nexus..."); + Gater->CastSpell(2432, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "pok")) && (c->GetLevel() >= 38)) { + Gater->Say("Casting Circle of Knowledge..."); + Gater->CastSpell(3184, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "stonebrunt")) && (c->GetLevel() >= 28)) { + Gater->Say("Casting Circle of Stonebrunt Mountains..."); + Gater->CastSpell(3792, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "bloodfields")) && (c->GetLevel() >= 55)) { + Gater->Say("Casting Circle of Bloodfields..."); + Gater->CastSpell(6184, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "emerald")) && (c->GetLevel() >= 39)) { + Gater->Say("Casting Wind of the South..."); + Gater->CastSpell(1737, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "skyfire")) && (c->GetLevel() >= 44)) { + Gater->Say("Casting Wind of the North..."); + Gater->CastSpell(1736, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "slaughter")) && (c->GetLevel() >= 64)) { + Gater->Say("Casting Circle of Slaughter..."); + Gater->CastSpell(6179, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "karana") + || !strcasecmp(sep->arg[2], "tox") + || !strcasecmp(sep->arg[2], "butcher") && (c->GetLevel() <= 25)) + || !strcasecmp(sep->arg[2], "commons") && (c->GetLevel() <= 27) + || (!strcasecmp(sep->arg[2], "ro") + || !strcasecmp(sep->arg[2], "feerrott") && (c->GetLevel() <= 32)) + || !strcasecmp(sep->arg[2], "steamfont") && (c->GetLevel() <= 31) + || !strcasecmp(sep->arg[2], "misty") && (c->GetLevel() <= 36) + || !strcasecmp(sep->arg[2], "lava") && (c->GetLevel() <= 30) + || !strcasecmp(sep->arg[2], "wakening") && (c->GetLevel() <= 40) + || !strcasecmp(sep->arg[2], "iceclad") && (c->GetLevel() <= 32) + || !strcasecmp(sep->arg[2], "divide") && (c->GetLevel() <= 38) + || !strcasecmp(sep->arg[2], "cobalt") && (c->GetLevel() <= 42) + || !strcasecmp(sep->arg[2], "combines") && (c->GetLevel() <= 33) + || !strcasecmp(sep->arg[2], "surefall") && (c->GetLevel() <= 26) + || !strcasecmp(sep->arg[2], "grimling") && (c->GetLevel() <= 29) + || !strcasecmp(sep->arg[2], "twilight") && (c->GetLevel() <= 33) + || !strcasecmp(sep->arg[2], "dawnshroud") && (c->GetLevel() <= 37) + || !strcasecmp(sep->arg[2], "nexus") && (c->GetLevel() <= 26) + || !strcasecmp(sep->arg[2], "pok") && (c->GetLevel() <= 38) + || !strcasecmp(sep->arg[2], "stonebrunt") && (c->GetLevel() <= 28) + || !strcasecmp(sep->arg[2], "bloodfields") && (c->GetLevel() <= 55) + || !strcasecmp(sep->arg[2], "emerald") && (c->GetLevel() <= 38) + || !strcasecmp(sep->arg[2], "skyfire") && (c->GetLevel() <= 43) + || !strcasecmp(sep->arg[2], "wos") && (c->GetLevel() <= 64)) { + Gater->Say("I don't have the needed level yet", sep->arg[2]); + } + else { + Gater->Say("With the proper level I can [gate] to [karana],[commons],[tox],[butcher],[lava],[ro],[feerrott],[steamfont],[misty],[wakening],[iceclad],[divide],[cobalt],[combines],[surefall],[grimling],[twilight],[dawnshroud],[nexus],[pok],[stonebrunt],[bloodfields],[emerald],[skyfire] or [wos].", c->GetName()); + } + break; + + case WIZARD: + + if ((!strcasecmp(sep->arg[2], "commons")) && (c->GetLevel() >= 35) ) { + Gater->Say("Casting Common Portal..."); + Gater->CastSpell(566, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "fay")) && (c->GetLevel() >= 27)) { + Gater->Say("Casting Fay Portal..."); + Gater->CastSpell(563, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "ro")) && (c->GetLevel() >= 37)) { + Gater->Say("Casting Ro Portal..."); + Gater->CastSpell(567, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "tox")) && (c->GetLevel() >= 25)) { + Gater->Say("Casting Toxxula Portal..."); + Gater->CastSpell(561, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "nk")) && (c->GetLevel() >= 27)) { + Gater->Say("Casting North Karana Portal..."); + Gater->CastSpell(562, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "nek")) && (c->GetLevel() >= 32)) { + Gater->Say("Casting Nektulos Portal..."); + Gater->CastSpell(564, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "wakening")) && (c->GetLevel() >= 43)) { + Gater->Say("Casting Wakening Lands Portal..."); + Gater->CastSpell(1399, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "iceclad")) && (c->GetLevel() >= 33)) { + Gater->Say("Casting Iceclad Ocean Portal..."); + Gater->CastSpell(1418, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "divide")) && (c->GetLevel() >= 36)) { + Gater->Say("Casting Great Divide Portal..."); + Gater->CastSpell(1423, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "cobalt")) && (c->GetLevel() >= 43)) { + Gater->Say("Casting Cobalt Scar Portal..."); + Gater->CastSpell(1425, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "combines")) && (c->GetLevel() >= 34)) { + Gater->Say("Casting Combines Portal..."); + Gater->CastSpell(1516, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "wk")) && (c->GetLevel() >= 27)) { + Gater->Say("Casting West Karana Portal..."); + Gater->CastSpell(568, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "twilight")) && (c->GetLevel() >= 27)) { + Gater->Say("Casting Twilight Portal..."); + Gater->CastSpell(2425, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "dawnshroud")) && (c->GetLevel() >= 27)) { + Gater->Say("Casting Dawnshroud Portal..."); + Gater->CastSpell(2430, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "nexus")) && (c->GetLevel() >= 29)) { + Gater->Say("Casting Nexus Portal..."); + Gater->CastSpell(2944, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "pok")) && (c->GetLevel() >= 27)) { + Gater->Say("Casting Plane of Knowledge Portal..."); + Gater->CastSpell(3180, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "wos")) && (c->GetLevel() >= 27)) { + Gater->Say("Casting Wall of Slaughter Portal..."); + Gater->CastSpell(6178, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "grimling")) && (c->GetLevel() >= 29)) { + Gater->Say("Casting Fay Portal..."); + Gater->CastSpell(2420, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "emerald")) && (c->GetLevel() >= 37)) { + Gater->Say("Porting to Emerald Jungle..."); + Gater->CastSpell(1739, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "hateplane")) && (c->GetLevel() >= 39)) { + Gater->Say("Porting to Hate Plane..."); + Gater->CastSpell(666, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "airplane")) && (c->GetLevel() >= 39)) { + Gater->Say("Porting to airplane..."); + Gater->CastSpell(674, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "skyfire")) && (c->GetLevel() >= 36)) { + Gater->Say("Porting to Skyfire..."); + Gater->CastSpell(1738, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "bloodfields")) && (c->GetLevel() >= 55)) { + Gater->Say("Casting Bloodfields Portal..."); + Gater->CastSpell(6183, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "stonebrunt")) && (c->GetLevel() >= 27)) { + Gater->Say("Casting Stonebrunt Portal..."); + Gater->CastSpell(3793, c->GetID(), 1, -1, -1); + } + else if ((!strcasecmp(sep->arg[2], "commons") && (c->GetLevel() <= 35)) + || !strcasecmp(sep->arg[2], "fay") && (c->GetLevel() <= 27) + || (!strcasecmp(sep->arg[2], "ro") && (c->GetLevel() <= 37)) + || !strcasecmp(sep->arg[2], "tox") && (c->GetLevel() <= 25) + || !strcasecmp(sep->arg[2], "nk") && (c->GetLevel() <= 25) + || !strcasecmp(sep->arg[2], "nek") && (c->GetLevel() <= 32) + || !strcasecmp(sep->arg[2], "wakening") && (c->GetLevel() <= 43) + || !strcasecmp(sep->arg[2], "iceclad") && (c->GetLevel() <= 33) + || !strcasecmp(sep->arg[2], "divide") && (c->GetLevel() <= 36) + || !strcasecmp(sep->arg[2], "cobalt") && (c->GetLevel() <= 43) + || !strcasecmp(sep->arg[2], "combines") && (c->GetLevel() <= 34) + || !strcasecmp(sep->arg[2], "wk") && (c->GetLevel() <= 37) + || !strcasecmp(sep->arg[2], "twilight") && (c->GetLevel() <= 33) + || !strcasecmp(sep->arg[2], "dawnshroud") && (c->GetLevel() <= 39) + || !strcasecmp(sep->arg[2], "nexus") && (c->GetLevel() <= 29) + || (!strcasecmp(sep->arg[2], "pok") + || !strcasecmp(sep->arg[2], "hateplane") + || !strcasecmp(sep->arg[2], "airplane") && (c->GetLevel() <= 38)) + || !strcasecmp(sep->arg[2], "grimling") && (c->GetLevel() <= 29) + || !strcasecmp(sep->arg[2], "bloodfields") && (c->GetLevel() <= 55) + || !strcasecmp(sep->arg[2], "stonebrunt") && (c->GetLevel() <= 27) + || !strcasecmp(sep->arg[2], "emerald") && (c->GetLevel() <= 36) + || !strcasecmp(sep->arg[2], "skyfire") && (c->GetLevel() <= 36) + || !strcasecmp(sep->arg[2], "wos") && (c->GetLevel() <= 64)) { + Gater->Say("I don't have the needed level yet", sep->arg[2]); + } + else { + Gater->Say("With the proper level I can [gate] to [commons],[fay],[ro],[tox],[nk],[wakening],[iceclad],[divide],[cobalt],[combines],[wk],[grimling],[twilight],[dawnshroud],[nexus],[pok],[stonebrunt],[bloodfields],[emerald],[skyfire],[hateplane],[airplane] or [wos].", c->GetName()); + } + break; + default: + c->Message(15, "You must have a Druid or Wizard in your group."); + break; + } + } + } + + //Endure Breath + if ((!strcasecmp(sep->arg[1], "endureb")) && (c->IsGrouped())) { + Mob *Endurer; + uint32 EndurerClass = 0; + Group *g = c->GetGroup(); + if(g) { + for(int i=0; imembers[i] && g->members[i]->IsBot()) { + switch(g->members[i]->GetClass()) { + case DRUID: + Endurer = g->members[i]; + EndurerClass = DRUID; + break; + case SHAMAN: + if (EndurerClass != DRUID){ + Endurer = g->members[i]; + EndurerClass = SHAMAN; + } + break; + case ENCHANTER: + if(EndurerClass == 0){ + Endurer = g->members[i]; + EndurerClass = ENCHANTER; + } + break; + case RANGER: + if(EndurerClass == 0) { + Endurer = g->members[i]; + EndurerClass = RANGER; + } + break; + case BEASTLORD: + if(EndurerClass == 0) { + Endurer = g->members[i]; + EndurerClass = BEASTLORD; + } + break; + default: + break; + } + } + } + switch(EndurerClass) { + case DRUID: + if (c->GetLevel() < 6) { + Endurer->Say("I'm not level 6 yet."); + } + else { + Endurer->Say("Casting Enduring Breath..."); + Endurer->CastSpell(86, c->GetID(), 1, -1, -1); + break; + } + break; + case SHAMAN: + if (c->GetLevel() < 12) { + Endurer->Say("I'm not level 12 yet."); + } + else { + Endurer->Say("Casting Enduring Breath..."); + Endurer->CastSpell(86, c->GetID(), 1, -1, -1); + } + break; + case RANGER: + if (c->GetLevel() < 20) { + Endurer->Say("I'm not level 20 yet."); + } + else { + Endurer->Say("Casting Enduring Breath..."); + Endurer->CastSpell(86, c->GetID(), 1, -1, -1); + } + break; + case ENCHANTER: + if (c->GetLevel() < 12) { + Endurer->Say("I'm not level 12 yet."); + } + else { + Endurer->Say("Casting Enduring Breath..."); + Endurer->CastSpell(86, c->GetID(), 1, -1, -1); + } + break; + case BEASTLORD: + if (c->GetLevel() < 25) { + Endurer->Say("I'm not level 25 yet."); + } + else { + Endurer->Say("Casting Enduring Breath..."); + Endurer->CastSpell(86, c->GetID(), 1, -1, -1); + } + break; + default: + c->Message(15, "You must have a Druid, Shaman, Ranger, Enchanter, or Beastlord in your group."); + break; + } + } + } + + //Invisible + if ((!strcasecmp(sep->arg[1], "invis")) && (c->IsGrouped())) { + Mob *Inviser; + uint32 InviserClass = 0; + Group *g = c->GetGroup(); + if(g) { + for(int i=0; imembers[i] && g->members[i]->IsBot()) { + switch(g->members[i]->GetClass()) { + case ENCHANTER: + Inviser = g->members[i]; + InviserClass = ENCHANTER; + break; + case MAGICIAN: + if (InviserClass != ENCHANTER){ + Inviser = g->members[i]; + InviserClass = MAGICIAN; + } + break; + case WIZARD: + if((InviserClass != ENCHANTER) && (InviserClass != MAGICIAN)){ + Inviser = g->members[i]; + InviserClass = WIZARD; + } + break; + case NECROMANCER: + if(InviserClass == 0){ + Inviser = g->members[i]; + InviserClass = NECROMANCER; + } + break; + case DRUID: + if((InviserClass != ENCHANTER) && (InviserClass != WIZARD) + || (InviserClass != MAGICIAN)){ + Inviser = g->members[i]; + InviserClass = DRUID; + } + break; + default: + break; + } + } + } + switch(InviserClass) { + case ENCHANTER: + if ((c->GetLevel() <= 14) && (!strcasecmp(sep->arg[2], "undead"))) { + Inviser->Say("I'm not level 14 yet."); + } + else if ((!c->IsInvisible(c)) && (!c->invisible_undead) && (c->GetLevel() >= 14) && (!strcasecmp(sep->arg[2], "undead"))) { + Inviser->Say("Casting invis undead..."); + Inviser->CastSpell(235, c->GetID(), 1, -1, -1); + } + else if ((c->GetLevel() <= 4) && (!strcasecmp(sep->arg[2], "live"))) { + Inviser->Say("I'm not level 4 yet."); + } + else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 4) && (!strcasecmp(sep->arg[2], "live"))) { + Inviser->Say("Casting invisibilty..."); + Inviser->CastSpell(42, c->GetID(), 1, -1, -1); + } + else if ((c->GetLevel() <= 6) && (!strcasecmp(sep->arg[2], "see"))) { + Inviser->Say("I'm not level 6 yet."); + } + else if ((c->GetLevel() >= 6) && (!strcasecmp(sep->arg[2], "see"))) { + Inviser->Say("Casting see invisible..."); + Inviser->CastSpell(80, c->GetID(), 1, -1, -1); + } + else if ((c->IsInvisible(c)) || (c->invisible_undead)) { + Inviser->Say("I can't cast this if you're already invis-buffed..."); + } + else { + Inviser->Say("Do you want [invis undead], [invis live] or [invis see] ?", c->GetName()); + } + break; + case MAGICIAN: + if (!strcasecmp(sep->arg[2], "undead")) { + Inviser->Say("I don't have that spell."); + } + else if ((c->GetLevel() <= 8) && (!strcasecmp(sep->arg[2], "live"))) { + Inviser->Say("I'm not level 8 yet."); + } + else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 8) && (!strcasecmp(sep->arg[2], "live"))) { + Inviser->Say("Casting invisibilty..."); + Inviser->CastSpell(42, c->GetID(), 1, -1, -1); + } + else if ((c->GetLevel() <= 16) && (!strcasecmp(sep->arg[2], "see"))) { + Inviser->Say("I'm not level 16 yet."); + } + else if ((c->GetLevel() >= 16) && (!strcasecmp(sep->arg[2], "see"))) { + Inviser->Say("Casting see invisible..."); + Inviser->CastSpell(80, c->GetID(), 1, -1, -1); + } + else if ((c->IsInvisible(c)) || (c->invisible_undead)) { + Inviser->Say("I can't cast this if you're already invis-buffed..."); + } + else { + Inviser->Say("Do you want [invis live] or [invis see] ?", c->GetName()); + } + break; + case WIZARD: + if ((c->GetLevel() <= 39) && (!strcasecmp(sep->arg[2], "undead"))) { + Inviser->Say("I'm not level 39 yet."); + } + else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 39) && (!strcasecmp(sep->arg[2], "undead"))) { + Inviser->Say("Casting invis undead..."); + Inviser->CastSpell(235, c->GetID(), 1, -1, -1); + } + else if ((c->GetLevel() <= 16) && (!strcasecmp(sep->arg[2], "live"))) { + Inviser->Say("I'm not level 16 yet."); + } + else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 16) && (!strcasecmp(sep->arg[2], "live"))) { + Inviser->Say("Casting invisibilty..."); + Inviser->CastSpell(42, c->GetID(), 1, -1, -1); + } + else if ((c->GetLevel() <= 4) && (!strcasecmp(sep->arg[2], "see"))) { + Inviser->Say("I'm not level 6 yet."); + } + else if ((c->GetLevel() >= 4) && (!strcasecmp(sep->arg[2], "see"))) { + Inviser->Say("Casting see invisible..."); + Inviser->CastSpell(80, c->GetID(), 1, -1, -1); + } + else if ((c->IsInvisible(c)) || (c->invisible_undead)) { + Inviser->Say("I can't cast this if you're already invis-buffed..."); + } + else { + Inviser->Say("Do you want [invis undead], [invis live] or [invis see] ?", c->GetName()); + } + break; + case NECROMANCER: + if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (!strcasecmp(sep->arg[2], "undead"))) { + Inviser->Say("Casting invis undead..."); + Inviser->CastSpell(235, c->GetID(), 1, -1, -1); + } + else if (!strcasecmp(sep->arg[2], "see")) { + Inviser->Say("I don't have that spell..."); + } + else if (!strcasecmp(sep->arg[2], "live")) { + Inviser->Say("I don't have that spell..."); + } + else if ((c->IsInvisible(c))|| (c->invisible_undead)) { + Inviser->Say("I can't cast this if you're already invis-buffed..."); + } + else { + Inviser->Say("I only have [invis undead]", c->GetName()); + } + break; + case DRUID: + if (!strcasecmp(sep->arg[2], "undead")) { + Inviser->Say("I don't have that spell..."); + } + else if ((c->GetLevel() <= 4) && (!strcasecmp(sep->arg[2], "live"))) { + Inviser->Say("I'm not level 4 yet."); + } + else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 18) && (!strcasecmp(sep->arg[2], "live"))) { + Inviser->Say("Casting Superior Camouflage..."); + Inviser->CastSpell(34, c->GetID(), 1, -1, -1); + } + else if ((!c->IsInvisible(c))&& (!c->invisible_undead) && (c->GetLevel() >= 4) && (!strcasecmp(sep->arg[2], "live")) && (zone->CanCastOutdoor())) { + Inviser->Say("Casting Camouflage..."); + Inviser->CastSpell(247, c->GetID(), 1, -1, -1); + } + else if ((c->GetLevel() >= 4) && (!strcasecmp(sep->arg[2], "live")) && (!zone->CanCastOutdoor())) { + Inviser->Say("I can't cast this spell indoors..."); + } + else if ((c->GetLevel() <= 13) && (!strcasecmp(sep->arg[2], "see"))) { + Inviser->Say("I'm not level 13 yet."); + } + else if ((c->GetLevel() >= 13) && (!strcasecmp(sep->arg[2], "see"))) { + Inviser->Say("Casting see invisible..."); + Inviser->CastSpell(80, c->GetID(), 1, -1, -1); + } + else if ((c->IsInvisible(c)) || (c->invisible_undead)) { + Inviser->Say("I can't cast this if you're already invis-buffed..."); + } + else { + Inviser->Say("Do you want [invis live] or [invis see] ?", c->GetName()); + } + break; + default: + c->Message(15, "You must have a Enchanter, Magician, Wizard, Druid, or Necromancer in your group."); + break; + } + } + } + + //Levitate + if ((!strcasecmp(sep->arg[1], "levitate")) && (c->IsGrouped())) { + Mob *Lever; + uint32 LeverClass = 0; + Group *g = c->GetGroup(); + if(g) { + for(int i=0; imembers[i] && g->members[i]->IsBot()) { + switch(g->members[i]->GetClass()) { + case DRUID: + Lever = g->members[i]; + LeverClass = DRUID; + break; + case SHAMAN: + if (LeverClass != DRUID){ + Lever = g->members[i]; + LeverClass = SHAMAN; + } + break; + case WIZARD: + if(LeverClass == 0){ + Lever = g->members[i]; + LeverClass = WIZARD; + } + break; + case ENCHANTER: + if (LeverClass == 0) { + Lever = g->members[i]; + LeverClass = ENCHANTER; + } + break; + default: + break; + } + } + } + switch(LeverClass) { + case DRUID: + if (c->GetLevel() <= 14) { + Lever->Say("I'm not level 14 yet."); + } + else if (zone->CanCastOutdoor()) { + Lever->Say("Casting Levitate..."); + Lever->CastSpell(261, c->GetID(), 1, -1, -1); + break; + } + else if (!zone->CanCastOutdoor()) { + Lever->Say("I can't cast this spell indoors", c->GetName()); + } + break; + + case SHAMAN: + + if ((zone->CanCastOutdoor()) && (c->GetLevel() >= 10)) { + Lever->Say("Casting Levitate..."); + Lever->CastToClient()->CastSpell(261, c->GetID(), 1, -1, -1); + } + else if (!zone->CanCastOutdoor()) { + Lever->Say("I can't cast this spell indoors", c->GetName()); + } + else if (c->GetLevel() <= 10) { + Lever->Say("I'm not level 10 yet."); + } + break; + + case WIZARD: + + if((zone->CanCastOutdoor()) && (c->GetLevel() >= 22)){ + Lever->Say("Casting Levitate..."); + Lever->CastToClient()->CastSpell(261, c->GetID(), 1, -1, -1); + } + else if (!zone->CanCastOutdoor()) { + Lever->Say("I can't cast this spell indoors", c->GetName()); + } + else if (c->GetLevel() <= 22) { + Lever->Say("I'm not level 22 yet."); + } + break; + + case ENCHANTER: + + if((zone->CanCastOutdoor()) && (c->GetLevel() >= 15)) { + Lever->Say("Casting Levitate..."); + Lever->CastToClient()->CastSpell(261, c->GetID(), 1, -1, -1); + } + else if (!zone->CanCastOutdoor()) { + Lever->Say("I can't cast this spell indoors", c->GetName()); + } + else if (c->GetLevel() <= 15) { + Lever->Say("I'm not level 15 yet."); + } + break; + + + default: + c->Message(15, "You must have a Druid, Shaman, Wizard, or Enchanter in your group."); + break; + } + } + } + + //Resists + if ((!strcasecmp(sep->arg[1], "resist")) && (c->IsGrouped())) { + Mob *Resister; + uint32 ResisterClass = 0; + Group *g = c->GetGroup(); + if(g) { + for(int i=0; imembers[i] && g->members[i]->IsBot()) { + switch(g->members[i]->GetClass()) { + case CLERIC: + Resister = g->members[i]; + ResisterClass = CLERIC; + break; + case SHAMAN: + if(ResisterClass != CLERIC){ + Resister = g->members[i]; + ResisterClass = SHAMAN; + } + case DRUID: + if (ResisterClass == 0){ + Resister = g->members[i]; + ResisterClass = DRUID; + } + break; + break; + default: + break; + } + } + } + switch(ResisterClass) { + case CLERIC: + if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 6)) { + Resister->Say("Casting poison protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(1, Resister->GetLevel()); + } + else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 11)) { + Resister->Say("Casting disease protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(2, Resister->GetLevel()); + } + else if(!strcasecmp(sep->arg[2], "fire") && (c->GetLevel() >= 8)) { + Resister->Say("Casting fire protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(3, Resister->GetLevel()); + } + else if(!strcasecmp(sep->arg[2], "cold") && (c->GetLevel() >= 13)) { + Resister->Say("Casting cold protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(4, Resister->GetLevel()); + } + else if(!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() >= 16)) { + Resister->Say("Casting magic protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(5, Resister->GetLevel()); + } + else if (!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() <= 16) + || !strcasecmp(sep->arg[2], "cold") && (c->GetLevel() <= 13) + || !strcasecmp(sep->arg[2], "fire") && (c->GetLevel() <= 8) + || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 11) + || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 6)) { + Resister->Say("I don't have the needed level yet", sep->arg[2]); + } + else + Resister->Say("Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?", c->GetName()); + + break; + + case SHAMAN: + if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 20)) { + Resister->Say("Casting poison protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(12, Resister->GetLevel()); + } + else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 8)) { + Resister->Say("Casting disease protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(13, Resister->GetLevel()); + } + else if(!strcasecmp(sep->arg[2], "fire") && (c->GetLevel() >= 5)) { + Resister->Say("Casting fire protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(14, Resister->GetLevel()); + } + else if(!strcasecmp(sep->arg[2], "cold") && (c->GetLevel() >= 1)) { + Resister->Say("Casting cold protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(15, Resister->GetLevel()); + } + else if(!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() >= 19)) { + Resister->Say("Casting magic protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(16, Resister->GetLevel()); + } + else if (!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() <= 19) + || !strcasecmp(sep->arg[2], "cold") && (c->GetLevel() <= 1) + || !strcasecmp(sep->arg[2], "fire") && (c->GetLevel() <= 5) + || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 8) + || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 20)) { + Resister->Say("I don't have the needed level yet", sep->arg[2]); + } + else + Resister->Say("Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?", c->GetName()); + + break; + + case DRUID: + + if (!strcasecmp(sep->arg[2], "poison") && (c->GetLevel() >= 19)) { + Resister->Say("Casting poison protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(7, Resister->GetLevel()); + } + else if (!strcasecmp(sep->arg[2], "disease") && (c->GetLevel() >= 19)) { + Resister->Say("Casting disease protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(8, Resister->GetLevel()); + } + else if(!strcasecmp(sep->arg[2], "fire")) { // Fire level 1 + Resister->Say("Casting fire protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(9, Resister->GetLevel()); + } + else if(!strcasecmp(sep->arg[2], "cold") && (c->GetLevel() >= 13)) { + Resister->Say("Casting cold protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(10, Resister->GetLevel()); + } + else if(!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() >= 16)) { + Resister->Say("Casting magic protection...", sep->arg[2]); + Resister->CastToBot()->Bot_Command_Resist(11, Resister->GetLevel()); + } + else if (!strcasecmp(sep->arg[2], "magic") && (c->GetLevel() <= 16) + || !strcasecmp(sep->arg[2], "cold") && (c->GetLevel() <= 9) + || !strcasecmp(sep->arg[2], "disease") && (c->GetLevel() <= 19) + || !strcasecmp(sep->arg[2], "poison") && (c->GetLevel() <= 19)) { + Resister->Say("I don't have the needed level yet", sep->arg[2]) ; + } + else + Resister->Say("Do you want [resist poison], [resist disease], [resist fire], [resist cold], or [resist magic]?", c->GetName()); + + break; + + default: + c->Message(15, "You must have a Cleric, Shaman, or Druid in your group."); + break; + } + } + } + + // debug commands + if(!strcasecmp(sep->arg[1], "debug") && !strcasecmp(sep->arg[2], "inventory")) { + Mob *target = c->GetTarget(); + + if(target && target->IsBot()) { + for(int i=0; i<9; i++) { + c->Message(15,"Equiped slot: %i , item: %i \n", i, target->CastToBot()->GetEquipment(i)); + } + if(target->CastToBot()->GetEquipment(8) > 0) + c->Message(15,"This bot has an item in off-hand."); + } + return; + } + + if(!strcasecmp(sep->arg[1], "debug") && !strcasecmp(sep->arg[2], "botcaracs")) + { + Mob *target = c->GetTarget(); + if(target && target->IsBot()) + { + if(target->CanThisClassDualWield()) + c->Message(15, "This class can dual wield."); + if(target->CanThisClassDoubleAttack()) + c->Message(15, "This class can double attack."); + } + if(target->GetPetID()) + c->Message(15, "I've a pet and its name is %s", target->GetPet()->GetCleanName() ); + return; + } + + if(!strcasecmp(sep->arg[1], "debug") && !strcasecmp(sep->arg[2], "spells")) + { + Mob *target = c->GetTarget(); + if(target && target->IsBot()) + { + for(int i=0; iCastToBot()->AIspells.size(); i++) + { + if(target->CastToBot()->BotGetSpells(i) != 0) + { + SPDat_Spell_Struct botspell = spells[target->CastToBot()->BotGetSpells(i)]; + c->Message(15, "(DEBUG) %s , Slot(%i), Spell (%s) Priority (%i) \n", target->GetCleanName(), i, botspell.name, target->CastToBot()->BotGetSpellPriority(i)); + } + } + } + return; + } + + // #bot group ... + if(!strcasecmp(sep->arg[1], "group") && !strcasecmp(sep->arg[2], "help")) { + c->Message(0, "#bot group help - will show this help."); + c->Message(0, "#bot group summon . Summons the bot group to your location."); + c->Message(0, "#bot group follow "); + c->Message(0, "#bot group guard "); + c->Message(0, "#bot group attack "); + + return; + } + + if(!strcasecmp(sep->arg[1], "group")) { + if(!strcasecmp(sep->arg[2], "follow")) { + if(c->IsGrouped()) + BotGroupOrderFollow(c->GetGroup(), c); + } + else if(!strcasecmp(sep->arg[2], "guard")) { + if(c->IsGrouped()) + BotGroupOrderGuard(c->GetGroup(), c); + } + else if(!strcasecmp(sep->arg[2], "attack")) { + if(c->IsGrouped() && (c->GetTarget() != NULL) && c->IsAttackAllowed(c->GetTarget())) { + BotGroupOrderAttack(c->GetGroup(), c->GetTarget(), c); + } + else + c->Message(15, "You must target a monster."); + } + else if(!strcasecmp(sep->arg[2], "summon")) { + if(c->IsGrouped()) + BotGroupSummon(c->GetGroup(), c); + } + + return; + } + + // #bot botgroup ... + if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "help")) { + c->Message(0, "#bot botgroup help - will show this help."); + c->Message(0, "#bot botgroup create . This will designate a bot to be a bot group leader."); + c->Message(0, "#bot botgroup add "); + c->Message(0, "#bot botgroup remove "); + c->Message(0, "#bot botgroup disband . Disbands the designated bot group leader's bot group."); + c->Message(0, "#bot botgroup summon . Summons the bot group to your location."); + c->Message(0, "#bot botgroup follow "); + c->Message(0, "#bot botgroup guard "); + c->Message(0, "#bot botgroup attack "); + c->Message(0, "#bot botgroup list"); + c->Message(0, "#bot botgroup load "); + c->Message(0, "#bot botgroup save "); + c->Message(0, "#bot botgroup delete "); + + return; + } + + if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "create")) { + Mob* targetMob = c->GetTarget(); + std::string targetName = std::string(sep->arg[3]); + Bot* botGroupLeader = 0; + + if(!targetName.empty()) { + botGroupLeader = entity_list.GetBotByBotName(targetName); + } + else if(targetMob) { + if(targetMob->IsBot()) + botGroupLeader = targetMob->CastToBot(); + } + + if(botGroupLeader) { + if(Bot::BotGroupCreate(botGroupLeader)) + botGroupLeader->Say("I am prepared to lead."); + else + botGroupLeader->Say("I can not lead."); + } + else + c->Message(13, "You must target a spawned bot first."); + + return; + } + + if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "add")) { + int argCount = 0; + + argCount = sep->argnum; + + std::string botGroupLeaderName; + std::string botGroupMemberName; + + if(argCount >= 3) + botGroupMemberName = std::string(sep->arg[3]); + + Bot* botGroupMember = entity_list.GetBotByBotName(botGroupMemberName); + + if(!botGroupMember) { + if(botGroupMemberName.empty()) + c->Message(13, "You must target a bot in this zone. Please try again."); + else + c->Message(13, "%s is not a bot in this zone. Please try again.", botGroupMemberName.c_str()); + + return; + } + + Bot* botGroupLeader = 0; + + if(argCount == 4) { + botGroupLeaderName = std::string(sep->arg[4]); + + botGroupLeader = entity_list.GetBotByBotName(botGroupLeaderName); + } + else if(c->GetTarget() && c->GetTarget()->IsBot()) + botGroupLeader = c->GetTarget()->CastToBot(); + + if(!botGroupLeader) { + if(botGroupLeaderName.empty()) + c->Message(13, "You must target a bot in this zone. Please try again."); + else + c->Message(13, "%s is not a bot in this zone. Please try again.", botGroupLeaderName.c_str()); + + return; + } + + if(botGroupLeader->HasGroup()) { + Group* g = botGroupLeader->GetGroup(); + + if(g) { + if(g->IsLeader(botGroupLeader)) { + if(g->GroupCount() < MAX_GROUP_MEMBERS) { + if(!botGroupMemberName.empty() && botGroupMember) { + botGroupMember = entity_list.GetBotByBotName(botGroupMemberName); + } + + if(botGroupMember) { + if(!botGroupMember->HasGroup()) { + // invite + if(Bot::AddBotToGroup(botGroupMember, g)) { + database.SetGroupID(botGroupMember->GetName(), g->GetID(), botGroupMember->GetBotID()); + botGroupMember->Say("I have joined %s\'s group.", botGroupLeader->GetName()); + } + else { + botGroupMember->Say("I can not join %s\'s group.", botGroupLeader->GetName()); + } + } + else { + // "I am already in a group." + Group* tempGroup = botGroupMember->GetGroup(); + if(tempGroup) + botGroupMember->Say("I can not join %s\'s group. I am already a member in %s\'s group.", botGroupLeader->GetName(), tempGroup->GetLeaderName()); + } + } + else { + // must target a bot message + c->Message(13, "You must target a spawned bot first."); + } + } + else { + // "My group is full." + botGroupLeader->Say("I have no more openings in my group, %s.", c->GetName()); + } + } + else { + // "I am not a group leader." + Group* tempGroup = botGroupLeader->GetGroup(); + if(tempGroup) + botGroupLeader->Say("I can not lead anyone because I am a member in %s\'s group.", tempGroup->GetLeaderName()); + } + } + } + + return; + } + + if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "remove")) { + Mob* targetMob = c->GetTarget(); + std::string targetName = std::string(sep->arg[3]); + Bot* botGroupMember = 0; + + if(!targetName.empty()) { + botGroupMember = entity_list.GetBotByBotName(targetName); + } + else if(targetMob) { + if(targetMob->IsBot()) + botGroupMember = targetMob->CastToBot(); + } + + if(botGroupMember) { + if(botGroupMember->HasGroup()) { + Group* g = botGroupMember->GetGroup(); + + if(Bot::RemoveBotFromGroup(botGroupMember, g)) + botGroupMember->Say("I am no longer in a group."); + else + botGroupMember->Say("I can not leave %s\'s group.", g->GetLeaderName()); + } + else + botGroupMember->Say("I am not in a group."); + } + else + c->Message(13, "You must target a spawned bot first."); + + return; + } + + if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "disband")) { + Mob* targetMob = c->GetTarget(); + std::string targetName = std::string(sep->arg[3]); + Bot* botGroupLeader = 0; + + if(!targetName.empty()) { + botGroupLeader = entity_list.GetBotByBotName(targetName); + } + else if(targetMob) { + if(targetMob->IsBot()) + botGroupLeader = targetMob->CastToBot(); + } + + if(botGroupLeader) { + if(botGroupLeader->HasGroup()) { + Group* g = botGroupLeader->GetGroup(); + + if(g->IsLeader(botGroupLeader)) { + if(Bot::RemoveBotFromGroup(botGroupLeader, g)) + botGroupLeader->Say("I have disbanded my group, %s.", c->GetName()); + else + botGroupLeader->Say("I was not able to disband my group, %s.", c->GetName()); + } + else { + botGroupLeader->Say("I can not disband my group, %s, because I am not the leader. %s is the leader of my group.", c->GetName(), g->GetLeaderName()); + } + } + else + botGroupLeader->Say("I am not a group leader, %s.", c->GetName()); + } + else + c->Message(13, "You must target a spawned bot group leader first."); + + return; + } + + if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "summon") ) { + Mob* targetMob = c->GetTarget(); + std::string targetName = std::string(sep->arg[3]); + Bot* botGroupLeader = 0; + + if(!targetName.empty()) { + botGroupLeader = entity_list.GetBotByBotName(targetName); + } + else if(targetMob) { + if(targetMob->IsBot()) + botGroupLeader = targetMob->CastToBot(); + } + + if(botGroupLeader) { + if(botGroupLeader->HasGroup()) { + Group* g = botGroupLeader->GetGroup(); + + if(g->IsLeader(botGroupLeader)) + BotGroupSummon(g, c); + } + } + else if(c->HasGroup()) + BotGroupSummon(c->GetGroup(), c); + + return; + } + + if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "follow") ) { + Mob* targetMob = c->GetTarget(); + std::string targetName = std::string(sep->arg[3]); + Bot* botGroupLeader = 0; + + if(!targetName.empty()) { + botGroupLeader = entity_list.GetBotByBotName(targetName); + } + else if(targetMob) { + if(targetMob->IsBot()) + botGroupLeader = targetMob->CastToBot(); + } + + if(botGroupLeader) { + if(botGroupLeader->HasGroup()) { + Group* g = botGroupLeader->GetGroup(); + + if(g->IsLeader(botGroupLeader)) + BotGroupOrderFollow(g, c); + } + } + else if(c->HasGroup()) + BotGroupOrderFollow(c->GetGroup(), c); + + return; + } + + if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "guard") ) { + Mob* targetMob = c->GetTarget(); + std::string targetName = std::string(sep->arg[3]); + Bot* botGroupLeader = 0; + + if(!targetName.empty()) { + botGroupLeader = entity_list.GetBotByBotName(targetName); + } + else if(targetMob) { + if(targetMob->IsBot()) + botGroupLeader = targetMob->CastToBot(); + } + + if(botGroupLeader) { + if(botGroupLeader->HasGroup()) { + Group* g = botGroupLeader->GetGroup(); + + if(g->IsLeader(botGroupLeader)) + BotGroupOrderGuard(g, c); + } + } + else if(c->HasGroup()) + BotGroupOrderGuard(c->GetGroup(), c); + + return; + } + + if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "attack") ) { + Mob* targetMob = c->GetTarget(); + Bot* botGroupLeader = 0; + std::string botGroupLeaderName = std::string(sep->arg[3]); + std::string targetName = std::string(sep->arg[4]); + + if(!botGroupLeaderName.empty()) { + botGroupLeader = entity_list.GetBotByBotName(botGroupLeaderName); + + if(botGroupLeader) { + if(!targetName.empty()) { + targetMob = entity_list.GetMob(targetName.c_str()); + } + + if(targetMob) { + if(c->IsAttackAllowed(targetMob)) { + if(botGroupLeader->HasGroup()) { + Group* g = botGroupLeader->GetGroup(); + + if(g) { + if(g->IsLeader(botGroupLeader)) + BotGroupOrderAttack(g, targetMob, c); + } + } + else if(c->HasGroup()) + BotGroupOrderAttack(c->GetGroup(), targetMob, c); + } + else + c->Message(13, "You must target a monster."); + } + else + c->Message(13, "You must target a monster."); + } + else + c->Message(13, "You must target a spawned bot group leader first."); + } + + return; + } + + if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "list")) { + std::list botGroupList = GetBotGroupListByBotOwnerCharacterId(c->CharacterID(), &TempErrorMessage); + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + if(!botGroupList.empty()) { + for(std::list::iterator botGroupListItr = botGroupList.begin(); botGroupListItr != botGroupList.end(); botGroupListItr++) { + c->Message(0, "Bot Group Name: %s -- Bot Group Leader: %s", botGroupListItr->BotGroupName.c_str(), botGroupListItr->BotGroupLeaderName.c_str()); + } + } + else { + c->Message(0, "You have no bot groups created. Use the #bot botgroup save command to save bot groups."); + } + + return; + } + + if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "load")) { + + // If client is grouped, check for aggro on each group member. + Group *g = c->GetGroup(); + if(g) { + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + // Skip invalid group members. + if(!g->members[i]) { continue; } + + // Fail if current group member is client and has aggro + // OR has a popuplated hate list (assume bot). + if((g->members[i]->IsClient() && g->members[i]->CastToClient()->GetAggroCount()) + || g->members[i]->IsEngaged()) { + c->Message(0, "You can't spawn bots while your group is engaged."); + return; + } + } + } + // Fail if ungrouped client has aggro. + else { + if(c->GetAggroCount() > 0) { + c->Message(0, "You can't spawn bots while you are engaged."); + return; + } + } + + // Parse botgroup name. + std::string botGroupName = std::string(sep->arg[3]); + if(botGroupName.empty()) { + c->Message(13, "Invalid botgroup name supplied."); + return; + } + + // Get botgroup id. + uint32 botGroupID = CanLoadBotGroup(c->CharacterID(), botGroupName, &TempErrorMessage); + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + if(botGroupID <= 0) { + c->Message(13, "Invalid botgroup id found."); + return; + } + + // Get list of bots in specified group. + std::list botGroup = LoadBotGroup(botGroupName, &TempErrorMessage); + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + // Count of client's currently spawned bots. + int spawnedBots = SpawnedBotCount(c->CharacterID(), &TempErrorMessage); + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + // BotQuest rule value in database is True. + if(RuleB(Bots, BotQuest)) { + // Max number of allowed spawned bots for client. + const int allowedBotsBQ = AllowedBotSpawns(c->CharacterID(), &TempErrorMessage); + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + // Fail if no bots allowed for client. + if(allowedBotsBQ == 0) { + c->Message(0, "You can't spawn any bots."); + return; + } + + // Fail if maximum number of spawned bots allowed for client met or exceeded + // OR will be when bot group is spawned. + if(spawnedBots >= allowedBotsBQ + || spawnedBots + (int)botGroup.size() > allowedBotsBQ) { + c->Message(0, "You can't spawn more than %i bot(s). [Rule:BQ]", allowedBotsBQ); + return; + } + } + + // Fail if maximum number of spawned bots allowed for client met or exceeded + // OR will be when bot group is spawned. + const int allowedBotsSBC = RuleI(Bots, SpawnBotCount); + if(spawnedBots >= allowedBotsSBC + || spawnedBots + (int)botGroup.size() > allowedBotsSBC) { + c->Message(0, "You can't spawn more than %i bots. [Rule:SBC]", allowedBotsSBC); + return; + } + + // Passed all checks. Spawn requested bot group. + + // Get botgroup's leader's id. + uint32 botGroupLeaderBotID = GetBotGroupLeaderIdByBotGroupName(botGroupName); + + // Load botgroup's leader. + Bot *botGroupLeader = LoadBot(botGroupLeaderBotID, &TempErrorMessage); + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + safe_delete(botGroupLeader); + return; + } + if(!botGroupLeader) { + c->Message(13, "Failed to load botgroup leader."); + safe_delete(botGroupLeader); + return; + } + + // Spawn botgroup's leader. + botGroupLeader->Spawn(c, &TempErrorMessage); + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + safe_delete(botGroupLeader); + return; + } + + // Create botgroup. + if(!BotGroupCreate(botGroupLeader)) { + c->Message(13, "Unable to create botgroup."); + return; + } + Group *newBotGroup = botGroupLeader->GetGroup(); + if(!newBotGroup) { + c->Message(13, "Unable to find valid botgroup"); + return; + } + + std::list::iterator botGroupItr = botGroup.begin(); + for(botGroupItr; botGroupItr != botGroup.end(); botGroupItr++) { + // Don't try to re-spawn the botgroup's leader. + if(botGroupItr->BotID == botGroupLeader->GetBotID()) { continue; } + + // Load current botgroup member + Bot *botGroupMember = LoadBot(botGroupItr->BotID, &TempErrorMessage); + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + safe_delete(botGroupMember); + return; + } + // Skip invalid botgroup members. + if(!botGroupMember) { + safe_delete(botGroupMember); + continue; + } + + // Spawn current botgroup member. + botGroupMember->Spawn(c, &TempErrorMessage); + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + safe_delete(botGroupMember); + return; + } + + // Add current botgroup member to botgroup. + AddBotToGroup(botGroupMember, newBotGroup); + } + + return; + } + + if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "delete")) { + std::string botGroupName = std::string(sep->arg[3]); + + if(!botGroupName.empty()) { + uint32 botGroupId = CanLoadBotGroup(c->CharacterID(), botGroupName, &TempErrorMessage); + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + + if(botGroupId > 0) { + DeleteBotGroup(botGroupName, &TempErrorMessage); + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return; + } + } + } + + return; + } + + if(!strcasecmp(sep->arg[1], "botgroup") && !strcasecmp(sep->arg[2], "save")) { + std::string botGroupName = std::string(sep->arg[3]); + + if(!botGroupName.empty()) { + if(!DoesBotGroupNameExist(botGroupName)) { + Bot* groupLeader = 0; + + if(c->GetTarget() && c->GetTarget()->IsBot()) + groupLeader = c->GetTarget()->CastToBot(); + else + groupLeader = entity_list.GetBotByBotName(std::string(sep->arg[4])); + + if(groupLeader) { + if(groupLeader->HasGroup() && groupLeader->GetGroup()->IsLeader(groupLeader)) { + SaveBotGroup(groupLeader->GetGroup(), botGroupName, &TempErrorMessage); + + if(!TempErrorMessage.empty()) { + c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + } + else + c->Message(0, "%s's bot group has been saved as %s.", groupLeader->GetName(), botGroupName.c_str()); + } + else + c->Message(0, "You must target a bot group leader only."); + } + else + c->Message(0, "You must target a bot that is in the same zone as you."); + } + else + c->Message(0, "The bot group name already exists. Please choose another name to save your bot group as."); + } + + return; + } + + if(!strcasecmp(sep->arg[1], "haircolor") || !strcasecmp(sep->arg[1], "hair") || !strcasecmp(sep->arg[1], "beard") || !strcasecmp(sep->arg[1], "beardcolor") || !strcasecmp(sep->arg[1], "face") + || !strcasecmp(sep->arg[1], "eyes") || !strcasecmp(sep->arg[1], "heritage") || !strcasecmp(sep->arg[1], "tattoo") || !strcasecmp(sep->arg[1], "details")) { + if(c->GetTarget() && c->GetTarget()->IsBot()) { + if (sep->IsNumber(2)) { + if (c->GetTarget()->CastToBot()->GetBotOwnerCharacterID() == c->CharacterID()) { + Bot *target = c->GetTarget()->CastToBot(); + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairStyle = target->GetHairStyle(); + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + float Size = target->GetSize(); + + if (!strcasecmp(sep->arg[1], "hair")) + HairStyle = atoi(sep->arg[2]); + if (!strcasecmp(sep->arg[1], "haircolor")) + HairColor = atoi(sep->arg[2]); + if (!strcasecmp(sep->arg[1], "beard") || !strcasecmp(sep->arg[1], "beardcolor")) { + if (!Gender || Race == 8) { + if (!strcasecmp(sep->arg[1], "beard")) + Beard = atoi(sep->arg[2]); + if (!strcasecmp(sep->arg[1], "beardcolor")) + BeardColor = atoi(sep->arg[2]); + } else { + c->Message(0, "Must be a male bot, or dwarf."); + return; + } + } + if (!strcasecmp(sep->arg[1], "face")) + LuclinFace = atoi(sep->arg[2]); + + if (!strcasecmp(sep->arg[1], "eyes")) { + EyeColor1 = EyeColor2 = atoi(sep->arg[2]); + c->Message(0, "Eye Values = 0 - 11"); + } + if(!strcasecmp(sep->arg[1], "heritage") || !strcasecmp(sep->arg[1], "tattoo") || !strcasecmp(sep->arg[1], "details")) { + if(Race == 522) { + if(!strcasecmp(sep->arg[1], "heritage")) { + DrakkinHeritage = atoi(sep->arg[2]); + c->Message(0, "Heritage Values = 0 - 6"); + } + if(!strcasecmp(sep->arg[1], "tattoo")) { + DrakkinTattoo = atoi(sep->arg[2]); + c->Message(0, "Tattoo Values = 0 - 7"); + } + if(!strcasecmp(sep->arg[1], "details")) { + DrakkinDetails = atoi(sep->arg[2]); + c->Message(0, "Details Values = 0 - 7"); + } + } + else { + c->Message(0, "Drakkin only."); + return; + } + } + + target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails, Size); + + if(target->CastToBot()->Save()) + c->Message(0, "%s saved.", target->GetCleanName()); + else + c->Message(13, "%s save failed!", target->GetCleanName()); + + c->Message(0,"Feature changed."); + } else { + c->Message(0, "You must own the bot to make changes."); + } + } else { + c->Message(0, "Requires a value."); + } + } else { + c->Message(0,"A bot needs to be targetted."); + } + return; + } + + if(!strcasecmp(sep->arg[1], "taunt")) { + bool taunt = false; + bool toggle = false; + + if(sep->arg[2]){ + if(!strcasecmp(sep->arg[2], "on")) + taunt = true; + else if (!strcasecmp(sep->arg[2], "off")) + taunt = false; + else { + c->Message(0, "Usage #bot taunt [on|off]"); + return; + } + + Bot* targetedBot; + + if(c->GetTarget() != NULL) { + if (c->GetTarget()->IsBot() && (c->GetTarget()->CastToBot()->GetBotOwner() == c)) + targetedBot = c->GetTarget()->CastToBot(); + else + c->Message(13, "You must target a bot that you own."); + + if(targetedBot) { + if(targetedBot->GetSkill(TAUNT) > 0) { + if(toggle) + taunt = !targetedBot->taunting; + + if(taunt) { + if(!targetedBot->taunting) + targetedBot->Say("I am now taunting."); + } + else { + if(targetedBot->taunting) + targetedBot->Say("I am no longer taunting."); + } + + targetedBot->SetTaunting(taunt); + } + else + c->Message(13, "You must select a bot with the taunt skill."); + } + else { + c->Message(13, "You must target a spawned bot."); + } + } + } + else { + c->Message(0, "Usage #bot taunt [on|off]"); + } + + return; + } + + if(!strcasecmp(sep->arg[1], "stance")) { + if(sep->argnum == 3){ + Bot* tempBot; + std::string botName = std::string(sep->arg[2]); + + if(!botName.empty()) + tempBot = entity_list.GetBotByBotName(botName); + else + c->Message(13, "You must name a valid bot."); + + if(tempBot) { + std::string stanceName; + BotStanceType botStance; + + if (tempBot->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + if(!strcasecmp(sep->arg[3], "list")) { + botStance = tempBot->GetBotStance(); + } + else { + int stance = atoi(sep->arg[3]); + + if(stance >= MaxStances || stance < 0){ + c->Message(0, "Usage #bot stance [name] [stance (id)] (Passive = 0, Balanced = 1, Efficient = 2, Reactive = 3, Aggressive = 4, Burn = 5, BurnAE = 6)"); + return; + } + else { + botStance = (BotStanceType)stance; + if(botStance != tempBot->GetBotStance()) { + tempBot->SetBotStance(botStance); + tempBot->CalcChanceToCast(); + tempBot->Save(); + } + } + } + + switch(botStance) { + case BotStancePassive: { + stanceName = "Passive"; + break; + } + case BotStanceBalanced: { + stanceName = "Balanced"; + break; + } + case BotStanceEfficient: { + stanceName = "Efficient"; + break; + } + case BotStanceReactive: { + stanceName = "Reactive"; + break; + } + case BotStanceAggressive: { + stanceName = "Aggressive"; + break; + } + case BotStanceBurn: { + stanceName = "Burn"; + break; + } + case BotStanceBurnAE: { + stanceName = "BurnAE"; + break; + } + default: { + stanceName = "None"; + break; + } + } + c->Message(0, "Stance for %s: %s.", tempBot->GetCleanName(), stanceName.c_str()); + } + else { + c->Message(13, "You must name a valid bot."); + } + } + else { + c->Message(0, "Usage #bot stance [name] [stance (id)] (Passive = 0, Balanced = 1, Efficient = 2, Reactive = 3, Aggressive = 4, Burn = 5, BurnAE = 6)"); + } + return; + } + + if(!strcasecmp(sep->arg[1], "groupmessages")) { + bool groupMessages = false; + + if(sep->arg[2] && sep->arg[3]){ + if(!strcasecmp(sep->arg[2], "on")) + groupMessages = true; + else if (!strcasecmp(sep->arg[2], "off")) + groupMessages = false; + else { + c->Message(0, "Usage #bot groupmessages [on|off] [bot name|all]"); + return; + } + + Bot* tempBot; + + if(!strcasecmp(sep->arg[3], "all")) { + std::list spawnedBots = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); + + if(!spawnedBots.empty()) { + for(std::list::iterator botsListItr = spawnedBots.begin(); botsListItr != spawnedBots.end(); botsListItr++) { + Bot* tempBot = *botsListItr; + if(tempBot) { + tempBot->SetGroupMessagesOn(groupMessages); + } + } + } + else { + c->Message(0, "You have no spawned bots in this zone."); + } + + c->Message(0, "Group messages now %s for all bots.", groupMessages?"on":"off"); + } + else { + std::string botName = std::string(sep->arg[3]); + + if(!botName.empty()) + tempBot = entity_list.GetBotByBotName(botName); + else { + c->Message(13, "You must name a valid bot."); + return; + } + + if(tempBot) { + if (tempBot->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + tempBot->SetGroupMessagesOn(groupMessages); + c->Message(0, "Group messages now %s.", groupMessages?"on":"off"); + } + else { + c->Message(13, "You must name a valid bot."); + } + } + } + else { + c->Message(0, "Usage #bot groupmessages [on|off] [bot name|all]"); + } + return; + } + + if(!strcasecmp(sep->arg[1], "defensive")) { + Bot* tempBot; + std::string botName = std::string(sep->arg[2]); + + if(!botName.empty()) + tempBot = entity_list.GetBotByBotName(botName); + else { + c->Message(13, "You must name a valid bot."); + return; + } + + if(tempBot) { + uint8 botlevel = tempBot->GetLevel(); + uint32 defensiveSpellID = 0; + + if (tempBot->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + switch (tempBot->GetClass()) { + case WARRIOR: + if(botlevel >= 72) + defensiveSpellID = 10965; //Final Stand discipline + else if(botlevel >= 65) + defensiveSpellID = 4688; //Stonewall discipline + else if(botlevel >= 55) + defensiveSpellID = 4499; //Defensive discipline + else if(botlevel >= 52) + defensiveSpellID = 4503; //Evasive discipline + else + c->Message(0, "Error: warrior must be level 52+"); + break; + case PALADIN: + if(botlevel >= 73) + defensiveSpellID = 11854; //Armor of Righteousness + else if(botlevel >= 69) + defensiveSpellID = 6663; //Guard of Righteousness + else if(botlevel >= 61) + defensiveSpellID = 6731; //Guard of Humility + else if(botlevel >= 56) + defensiveSpellID = 7004; //Guard of Piety + else + c->Message(0, "Error: paladin must be level 56+"); + break; + case SHADOWKNIGHT: + if(botlevel >= 73) + defensiveSpellID = 11866; //Soul Carapace + else if(botlevel >= 69) + defensiveSpellID = 6673; //Soul shield + else if(botlevel >= 61) + defensiveSpellID = 6741; //Soul guard + else if(botlevel >= 56) + defensiveSpellID = 7005; //Ichor guard + else + c->Message(0, "Error: shadowknight must be level 56+"); + break; + default: + c->Message(0, "Error: you must select a warrior or knight"); + break; + } + + if(defensiveSpellID > 0) { + tempBot->UseDiscipline(defensiveSpellID, tempBot->GetID()); + } + } + else { + c->Message(13, "You must name a valid bot."); + } + return; + } + + // #bot healrotation ... + if(!strcasecmp(sep->arg[1], "healrotation")) { + if(!strcasecmp(sep->arg[2], "help")) { + c->Message(0, "#bot healrotation help - will show this help."); + c->Message(0, "#bot healrotation create [target]. This will create a heal rotation with the designated leader."); + c->Message(0, "#bot healrotation addmember "); + c->Message(0, "#bot healrotation removemember "); + c->Message(0, "#bot healrotation addtarget [bot healrotation target name to add] "); + c->Message(0, "#bot healrotation removetarget "); + c->Message(0, "#bot healrotation cleartargets "); + c->Message(0, "#bot healrotation fastheals "); + c->Message(0, "#bot healrotation start "); + c->Message(0, "#bot healrotation stop "); + c->Message(0, "#bot healrotation list "); + + return; + } + + if(!strcasecmp(sep->arg[2], "create")) { + if(sep->argnum == 5 || sep->argnum == 6) { //allows for target or not + Bot* leaderBot; + + std::string botName = std::string(sep->arg[3]); + + if(!botName.empty()) + leaderBot = entity_list.GetBotByBotName(botName); + else { + c->Message(13, "You must name a valid heal rotation leader."); + return; + } + + if(leaderBot) { + Mob* target = NULL; + uint32 timer; + bool fastHeals = false; + + if (!sep->IsNumber(4)) { + c->Message(0, "Usage #bot healrotation create [target]."); + return; + } + + timer = (uint32)(atof(sep->arg[4]) * 1000); + + if (leaderBot->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + if (!(leaderBot->IsBotCaster() && leaderBot->CanHeal())) { + c->Message(13, "Heal rotation members must be able to heal."); + return; + } + + //get percentage heals + if(!strcasecmp(sep->arg[5], "fasthealson")) + fastHeals = true; + else if(strcasecmp(sep->arg[5], "fasthealsoff")) { + c->Message(0, "Usage #bot healrotation create [target]."); + return; + } + + if(!leaderBot->GetInHealRotation()) { + //check for target + if(sep->argnum == 6) { + std::string targetName = std::string(sep->arg[6]); + + if(!targetName.empty()) + target = entity_list.GetMob(targetName.c_str()); + else { + c->Message(13, "You must name a valid target."); + return; + } + + if(!target) { + c->Message(13, "You must name a valid target."); + return; + } + } + + //create rotation + leaderBot->CreateHealRotation(target, timer); + leaderBot->SetHealRotationUseFastHeals(fastHeals); + c->Message(0, "Bot heal rotation created successfully."); + } + else { + c->Message(13, "That bot is already in a heal rotation."); + return; + } + } + else { + c->Message(13, "You must name a valid bot."); + return; + } + } + else { + c->Message(0, "Usage #bot healrotation create [target]."); + return; + } + } + + if(!strcasecmp(sep->arg[2], "addmember")) { + if(sep->argnum == 4) { + Bot* leaderBot; + std::string botName = std::string(sep->arg[3]); + + if(!botName.empty()) + leaderBot = entity_list.GetBotByBotName(botName); + else { + c->Message(13, "You must name a valid bot."); + return; + } + + if(leaderBot) { + Bot* healer; + std::string healerName = std::string(sep->arg[4]); + + if (leaderBot->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + if(!healerName.empty()) + healer = entity_list.GetBotByBotName(healerName); + else { + c->Message(13, "You must name a valid bot."); + return; + } + + if(healer) { + if (healer->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + if (!(healer->IsBotCaster() && healer->CanHeal())) { + c->Message(13, "Heal rotation members must be able to heal."); + return; + } + + //add to rotation + if(leaderBot->AddHealRotationMember(healer)) { + c->Message(0, "Bot heal rotation member added successfully."); + } + else { + c->Message(13, "Unable to add bot to rotation. "); + } + } + } + else { + c->Message(13, "You must name a valid bot."); + return; + } + } + else { + c->Message(0, "#bot healrotation addmember "); + return; + } + } + + if(!strcasecmp(sep->arg[2], "removemember")) { + if(sep->argnum == 4) { + Bot* leaderBot; + std::string botName = std::string(sep->arg[3]); + + if(!botName.empty()) + leaderBot = entity_list.GetBotByBotName(botName); + else { + c->Message(13, "You must name a valid bot."); + return; + } + + if(leaderBot) { + if (leaderBot->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + Bot* healer; + std::string healerName = std::string(sep->arg[4]); + + if(!healerName.empty()) + healer = entity_list.GetBotByBotName(healerName); + else { + c->Message(13, "You must name a valid bot."); + return; + } + + if(healer) { + if (healer->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + //remove from rotation + if(leaderBot->RemoveHealRotationMember(healer)) { + c->Message(0, "Bot heal rotation member removed successfully."); + } + else { + c->Message(13, "Unable to remove bot from rotation. "); + } + } + else { + c->Message(13, "You must name a valid bot."); + return; + } + } + else { + c->Message(13, "You must name a valid bot."); + return; + } + } + else { + c->Message(0, "#bot healrotation removemember "); + return; + } + } + + if(!strcasecmp(sep->arg[2], "addtarget")) { + if(sep->argnum == 3 || sep->argnum == 4) { + Bot* leaderBot; + std::string botName = std::string(sep->arg[3]); + + if(!botName.empty()) + leaderBot = entity_list.GetBotByBotName(botName); + else { + c->Message(13, "You must name a valid heal rotation leader."); + return; + } + + if(leaderBot) { + if (leaderBot->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + Mob* target; + std::string targetName = std::string(sep->arg[4]); + + if(!targetName.empty()) + target = entity_list.GetMob(targetName.c_str()); + else { + if(c->GetTarget() != NULL) { + target = c->GetTarget(); + } + } + + if(target) { + //add target + if(leaderBot->AddHealRotationTarget(target)) { + c->Message(0, "Bot heal rotation target added successfully."); + } + else { + c->Message(13, "Unable to add rotation target. "); + } + } + else { + c->Message(13, "Invalid target."); + return; + } + } + else { + c->Message(13, "You must name a valid bot."); + return; + } + } + else { + c->Message(0, "#bot healrotation addtarget [bot healrotation target name to add] "); + return; + } + } + + if(!strcasecmp(sep->arg[2], "removetarget")) { + if(sep->argnum == 4) { + Bot* leaderBot; + std::string botName = std::string(sep->arg[3]); + + if(!botName.empty()) + leaderBot = entity_list.GetBotByBotName(botName); + else { + c->Message(13, "You must name a valid heal rotation leader."); + return; + } + + if(leaderBot) { + if (leaderBot->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + Mob* target; + std::string targetName = std::string(sep->arg[4]); + + if(!targetName.empty()) + target = entity_list.GetMob(targetName.c_str()); + else { + c->Message(13, "You must name a valid target."); + return; + } + + if(target) { + //add to rotation + if(leaderBot->RemoveHealRotationTarget(target)) { + c->Message(0, "Bot heal rotation target removed successfully."); + } + else { + c->Message(13, "Unable to remove rotation target. "); + } + } + } + else { + c->Message(13, "You must name a valid bot."); + return; + } + } + else { + c->Message(0, "#bot healrotation removetarget "); + return; + } + } + + if(!strcasecmp(sep->arg[2], "start")) { + if(sep->argnum == 3) { + if(!strcasecmp(sep->arg[3], "all")) { + std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); + + for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); botListItr++) { + Bot* leaderBot = *botListItr; + if(leaderBot->GetInHealRotation() && leaderBot->GetHealRotationLeader() == leaderBot) { + //start all heal rotations + list rotationMemberList; + int index = 0; + + rotationMemberList = GetBotsInHealRotation(leaderBot); + + for(list::iterator rotationMemberItr = rotationMemberList.begin(); rotationMemberItr != rotationMemberList.end(); rotationMemberItr++) { + Bot* tempBot = *rotationMemberItr; + + if(tempBot) { + tempBot->SetHealRotationActive(true); + tempBot->SetHealRotationNextHealTime(Timer::GetCurrentTime() + index * leaderBot->GetHealRotationTimer() * 1000); + tempBot->SetHasHealedThisCycle(false); + } + + index++; + } + + c->Message(0, "Bot heal rotation started successfully."); + } + } + } + else { + Bot* leaderBot; + std::string botName = std::string(sep->arg[3]); + + if(!botName.empty()) + leaderBot = entity_list.GetBotByBotName(botName); + else { + c->Message(13, "You must name a valid heal rotation leader."); + return; + } + + if(leaderBot) { + list botList; + int index = 0; + if (leaderBot->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + botList = GetBotsInHealRotation(leaderBot); + + for(list::iterator botListItr = botList.begin(); botListItr != botList.end(); botListItr++) { + Bot* tempBot = *botListItr; + + if(tempBot) { + tempBot->SetHealRotationActive(true); + tempBot->SetHealRotationNextHealTime(Timer::GetCurrentTime() + index * leaderBot->GetHealRotationTimer() * 1000); + tempBot->SetHasHealedThisCycle(false); + } + + index++; + } + + c->Message(0, "Bot heal rotation started successfully."); + } + else { + c->Message(13, "You must name a valid bot."); + return; + } + } + } + else { + c->Message(0, "#bot healrotation start "); + return; + } + } + + if(!strcasecmp(sep->arg[2], "stop")) { + if(sep->argnum == 3) { + if(!strcasecmp(sep->arg[3], "all")) { + std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); + + for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); botListItr++) { + Bot* leaderBot = *botListItr; + if(leaderBot->GetInHealRotation() && leaderBot->GetHealRotationLeader() == leaderBot) { + //start all heal rotations + list rotationMemberList; + + rotationMemberList = GetBotsInHealRotation(leaderBot); + + for(list::iterator rotationMemberItr = rotationMemberList.begin(); rotationMemberItr != rotationMemberList.end(); rotationMemberItr++) { + Bot* tempBot = *rotationMemberItr; + + if(tempBot) { + tempBot->SetHealRotationActive(false); + tempBot->SetHasHealedThisCycle(false); + } + } + + c->Message(0, "Bot heal rotation started successfully."); + } + } + } + else { + Bot* leaderBot; + std::string botName = std::string(sep->arg[3]); + + if(!botName.empty()) + leaderBot = entity_list.GetBotByBotName(botName); + else { + c->Message(13, "You must name a valid heal rotation leader."); + return; + } + + if(leaderBot) { + list botList; + if (leaderBot->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + botList = GetBotsInHealRotation(leaderBot); + + for(list::iterator botListItr = botList.begin(); botListItr != botList.end(); botListItr++) { + Bot* tempBot = *botListItr; + + if(tempBot && tempBot->GetBotOwnerCharacterID() == c->CharacterID()) { + tempBot->SetHealRotationActive(false); + tempBot->SetHasHealedThisCycle(false); + } + } + + c->Message(0, "Bot heal rotation stopped successfully."); + } + else { + c->Message(13, "You must name a valid bot."); + return; + } + } + } + else { + c->Message(0, "#bot healrotation stop "); + return; + } + } + + if(!strcasecmp(sep->arg[2], "list")) { + if(sep->argnum == 3) { + bool showAll = false; + Bot* leaderBot; + std::string botName = std::string(sep->arg[3]); + + if(!strcasecmp(sep->arg[3], "all")) { + std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); + + for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); botListItr++) { + Bot* tempBot = *botListItr; + if(tempBot->GetInHealRotation() && tempBot->GetHealRotationLeader() == tempBot) { + //list leaders and number of bots per rotation + c->Message(0, "Bot Heal Rotation- Leader: %s, Number of Members: %i, Timer: %1.1f", tempBot->GetCleanName(), tempBot->GetNumHealRotationMembers(), (float)(tempBot->GetHealRotationTimer()/1000)); + } + } + } + else { + std::string botName = std::string(sep->arg[3]); + + if(!botName.empty()) + leaderBot = entity_list.GetBotByBotName(botName); + else { + c->Message(13, "You must name a valid heal rotation leader."); + return; + } + + if(leaderBot) { + list botList; + if (leaderBot->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + botList = GetBotsInHealRotation(leaderBot); + + //list leader and number of members + c->Message(0, "Bot Heal Rotation- Leader: %s", leaderBot->GetCleanName()); + c->Message(0, "Bot Heal Rotation- Timer: %1.1f", ((float)leaderBot->GetHealRotationTimer()/1000.0f)); + + for(list::iterator botListItr = botList.begin(); botListItr != botList.end(); botListItr++) { + Bot* tempBot = *botListItr; + + if(tempBot && tempBot->GetBotOwnerCharacterID() == c->CharacterID()) { + //list rotation members + c->Message(0, "Bot Heal Rotation- Member: %s", tempBot->GetCleanName()); + } + } + + for(int i=0; iGetHealRotationTarget(i)) { + Mob* tempTarget = leaderBot->GetHealRotationTarget(i); + + if(tempTarget) { + std::string targetInfo = ""; + + targetInfo += tempTarget->GetHPRatio() < 0 ? "(dead) " : ""; + targetInfo += tempTarget->GetZoneID() != leaderBot->GetZoneID() ? "(not in zone) " : ""; + + //list targets + c->Message(0, "Bot Heal Rotation- Target: %s %s", tempTarget->GetCleanName(), targetInfo.c_str()); + } + } + } + } + else { + c->Message(13, "You must name a valid bot."); + return; + } + } + } + else { + c->Message(0, "#bot healrotation list "); + return; + } + } + + if(!strcasecmp(sep->arg[2], "cleartargets")) { + if(sep->argnum == 3) { + Bot* leaderBot; + std::string botName = std::string(sep->arg[3]); + + if(!botName.empty()) + leaderBot = entity_list.GetBotByBotName(botName); + else { + c->Message(13, "You must name a valid heal rotation leader."); + return; + } + + if(leaderBot) { + list botList; + if (leaderBot->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + botList = GetBotsInHealRotation(leaderBot); + + for(list::iterator botListItr = botList.begin(); botListItr != botList.end(); botListItr++) { + Bot* tempBot = *botListItr; + + if(tempBot && tempBot->GetBotOwnerCharacterID() == c->CharacterID()) + tempBot->ClearHealRotationTargets(); + } + } + else { + c->Message(13, "You must name a valid bot."); + return; + } + } + else { + c->Message(0, "#bot healrotation cleartargets "); + return; + } + } + + if(!strcasecmp(sep->arg[2], "fastheals")) { + if(sep->argnum == 3) { + Bot* leaderBot; + std::string botName = std::string(sep->arg[3]); + + if(!botName.empty()) + leaderBot = entity_list.GetBotByBotName(botName); + else { + c->Message(13, "You must name a valid heal rotation leader."); + return; + } + + if(leaderBot) { + bool fastHeals = false; + list botList; + if (leaderBot->GetBotOwner() != c) { + c->Message(13, "You must target a bot that you own."); + return; + } + + //get percentage heals & target + if(!strcasecmp(sep->arg[4], "on")) + fastHeals = true; + else if(strcasecmp(sep->arg[4], "off")) { + c->Message(0, "Usage #bot healrotation fastheals ."); + return; + } + + botList = GetBotsInHealRotation(leaderBot); + + for(list::iterator botListItr = botList.begin(); botListItr != botList.end(); botListItr++) { + Bot* tempBot = *botListItr; + + if(tempBot && tempBot->GetBotOwnerCharacterID() == c->CharacterID()) + tempBot->SetHealRotationUseFastHeals(fastHeals); + } + } + else { + c->Message(13, "You must name a valid bot."); + return; + } + } + else { + c->Message(0, "#bot healrotation fastheals "); + return; + } + } + + if(!strcasecmp(sep->arg[2], "load")) { + } + + if(!strcasecmp(sep->arg[2], "save")) { + } + + if(!strcasecmp(sep->arg[2], "delete")) { + } + } + + // #bot setinspectmessage + if(!strcasecmp(sep->arg[1], "setinspectmessage")) { + if(!strcasecmp(sep->arg[2], "help")) { + c->Message(0, "[Titanium clients:]"); + c->Message(0, "- Self-inspect and type your bot's inspect message"); + c->Message(0, "- Close the self-inspect window"); + c->Message(0, "- Self-inspect again to update the server"); + c->Message(0, "- Target a bot that you own and wish to update"); + c->Message(0, "- type #bot setinspectmessage to set the bot's message"); + c->Message(0, "[Secrets of Faydwer and higher clients:]"); + c->Message(0, "- Self-inspect and type your bot's inspect message"); + c->Message(0, "- Close the self-inspect window to update the server"); + c->Message(0, "- Target a bot that you own and wish to update"); + c->Message(0, "- type #bot setinspectmessage to set the bot's message"); + } + else { + Mob *target = c->GetTarget(); + + if(target->IsBot() && (c == target->GetOwner()->CastToClient())) { + const InspectMessage_Struct& playermessage = c->GetInspectMessage(); + InspectMessage_Struct& botmessage = target->CastToBot()->GetInspectMessage(); + + memcpy(&botmessage, &playermessage, sizeof(InspectMessage_Struct)); + database.SetBotInspectMessage(target->CastToBot()->GetBotID(), &botmessage); + + c->Message(0, "Bot %s's inspect message now reflects your inspect message.", target->GetName()); + } + else { + c->Message(0, "Your target must be a bot that you own."); + } + } + } +} + +// franck: EQoffline +// This function has been reworked for the caster bots, when engaged. +// Healers bots must heal thoses who loose HP. +bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 iSpellTypes) { + _ZP(EntityList_Bot_AICheckCloseBeneficialSpells); + + if((iSpellTypes&SpellTypes_Detrimental) != 0) { + //according to live, you can buff and heal through walls... + //now with PCs, this only applies if you can TARGET the target, but + // according to Rogean, Live NPCs will just cast through walls/floors, no problem.. + // + // This check was put in to address an idle-mob CPU issue + _log(AI__ERROR, "Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); + return(false); + } + + if(!caster) + return false; + + if(!caster->AI_HasSpells()) + return false; + + if (iChance < 100) { + uint8 tmp = MakeRandomInt(1, 100); + if (tmp > iChance) + return false; + } + + uint8 botCasterClass = caster->GetClass(); + + if( iSpellTypes == SpellType_Heal ) { + // Changed so heal based on health percentage is different for hybrids + if( botCasterClass == CLERIC || botCasterClass == DRUID || botCasterClass == SHAMAN) { + //If AI_EngagedCastCheck() said to the healer that he had to heal + + // check in group + if(caster->HasGroup()) { + Group *g = caster->GetGroup(); + + if(g) { + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g->members[i] && !g->members[i]->qglobal) { + if(g->members[i]->IsClient() && g->members[i]->GetHPRatio() < 90) { + if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) + return true; + } + else if((g->members[i]->GetClass() == WARRIOR || g->members[i]->GetClass() == PALADIN || g->members[i]->GetClass() == SHADOWKNIGHT) && g->members[i]->GetHPRatio() < 95) { + if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) + return true; + } + else if(g->members[i]->GetClass() == ENCHANTER && g->members[i]->GetHPRatio() < 80) { + if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) + return true; + } + else if(g->members[i]->GetHPRatio() < 70) { + if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) + return true; + } + } + + if(g->members[i] && !g->members[i]->qglobal && g->members[i]->HasPet() && g->members[i]->GetPet()->GetHPRatio() < 50) { + if(g->members[i]->GetPet()->GetOwner() != caster && caster->IsEngaged() && g->members[i]->IsCasting() && g->members[i]->GetClass() != ENCHANTER ) + continue; + + if(caster->AICastSpell(g->members[i]->GetPet(), 100, SpellType_Heal)) + return true; + } + } + } + } + + // TODO: raid heals + } + + // Changed so heal based on health percentage is different for hybrids + if( botCasterClass == PALADIN || botCasterClass == BEASTLORD || botCasterClass == RANGER) { + //If AI_EngagedCastCheck() said to the healer that he had to heal + + // check in group + if(caster->HasGroup()) { + Group *g = caster->GetGroup(); + + float hpRatioToHeal = 25.0f; + + switch(caster->GetBotStance()) + { + case BotStanceAggressive: + case BotStanceEfficient: + hpRatioToHeal = 25.0f; + break; + case BotStanceReactive: + case BotStanceBalanced: + hpRatioToHeal = 50.0f; + break; + case BotStanceBurn: + case BotStanceBurnAE: + hpRatioToHeal = 20.0f; + break; + default: + hpRatioToHeal = 25.0f; + break; + } + + if(g) { + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g->members[i] && !g->members[i]->qglobal) { + if(g->members[i]->IsClient() && g->members[i]->GetHPRatio() < hpRatioToHeal) { + if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) + return true; + } + else if((g->members[i]->GetClass() == WARRIOR || g->members[i]->GetClass() == PALADIN || g->members[i]->GetClass() == SHADOWKNIGHT) && g->members[i]->GetHPRatio() < hpRatioToHeal) { + if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) + return true; + } + else if(g->members[i]->GetClass() == ENCHANTER && g->members[i]->GetHPRatio() < hpRatioToHeal) { + if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) + return true; + } + else if(g->members[i]->GetHPRatio() < hpRatioToHeal/2) { + if(caster->AICastSpell(g->members[i], 100, SpellType_Heal)) + return true; + } + } + + if(g->members[i] && !g->members[i]->qglobal && g->members[i]->HasPet() && g->members[i]->GetPet()->GetHPRatio() < 25) { + if(g->members[i]->GetPet()->GetOwner() != caster && caster->IsEngaged() && g->members[i]->IsCasting() && g->members[i]->GetClass() != ENCHANTER ) + continue; + + if(caster->AICastSpell(g->members[i]->GetPet(), 100, SpellType_Heal)) + return true; + } + } + } + } + + // TODO: raid heals + } + } + + //Ok for the buffs.. + if( iSpellTypes == SpellType_Buff) { + uint8 chanceToCast = caster->IsEngaged()?caster->GetChanceToCastBySpellType(SpellType_Buff):100; + // Let's try to make Bard working... + if(botCasterClass == BARD) { + if(caster->AICastSpell(caster, chanceToCast, SpellType_Buff)) + return true; + else + return false; + } + + if(caster->HasGroup()) { + Group *g = caster->GetGroup(); + + if(g) { + for( int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g->members[i]) { + if(caster->AICastSpell(g->members[i], chanceToCast, SpellType_Buff)) + return true; + + if(caster->AICastSpell(g->members[i]->GetPet(), chanceToCast, SpellType_Buff)) + return true; + } + } + } + } + + // TODO: raid buffs + } + + if( iSpellTypes == SpellType_Cure) { + if(caster->HasGroup()) { + Group *g = caster->GetGroup(); + + if(g) { + for( int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g->members[i] && caster->GetNeedsCured(g->members[i])) { + if(caster->AICastSpell(g->members[i], caster->GetChanceToCastBySpellType(SpellType_Cure), SpellType_Cure)) + return true; + else if(botCasterClass == BARD) + return false; + } + + if(g->members[i] && g->members[i]->GetPet() && caster->GetNeedsCured(g->members[i]->GetPet())) { + if(caster->AICastSpell(g->members[i]->GetPet(), (int)caster->GetChanceToCastBySpellType(SpellType_Cure)/4, SpellType_Cure)) + return true; + } + } + } + } + + // TODO: raid buffs + } + + return false; +} + + +Mob* EntityList::GetMobByBotID(uint32 botID) { + Mob* Result = 0; + + if(botID > 0) { + LinkedListIterator iterator(mob_list); + + iterator.Reset(); + + while(iterator.MoreElements()) { + if(iterator.GetData()->IsBot() && iterator.GetData()->CastToBot()->GetBotID() == botID) { + Result = iterator.GetData(); + break; + } + + iterator.Advance(); + } + } + + return Result; +} + +Bot* EntityList::GetBotByBotID(uint32 botID) { + Bot* Result = 0; + + if(botID > 0) { + for(list::iterator botListItr = bot_list.begin(); botListItr != bot_list.end(); botListItr++) { + Bot* tempBot = *botListItr; + + if(tempBot && tempBot->GetBotID() == botID) { + Result = tempBot; + break; + } + } + } + + return Result; +} + +Bot* EntityList::GetBotByBotName(std::string botName) { + Bot* Result = 0; + + if(!botName.empty()) { + for(list::iterator botListItr = bot_list.begin(); botListItr != bot_list.end(); botListItr++) { + Bot* tempBot = *botListItr; + + if(tempBot && std::string(tempBot->GetName()) == botName) { + Result = tempBot; + break; + } + } + } + + return Result; +} + +void EntityList::AddBot(Bot *newBot, bool SendSpawnPacket, bool dontqueue) { + if(newBot) { + newBot->SetID(GetFreeID()); + + if(SendSpawnPacket) { + if(dontqueue) { + // Send immediately + EQApplicationPacket* outapp = new EQApplicationPacket(); + newBot->CreateSpawnPacket(outapp); + outapp->priority = 6; + QueueClients(newBot, outapp, true); + safe_delete(outapp); + } + else { + // Queue the packet + NewSpawn_Struct* ns = new NewSpawn_Struct; + memset(ns, 0, sizeof(NewSpawn_Struct)); + newBot->FillSpawnStruct(ns, newBot); + AddToSpawnQueue(newBot->GetID(), &ns); + safe_delete(ns); + } + + parse->EventNPC(EVENT_SPAWN, newBot, NULL, "", 0); + } + + bot_list.push_back(newBot); + + mob_list.Insert(newBot); + } +} + +list EntityList::GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID) { + list Result; + + if(botOwnerCharacterID > 0) { + for(list::iterator botListItr = bot_list.begin(); botListItr != bot_list.end(); botListItr++) { + Bot* tempBot = *botListItr; + + if(tempBot && tempBot->GetBotOwnerCharacterID() == botOwnerCharacterID) + Result.push_back(tempBot); + } + } + + return Result; +} + +void EntityList::BotPickLock(Bot* rogue) +{ + LinkedListIterator iterator(door_list); + iterator.Reset(); + while(iterator.MoreElements()) { + Doors *cdoor = iterator.GetData(); + if(cdoor && !cdoor->IsDoorOpen()) { + float zdiff = rogue->GetZ() - cdoor->GetZ(); + if(zdiff < 0) + zdiff = 0 - zdiff; + float curdist = 0; + float tmp = rogue->GetX() - cdoor->GetX(); + curdist += (tmp * tmp); + tmp = rogue->GetY() - cdoor->GetY(); + curdist += (tmp * tmp); + if((zdiff < 10) && (curdist <= 130)) { + // All rogue items with lock pick bonuses are hands or primary + const ItemInst* item1 = rogue->GetBotItem(SLOT_HANDS); + const ItemInst* item2 = rogue->GetBotItem(SLOT_PRIMARY); + + float bonus1 = 0.0f; + float bonus2 = 0.0f; + float skill = rogue->GetSkill(PICK_LOCK); + + if(item1) { // Hand slot item + if(item1->GetItem()->SkillModType == PICK_LOCK) { + bonus1 = skill * (((float)item1->GetItem()->SkillModValue) / 100.0f); + } + } + + if(item2) { // Primary slot item + if(item2->GetItem()->SkillModType == PICK_LOCK) { + bonus2 = skill * (((float)item2->GetItem()->SkillModValue) / 100.0f); + } + } + + if((skill+bonus1+bonus2) >= cdoor->GetLockpick()) { + cdoor->ForceOpen(rogue); + } + else { + rogue->Say("I am not skilled enough for this lock."); + } + } + } + iterator.Advance(); + } +} + +bool EntityList::RemoveBot(uint16 entityID) { + bool Result = false; + + if(entityID > 0) { + for(list::iterator botListItr = bot_list.begin(); botListItr != bot_list.end(); botListItr++) + { + Bot* tempBot = *botListItr; + + if(tempBot && tempBot->GetID() == entityID) { + bot_list.erase(botListItr); + Result = true; + break; + } + } + } + + return Result; +} + +void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) { + + const char *WindowTitle = "Bot Tracking Window"; + + string WindowText; + int LastCon = -1; + int CurrentCon = 0; + + uint32 array_counter = 0; + + LinkedListIterator iterator(mob_list); + iterator.Reset(); + + while(iterator.MoreElements()) + { + if (iterator.GetData() && (iterator.GetData()->DistNoZ(*client)<=Distance)) + { + if(iterator.GetData()->IsTrackable()) { + Mob* cur_entity = iterator.GetData(); + int Extras = (cur_entity->IsBot() || cur_entity->IsPet() || cur_entity->IsFamiliar() || cur_entity->IsClient()); + const char *const MyArray[] = { + "a_","an_","Innkeep_","Barkeep_", + "Guard_","Merchant_","Lieutenant_", + "Banker_","Centaur_","Aviak_","Baker_", + "Sir_","Armorer_","Deathfist_","Deputy_", + "Sentry_","Sentinel_","Leatherfoot_", + "Corporal_","goblin_","Bouncer_","Captain_", + "orc_","fire_","inferno_","young_","cinder_", + "flame_","gnomish_","CWG_","sonic_","greater_", + "ice_","dry_","Priest_","dark-boned_", + "Tentacle_","Basher_","Dar_","Greenblood_", + "clockwork_","guide_","rogue_","minotaur_", + "brownie_","Teir'","dark_","tormented_", + "mortuary_","lesser_","giant_","infected_", + "wharf_","Apprentice_","Scout_","Recruit_", + "Spiritist_","Pit_","Royal_","scalebone_", + "carrion_","Crusader_","Trooper_","hunter_", + "decaying_","iksar_","klok_","templar_","lord_", + "froglok_","war_","large_","charbone_","icebone_", + "Vicar_","Cavalier_","Heretic_","Reaver_","venomous_", + "Sheildbearer_","pond_","mountain_","plaguebone_","Brother_", + "great_","strathbone_","briarweb_","strathbone_","skeletal_", + "minion_","spectral_","myconid_","spurbone_","sabretooth_", + "Tin_","Iron_","Erollisi_","Petrifier_","Burynai_", + "undead_","decayed_","You_","smoldering_","gyrating_", + "lumpy_","Marshal_","Sheriff_","Chief_","Risen_", + "lascar_","tribal_","fungi_","Xi_","Legionnaire_", + "Centurion_","Zun_","Diabo_","Scribe_","Defender_","Capt_", + "blazing_","Solusek_","imp_","hexbone_","elementalbone_", + "stone_","lava_","_","" + }; + unsigned int MyArraySize; + for ( MyArraySize = 0; true; MyArraySize++) { //Find empty string & get size + if (!(*(MyArray[MyArraySize]))) break; //Checks for null char in 1st pos + }; + if (NamedOnly) { + bool ContinueFlag = false; + const char *CurEntityName = cur_entity->GetName(); //Call function once + for (int Index = 0; Index < MyArraySize; Index++) { + if (!strncasecmp(CurEntityName, MyArray[Index], strlen(MyArray[Index])) || (Extras)) { + iterator.Advance(); + ContinueFlag = true; + break; //From Index for + }; + }; + if (ContinueFlag) continue; //Moved here or would apply to Index for + }; + + CurrentCon = client->GetLevelCon(cur_entity->GetLevel()); + if(CurrentCon != LastCon) { + + if(LastCon != -1) + WindowText += ""; + + LastCon = CurrentCon; + + switch(CurrentCon) { + + case CON_GREEN: { + WindowText += ""; + break; + } + + case CON_LIGHTBLUE: { + WindowText += ""; + break; + } + case CON_BLUE: { + WindowText += ""; + break; + } + + case CON_YELLOW: { + WindowText += ""; + break; + } + case CON_RED: { + WindowText += ""; + break; + } + default: { + WindowText += ""; + break; + } + } + } + + WindowText += cur_entity->GetCleanName(); + WindowText += "
"; + + if(strlen(WindowText.c_str()) > 4000) { + // Popup window is limited to 4096 characters. + WindowText += "


List truncated ... too many mobs to display"; + break; + } + } + } + + iterator.Advance(); + } + WindowText += "
"; + + client->SendPopupToClient(WindowTitle, WindowText.c_str()); + + return; +} + +uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets) { + uint8 needHealed = 0; + Group *g; + + if(this->HasGroup()) { + g = this->GetGroup(); + + if(g) { + for( int i = 0; imembers[i] && !g->members[i]->qglobal) { + + if(g->members[i]->GetHPRatio() <= hpr) + needHealed++; + + if(includePets) { + if(g->members[i]->GetPet() && g->members[i]->GetPet()->GetHPRatio() <= hpr) { + needHealed++; + } + } + } + } + } + } + + return needHealed; +} + +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; + + 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; +} + +int Bot::GetRawACNoShield(int &shield_ac) +{ + int ac = itembonuses.AC + spellbonuses.AC; + shield_ac = 0; + ItemInst* inst = GetBotItem(SLOT_SECONDARY); + if(inst) + { + if(inst->GetItem()->ItemType == ItemTypeShield) + { + ac -= inst->GetItem()->AC; + shield_ac = inst->GetItem()->AC; + for(uint8 i = 0; i < MAX_AUGMENT_SLOTS; i++) + { + if(inst->GetAugment(i)) + { + ac -= inst->GetAugment(i)->GetItem()->AC; + shield_ac += inst->GetAugment(i)->GetItem()->AC; + } + } + } + } + return ac; +} + +uint32 Bot::CalcCurrentWeight() { + + const Item_Struct* TempItem = 0; + ItemInst* inst; + uint32 Total = 0; + + for(int i=0; i<=21; ++i) { + inst = GetBotItem(i); + if(inst) { + TempItem = inst->GetItem(); + if (TempItem) + Total += TempItem->Weight; + } + } + + float Packrat = (float)spellbonuses.Packrat + (float)aabonuses.Packrat; + + if (Packrat > 0) + Total = (uint32)((float)Total * (1.0f - ((Packrat * 1.0f) / 100.0f))); //AndMetal: 1% per level, up to 5% (calculated from Titanium client). verified thru client that it reduces coin weight by the same % + //without casting to float & back to uint32, this didn't work right + + return Total; +} + +int Bot::GroupLeadershipAAHealthEnhancement() +{ + Group *g = GetGroup(); + + if(!g || (g->GroupCount() < 3)) + return 0; + + switch(g->GetLeadershipAA(groupAAHealthEnhancement)) + { + case 0: + return 0; + case 1: + return 30; + case 2: + return 60; + case 3: + return 100; + } + + return 0; +} + +int Bot::GroupLeadershipAAManaEnhancement() +{ + Group *g = GetGroup(); + + if(!g || (g->GroupCount() < 3)) + return 0; + + switch(g->GetLeadershipAA(groupAAManaEnhancement)) + { + case 0: + return 0; + case 1: + return 30; + case 2: + return 60; + case 3: + return 100; + } + + return 0; +} + +int Bot::GroupLeadershipAAHealthRegeneration() +{ + Group *g = GetGroup(); + + if(!g || (g->GroupCount() < 3)) + return 0; + + switch(g->GetLeadershipAA(groupAAHealthRegeneration)) + { + case 0: + return 0; + case 1: + return 4; + case 2: + return 6; + case 3: + return 8; + } + + return 0; +} + +int Bot::GroupLeadershipAAOffenseEnhancement() +{ + Group *g = GetGroup(); + + if(!g || (g->GroupCount() < 3)) + return 0; + + 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; + } + return 0; +} + +bool Bot::GetNeedsCured(Mob *tar) { + bool needCured = false; + + if(tar) { + if(tar->FindType(SE_PoisonCounter) || tar->FindType(SE_DiseaseCounter) || tar->FindType(SE_CurseCounter) || tar->FindType(SE_CorruptionCounter)) { + uint32 buff_count = GetMaxTotalSlots(); + int buffsWithCounters = 0; + needCured = true; + + for (unsigned int j = 0; j < buff_count; j++) { + if(tar->GetBuffs()[j].spellid != SPELL_UNKNOWN) { + if(CalculateCounters(tar->GetBuffs()[j].spellid) > 0) { + buffsWithCounters++; + + if(buffsWithCounters == 1 && (tar->GetBuffs()[j].ticsremaining < 2 || (int32)((tar->GetBuffs()[j].ticsremaining * 6) / tar->GetBuffs()[j].counters) < 2)) { + // Spell has ticks remaining but may have too many counters to cure in the time remaining; + // We should try to just wait it out. Could spend entire time trying to cure spell instead of healing, buffing, etc. + // Since this is the first buff with counters, don't try to cure. Cure spell will be wasted, as cure will try to + // remove counters from the first buff that has counters remaining. + needCured = false; + break; + } + } + } + } + } + } + + return needCured; +} + +bool Bot::HasOrMayGetAggro() { + bool mayGetAggro = false; + + if(GetTarget() && GetTarget()->GetHateTop()) { + Mob *topHate = GetTarget()->GetHateTop(); + + if(topHate == this) + mayGetAggro = true; //I currently have aggro + else { + uint32 myHateAmt = GetTarget()->GetHateAmount(this); + uint32 topHateAmt = GetTarget()->GetHateAmount(topHate); + + if(myHateAmt > 0 && topHateAmt > 0 && (uint8)((myHateAmt/topHateAmt)*100) > 90) //I have 90% as much hate as top, next action may give me aggro + mayGetAggro = true; + } + } + + return mayGetAggro; +} + +void Bot::SetHasBeenSummoned(bool wasSummoned) { + _hasBeenSummoned = wasSummoned; + if(!wasSummoned) { + _preSummonX = 0; + _preSummonY = 0; + _preSummonZ = 0; + } +} + +void Bot::SetDefaultBotStance() { + BotStanceType defaultStance; + + switch(GetClass()) + { + case DRUID: + case CLERIC: + case SHAMAN: + case ENCHANTER: + case NECROMANCER: + case MAGICIAN: + case WIZARD: + case BEASTLORD: + case BERSERKER: + case MONK: + case ROGUE: + case BARD: + case SHADOWKNIGHT: + case PALADIN: + case RANGER: + defaultStance = BotStanceBalanced; + break; + case WARRIOR: + defaultStance = BotStanceAggressive; + break; + default: + defaultStance = BotStanceBalanced; + break; + } + _baseBotStance = BotStancePassive; + _botStance = defaultStance; +} + +void Bot::BotGroupSay(Mob *speaker, const char *msg, ...) +{ + + char buf[1000]; + va_list ap; + + va_start(ap, msg); + vsnprintf(buf, 1000, msg, ap); + va_end(ap); + + if(speaker->HasGroup()) { + Group *g = speaker->GetGroup(); + + if(g) + g->GroupMessage(speaker->CastToMob(), 0, 100, buf); + } +} + +bool Bot::UseDiscipline(uint32 spell_id, uint32 target) { + //make sure we have the spell... + int r; + /*for(r = 0; r < MAX_PP_DISCIPLINES; r++) { + if(m_pp.disciplines.values[r] == spell_id) + break; + } + if(r == MAX_PP_DISCIPLINES) + return(false); //not found. + + //Check the disc timer + pTimerType DiscTimer = pTimerDisciplineReuseStart + spells[spell_id].EndurTimerIndex; + if(!p_timers.Expired(&database, DiscTimer)) { + uint32 remain = p_timers.GetRemainingTime(DiscTimer); + //Message_StringID(0, DISCIPLINE_CANUSEIN, ConvertArray((remain)/60,val1), ConvertArray(remain%60,val2)); + Message(0, "You can use this discipline in %d minutes %d seconds.", ((remain)/60), (remain%60)); + return(false); + }*/ + + //make sure we can use it.. + if(!IsValidSpell(spell_id)) { + Say("Not a valid spell"); + return(false); + } + + //can we use the spell? + const SPDat_Spell_Struct &spell = spells[spell_id]; + uint8 level_to_use = spell.classes[GetClass() - 1]; + if(level_to_use == 255) { + return(false); + } + + if(level_to_use > GetLevel()) { + return(false); + } + + if(GetEndurance() > spell.EndurCost) { + SetEndurance(GetEndurance() - spell.EndurCost); + } else { + return(false); + } + + if(spell.recast_time > 0) + { + if(CheckDisciplineRecastTimers(this, spells[spell_id].EndurTimerIndex)) { + + //CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT); + if(spells[spell_id].EndurTimerIndex > 0 && spells[spell_id].EndurTimerIndex < MAX_DISCIPLINE_TIMERS) { + SetDisciplineRecastTimer(spells[spell_id].EndurTimerIndex, spell.recast_time); + } + } + else { + uint32 remain = GetDisciplineRemainingTime(this, spells[spell_id].EndurTimerIndex) / 1000; + GetOwner()->Message(0, "%s can use this discipline in %d minutes %d seconds.", GetCleanName(), ((remain)/60), (remain%60)); + return(false); + } + } + + if(IsCasting()) + InterruptSpell(); + + CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT); + + return(true); +} + +void Bot::CreateHealRotation( Mob* target, uint32 timer ) { + SetInHealRotation(true); + SetHealRotationActive(false); + SetNumHealRotationMembers(GetNumHealRotationMembers()+1); + SetHealRotationLeader(this); + SetNextHealRotationMember(this); + SetPrevHealRotationMember(this); + SetHealRotationTimer(timer); + SetHasHealedThisCycle(false); + + if(target) + AddHealRotationTarget(target); +} + +bool Bot::AddHealRotationMember( Bot* healer ) { + if(healer) { + if(GetNumHealRotationMembers() > 0 && GetNumHealRotationMembers() < MaxHealRotationMembers) { + Bot* tempBot = GetPrevHealRotationMember(); + + if(tempBot) { + //add new healer to rotation at end of list + for(int i=0; i<3; i++){ + healer->ClearHealRotationMembers(); + healer->ClearHealRotationTargets(); + healer->AddHealRotationTarget(entity_list.GetMob(_healRotationTargets[i])); // add all targets.. + } + healer->SetHealRotationTimer(tempBot->GetHealRotationTimer()); + healer->SetHealRotationLeader(this); + healer->SetNextHealRotationMember(this); + healer->SetPrevHealRotationMember(tempBot); + healer->SetInHealRotation(true); + healer->SetHasHealedThisCycle(false); + healer->SetHealRotationUseFastHeals(tempBot->GetHealRotationUseFastHeals()); + + //set previous rotation member's next member to new member + tempBot->SetNextHealRotationMember(healer); + + //update leader's previous member (end of list) to new member and update rotation data + SetPrevHealRotationMember(healer); + + list botList = GetBotsInHealRotation(this); + + for(list::iterator botListItr = botList.begin(); botListItr != botList.end(); botListItr++) { + Bot* tempBot = *botListItr; + + if(tempBot) + tempBot->SetNumHealRotationMembers(GetNumHealRotationMembers()+1); + } + + return true; + } + } + } + + return false; +} + +bool Bot::RemoveHealRotationMember( Bot* healer ) { + if(healer && GetNumHealRotationMembers() > 0) { + Bot* leader = healer->GetHealRotationLeader(); + Bot* prevBot = healer->GetPrevHealRotationMember(); + Bot* nextBot = healer->GetNextHealRotationMember(); + + if(healer == this) { + if(nextBot != this) { + //get new leader + leader = nextBot; + } + } + + //remove healer from list + healer->SetHealRotationTimer(0); + healer->ClearHealRotationMembers(); + healer->ClearHealRotationTargets(); + healer->ClearHealRotationLeader(); + healer->SetHasHealedThisCycle(false); + healer->SetHealRotationActive(false); + healer->SetInHealRotation(false); + + if(prevBot && nextBot && GetNumHealRotationMembers() > 1) { + //set previous rotation member's next member to new member + prevBot->SetNextHealRotationMember(nextBot); + + //set previous rotation member's next member to new member + nextBot->SetPrevHealRotationMember(prevBot); + } + + //update rotation data + list botList = GetBotsInHealRotation(leader); + + for(list::iterator botListItr = botList.begin(); botListItr != botList.end(); botListItr++) { + Bot* tempBot = *botListItr; + + if(tempBot) { + tempBot->SetNumHealRotationMembers(GetNumHealRotationMembers()-1); + + if(tempBot->GetHealRotationLeader() != leader) { + // change leader if leader is being removed + tempBot->SetHealRotationLeader(leader); + } + } + } + + return true; + } + + return false; +} + +void Bot::SetHealRotationLeader( Bot* leader ) { + _healRotationLeader = leader->GetBotID(); +} + +void Bot::SetNextHealRotationMember( Bot* healer ) { + _healRotationMemberNext = healer->GetBotID(); +} + +void Bot::SetPrevHealRotationMember( Bot* healer ) { + _healRotationMemberPrev = healer->GetBotID(); +} + +Bot* Bot::GetHealRotationLeader( ) { + if(_healRotationLeader) + return entity_list.GetBotByBotID(_healRotationLeader); + return 0; +} + +Bot* Bot::GetNextHealRotationMember( ) { + if(_healRotationMemberNext) + return entity_list.GetBotByBotID(_healRotationMemberNext); + return 0; +} + +Bot* Bot::GetPrevHealRotationMember( ) { + if(_healRotationMemberNext) + return entity_list.GetBotByBotID(_healRotationMemberPrev); + return 0; +} + +bool Bot::AddHealRotationTarget( Mob* target ) { + if(target) { + + for (int i = 0; i < MaxHealRotationTargets; ++i) { + if(_healRotationTargets[i] > 0) { + Mob* tempTarget = entity_list.GetMob(_healRotationTargets[i]); + + if(!tempTarget) { + _healRotationTargets[i] = 0; + } + else if(!strcasecmp(tempTarget->GetCleanName(), target->GetCleanName())) { + //check to see if target's ID is incorrect (could have zoned, died, etc) + if(tempTarget->GetID() != target->GetID()) { + _healRotationTargets[i] = target->GetID(); + } + //target already in list + return false; + } + } + + if (_healRotationTargets[i] == 0) + { + list botList = GetBotsInHealRotation(this); + + _healRotationTargets[i] = target->GetID(); + + for(list::iterator botListItr = botList.begin(); botListItr != botList.end(); botListItr++) { + Bot* tempBot = *botListItr; + + if(tempBot && tempBot != this) { + //add target to all members + tempBot->AddHealRotationTarget(target, i); + } + } + + return true; + } + } + } + + return false; +} + +bool Bot::AddHealRotationTarget( Mob *target, int index ) { + if (target && index < MaxHealRotationTargets) { + //add target to list of targets at specified index + _healRotationTargets[index] = target->GetID(); + return true; + } + return false; +} + +bool Bot::RemoveHealRotationTarget( Mob* target ) { + int index = 0; + bool removed = false; + if(target) { + //notify all heal rotation members to remove target + for(int i=0; iGetID()) { + list botList = GetBotsInHealRotation(this); + _healRotationTargets[i] = 0; + index = i; + removed = true; + + for(list::iterator botListItr = botList.begin(); botListItr != botList.end(); botListItr++) { + Bot* tempBot = *botListItr; + + if(tempBot) + tempBot->RemoveHealRotationTarget(i); + } + } + } + } + + return removed; +} + +bool Bot::RemoveHealRotationTarget( int index ) { + if(index >= 0) { + //clear rotation target at index + _healRotationTargets[index] = 0; + + if(index < MaxHealRotationTargets) { + for(int i=index; i 0) { + + //get first target in list + target = entity_list.GetMob(_healRotationTargets[i]); + + if(target) { + //check if valid target + if(target->GetZoneID() == GetZoneID() + && !(target->GetAppearance() == eaDead + && !(target->IsClient() && target->CastToClient()->GetFeigned()))) { + + count++; + + //get first valid target + if(!first) { + first = target; + } + + //check to see if target is group main tank + //(target first, in case top target has died and was rez'd - + //we don't want to heal them then) + if(!tank) { + Group* g = target->GetGroup(); + if(g && !strcasecmp(g->GetMainTankName(), target->GetCleanName())) { + tank = target; + } + } + } + } + else { + //if not valid target, remove from list + if(removeIndex == 0) + removeIndex = i; + } + } + } + + if (removeIndex > 0) { + RemoveHealRotationTarget( removeIndex ); + } + + if(tank) + return tank; + + return first; +} + +Mob* Bot::GetHealRotationTarget( uint8 index ) { + Mob* target = NULL; + + if(_healRotationTargets[index] > 0) { + //get target at specified index + target = entity_list.GetMob(_healRotationTargets[index]); + } + + return target; +} + +list Bot::GetBotsInHealRotation(Bot* rotationLeader) { + list Result; + + if(rotationLeader != NULL) { + Result.push_back(rotationLeader); + Bot* rotationMember = rotationLeader->GetNextHealRotationMember(); + + while(rotationMember && rotationMember != rotationLeader) { + Result.push_back(rotationMember); + rotationMember = rotationMember->GetNextHealRotationMember(); + } + } + + return Result; +} + +void Bot::NotifyNextHealRotationMember(bool notifyNow) { + //check if we need to notify to start now, or after timer + uint32 nextHealTime = notifyNow ? Timer::GetCurrentTime() : Timer::GetCurrentTime() + GetHealRotationTimer(); + + Bot* nextMember = GetNextHealRotationMember(); + if(nextMember && nextMember != this) { + nextMember->SetHealRotationNextHealTime(nextHealTime); + nextMember->SetHasHealedThisCycle(false); + } +} + +void Bot::BotHealRotationsClear(Client* c) { + if(c) { + std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); + + for(std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); botListItr++) { + Bot* tempBot = *botListItr; + if(tempBot->GetInHealRotation()) { + //clear all heal rotation data for bots in a heal rotation + tempBot->SetInHealRotation(false); + tempBot->SetHealRotationActive(false); + tempBot->SetHasHealedThisCycle(false); + tempBot->SetHealRotationTimer(0); + tempBot->ClearHealRotationMembers(); + tempBot->ClearHealRotationTargets(); + tempBot->SetNumHealRotationMembers(0); + tempBot->ClearHealRotationLeader(); + } + } + } +} + +#endif diff --git a/zone/bot.h b/zone/bot.h new file mode 100644 index 000000000..2d916f9f6 --- /dev/null +++ b/zone/bot.h @@ -0,0 +1,691 @@ +#ifndef BOT_H +#define BOT_H + +#ifdef BOTS + +#include "botStructs.h" +#include "mob.h" +#include "client.h" +#include "pets.h" +#include "groups.h" +#include "PlayerCorpse.h" +#include "zonedb.h" +#include "StringIDs.h" +#include "../common/MiscFunctions.h" +#include "../common/debug.h" +#include "guild_mgr.h" +#include "worldserver.h" + +#include + +using namespace std; + +extern bool spells_loaded; +extern WorldServer worldserver; + +const int BotAISpellRange = 100; // TODO: Write a method that calcs what the bot's spell range is based on spell, equipment, AA, whatever and replace this +const int MaxSpellTimer = 15; +const int MaxDisciplineTimer = 10; +const int DisciplineReuseStart = MaxSpellTimer + 1; +const int MaxTimer = MaxSpellTimer + MaxDisciplineTimer; +const int MaxStances = 7; +const int MaxSpellTypes = 16; +const int MaxHealRotationMembers = 6; +const int MaxHealRotationTargets = 3; + +typedef enum BotStanceType { + BotStancePassive, + BotStanceBalanced, + BotStanceEfficient, + BotStanceReactive, + BotStanceAggressive, + BotStanceBurn, + BotStanceBurnAE +}; + +typedef enum SpellTypeIndex { + SpellType_NukeIndex, + SpellType_HealIndex, + SpellType_RootIndex, + SpellType_BuffIndex, + SpellType_EscapeIndex, + SpellType_PetIndex, + SpellType_LifetapIndex, + SpellType_SnareIndex, + SpellType_DOTIndex, + SpellType_DispelIndex, + SpellType_InCombatBuffIndex, + SpellType_MezIndex, + SpellType_CharmIndex, + SpellType_SlowIndex, + SpellType_DebuffIndex, + SpellType_CureIndex +}; + +class Bot : public NPC { +public: + // Class enums + typedef enum BotfocusType { //focus types + BotfocusSpellHaste = 1, + BotfocusSpellDuration, + BotfocusRange, + BotfocusReagentCost, + BotfocusManaCost, + BotfocusImprovedHeal, + BotfocusImprovedDamage, + BotfocusImprovedDOT, //i dont know about this... + BotfocusImprovedDamage2, + BotfocusImprovedUndeadDamage, + BotfocusPetPower, + BotfocusResistRate, + BotfocusSpellHateMod, + BotfocusTriggerOnCast, + BotfocusSpellVulnerability, + BotfocusTwincast, + BotfocusSympatheticProc, + BotfocusSpellDamage, + BotfocusFF_Damage_Amount, + BotfocusSpellDurByTic, + BotfocusSwarmPetDuration, + BotfocusReduceRecastTime, + BotfocusBlockNextSpell, + BotfocusHealRate, + BotfocusAdditionalDamage, + BotfocusSpellEffectiveness, + BotfocusIncreaseNumHits, + BotfocusCriticalHealRate, + BotfocusAdditionalHeal2, + BotfocusAdditionalHeal, + }; + + typedef enum BotTradeType { // types of trades a bot can do + BotTradeClientNormal, + BotTradeClientNoDropNoTrade + }; + + typedef enum BotRoleType { + BotRoleMainAssist, + BotRoleGroupHealer, + BotRoleRaidHealer + }; + + typedef enum EqExpansions { + ExpansionNone, + ExpansionEQ, + ExpansionRoK, + ExpansionSoV, + ExpansionSoL, + ExpansionPoP, + ExpansionLoY, + ExpansionLDoN, + ExpansionGoD, + ExpansionOoW, + ExpansionDoN, + ExpansionDoDH, + ExpansionPoR, + ExpansionTSS, + ExpansionSoF, + ExpansionSoD, + ExpansionUF, + ExpansionHoT, + ExpansionVoA, + ExpansionRoF + }; + + // Class Constructors + Bot(NPCType npcTypeData, Client* botOwner); + Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType npcTypeData); + + //abstract virtual function implementations requird by base abstract class + virtual void Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillType attack_skill); + virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false); + virtual bool Attack(Mob* other, int Hand = 13, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false); + virtual bool HasRaid() { return (GetRaid() ? true : false); } + virtual bool HasGroup() { return (GetGroup() ? true : false); } + virtual Raid* GetRaid() { return entity_list.GetRaidByMob(this); } + virtual Group* GetGroup() { return entity_list.GetGroupByMob(this); } + + // Common, but informal "interfaces" with Client object + uint32 CharacterID() { return GetBotID(); } // Just returns the Bot Id + inline bool IsInAGuild() const { return (_guildId != GUILD_NONE && _guildId != 0); } + inline bool IsInGuild(uint32 in_gid) const { return (in_gid == _guildId && IsInAGuild()); } + inline uint32 GuildID() const { return _guildId; } + inline uint8 GuildRank() const { return _guildRank; } + + // Class Methods + bool IsValidRaceClassCombo(); + bool IsValidName(); + bool IsBotNameAvailable(std::string* errorMessage); + bool DeleteBot(std::string* errorMessage); + void Spawn(Client* botCharacterOwner, std::string* errorMessage); + virtual void SetLevel(uint8 in_level, bool command = false); + virtual void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); + virtual bool Process(); + void FinishTrade(Client* client, BotTradeType tradeType); + virtual bool Save(); + virtual void Depop(); + void CalcBotStats(bool showtext = true); + uint16 BotGetSpells(int spellslot) { return AIspells[spellslot].spellid; } + uint16 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; } + uint16 BotGetSpellPriority(int spellslot) { return AIspells[spellslot].priority; } + virtual float GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand); + virtual bool AvoidDamage(Mob* other, int32 &damage, bool CanRiposte); + virtual int GetMonkHandToHandDamage(void); + virtual void TryCriticalHit(Mob *defender, uint16 skill, int32 &damage); + virtual bool TryFinishingBlow(Mob *defender, SkillType skillinuse); + virtual void DoRiposte(Mob* defender); + inline virtual int16 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK + ((GetSTR() + GetSkill(OFFENSE)) * 9 / 10); } + inline virtual int16 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; } + uint16 GetTotalATK(); + uint16 GetATKRating(); + uint16 GetPrimarySkillValue(); + uint16 MaxSkill(SkillType skillid, uint16 class_, uint16 level) const; + inline uint16 MaxSkill(SkillType skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } + virtual void MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit); + virtual void DoSpecialAttackDamage(Mob *who, SkillType skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance=false); + virtual void TryBackstab(Mob *other,int ReuseTime = 10); + virtual void RogueBackstab(Mob* other, bool min_damage = false, int ReuseTime = 10); + virtual void RogueAssassinate(Mob* other); + virtual void DoClassAttacks(Mob *target, bool IsRiposte=false); + virtual bool TryHeadShot(Mob* defender, SkillType skillInUse); + virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillType skillinuse, int16 chance_mod=0, int16 focus=0, bool CanRiposte=false); + virtual void ApplySpecialAttackMod(SkillType skill, int32 &dmg, int32 &mindmg); + bool CanDoSpecialAttack(Mob *other); + virtual int32 CheckAggroAmount(uint16 spellid); + virtual void CalcBonuses(); + void CalcItemBonuses(); + virtual void MakePet(uint16 spell_id, const char* pettype, const char *petname = NULL); + virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther); + inline virtual bool IsPet() { return false; } + virtual bool IsNPC() const { return false; } + virtual Mob* GetOwner(); + virtual Mob* GetOwnerOrSelf(); + inline virtual bool HasOwner() { return (GetBotOwner() ? true : false); } + virtual int32 CheckHealAggroAmount(uint16 spellid, uint32 heal_possible = 0); + virtual int32 CalcMaxMana(); + virtual void SetAttackTimer(); + uint32 GetClassHPFactor(); + virtual int32 CalcMaxHP(); + bool DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool &stopLogic); + bool DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool &stopLogic); + bool DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, uint16 slot, bool &stopLogic); + void SendBotArcheryWearChange(uint8 material_slot, uint32 material, uint32 color); + void Camp(bool databaseSave = true); + virtual void AddToHateList(Mob* other, int32 hate = 0, int32 damage = 0, bool iYellForHelp = true, bool bFrenzy = false, bool iBuffTic = false); + virtual void SetTarget(Mob* mob); + virtual void Zone(); + std::vector GetBotSpells() { return AIspells; } + bool IsArcheryRange(Mob* target); + void ChangeBotArcherWeapons(bool isArcher); + void Sit(); + void Stand(); + bool IsSitting(); + bool IsStanding(); + bool IsBotCasterCombatRange(Mob *target); + bool CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ = true) ; + bool UseDiscipline(uint32 spell_id, uint32 target); + uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets); + bool GetNeedsCured(Mob *tar); + bool HasOrMayGetAggro(); + void SetDefaultBotStance(); + void CalcChanceToCast(); + void CreateHealRotation( Mob* target, uint32 timer = 10000 ); + bool AddHealRotationMember( Bot* healer ); + bool RemoveHealRotationMember( Bot* healer ); + bool AddHealRotationTarget( Mob* target ); + //bool AddHealRotationTarget( const char *targetName, int index); + bool AddHealRotationTarget( Mob* target, int index); + bool RemoveHealRotationTarget( Mob* target ); + bool RemoveHealRotationTarget( int index); + void NotifyNextHealRotationMember( bool notifyNow = false ); + void ClearHealRotationLeader() { _healRotationLeader = NULL; } + 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(); + int32 CalcHPRegenCap(); + int32 CalcManaRegenCap(); + int32 LevelRegen(); + int32 CalcHPRegen(); + int32 CalcManaRegen(); + uint32 CalcCurrentWeight(); + int GroupLeadershipAAHealthEnhancement(); + int GroupLeadershipAAManaEnhancement(); + int GroupLeadershipAAHealthRegeneration(); + int GroupLeadershipAAOffenseEnhancement(); + void CalcRestState(); + int32 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(); + 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 + + // AI Methods + virtual bool AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes); + virtual bool AI_EngagedCastCheck(); + virtual bool AI_PursueCastCheck(); + virtual bool AI_IdleCastCheck(); + bool AIHealRotation(Mob* tar, bool useFastHeals); + + // Mob AI Virtual Override Methods + virtual void AI_Process(); + virtual void AI_Stop(); + + // Mob Spell Virtual Override Methods + virtual void SpellProcess(); + int32 Additional_SpellDmg(uint16 spell_id, bool bufftick = false); + int32 Additional_Heal(uint16 spell_id); + virtual int32 GetActSpellDamage(uint16 spell_id, int32 value); + virtual int32 GetActSpellHealing(uint16 spell_id, int32 value); + virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime); + virtual int32 GetActSpellCost(uint16 spell_id, int32 cost); + virtual float GetActSpellRange(uint16 spell_id, float range); + virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration); + virtual float GetAOERange(uint16 spell_id); + virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100); + virtual void DoBuffTic(uint16 spell_id, 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 = NULL); + 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); + + // Bot Action Command Methods + bool MesmerizeTarget(Mob* target); + bool Bot_Command_Resist(int resisttype, int level); + bool Bot_Command_DireTarget(int diretype, Mob *target); + bool Bot_Command_CharmTarget(int charmtype, Mob *target); + bool Bot_Command_CalmTarget(Mob *target); + bool Bot_Command_RezzTarget(Mob *target); + bool Bot_Command_Cure(int curetype, int level); + + // Bot Equipment & Inventory Class Methods + void BotTradeSwapItem(Client* client, int16 lootSlot, const ItemInst* inst, const ItemInst* inst_swap, uint32 equipableSlots, std::string* errorMessage, bool swap = true); + void BotTradeAddItem(uint32 id, const ItemInst* inst, int16 charges, uint32 equipableSlots, uint16 lootSlot, std::string* errorMessage, bool addToDb = true); + void EquipBot(std::string* errorMessage); + bool CheckLoreConflict(const Item_Struct* item); + uint32 GetEquipmentColor(uint8 material_slot) const; + + // Static Class Methods + static void SaveBotGroup(Group* botGroup, std::string botGroupName, std::string* errorMessage); + static void DeleteBotGroup(std::string botGroupName, std::string* errorMessage); + static std::list LoadBotGroup(std::string botGroupName, std::string* errorMessage); + static uint32 CanLoadBotGroup(uint32 botOwnerCharacterId, std::string botGroupName, std::string* errorMessage); + static uint32 GetBotGroupIdByBotGroupName(std::string botGroupName, std::string* errorMessage); + static uint32 GetBotGroupLeaderIdByBotGroupName(std::string botGroupName); + static std::list GetBotGroupListByBotOwnerCharacterId(uint32 botOwnerCharacterId, std::string* errorMessage); + static bool DoesBotGroupNameExist(std::string botGroupName); + //static void DestroyBotRaidObjects(Client* client); // Can be removed after bot raids are dumped + static uint32 GetBotIDByBotName(std::string botName); + static Bot* LoadBot(uint32 botID, std::string* errorMessage); + static std::list GetBotList(uint32 botOwnerCharacterID, std::string* errorMessage); + static void ProcessBotCommands(Client *c, const Seperator *sep); + static std::list ListSpawnedBots(uint32 characterID, std::string* errorMessage); + static uint32 SpawnedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage); + static uint32 CreatedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage); + static uint32 AllowedBotSpawns(uint32 botOwnerCharacterID, std::string* errorMessage); + static uint32 GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage); + //static bool SetBotOwnerCharacterID(uint32 botID, uint32 botOwnerCharacterID, std::string* errorMessage); + static std::string ClassIdToString(uint16 classId); + static std::string RaceIdToString(uint16 raceId); + static bool IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined); + static void BotGroupOrderFollow(Group* group, Client* client); + static void BotGroupOrderGuard(Group* group, Client* client); + static void BotGroupOrderAttack(Group* group, Mob* target, Client* client); + static void BotGroupSummon(Group* group, Client* client); + static Bot* GetBotByBotClientOwnerAndBotName(Client* c, std::string botName); + static void ProcessBotGroupInvite(Client* c, std::string botName); + static void ProcessBotGroupDisband(Client* c, std::string botName); + static void BotOrderCampAll(Client* c); + static void BotHealRotationsClear( Client* c ); + static void ProcessBotInspectionRequest(Bot* inspectedBot, Client* client); + static std::list GetGroupedBotsByGroupId(uint32 groupId, std::string* errorMessage); + static void LoadAndSpawnAllZonedBots(Client* botOwner); + static bool GroupHasBot(Group* group); + static Bot* GetFirstBotInGroup(Group* group); + static void ProcessClientZoneChange(Client* botOwner); + static void ProcessBotOwnerRefDelete(Mob* botOwner); // Removes a Client* reference when the Client object is destroyed + static void ProcessGuildInvite(Client* guildOfficer, Bot* botToGuild); // Processes a client's request to guild a bot + static bool ProcessGuildRemoval(Client* guildOfficer, std::string botName); // Processes a client's request to deguild a bot + static int32 GetSpellRecastTimer(Bot *caster, int timer_index); + static bool CheckSpellRecastTimers(Bot *caster, int SpellIndex); + static int32 GetDisciplineRecastTimer(Bot *caster, int timer_index); + static bool CheckDisciplineRecastTimers(Bot *caster, int timer_index); + static uint32 GetDisciplineRemainingTime(Bot *caster, int timer_index); + static std::list GetBotSpellsForSpellEffect(Bot* botCaster, int spellEffect); + static std::list GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, int spellEffect, SpellTargetType targetType); + static std::list GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType); + static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType); + static BotSpell GetBestBotSpellForFastHeal(Bot* botCaster); + static BotSpell GetBestBotSpellForHealOverTime(Bot* botCaster); + static BotSpell GetBestBotSpellForPercentageHeal(Bot* botCaster); + static BotSpell GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster); + static BotSpell GetFirstBotSpellForSingleTargetHeal(Bot* botCaster); + static BotSpell GetBestBotSpellForGroupHealOverTime(Bot* botCaster); + static BotSpell GetBestBotSpellForGroupCompleteHeal(Bot* botCaster); + static BotSpell GetBestBotSpellForGroupHeal(Bot* botCaster); + static BotSpell GetBestBotSpellForMagicBasedSlow(Bot* botCaster); + static BotSpell GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster); + static Mob* GetFirstIncomingMobToMez(Bot* botCaster, BotSpell botSpell); + static BotSpell GetBestBotSpellForMez(Bot* botCaster); + static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster); + static std::string GetBotMagicianPetType(Bot* botCaster); + static BotSpell GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType); + static BotSpell GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType); + static BotSpell GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target); + static BotSpell GetDebuffBotSpell(Bot* botCaster, Mob* target); + static BotSpell GetBestBotSpellForCure(Bot* botCaster, Mob* target); + static BotSpell GetBestBotSpellForResistDebuff(Bot* botCaster, Mob* target); + static NPCType CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender); + static std::list GetBotsInHealRotation( Bot* leader ); + + // Static Bot Group Methods + static bool AddBotToGroup(Bot* bot, Group* group); + static bool RemoveBotFromGroup(Bot* bot, Group* group); + static bool BotGroupCreate(std::string botGroupLeaderName); + static bool BotGroupCreate(Bot* botGroupLeader); + static bool GroupHasClass(Group* group, uint8 classId); + static bool GroupHasClericClass(Group* group) { return GroupHasClass(group, CLERIC); } + static bool GroupHasDruidClass(Group* group) { return GroupHasClass(group, DRUID); } + static bool GroupHasShamanClass(Group* group) { return GroupHasClass(group, SHAMAN); } + static bool GroupHasEnchanterClass(Group* group) { return GroupHasClass(group, ENCHANTER); } + static bool GroupHasPriestClass(Group* group) { return GroupHasClass(group, CLERIC | DRUID | SHAMAN); } + static void BotGroupSay(Mob *speaker, const char *msg, ...); + + // "GET" Class Methods + uint32 GetBotID() const { return _botID; } + uint32 GetBotOwnerCharacterID() { return _botOwnerCharacterID; } + uint32 GetBotSpellID() { return npc_spells_id; } + Mob* GetBotOwner() { return this->_botOwner; } + uint32 GetBotArcheryRange(); + ItemInst* GetBotItem(uint32 slotID); + virtual bool GetSpawnStatus() { return _spawnStatus; } + uint8 GetPetChooserID() { return _petChooserID; } + bool IsPetChooser() { return _petChooser; } + bool IsBotArcher() { return _botArcher; } + bool IsBotCharmer() { return _botCharmer; } + virtual bool IsBot() const { return true; } + bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; } + BotRoleType GetBotRole() { return _botRole; } + BotStanceType GetBotStance() { return _botStance; } + uint8 GetChanceToCastBySpellType(uint16 spellType); + bool IsGroupPrimaryHealer(); + bool IsGroupPrimarySlower(); + bool IsBotCaster() { return (GetClass() == CLERIC || GetClass() == DRUID || GetClass() == SHAMAN || GetClass() == NECROMANCER || GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == ENCHANTER); } + bool IsBotINTCaster() { return (GetClass() == NECROMANCER || GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == ENCHANTER); } + bool IsBotWISCaster() { return (GetClass() == CLERIC || GetClass() == DRUID || GetClass() == SHAMAN); } + bool CanHeal(); + int GetRawACNoShield(int &shield_ac); + void LoadAAs(); + uint32 GetAA(uint32 aa_id); + void ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon); + bool GetHasBeenSummoned() { return _hasBeenSummoned; } + float GetPreSummonX() { return _preSummonX; } + float GetPreSummonY() { return _preSummonY; } + float GetPreSummonZ() { return _preSummonZ; } + bool GetGroupMessagesOn() { return _groupMessagesOn; } + bool GetInHealRotation() { return _isInHealRotation; } + bool GetHealRotationActive() { return (GetInHealRotation() && _isHealRotationActive); } + bool GetHealRotationUseFastHeals() { return _healRotationUseFastHeals; } + bool GetHasHealedThisCycle() { return _hasHealedThisCycle; } + Mob* GetHealRotationTarget(); + Mob* GetHealRotationTarget(uint8 index); + Bot* GetHealRotationLeader(); + Bot* GetNextHealRotationMember(); + Bot* GetPrevHealRotationMember(); + uint8 GetNumHealRotationMembers () { return _numHealRotationMembers; } + uint32 GetHealRotationNextHealTime() { return _healRotationNextHeal; } + uint32 GetHealRotationTimer () { return _healRotationTimer; } + 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; } + //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; } + // 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; } + // 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 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 int16 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath; } + + inline InspectMessage_Struct& GetInspectMessage() { return _botInspectMessage; } + inline const InspectMessage_Struct& GetInspectMessage() const { return _botInspectMessage; } + + // "SET" Class Methods + void SetBotSpellID(uint32 newSpellID); + virtual void SetSpawnStatus(bool spawnStatus) { _spawnStatus = spawnStatus; } + void SetPetChooserID(uint8 id) { _petChooserID = id; } + void SetBotArcher(bool a) { _botArcher = a; } + void SetBotCharmer(bool c) { _botCharmer = c; } + void SetPetChooser(bool p) { _petChooser = p; } + void SetBotOwner(Mob* botOwner) { this->_botOwner = botOwner; } + // void SetBotOwnerCharacterID(uint32 botOwnerCharacterID) { _botOwnerCharacterID = botOwnerCharacterID; } + void SetRangerAutoWeaponSelect(bool enable) { GetClass() == RANGER ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; } + void SetBotRole(BotRoleType botRole) { _botRole = botRole; } + void SetBotStance(BotStanceType botStance) { _botStance = botStance; } + void SetSpellRecastTimer(int timer_index, int32 recast_delay); + void SetDisciplineRecastTimer(int timer_index, int32 recast_delay); + void SetHasBeenSummoned(bool s); + void SetPreSummonX(float x) { _preSummonX = x; } + void SetPreSummonY(float y) { _preSummonY = y; } + void SetPreSummonZ(float z) { _preSummonZ = z; } + void SetGroupMessagesOn(bool groupMessagesOn) { _groupMessagesOn = groupMessagesOn; } + void SetInHealRotation( bool inRotation ) { _isInHealRotation = inRotation; } + void SetHealRotationActive( bool isActive ) { _isHealRotationActive = isActive; } + void SetHealRotationUseFastHeals( bool useFastHeals ) { _healRotationUseFastHeals = useFastHeals; } + void SetHasHealedThisCycle( bool hasHealed ) { _hasHealedThisCycle = hasHealed; } + void SetHealRotationLeader( Bot* leader ); + void SetNextHealRotationMember( Bot* healer ); + void SetPrevHealRotationMember( Bot* healer ); + void SetHealRotationNextHealTime( uint32 nextHealTime ) { _healRotationNextHeal = nextHealTime; } + void SetHealRotationTimer( uint32 timer ) { _healRotationTimer = timer; } + void SetNumHealRotationMembers( uint8 numMembers ) { _numHealRotationMembers = numMembers; } + + // Class Destructors + virtual ~Bot(); + +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); + 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 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); + + static void SetBotGuildMembership(uint32 botId, uint32 guildid, uint8 rank); + +private: + // Class Members + uint32 _botID; + uint32 _botOwnerCharacterID; + //uint32 _botSpellID; + bool _spawnStatus; + Mob* _botOwner; + bool _botOrderAttack; + bool _botArcher; + bool _botCharmer; + bool _petChooser; + uint8 _petChooserID; + bool berserk; + Inventory m_inv; + double _lastTotalPlayTime; + time_t _startTotalPlayTime; + Mob* _previousTarget; + uint32 _guildId; + uint8 _guildRank; + std::string _guildName; + uint32 _lastZoneId; + bool _rangerAutoWeaponSelect; + BotRoleType _botRole; + BotStanceType _botStance; + BotStanceType _baseBotStance; + unsigned int RestRegenHP; + unsigned int RestRegenMana; + unsigned int RestRegenEndurance; + Timer rest_timer; + int32 base_end; + int32 cur_end; + int32 max_end; + int16 end_regen; + uint32 timers[MaxTimer]; + bool _hasBeenSummoned; + float _preSummonX; + float _preSummonY; + float _preSummonZ; + uint8 _spellCastingChances[MaxStances][MaxSpellTypes]; + bool _groupMessagesOn; + bool _isInHealRotation; + bool _isHealRotationActive; + bool _healRotationUseFastHeals; + bool _hasHealedThisCycle; + uint32 _healRotationTimer; + uint32 _healRotationNextHeal; + //char _healRotationTargets[MaxHealRotationTargets][64]; + uint16 _healRotationTargets[MaxHealRotationTargets]; + uint32 _healRotationLeader; + uint32 _healRotationMemberNext; + uint32 _healRotationMemberPrev; + uint8 _numHealRotationMembers; + std::map botAAs; + InspectMessage_Struct _botInspectMessage; + + // 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; + 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(); + void GenerateBaseStats(); + void GenerateAppearance(); + void GenerateArmorClass(); + int32 GenerateBaseHitPoints(); + void GenerateAABonuses(StatBonuses* newbon); + int32 GenerateBaseManaPoints(); + void GenerateSpecialAttacks(); + void SetBotID(uint32 botID); + + // Private "Inventory" Methods + void GetBotItems(std::string* errorMessage, Inventory &inv); + void BotRemoveEquipItem(int slot); + void BotAddEquipItem(int slot, uint32 id); + uint32 GetBotItemBySlot(uint32 slotID); + void RemoveBotItemBySlot(uint32 slotID, std::string* errorMessage); + void SetBotItemInSlot(uint32 slotID, uint32 itemID, const ItemInst* inst, std::string* errorMessage); + uint32 GetBotItemsCount(std::string* errorMessage); + uint32 GetTotalPlayTime(); + void SaveBuffs(); // Saves existing buffs to the database to persist zoning and camping + void LoadBuffs(); // Retrieves saved buffs from the database on spawning + void LoadPetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId); + void SavePetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId); + void LoadPetItems(uint32* petItems, uint32 botPetSaveId); + void SavePetItems(uint32* petItems, uint32 botPetSaveId); + void LoadPetStats(std::string* petName, uint16* petMana, uint16* petHitPoints, uint32* botPetId, uint32 botPetSaveId); + uint32 SavePetStats(std::string petName, uint16 petMana, uint16 petHitPoints, uint32 botPetId); + void LoadPet(); // Load and spawn bot pet if there is one + void SavePet(); // Save and depop bot pet if there is one + uint32 GetPetSaveId(); + void DeletePetBuffs(uint32 botPetSaveId); + void DeletePetItems(uint32 botPetSaveId); + void DeletePetStats(uint32 botPetSaveId); + void LoadGuildMembership(uint32* guildId, uint8* guildRank, std::string* guildName); + void LoadStance(); + void SaveStance(); + void LoadTimers(); + void SaveTimers(); +}; + +#endif // BOTS + +#endif // BOT_H diff --git a/zone/botStructs.h b/zone/botStructs.h new file mode 100644 index 000000000..fd7294da4 --- /dev/null +++ b/zone/botStructs.h @@ -0,0 +1,50 @@ +#ifndef BOT_STRUCTS +#define BOT_STRUCTS + +#ifdef BOTS + +#include "../common/types.h" + +#include + +using namespace std; + +struct BotsAvailableList { + uint32 BotID; + char BotName[64]; + uint16 BotClass; + uint8 BotLevel; + uint16 BotRace; +}; + +struct BotGroup { + uint32 BotGroupID; + uint32 BotID; +}; + +struct BotGroupList { + std::string BotGroupName; + std::string BotGroupLeaderName; +}; + +struct SpawnedBotsList { + char BotName[64]; + char ZoneName[64]; + uint32 BotLeaderCharID; +}; + +struct BotSpell { + uint16 SpellId; + int SpellIndex; + int16 ManaCost; +}; + +struct BotAA { + uint32 aa_id; + uint8 req_level; + uint8 total_levels; +}; + +#endif // BOTS + +#endif // BOT_STRUCTS diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp new file mode 100644 index 000000000..64937b92d --- /dev/null +++ b/zone/botspellsai.cpp @@ -0,0 +1,3266 @@ +#ifdef BOTS + +#include "bot.h" + +bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { + _ZP(Bot_AICastSpell); + + if (!tar) { + return false; + } + + if(!AI_HasSpells()) + return false; + + if (iChance < 100) { + if (MakeRandomInt(0, 100) > iChance){ + return false; + } + } + + if(tar->GetAppearance() == eaDead) { + if((tar->IsClient() && tar->CastToClient()->GetFeigned()) || tar->IsBot()) { + // do nothing + } + else { + return false; + } + } + + uint8 botClass = GetClass(); + uint8 botLevel = 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 castedSpell = false; + + BotSpell botSpell; + botSpell.SpellId = 0; + botSpell.SpellIndex = 0; + botSpell.ManaCost = 0; + + switch (iSpellTypes) { + case SpellType_Mez: { + if (tar->GetBodyType() != BT_Giant) { + if(!checked_los) { + if(!CheckLosFN(tar)) + break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + + checked_los = true; + } + + //TODO + //Check if single target or AoE mez is best + //if (TARGETS ON MT IS => 3 THEN botSpell = AoEMez) + //if (TARGETS ON MT IS <= 2 THEN botSpell = BestMez) + + botSpell = GetBestBotSpellForMez(this); + + if(botSpell.SpellId == 0) + break; + + Mob* addMob = GetFirstIncomingMobToMez(this, botSpell); + + if(!addMob){ + //Say("!addMob."); + break;} + + if(!(!addMob->IsImmuneToSpell(botSpell.SpellId, this) && addMob->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) + break; + + castedSpell = AIDoSpellCast(botSpell.SpellIndex, addMob, botSpell.ManaCost); + + if(castedSpell) { + char* gmsg = 0; + + MakeAnyLenString(&gmsg, "Attempting to mez %s.", addMob->GetCleanName()); + + if(gmsg && GetGroupMessagesOn()) + BotGroupSay(this, gmsg); + } + } + break; + } + case SpellType_Heal: { + if (tar->DontHealMeBefore() < Timer::GetCurrentTime()) { + uint8 hpr = (uint8)tar->GetHPRatio(); + bool hasAggro = false; + bool isPrimaryHealer = false; + + if(HasGroup()) { + isPrimaryHealer = IsGroupPrimaryHealer(); + } + + if(hpr < 95 || (tar->IsClient() && (hpr < 95)) || (botClass == BARD)) { + if(tar->GetClass() == NECROMANCER) { + // Give necromancers a chance to go lifetap something or cleric can spend too much mana on a necro + if(hpr >= 40) { + break; + } + } + + if(tar->GetClass() == SHAMAN) { + // Give shaman the chance to canni without wasting the cleric's mana + if(hpr >= 80) { + break; + } + } + + // Evaluate the situation + if((IsEngaged()) && ((botClass == CLERIC) || (botClass == DRUID) || (botClass == SHAMAN) || (botClass == PALADIN))) { + if(tar->GetTarget() && tar->GetTarget()->GetHateTop() && tar->GetTarget()->GetHateTop() == tar) { + hasAggro = true; + } + + if(hpr < 35) { + botSpell = GetBestBotSpellForFastHeal(this); + } + else if(hpr >= 35 && hpr < 70){ + if(GetNumberNeedingHealedInGroup(60, false) >= 3) + botSpell = GetBestBotSpellForGroupHeal(this); + + if(botSpell.SpellId == 0) + botSpell = GetBestBotSpellForPercentageHeal(this); + } + else if(hpr >= 70 && hpr < 95){ + if(GetNumberNeedingHealedInGroup(80, false) >= 3) + botSpell = GetBestBotSpellForGroupHealOverTime(this); + + if(hasAggro) + botSpell = GetBestBotSpellForPercentageHeal(this); + } + else { + if(!tar->FindType(SE_HealOverTime)) + botSpell = GetBestBotSpellForHealOverTime(this); + } + } + else if ((botClass == CLERIC) || (botClass == DRUID) || (botClass == SHAMAN) || (botClass == PALADIN)) { + if(GetNumberNeedingHealedInGroup(40, true) >= 2){ + botSpell = GetBestBotSpellForGroupCompleteHeal(this); + + if(botSpell.SpellId == 0) + botSpell = GetBestBotSpellForGroupHeal(this); + + if(botSpell.SpellId == 0) + botSpell = GetBestBotSpellForGroupHealOverTime(this); + + if(hpr < 40) { + if(botSpell.SpellId == 0) + botSpell = GetBestBotSpellForPercentageHeal(this); + } + } + else if(GetNumberNeedingHealedInGroup(60, true) >= 2){ + botSpell = GetBestBotSpellForGroupHeal(this); + + if(botSpell.SpellId == 0) + botSpell = GetBestBotSpellForGroupHealOverTime(this); + + if(hpr < 40) { + if(botSpell.SpellId == 0) + botSpell = GetBestBotSpellForPercentageHeal(this); + } + } + else if(hpr < 40) + botSpell = GetBestBotSpellForPercentageHeal(this); + else if(hpr >= 40 && hpr < 75) + botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); + else { + if(hpr < 90 && !tar->FindType(SE_HealOverTime)) + botSpell = GetBestBotSpellForHealOverTime(this); + } + } + else { + float hpRatioToCast = 0.0f; + + switch(this->GetBotStance()) + { + case BotStanceEfficient: + case BotStanceAggressive: + hpRatioToCast = isPrimaryHealer?90.0f:50.0f; + break; + case BotStanceBalanced: + hpRatioToCast = isPrimaryHealer?95.0f:75.0f; + break; + case BotStanceReactive: + hpRatioToCast = isPrimaryHealer?100.0f:90.0f; + break; + case BotStanceBurn: + case BotStanceBurnAE: + hpRatioToCast = isPrimaryHealer?75.0f:25.0f; + break; + default: + hpRatioToCast = isPrimaryHealer?100.0f:0.0f; + break; + } + + //If we're at specified mana % or below, don't heal as hybrid + if(tar->GetHPRatio() <= hpRatioToCast) + botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); + } + + if(botSpell.SpellId == 0) + botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); + + if(botSpell.SpellId == 0) + botSpell = GetFirstBotSpellForSingleTargetHeal(this); + + if(botSpell.SpellId == 0 && botClass == BARD){ + botSpell = GetFirstBotSpellBySpellType(this, SpellType_Heal); + } + + // If there is still no spell id, then there isn't going to be one so we are done + if(botSpell.SpellId == 0) + break; + + // Can we cast this spell on this target? + if(!(spells[botSpell.SpellId].targettype==ST_GroupTeleport || spells[botSpell.SpellId].targettype == ST_Target || tar == this) + && !(tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) + break; + + uint32 TempDontHealMeBeforeTime = tar->DontHealMeBefore(); + + castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime); + + if(castedSpell) { + char* gmsg = 0; + /*if(TempDontHealMeBeforeTime != tar->DontHealMeBefore()) + tar->SetDontHealMeBefore(TempDontHealMeBeforeTime); + + // For non-HoT heals, do a 4 second delay + // TODO: Replace this code with logic that calculates the delay based on number of clerics in rotation + // and ignores heals for anyone except the main tank + if(!IsHealOverTimeSpell(botSpell.SpellId)) { + if(IsCompleteHealSpell(botSpell.SpellId)) { + // Complete Heal 4 second rotation + tar->SetDontHealMeBefore(Timer::GetCurrentTime() + 4000); + } + else { + tar->SetDontHealMeBefore(Timer::GetCurrentTime() + 1000); + } + }*/ + if(botClass != BARD) { + if(IsGroupSpell(botSpell.SpellId)){ + if(this->HasGroup()) { + Group *g = this->GetGroup(); + + if(g) { + MakeAnyLenString(&gmsg, "Casting %s.", spells[botSpell.SpellId].name); + + for( int i = 0; imembers[i] && !g->members[i]->qglobal) { + g->members[i]->SetDontHealMeBefore(Timer::GetCurrentTime() + 1000); + } + } + } + } + } + else { + if(tar != this) //we don't need spam of bots healing themselves + MakeAnyLenString(&gmsg, "Casting %s on %s", spells[botSpell.SpellId].name, tar->GetCleanName()); + + tar->SetDontHealMeBefore(Timer::GetCurrentTime() + 2000); + } + } + + if(gmsg && GetGroupMessagesOn()) + BotGroupSay(this, gmsg); + } + } + } + break; + } + case SpellType_Root: { + if (!tar->IsRooted() && tar->DontRootMeBefore() < Timer::GetCurrentTime()) { + if(!checked_los) { + if(!CheckLosFN(tar)) + break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + + checked_los = true; + } + + // TODO: If there is a ranger in the group then don't allow root spells + + botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); + + if(botSpell.SpellId == 0) + break; + + if(tar->CanBuffStack(botSpell.SpellId, botLevel, true) == 0) + break; + + uint32 TempDontRootMeBefore = tar->DontRootMeBefore(); + + castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontRootMeBefore); + + if(TempDontRootMeBefore != tar->DontRootMeBefore()) + tar->SetDontRootMeBefore(TempDontRootMeBefore); + } + break; + } + case SpellType_Buff: { + if (tar->DontBuffMeBefore() < Timer::GetCurrentTime()) { + std::list buffSpellList = GetBotSpellsBySpellType(this, SpellType_Buff); + + for(std::list::iterator itr = buffSpellList.begin(); itr != buffSpellList.end(); itr++) { + BotSpell selectedBotSpell = *itr; + + if(selectedBotSpell.SpellId == 0) + continue; + + // no buffs with illusions.. use #bot command to cast illusions + if(IsEffectInSpell(selectedBotSpell.SpellId, SE_Illusion)) + continue; + + //no teleport spells use #bot command to cast teleports + if(IsEffectInSpell(selectedBotSpell.SpellId, SE_Teleport) || IsEffectInSpell(selectedBotSpell.SpellId, SE_Succor)) + continue; + + // can not cast buffs for your own pet only on another pet that isn't yours + if((spells[selectedBotSpell.SpellId].targettype == ST_Pet) && (tar != this->GetPet())) + continue; + + // Validate target + + if(!((spells[selectedBotSpell.SpellId].targettype == ST_Target || spells[selectedBotSpell.SpellId].targettype == ST_Pet || tar == this || + spells[selectedBotSpell.SpellId].targettype == ST_Group || spells[selectedBotSpell.SpellId].targettype == ST_GroupTeleport || + (botClass == BARD && spells[selectedBotSpell.SpellId].targettype == ST_AEBard)) + && !tar->IsImmuneToSpell(selectedBotSpell.SpellId, this) + && (tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0))) { + continue; + } + + // Put the zone levitate and movement check here since bots are able to bypass the client casting check + if((IsEffectInSpell(selectedBotSpell.SpellId, SE_Levitate) && !zone->CanLevitate()) + || (IsEffectInSpell(selectedBotSpell.SpellId, SE_MovementSpeed) && !zone->CanCastOutdoor())) { + continue; + } + + switch(tar->GetArchetype()) + { + case ARCHETYPE_CASTER: + //TODO: probably more caster specific spell effects in here + if(IsEffectInSpell(selectedBotSpell.SpellId, SE_AttackSpeed) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ATK) || + IsEffectInSpell(selectedBotSpell.SpellId, SE_STR) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ReverseDS)) + { + continue; + } + break; + case ARCHETYPE_MELEE: + if(IsEffectInSpell(selectedBotSpell.SpellId, SE_IncreaseSpellHaste) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ManaPool) || + IsEffectInSpell(selectedBotSpell.SpellId, SE_CastingLevel) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ManaRegen_v2) || + IsEffectInSpell(selectedBotSpell.SpellId, SE_CurrentMana)) + { + continue; + } + break; + case ARCHETYPE_HYBRID: + //Hybrids get all buffs + default: + break; + } + + if(botClass == ENCHANTER && IsEffectInSpell(selectedBotSpell.SpellId, SE_Rune)) + { + float manaRatioToCast = 75.0f; + + switch(this->GetBotStance()) + { + case BotStanceEfficient: + manaRatioToCast = 90.0f; + break; + case BotStanceBalanced: + case BotStanceAggressive: + manaRatioToCast = 75.0f; + break; + case BotStanceReactive: + case BotStanceBurn: + case BotStanceBurnAE: + manaRatioToCast = 50.0f; + break; + default: + manaRatioToCast = 75.0f; + break; + } + + //If we're at specified mana % or below, don't rune as enchanter + if(this->GetManaRatio() <= manaRatioToCast) + break; + } + + if(CheckSpellRecastTimers(this, itr->SpellIndex)) + { + + uint32 TempDontBuffMeBefore = tar->DontBuffMeBefore(); + + castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontBuffMeBefore); + + if(TempDontBuffMeBefore != tar->DontBuffMeBefore()) + tar->SetDontBuffMeBefore(TempDontBuffMeBefore); + } + + if(castedSpell) + break; + } + } + break; + } + case SpellType_Escape: { + uint8 hpr = (uint8)GetHPRatio(); + bool mayGetAggro = false; + +#ifdef IPC + if (hpr <= 5 || (IsNPC() && CastToNPC()->IsInteractive() && tar != this) ) +#else + if(hpr > 15 && ((botClass == WIZARD) || (botClass == ENCHANTER) || (botClass == RANGER))) + mayGetAggro = HasOrMayGetAggro(); //classes have hate reducing spells + + if (hpr <= 15 || mayGetAggro) +#endif + { + botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); + + if(botSpell.SpellId == 0) + break; + + if(IsInvulnerabilitySpell(botSpell.SpellId)) + tar = this; //target self for invul type spells + + castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); + } + break; + } + case SpellType_Nuke: { + if((tar->GetHPRatio() <= 95.0f) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER))) + { + if(!checked_los) { + if(!CheckLosFN(tar)) + break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + + checked_los = true; + } + + if(botClass == CLERIC || botClass == ENCHANTER) + { + float manaRatioToCast = 75.0f; + + switch(this->GetBotStance()) + { + case BotStanceEfficient: + manaRatioToCast = 90.0f; + break; + case BotStanceBalanced: + manaRatioToCast = 75.0f; + break; + case BotStanceReactive: + case BotStanceAggressive: + manaRatioToCast = 50.0f; + break; + case BotStanceBurn: + case BotStanceBurnAE: + manaRatioToCast = 25.0f; + break; + default: + manaRatioToCast = 50.0f; + break; + } + + //If we're at specified mana % or below, don't nuke as cleric or enchanter + if(this->GetManaRatio() <= manaRatioToCast) + break; + } + + if(botClass == MAGICIAN || botClass == SHADOWKNIGHT || botClass == NECROMANCER || botClass == PALADIN || botClass == RANGER || botClass == DRUID || botClass == CLERIC) { + if(tar->GetBodyType() == BT_Undead || tar->GetBodyType() == BT_SummonedUndead || tar->GetBodyType() == BT_Vampire) + botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Undead); + else if(tar->GetBodyType() == BT_Summoned || tar->GetBodyType() == BT_Summoned2 || tar->GetBodyType() == BT_Summoned3) + botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Summoned); + } + + if(botClass == PALADIN || botClass == DRUID || botClass == CLERIC || botClass == ENCHANTER || botClass == WIZARD) { + if(botSpell.SpellId == 0) { + uint8 stunChance = (tar->IsCasting() ? 30: 15); + + if(botClass == PALADIN) + stunChance = 50; + + if(!tar->SpecAttacks[UNSTUNABLE] && !tar->IsStunned() && (MakeRandomInt(1, 100) <= stunChance)) { + botSpell = GetBestBotSpellForStunByTargetType(this, ST_Target); + } + } + } + + if(botClass == WIZARD && botSpell.SpellId == 0) { + botSpell = GetBestBotWizardNukeSpellByTargetResists(this, tar); + } + + if(botSpell.SpellId == 0) + botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Target); + + if(botSpell.SpellId == 0) + break; + + if(!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))) + break; + + if(IsFearSpell(botSpell.SpellId)) { + // don't let fear cast if the npc isn't snared or rooted + if(tar->GetSnaredAmount() == -1) { + if(!tar->IsRooted()) + break; + } + } + + castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); + } + break; + } + case SpellType_Dispel: { + if(tar->GetHPRatio() > 95.0f) { + if(!checked_los) { + if(!CheckLosFN(tar)) + break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + + checked_los = true; + } + + botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); + + if(botSpell.SpellId == 0) + break; + + // TODO: Check target to see if there is anything to dispel + + if(tar->CountDispellableBuffs() > 0) { + castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); + } + } + break; + } + case SpellType_Pet: { + //keep mobs from recasting pets when they have them. + if (!IsPet() && !GetPetID() && !IsBotCharmer()) { + if(botClass == MAGICIAN) + botSpell = GetBestBotMagicianPetSpell(this); + else + botSpell = GetFirstBotSpellBySpellType(this, SpellType_Pet); + + if(botSpell.SpellId == 0) + break; + + castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); + } + break; + } + case SpellType_InCombatBuff: { + + if(botClass == SHAMAN) { + checked_los = true; + + std::list inCombatBuffList = GetBotSpellsBySpellType(this, SpellType_InCombatBuff); + + for(list::iterator itr = inCombatBuffList.begin(); itr != inCombatBuffList.end(); itr++) { + BotSpell selectedBotSpell = *itr; + + if(selectedBotSpell.SpellId == 0) + continue; + + if(CheckSpellRecastTimers(this, itr->SpellIndex)) + { + if(!(!tar->IsImmuneToSpell(selectedBotSpell.SpellId, this) && (spells[selectedBotSpell.SpellId].buffduration < 1 || tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0))) + continue; + + //short duration buffs or other buffs only to be cast during combat. + if (IsSelfConversionSpell(selectedBotSpell.SpellId)) { + if(GetManaRatio() > 90.0f || GetHPRatio() < 50.0f || GetHPRatio() < (GetManaRatio() + 10.0f)) + break; //don't cast if low hp, lots of mana, or if mana is higher than hps + } + + castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost); + } + + if(castedSpell) + break; + } + } + break; + } + case SpellType_Lifetap: { + if (GetHPRatio() < 90.0f) { + if(!checked_los) { + if(!CheckLosFN(tar)) + break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + + checked_los = true; + } + + botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); + + if(botSpell.SpellId == 0) + break; + + if(!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))) + break; + + castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); + } + break; + } + case SpellType_Snare: { + if (tar->DontSnareMeBefore() < Timer::GetCurrentTime()) { + if(!checked_los) { + if(!CheckLosFN(tar)) + break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + + checked_los = true; + } + + botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); + + if(botSpell.SpellId == 0) + break; + + if(!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) + break; + + uint32 TempDontSnareMeBefore = tar->DontSnareMeBefore(); + + castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontSnareMeBefore); + + if(TempDontSnareMeBefore != tar->DontSnareMeBefore()) + tar->SetDontSnareMeBefore(TempDontSnareMeBefore); + } + break; + } + case SpellType_DOT: { + if ((tar->GetHPRatio() <= 98.0f) && (tar->DontDotMeBefore() < Timer::GetCurrentTime()) && (tar->GetHPRatio() > 15.0f)) { + if(!checked_los) { + if(!CheckLosFN(tar)) + break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + + checked_los = true; + } + + std::list dotList = GetBotSpellsBySpellType(this, SpellType_DOT); + + const int maxDotSelect = 5; + int dotSelectCounter = 0; + + for(list::iterator itr = dotList.begin(); itr != dotList.end(); itr++) { + BotSpell selectedBotSpell = *itr; + + if(selectedBotSpell.SpellId == 0) + continue; + + if(CheckSpellRecastTimers(this, itr->SpellIndex)) + { + + if(!(!tar->IsImmuneToSpell(selectedBotSpell.SpellId, this) && tar->CanBuffStack(selectedBotSpell.SpellId, botLevel, true) >= 0)) + continue; + + uint32 TempDontDotMeBefore = tar->DontDotMeBefore(); + + castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontDotMeBefore); + + if(TempDontDotMeBefore != tar->DontDotMeBefore()) + tar->SetDontDotMeBefore(TempDontDotMeBefore); + } + + dotSelectCounter++; + + if((dotSelectCounter == maxDotSelect) || castedSpell) + break; + } + } + break; + } + case SpellType_Slow: { + if (tar->GetHPRatio() <= 99.0f) { + + if(!checked_los) { + if(!CheckLosFN(tar)) + break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + + checked_los = true; + } + + switch (botClass) { + case ENCHANTER: { + botSpell = GetBestBotSpellForMagicBasedSlow(this); + break; + } + case SHAMAN: + case BEASTLORD: { + botSpell = GetBestBotSpellForDiseaseBasedSlow(this); + + if(botSpell.SpellId == 0 || ((tar->GetMR() - 50) < (tar->GetDR() + spells[botSpell.SpellId].ResistDiff))) + botSpell = GetBestBotSpellForMagicBasedSlow(this); + break; + } + } + + if(botSpell.SpellId == 0) + break; + + if(!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) + break; + + castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); + + if(castedSpell) { + char* gmsg = 0; + + MakeAnyLenString(&gmsg, "Attempting to slow %s.", tar->GetCleanName()); + + if(gmsg && GetGroupMessagesOn()) + BotGroupSay(this, gmsg); + } + } + break; + } + case SpellType_Debuff: { + if((tar->GetHPRatio() <= 99.0f) || ((botClass == BARD) || (botClass == SHAMAN) || (botClass == ENCHANTER) || (botClass == DRUID)) && (tar->GetHPRatio() > 40.0f)) + { + if(!checked_los) { + if(!CheckLosFN(tar)) + break; //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call + + checked_los = true; + } + + botSpell = GetBestBotSpellForResistDebuff(this, tar); + + if(botSpell.SpellId == 0) + botSpell = GetDebuffBotSpell(this, tar); + + if(botSpell.SpellId == 0) + break; + + if(!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))) + break; + + castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); + } + break; + } + case SpellType_Cure: { + if(GetNeedsCured(tar) && (tar->DontCureMeBefore() < Timer::GetCurrentTime()) && !(GetNumberNeedingHealedInGroup(25, false) > 0) && !(GetNumberNeedingHealedInGroup(40, false) > 2)) + { + botSpell = GetBestBotSpellForCure(this, tar); + + if(botSpell.SpellId == 0) + break; + + uint32 TempDontCureMeBeforeTime = tar->DontCureMeBefore(); + + castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontCureMeBeforeTime); + + if(castedSpell) { + if(botClass != BARD) { + if(IsGroupSpell(botSpell.SpellId)){ + Group *g; + + 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); + } + } + } + } + break; + } + } + + return castedSpell; +} + +bool Bot::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore) { + bool result = false; + + // manacost has special values, -1 is no mana cost, -2 is instant cast (no mana) + int32 manaCost = mana_cost; + + if (manaCost == -1) + manaCost = spells[AIspells[i].spellid].mana; + else if (manaCost == -2) + manaCost = 0; + + int32 extraMana = 0; + int32 hasMana = GetMana(); + + // Allow bots to cast buff spells even if they are out of mana + if(RuleB(Bots, BotFinishBuffing)) { + if(manaCost > hasMana) { + // Let's have the bots complete the buff time process + if(AIspells[i].type & SpellType_Buff) { + extraMana = manaCost - hasMana; + SetMana(manaCost); + } + } + } + + float dist2 = 0; + + if (AIspells[i].type & SpellType_Escape) { + dist2 = 0; + } else + dist2 = DistNoRoot(*tar); + + if (((((spells[AIspells[i].spellid].targettype==ST_GroupTeleport && AIspells[i].type==2) + || spells[AIspells[i].spellid].targettype==ST_AECaster + || spells[AIspells[i].spellid].targettype==ST_Group + || spells[AIspells[i].spellid].targettype==ST_AEBard) + && dist2 <= spells[AIspells[i].spellid].aoerange*spells[AIspells[i].spellid].aoerange) + || dist2 <= GetActSpellRange(AIspells[i].spellid, spells[AIspells[i].spellid].range)*GetActSpellRange(AIspells[i].spellid, spells[AIspells[i].spellid].range)) && (mana_cost <= GetMana() || GetMana() == GetMaxMana())) + { + result = NPC::AIDoSpellCast(i, tar, mana_cost, oDontDoAgainBefore); + + if(IsCasting() && IsSitting()) + Stand(); + } + + // if the spell wasn't casted, then take back any extra mana that was given to the bot to cast that spell + if(!result) { + SetMana(hasMana); + extraMana = false; + } + else { //handle spell recast and recast timers + if(GetClass() == BARD && IsGroupSpell(AIspells[i].spellid)) { + AIspells[i].time_cancast = (spells[AIspells[i].spellid].recast_time > (spells[AIspells[i].spellid].buffduration * 6000)) ? Timer::GetCurrentTime() + spells[AIspells[i].spellid].recast_time : Timer::GetCurrentTime() + spells[AIspells[i].spellid].buffduration * 6000; + //spellend_timer.Start(spells[AIspells[i].spellid].cast_time); + } + else + AIspells[i].time_cancast = Timer::GetCurrentTime() + spells[AIspells[i].spellid].recast_time; + if(spells[AIspells[i].spellid].EndurTimerIndex > 0) { + SetSpellRecastTimer(spells[AIspells[i].spellid].EndurTimerIndex, spells[AIspells[i].spellid].recast_time); + } + } + + return result; +} + +bool Bot::AI_PursueCastCheck() { + bool result = false; + + if (AIautocastspell_timer->Check(false)) { + _ZP(Bot_AI_Process_pursue_cast); + + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. + + mlog(AI__SPELLS, "Bot Engaged (pursuing) autocast check triggered. Trying to cast offensive spells."); + + if(!AICastSpell(GetTarget(), 100, SpellType_Snare)) { + if(!AICastSpell(GetTarget(), 100, SpellType_Lifetap)) { + if(!AICastSpell(GetTarget(), 100, SpellType_Nuke)) { + /*AIautocastspell_timer->Start(RandomTimer(500, 2000), false); + result = true;*/ + result = true; + } + + result = true; + } + + result = true; + } + + if(!AIautocastspell_timer->Enabled()) + AIautocastspell_timer->Start(RandomTimer(100, 250), false); + } + + return result; +} + +bool Bot::AI_IdleCastCheck() { + bool result = false; + + if (AIautocastspell_timer->Check(false)) { + _ZP(Bot_AI_IdleCastCheck); +#if MobAI_DEBUG_Spells >= 25 + cout << "Non-Engaged autocast check triggered: " << this->GetCleanName() << endl; +#endif + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. + + //Ok, IdleCastCheck depends of class. + // Healers WITHOUT pets will check if a heal is needed before buffing. + uint8 botClass = GetClass(); + + if(botClass == CLERIC || botClass == PALADIN || botClass == RANGER) { + if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) { + if (!AICastSpell(this, 100, SpellType_Heal)) { + if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) { + if (!AICastSpell(this, 100, SpellType_Buff)) { + if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) { + // + } + } + } + } + } + + result = true; + } + // Pets class will first cast their pet, then buffs + else if(botClass == DRUID || botClass == MAGICIAN || botClass == SHADOWKNIGHT || botClass == SHAMAN || botClass == NECROMANCER || botClass == ENCHANTER || botClass == BEASTLORD || botClass == WIZARD) { + if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) { + if (!AICastSpell(this, 100, SpellType_Pet)) { + if (!AICastSpell(this, 100, SpellType_Heal)) { + if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) { + if (!AICastSpell(this, 100, SpellType_Buff)) { + if (!AICastSpell(GetPet(), 100, SpellType_Heal)) { + if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) { + // + } + } + } + } + } + } + } + + result = true; + } + else if(botClass == BARD) { + // bard bots + if(!AICastSpell(this, 100, SpellType_Cure)) { + if(!AICastSpell(this, 100, SpellType_Heal)) { + if(!AICastSpell(this, 100, SpellType_Buff)) { + // + } + } + } + + result = true; + } + + if(!AIautocastspell_timer->Enabled()) + AIautocastspell_timer->Start(RandomTimer(1000, 5000), false); + } + + return result; +} + +bool Bot::AI_EngagedCastCheck() { + bool result = false; + bool failedToCast = false; + + if (GetTarget() && AIautocastspell_timer->Check(false)) { + _ZP(Bot_AI_Process_engaged_cast); + + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. + + uint8 botClass = GetClass(); + BotStanceType botStance = GetBotStance(); + bool mayGetAggro = HasOrMayGetAggro(); + + mlog(AI__SPELLS, "Engaged autocast check triggered (BOTS). Trying to cast healing spells then maybe offensive spells."); + + if(botClass == CLERIC) { + if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { + if(!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { + if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { + if(!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) { + //AIautocastspell_timer->Start(RandomTimer(100, 250), false); // Do not give healer classes a lot of time off or your tank's die + failedToCast = true; + } + } + } + } + } + } + else if(botClass == DRUID) { + if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { + if(!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { + if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { + if(!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + //AIautocastspell_timer->Start(RandomTimer(100, 250), false); // Do not give healer classes a lot of time off or your tank's die + failedToCast = true; + } + } + } + } + } + } + } + else if(botClass == SHAMAN) { + if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) { + if(!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { + if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { + if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { + if(!AICastSpell(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { + if(!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + //AIautocastspell_timer->Start(RandomTimer(100, 250), false); // Do not give healer classes a lot of time off or your tank's die + failedToCast = true; + } + } + } + } + } + } + } + } + } + } + else if(botClass == RANGER) { + if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { + if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { + if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { + if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + // + failedToCast = true; + } + } + } + } + } + } + else if(botClass == BEASTLORD) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) { + if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { + if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { + if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { + if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { + if(!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + // + failedToCast = true; + } + } + } + } + } + } + } + } + } + else if(botClass == WIZARD) { + if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { + if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + // + failedToCast = true; + } + } + } + else if(botClass == PALADIN) { + if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { + if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { + if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) { + // + failedToCast = true; + } + } + } + } + } + } + else if(botClass == SHADOWKNIGHT) { + if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { + if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Lifetap), SpellType_Lifetap)) { + if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + // + failedToCast = true; + } + } + } + } + } + } + } + } + else if(botClass == MAGICIAN) { + if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) { + if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { + if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + // + failedToCast = true; + } + } + } + } + } + else if(botClass == NECROMANCER) { + if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { + if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Lifetap), SpellType_Lifetap)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { + if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { + if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + // + failedToCast = true; + } + } + } + } + } + } + } + } + else if(botClass == ENCHANTER) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Mez), SpellType_Mez)) { + if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) { + if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { + if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { + if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + // + failedToCast = true; + } + } + } + } + } + } + } + else if(botClass == BARD) { + if(!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Buff), SpellType_Buff)) { + if(!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { + if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Dispel), SpellType_Dispel)) {// Bards will use their debuff songs + if(!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {// Bards will use their debuff songs + if(!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {// Bards will use their debuff songs + // + failedToCast = true; + } + } + } + } + } + } + + if(!AIautocastspell_timer->Enabled()) { + AIautocastspell_timer->Start(RandomTimer(100, 250), false); + } + + if(!failedToCast) + result = true; + } + + return result; +} + +bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) { + + if (!tar) { + return false; + } + + if(!AI_HasSpells()) + return false; + + if(tar->GetAppearance() == eaDead) { + if((tar->IsClient() && tar->CastToClient()->GetFeigned()) || tar->IsBot()) { + // do nothing + } + else { + return false; + } + } + + uint8 botLevel = GetLevel(); + + bool castedSpell = false; + + BotSpell botSpell; + botSpell.SpellId = 0; + botSpell.SpellIndex = 0; + botSpell.ManaCost = 0; + + if (useFastHeals) { + botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); + + if(botSpell.SpellId == 0) + botSpell = GetBestBotSpellForFastHeal(this); + } + else { + botSpell = GetBestBotSpellForPercentageHeal(this); + + if(botSpell.SpellId == 0) + botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); + + if(botSpell.SpellId == 0) + botSpell = GetFirstBotSpellForSingleTargetHeal(this); + + if(botSpell.SpellId == 0){ + botSpell = GetFirstBotSpellBySpellType(this, SpellType_Heal); + } + } + + // If there is still no spell id, then there isn't going to be one so we are done + if(botSpell.SpellId == 0) + return false; + + // Can we cast this spell on this target? + if(!(spells[botSpell.SpellId].targettype==ST_GroupTeleport || spells[botSpell.SpellId].targettype == ST_Target || tar == this) + && !(tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) + return false; + + uint32 TempDontHealMeBeforeTime = tar->DontHealMeBefore(); + + castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime); + + if(castedSpell) { + char* gmsg = 0; + + MakeAnyLenString(&gmsg, "Casting %s on %s, please stay in range!", spells[botSpell.SpellId].name, tar->GetCleanName()); + + if(gmsg) + Say(gmsg); + } + + return castedSpell; +} + +std::list Bot::GetBotSpellsForSpellEffect(Bot* botCaster, int spellEffect) { + std::list result; + + if(botCaster && botCaster->AI_HasSpells()) { + std::vector botSpellList = botCaster->GetBotSpells(); + + for (int i = botSpellList.size() - 1; i >= 0; i--) { + if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if(IsEffectInSpell(botSpellList[i].spellid, spellEffect)) { + BotSpell botSpell; + botSpell.SpellId = botSpellList[i].spellid; + botSpell.SpellIndex = i; + botSpell.ManaCost = botSpellList[i].manacost; + + result.push_back(botSpell); + } + } + } + + return result; +} + +std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, int spellEffect, SpellTargetType targetType) { + std::list result; + + if(botCaster && botCaster->AI_HasSpells()) { + std::vector botSpellList = botCaster->GetBotSpells(); + + for (int i = botSpellList.size() - 1; i >= 0; i--) { + if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if(IsEffectInSpell(botSpellList[i].spellid, spellEffect)) { + if(spells[botSpellList[i].spellid].targettype == targetType) { + BotSpell botSpell; + botSpell.SpellId = botSpellList[i].spellid; + botSpell.SpellIndex = i; + botSpell.ManaCost = botSpellList[i].manacost; + + result.push_back(botSpell); + } + } + } + } + + return result; +} + +std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType) { + std::list result; + + if(botCaster && botCaster->AI_HasSpells()) { + std::vector botSpellList = botCaster->GetBotSpells(); + + for (int i = botSpellList.size() - 1; i >= 0; i--) { + if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if(botSpellList[i].type & spellType) { + BotSpell botSpell; + botSpell.SpellId = botSpellList[i].spellid; + botSpell.SpellIndex = i; + botSpell.ManaCost = botSpellList[i].manacost; + + result.push_back(botSpell); + } + } + } + + return result; +} + +BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster && botCaster->AI_HasSpells()) { + std::vector botSpellList = botCaster->GetBotSpells(); + + for (int i = botSpellList.size() - 1; i >= 0; i--) { + if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if((botSpellList[i].type & spellType) && CheckSpellRecastTimers(botCaster, i)) { + result.SpellId = botSpellList[i].spellid; + result.SpellIndex = i; + result.ManaCost = botSpellList[i].manacost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP); + + for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + if(IsFastHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster) { + std::list botHoTSpellList = GetBotSpellsForSpellEffect(botCaster, SE_HealOverTime); + std::vector botSpellList = botCaster->GetBotSpells(); + + for(std::list::iterator botSpellListItr = botHoTSpellList.begin(); botSpellListItr != botHoTSpellList.end(); botSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + if(IsHealOverTimeSpell(botSpellListItr->SpellId)) { + + for (int i = botSpellList.size() - 1; i >= 0; i--) { + if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if(botSpellList[i].spellid == botSpellListItr->SpellId && (botSpellList[i].type & SpellType_Heal) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + } + } + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster && botCaster->AI_HasSpells()) { + std::vector botSpellList = botCaster->GetBotSpells(); + + for (int i = botSpellList.size() - 1; i >= 0; i--) { + if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if(IsCompleteHealSpell(botSpellList[i].spellid) && CheckSpellRecastTimers(botCaster, i)) { + result.SpellId = botSpellList[i].spellid; + result.SpellIndex = i; + result.ManaCost = botSpellList[i].manacost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP); + + for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + if(IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP); + + for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + if((IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) || IsFastHealSpell(botSpellListItr->SpellId)) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP); + + for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + if(IsRegularGroupHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster) { + std::list botHoTSpellList = GetBotSpellsForSpellEffect(botCaster, SE_HealOverTime); + std::vector botSpellList = botCaster->GetBotSpells(); + + for(std::list::iterator botSpellListItr = botHoTSpellList.begin(); botSpellListItr != botHoTSpellList.end(); botSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + if(IsGroupHealOverTimeSpell(botSpellListItr->SpellId)) { + + for (int i = botSpellList.size() - 1; i >= 0; i--) { + if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if(botSpellList[i].spellid == botSpellListItr->SpellId && (botSpellList[i].type & SpellType_Heal) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + } + } + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CompleteHeal); + + for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + if(IsGroupCompleteHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_Mez); + + for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + if(IsMezSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForMagicBasedSlow(Bot* botCaster) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_AttackSpeed); + + for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + if(IsSlowSpell(botSpellListItr->SpellId) && spells[botSpellListItr->SpellId].resisttype == RESIST_MAGIC && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_AttackSpeed); + + for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + if(IsSlowSpell(botSpellListItr->SpellId) && spells[botSpellListItr->SpellId].resisttype == RESIST_DISEASE && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + } + + return result; +} + +Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, BotSpell botSpell) { + Mob* result = 0; + + if(botCaster && IsMezSpell(botSpell.SpellId)) { + + std::list npc_list; + entity_list.GetNPCList(npc_list); + + for(std::list::iterator itr = npc_list.begin(); itr != npc_list.end(); itr++) { + NPC* npc = *itr; + + if(npc->DistNoRootNoZ(*botCaster) <= botCaster->GetActSpellRange(botSpell.SpellId, spells[botSpell.SpellId].range)) { + if(!npc->IsMezzed()) { + if(botCaster->HasGroup()) { + Group* g = botCaster->GetGroup(); + + if(g) { + for(int counter = 0; counter < g->GroupCount(); counter++) { + if(npc->IsOnHatelist(g->members[counter]) && g->members[counter]->GetTarget() != npc && g->members[counter]->IsEngaged()) { + result = npc; + break; + } + } + } + } + } + } + + if(result) + break; + } + } + + return result; +} + +BotSpell Bot::GetBestBotMagicianPetSpell(Bot *botCaster) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_SummonPet); + + std::string petType = GetBotMagicianPetType(botCaster); + + for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + if(IsSummonPetSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + if(!strncmp(spells[botSpellListItr->SpellId].teleport_zone, petType.c_str(), petType.length())) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + } + } + + return result; +} + +std::string Bot::GetBotMagicianPetType(Bot* botCaster) { + std::string result; + + if(botCaster) { + if(botCaster->IsPetChooser()) { + switch(botCaster->GetPetChooserID()) { + case 0: + result = std::string("SumWater"); + break; + case 1: + result = std::string("SumFire"); + break; + case 2: + result = std::string("SumAir"); + break; + case 3: + result = std::string("SumEarth"); + break; + default: + result = std::string("MonsterSum"); + break; + } + } + else { + if(botCaster->GetLevel() == 2) + result = std::string("SumWater"); + else if(botCaster->GetLevel() == 3) + result = std::string("SumFire"); + else if(botCaster->GetLevel() == 4) + result = std::string("SumAir"); + else if(botCaster->GetLevel() == 5) + result = std::string("SumEarth"); + else if(botCaster->GetLevel() < 30) { + // Under level 30 + int counter = MakeRandomInt(0, 3); + + switch(counter) { + case 0: + result = std::string("SumWater"); + break; + case 1: + result = std::string("SumFire"); + break; + case 2: + result = std::string("SumAir"); + break; + case 3: + result = std::string("SumEarth"); + break; + default: + result = std::string("MonsterSum"); + break; + } + } + else { + // Over level 30 + int counter = MakeRandomInt(0, 4); + + switch(counter) { + case 0: + result = std::string("SumWater"); + break; + case 1: + result = std::string("SumFire"); + break; + case 2: + result = std::string("SumAir"); + break; + case 3: + result = std::string("SumEarth"); + break; + default: + result = std::string("MonsterSum"); + break; + } + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_CurrentHP, targetType); + + for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + if(IsPureNukeSpell(botSpellListItr->SpellId) && IsDamageSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType) +{ + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster) + { + std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_Stun, targetType); + + for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) + { + // Assuming all the spells have been loaded into this list by level and in descending order + if(IsStunSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) + { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(botCaster && target) { + const int lureResisValue = -100; + const int maxTargetResistValue = 300; + bool selectLureNuke = false; + + if((target->GetMR() > maxTargetResistValue) && (target->GetCR() > maxTargetResistValue) && (target->GetFR() > maxTargetResistValue)) + selectLureNuke = true; + + + std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_CurrentHP, ST_Target); + + BotSpell firstWizardMagicNukeSpellFound; + firstWizardMagicNukeSpellFound.SpellId = 0; + firstWizardMagicNukeSpellFound.SpellIndex = 0; + firstWizardMagicNukeSpellFound.ManaCost = 0; + + for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); botSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + bool spellSelected = false; + + if(CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) { + if(selectLureNuke && (spells[botSpellListItr->SpellId].ResistDiff < lureResisValue)) { + spellSelected = true; + } + else if(IsPureNukeSpell(botSpellListItr->SpellId)) { + if(((target->GetMR() < target->GetCR()) || (target->GetMR() < target->GetFR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) + && (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue)) + { + spellSelected = true; + } + else if(((target->GetCR() < target->GetMR()) || (target->GetCR() < target->GetFR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_COLD) + && (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue)) + { + spellSelected = true; + } + else if(((target->GetFR() < target->GetCR()) || (target->GetFR() < target->GetMR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_FIRE) + && (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue)) + { + spellSelected = true; + } + else if((GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) && (spells[botSpellListItr->SpellId].ResistDiff > lureResisValue) && !IsStunSpell(botSpellListItr->SpellId)) { + firstWizardMagicNukeSpellFound.SpellId = botSpellListItr->SpellId; + firstWizardMagicNukeSpellFound.SpellIndex = botSpellListItr->SpellIndex; + firstWizardMagicNukeSpellFound.ManaCost = botSpellListItr->ManaCost; + } + } + } + + if(spellSelected) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + + if(result.SpellId == 0) { + result = firstWizardMagicNukeSpellFound; + } + } + + return result; +} + +BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(!tar || !botCaster) + return result; + + if(botCaster && botCaster->AI_HasSpells()) { + std::vector botSpellList = botCaster->GetBotSpells(); + + for (int i = botSpellList.size() - 1; i >= 0; i--) { + if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if(((botSpellList[i].type & SpellType_Debuff) || IsDebuffSpell(botSpellList[i].spellid)) + && (!tar->IsImmuneToSpell(botSpellList[i].spellid, botCaster) + && tar->CanBuffStack(botSpellList[i].spellid, botCaster->GetLevel(), true) >= 0) + && CheckSpellRecastTimers(botCaster, i)) { + result.SpellId = botSpellList[i].spellid; + result.SpellIndex = i; + result.ManaCost = botSpellList[i].manacost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(!tar) + return result; + + int level_mod = (tar->GetLevel() - botCaster->GetLevel())* (tar->GetLevel() - botCaster->GetLevel()) / 2; + if(tar->GetLevel() - botCaster->GetLevel() < 0) + { + level_mod = -level_mod; + } + bool needsMagicResistDebuff = (tar->GetMR() + level_mod) > 100 ? true: false; + bool needsColdResistDebuff = (tar->GetCR() + level_mod) > 100 ? true: false; + bool needsFireResistDebuff = (tar->GetFR() + level_mod) > 100 ? true: false; + bool needsPoisonResistDebuff = (tar->GetPR() + level_mod) > 100 ? true: false; + bool needsDiseaseResistDebuff = (tar->GetDR() + level_mod) > 100 ? true: false; + + if(botCaster && botCaster->AI_HasSpells()) { + std::vector botSpellList = botCaster->GetBotSpells(); + + for (int i = botSpellList.size() - 1; i >= 0; i--) { + if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if(((botSpellList[i].type & SpellType_Debuff) || IsResistDebuffSpell(botSpellList[i].spellid)) + && ((needsMagicResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistMagic)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)) + || (needsColdResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistCold)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)) + || (needsFireResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistFire)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)) + || (needsPoisonResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistPoison)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)) + || (needsDiseaseResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistDisease)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll))) + && (!tar->IsImmuneToSpell(botSpellList[i].spellid, botCaster) + && tar->CanBuffStack(botSpellList[i].spellid, botCaster->GetLevel(), true) >= 0) + && CheckSpellRecastTimers(botCaster, i)) { + result.SpellId = botSpellList[i].spellid; + result.SpellIndex = i; + result.ManaCost = botSpellList[i].manacost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) { + BotSpell result; + bool spellSelected = false; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if(!tar) + return result; + + int countNeedsCured = 0; + bool isPoisoned = tar->FindType(SE_PoisonCounter); + bool isDiseased = tar->FindType(SE_DiseaseCounter); + bool isCursed = tar->FindType(SE_CurseCounter); + bool isCorrupted = tar->FindType(SE_CorruptionCounter); + + if(botCaster && botCaster->AI_HasSpells()) { + std::list cureList = GetBotSpellsBySpellType(botCaster, SpellType_Cure); + + if(tar->HasGroup()) { + Group *g = tar->GetGroup(); + + if(g) { + for( int i = 0; imembers[i] && !g->members[i]->qglobal) { + if(botCaster->GetNeedsCured(g->members[i])) + countNeedsCured++; + } + } + } + } + + //Check for group cure first + if(countNeedsCured > 2) { + for(list::iterator itr = cureList.begin(); itr != cureList.end(); itr++) { + BotSpell selectedBotSpell = *itr; + + if(IsGroupSpell(itr->SpellId) && CheckSpellRecastTimers(botCaster, itr->SpellIndex)) { + if(selectedBotSpell.SpellId == 0) + continue; + + if(isPoisoned && IsEffectInSpell(itr->SpellId, SE_PoisonCounter)) { + spellSelected = true; + } + else if(isDiseased && IsEffectInSpell(itr->SpellId, SE_DiseaseCounter)) { + spellSelected = true; + } + else if(isCursed && IsEffectInSpell(itr->SpellId, SE_CurseCounter)) { + spellSelected = true; + } + else if(isCorrupted && IsEffectInSpell(itr->SpellId, SE_CorruptionCounter)) { + spellSelected = true; + } + else if(IsEffectInSpell(itr->SpellId, SE_DispelDetrimental)) { + spellSelected = true; + } + + if(spellSelected) + { + result.SpellId = itr->SpellId; + result.SpellIndex = itr->SpellIndex; + result.ManaCost = itr->ManaCost; + + break; + } + } + } + } + + //no group cure for target- try to find single target spell + if(!spellSelected) { + for(list::iterator itr = cureList.begin(); itr != cureList.end(); itr++) { + BotSpell selectedBotSpell = *itr; + + if(CheckSpellRecastTimers(botCaster, itr->SpellIndex)) { + if(selectedBotSpell.SpellId == 0) + continue; + + if(isPoisoned && IsEffectInSpell(itr->SpellId, SE_PoisonCounter)) { + spellSelected = true; + } + else if(isDiseased && IsEffectInSpell(itr->SpellId, SE_DiseaseCounter)) { + spellSelected = true; + } + else if(isCursed && IsEffectInSpell(itr->SpellId, SE_CurseCounter)) { + spellSelected = true; + } + else if(isCorrupted && IsEffectInSpell(itr->SpellId, SE_CorruptionCounter)) { + spellSelected = true; + } + else if(IsEffectInSpell(itr->SpellId, SE_DispelDetrimental)) { + spellSelected = true; + } + + if(spellSelected) + { + result.SpellId = itr->SpellId; + result.SpellIndex = itr->SpellIndex; + result.ManaCost = itr->ManaCost; + + break; + } + } + } + } + } + + return result; +} + +void Bot::SetSpellRecastTimer(int timer_index, int32 recast_delay) { + if(timer_index > 0 && timer_index <= MaxSpellTimer) { + timers[timer_index - 1] = Timer::GetCurrentTime() + recast_delay; + } +} + +int32 Bot::GetSpellRecastTimer(Bot *caster, int timer_index) { + int32 result = 0; + if(caster) { + if(timer_index > 0 && timer_index <= MaxSpellTimer) { + result = caster->timers[timer_index - 1]; + } + } + return result; +} + +bool Bot::CheckSpellRecastTimers(Bot *caster, int SpellIndex) { + if(caster) { + if(caster->AIspells[SpellIndex].time_cancast < Timer::GetCurrentTime()) { //checks spell recast + if(GetSpellRecastTimer(caster, spells[caster->AIspells[SpellIndex].spellid].EndurTimerIndex) < Timer::GetCurrentTime()) { //checks for spells on the same timer + return true; //can cast spell + } + } + } + return false; +} + +void Bot::SetDisciplineRecastTimer(int timer_index, int32 recast_delay) { + if(timer_index > 0 && timer_index <= MaxDisciplineTimer) { + timers[DisciplineReuseStart + timer_index - 1] = Timer::GetCurrentTime() + recast_delay; + } +} + +int32 Bot::GetDisciplineRecastTimer(Bot *caster, int timer_index) { + int32 result = 0; + if(caster) { + if(timer_index > 0 && timer_index <= MaxDisciplineTimer) { + result = caster->timers[DisciplineReuseStart + timer_index - 1]; + } + } + return result; +} + +uint32 Bot::GetDisciplineRemainingTime(Bot *caster, int timer_index) { + int32 result = 0; + if(caster) { + if(timer_index > 0 && timer_index <= MaxDisciplineTimer) { + if(GetDisciplineRecastTimer(caster, timer_index) > Timer::GetCurrentTime()) + result = GetDisciplineRecastTimer(caster, timer_index) - Timer::GetCurrentTime(); + } + } + return result; +} + +bool Bot::CheckDisciplineRecastTimers(Bot *caster, int timer_index) { + if(caster) { + if(GetDisciplineRecastTimer(caster, timer_index) < Timer::GetCurrentTime()) { //checks for spells on the same timer + return true; //can cast spell + } + } + return false; +} + +void Bot::CalcChanceToCast() { + uint8 castChance = 0; + + for(int i=0; i < MaxStances; i++) { + for(int j=0; j < MaxSpellTypes; j++) { + _spellCastingChances[i][j] = 0; + } + } + + BotStanceType botStance = GetBotStance(); + uint8 botClass = GetClass(); + bool isPrimaryHealer = false; + bool isPrimarySlower = false; + + if(HasGroup()) { + isPrimaryHealer = IsGroupPrimaryHealer(); + isPrimarySlower = IsGroupPrimarySlower(); + } + + //Nuke + switch(botClass) + { + case WIZARD: + case MAGICIAN: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + case BotStanceAggressive: + castChance = 75; + break; + case BotStanceEfficient: + castChance = 50; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 100; + break; + default: + castChance = 0; + break; + } + break; + case DRUID: + case CLERIC: + case PALADIN: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = isPrimaryHealer?15:25; + break; + case BotStanceEfficient: + castChance = isPrimaryHealer?0:15; + break; + case BotStanceAggressive: + castChance = isPrimaryHealer?15:50; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = isPrimaryHealer?25:50; + break; + default: + castChance = 0; + break; + } + break; + case ENCHANTER: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = isPrimarySlower?15:25; + break; + case BotStanceEfficient: + castChance = isPrimarySlower?0:15; + break; + case BotStanceAggressive: + castChance = isPrimarySlower?15:50; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = isPrimarySlower?25:50; + break; + default: + castChance = 0; + break; + } + break; + case BEASTLORD: + case SHAMAN: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = isPrimarySlower?isPrimaryHealer?0:5:isPrimaryHealer?10:15; + break; + case BotStanceEfficient: + castChance = isPrimarySlower?isPrimaryHealer?0:0:isPrimaryHealer?5:10; + break; + case BotStanceAggressive: + castChance = isPrimarySlower?isPrimaryHealer?5:15:isPrimaryHealer?15:25; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = isPrimarySlower?isPrimaryHealer?15:25:isPrimaryHealer?25:50; + break; + default: + castChance = 0; + break; + } + break; + case NECROMANCER: + case RANGER: + case SHADOWKNIGHT: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + case BotStanceAggressive: + castChance = 15; + break; + case BotStanceEfficient: + castChance = 5; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 50; + break; + default: + castChance = 0; + break; + } + break; + case BARD: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + case BotStanceAggressive: + castChance = 50; + break; + case BotStanceEfficient: + castChance = 25; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 100; + break; + default: + castChance = 0; + break; + } + break; + case WARRIOR: + case BERSERKER: + case MONK: + case ROGUE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + _spellCastingChances[botStance][SpellType_NukeIndex] = castChance; + + //Heal + switch(botClass) + { + case CLERIC: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceEfficient: + case BotStanceReactive: + castChance = 100; + break; + case BotStanceAggressive: + castChance = 75; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 50; + break; + default: + castChance = 0; + break; + } + break; + case DRUID: + case SHAMAN: + switch(botStance) + { + case BotStanceEfficient: + castChance = isPrimaryHealer?100:15; + break; + case BotStanceBalanced: + case BotStanceReactive: + castChance = isPrimaryHealer?100:25; + break; + case BotStanceAggressive: + castChance = isPrimaryHealer?75:15; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = isPrimaryHealer?50:10; + break; + default: + castChance = 0; + break; + } + break; + case NECROMANCER: + case MAGICIAN: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = 100; + break; + case BotStanceEfficient: + case BotStanceAggressive: + castChance = 50; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 25; + break; + default: + castChance = 0; + break; + } + break; + case SHADOWKNIGHT: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = 50; + break; + case BotStanceEfficient: + case BotStanceAggressive: + castChance = 25; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 15; + break; + default: + castChance = 0; + break; + } + break; + case BEASTLORD: + case PALADIN: + case RANGER: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = isPrimaryHealer?100:25; + break; + case BotStanceEfficient: + case BotStanceAggressive: + castChance = isPrimaryHealer?75:15; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = isPrimaryHealer?50:0; + break; + default: + castChance = 0; + break; + } + break; + case BARD: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = 100; + break; + case BotStanceEfficient: + case BotStanceAggressive: + castChance = 50; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 25; + break; + default: + castChance = 0; + break; + } + break; + case ENCHANTER: + case WIZARD: + case WARRIOR: + case BERSERKER: + case MONK: + case ROGUE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + _spellCastingChances[botStance][SpellType_HealIndex] = castChance; + + //Root + castChance = 0; + _spellCastingChances[botStance][SpellType_RootIndex] = castChance; + + //Buff + switch(botClass) + { + case BARD: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = 100; + break; + case BotStanceEfficient: + case BotStanceAggressive: + castChance = 50; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 25; + break; + default: + castChance = 0; + break; + } + break; + case CLERIC: + case DRUID: + case SHAMAN: + case NECROMANCER: + case MAGICIAN: + case SHADOWKNIGHT: + case BEASTLORD: + case PALADIN: + case RANGER: + case ENCHANTER: + case WIZARD: + case WARRIOR: + case BERSERKER: + case MONK: + case ROGUE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + _spellCastingChances[botStance][SpellType_BuffIndex] = castChance; + + //Escape + switch(botClass) + { + case ENCHANTER: + case WIZARD: + case RANGER: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + case BotStanceEfficient: + castChance = 100; + break; + case BotStanceAggressive: + castChance = 50; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 25; + break; + default: + castChance = 0; + break; + } + break; + case CLERIC: + case DRUID: + case SHAMAN: + case NECROMANCER: + case MAGICIAN: + case BARD: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + case BotStanceEfficient: + castChance = 50; + break; + case BotStanceAggressive: + castChance = 25; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 15; + break; + default: + castChance = 0; + break; + } + break; + case SHADOWKNIGHT: + case PALADIN: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = 25; + break; + case BotStanceEfficient: + castChance = 15; + break; + case BotStanceAggressive: + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + break; + case BEASTLORD: + case WARRIOR: + case BERSERKER: + case MONK: + case ROGUE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + _spellCastingChances[botStance][SpellType_EscapeIndex] = castChance; + + //Pet + switch(botClass) + { + case MAGICIAN: + case NECROMANCER: + case BEASTLORD: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = 100; + break; + case BotStanceEfficient: + case BotStanceAggressive: + castChance = 50; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 25; + break; + default: + castChance = 0; + break; + } + break; + case DRUID: + case SHAMAN: + case ENCHANTER: + case SHADOWKNIGHT: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = 25; + break; + case BotStanceEfficient: + case BotStanceAggressive: + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 10; + break; + default: + castChance = 0; + break; + } + break; + case BARD: + case WIZARD: + case CLERIC: + case RANGER: + case PALADIN: + case WARRIOR: + case BERSERKER: + case MONK: + case ROGUE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + _spellCastingChances[botStance][SpellType_PetIndex] = castChance; + + //Lifetap + switch(botClass) + { + case NECROMANCER: + case SHADOWKNIGHT: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + case BotStanceEfficient: + case BotStanceAggressive: + castChance = 50; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 100; + break; + default: + castChance = 0; + break; + } + break; + case MAGICIAN: + case BEASTLORD: + case DRUID: + case SHAMAN: + case ENCHANTER: + case BARD: + case WIZARD: + case CLERIC: + case RANGER: + case PALADIN: + case WARRIOR: + case BERSERKER: + case MONK: + case ROGUE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + _spellCastingChances[botStance][SpellType_LifetapIndex] = castChance; + + //Snare + castChance = 0; + _spellCastingChances[botStance][SpellType_SnareIndex] = castChance; + + //DOT + switch(botClass) + { + case NECROMANCER: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + case BotStanceAggressive: + castChance = 50; + break; + case BotStanceEfficient: + castChance = 25; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 75; + break; + default: + castChance = 0; + break; + } + break; + case DRUID: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = isPrimaryHealer?15:50; + break; + case BotStanceEfficient: + castChance = isPrimaryHealer?10:25; + break; + case BotStanceAggressive: + case BotStanceBurn: + case BotStanceBurnAE: + castChance = isPrimaryHealer?25:50; + break; + default: + castChance = 0; + break; + } + break; + case ENCHANTER: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = isPrimarySlower?15:50; + break; + case BotStanceEfficient: + castChance = isPrimarySlower?10:25; + break; + case BotStanceAggressive: + case BotStanceBurn: + case BotStanceBurnAE: + castChance = isPrimarySlower?25:15; + break; + default: + castChance = 0; + break; + } + break; + case SHAMAN: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = isPrimarySlower?isPrimaryHealer?0:15:isPrimaryHealer?15:25; + break; + case BotStanceEfficient: + castChance = isPrimarySlower?isPrimaryHealer?0:10:isPrimaryHealer?10:15; + break; + case BotStanceAggressive: + case BotStanceBurn: + case BotStanceBurnAE: + castChance = isPrimarySlower?isPrimaryHealer?15:50:isPrimaryHealer?25:50; + break; + default: + castChance = 0; + break; + } + break; + case BEASTLORD: + case RANGER: + case SHADOWKNIGHT: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = isPrimarySlower?10:15; + break; + case BotStanceEfficient: + castChance = isPrimarySlower?0:10; + break; + case BotStanceAggressive: + case BotStanceBurn: + case BotStanceBurnAE: + castChance = isPrimarySlower?25:50; + break; + default: + castChance = 0; + break; + } + break; + case BARD: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = isPrimarySlower?25:50; + break; + case BotStanceEfficient: + castChance = isPrimarySlower?15:25; + break; + case BotStanceAggressive: + case BotStanceBurn: + case BotStanceBurnAE: + castChance = isPrimarySlower?50:100; + break; + default: + castChance = 0; + break; + } + break; + case MAGICIAN: + case PALADIN: + case CLERIC: + case WIZARD: + case WARRIOR: + case BERSERKER: + case MONK: + case ROGUE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + _spellCastingChances[botStance][SpellType_DOTIndex] = castChance; + + //Dispel + switch(botClass) + { + case BARD: + case ENCHANTER: + case WIZARD: + case MAGICIAN: + case CLERIC: + case DRUID: + case SHAMAN: + case NECROMANCER: + case RANGER: + case SHADOWKNIGHT: + case PALADIN: + case BEASTLORD: + case WARRIOR: + case BERSERKER: + case MONK: + case ROGUE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + _spellCastingChances[botStance][SpellType_DispelIndex] = castChance; + + //InCombatBuff + switch(botClass) + { + case CLERIC: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = 15; + break; + case BotStanceEfficient: + castChance = 0; + break; + case BotStanceAggressive: + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 25; + break; + default: + castChance = 0; + break; + } + break; + case PALADIN: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = 25; + break; + case BotStanceEfficient: + castChance = 0; + break; + case BotStanceAggressive: + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 50; + break; + default: + castChance = 0; + break; + } + break; + case SHAMAN: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = isPrimaryHealer?75:50; + break; + case BotStanceEfficient: + castChance = isPrimaryHealer?50:25; + break; + case BotStanceAggressive: + case BotStanceBurn: + case BotStanceBurnAE: + castChance = isPrimaryHealer?100:75; + break; + default: + castChance = 0; + break; + } + break; + case BEASTLORD: + case MAGICIAN: + case DRUID: + case ENCHANTER: + case BARD: + case WIZARD: + case NECROMANCER: + case SHADOWKNIGHT: + case RANGER: + case WARRIOR: + case BERSERKER: + case MONK: + case ROGUE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + _spellCastingChances[botStance][SpellType_InCombatBuffIndex] = castChance; + + //Mez + switch(botClass) + { + case ENCHANTER: + case BARD: + castChance = 100; + break; + case WIZARD: + case CLERIC: + case DRUID: + case SHAMAN: + case NECROMANCER: + case MAGICIAN: + case RANGER: + case SHADOWKNIGHT: + case PALADIN: + case BEASTLORD: + case WARRIOR: + case BERSERKER: + case MONK: + case ROGUE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + _spellCastingChances[botStance][SpellType_MezIndex] = castChance; + + //Charm + castChance = 0; + _spellCastingChances[botStance][SpellType_CharmIndex] = castChance; + + //Slow + switch(botClass) + { + case ENCHANTER: + case SHAMAN: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = isPrimarySlower?100:50; + break; + case BotStanceEfficient: + castChance = isPrimarySlower?100:25; + break; + case BotStanceAggressive: + case BotStanceBurn: + case BotStanceBurnAE: + castChance = isPrimarySlower?50:15; + break; + default: + castChance = 0; + break; + } + break; + case BARD: + case BEASTLORD: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = isPrimarySlower?100:25; + break; + case BotStanceEfficient: + castChance = isPrimarySlower?100:15; + break; + case BotStanceAggressive: + case BotStanceBurn: + case BotStanceBurnAE: + castChance = isPrimarySlower?50:0; + break; + default: + castChance = 0; + break; + } + break; + case MAGICIAN: + case DRUID: + case PALADIN: + case CLERIC: + case WIZARD: + case NECROMANCER: + case SHADOWKNIGHT: + case RANGER: + case WARRIOR: + case BERSERKER: + case MONK: + case ROGUE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + _spellCastingChances[botStance][SpellType_SlowIndex] = castChance; + + //Debuff + switch(botClass) + { + case ENCHANTER: + case SHAMAN: + case MAGICIAN: + case DRUID: + case NECROMANCER: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = 25; + break; + case BotStanceEfficient: + case BotStanceAggressive: + castChance = 15; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + break; + case BEASTLORD: + case RANGER: + case SHADOWKNIGHT: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceReactive: + castChance = 15; + break; + case BotStanceEfficient: + case BotStanceAggressive: + castChance = 10; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + break; + case BARD: + switch(botStance) + { + case BotStanceBalanced: + case BotStanceEfficient: + castChance = 25; + break; + case BotStanceReactive: + case BotStanceAggressive: + castChance = 50; + break; + case BotStanceBurn: + case BotStanceBurnAE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + break; + case CLERIC: + case PALADIN: + case WIZARD: + case WARRIOR: + case BERSERKER: + case MONK: + case ROGUE: + castChance = 0; + break; + default: + castChance = 0; + break; + } + _spellCastingChances[botStance][SpellType_DebuffIndex] = castChance; + + //Cure + castChance = 0; + _spellCastingChances[botStance][SpellType_CureIndex] = castChance; +} + +uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) { + int index = 0; + int botStance = (int)GetBotStance(); + uint8 chance = 0; + + if(GetBotStance() >= MaxStances) + return 0; + + switch (spellType) { + case SpellType_Nuke: { + index = SpellType_NukeIndex; + break; + } + case SpellType_Heal: { + index = SpellType_HealIndex; + break; + } + case SpellType_Root: { + index = SpellType_RootIndex; + break; + } + case SpellType_Buff: { + index = SpellType_BuffIndex; + break; + } + case SpellType_Escape: { + index = SpellType_EscapeIndex; + break; + } + case SpellType_Pet: { + index = SpellType_PetIndex; + break; + } + case SpellType_Lifetap: { + index = SpellType_LifetapIndex; + break; + } + case SpellType_Snare: { + index = SpellType_SnareIndex; + break; + } + case SpellType_DOT: { + index = SpellType_DOTIndex; + break; + } + case SpellType_Dispel: { + index = SpellType_DispelIndex; + break; + } + case SpellType_InCombatBuff: { + index = SpellType_InCombatBuffIndex; + break; + } + case SpellType_Mez: { + index = SpellType_MezIndex; + break; + } + case SpellType_Charm: { + index = SpellType_CharmIndex; + break; + } + case SpellType_Slow: { + index = SpellType_SlowIndex; + break; + } + case SpellType_Debuff: { + index = SpellType_DebuffIndex; + break; + } + case SpellType_Cure: { + index = SpellType_CureIndex; + break; + } + } + + if(index >= MaxSpellTypes) + return 0; + + chance = _spellCastingChances[botStance][index]; + + return chance; +} + +#endif diff --git a/zone/client.cpp b/zone/client.cpp new file mode 100644 index 000000000..ff90e3e00 --- /dev/null +++ b/zone/client.cpp @@ -0,0 +1,7279 @@ +/* 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 + 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 +using namespace std; +#include +using namespace std; +#include +#include +#include +#include +#include + +// for windows compile +#ifdef _WINDOWS +#define abs64 _abs64 +#define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#else +#include +#include +#include +#include "../common/unix.h" +#define abs64 abs +#endif + +extern volatile bool RunLoops; +extern bool spells_loaded; + +#include "features.h" +#include "masterentity.h" +#include "worldserver.h" +#include "../common/misc.h" +#include "zonedb.h" +#include "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/MiscFunctions.h" +#include "forage.h" +#include "command.h" +#include "StringIDs.h" +#include "NpcAI.h" +#include "client_logs.h" +#include "guild_mgr.h" +#include "QuestParserCollection.h" + + +extern EntityList entity_list; +extern Zone* zone; +extern volatile bool ZoneLoaded; +extern WorldServer worldserver; +#if !defined(NEW_LoadSPDat) && !defined(DB_LoadSPDat) + extern SPDat_Spell_Struct spells[SPDAT_RECORDS]; +#endif +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 + "", // lastname + 0, // cur_hp + 0, // max_hp + 0, // gender + 0, // race + 0, // class + BT_Humanoid, // bodytype + 0, // deity + 0, // level + 0, // npctypeid + 0, // size + 0.7, // runspeed + 0, // heading + 0, // x + 0, // y + 0, // z + 0, // light + 0xFF, // texture + 0xFF, // helmtexture + 0, // ac + 0, // atk + 0, // str + 0, // sta + 0, // dex + 0, // agi + 0, // int + 0, // wis + 0, // cha + 0, // Luclin Hair Colour + 0, // Luclin Beard Color + 0, // Luclin Eye1 + 0, // Luclin Eye2 + 0, // Luclin Hair Style + 0, // Luclin Face + 0, // Luclin Beard + 0, // Drakkin Heritage + 0, // Drakkin Tattoo + 0, // Drakkin Details + 0, // Armor Tint + 0xff, // AA Title + 0, // see_invis + 0, // see_invis_undead + 0, + 0, + 0, + 0, + 0, // qglobal + 0, // maxlevel + 0 // scalerate + + ), + //these must be listed in the order they appear in client.h + position_timer(250), + hpupdate_timer(1800), + camp_timer(29000), + process_timer(100), + stamina_timer(40000), + zoneinpacket_timer(3000), + linkdead_timer(RuleI(Zone,ClientLinkdeadMS)), + dead_timer(2000), + global_channel_timer(1000), + shield_timer(500), + fishing_timer(8000), + endupkeep_timer(1000), + forget_timer(0), + autosave_timer(RuleI(Character, AutosaveIntervalS)*1000), +#ifdef REVERSE_AGGRO + scanarea_timer(AIClientScanarea_delay), +#endif + tribute_timer(Tribute_duration), +#ifdef PACKET_UPDATE_MANAGER + update_manager(ieqs), +#endif + proximity_timer(ClientProximity_interval), + TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), + charm_update_timer(60000), + rest_timer(1), + charm_class_attacks_timer(3000), + charm_cast_timer(3500), + qglobal_purge_timer(30000), + TrackingTimer(2000), + RespawnFromHoverTimer(0), + merc_timer(RuleI(Mercs, UpkeepIntervalMS)) +{ + for(int cf=0; cf < _FilterCount; cf++) + ClientFilters[cf] = FilterShow; + character_id = 0; + conn_state = NoPacketsReceived; + client_data_loaded = false; + feigned = false; + berserk = false; + dead = false; + eqs = ieqs; + ip = eqs->GetRemoteIP(); + port = ntohs(eqs->GetRemotePort()); + client_state = CLIENT_CONNECTING; + Trader=false; + Buyer = false; + CustomerID = 0; + TrackingID = 0; + WID = 0; + account_id = 0; + admin = 0; + lsaccountid = 0; + shield_target = NULL; + SQL_log = NULL; + guild_id = GUILD_NONE; + guildrank = 0; + GuildBanker = false; + memset(lskey, 0, sizeof(lskey)); + strcpy(account_name, ""); + tellsoff = false; + last_reported_mana = 0; + last_reported_endur = 0; + gmhideme = false; + AFK = false; + LFG = false; + LFGFromLevel = 0; + LFGToLevel = 0; + LFGMatchFilter = false; + LFGComments[0] = '\0'; + LFP = false; + gmspeed = 0; + playeraction = 0; + SetTarget(0); + auto_attack = false; + auto_fire = false; + linkdead_timer.Disable(); + zonesummon_x = -2; + zonesummon_y = -2; + zonesummon_z = -2; + zonesummon_id = 0; + zonesummon_ignorerestrictions = 0; + zoning = false; + zone_mode = ZoneUnsolicited; + proximity_x = FLT_MAX; //arbitrary large number + proximity_y = FLT_MAX; + proximity_z = FLT_MAX; + casting_spell_id = 0; + npcflag = false; + npclevel = 0; + pQueuedSaveWorkID = 0; + position_timer_counter = 0; + fishing_timer.Disable(); + shield_timer.Disable(); + dead_timer.Disable(); + camp_timer.Disable(); + autosave_timer.Disable(); + instalog = false; + pLastUpdate = 0; + pLastUpdateWZ = 0; + m_pp.autosplit = false; + // initialise haste variable + m_tradeskill_object = NULL; + delaytimer = false; + PendingRezzXP = -1; + PendingRezzDBID = 0; + PendingRezzSpellID = 0; + numclients++; + // emuerror; + UpdateWindowTitle(); + horseId = 0; + tgb = false; + tribute_master_id = 0xFFFFFFFF; + tribute_timer.Disable(); + taskstate = NULL; + TotalSecondsPlayed = 0; + keyring.clear(); + bind_sight_target = NULL; + mercid = 0; + SetMerc(0); + + logging_enabled = CLIENT_DEFAULT_LOGGING_ENABLED; + + //for good measure: + memset(&m_pp, 0, sizeof(m_pp)); + memset(&m_epp, 0, sizeof(m_epp)); + PendingTranslocate = false; + PendingSacrifice = false; + BoatID = 0; + + KarmaUpdateTimer = new Timer(RuleI(Chat, KarmaUpdateIntervalMS)); + GlobalChatLimiterTimer = new Timer(RuleI(Chat, IntervalDurationMS)); + AttemptedMessages = 0; + TotalKarma = 0; + ClientVersion = EQClientUnknown; + ClientVersionBit = 0; + AggroCount = 0; + RestRegenHP = 0; + RestRegenMana = 0; + RestRegenEndurance = 0; + XPRate = 100; + cur_end = 0; + + m_TimeSinceLastPositionCheck = 0; + m_DistanceSinceLastPositionCheck = 0.0f; + m_ShadowStepExemption = 0; + m_KnockBackExemption = 0; + m_PortExemption = 0; + m_SenseExemption = 0; + m_CheatDetectMoved = false; + CanUseReport = true; + aa_los_me.x = 0; + aa_los_me.y = 0; + aa_los_me.z = 0; + aa_los_them.x = 0; + aa_los_them.y = 0; + aa_los_them.z = 0; + aa_los_them_mob = NULL; + los_status = false; + qGlobals = NULL; + HideCorpseMode = HideCorpseNone; + PendingGuildInvitation = false; + + cur_end = 0; + + InitializeBuffSlots(); + + adventure_request_timer = NULL; + adventure_create_timer = NULL; + adventure_leave_timer = NULL; + adventure_door_timer = NULL; + adv_requested_data = NULL; + adventure_stats_timer = NULL; + adventure_leaderboard_timer = NULL; + adv_data = NULL; + adv_requested_theme = 0; + adv_requested_id = 0; + adv_requested_member_count = 0; + + for(int i = 0; i < XTARGET_HARDCAP; ++i) + { + XTargets[i].Type = Auto; + XTargets[i].ID = 0; + XTargets[i].Name[0] = 0; + } + MaxXTargets = 5; + XTargetAutoAddHaters = true; +} + +Client::~Client() { +#ifdef BOTS + Bot::ProcessBotOwnerRefDelete(this); +#endif + if(IsInAGuild()) + guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(NULL)); + + Mob* horse = entity_list.GetMob(this->CastToClient()->GetHorseId()); + if (horse) + horse->Depop(); + + if(Trader) + database.DeleteTraderItem(this->CharacterID()); + + if(Buyer) + ToggleBuyerMode(false); + + if(conn_state != ClientConnectFinished) { + LogFile->write(EQEMuLog::Debug, "Client '%s' was destroyed before reaching the connected state:", GetName()); + ReportConnectingState(); + } + + if(m_tradeskill_object != NULL) { + m_tradeskill_object->Close(); + m_tradeskill_object = NULL; + } + +#ifdef CLIENT_LOGS + client_logs.unsubscribeAll(this); +#endif + + +// if(AbilityTimer || GetLevel()>=51) +// database.UpdateAndDeleteAATimers(CharacterID()); + + ChangeSQLLog(NULL); + if(IsDueling() && GetDuelTarget() != 0) { + Entity* entity = entity_list.GetID(GetDuelTarget()); + if(entity != NULL && entity->IsClient()) { + entity->CastToClient()->SetDueling(false); + entity->CastToClient()->SetDuelTarget(0); + entity_list.DuelMessage(entity->CastToClient(),this,true); + } + } + + if (shield_target) { + 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; + } + } + shield_target = NULL; + } + + if(GetTarget()) + GetTarget()->IsTargeted(-1); + + //if we are in a group and we are not zoning, force leave the group + if(isgrouped && !zoning && ZoneLoaded) + LeaveGroup(); + + UpdateWho(2); + + if(IsHoveringForRespawn()) + { + m_pp.zone_id = m_pp.binds[0].zoneId; + m_pp.zoneInstance = 0; + x_pos = m_pp.binds[0].x; + y_pos = m_pp.binds[0].y; + z_pos = m_pp.binds[0].z; + } + + // we save right now, because the client might be zoning and the world + // will need this data right away + Save(2); // This fails when database destructor is called first on shutdown + + safe_delete(taskstate); + safe_delete(KarmaUpdateTimer); + safe_delete(GlobalChatLimiterTimer); + safe_delete(qGlobals); + safe_delete(adventure_request_timer); + safe_delete(adventure_create_timer); + safe_delete(adventure_leave_timer); + safe_delete(adventure_door_timer); + safe_delete(adventure_stats_timer); + safe_delete(adventure_leaderboard_timer); + safe_delete_array(adv_requested_data); + safe_delete_array(adv_data); + + numclients--; + UpdateWindowTitle(); + if(zone) + zone->RemoveAuth(GetName()); + + //let the stream factory know were done with this stream + eqs->Close(); + eqs->ReleaseFromUse(); + + entity_list.RemoveClient(this); + UninitializeBuffSlots(); +} + +void Client::SendLogoutPackets() { + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_CancelTrade, sizeof(CancelTrade_Struct)); + CancelTrade_Struct* ct = (CancelTrade_Struct*) outapp->pBuffer; + ct->fromid = GetID(); + ct->action = groupActUpdate; + FastQueuePacket(&outapp); + + outapp = new EQApplicationPacket(OP_PreLogoutReply); + FastQueuePacket(&outapp); + +} + +void Client::ReportConnectingState() { + switch(conn_state) { + case NoPacketsReceived: //havent gotten anything + LogFile->write(EQEMuLog::Debug, "Client has not sent us an initial zone entry packet."); + break; + case ReceivedZoneEntry: //got the first packet, loading up PP + LogFile->write(EQEMuLog::Debug, "Client sent initial zone packet, but we never got their player info from the database."); + break; + case PlayerProfileLoaded: //our DB work is done, sending it + LogFile->write(EQEMuLog::Debug, "We were sending the player profile, tributes, tasks, spawns, time and weather, but never finished."); + break; + case ZoneInfoSent: //includes PP, tributes, tasks, spawns, time and weather + LogFile->write(EQEMuLog::Debug, "We successfully sent player info and spawns, waiting for client to request new zone."); + break; + case NewZoneRequested: //received and sent new zone request + LogFile->write(EQEMuLog::Debug, "We received client's new zone request, waiting for client spawn request."); + break; + case ClientSpawnRequested: //client sent ReqClientSpawn + LogFile->write(EQEMuLog::Debug, "We received the client spawn request, and were sending objects, doors, zone points and some other stuff, but never finished."); + break; + case ZoneContentsSent: //objects, doors, zone points + LogFile->write(EQEMuLog::Debug, "The rest of the zone contents were successfully sent, waiting for client ready notification."); + break; + case ClientReadyReceived: //client told us its ready, send them a bunch of crap like guild MOTD, etc + LogFile->write(EQEMuLog::Debug, "We received client ready notification, but never finished Client::CompleteConnect"); + break; + case ClientConnectFinished: //client finally moved to finished state, were done here + LogFile->write(EQEMuLog::Debug, " Client is successfully connected."); + break; + }; +} + +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; + _ZP(Client_Save); + + 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++) { + uint32 points = aa[a]->value; + if(points > HIGHEST_AA_VALUE) // Unifying this + { + 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()) + { + spentpoints += RequiredLevel->second.Cost; + } + else + spentpoints += (curAA->cost + (curAA->cost_inc * rank)); + } + } + } + } + + m_pp.aapoints_spent = spentpoints + m_epp.expended_aa; + + if (GetHP() <= 0) { + m_pp.cur_hp = GetMaxHP(); + } + else + m_pp.cur_hp = GetHP(); + + m_pp.mana = cur_mana; + m_pp.endurance = cur_end; + + database.SaveBuffs(this); + + TotalSecondsPlayed += (time(NULL) - m_pp.lastlogin); + m_pp.timePlayedMin = (TotalSecondsPlayed / 60); + m_pp.RestTimer = rest_timer.GetRemainingTime() / 1000; + + if(GetEPP().mercTimerRemaining > RuleI(Mercs, UpkeepIntervalMS)) + GetEPP().mercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); + + if(merc_timer.Enabled()) + { + GetEPP().mercTimerRemaining = merc_timer.GetRemainingTime(); + } + + m_pp.lastlogin = time(NULL); + if (pQueuedSaveWorkID) { + dbasync->CancelWork(pQueuedSaveWorkID); + pQueuedSaveWorkID = 0; + } + + if (GetPet() && !GetPet()->IsFamiliar() && GetPet()->CastToNPC()->GetPetSpellID() && !dead) { + NPC *pet = GetPet()->CastToNPC(); + m_petinfo.SpellID = pet->CastToNPC()->GetPetSpellID(); + m_petinfo.HP = pet->GetHP(); + m_petinfo.Mana = pet->GetMana(); + pet->GetPetState(m_petinfo.Buffs, m_petinfo.Items, m_petinfo.Name); + m_petinfo.petpower = pet->GetPetPower(); + } else { + memset(&m_petinfo, 0, sizeof(struct PetInfo)); + } + database.SavePetInfo(this); + + if(tribute_timer.Enabled()) { + m_pp.tribute_time_remaining = tribute_timer.GetRemainingTime(); + } else { + m_pp.tribute_time_remaining = 0xFFFFFFFF; + m_pp.tribute_active = 0; + } + + p_timers.Store(&database); + +// printf("Dumping inventory on save:\n"); +// m_inv.dumpInventory(); + + 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 { + cerr << "Failed to update player profile" << endl; + return false; + } + + 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() +{ + app = NULL; + ack_req = false; +} + +CLIENTPACKET::~CLIENTPACKET() +{ + safe_delete(app); +} + +//this assumes we do not own pApp, and clones it. +bool Client::AddPacket(const EQApplicationPacket *pApp, bool bAckreq) { + if (!pApp) + return false; + if(!zoneinpacket_timer.Enabled()) { + //drop the packet because it will never get sent. + return(false); + } + CLIENTPACKET *c = new CLIENTPACKET; + + c->ack_req = bAckreq; + c->app = pApp->Copy(); + + clientpackets.Append(c); + return true; +} + +//this assumes that it owns the object pointed to by *pApp +bool Client::AddPacket(EQApplicationPacket** pApp, bool bAckreq) { + if (!pApp || !(*pApp)) + return false; + if(!zoneinpacket_timer.Enabled()) { + //drop the packet because it will never get sent. + return(false); + } + CLIENTPACKET *c = new CLIENTPACKET; + + c->ack_req = bAckreq; + c->app = *pApp; + *pApp = 0; + + clientpackets.Append(c); + return true; +} + +bool Client::SendAllPackets() { + LinkedListIterator iterator(clientpackets); + + CLIENTPACKET* cp = 0; + iterator.Reset(); + while(iterator.MoreElements()) { + cp = iterator.GetData(); + if(eqs) + eqs->FastQueuePacket((EQApplicationPacket **)&cp->app, cp->ack_req); + iterator.RemoveCurrent(); +#if EQDEBUG >= 6 + LogFile->write(EQEMuLog::Normal, "Transmitting a packet"); +#endif + } + return true; +} + +void Client::QueuePacket(const EQApplicationPacket* app, bool ack_req, CLIENT_CONN_STATUS required_state, eqFilterType filter) { +/* if (app->opcode==0x9999) { + cout << "Sending an unknown opcode from: " << endl; + print_stacktrace(); + } + if (app->opcode==OP_SkillUpdate) { + cout << "Sending OP_SkillUpdate from: " << endl; + print_stacktrace(); + } +*/ + _ZP(Client_QueuePacket); + if(filter!=FilterNone){ + //this is incomplete... no support for FilterShowGroupOnly or FilterShowSelfOnly + if(GetFilter(filter) == FilterHide) + return; //Client has this filter on, no need to send packet + } + if(client_state != CLIENT_CONNECTED && required_state == CLIENT_CONNECTED){ + AddPacket(app, ack_req); + return; + } + + // 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); + } + else + if(eqs) + eqs->QueuePacket(app, ack_req); +} + +void Client::FastQueuePacket(EQApplicationPacket** app, bool ack_req, CLIENT_CONN_STATUS required_state) { + + //cout << "Sending: 0x" << hex << setw(4) << setfill('0') << (*app)->GetOpcode() << dec << ", size=" << (*app)->size << 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 { + if(eqs) + eqs->FastQueuePacket((EQApplicationPacket **)app, ack_req); + else if (app && (*app)) + delete *app; + *app = 0; + } + return; +} + +void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, const char* targetname) { + #if EQDEBUG >= 11 + LogFile->write(EQEMuLog::Debug,"Client::ChannelMessageReceived() Channel:%i message:'%s'", chan_num, message); + #endif + + if (targetname == NULL) { + targetname = (!GetTarget()) ? "" : GetTarget()->GetName(); + } + + if(RuleB(Chat, EnableAntiSpam)) + { + if(strcmp(targetname, "discard") != 0) + { + if(chan_num == 3 || chan_num == 4 || chan_num == 5 || chan_num == 7) + { + if(GlobalChatLimiterTimer) + { + if(GlobalChatLimiterTimer->Check(false)) + { + GlobalChatLimiterTimer->Start(RuleI(Chat, IntervalDurationMS)); + AttemptedMessages = 0; + } + } + + uint32 AllowedMessages = RuleI(Chat, MinimumMessagesPerInterval) + TotalKarma; + AllowedMessages = AllowedMessages > RuleI(Chat, MaximumMessagesPerInterval) ? RuleI(Chat, MaximumMessagesPerInterval) : AllowedMessages; + + if(RuleI(Chat, MinStatusToBypassAntiSpam) <= Admin()) + AllowedMessages = 10000; + + AttemptedMessages++; + if(AttemptedMessages > AllowedMessages) + { + if(AttemptedMessages > RuleI(Chat, MaxMessagesBeforeKick)) + { + Kick(); + return; + } + if(GlobalChatLimiterTimer) + { + Message(0, "You have been rate limited, you can send more messages in %i seconds.", + GlobalChatLimiterTimer->GetRemainingTime() / 1000); + return; + } + else + { + Message(0, "You have been rate limited, you can send more messages in 60 seconds."); + return; + } + } + } + } + } + + + if(RuleB(QueryServ, PlayerChatLogging)){ + ServerPacket* pack = new ServerPacket(ServerOP_Speech, sizeof(Server_Speech_Struct)+strlen(message)+1); + Server_Speech_Struct* sem = (Server_Speech_Struct*) pack->pBuffer; + + if(chan_num == 0) + sem->guilddbid = GuildID(); + else + sem->guilddbid = 0; + + strcpy(sem->message, message); + sem->minstatus = this->Admin(); + sem->type = chan_num; + if(targetname != 0) + strcpy(sem->to,targetname); + + if(GetName() != 0) + strcpy(sem->from,GetName()); + + pack->Deflate(); + if(worldserver.Connected()) + worldserver.SendPacket(pack); + safe_delete(pack); + } + + switch(chan_num) + { + case 0: { // GuildChat + 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)) + Message(0, "Error: You dont have permission to speak to the guild."); + else if (!worldserver.SendChannelMessage(this, targetname, chan_num, GuildID(), language, message)) + Message(0, "Error: World server disconnected"); + break; + } + case 2: { // GroupChat + Raid* raid = entity_list.GetRaidByClient(this); + if(raid){ + raid->RaidGroupSay((const char*) message, this); + break; + } + + Group* group = GetGroup(); + if(group != NULL) { + group->GroupMessage(this,language,lang_skill,(const char*) message); + } + break; + } + case 15: { //raid say + Raid* raid = entity_list.GetRaidByClient(this); + if(raid){ + raid->RaidSay((const char*) message, this); + } + break; + } + case 3: { // Shout + Mob *sender = this; + if (GetPet() && GetPet()->FindType(SE_VoiceGraft)) + sender = GetPet(); + + entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message); + break; + } + case 4: { // Auction + if(RuleB(Chat, ServerWideAuction)) + { + if(!global_channel_timer.Check()) + { + if(strlen(targetname)==0) + ChannelMessageReceived(5, language, lang_skill, message, "discard"); //Fast typer or spammer?? + else + return; + } + + if(GetRevoked()) + { + Message(0, "You have been revoked. You may not talk on Auction."); + return; + } + + if(TotalKarma < RuleI(Chat, KarmaGlobalChatLimit)) + { + if(GetLevel() < RuleI(Chat, GlobalChatLevelLimit)) + { + Message(0, "You do not have permission to talk in Auction at this time."); + return; + } + } + + if (!worldserver.SendChannelMessage(this, 0, 4, 0, language, message)) + Message(0, "Error: World server disconnected"); + } + else if(!RuleB(Chat, ServerWideAuction)){ + Mob *sender = this; + + if (GetPet() && GetPet()->FindType(SE_VoiceGraft)) + sender = GetPet(); + + entity_list.ChannelMessage(sender, chan_num, language, message); + } + break; + } + case 5: { // OOC + if(RuleB(Chat, ServerWideOOC)) + { + if(!global_channel_timer.Check()) + { + if(strlen(targetname)==0) + ChannelMessageReceived(5, language, lang_skill, message,"discard"); //Fast typer or spammer?? + else + return; + } + if(worldserver.IsOOCMuted() && admin < 100) + { + Message(0,"OOC has been muted. Try again later."); + return; + } + + if(GetRevoked()) + { + Message(0, "You have been revoked. You may not talk on OOC."); + return; + } + + if(TotalKarma < RuleI(Chat, KarmaGlobalChatLimit)) + { + if(GetLevel() < RuleI(Chat, GlobalChatLevelLimit)) + { + Message(0, "You do not have permission to talk in OOC at this time."); + return; + } + } + + if (!worldserver.SendChannelMessage(this, 0, 5, 0, language, message)) + { + Message(0, "Error: World server disconnected"); + } + } + else if(!RuleB(Chat, ServerWideOOC)) + { + Mob *sender = this; + + if (GetPet() && GetPet()->FindType(SE_VoiceGraft)) + sender = GetPet(); + + entity_list.ChannelMessage(sender, chan_num, language, message); + } + break; + } + case 6: // Broadcast + case 11: { // GMSay + 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 + if(!global_channel_timer.Check()) + { + if(strlen(targetname)==0) + ChannelMessageReceived(7, language, lang_skill, message, "discard"); //Fast typer or spammer?? + else + return; + } + + if(GetRevoked()) + { + Message(0, "You have been revoked. You may not send tells."); + return; + } + + if(TotalKarma < RuleI(Chat, KarmaGlobalChatLimit)) + { + if(GetLevel() < RuleI(Chat, GlobalChatLevelLimit)) + { + Message(0, "You do not have permission to send tells at this time."); + return; + } + } + + char target_name[64]; + + if(targetname) + { + size_t i = strlen(targetname); + int x; + for(x = 0; x < i; ++x) + { + if(targetname[x] == '%') + { + target_name[x] = '/'; + } + else + { + target_name[x] = targetname[x]; + } + } + target_name[x] = '\0'; + } + + if(!worldserver.SendChannelMessage(this, target_name, chan_num, 0, language, message)) + Message(0, "Error: World server disconnected"); + break; + } + case 8: { // /say + if(message[0] == COMMAND_CHAR) { + if(command_dispatch(this, message) == -2){ + if(RuleB(Chat, FlowCommandstoPerl_EVENT_SAY)){ + if(parse->PlayerHasQuestSub("EVENT_SAY")) { + parse->EventPlayer(EVENT_SAY, this, message, language); + } + }else{ + this->Message(13, "Command '%s' not recognized.", message); + } + } + break; + } + Mob* sender = this; + if (GetPet() && GetPet()->FindType(SE_VoiceGraft)) + sender = GetPet(); + + printf("Message: %s\n",message); + entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message); + if(parse->PlayerHasQuestSub("EVENT_SAY")) + { + parse->EventPlayer(EVENT_SAY, this, message, language); + } + + if (sender != this) + break; + + if(quest_manager.ProximitySayInUse()) + entity_list.ProcessProximitySay(message, this, language); + + if (GetTarget() != 0 && GetTarget()->IsNPC()) { + if(!GetTarget()->CastToNPC()->IsEngaged()) { + CheckLDoNHail(GetTarget()); + CheckEmoteHail(GetTarget(),message); + + if(parse->HasQuestSub(GetTarget()->GetNPCTypeID(),"EVENT_SAY")){ + if (DistNoRootNoZ(*GetTarget()) <= 200) { + if(GetTarget()->CastToNPC()->IsMoving() && !GetTarget()->CastToNPC()->IsOnHatelist(GetTarget())) + GetTarget()->CastToNPC()->PauseWandering(RuleI(NPC, SayPauseTimeInSec)); + parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, message, language); + } + } + + if (RuleB(TaskSystem, EnableTaskSystem) && DistNoRootNoZ(*GetTarget()) <= 200) { + + if(GetTarget()->CastToNPC()->IsMoving() && !GetTarget()->CastToNPC()->IsOnHatelist(GetTarget())) + GetTarget()->CastToNPC()->PauseWandering(RuleI(NPC, SayPauseTimeInSec)); + + if(UpdateTasksOnSpeakWith(GetTarget()->GetNPCTypeID())) { + // If the client had an activity to talk to this NPC, make the NPC turn to face him if + // he isn't moving. Makes things look better. + if(!GetTarget()->CastToNPC()->IsMoving()) + GetTarget()->FaceTarget(this); + } + } + } + else { + if(parse->HasQuestSub(GetTarget()->GetNPCTypeID(),"EVENT_AGGRO_SAY")) { + if (DistNoRootNoZ(*GetTarget()) <= 200) { + parse->EventNPC(EVENT_AGGRO_SAY, GetTarget()->CastToNPC(), this, message, language); + } + } + } + + } + break; + } + case 20: + { + // UCS Relay for Underfoot and later. + if(!worldserver.SendChannelMessage(this, 0, chan_num, 0, language, message)) + Message(0, "Error: World server disconnected"); + break; + } + case 22: + { + // Emotes for Underfoot and later. + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Emote, 4 + strlen(message) + strlen(GetName()) + 2); + Emote_Struct* es = (Emote_Struct*)outapp->pBuffer; + char *Buffer = (char *)es; + Buffer += 4; + snprintf(Buffer, sizeof(Emote_Struct) - 4, "%s %s", GetName(), message); + entity_list.QueueCloseClients(this, outapp, true, 100,0,true,FILTER_SOCIALS); + safe_delete(outapp); + + break; + } + default: { + Message(0, "Channel (%i) not implemented",(uint16)chan_num); + } + } +} + +// if no language skill is specified, call the function with a skill of 100. +void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, const char* message, ...) { + ChannelMessageSend(from, to, chan_num, language, 100, message); +} + +void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...) { + if ((chan_num==11 && !(this->GetGM())) || (chan_num==10 && this->Admin()<80)) // dont need to send /pr & /petition to everybody + return; + va_list argptr; + char buffer[4096]; + char message_sender[64]; + + va_start(argptr, message); + vsnprintf(buffer, 4096, message, argptr); + va_end(argptr); + + EQApplicationPacket app(OP_ChannelMessage, sizeof(ChannelMessage_Struct)+strlen(buffer)+1); + ChannelMessage_Struct* cm = (ChannelMessage_Struct*)app.pBuffer; + + if (from == 0) + strcpy(cm->sender, "ZServer"); + else if (from[0] == 0) + strcpy(cm->sender, "ZServer"); + else { + CleanMobName(from, message_sender); + strcpy(cm->sender, message_sender); + } + if (to != 0) + strcpy((char *) cm->targetname, to); + else if (chan_num == 7) + strcpy(cm->targetname, m_pp.name); + else + cm->targetname[0] = 0; + + uint8 ListenerSkill; + + if (language < MAX_PP_LANGUAGE) { + ListenerSkill = m_pp.languages[language]; + if (ListenerSkill == 0) { + cm->language = (MAX_PP_LANGUAGE - 1); // in an unknown tongue + } + else { + cm->language = language; + } + } + else { + ListenerSkill = m_pp.languages[0]; + cm->language = 0; + } + + // set effective language skill = lower of sender and receiver skills + int32 EffSkill = (lang_skill < ListenerSkill ? lang_skill : ListenerSkill); + if (EffSkill > 100) // maximum language skill is 100 + EffSkill = 100; + cm->skill_in_language = EffSkill; + + // Garble the message based on listener skill + if (ListenerSkill < 100) { + GarbleMessage(buffer, (100 - ListenerSkill)); + } + + cm->chan_num = chan_num; + strcpy(&cm->message[0], buffer); + QueuePacket(&app); + + if ((chan_num == 2) && (ListenerSkill < 100)) { // group message in unmastered language, check for skill up + if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName())) + CheckLanguageSkillIncrease(language, lang_skill); + } +} + +void Client::Message(uint32 type, const char* message, ...) { + if (GetFilter(FilterSpellDamage) == FilterHide && type == MT_NonMelee) + return; + if (GetFilter(FilterMeleeCrits) == FilterHide && type == MT_CritMelee) //98 is self... + return; + if (GetFilter(FilterSpellCrits) == FilterHide && type == MT_SpellCrits) + return; + + va_list argptr; + char *buffer = new char[4096]; + va_start(argptr, message); + vsnprintf(buffer, 4096, message, argptr); + va_end(argptr); + + size_t len = strlen(buffer); + + //client dosent like our packet all the time unless + //we make it really big, then it seems to not care that + //our header is malformed. + //len = 4096 - sizeof(SpecialMesg_Struct); + + uint32 len_packet = sizeof(SpecialMesg_Struct)+len; + EQApplicationPacket* app = new EQApplicationPacket(OP_SpecialMesg, len_packet); + SpecialMesg_Struct* sm=(SpecialMesg_Struct*)app->pBuffer; + sm->header[0] = 0x00; // Header used for #emote style messages.. + sm->header[1] = 0x00; // Play around with these to see other types + sm->header[2] = 0x00; + sm->msg_type = type; + memcpy(sm->message, buffer, len+1); + + FastQueuePacket(&app); + + safe_delete_array(buffer); +} + +void Client::QuestJournalledMessage(const char *npcname, const char* message) { + + // npcnames longer than 60 characters crash the client when they log back in + const int MaxNPCNameLength = 60; + // I assume there is an upper safe limit on the message length. Don't know what it is, but 4000 doesn't crash + // the client. + const int MaxMessageLength = 4000; + + char OutNPCName[MaxNPCNameLength+1]; + char OutMessage[MaxMessageLength+1]; + + // Apparently Visual C++ snprintf is not C99 compliant and doesn't put the null terminator + // in if the formatted string >= the maximum length, so we put it in. + // + snprintf(OutNPCName, MaxNPCNameLength, "%s", npcname); OutNPCName[MaxNPCNameLength]='\0'; + snprintf(OutMessage, MaxMessageLength, "%s", message); OutMessage[MaxMessageLength]='\0'; + + uint32 len_packet = sizeof(SpecialMesg_Struct) + strlen(OutNPCName) + strlen(OutMessage); + EQApplicationPacket* app = new EQApplicationPacket(OP_SpecialMesg, len_packet); + SpecialMesg_Struct* sm=(SpecialMesg_Struct*)app->pBuffer; + + sm->header[0] = 0; + sm->header[1] = 2; + sm->header[2] = 0; + sm->msg_type = 0x0a; + sm->target_spawn_id = GetID(); + + char *dest = &sm->sayer[0]; + + memcpy(dest, OutNPCName, strlen(OutNPCName) + 1); + + dest = dest + strlen(OutNPCName) + 13; + + memcpy(dest, OutMessage, strlen(OutMessage) + 1); + + QueuePacket(app); + + safe_delete(app); +} + +void Client::SetMaxHP() { + if(dead) + return; + SetHP(CalcMaxHP()); + SendHPUpdate(); + Save(); +} + +bool Client::UpdateLDoNPoints(int32 points, uint32 theme) +{ + +/* make sure total stays in sync with individual buckets + m_pp.ldon_points_available = m_pp.ldon_points_guk + +m_pp.ldon_points_mir + +m_pp.ldon_points_mmc + +m_pp.ldon_points_ruj + +m_pp.ldon_points_tak; */ + + if(points < 0) + { + if(m_pp.ldon_points_available < (0-points)) + return false; + } + switch(theme) + { + // handle generic points (theme=0) + case 0: + { // no theme, so distribute evenly across all + int splitpts=points/5; + int gukpts=splitpts+(points%5); + int mirpts=splitpts; + int mmcpts=splitpts; + int rujpts=splitpts; + int takpts=splitpts; + + splitpts=0; + + if(points < 0) + { + if(m_pp.ldon_points_available < (0-points)) + { + return false; + } + if(m_pp.ldon_points_guk < (0-gukpts)) + { + mirpts+=gukpts+m_pp.ldon_points_guk; + gukpts=0-m_pp.ldon_points_guk; + } + if(m_pp.ldon_points_mir < (0-mirpts)) + { + mmcpts+=mirpts+m_pp.ldon_points_mir; + mirpts=0-m_pp.ldon_points_mir; + } + if(m_pp.ldon_points_mmc < (0-mmcpts)) + { + rujpts+=mmcpts+m_pp.ldon_points_mmc; + mmcpts=0-m_pp.ldon_points_mmc; + } + if(m_pp.ldon_points_ruj < (0-rujpts)) + { + takpts+=rujpts+m_pp.ldon_points_ruj; + rujpts=0-m_pp.ldon_points_ruj; + } + if(m_pp.ldon_points_tak < (0-takpts)) + { + splitpts=takpts+m_pp.ldon_points_tak; + takpts=0-m_pp.ldon_points_tak; + } + } + m_pp.ldon_points_guk += gukpts; + m_pp.ldon_points_mir+=mirpts; + m_pp.ldon_points_mmc += mmcpts; + m_pp.ldon_points_ruj += rujpts; + m_pp.ldon_points_tak += takpts; + points-=splitpts; + // if anything left, recursively loop thru again + if (splitpts !=0) + UpdateLDoNPoints(splitpts,0); + break; + } + case 1: + { + if(points < 0) + { + if(m_pp.ldon_points_guk < (0-points)) + return false; + } + m_pp.ldon_points_guk += points; + break; + } + case 2: + { + if(points < 0) + { + if(m_pp.ldon_points_mir < (0-points)) + return false; + } + m_pp.ldon_points_mir += points; + break; + } + case 3: + { + if(points < 0) + { + if(m_pp.ldon_points_mmc < (0-points)) + return false; + } + m_pp.ldon_points_mmc += points; + break; + } + case 4: + { + if(points < 0) + { + if(m_pp.ldon_points_ruj < (0-points)) + return false; + } + m_pp.ldon_points_ruj += points; + break; + } + case 5: + { + if(points < 0) + { + if(m_pp.ldon_points_tak < (0-points)) + return false; + } + m_pp.ldon_points_tak += points; + break; + } + } + 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; + apus->ldon_guk_points = m_pp.ldon_points_guk; + apus->ldon_mirugal_points = m_pp.ldon_points_mir; + apus->ldon_mistmoore_points = m_pp.ldon_points_mmc; + apus->ldon_rujarkian_points = m_pp.ldon_points_ruj; + apus->ldon_takish_points = m_pp.ldon_points_tak; + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + return true; + + return(false); +} + +void Client::SetSkill(SkillType skillid, uint16 value) { + if (skillid > HIGHEST_SKILL) + return; + m_pp.skills[skillid] = value; // We need to be able to #setskill 254 and 255 to reset skills + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct)); + SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer; + skill->skillId=skillid; + skill->value=value; + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::IncreaseLanguageSkill(int skill_id, int value) { + + if (skill_id >= MAX_PP_LANGUAGE) + return; //Invalid lang id + + m_pp.languages[skill_id] += value; + + if (m_pp.languages[skill_id] > 100) //Lang skill above max + m_pp.languages[skill_id] = 100; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct)); + SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer; + skill->skillId = 100 + skill_id; + skill->value = m_pp.languages[skill_id]; + QueuePacket(outapp); + safe_delete(outapp); + + Message_StringID( MT_Skills, LANG_SKILL_IMPROVED ); //Notify client +} + +void Client::AddSkill(SkillType skillid, uint16 value) { + if (skillid > HIGHEST_SKILL) + return; + value = GetRawSkill(skillid) + value; + uint16 max = GetMaxSkillAfterSpecializationRules(skillid, MaxSkill(skillid)); + if (value > max) + value = max; + SetSkill(skillid, value); +} + +void Client::SendSound(){//Makes a sound. + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Sound, 68); + unsigned char x[68]; + memset(x, 0, 68); + x[0]=0x22; + memset(&x[4],0x8002,sizeof(uint16)); + memset(&x[8],0x8624,sizeof(uint16)); + memset(&x[12],0x4A01,sizeof(uint16)); + x[16]=0x05; + x[28]=0x00;//change this value to give gold to the client + memset(&x[40],0xFFFFFFFF,sizeof(uint32)); + memset(&x[44],0xFFFFFFFF,sizeof(uint32)); + memset(&x[48],0xFFFFFFFF,sizeof(uint32)); + memset(&x[52],0xFFFFFFFF,sizeof(uint32)); + memset(&x[56],0xFFFFFFFF,sizeof(uint32)); + memset(&x[60],0xFFFFFFFF,sizeof(uint32)); + memset(&x[64],0xffffffff,sizeof(uint32)); + memcpy(outapp->pBuffer,x,outapp->size); + QueuePacket(outapp); + DumpPacket(outapp); + safe_delete(outapp); + +} +void Client::UpdateWho(uint8 remove) { + if (account_id == 0) + return; + if (!worldserver.Connected()) + return; + ServerPacket* pack = new ServerPacket(ServerOP_ClientList, sizeof(ServerClientList_Struct)); + ServerClientList_Struct* scl = (ServerClientList_Struct*) pack->pBuffer; + scl->remove = remove; + scl->wid = this->GetWID(); + scl->IP = this->GetIP(); + scl->charid = this->CharacterID(); + strcpy(scl->name, this->GetName()); + + scl->gm = GetGM(); + scl->Admin = this->Admin(); + scl->AccountID = this->AccountID(); + strcpy(scl->AccountName, this->AccountName()); + scl->LSAccountID = this->LSAccountID(); + strn0cpy(scl->lskey, lskey, sizeof(scl->lskey)); + scl->zone = zone->GetZoneID(); + scl->instance_id = zone->GetInstanceID(); + scl->race = this->GetRace(); + scl->class_ = GetClass(); + scl->level = GetLevel(); + if (m_pp.anon == 0) + scl->anon = 0; + else if (m_pp.anon == 1) + scl->anon = 1; + else if (m_pp.anon >= 2) + scl->anon = 2; + + scl->ClientVersion = GetClientVersion(); + scl->tellsoff = tellsoff; + scl->guild_id = guild_id; + scl->LFG = LFG; + if(LFG) { + scl->LFGFromLevel = LFGFromLevel; + scl->LFGToLevel = LFGToLevel; + scl->LFGMatchFilter = LFGMatchFilter; + memcpy(scl->LFGComments, LFGComments, sizeof(scl->LFGComments)); + } + + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void Client::WhoAll(Who_All_Struct* whom) { + + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_Who, sizeof(ServerWhoAll_Struct)); + ServerWhoAll_Struct* whoall = (ServerWhoAll_Struct*) pack->pBuffer; + whoall->admin = this->Admin(); + whoall->fromid=this->GetID(); + strcpy(whoall->from, this->GetName()); + strn0cpy(whoall->whom, whom->whom, 64); + whoall->lvllow = whom->lvllow; + whoall->lvlhigh = whom->lvlhigh; + whoall->gmlookup = whom->gmlookup; + whoall->wclass = whom->wclass; + whoall->wrace = whom->wrace; + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +void Client::FriendsWho(char *FriendsString) { + + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_FriendsWho, sizeof(ServerFriendsWho_Struct) + strlen(FriendsString)); + ServerFriendsWho_Struct* FriendsWho = (ServerFriendsWho_Struct*) pack->pBuffer; + FriendsWho->FromID = this->GetID(); + strcpy(FriendsWho->FromName, GetName()); + strcpy(FriendsWho->FriendsString, FriendsString); + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + + +void Client::UpdateAdmin(bool iFromDB) { + int16 tmp = admin; + if (iFromDB) + admin = database.CheckStatus(account_id); + if (tmp == admin && iFromDB) + return; + + if(m_pp.gm) + { +#if EQDEBUG >= 5 + printf("%s is a GM\n", GetName()); +#endif +// no need for this, having it set in pp you already start as gm +// and it's also set in your spawn packet so other people see it too +// SendAppearancePacket(AT_GM, 1, false); + petition_list.UpdateGMQueue(); + } + + UpdateWho(); +} + +void Client::SetStats(uint8 type,int16 set_val){ + if(type>STAT_DISEASE){ + printf("Error in Client::IncStats, received invalid type of: %i\n",type); + return; + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_IncreaseStats,sizeof(IncreaseStat_Struct)); + IncreaseStat_Struct* iss=(IncreaseStat_Struct*)outapp->pBuffer; + switch(type){ + case STAT_STR: + if(set_val>0) + iss->str=set_val; + if(set_val<0) + m_pp.STR=0; + else if(set_val>255) + m_pp.STR=255; + else + m_pp.STR=set_val; + break; + case STAT_STA: + if(set_val>0) + iss->sta=set_val; + if(set_val<0) + m_pp.STA=0; + else if(set_val>255) + m_pp.STA=255; + else + m_pp.STA=set_val; + break; + case STAT_AGI: + if(set_val>0) + iss->agi=set_val; + if(set_val<0) + m_pp.AGI=0; + else if(set_val>255) + m_pp.AGI=255; + else + m_pp.AGI=set_val; + break; + case STAT_DEX: + if(set_val>0) + iss->dex=set_val; + if(set_val<0) + m_pp.DEX=0; + else if(set_val>255) + m_pp.DEX=255; + else + m_pp.DEX=set_val; + break; + case STAT_INT: + if(set_val>0) + iss->int_=set_val; + if(set_val<0) + m_pp.INT=0; + else if(set_val>255) + m_pp.INT=255; + else + m_pp.INT=set_val; + break; + case STAT_WIS: + if(set_val>0) + iss->wis=set_val; + if(set_val<0) + m_pp.WIS=0; + else if(set_val>255) + m_pp.WIS=255; + else + m_pp.WIS=set_val; + break; + case STAT_CHA: + if(set_val>0) + iss->cha=set_val; + if(set_val<0) + m_pp.CHA=0; + else if(set_val>255) + m_pp.CHA=255; + else + m_pp.CHA=set_val; + break; + } + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::IncStats(uint8 type,int16 increase_val){ + if(type>STAT_DISEASE){ + printf("Error in Client::IncStats, received invalid type of: %i\n",type); + return; + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_IncreaseStats,sizeof(IncreaseStat_Struct)); + IncreaseStat_Struct* iss=(IncreaseStat_Struct*)outapp->pBuffer; + switch(type){ + case STAT_STR: + if(increase_val>0) + iss->str=increase_val; + if((m_pp.STR+increase_val*2)<0) + m_pp.STR=0; + else if((m_pp.STR+increase_val*2)>255) + m_pp.STR=255; + else + m_pp.STR+=increase_val*2; + break; + case STAT_STA: + if(increase_val>0) + iss->sta=increase_val; + if((m_pp.STA+increase_val*2)<0) + m_pp.STA=0; + else if((m_pp.STA+increase_val*2)>255) + m_pp.STA=255; + else + m_pp.STA+=increase_val*2; + break; + case STAT_AGI: + if(increase_val>0) + iss->agi=increase_val; + if((m_pp.AGI+increase_val*2)<0) + m_pp.AGI=0; + else if((m_pp.AGI+increase_val*2)>255) + m_pp.AGI=255; + else + m_pp.AGI+=increase_val*2; + break; + case STAT_DEX: + if(increase_val>0) + iss->dex=increase_val; + if((m_pp.DEX+increase_val*2)<0) + m_pp.DEX=0; + else if((m_pp.DEX+increase_val*2)>255) + m_pp.DEX=255; + else + m_pp.DEX+=increase_val*2; + break; + case STAT_INT: + if(increase_val>0) + iss->int_=increase_val; + if((m_pp.INT+increase_val*2)<0) + m_pp.INT=0; + else if((m_pp.INT+increase_val*2)>255) + m_pp.INT=255; + else + m_pp.INT+=increase_val*2; + break; + case STAT_WIS: + if(increase_val>0) + iss->wis=increase_val; + if((m_pp.WIS+increase_val*2)<0) + m_pp.WIS=0; + else if((m_pp.WIS+increase_val*2)>255) + m_pp.WIS=255; + else + m_pp.WIS+=increase_val*2; + break; + case STAT_CHA: + if(increase_val>0) + iss->cha=increase_val; + if((m_pp.CHA+increase_val*2)<0) + m_pp.CHA=0; + else if((m_pp.CHA+increase_val*2)>255) + m_pp.CHA=255; + else + m_pp.CHA+=increase_val*2; + break; + } + QueuePacket(outapp); + safe_delete(outapp); +} + +const int32& Client::SetMana(int32 amount) { + bool update = false; + if (amount < 0) + amount = 0; + if (amount > GetMaxMana()) + amount = GetMaxMana(); + if (amount != cur_mana) + update = true; + cur_mana = amount; + if (update) + Mob::SetMana(amount); + SendManaUpdatePacket(); + return cur_mana; +} + +void Client::SendManaUpdatePacket() { + if (!Connected() || IsCasting()) + return; + + if (GetClientVersion() >= EQClientSoD) { + SendManaUpdate(); + SendEnduranceUpdate(); + } + + //cout << "Sending mana update: " << (cur_mana - last_reported_mana) << endl; + if (last_reported_mana != cur_mana || last_reported_endur != cur_end) { + + + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ManaChange, sizeof(ManaChange_Struct)); + ManaChange_Struct* manachange = (ManaChange_Struct*)outapp->pBuffer; + manachange->new_mana = cur_mana; + manachange->stamina = cur_end; + manachange->spell_id = casting_spell_id; //always going to be 0... since we check IsCasting() + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + Group *g = GetGroup(); + + if(g) + { + outapp = new EQApplicationPacket(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct)); + EQApplicationPacket *outapp2 = new EQApplicationPacket(OP_MobEnduranceUpdate, sizeof(MobEnduranceUpdate_Struct)); + + MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp->pBuffer; + MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp2->pBuffer; + + mmus->spawn_id = meus->spawn_id = GetID(); + + mmus->mana = GetManaPercent(); + meus->endurance = GetEndurancePercent(); + + + for(int i = 0; i < MAX_GROUP_MEMBERS; ++i) + if(g->members[i] && g->members[i]->IsClient() && (g->members[i] != this) && (g->members[i]->CastToClient()->GetClientVersion() >= EQClientSoD)) + { + g->members[i]->CastToClient()->QueuePacket(outapp); + g->members[i]->CastToClient()->QueuePacket(outapp2); + } + + safe_delete(outapp); + safe_delete(outapp2); + } + + + last_reported_mana = cur_mana; + last_reported_endur = cur_end; + } +} + +// sends mana update to self +void Client::SendManaUpdate() +{ + EQApplicationPacket* mana_app = new EQApplicationPacket(OP_ManaUpdate,sizeof(ManaUpdate_Struct)); + ManaUpdate_Struct* mus = (ManaUpdate_Struct*)mana_app->pBuffer; + mus->cur_mana = GetMana(); + mus->max_mana = GetMaxMana(); + mus->spawn_id = GetID(); + QueuePacket(mana_app); + entity_list.QueueClientsByXTarget(this, mana_app, false); + safe_delete(mana_app); +} + +// sends endurance update to self +void Client::SendEnduranceUpdate() +{ + EQApplicationPacket* end_app = new EQApplicationPacket(OP_EnduranceUpdate,sizeof(EnduranceUpdate_Struct)); + EnduranceUpdate_Struct* eus = (EnduranceUpdate_Struct*)end_app->pBuffer; + eus->cur_end = GetEndurance(); + eus->max_end = GetMaxEndurance(); + eus->spawn_id = GetID(); + QueuePacket(end_app); + entity_list.QueueClientsByXTarget(this, end_app, false); + safe_delete(end_app); +} + +void Client::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) +{ + Mob::FillSpawnStruct(ns, ForWho); + + // Populate client-specific spawn information + ns->spawn.afk = AFK; + ns->spawn.lfg = LFG; // afk and lfg are cleared on zoning on live + ns->spawn.anon = m_pp.anon; + ns->spawn.gm = GetGM() ? 1 : 0; + ns->spawn.guildID = GuildID(); +// ns->spawn.linkdead = IsLD() ? 1 : 0; +// ns->spawn.pvp = GetPVP() ? 1 : 0; + + + strcpy(ns->spawn.title, m_pp.title); + strcpy(ns->spawn.suffix, m_pp.suffix); + + if (IsBecomeNPC() == true) + ns->spawn.NPC = 1; + else if (ForWho == this) + ns->spawn.NPC = 10; + else + ns->spawn.NPC = 0; + ns->spawn.is_pet = 0; + + if (!IsInAGuild()) { + ns->spawn.guildrank = 0xFF; + } else { + ns->spawn.guildrank = guild_mgr.GetDisplayedRank(GuildID(), GuildRank(), AccountID()); + } + ns->spawn.size = 0; // Changing size works, but then movement stops! (wth?) + ns->spawn.runspeed = (gmspeed == 0) ? runspeed : 3.125f; + if (!m_pp.showhelm) ns->spawn.showhelm = 0; + + // pp also hold this info; should we pull from there or inventory? + // (update: i think pp should do it, as this holds LoY dye - plus, this is ugly code with Inventory!) + const Item_Struct* item = NULL; + const ItemInst* inst = NULL; + if ((inst = m_inv[SLOT_HANDS]) && inst->IsType(ItemClassCommon)) { + item = inst->GetItem(); + ns->spawn.equipment[MATERIAL_HANDS] = item->Material; + ns->spawn.colors[MATERIAL_HANDS].color = GetEquipmentColor(MATERIAL_HANDS); + } + if ((inst = m_inv[SLOT_HEAD]) && inst->IsType(ItemClassCommon)) { + item = inst->GetItem(); + ns->spawn.equipment[MATERIAL_HEAD] = item->Material; + ns->spawn.colors[MATERIAL_HEAD].color = GetEquipmentColor(MATERIAL_HEAD); + } + if ((inst = m_inv[SLOT_ARMS]) && inst->IsType(ItemClassCommon)) { + item = inst->GetItem(); + ns->spawn.equipment[MATERIAL_ARMS] = item->Material; + ns->spawn.colors[MATERIAL_ARMS].color = GetEquipmentColor(MATERIAL_ARMS); + } + if ((inst = m_inv[SLOT_BRACER01]) && inst->IsType(ItemClassCommon)) { + item = inst->GetItem(); + ns->spawn.equipment[MATERIAL_BRACER]= item->Material; + ns->spawn.colors[MATERIAL_BRACER].color = GetEquipmentColor(MATERIAL_BRACER); + } + if ((inst = m_inv[SLOT_BRACER02]) && inst->IsType(ItemClassCommon)) { + item = inst->GetItem(); + ns->spawn.equipment[MATERIAL_BRACER]= item->Material; + ns->spawn.colors[MATERIAL_BRACER].color = GetEquipmentColor(MATERIAL_BRACER); + } + if ((inst = m_inv[SLOT_CHEST]) && inst->IsType(ItemClassCommon)) { + item = inst->GetItem(); + ns->spawn.equipment[MATERIAL_CHEST] = item->Material; + ns->spawn.colors[MATERIAL_CHEST].color = GetEquipmentColor(MATERIAL_CHEST); + } + if ((inst = m_inv[SLOT_LEGS]) && inst->IsType(ItemClassCommon)) { + item = inst->GetItem(); + ns->spawn.equipment[MATERIAL_LEGS] = item->Material; + ns->spawn.colors[MATERIAL_LEGS].color = GetEquipmentColor(MATERIAL_LEGS); + } + if ((inst = m_inv[SLOT_FEET]) && inst->IsType(ItemClassCommon)) { + item = inst->GetItem(); + ns->spawn.equipment[MATERIAL_FEET] = item->Material; + ns->spawn.colors[MATERIAL_FEET].color = GetEquipmentColor(MATERIAL_FEET); + } + if ((inst = m_inv[SLOT_PRIMARY]) && inst->IsType(ItemClassCommon)) { + item = inst->GetItem(); + if (strlen(item->IDFile) > 2) + ns->spawn.equipment[MATERIAL_PRIMARY] = atoi(&item->IDFile[2]); + } + if ((inst = m_inv[SLOT_SECONDARY]) && inst->IsType(ItemClassCommon)) { + item = inst->GetItem(); + if (strlen(item->IDFile) > 2) + ns->spawn.equipment[MATERIAL_SECONDARY] = atoi(&item->IDFile[2]); + } + + //these two may be related to ns->spawn.texture + /* + ns->spawn.npc_armor_graphic = texture; + ns->spawn.npc_helm_graphic = helmtexture; + */ + + //filling in some unknowns to make the client happy +// ns->spawn.unknown0002[2] = 3; + +} + +bool Client::GMHideMe(Client* client) { + if (gmhideme) { + if (client == 0) + return true; + else if (admin > client->Admin()) + return true; + else + return false; + } + else + return false; +} + +void Client::Duck() { + SetAppearance(eaCrouching, false); +} + +void Client::Stand() { + SetAppearance(eaStanding, false); +} + +void Client::ChangeLastName(const char* in_lastname) { + memset(m_pp.last_name, 0, sizeof(m_pp.last_name)); + strn0cpy(m_pp.last_name, in_lastname, sizeof(m_pp.last_name)); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMLastName, sizeof(GMLastName_Struct)); + GMLastName_Struct* gmn = (GMLastName_Struct*)outapp->pBuffer; + strcpy(gmn->name, name); + strcpy(gmn->gmname, name); + strcpy(gmn->lastname, in_lastname); + gmn->unknown[0]=1; + gmn->unknown[1]=1; + gmn->unknown[2]=1; + gmn->unknown[3]=1; + entity_list.QueueClients(this, outapp, false); + // Send name update packet here... once know what it is + safe_delete(outapp); +} + +bool Client::ChangeFirstName(const char* in_firstname, const char* gmname) +{ + // check duplicate name + bool usedname = database.CheckUsedName((const char*) in_firstname); + if (!usedname) { + return false; + } + + // update character_ + if(!database.UpdateName(GetName(), in_firstname)) + return false; + + // update pp + memset(m_pp.name, 0, sizeof(m_pp.name)); + snprintf(m_pp.name, sizeof(m_pp.name), "%s", in_firstname); + strcpy(name, m_pp.name); + Save(); + + // send name update packet + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMNameChange, sizeof(GMName_Struct)); + GMName_Struct* gmn=(GMName_Struct*)outapp->pBuffer; + strn0cpy(gmn->gmname,gmname,64); + strn0cpy(gmn->oldname,GetName(),64); + strn0cpy(gmn->newname,in_firstname,64); + gmn->unknown[0] = 1; + gmn->unknown[1] = 1; + gmn->unknown[2] = 1; + entity_list.QueueClients(this, outapp, false); + safe_delete(outapp); + + // finally, update the /who list + UpdateWho(); + + // success + return true; +} + +void Client::SetGM(bool toggle) { + m_pp.gm = toggle ? 1 : 0; + Message(13, "You are %s a GM.", m_pp.gm ? "now" : "no longer"); + SendAppearancePacket(AT_GM, m_pp.gm); + Save(); + UpdateWho(); +} + +void Client::ReadBook(BookRequest_Struct *book) { + char *txtfile = book->txtfile; + + if(txtfile[0] == '0' && txtfile[1] == '\0') { + //invalid book... coming up on non-book items. + return; + } + + string booktxt2 = database.GetBook(txtfile); + int length = booktxt2.length(); + + if (booktxt2[0] != '\0') { +#if EQDEBUG >= 6 + LogFile->write(EQEMuLog::Normal,"Client::ReadBook() textfile:%s Text:%s", txtfile, booktxt2.c_str()); +#endif + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ReadBook, length + sizeof(BookText_Struct)); + + BookText_Struct *out = (BookText_Struct *) outapp->pBuffer; + out->window = book->window; + if(GetClientVersion() >= EQClientSoF) + { + const ItemInst *inst = m_inv[book->invslot]; + if(inst) + out->type = inst->GetItem()->Book; + else + out->type = book->type; + } + else + { + out->type = book->type; + } + out->invslot = book->invslot; + memcpy(out->booktext, booktxt2.c_str(), length); + + QueuePacket(outapp); + safe_delete(outapp); + } +} + +void Client::QuestReadBook(const char* text, uint8 type) { + string booktxt2 = text; + int length = booktxt2.length(); + if (booktxt2[0] != '\0') { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ReadBook, length + sizeof(BookText_Struct)); + BookText_Struct *out = (BookText_Struct *) outapp->pBuffer; + out->window = 0xFF; + out->type = type; + out->invslot = 0; + memcpy(out->booktext, booktxt2.c_str(), length); + QueuePacket(outapp); + safe_delete(outapp); + } +} + +void Client::SendClientMoneyUpdate(uint8 type,uint32 amount){ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeMoneyUpdate,sizeof(TradeMoneyUpdate_Struct)); + TradeMoneyUpdate_Struct* mus= (TradeMoneyUpdate_Struct*)outapp->pBuffer; + mus->amount=amount; + mus->trader=0; + mus->type=type; + QueuePacket(outapp); + safe_delete(outapp); +} + +bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { + int64 copperpp,silver,gold,platinum; + copperpp = m_pp.copper; + silver = static_cast(m_pp.silver) * 10; + gold = static_cast(m_pp.gold) * 100; + platinum = static_cast(m_pp.platinum) * 1000; + + int64 clienttotal = copperpp + silver + gold + platinum; + + clienttotal -= copper; + if(clienttotal < 0) + { + return false; // Not enough money! + } + else + { + copperpp -= copper; + if(copperpp <= 0) + { + copper = abs64(copperpp); + m_pp.copper = 0; + } + else + { + m_pp.copper = copperpp; + if(updateclient) + SendMoneyUpdate(); + Save(); + return true; + } + silver -= copper; + if(silver <= 0) + { + copper = abs64(silver); + m_pp.silver = 0; + } + else + { + m_pp.silver = silver/10; + m_pp.copper += (silver-(m_pp.silver*10)); + if(updateclient) + SendMoneyUpdate(); + Save(); + return true; + } + + gold -=copper; + + if(gold <= 0) + { + copper = abs64(gold); + m_pp.gold = 0; + } + else + { + m_pp.gold = gold/100; + uint64 silvertest = (gold-(static_cast(m_pp.gold)*100))/10; + m_pp.silver += silvertest; + uint64 coppertest = (gold-(static_cast(m_pp.gold)*100+silvertest*10)); + m_pp.copper += coppertest; + if(updateclient) + SendMoneyUpdate(); + Save(); + return true; + } + + platinum -= copper; + + //Impossible for plat to be negative, already checked above + + m_pp.platinum = platinum/1000; + uint64 goldtest = (platinum-(static_cast(m_pp.platinum)*1000))/100; + m_pp.gold += goldtest; + uint64 silvertest = (platinum-(static_cast(m_pp.platinum)*1000+goldtest*100))/10; + m_pp.silver += silvertest; + uint64 coppertest = (platinum-(static_cast(m_pp.platinum)*1000+goldtest*100+silvertest*10)); + m_pp.copper = coppertest; + if(updateclient) + SendMoneyUpdate(); + RecalcWeight(); + Save(); + return true; + } +} + +void Client::AddMoneyToPP(uint64 copper, bool updateclient){ + uint64 tmp; + uint64 tmp2; + tmp = copper; + + // 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; + } + tmp-=tmp2*1000; + + //if (updateclient) + // SendClientMoneyUpdate(3,tmp2); + + // 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; + } + tmp-=tmp2*100; + //if (updateclient) + // SendClientMoneyUpdate(2,tmp2); + + // Add Amount of Silver + tmp2 = tmp/10; + new_val = m_pp.silver + tmp2; + if(new_val < 0) { + m_pp.silver = 0; + } else { + m_pp.silver = m_pp.silver + tmp2; + } + tmp-=tmp2*10; + //if (updateclient) + // SendClientMoneyUpdate(1,tmp2); + + // Add Copper + //tmp = tmp - (tmp2* 10); + //if (updateclient) + // SendClientMoneyUpdate(0,tmp); + tmp2 = tmp; + new_val = m_pp.copper + tmp2; + if(new_val < 0) { + m_pp.copper = 0; + } else { + m_pp.copper = m_pp.copper + tmp2; + } + + + //send them all at once, since the above code stopped working. + if(updateclient) + SendMoneyUpdate(); + + RecalcWeight(); + + Save(); + + LogFile->write(EQEMuLog::Debug, "Client::AddMoneyToPP() %s should have: plat:%i gold:%i silver:%i copper:%i", GetName(), m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); +} + +void Client::AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool updateclient){ + + int32 new_value = m_pp.platinum + platinum; + if(new_value >= 0 && new_value > m_pp.platinum) + m_pp.platinum += platinum; + + new_value = m_pp.gold + gold; + if(new_value >= 0 && new_value > m_pp.gold) + m_pp.gold += gold; + + new_value = m_pp.silver + silver; + if(new_value >= 0 && new_value > m_pp.silver) + m_pp.silver += silver; + + new_value = m_pp.copper + copper; + if(new_value >= 0 && new_value > m_pp.copper) + m_pp.copper += copper; + + if(updateclient) + SendMoneyUpdate(); + + RecalcWeight(); + Save(); + +#if (EQDEBUG>=5) + 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); +#endif +} + +void Client::SendMoneyUpdate() { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_MoneyUpdate,sizeof(MoneyUpdate_Struct)); + MoneyUpdate_Struct* mus= (MoneyUpdate_Struct*)outapp->pBuffer; + + mus->platinum = m_pp.platinum; + mus->gold = m_pp.gold; + mus->silver = m_pp.silver; + mus->copper = m_pp.copper; + + FastQueuePacket(&outapp); +} + +bool Client::HasMoney(uint64 Copper) { + + if((static_cast(m_pp.copper) + + (static_cast(m_pp.silver) * 10) + + (static_cast(m_pp.gold) * 100) + + (static_cast(m_pp.platinum) * 1000)) >= Copper) + return true; + + return false; +} + +uint64 Client::GetCarriedMoney() { + + return ((static_cast(m_pp.copper) + + (static_cast(m_pp.silver) * 10) + + (static_cast(m_pp.gold) * 100) + + (static_cast(m_pp.platinum) * 1000))); +} + +uint64 Client::GetAllMoney() { + + return ( + (static_cast(m_pp.copper) + + (static_cast(m_pp.silver) * 10) + + (static_cast(m_pp.gold) * 100) + + (static_cast(m_pp.platinum) * 1000) + + (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) + + (static_cast(m_pp.copper_cursor) + + (static_cast(m_pp.silver_cursor) * 10) + + (static_cast(m_pp.gold_cursor) * 100) + + (static_cast(m_pp.platinum_cursor) * 1000) + + (static_cast(m_pp.platinum_shared) * 1000))))); +} + +bool Client::CheckIncreaseSkill(SkillType skillid, Mob *against_who, int chancemodi) { + if (IsAIControlled()) // no skillups while chamred =p + return false; + if (skillid > HIGHEST_SKILL) + return false; + int skillval = GetRawSkill(skillid); + int maxskill = GetMaxSkillAfterSpecializationRules(skillid, MaxSkill(skillid)); + + if(against_who) + { + if(against_who->SpecAttacks[IMMUNE_AGGRO] || against_who->IsClient() || + GetLevelCon(against_who->GetLevel()) == CON_GREEN) + { + return false; + } + } + + // Make sure we're not already at skill cap + 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 + Chance = (Chance * RuleI(Character, SkillUpModifier) / 100); + if(MakeRandomFloat(0, 99) < Chance) + { + SetSkill(skillid, GetRawSkill(skillid) + 1); + _log(SKILLS__GAIN, "Skill %d at value %d successfully gain with %.4f%%chance (mod %d)", skillid, skillval, Chance, chancemodi); + return true; + } else { + _log(SKILLS__GAIN, "Skill %d at value %d failed to gain with %.4f%%chance (mod %d)", skillid, skillval, Chance, chancemodi); + } + } else { + _log(SKILLS__GAIN, "Skill %d at value %d cannot increase due to maxmum %d", skillid, skillval, maxskill); + } + return false; +} + +void Client::CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill) { + if (langid >= MAX_PP_LANGUAGE) + return; // do nothing if langid is an invalid language + + 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 + Chance = (Chance * RuleI(Character, SkillUpModifier)/100); + + if(MakeRandomFloat(0,100) < Chance) { // if they make the roll + IncreaseLanguageSkill(langid); // increase the language skill by 1 + _log(SKILLS__GAIN, "Language %d at value %d successfully gain with %.4f%%chance", langid, LangSkill, Chance); + } + else + _log(SKILLS__GAIN, "Language %d at value %d failed to gain with %.4f%%chance", langid, LangSkill, Chance); + } +} + +bool Client::HasSkill(SkillType skill_id) const { + return((GetSkill(skill_id) > 0) && CanHaveSkill(skill_id)); +} + +bool Client::CanHaveSkill(SkillType skill_id) const { + return(database.GetSkillCap(GetClass(), skill_id, RuleI(Character, MaxLevel)) > 0); + //if you don't have it by max level, then odds are you never will? +} + +uint16 Client::MaxSkill(SkillType skillid, uint16 class_, uint16 level) const { + return(database.GetSkillCap(class_, skillid, level)); +} + +uint8 Client::SkillTrainLevel(SkillType skillid, uint16 class_){ + return(database.GetTrainLevel(class_, skillid, RuleI(Character, MaxLevel))); +} + +uint16 Client::GetMaxSkillAfterSpecializationRules(SkillType skillid, uint16 maxSkill) +{ + uint16 Result = maxSkill; + + uint16 PrimarySpecialization = 0, SecondaryForte = 0; + + uint16 PrimarySkillValue = 0, SecondarySkillValue = 0; + + uint16 MaxSpecializations = GetAA(aaSecondaryForte) ? 2 : 1; + + if(skillid >= SPECIALIZE_ABJURE && skillid <= SPECIALIZE_EVOCATION) + { + bool HasPrimarySpecSkill = false; + + int NumberOfPrimarySpecSkills = 0; + + for(int i = SPECIALIZE_ABJURE; i <= SPECIALIZE_EVOCATION; ++i) + { + if(m_pp.skills[i] > 50) + { + HasPrimarySpecSkill = true; + NumberOfPrimarySpecSkills++; + } + if(m_pp.skills[i] > PrimarySkillValue) + { + if(PrimarySkillValue > SecondarySkillValue) + { + SecondarySkillValue = PrimarySkillValue; + SecondaryForte = PrimarySpecialization; + } + + PrimarySpecialization = i; + PrimarySkillValue = m_pp.skills[i]; + } + else if(m_pp.skills[i] > SecondarySkillValue) + { + SecondaryForte = i; + SecondarySkillValue = m_pp.skills[i]; + } + } + + if(SecondarySkillValue <=50) + SecondaryForte = 0; + + if(HasPrimarySpecSkill) + { + if(NumberOfPrimarySpecSkills <= MaxSpecializations) + { + if(MaxSpecializations == 1) + { + if(skillid != PrimarySpecialization) + { + Result = 50; + } + } + else + { + if((skillid != PrimarySpecialization) && ((skillid == SecondaryForte) || (SecondaryForte == 0))) + { + if((PrimarySkillValue > 100) || (!SecondaryForte)) + Result = 100; + } + else if(skillid != PrimarySpecialization) + { + Result = 50; + } + } + } + else + { + Message(13, "Your spell casting specializations skills have been reset. " + "Only %i primary specialization skill is allowed.", MaxSpecializations); + + for(int i = SPECIALIZE_ABJURE; i <= SPECIALIZE_EVOCATION; ++i) + SetSkill((SkillType)i, 1); + + Save(); + + LogFile->write(EQEMuLog::Normal, "Reset %s's caster specialization skills to 1. " + "Too many specializations skills were above 50.", GetCleanName()); + } + + } + } + // This should possibly be handled by bonuses rather than here. + switch(skillid) + { + case TRACKING: + { + Result += ((GetAA(aaAdvancedTracking) * 10) + (GetAA(aaTuneofPursuance) * 10)); + break; + } + + default: + break; + } + + return Result; +} + +void Client::SetPVP(bool toggle) { + m_pp.pvp = toggle ? 1 : 0; + + if(GetPVP()) + this->Message_StringID(MT_Shout,PVP_ON); + else + Message(13, "You no longer follow the ways of discord."); + + SendAppearancePacket(AT_PVP, GetPVP()); + Save(); +} + +void Client::WorldKick() { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMKick, sizeof(GMKick_Struct)); + GMKick_Struct* gmk = (GMKick_Struct *)outapp->pBuffer; + strcpy(gmk->name,GetName()); + QueuePacket(outapp); + safe_delete(outapp); + Kick(); +} + +void Client::GMKill() { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMKill, sizeof(GMKill_Struct)); + GMKill_Struct* gmk = (GMKill_Struct *)outapp->pBuffer; + strcpy(gmk->name,GetName()); + QueuePacket(outapp); + safe_delete(outapp); +} + +bool Client::CheckAccess(int16 iDBLevel, int16 iDefaultLevel) { + if ((admin >= iDBLevel) || (iDBLevel == 255 && admin >= iDefaultLevel)) + return true; + else + return false; +} + +void Client::MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing){ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_MemorizeSpell,sizeof(MemorizeSpell_Struct)); + MemorizeSpell_Struct* mss=(MemorizeSpell_Struct*)outapp->pBuffer; + mss->scribing=scribing; + mss->slot=slot; + mss->spell_id=spellid; + outapp->priority = 5; + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SetFeigned(bool in_feigned) { + if (in_feigned) + { + if(RuleB(Character, FeignKillsPet)) + { + SetPet(0); + } + SetHorseId(0); + entity_list.ClearFeignAggro(this); + forget_timer.Start(FeignMemoryDuration); + } else { + forget_timer.Disable(); + } + feigned=in_feigned; + } + +void Client::LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 price, const Item_Struct* item, bool buying) +{ + if(!player || !merchant || !item) + return; + + string LogText = "Qty: "; + + char Buffer[255]; + memset(Buffer, 0, sizeof(Buffer)); + + snprintf(Buffer, sizeof(Buffer)-1, "%3i", quantity); + LogText += Buffer; + snprintf(Buffer, sizeof(Buffer)-1, "%10i", price); + LogText += " TotalValue: "; + LogText += Buffer; + snprintf(Buffer, sizeof(Buffer)-1, " ItemID: %7i", item->ID); + LogText += Buffer; + LogText += " "; + snprintf(Buffer, sizeof(Buffer)-1, " %s", item->Name); + LogText += Buffer; + + if (buying==true) { + database.logevents(player->AccountName(),player->AccountID(),player->admin,player->GetName(),merchant->GetName(),"Buying from Merchant",LogText.c_str(),2); + } + else { + database.logevents(player->AccountName(),player->AccountID(),player->admin,player->GetName(),merchant->GetName(),"Selling to Merchant",LogText.c_str(),3); + } +} + +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) { + outapp = new EQApplicationPacket(OP_Bind_Wound, sizeof(BindWound_Struct)); + BindWound_Struct* bind_out = (BindWound_Struct*) outapp->pBuffer; + // Start bind + if(!bindwound_timer.Enabled()) { + //make sure we actually have a bandage... and consume it. + int16 bslot = m_inv.HasItemByUse(ItemTypeBandage, 1, invWhereWorn|invWherePersonal); + if(bslot == SLOT_INVALID) { + bind_out->type = 3; + QueuePacket(outapp); + bind_out->type = 7; //this is the wrong message, dont know the right one. + QueuePacket(outapp); + return(true); + } + DeleteItemInInventory(bslot, 1, true); //do we need client update? + + // start complete timer + bindwound_timer.Start(10000); + bindwound_target = bindmob; + + // Send client unlock + bind_out->type = 3; + QueuePacket(outapp); + bind_out->type = 0; + // Client Unlocked + if(!bindmob) { + // send "bindmob dead" to client + bind_out->type = 4; + QueuePacket(outapp); + bind_out->type = 0; + bindwound_timer.Disable(); + bindwound_target = 0; + } + else { + // send bindmob "stand still" + if(!bindmob->IsAIControlled() && bindmob != this ) { + bind_out->type = 2; // ? + //bind_out->type = 3; // ? + bind_out->to = GetID(); // ? + bindmob->CastToClient()->QueuePacket(outapp); + bind_out->type = 0; + bind_out->to = 0; + } + else if (bindmob->IsAIControlled() && bindmob != this ){ + ; // Tell IPC to stand still? + } + else { + ; // Binding self + } + } + } else { + // finish bind + // disable complete timer + bindwound_timer.Disable(); + bindwound_target = 0; + if(!bindmob){ + // send "bindmob gone" to client + bind_out->type = 5; // not in zone + QueuePacket(outapp); + bind_out->type = 0; + } + + else { + if (!GetFeigned() && (bindmob->DistNoRoot(*this) <= 400)) { + // send bindmob bind done + if(!bindmob->IsAIControlled() && bindmob != this ) { + + } + else if(bindmob->IsAIControlled() && bindmob != this ) { + // Tell IPC to resume?? + } + else { + // Binding self + } + // Send client bind done + + //this is taken care of on start of bind, not finish now, and is improved + //DeleteItemInInventory(m_inv.HasItem(13009, 1), 1, true); + + bind_out->type = 1; // Done + QueuePacket(outapp); + bind_out->type = 0; + CheckIncreaseSkill(BIND_WOUND, NULL, 5); + + int maxHPBonus = spellbonuses.MaxBindWound + itembonuses.MaxBindWound + aabonuses.MaxBindWound; + + int max_percent = 50 + 10 * maxHPBonus; + + if(GetClass() == MONK && GetSkill(BIND_WOUND) > 200) { + max_percent = 70 + 10 * maxHPBonus; + } + + int max_hp = bindmob->GetMaxHP()*max_percent/100; + + // send bindmob new hp's + if (bindmob->GetHP() < bindmob->GetMaxHP() && bindmob->GetHP() <= (max_hp)-1){ + // 0.120 per skill point, 0.60 per skill level, minimum 3 max 30 + int bindhps = 3; + + + if (GetSkill(BIND_WOUND) > 200) { + bindhps += GetSkill(BIND_WOUND)*4/10; + } else if (GetSkill(BIND_WOUND) >= 10) { + bindhps += GetSkill(BIND_WOUND)/4; + } + + //Implementation of aaMithanielsBinding is a guess (the multiplier) + int bindBonus = spellbonuses.BindWound + itembonuses.BindWound + aabonuses.BindWound; + + bindhps += bindhps*bindBonus / 100; + + //if the bind takes them above the max bindable + //cap it at that value. Dont know if live does it this way + //but it makes sense to me. + int chp = bindmob->GetHP() + bindhps; + if(chp > max_hp) + chp = max_hp; + + bindmob->SetHP(chp); + bindmob->SendHPUpdate(); + } + else { + //I dont have the real, live + Message(15, "You cannot bind wounds above %d%% hitpoints.", max_percent); + if(bindmob->IsClient()) + bindmob->CastToClient()->Message(15, "You cannot have your wounds bound above %d%% hitpoints.", max_percent); + // Too many hp message goes here. + } + } + else { + // Send client bind failed + if(bindmob != this) + bind_out->type = 6; // They moved + else + bind_out->type = 7; // Bandager moved + + QueuePacket(outapp); + bind_out->type = 0; + } + } + } + } + else if (bindwound_timer.Enabled()) { + // You moved + outapp = new EQApplicationPacket(OP_Bind_Wound, sizeof(BindWound_Struct)); + BindWound_Struct* bind_out = (BindWound_Struct*) outapp->pBuffer; + bindwound_timer.Disable(); + bindwound_target = 0; + bind_out->type = 7; + QueuePacket(outapp); + bind_out->type = 3; + QueuePacket(outapp); + } + safe_delete(outapp); + return true; +} + +void Client::SetMaterial(int16 in_slot, uint32 item_id){ + const Item_Struct* item = database.GetItem(item_id); + if (item && (item->ItemClass==ItemClassCommon)) { + if (in_slot==SLOT_HEAD) + m_pp.item_material[MATERIAL_HEAD] = item->Material; + else if (in_slot==SLOT_CHEST) + m_pp.item_material[MATERIAL_CHEST] = item->Material; + else if (in_slot==SLOT_ARMS) + m_pp.item_material[MATERIAL_ARMS] = item->Material; + else if (in_slot==SLOT_BRACER01) + m_pp.item_material[MATERIAL_BRACER] = item->Material; + else if (in_slot==SLOT_BRACER02) + m_pp.item_material[MATERIAL_BRACER] = item->Material; + else if (in_slot==SLOT_HANDS) + m_pp.item_material[MATERIAL_HANDS] = item->Material; + else if (in_slot==SLOT_LEGS) + m_pp.item_material[MATERIAL_LEGS] = item->Material; + else if (in_slot==SLOT_FEET) + m_pp.item_material[MATERIAL_FEET] = item->Material; + else if (in_slot==SLOT_PRIMARY) + m_pp.item_material[MATERIAL_PRIMARY] = atoi(item->IDFile+2); + else if (in_slot==SLOT_SECONDARY) + m_pp.item_material[MATERIAL_SECONDARY] = atoi(item->IDFile+2); + } +} + +void Client::ServerFilter(SetServerFilter_Struct* filter){ + +/* this code helps figure out the filter IDs in the packet if needed + static SetServerFilter_Struct ssss; + int r; + uint32 *o = (uint32 *) &ssss; + uint32 *n = (uint32 *) filter; + for(r = 0; r < (sizeof(SetServerFilter_Struct)/4); r++) { + if(*o != *n) + LogFile->write(EQEMuLog::Debug, "Filter %d changed from %d to %d", r, *o, *n); + o++; n++; + } + memcpy(&ssss, filter, sizeof(SetServerFilter_Struct)); +*/ +#define Filter0(type) \ + if(filter->filters[type] == 1) \ + ClientFilters[type] = FilterShow; \ + else \ + ClientFilters[type] = FilterHide; +#define Filter1(type) \ + if(filter->filters[type] == 0) \ + ClientFilters[type] = FilterShow; \ + else \ + ClientFilters[type] = FilterHide; + + Filter0(FilterGuildChat); + Filter0(FilterSocials); + Filter0(FilterGroupChat); + Filter0(FilterShouts); + Filter0(FilterAuctions); + Filter0(FilterOOC); + Filter0(FilterBadWords); + + if(filter->filters[FilterPCSpells] == 0) + ClientFilters[FilterPCSpells] = FilterShow; + else if(filter->filters[FilterPCSpells] == 1) + ClientFilters[FilterPCSpells] = FilterHide; + else + ClientFilters[FilterPCSpells] = FilterShowGroupOnly; + + Filter1(FilterNPCSpells); + + if(filter->filters[FilterBardSongs] == 0) + ClientFilters[FilterBardSongs] = FilterShow; + else if(filter->filters[FilterBardSongs] == 1) + ClientFilters[FilterBardSongs] = FilterShowSelfOnly; + else if(filter->filters[FilterBardSongs] == 2) + ClientFilters[FilterBardSongs] = FilterShowGroupOnly; + else + ClientFilters[FilterBardSongs] = FilterHide; + + if(filter->filters[FilterSpellCrits] == 0) + ClientFilters[FilterSpellCrits] = FilterShow; + else if(filter->filters[FilterSpellCrits] == 1) + ClientFilters[FilterSpellCrits] = FilterShowSelfOnly; + else + ClientFilters[FilterSpellCrits] = FilterHide; + + Filter1(FilterMeleeCrits); + + if(filter->filters[FilterSpellDamage] == 0) + ClientFilters[FilterSpellDamage] = FilterShow; + else if(filter->filters[FilterSpellDamage] == 1) + ClientFilters[FilterSpellDamage] = FilterShowSelfOnly; + else + ClientFilters[FilterSpellDamage] = FilterHide; + + Filter0(FilterMyMisses); + Filter0(FilterOthersMiss); + Filter0(FilterOthersHit); + Filter0(FilterMissedMe); + Filter1(FilterDamageShields); + Filter1(FilterDOT); + Filter1(FilterPetHits); + Filter1(FilterPetMisses); + Filter1(FilterFocusEffects); + Filter1(FilterPetSpells); + Filter1(FilterHealOverTime); +} + +// this version is for messages with no parameters +void Client::Message_StringID(uint32 type, uint32 string_id, uint32 distance) +{ + if (GetFilter(FilterSpellDamage) == FilterHide && type == MT_NonMelee) + return; + if (GetFilter(FilterMeleeCrits) == FilterHide && type == MT_CritMelee) //98 is self... + return; + if (GetFilter(FilterSpellCrits) == FilterHide && type == MT_SpellCrits) + return; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SimpleMessage,12); + SimpleMessage_Struct* sms = (SimpleMessage_Struct*)outapp->pBuffer; + sms->color=type; + sms->string_id=string_id; + + sms->unknown8=0; + + if(distance>0) + entity_list.QueueCloseClients(this,outapp,false,distance); + else + QueuePacket(outapp); + safe_delete(outapp); +} + +// +// this list of 9 args isn't how I want to do it, but to use va_arg +// you have to know how many args you're expecting, and to do that we have +// to load the eqstr file and count them in the string. +// This hack sucks but it's gonna work for now. +// +void Client::Message_StringID(uint32 type, uint32 string_id, const char* message1, + const char* message2,const char* message3,const char* message4, + const char* message5,const char* message6,const char* message7, + const char* message8,const char* message9, uint32 distance) +{ + if (GetFilter(FilterSpellDamage) == FilterHide && type == MT_NonMelee) + return; + if (GetFilter(FilterMeleeCrits) == FilterHide && type == MT_CritMelee) //98 is self... + return; + if (GetFilter(FilterSpellCrits) == FilterHide && type == MT_SpellCrits) + return; + if (GetFilter(FilterDamageShields) == FilterHide && type == MT_DS) + return; + + int i, argcount, length; + char *bufptr; + const char *message_arg[9] = {0}; + + if(type==MT_Emote) + type=4; + + if(!message1) + { + Message_StringID(type, string_id); // use the simple message instead + return; + } + + i = 0; + message_arg[i++] = message1; + message_arg[i++] = message2; + message_arg[i++] = message3; + message_arg[i++] = message4; + message_arg[i++] = message5; + message_arg[i++] = message6; + message_arg[i++] = message7; + message_arg[i++] = message8; + message_arg[i++] = message9; + + for(argcount = length = 0; message_arg[argcount]; argcount++) + length += strlen(message_arg[argcount]) + 1; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_FormattedMessage, length+13); + FormattedMessage_Struct *fm = (FormattedMessage_Struct *)outapp->pBuffer; + fm->string_id = string_id; + fm->type = type; + bufptr = fm->message; + for(i = 0; i < argcount; i++) + { + strcpy(bufptr, message_arg[i]); + bufptr += strlen(message_arg[i]) + 1; + } + + + if(distance>0) + entity_list.QueueCloseClients(this,outapp,false,distance); + else + QueuePacket(outapp); + safe_delete(outapp); +} + + +void Client::SetTint(int16 in_slot, uint32 color) { + Color_Struct new_color; + new_color.color = color; + SetTint(in_slot, new_color); +} + +// Still need to reconcile bracer01 versus bracer02 +void Client::SetTint(int16 in_slot, Color_Struct& color) { + if (in_slot==SLOT_HEAD) + m_pp.item_tint[MATERIAL_HEAD].color=color.color; + else if (in_slot==SLOT_ARMS) + m_pp.item_tint[MATERIAL_ARMS].color=color.color; + else if (in_slot==SLOT_BRACER01) + m_pp.item_tint[MATERIAL_BRACER].color=color.color; + else if (in_slot==SLOT_BRACER02) + m_pp.item_tint[MATERIAL_BRACER].color=color.color; + else if (in_slot==SLOT_HANDS) + m_pp.item_tint[MATERIAL_HANDS].color=color.color; + else if (in_slot==SLOT_PRIMARY) + m_pp.item_tint[MATERIAL_PRIMARY].color=color.color; + else if (in_slot==SLOT_SECONDARY) + m_pp.item_tint[MATERIAL_SECONDARY].color=color.color; + else if (in_slot==SLOT_CHEST) + m_pp.item_tint[MATERIAL_CHEST].color=color.color; + else if (in_slot==SLOT_LEGS) + m_pp.item_tint[MATERIAL_LEGS].color=color.color; + else if (in_slot==SLOT_FEET) + m_pp.item_tint[MATERIAL_FEET].color=color.color; +} + +void Client::SetHideMe(bool flag) +{ + EQApplicationPacket app; + + gmhideme = flag; + + if(gmhideme) + { + database.SetHideMe(AccountID(),true); + CreateDespawnPacket(&app, false); + entity_list.RemoveFromTargets(this); + trackable = false; + } + else + { + database.SetHideMe(AccountID(),false); + CreateSpawnPacket(&app); + trackable = true; + } + + entity_list.QueueClientsStatus(this, &app, true, 0, Admin()-1); +} + +void Client::SetLanguageSkill(int langid, int value) +{ + if (langid >= MAX_PP_LANGUAGE) + return; //Invalid Language + + if (value > 100) + value = 100; //Max lang value + + m_pp.languages[langid] = value; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct)); + SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer; + skill->skillId = 100 + langid; + skill->value = m_pp.languages[langid]; + QueuePacket(outapp); + safe_delete(outapp); + + Message_StringID( MT_Skills, LANG_SKILL_IMPROVED ); //Notify the client +} + +void Client::LinkDead() +{ + if (GetGroup()) + { + entity_list.MessageGroup(this,true,15,"%s has gone linkdead.",GetName()); + GetGroup()->DelMember(this); + } + Raid *raid = entity_list.GetRaidByClient(this); + if(raid){ + raid->MemberZoned(this); + } +// save_timer.Start(2500); + linkdead_timer.Start(RuleI(Zone,ClientLinkdeadMS)); + SendAppearancePacket(AT_Linkdead, 1); + client_state = CLIENT_LINKDEAD; + AI_Start(CLIENT_LD_TIMEOUT); +} + +uint8 Client::SlotConvert(uint8 slot,bool bracer){ + uint8 slot2=0; + if(bracer) + return SLOT_BRACER02; + switch(slot){ + case MATERIAL_HEAD: + slot2=SLOT_HEAD; + break; + case MATERIAL_CHEST: + slot2=SLOT_CHEST; + break; + case MATERIAL_ARMS: + slot2=SLOT_ARMS; + break; + case MATERIAL_BRACER: + slot2=SLOT_BRACER01; + break; + case MATERIAL_HANDS: + slot2=SLOT_HANDS; + break; + case MATERIAL_LEGS: + slot2=SLOT_LEGS; + break; + case MATERIAL_FEET: + slot2=SLOT_FEET; + break; + } + return slot2; +} + +uint8 Client::SlotConvert2(uint8 slot){ + uint8 slot2=0; + switch(slot){ + case SLOT_HEAD: + slot2=MATERIAL_HEAD; + break; + case SLOT_CHEST: + slot2=MATERIAL_CHEST; + break; + case SLOT_ARMS: + slot2=MATERIAL_ARMS; + break; + case SLOT_BRACER01: + slot2=MATERIAL_BRACER; + break; + case SLOT_HANDS: + slot2=MATERIAL_HANDS; + break; + case SLOT_LEGS: + slot2=MATERIAL_LEGS; + break; + case SLOT_FEET: + slot2=MATERIAL_FEET; + break; + } + return slot2; +} + +void Client::Escape() +{ + hidden = true; + entity_list.ClearFeignAggro(this); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage,12); + SimpleMessage_Struct *msg=(SimpleMessage_Struct *)outapp->pBuffer; + msg->color=0x010E; + msg->string_id=114; + FastQueuePacket(&outapp); + + 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); + safe_delete(outapp); +} + +float Client::CalcPriceMod(Mob* other, bool reverse) +{ + float chaformula = 0; + if (other) + { + int factionlvl = GetFactionLevel(CharacterID(), other->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), other->CastToNPC()->GetPrimaryFaction(), other); + if (factionlvl >= FACTION_APPREHENSIVE) // Apprehensive or worse. + { + if (GetCHA() > 103) + { + chaformula = (GetCHA() - 103)*((-(RuleR(Merchant, ChaBonusMod))/100)*(RuleI(Merchant, PriceBonusPct))); // This will max out price bonus. + if (chaformula < -1*(RuleI(Merchant, PriceBonusPct))) + chaformula = -1*(RuleI(Merchant, PriceBonusPct)); + } + else if (GetCHA() < 103) + { + chaformula = (103 - GetCHA())*(((RuleR(Merchant, ChaPenaltyMod))/100)*(RuleI(Merchant, PricePenaltyPct))); // This will bottom out price penalty. + if (chaformula > 1*(RuleI(Merchant, PricePenaltyPct))) + chaformula = 1*(RuleI(Merchant, PricePenaltyPct)); + } + } + if (factionlvl <= FACTION_INDIFFERENT) // Indifferent or better. + { + if (GetCHA() > 75) + { + chaformula = (GetCHA() - 75)*((-(RuleR(Merchant, ChaBonusMod))/100)*(RuleI(Merchant, PriceBonusPct))); // This will max out price bonus. + if (chaformula < -1*(RuleI(Merchant, PriceBonusPct))) + chaformula = -1*(RuleI(Merchant, PriceBonusPct)); + } + else if (GetCHA() < 75) + { + chaformula = (75 - GetCHA())*(((RuleR(Merchant, ChaPenaltyMod))/100)*(RuleI(Merchant, PricePenaltyPct))); // Faction modifier keeps up from reaching bottom price penalty. + if (chaformula > 1*(RuleI(Merchant, PricePenaltyPct))) + chaformula = 1*(RuleI(Merchant, PricePenaltyPct)); + } + } + } + + if (reverse) + chaformula *= -1; //For selling + //Now we have, for example, 10 + chaformula /= 100; //Convert to 0.10 + chaformula += 1; //Convert to 1.10; + return chaformula; //Returns 1.10, expensive stuff! +} + +//neat idea from winter's roar, not implemented +void Client::Insight(uint32 t_id) +{ + Mob* who = entity_list.GetMob(t_id); + if (!who) + return; + if (!who->IsNPC()) + { + Message(0,"This ability can only be used on NPCs."); + return; + } + if (Dist(*who) > 200) + { + Message(0,"You must get closer to your target!"); + return; + } + if (!CheckLosFN(who)) + { + Message(0,"You must be able to see your target!"); + return; + } + char hitpoints[64]; + char resists[320]; + char dmg[64]; + memset(hitpoints,0,sizeof(hitpoints)); + memset(resists,0,sizeof(resists)); + memset(dmg,0,sizeof(dmg)); + //Start with HP blah + int avg_hp = GetLevelHP(who->GetLevel()); + int cur_hp = who->GetHP(); + if (cur_hp == avg_hp) + { + strn0cpy(hitpoints,"averagely tough",32); + } + else if (cur_hp >= avg_hp*5) + { + strn0cpy(hitpoints,"extremely tough",32); + } + else if (cur_hp >= avg_hp*4) + { + strn0cpy(hitpoints,"exceptionally tough",32); + } + else if (cur_hp >= avg_hp*3) + { + strn0cpy(hitpoints,"very tough",32); + } + else if (cur_hp >= avg_hp*2) + { + strn0cpy(hitpoints,"quite tough",32); + } + else if (cur_hp >= avg_hp*1.25) + { + strn0cpy(hitpoints,"rather tough",32); + } + else if (cur_hp > avg_hp) + { + strn0cpy(hitpoints,"slightly tough",32); + } + else if (cur_hp <= avg_hp*0.20) + { + strn0cpy(hitpoints,"extremely frail",32); + } + else if (cur_hp <= avg_hp*0.25) + { + strn0cpy(hitpoints,"exceptionally frail",32); + } + else if (cur_hp <= avg_hp*0.33) + { + strn0cpy(hitpoints,"very frail",32); + } + else if (cur_hp <= avg_hp*0.50) + { + strn0cpy(hitpoints,"quite frail",32); + } + else if (cur_hp <= avg_hp*0.75) + { + strn0cpy(hitpoints,"rather frail",32); + } + else if (cur_hp < avg_hp) + { + strn0cpy(hitpoints,"slightly frail",32); + } + + int avg_dmg = who->CastToNPC()->GetMaxDamage(who->GetLevel()); + int cur_dmg = who->CastToNPC()->GetMaxDMG(); + if (cur_dmg == avg_dmg) + { + strn0cpy(dmg,"averagely strong",32); + } + else if (cur_dmg >= avg_dmg*4) + { + strn0cpy(dmg,"extremely strong",32); + } + else if (cur_dmg >= avg_dmg*3) + { + strn0cpy(dmg,"exceptionally strong",32); + } + else if (cur_dmg >= avg_dmg*2) + { + strn0cpy(dmg,"very strong",32); + } + else if (cur_dmg >= avg_dmg*1.25) + { + strn0cpy(dmg,"quite strong",32); + } + else if (cur_dmg >= avg_dmg*1.10) + { + strn0cpy(dmg,"rather strong",32); + } + else if (cur_dmg > avg_dmg) + { + strn0cpy(dmg,"slightly strong",32); + } + else if (cur_dmg <= avg_dmg*0.20) + { + strn0cpy(dmg,"extremely weak",32); + } + else if (cur_dmg <= avg_dmg*0.25) + { + strn0cpy(dmg,"exceptionally weak",32); + } + else if (cur_dmg <= avg_dmg*0.33) + { + strn0cpy(dmg,"very weak",32); + } + else if (cur_dmg <= avg_dmg*0.50) + { + strn0cpy(dmg,"quite weak",32); + } + else if (cur_dmg <= avg_dmg*0.75) + { + strn0cpy(dmg,"rather weak",32); + } + else if (cur_dmg < avg_dmg) + { + strn0cpy(dmg,"slightly weak",32); + } + + //Resists + int res; + int i = 1; + + //MR + res = who->GetResist(i); + i++; + if (res >= 1000) + { + strcat(resists,"immune"); + } + else if (res >= 500) + { + strcat(resists,"practically immune"); + } + else if (res >= 250) + { + strcat(resists,"exceptionally resistant"); + } + else if (res >= 150) + { + strcat(resists,"very resistant"); + } + else if (res >= 100) + { + strcat(resists,"fairly resistant"); + } + else if (res >= 50) + { + strcat(resists,"averagely resistant"); + } + else if (res >= 25) + { + strcat(resists,"weakly resistant"); + } + else + { + strcat(resists,"barely resistant"); + } + strcat(resists," to magic, "); + + //FR + res = who->GetResist(i); + i++; + if (res >= 1000) + { + strcat(resists,"immune"); + } + else if (res >= 500) + { + strcat(resists,"practically immune"); + } + else if (res >= 250) + { + strcat(resists,"exceptionally resistant"); + } + else if (res >= 150) + { + strcat(resists,"very resistant"); + } + else if (res >= 100) + { + strcat(resists,"fairly resistant"); + } + else if (res >= 50) + { + strcat(resists,"averagely resistant"); + } + else if (res >= 25) + { + strcat(resists,"weakly resistant"); + } + else + { + strcat(resists,"barely resistant"); + } + strcat(resists," to fire, "); + + //CR + res = who->GetResist(i); + i++; + if (res >= 1000) + { + strcat(resists,"immune"); + } + else if (res >= 500) + { + strcat(resists,"practically immune"); + } + else if (res >= 250) + { + strcat(resists,"exceptionally resistant"); + } + else if (res >= 150) + { + strcat(resists,"very resistant"); + } + else if (res >= 100) + { + strcat(resists,"fairly resistant"); + } + else if (res >= 50) + { + strcat(resists,"averagely resistant"); + } + else if (res >= 25) + { + strcat(resists,"weakly resistant"); + } + else + { + strcat(resists,"barely resistant"); + } + strcat(resists," to cold, "); + + //PR + res = who->GetResist(i); + i++; + if (res >= 1000) + { + strcat(resists,"immune"); + } + else if (res >= 500) + { + strcat(resists,"practically immune"); + } + else if (res >= 250) + { + strcat(resists,"exceptionally resistant"); + } + else if (res >= 150) + { + strcat(resists,"very resistant"); + } + else if (res >= 100) + { + strcat(resists,"fairly resistant"); + } + else if (res >= 50) + { + strcat(resists,"averagely resistant"); + } + else if (res >= 25) + { + strcat(resists,"weakly resistant"); + } + else + { + strcat(resists,"barely resistant"); + } + strcat(resists," to poison, and "); + + //MR + res = who->GetResist(i); + i++; + if (res >= 1000) + { + strcat(resists,"immune"); + } + else if (res >= 500) + { + strcat(resists,"practically immune"); + } + else if (res >= 250) + { + strcat(resists,"exceptionally resistant"); + } + else if (res >= 150) + { + strcat(resists,"very resistant"); + } + else if (res >= 100) + { + strcat(resists,"fairly resistant"); + } + else if (res >= 50) + { + strcat(resists,"averagely resistant"); + } + else if (res >= 25) + { + strcat(resists,"weakly resistant"); + } + else + { + strcat(resists,"barely resistant"); + } + strcat(resists," to disease."); + + Message(0,"Your target is a level %i %s. It appears %s and %s for its level. It seems %s",who->GetLevel(),GetEQClassName(who->GetClass(),1),dmg,hitpoints,resists); +} + +void Client::ChangeSQLLog(const char *file) { + if(SQL_log != NULL) { + fclose(SQL_log); + SQL_log = NULL; + } + if(file != NULL) { + if(strstr(file, "..") != NULL) { + Message(13, ".. is forbibben in SQL log file names."); + return; + } + char buf[512]; + snprintf(buf, 511, "%s%s", SQL_LOG_PATH, file); + buf[511] = '\0'; + SQL_log = fopen(buf, "a"); + if(SQL_log == NULL) { + Message(13, "Unable to open SQL log file: %s\n", strerror(errno)); + } + } +} + +void Client::LogSQL(const char *fmt, ...) { + if(SQL_log == NULL) + return; + + va_list argptr; + va_start(argptr, fmt); + vfprintf(SQL_log, fmt, argptr ); + fputc('\n', SQL_log); + va_end(argptr); +} + +void Client::GetGroupAAs(GroupLeadershipAA_Struct *into) const { + memcpy(into, &m_pp.leader_abilities, sizeof(GroupLeadershipAA_Struct)); +} + +void Client::EnteringMessages(Client* client) +{ + //server rules + char *rules; + rules = new char [4096]; + + if(database.GetVariable("Rules", rules, 4096)) + { + uint8 flag = database.GetAgreementFlag(client->AccountID()); + if(!flag) + { + client->Message(13,"You must agree to the Rules, before you can move. (type #serverrules to view the rules)"); + client->Message(13,"You must agree to the Rules, before you can move. (type #serverrules to view the rules)"); + client->Message(13,"You must agree to the Rules, before you can move. (type #serverrules to view the rules)"); + client->SendAppearancePacket(AT_Anim, ANIM_FREEZE); + } + } + safe_delete_array(rules); +} + +void Client::SendRules(Client* client) +{ + char *rules; + rules = new char [4096]; + char *ptr; + + database.GetVariable("Rules", rules, 4096); + + ptr = strtok(rules, "\n"); + while(ptr != NULL) + { + + client->Message(0,"%s",ptr); + ptr = strtok(NULL, "\n"); + } + safe_delete_array(rules); +} + +void Client::SetEndurance(int32 newEnd) +{ + /*Endurance can't be less than 0 or greater than max*/ + if(newEnd < 0) + newEnd = 0; + else if(newEnd > GetMaxEndurance()){ + newEnd = GetMaxEndurance(); + } + + cur_end = newEnd; + SendManaUpdatePacket(); +} + +void Client::SacrificeConfirm(Client *caster) { + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Sacrifice, sizeof(Sacrifice_Struct)); + Sacrifice_Struct *ss = (Sacrifice_Struct*)outapp->pBuffer; + + if(!caster || PendingSacrifice) return; + + if(GetLevel() < RuleI(Spells, SacrificeMinLevel)){ + caster->Message_StringID(13, SAC_TOO_LOW); //This being is not a worthy sacrifice. + return; + } + if (GetLevel() > RuleI(Spells, SacrificeMaxLevel)) { + caster->Message_StringID(13, SAC_TOO_HIGH); + return; + } + + ss->CasterID = caster->GetID(); + ss->TargetID = GetID(); + ss->Confirm = 0; + QueuePacket(outapp); + safe_delete(outapp); + // We store the Caster's name, because when the packet comes back, it only has the victim's entityID in it, + // not the caster. + SacrificeCaster += caster->GetName(); + PendingSacrifice = true; +} + +//Essentially a special case death function +void Client::Sacrifice(Client *caster) +{ + if(GetLevel() >= RuleI(Spells, SacrificeMinLevel) && GetLevel() <= RuleI(Spells, SacrificeMaxLevel)){ + int exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000); + if(exploss < GetEXP()){ + SetEXP(GetEXP()-exploss, GetAAXP()); + SendLogoutPackets(); + + //make our become corpse packet, and queue to ourself before OP_Death. + EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct)); + BecomeCorpse_Struct* bc = (BecomeCorpse_Struct*)app2.pBuffer; + bc->spawn_id = GetID(); + bc->x = GetX(); + bc->y = GetY(); + bc->z = GetZ(); + QueuePacket(&app2); + + // make death packet + EQApplicationPacket app(OP_Death, sizeof(Death_Struct)); + Death_Struct* d = (Death_Struct*)app.pBuffer; + d->spawn_id = GetID(); + d->killer_id = caster ? caster->GetID() : 0; + d->bindzoneid = GetPP().binds[0].zoneId; + d->spell_id = SPELL_UNKNOWN; + d->attack_skill = 0xe7; + d->damage = 0; + app.priority = 6; + entity_list.QueueClients(this, &app); + + BuffFadeAll(); + UnmemSpellAll(); + Group *g = GetGroup(); + if(g){ + g->MemberZoned(this); + } + Raid *r = entity_list.GetRaidByClient(this); + if(r){ + r->MemberZoned(this); + } + ClearAllProximities(); + if(RuleB(Character, LeaveCorpses)){ + Corpse *new_corpse = new Corpse(this, 0); + entity_list.AddCorpse(new_corpse, GetID()); + SetID(0); + entity_list.QueueClients(this, &app2, true); + } + Save(); + GoToDeath(); + caster->SummonItem(RuleI(Spells, SacrificeItemID)); + } + } + else{ + caster->Message_StringID(13, SAC_TOO_LOW); //This being is not a worthy sacrifice. + } +} + +void Client::SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID) { + + if(!Caster || PendingTranslocate) return; + + const SPDat_Spell_Struct &Spell = spells[SpellID]; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Translocate, sizeof(Translocate_Struct)); + Translocate_Struct *ts = (Translocate_Struct*)outapp->pBuffer; + + strcpy(ts->Caster, Caster->GetName()); + 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; + } + else { + ts->ZoneID = database.GetZoneID(Spell.teleport_zone); + ts->y = Spell.base[0]; + ts->x = Spell.base[1]; + ts->z = Spell.base[2]; + } + + ts->unknown008 = 0; + ts->Complete = 0; + + PendingTranslocateData = *ts; + PendingTranslocate=true; + TranslocateTime = time(NULL); + + QueuePacket(outapp); + safe_delete(outapp); + + return; +} +void Client::SendPickPocketResponse(Mob *from, uint32 amt, int type, const Item_Struct* item){ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); + sPickPocket_Struct* pick_out = (sPickPocket_Struct*) outapp->pBuffer; + pick_out->coin = amt; + pick_out->from = GetID(); + pick_out->to = from->GetID(); + pick_out->myskill = GetSkill(PICK_POCKETS); + + if((type >= PickPocketPlatinum) && (type <= PickPocketCopper) && (amt == 0)) + type = PickPocketFailed; + + pick_out->type = type; + if(item) + strcpy(pick_out->itemname, item->Name); + else + pick_out->itemname[0] = '\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::SetHoTT(uint32 mobid) { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_TargetHoTT, sizeof(ClientTarget_Struct)); + ClientTarget_Struct *ct = (ClientTarget_Struct *) outapp->pBuffer; + ct->new_target = mobid; + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendPopupToClient(const char *Title, const char *Text, uint32 PopupID, uint32 Buttons, uint32 Duration) { + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_OnLevelMessage, sizeof(OnLevelMessage_Struct)); + OnLevelMessage_Struct *olms = (OnLevelMessage_Struct *) outapp->pBuffer; + + if((strlen(Title) > (sizeof(olms->Title)-1)) || + (strlen(Text) > (sizeof(olms->Text)-1))) return; + + strcpy(olms->Title, Title); + strcpy(olms->Text, Text); + + olms->Buttons = Buttons; + + if(Duration > 0) + olms->Duration = Duration * 1000; + else + olms->Duration = 0xffffffff; + + olms->PopupID = PopupID; + olms->NegativeID = 0; + + sprintf(olms->ButtonName0, "%s", "Yes"); + sprintf(olms->ButtonName1, "%s", "No"); + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::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, ...) { + va_list argptr; + char buffer[4096]; + + va_start(argptr, Text); + vsnprintf(buffer, sizeof(buffer), Text, argptr); + va_end(argptr); + + size_t len = strlen(buffer); + + EQApplicationPacket* app = new EQApplicationPacket(OP_OnLevelMessage, sizeof(OnLevelMessage_Struct)); + OnLevelMessage_Struct* olms=(OnLevelMessage_Struct*)app->pBuffer; + + if(strlen(Text) > (sizeof(olms->Text)-1)) + return; + + if(!target) + title_type = 0; + + switch (title_type) + { + case 1: { + char name[64] = ""; + strcpy(name, target->GetName()); + if(target->GetLastName()) { + char last_name[64] = ""; + strcpy(last_name, target->GetLastName()); + strcat(name, " "); + strcat(name, last_name); + } + strcpy(olms->Title, name); + break; + } + case 2: { + if(target->GuildID()) { + char *guild_name = (char*)guild_mgr.GetGuildName(target->GuildID()); + strcpy(olms->Title, guild_name); + } + else { + strcpy(olms->Title, "No Guild"); + } + break; + } + default: { + strcpy(olms->Title, Title); + break; + } + } + + memcpy(olms->Text, buffer, len+1); + + olms->Buttons = Buttons; + + sprintf(olms->ButtonName0, "%s", ButtonName0); + sprintf(olms->ButtonName1, "%s", ButtonName1); + + if(Duration > 0) + olms->Duration = Duration * 1000; + else + olms->Duration = 0xffffffff; + + olms->PopupID = PopupID; + olms->NegativeID = NegativeID; + + FastQueuePacket(&app); +} + +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 { + cerr << "Error in Client::KeyRingLoad query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return; + } +} + +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 + { + cerr << "Error in Doors::HandleClick query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return; + } + keyring.push_back(item_id); + } +} + +bool Client::KeyRingCheck(uint32 item_id) +{ + for(std::list::iterator iter = keyring.begin(); + iter != keyring.end(); + ++iter) + { + if(*iter == item_id) + return true; + } + return false; +} + +void Client::KeyRingList() +{ + Message(4,"Keys on Keyring:"); + const Item_Struct *item = 0; + for(std::list::iterator iter = keyring.begin(); + iter != keyring.end(); + ++iter) + { + if ((item = database.GetItem(*iter))!=NULL) { + Message(4,item->Name); + } + } +} + +bool Client::IsDiscovered(uint32 itemid) { + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + 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 + { + cerr << "Error in IsDiscovered query '" << query << "' " << errbuf << endl; + } + mysql_free_result(result); + safe_delete_array(query); + return false; +} + +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); + + parse->EventPlayer(EVENT_DISCOVER_ITEM, this, "", itemid); +} + +void Client::UpdateLFP() { + + Group *g = GetGroup(); + + if(g && !g->IsLeader(this)) { + database.SetLFP(CharacterID(), false); + worldserver.StopLFP(CharacterID()); + LFP = false; + return; + } + + GroupLFPMemberEntry LFPMembers[MAX_GROUP_MEMBERS]; + + for(unsigned int i=0; iGetZoneID(); + + if(g) { + // 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((g->membername[i][0] != '\0') && strcasecmp(g->membername[i], LFPMembers[0].Name)) + strcpy(LFPMembers[NextFreeSlot++].Name, g->membername[i]); + } + } + worldserver.UpdateLFP(CharacterID(), LFPMembers); +} + +uint16 Client::GetPrimarySkillValue() +{ + SkillType skill = HIGHEST_SKILL; //because NULL == 0, which is 1H Slashing, & we want it to return 0 from GetSkill + bool equiped = m_inv.GetItem(13); + + if (!equiped) + skill = HAND_TO_HAND; + + else { + + uint8 type = m_inv.GetItem(13)->GetItem()->ItemType; //is this the best way to do this? + + switch (type) + { + case ItemType1HS: // 1H Slashing + { + skill = _1H_SLASHING; + break; + } + case ItemType2HS: // 2H Slashing + { + skill = _2H_SLASHING; + break; + } + case ItemTypePierce: // Piercing + { + skill = PIERCING; + break; + } + case ItemType1HB: // 1H Blunt + { + skill = _1H_BLUNT; + break; + } + case ItemType2HB: // 2H Blunt + { + skill = _2H_BLUNT; + break; + } + case ItemType2HPierce: // 2H Piercing + { + skill = PIERCING; + break; + } + case ItemTypeHand2Hand: // Hand to Hand + { + skill = HAND_TO_HAND; + break; + } + default: // All other types default to Hand to Hand + { + skill = HAND_TO_HAND; + break; + } + } + } + + return GetSkill(skill); +} + +uint16 Client::GetTotalATK() +{ + uint16 AttackRating = 0; + uint16 WornCap = itembonuses.ATK; + + if(IsClient()) { + AttackRating = ((WornCap * 1.342) + (GetSkill(OFFENSE) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69)); + AttackRating += aabonuses.ATK + GroupLeadershipAAOffenseEnhancement(); + + if (AttackRating < 10) + AttackRating = 10; + } + else + AttackRating = GetATK(); + + AttackRating += spellbonuses.ATK; + + return AttackRating; +} + +uint16 Client::GetATKRating() +{ + uint16 AttackRating = 0; + if(IsClient()) { + AttackRating = (GetSkill(OFFENSE) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69); + + if (AttackRating < 10) + AttackRating = 10; + } + return AttackRating; +} + +void Client::VoiceMacroReceived(uint32 Type, char *Target, uint32 MacroNumber) { + + uint32 GroupOrRaidID = 0; + + switch(Type) { + + case VoiceMacroGroup: { + + Group* g = GetGroup(); + + if(g) + GroupOrRaidID = g->GetID(); + else + return; + + break; + } + + case VoiceMacroRaid: { + + Raid* r = GetRaid(); + + if(r) + GroupOrRaidID = r->GetID(); + else + return; + + break; + } + } + + if(!worldserver.SendVoiceMacro(this, Type, Target, MacroNumber, GroupOrRaidID)) + Message(0, "Error: World server disconnected"); +} + +void Client::ClearGroupAAs() { + + for(unsigned int i = 0; i < MAX_GROUP_LEADERSHIP_AA_ARRAY; i++) + m_pp.leader_abilities.ranks[i] = 0; + + m_pp.group_leadership_points = 0; + m_pp.raid_leadership_points = 0; + m_pp.group_leadership_exp = 0; + m_pp.raid_leadership_exp = 0; + + Save(); +} + +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; + } + } + SendLeadershipEXPUpdate(); +} + +bool Client::IsLeadershipEXPOn() +{ + + if(!m_pp.leadAAActive) + return false; + + Group *g = GetGroup(); + + if(g && g->IsLeader(this) && (g->GroupCount() > 2)) + return true; + + Raid *r = GetRaid(); + + if(r && r->IsLeader(this) && (r->RaidCount() > 17)) + return true; + + return false; + +} + +int Client::GetAggroCount() { + return AggroCount; +} + +void Client::IncrementAggroCount() { + + // This method is called when a client is added to a mob's hate list. It turns the clients aggro flag on so + // rest state regen is stopped, and for SoF, it sends the opcode to show the crossed swords in-combat indicator. + // + // + AggroCount++; + + 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; + + if(GetClientVersion() >= EQClientSoF) { + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RestState, 1); + char *Buffer = (char *)outapp->pBuffer; + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0x01); + QueuePacket(outapp); + safe_delete(outapp); + } + +} + +void Client::DecrementAggroCount() { + + // This should be called when a client is removed from a mob's hate list (it dies or is memblurred). + // It checks whether any other mob is aggro on the player, and if not, starts the rest timer. + // For SoF, the opcode to start the rest state countdown timer in the UI is sent. + // + + // If we didn't have aggro before, this method should not have been called. + if(!AggroCount) + return; + + AggroCount--; + + if(!RuleI(Character, RestRegenPercent)) + return; + + // Something else is still aggro on us, can't rest yet. + if(AggroCount) return; + + rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000); + + if(GetClientVersion() >= EQClientSoF) { + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RestState, 5); + char *Buffer = (char *)outapp->pBuffer; + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0x00); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, RuleI(Character, RestRegenTimeToActivate)); + QueuePacket(outapp); + safe_delete(outapp); + } +} + +void Client::SendPVPStats() +{ + // This sends the data to the client to populate the PVP Stats Window. + // + // When the PVP Stats window is opened, no opcode is sent. Therefore this method should be called + // from Client::CompleteConnect, and also when the player makes a PVP kill. + // + EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPStats, sizeof(PVPStats_Struct)); + PVPStats_Struct *pvps = (PVPStats_Struct *)outapp->pBuffer; + + pvps->Kills = m_pp.PVPKills; + pvps->Deaths = m_pp.PVPDeaths; + pvps->PVPPointsAvailable = m_pp.PVPCurrentPoints; + pvps->TotalPVPPoints = m_pp.PVPCareerPoints; + pvps->BestKillStreak = m_pp.PVPBestKillStreak; + pvps->WorstDeathStreak = m_pp.PVPWorstDeathStreak; + pvps->CurrentKillStreak = m_pp.PVPCurrentKillStreak; + + // TODO: Record and send other PVP Stats + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendCrystalCounts() +{ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_CrystalCountUpdate, sizeof(CrystalCountUpdate_Struct)); + CrystalCountUpdate_Struct *ccus = (CrystalCountUpdate_Struct *)outapp->pBuffer; + + ccus->CurrentRadiantCrystals = GetRadiantCrystals(); + ccus->CurrentEbonCrystals = GetEbonCrystals(); + ccus->CareerRadiantCrystals = m_pp.careerRadCrystals; + ccus->CareerEbonCrystals = m_pp.careerEbonCrystals; + + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::SendDisciplineTimers() +{ + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_DisciplineTimer, sizeof(DisciplineTimer_Struct)); + DisciplineTimer_Struct *dts = (DisciplineTimer_Struct *)outapp->pBuffer; + + for(unsigned int i = 0; i < MAX_DISCIPLINE_TIMERS; ++i) + { + uint32 RemainingTime = p_timers.GetRemainingTime(pTimerDisciplineReuseStart + i); + + if(RemainingTime > 0) + { + dts->TimerID = i; + dts->Duration = RemainingTime; + QueuePacket(outapp); + } + } + + safe_delete(outapp); +} + +void Client::SendRespawnBinds() +{ + // This sends the data to the client to populate the Respawn from Death Window. + // + // This should be sent after OP_Death for SoF clients + // Client will respond with a 4 byte packet that includes the number of the selection made + // + + + const char* BindName = "Bind Location"; + const char* Resurrect = "Resurrect"; + + int PacketLength; + + PacketLength = 17 + (26 * 2) + strlen(BindName) + strlen(Resurrect); // SoF + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RespawnWindow, PacketLength); + + char *Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, RuleI(Character, RespawnFromHoverTimer) * 1000); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 2); // Two options, Bind or Rez + + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Entry 0 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, m_pp.binds[0].zoneId); + VARSTRUCT_ENCODE_TYPE(float, Buffer, m_pp.binds[0].x); + VARSTRUCT_ENCODE_TYPE(float, Buffer, m_pp.binds[0].y); + VARSTRUCT_ENCODE_TYPE(float, Buffer, m_pp.binds[0].z); + VARSTRUCT_ENCODE_TYPE(float, Buffer, m_pp.binds[0].heading); + VARSTRUCT_ENCODE_STRING(Buffer, BindName); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); // Entry 1 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, zone->GetZoneID()); + VARSTRUCT_ENCODE_TYPE(float, Buffer, GetX()); + VARSTRUCT_ENCODE_TYPE(float, Buffer, GetY()); + VARSTRUCT_ENCODE_TYPE(float, Buffer, GetZ()); + VARSTRUCT_ENCODE_TYPE(float, Buffer, GetHeading()); + VARSTRUCT_ENCODE_STRING(Buffer, Resurrect); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); + + QueuePacket(outapp); + safe_delete(outapp); + + return; +} + +void Client::HandleLDoNOpen(NPC *target) +{ + if(target) + { + if(target->GetClass() != LDON_TREASURE) + { + LogFile->write(EQEMuLog::Debug, "%s tried to open %s but %s was not a treasure chest.", + GetName(), target->GetName(), target->GetName()); + return; + } + + if(DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + LogFile->write(EQEMuLog::Debug, "%s tried to open %s but %s was out of range", + GetName(), target->GetName(), target->GetName()); + Message(13, "Treasure chest out of range."); + return; + } + + if(target->IsLDoNTrapped()) + { + if(target->GetLDoNTrapSpellID() != 0) + { + Message_StringID(13, LDON_ACCIDENT_SETOFF2); + target->SpellFinished(target->GetLDoNTrapSpellID(), this, 10, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff); + target->SetLDoNTrapSpellID(0); + target->SetLDoNTrapped(false); + target->SetLDoNTrapDetected(false); + } + else + { + target->SetLDoNTrapSpellID(0); + target->SetLDoNTrapped(false); + target->SetLDoNTrapDetected(false); + } + } + + if(target->IsLDoNLocked()) + { + Message_StringID(MT_Skills, LDON_STILL_LOCKED, target->GetCleanName()); + return; + } + else + { + target->AddToHateList(this, 0, 500000, false, false, false); + if(target->GetLDoNTrapType() != 0) + { + if(GetRaid()) + { + GetRaid()->SplitExp(target->GetLevel()*target->GetLevel()*2625/10, target); + } + else if(GetGroup()) + { + GetGroup()->SplitExp(target->GetLevel()*target->GetLevel()*2625/10, target); + } + else + { + AddEXP(target->GetLevel()*target->GetLevel()*2625/10, GetLevelCon(target->GetLevel())); + } + } + target->Death(this, 1, SPELL_UNKNOWN, HAND_TO_HAND); + } + } +} + +void Client::HandleLDoNSenseTraps(NPC *target, uint16 skill, uint8 type) +{ + if(target && target->GetClass() == LDON_TREASURE) + { + if(target->IsLDoNTrapped()) + { + if((target->GetLDoNTrapType() == LDoNTypeCursed || target->GetLDoNTrapType() == LDoNTypeMagical) && type != target->GetLDoNTrapType()) + { + Message_StringID(MT_Skills, LDON_CANT_DETERMINE_TRAP, target->GetCleanName()); + return; + } + + if(target->IsLDoNTrapDetected()) + { + Message_StringID(MT_Skills, LDON_CERTAIN_TRAP, target->GetCleanName()); + } + else + { + int check = LDoNChest_SkillCheck(target, skill); + switch(check) + { + case -1: + case 0: + Message_StringID(MT_Skills, LDON_DONT_KNOW_TRAPPED, target->GetCleanName()); + break; + case 1: + Message_StringID(MT_Skills, LDON_CERTAIN_TRAP, target->GetCleanName()); + target->SetLDoNTrapDetected(true); + break; + default: + break; + } + } + } + else + { + Message_StringID(MT_Skills, LDON_CERTAIN_NOT_TRAP, target->GetCleanName()); + } + } +} + +void Client::HandleLDoNDisarm(NPC *target, uint16 skill, uint8 type) +{ + if(target) + { + if(target->GetClass() == LDON_TREASURE) + { + if(!target->IsLDoNTrapped()) + { + Message_StringID(MT_Skills, LDON_WAS_NOT_TRAPPED, target->GetCleanName()); + return; + } + + if((target->GetLDoNTrapType() == LDoNTypeCursed || target->GetLDoNTrapType() == LDoNTypeMagical) && type != target->GetLDoNTrapType()) + { + Message_StringID(MT_Skills, LDON_HAVE_NOT_DISARMED, target->GetCleanName()); + return; + } + + int check = 0; + if(target->IsLDoNTrapDetected()) + { + check = LDoNChest_SkillCheck(target, skill); + } + else + { + check = LDoNChest_SkillCheck(target, skill*33/100); + } + switch(check) + { + case 1: + target->SetLDoNTrapDetected(false); + target->SetLDoNTrapped(false); + target->SetLDoNTrapSpellID(0); + Message_StringID(MT_Skills, LDON_HAVE_DISARMED, target->GetCleanName()); + break; + case 0: + Message_StringID(MT_Skills, LDON_HAVE_NOT_DISARMED, target->GetCleanName()); + break; + case -1: + Message_StringID(13, LDON_ACCIDENT_SETOFF2); + target->SpellFinished(target->GetLDoNTrapSpellID(), this, 10, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff); + target->SetLDoNTrapSpellID(0); + target->SetLDoNTrapped(false); + target->SetLDoNTrapDetected(false); + break; + } + } + } +} + +void Client::HandleLDoNPickLock(NPC *target, uint16 skill, uint8 type) +{ + if(target) + { + if(target->GetClass() == LDON_TREASURE) + { + if(target->IsLDoNTrapped()) + { + Message_StringID(13, LDON_ACCIDENT_SETOFF2); + target->SpellFinished(target->GetLDoNTrapSpellID(), this, 10, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff); + target->SetLDoNTrapSpellID(0); + target->SetLDoNTrapped(false); + target->SetLDoNTrapDetected(false); + } + + if(!target->IsLDoNLocked()) + { + Message_StringID(MT_Skills, LDON_WAS_NOT_LOCKED, target->GetCleanName()); + return; + } + + if((target->GetLDoNTrapType() == LDoNTypeCursed || target->GetLDoNTrapType() == LDoNTypeMagical) && type != target->GetLDoNTrapType()) + { + Message(MT_Skills, "You cannot unlock %s with this skill.", target->GetCleanName()); + return; + } + + int check = LDoNChest_SkillCheck(target, skill); + + switch(check) + { + case 0: + case -1: + Message_StringID(MT_Skills, LDON_PICKLOCK_FAILURE, target->GetCleanName()); + break; + case 1: + target->SetLDoNLocked(false); + Message_StringID(MT_Skills, LDON_PICKLOCK_SUCCESS, target->GetCleanName()); + break; + } + } + } +} + +int Client::LDoNChest_SkillCheck(NPC *target, int skill) +{ + if(!target) + return -1; + + int chest_difficulty = target->GetLDoNLockedSkill() == 0 ? (target->GetLevel() * 5) : target->GetLDoNLockedSkill(); + float base_difficulty = RuleR(Adventure, LDoNBaseTrapDifficulty); + + if(chest_difficulty == 0) + chest_difficulty = 5; + + float chance = ((100.0f - base_difficulty) * ((float)skill / (float)chest_difficulty)); + + if(chance > (100.0f - base_difficulty)) + { + chance = 100.0f - base_difficulty; + } + + float d100 = (float)MakeRandomFloat(0, 100); + + if(d100 <= chance) + return 1; + else + { + if(d100 > (chance + RuleR(Adventure, LDoNCriticalFailTrapThreshold))) + return -1; + } + + return 0; +} + +void Client::SummonAndRezzAllCorpses() +{ + PendingRezzXP = -1; + + ServerPacket *Pack = new ServerPacket(ServerOP_DepopAllPlayersCorpses, sizeof(ServerDepopAllPlayersCorpses_Struct)); + + ServerDepopAllPlayersCorpses_Struct *sdapcs = (ServerDepopAllPlayersCorpses_Struct*)Pack->pBuffer; + + sdapcs->CharacterID = CharacterID(); + sdapcs->ZoneID = zone->GetZoneID(); + sdapcs->InstanceID = zone->GetInstanceID(); + + worldserver.SendPacket(Pack); + + safe_delete(Pack); + + entity_list.RemoveAllCorpsesByCharID(CharacterID()); + + int CorpseCount = database.SummonAllPlayerCorpses(CharacterID(), zone->GetZoneID(), zone->GetInstanceID(), + GetX(), GetY(), GetZ(), GetHeading()); + if(CorpseCount <= 0) + { + Message(clientMessageYellow, "You have no corpses to summnon."); + return; + } + + int RezzExp = entity_list.RezzAllCorpsesByCharID(CharacterID()); + + if(RezzExp > 0) + SetEXP(GetEXP() + RezzExp, GetAAXP(), true); + + Message(clientMessageYellow, "All your corpses have been summoned to your feet and have received a 100% resurrection."); +} + +void Client::SummonAllCorpses(float dest_x, float dest_y, float dest_z, float dest_heading) +{ + + if(dest_x == 0 && dest_y == 0 && dest_z == 0 && dest_heading == 0) + { + dest_x = GetX(); dest_y = GetY(); dest_z = GetZ(); dest_heading = GetHeading(); + } + + ServerPacket *Pack = new ServerPacket(ServerOP_DepopAllPlayersCorpses, sizeof(ServerDepopAllPlayersCorpses_Struct)); + + ServerDepopAllPlayersCorpses_Struct *sdapcs = (ServerDepopAllPlayersCorpses_Struct*)Pack->pBuffer; + + sdapcs->CharacterID = CharacterID(); + sdapcs->ZoneID = zone->GetZoneID(); + sdapcs->InstanceID = zone->GetInstanceID(); + + worldserver.SendPacket(Pack); + + safe_delete(Pack); + + entity_list.RemoveAllCorpsesByCharID(CharacterID()); + + int CorpseCount = database.SummonAllPlayerCorpses(CharacterID(), zone->GetZoneID(), zone->GetInstanceID(), + dest_x, dest_y, dest_z, dest_heading); + if(CorpseCount <= 0) + { + return; + } +} + +void Client::DepopAllCorpses() +{ + ServerPacket *Pack = new ServerPacket(ServerOP_DepopAllPlayersCorpses, sizeof(ServerDepopAllPlayersCorpses_Struct)); + + ServerDepopAllPlayersCorpses_Struct *sdapcs = (ServerDepopAllPlayersCorpses_Struct*)Pack->pBuffer; + + sdapcs->CharacterID = CharacterID(); + sdapcs->ZoneID = zone->GetZoneID(); + sdapcs->InstanceID = zone->GetInstanceID(); + + worldserver.SendPacket(Pack); + + safe_delete(Pack); + + entity_list.RemoveAllCorpsesByCharID(CharacterID()); +} + +void Client::DepopPlayerCorpse(uint32 dbid) +{ + ServerPacket *Pack = new ServerPacket(ServerOP_DepopPlayerCorpse, sizeof(ServerDepopPlayerCorpse_Struct)); + + ServerDepopPlayerCorpse_Struct *sdpcs = (ServerDepopPlayerCorpse_Struct*)Pack->pBuffer; + + sdpcs->DBID = dbid; + sdpcs->ZoneID = zone->GetZoneID(); + sdpcs->InstanceID = zone->GetInstanceID(); + + worldserver.SendPacket(Pack); + + safe_delete(Pack); + + entity_list.RemoveCorpseByDBID(dbid); +} + +void Client::BuryPlayerCorpses() +{ + database.BuryAllPlayerCorpses(CharacterID()); +} + +void Client::NotifyNewTitlesAvailable() +{ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_NewTitlesAvailable, 0); + + QueuePacket(outapp); + + safe_delete(outapp); + +} + +void Client::SetStartZone(uint32 zoneid, float x, float y, float z) +{ + // setting city to zero allows the player to use /setstartcity to set the city themselves + if(zoneid == 0) { + m_pp.binds[4].zoneId = 0; + this->Message(15,"Your starting city has been reset. Use /setstartcity to choose a new one"); + return; + } + + // check to make sure the zone is valid + const char *target_zone_name = database.GetZoneName(zoneid); + if(target_zone_name == NULL) + return; + + m_pp.binds[4].zoneId = zoneid; + 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 { + m_pp.binds[4].x = x; + m_pp.binds[4].y = y; + m_pp.binds[4].z = z; + } +} + +uint32 Client::GetStartZone() +{ + return m_pp.binds[4].zoneId; +} + +void Client::ShowSkillsWindow() +{ + const char *WindowTitle = "Skills"; + string WindowText; + // using a map for easy alphabetizing of the skills list + map Skills; + map::iterator it; + + // this list of names must keep the same order as that in common/skills.h + const char* SkillName[] = {"1H Blunt","1H Slashing","2H Blunt","2H Slashing","Abjuration","Alteration","Apply Poison","Archery", + "Backstab","Bind Wound","Bash","Block","Brass Instruments","Channeling","Conjuration","Defense","Disarm","Disarm Traps","Divination", + "Dodge","Double Attack","Dragon Punch","Dual Wield","Eagle Strike","Evocation","Feign Death","Flying Kick","Forage","Hand to Hand", + "Hide","Kick","Meditate","Mend","Offense","Parry","Pick Lock","Piercing","Ripost","Round Kick","Safe Fall","Sense Heading", + "Singing","Sneak","Specialize Abjuration","Specialize Alteration","Specialize Conjuration","Specialize Divination","Specialize Evocation","Pick Pockets", + "Stringed Instruments","Swimming","Throwing","Tiger Claw","Tracking","Wind Instruments","Fishing","Make Poison","Tinkering","Research", + "Alchemy","Baking","Tailoring","Sense Traps","Blacksmithing","Fletching","Brewing","Alcohol Tolerance","Begging","Jewelry Making", + "Pottery","Percussion Instruments","Intimidation","Berserking","Taunt","Frenzy"}; + for(int i = 0; i <= (int)HIGHEST_SKILL; i++) + Skills[SkillName[i]] = (SkillType)i; + + // print out all available skills + for(it = Skills.begin(); it != Skills.end(); it++) { + if(GetSkill(it->second) > 0 || MaxSkill(it->second) > 0) { + WindowText += it->first; + // line up the values + for (int j = 0; j < 5; j++) + WindowText += " "; + WindowText += itoa(this->GetSkill(it->second)); + if (MaxSkill(it->second) > 0) { + WindowText += "/"; + WindowText += itoa(this->GetMaxSkillAfterSpecializationRules(it->second,this->MaxSkill(it->second))); + } + WindowText += "
"; + } + } + this->SendPopupToClient(WindowTitle, WindowText.c_str()); +} + + +void Client::SetShadowStepExemption(bool v) +{ + if(v == true) + { + uint32 cur_time = Timer::GetCurrentTime(); + if((cur_time - m_TimeSinceLastPositionCheck) > 1000) + { + float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); + float runs = GetRunspeed(); + if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) + { + printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__, + m_DistanceSinceLastPositionCheck, (cur_time - m_TimeSinceLastPositionCheck), speed); + if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) + { + if(IsShadowStepExempted()) + { + if(m_DistanceSinceLastPositionCheck > 800) + { + CheatDetected(MQWarpShadowStep, GetX(), GetY(), GetZ()); + } + } + else if(IsKnockBackExempted()) + { + //still potential to trigger this if you're knocked back off a + //HUGE fall that takes > 2.5 seconds + if(speed > 30.0f) + { + CheatDetected(MQWarpKnockBack, GetX(), GetY(), GetZ()); + } + } + else if(!IsPortExempted()) + { + if(!IsMQExemptedArea(zone->GetZoneID(), GetX(), GetY(), GetZ())) + { + if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor))) + { + CheatDetected(MQWarp, GetX(), GetY(), GetZ()); + m_TimeSinceLastPositionCheck = cur_time; + m_DistanceSinceLastPositionCheck = 0.0f; + //Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT); + } + else + { + CheatDetected(MQWarpLight, GetX(), GetY(), GetZ()); + } + } + } + } + } + } + m_TimeSinceLastPositionCheck = cur_time; + m_DistanceSinceLastPositionCheck = 0.0f; + } + m_ShadowStepExemption = v; +} + +void Client::SetKnockBackExemption(bool v) +{ + if(v == true) + { + uint32 cur_time = Timer::GetCurrentTime(); + if((cur_time - m_TimeSinceLastPositionCheck) > 1000) + { + float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); + float runs = GetRunspeed(); + if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) + { + if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) + { + printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__, + m_DistanceSinceLastPositionCheck, (cur_time - m_TimeSinceLastPositionCheck), speed); + if(IsShadowStepExempted()) + { + if(m_DistanceSinceLastPositionCheck > 800) + { + CheatDetected(MQWarpShadowStep, GetX(), GetY(), GetZ()); + } + } + else if(IsKnockBackExempted()) + { + //still potential to trigger this if you're knocked back off a + //HUGE fall that takes > 2.5 seconds + if(speed > 30.0f) + { + CheatDetected(MQWarpKnockBack, GetX(), GetY(), GetZ()); + } + } + else if(!IsPortExempted()) + { + if(!IsMQExemptedArea(zone->GetZoneID(), GetX(), GetY(), GetZ())) + { + if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor))) + { + m_TimeSinceLastPositionCheck = cur_time; + m_DistanceSinceLastPositionCheck = 0.0f; + CheatDetected(MQWarp, GetX(), GetY(), GetZ()); + //Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT); + } + else + { + CheatDetected(MQWarpLight, GetX(), GetY(), GetZ()); + } + } + } + } + } + } + m_TimeSinceLastPositionCheck = cur_time; + m_DistanceSinceLastPositionCheck = 0.0f; + } + m_KnockBackExemption = v; +} + +void Client::SetPortExemption(bool v) +{ + if(v == true) + { + uint32 cur_time = Timer::GetCurrentTime(); + if((cur_time - m_TimeSinceLastPositionCheck) > 1000) + { + float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); + float runs = GetRunspeed(); + if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) + { + if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) + { + printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__, + m_DistanceSinceLastPositionCheck, (cur_time - m_TimeSinceLastPositionCheck), speed); + if(IsShadowStepExempted()) + { + if(m_DistanceSinceLastPositionCheck > 800) + { + CheatDetected(MQWarpShadowStep, GetX(), GetY(), GetZ()); + } + } + else if(IsKnockBackExempted()) + { + //still potential to trigger this if you're knocked back off a + //HUGE fall that takes > 2.5 seconds + if(speed > 30.0f) + { + CheatDetected(MQWarpKnockBack, GetX(), GetY(), GetZ()); + } + } + else if(!IsPortExempted()) + { + if(!IsMQExemptedArea(zone->GetZoneID(), GetX(), GetY(), GetZ())) + { + if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor))) + { + m_TimeSinceLastPositionCheck = cur_time; + m_DistanceSinceLastPositionCheck = 0.0f; + CheatDetected(MQWarp, GetX(), GetY(), GetZ()); + //Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT); + } + else + { + CheatDetected(MQWarpLight, GetX(), GetY(), GetZ()); + } + } + } + } + } + } + m_TimeSinceLastPositionCheck = cur_time; + m_DistanceSinceLastPositionCheck = 0.0f; + } + m_PortExemption = v; +} + +void Client::Signal(uint32 data) +{ + char buf[32]; + snprintf(buf, 31, "%d", data); + buf[31] = '\0'; + parse->EventPlayer(EVENT_SIGNAL, this, buf, 0); +} + +const bool Client::IsMQExemptedArea(uint32 zoneID, float x, float y, float z) const +{ + float max_dist = 90000; + switch(zoneID) + { + case 2: + { + float delta = (x-(-713.6)); + delta *= delta; + float distance = delta; + delta = (y-(-160.2)); + delta *= delta; + distance += delta; + delta = (z-(-12.8)); + delta *= delta; + distance += delta; + + if(distance < max_dist) + return true; + + delta = (x-(-153.8)); + delta *= delta; + distance = delta; + delta = (y-(-30.3)); + delta *= delta; + distance += delta; + delta = (z-(8.2)); + delta *= delta; + distance += delta; + + if(distance < max_dist) + return true; + + break; + } + case 9: + { + float delta = (x-(-682.5)); + delta *= delta; + float distance = delta; + delta = (y-(147.0)); + delta *= delta; + distance += delta; + delta = (z-(-9.9)); + delta *= delta; + distance += delta; + + if(distance < max_dist) + return true; + + delta = (x-(-655.4)); + delta *= delta; + distance = delta; + delta = (y-(10.5)); + delta *= delta; + distance += delta; + delta = (z-(-51.8)); + delta *= delta; + distance += delta; + + if(distance < max_dist) + return true; + + break; + } + case 62: + case 75: + case 114: + case 209: + { + //The portals are so common in paineel/felwitheb that checking + //distances wouldn't be worth it cause unless you're porting to the + //start field you're going to be triggering this and that's a level of + //accuracy I'm willing to sacrifice + return true; + break; + } + + case 24: + { + float delta = (x-(-183.0)); + delta *= delta; + float distance = delta; + delta = (y-(-773.3)); + delta *= delta; + distance += delta; + delta = (z-(54.1)); + delta *= delta; + distance += delta; + + if(distance < max_dist) + return true; + + delta = (x-(-8.8)); + delta *= delta; + distance = delta; + delta = (y-(-394.1)); + delta *= delta; + distance += delta; + delta = (z-(41.1)); + delta *= delta; + distance += delta; + + if(distance < max_dist) + return true; + + delta = (x-(-310.3)); + delta *= delta; + distance = delta; + delta = (y-(-1411.6)); + delta *= delta; + distance += delta; + delta = (z-(-42.8)); + delta *= delta; + distance += delta; + + if(distance < max_dist) + return true; + + delta = (x-(-183.1)); + delta *= delta; + distance = delta; + delta = (y-(-1409.8)); + delta *= delta; + distance += delta; + delta = (z-(37.1)); + delta *= delta; + distance += delta; + + if(distance < max_dist) + return true; + + break; + } + + case 110: + case 34: + case 96: + case 93: + case 68: + case 84: + { + if(GetBoatID() != 0) + return true; + break; + } + default: + break; + } + return false; +} + +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); + 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; + list::iterator iter = zone->VeteranRewards.begin(); + while(iter != zone->VeteranRewards.end()) + { + if((*iter).claim_id == rewards[i].id) + { + break; + } + iter++; + } + + 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) +{ + //Make sure we have an open spot + //Make sure we have it in our acct and count > 0 + //Make sure the entry was found + //If we meet all the criteria: + //Decrement our count by 1 if it > 1 delete if it == 1 + //Create our item in bag if necessary at the free inv slot + //save + uint32 free_slot = 0xFFFFFFFF; + + for(int i = 22; i < 30; ++i) + { + ItemInst *item = GetInv().GetItem(i); + 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); + return false; + } + + if(amt == 0) + { + return false; + } + + list::iterator iter = zone->VeteranRewards.begin(); + while(iter != zone->VeteranRewards.end()) + { + 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); + } + } + 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); + } + } + + 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); + } + } + + Save(); + return true; +} + +uint32 Client::GetLDoNPointsTheme(uint32 t) +{ + switch(t) + { + case 1: + return m_pp.ldon_points_guk; + case 2: + return m_pp.ldon_points_mir; + case 3: + return m_pp.ldon_points_mmc; + case 4: + return m_pp.ldon_points_ruj; + case 5: + return m_pp.ldon_points_tak; + default: + return 0; + } +} + +uint32 Client::GetLDoNWinsTheme(uint32 t) +{ + switch(t) + { + case 1: + return m_pp.ldon_wins_guk; + case 2: + return m_pp.ldon_wins_mir; + case 3: + return m_pp.ldon_wins_mmc; + case 4: + return m_pp.ldon_wins_ruj; + case 5: + return m_pp.ldon_wins_tak; + default: + return 0; + } +} + +uint32 Client::GetLDoNLossesTheme(uint32 t) +{ + switch(t) + { + case 1: + return m_pp.ldon_losses_guk; + case 2: + return m_pp.ldon_losses_mir; + case 3: + return m_pp.ldon_losses_mmc; + case 4: + return m_pp.ldon_losses_ruj; + case 5: + return m_pp.ldon_losses_tak; + default: + return 0; + } +} + +void Client::UpdateLDoNWins(uint32 t, int32 n) +{ + switch(t) + { + case 1: + m_pp.ldon_wins_guk = n; + break; + case 2: + m_pp.ldon_wins_mir = n; + break; + case 3: + m_pp.ldon_wins_mmc = n; + break; + case 4: + m_pp.ldon_wins_ruj = n; + break; + case 5: + m_pp.ldon_wins_tak = n; + break; + default: + return; + } +} + +void Client::UpdateLDoNLosses(uint32 t, int32 n) +{ + switch(t) + { + case 1: + m_pp.ldon_losses_guk = n; + break; + case 2: + m_pp.ldon_losses_mir = n; + break; + case 3: + m_pp.ldon_losses_mmc = n; + break; + case 4: + m_pp.ldon_losses_ruj = n; + break; + case 5: + m_pp.ldon_losses_tak = n; + break; + default: + return; + } +} + + +void Client::SuspendMinion() +{ + NPC *CurrentPet = GetPet()->CastToNPC(); + + int AALevel = GetAA(aaSuspendedMinion); + + if(AALevel == 0) + return; + + if(GetLevel() < 62) + return; + + if(!CurrentPet) + { + if(m_suspendedminion.SpellID > 0) + { + MakePoweredPet(m_suspendedminion.SpellID, spells[m_suspendedminion.SpellID].teleport_zone, + m_suspendedminion.petpower, m_suspendedminion.Name); + + CurrentPet = GetPet()->CastToNPC(); + + if(!CurrentPet) + { + Message(13, "Failed to recall suspended minion."); + return; + } + + if(AALevel >= 2) + { + CurrentPet->SetPetState(m_suspendedminion.Buffs, m_suspendedminion.Items); + + CurrentPet->SendPetBuffsToClient(); + } + CurrentPet->CalcBonuses(); + + CurrentPet->SetHP(m_suspendedminion.HP); + + CurrentPet->SetMana(m_suspendedminion.Mana); + + Message_StringID(clientMessageTell, SUSPEND_MINION_UNSUSPEND, CurrentPet->GetCleanName()); + + memset(&m_suspendedminion, 0, sizeof(struct PetInfo)); + } + else + return; + + } + else + { + uint16 SpellID = CurrentPet->GetPetSpellID(); + + if(SpellID) + { + if(m_suspendedminion.SpellID > 0) + { + Message_StringID(clientMessageError,ONLY_ONE_PET); + + return; + } + else if(CurrentPet->IsEngaged()) + { + Message_StringID(clientMessageError,SUSPEND_MINION_FIGHTING); + + return; + } + else if(entity_list.Fighting(CurrentPet)) + { + Message_StringID(clientMessageBlue,SUSPEND_MINION_HAS_AGGRO); + } + else + { + m_suspendedminion.SpellID = SpellID; + + m_suspendedminion.HP = CurrentPet->GetHP();; + + m_suspendedminion.Mana = CurrentPet->GetMana(); + m_suspendedminion.petpower = CurrentPet->GetPetPower(); + + if(AALevel >= 2) + CurrentPet->GetPetState(m_suspendedminion.Buffs, m_suspendedminion.Items, m_suspendedminion.Name); + else + strn0cpy(m_suspendedminion.Name, CurrentPet->GetName(), 64); // Name stays even at rank 1 + + Message_StringID(clientMessageTell, SUSPEND_MINION_SUSPEND, CurrentPet->GetCleanName()); + + CurrentPet->Depop(false); + + SetPetID(0); + } + } + else + { + Message_StringID(clientMessageError, ONLY_SUMMONED_PETS); + + return; + } + } +} + +void Client::AddPVPPoints(uint32 Points) +{ + m_pp.PVPCurrentPoints += Points; + m_pp.PVPCareerPoints += Points; + + Save(); + + SendPVPStats(); +} + +void Client::AddCrystals(uint32 Radiant, uint32 Ebon) +{ + m_pp.currentRadCrystals += Radiant; + m_pp.careerRadCrystals += Radiant; + m_pp.currentEbonCrystals += Ebon; + m_pp.careerEbonCrystals += Ebon; + + Save(); + + SendCrystalCounts(); +} + +// Processes a client request to inspect a SoF client's equipment. +void Client::ProcessInspectRequest(Client* requestee, Client* requester) { + if(requestee && requester) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_InspectAnswer, sizeof(InspectResponse_Struct)); + InspectResponse_Struct* insr = (InspectResponse_Struct*) outapp->pBuffer; + insr->TargetID = requester->GetID(); + insr->playerid = requestee->GetID(); + + const Item_Struct* item = NULL; + const ItemInst* inst = NULL; + + 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; + } + else + insr->itemicons[L] = 0xFFFFFFFF; + } + } + + inst = requestee->GetInv().GetItem(9999); + + if(inst) { + item = inst->GetItem(); + if(item) { + strcpy(insr->itemnames[21], item->Name); + insr->itemicons[21] = item->Icon; + } + else + insr->itemicons[21] = 0xFFFFFFFF; + } + + inst = requestee->GetInv().GetItem(21); + + if(inst) { + item = inst->GetItem(); + if(item) { + strcpy(insr->itemnames[22], item->Name); + insr->itemicons[22] = item->Icon; + } + else + insr->itemicons[22] = 0xFFFFFFFF; + } + + strcpy(insr->text, requestee->GetInspectMessage().text); + + // There could be an OP for this..or not... (Ti clients are not processed here..this message is generated client-side) + if(requestee->IsClient() && (requestee != requester)) { requestee->Message(0, "%s is looking at your equipment...", requester->GetName()); } + + requester->QueuePacket(outapp); // Send answer to requester + safe_delete(outapp); + } +} + +void Client::GuildBankAck() +{ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildBank, sizeof(GuildBankAck_Struct)); + + GuildBankAck_Struct *gbas = (GuildBankAck_Struct*) outapp->pBuffer; + + gbas->Action = GuildBankAcknowledge; + + FastQueuePacket(&outapp); +} + +void Client::GuildBankDepositAck(bool Fail) +{ + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildBank, sizeof(GuildBankDepositAck_Struct)); + + GuildBankDepositAck_Struct *gbdas = (GuildBankDepositAck_Struct*) outapp->pBuffer; + + gbdas->Action = GuildBankDeposit; + + gbdas->Fail = Fail ? 1 : 0; + + FastQueuePacket(&outapp); +} + +void Client::ClearGuildBank() +{ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildBank, sizeof(GuildBankClear_Struct)); + + GuildBankClear_Struct *gbcs = (GuildBankClear_Struct*) outapp->pBuffer; + + gbcs->Action = GuildBankBulkItems; + gbcs->DepositAreaCount = 0; + gbcs->MainAreaCount = 0; + + FastQueuePacket(&outapp); +} + +void Client::SendGroupCreatePacket() +{ + // For SoD and later clients, this is sent the Group Leader upon initial creation of the group + // + EQApplicationPacket *outapp=new EQApplicationPacket(OP_GroupUpdateB, 32 + strlen(GetName())); + + char *Buffer = (char *)outapp->pBuffer; + // Header + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // Null Leader name + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Member 0 + VARSTRUCT_ENCODE_STRING(Buffer, GetName()); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, GetLevel()); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + + FastQueuePacket(&outapp); +} + +void Client::SendGroupLeaderChangePacket(const char *LeaderName) +{ + // For SoD and later, send name of Group Leader to this client + + EQApplicationPacket *outapp=new EQApplicationPacket(OP_GroupLeaderChange, sizeof(GroupLeaderChange_Struct)); + + GroupLeaderChange_Struct *glcs = (GroupLeaderChange_Struct*)outapp->pBuffer; + + strn0cpy(glcs->LeaderName, LeaderName, sizeof(glcs->LeaderName)); + + FastQueuePacket(&outapp); +} + +void Client::SendGroupJoinAcknowledge() +{ + // For SoD and later, This produces the 'You have joined the group' message. + EQApplicationPacket* outapp=new EQApplicationPacket(OP_GroupAcknowledge, 4); + FastQueuePacket(&outapp); +} + +void Client::SendAdventureError(const char *error) +{ + size_t error_size = strlen(error); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (error_size + 2)); + strn0cpy((char*)outapp->pBuffer, error, error_size); + FastQueuePacket(&outapp); +} + +void Client::SendAdventureDetails() +{ + if(adv_data) + { + ServerSendAdventureData_Struct *ad = (ServerSendAdventureData_Struct*)adv_data; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureData, sizeof(AdventureRequestResponse_Struct)); + AdventureRequestResponse_Struct *arr = (AdventureRequestResponse_Struct*)outapp->pBuffer; + arr->unknown000 = 0xBFC40100; + arr->unknown2080 = 0x0A; + arr->risk = ad->risk; + strcpy(arr->text, ad->text); + + if(ad->time_to_enter != 0) + { + arr->timetoenter = ad->time_to_enter; + } + else + { + arr->timeleft = ad->time_left; + } + + if(ad->zone_in_id == zone->GetZoneID()) + { + arr->y = ad->x; + arr->x = ad->y; + arr->showcompass = 1; + } + FastQueuePacket(&outapp); + + SendAdventureCount(ad->count, ad->total); + } + else + { + ServerSendAdventureData_Struct *ad = (ServerSendAdventureData_Struct*)adv_data; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureData, sizeof(AdventureRequestResponse_Struct)); + FastQueuePacket(&outapp); + } +} + +void Client::SendAdventureCount(uint32 count, uint32 total) +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureUpdate, sizeof(AdventureCountUpdate_Struct)); + AdventureCountUpdate_Struct *acu = (AdventureCountUpdate_Struct*)outapp->pBuffer; + acu->current = count; + acu->total = total; + FastQueuePacket(&outapp); +} + +void Client::NewAdventure(int id, int theme, const char *text, int member_count, const char *members) +{ + size_t text_size = strlen(text); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureDetails, text_size + 2); + strn0cpy((char*)outapp->pBuffer, text, text_size); + FastQueuePacket(&outapp); + + adv_requested_id = id; + adv_requested_theme = theme; + safe_delete_array(adv_requested_data); + adv_requested_member_count = member_count; + adv_requested_data = new char[64 * member_count]; + memcpy(adv_requested_data, members, (64 * member_count)); +} + +void Client::ClearPendingAdventureData() +{ + adv_requested_id = 0; + adv_requested_theme = 0; + safe_delete_array(adv_requested_data); + adv_requested_member_count = 0; +} + +bool Client::IsOnAdventure() +{ + if(adv_data) + { + ServerSendAdventureData_Struct *ad = (ServerSendAdventureData_Struct*)adv_data; + if(ad->zone_in_id == 0) + { + return false; + } + else + { + return true; + } + } + return false; +} + +void Client::LeaveAdventure() +{ + if(!GetPendingAdventureLeave()) + { + PendingAdventureLeave(); + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeave, 64); + strcpy((char*)pack->pBuffer, GetName()); + pack->Deflate(); + worldserver.SendPacket(pack); + delete pack; + } +} + +void Client::ClearCurrentAdventure() +{ + if(adv_data) + { + ServerSendAdventureData_Struct* ds = (ServerSendAdventureData_Struct*)adv_data; + if(ds->finished_adventures > 0) + { + ds->instance_id = 0; + ds->risk = 0; + memset(ds->text, 0, 512); + ds->time_left = 0; + ds->time_to_enter = 0; + ds->x = 0; + ds->y = 0; + ds->zone_in_id = 0; + ds->zone_in_object = 0; + } + else + { + safe_delete(adv_data); + } + + SendAdventureError("You are not currently assigned to an adventure."); + } +} + +void Client::AdventureFinish(bool win, int theme, int points) +{ + UpdateLDoNPoints(points, theme); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureFinish, sizeof(AdventureFinish_Struct)); + AdventureFinish_Struct *af = (AdventureFinish_Struct*)outapp->pBuffer; + af->win_lose = win ? 1 : 0; + af->points = points; + FastQueuePacket(&outapp); +} + +void Client::CheckLDoNHail(Mob *target) +{ + if(!zone->adv_data) + { + return; + } + + if(!target || !target->IsNPC()) + { + return; + } + + if(target->GetOwnerID() != 0) + { + return; + } + + ServerZoneAdventureDataReply_Struct* ds = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; + if(ds->type != Adventure_Rescue) + { + return; + } + + if(ds->data_id != target->GetNPCTypeID()) + { + return; + } + + if(entity_list.CheckNPCsClose(target) != 0) + { + target->Say("You're here to save me? I couldn't possibly risk leaving yet. There are " + "far too many of those horrid things out there waiting to recapture me! Please get" + " rid of some more of those vermin and then we can try to leave."); + return; + } + + Mob *pet = GetPet(); + if(pet) + { + if(pet->GetPetType() == petCharmed) + { + pet->BuffFadeByEffect(SE_Charm); + } + else if(pet->GetPetType() == petNPCFollow) + { + pet->SetOwnerID(0); + } + else + { + pet->Depop(); + } + } + + SetPet(target); + target->SetOwnerID(GetID()); + target->Say("Wonderful! Someone to set me free! I feared for my life for so long," + " never knowing when they might choose to end my life. Now that you're here though" + " I can rest easy. Please help me find my way out of here as soon as you can" + " I'll stay close behind you!"); +} + +void Client::CheckEmoteHail(Mob *target, const char* message) +{ + if( + (message[0] != 'H' && + message[0] != 'h') || + message[1] != 'a' || + message[2] != 'i' || + message[3] != 'l'){ + return; + } + + if(!target || !target->IsNPC()) + { + return; + } + + if(target->GetOwnerID() != 0) + { + return; + } + uint16 emoteid = target->CastToNPC()->GetNPCEmoteID(); + if(emoteid != 0) + target->CastToNPC()->DoNPCEmote(HAILED,emoteid); +} + +void Client::MarkSingleCompassLoc(float in_x, float in_y, float in_z, uint8 count) +{ + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_DzCompass, sizeof(ExpeditionInfo_Struct) + sizeof(ExpeditionCompassEntry_Struct) * count); + ExpeditionCompass_Struct *ecs = (ExpeditionCompass_Struct*)outapp->pBuffer; + //ecs->clientid = GetID(); + ecs->count = count; + + if (count) { + ecs->entries[0].x = in_x; + ecs->entries[0].y = in_y; + ecs->entries[0].z = in_z; + } + + FastQueuePacket(&outapp); + safe_delete(outapp); +} + +void Client::SendZonePoints() +{ + int count = 0; + LinkedListIterator iterator(zone->zone_point_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + ZonePoint* data = iterator.GetData(); + if(GetClientVersionBit() & data->client_version_mask) + { + count++; + } + iterator.Advance(); + } + + uint32 zpsize = sizeof(ZonePoints) + ((count + 1) * sizeof(ZonePoint_Entry)); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendZonepoints, zpsize); + ZonePoints* zp = (ZonePoints*)outapp->pBuffer; + zp->count = count; + + int i = 0; + iterator.Reset(); + while(iterator.MoreElements()) + { + ZonePoint* data = iterator.GetData(); + if(GetClientVersionBit() & data->client_version_mask) + { + zp->zpe[i].iterator = data->number; + zp->zpe[i].x = data->target_x; + zp->zpe[i].y = data->target_y; + zp->zpe[i].z = data->target_z; + zp->zpe[i].heading = data->target_heading; + zp->zpe[i].zoneid = data->target_zone_id; + zp->zpe[i].zoneinstance = data->target_zone_instance; + i++; + } + iterator.Advance(); + } + FastQueuePacket(&outapp); +} + +void Client::SendTargetCommand(uint32 EntityID) +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetCommand, sizeof(ClientTarget_Struct)); + ClientTarget_Struct *cts = (ClientTarget_Struct*)outapp->pBuffer; + cts->new_target = EntityID; + FastQueuePacket(&outapp); +} + +void Client::LocateCorpse() +{ + Corpse *ClosestCorpse = NULL; + if(!GetTarget()) + ClosestCorpse = entity_list.GetClosestCorpse(this, NULL); + else if(GetTarget()->IsCorpse()) + ClosestCorpse = entity_list.GetClosestCorpse(this, GetTarget()->CastToCorpse()->GetOwnerName()); + else + ClosestCorpse = entity_list.GetClosestCorpse(this, GetTarget()->GetCleanName()); + + if(ClosestCorpse) + { + Message_StringID(MT_Spells, SENSE_CORPSE_DIRECTION); + SetHeading(CalculateHeadingToTarget(ClosestCorpse->GetX(), ClosestCorpse->GetY())); + SetTarget(ClosestCorpse); + SendTargetCommand(ClosestCorpse->GetID()); + SendPosUpdate(2); + } + else if(!GetTarget()) + Message_StringID(clientMessageError, SENSE_CORPSE_NONE); + else + Message_StringID(clientMessageError, SENSE_CORPSE_NOT_NAME); +} + +void Client::NPCSpawn(NPC *target_npc, const char *identifier, uint32 extra) +{ + if (!target_npc || !identifier) + return; + + std::string id = identifier; + for(int i = 0; i < id.length(); ++i) + { + id[i] = tolower(id[i]); + } + + if (id == "create") { + // extra tries to create the npc_type ID within the range for the current zone (zone_id * 1000) + database.NPCSpawnDB(0, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC(), extra); + } + else if (id == "add") { + // extra sets the respawn timer for add + database.NPCSpawnDB(1, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC(), extra); + } + else if (id == "update") { + database.NPCSpawnDB(2, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC()); + } + else if (id == "remove") { + database.NPCSpawnDB(3, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC()); + target_npc->Depop(false); + } + else if (id == "delete") { + database.NPCSpawnDB(4, zone->GetShortName(), zone->GetInstanceVersion(), this, target_npc->CastToNPC()); + target_npc->Depop(false); + } + else { + return; + } +} + +bool Client::IsDraggingCorpse(const char *CorpseName) +{ + for(std::list::iterator Iterator = DraggedCorpses.begin(); Iterator != DraggedCorpses.end(); ++Iterator) + { + if(!strcasecmp((*Iterator).c_str(), CorpseName)) + return true; + } + + return false; +} + +void Client::DragCorpses() +{ + for(std::list::iterator Iterator = DraggedCorpses.begin(); Iterator != DraggedCorpses.end(); ++Iterator) + { + Mob* corpse = entity_list.GetMob((*Iterator).c_str()); + + if(corpse && corpse->IsPlayerCorpse() && (DistNoRootNoZ(*corpse) <= RuleR(Character, DragCorpseDistance))) + continue; + + if(!corpse || !corpse->IsPlayerCorpse() || corpse->CastToCorpse()->IsBeingLooted() || !corpse->CastToCorpse()->Summon(this, false, false)) + { + Message_StringID(MT_DefaultText, CORPSEDRAG_STOP); + Iterator = DraggedCorpses.erase(Iterator); + } + } +} +void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration) +{ + if(!target || !IsValidSpell(spell_id) || this->GetID() == target->GetID()) + return; + + PetRecord record; + if(!database.GetPetEntry(spells[spell_id].teleport_zone, &record)) + { + LogFile->write(EQEMuLog::Error, "Unknown doppelganger spell id: %d, check pets table", spell_id); + Message(13, "Unable to find data for pet %s", spells[spell_id].teleport_zone); + return; + } + + AA_SwarmPet pet; + pet.count = pet_count; + pet.duration = pet_duration; + pet.npc_id = record.npc_type; + + NPCType *made_npc = NULL; + + const NPCType *npc_type = database.GetNPCType(pet.npc_id); + if(npc_type == NULL) { + LogFile->write(EQEMuLog::Error, "Unknown npc type for doppelganger spell id: %d", spell_id); + Message(0,"Unable to find pet!"); + return; + } + // make a custom NPC type for this + made_npc = new NPCType; + memcpy(made_npc, npc_type, sizeof(NPCType)); + + strcpy(made_npc->name, name_override); + made_npc->level = GetLevel(); + made_npc->race = GetRace(); + made_npc->gender = GetGender(); + made_npc->size = GetSize(); + made_npc->AC = GetAC(); + made_npc->STR = GetSTR(); + made_npc->STA = GetSTA(); + made_npc->DEX = GetDEX(); + made_npc->AGI = GetAGI(); + made_npc->MR = GetMR(); + made_npc->FR = GetFR(); + made_npc->CR = GetCR(); + made_npc->DR = GetDR(); + made_npc->PR = GetPR(); + made_npc->Corrup = GetCorrup(); + // looks + made_npc->texture = GetEquipmentMaterial(1); + made_npc->helmtexture = GetEquipmentMaterial(0); + made_npc->haircolor = GetHairColor(); + made_npc->beardcolor = GetBeardColor(); + made_npc->eyecolor1 = GetEyeColor1(); + made_npc->eyecolor2 = GetEyeColor2(); + made_npc->hairstyle = GetHairStyle(); + made_npc->luclinface = GetLuclinFace(); + made_npc->beard = GetBeard(); + made_npc->drakkin_heritage = GetDrakkinHeritage(); + made_npc->drakkin_tattoo = GetDrakkinTattoo(); + made_npc->drakkin_details = GetDrakkinDetails(); + made_npc->d_meele_texture1 = GetEquipmentMaterial(7); + made_npc->d_meele_texture2 = GetEquipmentMaterial(8); + for (int i = 0; i < MAX_MATERIALS; i++) { + made_npc->armor_tint[i] = GetEquipmentColor(i); + } + made_npc->loottable_id = 0; + + npc_type = made_npc; + + int summon_count = 0; + summon_count = pet.count; + + if(summon_count > MAX_SWARM_PETS) + summon_count = MAX_SWARM_PETS; + + 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 = NULL; + if(made_npc != NULL) { + npc_dup = new NPCType; + memcpy(npc_dup, made_npc, sizeof(NPCType)); + } + + NPC* npca = new NPC( + (npc_dup!=NULL)?npc_dup:npc_type, //make sure we give the NPC the correct data pointer + 0, + GetX()+swarm_pet_x[summon_count], GetY()+swarm_pet_y[summon_count], + GetZ(), GetHeading(), FlyMode3); + + if(!npca->GetSwarmInfo()){ + AA_SwarmPetInfo* nSI = new AA_SwarmPetInfo; + npca->SetSwarmInfo(nSI); + npca->GetSwarmInfo()->duration = new Timer(pet_duration*1000); + } + else{ + npca->GetSwarmInfo()->duration->Start(pet_duration*1000); + } + + npca->GetSwarmInfo()->owner_id = GetID(); + + // Give the pets alittle more agro than the caster and then agro them on the target + target->AddToHateList(npca, (target->GetHateAmount(this) + 100), (target->GetDamageAmount(this) + 100)); + npca->AddToHateList(target, 1000, 1000); + npca->GetSwarmInfo()->target = target->GetID(); + + //we allocated a new NPC type object, give the NPC ownership of that memory + if(npc_dup != NULL) + npca->GiveNPCTypeData(npc_dup); + + entity_list.AddNPC(npca); + summon_count--; + } +} + +void Client::AssignToInstance(uint16 instance_id) +{ + database.AddClientToInstance(instance_id, CharacterID()); +} + +void Client::SendStatsWindow(Client* client, bool use_window) +{ + // Define the types of page breaks we need + std::string indP = " "; + std::string indS = "          "; + std::string indM = "                          "; + std::string indL = "                                 "; + std::string div = " | "; + + std::string color_red = ""; + std::string color_blue = ""; + std::string color_green = ""; + std::string bright_green = ""; + std::string bright_red = ""; + std::string heroic_color = " +"; + + // Set Class + std::string class_Name = itoa(GetClass()); + std::string class_List[] = { "WAR", "CLR", "PAL", "RNG", "SK", "DRU", "MNK", "BRD", "ROG", "SHM", "NEC", "WIZ", "MAG", "ENC", "BST", "BER" }; + + if(GetClass() < 17 && GetClass() > 0) { class_Name = class_List[GetClass()-1]; } + + // Race + std::string race_Name = itoa(GetRace()); + switch(GetRace()) + { + case 1: race_Name = "Human"; break; + case 2: race_Name = "Barbarian"; break; + case 3: race_Name = "Erudite"; break; + case 4: race_Name = "Wood Elf"; break; + case 5: race_Name = "High Elf"; break; + case 6: race_Name = "Dark Elf"; break; + case 7: race_Name = "Half Elf"; break; + case 8: race_Name = "Dwarf"; break; + case 9: race_Name = "Troll"; break; + case 10: race_Name = "Ogre"; break; + case 11: race_Name = "Halfing"; break; + case 12: race_Name = "Gnome"; break; + case 128: race_Name = "Iksar"; break; + case 130: race_Name = "Vah Shir"; break; + case 330: race_Name = "Froglok"; break; + case 522: race_Name = "Drakkin"; break; + default: break; + } + /*########################################################## + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + H/M/E String + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ##########################################################*/ + std::string HME_row = ""; + //Loop Variables + /*===========================*/ + std::string cur_field = ""; + std::string total_field = ""; + std::string cur_name = ""; + std::string cur_spacing = ""; + std::string cur_color = ""; + + int hme_rows = 3; // Rows in display + int max_HME_value_len = 9; // 9 digits in the displayed value + + for(int hme_row_counter = 0; hme_row_counter < hme_rows; hme_row_counter++) + { + switch(hme_row_counter) { + case 0: { + cur_name = " H: "; + cur_field = itoa(GetHP()); + total_field = itoa(GetMaxHP()); + break; + } + case 1: { + if(CalcMaxMana() > 0) { + cur_name = " M: "; + cur_field = itoa(GetMana()); + total_field = itoa(CalcMaxMana()); + } + else { continue; } + + break; + } + case 2: { + cur_name = " E: "; + cur_field = itoa(GetEndurance()); + total_field = itoa(GetMaxEndurance()); + break; + } + default: { break; } + } + if(cur_field.compare(total_field) == 0) { cur_color = bright_green; } + else { cur_color = bright_red; } + + cur_spacing.clear(); + for(int a = cur_field.size(); a < max_HME_value_len; a++) { cur_spacing += " ."; } + + HME_row += indM + cur_name + cur_spacing + cur_color + cur_field + " / " + total_field + "
"; + } + /*########################################################## + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Regen String + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ##########################################################*/ + std::string regen_string; + //Loop Variables + /*===========================*/ + std::string regen_row_header = ""; + std::string regen_row_color = ""; + std::string base_regen_field = ""; + std::string base_regen_spacing = ""; + std::string item_regen_field = ""; + std::string item_regen_spacing = ""; + std::string cap_regen_field = ""; + std::string cap_regen_spacing = ""; + std::string spell_regen_field = ""; + std::string spell_regen_spacing = ""; + std::string aa_regen_field = ""; + std::string aa_regen_spacing = ""; + std::string total_regen_field = ""; + int regen_rows = 3; // Number of rows + int max_regen_value_len = 5; // 5 digits in the displayed value(larger values will not get cut off, this is just a baseline) + + for(int regen_row_counter = 0; regen_row_counter < regen_rows; regen_row_counter++) + { + switch(regen_row_counter) + { + case 0: { + regen_row_header = "H: "; + regen_row_color = color_red; + + base_regen_field = itoa(LevelRegen()); + item_regen_field = itoa(itembonuses.HPRegen); + cap_regen_field = itoa(CalcHPRegenCap()); + spell_regen_field = itoa(spellbonuses.HPRegen); + aa_regen_field = itoa(aabonuses.HPRegen); + total_regen_field = itoa(CalcHPRegen()); + break; + } + case 1: { + if(CalcMaxMana() > 0) { + regen_row_header = "M: "; + regen_row_color = color_blue; + + base_regen_field = itoa(CalcBaseManaRegen()); + item_regen_field = itoa(itembonuses.ManaRegen); + cap_regen_field = itoa(CalcManaRegenCap()); + spell_regen_field = itoa(spellbonuses.ManaRegen); + aa_regen_field = itoa(aabonuses.ManaRegen); + total_regen_field = itoa(CalcManaRegen()); + } + else { continue; } + break; + } + case 2: { + regen_row_header = "E: "; + regen_row_color = color_green; + + base_regen_field = itoa(((GetLevel() * 4 / 10) + 2)); + item_regen_field = itoa(itembonuses.EnduranceRegen); + cap_regen_field = itoa(CalcEnduranceRegenCap()); + spell_regen_field = itoa(spellbonuses.EnduranceRegen); + aa_regen_field = itoa(aabonuses.EnduranceRegen); + total_regen_field = itoa(CalcEnduranceRegen()); + break; + } + default: { break; } + } + + base_regen_spacing.clear(); + item_regen_spacing.clear(); + cap_regen_spacing.clear(); + spell_regen_spacing.clear(); + aa_regen_spacing.clear(); + + for(int b = base_regen_field.size(); b < max_regen_value_len; b++) { base_regen_spacing += " ."; } + for(int b = item_regen_field.size(); b < max_regen_value_len; b++) { item_regen_spacing += " ."; } + for(int b = cap_regen_field.size(); b < max_regen_value_len; b++) { cap_regen_spacing += " ."; } + for(int b = spell_regen_field.size(); b < max_regen_value_len; b++) { spell_regen_spacing += " ."; } + for(int b = aa_regen_field.size(); b < max_regen_value_len; b++) { aa_regen_spacing += " ."; } + + regen_string += indS + regen_row_color + regen_row_header + base_regen_spacing + base_regen_field; + regen_string += div + item_regen_spacing + item_regen_field + " (" + cap_regen_field; + regen_string += ") " + cap_regen_spacing + div + spell_regen_spacing + spell_regen_field; + regen_string += div + aa_regen_spacing + aa_regen_field + div + total_regen_field + "

"; + } + /*########################################################## + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Stat String + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ##########################################################*/ + std::string stat_field = ""; + //Loop Variables + /*===========================*/ + //first field(stat) + std::string a_stat = "";; + std::string a_stat_name = ""; + std::string a_stat_spacing = ""; + //second field(heroic stat) + std::string h_stat = ""; + std::string h_stat_spacing = ""; + //third field(resist) + std::string a_resist = ""; + std::string a_resist_name = ""; + std::string a_resist_spacing = ""; + //fourth field(heroic resist) + std::string h_resist_field = ""; + + int stat_rows = 7; // Number of rows + int max_stat_value_len = 3; // 3 digits in the displayed value + + for(int stat_row_counter = 0; stat_row_counter < stat_rows; stat_row_counter++) + { + switch(stat_row_counter) { + case 0: { + a_stat_name = " STR: "; + a_resist_name = "MR: "; + a_stat = itoa(GetSTR()); + h_stat = itoa(GetHeroicSTR()); + a_resist = itoa(GetMR()); + h_resist_field = itoa(GetHeroicMR()); + break; + } + case 1: { + a_stat_name = " STA: "; + a_resist_name = "CR: "; + a_stat = itoa(GetSTA()); + h_stat = itoa(GetHeroicSTA()); + a_resist = itoa(GetCR()); + h_resist_field = itoa(GetHeroicCR()); + break; + } + case 2: { + a_stat_name = " AGI : "; + a_resist_name = "FR: "; + a_stat = itoa(GetAGI()); + h_stat = itoa(GetHeroicAGI()); + a_resist = itoa(GetFR()); + h_resist_field = itoa(GetHeroicFR()); + break; + } + case 3: { + a_stat_name = " DEX: "; + a_resist_name = "PR: "; + a_stat = itoa(GetDEX()); + h_stat = itoa(GetHeroicDEX()); + a_resist = itoa(GetPR()); + h_resist_field = itoa(GetHeroicPR()); + break; + } + case 4: { + a_stat_name = " INT : "; + a_resist_name = "DR: "; + a_stat = itoa(GetINT()); + h_stat = itoa(GetHeroicINT()); + a_resist = itoa(GetDR()); + h_resist_field = itoa(GetHeroicDR()); + break; + } + case 5: { + a_stat_name = " WIS: "; + a_resist_name = "Cp: "; + a_stat = itoa(GetWIS()); + h_stat = itoa(GetHeroicWIS()); + a_resist = itoa(GetCorrup()); + h_resist_field = itoa(GetHeroicCorrup()); + break; + } + case 6: { + a_stat_name = " CHA: "; + a_stat = itoa(GetCHA()); + h_stat = itoa(GetHeroicCHA()); + break; + } + default: { break; } + } + + a_stat_spacing.clear(); + h_stat_spacing.clear(); + a_resist_spacing.clear(); + + for(int a = a_stat.size(); a < max_stat_value_len; a++) { a_stat_spacing += " . "; } + for(int h = h_stat.size(); h < 20; h++) { h_stat_spacing += " . "; } + for(int h = a_resist.size(); h < max_stat_value_len; h++) { a_resist_spacing += " . "; } + + stat_field += indP + a_stat_name + a_stat_spacing + a_stat + heroic_color + h_stat + "
"; + if(stat_row_counter < 6) { + stat_field += h_stat_spacing + a_resist_name + a_resist_spacing + a_resist + heroic_color + h_resist_field + "

"; + } + } + /*########################################################## + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + Mod2 String + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + ##########################################################*/ + std::string mod2_field = ""; + //Loop Variables + /*===========================*/ + std::string mod2a = ""; + std::string mod2a_name = ""; + std::string mod2a_spacing = ""; + std::string mod2a_cap = ""; + std::string mod_row_spacing = ""; + std::string mod2b = ""; + std::string mod2b_name = ""; + std::string mod2b_spacing = ""; + std::string mod2b_cap = ""; + int mod2a_space_count; + int mod2b_space_count; + + int mod2_rows = 4; + int max_mod2_value_len = 3; // 3 digits in the displayed value + + for(int mod2_row_counter = 0; mod2_row_counter < mod2_rows; mod2_row_counter++) + { + switch (mod2_row_counter) + { + case 0: { + mod2a_name = "Avoidance: "; + mod2b_name = "Combat Effects: "; + mod2a = itoa(GetAvoidance()); + mod2a_cap = itoa(RuleI(Character, ItemAvoidanceCap)); + mod2b = itoa(GetCombatEffects()); + mod2b_cap = itoa(RuleI(Character, ItemCombatEffectsCap)); + mod2a_space_count = 2; + mod2b_space_count = 0; + break; + } + case 1: { + mod2a_name = "Accuracy: "; + mod2b_name = "Strike Through: "; + mod2a = itoa(GetAccuracy()); + mod2a_cap = itoa(RuleI(Character, ItemAccuracyCap)); + mod2b = itoa(GetStrikeThrough()); + mod2b_cap = itoa(RuleI(Character, ItemStrikethroughCap)); + mod2a_space_count = 3; + mod2b_space_count = 1; + break; + } + case 2: { + mod2a_name = "Shielding: "; + mod2b_name = "Spell Shielding: "; + mod2a = itoa(GetShielding()); + mod2a_cap = itoa(RuleI(Character, ItemShieldingCap)); + mod2b = itoa(GetSpellShield()); + mod2b_cap = itoa(RuleI(Character, ItemSpellShieldingCap)); + mod2a_space_count = 2; + mod2b_space_count = 1; + break; + } + case 3: { + mod2a_name = "Stun Resist: "; + mod2b_name = "DoT Shielding: "; + mod2a = itoa(GetStunResist()); + mod2a_cap = itoa(RuleI(Character, ItemStunResistCap)); + mod2b = itoa(GetDoTShield()); + mod2b_cap = itoa(RuleI(Character, ItemDoTShieldingCap)); + mod2a_space_count = 0; + mod2b_space_count = 2; + break; + } + } + + mod2a_spacing.clear(); + mod_row_spacing.clear(); + mod2b_spacing.clear(); + + for(int a = mod2a.size(); a < (max_mod2_value_len + mod2a_space_count); a++) { mod2a_spacing += " . "; } + for(int a = mod2a_cap.size(); a < 6 ; a++) { mod_row_spacing += " . "; } + for(int a = mod2b.size(); a < (max_mod2_value_len + mod2b_space_count); a++) { mod2b_spacing += " . "; } + + mod2_field += indP + mod2a_name + mod2a_spacing + mod2a + " / " + mod2a_cap + mod_row_spacing; + mod2_field += mod2b_name + mod2b_spacing + mod2b + " / " + mod2b_cap + "
"; + } + + uint32 rune_number = 0; + uint32 magic_rune_number = 0; + uint32 buff_count = GetMaxTotalSlots(); + for (int i=0; i < buff_count; i++) { + if (buffs[i].spellid != SPELL_UNKNOWN) { + if ((HasRune() || HasPartialMeleeRune()) && buffs[i].melee_rune > 0) { rune_number += buffs[i].melee_rune; } + + if ((HasSpellRune() || HasPartialSpellRune()) && buffs[i].magic_rune > 0) { magic_rune_number += buffs[i].magic_rune; } + } + } + + int shield_ac = 0; + GetRawACNoShield(shield_ac); + + std::string skill_list[] = { + "1H Blunt","1H Slashing","2H Blunt","2H Slashing","Abjuration","Alteration","Apply Poison","Archery","Backstab","Bind Wound","Bash","Block","Brass Instruments","Channeling","Conjuration", + "Defense","Disarm","Disarm Traps","Divination","Dodge","Double Attack","Dragon Punch","Dual Wield","Eagle Strike","Evocation","Feign Death","Flying Kick","Forage","Hand To Hand","Hide","Kick", + "Meditate","Mend","Offense","Parry","Pick Lock","Piercing","Riposte","Round Kick","Safe Fall","Sense Heading","Singing","Sneak","Specialize Abjuration","Specialize Alteration","Specialize Conjuration", + "Specialize Divination","Specialize Evocation","Pick Pockets","Stringed_Instruments","Swimming","Throwing","Tiger Claw","Tracking","Wind Instruments","Fishing","Make Poison","Tinkering","Research","Alchemy", + "Baking","Tailoring","Sense Traps","Blacksmithing","Fletching","Brewing","Alcohol_Tolerance","Begging","Jewelry Making","Pottery","Percussion Instruments","Intimidation","Berserking","Taunt","Frenzy" + }; + + std::string skill_mods = ""; + for(int j = 0; j <= HIGHEST_SKILL; j++) { + if(itembonuses.skillmod[j] > 0) + skill_mods += indP + skill_list[j] + " : +" + itoa(itembonuses.skillmod[j]) + "%
"; + else if(itembonuses.skillmod[j] < 0) + skill_mods += indP + skill_list[j] + " : -" + itoa(itembonuses.skillmod[j]) + "%
"; + } + + std::string skill_dmgs = ""; + for(int j = 0; j <= HIGHEST_SKILL; j++) { + if((itembonuses.SkillDamageAmount[j] + spellbonuses.SkillDamageAmount[j]) > 0) + skill_dmgs += indP + skill_list[j] + " : +" + itoa(itembonuses.SkillDamageAmount[j] + spellbonuses.SkillDamageAmount[j]) + "
"; + else if((itembonuses.SkillDamageAmount[j] + spellbonuses.SkillDamageAmount[j]) < 0) + skill_dmgs += indP + skill_list[j] + " : -" + itoa(itembonuses.SkillDamageAmount[j] + spellbonuses.SkillDamageAmount[j]) + "
"; + } + + std::string faction_item_string = ""; + char faction_buf[256]; + + for(std::map ::iterator iter = item_faction_bonuses.begin(); + iter != item_faction_bonuses.end(); + iter++) + { + memset(&faction_buf, 0, sizeof(faction_buf)); + + if(!database.GetFactionName((int32)((*iter).first), faction_buf, sizeof(faction_buf))) + strcpy(faction_buf, "Not in DB"); + + if((*iter).second > 0) { + faction_item_string += indP + faction_buf + " : +" + itoa((*iter).second) + "
"; + } + else if((*iter).second < 0) { + faction_item_string += indP + faction_buf + " : -" + itoa((*iter).second) + "
"; + } + } + + std::string bard_info = ""; + if(GetClass() == BARD) { + bard_info = indP + "Singing: " + itoa(GetSingMod()) + "
" + + indP + "Brass: " + itoa(GetBrassMod()) + "
" + + indP + "String: " + itoa(GetStringMod()) + "
" + + indP + "Percussion: " + itoa(GetPercMod()) + "
" + + indP + "Wind: " + itoa(GetWindMod()) + "
"; + } + + std::string final_stats = "" + + /* C/L/R */ indP + "Class: " + class_Name + indS + "Level: " + itoa(GetLevel()) + indS + "Race: " + race_Name + "
" + + /* Runes */ indP + "Rune: " + itoa(rune_number) + indL + indS + "Spell Rune: " + itoa(magic_rune_number) + "
" + + /* HP/M/E */ HME_row + + /* DS */ indP + "DS: " + itoa(itembonuses.DamageShield + spellbonuses.DamageShield*-1) + " (Spell: " + itoa(spellbonuses.DamageShield*-1) + " + Item: " + itoa(itembonuses.DamageShield) + " / " + itoa(RuleI(Character, ItemDamageShieldCap)) + ")
" + + /* Atk */ indP + "ATK: " + itoa(GetTotalATK()) + "
" + + /* Atk2 */ indP + "- Base: " + itoa(GetATKRating()) + " | Item: " + itoa(itembonuses.ATK) + " (" + itoa(RuleI(Character, ItemATKCap)) + ")~Used: " + itoa((itembonuses.ATK * 1.342)) + " | Spell: " + itoa(spellbonuses.ATK) + "
" + + /* AC */ indP + "AC: " + itoa(CalcAC()) + "
" + + /* AC2 */ indP + "- Mit: " + itoa(GetACMit()) + " | Avoid: " + itoa(GetACAvoid()) + " | Spell: " + itoa(spellbonuses.AC) + " | Shield: " + itoa(shield_ac) + "
" + + /* Haste */ indP + "Haste: " + itoa(GetHaste()) + "
" + + /* Haste2 */ indP + " - Item: " + itoa(itembonuses.haste) + " + Spell: " + itoa(spellbonuses.haste + spellbonuses.hastetype2) + " (Cap: " + itoa(RuleI(Character, HasteCap)) + ") | Over: " + itoa(spellbonuses.hastetype3 + ExtraHaste) + "

" + + /* RegenLbl */ indL + indS + "Regen
" + indS + indP + indP + " Base | Items (Cap) " + indP + " | Spell | A.A.s | Total
" + + /* Regen */ regen_string + "
" + + /* Stats */ stat_field + "

" + + /* Mod2s */ mod2_field + "
" + + /* HealAmt */ indP + "Heal Amount: " + itoa(GetHealAmt()) + " / " + itoa(RuleI(Character, ItemHealAmtCap)) + "
" + + /* SpellDmg*/ indP + "Spell Dmg: " + itoa(GetSpellDmg()) + " / " + itoa(RuleI(Character, ItemSpellDmgCap)) + "
" + + /* Clair */ indP + "Clairvoyance: " + itoa(GetClair()) + " / " + itoa(RuleI(Character, ItemClairvoyanceCap)) + "
" + + /* DSMit */ indP + "Dmg Shld Mit: " + itoa(GetDSMit()) + " / " + itoa(RuleI(Character, ItemDSMitigationCap)) + "

"; + if(GetClass() == BARD) + final_stats += bard_info + "
"; + if(skill_mods.size() > 0) + final_stats += skill_mods + "
"; + if(skill_dmgs.size() > 0) + final_stats += skill_dmgs + "
"; + if(faction_item_string.size() > 0) + final_stats += faction_item_string; + + + if(use_window) { + if(final_stats.size() < 4096) + { + uint32 Buttons = (client->GetClientVersion() < EQClientSoD) ? 0 : 1; + client->SendWindow(0, POPUPID_UPDATE_SHOWSTATSWINDOW, Buttons, "Cancel", "Update", 0, 1, this, "", "%s", final_stats.c_str()); + goto Extra_Info; + } + else { + client->Message(15, "The window has exceeded its character limit, displaying stats to chat window:"); + } + } + + client->Message(15, "~~~~~ %s %s ~~~~~", GetCleanName(), GetLastName()); + client->Message(0, " Level: %i Class: %i Race: %i DS: %i/%i Size: %1.1f Weight: %.1f/%d ", GetLevel(), GetClass(), GetRace(), GetDS(), RuleI(Character, ItemDamageShieldCap), GetSize(), (float)CalcCurrentWeight() / 10.0f, GetSTR()); + client->Message(0, " HP: %i/%i HP Regen: %i/%i",GetHP(), GetMaxHP(), CalcHPRegen(), CalcHPRegenCap()); + client->Message(0, " AC: %i ( Mit.: %i + Avoid.: %i + Spell: %i ) | Shield AC: %i", CalcAC(), GetACMit(), GetACAvoid(), spellbonuses.AC, shield_ac); + if(CalcMaxMana() > 0) + client->Message(0, " Mana: %i/%i Mana Regen: %i/%i", GetMana(), GetMaxMana(), CalcManaRegen(), CalcManaRegenCap()); + client->Message(0, " End.: %i/%i End. Regen: %i/%i",GetEndurance(), GetMaxEndurance(), CalcEnduranceRegen(), CalcEnduranceRegenCap()); + client->Message(0, " ATK: %i Worn/Spell ATK %i/%i Server Side ATK: %i", GetTotalATK(), RuleI(Character, ItemATKCap), GetATKBonus(), GetATK()); + client->Message(0, " Haste: %i / %i (Item: %i + Spell: %i + Over: %i)", GetHaste(), RuleI(Character, HasteCap), itembonuses.haste, spellbonuses.haste + spellbonuses.hastetype2, spellbonuses.hastetype3 + ExtraHaste); + client->Message(0, " STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA()); + client->Message(0, " hSTR: %i hSTA: %i hDEX: %i hAGI: %i hINT: %i hWIS: %i hCHA: %i", GetHeroicSTR(), GetHeroicSTA(), GetHeroicDEX(), GetHeroicAGI(), GetHeroicINT(), GetHeroicWIS(), GetHeroicCHA()); + client->Message(0, " MR: %i PR: %i FR: %i CR: %i DR: %i Corruption: %i", GetMR(), GetPR(), GetFR(), GetCR(), GetDR(), GetCorrup()); + client->Message(0, " hMR: %i hPR: %i hFR: %i hCR: %i hDR: %i hCorruption: %i", GetHeroicMR(), GetHeroicPR(), GetHeroicFR(), GetHeroicCR(), GetHeroicDR(), GetHeroicCorrup()); + client->Message(0, " Shielding: %i Spell Shield: %i DoT Shielding: %i Stun Resist: %i Strikethrough: %i Avoidance: %i Accuracy: %i Combat Effects: %i", GetShielding(), GetSpellShield(), GetDoTShield(), GetStunResist(), GetStrikeThrough(), GetAvoidance(), GetAccuracy(), GetCombatEffects()); + client->Message(0, " Heal Amt.: %i Spell Dmg.: %i Clairvoyance: %i DS Mitigation: %i", GetHealAmt(), GetSpellDmg(), GetClair(), GetDSMit()); + if(GetClass() == BARD) + client->Message(0, " Singing: %i Brass: %i String: %i Percussion: %i Wind: %i", GetSingMod(), GetBrassMod(), GetStringMod(), GetPercMod(), GetWindMod()); + + Extra_Info: + + client->Message(0, " BaseRace: %i Gender: %i BaseGender: %i Texture: %i HelmTexture: %i", GetBaseRace(), GetGender(), GetBaseGender(), GetTexture(), GetHelmTexture()); + if (client->Admin() >= 100) { + client->Message(0, " CharID: %i EntityID: %i PetID: %i OwnerID: %i AIControlled: %i Targetted: %i", CharacterID(), GetID(), GetPetID(), GetOwnerID(), IsAIControlled(), targeted); + } +} + +void Client::SendAltCurrencies() { + if(GetClientVersion() >= EQClientSoF) { + uint32 count = zone->AlternateCurrencies.size(); + if(count == 0) { + return; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, + sizeof(AltCurrencyPopulate_Struct) + sizeof(AltCurrencyPopulateEntry_Struct) * count); + AltCurrencyPopulate_Struct *altc = (AltCurrencyPopulate_Struct*)outapp->pBuffer; + altc->opcode = ALT_CURRENCY_OP_POPULATE; + altc->count = count; + + uint32 i = 0; + list::iterator iter = zone->AlternateCurrencies.begin(); + while(iter != zone->AlternateCurrencies.end()) { + const Item_Struct* item = database.GetItem((*iter).item_id); + altc->entries[i].currency_number = (*iter).id; + altc->entries[i].unknown00 = 1; + altc->entries[i].currency_number2 = (*iter).id; + altc->entries[i].item_id = (*iter).item_id; + if(item) { + altc->entries[i].item_icon = item->Icon; + altc->entries[i].stack_size = item->StackSize; + } else { + altc->entries[i].item_icon = 1000; + altc->entries[i].stack_size = 1000; + } + i++; + iter++; + } + + FastQueuePacket(&outapp); + } +} + +void Client::SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount) +{ + alternate_currency[currency_id] = new_amount; + database.UpdateAltCurrencyValue(CharacterID(), currency_id, new_amount); + SendAlternateCurrencyValue(currency_id); +} + +void Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount) +{ + if(amount == 0) { + return; + } + + int new_value = 0; + std::map::iterator iter = alternate_currency.find(currency_id); + if(iter == alternate_currency.end()) { + new_value = amount; + } else { + new_value = (*iter).second + amount; + } + + if(new_value < 0) { + alternate_currency[currency_id] = 0; + database.UpdateAltCurrencyValue(CharacterID(), currency_id, 0); + } else { + alternate_currency[currency_id] = new_value; + database.UpdateAltCurrencyValue(CharacterID(), currency_id, new_value); + } + SendAlternateCurrencyValue(currency_id); +} + +void Client::SendAlternateCurrencyValues() +{ + list::iterator iter = zone->AlternateCurrencies.begin(); + while(iter != zone->AlternateCurrencies.end()) { + SendAlternateCurrencyValue((*iter).id, false); + iter++; + } +} + +void Client::SendAlternateCurrencyValue(uint32 currency_id, bool send_if_null) +{ + uint32 value = GetAlternateCurrencyValue(currency_id); + if(value > 0 || (value == 0 && send_if_null)) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(AltCurrencyUpdate_Struct)); + AltCurrencyUpdate_Struct *update = (AltCurrencyUpdate_Struct*)outapp->pBuffer; + update->opcode = 7; + strcpy(update->name, GetName()); + update->currency_number = currency_id; + update->amount = value; + update->unknown072 = 1; + FastQueuePacket(&outapp); + } +} + +uint32 Client::GetAlternateCurrencyValue(uint32 currency_id) const +{ + std::map::const_iterator iter = alternate_currency.find(currency_id); + if(iter == alternate_currency.end()) { + return 0; + } else { + return (*iter).second; + } +} + +void Client::OpenLFGuildWindow() +{ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, 8); + + outapp->WriteUInt32(6); + + FastQueuePacket(&outapp); +} + +bool Client::IsXTarget(const Mob *m) const +{ + if(!XTargettingAvailable() || !m || (m->GetID() == 0)) + return false; + + for(int i = 0; i < GetMaxXTargets(); ++i) + { + if(XTargets[i].ID == m->GetID()) + return true; + } + return false; +} + +bool Client::IsClientXTarget(const Client *c) const +{ + if(!XTargettingAvailable() || !c) + return false; + + for(int i = 0; i < GetMaxXTargets(); ++i) + { + if(!strcasecmp(XTargets[i].Name, c->GetName())) + return true; + } + return false; +} + + +void Client::UpdateClientXTarget(Client *c) +{ + if(!XTargettingAvailable() || !c) + return; + + for(int i = 0; i < GetMaxXTargets(); ++i) + { + if(!strcasecmp(XTargets[i].Name, c->GetName())) + { + XTargets[i].ID = c->GetID(); + SendXTargetPacket(i, c); + } + } +} + +void Client::AddAutoXTarget(Mob *m) +{ + if(!XTargettingAvailable() || !XTargetAutoAddHaters) + return; + + if(IsXTarget(m)) + return; + + for(int i = 0; i < GetMaxXTargets(); ++i) + { + if((XTargets[i].Type == Auto) && (XTargets[i].ID == 0)) + { + XTargets[i].ID = m->GetID(); + SendXTargetPacket(i, m); + break; + } + } +} + +void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots) +{ + if(!XTargettingAvailable()) + return; + + bool HadFreeAutoSlotsBefore = false; + + int FreedAutoSlots = 0; + + if(m->GetID() == 0) + return; + + for(int i = 0; i < GetMaxXTargets(); ++i) + { + if(OnlyAutoSlots && (XTargets[i].Type !=Auto)) + continue; + + if(XTargets[i].ID == m->GetID()) + { + if(XTargets[i].Type == CurrentTargetNPC) + XTargets[i].Type = Auto; + + if(XTargets[i].Type == Auto) + ++FreedAutoSlots; + + XTargets[i].ID = 0; + + SendXTargetPacket(i, NULL); + } + else + { + if((XTargets[i].Type == Auto) && (XTargets[i].ID == 0)) + HadFreeAutoSlotsBefore = true; + } + } + // If there are more mobs aggro on us than we had auto-hate slots, add one of those haters into the slot(s) we just freed up. + if(!HadFreeAutoSlotsBefore && FreedAutoSlots) + entity_list.RefreshAutoXTargets(this); +} + +void Client::UpdateXTargetType(XTargetType Type, Mob *m, const char *Name) +{ + if(!XTargettingAvailable()) + return; + + for(int i = 0; i < GetMaxXTargets(); ++i) + { + if(XTargets[i].Type == Type) + { + if(m) + XTargets[i].ID = m->GetID(); + else + XTargets[i].ID = 0; + + if(Name) + strncpy(XTargets[i].Name, Name, 64); + + SendXTargetPacket(i, m); + } + } +} + +void Client::SendXTargetPacket(uint32 Slot, Mob *m) +{ + if(!XTargettingAvailable()) + return; + + uint32 PacketSize = 18; + + if(m) + PacketSize += strlen(m->GetCleanName()); + else + { + PacketSize += strlen(XTargets[Slot].Name); + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_XTargetResponse, PacketSize); + outapp->WriteUInt32(GetMaxXTargets()); + outapp->WriteUInt32(1); + outapp->WriteUInt32(Slot); + if(m) + { + outapp->WriteUInt8(1); + } + else + { + if(strlen(XTargets[Slot].Name) && ((XTargets[Slot].Type == CurrentTargetPC) || + (XTargets[Slot].Type == GroupTank) || + (XTargets[Slot].Type == GroupAssist) || + (XTargets[Slot].Type == Puller) || + (XTargets[Slot].Type == RaidAssist1) || + (XTargets[Slot].Type == RaidAssist2) || + (XTargets[Slot].Type == RaidAssist3))) + { + outapp->WriteUInt8(2); + } + else + { + outapp->WriteUInt8(0); + } + } + outapp->WriteUInt32(XTargets[Slot].ID); + outapp->WriteString(m ? m->GetCleanName() : XTargets[Slot].Name); + FastQueuePacket(&outapp); +} + +void Client::RemoveGroupXTargets() +{ + if(!XTargettingAvailable()) + return; + + for(int i = 0; i < GetMaxXTargets(); ++i) + { + if((XTargets[i].Type == GroupTank) || + (XTargets[i].Type == GroupAssist) || + (XTargets[i].Type == Puller) || + (XTargets[i].Type == RaidAssist1) || + (XTargets[i].Type == RaidAssist2) || + (XTargets[i].Type == RaidAssist3) || + (XTargets[i].Type == GroupMarkTarget1) || + (XTargets[i].Type == GroupMarkTarget2) || + (XTargets[i].Type == GroupMarkTarget3)) + { + XTargets[i].ID = 0; + XTargets[i].Name[0] = 0; + SendXTargetPacket(i, NULL); + } + } +} + +void Client::ShowXTargets(Client *c) +{ + if(!c) + return; + + for(int i = 0; i < GetMaxXTargets(); ++i) + c->Message(0, "Xtarget Slot: %i, Type: %2i, ID: %4i, Name: %s", i, XTargets[i].Type, XTargets[i].ID, XTargets[i].Name); +} + +void Client::SetMaxXTargets(uint8 NewMax) +{ + if(!XTargettingAvailable()) + return; + + if(NewMax > XTARGET_HARDCAP) + return; + + MaxXTargets = NewMax; + + Save(0); + + for(int i = MaxXTargets; i < XTARGET_HARDCAP; ++i) + { + XTargets[i].Type = Auto; + XTargets[i].ID = 0; + XTargets[i].Name[0] = 0; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_XTargetResponse, 8); + outapp->WriteUInt32(GetMaxXTargets()); + outapp->WriteUInt32(0); + FastQueuePacket(&outapp); +} + +char* Client::GetRacePlural(Client* client) { + + switch (client->CastToMob()->GetRace()) { + case HUMAN: + return "Humans"; break; + case BARBARIAN: + return "Barbarians"; break; + case ERUDITE: + return "Erudites"; break; + case WOOD_ELF: + return "Wood Elves"; break; + case HIGH_ELF: + return "High Elves"; break; + case DARK_ELF: + return "Dark Elves"; break; + case HALF_ELF: + return "Half Elves"; break; + case DWARF: + return "Dwarves"; break; + case TROLL: + return "Trolls"; break; + case OGRE: + return "Ogres"; break; + case HALFLING: + return "Halflings"; break; + case GNOME: + return "Gnomes"; break; + case IKSAR: + return "Iksar"; break; + case VAHSHIR: + return "Vah Shir"; break; + case FROGLOK: + return "Frogloks"; break; + case DRAKKIN: + return "Drakkin"; break; + default: + return "Races"; break; + } +} + +char* Client::GetClassPlural(Client* client) { + + switch (client->CastToMob()->GetClass()) { + case WARRIOR: + return "Warriors"; break; + case CLERIC: + return "Clerics"; break; + case PALADIN: + return "Paladins"; break; + case RANGER: + return "Rangers"; break; + case SHADOWKNIGHT: + return "Shadowknights"; break; + case DRUID: + return "Druids"; break; + case MONK: + return "Monks"; break; + case BARD: + return "Bards"; break; + case ROGUE: + return "Rogues"; break; + case SHAMAN: + return "Shamen"; break; + case NECROMANCER: + return "Necromancers"; break; + case WIZARD: + return "Wizards"; break; + case MAGICIAN: + return "Magicians"; break; + case ENCHANTER: + return "Enchanters"; break; + case BEASTLORD: + return "Beastlords"; break; + case BERSERKER: + return "Berserkers"; break; + default: + return "Classes"; break; + } +} + + +void Client::SendWebLink(const char *website) +{ + if(website != 0) + { + string str = website; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Weblink, sizeof(Weblink_Struct) + str.length() + 1); + Weblink_Struct *wl = (Weblink_Struct*)outapp->pBuffer; + memcpy(wl->weblink, str.c_str(), str.length() + 1); + wl->weblink[str.length() + 1] = '\0'; + + FastQueuePacket(&outapp); + } +} + +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 altCurrentType = 19; //TODO: Implement alternate currency purchases involving mercs! + + if (GetClientVersion() >= EQClientRoF) + { + MercenaryDataUpdate_Struct* mdus = new MercenaryDataUpdate_Struct; + + MercTemplate *mercData = &zone->merc_templates[GetEPP().mercTemplateID]; + + if (mercData) + { + mdus->MercStatus = 0; + mdus->MercCount = mercCount; + if (mercCount > 0) + { + mdus->MercData = new MercenaryData_Struct[mercCount]; + mdus->MercData[i].MercID = mercData->MercTemplateID; + mdus->MercData[i].MercType = mercData->MercType; + mdus->MercData[i].MercSubType = mercData->MercSubType; + mdus->MercData[i].PurchaseCost = Merc::CalcPurchaseCost(mercData->MercTemplateID, GetLevel(), 0); + mdus->MercData[i].UpkeepCost = Merc::CalcUpkeepCost(mercData->MercTemplateID, GetLevel(), 0); + mdus->MercData[i].Status = 0; + mdus->MercData[i].AltCurrencyCost = Merc::CalcPurchaseCost(mercData->MercTemplateID, GetLevel(), altCurrentType); + mdus->MercData[i].AltCurrencyUpkeep = Merc::CalcPurchaseCost(mercData->MercTemplateID, GetLevel(), altCurrentType); + mdus->MercData[i].AltCurrencyType = altCurrentType; + mdus->MercData[i].MercUnk01 = 0; + mdus->MercData[i].TimeLeft = GetEPP().mercTimerRemaining; //GetMercTimer().GetRemainingTime(); + mdus->MercData[i].MerchantSlot = i + 1; + mdus->MercData[i].MercUnk02 = 1; + mdus->MercData[i].StanceCount = zone->merc_stance_list[mercData->MercTemplateID].size(); + mdus->MercData[i].MercUnk03 = 0; + mdus->MercData[i].MercUnk04 = 1; + strn0cpy(mdus->MercData[i].MercName, GetEPP().merc_name , sizeof(mdus->MercData[i].MercName)); + uint32 stanceindex = 0; + if (mdus->MercData[i].StanceCount != 0) + { + mdus->MercData[i].Stances = new MercenaryStance_Struct[mdus->MercData[i].StanceCount]; + list::iterator iter = zone->merc_stance_list[mercData->MercTemplateID].begin(); + while(iter != zone->merc_stance_list[mercData->MercTemplateID].end()) + { + mdus->MercData[i].Stances[stanceindex].StanceIndex = stanceindex; + mdus->MercData[i].Stances[stanceindex].Stance = (iter->StanceID); + stanceindex++; + iter++; + } + } + + mdus->MercData[i].MercUnk05 = 1; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, 1); //Packet sizes are handled by the encoder. + outapp->pBuffer = (unsigned char*)mdus; + //DumpPacket(outapp); + FastQueuePacket(&outapp); + } + } + } + else + { + MercenaryMerchantList_Struct* mml = new MercenaryMerchantList_Struct; + MercTemplate *mercData = &zone->merc_templates[GetEPP().mercTemplateID]; + + if(mercData) + { + if(mercTypeCount > 0) + { + mml->MercTypeCount = mercTypeCount; //We only should have one merc entry. + mml->MercGrades = new MercenaryGrade_Struct[mercTypeCount]; // DBStringID for Type + } + mml->MercCount = mercCount; + if(mercCount > 0) + { + mml->Mercs = new MercenaryListEntry_Struct[mercCount]; + mml->Mercs[i].MercID = mercData->MercTemplateID; + mml->Mercs[i].MercType = mercData->MercType; + mml->Mercs[i].MercSubType = mercData->MercSubType; + mml->Mercs[i].PurchaseCost = Merc::CalcPurchaseCost(mercData->MercTemplateID, GetLevel(), 0); + mml->Mercs[i].UpkeepCost = Merc::CalcUpkeepCost(mercData->MercTemplateID, GetLevel(), 0); + mml->Mercs[i].Status = 0; + mml->Mercs[i].AltCurrencyCost = Merc::CalcPurchaseCost(mercData->MercTemplateID, GetLevel(), altCurrentType); + mml->Mercs[i].AltCurrencyUpkeep = Merc::CalcPurchaseCost(mercData->MercTemplateID, GetLevel(), altCurrentType); + mml->Mercs[i].AltCurrencyType = altCurrentType; + mml->Mercs[i].MercUnk01 = 0; + mml->Mercs[i].TimeLeft = GetEPP().mercTimerRemaining; + mml->Mercs[i].MerchantSlot = i + 1; + mml->Mercs[i].MercUnk02 = 1; + 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; + int stanceindex = 0; + if(mml->Mercs[i].StanceCount != 0) + { + mml->Mercs[i].Stances = new MercenaryStance_Struct[mml->Mercs[i].StanceCount]; + list::iterator iter = zone->merc_stance_list[mercData->MercTemplateID].begin(); + while(iter != zone->merc_stance_list[mercData->MercTemplateID].end()) + { + mml->Mercs[i].Stances[stanceindex].StanceIndex = stanceindex; + mml->Mercs[i].Stances[stanceindex].Stance = (iter->StanceID); + stanceindex++; + iter++; + } + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, 1); //Packet sizes are handled by the encoder. + outapp->pBuffer = (unsigned char*)mml; + // DumpPacket(outapp); + FastQueuePacket(&outapp); + } + if (GetClientVersion() == EQClientSoD) + { + SendMercMerchantResponsePacket(0); + } + } + } +} + +void Client::SendClearMercInfo() +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, sizeof(NoMercenaryHired_Struct)); + NoMercenaryHired_Struct *nmhs = (NoMercenaryHired_Struct*)outapp->pBuffer; + nmhs->MercStatus = -1; + nmhs->MercCount = 0; + nmhs->MercID = 1; + + DumpPacket(outapp); + FastQueuePacket(&outapp); +} + + +void Client::DuplicateLoreMessage(uint32 ItemID) +{ + if(!(ClientVersionBit & BIT_RoFAndLater)) + { + Message_StringID(0, PICK_LORE); + return; + } + + const Item_Struct *item = database.GetItem(ItemID); + + if(!item) + return; + + Message_StringID(0, PICK_LORE, item->Name); +} + +void Client::GarbleMessage(char *message, uint8 variance) +{ + const char alpha_list[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; // only change alpha characters for now + + for (size_t i = 0; i < strlen(message); i++) { + uint8 chance = (uint8)MakeRandomInt(0, 115); // variation just over worst possible scrambling + if (isalpha(message[i]) && (chance <= variance)) { + uint8 rand_char = (uint8)MakeRandomInt(0,51); // choose a random character from the alpha list + message[i] = alpha_list[rand_char]; + } + } +} \ No newline at end of file diff --git a/zone/client.h b/zone/client.h new file mode 100644 index 000000000..ab283140b --- /dev/null +++ b/zone/client.h @@ -0,0 +1,1439 @@ +/* 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 + 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 CLIENT_H +#define CLIENT_H +class Client; + +#include "../common/timer.h" +#include "../common/ptimer.h" +#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/linked_list.h" +#include "../common/extprofile.h" +#include "zonedb.h" +#include "errno.h" +#include "../common/classes.h" +#include "../common/races.h" +#include "../common/deity.h" +#include "mob.h" +#include "npc.h" +#include "merc.h" +#include "zone.h" +#include "AA.h" +#include "../common/seperator.h" +#include "../common/Item.h" +#include "updatemgr.h" +#include "../common/guilds.h" +#include "questmgr.h" +#include +#include +#include +#include "../common/item_struct.h" +#include "QGlobals.h" + +#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; + +class CLIENTPACKET +{ +public: + CLIENTPACKET(); + ~CLIENTPACKET(); + EQApplicationPacket *app; + bool ack_req; +}; + +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 + clientMessageWhite = 7, + clientMessageWhite2 = 10, + clientMessageLightGrey = 12, + clientMessageError = 13, //red + clientMessageGreen = 14, + clientMessageYellow = 15, + clientMessageBlue = 16, + clientMessageGroup = 18, //cyan + clientMessageWhite3 = 20, +}; + +#define SPELLBAR_UNLOCK 0x2bc +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 + 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. + EvacToSafeCoords +} ZoneMode; + +typedef enum { + MQWarp, + MQWarpShadowStep, + MQWarpKnockBack, + MQWarpLight, + MQZone, + MQZoneUnknownDest, + MQGate, + MQGhost +} CheatTypes; + +typedef enum { + EQClientUnknown = 0, + EQClient62, + EQClientTitanium, + EQClientSoF, + EQClientSoD, + EQClientUnderfoot, + EQClientRoF +} EQClientVersion; + +enum { + HideCorpseNone = 0, + HideCorpseAll = 1, + HideCorpseAllButGroup = 2, + HideCorpseLooted = 3, + HideCorpseNPC = 5 +}; + +typedef enum +{ + Empty = 0, + Auto = 1, + CurrentTargetPC = 2, + CurrentTargetNPC = 3, + TargetsTarget = 4, + GroupTank = 5, + GroupTankTarget = 6, + GroupAssist = 7, + GroupAssistTarget = 8, + Puller = 9, + PullerTarget = 10, + GroupMarkTarget1 = 11, + GroupMarkTarget2 = 12, + GroupMarkTarget3 = 13, + RaidAssist1 = 14, + RaidAssist2 = 15, + RaidAssist3 = 16, + RaidAssist1Target = 17, + RaidAssist2Target = 18, + RaidAssist3Target = 19, + RaidMarkTarget1 = 20, + RaidMarkTarget2 = 21, + RaidMarkTarget3 = 22, + MyPet = 23, + MyPetTarget = 24, + MyMercenary = 25, + MyMercenaryTarget = 26 + +} XTargetType; + +struct XTarget_Struct +{ + XTargetType Type; + uint16 ID; + char Name[65]; +}; + + +const uint32 POPUPID_UPDATE_SHOWSTATSWINDOW = 1000000; + +struct ClientReward +{ + uint32 id; + uint32 amount; +}; + +class ClientFactory { +public: + Client *MakeClient(EQStream* ieqs); +}; + +class Client : public Mob +{ +public: + //pull in opcode mappings: + #include "client_packet.h" + + Client(EQStreamInterface * ieqs); + ~Client(); + + //abstract virtual function implementations requird by base abstract class + virtual void Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillType attack_skill); + virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false); + virtual bool Attack(Mob* other, int Hand = 13, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false); + 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); } + +// void Discipline(ClientDiscipline_Struct* disc_in, Mob* tar); + 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); + 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); + 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 = NULL, bool finalizer = false); + 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 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* message, const char* targetname=NULL); + 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(SkillType tradeskill); + + 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); } + 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(); + + 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 InspectMessage_Struct& GetInspectMessage() { return m_inspect_message; } + inline const InspectMessage_Struct& GetInspectMessage() const { return m_inspect_message; } + + 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); + + bool ChangeFirstName(const char* in_firstname,const char* gmname); + + void Duck(); + void Stand(); + + 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 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(); + + 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]; } + + void SendPickPocketResponse(Mob *from, uint32 amt, int type, const Item_Struct* item = NULL); + + inline const char* GetLastName() const { return lastname; } + + inline float ProximityX() const { return(proximity_x); } + inline float ProximityY() const { return(proximity_y); } + inline float ProximityZ() const { return(proximity_z); } + 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 + */ + + 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(OFFENSE)) * 9 / 10); } + inline virtual int16 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; } + + 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 + + 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; } + // 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; } + // 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 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 int16 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; } + + int32 Additional_SpellDmg(uint16 spell_id, bool bufftick = false); + int32 Additional_Heal(uint16 spell_id); + float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false); + int32 GetActSpellDamage(uint16 spell_id, int32 value); + int32 GetActSpellHealing(uint16 spell_id, int32 value); + int32 GetActSpellCost(uint16 spell_id, int32); + int32 GetActSpellDuration(uint16 spell_id, int32); + int32 GetActSpellCasttime(uint16 spell_id, int32); + int32 GetDotFocus(uint16 spell_id, int32 value); + int32 GetActDoTDamage(uint16 spell_id, int32 value); + virtual bool CheckFizzle(uint16 spell_id); + virtual int GetCurrentBuffSlots() const; + virtual int GetCurrentSongSlots() const; + virtual int GetCurrentDiscSlots() const { return 1; } + virtual int GetMaxBuffSlots() const { return 25; } + virtual int GetMaxSongSlots() const { return 12; } + virtual int GetMaxDiscSlots() const { return 1; } + virtual int GetMaxTotalSlots() const { return 38; } + virtual void InitializeBuffSlots(); + virtual void UninitializeBuffSlots(); + + 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; } + + + /*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(); + 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 + + //This calculates total Attack Rating to match very close to what the client should show + uint16 GetTotalATK(); + uint16 GetATKRating(); + //This gets the skill value of the item type equiped in the Primary Slot + uint16 GetPrimarySkillValue(); + + bool Flurry(); + bool Rampage(); + void DurationRampage(uint32 duration); + + 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(); + + 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); } + 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 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(); + + 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 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 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 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 TGB() const { return tgb; } + + void OnDisconnect(bool hard_disconnect); + + 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(SkillType 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(SkillType skill_id) const { if (skill_id <= HIGHEST_SKILL) { return(m_pp.skills[skill_id]); } return 0; } + bool HasSkill(SkillType skill_id) const; + bool CanHaveSkill(SkillType skill_id) const; + void SetSkill(SkillType skill_num, uint16 value); + void AddSkill(SkillType skillid, uint16 value); + void CheckSpecializeIncrease(uint16 spell_id); + void CheckSongSkillIncrease(uint16 spell_id); + bool CheckIncreaseSkill(SkillType 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(SkillType skillid, uint16 class_, uint16 level) const; + inline uint16 MaxSkill(SkillType skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } + uint8 SkillTrainLevel(SkillType skillid, uint16 class_); + + void TradeskillSearchResults(const char *query, unsigned long qlen, 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, SkillType tradeskill); + + void GMKill(); + 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; } + // use this one instead + void MemSpell(uint16 spell_id, int slot, bool update_client = true); + void UnmemSpell(int slot, bool update_client = true); + void UnmemSpellAll(bool update_client = true); + void ScribeSpell(uint16 spell_id, int slot, bool update_client = true); + void UnscribeSpell(int slot, bool update_client = true); + 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); + 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; } + bool LootToStack(uint32 itemid); + 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); } + EQStreamInterface* Connection() { return eqs; } +#ifdef PACKET_PROFILER + void DumpPacketProfile() { if(eqs) eqs->DumpPacketProfile(); } +#endif + uint32 GetEquipment(uint8 material_slot) const; // returns item id + uint32 GetEquipmentColor(uint8 material_slot) const; + + inline bool AutoSplitEnabled() { return m_pp.autosplit != 0; } + + void SummonHorse(uint16 spell_id); + void SetHorseId(uint16 horseid_in); + uint16 GetHorseId() const { return horseId; } + + void NPCSpawn(NPC *target_npc, const char *identifier, uint32 extra = 0); + + 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(vector &path); + + inline PTimerList &GetPTimers() { return(p_timers); } + + //AA Methods + void SendAAList(); + void ResetAA(); + void SendAA(uint32 id, int seq=1); + void SendPreviousAA(uint32 id, int seq=1); + 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; } + inline uint32 GetAAXP() const { return m_pp.expAA; } + void SendAAStats(); + void SendAATable(); + void SendAATimers(); + int GetAATimerID(aaID activate); + int CalcAAReuseTimer(const AA_DBAction *caa); + void ActivateAA(aaID activate); + void SendAATimer(uint32 ability, uint32 begin, uint32 end); + void EnableAAEffect(aaEffectType type, uint32 duration = 0); + void DisableAAEffect(aaEffectType type); + bool CheckAAEffect(aaEffectType type); + void HandleAAAction(aaID activate); + uint32 GetAA(uint32 aa_id) const; + bool SetAA(uint32 aa_id, uint32 new_value); + inline uint32 GetAAPointsSpent() { return m_pp.aapoints_spent; } + int16 CalcAAFocusEffect(focusType type, uint16 focus_spell, uint16 spell_id); + int16 CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id); + + int16 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(); + uint32 GetItemIDAt(int16 slot_id); + uint32 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); + void SummonItem(uint32 item_id, int16 charges = 0, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0, bool attuned=false, uint16 to_slot=SLOT_CURSOR); + 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; } + + 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;} + + bool CheckTradeLoreConflict(Client* other); + void LinkDead(); + void Insight(uint32 t_id); + bool CheckDoubleAttack(bool tripleAttack = false); + + //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); + virtual void ThrowingAttack(Mob* other); + 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 ChangeSQLLog(const char *file); + void LogSQL(const char *fmt, ...); + bool CanFish(); + void GoFish(); + void ForageItem(); + //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); + + bool CheckTitle(int titleset); + void EnableTitle(int titleset); + void RemoveTitle(int titleset); + +#ifdef PACKET_UPDATE_MANAGER + inline UpdateManager *GetUpdateManager() { return(&update_manager); } +#endif + void EnteringMessages(Client* client); + void SendRules(Client* client); + std::list consent_list; + + //Anti-Cheat Stuff + uint32 m_TimeSinceLastPositionCheck; + float m_DistanceSinceLastPositionCheck; + bool m_CheatDetectMoved; + void SetShadowStepExemption(bool v); + void SetKnockBackExemption(bool v); + void SetPortExemption(bool v); + void SetSenseExemption(bool v) { m_SenseExemption = v; } + const bool IsShadowStepExempted() const { return m_ShadowStepExemption; } + const bool IsKnockBackExempted() const { return m_KnockBackExemption; } + const bool IsPortExempted() const { return m_PortExemption; } + const bool IsSenseExempted() const { return m_SenseExemption; } + const bool GetGMSpeed() const { return (gmspeed > 0); } + void CheatDetected(CheatTypes CheatType, float x, float y, float z); + const bool IsMQExemptedArea(uint32 zoneID, float x, float y, float z) const; + bool CanUseReport; + + //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 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(SkillType 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; + string SacrificeCaster; + struct Translocate_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); + + 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 != NULL; } + + 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; } + inline const uint32 GetClientVersionBit() const { return ClientVersionBit; } + + /** Adventure Stuff **/ + void SendAdventureError(const char *error); + void SendAdventureDetails(); + void SendAdventureCount(uint32 count, uint32 total); + void NewAdventure(int id, int theme, const char *text, int member_count, const char *members); + bool IsOnAdventure(); + void LeaveAdventure(); + void AdventureFinish(bool win, int theme, int points); + void SetAdventureData(char *data) { adv_data = data; } + void ClearAdventureData() { safe_delete(adv_data); } + bool HasAdventureData() { return adv_data != NULL; } + void ClearCurrentAdventure(); + void PendingAdventureRequest() { adventure_request_timer = new Timer(8000); } + bool GetPendingAdventureRequest() const { return (adventure_request_timer != NULL); } + void ClearPendingAdventureRequest() { safe_delete(adventure_request_timer); } + void PendingAdventureCreate() { adventure_create_timer = new Timer(8000); } + bool GetPendingAdventureCreate() const { return (adventure_create_timer != NULL); } + void ClearPendingAdventureCreate() { safe_delete(adventure_create_timer); } + void PendingAdventureLeave() { adventure_leave_timer = new Timer(8000); } + bool GetPendingAdventureLeave() const { return (adventure_leave_timer != NULL); } + void ClearPendingAdventureLeave() { safe_delete(adventure_leave_timer); } + void PendingAdventureDoorClick() { adventure_door_timer = new Timer(8000); } + bool GetPendingAdventureDoorClick() const { return (adventure_door_timer != NULL); } + void ClearPendingAdventureDoorClick() { safe_delete(adventure_door_timer); } + void ClearPendingAdventureData(); + + int GetAggroCount(); + void IncrementAggroCount(); + void DecrementAggroCount(); + void SendPVPStats(); + void SendDisciplineTimers(); + void SendRespawnBinds(); + + uint32 GetLDoNWins() { return (m_pp.ldon_wins_guk + m_pp.ldon_wins_mir + m_pp.ldon_wins_mmc + m_pp.ldon_wins_ruj + m_pp.ldon_wins_tak); } + uint32 GetLDoNLosses() { return (m_pp.ldon_losses_guk + m_pp.ldon_losses_mir + m_pp.ldon_losses_mmc + m_pp.ldon_losses_ruj + m_pp.ldon_losses_tak); } + uint32 GetLDoNWinsTheme(uint32 t); + uint32 GetLDoNLossesTheme(uint32 t); + uint32 GetLDoNPointsTheme(uint32 t); + void UpdateLDoNWins(uint32 t, int32 n); + void UpdateLDoNLosses(uint32 t, int32 n); + void CheckLDoNHail(Mob *target); + void CheckEmoteHail(Mob *target, const char* message); + + void HandleLDoNOpen(NPC *target); + 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); + + void MarkSingleCompassLoc(float in_x, float in_y, float in_z, uint8 count=1); + + void CalcItemScale(bool login = false); + bool CalcItemScale(uint32 slot_x, uint32 slot_y, bool login = false); + void SummonAndRezzAllCorpses(); + void SummonAllCorpses(float dest_x, float dest_y, float dest_z, float dest_heading); + void DepopAllCorpses(); + void DepopPlayerCorpse(uint32 dbid); + void BuryPlayerCorpses(); + void SuspendMinion(); + void Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration); + void NotifyNewTitlesAvailable(); + void Signal(uint32 data); + Mob *GetBindSightTarget() { return bind_sight_target; } + void SetBindSightTarget(Mob *n) { bind_sight_target = n; } + const uint16 GetBoatID() const { return BoatID; } + void SendRewards(); + bool TryReward(uint32 claim_id); + QGlobalCache *GetQGlobals() { return qGlobals; } + QGlobalCache *CreateQGlobals() { qGlobals = new QGlobalCache(); return qGlobals; } + void GuildBankAck(); + void GuildBankDepositAck(bool Fail); + inline bool IsGuildBanker() { return GuildBanker; } + void ClearGuildBank(); + void SendGroupCreatePacket(); + void SendGroupLeaderChangePacket(const char *LeaderName); + void SendGroupJoinAcknowledge(); + void DoTracking(); + inline bool IsTracking() { return (TrackingID > 0); } + inline void SetPendingGuildInvitation(bool inPendingGuildInvitation) { PendingGuildInvitation = inPendingGuildInvitation; } + inline bool GetPendingGuildInvitation() { return PendingGuildInvitation; } + void LocateCorpse(); + void SendTargetCommand(uint32 EntityID); + bool MoveItemToInventory(ItemInst *BInst, bool UpdateClient = false); + void HandleRespawnFromHover(uint32 Option); + bool IsHoveringForRespawn() { return RespawnFromHoverTimer.Enabled(); } + void SetPendingRezzData(int XP, uint32 DBID, uint16 SpellID, const char *CorpseName) { PendingRezzXP = XP; PendingRezzDBID = DBID; PendingRezzSpellID = SpellID; PendingRezzCorpseName = CorpseName; } + bool IsRezzPending() { return PendingRezzSpellID > 0; } + void ClearHover(); + inline bool IsBlockedBuff(int16 SpellID) { return PlayerBlockedBuffs.find(SpellID) != PlayerBlockedBuffs.end(); } + inline bool IsBlockedPetBuff(int16 SpellID) { return PetBlockedBuffs.find(SpellID) != PetBlockedBuffs.end(); } + bool IsDraggingCorpse(const char* CorpseName); + inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); } + void DragCorpses(); + inline void ClearDraggedCorpses() { DraggedCorpses.clear(); } + void SendAltCurrencies(); + void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount); + void AddAlternateCurrencyValue(uint32 currency_id, int32 amount); + void SendAlternateCurrencyValues(); + void SendAlternateCurrencyValue(uint32 currency_id, bool send_if_null = true); + uint32 GetAlternateCurrencyValue(uint32 currency_id) const; + void OpenLFGuildWindow(); + void HandleLFGuildResponse(ServerPacket *pack); + void SendLFGuildStatus(); + void SendGuildLFGuildStatus(); + inline bool XTargettingAvailable() const { return ((ClientVersionBit & BIT_UnderfootAndLater) && RuleB(Character, EnableXTargetting)); } + inline uint8 GetMaxXTargets() const { return MaxXTargets; } + void SetMaxXTargets(uint8 NewMax); + bool IsXTarget(const Mob *m) const; + bool IsClientXTarget(const Client *c) const; + void UpdateClientXTarget(Client *c); + void UpdateXTargetType(XTargetType Type, Mob *m, const char *Name = NULL); + void AddAutoXTarget(Mob *m); + void RemoveXTarget(Mob *m, bool OnlyAutoSlots); + void SendXTargetPacket(uint32 Slot, Mob *m); + void RemoveGroupXTargets(); + void ShowXTargets(Client *c); + inline uint32 GetMercID() const { return mercid; } + void SetMercID( uint32 newmercid) { mercid = newmercid; } + Merc* GetMerc(); + void SetMerc(Merc* newmerc); + void SendMercMerchantResponsePacket(int32 response_type); + void SendMercenaryUnknownPacket(uint8 type); + void SendMercenaryUnsuspendPacket(uint8 type); + 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); + void SendMercPersonalInfo(); + void SendClearMercInfo(); + void SuspendMercCommand(); + void SpawnMercOnZone(); + void SpawnMerc(Merc* merc); + void UpdateMercTimer(); + void UpdateMercLevel(); + void CheckMercSuspendTimer(); + Timer GetMercTimer() { return merc_timer; }; + char* GetRacePlural(Client* client); + char* GetClassPlural(Client* client); + void SendWebLink(const char* website); + + bool StoreTurnInItems(Mob* with); + void DuplicateLoreMessage(uint32 ItemID); + void GarbleMessage(char *, uint8); + +protected: + friend class Mob; + void CalcItemBonuses(StatBonuses* newbon); + void AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug = false, bool isTribute = false); + int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat); + void CalcEdibleBonuses(StatBonuses* newbon); + void CalcAABonuses(StatBonuses* newbon); + void ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon); + 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); + + Mob* bind_sight_target; + + VERTEX aa_los_me; + VERTEX aa_los_them; + Mob *aa_los_them_mob; + bool los_status; + QGlobalCache *qGlobals; + + /** Adventure Variables **/ + Timer *adventure_request_timer; + Timer *adventure_create_timer; + Timer *adventure_leave_timer; + Timer *adventure_door_timer; + Timer *adventure_stats_timer; + Timer *adventure_leaderboard_timer; + int adv_requested_theme; + int adv_requested_id; + char *adv_requested_data; + int adv_requested_member_count; + char *adv_data; + +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); + + // Bandolier Methods + void CreateBandolier(const EQApplicationPacket *app); + void RemoveBandolier(const EQApplicationPacket *app); + void SetBandolier(const EQApplicationPacket *app); + + void HandleTraderPriceUpdate(const EQApplicationPacket *app); + + int16 CalcAC(); + int16 GetACMit(); + int16 GetACAvoid(); + int16 CalcATK(); + int CalcHaste(); + + int16 CalcAlcoholPhysicalEffect(); + 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(); + int32 CalcMaxHP(); + int32 CalcBaseHP(); + int32 CalcHPRegen(); + int32 CalcManaRegen(); + int32 CalcBaseManaRegen(); + uint32 GetClassHPFactor(); + void DoHPRegen(); + void DoManaRegen(); + void DoStaminaUpdate(); + void CalcRestState(); + + uint32 pLastUpdate; + uint32 pLastUpdateWZ; + uint8 playeraction; + + 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; + 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; + bool Trader; + bool Buyer; + string BuyerWelcomeMessage; + bool AbilityTimer; + int Haste; //precalced value + + 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; // 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(); + LinkedList clientpackets; + + //Zoning related stuff + void SendZoneCancel(ZoneChange_Struct *zc); + void SendZoneError(ZoneChange_Struct *zc, int8 err); + 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; + ZoneMode zone_mode; + + + 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; +#ifdef REVERSE_AGGRO + Timer scanarea_timer; +#endif + Timer tribute_timer; + +#ifdef PACKET_UPDATE_MANAGER + UpdateManager update_manager; +#endif + + 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; + + + 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 + map aa_points; + bool npcflag; + uint8 npclevel; + bool feigned; + bool zoning; + bool tgb; + bool instalog; + 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; + + set zone_flags; + + ClientTaskState *taskstate; + int TotalSecondsPlayed; + + //Anti Spam Stuff + Timer *KarmaUpdateTimer; + uint32 TotalKarma; + + Timer *GlobalChatLimiterTimer; //60 seconds + uint32 AttemptedMessages; + + EQClientVersion ClientVersion; + uint32 ClientVersionBit; + + int XPRate; + + bool m_ShadowStepExemption; + bool m_KnockBackExemption; + bool m_PortExemption; + bool m_SenseExemption; + std::map alternate_currency; + + //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 + } conn_state; + void ReportConnectingState(); + + uint8 HideCorpseMode; + bool PendingGuildInvitation; + int PendingRezzXP; + uint32 PendingRezzDBID; + uint16 PendingRezzSpellID; // Only used for resurrect while hovering. + std::string PendingRezzCorpseName; // Only used for resurrect while hovering. + + std::set PlayerBlockedBuffs; + std::set PetBlockedBuffs; + std::list DraggedCorpses; + + uint8 MaxXTargets; + bool XTargetAutoAddHaters; + + struct XTarget_Struct XTargets[XTARGET_HARDCAP]; + +}; + +#include "parser.h" +#endif diff --git a/zone/client_logs.cpp b/zone/client_logs.cpp new file mode 100644 index 000000000..3a4922bfd --- /dev/null +++ b/zone/client_logs.cpp @@ -0,0 +1,141 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" + +#ifdef CLIENT_LOGS +#include "client_logs.h" +#include "client.h" +#include + +ClientLogs client_logs; + +char ClientLogs::_buffer[MAX_CLIENT_LOG_MESSAGE_LENGTH+1]; + +void ClientLogs::subscribe(EQEMuLog::LogIDs id, Client *c) { + if(id >= EQEMuLog::MaxLogID) + return; + if(c == NULL) + return; + + //make sure they arnt allready subscribed. + + vector::iterator cur,end; + cur = entries[id].begin(); + end = entries[id].end(); + for(; cur != end; cur++) { + if(*cur == c) { + printf("%s was allready subscribed to %d\n", c->GetName(), id); + return; + } + } + + printf("%s has been subscribed to %d\n", c->GetName(), id); + entries[id].push_back(c); +} + +void ClientLogs::unsubscribe(EQEMuLog::LogIDs id, Client *c) { + if(id >= EQEMuLog::MaxLogID) + return; + if(c == NULL) + return; + + vector::iterator cur,end; + cur = entries[id].begin(); + end = entries[id].end(); + for(; cur != end; cur++) { + if(*cur == c) { + entries[id].erase(cur); + return; + } + } +} + +void ClientLogs::subscribeAll(Client *c) { + if(c == NULL) + return; + int r; + for(r = EQEMuLog::Status; r < EQEMuLog::MaxLogID; r++) { + subscribe((EQEMuLog::LogIDs)r, c); + } +} + +void ClientLogs::unsubscribeAll(Client *c) { + if(c == NULL) + return; + int r; + for(r = EQEMuLog::Status; r < EQEMuLog::MaxLogID; r++) { + unsubscribe((EQEMuLog::LogIDs)r, c); + } +} + +void ClientLogs::clear() { + int r; + for(r = EQEMuLog::Status; r < EQEMuLog::MaxLogID; r++) { + entries[r].clear(); + } +} + +void ClientLogs::msg(EQEMuLog::LogIDs id, const char *buf) { + if(id >= EQEMuLog::MaxLogID) + return; + vector::iterator cur,end; + cur = entries[id].begin(); + end = entries[id].end(); + for(; cur != end; cur++) { + if(!(*cur)->InZone()) + continue; + (*cur)->Message(CLIENT_LOG_CHANNEL, buf); + } +} + +void ClientLogs::EQEmuIO_buf(EQEMuLog::LogIDs id, const char *buf, uint8 size, uint32 count) { + if(size != 1) + return; //cannot print multibyte data + if(buf[0] == '\n' || buf[0] == '\r') + return; //skip new lines... + if(count > MAX_CLIENT_LOG_MESSAGE_LENGTH) + count = MAX_CLIENT_LOG_MESSAGE_LENGTH; + memcpy(_buffer, buf, count); + _buffer[count] = '\0'; + client_logs.msg(id, _buffer); +} + +void ClientLogs::EQEmuIO_fmt(EQEMuLog::LogIDs id, const char *fmt, va_list ap) { + if(fmt[0] == '\n' || fmt[0] == '\r') + return; //skip new lines... + vsnprintf(_buffer, MAX_CLIENT_LOG_MESSAGE_LENGTH, fmt, ap); + _buffer[MAX_CLIENT_LOG_MESSAGE_LENGTH] = '\0'; + client_logs.msg(id, _buffer); +} + +void ClientLogs::EQEmuIO_pva(EQEMuLog::LogIDs id, const char *prefix, const char *fmt, va_list ap) { + if(fmt[0] == '\n' || fmt[0] == '\r') + return; //skip new lines... + char *buf = _buffer; + int plen = snprintf(buf, MAX_CLIENT_LOG_MESSAGE_LENGTH, prefix); + buf += plen; + vsnprintf(buf, MAX_CLIENT_LOG_MESSAGE_LENGTH-plen, fmt, ap); + _buffer[MAX_CLIENT_LOG_MESSAGE_LENGTH] = '\0'; + client_logs.msg(id, _buffer); +} + +#endif //CLIENT_LOGS + + + diff --git a/zone/client_logs.h b/zone/client_logs.h new file mode 100644 index 000000000..7a16eb7d7 --- /dev/null +++ b/zone/client_logs.h @@ -0,0 +1,64 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 CLIENT_LOGS_H +#define CLIENT_LOGS_H +#include "../common/debug.h" +#include "features.h" + +#ifdef CLIENT_LOGS +#include "../common/eq_packet_structs.h" + +#define CLIENT_LOG_CHANNEL MT_Chat10Echo + +//trim messages to this length before sending to any clients +#define MAX_CLIENT_LOG_MESSAGE_LENGTH 512 + +#include +using namespace std; + +class Client; + +class ClientLogs { +public: + static void EQEmuIO_buf(EQEMuLog::LogIDs id, const char *buf, uint8 size, uint32 count); + static void EQEmuIO_fmt(EQEMuLog::LogIDs id, const char *fmt, va_list ap); + static void EQEmuIO_pva(EQEMuLog::LogIDs id, const char *prefix, const char *fmt, va_list ap); + + void subscribe(EQEMuLog::LogIDs id, Client *c); + void unsubscribe(EQEMuLog::LogIDs id, Client *c); + void subscribeAll(Client *c); + void unsubscribeAll(Client *c); + void clear(); //unsubscribes everybody + + void msg(EQEMuLog::LogIDs id, const char *buf); + +protected: + + vector entries[EQEMuLog::MaxLogID]; + + static char _buffer[MAX_CLIENT_LOG_MESSAGE_LENGTH+1]; +}; + +extern ClientLogs client_logs; + +#endif //CLIENT_LOGS +#endif + + + diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp new file mode 100644 index 000000000..81f3a35d1 --- /dev/null +++ b/zone/client_mods.cpp @@ -0,0 +1,2050 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 "masterentity.h" +#include "worldserver.h" +#include "zonedb.h" +#include "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 "StringIDs.h" +#include "NpcAI.h" + + +// Return max stat value for level +int16 Client::GetMaxStat() const { + + if((RuleI(Character, StatCap)) > 0) + return (RuleI(Character, StatCap)); + + int level = GetLevel(); + + int16 base = 0; + + if (level < 61) { + base = 255; + } + else if (GetClientVersion() >= EQClientSoF) { + base = 255 + 5 * (level - 60); + } + else if (level < 71) { + base = 255 + 5 * (level - 60); + } + else { + base = 330; + } + + return(base); +} + +int16 Client::GetMaxResist() const +{ + int level = GetLevel(); + + int16 base = 500; + + if(level > 60) + base += ((level - 60) * 5); + + return base; +} + +int16 Client::GetMaxSTR() const { + return GetMaxStat() + + itembonuses.STRCapMod + + spellbonuses.STRCapMod + + aabonuses.STRCapMod; +} +int16 Client::GetMaxSTA() const { + return GetMaxStat() + + itembonuses.STACapMod + + spellbonuses.STACapMod + + aabonuses.STACapMod; +} +int16 Client::GetMaxDEX() const { + return GetMaxStat() + + itembonuses.DEXCapMod + + spellbonuses.DEXCapMod + + aabonuses.DEXCapMod; +} +int16 Client::GetMaxAGI() const { + return GetMaxStat() + + itembonuses.AGICapMod + + spellbonuses.AGICapMod + + aabonuses.AGICapMod; +} +int16 Client::GetMaxINT() const { + return GetMaxStat() + + itembonuses.INTCapMod + + spellbonuses.INTCapMod + + aabonuses.INTCapMod; +} +int16 Client::GetMaxWIS() const { + return GetMaxStat() + + itembonuses.WISCapMod + + spellbonuses.WISCapMod + + aabonuses.WISCapMod; +} +int16 Client::GetMaxCHA() const { + return GetMaxStat() + + itembonuses.CHACapMod + + spellbonuses.CHACapMod + + aabonuses.CHACapMod; +} +int16 Client::GetMaxMR() const { + return GetMaxResist() + + itembonuses.MRCapMod + + spellbonuses.MRCapMod + + aabonuses.MRCapMod; +} +int16 Client::GetMaxPR() const { + return GetMaxResist() + + itembonuses.PRCapMod + + spellbonuses.PRCapMod + + aabonuses.PRCapMod; +} +int16 Client::GetMaxDR() const { + return GetMaxResist() + + itembonuses.DRCapMod + + spellbonuses.DRCapMod + + aabonuses.DRCapMod; +} +int16 Client::GetMaxCR() const { + return GetMaxResist() + + itembonuses.CRCapMod + + spellbonuses.CRCapMod + + aabonuses.CRCapMod; +} +int16 Client::GetMaxFR() const { + return GetMaxResist() + + itembonuses.FRCapMod + + spellbonuses.FRCapMod + + aabonuses.FRCapMod; +} +int16 Client::GetMaxCorrup() const { + return GetMaxResist() + + itembonuses.CorrupCapMod + + spellbonuses.CorrupCapMod + + aabonuses.CorrupCapMod; +} +int32 Client::LevelRegen() +{ + bool sitting = IsSitting(); + bool feigned = GetFeigned(); + int level = GetLevel(); + bool bonus = GetRaceBitmask(GetBaseRace()) & RuleI(Character, BaseHPRegenBonusRaces); + uint8 multiplier1 = bonus ? 2 : 1; + int32 hp = 0; + + //these calculations should match up with the info from Monkly Business, which was last updated ~05/2008: http://www.monkly-business.net/index.php?pageid=abilities + if (level < 51) { + if (sitting) { + if (level < 20) + hp += 2 * multiplier1; + else if (level < 50) + hp += 3 * multiplier1; + else //level == 50 + hp += 4 * multiplier1; + } + else //feigned or standing + hp += 1 * multiplier1; + } + //there may be an easier way to calculate this next part, but I don't know what it is + else { //level >= 51 + int32 tmp = 0; + float multiplier2 = 1; + if (level < 56) { + tmp = 2; + if (bonus) + multiplier2 = 3; + } + else if (level < 60) { + tmp = 3; + if (bonus) + multiplier2 = 3.34; + } + else if (level < 61) { + tmp = 4; + if (bonus) + multiplier2 = 3; + } + else if (level < 63) { + tmp = 5; + if (bonus) + multiplier2 = 2.8; + } + else if (level < 65) { + tmp = 6; + if (bonus) + multiplier2 = 2.67; + } + else { //level >= 65 + tmp = 7; + if (bonus) + multiplier2 = 2.58; + } + + hp += int32(float(tmp) * multiplier2); + + if (sitting) + hp += 3 * multiplier1; + else if (feigned) + hp += 1 * multiplier1; + } + + return hp; +} + +int32 Client::CalcHPRegen() { + int32 regen = LevelRegen() + itembonuses.HPRegen + spellbonuses.HPRegen; + + regen += aabonuses.HPRegen + GroupLeadershipAAHealthRegeneration(); + + return (regen * RuleI(Character, HPRegenMultiplier) / 100); +} + +int32 Client::CalcHPRegenCap() +{ + int cap = RuleI(Character, ItemHealthRegenCap) + itembonuses.HeroicSTA/25; + + cap += aabonuses.ItemHPRegenCap + spellbonuses.ItemHPRegenCap + itembonuses.ItemHPRegenCap; + + return (cap * RuleI(Character, HPRegenMultiplier) / 100); +} + +int32 Client::CalcMaxHP() { + float nd = 10000; + max_hp = (CalcBaseHP() + itembonuses.HP); + + //The AA desc clearly says it only applies to base hp.. + //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 + + max_hp = (float)max_hp * (float)nd / (float)10000; //this is to fix the HP-above-495k issue + max_hp += spellbonuses.HP + aabonuses.HP; + + max_hp += GroupLeadershipAAHealthEnhancement(); + + max_hp += max_hp * (spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000; + + if (cur_hp > max_hp) + cur_hp = max_hp; + + int hp_perc_cap = spellbonuses.HPPercCap; + if(hp_perc_cap) { + int curHP_cap = (max_hp * hp_perc_cap) / 100; + if (cur_hp > curHP_cap) + cur_hp = curHP_cap; + } + + return max_hp; +} + +uint16 Mob::GetClassLevelFactor(){ + uint16 multiplier = 0; + uint8 mlevel=GetLevel(); + switch(GetClass()) + { + case WARRIOR:{ + if (mlevel < 20) + multiplier = 220; + else if (mlevel < 30) + multiplier = 230; + else if (mlevel < 40) + multiplier = 250; + else if (mlevel < 53) + multiplier = 270; + else if (mlevel < 57) + multiplier = 280; + else if (mlevel < 60) + multiplier = 290; + else if (mlevel < 70) + multiplier = 300; + else + multiplier = 311; + break; + } + case DRUID: + case CLERIC: + case SHAMAN:{ + if (mlevel < 70) + multiplier = 150; + else + multiplier = 157; + break; + } + case BERSERKER: + case PALADIN: + case SHADOWKNIGHT:{ + if (mlevel < 35) + multiplier = 210; + else if (mlevel < 45) + multiplier = 220; + else if (mlevel < 51) + multiplier = 230; + else if (mlevel < 56) + multiplier = 240; + else if (mlevel < 60) + multiplier = 250; + else if (mlevel < 68) + multiplier = 260; + else + multiplier = 270; + break; + } + case MONK: + case BARD: + case ROGUE: + case BEASTLORD:{ + if (mlevel < 51) + multiplier = 180; + else if (mlevel < 58) + multiplier = 190; + else if (mlevel < 70) + multiplier = 200; + else + multiplier = 210; + break; + } + case RANGER:{ + if (mlevel < 58) + multiplier = 200; + else if (mlevel < 70) + multiplier = 210; + else + multiplier = 220; + break; + } + case MAGICIAN: + case WIZARD: + case NECROMANCER: + case ENCHANTER:{ + if (mlevel < 70) + multiplier = 120; + else + multiplier = 127; + break; + } + default:{ + if (mlevel < 35) + multiplier = 210; + else if (mlevel < 45) + multiplier = 220; + else if (mlevel < 51) + multiplier = 230; + else if (mlevel < 56) + multiplier = 240; + else if (mlevel < 60) + multiplier = 250; + else + multiplier = 260; + break; + } + } + return multiplier; +} + +int32 Client::CalcBaseHP() +{ + if(GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { + float SoDPost255; + uint16 NormalSTA = GetSTA(); + + if(((NormalSTA - 255) / 2) > 0) + SoDPost255 = ((NormalSTA - 255) / 2); + else + SoDPost255 = 0; + + int hp_factor = GetClassHPFactor(); + + if (level < 41) { + base_hp = (5 + (GetLevel() * hp_factor / 12) + + ((NormalSTA - SoDPost255) * GetLevel() * hp_factor / 3600)); + } + else if (level < 81) { + base_hp = (5 + (40 * hp_factor / 12) + ((GetLevel() - 40) * hp_factor / 6) + + ((NormalSTA - SoDPost255) * hp_factor / 90) + + ((NormalSTA - SoDPost255) * (GetLevel() - 40) * hp_factor / 1800)); + } + else { + base_hp = (5 + (80 * hp_factor / 8) + ((GetLevel() - 80) * hp_factor / 10) + + ((NormalSTA - SoDPost255) * hp_factor / 90) + + ((NormalSTA - SoDPost255) * hp_factor / 45)); + } + + base_hp += (GetHeroicSTA() * 10); + + } + else { + uint16 Post255; + uint16 lm=GetClassLevelFactor(); + if((GetSTA()-255)/2 > 0) + Post255 = (GetSTA()-255)/2; + else + Post255 = 0; + + base_hp = (5)+(GetLevel()*lm/10) + (((GetSTA()-Post255)*GetLevel()*lm/3000)) + ((Post255*GetLevel())*lm/6000); + } + return base_hp; +} + +// This is for calculating Base HPs + STA bonus for SoD or later clients. +uint32 Client::GetClassHPFactor() { + + int factor; + + // Note: Base HP factor under level 41 is equal to factor / 12, and from level 41 to 80 is factor / 6. + // Base HP over level 80 is factor / 10 + // HP per STA point per level is factor / 30 for level 80+ + // HP per STA under level 40 is the level 80 HP Per STA / 120, and for over 40 it is / 60. + + switch(GetClass()) + { + case DRUID: + case ENCHANTER: + case NECROMANCER: + case MAGICIAN: + case WIZARD: + factor = 240; + break; + case BEASTLORD: + case BERSERKER: + case MONK: + case ROGUE: + case SHAMAN: + factor = 255; + break; + case BARD: + case CLERIC: + factor = 264; + break; + case SHADOWKNIGHT: + case PALADIN: + factor = 288; + break; + case RANGER: + factor = 276; + break; + case WARRIOR: + factor = 300; + break; + default: + factor = 240; + break; + } + return factor; +} + +// This should return the combined AC of all the items the player is wearing. +int16 Client::GetRawItemAC() { + int16 Total = 0; + + for (int16 slot_id=0; slot_id<21; slot_id++) { + const ItemInst* inst = m_inv[slot_id]; + if (inst && inst->IsType(ItemClassCommon)) { + Total += inst->GetItem()->AC; + } + } + + return Total; +} + +int16 Client::acmod() { + int agility = GetAGI(); + int level = GetLevel(); + if(agility < 1 || level < 1) + return(0); + + if (agility <=74){ + if (agility == 1) + return -24; + else if (agility <=3) + return -23; + else if (agility == 4) + return -22; + else if (agility <=6) + return -21; + else if (agility <=8) + return -20; + else if (agility == 9) + return -19; + else if (agility <=11) + return -18; + else if (agility == 12) + return -17; + else if (agility <=14) + return -16; + else if (agility <=16) + return -15; + else if (agility == 17) + return -14; + else if (agility <=19) + return -13; + else if (agility == 20) + return -12; + else if (agility <=22) + return -11; + else if (agility <=24) + return -10; + else if (agility == 25) + return -9; + else if (agility <=27) + return -8; + else if (agility == 28) + return -7; + else if (agility <=30) + return -6; + else if (agility <=32) + return -5; + else if (agility == 33) + return -4; + else if (agility <=35) + return -3; + else if (agility == 36) + return -2; + else if (agility <=38) + return -1; + else if (agility <=65) + return 0; + else if (agility <=70) + return 1; + else if (agility <=74) + return 5; + } + else if(agility <= 137) { + if (agility == 75){ + if (level <= 6) + return 9; + else if (level <= 19) + return 23; + else if (level <= 39) + return 33; + else + return 39; + } + else if (agility >= 76 && agility <= 79){ + if (level <= 6) + return 10; + else if (level <= 19) + return 23; + else if (level <= 39) + return 33; + else + return 40; + } + else if (agility == 80){ + if (level <= 6) + return 11; + else if (level <= 19) + return 24; + else if (level <= 39) + return 34; + else + return 41; + } + else if (agility >= 81 && agility <= 85){ + if (level <= 6) + return 12; + else if (level <= 19) + return 25; + else if (level <= 39) + return 35; + else + return 42; + } + else if (agility >= 86 && agility <= 90){ + if (level <= 6) + return 12; + else if (level <= 19) + return 26; + else if (level <= 39) + return 36; + else + return 42; + } + else if (agility >= 91 && agility <= 95){ + if (level <= 6) + return 13; + else if (level <= 19) + return 26; + else if (level <= 39) + return 36; + else + return 43; + } + else if (agility >= 96 && agility <= 99){ + if (level <= 6) + return 14; + else if (level <= 19) + return 27; + else if (level <= 39) + return 37; + else + return 44; + } + else if (agility == 100 && level >= 7){ + if (level <= 19) + return 28; + else if (level <= 39) + return 38; + else + return 45; + } + else if (level <= 6) { + return 15; + } + //level is >6 + else if (agility >= 101 && agility <= 105){ + if (level <= 19) + return 29; + else if (level <= 39) + return 39;// not verified + else + return 45; + } + else if (agility >= 106 && agility <= 110){ + if (level <= 19) + return 29; + else if (level <= 39) + return 39;// not verified + else + return 46; + } + else if (agility >= 111 && agility <= 115){ + if (level <= 19) + return 30; + else if (level <= 39) + return 40;// not verified + else + return 47; + } + else if (agility >= 116 && agility <= 119){ + if (level <= 19) + return 31; + else if (level <= 39) + return 41; + else + return 47; + } + else if (level <= 19) { + return 32; + } + //level is > 19 + else if (agility == 120){ + if (level <= 39) + return 42; + else + return 48; + } + else if (agility <= 125){ + if (level <= 39) + return 42; + else + return 49; + } + else if (agility <= 135){ + if (level <= 39) + return 42; + else + return 50; + } + else { + if (level <= 39) + return 42; + else + return 51; + } + } else if(agility <= 300) { + if(level <= 6) { + if(agility <= 139) + return(21); + else if(agility == 140) + return(22); + else if(agility <= 145) + return(23); + else if(agility <= 150) + return(23); + else if(agility <= 155) + return(24); + else if(agility <= 159) + return(25); + else if(agility == 160) + return(26); + else if(agility <= 165) + return(26); + else if(agility <= 170) + return(27); + else if(agility <= 175) + return(28); + else if(agility <= 179) + return(28); + else if(agility == 180) + return(29); + else if(agility <= 185) + return(30); + else if(agility <= 190) + return(31); + else if(agility <= 195) + return(31); + else if(agility <= 199) + return(32); + else if(agility <= 219) + return(33); + else if(agility <= 239) + return(34); + else + return(35); + } else if(level <= 19) { + if(agility <= 139) + return(34); + else if(agility == 140) + return(35); + else if(agility <= 145) + return(36); + else if(agility <= 150) + return(37); + else if(agility <= 155) + return(37); + else if(agility <= 159) + return(38); + else if(agility == 160) + return(39); + else if(agility <= 165) + return(40); + else if(agility <= 170) + return(40); + else if(agility <= 175) + return(41); + else if(agility <= 179) + return(42); + else if(agility == 180) + return(43); + else if(agility <= 185) + return(43); + else if(agility <= 190) + return(44); + else if(agility <= 195) + return(45); + else if(agility <= 199) + return(45); + else if(agility <= 219) + return(46); + else if(agility <= 239) + return(47); + else + return(48); + } else if(level <= 39) { + if(agility <= 139) + return(44); + else if(agility == 140) + return(45); + else if(agility <= 145) + return(46); + else if(agility <= 150) + return(47); + else if(agility <= 155) + return(47); + else if(agility <= 159) + return(48); + else if(agility == 160) + return(49); + else if(agility <= 165) + return(50); + else if(agility <= 170) + return(50); + else if(agility <= 175) + return(51); + else if(agility <= 179) + return(52); + else if(agility == 180) + return(53); + else if(agility <= 185) + return(53); + else if(agility <= 190) + return(54); + else if(agility <= 195) + return(55); + else if(agility <= 199) + return(55); + else if(agility <= 219) + return(56); + else if(agility <= 239) + return(57); + else + return(58); + } else { //lvl >= 40 + if(agility <= 139) + return(51); + else if(agility == 140) + return(52); + else if(agility <= 145) + return(53); + else if(agility <= 150) + return(53); + else if(agility <= 155) + return(54); + else if(agility <= 159) + return(55); + else if(agility == 160) + return(56); + else if(agility <= 165) + return(56); + else if(agility <= 170) + return(57); + else if(agility <= 175) + return(58); + else if(agility <= 179) + return(58); + else if(agility == 180) + return(59); + else if(agility <= 185) + return(60); + else if(agility <= 190) + return(61); + else if(agility <= 195) + return(61); + else if(agility <= 199) + return(62); + else if(agility <= 219) + return(63); + else if(agility <= 239) + return(64); + else + return(65); + } + } + else{ + //seems about 21 agil per extra AC pt over 300... + return (65 + ((agility-300) / 21)); + } +#if EQDEBUG >= 11 + LogFile->write(EQEMuLog::Error, "Error in Client::acmod(): Agility: %i, Level: %i",agility,level); +#endif + return 0; +}; + +// This is a testing formula for AC, the value this returns should be the same value as the one the client shows... +// 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() { + + // new formula + int avoidance = (acmod() + ((GetSkill(DEFENSE) + itembonuses.HeroicAGI/10)*16)/9); + if (avoidance < 0) + avoidance = 0; + + int mitigation = 0; + if (m_pp.class_ == WIZARD || m_pp.class_ == MAGICIAN || m_pp.class_ == NECROMANCER || m_pp.class_ == ENCHANTER) { + //something is wrong with this, naked casters have the wrong natural AC +// mitigation = (spellbonuses.AC/3) + (GetSkill(DEFENSE)/2) + (itembonuses.AC+1); + mitigation = (GetSkill(DEFENSE) + itembonuses.HeroicAGI/10)/4 + (itembonuses.AC+1); + //this might be off by 4.. + mitigation -= 4; + } else { +// mitigation = (spellbonuses.AC/4) + (GetSkill(DEFENSE)/3) + ((itembonuses.AC*4)/3); + mitigation = (GetSkill(DEFENSE) + itembonuses.HeroicAGI/10)/3 + ((itembonuses.AC*4)/3); + if(m_pp.class_ == MONK) + mitigation += GetLevel() * 13/10; //the 13/10 might be wrong, but it is close... + } + int displayed = 0; + displayed += ((avoidance+mitigation)*1000)/847; //natural AC + + //Iksar AC, untested + if (GetRace() == IKSAR) { + displayed += 12; + int iksarlevel = GetLevel(); + iksarlevel -= 10; + if (iksarlevel > 25) + iksarlevel = 25; + if (iksarlevel > 0) + displayed += iksarlevel * 12 / 10; + } + + // Shield AC bonus for HeroicSTR + if(itembonuses.HeroicSTR) { + bool equiped = CastToClient()->m_inv.GetItem(14); + if(equiped) { + uint8 shield = CastToClient()->m_inv.GetItem(14)->GetItem()->ItemType; + if(shield == ItemTypeShield) + displayed += itembonuses.HeroicSTR/2; + } + } + + //spell AC bonuses are added directly to natural total + displayed += spellbonuses.AC; + + AC = displayed; + return(AC); +} + +int16 Client::GetACMit() { + + int mitigation = 0; + if (m_pp.class_ == WIZARD || m_pp.class_ == MAGICIAN || m_pp.class_ == NECROMANCER || m_pp.class_ == ENCHANTER) { + mitigation = (GetSkill(DEFENSE) + itembonuses.HeroicAGI/10)/4 + (itembonuses.AC+1); + mitigation -= 4; + } + else { + mitigation = (GetSkill(DEFENSE) + itembonuses.HeroicAGI/10)/3 + ((itembonuses.AC*4)/3); + if(m_pp.class_ == MONK) + mitigation += GetLevel() * 13/10; //the 13/10 might be wrong, but it is close... + } + + // Shield AC bonus for HeroicSTR + if(itembonuses.HeroicSTR) { + bool equiped = CastToClient()->m_inv.GetItem(14); + if(equiped) { + uint8 shield = CastToClient()->m_inv.GetItem(14)->GetItem()->ItemType; + if(shield == ItemTypeShield) + mitigation += itembonuses.HeroicSTR/2; + } + } + + return(mitigation*1000/847); +} + +int16 Client::GetACAvoid() { + + int avoidance = (acmod() + ((GetSkill(DEFENSE) + itembonuses.HeroicAGI/10)*16)/9); + if (avoidance < 0) + avoidance = 0; + + return(avoidance*1000/847); +} + +int32 Client::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; + } + } + if (max_mana < 0) { + max_mana = 0; + } + + if (cur_mana > max_mana) { + cur_mana = max_mana; + } + + int mana_perc_cap = spellbonuses.ManaPercCap; + if(mana_perc_cap) { + int curMana_cap = (max_mana * mana_perc_cap) / 100; + if (cur_mana > curMana_cap) + cur_mana = curMana_cap; + } + +#if EQDEBUG >= 11 + LogFile->write(EQEMuLog::Debug, "Client::CalcMaxMana() called for %s - returning %d", GetName(), max_mana); +#endif + return max_mana; +} + +int32 Client::CalcBaseMana() +{ + int WisInt = 0; + int MindLesserFactor, MindFactor; + int32 max_m = 0; + int wisint_mana = 0; + int base_mana = 0; + int ConvertedWisInt = 0; + switch(GetCasterClass()) + { + case 'I': + WisInt = GetINT(); + + if (GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { + + if (WisInt > 100) { + ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); + if (WisInt > 201) { + ConvertedWisInt -= ((WisInt - 201) * 5 / 4); + } + } + else { + ConvertedWisInt = WisInt; + } + + if (GetLevel() < 41) { + wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000); + base_mana = (GetLevel() * 15); + } + else if (GetLevel() < 81) { + wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100)); + base_mana = (600 + ((GetLevel() - 40) * 30)); + } + else { + wisint_mana = (9 * ConvertedWisInt); + base_mana = (1800 + ((GetLevel() - 80) * 18)); + } + max_m = base_mana + wisint_mana + (GetHeroicINT() * 10); + } + else + { + if((( WisInt - 199 ) / 2) > 0) + MindLesserFactor = ( WisInt - 199 ) / 2; + else + MindLesserFactor = 0; + + MindFactor = WisInt - MindLesserFactor; + if(WisInt > 100) + max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40); + else + max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100); + } + break; + + case 'W': + WisInt = GetWIS(); + + if (GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { + + if (WisInt > 100) { + ConvertedWisInt = (((WisInt - 100) * 5 / 2) + 100); + if (WisInt > 201) { + ConvertedWisInt -= ((WisInt - 201) * 5 / 4); + } + } + else { + ConvertedWisInt = WisInt; + } + + if (GetLevel() < 41) { + wisint_mana = (GetLevel() * 75 * ConvertedWisInt / 1000); + base_mana = (GetLevel() * 15); + } + else if (GetLevel() < 81) { + wisint_mana = ((3 * ConvertedWisInt) + ((GetLevel() - 40) * 15 * ConvertedWisInt / 100)); + base_mana = (600 + ((GetLevel() - 40) * 30)); + } + else { + wisint_mana = (9 * ConvertedWisInt); + base_mana = (1800 + ((GetLevel() - 80) * 18)); + } + max_m = base_mana + wisint_mana + (GetHeroicWIS() * 10); + } + else + { + if((( WisInt - 199 ) / 2) > 0) + MindLesserFactor = ( WisInt - 199 ) / 2; + else + MindLesserFactor = 0; + + MindFactor = WisInt - MindLesserFactor; + if(WisInt > 100) + max_m = (((5 * (MindFactor + 20)) / 2) * 3 * GetLevel() / 40); + else + max_m = (((5 * (MindFactor + 200)) / 2) * 3 * GetLevel() / 100); + } + break; + + case 'N': { + max_m = 0; + break; + } + default: { + LogFile->write(EQEMuLog::Debug, "Invalid Class '%c' in CalcMaxMana", GetCasterClass()); + max_m = 0; + break; + } + } + +#if EQDEBUG >= 11 + LogFile->write(EQEMuLog::Debug, "Client::CalcBaseMana() called for %s - returning %d", GetName(), max_m); +#endif + return max_m; +} + +int32 Client::CalcBaseManaRegen() +{ + uint8 clevel = GetLevel(); + int32 regen = 0; + if (IsSitting() || (GetHorseId() != 0)) + { + if(HasSkill(MEDITATE)) + regen = (((GetSkill(MEDITATE) / 10) + (clevel - (clevel / 4))) / 4) + 4; + else + regen = 2; + } + else { + regen = 2; + } + return regen; +} + +int32 Client::CalcManaRegen() +{ + uint8 clevel = GetLevel(); + int32 regen = 0; + //this should be changed so we dont med while camping, etc... + if (IsSitting() || (GetHorseId() != 0)) + { + BuffFadeBySitModifier(); + if(HasSkill(MEDITATE)) { + this->medding = true; + regen = (((GetSkill(MEDITATE) / 10) + (clevel - (clevel / 4))) / 4) + 4; + regen += spellbonuses.ManaRegen + itembonuses.ManaRegen; + CheckIncreaseSkill(MEDITATE, NULL, -5); + } + else + regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen; + } + else { + this->medding = false; + regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen; + } + + //AAs + regen += aabonuses.ManaRegen; + + return (regen * RuleI(Character, ManaRegenMultiplier) / 100); +} + +int32 Client::CalcManaRegenCap() +{ + int32 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap; + switch(GetCasterClass()) + { + case 'I': + cap += (itembonuses.HeroicINT / 25); + break; + case 'W': + cap += (itembonuses.HeroicWIS / 25); + break; + } + + return (cap * RuleI(Character, ManaRegenMultiplier) / 100); +} + +uint32 Client::CalcCurrentWeight() { + + const Item_Struct* TempItem = 0; + ItemInst* ins; + uint32 Total = 0; + int x; + for(x = 0; x <= 30; x++) + { + TempItem = 0; + ins = GetInv().GetItem(x); + if (ins) + TempItem = ins->GetItem(); + if (TempItem) + Total += TempItem->Weight; + } + for (x = 251; x < 331; x++) + { + int TmpWeight = 0; + TempItem = 0; + ins = GetInv().GetItem(x); + if (ins) + TempItem = ins->GetItem(); + if (TempItem) + TmpWeight = TempItem->Weight; + if (TmpWeight > 0) + { + int bagslot = 22; + int reduction = 0; + for (int m = 261; m < 331; m += 10) + { + if (x >= m) + bagslot += 1; + } + ItemInst* baginst = GetInv().GetItem(bagslot); + if (baginst && baginst->GetItem() && baginst->IsType(ItemClassContainer)) + reduction = baginst->GetItem()->BagWR; + if (reduction > 0) + TmpWeight -= TmpWeight*reduction/100; + Total += TmpWeight; + } + } + + //TODO: coin weight reduction (from purses, etc), since client already calculates it + /*From the Wiki http://www.eqemulator.net/wiki/wikka.php?wakka=EQEmuDBSchemaitems under bagwr (thanks Trevius): + Interestingly, you can also have bags that reduce coin weight. However, in order to set bags to reduce coin weight, you MUST set the Item ID somewhere between 17201 and 17230. This is hard coded into the client. + The client is set to have certain coin weight reduction on a per Item ID basis within this range. The best way to create an new item to reduce coin weight is to examine existing bags in this range. + Search for the words "coin purse" with the #finditem command in game and the Bag WR setting on those bags is the amount they will reduce coin weight. It is easiest to overwrite one of those bags if you wish to create one with the + same weight reduction amount for coins. You can use other Item IDs in this range for setting coin weight reduction, but by using an existing item, at least you will know the amount the client will reduce it by before you create it. + This is the ONLY instance I have seen where the client is hard coded to particular Item IDs to set a certain property for an item. It is very odd. + */ + + // SoD client has no weight for coin + if (GetClientVersion() < EQClientSoD) { + Total += (m_pp.platinum + m_pp.gold + m_pp.silver + m_pp.copper) / 4; + } + + float Packrat = (float)spellbonuses.Packrat + (float)aabonuses.Packrat; + if (Packrat > 0) + Total = (uint32)((float)Total * (1.0f - ((Packrat * 1.0f) / 100.0f))); //AndMetal: 1% per level, up to 5% (calculated from Titanium client). verified thru client that it reduces coin weight by the same % + //without casting to float & back to uint32, this didn't work right + return Total; +} + +int16 Client::CalcAlcoholPhysicalEffect() +{ + if(m_pp.intoxication <= 55) + return 0; + + return (m_pp.intoxication - 40) / 16; +} + +int16 Client::CalcSTR() { + int16 val = m_pp.STR + itembonuses.STR + spellbonuses.STR + CalcAlcoholPhysicalEffect(); + + int16 mod = aabonuses.STR; + + if(val>255 && GetLevel() <= 60) + val = 255; + STR = val + mod; + + if(STR < 1) + STR = 1; + + int m = GetMaxSTR(); + if(STR > m) + STR = m; + + return(STR); +} + +int16 Client::CalcSTA() { + int16 val = m_pp.STA + itembonuses.STA + spellbonuses.STA + CalcAlcoholPhysicalEffect();; + + int16 mod = aabonuses.STA; + + if(val>255 && GetLevel() <= 60) + val = 255; + STA = val + mod; + + if(STA < 1) + STA = 1; + + int m = GetMaxSTA(); + if(STA > m) + STA = m; + + return(STA); +} + +int16 Client::CalcAGI() { + int16 val = m_pp.AGI + itembonuses.AGI + spellbonuses.AGI - CalcAlcoholPhysicalEffect();; + int16 mod = aabonuses.AGI; + + if(val>255 && GetLevel() <= 60) + val = 255; + + int16 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 + } else + AGI = val + mod; + + if(AGI < 1) + AGI = 1; + + int m = GetMaxAGI(); + if(AGI > m) + AGI = m; + + return(AGI); +} + +int16 Client::CalcDEX() { + int16 val = m_pp.DEX + itembonuses.DEX + spellbonuses.DEX - CalcAlcoholPhysicalEffect();; + + int16 mod = aabonuses.DEX; + + if(val>255 && GetLevel() <= 60) + val = 255; + DEX = val + mod; + + if(DEX < 1) + DEX = 1; + + int m = GetMaxDEX(); + if(DEX > m) + DEX = m; + + return(DEX); +} + +int16 Client::CalcINT() { + int16 val = m_pp.INT + itembonuses.INT + spellbonuses.INT; + + int16 mod = aabonuses.INT; + + if(val>255 && GetLevel() <= 60) + val = 255; + INT = val + mod; + + if(m_pp.intoxication) + { + int16 AlcINT = INT - (int16)((float)m_pp.intoxication / 200.0f * (float)INT) - 1; + + if((AlcINT < (int)(0.2 * INT))) + INT = (int)(0.2f * (float)INT); + else + INT = AlcINT; + } + + if(INT < 1) + INT = 1; + + int m = GetMaxINT(); + if(INT > m) + INT = m; + + return(INT); +} + +int16 Client::CalcWIS() { + int16 val = m_pp.WIS + itembonuses.WIS + spellbonuses.WIS; + + int16 mod = aabonuses.WIS; + + if(val>255 && GetLevel() <= 60) + val = 255; + WIS = val + mod; + + if(m_pp.intoxication) + { + int16 AlcWIS = WIS - (int16)((float)m_pp.intoxication / 200.0f * (float)WIS) - 1; + + if((AlcWIS < (int)(0.2 * WIS))) + WIS = (int)(0.2f * (float)WIS); + else + WIS = AlcWIS; + } + + if(WIS < 1) + WIS = 1; + + int m = GetMaxWIS(); + if(WIS > m) + WIS = m; + + return(WIS); +} + +int16 Client::CalcCHA() { + int16 val = m_pp.CHA + itembonuses.CHA + spellbonuses.CHA; + + int16 mod = aabonuses.CHA; + + if(val>255 && GetLevel() <= 60) + val = 255; + CHA = val + mod; + + if(CHA < 1) + CHA = 1; + + int m = GetMaxCHA(); + if(CHA > m) + CHA = m; + + return(CHA); +} + +int Client::CalcHaste() { + int h = spellbonuses.haste + spellbonuses.hastetype2 + itembonuses.haste; + int cap = 0; + int level = GetLevel(); + /* + if(disc_inuse == discBlindingSpeed) { + if(!disc_elapse.Check(false)) { + h += 20; //this ammount is completely unknown + } else { + disc_inuse = discNone; + } + } */ + + if(level < 30) { // Rogean: Are these caps correct? Will use for now. + cap = 50; + } else if(level < 50) { + cap = 74; + } else if(level < 55) { + cap = 84; + } else if(level < 60) { + cap = 94; + } else { + cap = RuleI(Character, HasteCap); + } + + if(h > cap) h = cap; + + h += spellbonuses.hastetype3; + h += ExtraHaste; //GM granted haste. + + if (spellbonuses.inhibitmelee){ + if (h >= 0) + h -= spellbonuses.inhibitmelee; + + else + h -=((100+h)*spellbonuses.inhibitmelee/100); + } + + Haste = 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() +{ + //racial bases + switch(GetBaseRace()) { + case HUMAN: + MR = 25; + break; + case BARBARIAN: + MR = 25; + break; + case ERUDITE: + MR = 30; + break; + case WOOD_ELF: + MR = 25; + break; + case HIGH_ELF: + MR = 25; + break; + case DARK_ELF: + MR = 25; + break; + case HALF_ELF: + MR = 25; + break; + case DWARF: + MR = 30; + break; + case TROLL: + MR = 25; + break; + case OGRE: + MR = 25; + break; + case HALFLING: + MR = 25; + break; + case GNOME: + MR = 25; + break; + case IKSAR: + MR = 25; + break; + case VAHSHIR: + MR = 25; + break; + case FROGLOK: + MR = 30; + break; + case DRAKKIN: + MR = 35; + break; + default: + MR = 20; + } + + MR += itembonuses.MR + spellbonuses.MR + aabonuses.MR; + + if(GetClass() == WARRIOR) + MR += GetLevel() / 2; + + if(MR < 1) + MR = 1; + + if(MR > GetMaxMR()) + MR = GetMaxMR(); + + return(MR); +} + +int16 Client::CalcFR() +{ + //racial bases + switch(GetBaseRace()) { + case HUMAN: + FR = 25; + break; + case BARBARIAN: + FR = 25; + break; + case ERUDITE: + FR = 25; + break; + case WOOD_ELF: + FR = 25; + break; + case HIGH_ELF: + FR = 25; + break; + case DARK_ELF: + FR = 25; + break; + case HALF_ELF: + FR = 25; + break; + case DWARF: + FR = 25; + break; + case TROLL: + FR = 5; + break; + case OGRE: + FR = 25; + break; + case HALFLING: + FR = 25; + break; + case GNOME: + FR = 25; + break; + case IKSAR: + FR = 30; + break; + case VAHSHIR: + FR = 25; + break; + case FROGLOK: + FR = 25; + break; + case DRAKKIN: + FR = 25; + break; + default: + FR = 20; + } + + int c = GetClass(); + if(c == RANGER) { + FR += 4; + + int l = GetLevel(); + if(l > 49) + FR += l - 49; + } + + FR += itembonuses.FR + spellbonuses.FR + aabonuses.FR; + + if(FR < 1) + FR = 1; + + if(FR > GetMaxFR()) + FR = GetMaxFR(); + + return(FR); +} + +int16 Client::CalcDR() +{ + //racial bases + switch(GetBaseRace()) { + case HUMAN: + DR = 15; + break; + case BARBARIAN: + DR = 15; + break; + case ERUDITE: + DR = 10; + break; + case WOOD_ELF: + DR = 15; + break; + case HIGH_ELF: + DR = 15; + break; + case DARK_ELF: + DR = 15; + break; + case HALF_ELF: + DR = 15; + break; + case DWARF: + DR = 15; + break; + case TROLL: + DR = 15; + break; + case OGRE: + DR = 15; + break; + case HALFLING: + DR = 20; + break; + case GNOME: + DR = 15; + break; + case IKSAR: + DR = 15; + break; + case VAHSHIR: + DR = 15; + break; + case FROGLOK: + DR = 15; + break; + case DRAKKIN: + DR = 15; + break; + default: + DR = 15; + } + + int c = GetClass(); + if(c == PALADIN) { + DR += 8; + + int l = GetLevel(); + if(l > 49) + DR += l - 49; + + } else if(c == SHADOWKNIGHT) { + DR += 4; + + int l = GetLevel(); + if(l > 49) + DR += l - 49; + } + + DR += itembonuses.DR + spellbonuses.DR + aabonuses.DR; + + if(DR < 1) + DR = 1; + + if(DR > GetMaxDR()) + DR = GetMaxDR(); + + return(DR); +} + +int16 Client::CalcPR() +{ + //racial bases + switch(GetBaseRace()) { + case HUMAN: + PR = 15; + break; + case BARBARIAN: + PR = 15; + break; + case ERUDITE: + PR = 15; + break; + case WOOD_ELF: + PR = 15; + break; + case HIGH_ELF: + PR = 15; + break; + case DARK_ELF: + PR = 15; + break; + case HALF_ELF: + PR = 15; + break; + case DWARF: + PR = 20; + break; + case TROLL: + PR = 15; + break; + case OGRE: + PR = 15; + break; + case HALFLING: + PR = 20; + break; + case GNOME: + PR = 15; + break; + case IKSAR: + PR = 15; + break; + case VAHSHIR: + PR = 15; + break; + case FROGLOK: + PR = 30; + break; + case DRAKKIN: + PR = 15; + break; + default: + PR = 15; + } + + int c = GetClass(); + if(c == ROGUE) { + PR += 8; + + int l = GetLevel(); + if(l > 49) + PR += l - 49; + + } else if(c == SHADOWKNIGHT) { + PR += 4; + + int l = GetLevel(); + if(l > 49) + PR += l - 49; + } + + PR += itembonuses.PR + spellbonuses.PR + aabonuses.PR; + + if(PR < 1) + PR = 1; + + if(PR > GetMaxPR()) + PR = GetMaxPR(); + + return(PR); +} + +int16 Client::CalcCR() +{ + //racial bases + switch(GetBaseRace()) { + case HUMAN: + CR = 25; + break; + case BARBARIAN: + CR = 35; + break; + case ERUDITE: + CR = 25; + break; + case WOOD_ELF: + CR = 25; + break; + case HIGH_ELF: + CR = 25; + break; + case DARK_ELF: + CR = 25; + break; + case HALF_ELF: + CR = 25; + break; + case DWARF: + CR = 25; + break; + case TROLL: + CR = 25; + break; + case OGRE: + CR = 25; + break; + case HALFLING: + CR = 25; + break; + case GNOME: + CR = 25; + break; + case IKSAR: + CR = 15; + break; + case VAHSHIR: + CR = 25; + break; + case FROGLOK: + CR = 25; + break; + case DRAKKIN: + CR = 25; + break; + default: + CR = 25; + } + + int c = GetClass(); + if(c == RANGER) { + CR += 4; + + int l = GetLevel(); + if(l > 49) + CR += l - 49; + } + + CR += itembonuses.CR + spellbonuses.CR + aabonuses.CR; + + if(CR < 1) + CR = 1; + + if(CR > GetMaxCR()) + CR = GetMaxCR(); + + return(CR); +} + +int16 Client::CalcCorrup() +{ + Corrup = GetBaseCorrup() + itembonuses.Corrup + spellbonuses.Corrup + aabonuses.Corrup; + + if(Corrup > GetMaxCorrup()) + Corrup = GetMaxCorrup(); + + return(Corrup); +} + +int16 Client::CalcATK() { + ATK = itembonuses.ATK + spellbonuses.ATK + aabonuses.ATK + GroupLeadershipAAOffenseEnhancement(); + return(ATK); +} + +uint16 Mob::GetInstrumentMod(uint16 spell_id) const { + if(GetClass() != BARD) + return(10); + + uint16 effectmod = 10; + + //this should never use spell modifiers... + //if a spell grants better modifers, they are copied into the item mods + //because the spells are supposed to act just like having the intrument. + + //item mods are in 10ths of percent increases + switch(spells[spell_id].skill) { + case PERCUSSION_INSTRUMENTS: + if(itembonuses.percussionMod == 0 && spellbonuses.percussionMod == 0) + effectmod = 10; + else if(GetSkill(PERCUSSION_INSTRUMENTS) == 0) + effectmod = 10; + else if(itembonuses.percussionMod > spellbonuses.percussionMod) + effectmod = itembonuses.percussionMod; + else + effectmod = spellbonuses.percussionMod; + break; + case STRINGED_INSTRUMENTS: + if(itembonuses.stringedMod == 0 && spellbonuses.stringedMod == 0) + effectmod = 10; + else if(GetSkill(STRINGED_INSTRUMENTS) == 0) + effectmod = 10; + else if(itembonuses.stringedMod > spellbonuses.stringedMod) + effectmod = itembonuses.stringedMod; + else + effectmod = spellbonuses.stringedMod; + break; + case WIND_INSTRUMENTS: + if(itembonuses.windMod == 0 && spellbonuses.windMod == 0) + effectmod = 10; + else if(GetSkill(WIND_INSTRUMENTS) == 0) + effectmod = 10; + else if(itembonuses.windMod > spellbonuses.windMod) + effectmod = itembonuses.windMod; + else + effectmod = spellbonuses.windMod; + break; + case BRASS_INSTRUMENTS: + if(itembonuses.brassMod == 0 && spellbonuses.brassMod == 0) + effectmod = 10; + else if(GetSkill(BRASS_INSTRUMENTS) == 0) + effectmod = 10; + else if(itembonuses.brassMod > spellbonuses.brassMod) + effectmod = itembonuses.brassMod; + else + effectmod = spellbonuses.brassMod; + break; + case SINGING: + if(itembonuses.singingMod == 0 && spellbonuses.singingMod == 0) + effectmod = 10; + else if(itembonuses.singingMod > spellbonuses.singingMod) + effectmod = itembonuses.singingMod; + else + effectmod = spellbonuses.singingMod; + break; + default: + effectmod = 10; + break; + } + + if(spells[spell_id].skill == SINGING) + { + effectmod += 2*GetAA(aaSingingMastery); + effectmod += 2*GetAA(aaImprovedSingingMastery); + } + else + { + effectmod += 2*GetAA(aaInstrumentMastery); + effectmod += 2*GetAA(aaImprovedInstrumentMastery); + } + effectmod += 2*GetAA(aaAyonaesTutelage); //singing & instruments + effectmod += 2*GetAA(aaEchoofTaelosia); //singing & instruments + + + if(effectmod < 10) + effectmod = 10; + + _log(SPELLS__BARDS, "%s::GetInstrumentMod() spell=%d mod=%d\n", GetName(), spell_id, effectmod); + + return(effectmod); +} + +void Client::CalcMaxEndurance() +{ + max_end = CalcBaseEndurance() + spellbonuses.Endurance + itembonuses.Endurance; + + if (max_end < 0) { + max_end = 0; + } + + if (cur_end > max_end) { + cur_end = max_end; + } + + int end_perc_cap = spellbonuses.EndPercCap; + if(end_perc_cap) { + int curEnd_cap = (max_end * end_perc_cap) / 100; + if (cur_end > curEnd_cap) + cur_end = curEnd_cap; + } +} + +int32 Client::CalcBaseEndurance() +{ + int32 base_end = 0; + int32 base_endurance = 0; + int32 ConvertedStats = 0; + int32 sta_end = 0; + int Stats = 0; + + if(GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { + int HeroicStats = 0; + + Stats = ((GetSTR() + GetSTA() + GetDEX() + GetAGI()) / 4); + HeroicStats = ((GetHeroicSTR() + GetHeroicSTA() + GetHeroicDEX() + GetHeroicAGI()) / 4); + + if (Stats > 100) { + ConvertedStats = (((Stats - 100) * 5 / 2) + 100); + if (Stats > 201) { + ConvertedStats -= ((Stats - 201) * 5 / 4); + } + } + else { + ConvertedStats = Stats; + } + + if (GetLevel() < 41) { + sta_end = (GetLevel() * 75 * ConvertedStats / 1000); + base_endurance = (GetLevel() * 15); + } + else if (GetLevel() < 81) { + sta_end = ((3 * ConvertedStats) + ((GetLevel() - 40) * 15 * ConvertedStats / 100)); + base_endurance = (600 + ((GetLevel() - 40) * 30)); + } + else { + sta_end = (9 * ConvertedStats); + base_endurance = (1800 + ((GetLevel() - 80) * 18)); + } + base_end = (base_endurance + sta_end + (HeroicStats * 10)); + } + else + { + Stats = GetSTR()+GetSTA()+GetDEX()+GetAGI(); + int LevelBase = GetLevel() * 15; + + int at_most_800 = Stats; + if(at_most_800 > 800) + at_most_800 = 800; + + int Bonus400to800 = 0; + int HalfBonus400to800 = 0; + int Bonus800plus = 0; + int HalfBonus800plus = 0; + + int BonusUpto800 = int( at_most_800 / 4 ) ; + if(Stats > 400) { + Bonus400to800 = int( (at_most_800 - 400) / 4 ); + HalfBonus400to800 = int( max( ( at_most_800 - 400 ), 0 ) / 8 ); + + if(Stats > 800) { + Bonus800plus = int( (Stats - 800) / 8 ) * 2; + HalfBonus800plus = int( (Stats - 800) / 16 ); + } + } + int bonus_sum = BonusUpto800 + Bonus400to800 + HalfBonus400to800 + Bonus800plus + HalfBonus800plus; + + base_end = LevelBase; + + //take all of the sums from above, then multiply by level*0.075 + base_end += ( bonus_sum * 3 * GetLevel() ) / 40; + } + return base_end; +} + +int32 Client::CalcEnduranceRegen() { + int32 regen = int32(GetLevel() * 4 / 10) + 2; + regen += aabonuses.EnduranceRegen + spellbonuses.EnduranceRegen + itembonuses.EnduranceRegen; + + return (regen * RuleI(Character, EnduranceRegenMultiplier) / 100); +} + +int32 Client::CalcEnduranceRegenCap() { + int cap = (RuleI(Character, ItemEnduranceRegenCap) + itembonuses.HeroicSTR/25 + itembonuses.HeroicDEX/25 + itembonuses.HeroicAGI/25 + itembonuses.HeroicSTA/25); + + return (cap * RuleI(Character, EnduranceRegenMultiplier) / 100); +} + +int Client::GetRawACNoShield(int &shield_ac) const +{ + int ac = itembonuses.AC + spellbonuses.AC; + shield_ac = 0; + const ItemInst *inst = m_inv.GetItem(SLOT_SECONDARY); + if(inst) + { + if(inst->GetItem()->ItemType == ItemTypeShield) + { + ac -= inst->GetItem()->AC; + shield_ac = inst->GetItem()->AC; + for(uint8 i = 0; i < MAX_AUGMENT_SLOTS; i++) + { + if(inst->GetAugment(i)) + { + ac -= inst->GetAugment(i)->GetItem()->AC; + shield_ac += inst->GetAugment(i)->GetItem()->AC; + } + } + } + } + return ac; +} diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp new file mode 100644 index 000000000..641e96068 --- /dev/null +++ b/zone/client_packet.cpp @@ -0,0 +1,13842 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2009 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WINDOWS + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#else + #include + #include + #include + #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/MiscFunctions.h" +#include "../common/breakdowns.h" +#include "../common/guilds.h" +#include "../common/rulesys.h" +#include "spdat.h" +#include "petitions.h" +#include "NpcAI.h" +#include "../common/skills.h" +#include "forage.h" +#include "zone.h" +#include "event_codes.h" +#include "faction.h" +#include "../common/crc32.h" +#include "StringIDs.h" +#include "map.h" +#include "titles.h" +#include "pets.h" +#include "ZoneConfig.h" +#include "guild_mgr.h" +#include "pathing.h" +#include "watermap.h" +#include "merc.h" +#include "../common/ZoneNumbers.h" +#include "QuestParserCollection.h" + +using namespace std; + + +extern Zone* zone; +extern volatile bool ZoneLoaded; +extern WorldServer worldserver; +#if !defined(NEW_LoadSPDat) && !defined(DB_LoadSPDat) + extern SPDat_Spell_Struct spells[SPDAT_RECORDS]; +#endif +extern bool spells_loaded; +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 +map ConnectingOpcodes; +//Use a static array for connected, for speed +ClientPacketProc ConnectedOpcodes[_maxEmuOpcode]; + +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; + 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_XTargetAutoAddHaters] = &Client::Handle_OP_XTargetAutoAddHaters; +//temporary hack: + ConnectingOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; + + //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; + 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_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_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_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; +} + +int Client::HandlePacket(const EQApplicationPacket *app) +{ + _ZP(Client_HandlePacket); + + if(is_log_enabled(CLIENT__NET_IN_TRACE)) { + char buffer[64]; + app->build_header_dump(buffer); + mlog(CLIENT__NET_IN_TRACE, "Dispatch opcode: %s", buffer); + mpkt(CLIENT__NET_IN_TRACE, app); + } + + EmuOpcode opcode = app->GetOpcode(); + if (opcode == OP_AckPacket) { + return true; + } + + #if EQDEBUG >= 9 + cout << "Received 0x" << hex << setw(4) << setfill('0') << opcode << ", size=" << dec << app->size << endl; + #endif + + #ifdef SOLAR + if(0 && opcode != OP_ClientUpdate) + { + LogFile->write(EQEMuLog::Debug,"HandlePacket() OPCODE debug enabled client %s", GetName()); + cerr << "OPCODE: " << hex << setw(4) << setfill('0') << opcode << dec << ", size: " << app->size << endl; + DumpPacket(app); + } + #endif + + switch(client_state) { + case CLIENT_CONNECTING: { + if(ConnectingOpcodes.count(opcode) != 1) { +//TODO: replace this 0 with the EQ opcode + LogFile->write(EQEMuLog::Error, "HandlePacket() Opcode error: Unexpected packet during CLIENT_CONNECTING: opcode: %s (#%d eq=0x%04x), size: %i", OpcodeNames[opcode], opcode, 0, app->size); +#if EQDEBUG >= 9 + cout << "Unexpected packet during CLIENT_CONNECTING: OpCode: 0x" << hex << setw(4) << setfill('0') << opcode << dec << ", size: " << app->size << endl; + DumpPacket(app); +#endif + break; + } + + ClientPacketProc p; + p = ConnectingOpcodes[opcode]; + + //call the processing routine + (this->*p)(app); + + //special case where connecting code needs to boot client... + if(client_state == CLIENT_KICKED) { + return(false); + } + + break; + } + case CLIENT_CONNECTED: { + ClientPacketProc p; + p = ConnectedOpcodes[opcode]; + if(p == NULL) { + char buffer[64]; + app->build_header_dump(buffer); + mlog(CLIENT__NET_ERR, "Unhandled incoming opcode: %s", buffer); + if(app->size<1000) + DumpPacket(app->pBuffer, app->size); + else{ + cout << "Dump limited to 1000 characters:\n"; + DumpPacket(app->pBuffer, 1000); + } + break; + } + + //call the processing routine + (this->*p)(app); + break; + } + case CLIENT_KICKED: + case DISCONNECTED: + case CLIENT_LINKDEAD: + break; + default: + LogFile->write(EQEMuLog::Debug, "Unknown client_state: %d\n", client_state); + break; + } + + return(true); +} + + + +/*void Client::Handle_Connect_OP_SetDataRate(const EQApplicationPacket *app) +{ + // Set client datarate + //if (app->size != sizeof(float)) { + //LogFile->write(EQEMuLog::Error,"Wrong size on OP_SetDatarate. Got: %i, Expected: %i", app->size, sizeof(float)); + //return; + //} + //LogFile->write(EQEMuLog::Debug, "HandlePacket() OP_SetDataRate request : %f", *(float*) app->pBuffer); + //float tmpDR = *(float*) app->pBuffer; + //if (tmpDR <= 0.0f) { + //LogFile->write(EQEMuLog::Error,"HandlePacket() OP_SetDataRate INVALID request : %f <= 0", tmpDR); + //LogFile->write(EQEMuLog::Normal,"WARNING: Setting datarate for client to 5.0 expect a client lock up =("); + //tmpDR = 5.0f; + //} + //if (tmpDR > 25.0f) + //tmpDR = 25.0f; + //eqs->SetDataRate(tmpDR); + return; +}*/ + +void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) +{ + if(app->size != sizeof(ClientZoneEntry_Struct)) + return; + ClientZoneEntry_Struct *cze = (ClientZoneEntry_Struct *) app->pBuffer; + + if(strlen(cze->char_name) > 63) + return; + + conn_state = ReceivedZoneEntry; + + string StreamDescription = Connection()->Describe(); + + if(StreamDescription == "Patch Titanium") + { + ClientVersion = EQClientTitanium; + ClientVersionBit = BIT_Titanium; + } + else if(StreamDescription == "Patch 6.2") + { + ClientVersion = EQClient62; + ClientVersionBit = BIT_Client62; + } + else if(StreamDescription == "Patch SoF") + { + ClientVersion = EQClientSoF; + ClientVersionBit = BIT_SoF; + } + else if(StreamDescription == "Patch SoD") + { + ClientVersion = EQClientSoD; + ClientVersionBit = BIT_SoD; + } + else if(StreamDescription == "Patch Underfoot") + { + ClientVersion = EQClientUnderfoot; + ClientVersionBit = BIT_Underfoot; + } + else if(StreamDescription == "Patch RoF") + { + ClientVersion = EQClientRoF; + ClientVersionBit = BIT_RoF; + } + // 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; + } + + strcpy(name, cze->char_name); + 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(); + } + + 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; + return; + } + 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_SendAATable(const EQApplicationPacket *app) +{ + SendAAList(); + return; +} + +void Client::Handle_Connect_OP_SendTributes(const EQApplicationPacket *app) +{ + SendTributes(); + return; +} + +void Client::Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app) +{ + SendGuildTributes(); + 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_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; + + EQApplicationPacket* outapp = new EQApplicationPacket; + + // Send Zone Doors + if(entity_list.MakeDoorSpawnPacket(outapp, this)) + { + QueuePacket(outapp); + } + safe_delete(outapp); + + // Send Zone Objects + entity_list.SendZoneObjects(this); + SendZonePoints(); + // Live does this + outapp = new EQApplicationPacket(OP_SendAAStats, 0); + FastQueuePacket(&outapp); + + // Tell client they can continue we're done + outapp = new EQApplicationPacket(OP_ZoneServerReady, 0); + FastQueuePacket(&outapp); + outapp = new EQApplicationPacket(OP_SendExpZonein, 0); + FastQueuePacket(&outapp); + + if(GetClientVersion() >= EQClientRoF) + { + outapp = new EQApplicationPacket(OP_ClientReady, 0); + FastQueuePacket(&outapp); + } + + // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein + outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); + QueuePacket(outapp); + safe_delete(outapp); + + if(strncasecmp(zone->GetShortName(), "bazaar", 6) == 0) + SendBazaarWelcome(); + + conn_state = ZoneContentsSent; + + return; +} + +void Client::Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app) +{ + conn_state = NewZoneRequested; + + EQApplicationPacket* outapp; + + ///////////////////////////////////// + // New Zone Packet + outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + NewZone_Struct* nz = (NewZone_Struct*)outapp->pBuffer; + memcpy(outapp->pBuffer, &zone->newzone_data, sizeof(NewZone_Struct)); + strcpy(nz->char_name, m_pp.name); + + FastQueuePacket(&outapp); + + return; +} + +void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app) +{ + ////////////////////////////////////////////////////// + // Spawn Appearance Packet + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; + sa->type = AT_SpawnID; // Is 0x10 used to set the player id? + sa->parameter = GetID(); // Four bytes for this parameter... + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + // Inform the world about the client + outapp = new EQApplicationPacket(); + + CreateSpawnPacket(outapp); + 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 + SendAppearancePacket(AT_PVP, GetPVP(), true, false); + + //Send AA Exp packet: + 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 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 ); + eu->exp = (uint32)(330.0f * tmpxp); + outapp->priority = 6; + QueuePacket(outapp); + } + safe_delete(outapp); + + SendAATimers(); + + outapp = new EQApplicationPacket(OP_SendExpZonein, 0); + QueuePacket(outapp); + 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; + QueuePacket(outapp); + safe_delete(outapp); + + /* this is actually the guild MOTD + outapp = new EQApplicationPacket(OP_ZoneInSendName2, sizeof(ZoneInSendName_Struct2)); + ZoneInSendName_Struct2* zonesendname2=(ZoneInSendName_Struct2*)outapp->pBuffer; + strcpy(zonesendname2->name,m_pp.name); + QueuePacket(outapp); + safe_delete(outapp);*/ + + if(IsInAGuild()) { + SendGuildMembers(); + SendGuildURL(); + SendGuildChannel(); + SendGuildLFGuildStatus(); + } + SendLFGuildStatus(); + + //No idea why live sends this if even were not in a guild + SendGuildMOTD(); + SpawnMercOnZone(); + + return; +} + +void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app) +{ + //This is a copy of SendExpZonein created for SoF due to packet order change + //This does not affect clients other than SoF + + ////////////////////////////////////////////////////// + // Spawn Appearance Packet + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer; + sa->type = AT_SpawnID; // Is 0x10 used to set the player id? + sa->parameter = GetID(); // Four bytes for this parameter... + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + // Inform the world about the client + outapp = new EQApplicationPacket(); + + CreateSpawnPacket(outapp); + 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 + SendAppearancePacket(AT_PVP, GetPVP(), true, false); + + //Send AA Exp packet: + 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 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 ); + eu->exp = (uint32)(330.0f * tmpxp); + outapp->priority = 6; + QueuePacket(outapp); + } + safe_delete(outapp); + + SendAATimers(); + + // New for Secrets of Faydwer - Used in Place of OP_SendExpZonein + outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0); + QueuePacket(outapp); + 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; + QueuePacket(outapp); + safe_delete(outapp); + + if(IsInAGuild()) { + SendGuildMembers(); + SendGuildURL(); + SendGuildChannel(); + SendGuildLFGuildStatus(); + } + SendLFGuildStatus(); + + //No idea why live sends this if even were not in a guild + SendGuildMOTD(); + + SpawnMercOnZone(); + + return; +} + +void Client::Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app) +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_0x0347, 0); + QueuePacket(outapp); + safe_delete(outapp); + return; +} + +void Client::Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app) +{ + return; +} + +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_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 + + Handle_Connect_OP_ClientReady(app); +} + +void Client::Handle_Connect_OP_ClientReady(const EQApplicationPacket *app) +{ + conn_state = ClientReadyReceived; + + 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); + 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); +#endif + 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_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::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 = NULL; + 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 = NULL; + 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 = NULL; + 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."); + database.SetMQDetectionFlag(this->account_name,this->name, "/MQGate", 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 = NULL; + 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; + } +} + +void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) +{ + if (IsAIControlled()) + return; + + if(dead) + return; + + //currently accepting two sizes, one has an extra byte on the end + if (app->size != sizeof(PlayerPositionUpdateClient_Struct) + && app->size != (sizeof(PlayerPositionUpdateClient_Struct)+1) + ) { + LogFile->write(EQEMuLog::Error, "OP size error: OP_ClientUpdate expected:%i got:%i", sizeof(PlayerPositionUpdateClient_Struct), app->size); + return; + } + PlayerPositionUpdateClient_Struct* ppu = (PlayerPositionUpdateClient_Struct*)app->pBuffer; + + if(ppu->spawn_id != GetID()) { + // check if the id is for a boat the player is controlling + if (ppu->spawn_id == BoatID) { + Mob* boat = entity_list.GetMob(BoatID); + if (boat == 0) { // if the boat ID is invalid, reset the id and abort + BoatID = 0; + return; + } + + // set the boat's position deltas + boat->SetDeltas(ppu->delta_x, ppu->delta_y, ppu->delta_z, ppu->delta_heading); + // send an update to everyone nearby except the client controlling the boat + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + PlayerPositionUpdateServer_Struct* ppus = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer; + boat->MakeSpawnUpdate(ppus); + entity_list.QueueCloseClients(boat,outapp,true,300,this,false); + safe_delete(outapp); + // update the boat's position on the server, without sending an update + boat->GMMove(ppu->x_pos, ppu->y_pos, ppu->z_pos, EQ19toFloat(ppu->heading), false); + return; + } + else return; // if not a boat, do nothing + } + + float dist = 0; + float tmp; + tmp = x_pos - ppu->x_pos; + dist += tmp*tmp; + tmp = y_pos - ppu->y_pos; + dist += tmp*tmp; + dist = sqrt(dist); + + //the purpose of this first block may not be readily apparent + //basically it's so people don't do a moderate warp every 2.5 seconds + //letting it even out and basically getting the job done without triggering + if(dist == 0) + { + if(m_DistanceSinceLastPositionCheck > 0.0) + { + uint32 cur_time = Timer::GetCurrentTime(); + if((cur_time - m_TimeSinceLastPositionCheck) > 0) + { + float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); + float runs = GetRunspeed(); + if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) + { + if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) + { + if(IsShadowStepExempted()) + { + if(m_DistanceSinceLastPositionCheck > 800) + { + CheatDetected(MQWarpShadowStep, ppu->x_pos, ppu->y_pos, ppu->z_pos); + } + } + else if(IsKnockBackExempted()) + { + //still potential to trigger this if you're knocked back off a + //HUGE fall that takes > 2.5 seconds + if(speed > 30.0f) + { + CheatDetected(MQWarpKnockBack, ppu->x_pos, ppu->y_pos, ppu->z_pos); + } + } + else if(!IsPortExempted()) + { + if(!IsMQExemptedArea(zone->GetZoneID(), ppu->x_pos, ppu->y_pos, ppu->z_pos)) + { + if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor))) + { + m_TimeSinceLastPositionCheck = cur_time; + m_DistanceSinceLastPositionCheck = 0.0f; + CheatDetected(MQWarp, ppu->x_pos, ppu->y_pos, ppu->z_pos); + //Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT); + } + else + { + CheatDetected(MQWarpLight, ppu->x_pos, ppu->y_pos, ppu->z_pos); + } + } + } + } + } + SetShadowStepExemption(false); + SetKnockBackExemption(false); + SetPortExemption(false); + m_TimeSinceLastPositionCheck = cur_time; + m_DistanceSinceLastPositionCheck = 0.0f; + m_CheatDetectMoved = false; + } + } + else + { + m_TimeSinceLastPositionCheck = Timer::GetCurrentTime(); + m_CheatDetectMoved = false; + } + } + else + { + m_DistanceSinceLastPositionCheck += dist; + m_CheatDetectMoved = true; + if(m_TimeSinceLastPositionCheck == 0) + { + m_TimeSinceLastPositionCheck = Timer::GetCurrentTime(); + } + else + { + uint32 cur_time = Timer::GetCurrentTime(); + if((cur_time - m_TimeSinceLastPositionCheck) > 2500) + { + float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); + float runs = GetRunspeed(); + if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) + { + if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) + { + if(IsShadowStepExempted()) + { + if(m_DistanceSinceLastPositionCheck > 800) + { + //if(!IsMQExemptedArea(zone->GetZoneID(), ppu->x_pos, ppu->y_pos, ppu->z_pos)) + //{ + CheatDetected(MQWarpShadowStep, ppu->x_pos, ppu->y_pos, ppu->z_pos); + //Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT); + //} + } + } + else if(IsKnockBackExempted()) + { + //still potential to trigger this if you're knocked back off a + //HUGE fall that takes > 2.5 seconds + if(speed > 30.0f) + { + CheatDetected(MQWarpKnockBack, ppu->x_pos, ppu->y_pos, ppu->z_pos); + } + } + else if(!IsPortExempted()) + { + if(!IsMQExemptedArea(zone->GetZoneID(), ppu->x_pos, ppu->y_pos, ppu->z_pos)) + { + if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor))) + { + m_TimeSinceLastPositionCheck = cur_time; + m_DistanceSinceLastPositionCheck = 0.0f; + CheatDetected(MQWarp, ppu->x_pos, ppu->y_pos, ppu->z_pos); + //Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT); + } + else + { + CheatDetected(MQWarpLight, ppu->x_pos, ppu->y_pos, ppu->z_pos); + } + } + } + } + } + SetShadowStepExemption(false); + SetKnockBackExemption(false); + SetPortExemption(false); + m_TimeSinceLastPositionCheck = cur_time; + m_DistanceSinceLastPositionCheck = 0.0f; + } + } + + if(IsDraggingCorpse()) + DragCorpses(); + } + + //Check to see if PPU should trigger an update to the rewind position. + float rewind_x_diff = 0; + float rewind_y_diff = 0; + + rewind_x_diff = ppu->x_pos - rewind_x; + rewind_x_diff *= rewind_x_diff; + rewind_y_diff = ppu->y_pos - rewind_y; + rewind_y_diff *= rewind_y_diff; + + //We only need to store updated values if the player has moved. + //If the player has moved more than units for x or y, then we'll store + //his pre-PPU x and y for /rewind, in case he gets stuck. + if ((rewind_x_diff > 750) || (rewind_y_diff > 750)) { + rewind_x = x_pos; + rewind_y = y_pos; + rewind_z = z_pos; + } + + //If the PPU was a large jump, such as a cross zone gate or Call of Hero, + //just update rewind coords to the new ppu coords. This will prevent exploitation. + + if ((rewind_x_diff > 5000) || (rewind_y_diff > 5000)) { + rewind_x = ppu->x_pos; + rewind_y = ppu->y_pos; + rewind_z = ppu->z_pos; + } + + if(proximity_timer.Check()) { + entity_list.ProcessMove(this, ppu->x_pos, ppu->y_pos, ppu->z_pos); + if(RuleB(TaskSystem, EnableTaskSystem) && RuleB(TaskSystem,EnableTaskProximity)) + ProcessTaskProximities(ppu->x_pos, ppu->y_pos, ppu->z_pos); + proximity_x = ppu->x_pos; + proximity_y = ppu->y_pos; + proximity_z = ppu->z_pos; + } + + // Update internal state + delta_x = ppu->delta_x; + delta_y = ppu->delta_y; + delta_z = ppu->delta_z; + delta_heading = ppu->delta_heading; + heading = EQ19toFloat(ppu->heading); + + if(IsTracking() && ((x_pos!=ppu->x_pos) || (y_pos!=ppu->y_pos))){ + if(MakeRandomFloat(0, 100) < 70)//should be good + CheckIncreaseSkill(TRACKING, NULL, -20); + } + + // Break Hide if moving without sneaking and set rewind timer if moved + if(ppu->y_pos != y_pos || ppu->x_pos != x_pos){ + if((hidden || improved_hidden) && !sneaking){ + hidden = false; + improved_hidden = false; + if(!invisible) { + 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); + } + } + rewind_timer.Start(30000, true); + } + + // Outgoing client packet + if (ppu->y_pos != y_pos || ppu->x_pos != x_pos || ppu->heading != heading || ppu->animation != animation) + { + x_pos = ppu->x_pos; + y_pos = ppu->y_pos; + z_pos = ppu->z_pos; + animation = ppu->animation; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + PlayerPositionUpdateServer_Struct* ppu = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer; + MakeSpawnUpdate(ppu); + if (gmhideme) + entity_list.QueueClientsStatus(this,outapp,true,Admin(),250); + else +#ifdef PACKET_UPDATE_MANAGER + entity_list.QueueManaged(this,outapp,true,false); +#else + entity_list.QueueCloseClients(this,outapp,true,300,NULL,false); +#endif + safe_delete(outapp); + } + + if(zone->watermap) + { + if(zone->watermap->InLiquid(x_pos, y_pos, z_pos)) + { + CheckIncreaseSkill(SWIMMING, NULL, -17); + } + } + + 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_them.x = 0; + aa_los_them.y = 0; + aa_los_them.z = 0; + aa_los_them_mob = NULL; + } + 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_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(); + if(CheckLosFN(aa_los_them_mob)) + los_status = true; + else + los_status = false; + } + else + { + aa_los_me.x = GetX(); + aa_los_me.y = GetY(); + aa_los_me.z = GetZ(); + aa_los_them.x = 0; + aa_los_them.y = 0; + aa_los_them.z = 0; + aa_los_them_mob = NULL; + los_status = false; + } + } +} + +void Client::Handle_OP_AutoAttack2(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_Consent(const EQApplicationPacket *app) +{ + if(app->size<64){ + Consent_Struct* c = (Consent_Struct*)app->pBuffer; + if(strcmp(c->name, GetName()) != 0) { + ServerPacket* pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct)); + ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; + strcpy(scs->grantname, c->name); + strcpy(scs->ownername, GetName()); + scs->message_string_id = 0; + scs->permission = 1; + scs->zone_id = zone->GetZoneID(); + scs->instance_id = zone->GetInstanceID(); + //consent_list.push_back(scs->grantname); + worldserver.SendPacket(pack); + safe_delete(pack); + } + else { + Message_StringID(0, CONSENT_YOURSELF); + } + } + return; +} + +void Client::Handle_OP_ConsentDeny(const EQApplicationPacket *app) +{ + if(app->size<64){ + Consent_Struct* c = (Consent_Struct*)app->pBuffer; + ServerPacket* pack = new ServerPacket(ServerOP_Consent, sizeof(ServerOP_Consent_Struct)); + ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; + strcpy(scs->grantname, c->name); + strcpy(scs->ownername, GetName()); + scs->message_string_id = 0; + scs->permission = 0; + scs->zone_id = zone->GetZoneID(); + scs->instance_id = zone->GetInstanceID(); + //consent_list.remove(scs->grantname); + worldserver.SendPacket(pack); + safe_delete(pack); + } + return; +} + +void Client::Handle_OP_TargetMouse(const EQApplicationPacket *app) +{ + Handle_OP_TargetCommand(app); +} + +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); + 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(NULL); + SetHoTT(0); + UpdateXTargetType(TargetsTarget, NULL); + + 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(NULL); + SetHoTT(0); + UpdateXTargetType(TargetsTarget, NULL); + return; + } + + // HoTT + if (GetTarget() && GetTarget()->GetTarget()) + { + SetHoTT(GetTarget()->GetTarget()->GetID()); + UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); + } + else + { + SetHoTT(0); + UpdateXTargetType(TargetsTarget, NULL); + } + + 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(NULL); + } + 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(NULL); + } + QueuePacket(outapp); + safe_delete(outapp); + } + } + else + { + if(GetTarget()) + { + if(GetGM()) + { + GetTarget()->IsTargeted(1); + 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 = NULL; + 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*)NULL); + return; + } + else if(IsPortExempted()) + { + GetTarget()->IsTargeted(1); + return; + } + else if(IsSenseExempted()) + { + GetTarget()->IsTargeted(1); + SetSenseExemption(false); + 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 = NULL; + 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(NULL); + return; + } + } + } + else if(DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) + { + char *hacker_str = NULL; + 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(NULL); + return; + } + + GetTarget()->IsTargeted(1); + } + } + return; +} + +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(this,false,100,0,"%s ceases shielding %s.",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(14); + 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(this,false,100,0,"%s uses their shield to guard %s.",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(0, "No more than two warriors may shield the same being."); + 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 = NULL; + Group *g = NULL; + + 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 = NULL; + 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 = NULL; + 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) +{ + if (app->size != sizeof(Consume_Struct)) + { + LogFile->write(EQEMuLog::Error, "OP size error: OP_Consume expected:%i got:%i", sizeof(Consume_Struct), app->size); + return; + } + Consume_Struct* pcs = (Consume_Struct*)app->pBuffer; + if(pcs->type == 0x01) + { + if(m_pp.hunger_level > 6000) + { + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); + Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; + sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; + sta->water = m_pp.thirst_level > 6000 ? 6000 : m_pp.thirst_level; + + QueuePacket(outapp); + safe_delete(outapp); + return; + } + } + else if(pcs->type == 0x02) + { + if(m_pp.thirst_level > 6000) + { + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); + Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; + sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; + sta->water = m_pp.thirst_level > 6000 ? 6000 : m_pp.thirst_level; + + QueuePacket(outapp); + safe_delete(outapp); + return; + } + } + + + uint16 cons_mod = 180; + + switch(GetAA(aaInnateMetabolism)){ + case 1: + cons_mod = cons_mod * 110 * RuleI(Character, ConsumptionMultiplier) / 10000; + break; + case 2: + cons_mod = cons_mod * 125 * RuleI(Character, ConsumptionMultiplier) / 10000; + break; + case 3: + cons_mod = cons_mod * 150 * RuleI(Character, ConsumptionMultiplier) / 10000; + break; + default: + cons_mod = cons_mod * RuleI(Character, ConsumptionMultiplier) / 100; + break; + } + + ItemInst *myitem = GetInv().GetItem(pcs->slot); + if(myitem == NULL) { + LogFile->write(EQEMuLog::Error, "Consuming from empty slot %d", pcs->slot); + return; + } + + const Item_Struct* eat_item = myitem->GetItem(); + if (pcs->type == 0x01) { +#if EQDEBUG >= 1 + LogFile->write(EQEMuLog::Debug, "Eating from slot:%i", (int)pcs->slot); +#endif + m_pp.hunger_level += eat_item->CastTime*cons_mod; //roughly 1 item per 10 minutes + DeleteItemInInventory(pcs->slot, 1, false); + + if(pcs->auto_consumed != 0xffffffff) //no message if the client consumed for us + entity_list.MessageClose_StringID(this, true, 50, 0, EATING_MESSAGE, GetName(), eat_item->Name); + } + else if (pcs->type == 0x02) { +#if EQDEBUG >= 1 + LogFile->write(EQEMuLog::Debug, "Drinking from slot:%i", (int)pcs->slot); +#endif + // 6000 is the max. value + //m_pp.thirst_level += 1000; + m_pp.thirst_level += eat_item->CastTime*cons_mod; //roughly 1 item per 10 minutes + DeleteItemInInventory(pcs->slot, 1, false); + + if(pcs->auto_consumed != 0xffffffff) //no message if the client consumed for us + entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), eat_item->Name); + } + else { + LogFile->write(EQEMuLog::Error, "OP_Consume: unknown type, type:%i", (int)pcs->type); + return; + } + if (m_pp.hunger_level > 50000) + m_pp.hunger_level = 50000; + if (m_pp.thirst_level > 50000) + m_pp.thirst_level = 50000; + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); + Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; + sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; + sta->water = m_pp.thirst_level > 6000 ? 6000 : m_pp.thirst_level; + + QueuePacket(outapp); + safe_delete(outapp); + return; +} + +void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ItemVerifyRequest_Struct)) + { + LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemVerifyRequest expected:%i got:%i", sizeof(ItemVerifyRequest_Struct), app->size); + return; + } + + ItemVerifyRequest_Struct* request = (ItemVerifyRequest_Struct*)app->pBuffer; + int32 slot_id; + int32 target_id; + int32 spell_id = 0; + slot_id = request->slot; + target_id = request->target; + + + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_ItemVerifyReply, sizeof(ItemVerifyReply_Struct)); + ItemVerifyReply_Struct* reply = (ItemVerifyReply_Struct*)outapp->pBuffer; + reply->slot = slot_id; + reply->target = target_id; + + QueuePacket(outapp); + safe_delete(outapp); + + + if (IsAIControlled()) { + this->Message_StringID(13,NOT_IN_CONTROL); + return; + } + + if(slot_id < 0) { + LogFile->write(EQEMuLog::Debug, "Unknown slot being used by %s, slot being used is: %i",GetName(),request->slot); + return; + } + + const ItemInst* inst = m_inv[slot_id]; + if (!inst) { + Message(0, "Error: item not found in inventory slot #%i", slot_id); + DeleteItemInInventory(slot_id,0,true); + return; + } + + const Item_Struct* item = inst->GetItem(); + if (!item) { + Message(0, "Error: item not found in inventory slot #%i", slot_id); + DeleteItemInInventory(slot_id,0,true); + return; + } + + spell_id = item->Click.Effect; + + if + ( + spell_id > 0 && + ( + !IsValidSpell(spell_id) || + casting_spell_id || + delaytimer || + spellend_timer.Enabled() || + IsStunned() || + IsFeared() || + IsMezzed() || + DivineAura() || + (IsSilenced() && !IsDiscipline(spell_id)) || + (IsAmnesiad() && IsDiscipline(spell_id)) || + (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) + ) + ) + { + //Message(13, "Error: the item effect can not be used at this time"); + 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 < 30) || (slot_id == 9999) || (slot_id > 250 && slot_id < 331 && ((item->ItemType == ItemTypePotion) || item->PotionBelt))) // sanity check + { + ItemInst* p_inst = (ItemInst*)inst; + + if(parse->ItemHasQuestSub(p_inst, "EVENT_ITEM_CLICK")) + { + parse->EventItem(EVENT_ITEM_CLICK, this, p_inst, p_inst->GetID(), slot_id); + inst = m_inv[slot_id]; + if (!inst) + { + // Item was deleted by the perl event + return; + } + } + + 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 ")) + { + 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) + { + if(parse->ItemHasQuestSub(p_inst, "EVENT_ITEM_CLICK_CAST")) + { + //TODO: need to enforce and set recast timers here because the spell may not be cast. + parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, p_inst->GetID(), slot_id); + inst = m_inv[slot_id]; + if (!inst) + { + // Item was deleted by the perl event + return; + } + } + else + { + CastSpell(item->Click.Effect, target_id, 10, item->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 + uint16 cons_mod = 180; + switch(GetAA(aaInnateMetabolism)) + { + case 1: + cons_mod = cons_mod * 110 * RuleI(Character, ConsumptionMultiplier) / 10000; + break; + case 2: + cons_mod = cons_mod * 125 * RuleI(Character, ConsumptionMultiplier) / 10000; + break; + case 3: + cons_mod = cons_mod * 150 * RuleI(Character, ConsumptionMultiplier) / 10000; + break; + default: + cons_mod = cons_mod * RuleI(Character, ConsumptionMultiplier) / 100; + break; + } + + if (item->ItemType == ItemTypeFood && m_pp.hunger_level < 5000) + { +#if EQDEBUG >= 1 + LogFile->write(EQEMuLog::Debug, "Eating from slot:%i", slot_id); +#endif + m_pp.hunger_level += item->CastTime*cons_mod; //roughly 1 item per 10 minutes + DeleteItemInInventory(slot_id, 1, false); + entity_list.MessageClose_StringID(this, true, 50, 0, EATING_MESSAGE, GetName(), item->Name); + } + else if (item->ItemType == ItemTypeDrink && m_pp.thirst_level < 5000) + { +#if EQDEBUG >= 1 + LogFile->write(EQEMuLog::Debug, "Drinking from slot:%i", slot_id); +#endif + // 6000 is the max. value + m_pp.thirst_level += item->CastTime*cons_mod; //roughly 1 item per 10 minutes + DeleteItemInInventory(slot_id, 1, false); + entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name); + } + 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, NULL, 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 = NULL; + 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(SLOT_CURSOR, *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, restime; + 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; + } + + 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(BEGGING) || !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(BEGGING); + + 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(BEGGING, NULL, -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 = NULL; + 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->IsInvisible(this) && assistee->GetTarget()) + { + Mob *new_target = assistee->GetTarget(); + if + ( + new_target && + !new_target->IsInvisible(this) && + (GetGM() || (Dist(*assistee) <= TARGETING_RANGE && + Dist(*new_target) <= TARGETING_RANGE)) + ) + { + 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)) + return; + 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()) + 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()); + else + initiator->CastToClient()->Message_StringID(10,DUEL_DECLINE,entity->GetName()); + return; +} + +void Client::Handle_OP_DuelResponse2(const EQApplicationPacket *app) +{ + if(app->size != sizeof(Duel_Struct)) + return; + + 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; + + ds2->duel_initiator = entity->GetID(); + ds2->duel_target = entity->GetID(); + initiator->CastToClient()->QueuePacket(outapp); + + outapp->SetOpcode(OP_DuelResponse2); + ds2->duel_initiator = initiator->GetID(); + + initiator->CastToClient()->QueuePacket(outapp); + + QueuePacket(outapp); + SetDueling(true); + initiator->CastToClient()->SetDueling(true); + SetDuelTarget(ds->duel_initiator); + safe_delete(outapp); + + if (IsCasting()) + InterruptSpell(); + if (initiator->CastToClient()->IsCasting()) + initiator->CastToClient()->InterruptSpell(); + } + return; +} + +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_SpawnAppearance(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SpawnAppearance_Struct)) { + cout << "Wrong size on OP_SpawnAppearance. Got: " << app->size << ", Expected: " << sizeof(SpawnAppearance_Struct) << 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(HIDE) && GetSkill(HIDE) == 0) + { + if(GetClientVersion() < EQClientSoF) + { + char *hack_str = NULL; + 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 + cout << "Illusion packet recv'd:" << endl; + DumpPacket(app); + } + */ + else { + cerr << "Client " << name << " unknown apperance " << (int)sa->parameter << 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 { + cerr << "Client " << name << " unknown Anon/Roleplay Switch " << (int)sa->parameter << 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(SNEAK)) + { + char *hack_str = NULL; + 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 = NULL; + 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 { + cout << "Unknown SpawnAppearance type: 0x" << hex << setw(4) << setfill('0') << sa->type << dec + << " value: 0x" << hex << setw(8) << setfill('0') << sa->parameter << dec << endl; + } + return; +} + +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_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, (SkillType)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)); + 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); + 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) + { + 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(this->GetTarget() && this->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_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 < 30 || mi->from_slot > 39) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) + { + char *detect = NULL; + 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 >= 251 && mi->from_slot <= 340) { + if(mi->from_slot > 330) { 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 >= 251 && mi->to_slot <= 340) { + if(mi->to_slot > 330) { mi_hack = true; } + else { + int16 to_parent = m_inv.CalcSlotId(mi->to_slot); + if(!m_inv[to_parent]) { mi_hack = true; } + else if(!m_inv[to_parent]->IsType(ItemClassContainer)) { mi_hack = true; } + else if(m_inv.CalcBagIdx(mi->to_slot) >= m_inv[to_parent]->GetItem()->BagSlots) { mi_hack = true; } + } + } + + if(mi_hack) { Message(15, "Caution: Illegal use of inaccessable bag slots!"); } + + if(!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) { SwapItemResync(mi); } + + return; +} + +void Client::Handle_OP_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) +{ + //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_FeignDeath(const EQApplicationPacket *app) +{ + if(GetClass() != MONK) + return; + 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; + } + p_timers.Start(pTimerFeignDeath, reuse-1); + + //BreakInvis(); + + uint16 primfeign = GetSkill(FEIGN_DEATH); + uint16 secfeign = GetSkill(FEIGN_DEATH); + if (primfeign > 100) { + primfeign = 100; + secfeign = secfeign - 100; + secfeign = secfeign / 2; + } + else + secfeign = 0; + + uint16 totalfeign = primfeign + secfeign; + if (MakeRandomFloat(0, 160) > totalfeign) { + SetFeigned(false); + entity_list.MessageClose_StringID(this, false, 200, 10, STRING_FEIGNFAILED, GetName()); + } + else { + SetFeigned(true); + } + + CheckIncreaseSkill(FEIGN_DEATH, NULL, 5); + return; +} + +void Client::Handle_OP_Sneak(const EQApplicationPacket *app) +{ + if(!HasSkill(SNEAK) && GetSkill(SNEAK) == 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(SNEAK, NULL, 5); + } + float hidechance = ((GetSkill(SNEAK)/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_Hide(const EQApplicationPacket *app) +{ + if(!HasSkill(HIDE) && GetSkill(HIDE) == 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(HIDE)/250.0f) + .25) * 100; + float random = MakeRandomFloat(0, 100); + CheckIncreaseSkill(HIDE, NULL, 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; + if (!auto_attack && entity_list.Fighting(this)) { + if (MakeRandomInt(0, 260) < (int)GetSkill(HIDE)) { + msg->string_id=343; + entity_list.Evade(this); + } else { + msg->string_id=344; + } + } else { + if (hidden){ + msg->string_id=346; + } + else { + msg->string_id=345; + } + } + FastQueuePacket(&outapp); + } + return; +} + +void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app) +{ + ChannelMessage_Struct* cm=(ChannelMessage_Struct*)app->pBuffer; + + if (app->size < sizeof(ChannelMessage_Struct)) { + cout << "Wrong size " << app->size << ", should be " << sizeof(ChannelMessage_Struct) << "+ on 0x" << hex << setfill('0') << setw(4) << app->GetOpcode() << dec << 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_WearChange(const EQApplicationPacket *app) +{ + if (app->size != sizeof(WearChange_Struct)) { + cout << "Wrong size: OP_WearChange, size=" << app->size << ", expected " << sizeof(WearChange_Struct) << endl; + //DumpPacket(app); + return; + } + + WearChange_Struct* wc=(WearChange_Struct*)app->pBuffer; + //printf("Wearchange:\n"); + //DumpPacket(app); + 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; +} + +//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)) { + cout << "Wrong size on OP_WhoAll. Got: " << app->size << ", Expected: " << sizeof(Who_All_Struct) << endl; + //DumpPacket(app); + 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; + FriendsWho(FriendsString); + return; +} + +void Client::Handle_OP_GMZoneRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMZoneRequest_Struct)) { + cout << "Wrong size on OP_GMZoneRequest. Got: " << app->size << ", Expected: " << sizeof(GMZoneRequest_Struct) << endl; + return; + } + if (this->Admin() < minStatusToBeGM) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/zone"); + return; + } + + GMZoneRequest_Struct* gmzr = (GMZoneRequest_Struct*)app->pBuffer; + float tarx = -1, tary = -1, tarz = -1; + + int16 minstatus = 0; + uint8 minlevel = 0; + char tarzone[32]; + uint16 zid = gmzr->zone_id; + if (gmzr->zone_id == 0) + zid = zonesummon_id; + const char * zname = database.GetZoneName(zid); + if(zname == NULL) + tarzone[0] = 0; + else + strcpy(tarzone, zname); + + // this both loads the safe points and does a sanity check on zone name + if (!database.GetSafePoints(tarzone, 0, &tarx, &tary, &tarz, &minstatus, &minlevel)) { + tarzone[0] = 0; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMZoneRequest, sizeof(GMZoneRequest_Struct)); + GMZoneRequest_Struct* gmzr2 = (GMZoneRequest_Struct*) outapp->pBuffer; + strcpy(gmzr2->charname, this->GetName()); + gmzr2->zone_id = gmzr->zone_id; + gmzr2->x = tarx; + gmzr2->y = tary; + gmzr2->z = tarz; + // Next line stolen from ZoneChange as well... - This gives us a nicer message than the normal "zone is down" message... + if (tarzone[0] != 0 && admin >= minstatus && GetLevel() >= minlevel) + gmzr2->success = 1; + else { + cout << "GetZoneSafeCoords failed. zoneid = " << gmzr->zone_id << "; czone = " << zone->GetZoneID() << endl; + gmzr2->success = 0; + } + + QueuePacket(outapp); + safe_delete(outapp); + return; +} + +void Client::Handle_OP_GMZoneRequest2(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToBeGM) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/zone"); + return; + } + if (app->size < sizeof(uint32)) { + LogFile->write(EQEMuLog::Error, "OP size error: OP_GMZoneRequest2 expected:%i got:%i", sizeof(uint32), app->size); + return; + } + + uint32 zonereq = *((uint32 *)app->pBuffer); + GoToSafeCoords(zonereq, 0); + return; +} + +void Client::Handle_OP_EndLootRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + cout << "Wrong size: OP_EndLootRequest, size=" << app->size << ", expected " << sizeof(uint32) << endl; + return; + } + + SetLooting(false); + + Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); + if (entity == 0) { + //DumpPacket(app); + 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)) { + cout << "Wrong size: OP_LootRequest, size=" << app->size << ", expected " << sizeof(uint32) << 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 { + cout << "npc == 0 LOOTING FOOKED3" << 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(SENSE_TRAPS)) + { + if(DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + HandleLDoNSenseTraps(target->CastToNPC(), GetSkill(SENSE_TRAPS), 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(DISARM_TRAPS)) + { + if(DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + HandleLDoNDisarm(target->CastToNPC(), GetSkill(DISARM_TRAPS), 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(PICK_LOCK)) + { + if(DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + HandleLDoNPickLock(target->CastToNPC(), GetSkill(PICK_LOCK), 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: %i\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 %i\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 %i\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)) { + cout << "Wrong size: OP_GuildInvite, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << 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)) { + cout << "Wrong size: OP_GuildRemove, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << 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)) { + cout << "Wrong size: OP_GuildInviteAccept, size=" << app->size << ", expected " << sizeof(GuildJoin_Struct) << 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); + + } + } +} + +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)) { + cout << "Wrong size on OP_SwapSpell. Got: " << app->size << ", Expected: " << sizeof(SwapSpell_Struct) << 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)) { + cout << "Wrong size: OP_CastSpell, size=" << app->size << ", expected " << sizeof(CastSpell_Struct) << 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)) // this means item + { + //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 < 30) || (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; + if(parse->ItemHasQuestSub(p_inst, "EVENT_ITEM_CLICK_CAST")) + { + parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, p_inst->GetID(), castspell->inventoryslot); + SendSpellBarEnable(castspell->spell_id); + return; + } + else + { + CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); + } + } + 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; + if(parse->ItemHasQuestSub(p_inst, "EVENT_ITEM_CLICK_CAST")) + { + parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, p_inst->GetID(), castspell->inventoryslot); + SendSpellBarEnable(castspell->spell_id); + return; + } + else + { + CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); + } + } + } + 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 >= 30 (0x%04x)", castspell->inventoryslot); + InterruptSpell(castspell->spell_id); + } + } + else // ability, or regular memmed spell + { + uint16 spell_to_cast = 0; + + //current client seems to send LH in slot 8 now... + if(castspell->slot == ABILITY_SPELL_SLOT && + 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->slot == ABILITY_SPELL_SLOT && + (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; + } + + if(GetLevel() < 40) + spell_to_cast = SPELL_HARM_TOUCH; + else + spell_to_cast = SPELL_HARM_TOUCH2; + p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); + } + + //handle disciplines, OLD, they keep changing this + 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; + } + + 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; + } + } + /* + these are coming through with slot 8 now... + else if(castspell->slot == 9) //discipline, LoH, HT, etc + { + if(GetClass() == PALADIN && castspell->spell_id == SPELL_LAY_ON_HANDS) + { + spell_to_cast = SPELL_LAY_ON_HANDS; + p_timers.Start(pTimerLayHands, LayOnHandsReuseTime); + CastSpell(spell_to_cast, castspell->target_id, castspell->slot); + } + else if(GetClass() == SHADOWKNIGHT + && (castspell->spell_id == SPELL_HARM_TOUCH || castspell->spell_id == SPELL_HARM_TOUCH2)) + { + if(GetLevel() < 40) + spell_to_cast = SPELL_HARM_TOUCH; + else + spell_to_cast = SPELL_HARM_TOUCH2; + p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); + } + else*/ + //try disciplines + + CastSpell(spell_to_cast, castspell->target_id, castspell->slot); + } + return; +} + +void Client::Handle_OP_DeleteItem(const EQApplicationPacket *app) +{ + if (app->size != sizeof(DeleteItem_Struct)) { + cout << "Wrong size on OP_DeleteItem. Got: " << app->size << ", Expected: " << sizeof(DeleteItem_Struct) << 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(ALCOHOL_TOLERANCE, NULL, 25); + + int16 AlcoholTolerance = GetSkill(ALCOHOL_TOLERANCE); + 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)) { + cout << "Wrong size on OP_CombatAbility. Got: " << app->size << ", Expected: " << sizeof(CombatAbility_Struct) << endl; + return; + } + OPCombatAbility(app); + return; +} + +void Client::Handle_OP_Taunt(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClientTarget_Struct)) { + cout << "Wrong size on OP_Taunt. Got: " << app->size << ", Expected: "<< sizeof(ClientTarget_Struct) << 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() == NULL || !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) +{ + if (app->size != sizeof(Resurrect_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_RezzAnswer, size=%i, expected %i", app->size, sizeof(Resurrect_Struct)); + return; + } + 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); + } + return; +} + +void Client::Handle_OP_GMSummon(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMSummon_Struct)) { + cout << "Wrong size on OP_GMSummon. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << 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,104); + other->Message_StringID(13,104); + 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=3000; slot_id<=3007; 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=3000; slot_id<=3003; 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)) { + cout << "Wrong size on OP_GMLastName. Got: " << app->size << ", Expected: " << sizeof(GMLastName_Struct) << 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)) { + cout << "Wrong size on OP_GMToggle. Got: " << app->size << ", Expected: " << sizeof(GMToggle_Struct) << 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)) { + cout << "Wrong size on OP_LFGCommand. Got: " << app->size << ", Expected: " << sizeof(LFG_Struct) << 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)) { + cout << "Wrong size on OP_GMGoto. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << 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 != NULL) { + 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 NULL 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; + } + + 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 = NULL; + 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)) + 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) { + charges = mp->quantity; + } else { + // this needs expanded to handle varying charges from the merchant, + // but will require merchantlist_temp changes amonst other things. + 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); + + mpo->price = SinglePrice * mp->quantity; + if(mpo->price < 0 ) + { + safe_delete(outapp); + safe_delete(inst); + return; + } + + if(!TakeMoneyFromPP(mpo->price)) + { + char *hacker_str = NULL; + 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 == SLOT_CURSOR) { + if(m_inv.GetItem(SLOT_CURSOR) != NULL) { + Message(13, "You do not have room for any more items."); + safe_delete(outapp); + safe_delete(inst); + return; + } + } + + if(freeslotid == SLOT_INVALID) + { + Message(13, "You do not have room for any more items."); + safe_delete(outapp); + safe_delete(inst); + return; + } + + string packet; + if(mp->quantity==1 && item->MaxCharges>0 && item->MaxCharges<255) + mp->quantity=item->MaxCharges; + + 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(); + cout << "At 1: " << t1.getDuration() << 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; + } + if (RuleB(Merchant, UsePriceMod)){ + price=(int)((item->Price*mp->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*mp->quantity)*(RuleR(Merchant, BuyCostMod))+0.5); + AddMoneyToPP(price,false); + + if (inst->IsStackable()) + { + unsigned int i_quan = inst->GetCharges(); + if (mp->quantity > i_quan) + mp->quantity = i_quan; + } + else + { + mp->quantity = 1; + } + + if (RuleB(EventLog, RecordSellToMerchant)) + LogMerchant(this, vendor, mp->quantity, price, item, false); + + int freeslot = 0; + int charges = 0; + if(inst->IsStackable()) + charges = mp->quantity; + else + //charges = inst->GetCharges(); + //FIXME: Temp merchant table uses 'charges' as the quantity, so doesn't properly handle charged items. + charges = 1; + + if((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); + + 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(); + cout << "Save took: " << t1.getDuration() << 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(NULL); + + 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(NULL); + + 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); + //TODO: should enforce range checking here. + if (entity && entity->IsObject()) { + Object* object = entity->CastToObject(); + object->HandleClick(this, click_object); + + char buf[10]; + snprintf(buf, 9, "%u", click_object->drop_id); + buf[9] = '\0'; + parse->EventPlayer(EVENT_CLICK_OBJECT, this, buf, 0); + } + + // 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.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.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 == NULL) { + 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))!=NULL) { + 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; + } + /*if (m_tradeskill_object == NULL) { + 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 + 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 %u", cd->doorid, zone->GetInstanceVersion()); + buf[19] = '\0'; + parse->EventPlayer(EVENT_CLICKDOOR, this, buf, 0); + + currentdoor->HandleClick(this,0); + return; +} + +void Client::Handle_OP_CreateObject(const EQApplicationPacket *app) +{ + DropItem(SLOT_CURSOR); + 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; +} + +void Client::Handle_OP_GroupCancelInvite(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GroupCancel_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for OP_GroupCancelInvite: Expected: %i, Got: %i", + sizeof(GroupCancel_Struct), app->size); + return; + } + + GroupCancel_Struct* gf = (GroupCancel_Struct*) app->pBuffer; + Mob* inviter = entity_list.GetClientByName(gf->name1); + + if(inviter != NULL) + { + if(inviter->IsClient()) + inviter->CastToClient()->QueuePacket(app); + } + else + { + ServerPacket* pack = new ServerPacket(ServerOP_GroupCancelInvite, sizeof(GroupCancel_Struct)); + memcpy(pack->pBuffer, gf, sizeof(GroupCancel_Struct)); + worldserver.SendPacket(pack); + safe_delete(pack); + } + + database.SetGroupID(GetName(), 0, CharacterID()); + return; +} + +void Client::Handle_OP_GroupFollow(const EQApplicationPacket *app) +{ + Handle_OP_GroupFollow2(app); +} + +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) { + // 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 != NULL && 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; + } + } + } + + // Remove the merc from the old group + if (GetMerc()) + GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); + + 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()); + + // Add the merc back into the new group + if (GetMerc()) + { + if (GetMerc()->AddMercToGroup(GetMerc(), group)) + { + database.SetGroupID(GetMerc()->GetName(), group->GetID(), inviter->CastToClient()->CharacterID(), true); + } + } + + 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(GetMerc()) + { + group->AddMember(GetMerc()); + } + + 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()); + + //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 == NULL) + { + 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_GroupDisband(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GroupGeneric_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for GroupGeneric_Struct: Expected: %i, Got: %i", + sizeof(GroupGeneric_Struct), app->size); + return; + } + + LogFile->write(EQEMuLog::Debug, "Member Disband Request from %s\n", GetName()); + + GroupGeneric_Struct* gd = (GroupGeneric_Struct*) app->pBuffer; + + Raid *raid = entity_list.GetRaidByClient(this); + if(raid){ + Mob* memberToDisband = NULL; + + if(!raid->IsGroupLeader(GetName())) + memberToDisband = this; + else + memberToDisband = GetTarget(); + + if(!memberToDisband) + memberToDisband = entity_list.GetMob(gd->name2); + + if(!memberToDisband) + memberToDisband = this; + + if(!memberToDisband->IsClient()) + return; + + //we have a raid.. see if we're in a raid group + uint32 grp = raid->GetGroup(memberToDisband->GetName()); + bool wasGrpLdr = raid->members[raid->GetPlayerIndex(memberToDisband->GetName())].IsGroupLeader; + if(grp < 12){ + if(wasGrpLdr){ + raid->SetGroupLeader(memberToDisband->GetName(), false); + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(raid->members[x].GroupNumber == grp) + { + if(strlen(raid->members[x].membername) > 0 && strcmp(raid->members[x].membername, memberToDisband->GetName()) != 0) + { + raid->SetGroupLeader(raid->members[x].membername); + break; + } + } + } + } + raid->MoveMember(memberToDisband->GetName(), 0xFFFFFFFF); + raid->GroupUpdate(grp); //break + //raid->SendRaidGroupRemove(memberToDisband->GetName(), grp); + //raid->SendGroupUpdate(memberToDisband->CastToClient()); + raid->SendGroupDisband(memberToDisband->CastToClient()); + } + //we're done + return; + } + + Group* group = GetGroup(); + + if(!group) + return; + +#ifdef BOTS + // this block is necessary to allow more control over controlling how bots are zoned or camped. + if(Bot::GroupHasBot(group)) { + if(group->IsLeader(this)) { + if((GetTarget() == 0 || GetTarget() == this) || (group->GroupCount() < 3)) { + Bot::ProcessBotGroupDisband(this, std::string()); + } else { + Mob* tempMember = entity_list.GetMob(gd->name2); + if(tempMember) { + if(tempMember->IsBot()) + Bot::ProcessBotGroupDisband(this, std::string(tempMember->GetCleanName())); + } + } + } + } +#endif + if((group->IsLeader(this) && (GetTarget() == 0 || GetTarget() == this)) || (group->GroupCount()<3)) { + group->DisbandGroup(); + if(GetMerc() != NULL) + GetMerc()->Suspend(); + } else { + Mob* memberToDisband = NULL; + memberToDisband = GetTarget(); + + if(!memberToDisband) + memberToDisband = entity_list.GetMob(gd->name2); + if(memberToDisband ) + { + if(group->IsLeader(this)) // the group leader can kick other members out of the group... + { + group->DelMember(memberToDisband,false); + if(memberToDisband->IsClient()) + { + Client* memberClient = memberToDisband->CastToClient(); + Merc* memberMerc = memberToDisband->CastToClient()->GetMerc(); + if(memberMerc != NULL) + { + memberMerc->RemoveMercFromGroup(memberMerc, group); + if(!memberMerc->IsGrouped() && !memberClient->IsGrouped()) + { + Group *g = new Group(memberClient); + if(memberMerc->AddMercToGroup(memberMerc, g)) { + entity_list.AddGroup(g); + database.SetGroupLeaderName(g->GetID(), memberClient->GetName()); + g->SaveGroupLeaderAA(); + database.SetGroupID(memberClient->GetName(), g->GetID(), memberClient->CharacterID()); + database.SetGroupID(memberMerc->GetName(), g->GetID(), memberClient->CharacterID(), true); + database.RefreshGroupFromDB(memberClient); + } + } + } + } + else if(memberToDisband->IsMerc()) + { + memberToDisband->CastToMerc()->Suspend(); + } + } + else + { // ...but other members can only remove themselves + group->DelMember(this,false); + + if(!IsGrouped() && GetMerc() != NULL) + { + if(!IsGrouped()) { + Group *g = new Group(this); + if(GetMerc()->AddMercToGroup(GetMerc(), g)) { + entity_list.AddGroup(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 + LogFile->write(EQEMuLog::Error, "Failed to remove player from group. Unable to find player named %s in player group", gd->name2); + } + if(LFP) { + // If we are looking for players, update to show we are on our own now. + UpdateLFP(); + } + + return; +} + +void Client::Handle_OP_GroupDelete(const EQApplicationPacket *app) +{ +//should check for leader, only they should be able to do this.. + Group* group = GetGroup(); + if (group) + group->DisbandGroup(); + + if(LFP) + UpdateLFP(); + + return; +} + +void Client::Handle_OP_GMEmoteZone(const EQApplicationPacket *app) +{ + if(this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/emote"); + return; + } + if (app->size != sizeof(GMEmoteZone_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMEmoteZone, size=%i, expected %i", app->size, sizeof(GMEmoteZone_Struct)); + return; + } + GMEmoteZone_Struct* gmez = (GMEmoteZone_Struct*)app->pBuffer; + char* newmessage=0; + if(strstr(gmez->text,"^")==0) + entity_list.Message(0, 15, gmez->text); + else{ + for(newmessage = strtok((char*)gmez->text,"^");newmessage!=NULL;newmessage=strtok(NULL, "^")) + entity_list.Message(0, 15, newmessage); + } + return; +} + +void Client::Handle_OP_InspectRequest(const EQApplicationPacket *app) { + + if(app->size != sizeof(Inspect_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectRequest, size=%i, expected %i", app->size, sizeof(Inspect_Struct)); + return; + } + + Inspect_Struct* ins = (Inspect_Struct*) app->pBuffer; + Mob* tmp = entity_list.GetMob(ins->TargetID); + + if(tmp != 0 && tmp->IsClient()) { + if(tmp->CastToClient()->GetClientVersion() < EQClientSoF) { tmp->CastToClient()->QueuePacket(app); } // Send request to target + // Inspecting an SoF or later client will make the server handle the request + else { ProcessInspectRequest(tmp->CastToClient(), this); } + } + +#ifdef BOTS + if(tmp != 0 && tmp->IsBot()) { Bot::ProcessBotInspectionRequest(tmp->CastToBot(), this); } +#endif + + return; +} + +void Client::Handle_OP_InspectAnswer(const EQApplicationPacket *app) { + + if (app->size != sizeof(InspectResponse_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectAnswer, size=%i, expected %i", app->size, sizeof(InspectResponse_Struct)); + return; + } + + //Fills the app sent from client. + EQApplicationPacket* outapp = app->Copy(); + InspectResponse_Struct* insr = (InspectResponse_Struct*) outapp->pBuffer; + Mob* tmp = entity_list.GetMob(insr->TargetID); + const Item_Struct* item = NULL; + + for (int16 L = 0; L <= 20; L++) { + const ItemInst* inst = GetInv().GetItem(L); + item = inst ? inst->GetItem() : NULL; + + if(item) { + strcpy(insr->itemnames[L], item->Name); + insr->itemicons[L] = item->Icon; + } + else { insr->itemicons[L] = 0xFFFFFFFF; } + } + + const ItemInst* inst = GetInv().GetItem(21); + item = inst ? inst->GetItem() : NULL; + + if(item) { + strcpy(insr->itemnames[22], item->Name); + insr->itemicons[22] = item->Icon; + } + else { insr->itemicons[22] = 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: %i, 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: %i, 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 %d!\n", app->size, sizeof(BugStruct)); + else{ + BugStruct* bug=(BugStruct*)app->pBuffer; + database.UpdateBug(bug); + } + return; +} + +void Client::Handle_OP_Petition(const EQApplicationPacket *app) +{ + if (app->size <= 1) + return; + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + /*else if(petition_list.FindPetitionByAccountName(this->AccountName())) + { + Message(0,"You already have a petition in queue, you cannot petition again until this one has been responded to or you have deleted the petition."); + return; + }*/ + else + { + if(petition_list.FindPetitionByAccountName(AccountName())) + { + Message(0,"You already have a petition in the queue, you must wait for it to be answered or use /deletepetition to delete it."); + return; + } + Petition* pet = new Petition(CharacterID()); + pet->SetAName(this->AccountName()); + pet->SetClass(this->GetClass()); + pet->SetLevel(this->GetLevel()); + pet->SetCName(this->GetName()); + pet->SetRace(this->GetRace()); + pet->SetLastGM(""); + pet->SetCName(this->GetName()); + pet->SetPetitionText((char*) app->pBuffer); + pet->SetZone(zone->GetZoneID()); + pet->SetUrgency(0); + petition_list.AddPetition(pet); + database.InsertPetitionToDB(pet); + petition_list.UpdateGMQueue(); + petition_list.UpdateZoneListQueue(); + worldserver.SendEmoteMessage(0, 0, 80, 15, "%s has made a petition. #%i", GetName(), pet->GetID()); + } + return; +} + +void Client::Handle_OP_PetitionCheckIn(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Petition_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionCheckIn, size=%i, expected %i", app->size, sizeof(Petition_Struct)); + return; + } + Petition_Struct* inpet = (Petition_Struct*) app->pBuffer; + + Petition* pet = petition_list.GetPetitionByID(inpet->petnumber); + //if (inpet->urgency != pet->GetUrgency()) + pet->SetUrgency(inpet->urgency); + pet->SetLastGM(this->GetName()); + pet->SetGMText(inpet->gmtext); + + pet->SetCheckedOut(false); + petition_list.UpdatePetition(pet); + petition_list.UpdateGMQueue(); + petition_list.UpdateZoneListQueue(); + return; +} + +void Client::Handle_OP_PetitionResolve(const EQApplicationPacket *app) +{ + Handle_OP_PetitionDelete(app); +} + +void Client::Handle_OP_PetitionDelete(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PetitionUpdate_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionDelete, size=%i, expected %i", app->size, sizeof(PetitionUpdate_Struct)); + return; + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PetitionUpdate,sizeof(PetitionUpdate_Struct)); + PetitionUpdate_Struct* pet = (PetitionUpdate_Struct*) outapp->pBuffer; + pet->petnumber = *((int*) app->pBuffer); + pet->color = 0x00; + pet->status = 0xFFFFFFFF; + pet->senttime = 0; + strcpy(pet->accountid, ""); + strcpy(pet->gmsenttoo, ""); + pet->quetotal = petition_list.GetTotalPetitions(); + strcpy(pet->charname, ""); + FastQueuePacket(&outapp); + + if (petition_list.DeletePetition(pet->petnumber) == -1) + cout << "Something is borked with: " << pet->petnumber << endl; + petition_list.ClearPetitions(); + petition_list.UpdateGMQueue(); + petition_list.ReadDatabase(); + petition_list.UpdateZoneListQueue(); + return; +} + +void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PetCommand_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetCommands, size=%i, expected %i", app->size, sizeof(PetCommand_Struct)); + return; + } + char val1[20]={0}; + PetCommand_Struct* pet = (PetCommand_Struct*) app->pBuffer; + Mob* mypet = this->GetPet(); + + if(!mypet || pet->command == PET_LEADER) + { + if(pet->command == PET_LEADER) + { + if(mypet && (!GetTarget() || GetTarget() == mypet)) + { + mypet->Say_StringID(PET_LEADERIS, GetName()); + } + else if((mypet = GetTarget())) + { + Mob *Owner = mypet->GetOwner(); + if(Owner) + mypet->Say_StringID(PET_LEADERIS, Owner->GetCleanName()); + else + mypet->Say_StringID(I_FOLLOW_NOONE); + } + } + + return; + } + + if(mypet->GetPetType() == petAnimation && (pet->command != PET_HEALTHREPORT && pet->command != PET_GETLOST) && !GetAA(aaAnimationEmpathy)) + return; + + // just let the command "/pet get lost" work for familiars + if(mypet->GetPetType() == petFamiliar && pet->command != PET_GETLOST) + return; + + uint32 PetCommand = pet->command; + + // Handle Sit/Stand toggle in UF and later. + if(GetClientVersion() >= EQClientUnderfoot) + { + if(PetCommand == PET_SITDOWN) + if(mypet->GetPetOrder() == SPO_Sit) + PetCommand = PET_STANDUP; + } + + switch(PetCommand) + { + case PET_ATTACK: { + if (!GetTarget()) + break; + if (GetTarget()->IsMezzed()) { + Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), GetTarget()->GetCleanName()); + break; + } + if (mypet->IsFeared()) break; //prevent pet from attacking stuff while feared + + if((mypet->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(10, 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(PET_CALMING); + mypet->WipeHateList(); + mypet->SetTarget(NULL); + } + break; + } + case PET_HEALTHREPORT: { + Message_StringID(10, 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(NULL); + } + + mypet->Say_StringID(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(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(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(0,"%s says, 'Now taunting foes, Master!",mypet->GetCleanName()); + mypet->CastToNPC()->SetTaunting(true); + } + break; + } + case PET_NOTAUNT: { + if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + Message(0,"%s says, 'No longer taunting foes, Master!",mypet->GetCleanName()); + 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(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(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(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(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("I will hold until given an order, master."); + mypet->WipeHateList(); + mypet->SetHeld(true); + } + break; + } + case PET_NOCAST: { + if(GetAA(aaAdvancedPetDiscipline) == 2 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (mypet->IsNoCast()) { + Message(0,"%s says, 'I will now cast spells, Master!",mypet->GetCleanName()); + mypet->CastToNPC()->SetNoCast(false); + } else { + Message(0,"%s says, 'I will no longer cast spells, Master!",mypet->GetCleanName()); + mypet->CastToNPC()->SetNoCast(true); + } + } + break; + } + case PET_FOCUS: { + if(GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (mypet->IsFocused()) { + Message(0,"%s says, 'I am no longer focused, Master!",mypet->GetCleanName()); + mypet->CastToNPC()->SetFocused(false); + } else { + Message(0,"%s says, 'I will now focus my attention, Master!",mypet->GetCleanName()); + mypet->CastToNPC()->SetFocused(true); + } + } + break; + } + case PET_FOCUS_ON: { + if(GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (mypet->IsFocused()) { + Message(0,"%s says, 'I am already focused, Master!",mypet->GetCleanName()); + } else { + Message(0,"%s says, 'I will now focus my attention, Master!",mypet->GetCleanName()); + mypet->CastToNPC()->SetFocused(true); + } + } + break; + } + case PET_FOCUS_OFF: { + if(GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (mypet->IsFocused()) { + Message(0,"%s says, 'I am no longer focused, Master!",mypet->GetCleanName()); + mypet->CastToNPC()->SetFocused(false); + } else { + Message(0,"%s says, 'I am already not focused, Master!",mypet->GetCleanName()); + } + } + 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)) { + cout << "Wrong size: OP_PetitionUnCheckout, size=" << app->size << ", expected " << sizeof(uint32) << 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)) { + cout << "Wrong size: OP_PetitionCheckout, size=" << app->size << ", expected " << sizeof(uint32) << 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; + const char* name = GetName(); + uint32 len_name = strlen(name); + uint32 len_msg = strlen(in->message); + uint32 len_packet = sizeof(in->unknown01) + len_name + + strlen(in->message) + 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); + + //cout << "######### Outgoing emote packet" << endl; + //DumpPacket(outapp); + + /* + 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,FILTER_SOCIALS); + + 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 + + // an emote (i.e., waving arm to say hello) + 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(); + cout << name << " deleted corpse " << dc->corpsename << 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(MEND)) + 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(MEND)) { + int criticalchance = 0; + switch(GetAA(aaCriticalMend)){ + case 1: + criticalchance = 5; + break; + case 2: + criticalchance = 10; + break; + case 3: + criticalchance = 25; + break; + } + criticalchance += 5*GetAA(aaMendingoftheTranquil); + + 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(MEND) <= 75) && (MakeRandomInt(GetSkill(MEND),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(MEND, NULL, 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) + Death(0, 32000, SPELL_UNKNOWN, HAND_TO_HAND); + 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==FallingDamageType)); + 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, 119); //119 Alternate Experience is *OFF*. + m_epp.perAA = 0; + SendAAStats(); + } else if(action->action == aaActionSetEXP) { + if(m_epp.perAA == 0) + Message_StringID(0, 121); //121 Alternate Experience is *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."); + DumpPacket(app); + 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(int i=0; iItems[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(); + DumpPacket(app); + return; + } + + for (int i=0;iItems[i]>0 && gis->Items[i]Items[i])!=0) + 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; + } + + DumpPacket(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_PickPocket(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PickPocket_Struct)) + { + LogFile->write(EQEMuLog::Error, "Size mismatch for Pick Pocket packet"); + DumpPacket(app); + } + + if(!HasSkill(PICK_POCKETS)) + { + 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(PICK_POCKETS); + 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(PICK_POCKETS); + 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(PICK_POCKETS); + 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(TRACKING)==0 ) + SetSkill(TRACKING,1); + else + CheckIncreaseSkill(TRACKING, NULL, 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); + //if (EQDEBUG>=5) + // DumpPacket(app); + return; +} + +void Client::Handle_OP_ReloadUI(const EQApplicationPacket *app) +{ + if(IsInAGuild()) + 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 == NULL) { + //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(SENSE_TRAPS)) + 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(SENSE_TRAPS, NULL); + + if (trap && trap->skill > 0) { + int uskill = GetSkill(SENSE_TRAPS); + 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(DISARM_TRAPS)) + 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(DISARM_TRAPS); + 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(DISARM_TRAPS, NULL); + 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: %i, 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: %i, 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: %i, 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: %i, 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 = NULL; + 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(SAFE_FALL)) //this should only get called if the client has safe fall, but just in case... + CheckIncreaseSkill(SAFE_FALL, NULL); //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: %i, but got: %i\n",sizeof(FindPersonRequest_Struct),app->size); + else { + FindPersonRequest_Struct* t = (FindPersonRequest_Struct*)app->pBuffer; + + vector points; + + Message(13, "Searched for NPC ID: %d\n", t->npc_id); + Mob* target = entity_list.GetMob(t->npc_id); + + if(target == NULL) { + //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); + } + else + Message(13, "Found NPC '%s'\n", target->GetName()); + + 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 + { + VERTEX Start(GetX(), GetY(), GetZ() + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION); + 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, NULL, NULL) && 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 + { + 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; + + 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(list::iterator Iterator = pathlist.begin(); Iterator != pathlist.end(); ++Iterator) + { + if((*Iterator) == -1) // Teleporter + { + LeadsToTeleporter = true; + break; + } + + 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 { + cout << "Async client save failed. '" << errbuf << "'" << endl; + Message(13, "Error: Asyncronous save of your character failed."); + if (Admin() >= 200) + Message(13, "errbuf: %s", errbuf); + } + pQueuedSaveWorkID = 0; + break; + } + default: { + cout << "Error: Client::DBAWComplete(): Unknown workpt_b1" << 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) { + cout << "Error in FinishConnState2(): dbaq==0" << endl; + return false; + } + if (!dbaq->GetAnswer(errbuf, &result)) { + cout << "Error in FinishConnState2(): !dbaq[" << dbaq->QPT() << "]->GetAnswer(): " << errbuf << 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 { + cout << "Error in FinishConnState2(): dbaq->PQT() unknown" << 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) { + // cout << "Client dropped: !GetPlayerAlternateAdv, name=" << name << 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 != NULL) { + + //for whatever reason, LineIntersectsNode is giving better results than FindBestZ + + NodeRef pnode; + VERTEX me; + me.x = GetX(); + me.y = GetY(); + me.z = GetZ() + (GetSize()==0.0?6:GetSize()); + pnode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), me.x, me.y ); + + VERTEX hit; + VERTEX below_me(me); + below_me.z -= 500; + if(!zone->zonemap->LineIntersectsNode(pnode, me, below_me, &hit, NULL) || hit.z < -5000) { +#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 + //theres nothing below us... try to find something to stand on + me.z += 200; //arbitrary # + if(zone->zonemap->LineIntersectsNode(pnode, me, below_me, &hit, NULL)) { + //+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->LineIntersectsNode(pnode, me, below_me, &hit, NULL)) { + //+10 so they dont stick in the ground + SendTo(me.x, me.y, hit.z + 10); + m_pp.z = hit.z + 10; + } + } + } + } + + //m_pp.hunger_level = 6000; + //m_pp.thirst_level = 6000; + + //aa_title = m_pp.aa_title; + //m_pp.timeplayed=64; + //m_pp.birthday=1057434792; + //m_pp.lastlogin=1057464792; + + 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(SWIMMING) < 100) + SetSkill(SWIMMING,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 (spells_loaded) + { + 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 = NULL; + 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 = NULL; + } + } //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.dumpInventory(); +#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; + + if(m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) + 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); + 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(SLOT_CURSOR, 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 = NULL; + 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 = NULL; + } + 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, 0xFFFF, 0xFFFF); + } + 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, 0xFFFF, 0xFFFF); + } + 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]); + 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; + } + case SE_SkillProc2: + case SE_SkillProc: + { + AddSkillProc(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 != NULL) { + 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_ENTERZONE, this, "", 0); + + if(firstlogon == 1) + parse->EventPlayer(EVENT_CONNECT, this, "", 0); //This sub event is for if a player logs in for the first time since entering world. + + 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(); + CalcItemScale(true); + + if(zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) + GuildBanks->SendGuildBank(this); + + if(GetClientVersion() >= EQClientSoD) + entity_list.SendFindableNPCList(this); + + if(IsInAGuild()) + { + guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(NULL)); + 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; + + 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 != NULL) + 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 = NULL; + 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,NULL,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, 4059); //You must wait a bit longer before using the rewind command again. + } 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 = NULL; + for(int x = 0; x < 6; x++) + { + if(g->members[x]){ + Client *c = NULL; + 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 = NULL; + 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 = NULL; + 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 = NULL; + 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 = NULL; + //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 = NULL; + 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 = NULL; + 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 = NULL; + 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 = NULL; + 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 = NULL; + 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(NULL) > (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; + if(parse->SpellHasQuestSub(SpellID, "EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE")) + { + parse->EventSpell(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, NULL, this, SpellID, 0); + } + else + { + // 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 = 0; + MakeAnyLenString(&buf, "%d", prs->popupid); + + parse->EventPlayer(EVENT_POPUPRESPONSE, this, buf, 0); + + Mob* Target = GetTarget(); + if(Target && Target->IsNPC()) { + parse->EventNPC(EVENT_POPUPRESPONSE, Target->CastToNPC(), this, buf, 0); + } + + safe_delete_array(buf); +} + +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; + } + } + else { + m_pp.potionbelt.items[mptbs->SlotNumber].item_id = 0; + m_pp.potionbelt.items[mptbs->SlotNumber].icon = 0; + } + + 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(SLOT_PRIMARY); + const ItemInst* SecondaryWeapon = GetInv().GetItem(SLOT_SECONDARY); + 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 == ItemTypePierce) || + (SecondaryWeapon && SecondaryWeapon->GetItem()->ItemType == ItemTypePierce)) + { + float SuccessChance = (GetSkill(APPLY_POISON) + GetLevel()) / 400.0f; + double ChanceRoll = MakeRandomFloat(0, 1); + + CheckIncreaseSkill(APPLY_POISON, NULL, 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, NULL, 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 = NULL; + + 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)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch on OP_GroupUpdate: got %u expected %u", + app->size, sizeof(GroupUpdate_Struct)); + DumpPacket(app); + return; + } + + GroupUpdate_Struct* gu = (GroupUpdate_Struct*)app->pBuffer; + + 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); + } + } + break; + } + + default: + { + LogFile->write(EQEMuLog::Debug, "Received unhandled OP_GroupUpdate requesting action %u", gu->action); + DumpPacket(app); + return; + } + } +} + +void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) +{ + // if the character has a start city, don't let them use the command + if(m_pp.binds[4].zoneId != 0) { + Message(15,"Your home city has already been set.", m_pp.binds[4].zoneId, database.GetZoneName(m_pp.binds[4].zoneId)); + return; + } + if (app->size < 1) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_SetStartCity, size=%i, expected %i", app->size, 1); + DumpPacket(app); + return; + } + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result = NULL; + MYSQL_ROW row = 0; + float x(0),y(0),z(0); + uint32 zoneid = 0; + + uint32 StartCity = (uint32)strtol((const char*)app->pBuffer, NULL, 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, 12945); + return; + } + + uint32 size = app->size; + uint32 current_point = 0; + string reported, reporter; + 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; + + 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]; + + 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) + return; + + if((int)zone->GetZoneID() != RuleI(World, GuildBankZoneID)) + { + Message(13, "The Guild Bank is not available in this zone."); + + return; + } + + if (app->size < sizeof(uint32)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GuildBank, size=%i, expected %i", app->size, sizeof(uint32)); + DumpPacket(app); + return; + } + + char *Buffer = (char *)app->pBuffer; + + uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + + if(!IsInAGuild()) + { + Message(13, "You must be in a Guild to use the Guild Bank."); + + if(Action == GuildBankDeposit) + GuildBankDepositAck(true); + else + GuildBankAck(); + + return; + } + + if(!IsGuildBanker()) + { + if((Action != GuildBankDeposit) && (Action != GuildBankViewItem) && (Action != GuildBankWithdraw)) + { + _log(GUILDS__BANK_ERROR, "Suspected hacking attempt on guild bank from %s", GetName()); + + GuildBankAck(); + + return; + } + } + + switch(Action) + { + case GuildBankPromote: + { + if(GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) + { + Message_StringID(13, GUILD_BANK_FULL); + + 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(SLOT_CURSOR); + + 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(SLOT_CURSOR, 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()[SLOT_CURSOR]) + { + 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(SLOT_CURSOR, 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 = NULL; + + 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(cds->CorpseName); + + 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(cds->CorpseName); + + 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(std::list::iterator Iterator = DraggedCorpses.begin(); Iterator != DraggedCorpses.end(); ++Iterator) + { + if(!strcasecmp((*Iterator).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); + + GroupMakeLeader_Struct *gmls = (GroupMakeLeader_Struct *)app->pBuffer; + + Mob* NewLeader = entity_list.GetClientByName(gmls->NewLeader); + + 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); + } + } +} + +void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) +{ + if(IsInAGuild()) + { + Message(clientMessageError, "You are already in a guild!"); + return; + } + + 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; + } + + if((Admin() < RuleI(Guild, PlayerCreationRequiredStatus)) || + (GetLevel() < RuleI(Guild, PlayerCreationRequiredLevel)) || + (database.GetTotalTimeEntitledOnAccount(AccountID()) < (unsigned int)RuleI(Guild, PlayerCreationRequiredTime))) + { + Message(clientMessageError, "Your status, level or time playing on this account are insufficient to use this feature."); + return; + } + + // The Underfoot client Guild Creation window will only allow a guild name of <= around 30 characters, but the packet is 64 bytes. Sanity check the + // name anway. + // + + char *GuildName = (char *)app->pBuffer; +#ifdef FREEBSD + if(strlen(GuildName) > 60) +#else + if(strnlen(GuildName, 64) > 60) +#endif + { + Message(clientMessageError, "Guild name too long."); + return; + } + + for(unsigned int i = 0; i < strlen(GuildName); ++i) + { + if(!isalpha(GuildName[i]) && (GuildName[i] != ' ')) + { + Message(clientMessageError, "Invalid character in Guild name."); + return; + } + } + + int32 GuildCount = guild_mgr.DoesAccountContainAGuildLeader(AccountID()); + + 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) + { + Message_StringID(clientMessageError, GUILD_NAME_IN_USE); + return; + } + + uint32 NewGuildID = guild_mgr.CreateGuild(GuildName, CharacterID()); + + _log(GUILDS__ACTIONS, "%s: Creating guild %s with leader %d via UF+ GUI. It was given id %lu.", GetName(), + GuildName, CharacterID(), (unsigned long)NewGuildID); + + if (NewGuildID == GUILD_NONE) + Message(clientMessageError, "Guild creation failed."); + else + { + 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) + GuildBanks->SendGuildBank(this); + } + } +} + +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; + } + + 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 = NULL; + + std::list merlist = zone->merchanttable[merchant_id]; + std::list::const_iterator itr; + for(itr = merlist.begin(); itr != merlist.end() && count < 255; itr++){ + const MerchantList &ml = *itr; + if(GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tar->GetPrimaryFaction(); + if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if(item) + { + item_ss << "^" << item->Name << "|"; + item_ss << item->ID << "|"; + item_ss << ml.alt_currency_cost << "|"; + item_ss << "0|"; + item_ss << "1|"; + item_ss << item->Races << "|"; + item_ss << item->Classes; + count++; + } + } + + if(count > 0) { + ss << "|" << count << item_ss.str(); + } else { + ss << "|0"; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencyMerchantReply, ss.str().length() + 1); + memcpy(outapp->pBuffer, ss.str().c_str(), ss.str().length()); + FastQueuePacket(&outapp); + } +} + +void Client::Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app) { + VERIFY_PACKET_LENGTH(OP_AltCurrencySellSelection, app, AltCurrencySelectItem_Struct); + + AltCurrencySelectItem_Struct *select = (AltCurrencySelectItem_Struct*)app->pBuffer; + NPC* tar = entity_list.GetNPCByID(select->merchant_entity_id); + if(tar) { + if(DistNoRoot(*tar) > USE_NPC_RANGE2) + return; + + if(tar->GetClass() != ALT_CURRENCY_MERCHANT) { + return; + } + + uint32 alt_cur_id = tar->GetAltCurrencyType(); + if(alt_cur_id == 0) { + return; + } + + ItemInst *inst = m_inv.GetItem(select->slot_id); + if(!inst) { + return; + } + + const Item_Struct* item = NULL; + 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 == inst->GetItem()->ID) { + cost = ml.alt_currency_cost; + found = true; + break; + } + } + + if(!found) { + 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_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 = NULL; + 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(SLOT_CURSOR, *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; + 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; + } + + 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, SLOT_CURSOR); + AddAlternateCurrencyValue(reclaim->currency_id, -((int32)reclaim->count)); + } + } +} + +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; + } + + const Item_Struct* item = NULL; + 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 == 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; + + FastQueuePacket(&outapp); + AddAlternateCurrencyValue(alt_cur_id, cost); + Save(1); + } +} + +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) +{ + 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; + + if(strnlen(pts->Comment, 256) > 256) + 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; + + if(strnlen(gts->Comment, 256) > 256) + 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_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, NULL); + + 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_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_ItemPreview(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_ItemPreview, app, ItemPreview_Struct); + ItemPreview_Struct *ips = (ItemPreview_Struct *)app->pBuffer; + + const Item_Struct* item = database.GetItem(ips->itemid); + + if (item) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ItemPreview, strlen(item->Name) + strlen(item->Lore) + strlen(item->IDFile) + 898); + + int spacer; + for (spacer = 0; spacer < 16; spacer++) { + outapp->WriteUInt8(48); + } + outapp->WriteUInt16(256); + for (spacer = 0; spacer < 7; spacer++) { + outapp->WriteUInt8(0); + } + for (spacer = 0; spacer < 7; spacer++) { + outapp->WriteUInt8(255); + } + outapp->WriteUInt32(0); + outapp->WriteUInt32(1); + outapp->WriteUInt32(0); + outapp->WriteUInt8(237); // Seems to be some kind of counter? increases by 1 for each preview that you do. + outapp->WriteUInt16(2041); //F907 + for (spacer = 0; spacer < 36; spacer++) { + outapp->WriteUInt8(0); + } + for (spacer = 0; spacer < 4; spacer++) { + outapp->WriteUInt8(255); + } + for (spacer = 0; spacer < 9; spacer++) { + outapp->WriteUInt8(0); + } + for (spacer = 0; spacer < 5; spacer++) { + outapp->WriteUInt8(255); + } + for (spacer = 0; spacer < 5; spacer++) { + outapp->WriteUInt8(0); + } + outapp->WriteString(item->Name); + outapp->WriteString(item->Lore); + outapp->WriteUInt8(0); + outapp->WriteUInt32(ips->itemid); + outapp->WriteUInt32(item->Weight); + outapp->WriteUInt8(item->NoRent); + outapp->WriteUInt8(item->NoDrop); + outapp->WriteUInt8(item->Attuneable); + outapp->WriteUInt8(item->Size); + outapp->WriteUInt32(item->Slots); + outapp->WriteUInt32(item->Price); + outapp->WriteUInt32(item->Icon); + outapp->WriteUInt8(0); //Unknown? + outapp->WriteUInt8(0); //Placeable flag? + outapp->WriteUInt32(item->BenefitFlag); + outapp->WriteUInt8(item->Tradeskills); + outapp->WriteUInt8(item->CR); + outapp->WriteUInt8(item->DR); + outapp->WriteUInt8(item->PR); + outapp->WriteUInt8(item->MR); + outapp->WriteUInt8(item->FR); + outapp->WriteUInt8(item->AStr); + outapp->WriteUInt8(item->ASta); + outapp->WriteUInt8(item->AAgi); + outapp->WriteUInt8(item->ADex); + outapp->WriteUInt8(item->ACha); + outapp->WriteUInt8(item->AInt); + outapp->WriteUInt8(item->AWis); + outapp->WriteSInt32(item->HP); + outapp->WriteSInt32(item->Mana); + outapp->WriteSInt32(item->Endur); + outapp->WriteSInt32(item->AC); + outapp->WriteUInt32(item->Regen); + outapp->WriteUInt32(item->ManaRegen); + outapp->WriteSInt32(item->EnduranceRegen); + outapp->WriteUInt32(item->Classes); + outapp->WriteUInt32(item->Races); + outapp->WriteUInt32(item->Deity); + outapp->WriteUInt32(item->SkillModValue); + outapp->WriteUInt32(0); //SkillModValue + outapp->WriteUInt32(item->SkillModType); + outapp->WriteUInt32(0); //SkillModExtra + outapp->WriteUInt32(item->BaneDmgRace); + outapp->WriteUInt32(item->BaneDmgBody); + outapp->WriteUInt32(item->BaneDmgRaceAmt); + outapp->WriteUInt32(item->BaneDmgAmt); + outapp->WriteUInt8(item->Magic); + outapp->WriteUInt32(item->CastTime_); + outapp->WriteUInt32(item->ReqLevel); + outapp->WriteUInt32(item->RecLevel); + outapp->WriteUInt32(item->RecSkill); + outapp->WriteUInt32(item->BardType); + outapp->WriteUInt32(item->BardValue); + outapp->WriteUInt8(item->Light); + outapp->WriteUInt8(item->Delay); + outapp->WriteUInt8(item->ElemDmgType); + outapp->WriteUInt8(item->ElemDmgAmt); + outapp->WriteUInt8(item->Range); + outapp->WriteUInt32(item->Damage); + outapp->WriteUInt32(item->Color); + outapp->WriteUInt32(0); // Prestige + outapp->WriteUInt8(item->ItemType); + outapp->WriteUInt32(item->Material); + outapp->WriteUInt32(0); //unknown + outapp->WriteUInt32(item->EliteMaterial); + outapp->WriteUInt32(0); // unknown + outapp->WriteUInt32(0); // unknown + outapp->WriteUInt32(0); //This is unknown057 from lucy + for (spacer = 0; spacer < 77; spacer++) { //More Item stats, but some seem to be off based on packet check + outapp->WriteUInt8(0); + } + outapp->WriteUInt32(4294967295); //Unknown but always seen as FF FF FF FF + outapp->WriteUInt32(0); //Unknown + for (spacer = 0; spacer < 5; spacer++) { //Augment stuff + outapp->WriteUInt32(item->AugSlotType[spacer]); + outapp->WriteUInt8(item->AugSlotVisible[spacer]); + outapp->WriteUInt8(item->AugSlotUnk2[spacer]); + } + outapp->WriteUInt32(0); //New RoF 6th Aug Slot + outapp->WriteUInt8(1); //^ + outapp->WriteUInt8(0); //^^ + outapp->WriteUInt32(item->LDoNSold); + outapp->WriteUInt32(item->LDoNTheme); + outapp->WriteUInt32(item->LDoNPrice); + outapp->WriteUInt32(item->LDoNSellBackRate); + for (spacer = 0; spacer < 11; spacer++) { //unknowns + outapp->WriteUInt8(0); + } + outapp->WriteUInt32(4294967295); //Unknown but always seen as FF FF FF FF + outapp->WriteUInt16(0); //Unknown + outapp->WriteUInt32(item->Favor); // Tribute + for (spacer = 0; spacer < 17; spacer++) { //unknowns + outapp->WriteUInt8(0); + } + outapp->WriteUInt32(item->GuildFavor); // Tribute + outapp->WriteUInt32(0); //Unknown + outapp->WriteUInt32(4294967295); //Unknown but always seen as FF FF FF FF + for (spacer = 0; spacer < 11; spacer++) { //unknowns + outapp->WriteUInt8(0); + } + outapp->WriteUInt8(1); + for (spacer = 0; spacer < 25; spacer++) { //unknowns + outapp->WriteUInt8(0); + } + for (spacer = 0; spacer < 304; spacer++) { //Cast stuff and whole bunch of unknowns + outapp->WriteUInt8(0); + } + outapp->WriteUInt8(142); // Always seen not in the item structure though 8E + outapp->WriteUInt32(0); //unknown + outapp->WriteUInt32(1); // Always seen as 1 + outapp->WriteUInt32(0); //unknown + outapp->WriteUInt32(3452750909); //0x3DCCCCCD/3452750909 + outapp->WriteUInt32(0); + outapp->WriteUInt16(8256); //0x4020/8256 + outapp->WriteUInt16(0); + outapp->WriteUInt32(4294967295); //Unknown but always seen as FF FF FF FF + outapp->WriteUInt16(0); + outapp->WriteUInt32(4294967295); //Unknown but always seen as FF FF FF FF + outapp->WriteUInt32(0); //unknown + outapp->WriteUInt32(0); //unknown + outapp->WriteUInt16(0); //unknown + outapp->WriteUInt32(32831); //0x3F80 + for (spacer = 0; spacer < 24; spacer++) { //whole bunch of unknowns always 0's + outapp->WriteUInt8(0); + } + outapp->WriteUInt8(1); + for (spacer = 0; spacer < 6; spacer++) { //whole bunch of unknowns always 0's + outapp->WriteUInt8(0); + } + + QueuePacket(outapp); + safe_delete(outapp); + } else + return; +} + +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)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDataRequest expected 4 got %i", app->size); + + DumpPacket(app); + + return; + } + + MercenaryMerchantShopRequest_Struct* mmsr = (MercenaryMerchantShopRequest_Struct*) app->pBuffer; + uint32 merchant_id = mmsr->MercMerchantID; + uint32 altCurrentType = 19; + + 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) { + + //send info about your current merc(s) + } + + DumpPacket(app); + + if(!RuleB(Mercs, AllowMercs)) { + return; + } + + NPC* tar = entity_list.GetNPCByID(merchant_id); + if(tar) { + int mercTypeCount = 0; + int mercCount = 0; + //int mercStanceCount = 2; // Temporarily Hard Set + int packetSize; + + MercenaryMerchantList_Struct* mml = new MercenaryMerchantList_Struct; + + if(DistNoRoot(*tar) > USE_NPC_RANGE2) + return; + + if(tar->GetClass() != MERCERNARY_MASTER) { + return; + } + + mercTypeCount = tar->GetNumMercTypes(GetClientVersion()); + mercCount = tar->GetNumMercs(GetClientVersion()); + + + std::list mercTypeList = tar->GetMercTypesList(GetClientVersion()); + std::list mercDataList = tar->GetMercsList(GetClientVersion()); + + mml->MercTypeCount = mercTypeCount; + int i = 0; + + + if(mercTypeCount > 0) + { + mml->MercGrades = new MercenaryGrade_Struct[mercTypeCount]; + for(std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); mercTypeListItr++) { + mml->MercGrades[i].GradeCountEntry = mercTypeListItr->Type; // DBStringID for Type + i++; + } + } + mml->MercCount = mercCount; + + if(mercCount > 0) + { + mml->Mercs = new MercenaryListEntry_Struct[mercCount]; + i = 0; + for(std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); mercListItr++) + { + mml->Mercs[i].MercID = mercListItr->MercTemplateID; + mml->Mercs[i].MercType = mercListItr->MercType; + mml->Mercs[i].MercSubType = mercListItr->MercSubType; + mml->Mercs[i].PurchaseCost = Merc::CalcPurchaseCost(mercListItr->MercTemplateID, GetLevel(), 0); + mml->Mercs[i].UpkeepCost = Merc::CalcUpkeepCost(mercListItr->MercTemplateID, GetLevel(), 0); + mml->Mercs[i].Status = 0; + mml->Mercs[i].AltCurrencyCost = Merc::CalcPurchaseCost(mercListItr->MercTemplateID, GetLevel(), altCurrentType); + mml->Mercs[i].AltCurrencyUpkeep = Merc::CalcUpkeepCost(mercListItr->MercTemplateID, GetLevel(), altCurrentType); + mml->Mercs[i].AltCurrencyType = altCurrentType; + mml->Mercs[i].MercUnk01 = 0; + mml->Mercs[i].TimeLeft = -1; + mml->Mercs[i].MerchantSlot = i + 1; + mml->Mercs[i].MercUnk02 = 1; + mml->Mercs[i].StanceCount = zone->merc_stance_list[mercListItr->MercTemplateID].size(); + mml->Mercs[i].MercUnk03 = 519044964; + mml->Mercs[i].MercUnk04 = 1; + //mml->Mercs[i].MercName; + int stanceindex = 0; + if(mml->Mercs[i].StanceCount != 0) + { + mml->Mercs[i].Stances = new MercenaryStance_Struct[mml->Mercs[i].StanceCount]; + list::iterator iter = zone->merc_stance_list[mercListItr->MercTemplateID].begin(); + while(iter != zone->merc_stance_list[mercListItr->MercTemplateID].end()) + { + mml->Mercs[i].Stances[stanceindex].StanceIndex = stanceindex; + mml->Mercs[i].Stances[stanceindex].Stance = (iter->StanceID); + stanceindex++; + iter++; + } + } + i++; + } + } + + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, 1); //Packet sizes are handled by the encoder. + outapp->pBuffer = (unsigned char*)mml; + // DumpPacket(outapp); + FastQueuePacket(&outapp); + + } +} + +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; + + DumpPacket(app); + + 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); + + MercTemplate* merc_template = zone->GetMercTemplate(merc_template_id); + + if(merc_template) { + + if (GetMercID()) { + // 6 - You must dismiss the mercenary before hiring a new one. + SendMercMerchantResponsePacket(6); + } + else + { + // 0 is approved hire request + SendMercMerchantResponsePacket(0); + + // Set time remaining to max on Hire + GetEPP().mercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); + + // Get merc, assign it to client & spawn + Merc* merc = Merc::LoadMerc(this, merc_template, merchant_id); + SpawnMerc(merc); + } + } + +} + +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 + + DumpPacket(app); + + if(MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Suspend ( %i ) received.", merc_suspend); + + // 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(option >= 0) + { + GetEPP().mercState = option; + } + + DumpPacket(app); + + if(MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Command %i, Option %i received.", merc_command, option); + + // 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 +} + +void Client::Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app) +{ + // The payload is 0 bytes. + if(app->size != 0) + { + Message(13, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); + DumpPacket(app); + return; + } + + DumpPacket(app); + + if(MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Data Update Request Received."); + + if(GetMercID()) + { + SendMercPersonalInfo(); + } +} + +void Client::Handle_OP_MercenaryDismiss(const EQApplicationPacket *app) +{ + // The payload is 0 or 1 bytes. + if(app->size > 1) + { + Message(13, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); + DumpPacket(app); + return; + } + + DumpPacket(app); + + uint8 Command = 0; + if(app->size > 0) + { + char *InBuffer = (char *)app->pBuffer; + Command = VARSTRUCT_DECODE_TYPE(uint8, InBuffer); + } + + if(MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Dismiss Request ( %i ) Received.", Command); + + // Handle the dismiss here... + if(GetMercID()) { + Merc* merc = GetMerc(); + + if(merc) + merc->Dismiss(); + } + + // Unsure if there is a server response to this packet + +} + +void Client::Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app) +{ + // The payload is 0 bytes. + 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); + DumpPacket(app); + return; + } + + DumpPacket(app); + + if(MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Timer Request received."); + + if(!RuleB(Mercs, AllowMercs)) { + return; + } + + // To Do: Load Mercenary Timer Data to properly populate this reply packet + // All hard set values for now + uint32 entityID = 0; + uint32 mercState = 5; + uint32 suspendedTime = 0; + if(GetMercID()) { + Merc* merc = GetMerc(); + + if(merc) { + entityID = merc->GetID(); + + if(GetEPP().mercIsSuspended) { + mercState = 1; + suspendedTime = GetEPP().mercSuspendedTime; + } + } + } + + SendMercTimerPacket(entityID, mercState, suspendedTime, RuleI(Mercs, UpkeepIntervalMS), 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_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 + // RoF client sends a 12-byte packet based on the RoF::Structs::ItemSlotStruct + + // RoF structure types are defined as signed uint16 and currently untranslated + // RoF::struct.SlotType = {0 - Equipment, 1 - Bank, 2 - Shared Bank} // not tested beyond listed types + // RoF::struct.Unknown2 = 0 + // RoF::struct.MainSlot = { } + // RoF::struct.SubSlot = -1 (non-child) + // RoF::struct.AugSlot = -1 (non-child) + // RoF::struct.Unknown1 = 141 (unsure why, but always appears to be this value..combine containers not tested) + + // SideNote: Watching the slot translations, Unknown1 is showing '141' as well on certain item swaps. + // Manually looting a corpse results in a from '34' to '68' value for equipment items, '0' to '0' for inventory. +} diff --git a/zone/client_packet.h b/zone/client_packet.h new file mode 100644 index 000000000..0156405d9 --- /dev/null +++ b/zone/client_packet.h @@ -0,0 +1,290 @@ + 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); + void Handle_Connect_0x3e33(const EQApplicationPacket *app); + void Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app); + void Handle_Connect_OP_SendExpZonein(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_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_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_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_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); diff --git a/zone/client_process.cpp b/zone/client_process.cpp new file mode 100644 index 000000000..7b884309a --- /dev/null +++ b/zone/client_process.cpp @@ -0,0 +1,2368 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 + + client_process.cpp: + Handles client login sequence and packets sent from client to zone +*/ +#include "../common/debug.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WINDOWS + #include + #include + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#else + #include + #include + #include + #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/MiscFunctions.h" +#include "spdat.h" +#include "petitions.h" +#include "NpcAI.h" +#include "../common/skills.h" +#include "forage.h" +#include "zone.h" +#include "event_codes.h" +#include "faction.h" +#include "../common/crc32.h" +#include "../common/rulesys.h" +#include "StringIDs.h" +#include "map.h" +#include "guild_mgr.h" +#include +#include "QuestParserCollection.h" + +using namespace std; + + +extern Zone* zone; +extern volatile bool ZoneLoaded; +extern WorldServer worldserver; +#if !defined(NEW_LoadSPDat) && !defined(DB_LoadSPDat) + extern SPDat_Spell_Struct spells[SPDAT_RECORDS]; +#endif +extern bool spells_loaded; +extern PetitionList petition_list; +extern EntityList entity_list; + +bool Client::Process() { + _ZP(Client_Process); + adverrorinfo = 1; + bool ret = true; + + if(Connected() || IsLD()) + { + // try to send all packets that weren't sent before + if(!IsLD() && zoneinpacket_timer.Check()){ + SendAllPackets(); + } + +#ifdef PACKET_UPDATE_MANAGER + update_manager.Process(); +#endif + + if(adventure_request_timer) + { + if(adventure_request_timer->Check()) + { + safe_delete(adventure_request_timer); + } + } + + if(adventure_create_timer) + { + if(adventure_create_timer->Check()) + { + safe_delete(adventure_create_timer); + } + } + + if(adventure_leave_timer) + { + if(adventure_leave_timer->Check()) + { + safe_delete(adventure_leave_timer); + } + } + + if(adventure_door_timer) + { + if(adventure_door_timer->Check()) + { + safe_delete(adventure_door_timer); + } + } + + if(adventure_stats_timer) + { + if(adventure_stats_timer->Check()) + { + safe_delete(adventure_stats_timer); + } + } + + if(adventure_leaderboard_timer) + { + if(adventure_leaderboard_timer->Check()) + { + safe_delete(adventure_leaderboard_timer); + } + } + + if(dead) + { + SetHP(-100); + if(RespawnFromHoverTimer.Check()) + HandleRespawnFromHover(0); + } + + if(IsTracking() && (GetClientVersion() >= EQClientSoD) && TrackingTimer.Check()) + DoTracking(); + + if(hpupdate_timer.Check()) + SendHPUpdate(); + + if(mana_timer.Check()) + SendManaUpdatePacket(); + 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.x = m_pp.binds[0].x; + m_pp.y = m_pp.binds[0].y; + m_pp.z = m_pp.binds[0].z; + Save(); + + Group *mygroup = GetGroup(); + if (mygroup) // && zone.GetZoneID() != m_pp.binds[0].zoneId + { + entity_list.MessageGroup(this,true,15,"%s died.", GetName()); + mygroup->MemberZoned(this); + } + Raid *myraid = entity_list.GetRaidByClient(this); + if (myraid) + { + myraid->MemberZoned(this); + } + return(false); + } + + if(charm_update_timer.Check()) + { + CalcItemScale(); + } + + if(TaskPeriodic_Timer.Check() && taskstate) + taskstate->TaskPeriodicChecks(this); + + if(linkdead_timer.Check()){ + Save(); + LeaveGroup(); + if (GetMerc()) + { + GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); + GetMerc()->Depop(); + } + Raid *myraid = entity_list.GetRaidByClient(this); + if (myraid) + { + myraid->MemberZoned(this); + } + return false; //delete client + } + + if (camp_timer.Check()) { + LeaveGroup(); + Save(); + if (GetMerc()) + { + GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); + GetMerc()->Depop(); + } + instalog = true; + } + + if (IsStunned() && stunned_timer.Check()) { + this->stunned = false; + this->stunned_timer.Disable(); + } + + if(!m_CheatDetectMoved) + { + m_TimeSinceLastPositionCheck = Timer::GetCurrentTime(); + } + + if (bardsong_timer.Check() && bardsong != 0) { + //NOTE: this is kinda a heavy-handed check to make sure the mob still exists before + //doing the next pulse on them... + Mob *song_target; + if(bardsong_target_id == GetID()) { + song_target = this; + } else { + song_target = entity_list.GetMob(bardsong_target_id); + } + + if (song_target == NULL) { + InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong); + } else { + if(!ApplyNextBardPulse(bardsong, song_target, bardsong_slot)) + InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong); +// SpellFinished(bardsong, bardsong_target, bardsong_slot, spells[bardsong].mana); + } + } + + if(GetMerc()) + { + UpdateMercTimer(); + } + + if(GetEPP().mercTemplateID != 0) + { + if(p_timers.Expired(&database, pTimerMercSuspend, false)) { + CheckMercSuspendTimer(); + } + } + + if(IsAIControlled()) + AI_Process(); + + if (bindwound_timer.Check() && bindwound_target != 0) { + BindWound(bindwound_target, false); + } + + if(KarmaUpdateTimer) + { + if(KarmaUpdateTimer->Check(false)) + { + KarmaUpdateTimer->Start(RuleI(Chat, KarmaUpdateIntervalMS)); + database.UpdateKarma(AccountID(), ++TotalKarma); + } + } + + if(qGlobals) + { + if(qglobal_purge_timer.Check()) + { + qGlobals->PurgeExpiredGlobals(); + } + } + + bool may_use_attacks = false; + /* + Things which prevent us from attacking: + - being under AI control, the AI does attacks + - being dead + - casting a spell (not sure what the rest is doing, prolly bard) + - not having a target + - being stunned or mezzed + - having used a ranged weapon recently + */ + if(auto_attack) { + if(!IsAIControlled() && !dead + && !(spellend_timer.Enabled() && (spells[casting_spell_id].classes[7] < 1 && spells[casting_spell_id].classes[7] > 65)) + && !IsStunned() && !IsFeared() && !IsMezzed() && GetAppearance() != eaDead && !IsMeleeDisabled() + ) + may_use_attacks = true; + + if(may_use_attacks && ranged_timer.Enabled()) { + //if the range timer is enabled, we need to consider it + if(!ranged_timer.Check(false)) { + //the ranged timer has not elapsed, cannot attack. + may_use_attacks = false; + } + } + } + + if(AutoFireEnabled()){ + ItemInst *ranged = GetInv().GetItem(SLOT_RANGE); + if(ranged) + { + if(ranged->GetItem() && ranged->GetItem()->ItemType == ItemTypeBow){ + if(ranged_timer.Check(false)){ + if(GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient())){ + if(!GetTarget()->BehindMob(this, GetTarget()->GetX(), GetTarget()->GetY())){ + if(CheckLosFN(GetTarget())){ + //client has built in los check, but auto fire does not.. done last. + RangedAttack(GetTarget()); + } + else + ranged_timer.Start(); + } + else + ranged_timer.Start(); + } + else + ranged_timer.Start(); + } + } + else if(ranged->GetItem() && (ranged->GetItem()->ItemType == ItemTypeThrowing || ranged->GetItem()->ItemType == ItemTypeThrowingv2)){ + if(ranged_timer.Check(false)){ + if(GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient())){ + if(!GetTarget()->BehindMob(this, GetTarget()->GetX(), GetTarget()->GetY())){ + if(CheckLosFN(GetTarget())){ + //client has built in los check, but auto fire does not.. done last. + ThrowingAttack(GetTarget()); + } + else + ranged_timer.Start(); + } + else + ranged_timer.Start(); + } + else + ranged_timer.Start(); + } + } + } + } + + Mob *auto_attack_target = GetTarget(); + if (auto_attack && auto_attack_target != NULL && may_use_attacks && attack_timer.Check()) + { + //check if change + //only check on primary attack.. sorry offhand you gotta wait! + if(aa_los_them_mob) + { + if(auto_attack_target != aa_los_them_mob || + aa_los_me.x != GetX() || + aa_los_me.y != GetY() || + aa_los_me.z != GetZ() || + 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()) + { + aa_los_them_mob = auto_attack_target; + aa_los_me.x = GetX(); + aa_los_me.y = GetY(); + aa_los_me.z = GetZ(); + 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(); + if(CheckLosFN(auto_attack_target)) + los_status = true; + else + los_status = false; + } + } + else + { + aa_los_them_mob = auto_attack_target; + aa_los_me.x = GetX(); + aa_los_me.y = GetY(); + aa_los_me.z = GetZ(); + 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(); + if(CheckLosFN(auto_attack_target)) + los_status = true; + else + los_status = false; + } + + if (!CombatRange(auto_attack_target)) + { + //duplicate message not wanting to see it. + //Message_StringID(MT_TooFarAway,TARGET_TOO_FAR); + } + else if (auto_attack_target == this) + { + Message_StringID(MT_TooFarAway,TRY_ATTACKING_SOMEONE); + } + else if (!los_status) + { + //you can't see your target + } + else if (auto_attack_target->GetHP() > -10) // -10 so we can watch people bleed in PvP + { + if(CheckAAEffect(aaEffectRampage)) + { + entity_list.AEAttack(this, 30); + } else { + Attack(auto_attack_target, 13); // Kaiyodo - added attacking hand to arguments + } + ItemInst *wpn = GetInv().GetItem(SLOT_PRIMARY); + TryWeaponProc(wpn, auto_attack_target, 13); + + bool tripleAttackSuccess = false; + if( auto_attack_target && CanThisClassDoubleAttack() ) { + + CheckIncreaseSkill(DOUBLE_ATTACK, auto_attack_target, -10); + if(CheckDoubleAttack()) { + //should we allow rampage on double attack? + if(CheckAAEffect(aaEffectRampage)) { + entity_list.AEAttack(this, 30); + } else { + Attack(auto_attack_target, 13, false); + } + } + + //triple attack: rangers, monks, warriors, berserkers over level 60 + if((((GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER) + && GetLevel() >= 60) || SpecAttacks[SPECATK_TRIPLE]) + && CheckDoubleAttack(true)) + { + tripleAttackSuccess = true; + Attack(auto_attack_target, 13, false); + } + + //quad attack, does this belong here?? + if(SpecAttacks[SPECATK_QUAD] && CheckDoubleAttack(true)) + { + Attack(auto_attack_target, 13, false); + } + } + + //Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack). + int16 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; + + if (auto_attack_target && flurrychance) + { + if(MakeRandomInt(0, 100) < flurrychance) + { + Message_StringID(MT_NPCFlurry, 128); + Attack(auto_attack_target, 13, false); + Attack(auto_attack_target, 13, false); + } + } + + int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; + + if (auto_attack_target && ExtraAttackChanceBonus) { + ItemInst *wpn = GetInv().GetItem(SLOT_PRIMARY); + if(wpn){ + if(wpn->GetItem()->ItemType == ItemType2HS || + wpn->GetItem()->ItemType == ItemType2HB || + wpn->GetItem()->ItemType == ItemType2HPierce ) + { + if(MakeRandomInt(0, 100) < ExtraAttackChanceBonus) + { + Attack(auto_attack_target, 13, false); + } + } + } + } + } + } + + if (GetClass() == WARRIOR || GetClass() == BERSERKER) { + if(!dead && !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(auto_attack && may_use_attacks && auto_attack_target != NULL + && CanThisClassDualWield() && attack_dw_timer.Check()) + { + // Range check + if(!CombatRange(auto_attack_target)) { + // this is a duplicate message don't use it. + Message_StringID(MT_TooFarAway,TARGET_TOO_FAR); + } + // Don't attack yourself + else if(auto_attack_target == this) { + Message_StringID(MT_TooFarAway,TRY_ATTACKING_SOMEONE); + } + else if (!los_status) + { + //you can't see your target + } + else if(auto_attack_target->GetHP() > -10) { + float DualWieldProbability = 0.0f; + + int16 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity; + DualWieldProbability = (GetSkill(DUAL_WIELD) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max + int16 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance; + DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f; + + float random = MakeRandomFloat(0, 1); + CheckIncreaseSkill(DUAL_WIELD, auto_attack_target, -10); + if (random < DualWieldProbability){ // Max 78% of DW + if(CheckAAEffect(aaEffectRampage)) { + entity_list.AEAttack(this, 30, 14); + } else { + Attack(auto_attack_target, 14); // Single attack with offhand + } + ItemInst *wpn = GetInv().GetItem(SLOT_SECONDARY); + TryWeaponProc(wpn, auto_attack_target, 14); + + if( CanThisClassDoubleAttack() && CheckDoubleAttack()) { + if(CheckAAEffect(aaEffectRampage)) { + entity_list.AEAttack(this, 30, 14); + } else { + if(auto_attack_target && auto_attack_target->GetHP() > -10) + Attack(auto_attack_target, 14); // Single attack with offhand + } + } + } + } + } + + adverrorinfo = 2; + if (position_timer.Check()) { + if (IsAIControlled()) + { + if(IsMoving()) + SendPosUpdate(2); + else + { + animation = 0; + delta_x = 0; + delta_y = 0; + delta_z = 0; + SendPosUpdate(2); + } + } + + // Send a position packet every 8 seconds - if not done, other clients + // see this char disappear after 10-12 seconds of inactivity + if (position_timer_counter >= 36) { // Approx. 4 ticks per second + entity_list.SendPositionUpdates(this, pLastUpdateWZ, 500, GetTarget(), true); + pLastUpdate = Timer::GetCurrentTime(); + pLastUpdateWZ = pLastUpdate; + position_timer_counter = 0; + } + else { + pLastUpdate = Timer::GetCurrentTime(); + position_timer_counter++; + } + } + + if(HasVirus()) { + if(viral_timer.Check()) { + viral_timer_counter++; + for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) { + if(viral_spells[i]) { + if(viral_timer_counter % spells[viral_spells[i]].viral_timer == 0) { + SpreadVirus(viral_spells[i], viral_spells[i+1]); + } + } + } + } + if(viral_timer_counter > 999) + viral_timer_counter = 0; + } + + if(spellbonuses.GravityEffect == 1) { + if(gravity_timer.Check()) + DoGravityEffect(); + } + + if (shield_timer.Check()) + { + if (shield_target) + { + if (!CombatRange(shield_target)) + { + entity_list.MessageClose(this,false,100,0,"%s ceases shielding %s.",GetCleanName(),shield_target->GetCleanName()); + 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; + } + } + shield_target = 0; + shield_timer.Disable(); + } + } + else + { + shield_target = 0; + shield_timer.Disable(); + } + } + + adverrorinfo = 3; + SpellProcess(); + adverrorinfo = 4; + if (endupkeep_timer.Check() && !dead){ + DoEnduranceUpkeep(); + } + + if (tic_timer.Check() && !dead) { + CalcMaxHP(); + CalcMaxMana(); + CalcATK(); + CalcMaxEndurance(); + CalcRestState(); + DoHPRegen(); + DoManaRegen(); + DoEnduranceRegen(); + BuffProcess(); + DoStaminaUpdate(); + + if(tribute_timer.Check()) { + ToggleTribute(true); //re-activate the tribute. + } + + if (fishing_timer.Check()) { + GoFish(); + } + + if (autosave_timer.Check()) { + Save(0); + } + + if(m_pp.intoxication > 0) + { + --m_pp.intoxication; + CalcBonuses(); + } + } + } + + if (client_state == CLIENT_KICKED) { + Save(); + OnDisconnect(true); + cout << "Client disconnected (cs=k): " << GetName() << endl; + return false; + } + + if (client_state == DISCONNECTED) { + OnDisconnect(true); + cout << "Client disconnected (cs=d): " << GetName() << endl; + database.SetMQDetectionFlag(this->AccountName(), GetName(), "/MQInstantCamp: Possible instant camp disconnect.", zone->GetShortName()); + return false; + } + + if (client_state == CLIENT_ERROR) { + OnDisconnect(true); + cout << "Client disconnected (cs=e): " << GetName() << endl; + return false; + } + + if (client_state != CLIENT_LINKDEAD && !eqs->CheckState(ESTABLISHED)) { + OnDisconnect(true); + cout << "Client linkdead: " << name << endl; + + if (GetGM()) { + if (GetMerc()) + { + GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); + GetMerc()->Depop(); + } + return false; + } + else if(!linkdead_timer.Enabled()){ + linkdead_timer.Start(RuleI(Zone,ClientLinkdeadMS)); + client_state = CLIENT_LINKDEAD; + AI_Start(CLIENT_LD_TIMEOUT); + SendAppearancePacket(AT_Linkdead, 1); + } + } + + + /************ Get all packets from packet manager out queue and process them ************/ + adverrorinfo = 5; + + EQApplicationPacket *app = 0; +// if(eqs->GetState()==CLOSING && eqs->CheckActive()) + if(eqs->CheckState(CLOSING)) + { + //eqs->Close(); + //return false; + //handled below + } else { + while(ret && (app = (EQApplicationPacket *)eqs->PopPacket())) { + if(app) + ret = HandlePacket(app); + safe_delete(app); + } + } + +#ifdef REVERSE_AGGRO + //At this point, we are still connected, everything important has taken + //place, now check to see if anybody wants to aggro us. + // Everhood 6/15/06 - only if client is not feigned + if(ret && !GetFeigned() && scanarea_timer.Check()) { + entity_list.CheckClientAggro(this); + } +#endif + + 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()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); + GetMerc()->Depop(); + } + adverrorinfo = 811; + client_state = CLIENT_LINKDEAD; + if (/*!loggedin || */zoning || instalog || GetGM()) + { + adverrorinfo = 811; + Group *mygroup = GetGroup(); + if (mygroup) + { + adverrorinfo = 812; + if (!zoning) { + entity_list.MessageGroup(this,true,15,"%s logged out.",GetName()); + mygroup->DelMember(this); + } else { + entity_list.MessageGroup(this,true,15,"%s left the zone.",GetName()); + mygroup->MemberZoned(this); + } + + adverrorinfo = 813; + } + Raid *myraid = entity_list.GetRaidByClient(this); + if (myraid) + { + if (!zoning) { + //entity_list.MessageGroup(this,true,15,"%s logged out.",GetName()); + //mygroup->DelMember(this); + myraid->MemberZoned(this); + } else { + //entity_list.MessageGroup(this,true,15,"%s left the zone.",GetName()); + myraid->MemberZoned(this); + } + } + OnDisconnect(false); + return false; + } + else + { + adverrorinfo = 814; + LinkDead(); + } + OnDisconnect(true); + } + // EverHood Feign Death 2 minutes and zone forgets you + if (forget_timer.Check()) { + forget_timer.Disable(); + entity_list.ClearZoneFeignAggro(this); + Message(0,"Your enemies have forgotten you!"); + } + + return ret; +} + +//just a set of actions preformed all over in Client::Process +void Client::OnDisconnect(bool hard_disconnect) { + if(hard_disconnect) { + LeaveGroup(); + + Raid *MyRaid = entity_list.GetRaidByClient(this); + + if (MyRaid) + MyRaid->MemberZoned(this); + + if(this->IsClient()){ + if(parse->PlayerHasQuestSub("EVENT_DISCONNECT")) { + parse->EventPlayer(EVENT_DISCONNECT, this, "", 0); + } + } + } + + Mob *Other = trade->With(); + + if(Other) + { + mlog(TRADING__CLIENT, "Client disconnected during a trade. Returning their items."); + + FinishTrade(this); + + if(Other->IsClient()) + Other->CastToClient()->FinishTrade(Other); + + 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 + ClearAllProximities(); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); + FastQueuePacket(&outapp); + + Disconnect(); +} + +// Sends the client complete inventory used in character login + +// DO WE STILL NEED THE 'ITEMCOMBINED' CONDITIONAL CODE? -U + +//#ifdef ITEMCOMBINED +void Client::BulkSendInventoryItems() { + // For future reference: Only the parent item needs to be sent..the ItemInst already contains child ItemInst information + int16 slot_id = 0; + + // LINKDEAD TRADE ITEMS + // Move trade slot items back into normal inventory..need them there now for the proceeding validity checks -U + for(slot_id = 3000; slot_id <= 3007; slot_id++) { + ItemInst* inst = m_inv.PopItem(slot_id); + if(inst) { + bool is_arrow = (inst->GetItem()->ItemType == ItemTypeArrow) ? true : false; + int16 free_slot_id = m_inv.FindFreeSlot(inst->IsType(ItemClassContainer), true, inst->GetItem()->Size, is_arrow); + mlog(INVENTORY__ERROR, "Incomplete Trade Transaction: Moving %s from slot %i to %i", inst->GetItem()->Name, slot_id, free_slot_id); + PutItemInInventory(free_slot_id, *inst, false); + database.SaveInventory(character_id, NULL, slot_id); + safe_delete(inst); + } + } + + // 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 + + RemoveDuplicateLore(false); + MoveSlotNotAllowed(false); + + // The previous three method calls took care of moving/removing expired/illegal item placements -U + + //TODO: this function is just retarded... it re-allocates the buffer for every + //new item. It should be changed to loop through once, gather the + //lengths, and item packet pointers into an array (fixed length), and + //then loop again to build the packet. + //EQApplicationPacket *packets[50]; + //unsigned long buflen = 0; + //unsigned long pos = 0; + //memset(packets, 0, sizeof(packets)); + //foreach item in the invendor sections + // packets[pos++] = ReturnItemPacket(...) + // buflen += temp->size + //... + //allocat the buffer + //for r from 0 to pos + // put pos[r]->pBuffer into the buffer + //for r from 0 to pos + // safe_delete(pos[r]); + + uint32 size = 0; + uint16 i = 0; + map ser_items; + map::iterator itr; + + //Inventory items + for(slot_id = 0; slot_id <= 30; slot_id++) { + const ItemInst* inst = m_inv[slot_id]; + if(inst) { + string packet = inst->Serialize(slot_id); + ser_items[i++] = packet; + size += packet.length(); + } + } + + // Power Source + if(GetClientVersion() >= EQClientSoF) { + const ItemInst* inst = m_inv[9999]; + if(inst) { + string packet = inst->Serialize(9999); + ser_items[i++] = packet; + size += packet.length(); + } + } + + // Bank items + for(slot_id = 2000; slot_id <= 2023; slot_id++) { + const ItemInst* inst = m_inv[slot_id]; + if(inst) { + string packet = inst->Serialize(slot_id); + ser_items[i++] = packet; + size += packet.length(); + } + } + + // Shared Bank items + for(slot_id = 2500; slot_id <= 2501; slot_id++) { + const ItemInst* inst = m_inv[slot_id]; + if(inst) { + string packet = inst->Serialize(slot_id); + ser_items[i++] = packet; + size += packet.length(); + } + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_CharInventory, size); + uchar* ptr = outapp->pBuffer; + for(itr = ser_items.begin(); itr != ser_items.end(); itr++){ + int length = itr->second.length(); + if(length > 5) { + memcpy(ptr, itr->second.c_str(), length); + ptr += length; + } + } + //DumpPacket(outapp); + QueuePacket(outapp); + safe_delete(outapp); +} +/*#else +void Client::BulkSendInventoryItems() +{ + // Search all inventory buckets for items + bool deletenorent=database.NoRentExpired(GetName()); + // Worn items and Inventory items + int16 slot_id = 0; + if(deletenorent){//client was offline for more than 30 minutes, delete no rent items + RemoveNoRent(); + } + for (slot_id=0; slot_id<=30; slot_id++) { + const ItemInst* inst = m_inv[slot_id]; + if (inst){ + SendItemPacket(slot_id, inst, ItemPacketCharInventory); + } + } + // Bank items + for (slot_id=2000; slot_id<=2015; slot_id++) { + const ItemInst* inst = m_inv[slot_id]; + if (inst){ + SendItemPacket(slot_id, inst, ItemPacketCharInventory); + } + } + + // Shared Bank items + for (slot_id=2500; slot_id<=2501; slot_id++) { + const ItemInst* inst = m_inv[slot_id]; + if (inst){ + SendItemPacket(slot_id, inst, ItemPacketCharInventory); + } + } + + // LINKDEAD TRADE ITEMS + // If player went LD during a trade, they have items in the trade inventory + // slots. These items are now being put into their inventory (then queue up on cursor) + for (int16 trade_slot_id=3000; trade_slot_id<=3007; trade_slot_id++) { + const ItemInst* inst = m_inv[slot_id]; + if (inst) { + int16 free_slot_id = m_inv.FindFreeSlot(inst->IsType(ItemClassContainer), true, inst->GetItem()->Size); + DeleteItemInInventory(trade_slot_id, 0, false); + PutItemInInventory(free_slot_id, *inst, true); + } + } +} +#endif*/ + +void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { + const Item_Struct* handyitem = NULL; + 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 + zone->LoadNewMerchantData(merchant_id); + merlist = zone->merchanttable[merchant_id]; + if(merlist.size()==0) + return; + } + std::list tmp_merlist = zone->tmpmerchanttable[npcid]; + std::list::iterator tmp_itr; + + uint32 i=1; + uint8 handychance = 0; + for(itr = merlist.begin();itr != merlist.end() && iGetPrimaryFaction() : 0; + if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + handychance = MakeRandomInt(0, merlist.size() + tmp_merlist.size() - 1 ); + + item = database.GetItem(ml.item); + if(item) { + if(handychance==0) + handyitem=item; + else + handychance--; + 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))); + } + else + inst->SetPrice((item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate)); + inst->SetMerchantSlot(ml.slot); + inst->SetMerchantCount(-1); //unlimited + if(charges > 0) + inst->SetCharges(charges); + else + inst->SetCharges(1); + + SendItemPacket(ml.slot-1, inst, ItemPacketMerchant); + safe_delete(inst); + } + } + // Account for merchant lists with gaps. + if(ml.slot >= i) + 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))); + } + else + inst->SetPrice((item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate)); + inst->SetMerchantSlot(ml.slot); + inst->SetMerchantCount(ml.charges); + if(charges > 0) + inst->SetCharges(item->MaxCharges);//inst->SetCharges(charges); + else + inst->SetCharges(1); + SendItemPacket(ml.slot-1, inst, ItemPacketMerchant); + safe_delete(inst); + } + } + tmp_merlist.push_back(ml); + i++; + } + //this resets the slot + zone->tmpmerchanttable[npcid] = tmp_merlist; + if(merch != NULL && handyitem){ + char handy_id[8]={0}; + int greeting=MakeRandomInt(0, 4); + int greet_id=0; + switch(greeting){ + case 1: + greet_id=MERCHANT_GREETING; + break; + case 2: + greet_id=MERCHANT_HANDY_ITEM1; + break; + case 3: + greet_id=MERCHANT_HANDY_ITEM2; + break; + case 4: + greet_id=MERCHANT_HANDY_ITEM3; + break; + default: + greet_id=MERCHANT_HANDY_ITEM4; + } + 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); + else + Message_StringID(10,GENERIC_STRINGID_SAY,merch->GetCleanName(),handy_id,this->GetName()); + + merch->CastToNPC()->FaceTarget(this->CastToMob()); + } + +// safe_delete_array(cpi); +} + +uint8 Client::WithCustomer(uint16 NewCustomer){ + + if(NewCustomer == 0) { + CustomerID = 0; + return 0; + } + + if(CustomerID == 0) { + CustomerID = NewCustomer; + return 1; + } + + // Check that the player browsing our wares hasn't gone away. + + Client* c = entity_list.GetClientByID(CustomerID); + + if(!c) { + _log(TRADING__CLIENT, "Previous customer has gone away."); + CustomerID = NewCustomer; + return 1; + } + + return 0; +} + +void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 InstanceID, float x, float y, float z) +{ + if(PendingRezzXP < 0) { + // pendingrezexp is set to -1 if we are not expecting an OP_RezzAnswer + _log(SPELLS__REZ, "Unexpected OP_RezzAnswer. Ignoring it."); + Message(13, "You have already been resurrected.\n"); + return; + } + + if (Action == 1) + { + // Mark the corpse as rezzed in the database, just in case the corpse has buried, or the zone the + // corpse is in has shutdown since the rez spell was cast. + database.MarkCorpseAsRezzed(PendingRezzDBID); + _log(SPELLS__REZ, "Player %s got a %i Rezz, spellid %i in zone%i, instance id %i", + this->name, (uint16)spells[SpellID].base[0], + SpellID, ZoneID, InstanceID); + + this->BuffFadeAll(); + int SpellEffectDescNum = GetSpellEffectDescNum(SpellID); + // Rez spells with Rez effects have this DescNum (first is Titanium, second is 6.2 Client) + if((SpellEffectDescNum == 82) || (SpellEffectDescNum == 39067)) { + SetMana(0); + SetHP(GetMaxHP()/5); + SpellOnTarget(756, this); // Rezz effects + } + else { + SetMana(GetMaxMana()); + SetHP(GetMaxHP()); + } + if(spells[SpellID].base[0] < 100 && spells[SpellID].base[0] > 0 && PendingRezzXP > 0) + { + SetEXP(((int)(GetEXP()+((float)((PendingRezzXP / 100) * spells[SpellID].base[0])))), + GetAAXP(),true); + } + else if (spells[SpellID].base[0] == 100 && PendingRezzXP > 0) { + SetEXP((GetEXP() + PendingRezzXP), GetAAXP(), true); + } + + //Was sending the packet back to initiate client zone... + //but that could be abusable, so lets go through proper channels + MovePC(ZoneID, InstanceID, x, y, z, GetHeading(), 0, ZoneSolicited); + entity_list.RefreshClientXTargets(this); + } + PendingRezzXP = -1; + PendingRezzSpellID = 0; +} + +void Client::OPTGB(const EQApplicationPacket *app) +{ + if(!app) return; + if(!app->pBuffer) return; + + uint32 tgb_flag = *(uint32 *)app->pBuffer; + if(tgb_flag == 2) + Message_StringID(0, TGB() ? TGB_ON : TGB_OFF); + else + tgb = tgb_flag; +} + +void Client::OPMemorizeSpell(const EQApplicationPacket* app) +{ + if(app->size != sizeof(MemorizeSpell_Struct)) + { + LogFile->write(EQEMuLog::Error,"Wrong size on OP_MemorizeSpell. Got: %i, Expected: %i", app->size, sizeof(MemorizeSpell_Struct)); + DumpPacket(app); + return; + } + + const MemorizeSpell_Struct* memspell = (const MemorizeSpell_Struct*) app->pBuffer; + + if(!IsValidSpell(memspell->spell_id)) + { + Message(13, "Unexpected error: spell id out of range"); + return; + } + + if + ( + GetClass() > 16 || + GetLevel() < spells[memspell->spell_id].classes[GetClass()-1] + ) + { + char val1[20]={0}; + Message_StringID(13,SPELL_LEVEL_TO_LOW,ConvertArray(spells[memspell->spell_id].classes[GetClass()-1],val1),spells[memspell->spell_id].name); + //Message(13, "Unexpected error: Class cant use this spell at your level!"); + return; + } + + switch(memspell->scribing) + { + case memSpellScribing: { // scribing spell to book + const ItemInst* inst = m_inv[SLOT_CURSOR]; + + if(inst && inst->IsType(ItemClassCommon)) + { + const Item_Struct* item = inst->GetItem(); + + if(item && item->Scroll.Effect == (int32)(memspell->spell_id)) + { + ScribeSpell(memspell->spell_id, memspell->slot); + DeleteItemInInventory(SLOT_CURSOR, 1, true); + } + else + Message(0,"Scribing spell: inst exists but item does not or spell ids do not match."); + } + else + Message(0,"Scribing a spell without an inst on your cursor?"); + break; + } + case memSpellMemorize: { // memming spell + if(HasSpellScribed(memspell->spell_id)) + { + MemSpell(memspell->spell_id, memspell->slot); + } + else + { + database.SetMQDetectionFlag(AccountName(), GetName(), "OP_MemorizeSpell but we don't have this spell scribed...", zone->GetShortName()); + } + break; + } + case memSpellForget: { // unmemming spell + UnmemSpell(memspell->slot); + break; + } + } + + Save(); +} + +void Client::BreakInvis() +{ + if (invisible) + { + 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); + invisible = false; + invisible_undead = false; + invisible_animals = false; + hidden = false; + improved_hidden = false; + } +} + +static uint64 CoinTypeCoppers(uint32 type) { + switch(type) { + case COINTYPE_PP: + return(1000); + case COINTYPE_GP: + return(100); + case COINTYPE_SP: + return(10); + case COINTYPE_CP: + default: + break; + } + return(1); +} + +void Client::OPMoveCoin(const EQApplicationPacket* app) +{ + MoveCoin_Struct* mc = (MoveCoin_Struct*)app->pBuffer; + uint64 value = 0, amount_to_take = 0, amount_to_add = 0; + int32 *from_bucket = 0, *to_bucket = 0; + Mob* trader = trade->With(); + + //DumpPacket(app); + + // could just do a range, but this is clearer and explicit + if + ( + ( + mc->cointype1 != COINTYPE_PP && + mc->cointype1 != COINTYPE_GP && + mc->cointype1 != COINTYPE_SP && + mc->cointype1 != COINTYPE_CP + ) || + ( + mc->cointype2 != COINTYPE_PP && + mc->cointype2 != COINTYPE_GP && + mc->cointype2 != COINTYPE_SP && + mc->cointype2 != COINTYPE_CP + ) + ) + { + return; + } + + switch(mc->from_slot) + { + case -1: // destroy + { + // solar: I don't think you can move coin from the void, + // but need to check this + break; + } + case 0: // cursor + { + switch(mc->cointype1) + { + case COINTYPE_PP: + from_bucket = (int32 *) &m_pp.platinum_cursor; break; + case COINTYPE_GP: + from_bucket = (int32 *) &m_pp.gold_cursor; break; + case COINTYPE_SP: + from_bucket = (int32 *) &m_pp.silver_cursor; break; + case COINTYPE_CP: + from_bucket = (int32 *) &m_pp.copper_cursor; break; + } + break; + } + case 1: // inventory + { + switch(mc->cointype1) + { + case COINTYPE_PP: + from_bucket = (int32 *) &m_pp.platinum; break; + case COINTYPE_GP: + from_bucket = (int32 *) &m_pp.gold; break; + case COINTYPE_SP: + from_bucket = (int32 *) &m_pp.silver; break; + case COINTYPE_CP: + from_bucket = (int32 *) &m_pp.copper; break; + } + break; + } + case 2: // bank + { + uint32 distance = 0; + NPC *banker = entity_list.GetClosestBanker(this, distance); + if(!banker || distance > USE_NPC_RANGE2) + { + char *hacked_string = NULL; + MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(coin move) 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; + } + + switch(mc->cointype1) + { + case COINTYPE_PP: + from_bucket = (int32 *) &m_pp.platinum_bank; break; + case COINTYPE_GP: + from_bucket = (int32 *) &m_pp.gold_bank; break; + case COINTYPE_SP: + from_bucket = (int32 *) &m_pp.silver_bank; break; + case COINTYPE_CP: + from_bucket = (int32 *) &m_pp.copper_bank; break; + } + break; + } + case 3: // trade + { + // can't move coin from trade + break; + } + case 4: // shared bank + { + uint32 distance = 0; + NPC *banker = entity_list.GetClosestBanker(this, distance); + if(!banker || distance > USE_NPC_RANGE2) + { + char *hacked_string = NULL; + MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(shared coin move) 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; + } + if(mc->cointype1 == COINTYPE_PP) // there's only platinum here + from_bucket = (int32 *) &m_pp.platinum_shared; + break; + } + } + + switch(mc->to_slot) + { + case -1: // destroy + { + // no action required + break; + } + case 0: // cursor + { + switch(mc->cointype2) + { + case COINTYPE_PP: + to_bucket = (int32 *) &m_pp.platinum_cursor; break; + case COINTYPE_GP: + to_bucket = (int32 *) &m_pp.gold_cursor; break; + case COINTYPE_SP: + to_bucket = (int32 *) &m_pp.silver_cursor; break; + case COINTYPE_CP: + to_bucket = (int32 *) &m_pp.copper_cursor; break; + } + break; + } + case 1: // inventory + { + switch(mc->cointype2) + { + case COINTYPE_PP: + to_bucket = (int32 *) &m_pp.platinum; break; + case COINTYPE_GP: + to_bucket = (int32 *) &m_pp.gold; break; + case COINTYPE_SP: + to_bucket = (int32 *) &m_pp.silver; break; + case COINTYPE_CP: + to_bucket = (int32 *) &m_pp.copper; break; + } + break; + } + case 2: // bank + { + uint32 distance = 0; + NPC *banker = entity_list.GetClosestBanker(this, distance); + if(!banker || distance > USE_NPC_RANGE2) + { + char *hacked_string = NULL; + MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(coin move) 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; + } + switch(mc->cointype2) + { + case COINTYPE_PP: + to_bucket = (int32 *) &m_pp.platinum_bank; break; + case COINTYPE_GP: + to_bucket = (int32 *) &m_pp.gold_bank; break; + case COINTYPE_SP: + to_bucket = (int32 *) &m_pp.silver_bank; break; + case COINTYPE_CP: + to_bucket = (int32 *) &m_pp.copper_bank; break; + } + break; + } + case 3: // trade + { + if(trader) + { + switch(mc->cointype2) + { + case COINTYPE_PP: + to_bucket = (int32 *) &trade->pp; break; + case COINTYPE_GP: + to_bucket = (int32 *) &trade->gp; break; + case COINTYPE_SP: + to_bucket = (int32 *) &trade->sp; break; + case COINTYPE_CP: + to_bucket = (int32 *) &trade->cp; break; + } + } + break; + } + case 4: // shared bank + { + uint32 distance = 0; + NPC *banker = entity_list.GetClosestBanker(this, distance); + if(!banker || distance > USE_NPC_RANGE2) + { + char *hacked_string = NULL; + MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(shared coin move) 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; + } + if(mc->cointype2 == COINTYPE_PP) // there's only platinum here + to_bucket = (int32 *) &m_pp.platinum_shared; + break; + } + } + + if(!from_bucket) + { + return; + } + + // don't allow them to go into negatives (from our point of view) + amount_to_take = *from_bucket < mc->amount ? *from_bucket : mc->amount; + + // solar: if you move 11 gold into a bank platinum location, the packet + // will say 11, but the client will have 1 left on their cursor, so we have + // to figure out the conversion ourselves + + amount_to_add = amount_to_take * ((float)CoinTypeCoppers(mc->cointype1) / (float)CoinTypeCoppers(mc->cointype2)); + + // the amount we're adding could be different than what was requested, so + // we have to adjust the amount we take as well + amount_to_take = amount_to_add * ((float)CoinTypeCoppers(mc->cointype2) / (float)CoinTypeCoppers(mc->cointype1)); + + // solar: now we should have a from_bucket, a to_bucket, an amount_to_take + // and an amount_to_add + +#ifdef SOLAR + printf("taking %d coins, adding %d coins\n", amount_to_take, amount_to_add); +#endif + + // solar: now we actually take it from the from bucket. if there's an error + // with the destination slot, they lose their money + *from_bucket -= amount_to_take; + // why are intentionally inducing a crash here rather than letting the code attempt to stumble on? + // assert(*from_bucket >= 0); + + if(to_bucket) + { + if(*to_bucket + amount_to_add > *to_bucket) // overflow check + *to_bucket += amount_to_add; + + //shared bank plat + if (RuleB(Character, SharedBankPlat)) + { + if (to_bucket == &m_pp.platinum_shared || from_bucket == &m_pp.platinum_shared) + { + if (from_bucket == &m_pp.platinum_shared) + amount_to_add = 0 - amount_to_take; + + database.SetSharedPlatinum(AccountID(),amount_to_add); + } + } + } + +#ifdef SOLAR + printf("from bucket = %d ", *from_bucket); + if(to_bucket) + printf("to bucket = %d", *to_bucket); + printf("\n"); +#endif + + // if this is a trade move, inform the person being traded with + if(mc->to_slot == 3 && trader && trader->IsClient()) + { + + // If one party accepted the trade then some coin was added, their state needs to be reset + trade->state = Trading; + Mob* with = trade->With(); + if (with) + with->trade->state = Trading; + + Client* recipient = trader->CastToClient(); + recipient->Message(15, "%s adds some coins to the trade.", GetName()); + recipient->Message(15, "The total trade is: %i PP, %i GP, %i SP, %i CP", + trade->pp, trade->gp, + trade->sp, trade->cp + ); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeCoins,sizeof(TradeCoin_Struct)); + TradeCoin_Struct* tcs = (TradeCoin_Struct*)outapp->pBuffer; + tcs->trader = trader->GetID(); + tcs->slot = mc->cointype2; + tcs->unknown5 = 0x4fD2; + tcs->unknown7 = 0; + tcs->amount = amount_to_add; + recipient->QueuePacket(outapp); + safe_delete(outapp); + } + + Save(); +} + +void Client::OPGMTraining(const EQApplicationPacket *app) +{ + + EQApplicationPacket* outapp = app->Copy(); + GMTrainee_Struct* gmtrain = (GMTrainee_Struct*) outapp->pBuffer; + + Mob* pTrainer = entity_list.GetMob(gmtrain->npcid); + + if(!pTrainer || !pTrainer->IsNPC() || pTrainer->GetClass() < WARRIORGM || pTrainer->GetClass() > BERSERKERGM) + return; + + //you can only use your own trainer, client enforces this, but why trust it + int trains_class = pTrainer->GetClass() - (WARRIORGM - WARRIOR); + if(GetClass() != trains_class) + return; + + //you have to be somewhat close to a trainer to be properly using them + if(DistNoRoot(*pTrainer) > USE_NPC_RANGE2) + return; + + SkillType sk; + for (sk = _1H_BLUNT; sk <= HIGHEST_SKILL; sk = (SkillType)(sk+1)) { + if(sk == TINKERING && GetRace() != GNOME) { + gmtrain->skills[sk] = 0; //Non gnomes can't tinker! + } else { + gmtrain->skills[sk] = GetMaxSkillAfterSpecializationRules(sk, MaxSkill(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. + } + } + + uchar ending[]={0x34,0x87,0x8a,0x3F,0x01 + ,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9 + ,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9 + ,0x76,0x75,0x3f}; + memcpy(&outapp->pBuffer[outapp->size-40],ending,sizeof(ending)); + FastQueuePacket(&outapp); + + // welcome message + if (pTrainer && pTrainer->IsNPC()) + { + pTrainer->Say_StringID(MakeRandomInt(1204, 1207), GetCleanName()); + } +} + +void Client::OPGMEndTraining(const EQApplicationPacket *app) +{ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GMEndTrainingResponse, 0); + GMTrainEnd_Struct *p = (GMTrainEnd_Struct *)app->pBuffer; + + FastQueuePacket(&outapp); + + Mob* pTrainer = entity_list.GetMob(p->npcid); + if(!pTrainer || !pTrainer->IsNPC() || pTrainer->GetClass() < WARRIORGM || pTrainer->GetClass() > BERSERKERGM) + return; + + //you can only use your own trainer, client enforces this, but why trust it + int trains_class = pTrainer->GetClass() - (WARRIORGM - WARRIOR); + if(GetClass() != trains_class) + return; + + //you have to be somewhat close to a trainer to be properly using them + if(DistNoRoot(*pTrainer) > USE_NPC_RANGE2) + return; + + // goodbye message + if (pTrainer->IsNPC()) + { + pTrainer->Say_StringID(MakeRandomInt(1208, 1211), GetCleanName()); + } +} + +void Client::OPGMTrainSkill(const EQApplicationPacket *app) +{ + if(!m_pp.points) + return; + + int Cost = 0; + + GMSkillChange_Struct* gmskill = (GMSkillChange_Struct*) app->pBuffer; + + Mob* pTrainer = entity_list.GetMob(gmskill->npcid); + if(!pTrainer || !pTrainer->IsNPC() || pTrainer->GetClass() < WARRIORGM || pTrainer->GetClass() > BERSERKERGM) + return; + + //you can only use your own trainer, client enforces this, but why trust it + int trains_class = pTrainer->GetClass() - (WARRIORGM - WARRIOR); + if(GetClass() != trains_class) + return; + + //you have to be somewhat close to a trainer to be properly using them + if(DistNoRoot(*pTrainer) > USE_NPC_RANGE2) + return; + + if (gmskill->skillbank == 0x01) + { + // languages go here + if (gmskill->skill_id > 25) + { + cout << "Wrong Training Skill (languages)" << endl; + DumpPacket(app); + return; + } + int AdjustedSkillLevel = GetLanguageSkill(gmskill->skill_id) - 10; + if(AdjustedSkillLevel > 0) + Cost = AdjustedSkillLevel * AdjustedSkillLevel * AdjustedSkillLevel / 100; + + IncreaseLanguageSkill(gmskill->skill_id); + } + else if (gmskill->skillbank == 0x00) + { + // normal skills go here + if (gmskill->skill_id > HIGHEST_SKILL) + { + cout << "Wrong Training Skill (abilities)" << endl; + DumpPacket(app); + return; + } + + SkillType skill = (SkillType) gmskill->skill_id; + + if(!CanHaveSkill(skill)) { + mlog(CLIENT__ERROR, "Tried to train skill %d, which is not allowed.", skill); + return; + } + + uint16 skilllevel = GetRawSkill(skill); + if(skilllevel == 0) { + //this is a new skill.. + uint16 t_level = SkillTrainLevel(skill, GetClass()); + if (t_level == 0) + { + return; + } + SetSkill(skill, t_level); + } else { + switch(skill) { + case BREWING: + case MAKE_POISON: + case TINKERING: + case RESEARCH: + case ALCHEMY: + case BAKING: + case TAILORING: + case BLACKSMITHING: + case FLETCHING: + case JEWELRY_MAKING: + case POTTERY: + if(skilllevel >= RuleI(Skills, MaxTrainTradeskills)) { + Message_StringID(13, MORE_SKILLED_THAN_I, pTrainer->GetCleanName()); + return; + } + break; + case SPECIALIZE_ABJURE: + case SPECIALIZE_ALTERATION: + case SPECIALIZE_CONJURATION: + case SPECIALIZE_DIVINATION: + case SPECIALIZE_EVOCATION: + if(skilllevel >= RuleI(Skills, MaxTrainSpecializations)) { + Message_StringID(13, MORE_SKILLED_THAN_I, pTrainer->GetCleanName()); + return; + } + default: + break; + } + + int MaxSkillValue = MaxSkill(skill); + if (skilllevel >= MaxSkillValue) + { + // Don't allow training over max skill level + Message_StringID(13, MORE_SKILLED_THAN_I, pTrainer->GetCleanName()); + return; + } + + if(gmskill->skill_id >= SPECIALIZE_ABJURE && gmskill->skill_id <= SPECIALIZE_EVOCATION) + { + int MaxSpecSkill = GetMaxSkillAfterSpecializationRules(skill, MaxSkillValue); + if (skilllevel >= MaxSpecSkill) + { + // Restrict specialization training to follow the rules + Message_StringID(13, MORE_SKILLED_THAN_I, pTrainer->GetCleanName()); + return; + } + } + + // Client train a valid skill + // + int AdjustedSkillLevel = skilllevel - 10; + + if(AdjustedSkillLevel > 0) + Cost = AdjustedSkillLevel * AdjustedSkillLevel * AdjustedSkillLevel / 100; + + SetSkill(skill, skilllevel + 1); + + + } + } + + if(GetClientVersion() >= EQClientSoF) { + // The following packet decreases the skill points left in the Training Window and + // produces the 'You have increased your skill / learned the basics of' message. + // + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMTrainSkillConfirm, sizeof(GMTrainSkillConfirm_Struct)); + + GMTrainSkillConfirm_Struct *gmtsc = (GMTrainSkillConfirm_Struct *)outapp->pBuffer; + gmtsc->SkillID = gmskill->skill_id; + + if(gmskill->skillbank == 1) { + gmtsc->NewSkill = (GetLanguageSkill(gmtsc->SkillID) == 1); + gmtsc->SkillID += 100; + } + else + gmtsc->NewSkill = (GetRawSkill((SkillType)gmtsc->SkillID) == 1); + + gmtsc->Cost = Cost; + + strcpy(gmtsc->TrainerName, pTrainer->GetCleanName()); + QueuePacket(outapp); + safe_delete(outapp); + } + + if(Cost) + TakeMoneyFromPP(Cost); + + m_pp.points--; +} + +// this is used for /summon and /corpse +void Client::OPGMSummon(const EQApplicationPacket *app) +{ + GMSummon_Struct* gms = (GMSummon_Struct*) app->pBuffer; + Mob* st = entity_list.GetMob(gms->charname); + + if(st && st->IsCorpse()) + { + st->CastToCorpse()->Summon(this, false, true); + } + else + { + if(admin < 80) + { + return; + } + if(st) + { + Message(0, "Local: Summoning %s to %f, %f, %f", gms->charname, gms->x, gms->y, gms->z); + if (st->IsClient() && (st->CastToClient()->GetAnon() != 1 || this->Admin() >= st->CastToClient()->Admin())) + st->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), (float)gms->x, (float)gms->y, (float)gms->z, this->GetHeading(), true); + else + st->GMMove(this->GetX(), this->GetY(), this->GetZ(),this->GetHeading()); + } + else + { + uint8 tmp = gms->charname[strlen(gms->charname)-1]; + if (!worldserver.Connected()) + { + Message(0, "Error: World server disconnected"); + } + else if (tmp < '0' || tmp > '9') // dont send to world if it's not a player's name + { + ServerPacket* pack = new ServerPacket(ServerOP_ZonePlayer, sizeof(ServerZonePlayer_Struct)); + ServerZonePlayer_Struct* szp = (ServerZonePlayer_Struct*) pack->pBuffer; + strcpy(szp->adminname, this->GetName()); + szp->adminrank = this->Admin(); + strcpy(szp->name, gms->charname); + strcpy(szp->zone, zone->GetShortName()); + szp->x_pos = (float)gms->x; + szp->y_pos = (float)gms->y; + szp->z_pos = (float)gms->z; + szp->ignorerestrictions = 2; + worldserver.SendPacket(pack); + safe_delete(pack); + } + else { + //all options have been exhausted + //summon our target... + if(GetTarget() && GetTarget()->IsCorpse()){ + GetTarget()->CastToCorpse()->Summon(this, false, true); + } + } + } + } +} + +void Client::DoHPRegen() { + SetHP(GetHP() + CalcHPRegen() + RestRegenHP); + SendHPUpdate(); +} + +void Client::DoManaRegen() { + if (GetMana() >= max_mana) + return; + + SetMana(GetMana() + CalcManaRegen() + RestRegenMana); + SendManaUpdatePacket(); +} + + +void Client::DoStaminaUpdate() { + if(!stamina_timer.Check()) + return; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); + Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer; + + if(zone->GetZoneID() != 151) { + if (m_pp.hunger_level > 0) + m_pp.hunger_level-=35; + if (m_pp.thirst_level > 0) + m_pp.thirst_level-=35; + sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level; + sta->water = m_pp.thirst_level> 6000 ? 6000 : m_pp.thirst_level; + } + else { + // No auto food/drink consumption in the Bazaar + sta->food = 6000; + sta->water = 6000; + } + FastQueuePacket(&outapp); +} + +void Client::DoEnduranceRegen() +{ + if(GetEndurance() >= GetMaxEndurance()) + return; + + SetEndurance(GetEndurance() + CalcEnduranceRegen() + RestRegenEndurance); +} + +void Client::DoEnduranceUpkeep() { + int upkeep_sum = 0; + + int cost_redux = spellbonuses.EnduranceReduction + itembonuses.EnduranceReduction; + + 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) { + if(cost_redux > 0) { + if(upkeep <= cost_redux) + continue; //reduced to 0 + upkeep -= cost_redux; + } + if((upkeep+upkeep_sum) > GetEndurance()) { + //they do not have enough to keep this one going. + BuffFadeBySlot(buffs_i); + } else { + upkeep_sum += upkeep; + } + } + } + } + + if(upkeep_sum != 0) + SetEndurance(GetEndurance() - upkeep_sum); +} + +void Client::CalcRestState() { + + // This method calculates rest state HP and mana regeneration. + // The client must have been out of combat for RuleI(Character, RestRegenTimeToActivate) seconds, + // must be sitting down, and must not have any detrimental spells affecting them. + // + if(!RuleI(Character, RestRegenPercent)) + return; + + RestRegenHP = RestRegenMana = RestRegenEndurance = 0; + + if(AggroCount || !IsSitting()) + return; + + if(!rest_timer.Check(false)) + return; + + uint32 buff_count = GetMaxTotalSlots(); + for (unsigned int j = 0; j < buff_count; j++) { + if(buffs[j].spellid != SPELL_UNKNOWN) { + if(IsDetrimentalSpell(buffs[j].spellid) && (buffs[j].ticsremaining > 0)) + if(!DetrimentalSpellAllowsRest(buffs[j].spellid)) + return; + } + } + + RestRegenHP = (GetMaxHP() * RuleI(Character, RestRegenPercent) / 100); + + RestRegenMana = (GetMaxMana() * RuleI(Character, RestRegenPercent) / 100); + + if(RuleB(Character, RestRegenEndurance)) + RestRegenEndurance = (GetMaxEndurance() * RuleI(Character, RestRegenPercent) / 100); +} + +void Client::DoTracking() +{ + if(TrackingID == 0) + return; + + Mob *m = entity_list.GetMob(TrackingID); + + if(!m || m->IsCorpse()) + { + Message_StringID(MT_Skills, TRACK_LOST_TARGET); + + TrackingID = 0; + + return; + } + + float RelativeHeading = GetHeading() - CalculateHeadingToTarget(m->GetX(), m->GetY()); + + if(RelativeHeading < 0) + RelativeHeading += 256; + + if((RelativeHeading <= 16) || (RelativeHeading >= 240)) + { + Message_StringID(MT_Skills, TRACK_STRAIGHT_AHEAD, m->GetCleanName()); + } + else if((RelativeHeading > 16) && (RelativeHeading <= 48)) + { + Message_StringID(MT_Skills, TRACK_AHEAD_AND_TO, m->GetCleanName(), "right"); + } + else if((RelativeHeading > 48) && (RelativeHeading <= 80)) + { + Message_StringID(MT_Skills, TRACK_TO_THE, m->GetCleanName(), "right"); + } + else if((RelativeHeading > 80) && (RelativeHeading <= 112)) + { + Message_StringID(MT_Skills, TRACK_BEHIND_AND_TO, m->GetCleanName(), "right"); + } + else if((RelativeHeading > 112) && (RelativeHeading <= 144)) + { + Message_StringID(MT_Skills, TRACK_BEHIND_YOU, m->GetCleanName()); + } + else if((RelativeHeading > 144) && (RelativeHeading <= 176)) + { + Message_StringID(MT_Skills, TRACK_BEHIND_AND_TO, m->GetCleanName(), "left"); + } + else if((RelativeHeading > 176) && (RelativeHeading <= 208)) + { + Message_StringID(MT_Skills, TRACK_TO_THE, m->GetCleanName(), "left"); + } + else if((RelativeHeading > 208) && (RelativeHeading < 240)) + { + Message_StringID(MT_Skills, TRACK_AHEAD_AND_TO, m->GetCleanName(), "left"); + } +} + +void Client::HandleRespawnFromHover(uint32 Option) +{ + RespawnFromHoverTimer.Disable(); + + if(Option == 1) // Resurrect + { + if((PendingRezzXP < 0) || (PendingRezzSpellID == 0)) + { + _log(SPELLS__REZ, "Unexpected Rezz from hover request."); + return; + } + SetHP(GetMaxHP() / 5); + + Corpse* corpse = entity_list.GetCorpseByName(PendingRezzCorpseName.c_str()); + + if(corpse) + { + x_pos = corpse->GetX(); + y_pos = corpse->GetY(); + z_pos = corpse->GetZ(); + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZonePlayerToBind, sizeof(ZonePlayerToBind_Struct) + 10); + ZonePlayerToBind_Struct* gmg = (ZonePlayerToBind_Struct*) outapp->pBuffer; + + gmg->bind_zone_id = zone->GetZoneID(); + gmg->bind_instance_id = zone->GetInstanceID(); + gmg->x = GetX(); + gmg->y = GetY(); + gmg->z = GetZ(); + gmg->heading = GetHeading(); + strcpy(gmg->zone_name, "Resurrect"); + + FastQueuePacket(&outapp); + + ClearHover(); + SendHPUpdate(); + OPRezzAnswer(1, PendingRezzSpellID, zone->GetZoneID(), zone->GetInstanceID(), GetX(), GetY(), GetZ()); + + if (corpse && corpse->IsCorpse()) { + _log(SPELLS__REZ, "Hover Rez in zone %s for corpse %s", + zone->GetShortName(), PendingRezzCorpseName.c_str()); + + _log(SPELLS__REZ, "Found corpse. Marking corpse as rezzed."); + + corpse->Rezzed(true); + corpse->CompleteRezz(); + } + return; + } + + // Respawn at Bind Point. + // + if(m_pp.binds[0].zoneId == zone->GetZoneID()) + { + PendingRezzSpellID = 0; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZonePlayerToBind, sizeof(ZonePlayerToBind_Struct) + 14); + ZonePlayerToBind_Struct* gmg = (ZonePlayerToBind_Struct*) outapp->pBuffer; + + gmg->bind_zone_id = m_pp.binds[0].zoneId; + gmg->x = m_pp.binds[0].x; + gmg->y = m_pp.binds[0].y; + gmg->z = m_pp.binds[0].z; + gmg->heading = 0; + strcpy(gmg->zone_name, "Bind Location"); + + FastQueuePacket(&outapp); + + CalcBonuses(); + SetHP(GetMaxHP()); + SetMana(GetMaxMana()); + SetEndurance(GetMaxEndurance()); + + x_pos = m_pp.binds[0].x; + y_pos = m_pp.binds[0].y; + z_pos = m_pp.binds[0].z; + + ClearHover(); + entity_list.RefreshClientXTargets(this); + SendHPUpdate(); + + } + else + { + if(isgrouped) + { + Group *g = GetGroup(); + if(g) + g->MemberZoned(this); + } + + Raid* r = entity_list.GetRaidByClient(this); + + if(r) + r->MemberZoned(this); + + m_pp.zone_id = m_pp.binds[0].zoneId; + m_pp.zoneInstance = 0; + database.MoveCharacterToZone(this->CharacterID(), database.GetZoneName(m_pp.zone_id)); + + Save(); + + GoToDeath(); + } +} + +void Client::ClearHover() +{ + // Our Entity ID is currently zero, set in Client::Death + SetID(entity_list.GetFreeID()); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct)); + ServerZoneEntry_Struct* sze = (ServerZoneEntry_Struct*)outapp->pBuffer; + + FillSpawnStruct(&sze->player,CastToMob()); + + sze->player.spawn.NPC = 0; + sze->player.spawn.z += 6; //arbitrary lift, seems to help spawning under zone. + + entity_list.QueueClients(this, outapp, false); + safe_delete(outapp); + + if(IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) + { + EQApplicationPacket *outapp = MakeBuffsPacket(false); + CastToClient()->FastQueuePacket(&outapp); + } + + dead = false; +} + +void Client::HandleLFGuildResponse(ServerPacket *pack) +{ + pack->SetReadPosition(8); + + char Tmp[257]; + + pack->ReadString(Tmp); + + pack->ReadSkipBytes(4); + uint32 SubType, NumberOfMatches; + + SubType = pack->ReadUInt32(); + + switch(SubType) + { + case QSG_LFGuild_PlayerMatches: + { + NumberOfMatches = pack->ReadUInt32(); + uint32 StartOfMatches = pack->GetReadPosition(); + uint32 i = NumberOfMatches; + uint32 PacketSize = 12; + while(i > 0) + { + pack->ReadString(Tmp); + PacketSize += strlen(Tmp) + 1; + pack->ReadString(Tmp); + PacketSize += strlen(Tmp) + 1; + PacketSize += 16; + pack->ReadSkipBytes(16); + --i; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, PacketSize); + outapp->WriteUInt32(3); + outapp->WriteUInt32(0xeb63); // Don't know the significance of this value. + outapp->WriteUInt32(NumberOfMatches); + pack->SetReadPosition(StartOfMatches); + + while(NumberOfMatches > 0) + { + pack->ReadString(Tmp); + outapp->WriteString(Tmp); + pack->ReadString(Tmp); + uint32 Level = pack->ReadUInt32(); + uint32 Class = pack->ReadUInt32(); + uint32 AACount = pack->ReadUInt32(); + uint32 TimeZone = pack->ReadUInt32(); + outapp->WriteUInt32(Level); + outapp->WriteUInt32(Class); + outapp->WriteUInt32(AACount); + outapp->WriteUInt32(TimeZone); + outapp->WriteString(Tmp); + --NumberOfMatches; + } + + FastQueuePacket(&outapp); + break; + } + case QSG_LFGuild_RequestPlayerInfo: + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, sizeof(LFGuild_PlayerToggle_Struct)); + LFGuild_PlayerToggle_Struct *pts = (LFGuild_PlayerToggle_Struct *)outapp->pBuffer; + + pts->Command = 0; + pack->ReadString(pts->Comment); + pts->TimeZone = pack->ReadUInt32(); + pts->TimePosted = pack->ReadUInt32(); + pts->Toggle = pack->ReadUInt32(); + + FastQueuePacket(&outapp); + + break; + } + case QSG_LFGuild_GuildMatches: + { + NumberOfMatches = pack->ReadUInt32(); + uint32 StartOfMatches = pack->GetReadPosition(); + uint32 i = NumberOfMatches; + uint32 PacketSize = 12; + while(i > 0) + { + pack->ReadString(Tmp); + PacketSize += strlen(Tmp) + 1; + pack->ReadSkipBytes(4); + pack->ReadString(Tmp); + PacketSize += strlen(Tmp) + 1; + PacketSize += 4; + --i; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, PacketSize); + outapp->WriteUInt32(4); + outapp->WriteUInt32(0xeb63); + outapp->WriteUInt32(NumberOfMatches); + pack->SetReadPosition(StartOfMatches); + + while(NumberOfMatches > 0) + { + pack->ReadString(Tmp); + uint32 TimeZone = pack->ReadUInt32(); + outapp->WriteString(Tmp); + outapp->WriteUInt32(TimeZone); + pack->ReadString(Tmp); + outapp->WriteString(Tmp); + --NumberOfMatches; + } + FastQueuePacket(&outapp); + + break; + } + case QSG_LFGuild_RequestGuildInfo: + { + + char Comments[257]; + uint32 FromLevel, ToLevel, Classes, AACount, TimeZone, TimePosted; + + pack->ReadString(Comments); + FromLevel = pack->ReadUInt32(); + ToLevel = pack->ReadUInt32(); + Classes = pack->ReadUInt32(); + AACount = pack->ReadUInt32(); + TimeZone = pack->ReadUInt32(); + TimePosted = pack->ReadUInt32(); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, sizeof(LFGuild_GuildToggle_Struct)); + + LFGuild_GuildToggle_Struct *gts = (LFGuild_GuildToggle_Struct *)outapp->pBuffer; + gts->Command = 1; + strcpy(gts->Comment, Comments); + gts->FromLevel = FromLevel; + gts->ToLevel = ToLevel; + gts->Classes = Classes; + gts->AACount = AACount; + gts->TimeZone = TimeZone; + gts->Toggle = 1; + gts->TimePosted = TimePosted; + gts->Name[0] = 0; + + FastQueuePacket(&outapp); + + break; + } + + default: + break; + } + +} + +void Client::SendLFGuildStatus() +{ + ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 17); + + pack->WriteUInt32(zone->GetZoneID()); + pack->WriteUInt32(zone->GetInstanceID()); + pack->WriteString(GetName()); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_RequestPlayerInfo); + + worldserver.SendPacket(pack); + safe_delete(pack); + +} + +void Client::SendGuildLFGuildStatus() +{ + ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + +strlen(guild_mgr.GetGuildName(GuildID())) + 18); + + pack->WriteUInt32(zone->GetZoneID()); + pack->WriteUInt32(zone->GetInstanceID()); + pack->WriteString(GetName()); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_RequestGuildInfo); + pack->WriteString(guild_mgr.GetGuildName(GuildID())); + + worldserver.SendPacket(pack); + safe_delete(pack); +} diff --git a/zone/command.cpp b/zone/command.cpp new file mode 100644 index 000000000..a93002785 --- /dev/null +++ b/zone/command.cpp @@ -0,0 +1,11659 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2002 EQEMu Development Team (http://eqemulator.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 +*/ + +/* + + To add a new command 3 things must be done: + + 1. At the bottom of command.h you must add a prototype for it. + 2. Add the function in this file. + 3. In the command_init function you must add a call to command_add + for your function. If you want an alias for your command, add + a second call to command_add with the descriptin and access args + set to NULL and 0 respectively since they aren't used when adding + an alias. The function pointers being equal is makes it an alias. + The access level you set with command_add is only a default if + the command isn't listed in the addon.ini file. + +*/ + +#include +#include +#include +#include + +#ifdef _WINDOWS +#define strcasecmp _stricmp +#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/MiscFunctions.h" +//#include "../common/servertalk.h" // for oocmute and revoke +#include "worldserver.h" +#include "masterentity.h" +#include "map.h" +#include "watermap.h" +#include "features.h" +#include "pathing.h" +#include "client_logs.h" +#include "guild_mgr.h" +#include "titles.h" +#include "../common/patches/patches.h" + +// these should be in the headers... +extern WorldServer worldserver; +extern bool spells_loaded; +extern TaskManager *taskmanager; + +#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 + +// this is the pointer to the dispatch function, updated once +// init has been performed to point at the real function +int (*command_dispatch)(Client *,char const *)=command_notavail; + + +void command_bestz(Client *c, const Seperator *message); +void command_pf(Client *c, const Seperator *message); + +map commandlist; + +//All allocated CommandRecords get put in here so they get deleted on shutdown +LinkedList cleanup_commandlist; + + +/* + * command_notavail + * This is the default dispatch function when commands aren't loaded. + * + * Parameters: + * not used + * + */ +int command_notavail(Client *c, const char *message) +{ + c->Message(13, "Commands not available."); + return -1; +} + +/*****************************************************************************/ +/* the rest below here could be in a dynamically loaded module eventually */ +/*****************************************************************************/ + +/* + +Access Levels: + +0 Normal +10 * Steward * +20 * Apprentice Guide * +50 * Guide * +80 * QuestTroupe * +81 * Senior Guide * +85 * GM-Tester * +90 * EQ Support * +95 * GM-Staff * +100 * GM-Admin * +150 * GM-Lead Admin * +160 * QuestMaster * +170 * GM-Areas * +180 * GM-Coder * +200 * GM-Mgmt * +250 * GM-Impossible * + +*/ + +/* + * command_init + * initializes the command list, call at startup + * + * Parameters: + * none + * + * When adding a command, if it's the first time that function pointer is + * used it is a new command. If that function pointer is used for another + * command, the command is added as an alias; description and access level + * are not used and can be NULL. + * + */ +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("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("eitem","- Changes item stats",200,command_eitem) || + 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",NULL,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) || + command_add("summon","[charname] - Summons your player/npc/corpse target, or charname if specified",80,command_summon) || + 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",NULL,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) || + command_add("petitioninfo","[petition number] - Get info about a petition",20,command_petitioninfo) || + command_add("delpetition","[petition number] - Delete a petition",20,command_delpetition) || + command_add("listnpcs","[name/range] - Search NPCs",20,command_listnpcs) || + 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",NULL,0,command_invul) || + command_add("hideme","[on/off] - Hide yourself from spawn lists.",80,command_hideme) || + command_add("gmhideme",NULL,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) || + command_add("zstats","- Show info about zone header",80,command_zstats) || + command_add("zsave"," - Saves zheader to the database",80,command_zsave) || + command_add("permaclass","[classnum] - Change your or your player target's class (target is disconnected)",80,command_permaclass) || + command_add("permarace","[racenum] - Change your or your player target's race (zone to take effect)",80,command_permarace) || + 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",NULL,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) || + command_add("zsafecoords","[x] [y] [z] - Set safe coords",80,command_zsafecoords) || + 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("setitemstatus","[itemid] [status] - Set the minimum admin status required to use itemid",100,command_setitemstatus) || + command_add("ai","[factionid/spellslist/con/guard/roambox/stop/start] - Modify AI on NPC target",100,command_ai) || + 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) || + command_add("setlsinfo","[email] [password] - Set login server email address and password (if supported by login server)",10,command_setlsinfo) || + command_add("grid","[add/delete] [grid_num] [wandertype] [pausetype] - Create/delete a wandering grid",170,command_grid) || + command_add("wp","[add/delete] [grid_num] [pause] [wp_num] [-h] - Add/delete a waypoint to/from a wandering grid",170,command_wp) || + command_add("wpadd","[pause] [-h] - Add your current location as a waypoint to your NPC target's AI path",170,command_wpadd) || + command_add("wpinfo","- Show waypoint info about your NPC target",170,command_wpinfo) || + command_add("iplookup","[charname] - Look up IP address of charname",200,command_iplookup) || + command_add("size","[size] - Change size of you or your target",50,command_size) || + command_add("mana","- Fill your or your target's mana",50,command_mana) || + command_add("flymode","[0/1/2] - Set your or your player target's flymode to off/on/levitate",50,command_flymode) || + command_add("showskills","- Show the values of your or your player target's skills",50,command_showskills) || + command_add("findspell","[searchstring] - Search for a spell",50,command_findspell) || + command_add("spfind",NULL,0,command_findspell) || + command_add("castspell","[spellid] - Cast a spell",50,command_castspell) || + command_add("cast",NULL,0,command_castspell) || + command_add("setlanguage","[language ID] [value] - Set your target's language skillnum to value",50,command_setlanguage) || + command_add("setskill","[skillnum] [value] - Set your target's skill skillnum to value",50,command_setskill) || + command_add("setskillall","[value] - Set all of your target's skills to value",50,command_setskillall) || + command_add("setallskill",NULL,0,command_setskillall) || + command_add("setallskills",NULL,0,command_setskillall) || + command_add("race","[racenum] - Change your or your target's race. Use racenum 0 to return to normal",50,command_race) || + command_add("gender","[0/1/2] - Change your or your target's gender to male/female/neuter",50,command_gender) || + command_add("makepet","[level] [class] [race] [texture] - Make a pet",50,command_makepet) || + command_add("level","[level] - Set your or your target's level",10,command_level) || + 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",NULL,0,command_npctypespawn) || + command_add("heal","- Completely heal your target",10,command_heal) || + command_add("appearance","[type] [value] - Send an appearance packet for you or your target",150,command_appearance) || + command_add("charbackup","[list/restore] - Query or restore character backups",150,command_charbackup) || + command_add("nukeitem","[itemid] - Remove itemid from your player target's inventory",150,command_nukeitem) || + command_add("peekinv","[worn/cursor/inv/bank/trade/trib/all] - Print out contents of your player target's inventory",100,command_peekinv) || + command_add("findnpctype","[search criteria] - Search database NPC types",100,command_findnpctype) || + command_add("findzone","[search criteria] - Search database zones",100,command_findzone) || + command_add("fz",NULL,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",NULL,0,command_reloadqst) || + command_add("reloadpl",NULL,0,command_reloadqst) || + command_add("reloadworld",NULL,0,command_reloadworld) || + command_add("reloadlevelmods",NULL,0,command_reloadlevelmods) || + command_add("rq",NULL,0,command_reloadqst) || + command_add("reloadzonepoints","- Reload zone points from database",150,command_reloadzps) || + command_add("reloadzps",NULL,0,command_reloadzps) || + command_add("zoneshutdown","[shortname] - Shut down a zone server",150,command_zoneshutdown) || + command_add("zonebootup","[ZoneServerID] [shortname] - Make a zone server boot a specific zone",150,command_zonebootup) || + command_add("kick","[charname] - Disconnect charname",150,command_kick) || + command_add("attack","[targetname] - Make your NPC target attack targetname",150,command_attack) || + command_add("lock","- Lock the worldserver",150,command_lock) || + command_add("unlock","- Unlock the worldserver",150,command_unlock) || + command_add("motd","[new motd] - Set message of the day",150,command_motd) || + command_add("listpetition","- List petitions",50,command_listpetition) || + command_add("equipitem","[slotid(0-21)] - Equip the item on your cursor into the specified slot",50,command_equipitem) || + command_add("zonelock","[list/lock/unlock] - Set/query lock flag for zoneservers",100,command_zonelock) || + command_add("corpse","- Manipulate corpses, use with no arguments for help",50,command_corpse) || + command_add("fixmob","[race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev] - Manipulate appearance of your target",80,command_fixmob) || + command_add("gmspeed","[on/off] - Turn GM speed hack on/off for you or your player target",100,command_gmspeed) || + command_add("title","[text] [1 = create title table row] - Set your or your player target's title",50,command_title) || + command_add("titlesuffix","[text] [1 = create title table row] - Set your or your player target's title suffix",50,command_titlesuffix) || + command_add("spellinfo","[spellid] - Get detailed info about a spell",10,command_spellinfo) || + command_add("lastname","[new lastname] - Set your or your player target's lastname",50,command_lastname) || + command_add("memspell","[slotid] [spellid] - Memorize spellid in the specified slot",50,command_memspell) || + command_add("save","- Force your player or player corpse target to be saved to the database",50,command_save) || + command_add("showstats","- Show details about you or your target",50,command_showstats) || + command_add("mystats","- Show details about you or your pet",50,command_mystats) || + command_add("myskills","- Show details about your current skill levels",0,command_myskills) || + command_add("depop","- Depop your NPC target",50,command_depop) || + command_add("depopzone","- Depop the zone",100,command_depopzone) || + command_add("repop","[delay] - Repop the zone with optional delay",100,command_repop) || + command_add("spawnstatus","- Show respawn timer status",100,command_spawnstatus) || + command_add("nukebuffs","- Strip all buffs on you or your target",50,command_nukebuffs) || + command_add("freeze","- Freeze your target",80,command_freeze) || + command_add("unfreeze","- Unfreeze your target",80,command_unfreeze) || + command_add("pvp","[on/off] - Set your or your player target's PVP status",100,command_pvp) || + command_add("setxp","[value] - Set your or your player target's experience",100,command_setxp) || + command_add("setpvppoints","[value] - Set your or your player target's PVP points",100,command_setpvppoints) || + command_add("setexp",NULL,0,command_setxp) || + command_add("setaaxp","[value] - Set your or your player target's AA experience",100,command_setaaxp) || + command_add("setaaexp",NULL,0,command_setaaxp) || + command_add("setaapts","[value] - Set your or your player target's available AA points",100,command_setaapts) || + command_add("setaapoints",NULL,0,command_setaapts) || + command_add("setcrystals","[value] - Set your or your player target's available radiant or ebon crystals",100,command_setcrystals) || + command_add("name","[newname] - Rename your player target",150,command_name) || + command_add("tempname","[newname] - Temporarily renames your target. Leave name blank to restore the original name.",100,command_tempname) || + command_add("npcspecialattk","[flagchar] [perm] - Set NPC special attack flags. Flags are E(nrage) F(lurry) R(ampage) S(ummon).",80,command_npcspecialattk) || + command_add("npcspecialattack",NULL,0,command_npcspecialattk) || + command_add("npcspecialatk",NULL,0,command_npcspecialattk) || + command_add("kill","- Kill your target",100,command_kill) || + command_add("haste","[percentage] - Set your haste percentage",100,command_haste) || + command_add("damage","[amount] - Damage your target",100,command_damage) || + command_add("zonespawn","- Not implemented",250,command_zonespawn) || + command_add("npcspawn","[create/add/update/remove/delete] - Manipulate spawn DB",170,command_npcspawn) || + command_add("spawnfix","- Find targeted NPC in database based on its X/Y/heading and update the database to make it spawn at your current location/heading.",170,command_spawnfix) || + command_add("npcedit","[column] [value] - Mega NPC editing command",100,command_npcedit) || + command_add("qglobal","[on/off/view] - Toggles qglobal functionality on an NPC",100,command_qglobal) || + command_add("loc","- Print out your or your target's current location and heading",0,command_loc) || + command_add("goto","[x] [y] [z] - Teleport to the provided coordinates or to your target",10,command_goto) || +#ifdef EMBPERL_PLUGIN +#ifdef EMBPERL_EVAL_COMMANDS + command_add("plugin","(sub) [args] - execute a plugin",PERL_PRIVS,command_embperl_plugin) || + command_add("peval","(expression) - execute some perl",PERL_PRIVS,command_embperl_eval) || +#endif //EMBPERL_EVAL_COMMANDS +#endif //EMBPERL_PLUGIN + command_add("iteminfo","- Get information about the item on your cursor",10,command_iteminfo) || + command_add("uptime","[zone server id] - Get uptime of worldserver, or zone server if argument provided",10,command_uptime) || + command_add("flag","[status] [acctname] - Refresh your admin status, or set an account's admin status if arguments provided",0,command_flag) || + command_add("guild","- Guild manipulation commands. Use argument help for more info.",10,command_guild) || + command_add("guilds",NULL,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",NULL,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",NULL,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) || + command_add("haircolor","- Change the hair color of your target",80,command_haircolor) || + command_add("beard","- Change the beard of your target",80,command_beard) || + command_add("beardcolor","- Change the beard color of your target",80,command_beardcolor) || + command_add("heritage","- Change the heritage of your target (Drakkin Only)",80,command_heritage) || + command_add("tattoo","- Change the tattoo of your target (Drakkin Only)",80,command_tattoo) || + command_add("details","- Change the details of your target (Drakkin Only)",80,command_details) || + command_add("scribespells","[max level] [min level] - Scribe all spells for you or your player target that are usable by them, up to level specified. (may freeze client for a few seconds)",150,command_scribespells) || + command_add("unscribespells","- Clear out your or your player target's spell book.",180,command_unscribespells) || + 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",NULL,200,command_summonitem) || + command_add("giveitem","[itemid] [charges] - Summon an item onto your target's cursor. Charges are optional.",200,command_giveitem) || + command_add("gi",NULL,200,command_giveitem) || + command_add("givemoney","[pp] [gp] [sp] [cp] - Gives specified amount of money to the target player.",200,command_givemoney) || + command_add("itemsearch","[search criteria] - Search for an item",10,command_itemsearch) || + command_add("search",NULL,10,command_itemsearch) || + command_add("stun","[duration] - Stuns you or your target for duration",100,command_stun) || + command_add("finditem",NULL,10,command_itemsearch) || + command_add("fi",NULL,10,command_itemsearch) || +#ifdef PACKET_PROFILER + command_add("packetprofile","- Dump packet profile for target or self.",250,command_packetprofile) || +#endif +#ifdef EQPROFILE + command_add("profiledump","- Dump profiling info to logs",250,command_profiledump) || + command_add("profilereset","- Reset profiling info",250,command_profilereset) || +#endif + + 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("datarate","[rate] - Query/set datarate",100,command_datarate) || + 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("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) || + command_add("checklos","- Check for line of sight to your target",50,command_checklos) || + command_add("los",NULL,0,command_checklos) || + command_add("setadventurepoints","- Set your or your player target's available adventure points",150,command_set_adventure_points) || + command_add("npcsay","[message] - Make your NPC target say a message.",150,command_npcsay) || + 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("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) || + command_add("fear","- view and edit fear grids and hints",200,command_fear) || + command_add("path","- view and edit pathing",200,command_path) || + command_add("flags","- displays the flags of you or your target",0,command_flags) || + command_add("flagedit","- Edit zone flags on your target",100,command_flagedit) || + command_add("mlog","- Manage log settings",250,command_mlog) || + command_add("aggro","(range) [-v] - Display aggro information for all mobs 'range' distance from your target. -v is verbose faction info.",80,command_aggro) || + command_add("hatelist"," - Display hate list for target.", 80,command_hatelist) || + command_add("aggrozone","[aggro] - Aggro every mob in the zone with X aggro. Default is 0. Not recommend if you're not invulnerable.",100,command_aggrozone) || + command_add("npcemote","[message] - Make your NPC target emote a message.",150,command_npcemote) || + command_add("serverrules","- Read this server's rules",0,command_serverrules) || + command_add("acceptrules","[acceptrules] - Accept the EQEmu Agreement",0,command_acceptrules) || + command_add("rules","(subcommand) - Manage server rules", 250, command_rules) || + command_add("task","(subcommand) - Task system commands", 150, command_task) || + command_add("reloadtitles","- Reload player titles from the database", 150, command_reloadtitles) || + command_add("guildcreate","[guildname] - Creates an approval setup for guild name specified",0,command_guildcreate) || + 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) || +#endif + + command_add("traindisc","[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)",150,command_traindisc) || + command_add("setgraveyard","[zone name] - Creates a graveyard for the specified zone based on your target's LOC.", 200, command_setgraveyard) || + command_add("deletegraveyard","[zone name] - Deletes the graveyard for the specified zone.", 200, command_deletegraveyard) || + command_add("getplayerburriedcorpsecount","- Get the target's total number of burried player corpses.", 100, command_getplayerburriedcorpsecount) || + command_add("summonburriedplayercorpse","- Summons the target's oldest burried corpse, if any exist.", 100, command_summonburriedplayercorpse) || + command_add("refreshgroup","- Refreshes Group.", 0, command_refreshgroup) || + command_add("advnpcspawn","[maketype|makegroup|addgroupentry|addgroupspawn][removegroupspawn|movespawn|editgroupbox|cleargroupbox]",150,command_advnpcspawn) || + command_add("advnpc","analog for advnpcspawn [maketype|makegroup|addgroupentry|addgroupspawn][removegroupspawn|movespawn|editgroupbox|cleargroupbox]",150,command_advnpcspawn) || + command_add("modifynpcstat","- Modifys a NPC's stats",150,command_modifynpcstat) || + command_add("undyeme","- Remove dye from all of your armor slots",0,command_undyeme) || + command_add("instance","- Modify Instances",200,command_instance) || + command_add("setstartzone","[zoneid] - Set target's starting zone. Set to zero to allow the player to use /setstartcity",80,command_setstartzone) || + command_add("netstats","- Gets the network stats for a stream.",200,command_netstats) || + command_add("object","List|Add|Edit|Move|Rotate|Copy|Save|Undo|Delete - Manipulate static and tradeskill objects within the zone",100,command_object) || + command_add("raidloot","LEADER|GROUPLEADER|SELECTED|ALL - Sets your raid loot settings if you have permission to do so.",0,command_raidloot) || + command_add("globalview","Lists all qglobals in cache if you were to do a quest with this target.",80,command_globalview) || + command_add("emoteview","Lists all NPC Emotes",80,command_emoteview) || + command_add("reloademote","Reloads NPC Emotes",80,command_reloademote) || + command_add("emotesearch","Searches NPC Emotes",80,command_emotesearch) || + command_add("distance","- Reports the distance between you and your target.", 80, command_distance) || + command_add("cvs","- Summary of client versions currently online.", 200, command_cvs) || + command_add("maxskills","Maxes skills for you.", 200, command_max_all_skills) || + command_add("showbonusstats","[item|spell|all] Shows bonus stats for target from items or spells. Shows both by default.",50, command_showbonusstats) || + command_add("reloadallrules","Executes a reload of all rules.",80, command_reloadallrules) || + command_add("reloadrulesworld","Executes a reload of all rules in world specifically.",80, command_reloadworldrules) || + command_add("camerashake", "Shakes the camera on everyone's screen globally.", 80, command_camerashake) || + command_add("disarmtrap", "Analog for ldon disarm trap for the newer clients since we still don't have it working.", 0, command_disarmtrap) || + command_add("sensetrap", "Analog for ldon sense trap for the newer clients since we still don't have it working.", 0, command_sensetrap) || + command_add("picklock", "Analog for ldon pick lock for the newer clients since we still don't have it working.", 0, command_picklock) || + command_add("mysql", "Mysql CLI, see 'help' for options.", 250, command_mysql) || + command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", 250, command_xtargets) || + command_add("printquestitems","Returns available quest items for multiquesting currently on the target npc.",200,command_printquestitems) || + command_add("clearquestitems","Clears quest items for multiquesting currently on the target npc.",200,command_clearquestitems) || + command_add("zopp", "Troubleshooting command - Sends a fake item packet to you. No server reference is created.", 250, command_zopp) || + command_add("augmentitem", "Force augments an item. Must have the augment item window open.", 250, command_augmentitem) + ) + { + command_deinit(); + return -1; + } + + map::iterator cur,end; + cur = commandlist.begin(); + end = commandlist.end(); + map command_settings; + map::iterator itr; + database.GetCommandSettings(command_settings); + for(; cur != end; cur++) { + if ((itr=command_settings.find(cur->first))!=command_settings.end()) + { + cur->second->access = itr->second; +#if EQDEBUG >=5 + LogFile->write(EQEMuLog::Debug, "command_init(): - Command '%s' set to access level %d." , cur->first.c_str(), itr->second); +#endif + } + else + { +#ifdef COMMANDS_WARNINGS + if(cur->second->access == 0) + LogFile->write(EQEMuLog::Status, "command_init(): Warning: Command '%s' defaulting to access level 0!" , cur->first.c_str()); +#endif + } + } + + command_dispatch = command_realdispatch; + + return commandcount; +} + +/* + * command_deinit + * clears the command list, freeing resources + * + * Parameters: + * none + * + */ +void command_deinit(void) +{ +/* LinkedListIterator cur(cleanup_commandlist); + while(cur.MoreElements()) { + CommandRecord *tmp = cur.GetData(); + safe_delete(tmp); + cur.Advance(); + } +*/ commandlist.clear(); + + command_dispatch = command_notavail; + commandcount = 0; +} + +/* + * command_add + * adds a command to the command list; used by command_init + * + * Parameters: + * command_string - the command ex: "spawn" + * desc - text description of command for #help + * access - default access level required to use command + * function - pointer to function that handles command + * + */ +int command_add(const char *command_string, const char *desc, int access, CmdFuncPtr function) +{ + if(function == NULL) + return(-1); + + string cstr(command_string); + + if(commandlist.count(cstr) != 0) { + LogFile->write(EQEMuLog::Error, "command_add() - Command '%s' is a duplicate - check command.cpp." , command_string); + return(-1); + } + + //look for aliases... + map::iterator cur,end,del; + cur = commandlist.begin(); + end = commandlist.end(); + for(; cur != end; cur++) { + if(cur->second->function == function) { + int r; + for(r = 1; r < CMDALIASES; r++) { + if(cur->second->command[r] == NULL) { + cur->second->command[r] = command_string; + break; + } + } + commandlist[cstr] = cur->second; + return(0); + } + } + + CommandRecord *c = new CommandRecord; + cleanup_commandlist.Append(c); + c->desc = desc; + c->access = access; + c->function = function; + memset(c->command, 0, sizeof(c->command)); + c->command[0] = command_string; + + commandlist[cstr] = c; + + commandcount++; + return 0; +} + + +#ifdef EMBPERL_COMMANDS +/* + * command_add_perl + * adds a command to the command list, as a perl function + * + * Parameters: + * command_string - the command ex: "spawn" + * desc - text description of command for #help + * access - default access level required to use command + * + */ +int command_add_perl(const char *command_string, const char *desc, int access) { + string cstr(command_string); + + if(commandlist.count(cstr) != 0) { +#ifdef COMMANDS_PERL_OVERRIDE + //print a warning so people dont get too confused when this happens + LogFile->write(EQEMuLog::Status, "command_add_perl() - Perl Command '%s' is overriding the compiled command." , command_string); + CommandRecord *tmp = commandlist[cstr]; + safe_delete(tmp); +#else + LogFile->write(EQEMuLog::Error, "command_add_perl() - Command '%s' is a duplicate - check commands.pl." , command_string); + return(-1); +#endif + } + + CommandRecord *c = new CommandRecord; + c->desc = desc; + c->access = access; + c->function = NULL; + + commandlist[cstr] = c; + + commandcount++; + return 0; + +} + +//clear out any perl commands. +//should restore any overridden C++ commands, but thats a lot of work. +void command_clear_perl() { + map::iterator cur,end,del; + cur = commandlist.begin(); + end = commandlist.end(); + for(; cur != end;) { + del = cur; + cur++; + if(del->second->function == NULL) { + safe_delete(del->second); + commandlist.erase(del); + } + } +} + +#endif //EMBPERL_COMMANDS + + +/* + * + * command_realdispatch + * Calls the correct function to process the client's command string. + * Called from Client::ChannelMessageReceived if message starts with + * command character (#). + * + * Parameters: + * c - pointer to the calling client object + * message - what the client typed + * + */ +int command_realdispatch(Client *c, const char *message) +{ + _ZP(command_realdispatch); + + + Seperator sep(message, ' ', 10, 100, true); // "three word argument" should be considered 1 arg + + command_logcommand(c, message); + + string cstr(sep.arg[0]+1); + + if(commandlist.count(cstr) != 1) { + return(-2); + } + + CommandRecord *cur = commandlist[cstr]; + if(c->Admin() < cur->access){ + c->Message(13,"Your access level is not high enough to use this command."); + return(-1); + } + +#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"); + } +#endif + + if(cur->function == NULL) { +#ifdef EMBPERL_COMMANDS + //todo reimplement this stuff + //dispatch perl command + //PerlembParser *embparse = (PerlembParser *) parse; + //embparse->ExecCommand(c, &sep); +#else + LogFile->write(EQEMuLog::Error, "Command '%s' has a null function, but perl commands are diabled!\n", cstr.c_str()); + return(-1); +#endif + } else { + //dispatch C++ command + cur->function(c, &sep); // dispatch command + } + return 0; + +} + +void command_logcommand(Client *c, const char *message) +{ + int admin=c->Admin(); + + bool continueevents=false; + switch (zone->loglevelvar){ //catch failsafe + case 9: { // log only LeadGM + if ((admin>= 150) && (admin <200)) + continueevents=true; + break; + } + case 8: { // log only GM + if ((admin>= 100) && (admin <150)) + continueevents=true; + break; + } + case 1: { + if ((admin>= 200)) + continueevents=true; + break; + } + case 2: { + if ((admin>= 150)) + continueevents=true; + break; + } + case 3: { + if ((admin>= 100)) + continueevents=true; + break; + } + case 4: { + if ((admin>= 80)) + continueevents=true; + break; + } + case 5: { + if ((admin>= 20)) + continueevents=true; + break; + } + case 6: { + if ((admin>= 10)) + continueevents=true; + break; + } + case 7: { + continueevents=true; + break; + } + } + + if (continueevents) + database.logevents( + c->AccountName(), + c->AccountID(), + admin,c->GetName(), + c->GetTarget()?c->GetTarget()->GetName():"None", + "Command", + message, + 1 + ); +} + + +/* + * commands go below here + */ +void command_setstat(Client* c, const Seperator* sep){ + if(sep->arg[1][0] && sep->arg[2][0] && c->GetTarget()!=0 && c->GetTarget()->IsClient()){ + c->GetTarget()->CastToClient()->SetStats(atoi(sep->arg[1]),atoi(sep->arg[2])); + } + else{ + c->Message(0,"This command is used to permanently increase or decrease a players stats."); + c->Message(0,"Usage: #setstat {type} {value the stat should be}"); + c->Message(0,"Types: Str: 0, Sta: 1, Agi: 2, Dex: 3, Int: 4, Wis: 5, Cha: 6"); + } +} + +void command_incstat(Client* c, const Seperator* sep){ + if(sep->arg[1][0] && sep->arg[2][0] && c->GetTarget()!=0 && c->GetTarget()->IsClient()){ + c->GetTarget()->CastToClient()->IncStats(atoi(sep->arg[1]),atoi(sep->arg[2])); + } + else{ + c->Message(0,"This command is used to permanently increase or decrease a players stats."); + c->Message(0,"Usage: #setstat {type} {value by which to increase or decrease}"); + c->Message(0,"Note: The value is in increments of 2, so a value of 3 will actually increase the stat by 6"); + c->Message(0,"Types: Str: 0, Sta: 1, Agi: 2, Dex: 3, Int: 4, Wis: 5, Cha: 6"); + } +} + +void command_resetaa(Client* c,const Seperator *sep){ + if(c->GetTarget()!=0 && c->GetTarget()->IsClient()){ + c->GetTarget()->CastToClient()->ResetAA(); + c->Message(13,"Successfully reset %s's AAs",c->GetTarget()->GetName()); + } + else + 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; + + /*if(sep->arg[1][0] && sep->arg[2][0]) + { + c->Message_StringID(atoi(sep->arg[1]),atoi(sep->arg[2]),sep->arg[3],sep->arg[4],sep->arg[5],sep->arg[6],sep->arg[7],sep->arg[8]); + } + else + c->Message(0,"type,string id, message1...");*/ + /* + clientupdate lvl and such + uint32 level; //new level + + + */ + + + /* + if(sep->arg[1][0] && sep->arg[2][0]){ + EQApplicationPacket* outapp = new EQApplicationPacket((EmuOpcode)atoi(sep->arg[1]),sizeof(GMName_Struct)); + GMName_Struct* gms=(GMName_Struct*)outapp->pBuffer; + memset(outapp->pBuffer,0,outapp->size); + strcpy(gms->gmname,c->GetName()); + strcpy(gms->oldname,c->GetName()); + strcpy(gms->newname,sep->arg[3]); + if(sep->arg[4][0]) + gms->badname=atoi(sep->arg[4]); + c->QueuePacket(outapp); + safe_delete(outapp); + } + */ + /* + else{ + EQApplicationPacket* outapp = new EQApplicationPacket(121,atoi(sep->arg[2])); + memset(outapp->pBuffer,0,outapp->size); + uint8 offset=atoi(sep->arg[3]); + if(offsetsize && sep->arg[4][0]) + outapp->pBuffer[offset]=atoi(sep->arg[4]); + offset++; + if(offsetsize && sep->arg[5][0]) + outapp->pBuffer[offset+3]=atoi(sep->arg[5]); + offset++; + if(offsetsize && sep->arg[6][0]) + outapp->pBuffer[offset+3]=atoi(sep->arg[6]); + offset++; + if(offsetsize && sep->arg[7][0]) + outapp->pBuffer[offset]=atoi(sep->arg[7]); + offset++; + if(offsetsize && sep->arg[8][0]) + outapp->pBuffer[offset]=atoi(sep->arg[8]); + offset++; + if(offsetsize && sep->arg[9][0]) + outapp->pBuffer[offset]=atoi(sep->arg[9]); + c->QueuePacket(outapp); + safe_delete(outapp); + }*/ + //c->SetStats(atoi(sep->arg[1]),atoi(sep->arg[2])); + //} + /*EQApplicationPacket* outapp = new EQApplicationPacket(atoi(sep->arg[1]), sizeof(PlayerAA_Struct)); + memcpy(outapp->pBuffer,c->GetAAStruct(),outapp->size); + c->QueuePacket(outapp); + safe_delete(outapp); + } + else + c->Message(15,"Invalid opcode!"); + */ + +} + +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; + } + default: + { + break; + } + } + } +} + +void command_help(Client *c, const Seperator *sep) +{ + int commands_shown=0; + + c->Message(0, "Available EQEMu commands:"); + + map::iterator cur,end; + cur = commandlist.begin(); + end = commandlist.end(); + + for(; cur != end; cur++) { + if(sep->arg[1][0]) { + if(cur->first.find(sep->arg[1]) == string::npos) { + continue; + } + } + + if(c->Admin() < cur->second->access) + continue; + commands_shown++; + c->Message(0, " %c%s %s", COMMAND_CHAR, cur->first.c_str(), cur->second->desc == NULL?"":cur->second->desc); + } + c->Message(0, "%d command%s listed.", commands_shown, commands_shown!=1?"s":""); + +} + +void command_version(Client *c, const Seperator *sep) +{ + c->Message(0, "Current version information."); + c->Message(0, " %s", CURRENT_ZONE_VERSION); + c->Message(0, " Compiled on: %s at %s", COMPILE_DATE, COMPILE_TIME); + c->Message(0, " Last modified on: %s", LAST_MODIFIED); +} + +void command_eitem(Client *c, const Seperator *sep) +{ +#ifdef SHAREMEM + c->Message(0, "Error: Function doesnt work in ShareMem mode"); +#else + char hehe[255]; + if(strstr(sep->arg[2],"classes")) + snprintf(hehe,255,"%s %s",sep->arg[3],strstr(sep->argplus[0],sep->arg[3])); + else + strcpy(hehe,sep->arg[3]); + database.SetItemAtt(sep->arg[2],hehe,atoi(sep->arg[1])); +#endif +} + +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()))) + 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); + } +} + +void command_serversidename(Client *c, const Seperator *sep) +{ + if(c->GetTarget()) + c->Message(0, c->GetTarget()->GetName()); + else + 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) + { + c->Message(0, "Usage: #wc [wear slot] [material] [ [hero_forge_model] [elite_material] [unknown06] [unknown18] ]"); + } + else if(c->GetTarget() == NULL) { + c->Message(13, "You must have a target to do a wear change."); + } + else + { + uint32 hero_forge_model = 0; + uint32 wearslot = atoi(sep->arg[1]); + + if (sep->argnum > 2) + { + hero_forge_model = atoi(sep->arg[3]); + if (hero_forge_model > 0) + { + // Conversion to simplify the command arguments + // Hero's Forge model is actually model * 1000 + texture * 100 + wearslot + hero_forge_model *= 1000; + hero_forge_model += (atoi(sep->arg[2]) * 100); + hero_forge_model += wearslot; + + // For Hero's Forge, slot 7 is actually for Robes, but it still needs to use slot 1 in the packet + if (wearslot == 7) + { + wearslot = 1; + } + } + + } + /* + // Leaving here to add color option to the #wc command eventually + uint32 Color; + if (c->GetTarget()->IsClient()) + Color = c->GetTarget()->GetEquipmentColor(atoi(sep->arg[1])); + else + Color = c->GetTarget()->GetArmorTint(atoi(sep->arg[1])); + */ + c->GetTarget()->SendTextureWC(wearslot, atoi(sep->arg[2]), hero_forge_model, atoi(sep->arg[4]), atoi(sep->arg[5]), atoi(sep->arg[6])); + } +} + +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)) { + int num = atoi(sep->arg[1]); + if(num < 0 || num >= _eaMaxAppearance) { + c->Message(0, "Invalid animation number, between 0 and %d", _eaMaxAppearance-1); + } + c->GetTarget()->SetAppearance(EmuAppearance(num)); + } else + c->Message(0, "Usage: #setanim [animnum]"); +} + +void command_connectworldserver(Client *c, const Seperator *sep) +{ + if(worldserver.Connected()) + c->Message(0, "Error: Already connected to world server"); + else + { + c->Message(0, "Attempting to connect to world server..."); + worldserver.AsyncConnect(); + } +} + +void command_serverinfo(Client *c, const Seperator *sep) +{ +#ifdef _WINDOWS + char intbuffer [sizeof(unsigned long)]; + c->Message(0, "Operating system information."); + c->Message(0, " %s", Ver_name); + c->Message(0, " Build number: %s", ultoa(Ver_build, intbuffer, 10)); + c->Message(0, " Minor version: %s", ultoa(Ver_min, intbuffer, 10)); + c->Message(0, " Major version: %s", ultoa(Ver_maj, intbuffer, 10)); + c->Message(0, " Platform Id: %s", ultoa(Ver_pid, intbuffer, 10)); +#else +char buffer[255]; + c->Message(0, "Operating system information: %s",GetOS(buffer)); +#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]; + if (database.GetVariable(sep->argplus[1], tmp, sizeof(tmp))) + c->Message(0, "%s = %s", sep->argplus[1], tmp); + else + c->Message(0, "GetVariable(%s) returned false", sep->argplus[1]); +} + +void command_chat(Client *c, const Seperator *sep) +{ + if (sep->arg[2][0] == 0) + c->Message(0, "Usage: #chat [channum] [message]"); + else + if (!worldserver.SendChannelMessage(0, 0, (uint8) atoi(sep->arg[1]), 0, 0, sep->argplus[2])) + 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 (!spells_loaded) + 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 != NULL) && (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) + c->Message(0, "Error: No target"); + // #npcloot show + else if (strcasecmp(sep->arg[1], "show") == 0) + { + if (c->GetTarget()->IsNPC()) + c->GetTarget()->CastToNPC()->QueryLoot(c); + else if (c->GetTarget()->IsCorpse()) + c->GetTarget()->CastToCorpse()->QueryLoot(c); + else + c->Message(0, "Error: Target's type doesnt have loot"); + } + // These 2 types are *BAD* for the next few commands + else if (c->GetTarget()->IsClient() || c->GetTarget()->IsCorpse()) + c->Message(0, "Error: Invalid target type, try a NPC =)."); + // #npcloot add + else if (strcasecmp(sep->arg[1], "add") == 0) + { + // #npcloot add item + if (c->GetTarget()->IsNPC() && sep->IsNumber(2)) + { + uint32 item = atoi(sep->arg[2]); + if (database.GetItem(item)) + { + if (sep->arg[3][0] != 0 && sep->IsNumber(3)) + c->GetTarget()->CastToNPC()->AddItem(item, atoi(sep->arg[3]), 0); + else + c->GetTarget()->CastToNPC()->AddItem(item, 1, 0); + c->Message(0, "Added item(%i) to the %s's loot.", item, c->GetTarget()->GetName()); + } + else + c->Message(0, "Error: #npcloot add: Item(%i) does not exist!", item); + } + else if (!sep->IsNumber(2)) + c->Message(0, "Error: #npcloot add: Itemid must be a number."); + else + c->Message(0, "Error: #npcloot add: This is not a valid target."); + } + // #npcloot remove + else if (strcasecmp(sep->arg[1], "remove") == 0) + { + //#npcloot remove all + if (strcasecmp(sep->arg[2], "all") == 0) + c->Message(0, "Error: #npcloot remove all: Not yet implemented."); + //#npcloot remove itemid + else + { + if(c->GetTarget()->IsNPC() && sep->IsNumber(2)) + { + uint32 item = atoi(sep->arg[2]); + c->GetTarget()->CastToNPC()->RemoveItem(item); + c->Message(0, "Removed item(%i) from the %s's loot.", item, c->GetTarget()->GetName()); + } + else if (!sep->IsNumber(2)) + c->Message(0, "Error: #npcloot remove: Item must be a number."); + else + c->Message(0, "Error: #npcloot remove: This is not a valid target."); + } + } + // #npcloot money + else if (strcasecmp(sep->arg[1], "money") == 0) + { + if (c->GetTarget()->IsNPC() && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5)) + { + if ((atoi(sep->arg[2]) < 34465 && atoi(sep->arg[2]) >= 0) && (atoi(sep->arg[3]) < 34465 && atoi(sep->arg[3]) >= 0) && (atoi(sep->arg[4]) < 34465 && atoi(sep->arg[4]) >= 0) && (atoi(sep->arg[5]) < 34465 && atoi(sep->arg[5]) >= 0)) + { + c->GetTarget()->CastToNPC()->AddCash(atoi(sep->arg[5]), atoi(sep->arg[4]), atoi(sep->arg[3]), atoi(sep->arg[2])); + c->Message(0, "Set %i Platinum, %i Gold, %i Silver, and %i Copper as %s's money.", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]), c->GetTarget()->GetName()); + } + else + c->Message(0, "Error: #npcloot money: Values must be between 0-34465."); + } + else + c->Message(0, "Usage: #npcloot money platinum gold silver copper"); + } + else + c->Message(0, "Usage: #npcloot [show/money/add/remove] [itemid/all/money: pp gp sp cp]"); +} + +void command_log(Client *c, const Seperator *sep) +{ + if(strlen(sep->arg[4]) == 0 || strlen(sep->arg[1]) == 0 || strlen(sep->arg[2]) == 0 || (strlen(sep->arg[3]) == 0 && atoi(sep->arg[3]) == 0)) + { + c->Message(0,"#log
"); + c->Message(0,"(Req.) Types: 1) Command, 2) Merchant Buying, 3) Merchant Selling, 4) Loot, 5) Money Loot 6) Trade"); + c->Message(0,"(Req.) byaccountid/bycharname: choose either byaccountid or bycharname and then set querytype to effect it"); + c->Message(0,"(Req.) Details are information about the event, for example, partially an items name, or item id."); + c->Message(0,"Timestamp allows you to set a date to when the event occured: YYYYMMDDHHMMSS (Year,Month,Day,Hour,Minute,Second). It can be a partial timestamp."); + c->Message(0,"Note: when specifying a target, spaces in EQEMu use '_'"); + return; + // help + } + CharacterEventLog_Struct* cel = new CharacterEventLog_Struct; + memset(cel,0,sizeof(CharacterEventLog_Struct)); + if(strcasecmp(sep->arg[2], "byaccountid") == 0) + database.GetEventLogs("",sep->arg[5],atoi(sep->arg[3]),atoi(sep->arg[1]),sep->arg[4],sep->arg[6],cel); + else if(strcasecmp(sep->arg[2], "bycharname") == 0) + database.GetEventLogs(sep->arg[3],sep->arg[5],0,atoi(sep->arg[1]),sep->arg[4],sep->arg[6],cel); + else + { + c->Message(0,"Incorrect query type, use either byaccountid or bycharname"); + safe_delete(cel); + return; + } + if(cel->count != 0) + { + uint32 count = 0; + bool cont = true; + while(cont) + { + if(count >= cel->count) + cont = false; + else if(cel->eld[count].id != 0) + { + c->Message(0,"ID: %i AccountName: %s AccountID: %i Status: %i CharacterName: %s TargetName: %s",cel->eld[count].id,cel->eld[count].accountname,cel->eld[count].account_id,cel->eld[count].status,cel->eld[count].charactername,cel->eld[count].targetname); + + c->Message(0,"LogType: %s Timestamp: %s LogDetails: %s",cel->eld[count].descriptiontype,cel->eld[count].timestamp,cel->eld[count].details); + } + else + cont = false; + count++; + if(count > 20) + { + c->Message(0,"Please refine search."); + cont = false; + } + } + } + c->Message(0,"End of Query"); + safe_delete(cel); +} + +void command_gm(Client *c, const Seperator *sep) +{ + bool state=atobool(sep->arg[1]); + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + + if(sep->arg[1][0] != 0) { + t->SetGM(state); + c->Message(0, "%s is %s a GM.", t->GetName(), state?"now":"no longer"); + } + else + c->Message(0, "Usage: #gm [on/off]"); +} + +// there's no need for this, as /summon already takes care of it +// this command is here for reference but it is not added to the +// list above + +//To whoever wrote the above: And what about /kill, /zone, /zoneserver, etc? +//There is a reason for the # commands: so that admins can specifically enable certain +//commands for their users. Some might want users to #summon but not to /kill. Cant do that if they are a GM +void command_summon(Client *c, const Seperator *sep) +{ + Mob *t; + + if(sep->arg[1][0] != 0) // arg specified + { + Client* client = entity_list.GetClientByName(sep->arg[1]); + if (client != 0) // found player in zone + t=client->CastToMob(); + else + { + if (!worldserver.Connected()) + c->Message(0, "Error: World server disconnected."); + else + { // player is in another zone + //Taking this command out until we test the factor of 8 in ServerOP_ZonePlayer + //c->Message(0, "Summoning player from another zone not yet implemented."); + //return; + + ServerPacket* pack = new ServerPacket(ServerOP_ZonePlayer, sizeof(ServerZonePlayer_Struct)); + ServerZonePlayer_Struct* szp = (ServerZonePlayer_Struct*) pack->pBuffer; + strcpy(szp->adminname, c->GetName()); + szp->adminrank = c->Admin(); + szp->ignorerestrictions = 2; + strcpy(szp->name, sep->arg[1]); + strcpy(szp->zone, zone->GetShortName()); + szp->x_pos = c->GetX(); // May need to add a factor of 8 in here.. + szp->y_pos = c->GetY(); + szp->z_pos = c->GetZ(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + return; + } + } + else if(c->GetTarget()) // have target + t=c->GetTarget(); + else + { + /*if(c->Admin() < 150) + c->Message(0, "You need a NPC/corpse target for this command"); + else*/ + c->Message(0, "Usage: #summon [charname] Either target or charname is required"); + return; + } + + if(!t) + return; + + if (t->IsNPC()) + { // npc target + c->Message(0, "Summoning NPC %s to %1.1f, %1.1f, %1.1f", t->GetName(), c->GetX(), c->GetY(), c->GetZ()); + t->CastToNPC()->GMMove(c->GetX(), c->GetY(), c->GetZ(), c->GetHeading()); + t->CastToNPC()->SaveGuardSpot(true); + } + else if (t->IsCorpse()) + { // corpse target + c->Message(0, "Summoning corpse %s to %1.1f, %1.1f, %1.1f", t->GetName(), c->GetX(), c->GetY(), c->GetZ()); + t->CastToCorpse()->GMMove(c->GetX(), c->GetY(), c->GetZ(), c->GetHeading()); + } + else if (t->IsClient()) + { + /*if(c->Admin() < 150) + { + c->Message(0, "You may not summon a player."); + return; + }*/ + c->Message(0, "Summoning player %s to %1.1f, %1.1f, %1.1f", t->GetName(), c->GetX(), c->GetY(), c->GetZ()); + t->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(), 2, GMSummon); + } +} + +void command_zone(Client *c, const Seperator *sep) +{ + if(c->Admin() < commandZoneToCoords && + (sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4))) { + c->Message(0, "Your status is not high enough to zone to specific coordinates."); + return; + } + + uint16 zoneid = 0; + + if (sep->IsNumber(1)) + { + if(atoi(sep->arg[1])==26 && (c->Admin() < commandZoneToSpecials)){ //cshome + c->Message(0, "Only Guides and above can goto that zone."); + return; + } + zoneid = atoi(sep->arg[1]); + } + else if (sep->arg[1][0] == 0) + { + c->Message(0, "Usage: #zone [zonename]"); + c->Message(0, "Optional Usage: #zone [zonename] y x z"); + return; + } + else if (zone->GetZoneID() == 184 && c->Admin() < commandZoneToSpecials) { // Zone: 'Load' + c->Message(0, "The Gods brought you here, only they can send you away."); + return; + } else { + if((strcasecmp(sep->arg[1], "cshome")==0) && (c->Admin() < commandZoneToSpecials)){ + c->Message(0, "Only Guides and above can goto that zone."); + return; + } + + zoneid = database.GetZoneID(sep->arg[1]); + if(zoneid == 0) { + c->Message(0, "Unable to locate zone '%s'", sep->arg[1]); + return; + } + } + +#ifdef BOTS + // This block is necessary to clean up any bot objects owned by a Client + if(zoneid != c->GetZoneID()) + Bot::ProcessClientZoneChange(c); +#endif + + if (sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4)){ + //zone to specific coords + c->MovePC(zoneid, (float)atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), 0.0f, 0); + } + else + //zone to safe coords + c->MovePC(zoneid, 0.0f, 0.0f, 0.0f, 0.0f, 0, ZoneToSafeCoords); +} + +//todo: fix this so it checks if you're in the instance set +void command_zone_instance(Client *c, const Seperator *sep) +{ + if(c->Admin() < commandZoneToCoords && + (sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4))) { + c->Message(0, "Your status is not high enough to zone to specific coordinates."); + return; + } + + if (sep->arg[1][0] == 0) + { + c->Message(0, "Usage: #zoneinstance [instance id]"); + c->Message(0, "Optional Usage: #zoneinstance [instance id] y x z"); + return; + } + + uint16 zoneid = 0; + uint16 instanceid = 0; + + if(sep->IsNumber(1)) + { + instanceid = atoi(sep->arg[1]); + if(!instanceid) + { + c->Message(0, "Must enter a valid instance id."); + return; + } + + zoneid = database.ZoneIDFromInstanceID(instanceid); + if(!zoneid) + { + c->Message(0, "Instance not found or zone is set to null."); + return; + } + } + else + { + c->Message(0, "Must enter a valid instance id."); + return; + } + + if(!database.VerifyInstanceAlive(instanceid, c->CharacterID())) + { + c->Message(0, "Instance ID expiried or you are not apart of this instance."); + return; + } + + if (sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4)){ + //zone to specific coords + c->MovePC(zoneid, instanceid, atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), 0.0f, 0); + } + else{ + c->MovePC(zoneid, instanceid, 0.0f, 0.0f, 0.0f, 0.0f, 0, ZoneToSafeCoords); + } +} + +void command_showbuffs(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) + c->CastToMob()->ShowBuffs(c); + else + c->GetTarget()->CastToMob()->ShowBuffs(c); +} + +void command_peqzone(Client *c, const Seperator *sep) +{ + uint32 timeleft = c->GetPTimers().GetRemainingTime(pTimerPeqzoneReuse)/60; + + if(!c->GetPTimers().Expired(&database, pTimerPeqzoneReuse, false)) { + c->Message(13,"You must wait %i minute(s) before using this ability again.", timeleft); + return; + } + if(c->GetHPRatio() < 75) { + c->Message(0, "You cannot use this command with less than 75 percent health."); + return; + } + //this isnt perfect, but its better... + if( + c->IsInvisible(c) + || c->IsRooted() + || c->IsStunned() + || c->IsMezzed() + || c->AutoAttackEnabled() + || c->GetInvul() + ) { + c->Message(0, "You cannot use this command in your current state. Settle down and wait."); + return; + } + uint16 zoneid = 0; + uint8 destzone = 0; + if (sep->IsNumber(1)) + { + zoneid = atoi(sep->arg[1]); + destzone = database.GetPEQZone(zoneid, 0); + if(destzone == 0){ + c->Message(13, "You cannot use this command to enter that zone!"); + return; + } + if(zoneid == zone->GetZoneID()) { + c->Message(13, "You cannot use this command on the zone you are in!"); + return; + } + } + else if (sep->arg[1][0] == 0 || sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4) || sep->IsNumber(5)) + { + c->Message(0, "Usage: #peqzone [zonename]"); + c->Message(0, "Optional Usage: #peqzone [zoneid]"); + return; + } else { + zoneid = database.GetZoneID(sep->arg[1]); + destzone = database.GetPEQZone(zoneid, 0); + if(zoneid == 0) { + c->Message(0, "Unable to locate zone '%s'", sep->arg[1]); + return; + } + if(destzone == 0){ + c->Message(13, "You cannot use this command to enter that zone!"); + return; + } + if(zoneid == zone->GetZoneID()) { + c->Message(13, "You cannot use this command on the zone you are in!"); + return; + } + } + if(RuleB (Zone, UsePEQZoneDebuffs)){ + c->SpellOnTarget(RuleI(Zone, PEQZoneDebuff1), c); + c->SpellOnTarget(RuleI(Zone, PEQZoneDebuff2), c); + } + + //zone to safe coords + c->GetPTimers().Start(pTimerPeqzoneReuse, RuleI(Zone, PEQZoneReuseTime)); + c->MovePC(zoneid, 0.0f, 0.0f, 0.0f, 0.0f, 0, ZoneToSafeCoords); +} + +void command_movechar(Client *c, const Seperator *sep) +{ + if(sep->arg[1][0]==0 || sep->arg[2][0] == 0) + c->Message(0, "Usage: #movechar [charactername] [zonename]"); + else if (c->Admin() < commandMovecharToSpecials && strcasecmp(sep->arg[2], "cshome") == 0 || strcasecmp(sep->arg[2], "load") == 0 || strcasecmp(sep->arg[2], "load2") == 0) + c->Message(0, "Invalid zone name"); + else + { + uint32 tmp = database.GetAccountIDByChar(sep->arg[1]); + if (tmp) + { + if (c->Admin() >= commandMovecharSelfOnly || tmp == c->AccountID()) + if (!database.MoveCharacterToZone((char*) sep->arg[1], (char*) sep->arg[2])) + c->Message(0, "Character Move Failed!"); + else + c->Message(0, "Character has been moved."); + else + c->Message(13,"You cannot move characters that are not on your account."); + } + else + c->Message(0, "Character Does Not Exist"); + } +} + +void command_viewpetition(Client *c, const Seperator *sep) +{ + 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); + } +} + +void command_petitioninfo(Client *c, const Seperator *sep) +{ + 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; + + 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) + 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); + } +} + +void command_listnpcs(Client *c, const Seperator *sep) +{ + if (strcasecmp(sep->arg[1], "all") == 0) + entity_list.ListNPCs(c,sep->arg[1],sep->arg[2],0); + else if(sep->IsNumber(1) && sep->IsNumber(2)) + entity_list.ListNPCs(c,sep->arg[1],sep->arg[2],2); + else if(sep->arg[1][0] != 0) + entity_list.ListNPCs(c,sep->arg[1],sep->arg[2],1); + else { + c->Message(0, "Usage of #listnpcs:"); + c->Message(0, "#listnpcs [#] [#] (Each number would search by ID, ex. #listnpcs 1 30, searches 1-30)"); + c->Message(0, "#listnpcs [name] (Would search for a npc with [name])"); + } +} + +void command_date(Client *c, const Seperator *sep) +{ + //yyyy mm dd hh mm local + if(sep->arg[3][0]==0 || !sep->IsNumber(1) || !sep->IsNumber(2) || !sep->IsNumber(3)) { + c->Message(13, "Usage: #date yyyy mm dd [HH MM]"); + } + else { + int h=0, m=0; + TimeOfDay_Struct eqTime; + zone->zone_time.getEQTimeOfDay( time(0), &eqTime); + if(!sep->IsNumber(4)) + h=eqTime.hour; + else + h=atoi(sep->arg[4]); + if(!sep->IsNumber(5)) + m=eqTime.minute; + else + m=atoi(sep->arg[5]); + c->Message(13, "Setting world time to %s-%s-%s %i:%i...", sep->arg[1], sep->arg[2], sep->arg[3], h, m); + zone->SetDate(atoi(sep->arg[1]), atoi(sep->arg[2]), atoi(sep->arg[3]), h, m); + } +} + +void command_timezone(Client *c, const Seperator *sep) +{ + if(sep->arg[1][0]==0 && !sep->IsNumber(1)) { + c->Message(13, "Usage: #timezone HH [MM]"); + c->Message(13, "Current timezone is: %ih %im", zone->zone_time.getEQTimeZoneHr(), zone->zone_time.getEQTimeZoneMin()); + } + else { + if(sep->arg[2]=="") + strcpy(sep->arg[2], "0"); + c->Message(13, "Setting timezone to %s h %s m", sep->arg[1], sep->arg[2]); + uint32 ntz=(atoi(sep->arg[1])*60)+atoi(sep->arg[2]); + zone->zone_time.setEQTimeZone(ntz); + database.SetZoneTZ(zone->GetZoneID(), zone->GetInstanceVersion(), ntz); + + // Update all clients with new TZ. + 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_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]); + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + + if(sep->arg[1][0] != 0) { + t->SetInvul(state); + c->Message(0, "%s is %s invulnerable from attack.", t->GetName(), state?"now":"no longer"); + } + else + c->Message(0, "Usage: #invulnerable [on/off]"); +} + +void command_hideme(Client *c, const Seperator *sep) +{ + bool state=atobool(sep->arg[1]); + + if(sep->arg[1][0]==0) + c->Message(0, "Usage: #hideme [on/off]"); + else + { + c->SetHideMe(state); + c->Message_StringID(MT_Broadcasts, c->GetHideMe() ? NOW_INVISIBLE : NOW_VISIBLE, c->GetName()); + } +} + +void command_emote(Client *c, const Seperator *sep) +{ + if (sep->arg[3][0] == 0) + c->Message(0, "Usage: #emote [name | world | zone] type# message"); + else { + if (strcasecmp(sep->arg[1], "zone") == 0){ + char* newmessage=0; + if(strstr(sep->arg[3],"^")==0) + entity_list.Message(0, atoi(sep->arg[2]), sep->argplus[3]); + else{ + for(newmessage = strtok((char*)sep->arg[3],"^");newmessage!=NULL;newmessage=strtok(NULL, "^")) + entity_list.Message(0, atoi(sep->arg[2]), newmessage); + } + } + else if (!worldserver.Connected()) + c->Message(0, "Error: World server disconnected"); + else if (strcasecmp(sep->arg[1], "world") == 0) + worldserver.SendEmoteMessage(0, 0, atoi(sep->arg[2]), sep->argplus[3]); + else + worldserver.SendEmoteMessage(sep->arg[1], 0, atoi(sep->arg[2]), sep->argplus[3]); + } +} + +void command_fov(Client *c, const Seperator *sep) +{ + if(c->GetTarget()) + if(c->BehindMob(c->GetTarget(), c->GetX(), c->GetY())) + c->Message(0, "You are behind mob %s, it is looking to %d", c->GetTarget()->GetName(), c->GetTarget()->GetHeading()); + else + c->Message(0, "You are NOT behind mob %s, it is looking to %d", c->GetTarget()->GetName(), c->GetTarget()->GetHeading()); + else + 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) + c->Message(0, "ERROR: No target!"); + else if (!c->GetTarget()->IsNPC()) + c->Message(0, "ERROR: Target is not a NPC!"); + else { + c->Message(0, "NPC Stats:"); + c->Message(0, "Name: %s NpcID: %u", c->GetTarget()->GetName(), c->GetTarget()->GetNPCTypeID()); + c->Message(0, "Race: %i Level: %i Class: %i Material: %i", c->GetTarget()->GetRace(), c->GetTarget()->GetLevel(), c->GetTarget()->GetClass(), c->GetTarget()->GetTexture()); + c->Message(0, "Current HP: %i Max HP: %i", c->GetTarget()->GetHP(), c->GetTarget()->GetMaxHP()); + //c->Message(0, "Weapon Item Number: %s",c->GetTarget()->GetWeapNo()); + c->Message(0, "Gender: %i Size: %f Bodytype: %d", c->GetTarget()->GetGender(), c->GetTarget()->GetSize(), c->GetTarget()->GetBodyType()); + c->Message(0, "Runspeed: %f Walkspeed: %f", c->GetTarget()->GetRunspeed(), c->GetTarget()->GetWalkspeed()); + c->Message(0, "Spawn Group: %i Grid: %i", c->GetTarget()->CastToNPC()->GetSp2(), c->GetTarget()->CastToNPC()->GetGrid()); + c->Message(0, "EmoteID: %i", c->GetTarget()->CastToNPC()->GetNPCEmoteID()); + c->GetTarget()->CastToNPC()->QueryLoot(c); + } +} + +void command_zclip(Client *c, const Seperator *sep) +{ + // modifys and resends zhdr packet + if(sep->arg[2][0]==0) + c->Message(0, "Usage: #zclip "); + else if(atoi(sep->arg[1])<=0) + c->Message(0, "ERROR: Min clip can not be zero or less!"); + else if(atoi(sep->arg[2])<=0) + c->Message(0, "ERROR: Max clip can not be zero or less!"); + else if(atoi(sep->arg[1])>atoi(sep->arg[2])) + c->Message(0, "ERROR: Min clip is greater than max clip!"); + else { + zone->newzone_data.minclip = atof(sep->arg[1]); + zone->newzone_data.maxclip = atof(sep->arg[2]); + if(sep->arg[3][0]!=0) + zone->newzone_data.fog_minclip[0]=atof(sep->arg[3]); + if(sep->arg[4][0]!=0) + zone->newzone_data.fog_minclip[1]=atof(sep->arg[4]); + if(sep->arg[5][0]!=0) + zone->newzone_data.fog_maxclip[0]=atof(sep->arg[5]); + if(sep->arg[6][0]!=0) + zone->newzone_data.fog_maxclip[1]=atof(sep->arg[6]); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + +void command_npccast(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsNPC() && !sep->IsNumber(1) && sep->arg[1] != 0 && sep->IsNumber(2)) { + Mob* spelltar = entity_list.GetMob(sep->arg[1]); + if (spelltar) + c->GetTarget()->CastSpell(atoi(sep->arg[2]), spelltar->GetID()); + else + c->Message(0, "Error: %s not found", sep->arg[1]); + } + else if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->IsNumber(1) && sep->IsNumber(2) ) { + Mob* spelltar = entity_list.GetMob(atoi(sep->arg[1])); + if (spelltar) + c->GetTarget()->CastSpell(atoi(sep->arg[2]), spelltar->GetID()); + else + c->Message(0, "Error: target ID %i not found", atoi(sep->arg[1])); + } + else + c->Message(0, "Usage: (needs NPC targeted) #npccast targetname/entityid spellid"); +} + +void command_zstats(Client *c, const Seperator *sep) +{ + c->Message(0, "Zone Header Data:"); + c->Message(0, "Sky Type: %i", zone->newzone_data.sky); + c->Message(0, "Fog Colour: Red: %i; Blue: %i; Green %i", zone->newzone_data.fog_red[0], zone->newzone_data.fog_green[0], zone->newzone_data.fog_blue[0]); + c->Message(0, "Safe Coords: %f, %f, %f", zone->newzone_data.safe_x, zone->newzone_data.safe_y, zone->newzone_data.safe_z); + c->Message(0, "Underworld Coords: %f", zone->newzone_data.underworld); + c->Message(0, "Clip Plane: %f - %f", zone->newzone_data.minclip, zone->newzone_data.maxclip); +} + +void command_permaclass(Client *c, const Seperator *sep) +{ + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + + if(sep->arg[1][0]==0) { + c->Message(0,"Usage: #permaclass "); + } + else if(!t->IsClient()) + c->Message(0,"Target is not a client."); + else { + c->Message(0, "Setting %s's class...Sending to char select.", t->GetName()); + LogFile->write(EQEMuLog::Normal,"Class change request from %s for %s, requested class:%i", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); + t->SetBaseClass(atoi(sep->arg[1])); + t->Save(); + t->Kick(); + } +} + +void command_permarace(Client *c, const Seperator *sep) +{ + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + + if(sep->arg[1][0]==0) { + c->Message(0,"Usage: #permarace "); + c->Message(0,"NOTE: Not all models are global. If a model is not global, it will appear as a human on character select and in zones without the model."); + } + else if(!t->IsClient()) + c->Message(0,"Target is not a client."); + else { + c->Message(0, "Setting %s's race - zone to take effect",t->GetName()); + LogFile->write(EQEMuLog::Normal,"Permanant race change request from %s for %s, requested race:%i", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); + uint32 tmp = Mob::GetDefaultGender(atoi(sep->arg[1]), t->GetBaseGender()); + t->SetBaseRace(atoi(sep->arg[1])); + t->SetBaseGender(tmp); + t->Save(); + t->SendIllusionPacket(atoi(sep->arg[1])); + } +} + +void command_permagender(Client *c, const Seperator *sep) +{ + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + + if(sep->arg[1][0]==0) { + c->Message(0,"Usage: #permagender "); + c->Message(0,"Gender Numbers: 0=Male, 1=Female, 2=Neuter"); + } + else if(!t->IsClient()) + c->Message(0,"Target is not a client."); + else { + c->Message(0, "Setting %s's gender - zone to take effect",t->GetName()); + LogFile->write(EQEMuLog::Normal,"Permanant gender change request from %s for %s, requested gender:%i", c->GetName(), t->GetName(), atoi(sep->arg[1]) ); + t->SetBaseGender(atoi(sep->arg[1])); + t->Save(); + t->SendIllusionPacket(atoi(sep->arg[1])); + } +} + +void command_weather(Client *c, const Seperator *sep) +{ + if (!(sep->arg[1][0] == '0' || sep->arg[1][0] == '1' || sep->arg[1][0] == '2' || sep->arg[1][0] == '3')) { + c->Message(0, "Usage: #weather <0/1/2/3> - Off/Rain/Snow/Manual."); + } + else if(zone->zone_weather == 0) { + if(sep->arg[1][0] == '3') { // Put in modifications here because it had a very good chance at screwing up the client's weather system if rain was sent during snow -T7 + if(sep->arg[2][0] != 0 && sep->arg[3][0] != 0) { + c->Message(0, "Sending weather packet... TYPE=%s, INTENSITY=%s", sep->arg[2], sep->arg[3]); + zone->zone_weather = atoi(sep->arg[2]); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Weather, 8); + outapp->pBuffer[0] = atoi(sep->arg[2]); + outapp->pBuffer[4] = atoi(sep->arg[3]); // This number changes in the packets, intensity? + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } + else { + c->Message(0, "Manual Usage: #weather 3 "); + } + } + else if(sep->arg[1][0] == '2') { + entity_list.Message(0, 0, "Snowflakes begin to fall from the sky."); + zone->zone_weather = 2; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Weather, 8); + outapp->pBuffer[0] = 0x01; + outapp->pBuffer[4] = 0x02; // This number changes in the packets, intensity? + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } + else if(sep->arg[1][0] == '1') { + entity_list.Message(0, 0, "Raindrops begin to fall from the sky."); + zone->zone_weather = 1; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Weather, 8); + outapp->pBuffer[4] = 0x01; // This is how it's done in Fear, and you can see a decent distance with it at this value + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } + } + else { + if(zone->zone_weather == 1) { // Doing this because if you have rain/snow on, you can only turn one off. + entity_list.Message(0, 0, "The sky clears as the rain ceases to fall."); + zone->zone_weather = 0; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Weather, 8); + // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } + else if(zone->zone_weather == 2) { + entity_list.Message(0, 0, "The sky clears as the snow stops falling."); + zone->zone_weather = 0; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Weather, 8); + // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) + outapp->pBuffer[0] = 0x01; // Snow has it's own shutoff packet + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } + else { + entity_list.Message(0, 0, "The sky clears."); + zone->zone_weather = 0; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Weather, 8); + // To shutoff weather you send an empty 8 byte packet (You get this everytime you zone even if the sky is clear) + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } + } +} + +void command_zheader(Client *c, const Seperator *sep) +{ + // sends zhdr packet + if(sep->arg[1][0]==0) { + c->Message(0, "Usage: #zheader "); + } + else if(database.GetZoneID(sep->argplus[1])==0) + c->Message(0, "Invalid Zone Name: %s", sep->argplus[1]); + else { + + if (zone->LoadZoneCFG(sep->argplus[1], true)) + c->Message(0, "Successfully loaded zone header for %s from database.", sep->argplus[1]); + else + c->Message(0, "Failed to load zone header %s from database", sep->argplus[1]); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + +void command_zsky(Client *c, const Seperator *sep) +{ + // modifys and resends zhdr packet + if(sep->arg[1][0]==0) + c->Message(0, "Usage: #zsky "); + else if(atoi(sep->arg[1])<0||atoi(sep->arg[1])>255) + c->Message(0, "ERROR: Sky type can not be less than 0 or greater than 255!"); + else { + zone->newzone_data.sky = atoi(sep->arg[1]); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + +void command_zcolor(Client *c, const Seperator *sep) +{ + // modifys and resends zhdr packet + if (sep->arg[3][0]==0) + c->Message(0, "Usage: #zcolor "); + else if (atoi(sep->arg[1])<0||atoi(sep->arg[1])>255) + c->Message(0, "ERROR: Red can not be less than 0 or greater than 255!"); + else if (atoi(sep->arg[2])<0||atoi(sep->arg[2])>255) + c->Message(0, "ERROR: Green can not be less than 0 or greater than 255!"); + else if (atoi(sep->arg[3])<0||atoi(sep->arg[3])>255) + c->Message(0, "ERROR: Blue can not be less than 0 or greater than 255!"); + else { + for (int z=0; z<4; z++) { + zone->newzone_data.fog_red[z] = atoi(sep->arg[1]); + zone->newzone_data.fog_green[z] = atoi(sep->arg[2]); + zone->newzone_data.fog_blue[z] = atoi(sep->arg[3]); + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + +void command_spon(Client *c, const Seperator *sep) +{ + c->MemorizeSpell(0, SPELLBAR_UNLOCK, memSpellSpellbar); +} + +void command_spoff(Client *c, const Seperator *sep) +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ManaChange, 0); + outapp->priority = 5; + c->QueuePacket(outapp); + safe_delete(outapp); +} + +void command_itemtest(Client *c, const Seperator *sep) +{ + char chBuffer[8192] = {0}; + //Using this to determine new item layout + FILE* f = NULL; + if (!(f = fopen("c:\\EQEMUcvs\\ItemDump.txt", "rb"))) { + c->Message(13, "Error: Could not open c:\\EQEMUcvs\\ItemDump.txt"); + return; + } + + fread(chBuffer, sizeof(chBuffer), sizeof(char), f); + fclose(f); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ItemLinkResponse, strlen(chBuffer)+5); + memcpy(&outapp->pBuffer[4], chBuffer, strlen(chBuffer)); + c->QueuePacket(outapp); + safe_delete(outapp); +} + +void command_gassign(Client *c, const Seperator *sep) +{ + if (sep->IsNumber(1) && c->GetTarget() && c->GetTarget()->IsNPC()) + { + database.AssignGrid( + c, + (c->GetTarget()->CastToNPC()->org_x), + (c->GetTarget()->CastToNPC()->org_y), + atoi(sep->arg[1]) + ); + } + else + c->Message(0,"Usage: #gassign [num] - must have an npc target!"); +} + +void command_setitemstatus(Client *c, const Seperator *sep) +{ + if (sep->IsNumber(1) && sep->IsNumber(2)) { + uint32 tmp = atoi(sep->arg[1]); + if (tmp >= 0xFFFF) + c->Message(0, "Item# out of range"); + else if (!database.DBSetItemStatus(tmp, atoi(sep->arg[2]))) + c->Message(0, "DB query failed"); + else { + c->Message(0, "Item updated"); + ServerPacket* pack = new ServerPacket(ServerOP_ItemStatus, 5); + *((uint32*) &pack->pBuffer[0]) = tmp; + *((uint8*) &pack->pBuffer[4]) = atoi(sep->arg[2]); + worldserver.SendPacket(pack); + delete pack; + } + } + else + c->Message(0, "Usage: #setitemstatus [itemid] [status]"); +} + +void command_ai(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + + if (strcasecmp(sep->arg[1], "factionid") == 0) { + if (target && sep->IsNumber(2)) { + if (target->IsNPC()) + target->CastToNPC()->SetNPCFactionID(atoi(sep->arg[2])); + else + c->Message(0, "%s is not an NPC.", target->GetName()); + } + else + c->Message(0, "Usage: (targeted) #ai factionid [factionid]"); + } + else if (strcasecmp(sep->arg[1], "spellslist") == 0) { + if (target && sep->IsNumber(2) && atoi(sep->arg[2]) >= 0) { + if (target->IsNPC()) + target->CastToNPC()->AI_AddNPCSpells(atoi(sep->arg[2])); + else + c->Message(0, "%s is not an NPC.", target->GetName()); + } + else + c->Message(0, "Usage: (targeted) #ai spellslist [npc_spells_id]"); + } + else if (strcasecmp(sep->arg[1], "con") == 0) { + if (target && sep->arg[2][0] != 0) { + Mob* tar2 = entity_list.GetMob(sep->arg[2]); + if (tar2) + c->Message(0, "%s considering %s: %i", target->GetName(), tar2->GetName(), tar2->GetReverseFactionCon(target)); + else + c->Message(0, "Error: %s not found.", sep->arg[2]); + } + else + c->Message(0, "Usage: (targeted) #ai con [mob name]"); + } + else if (strcasecmp(sep->arg[1], "guard") == 0) { + if (target && target->IsNPC()) + target->CastToNPC()->SaveGuardSpot(); + else + c->Message(0, "Usage: (targeted) #ai guard - sets npc to guard the current location (use #summon to move)"); + } + else if (strcasecmp(sep->arg[1], "roambox") == 0) { + if (target && target->IsAIControlled() && target->IsNPC()) { + if ((sep->argnum == 6 || sep->argnum == 7) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) { + uint32 tmp = 2500; + if (sep->IsNumber(7)) + tmp = atoi(sep->arg[7]); + target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atof(sep->arg[6]), tmp); + } + else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) { + uint32 tmp = 2500; + if (sep->IsNumber(4)) + tmp = atoi(sep->arg[4]); + target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp); + } + else { + c->Message(0, "Usage: #ai roambox dist max_x min_x max_y min_y [delay]"); + c->Message(0, "Usage: #ai roambox dist roamdist [delay]"); + } + } + else + c->Message(0, "You need a AI NPC targeted"); + } + else if (strcasecmp(sep->arg[1], "stop") == 0 && c->Admin() >= commandToggleAI) { + if (target) { + if (target->IsAIControlled()) + target->AI_Stop(); + else + c->Message(0, "Error: Target is not AI controlled"); + } + else + c->Message(0, "Usage: Target a Mob with AI enabled and use this to turn off their AI."); + } + else if (strcasecmp(sep->arg[1], "start") == 0 && c->Admin() >= commandToggleAI) { + if (target) { + if (!target->IsAIControlled()) + target->AI_Start(); + else + c->Message(0, "Error: Target is already AI controlled"); + } + else + c->Message(0, "Usage: Target a Mob with AI disabled and use this to turn on their AI."); + } + else { + c->Message(0, "#AI Sub-commands"); + c->Message(0, " factionid"); + c->Message(0, " spellslist"); + c->Message(0, " con"); + c->Message(0, " guard"); + } +} + +void command_worldshutdown(Client *c, const Seperator *sep) +{ + // GM command to shutdown world server and all zone servers + uint32 time=0; + 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); + ServerPacket* pack = new ServerPacket(ServerOP_ShutdownAll,sizeof(WorldShutDown_Struct)); + WorldShutDown_Struct* wsd = (WorldShutDown_Struct*)pack->pBuffer; + wsd->time=time*1000; + wsd->interval=(interval*1000); + worldserver.SendPacket(pack); + safe_delete(pack); + } + else if(strcasecmp(sep->arg[1], "now") == 0){ + worldserver.SendEmoteMessage(0,0,15,":SYSTEM MSG:World coming down, everyone log out now."); + c->Message(0, "Sending shutdown packet"); + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_ShutdownAll; + pack->size=0; + worldserver.SendPacket(pack); + safe_delete(pack); + } + else if(strcasecmp(sep->arg[1], "disable") == 0){ + c->Message(0, "Shutdown prevented, next time I may not be so forgiving..."); + ServerPacket* pack = new ServerPacket(ServerOP_ShutdownAll,sizeof(WorldShutDown_Struct)); + WorldShutDown_Struct* wsd = (WorldShutDown_Struct*)pack->pBuffer; + wsd->time=0; + wsd->interval=0; + worldserver.SendPacket(pack); + safe_delete(pack); + } + else{ + c->Message(0,"#worldshutdown - Shuts down the server and all zones."); + c->Message(0,"Usage: #worldshutdown now - Shuts down the server and all zones immediately."); + c->Message(0,"Usage: #worldshutdown disable - Stops the server from a previously scheduled shut down."); + c->Message(0,"Usage: #worldshutdown [timer] [interval] - Shuts down the server and all zones after [timer] seconds and sends warning every [interval] seconds."); + } + } + else + c->Message(0, "Error: World server disconnected"); +} + +void command_sendzonespawns(Client *c, const Seperator *sep) +{ + entity_list.SendZoneSpawns(c); +} + +void command_zsave(Client *c, const Seperator *sep) +{ + if(zone->SaveZoneCFG()) + c->Message(13, "Zone header saved successfully."); + else + c->Message(13, "ERROR: Zone header data was NOT saved."); +} + +void command_dbspawn2(Client *c, const Seperator *sep) +{ + + if (sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3)) { + LogFile->write(EQEMuLog::Normal,"Spawning database spawn"); + uint16 cond = 0; + int16 cond_min = 0; + if(sep->IsNumber(4)) { + cond = atoi(sep->arg[4]); + if(sep->IsNumber(5)) + cond_min = atoi(sep->arg[5]); + } + database.CreateSpawn2(c, atoi(sep->arg[1]), zone->GetShortName(), c->GetHeading(), c->GetX(), c->GetY(), c->GetZ(), atoi(sep->arg[2]), atoi(sep->arg[3]), cond, cond_min); + } + else { + c->Message(0, "Usage: #dbspawn2 spawngroup respawn variance [condition_id] [condition_min]"); + } +} + +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); +} + +void command_delacct(Client *c, const Seperator *sep) +{ + if(sep->arg[1][0] == 0) + c->Message(0, "Format: #delacct accountname"); + else + if (database.DeleteAccount(sep->arg[1])) + c->Message(0, "The account was deleted."); + else + c->Message(0, "Unable to delete account."); +} + +void command_setpass(Client *c, const Seperator *sep) +{ + if(sep->argnum != 2) + c->Message(0, "Format: #setpass accountname password"); + else { + int16 tmpstatus = 0; + uint32 tmpid = database.GetAccountIDByName(sep->arg[1], &tmpstatus); + if (!tmpid) + c->Message(0, "Error: Account not found"); + else if (tmpstatus > c->Admin()) + c->Message(0, "Cannot change password: Account's status is higher than yours"); + else if (database.SetLocalPassword(tmpid, sep->arg[2])) + c->Message(0, "Password changed."); + else + c->Message(0, "Error changing password."); + } +} + +void command_setlsinfo(Client *c, const Seperator *sep) +{ + if(sep->argnum != 2) + c->Message(0, "Format: #setlsinfo email password"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_LSAccountUpdate, sizeof(ServerLSAccountUpdate_Struct)); + ServerLSAccountUpdate_Struct* s = (ServerLSAccountUpdate_Struct *) pack->pBuffer; + s->useraccountid = c->LSAccountID(); + strn0cpy(s->useraccount, c->AccountName(), 30); + strn0cpy(s->useremail, sep->arg[1], 100); + strn0cpy(s->userpassword, sep->arg[2], 50); + worldserver.SendPacket(pack); + c->Message(0, "Login Server update packet sent."); + } +} + +void command_grid(Client *c, const Seperator *sep) +{ + if (strcasecmp("max",sep->arg[1]) == 0) + c->Message(0, "Highest grid ID in this zone: %d", database.GetHighestGrid(zone->GetZoneID())); + else if (strcasecmp("add",sep->arg[1]) == 0) + database.ModifyGrid(c, false,atoi(sep->arg[2]),atoi(sep->arg[3]), atoi(sep->arg[4]),zone->GetZoneID()); + else if (strcasecmp("delete",sep->arg[1]) == 0) + database.ModifyGrid(c, true,atoi(sep->arg[2]),0,0,zone->GetZoneID()); + else { + c->Message(0,"Usage: #grid add/delete grid_num wandertype pausetype"); + c->Message(0,"Usage: #grid max - displays the highest grid ID used in this zone (for add)"); + } +} + +void command_wp(Client *c, const Seperator *sep) +{ + int wp = atoi(sep->arg[4]); + + if (strcasecmp("add",sep->arg[1]) == 0) { + if (wp == 0) //default to highest if it's left blank, or we enter 0 + wp = database.GetHighestWaypoint(zone->GetZoneID(), atoi(sep->arg[2])) + 1; + if (strcasecmp("-h",sep->arg[5]) == 0) { + database.AddWP(c, atoi(sep->arg[2]),wp, c->GetX(), c->GetY(), c->GetZ(), atoi(sep->arg[3]),zone->GetZoneID(), c->GetHeading()); + } + else { + database.AddWP(c, atoi(sep->arg[2]),wp, c->GetX(), c->GetY(), c->GetZ(), atoi(sep->arg[3]),zone->GetZoneID(), -1); + } + } + else if (strcasecmp("delete",sep->arg[1]) == 0) + database.DeleteWaypoint(c, atoi(sep->arg[2]),wp,zone->GetZoneID()); + else + c->Message(0,"Usage: #wp add/delete grid_num pause wp_num [-h]"); +} + +void command_iplookup(Client *c, const Seperator *sep) +{ + ServerPacket* pack = new ServerPacket(ServerOP_IPLookup, sizeof(ServerGenericWorldQuery_Struct) + strlen(sep->argplus[1]) + 1); + ServerGenericWorldQuery_Struct* s = (ServerGenericWorldQuery_Struct *) pack->pBuffer; + strcpy(s->from, c->GetName()); + s->admin = c->Admin(); + if (sep->argplus[1][0] != 0) + strcpy(s->query, sep->argplus[1]); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void command_size(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + if (!sep->IsNumber(1)) + c->Message(0, "Usage: #size [0 - 255] (Decimal increments are allowed)"); + else { + float newsize = atof(sep->arg[1]); + if (newsize > 255) + c->Message(0, "Error: #size: Size can not be greater than 255."); + else if (newsize < 0) + c->Message(0, "Error: #size: Size can not be less than 0."); + else if (!target) + c->Message(0,"Error: this command requires a target"); + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails, newsize); + + c->Message(0,"Size = %f", atof(sep->arg[1])); + } + } +} + +void command_mana(Client *c, const Seperator *sep) +{ + Mob *t; + + t = c->GetTarget() ? c->GetTarget() : c; + + if(t->IsClient()) + t->CastToClient()->SetMana(t->CastToClient()->CalcMaxMana()); + else + t->SetMana(t->CalcMaxMana()); +} + +void command_flymode(Client *c, const Seperator *sep) +{ + Client *t=c; + + if (strlen(sep->arg[1]) == 1 && !(sep->arg[1][0] == '0' || sep->arg[1][0] == '1' || sep->arg[1][0] == '2')) + c->Message(0, "#flymode [0/1/2]"); + else { + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + t->SendAppearancePacket(AT_Levitate, atoi(sep->arg[1])); + if (sep->arg[1][0] == '1') + c->Message(0, "Turning %s's Flymode ON", t->GetName()); + else if (sep->arg[1][0] == '2') + c->Message(0, "Turning %s's Flymode LEV", t->GetName()); + else + c->Message(0, "Turning %s's Flymode OFF", t->GetName()); + } +} + +void command_showskills(Client *c, const Seperator *sep) +{ + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + + c->Message(0, "Skills for %s", t->GetName()); + for (SkillType i=_1H_BLUNT; i <= HIGHEST_SKILL; i=(SkillType)(i+1)) + c->Message(0, "Skill [%d] is at [%d]", i, t->GetSkill(i)); +} + +void command_findspell(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) + c->Message(0, "Usage: #FindSpell [spellname]"); + else if (!spells_loaded) + 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", spellid, spells[spellid].name); + } + } + else { + int count=0; + //int iSearchLen = strlen(sep->argplus[1])+1; + char sName[64]; + char sCriteria[65]; + strn0cpy(sCriteria, sep->argplus[1], 64); + strupr(sCriteria); + for (int i=0; iMessage(0, " %i: %s", i, 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); + } +} + +void command_castspell(Client *c, const Seperator *sep) +{ + if (!sep->IsNumber(1)) + c->Message(0, "Usage: #CastSpell spellid"); + else { + uint16 spellid = atoi(sep->arg[1]); + /* + Spell restrictions. + */ + if (((spellid == 2859) || (spellid == 841) || (spellid == 300) || (spellid == 2314) || + (spellid == 3716) || (spellid == 911) || (spellid == 3014) || (spellid == 982) || + (spellid == 905) || (spellid == 2079) || (spellid == 1218) || (spellid == 819) || + ((spellid >= 780) && (spellid <= 785)) || ((spellid >= 1200) && (spellid <= 1205)) || + ((spellid >= 1342) && (spellid <= 1348)) || (spellid == 1923) || (spellid == 1924) || + (spellid == 3355)) && + c->Admin() < commandCastSpecials) + c->Message(13, "Unable to cast spell."); + else if (spellid >= SPDAT_RECORDS) + c->Message(0, "Error: #CastSpell: Argument out of range"); + else + if (c->GetTarget() == 0) + if(c->Admin() >= commandInstacast) + c->SpellFinished(spellid, 0, 10, 0, -1, spells[spellid].ResistDiff); + else + c->CastSpell(spellid, 0, 10, 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); + } +} + +void command_setlanguage(Client *c, const Seperator *sep) +{ + if ( strcasecmp( sep->arg[1], "list" ) == 0 ) + { + c->Message(0, "Languages:"); + c->Message(0, "(0) Common Tongue"); + c->Message(0, "(1) Barbarian"); + c->Message(0, "(2) Erudian"); + c->Message(0, "(3) Elvish"); + c->Message(0, "(4) Dark Elvish"); + c->Message(0, "(5) Dwarvish"); + c->Message(0, "(6) Troll"); + c->Message(0, "(7) Ogre"); + c->Message(0, "(8) Gnomish"); + c->Message(0, "(9) Halfling"); + c->Message(0, "(10) Thieves Cant"); + c->Message(0, "(11) Old Erudian"); + c->Message(0, "(12) Elder Elvish"); + c->Message(0, "(13) Froglok"); + c->Message(0, "(14) Goblin"); + c->Message(0, "(15) Gnoll"); + c->Message(0, "(16) Combine Tongue"); + c->Message(0, "(17) Elder Teir`Dal"); + c->Message(0, "(18) Lizardman"); + c->Message(0, "(19) Orcish"); + c->Message(0, "(20) Faerie"); + c->Message(0, "(21) Dragon"); + c->Message(0, "(22) Elder Dragon"); + c->Message(0, "(23) Dark Speech"); + c->Message(0, "(24) Vah Shir"); + c->Message(0, "(25) Alaran"); + c->Message(0, "(26) Hadal"); + c->Message(0, "(27) Unknown1"); + } + else if( c->GetTarget() == 0 ) + { + c->Message(0, "Error: #setlanguage: No target."); + } + else if( !c->GetTarget()->IsClient() ) + { + c->Message(0, "Error: Target must be a player."); + } + else if ( + !sep->IsNumber(1) || atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > 27 || + !sep->IsNumber(2) || atoi(sep->arg[2]) < 0 || atoi(sep->arg[2]) > 100 + ) + { + c->Message(0, "Usage: #setlanguage [language ID] [value] (0-27, 0-100)"); + c->Message(0, "Try #setlanguage list for a list of language IDs"); + } + else + { + LogFile->write(EQEMuLog::Normal,"Set language request from %s, target:%s lang_id:%i value:%i", c->GetName(), c->GetTarget()->GetName(), atoi(sep->arg[1]), atoi(sep->arg[2]) ); + uint8 langid = (uint8)atoi(sep->arg[1]); + uint8 value = (uint8)atoi(sep->arg[2]); + c->GetTarget()->CastToClient()->SetLanguageSkill( langid, value ); + } +} + +void command_setskill(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == NULL) { + c->Message(0, "Error: #setskill: No target."); + } + else if (!c->GetTarget()->IsClient()) { + c->Message(0, "Error: #setskill: Target must be a client."); + } + else if ( + !sep->IsNumber(1) || atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > HIGHEST_SKILL || + !sep->IsNumber(2) || atoi(sep->arg[2]) < 0 || atoi(sep->arg[2]) > HIGHEST_CAN_SET_SKILL + ) + { + c->Message(0, "Usage: #setskill skill x "); + c->Message(0, " skill = 0 to %d", HIGHEST_SKILL); + c->Message(0, " x = 0 to %d", HIGHEST_CAN_SET_SKILL); + } + else { + LogFile->write(EQEMuLog::Normal,"Set skill request from %s, target:%s skill_id:%i value:%i", c->GetName(), c->GetTarget()->GetName(), atoi(sep->arg[1]), atoi(sep->arg[2]) ); + int skill_num = atoi(sep->arg[1]); + uint16 skill_value = atoi(sep->arg[2]); + if(skill_num < HIGHEST_SKILL) + c->GetTarget()->CastToClient()->SetSkill((SkillType)skill_num, skill_value); + } +} + +void command_setskillall(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) + c->Message(0, "Error: #setallskill: No target."); + else if (!c->GetTarget()->IsClient()) + c->Message(0, "Error: #setskill: Target must be a client."); + else if (!sep->IsNumber(1) || atoi(sep->arg[1]) < 0 || atoi(sep->arg[1]) > HIGHEST_CAN_SET_SKILL) { + c->Message(0, "Usage: #setskillall value "); + c->Message(0, " value = 0 to %d", HIGHEST_CAN_SET_SKILL); + } + else { + if (c->Admin() >= commandSetSkillsOther || c->GetTarget()==c || c->GetTarget()==0) { + LogFile->write(EQEMuLog::Normal,"Set ALL skill request from %s, target:%s", c->GetName(), c->GetTarget()->GetName()); + uint16 level = atoi(sep->arg[1]); + for(SkillType skill_num=_1H_BLUNT;skill_num <= HIGHEST_SKILL;skill_num=(SkillType)(skill_num+1)) { + c->GetTarget()->CastToClient()->SetSkill(skill_num, level); + } + } + else + c->Message(0, "Error: Your status is not high enough to set anothers skills"); + } +} + +void command_race(Client *c, const Seperator *sep) +{ + Mob *t=c->CastToMob(); + + // Need to figure out max race for LoY/LDoN: going with upper bound of 500 now for testing + if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 724) { + if ((c->GetTarget()) && c->Admin() >= commandRaceOthers) + t=c->GetTarget(); + t->SendIllusionPacket(atoi(sep->arg[1])); + } + else + c->Message(0, "Usage: #race [0-724] (0 for back to normal)"); +} + +void command_gender(Client *c, const Seperator *sep) +{ + Mob *t=c->CastToMob(); + + if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 500) { + if ((c->GetTarget()) && c->Admin() >= commandGenderOthers) + t=c->GetTarget(); + t->SendIllusionPacket(t->GetRace(), atoi(sep->arg[1])); + } + else + c->Message(0, "Usage: #gender [0/1/2]"); +} + +void command_makepet(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == '\0') + c->Message(0, "Usage: #makepet pet_type_name (will not survive across zones)"); + else + c->MakePet(0, sep->arg[1]); +} + +void command_level(Client *c, const Seperator *sep) +{ + uint16 level = atoi(sep->arg[1]); + if ((level <= 0) || ((level > RuleI(Character, MaxLevel)) && (c->Admin() < commandLevelAboveCap)) ) + c->Message(0, "Error: #Level: Invalid Level"); + else if (c->Admin() < 100) + c->SetLevel(level, true); + else if (!c->GetTarget()) + c->Message(0, "Error: #Level: No target"); + else + if (!c->GetTarget()->IsNPC() && ((c->Admin() < commandLevelNPCAboveCap) && (level > RuleI(Character, MaxLevel)))) + c->Message(0, "Error: #Level: Invalid Level"); + else + c->GetTarget()->SetLevel(level, true); + if(c->GetTarget() && c->GetTarget()->IsClient()) + c->GetTarget()->CastToClient()->SendLevelAppearance(); +} + +void command_spawn(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] != 0){ + Client* client = entity_list.GetClientByName(sep->arg[1]); + if(client){ + c->Message(0,"You cannot spawn a mob with the same name as a character!"); + return; + } + } + #if EQDEBUG >= 11 + LogFile->write(EQEMuLog::Debug,"#spawn Spawning:"); + #endif + + NPC* npc = NPC::SpawnNPC(sep->argplus[1], c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(), c); + if (!npc) { + c->Message(0, "Format: #spawn name race level material hp gender class priweapon secweapon merchantid bodytype - spawns a npc those parameters."); + c->Message(0, "Name Format: NPCFirstname_NPCLastname - All numbers in a name are stripped and \"_\" characters become a space."); + c->Message(0, "Note: Using \"-\" for gender will autoselect the gender for the race. Using \"-\" for HP will use the calculated maximum HP."); + } +} + +void command_texture(Client *c, const Seperator *sep) +{ + + uint16 texture; + if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 255) { + texture = atoi(sep->arg[1]); + uint8 helm = 0xFF; + + // Player Races Wear Armor, so Wearchange is sent instead + int i; + if (!c->GetTarget()) + for (i = 0; i < 7; i++) + { + c->SendTextureWC(i, texture); + } + else if ((c->GetTarget()->GetRace() > 0 && c->GetTarget()->GetRace() <= 12) || + c->GetTarget()->GetRace() == 128 || c->GetTarget()->GetRace() == 130 || + c->GetTarget()->GetRace() == 330 || c->GetTarget()->GetRace() == 522) { + for (i = 0; i < 7; i++) + { + c->GetTarget()->SendTextureWC(i, texture); + } + } + else // Non-Player Races only need Illusion Packets to be sent for texture + { + if (sep->IsNumber(2) && atoi(sep->arg[2]) >= 0 && atoi(sep->arg[2]) <= 255) + helm = atoi(sep->arg[2]); + else + helm = texture; + + if (texture == 255) { + texture = 0xFFFF; // Should be pulling these from the database instead + helm = 0xFF; + } + + if ((c->GetTarget()) && (c->Admin() >= commandTextureOthers)) + c->GetTarget()->SendIllusionPacket(c->GetTarget()->GetRace(), 0xFF, texture, helm); + else + c->SendIllusionPacket(c->GetRace(), 0xFF, texture, helm); + } + } + else + c->Message(0, "Usage: #texture [texture] [helmtexture] (0-255, 255 for show equipment)"); +} + +void command_npctypespawn(Client *c, const Seperator *sep) +{ + if (sep->IsNumber(1)) { + const NPCType* tmp = 0; + if ((tmp = database.GetNPCType(atoi(sep->arg[1])))) { + //tmp->fixedZ = 1; + NPC* npc = new NPC(tmp, 0, c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(), FlyMode3); + if (npc && sep->IsNumber(2)) + npc->SetNPCFactionID(atoi(sep->arg[2])); + + npc->AddLootTable(); + entity_list.AddNPC(npc); + } + else + c->Message(0, "NPC Type %i not found", atoi(sep->arg[1])); + } + else + c->Message(0, "Usage: #npctypespawn npctypeid factionid"); + +} + +void command_heal(Client *c, const Seperator *sep) +{ + if (c->GetTarget()==0) + c->Message(0, "Error: #Heal: No Target."); + else + c->GetTarget()->Heal(); +} + +void command_appearance(Client *c, const Seperator *sep) +{ + Mob *t=c->CastToMob(); + + // sends any appearance packet + // Dev debug command, for appearance types + if (sep->arg[2][0] == 0) + c->Message(0, "Usage: #appearance type value"); + else { + if ((c->GetTarget())) + t=c->GetTarget(); + t->SendAppearancePacket(atoi(sep->arg[1]), atoi(sep->arg[2])); + c->Message(0, "Sending appearance packet: target=%s, type=%s, value=%s", t->GetName(), sep->arg[1], sep->arg[2]); + } +} + +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; + + if (c->GetTarget() && c->GetTarget()->IsClient() && (sep->IsNumber(1) || sep->IsHexNumber(1))) { + itemid=sep->IsNumber(1)?atoi(sep->arg[1]):hextoi(sep->arg[1]); + numitems = c->GetTarget()->CastToClient()->NukeItem(itemid); + c->Message(0, " %u items deleted", numitems); + } + else + c->Message(0, "Usage: (targted) #nukeitem itemnum - removes the item from the player's inventory"); +} + +void command_peekinv(Client *c, const Seperator *sep) +{ + // Displays what the server thinks the user has in inventory + 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 = NULL; + c->Message(0, "Displaying inventory for %s...", client->GetName()); + + if (bAll || (strcasecmp(sep->arg[1], "worn")==0)) { + // Worn items + bFound = true; + for (int16 i=0; i<=21; i++) { + const ItemInst* inst = client->GetInv().GetItem(i); + item = (inst) ? inst->GetItem() : NULL; + if (c->GetClientVersion() >= EQClientSoF) + { + 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=22; i<=29; i++) { + const ItemInst* inst = client->GetInv().GetItem(i); + item = (inst) ? inst->GetItem() : NULL; + 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 (inst && inst->IsType(ItemClassContainer)) { + for (uint8 j=0; j<10; j++) { + const ItemInst* instbag = client->GetInv().GetItem(i, j); + item = (instbag) ? instbag->GetItem() : NULL; + 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(9999); + item = (inst) ? inst->GetItem() : NULL; + c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", 9999, + ((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", SLOT_CURSOR,i, + 0, 0x12, 0, "null", 0x12, 0); + } + else + { + c->Message((item==0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", SLOT_CURSOR,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() : NULL; + if (c->GetClientVersion() >= EQClientSoF) + { + c->Message((item==0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", SLOT_CURSOR,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", SLOT_CURSOR,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=0; j<10; j++) { + const ItemInst* instbag = client->GetInv().GetItem(SLOT_CURSOR, j); + item = (instbag) ? instbag->GetItem() : NULL; + if (c->GetClientVersion() >= EQClientSoF) + { + c->Message((item==0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", + Inventory::CalcSlotId(SLOT_CURSOR, j), + SLOT_CURSOR, 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(SLOT_CURSOR, j), + SLOT_CURSOR, 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=TRIBUTE_SLOT_START; i<(TRIBUTE_SLOT_START + MAX_PLAYER_TRIBUTES); i++) { + const ItemInst* inst = client->GetInv().GetItem(i); + item = (inst) ? inst->GetItem() : NULL; + 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=2000; i<=2023; i++) { + const ItemInst* inst = client->GetInv().GetItem(i); + item = (inst) ? inst->GetItem() : NULL; + 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=0; j<10; j++) { + const ItemInst* instbag = client->GetInv().GetItem(i, j); + item = (instbag) ? instbag->GetItem() : NULL; + 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=2500; i<=2501; i++) { + const ItemInst* inst = client->GetInv().GetItem(i); + item = (inst) ? inst->GetItem() : NULL; + 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=0; j<10; j++) { + const ItemInst* instbag = client->GetInv().GetItem(i, j); + item = (instbag) ? instbag->GetItem() : NULL; + 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=3000; i<=3007; i++) { + const ItemInst* inst = client->GetInv().GetItem(i); + item = (inst) ? inst->GetItem() : NULL; + 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=0; j<10; j++) { + const ItemInst* instbag = client->GetInv().GetItem(i, j); + item = (instbag) ? instbag->GetItem() : NULL; + 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]"); + c->Message(0, " Displays a portion of the targeted user's inventory"); + c->Message(0, " Caution: 'all' is a lot of information!"); + } +} + +void command_findnpctype(Client *c, const Seperator *sep) +{ + 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; + + query = new char[256]; + + // 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); + + // If query runs successfully. + if (database.RunQuery(query, strlen(query), errbuf, &result)) + { + count = 0; + + // 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 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]); + + mysql_free_result(result); + } + // If query failed. + else + { + c->Message (0, "Error querying database."); + c->Message (0, query); + } + + safe_delete_array(query); + } +} + +void command_findzone(Client *c, const Seperator *sep) +{ + 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; + + query = new char[256]; + + // 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])); + + 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); + + if (database.RunQuery(query, strlen(query), errbuf, &result)) + { + count = 0; + + 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]); + } + + 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]); + + mysql_free_result(result); + } + else + { + c->Message (0, "Error querying database."); + c->Message (0, query); + } + + safe_delete_array(query); + } +} + +void command_viewnpctype(Client *c, const Seperator *sep) +{ + if (!sep->IsNumber(1)) + c->Message(0, "Usage: #viewnpctype [npctype id]"); + else + { + uint32 npctypeid=atoi(sep->arg[1]); + const NPCType* npct = database.GetNPCType(npctypeid); + if (npct) { + c->Message(0, " NPCType Info, "); + c->Message(0, " NPCTypeID: %u", npct->npc_id); + c->Message(0, " Name: %s", npct->name); + c->Message(0, " Level: %i", npct->level); + c->Message(0, " Race: %i", npct->race); + c->Message(0, " Class: %i", npct->class_); + c->Message(0, " MinDmg: %i", npct->min_dmg); + c->Message(0, " MaxDmg: %i", npct->max_dmg); + c->Message(0, " Attacks: %s", npct->npc_attacks); + c->Message(0, " Spells: %i", npct->npc_spells_id); + c->Message(0, " Loot Table: %i", npct->loottable_id); + c->Message(0, " NPCFactionID: %i", npct->npc_faction_id); + } + else + c->Message(0, "NPC #%d not found", npctypeid); + } +} + +void command_reloadqst(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) + { + c->Message(0, "Clearing quest memory cache."); + parse->ReloadQuests(); + } + else + { + c->Message(0, "Clearing quest memory cache and stopping timers."); + parse->ReloadQuests(true); + } + +} + +void command_reloadworld(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) + { + c->Message(0, "Reloading quest cache and repopping zones worldwide."); + ServerPacket* pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct)); + ReloadWorld_Struct* RW = (ReloadWorld_Struct*) pack->pBuffer; + RW->Option = 1; + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +void command_reloadlevelmods(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) + { + if(RuleB(Zone, LevelBasedEXPMods)){ + zone->LoadLevelEXPMods(); + c->Message(15, "Level based EXP Mods have been reloaded zonewide"); + }else{ + c->Message(15, "Level based EXP Mods are disabled in rules!"); + } + } +} + +void command_reloadzps(Client *c, const Seperator *sep) +{ + database.LoadStaticZonePoints(&zone->zone_point_list, zone->GetShortName(), zone->GetInstanceVersion()); + c->Message(0, "Reloading server zone_points."); +} + +void command_zoneshutdown(Client *c, const Seperator *sep) +{ + if (!worldserver.Connected()) + c->Message(0, "Error: World server disconnected"); + else if (sep->arg[1][0] == 0) + c->Message(0, "Usage: #zoneshutdown zoneshortname"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_ZoneShutdown, sizeof(ServerZoneStateChange_struct)); + ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer; + strcpy(s->adminname, c->GetName()); + if (sep->arg[1][0] >= '0' && sep->arg[1][0] <= '9') + s->ZoneServerID = atoi(sep->arg[1]); + else + s->zoneid = database.GetZoneID(sep->arg[1]); + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +void command_zonebootup(Client *c, const Seperator *sep) +{ + if (!worldserver.Connected()) + c->Message(0, "Error: World server disconnected"); + else if (sep->arg[2][0] == 0) { + c->Message(0, "Usage: #zonebootup ZoneServerID# zoneshortname"); + } + else { + ServerPacket* pack = new ServerPacket(ServerOP_ZoneBootup, sizeof(ServerZoneStateChange_struct)); + ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer; + s->ZoneServerID = atoi(sep->arg[1]); + strcpy(s->adminname, c->GetName()); + s->zoneid = database.GetZoneID(sep->arg[2]); + s->makestatic = (bool) (strcasecmp(sep->arg[3], "static") == 0); + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +void command_kick(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) + c->Message(0, "Usage: #kick [charname]"); + else { + Client* client = entity_list.GetClientByName(sep->arg[1]); + if (client != 0) { + if (client->Admin() <= c->Admin()) { + client->Message(0, "You have been kicked by %s",c->GetName()); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMKick,0); + client->QueuePacket(outapp); + client->Kick(); + c->Message(0, "Kick: local: kicking %s", sep->arg[1]); + } + } + else if (!worldserver.Connected()) + c->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, c->GetName()); + strcpy(skp->name, sep->arg[1]); + skp->adminrank = c->Admin(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + } +} + +void command_attack(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1] != 0) { + Mob* sictar = entity_list.GetMob(sep->argplus[1]); + if (sictar) + c->GetTarget()->CastToNPC()->AddToHateList(sictar, 1, 0); + else + c->Message(0, "Error: %s not found", sep->arg[1]); + } + else + c->Message(0, "Usage: (needs NPC targeted) #attack targetname"); +} + +void command_lock(Client *c, const Seperator *sep) +{ + ServerPacket* outpack = new ServerPacket(ServerOP_Lock, sizeof(ServerLock_Struct)); + ServerLock_Struct* lss = (ServerLock_Struct*) outpack->pBuffer; + strcpy(lss->myname, c->GetName()); + lss->mode = 1; + worldserver.SendPacket(outpack); + safe_delete(outpack); +} + +void command_unlock(Client *c, const Seperator *sep) +{ + ServerPacket* outpack = new ServerPacket(ServerOP_Lock, sizeof(ServerLock_Struct)); + ServerLock_Struct* lss = (ServerLock_Struct*) outpack->pBuffer; + strcpy(lss->myname, c->GetName()); + lss->mode = 0; + worldserver.SendPacket(outpack); + safe_delete(outpack); +} + +void command_motd(Client *c, const Seperator *sep) +{ + ServerPacket* outpack = new ServerPacket(ServerOP_Motd, sizeof(ServerMotd_Struct)); + ServerMotd_Struct* mss = (ServerMotd_Struct*) outpack->pBuffer; + strn0cpy(mss->myname, c->GetName(),64); + strn0cpy(mss->motd, sep->argplus[1],512); + worldserver.SendPacket(outpack); + safe_delete(outpack); +} + +void command_listpetition(Client *c, const Seperator *sep) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + int blahloopcount=0; + 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 (blahloopcount==0) { + blahloopcount=1; + 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); +} + +void command_equipitem(Client *c, const Seperator *sep) +{ + uint32 slot_id = atoi(sep->arg[1]); + if (sep->IsNumber(1) && (slot_id>=0) && (slot_id<=21)) { + const ItemInst* from_inst = c->GetInv().GetItem(SLOT_CURSOR); + const ItemInst* to_inst = c->GetInv().GetItem(slot_id); // added (desync issue when forcing stack to stack) + bool partialmove = false; + int16 movecount; + + if (from_inst && from_inst->IsType(ItemClassCommon)) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_MoveItem, sizeof(MoveItem_Struct)); + MoveItem_Struct* mi = (MoveItem_Struct*)outapp->pBuffer; + mi->from_slot = SLOT_CURSOR; + mi->to_slot = slot_id; + // mi->number_in_stack = from_inst->GetCharges(); // replaced with con check for stacking + + // crude stackable check to only 'move' the difference count on client instead of entire stack when applicable + if (to_inst && to_inst->IsStackable() && + (to_inst->GetItem()->ID == from_inst->GetItem()->ID) && + (to_inst->GetCharges() < to_inst->GetItem()->StackSize) && + (from_inst->GetCharges() > to_inst->GetItem()->StackSize - to_inst->GetCharges())) { + movecount = to_inst->GetItem()->StackSize - to_inst->GetCharges(); + mi->number_in_stack = (uint32)movecount; + partialmove = true; + } + else + mi->number_in_stack = from_inst->GetCharges(); + + // Save move changes + // Added conditional check to packet send..would have sent change even on a swap failure..whoops! + + if (partialmove) { // remove this con check if someone can figure out removing charges from cursor stack issue below + // mi->number_in_stack is always from_inst->GetCharges() when partialmove is false + c->Message(13, "Error: Partial stack added to existing stack exceeds allowable stacksize"); + return; + } + else if(c->SwapItem(mi)) { + c->FastQueuePacket(&outapp); + + // below code has proper logic, but client does not like to have cursor charges changed + // (we could delete the cursor item and resend, but issues would arise if there are queued items) + //if (partialmove) { + // EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_DeleteItem, sizeof(DeleteItem_Struct)); + // DeleteItem_Struct* di = (DeleteItem_Struct*)outapp2->pBuffer; + // di->from_slot = SLOT_CURSOR; + // di->to_slot = 0xFFFFFFFF; + // di->number_in_stack = 0xFFFFFFFF; + + // c->Message(0, "Deleting %i charges from stack", movecount); // debug line..delete + + // for (int16 deletecount=0; deletecount < movecount; deletecount++) + // have to use 'movecount' because mi->number_in_stack is 'ENCODED' at this point (i.e., 99 charges returns 22...) + // c->QueuePacket(outapp2); + + // safe_delete(outapp2); + //} + } + else { + c->Message(13, "Error: Unable to equip current item"); + } + safe_delete(outapp); + + // also send out a wear change packet? + } + else if (from_inst == NULL) + c->Message(13, "Error: There is no item on your cursor"); + else + c->Message(13, "Error: Item on your cursor cannot be equipped"); + } + else + c->Message(0, "Usage: #equipitem slotid[0-21] - equips the item on your cursor to the position"); +} + +void command_zonelock(Client *c, const Seperator *sep) +{ + ServerPacket* pack = new ServerPacket(ServerOP_LockZone, sizeof(ServerLockZone_Struct)); + ServerLockZone_Struct* s = (ServerLockZone_Struct*) pack->pBuffer; + strn0cpy(s->adminname, c->GetName(), sizeof(s->adminname)); + if (strcasecmp(sep->arg[1], "list") == 0) { + s->op = 0; + worldserver.SendPacket(pack); + } + else if (strcasecmp(sep->arg[1], "lock") == 0 && c->Admin() >= commandLockZones) { + uint16 tmp = database.GetZoneID(sep->arg[2]); + if (tmp) { + s->op = 1; + s->zoneID = tmp; + worldserver.SendPacket(pack); + } + else + c->Message(0, "Usage: #zonelock lock [zonename]"); + } + else if (strcasecmp(sep->arg[1], "unlock") == 0 && c->Admin() >= commandLockZones) { + uint16 tmp = database.GetZoneID(sep->arg[2]); + if (tmp) { + s->op = 2; + s->zoneID = tmp; + worldserver.SendPacket(pack); + } + else + c->Message(0, "Usage: #zonelock unlock [zonename]"); + } + else { + c->Message(0, "#zonelock sub-commands"); + c->Message(0, " list"); + if(c->Admin() >= commandLockZones) + { + c->Message(0, " lock [zonename]"); + c->Message(0, " unlock [zonename]"); + } + } + safe_delete(pack); +} + +void command_corpse(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + + if (strcasecmp(sep->arg[1], "DeletePlayerCorpses") == 0 && c->Admin() >= commandEditPlayerCorpses) { + int32 tmp = entity_list.DeletePlayerCorpses(); + if (tmp >= 0) + c->Message(0, "%i corpses deleted.", tmp); + else + c->Message(0, "DeletePlayerCorpses Error #%i", tmp); + } + else if (strcasecmp(sep->arg[1], "delete") == 0) { + if (target == 0 || !target->IsCorpse()) + c->Message(0, "Error: Target the corpse you wish to delete"); + else if (target->IsNPCCorpse()) { + + c->Message(0, "Depoping %s.", target->GetName()); + target->CastToCorpse()->Delete(); + } + else if (c->Admin() >= commandEditPlayerCorpses) { + c->Message(0, "Deleting %s.", target->GetName()); + target->CastToCorpse()->Delete(); + } + else + c->Message(0, "Insufficient status to delete player corpse."); + } + else if (strcasecmp(sep->arg[1], "ListNPC") == 0) { + entity_list.ListNPCCorpses(c); + } + else if (strcasecmp(sep->arg[1], "ListPlayer") == 0) { + entity_list.ListPlayerCorpses(c); + } + else if (strcasecmp(sep->arg[1], "DeleteNPCCorpses") == 0) { + int32 tmp = entity_list.DeleteNPCCorpses(); + if (tmp >= 0) + c->Message(0, "%d corpses deleted.", tmp); + else + c->Message(0, "DeletePlayerCorpses Error #%d", tmp); + } + else if (strcasecmp(sep->arg[1], "charid") == 0 && c->Admin() >= commandEditPlayerCorpses) { + if (target == 0 || !target->IsPlayerCorpse()) + c->Message(0, "Error: Target must be a player corpse."); + else if (!sep->IsNumber(2)) + c->Message(0, "Error: charid must be a number."); + else + c->Message(0, "Setting CharID=%u on PlayerCorpse '%s'", target->CastToCorpse()->SetCharID(atoi(sep->arg[2])), target->GetName()); + } + else if (strcasecmp(sep->arg[1], "ResetLooter") == 0) { + if (target == 0 || !target->IsCorpse()) + c->Message(0, "Error: Target the corpse you wish to reset"); + else + target->CastToCorpse()->ResetLooter(); + } + else if (strcasecmp(sep->arg[1], "RemoveCash") == 0) { + if (target == 0 || !target->IsCorpse()) + c->Message(0, "Error: Target the corpse you wish to remove the cash from"); + else if (!target->IsPlayerCorpse() || c->Admin() >= commandEditPlayerCorpses) { + c->Message(0, "Removing Cash from %s.", target->GetName()); + target->CastToCorpse()->RemoveCash(); + } + else + c->Message(0, "Insufficient status to modify player corpse."); + } + else if (strcasecmp(sep->arg[1], "InspectLoot") == 0) { + if (target == 0 || !target->IsCorpse()) + c->Message(0, "Error: Target must be a corpse."); + else + target->CastToCorpse()->QueryLoot(c); + } + else if (strcasecmp(sep->arg[1], "lock") == 0) { + if (target == 0 || !target->IsCorpse()) + c->Message(0, "Error: Target must be a corpse."); + else { + target->CastToCorpse()->Lock(); + c->Message(0, "Locking %s...", target->GetName()); + } + } + else if (strcasecmp(sep->arg[1], "unlock") == 0) { + if (target == 0 || !target->IsCorpse()) + c->Message(0, "Error: Target must be a corpse."); + else { + target->CastToCorpse()->UnLock(); + c->Message(0, "Unlocking %s...", target->GetName()); + } + } + else if (strcasecmp(sep->arg[1], "depop") == 0) { + if (target == 0 || !target->IsPlayerCorpse()) + 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(); + if(!sep->arg[2][0] || atoi(sep->arg[2]) != 0) + target->CastToCorpse()->Bury(); + } + else + c->Message(0, "Insufficient status to depop player corpse."); + } + else if (strcasecmp(sep->arg[1], "depopall") == 0) { + if (target == 0 || !target->IsClient()) + c->Message(0, "Error: Target must be a player."); + else if (c->Admin() >= commandEditPlayerCorpses && target->IsClient()) { + c->Message(0, "Depoping %s\'s corpses.", target->GetName()); + target->CastToClient()->DepopAllCorpses(); + if(!sep->arg[2][0] || atoi(sep->arg[2]) != 0) + target->CastToClient()->BuryPlayerCorpses(); + } + else + c->Message(0, "Insufficient status to depop player corpse."); + + } + else if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "help") == 0) { + c->Message(0, "#Corpse Sub-Commands:"); + c->Message(0, " DeleteNPCCorpses"); + c->Message(0, " Delete - Delete targetted corpse"); + c->Message(0, " ListNPC"); + c->Message(0, " ListPlayer"); + c->Message(0, " Lock - GM locks the corpse - cannot be looted by non-GM"); + c->Message(0, " UnLock"); + c->Message(0, " RemoveCash"); + c->Message(0, " InspectLoot"); + c->Message(0, " [to remove items from corpses, loot them]"); + c->Message(0, "Lead-GM status required to delete/modify player corpses"); + c->Message(0, " DeletePlayerCorpses"); + c->Message(0, " CharID [charid] - change player corpse's owner"); + c->Message(0, " Depop [bury] - Depops single target corpse."); + c->Message(0, " Depopall [bury] - Depops all target player's corpses."); + c->Message(0, "Set bury to 0 to skip burying the corpses."); + } + else + c->Message(0, "Error, #corpse sub-command not found"); +} + +void command_fixmob(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + const char* Usage = "Usage: #fixmob [race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev]"; + + if (!sep->arg[1]) + c->Message(0,Usage); + else if (!target) + c->Message(0,"Error: this command requires a target"); + else + { + + uint32 Adjustment = 1; // Previous or Next + char codeMove; + + if (sep->arg[2]) + { + char* command2 = sep->arg[2]; + codeMove = (command2[0] | 0x20); // First character, lower-cased + if (codeMove == 'n') + Adjustment = 1; + else if (codeMove == 'p') + Adjustment = -1; + } + + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + char* ChangeType = NULL; // If it's still NULL after processing, they didn't send a valid command + uint32 ChangeSetting; + char* command = sep->arg[1]; + + if (strcasecmp(command, "race") == 0) + { + if (Race == 1 && codeMove == 'p') + Race = 724; + else if (Race >= 724 && codeMove != 'p') + Race = 1; + else + Race += Adjustment; + ChangeType = "Race"; + ChangeSetting = Race; + } + else if (strcasecmp(command, "gender") == 0) + { + if (Gender == 0 && codeMove == 'p') + Gender = 2; + else if (Gender >= 2 && codeMove != 'p') + Gender = 0; + else + Gender += Adjustment; + ChangeType = "Gender"; + ChangeSetting = Gender; + } + else if (strcasecmp(command, "texture") == 0) + { + Texture = target->GetTexture(); + + if (Texture == 0 && codeMove == 'p') + Texture = 25; + else if (Texture >= 25 && codeMove != 'p') + Texture = 0; + else + Texture += Adjustment; + ChangeType = "Texture"; + ChangeSetting = Texture; + } + else if (strcasecmp(command, "helm") == 0) + { + HelmTexture = target->GetHelmTexture(); + if (HelmTexture == 0 && codeMove == 'p') + HelmTexture = 25; + else if (HelmTexture >= 25 && codeMove != 'p') + HelmTexture = 0; + else + HelmTexture += Adjustment; + ChangeType = "HelmTexture"; + ChangeSetting = HelmTexture; + } + else if (strcasecmp(command, "face") == 0) + { + if (LuclinFace == 0 && codeMove == 'p') + LuclinFace = 87; + else if (LuclinFace >= 87 && codeMove != 'p') + LuclinFace = 0; + else + LuclinFace += Adjustment; + ChangeType = "LuclinFace"; + ChangeSetting = LuclinFace; + } + else if (strcasecmp(command, "hair") == 0) + { + if (HairStyle == 0 && codeMove == 'p') + HairStyle = 8; + else if (HairStyle >= 8 && codeMove != 'p') + HairStyle = 0; + else + HairStyle += Adjustment; + ChangeType = "HairStyle"; + ChangeSetting = HairStyle; + } + else if (strcasecmp(command, "haircolor") == 0) + { + if (HairColor == 0 && codeMove == 'p') + HairColor = 24; + else if (HairColor >= 24 && codeMove != 'p') + HairColor = 0; + else + HairColor += Adjustment; + ChangeType = "HairColor"; + ChangeSetting = HairColor; + } + else if (strcasecmp(command, "beard") == 0) + { + if (Beard == 0 && codeMove == 'p') + Beard = 11; + else if (Beard >= 11 && codeMove != 'p') + Beard = 0; + else + Beard += Adjustment; + ChangeType = "Beard"; + ChangeSetting = Beard; + } + else if (strcasecmp(command, "beardcolor") == 0) + { + if (BeardColor == 0 && codeMove == 'p') + BeardColor = 24; + else if (BeardColor >= 24 && codeMove != 'p') + BeardColor = 0; + else + BeardColor += Adjustment; + ChangeType = "BeardColor"; + ChangeSetting = BeardColor; + } + else if (strcasecmp(command, "heritage") == 0) + { + if (DrakkinHeritage == 0 && codeMove == 'p') + DrakkinHeritage = 6; + else if (DrakkinHeritage >= 6 && codeMove != 'p') + DrakkinHeritage = 0; + else + DrakkinHeritage += Adjustment; + ChangeType = "DrakkinHeritage"; + ChangeSetting = DrakkinHeritage; + } + else if (strcasecmp(command, "tattoo") == 0) + { + if (DrakkinTattoo == 0 && codeMove == 'p') + DrakkinTattoo = 8; + else if (DrakkinTattoo >= 8 && codeMove != 'p') + DrakkinTattoo = 0; + else + DrakkinTattoo += Adjustment; + ChangeType = "DrakkinTattoo"; + ChangeSetting = DrakkinTattoo; + } + else if (strcasecmp(command, "detail") == 0) + { + if (DrakkinDetails == 0 && codeMove == 'p') + DrakkinDetails = 7; + else if (DrakkinDetails >= 7 && codeMove != 'p') + DrakkinDetails = 0; + else + DrakkinDetails += Adjustment; + ChangeType = "DrakkinDetails"; + ChangeSetting = DrakkinDetails; + } + + // Hack to fix some races that base features from face + switch (Race) + { + case 2: // Barbarian + if (LuclinFace > 10) { + LuclinFace -= ((DrakkinTattoo - 1) * 10); + } + LuclinFace += (DrakkinTattoo * 10); + break; + case 3: // Erudite + if (LuclinFace > 10) { + LuclinFace -= ((HairStyle - 1) * 10); + } + LuclinFace += (HairStyle * 10); + break; + case 5: // HighElf + case 6: // DarkElf + case 7: // HalfElf + if (LuclinFace > 10) { + LuclinFace -= ((Beard - 1) * 10); + } + LuclinFace += (Beard * 10); + break; + default: + break; + } + + + if (ChangeType == NULL) + { + c->Message(0,Usage); + } + else + { + target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails); + + c->Message(0, "%s=%i", ChangeType, ChangeSetting); + } + } +} + +void command_gmspeed(Client *c, const Seperator *sep) +{ + bool state=atobool(sep->arg[1]); + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + + if(sep->arg[1][0] != 0) { + database.SetGMSpeed(t->AccountID(), state?1:0); + c->Message(0, "Turning GMSpeed %s for %s (zone to take effect)", state?"On":"Off",t->GetName()); + } + else + c->Message(0, "Usage: #gmspeed [on/off]"); +} + +void command_title(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0]==0) + c->Message(0, "Usage: #title [remove|text] [1 = Create row in title table] - remove or set title to 'text'"); + else { + bool Save = (atoi(sep->arg[2]) == 1); + + Mob *target_mob = c->GetTarget(); + if(!target_mob) + target_mob = c; + if(!target_mob->IsClient()) { + c->Message(13, "#title only works on players."); + return; + } + Client *t = target_mob->CastToClient(); + + if(strlen(sep->arg[1]) > 31) { + c->Message(13, "Title must be 31 characters or less."); + return; + } + + bool removed = false; + if(!strcasecmp(sep->arg[1], "remove")) { + t->SetAATitle(""); + removed = true; + } else { + for(unsigned int i=0; iarg[1]); i++) + if(sep->arg[1][i]=='_') + sep->arg[1][i] = ' '; + if(!Save) + t->SetAATitle(sep->arg[1]); + else + title_manager.CreateNewPlayerTitle(t, sep->arg[1]); + } + + t->Save(); + + if(removed) { + c->Message(13, "%s's title has been removed.", t->GetName(), sep->arg[1]); + if(t != c) + t->Message(13, "Your title has been removed.", sep->arg[1]); + } else { + c->Message(13, "%s's title has been changed to '%s'.", t->GetName(), sep->arg[1]); + if(t != c) + t->Message(13, "Your title has been changed to '%s'.", sep->arg[1]); + } + } +} + + +void command_titlesuffix(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0]==0) + c->Message(0, "Usage: #titlesuffix [remove|text] [1 = create row in title table] - remove or set title suffix to 'text'"); + else { + bool Save = (atoi(sep->arg[2]) == 1); + + Mob *target_mob = c->GetTarget(); + if(!target_mob) + target_mob = c; + if(!target_mob->IsClient()) { + c->Message(13, "#titlesuffix only works on players."); + return; + } + Client *t = target_mob->CastToClient(); + + if(strlen(sep->arg[1]) > 31) { + c->Message(13, "Title suffix must be 31 characters or less."); + return; + } + + bool removed = false; + if(!strcasecmp(sep->arg[1], "remove")) { + t->SetTitleSuffix(""); + removed = true; + } else { + for(unsigned int i=0; iarg[1]); i++) + if(sep->arg[1][i]=='_') + sep->arg[1][i] = ' '; + + if(!Save) + t->SetTitleSuffix(sep->arg[1]); + else + title_manager.CreateNewPlayerSuffix(t, sep->arg[1]); + } + + t->Save(); + + if(removed) { + c->Message(13, "%s's title suffix has been removed.", t->GetName(), sep->arg[1]); + if(t != c) + t->Message(13, "Your title suffix has been removed.", sep->arg[1]); + } else { + c->Message(13, "%s's title suffix has been changed to '%s'.", t->GetName(), sep->arg[1]); + if(t != c) + t->Message(13, "Your title suffix has been changed to '%s'.", sep->arg[1]); + } + } +} + +void command_spellinfo(Client *c, const Seperator *sep) +{ + if(sep->arg[1][0]==0) + c->Message(0, "Usage: #spellinfo [spell_id]"); + else { + short int spell_id=atoi(sep->arg[1]); + const struct SPDat_Spell_Struct *s=&spells[spell_id]; + c->Message(0, "Spell info for spell #%d:", spell_id); + c->Message(0, " name: %s", s->name); + c->Message(0, " player_1: %s", s->player_1); + c->Message(0, " teleport_zone: %s", s->teleport_zone); + c->Message(0, " you_cast: %s", s->you_cast); + c->Message(0, " other_casts: %s", s->other_casts); + c->Message(0, " cast_on_you: %s", s->cast_on_you); + c->Message(0, " spell_fades: %s", s->spell_fades); + c->Message(0, " range: %f", s->range); + c->Message(0, " aoerange: %f", s->aoerange); + c->Message(0, " pushback: %f", s->pushback); + c->Message(0, " pushup: %f", s->pushup); + c->Message(0, " cast_time: %d", s->cast_time); + c->Message(0, " recovery_time: %d", s->recovery_time); + c->Message(0, " recast_time: %d", s->recast_time); + c->Message(0, " buffdurationformula: %d", s->buffdurationformula); + c->Message(0, " buffduration: %d", s->buffduration); + c->Message(0, " AEDuration: %d", s->AEDuration); + c->Message(0, " mana: %d", s->mana); + c->Message(0, " base[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->base[0], s->base[1], s->base[2], s->base[3], s->base[4], s->base[5], s->base[6], s->base[7], s->base[8], s->base[9], s->base[10], s->base[11]); + c->Message(0, " base22[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->base2[0], s->base2[1], s->base2[2], s->base2[3], s->base2[4], s->base2[5], s->base2[6], s->base2[7], s->base2[8], s->base2[9], s->base2[10], s->base2[11]); + c->Message(0, " max[12]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", s->max[0], s->max[1], s->max[2], s->max[3], s->max[4], s->max[5], s->max[6], s->max[7], s->max[8], s->max[9], s->max[10], s->max[11]); + c->Message(0, " icon: %d", s->icon); + c->Message(0, " memicon: %d", s->memicon); + c->Message(0, " components[4]: %d, %d, %d, %d", s->components[0], s->components[1], s->components[2], s->components[3]); + c->Message(0, " component_counts[4]: %d, %d, %d, %d", s->component_counts[0], s->component_counts[1], s->component_counts[2], s->component_counts[3]); + c->Message(0, " NoexpendReagent[4]: %d, %d, %d, %d", s->NoexpendReagent[0], s->NoexpendReagent[1], s->NoexpendReagent[2], s->NoexpendReagent[3]); + c->Message(0, " formula[12]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", s->formula[0], s->formula[1], s->formula[2], s->formula[3], s->formula[4], s->formula[5], s->formula[6], s->formula[7], s->formula[8], s->formula[9], s->formula[10], s->formula[11]); + c->Message(0, " LightType: %d", s->LightType); + c->Message(0, " goodEffect: %d", s->goodEffect); + c->Message(0, " Activated: %d", s->Activated); + c->Message(0, " resisttype: %d", s->resisttype); + c->Message(0, " effectid[12]: 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x", s->effectid[0], s->effectid[1], s->effectid[2], s->effectid[3], s->effectid[4], s->effectid[5], s->effectid[6], s->effectid[7], s->effectid[8], s->effectid[9], s->effectid[10], s->effectid[11]); + c->Message(0, " targettype: %d", s->targettype); + c->Message(0, " basediff: %d", s->basediff); + c->Message(0, " skill: %d", s->skill); + c->Message(0, " zonetype: %d", s->zonetype); + c->Message(0, " EnvironmentType: %d", s->EnvironmentType); + c->Message(0, " TimeOfDay: %d", s->TimeOfDay); + c->Message(0, " classes[15]: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + s->classes[0], s->classes[1], s->classes[2], s->classes[3], s->classes[4], + s->classes[5], s->classes[6], s->classes[7], s->classes[8], s->classes[9], + s->classes[10], s->classes[11], s->classes[12], s->classes[13], s->classes[14]); + c->Message(0, " CastingAnim: %d", s->CastingAnim); + c->Message(0, " TargetAnim: %d", s->TargetAnim); + c->Message(0, " SpellAffectIndex: %d", s->SpellAffectIndex); + c->Message(0, " RecourseLink: %d", s->RecourseLink); + } +} + +void command_lastname(Client *c, const Seperator *sep) +{ + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + LogFile->write(EQEMuLog::Normal,"#lastname request from %s for %s", c->GetName(), t->GetName()); + + if(strlen(sep->arg[1]) <= 70) + t->ChangeLastName(sep->arg[1]); + else + c->Message(0, "Usage: #lastname where is less than 70 chars long"); +} + +void command_memspell(Client *c, const Seperator *sep) +{ + uint32 slot; + uint16 spell_id; + + if (!(sep->IsNumber(1) && sep->IsNumber(2))) + { + c->Message(0, "Usage: #MemSpell slotid spellid"); + } + else + { + slot = atoi(sep->arg[1]) - 1; + spell_id = atoi(sep->arg[2]); + if (slot > MAX_PP_MEMSPELL || spell_id >= SPDAT_RECORDS) + { + c->Message(0, "Error: #MemSpell: Arguement out of range"); + } + else + { + c->MemSpell(spell_id, slot); + c->Message(0, "Spell slot changed, have fun!"); + } + } +} + +void command_save(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) + c->Message(0, "Error: no target"); + else if (c->GetTarget()->IsClient()) { + if (c->GetTarget()->CastToClient()->Save(2)) + c->Message(0, "%s successfully saved.", c->GetTarget()->GetName()); + else + c->Message(0, "Manual save for %s failed.", c->GetTarget()->GetName()); + } + 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()); + else + c->Message(0, "Manual save for %s failed.", c->GetTarget()->GetName()); + } + else + c->Message(0, "Error: target not a Client/PlayerCorpse"); +} + +void command_showstats(Client *c, const Seperator *sep) +{ + if (c->GetTarget() != 0 ) + c->GetTarget()->ShowStats(c); + else + c->ShowStats(c); +} + +void command_mystats(Client *c, const Seperator *sep) +{ + if (c->GetTarget() && c->GetPet()) { + if (c->GetTarget()->IsPet() && c->GetTarget() == c->GetPet()) + c->GetTarget()->ShowStats(c); + else + c->ShowStats(c); + } + else + c->ShowStats(c); +} + +void command_myskills(Client *c, const Seperator *sep) +{ + c->ShowSkillsWindow(); +} + +void command_bind(Client *c, const Seperator *sep) +{ + if (c->GetTarget() != 0 ) { + if (c->GetTarget()->IsClient()) + c->GetTarget()->CastToClient()->SetBindPoint(); + else + c->Message(0, "Error: target not a Player"); + } else + c->SetBindPoint(); +} + +void command_depop(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0 || !(c->GetTarget()->IsNPC() || c->GetTarget()->IsNPCCorpse())) + c->Message(0, "You must have a NPC target for this command. (maybe you meant #depopzone?)"); + else { + c->Message(0, "Depoping '%s'.", c->GetTarget()->GetName()); + c->GetTarget()->Depop(); + } +} + +void command_depopzone(Client *c, const Seperator *sep) +{ + zone->Depop(); + c->Message(0, "Zone depoped."); +} + +void command_repop(Client *c, const Seperator *sep) +{ + int timearg = 1; + if (sep->arg[1] && strcasecmp(sep->arg[1], "force") == 0) { + timearg++; + + 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(); + } + 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."); + zone->Repop(); + } +} + +void command_spawnstatus(Client *c, const Seperator *sep) +{ + if((sep->arg[1][0] == 'e') | (sep->arg[1][0] == 'E')) + { + // show only enabled spawns + zone->ShowEnabledSpawnStatus(c); + } + else if((sep->arg[1][0] == 'd') | (sep->arg[1][0] == 'D')) + { + // show only disabled spawns + zone->ShowDisabledSpawnStatus(c); + } + else if((sep->arg[1][0] == 'a') | (sep->arg[1][0] == 'A')) + { + // show all spawn staus with no filters + zone->SpawnStatus(c); + } + else if(sep->IsNumber(1)) + { + // show spawn status by spawn2 id + zone->ShowSpawnStatusByID(c, atoi(sep->arg[1])); + } + else if(strcmp(sep->arg[1], "help") == 0) + { + c->Message(0, "Usage: #spawnstatus <[a]ll | [d]isabled | [e]nabled | {Spawn2 ID}>"); + } + else { + zone->SpawnStatus(c); + } +} + +void command_nukebuffs(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) + c->BuffFadeAll(); + else + c->GetTarget()->BuffFadeAll(); +} + +void command_zuwcoords(Client *c, const Seperator *sep) +{ + // modifys and resends zhdr packet + if(sep->arg[1][0]==0) + c->Message(0, "Usage: #zuwcoords "); + else { + zone->newzone_data.underworld = atof(sep->arg[1]); + //float newdata = atof(sep->arg[1]); + //memcpy(&zone->zone_header_data[130], &newdata, sizeof(float)); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + +void command_zunderworld(Client *c, const Seperator *sep) +{ + if(sep->arg[1][0]==0) + c->Message(0, "Usage: #zunderworld "); + else { + zone->newzone_data.underworld = atof(sep->arg[1]); + } +} + +void command_zsafecoords(Client *c, const Seperator *sep) +{ + // modifys and resends zhdr packet + if(sep->arg[3][0]==0) + c->Message(0, "Usage: #zsafecoords "); + else { + zone->newzone_data.safe_x = atof(sep->arg[1]); + zone->newzone_data.safe_y = atof(sep->arg[2]); + zone->newzone_data.safe_z = atof(sep->arg[3]); + //float newdatax = atof(sep->arg[1]); + //float newdatay = atof(sep->arg[2]); + //float newdataz = atof(sep->arg[3]); + //memcpy(&zone->zone_header_data[114], &newdatax, sizeof(float)); + //memcpy(&zone->zone_header_data[118], &newdatay, sizeof(float)); + //memcpy(&zone->zone_header_data[122], &newdataz, sizeof(float)); + //zone->SetSafeCoords(); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(c, outapp); + safe_delete(outapp); + } +} + +void command_freeze(Client *c, const Seperator *sep) +{ + if (c->GetTarget() != 0) + c->GetTarget()->SendAppearancePacket(AT_Anim, ANIM_FREEZE); + else + c->Message(0, "ERROR: Freeze requires a target."); +} + +void command_unfreeze(Client *c, const Seperator *sep) +{ + if (c->GetTarget() != 0) + c->GetTarget()->SendAppearancePacket(AT_Anim, ANIM_STAND); + else + c->Message(0, "ERROR: Unfreeze requires a target."); +} + +void command_pvp(Client *c, const Seperator *sep) +{ + bool state=atobool(sep->arg[1]); + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + + if(sep->arg[1][0] != 0) { + t->SetPVP(state); + c->Message(0, "%s now follows the ways of %s.", t->GetName(), state?"discord":"order"); + } + else + c->Message(0, "Usage: #pvp [on/off]"); +} + +void command_setxp(Client *c, const Seperator *sep) +{ + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + + if (sep->IsNumber(1)) { + if (atoi(sep->arg[1]) > 9999999) + c->Message(0, "Error: Value too high."); + else + t->AddEXP(atoi(sep->arg[1])); + } + else + c->Message(0, "Usage: #setxp number"); +} + +void command_setpvppoints(Client *c, const Seperator *sep) +{ + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + + if (sep->IsNumber(1)) { + if (atoi(sep->arg[1]) > 9999999) + c->Message(0, "Error: Value too high."); + else + { + t->SetPVPPoints(atoi(sep->arg[1])); + t->Save(); + t->SendPVPStats(); + } + } + else + c->Message(0, "Usage: #setpvppoints number"); +} + +void command_name(Client *c, const Seperator *sep) +{ + Client *target; + + if( (strlen(sep->arg[1]) == 0) || (!(c->GetTarget() && c->GetTarget()->IsClient())) ) + c->Message(0, "Usage: #name newname (requires player target)"); + else + { + target = c->GetTarget()->CastToClient(); + char *oldname = strdup(target->GetName()); + if(target->ChangeFirstName(sep->arg[1], c->GetName())) + { + c->Message(0, "Successfully renamed %s to %s", oldname, sep->arg[1]); + // until we get the name packet working right this will work + c->Message(0, "Sending player to char select."); + target->Kick(); + } + else + c->Message(13, "ERROR: Unable to rename %s. Check that the new name '%s' isn't already taken.", oldname, sep->arg[2]); + free(oldname); + } +} + +void command_tempname(Client *c, const Seperator *sep) +{ + Mob *target; + target = c->GetTarget(); + + if(!target) + c->Message(0, "Usage: #tempname newname (requires a target)"); + else if(strlen(sep->arg[1]) > 0) + { + char *oldname = strdup(target->GetName()); + target->TempName(sep->arg[1]); + c->Message(0, "Renamed %s to %s", oldname, sep->arg[1]); + free(oldname); + } + else { + target->TempName(); + c->Message(0, "Restored the original name"); + } +} + +void command_npcspecialattk(Client *c, const Seperator *sep) +{ + if (c->GetTarget()==0 || c->GetTarget()->IsClient() || strlen(sep->arg[1]) <= 0 || strlen(sep->arg[2]) <= 0) + c->Message(0, "Usage: #npcspecialattk *flagchar* *permtag* (Flags are E(nrage) F(lurry) R(ampage) S(ummon), permtag is 1 = True, 0 = False)."); + else { + c->GetTarget()->CastToNPC()->NPCSpecialAttacks(sep->arg[1],atoi(sep->arg[2])); + c->Message(0, "NPC Special Attack set."); + } +} + +void command_kill(Client *c, const Seperator *sep) +{ + if (!c->GetTarget()) { + c->Message(0, "Error: #Kill: No target."); + } + else + if (!c->GetTarget()->IsClient() || c->GetTarget()->CastToClient()->Admin() <= c->Admin()) + c->GetTarget()->Kill(); +} + +void command_haste(Client *c, const Seperator *sep) +{ + // #haste command to set client attack speed. Takes a percentage (100 = twice normal attack speed) + if(sep->arg[1][0] != 0) { + uint16 Haste = atoi(sep->arg[1]); + if(Haste > 85) + Haste = 85; + c->SetExtraHaste(Haste); + // SetAttackTimer must be called to make this take effect, so player needs to change + // the primary weapon. + c->Message(0, "Haste set to %d%% - Need to re-equip primary weapon before it takes effect", Haste); + } + else + c->Message(0, "Usage: #haste [percentage]"); +} + +void command_damage(Client *c, const Seperator *sep) +{ + if (c->GetTarget()==0) + c->Message(0, "Error: #Damage: No Target."); + else if (!sep->IsNumber(1)) { + c->Message(0, "Usage: #damage x"); + } + else { + int32 nkdmg = atoi(sep->arg[1]); + if (nkdmg > 2100000000) + c->Message(0, "Enter a value less then 2,100,000,000."); + else + c->GetTarget()->Damage(c, nkdmg, SPELL_UNKNOWN, HAND_TO_HAND, false); + } +} + +void command_zonespawn(Client *c, const Seperator *sep) +{ + c->Message(0, "This command is not yet implemented."); + return; + +/* this was kept from client.cpp verbatim (it was commented out) */ + // if (target && target->IsNPC()) { + // Message(0, "Inside main if."); + // if (strcasecmp(sep->arg[1], "add")==0) { + // Message(0, "Inside add if."); + // database.DBSpawn(1, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC()); + // } + // else if (strcasecmp(sep->arg[1], "update")==0) { + // database.DBSpawn(2, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC()); + // } + // else if (strcasecmp(sep->arg[1], "remove")==0) { + // if (strcasecmp(sep->arg[2], "all")==0) { + // database.DBSpawn(4, StaticGetZoneName(this->GetPP().current_zone)); + // } + // else { + // if (database.DBSpawn(3, StaticGetZoneName(this->GetPP().current_zone), target->CastToNPC())) { + // Message(0, "#zonespawn: %s removed successfully!", target->GetName()); + // target->CastToNPC()->Death(target, target->GetHP()); + // } + // } + // } + // else + // Message(0, "Error: #dbspawn: Invalid command. (Note: EDIT and REMOVE are NOT in yet.)"); + // if (target->CastToNPC()->GetNPCTypeID() > 0) { + // Message(0, "Spawn is type %i", target->CastToNPC()->GetNPCTypeID()); + // } + // } + // else if(!target || !target->IsNPC()) + // Message(0, "Error: #zonespawn: You must have a NPC targeted!"); + // else + // Message(0, "Usage: #zonespawn [add|edit|remove|remove all]"); +} + +void command_npcspawn(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + uint32 extra = 0; + + if (target && target->IsNPC()) { + if (strcasecmp(sep->arg[1], "create") == 0) { + if (atoi(sep->arg[2])) + { + // Option to try to create the npc_type ID within the range for the current zone (zone_id * 1000) + extra = 1; + } + database.NPCSpawnDB(0, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC(), extra); + c->Message(0, "%s created successfully!", target->GetName()); + } + else if (strcasecmp(sep->arg[1], "add") == 0) { + if (atoi(sep->arg[2])) + { + extra = atoi(sep->arg[2]); + } + else + { + // Respawn Timer default if not set + extra = 1200; + } + database.NPCSpawnDB(1, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC(), extra); + c->Message(0, "%s added successfully!", target->GetName()); + } + else if (strcasecmp(sep->arg[1], "update") == 0) { + database.NPCSpawnDB(2, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC()); + c->Message(0, "%s updated!", target->GetName()); + } + else if (strcasecmp(sep->arg[1], "remove") == 0) { + database.NPCSpawnDB(3, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC()); + c->Message(0, "%s removed successfully from database!", target->GetName()); + target->Depop(false); + } + else if (strcasecmp(sep->arg[1], "delete") == 0) { + database.NPCSpawnDB(4, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC()); + c->Message(0, "%s deleted from database!", target->GetName()); + target->Depop(false); + } + else { + c->Message(0, "Error: #npcspawn: Invalid command."); + c->Message(0, "Usage: #npcspawn [create|add|update|remove|delete]"); + } + } + else + c->Message(0, "Error: #npcspawn: You must have a NPC targeted!"); +} + +void command_spawnfix(Client *c, const Seperator *sep) { + Mob *t = c->GetTarget(); + if (!t || !t->IsNPC()) + c->Message(0, "Error: #spawnfix: Need an NPC target."); + else { + Spawn2* s2 = t->CastToNPC()->respawn2; + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + 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); + } + } +} + +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()); +} + +void command_goto(Client *c, const Seperator *sep) +{ + // goto function + if (sep->arg[1][0] == '\0' && c->GetTarget()) + c->MovePC(zone->GetZoneID(), zone->GetInstanceID(), c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ(), c->GetTarget()->GetHeading()); + else if (!(sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3))) + c->Message(0, "Usage: #goto [x y z]"); + else + c->MovePC(zone->GetZoneID(), zone->GetInstanceID(), atof(sep->arg[1]), atof(sep->arg[2]), atof(sep->arg[3]), 0.0f); +} + +void command_iteminfo(Client *c, const Seperator *sep) +{ + const ItemInst* inst = c->GetInv()[SLOT_CURSOR]; + + if (!inst) + c->Message(13, "Error: You need an item on your cursor for this command"); + else { + const Item_Struct* item = inst->GetItem(); + c->Message(0, "ID: %i Name: %s", item->ID, item->Name); + c->Message(0, " Lore: %s ND: %i NS: %i Type: %i", (item->LoreFlag) ? "true":"false", item->NoDrop, item->NoRent, item->ItemClass); + c->Message(0, " IDF: %s Size: %i Weight: %i icon_id: %i Price: %i", item->IDFile, item->Size, item->Weight, item->Icon, item->Price); + if (c->Admin() >= 200) + c->Message(0, "MinStatus: %i", database.GetItemStatus(item->ID)); + if (item->ItemClass==ItemClassBook) + c->Message(0, " This item is a Book: %s", item->Filename); + else if (item->ItemClass==ItemClassContainer) + c->Message(0, " This item is a container with %i slots", item->BagSlots); + else { + c->Message(0, " equipableSlots: %u equipable Classes: %u", item->Slots, item->Classes); + c->Message(0, " Magic: %i SpellID: %i Proc Level: %i DBCharges: %i CurCharges: %i", item->Magic, item->Click.Effect, item->Click.Level, item->MaxCharges, inst->GetCharges()); + c->Message(0, " EffectType: 0x%02x CastTime: %.2f", (uint8) item->Click.Type, (double) item->CastTime/1000); + c->Message(0, " Material: 0x%02x Color: 0x%08x Skill: %i", item->Material, item->Color, item->ItemType); + c->Message(0, " Required level: %i Required skill: %i Recommended level:%i", item->ReqLevel, item->RecSkill, item->RecLevel); + c->Message(0, " Skill mod: %i percent: %i", item->SkillModType, item->SkillModValue); + c->Message(0, " BaneRace: %i BaneBody: %i BaneDMG: %i", item->BaneDmgRace, item->BaneDmgBody, item->BaneDmgAmt); + } + } +} + +void command_uptime(Client *c, const Seperator *sep) +{ + if (!worldserver.Connected()) + c->Message(0, "Error: World server disconnected"); + else + { + ServerPacket* pack = new ServerPacket(ServerOP_Uptime, sizeof(ServerUptime_Struct)); + ServerUptime_Struct* sus = (ServerUptime_Struct*) pack->pBuffer; + strcpy(sus->adminname, c->GetName()); + if (sep->IsNumber(1) && atoi(sep->arg[1]) > 0) + sus->zoneserverid = atoi(sep->arg[1]); + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +void command_flag(Client *c, const Seperator *sep) +{ + if(sep->arg[2][0] == 0) { + c->UpdateAdmin(); + c->Message(0, "Refreshed your admin flag from DB."); + } + else if (!sep->IsNumber(1) || atoi(sep->arg[1]) < -2 || atoi(sep->arg[1]) > 255 || strlen(sep->arg[2]) == 0) + c->Message(0, "Usage: #flag [status] [acctname]"); + + else if (c->Admin() < commandChangeFlags) { +//this check makes banning players by less than this level +//impossible, but i'll leave it in anyways + c->Message(0, "You may only refresh your own flag, doing so now."); + c->UpdateAdmin(); + } + else { + if (atoi(sep->arg[1]) > c->Admin()) + c->Message(0, "You cannot set people's status to higher than your own"); + else if (atoi(sep->arg[1]) < 0 && c->Admin() < commandBanPlayers) + c->Message(0, "You have too low of status to suspend/ban"); + else if (!database.SetAccountStatus(sep->argplus[2], atoi(sep->arg[1]))) + c->Message(0, "Unable to set GM Flag."); + else { + c->Message(0, "Set GM Flag on account."); + ServerPacket* pack = new ServerPacket(ServerOP_FlagUpdate, 6); + *((uint32*) pack->pBuffer) = database.GetAccountIDByName(sep->argplus[2]); + *((int16*) &pack->pBuffer[4]) = atoi(sep->arg[1]); + worldserver.SendPacket(pack); + delete pack; + } + } +} + +void command_time(Client *c, const Seperator *sep) +{ + char timeMessage[255]; + int minutes=0; + if(sep->IsNumber(1)) { + if(sep->IsNumber(2)) { + minutes=atoi(sep->arg[2]); + } + c->Message(13, "Setting world time to %s:%i (Timezone: 0)...", sep->arg[1], minutes); + zone->SetTime(atoi(sep->arg[1])+1, minutes); + } + else { + c->Message(13, "To set the Time: #time HH [MM]"); + TimeOfDay_Struct eqTime; + zone->zone_time.getEQTimeOfDay( time(0), &eqTime); + sprintf(timeMessage,"%02d:%s%d %s (Timezone: %ih %im)", + ((eqTime.hour - 1) % 12) == 0 ? 12 : ((eqTime.hour - 1) % 12), + (eqTime.minute < 10) ? "0" : "", + eqTime.minute, + (eqTime.hour >= 13) ? "pm" : "am", + zone->zone_time.getEQTimeZoneHr(), + zone->zone_time.getEQTimeZoneMin() + ); + c->Message(13, "It is now %s.", timeMessage); +#if EQDEBUG >= 11 + LogFile->write(EQEMuLog::Debug,"Recieved timeMessage:%s", timeMessage); +#endif + } +} + +void command_guild(Client *c, const Seperator *sep) +{ + int admin=c->Admin(); + Mob *target=c->GetTarget(); + + if (strcasecmp(sep->arg[1], "help") == 0) { + /* + c->Message(0, "Guild commands:"); + c->Message(0, " #guild status [name] - shows guild and rank of target"); + c->Message(0, " #guild info guildnum - shows info/current structure"); + c->Message(0, " #guild invite [charname]"); + c->Message(0, " #guild remove [charname]"); + c->Message(0, " #guild promote rank [charname]"); + c->Message(0, " #guild demote rank [charname]"); + c->Message(0, " /guildmotd [newmotd] (use 'none' to clear)"); + c->Message(0, " #guild edit rank title newtitle"); + c->Message(0, " #guild edit rank permission 0/1"); + c->Message(0, " #guild leader newleader (they must be rank0)"); + */ + c->Message(0, "GM Guild commands:"); + c->Message(0, " #guild list - lists all guilds on the server"); + c->Message(0, " #guild create {guildleader charname or CharID} guildname"); + c->Message(0, " #guild delete guildID"); + c->Message(0, " #guild rename guildID newname"); + c->Message(0, " #guild set charname guildID (0=no guild)"); + c->Message(0, " #guild setrank charname rank"); + //c->Message(0, " #guild gmedit guilddbid rank title newtitle"); + //c->Message(0, " #guild gmedit guilddbid rank permission 0/1"); + c->Message(0, " #guild setleader guildID {guildleader charname or CharID}"); + //c->Message(0, " #guild setdoor guildEQID"); + } + else if (strcasecmp(sep->arg[1], "status") == 0 || strcasecmp(sep->arg[1], "stat") == 0) { + Client* client = 0; + if (sep->arg[2][0] != 0) + client = entity_list.GetClientByName(sep->argplus[2]); + else if (target != 0 && target->IsClient()) + client = target->CastToClient(); + if (client == 0) + c->Message(0, "You must target someone or specify a character name"); + else if ((client->Admin() >= minStatusToEditOtherGuilds && admin < minStatusToEditOtherGuilds) && client->GuildID() != c->GuildID()) // no peeping for GMs, make sure tell message stays the same + c->Message(0, "You must target someone or specify a character name."); + else { + if (client->IsInAGuild()) + c->Message(0, "%s is not in a guild.", client->GetName()); + else if (guild_mgr.IsGuildLeader(client->GuildID(), client->CharacterID())) + c->Message(0, "%s is the leader of <%s> rank: %s", client->GetName(), guild_mgr.GetGuildName(client->GuildID()), guild_mgr.GetRankName(client->GuildID(), client->GuildRank())); + else + c->Message(0, "%s is a member of <%s> rank: %s", client->GetName(), guild_mgr.GetGuildName(client->GuildID()), guild_mgr.GetRankName(client->GuildID(), client->GuildRank())); + } + } + else if (strcasecmp(sep->arg[1], "info") == 0) { + if (sep->arg[2][0] == 0 && c->IsInAGuild()) { + if (admin >= minStatusToEditOtherGuilds) + c->Message(0, "Usage: #guildinfo guild_id"); + else + c->Message(0, "You're not in a guild"); + } + else { + uint32 tmp = GUILD_NONE; + if (sep->arg[2][0] == 0) + tmp = c->GuildID(); + else if (admin >= minStatusToEditOtherGuilds) + tmp = atoi(sep->arg[2]); + + if(tmp != GUILD_NONE) + guild_mgr.DescribeGuild(c, tmp); + } + } + /* + else if (strcasecmp(sep->arg[1], "edit") == 0) { + if (c->GuildDBID() == 0) + c->Message(0, "You arent in a guild!"); + else if (!sep->IsNumber(2)) + c->Message(0, "Error: invalid rank #."); + else if (atoi(sep->arg[2]) < 0 || atoi(sep->arg[2]) > GUILD_MAX_RANK) + c->Message(0, "Error: invalid rank #."); + else if (!c->GuildRank() == 0) + c->Message(0, "You must be rank %s to use edit.", guilds[c->GuildEQID()].rank[0].rankname); + else if (!worldserver.Connected()) + c->Message(0, "Error: World server dirconnected"); + else { + if (!helper_guild_edit(c, c->GuildDBID(), c->GuildEQID(), atoi(sep->arg[2]), sep->arg[3], sep->argplus[4])) { + c->Message(0, " #guild edit rank title newtitle"); + c->Message(0, " #guild edit rank permission 0/1"); + } + else { + ServerPacket* pack = new ServerPacket(ServerOP_RefreshGuild, 5); + int32 geqid=c->GuildEQID(); + memcpy(pack->pBuffer, &geqid, 4); + worldserver.SendPacket(pack); + safe_delete(pack); + } + } + } + else if (strcasecmp(sep->arg[1], "gmedit") == 0 && admin >= 100) { + if (!sep->IsNumber(2)) + c->Message(0, "Error: invalid guilddbid."); + else if (!sep->IsNumber(3)) + c->Message(0, "Error: invalid rank #."); + else if (atoi(sep->arg[3]) < 0 || atoi(sep->arg[3]) > GUILD_MAX_RANK) + c->Message(0, "Error: invalid rank #."); + else if (!worldserver.Connected()) + c->Message(0, "Error: World server dirconnected"); + else { + uint32 eqid = database.GetGuildEQID(atoi(sep->arg[2])); + if (eqid == GUILD_NONE) + c->Message(0, "Error: Guild not found"); + else if (!helper_guild_edit(c, atoi(sep->arg[2]), eqid, atoi(sep->arg[3]), sep->arg[4], sep->argplus[5])) { + c->Message(0, " #guild gmedit guilddbid rank title newtitle"); + c->Message(0, " #guild gmedit guilddbid rank permission 0/1"); + } + else { + ServerPacket* pack = new ServerPacket(ServerOP_RefreshGuild, 5); + memcpy(pack->pBuffer, &eqid, 4); + worldserver.SendPacket(pack); + safe_delete(pack); + } + } + } + */ + else if (strcasecmp(sep->arg[1], "set") == 0) { + if (!sep->IsNumber(3)) + c->Message(0, "Usage: #guild set charname guildgbid (0 = clear guildtag)"); + else { + uint32 guild_id = atoi(sep->arg[3]); + + if(guild_id == 0) + guild_id = GUILD_NONE; + else if(!guild_mgr.GuildExists(guild_id)) { + c->Message(13, "Guild %d does not exist.", guild_id); + return; + } + + uint32 charid = database.GetCharacterID(sep->arg[2]); + if(charid == 0) { + c->Message(13, "Unable to find character '%s'", charid); + return; + } + + //we could do the checking we need for guild_mgr.CheckGMStatus, but im lazy right now + if(admin < minStatusToEditOtherGuilds) { + c->Message(13, "Access denied."); + return; + } + + if(guild_id == GUILD_NONE) { + _log(GUILDS__ACTIONS, "%s: Removing %s (%d) from guild with GM command.", c->GetName(), + sep->arg[2], charid); + } else { + _log(GUILDS__ACTIONS, "%s: Putting %s (%d) into guild %s (%d) with GM command.", c->GetName(), + sep->arg[2], charid, + guild_mgr.GetGuildName(guild_id), guild_id); + } + + if(!guild_mgr.SetGuild(charid, guild_id, GUILD_MEMBER)) { + c->Message(13, "Error putting '%s' into guild %d", sep->arg[2], guild_id); + } else { + c->Message(0, "%s has been put into guild %d", sep->arg[2], guild_id); + } + } + } + /*else if (strcasecmp(sep->arg[1], "setdoor") == 0 && admin >= minStatusToEditOtherGuilds) { + + if (!sep->IsNumber(2)) + c->Message(0, "Usage: #guild setdoor guildEQid (0 = delete guilddoor)"); + else { +// guild doors + if((!guilds[atoi(sep->arg[2])].databaseID) && (atoi(sep->arg[2])!=0) ) + { + + c->Message(0, "These is no guild with this guildEQid"); + } + else { + c->SetIsSettingGuildDoor(true); + c->Message(0, "Click on a door you want to become a guilddoor"); + c->SetSetGuildDoorID(atoi(sep->arg[2])); + } + } + }*/ + else if (strcasecmp(sep->arg[1], "setrank") == 0) { + int rank = atoi(sep->arg[3]); + if (!sep->IsNumber(3)) + c->Message(0, "Usage: #guild setrank charname rank"); + else if (rank < 0 || rank > GUILD_MAX_RANK) + c->Message(0, "Error: invalid rank #."); + else { + uint32 charid = database.GetCharacterID(sep->arg[2]); + if(charid == 0) { + c->Message(13, "Unable to find character '%s'", charid); + return; + } + + //we could do the checking we need for guild_mgr.CheckGMStatus, but im lazy right now + if(admin < minStatusToEditOtherGuilds) { + c->Message(13, "Access denied."); + return; + } + + _log(GUILDS__ACTIONS, "%s: Setting %s (%d)'s guild rank to %d with GM command.", c->GetName(), + sep->arg[2], charid, rank); + + if(!guild_mgr.SetGuildRank(charid, rank)) + c->Message(13, "Error while setting rank %d on '%s'.", rank, sep->arg[2]); + else + c->Message(0, "%s has been set to rank %d", sep->arg[2], rank); + } + } + else if (strcasecmp(sep->arg[1], "create") == 0) { + if (sep->arg[3][0] == 0) + c->Message(0, "Usage: #guild create {guildleader charname or CharID} guild name"); + else if (!worldserver.Connected()) + c->Message(0, "Error: World server dirconnected"); + else { + uint32 leader = 0; + if (sep->IsNumber(2)) { + leader = atoi(sep->arg[2]); + } else if((leader=database.GetCharacterID(sep->arg[2])) != 0) { + //got it from the db.. + } else { + c->Message(13, "Unable to find char '%s'", sep->arg[2]); + return; + } + if (leader == 0) { + c->Message(0, "Guild leader not found."); + return; + } + + uint32 tmp = guild_mgr.FindGuildByLeader(leader); + if (tmp != GUILD_NONE) { + c->Message(0, "Error: %s already is the leader of DB# %i '%s'.", sep->arg[2], tmp, guild_mgr.GetGuildName(tmp)); + } + else { + + if(admin < minStatusToEditOtherGuilds) { + c->Message(13, "Access denied."); + return; + } + + uint32 id = guild_mgr.CreateGuild(sep->argplus[3], leader); + + _log(GUILDS__ACTIONS, "%s: Creating guild %s with leader %d with GM command. It was given id %lu.", c->GetName(), + sep->argplus[3], leader, (unsigned long)id); + + if (id == GUILD_NONE) + c->Message(0, "Guild creation failed."); + else { + c->Message(0, "Guild created: Leader: %i, number %i: %s", leader, id, sep->argplus[3]); + + if(!guild_mgr.SetGuild(leader, id, GUILD_LEADER)) + c->Message(0, "Unable to set guild leader's guild in the database. Your going to have to run #guild set"); + } + + } + } + } + else if (strcasecmp(sep->arg[1], "delete") == 0) { + if (!sep->IsNumber(2)) + c->Message(0, "Usage: #guild delete guildID"); + else if (!worldserver.Connected()) + c->Message(0, "Error: World server dirconnected"); + else { + uint32 id = atoi(sep->arg[2]); + + if(!guild_mgr.GuildExists(id)) { + c->Message(0, "Guild %d does not exist!", id); + return; + } + + if(admin < minStatusToEditOtherGuilds) { + //this person is not allowed to just edit any guild, check this guild's min status. + if(c->GuildID() != id) { + c->Message(13, "Access denied to edit other people's guilds"); + return; + } else if(!guild_mgr.CheckGMStatus(id, admin)) { + c->Message(13, "Access denied to edit your guild with GM commands."); + return; + } + } + + _log(GUILDS__ACTIONS, "%s: Deleting guild %s (%d) with GM command.", c->GetName(), + guild_mgr.GetGuildName(id), id); + + if (!guild_mgr.DeleteGuild(id)) + c->Message(0, "Guild delete failed."); + else { + c->Message(0, "Guild %d deleted.", id); + } + } + } + else if (strcasecmp(sep->arg[1], "rename") == 0) { + if ((!sep->IsNumber(2)) || sep->arg[3][0] == 0) + c->Message(0, "Usage: #guild rename guildID newname"); + else if (!worldserver.Connected()) + c->Message(0, "Error: World server dirconnected"); + else { + uint32 id = atoi(sep->arg[2]); + + if(!guild_mgr.GuildExists(id)) { + c->Message(0, "Guild %d does not exist!", id); + return; + } + + if(admin < minStatusToEditOtherGuilds) { + //this person is not allowed to just edit any guild, check this guild's min status. + if(c->GuildID() != id) { + c->Message(13, "Access denied to edit other people's guilds"); + return; + } else if(!guild_mgr.CheckGMStatus(id, admin)) { + c->Message(13, "Access denied to edit your guild with GM commands."); + return; + } + } + + _log(GUILDS__ACTIONS, "%s: Renaming guild %s (%d) to '%s' with GM command.", c->GetName(), + guild_mgr.GetGuildName(id), id, sep->argplus[3]); + + if (!guild_mgr.RenameGuild(id, sep->argplus[3])) + c->Message(0, "Guild rename failed."); + else { + c->Message(0, "Guild %d renamed to %s", id, sep->argplus[3]); + } + } + } + else if (strcasecmp(sep->arg[1], "setleader") == 0) { + if (sep->arg[3][0] == 0 || !sep->IsNumber(2)) + c->Message(0, "Usage: #guild setleader guild_id {guildleader charname or CharID}"); + else if (!worldserver.Connected()) + c->Message(0, "Error: World server dirconnected"); + else { + uint32 leader = 0; + if (sep->IsNumber(2)) { + leader = atoi(sep->arg[2]); + } else if((leader=database.GetCharacterID(sep->arg[2])) != 0) { + //got it from the db.. + } else { + c->Message(13, "Unable to find char '%s'", sep->arg[2]); + return; + } + + uint32 tmpdb = guild_mgr.FindGuildByLeader(leader); + if (leader == 0) + c->Message(0, "New leader not found."); + else if (tmpdb != 0) { + c->Message(0, "Error: %s already is the leader of guild # %i", sep->arg[2], tmpdb); + } + else { + uint32 id = atoi(sep->arg[2]); + + if(!guild_mgr.GuildExists(id)) { + c->Message(0, "Guild %d does not exist!", id); + return; + } + + if(admin < minStatusToEditOtherGuilds) { + //this person is not allowed to just edit any guild, check this guild's min status. + if(c->GuildID() != id) { + c->Message(13, "Access denied to edit other people's guilds"); + return; + } else if(!guild_mgr.CheckGMStatus(id, admin)) { + c->Message(13, "Access denied to edit your guild with GM commands."); + return; + } + } + + _log(GUILDS__ACTIONS, "%s: Setting leader of guild %s (%d) to %d with GM command.", c->GetName(), + guild_mgr.GetGuildName(id), id, leader); + + if(!guild_mgr.SetGuildLeader(id, leader)) + c->Message(0, "Guild leader change failed."); + else { + c->Message(0, "Guild leader changed: guild # %d, Leader: %s", id, sep->argplus[3]); + } + } + } + } + else if (strcasecmp(sep->arg[1], "list") == 0) { + if(admin < minStatusToEditOtherGuilds) { + c->Message(13, "Access denied."); + return; + } + guild_mgr.ListGuilds(c); + } + else { + c->Message(0, "Unknown guild command, try #guild help"); + } +} +/* +bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char* what, const char* value) { + struct GuildRankLevel_Struct grl; + strcpy(grl.rankname, guild_mgr.GetRankName(eqid, rank)); + grl.demote = guilds[eqid].rank[rank].demote; + grl.heargu = guilds[eqid].rank[rank].heargu; + grl.invite = guilds[eqid].rank[rank].invite; + grl.motd = guilds[eqid].rank[rank].motd; + grl.promote = guilds[eqid].rank[rank].promote; + grl.remove = guilds[eqid].rank[rank].remove; + grl.speakgu = guilds[eqid].rank[rank].speakgu; + grl.warpeace = guilds[eqid].rank[rank].warpeace; + + if (strcasecmp(what, "title") == 0) { + if (strlen(value) > 100) + c->Message(0, "Error: Title has a maxium length of 100 characters."); + else + strcpy(grl.rankname, value); + } + else if (rank == 0) + c->Message(0, "Error: Rank 0's permissions can not be changed."); + else { + if (!(strlen(value) == 1 && (value[0] == '0' || value[0] == '1'))) + + return false; + if (strcasecmp(what, "demote") == 0) + grl.demote = (value[0] == '1'); + else if (strcasecmp(what, "heargu") == 0) + grl.heargu = (value[0] == '1'); + else if (strcasecmp(what, "invite") == 0) + grl.invite = (value[0] == '1'); + else if (strcasecmp(what, "motd") == 0) + grl.motd = (value[0] == '1'); + else if (strcasecmp(what, "promote") == 0) + grl.promote = (value[0] == '1'); + else if (strcasecmp(what, "remove") == 0) + + grl.remove = (value[0] == '1'); + else if (strcasecmp(what, "speakgu") == 0) + grl.speakgu = (value[0] == '1'); + else if (strcasecmp(what, "warpeace") == 0) + grl.warpeace = (value[0] == '1'); + else + c->Message(0, "Error: Permission name not recognized."); + } + if (!database.EditGuild(dbid, rank, &grl)) + c->Message(0, "Error: database.EditGuild() failed"); + return true; +}*/ + +void command_zonestatus(Client *c, const Seperator *sep) +{ + if (!worldserver.Connected()) + c->Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_ZoneStatus, strlen(c->GetName())+2); + memset(pack->pBuffer, (uint8) c->Admin(), 1); + strcpy((char *) &pack->pBuffer[1], c->GetName()); + worldserver.SendPacket(pack); + delete pack; + } +} + +void command_manaburn(Client *c, const Seperator *sep) +{ + Mob* target=c->GetTarget(); + + if (c->GetTarget() == 0) + c->Message(0, "#Manaburn needs a target."); + else { + int cur_level=c->GetAA(MANA_BURN);//ManaBurn ID + if (c->DistNoRootNoZ(*target) > 200) + c->Message(0,"You are too far away from your target."); + else { + if(cur_level == 1) { + if(c->IsAttackAllowed(target)) + { + c->SetMana(0); + int nukedmg=(c->GetMana())*2; + if (nukedmg>0) + { + target->Damage(c, nukedmg, 2751, ABJURE/*hackish*/); + c->Message(4,"You unleash an enormous blast of magical energies."); + } + LogFile->write(EQEMuLog::Normal,"Manaburn request from %s, damage: %d", c->GetName(), nukedmg); + } + } + else + c->Message(0, "You have not learned this skill."); + } + } +} + +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)) + c->Message(0, "Usage: #DoAnim [number]"); + else + if (c->Admin() >= commandDoAnimOthers) + if (c->GetTarget() == 0) + c->Message(0, "Error: You need a target."); + else + c->GetTarget()->DoAnim(atoi(sep->arg[1]),atoi(sep->arg[2])); + else + c->DoAnim(atoi(sep->arg[1]),atoi(sep->arg[2])); +} + +void command_randomfeatures(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + if (!target) + 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"); + } + else + c->Message(0,"This command requires a Playable Race as the Target"); + } +} + +void command_face(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + if (!sep->IsNumber(1)) + c->Message(0,"Usage: #face [number of face]"); + else if (!target) + c->Message(0,"Error: this command requires a target"); + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = atoi(sep->arg[1]); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails); + + c->Message(0,"Face = %i", atoi(sep->arg[1])); + } +} + +void command_details(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + if (!sep->IsNumber(1)) + c->Message(0,"Usage: #details [number of drakkin detail]"); + else if (!target) + c->Message(0,"Error: this command requires a target"); + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = atoi(sep->arg[1]); + + target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails); + + c->Message(0,"Details = %i", atoi(sep->arg[1])); + } +} + +void command_heritage(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + if (!sep->IsNumber(1)) + c->Message(0,"Usage: #heritage [number of Drakkin heritage]"); + else if (!target) + c->Message(0,"Error: this command requires a target"); + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = atoi(sep->arg[1]); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails); + + c->Message(0,"Heritage = %i", atoi(sep->arg[1])); + } +} + +void command_tattoo(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + if (!sep->IsNumber(1)) + c->Message(0,"Usage: #tattoo [number of Drakkin tattoo]"); + else if (!target) + c->Message(0,"Error: this command requires a target"); + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = atoi(sep->arg[1]); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails); + + c->Message(0,"Tattoo = %i", atoi(sep->arg[1])); + } +} + +void command_helm(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + if (!sep->IsNumber(1)) + c->Message(0,"Usage: #helm [number of helm texture]"); + else if (!target) + c->Message(0,"Error: this command requires a target"); + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = atoi(sep->arg[1]); + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails); + + c->Message(0,"Helm = %i", atoi(sep->arg[1])); + } +} + +void command_hair(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + if (!sep->IsNumber(1)) + c->Message(0,"Usage: #hair [number of hair style]"); + else if (!target) + c->Message(0,"Error: this command requires a target"); + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = atoi(sep->arg[1]); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails); + + c->Message(0,"Hair = %i", atoi(sep->arg[1])); + } +} + +void command_haircolor(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + if (!sep->IsNumber(1)) + c->Message(0,"Usage: #haircolor [number of hair color]"); + else if (!target) + c->Message(0,"Error: this command requires a target"); + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = atoi(sep->arg[1]); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails); + + c->Message(0,"Hair Color = %i", atoi(sep->arg[1])); + } +} + +void command_beard(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + if (!sep->IsNumber(1)) + c->Message(0,"Usage: #beard [number of beard style]"); + else if (!target) + c->Message(0,"Error: this command requires a target"); + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = target->GetBeardColor(); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = atoi(sep->arg[1]); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails); + + c->Message(0,"Beard = %i", atoi(sep->arg[1])); + } +} + +void command_beardcolor(Client *c, const Seperator *sep) +{ + Mob *target=c->GetTarget(); + if (!sep->IsNumber(1)) + c->Message(0,"Usage: #beardcolor [number of beard color]"); + else if (!target) + c->Message(0,"Error: this command requires a target"); + else { + uint16 Race = target->GetRace(); + uint8 Gender = target->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = target->GetHairColor(); + uint8 BeardColor = atoi(sep->arg[1]); + uint8 EyeColor1 = target->GetEyeColor1(); + uint8 EyeColor2 = target->GetEyeColor2(); + uint8 HairStyle = target->GetHairStyle(); + uint8 LuclinFace = target->GetLuclinFace(); + uint8 Beard = target->GetBeard(); + uint32 DrakkinHeritage = target->GetDrakkinHeritage(); + uint32 DrakkinTattoo = target->GetDrakkinTattoo(); + uint32 DrakkinDetails = target->GetDrakkinDetails(); + + target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails); + + c->Message(0,"Beard Color = %i", atoi(sep->arg[1])); + } +} + +void command_scribespells(Client *c, const Seperator *sep) +{ + uint8 max_level, min_level; + uint16 book_slot, curspell, count; + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) + t=c->GetTarget()->CastToClient(); + + if(!sep->arg[1][0]) + { + c->Message(0, "FORMAT: #scribespells "); + return; + } + + max_level = (uint8)atoi(sep->arg[1]); + if (!c->GetGM() && max_level > RuleI(Character, MaxLevel)) + max_level = RuleI(Character, MaxLevel); //default to Character:MaxLevel if we're not a GM & it's higher than the max level + min_level = sep->arg[2][0] ? (uint8)atoi(sep->arg[2]) : 1; //default to 1 if there isn't a 2nd argument + if (!c->GetGM() && min_level > RuleI(Character, MaxLevel)) + min_level = RuleI(Character, MaxLevel); //default to Character:MaxLevel if we're not a GM & it's higher than the max level + + + if(max_level < 1 || min_level < 1) + { + c->Message(0, "ERROR: Level must be greater than 1."); + return; + } + if (min_level > max_level) { + c->Message(0, "Error: Min Level must be less than or equal to Max Level."); + return; + } + + t->Message(0, "Scribing spells to spellbook."); + if(t != c) + c->Message(0, "Scribing spells for %s.", t->GetName()); + LogFile->write(EQEMuLog::Normal, "Scribe spells request for %s from %s, levels: %u -> %u", t->GetName(), c->GetName(), min_level, max_level); + + for(curspell = 0, book_slot = t->GetNextAvailableSpellBookSlot(), count = 0; curspell < SPDAT_RECORDS && book_slot < MAX_PP_SPELLBOOK; curspell++, book_slot = t->GetNextAvailableSpellBookSlot(book_slot)) + { + if + ( + spells[curspell].classes[WARRIOR] != 0 && // check if spell exists + spells[curspell].classes[t->GetPP().class_-1] <= max_level && //maximum level + spells[curspell].classes[t->GetPP().class_-1] >= min_level && //minimum level + spells[curspell].skill != 52 + ) + { + if (book_slot == -1) { //no more book slots + t->Message(13, "Unable to scribe spell %s (%u) to spellbook: no more spell book slots available.", spells[curspell].name, curspell); + if (t != c) + c->Message(13, "Error scribing spells: %s ran out of spell book slots on spell %s (%u)", t->GetName(), spells[curspell].name, curspell); + break; + } + if(!IsDiscipline(curspell) && !t->HasSpellScribed(curspell)) { //isn't a discipline & we don't already have it scribed + t->ScribeSpell(curspell, book_slot); + count++; + } + } + } + + if (count > 0) { + t->Message(0, "Successfully scribed %u spells.", count); + if (t != c) + c->Message(0, "Successfully scribed %u spells for %s.", count, t->GetName()); + } else { + t->Message(0, "No spells scribed."); + if (t != c) + c->Message(0, "No spells scribed for %s.", t->GetName()); + } +} + +void command_scribespell(Client *c, const Seperator *sep) { + uint16 spell_id = 0; + uint16 book_slot = -1; + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) + t=c->GetTarget()->CastToClient(); + + if(!sep->arg[1][0]) { + c->Message(0, "FORMAT: #scribespell "); + return; + } + + spell_id = atoi(sep->arg[1]); + + if(IsValidSpell(spell_id)) { + t->Message(0, "Scribing spell: %s (%i) to spellbook.", spells[spell_id].name, spell_id); + + if(t != c) + c->Message(0, "Scribing spell: %s (%i) for %s.", spells[spell_id].name, spell_id, t->GetName()); + + LogFile->write(EQEMuLog::Normal, "Scribe spell: %s (%i) request for %s from %s.", spells[spell_id].name, spell_id, t->GetName(), c->GetName()); + + if (spells[spell_id].classes[WARRIOR] != 0 && spells[spell_id].skill != 52 && spells[spell_id].classes[t->GetPP().class_ - 1] > 0 && !IsDiscipline(spell_id)) { + book_slot = t->GetNextAvailableSpellBookSlot(); + + if(book_slot >= 0 && t->FindSpellBookSlotBySpellID(spell_id) < 0) + t->ScribeSpell(spell_id, book_slot); + else { + t->Message(13, "Unable to scribe spell: %s (%i) to your spellbook.", spells[spell_id].name, spell_id); + + if(t != c) + c->Message(13, "Unable to scribe spell: %s (%i) for %s.", spells[spell_id].name, spell_id, t->GetName()); + } + } + else + c->Message(13, "Your target can not scribe this spell."); + } + else + c->Message(13, "Spell ID: %i is an unknown spell and cannot be scribed.", spell_id); +} + +void command_unscribespell(Client *c, const Seperator *sep) { + uint16 spell_id = 0; + uint16 book_slot = -1; + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) + t=c->GetTarget()->CastToClient(); + + if(!sep->arg[1][0]) { + c->Message(0, "FORMAT: #unscribespell "); + return; + } + + spell_id = atoi(sep->arg[1]); + + if(IsValidSpell(spell_id)) { + book_slot = t->FindSpellBookSlotBySpellID(spell_id); + + if(book_slot >= 0) { + t->UnscribeSpell(book_slot); + + t->Message(0, "Unscribing spell: %s (%i) from spellbook.", spells[spell_id].name, spell_id); + + if(t != c) + c->Message(0, "Unscribing spell: %s (%i) for %s.", spells[spell_id].name, spell_id, t->GetName()); + + LogFile->write(EQEMuLog::Normal, "Unscribe spell: %s (%i) request for %s from %s.", spells[spell_id].name, spell_id, t->GetName(), c->GetName()); + } + else { + t->Message(13, "Unable to unscribe spell: %s (%i) from your spellbook. This spell is not scribed.", spells[spell_id].name, spell_id); + + if(t != c) + c->Message(13, "Unable to unscribe spell: %s (%i) for %s due to spell not scribed.", spells[spell_id].name, spell_id, t->GetName()); + } + } +} + +void command_unscribespells(Client *c, const Seperator *sep) +{ + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) + t=c->GetTarget()->CastToClient(); + + t->UnscribeSpellAll(); +} + +void command_wpinfo(Client *c, const Seperator *sep) +{ + Mob *t=c->GetTarget(); + + if (t == NULL || !t->IsNPC()) { + c->Message(0,"You must target an NPC to use this."); + return; + } + + NPC *n = t->CastToNPC(); + n->DisplayWaypointInfo(c); +} + +void command_wpadd(Client *c, const Seperator *sep) +{ + int type1=0, + type2=0, + pause=0, + heading=-1; // Defaults for a new grid + + Mob *t=c->GetTarget(); + if (t && t->IsNPC()) + { + Spawn2* s2info = t->CastToNPC()->respawn2; + + if(s2info == NULL) // Can't figure out where this mob's spawn came from... maybe a dynamic mob created by #spawn + { + c->Message(0,"#wpadd FAILED -- Can't determine which spawn record in the database this mob came from!"); + return; + } + + if (sep->arg[1][0]) + { + if (atoi(sep->arg[1]) >= 0) + pause=atoi(sep->arg[1]); + else + { + c->Message(0,"Usage: #wpadd [pause] [-h]"); + return; + } + } + if (strcmp("-h",sep->arg[2]) == 0) + heading = c->GetHeading(); + uint32 tmp_grid = database.AddWPForSpawn(c, s2info->GetID(), c->GetX(),c->GetY(),c->GetZ(), pause, type1, type2, zone->GetZoneID(), heading); + if (tmp_grid) + t->CastToNPC()->SetGrid(tmp_grid); + + t->CastToNPC()->AssignWaypoints(t->CastToNPC()->GetGrid()); + c->Message(0,"Waypoint added. Use #wpinfo to see waypoints for this NPC (may need to #repop first)."); + } + else + c->Message(0,"You must target an NPC to use this."); +} + + +void command_interrupt(Client *c, const Seperator *sep) +{ + uint16 ci_message=0x01b7, ci_color=0x0121; + + if(sep->arg[1][0]) + ci_message=atoi(sep->arg[1]); + if(sep->arg[2][0]) + ci_color=atoi(sep->arg[2]); + + 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)) + c->Message(0, "Usage: #summonitem [item id] [charges], charges are optional"); + else { + uint32 itemid = atoi(sep->arg[1]); + if (database.GetItemStatus(itemid) > c->Admin()) + c->Message(13, "Error: Insufficient status to summon this item."); + else if (sep->argnum==2 && sep->IsNumber(2)) { + c->SummonItem(itemid, atoi(sep->arg[2]) ); + } else if (sep->argnum==3) { + c->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]) ); + } else if (sep->argnum==4) + c->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]) ); + else if (sep->argnum==5) + c->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]) ); + else if (sep->argnum==6) + c->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]), atoi(sep->arg[6]) ); + else if (sep->argnum==7) + c->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]), atoi(sep->arg[6]), atoi(sep->arg[7]) ); + else { + c->SummonItem(itemid); + } + } +} + +void command_giveitem(Client *c, const Seperator *sep) +{ + if (!sep->IsNumber(1)) { + c->Message(13, "Usage: #summonitem [item id] [charges], charges are optional"); + } else if(c->GetTarget() == NULL) { + c->Message(13, "You must target a client to give the item to."); + } else if(!c->GetTarget()->IsClient()) { + c->Message(13, "You can only give items to players with this command."); + } else { + Client *t = c->GetTarget()->CastToClient(); + uint32 itemid = atoi(sep->arg[1]); + if (database.GetItemStatus(itemid) > c->Admin()) + c->Message(13, "Error: Insufficient status to summon this item."); + else if (sep->argnum==2 && sep->IsNumber(2)) { + t->SummonItem(itemid, atoi(sep->arg[2]) ); + } else if (sep->argnum==3) { + t->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]) ); + } else if (sep->argnum==4) + t->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]) ); + else if (sep->argnum==5) + t->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]) ); + else if (sep->argnum==6) + t->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]), atoi(sep->arg[6]) ); + else if (sep->argnum==7) + t->SummonItem(itemid, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), atoi(sep->arg[5]), atoi(sep->arg[6]), atoi(sep->arg[7]) ); + else { + t->SummonItem(itemid); + } + } +} + +void command_givemoney(Client *c, const Seperator *sep) +{ + if (!sep->IsNumber(1)) { //as long as the first one is a number, we'll just let atoi convert the rest to 0 or a number + c->Message(13, "Usage: #Usage: #givemoney [pp] [gp] [sp] [cp]"); + } + else if(c->GetTarget() == NULL) { + c->Message(13, "You must target a player to give money to."); + } + else if(!c->GetTarget()->IsClient()) { + c->Message(13, "You can only give money to players with this command."); + } + else { + //TODO: update this to the client, otherwise the client doesn't show any weight change until you zone, move an item, etc + c->GetTarget()->CastToClient()->AddMoneyToPP(atoi(sep->arg[4]), atoi(sep->arg[3]), atoi(sep->arg[2]), atoi(sep->arg[1]), true); + c->Message(0, "Added %i Platinum, %i Gold, %i Silver, and %i Copper to %s's inventory.", atoi(sep->arg[1]), atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), c->GetTarget()->GetName()); + } +} + +void command_itemsearch(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) + c->Message(0, "Usage: #itemsearch [search string]"); + else + { + const char *search_criteria=sep->argplus[1]; + + const Item_Struct* item = 0; + if (Seperator::IsNumber(search_criteria)) { + item = database.GetItem(atoi(search_criteria)); + if (item) + if (c->GetClientVersion() >= EQClientRoF) + { + c->Message(0, " %i: %c%06X0000000000000000000000000000000000000000000000000%s%c",(int) item->ID,0x12, item->ID, item->Name, 0x12); + } + else if (c->GetClientVersion() >= EQClientSoF) + { + c->Message(0, " %i: %c%06X00000000000000000000000000000000000000000000%s%c",(int) item->ID,0x12, item->ID, item->Name, 0x12); + } + else + { + c->Message(0, " %i: %c%06X000000000000000000000000000000000000000%s%c",(int) item->ID,0x12, item->ID, item->Name, 0x12); + } + else + c->Message(0, "Item #%s not found", search_criteria); + return; + } +#ifdef SHAREMEM + int count=0; + //int iSearchLen = strlen(search_criteria)+1; + char sName[64]; + char sCriteria[255]; + strn0cpy(sCriteria, search_criteria, sizeof(sCriteria)); + strupr(sCriteria); + char* pdest; + uint32 it = 0; + while ((item = database.IterateItems(&it))) { + strn0cpy(sName, item->Name, sizeof(sName)); + strupr(sName); + pdest = strstr(sName, sCriteria); + if (pdest != NULL) { + if (c->GetClientVersion() >= EQClientRoF) + { + c->Message(0, " %i: %c%06X0000000000000000000000000000000000000000000000000%s%c",(int) item->ID,0x12, item->ID, item->Name, 0x12); + } + else if (c->GetClientVersion() >= EQClientSoF) + { + c->Message(0, " %i: %c%06X00000000000000000000000000000000000000000000%s%c",(int) item->ID,0x12, item->ID, item->Name, 0x12); + } + else + { + c->Message(0, " %i: %c%06X000000000000000000000000000000000000000%s%c",(int) item->ID,0x12, item->ID, item->Name, 0x12); + } + count++; + } + if (count == 50) + break; + } + if (count == 50) + c->Message(0, "50 items shown...too many results."); + else + c->Message(0, "%i items found", count); +#endif + } +} + +void command_datarate(Client *c, const Seperator *sep) +{ +// EQStream *eqs = c->Connection(); + + if (sep->arg[1][0] == 0) { + //c->Message(0, "Datarate: %1.1f", eqs->GetDataRate()); + //if (c->Admin() >= commandChangeDatarate) { + //c->Message(0, "Dataflow: %i", eqs->GetDataFlow()); + //c->Message(0, "Datahigh: %i", eqs->GetDataHigh()); + //} + } + else if (sep->IsNumber(1) && atof(sep->arg[1]) > 0 && (c->Admin() >= commandChangeDatarate || atof(sep->arg[1]) <= 25)) { + //eqs->SetDataRate(atof(sep->arg[1])); + //c->Message(0, "Datarate: %1.1f", eqs->GetDataRate()); + } + else + c->Message(0, "Usage: #DataRate [new data rate in kb/sec, max 25]"); +} + +void command_setaaxp(Client *c, const Seperator *sep) +{ + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + + if (sep->IsNumber(1)) { + t->SetEXP(t->GetEXP(), atoi(sep->arg[1]), false); + if(sep->IsNumber(2) && sep->IsNumber(3)) { + t->SetLeadershipEXP(atoi(sep->arg[2]), atoi(sep->arg[3])); + } + } else + c->Message(0, "Usage: #setaaxp ( )"); +} + +void command_setaapts(Client *c, const Seperator *sep) +{ + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + + if(sep->arg[1][0] == '\0' || sep->arg[2][0] == '\0') + c->Message(0, "Usage: #setaapts "); + else if(atoi(sep->arg[2]) <= 0 || atoi(sep->arg[2]) > 200) + c->Message(0, "You must have a number greater than 0 for points and no more than 200."); + else if(!strcasecmp(sep->arg[1], "group")) { + t->SetLeadershipEXP(atoi(sep->arg[2])*GROUP_EXP_PER_POINT, t->GetRaidEXP()); + } else if(!strcasecmp(sep->arg[1], "raid")) { + t->SetLeadershipEXP(t->GetGroupEXP(), atoi(sep->arg[2])*RAID_EXP_PER_POINT); + } else { + t->SetEXP(t->GetEXP(),t->GetMaxAAXP()*atoi(sep->arg[2]),false); + t->SendAAStats(); + t->SendAATable(); + } +} + +void command_setcrystals(Client *c, const Seperator *sep) +{ + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + + if(sep->arg[1][0] == '\0' || sep->arg[2][0] == '\0') + c->Message(0, "Usage: #setcrystals "); + else if(atoi(sep->arg[2]) <= 0 || atoi(sep->arg[2]) > 100000) + c->Message(0, "You must have a number greater than 0 for crystals and no more than 100000."); + else if(!strcasecmp(sep->arg[1], "radiant")) + { + t->SetRadiantCrystals(atoi(sep->arg[2])); + t->SendCrystalCounts(); + t->Save(); + } + else if(!strcasecmp(sep->arg[1], "ebon")) + { + t->SetEbonCrystals(atoi(sep->arg[2])); + t->SendCrystalCounts(); + t->Save(); + } + else + { + c->Message(0, "Usage: #setcrystals "); + } +} + +void command_stun(Client *c, const Seperator *sep) +{ + Mob *t=c->CastToMob(); + uint32 duration; + + if(sep->arg[1][0]) + { + duration = atoi(sep->arg[1]); + if(c->GetTarget()) + t=c->GetTarget(); + if(t->IsClient()) + t->CastToClient()->Stun(duration); + else + t->CastToNPC()->Stun(duration); + } + else + c->Message(0, "Usage: #stun [duration]"); +} + +#ifdef EMBPERL_PLUGIN +#ifdef EMBPERL_EVAL_COMMANDS + +void command_embperl_plugin(Client *c, const Seperator *sep) +{ + if(sep->arg[1][0] == 0) + { + c->Message(0, "Usage: #plugin (subname) [arguments]"); + return; + } + + Embperl * perl; + if(!parse || !(perl = ((PerlembParser *)parse)->getperl())) + { + c->Message(0, "Error: Perl module not loaded"); + return; + } + + std::string exports = "$plugin::printbuff='';$plugin::ip='"; + struct in_addr ip; ip.s_addr = c->GetIP(); + exports += inet_ntoa(ip); + exports += "';$plugin::name=qq("; + exports += c->GetName(); + exports += ");package plugin;"; + perl->eval(exports.c_str()); + + std::string fqsubname("plugin::"); + fqsubname.append(sep->arg[1]); + + //convert args into a vector of strings. + std::vector args; + for(int i = 2; i < sep->argnum; ++i) + { + args.push_back(sep->arg[i]); + } + + try + { + perl->dosub(fqsubname.c_str(), &args); + std::string output = perl->getstr("$plugin::printbuff"); + if(output.length()) + c->Message(0, "%s", output.c_str()); + } + catch(const char * err) + { + c->Message(0, "Error executing plugin: %s", perl->lasterr().c_str()); + } + + perl->eval("package main;"); + +} + +void command_embperl_eval(Client *c, const Seperator *sep) +{ + if(sep->arg[1][0] == 0) + { + c->Message(0, "Usage: #peval (expr)"); + return; + } + + Embperl * perl; + if(!parse || !(perl = ((PerlembParser *)parse)->getperl())) + { + c->Message(0, "Error: Perl module not loaded"); + return; + } + + std::string exports = "$plugin::printbuff='';$plugin::ip='"; + struct in_addr ip; ip.s_addr = c->GetIP(); + exports += inet_ntoa(ip); + exports += "';$plugin::name=qq("; + exports += c->GetName(); + exports += ");"; + perl->eval(exports.c_str()); + + try + { + std::string cmd = std::string("package plugin;") + std::string(sep->msg + sizeof("peval ")); + perl->eval(cmd.c_str()); + std::string output = perl->getstr("$plugin::printbuff"); + if(output.length()) + c->Message(0, "%s", output.c_str()); + } + catch(const char * err) + { + c->Message(0, "Error: %s", perl->lasterr().c_str()); + } + + perl->eval("package main;"); + +} + +#endif //EMBPERL_PLUGIN +#endif //EMBPERL_EVAL_COMMANDS + +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]"); + } + 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]); + + ServerPacket* pack = new ServerPacket(ServerOP_FlagUpdate, 6); + *((uint32*) pack->pBuffer) = atoi(row[0]); + *((int16*) &pack->pBuffer[4]) = -2; + worldserver.SendPacket(pack); + safe_delete(pack); + + Client *client = NULL; + client = entity_list.GetClientByName(sep->arg[1]); + if(client) + { + client->Kick(); + } + 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); + } + + mysql_free_result(result); + } + else + { + c->Message(13,"Character does not exist."); + } + if(query) + { + safe_delete_array(query); + } + } +} + +void command_suspend(Client *c, const Seperator *sep) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = NULL; + + 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]); + + if(Duration < 0) + Duration = 0; + + char *EscName = new char[strlen(sep->arg[1]) * 2 + 1]; + + database.DoEscapeString(EscName, sep->arg[1], strlen(sep->arg[1])); + + int AccountID; + + 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); + + 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]); + + safe_delete_array(query); + + Client *BannedClient = entity_list.GetClientByName(sep->arg[1]); + + if(BannedClient) + BannedClient->Kick(); + else + { + ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct* sks = (ServerKickPlayer_Struct*) pack->pBuffer; + + 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); + } + + } else + c->Message(13,"Character does not exist."); + + safe_delete_array(EscName); + } +} + +void command_ipban(Client *c, const Seperator *sep) +{ + if(sep->arg[1] == 0) + { + c->Message(0, "Usage: #ipban [xxx.xxx.xxx.xxx]"); + } else { + if(database.AddBannedIP(sep->arg[1], c->GetName())) { + c->Message(0, "%s has been successfully added to the Banned_IPs table by %s",sep->arg[1], c->GetName()); + } else { + c->Message(0, "IPBan Failed (IP address is possibly already in the table?)"); + } + } +} + +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) + { + c->Message(0, "Usage: #revoke [charname] [1/0]"); + } + 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 + { +#if EQDEBUG >= 6 + 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=NULL; + } + } +} + +void command_oocmute(Client *c, const Seperator *sep) +{ + if(sep->arg[1][0] == 0 || !(sep->arg[1][0] == '1' || sep->arg[1][0] == '0')) + c->Message(0, "Usage: #oocmute [1/0]"); + else { + ServerPacket * outapp = new ServerPacket (ServerOP_OOCMute,1); + *(outapp->pBuffer)=atoi(sep->arg[1]); + worldserver.SendPacket(outapp); + safe_delete(outapp); + } +} + +void command_checklos(Client *c, const Seperator *sep) +{ + if(c->GetTarget()) + { +// if(c->CheckLos(c->GetTarget())) + if(c->CheckLosFN(c->GetTarget())) + c->Message(0, "You have LOS to %s", c->GetTarget()->GetName()); + else + c->Message(0, "You do not have LOS to %s", c->GetTarget()->GetName()); + } + else + { + c->Message(0, "ERROR: Target required"); + } +} + +void command_set_adventure_points(Client *c, const Seperator *sep) +{ + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t=c->GetTarget()->CastToClient(); + + if(!sep->arg[1][0]) + { + c->Message(0, "Usage: #setadventurepoints [points] [theme]"); + return; + } + + if(!sep->IsNumber(1) || !sep->IsNumber(2)) + { + c->Message(0, "Usage: #setadventurepoints [points] [theme]"); + return; + } + + c->Message(0, "Updating adventure points for %s", t->GetName()); + t->UpdateLDoNPoints(atoi(sep->arg[1]), atoi(sep->arg[2])); +} + +void command_npcsay(Client *c, const Seperator *sep) +{ + if(c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) + { + c->GetTarget()->Say(sep->argplus[1]); + } + else + { + c->Message(0, "Usage: #npcsay message (requires NPC target"); + } +} + +void command_npcshout(Client *c, const Seperator *sep) +{ + if(c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) + { + c->GetTarget()->Shout(sep->argplus[1]); + } + else + { + c->Message(0, "Usage: #npcshout message (requires NPC target"); + } +} + +void command_timers(Client *c, const Seperator *sep) { + if(!c->GetTarget() || !c->GetTarget()->IsClient()) { + c->Message(0,"Need a player target for timers."); + return; + } + Client *them = c->GetTarget()->CastToClient(); + + vector< pair > res; + them->GetPTimers().ToVector(res); + + c->Message(0,"Timers for target:"); + + int r; + int l = res.size(); + for(r = 0; r < l; r++) { + c->Message(0,"Timer %d: %d seconds remain.", res[r].first, res[r].second->GetRemainingTime()); + } +} + +void command_npcemote(Client *c, const Seperator *sep) +{ + if(c->GetTarget() && c->GetTarget()->IsNPC() && sep->arg[1][0]) + { + c->GetTarget()->Emote(sep->argplus[1]); + } + else + { + c->Message(0, "Usage: #npcemote message (requires NPC target"); + } +} + +void command_npcedit(Client *c, const Seperator *sep) +{ + 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:"); + c->Message(0, "#npcedit Name - Sets an NPCs name"); + c->Message(0, "#npcedit Lastname - Sets an NPCs lastname"); + c->Message(0, "#npcedit Level - Sets an NPCs level"); + c->Message(0, "#npcedit Race - Sets an NPCs race"); + c->Message(0, "#npcedit Class - Sets an NPCs class"); + c->Message(0, "#npcedit Bodytype - Sets an NPCs bodytype"); + c->Message(0, "#npcedit HP - Sets an NPCs hitpoints"); + c->Message(0, "#npcedit Gender - Sets an NPCs gender"); + c->Message(0, "#npcedit Texture - Sets an NPCs texture"); + c->Message(0, "#npcedit Helmtexture - Sets an NPCs helmtexture"); + c->Message(0, "#npcedit Size - Sets an NPCs size"); + c->Message(0, "#npcedit Hpregen - Sets an NPCs hitpoint regen rate per tick"); + c->Message(0, "#npcedit Manaregen - Sets an NPCs mana regen rate per tick"); + c->Message(0, "#npcedit Loottable - Sets the lootable ID for an NPC "); + c->Message(0, "#npcedit Merchantid - Sets the merchant ID for an NPC"); + c->Message(0, "#npcedit alt_currency_id - Sets the Alternate Currency ID for an alterative currency Merchant"); + c->Message(0, "#npcedit Spell - Sets the npc spells list ID for an NPC"); + c->Message(0, "#npcedit Faction - Sets the NPCs faction id"); + c->Message(0, "#npcedit Mindmg - Sets an NPCs minimum damage"); + c->Message(0, "#npcedit Maxdmg - Sets an NPCs maximum damage"); + c->Message(0, "#npcedit Aggroradius - Sets an NPCs aggro radius"); + c->Message(0, "#npcedit Social - Set to 1 if an NPC should assist others on its faction"); + c->Message(0, "#npcedit Runspeed - Sets an NPCs run speed"); + c->Message(0, "#npcedit MR - Sets an NPCs magic resistance"); + c->Message(0, "#npcedit PR - Sets an NPCs poisen resistance"); + c->Message(0, "#npcedit DR - Sets an NPCs disease resistance"); + c->Message(0, "#npcedit FR - Sets an NPCs fire resistance"); + c->Message(0, "#npcedit CR - Sets an NPCs cold resistance"); + c->Message(0, "#npcedit Corrup - Sets an NPCs corruption resistance"); + c->Message(0, "#npcedit Seeinvis - Sets an NPCs ability to see invis"); + c->Message(0, "#npcedit Seeinvisundead - Sets an NPCs ability to see through invis vs. undead"); + c->Message(0, "#npcedit Seehide - Sets an NPCs ability to see through hide"); + c->Message(0, "#npcedit Seeimprovedhide - Sets an NPCs ability to see through improved hide"); + c->Message(0, "#npcedit AC - Sets an NPCs armor class"); + c->Message(0, "#npcedit npcaggro - Sets an NPC's npc_aggro flag"); + 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 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"); + c->Message(0, "#npcedit featuresave - Saves all current facial features to the database"); + c->Message(0, "#npcedit armortint_id - Set NPC Armor tint ID"); + c->Message(0, "#npcedit setanimation - Set NPC's animation on spawn (Stored in spawn2 table)"); + + } + 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); + } + + 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 now is 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], "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], "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], "MR" ) == 0 ) + { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + c->Message(15,"NPCID %u now has a magic resist 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 resist 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 resist 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 resist 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 resist 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 resist 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], "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], "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], "qglobal" ) == 0 ) + { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + c->Message(15,"Quest globals have been %d for NPCID %u",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])==0?"disabled":"enabled"); + 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()); + + 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); + + } + + 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); + } + 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 + } + } + 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((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 +void command_packetprofile(Client *c, const Seperator *sep) { + Client *t = c; + if(c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + c->DumpPacketProfile(); +} +#endif + +#ifdef EQPROFILE +void command_profiledump(Client *c, const Seperator *sep) { + DumpZoneProfile(); +} + +void command_profilereset(Client *c, const Seperator *sep) { + ResetZoneProfile(); +} +#endif + +void command_opcode(Client *c, const Seperator *sep) { + if(!strcasecmp( sep->arg[1], "reload" )) { + ReloadAllPatches(); + c->Message(0, "Opcodes for all patches have been reloaded"); + } +} + +void command_logsql(Client *c, const Seperator *sep) { + if(!strcasecmp( sep->arg[1], "off" )) { + c->ChangeSQLLog(NULL); + } else if(sep->arg[1][0] != '\0') { + c->ChangeSQLLog(sep->argplus[1]); + } else { + c->Message(0, "Usage: #logsql (file name)"); + } +} + +void command_logs(Client *c, const Seperator *sep) +{ +#ifdef CLIENT_LOGS + Client *t = c; + if(c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + + if(!strcasecmp( sep->arg[1], "status" ) ) + client_logs.subscribe(EQEMuLog::Status, t); + else if(!strcasecmp( sep->arg[1], "normal" ) ) + client_logs.subscribe(EQEMuLog::Normal, t); + else if(!strcasecmp( sep->arg[1], "error" ) ) + client_logs.subscribe(EQEMuLog::Error, t); + else if(!strcasecmp( sep->arg[1], "debug" ) ) + client_logs.subscribe(EQEMuLog::Debug, t); + else if(!strcasecmp( sep->arg[1], "quest" ) ) + client_logs.subscribe(EQEMuLog::Quest, t); + else if(!strcasecmp( sep->arg[1], "all" ) ) + client_logs.subscribeAll(t); + else { + c->Message(0, "Usage: #logs [status|normal|error|debug|quest|all]"); + return; + } + if(c != t) + c->Message(0, "%s have been subscribed to %s logs.", t->GetName(), sep->arg[1]); + t->Message(0, "You have been subscribed to %s logs.", sep->arg[1]); +#else + c->Message(0, "Client logs are disabled in this server's build."); +#endif +} + +void command_nologs(Client *c, const Seperator *sep) +{ +#ifdef CLIENT_LOGS + Client *t = c; + if(c->GetTarget() && c->GetTarget()->IsClient()) { + t = c; + } + + if(!strcasecmp( sep->arg[1], "status" ) ) + client_logs.unsubscribe(EQEMuLog::Status, t); + else if(!strcasecmp( sep->arg[1], "normal" ) ) + client_logs.unsubscribe(EQEMuLog::Normal, t); + else if(!strcasecmp( sep->arg[1], "error" ) ) + client_logs.unsubscribe(EQEMuLog::Error, t); + else if(!strcasecmp( sep->arg[1], "debug" ) ) + client_logs.unsubscribe(EQEMuLog::Debug, t); + else if(!strcasecmp( sep->arg[1], "quest" ) ) + client_logs.unsubscribe(EQEMuLog::Quest, t); + else if(!strcasecmp( sep->arg[1], "all" ) ) + client_logs.unsubscribeAll(t); + else { + c->Message(0, "Usage: #logs [status|normal|error|debug|quest|all]"); + return; + } + + c->Message(0, "You have been unsubscribed from %s logs.", sep->arg[1]); +#else + c->Message(0, "Client logs are disabled in this server's build."); +#endif +} + +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()) { + 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)) + { + c->Message(15, "Could not update database."); + } + else + { + c->LogSQL(query); + c->Message(15, "Success! Changes take effect on zone reboot."); + } + safe_delete(query); + } + 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)) + { + c->Message(15, "Could not update database."); + } + else + { + c->LogSQL(query); + c->Message(15, "Success! Changes take effect on zone reboot."); + } + safe_delete(query); + } + else if(!strcasecmp(sep->arg[1], "view")) + { + const NPCType *type = database.GetNPCType(t->GetNPCTypeID()); + if(!type) { + c->Message(15, "Invalid NPC type."); + } else if(type->qglobal) { + c->Message(15, "This NPC has quest globals active."); + } else { + c->Message(15, "This NPC has quest globals disabled."); + } + } else { + c->Message(15, "Invalid action specified."); + } +} + +void command_fear(Client *c, const Seperator *sep) { +/* + //super-command for editing fear grids and hints +// char errbuf[MYSQL_ERRMSG_SIZE]; +// char *query = 0; + if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { + c->Message(0, "Syntax: #fear [view|close|add|link|list|del]."); + c->Message(0, "...view - spawn an NPC at each fear grid point, and fear hint"); + c->Message(0, "...close - spawn an NPC at the closest fear point"); + c->Message(0, "...path [##] [draw|npc] - draw a path (or spawn an NPC) at each fear point for ## hops"); +// c->Message(0, "...add [force|disjoint]- add a new fear hint point where your standing"); +// c->Message(0, "....... force requires this point not move when combining nodes"); +// c->Message(0, "....... disjoint marks the graph connected to this node as a valid disjoint graph"); +// c->Message(0, "...link [id1] [id2] - make a fear hint link between two hint points"); +// c->Message(0, "...list - show a list of all fear hint points for this zone"); +// c->Message(0, "...find [range] - show a list of all fear hint points eithin range of you"); +// c->Message(0, "...del [id] - remove the fear hint 'id'"); +// c->Message(0, "...start - fears your target until you #fear stop them"); +// c->Message(0, "...stop - Stops fear on your target"); + return; + } + + if(!strcasecmp(sep->arg[1], "view")) { + if(zone->pathing == NULL) { + c->Message(13, "There is no fear grid file loaded for this zone."); + return; + } + + uint32 count = zone->pathing->CountNodes(); + uint32 r; + char buf[128]; + PathNode_Struct *node = zone->pathing->GetNode(0); //assumes nodes are stored in a linear array + for(r = 0; r < count; r++, node++) { + sprintf(buf, "Fear_Point%d 3", r); + NPC* npc = NPC::SpawnNPC(buf, node->x, node->y, node->z, c->GetHeading(), NULL); + if(npc == NULL) + c->Message(13, "Unable to spawn new NPC marker."); + //do we need to do anything else? + } + } else if(!strcasecmp(sep->arg[1], "path")) { + if(zone->pathing == NULL) { + c->Message(13, "There is no fear grid file loaded for this zone."); + return; + } + + int dist = atoi(sep->arg[2]); + char buf[128]; + + FindPerson_Point it; + vector pts; + pts.reserve(dist+2); + bool path_mode = (strcasecmp(sep->arg[3], "npc") != 0); + + MobFearState fs; + + sprintf(buf, "Close_Fear_Link%d_ 3", dist); + if(!zone->pathing->FindNearestFear(&fs, c->GetX(), c->GetY(), c->GetZ())) { + c->Message(13, "Unable to locate a closest fear path."); + return; + } + + if(path_mode) { + it.x = c->GetX(); + it.y = c->GetY(); + it.z = c->GetZ(); + pts.push_back(it); + it.x = fs.x; + it.y = fs.y; + it.z = fs.z; + pts.push_back(it); + } else { + NPC* npc = NPC::SpawnNPC(buf, fs.x, fs.y, fs.z, c->GetHeading(), NULL); + if(npc == NULL) + c->Message(13, "Unable to spawn new NPC marker."); + } + + for(dist--; dist > 0; dist--) { + sprintf(buf, "Close_Fear_Link%d_ 3", dist); + if(!zone->pathing->NextFearPath(&fs)) { + c->Message(13, "Unable to locate next fear path."); + return; + } + + if(path_mode) { + it.x = fs.x; + it.y = fs.y; + it.z = fs.z; + pts.push_back(it); + } else { + NPC::SpawnNPC(buf, fs.x, fs.y, fs.z, c->GetHeading(), NULL); + } + } + + if(path_mode) { + c->SendPathPacket(pts); + } + + } else if(!strcasecmp(sep->arg[1], "close")) { + if(zone->pathing == NULL) { + c->Message(13, "There is no fear grid file loaded for this zone."); + return; + } + MobFearState fs; + + if(!zone->pathing->FindNearestFear(&fs, c->GetX(), c->GetY(), c->GetZ())) { + c->Message(13, "Unable to locate a closest fear path."); + return; + } + + NPC* npc = NPC::SpawnNPC("Close_Fear_Point 2", fs.x, fs.y, fs.z, c->GetHeading(), NULL); + if(npc == NULL) + c->Message(13, "Unable to spawn new NPC marker."); + + } else if(!strcasecmp(sep->arg[1], "see")) { + + vector points; + + Mob* target = c->GetTarget(); + + if(target == NULL) { + //empty length packet == not found. + EQApplicationPacket outapp(OP_FindPersonReply, 0); + c->QueuePacket(&outapp); + return; + } + + c->Message(13, "Found NPC '%s'\n", target->GetName()); + + //fill in the path array... + points.resize(4); + points[0].x = c->GetX(); + points[0].y = c->GetY(); + points[0].z = c->GetZ(); + points[1].x = target->GetX(); + points[1].y = target->GetY(); + points[1].z = target->GetZ(); + points[2].x = 10; + points[2].y = 10; + points[2].z = 10; + points[3].x = 0; + points[3].y = 0; + points[3].z = 0; + + + + if(points.size() == 0) { + //empty length packet == not found. + EQApplicationPacket outapp(OP_FindPersonReply, 0); + c->QueuePacket(&outapp); + return; + } + + int len = sizeof(FindPersonResult_Struct) + points.size() * sizeof(FindPerson_Point); + EQApplicationPacket *outapp = new EQApplicationPacket(OP_FindPersonReply, len); + FindPersonResult_Struct* fpr=(FindPersonResult_Struct*)outapp->pBuffer; + + vector::iterator cur, end; + cur = points.begin(); + end = points.end(); + int r; + for(r = 0; cur != end; cur++, r++) { + fpr->path[r] = *cur; + } + cur--; //last element. + fpr->dest = *cur; + + c->FastQueuePacket(&outapp); + } else { + c->Message(15, "Invalid action specified. use '#fear help' for help"); + } + */ +} + +void command_path(Client *c, const Seperator *sep) +{ + if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) + { + c->Message(0, "Syntax: #path shownodes: Spawns a npc to represent every npc node."); + c->Message(0, "#path info node_id: Gives information about node info (requires shownode target)."); + c->Message(0, "#path dump file_name: Dumps the current zone->pathing to a file of your naming."); + c->Message(0, "#path add [requested_id]: Adds a node at your current location will try to take the requested id if possible."); + c->Message(0, "#path connect connect_to_id [is_teleport] [door_id]: Connects the currently targeted node to connect_to_id's node and connects that node back (requires shownode target)."); + c->Message(0, "#path sconnect connect_to_id [is_teleport] [door_id]: Connects the currently targeted node to connect_to_id's node (requires shownode target)."); + c->Message(0, "#path qconnect [set]: short cut connect, connects the targeted node to the node you set with #path qconnect set (requires shownode target)."); + c->Message(0, "#path disconnect [all]/disconnect_from_id: Disconnects the currently targeted node to disconnect from disconnect from id's node (requires shownode target), if passed all as the second argument it will disconnect this node from every other node."); + c->Message(0, "#path move: Moves your targeted node to your current position"); + c->Message(0, "#path process file_name: processes the map file and tries to automatically generate a rudimentary path setup and then dumps the current zone->pathing to a file of your naming."); + c->Message(0, "#path resort [nodes]: resorts the connections/nodes after you've manually altered them so they'll work."); + return; + } + if(!strcasecmp(sep->arg[1], "shownodes")) + { + if(zone->pathing) + zone->pathing->SpawnPathNodes(); + + return; + } + + if(!strcasecmp(sep->arg[1], "info")) + { + if(zone->pathing) + { + zone->pathing->NodeInfo(c); + } + return; + } + + if(!strcasecmp(sep->arg[1], "dump")) + { + if(zone->pathing) + { + if(sep->arg[2][0] == '\0') + return; + + zone->pathing->DumpPath(sep->arg[2]); + } + return; + } + + if(!strcasecmp(sep->arg[1], "add")) + { + if(zone->pathing) + { + float px = c->GetX(); + float py = c->GetY(); + float pz = c->GetZ(); + float best_z; + + if(zone->zonemap) + { + VERTEX loc(px, py, pz); + best_z = zone->zonemap->FindBestZ(MAP_ROOT_NODE, loc, NULL, NULL); + } + else + { + best_z = pz; + } + int32 res = zone->pathing->AddNode(px, py, pz, best_z, atoi(sep->arg[2])); + if(res >= 0) + { + c->Message(0, "Added Path Node: %i", res); + } + else + { + c->Message(0, "Failed to add Path Node"); + } + } + else + { + zone->pathing = new PathManager(); + float px = c->GetX(); + float py = c->GetY(); + float pz = c->GetZ(); + float best_z; + + if(zone->zonemap) + { + VERTEX loc(px, py, pz); + best_z = zone->zonemap->FindBestZ(MAP_ROOT_NODE, loc, NULL, NULL); + } + else + { + best_z = pz; + } + int32 res = zone->pathing->AddNode(px, py, pz, best_z, atoi(sep->arg[2])); + if(res >= 0) + { + c->Message(0, "Added Path Node: %i", res); + } + else + { + c->Message(0, "Failed to add Path Node"); + } + } + return; + } + + if(!strcasecmp(sep->arg[1], "remove")) + { + if(zone->pathing) + { + if(zone->pathing->DeleteNode(c)) + { + c->Message(0, "Removed Node."); + } + else + { + c->Message(0, "Unable to Remove Node."); + } + } + return; + } + + if(!strcasecmp(sep->arg[1], "connect")) + { + if(zone->pathing) + { + zone->pathing->ConnectNodeToNode(c, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])); + } + return; + } + + if(!strcasecmp(sep->arg[1], "sconnect")) + { + if(zone->pathing) + { + zone->pathing->ConnectNode(c, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])); + } + return; + } + + if(!strcasecmp(sep->arg[1], "qconnect")) + { + if(zone->pathing) + { + if(!strcasecmp(sep->arg[2], "set")) + { + zone->pathing->QuickConnect(c, true); + } + else + { + zone->pathing->QuickConnect(c, false); + } + } + return; + } + + if(!strcasecmp(sep->arg[1], "disconnect")) + { + if(zone->pathing) + { + if(!strcasecmp(sep->arg[2], "all")) + { + zone->pathing->DisconnectAll(c); + } + else + { + zone->pathing->DisconnectNodeToNode(c, atoi(sep->arg[2])); + } + } + return; + } + + + if(!strcasecmp(sep->arg[1], "move")) + { + if(zone->pathing) + { + zone->pathing->MoveNode(c); + } + return; + } + + if(!strcasecmp(sep->arg[1], "process")) + { + if(zone->pathing) + { + if(sep->arg[2][0] == '\0') + return; + + zone->pathing->ProcessNodesAndSave(sep->arg[2]); + c->Message(0, "Path processed..."); + } + return; + } + + if(!strcasecmp(sep->arg[1], "resort")) + { + if(zone->pathing) + { + if(!strcasecmp(sep->arg[2], "nodes")) + { + zone->pathing->SortNodes(); + c->Message(0, "Nodes resorted..."); + } + else + { + zone->pathing->ResortConnections(); + c->Message(0, "Connections resorted..."); + } + } + return; + } + + if(!strcasecmp(sep->arg[1], "hazard")) + { + if(zone->pathing) + { + if(c && c->GetTarget()) + { + if(zone->pathing->NoHazardsAccurate(VERTEX(c->GetX(),c->GetY(),c->GetZ()), + VERTEX(c->GetTarget()->GetX(),c->GetTarget()->GetY(),c->GetTarget()->GetZ()))) + { + c->Message(0, "No hazards."); + } + else + { + c->Message(0, "Hazard Detected..."); + } + } + } + return; + } + + if(!strcasecmp(sep->arg[1], "print")) + { + if(zone->pathing) + { + zone->pathing->PrintPathing(); + } + return; + } + + if(!strcasecmp(sep->arg[1], "showneighbours") || !strcasecmp(sep->arg[1], "showneighbors")) + { + if(!c->GetTarget()) + { + c->Message(0, "First #path shownodes to spawn the pathnodes, and then target one of them."); + return; + } + if(zone->pathing) + { + zone->pathing->ShowPathNodeNeighbours(c); + return; + } + } + if(!strcasecmp(sep->arg[1], "meshtest")) + { + if(zone->pathing) + { + if(!strcasecmp(sep->arg[2], "simple")) + { + c->Message(0, "You may go linkdead. Results will be in the log file."); + zone->pathing->SimpleMeshTest(); + return; + } + else + { + c->Message(0, "You may go linkdead. Results will be in the log file."); + zone->pathing->MeshTest(); + return; + } + } + } + + if(!strcasecmp(sep->arg[1], "allspawns")) + { + if(zone->pathing) + { + c->Message(0, "You may go linkdead. Results will be in the log file."); + entity_list.FindPathsToAllNPCs(); + return; + } + } + + if(!strcasecmp(sep->arg[1], "nearest")) + { + if(!c->GetTarget() || !c->GetTarget()->IsMob()) + { + c->Message(0, "You must target something."); + return; + } + + if(zone->pathing) + { + Mob *m = c->GetTarget(); + + VERTEX Position(m->GetX(), m->GetY(), m->GetZ()); + + int Node = zone->pathing->FindNearestPathNode(Position); + + if(Node == -1) + c->Message(0, "Unable to locate a path node within range."); + else + c->Message(0, "Nearest path node is %i", Node); + + return; + } + } + + return; +} + +void Client::Undye() { + for (int cur_slot = 0; cur_slot < 9 ; cur_slot++ ){ + uint8 slot2=SlotConvert(cur_slot); + ItemInst* inst = m_inv.GetItem(slot2); + if(inst != NULL) { + inst->SetColor(inst->GetItem()->Color); + database.SaveInventory(CharacterID(), inst, slot2); + } + m_pp.item_tint[cur_slot].color = 0; + SendWearChange(cur_slot); + } + Save(0); +} + +void command_undye(Client *c, const Seperator *sep) +{ + if(c->GetTarget() && c->GetTarget()->IsClient()) + { + c->GetTarget()->CastToClient()->Undye(); + } + else + { + c->Message(0, "ERROR: Client target required"); + } +} + +void command_undyeme(Client *c, const Seperator *sep) +{ + if(c) { + c->Undye(); + c->Message(13, "Dye removed from all slots. Please zone for the process to complete."); + } +} + +void command_ginfo(Client *c, const Seperator *sep) +{ + Client *t; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t = c->GetTarget()->CastToClient(); + else + t = c; + + Group *g = t->GetGroup(); + if(!g) { + c->Message(0, "This client is not in a group"); + return; + } + + c->Message(0, "Player: %s is in Group #%lu: with %i members", t->GetName(), (unsigned long)g->GetID(), g->GroupCount()); + + uint32 r; + for(r = 0; r < MAX_GROUP_MEMBERS; r++) { + if(g->members[r] == NULL) { + if(g->membername[r][0] == '\0') + continue; + c->Message(0, "...Zoned Member: %s, Roles: %s %s %s", g->membername[r], + (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", + (g->MemberRoles[r] & RoleTank) ? "Tank" : "", + (g->MemberRoles[r] & RolePuller) ? "Puller" : ""); + } else { + c->Message(0, "...In-Zone Member: %s (0x%x) Roles: %s %s %s", g->membername[r], g->members[r], + (g->MemberRoles[r] & RoleAssist) ? "Assist" : "", + (g->MemberRoles[r] & RoleTank) ? "Tank" : "", + (g->MemberRoles[r] & RolePuller) ? "Puller" : ""); + + } + } +} + +void command_hp(Client *c, const Seperator *sep) +{ + c->SendHPUpdate(); + c->SendManaUpdatePacket(); +} + +void command_aggro(Client *c, const Seperator *sep) +{ + if(c->GetTarget() == NULL || !c->GetTarget()->IsNPC()) { + c->Message(0, "Error: you must have an NPC target."); + return; + } + float d = atof(sep->arg[1]); + if(d == 0.0f) { + c->Message(13, "Error: distance argument required."); + return; + } + bool verbose = false; + if(sep->arg[2][0] == '-' && sep->arg[2][1] == 'v' && sep->arg[2][2] == '\0') { + verbose = true; + } + + entity_list.DescribeAggro(c, c->GetTarget()->CastToNPC(), d, verbose); +} + +void command_pf(Client *c, const Seperator *sep) +{ + if(c->GetTarget()) + { + Mob *who = c->GetTarget(); + c->Message(0, "POS: (%.2f, %.2f, %.2f)", who->GetX(), who->GetY(), who->GetZ()); + c->Message(0, "WP: (%.2f, %.2f, %.2f) (%d/%d)", who->GetCWPX(), who->GetCWPY(), who->GetCWPZ(), who->GetCWP(), who->IsNPC()?who->CastToNPC()->GetMaxWp():-1); + c->Message(0, "TAR: (%.2f, %.2f, %.2f)", who->GetTarX(), who->GetTarY(), who->GetTarZ()); + c->Message(0, "TARV: (%.2f, %.2f, %.2f)", who->GetTarVX(), who->GetTarVY(), who->GetTarVZ()); + c->Message(0, "|TV|=%.2f index=%d", who->GetTarVector(), who->GetTarNDX()); + c->Message(0, "pause=%d RAspeed=%d", who->GetCWPP(), who->GetRunAnimSpeed()); + } else { + c->Message(0, "ERROR: target required"); + } +} + +void command_bestz(Client *c, const Seperator *sep) { + if (zone->zonemap == NULL) { + c->Message(0,"Maps deactivated in this zone."); + return; + } + + NodeRef pnode; + if(c->GetTarget()) { + pnode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), c->GetTarget()->GetX(), c->GetTarget()->GetY() ); + } else { + pnode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), c->GetX(), c->GetY() ); + } + if (pnode == NODE_NONE) { + c->Message(0,"Unable to find your node."); + return; + } + + VERTEX me; + me.x = c->GetX(); + me.y = c->GetY(); + me.z = c->GetZ() + (c->GetSize()==0.0?6:c->GetSize()) * HEAD_POSITION; + VERTEX hit; + VERTEX bme(me); + bme.z -= 500; + + float best_z = zone->zonemap->FindBestZ(pnode, me, &hit, NULL); + + float best_z2 = -999990; + if(zone->zonemap->LineIntersectsNode(pnode, me, bme, &hit, NULL)) { + best_z2 = hit.z; + } + + if (best_z != -999999) + { + c->Message(0,"Z is %.3f or %.3f at (%.3f, %.3f).", best_z, best_z2, me.x, me.y); + } + else + { + c->Message(0,"Found no Z."); + } + + if(zone->watermap == NULL) { + c->Message(0,"Water Region Map not loaded for this zone"); + } else { + WaterRegionType RegionType; + float z; + + if(c->GetTarget()) { + z=c->GetTarget()->GetZ(); + RegionType = zone->watermap->BSPReturnRegionType(1, c->GetTarget()->GetX(), c->GetTarget()->GetY(), z); + c->Message(0,"InWater returns %d", zone->watermap->InWater(c->GetTarget()->GetX(), c->GetTarget()->GetY(), z)); + c->Message(0,"InLava returns %d", zone->watermap->InLava(c->GetTarget()->GetX(), c->GetTarget()->GetY(), z)); + + } + else { + z=c->GetZ(); + RegionType = zone->watermap->BSPReturnRegionType(1, c->GetX(), c->GetY(),z); + c->Message(0,"InWater returns %d", zone->watermap->InWater(c->GetX(), c->GetY(), z)); + c->Message(0,"InLava returns %d", zone->watermap->InLava(c->GetX(), c->GetY(), z)); + + } + + switch(RegionType) { + case RegionTypeNormal: { c->Message(0,"There is nothing special about the region you are in!"); break; } + case RegionTypeWater: { c->Message(0,"You/your target are in Water."); break; } + case RegionTypeLava: { c->Message(0,"You/your target are in Lava."); break; } + case RegionTypeVWater: { c->Message(0,"You/your target are in VWater (Icy Water?)."); break; } + default: c->Message(0,"You/your target are in an unknown region type."); + } + } + + +} + + +void command_reloadstatic(Client *c, const Seperator *sep) { + c->Message(0, "Reloading zone static data..."); + zone->ReloadStaticData(); +} + +void command_flags(Client *c, const Seperator *sep) { + Client *t = c; + + if(c->Admin() >= minStatusToSeeOthersZoneFlags) { + Mob *tgt = c->GetTarget(); + if(tgt != NULL && tgt->IsClient()) + t = tgt->CastToClient(); + } + + t->SendZoneFlagInfo(c); +} + +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"); + c->Message(0, "...unlockzone [zone id/short] - Removes the flag requirement from the specified zone"); + c->Message(0, "...listzones - List all zones which require a flag, and their flag's name"); + c->Message(0, "...give [zone id/short] - Give your target the zone flag for the specified zone."); + c->Message(0, "...take [zone id/short] - Take the zone flag for the specified zone away from your target"); + c->Message(0, "...Note: use #flags to view flags on a person"); + return; + } + + if(!strcasecmp(sep->arg[1], "lockzone")) { + uint32 zoneid = 0; + if(sep->arg[2][0] != '\0') { + zoneid = atoi(sep->arg[2]); + if(zoneid < 1) { + zoneid = database.GetZoneID(sep->arg[2]); + } + } + if(zoneid < 1) { + c->Message(13, "zone required. see help."); + return; + } + + char flag_name[128]; + if(sep->argplus[3][0] == '\0') { + c->Message(13, "flag name required. see help."); + return; + } + 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); + } + safe_delete(query); + } else if(!strcasecmp(sep->arg[1], "unlockzone")) { + uint32 zoneid = 0; + if(sep->arg[2][0] != '\0') { + zoneid = atoi(sep->arg[2]); + if(zoneid < 1) { + 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)); + } + 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")) { + uint32 zoneid = 0; + if(sep->arg[2][0] != '\0') { + zoneid = atoi(sep->arg[2]); + if(zoneid < 1) { + zoneid = database.GetZoneID(sep->arg[2]); + } + } + if(zoneid < 1) { + c->Message(13, "zone required. see help."); + return; + } + + Mob *t = c->GetTarget(); + if(t == NULL || !t->IsClient()) { + c->Message(13, "client target required"); + return; + } + + t->CastToClient()->SetZoneFlag(zoneid); + } else if(!strcasecmp(sep->arg[1], "give")) { + uint32 zoneid = 0; + if(sep->arg[2][0] != '\0') { + zoneid = atoi(sep->arg[2]); + if(zoneid < 1) { + zoneid = database.GetZoneID(sep->arg[2]); + } + } + if(zoneid < 1) { + c->Message(13, "zone required. see help."); + return; + } + + Mob *t = c->GetTarget(); + if(t == NULL || !t->IsClient()) { + c->Message(13, "client target required"); + return; + } + + t->CastToClient()->ClearZoneFlag(zoneid); + } else { + c->Message(15, "Invalid action specified. use '#flagedit help' for help"); + } +} + +void command_mlog(Client *c, const Seperator *sep) { + //super-command for managing log settings + if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { + c->Message(0, "Syntax: #mlog [subcommand]."); + c->Message(0, "-- Mob Logging Togglers --"); + c->Message(0, "...target [on|off] - Set logging enabled for your target"); + c->Message(0, "...all [on|off] - Set logging enabled for all mobs and clients (prolly a bad idea)"); + c->Message(0, "...mobs [on|off] - Set logging enabled for all mobs"); + c->Message(0, "...clients [on|off] - Set logging enabled for all clients"); + c->Message(0, "...radius [on|off] [radius] - Set logging enable for all mobs and clients within `radius`"); + c->Message(0, "-------------"); + c->Message(0, "-- Log Settings --"); + c->Message(0, "...list [category] - List all log types in specified category, or all categories if none specified."); + c->Message(0, "...setcat [category] [on|off] - Enable/Disable all types in a specified category"); + c->Message(0, "...set [type] [on|off] - Enable/Disable the specified log type"); + c->Message(0, "...load [filename] - Load log type settings from the file `filename`"); + return; + } + bool onoff; + string on("on"); + string off("off"); + + if(!strcasecmp(sep->arg[1], "target")) { + if(on == sep->arg[2]) onoff = true; + else if(off == sep->arg[2]) onoff = false; + else { c->Message(13, "Invalid argument. Expected on/off."); return; } + + Mob *tgt = c->GetTarget(); + if(tgt == NULL) { + c->Message(13, "You must have a target for this command."); + return; + } + + if(onoff) + tgt->EnableLogging(); + else + tgt->DisableLogging(); + + c->Message(0, "Logging has been enabled on %s", tgt->GetName()); + } else if(!strcasecmp(sep->arg[1], "all")) { + if(on == sep->arg[2]) onoff = true; + else if(off == sep->arg[2]) onoff = false; + else { c->Message(13, "Invalid argument '%s'. Expected on/off.", sep->arg[2]); return; } + + entity_list.RadialSetLogging(c, onoff, true, true); + + c->Message(0, "Logging has been enabled for all entities"); + } else if(!strcasecmp(sep->arg[1], "mobs")) { + if(on == sep->arg[2]) onoff = true; + else if(off == sep->arg[2]) onoff = false; + else { c->Message(13, "Invalid argument '%s'. Expected on/off.", sep->arg[2]); return; } + + entity_list.RadialSetLogging(c, onoff, false, true); + + c->Message(0, "Logging has been enabled for all mobs"); + } else if(!strcasecmp(sep->arg[1], "clients")) { + if(on == sep->arg[2]) onoff = true; + else if(off == sep->arg[2]) onoff = false; + else { c->Message(13, "Invalid argument '%s'. Expected on/off.", sep->arg[2]); return; } + + entity_list.RadialSetLogging(c, onoff, true, false); + + c->Message(0, "Logging has been enabled for all clients"); + } else if(!strcasecmp(sep->arg[1], "radius")) { + if(on == sep->arg[2]) onoff = true; + else if(off == sep->arg[2]) onoff = false; + else { c->Message(13, "Invalid argument '%s'. Expected on/off.", sep->arg[2]); return; } + + float radius = atof(sep->arg[3]); + if(radius <= 0) { + c->Message(13, "Invalid radius %f", radius); + return; + } + + entity_list.RadialSetLogging(c, onoff, false, true, radius); + + c->Message(0, "Logging has been enabled for all entities within %f", radius); + } else if(!strcasecmp(sep->arg[1], "list")) { + int r; + if(sep->arg[2][0] == '\0') { + c->Message(0, "Listing all log categories:"); + for(r = 0; r < NUMBER_OF_LOG_CATEGORIES; r++) { + c->Message(0, "Category %d: %s", r, log_category_names[r]); + } + } else { + //first we have to find the category ID. + for(r = 0; r < NUMBER_OF_LOG_CATEGORIES; r++) { + if(!strcasecmp(log_category_names[r], sep->arg[2])) + break; + } + if(r == NUMBER_OF_LOG_CATEGORIES) { + c->Message(13, "Unable to find category '%s'", sep->arg[2]); + return; + } + int logcat = r; + c->Message(0, "Types for category %d: %s", logcat, log_category_names[logcat]); + for(r = 0; r < NUMBER_OF_LOG_TYPES; r++) { + if(log_type_info[r].category != logcat) + continue; + c->Message(0, "...%d: %s (%s)", r, log_type_info[r].name, is_log_enabled(LogType(r))?"enabled":"disabled"); + } + } + } else if(!strcasecmp(sep->arg[1], "setcat")) { + if(on == sep->arg[3]) onoff = true; + else if(off == sep->arg[3]) onoff = false; + else { c->Message(13, "Invalid argument %s. Expected on/off.", sep->arg[3]); return; } + + int r; + //first we have to find the category ID. + for(r = 0; r < NUMBER_OF_LOG_CATEGORIES; r++) { + if(!strcasecmp(log_category_names[r], sep->arg[2])) + break; + } + if(r == NUMBER_OF_LOG_CATEGORIES) { + c->Message(13, "Unable to find category '%s'", sep->arg[2]); + return; + } + + LogCategory logcat = LogCategory(r); + for(r = 0; r < NUMBER_OF_LOG_TYPES; r++) { + if(log_type_info[r].category != logcat) + continue; + + if(onoff) { + log_enable(LogType(r)); + c->Message(0, "Log type %s (%d) has been enabled", log_type_info[r].name, r); + } else { + log_disable(LogType(r)); + c->Message(0, "Log type %s (%d) has been disabled", log_type_info[r].name, r); + } + } + } else if(!strcasecmp(sep->arg[1], "set")) { + if(on == sep->arg[3]) onoff = true; + else if(off == sep->arg[3]) onoff = false; + else { c->Message(13, "Invalid argument %s. Expected on/off.", sep->arg[3]); return; } + + //first we have to find the category ID. + int r; + for(r = 0; r < NUMBER_OF_LOG_TYPES; r++) { + if(!strcasecmp(log_type_info[r].name, sep->arg[2])) + break; + } + if(r == NUMBER_OF_LOG_TYPES) { + c->Message(13, "Unable to find log type %s", sep->arg[2]); + return; + } + + if(onoff) { + log_enable(LogType(r)); + c->Message(0, "Log type %s (%d) has been enabled", log_type_info[r].name, r); + } else { + log_disable(LogType(r)); + c->Message(0, "Log type %s (%d) has been disabled", log_type_info[r].name, r); + } + } else { + c->Message(15, "Invalid action specified. use '#mlog help' for help"); + } +} + +void command_serverrules(Client *c, const Seperator *sep) +{ + c->SendRules(c); +} + +void command_acceptrules(Client *c, const Seperator *sep) +{ + if(!database.GetAgreementFlag(c->AccountID())) + { + database.SetAgreementFlag(c->AccountID()); + c->SendAppearancePacket(AT_Anim, ANIM_STAND); + c->Message(0,"It is recorded you have agreed to the rules."); + } +} + +void command_guildcreate(Client *c, const Seperator *sep) +{ + char founders[3]; + if (database.GetVariable("GuildCreation", founders, 3)); + { + if(strlen(sep->argplus[1])>4 && strlen(sep->argplus[1])<16) + { + guild_mgr.AddGuildApproval(sep->argplus[1],c); + } + else + { + c->Message(0,"Guild name must be more than 4 characters and less than 16."); + } + } +} + +void command_guildapprove(Client *c, const Seperator *sep) +{ + guild_mgr.AddMemberApproval(atoi(sep->arg[1]),c); +} + +void command_guildlist(Client *c, const Seperator *sep) +{ + GuildApproval* tmp = guild_mgr.FindGuildByIDApproval(atoi(sep->arg[1])); + if(tmp) + { + tmp->ApprovedMembers(c); + } + else + c->Message(0,"Could not find reference id."); +} + +void command_hatelist(Client *c, const Seperator *sep) { + Mob *target = c->GetTarget(); + if(target == NULL) { + c->Message(0, "Error: you must have a target."); + return; + } + + c->Message(0, "Display hate list for %s..", target->GetName()); + target->PrintHateListToClient(c); +} + + +void command_rules(Client *c, const Seperator *sep) { + //super-command for managing rules settings + if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { + c->Message(0, "Syntax: #rules [subcommand]."); + c->Message(0, "-- Rule Set Manipulation --"); + c->Message(0, "...listsets - List avaliable rule sets"); + c->Message(0, "...current - gives the name of the ruleset currently running in this zone"); + c->Message(0, "...reload - Reload the selected ruleset in this zone"); + c->Message(0, "...switch (ruleset name) - Change the selected ruleset and load it"); + c->Message(0, "...load (ruleset name) - Load a ruleset in just this zone without changing the selected set"); +//too lazy to write this right now: +// c->Message(0, "...wload (ruleset name) - Load a ruleset in all zones without changing the selected set"); + c->Message(0, "...store [ruleset name] - Store the running ruleset as the specified name"); + c->Message(0, "---------------------"); + c->Message(0, "-- Running Rule Manipulation --"); + c->Message(0, "...reset - Reset all rules to their default values"); + c->Message(0, "...get [rule] - Get the specified rule's local value"); + c->Message(0, "...set (rule) (value) - Set the specified rule to the specified value locally only"); + c->Message(0, "...setdb (rule) (value) - Set the specified rule to the specified value locally and in the DB"); + c->Message(0, "...list [catname] - List all rules in the specified category (or all categiries if omitted)"); + c->Message(0, "...values [catname] - List the value of all rules in the specified category"); + return; + } + + if(!strcasecmp(sep->arg[1], "current")) { + c->Message(0, "Currently running ruleset '%s' (%d)", rules->GetActiveRuleset(), rules->GetActiveRulesetID()); + } else if(!strcasecmp(sep->arg[1], "listsets")) { + std::map sets; + if(!rules->ListRulesets(&database, sets)) { + c->Message(13, "Failed to list rule sets!"); + return; + } + + c->Message(0, "Avaliable rule sets:"); + std::map::iterator cur, end; + cur = sets.begin(); + end = sets.end(); + for(; cur != end; cur++) { + c->Message(0, "(%d) %s", cur->first, cur->second.c_str()); + } + } else if(!strcasecmp(sep->arg[1], "reload")) { + rules->LoadRules(&database, rules->GetActiveRuleset()); + c->Message(0, "The active ruleset (%s (%d)) has been reloaded", rules->GetActiveRuleset(), rules->GetActiveRulesetID()); + } else if(!strcasecmp(sep->arg[1], "switch")) { + //make sure this is a valid rule set.. + int rsid = rules->GetRulesetID(&database, sep->arg[2]); + if(rsid < 0) { + c->Message(13, "Unknown rule set '%s'", sep->arg[2]); + return; + } + if(!database.SetVariable("RuleSet", sep->arg[2])) { + c->Message(13, "Failed to update variables table to change selected rule set"); + return; + } + + //TODO: we likely want to reload this ruleset everywhere... + rules->LoadRules(&database, sep->arg[2]); + + c->Message(0, "The selected ruleset has been changed to (%s (%d)) and reloaded locally", sep->arg[2], rsid); + } else if(!strcasecmp(sep->arg[1], "load")) { + //make sure this is a valid rule set.. + int rsid = rules->GetRulesetID(&database, sep->arg[2]); + if(rsid < 0) { + c->Message(13, "Unknown rule set '%s'", sep->arg[2]); + return; + } + rules->LoadRules(&database, sep->arg[2]); + c->Message(0, "Loaded ruleset '%s' (%d) locally", sep->arg[2], rsid); + } else if(!strcasecmp(sep->arg[1], "store")) { + if(sep->argnum == 1) { + //store current rule set. + rules->SaveRules(&database); + c->Message(0, "Rules saved"); + } else if(sep->argnum == 2) { + rules->SaveRules(&database, sep->arg[2]); + int prersid = rules->GetActiveRulesetID(); + int rsid = rules->GetRulesetID(&database, sep->arg[2]); + if(rsid < 0) { + c->Message(13, "Unable to query ruleset ID after store, it most likely failed."); + } else { + c->Message(0, "Stored rules as ruleset '%s' (%d)", sep->arg[2], rsid); + if(prersid != rsid) { + c->Message(0, "Rule set %s (%d) is now active in this zone", sep->arg[2], rsid); + } + } + } else { + c->Message(13, "Invalid argument count, see help."); + return; + } + } else if(!strcasecmp(sep->arg[1], "reset")) { + rules->ResetRules(); + c->Message(0, "The running ruleset has been set to defaults"); + + } else if(!strcasecmp(sep->arg[1], "get")) { + if(sep->argnum != 2) { + c->Message(13, "Invalid argument count, see help."); + return; + } + std::string value; + if(!rules->GetRule(sep->arg[2], value)) + c->Message(13, "Unable to find rule %s", sep->arg[2]); + else + c->Message(0, "%s - %s", sep->arg[2], value.c_str()); + + } else if(!strcasecmp(sep->arg[1], "set")) { + if(sep->argnum != 3) { + c->Message(13, "Invalid argument count, see help."); + return; + } + if(!rules->SetRule(sep->arg[2], sep->arg[3])) { + c->Message(13, "Failed to modify rule"); + } else { + c->Message(0, "Rule modified locally."); + } + } else if(!strcasecmp(sep->arg[1], "setdb")) { + if(sep->argnum != 3) { + c->Message(13, "Invalid argument count, see help."); + return; + } + if(!rules->SetRule(sep->arg[2], sep->arg[3], &database, true)) { + c->Message(13, "Failed to modify rule"); + } else { + c->Message(0, "Rule modified locally and in the database."); + } + } else if(!strcasecmp(sep->arg[1], "list")) { + if(sep->argnum == 1) { + std::vector rule_list; + if(!rules->ListCategories(rule_list)) { + c->Message(13, "Failed to list categories!"); + return; + } + c->Message(0, "Rule Categories:"); + std::vector::iterator cur, end; + cur = rule_list.begin(); + end = rule_list.end(); + for(; cur != end; cur++) { + c->Message(0, " %s", *cur); + } + } else if(sep->argnum == 2) { + const char *catfilt = NULL; + if(std::string("all") != sep->arg[2]) + catfilt = sep->arg[2]; + std::vector rule_list; + if(!rules->ListRules(catfilt, rule_list)) { + c->Message(13, "Failed to list rules!"); + return; + } + c->Message(0, "Rules in category %s:", sep->arg[2]); + std::vector::iterator cur, end; + cur = rule_list.begin(); + end = rule_list.end(); + for(; cur != end; cur++) { + c->Message(0, " %s", *cur); + } + } else { + c->Message(13, "Invalid argument count, see help."); + } + } else if(!strcasecmp(sep->arg[1], "values")) { + if(sep->argnum != 2) { + c->Message(13, "Invalid argument count, see help."); + return; + } else { + const char *catfilt = NULL; + if(std::string("all") != sep->arg[2]) + catfilt = sep->arg[2]; + std::vector rule_list; + if(!rules->ListRules(catfilt, rule_list)) { + c->Message(13, "Failed to list rules!"); + return; + } + c->Message(0, "Rules & values in category %s:", sep->arg[2]); + std::vector::iterator cur, end; + cur = rule_list.begin(); + end = rule_list.end(); + for(std::string tmp_value; cur != end; cur++) { + if (rules->GetRule(*cur, tmp_value)) + c->Message(0, " %s - %s", *cur, tmp_value.c_str()); + } + } + + } else { + c->Message(15, "Invalid action specified. use '#rules help' for help"); + } +} + + +void command_task(Client *c, const Seperator *sep) { + //super-command for managing tasks + if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { + c->Message(0, "Syntax: #task [subcommand]."); + c->Message(0, "-- Task System Commands --"); + c->Message(0, "...show - List active tasks for a client"); + c->Message(0, "...update [Count]"); + c->Message(0, "...reloadall - Reload all Task information from the database"); + c->Message(0, "...reload task - Reload Task and Activity informnation for a single task"); + c->Message(0, "...reload lists - Reload goal/reward list information"); + c->Message(0, "...reload prox - Reload proximity information"); + c->Message(0, "...reload sets - Reload task set information"); + return; + } + + if(!strcasecmp(sep->arg[1], "show")) { + if(c->GetTarget() && c->GetTarget()->IsClient()) + c->GetTarget()->CastToClient()->ShowClientTasks(); + else + c->ShowClientTasks(); + + return; + } + + if(!strcasecmp(sep->arg[1], "update")) { + if(sep->argnum>=3) { + int TaskID = atoi(sep->arg[2]); + int ActivityID = atoi(sep->arg[3]); + int Count=1; + + if(sep->argnum>=4) { + Count = atoi(sep->arg[4]); + if(Count <= 0) + Count = 1; + } + c->Message(15, "Updating Task %i, Activity %i, Count %i", TaskID, ActivityID, Count); + c->UpdateTaskActivity(TaskID, ActivityID, Count); + } + return; + } + if(!strcasecmp(sep->arg[1], "reloadall")) { + c->Message(15, "Sending reloadtasks to world"); + worldserver.SendReloadTasks(RELOADTASKS); + c->Message(15, "Back again"); + return; + } + + if(!strcasecmp(sep->arg[1], "reload")) { + if(sep->arg[2][0] != '\0') { + if(!strcasecmp(sep->arg[2], "lists")) { + c->Message(15, "Sending reload lists to world"); + worldserver.SendReloadTasks(RELOADTASKGOALLISTS); + c->Message(15, "Back again"); + return; + } + if(!strcasecmp(sep->arg[2], "prox")) { + c->Message(15, "Sending reload proximities to world"); + worldserver.SendReloadTasks(RELOADTASKPROXIMITIES); + c->Message(15, "Back again"); + return; + } + if(!strcasecmp(sep->arg[2], "sets")) { + c->Message(15, "Sending reload task sets to world"); + worldserver.SendReloadTasks(RELOADTASKSETS); + c->Message(15, "Back again"); + return; + } + if(!strcasecmp(sep->arg[2], "task") && (sep->arg[3][0] != '\0')) { + int TaskID = atoi(sep->arg[3]); + if((TaskID > 0) && (TaskID < MAXTASKS)) { + c->Message(15, "Sending reload task %i to world"); + worldserver.SendReloadTasks(RELOADTASKS, TaskID); + c->Message(15, "Back again"); + return; + } + } + } + + } + c->Message(0, "Unable to interpret command. Type #task help"); + +} +void command_reloadtitles(Client *c, const Seperator *sep) +{ + ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0); + worldserver.SendPacket(pack); + safe_delete(pack); + c->Message(15, "Player Titles Reloaded."); + +} + +void command_altactivate(Client *c, const Seperator *sep){ + if(sep->arg[1][0] == '\0'){ + c->Message(10, "Invalid argument, usage:"); + c->Message(10, "#altactivate list - lists the AA ID numbers that are available to you"); + c->Message(10, "#altactivate time [argument] - returns the time left until you can use the AA with the ID that matches the argument."); + c->Message(10, "#altactivate [argument] - activates the AA with the ID that matches the argument."); + return; + } + if(!strcasecmp(sep->arg[1], "help")){ + c->Message(10, "Usage:"); + c->Message(10, "#altactivate list - lists the AA ID numbers that are available to you"); + c->Message(10, "#altactivate time [argument] - returns the time left until you can use the AA with the ID that matches the argument."); + c->Message(10, "#altactivate [argument] - activates the AA with the ID that matches the argument."); + return; + } + if(!strcasecmp(sep->arg[1], "list")){ + c->Message(10, "You have access to the following AA Abilities:"); + int x, val; + SendAA_Struct* saa = NULL; + for(x = 0; x < aaHighestID; x++){ + if(AA_Actions[x][0].spell_id || AA_Actions[x][0].action){ //if there's an action or spell associated we assume it's a valid + val = 0; //and assume if they don't have a value for the first rank then it isn't valid for any rank + saa = NULL; + val = c->GetAA(x); + if(val){ + saa = zone->FindAA(x); + c->Message(10, "%d: %s %d", x, saa->name, val); + } + } + } + } + else if(!strcasecmp(sep->arg[1], "time")){ + int ability = atoi(sep->arg[2]); + if(c->GetAA(ability)){ + int remain = c->GetPTimers().GetRemainingTime(pTimerAAStart + ability); + if(remain) + c->Message(10, "You may use that ability in %d minutes and %d seconds.", (remain/60), (remain%60)); + else + c->Message(10, "You may use that ability now."); + } + else{ + c->Message(10, "You do not have access to that ability."); + } + } + else + { + c->ActivateAA((aaID) atoi(sep->arg[1])); + } +} + +void command_refundaa(Client *c, const Seperator *sep){ + Client* refundee = NULL; + int curpt = 0; + bool refunded = false; + 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){ + for(int x1=0;x1GetAA(x1); + if(curpt > 0){ + SendAA_Struct* curaa = zone->FindAA(x1); + if(curaa){ + refundee->SetAA(x1, 0); + for(int x2=0;x2GetPP().aapoints += curaa->cost + (curaa->cost_inc * x2); + refunded = true; + } + } + else //aa doesn't exist.. but if they bought it then it had at least a cost of 1 point each + { //so give back what we can + refundee->GetPP().aapoints += curpt; + refundee->SetAA(x1, 0); + refunded = true; + } + } + } + } + } + if(refunded){ + refundee->Save(); //save of course + refundee->Kick(); //client gets all buggy if we don't immediatly relog so just force it on them + } +} + +void command_traindisc(Client *c, const Seperator *sep) +{ + uint8 max_level, min_level; + uint16 curspell, count; + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) + t=c->GetTarget()->CastToClient(); + + if(!sep->arg[1][0]) + { + c->Message(0, "FORMAT: #traindisc "); + return; + } + + max_level = (uint8)atoi(sep->arg[1]); + if (!c->GetGM() && max_level > RuleI(Character, MaxLevel)) + max_level = RuleI(Character, MaxLevel); //default to Character:MaxLevel if we're not a GM & it's higher than the max level + min_level = sep->arg[2][0] ? (uint8)atoi(sep->arg[2]) : 1; //default to 1 if there isn't a 2nd argument + if (!c->GetGM() && min_level > RuleI(Character, MaxLevel)) + min_level = RuleI(Character, MaxLevel); //default to Character:MaxLevel if we're not a GM & it's higher than the max level + + if(max_level < 1 || min_level < 1) + { + c->Message(0, "ERROR: Level must be greater than 1."); + return; + } + if (min_level > max_level) { + c->Message(0, "Error: Min Level must be less than or equal to Max Level."); + return; + } + + t->Message(0, "Training disciplines"); + if(t != c) + c->Message(0, "Training disciplines for %s.", t->GetName()); + LogFile->write(EQEMuLog::Normal, "Train disciplines request for %s from %s, levels: %u -> %u", t->GetName(), c->GetName(), min_level, max_level); + + for(curspell = 0, count = 0; curspell < SPDAT_RECORDS; curspell++) + { + if + ( + spells[curspell].classes[WARRIOR] != 0 && // check if spell exists + spells[curspell].classes[t->GetPP().class_-1] <= max_level && //maximum level + spells[curspell].classes[t->GetPP().class_-1] >= min_level && //minimum level + spells[curspell].skill != 52 + ) + { + if(IsDiscipline(curspell)){ + //we may want to come up with a function like Client::GetNextAvailableSpellBookSlot() to help speed this up a little + for(int r = 0; r < MAX_PP_DISCIPLINES; r++) { + if(t->GetPP().disciplines.values[r] == curspell) { + t->Message(13, "You already know this discipline."); + break; //continue the 1st loop + } else if(t->GetPP().disciplines.values[r] == 0) { + t->GetPP().disciplines.values[r] = curspell; + t->SendDisciplineUpdate(); + t->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 continue onto the next slot + } + } + } + } + + if (count > 0) { + t->Message(0, "Successfully trained %u disciplines.", count); + if (t != c) + c->Message(0, "Successfully trained %u disciplines for %s.", count, t->GetName()); + } else { + t->Message(0, "No disciplines trained."); + if (t != c) + c->Message(0, "No disciplines trained for %s.", t->GetName()); + } +} + +void command_setgraveyard(Client *c, const Seperator *sep) +{ + uint32 zoneid = 0; + uint32 graveyard_id = 0; + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) + t=c->GetTarget()->CastToClient(); + + if(!sep->arg[1][0]) { + c->Message(0, "Usage: #setgraveyard [zonename]"); + return; + } + + zoneid = database.GetZoneID(sep->arg[1]); + + if(zoneid > 0) { + graveyard_id = database.NewGraveyardRecord(zoneid, t->GetX(), t->GetY(), t->GetZ(), t->GetHeading()); + + if(graveyard_id > 0) { + c->Message(0, "Successfuly added a new record for this graveyard!"); + if(database.AddGraveyardIDToZone(zoneid, graveyard_id) > 0) { + c->Message(0, "Successfuly added this new graveyard for the zone %s.", sep->arg[1]); + // TODO: Set graveyard data to the running zone process. + c->Message(0, "Done!"); + } + else + c->Message(0, "Unable to add this new graveyard to the zone %s.", sep->arg[1]); + } + else { + c->Message(0, "Unable to create a new graveyard record in the database."); + } + } + else { + c->Message(0, "Unable to retrieve a ZoneID for the zone: %s", sep->arg[1]); + } + + return; +} + +void command_deletegraveyard(Client *c, const Seperator *sep) +{ + uint32 zoneid = 0; + uint32 graveyard_id = 0; + + if(!sep->arg[1][0]) { + c->Message(0, "Usage: #deletegraveyard [zonename]"); + return; + } + + zoneid = database.GetZoneID(sep->arg[1]); + graveyard_id = database.GetZoneGraveyardID(zoneid, 0); + + if(zoneid > 0 && graveyard_id > 0) { + if(database.DeleteGraveyard(zoneid, graveyard_id)) + c->Message(0, "Successfuly deleted graveyard %u for zone %s.", graveyard_id, sep->arg[1]); + else + c->Message(0, "Unable to delete graveyard %u for zone %s.", graveyard_id, sep->arg[1]); + } + else { + if(zoneid <= 0) + c->Message(0, "Unable to retrieve a ZoneID for the zone: %s", sep->arg[1]); + else if(graveyard_id <= 0) + c->Message(0, "Unable to retrieve a valid GraveyardID for the zone: %s", sep->arg[1]); + } + + return; +} + +void command_summonburriedplayercorpse(Client *c, const Seperator *sep) +{ + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) + t = c->GetTarget()->CastToClient(); + else { + c->Message(0, "You must first select a target!"); + return; + } + + Corpse* PlayerCorpse = database.SummonBurriedPlayerCorpse(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."); + + return; +} + +void command_getplayerburriedcorpsecount(Client *c, const Seperator *sep) +{ + Client *t=c; + + if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) + t = c->GetTarget()->CastToClient(); + else { + c->Message(0, "You must first select a target!"); + return; + } + + uint32 CorpseCount = database.GetPlayerBurriedCorpseCount(t->CharacterID()); + + if(CorpseCount > 0) + c->Message(0, "Your target has a total of %u burried corpses.", CorpseCount); + else + c->Message(0, "Your target doesn't have any burried corpses."); + + return; +} + +void command_refreshgroup(Client *c, const Seperator *sep) +{ + if(!c) + return; + + Group *g = c->GetGroup(); + + if(!g) + return; + + database.RefreshGroupFromDB(c); + //g->SendUpdate(7, c); +} + +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(!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; + + 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; + + 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]); + } + + 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."); + } + 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]"); + } + } + +void command_aggrozone(Client *c, const Seperator *sep) { + if(!c) + return; + + Mob *m = c->CastToMob(); + + if (!m) + return; + + int hate = atoi(sep->arg[1]); //should default to 0 if we don't enter anything + entity_list.AggroZone(m,hate); + c->Message(0, "Train to you! Last chance to go invulnerable..."); +} + +void command_modifynpcstat(Client *c, const Seperator *sep) +{ + if(!c) + return; + + if(sep->arg[1][0] == '\0') + { + c->Message(0, "usage #modifynpcstat arg value"); + c->Message(0, "Args: ac, str, sta, agi, dex, wis, _int, cha, max_hp, mr, fr, cr, pr, dr, runspeed, special_attacks, " + "attack_speed, atk, accuracy, trackable, min_hit, max_hit, see_invis_undead, see_hide, see_improved_hide, " + "hp_regen, mana_regen, aggro, assist, slow_mitigation, loottable_id, healscale, spellscale"); + return; + } + + if(!c->GetTarget()) + return; + + if(!c->GetTarget()->IsNPC()) + return; + + c->GetTarget()->CastToNPC()->ModifyNPCStat(sep->arg[1], sep->arg[2]); +} + +void command_instance(Client *c, const Seperator *sep) +{ + if(!c) + return; + + //options: + //help + //create [zone_id] [version] + //destroy [instance_id] + //add [instance_id] [player_name] + //remove [instance_id] [player_name] + //list [player_name] + + if(strcasecmp(sep->arg[1], "help") == 0) + { + c->Message(0, "#instance usage:"); + c->Message(0, "#instance create zone_id version duration - Creates an instance of version 'version' in the " + "zone with id matching zone_id, will last for duration seconds."); + c->Message(0, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); + c->Message(0, "#instance add instance_id player_name - adds the player 'player_name' to the instance " + "with id matching instance_id."); + c->Message(0, "#instance remove instance_id player_name - removes the player 'player_name' from the " + "instance with id matching instance_id."); + c->Message(0, "#instance list player_name - lists all the instances 'player_name' is apart of."); + return; + } + else if(strcasecmp(sep->arg[1], "create") == 0) + { + if(!sep->IsNumber(3) || !sep->IsNumber(4)) + { + c->Message(0, "#instance create zone_id version duration - Creates an instance of version 'version' in the " + "zone with id matching zone_id, will last for duration seconds."); + return; + } + + const char * zn = NULL; + uint32 zone_id = 0; + + if(sep->IsNumber(2)) + { + zone_id = atoi(sep->arg[2]); + } + else + { + zone_id = database.GetZoneID(sep->arg[2]); + } + + uint32 version = atoi(sep->arg[3]); + uint32 duration = atoi(sep->arg[4]); + zn = database.GetZoneName(zone_id); + + if(!zn) + { + c->Message(0, "Zone with id %lu was not found by the server.", (unsigned long)zone_id); + return; + } + + uint16 id = 0; + if(!database.GetUnusedInstanceID(id)) + { + c->Message(0, "Server was unable to find a free instance id."); + return; + } + + if(!database.CreateInstance(id, zone_id, version, duration)) + { + c->Message(0, "Server was unable to create a new instance."); + return; + } + + c->Message(0, "New instance %s was created with id %lu.", zn, (unsigned long)id); + } + else if(strcasecmp(sep->arg[1], "destroy") == 0) + { + if(!sep->IsNumber(2)) + { + c->Message(0, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); + return; + } + + uint16 id = atoi(sep->arg[2]); + database.DeleteInstance(id); + c->Message(0, "Destroyed instance with id %lu.", (unsigned long)id); + } + else if(strcasecmp(sep->arg[1], "add") == 0) + { + if(!sep->IsNumber(2)) + { + c->Message(0, "#instance add instance_id player_name - adds the player 'player_name' to the instance " + "with id matching instance_id."); + return; + } + + uint16 id = atoi(sep->arg[2]); + uint32 charid = database.GetCharacterID(sep->arg[3]); + + if(id <= 0 || charid <= 0) + { + c->Message(0, "Must enter a valid instance id and player name."); + return; + } + + if(!database.CheckInstanceExists(id)) + { + c->Message(0, "Instance does not exist."); + return; + } + + uint32 zone_id = database.ZoneIDFromInstanceID(id); + uint32 version = database.VersionFromInstanceID(id); + uint32 cur_id = database.GetInstanceID(zone_id, charid, version); + if(cur_id == 0) + { + if(database.AddClientToInstance(id, charid)) + { + c->Message(0, "Added client to instance."); + } + else + { + c->Message(0, "Failed to add client to instance."); + } + } + else + { + c->Message(0, "Client was already saved to %u which has uses the same zone and version as that instance.", cur_id); + } + } + else if(strcasecmp(sep->arg[1], "remove") == 0) + { + if(!sep->IsNumber(2)) + { + c->Message(0, "#instance remove instance_id player_name - removes the player 'player_name' from the " + "instance with id matching instance_id."); + return; + } + + uint16 id = atoi(sep->arg[2]); + uint32 charid = database.GetCharacterID(sep->arg[3]); + + if(id <= 0 || charid <= 0) + { + c->Message(0, "Must enter a valid instance id and player name."); + } + + if(database.RemoveClientFromInstance(id, charid)) + { + c->Message(0, "Removed client from instance."); + } + else + { + c->Message(0, "Failed to remove client from instance."); + } + } + else if(strcasecmp(sep->arg[1], "list") == 0) + { + uint32 charid = database.GetCharacterID(sep->arg[2]); + if(charid <= 0) + { + if(c->GetTarget() == NULL || (c->GetTarget() && !c->GetTarget()->IsClient())) + { + c->Message(0, "Character not found."); + return; + } + else + charid = c->GetTarget()->CastToClient()->CharacterID(); + } + + database.ListAllInstances(c, charid); + } + else + { + c->Message(0, "Invalid Argument."); + c->Message(0, "#instance usage:"); + c->Message(0, "#instance create zone_id version duration - Creates an instance of version 'version' in the " + "zone with id matching zone_id, will last for duration seconds."); + c->Message(0, "#instance destroy instance_id - Destroys the instance with id matching instance_id."); + c->Message(0, "#instance add instance_id player_name - adds the player 'player_name' to the instance " + "with id matching instance_id."); + c->Message(0, "#instance remove instance_id player_name - removes the player 'player_name' from the " + "instance with id matching instance_id."); + c->Message(0, "#instance list player_name - lists all the instances 'player_name' is apart of."); + return; + } +} + +void command_setstartzone(Client *c, const Seperator *sep) +{ + uint32 startzone = 0; + Client* target = NULL; + if(c->GetTarget() && c->GetTarget()->IsClient() && sep->arg[1][0] != 0) + target = c->GetTarget()->CastToClient(); + else { + c->Message(0, "Usage: (needs PC target) #setstartzone zonename"); + c->Message(0, "Optional Usage: Use '#setstartzone reset' or '#setstartzone 0' to clear a starting zone. A player can select a starting zone using /setstartcity"); + return; + } + + if(sep->IsNumber(1)) { + startzone = atoi(sep->arg[1]); + } + else if(strcasecmp(sep->arg[1],"reset") == 0) { + startzone = 0; + } + else { + startzone = database.GetZoneID(sep->arg[1]); + if(startzone == 0) { + c->Message(0, "Unable to locate zone '%s'", sep->arg[1]); + return; + } + } + + target->SetStartZone(startzone); +} + +void command_netstats(Client *c, const Seperator *sep) +{ + if(c) + { + if(c->GetTarget() && c->GetTarget()->IsClient()) + { + c->Message(0, "Sent:"); + c->Message(0, "Total: %u, per second: %u", c->GetTarget()->CastToClient()->Connection()->GetBytesSent(), + c->GetTarget()->CastToClient()->Connection()->GetBytesSentPerSecond()); + c->Message(0, "Recieved:"); + c->Message(0, "Total: %u, per second: %u", c->GetTarget()->CastToClient()->Connection()->GetBytesRecieved(), + c->GetTarget()->CastToClient()->Connection()->GetBytesRecvPerSecond()); + + } + else + { + c->Message(0, "Sent:"); + c->Message(0, "Total: %u, per second: %u", c->Connection()->GetBytesSent(), c->Connection()->GetBytesSentPerSecond()); + c->Message(0, "Recieved:"); + c->Message(0, "Total: %u, per second: %u", c->Connection()->GetBytesRecieved(), c->Connection()->GetBytesRecvPerSecond()); + } + } +} + +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. + 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. + c->Message(0, usage_string); + + return; + } + + char errbuf[MYSQL_ERRMSG_SIZE]; + char query[512]; + char line[256]; + uint32 col; + uint32 lastid; + MYSQL_RES *result; + MYSQL_ROW row; + int iObjectsFound = 0; + int len; + + Object* o = NULL; + Object_Struct od; + Door door; + Doors* doors; + Door_Struct* ds; + uint32 id = 0; + uint32 itemid = 0; + uint32 icon = 0; + uint32 instance = 0; + uint32 newid = 0; + uint16 radius; + EQApplicationPacket* app; + + 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; + + // 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[0] = atoi(row[col++]); + od.unknown008[1] = 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] == 0) // Unknown08 field is optional Size parameter for static objects + { + od.unknown008[0] = 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[0], od.unknown008[1], 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[0] = atoi(sep->arg[4 + col]); // Size specified + + if ((sep->argnum - col) > 4) + { + od.unknown008[1] = 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)) != NULL) + { + 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)) != NULL) + { + 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, NULL); + + // 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 + // 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[0] = atoi(sep->arg[4]); + o->SetObjectData(&od); + + if (od.unknown008[0] == 0) // 0 == unspecified == 100% + { + od.unknown008[0] = 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[0]); + } + 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.unknown008[1] = 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.unknown008[1]); + } + 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(NULL, &od.y, NULL); + } + + if (sep->argnum > 4) + { + od.z = atof(sep->arg[5]); + } + else + { + o->GetLocation(NULL, NULL, &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)) == NULL) + { + 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[0], od.unknown008[1], 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[0], od.unknown008[1], 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[0], od.unknown008[1], 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]) == 0) // unknown08 = optional size percentage + { + door.size = 100; + } + + switch (door.opentype = od.unknown008[1]) // 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(NULL, 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[0] = atoi(row[col++]); + od.unknown008[1] = 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, NULL); + entity_list.AddObject(o, true); + + c->Message(0, "Object %u reloaded from database.", id); + break; + default: // Unrecognized command + c->Message(0, usage_string); + break; + } +} + +// All new code added to command.cpp ought to be BEFORE this comment line. Do no append code to this file below the BOTS code block. +#ifdef BOTS +// Function delegate to support the command interface for Bots with the client. +void command_bot(Client *c, const Seperator *sep) { + Bot::ProcessBotCommands(c, sep); +} +#endif + +void command_raidloot(Client *c, const Seperator *sep) +{ + if(!sep->arg[1][0]) { + c->Message(0, "Usage: #raidloot [LEADER/GROUPLEADER/SELECTED/ALL]"); + return; + } + + Raid *r = c->GetRaid(); + if(r) + { + for(int x = 0; x < 72; ++x) + { + if(r->members[x].member == c) + { + if(r->members[x].IsRaidLeader == 0) + { + c->Message(0, "You must be the raid leader to use this command."); + } + else + { + break; + } + } + } + + if(strcasecmp(sep->arg[1], "LEADER") == 0) + { + c->Message(15, "Loot type changed to: 1"); + r->ChangeLootType(1); + } + else if(strcasecmp(sep->arg[1], "GROUPLEADER") == 0) + { + c->Message(15, "Loot type changed to: 2"); + r->ChangeLootType(2); + } + else if(strcasecmp(sep->arg[1], "SELECTED") == 0) + { + c->Message(15, "Loot type changed to: 3"); + r->ChangeLootType(3); + } + else if(strcasecmp(sep->arg[1], "ALL") == 0) + { + c->Message(15, "Loot type changed to: 4"); + r->ChangeLootType(4); + } + else + { + c->Message(0, "Usage: #raidloot [LEADER/GROUPLEADER/SELECTED/ALL]"); + } + } + else + { + c->Message(0, "You must be in a raid to use that command."); + } +} + +void command_emoteview(Client *c, const Seperator *sep) +{ + if(!c->GetTarget() || !c->GetTarget()->IsNPC()) + { + c->Message(0, "You must target a NPC to view their emotes."); + return; + } + + if(c->GetTarget() && c->GetTarget()->IsNPC()) + { + int count=0; + int emoteid = c->GetTarget()->CastToNPC()->GetNPCEmoteID(); + + LinkedListIterator iterator(zone->NPCEmoteList); + iterator.Reset(); + while(iterator.MoreElements()) + { + NPC_Emote_Struct* nes = iterator.GetData(); + if(emoteid == nes->emoteid) + { + c->Message(0, "EmoteID: %i Event: %i Type: %i Text: %s", nes->emoteid, nes->event_, nes->type, nes->text); + count++; + } + iterator.Advance(); + } + if (count == 0) + c->Message(0, "No emotes found."); + else + c->Message(0, "%i emote(s) found", count); + } +} + +void command_emotesearch(Client *c, const Seperator *sep) +{ + if (sep->arg[1][0] == 0) + c->Message(0, "Usage: #emotesearch [search string or emoteid]"); + else + { + const char *search_criteria=sep->argplus[1]; + int count=0; + + if (Seperator::IsNumber(search_criteria)) + { + uint16 emoteid = atoi(search_criteria); + LinkedListIterator iterator(zone->NPCEmoteList); + iterator.Reset(); + while(iterator.MoreElements()) + { + NPC_Emote_Struct* nes = iterator.GetData(); + if(emoteid == nes->emoteid) + { + c->Message(0, "EmoteID: %i Event: %i Type: %i Text: %s", nes->emoteid, nes->event_, nes->type, nes->text); + count++; + } + iterator.Advance(); + } + if (count == 0) + c->Message(0, "No emotes found."); + else + c->Message(0, "%i emote(s) found", count); + } + else + { + char sText[64]; + char sCriteria[515]; + strn0cpy(sCriteria, search_criteria, sizeof(sCriteria)); + strupr(sCriteria); + char* pdest; + + LinkedListIterator iterator(zone->NPCEmoteList); + iterator.Reset(); + while(iterator.MoreElements()) + { + NPC_Emote_Struct* nes = iterator.GetData(); + strn0cpy(sText, nes->text, sizeof(sText)); + strupr(sText); + pdest = strstr(sText, sCriteria); + if (pdest != NULL) + { + c->Message(0, "EmoteID: %i Event: %i Type: %i Text: %s", nes->emoteid, nes->event_, nes->type, nes->text); + count++; + } + if (count == 50) + break; + + iterator.Advance(); + } + if (count == 50) + c->Message(0, "50 emotes shown...too many results."); + else + c->Message(0, "%i emote(s) found", count); + } + } +} + +void command_reloademote(Client *c, const Seperator *sep) +{ + zone->NPCEmoteList.Clear(); + zone->LoadNPCEmotes(&zone->NPCEmoteList); + c->Message(0, "NPC emotes reloaded."); +} + +void command_globalview(Client *c, const Seperator *sep) +{ + NPC * npcmob = NULL; + + if(c->GetTarget() && c->GetTarget()->IsNPC()) + { + npcmob = c->GetTarget()->CastToNPC(); + QGlobalCache *npc_c = NULL; + QGlobalCache *char_c = NULL; + QGlobalCache *zone_c = NULL; + + if(npcmob) + npc_c = npcmob->GetQGlobals(); + + char_c = c->GetQGlobals(); + zone_c = zone->GetQGlobals(); + + std::list globalMap; + uint32 ntype = 0; + + if(npcmob) + ntype = npcmob->GetNPCTypeID(); + + if(npc_c) + { + QGlobalCache::Combine(globalMap, npc_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + if(char_c) + { + QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + if(zone_c) + { + QGlobalCache::Combine(globalMap, zone_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + std::list::iterator iter = globalMap.begin(); + uint32 gcount = 0; + + c->Message(0, "Name, Value"); + while(iter != globalMap.end()) + { + c->Message(0, "%s %s", (*iter).name.c_str(), (*iter).value.c_str()); + ++iter; + ++gcount; + } + c->Message(0, "%u globals loaded.", gcount); + } + else + { + QGlobalCache *char_c = NULL; + QGlobalCache *zone_c = NULL; + + char_c = c->GetQGlobals(); + zone_c = zone->GetQGlobals(); + + std::list globalMap; + uint32 ntype = 0; + + if(char_c) + { + QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + if(zone_c) + { + QGlobalCache::Combine(globalMap, zone_c->GetBucket(), ntype, c->CharacterID(), zone->GetZoneID()); + } + + std::list::iterator iter = globalMap.begin(); + uint32 gcount = 0; + + c->Message(0, "Name, Value"); + while(iter != globalMap.end()) + { + c->Message(0, "%s %s", (*iter).name.c_str(), (*iter).value.c_str()); + ++iter; + ++gcount; + } + c->Message(0, "%u globals loaded.", gcount); + } +} + +void command_distance(Client *c, const Seperator *sep) { + if(c && c->GetTarget()) { + Mob* target = c->GetTarget(); + + c->Message(0, "Your target, %s, is %1.1f units from you.", c->GetTarget()->GetName(), c->Dist(*target)); + } +} + +void command_cvs(Client *c, const Seperator *sep) +{ + if(c) + { + ServerPacket *pack = new ServerPacket(ServerOP_ClientVersionSummary, sizeof(ServerRequestClientVersionSummary_Struct)); + + ServerRequestClientVersionSummary_Struct *srcvss = (ServerRequestClientVersionSummary_Struct*)pack->pBuffer; + + strn0cpy(srcvss->Name, c->GetName(), sizeof(srcvss->Name)); + + worldserver.SendPacket(pack); + + safe_delete(pack); + + } +} + +void command_max_all_skills(Client *c, const Seperator *sep) +{ + if(c) + { + for(int i = 0; i <= HIGHEST_SKILL; ++i) + { + if(i >= SPECIALIZE_ABJURE && i <= SPECIALIZE_EVOCATION) + { + c->SetSkill((SkillType)i, 50); + } + else + { + int max_skill_level = database.GetSkillCap(c->GetClass(), (SkillType)i, c->GetLevel()); + c->SetSkill((SkillType)i, max_skill_level); + } + } + } +} + +void command_showbonusstats(Client *c, const Seperator *sep) +{ + if (c->GetTarget() == 0) + c->Message(0, "ERROR: No target!"); + else if (!c->GetTarget()->IsMob() && !c->GetTarget()->IsClient()) + c->Message(0, "ERROR: Target is not a Mob or Player!"); + else { + bool bAll = false; + if(sep->arg[1][0] == '\0' || strcasecmp(sep->arg[1], "all") == 0) + bAll = true; + if (bAll || (strcasecmp(sep->arg[1], "item")==0)) { + c->Message(0, "Target Item Bonuses:"); + c->Message(0, " Accuracy: %i%% Divine Save: %i%%",c->GetTarget()->GetItemBonuses().Accuracy, c->GetTarget()->GetItemBonuses().DivineSaveChance); + c->Message(0, " Flurry: %i%% HitChance: %i%%",c->GetTarget()->GetItemBonuses().FlurryChance, c->GetTarget()->GetItemBonuses().HitChance / 15); + } + if (bAll || (strcasecmp(sep->arg[1], "spell")==0)) { + c->Message(0, " Target Spell Bonuses:"); + c->Message(0, " Accuracy: %i%% Divine Save: %i%%",c->GetTarget()->GetSpellBonuses().Accuracy, c->GetTarget()->GetSpellBonuses().DivineSaveChance); + c->Message(0, " Flurry: %i%% HitChance: %i%% ",c->GetTarget()->GetSpellBonuses().FlurryChance, c->GetTarget()->GetSpellBonuses().HitChance / 15); + int deathsaveslot = c->GetTarget()->GetBuffSlotFromType(SE_DeathSave); + int dschance = deathsaveslot >= 0 ? c->GetTarget()->GetBuffs()[deathsaveslot].deathSaveSuccessChance : 0; + c->Message(0, " Death Save: %i%%",dschance); + } + c->Message(0, " Effective Casting Level: %i",c->GetTarget()->GetCasterLevel(0)); + } +} + +void command_reloadallrules(Client *c, const Seperator *sep) +{ + if(c) + { + ServerPacket *pack = new ServerPacket(ServerOP_ReloadRules, 0); + worldserver.SendPacket(pack); + c->Message(13, "Successfully sent the packet to world to reload rules globally. (including world)"); + safe_delete(pack); + + } +} + +void command_reloadworldrules(Client *c, const Seperator *sep) +{ + if(c) + { + ServerPacket *pack = new ServerPacket(ServerOP_ReloadRulesWorld, 0); + worldserver.SendPacket(pack); + c->Message(13, "Successfully sent the packet to world to reload rules. (only world)"); + safe_delete(pack); + } +} + +void command_camerashake(Client *c, const Seperator *sep) +{ + if(c) + { + if(sep->arg[1][0] && sep->arg[2][0]) + { + ServerPacket *pack = new ServerPacket(ServerOP_CameraShake, sizeof(ServerCameraShake_Struct)); + memset(pack->pBuffer, 0, sizeof(pack->pBuffer)); + ServerCameraShake_Struct* scss = (ServerCameraShake_Struct*) pack->pBuffer; + scss->duration = atoi(sep->arg[1]); + scss->intensity = atoi(sep->arg[2]); + worldserver.SendPacket(pack); + c->Message(13, "Successfully sent the packet to world! Shake it, world, shake it!"); + safe_delete(pack); + } + else { + c->Message(13, "Usage -- #camerashake [duration], [intensity [1-10])"); + } + } + return; +} + +void command_disarmtrap(Client *c, const Seperator *sep) +{ + Mob * target = c->GetTarget(); + if(target->IsNPC()) + { + if(c->HasSkill(DISARM_TRAPS)) + { + if(c->DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + c->Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + c->HandleLDoNDisarm(target->CastToNPC(), c->GetSkill(DISARM_TRAPS), LDoNTypeMechanical); + } + else + c->Message(13, "You do not have the disarm trap skill."); + } +} + +void command_sensetrap(Client *c, const Seperator *sep) +{ + Mob * target = c->GetTarget(); + if(target->IsNPC()) + { + if(c->HasSkill(SENSE_TRAPS)) + { + if(c->DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + c->Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + c->HandleLDoNSenseTraps(target->CastToNPC(), c->GetSkill(SENSE_TRAPS), LDoNTypeMechanical); + } + else + c->Message(13, "You do not have the sense traps skill."); + } +} + +void command_picklock(Client *c, const Seperator *sep) +{ + Mob * target = c->GetTarget(); + if(target->IsNPC()) + { + if(c->HasSkill(PICK_LOCK)) + { + if(c->DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + c->Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + c->HandleLDoNPickLock(target->CastToNPC(), c->GetSkill(PICK_LOCK), LDoNTypeMechanical); + } + else + c->Message(13, "You do not have the pick locks skill."); + } +} + +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\""); + } + 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"); + c->Message(0, "To use 'like \"%%something%%\" replace the %% with #"); + 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"); + } + if ( strcasecmp( sep->arg[1], "query" ) == 0 ) { + ///Parse switches here + int argnum = 3; bool Options = false, Optionh = false; bool Fail = 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; + } + ++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(), '#', '%'); + + 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('#'); + } + + MsgText << "---Running query: '" << QueryText << "'"; + c->Message (15, MsgText.str().c_str()); + MsgText.str(""); + + MYSQL_ROW row; + while ((row = mysql_fetch_row(result))) { + + 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] : "NULL") << "] "; + } + 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 + 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(""); + } + } + } +} + +void command_xtargets(Client *c, const Seperator *sep) +{ + Client *t; + + if(c->GetTarget() && c->GetTarget()->IsClient()) + t = c->GetTarget()->CastToClient(); + else + t = c; + + if(sep->arg[1][0]) + { + uint8 NewMax = atoi(sep->arg[1]); + + if((NewMax < 5) || (NewMax > XTARGET_HARDCAP)) + { + c->Message(13, "Number of XTargets must be between 5 and %i", XTARGET_HARDCAP); + return; + } + t->SetMaxXTargets(NewMax); + c->Message(0, "Max number of XTargets set to %i", NewMax); + } + else + t->ShowXTargets(c); +} + +void command_printquestitems(Client *c, const Seperator *sep) +{ + if (c->GetTarget() != 0) + { + if ( c->GetTarget()->IsNPC() ) + c->GetTarget()->CastToNPC()->PrintOutQuestItems(c); + else + c->Message(13,"Pick a NPC target."); + } + else + c->Message(13,"Pick a NPC target."); +} + +void command_clearquestitems(Client *c, const Seperator *sep) +{ + if (c->GetTarget() != 0) + { + if ( c->GetTarget()->IsNPC() ) + { + c->GetTarget()->CastToNPC()->ClearQuestLists(); + c->Message(5,"Quest item list cleared."); + } + else + c->Message(13,"Pick a NPC target."); + } + else + c->Message(13,"Pick a NPC target."); +} + +void command_zopp(Client *c, const Seperator *sep) +{ // - Owner only command..non-targetable to eliminate malicious or mischievious activities. + if (!c) + return; + else if (sep->argnum < 3 || sep->argnum > 4) + c->Message(0, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); + else if (!strcasecmp(sep->arg[1], "trade") == 0 && !strcasecmp(sep->arg[1], "t") == 0 && !strcasecmp(sep->arg[1], "summon") == 0 && !strcasecmp(sep->arg[1], "s") == 0) + c->Message(0, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); + else if (!sep->IsNumber(2) || !sep->IsNumber(3) || (sep->argnum == 4 && !sep->IsNumber(4))) + c->Message(0, "Usage: #zopp [trade/summon] [slot id] [item id] [*charges]"); + else { + ItemPacketType packettype; + + if (strcasecmp(sep->arg[1], "trade") == 0 || strcasecmp(sep->arg[1], "t") == 0) { + packettype = ItemPacketTrade; + } + else { + packettype = ItemPacketSummonItem; + } + + int16 slotid = atoi(sep->arg[2]); + uint32 itemid = atoi(sep->arg[3]); + int16 charges = sep->argnum == 4 ? atoi(sep->arg[4]) : 1; // defaults to 1 charge if not specified + + const Item_Struct* FakeItem = database.GetItem(itemid); + + if (!FakeItem) { + c->Message(13, "Error: Item [%u] is not a valid item id.", itemid); + return; + } + + if (database.GetItemStatus(itemid) > c->Admin()) { + c->Message(13, "Error: Insufficient status to use this command."); + return; + } + + if (charges < 0 || charges > FakeItem->StackSize) { + c->Message(13, "Warning: The specified charge count does not meet expected criteria!"); + c->Message(0, "Processing request..results may cause unpredictable behavior."); + } + + ItemInst* FakeItemInst = database.CreateItem(FakeItem, charges); + c->SendItemPacket(slotid, FakeItemInst, packettype); + c->Message(0, "Sending zephyr op packet to client - [%s] %s (%u) with %i %s to slot %i.", packettype==ItemPacketTrade?"Trade":"Summon", FakeItem->Name, itemid, charges, abs(charges==1)?"charge":"charges", slotid); + safe_delete(FakeItemInst); + } +} + +void command_augmentitem(Client *c, const Seperator *sep) +{ + if (!c) + return; + + 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() != NULL) + Object::HandleAugmentation(c, in_augment, c->GetTradeskillObject()); + safe_delete(in_augment); +} \ No newline at end of file diff --git a/zone/command.h b/zone/command.h new file mode 100644 index 000000000..6341247f9 --- /dev/null +++ b/zone/command.h @@ -0,0 +1,351 @@ +/* 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 COMMAND_H +#define COMMAND_H + +#include "../common/seperator.h" +#include "../common/EQStream.h" +#include "client.h" + +#define COMMAND_CHAR '#' +#define CMDALIASES 5 + +typedef void (*CmdFuncPtr)(Client *,const Seperator *); + +// this is a command list item +/*struct cl_struct +{ + char *command[CMDALIASES]; // the command(s) + char *desc; // description of command + CmdFuncPtr function; // the function to call + int access; // the required 'status' level + + struct cl_struct *next; // linked list +}; + +extern struct cl_struct *commandlist; // the head of the list +*/ + +typedef struct { + const char *command[CMDALIASES]; // the command(s) + int access; + const char *desc; // description of command + CmdFuncPtr function; //null means perl function +} CommandRecord; + +extern int (*command_dispatch)(Client *,char const*); +extern int commandcount; // number of commands loaded + +// the command system: +int command_init(void); +void command_deinit(void); +int command_add(const char *command_string, const char *desc, int access, CmdFuncPtr function); +int command_notavail(Client *c, const char *message); +int command_realdispatch(Client *c, char const *message); +void command_logcommand(Client *c, const char *message); +int command_add_perl(const char *command_string, const char *desc, int access); +void command_clear_perl(); + +//commands +void command_resetaa(Client* c,const Seperator *sep); +void command_bind(Client* c,const Seperator *sep); +void command_sendop(Client *c, const Seperator *sep); +void command_optest(Client *c, const Seperator *sep); +void command_setstat(Client *c, const Seperator *sep); +void command_incstat(Client *c, const Seperator *sep); +void command_help(Client *c, const Seperator *sep); +void command_version(Client *c, const Seperator *sep); +void command_eitem(Client *c, const Seperator *sep); +void command_setfaction(Client *c, const Seperator *sep); +void command_serversidename(Client *c, const Seperator *sep); +void command_testspawnkill(Client *c, const Seperator *sep); +void command_testspawn(Client *c, const Seperator *sep); +void command_wc(Client *c, const Seperator *sep); +void command_numauths(Client *c, const Seperator *sep); +void command_setanim(Client *c, const Seperator *sep); +void command_connectworldserver(Client *c, const Seperator *sep); +void command_serverinfo(Client *c, const Seperator *sep); +void command_crashtest(Client *c, const Seperator *sep); +void command_getvariable(Client *c, const Seperator *sep); +void command_chat(Client *c, const Seperator *sep); +void command_showpetspell(Client *c, const Seperator *sep); +void command_ipc(Client *c, const Seperator *sep); +void command_npcloot(Client *c, const Seperator *sep); +void command_log(Client *c, const Seperator *sep); +void command_gm(Client *c, const Seperator *sep); +void command_summon(Client *c, const Seperator *sep); +void command_zone(Client *c, const Seperator *sep); +void command_zone_instance(Client *c, const Seperator *sep); +void command_peqzone(Client *c, const Seperator *sep); +void command_showbuffs(Client *c, const Seperator *sep); +void command_movechar(Client *c, const Seperator *sep); +void command_viewpetition(Client *c, const Seperator *sep); +void command_petitioninfo(Client *c, const Seperator *sep); +void command_delpetition(Client *c, const Seperator *sep); +void command_listnpcs(Client *c, const Seperator *sep); +void command_date(Client *c, const Seperator *sep); +void command_timezone(Client *c, const Seperator *sep); +void command_synctod(Client *c, const Seperator *sep); +void command_invul(Client *c, const Seperator *sep); +void command_hideme(Client *c, const Seperator *sep); +void command_emote(Client *c, const Seperator *sep); +void command_fov(Client *c, const Seperator *sep); +void command_manastat(Client *c, const Seperator *sep); +void command_npcstats(Client *c, const Seperator *sep); +void command_zclip(Client *c, const Seperator *sep); +void command_npccast(Client *c, const Seperator *sep); +void command_zstats(Client *c, const Seperator *sep); +void command_permaclass(Client *c, const Seperator *sep); +void command_permarace(Client *c, const Seperator *sep); +void command_permagender(Client *c, const Seperator *sep); +void command_weather(Client *c, const Seperator *sep); +void command_zheader(Client *c, const Seperator *sep); +void command_zsky(Client *c, const Seperator *sep); +void command_zcolor(Client *c, const Seperator *sep); +void command_spon(Client *c, const Seperator *sep); +void command_spoff(Client *c, const Seperator *sep); +void command_itemtest(Client *c, const Seperator *sep); +void command_gassign(Client *c, const Seperator *sep); +void command_setitemstatus(Client *c, const Seperator *sep); +void command_ai(Client *c, const Seperator *sep); +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); +void command_setlsinfo(Client *c, const Seperator *sep); +void command_grid(Client *c, const Seperator *sep); +void command_wp(Client *c, const Seperator *sep); +void command_iplookup(Client *c, const Seperator *sep); +void command_size(Client *c, const Seperator *sep); +void command_mana(Client *c, const Seperator *sep); +void command_flymode(Client *c, const Seperator *sep); +void command_showskills(Client *c, const Seperator *sep); +void command_findspell(Client *c, const Seperator *sep); +void command_castspell(Client *c, const Seperator *sep); +void command_setlanguage(Client *c, const Seperator *sep); +void command_setskill(Client *c, const Seperator *sep); +void command_setskillall(Client *c, const Seperator *sep); +void command_race(Client *c, const Seperator *sep); +void command_gender(Client *c, const Seperator *sep); +void command_makepet(Client *c, const Seperator *sep); +void command_level(Client *c, const Seperator *sep); +void command_spawn(Client *c, const Seperator *sep); +void command_texture(Client *c, const Seperator *sep); +void command_npctypespawn(Client *c, const Seperator *sep); +void command_heal(Client *c, const Seperator *sep); +void command_appearance(Client *c, const Seperator *sep); +void command_charbackup(Client *c, const Seperator *sep); +void command_nukeitem(Client *c, const Seperator *sep); +void command_peekinv(Client *c, const Seperator *sep); +void command_findnpctype(Client *c, const Seperator *sep); +void command_findzone(Client *c, const Seperator *sep); +void command_viewnpctype(Client *c, const Seperator *sep); +void command_reloadqst(Client *c, const Seperator *sep); +void command_reloadworld(Client *c, const Seperator *sep); +void command_reloadzps(Client *c, const Seperator *sep); +void command_zoneshutdown(Client *c, const Seperator *sep); +void command_zonebootup(Client *c, const Seperator *sep); +void command_kick(Client *c, const Seperator *sep); +void command_attack(Client *c, const Seperator *sep); +void command_lock(Client *c, const Seperator *sep); +void command_unlock(Client *c, const Seperator *sep); +void command_motd(Client *c, const Seperator *sep); +void command_listpetition(Client *c, const Seperator *sep); +void command_equipitem(Client *c, const Seperator *sep); +void command_zonelock(Client *c, const Seperator *sep); +void command_corpse(Client *c, const Seperator *sep); +void command_fixmob(Client *c, const Seperator *sep); +void command_gmspeed(Client *c, const Seperator *sep); +void command_title(Client *c, const Seperator *sep); +void command_titlesuffix(Client *c, const Seperator *sep); +void command_spellinfo(Client *c, const Seperator *sep); +void command_lastname(Client *c, const Seperator *sep); +void command_memspell(Client *c, const Seperator *sep); +void command_save(Client *c, const Seperator *sep); +void command_showstats(Client *c, const Seperator *sep); +void command_mystats(Client *c, const Seperator *sep); +void command_myskills(Client *c, const Seperator *sep); +void command_depop(Client *c, const Seperator *sep); +void command_depopzone(Client *c, const Seperator *sep); +void command_repop(Client *c, const Seperator *sep); +void command_spawnstatus(Client *c, const Seperator *sep); +void command_nukebuffs(Client *c, const Seperator *sep); +void command_zuwcoords(Client *c, const Seperator *sep); +void command_zunderworld(Client *c, const Seperator *sep); +void command_zsafecoords(Client *c, const Seperator *sep); +void command_freeze(Client *c, const Seperator *sep); +void command_unfreeze(Client *c, const Seperator *sep); +void command_pvp(Client *c, const Seperator *sep); +void command_setxp(Client *c, const Seperator *sep); +void command_setpvppoints(Client *c, const Seperator *sep); +void command_name(Client *c, const Seperator *sep); +void command_tempname(Client *c, const Seperator *sep); +void command_npcspecialattk(Client *c, const Seperator *sep); +void command_kill(Client *c, const Seperator *sep); +void command_haste(Client *c, const Seperator *sep); +void command_damage(Client *c, const Seperator *sep); +void command_zonespawn(Client *c, const Seperator *sep); +void command_npcspawn(Client *c, const Seperator *sep); +void command_spawnfix(Client *c, const Seperator *sep); +void command_loc(Client *c, const Seperator *sep); +void command_goto(Client *c, const Seperator *sep); +#ifdef BUGTRACK +void command_bug(Client *c, const Seperator *sep); +#endif +void command_iteminfo(Client *c, const Seperator *sep); +void command_uptime(Client *c, const Seperator *sep); +void command_flag(Client *c, const Seperator *sep); +void command_time(Client *c, const Seperator *sep); +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); +void command_helm(Client *c, const Seperator *sep); +void command_hair(Client *c, const Seperator *sep); +void command_haircolor(Client *c, const Seperator *sep); +void command_beard(Client *c, const Seperator *sep); +void command_beardcolor(Client *c, const Seperator *sep); +void command_tattoo(Client *c, const Seperator *sep); +void command_heritage(Client *c, const Seperator *sep); +void command_details(Client *c, const Seperator *sep); +void command_scribespells(Client *c, const Seperator *sep); +void command_unscribespells(Client *c, const Seperator *sep); +void command_wpinfo(Client *c, const Seperator *sep); +void command_wpadd(Client *c, const Seperator *sep); +void command_interrupt(Client *c, const Seperator *sep); +void command_d1(Client *c, const Seperator *sep); +void command_summonitem(Client *c, const Seperator *sep); +void command_giveitem(Client *c, const Seperator *sep); +void command_givemoney(Client *c, const Seperator *sep); +void command_itemsearch(Client *c, const Seperator *sep); +void command_datarate(Client *c, const Seperator *sep); +void command_setaaxp(Client *c, const Seperator *sep); +void command_setaapts(Client *c, const Seperator *sep); +void command_setcrystals(Client *c, const Seperator *sep); +void command_stun(Client *c, const Seperator *sep); +void command_ban(Client *c, const Seperator *sep); +void command_suspend(Client *c, const Seperator *sep); +void command_ipban(Client *c, const Seperator *sep); +void command_oocmute(Client *c, const Seperator *sep); +void command_revoke(Client *c, const Seperator *sep); +void command_checklos(Client *c, const Seperator *sep); +void command_set_adventure_points(Client *c, const Seperator *sep); +void command_npcsay(Client *c, const Seperator *sep); +void command_npcshout(Client *c, const Seperator *sep); +void command_npcemote(Client *c, const Seperator *sep); +void command_npcedit(Client *c, const Seperator *sep); +void command_timers(Client *c, const Seperator *sep); +void command_undye(Client *c, const Seperator *sep); +void command_undyeme(Client *c, const Seperator *sep); +void command_hp(Client *c, const Seperator *sep); +void command_ginfo(Client *c, const Seperator *sep); +void command_logs(Client *c, const Seperator *sep); +void command_nologs(Client *c, const Seperator *sep); +void command_logsql(Client *c, const Seperator *sep); +void command_qglobal(Client *c, const Seperator *sep); +void command_fear(Client *c, const Seperator *sep); +void command_path(Client *c, const Seperator *sep); +void command_ginfo(Client *c, const Seperator *sep); +void command_opcode(Client *c, const Seperator *sep); +void command_aggro(Client *c, const Seperator *sep); +void command_hatelist(Client *c, const Seperator *sep); +void command_aggrozone(Client *c, const Seperator *sep); +void command_reloadstatic(Client *c, const Seperator *sep); +void command_flags(Client *c, const Seperator *sep); +void command_flagedit(Client *c, const Seperator *sep); +void command_mlog(Client *c, const Seperator *sep); +void command_serverrules(Client *c, const Seperator *sep); +void command_acceptrules(Client *c, const Seperator *sep); +void command_guildcreate(Client *c, const Seperator *sep); +void command_guildapprove(Client *c, const Seperator *sep); +void command_guildlist(Client *c, const Seperator *sep); +void command_rules(Client *c, const Seperator *sep); +void command_task(Client *c, const Seperator *sep); +void command_reloadtitles(Client *c, const Seperator *sep); +void command_altactivate(Client *c, const Seperator *sep); +void command_refundaa(Client *c, const Seperator *sep); +void command_traindisc(Client *c, const Seperator *sep); +void command_deletegraveyard(Client *c, const Seperator *sep); +void command_setgraveyard(Client *c, const Seperator *sep); +void command_getplayerburriedcorpsecount(Client *c, const Seperator *sep); +void command_summonburriedplayercorpse(Client *c, const Seperator *sep); +void command_unscribespell(Client *c, const Seperator *sep); +void command_scribespell(Client *c, const Seperator *sep); +void command_refreshgroup(Client *c, const Seperator *sep); +void command_advnpcspawn(Client *c, const Seperator *sep); +void command_modifynpcstat(Client *c, const Seperator *sep); +void command_instance(Client *c, const Seperator *sep); +void command_setstartzone(Client *c, const Seperator *sep); +void command_netstats(Client *c, const Seperator *sep); +void command_object(Client* c, const Seperator *sep); +void command_raidloot(Client* c, const Seperator *sep); +void command_globalview(Client* c, const Seperator *sep); +void command_emoteview(Client* c, const Seperator *sep); +void command_reloademote(Client* c, const Seperator *sep); +void command_emotesearch(Client* c, const Seperator *sep); +void command_distance(Client *c, const Seperator *sep); +void command_cvs(Client *c, const Seperator *sep); +void command_max_all_skills(Client *c, const Seperator *sep); +void command_showbonusstats(Client *c, const Seperator *sep); +void command_reloadallrules(Client *c, const Seperator *sep); +void command_reloadworldrules(Client *c, const Seperator *sep); +void command_reloadlevelmods(Client *c, const Seperator *sep); +void command_camerashake(Client *c, const Seperator *sep); +void command_disarmtrap(Client *c, const Seperator *sep); +void command_sensetrap(Client *c, const Seperator *sep); +void command_picklock(Client *c, const Seperator *sep); +void command_qtest(Client *c, const Seperator *sep); +void command_mysql(Client *c, const Seperator *sep); +void command_xtargets(Client *c, const Seperator *sep); +void command_printquestitems(Client *c, const Seperator *sep); +void command_clearquestitems(Client *c, const Seperator *sep); +void command_zopp(Client *c, const Seperator *sep); +void command_augmentitem(Client *c, const Seperator *sep); + +#ifdef EMBPERL +void command_embperl_plugin(Client *c, const Seperator *sep); +void command_embperl_eval(Client *c, const Seperator *sep); +void command_reloadpl(Client *c, const Seperator *sep); +#endif + +#ifdef EQPROFILE +void command_profiledump(Client *c, const Seperator *sep); +void command_profilereset(Client *c, const Seperator *sep); +#endif + +#ifdef PACKET_PROFILER +void command_packetprofile(Client *c, const Seperator *sep); +#endif + +#ifdef BOTS +#include "bot.h" +void command_bot(Client*c, const Seperator *sep); +#endif + +#endif + diff --git a/zone/common.h b/zone/common.h new file mode 100644 index 000000000..a5bb7c530 --- /dev/null +++ b/zone/common.h @@ -0,0 +1,486 @@ +#ifndef __EQEMU_ZONE_COMMON_H +#define __EQEMU_ZONE_COMMON_H + +#include "../common/types.h" +#include "spdat.h" + +#define HIGHEST_RESIST 9 //Max resist type value + +/* solar: macros for IsAttackAllowed, IsBeneficialAllowed */ +#define _CLIENT(x) (x && x->IsClient() && !x->CastToClient()->IsBecomeNPC()) +#define _NPC(x) (x && x->IsNPC() && !x->CastToMob()->GetOwnerID()) +#define _BECOMENPC(x) (x && x->IsClient() && x->CastToClient()->IsBecomeNPC()) +#define _CLIENTCORPSE(x) (x && x->IsCorpse() && x->CastToCorpse()->IsPlayerCorpse() && !x->CastToCorpse()->IsBecomeNPCCorpse()) +#define _NPCCORPSE(x) (x && x->IsCorpse() && (x->CastToCorpse()->IsNPCCorpse() || x->CastToCorpse()->IsBecomeNPCCorpse())) +#define _CLIENTPET(x) (x && x->CastToMob()->GetOwner() && x->CastToMob()->GetOwner()->IsClient()) +#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()) + + +//LOS Parameters: +#define HEAD_POSITION 0.9f //ratio of GetSize() where NPCs see from +#define SEE_POSITION 0.5f //ratio of GetSize() where NPCs try to see for LOS +#define CHECK_LOS_STEP 1.0f + +#define MAX_SHIELDERS 2 //I dont know if this is based on a client limit + +#define ARCHETYPE_HYBRID 1 +#define ARCHETYPE_CASTER 2 +#define ARCHETYPE_MELEE 3 + +#define CON_GREEN 2 +#define CON_LIGHTBLUE 18 +#define CON_BLUE 4 +#define CON_WHITE 20 +#define CON_YELLOW 15 +#define CON_RED 13 + +//Spell specialization parameters, not sure of a better place for them +#define SPECIALIZE_FIZZLE 11 //% fizzle chance reduce at 200 specialized +#define SPECIALIZE_MANA_REDUCE 12 //% mana cost reduction at 200 specialized + +//these are large right now because the x,y,z coords of the zone +//lines do not make a lot of sense +//Maximum distance from a zone point given that the request didnt +//know what zone that the line was for +#define ZONEPOINT_NOZONE_RANGE 40000.0f +//Maximum distance from a zone point if zone was specified +#define ZONEPOINT_ZONE_RANGE 40000.0f + +typedef enum { //focus types + focusSpellHaste = 1, + focusSpellDuration, + focusRange, + focusReagentCost, + focusManaCost, + focusImprovedHeal, + focusImprovedDamage, + focusImprovedDOT, //i dont know about this... + focusImprovedDamage2, + focusImprovedUndeadDamage, + focusPetPower, + focusResistRate, + focusSpellHateMod, + focusTriggerOnCast, + focusSpellVulnerability, + focusTwincast, + focusSympatheticProc, + focusSpellDamage, + focusFF_Damage_Amount, + focusSpellDurByTic, + focusSwarmPetDuration, + focusReduceRecastTime, + focusBlockNextSpell, + focusHealRate, + focusAdditionalDamage, + focusSpellEffectiveness, + focusIncreaseNumHits, + focusCriticalHealRate, + focusAdditionalHeal2, + focusAdditionalHeal, +} focusType; //Any new FocusType needs to be added to the Mob::IsFocus function +#define HIGHEST_FOCUS focusAdditionalHeal //Should always be last focusType in enum + + +/* +Used: +b,d,f,g,j,m,n,o,p,r,t +A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,Q,R,S,T,U,W,Y + +Unused: +a,c,e,h,k,l,q,s,u,v,w,x,y,z +P,V,X +*/ + +enum { + SPECATK_NONE = 0, + SPECATK_SUMMON, // S + SPECATK_ENRAGE, // E + SPECATK_RAMPAGE, // R + SPECATK_AREA_RAMPAGE, // r + SPECATK_FLURRY, // F + SPECATK_TRIPLE, // T + SPECATK_QUAD, // Q + SPECATK_INNATE_DW, // L + SPECATK_BANE, // b + SPECATK_MAGICAL, // m + SPECATK_RANGED_ATK, // Y + UNSLOWABLE, // U + UNMEZABLE, // M + UNCHARMABLE, // C + UNSTUNABLE, // N + UNSNAREABLE, // I + UNFEARABLE, // D + UNDISPELLABLE, // K + IMMUNE_MELEE, // A + IMMUNE_MAGIC, // B + IMMUNE_FLEEING, // f + IMMUNE_MELEE_EXCEPT_BANE, // O + IMMUNE_MELEE_NONMAGICAL, // W + IMMUNE_AGGRO, // H - Won't aggro, ever. + IMMUNE_AGGRO_ON, // G - Immune to being aggroed + IMMUNE_CASTING_FROM_RANGE, // g + IMMUNE_FEIGN_DEATH, // d + IMMUNE_TAUNT, // i + NPC_TUNNELVISION, // t + NPC_NO_BUFFHEAL_FRIENDS, // n + IMMUNE_PACIFY, // p + LEASH, // J - Dispell, wipe agro && return to spawn + TETHER, // j - Return to spawn + DESTRUCTIBLE_OBJECT, // o - This is only for destructible objects + NO_HARM_FROM_CLIENT, // Z - This is to prevent attacking NPC's period for clients + SPECATK_MAXNUM +}; + +typedef enum { //fear states + fearStateNotFeared = 0, + fearStateRunning, //I am running, hoping to find a grid at my WP + fearStateRunningForever, //can run straight until spell ends + fearStateGrid, //I am allready on a fear grid + fearStateStuck //I cannot move somehow... +} FearState; + +enum { FlyMode0 = 0, FlyMode1 = 1, Flymode2 = 2, FlyMode3 = 3 }; + +struct TradeEntity; +class Trade; +enum TradeState { + TradeNone, + Trading, + TradeAccepted, + TradeCompleting +}; + +//this is our internal representation of the BUFF struct, can put whatever we want in it +struct Buffs_Struct { + uint16 spellid; + uint8 casterlevel; + uint16 casterid; // Maybe change this to a pointer sometime, but gotta make sure it's 0'd when it no longer points to anything + char caster_name[64]; + int32 ticsremaining; + uint32 counters; + uint32 numhits; //the number of physical hits this buff can take before it fades away, lots of druid armor spells take advantage of this mixed with powerful effects + uint32 melee_rune; + uint32 magic_rune; + uint8 deathSaveSuccessChance; + uint8 deathsaveCasterAARank; + bool persistant_buff; + bool client; //True if the caster is a client + bool UpdateClient; +}; + +struct StatBonuses { + int16 AC; + int32 HP; + int32 HPRegen; + int32 MaxHP; + int32 ManaRegen; + int32 EnduranceRegen; + int32 Mana; + int32 Endurance; + int16 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; + uint16 DamageShieldSpellID; + int DamageShield; // this is damage done to mobs that attack this + DmgShieldType DamageShieldType; + int SpellDamageShield; + int SpellShield; + int ReverseDamageShield; // this is damage done to the mob when it attacks + uint16 ReverseDamageShieldSpellID; + DmgShieldType ReverseDamageShieldType; + int movementspeed; + int16 haste; + int16 hastetype2; + int16 hastetype3; + int16 inhibitmelee; + float AggroRange; // when calculate just replace original value with this + float AssistRange; + int16 skillmod[HIGHEST_SKILL+1]; + int effective_casting_level; + int reflect_chance; // chance to reflect incoming spell + uint16 singingMod; + uint16 brassMod; + uint16 percussionMod; + uint16 windMod; + uint16 stringedMod; + int8 hatemod; + int32 EnduranceReduction; + + int16 StrikeThrough; // PoP: Strike Through % + int16 MeleeMitigation; //i = Shielding + int16 CriticalHitChance[HIGHEST_SKILL+2]; //i + int16 CriticalSpellChance; //i + int16 SpellCritDmgIncrease; //i + int16 DotCritDmgIncrease; //i + int16 CriticalHealChance; //i + int16 CriticalHealOverTime; //i + int16 CriticalDoTChance; //i + int16 CrippBlowChance; // + int16 AvoidMeleeChance; //AvoidMeleeChance/10 == % chance i = Avoidance + int16 RiposteChance; //i + int16 DodgeChance; //i + int16 ParryChance; //i + int16 DualWieldChance; //i + int16 DoubleAttackChance; //i + int16 TripleAttackChance; //i + int16 ResistSpellChance; //i + int16 ResistFearChance; //i + bool Fearless; //i + bool IsFeared; //i + int16 StunResist; //i + int16 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 MinDamageModifier[HIGHEST_SKILL+2]; //i + int16 ProcChance; // ProcChance/10 == % increase i = CombatEffects + 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 + int8 MeleeLifetap; //i + int16 HealRate; // Spell effect that influences effectiveness of heals + int16 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. + 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 + 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 + int XPRateMod; //i + int HPPercCap; //Spell effect that limits you to being healed/regening beyond a % of your max + int ManaPercCap; // ^^ + int EndPercCap; // ^^ + 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 + uint16 SpellProcChance; // chance to proc from sympathetic spell effects + uint16 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. + bool NegateEffects; // Check if you contain a buff with negate effect. (only spellbonuses) + int16 SkillDamageAmount2[HIGHEST_SKILL+2]; // Adds skill specific damage + uint16 NegateAttacks[2]; // 0 = bool HasEffect 1 = Buff Slot + uint16 MitigateMeleeRune[2]; // 0 = Mitigation value 1 = Buff Slot + uint16 MitigateSpellRune[2]; // 0 = Mitigation value 1 = Buff Slot + 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) + //bool AbsorbMagicAtt; // Magic Rune *Need to be implemented for NegateEffect + //bool MeleeRune; // Melee Rune *Need to be implemented for NegateEffect + + // 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 + 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) + 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. + uint8 SeeInvis; // See Invs. + uint8 TripleBackstab; // Chance to triple backstab + bool FrontalBackstabMinDmg; // Allow frontal backstabs for min damage + uint8 FrontalBackstabChance; // Chance to backstab from the front for full damage + uint8 ConsumeProjectile; // Chance to not consume arrow. + uint16 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 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. + 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 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= ???) +}; + +typedef struct +{ + uint16 spellID; + uint16 chance; + uint16 base_spellID; +} tProc; + +struct Shielders_Struct { + uint32 shielder_id; + uint16 shielder_bonus; +}; + +//eventually turn this into a typedef and +//make DoAnim take it instead of int, to enforce its use. +enum { //type arguments to DoAnim + animKick = 1, + animPiercing = 2, //might be piercing? + anim2HSlashing = 3, + anim2HWeapon = 4, + anim1HWeapon = 5, + animDualWield = 6, + animTailRake = 7, //slam & Dpunch too + animHand2Hand = 8, + animShootBow = 9, + animRoundKick = 11, + animSwarmAttack = 20, //dunno about this one.. + animFlyingKick = 45, + animTigerClaw = 46, + animEagleStrike = 47, + +}; + + +typedef enum { + petFamiliar, //only listens to /pet get lost + petAnimation, //does not listen to any commands + petOther, + petCharmed, + petNPCFollow, + petHatelist //remain active as long something is on the hatelist. Don't listen to any commands +} PetType; + +typedef enum { + SingleTarget, // causes effect to spell_target + AETarget, // causes effect in aerange of target + target + AECaster, // causes effect in aerange of 'this' + GroupSpell, // causes effect to caster + target's group + CAHateList, // causes effect to all people on caster's hate list within some range + DirectionalAE, + CastActUnknown +} CastAction_type; + + +struct MercType { + uint32 Type; + uint32 ClientVersion; +}; + +struct MercData { + uint32 MercTemplateID; + uint32 MercType; // From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) + uint32 MercSubType; // From dbstr_us.txt - 330020105^23^Race: Guktan
Type: Healer
Confidence: High
Proficiency: Apprentice, Tier V... + uint32 CostFormula; // To determine cost to client + uint32 ClientVersion; // Only send valid mercs per expansion + uint32 NPCID; +}; + +class ItemInst; +class Mob; +// All data associated with a single trade +class Trade +{ +public: + Trade(Mob* in_owner); + virtual ~Trade(); + + void Reset(); + void SetTradeCash(uint32 in_pp, uint32 in_gp, uint32 in_sp, uint32 in_cp); + + // Initiate a trade with another mob + // Also puts other mob into trader mode with this mob + void Start(uint32 mob_id, bool initiate_with=true); + + // Mob the owner is trading with + 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); + + // Audit trade + void LogTrade(); + + // Debug only method + #if (EQDEBUG >= 9) + void DumpTrade(); + #endif + +public: + // Object state + TradeState state; + int32 pp; + int32 gp; + int32 sp; + int32 cp; + +private: + // Send item data for trade item to other person involved in trade + void SendItemData(const ItemInst* inst, int16 dest_slot_id); + + uint32 with_id; + Mob* owner; +}; + +#endif + diff --git a/zone/doors.cpp b/zone/doors.cpp new file mode 100644 index 000000000..bfeb4f765 --- /dev/null +++ b/zone/doors.cpp @@ -0,0 +1,823 @@ +/* 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 "../common/debug.h" +#include +#include +#include +using namespace std; +#include "masterentity.h" +#include "worldserver.h" +#include "StringIDs.h" +#include "zonedb.h" +#include "../common/packet_functions.h" +#include "../common/packet_dump.h" +#include "../common/MiscFunctions.h" +#include "guild_mgr.h" + +#define OPEN_DOOR 0x02 +#define CLOSE_DOOR 0x03 +#define OPEN_INVDOOR 0x03 +#define CLOSE_INVDOOR 0x02 + +extern EntityList entity_list; +extern WorldServer worldserver; + +Doors::Doors(const Door* door) +: close_timer(5000) +{ + db_id = door->db_id; + door_id = door->door_id; + strn0cpy(zone_name,door->zone_name,32); + strn0cpy(door_name,door->door_name,32); + pos_x = door->pos_x; + pos_y = door->pos_y; + pos_z = door->pos_z; + heading = door->heading; + incline = door->incline; + opentype = door->opentype; + guild_id = door->guild_id; + lockpick = door->lockpick; + keyitem = door->keyitem; + nokeyring = door->nokeyring; + trigger_door = door->trigger_door; + trigger_type = door->trigger_type; + triggered=false; + door_param = door->door_param; + size = door->size; + invert_state = door->invert_state; + SetOpenState(false); + + close_timer.Disable(); + + strn0cpy(dest_zone,door->dest_zone,32); + dest_instance_id = door->dest_instance_id; + dest_x = door->dest_x; + dest_y = door->dest_y; + dest_z = door->dest_z; + dest_heading = door->dest_heading; + + is_ldon_door = door->is_ldon_door; + client_version_mask = door->client_version_mask; +} + +Doors::Doors(const char *dmodel, float dx, float dy, float dz, float dheading, uint8 dopentype, uint16 dsize) +: close_timer(5000) +{ + db_id = database.GetDoorsCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion()); + door_id = database.GetDoorsDBCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion()); + strn0cpy(zone_name,zone->GetShortName(),32); + strn0cpy(door_name,dmodel,32); + pos_x = dx; + pos_y = dy; + pos_z = dz; + heading = dheading; + incline = 0; + opentype = dopentype; + guild_id = 0; + lockpick = 0; + keyitem = 0; + nokeyring = 0; + trigger_door = 0; + trigger_type = 0; + triggered=false; + door_param = 0; + size = dsize; + invert_state = 0; + SetOpenState(false); + + close_timer.Disable(); + + strn0cpy(dest_zone,"NONE",32); + dest_instance_id = 0; + dest_x = 0; + dest_y = 0; + dest_z = 0; + dest_heading = 0; + + is_ldon_door = 0; + client_version_mask = 4294967295; +} + + +Doors::~Doors() +{ +} + +bool Doors::Process() +{ + if(close_timer.Enabled() && close_timer.Check() && IsDoorOpen()) + { + if (opentype == 40 || GetTriggerType() == 1) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct)); + MoveDoor_Struct* md = (MoveDoor_Struct*)outapp->pBuffer; + md->doorid = door_id; + md->action = invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR; + entity_list.QueueClients(0, outapp); + safe_delete(outapp); + } + + triggered=false; + close_timer.Disable(); + SetOpenState(false); + } + return true; +} + +void Doors::HandleClick(Client* sender, uint8 trigger) +{ + //door debugging info dump + _log(DOORS__INFO, "%s clicked door %s (dbid %d, eqid %d) at (%.4f,%.4f,%.4f @%.4f)", sender->GetName(), door_name, db_id, door_id, pos_x, pos_y, pos_z, heading); + _log(DOORS__INFO, " incline %d, opentype %d, lockpick %d, key %d, nokeyring %d, trigger %d type %d, param %d", incline, opentype, lockpick, keyitem, nokeyring, trigger_door, trigger_type, door_param); + _log(DOORS__INFO, " size %d, invert %d, dest: %s (%.4f,%.4f,%.4f @%.4f)", size, invert_state, dest_zone, dest_x, dest_y, dest_z, dest_heading); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct)); + MoveDoor_Struct* md = (MoveDoor_Struct*)outapp->pBuffer; + //DumpPacket(app); + md->doorid = door_id; + ///////////////////////////////////////////////////////////////// + //used_pawn: Locked doors! Rogue friendly too =) + //TODO: add check for other lockpick items + ////////////////////////////////////////////////////////////////// + + //TODO: ADVENTURE DOOR + if(IsLDoNDoor()) + { + if(sender) + { + if(RuleI(Adventure, ItemIDToEnablePorts) != 0) + { + if(!sender->KeyRingCheck(RuleI(Adventure, ItemIDToEnablePorts))) + { + if(sender->GetInv().HasItem(RuleI(Adventure, ItemIDToEnablePorts)) == SLOT_INVALID) + { + sender->Message_StringID(13, 5141); + safe_delete(outapp); + return; + } + else + { + sender->KeyRingAdd(RuleI(Adventure, ItemIDToEnablePorts)); + } + } + } + + if(!sender->GetPendingAdventureDoorClick()) + { + sender->PendingAdventureDoorClick(); + ServerPacket *pack = new ServerPacket(ServerOP_AdventureClickDoor, sizeof(ServerPlayerClickedAdventureDoor_Struct)); + ServerPlayerClickedAdventureDoor_Struct *ads = (ServerPlayerClickedAdventureDoor_Struct*)pack->pBuffer; + strcpy(ads->player, sender->GetName()); + ads->zone_id = zone->GetZoneID(); + ads->id = GetDoorDBID(); + worldserver.SendPacket(pack); + safe_delete(pack); + safe_delete(outapp); + } + return; + } + } + + uint32 keyneeded = GetKeyItem(); + uint8 keepoffkeyring = GetNoKeyring(); + uint32 haskey = 0; + uint32 playerkey = 0; + const ItemInst *lockpicks = sender->GetInv().GetItem(SLOT_CURSOR); + + haskey = sender->GetInv().HasItem(keyneeded, 1); + + if(haskey != SLOT_INVALID) + { + playerkey = keyneeded; + } + + if(GetTriggerType() == 255) + { // this object isnt triggered + if(trigger == 1) + { // this door is only triggered by an object + if(!IsDoorOpen() || (opentype == 58)) + { + md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR; + } + else + { + md->action = invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR; + } + } + else + { + safe_delete(outapp); + return; + } + } + + // guild doors + if(((keyneeded == 0) && (GetLockpick() == 0) && (guild_id == 0)) || + (IsDoorOpen() && (opentype == 58)) || + ((guild_id > 0) && (guild_id == sender->GuildID()))) + { //door not locked + if(!IsDoorOpen() || (opentype == 58)) + { + md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR; + } + else + { + md->action = invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR; + } + } + else + { // guild doors + if((guild_id > 0) && !sender->GetGM()) + { + string tmp; + char tmpmsg[240]; // guild doors msgs + if(guild_mgr.GetGuildNameByID(guild_id, tmp)) + { + sprintf(tmpmsg, "Only members of the <%s> guild may enter here", tmp.c_str()); + } + else + { + 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 + return; + } + // a key is required or the door is locked but can be picked or both + sender->Message(4, "This is locked..."); // debug spam - should probably go + if(sender->GetGM()) // GM can always open locks - should probably be changed to require a key + { + sender->Message_StringID(4,DOORS_GM); + if(!IsDoorOpen() || (opentype == 58)) + { + md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR; + } + else + { + md->action = invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR; + } + } + else if(playerkey) + { // they have something they are trying to open it with + if(keyneeded && (keyneeded == playerkey)) + { // key required and client is using the right key + if(!keepoffkeyring) + { + sender->KeyRingAdd(playerkey); + } + sender->Message(4, "You got it open!"); + if(!IsDoorOpen() || (opentype == 58)) + { + md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR; + } + else + { + md->action = invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR; + } + } + } + else if(lockpicks != NULL) + { + if(sender->GetSkill(PICK_LOCK)) + { + if(lockpicks->GetItem()->ItemType == ItemTypeLockPick) + { + float modskill=sender->GetSkill(PICK_LOCK); + sender->CheckIncreaseSkill(PICK_LOCK, NULL, 1); + +#if EQDEBUG>=5 + LogFile->write(EQEMuLog::Debug, "Client has lockpicks: skill=%f", modskill); +#endif + + if(GetLockpick() <= modskill) + { + if(!IsDoorOpen()) + { + md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR; + } + else + { + md->action = invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR; + } + sender->Message_StringID(4, DOORS_SUCCESSFUL_PICK); + } + else + { + sender->Message_StringID(4, DOORS_INSUFFICIENT_SKILL); + safe_delete(outapp); + return; + } + } + else + { + sender->Message_StringID(4, DOORS_NO_PICK); + safe_delete(outapp); + return; + } + } + else + { + sender->Message_StringID(4, DOORS_CANT_PICK); + safe_delete(outapp); + return; + } + } + else + { // locked door and nothing to open it with + // search for key on keyring + if(sender->KeyRingCheck(keyneeded)) + { + playerkey = keyneeded; + sender->Message(4, "You got it open!"); // more debug spam + if(!IsDoorOpen() || (opentype == 58)) + { + md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR; + } + else + { + md->action = invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR; + } + } + else + { + sender->Message_StringID(4, DOORS_LOCKED); + safe_delete(outapp); + return; + } + } + } + + entity_list.QueueClients(sender, outapp, false); + if(!IsDoorOpen() || (opentype == 58)) + { + close_timer.Start(); + SetOpenState(true); + } + else + { + close_timer.Disable(); + SetOpenState(false); + } + + //everything past this point assumes we opened the door + //and met all the reqs for opening + //everything to do with closed doors has already been taken care of + //we return because we don't want people using teleports on an unlocked door (exploit!) + if((md->action == CLOSE_DOOR && invert_state == 0) || (md->action == CLOSE_INVDOOR && invert_state == 1)) + { + safe_delete(outapp); + return; + } + + safe_delete(outapp); + + if((GetTriggerDoorID() != 0) && (GetTriggerType() == 1)) + { + Doors* triggerdoor = entity_list.FindDoor(GetTriggerDoorID()); + if(triggerdoor && !triggerdoor->triggered) + { + triggered=true; + triggerdoor->HandleClick(sender, 1); + } + else + { + triggered=false; + } + } + else if((GetTriggerDoorID() != 0) && (GetTriggerType() != 1)) + { + Doors* triggerdoor = entity_list.FindDoor(GetTriggerDoorID()); + if(triggerdoor && !triggerdoor->triggered) + { + triggered=true; + triggerdoor->HandleClick(sender, 0); + } + else + { + triggered=false; + } + } + + if(((opentype == 57) || (opentype == 58)) && (strncmp(dest_zone, "NONE", strlen("NONE")) != 0)) + { // Teleport door! + if (( strncmp(dest_zone,zone_name,strlen(zone_name)) == 0) && (!keyneeded)) + { + if(!keepoffkeyring) + { + sender->KeyRingAdd(playerkey); + } + sender->MovePC(zone->GetZoneID(), zone->GetInstanceID(), dest_x, dest_y, dest_z, dest_heading); + } + else if (( !IsDoorOpen() || opentype == 58 ) && (keyneeded && ((keyneeded == playerkey) || sender->GetGM()))) + { + if(!keepoffkeyring) + { + sender->KeyRingAdd(playerkey); + } + if(database.GetZoneID(dest_zone) == zone->GetZoneID()) + { + sender->MovePC(zone->GetZoneID(), zone->GetInstanceID(), dest_x, dest_y, dest_z, dest_heading); + } + else + { + sender->MovePC(database.GetZoneID(dest_zone), dest_instance_id, dest_x, dest_y, dest_z, dest_heading); + } + } + if (( !IsDoorOpen() || opentype == 58 ) && (!keyneeded)) + { + if(database.GetZoneID(dest_zone) == zone->GetZoneID()) + { + sender->MovePC(zone->GetZoneID(), zone->GetInstanceID(), dest_x, dest_y, dest_z, dest_heading); + } + else + { + sender->MovePC(database.GetZoneID(dest_zone), dest_instance_id, dest_x, dest_y, dest_z, dest_heading); + } + } + } +} + +void Doors::NPCOpen(NPC* sender, bool alt_mode) +{ + if(sender) { + if(GetTriggerType() == 255 || GetTriggerDoorID() > 0 || GetLockpick() != 0 || GetKeyItem() != 0 || opentype == 59 || opentype == 58 || !sender->IsNPC()) { // this object isnt triggered or door is locked - NPCs should not open locked doors! + return; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct)); + MoveDoor_Struct* md=(MoveDoor_Struct*)outapp->pBuffer; + md->doorid = door_id; + md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR; + entity_list.QueueCloseClients(sender,outapp,false,200); + safe_delete(outapp); + + if(!alt_mode) { // original function + if(!isopen) { + close_timer.Start(); + isopen=true; + } + else { + close_timer.Disable(); + isopen=false; + } + } + else { // alternative function + close_timer.Start(); + isopen=true; + } + } +} + +void Doors::ForceOpen(Mob *sender, bool alt_mode) +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct)); + MoveDoor_Struct* md=(MoveDoor_Struct*)outapp->pBuffer; + md->doorid = door_id; + md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR; + entity_list.QueueClients(sender,outapp,false); + safe_delete(outapp); + + if(!alt_mode) { // original function + if(!isopen) { + close_timer.Start(); + isopen=true; + } + else { + close_timer.Disable(); + isopen=false; + } + } + else { // alternative function + close_timer.Start(); + isopen=true; + } +} + +void Doors::ForceClose(Mob *sender, bool alt_mode) +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct)); + MoveDoor_Struct* md=(MoveDoor_Struct*)outapp->pBuffer; + md->doorid = door_id; + md->action = invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR; // change from original (open to close) + entity_list.QueueClients(sender,outapp,false); + safe_delete(outapp); + + if(!alt_mode) { // original function + if(!isopen) { + close_timer.Start(); + isopen=true; + } + else { + close_timer.Disable(); + isopen=false; + } + } + else { // alternative function + if(isopen) + close_timer.Trigger(); + } +} + +void Doors::ToggleState(Mob *sender) +{ + if(GetTriggerDoorID() > 0 || GetLockpick() != 0 || GetKeyItem() != 0 || opentype == 58 || opentype == 40) { // borrowed some NPCOpen criteria + return; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct)); + MoveDoor_Struct* md=(MoveDoor_Struct*)outapp->pBuffer; + md->doorid = door_id; + + if(!isopen) { + md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR; + isopen=true; + } + else + { + md->action = invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR; + isopen=false; + } + + entity_list.QueueClients(sender,outapp,false); + safe_delete(outapp); +} + +void Doors::DumpDoor(){ + LogFile->write(EQEMuLog::Debug, + "db_id:%i door_id:%i zone_name:%s door_name:%s pos_x:%f pos_y:%f pos_z:%f heading:%f", + db_id, door_id, zone_name, door_name, pos_x, pos_y, pos_z, heading); + LogFile->write(EQEMuLog::Debug, + "opentype:%i guild_id:%i lockpick:%i keyitem:%i nokeyring:%i trigger_door:%i trigger_type:%i door_param:%i open:%s", + opentype, guild_id, lockpick, keyitem, nokeyring, trigger_door, trigger_type, door_param, (isopen) ? "open":"closed"); + LogFile->write(EQEMuLog::Debug, + "dest_zone:%s dest_x:%f dest_y:%f dest_z:%f dest_heading:%f", + dest_zone, dest_x, dest_y, dest_z, dest_heading); +} + +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 != NULL && 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 { + cerr << "Error in GetDoorsCount query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return -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 != NULL && 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 { + cerr << "Error in GetDoorsCountPlusOne query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return -1; + } + + return -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 != NULL && 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 { + cerr << "Error in GetDoorsCountPlusOne query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return -1; + } + + return -1; +} + +/* +extern "C" bool extDBLoadDoors(int32 iDoorCount, uint32 iMaxDoorID) { return database.DBLoadDoors(iDoorCount, iMaxDoorID); } +const Door* ZoneDatabase::GetDoor(uint8 door_id, const char* zone_name) { + for(uint32 i=0; i!=max_door_type;i++) + { + const Door* door; + door = GetDoorDBID(i); + if (!door) + continue; + if(door->door_id == door_id && strcasecmp(door->zone_name, zone_name) == 0) + return door; + } +return 0; +} + +const Door* ZoneDatabase::GetDoorDBID(uint32 db_id) { + return EMuShareMemDLL.Doors.GetDoor(db_id); +} + +bool ZoneDatabase::LoadDoors() { + if (!EMuShareMemDLL.Load()) + return false; + int32 tmp = 0; + tmp = GetDoorsCount(&max_door_type); + if (tmp == -1) { + cout << "Error: ZoneDatabase::LoadDoors-ShareMem: GetDoorsCount() returned < 0" << endl; + return false; + } + bool ret = EMuShareMemDLL.Doors.DLLLoadDoors(&extDBLoadDoors, sizeof(Door), &tmp, &max_door_type); + return ret; +}*/ + +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) { + cerr << "Error, Door Count of " << iDoorCount << " exceeded." << 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], NULL, 10); + } + mysql_free_result(result); + } + else + { + cerr << "Error in DBLoadDoors query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + return true; +} + + +void Doors::SetLocation(float x, float y, float z) +{ + entity_list.DespawnAllDoors(); + pos_x = x; + pos_y = y; + pos_z = z; + entity_list.RespawnAllDoors(); +} + +void Doors::SetX(float in) { + entity_list.DespawnAllDoors(); + pos_x = in; + entity_list.RespawnAllDoors(); +} +void Doors::SetY(float in) { + entity_list.DespawnAllDoors(); + pos_y = in; + entity_list.RespawnAllDoors(); +} +void Doors::SetZ(float in) { + entity_list.DespawnAllDoors(); + pos_z = in; + entity_list.RespawnAllDoors(); +} +void Doors::SetHeading(float in) { + entity_list.DespawnAllDoors(); + heading = in; + entity_list.RespawnAllDoors(); +} + +void Doors::SetIncline(int in) { + entity_list.DespawnAllDoors(); + incline = in; + entity_list.RespawnAllDoors(); +} + +void Doors::SetOpenType(uint8 in) { + entity_list.DespawnAllDoors(); + opentype = in; + entity_list.RespawnAllDoors(); +} + +void Doors::SetDoorName(char* name) { + entity_list.DespawnAllDoors(); + memset(door_name, 0, sizeof(door_name)); + strncpy(door_name, name, sizeof(door_name)); + entity_list.RespawnAllDoors(); +} + +void Doors::SetSize(uint16 in) { + entity_list.DespawnAllDoors(); + size = in; + entity_list.RespawnAllDoors(); +} + +void Doors::CreateDatabaseEntry() +{ + if(database.GetDoorsDBCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion()) - 1 >= 255) + { + return; + } + database.InsertDoor(GetDoorDBID(), GetDoorID(), GetDoorName(), GetX(), GetY(), GetZ(), GetHeading(), GetOpenType(), GetGuildID(), GetLockpick(), GetKeyItem(), GetDoorParam(), GetInvertState(), GetIncline(), GetSize()); +} \ No newline at end of file diff --git a/zone/doors.h b/zone/doors.h new file mode 100644 index 000000000..c99c17949 --- /dev/null +++ b/zone/doors.h @@ -0,0 +1,113 @@ +#ifndef DOORS_H +#define DOORS_H +#include "../common/types.h" +#include "../common/linked_list.h" +#include "../common/timer.h" +#include "../common/emu_opcodes.h" +#include "../common/eq_packet_structs.h" +#include "entity.h" +#include "mob.h" +#include "zonedump.h" + +class Doors : public Entity +{ +public: + Doors(const Door* door); + Doors(const char *dmodel, float dx, float dy, float dz, float dheading, uint8 dopentype = 58, uint16 dsize = 100); + ~Doors(); + bool IsDoor() const { return true; } + void HandleClick(Client* sender, uint8 trigger); + bool Process(); + uint8 GetDoorID() { return door_id; } + uint32 GetDoorDBID() { return db_id; } + uint32 GetGuildID() { return guild_id; } + uint8 GetOpenType() { return opentype; } + char* GetDoorName() { return door_name; } + uint32 GetDoorParam() { return door_param; } + int GetInvertState() { return invert_state; } + float GetX() { return pos_x; } + float GetY() { return pos_y; } + float GetZ() { return pos_z; } + float GetHeading() { return heading; } + int GetIncline() { return incline; } + bool triggered; + void SetOpenState(bool st) { isopen = st; } + bool IsDoorOpen() { return isopen; } + + uint8 GetTriggerDoorID() { return trigger_door; } + uint8 GetTriggerType() { return trigger_type; } + + uint32 GetKeyItem() { return keyitem; } + void SetKeyItem(uint32 in) { keyitem = in; } + uint8 GetNoKeyring() { return nokeyring; } + void SetNoKeyring(uint8 in) { nokeyring = in; } + uint16 GetLockpick() { return lockpick; } + void SetLockpick(uint16 in) { lockpick = in; } + uint16 GetSize() { return size; } + void SetGuildID(uint32 guild_id) { this->guild_id = guild_id; } + + uint32 GetEntityID() { return entity_id; } + void SetEntityID(uint32 entity) { entity_id = entity; } + + void DumpDoor(); + float GetDestX() { return dest_x; } + float GetDestY() { return dest_y; } + float GetDestZ() { return dest_z; } + float GetDestHeading() { return dest_heading; } + + uint8 IsLDoNDoor() { return is_ldon_door; } + uint32 GetClientVersionMask() { return client_version_mask; } + + void NPCOpen(NPC* sender, bool alt_mode=false); + void ForceOpen(Mob *sender, bool alt_mode=false); + void ForceClose(Mob *sender, bool alt_mode=false); + void ToggleState(Mob *sender); + + void SetX(float in); + void SetY(float in); + void SetZ(float in); + void SetHeading(float in); + void SetIncline(int in); + void SetDoorName(char* name); + void SetOpenType(uint8 in); + void SetLocation(float x, float y, float z); + void SetSize(uint16 size); + void CreateDatabaseEntry(); + +private: + + uint32 db_id; + uint8 door_id; + char zone_name[32]; + char door_name[32]; + float pos_x; + float pos_y; + float pos_z; + float heading; + int incline; + uint8 opentype; + uint32 guild_id; + uint16 lockpick; + uint32 keyitem; + uint8 nokeyring; + uint8 trigger_door; + uint8 trigger_type; + uint32 door_param; + uint16 size; + int invert_state; + uint32 entity_id; + bool isopen; + Timer close_timer; + //Timer trigger_timer; + + char dest_zone[16]; + int dest_instance_id; + float dest_x; + float dest_y; + float dest_z; + float dest_heading; + + uint8 is_ldon_door; + uint32 client_version_mask; +}; +#endif diff --git a/zone/effects.cpp b/zone/effects.cpp new file mode 100644 index 000000000..1a3ae9e56 --- /dev/null +++ b/zone/effects.cpp @@ -0,0 +1,841 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 "masterentity.h" +#include "worldserver.h" +#include "zonedb.h" +#include "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 "StringIDs.h" +#include "NpcAI.h" + +float Client::GetActSpellRange(uint16 spell_id, float range, bool IsBard) +{ + float extrange = 100; + + extrange += GetFocusEffect(focusRange, spell_id); + + return (range * extrange) / 100; +} + + +int32 Client::Additional_SpellDmg(uint16 spell_id, bool bufftick) +{ + int32 spell_dmg = 0; + spell_dmg += GetFocusEffect(focusFF_Damage_Amount, spell_id); + spell_dmg += GetFocusEffect(focusSpellDamage, spell_id); + + //For DOTs you need to apply the damage over the duration of the dot to each tick (this is how live did it) + if (bufftick){ + int duration = CalcBuffDuration(this, this, spell_id); + if (duration > 0) + return spell_dmg /= duration; + else + return 0; + } + return spell_dmg; +} + +//Scale all NPC spell Damage via $npc->SetSpellFocusDMG(value) +//Direct Damage is checked in Mob::SpellEffect [spell_effects.cpp] +//DoT Damage is checked in Mob::DoBuffTic [spell_effects.cpp] (This was added for npcs in that routine) +int32 NPC::GetActSpellDamage(uint16 spell_id, int32 value) { + + int32 modifier = 100; + + modifier += SpellFocusDMG; + + return (value * modifier / 100); +} + +int32 Client::GetActSpellDamage(uint16 spell_id, int32 value) { + // Important variables: + // value: the actual damage after resists, passed from Mob::SpellEffect + // modifier: modifier to damage (from spells & focus effects?) + // ratio: % of the modifier to apply (from AAs & natural bonus?) + // chance: critital chance % + + int32 modifier = 100; + int16 spell_dmg = 0; + + + //Dunno if this makes sense: + if (spells[spell_id].resisttype > 0) + modifier += GetFocusEffect((focusType)(0-spells[spell_id].resisttype), spell_id); + + + int tt = spells[spell_id].targettype; + if (tt == ST_UndeadAE || tt == ST_Undead || tt == ST_Summoned) { + //undead/summoned spells + modifier += GetFocusEffect(focusImprovedUndeadDamage, spell_id); + } else { + //damage spells. + modifier += GetFocusEffect(focusImprovedDamage, spell_id); + modifier += GetFocusEffect(focusSpellEffectiveness, spell_id); + modifier += GetFocusEffect(focusImprovedDamage2, spell_id); + } + + // 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 ) { + if (this->GetLevel() > 40) + value -= (this->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 + } + + // This adds the extra damage for the AA's Consumption of the Soul and Improved Consumption of the Soul, 200 per level to the AA Leech Curse for Shadowknights. + if (spell_id == SPELL_LEECH_TOUCH) { //Leech Touch + value -= GetAA(aaConsumptionoftheSoul) * 200; //Consumption of the Soul + value -= GetAA(aaImprovedConsumptionofSoul) * 200; //Improved Consumption of the Soul + } + + //spell crits, dont make sense if cast on self. + if(tt != ST_Self) { + // item SpellDmg bonus + // Formula = SpellDmg * (casttime + recastime) / 7; Cant trigger off spell less than 5 levels below and cant cause more dmg than the spell itself. + if(this->itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) { + spell_dmg = this->itembonuses.SpellDmg * (spells[spell_id].cast_time + spells[spell_id].recast_time) / 7000; + if(spell_dmg > -value) + spell_dmg = -value; + } + + // Spell-based SpellDmg adds directly but it restricted by focuses. + spell_dmg += Additional_SpellDmg(spell_id); + + int chance = RuleI(Spells, BaseCritChance); + int32 ratio = RuleI(Spells, BaseCritRatio); + + chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; + ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease; + + if(GetClass() == WIZARD) { + if (GetLevel() >= RuleI(Spells, WizCritLevel)) { + chance += RuleI(Spells, WizCritChance); + ratio += RuleI(Spells, WizCritRatio); + } + if(aabonuses.SpellCritDmgIncrease > 0) // wizards get an additional bonus + ratio += aabonuses.SpellCritDmgIncrease * 1.5; //108%, 115%, 124%, close to Graffe's 207%, 215%, & 225% + } + + //Improved Harm Touch is a guaranteed crit if you have at least one level of SCF. + if (spell_id == SPELL_IMP_HARM_TOUCH) { + if ( (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0) ) + chance = 100; + } + + /* + //Handled in aa_effects will focus spells from 'spellgroup=99'. (SK life tap from buff procs) + //If you are using an older spell file table (Pre SOF)... + //Use SQL optional_EnableSoulAbrasionAA to update your spells table to properly use the effect. + //If you do not want to update your table then you may want to enable this. + if(tt == ST_Tap) { + if(spells[spell_id].classes[SHADOWKNIGHT-1] >= 254 && spell_id != SPELL_LEECH_TOUCH){ + if(ratio < 100) //chance increase and ratio are made up, not confirmed + ratio = 100; + + switch (GetAA(aaSoulAbrasion)) + { + case 1: + modifier += 100; + break; + case 2: + modifier += 200; + break; + case 3: + modifier += 300; + break; + } + } + } + */ + + if (chance > 0) { + mlog(SPELLS__CRITS, "Attempting spell crit. Spell: %s (%d), Value: %d, Modifier: %d, Chance: %d, Ratio: %d", spells[spell_id].name, spell_id, value, modifier, chance, ratio); + if(MakeRandomInt(0,100) <= chance) { + modifier += modifier*ratio/100; + spell_dmg *= 2; + mlog(SPELLS__CRITS, "Spell crit successful. Final damage modifier: %d, Final Damage: %d", modifier, (value * modifier / 100) - spell_dmg); + entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s delivers a critical blast! (%d)", GetName(), (-value * modifier / 100) + spell_dmg); + } else + mlog(SPELLS__CRITS, "Spell crit failed. Final Damage Modifier: %d, Final Damage: %d", modifier, (value * modifier / 100) - spell_dmg); + } + } + + return ((value * modifier / 100) - spell_dmg); +} + +int32 Client::GetActDoTDamage(uint16 spell_id, int32 value) { + + int32 modifier = 100; + int16 spell_dmg = 0; + int16 critChance = 0; + int32 ratio = 0; + + modifier += GetFocusEffect(focusImprovedDamage, spell_id); + critChance += itembonuses.CriticalDoTChance + spellbonuses.CriticalDoTChance + aabonuses.CriticalDoTChance; + ratio += itembonuses.DotCritDmgIncrease + spellbonuses.DotCritDmgIncrease + aabonuses.DotCritDmgIncrease; + spell_dmg += Additional_SpellDmg(spell_id,true); + + // since DOTs are the Necromancer forte, give an innate bonus (Kayen: Is this a real bonus?) + // however, no chance to crit unless they've trained atleast one level in the AA first + if (GetClass() == NECROMANCER && critChance > 0) + critChance += 5; + + if (critChance > 0){ + if (MakeRandomInt(0, 99) < critChance){ + modifier += modifier*ratio/100; + return (((value*modifier/100)-spell_dmg)*2); + } + } + + return ((value*modifier/100)-spell_dmg); + +} + +//Scale all NPC spell healing via SetSpellFocusHeal(value) +int32 NPC::GetActSpellHealing(uint16 spell_id, int32 value) { + + int32 modifier = 100; + modifier += SpellFocusHeal; + + // Check for buffs that affect the healrate of the target + if(this->GetTarget()) + { + value += value * GetHealRate(spell_id) / 100; + } + + return (value * modifier / 100); +} + +int32 Client::Additional_Heal(uint16 spell_id) +{ + int32 heal_amt = 0; + + heal_amt += GetFocusEffect(focusAdditionalHeal, spell_id); + heal_amt += GetFocusEffect(focusAdditionalHeal2, spell_id); + + if (heal_amt){ + int duration = CalcBuffDuration(this, this, spell_id); + if (duration > 0) + return heal_amt /= duration; + } + + return heal_amt; +} + +int32 Client::GetActSpellHealing(uint16 spell_id, int32 value) { + + int32 modifier = 100; + int16 heal_amt = 0; + modifier += GetFocusEffect(focusImprovedHeal, spell_id); + modifier += GetFocusEffect(focusSpellEffectiveness, spell_id); + heal_amt += Additional_Heal(spell_id); + int chance = 0; + + // Instant Heals + if(spells[spell_id].buffduration < 1) + { + // Formula = HealAmt * (casttime + recastime) / 7; Cant trigger off spell less than 5 levels below and cant heal more than the spell itself. + if(this->itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) { + heal_amt = this->itembonuses.HealAmt * (spells[spell_id].cast_time + spells[spell_id].recast_time) / 7000; + if(heal_amt > value) + heal_amt = value; + } + + // Check for buffs that affect the healrate of the target and critical heal rate of target + if(GetTarget()){ + value += value * GetHealRate(spell_id) / 100; + chance += GetCriticalHealRate(spell_id); + } + + //Live AA - Healing Gift, Theft of Life + chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + + if(MakeRandomInt(0,99) < chance) { + entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s performs an exceptional heal! (%d)", GetName(), ((value * modifier / 50) + heal_amt*2)); + return ((value * modifier / 50) + heal_amt*2); + } + else{ + return ((value * modifier / 100) + heal_amt); + } + } + // Hots + else { + chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + if(MakeRandomInt(0,99) < chance) + return ((value * modifier / 50) + heal_amt*2); + } + return ((value * modifier / 100) + heal_amt); +} + +int32 Client::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; + // Doesnt generate mana, so best case is a free spell + if(mana_back > cost) + mana_back = cost; + + cost -= mana_back; + } + + // This formula was derived from the following resource: + // http://www.eqsummoners.com/eq1/specialization-library.html + // WildcardX + float PercentManaReduction = 0; + float SpecializeSkill = GetSpecializeSkillValue(spell_id); + int SuccessChance = MakeRandomInt(0, 100); + + float bonus = 1.0; + switch(GetAA(aaSpellCastingMastery)) + { + case 1: + bonus += 0.05; + break; + case 2: + bonus += 0.15; + break; + case 3: + bonus += 0.30; + break; + } + + bonus += 0.05 * GetAA(aaAdvancedSpellCastingMastery); + + if(SuccessChance <= (SpecializeSkill * 0.3 * bonus)) + { + PercentManaReduction = 1 + 0.05 * SpecializeSkill; + switch(GetAA(aaSpellCastingMastery)) + { + case 1: + PercentManaReduction += 2.5; + break; + case 2: + PercentManaReduction += 5.0; + break; + case 3: + PercentManaReduction += 10.0; + break; + } + + switch(GetAA(aaAdvancedSpellCastingMastery)) + { + case 1: + PercentManaReduction += 2.5; + break; + case 2: + PercentManaReduction += 5.0; + break; + case 3: + PercentManaReduction += 10.0; + break; + } + } + + int16 focus_redux = GetFocusEffect(focusManaCost, spell_id); + + if(focus_redux > 0) + { + PercentManaReduction += MakeRandomFloat(1, (double)focus_redux); + } + + cost -= (cost * (PercentManaReduction / 100)); + + // Gift of Mana - reduces spell cost to 1 mana + if(focus_redux >= 100) { + uint32 buff_max = GetMaxTotalSlots(); + for (int buffSlot = 0; buffSlot < buff_max; buffSlot++) { + if (buffs[buffSlot].spellid == 0 || buffs[buffSlot].spellid >= SPDAT_RECORDS) + continue; + + if(IsEffectInSpell(buffs[buffSlot].spellid, SE_ReduceManaCost)) { + if(CalcFocusEffect(focusManaCost, buffs[buffSlot].spellid, spell_id) == 100) + cost = 1; + } + } + } + + if(cost < 0) + cost = 0; + + return cost; +} + +int32 Client::GetActSpellDuration(uint16 spell_id, int32 duration) +{ + int increase = 100; + increase += GetFocusEffect(focusSpellDuration, spell_id); + int tic_inc = 0; + tic_inc = GetFocusEffect(focusSpellDurByTic, spell_id); + + if(IsBeneficialSpell(spell_id)) + { + switch(GetAA(aaSpellCastingReinforcement)) { + case 1: + increase += 5; + break; + case 2: + increase += 15; + break; + case 3: + increase += 30; + if (GetAA(aaSpellCastingReinforcementMastery) == 1) + increase += 20; + break; + } + } + + if(IsMezSpell(spell_id)) { + tic_inc += GetAA(aaMesmerizationMastery); + } + + return (((duration * increase) / 100) + tic_inc); +} + +int32 Client::GetActSpellCasttime(uint16 spell_id, int32 casttime) +{ + int32 cast_reducer = 0; + cast_reducer += GetFocusEffect(focusSpellHaste, spell_id); + + //this function loops through the effects of spell_id many times + //could easily be consolidated. + + if (GetLevel() >= 51 && casttime >= 3000 && !BeneficialSpell(spell_id) + && (GetClass() == SHADOWKNIGHT || GetClass() == RANGER + || GetClass() == PALADIN || GetClass() == BEASTLORD )) + cast_reducer += (GetLevel()-50)*3; + + //LIVE AA SpellCastingDeftness, QuickBuff, QuickSummoning, QuickEvacuation, QuickDamage + + if (cast_reducer > RuleI(Spells, MaxCastTimeReduction)) + cast_reducer = RuleI(Spells, MaxCastTimeReduction); + + casttime = (casttime*(100 - cast_reducer)/100); + + return casttime; +} + +bool Client::TrainDiscipline(uint32 itemid) { + + //get the item info + const Item_Struct *item = database.GetItem(itemid); + if(item == NULL) { + Message(13, "Unable to find the tome you turned in!"); + LogFile->write(EQEMuLog::Error, "Unable to find turned in tome id %lu\n", (unsigned long)itemid); + return(false); + } + + if(item->ItemClass != ItemClassCommon || item->ItemType != ItemTypeSpell) { + Message(13, "Invalid item type, you cannot learn from this item."); + //summon them the item back... + SummonItem(itemid); + return(false); + } + + //Need a way to determine the difference between a spell and a tome + //so they cant turn in a spell and get it as a discipline + //this is kinda a hack: + if(!( + item->Name[0] == 'T' && + item->Name[1] == 'o' && + item->Name[2] == 'm' && + item->Name[3] == 'e' && + item->Name[4] == ' ' + )) { + Message(13, "This item is not a tome."); + //summon them the item back... + SummonItem(itemid); + return(false); + } + + int myclass = GetClass(); + if(myclass == WIZARD || myclass == ENCHANTER || myclass == MAGICIAN || myclass == NECROMANCER) { + Message(13, "Your class cannot learn from this tome."); + //summon them the item back... + SummonItem(itemid); + return(false); + } + + //make sure we can train this... + //can we use the item? + uint32 cbit = 1 << (myclass-1); + if(!(item->Classes & cbit)) { + Message(13, "Your class cannot learn from this tome."); + //summon them the item back... + SummonItem(itemid); + return(false); + } + + uint32 spell_id = item->Scroll.Effect; + if(!IsValidSpell(spell_id)) { + Message(13, "This tome contains invalid knowledge."); + return(false); + } + + //can we use the spell? + const SPDat_Spell_Struct &spell = spells[spell_id]; + uint8 level_to_use = spell.classes[myclass - 1]; + if(level_to_use == 255) { + Message(13, "Your class cannot learn from this tome."); + //summon them the item back... + SummonItem(itemid); + return(false); + } + + if(level_to_use > GetLevel()) { + Message(13, "You must be at least level %d to learn this discipline.", level_to_use); + //summon them the item back... + SummonItem(itemid); + return(false); + } + + //add it to PP. + int r; + for(r = 0; r < MAX_PP_DISCIPLINES; r++) { + if(m_pp.disciplines.values[r] == spell_id) { + Message(13, "You already know this discipline."); + //summon them the item back... + SummonItem(itemid); + return(false); + } else if(m_pp.disciplines.values[r] == 0) { + m_pp.disciplines.values[r] = spell_id; + SendDisciplineUpdate(); + Message(0, "You have learned a new discipline!"); + return(true); + } + } + Message(13, "You have learned too many disciplines and can learn no more."); + return(false); +} + +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); +} + +bool Client::UseDiscipline(uint32 spell_id, uint32 target) { + // Dont let client waste a reuse timer if they can't use the disc + if (IsStunned() || IsFeared() || IsMezzed() || IsAmnesiad() || IsPet()) + { + return(false); + } + + //make sure we have the spell... + int r; + for(r = 0; r < MAX_PP_DISCIPLINES; r++) { + if(m_pp.disciplines.values[r] == spell_id) + break; + } + if(r == MAX_PP_DISCIPLINES) + return(false); //not found. + + //Check the disc timer + pTimerType DiscTimer = pTimerDisciplineReuseStart + spells[spell_id].EndurTimerIndex; + if(!p_timers.Expired(&database, DiscTimer)) { + /*char val1[20]={0};*/ //unused + /*char val2[20]={0};*/ //unused + uint32 remain = p_timers.GetRemainingTime(DiscTimer); + //Message_StringID(0, DISCIPLINE_CANUSEIN, ConvertArray((remain)/60,val1), ConvertArray(remain%60,val2)); + Message(0, "You can use this discipline in %d minutes %d seconds.", ((remain)/60), (remain%60)); + return(false); + } + + //make sure we can use it.. + if(!IsValidSpell(spell_id)) { + Message(13, "This tome contains invalid knowledge."); + return(false); + } + + //can we use the spell? + const SPDat_Spell_Struct &spell = spells[spell_id]; + uint8 level_to_use = spell.classes[GetClass() - 1]; + if(level_to_use == 255) { + Message(13, "Your class cannot learn from this tome."); + //should summon them a new one... + return(false); + } + + if(level_to_use > GetLevel()) { + Message_StringID(13, DISC_LEVEL_USE_ERROR); + //should summon them a new one... + return(false); + } + + if(GetEndurance() > spell.EndurCost) { + SetEndurance(GetEndurance() - spell.EndurCost); + } else { + Message(11, "You are too fatigued to use this skill right now."); + return(false); + } + + if(spell.recast_time > 0) + { + uint32 reduced_recast = spell.recast_time / 1000; + reduced_recast -= CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id); + 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); + } + } + else + { + CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT); + } + return(true); +} + +void EntityList::AETaunt(Client* taunter, float range) { + LinkedListIterator iterator(npc_list); + + if(range == 0) { + range = 100; //arbitrary default... + } + + range = range * range; + + iterator.Reset(); + while(iterator.MoreElements()) + { + NPC * them = iterator.GetData(); + float zdiff = taunter->GetZ() - them->GetZ(); + if (zdiff < 0) + zdiff *= -1; + if (zdiff < 10 + && taunter->IsAttackAllowed(them) + && taunter->DistNoRootNoZ(*them) <= range) { + + if (taunter->CheckLosFN(them)) { + taunter->Taunt(them, true); + } + } + iterator.Advance(); + } +} + +// solar: causes caster to hit every mob within dist range of center with +// spell_id. +// NPC spells will only affect other NPCs with compatible faction +void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster, int16 resist_adjust) +{ + LinkedListIterator iterator(mob_list); + Mob *curmob; + + float dist = caster->GetAOERange(spell_id); + float dist2 = dist * dist; + + bool bad = IsDetrimentalSpell(spell_id); + bool isnpc = caster->IsNPC(); + const int MAX_TARGETS_ALLOWED = 4; + int iCounter = 0; + + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) + { + curmob = iterator.GetData(); + if(curmob == center) //do not affect center + continue; + if(curmob == caster && !affect_caster) //watch for caster too + continue; + if(center->DistNoRoot(*curmob) > dist2) //make sure they are in range + continue; + if(isnpc && curmob->IsNPC()) { //check npc->npc casting + FACTION_VALUE f = curmob->GetReverseFactionCon(caster); + if(bad) { + //affect mobs that are on our hate list, or + //which have bad faction with us + if( ! (caster->CheckAggro(curmob) || f == FACTION_THREATENLY || f == FACTION_SCOWLS) ) + continue; + } else { + //only affect mobs we would assist. + if( ! (f <= FACTION_AMIABLE)) + continue; + } + } + //finally, make sure they are within range + if(bad) { + if(!caster->IsAttackAllowed(curmob, true)) + continue; + if(!center->CheckLosFN(curmob)) + continue; + } + + //if we get here... cast the spell. + if(IsTargetableAESpell(spell_id) && bad) + { + if(iCounter < MAX_TARGETS_ALLOWED) + { + caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); + } + } + else + { + caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); + } + + if(!isnpc) //npcs are not target limited... + iCounter++; + } +} + +void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) +{ + LinkedListIterator iterator(mob_list); + Mob *curmob; + + float dist = caster->GetAOERange(spell_id); + float dist2 = dist * dist; + + bool bad = IsDetrimentalSpell(spell_id); + + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) + { + curmob = iterator.GetData(); + if(curmob == center) //do not affect center + continue; + if(curmob == caster && !affect_caster) //watch for caster too + continue; + if(center->DistNoRoot(*curmob) > dist2) //make sure they are in range + continue; + + //Only npcs mgb should hit are client pets... + if(curmob->IsNPC()) + { + Mob *owner = curmob->GetOwner(); + if(owner) + { + if(!owner->IsClient()) + { + continue; + } + } + else + { + continue; + } + } + + if(bad) + { + continue; + } + + caster->SpellOnTarget(spell_id, curmob); + } +} + +// solar: causes caster to hit every mob within dist range of center with +// a bard pulse of spell_id. +// NPC spells will only affect other NPCs with compatible faction +void EntityList::AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster) +{ + LinkedListIterator iterator(mob_list); + Mob *curmob; + + float dist = caster->GetAOERange(spell_id); + float dist2 = dist * dist; + + bool bad = IsDetrimentalSpell(spell_id); + bool isnpc = caster->IsNPC(); + + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) + { + curmob = iterator.GetData(); + if(curmob == center) //do not affect center + continue; + if(curmob == caster && !affect_caster) //watch for caster too + continue; + if(center->DistNoRoot(*curmob) > dist2) //make sure they are in range + continue; + if(isnpc && curmob->IsNPC()) { //check npc->npc casting + FACTION_VALUE f = curmob->GetReverseFactionCon(caster); + if(bad) { + //affect mobs that are on our hate list, or + //which have bad faction with us + if( ! (caster->CheckAggro(curmob) || f == FACTION_THREATENLY || f == FACTION_SCOWLS) ) + continue; + } else { + //only affect mobs we would assist. + if( ! (f <= FACTION_AMIABLE)) + continue; + } + } + //finally, make sure they are within range + if(bad) { + if(!center->CheckLosFN(curmob)) + continue; + } + //if we get here... cast the spell. + curmob->BardPulse(spell_id, caster); + } + if(caster->IsClient()) + caster->CastToClient()->CheckSongSkillIncrease(spell_id); +} + +//Dook- Rampage and stuff for clients. +//NPCs handle it differently in Mob::Rampage +void EntityList::AEAttack(Mob *attacker, float dist, int Hand, int count, bool IsFromSpell) { +//Dook- Will need tweaking, currently no pets or players or horses + LinkedListIterator iterator(mob_list); + Mob *curmob; + + float dist2 = dist * dist; + + int hit = 0; + + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) { + curmob = iterator.GetData(); + if(curmob->IsNPC() + && curmob != attacker //this is not needed unless NPCs can use this + &&(attacker->IsAttackAllowed(curmob)) + && curmob->GetRace() != 216 && curmob->GetRace() != 472 /* dont attack horses */ + && (curmob->DistNoRoot(*attacker) <= dist2) + ) { + attacker->Attack(curmob, Hand, false, false, IsFromSpell); + hit++; + if(count != 0 && hit >= count) + return; + } + } +} + + diff --git a/zone/embparser.cpp b/zone/embparser.cpp new file mode 100644 index 000000000..87484be3a --- /dev/null +++ b/zone/embparser.cpp @@ -0,0 +1,1702 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 +*/ + +//extends the parser to include perl + +#ifndef EMBPARSER_CPP +#define EMBPARSER_CPP + +#ifdef EMBPERL + +#include "../common/debug.h" +#include "masterentity.h" +#include "features.h" +#include "embparser.h" +#include "questmgr.h" +#include "command.h" +#include "../common/seperator.h" +#include "../common/MiscFunctions.h" +#include "QGlobals.h" +#include "zone.h" + +#include + +//these MUST be in the same order as the QuestEventID enum +const char *QuestEventSubroutines[_LargestEventID] = { + "EVENT_SAY", + "EVENT_ITEM", + "EVENT_DEATH", + "EVENT_SPAWN", + "EVENT_ATTACK", + "EVENT_COMBAT", + "EVENT_AGGRO", + "EVENT_SLAY", + "EVENT_NPC_SLAY", + "EVENT_WAYPOINT_ARRIVE", + "EVENT_WAYPOINT_DEPART", + "EVENT_TIMER", + "EVENT_SIGNAL", + "EVENT_HP", + "EVENT_ENTER", + "EVENT_EXIT", + "EVENT_ENTERZONE", + "EVENT_CLICKDOOR", + "EVENT_LOOT", + "EVENT_ZONE", + "EVENT_LEVEL_UP", + "EVENT_KILLED_MERIT", + "EVENT_CAST_ON", + "EVENT_TASKACCEPTED", + "EVENT_TASK_STAGE_COMPLETE", + "EVENT_TASK_UPDATE", + "EVENT_TASK_COMPLETE", + "EVENT_TASK_FAIL", + "EVENT_AGGRO_SAY", + "EVENT_PLAYER_PICKUP", + "EVENT_POPUPRESPONSE", + "EVENT_PROXIMITY_SAY", + "EVENT_CAST", + "EVENT_SCALE_CALC", + "EVENT_ITEM_ENTERZONE", + "EVENT_TARGET_CHANGE", + "EVENT_HATE_LIST", + "EVENT_SPELL_EFFECT_CLIENT", + "EVENT_SPELL_EFFECT_NPC", + "EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT", + "EVENT_SPELL_EFFECT_BUFF_TIC_NPC", + "EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE", + "EVENT_COMBINE_SUCCESS", + "EVENT_COMBINE_FAILURE", + "EVENT_ITEM_CLICK", + "EVENT_ITEM_CLICK_CAST", + "EVENT_GROUP_CHANGE", + "EVENT_FORAGE_SUCCESS", + "EVENT_FORAGE_FAILURE", + "EVENT_FISH_START", + "EVENT_FISH_SUCCESS", + "EVENT_FISH_FAILURE", + "EVENT_CLICK_OBJECT", + "EVENT_DISCOVER_ITEM", + "EVENT_DISCONNECT", + "EVENT_CONNECT" +}; + +extern Zone* zone; + +PerlembParser::PerlembParser(void) : Parser() +{ + perl = NULL; + eventQueueProcessing = false; + globalPlayerQuestLoaded = pQuestReadyToLoad; + globalNPCQuestLoaded = nQuestReadyToLoad; +} + +PerlembParser::~PerlembParser() +{ + safe_delete(perl); +} + +void PerlembParser::ExportVar(const char * pkgprefix, const char * varname, const char * value) const +{ + if(!perl) + return; + //this crap cant possibly throw anything in its current state... oh well + try + { + perl->setstr(std::string(pkgprefix).append("::").append(varname).c_str(), value); + //todo: consider replacing ' w/ ", so that values can be expanded on the perl side + } + catch(const char * err) + { //todo: consider rethrowing + LogFile->write(EQEMuLog::Status, "Error exporting var: %s", err); + } +} + +// Exports key-value pairs to a hash named pkgprefix::hashname +void PerlembParser::ExportHash(const char *pkgprefix, const char *hashname, std::map &vals) +{ + if (!perl) + return; + + try + { + perl->sethash( + std::string(pkgprefix).append("::").append(hashname).c_str(), + vals + ); + } catch(const char * err) { + LogFile->write(EQEMuLog::Status, "Error exporting hash: %s", err); + } +} + +void PerlembParser::ExportVar(const char * pkgprefix, const char * varname, int value) const +{ + + if(!perl) + return; + //this crap cant possibly throw anything in its current state... oh well + try { + perl->seti(std::string(pkgprefix).append("::").append(varname).c_str(), value); + + } catch(const char * err) { + LogFile->write(EQEMuLog::Status, "Error exporting var: %s", err); + } +} + +void PerlembParser::ExportVar(const char * pkgprefix, const char * varname, unsigned int value) const +{ + + if(!perl) + return; + //this crap cant possibly throw anything in its current state... oh well + try { + perl->seti(std::string(pkgprefix).append("::").append(varname).c_str(), value); + + } catch(const char * err) { + LogFile->write(EQEMuLog::Status, "Error exporting var: %s", err); + } +} + +void PerlembParser::ExportVar(const char * pkgprefix, const char * varname, float value) const +{ + + if(!perl) + return; + //this crap cant possibly throw anything in its current state... oh well + try { + perl->setd(std::string(pkgprefix).append("::").append(varname).c_str(), value); + } catch(const char * err) { + LogFile->write(EQEMuLog::Status, "Error exporting var: %s", err); + } +} + +void PerlembParser::ExportVarComplex(const char * pkgprefix, const char * varname, const char * value) const +{ + + if(!perl) + return; + try + { + //todo: consider replacing ' w/ ", so that values can be expanded on the perl side + perl->eval(std::string("$").append(pkgprefix).append("::").append(varname).append("=").append(value).append(";").c_str()); + } + catch(const char * err) + { //todo: consider rethrowing + LogFile->write(EQEMuLog::Status, "Error exporting var: %s", err); + } +} + +void PerlembParser::HandleQueue() { + if(eventQueueProcessing) + return; + eventQueueProcessing = true; + + while(!eventQueue.empty()) { + EventRecord e = eventQueue.front(); + eventQueue.pop(); + + EventCommon(e.event, e.objid, e.data.c_str(), e.npcmob, e.iteminst, e.mob, e.extradata, e.global); + } + + eventQueueProcessing = false; +} + +void PerlembParser::EventCommon(QuestEventID event, uint32 objid, const char * data, NPC* npcmob, ItemInst* iteminst, Mob* mob, uint32 extradata, bool global) +{ + if(!perl) + return; + + if(event >= _LargestEventID) + return; + + if(perl->InUse()) { + //queue the event for later. + EventRecord e; + e.event = event; + e.objid = objid; + if(data != NULL) + e.data = data; + e.npcmob = npcmob; + e.iteminst = iteminst; + e.mob = mob; + e.extradata = extradata; + e.global = global; + eventQueue.push(e); + return; + } + + bool isPlayerQuest = false; + bool isGlobalPlayerQuest = false; + bool isGlobalNPC = false; + bool isItemQuest = false; + bool isSpellQuest = false; + if(event == EVENT_SPELL_EFFECT_CLIENT || + event == EVENT_SPELL_EFFECT_NPC || + event == EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT || + event == EVENT_SPELL_EFFECT_BUFF_TIC_NPC || + event == EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE) + { + isSpellQuest = true; + } + else + { + if(!npcmob && mob) { + if(!iteminst) { + if(global) { + isGlobalPlayerQuest = true; + } else { + isPlayerQuest = true; + } + } + else + isItemQuest = true; + } + } + + string packagename; + if(!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest){ + + if(global){ + isGlobalNPC = true; + packagename = "global_npc"; + LoadGlobalNPCScript(); + }else{ + + packagename = GetPkgPrefix(objid); + + if(!isloaded(packagename.c_str())) + { + LoadScript(objid, zone->GetShortName()); + } + } + } + else if(isItemQuest) { + const Item_Struct* item = iteminst->GetItem(); + if (!item) return; + + if (event == EVENT_SCALE_CALC || event == EVENT_ITEM_ENTERZONE) { + packagename = item->CharmFile; + if(!isloaded(packagename.c_str())) { + LoadItemScript(iteminst, packagename, itemQuestScale); + } + } + else if (event == EVENT_ITEM_CLICK || event == EVENT_ITEM_CLICK_CAST) { + packagename = "script_"; + packagename += itoa(item->ScriptFileID); + if(!isloaded(packagename.c_str())) { + LoadItemScript(iteminst, packagename, itemScriptFileID); + } + } + else { + packagename = "item_"; + packagename += itoa(objid); + if(!isloaded(packagename.c_str())) + LoadItemScript(iteminst, packagename, itemQuestID); + } + } + else if(isPlayerQuest) { + if(!zone || !zone->GetShortName()) // possible segfault fix + return; + packagename = "player"; + packagename += "_"; + packagename += zone->GetShortName(); + + if(!isloaded(packagename.c_str())) + { + LoadPlayerScript(zone->GetShortName()); + } + } + else if(isGlobalPlayerQuest) { + packagename = "global_player"; + + if(!isloaded(packagename.c_str())) + { + LoadGlobalPlayerScript(); + } + } + else + { + packagename = "spell_effect_"; + packagename += data; + if(!isloaded(packagename.c_str())) + { + LoadSpellScript(atoi(data)); + } + } + + const char *sub_name = QuestEventSubroutines[event]; + + //make sure the sub we need even exists before we even do all this crap. + if(!perl->SubExists(packagename.c_str(), sub_name)) { + return; + } + + int charid = 0; + if (mob && mob->IsClient()) { // some events like waypoint and spawn don't have a player involved + charid = mob->CastToClient()->CharacterID(); + } else { + if(npcmob) + { + charid = -npcmob->GetNPCTypeID(); // make char id negative npc id as a fudge + } + else if(mob && mob->IsNPC()) + { + charid = -mob->CastToNPC()->GetNPCTypeID(); // make char id negative npc id as a fudge + } + } + ExportVar(packagename.c_str(), "charid", charid); + + //NPC quest + if(!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) + { + //only export for npcs that are global enabled. + if(npcmob && npcmob->GetQglobal()) + { + map globhash; + QGlobalCache *npc_c = NULL; + QGlobalCache *char_c = NULL; + QGlobalCache *zone_c = NULL; + + //retrieve our globals + npc_c = npcmob->GetQGlobals(); + if(mob && mob->IsClient()) + char_c = mob->CastToClient()->GetQGlobals(); + zone_c = zone->GetQGlobals(); + + if(!npc_c) + { + npc_c = npcmob->CreateQGlobals(); + npc_c->LoadByNPCID(npcmob->GetNPCTypeID()); + } + + if(!char_c) + { + if(mob && mob->IsClient()) + { + char_c = mob->CastToClient()->CreateQGlobals(); + char_c->LoadByCharID(mob->CastToClient()->CharacterID()); + } + } + + if(!zone_c) + { + zone_c = zone->CreateQGlobals(); + zone_c->LoadByZoneID(zone->GetZoneID()); + zone_c->LoadByGlobalContext(); + } + + std::list globalMap; + if(npc_c) + { + QGlobalCache::Combine(globalMap, npc_c->GetBucket(), npcmob->GetNPCTypeID(), charid, zone->GetZoneID()); + } + + if(char_c) + { + QGlobalCache::Combine(globalMap, char_c->GetBucket(), npcmob->GetNPCTypeID(), charid, zone->GetZoneID()); + } + + if(zone_c) + { + QGlobalCache::Combine(globalMap, zone_c->GetBucket(), npcmob->GetNPCTypeID(), charid, zone->GetZoneID()); + } + + std::list::iterator iter = globalMap.begin(); + while(iter != globalMap.end()) + { + globhash[(*iter).name] = (*iter).value; + ExportVar(packagename.c_str(), (*iter).name.c_str(), (*iter).value.c_str()); + ++iter; + } + ExportHash(packagename.c_str(), "qglobals", globhash); + } + } + else + { + map globhash; + QGlobalCache *char_c = NULL; + QGlobalCache *zone_c = NULL; + + //retrieve our globals + if(mob && mob->IsClient()) + char_c = mob->CastToClient()->GetQGlobals(); + zone_c = zone->GetQGlobals(); + + if(!char_c) + { + if(mob && mob->IsClient()) + { + char_c = mob->CastToClient()->CreateQGlobals(); + char_c->LoadByCharID(mob->CastToClient()->CharacterID()); + } + } + + if(!zone_c) + { + zone_c = zone->CreateQGlobals(); + zone_c->LoadByZoneID(zone->GetZoneID()); + zone_c->LoadByGlobalContext(); + } + + std::list globalMap; + if(char_c) + { + QGlobalCache::Combine(globalMap, char_c->GetBucket(), 0, charid, zone->GetZoneID()); + } + + if(zone_c) + { + QGlobalCache::Combine(globalMap, zone_c->GetBucket(), 0, charid, zone->GetZoneID()); + } + + std::list::iterator iter = globalMap.begin(); + while(iter != globalMap.end()) + { + globhash[(*iter).name] = (*iter).value; + ExportVar(packagename.c_str(), (*iter).name.c_str(), (*iter).value.c_str()); + ++iter; + } + ExportHash(packagename.c_str(), "qglobals", globhash); + } + + uint8 fac = 0; + if (mob && mob->IsClient()) { + ExportVar(packagename.c_str(), "uguild_id", mob->CastToClient()->GuildID()); + ExportVar(packagename.c_str(), "uguildrank", mob->CastToClient()->GuildRank()); + ExportVar(packagename.c_str(), "status", mob->CastToClient()->Admin()); + } + + if(!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest){ + if (mob && npcmob && mob->IsClient() && npcmob->IsNPC()) { + Client* client = mob->CastToClient(); + NPC* npc = npcmob->CastToNPC(); + + // Need to figure out why one of these casts would fail.. + if (client && npc) { + fac = client->GetFactionLevel(client->CharacterID(), npcmob->GetID(), client->GetRace(), client->GetClass(), client->GetDeity(), npc->GetPrimaryFaction(), npcmob); + } + else if (!client) { + LogFile->write(EQEMuLog::Status, "WARNING: cast failure on mob->CastToClient()"); + } + else if (!npc) { + LogFile->write(EQEMuLog::Status, "WARNING: cast failure on npcmob->CastToNPC()"); + } + } + } + if (mob) { + ExportVar(packagename.c_str(), "name", mob->GetName()); + ExportVar(packagename.c_str(), "race", GetRaceName(mob->GetRace())); + ExportVar(packagename.c_str(), "class", GetEQClassName(mob->GetClass())); + ExportVar(packagename.c_str(), "ulevel", mob->GetLevel()); + ExportVar(packagename.c_str(), "userid", mob->GetID()); + } + + if(!isPlayerQuest && !isGlobalPlayerQuest && !isItemQuest && !isSpellQuest) + { + if (npcmob) + { + ExportVar(packagename.c_str(), "mname", npcmob->GetName()); + ExportVar(packagename.c_str(), "mobid", npcmob->GetID()); + ExportVar(packagename.c_str(), "mlevel", npcmob->GetLevel()); + ExportVar(packagename.c_str(), "hpratio",npcmob->GetHPRatio()); + ExportVar(packagename.c_str(), "x", npcmob->GetX() ); + ExportVar(packagename.c_str(), "y", npcmob->GetY() ); + ExportVar(packagename.c_str(), "z", npcmob->GetZ() ); + ExportVar(packagename.c_str(), "h", npcmob->GetHeading() ); + if ( npcmob->GetTarget() ) { + ExportVar(packagename.c_str(), "targetid", npcmob->GetTarget()->GetID()); + ExportVar(packagename.c_str(), "targetname", npcmob->GetTarget()->GetName()); + } + } + + if (fac) { + ExportVar(packagename.c_str(), "faction", itoa(fac)); + } + } + + if (zone) { + ExportVar(packagename.c_str(), "zoneid", zone->GetZoneID()); + ExportVar(packagename.c_str(), "zoneln", zone->GetLongName()); + ExportVar(packagename.c_str(), "zonesn", zone->GetShortName()); + ExportVar(packagename.c_str(), "instanceid", zone->GetInstanceID()); + ExportVar(packagename.c_str(), "instanceversion", zone->GetInstanceVersion()); + TimeOfDay_Struct eqTime; + zone->zone_time.getEQTimeOfDay( time(0), &eqTime); + ExportVar(packagename.c_str(), "zonehour", eqTime.hour - 1); + ExportVar(packagename.c_str(), "zonemin", eqTime.minute); + ExportVar(packagename.c_str(), "zonetime", (eqTime.hour - 1) * 100 + eqTime.minute); + ExportVar(packagename.c_str(), "zoneweather", zone->zone_weather); + } + +// $hasitem +#define HASITEM_FIRST 0 +#define HASITEM_LAST 29 // this includes worn plus 8 base slots +#define HASITEM_ISNULLITEM(item) ((item==-1) || (item==0)) + + if(mob && mob->IsClient()) + { + string hashname = packagename + std::string("::hasitem"); +#if EQDEBUG >= 7 + LogFile->write(EQEMuLog::Debug, "starting hasitem, on : %s",hashname.c_str() ); +#endif + + //start with an empty hash + perl->eval(std::string("%").append(hashname).append(" = ();").c_str()); + + for(int slot=HASITEM_FIRST; slot<=HASITEM_LAST;slot++) + { + char *hi_decl=NULL; + int itemid=mob->CastToClient()->GetItemIDAt(slot); + if(!HASITEM_ISNULLITEM(itemid)) + { + MakeAnyLenString(&hi_decl, "push (@{$%s{%d}},%d);",hashname.c_str(),itemid,slot); +// this is annoying +#if EQDEBUG >= 7 + LogFile->write(EQEMuLog::Debug, "declare hasitem : %s",hi_decl); +#endif + perl->eval(hi_decl); + safe_delete_array(hi_decl); + } + } + } +// $oncursor + if(mob && mob->IsClient()) { + string hashname = packagename + std::string("::oncursor"); + perl->eval(std::string("%").append(hashname).append(" = ();").c_str()); + char *hi_decl = NULL; + int itemid = mob->CastToClient()->GetItemIDAt(30); + if(!HASITEM_ISNULLITEM(itemid)) { + MakeAnyLenString(&hi_decl, "push (@{$%s{%d}},%d);",hashname.c_str(),itemid,30); + perl->eval(hi_decl); + safe_delete_array(hi_decl); + } + } + //do any event-specific stuff... + switch (event) { + case EVENT_SAY: { + if (npcmob && npcmob->GetAppearance() != eaDead) + npcmob->FaceTarget(mob); + ExportVar(packagename.c_str(), "data", objid); + ExportVar(packagename.c_str(), "text", data); + ExportVar(packagename.c_str(), "langid", extradata); + break; + } + case EVENT_ITEM: { + if (npcmob->GetAppearance() != eaDead) + npcmob->FaceTarget(mob); + //this is such a hack... why aren't these just set directly.. + ExportVar(packagename.c_str(), "item1", GetVar("item1", objid).c_str()); + ExportVar(packagename.c_str(), "item2", GetVar("item2", objid).c_str()); + ExportVar(packagename.c_str(), "item3", GetVar("item3", objid).c_str()); + ExportVar(packagename.c_str(), "item4", GetVar("item4", objid).c_str()); + ExportVar(packagename.c_str(), "item1_charges", GetVar("item1.charges", objid).c_str()); + ExportVar(packagename.c_str(), "item2_charges", GetVar("item2.charges", objid).c_str()); + ExportVar(packagename.c_str(), "item3_charges", GetVar("item3.charges", objid).c_str()); + ExportVar(packagename.c_str(), "item4_charges", GetVar("item4.charges", objid).c_str()); + ExportVar(packagename.c_str(), "item1_attuned", GetVar("item1.attuned", objid).c_str()); + ExportVar(packagename.c_str(), "item2_attuned", GetVar("item2.attuned", objid).c_str()); + ExportVar(packagename.c_str(), "item3_attuned", GetVar("item3.attuned", objid).c_str()); + ExportVar(packagename.c_str(), "item4_attuned", GetVar("item4.attuned", objid).c_str()); + ExportVar(packagename.c_str(), "copper", GetVar("copper", objid).c_str()); + ExportVar(packagename.c_str(), "silver", GetVar("silver", objid).c_str()); + ExportVar(packagename.c_str(), "gold", GetVar("gold", objid).c_str()); + ExportVar(packagename.c_str(), "platinum", GetVar("platinum", objid).c_str()); + string hashname = packagename + std::string("::itemcount"); + perl->eval(std::string("%").append(hashname).append(" = ();").c_str()); + perl->eval(std::string("++$").append(hashname).append("{$").append(packagename).append("::item1};").c_str()); + perl->eval(std::string("++$").append(hashname).append("{$").append(packagename).append("::item2};").c_str()); + perl->eval(std::string("++$").append(hashname).append("{$").append(packagename).append("::item3};").c_str()); + perl->eval(std::string("++$").append(hashname).append("{$").append(packagename).append("::item4};").c_str()); + break; + } + case EVENT_WAYPOINT_ARRIVE: + case EVENT_WAYPOINT_DEPART: { + ExportVar(packagename.c_str(), "wp", data); + break; + } + case EVENT_HP: { + if (extradata == 1) { + ExportVar(packagename.c_str(), "hpevent", "-1"); + ExportVar(packagename.c_str(), "inchpevent", data); + } + else + { + ExportVar(packagename.c_str(), "hpevent", data); + ExportVar(packagename.c_str(), "inchpevent", "-1"); + } + break; +} + case EVENT_TIMER: { + ExportVar(packagename.c_str(), "timer", data); + break; + } + case EVENT_SIGNAL: { + ExportVar(packagename.c_str(), "signal", data); + break; + } + case EVENT_NPC_SLAY: { + ExportVar(packagename.c_str(), "killed", mob->GetNPCTypeID()); + break; + } + case EVENT_COMBAT: { + ExportVar(packagename.c_str(), "combat_state", data); + break; + } + + case EVENT_CLICKDOOR: { + Seperator *sep = new Seperator(data); + ExportVar(packagename.c_str(), "doorid", sep->arg[0]); + ExportVar(packagename.c_str(), "version", sep->arg[1]); + break; + } + + case EVENT_LOOT:{ + Seperator *sep = new Seperator(data); + ExportVar(packagename.c_str(), "looted_id", sep->arg[0]); + ExportVar(packagename.c_str(), "looted_charges", sep->arg[1]); + ExportVar(packagename.c_str(), "corpse", sep->arg[2]); + safe_delete(sep); + break; + } + + case EVENT_ZONE:{ + ExportVar(packagename.c_str(), "target_zone_id", data); + break; + } + + case EVENT_CAST_ON: + case EVENT_CAST:{ + ExportVar(packagename.c_str(), "spell_id", data); + break; + } + + case EVENT_TASKACCEPTED:{ + ExportVar(packagename.c_str(), "task_id", data); + break; + } + + case EVENT_TASK_STAGE_COMPLETE:{ + Seperator *sep = new Seperator(data); + ExportVar(packagename.c_str(), "task_id", sep->arg[0]); + ExportVar(packagename.c_str(), "activity_id", sep->arg[1]); + safe_delete(sep); + break; + } + case EVENT_TASK_FAIL:{ + Seperator *sep = new Seperator(data); + ExportVar(packagename.c_str(), "task_id", sep->arg[0]); + safe_delete(sep); + break; + } + case EVENT_TASK_COMPLETE: + case EVENT_TASK_UPDATE:{ + Seperator *sep = new Seperator(data); + ExportVar(packagename.c_str(), "donecount", sep->arg[0]); + ExportVar(packagename.c_str(), "activity_id", sep->arg[1]); + ExportVar(packagename.c_str(), "task_id", sep->arg[2]); + safe_delete(sep); + break; + } + case EVENT_PLAYER_PICKUP:{ + ExportVar(packagename.c_str(), "picked_up_id", data); + break; + } + + case EVENT_AGGRO_SAY: { + ExportVar(packagename.c_str(), "data", objid); + ExportVar(packagename.c_str(), "text", data); + ExportVar(packagename.c_str(), "langid", extradata); + break; + } + case EVENT_POPUPRESPONSE:{ + ExportVar(packagename.c_str(), "popupid", data); + break; + } + case EVENT_PROXIMITY_SAY: { + ExportVar(packagename.c_str(), "data", objid); + ExportVar(packagename.c_str(), "text", data); + ExportVar(packagename.c_str(), "langid", extradata); + break; + } + case EVENT_SCALE_CALC: + case EVENT_ITEM_ENTERZONE: { + ExportVar(packagename.c_str(), "itemid", objid); + ExportVar(packagename.c_str(), "itemname", iteminst->GetItem()->Name); + break; + } + case EVENT_ITEM_CLICK_CAST: + case EVENT_ITEM_CLICK: { + ExportVar(packagename.c_str(), "itemid", objid); + ExportVar(packagename.c_str(), "itemname", iteminst->GetItem()->Name); + ExportVar(packagename.c_str(), "slotid", extradata); + break; + } + case EVENT_GROUP_CHANGE: { + if(mob && mob->IsClient()) + { + ExportVar(packagename.c_str(), "grouped", mob->IsGrouped()); + ExportVar(packagename.c_str(), "raided", mob->IsRaidGrouped()); + } + break; + } + case EVENT_HATE_LIST: { + ExportVar(packagename.c_str(), "hate_state", data); + break; + } + + case EVENT_SPELL_EFFECT_CLIENT: + case EVENT_SPELL_EFFECT_NPC: + case EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT: + case EVENT_SPELL_EFFECT_BUFF_TIC_NPC: + { + ExportVar(packagename.c_str(), "caster_id", extradata); + break; + } + //tradeskill events + case EVENT_COMBINE_SUCCESS: + case EVENT_COMBINE_FAILURE: + { + ExportVar(packagename.c_str(), "recipe_id", extradata); + ExportVar(packagename.c_str(), "recipe_name", data); + break; + } + + case EVENT_FORAGE_SUCCESS: { + ExportVar(packagename.c_str(), "foraged_item", extradata); + break; + } + + case EVENT_FISH_SUCCESS: { + ExportVar(packagename.c_str(), "fished_item", extradata); + break; + } + + case EVENT_CLICK_OBJECT: { + ExportVar(packagename.c_str(), "objectid", data); + break; + } + + case EVENT_DISCOVER_ITEM: { + ExportVar(packagename.c_str(), "itemid", extradata); + break; + } + + //nothing special about these events + case EVENT_DEATH: + case EVENT_SPAWN: + case EVENT_ATTACK: + case EVENT_SLAY: + case EVENT_AGGRO: + case EVENT_ENTER: + case EVENT_EXIT: + case EVENT_ENTERZONE: + case EVENT_LEVEL_UP: + case EVENT_KILLED_MERIT: + case EVENT_TARGET_CHANGE: + break; + + default: { + // should we do anything here? + break; + } + } + + if(isPlayerQuest || isGlobalPlayerQuest){ + SendCommands(packagename.c_str(), sub_name, 0, mob, mob, NULL); + } + else if(isItemQuest) { + SendCommands(packagename.c_str(), sub_name, 0, mob, mob, iteminst); + } + else if(isSpellQuest) + { + if(mob) { + SendCommands(packagename.c_str(), sub_name, 0, mob, mob, NULL); + } else { + SendCommands(packagename.c_str(), sub_name, 0, npcmob, mob, NULL); + } + } + else { + SendCommands(packagename.c_str(), sub_name, objid, npcmob, mob, NULL); + } + + //now handle any events that cropped up... + HandleQueue(); +} + +void PerlembParser::EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data) { + EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, NULL, init, extra_data, true); +} + +void PerlembParser::EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data) { + EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, NULL, init, extra_data); +} + +void PerlembParser::EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data) { + EventCommon(evt, 0, data.c_str(), NULL, NULL, client, extra_data); +} + +void PerlembParser::EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data) { + EventCommon(evt, 0, data.c_str(), NULL, NULL, client, extra_data, true); +} + +void PerlembParser::EventItem(QuestEventID evt, Client *client, ItemInst *item, uint32 objid, uint32 extra_data) { + EventCommon(evt, objid, NULL, NULL, item, client, extra_data); +} + +void PerlembParser::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data) { + EventCommon(evt, 0, itoa(spell_id), npc, NULL, client, extra_data); +} + +void PerlembParser::ReloadQuests(bool with_timers) { + + if (with_timers) + { + // Clear all quest timers before reloading quests to prevent potential crashes + quest_manager.ClearAllTimers(); + } + + command_clear_perl(); + + try { + if(perl == NULL) + perl = new Embperl; + else + perl->Reinit(); + map_funs(); + } + catch(std::exception &e) { + if(perl != NULL) { + delete perl; + perl = NULL; + } + LogFile->write(EQEMuLog::Status, "Error re-initializing perlembed: %s", e.what()); + throw e.what(); + } + try { + LoadScript(0, NULL); + } + catch(const char * err) { + LogFile->write(EQEMuLog::Status, "Error loading default script: %s", err); + } + + hasQuests.clear(); + playerQuestLoaded.clear(); + globalPlayerQuestLoaded = pQuestReadyToLoad; + globalNPCQuestLoaded = nQuestReadyToLoad; + itemQuestLoaded.clear(); + spellQuestLoaded.clear(); +} + +int PerlembParser::LoadScript(int npcid, const char * zone, Mob* activater) +{ + if(!perl) + { + return(0); + } + + //we have already tried to load this quest... + if(hasQuests.count(npcid) == 1) + { + return(1); + } + + string filename = "quests/", packagename = GetPkgPrefix(npcid); + //each package name is of the form qstxxxx where xxxx = npcid (since numbers alone are not valid package names) + questMode curmode = questDefault; + FILE *tmpf; + //LogFile->write(EQEMuLog::Debug, "LoadScript(%d, %s):\n", npcid, zone); + if(!npcid || !zone) + { + //Load quests/default.pl + filename += DEFAULT_QUEST_PREFIX; + filename += ".pl"; + curmode = questDefault; + } + else + { + filename += zone; + filename += "/"; +#ifdef QUEST_SCRIPTS_BYNAME + string bnfilename = filename; +#endif + filename += itoa(npcid); + filename += ".pl"; + curmode = questByID; + +#ifdef QUEST_SCRIPTS_BYNAME + //assuming name limit stays 64 chars. + char tmpname[64]; + int count0 = 0; + bool filefound = false; + tmpf = fopen(filename.c_str(), "r"); + if(tmpf != NULL) + { + fclose(tmpf); + filefound = true; + } + //LogFile->write(EQEMuLog::Debug, " tried '%s': %d", filename.c_str(), filefound); + + tmpname[0] = 0; + //if there is no file for the NPC's ID, try for the NPC's name + if(!filefound) + { + //revert to just path + filename = bnfilename; + const NPCType *npct = database.GetNPCType(npcid); + if(npct == NULL) + { + //LogFile->write(EQEMuLog::Debug, " no npc type"); + //revert and go on with life + filename += itoa(npcid); + filename += ".pl"; + curmode = questByID; + } + else + { + //trace out the ` characters, turn into - + int nlen = strlen(npct->name); + //just to make sure + if(nlen < 64) + { + int r; + //this should get our NULL as well.. + for(r = 0; r <= nlen; r++) + { + tmpname[r] = npct->name[r]; + + //watch for 00 delimiter + if(tmpname[r] == '0') + { + count0++; + //second '0' + if(count0 > 1) + { + //stop before previous 0 + tmpname[r-1] = '\0'; + break; + } + } + else + { + count0 = 0; + } + + //rewrite ` to be more file name friendly + if(tmpname[r] == '`') + { + tmpname[r] = '-'; + } + + } + filename += tmpname; + filename += ".pl"; + curmode = questByName; + } + else + { + //LogFile->write(EQEMuLog::Debug, " namelen too long"); + //revert and go on with life, again + filename += itoa(npcid); + filename += ".pl"; + curmode = questByID; + } + } + } + +#ifdef QUEST_TEMPLATES_BYNAME + + tmpf = fopen(filename.c_str(), "r"); + if(tmpf != NULL) + { + fclose(tmpf); + filefound = true; + } + + + //LogFile->write(EQEMuLog::Debug, " tried '%s': %d", filename.c_str(), filefound2); + + //if there is no file for the NPC's ID or name, + //try for the NPC's name in the templates directory + //only works if we have gotten the NPC's name above + if(!filefound) + { + if(tmpname[0] != 0) + { + //revert to just path + filename = "quests/"; + filename += QUEST_TEMPLATES_DIRECTORY; + filename += "/"; + filename += tmpname; + filename += ".pl"; + curmode = questTemplate; + //LogFile->write(EQEMuLog::Debug, " template '%s'", filename.c_str(), filefound2); + } + else + { + //LogFile->write(EQEMuLog::Debug, " no template name"); + filename = "quests/"; + filename += QUEST_TEMPLATES_DIRECTORY; + filename += "/"; + filename += itoa(npcid); + filename += ".pl"; + curmode = questTemplateByID; + } + } + +#endif //QUEST_TEMPLATES_BYNAME + +#endif //QUEST_SCRIPTS_BYNAME + + tmpf = fopen(filename.c_str(), "r"); + if(tmpf != NULL) + { + fclose(tmpf); + filefound = true; + } + + // If by ID, Name or Template wasn't found, load /quests/zone/default.pl + if(!filefound) + { + //Load Default Quests Per Zone quests/zonename/default.pl + filename = bnfilename; + filename += "default.pl"; + curmode = questDefaultByZone; + //LogFile->write(EQEMuLog::Debug, "LoadScript(%s)", filename.c_str()); + } + + tmpf = fopen(filename.c_str(), "r"); + if(tmpf != NULL) + { + fclose(tmpf); + filefound = true; + } + + // If zone template isn't found look for it globally /quests/template/default.pl + if(!filefound) + { + //Load Default Quests Globally + //filename = bnfilename; + filename = "quests/"; + filename += QUEST_TEMPLATES_DIRECTORY; + filename += "/"; + filename += "default.pl"; + curmode = questDefaultByZone; + //LogFile->write(EQEMuLog::Debug, "LoadScript(%s)", filename.c_str()); + } + } + + //check for existance of quest file before trying to make perl load it. + tmpf = fopen(filename.c_str(), "r"); + if(tmpf == NULL) + { + //the npc has no qst file, attach the defaults + std::string setdefcmd = "$"; + setdefcmd += packagename; + setdefcmd += "::isdefault = 1;"; + perl->eval(setdefcmd.c_str()); + setdefcmd = "$"; + setdefcmd += packagename; + setdefcmd += "::isloaded = 1;"; + perl->eval(setdefcmd.c_str()); + hasQuests[npcid] = questDefault; + return(1); + } + else + { + fclose(tmpf); + } + + //LogFile->write(EQEMuLog::Debug, " finally settling on '%s'", filename.c_str()); + // LogFile->write(EQEMuLog::Status, "Looking for quest file: '%s'", filename.c_str()); + + // todo: decide whether or not to delete the package to allow for script refreshes w/o restarting the server + // remember to guard against deleting the default package, on a similar note... consider deleting packages upon zone change + // try { perl->eval(std::string("delete_package(\"").append(packagename).append("\");").c_str()); } + // catch(...) {/*perl balked at us trynig to delete a non-existant package... no big deal.*/} + + try { + perl->eval_file(packagename.c_str(), filename.c_str()); + } + catch(const char * err) + { + //try to reduce some of the console spam... + //todo: tweak this to be more accurate at deciding what to filter (we don't want to gag legit errors) + //if(!strstr(err,"No such file or directory")) + LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s (reverting to default questfile)", filename.c_str(), err); + } + //todo: change this to just read eval_file's %cache - duh! + if(!isloaded(packagename.c_str())) + { + //the npc has no qst file, attach the defaults + std::string setdefcmd = "$"; + setdefcmd += packagename; + setdefcmd += "::isdefault = 1;"; + perl->eval(setdefcmd.c_str()); + setdefcmd = "$"; + setdefcmd += packagename; + setdefcmd += "::isloaded = 1;"; + perl->eval(setdefcmd.c_str()); + curmode = questDefault; + } + + hasQuests[npcid] = curmode; + return(1); +} + +int PerlembParser::LoadGlobalNPCScript() +{ + if(!perl) + return 0; + + if(perl->InUse()) + { + return 0; + } + + if(globalNPCQuestLoaded != nQuestReadyToLoad) { + return 1; + } + + string filename = "quests/"; + filename += QUEST_TEMPLATES_DIRECTORY; + filename += "/global_npc.pl"; + string packagename = "global_npc"; + + try { + perl->eval_file(packagename.c_str(), filename.c_str()); + } + catch(const char * err) + { + LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err); + } + + globalNPCQuestLoaded = nQuestLoaded; + + return 1; +} + +int PerlembParser::LoadPlayerScript(const char *zone_name) +{ + if(!perl) + return 0; + + if(perl->InUse()) + { + return 0; + } + + if(playerQuestLoaded.count(zone_name) == 1) { + return 1; + } + + string filename= "quests/"; + filename += zone_name; + filename += "/player_v"; + filename += itoa(zone->GetInstanceVersion()); + filename += ".pl"; + string packagename = "player"; + packagename += "_"; + packagename += zone_name; + + try { + perl->eval_file(packagename.c_str(), filename.c_str()); + } + catch(const char * err) + { + LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err); + } + + if(!isloaded(packagename.c_str())) + { + filename= "quests/"; + filename += zone_name; + filename += "/player.pl"; + try { + perl->eval_file(packagename.c_str(), filename.c_str()); + } + catch(const char * err) + { + LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err); + } + } + + //todo: change this to just read eval_file's %cache - duh! + if(!isloaded(packagename.c_str())) + { + filename = "quests/"; + filename += QUEST_TEMPLATES_DIRECTORY; + filename += "/player.pl"; + try { + perl->eval_file(packagename.c_str(), filename.c_str()); + } + catch(const char * err) + { + LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err); + } + if(!isloaded(packagename.c_str())) + { + playerQuestLoaded[zone_name] = pQuestUnloaded; + return 0; + } + } + + if(perl->SubExists(packagename.c_str(), "EVENT_CAST")) + playerQuestLoaded[zone_name] = pQuestEventCast; + else + playerQuestLoaded[zone_name] = pQuestLoaded; + return 1; +} + +int PerlembParser::LoadGlobalPlayerScript() +{ + if(!perl) + return 0; + + if(perl->InUse()) + { + return 0; + } + + if(globalPlayerQuestLoaded != pQuestReadyToLoad) { + return 1; + } + + string filename = "quests/"; + filename += QUEST_TEMPLATES_DIRECTORY; + filename += "/global_player.pl"; + string packagename = "global_player"; + + try { + perl->eval_file(packagename.c_str(), filename.c_str()); + } + catch(const char * err) + { + LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err); + } + + if(perl->SubExists(packagename.c_str(), "EVENT_CAST")) + globalPlayerQuestLoaded = pQuestEventCast; + else + globalPlayerQuestLoaded = pQuestLoaded; + return 1; +} + +int PerlembParser::LoadItemScript(ItemInst* iteminst, string packagename, itemQuestMode Qtype) { + if(!perl) + return 0; + + if(perl->InUse()) + { + return 0; + } + + // if we've already tried to load it, don't try again + if(itemQuestLoaded.count(packagename) == 1) + return 1; + + string filename = "quests/items/"; + if(Qtype == itemQuestScale) + filename += packagename; + else if(Qtype == itemQuestLore) { + filename += "lore_"; + filename += itoa(iteminst->GetItem()->LoreGroup); + } + else if(Qtype == itemScriptFileID) { + filename += "script_"; + filename += itoa(iteminst->GetItemScriptID()); + } + else + filename += itoa(iteminst->GetID()); + filename += ".pl"; + printf("Loading file %s\n",filename.c_str()); + + try { + perl->eval_file(packagename.c_str(), filename.c_str()); + } + catch(const char* err) { + LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err); + } + + if(!isloaded(packagename.c_str())) { + itemQuestLoaded[packagename] = Qtype; + return 0; + } + + itemQuestLoaded[packagename] = itemQuestUnloaded; + return 1; +} + +int PerlembParser::LoadSpellScript(uint32 id) +{ + if(!perl) + return 0; + + if(perl->InUse()) + { + return 0; + } + + // if we've already tried to load it, don't try again + if(spellQuestLoaded.count(id) == 1) + return 1; + + string filename = "quests/spells/"; + string packagename = "spell_effect_"; + filename += itoa(id); + packagename += itoa(id); + filename += ".pl"; + printf("Loading file %s\n", filename.c_str()); + + try { + perl->eval_file(packagename.c_str(), filename.c_str()); + } + catch(const char* err) { + LogFile->write(EQEMuLog::Quest, "WARNING: error compiling quest file %s: %s", filename.c_str(), err); + } + + if(!isloaded(packagename.c_str())) { + spellQuestLoaded[id] = spellQuestFailed; + return 0; + } + + spellQuestLoaded[id] = spellQuestFullyLoaded; + return 1; +} + +bool PerlembParser::isloaded(const char *packagename) const { + char buffer[120]; + snprintf(buffer, 120, "$%s::isloaded", packagename); + if(!perl->VarExists(packagename, "isloaded")) + return(false); + return perl->geti(buffer); +} + + +//this function does NOT consider the default to be a quest +int PerlembParser::HasQuestFile(uint32 npcid) { + int32 qstID = GetNPCqstID(npcid); + int success=1; + + if(hasQuests.count(npcid) == 1) { + questMode mode = hasQuests[npcid]; + if(mode == questDefault) + return(false); + return(true); + } + + if (qstID==-1) + success = LoadScript(npcid, zone->GetShortName()); + if (!success) + return(false); + + if(hasQuests.count(npcid) != 1) + return(false); + + questMode mode = hasQuests[npcid]; + if(mode == questDefault) + return(false); + + return(true); +} + +bool PerlembParser::HasQuestSub(uint32 npcid, const char *subname) { + int32 qstID = GetNPCqstID(npcid); + + if (qstID == -1) { + if(!LoadScript(npcid, zone->GetShortName())) { + return(false); + } + } + + string packagename = GetPkgPrefix(npcid); + + return(perl->SubExists(packagename.c_str(), subname)); +} + +bool PerlembParser::HasGlobalQuestSub(const char *subname) { + if(!LoadGlobalNPCScript()) { + return(false); + } + + string packagename = "global_npc"; + + return(perl->SubExists(packagename.c_str(), subname)); +} + +bool PerlembParser::PlayerHasQuestSub(const char *subname) { + + string packagename = "player_"; + packagename += zone->GetShortName(); + + if(playerQuestLoaded.count(zone->GetShortName()) == 0) + LoadPlayerScript(zone->GetShortName()); + + if(subname == "EVENT_CAST") + return (playerQuestLoaded[zone->GetShortName()] == pQuestEventCast); + + return(perl->SubExists(packagename.c_str(), subname)); +} + +bool PerlembParser::GlobalPlayerHasQuestSub(const char *subname) { + + string packagename = "global_player"; + + if(globalPlayerQuestLoaded == pQuestReadyToLoad) + LoadGlobalPlayerScript(); + + if(subname == "EVENT_CAST") + return (globalPlayerQuestLoaded == pQuestEventCast); + + return(perl->SubExists(packagename.c_str(), subname)); +} + +bool PerlembParser::SpellHasQuestSub(uint32 id, const char *subname) +{ + string packagename = "spell_effect_"; + packagename += itoa(id); + + if(spellQuestLoaded.count(id) == 0) + LoadSpellScript(id); + + return(perl->SubExists(packagename.c_str(), subname)); +} + +bool PerlembParser::ItemHasQuestSub(ItemInst *itm, const char *subname) +{ + string packagename; + const Item_Struct* item = itm->GetItem(); + if(!item) + return false; + + if(strcmp("EVENT_SCALE_CALC", subname) == 0 || strcmp("EVENT_ITEM_ENTERZONE", subname) == 0) + { + packagename = item->CharmFile; + if(itemQuestLoaded.count(packagename) == 0) + LoadItemScript(itm, packagename, itemQuestScale); + } + else if(strcmp("EVENT_ITEM_CLICK", subname) == 0 || strcmp("EVENT_ITEM_CLICK_CAST", subname) == 0 ) + { + packagename = "script_"; + packagename += itoa(item->ScriptFileID); + if(itemQuestLoaded.count(packagename) == 0) + LoadItemScript(itm, packagename, itemScriptFileID); + } + else + { + packagename = "item_"; + packagename += itoa(item->ID); + if(itemQuestLoaded.count(packagename) == 0) + LoadItemScript(itm, packagename, itemQuestID); + } + + return perl->SubExists(packagename.c_str(), subname); +} + +//utility - return something of the form "qst1234"... +//will return "qst[DEFAULT_QUEST_PREFIX]" if the npc in question has no script of its own or failed to compile and defaultOK is set to true +std::string PerlembParser::GetPkgPrefix(uint32 npcid, bool defaultOK) +{ + char buf[32]; + snprintf(buf, 32, "qst%lu", (unsigned long) npcid); +// std::string prefix = "qst"; +// std::string temp = prefix + (std::string)(itoa(npcid)); +// if(!npcid || (defaultOK && isdefault(temp.c_str()))) + if(!npcid || (defaultOK && (hasQuests.count(npcid) == 1 && hasQuests[npcid] == questDefault))) + { + snprintf(buf, 32, "qst%s", DEFAULT_QUEST_PREFIX.c_str()); + } + + return(std::string(buf)); +} + +void PerlembParser::SendCommands(const char * pkgprefix, const char *event, uint32 npcid, Mob* other, Mob* mob, ItemInst* iteminst) +{ + if(!perl) + return; + _ZP(PerlembParser_SendCommands); + + if(mob && mob->IsClient()) + quest_manager.StartQuest(other, mob->CastToClient()); + else + quest_manager.StartQuest(other, NULL); + + try + { + std::string cmd = "@quest::cmd_queue = (); package " + (std::string)(pkgprefix) + (std::string)(";"); + perl->eval(cmd.c_str()); + perl->dosub(std::string(pkgprefix).append("::").append(event).c_str()); + } + catch(const char * err) + { + //try to reduce some of the console spam... + //todo: tweak this to be more accurate at deciding what to filter (we don't want to gag legit errors) + if(!strstr(err,"Undefined subroutine")) + LogFile->write(EQEMuLog::Status, "Script error: %s::%s - %s", pkgprefix, event, err); + return; + } + + int numcoms = perl->geti("quest::qsize()"); + for(int c = 0; c < numcoms; ++c) + { + char var[1024] = {0}; + sprintf(var,"$quest::cmd_queue[%d]{func}",c); + std::string cmd = perl->getstr(var); + sprintf(var,"$quest::cmd_queue[%d]{args}",c); + std::string args = perl->getstr(var); + size_t num_args = std::count(args.begin(), args.end(), ',') + 1; + + ExCommands(cmd, args, num_args, npcid, other, mob); + } + + quest_manager.EndQuest(); +} + +#ifdef EMBPERL_COMMANDS +void PerlembParser::ExecCommand(Client *c, Seperator *sep) { +#ifdef EMBPERL_XS_CLASSES + SV *client = get_sv("commands::client", true); + if(c != NULL) { + sv_setref_pv(client, "Client", c); + } else { + //clear out the value, mainly to get rid of blessedness + //which prevents us from accessing an invalid pointer + sv_setsv(client, newSV(0)); + } +#endif + + char namebuf[128]; + snprintf(namebuf, 128, "commands::%s", sep->arg[0]+1); + namebuf[127] = '\0'; + std::vector args; + int i; + for(i = 1; i <= sep->argnum; i++) { + args.push_back(sep->arg[i]); + } + + try + { + perl->dosub(namebuf, &args); + } catch(const char * err) + { + c->Message(13, "Error executing perl command, check the logs."); + LogFile->write(EQEMuLog::Quest, "Script error: %s", err); + } + + //now handle any events that cropped up... + HandleQueue(); +} +#endif + +void PerlembParser::map_funs() +{ + //map each "exported" function to a variable list that we can access from c + //todo: + // break 1|settimer 2|stoptimer 1|dbspawnadd 2|flagcheck 1|write 2| + // settarget 2|follow 1|sfollow 1|save 1|setallskill 1 + //update/ensure that the api matches that of the native script engine + perl->eval( +"{" +"package quest;" +"&boot_qc;" +"@cmd_queue = ();" +"sub qsize{return scalar(@cmd_queue)};" +"sub say{push(@cmd_queue,{func=>'say',args=>join(',',@_)});}" +"sub emote{push(@cmd_queue,{func=>'emote',args=>join(',',@_)});}" +"sub shout{push(@cmd_queue,{func=>'shout',args=>join(',',@_)});}" +"sub spawn{push(@cmd_queue,{func=>'spawn',args=>join(',',@_)});}" +"sub spawn2{push(@cmd_queue,{func=>'spawn2',args=>join(',',@_)});}" +"sub unique_spawn{push(@cmd_queue,{func=>'unique_spawn',args=>join(',',@_)});}" +"sub echo{push(@cmd_queue,{func=>'echo',args=>join(',',@_)});}" +"sub summonitem{push(@cmd_queue,{func=>'summonitem',args=>join(',',@_)});}" +"sub castspell{push(@cmd_queue,{func=>'castspell',args=>join(',',@_)});}" +"sub selfcast{push(@cmd_queue,{func=>'selfcast',args=>join(',',@_)});}" +"sub depop{push(@cmd_queue,{func=>'depop'});}" +"sub exp{push(@cmd_queue,{func=>'exp',args=>join(',',@_)});}" +"sub level{push(@cmd_queue,{func=>'level',args=>join(',',@_)});}" +"sub safemove{push(@cmd_queue,{func=>'safemove'});}" +"sub rain{push(@cmd_queue,{func=>'rain',args=>join(',',@_)});}" +"sub snow{push(@cmd_queue,{func=>'snow',args=>join(',',@_)});}" +"sub givecash{push(@cmd_queue,{func=>'givecash',args=>join(',',@_)});}" +"sub pvp{push(@cmd_queue,{func=>'pvp',args=>join(',',@_)});}" +"sub doanim{push(@cmd_queue,{func=>'doanim',args=>join(',',@_)});}" +"sub addskill{push(@cmd_queue,{func=>'addskill',args=>join(',',@_)});}" +"sub me{push(@cmd_queue,{func=>'me',args=>join(',',@_)});}" +"sub permagender{push(@cmd_queue,{func=>'permagender',args=>join(',',@_)});}" +"sub permarace{push(@cmd_queue,{func=>'permarace',args=>join(',',@_)});}" +"sub scribespells{push(@cmd_queue,{func=>'scribespells',args=>join(',',@_)});}" +"sub permaclass{push(@cmd_queue,{func=>'permaclass',args=>join(',',@_)});}" +"sub surname{push(@cmd_queue,{func=>'surname',args=>join(',',@_)});}" +"sub addldonpoint{push(@cmd_queue,{func=>'addldonpoint',args=>join(',',@_)});}" +"sub ding{push(@cmd_queue,{func=>'ding',args=>join(',',@_)});}" +"sub faction{push(@cmd_queue,{func=>'faction',args=>join(',',@_)});}" +"sub setguild{push(@cmd_queue,{func=>'setguild',args=>join(',',@_)});}" +"sub rebind{push(@cmd_queue,{func=>'rebind',args=>join(',',@_)});}" +"sub flagcheck{push(@cmd_queue,{func=>'flagcheck',args=>join(',',@_)});}" +"sub write{push(@cmd_queue,{func=>'write',args=>join(',',@_)});}" +"sub settime{push(@cmd_queue,{func=>'settime',args=>join(',',@_)});}" +"sub setsky{push(@cmd_queue,{func=>'setsky',args=>join(',',@_)});}" +"sub settimer{push(@cmd_queue,{func=>'settimer',args=>join(',',@_)});}" +"sub stoptimer{push(@cmd_queue,{func=>'stoptimer',args=>join(',',@_)});}" +"sub settarget{push(@cmd_queue,{func=>'settarget',args=>join(',',@_)});}" +"sub follow{push(@cmd_queue,{func=>'follow',args=>join(',',@_)});}" +"sub sfollow{push(@cmd_queue,{func=>'sfollow',args=>join(',',@_)});}" +"sub movepc{push(@cmd_queue,{func=>'movepc',args=>join(',',@_)});}" +"sub gmmove{push(@cmd_queue,{func=>'gmmove',args=>join(',',@_)});}" +"sub movegrp{push(@cmd_queue,{func=>'movegrp',args=>join(',',@_)});}" +"sub setlanguage{push(@cmd_queue,{func=>'setlanguage',args=>join(',',@_)});}" +"sub setskill{push(@cmd_queue,{func=>'setskill',args=>join(',',@_)});}" +"sub setallskill{push(@cmd_queue,{func=>'setallskill',args=>join(',',@_)});}" +"sub attack{push(@cmd_queue,{func=>'attack',args=>join(',',@_)});}" +"sub save{push(@cmd_queue,{func=>'save',args=>join(',',@_)});}" +"sub linkitem{push(@cmd_queue,{func=>'linkitem',args=>join(',',@_)});}" +"sub sethp{push(@cmd_queue,{func=>'sethp',args=>join(',',@_)});}" +"sub signal{push(@cmd_queue,{func=>'signal',args=>join(',',@_)});}" +"sub setglobal{push(@cmd_queue,{func=>'setglobal',args=>join(',',@_)});}" +"sub targlobal{push(@cmd_queue,{func=>'targlobal',args=>join(',',@_)});}" +"sub delglobal{push(@cmd_queue,{func=>'delglobal',args=>join(',',@_)});}" +"sub setnexthpevent{push(@cmd_queue,{func=>'setnexthpevent',args=>join(',',@_)});}" +"sub setnextinchpevent{push(@cmd_queue,{func=>'setnextinchpevent',args=>join(',',@_)});}" +"sub respawn{push(@cmd_queue,{func=>'respawn',args=>join(',',@_)});}" +"sub stop{push(@cmd_queue,{func=>'stop',args=>join(',',@_)});}" +"sub pause{push(@cmd_queue,{func=>'pause',args=>join(',',@_)});}" +"sub resume{push(@cmd_queue,{func=>'resume',args=>join(',',@_)});}" +"sub start{push(@cmd_queue,{func=>'start',args=>join(',',@_)});}" +"sub moveto{push(@cmd_queue,{func=>'moveto',args=>join(',',@_)});}" +"sub warp{push(@cmd_queue,{func=>'warp',args=>join(',',@_)});}" +"sub changedeity{push(@cmd_queue,{func=>'changedeity',args=>join(',',@_)});}" +"sub addldonpoints{push(@cmd_queue,{func=>'addldonpoints',args=>join(',',@_)});}" +"sub addloot{push(@cmd_queue,{func=>'addloot',args=>join(',',@_)});}" +"sub traindisc{push(@cmd_queue,{func=>'traindisc',args=>join(',',@_)});}" +"sub set_proximity{push(@cmd_queue,{func=>'set_proximity',args=>join(',',@_)});}" +"sub clear_proximity{push(@cmd_queue,{func=>'clear_proximity',args=>join(',',@_)});}" +"sub setanim{push(@cmd_queue,{func=>'setanim',args=>join(',',@_)});}" +"sub showgrid{push(@cmd_queue,{func=>'showgrid',args=>join(',',@_)});}" +"sub showpath{push(@cmd_queue,{func=>'showpath',args=>join(',',@_)});}" +"sub pathto{push(@cmd_queue,{func=>'pathto',args=>join(',',@_)});}" +"sub spawn_condition{push(@cmd_queue,{func=>'spawn_condition',args=>join(',',@_)});}" +"sub toggle_spawn_event{push(@cmd_queue,{func=>'toggle_spawn_event',args=>join(',',@_)});}" +"sub set_zone_flag{push(@cmd_queue,{func=>'set_zone_flag',args=>join(',',@_)});}" +"sub clear_zone_flag{push(@cmd_queue,{func=>'clear_zone_flag',args=>join(',',@_)});}" +"package main;" +"}" +);//eval +} + +#endif //EMBPERL + +#endif //EMBPARSER_CPP diff --git a/zone/embparser.h b/zone/embparser.h new file mode 100644 index 000000000..daebae775 --- /dev/null +++ b/zone/embparser.h @@ -0,0 +1,156 @@ +//extends the parser to include perl +//Eglin + +#ifndef EMBPARSER_H +#define EMBPARSER_H + +#ifdef EMBPERL + +#include "client.h" +#include "parser.h" +#include "embperl.h" +#include "features.h" +#include "QuestParserCollection.h" +#include "QuestInterface.h" + +#include +#include +#include +using namespace std; + +class Seperator; + +typedef enum { + questDefault = 1, + questDefaultByZone, + questByName, + questTemplate, + questTemplateByID, + questByID +} questMode; + +typedef enum { + itemQuestUnloaded = 1, + itemQuestScale, + itemQuestLore, + itemQuestID, + itemScriptFileID +} itemQuestMode; + +typedef enum { + pQuestLoaded = 1, + pQuestUnloaded, + pQuestEventCast, // player.pl loaded, has an EVENT_CAST sub + pQuestReadyToLoad +} playerQuestMode; + +typedef enum { + nQuestLoaded = 1, + nQuestUnloaded, + nQuestReadyToLoad +} GlobalNPCQuestMode; + +typedef enum { + spellQuestUnloaded = 1, + spellQuestFullyLoaded, + spellQuestFailed +} spellQuestMode; + + +struct EventRecord { + QuestEventID event; + uint32 objid; + string data; + NPC* npcmob; + ItemInst* iteminst; + Mob* mob; + uint32 extradata; + bool global; +}; + +class PerlembParser : public Parser +{ +protected: + + //could prolly get rid of this map now, since I check for the + //actual subroutine in the quest package as opposed to just seeing + //if they do not have a quest or the default. + map hasQuests; //npcid -> questMode + map playerQuestLoaded; //zone shortname -> playerQuestMode + playerQuestMode globalPlayerQuestLoaded; + GlobalNPCQuestMode globalNPCQuestLoaded; + map itemQuestLoaded; // package name - > itemQuestMode + map spellQuestLoaded; + + queue eventQueue; //for events that happen when perl is in use. + bool eventQueueProcessing; + + void HandleQueue(); + + void EventCommon(QuestEventID event, uint32 objid, const char * data, NPC* npcmob, ItemInst* iteminst, Mob* mob, uint32 extradata, bool global = false); + + Embperl * perl; + //export a symbol table of sorts + virtual void map_funs(); +public: + PerlembParser(void); + ~PerlembParser(); + Embperl * getperl(void) { return perl; }; + //todo, consider making the following two methods static (need to check for perl!=null, first, then) + bool isloaded(const char *packagename) const; + + //interface stuff + virtual void EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data); + virtual void EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data); + virtual void EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data); + virtual void EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data); + virtual void EventItem(QuestEventID evt, Client *client, ItemInst *item, uint32 objid, uint32 extra_data); + virtual void EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data); + + virtual bool HasQuestSub(uint32 npcid, const char *subname); + virtual bool HasGlobalQuestSub(const char *subname); + virtual bool PlayerHasQuestSub(const char *subname); + virtual bool GlobalPlayerHasQuestSub(const char *subname); + virtual bool SpellHasQuestSub(uint32 spell_id, const char *subname); + virtual bool ItemHasQuestSub(ItemInst *itm, const char *subname); + + virtual void ReloadQuests(bool with_timers = false); + virtual void AddVar(std::string name, std::string val) { Parser::AddVar(name, val); }; + virtual uint32 GetIdentifier() { return 0xf8b05c11; } + + int LoadScript(int npcid, const char * zone, Mob* activater=0); + int LoadGlobalNPCScript(); + int LoadPlayerScript(const char *zone); + int LoadGlobalPlayerScript(); + int LoadItemScript(ItemInst* iteminst, string packagename, itemQuestMode Qtype); + int LoadSpellScript(uint32 id); + + //expose a var to the script (probably parallels addvar)) + //i.e. exportvar("qst1234", "name", "somemob"); + //would expose the variable $name='somemob' to the script that handles npc1234 + void ExportHash(const char *pkgprefix, const char *hashname, std::map &vals); + void ExportVar(const char * pkgprefix, const char * varname, const char * value) const; + void ExportVar(const char * pkgprefix, const char * varname, int value) const; + void ExportVar(const char * pkgprefix, const char * varname, unsigned int value) const; + void ExportVar(const char * pkgprefix, const char * varname, float value) const; + //I don't escape the strings, so use caution!! + //Same as export var, except value is not quoted, and is evaluated as perl + void ExportVarComplex(const char * pkgprefix, const char * varname, const char * value) const; + + //get an appropriate namespage/packagename from an npcid + std::string GetPkgPrefix(uint32 npcid, bool defaultOK = true); + //call the appropriate perl handler. afterwards, parse and dispatch the command queue + //SendCommands("qst1234", "EVENT_SAY") would trigger sub EVENT_SAY() from the qst1234.pl file + virtual void SendCommands(const char * pkgprefix, const char *event, uint32 npcid, Mob* other, Mob* mob, ItemInst* iteminst); + + int HasQuestFile(uint32 npcid); + +#ifdef EMBPERL_COMMANDS + void ExecCommand(Client *c, Seperator *sep); +#endif + +}; + +#endif //EMBPERL + +#endif //EMBPARSER_H diff --git a/zone/embperl.cpp b/zone/embperl.cpp new file mode 100644 index 000000000..c94a7537a --- /dev/null +++ b/zone/embperl.cpp @@ -0,0 +1,372 @@ +/* +embperl.cpp +--------------- +wraps a perl interpreter for use in eqemu +Eglin +*/ + +#ifndef EMBPERL_CPP +#define EMBPERL_CPP + +#ifdef EMBPERL + +#include "../common/debug.h" +#include +#include +#include +#include "embperl.h" +#include "embxs.h" +#include "features.h" +#ifndef GvCV_set +#define GvCV_set(gv,cv) (GvCV(gv) = (cv)) +#endif + +#ifdef EMBPERL_XS +EXTERN_C XS(boot_quest); +#ifdef EMBPERL_XS_CLASSES +EXTERN_C XS(boot_Mob); +EXTERN_C XS(boot_NPC); +EXTERN_C XS(boot_Client); +EXTERN_C XS(boot_Corpse); +EXTERN_C XS(boot_EntityList); +EXTERN_C XS(boot_Group); +EXTERN_C XS(boot_Raid); +EXTERN_C XS(boot_QuestItem); +EXTERN_C XS(boot_HateEntry); +EXTERN_C XS(boot_Object); +EXTERN_C XS(boot_Doors); +EXTERN_C XS(boot_PerlPacket); +/*XS(XS_Client_new); +//XS(XS_Mob_new); +XS(XS_NPC_new); +//XS(XS_Corpse_new); +XS(XS_EntityList_new); +//XS(XS_Group_new);*/ +#endif +#endif +#ifdef EMBPERL_COMMANDS +XS(XS_command_add); +#endif + +#ifdef EMBPERL_IO_CAPTURE +XS(XS_EQEmuIO_PRINT); +#endif //EMBPERL_IO_CAPTURE + +//so embedded scripts can use xs extensions (ala 'use socket;') +EXTERN_C void boot_DynaLoader(pTHX_ CV* cv); +EXTERN_C void xs_init(pTHX) +{ + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = '\0'; + + char buf[128]; //shouldent have any function names longer than this. + + //add the strcpy stuff to get rid of const warnings.... + + newXS(strcpy(buf, "DynaLoader::boot_DynaLoader"), boot_DynaLoader, file); + newXS(strcpy(buf, "quest::boot_qc"), boot_qc, file); +#ifdef EMBPERL_XS + newXS(strcpy(buf, "quest::boot_quest"), boot_quest, file); +#ifdef EMBPERL_XS_CLASSES + newXS(strcpy(buf, "Mob::boot_Mob"), boot_Mob, file); + newXS(strcpy(buf, "NPC::boot_Mob"), boot_Mob, file); + newXS(strcpy(buf, "NPC::boot_NPC"), boot_NPC, file); +/// newXS(strcpy(buf, "NPC::new"), XS_NPC_new, file); + newXS(strcpy(buf, "Corpse::boot_Mob"), boot_Mob, file); + newXS(strcpy(buf, "Corpse::boot_Corpse"), boot_Corpse, file); + newXS(strcpy(buf, "Client::boot_Mob"), boot_Mob, file); + newXS(strcpy(buf, "Client::boot_Client"), boot_Client, file); +// newXS(strcpy(buf, "Client::new"), XS_Client_new, file); + newXS(strcpy(buf, "EntityList::boot_EntityList"), boot_EntityList, file); +// newXS(strcpy(buf, "EntityList::new"), XS_EntityList_new, file); + newXS(strcpy(buf, "PerlPacket::boot_PerlPacket"), boot_PerlPacket, file); + newXS(strcpy(buf, "Group::boot_Group"), boot_Group, file); + newXS(strcpy(buf, "Raid::boot_Raid"), boot_Raid, file); + newXS(strcpy(buf, "QuestItem::boot_QuestItem"), boot_QuestItem, file); + newXS(strcpy(buf, "HateEntry::boot_HateEntry"), boot_HateEntry, file); + newXS(strcpy(buf, "Object::boot_Object"), boot_Object, file); + newXS(strcpy(buf, "Doors::boot_Doors"), boot_Doors, file); +; +#endif +#endif +#ifdef EMBPERL_COMMANDS + newXS(strcpy(buf, "commands::command_add"), XS_command_add, file); +#endif +#ifdef EMBPERL_IO_CAPTURE + newXS(strcpy(buf, "EQEmuIO::PRINT"), XS_EQEmuIO_PRINT, file); +#endif +} + +Embperl::Embperl() +{ + in_use = true; //in case one of these files generates an event + + //setup perl... + my_perl = perl_alloc(); + if(!my_perl) + throw "Failed to init Perl (perl_alloc)"; + DoInit(); +} + +void Embperl::DoInit() { + const char *argv_eqemu[] = { "", +#ifdef EMBPERL_IO_CAPTURE + "-w", "-W", +#endif + "-e", "0;", NULL }; + + int argc = 3; +#ifdef EMBPERL_IO_CAPTURE + argc = 5; +#endif + + char **argv = (char **)argv_eqemu; + char **env = { NULL }; + + PL_perl_destruct_level = 1; + + perl_construct(my_perl); + + PERL_SYS_INIT3(&argc, &argv, &env); + + perl_parse(my_perl, xs_init, argc, argv, env); + + perl_run(my_perl); + + //a little routine we use a lot. + eval_pv("sub my_eval {eval $_[0];}", TRUE); //dies on error + + //ruin the perl exit and command: + eval_pv("sub my_exit {}",TRUE); + eval_pv("sub my_sleep {}",TRUE); + if(gv_stashpv("CORE::GLOBAL", FALSE)) { + GV *exitgp = gv_fetchpv("CORE::GLOBAL::exit", TRUE, SVt_PVCV); + GvCV_set(exitgp, perl_get_cv("my_exit", TRUE)); //dies on error + GvIMPORTED_CV_on(exitgp); + GV *sleepgp = gv_fetchpv("CORE::GLOBAL::sleep", TRUE, SVt_PVCV); + GvCV_set(sleepgp, perl_get_cv("my_sleep", TRUE)); //dies on error + GvIMPORTED_CV_on(sleepgp); + } + + //declare our file eval routine. + try { + init_eval_file(); + } + catch(const char *err) + { + //remember... lasterr() is no good if we crap out here, in construction + LogFile->write(EQEMuLog::Quest, "perl error: %s", err); + throw "failed to install eval_file hook"; + } + +#ifdef EMBPERL_IO_CAPTURE + LogFile->write(EQEMuLog::Quest, "Tying perl output to eqemu logs"); + //make a tieable class to capture IO and pass it into EQEMuLog + eval_pv( + "package EQEmuIO; " +// "&boot_EQEmuIO;" + "sub TIEHANDLE { my $me = bless {}, $_[0]; $me->PRINT('Creating '.$me); return($me); } " + "sub WRITE { } " + //dunno why I need to shift off fmt here, but it dosent like without it + "sub PRINTF { my $me = shift; my $fmt = shift; $me->PRINT(sprintf($fmt, @_)); } " + "sub CLOSE { my $me = shift; $me->PRINT('Closing '.$me); } " + "sub DESTROY { my $me = shift; $me->PRINT('Destroying '.$me); } " +//this ties us for all packages, just do it in quest since thats kinda 'our' package + "package quest;" + " if(tied *STDOUT) { untie(*STDOUT); }" + " if(tied *STDERR) { untie(*STDERR); }" + " tie *STDOUT, 'EQEmuIO';" + " tie *STDERR, 'EQEmuIO';" + ,FALSE); +#endif //EMBPERL_IO_CAPTURE + +#ifdef EMBPERL_PLUGIN + eval_pv( + "package plugin; " + ,FALSE + ); +#ifdef EMBPERL_EVAL_COMMANDS + try { + eval_pv( + "use IO::Scalar;" + "$plugin::printbuff='';" + "tie *PLUGIN,'IO::Scalar',\\$plugin::printbuff;" + ,FALSE); + } + catch(const char *err) { + throw "failed to install plugin printhook, do you lack IO::Scalar?"; + } +#endif + + LogFile->write(EQEMuLog::Quest, "Loading perlemb plugins."); + try + { + eval_pv("main::eval_file('plugin', 'plugin.pl');", FALSE); + } + catch(const char *err) + { + LogFile->write(EQEMuLog::Quest, "Warning - plugin.pl: %s", err); + } + try + { + //should probably read the directory in c, instead, so that + //I can echo filenames as I do it, but c'mon... I'm lazy and this 1 line reads in all the plugins + eval_pv( + "if(opendir(D,'plugins')) { " + " my @d = readdir(D);" + " closedir(D);" + " foreach(@d){ " + " main::eval_file('plugin','plugins/'.$_)if/\\.pl$/;" + " }" + "}" + ,FALSE); + } + catch(const char *err) + { + LogFile->write(EQEMuLog::Quest, "Perl warning: %s", err); + } +#endif //EMBPERL_PLUGIN +#ifdef EMBPERL_COMMANDS + LogFile->write(EQEMuLog::Quest, "Loading perl commands..."); + try + { + eval_pv( + "package commands;" + "main::eval_file('commands', 'commands.pl');" + "&commands::commands_init();" + , FALSE); + } + catch(const char *err) + { + LogFile->write(EQEMuLog::Quest, "Warning - commands.pl: %s", err); + } +#endif //EMBPERL_COMMANDS + in_use = false; +} + +Embperl::~Embperl() +{ + in_use = true; +#ifdef EMBPERL_IO_CAPTURE + eval_pv( + "package quest;" + " if(tied *STDOUT) { untie(*STDOUT); }" + " if(tied *STDERR) { untie(*STDERR); }" + ,FALSE); +#endif + perl_free(my_perl); +} + +void Embperl::Reinit() { + in_use = true; + PL_perl_destruct_level = 1; + perl_destruct(my_perl); + DoInit(); + in_use = false; +} + +void Embperl::init_eval_file(void) +{ + eval_pv( + "our %Cache;" + "use Symbol qw(delete_package);" + "sub eval_file {" + "my($package, $filename) = @_;" + "$filename=~s/\'//g;" + "if(! -r $filename) { print \"Unable to read perl file '$filename'\\n\"; return; }" + "my $mtime = -M $filename;" + "if(defined $Cache{$package}{mtime}&&$Cache{$package}{mtime} <= $mtime && !($package eq 'plugin')){" + " return;" + "} else {" + //we 'my' $filename,$mtime,$package,$sub to prevent them from changing our state up here. + " eval(\"package $package; my(\\$filename,\\$mtime,\\$package,\\$sub); \\$isloaded = 1; require '$filename'; \");" +/* "local *FH;open FH, $filename or die \"open '$filename' $!\";" + "local($/) = undef;my $sub = ;close FH;" + "my $eval = qq{package $package; sub handler { $sub; }};" + "{ my($filename,$mtime,$package,$sub); eval $eval; }" + "die $@ if $@;" + "$Cache{$package}{mtime} = $mtime; ${$package.'::isloaded'} = 1;}" +*/ + "}" + "}" + ,FALSE); + } + +void Embperl::eval_file(const char * packagename, const char * filename) +{ + std::vector args; + args.push_back(packagename); + args.push_back(filename); + dosub("eval_file", &args); +} + +void Embperl::dosub(const char * subname, const std::vector * args, int mode) +{//as seen in perlembed docs +#if EQDEBUG >= 5 + if(InUse()) { + LogFile->write(EQEMuLog::Debug, "Warning: Perl dosub called for %s when perl is allready in use.\n", subname); + } +#endif + in_use = true; + bool err = false; + dSP; /* initialize stack pointer */ + ENTER; /* everything created after here */ + SAVETMPS; /* ...is a temporary variable. */ + PUSHMARK(SP); /* remember the stack pointer */ + if(args && args->size()) + { + for(std::vector::const_iterator i = args->begin(); i != args->end(); ++i) + {/* push the arguments onto the perl stack */ + XPUSHs(sv_2mortal(newSVpv(i->c_str(), i->length()))); + } + } + PUTBACK; /* make local stack pointer global */ + call_pv(subname, mode); /*eval our code*/ + SPAGAIN; /* refresh stack pointer */ + if(SvTRUE(ERRSV)) + { + err = true; + } + FREETMPS; /* free temp values */ + LEAVE; /* ...and the XPUSHed "mortal" args.*/ + + in_use = false; + if(err) + { + errmsg = "Perl runtime error: "; + errmsg += SvPVX(ERRSV); + throw errmsg.c_str(); + } +} + +//evaluate an expression. throw error on fail +void Embperl::eval(const char * code) +{ + std::vector arg; + arg.push_back(code); +// MYRA - added EVAL & KEEPERR to eval per Eglin's recommendation + dosub("my_eval", &arg, G_SCALAR|G_DISCARD|G_EVAL|G_KEEPERR); +//end Myra +} + +bool Embperl::SubExists(const char *package, const char *sub) { + HV *stash = gv_stashpv(package, false); + if(!stash) + return(false); + int len = strlen(sub); + return(hv_exists(stash, sub, len)); +} + +bool Embperl::VarExists(const char *package, const char *var) { + HV *stash = gv_stashpv(package, false); + if(!stash) + return(false); + int len = strlen(var); + return(hv_exists(stash, var, len)); +} + + +#endif //EMBPERL + +#endif //EMBPERL_CPP diff --git a/zone/embperl.h b/zone/embperl.h new file mode 100644 index 000000000..8e402590c --- /dev/null +++ b/zone/embperl.h @@ -0,0 +1,160 @@ +/* +Embperl.h +--------------- +eqemu perl wrapper +Eglin +*/ + +#ifndef EMBPERL_H +#define EMBPERL_H + +#ifdef EMBPERL + +#include +#include +#include +#include +#include + +//headers from the Perl distribution +#include +#define WIN32IO_IS_STDIO + +#ifndef WIN32 +extern "C" { //the perl headers dont do this for us... +#endif +#include +#include +#ifndef WIN32 +}; +#endif +#ifdef WIN32 +#define snprintf _snprintf +#endif + +//perl defines these macros and dosent clean them up, lazy bastards. -- I hate them too! +#ifdef Copy +#undef Copy +#endif + +#ifdef list +#undef list +#endif + +#ifdef write +#undef write +#endif + +#ifdef bool +#undef bool +#endif + +#ifdef Zero +#undef Zero +#endif + +//so embedded scripts can use xs extensions (ala 'use socket;') +EXTERN_C void boot_DynaLoader(pTHX_ CV* cv); +EXTERN_C void xs_init(pTHX); + +class Embperl +{ +private: + //if we fail inside a script evaluation, this will hold the croak msg (not much help if we die during construction, but that's our own fault) + mutable std::string errmsg; + //kludgy workaround for the fact that we can't directly do something like SvIV(get_sv($big[0]{ass}->{struct})) + SV * my_get_sv(const char * varname) { + char buffer[256]; + snprintf(buffer, 256, "if(defined(%s)) { $scratch::temp = %s; } else { $scratch::temp = 'UNDEF'; }", varname, varname); + eval(buffer); + return get_sv("scratch::temp", false); + } + + //install a perl func + void init_eval_file(void); + + bool in_use; //true if perl is executing +protected: + //the embedded interpreter + PerlInterpreter * my_perl; + + void DoInit(); + +public: + Embperl(void); //This can throw errors! Buyer beware + ~Embperl(void); + + void Reinit(); + + //return the last error msg + std::string lasterr(void) const { return errmsg;}; + //evaluate an expression. throws string errors on fail + void eval(const char * code); + //execute a subroutine. throws lasterr on failure + void dosub(const char * subname, const std::vector * args = NULL, int mode = G_SCALAR|G_DISCARD|G_EVAL); + + //Access to perl variables + //all varnames here should be of the form package::name + //returns the contents of the perl variable named in varname as a c int + int geti(const char * varname) { return SvIV(my_get_sv(varname)); }; + //returns the contents of the perl variable named in varname as a c float + float getd(const char * varname) { return SvNV(my_get_sv(varname));}; + //returns the contents of the perl variable named in varname as a string + std::string getstr(const char * varname) { + SV * temp = my_get_sv(varname); + return std::string(SvPV_nolen(temp),SvLEN(temp)); + } + + //put an integer into a perl varable + void seti(const char *varname, int val) const { + SV *t = get_sv(varname, true); + sv_setiv(t, val); + } + //put a real into a perl varable + void setd(const char *varname, float val) const { + SV *t = get_sv(varname, true); + sv_setnv(t, val); + } + //put a string into a perl varable + void setstr(const char *varname, const char *val) const { + SV *t = get_sv(varname, true); + sv_setpv(t, val); + } + + // put key-value pairs in hash + void sethash(const char *varname, std::map &vals) + { + std::map::iterator it; + + // Get hash and clear it. + HV *hv = get_hv(varname, TRUE); + hv_clear(hv); + + // Iterate through key-value pairs, storing them in hash + for (it = vals.begin(); it != vals.end(); it++) + { + int keylen = it->first.length(); + + SV *val = newSVpv(it->second.c_str(), it->second.length()); + + // If val was not added to hash, reset reference count + if (hv_store(hv, it->first.c_str(), keylen, val, 0) == NULL) + val->sv_refcnt = 0; + } + } + + //loads a file and compiles it into our interpreter (assuming it hasn't already been read in) + //idea borrowed from perlembed + void eval_file(const char * packagename, const char * filename); + + inline bool InUse() const { return(in_use); } + + //check to see if a sub exists in package + bool SubExists(const char *package, const char *sub); + + //check to see if a variable exists in package + bool VarExists(const char *package, const char *var); +}; +#endif //EMBPERL + +#endif //EMBPERL_H diff --git a/zone/embxs.cpp b/zone/embxs.cpp new file mode 100644 index 000000000..35e7730f4 --- /dev/null +++ b/zone/embxs.cpp @@ -0,0 +1,136 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 +*/ +#ifdef EMBPERL + +#include "../common/debug.h" +#include "masterentity.h" +#include "command.h" + +#include "embperl.h" +#include "embxs.h" + + + +const char *getItemName(unsigned itemid) +{ + const Item_Struct* item = NULL; + item = database.GetItem(itemid); + + if (item) + return item->Name; + else + return NULL; +} + +XS(XS_qc_getItemName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_qc_getItemName) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getItemName(itemid)"); + { + unsigned itemid = (unsigned)SvUV(ST(0)); + const char * RETVAL; + dXSTARG; + RETVAL = getItemName(itemid); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + + +EXTERN_C XS(boot_qc); /* prototype to pass -Wmissing-prototypes */ +EXTERN_C XS(boot_qc) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = '\0'; + + if(items != 1) + LogFile->write(EQEMuLog::Error, "boot_qc does not take any arguments."); + + char buf[128]; //shouldent have any function names longer than this. + + //add the strcpy stuff to get rid of const warnings.... + + XS_VERSION_BOOTCHECK ; + + newXS(strcpy(buf, "quest::getItemName"), XS_qc_getItemName, file); + + XSRETURN_YES; +} + +#ifdef EMBPERL_IO_CAPTURE + +XS(XS_EQEmuIO_PRINT); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EQEmuIO_PRINT) +{ + dXSARGS; + if (items < 2) + return; +// Perl_croak(aTHX_ "Usage: EQEmuIO::PRINT(@strings)"); + + int r; + for(r = 1; r < items; r++) { + char *str = SvPV_nolen(ST(r)); + char *cur = str; + + int i; + int pos = 0; + int len = 0; + for(i = 0; *cur != '\0'; i++, cur++) { + if(*cur == '\n') { + LogFile->writebuf(EQEMuLog::Quest, str + pos, 1, len); + len = 0; + pos = i+1; + } else { + len++; + } + } + if(len > 0) { + LogFile->writebuf(EQEMuLog::Quest, str + pos, 1, len); + } + } + + XSRETURN_EMPTY; +} +#endif //EMBPERL_IO_CAPTURE + + +#ifdef EMBPERL_COMMANDS + +XS(XS_command_add); /* prototype to pass -Wmissing-prototypes */ +XS(XS_command_add) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: commands::command_add(name, desc, access)"); + + char *name = SvPV_nolen(ST(0)); + char *desc = SvPV_nolen(ST(1)); + int access = (int)SvIV(ST(2)); + + command_add_perl(name, desc, access); + + XSRETURN_EMPTY; +} + +#endif //EMBPERL_COMMANDS + +#endif // EMBPERL diff --git a/zone/embxs.h b/zone/embxs.h new file mode 100644 index 000000000..a82a25141 --- /dev/null +++ b/zone/embxs.h @@ -0,0 +1,20 @@ +#ifndef EMBXS_H +#define EMBXS_H + +//headers from the Perl distribution +#include +#define WIN32IO_IS_STDIO + +#ifndef WIN32 +extern "C" { //the perl headers dont do this for us... +#endif +#include +#include +#ifndef WIN32 +}; +#endif + +const char *getItemName(unsigned itemid); +XS(XS_qc_getItemName); /* prototype to pass -Wmissing-prototypes */ +EXTERN_C XS(boot_qc); /* prototype to pass -Wmissing-prototypes */ +#endif // EMBXS_H diff --git a/zone/entity.cpp b/zone/entity.cpp new file mode 100644 index 000000000..1eec7dc78 --- /dev/null +++ b/zone/entity.cpp @@ -0,0 +1,5292 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 +#include +#include +#include +#include +#include +using namespace std; + +#ifdef _WINDOWS +#include +#else +#include +#include "../common/unix.h" +#endif + +#include "net.h" +#include "masterentity.h" +#include "worldserver.h" +#include "PlayerCorpse.h" +#include "../common/guilds.h" +#include "../common/packet_dump.h" +#include "../common/packet_functions.h" +#include "petitions.h" +#include "spdat.h" +#include "features.h" +#include "StringIDs.h" +#include "parser.h" +#include "../common/dbasync.h" +#include "guild_mgr.h" +#include "raids.h" +#include "QuestParserCollection.h" + +#ifdef _WINDOWS +#define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +extern Zone* zone; +extern volatile bool ZoneLoaded; +extern WorldServer worldserver; +extern NetConnection net; +extern uint32 numclients; +#if !defined(NEW_LoadSPDat) && !defined(DB_LoadSPDat) + extern SPDat_Spell_Struct spells[SPDAT_RECORDS]; +#endif +extern bool spells_loaded; +extern PetitionList petition_list; +extern DBAsync *dbasync; + +extern char errorname[32]; +extern uint16 adverrornum; + +Entity::Entity() { + id = 0; + pDBAsyncWorkID = 0; +} + +Entity::~Entity() { + dbasync->CancelWork(pDBAsyncWorkID); +} + +void Entity::SetID(uint16 set_id) { + id = set_id; +} + +Client* Entity::CastToClient() { + if(this==0x00){ + cout << "CastToClient error (NULL)" << endl; + DebugBreak(); + return 0; + } +#ifdef _EQDEBUG + if(!IsClient()) { + cout << "CastToClient error (not client?)" << endl; + DebugBreak(); + return 0; + } +#endif + return static_cast(this); +} + +NPC* Entity::CastToNPC() { +#ifdef _EQDEBUG + if(!IsNPC()) { + cout << "CastToNPC error" << endl; + DebugBreak(); + return 0; + } +#endif + return static_cast(this); +} + +Mob* Entity::CastToMob() { +#ifdef _EQDEBUG + if(!IsMob()) { + cout << "CastToMob error" << endl; + DebugBreak(); + return 0; + } +#endif + return static_cast(this); +} + +Merc* Entity::CastToMerc() { +#ifdef _EQDEBUG + if(!IsMerc()) { + cout << "CastToMerc error" << endl; + DebugBreak(); + return 0; + } +#endif + return static_cast(this); +} + + +Trap* Entity::CastToTrap() +{ +#ifdef DEBUG + if(!IsTrap()) + { + //cout << "CastToTrap error" << endl; + return 0; + } +#endif + return static_cast(this); +} + +Corpse* Entity::CastToCorpse() { +#ifdef _EQDEBUG + if(!IsCorpse()) { + cout << "CastToCorpse error" << endl; + DebugBreak(); + return 0; + } +#endif + return static_cast(this); +} +Object* Entity::CastToObject() { +#ifdef _EQDEBUG + if(!IsObject()) { + cout << "CastToObject error" << endl; + DebugBreak(); + return 0; + } +#endif + return static_cast(this); +} + +/*Group* Entity::CastToGroup() { +#ifdef _EQDEBUG + if(!IsGroup()) { + cout << "CastToGroup error" << endl; + DebugBreak(); + return 0; + } +#endif + return static_cast(this); +}*/ + +Doors* Entity::CastToDoors() { +return static_cast(this); +} + +Beacon* Entity::CastToBeacon() { + return static_cast(this); +} + + + + +const Client* Entity::CastToClient() const { + if(this==0x00){ + cout << "CastToClient error (NULL)" << endl; + DebugBreak(); + return 0; + } +#ifdef _EQDEBUG + if(!IsClient()) { + cout << "CastToClient error (not client?)" << endl; + DebugBreak(); + return 0; + } +#endif + return static_cast(this); +} + +const NPC* Entity::CastToNPC() const { +#ifdef _EQDEBUG + if(!IsNPC()) { + cout << "CastToNPC error" << endl; + DebugBreak(); + return 0; + } +#endif + return static_cast(this); +} + +const Mob* Entity::CastToMob() const { +#ifdef _EQDEBUG + if(!IsMob()) { + cout << "CastToMob error" << endl; + DebugBreak(); + return 0; + } +#endif + return static_cast(this); +} + +const Merc* Entity::CastToMerc() const { +#ifdef _EQDEBUG + if(!IsMerc()) { + cout << "CastToMerc error" << endl; + DebugBreak(); + return 0; + } +#endif + return static_cast(this); +} + +const Trap* Entity::CastToTrap() const { +#ifdef DEBUG + if(!IsTrap()) + { + //cout << "CastToTrap error" << endl; + return 0; + } +#endif + return static_cast(this); +} + +const Corpse* Entity::CastToCorpse() const { +#ifdef _EQDEBUG + if(!IsCorpse()) { + cout << "CastToCorpse error" << endl; + DebugBreak(); + return 0; + } +#endif + return static_cast(this); +} + +const Object* Entity::CastToObject() const { +#ifdef _EQDEBUG + if(!IsObject()) { + cout << "CastToObject error" << endl; + DebugBreak(); + return 0; + } +#endif + return static_cast(this); +} + +const Doors* Entity::CastToDoors() const { +return static_cast(this); +} + +const Beacon* Entity::CastToBeacon() const { + return static_cast(this); +} + +#ifdef BOTS +Bot* Entity::CastToBot() { +#ifdef _EQDEBUG + if(!IsBot()) { + cout << "CastToBot error" << endl; + DebugBreak(); + return 0; + } +#endif + return static_cast(this); +} +#endif + +EntityList::EntityList() { + last_insert_id = 0; +} + +EntityList::~EntityList() { + //must call this before the list is destroyed, or else it will try to + //delete the NPCs in the list, which it cannot do. + RemoveAllLocalities(); +} + +bool EntityList::CanAddHateForMob(Mob *p) { + LinkedListIterator iterator(npc_list); + int count = 0; + + iterator.Reset(); + while( iterator.MoreElements()) + { + NPC *npc=iterator.GetData(); + if (npc->IsOnHatelist(p)) + count++; + // no need to continue if we already hit the limit + if (count > 3) + return false; + iterator.Advance(); + } + + if (count <= 2) + return true; + return false; +} + +void EntityList::AddClient(Client* client) { + client->SetID(GetFreeID()); + client_list.Insert(client); + mob_list.Insert(client); + if(!client_list.dont_delete) + client_list.dont_delete=true; +} + + +void EntityList::TrapProcess() { + if(numclients < 1) + return; + _ZP(EntityList_TrapProcess); + LinkedListIterator iterator(trap_list); + iterator.Reset(); + uint32 count=0; + while(iterator.MoreElements()) + { + count++; + if(!iterator.GetData()->Process()){ + iterator.RemoveCurrent(); + } + else + iterator.Advance(); + } + if(count==0) + net.trap_timer.Disable();//No traps in list, disable until one is added +} + + +// Debug function -- checks to see if group_list has any NULL entries. +// Meant to be called after each group-related function, in order +// to track down bugs. +void EntityList::CheckGroupList (const char *fname, const int fline) +{ + list::iterator it; + + for (it = group_list.begin(); it != group_list.end(); it++) + { + if (*it == NULL) + { + LogFile->write(EQEMuLog::Error, "NULL group, %s:%i", fname, fline); + } + } +} + +void EntityList::GroupProcess() { + list::iterator iterator; + uint32 count = 0; + + if(numclients < 1) + return; + _ZP(EntityList_GroupProcess); + + iterator = group_list.begin(); + while(iterator != group_list.end()) + { + count++; + (*iterator)->Process(); + /* + if(!iterator.GetData()->Process()){ + iterator.RemoveCurrent(); + } + else + iterator.Advance(); + */ + iterator++; + } + if(count == 0) + net.group_timer.Disable();//No groups in list, disable until one is added + +#if EQDEBUG >= 5 + CheckGroupList (__FILE__, __LINE__); +#endif +} + +void EntityList::QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app) +{ + + list::iterator iterator = group_list.begin(); + + _ZP(EntityList_QueueToGroupsForNPCHealthAA); + + while(iterator != group_list.end()) + { + (*iterator)->QueueHPPacketsForNPCHealthAA(sender, app); + iterator++; + } +} + +void EntityList::RaidProcess() { + list::iterator iterator; + uint32 count = 0; + + if(numclients < 1) + return; + _ZP(EntityList_RaidProcess); + + iterator = raid_list.begin(); + while(iterator != raid_list.end()) + { + count++; + (*iterator)->Process(); + iterator++; + } + if(count == 0) + net.raid_timer.Disable();//No groups in list, disable until one is added +} + +void EntityList::DoorProcess() { +#ifdef IDLE_WHEN_EMPTY + if(numclients < 1) + return; +#endif + _ZP(EntityList_DoorProcess); + LinkedListIterator iterator(door_list); + iterator.Reset(); + uint32 count=0; + while(iterator.MoreElements()) + { + count++; + if(!iterator.GetData()->Process()){ + iterator.RemoveCurrent(); + } + else + iterator.Advance(); + } + if (count==0) + net.door_timer.Disable();//No doors in list, disable until one is added +} + +void EntityList::ObjectProcess() { + _ZP(EntityList_ObjectProcess); + LinkedListIterator iterator(object_list); + iterator.Reset(); + uint32 count=0; + while(iterator.MoreElements()) + { + count++; + if(!iterator.GetData()->Process()){ + iterator.RemoveCurrent(); + } + else + iterator.Advance(); + } + if(count==0) + net.object_timer.Disable();//No objects in list, disable until one is added +} + +void EntityList::CorpseProcess() { + _ZP(EntityList_CorpseProcess); + LinkedListIterator iterator(corpse_list); + iterator.Reset(); + uint32 count=0; + while(iterator.MoreElements()) + { + count++; + if(!iterator.GetData()->Process()){ + iterator.RemoveCurrent(); + } + else + iterator.Advance(); + } + if(count==0) + net.corpse_timer.Disable();//No corpses in list, disable until one is added +} + +void EntityList::MobProcess() { +#ifdef IDLE_WHEN_EMPTY + if(numclients < 1) + return; +#endif + _ZP(EntityList_MobProcess); + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(!iterator.GetData()) + { + iterator.Advance(); + continue; + } + if(!iterator.GetData()->Process()){ + Mob* mob=iterator.GetData(); + if(mob->IsNPC()) + entity_list.RemoveNPC(mob->CastToNPC()->GetID()); + else if(mob->IsMerc()) { + entity_list.RemoveMerc(mob->CastToMerc()->GetID()); + } +#ifdef BOTS + else if(mob->IsBot()) { + entity_list.RemoveBot(mob->CastToBot()->GetID()); + } +#endif + else{ +#ifdef _WINDOWS + struct in_addr in; + in.s_addr = mob->CastToClient()->GetIP(); + cout << "Dropping client: Process=false, ip=" << inet_ntoa(in) << ", port=" << mob->CastToClient()->GetPort() << endl; +#endif + zone->StartShutdownTimer(); + Group *g = GetGroupByMob(mob); + 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) { + LogFile->write(EQEMuLog::Error, "About to delete a client still in a raid."); + r->MemberZoned(mob->CastToClient()); + } + entity_list.RemoveClient(mob->GetID()); + } + iterator.RemoveCurrent(); + } + else + iterator.Advance(); + } +} + +void EntityList::BeaconProcess() { + _ZP(EntityList_BeaconProcess); + LinkedListIterator iterator(beacon_list); + int count; + + for(iterator.Reset(), count = 0; iterator.MoreElements(); count++) + { + if(!iterator.GetData()->Process()) + iterator.RemoveCurrent(); + else + iterator.Advance(); + } +} + + +void EntityList::AddGroup(Group* group) { + if(group == NULL) //this seems to be happening somehow... + return; + + uint32 gid = worldserver.NextGroupID(); + if(gid == 0) { + LogFile->write(EQEMuLog::Error, "Unable to get new group ID from world server. group is going to be broken."); + return; + } + + AddGroup(group, gid); +#if EQDEBUG >= 5 + CheckGroupList (__FILE__, __LINE__); +#endif +} + + +void EntityList::AddGroup(Group* group, uint32 gid) { + group->SetID(gid); + //group_list.Insert(group); + group_list.push_back(group); + if(!net.group_timer.Enabled()) + net.group_timer.Start(); +#if EQDEBUG >= 5 + CheckGroupList (__FILE__, __LINE__); +#endif +} + +void EntityList::AddRaid(Raid* raid) { + if(raid == NULL) + return; + + uint32 gid = worldserver.NextGroupID(); + if(gid == 0) { + LogFile->write(EQEMuLog::Error, "Unable to get new group ID from world server. group is going to be broken."); + return; + } + + AddRaid(raid, gid); +} +void EntityList::AddRaid(Raid* raid, uint32 gid) { + raid->SetID(gid); + raid_list.push_back(raid); + if(!net.raid_timer.Enabled()) + net.raid_timer.Start(); +} + + +void EntityList::AddCorpse(Corpse* corpse, uint32 in_id) { + if (corpse == 0) + return; + + if (in_id == 0xFFFFFFFF) + corpse->SetID(GetFreeID()); + else + corpse->SetID(in_id); + corpse->CalcCorpseName(); + corpse_list.Insert(corpse); + if(!net.corpse_timer.Enabled()) + net.corpse_timer.Start(); +} + +void EntityList::AddNPC(NPC* npc, bool SendSpawnPacket, bool dontqueue) { + npc->SetID(GetFreeID()); + parse->EventNPC(EVENT_SPAWN, npc, NULL, "", 0); + + uint16 emoteid = npc->GetNPCEmoteID(); + if(emoteid != 0) + npc->DoNPCEmote(ONSPAWN,emoteid); + + if (SendSpawnPacket) { + if (dontqueue) { // aka, SEND IT NOW BITCH! + EQApplicationPacket* app = new EQApplicationPacket; + npc->CreateSpawnPacket(app,npc); + QueueClients(npc, app); + safe_delete(app); + } + else { + NewSpawn_Struct* ns = new NewSpawn_Struct; + memset(ns, 0, sizeof(NewSpawn_Struct)); + npc->FillSpawnStruct(ns, 0); // Not working on player newspawns, so it's safe to use a ForWho of 0 + AddToSpawnQueue(npc->GetID(), &ns); + safe_delete(ns); + } + if(npc->IsFindable()) + UpdateFindableNPCState(npc, false); + } + + npc_list.Insert(npc); + if(!npc_list.dont_delete) + npc_list.dont_delete=true; + mob_list.Insert(npc); +} + +void EntityList::AddMerc(Merc* merc, bool SendSpawnPacket, bool dontqueue) { + if(merc) { + merc->SetID(GetFreeID()); + + if(SendSpawnPacket) { + if(dontqueue) { + // Send immediately + EQApplicationPacket* outapp = new EQApplicationPacket(); + merc->CreateSpawnPacket(outapp); + outapp->priority = 6; + QueueClients(merc, outapp, true); + safe_delete(outapp); + } + else { + // Queue the packet + NewSpawn_Struct* ns = new NewSpawn_Struct; + memset(ns, 0, sizeof(NewSpawn_Struct)); + merc->FillSpawnStruct(ns, merc); + AddToSpawnQueue(merc->GetID(), &ns); + safe_delete(ns); + } + + //parse->EventMERC(EVENT_SPAWN, merc, NULL, "", 0); + } + + merc_list.Insert(merc); + mob_list.Insert(merc); + if(!merc_list.dont_delete) + merc_list.dont_delete=true; + } +} + +void EntityList::AddObject(Object* obj, bool SendSpawnPacket) { + obj->SetID(GetFreeID()); + if (SendSpawnPacket) { + EQApplicationPacket app; + obj->CreateSpawnPacket(&app); + #if (EQDEBUG >= 6) + DumpPacket(&app); + #endif + QueueClients(0, &app,false); + } + object_list.Insert(obj); + if(!net.object_timer.Enabled()) + net.object_timer.Start(); +} + +void EntityList::AddDoor(Doors* door) { + door->SetEntityID(GetFreeID()); + door_list.Insert(door); + if(!net.door_timer.Enabled()) + net.door_timer.Start(); +} + +void EntityList::AddTrap(Trap* trap) { + trap->SetID(GetFreeID()); + trap_list.Insert(trap); + if(!net.trap_timer.Enabled()) + net.trap_timer.Start(); +} + +void EntityList::AddBeacon(Beacon *beacon) +{ + beacon->SetID(GetFreeID()); + beacon_list.Insert(beacon); +} + +void EntityList::AddToSpawnQueue(uint16 entityid, NewSpawn_Struct** ns) { + uint32 count; + if((count=(client_list.Count()))==0) + return; + SpawnQueue.Append(*ns); + NumSpawnsOnQueue++; + if (tsFirstSpawnOnQueue == 0xFFFFFFFF) + tsFirstSpawnOnQueue = Timer::GetCurrentTime(); + *ns = 0; // make it so the calling function cant fuck us and delete the data =) +} + +void EntityList::CheckSpawnQueue() { + // Send the stuff if the oldest packet on the queue is older than 50ms -Quagmire + if (tsFirstSpawnOnQueue != 0xFFFFFFFF && (Timer::GetCurrentTime() - tsFirstSpawnOnQueue) > 50) { + //if (NumSpawnsOnQueue <= 5) { + LinkedListIterator iterator(SpawnQueue); + EQApplicationPacket* outapp = 0; + + iterator.Reset(); + while(iterator.MoreElements()) { + outapp = new EQApplicationPacket; + Mob::CreateSpawnPacket(outapp, iterator.GetData()); +// cout << "Sending spawn packet: " << iterator.GetData()->spawn.name << endl; + QueueClients(0, outapp); + safe_delete(outapp); + iterator.RemoveCurrent(); + } + //sending Spawns like this after zone in causes the client to freeze... + /*} + else { + uint32 spawns_per_pack = MAX_SPAWNS_PER_PACKET; + if(NumSpawnsOnQueue < spawns_per_pack) + spawns_per_pack = NumSpawnsOnQueue; + + BulkZoneSpawnPacket* bzsp = new BulkZoneSpawnPacket(0, spawns_per_pack); + LinkedListIterator iterator(SpawnQueue); + + iterator.Reset(); + while(iterator.MoreElements()) { + bzsp->AddSpawn(iterator.GetData()); + iterator.RemoveCurrent(); + } + safe_delete(bzsp); + }*/ + + tsFirstSpawnOnQueue = 0xFFFFFFFF; + NumSpawnsOnQueue = 0; + } +} + +Doors* EntityList::FindDoor(uint8 door_id) +{ + if (door_id == 0) + return 0; + + LinkedListIterator iterator(door_list); + iterator.Reset(); + + while(iterator.MoreElements()) + { + Doors* door=iterator.GetData(); + if (door->GetDoorID() == door_id) + { + return door; + } + iterator.Advance(); + } + return 0; +} + +Object* EntityList::FindObject(uint32 object_id) +{ + LinkedListIterator iterator(object_list); + iterator.Reset(); + + while(iterator.MoreElements()) + { + Object* object=iterator.GetData(); + if (object->GetDBID() == object_id) + { + return object; + } + iterator.Advance(); + } + return NULL; +} + +Object* EntityList::FindNearbyObject(float x, float y, float z, float radius) +{ + LinkedListIterator iterator(object_list); + iterator.Reset(); + + float ox; + float oy; + float oz; + + while(iterator.MoreElements()) + { + Object* object=iterator.GetData(); + + object->GetLocation(&ox, &oy, &oz); + + ox = (x < ox) ? (ox - x) : (x - ox); + oy = (y < oy) ? (oy - y) : (y - oy); + oz = (z < oz) ? (oz - z) : (z - oz); + + if ((ox <= radius) && (oy <= radius) && (oz <= radius)) + { + return object; + } + iterator.Advance(); + } + + + return NULL; +} + + +bool EntityList::MakeDoorSpawnPacket(EQApplicationPacket* app, Client *client) +{ + uint32 mask_test = client->GetClientVersionBit(); + int count = 0; + LinkedListIterator iterator(door_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if((iterator.GetData()->GetClientVersionMask() & mask_test) && strlen(iterator.GetData()->GetDoorName()) > 3) + { + count++; + } + iterator.Advance(); + } + + if(count == 0 || count > 500) + { + return false; + } + uint32 length = count * sizeof(Door_Struct); + uchar* packet_buffer = new uchar[length]; + memset(packet_buffer, 0, length); + uchar* ptr = packet_buffer; + Doors *door; + Door_Struct nd; + + iterator.Reset(); + while(iterator.MoreElements()) + { + door = iterator.GetData(); + if(door && (door->GetClientVersionMask() & mask_test) && strlen(door->GetDoorName()) > 3) + { + memset(&nd, 0, sizeof(nd)); + memcpy(nd.name, door->GetDoorName(), 32); + nd.xPos = door->GetX(); + nd.yPos = door->GetY(); + nd.zPos = door->GetZ(); + nd.heading = door->GetHeading(); + nd.incline = door->GetIncline(); + nd.size = door->GetSize(); + nd.doorId = door->GetDoorID(); + nd.opentype = door->GetOpenType(); + nd.state_at_spawn = door->GetInvertState() ? !door->IsDoorOpen() : door->IsDoorOpen(); + nd.invert_state = door->GetInvertState(); + nd.door_param = door->GetDoorParam(); + memcpy(ptr, &nd, sizeof(nd)); + ptr+=sizeof(nd); + *(ptr-1)=0x01; + *(ptr-3)=0x01; + } + iterator.Advance(); + } + + app->SetOpcode(OP_SpawnDoor); + app->size = length; + app->pBuffer = packet_buffer; + return true; +} +Entity* EntityList::GetEntityMob(uint16 id){ + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetID() == id) + { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} +Entity* EntityList::GetEntityMerc(uint16 id){ + LinkedListIterator iterator(merc_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetID() == id) + { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} +Entity* EntityList::GetEntityMob(const char *name) +{ + if (name == 0) + return 0; + + LinkedListIterator iterator(mob_list); + + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) + { + if (strcasecmp(iterator.GetData()->GetName(), name) == 0) + { + return iterator.GetData(); + } + } + return 0; +} +Entity* EntityList::GetEntityDoor(uint16 id){ + LinkedListIterator iterator(door_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetID() == id) + { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} +Entity* EntityList::GetEntityCorpse(uint16 id){ + LinkedListIterator iterator(corpse_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetID() == id) + { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} +Entity* EntityList::GetEntityCorpse(const char *name) +{ + if (name == 0) + return 0; + + LinkedListIterator iterator(corpse_list); + + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) + { + if (strcasecmp(iterator.GetData()->GetName(), name) == 0) + { + return iterator.GetData(); + } + } + return 0; +} + +Entity* EntityList::GetEntityTrap(uint16 id){ + LinkedListIterator iterator(trap_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetID() == id) + { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} + +Entity* EntityList::GetEntityObject(uint16 id){ + LinkedListIterator iterator(object_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetID() == id) + { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} +/* +Entity* EntityList::GetEntityGroup(uint16 id){ + LinkedListIterator iterator(group_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetID() == id) + { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} +*/ +Entity* EntityList::GetEntityBeacon(uint16 id) { + LinkedListIterator iterator(beacon_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetID() == id) + { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} +Entity* EntityList::GetID(uint16 get_id) +{ + Entity* ent=0; + if((ent=entity_list.GetEntityMob(get_id))!=0) + return ent; + else if((ent=entity_list.GetEntityDoor(get_id))!=0) + return ent; + else if((ent=entity_list.GetEntityCorpse(get_id))!=0) + return ent; +// else if((ent=entity_list.GetEntityGroup(get_id))!=0) +// return ent; + else if((ent=entity_list.GetEntityObject(get_id))!=0) + return ent; + else if((ent=entity_list.GetEntityTrap(get_id))!=0) + return ent; + else if((ent=entity_list.GetEntityBeacon(get_id))!=0) + return ent; + else + return 0; +} + +NPC* EntityList::GetNPCByID(uint16 id) { + LinkedListIterator iterator(npc_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()) + { + if (iterator.GetData()->GetID() == id) { + return iterator.GetData(); + } + } + iterator.Advance(); + } + return 0; +} + +NPC* EntityList::GetNPCByNPCTypeID(uint32 npc_id) +{ + if (npc_id == 0) + return 0; + LinkedListIterator iterator(npc_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetNPCTypeID() == npc_id) + { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} + +Merc* EntityList::GetMercByID(uint16 id) { + LinkedListIterator iterator(merc_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()) + { + if (iterator.GetData()->GetID() == id) { + return iterator.GetData(); + } + } + iterator.Advance(); + } + return 0; +} + +Mob* EntityList::GetMob(uint16 get_id) +{ + Entity* ent=0; + + if (get_id == 0) + return 0; + + if((ent=entity_list.GetEntityMob(get_id))!=0) + return ent->CastToMob(); + else if((ent=entity_list.GetEntityCorpse(get_id))!=0) + return ent->CastToMob(); + + return 0; +} + +Mob* EntityList::GetMob(const char* name) +{ + Entity* ent=0; + + if (name == 0) + return 0; + + if((ent=entity_list.GetEntityMob(name))!=0) + return ent->CastToMob(); + else if((ent=entity_list.GetEntityCorpse(name))!=0) + return ent->CastToMob(); + + return 0; +} + +Mob* EntityList::GetMobByNpcTypeID(uint32 get_id) +{ + if (get_id == 0) + return 0; + LinkedListIterator iterator(mob_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetNPCTypeID() == get_id) + { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} + +Object* EntityList::GetObjectByDBID(uint32 id) +{ + if (id == 0) + return 0; + + LinkedListIterator iterator(object_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()) + { + if (iterator.GetData()->CastToObject()->GetDBID() == id) + { + return iterator.GetData(); + } + } + iterator.Advance(); + } + return 0; +} + +Object* EntityList::GetObjectByID(uint16 id) +{ + if (id == 0) + return 0; + + LinkedListIterator iterator(object_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()) + { + if (iterator.GetData()->CastToObject()->GetID() == id) + { + return iterator.GetData(); + } + } + iterator.Advance(); + } + return 0; +} + +Doors* EntityList::GetDoorsByID(uint16 id) +{ + if (id == 0) + return 0; + + LinkedListIterator iterator(door_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()) + { + if (iterator.GetData()->CastToDoors()->GetEntityID() == id) + { + return iterator.GetData(); + } + } + iterator.Advance(); + } + return 0; +} + +Doors* EntityList::GetDoorsByDBID(uint32 id) +{ + if (id == 0) + return 0; + + LinkedListIterator iterator(door_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()) + { + if (iterator.GetData()->CastToDoors()->GetDoorDBID() == id) + { + return iterator.GetData(); + } + } + iterator.Advance(); + } + return 0; +} + +Doors* EntityList::GetDoorsByDoorID(uint32 id) +{ + if (id == 0) + return 0; + + LinkedListIterator iterator(door_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()) + { + if (iterator.GetData()->CastToDoors()->GetDoorID() == id) + { + return iterator.GetData(); + } + } + iterator.Advance(); + } + return 0; +} + +uint16 EntityList::GetFreeID() +{ + if(last_insert_id > 1500) + last_insert_id = 0; + uint16 getid=last_insert_id; + while(1) + { + getid++; + if (GetID(getid) == 0) + { + last_insert_id = getid; + return getid; + } + } +} + +// if no language skill is specified, sent with 100 skill +void EntityList::ChannelMessage(Mob* from, uint8 chan_num, uint8 language, const char* message, ...) { + ChannelMessage(from, chan_num, language, 100, message); +} + +void EntityList::ChannelMessage(Mob* from, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...) { + LinkedListIterator iterator(client_list); + va_list argptr; + char buffer[4096]; + + va_start(argptr, message); + vsnprintf(buffer, 4096, message, argptr); + va_end(argptr); + + iterator.Reset(); + while(iterator.MoreElements()) + { + Client* client = iterator.GetData(); + eqFilterType filter = FilterNone; + if(chan_num==3)//shout + filter=FILTER_SHOUT; + else if(chan_num==4) //auction + filter=FILTER_AUCTION; + + if (chan_num != 8 || client->Dist(*from) < 200) // Only say is limited in range + { + if(filter==FilterNone || client->GetFilter(filter)!=FilterHide) + client->ChannelMessageSend(from->GetName(), 0, chan_num, language, lang_skill, buffer); + } + iterator.Advance(); + } +} + +void EntityList::ChannelMessageSend(Mob* to, uint8 chan_num, uint8 language, const char* message, ...) { + LinkedListIterator iterator(client_list); + va_list argptr; + char buffer[4096]; + va_start(argptr, message); + vsnprintf(buffer, 4096, message, argptr); + va_end(argptr); + iterator.Reset(); + while(iterator.MoreElements()) + { + Client* client = iterator.GetData(); + if (client->GetID() == to->GetID()) { + client->ChannelMessageSend(0, 0, chan_num, language, buffer); + break; + } + iterator.Advance(); + } +} + +void EntityList::SendZoneSpawns(Client* client) +{ + LinkedListIterator iterator(mob_list); + + EQApplicationPacket* app; + iterator.Reset(); + while(iterator.MoreElements()) { + Mob* ent = iterator.GetData(); + if (!( ent->InZone() ) || (ent->IsClient())) + { + if(ent->CastToClient()->GMHideMe(client) || ent->CastToClient()->IsHoveringForRespawn()) + { + iterator.Advance(); + continue; + } + } + app = new EQApplicationPacket; + iterator.GetData()->CastToMob()->CreateSpawnPacket(app); // TODO: Use zonespawns opcode instead + client->QueuePacket(app, true, Client::CLIENT_CONNECTED); + safe_delete(app); + iterator.Advance(); + } +} + +void EntityList::SendZoneSpawnsBulk(Client* client) +{ + //float rate = client->Connection()->GetDataRate(); + LinkedListIterator iterator(mob_list); + NewSpawn_Struct ns; + Mob *spawn; + uint32 maxspawns=100; + + //rate = rate > 1.0 ? (rate < 10.0 ? rate : 10.0) : 1.0; + //maxspawns = (uint32)rate * SPAWNS_PER_POINT_DATARATE; // FYI > 10240 entities will cause BulkZoneSpawnPacket to throw exception + if(maxspawns > mob_list.Count()) + maxspawns = mob_list.Count(); + BulkZoneSpawnPacket* bzsp = new BulkZoneSpawnPacket(client, maxspawns); + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) + { + spawn = iterator.GetData(); + if(spawn && spawn->InZone()) + { + if(spawn->IsClient() && (spawn->CastToClient()->GMHideMe(client) || spawn->CastToClient()->IsHoveringForRespawn())) + continue; + memset(&ns, 0, sizeof(NewSpawn_Struct)); + spawn->FillSpawnStruct(&ns, client); + bzsp->AddSpawn(&ns); + } + } + safe_delete(bzsp); +} + +//this is a hack to handle a broken spawn struct +void EntityList::SendZonePVPUpdates(Client *to) { + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) { + Client *c = iterator.GetData(); + if(c->GetPVP()) + c->SendAppearancePacket(AT_PVP, c->GetPVP(), true, false, to); + iterator.Advance(); + } +} + +void EntityList::SendZoneCorpses(Client* client) +{ + EQApplicationPacket* app; + LinkedListIterator iterator(corpse_list); + + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) + { + Corpse *ent = iterator.GetData(); + app = new EQApplicationPacket; + ent->CreateSpawnPacket(app); + client->QueuePacket(app, true, Client::CLIENT_CONNECTED); + safe_delete(app); + } +} + +void EntityList::SendZoneCorpsesBulk(Client* client) { + //float rate = client->Connection()->GetDataRate(); + LinkedListIterator iterator(corpse_list); + NewSpawn_Struct ns; + Corpse *spawn; + uint32 maxspawns=100; + + //rate = rate > 1.0 ? (rate < 10.0 ? rate : 10.0) : 1.0; + //maxspawns = (uint32)rate * SPAWNS_PER_POINT_DATARATE; // FYI > 10240 entities will cause BulkZoneSpawnPacket to throw exception + BulkZoneSpawnPacket* bzsp = new BulkZoneSpawnPacket(client, maxspawns); + + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) + { + spawn = iterator.GetData(); + if(spawn && spawn->InZone()) + { + memset(&ns, 0, sizeof(NewSpawn_Struct)); + spawn->FillSpawnStruct(&ns, client); + bzsp->AddSpawn(&ns); + } + } + safe_delete(bzsp); +} + +void EntityList::SendZoneObjects(Client* client) +{ + LinkedListIterator iterator(object_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + EQApplicationPacket *app = new EQApplicationPacket; + iterator.GetData()->CreateSpawnPacket(app); + client->FastQueuePacket(&app); + iterator.Advance(); + } +} + +void EntityList::Save() +{ + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + iterator.GetData()->Save(); + iterator.Advance(); + } +} + +void EntityList::ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget) +{ + if(!pNewTarget) + return; + LinkedListIterator iterator(mob_list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->IsAIControlled()) { + // replace the old mob with the new one + if (iterator.GetData()->RemoveFromHateList(pOldMob)) + iterator.GetData()->AddToHateList(pNewTarget, 1, 0); + } + iterator.Advance(); + } +} + +void EntityList::RemoveFromTargets(Mob* mob, bool RemoveFromXTargets) +{ + LinkedListIterator iterator(mob_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + Mob *m = iterator.GetData(); + iterator.Advance(); + + if(!m) + continue; + + m->RemoveFromHateList(mob); + + if(RemoveFromXTargets) + if(m->IsClient()) + m->CastToClient()->RemoveXTarget(mob, false); + // FadingMemories calls this function passing the client. + else if(mob->IsClient()) + mob->CastToClient()->RemoveXTarget(m, false); + + } +} + +void EntityList::RemoveFromXTargets(Mob* mob) +{ + LinkedListIterator iterator(mob_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + Mob *m = iterator.GetData(); + iterator.Advance(); + + if(!m) + continue; + + if(m->IsClient()) + m->CastToClient()->RemoveXTarget(mob, false); + + } +} + +void EntityList::RemoveFromAutoXTargets(Mob* mob) +{ + LinkedListIterator iterator(mob_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + Mob *m = iterator.GetData(); + iterator.Advance(); + + if(!m) + continue; + + if(m->IsClient()) + m->CastToClient()->RemoveXTarget(mob, true); + + } +} + +void EntityList::RefreshAutoXTargets(Client *c) +{ + if(!c) + return; + + LinkedListIterator iterator(mob_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + Mob *m = iterator.GetData(); + iterator.Advance(); + + if(!m || m->GetHP() <= 0) + continue; + + if(m->CheckAggro(c) && !c->IsXTarget(m)) + { + c->AddAutoXTarget(m); + break; + } + + } +} + +void EntityList::RefreshClientXTargets(Client *c) +{ + if(!c) + return; + + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + Client *c2 = iterator.GetData(); + iterator.Advance(); + + if(!c2) + continue; + + if(c2->IsClientXTarget(c)) + c2->UpdateClientXTarget(c); + } +} + +void EntityList::QueueClientsByTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender, Mob* SkipThisMob, bool ackreq, bool HoTT, + uint32 ClientVersionBits) +{ + LinkedListIterator iterator(client_list); + + iterator.Reset(); + + while(iterator.MoreElements()) + { + Client *c = iterator.GetData(); + + iterator.Advance(); + + Mob *Target = c->GetTarget(); + + if(!Target) + continue; + + Mob *TargetsTarget = NULL; + + if(Target) + TargetsTarget = Target->GetTarget(); + + bool Send = false; + + if(c == SkipThisMob) + continue; + + if(iSendToSender) + if(c == sender) + Send = true; + + if(c != sender) + { + if(Target == sender) + { + Send = true; + } + else if(HoTT) + { + if(TargetsTarget == sender) + Send = true; + } + } + + if(Send && (c->GetClientVersionBit() & ClientVersionBits)) + c->QueuePacket(app, ackreq); + } +} + +void EntityList::QueueClientsByXTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender) +{ + LinkedListIterator iterator(client_list); + + iterator.Reset(); + + while(iterator.MoreElements()) + { + Client *c = iterator.GetData(); + + iterator.Advance(); + + if(!c || ((c == sender) && !iSendToSender)) + continue; + + if(!c->IsXTarget(sender)) + continue; + + c->QueuePacket(app); + } +} + +void EntityList::QueueCloseClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender, float dist, Mob* SkipThisMob, bool ackreq, eqFilterType filter) { + if (sender == NULL) { + QueueClients(sender, app, ignore_sender); + return; + } + if(dist <= 0) { + dist = 600; + } + float dist2 = dist * dist; //pow(dist, 2); + + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) { + + Client* ent = iterator.GetData(); + + if ((!ignore_sender || ent != sender) && (ent != SkipThisMob)) { + eqFilterMode filter2 = ent->GetFilter(filter); + if(ent->Connected() && + ( filter==FilterNone + || filter2 == FilterShow + || (filter2 == FilterShowGroupOnly && (sender == ent || + (ent->GetGroup() && ent->GetGroup()->IsGroupMember(sender)))) + || (filter2 == FilterShowSelfOnly && ent==sender)) + && (ent->DistNoRoot(*sender) <= dist2)) { + ent->QueuePacket(app, ackreq, Client::CLIENT_CONNECTED); + } + } + iterator.Advance(); + } +} + +//sender can be null +void EntityList::QueueClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender, bool ackreq) { + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + Client* ent = iterator.GetData(); + + if ((!ignore_sender || ent != sender)) + { + ent->QueuePacket(app, ackreq, Client::CLIENT_CONNECTED); + } + iterator.Advance(); + } +} + +/* +rewrite of all the queue close methods to use the update manager +void EntityList::FilterQueueCloseClients(uint8 filter, uint8 required, Mob* sender, const EQApplicationPacket* app, bool ignore_sender, float dist, Mob* SkipThisMob, bool ackreq){ + if(dist <= 0) { + dist = 600; + } + +#ifdef PACKET_UPDATE_MANAGER + EQApplicationPacket* tmp_app = app->Copy(); +#else + float dist2 = dist * dist; //pow(dist, 2); +#endif + + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) { + + Client* ent = iterator.GetData(); + uint8 filterval=ent->GetFilter(filter); + if(required==0) + required=1; + if(filterval==required){ + if ((!ignore_sender || ent != sender) && (ent != SkipThisMob) + ) { +#ifdef PACKET_UPDATE_MANAGER + if(ent->Connected()) { + ent->GetUpdateManager()->QueuePacket(tmp_app, ackreq, sender, ent->DistNoRoot(*sender)); + } +#else + if(ent->Connected() && (ent->DistNoRoot(*sender) <= dist2 || dist == 0)) { + ent->QueuePacket(app, ackreq); + } +#endif + } + } + iterator.Advance(); + } +#ifdef PACKET_UPDATE_MANAGER + EQApplicationPacket::PacketUsed(&tmp_app); +#endif +} + +void EntityList::QueueCloseClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender, float dist, Mob* SkipThisMob, bool ackreq,uint8 filter) { + if (sender == 0) { + QueueClients(sender, app, ignore_sender); + return; + } + if(dist <= 0) { + dist = 600; + } +#ifdef PACKET_UPDATE_MANAGER + EQApplicationPacket* tmp_app = app->Copy(); +#else + float dist2 = dist * dist; //pow(dist, 2); +#endif + + + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) { + + Client* ent = iterator.GetData(); + + if ((!ignore_sender || ent != sender) && (ent != SkipThisMob)) { + uint8 filter2=ent->GetFilter(filter); + if(ent->Connected() && + (filter==0 || (filter2==1 || + (filter2==99 && entity_list.GetGroupByClient(ent)!=0 && + entity_list.GetGroupByClient(ent)->IsGroupMember(sender)) + || (filter2==98 && ent==sender))) +#ifdef PACKET_UPDATE_MANAGER + ) { + ent->GetUpdateManager()->QueuePacket(tmp_app, ackreq, sender, ent->DistNoRoot(*sender)); + } +#else + && (ent->DistNoRoot(*sender) <= dist2 || dist == 0)) { + ent->QueuePacket(app, ackreq, Client::CLIENT_CONNECTED); + } +#endif + } + iterator.Advance(); + } +#ifdef PACKET_UPDATE_MANAGER + EQApplicationPacket::PacketUsed(&tmp_app); +#endif +} + +void EntityList::QueueClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender, bool ackreq) { + LinkedListIterator iterator(client_list); + +#ifdef PACKET_UPDATE_MANAGER + EQApplicationPacket* tmp_app = app->Copy(); +#endif + + iterator.Reset(); + while(iterator.MoreElements()) + { + Client* ent = iterator.GetData(); + + if ((!ignore_sender || ent != sender)) + { +#ifdef PACKET_UPDATE_MANAGER + ent->GetUpdateManager()->QueuePacket(tmp_app, ackreq, sender, ent->DistNoRoot(*sender)); +#else + ent->QueuePacket(app, ackreq, Client::CLIENT_CONNECTED); +#endif + } + iterator.Advance(); + } +#ifdef PACKET_UPDATE_MANAGER + EQApplicationPacket::PacketUsed(&tmp_app); +#endif +} +*/ + +/* +void EntityList::QueueManaged(Mob* sender, const EQApplicationPacket* app, bool ignore_sender, bool ackreq) { + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + Client* ent = iterator.GetData(); + + if ((!ignore_sender || ent != sender)) + { + ent->QueuePacket(app, ackreq, Client::CLIENT_CONNECTED); + } + iterator.Advance(); + } +}*/ + +void EntityList::QueueManaged(Mob* sender, const EQApplicationPacket* app, bool ignore_sender, bool ackreq) { + LinkedListIterator iterator(client_list); + +#ifdef PACKET_UPDATE_MANAGER + EQApplicationPacket* tmp_app = app->Copy(); +#endif + + iterator.Reset(); + while(iterator.MoreElements()) + { + Client* ent = iterator.GetData(); + + if ((!ignore_sender || ent != sender)) + { +#ifdef PACKET_UPDATE_MANAGER + ent->GetUpdateManager()->QueuePacket(tmp_app, ackreq, sender, ent->DistNoRoot(*sender)); +#else + ent->QueuePacket(app, ackreq, Client::CLIENT_CONNECTED); +#endif + } + iterator.Advance(); + } +#ifdef PACKET_UPDATE_MANAGER + EQApplicationPacket::PacketUsed(&tmp_app); +#endif +} + + +void EntityList::QueueClientsStatus(Mob* sender, const EQApplicationPacket* app, bool ignore_sender, uint8 minstatus, uint8 maxstatus) +{ + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if ((!ignore_sender || iterator.GetData() != sender) && (iterator.GetData()->Admin() >= minstatus && iterator.GetData()->Admin() <= maxstatus)) + { + iterator.GetData()->QueuePacket(app); + } + iterator.Advance(); + } +} + +void EntityList::DuelMessage(Mob* winner, Mob* loser, bool flee) { + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) { + Client *cur = iterator.GetData(); + //might want some sort of distance check in here? + if (cur != winner && cur != loser) + { + if (flee) + cur->Message_StringID(15, DUEL_FLED, winner->GetName(),loser->GetName(),loser->GetName()); + else + cur->Message_StringID(15, DUEL_FINISHED, winner->GetName(),loser->GetName()); + } + iterator.Advance(); + } +} + +Client* EntityList::GetClientByName(const char *checkname) { + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (strcasecmp(iterator.GetData()->GetName(), checkname) == 0) { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} + +Client* EntityList::GetClientByCharID(uint32 iCharID) { + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->CharacterID() == iCharID) { + + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} + +Client* EntityList::GetClientByWID(uint32 iWID) { + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetWID() == iWID) { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} + +Client* EntityList::GetRandomClient(float x, float y, float z, float Distance, Client* ExcludeClient) +{ + vector ClientsInRange; + + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if((iterator.GetData() != ExcludeClient) && (iterator.GetData()->DistNoRoot(x, y, z) <= Distance)) + { + ClientsInRange.push_back(iterator.GetData()); + } + + iterator.Advance(); + } + + if(ClientsInRange.size() == 0) + return NULL; + + return ClientsInRange[MakeRandomInt(0, ClientsInRange.size() - 1)]; +} + +Corpse* EntityList::GetCorpseByOwner(Client* client){ + LinkedListIterator iterator(corpse_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->IsPlayerCorpse()) + { + if (strcasecmp(iterator.GetData()->GetOwnerName(), client->GetName()) == 0) { + return iterator.GetData(); + } + } + iterator.Advance(); + } + return 0; +} +Corpse* EntityList::GetCorpseByID(uint16 id){ + LinkedListIterator iterator(corpse_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->id == id) { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} + +Corpse* EntityList::GetCorpseByDBID(uint32 dbid){ + LinkedListIterator iterator(corpse_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetDBID() == dbid) { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} + +Corpse* EntityList::GetCorpseByName(const char* name){ + LinkedListIterator iterator(corpse_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (strcmp(iterator.GetData()->GetName(),name)==0) { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} + +void EntityList::RemoveAllCorpsesByCharID(uint32 charid) +{ + LinkedListIterator iterator(corpse_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetCharID() == charid) + iterator.RemoveCurrent(); + else + iterator.Advance(); + } +} + +void EntityList::RemoveCorpseByDBID(uint32 dbid) +{ + LinkedListIterator iterator(corpse_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetDBID() == dbid) + iterator.RemoveCurrent(); + else + iterator.Advance(); + } +} + +int EntityList::RezzAllCorpsesByCharID(uint32 charid) +{ + int RezzExp = 0; + + LinkedListIterator iterator(corpse_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetCharID() == charid) + { + RezzExp += iterator.GetData()->GetRezzExp(); + iterator.GetData()->Rezzed(true); + iterator.GetData()->CompleteRezz(); + } + iterator.Advance(); + } + return RezzExp; +} + +Group* EntityList::GetGroupByMob(Mob* mob) +{ + list::iterator iterator; + + iterator = group_list.begin(); + + while(iterator != group_list.end()) + { + if ((*iterator)->IsGroupMember(mob)) { + return *iterator; + } + iterator++; + } +#if EQDEBUG >= 5 + CheckGroupList (__FILE__, __LINE__); +#endif + return 0; +} + +Group* EntityList::GetGroupByLeaderName(char* leader){ + list::iterator iterator; + + iterator = group_list.begin(); + + while(iterator != group_list.end()) + { + if (!strcmp((*iterator)->GetLeaderName(), leader)) { + return *iterator; + } + iterator++; + } +#if EQDEBUG >= 5 + CheckGroupList (__FILE__, __LINE__); +#endif + return 0; +} +Group* EntityList::GetGroupByID(uint32 group_id){ + list::iterator iterator; + + iterator = group_list.begin(); + + while(iterator != group_list.end()) + { + if ((*iterator)->GetID() == group_id) { + return *iterator; + } + iterator++; + } +#if EQDEBUG >= 5 + CheckGroupList (__FILE__, __LINE__); +#endif + return 0; +} +Group* EntityList::GetGroupByClient(Client* client) +{ + list ::iterator iterator; + + iterator = group_list.begin(); + + while(iterator != group_list.end()) + { + if ((*iterator)->IsGroupMember(client->CastToMob())) { + return *iterator; + } + iterator++; + } +#if EQDEBUG >= 5 + CheckGroupList (__FILE__, __LINE__); +#endif + return 0; +} + +Raid* EntityList::GetRaidByLeaderName(const char *leader){ + list::iterator iterator; + + iterator = raid_list.begin(); + + while(iterator != raid_list.end()) + { + if((*iterator)->GetLeader()){ + if(strcmp((*iterator)->GetLeader()->GetName(), leader) == 0){ + return *iterator; + } +} + iterator++; + } + return 0; +} +Raid* EntityList::GetRaidByID(uint32 id){ + list::iterator iterator; + + iterator = raid_list.begin(); + + while(iterator != raid_list.end()) + { + if ((*iterator)->GetID() == id) { + return *iterator; + } + iterator++; + } + return 0; +} + +Raid* EntityList::GetRaidByClient(Client* client) +{ + list::iterator iterator; + + iterator = raid_list.begin(); + + while(iterator != raid_list.end()) + { + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if((*iterator)->members[x].member){ + if((*iterator)->members[x].member == client) + return *iterator; + } + } + iterator++; + } + return 0; +} + +Raid* EntityList::GetRaidByMob(Mob* mob) { + list::iterator iterator; + + iterator = raid_list.begin(); + + while(iterator != raid_list.end()) + { + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + // TODO: Implement support for Mob objects in Raid class + /*if((*iterator)->members[x].member){ + if((*iterator)->members[x].member == mob) + return *iterator; + }*/ + } + iterator++; + } + return 0; +} + +Client* EntityList::GetClientByAccID(uint32 accid) +{ + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->AccountID() == accid) { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} +Client* EntityList::GetClientByID(uint16 id) { + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()) + { + if (iterator.GetData()->GetID() == id) { + return iterator.GetData(); + } + } + iterator.Advance(); + } + return 0; +} + +void EntityList::ChannelMessageFromWorld(const char* from, const char* to, uint8 chan_num, uint32 guild_id, uint8 language, const char* message) { + + LinkedListIterator iterator(client_list); + + iterator.Reset(); + for(; iterator.MoreElements(); iterator.Advance()) + { + Client* client = iterator.GetData(); + if(chan_num == 0) { + if(!client->IsInGuild(guild_id)) + continue; + if(!guild_mgr.CheckPermission(guild_id, client->GuildRank(), GUILD_HEAR)) + continue; + if(client->GetFilter(FILTER_GUILDSAY) == FilterHide) + continue; + } else if(chan_num == 5) { + if(client->GetFilter(FILTER_OOC) == FilterHide) + continue; + } + client->ChannelMessageSend(from, to, chan_num, language, message); + } +} + +void EntityList::Message(uint32 to_guilddbid, uint32 type, const char* message, ...) { + va_list argptr; + char buffer[4096]; + + va_start(argptr, message); + vsnprintf(buffer, 4096, message, argptr); + va_end(argptr); + + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + Client* client = iterator.GetData(); + if (to_guilddbid == 0 || client->IsInGuild(to_guilddbid)) + client->Message(type, buffer); + iterator.Advance(); + } +} + +void EntityList::QueueClientsGuild(Mob* sender, const EQApplicationPacket* app, bool ignore_sender, uint32 guild_id){ + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Client* client = iterator.GetData()->CastToClient(); + if (client->IsInGuild(guild_id)) + client->QueuePacket(app); + iterator.Advance(); + } +} + +void EntityList::QueueClientsGuildBankItemUpdate(const GuildBankItemUpdate_Struct *gbius, uint32 GuildID) +{ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildBank, sizeof(GuildBankItemUpdate_Struct)); + + GuildBankItemUpdate_Struct *outgbius = (GuildBankItemUpdate_Struct*)outapp->pBuffer; + + memcpy(outgbius, gbius, sizeof(GuildBankItemUpdate_Struct)); + + const Item_Struct *Item = database.GetItem(gbius->ItemID); + + LinkedListIterator iterator(client_list); + + iterator.Reset(); + + while(iterator.MoreElements()) + { + Client* client = iterator.GetData()->CastToClient(); + + if (client->IsInGuild(GuildID)) + { + if(Item && (gbius->Permissions == GuildBankPublicIfUsable)) + outgbius->Useable = Item->IsEquipable(client->GetBaseRace(), client->GetBaseClass()); + + client->QueuePacket(outapp); + } + + iterator.Advance(); + } + safe_delete(outapp); +} + +void EntityList::MessageStatus(uint32 to_guild_id, int to_minstatus, uint32 type, const char* message, ...) { + va_list argptr; + char buffer[4096]; + + va_start(argptr, message); + vsnprintf(buffer, 4096, message, argptr); + va_end(argptr); + + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) { + Client* client = iterator.GetData(); + if ((to_guild_id == 0 || client->IsInGuild(to_guild_id)) && client->Admin() >= to_minstatus) + client->Message(type, buffer); + iterator.Advance(); + } +} + +// works much like MessageClose, but with formatted strings +void EntityList::MessageClose_StringID(Mob *sender, bool skipsender, float dist, uint32 type, uint32 string_id, const char* message1,const char* message2,const char* message3,const char* message4,const char* message5,const char* message6,const char* message7,const char* message8,const char* message9) +{ + Client *c; + LinkedListIterator iterator(client_list); + float dist2 = dist * dist; + + + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) + { + c = iterator.GetData(); + if(c && c->DistNoRoot(*sender) <= dist2 && (!skipsender || c != sender)) + c->Message_StringID(type, string_id, message1, message2, message3, message4, message5, message6, message7, message8, message9); + } +} + +void EntityList::Message_StringID(Mob *sender, bool skipsender, uint32 type, uint32 string_id, const char* message1,const char* message2,const char* message3,const char* message4,const char* message5,const char* message6,const char* message7,const char* message8,const char* message9) +{ + Client *c; + LinkedListIterator iterator(client_list); + + + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) + { + c = iterator.GetData(); + if(c && (!skipsender || c != sender)) + c->Message_StringID(type, string_id, message1, message2, message3, message4, message5, message6, message7, message8, message9); + } +} + +void EntityList::MessageClose(Mob* sender, bool skipsender, float dist, uint32 type, const char* message, ...) { + va_list argptr; + char buffer[4096]; + + va_start(argptr, message); + vsnprintf(buffer, 4095, message, argptr); + va_end(argptr); + + float dist2 = dist * dist; + + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->DistNoRoot(*sender) <= dist2 && (!skipsender || iterator.GetData() != sender)) { + iterator.GetData()->Message(type, buffer); + } + iterator.Advance(); + } +} + +void EntityList::RemoveAllMobs(){ + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) + iterator.RemoveCurrent(); +} +void EntityList::RemoveAllClients(){ + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) + iterator.RemoveCurrent(false); +} +void EntityList::RemoveAllNPCs(){ + LinkedListIterator iterator(npc_list); + iterator.Reset(); + while(iterator.MoreElements()) { + iterator.RemoveCurrent(false); + } + npc_limit_list.clear(); +} +void EntityList::RemoveAllMercs(){ + LinkedListIterator iterator(merc_list); + iterator.Reset(); + while(iterator.MoreElements()) { + iterator.RemoveCurrent(false); + } +} +void EntityList::RemoveAllGroups(){ + while (group_list.size()) + group_list.pop_front(); +#if EQDEBUG >= 5 + CheckGroupList (__FILE__, __LINE__); +#endif +} + +void EntityList::RemoveAllRaids(){ + while (raid_list.size()) + raid_list.pop_front(); +} + +void EntityList::RemoveAllDoors(){ + LinkedListIterator iterator(door_list); + iterator.Reset(); + while(iterator.MoreElements()) + iterator.RemoveCurrent(); + DespawnAllDoors(); +} + +void EntityList::DespawnAllDoors(){ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RemoveAllDoors, 0); + this->QueueClients(0,outapp); + safe_delete(outapp); +} + +void EntityList::RespawnAllDoors(){ + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData() != 0) + { + EQApplicationPacket* outapp = new EQApplicationPacket(); + MakeDoorSpawnPacket(outapp, iterator.GetData()); + iterator.GetData()->FastQueuePacket(&outapp); + } + iterator.Advance(); + } +} + +void EntityList::RemoveAllCorpses(){ + LinkedListIterator iterator(corpse_list); + iterator.Reset(); + while(iterator.MoreElements()) + iterator.RemoveCurrent(); +} +void EntityList::RemoveAllObjects(){ + LinkedListIterator iterator(object_list); + iterator.Reset(); + while(iterator.MoreElements()) + iterator.RemoveCurrent(); +} +void EntityList::RemoveAllTraps(){ + LinkedListIterator iterator(trap_list); + iterator.Reset(); + while(iterator.MoreElements()) + iterator.RemoveCurrent(); +} +bool EntityList::RemoveMob(uint16 delete_id){ + if(delete_id==0) + return true; + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData()->GetID()==delete_id){ + if(iterator.GetData()->IsNPC()) + entity_list.RemoveNPC(delete_id); + else if(iterator.GetData()->IsClient()) + entity_list.RemoveClient(delete_id); + iterator.RemoveCurrent(); + return true; + } + iterator.Advance(); + } + return false; +} + +bool EntityList::RemoveMob(Mob *delete_mob) { + if(delete_mob==0) + return true; + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData()==delete_mob){ + iterator.RemoveCurrent(); + return true; + } + iterator.Advance(); + } + return false; +} + +bool EntityList::RemoveNPC(uint16 delete_id){ + LinkedListIterator iterator(npc_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData()->GetID()==delete_id){ + //make sure its proximity is removed + RemoveProximity(iterator.GetData()->GetID()); + //take it out of the list + iterator.RemoveCurrent(false);//Already Deleted + //take it out of our limit list + if(npc_limit_list.count(delete_id) == 1) + npc_limit_list.erase(delete_id); + return true; + } + iterator.Advance(); + } + return false; +} +bool EntityList::RemoveMerc(uint16 delete_id){ + LinkedListIterator iterator(merc_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData()->GetID()==delete_id){ + iterator.RemoveCurrent(false);//Already Deleted + return true; + } + iterator.Advance(); + } + return false; +} +bool EntityList::RemoveClient(uint16 delete_id){ + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData()->GetID()==delete_id){ + iterator.RemoveCurrent(false);//Already Deleted + return true; + } + iterator.Advance(); + } + return false; +} + +bool EntityList::RemoveClient(Client *delete_client){ + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData()==delete_client){ + iterator.RemoveCurrent(false);//Already Deleted + return true; + } + iterator.Advance(); + } + return false; +} + +bool EntityList::RemoveObject(uint16 delete_id){ + LinkedListIterator iterator(object_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData()->GetID()==delete_id){ + iterator.RemoveCurrent(); + return true; + } + iterator.Advance(); + } + return false; +} +bool EntityList::RemoveTrap(uint16 delete_id){ + LinkedListIterator iterator(trap_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData()->GetID()==delete_id){ + iterator.RemoveCurrent(); + return true; + } + iterator.Advance(); + } + return false; +} +bool EntityList::RemoveDoor(uint16 delete_id){ + LinkedListIterator iterator(door_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData()->GetID()==delete_id){ + iterator.RemoveCurrent(); + return true; + } + iterator.Advance(); + } + return false; +} +bool EntityList::RemoveCorpse(uint16 delete_id){ + LinkedListIterator iterator(corpse_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData()->GetID()==delete_id){ + iterator.RemoveCurrent(); + return true; + } + iterator.Advance(); + } + return false; +} +bool EntityList::RemoveGroup(uint32 delete_id){ + list::iterator iterator; + + iterator = group_list.begin(); + + while(iterator != group_list.end()) + { + if((*iterator)->GetID() == delete_id) { + group_list.remove (*iterator); +#if EQDEBUG >= 5 + CheckGroupList (__FILE__, __LINE__); +#endif + return true; + } + iterator++; + } +#if EQDEBUG >= 5 + CheckGroupList (__FILE__, __LINE__); +#endif + return false; +} + +bool EntityList::RemoveRaid(uint32 delete_id){ + list::iterator iterator; + + iterator = raid_list.begin(); + + while(iterator != raid_list.end()) + { + if((*iterator)->GetID() == delete_id) { + raid_list.remove (*iterator); + return true; + } + iterator++; + } + return false; +} + +void EntityList::Clear() +{ + RemoveAllClients(); + entity_list.RemoveAllTraps(); //we can have child npcs so we go first + entity_list.RemoveAllNPCs(); + entity_list.RemoveAllMobs(); + entity_list.RemoveAllCorpses(); + entity_list.RemoveAllGroups(); + entity_list.RemoveAllDoors(); + entity_list.RemoveAllObjects(); + entity_list.RemoveAllRaids(); + entity_list.RemoveAllLocalities(); + last_insert_id = 0; +} + +void EntityList::UpdateWho(bool iSendFullUpdate) { + if ((!worldserver.Connected()) || !ZoneLoaded) + return; + LinkedListIterator iterator(client_list); + uint32 tmpNumUpdates = numclients + 5; + ServerPacket* pack = 0; + ServerClientListKeepAlive_Struct* sclka = 0; + if (!iSendFullUpdate) { + pack = new ServerPacket(ServerOP_ClientListKA, sizeof(ServerClientListKeepAlive_Struct) + (tmpNumUpdates * 4)); + sclka = (ServerClientListKeepAlive_Struct*) pack->pBuffer; + } + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->InZone()) { + if (iSendFullUpdate) { + iterator.GetData()->UpdateWho(); + } + else { + + if (sclka->numupdates >= tmpNumUpdates) { + tmpNumUpdates += 10; + uint8* tmp = pack->pBuffer; + pack->pBuffer = new uint8[sizeof(ServerClientListKeepAlive_Struct) + (tmpNumUpdates * 4)]; + memset(pack->pBuffer, 0, sizeof(ServerClientListKeepAlive_Struct) + (tmpNumUpdates * 4)); + memcpy(pack->pBuffer, tmp, pack->size); + pack->size = sizeof(ServerClientListKeepAlive_Struct) + (tmpNumUpdates * 4); + safe_delete_array(tmp); + } + sclka->wid[sclka->numupdates] = iterator.GetData()->GetWID(); + sclka->numupdates++; + } + } + iterator.Advance(); + } + if (!iSendFullUpdate) { + pack->size = sizeof(ServerClientListKeepAlive_Struct) + (sclka->numupdates * 4); + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +void EntityList::RemoveEntity(uint16 id) +{ + if (id == 0) + return; + if(entity_list.RemoveMob(id)) + return; + else if(entity_list.RemoveCorpse(id)) + return; + else if(entity_list.RemoveDoor(id)) + return; + else if(entity_list.RemoveGroup(id)) + return; + else if(entity_list.RemoveTrap(id)) + return; + +#ifdef BOTS + // This block of code is necessary to clean up bot objects + else if(entity_list.RemoveBot(id)) + return; +#endif //BOTS + + else + entity_list.RemoveObject(id); +} + +void EntityList::Process() +{ + _ZP(EntityList_Process); + CheckSpawnQueue(); +} + +void EntityList::CountNPC(uint32* NPCCount, uint32* NPCLootCount, uint32* gmspawntype_count) { + LinkedListIterator iterator(npc_list); + *NPCCount = 0; + *NPCLootCount = 0; + + iterator.Reset(); + while(iterator.MoreElements()) + { + (*NPCCount)++; + (*NPCLootCount) += iterator.GetData()->CastToNPC()->CountLoot(); + if (iterator.GetData()->CastToNPC()->GetNPCTypeID() == 0) + (*gmspawntype_count)++; + iterator.Advance(); + } +} + +void EntityList::DoZoneDump(ZSDump_Spawn2* spawn2_dump, ZSDump_NPC* npc_dump, ZSDump_NPC_Loot* npcloot_dump, NPCType* gmspawntype_dump) { + uint32 spawn2index = 0; + uint32 NPCindex = 0; + uint32 NPCLootindex = 0; + uint32 gmspawntype_index = 0; + + if (npc_dump != 0) { + LinkedListIterator iterator(npc_list); + NPC* npc = 0; + iterator.Reset(); + while(iterator.MoreElements()) + { + npc = iterator.GetData()->CastToNPC(); + if (spawn2_dump != 0) + npc_dump[NPCindex].spawn2_dump_index = zone->DumpSpawn2(spawn2_dump, &spawn2index, npc->respawn2); + npc_dump[NPCindex].npctype_id = npc->GetNPCTypeID(); + npc_dump[NPCindex].cur_hp = npc->GetHP(); + if (npc->IsCorpse()) { + if (npc->CastToCorpse()->IsLocked()) + npc_dump[NPCindex].corpse = 2; + else + npc_dump[NPCindex].corpse = 1; + } + else + npc_dump[NPCindex].corpse = 0; + npc_dump[NPCindex].decay_time_left = 0xFFFFFFFF; + npc_dump[NPCindex].x = npc->GetX(); + npc_dump[NPCindex].y = npc->GetY(); + npc_dump[NPCindex].z = npc->GetZ(); + npc_dump[NPCindex].heading = npc->GetHeading(); + npc_dump[NPCindex].copper = npc->copper; + npc_dump[NPCindex].silver = npc->silver; + npc_dump[NPCindex].gold = npc->gold; + npc_dump[NPCindex].platinum = npc->platinum; + if (npcloot_dump != 0) + npc->DumpLoot(NPCindex, npcloot_dump, &NPCLootindex); + if (gmspawntype_dump != 0) { + if (npc->GetNPCTypeID() == 0) { + memcpy(&gmspawntype_dump[gmspawntype_index], npc->NPCTypedata, sizeof(NPCType)); + npc_dump[NPCindex].gmspawntype_index = gmspawntype_index; + gmspawntype_index++; + } + } + NPCindex++; + iterator.Advance(); + } + } + if (spawn2_dump != 0) + zone->DumpAllSpawn2(spawn2_dump, &spawn2index); +} + +void EntityList::Depop(bool StartSpawnTimer) { + LinkedListIterator iterator(npc_list); + + iterator.Reset(); + for(; iterator.MoreElements(); iterator.Advance()) + { + NPC *it = iterator.GetData(); + if(it) { + Mob *own = it->GetOwner(); + //do not depop player's pets... + if(own && own->IsClient()) + continue; + + if(it->IsFindable()) + UpdateFindableNPCState(it, true); + + it->Depop(StartSpawnTimer); + } + } +} + +void EntityList::DepopAll(int NPCTypeID, bool StartSpawnTimer) { + LinkedListIterator iterator(npc_list); + + iterator.Reset(); + for(; iterator.MoreElements(); iterator.Advance()) + { + NPC *it = iterator.GetData(); + if(it && (it->GetNPCTypeID() == (uint32)NPCTypeID)) + it->Depop(StartSpawnTimer); + } +} + +void EntityList::SendTraders(Client* client){ + LinkedListIterator iterator(client_list); + iterator.Reset(); + Client* trader; + while(iterator.MoreElements()) { + trader=iterator.GetData(); + if(trader->IsTrader()) + client->SendTraderPacket(trader); + + if(trader->IsBuyer()) + client->SendBuyerPacket(trader); + + iterator.Advance(); + } +} + +void EntityList::RemoveFromHateLists(Mob* mob, bool settoone) { + LinkedListIterator iterator(npc_list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->CheckAggro(mob)) { + if (!settoone) + { + iterator.GetData()->RemoveFromHateList(mob); + } + else + { + iterator.GetData()->SetHate(mob,1); + } + } + iterator.Advance(); + } +} + +void EntityList::RemoveDebuffs(Mob* caster) +{ + LinkedListIterator iterator(mob_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + iterator.GetData()->BuffFadeDetrimentalByCaster(caster); + iterator.Advance(); + } +} + + +// Currently, a new packet is sent per entity. +// @todo: Come back and use FLAG_COMBINED to pack +// all updates into one packet. +void EntityList::SendPositionUpdates(Client* client, uint32 cLastUpdate, float range, Entity* alwayssend, bool iSendEvenIfNotChanged) { + range = range * range; + LinkedListIterator iterator(mob_list); + + EQApplicationPacket* outapp = 0; + PlayerPositionUpdateServer_Struct* ppu = 0; + Mob* mob = 0; + + iterator.Reset(); + while(iterator.MoreElements()) { + if (outapp == 0) { + outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + ppu = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer; + } + mob = iterator.GetData()->CastToMob(); + if (mob && !mob->IsCorpse() && (iterator.GetData() != client) + && (mob->IsClient() || iSendEvenIfNotChanged || (mob->LastChange() >= cLastUpdate)) + && (!iterator.GetData()->IsClient() || !iterator.GetData()->CastToClient()->GMHideMe(client))) { + + //bool Grouped = client->HasGroup() && mob->IsClient() && (client->GetGroup() == mob->CastToClient()->GetGroup()); + + //if (range == 0 || (iterator.GetData() == alwayssend) || Grouped || (mob->DistNoRootNoZ(*client) <= range)) { + if (range == 0 || (iterator.GetData() == alwayssend) || mob->IsClient() || (mob->DistNoRoot(*client) <= range)) { + mob->MakeSpawnUpdate(ppu); + } + if(mob && mob->IsClient() && mob->GetID()>0) { + client->QueuePacket(outapp, false, Client::CLIENT_CONNECTED); + } + } + safe_delete(outapp); + outapp = 0; + iterator.Advance(); + } + + safe_delete(outapp); +} + +char* EntityList::MakeNameUnique(char* name) { + bool used[300]; + memset(used, 0, sizeof(used)); + name[61] = 0; name[62] = 0; name[63] = 0; + + LinkedListIterator iterator(mob_list); + + iterator.Reset(); + int len = strlen(name); + while(iterator.MoreElements()) { + if (iterator.GetData()->IsMob()) { + if (strncasecmp(iterator.GetData()->CastToMob()->GetName(), name, len) == 0) { + if (Seperator::IsNumber(&iterator.GetData()->CastToMob()->GetName()[len])) { + used[atoi(&iterator.GetData()->CastToMob()->GetName()[len])] = true; + } + } + } + iterator.Advance(); + } + for (int i=0; i < 300; i++) { + if (!used[i]) { + #ifdef _WINDOWS + snprintf(name, 64, "%s%03d", name, i); + #else + //glibc clears destination of snprintf + //make a copy of name before snprintf--misanthropicfiend + char temp_name[64]; + strn0cpy(temp_name, name, 64); + snprintf(name, 64, "%s%03d", temp_name, i); + #endif + return name; + } + } + LogFile->write(EQEMuLog::Error, "Fatal error in EntityList::MakeNameUnique: Unable to find unique name for '%s'", name); + char tmp[64] = "!"; + strn0cpy(&tmp[1], name, sizeof(tmp) - 1); + strcpy(name, tmp); + return MakeNameUnique(name); +} + +char* EntityList::RemoveNumbers(char* name) { + char tmp[64]; + memset(tmp, 0, sizeof(tmp)); + int k = 0; + for (unsigned int i=0; i '9') + tmp[k++] = name[i]; + } + strn0cpy(name, tmp, sizeof(tmp)); + return name; +} +void EntityList::ListNPCs(Client* client, const char* arg1, const char* arg2, uint8 searchtype) { + if (arg1 == 0) + searchtype = 0; + else if (arg2 == 0 && searchtype >= 2) + searchtype = 0; + LinkedListIterator iterator(npc_list); + uint32 x = 0; + uint32 z = 0; + char sName[36]; + + iterator.Reset(); + client->Message(0, "NPCs in the zone:"); + if(searchtype == 0) { + while(iterator.MoreElements()) + { + NPC *n = iterator.GetData(); + + client->Message(0, " %5d: %s (%.0f, %0.f, %.0f)", n->GetID(), n->GetName(), n->GetX(), n->GetY(), n->GetZ()); + x++; + z++; + iterator.Advance(); + } + } + else if(searchtype == 1) { + client->Message(0, "Searching by name method. (%s)",arg1); + char* tmp = new char[strlen(arg1) + 1]; + strcpy(tmp, arg1); + strupr(tmp); + while(iterator.MoreElements()) { + z++; + strcpy(sName, iterator.GetData()->GetName()); + strupr(sName); + if (strstr(sName, tmp)) { + NPC *n = iterator.GetData(); + client->Message(0, " %5d: %s (%.0f, %.0f, %.0f)", n->GetID(), n->GetName(), n->GetX(), n->GetY(), n->GetZ()); + x++; + } + iterator.Advance(); + } + safe_delete_array(tmp); + } + else if(searchtype == 2) { + client->Message(0, "Searching by number method. (%s %s)",arg1,arg2); + while(iterator.MoreElements()) { + z++; + if ((iterator.GetData()->GetID() >= atoi(arg1)) && (iterator.GetData()->GetID() <= atoi(arg2)) && (atoi(arg1) <= atoi(arg2))) { + client->Message(0, " %5d: %s", iterator.GetData()->GetID(), iterator.GetData()->GetName()); + x++; + } + iterator.Advance(); + } + } + client->Message(0, "%d npcs listed. There is a total of %d npcs in this zone.", x, z); +} + +void EntityList::ListNPCCorpses(Client* client) { + LinkedListIterator iterator(corpse_list); + uint32 x = 0; + + iterator.Reset(); + client->Message(0, "NPC Corpses in the zone:"); + while(iterator.MoreElements()) { + if (iterator.GetData()->IsNPCCorpse()) { + client->Message(0, " %5d: %s", iterator.GetData()->GetID(), iterator.GetData()->GetName()); + x++; + } + iterator.Advance(); + } + client->Message(0, "%d npc corpses listed.", x); +} + +void EntityList::ListPlayerCorpses(Client* client) { + LinkedListIterator iterator(corpse_list); + uint32 x = 0; + + iterator.Reset(); + client->Message(0, "Player Corpses in the zone:"); + while(iterator.MoreElements()) { + if (iterator.GetData()->IsPlayerCorpse()) { + client->Message(0, " %5d: %s", iterator.GetData()->GetID(), iterator.GetData()->GetName()); + x++; + } + iterator.Advance(); + } + client->Message(0, "%d player corpses listed.", x); +} + +void EntityList::FindPathsToAllNPCs() +{ + if(!zone->pathing) + return; + + LinkedListIterator Iterator(npc_list); + + Iterator.Reset(); + + while(Iterator.MoreElements()) + { + VERTEX Node0 = zone->pathing->GetPathNodeCoordinates(0, false); + VERTEX Dest(Iterator.GetData()->GetX(), Iterator.GetData()->GetY(), Iterator.GetData()->GetZ()); + list Route = zone->pathing->FindRoute(Node0, Dest); + if(Route.size() == 0) + printf("Unable to find a route to %s\n", Iterator.GetData()->GetName()); + else + printf("Found a route to %s\n", Iterator.GetData()->GetName()); + + Iterator.Advance(); + } + + fflush(stdout); + +} + +// returns the number of corpses deleted. A negative number indicates an error code. +int32 EntityList::DeleteNPCCorpses() { + LinkedListIterator iterator(corpse_list); + int32 x = 0; + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->IsNPCCorpse()) { + iterator.GetData()->Depop(); + x++; + } + iterator.Advance(); + } + return x; +} + +// returns the number of corpses deleted. A negative number indicates an error code. +int32 EntityList::DeletePlayerCorpses() { + LinkedListIterator iterator(corpse_list); + int32 x = 0; + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->IsPlayerCorpse()) { + iterator.GetData()->CastToCorpse()->Delete(); + x++; + } + iterator.Advance(); + } + return x; +} +void EntityList::SendPetitionToAdmins(){ + LinkedListIterator iterator(client_list); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PetitionUpdate,sizeof(PetitionUpdate_Struct)); + PetitionUpdate_Struct* pcus = (PetitionUpdate_Struct*) outapp->pBuffer; + pcus->petnumber = 0; // Petition Number + pcus->color = 0; + pcus->status = 0xFFFFFFFF; + pcus->senttime = 0; + strcpy(pcus->accountid, ""); + strcpy(pcus->gmsenttoo, ""); + pcus->quetotal=0; + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->CastToClient()->Admin() >= 80) + iterator.GetData()->CastToClient()->QueuePacket(outapp); + iterator.Advance(); + } + safe_delete(outapp); +} +void EntityList::SendPetitionToAdmins(Petition* pet) { + LinkedListIterator iterator(client_list); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PetitionUpdate,sizeof(PetitionUpdate_Struct)); + PetitionUpdate_Struct* pcus = (PetitionUpdate_Struct*) outapp->pBuffer; + pcus->petnumber = pet->GetID(); // Petition Number + if (pet->CheckedOut()) { + pcus->color = 0x00; + pcus->status = 0xFFFFFFFF; + pcus->senttime = pet->GetSentTime(); + strcpy(pcus->accountid, ""); + strcpy(pcus->gmsenttoo, ""); + } + else { + pcus->color = pet->GetUrgency(); // 0x00 = green, 0x01 = yellow, 0x02 = red + pcus->status = pet->GetSentTime(); + pcus->senttime = pet->GetSentTime(); // 4 has to be 0x1F + strcpy(pcus->accountid, pet->GetAccountName()); + strcpy(pcus->charname, pet->GetCharName()); + } + pcus->quetotal = petition_list.GetTotalPetitions(); + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->CastToClient()->Admin() >= 80) { + if (pet->CheckedOut()) + strcpy(pcus->gmsenttoo, ""); + else + strcpy(pcus->gmsenttoo, iterator.GetData()->CastToClient()->GetName()); + iterator.GetData()->CastToClient()->QueuePacket(outapp); + } + iterator.Advance(); + } + safe_delete(outapp); +} + +void EntityList::ClearClientPetitionQueue() { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PetitionUpdate,sizeof(PetitionUpdate_Struct)); + PetitionUpdate_Struct* pet = (PetitionUpdate_Struct*) outapp->pBuffer; + pet->color = 0x00; + pet->status = 0xFFFFFFFF; + pet->senttime = 0; + strcpy(pet->accountid, ""); + strcpy(pet->gmsenttoo, ""); + strcpy(pet->charname, ""); + pet->quetotal = petition_list.GetTotalPetitions(); + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->CastToClient()->Admin() >= 100) { + int x = 0; + for (x=0;x<64;x++) { + pet->petnumber = x; + iterator.GetData()->CastToClient()->QueuePacket(outapp); + } + } + iterator.Advance(); + } + safe_delete(outapp); + return; +} + +void EntityList::WriteEntityIDs() { + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + cout << "ID: " << iterator.GetData()->GetID() << " Name: " << iterator.GetData()->GetName() << endl; + iterator.Advance(); + } +} + +BulkZoneSpawnPacket::BulkZoneSpawnPacket(Client* iSendTo, uint32 iMaxSpawnsPerPacket) { + data = 0; + pSendTo = iSendTo; + pMaxSpawnsPerPacket = iMaxSpawnsPerPacket; +#ifdef _EQDEBUG + if (pMaxSpawnsPerPacket <= 0 || pMaxSpawnsPerPacket > MAX_SPAWNS_PER_PACKET) { + // ok, this *cant* be right =p + ThrowError("Error in BulkZoneSpawnPacket::BulkZoneSpawnPacket(): pMaxSpawnsPerPacket outside range that makes sense"); + } +#endif +} + +BulkZoneSpawnPacket::~BulkZoneSpawnPacket() { + SendBuffer(); + safe_delete_array(data) +} + +bool BulkZoneSpawnPacket::AddSpawn(NewSpawn_Struct* ns) { + if (!data) { + data = new NewSpawn_Struct[pMaxSpawnsPerPacket]; + memset(data, 0, sizeof(NewSpawn_Struct) * pMaxSpawnsPerPacket); + index = 0; + } + memcpy(&data[index], ns, sizeof(NewSpawn_Struct)); + index++; + if (index >= pMaxSpawnsPerPacket) { + SendBuffer(); + return true; + } + return false; +} + +void BulkZoneSpawnPacket::SendBuffer() { + if (!data) + return; + + uint32 tmpBufSize = (index * sizeof(NewSpawn_Struct)); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZoneSpawns, (unsigned char *)data, tmpBufSize); + + if (pSendTo) { + pSendTo->FastQueuePacket(&outapp); + } else { + entity_list.QueueClients(0, outapp); + safe_delete(outapp); + } + memset(data, 0, sizeof(NewSpawn_Struct) * pMaxSpawnsPerPacket); + index = 0; +} + +void EntityList::DoubleAggro(Mob* who) +{ + LinkedListIterator iterator(npc_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->CheckAggro(who)) + iterator.GetData()->SetHate(who,iterator.GetData()->CastToNPC()->GetHateAmount(who),iterator.GetData()->CastToNPC()->GetHateAmount(who)*2); + iterator.Advance(); + } +} + +void EntityList::HalveAggro(Mob* who) +{ + LinkedListIterator iterator(npc_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->CastToNPC()->CheckAggro(who)) + iterator.GetData()->CastToNPC()->SetHate(who,iterator.GetData()->CastToNPC()->GetHateAmount(who)/2); + iterator.Advance(); + } +} + + +void EntityList::Evade(Mob *who) +{ + uint32 flatval = who->GetLevel() * 13; + int amt = 0; + LinkedListIterator iterator(npc_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->CastToNPC()->CheckAggro(who)){ + amt = iterator.GetData()->CastToNPC()->GetHateAmount(who); + amt -= flatval; + if(amt > 0) + iterator.GetData()->CastToNPC()->SetHate(who, amt); + else + iterator.GetData()->CastToNPC()->SetHate(who, 0); + } + iterator.Advance(); + } +} + +//removes "targ" from all hate lists, including feigned, in the zone +void EntityList::ClearAggro(Mob* targ) { + LinkedListIterator iterator(npc_list); + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->CheckAggro(targ)) + iterator.GetData()->RemoveFromHateList(targ); + iterator.GetData()->RemoveFromFeignMemory(targ->CastToClient()); //just in case we feigned + iterator.Advance(); + } +} + +void EntityList::ClearFeignAggro(Mob* targ) +{ + LinkedListIterator iterator(npc_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->CheckAggro(targ)) + { + if(iterator.GetData()->SpecAttacks[IMMUNE_FEIGN_DEATH]) + { + iterator.GetData()->SetHate(targ, 0); + iterator.Advance(); + continue; + } + + iterator.GetData()->RemoveFromHateList(targ); + // EverHood 6/24/06 + // For client targets if the mob that hated us is 35+ + // there is a 3 outta 5 chance he adds us to feign memory + if(targ->IsClient()){ + if (iterator.GetData()->GetLevel() >= 35 && (MakeRandomInt(1,100)<=60)){ + iterator.GetData()->AddFeignMemory(targ->CastToClient()); + } else { + targ->CastToClient()->RemoveXTarget(iterator.GetData(), false); + } + } + } + iterator.Advance(); + } +} +// EverHood 6/17/06 +void EntityList::ClearZoneFeignAggro(Client* targ) +{ + LinkedListIterator iterator(npc_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + iterator.GetData()->RemoveFromFeignMemory(targ); + targ->CastToClient()->RemoveXTarget(iterator.GetData(), false); + iterator.Advance(); + } +} + +void EntityList::AggroZone(Mob* who, int hate) { + LinkedListIterator iterator(npc_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + iterator.GetData()->AddToHateList(who, hate); + iterator.Advance(); + } +} + +// Signal Quest command function +void EntityList::SignalMobsByNPCID(uint32 snpc, int signal_id) +{ + LinkedListIterator iterator(npc_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + NPC *it = iterator.GetData(); + if (it->GetNPCTypeID() == snpc) + { + it->SignalNPC(signal_id); + } + iterator.Advance(); + } +} + + +bool EntityList::MakeTrackPacket(Client* client) +{ + uint32 distance = 0; + float MobDistance; + + if(client->GetClass() == DRUID) + distance = (client->GetSkill(TRACKING)*10); + else if(client->GetClass() == RANGER) + distance = (client->GetSkill(TRACKING)*12); + else if(client->GetClass() == BARD) + distance = (client->GetSkill(TRACKING)*7); + if(distance <= 0) + return false; + if(distance<300) + distance=300; + + uint32 spe= 0; + bool ret = false; + + spe = mob_list.Count() + 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; + + LinkedListIterator iterator(mob_list); + iterator.Reset(); + + Group *g = client->GetGroup(); + + while(iterator.MoreElements()) + { + if (iterator.GetData() && ((MobDistance = iterator.GetData()->DistNoZ(*client))<=distance)) + { + if((iterator.GetData() != client) && iterator.GetData()->IsTrackable()) { + memset(track_ent, 0, sizeof(Track_Struct)); + Mob* cur_entity = iterator.GetData(); + 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++; + } + } + + iterator.Advance(); + } + + 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); + } + + safe_delete_array(buffer1); + safe_delete_array(buffer2); + + return ret; +} + +void EntityList::MessageGroup(Mob* sender, bool skipclose, uint32 type, const char* message, ...) { + va_list argptr; + char buffer[4096]; + + va_start(argptr, message); + vsnprintf(buffer, 4095, message, argptr); + va_end(argptr); + + float dist2 = 100; + + if (skipclose) + dist2 = 0; + + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData() != sender && (iterator.GetData()->Dist(*sender) <= dist2 || iterator.GetData()->GetGroup() == sender->CastToClient()->GetGroup())) { + iterator.GetData()->Message(type, buffer); + } + iterator.Advance(); + } +} + + +bool EntityList::Fighting(Mob* targ) { + LinkedListIterator iterator(npc_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->CheckAggro(targ)) + { + return true; + } + iterator.Advance(); + } + return false; +} + +void EntityList::AddHealAggro(Mob* target, Mob* caster, uint16 thedam) +{ + LinkedListIterator iterator(npc_list); + + iterator.Reset(); + NPC *cur = NULL; + uint16 count = 0; + while(iterator.MoreElements()) + { + cur = iterator.GetData(); + + if(!cur->CheckAggro(target)) + { + iterator.Advance(); + continue; + } + if (!cur->IsMezzed() && !cur->IsStunned() && !cur->IsFeared()) + { + ++count; + } + iterator.Advance(); + } + + if(thedam > 1) + { + if(count > 0) + thedam = (thedam / count); + + if(thedam < 1) + thedam = 1; + } + + cur = NULL; + iterator.Reset(); + while(iterator.MoreElements()) + { + cur = iterator.GetData(); + if(!cur->CheckAggro(target)){ + iterator.Advance(); + continue; + } + + if (!cur->IsMezzed() && !cur->IsStunned() && !cur->IsFeared()) + { + if(cur->IsPet()){ + if(caster){ + if(cur->CheckAggro(caster)) + { + cur->AddToHateList(caster, thedam); + } + } + } + else{ + if(caster){ + if(cur->CheckAggro(caster)) + { + cur->AddToHateList(caster, thedam); + } + else + { + cur->AddToHateList(caster, thedam*0.33); + } + } + } + } + iterator.Advance(); + } +} + +void EntityList::OpenDoorsNear(NPC* who) +{ + LinkedListIterator iterator(door_list); + iterator.Reset(); + while(iterator.MoreElements()) { + Doors *cdoor = iterator.GetData(); + if(cdoor && !cdoor->IsDoorOpen()) { + float zdiff = who->GetZ() - cdoor->GetZ(); + if(zdiff < 0) + zdiff = 0 - zdiff; + float curdist = 0; + float tmp = who->GetX() - cdoor->GetX(); + curdist += tmp * tmp; + tmp = who->GetY() - cdoor->GetY(); + curdist += tmp * tmp; + if (zdiff < 10 && curdist <= 100) { + cdoor->NPCOpen(who); + } + } + iterator.Advance(); + } +} + +void EntityList::SendAlarm(Trap* trap, Mob* currenttarget, uint8 kos) +{ + LinkedListIterator iterator(npc_list); + iterator.Reset(); + + float val2 = trap->effectvalue * trap->effectvalue; + + while(iterator.MoreElements()) + { + NPC *cur = iterator.GetData(); + float curdist = 0; + float tmp = cur->GetX() - trap->x; + curdist += tmp*tmp; + tmp = cur->GetY() - trap->y; + curdist += tmp*tmp; + tmp = cur->GetZ() - trap->z; + curdist += tmp*tmp; + if (!cur->GetOwner() && + /*!cur->CastToMob()->dead && */ + !cur->IsEngaged() && + curdist <= val2 ) + { + if(kos) + { + uint8 factioncon = currenttarget->GetReverseFactionCon(cur); + if(factioncon == FACTION_THREATENLY || factioncon == FACTION_SCOWLS) + { + cur->AddToHateList(currenttarget,1); + } + } + else + { + cur->AddToHateList(currenttarget,1); + } + } + iterator.Advance(); + } +} + +void EntityList::AddProximity(NPC *proximity_for) { + RemoveProximity(proximity_for->GetID()); + + proximity_list.Insert(proximity_for); + + proximity_for->proximity = new NPCProximity; +} + +bool EntityList::RemoveProximity(uint16 delete_npc_id) { + LinkedListIterator iterator(proximity_list); + iterator.Reset(); + while(iterator.MoreElements()) { + NPC *d = iterator.GetData(); + if(d->GetID() == delete_npc_id) { + //safe_delete(d->proximity); + iterator.RemoveCurrent(false); + return true; + } + iterator.Advance(); + } + return false; +} + +void EntityList::RemoveAllLocalities() { + LinkedListIterator iterator(proximity_list); + iterator.Reset(); + while(iterator.MoreElements()) + iterator.RemoveCurrent(false); +} + +void EntityList::ProcessMove(Client *c, float x, float y, float z) { + /* + We look through each proximity, looking to see if last_* was in(out) + the proximity, and the new supplied coords are out(in)... + */ + LinkedListIterator iterator(proximity_list); + std::list skip_ids; + + float last_x = c->ProximityX(); + float last_y = c->ProximityY(); + float last_z = c->ProximityZ(); + + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) { + NPC *d = iterator.GetData(); + NPCProximity *l = d->proximity; + if(l == NULL) + continue; + + //This is done to address the issue of this code not being reentrant + //because perl can call clear_proximity() while we're still iterating through the list + //This causes our list to become invalid but we don't know it. On GCC it's basic heap + //corruption and it doesn't appear to catch it at all. + //MSVC it's a crash with 0xfeeefeee debug address (freed memory off the heap) + std::list::iterator iter = skip_ids.begin(); + bool skip = false; + while(iter != skip_ids.end()) + { + if(d->GetID() == (*iter)) + { + skip = true; + break; + } + iter++; + } + + if(skip) + { + continue; + } + + //check both bounding boxes, if either coords pairs + //cross a boundary, send the event. + bool old_in = true; + bool new_in = true; + if( last_x < l->min_x || last_x > l->max_x + || last_y < l->min_y || last_y > l->max_y + || last_z < l->min_z || last_z > l->max_z ) { + old_in = false; + } + if( x < l->min_x || x > l->max_x + || y < l->min_y || y > l->max_y + || z < l->min_z || z > l->max_z ) { + new_in = false; + } + + if(old_in && !new_in) { + //we were in the proximity, we are no longer, send event exit + parse->EventNPC(EVENT_EXIT, d, c, "", 0); + + //Reentrant fix + iterator.Reset(); + skip_ids.push_back(d->GetID()); + } else if(new_in && !old_in) { + //we were not in the proximity, we are now, send enter event + parse->EventNPC(EVENT_ENTER, d, c, "", 0); + + //Reentrant fix + iterator.Reset(); + skip_ids.push_back(d->GetID()); + } + } + +} + +void EntityList::ProcessProximitySay(const char *Message, Client *c, uint8 language) { + + if(!Message || !c) + return; + + LinkedListIterator iterator(proximity_list); + + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) { + NPC *d = iterator.GetData(); + NPCProximity *l = d->proximity; + if(l == NULL || !l->say) + continue; + + if( c->GetX() < l->min_x || c->GetX() > l->max_x + || c->GetY() < l->min_y || c->GetY() > l->max_y + || c->GetZ() < l->min_z || c->GetZ() > l->max_z ) + continue; + + parse->EventNPC(EVENT_PROXIMITY_SAY, d, c, Message, language); + } +} + +void EntityList::SaveAllClientsTaskState() { + + if(!taskmanager) return; + + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Client* client = iterator.GetData(); + if(client->IsTaskStateLoaded()) { + client->SaveTaskState(); + } + + iterator.Advance(); + } +} + +void EntityList::ReloadAllClientsTaskState(int TaskID) { + + if(!taskmanager) return; + + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Client* client = iterator.GetData(); + if(client->IsTaskStateLoaded()) { + // If we have been passed a TaskID, only reload the client state if they have + // that Task active. + if((!TaskID) || (TaskID && client->IsTaskActive(TaskID))) { + _log(TASKS__CLIENTLOAD, "Reloading Task State For Client %s", client->GetName()); + client->RemoveClientTaskState(); + client->LoadClientTaskState(); + taskmanager->SendActiveTasksToClient(client); + } + } + iterator.Advance(); + } +} + +bool EntityList::IsMobInZone(Mob *who) { + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(who == iterator.GetData()) + return(true); + iterator.Advance(); + } + return(false); +} + +/* +Code to limit the ammount of certain NPCs in a given zone. +Primarily used to make a named mob unique within the zone, but written +to be more generic allowing limits larger than 1. + +Maintain this stuff in a seperate list since the number +of limited NPCs will most likely be much smaller than the number +of NPCs in the entire zone. +*/ +void EntityList::LimitAddNPC(NPC *npc) { + if(!npc) + return; + + SpawnLimitRecord r; + + uint16 eid = npc->GetID(); + r.spawngroup_id = npc->GetSp2(); + r.npc_type = npc->GetNPCTypeID(); + + npc_limit_list[eid] = r; +} + +void EntityList::LimitRemoveNPC(NPC *npc) { + if(!npc) + return; + + uint16 eid = npc->GetID(); + npc_limit_list.erase(eid); +} + +//check a limit over the entire zone. +//returns true if the limit has not been reached +bool EntityList::LimitCheckType(uint32 npc_type, int count) { + if(count < 1) + return(true); + + map::iterator cur,end; + cur = npc_limit_list.begin(); + end = npc_limit_list.end(); + + for(; cur != end; cur++) { + if(cur->second.npc_type == npc_type) { + count--; + if(count == 0) { + return(false); + } + } + } + return(true); +} + +//check limits on an npc type in a given spawn group. +//returns true if the limit has not been reached +bool EntityList::LimitCheckGroup(uint32 spawngroup_id, int count) { + if(count < 1) + return(true); + + map::iterator cur,end; + cur = npc_limit_list.begin(); + end = npc_limit_list.end(); + + for(; cur != end; cur++) { + if(cur->second.spawngroup_id == spawngroup_id) { + count--; + if(count == 0) { + return(false); + } + } + } + return(true); +} + +//check limits on an npc type in a given spawn group, and +//checks limits on the entire zone in one pass. +//returns true if neither limit has been reached +bool EntityList::LimitCheckBoth(uint32 npc_type, uint32 spawngroup_id, int group_count, int type_count) { + if(group_count < 1 && type_count < 1) + return(true); + + map::iterator cur,end; + cur = npc_limit_list.begin(); + end = npc_limit_list.end(); + + for(; cur != end; cur++) { + if(cur->second.npc_type == npc_type) { + type_count--; + if(type_count == 0) { + return(false); + } + } + if(cur->second.spawngroup_id == spawngroup_id) { + group_count--; + if(group_count == 0) { + return(false); + } + } + } + return(true); +} + +bool EntityList::LimitCheckName(const char *npc_name) +{ + LinkedListIterator iterator(npc_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + NPC* npc = iterator.GetData(); + if(npc) + { + if(strcasecmp(npc_name, npc->GetRawNPCTypeName()) == 0) + { + return false; + } + } + iterator.Advance(); + } + return true; +} + +void EntityList::RadialSetLogging(Mob *around, bool enabled, bool clients, bool non_clients, float range) { + float range2 = range * range; + + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) { + Mob* mob = iterator.GetData(); + + iterator.Advance(); + + if(mob->IsClient()) { + if(!clients) + continue; + } else { + if(!non_clients) + continue; + } + + if(around->DistNoRoot(*mob) > range2) + continue; + + if(enabled) + mob->EnableLogging(); + else + mob->DisableLogging(); + } +} + +void EntityList::UpdateHoTT(Mob* target) { + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Client* c = iterator.GetData(); + if (c->GetTarget() == target) { + if (target->GetTarget()) c->SetHoTT(target->GetTarget()->GetID()); + else c->SetHoTT(0); + + c->UpdateXTargetType(TargetsTarget, target->GetTarget()); + } + iterator.Advance(); + } +} + +void EntityList::DestroyTempPets(Mob *owner) +{ + LinkedListIterator iterator(npc_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + NPC* n = iterator.GetData(); + if(n->GetSwarmInfo()) + { + if(n->GetSwarmInfo()->owner_id == owner->GetID()) + { + n->Depop(); + } + } + iterator.Advance(); + } +} + +bool Entity::CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z, float trg_x, float trg_y, float trg_z, float perwalk) +{ + if(zone->zonemap == NULL) { + return(true); + } + VERTEX myloc; + VERTEX oloc; + VERTEX hit; + + myloc.x = cur_x; + myloc.y = cur_y; + myloc.z = cur_z+5; + + oloc.x = trg_x; + oloc.y = trg_y; + oloc.z = trg_z+5; + + if (myloc.x == oloc.x && myloc.y == oloc.y && myloc.z == oloc.z) + return true; + + FACE *onhit; + + if (!zone->zonemap->LineIntersectsZoneNoZLeaps(myloc,oloc,perwalk,&hit,&onhit)) + return true; + return false; +} + +void EntityList::QuestJournalledSayClose(Mob *sender, Client *QuestInitiator, float dist, const char* mobname, const char* message) +{ + Client *c; + LinkedListIterator iterator(client_list); + float dist2 = dist * dist; + + // Send the message to the quest initiator such that the client will enter it into the NPC Quest Journal + if(QuestInitiator) { + + char *buf = new char[strlen(mobname) + strlen(message) + 10]; + sprintf(buf, "%s says, '%s'", mobname, message); + QuestInitiator->QuestJournalledMessage(mobname, buf); + safe_delete_array(buf); + } + // Use the old method for all other nearby clients + for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) + { + c = iterator.GetData(); + if(c && (c != QuestInitiator) && c->DistNoRoot(*sender) <= dist2) + c->Message_StringID(10, GENERIC_SAY, mobname, message); + } +} + +Corpse* EntityList::GetClosestCorpse(Mob* sender, const char* Name) +{ + if(!sender) + return NULL; + + uint32 CurrentDistance, ClosestDistance = 4294967295u; + + Corpse *CurrentCorpse, *ClosestCorpse = NULL; + + LinkedListIterator iterator(corpse_list); + + iterator.Reset(); + + while(iterator.MoreElements()) + { + CurrentCorpse = iterator.GetData(); + + iterator.Advance(); + + if(Name && strcasecmp(CurrentCorpse->GetOwnerName(), Name)) + continue; + + CurrentDistance = ((CurrentCorpse->GetY() - sender->GetY()) * (CurrentCorpse->GetY() - sender->GetY())) + + ((CurrentCorpse->GetX() - sender->GetX()) * (CurrentCorpse->GetX() - sender->GetX())); + + if(CurrentDistance < ClosestDistance) + { + ClosestDistance = CurrentDistance; + + ClosestCorpse = CurrentCorpse; + + } + } + return ClosestCorpse; +} + +void EntityList::ForceGroupUpdate(uint32 gid) { + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if(iterator.GetData()){ + Group *g = NULL; + g = iterator.GetData()->GetGroup(); + if(g){ + if(g->GetID() == gid) + { + database.RefreshGroupFromDB(iterator.GetData()); + } + } + } + iterator.Advance(); + } +} + +void EntityList::SendGroupLeave(uint32 gid, const char *name) { + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) { + Client *c = iterator.GetData(); + if(c){ + Group *g = NULL; + g = c->GetGroup(); + if(g){ + if(g->GetID() == gid) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); + GroupJoin_Struct* gj = (GroupJoin_Struct*) outapp->pBuffer; + strcpy(gj->membername, name); + gj->action = groupActLeave; + strcpy(gj->yourname, c->GetName()); + Mob *Leader = g->GetLeader(); + if(Leader) + Leader->CastToClient()->GetGroupAAs(&gj->leader_aas); + c->QueuePacket(outapp); + safe_delete(outapp); + g->DelMemberOOZ(name); + if(g->IsLeader(c) && c->IsLFP()) + c->UpdateLFP(); + } + } + } + iterator.Advance(); + } +} + +void EntityList::SendGroupJoin(uint32 gid, const char *name) { + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) { + if(iterator.GetData()){ + Group *g = NULL; + g = iterator.GetData()->GetGroup(); + if(g){ + if(g->GetID() == gid) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); + GroupJoin_Struct* gj = (GroupJoin_Struct*) outapp->pBuffer; + strcpy(gj->membername, name); + gj->action = groupActJoin; + strcpy(gj->yourname, iterator.GetData()->GetName()); + Mob *Leader = g->GetLeader(); + if(Leader) + Leader->CastToClient()->GetGroupAAs(&gj->leader_aas); + + iterator.GetData()->QueuePacket(outapp); + safe_delete(outapp); + } + } + } + iterator.Advance(); + } +} + +void EntityList::GroupMessage(uint32 gid, const char *from, const char *message) +{ + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) { + if(iterator.GetData()){ + Group *g = NULL; + g = iterator.GetData()->GetGroup(); + if(g){ + if(g->GetID() == gid) + { + iterator.GetData()->ChannelMessageSend(from, iterator.GetData()->GetName(),2,0,message); + } + } + } + iterator.Advance(); + } +} + +uint16 EntityList::CreateGroundObject(uint32 itemid, float x, float y, float z, float heading, uint32 decay_time) +{ + const Item_Struct* is = database.GetItem(itemid); + if(is) + { + ItemInst *i = new ItemInst(is, is->MaxCharges); + if(i) + { + Object* object = new Object(i,x,y,z,heading,decay_time); + entity_list.AddObject(object, true); + + safe_delete(i); + if(object) + return object->GetID(); + } + return 0; // fell through itemstruct + } + return 0; // fell through everything, this is bad/incomplete from perl +} + +uint16 EntityList::CreateGroundObjectFromModel(const char *model, float x, float y, float z, float heading, uint8 type, uint32 decay_time) +{ + if(model) + { + Object* object = new Object(model,x,y,z,heading,type); + entity_list.AddObject(object, true); + + if(object) + return object->GetID(); + } + return 0; // fell through everything, this is bad/incomplete from perl +} + +uint16 EntityList::CreateDoor(const char *model, float x, float y, float z, float heading, uint8 opentype, uint16 size) +{ + if(model) + { + Doors* door = new Doors(model,x,y,z,heading,opentype, size); + RemoveAllDoors(); + zone->LoadZoneDoors(zone->GetShortName(), zone->GetInstanceVersion()); + entity_list.AddDoor(door); + entity_list.RespawnAllDoors(); + + if(door) + return door->GetEntityID(); + } + return 0; // fell through everything, this is bad/incomplete from perl +} + + +Mob* EntityList::GetTargetForMez(Mob* caster) +{ + if(!caster) + return NULL; + + LinkedListIterator iterator(mob_list); + iterator.Reset(); + //TODO: make this smarter and not mez targets being damaged by dots + while(iterator.MoreElements()) { + Mob* d = iterator.GetData(); + if(d){ + if(d == caster){ //caster can't pick himself + iterator.Advance(); + continue; + } + + if(caster->GetTarget() == d){ //caster can't pick his target + iterator.Advance(); + continue; + } + + if(!caster->CheckAggro(d)){ //caster can't pick targets that aren't aggroed on himself + iterator.Advance(); + continue; + } + + if(caster->DistNoRoot(*d) > 22250){ //only pick targets within 150 range + iterator.Advance(); + continue; + } + + if(!caster->CheckLosFN(d)){ //this is wasteful but can't really think of another way to do it + iterator.Advance(); //that wont have us trying to los the same target every time + continue; //it's only in combat so it's impact should be minimal.. but stil. + } + return d; + } + iterator.Advance(); + } + return NULL; +} + +void EntityList::SendZoneAppearance(Client *c) +{ + if(!c) + return; + + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) { + Mob *cur = iterator.GetData(); + + if(cur) + { + if(cur == c) + { + iterator.Advance(); + continue; + } + if(cur->GetAppearance() != eaStanding) + { + cur->SendAppearancePacket(AT_Anim, cur->GetAppearanceValue(cur->GetAppearance()), false, true, c); + } + if(cur->GetSize() != cur->GetBaseSize()) + { + cur->SendAppearancePacket(AT_Size, (uint32)cur->GetSize(), false, true, c); + } + } + iterator.Advance(); + } +} + +void EntityList::SendNimbusEffects(Client *c) +{ + if(!c) + return; + + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) { + Mob *cur = iterator.GetData(); + + if(cur) + { + if(cur == c) + { + iterator.Advance(); + continue; + } + if(cur->GetNimbusEffect1() != 0) + { + cur->SendSpellEffect(cur->GetNimbusEffect1(), 1000, 0, 1, 3000, false, c); + } + if(cur->GetNimbusEffect2() != 0) + { + cur->SendSpellEffect(cur->GetNimbusEffect2(), 2000, 0, 1, 3000, false, c); + } + if(cur->GetNimbusEffect3() != 0) + { + cur->SendSpellEffect(cur->GetNimbusEffect3(), 3000, 0, 1, 3000, false, c); + } + } + iterator.Advance(); + } +} + +void EntityList::SendUntargetable(Client *c) +{ + if(!c) + return; + + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) { + Mob *cur = iterator.GetData(); + + if(cur) + { + if(cur == c) + { + iterator.Advance(); + continue; + } + if(!cur->IsTargetable()) { + cur->SendTargetable(false, c); + } + } + iterator.Advance(); + } +} + +void EntityList::ZoneWho(Client *c, Who_All_Struct* Who) { + + // This is only called for SoF clients, as regular /who is now handled server-side for that client. + // + uint32 PacketLength = 0; + + uint32 Entries = 0; + + uint8 WhomLength = strlen(Who->whom); + + LinkedListIterator iterator(client_list); + + iterator.Reset(); + + while(iterator.MoreElements()) { + + Client *ClientEntry = iterator.GetData(); + + iterator.Advance(); + + if(ClientEntry) { + + if(ClientEntry->GMHideMe(c)) + continue; + + if((Who->wrace != 0xFFFFFFFF) && (ClientEntry->GetRace() != Who->wrace)) + continue; + + if((Who->wclass != 0xFFFFFFFF) && (ClientEntry->GetClass() != Who->wclass)) + continue; + + if((Who->lvllow != 0xFFFFFFFF) && (ClientEntry->GetLevel() < Who->lvllow)) + continue; + + if((Who->lvlhigh != 0xFFFFFFFF) && (ClientEntry->GetLevel() > Who->lvlhigh)) + continue; + + if(Who->guildid != 0xFFFFFFFF) { + + if((Who->guildid == 0xFFFFFFFC) && !ClientEntry->IsTrader()) + continue; + + if((Who->guildid == 0xFFFFFFFB) && !ClientEntry->IsBuyer()) + continue; + + if(Who->guildid != ClientEntry->GuildID()) + continue; + } + + if(WhomLength && strncasecmp(Who->whom, ClientEntry->GetName(), WhomLength) && + strncasecmp(guild_mgr.GetGuildName(ClientEntry->GuildID()), Who->whom, WhomLength)) + continue; + + Entries++; + + PacketLength = PacketLength + strlen(ClientEntry->GetName()); + + if(strlen(guild_mgr.GetGuildName(ClientEntry->GuildID())) > 0) + PacketLength = PacketLength + strlen(guild_mgr.GetGuildName(ClientEntry->GuildID())) + 2; + } + } + + PacketLength = PacketLength + sizeof(WhoAllReturnStruct) + (47 * Entries); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_WhoAllResponse, PacketLength); + + char *Buffer = (char *)outapp->pBuffer; + + WhoAllReturnStruct *WARS = (WhoAllReturnStruct *)Buffer; + + WARS->id = 0; + + WARS->playerineqstring = 5001; + + strncpy(WARS->line, "---------------------------", sizeof(WARS->line)); + + WARS->unknown35 = 0x0a; + + WARS->unknown36 = 0; + + switch(Entries) { + case 0: + WARS->playersinzonestring = 5029; + break; + case 1: + WARS->playersinzonestring = 5028; // 5028 There is %1 player in EverQuest. + break; + default: + WARS->playersinzonestring = 5036; // 5036 There are %1 players in EverQuest. + } + + WARS->unknown44[0] = 0; + + WARS->unknown44[1] = 0; + + WARS->unknown52 = Entries; + + WARS->unknown56 = Entries; + + WARS->playercount = Entries; + + Buffer += sizeof(WhoAllReturnStruct); + + iterator.Reset(); + + while(iterator.MoreElements()) { + + Client *ClientEntry = iterator.GetData(); + + iterator.Advance(); + + if(ClientEntry) { + + if(ClientEntry->GMHideMe(c)) continue; + + if((Who->wrace != 0xFFFFFFFF) && (ClientEntry->GetRace() != Who->wrace)) + continue; + + if((Who->wclass != 0xFFFFFFFF) && (ClientEntry->GetClass() != Who->wclass)) + continue; + + if((Who->lvllow != 0xFFFFFFFF) && (ClientEntry->GetLevel() < Who->lvllow)) + continue; + + if((Who->lvlhigh != 0xFFFFFFFF) && (ClientEntry->GetLevel() > Who->lvlhigh)) + continue; + + if(Who->guildid != 0xFFFFFFFF) { + + if((Who->guildid == 0xFFFFFFFC) && !ClientEntry->IsTrader()) + continue; + + if((Who->guildid == 0xFFFFFFFB) && !ClientEntry->IsBuyer()) + continue; + + if(Who->guildid != ClientEntry->GuildID()) + continue; + } + + if(WhomLength && strncasecmp(Who->whom, ClientEntry->GetName(), WhomLength) && + strncasecmp(guild_mgr.GetGuildName(ClientEntry->GuildID()), Who->whom, WhomLength)) + continue; + + string GuildName; + + if((ClientEntry->GuildID() != GUILD_NONE) && (ClientEntry->GuildID() > 0)) { + + GuildName = "<"; + + GuildName += guild_mgr.GetGuildName(ClientEntry->GuildID()); + + GuildName += ">"; + } + + uint32 FormatMSGID=5025; // 5025 %T1[%2 %3] %4 (%5) %6 %7 %8 %9 + + if(ClientEntry->GetAnon() == 1) + FormatMSGID = 5024; // 5024 %T1[ANONYMOUS] %2 %3 + else if(ClientEntry->GetAnon() == 2) + FormatMSGID = 5023; // 5023 %T1[ANONYMOUS] %2 %3 %4 + + uint32 PlayerClass = 0; + + uint32 PlayerLevel = 0; + + uint32 PlayerRace = 0; + + uint32 ZoneMSGID = 0xFFFFFFFF; + + if(ClientEntry->GetAnon()==0) { + + PlayerClass = ClientEntry->GetClass(); + + PlayerLevel = ClientEntry->GetLevel(); + + PlayerRace = ClientEntry->GetRace(); + } + + WhoAllPlayerPart1* WAPP1 = (WhoAllPlayerPart1*)Buffer; + + WAPP1->FormatMSGID = FormatMSGID; + + WAPP1->PIDMSGID = 0xFFFFFFFF; + + strcpy(WAPP1->Name, ClientEntry->GetName()); + + Buffer += sizeof(WhoAllPlayerPart1) + strlen(WAPP1->Name); + + WhoAllPlayerPart2* WAPP2 = (WhoAllPlayerPart2*)Buffer; + + if(ClientEntry->IsTrader()) + WAPP2->RankMSGID = 12315; + else if(ClientEntry->IsBuyer()) + WAPP2->RankMSGID = 6056; + else if(ClientEntry->Admin() >= 10) + WAPP2->RankMSGID = 12312; + else + WAPP2->RankMSGID = 0xFFFFFFFF; + + strcpy(WAPP2->Guild, GuildName.c_str()); + + Buffer += sizeof(WhoAllPlayerPart2) + strlen(WAPP2->Guild); + + WhoAllPlayerPart3* WAPP3 = (WhoAllPlayerPart3*)Buffer; + + WAPP3->Unknown80[0] = 0xFFFFFFFF; + + if(ClientEntry->IsLD()) + WAPP3->Unknown80[1] = 12313; // LinkDead + else + WAPP3->Unknown80[1] = 0xFFFFFFFF; + + WAPP3->ZoneMSGID = ZoneMSGID; + + WAPP3->Zone = 0; + + WAPP3->Class_ = PlayerClass; + + WAPP3->Level = PlayerLevel; + + WAPP3->Race = PlayerRace; + + WAPP3->Account[0] = 0; + + Buffer += sizeof(WhoAllPlayerPart3); + + WhoAllPlayerPart4* WAPP4 = (WhoAllPlayerPart4*)Buffer; + + WAPP4->Unknown100 = 0; + + Buffer += sizeof(WhoAllPlayerPart4); + } + + } + + c->QueuePacket(outapp); + + safe_delete(outapp); +} + +void EntityList::UnMarkNPC(uint16 ID) +{ + // Designed to be called from the Mob destructor, this method calls Group::UnMarkNPC for + // each group to remove the dead mobs entity ID from the groups list of NPCs marked via the + // Group Leadership AA Mark NPC ability. + // + LinkedListIterator iterator(client_list); + + iterator.Reset(); + + while(iterator.MoreElements()) + { + if(iterator.GetData()) + { + Group *g = NULL; + + g = iterator.GetData()->GetGroup(); + + if(g) + g->UnMarkNPC(ID); + } + iterator.Advance(); + } +} + +uint32 EntityList::CheckNPCsClose(Mob *center) +{ + LinkedListIterator iterator(npc_list); + uint32 count = 0; + + iterator.Reset(); + while(iterator.MoreElements()) + { + NPC *current = iterator.GetData(); + if(!current) + { + iterator.Advance(); + continue; + } + + if(current == center) + { + iterator.Advance(); + continue; + } + + if(current->IsPet()) + { + iterator.Advance(); + continue; + } + + if(current->GetClass() == LDON_TREASURE) + { + iterator.Advance(); + continue; + } + + if(current->GetBodyType() == BT_NoTarget || + current->GetBodyType() == BT_Special) + { + iterator.Advance(); + continue; + } + + float xDiff = current->GetX() - center->GetX(); + float yDiff = current->GetY() - center->GetY(); + float zDiff = current->GetZ() - center->GetZ(); + float dist = ((xDiff * xDiff) + (yDiff * yDiff) + (zDiff * zDiff)); + + if(dist <= RuleR(Adventure, DistanceForRescueAccept)) + { + count++; + } + iterator.Advance(); + } + return count; +} + +void EntityList::GateAllClients() +{ + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Client *c = iterator.GetData(); + if(c) + { + c->GoToBind(); + } + iterator.Advance(); + } +} + +void EntityList::SignalAllClients(uint32 data) +{ + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Client *ent = iterator.GetData(); + if(ent) + { + ent->Signal(data); + } + iterator.Advance(); + } +} + +void EntityList::GetMobList(list &m_list) +{ + m_list.clear(); + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Mob *ent = iterator.GetData(); + m_list.push_back(ent); + iterator.Advance(); + } +} + +void EntityList::GetNPCList(list &n_list) +{ + n_list.clear(); + LinkedListIterator iterator(npc_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + NPC *ent = iterator.GetData(); + n_list.push_back(ent); + iterator.Advance(); + } +} + +void EntityList::GetClientList(list &c_list) +{ + c_list.clear(); + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Client *ent = iterator.GetData(); + c_list.push_back(ent); + iterator.Advance(); + } +} + +void EntityList::GetCorpseList(list &c_list) +{ + c_list.clear(); + LinkedListIterator iterator(corpse_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Corpse *ent = iterator.GetData(); + c_list.push_back(ent); + iterator.Advance(); + } +} + +void EntityList::GetObjectList(list &o_list) +{ + o_list.clear(); + LinkedListIterator iterator(object_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Object *ent = iterator.GetData(); + o_list.push_back(ent); + iterator.Advance(); + } +} + +void EntityList::GetDoorsList(list &o_list) +{ + o_list.clear(); + LinkedListIterator iterator(door_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Doors *ent = iterator.GetData(); + o_list.push_back(ent); + iterator.Advance(); + } +} + +void EntityList::UpdateQGlobal(uint32 qid, QGlobal newGlobal) +{ + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Mob *ent = iterator.GetData(); + + if(ent->IsClient()) + { + QGlobalCache *qgc = ent->CastToClient()->GetQGlobals(); + if(qgc) + { + uint32 char_id = ent->CastToClient()->CharacterID(); + if(newGlobal.char_id == char_id && newGlobal.npc_id == 0) + { + qgc->AddGlobal(qid, newGlobal); + } + } + } + else if(ent->IsNPC()) + { + QGlobalCache *qgc = ent->CastToNPC()->GetQGlobals(); + if(qgc) + { + uint32 npc_id = ent->GetNPCTypeID(); + if(newGlobal.npc_id == npc_id) + { + qgc->AddGlobal(qid, newGlobal); + } + } + } + + iterator.Advance(); + } +} + +void EntityList::DeleteQGlobal(std::string name, uint32 npcID, uint32 charID, uint32 zoneID) +{ + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Mob *ent = iterator.GetData(); + + if(ent->IsClient()) + { + QGlobalCache *qgc = ent->CastToClient()->GetQGlobals(); + if(qgc) + { + qgc->RemoveGlobal(name, npcID, charID, zoneID); + } + } + else if(ent->IsNPC()) + { + QGlobalCache *qgc = ent->CastToNPC()->GetQGlobals(); + if(qgc) + { + qgc->RemoveGlobal(name, npcID, charID, zoneID); + } + } + + iterator.Advance(); + } +} + +void EntityList::SendFindableNPCList(Client *c) +{ + if(!c) + return; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendFindableNPCs, sizeof(FindableNPC_Struct)); + + FindableNPC_Struct *fnpcs = (FindableNPC_Struct *)outapp->pBuffer; + + fnpcs->Unknown109 = 0x16; + fnpcs->Unknown110 = 0x06; + fnpcs->Unknown111 = 0x24; + + fnpcs->Action = 0; + + + LinkedListIterator iterator(npc_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()) + { + NPC *n = iterator.GetData(); + + if(n->IsFindable()) + { + fnpcs->EntityID = n->GetID(); + strn0cpy(fnpcs->Name, n->GetCleanName(), sizeof(fnpcs->Name)); + strn0cpy(fnpcs->LastName, n->GetLastName(), sizeof(fnpcs->LastName)); + fnpcs->Race = n->GetRace(); + fnpcs->Class = n->GetClass(); + + c->QueuePacket(outapp); + } + + } + iterator.Advance(); + } + safe_delete(outapp); +} + +void EntityList::UpdateFindableNPCState(NPC *n, bool Remove) +{ + if(!n || !n->IsFindable()) + return; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendFindableNPCs, sizeof(FindableNPC_Struct)); + + FindableNPC_Struct *fnpcs = (FindableNPC_Struct *)outapp->pBuffer; + + fnpcs->Unknown109 = 0x16; + fnpcs->Unknown110 = 0x06; + fnpcs->Unknown111 = 0x24; + + fnpcs->Action = Remove ? 1: 0; + + fnpcs->EntityID = n->GetID(); + + strn0cpy(fnpcs->Name, n->GetCleanName(), sizeof(fnpcs->Name)); + + strn0cpy(fnpcs->LastName, n->GetLastName(), sizeof(fnpcs->LastName)); + + fnpcs->Race = n->GetRace(); + + fnpcs->Class = n->GetClass(); + + LinkedListIterator iterator(client_list); + + iterator.Reset(); + + while(iterator.MoreElements()) + { + Client *c = iterator.GetData(); + if(c && (c->GetClientVersion() >= EQClientSoD)) + c->QueuePacket(outapp); + + iterator.Advance(); + } + + safe_delete(outapp); +} + +void EntityList::HideCorpses(Client *c, uint8 CurrentMode, uint8 NewMode) +{ + if(!c) + return; + + if(NewMode == HideCorpseNone) + { + SendZoneCorpses(c); + return; + } + + Group *g = NULL; + + if(NewMode == HideCorpseAllButGroup) + { + g = c->GetGroup(); + + if(!g) + NewMode = HideCorpseAll; + } + + LinkedListIterator iterator(corpse_list); + + iterator.Reset(); + + while(iterator.MoreElements()) + { + Corpse *b = iterator.GetData(); + + if(b && (b->GetCharID() != c->CharacterID())) + { + if((NewMode == HideCorpseAll) || ((NewMode == HideCorpseNPC) && (b->IsNPCCorpse()))) + { + EQApplicationPacket outapp; + b->CreateDespawnPacket(&outapp, false); + c->QueuePacket(&outapp); + } + else if(NewMode == HideCorpseAllButGroup) + { + if(!g->IsGroupMember(b->GetOwnerName())) + { + EQApplicationPacket outapp; + b->CreateDespawnPacket(&outapp, false); + c->QueuePacket(&outapp); + } + else if((CurrentMode == HideCorpseAll)) + { + EQApplicationPacket outapp; + b->CreateSpawnPacket(&outapp); + c->QueuePacket(&outapp); + } + } + + } + iterator.Advance(); + } +} + +void EntityList::AddLootToNPCS(uint32 item_id, uint32 count) +{ + if(count == 0) + return; + + int npc_count = 0; + LinkedListIterator iterator(npc_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(!iterator.GetData()->IsPet() + && iterator.GetData()->GetClass() != LDON_TREASURE + && iterator.GetData()->GetBodyType() != BT_NoTarget + && iterator.GetData()->GetBodyType() != BT_NoTarget2 + && iterator.GetData()->GetBodyType() != BT_Special) + { + npc_count++; + } + iterator.Advance(); + } + + if(npc_count == 0) + { + return; + } + + NPC **npcs = new NPC*[npc_count]; + int *counts = new int[npc_count]; + bool *marked = new bool[npc_count]; + memset(counts, 0, sizeof(int) * npc_count); + memset(marked, 0, sizeof(bool) * npc_count); + + int i = 0; + iterator.Reset(); + while(iterator.MoreElements()) + { + if(!iterator.GetData()->IsPet() + && iterator.GetData()->GetClass() != LDON_TREASURE + && iterator.GetData()->GetBodyType() != BT_NoTarget + && iterator.GetData()->GetBodyType() != BT_NoTarget2 + && iterator.GetData()->GetBodyType() != BT_Special) + { + npcs[i++] = iterator.GetData(); + } + iterator.Advance(); + } + + while(count > 0) + { + vector selection; + selection.reserve(npc_count); + for(int j = 0; j < npc_count; ++j) + { + selection.push_back(j); + } + + while(selection.size() > 0 && count > 0) + { + int k = MakeRandomInt(0, selection.size() - 1); + counts[selection[k]]++; + count--; + selection.erase(selection.begin() + k); + } + } + + for(int j = 0; j < npc_count; ++j) + { + if(counts[j] > 0) + { + for(int k = 0; k < counts[j]; ++k) + { + npcs[j]->AddItem(item_id, 1); + } + } + } + + safe_delete_array(npcs); + safe_delete_array(counts); + safe_delete_array(marked); +} + +void EntityList::CameraEffect(uint32 duration, uint32 intensity) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_CameraEffect, sizeof(Camera_Struct)); + memset(outapp->pBuffer, 0, sizeof(outapp->pBuffer)); + Camera_Struct* cs = (Camera_Struct*) outapp->pBuffer; + cs->duration = duration; // Duration in milliseconds + cs->intensity = ((intensity * 6710886) + 1023410176); // Intensity ranges from 1023410176 to 1090519040, so simplify it from 0 to 10. + entity_list.QueueClients(0, outapp); + safe_delete(outapp); +} + + +NPC* EntityList::GetClosestBanker(Mob* sender, uint32 &distance) +{ + if(!sender) + return NULL; + + distance = 4294967295; + NPC* nc = NULL; + + LinkedListIterator iterator(npc_list); + iterator.Reset(); + + while(iterator.MoreElements()) + { + if(iterator.GetData()->GetClass() == BANKER) + { + uint32 nd = ((iterator.GetData()->GetY() - sender->GetY()) * (iterator.GetData()->GetY() - sender->GetY())) + + ((iterator.GetData()->GetX() - sender->GetX()) * (iterator.GetData()->GetX() - sender->GetX())); + if(nd < distance){ + distance = nd; + nc = iterator.GetData(); + } + } + iterator.Advance(); + } + return nc; +} + +void EntityList::ExpeditionWarning(uint32 minutes_left) +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_DzExpeditionEndsWarning, sizeof(ExpeditionExpireWarning)); + ExpeditionExpireWarning *ew = (ExpeditionExpireWarning*)outapp->pBuffer; + ew->minutes_remaining = minutes_left; + + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + iterator.GetData()->Message_StringID(15, 3551, itoa((int)minutes_left)); + iterator.GetData()->QueuePacket(outapp); + iterator.Advance(); + } +} + +Mob* EntityList::GetClosestMobByBodyType(Mob* sender, bodyType BodyType) +{ + + if(!sender) + return NULL; + + uint32 CurrentDistance, ClosestDistance = 4294967295u; + + Mob *CurrentMob, *ClosestMob = NULL; + + LinkedListIterator iterator(mob_list); + + iterator.Reset(); + + while(iterator.MoreElements()) + { + CurrentMob = iterator.GetData(); + + iterator.Advance(); + + if(CurrentMob->GetBodyType() != BodyType) + continue; + + CurrentDistance = ((CurrentMob->GetY() - sender->GetY()) * (CurrentMob->GetY() - sender->GetY())) + + ((CurrentMob->GetX() - sender->GetX()) * (CurrentMob->GetX() - sender->GetX())); + + if(CurrentDistance < ClosestDistance) + { + ClosestDistance = CurrentDistance; + + ClosestMob = CurrentMob; + + } + } + return ClosestMob; +} + +void EntityList::GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height, list &m_list) +{ + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Mob *ptr = iterator.GetData(); + if(ptr == start) + { + iterator.Advance(); + continue; + } + int32 x_diff = ptr->GetX() - start->GetX(); + int32 y_diff = ptr->GetY() - start->GetY(); + int32 z_diff = ptr->GetZ() - start->GetZ(); + + x_diff *= x_diff; + y_diff *= y_diff; + z_diff *= z_diff; + + if((x_diff + y_diff) <= (radius * radius)) + { + if(z_diff <= (height * height)) + { + m_list.push_back(ptr); + } + } + + iterator.Advance(); + } +} + +Client* EntityList::FindCorpseDragger(const char *CorpseName) +{ + LinkedListIterator iterator(client_list); + + iterator.Reset(); + + while(iterator.MoreElements()) + { + if (iterator.GetData()->IsDraggingCorpse(CorpseName)) + { + return iterator.GetData(); + } + iterator.Advance(); + } + return 0; +} + +Mob* EntityList::GetTargetForVirus(Mob* spreader) +{ + int max_spread_range = RuleI(Spells, VirusSpreadDistance); + + vector TargetsInRange; + LinkedListIterator iterator(mob_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + // Make sure the target is in range, has los and is not the mob doing the spreading + if ((iterator.GetData()->GetID() != spreader->GetID()) && + (iterator.GetData()->CalculateDistance(spreader->GetX(), spreader->GetY(), spreader->GetZ()) <= max_spread_range) && + (spreader->CheckLosFN(iterator.GetData()))) + { + // If the spreader is an npc it can only spread to other npc controlled mobs + if (spreader->IsNPC() && !spreader->IsPet() && iterator.GetData()->IsNPC()) { + TargetsInRange.push_back(iterator.GetData()); + } + // 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(iterator.GetData()->IsNPC() && !iterator.GetData()->IsPet()) { + TargetsInRange.push_back(iterator.GetData()); + } + else if(iterator.GetData()->IsNPC() && iterator.GetData()->IsPet() && iterator.GetData()->GetOwner()->IsNPC()) { + TargetsInRange.push_back(iterator.GetData()); + } + } + // if the spreader is anything else(bot, pet, etc) then it should spread to everything but non client controlled npcs + else if(!spreader->IsNPC() && !iterator.GetData()->IsNPC()) { + TargetsInRange.push_back(iterator.GetData()); + } + // 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(!iterator.GetData()->IsNPC()) { + TargetsInRange.push_back(iterator.GetData()); + } + else if (iterator.GetData()->IsNPC() && iterator.GetData()->IsPet() && !iterator.GetData()->GetOwner()->IsNPC()) { + TargetsInRange.push_back(iterator.GetData()); + } + } + } + iterator.Advance(); + } + + if(TargetsInRange.size() == 0) + return NULL; + + return TargetsInRange[MakeRandomInt(0, TargetsInRange.size() - 1)]; +} diff --git a/zone/entity.h b/zone/entity.h new file mode 100644 index 000000000..5b9af3884 --- /dev/null +++ b/zone/entity.h @@ -0,0 +1,467 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 ENTITY_H +#define ENTITY_H + +#include "../common/types.h" +#include "../common/linked_list.h" +#include "zonedb.h" +#include "../common/eq_constants.h" +#include "zonedump.h" +#include "zonedbasync.h" +#include "../common/servertalk.h" +#include "../common/bodytypes.h" +#include "QGlobals.h" + +// max number of newspawns to send per bulk packet +#define SPAWNS_PER_POINT_DATARATE 10 +#define MAX_SPAWNS_PER_PACKET 100 + +//#ifdef _WINDOWS + class EQApplicationPacket; +//#else +// struct EQApplicationPacket; +//#endif + +class Client; +class Mob; +class NPC; +class Merc; +class Corpse; +class Beacon; +class Petition; +class Object; +class Group; +class Raid; +class Doors; +class Trap; +class Entity; +class EntityList; + +#ifdef BOTS +class Bot; +class BotRaids; +#endif + +extern EntityList entity_list; + +void ProcessClientThreadSpawn(void *tmp); + +class Entity +{ +public: + Entity(); + virtual ~Entity(); + + virtual bool IsClient() const { return false; } + virtual bool IsNPC() const { return false; } + virtual bool IsMob() const { return false; } + virtual bool IsMerc() const { return false; } + virtual bool IsCorpse() const { return false; } + virtual bool IsPlayerCorpse() const { return false; } + virtual bool IsNPCCorpse() const { return false; } + virtual bool IsObject() const { return false; } +// virtual bool IsGroup() const { return false; } + virtual bool IsDoor() const { return false; } + virtual bool IsTrap() const { return false; } + virtual bool IsBeacon() const { return false; } + + virtual bool Process() { return false; } + virtual bool Save() { return true; } + virtual void Depop(bool StartSpawnTimer = false) {} + + Client* CastToClient(); + NPC* CastToNPC(); + Mob* CastToMob(); + Merc* CastToMerc(); + Corpse* CastToCorpse(); + Object* CastToObject(); + Doors* CastToDoors(); + Trap* CastToTrap(); + Beacon* CastToBeacon(); + + const Client* CastToClient() const; + const NPC* CastToNPC() const; + const Mob* CastToMob() const; + const Merc* CastToMerc() const; + const Corpse* CastToCorpse() const; + const Object* CastToObject() const; +// const Group* CastToGroup() const; + const Doors* CastToDoors() const; + const Trap* CastToTrap() const; + const Beacon* CastToBeacon() const; + + inline const uint16& GetID() const{ return id; } + virtual const char* GetName() { return ""; } + virtual void DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw) { pDBAsyncWorkID = 0; } + bool CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z, float trg_x, float trg_y, float trg_z, float perwalk=1); + +#ifdef BOTS + virtual bool IsBot() const { return false; } + Bot* CastToBot(); +#endif + +protected: + friend class EntityList; + virtual void SetID(uint16 set_id); + uint32 pDBAsyncWorkID; +private: + uint16 id; +}; + +class EntityList +{ +public: + EntityList(); + ~EntityList(); + + Entity* GetID(uint16 id); + Mob* GetMob(uint16 id); + inline Mob* GetMobID(uint16 id) { return(GetMob(id)); } //for perl + Mob* GetMob(const char* name); + Mob* GetMobByNpcTypeID(uint32 get_id); + Mob* GetTargetForVirus(Mob* spreader); + NPC* GetNPCByID(uint16 id); + NPC* GetNPCByNPCTypeID(uint32 npc_id); + Merc* GetMercByID(uint16 id); + Client* GetClientByName(const char *name); + Client* GetClientByAccID(uint32 accid); + Client* GetClientByID(uint16 id); + Client* GetClientByCharID(uint32 iCharID); + Client* GetClientByWID(uint32 iWID); + Client* GetClient(uint32 ip, uint16 port); + Client* GetRandomClient(float x, float y, float z, float Distance, Client *ExcludeClient = NULL); + Group* GetGroupByMob(Mob* mob); + Group* GetGroupByClient(Client* client); + Group* GetGroupByID(uint32 id); + Group* GetGroupByLeaderName(char* leader); + Raid* GetRaidByMob(Mob* mob); + Raid* GetRaidByClient(Client* client); + Raid* GetRaidByID(uint32 id); + Raid* GetRaidByLeaderName(const char *leader); + + Corpse* GetCorpseByOwner(Client* client); + Corpse* GetCorpseByID(uint16 id); + Corpse* GetCorpseByDBID(uint32 dbid); + Corpse* GetCorpseByName(const char* name); + + Client* FindCorpseDragger(const char *CorpseName); + + Object* GetObjectByID(uint16 id); + Object* GetObjectByDBID(uint32 id); + Doors* GetDoorsByID(uint16 id); + Doors* GetDoorsByDoorID(uint32 id); + Doors* GetDoorsByDBID(uint32 id); + void RemoveAllCorpsesByCharID(uint32 charid); + void RemoveCorpseByDBID(uint32 dbid); + int RezzAllCorpsesByCharID(uint32 charid); + bool IsMobInZone(Mob *who); + void ClearClientPetitionQueue(); + bool CanAddHateForMob(Mob *p); + void SendGuildMOTD(uint32 guild_id); + void SendGuildSpawnAppearance(uint32 guild_id); + void SendGuildMembers(uint32 guild_id); + void RefreshAllGuildInfo(uint32 guild_id); + void SendGuildList(); +// void SendGuildJoin(GuildJoin_Struct* gj); + // Check group list for NULL entries + void CheckGroupList (const char *fname, const int fline); + void GroupProcess(); + void RaidProcess(); + void DoorProcess(); + void ObjectProcess(); + void CorpseProcess(); + void MobProcess(); + void TrapProcess(); + void BeaconProcess(); + void ProcessMove(Client *c, float x, float y, float z); + void ProcessProximitySay(const char *Message, Client *c, uint8 language = 0); + void SendAATimer(uint32 charid,UseAA_Struct* uaa); + Doors* FindDoor(uint8 door_id); + Object* FindObject(uint32 object_id); + Object* FindNearbyObject(float x, float y, float z, float radius); + bool MakeDoorSpawnPacket(EQApplicationPacket* app, Client *client); + bool MakeTrackPacket(Client* client); + void SendTraders(Client* client); + void AddClient(Client*); + void AddNPC(NPC*, bool SendSpawnPacket = true, bool dontqueue = false); + void AddMerc(Merc*, bool SendSpawnPacket = true, bool dontqueue = false); + void AddCorpse(Corpse* pc, uint32 in_id = 0xFFFFFFFF); + void AddObject(Object*, bool SendSpawnPacket = true); + void AddGroup(Group*); + void AddGroup(Group*, uint32 id); + void AddRaid(Raid *raid); + void AddRaid(Raid*, uint32 id); + void AddDoor(Doors* door); + void AddTrap(Trap* trap); + void AddBeacon(Beacon *beacon); + void AddProximity(NPC *proximity_for); + void Clear(); + bool RemoveMob(uint16 delete_id); + bool RemoveMob(Mob* delete_mob); + bool RemoveClient(uint16 delete_id); + bool RemoveClient(Client* delete_client); + bool RemoveNPC(uint16 delete_id); + bool RemoveMerc(uint16 delete_id); + bool RemoveGroup(uint32 delete_id); + bool RemoveRaid(uint32 delete_id); + bool RemoveCorpse(uint16 delete_id); + bool RemoveDoor(uint16 delete_id); + bool RemoveTrap(uint16 delete_id); + bool RemoveObject(uint16 delete_id); + bool RemoveProximity(uint16 delete_npc_id); + void RemoveAllMobs(); + void RemoveAllClients(); + void RemoveAllNPCs(); + void RemoveAllMercs(); + void RemoveAllGroups(); + void RemoveAllCorpses(); + void RemoveAllDoors(); + void DespawnAllDoors(); + void RespawnAllDoors(); + void RemoveAllTraps(); + void RemoveAllObjects(); + void RemoveAllLocalities(); + void RemoveAllRaids(); + void DestroyTempPets(Mob *owner); + Entity* GetEntityMob(uint16 id); + Entity* GetEntityMob(const char *name); + Entity* GetEntityMerc(uint16 id); + Entity* GetEntityDoor(uint16 id); + Entity* GetEntityObject(uint16 id); + Entity* GetEntityCorpse(uint16 id); + Entity* GetEntityCorpse(const char *name); +// Entity* GetEntityGroup(uint32 id); + Entity* GetEntityTrap(uint16 id); + Entity* GetEntityBeacon(uint16 id); + + void DescribeAggro(Client *towho, NPC *from_who, float dist, bool verbose); + + void Message(uint32 to_guilddbid, uint32 type, const char* message, ...); + void MessageStatus(uint32 to_guilddbid, int to_minstatus, uint32 type, const char* message, ...); + void MessageClose(Mob* sender, bool skipsender, float dist, uint32 type, const char* message, ...); + void Message_StringID(Mob *sender, bool skipsender, uint32 type, uint32 string_id, const char* message1=0,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); + void MessageClose_StringID(Mob *sender, bool skipsender, float dist, uint32 type, uint32 string_id, const char* message1=0,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); + void ChannelMessageFromWorld(const char* from, const char* to, uint8 chan_num, uint32 guilddbid, uint8 language, const char* message); + void ChannelMessage(Mob* from, uint8 chan_num, uint8 language, const char* message, ...); + void ChannelMessage(Mob* from, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...); + void ChannelMessageSend(Mob* to, uint8 chan_num, uint8 language, const char* message, ...); + void SendZoneSpawns(Client*); + void SendZonePVPUpdates(Client *); + void SendZoneSpawnsBulk(Client* client); + void Save(); + void SendZoneCorpses(Client*); + void SendZoneCorpsesBulk(Client*); + void SendZoneObjects(Client* client); + void SendZoneAppearance(Client *c); + void SendNimbusEffects(Client *c); + void SendUntargetable(Client *c); + void DuelMessage(Mob* winner, Mob* loser, bool flee); + void QuestJournalledSayClose(Mob *sender, Client *QuestIntiator, float dist, const char* mobname, const char* message); + void GroupMessage(uint32 gid, const char *from, const char *message); + void ExpeditionWarning(uint32 minutes_left); + + void RemoveFromTargets(Mob* mob, bool RemoveFromXTargets = false); + void RemoveFromXTargets(Mob* mob); + void RemoveFromAutoXTargets(Mob* mob); + void ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget); + void QueueCloseClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, float dist=200, Mob* SkipThisMob = 0, bool ackreq = true,eqFilterType filter=FilterNone); + void QueueClients(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true); + void QueueClientsStatus(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint8 minstatus = 0, uint8 maxstatus = 0); + 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); + + void QueueClientsByXTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender = true); + void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app); + void QueueManaged(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true); + + void AEAttack(Mob *attacker, float dist, int Hand = 13, int count = 0, bool IsFromSpell = false); + void AETaunt(Client *caster, float range = 0); + void AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true, int16 resist_adjust = 0); + void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); + void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true); + + void RadialSetLogging(Mob *around, bool enabled, bool clients, bool non_clients, float range = 0); + + //trap stuff + Mob* GetTrapTrigger(Trap* trap); + void SendAlarm(Trap* trap, Mob* currenttarget, uint8 kos); + Trap* FindNearbyTrap(Mob* searcher, float max_dist); + + void AddHealAggro(Mob* target, Mob* caster, uint16 thedam); + Mob* FindDefenseNPC(uint32 npcid); + void OpenDoorsNear(NPC* opener); + void UpdateWho(bool iSendFullUpdate = false); + void SendPositionUpdates(Client* client, uint32 cLastUpdate = 0, float range = 0, Entity* alwayssend = 0, bool iSendEvenIfNotChanged = false); + char* MakeNameUnique(char* name); + static char* RemoveNumbers(char* name); + void SignalMobsByNPCID(uint32 npc_type, int signal_id); + void CountNPC(uint32* NPCCount, uint32* NPCLootCount, uint32* gmspawntype_count); + void DoZoneDump(ZSDump_Spawn2* spawn2dump, ZSDump_NPC* npcdump, ZSDump_NPC_Loot* npclootdump, NPCType* gmspawntype_dump); + void RemoveEntity(uint16 id); + void SendPetitionToAdmins(Petition* pet); + void SendPetitionToAdmins(); + void AddLootToNPCS(uint32 item_id, uint32 count); + + void ListNPCs(Client* client, const char* arg1 = 0, const char* arg2 = 0, uint8 searchtype = 0); + void ListNPCCorpses(Client* client); + void ListPlayerCorpses(Client* client); + void FindPathsToAllNPCs(); + int32 DeleteNPCCorpses(); + int32 DeletePlayerCorpses(); + void WriteEntityIDs(); + void HalveAggro(Mob* who); + void DoubleAggro(Mob* who); + void Evade(Mob *who); + void UpdateHoTT(Mob* target); + + void Process(); + void ClearAggro(Mob* targ); + void ClearFeignAggro(Mob* targ); + void ClearZoneFeignAggro(Client* targ); + void AggroZone(Mob* who, int hate = 0); + + bool Fighting(Mob* targ); + void RemoveFromHateLists(Mob* mob, bool settoone = false); + void RemoveDebuffs(Mob* caster); + + + void MessageGroup(Mob* sender, bool skipclose, uint32 type, const char* message, ...); + + void LimitAddNPC(NPC *npc); + void LimitRemoveNPC(NPC *npc); + bool LimitCheckType(uint32 npc_type, int count); + bool LimitCheckGroup(uint32 spawngroup_id, int count); + bool LimitCheckBoth(uint32 npc_type, uint32 spawngroup_id, int group_count, int type_count); + bool LimitCheckName(const char* npc_name); + + void CheckClientAggro(Client *around); + Mob* AICheckCloseAggro(Mob* sender, float iAggroRange, float iAssistRange); + int GetHatedCount(Mob *attacker, Mob *exclude); + void AIYellForHelp(Mob* sender, Mob* attacker); + bool AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint16 iSpellTypes); + bool Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint16 iSpellTypes); + Mob* GetTargetForMez(Mob* caster); + uint32 CheckNPCsClose(Mob *center); + + Corpse* GetClosestCorpse(Mob* sender, const char *Name); + NPC* GetClosestBanker(Mob* sender, uint32 &distance); + void CameraEffect(uint32 duration, uint32 intensity); + Mob* GetClosestMobByBodyType(Mob* sender, bodyType BodyType); + void ForceGroupUpdate(uint32 gid); + void SendGroupLeave(uint32 gid, const char *name); + void SendGroupJoin(uint32 gid, const char *name); + + void SaveAllClientsTaskState(); + void ReloadAllClientsTaskState(int TaskID=0); + + uint16 CreateGroundObject(uint32 itemid, float x, float y, float z, float heading, uint32 decay_time = 300000); + uint16 CreateGroundObjectFromModel(const char *model, float x, float y, float z, float heading, uint8 type = 0x00, uint32 decay_time = 0); + uint16 CreateDoor(const char *model, float x, float y, float z, float heading, uint8 type = 0, uint16 size = 100); + void ZoneWho(Client *c, Who_All_Struct* Who); + void UnMarkNPC(uint16 ID); + + void GateAllClients(); + void SignalAllClients(uint32 data); + void UpdateQGlobal(uint32 qid, QGlobal newGlobal); + void DeleteQGlobal(std::string name, uint32 npcID, uint32 charID, uint32 zoneID); + void SendFindableNPCList(Client *c); + void UpdateFindableNPCState(NPC *n, bool Remove); + void HideCorpses(Client *c, uint8 CurrentMode, uint8 NewMode); + + void GetMobList(list &m_list); + void GetNPCList(list &n_list); + void GetMercList(list &n_list); + void GetClientList(list &c_list); + void GetCorpseList(list &c_list); + void GetObjectList(list &o_list); + void GetDoorsList(list &d_list); + void GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height, list &m_list); + + void DepopAll(int NPCTypeID, bool StartSpawnTimer = true); + + uint16 GetFreeID(); + void RefreshAutoXTargets(Client *c); + void RefreshClientXTargets(Client *c); + +protected: + friend class Zone; + void Depop(bool StartSpawnTimer = false); + +private: + void AddToSpawnQueue(uint16 entityid, NewSpawn_Struct** app); + void CheckSpawnQueue(); + + //used for limiting spawns + class SpawnLimitRecord { public: uint32 spawngroup_id; uint32 npc_type; }; + map npc_limit_list; //entity id -> npc type + + uint32 tsFirstSpawnOnQueue; // timestamp that the top spawn on the spawnqueue was added, should be 0xFFFFFFFF if queue is empty + uint32 NumSpawnsOnQueue; + LinkedList SpawnQueue; + + LinkedList client_list; + LinkedList mob_list; + LinkedList npc_list; + LinkedList merc_list; + list group_list; + LinkedList corpse_list; + LinkedList object_list; + LinkedList door_list; + LinkedList trap_list; + LinkedList beacon_list; + LinkedList proximity_list; + list raid_list; + uint16 last_insert_id; + + // Please Do Not Declare Any EntityList Class Members After This Comment +#ifdef BOTS + public: + void AddBot(Bot* newBot, bool SendSpawnPacket = true, bool dontqueue = false); + void BotPickLock(Bot* rogue); + bool RemoveBot(uint16 entityID); + Mob* GetMobByBotID(uint32 botID); + Bot* GetBotByBotID(uint32 botID); + Bot* GetBotByBotName(std::string botName); + list GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID); + + bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate + void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff + private: + std::list bot_list; +#endif +}; + +class BulkZoneSpawnPacket { +public: + BulkZoneSpawnPacket(Client* iSendTo, uint32 iMaxSpawnsPerPacket); // 0 = send zonewide + virtual ~BulkZoneSpawnPacket(); + + bool AddSpawn(NewSpawn_Struct* ns); + void SendBuffer(); // Sends the buffer and cleans up everything - can safely re-use the object after this function call (no need to free and do another new) +private: + uint32 pMaxSpawnsPerPacket; + uint32 index; + NewSpawn_Struct* data; + Client* pSendTo; +}; + +#endif + diff --git a/zone/errmsg.h b/zone/errmsg.h new file mode 100644 index 000000000..8087c5269 --- /dev/null +++ b/zone/errmsg.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA */ + +/* Error messages for mysql clients */ +/* error messages for the demon is in share/language/errmsg.sys */ + +#ifdef __cplusplus +extern "C" { +#endif +void init_client_errs(void); +extern const char *client_errors[]; /* Error messages */ +#ifdef __cplusplus +} +#endif + +#define CR_MIN_ERROR 2000 /* For easier client code */ +#define CR_MAX_ERROR 2999 +#if defined(OS2) && defined( MYSQL_SERVER) +#define CER(X) client_errors[(X)-CR_MIN_ERROR] +#else +#define ER(X) client_errors[(X)-CR_MIN_ERROR] +#endif +#define CLIENT_ERRMAP 2 /* Errormap used by my_error() */ + +#define CR_UNKNOWN_ERROR 2000 +#define CR_SOCKET_CREATE_ERROR 2001 +#define CR_CONNECTION_ERROR 2002 +#define CR_CONN_HOST_ERROR 2003 +#define CR_IPSOCK_ERROR 2004 +#define CR_UNKNOWN_HOST 2005 +#define CR_SERVER_GONE_ERROR 2006 +#define CR_VERSION_ERROR 2007 +#define CR_OUT_OF_MEMORY 2008 +#define CR_WRONG_HOST_INFO 2009 +#define CR_LOCALHOST_CONNECTION 2010 +#define CR_TCP_CONNECTION 2011 +#define CR_SERVER_HANDSHAKE_ERR 2012 +#define CR_SERVER_LOST 2013 +#define CR_COMMANDS_OUT_OF_SYNC 2014 +#define CR_NAMEDPIPE_CONNECTION 2015 +#define CR_NAMEDPIPEWAIT_ERROR 2016 +#define CR_NAMEDPIPEOPEN_ERROR 2017 +#define CR_NAMEDPIPESETSTATE_ERROR 2018 +#define CR_CANT_READ_CHARSET 2019 +#define CR_NET_PACKET_TOO_LARGE 2020 diff --git a/zone/event_codes.h b/zone/event_codes.h new file mode 100644 index 000000000..3a2975693 --- /dev/null +++ b/zone/event_codes.h @@ -0,0 +1,68 @@ +#ifndef EVENT_CODES_H +#define EVENT_CODES_H + +typedef enum { + EVENT_SAY = 0, + EVENT_ITEM, //being given an item + EVENT_DEATH, //being killed + EVENT_SPAWN, //triggered when we first spawn + EVENT_ATTACK, //being attacked (resets after an interval of not being attacked) + EVENT_COMBAT, //being attacked or attacking (resets after an interval of not being attacked) + EVENT_AGGRO, //entering combat mode due to a PC attack + EVENT_SLAY, //killing a PC + EVENT_NPC_SLAY, //killing an NPC + EVENT_WAYPOINT_ARRIVE, // reaching a waypoint on a grid + EVENT_WAYPOINT_DEPART, // departing a waypoint on a grid + EVENT_TIMER, + EVENT_SIGNAL, + EVENT_HP, + EVENT_ENTER, //PC entering your set proximity + EVENT_EXIT, //PC leaving your set proximity + EVENT_ENTERZONE, //PC only, you enter zone + EVENT_CLICKDOOR, //pc only, you click a door + EVENT_LOOT, //pc only + EVENT_ZONE, //pc only + EVENT_LEVEL_UP, //pc only + EVENT_KILLED_MERIT, //killed by a PC or group, gave experience; will repeat several times for groups + EVENT_CAST_ON, //pc casted a spell on npc + EVENT_TASKACCEPTED, //pc accepted a task + EVENT_TASK_STAGE_COMPLETE, + EVENT_TASK_UPDATE, + EVENT_TASK_COMPLETE, + EVENT_TASK_FAIL, + EVENT_AGGRO_SAY, + EVENT_PLAYER_PICKUP, + EVENT_POPUPRESPONSE, + EVENT_PROXIMITY_SAY, + EVENT_CAST, + EVENT_SCALE_CALC, + EVENT_ITEM_ENTERZONE, + EVENT_TARGET_CHANGE, //target selected, target changed, or target removed + EVENT_HATE_LIST, + EVENT_SPELL_EFFECT_CLIENT, + EVENT_SPELL_EFFECT_NPC, + EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT, + EVENT_SPELL_EFFECT_BUFF_TIC_NPC, + EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, + EVENT_COMBINE_SUCCESS, //PC successfully combined a recipe + EVENT_COMBINE_FAILURE, //PC failed to combine a recipe + EVENT_ITEM_CLICK, //SoF+ Item Right Clicked from worn or main/top inventory slot + EVENT_ITEM_CLICK_CAST, + EVENT_GROUP_CHANGE, + EVENT_FORAGE_SUCCESS, + EVENT_FORAGE_FAILURE, + EVENT_FISH_START, + EVENT_FISH_SUCCESS, + EVENT_FISH_FAILURE, + EVENT_CLICK_OBJECT, + EVENT_DISCOVER_ITEM, + EVENT_DISCONNECT, + EVENT_CONNECT, + + _LargestEventID +} QuestEventID; + +extern const char *QuestEventSubroutines[_LargestEventID]; + +#endif + diff --git a/zone/exp.cpp b/zone/exp.cpp new file mode 100644 index 000000000..ede539a69 --- /dev/null +++ b/zone/exp.cpp @@ -0,0 +1,668 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 "features.h" +#include "masterentity.h" +#include "StringIDs.h" +#include "../common/MiscFunctions.h" +#include "../common/rulesys.h" +#include "QuestParserCollection.h" + + +static uint32 MaxBankedGroupLeadershipPoints(int Level) +{ + if(Level < 35) + return 4; + + if(Level < 51) + return 6; + + return 8; +} + +static uint32 MaxBankedRaidLeadershipPoints(int Level) +{ + if(Level < 45) + return 6; + + if(Level < 55) + return 8; + + return 10; +} + +void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { + + uint32 add_exp = in_add_exp; + + if(!resexp && (XPRate != 0)) + add_exp = static_cast(in_add_exp * (static_cast(XPRate) / 100.0f)); + + if (m_epp.perAA<0 || m_epp.perAA>100) + m_epp.perAA=0; // stop exploit with sanity check + + uint32 add_aaxp; + if(resexp) { + add_aaxp = 0; + } else { + + //figure out how much of this goes to AAs + add_aaxp = add_exp * m_epp.perAA / 100; + //take that ammount away from regular exp + add_exp -= add_aaxp; + + float totalmod = 1.0; + float zemmod = 1.0; + //get modifiers + if(RuleR(Character, ExpMultiplier) >= 0){ + totalmod *= RuleR(Character, ExpMultiplier); + } + + if(zone->newzone_data.zone_exp_multiplier >= 0){ + zemmod *= zone->newzone_data.zone_exp_multiplier; + } + + if(RuleB(Character,UseRaceClassExpBonuses)) + { + if(GetBaseRace() == HALFLING){ + totalmod *= 1.05; + } + + if(GetClass() == ROGUE || GetClass() == WARRIOR){ + totalmod *= 1.05; + } + } + + if(zone->IsHotzone()) + { + totalmod += RuleR(Zone, HotZoneBonus); + } + + add_exp = uint32(float(add_exp) * totalmod * zemmod); + + if(RuleB(Character,UseXPConScaling)) + { + if (conlevel != 0xFF && !resexp) { + switch (conlevel) + { + case CON_GREEN: + add_exp = 0; + add_aaxp = 0; + return; + case CON_LIGHTBLUE: + add_exp = add_exp * RuleI(Character, LightBlueModifier)/100; + add_aaxp = add_aaxp * RuleI(Character, LightBlueModifier)/100; + break; + case CON_BLUE: + add_exp = add_exp * RuleI(Character, BlueModifier)/100; + add_aaxp = add_aaxp * RuleI(Character, BlueModifier)/100; + break; + case CON_WHITE: + add_exp = add_exp * RuleI(Character, WhiteModifier)/100; + add_aaxp = add_aaxp * RuleI(Character, WhiteModifier)/100; + break; + case CON_YELLOW: + add_exp = add_exp * RuleI(Character, YellowModifier)/100; + add_aaxp = add_aaxp * RuleI(Character, YellowModifier)/100; + break; + case CON_RED: + add_exp = add_exp * RuleI(Character, RedModifier)/100; + add_aaxp = add_aaxp * RuleI(Character, RedModifier)/100; + break; + } + } + } + + 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 + 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); + } + + } + + } //end !resexp + + float aatotalmod = 1.0; + if(zone->newzone_data.zone_exp_multiplier >= 0){ + aatotalmod *= zone->newzone_data.zone_exp_multiplier; + } + + + + if(RuleB(Character,UseRaceClassExpBonuses)) + { + if(GetBaseRace() == HALFLING){ + aatotalmod *= 1.05; + } + + if(GetClass() == ROGUE || GetClass() == WARRIOR){ + aatotalmod *= 1.05; + } + } + + if(RuleB(Zone, LevelBasedEXPMods)){ + if(zone->level_exp_mod[GetLevel()].ExpMod){ + add_exp *= zone->level_exp_mod[GetLevel()].ExpMod; + add_aaxp *= zone->level_exp_mod[GetLevel()].AAExpMod; + } + } + + uint32 exp = GetEXP() + add_exp; + + uint32 aaexp = (uint32)(RuleR(Character, AAExpMultiplier) * add_aaxp * aatotalmod); + uint32 had_aaexp = GetAAXP(); + aaexp += had_aaexp; + if(aaexp < had_aaexp) + aaexp = had_aaexp; //watch for wrap + + SetEXP(exp, aaexp, resexp); +} + +void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { + _log(CLIENT__EXP, "Attempting to Set Exp for %s (XP: %u, AAXP: %u, Rez: %s)", this->GetCleanName(), set_exp, set_aaxp, isrezzexp ? "true" : "false"); + //max_AAXP = GetEXPForLevel(52) - GetEXPForLevel(51); //GetEXPForLevel() doesn't depend on class/race, just level, so it shouldn't change between Clients + max_AAXP = RuleI(AA, ExpPerPoint); //this may be redundant since we're doing this in Client::FinishConnState2() + if (max_AAXP == 0 || GetEXPForLevel(GetLevel()) == 0xFFFFFFFF) { + Message(13, "Error in Client::SetEXP. EXP not set."); + 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); + else{ + if(this->IsGrouped()) + this->Message_StringID(MT_Experience, GAIN_GROUPXP); + else if(IsRaidGrouped()) + Message_StringID(MT_Experience, GAIN_RAIDEXP); + else + this->Message_StringID(MT_Experience, GAIN_XP); + } + } + else if((set_exp + set_aaxp) < (m_pp.exp+m_pp.expAA)){ //only loss message if you lose exp, no message if you gained/lost nothing. + Message(15, "You have lost experience."); + } + + //check_level represents the level we should be when we have + //this ammount of exp (once these loops complete) + uint16 check_level = GetLevel()+1; + //see if we gained any levels + while (set_exp >= GetEXPForLevel(check_level)) { + check_level++; + if (check_level > 127) { //hard level cap + check_level = 127; + break; + } + if(GetMercID()) + UpdateMercLevel(); + } + //see if we lost any levels + while (set_exp < GetEXPForLevel(check_level-1)) { + check_level--; + if (check_level < 2) { //hard level minimum + check_level = 2; + break; + } + if(GetMercID()) + UpdateMercLevel(); + } + check_level--; + + + //see if we gained any AAs + if (set_aaxp >= max_AAXP) { + /* + Note: AA exp is stored differently than normal exp. + Exp points are only stored in m_pp.expAA until you + gain a full AA point, once you gain it, a point is + added to m_pp.aapoints and the ammount needed to gain + that point is subtracted from m_pp.expAA + + then, once they spend an AA point, it is subtracted from + m_pp.aapoints. In theory it then goes into m_pp.aapoints_spent, + but im not sure if we have that in the right spot. + */ + //record how many points we have + uint32 last_unspentAA = m_pp.aapoints; + + //figure out how many AA points we get from the exp were setting + m_pp.aapoints = set_aaxp / max_AAXP; + _log(CLIENT__EXP, "Calculating additional AA Points from AAXP for %s: %u / %u = %.1f points", this->GetCleanName(), set_aaxp, max_AAXP, (float)set_aaxp / (float)max_AAXP); + + //get remainder exp points, set in PP below + set_aaxp = set_aaxp - (max_AAXP * m_pp.aapoints); + + //add in how many points we had + m_pp.aapoints += last_unspentAA; + //set_aaxp = m_pp.expAA % max_AAXP; + + //figure out how many points were actually gained + /*uint32 gained = m_pp.aapoints - last_unspentAA;*/ //unused + + //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. + //Message(15, "You now have %d skill points available to spend.", m_pp.aapoints); + } + + uint8 maxlevel = RuleI(Character, MaxExpLevel) + 1; + + if(maxlevel <= 1) + maxlevel = RuleI(Character, MaxLevel) + 1; + + if(check_level > maxlevel) { + check_level = maxlevel; + set_exp = GetEXPForLevel(maxlevel); + } + + if(RuleB(Character, PerCharacterQglobalMaxLevel)){ + uint32 MaxLevel = GetCharMaxLevelFromQGlobal(); + if(MaxLevel){ + if(GetLevel() >= MaxLevel){ + uint32 expneeded = GetEXPForLevel(MaxLevel); + if(set_exp > expneeded) + { + set_exp = expneeded; + } + } + } + } + + if ((GetLevel() != check_level) && !(check_level >= maxlevel)) { + char val1[20]={0}; + 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); + } + 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); + } + else + Message(15, "Welcome to level %i!", check_level); + SetLevel(check_level); + } + + //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) + { + set_exp = expneeded; + } + } + + //set the client's EXP and AAEXP + m_pp.exp = set_exp; + m_pp.expAA = set_aaxp; + + if (GetLevel() < 51) { + m_epp.perAA = 0; // turn off aa exp if they drop below 51 + } else + SendAAStats(); //otherwise, send them an AA update + + //send the expdata in any case so the xp bar isnt stuck after leveling + uint32 tmpxp1 = GetEXPForLevel(GetLevel()+1); + uint32 tmpxp2 = GetEXPForLevel(GetLevel()); + // Quag: 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) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); + ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; + float tmpxp = (float) ( (float) set_exp-tmpxp2 ) / ( (float) tmpxp1-tmpxp2 ); + eu->exp = (uint32)(330.0f * tmpxp); + FastQueuePacket(&outapp); + } + + if (admin>=100 && GetGM()) { + char val1[20]={0}; + char val2[20]={0}; + char val3[20]={0}; + Message_StringID(MT_Experience, GM_GAINXP,ConvertArray(set_aaxp,val1),ConvertArray(set_exp,val2),ConvertArray(GetEXPForLevel(GetLevel()+1),val3)); //[GM] You have gained %1 AXP and %2 EXP (%3). + //Message(15, "[GM] You now have %d / %d EXP and %d / %d AA exp.", set_exp, GetEXPForLevel(GetLevel()+1), set_aaxp, max_AAXP); + } +} + +void Client::SetLevel(uint8 set_level, bool command) +{ + if (GetEXPForLevel(set_level) == 0xFFFFFFFF) { + LogFile->write(EQEMuLog::Error,"Client::SetLevel() GetEXPForLevel(%i) = 0xFFFFFFFF", set_level); + return; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_LevelUpdate, sizeof(LevelUpdate_Struct)); + LevelUpdate_Struct* lu = (LevelUpdate_Struct*)outapp->pBuffer; + lu->level = set_level; + if(m_pp.level2 != 0) + lu->level_old = m_pp.level2; + else + lu->level_old = level; + + level = set_level; + + if(IsRaidGrouped()) + { + Raid *r = this->GetRaid(); + if(r){ + r->UpdateLevel(GetName(), set_level); + } + } + if(set_level > m_pp.level2) + { + if(m_pp.level2 == 0) + m_pp.points += 5; + else + m_pp.points += (5 * (set_level - m_pp.level2)); + + m_pp.level2 = set_level; + } + if(set_level > m_pp.level) { + parse->EventPlayer(EVENT_LEVEL_UP, this, "", 0); + } + + m_pp.level = set_level; + if (command){ + m_pp.exp = GetEXPForLevel(set_level); + Message(15, "Welcome to level %i!", set_level); + lu->exp = 0; + } + else { + 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); + + CalcBonuses(); + if(!RuleB(Character, HealOnLevel)) + { + int mhp = CalcMaxHP(); + if(GetHP() > mhp) + SetHP(mhp); + } + else + { + SetHP(CalcMaxHP()); // Why not, lets give them a free heal + } + + DoTributeUpdate(); + SendHPUpdate(); + SetMana(CalcMaxMana()); + UpdateWho(); + if(GetMerc()) + UpdateMercLevel(); + Save(); +} + +// Note: The client calculates exp separately, we cant change this function +// Add: You can set the values you want now, client will be always sync :) - Merkur +uint32 Client::GetEXPForLevel(uint16 check_level) +{ + + uint16 check_levelm1 = check_level-1; + float mod; + if (check_level < 31) + mod = 1.0; + else if (check_level < 36) + mod = 1.1; + else if (check_level < 41) + mod = 1.2; + else if (check_level < 46) + mod = 1.3; + else if (check_level < 52) + mod = 1.4; + else if (check_level < 53) + mod = 1.5; + else if (check_level < 54) + mod = 1.6; + else if (check_level < 55) + mod = 1.7; + else if (check_level < 56) + mod = 1.9; + else if (check_level < 57) + mod = 2.1; + else if (check_level < 58) + mod = 2.3; + else if (check_level < 59) + mod = 2.5; + else if (check_level < 60) + mod = 2.7; + else if (check_level < 61) + mod = 3.0; + else + mod = 3.1; + + float base = (check_levelm1)*(check_levelm1)*(check_levelm1); + + mod *= 1000; + + return(uint32(base * mod)); +} + +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()); +} + +void Group::SplitExp(uint32 exp, Mob* other) { + if( other->CastToNPC()->MerchantType != 0 ) // Ensure NPC isn't a merchant + return; + + if(other->GetOwner() && other->GetOwner()->IsClient()) // Ensure owner isn't pc + return; + + unsigned int i; + uint32 groupexp = exp; + uint8 membercount = 0; + uint8 maxlevel = 1; + + for (i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (members[i] != NULL) { + if(members[i]->GetLevel() > maxlevel) + maxlevel = members[i]->GetLevel(); + + membercount++; + } + } + + float groupmod; + if (membercount == 2) + groupmod = 1.2; + else if (membercount == 3) + groupmod = 1.4; + else if (membercount == 4) + groupmod = 1.6; + else if (membercount == 5) + groupmod = 1.8; + else if (membercount == 6) + groupmod = 2.16; + else + groupmod = 1.0; + + groupexp += (uint32)((float)exp * groupmod * (RuleR(Character, GroupExpMultiplier))); + + int conlevel = Mob::GetLevelCon(maxlevel, other->GetLevel()); + if(conlevel == CON_GREEN) + return; //no exp for greenies... + + if (membercount == 0) + return; + + for (i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (members[i] != NULL && members[i]->IsClient()) // If Group Member is Client + { + Client *cmember = members[i]->CastToClient(); + // add exp + exp cap + int16 diff = cmember->GetLevel() - maxlevel; + int16 maxdiff = -(cmember->GetLevel()*15/10 - cmember->GetLevel()); + if(maxdiff > -5) + maxdiff = -5; + if (diff >= (maxdiff)) { /*Instead of person who killed the mob, the person who has the highest level in the group*/ + uint32 tmp = (cmember->GetLevel()+3) * (cmember->GetLevel()+3) * 75 * 35 / 10; + uint32 tmp2 = groupexp / membercount; + cmember->AddEXP( tmp < tmp2 ? tmp : tmp2, conlevel ); + } + } + } +} + +void Raid::SplitExp(uint32 exp, Mob* other) { + if( other->CastToNPC()->MerchantType != 0 ) // Ensure NPC isn't a merchant + return; + + if(other->GetOwner() && other->GetOwner()->IsClient()) // Ensure owner isn't pc + return; + + uint32 groupexp = exp; + uint8 membercount = 0; + uint8 maxlevel = 1; + + for (int i = 0; i < MAX_RAID_MEMBERS; i++) { + if (members[i].member != NULL) { + if(members[i].member->GetLevel() > maxlevel) + maxlevel = members[i].member->GetLevel(); + + membercount++; + } + } + + groupexp = (uint32)((float)groupexp * (1.0f-(RuleR(Character, RaidExpMultiplier)))); + + int conlevel = Mob::GetLevelCon(maxlevel, other->GetLevel()); + if(conlevel == CON_GREEN) + return; //no exp for greenies... + + if (membercount == 0) + return; + + for (unsigned int x = 0; x < MAX_RAID_MEMBERS; x++) { + if (members[x].member != NULL) // If Group Member is Client + { + Client *cmember = members[x].member; + // add exp + exp cap + int16 diff = cmember->GetLevel() - maxlevel; + int16 maxdiff = -(cmember->GetLevel()*15/10 - cmember->GetLevel()); + if(maxdiff > -5) + maxdiff = -5; + if (diff >= (maxdiff)) { /*Instead of person who killed the mob, the person who has the highest level in the group*/ + uint32 tmp = (cmember->GetLevel()+3) * (cmember->GetLevel()+3) * 75 * 35 / 10; + uint32 tmp2 = (groupexp / membercount) + 1; + cmember->AddEXP( tmp < tmp2 ? tmp : tmp2, conlevel ); + } + } + } +} + +void Client::SetLeadershipEXP(uint32 group_exp, uint32 raid_exp) { + while(group_exp >= GROUP_EXP_PER_POINT) { + group_exp -= GROUP_EXP_PER_POINT; + m_pp.group_leadership_points++; + Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_POINT); + } + while(raid_exp >= RAID_EXP_PER_POINT) { + raid_exp -= RAID_EXP_PER_POINT; + m_pp.raid_leadership_points++; + Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_POINT); + } + + m_pp.group_leadership_exp = group_exp; + m_pp.raid_leadership_exp = raid_exp; + + SendLeadershipEXPUpdate(); +} + +void Client::AddLeadershipEXP(uint32 group_exp, uint32 raid_exp) { + SetLeadershipEXP(GetGroupEXP() + group_exp, GetRaidEXP() + raid_exp); +} + +void Client::SendLeadershipEXPUpdate() { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_LeadershipExpUpdate, sizeof(LeadershipExpUpdate_Struct)); + LeadershipExpUpdate_Struct* eu = (LeadershipExpUpdate_Struct *) outapp->pBuffer; + + eu->group_leadership_exp = m_pp.group_leadership_exp; + eu->group_leadership_points = m_pp.group_leadership_points; + eu->raid_leadership_exp = m_pp.raid_leadership_exp; + eu->raid_leadership_points = m_pp.raid_leadership_points; + + FastQueuePacket(&outapp); +} + +uint32 Client::GetCharMaxLevelFromQGlobal() { + + QGlobalCache *char_c = NULL; + char_c = this->GetQGlobals(); + + std::list globalMap; + uint32 ntype = 0; + + 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; + } + + return false; // Default is false +} \ No newline at end of file diff --git a/zone/faction.cpp b/zone/faction.cpp new file mode 100644 index 000000000..7af8f50bc --- /dev/null +++ b/zone/faction.cpp @@ -0,0 +1,1064 @@ +/* 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 "../common/debug.h" +#include +using namespace std; +#include +#include + +#ifndef WIN32 + #include +#endif +#include "faction.h" +#include "zonedb.h" +#include "masterentity.h" +#include "zone.h" +#include "../common/MiscFunctions.h" + +extern Zone* zone; + + +#ifdef _WINDOWS + #define snprintf _snprintf +#endif + +//#define FACTIONS_DEBUG 5 + +const char *FactionValueToString(FACTION_VALUE fv) { + switch(fv) { + case FACTION_ALLY: + return("Ally"); + case FACTION_WARMLY: + return("Warmly"); + case FACTION_KINDLY: + return("Kindly"); + case FACTION_AMIABLE: + return("Amiable"); + case FACTION_INDIFFERENT: + return("Indifferent"); + case FACTION_APPREHENSIVE: + return("Apprehensive"); + case FACTION_DUBIOUS: + return("Dubious"); + case FACTION_THREATENLY: + return("Threatenly"); + case FACTION_SCOWLS: + return("Scowls, ready to attack."); + default: + break; + } + return("Unknown Faction Con"); +} + + +//o-------------------------------------------------------------- +//| Name: CalculateFaction; rembrant, Dec. 16, 2001 +//o-------------------------------------------------------------- +//| Notes: Returns the faction message value. +//| Modify these values to taste. +//o-------------------------------------------------------------- +FACTION_VALUE CalculateFaction(FactionMods* fm, int32 tmpCharacter_value) +{ +#if FACTIONS_DEBUG >= 5 + LogFile->write(EQEMuLog::Debug, "called CalculateFaction(0x%x, %ld)", fm, (unsigned long)tmpCharacter_value); +#endif + int32 character_value = tmpCharacter_value; + if (fm) + character_value += fm->base + fm->class_mod + fm->race_mod + fm->deity_mod; + if(character_value >= 1101) return FACTION_ALLY; + if(character_value >= 701 && character_value <= 1100) return FACTION_WARMLY; + if(character_value >= 401 && character_value <= 700) return FACTION_KINDLY; + if(character_value >= 101 && character_value <= 400) return FACTION_AMIABLE; + if(character_value >= 0 && character_value <= 100) return FACTION_INDIFFERENT; + if(character_value >= -100 && character_value <= -1) return FACTION_APPREHENSIVE; + if(character_value >= -700 && character_value <= -101) return FACTION_DUBIOUS; + if(character_value >= -999 && character_value <= -701) return FACTION_THREATENLY; + if(character_value <= -1000) return FACTION_SCOWLS; + return FACTION_INDIFFERENT; +} + +// neotokyo: this function should check if some races have more than one race define +bool IsOfEqualRace(int r1, int r2) +{ + if (r1 == r2) + return true; + // TODO: add more values + switch(r1) + { + case DARK_ELF: + if (r2 == 77) + return true; + break; + case BARBARIAN: + if (r2 == 90) + return true; + } + return false; +} + +// neotokyo: trolls endure ogres, dark elves, ... +bool IsOfIndiffRace(int r1, int r2) +{ + if (r1 == r2) + return true; + // TODO: add more values + switch(r1) + { + case DARK_ELF: + case OGRE: + case TROLL: + if (r2 == OGRE || r2 == TROLL || r2 == DARK_ELF) + return true; + break; + case HUMAN: + case BARBARIAN: + case HALF_ELF: + case GNOME: + case HALFLING: + case WOOD_ELF: + if (r2 == HUMAN || + r2 == BARBARIAN || + r2 == ERUDITE || + r2 == HALF_ELF || + r2 == GNOME || + r2 == HALFLING || + r2 == DWARF || + r2 == HIGH_ELF || + r2 == WOOD_ELF) + return true; + break; + case ERUDITE: + if (r2 == HUMAN || r2 == HALF_ELF) + return true; + break; + case DWARF: + if (r2 == HALFLING || r2 == GNOME) + return true; + break; + case HIGH_ELF: + if (r2 == WOOD_ELF) + return true; + break; + case VAHSHIR: + return true; + case IKSAR: + return false; + } + return false; +} + +// returns what Other thinks of this +FACTION_VALUE Client::GetReverseFactionCon(Mob* iOther) { +#if FACTIONS_DEBUG >= 5 + LogFile->write(EQEMuLog::Debug, "called $s::GetReverseFactionCon(%s)", GetName(), iOther->GetName()); +#endif + + if (GetOwnerID()) { + return GetOwnerOrSelf()->GetReverseFactionCon(iOther); + } + + iOther = iOther->GetOwnerOrSelf(); + +#if FACTIONS_DEBUG >= 5 + LogFile->write(EQEMuLog::Debug, " %s'd primary faction = %d", iOther->GetName(), iOther->GetPrimaryFaction()); +#endif + if (iOther->GetPrimaryFaction() < 0) + return GetSpecialFactionCon(iOther); + + if (iOther->GetPrimaryFaction() == 0) + return FACTION_INDIFFERENT; + + return GetFactionLevel(CharacterID(), 0, GetRace(), GetClass(), GetDeity(), iOther->GetPrimaryFaction(), iOther); +} + +//this is called with 'this' as the mob being looked at, and +//iOther the mob who is doing the looking. It should figure out +//what iOther thinks about 'this' +FACTION_VALUE NPC::GetReverseFactionCon(Mob* iOther) { +#if FACTIONS_DEBUG >= 20 + LogFile->write(EQEMuLog::Debug, "called N $s::GetReverseFactionCon(%s)", GetName(), iOther->GetName()); +#endif + + _ZP(NPC_GetReverseFactionCon); + + iOther = iOther->GetOwnerOrSelf(); + int primaryFaction= iOther->GetPrimaryFaction(); + +#if FACTIONS_DEBUG >= 20 + LogFile->write(EQEMuLog::Debug, " %s'd primary faction = %d", iOther->GetName(), primaryFaction); +#endif + + //I am pretty sure that this special faction call is backwards + //and should be iOther->GetSpecialFactionCon(this) + if (primaryFaction < 0) + return GetSpecialFactionCon(iOther); + + if (primaryFaction == 0) + return FACTION_INDIFFERENT; + + //if we are a pet, use our owner's faction stuff + Mob *own = GetOwner(); + if (own != NULL) + return own->GetReverseFactionCon(iOther); + + //make sure iOther is an npc + //also, if we dont have a faction, then they arnt gunna think anything of us either + if(!iOther->IsNPC() || GetPrimaryFaction() == 0) + return(FACTION_INDIFFERENT); + + //if we get here, iOther is an NPC too + + //otherwise, employ the npc faction stuff + //so we need to look at iOther's faction table to see + //what iOther thinks about our primary faction + return(iOther->CastToNPC()->CheckNPCFactionAlly(GetPrimaryFaction())); +} + +//Look through our faction list and return a faction con based +//on the npc_value for the other person's primary faction in our list. +FACTION_VALUE NPC::CheckNPCFactionAlly(int32 other_faction) { + list::iterator cur,end; + cur = faction_list.begin(); + end = faction_list.end(); + for(; cur != end; cur++) { + struct NPCFaction* fac = *cur; + if ((int32)fac->factionID == other_faction) { + if (fac->npc_value > 0) + return FACTION_ALLY; + else if (fac->npc_value < 0) + return FACTION_SCOWLS; + else + return FACTION_INDIFFERENT; + } + } + return FACTION_INDIFFERENT; +} + + +bool NPC::IsFactionListAlly(uint32 other_faction) { +/* list::iterator cur,end; + cur = faction_list.begin(); + end = faction_list.end(); + for(; cur != end; cur++) { + struct NPCFaction* fac = *cur; + if (fac->factionID == other_faction && fac->npc_value > 0) + return(true); + } + return(false);*/ + return(CheckNPCFactionAlly(other_faction) == FACTION_ALLY); +} + +// Faction Mods for Alliance type spells +void Mob::AddFactionBonus(uint32 pFactionID,int32 bonus) { + map :: const_iterator faction_bonus; + typedef std::pair NewFactionBonus; + + faction_bonus = faction_bonuses.find(pFactionID); + if(faction_bonus == faction_bonuses.end()) + { + faction_bonuses.insert(NewFactionBonus(pFactionID,bonus)); + } + else + { + if(faction_bonus->second :: const_iterator faction_bonus; + typedef std::pair NewFactionBonus; + + faction_bonus = item_faction_bonuses.find(pFactionID); + if(faction_bonus == item_faction_bonuses.end()) + { + item_faction_bonuses.insert(NewFactionBonus(pFactionID,bonus)); + } + else + { + if((bonus > 0 && faction_bonus->second < bonus) || (bonus < 0 && faction_bonus->second > bonus)) + { + item_faction_bonuses.erase(pFactionID); + item_faction_bonuses.insert(NewFactionBonus(pFactionID,bonus)); + } + } +} + +int32 Mob::GetFactionBonus(uint32 pFactionID) { + map :: const_iterator faction_bonus; + faction_bonus = faction_bonuses.find(pFactionID); + if(faction_bonus != faction_bonuses.end()) + { + return (*faction_bonus).second; + } + return 0; +} + +int32 Mob::GetItemFactionBonus(uint32 pFactionID) { + map :: const_iterator faction_bonus; + faction_bonus = item_faction_bonuses.find(pFactionID); + if(faction_bonus != item_faction_bonuses.end()) + { + return (*faction_bonus).second; + } + return 0; +} + +void Mob::ClearItemFactionBonuses() { + map :: iterator itr; + for(itr = item_faction_bonuses.begin(); itr != item_faction_bonuses.end(); itr++) + { + item_faction_bonuses.erase(itr->first); + } +} + +FACTION_VALUE Mob::GetSpecialFactionCon(Mob* iOther) { +#if FACTIONS_DEBUG >= 5 + LogFile->write(EQEMuLog::Debug, "called $s::GetSpecialFactionCon(%s)", GetName(), iOther->GetName()); +#endif + + if (!iOther) + return FACTION_INDIFFERENT; + + iOther = iOther->GetOwnerOrSelf(); + Mob* self = this->GetOwnerOrSelf(); + + bool selfAIcontrolled = self->IsAIControlled(); + bool iOtherAIControlled = iOther->IsAIControlled(); + int selfPrimaryFaction = self->GetPrimaryFaction(); + int iOtherPrimaryFaction = iOther->GetPrimaryFaction(); + +#if FACTIONS_DEBUG >= 5 + LogFile->write(EQEMuLog::Debug, " GSFC %d %d %d %d", selfAIcontrolled, iOtherAIControlled, selfPrimaryFaction, iOtherPrimaryFaction); +#endif + + if (selfPrimaryFaction >= 0 && selfAIcontrolled) + return FACTION_INDIFFERENT; + if (iOther->GetPrimaryFaction() >= 0) + return FACTION_INDIFFERENT; +/* special values: + -2 = indiff to player, ally to AI on special values, indiff to AI + -3 = dub to player, ally to AI on special values, indiff to AI + -4 = atk to player, ally to AI on special values, indiff to AI + -5 = indiff to player, indiff to AI + -6 = dub to player, indiff to AI + -7 = atk to player, indiff to AI + -8 = indiff to players, ally to AI on same value, indiff to AI + -9 = dub to players, ally to AI on same value, indiff to AI + -10 = atk to players, ally to AI on same value, indiff to AI + -11 = indiff to players, ally to AI on same value, atk to AI + -12 = dub to players, ally to AI on same value, atk to AI + -13 = atk to players, ally to AI on same value, atk to AI +*/ + switch (iOtherPrimaryFaction) { + case -2: // -2 = indiff to player, ally to AI on special values, indiff to AI + if (selfAIcontrolled && iOtherAIControlled) + return FACTION_ALLY; + else + return FACTION_INDIFFERENT; + case -3: // -3 = dub to player, ally to AI on special values, indiff to AI + if (selfAIcontrolled && iOtherAIControlled) + return FACTION_ALLY; + else + return FACTION_DUBIOUS; + case -4: // -4 = atk to player, ally to AI on special values, indiff to AI + if (selfAIcontrolled && iOtherAIControlled) + return FACTION_ALLY; + else + return FACTION_SCOWLS; + case -5: // -5 = indiff to player, indiff to AI + return FACTION_INDIFFERENT; + case -6: // -6 = dub to player, indiff to AI + if (selfAIcontrolled && iOtherAIControlled) + return FACTION_INDIFFERENT; + else + return FACTION_DUBIOUS; + case -7: // -7 = atk to player, indiff to AI + if (selfAIcontrolled && iOtherAIControlled) + return FACTION_INDIFFERENT; + else + return FACTION_SCOWLS; + case -8: // -8 = indiff to players, ally to AI on same value, indiff to AI + if (selfAIcontrolled && iOtherAIControlled) { + if (selfPrimaryFaction == iOtherPrimaryFaction) + return FACTION_ALLY; + else + return FACTION_INDIFFERENT; + } + else + return FACTION_INDIFFERENT; + case -9: // -9 = dub to players, ally to AI on same value, indiff to AI + if (selfAIcontrolled && iOtherAIControlled) { + if (selfPrimaryFaction == iOtherPrimaryFaction) + return FACTION_ALLY; + else + return FACTION_INDIFFERENT; + } + else + return FACTION_DUBIOUS; + case -10: // -10 = atk to players, ally to AI on same value, indiff to AI + if (selfAIcontrolled && iOtherAIControlled) { + if (selfPrimaryFaction == iOtherPrimaryFaction) + return FACTION_ALLY; + else + return FACTION_INDIFFERENT; + } + else + return FACTION_SCOWLS; + case -11: // -11 = indiff to players, ally to AI on same value, atk to AI + if (selfAIcontrolled && iOtherAIControlled) { + if (selfPrimaryFaction == iOtherPrimaryFaction) + return FACTION_ALLY; + else + return FACTION_SCOWLS; + } + else + return FACTION_INDIFFERENT; + case -12: // -12 = dub to players, ally to AI on same value, atk to AI + if (selfAIcontrolled && iOtherAIControlled) { + if (selfPrimaryFaction == iOtherPrimaryFaction) + return FACTION_ALLY; + else + return FACTION_SCOWLS; + + + } + else + return FACTION_DUBIOUS; + case -13: // -13 = atk to players, ally to AI on same value, atk to AI + if (selfAIcontrolled && iOtherAIControlled) { + if (selfPrimaryFaction == iOtherPrimaryFaction) + return FACTION_ALLY; + else + return FACTION_SCOWLS; + } + else + return FACTION_SCOWLS; + default: + return FACTION_INDIFFERENT; + } +} + +//o-------------------------------------------------------------- +//| Name: GetFactionLevel; rembrant, Dec. 16, 2001 +//o-------------------------------------------------------------- +//| Notes: Gets the characters faction standing with the +//| specified NPC. +//| Will return Indifferent on failure. +//o-------------------------------------------------------------- +FACTION_VALUE Client::GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_race, uint32 p_class, uint32 p_deity, int32 pFaction, Mob* tnpc) +{ +#if FACTIONS_DEBUG >= 5 + LogFile->write(EQEMuLog::Debug, "called %s::GetFactionLevel(%lu, %lu, %lu, %lu, %lu, %lu, %s)", GetName(), (unsigned long)char_id, (unsigned long)npc_id, (unsigned long)p_race, (unsigned long)p_class, (unsigned long)p_deity, (unsigned long)pFaction, tnpc?tnpc->GetName():"(NULL)"); +#endif + + _ZP(Client_GetFactionLevel); + + if (pFaction < 0) + return GetSpecialFactionCon(tnpc); + FACTION_VALUE fac = FACTION_INDIFFERENT; + //int32 pFacValue; -Trumpcard: commenting. Not currently used. + int32 tmpFactionValue; + FactionMods fmods; + + // neotokyo: few optimizations + if (GetFeigned()) + return FACTION_INDIFFERENT; + if (invisible_undead && tnpc && !tnpc->SeeInvisibleUndead()) + return FACTION_INDIFFERENT; + if (IsInvisible(tnpc)) + return FACTION_INDIFFERENT; + if (tnpc && tnpc->GetOwnerID() != 0) // pets con amiably to owner and indiff to rest + if (char_id == tnpc->GetOwner()->CastToClient()->CharacterID()) + return FACTION_AMIABLE; + else + return FACTION_INDIFFERENT; + + //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)) + { + //Get the players current faction with pFaction + tmpFactionValue = GetCharacterFactionLevel(pFaction); + // Everhood - tack on any bonuses from Alliance type spell effects + tmpFactionValue += GetFactionBonus(pFaction); + tmpFactionValue += GetItemFactionBonus(pFaction); + //Return the faction to the client + fac = CalculateFaction(&fmods, tmpFactionValue); + //Message(0,"Faction: %i %i %i %i",fmods.base,fmods.class_mod,fmods.race_mod,fmods.deity_mod); + //Message(0,"tmpFactionValue: %i, fac: %i",tmpFactionValue,fac); + } + } + else + { //pFaction == 0 + return(FACTION_INDIFFERENT); + /* + I think this is a good idea, but the consensus seems to be + that if the faction is not in the DB, it should not be + made up based on race and class like this is doing. + + fmods.base = 0; + fmods.deity_mod = 0; + + if (tnpc && p_class == (uint32) tnpc->GetClass()%16) + fmods.class_mod = 301; + else if (tnpc && tnpc->IsNPC() && tnpc->CastToNPC()->MerchantType == 0) + fmods.class_mod = -101; + else + fmods.class_mod = 0; + + if (tnpc && IsOfEqualRace(p_race, tnpc->GetRace()) ) + fmods.race_mod = 101; + else if (tnpc && IsOfIndiffRace(p_race, tnpc->GetRace()) ) + fmods.race_mod = 0; + else if (tnpc) + fmods.race_mod = -51; + else + fmods.race_mod = 0; + fac = CalculateFaction(&fmods, 0); + */ + } + + // merchant fix + if (tnpc && tnpc->IsNPC() && tnpc->CastToNPC()->MerchantType && (fac == FACTION_THREATENLY || fac == FACTION_SCOWLS)) + fac = FACTION_DUBIOUS; + + if (tnpc != 0 && fac != FACTION_SCOWLS && tnpc->CastToNPC()->CheckAggro(this)) + fac = FACTION_THREATENLY; + +#if FACTIONS_DEBUG >= 5 + LogFile->write(EQEMuLog::Debug, "%s::GetFactionLevel() result: %d", GetName(), fac); +#endif + return fac; +} + +//o-------------------------------------------------------------- +//| Name: SetFactionLevel; rembrant, Dec. 20, 2001 +//o-------------------------------------------------------------- +//| Notes: Sets the characters faction standing with the +//| specified NPC. +//o-------------------------------------------------------------- +void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity) +{ + _ZP(Client_SetFactionLevel); + 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; + // Get the npc faction list + if(!database.GetNPCFactionList(npc_id, faction_id, npc_value, temp)) + return; + for(int i = 0;iitembonuses.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) + 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) + 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; + + // Calculate the faction + if(npc_value[i] != 0) { + tmpValue = current_value + mod + npc_value[i]; + + // Make sure faction hits don't go to GMs... + if (m_pp.gm==1 && (tmpValue < current_value)) { + tmpValue = current_value; + } + + // Make sure we dont go over the min/max faction limits + if(tmpValue >= MAX_FACTION) + { + 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; + } + } + 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; + + char* msg = BuildFactionMessage(npc_value[i],faction_id[i],tmpValue,temp[i]); + if (msg != 0) + Message(0, msg); + safe_delete_array(msg); + } + } + } + return; +} + +void Client::SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value, uint8 temp) +{ + _ZP(Client_SetFactionLevel2); +// int32 tmpValue; + int32 current_value; +// FactionMods fm; + //Get the npc faction list + if(faction_id > 0 && value != 0) { + //Get the faction modifiers + current_value = GetCharacterFactionLevel(faction_id) + value; + if(!(database.SetCharacterFactionLevel(char_id, faction_id, current_value, temp, factionvalues))) + return; + + char* msg = BuildFactionMessage(value, faction_id, current_value, temp); + if (msg != 0) + Message(0, msg); + safe_delete(msg); + + } + return; +} + +int32 Client::GetCharacterFactionLevel(int32 faction_id) +{ + if (faction_id <= 0) + return 0; + faction_map::iterator res; + res = factionvalues.find(faction_id); + 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)) + Modded += fm.base + fm.class_mod + fm.race_mod + fm.deity_mod; + if (Modded > MAX_FACTION) + Modded = MAX_FACTION; + + return Modded; +} + +bool ZoneDatabase::GetFactionData(FactionMods* fm, uint32 class_mod, uint32 race_mod, uint32 deity_mod, int32 faction_id) { + if (faction_id <= 0 || faction_id > (int32) max_faction) + return false; + + if (faction_array[faction_id] == 0){ + return false; + } + + fm->base = faction_array[faction_id]->base; + + if(class_mod > 0) { + char str[32]; + sprintf(str, "c%u", class_mod); + + std::map::const_iterator iter = faction_array[faction_id]->mods.find(str); + if(iter != faction_array[faction_id]->mods.end()) { + fm->class_mod = iter->second; + } else { + fm->class_mod = 0; + } + } else { + fm->class_mod = 0; + } + + if(race_mod > 0) { + char str[32]; + sprintf(str, "r%u", race_mod); + + std::map::iterator iter = faction_array[faction_id]->mods.find(str); + if(iter != faction_array[faction_id]->mods.end()) { + fm->race_mod = iter->second; + } else { + fm->race_mod = 0; + } + } else { + fm->race_mod = 0; + } + + if(deity_mod > 0) { + char str[32]; + sprintf(str, "d%u", deity_mod); + + std::map::iterator iter = faction_array[faction_id]->mods.find(str); + if(iter != faction_array[faction_id]->mods.end()) { + fm->deity_mod = iter->second; + } else { + fm->deity_mod = 0; + } + } else { + fm->deity_mod = 0; + } + + 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 { + cerr << "Error in LoadFactionValues query '" << query << "' " << errbuf << 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: BuildFactionMessage; rembrant, Dec. 16, 2001 +//o-------------------------------------------------------------- +//| Purpose: duh? +//o-------------------------------------------------------------- +char* BuildFactionMessage(int32 tmpvalue, int32 faction_id, int32 totalvalue, uint8 temp) +{ +/* + +This should be replaced to send string-ID based messages using: +#define FACTION_WORST 469 //Your faction standing with %1 could not possibly get any worse. +#define FACTION_WORSE 470 //Your faction standing with %1 got worse. +#define FACTION_BEST 471 //Your faction standing with %1 could not possibly get any better. +#define FACTION_BETTER 472 //Your faction standing with %1 got better. + +some day. + +*/ + //tmpvalue is the change as best I can tell. + char *faction_message = 0; + + char name[50]; + + if(database.GetFactionName(faction_id, name, sizeof(name)) == false) { + snprintf(name, sizeof(name),"Faction%i",faction_id); + } + + if(tmpvalue == 0 || temp == 1 || temp == 2) { + return 0; + } + else if (totalvalue >= MAX_FACTION) { + MakeAnyLenString(&faction_message, "Your faction standing with %s could not possibly get any better!", name); + return faction_message; + } + else if(tmpvalue > 0 && totalvalue < MAX_FACTION) { + MakeAnyLenString(&faction_message, "Your faction standing with %s has gotten better!", name); + return faction_message; + } + else if(tmpvalue < 0 && totalvalue > MIN_FACTION) { + MakeAnyLenString(&faction_message, "Your faction standing with %s has gotten worse!", name); + return faction_message; + } + else if(totalvalue <= MIN_FACTION) { + MakeAnyLenString(&faction_message, "Your faction standing with %s could not possibly get any worse!", name); + return faction_message; + } + return 0; +} + +//o-------------------------------------------------------------- +//| Name: GetFactionName; rembrant, Dec. 16 +//o-------------------------------------------------------------- +//| Notes: Retrieves the name of the specified faction +//| Returns false on failure. +//o-------------------------------------------------------------- +bool ZoneDatabase::GetFactionName(int32 faction_id, char* name, uint32 buflen) { + if ((faction_id <= 0) || faction_id > int32(max_faction) ||(faction_array[faction_id] == 0)) + return false; + if (faction_array[faction_id]->name[0] != 0) { + strn0cpy(name, faction_array[faction_id]->name, buflen); + return true; + } + return false; + +} + +//o-------------------------------------------------------------- +//| Name: GetNPCFactionList; rembrant, Dec. 16, 2001 +//o-------------------------------------------------------------- +//| Purpose: Gets a list of faction_id's and values bound to +//| the npc_id. +//| Returns false on failure. +//o-------------------------------------------------------------- +bool ZoneDatabase::GetNPCFactionList(uint32 npcfaction_id, int32* faction_id, int32* value, uint8* temp, int32* primary_faction) { + if (npcfaction_id <= 0) { + if (primary_faction) + *primary_faction = npcfaction_id; + return true; + } + const NPCFactionList* nfl = GetNPCFactionEntry(npcfaction_id); + if (!nfl) + return false; + if (primary_faction) + *primary_faction = nfl->primaryfaction; + for (int i=0; ifactionid[i]; + value[i] = nfl->factionvalue[i]; + temp[i] = nfl->factiontemp[i]; + } + return true; +} + +//o-------------------------------------------------------------- +//| Name: SetCharacterFactionLevel; rembrant, 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)) { + cerr << "Error in SetCharacterFactionLevel query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + if(value == 0) + { + safe_delete_array(query); + return true; + } + + if(temp == 2) + temp = 0; + + 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)) { + cerr << "Error in SetCharacterFactionLevel query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + safe_delete_array(query); + + if (affected_rows == 0) + { + return false; + } + + val_list[faction_id] = value; + 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 { + cerr << "Error in LoadFactionData '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + } + else { + mysql_free_result(result); + } + } + else { + cerr << "Error in LoadFactionData '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + return true; +} + +bool ZoneDatabase::GetFactionIdsForNPC(uint32 nfl_id, list *faction_list, int32* primary_faction) { + if (nfl_id <= 0) { + list::iterator cur,end; + cur = faction_list->begin(); + end = faction_list->end(); + for(; cur != end; cur++) { + struct NPCFaction* tmp = *cur; + safe_delete(tmp); + } + + faction_list->clear(); + if (primary_faction) + *primary_faction = nfl_id; + return true; + } + const NPCFactionList* nfl = GetNPCFactionEntry(nfl_id); + if (!nfl) + return false; + if (primary_faction) + *primary_faction = nfl->primaryfaction; + + list::iterator cur,end; + cur = faction_list->begin(); + end = faction_list->end(); + for(; cur != end; cur++) { + struct NPCFaction* tmp = *cur; + safe_delete(tmp); + } + faction_list->clear(); + for (int i=0; ifactionid[i]) { + pFac = new struct NPCFaction; + pFac->factionID = nfl->factionid[i]; + pFac->value_mod = nfl->factionvalue[i]; + pFac->npc_value = nfl->factionnpcvalue[i]; + pFac->temp = nfl->factiontemp[i]; +/* if (nfl->primaryfaction == pFac->factionID) + pFac->primary = true; + else + pFac->primary = false; +*/ + faction_list->push_back(pFac); + } + } + return true; +} + +bool Client::HatedByClass(uint32 p_race, uint32 p_class, uint32 p_deity, int32 pFaction) +{ + + bool Result = false; + _ZP(Client_GetFactionLevel); + + 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; + } + } + return Result; +} diff --git a/zone/faction.h b/zone/faction.h new file mode 100644 index 000000000..d7705bb8f --- /dev/null +++ b/zone/faction.h @@ -0,0 +1,77 @@ +/* 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 FACTION_H +#define FACTION_H + +#include "../common/types.h" +#include "features.h" +#include +#include + +enum FACTION_VALUE { + FACTION_ALLY = 1, + FACTION_WARMLY = 2, + FACTION_KINDLY = 3, + FACTION_AMIABLE = 4, + + FACTION_INDIFFERENT = 5, + + FACTION_APPREHENSIVE = 6, + FACTION_DUBIOUS = 7, + FACTION_THREATENLY = 8, + FACTION_SCOWLS = 9 +}; + +struct NPCFactionList { + uint32 id; + uint32 primaryfaction; + bool assistprimaryfaction; + uint32 factionid[MAX_NPC_FACTIONS]; + int32 factionvalue[MAX_NPC_FACTIONS]; + int8 factionnpcvalue[MAX_NPC_FACTIONS]; + uint8 factiontemp[MAX_NPC_FACTIONS]; +}; + +struct FactionMods +{ + int32 base; + int32 class_mod; + int32 race_mod; + int32 deity_mod; +}; +struct Faction { + int32 id; + std::map mods; + int16 base; + char name[50]; +}; +typedef map faction_map; + +struct NPCFaction +{ +uint32 factionID; +int32 value_mod; +int8 npc_value; +uint8 temp; +//bool primary; +}; + +const char *FactionValueToString(FACTION_VALUE fv); +char* BuildFactionMessage(int32 tmpvalue, int32 faction_id, int32 totalvalue, uint8 temp); +FACTION_VALUE CalculateFaction(FactionMods* fm, int32 tmpCharacter_value); +#endif diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp new file mode 100644 index 000000000..6a1268beb --- /dev/null +++ b/zone/fearpath.cpp @@ -0,0 +1,645 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 +#include +#include +#include + +#include "../common/rulesys.h" +#include "../common/MiscFunctions.h" +#include "zone_profile.h" +#include "map.h" +#include "zone.h" +#include "pathing.h" +#ifdef _WINDOWS +#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... + if(flee_mode && curfp) + return; + + //dont bother if we are immune to fleeing + if(SpecAttacks[IMMUNE_FLEEING] || spellbonuses.ImmuneToFlee) + return; + + if(!flee_timer.Check()) + return; //only do all this stuff every little while, since + //its not essential that we start running RIGHT away + + //see if were possibly hurt enough + float ratio = GetHPRatio(); + if(ratio >= RuleI(Combat, FleeHPRatio)) + return; + + //we might be hurt enough, check con now.. + Mob *hate_top = GetHateTop(); + if(!hate_top) { + //this should never happen... + StartFleeing(); + return; + } + + float other_ratio = hate_top->GetHPRatio(); + if(other_ratio < 20) { + //our hate top is almost dead too... stay and fight + return; + } + + //base our flee ratio on our con. this is how the + //attacker sees the mob, since this is all we can observe + uint32 con = GetLevelCon(hate_top->GetLevel(), GetLevel()); + float run_ratio; + switch(con) { + //these values are not 100% researched + case CON_GREEN: + run_ratio = RuleI(Combat, FleeHPRatio); + break; + case CON_LIGHTBLUE: + run_ratio = RuleI(Combat, FleeHPRatio) * 8 / 10; + break; + case CON_BLUE: + run_ratio = RuleI(Combat, FleeHPRatio) * 6 / 10; + break; + default: + run_ratio = RuleI(Combat, FleeHPRatio) * 4 / 10; + break; + } + if(ratio < run_ratio) + { + if( RuleB(Combat, FleeIfNotAlone) + || ( !RuleB(Combat, FleeIfNotAlone) + && (entity_list.GetHatedCount(hate_top, this) == 0))) + StartFleeing(); + + } +} + + +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 && (SpecAttacks[IMMUNE_FLEEING] || spellbonuses.ImmuneToFlee) && !spellbonuses.IsFeared){ + curfp = false; + return; + } + + //see if we are still dying, if so, do nothing + if(GetHPRatio() < (float)RuleI(Combat, FleeHPRatio)) + 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... + curfp = false; + return; + } +} + +float Mob::GetFearSpeed() { + if(flee_mode) { + //we know ratio < FLEE_HP_RATIO + float speed = GetRunspeed(); + float ratio = GetHPRatio(); + + // mob's movement will halt with a decent snare at HP specified by rule. + if (ratio <= RuleI(Combat, FleeSnareHPRatio) && GetSnaredAmount() > 40) { + return 0.0001f; + } + + if (ratio < FLEE_HP_MINSPEED) + ratio = FLEE_HP_MINSPEED; + + speed = speed * 0.5 * ratio / 100; + + return(speed); + } + return(GetRunspeed()); +} + +void Mob::CalculateNewFearpoint() +{ + if(RuleB(Pathing, Fear) && zone->pathing) + { + int Node = zone->pathing->GetRandomPathNode(); + + VERTEX Loc = zone->pathing->GetPathNodeCoordinates(Node); + + ++Loc.z; + + VERTEX CurrentPosition(GetX(), GetY(), GetZ()); + + list Route = zone->pathing->FindRoute(CurrentPosition, Loc); + + if(Route.size() > 0) + { + fear_walkto_x = Loc.x; + fear_walkto_y = Loc.y; + fear_walkto_z = Loc.z; + curfp = true; + + mlog(PATHING__DEBUG, "Feared to node %i (%8.3f, %8.3f, %8.3f)", Node, Loc.x, Loc.y, Loc.z); + return; + } + + mlog(PATHING__DEBUG, "No path found to selected node. Falling through to old fear point selection."); + } + + int loop = 0; + float ranx, rany, ranz; + curfp = false; + while (loop < 100) //Max 100 tries + { + int ran = 250 - (loop*2); + loop++; + ranx = GetX()+MakeRandomInt(0, ran-1)-MakeRandomInt(0, ran-1); + rany = GetY()+MakeRandomInt(0, ran-1)-MakeRandomInt(0, ran-1); + ranz = FindGroundZ(ranx,rany); + if (ranz == -999999) + continue; + float fdist = ranz - GetZ(); + if (fdist >= -12 && fdist <= 12 && CheckCoordLosNoZLeaps(GetX(),GetY(),GetZ(),ranx,rany,ranz)) + { + curfp = true; + break; + } + } + if (curfp) + { + fear_walkto_x = ranx; + fear_walkto_y = rany; + fear_walkto_z = ranz; + } + else //Break fear + { + BuffFadeByEffect(SE_Fear); + } +} + +//we need to start acting scared... +//old fear function, kept for ref. +/*void Mob::SetFeared(Mob *caster, uint32 duration, bool flee) { + //special args to stop fear + if(caster == NULL && duration == 0) { + fear_state = fearStateNotFeared; +#ifdef FLEE_HP_RATIO + flee_mode = false; +#endif + safe_delete(fear_path_state); + return; + } + + flee_mode = flee; + + //fear dosent work without at least maps + if(zone->zonemap == NULL) { + fear_state = fearStateStuck; + return; //just stand there + } + + //if we are allready feared, and we are on a fear grid.. + //then just stay happy on the grid... + if(fear_path_state != NULL) { + if(fear_state != fearStateGrid) { + LogFile->write(EQEMuLog::Debug, "Umm... %s has a fear path state, but is not in a grid state. Wtf?", GetName()); + fear_state = fearStateGrid; + } + return; + } + + //try to run straight away from the caster + VERTEX hit, fear_vector; + if(FearTryStraight(caster, duration, flee, hit, fear_vector)) { + return; + } + + //OK, so if we just run, we are going to hit something... + //now we have to think a little more. + + //first, try to find a fear node that we can see. + if(zone->pathing != NULL) { + fear_path_state = new MobFearState(); + if(zone->pathing->FindNearestFear(fear_path_state, GetX(), GetY(), GetZ())) { +#ifdef FEAR_PATHING_DEBUG + LogFile->write(EQEMuLog::Debug, "Fear Pathing Start: found path, moving from (%.2f, %.2f, %.2f) to path node (%.2f, %.2f, %.2f)", + GetX(), GetY(), GetZ(), fear_path_state->x, fear_path_state->y, fear_path_state->z); +#endif + //we found a fear node... were on our way.. + cur_wp_x = fear_path_state->x; + cur_wp_y = fear_path_state->y; + cur_wp_z = fear_path_state->z; + fear_state = fearStateGrid; + return; + } + + //we have failed to find a path, so we dont need this.. + safe_delete(fear_path_state); + } + + //if we cannot just run, and we cannot see any paths, then + //we will give one last ditch effort to find a legit path. We + //will run as far as we can away from the player, and hope we + //can see a path from there if not, we will start breaking rules + +#ifdef FEAR_PATHING_DEBUG + LogFile->write(EQEMuLog::Debug, "Fear Pathing Start: Hope run from (%.2f, %.2f, %.2f), hit at (%.2f, %.2f, %.2f)", + GetX(), GetY(), GetZ(), hit.x, hit.y, hit.z); +#endif + //use the hit point - a little + a little Z as the first waypoint. + cur_wp_x = hit.x - fear_vector.x * 2; + cur_wp_y = hit.y - fear_vector.y * 2; + cur_wp_z = GetZ(); + fear_state = fearStateRunning; +} +//old fear function, kept for ref. +bool Mob::FearTryStraight(Mob *caster, uint32 duration, bool flee, VERTEX &hit, VERTEX &fear_vector) { + //gotta have somebody to run from + if(caster == NULL) + return(false); + + //our goal is to run along this vector... + fear_vector.x = GetX() - caster->GetX(); + fear_vector.y = GetY() - caster->GetY(); + fear_vector.z = 0; //I dont see any reason to use Z + float mag = sqrtf(fear_vector.x*fear_vector.x + fear_vector.y*fear_vector.y); + fear_vector.x /= mag; + fear_vector.y /= mag; + + //now see if we can just run without hitting anything... + VERTEX start, end; + start.x = GetX(); + start.y = GetY(); + start.z = GetZ() + 5.0; //raise up a little over small bumps + + //distance moved per movement tic. + float distance = NPC_SPEED_MULTIPLIER * GetFearSpeed(); + //times number of movement tics in the spell. + distance *= float(duration) / float(AImovement_duration); + + end.x = start.x + fear_vector.x * distance; + end.y = start.y + fear_vector.y * distance; + end.z = start.z; + + if(!zone->zonemap->LineIntersectsZone(start, end, 0.5, &hit, NULL)) { +#ifdef FEAR_PATHING_DEBUG + LogFile->write(EQEMuLog::Debug, "Fear Pathing Start: can run entire vector from (%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f)", + GetX(), GetY(), GetZ(), end.x, end.y, end.z); +#endif + //no hit, we can run this whole vector. + cur_wp_x = end.x; + cur_wp_y = end.y; + cur_wp_z = GetZ(); + fear_state = fearStateRunningForever; + return(true); //were done, nothing difficult needed. + } + + return(false); +} + +//old fear function, kept for ref. +void Mob::CalculateFearPosition() { + if(zone->zonemap == NULL || fear_state == fearStateStuck) { + return; //just stand there + } + + //This is the entire movement section, right here: + if (cur_wp_x != GetX() && cur_wp_y != GetY()) { + // not at waypoint yet, so keep moving + CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetFearSpeed(), true); + return; + } + + + + //we have reached our waypoint, now what? + //figure out a new waypoint to run at... + + if(fear_state == fearStateRunningForever) { + if(flee_mode) { + //a fleeing mob may run away again + VERTEX hit, fear_vector; + if(FearTryStraight(GetHateTop(), FLEE_RUN_DURATION, true, hit, fear_vector)) + return; //we are running again + //else, we need to find a grid, so act like we were on a hope run + fear_state = fearStateRunning; + } +#ifndef FORCE_FEAR_TO_RUN + else { + //we were supposed to run forever, but we did not... + //should re-fear ourself or something?? + fear_state = fearStateStuck; + return; + } +#endif + } + + //first see if we are on a path. if so our life is easy + if(fear_state == fearStateGrid && fear_path_state) { + //assume that we have zone->pathing since we got to this state. + if(!zone->pathing->NextFearPath(fear_path_state)) { + //this is bad, we were on a path and now its giving us + //an error... we dont have a good way to deal with this + fear_state = fearStateStuck; + return; + } +#ifdef FEAR_PATHING_DEBUG + LogFile->write(EQEMuLog::Debug, "Fear Pathing: on path, moving from (%.2f, %.2f, %.2f) to path node (%.2f, %.2f, %.2f)", + GetX(), GetY(), GetZ(), fear_path_state->x, fear_path_state->y, fear_path_state->z); +#endif + //we found a fear node... were on our way.. + cur_wp_x = fear_path_state->x; + cur_wp_y = fear_path_state->y; + cur_wp_z = fear_path_state->z; + + CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetFearSpeed(), true); + return; + } + + //the only valid state left is fearStateRunning, where we try to + //find a grid once we reach our waypoint, which we have.. + if(fear_state != fearStateRunning) { + //wtf... unknown state + LogFile->write(EQEMuLog::Debug, "Fear Pathing: Reached our fear waypoint, but we are in an unknown state %d... stopping.", fear_state); + fear_state = fearStateStuck; + return; + } + + //we wanted to try to find a waypoint now, so lets try.. + if(zone->pathing != NULL) { + fear_path_state = new MobFearState(); + + if(zone->pathing->FindNearestFear(fear_path_state, GetX(), GetY(), GetZ())) { +#ifdef FEAR_PATHING_DEBUG + LogFile->write(EQEMuLog::Debug, "Fear Pathing: ran to find path, moving from (%.2f, %.2f, %.2f) to path node (%.2f, %.2f, %.2f)", + GetX(), GetY(), GetZ(), fear_path_state->x, fear_path_state->y, fear_path_state->z); +#endif + //we found a fear node... were on our way.. + cur_wp_x = fear_path_state->x; + cur_wp_y = fear_path_state->y; + cur_wp_z = fear_path_state->z; + fear_state = fearStateGrid; + CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetFearSpeed(), true); + return; + } + + //if we get here... all valid methods have failed + +#ifdef FORCE_FEAR_TO_RUN + //ok, now we start making shit up + + //for now, we will limit our bullshitting to ignoring LOS + //when finding a pathing node, we SHOULD always get something.. + //do not force a path if we are fleeing + if(!flee_mode && zone->pathing->FindNearestFear(fear_path_state, GetX(), GetY(), GetZ(), false)) { +#ifdef FEAR_PATHING_DEBUG + LogFile->write(EQEMuLog::Debug, "Fear Pathing: Bullshit Path from (%.2f, %.2f, %.2f) to path node (%.2f, %.2f, %.2f)", + GetX(), GetY(), GetZ(), fear_path_state->x, fear_path_state->y, fear_path_state->z); +#endif + //we found a fear node... were on our way.. + cur_wp_x = fear_path_state->x; + cur_wp_y = fear_path_state->y; + cur_wp_z = fear_path_state->z; + fear_state = fearStateGrid; + CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetFearSpeed(), true); + return; + } +#endif //FORCE_FEAR_TO_RUN + + //we have failed to find a path once again, so we dont need this.. + safe_delete(fear_path_state); + } + + //if we get HERE... then NOTHING worked... just stick + fear_state = fearStateStuck; + + //end of function, everything else is #ifdef'd out +//} + +//I dont wanna get rid of this right now because it was a lot of hard +//work to write... but it dosent work reliably, so oh well.. +#ifdef OLD_FEAR_PATHING + /* + The idea... + + try to run along fear vector. + If we can see along it, run + otherwise, try to walk up a hill along the same vector + then try to move along a wall along largest component of FV + if cant move, change stae to stuck. + + once we know a place to run, use the waypoint code to do it + then if combat ends, we will reach the waypoint and + + + */ + /* + //first try our original fear vector again... + VERTEX start, end, hit, normalhit; + start.x = GetX() - fear_vector.x * 0.4; + start.y = GetY() - fear_vector.y * 0.4; + start.z = GetZ() + 6.0; //raise up a little over small bumps + + end.x = start.x + fear_vector.x * 10; + end.y = start.y + fear_vector.y * 10; + end.z = start.z; + + if(!zone->zonemap->LineIntersectsZone(start, end, 0.5, &normalhit, NULL)) { +#ifdef FEAR_PATHING_DEBUG + LogFile->write(EQEMuLog::Debug, "Fear Pathing: From (%.2f, %.2f, %.2f) normal run to (%.2f, %.2f, %.2f)", + GetX(), GetY(), GetZ(), end.x, end.y, end.z); +#endif + //we can run along this vector without hitting anything... + cur_wp_x = end.x; + cur_wp_y = end.y; + cur_wp_z = end.z - 6.0; + CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetRunspeed(), true); + return; + } + //see if we can make ANY useful progress along that vector + + //first, adjust normalhit to back up a little bit + //so we dont run through the wall + normalhit.x -= 0.4 * fear_vector.x; + normalhit.y -= 0.4 * fear_vector.y; + + float xd = normalhit.x - start.x; + if(xd < 0) + xd = 0 - xd; + float yd = normalhit.y - start.y; + if(yd < 0) + yd = 0 - yd; + + //this 2 is arbitrary + if((xd+yd) > 2.0) { +#ifdef FEAR_PATHING_DEBUG + LogFile->write(EQEMuLog::Debug, "Fear Pathing: From (%.2f, %.2f, %.2f) small run to (%.2f, %.2f, %.2f)", + GetX(), GetY(), GetZ(), cur_wp_x, cur_wp_y, cur_wp_z); +#endif + cur_wp_x = normalhit.x; + cur_wp_y = normalhit.y; + cur_wp_z = GetZ(); + + //try and fix up the Z coord if possible + //not sure if this is worth it, since it prolly isnt up much + + NodeRef c = zone->zonemap->SeekNode(zone->zonemap->GetRoot(), end.x, end.y); + if(c != NODE_NONE) { + cur_wp_z = zone->zonemap->FindBestZ(c, end, &hit, NULL); + if(cur_wp_z < start.z) + cur_wp_z = end.z; //revert on error + } + + CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetRunspeed(), true); + return; + } + +#ifdef FEAR_PATHING_DEBUG + LogFile->write(EQEMuLog::Debug, "Fear Pathing: From (%.2f, %.2f, %.2f) normal hit at (%.2f, %.2f, %.2f)", + GetX(), GetY(), GetZ(), normalhit.x, normalhit.y, normalhit.z); +#endif + + //if we get here, we cannot run along our normal vector... + //try up hill first + + /* + while this uphill stuff works great in outdoor zones, + it totally breaks dungeons... + + float speed = GetRunspeed(); + end.x = start.x + fear_vector.x * speed; + end.y = start.y + fear_vector.y * speed; + end.z = start.z + speed + speed; + + if(!zone->zonemap->LineIntersectsZone(start, end, 0.5, &hit, NULL)) { +#ifdef FEAR_PATHING_DEBUG + LogFile->write(EQEMuLog::Debug, "Fear Pathing: From (%.2f, %.2f, %.2f) up hill run to (%.2f, %.2f, %.2f)", + GetX(), GetY(), GetZ(), end.x, end.y, end.z); +#endif + //we can run along this vector without hitting anything... + cur_wp_x = end.x - 0.4 * fear_vector.x; + cur_wp_y = end.y - 0.4 * fear_vector.y; + cur_wp_z = end.z; + + //try and fix up the Z coord if possible + //not sure if this is worth it, since it prolly isnt up much + + NodeRef c = zone->zonemap->SeekNode(zone->zonemap->GetRoot(), end.x, end.y); + if(c != NODE_NONE) { + cur_wp_z = zone->zonemap->FindBestZ(c, end, &hit, NULL); + if(cur_wp_z < start.z) + cur_wp_z = end.z; //revert on error + } + + + CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetRunspeed(), true); + return; + } + */ + /* + //cant run along our vector at all.... + //one last ditch effort... try to move to the side a little + //along the minor component of the fear vector. + //try it in one direction first... + if(fear_vector.x < fear_vector.y) { + end.x = start.x + fear_vector.x * 3; + end.y = start.y; + } else { + end.x = start.x; + end.y = start.y + fear_vector.y * 3; + } + end.z = start.z + 3; //a little lift as always + + if(!zone->zonemap->LineIntersectsZone(start, end, 0.5, &hit, NULL)) { +#ifdef FEAR_PATHING_DEBUG + LogFile->write(EQEMuLog::Debug, "Fear Pathing: From (%.2f, %.2f, %.2f) strafe 1 to (%.2f, %.2f, %.2f)", + GetX(), GetY(), GetZ(), end.x, end.y, end.z); +#endif + //we can run along this vector without hitting anything... + cur_wp_x = end.x - 0.4 * fear_vector.x; + cur_wp_y = end.y - 0.4 * fear_vector.y; + cur_wp_z = end.z - 3; + CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetRunspeed(), true); + return; + } + + //now the other... + if(fear_vector.x < fear_vector.y) { + end.x = start.x + fear_vector.x * 3; + end.y = start.y; + } else { + end.x = start.x; + end.y = start.y + fear_vector.y * 3; + } + end.z = start.z + 3; //a little lift as always + + if(!zone->zonemap->LineIntersectsZone(start, end, 0.5, &hit, NULL)) { +#ifdef FEAR_PATHING_DEBUG + LogFile->write(EQEMuLog::Debug, "Fear Pathing: From (%.2f, %.2f, %.2f) strafe 2 to (%.2f, %.2f, %.2f)", + GetX(), GetY(), GetZ(), end.x, end.y, end.z); +#endif + //we can run along this vector without hitting anything... + cur_wp_x = end.x - 0.4 * fear_vector.x; + cur_wp_y = end.y - 0.4 * fear_vector.y; + cur_wp_z = end.z - 3; + CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetRunspeed(), true); + return; + } + + //if we get here... we have wasted enough CPU cycles + //just call it quits on fear pathing... + + //send them to normalhit and then stop + cur_wp_x = normalhit.x; + cur_wp_y = normalhit.y; + cur_wp_z = GetZ(); + fear_state = fearStateRunningToStick; +#ifdef FEAR_PATHING_DEBUG + LogFile->write(EQEMuLog::Debug, "Fear Pathing: From (%.2f, %.2f, %.2f) final move to (%.2f, %.2f, %.2f)", + GetX(), GetY(), GetZ(), normalhit.x, normalhit.y, normalhit.z); +#endif +#endif //OLD_FEAR_PATHING +}*/ + + + + + + + + + + + + + + diff --git a/zone/features.h b/zone/features.h new file mode 100644 index 000000000..f418f1bb5 --- /dev/null +++ b/zone/features.h @@ -0,0 +1,308 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 FEATURES_H +#define FEATURES_H + +/* + + This file defines many optional features for the emu + as well as various parameters used by the emu. + + If ambitious, most of these could prolly be turned into + database variables, but the really frequently run pieces + of code, should not be done that way for speed reasons IMO + +*/ + +/* + +Core Zone features + +*/ + + +//Uncomment this to cause a zone to basically idle +//when there are no players in it, mobs stop wandering, etc.. +#define IDLE_WHEN_EMPTY + +#ifdef EMBPERL +//Enable the new XS based perl parser +#define EMBPERL_XS + +//enable classes in the new XS based parser +#define EMBPERL_XS_CLASSES + +//enable IO capture and transmission to in game clients +//this seems to make perl very unhappy on reload, and crashes +#define EMBPERL_IO_CAPTURE + +//enable perl-based in-game command, pretty useless without EMBPERL_XS_CLASSES +#define EMBPERL_COMMANDS + +//enable #plugin and #peval, which requires IO::Stringy +//#define EMBPERL_EVAL_COMMANDS + +#endif + +/* + +Map Configuration +In general, these computations are expensive, so if you have performance +problems, consider turning them off. + +*/ + +//uncomment this to make the LOS code say all mobs can see all others +//when no map file is loaded, opposed to the default nobody-sees-anybody +#define LOS_DEFAULT_CAN_SEE + +/* + +Zone extensions and features + +*/ + +//Uncomment this to scale XP gained from MOBs based on their CON +//#define CON_XP_SCALING + +//Uncomment to make group buffs affect group pets +#define GROUP_BUFF_PETS + +//Uncomment this line to enable named quest files: +#define QUEST_SCRIPTS_BYNAME + +#ifdef QUEST_SCRIPTS_BYNAME +//extends byname system to look in a templates directory +//independant of zone name +#define QUEST_TEMPLATES_BYNAME +#define QUEST_TEMPLATES_DIRECTORY "templates" +#endif + +//the min ratio at which a mob's speed is reduced +#define FLEE_HP_MINSPEED 22 +//number of tics to try to run straight away before looking again +#define FLEE_RUN_DURATION 1000 +//number of miliseconds between when a mob will check its flee state +//this is only checked when the mob is damaged. +#define FLEE_CHECK_TIMER 2000 + +//enable functionality to send log message to the client +//it still needs to be enabled with the #logs command +#define CLIENT_LOGS + +//uncomment to print warnings about commands with 0 status... +//#define COMMANDS_WARNINGS + +//uncomment to allow perl commands to override compiled commands +#define COMMANDS_PERL_OVERRIDE + +//enable logging of commands used +#define COMMANDS_LOGGING + +//only log commands which require this minimum status or more +#define COMMANDS_LOGGING_MIN_STATUS 1 + +//path to where sql logs should be placed +#define SQL_LOG_PATH "sql_logs/" + +//New aggro system to reduce overhead. +#define REVERSE_AGGRO + +//Enable spacial queue to manage NPC update packets +//#define PACKET_UPDATE_MANAGER +//#define MANAGE_HP_UPDATES + +//The highest you can #setskill / #setallskill +#define HIGHEST_CAN_SET_SKILL 400 + +#define SKILL_MAX_LEVEL 75 + +//#define MIN_RANGED_ATK_RANGE 25 +//replaced the above define with RuleI(Combat, MinRangedAttackDist) + +/* + +Zone Numerical configuration + +*/ + +//Reuse times for various skills, here for convenience, in sec +//set to 0 to disable server side checking of timers. +enum { //reuse times + FeignDeathReuseTime = 9, + SneakReuseTime = 7, + HideReuseTime = 8, + TauntReuseTime = 5, + InstillDoubtReuseTime = 9, + FishingReuseTime = 11, + ForagingReuseTime = 50, + MendReuseTime = 290, + BashReuseTime = 5, + BackstabReuseTime = 9, + KickReuseTime = 5, + TailRakeReuseTime = 6, + EagleStrikeReuseTime = 5, + RoundKickReuseTime = 9, + TigerClawReuseTime = 6, + FlyingKickReuseTime = 7, + SenseTrapsReuseTime = 9, + DisarmTrapsReuseTime = 9, + HarmTouchReuseTime = 4300, + LayOnHandsReuseTime = 4300, + FrenzyReuseTime = 10 +}; + +enum { //timer settings, all in milliseconds + AImovement_duration = 100, + AIthink_duration = 150, + AIscanarea_delay = 500, + AIfeignremember_delay = 500, + AItarget_check_duration = 500, + AIClientScanarea_delay = 750, //used in REVERSE_AGGRO + AIassistcheck_delay = 3000, //now often a fighting NPC will yell for help + ClientProximity_interval = 1000, + 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. + EnragedTimer = 360000, + EnragedDurationTimer = 10000 +}; + +enum { //some random constants + //each of these attack modifiers are added to the NPC's level to determine their + //probability of executing such an attack (which may or may not hit) + NPCDualAttackModifier = 20, + NPCTripleAttackModifier = 0, + NPCQuadAttackModifier = -20 +}; + +//Max number of groups you can link with. Not tied to the client. +//if group linking is enabled above +#define MAX_GROUP_LINKS 8 + +//this is the number of levels above the thief's level that +//an npc can be and still let the theif PP them +#define THIEF_PICKPOCKET_OVER 5 + +//this is the % chance that an NPC will dual wield a 2nd weapon +//in its loot table, if it is able to. +//Aug 2007: was 5% chance.. changed to 100% by default since that seems more normal +//Kept it intact codewise incase someone wants to or is already using it. +#define NPC_DW_CHANCE 100 + +//This is the entry in npc_types to spawn for trap damagaes +#define TRAP_NPC_TYPE 1586 + +//This is the multiplier of eqemu speed to get client speed +//tweak this if pathing mobs seem to jump forward or backwards +//this should prolly be dynamic based on ping time or something.. who knows +//Values found in the emu somewhere at one point in time: 36, 43 +#define NPC_RUNANIM_RATIO 37 + +//this is used to multiply an NPCs movement rate, yeilding map units.. +#define NPC_SPEED_MULTIPLIER 46 //used to be 2.8... no idea why it changed + +//minimum level to do alchemy +#define MIN_LEVEL_ALCHEMY 25 + +//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 +#define MAX_NPC_FACTIONS 20 + +//value caps +#define MAX_FACTION 1500 +#define MIN_FACTION -1500 + +//The Level Cap: +//#define LEVEL_CAP RuleI(Character, MaxLevel) //hard cap is 127 +#define HARD_LEVEL_CAP 127 + +//the square of the maximum range at whihc you could possibly use NPC services (shop, tribute, etc) +#define USE_NPC_RANGE2 200*200 //arbitrary right now + +//the formula for experience for killing a mob. +//level is the only valid variable to use +#define EXP_FORMULA level*level*75*35/10 + +#define HIGHEST_AA_VALUE 35 + +//Leadership AA experience points +#define GROUP_EXP_PER_POINT 1000 +#define RAID_EXP_PER_POINT 2000 + +//Some hard coded statuses from commands and other places: +enum { + minStatusToBeGM = 40, + minStatusToUseGMCommands = 80, + minStatusToKick = 150, + minStatusToAvoidFalling = 100, + minStatusToHaveInvalidSpells = 80, + minStatusToHaveInvalidSkills = 80, + minStatusToIgnoreZoneFlags = 80, + minStatusToSeeOthersZoneFlags = 80, + minStatusToEditOtherGuilds = 80, + commandMovecharSelfOnly = 80, //below this == only self move allowed + commandMovecharToSpecials = 200, //ability to send people to cshom/load zones + commandZoneToSpecials = 80, //zone to cshome, out of load zones + commandToggleAI = 250, //can turn NPC AI on and off + commandCastSpecials = 100, //can cast special spells + commandInstacast = 100, //insta-cast all #casted spells + commandLevelAboveCap = 100, //can #level players above level cap + commandLevelNPCAboveCap = 100, //can #level NPCs above level cap + commandSetSkillsOther = 100, //ability to setskills on others + commandRaceOthers = 100, //ability to #race on others + commandGenderOthers = 100, //ability to #gender on others + commandTextureOthers = 100, //ability to #texture on others + commandDoAnimOthers = 100, //can #doanim on others + commandLockZones = 101, //can lock or unlock zones + commandEditPlayerCorpses = 150, //can Edit Player Corpses + 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 +}; + +//default states for logging flag on NPCs and clients (having NPCs on by default is prolly a bad idea) +#define CLIENT_DEFAULT_LOGGING_ENABLED true +#define NPC_DEFAULT_LOGGING_ENABLED false + + + +/* + +Developer configuration + +*/ + +//#define EQPROFILE +#ifdef EQPROFILE +//Enable the zone profiler +#define ZONE_PROFILE + +#define COMMON_PROFILE + +#define PROFILE_DUMP_TIME 3*60 +#endif //EQPROFILE + + + +#endif + diff --git a/zone/forage.cpp b/zone/forage.cpp new file mode 100644 index 000000000..4ffd969d6 --- /dev/null +++ b/zone/forage.cpp @@ -0,0 +1,487 @@ +/* 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 "../common/debug.h" +#include +using namespace std; +#include +#include + +#ifdef _WINDOWS +#define snprintf _snprintf +#endif + +#include "forage.h" +#include "entity.h" +#include "masterentity.h" +#include "npc.h" +#include "watermap.h" +#include "titles.h" +#include "StringIDs.h" +#include "../common/MiscFunctions.h" +#include "../common/rulesys.h" + +#include "zonedb.h" +#ifdef _WINDOWS +#define snprintf _snprintf +#endif + +#include "QuestParserCollection.h" + +//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; + + for (int c=0; c < FORAGE_ITEM_LIMIT; c++) { + item[c] = 0; + } + + 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); + return 0; + } + + if(chancepool == 0 || index < 1) + return(0); + + if(index == 1) { + return(item[0]); + } + + ret = 0; + + uint32 rindex = MakeRandomInt(1, chancepool); + + for(int i = 0; i < index; i++) { + if(rindex <= chance[i]) { + ret = item[i]; + break; + } + } + + return ret; +} + +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]; + uint32 npc_chances[50]; + uint32 chancepool = 0; + uint32 ret = 0; + + for (int c=0; c<50; c++) { + item[c]=0; + 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 { + cerr << "Error in Fishing query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return 0; + } + + 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; + } + + return ret; +} + +//we need this function to immediately determine, after we receive OP_Fishing, if we can even try to fish, otherwise we have to wait a while to get the failure +bool Client::CanFish() { + //make sure we still have a fishing pole on: + const ItemInst* Pole = m_inv[SLOT_PRIMARY]; + int32 bslot = m_inv.HasItemByUse(ItemTypeFishingBait, 1, invWhereWorn|invWherePersonal); + const ItemInst* Bait = NULL; + if(bslot != SLOT_INVALID) + Bait = m_inv.GetItem(bslot); + + if(!Pole || !Pole->IsType(ItemClassCommon) || Pole->GetItem()->ItemType != ItemTypeFishingPole) { + if (m_inv.HasItemByUse(ItemTypeFishingPole, 1, invWhereWorn|invWherePersonal|invWhereBank|invWhereSharedBank|invWhereTrading|invWhereCursor)) //We have a fishing pole somewhere, just not equipped + Message_StringID(MT_Skills, FISHING_EQUIP_POLE); //You need to put your fishing pole in your primary hand. + else //We don't have a fishing pole anywhere + Message_StringID(MT_Skills, FISHING_NO_POLE); //You can't fish without a fishing pole, go buy one. + return false; + } + + if (!Bait || !Bait->IsType(ItemClassCommon) || Bait->GetItem()->ItemType != ItemTypeFishingBait) { + Message_StringID(MT_Skills, FISHING_NO_BAIT); //You can't fish without fishing bait, go buy some. + return false; + } + + if(zone->zonemap!=NULL && zone->watermap != NULL && RuleB(Watermap, CheckForWaterWhenFishing)) { + float RodX, RodY, RodZ; + // Tweak Rod and LineLength if required + const float RodLength = RuleR(Watermap, FishingRodLength); + const float LineLength = RuleR(Watermap, FishingLineLength); + int HeadingDegrees; + + HeadingDegrees = (int) ((GetHeading()*360)/256); + HeadingDegrees = HeadingDegrees % 360; + + RodX = x_pos + RodLength * sin(HeadingDegrees * M_PI/180.0f); + RodY = y_pos + RodLength * cos(HeadingDegrees * M_PI/180.0f); + + // Do BestZ to find where the line hanging from the rod intersects the water (if it is water). + // and go 1 unit into the water. + VERTEX dest; + dest.x = RodX; + dest.y = RodY; + dest.z = z_pos+10; + NodeRef n = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), dest.x, dest.y); + if(n != NODE_NONE) { + RodZ = zone->zonemap->FindBestZ(n, dest, NULL, NULL) - 1; + 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); + if (in_lava) { + Message_StringID(MT_Skills, FISHING_LAVA); //Trying to catch a fire elemental or something? + return false; + } + if((!in_water) || (z_pos-RodZ)>LineLength) { //Didn't hit the water OR the water is too far below us + Message_StringID(MT_Skills, FISHING_LAND); //Trying to catch land sharks perhaps? + return false; + } + } + } + return true; +} + +void Client::GoFish() +{ + + //TODO: generate a message if we're already fishing + /*if (!fishing_timer.Check()) { //this isn't the right check, may need to add something to the Client class like 'bool is_fishing' + Message_StringID(0, ALREADY_FISHING); //You are already fishing! + return; + }*/ + + fishing_timer.Disable(); + + //we're doing this a second time (1st in Client::Handle_OP_Fishing) to make sure that, between when we started fishing & now, we're still able to fish (in case we move, change equip, etc) + if (!CanFish()) //if we can't fish here, we don't need to bother with the rest + return; + + //multiple entries yeilds higher probability of dropping... + uint32 common_fish_ids[MAX_COMMON_FISH_IDS] = { + 1038, // Tattered Cloth Sandals + 1038, // Tattered Cloth Sandals + 1038, // Tattered Cloth Sandals + 13019, // Fresh Fish + 13076, // Fish Scales + 13076, // Fish Scales + 7007, // Rusty Dagger + 7007, // Rusty Dagger + 7007 // Rusty Dagger + + }; + + //success formula is not researched at all + + int fishing_skill = GetSkill(FISHING); //will take into account skill bonuses on pole & bait + + //make sure we still have a fishing pole on: + int32 bslot = m_inv.HasItemByUse(ItemTypeFishingBait, 1, invWhereWorn|invWherePersonal); + const ItemInst* Bait = NULL; + if(bslot != SLOT_INVALID) + Bait = m_inv.GetItem(bslot); + + //if the bait isnt equipped, need to add its skill bonus + if(bslot >= IDX_INV && Bait->GetItem()->SkillModType == FISHING) { + fishing_skill += Bait->GetItem()->SkillModValue; + } + + if (fishing_skill > 100) + { + fishing_skill = 100+((fishing_skill-100)/2); + } + + if (MakeRandomInt(0,175) < fishing_skill) { + uint32 food_id = 0; + + //25% chance to fish an item. + if (MakeRandomInt(0, 399) <= fishing_skill ) { + uint32 npc_id = 0; + uint8 npc_chance = 0; + food_id = database.GetZoneFishing(m_pp.zone_id, fishing_skill, npc_id, npc_chance); + + //check for add NPC + if(npc_chance > 0 && npc_id) { + if(npc_chance < MakeRandomInt(0, 99)) { + const NPCType* tmp = database.GetNPCType(npc_id); + if(tmp != NULL) { + NPC* npc = new NPC(tmp, NULL, GetX()+3, GetY(), GetZ(), GetHeading(), FlyMode3); + npc->AddLootTable(); + + npc->AddToHateList(this, 1, 0, false); //no help yelling + + entity_list.AddNPC(npc); + + Message(MT_Emote, "You fish up a little more than you bargained for..."); + } + } + } + } + + //consume bait, should we always consume bait on success? + DeleteItemInInventory(bslot, 1, true); //do we need client update? + + if(food_id == 0) { + int index = MakeRandomInt(0, MAX_COMMON_FISH_IDS-1); + food_id = common_fish_ids[index]; + } + + const Item_Struct* food_item = database.GetItem(food_id); + + Message_StringID(MT_Skills, FISHING_SUCCESS); + const ItemInst* inst = database.CreateItem(food_item, 1); + if(inst != NULL) { + if(CheckLoreConflict(inst->GetItem())) + { + this->Message_StringID(0,DUP_LORE); + } + else + { + PushItemOnCursor(*inst); // changed from PutItemInInventory(SLOT_CURSOR, *inst); - was additional overhead + SendItemPacket(SLOT_CURSOR,inst,ItemPacketSummonItem); + if(RuleB(TaskSystem, EnableTaskSystem)) + UpdateTasksForItem(ActivityFish, food_id); + } + safe_delete(inst); + } + + parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst != NULL ? inst->GetItem()->ID : 0); + } + else + { + //chance to use bait when you dont catch anything... + if (MakeRandomInt(0, 4) == 1) { + DeleteItemInInventory(bslot, 1, true); //do we need client update? + Message_StringID(MT_Skills, FISHING_LOST_BAIT); //You lost your bait! + } else { + if (MakeRandomInt(0, 15) == 1) //give about a 1 in 15 chance to spill your beer. we could make this a rule, but it doesn't really seem worth it + //TODO: check for & consume an alcoholic beverage from inventory when this triggers, and set it as a rule that's disabled by default + Message_StringID(MT_Skills, FISHING_SPILL_BEER); //You spill your beer while bringing in your line. + else + Message_StringID(MT_Skills, FISHING_FAILED); //You didn't catch anything. + } + + parse->EventPlayer(EVENT_FISH_FAILURE, this, "", 0); + } + + //chance to break fishing pole... + //this is potentially exploitable in that they can fish + //and then swap out items in primary slot... too lazy to fix right now + if (MakeRandomInt(0, 49) == 1) { + Message_StringID(MT_Skills, FISHING_POLE_BROKE); //Your fishing pole broke! + DeleteItemInInventory(13,0,true); + } + + if(CheckIncreaseSkill(FISHING, NULL, 5)) + { + if(title_manager.IsNewTradeSkillTitleAvailable(FISHING, GetRawSkill(FISHING))) + NotifyNewTitlesAvailable(); + } +} + +void Client::ForageItem() { + + int skill_level = GetSkill(FORAGE); + + //be wary of the string ids in switch below when changing this. + uint32 common_food_ids[MAX_COMMON_FOOD_IDS] = { + 13046, // Fruit + 13045, // Berries + 13419, // Vegetables + 13048, // Rabbit Meat + 13047, // Roots + 13044, // Pod Of Water + 14905, // mushroom + 13106 // Fishing Grubs + }; + + // these may need to be fine tuned, I am just guessing here + if (MakeRandomInt(0,199) < skill_level) { + uint32 foragedfood = 0; + uint32 stringid = FORAGE_NOEAT; + + if (MakeRandomInt(0,99) <= 25) { + foragedfood = database.GetZoneForage(m_pp.zone_id, skill_level); + } + + //not an else in case theres no DB food + if(foragedfood == 0) { + uint8 index = 0; + index = MakeRandomInt(0, MAX_COMMON_FOOD_IDS-1); + foragedfood = common_food_ids[index]; + } + + const Item_Struct* food_item = database.GetItem(foragedfood); + + if(!food_item) { + LogFile->write(EQEMuLog::Error, "NULL returned from database.GetItem in ClientForageItem"); + return; + } + + if(foragedfood == 13106) + stringid = FORAGE_GRUBS; + else + switch(food_item->ItemType) { + + case ItemTypeFood: + stringid = FORAGE_FOOD; + break; + + case ItemTypeDrink: + if(strstr(food_item->Name, "ater")) + stringid = FORAGE_WATER; + else + stringid = FORAGE_DRINK; + break; + default: + break; + } + + Message_StringID(MT_Skills, stringid); + const ItemInst* inst = database.CreateItem(food_item, 1); + if(inst != NULL) { + // check to make sure it isn't a foraged lore item + if(CheckLoreConflict(inst->GetItem())) + { + this->Message_StringID(0,DUP_LORE); + } + else { + PushItemOnCursor(*inst); + SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem); + if(RuleB(TaskSystem, EnableTaskSystem)) + UpdateTasksForItem(ActivityForage, foragedfood); + } + safe_delete(inst); + } + + parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst != NULL ? inst->GetItem()->ID : 0); + + } else { + Message_StringID(MT_Skills, FORAGE_FAILED); + parse->EventPlayer(EVENT_FORAGE_FAILURE, this, "", 0); + } + + CheckIncreaseSkill(FORAGE, NULL, 5); + +} diff --git a/zone/forage.h b/zone/forage.h new file mode 100644 index 000000000..1ec559a25 --- /dev/null +++ b/zone/forage.h @@ -0,0 +1,26 @@ +/* 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 FORAGE_H +#define FORAGE_H +#include "../common/types.h" + +#define MAX_COMMON_FOOD_IDS 8 +#define MAX_COMMON_FISH_IDS 9 + +#endif diff --git a/zone/groups.cpp b/zone/groups.cpp new file mode 100644 index 000000000..53095b74d --- /dev/null +++ b/zone/groups.cpp @@ -0,0 +1,2134 @@ +/* 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 "../common/debug.h" +#include "masterentity.h" +#include "NpcAI.h" +#include "../common/packet_functions.h" +#include "../common/packet_dump.h" +#include "../common/MiscFunctions.h" +#include "worldserver.h" +extern EntityList entity_list; +extern WorldServer worldserver; + +// +// Xorlac: This will need proper synchronization to make it work correctly. +// Also, should investigate client ack for packet to ensure proper synch. +// + +/* + +note about how groups work: +A group contains 2 list, a list of pointers to members and a +list of member names. All members of a group should have their +name in the membername array, wether they are in the zone or not. +Only members in this zone will have non-null pointers in the +members array. + +*/ + +//create a group which should allready exist in the database +Group::Group(uint32 gid) +: GroupIDConsumer(gid) +{ + leader = NULL; + memset(members,0,sizeof(Mob*) * MAX_GROUP_MEMBERS); + AssistTargetID = 0; + TankTargetID = 0; + PullerTargetID = 0; + + memset(&LeaderAbilities, 0, sizeof(GroupLeadershipAA_Struct)); + uint32 i; + for(i=0;iSetGrouped(true); + SetLeader(leader); + AssistTargetID = 0; + TankTargetID = 0; + PullerTargetID = 0; + memset(&LeaderAbilities, 0, sizeof(GroupLeadershipAA_Struct)); + uint32 i; + for(i=0;iGetName()); + + if(leader->IsClient()) + strcpy(leader->CastToClient()->GetPP().groupMembers[0],leader->GetName()); + + for(int i = 0; i < MAX_MARKED_NPCS; ++i) + MarkedNPCs[i] = 0; + + NPCMarkerID = 0; +} + +Group::~Group() +{ + for(int i = 0; i < MAX_MARKED_NPCS; ++i) + if(MarkedNPCs[i]) + { + Mob* m = entity_list.GetMob(MarkedNPCs[i]); + if(m) + m->IsTargeted(-1); + } +} + +//Cofruben:Split money used in OP_Split. +//Rewritten by Father Nitwit +void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter) { + //avoid unneeded work + if(copper == 0 && silver == 0 && gold == 0 && platinum == 0) + return; + + uint32 i; + uint8 membercount = 0; + for (i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (members[i] != NULL) { + + membercount++; + } + } + + if (membercount == 0) + return; + + uint32 mod; + //try to handle round off error a little better + if(membercount > 1) { + mod = platinum % membercount; + if((mod) > 0) { + platinum -= mod; + gold += 10 * mod; + } + mod = gold % membercount; + if((mod) > 0) { + gold -= mod; + silver += 10 * mod; + } + mod = silver % membercount; + if((mod) > 0) { + silver -= mod; + copper += 10 * mod; + } + } + + //calculate the splits + //We can still round off copper pieces, but I dont care + uint32 sc; + uint32 cpsplit = copper / membercount; + sc = copper % membercount; + uint32 spsplit = silver / membercount; + uint32 gpsplit = gold / membercount; + uint32 ppsplit = platinum / membercount; + + char buf[128]; + buf[63] = '\0'; + string msg = "You receive"; + bool one = false; + + if(ppsplit > 0) { + snprintf(buf, 63, " %u platinum", ppsplit); + msg += buf; + one = true; + } + if(gpsplit > 0) { + if(one) + msg += ","; + snprintf(buf, 63, " %u gold", gpsplit); + msg += buf; + one = true; + } + if(spsplit > 0) { + if(one) + msg += ","; + snprintf(buf, 63, " %u silver", spsplit); + msg += buf; + one = true; + } + if(cpsplit > 0) { + if(one) + msg += ","; + //this message is not 100% accurate for the splitter + //if they are receiving any roundoff + snprintf(buf, 63, " %u copper", cpsplit); + msg += buf; + one = true; + } + msg += " as your split"; + + for (i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (members[i] != NULL && members[i]->IsClient()) { // 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()); + } + } +} + +bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 CharacterID) +{ + bool InZone = true; + bool ismerc = false; + + // This method should either be passed a Mob*, if the new member is in this zone, or a NULL Mob* + // and the name and CharacterID of the new member, if they are out of zone. + // + if(!newmember && !NewMemberName) + 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(); + if(owner) + { + CharacterID = owner->CastToClient()->CharacterID(); + NewMemberName = newmember->GetName(); + ismerc = true; + } + } + } + + uint32 i = 0; + + // See if they are already in the group + // + 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) + { + if (membername[i][0] == '\0') + { + if(InZone) + members[i] = newmember; + + break; + } + } + + if (i == MAX_GROUP_MEMBERS) + return false; + + strcpy(membername[i], NewMemberName); + MemberRoles[i] = 0; + + int x=1; + + //build the template join packet + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); + 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] != NULL && members[i] != newmember) { + //fill in group join & send it + if(members[i]->IsMerc()) + { + strcpy(gj->yourname, members[i]->GetName()); + } + else + { + strcpy(gj->yourname, members[i]->GetCleanName()); + } + if(members[i]->IsClient()) { + members[i]->CastToClient()->QueuePacket(outapp); + + //put new member into existing person's list + strcpy(members[i]->CastToClient()->GetPP().groupMembers[this->GroupCount()-1], NewMemberName); + } + + //put this existing person into the new member's list + if(InZone && newmember->IsClient()) { + if(IsLeader(members[i])) + strcpy(newmember->CastToClient()->GetPP().groupMembers[0], members[i]->GetCleanName()); + else { + strcpy(newmember->CastToClient()->GetPP().groupMembers[x], members[i]->GetCleanName()); + x++; + } + } + } + } + + if(InZone) + { + //put new member in his own list. + newmember->SetGrouped(true); + + if(newmember->IsClient()) + { + strcpy(newmember->CastToClient()->GetPP().groupMembers[x], NewMemberName); + newmember->CastToClient()->Save(); + database.SetGroupID(NewMemberName, GetID(), newmember->CastToClient()->CharacterID(), false); + SendMarkedNPCsToMember(newmember->CastToClient()); + + NotifyMainTank(newmember->CastToClient(), 1); + NotifyMainAssist(newmember->CastToClient(), 1); + NotifyPuller(newmember->CastToClient(), 1); + } + + if(newmember->IsMerc()) + { + Client* owner = newmember->CastToMerc()->GetMercOwner(); + if(owner) + { + database.SetGroupID(newmember->GetName(), GetID(), owner->CharacterID(), true); + } + } +#ifdef BOTS + for (i = 0;i < MAX_GROUP_MEMBERS; i++) { + if (members[i] != NULL && members[i]->IsBot()) { + members[i]->CastToBot()->CalcChanceToCast(); + } + } +#endif //BOTS + } + else + database.SetGroupID(NewMemberName, GetID(), CharacterID, ismerc); + + safe_delete(outapp); + + return true; +} + +void Group::AddMember(const char *NewMemberName) +{ + // This method should be called when both the new member and the group leader are in a different zone to this one. + // + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + if(!strcasecmp(membername[i], NewMemberName)) + { + return; + } + + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + { + if (membername[i][0] == '\0') + { + strcpy(membername[i], NewMemberName); + MemberRoles[i] = 0; + break; + } + } +} + + +void Group::QueuePacket(const EQApplicationPacket *app, bool ack_req) +{ + uint32 i; + for(i = 0; i < MAX_GROUP_MEMBERS; i++) + if(members[i] && members[i]->IsClient()) + members[i]->CastToClient()->QueuePacket(app, ack_req); +} + +// solar: sends the rest of the group's hps to member. this is useful when +// someone first joins a group, but otherwise there shouldn't be a need to +// call it +void Group::SendHPPacketsTo(Mob *member) +{ + if(member && member->IsClient()) + { + EQApplicationPacket hpapp; + EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct)); + + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) + { + if(members[i] && members[i] != member) + { + members[i]->CreateHPPacket(&hpapp); + member->CastToClient()->QueuePacket(&hpapp, false); + if(member->CastToClient()->GetClientVersion() >= EQClientSoD) + { + outapp.SetOpcode(OP_MobManaUpdate); + MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp.pBuffer; + mmus->spawn_id = members[i]->GetID(); + mmus->mana = members[i]->GetManaPercent(); + member->CastToClient()->QueuePacket(&outapp, false); + MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp.pBuffer; + outapp.SetOpcode(OP_MobEnduranceUpdate); + meus->endurance = members[i]->GetEndurancePercent(); + member->CastToClient()->QueuePacket(&outapp, false); + } + } + } + } +} + +void Group::SendHPPacketsFrom(Mob *member) +{ + EQApplicationPacket hp_app; + if(!member) + return; + + member->CreateHPPacket(&hp_app); + EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct)); + + uint32 i; + for(i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(members[i] && members[i] != member && members[i]->IsClient()) + { + members[i]->CastToClient()->QueuePacket(&hp_app); + if(members[i]->CastToClient()->GetClientVersion() >= EQClientSoD) + { + outapp.SetOpcode(OP_MobManaUpdate); + MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp.pBuffer; + mmus->spawn_id = member->GetID(); + mmus->mana = member->GetManaPercent(); + members[i]->CastToClient()->QueuePacket(&outapp, false); + MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp.pBuffer; + outapp.SetOpcode(OP_MobEnduranceUpdate); + meus->endurance = member->GetEndurancePercent(); + members[i]->CastToClient()->QueuePacket(&outapp, false); + } + } + } +} + +//updates a group member's client pointer when they zone in +//if the group was in the zone allready +bool Group::UpdatePlayer(Mob* update){ + + VerifyGroup(); + + uint32 i=0; + if(update->IsClient()) { + //update their player profile + PlayerProfile_Struct &pp = update->CastToClient()->GetPP(); + for (i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(membername[0] == '\0') + memset(pp.groupMembers[i], 0, 64); + else + strn0cpy(pp.groupMembers[i], membername[i], 64); + } + if(IsNPCMarker(update->CastToClient())) + { + NPCMarkerID = update->GetID(); + SendLeadershipAAUpdate(); + } + } + + for (i = 0; i < MAX_GROUP_MEMBERS; i++) + { + if (!strcasecmp(membername[i],update->GetName())) + { + members[i] = update; + members[i]->SetGrouped(true); + return true; + } + } + return false; +} + + +void Group::MemberZoned(Mob* removemob) { + uint32 i; + + if (removemob == NULL) + return; + + if(removemob == GetLeader()) + SetLeader(NULL); + + for (i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (members[i] == removemob) { + members[i] = NULL; + //should NOT clear the name, it is used for world communication. + break; + } +#ifdef BOTS + if (members[i] != NULL && members[i]->IsBot()) { + members[i]->CastToBot()->CalcChanceToCast(); + } +#endif //BOTS + } + if(removemob->IsClient() && HasRole(removemob, RoleAssist)) + SetGroupAssistTarget(0); + + if(removemob->IsClient() && HasRole(removemob, RoleTank)) + SetGroupTankTarget(0); + + if(removemob->IsClient() && HasRole(removemob, RolePuller)) + SetGroupPullerTarget(0); +} + +bool Group::DelMemberOOZ(const char *Name) { + + if(!Name) return false; + + // If a member out of zone has disbanded, clear out their name. + // + for(unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(!strcasecmp(Name, membername[i])) + // This shouldn't be called if the member is in this zone. + if(!members[i]) { + if(!strncmp(GetLeaderName(), Name, 64)) + { + //TODO: Transfer leadership if leader disbands OOZ. + UpdateGroupAAs(); + } + + memset(membername[i], 0, 64); + MemberRoles[i] = 0; + if(GroupCount() < 3) + { + UnDelegateMarkNPC(NPCMarkerName.c_str()); + if(GetLeader() && GetLeader()->IsClient() && GetLeader()->CastToClient()->GetClientVersion() < EQClientSoD) { + UnDelegateMainAssist(MainAssistName.c_str()); + } + ClearAllNPCMarks(); + } + return true; + } + } + + return false; +} + +bool Group::DelMember(Mob* oldmember,bool ignoresender) +{ + if (oldmember == NULL){ + return false; + } + + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (members[i] == oldmember) { + members[i] = NULL; + membername[i][0] = '\0'; + memset(membername[i],0,64); + MemberRoles[i] = 0; + break; + } + } + + //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; + } + } + } + + ServerPacket* pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct)); + ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer; + gl->gid = GetID(); + gl->zoneid = zone->GetZoneID(); + gl->instance_id = zone->GetInstanceID(); + strcpy(gl->member_name, oldmember->GetName()); + worldserver.SendPacket(pack); + safe_delete(pack); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); + GroupJoin_Struct* gu = (GroupJoin_Struct*) outapp->pBuffer; + gu->action = groupActLeave; + strcpy(gu->membername, oldmember->GetCleanName()); + strcpy(gu->yourname, oldmember->GetCleanName()); + + gu->leader_aas = LeaderAbilities; + + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (members[i] == NULL) { + //if (DEBUG>=5) LogFile->write(EQEMuLog::Debug, "Group::DelMember() null member at slot %i", i); + continue; + } + if (members[i] != oldmember) { + strcpy(gu->yourname, members[i]->GetCleanName()); + if(members[i]->IsClient()) + members[i]->CastToClient()->QueuePacket(outapp); + } +#ifdef BOTS + if (members[i] != NULL && members[i]->IsBot()) { + members[i]->CastToBot()->CalcChanceToCast(); + } +#endif //BOTS + } + + if (!ignoresender) { + strcpy(gu->yourname,oldmember->GetCleanName()); + strcpy(gu->membername,oldmember->GetCleanName()); + gu->action = groupActLeave; + + if(oldmember->IsClient()) + oldmember->CastToClient()->QueuePacket(outapp); + } + + if(oldmember->IsClient()) + database.SetGroupID(oldmember->GetCleanName(), 0, oldmember->CastToClient()->CharacterID()); + + oldmember->SetGrouped(false); + disbandcheck = true; + + safe_delete(outapp); + + if(HasRole(oldmember, RoleTank)) + { + SetGroupTankTarget(0); + UnDelegateMainTank(oldmember->GetName()); + } + + if(HasRole(oldmember, RoleAssist)) + { + SetGroupAssistTarget(0); + UnDelegateMainAssist(oldmember->GetName()); + } + + if(HasRole(oldmember, RolePuller)) + { + SetGroupPullerTarget(0); + UnDelegatePuller(oldmember->GetName()); + } + + if(oldmember->IsClient()) + SendMarkedNPCsToMember(oldmember->CastToClient(), true); + + if(GroupCount() < 3) + { + UnDelegateMarkNPC(NPCMarkerName.c_str()); + if(GetLeader() && GetLeader()->IsClient() && GetLeader()->CastToClient()->GetClientVersion() < EQClientSoD) { + UnDelegateMainAssist(MainAssistName.c_str()); + } + ClearAllNPCMarks(); + } + + return true; +} + +// does the caster + group +void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { + uint32 z; + float range, distance; + + if(!caster) + return; + + castspell = true; + range = caster->GetAOERange(spell_id); + + float range2 = range*range; + +// caster->SpellOnTarget(spell_id, caster); + + for(z=0; z < MAX_GROUP_MEMBERS; z++) + { + if(members[z] == caster) { + caster->SpellOnTarget(spell_id, caster); +#ifdef GROUP_BUFF_PETS + if(caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) + caster->SpellOnTarget(spell_id, caster->GetPet()); +#endif + } + else if(members[z] != NULL) + { + distance = caster->DistNoRoot(*members[z]); + if(distance <= range2) { + caster->SpellOnTarget(spell_id, members[z]); +#ifdef GROUP_BUFF_PETS + if(members[z]->GetPet() && members[z]->HasPetAffinity() && !members[z]->GetPet()->IsCharmed()) + caster->SpellOnTarget(spell_id, members[z]->GetPet()); +#endif + } else + _log(SPELLS__CASTING, "Group spell: %s is out of range %f at distance %f from %s", members[z]->GetName(), range, distance, caster->GetName()); + } + } + + castspell = false; + disbandcheck = true; +} + +// does the caster + group +void Group::GroupBardPulse(Mob* caster, uint16 spell_id) { + uint32 z; + float range, distance; + + if(!caster) + return; + + castspell = true; + range = caster->GetAOERange(spell_id); + + float range2 = range*range; + + for(z=0; z < MAX_GROUP_MEMBERS; z++) { + if(members[z] == caster) { + caster->BardPulse(spell_id, caster); +#ifdef GROUP_BUFF_PETS + if(caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) + caster->BardPulse(spell_id, caster->GetPet()); +#endif + } + else if(members[z] != NULL) + { + distance = caster->DistNoRoot(*members[z]); + if(distance <= range2) { + members[z]->BardPulse(spell_id, caster); +#ifdef GROUP_BUFF_PETS + if(members[z]->GetPet() && members[z]->HasPetAffinity() && !members[z]->GetPet()->IsCharmed()) + members[z]->GetPet()->BardPulse(spell_id, caster); +#endif + } else + _log(SPELLS__BARDS, "Group bard pulse: %s is out of range %f at distance %f from %s", members[z]->GetName(), range, distance, caster->GetName()); + } + } +} + +bool Group::IsGroupMember(Mob* client) +{ + bool Result = false; + + if(client) { + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (members[i] == client) + Result = true; + } + } + + return Result; +} + +bool Group::IsGroupMember(const char *Name) +{ + if(Name) + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) + if((strlen(Name) == strlen(membername[i])) && !strncmp(membername[i], Name, strlen(Name))) + return true; + + return false; +} + +void Group::GroupMessage(Mob* sender, uint8 language, uint8 lang_skill, const char* message) { + uint32 i; + for (i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(!members[i]) + continue; + + if (members[i]->IsClient() && members[i]->CastToClient()->GetFilter(FILTER_GROUP)!=0) + members[i]->CastToClient()->ChannelMessageSend(sender->GetName(),members[i]->GetName(),2,language,lang_skill,message); + } + + ServerPacket* pack = new ServerPacket(ServerOP_OOZGroupMessage, sizeof(ServerGroupChannelMessage_Struct) + strlen(message) + 1); + ServerGroupChannelMessage_Struct* gcm = (ServerGroupChannelMessage_Struct*)pack->pBuffer; + gcm->zoneid = zone->GetZoneID(); + gcm->groupid = GetID(); + gcm->instanceid = zone->GetInstanceID(); + strcpy(gcm->from, sender->GetName()); + strcpy(gcm->message, message); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +uint32 Group::GetTotalGroupDamage(Mob* other) { + uint32 total = 0; + + uint32 i; + for (i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(!members[i]) + continue; + if (other->CheckAggro(members[i])) + total += other->GetHateAmount(members[i],true); + } + return total; +} + +void Group::DisbandGroup() { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupUpdate_Struct)); + + GroupUpdate_Struct* gu = (GroupUpdate_Struct*) outapp->pBuffer; + gu->action = groupActDisband; + + Client *Leader = NULL; + + uint32 i; + for (i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (members[i] == NULL) { + continue; + } + + 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()); + members[i]->CastToClient()->QueuePacket(outapp); + SendMarkedNPCsToMember(members[i]->CastToClient(), true); + + } + + members[i]->SetGrouped(false); + members[i] = NULL; + membername[i][0] = '\0'; + } + + ClearAllNPCMarks(); + + ServerPacket* pack = new ServerPacket(ServerOP_DisbandGroup, sizeof(ServerDisbandGroup_Struct)); + ServerDisbandGroup_Struct* dg = (ServerDisbandGroup_Struct*)pack->pBuffer; + dg->zoneid = zone->GetZoneID(); + dg->groupid = GetID(); + dg->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); + + entity_list.RemoveGroup(GetID()); + if(GetID() != 0) + database.ClearGroup(GetID()); + + if(Leader && (Leader->IsLFP())) { + Leader->UpdateLFP(); + } + + safe_delete(outapp); +} + +bool Group::Process() { + if(disbandcheck && !GroupCount()) + return false; + else if(disbandcheck && GroupCount()) + disbandcheck = false; + return true; +} + +void Group::SendUpdate(uint32 type, Mob* member) +{ + if(!member->IsClient()) + return; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupUpdate2_Struct)); + GroupUpdate2_Struct* gu = (GroupUpdate2_Struct*)outapp->pBuffer; + gu->action = type; + strcpy(gu->yourname,member->GetName()); + + int x = 0; + + gu->leader_aas = LeaderAbilities; + + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + if((members[i] != NULL) && IsLeader(members[i])) + { + strcpy(gu->leadersname, members[i]->GetName()); + break; + } + + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + if (members[i] != NULL && members[i] != member) + strcpy(gu->membername[x++], members[i]->GetName()); + + member->CastToClient()->QueuePacket(outapp); + + safe_delete(outapp); +} + +void Group::SendLeadershipAAUpdate() +{ + // This method updates other members of the group in the current zone with the Leader's group leadership AAs. + // + // It is called when the leader purchases a leadership AA or enters a zone. + // + // If a group member is not in the same zone as the leader when the leader purchases a new AA, they will not become + // aware of it until they are next in the same zone as the leader. + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); + + GroupJoin_Struct* gu = (GroupJoin_Struct*)outapp->pBuffer; + + gu->action = groupActAAUpdate; + + uint32 i = 0; + + gu->leader_aas = LeaderAbilities; + + gu->NPCMarkerID = GetNPCMarkerID(); + + for (i = 0;i < MAX_GROUP_MEMBERS; ++i) + if(members[i] && members[i]->IsClient()) + { + strcpy(gu->yourname, members[i]->GetName()); + strcpy(gu->membername, members[i]->GetName()); + members[i]->CastToClient()->QueuePacket(outapp); + } + + safe_delete(outapp); +} + +uint8 Group::GroupCount() { + + uint8 MemberCount = 0; + + for(uint8 i = 0; i < MAX_GROUP_MEMBERS; ++i) + if(membername[i][0]) + ++MemberCount; + + return MemberCount; +} + +uint32 Group::GetHighestLevel() +{ +uint32 level = 1; +uint32 i; + for (i = 0; i < MAX_GROUP_MEMBERS; i++) + { + if (members[i]) + { + if(members[i]->GetLevel() > level) + level = members[i]->GetLevel(); + } + } + return level; +} +uint32 Group::GetLowestLevel() +{ +uint32 level = 255; +uint32 i; + for (i = 0; i < MAX_GROUP_MEMBERS; i++) + { + if (members[i]) + { + if(members[i]->GetLevel() < level) + level = members[i]->GetLevel(); + } + } + return level; +} + +void Group::TeleportGroup(Mob* sender, uint32 zoneID, uint16 instance_id, float x, float y, float z, float heading) +{ + uint32 i; + for (i = 0; i < MAX_GROUP_MEMBERS; i++) + { + if (members[i] != NULL && members[i]->IsClient() && members[i] != sender) + { + members[i]->CastToClient()->MovePC(zoneID, instance_id, x, y, z, heading, 0, ZoneSolicited); + } + } +} + +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] = NULL; + strn0cpy(membername[i], row[0], 64); + + i++; + } + mysql_free_result(result); + } + + return(true); +} + +void Group::VerifyGroup() { + /* + The purpose of this method is to make sure that a group + is in a valid state, to prevent dangling pointers. + Only called every once in a while (on member re-join for now). + */ + + uint32 i; + for (i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (membername[i][0] == '\0') { +#if EQDEBUG >= 7 +LogFile->write(EQEMuLog::Debug, "Group %lu: Verify %d: Empty.\n", (unsigned long)GetID(), i); +#endif + members[i] = NULL; + continue; + } + + //it should be safe to use GetClientByName, but Group is trying + //to be generic, so we'll go for general Mob + Mob *them = entity_list.GetMob(membername[i]); + if(them == NULL && members[i] != NULL) { //they arnt here anymore.... +#if EQDEBUG >= 6 + LogFile->write(EQEMuLog::Debug, "Member of group %lu named '%s' has disappeared!!", (unsigned long)GetID(), membername[i]); +#endif + membername[i][0] = '\0'; + members[i] = NULL; + continue; + } + + if(them != NULL && members[i] != them) { //our pointer is out of date... not so good. +#if EQDEBUG >= 5 + LogFile->write(EQEMuLog::Debug, "Member of group %lu named '%s' had an out of date pointer!!", (unsigned long)GetID(), membername[i]); +#endif + members[i] = them; + continue; + } +#if EQDEBUG >= 8 + LogFile->write(EQEMuLog::Debug, "Member of group %lu named '%s' is valid.", (unsigned long)GetID(), membername[i]); +#endif + } +} + + +void Group::GroupMessage_StringID(Mob* sender, uint32 type, uint32 string_id, const char* message,const char* message2,const char* message3,const char* message4,const char* message5,const char* message6,const char* message7,const char* message8,const char* message9, uint32 distance) { + uint32 i; + for (i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(members[i] == NULL) + continue; + + if(members[i] == sender) + continue; + + members[i]->Message_StringID(type, string_id, message, message2, message3, message4, message5, message6, message7, message8, message9, 0); + } +} + + + +void Client::LeaveGroup() { + Group *g = GetGroup(); + + if(g) { + if(g->GroupCount() < 3) + g->DisbandGroup(); + else + g->DelMember(this); + } else { + //force things a little + database.SetGroupID(GetName(), 0, CharacterID()); + } + + isgrouped = false; +} + +void Group::HealGroup(uint32 heal_amt, Mob* caster) +{ + if (!caster) + return; + + int numMem = 0; + unsigned int gi = 0; + for(; gi < MAX_GROUP_MEMBERS; gi++) + { + if(members[gi]){ + numMem += 1; + } + } + + heal_amt /= numMem; + for(gi = 0; gi < MAX_GROUP_MEMBERS; gi++) + { + if(members[gi]){ + //members[gi]->SetHP(members[gi]->GetHP() + heal_amt); + members[gi]->HealDamage(heal_amt, caster); + members[gi]->SendHPUpdate(); + } + } +} + + +void Group::BalanceHP(int32 penalty) +{ + int dmgtaken = 0, numMem = 0; + unsigned int gi = 0; + for(; gi < MAX_GROUP_MEMBERS; gi++) + { + if(members[gi]){ + dmgtaken += (members[gi]->GetMaxHP() - members[gi]->GetHP()); + numMem += 1; + } + } + + dmgtaken += dmgtaken * penalty / 100; + dmgtaken /= numMem; + for(gi = 0; gi < MAX_GROUP_MEMBERS; gi++) + { + if(members[gi]){ + if((members[gi]->GetMaxHP() - dmgtaken) < 1){ //this way the ability will never kill someone + members[gi]->SetHP(1); //but it will come darn close + members[gi]->SendHPUpdate(); + } + else{ + members[gi]->SetHP(members[gi]->GetMaxHP() - dmgtaken); + members[gi]->SendHPUpdate(); + } + } + } +} + +void Group::BalanceMana(int32 penalty) +{ + int manataken = 0, numMem = 0; + unsigned int gi = 0; + for(; gi < MAX_GROUP_MEMBERS; gi++) + { + if(members[gi]){ + manataken += (members[gi]->GetMaxMana() - members[gi]->GetMana()); + numMem += 1; + } + } + + manataken += manataken * penalty / 100; + manataken /= numMem; + for(gi = 0; gi < MAX_GROUP_MEMBERS; gi++) + { + if(members[gi]){ + if((members[gi]->GetMaxMana() - manataken) < 1){ + members[gi]->SetMana(1); + if (members[gi]->IsClient()) + members[gi]->CastToClient()->SendManaUpdate(); + } + else{ + members[gi]->SetMana(members[gi]->GetMaxMana() - manataken); + if (members[gi]->IsClient()) + members[gi]->CastToClient()->SendManaUpdate(); + } + } + } +} + +uint16 Group::GetAvgLevel() +{ + double levelHolder = 0; + uint8 i = 0; + uint8 numMem = 0; + while(i < MAX_GROUP_MEMBERS) + { + if (members[i]) + { + numMem++; + levelHolder = levelHolder + (members[i]->GetLevel()); + } + i++; + } + levelHolder = ((levelHolder/numMem)+.5); // total levels divided by num of characters + return (uint16(levelHolder)); +} + +void Group::MarkNPC(Mob* Target, int Number) +{ + // Send a packet to all group members in this zone causing the client to prefix the Target mob's name + // with the specified Number. + // + if(!Target || Target->IsClient()) + return; + + if((Number < 1) || (Number > MAX_MARKED_NPCS)) + return; + + bool AlreadyMarked = false; + + uint16 EntityID = Target->GetID(); + + for(int i = 0; i < MAX_MARKED_NPCS; ++i) + if(MarkedNPCs[i] == EntityID) + { + if(i == (Number - 1)) + return; + + UpdateXTargetMarkedNPC(i+1, NULL); + MarkedNPCs[i] = 0; + + AlreadyMarked = true; + + break; + } + + if(!AlreadyMarked) + { + if(MarkedNPCs[Number - 1]) + { + Mob* m = entity_list.GetMob(MarkedNPCs[Number-1]); + if(m) + m->IsTargeted(-1); + + UpdateXTargetMarkedNPC(Number, NULL); + } + + if(EntityID) + { + Mob* m = entity_list.GetMob(Target->GetID()); + if(m) + m->IsTargeted(1); + } + } + + MarkedNPCs[Number - 1] = EntityID; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MarkNPC, sizeof(MarkNPC_Struct)); + + MarkNPC_Struct* mnpcs = (MarkNPC_Struct *)outapp->pBuffer; + + mnpcs->TargetID = EntityID; + + mnpcs->Number = Number; + + Mob *m = entity_list.GetMob(EntityID); + + if(m) + sprintf(mnpcs->Name, "%s", m->GetCleanName()); + + QueuePacket(outapp); + + safe_delete(outapp); + + UpdateXTargetMarkedNPC(Number, m); +} + +void Group::DelegateMainTank(const char *NewMainTankName, uint8 toggle) +{ + // This method is called when the group leader Delegates the Main Tank role to a member of the group + // (or himself). All group members in the zone are notified of the new Main Tank and it is recorded + // in the group_leaders table so as to persist across zones. + // + + bool updateDB = false; + + if(!NewMainTankName) + return; + + Mob *m = entity_list.GetMob(NewMainTankName); + + if(!m) + return; + + if(MainTankName != NewMainTankName || !toggle) + updateDB = true; + + if(m->GetTarget()) + TankTargetID = m->GetTarget()->GetID(); + else + TankTargetID = 0; + + Mob *mtt = TankTargetID ? entity_list.GetMob(TankTargetID) : 0; + + SetMainTank(NewMainTankName); + + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + { + if(members[i] && members[i]->IsClient()) + { + NotifyMainTank(members[i]->CastToClient(), toggle); + members[i]->CastToClient()->UpdateXTargetType(GroupTank, m, NewMainTankName); + members[i]->CastToClient()->UpdateXTargetType(GroupTankTarget, mtt); + } + } + + if(updateDB) { + char errbuff[MYSQL_ERRMSG_SIZE]; + + char *Query = NULL; + + 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); + } +} + +void Group::DelegateMainAssist(const char *NewMainAssistName, uint8 toggle) +{ + // This method is called when the group leader Delegates the Main Assist role to a member of the group + // (or himself). All group members in the zone are notified of the new Main Assist and it is recorded + // in the group_leaders table so as to persist across zones. + // + + bool updateDB = false; + + if(!NewMainAssistName) + return; + + Mob *m = entity_list.GetMob(NewMainAssistName); + + if(!m) + return; + + if(MainAssistName != NewMainAssistName || !toggle) + updateDB = true; + + if(m->GetTarget()) + AssistTargetID = m->GetTarget()->GetID(); + else + AssistTargetID = 0; + + SetMainAssist(NewMainAssistName); + + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { + if(members[i] && members[i]->IsClient()) + { + NotifyMainAssist(members[i]->CastToClient(), toggle); + members[i]->CastToClient()->UpdateXTargetType(GroupAssist, m, NewMainAssistName); + members[i]->CastToClient()->UpdateXTargetType(GroupAssistTarget, m->GetTarget()); + } + } + + if(updateDB) { + char errbuff[MYSQL_ERRMSG_SIZE]; + + char *Query = NULL; + + 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); + } +} + +void Group::DelegatePuller(const char *NewPullerName, uint8 toggle) +{ + // This method is called when the group leader Delegates the Puller role to a member of the group + // (or himself). All group members in the zone are notified of the new Puller and it is recorded + // in the group_leaders table so as to persist across zones. + // + + bool updateDB = false; + + if(!NewPullerName) + return; + + Mob *m = entity_list.GetMob(NewPullerName); + + if(!m) + return; + + if(PullerName != NewPullerName || !toggle) + updateDB = true; + + if(m->GetTarget()) + PullerTargetID = m->GetTarget()->GetID(); + else + PullerTargetID = 0; + + SetPuller(NewPullerName); + + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { + if(members[i] && members[i]->IsClient()) + { + NotifyPuller(members[i]->CastToClient(), toggle); + members[i]->CastToClient()->UpdateXTargetType(Puller, m, NewPullerName); + members[i]->CastToClient()->UpdateXTargetType(PullerTarget, m->GetTarget()); + } + } + + if(updateDB) { + char errbuff[MYSQL_ERRMSG_SIZE]; + + char *Query = NULL; + + 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); + } + +} + +void Group::NotifyMainTank(Client *c, uint8 toggle) +{ + // Send a packet to the specified Client notifying them who the new Main Tank is. This causes the client to display + // a message with the name of the Main Tank. + // + + if(!c) + return; + + if(!MainTankName.size()) + return; + + if(c->GetClientVersion() < EQClientSoD) + { + if(toggle) + c->Message(0, "%s is now Main Tank.", MainTankName.c_str()); + else + c->Message(0, "%s is no longer Main Tank.", MainTankName.c_str()); + } + else + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupRoles, sizeof(GroupRole_Struct)); + + GroupRole_Struct *grs = (GroupRole_Struct*)outapp->pBuffer; + + strn0cpy(grs->Name1, MainTankName.c_str(), sizeof(grs->Name1)); + + strn0cpy(grs->Name2, GetLeaderName(), sizeof(grs->Name2)); + + grs->RoleNumber = 1; + + grs->Toggle = toggle; + + c->QueuePacket(outapp); + + safe_delete(outapp); + } + +} + +void Group::NotifyMainAssist(Client *c, uint8 toggle) +{ + // Send a packet to the specified Client notifying them who the new Main Assist is. This causes the client to display + // a message with the name of the Main Assist. + // + + if(!c) + return; + + if(!MainAssistName.size()) + return; + + if(c->GetClientVersion() < EQClientSoD) + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_DelegateAbility, sizeof(DelegateAbility_Struct)); + + DelegateAbility_Struct* das = (DelegateAbility_Struct*)outapp->pBuffer; + + das->DelegateAbility = 0; + + das->MemberNumber = 0; + + das->Action = 0; + + das->EntityID = 0; + + strn0cpy(das->Name, MainAssistName.c_str(), sizeof(das->Name)); + + c->QueuePacket(outapp); + + safe_delete(outapp); + } + else + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupRoles, sizeof(GroupRole_Struct)); + + GroupRole_Struct *grs = (GroupRole_Struct*)outapp->pBuffer; + + strn0cpy(grs->Name1, MainAssistName.c_str(), sizeof(grs->Name1)); + + strn0cpy(grs->Name2, GetLeaderName(), sizeof(grs->Name2)); + + grs->RoleNumber = 2; + + grs->Toggle = toggle; + + c->QueuePacket(outapp); + + safe_delete(outapp); + } + + NotifyAssistTarget(c); + +} + +void Group::NotifyPuller(Client *c, uint8 toggle) +{ + // Send a packet to the specified Client notifying them who the new Puller is. This causes the client to display + // a message with the name of the Puller. + // + + if(!c) + return; + + if(!PullerName.size()) + return; + + if(c->GetClientVersion() < EQClientSoD) + { + if(toggle) + c->Message(0, "%s is now Puller.", PullerName.c_str()); + else + c->Message(0, "%s is no longer Puller.", PullerName.c_str()); + } + else + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupRoles, sizeof(GroupRole_Struct)); + + GroupRole_Struct *grs = (GroupRole_Struct*)outapp->pBuffer; + + strn0cpy(grs->Name1, PullerName.c_str(), sizeof(grs->Name1)); + + strn0cpy(grs->Name2, GetLeaderName(), sizeof(grs->Name2)); + + grs->RoleNumber = 3; + + grs->Toggle = toggle; + + c->QueuePacket(outapp); + + safe_delete(outapp); + } + +} + +void Group::UnDelegateMainTank(const char *OldMainTankName, uint8 toggle) +{ + // Called when the group Leader removes the Main Tank delegation. Sends a packet to each group member in the zone + // 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); + + if(!toggle) { + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { + if(members[i] && members[i]->IsClient()) + { + NotifyMainTank(members[i]->CastToClient(), toggle); + members[i]->CastToClient()->UpdateXTargetType(GroupTank, NULL, ""); + members[i]->CastToClient()->UpdateXTargetType(GroupTankTarget, NULL); + } + } + } + + SetMainTank(""); + } +} + +void Group::UnDelegateMainAssist(const char *OldMainAssistName, uint8 toggle) +{ + // Called when the group Leader removes the Main Assist delegation. Sends a packet to each group member in the zone + // informing them of the change and update the group_leaders table. + // + if(OldMainAssistName == MainAssistName) { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_DelegateAbility, sizeof(DelegateAbility_Struct)); + + DelegateAbility_Struct* das = (DelegateAbility_Struct*)outapp->pBuffer; + + das->DelegateAbility = 0; + + das->MemberNumber = 0; + + das->Action = 1; + + das->EntityID = 0; + + strn0cpy(das->Name, OldMainAssistName, sizeof(das->Name)); + + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + if(members[i] && members[i]->IsClient()) + { + members[i]->CastToClient()->QueuePacket(outapp); + members[i]->CastToClient()->UpdateXTargetType(GroupAssist, NULL, ""); + } + + 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); + + if(!toggle) + { + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + { + if(members[i] && members[i]->IsClient()) + { + NotifyMainAssist(members[i]->CastToClient(), toggle); + members[i]->CastToClient()->UpdateXTargetType(GroupAssistTarget, NULL); + } + } + } + + SetMainAssist(""); + } +} + +void Group::UnDelegatePuller(const char *OldPullerName, uint8 toggle) +{ + // Called when the group Leader removes the Puller delegation. Sends a packet to each group member in the zone + // 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); + + if(!toggle) { + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { + if(members[i] && members[i]->IsClient()) + { + NotifyPuller(members[i]->CastToClient(), toggle); + members[i]->CastToClient()->UpdateXTargetType(Puller, NULL, ""); + members[i]->CastToClient()->UpdateXTargetType(PullerTarget, NULL); + } + } + } + + SetPuller(""); + } +} + +bool Group::IsNPCMarker(Client *c) +{ + // Returns true if the specified client has been delegated the NPC Marker Role + // + if(!c) + return false; + + if(NPCMarkerName.size()) + return(c->GetName() == NPCMarkerName); + + return false; + +} + +void Group::SetGroupAssistTarget(Mob *m) +{ + // Notify all group members in the zone of the new target the Main Assist has selected. + // + AssistTargetID = m ? m->GetID() : 0; + + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + { + if(members[i] && members[i]->IsClient()) + { + NotifyAssistTarget(members[i]->CastToClient()); + } + } +} + +void Group::SetGroupTankTarget(Mob *m) +{ + TankTargetID = m ? m->GetID() : 0; + + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + { + if(members[i] && members[i]->IsClient()) + { + members[i]->CastToClient()->UpdateXTargetType(GroupTankTarget, m); + } + } +} + +void Group::SetGroupPullerTarget(Mob *m) +{ + PullerTargetID = m ? m->GetID() : 0; + + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + { + if(members[i] && members[i]->IsClient()) + { + members[i]->CastToClient()->UpdateXTargetType(PullerTarget, m); + } + } +} + +void Group::NotifyAssistTarget(Client *c) +{ + // Send a packet to the specified client notifying them of the group target selected by the Main Assist. + + if(!c) + return; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SetGroupTarget, sizeof(MarkNPC_Struct)); + + MarkNPC_Struct* mnpcs = (MarkNPC_Struct *)outapp->pBuffer; + + mnpcs->TargetID = AssistTargetID; + + mnpcs->Number = 0; + + c->QueuePacket(outapp); + + safe_delete(outapp); + + Mob *m = entity_list.GetMob(AssistTargetID); + + c->UpdateXTargetType(GroupAssistTarget, m); + +} + +void Group::NotifyTankTarget(Client *c) +{ + if(!c) + return; + + Mob *m = entity_list.GetMob(TankTargetID); + + c->UpdateXTargetType(GroupTankTarget, m); +} + +void Group::NotifyPullerTarget(Client *c) +{ + if(!c) + return; + + Mob *m = entity_list.GetMob(PullerTargetID); + + c->UpdateXTargetType(PullerTarget, m); +} + +void Group::DelegateMarkNPC(const char *NewNPCMarkerName) +{ + // Called when the group leader has delegated the Mark NPC ability to a group member. + // Notify all group members in the zone of the change and save the change in the group_leaders + // table to persist across zones. + // + if(NPCMarkerName.size() > 0) + UnDelegateMarkNPC(NPCMarkerName.c_str()); + + if(!NewNPCMarkerName) + return; + + SetNPCMarker(NewNPCMarkerName); + + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + 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); + +} + +void Group::NotifyMarkNPC(Client *c) +{ + // Notify the specified client who the group member is who has been delgated the Mark NPC ability. + + if(!c) + return; + + if(!NPCMarkerName.size()) + return; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_DelegateAbility, sizeof(DelegateAbility_Struct)); + + DelegateAbility_Struct* das = (DelegateAbility_Struct*)outapp->pBuffer; + + das->DelegateAbility = 1; + + das->MemberNumber = 0; + + das->Action = 0; + + das->EntityID = NPCMarkerID; + + strn0cpy(das->Name, NPCMarkerName.c_str(), sizeof(das->Name)); + + c->QueuePacket(outapp); + + safe_delete(outapp); + +} +void Group::SetNPCMarker(const char *NewNPCMarkerName) +{ + NPCMarkerName = NewNPCMarkerName; + + Client *m = entity_list.GetClientByName(NPCMarkerName.c_str()); + + if(!m) + NPCMarkerID = 0; + else + NPCMarkerID = m->GetID(); +} + +void Group::UnDelegateMarkNPC(const char *OldNPCMarkerName) +{ + // Notify all group members in the zone that the Mark NPC ability has been rescinded from the specified + // group member. + + if(!OldNPCMarkerName) + return; + + if(!NPCMarkerName.size()) + return; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_DelegateAbility, sizeof(DelegateAbility_Struct)); + + DelegateAbility_Struct* das = (DelegateAbility_Struct*)outapp->pBuffer; + + das->DelegateAbility = 1; + + das->MemberNumber = 0; + + das->Action = 1; + + das->EntityID = 0; + + strn0cpy(das->Name, OldNPCMarkerName, sizeof(das->Name)); + + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + if(members[i] && members[i]->IsClient()) + members[i]->CastToClient()->QueuePacket(outapp); + + safe_delete(outapp); + + NPCMarkerName.clear(); + + char errbuff[MYSQL_ERRMSG_SIZE]; + + char *Query = 0; + + 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 *Query = new char[200 + sizeof(GroupLeadershipAA_Struct)*2]; + + 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) +{ + // Called from entity_list when the mob with the specified ID is being destroyed. + // + // If the given mob has been marked by this group, it is removed from the list of marked NPCs. + // The primary reason for doing this is so that when a new group member joins or zones in, we + // send them correct details of which NPCs are currently marked. + + if(AssistTargetID == ID) + AssistTargetID = 0; + + + if(TankTargetID == ID) + TankTargetID = 0; + + if(PullerTargetID == ID) + PullerTargetID = 0; + + for(int i = 0; i < MAX_MARKED_NPCS; ++i) + { + if(MarkedNPCs[i] == ID) + { + MarkedNPCs[i] = 0; + UpdateXTargetMarkedNPC(i + 1, NULL); + } + } +} + +void Group::SendMarkedNPCsToMember(Client *c, bool Clear) +{ + // Send the Entity IDs of the NPCs marked by the Group Leader or delegate to the specified client. + // If Clear == true, then tell the client to unmark the NPCs (when a member disbands). + // + // + if(!c) + return; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MarkNPC, sizeof(MarkNPC_Struct)); + + MarkNPC_Struct *mnpcs = (MarkNPC_Struct *)outapp->pBuffer; + + for(int i = 0; i < MAX_MARKED_NPCS; ++i) + { + if(MarkedNPCs[i]) + { + mnpcs->TargetID = MarkedNPCs[i]; + + Mob *m = entity_list.GetMob(MarkedNPCs[i]); + + if(m) + sprintf(mnpcs->Name, "%s", m->GetCleanName()); + + if(!Clear) + mnpcs->Number = i + 1; + else + mnpcs->Number = 0; + + c->QueuePacket(outapp); + c->UpdateXTargetType((mnpcs->Number == 1) ? GroupMarkTarget1 : ((mnpcs->Number == 2) ? GroupMarkTarget2 : GroupMarkTarget3), m); + } + } + + safe_delete(outapp); +} + +void Group::ClearAllNPCMarks() +{ + // This method is designed to be called when the number of members in the group drops below 3 and leadership AA + // may no longer be used. It removes all NPC marks. + // + for(uint8 i = 0; i < MAX_GROUP_MEMBERS; ++i) + if(members[i] && members[i]->IsClient()) + SendMarkedNPCsToMember(members[i]->CastToClient(), true); + + for(int i = 0; i < MAX_MARKED_NPCS; ++i) + { + if(MarkedNPCs[i]) + { + Mob* m = entity_list.GetMob(MarkedNPCs[i]); + + if(m) + m->IsTargeted(-1); + } + + MarkedNPCs[i] = 0; + } + +} + +int8 Group::GetNumberNeedingHealedInGroup(int8 hpr, bool includePets) { + int8 needHealed = 0; + + for( int i = 0; iqglobal) { + + if(members[i]->GetHPRatio() <= hpr) + needHealed++; + + if(includePets) { + if(members[i]->GetPet() && members[i]->GetPet()->GetHPRatio() <= hpr) { + needHealed++; + } + } + } + } + + + return needHealed; +} + +void Group::UpdateGroupAAs() +{ + // This method updates the Groups Leadership abilities from the Player Profile of the Leader. + // + Mob *m = GetLeader(); + + if(m && m->IsClient()) + m->CastToClient()->GetGroupAAs(&LeaderAbilities); + else + memset(&LeaderAbilities, 0, sizeof(GroupLeadershipAA_Struct)); + + SaveGroupLeaderAA(); +} + +void Group::QueueHPPacketsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app) +{ + // Send a mobs HP packets to group members if the leader has the NPC Health AA and the mob is the + // target of the group's main assist, or is marked, and the member doesn't already have the mob targeted. + + if(!sender || !app || !GetLeadershipAA(groupAANPCHealth)) + return; + + uint16 SenderID = sender->GetID(); + + if(SenderID != AssistTargetID) + { + bool Marked = false; + + for(int i = 0; i < MAX_MARKED_NPCS; ++i) + { + if(MarkedNPCs[i] == SenderID) + { + Marked = true; + break; + } + } + + if(!Marked) + return; + + } + + for(unsigned int i = 0; i < MAX_GROUP_MEMBERS; ++i) + if(members[i] && members[i]->IsClient()) + { + if(!members[i]->GetTarget() || (members[i]->GetTarget()->GetID() != SenderID)) + { + members[i]->CastToClient()->QueuePacket(app); + } + } + +} + +void Group::ChangeLeader(Mob* newleader) +{ + // this changes the current group leader, notifies other members, and updates leadship AA + + // if the new leader is invalid, do nothing + if (!newleader) + return; + + Mob* oldleader = GetLeader(); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); + GroupJoin_Struct* gu = (GroupJoin_Struct*) outapp->pBuffer; + gu->action = groupActMakeLeader; + + strcpy(gu->membername, newleader->GetName()); + strcpy(gu->yourname, oldleader->GetName()); + SetLeader(newleader); + database.SetGroupLeaderName(GetID(), newleader->GetName()); + UpdateGroupAAs(); + gu->leader_aas = LeaderAbilities; + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (members[i] && members[i]->IsClient()) + { + if(members[i]->CastToClient()->GetClientVersion() >= EQClientSoD) + members[i]->CastToClient()->SendGroupLeaderChangePacket(newleader->GetName()); + + members[i]->CastToClient()->QueuePacket(outapp); + } + } + safe_delete(outapp); +} + +const char *Group::GetClientNameByIndex(uint8 index) +{ + return membername[index]; +} + +void Group::UpdateXTargetMarkedNPC(uint32 Number, Mob *m) +{ + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + { + if(members[i] && members[i]->IsClient()) + { + members[i]->CastToClient()->UpdateXTargetType((Number == 1) ? GroupMarkTarget1 : ((Number == 2) ? GroupMarkTarget2 : GroupMarkTarget3), m); + } + } + +} + +void Group::SetMainTank(const char *NewMainTankName) +{ + MainTankName = NewMainTankName; + + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + { + if(!strncasecmp(membername[i], NewMainTankName, 64)) + MemberRoles[i] |= RoleTank; + else + MemberRoles[i] &= ~RoleTank; + } +} + +void Group::SetMainAssist(const char *NewMainAssistName) +{ + MainAssistName = NewMainAssistName; + + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + { + if(!strncasecmp(membername[i], NewMainAssistName, 64)) + MemberRoles[i] |= RoleAssist; + else + MemberRoles[i] &= ~RoleAssist; + } +} + +void Group::SetPuller(const char *NewPullerName) +{ + PullerName = NewPullerName; + + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + { + if(!strncasecmp(membername[i], NewPullerName, 64)) + MemberRoles[i] |= RolePuller; + else + MemberRoles[i] &= ~RolePuller; + } +} + +bool Group::HasRole(Mob *m, uint8 Role) +{ + if(!m) + return false; + + for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) + { + if((m == members[i]) && (MemberRoles[i] & Role)) + return true; + } + return false; +} diff --git a/zone/groups.h b/zone/groups.h new file mode 100644 index 000000000..37910e2c0 --- /dev/null +++ b/zone/groups.h @@ -0,0 +1,156 @@ +/* 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 GROUPS_H +#define GROUPS_H + +#include "../common/types.h" +#include "../common/linked_list.h" +#include "../common/emu_opcodes.h" +#include "../common/eq_packet_structs.h" +#include "entity.h" +#include "mob.h" +#include "features.h" +#include "../common/servertalk.h" + +#define MAX_MARKED_NPCS 3 + +enum { RoleAssist = 1, RoleTank = 2, RolePuller = 4 }; + +class GroupIDConsumer { +public: + GroupIDConsumer() { id = 0; } + GroupIDConsumer(uint32 gid) { id = gid; } + inline const uint32 GetID() const { return id; } + +protected: + friend class EntityList; + //use of this function is highly discouraged + inline void SetID(uint32 set_id) { id = set_id; } +private: + uint32 id; +}; + +class Group : public GroupIDConsumer { +public: + Group(Mob* leader); + Group(uint32 gid); + ~Group(); + + bool AddMember(Mob* newmember, const char* NewMemberName = NULL, uint32 CharacterID = 0); + void AddMember(const char* NewMemberName); + void SendUpdate(uint32 type,Mob* member); + void SendLeadershipAAUpdate(); + void SendWorldGroup(uint32 zone_id,Mob* zoningmember); + bool DelMemberOOZ(const char *Name); + bool DelMember(Mob* oldmember,bool ignoresender = false); + void DisbandGroup(); + bool IsGroupMember(Mob* client); + bool IsGroupMember(const char *Name); + bool Process(); + bool IsGroup() { return true; } + void CastGroupSpell(Mob* caster,uint16 spellid); + void GroupBardPulse(Mob* caster,uint16 spellid); + void SplitExp(uint32 exp, Mob* other); + void GroupMessage(Mob* sender,uint8 language,uint8 lang_skill,const char* message); + void GroupMessage_StringID(Mob* sender, 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); + uint32 GetTotalGroupDamage(Mob* other); + void SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter = NULL); + inline void SetLeader(Mob* newleader){ leader=newleader; }; + inline Mob* GetLeader(){ return leader; }; + char* GetLeaderName() { return membername[0]; }; + void SendHPPacketsTo(Mob* newmember); + void SendHPPacketsFrom(Mob* newmember); + bool UpdatePlayer(Mob* update); + void MemberZoned(Mob* removemob); + inline bool IsLeader(Mob* leadertest) { return leadertest==leader; }; + uint8 GroupCount(); + uint32 GetHighestLevel(); + uint32 GetLowestLevel(); + void QueuePacket(const EQApplicationPacket *app, bool ack_req = true); + void TeleportGroup(Mob* sender, uint32 zoneID, uint16 instance_id, float x, float y, float z, float heading); + uint16 GetAvgLevel(); + bool LearnMembers(); + void VerifyGroup(); + void BalanceHP(int32 penalty); + void BalanceMana(int32 penalty); + void HealGroup(uint32 heal_amt, Mob* caster); + 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(); + void SaveGroupLeaderAA(); + void MarkNPC(Mob* Target, int Number); + int8 GetNumberNeedingHealedInGroup(int8 hpr, bool includePets); + void DelegateMainTank(const char *NewMainAssistName, uint8 toggle = 0); + void DelegateMainAssist(const char *NewMainAssistName, uint8 toggle = 0); + void DelegatePuller(const char *NewMainAssistName, uint8 toggle = 0); + void UnDelegateMainTank(const char *OldMainAssistName, uint8 toggle = 0); + void UnDelegateMainAssist(const char *OldMainAssistName, uint8 toggle = 0); + void UnDelegatePuller(const char *OldMainAssistName, uint8 toggle = 0); + bool IsNPCMarker(Client *c); + void SetGroupAssistTarget(Mob *m); + void SetGroupTankTarget(Mob *m); + void SetGroupPullerTarget(Mob *m); + bool HasRole(Mob *m, uint8 Role); + void NotifyAssistTarget(Client *c); + void NotifyTankTarget(Client *c); + void NotifyPullerTarget(Client *c); + void DelegateMarkNPC(const char *NewNPCMarkerName); + void UnDelegateMarkNPC(const char *OldNPCMarkerName); + void NotifyMainTank(Client *c, uint8 toggle = 0); + void NotifyMainAssist(Client *c, uint8 toggle = 0); + void NotifyPuller(Client *c, uint8 toggle = 0); + void NotifyMarkNPC(Client *c); + inline uint32 GetNPCMarkerID() { return NPCMarkerID; } + void SetMainTank(const char *NewMainTankName); + void SetMainAssist(const char *NewMainAssistName); + void SetPuller(const char *NewPullerName); + const char *GetMainTankName() { return MainTankName.c_str(); } + const char *GetMainAssistName() { return MainAssistName.c_str(); } + const char *GetPullerName() { return PullerName.c_str(); } + void SetNPCMarker(const char *NewNPCMarkerName); + void UnMarkNPC(uint16 ID); + void SendMarkedNPCsToMember(Client *c, bool Clear = false); + inline int GetLeadershipAA(int AAID) { return LeaderAbilities.ranks[AAID]; } + void ClearAllNPCMarks(); + void QueueHPPacketsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app); + void ChangeLeader(Mob* newleader); + const char *GetClientNameByIndex(uint8 index); + void UpdateXTargetMarkedNPC(uint32 Number, Mob *m); + + Mob* members[MAX_GROUP_MEMBERS]; + char membername[MAX_GROUP_MEMBERS][64]; + uint8 MemberRoles[MAX_GROUP_MEMBERS]; + bool disbandcheck; + bool castspell; + +private: + Mob* leader; + GroupLeadershipAA_Struct LeaderAbilities; + string MainTankName; + string MainAssistName; + string PullerName; + string NPCMarkerName; + uint16 NPCMarkerID; + uint16 AssistTargetID; + uint16 TankTargetID; + uint16 PullerTargetID; + uint16 MarkedNPCs[MAX_MARKED_NPCS]; + +}; + +#endif diff --git a/zone/guild.cpp b/zone/guild.cpp new file mode 100644 index 000000000..224c568c2 --- /dev/null +++ b/zone/guild.cpp @@ -0,0 +1,434 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 "masterentity.h" +#include "worldserver.h" +#include "net.h" +#include "../common/database.h" +#include "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/MiscFunctions.h" +#include "guild_mgr.h" +#include "StringIDs.h" +#include "NpcAI.h" + +extern WorldServer worldserver; + +void Client::SendGuildMOTD(bool GetGuildMOTDReply) { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildMOTD, sizeof(GuildMOTD_Struct)); + + // When the Client gets an OP_GuildMOTD, it compares the text to the version it has previously stored. + // If the text in the OP_GuildMOTD packet is the same, it does nothing. If not the same, it displays + // the new MOTD and then stores the new text. + // + // When the Client receives an OP_GetGuildMOTDReply, it displays the text in the packet. + // + // So OP_GuildMOTD should be sent on zone entry and when an Officer changes the MOTD, and OP_GetGuildMOTDReply + // should be sent when the client issues the /getguildmotd command. + // + if(GetGuildMOTDReply) + outapp->SetOpcode(OP_GetGuildMOTDReply); + + GuildMOTD_Struct *motd = (GuildMOTD_Struct *) outapp->pBuffer; + motd->unknown0 = 0; + strn0cpy(motd->name, m_pp.name, 64); + + if(IsInAGuild()) { + if(!guild_mgr.GetGuildMOTD(GuildID(), motd->motd, motd->setby_name)) { + motd->setby_name[0] = '\0'; + strcpy(motd->motd, "ERROR GETTING MOTD!"); + } + } else { + //we have to send them an empty MOTD anywyas. + motd->motd[0] = '\0'; //just to be sure + motd->setby_name[0] = '\0'; //just to be sure + + } + + mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildMOTD of length %d", outapp->size); + mpkt(GUILDS__OUT_PACKET_TRACE, outapp); + + FastQueuePacket(&outapp); +} + +void Client::SendGuildURL() +{ + if(GetClientVersion() < EQClientSoF) + return; + + if(IsInAGuild()) + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildUpdateURLAndChannel, sizeof(GuildUpdateURLAndChannel_Struct)); + + GuildUpdateURLAndChannel_Struct *guuacs = (GuildUpdateURLAndChannel_Struct*) outapp->pBuffer; + + if(guild_mgr.GetGuildURL(GuildID(), guuacs->Text)) + { + guuacs->Action = 0; + FastQueuePacket(&outapp); + } + else + safe_delete(outapp); + } +} + +void Client::SendGuildChannel() +{ + if(GetClientVersion() < EQClientSoF) + return; + + if(IsInAGuild()) + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildUpdateURLAndChannel, sizeof(GuildUpdateURLAndChannel_Struct)); + + GuildUpdateURLAndChannel_Struct *guuacs = (GuildUpdateURLAndChannel_Struct*) outapp->pBuffer; + + if(guild_mgr.GetGuildChannel(GuildID(), guuacs->Text)) + { + guuacs->Action = 1; + + FastQueuePacket(&outapp); + } + else + safe_delete(outapp); + } +} + +void Client::SendGuildSpawnAppearance() { + if (!IsInAGuild()) { + // clear guildtag + SendAppearancePacket(AT_GuildID, GUILD_NONE); + mlog(GUILDS__OUT_PACKETS, "Sending spawn appearance for no guild tag."); + } else { + 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()); + SendAppearancePacket(AT_GuildRank, rank); + } + + UpdateWho(); +} + +void Client::SendGuildList() { + EQApplicationPacket *outapp; +// outapp = new EQApplicationPacket(OP_ZoneGuildList); + outapp = new EQApplicationPacket(OP_GuildsList); + + //ask the guild manager to build us a nice guild list packet + outapp->pBuffer = guild_mgr.MakeGuildList(/*GetName()*/"", outapp->size); + if(outapp->pBuffer == NULL) { + mlog(GUILDS__ERROR, "Unable to make guild list!"); + return; + } + + mlog(GUILDS__OUT_PACKETS, "Sending OP_ZoneGuildList of length %d", outapp->size); +// mpkt(GUILDS__OUT_PACKET_TRACE, outapp); + + FastQueuePacket(&outapp); +} + + +void Client::SendGuildMembers() { + uint32 len; + uint8 *data = guild_mgr.MakeGuildMembers(GuildID(), GetName(), len); + if(data == NULL) + return; //invalid guild, shouldent happen. + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildMemberList); + outapp->size = len; + outapp->pBuffer = data; + data = NULL; + + mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildMemberList of length %d", outapp->size); + mpkt(GUILDS__OUT_PACKET_TRACE, outapp); + + FastQueuePacket(&outapp); + + ServerPacket* pack = new ServerPacket(ServerOP_RequestOnlineGuildMembers, sizeof(ServerRequestOnlineGuildMembers_Struct)); + + ServerRequestOnlineGuildMembers_Struct *srogms = (ServerRequestOnlineGuildMembers_Struct*)pack->pBuffer; + + srogms->FromID = CharacterID(); + srogms->GuildID = GuildID(); + + worldserver.SendPacket(pack); + + safe_delete(pack); + + // We need to send the Guild URL and Channel name again, as sending OP_GuildMemberList appears to clear this information out. + SendGuildURL(); + SendGuildChannel(); +} + +void Client::RefreshGuildInfo() +{ + uint32 OldGuildID = guild_id; + + guildrank = GUILD_RANK_NONE; + guild_id = GUILD_NONE; + + bool WasBanker = GuildBanker; + + CharGuildInfo info; + if(!guild_mgr.GetCharInfo(CharacterID(), info)) { + mlog(GUILDS__ERROR, "Unable to obtain guild char info for %s (%d)", GetName(), CharacterID()); + return; + } + + guildrank = info.rank; + guild_id = info.guild_id; + GuildBanker = info.banker || guild_mgr.IsGuildLeader(GuildID(), CharacterID()); + + if(((int)zone->GetZoneID() == RuleI(World, GuildBankZoneID))) + { + if(WasBanker != GuildBanker) + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SetGuildRank, sizeof(GuildSetRank_Struct)); + + GuildSetRank_Struct *gsrs = (GuildSetRank_Struct*)outapp->pBuffer; + + gsrs->Rank = guildrank; + strn0cpy(gsrs->MemberName, GetName(), sizeof(gsrs->MemberName)); + gsrs->Banker = GuildBanker; + + FastQueuePacket(&outapp); + } + + if((guild_id != OldGuildID) && GuildBanks) + { + ClearGuildBank(); + + if(guild_id != GUILD_NONE) + GuildBanks->SendGuildBank(this); + } + } + + SendGuildSpawnAppearance(); +} + +void EntityList::SendGuildMOTD(uint32 guild_id) { + if(guild_id == GUILD_NONE) + return; + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) { + Client* client = iterator.GetData(); + if (client->GuildID() == guild_id) { + client->SendGuildMOTD(); + client->SendGuildURL(); + client->SendGuildChannel(); + } + iterator.Advance(); + } +} + +void EntityList::SendGuildSpawnAppearance(uint32 guild_id) { + if(guild_id == GUILD_NONE) + return; + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) { + Client* client = iterator.GetData(); + if (client->GuildID() == guild_id) { + client->SendGuildSpawnAppearance(); + } + iterator.Advance(); + } +} + +void EntityList::RefreshAllGuildInfo(uint32 guild_id) { + if(guild_id == GUILD_NONE) + return; + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) { + Client* client = iterator.GetData(); + if (client->GuildID() == guild_id) { + client->RefreshGuildInfo(); + } + iterator.Advance(); + } +} + +void EntityList::SendGuildMembers(uint32 guild_id) { + if(guild_id == GUILD_NONE) + return; + + //this could be optimized a bit to only build the member's packet once + //and then keep swapping out the name in the packet on each send. + + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) { + Client* client = iterator.GetData(); + if (client->GuildID() == guild_id) { + client->SendGuildMembers(); + } + iterator.Advance(); + } +} + +void EntityList::SendGuildList() { + LinkedListIterator iterator(client_list); + iterator.Reset(); + while(iterator.MoreElements()) { + Client* client = iterator.GetData(); + client->SendGuildList(); + iterator.Advance(); + } +} + +void Client::SendGuildJoin(GuildJoin_Struct* gj){ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildManageAdd, sizeof(GuildJoin_Struct)); + GuildJoin_Struct* outgj=(GuildJoin_Struct*)outapp->pBuffer; + outgj->class_ = gj->class_; + outgj->guild_id = gj->guild_id; + outgj->level = gj->level; + strcpy(outgj->name, gj->name); + outgj->rank = gj->rank; + outgj->zoneid = gj->zoneid; + + mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildManageAdd for join of length %d", outapp->size); + mpkt(GUILDS__OUT_PACKET_TRACE, outapp); + + FastQueuePacket(&outapp); + +// SendGuildMembers(gj->guild_id, true); +} + +/* +void EntityList::SendGuildJoin(GuildJoin_Struct* gj){ + LinkedListIterator iterator(client_list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + Client* client = iterator.GetData()->CastToClient(); + if (client->IsInGuild(gj->guild_id)) + client->SendGuildJoin(gj); + iterator.Advance(); + } +}*/ + + + +/*bool Client::SetGuild(uint32 in_guild_id, uint8 in_rank) { + if (in_guild_id == 0) { + // update DB + if (!guild_mgr.SetGuild(character_id, 0, GUILD_MEMBER)) + return false; + // clear guildtag + guild_id = GUILD_NONE; + SendAppearancePacket(AT_GuildID, GUILD_NONE); + SendAppearancePacket(AT_GuildRank, GUILD_RANK_NONE); + UpdateWho(); + return true; + } else { + if (!guild_mgr.SetGuild(character_id, in_guild_id, in_rank)) + return false; + guildrank = in_rank; + if (guild_id != in_guild_id) { + guild_id = in_guild_id; + SendAppearancePacket(AT_GuildID, in_guild_id); + } + SendAppearancePacket(AT_GuildRank, in_rank); + UpdateWho(); + return true; + } + UpdateWho(); + return false; +}*/ + +/* +void Client::GuildChangeRank(uint32 guild_id, uint32 oldrank, uint32 newrank){ + GuildChangeRank(GetName(), guild_id, oldrank, newrank); +} + +void Client::GuildChangeRank(const char* name, uint32 guild_id, uint32 oldrank, uint32 newrank) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildManageStatus, sizeof(GuildManageStatus_Struct)); + GuildManageStatus_Struct* gms = (GuildManageStatus_Struct*) outapp->pBuffer; + gms->guild_id = guild_id; + strcpy(gms->name, name); + gms->newrank = newrank; + gms->oldrank = oldrank; + entity_list.QueueClientsGuild(this, outapp, false, guild_id); + safe_delete(outapp); + SendGuildMembers(guild_id, true); +}*/ + + +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; + } + + // code below will never be reached + mysql_free_result(result); + return false; + } + } + return false; +} + +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); + return false; + } + + safe_delete_array(query); + + return(affected_rows > 0); +} + diff --git a/zone/guild_mgr.cpp b/zone/guild_mgr.cpp new file mode 100644 index 000000000..597b25359 --- /dev/null +++ b/zone/guild_mgr.cpp @@ -0,0 +1,1613 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "guild_mgr.h" +#include "zonedb.h" +#include "worldserver.h" +#include "../common/servertalk.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; + +extern WorldServer worldserver; +extern volatile bool ZoneLoaded; + +void ZoneGuildManager::SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation) { + _log(GUILDS__REFRESH, "Sending guild refresh for %d to world, changes: name=%d, motd=%d, rank=d, relation=%d", guild_id, name, motd, rank, relation); + ServerPacket* pack = new ServerPacket(ServerOP_RefreshGuild, sizeof(ServerGuildRefresh_Struct)); + ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer; + s->guild_id = guild_id; + s->name_change = name; + s->motd_change = motd; + s->rank_change = rank; + s->relation_change = relation; + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void ZoneGuildManager::SendCharRefresh(uint32 old_guild_id, uint32 guild_id, uint32 charid) { + if(guild_id == 0) { + _log(GUILDS__REFRESH, "Guild lookup for char %d when sending char refresh.", charid); + + CharGuildInfo gci; + if(!GetCharInfo(charid, gci)) { + guild_id = GUILD_NONE; + } else { + guild_id = gci.guild_id; + } + } + + _log(GUILDS__REFRESH, "Sending char refresh for %d from guild %d to world", charid, guild_id); + + ServerPacket* pack = new ServerPacket(ServerOP_GuildCharRefresh, sizeof(ServerGuildCharRefresh_Struct)); + ServerGuildCharRefresh_Struct *s = (ServerGuildCharRefresh_Struct *) pack->pBuffer; + s->guild_id = guild_id; + s->old_guild_id = old_guild_id; + s->char_id = charid; + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void ZoneGuildManager::SendRankUpdate(uint32 CharID) +{ + CharGuildInfo gci; + + if(!GetCharInfo(CharID, gci)) + return; + + ServerPacket* pack = new ServerPacket(ServerOP_GuildRankUpdate, sizeof(ServerGuildRankUpdate_Struct)); + + ServerGuildRankUpdate_Struct *sgrus = (ServerGuildRankUpdate_Struct*)pack->pBuffer; + + sgrus->GuildID = gci.guild_id; + strn0cpy(sgrus->MemberName, gci.char_name.c_str(), sizeof(sgrus->MemberName)); + sgrus->Rank = gci.rank; + sgrus->Banker = gci.banker + (gci.alt * 2); + + worldserver.SendPacket(pack); + + safe_delete(pack); +} + +void ZoneGuildManager::SendGuildDelete(uint32 guild_id) { + _log(GUILDS__REFRESH, "Sending guild delete for guild %d to world", guild_id); + ServerPacket* pack = new ServerPacket(ServerOP_DeleteGuild, sizeof(ServerGuildID_Struct)); + ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer; + s->guild_id = guild_id; + worldserver.SendPacket(pack); + safe_delete(pack); +} + +//makes a guild member list packet (internal format), returns ownership of the buffer. +uint8 *ZoneGuildManager::MakeGuildMembers(uint32 guild_id, const char *prefix_name, uint32 &length) { + uint8 *retbuffer; + + //hack because we dont have the "remove from guild" packet right now. + if(guild_id == GUILD_NONE) { + length = sizeof(Internal_GuildMembers_Struct); + retbuffer = new uint8[length]; + Internal_GuildMembers_Struct *gms = (Internal_GuildMembers_Struct *) retbuffer; + strcpy(gms->player_name, prefix_name); + gms->count = 0; + gms->name_length = 0; + gms->note_length = 0; + return(retbuffer); + } + + vector members; + if(!GetEntireGuild(guild_id, members)) + return(NULL); + + //figure out the actual packet length. + uint32 fixed_length = sizeof(Internal_GuildMembers_Struct) + members.size()*sizeof(Internal_GuildMemberEntry_Struct); + vector::iterator cur, end; + CharGuildInfo *ci; + cur = members.begin(); + end = members.end(); + uint32 name_len = 0; + uint32 note_len = 0; + for(; cur != end; cur++) { + ci = *cur; + name_len += ci->char_name.length(); + note_len += ci->public_note.length(); + } + + //calc total length. + length = fixed_length + name_len + note_len + members.size()*2; //string data + null terminators + + //make our nice buffer + retbuffer = new uint8[length]; + + Internal_GuildMembers_Struct *gms = (Internal_GuildMembers_Struct *) retbuffer; + + //fill in the global header + strcpy(gms->player_name, prefix_name); + gms->count = members.size(); + gms->name_length = name_len; + gms->note_length = note_len; + + char *name_buf = (char *) ( retbuffer + fixed_length ); + char *note_buf = (char *) ( name_buf + name_len + members.size() ); + + //fill in each member's entry. + Internal_GuildMemberEntry_Struct *e = gms->member; + + cur = members.begin(); + end = members.end(); + for(; cur != end; cur++) { + ci = *cur; + + //the order we set things here must match the struct + +//nice helper macro +#define SlideStructString(field, str) \ + strcpy(field, str.c_str()); \ + field += str.length() + 1 +#define PutField(field) \ + e->field = ci->field + + SlideStructString( name_buf, ci->char_name ); + PutField(level); + e->banker = ci->banker + (ci->alt * 2); // low bit is banker flag, next bit is 'alt' flag. + PutField(class_); + PutField(rank); + PutField(time_last_on); + PutField(tribute_enable); + PutField(total_tribute); + PutField(last_tribute); + SlideStructString( note_buf, ci->public_note ); + e->zoneinstance = 0; + e->zone_id = 0; // Flag them as offline (zoneid 0) as world will update us with their online status afterwards. +#undef SlideStructString +#undef PutFieldN + + delete *cur; + + e++; + } + + return(retbuffer); +} + +void ZoneGuildManager::ListGuilds(Client *c) const { + c->Message(0, "Listing guilds on the server:"); + char leadername[64]; + map::const_iterator cur, end; + cur = m_guilds.begin(); + end = m_guilds.end(); + int r = 0; + for(; cur != end; cur++) { + leadername[0] = '\0'; + database.GetCharName(cur->second->leader_char_id, leadername); + if (leadername[0] == '\0') + c->Message(0, " Guild #%i <%s>", cur->first, cur->second->name.c_str()); + else + c->Message(0, " Guild #%i <%s> Leader: %s", cur->first, cur->second->name.c_str(), leadername); + r++; + } + c->Message(0, "%i guilds listed.", r); +} + + +void ZoneGuildManager::DescribeGuild(Client *c, uint32 guild_id) const { + map::const_iterator res; + res = m_guilds.find(guild_id); + if(res == m_guilds.end()) { + c->Message(0, "Guild %d not found.", guild_id); + return; + } + + const GuildInfo *info = res->second; + + c->Message(0, "Guild info DB# %i <%s>", guild_id, info->name.c_str()); + + char leadername[64]; + database.GetCharName(info->leader_char_id, leadername); + c->Message(0, "Guild Leader: %s", leadername); + + char permbuffer[256]; + uint8 i; + for (i = 0; i <= GUILD_MAX_RANK; i++) { + char *permptr = permbuffer; + uint8 r; + for(r = 0; r < _MaxGuildAction; r++) + permptr += sprintf(permptr, " %s: %c", GuildActionNames[r], info->ranks[i].permissions[r]?'Y':'N'); + + c->Message(0, "Rank %i: %s", i, info->ranks[i].name.c_str()); + c->Message(0, "Permissions: %s", permbuffer); + } + +} + +//in theory, we could get a pile of unused entries in this array, but only if +//we had a malicious client sending controlled packets, plus its like 10 bytes per entry. +void ZoneGuildManager::RecordInvite(uint32 char_id, uint32 guild_id, uint8 rank) { + m_inviteQueue[char_id] = pair(guild_id, rank); +} + +bool ZoneGuildManager::VerifyAndClearInvite(uint32 char_id, uint32 guild_id, uint8 rank) { + map >::iterator res; + res = m_inviteQueue.find(char_id); + if(res == m_inviteQueue.end()) + return(false); //no entry... + bool valid = false; + if(res->second.first == guild_id && res->second.second == rank) { + valid = true; + } + m_inviteQueue.erase(res); + return(valid); +} + + + +void ZoneGuildManager::ProcessWorldPacket(ServerPacket *pack) { + switch(pack->opcode) { + case ServerOP_RefreshGuild: { + if(pack->size != sizeof(ServerGuildRefresh_Struct)) { + _log(GUILDS__ERROR, "Received ServerOP_RefreshGuild of incorrect size %d, expected %d", pack->size, sizeof(ServerGuildRefresh_Struct)); + return; + } + ServerGuildRefresh_Struct *s = (ServerGuildRefresh_Struct *) pack->pBuffer; + + _log(GUILDS__REFRESH, "Received guild refresh from world for %d, changes: name=%d, motd=%d, rank=%d, relation=%d", s->guild_id, s->name_change, s->motd_change, s->rank_change, s->relation_change); + + //reload all the guild details from the database. + RefreshGuild(s->guild_id); + + if(s->motd_change) { + //resend guild MOTD to all guild members in this zone. + entity_list.SendGuildMOTD(s->guild_id); + } + + if(s->name_change) { + //until we figure out the guild update packet, we resend the whole guild list. + entity_list.SendGuildList(); + } + + if(s->rank_change) { + //we need to send spawn appearance packets for all members of this guild in the zone, to everybody. + entity_list.SendGuildSpawnAppearance(s->guild_id); + } + + if(s->relation_change) { + //unknown until we implement guild relations. + } + + break; + } + + case ServerOP_GuildCharRefresh: { + if(pack->size != sizeof(ServerGuildCharRefresh_Struct)) { + _log(GUILDS__ERROR, "Received ServerOP_RefreshGuild of incorrect size %d, expected %d", pack->size, sizeof(ServerGuildCharRefresh_Struct)); + return; + } + ServerGuildCharRefresh_Struct *s = (ServerGuildCharRefresh_Struct *) pack->pBuffer; + + _log(GUILDS__REFRESH, "Received guild member refresh from world for char %d from guild %d", s->char_id, s->guild_id); + + Client *c = entity_list.GetClientByCharID(s->char_id); + + if(c != NULL) { + //this reloads the char's guild info from the database and sends appearance updates + c->RefreshGuildInfo(); + } + + //it would be nice if we had the packet to send just a one-person update + if(s->guild_id == GUILD_NONE) { + if(c != NULL) + c->SendGuildMembers(); //only need to update this player's list (trying to clear it) + } else { + entity_list.SendGuildMembers(s->guild_id); //even send GUILD_NONE (empty) + } + + if(s->old_guild_id != 0 && s->old_guild_id != GUILD_NONE && s->old_guild_id != s->guild_id) + entity_list.SendGuildMembers(s->old_guild_id); + else if(c != NULL && s->guild_id != GUILD_NONE) { + //char is in zone, and has changed into a new guild, send MOTD. + c->SendGuildMOTD(); + } + + + break; + } + + case ServerOP_GuildRankUpdate: + { + if(ZoneLoaded) + { + if(pack->size != sizeof(ServerGuildRankUpdate_Struct)) + { + _log(GUILDS__ERROR, "Received ServerOP_RankUpdate of incorrect size %d, expected %d", + pack->size, sizeof(ServerGuildRankUpdate_Struct)); + + return; + } + + ServerGuildRankUpdate_Struct *sgrus = (ServerGuildRankUpdate_Struct*)pack->pBuffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SetGuildRank, sizeof(GuildSetRank_Struct)); + + GuildSetRank_Struct *gsrs = (GuildSetRank_Struct*)outapp->pBuffer; + + gsrs->Rank = sgrus->Rank; + strn0cpy(gsrs->MemberName, sgrus->MemberName, sizeof(gsrs->MemberName)); + gsrs->Banker = sgrus->Banker; + + entity_list.QueueClientsGuild(NULL, outapp, false, sgrus->GuildID); + + safe_delete(outapp); + } + + break; + } + + case ServerOP_DeleteGuild: { + if(pack->size != sizeof(ServerGuildID_Struct)) { + _log(GUILDS__ERROR, "Received ServerOP_DeleteGuild of incorrect size %d, expected %d", pack->size, sizeof(ServerGuildID_Struct)); + return; + } + ServerGuildID_Struct *s = (ServerGuildID_Struct *) pack->pBuffer; + + _log(GUILDS__REFRESH, "Received guild delete from world for guild %d", s->guild_id); + + //clear all the guild tags. + entity_list.RefreshAllGuildInfo(s->guild_id); + + //remove the guild data from the local guild manager + guild_mgr.LocalDeleteGuild(s->guild_id); + + //if we stop forcing guild list to send on guild create, we need to do this: + //in the case that we delete a guild and add a new one. + //entity_list.SendGuildList(); + + break; + } + + case ServerOP_GuildMemberUpdate: + { + ServerGuildMemberUpdate_Struct *sgmus = (ServerGuildMemberUpdate_Struct*)pack->pBuffer; + + if(ZoneLoaded) + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildMemberUpdate, sizeof(GuildMemberUpdate_Struct)); + + GuildMemberUpdate_Struct *gmus = (GuildMemberUpdate_Struct*)outapp->pBuffer; + + gmus->GuildID = sgmus->GuildID; + strn0cpy(gmus->MemberName, sgmus->MemberName, sizeof(gmus->MemberName)); + gmus->ZoneID = sgmus->ZoneID; + gmus->InstanceID = 0; // I don't think we care what Instance they are in, for the Guild Management Window. + gmus->LastSeen = sgmus->LastSeen; + + entity_list.QueueClientsGuild(NULL, outapp, false, sgmus->GuildID); + + safe_delete(outapp); + } + break; + } + case ServerOP_OnlineGuildMembersResponse: + if (ZoneLoaded) + { + char *Buffer = (char *)pack->pBuffer; + + uint32 FromID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + uint32 Count = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + Client *c = entity_list.GetClientByCharID(FromID); + + if (!c || !c->IsInAGuild()) + { + _log(GUILDS__ERROR,"Invalid Client or not in guild. ID=%i", FromID); + break; + } + _log(GUILDS__IN_PACKETS,"Processing ServerOP_OnlineGuildMembersResponse"); + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildMemberUpdate, sizeof(GuildMemberUpdate_Struct)); + GuildMemberUpdate_Struct *gmus = (GuildMemberUpdate_Struct*)outapp->pBuffer; + char Name[64]; + gmus->LastSeen = time(NULL); + gmus->InstanceID = 0; + gmus->GuildID = c->GuildID(); + for (int i=0;iMemberName, Name, sizeof(gmus->MemberName)); + gmus->ZoneID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + _log(GUILDS__OUT_PACKETS,"Sending OP_GuildMemberUpdate to %i. Name=%s ZoneID=%i",FromID,Name,gmus->ZoneID); + c->QueuePacket(outapp); + } + safe_delete(outapp); + + } + break; + + case ServerOP_LFGuildUpdate: + { + if(ZoneLoaded) + { + char GuildName[33]; + char Comments[257]; + uint32 FromLevel, ToLevel, Classes, AACount, TimeZone, TimePosted, Toggle; + + pack->ReadString(GuildName); + pack->ReadString(Comments); + FromLevel = pack->ReadUInt32(); + ToLevel = pack->ReadUInt32(); + Classes = pack->ReadUInt32(); + AACount = pack->ReadUInt32(); + TimeZone = pack->ReadUInt32(); + TimePosted = pack->ReadUInt32(); + Toggle = pack->ReadUInt32(); + + uint32 GuildID = GetGuildIDByName(GuildName); + + if(GuildID == GUILD_NONE) + break; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, sizeof(LFGuild_GuildToggle_Struct)); + + LFGuild_GuildToggle_Struct *gts = (LFGuild_GuildToggle_Struct *)outapp->pBuffer; + gts->Command = 1; + strcpy(gts->Comment, Comments); + gts->FromLevel = FromLevel; + gts->ToLevel = ToLevel; + gts->Classes = Classes; + gts->AACount = AACount; + gts->TimeZone = TimeZone; + gts->Toggle = Toggle; + gts->TimePosted = TimePosted; + gts->Name[0] = 0; + entity_list.QueueClientsGuild(NULL, outapp, false, GuildID); + safe_delete(outapp); + break; + } + } + } +} + +void ZoneGuildManager::SendGuildMemberUpdateToWorld(const char *MemberName, uint32 GuildID, uint16 ZoneID, uint32 LastSeen) +{ + ServerPacket* pack = new ServerPacket(ServerOP_GuildMemberUpdate, sizeof(ServerGuildMemberUpdate_Struct)); + + ServerGuildMemberUpdate_Struct *sgmus = (ServerGuildMemberUpdate_Struct*)pack->pBuffer; + sgmus->GuildID = GuildID; + strn0cpy(sgmus->MemberName, MemberName, sizeof(sgmus->MemberName)); + sgmus->ZoneID = ZoneID; + sgmus->LastSeen = LastSeen; + worldserver.SendPacket(pack); + + safe_delete(pack); +} + +void ZoneGuildManager::RequestOnlineGuildMembers(uint32 FromID, uint32 GuildID) +{ + ServerPacket* pack = new ServerPacket(ServerOP_RequestOnlineGuildMembers, sizeof(ServerRequestOnlineGuildMembers_Struct)); + ServerRequestOnlineGuildMembers_Struct *srogm = (ServerRequestOnlineGuildMembers_Struct*)pack->pBuffer; + + srogm->FromID = FromID; + srogm->GuildID = GuildID; + worldserver.SendPacket(pack); + + safe_delete(pack); +} + +void ZoneGuildManager::ProcessApproval() +{ + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if(!iterator.GetData()->ProcessApproval()) + iterator.RemoveCurrent(); + iterator.Advance(); + } +} + +void ZoneGuildManager::AddGuildApproval(const char* guildname,Client* owner) +{ + GuildApproval* tmp = new GuildApproval(guildname,owner,GetFreeID()); + list.Insert(tmp); +} + +void ZoneGuildManager::AddMemberApproval(uint32 refid,Client* name) +{ + GuildApproval* tmp = FindGuildByIDApproval(refid); + if(tmp != 0) + { + if(!tmp->AddMemberApproval(name)) + name->Message(0,"Unable to add to list."); + else + { + name->Message(0,"Added to list."); + } + } + else + name->Message(0,"Unable to find guild reference id."); +} + +ZoneGuildManager::~ZoneGuildManager() +{ + ClearGuilds(); +} + +void ZoneGuildManager::ClearGuildsApproval() +{ + list.Clear(); +} + +GuildApproval* ZoneGuildManager::FindGuildByIDApproval(uint32 refid) +{ + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData()->GetID() == refid) + return iterator.GetData(); + iterator.Advance(); + } + return 0; +} + +GuildApproval* ZoneGuildManager::FindGuildByOwnerApproval(Client* owner) +{ + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData()->GetOwner() == owner) + return iterator.GetData(); + iterator.Advance(); + } + return 0; +} + +GuildBankManager::~GuildBankManager() +{ + std::list::iterator Iterator = Banks.begin(); + + while(Iterator != Banks.end()) + { + safe_delete(*Iterator); + + ++Iterator; + } +} + +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); + + return false; + } + + return true; + +} + +bool GuildBankManager::IsLoaded(uint32 GuildID) +{ + std::list::iterator Iterator = GetGuildBank(GuildID); + + return (Iterator != Banks.end()); +} + +void GuildBankManager::SendGuildBank(Client *c) +{ + if(!c || !c->IsInAGuild()) + return; + + if(!IsLoaded(c->GuildID())) + Load(c->GuildID()); + + std::list::iterator Iterator = GetGuildBank(c->GuildID()); + + if(Iterator == Banks.end()) + { + _log(GUILDS__BANK_ERROR, "Unable to find guild bank for guild ID %i", c->GuildID()); + + return; + } + + for(int i = 0; i < GUILD_BANK_DEPOSIT_AREA_SIZE; ++i) + { + if((*Iterator)->Items.DepositArea[i].ItemID > 0) + { + const Item_Struct *Item = database.GetItem((*Iterator)->Items.DepositArea[i].ItemID); + + if(!Item) + continue; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildBank, sizeof(GuildBankItemUpdate_Struct)); + + GuildBankItemUpdate_Struct *gbius = (GuildBankItemUpdate_Struct*)outapp->pBuffer; + + if(!Item->Stackable) + gbius->Init(GuildBankItemUpdate, 1, i, GuildBankDepositArea, 1, Item->ID, Item->Icon, 1, + (*Iterator)->Items.DepositArea[i].Permissions, 0, 0); + else + { + if((*Iterator)->Items.DepositArea[i].Quantity == Item->StackSize) + gbius->Init(GuildBankItemUpdate, 1, i, GuildBankDepositArea, 1, Item->ID, Item->Icon, + (*Iterator)->Items.DepositArea[i].Quantity, (*Iterator)->Items.DepositArea[i].Permissions, 0, 0); + else + gbius->Init(GuildBankItemUpdate, 1, i, GuildBankDepositArea, 1, Item->ID, Item->Icon, + (*Iterator)->Items.DepositArea[i].Quantity, (*Iterator)->Items.DepositArea[i].Permissions, 1, 0); + } + + strn0cpy(gbius->ItemName, Item->Name, sizeof(gbius->ItemName)); + + strn0cpy(gbius->Donator, (*Iterator)->Items.DepositArea[i].Donator, sizeof(gbius->Donator)); + + strn0cpy(gbius->WhoFor, (*Iterator)->Items.DepositArea[i].WhoFor, sizeof(gbius->WhoFor)); + + c->FastQueuePacket(&outapp); + } + } + + for(int i = 0; i < GUILD_BANK_MAIN_AREA_SIZE; ++i) + { + if((*Iterator)->Items.MainArea[i].ItemID > 0) + { + const Item_Struct *Item = database.GetItem((*Iterator)->Items.MainArea[i].ItemID); + + if(!Item) + continue; + + bool Useable = Item->IsEquipable(c->GetBaseRace(), c->GetBaseClass()); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GuildBank, sizeof(GuildBankItemUpdate_Struct)); + + GuildBankItemUpdate_Struct *gbius = (GuildBankItemUpdate_Struct*)outapp->pBuffer; + + if(!Item->Stackable) + gbius->Init(GuildBankItemUpdate, 1, i, GuildBankMainArea, 1, Item->ID, Item->Icon, 1, + (*Iterator)->Items.MainArea[i].Permissions, 0, Useable); + else + { + if((*Iterator)->Items.MainArea[i].Quantity == Item->StackSize) + gbius->Init(GuildBankItemUpdate, 1, i, GuildBankMainArea, 1, Item->ID, Item->Icon, + (*Iterator)->Items.MainArea[i].Quantity, (*Iterator)->Items.MainArea[i].Permissions, 0, Useable); + else + gbius->Init(GuildBankItemUpdate, 1, i, GuildBankMainArea, 1, Item->ID, Item->Icon, + (*Iterator)->Items.MainArea[i].Quantity, (*Iterator)->Items.MainArea[i].Permissions, 1, Useable); + } + + strn0cpy(gbius->ItemName, Item->Name, sizeof(gbius->ItemName)); + + strn0cpy(gbius->Donator, (*Iterator)->Items.MainArea[i].Donator, sizeof(gbius->Donator)); + + strn0cpy(gbius->WhoFor, (*Iterator)->Items.MainArea[i].WhoFor, sizeof(gbius->WhoFor)); + + c->FastQueuePacket(&outapp); + } + } +} +bool GuildBankManager::IsAreaFull(uint32 GuildID, uint16 Area) +{ + std::list::iterator Iterator = GetGuildBank(GuildID); + + if(Iterator == Banks.end()) + return true; + + GuildBankItem* BankArea = NULL; + + int AreaSize = 0; + + if(Area == GuildBankMainArea) + { + BankArea = &(*Iterator)->Items.MainArea[0]; + + AreaSize = GUILD_BANK_MAIN_AREA_SIZE; + } + else + { + BankArea = &(*Iterator)->Items.DepositArea[0]; + + AreaSize = GUILD_BANK_DEPOSIT_AREA_SIZE; + } + + for(int i = 0; i < AreaSize; ++i) + if(BankArea[i].ItemID == 0) + return false; + + return true; +} + +bool GuildBankManager::AddItem(uint32 GuildID, uint8 Area, uint32 ItemID, int32 QtyOrCharges, const char *Donator, uint8 Permissions, const char *WhoFor) +{ + std::list::iterator Iterator = GetGuildBank(GuildID); + + if(Iterator == Banks.end()) + { + _log(GUILDS__BANK_ERROR, "Unable to find guild bank for guild ID %i", GuildID); + + return false; + } + + GuildBankItem* BankArea = NULL; + + int AreaSize = 0; + + if(Area == GuildBankMainArea) + { + BankArea = &(*Iterator)->Items.MainArea[0]; + + AreaSize = GUILD_BANK_MAIN_AREA_SIZE; + } + else + { + BankArea = &(*Iterator)->Items.DepositArea[0]; + + AreaSize = GUILD_BANK_DEPOSIT_AREA_SIZE; + } + + int Slot = -1; + + for(int i = 0; i < AreaSize; ++i) + { + if(BankArea[i].ItemID == 0) + { + BankArea[i].ItemID = ItemID; + + BankArea[i].Quantity = QtyOrCharges; + + strn0cpy(BankArea[i].Donator, Donator, sizeof(BankArea[i].Donator)); + + BankArea[i].Permissions = Permissions; + + strn0cpy(BankArea[i].WhoFor, WhoFor, sizeof(BankArea[i].WhoFor)); + + Slot = i; + + break; + } + } + + if(Slot < 0) + { + _log(GUILDS__BANK_ERROR, "No space to add item to the guild bank."); + + 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); + + return false; + } + + safe_delete_array(query); + + const Item_Struct *Item = database.GetItem(ItemID); + + GuildBankItemUpdate_Struct gbius; + + if(!Item->Stackable) + gbius.Init(GuildBankItemUpdate, 1, Slot, Area, 1, ItemID, Item->Icon, Item->Stackable ? QtyOrCharges : 1, Permissions, 0, 0); + else + { + if(QtyOrCharges == Item->StackSize) + gbius.Init(GuildBankItemUpdate, 1, Slot, Area, 1, ItemID, Item->Icon, Item->Stackable ? QtyOrCharges : 1, Permissions, 0, 0); + else + gbius.Init(GuildBankItemUpdate, 1, Slot, Area, 1, ItemID, Item->Icon, Item->Stackable ? QtyOrCharges : 1, Permissions, 1, 0); + } + + strn0cpy(gbius.ItemName, Item->Name, sizeof(gbius.ItemName)); + + strn0cpy(gbius.Donator, Donator, sizeof(gbius.Donator)); + + strn0cpy(gbius.WhoFor, WhoFor, sizeof(gbius.WhoFor)); + + entity_list.QueueClientsGuildBankItemUpdate(&gbius, GuildID); + + return true; +} + +int GuildBankManager::Promote(uint32 GuildID, int SlotID) +{ + if((SlotID < 0) || (SlotID > (GUILD_BANK_DEPOSIT_AREA_SIZE - 1))) + return -1; + + std::list::iterator Iterator = GetGuildBank(GuildID); + + if(Iterator == Banks.end()) + { + return -1; + } + + if((*Iterator)->Items.DepositArea[SlotID].ItemID == 0) + { + return -1; + } + + int MainSlot = -1; + + for(int i = 0; i < GUILD_BANK_MAIN_AREA_SIZE; ++i) + if((*Iterator)->Items.MainArea[i].ItemID == 0) + { + MainSlot = i; + + break; + } + + if(MainSlot == -1) + return -1; + + + (*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); + + return -1; + } + + safe_delete_array(query); + + (*Iterator)->Items.DepositArea[SlotID].ItemID = 0; + + const Item_Struct *Item = database.GetItem((*Iterator)->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); + 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); + else + gbius.Init(GuildBankItemUpdate, 1, MainSlot, GuildBankMainArea, 1, Item->ID, Item->Icon, + (*Iterator)->Items.MainArea[MainSlot].Quantity, 0, 1, 0); + } + + strn0cpy(gbius.ItemName, Item->Name, sizeof(gbius.ItemName)); + + entity_list.QueueClientsGuildBankItemUpdate(&gbius, GuildID); + + gbius.Init(GuildBankItemUpdate, 1, SlotID, GuildBankDepositArea, 0, 0, 0, 0, 0, 0, 0); + + entity_list.QueueClientsGuildBankItemUpdate(&gbius, GuildID); + + return MainSlot; +} + +void GuildBankManager::SetPermissions(uint32 GuildID, uint16 SlotID, uint32 Permissions, const char *MemberName) +{ + if((SlotID > (GUILD_BANK_MAIN_AREA_SIZE - 1))) + return; + + std::list::iterator Iterator = GetGuildBank(GuildID); + + if(Iterator == Banks.end()) + { + return; + } + + if((*Iterator)->Items.MainArea[SlotID].ItemID == 0) + { + return; + } + + 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)); + else + (*Iterator)->Items.MainArea[SlotID].WhoFor[0] = '\0'; + + + const Item_Struct *Item = database.GetItem((*Iterator)->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); + 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); + else + gbius.Init(GuildBankItemUpdate, 1, SlotID, GuildBankMainArea, 1, Item->ID, Item->Icon, + (*Iterator)->Items.MainArea[SlotID].Quantity, (*Iterator)->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)); + + entity_list.QueueClientsGuildBankItemUpdate(&gbius, GuildID); +} + +ItemInst* GuildBankManager::GetItem(uint32 GuildID, uint16 Area, uint16 SlotID, uint32 Quantity) +{ + std::list::iterator Iterator = GetGuildBank(GuildID); + + if(Iterator == Banks.end()) + return NULL; + + GuildBankItem* BankArea = NULL; + + ItemInst* inst = NULL; + + if(Area == GuildBankDepositArea) + { + if((SlotID > (GUILD_BANK_DEPOSIT_AREA_SIZE - 1))) + return NULL; + + inst = database.CreateItem((*Iterator)->Items.DepositArea[SlotID].ItemID); + + if(!inst) + return NULL; + + BankArea = &(*Iterator)->Items.DepositArea[0]; + } + else + { + + if((SlotID > (GUILD_BANK_MAIN_AREA_SIZE - 1))) + return NULL; + + inst = database.CreateItem((*Iterator)->Items.MainArea[SlotID].ItemID); + + if(!inst) + return NULL; + + BankArea = &(*Iterator)->Items.MainArea[0]; + } + + if(!inst->IsStackable()) + inst->SetCharges(BankArea[SlotID].Quantity); + else + { + if(Quantity <= BankArea[SlotID].Quantity) + inst->SetCharges(Quantity); + else + inst->SetCharges(BankArea[SlotID].Quantity); + } + + return inst; +} + +bool GuildBankManager::HasItem(uint32 GuildID, uint32 ItemID) +{ + std::list::iterator Iterator = GetGuildBank(GuildID); + + if(Iterator == Banks.end()) + return false; + + for(int i = 0; i < GUILD_BANK_MAIN_AREA_SIZE; ++i) + if((*Iterator)->Items.MainArea[i].ItemID == ItemID) + return true; + + for(int i = 0; i < GUILD_BANK_DEPOSIT_AREA_SIZE; ++i) + if((*Iterator)->Items.DepositArea[i].ItemID == ItemID) + return true; + + return false; +} + +std::list::iterator GuildBankManager::GetGuildBank(uint32 GuildID) +{ + std::list::iterator Iterator = Banks.begin(); + + while(Iterator != Banks.end()) + { + if((*Iterator)->GuildID == GuildID) + break; + + ++Iterator; + } + + return Iterator; +} + +bool GuildBankManager::DeleteItem(uint32 GuildID, uint16 Area, uint16 SlotID, uint32 Quantity) +{ + std::list::iterator Iterator = GetGuildBank(GuildID); + + if(Iterator == Banks.end()) + return false; + + char errbuf[MYSQL_ERRMSG_SIZE]; + + char* query = 0; + + GuildBankItem* BankArea = NULL; + + if(Area == GuildBankMainArea) + { + if(SlotID > (GUILD_BANK_MAIN_AREA_SIZE - 1)) + return false; + + BankArea = &(*Iterator)->Items.MainArea[0]; + } + else + { + if(SlotID > (GUILD_BANK_DEPOSIT_AREA_SIZE - 1)) + return false; + + BankArea = &(*Iterator)->Items.DepositArea[0]; + } + + + 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); + + 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); + + return false; + } + + safe_delete_array(query); + + BankArea[SlotID].Quantity -= Quantity; + + Deleted = false; + } + GuildBankItemUpdate_Struct gbius; + + if(!Deleted) + { + 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)); + } + else + gbius.Init(GuildBankItemUpdate, 1, SlotID, Area, 0, 0, 0, 0, 0, 0, 0); + + entity_list.QueueClientsGuildBankItemUpdate(&gbius, GuildID); + + return true; + +} + +bool GuildBankManager::MergeStacks(uint32 GuildID, uint16 SlotID) +{ + if(SlotID > (GUILD_BANK_MAIN_AREA_SIZE - 1)) + return false; + + std::list::iterator Iterator = GetGuildBank(GuildID); + + if(Iterator == Banks.end()) + return false; + + GuildBankItem* BankArea = &(*Iterator)->Items.MainArea[0]; + + if(BankArea[SlotID].ItemID == 0) + return false; + + const Item_Struct *Item = database.GetItem(BankArea[SlotID].ItemID); + + if(!Item->Stackable) + return false; + + uint32 ItemID = BankArea[SlotID].ItemID; + + for(int i = 0; i < GUILD_BANK_MAIN_AREA_SIZE - 1; ++i) + { + if(BankArea[i].ItemID != ItemID) + continue; + + if(BankArea[i].Quantity == Item->StackSize) + continue; + + bool Merged = false; + + for(int j = i + 1; j < GUILD_BANK_MAIN_AREA_SIZE; ++j) + { + if(BankArea[j].ItemID != ItemID) + continue; + + if(BankArea[j].Permissions != BankArea[i].Permissions) + continue; + + if(BankArea[i].Permissions == 1) + if(strncmp(BankArea[i].WhoFor, BankArea[j].WhoFor, sizeof(BankArea[i].WhoFor))) + continue; + + if((BankArea[i].Quantity + BankArea[j].Quantity) <= Item->StackSize) + { + BankArea[i].Quantity += BankArea[j].Quantity; + + DeleteItem(GuildID, GuildBankMainArea, j, BankArea[j].Quantity); + + Merged = true; + + if(BankArea[i].Quantity == Item->StackSize) + break; + } + else + { + uint32 QuantityToMove = Item->StackSize - BankArea[i].Quantity; + + DeleteItem(GuildID, GuildBankMainArea, j, QuantityToMove); + + BankArea[i].Quantity = Item->StackSize; + + Merged = true; + + break; + } + } + + if(Merged) + { + UpdateItemQuantity(GuildID, GuildBankMainArea, i, BankArea[i].Quantity); + + GuildBankItemUpdate_Struct gbius; + + if(BankArea[i].Quantity == Item->StackSize) + gbius.Init(GuildBankItemUpdate, 1, i, GuildBankMainArea, 1, ItemID, Item->Icon, BankArea[i].Quantity, BankArea[i].Permissions, 0, 0); + else + gbius.Init(GuildBankItemUpdate, 1, i, GuildBankMainArea, 1, ItemID, Item->Icon, BankArea[i].Quantity, BankArea[i].Permissions, 1, 0); + + strn0cpy(gbius.ItemName, Item->Name, sizeof(gbius.ItemName)); + + strn0cpy(gbius.WhoFor, BankArea[i].WhoFor, sizeof(gbius.WhoFor)); + + entity_list.QueueClientsGuildBankItemUpdate(&gbius, GuildID); + } + + } + + return true; +} + +bool GuildBankManager::SplitStack(uint32 GuildID, uint16 SlotID, uint32 Quantity) +{ + if(SlotID > (GUILD_BANK_MAIN_AREA_SIZE - 1)) + return false; + + std::list::iterator Iterator = GetGuildBank(GuildID); + + if(Iterator == Banks.end()) + return false; + + if(IsAreaFull(GuildID, GuildBankMainArea)) + return false; + + GuildBankItem* BankArea = &(*Iterator)->Items.MainArea[0]; + + if(BankArea[SlotID].ItemID == 0) + return false; + + if(BankArea[SlotID].Quantity <= Quantity || Quantity == 0) + return false; + + const Item_Struct *Item = database.GetItem(BankArea[SlotID].ItemID); + + if(!Item->Stackable) + return false; + + AddItem(GuildID, GuildBankMainArea, BankArea[SlotID].ItemID, Quantity, "", BankArea[SlotID].Permissions, BankArea[SlotID].WhoFor); + + DeleteItem(GuildID, GuildBankMainArea, SlotID, Quantity); + + return true; +} + +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); + + return; + } + + safe_delete_array(query); +} + +bool GuildBankManager::AllowedToWithdraw(uint32 GuildID, uint16 Area, uint16 SlotID, const char *Name) +{ + // Is a none-Guild Banker allowed to withdraw the item at this slot ? + // This is really here for anti-hacking measures, as the client should not request an item it does not have permission to withdraw. + // + if(SlotID > (GUILD_BANK_MAIN_AREA_SIZE - 1)) + return false; + + std::list::iterator Iterator = GetGuildBank(GuildID); + + if(Iterator == Banks.end()) + return false; + + if(Area != GuildBankMainArea) + return false; + + uint8 Permissions = (*Iterator)->Items.MainArea[SlotID].Permissions; + + if(Permissions == GuildBankBankerOnly) + return false; + + if(Permissions != GuildBankSingleMember) // Public or Public-If-Useable (should really check if item is useable) + return true; + + if(!strncmp((*Iterator)->Items.MainArea[SlotID].WhoFor, Name, sizeof((*Iterator)->Items.MainArea[SlotID].WhoFor))) + return true; + + return false; +} + +/*================== GUILD APPROVAL ========================*/ + +bool GuildApproval::ProcessApproval() +{ + if(owner && owner->GuildID() != 0) + { + owner->Message(10,"You are already in a guild! Guild request deleted."); + return false; + } + if(deletion_timer->Check() || !owner) + { + if(owner) + owner->Message(0,"You took too long! Your guild request has been deleted."); + return false; + } + + return true; +} + +GuildApproval::GuildApproval(const char* guildname, Client* owner,uint32 id) +{ + database.GetVariable("GuildCreation", founders, 3); + uint8 tmp = atoi(founders); + deletion_timer = new Timer(1800000); + strcpy(guild,guildname); + this->owner = owner; + this->refid = id; + if(owner) + owner->Message(0,"You can now start getting your guild approved, tell your %i members to #guildapprove %i, you have 30 minutes to create your guild.",tmp,GetID()); + for(int i=0;iMessage(0,"%i: %s",i,members[i]->GetName()); + } +} + +void GuildApproval::GuildApproved() +{ + char petitext[PBUFFER] = "A new guild was founded! Guildname: "; + char gmembers[MBUFFER] = " "; + + if(!owner) + return; + database.GetVariable("GuildCreation", founders, 3); + uint8 tmp = atoi(founders); + uint32 tmpeq = guild_mgr.CreateGuild(guild, owner->CharacterID()); + guild_mgr.SetGuild(owner->CharacterID(),tmpeq,2); + owner->SendAppearancePacket(AT_GuildID,true,false); + for(int i=0;iMessage(0, "%s",members[i]->GetName()); + owner->Message(0, "%i",members[i]->CharacterID()); + if(guild_mgr.IsGuildLeader(tmpeq,members[i]->CharacterID())); + guild_mgr.SetGuild(members[i]->CharacterID(),tmpeq,0); + size_t len = MBUFFER - strlen(gmembers)+1; + strncat(gmembers," ",len); + strncat(gmembers,members[i]->GetName(),len); + } + } + size_t len = PBUFFER - strlen(petitext)+1; + strncat(petitext,guild,len); + strncat(petitext," Leader: ",len); + strncat(petitext,owner->CastToClient()->GetName(),len); + strncat(petitext," Members:",len); + strncat(petitext,gmembers,len); + Petition* pet = new Petition(owner->CastToClient()->CharacterID()); + pet->SetAName(owner->CastToClient()->AccountName()); + pet->SetClass(owner->CastToClient()->GetClass()); + pet->SetLevel(owner->CastToClient()->GetLevel()); + pet->SetCName(owner->CastToClient()->GetName()); + pet->SetRace(owner->CastToClient()->GetRace()); + pet->SetLastGM(""); + pet->SetCName(owner->CastToClient()->GetName()); //aza77 is this really 2 times needed ?? + pet->SetPetitionText(petitext); + 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", owner->CastToClient()->GetName(), pet->GetID()); + ServerPacket* pack = new ServerPacket; + pack->opcode = ServerOP_RefreshGuild; + pack->size = tmp; + pack->pBuffer = new uchar[pack->size]; + memcpy(pack->pBuffer, &tmpeq, 4); + worldserver.SendPacket(pack); + safe_delete(pack); + owner->Message(0, "Your guild was created."); + owner = 0; +} diff --git a/zone/guild_mgr.h b/zone/guild_mgr.h new file mode 100644 index 000000000..8a5deaad8 --- /dev/null +++ b/zone/guild_mgr.h @@ -0,0 +1,144 @@ +#ifndef GUILD_MGR_H_ +#define GUILD_MGR_H_ + +#include "../common/types.h" +#include "../common/guild_base.h" +#include +#include +#include "../zone/petitions.h" + +extern PetitionList petition_list; +//extern GuildRanks_Struct guilds[512]; +//extern ZoneDatabase database; + +#define PBUFFER 50 +#define MBUFFER 50 + +#define GUILD_BANK_MAIN_AREA_SIZE 200 +#define GUILD_BANK_DEPOSIT_AREA_SIZE 20 +class Client; +class ServerPacket; + +struct GuildBankItem +{ + uint32 ItemID; + uint32 Quantity; + char Donator[64]; + uint8 Permissions; + char WhoFor[64]; +}; + +struct GuildBankItems +{ + GuildBankItem MainArea[GUILD_BANK_MAIN_AREA_SIZE]; + GuildBankItem DepositArea[GUILD_BANK_DEPOSIT_AREA_SIZE]; +}; + +struct GuildBank +{ + uint32 GuildID; + GuildBankItems Items; +}; + +enum { GuildBankBulkItems = 0, GuildBankItemUpdate = 1, GuildBankPromote = 3, GuildBankViewItem = 4, GuildBankDeposit = 5, + GuildBankPermissions = 6, GuildBankWithdraw = 7, GuildBankSplitStacks = 8, GuildBankMergeStacks = 9, GuildBankAcknowledge = 10 }; + +enum { GuildBankDepositArea = 0, GuildBankMainArea = 1 }; + +enum { GuildBankBankerOnly = 0, GuildBankSingleMember = 1, GuildBankPublicIfUsable = 2, GuildBankPublic = 3 }; + +class GuildApproval +{ +public: + GuildApproval(const char* guildname,Client* owner,uint32 id); + ~GuildApproval(); + bool ProcessApproval(); + bool AddMemberApproval(Client* addition); + uint32 GetID() { return refid; } + Client* GetOwner() { return owner; } + void GuildApproved(); + void ApprovedMembers(Client* requestee); +private: + Timer* deletion_timer; + char guild[16]; + char founders[3]; + Client* owner; + Client* members[6]; + uint32 refid; +}; + +class ZoneGuildManager : public BaseGuildManager { +public: + ~ZoneGuildManager(void); + + void AddGuildApproval(const char* guildname, Client* owner); + void AddMemberApproval(uint32 refid,Client* name); + void ClearGuildsApproval(); + GuildApproval* FindGuildByIDApproval(uint32 refid); + GuildApproval* FindGuildByOwnerApproval(Client* owner); + void ProcessApproval(); + uint32 GetFreeID() { return id+1; } + //called by worldserver when it receives a message from world. + void ProcessWorldPacket(ServerPacket *pack); + + void ListGuilds(Client *c) const; + void DescribeGuild(Client *c, uint32 guild_id) const; + + +// bool DonateTribute(uint32 charid, uint32 guild_id, uint32 tribute_amount); + + uint8 *MakeGuildMembers(uint32 guild_id, const char *prefix_name, uint32 &length); //make a guild member list packet, returns ownership of the buffer. + + void RecordInvite(uint32 char_id, uint32 guild_id, uint8 rank); + bool VerifyAndClearInvite(uint32 char_id, uint32 guild_id, uint8 rank); + void SendGuildMemberUpdateToWorld(const char *MemberName, uint32 GuildID, uint16 ZoneID, uint32 LastSeen); + void RequestOnlineGuildMembers(uint32 FromID, uint32 GuildID); + +protected: + virtual void SendGuildRefresh(uint32 guild_id, bool name, bool motd, bool rank, bool relation); + virtual void SendCharRefresh(uint32 old_guild_id, uint32 guild_id, uint32 charid); + virtual void SendRankUpdate(uint32 CharID); + virtual void SendGuildDelete(uint32 guild_id); + + std::map > m_inviteQueue; //map from char ID to guild,rank + +private: + LinkedList list; + uint32 id; + +}; + + +class GuildBankManager +{ + +public: + ~GuildBankManager(); + void SendGuildBank(Client *c); + bool AddItem(uint32 GuildID, uint8 Area, uint32 ItemID, int32 QtyOrCharges, const char *Donator, uint8 Permissions, const char *WhoFor); + int Promote(uint32 GuildID, int SlotID); + void SetPermissions(uint32 GuildID, uint16 SlotID, uint32 Permissions, const char *MemberName); + ItemInst* GetItem(uint32 GuildID, uint16 Area, uint16 SlotID, uint32 Quantity); + bool DeleteItem(uint32 GuildID, uint16 Area, uint16 SlotID, uint32 Quantity); + bool HasItem(uint32 GuildID, uint32 ItemID); + bool IsAreaFull(uint32 GuildID, uint16 Area); + bool MergeStacks(uint32 GuildID, uint16 SlotID); + bool SplitStack(uint32 GuildID, uint16 SlotID, uint32 Quantity); + bool AllowedToWithdraw(uint32 GuildID, uint16 Area, uint16 SlotID, const char *Name); + +private: + bool IsLoaded(uint32 GuildID); + bool Load(uint32 GuildID); + std::list::iterator GetGuildBank(uint32 GuildID); + void UpdateItemQuantity(uint32 GuildID, uint16 Area, uint16 SlotID, uint32 Quantity); + + std::list Banks; + +}; + +extern ZoneGuildManager guild_mgr; +extern GuildBankManager *GuildBanks; + + +#endif /*GUILD_MGR_H_*/ + diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp new file mode 100644 index 000000000..6ea0e4ae5 --- /dev/null +++ b/zone/hate_list.cpp @@ -0,0 +1,577 @@ +/* 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 "../common/debug.h" +#include +#include +#include +#include "masterentity.h" +#include "../common/linked_list.h" +#include "../common/rulesys.h" +#include "../common/MiscFunctions.h" +#include "hate_list.h" +#include "QuestParserCollection.h" +#include "zone.h" +#include "watermap.h" + +extern Zone *zone; + +HateList::HateList() +{ + owner = NULL; +} + +HateList::~HateList() +{ +} + +// neotokyo: added for frenzy support +// checks if target still is in frenzy mode +void HateList::CheckFrenzyHate() +{ + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->ent->GetHPRatio() >= 20) + iterator.GetData()->bFrenzy = false; + iterator.Advance(); + } +} + +void HateList::Wipe() +{ + LinkedListIterator iterator(list); + iterator.Reset(); + + while(iterator.MoreElements()) + { + Mob* m = iterator.GetData()->ent; + parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), m, "0", 0); + iterator.RemoveCurrent(); + + if(m->IsClient()) + m->CastToClient()->DecrementAggroCount(); + } +} + +bool HateList::IsOnHateList(Mob *mob) +{ + if (Find(mob)) + return true; + return false; +} + +tHateEntry *HateList::Find(Mob *ent) +{ + _ZP(HateList_Find); + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData()->ent == ent) + return iterator.GetData(); + iterator.Advance(); + } + return NULL; +} + +void HateList::Set(Mob* other, uint32 in_hate, uint32 in_dam) +{ + tHateEntry *p = Find(other); + if(p) + { + if(in_dam > 0) + p->damage = in_dam; + if(in_hate > 0) + p->hate = in_hate; + } +} + +Mob* HateList::GetDamageTop(Mob* hater) +{ + _ZP(HateList_GetDamageTop); + Mob* current = NULL; + Group* grp = NULL; + Raid* r = NULL; + uint32 dmg_amt = 0; + + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) + { + grp = NULL; + r = NULL; + + if(iterator.GetData()->ent && iterator.GetData()->ent->IsClient()){ + r = entity_list.GetRaidByClient(iterator.GetData()->ent->CastToClient()); + } + + grp = entity_list.GetGroupByMob(iterator.GetData()->ent); + + if(iterator.GetData()->ent && r){ + if(r->GetTotalRaidDamage(hater) >= dmg_amt) + { + current = iterator.GetData()->ent; + dmg_amt = r->GetTotalRaidDamage(hater); + } + } + else if (iterator.GetData()->ent != NULL && grp != NULL) + { + if (grp->GetTotalGroupDamage(hater) >= dmg_amt) + { + current = iterator.GetData()->ent; + dmg_amt = grp->GetTotalGroupDamage(hater); + } + } + else if (iterator.GetData()->ent != NULL && (uint32)iterator.GetData()->damage >= dmg_amt) + { + current = iterator.GetData()->ent; + dmg_amt = iterator.GetData()->damage; + } + iterator.Advance(); + } + return current; +} + +Mob* HateList::GetClosest(Mob *hater) { + _ZP(HateList_GetClosest); + Mob* close = NULL; + float closedist = 99999.9f; + float thisdist; + + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) { + thisdist = iterator.GetData()->ent->DistNoRootNoZ(*hater); + if(iterator.GetData()->ent != NULL && thisdist <= closedist) { + closedist = thisdist; + close = iterator.GetData()->ent; + } + iterator.Advance(); + } + + if (close == 0 && hater->IsNPC()) + close = hater->CastToNPC()->GetHateTop(); + + return close; +} + + +// neotokyo: 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) + return; + + if(ent->IsCorpse()) + return; + + if(ent->IsClient() && ent->CastToClient()->IsDead()) + return; + + tHateEntry *p = Find(ent); + if (p) + { + p->damage+=(in_dam>=0)?in_dam:0; + p->hate+=in_hate; + p->bFrenzy = bFrenzy; + } + else if (iAddIfNotExist) { + p = new tHateEntry; + p->ent = ent; + p->damage = (in_dam>=0)?in_dam:0; + p->hate = in_hate; + p->bFrenzy = bFrenzy; + list.Append(p); + parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), ent, "1", 0); + + if(ent->IsClient()) + ent->CastToClient()->IncrementAggroCount(); + } +} + +bool HateList::RemoveEnt(Mob *ent) +{ + bool found = false; + LinkedListIterator iterator(list); + iterator.Reset(); + + while(iterator.MoreElements()) + { + if(iterator.GetData()->ent == ent) + { + parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), ent, "0", 0); + iterator.RemoveCurrent(); + found = true; + + if(ent->IsClient()) + ent->CastToClient()->DecrementAggroCount(); + + } + else + iterator.Advance(); + } + return found; +} + +void HateList::DoFactionHits(int32 nfl_id) { + _ZP(HateList_DoFactionHits); + if (nfl_id <= 0) + return; + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) + { + Client *p; + + if (iterator.GetData()->ent && iterator.GetData()->ent->IsClient()) + p = iterator.GetData()->ent->CastToClient(); + else + p = NULL; + + if (p) + p->SetFactionLevel(p->CharacterID(), nfl_id, p->GetBaseClass(), p->GetBaseRace(), p->GetDeity()); + iterator.Advance(); + } +} + +Mob *HateList::GetTop(Mob *center) +{ + _ZP(HateList_GetTop); + Mob* top = NULL; + int32 hate = -1; + + if (RuleB(Aggro,SmartAggroList)){ + Mob* topClientInRange = NULL; + int32 hateClientInRange = -1; + int skipped_count = 0; + + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) + { + tHateEntry *cur = iterator.GetData(); + int16 aggroMod = 0; + + if(!cur){ + iterator.Advance(); + continue; + } + + if(!cur->ent){ + iterator.Advance(); + continue; + } + + if(center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) { + if(!zone->watermap->InLiquid(cur->ent->GetX(), cur->ent->GetY(), cur->ent->GetZ())) { + skipped_count++; + iterator.Advance(); + continue; + } + } + + if(cur->ent->DivineAura() || cur->ent->IsMezzed() || cur->ent->IsFeared()){ + if(hate == -1) + { + top = cur->ent; + hate = 0; + } + iterator.Advance(); + continue; + } + + int32 currentHate = cur->hate; + + if(cur->ent->IsClient()){ + + if(cur->ent->CastToClient()->IsSitting()){ + aggroMod += RuleI(Aggro, SittingAggroMod); + } + + if(center){ + if(center->GetTarget() == cur->ent) + aggroMod += RuleI(Aggro, CurrentTargetAggroMod); + if(RuleI(Aggro, MeleeRangeAggroMod) != 0) + { + if(center->CombatRange(cur->ent)){ + aggroMod += RuleI(Aggro, MeleeRangeAggroMod); + + if(currentHate > hateClientInRange || cur->bFrenzy){ + hateClientInRange = currentHate; + topClientInRange = cur->ent; + } + } + } + } + + } + else{ + if(center){ + if(center->GetTarget() == cur->ent) + aggroMod += RuleI(Aggro, CurrentTargetAggroMod); + if(RuleI(Aggro, MeleeRangeAggroMod) != 0) + { + if(center->CombatRange(cur->ent)){ + aggroMod += RuleI(Aggro, MeleeRangeAggroMod); + } + } + } + } + + if(cur->ent->GetMaxHP() != 0 && ((cur->ent->GetHP()*100/cur->ent->GetMaxHP()) < 20)){ + aggroMod += RuleI(Aggro, CriticallyWoundedAggroMod); + } + + if(aggroMod){ + currentHate += (currentHate * aggroMod / 100); + } + + if(currentHate > hate || cur->bFrenzy){ + hate = currentHate; + top = cur->ent; + } + + iterator.Advance(); + } + + if(topClientInRange != NULL && top != NULL) { + bool isTopClientType = top->IsClient(); +#ifdef BOTS + if(!isTopClientType) { + if(top->IsBot()) { + isTopClientType = true; + topClientInRange = top; + } + } +#endif //BOTS + if(!isTopClientType) + return topClientInRange; + + return top; + } + else { + if(top == NULL && skipped_count > 0) { + return center->GetTarget(); + } + return top; + } + } + else{ + LinkedListIterator iterator(list); + iterator.Reset(); + int skipped_count = 0; + while(iterator.MoreElements()) + { + tHateEntry *cur = iterator.GetData(); + if(center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) { + if(!zone->watermap->InLiquid(cur->ent->GetX(), cur->ent->GetY(), cur->ent->GetZ())) { + skipped_count++; + iterator.Advance(); + continue; + } + } + + if(cur->ent != NULL && ((cur->hate > hate) || cur->bFrenzy )) + { + top = cur->ent; + hate = cur->hate; + } + iterator.Advance(); + } + if(top == NULL && skipped_count > 0) { + return center->GetTarget(); + } + return top; + } +} + +Mob *HateList::GetMostHate(){ + _ZP(HateList_GetMostHate); + + Mob* top = NULL; + int32 hate = -1; + + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) + { + tHateEntry *cur = iterator.GetData(); + if(cur->ent != NULL && (cur->hate > hate)) + { + top = cur->ent; + hate = cur->hate; + } + iterator.Advance(); + } + return top; +} + + +Mob *HateList::GetRandom() +{ + int count = 0; + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) + { + iterator.Advance(); + count++; + } + if(!count) + return NULL; + + int random = MakeRandomInt(0, count-1); + iterator.Reset(); + for (int i = 0; i < random-1; i++) + iterator.Advance(); + return iterator.GetData()->ent; +} + +int32 HateList::GetEntHate(Mob *ent, bool damage) +{ + tHateEntry *p; + + p = Find(ent); + + if ( p && damage) + return p->damage; + else if (p) + return p->hate; + else + return 0; +} + +//looking for any mob with hate > -1 +bool HateList::IsEmpty() { + _ZP(HateList_IsEmpty); + + return(list.Count() == 0); +} + +// Prints hate list to a client +void HateList::PrintToClient(Client *c) +{ + LinkedListIterator iterator(list); + iterator.Reset(); + while (iterator.MoreElements()) + { + tHateEntry *e = iterator.GetData(); + c->Message(0, "- name: %s, damage: %d, hate: %d", + (e->ent && e->ent->GetName()) ? e->ent->GetName() : "(null)", + e->damage, e->hate); + + iterator.Advance(); + } +} + +int HateList::AreaRampage(Mob *caster, Mob *target) +{ + if(!target || !caster) + return 0; + + int ret = 0; + std::list id_list; + LinkedListIterator iterator(list); + iterator.Reset(); + while (iterator.MoreElements()) + { + tHateEntry *h = iterator.GetData(); + iterator.Advance(); + if(h && h->ent && h->ent != caster) + { + if(caster->CombatRange(h->ent)) + { + id_list.push_back(h->ent->GetID()); + ++ret; + } + } + } + + std::list::iterator iter = id_list.begin(); + while(iter != id_list.end()) + { + Mob *cur = entity_list.GetMobID((*iter)); + if(cur) + { + caster->Attack(cur); + } + iter++; + } + + return ret; +} + +void HateList::SpellCast(Mob *caster, uint32 spell_id, float range) +{ + if(!caster) + { + return; + } + + //this is slower than just iterating through the list but avoids + //crashes when people kick the bucket in the middle of this call + //that invalidates our iterator but there's no way to know sadly + //So keep a list of entity ids and look up after + std::list id_list; + range = range * range; + LinkedListIterator iterator(list); + iterator.Reset(); + while (iterator.MoreElements()) + { + tHateEntry *h = iterator.GetData(); + if(range > 0) + { + if(caster->DistNoRoot(*h->ent) <= range) + { + id_list.push_back(h->ent->GetID()); + } + } + else + { + id_list.push_back(h->ent->GetID()); + } + iterator.Advance(); + } + + std::list::iterator iter = id_list.begin(); + while(iter != id_list.end()) + { + Mob *cur = entity_list.GetMobID((*iter)); + if(cur) + { + caster->SpellOnTarget(spell_id, cur); + } + iter++; + } +} + + +void HateList::GetHateList(std::list &h_list) +{ + h_list.clear(); + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) + { + tHateEntry *ent = iterator.GetData(); + h_list.push_back(ent); + iterator.Advance(); + } +} diff --git a/zone/hate_list.h b/zone/hate_list.h new file mode 100644 index 000000000..3334632e4 --- /dev/null +++ b/zone/hate_list.h @@ -0,0 +1,83 @@ +/* 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 HATELIST_H +#define HATELIST_H + +class tHateEntry +{ +public: + Mob *ent; + int32 damage, hate; + bool bFrenzy; +}; + +class HateList +{ +public: + HateList(); + ~HateList(); + + // adds a mob to the hatelist + void Add(Mob *ent, int32 in_hate=0, int32 in_dam=0, bool bFrenzy = false, bool iAddIfNotExist = true); + // sets existing hate + void Set(Mob *other, uint32 in_hate, uint32 in_dam); + // removes mobs from hatelist + bool RemoveEnt(Mob *ent); + // Remove all + void Wipe(); + // ??? + void DoFactionHits(int32 nfl_id); + // Gets Hate amount for mob + int32 GetEntHate(Mob *ent, bool damage = false); + // gets top hated mob + Mob *GetTop(Mob *center); + // gets any on the list + Mob *GetRandom(); + // get closest mob or NULL if list empty + Mob *GetClosest(Mob *hater); + // gets top mob or NULL if hate list empty + Mob *GetDamageTop(Mob *hater); + // used to check if mob is on hatelist + bool IsOnHateList(Mob *); + // used to remove or add frenzy hate + void CheckFrenzyHate(); + //Gets the target with the most hate regardless of things like frenzy etc. + Mob* GetMostHate(); + + int AreaRampage(Mob *caster, Mob *target); + + void SpellCast(Mob *caster, uint32 spell_id, float range); + + bool IsEmpty(); + void PrintToClient(Client *c); + + //For accessing the hate list via perl; don't use for anything else + void GetHateList(std::list &h_list); + + //setting owner + void SetOwner(Mob *newOwner) { owner = newOwner; } + +protected: + tHateEntry *Find(Mob *ent); +private: + LinkedList list; + Mob *owner; +}; + +#endif diff --git a/zone/horse.cpp b/zone/horse.cpp new file mode 100644 index 000000000..79e2bb881 --- /dev/null +++ b/zone/horse.cpp @@ -0,0 +1,208 @@ +/* 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 "../common/debug.h" +#include "masterentity.h" +#include "../common/Item.h" +#include "../common/linked_list.h" +#include "../common/MiscFunctions.h" +#include +#include +#include "worldserver.h" + +map Horse::horse_types; +LinkedList horses_auto_delete; + +Horse::Horse(Client *_owner, uint16 spell_id, float x, float y, float z, float heading) + : NPC(GetHorseType(spell_id), NULL, x, y, z, heading, FlyMode3) +{ + //give the horse its proper name. + strn0cpy(name, _owner->GetCleanName(), 55); + strcat(name,"`s_Mount00"); + + owner = _owner; +} + +void Horse::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { + NPC::FillSpawnStruct(ns, ForWho); + +// ns->spawn.texture = NPCTypedata->mount_color; + ns->spawn.petOwnerId = 0; + + //dunno why we do these, they should allready be set right. + ns->spawn.runspeed = NPCTypedata->runspeed; +} + +bool Horse::IsHorseSpell(uint16 spell_id) { + //written in terms of a function which does a ton more work + //than we need to to figure out if this is a horse spell. + //the logic is that people calling this function will post + //likely immediately summon the horse, so we need the extra anyways. + return(GetHorseType(spell_id) != NULL); +} + +const NPCType *Horse::GetHorseType(uint16 spell_id) { + if(horse_types.count(spell_id) == 1) + return(horse_types[spell_id]); + //cache invalid spell IDs as NULL entries + const NPCType *ret; + horse_types[spell_id] = ret = BuildHorseType(spell_id); + return(ret); +} + +const NPCType *Horse::BuildHorseType(uint16 spell_id) { + + 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->npc_attacks,"ABH"); + 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 NULL; + } + else { + LogFile->write(EQEMuLog::Error, "Error in Mount query '%s': %s", query, errbuf); + safe_delete_array(query); + return NULL; + } + +} + + + +void Client::SummonHorse(uint16 spell_id) { + if (GetHorseId() != 0) { + Message(13,"You already have a Horse. Get off, Fatbutt!"); + return; + } + if(!Horse::IsHorseSpell(spell_id)) { + LogFile->write(EQEMuLog::Error, "%s tried to summon an unknown horse, spell id %d", GetName(), spell_id); + return; + } + + // No Horse, lets get them one. + + Horse* horse = new Horse(this, spell_id, GetX(), GetY(), GetZ(), GetHeading()); + + //we want to manage the spawn packet ourself. + //another reason is we dont want quests executing on it. + entity_list.AddNPC(horse, false); + + // Okay, lets say they have a horse now. + + + EQApplicationPacket outapp; + horse->CreateHorseSpawnPacket(&outapp, GetName(), GetID()); +/* // Doodman: Kludged in here instead of adding a field to PCType. FIXME! + NewSpawn_Struct* ns=(NewSpawn_Struct*)outapp->pBuffer; + ns->spawn.texture=mount_color; + ns->spawn.pet_owner_id=0; + ns->spawn.walkspeed=npc_type->walkspeed; + ns->spawn.runspeed=npc_type->runspeed; +*/ + entity_list.QueueClients(horse, &outapp); + + + uint16 tmpID = horse->GetID(); + SetHorseId(tmpID); + +} + +void Client::SetHorseId(uint16 horseid_in) { + //if its the same, do nothing + if(horseId == horseid_in) + return; + + //otherwise it changed. + //if we have a horse, get rid of it no matter what. + if(horseId) { + Mob *horse = entity_list.GetMob(horseId); + if(horse != NULL) + horse->Depop(); + } + + //now we take whatever they gave us. + horseId = horseid_in; +} + +void Mob::CreateHorseSpawnPacket(EQApplicationPacket* app, const char* ownername, uint16 ownerid, Mob* ForWho) { + app->SetOpcode(OP_NewSpawn); + app->pBuffer = new uchar[sizeof(NewSpawn_Struct)]; + app->size = sizeof(NewSpawn_Struct); + memset(app->pBuffer, 0, sizeof(NewSpawn_Struct)); + NewSpawn_Struct* ns = (NewSpawn_Struct*)app->pBuffer; + FillSpawnStruct(ns, ForWho); + +#if (EQDEBUG >= 11) + printf("Horse Spawn Packet - Owner: %s\n", ownername); + DumpPacket(app); +#endif +} + + diff --git a/zone/horse.h b/zone/horse.h new file mode 100644 index 000000000..e9bdb932f --- /dev/null +++ b/zone/horse.h @@ -0,0 +1,48 @@ +/* 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 HORSES_H +#define HORSES_H + + +#include "../common/debug.h" +#include "npc.h" + +#include +using namespace std; + +class Horse : public NPC { +public: + Horse(Client *owner, uint16 spell_id, float x, float y, float z, float heading); + + virtual void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); + + static bool IsHorseSpell(uint16 spell_id); +protected: + uint8 mount_color; + Client *owner; + + //generate npc type records for horses + static map horse_types; + static const NPCType *GetHorseType(uint16 spell_id); + static const NPCType *BuildHorseType(uint16 spell_id); +}; + + + + +#endif diff --git a/zone/inventory.cpp b/zone/inventory.cpp new file mode 100644 index 000000000..04947dd8a --- /dev/null +++ b/zone/inventory.cpp @@ -0,0 +1,2398 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 "masterentity.h" +#include "worldserver.h" +#include "net.h" +#include "zonedb.h" +#include "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 "StringIDs.h" +#include "NpcAI.h" +extern WorldServer worldserver; + +// @merth: this needs to be touched up +uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) { + if (itemnum == 0) + return 0; + uint32 x = 0; + ItemInst *cur = NULL; + + int i; + if(where_to_check & invWhereWorn) { + for (i=0; i<=21; i++) { // Equipped + if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) { + cur = m_inv.GetItem(i); + if(cur && cur->GetItem()->Stackable) { + x += cur->GetCharges(); + } else { + x++; + } + + DeleteItemInInventory(i, 0, true); + } + } + + // Power Source Slot + if (GetItemIDAt(9999) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(9999) != INVALID_ID)) { + cur = m_inv.GetItem(9999); + if(cur && cur->GetItem()->Stackable) { + x += cur->GetCharges(); + } else { + x++; + } + + if (GetClientVersion() >= EQClientSoF) + DeleteItemInInventory(9999, 0, true); + else + DeleteItemInInventory(9999, 0, false); // Prevents Titanium crash + } + } + + if(where_to_check & invWhereCursor) { + if (GetItemIDAt(30) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(30) != INVALID_ID)) { + cur = m_inv.GetItem(30); + if(cur && cur->GetItem()->Stackable) { + x += cur->GetCharges(); + } else { + x++; + } + + DeleteItemInInventory(30, 0, true); + } + + for (i=331; i<=340; i++) { // cursor's containers + if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) { + cur = m_inv.GetItem(i); + if(cur && cur->GetItem()->Stackable) { + x += cur->GetCharges(); + } else { + x++; + } + + DeleteItemInInventory(i, 0, true); + } + } + } + + if(where_to_check & invWherePersonal) { + for (i=22; i<=29; i++) { // Equipped + if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) { + cur = m_inv.GetItem(i); + if(cur && cur->GetItem()->Stackable) { + x += cur->GetCharges(); + } else { + x++; + } + + DeleteItemInInventory(i, 0, true); + } + } + + for (i=251; i<=330; i++) { // Main inventory's containers + if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) { + cur = m_inv.GetItem(i); + if(cur && cur->GetItem()->Stackable) { + x += cur->GetCharges(); + } else { + x++; + } + + DeleteItemInInventory(i, 0, true); + } + } + } + + if(where_to_check & invWhereBank) { + for (i=2000; i<=2023; i++) { // Bank slots + if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) { + cur = m_inv.GetItem(i); + if(cur && cur->GetItem()->Stackable) { + x += cur->GetCharges(); + } else { + x++; + } + + DeleteItemInInventory(i, 0, true); + } + } + + for (i=2031; i<=2270; i++) { // Bank's containers + if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) { + cur = m_inv.GetItem(i); + if(cur && cur->GetItem()->Stackable) { + x += cur->GetCharges(); + } else { + x++; + } + + DeleteItemInInventory(i, 0, true); + } + } + } + + if(where_to_check & invWhereSharedBank) { + for (i=2500; i<=2501; i++) { // Shared bank + if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) { + cur = m_inv.GetItem(i); + if(cur && cur->GetItem()->Stackable) { + x += cur->GetCharges(); + } else { + x++; + } + + DeleteItemInInventory(i, 0, true); + } + } + + for (i=2531; i<=2550; i++) { // Shared bank's containers + if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) { + cur = m_inv.GetItem(i); + if(cur && cur->GetItem()->Stackable) { + x += cur->GetCharges(); + } else { + x++; + } + + DeleteItemInInventory(i, 0, true); + } + } + } + + return x; +} + + +bool Client::CheckLoreConflict(const Item_Struct* item) { + if (!item) + return false; + if (!(item->LoreFlag)) + return false; + + if (item->LoreGroup == -1) // Standard lore items; look everywhere except the shared bank, return the result + return (m_inv.HasItem(item->ID, 0, ~invWhereSharedBank) != SLOT_INVALID); + + //If the item has a lore group, we check for other items with the same group and return the result + return (m_inv.HasItemByLoreGroup(item->LoreGroup, ~invWhereSharedBank) != SLOT_INVALID); +} + +void Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) { + const Item_Struct* item = database.GetItem(item_id); + + if (item == NULL) { + Message(0, "No such item: %i", item_id); + return; + } else { + // if 0 and max charges 0 and stackable, set the created item charge to 1 + if (charges == 0 && item->MaxCharges == 0 && item->Stackable) + charges = 1; + // if 0 or no charge value was passed, set the created item charge to max charges + else if(charges == 0) + charges = item->MaxCharges; + } + // Checking to see if the Item is lore or not. + bool foundlore = CheckLoreConflict(item); + + //TODO: check for lore conflict on augments + + // Checking to see if it is a GM only Item or not. + //bool foundgm = (item->gm && (this->Admin() < 100)); + bool foundgm = false; + + if (!foundlore && !foundgm) { // Okay, It isn't LORE, or if it is, it is not in player's inventory. + ItemInst* inst = database.CreateItem(item, charges); + if (inst) { + // Corrected the augment references to reflect augment name/id instead of base item name/id + if (aug1) { + const Item_Struct* augitem1 = database.GetItem(aug1); + if (augitem1) { + if (!CheckLoreConflict(augitem1)) { + inst->PutAugment(&database, 0, aug1); + } + else { + Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem1->Name, aug1); + } + } + } + if (aug2) { + const Item_Struct* augitem2 = database.GetItem(aug2); + if (augitem2) { + if (!CheckLoreConflict(augitem2)) { + inst->PutAugment(&database, 1, aug2); + } + else { + Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem2->Name, aug2); + } + } + } + if (aug3) { + const Item_Struct* augitem3 = database.GetItem(aug3); + if (augitem3) { + if (!CheckLoreConflict(augitem3)) { + inst->PutAugment(&database, 2, aug3); + } + else { + Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem3->Name, aug3); + } + } + } + if (aug4) { + const Item_Struct* augitem4 = database.GetItem(aug4); + if (augitem4) { + if (!CheckLoreConflict(augitem4)) { + inst->PutAugment(&database, 3, aug4); + } + else { + Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem4->Name, aug4); + } + } + } + if (aug5) { + const Item_Struct* augitem5 = database.GetItem(aug5); + if (augitem5) { + if (!CheckLoreConflict(augitem5)) { + inst->PutAugment(&database, 4, aug5); + } + else { + Message(0, "You already have a %s (%u) in your inventory - Augment not added!", augitem5->Name, aug5); + } + } + } + if (attuned) { + if (inst->GetItem()->Attuneable) { + inst->SetInstNoDrop(true); + } + } + if (to_slot == SLOT_CURSOR) + { + //inst->SetCharges( + PushItemOnCursor(*inst); + // Send item packet to user + SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem); + } + else + { + PutItemInInventory(to_slot, *inst, true); + } + safe_delete(inst); + + if ((RuleB(Character, EnableDiscoveredItems))) + { + if(!GetGM() && !IsDiscovered(item_id)) + DiscoverItem(item_id); + } + } + } + else { // Item was already in inventory & is a LORE item or was a GM only item. Give them a message about it. + if (foundlore){ + DuplicateLoreMessage(item_id); + //Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id); + } + else if (foundgm) + Message(0, "You are not a GM to summon this item"); + } +} + +// Drop item from inventory to ground (generally only dropped from SLOT_CURSOR) +void Client::DropItem(int16 slot_id) +{ + + if (GetInv().CheckNoDrop(slot_id) && RuleI(World, FVNoDropFlag) == 0 || RuleI(Character, MinStatusForNoDropExemptions) < Admin() && RuleI(World, FVNoDropFlag) == 2) { + //Message(0, "No Drop Exploit: Items Destroyed."); + database.SetHackerFlag(this->AccountName(), this->GetCleanName(), "Tried to drop an item on the ground that was nodrop!"); + GetInv().DeleteItem(slot_id); + return; + } + + // Take control of item in client inventory + ItemInst* inst = m_inv.PopItem(slot_id); + + if (!inst) { + // Item doesn't exist in inventory! + Message(13, "Error: Item not found in slot %i", slot_id); + return; + } + + // Save client inventory change to database + if (slot_id==SLOT_CURSOR) { + list::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end(); + database.SaveCursor(CharacterID(), s, e); + } else + database.SaveInventory(CharacterID(), NULL, slot_id); + + // Package as zone object + Object* object = new Object(this, inst); + entity_list.AddObject(object, true); + object->StartDecay(); + + safe_delete(inst); +} + +// Drop inst +void Client::DropInst(const ItemInst* inst) +{ + if (!inst) { + // Item doesn't exist in inventory! + Message(13, "Error: Item not found"); + return; + } + + + if (inst->GetItem()->NoDrop == 0) + { + Message(13, "This item is NODROP. Deleting."); + return; + } + + // Package as zone object + Object* object = new Object(this, inst); + entity_list.AddObject(object, true); + object->StartDecay(); +} + +// Returns a slot's item ID (returns INVALID_ID if not found) +uint32 Client::GetItemIDAt(int16 slot_id) { + const ItemInst* inst = m_inv[slot_id]; + if (inst) + return inst->GetItem()->ID; + + // None found + return INVALID_ID; +} + +// Returns an augment's ID that's in an item (returns INVALID_ID if not found) +// Pass in the slot ID of the item and which augslot you want to check (0-4) +uint32 Client::GetAugmentIDAt(int16 slot_id, uint8 augslot) { + const ItemInst* inst = m_inv[slot_id]; + if (inst) + if (inst->GetAugmentItemID(augslot)) + return inst->GetAugmentItemID(augslot); + + // None found + return INVALID_ID; +} + +// Remove item from inventory +void Client::DeleteItemInInventory(int16 slot_id, int8 quantity, bool client_update, bool update_db) { + #if (EQDEBUG >= 5) + LogFile->write(EQEMuLog::Debug, "DeleteItemInInventory(%i, %i, %s)", slot_id, quantity, (client_update) ? "true":"false"); + #endif + + // Added 'IsSlotValid(slot_id)' check to both segments of client packet processing. + // - cursor queue slots were slipping through and crashing client + if(!m_inv[slot_id]) { + // Make sure the client deletes anything in this slot to match the server. + if(client_update && IsValidSlot(slot_id)) { + EQApplicationPacket* outapp; + outapp = new EQApplicationPacket(OP_DeleteItem, sizeof(DeleteItem_Struct)); + DeleteItem_Struct* delitem = (DeleteItem_Struct*)outapp->pBuffer; + delitem->from_slot = slot_id; + delitem->to_slot = 0xFFFFFFFF; + delitem->number_in_stack = 0xFFFFFFFF; + QueuePacket(outapp); + safe_delete(outapp); + } + return; + } + + // start QS code + if(RuleB(QueryServ, PlayerLogDeletes)) { + uint16 delete_count = 0; + + if(m_inv[slot_id]) { delete_count += m_inv.GetItem(slot_id)->GetTotalItemCount(); } + + ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogDeletes, sizeof(QSPlayerLogDelete_Struct) + (sizeof(QSDeleteItems_Struct) * delete_count)); + QSPlayerLogDelete_Struct* qsaudit = (QSPlayerLogDelete_Struct*)qspack->pBuffer; + uint16 parent_offset = 0; + + qsaudit->char_id = character_id; + qsaudit->stack_size = quantity; + qsaudit->char_count = delete_count; + + qsaudit->items[parent_offset].char_slot = slot_id; + qsaudit->items[parent_offset].item_id = m_inv[slot_id]->GetID(); + qsaudit->items[parent_offset].charges = m_inv[slot_id]->GetCharges(); + qsaudit->items[parent_offset].aug_1 = m_inv[slot_id]->GetAugmentItemID(1); + qsaudit->items[parent_offset].aug_2 = m_inv[slot_id]->GetAugmentItemID(2); + qsaudit->items[parent_offset].aug_3 = m_inv[slot_id]->GetAugmentItemID(3); + qsaudit->items[parent_offset].aug_4 = m_inv[slot_id]->GetAugmentItemID(4); + qsaudit->items[parent_offset].aug_5 = m_inv[slot_id]->GetAugmentItemID(5); + + if(m_inv[slot_id]->IsType(ItemClassContainer)) { + for(uint8 bag_idx = 0; bag_idx < m_inv[slot_id]->GetItem()->BagSlots; bag_idx++) { + ItemInst* bagitem = m_inv[slot_id]->GetItem(bag_idx); + + if(bagitem) { + int16 bagslot_id = Inventory::CalcSlotId(slot_id, bag_idx); + + qsaudit->items[++parent_offset].char_slot = bagslot_id; + qsaudit->items[parent_offset].item_id = bagitem->GetID(); + qsaudit->items[parent_offset].charges = bagitem->GetCharges(); + qsaudit->items[parent_offset].aug_1 = bagitem->GetAugmentItemID(1); + qsaudit->items[parent_offset].aug_2 = bagitem->GetAugmentItemID(2); + qsaudit->items[parent_offset].aug_3 = bagitem->GetAugmentItemID(3); + qsaudit->items[parent_offset].aug_4 = bagitem->GetAugmentItemID(4); + qsaudit->items[parent_offset].aug_5 = bagitem->GetAugmentItemID(5); + } + } + } + + qspack->Deflate(); + if(worldserver.Connected()) { worldserver.SendPacket(qspack); } + safe_delete(qspack); + } + // end QS code + + bool isDeleted = m_inv.DeleteItem(slot_id, quantity); + + const ItemInst* inst=NULL; + if (slot_id==SLOT_CURSOR) { + list::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end(); + if(update_db) + database.SaveCursor(character_id, s, e); + } + else { + // Save change to database + inst = m_inv[slot_id]; + if(update_db) + database.SaveInventory(character_id, inst, slot_id); + } + + if(client_update && IsValidSlot(slot_id)) { + EQApplicationPacket* outapp; + if(inst) { + if(!inst->IsStackable() && !isDeleted) + // Non stackable item with charges = Item with clicky spell effect ? Delete a charge. + outapp = new EQApplicationPacket(OP_DeleteCharge, sizeof(MoveItem_Struct)); + else + // Stackable, arrows, etc ? Delete one from the stack + outapp = new EQApplicationPacket(OP_DeleteItem, sizeof(MoveItem_Struct)); + + DeleteItem_Struct* delitem = (DeleteItem_Struct*)outapp->pBuffer; + delitem->from_slot = slot_id; + delitem->to_slot = 0xFFFFFFFF; + delitem->number_in_stack = 0xFFFFFFFF; + for(int loop=0;looppBuffer; + delitem->from_slot = slot_id; + delitem->to_slot = 0xFFFFFFFF; + delitem->number_in_stack = 0xFFFFFFFF; + QueuePacket(outapp); + safe_delete(outapp); + } + } +} + +// Puts an item into the person's inventory +// Any items already there will be removed from user's inventory +// (Also saves changes back to the database: this may be optimized in the future) +// client_update: Sends packet to client +bool Client::PushItemOnCursor(const ItemInst& inst, bool client_update) +{ + mlog(INVENTORY__SLOTS, "Putting item %s (%d) on the cursor", inst.GetItem()->Name, inst.GetItem()->ID); + m_inv.PushCursor(inst); + + if (client_update) { + SendItemPacket(SLOT_CURSOR, &inst, ItemPacketSummonItem); + } + + list::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end(); + return database.SaveCursor(CharacterID(), s, e); +} + +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==SLOT_CURSOR) + { + return PushItemOnCursor(inst,client_update); + } + else + m_inv.PutItem(slot_id, inst); + + if (client_update) { + SendItemPacket(slot_id, &inst, (slot_id==SLOT_CURSOR)?ItemPacketSummonItem:ItemPacketTrade); + } + + if (slot_id==SLOT_CURSOR) { + list::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end(); + return database.SaveCursor(this->CharacterID(), s, e); + } else + return database.SaveInventory(this->CharacterID(), &inst, slot_id); + + CalcBonuses(); +} + +void Client::PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootItem_Struct** bag_item_data) +{ + mlog(INVENTORY__SLOTS, "Putting loot item %s (%d) into slot %d", inst.GetItem()->Name, inst.GetItem()->ID, slot_id); + m_inv.PutItem(slot_id, inst); + + SendLootItemInPacket(&inst, slot_id); + + if (slot_id==SLOT_CURSOR) { + list::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end(); + database.SaveCursor(this->CharacterID(), s, e); + } else + database.SaveInventory(this->CharacterID(), &inst, slot_id); + + if(bag_item_data) // bag contents + { + int16 interior_slot; + // solar: our bag went into slot_id, now let's pack the contents in + for(int i = 0; i < 10; i++) + { + if(bag_item_data[i] == NULL) + 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); + 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); + safe_delete(bagitem); + } + } + + CalcBonuses(); +} +bool Client::TryStacking(ItemInst* item, uint8 type, bool try_worn, bool try_cursor){ + if(!item || !item->IsStackable() || item->GetCharges()>=item->GetItem()->StackSize) + return false; + int16 i; + uint32 item_id = item->GetItem()->ID; + for (i = 22; i <= 29; i++) + { + ItemInst* tmp_inst = m_inv.GetItem(i); + if(tmp_inst && tmp_inst->GetItem()->ID == item_id && tmp_inst->GetCharges() < tmp_inst->GetItem()->StackSize){ + MoveItemCharges(*item, i, type); + CalcBonuses(); + if(item->GetCharges()) // we didn't get them all + return AutoPutLootInInventory(*item, try_worn, try_cursor, 0); + return true; + } + } + for (i = 22; i <= 29; i++) + { + for (uint8 j = 0; j < 10; j++) + { + uint16 slotid = Inventory::CalcSlotId(i, j); + ItemInst* tmp_inst = m_inv.GetItem(slotid); + + if(tmp_inst && tmp_inst->GetItem()->ID == item_id && tmp_inst->GetCharges() < tmp_inst->GetItem()->StackSize){ + MoveItemCharges(*item, slotid, type); + CalcBonuses(); + if(item->GetCharges()) // we didn't get them all + return AutoPutLootInInventory(*item, try_worn, try_cursor, 0); + return true; + } + } + } + return false; +} +// Locate an available space in inventory to place an item +// and then put the item there +// The change will be saved to the database +bool Client::AutoPutLootInInventory(ItemInst& inst, bool try_worn, bool try_cursor, ServerLootItem_Struct** bag_item_data) +{ + // #1: Try to auto equip + if (try_worn && inst.IsEquipable(GetBaseRace(), GetClass()) && inst.GetItem()->ReqLevel<=level && !inst.GetItem()->Attuneable && inst.GetItem()->ItemType != ItemTypeAugment) + { + for (int16 i = 0; i < 9999; i++) // originally (i < 22) + { + if (i == 22) { + if(this->GetClientVersion() >= EQClientSoF) { i = 9999; } // added power source check for SoF+ clients + else { break; } + } + + if (!m_inv[i]) + { + if( i == SLOT_PRIMARY && inst.IsWeapon() ) // If item is primary slot weapon + { + if( (inst.GetItem()->ItemType == ItemType2HS) || (inst.GetItem()->ItemType == ItemType2HB) || (inst.GetItem()->ItemType == ItemType2HPierce) ) // and uses 2hs \ 2hb \ 2hp + { + if( m_inv[SLOT_SECONDARY] ) // and if secondary slot is not empty + { + continue; // Can't auto-equip + } + } + } + if( i== SLOT_SECONDARY && m_inv[SLOT_PRIMARY]) // check to see if primary slot is a two hander + { + uint8 use = m_inv[SLOT_PRIMARY]->GetItem()->ItemType; + if(use == ItemType2HS || use == ItemType2HB || use == ItemType2HPierce) + continue; + } + if + ( + i == SLOT_SECONDARY && + inst.IsWeapon() && + !CanThisClassDualWield() + ) + { + continue; + } + + if (inst.IsEquipable(i)) // Equippable at this slot? + { + //send worn to everyone... + PutLootInInventory(i, inst); + uint8 worn_slot_material = Inventory::CalcMaterialFromSlot(i); + if(worn_slot_material != 0xFF) + { + SendWearChange(worn_slot_material); + } + return true; + } + } + } + } + + // #2: Stackable item? + if (inst.IsStackable()) + { + if(TryStacking(&inst, ItemPacketTrade, try_worn, try_cursor)) + return true; + } + + // #3: put it in inventory + bool is_arrow = (inst.GetItem()->ItemType == ItemTypeArrow) ? true : false; + int16 slot_id = m_inv.FindFreeSlot(inst.IsType(ItemClassContainer), try_cursor, inst.GetItem()->Size, is_arrow); + if (slot_id != SLOT_INVALID) + { + PutLootInInventory(slot_id, inst, bag_item_data); + return true; + } + + return false; +} + +// solar: helper function for AutoPutLootInInventory +void Client::MoveItemCharges(ItemInst &from, int16 to_slot, uint8 type) +{ + ItemInst *tmp_inst = m_inv.GetItem(to_slot); + + if(tmp_inst && tmp_inst->GetCharges() < tmp_inst->GetItem()->StackSize) + { + // this is how much room is left on the item we're stacking onto + int charge_slots_left = tmp_inst->GetItem()->StackSize - tmp_inst->GetCharges(); + // this is how many charges we can move from the looted item to + // the item in the inventory + int charges_to_move = + from.GetCharges() < charge_slots_left ? + from.GetCharges() : + charge_slots_left; + + tmp_inst->SetCharges(tmp_inst->GetCharges() + charges_to_move); + from.SetCharges(from.GetCharges() - charges_to_move); + SendLootItemInPacket(tmp_inst, to_slot); + if (to_slot==SLOT_CURSOR){ + list::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end(); + database.SaveCursor(this->CharacterID(), s, e); + } else + database.SaveInventory(this->CharacterID(), tmp_inst, to_slot); + } +} + +bool Client::MakeItemLink(char* &ret_link, const ItemInst *inst) { + //we're sending back the entire "link", minus the null characters & item name + //that way, we can use it for regular links & Task links + //note: initiator needs to pass us ret_link + +/* + --- Usage --- + Chat: "%c" "%s" "%s" "%c", 0x12, ret_link, inst->GetItem()->name, 0x12 + Task: "" "%s" "", ret_link, inst->GetItem()->name + Master's Book of Wood Elven Culture + http://eqitems.13th-floor.org/phpBB2/viewtopic.php?p=510#510 +*/ + + if (!inst) //have to have an item to make the link + return false; + + const Item_Struct* item = inst->GetItem(); + //format: + //0 itemid aug1 aug2 aug3 aug4 aug5 evolving? loregroup evolved level hash + //0 00000 00000 00000 00000 00000 00000 0 0000 0 00000000 + //length: + //1 5 5 5 5 5 5 1 4 1 8 = 45 + //evolving item info: http://eqitems.13th-floor.org/phpBB2/viewtopic.php?t=145#558 + uint8 evolving = 0; + uint16 loregroup = 0; + uint8 evolvedlevel = 0; + int hash = 0; + //int hash = GetItemLinkHash(inst); //eventually this will work (currently crashes zone), but for now we'll skip the extra overhead + if (GetClientVersion() >= EQClientRoF) + { + MakeAnyLenString(&ret_link, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X", + 0, + item->ID, + inst->GetAugmentItemID(0), + inst->GetAugmentItemID(1), + inst->GetAugmentItemID(2), + inst->GetAugmentItemID(3), + inst->GetAugmentItemID(4), + inst->GetAugmentItemID(5), + evolving, + loregroup, + evolvedlevel, + 0, + hash + ); + } + else if (GetClientVersion() >= EQClientSoF) + { + MakeAnyLenString(&ret_link, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X", + 0, + item->ID, + inst->GetAugmentItemID(0), + inst->GetAugmentItemID(1), + inst->GetAugmentItemID(2), + inst->GetAugmentItemID(3), + inst->GetAugmentItemID(4), + evolving, + loregroup, + evolvedlevel, + 0, + hash + ); + } + else + { + MakeAnyLenString(&ret_link, "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%08X", + 0, + item->ID, + inst->GetAugmentItemID(0), + inst->GetAugmentItemID(1), + inst->GetAugmentItemID(2), + inst->GetAugmentItemID(3), + inst->GetAugmentItemID(4), + evolving, + loregroup, + evolvedlevel, + hash + ); + } + + return true; +} + +int Client::GetItemLinkHash(const ItemInst* inst) { + //pre-Titanium: http://eqitems.13th-floor.org/phpBB2/viewtopic.php?t=70&postdays=0&postorder=asc + //Titanium: http://eqitems.13th-floor.org/phpBB2/viewtopic.php?t=145 + if (!inst) //have to have an item to make the hash + return 0; + + const Item_Struct* item = inst->GetItem(); + char* hash_str = 0; + /*register */int hash = 0; + + //now the fun part, since different types of items use different hashes... + if (item->ItemClass == 0 && item->CharmFileID) { //charm + MakeAnyLenString(&hash_str, "%d%s-1-1-1-1-1%d %d %d %d %d %d %d %d %d", + item->ID, + item->Name, + item->Light, + item->Icon, + item->Price, + item->Size, + item->Weight, + item->ItemClass, + item->ItemType, + item->Favor, + item->GuildFavor); + } else if (item->ItemClass == 2) { //book + MakeAnyLenString(&hash_str, "%d%s%d%d%09X", + item->ID, + item->Name, + item->Weight, + item->BookType, + item->Price); + } else if (item->ItemClass == 1) { //bag + MakeAnyLenString(&hash_str, "%d%s%x%d%09X%d", + item->ID, + item->Name, + item->BagSlots, + item->BagWR, + item->Price, + item->Weight); + } else { //everything else + MakeAnyLenString(&hash_str, "%d%s-1-1-1-1-1%d %d %d %d %d %d %d %d %d %d %d %d %d", + item->ID, + item->Name, + item->Mana, + item->HP, + item->Favor, + item->Light, + item->Icon, + item->Price, + item->Weight, + item->ReqLevel, + item->Size, + item->ItemClass, + item->ItemType, + item->AC, + item->GuildFavor); + } + + //this currently crashes zone, so someone feel free to fix this so we can work with hashes: + //*** glibc detected *** double free or corruption (out): 0xb2403470 *** + + /* + while (*hash_str != '\0') { + register int c = toupper(*hash_str); + + asm volatile("\ + imul $31, %1, %1;\ + movzx %%ax, %%edx;\ + addl %%edx, %1;\ + movl %1, %0;\ + " + :"=r"(hash) + :"D"(hash), "a"(c) + :"%edx" + ); + + // This is what the inline asm is doing: + // hash *= 0x1f; + // hash += (int)c; + + hash_str++; + } + */ + + safe_delete_array(hash_str); + return hash; +} + +void Client::SendItemLink(const ItemInst* inst, bool send_to_all) +{ +/* + +this stuff is old, live dosent do this anymore. they send a much smaller +packet with the item number in it, but I cant seem to find it right now + +*/ + if (!inst) + return; + + const Item_Struct* item = inst->GetItem(); + const char* name2 = &item->Name[0]; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ItemLinkText,strlen(name2)+68); + char buffer2[135] = {0}; + char itemlink[135] = {0}; + sprintf(itemlink,"%c0%06u0%05u-%05u-%05u-%05u-%05u00000000%c", + 0x12, + item->ID, + inst->GetAugmentItemID(0), + inst->GetAugmentItemID(1), + inst->GetAugmentItemID(2), + inst->GetAugmentItemID(3), + inst->GetAugmentItemID(4), + 0x12); + sprintf(buffer2,"%c%c%c%c%c%c%c%c%c%c%c%c%s",0x00,0x00,0x00,0x00,0xD3,0x01,0x00,0x00,0x1E,0x01,0x00,0x00,itemlink); + memcpy(outapp->pBuffer,buffer2,outapp->size); + QueuePacket(outapp); + safe_delete(outapp); + if (send_to_all==false) + return; + const char* charname = this->GetName(); + outapp = new EQApplicationPacket(OP_ItemLinkText,strlen(itemlink)+14+strlen(charname)); + char buffer3[150] = {0}; + sprintf(buffer3,"%c%c%c%c%c%c%c%c%c%c%c%c%6s%c%s",0x00,0x00,0x00,0x00,0xD2,0x01,0x00,0x00,0x00,0x00,0x00,0x00,charname,0x00,itemlink); + memcpy(outapp->pBuffer,buffer3,outapp->size); + entity_list.QueueCloseClients(this->CastToMob(),outapp,true,200,0,false); + safe_delete(outapp); +} + +void Client::SendLootItemInPacket(const ItemInst* inst, int16 slot_id) +{ + SendItemPacket(slot_id,inst, ItemPacketTrade); +} + +bool Client::IsValidSlot(uint32 slot) +{ + if((slot == (uint32)SLOT_INVALID) || // Destroying/Dropping item + (slot >= 0 && slot <= 30) || // Worn inventory, normal inventory, and cursor + (slot >= 251 && slot <= 340) || // Normal inventory bags and cursor bag + (slot >= 400 && slot <= 404) || // Tribute + (slot >= 2000 && slot <= 2023) || // Bank + (slot >= 2031 && slot <= 2270) || // Bank bags + (slot >= 2500 && slot <= 2501) || // Shared bank + (slot >= 2531 && slot <= 2550) || // Shared bank bags + (slot >= 3000 && slot <= 3007) || // Trade window + (slot >= 4000 && slot <= 4009) || // Tradeskill container + (slot == 9999)) // Power Source + { + return true; + } + else { + return false; + } +} + +bool Client::IsBankSlot(uint32 slot) +{ + if((slot >= 2000 && slot <= 2023) || // Bank + (slot >= 2031 && slot <= 2270) || // Bank bags + (slot >= 2500 && slot <= 2501) || // Shared bank + (slot >= 2531 && slot <= 2550)) // Shared bank bags + { + return true; + } + + return false; +} + +// Moves items around both internally and in the database +// In the future, this can be optimized by pushing all changes through one database REPLACE call +bool Client::SwapItem(MoveItem_Struct* move_in) { + + uint32 src_slot_check = move_in->from_slot; + uint32 dst_slot_check = move_in->to_slot; + uint32 stack_count_check = move_in->number_in_stack; + + if(!IsValidSlot(src_slot_check)){ + // SoF+ sends a Unix timestamp (should be int32) for src and dst slots every 10 minutes for some reason. + if(src_slot_check < 2147483647) + Message(13, "Warning: Invalid slot move from slot %u to slot %u with %u charges!", src_slot_check, dst_slot_check, stack_count_check); + mlog(INVENTORY__SLOTS, "Invalid slot move from slot %u to slot %u with %u charges!", src_slot_check, dst_slot_check, stack_count_check); + return false; + } + + if(!IsValidSlot(dst_slot_check)) { + // SoF+ sends a Unix timestamp (should be int32) for src and dst slots every 10 minutes for some reason. + if(src_slot_check < 2147483647) + Message(13, "Warning: Invalid slot move from slot %u to slot %u with %u charges!", src_slot_check, dst_slot_check, stack_count_check); + mlog(INVENTORY__SLOTS, "Invalid slot move from slot %u to slot %u with %u charges!", src_slot_check, dst_slot_check, stack_count_check); + return false; + } + + // This could be expounded upon at some point to let the server know that + // the client has moved a buffered cursor item onto the active cursor -U + if (move_in->from_slot == move_in->to_slot) { // Item summon, no further proccessing needed + if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit + return true; + } + + if (move_in->to_slot == (uint32)SLOT_INVALID) { + if(move_in->from_slot == (uint32)SLOT_CURSOR) { + mlog(INVENTORY__SLOTS, "Client destroyed item from cursor slot %d", move_in->from_slot); + if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit + DeleteItemInInventory(move_in->from_slot); + return true; // Item destroyed by client + } + else { + mlog(INVENTORY__SLOTS, "Deleted item from slot %d as a result of an inventory container tradeskill combine.", move_in->from_slot); + if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit + DeleteItemInInventory(move_in->from_slot); + return true; // Item deletetion + } + } + if(auto_attack && (move_in->from_slot == SLOT_PRIMARY || move_in->from_slot == SLOT_SECONDARY || move_in->from_slot == SLOT_RANGE)) + SetAttackTimer(); + else if(auto_attack && (move_in->to_slot == SLOT_PRIMARY || move_in->to_slot == SLOT_SECONDARY || move_in->to_slot == SLOT_RANGE)) + SetAttackTimer(); + // Step 1: Variables + int16 src_slot_id = (int16)move_in->from_slot; + int16 dst_slot_id = (int16)move_in->to_slot; + + if(IsBankSlot(src_slot_id) || + IsBankSlot(dst_slot_id) || + IsBankSlot(src_slot_check) || + IsBankSlot(dst_slot_check)) + { + uint32 distance = 0; + NPC *banker = entity_list.GetClosestBanker(this, distance); + + if(!banker || distance > USE_NPC_RANGE2) + { + char *hacked_string = NULL; + MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(items) 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); + Kick(); // Kicking player to avoid item loss do to client and server inventories not being sync'd + return false; + } + } + + //Setup + uint32 srcitemid = 0; + uint32 dstitemid = 0; + ItemInst* src_inst = m_inv.GetItem(src_slot_id); + ItemInst* dst_inst = m_inv.GetItem(dst_slot_id); + if (src_inst){ + mlog(INVENTORY__SLOTS, "Src slot %d has item %s (%d) with %d charges in it.", src_slot_id, src_inst->GetItem()->Name, src_inst->GetItem()->ID, src_inst->GetCharges()); + srcitemid = src_inst->GetItem()->ID; + //SetTint(dst_slot_id,src_inst->GetColor()); + if (src_inst->GetCharges() > 0 && (src_inst->GetCharges() < (int16)move_in->number_in_stack || move_in->number_in_stack > src_inst->GetItem()->StackSize)) + { + Message(13,"Error: Insufficent number in stack."); + return false; + } + } + if (dst_inst) { + mlog(INVENTORY__SLOTS, "Dest slot %d has item %s (%d) with %d charges in it.", dst_slot_id, dst_inst->GetItem()->Name, dst_inst->GetItem()->ID, dst_inst->GetCharges()); + dstitemid = dst_inst->GetItem()->ID; + } + if (Trader && srcitemid>0){ + ItemInst* srcbag; + ItemInst* dstbag; + uint32 srcbagid =0; + uint32 dstbagid = 0; + if (src_slot_id>=250 && src_slot_id<330){ + srcbag=m_inv.GetItem(((int)(src_slot_id/10))-3); + if(srcbag) + srcbagid=srcbag->GetItem()->ID; + } + if (dst_slot_id>=250 && dst_slot_id<330){ + dstbag=m_inv.GetItem(((int)(dst_slot_id/10))-3); + if(dstbag) + dstbagid=dstbag->GetItem()->ID; + } + if (srcitemid==17899 || srcbagid==17899 || dstitemid==17899 || dstbagid==17899){ + this->Trader_EndTrader(); + this->Message(13,"You cannot move your Trader Satchels, or items inside them, while Trading."); + } + } + + // Step 2: Validate item in from_slot + // After this, we can assume src_inst is a valid ptr + if (!src_inst && (src_slot_id<4000 || src_slot_id>4009)) { + if (dst_inst) { + // If there is no source item, but there is a destination item, + // move the slots around before deleting the invalid source slot item, + // which is now in the destination slot. + move_in->from_slot = dst_slot_check; + move_in->to_slot = src_slot_check; + move_in->number_in_stack = dst_inst->GetCharges(); + if(!SwapItem(move_in)) { mlog(INVENTORY__ERROR, "Recursive SwapItem call failed due to non-existent destination item (charid: %i, fromslot: %i, toslot: %i)", CharacterID(), src_slot_id, dst_slot_id); } + } + + return false; + } + //verify shared bank transactions in the database + if(src_inst && src_slot_id >= 2500 && src_slot_id <= 2550) { + if(!database.VerifyInventory(account_id, src_slot_id, src_inst)) { + LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploiting the shared bank.\n", account_name, GetName()); + DeleteItemInInventory(dst_slot_id,0,true); + return(false); + } + if(src_slot_id >= 2500 && src_slot_id <= 2501 && src_inst->IsType(ItemClassContainer)){ + for (uint8 idx=0; idx<10; idx++) { + const ItemInst* baginst = src_inst->GetItem(idx); + if(baginst && !database.VerifyInventory(account_id, Inventory::CalcSlotId(src_slot_id, idx), baginst)){ + DeleteItemInInventory(Inventory::CalcSlotId(src_slot_id, idx),0,false); + } + } + } + } + if(dst_inst && dst_slot_id >= 2500 && dst_slot_id <= 2550) { + if(!database.VerifyInventory(account_id, dst_slot_id, dst_inst)) { + LogFile->write(EQEMuLog::Error, "Player %s on account %s was found exploting the shared bank.\n", account_name, GetName()); + DeleteItemInInventory(src_slot_id,0,true); + return(false); + } + if(dst_slot_id >= 2500 && dst_slot_id <= 2501 && dst_inst->IsType(ItemClassContainer)){ + for (uint8 idx=0; idx<10; idx++) { + const ItemInst* baginst = dst_inst->GetItem(idx); + if(baginst && !database.VerifyInventory(account_id, Inventory::CalcSlotId(dst_slot_id, idx), baginst)){ + DeleteItemInInventory(Inventory::CalcSlotId(dst_slot_id, idx),0,false); + } + } + } + } + + + // Check for No Drop Hacks + Mob* with = trade->With(); + if (((with && with->IsClient() && dst_slot_id>=3000 && dst_slot_id<=3007) || // Trade + (dst_slot_id >= 2500 && dst_slot_id <= 2550)) // Shared Bank + && GetInv().CheckNoDrop(src_slot_id) + && RuleI(World, FVNoDropFlag) == 0 || RuleI(Character, MinStatusForNoDropExemptions) < Admin() && RuleI(World, FVNoDropFlag) == 2) { + DeleteItemInInventory(src_slot_id); + WorldKick(); + return false; + } + + // Step 3: Check for interaction with World Container (tradeskills) + if(m_tradeskill_object != NULL) { + if (src_slot_id>=4000 && src_slot_id<=4009) { + // Picking up item from world container + ItemInst* inst = m_tradeskill_object->PopItem(Inventory::CalcBagIdx(src_slot_id)); + if (inst) { + PutItemInInventory(dst_slot_id, *inst, false); + safe_delete(inst); + } + + if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in, true); } // QS Audit + + return true; + } + else if (dst_slot_id>=4000 && dst_slot_id<=4009) { + // Putting item into world container, which may swap (or pile onto) with existing item + uint8 world_idx = Inventory::CalcBagIdx(dst_slot_id); + ItemInst* world_inst = m_tradeskill_object->PopItem(world_idx); + + // Case 1: No item in container, unidirectional "Put" + if (world_inst == NULL) { + m_tradeskill_object->PutItem(world_idx, src_inst); + m_inv.DeleteItem(src_slot_id); + } + else { + const Item_Struct* world_item = world_inst->GetItem(); + const Item_Struct* src_item = src_inst->GetItem(); + if (world_item && src_item) { + // Case 2: Same item on cursor, stacks, transfer of charges needed + if ((world_item->ID == src_item->ID) && src_inst->IsStackable()) { + int16 world_charges = world_inst->GetCharges(); + int16 src_charges = src_inst->GetCharges(); + + // Fill up destination stack as much as possible + world_charges += src_charges; + if (world_charges > world_inst->GetItem()->StackSize) { + src_charges = world_charges - world_inst->GetItem()->StackSize; + world_charges = world_inst->GetItem()->StackSize; + } + else { + src_charges = 0; + } + + world_inst->SetCharges(world_charges); + m_tradeskill_object->Save(); + + if (src_charges == 0) { + m_inv.DeleteItem(src_slot_id); // DB remove will occur below + } + else { + src_inst->SetCharges(src_charges); + } + } + else { + // Case 3: Swap the item on user with item in world container + // World containers don't follow normal rules for swapping + ItemInst* inv_inst = m_inv.PopItem(src_slot_id); + m_tradeskill_object->PutItem(world_idx, inv_inst); + m_inv.PutItem(src_slot_id, *world_inst); + safe_delete(inv_inst); + } + } + } + + safe_delete(world_inst); + if (src_slot_id==SLOT_CURSOR) { + list::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end(); + database.SaveCursor(character_id, s, e); + } else + database.SaveInventory(character_id, m_inv[src_slot_id], src_slot_id); + + if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in, true); } // QS Audit + + return true; + } + } + + // Step 4: Check for entity trade + if (dst_slot_id>=3000 && dst_slot_id<=3007) { + if (src_slot_id != SLOT_CURSOR) { + Kick(); + return false; + } + if (with) { + mlog(INVENTORY__SLOTS, "Trade item move from slot %d to slot %d (trade with %s)", src_slot_id, dst_slot_id, with->GetName()); + // Fill Trade list with items from cursor + if (!m_inv[SLOT_CURSOR]) { + Message(13, "Error: Cursor item not located on server!"); + return false; + } + + // Add cursor item to trade bucket + // 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); + + return true; + } else { + if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit + + SummonItem(src_inst->GetID(), src_inst->GetCharges()); + DeleteItemInInventory(SLOT_CURSOR); + + return true; + } + } + + // Step 5: Swap (or stack) items + if (move_in->number_in_stack > 0) { + // Determine if charged items can stack + if(src_inst && !src_inst->IsStackable()) { + mlog(INVENTORY__ERROR, "Move from %d to %d with stack size %d. %s is not a stackable item. (charname: %s)", src_slot_id, dst_slot_id, move_in->number_in_stack, src_inst->GetItem()->Name, GetName()); + return false; + } + + if (dst_inst) { + if(src_inst->GetID() != dst_inst->GetID()) { + mlog(INVENTORY__ERROR, "Move from %d to %d with stack size %d. Incompatible item types: %d != %d", src_slot_id, dst_slot_id, move_in->number_in_stack, src_inst->GetID(), dst_inst->GetID()); + return(false); + } + if(dst_inst->GetCharges() < dst_inst->GetItem()->StackSize) { + //we have a chance of stacking. + mlog(INVENTORY__SLOTS, "Move from %d to %d with stack size %d. dest has %d/%d charges", src_slot_id, dst_slot_id, move_in->number_in_stack, dst_inst->GetCharges(), dst_inst->GetItem()->StackSize); + // Charges can be emptied into dst + uint16 usedcharges = dst_inst->GetItem()->StackSize - dst_inst->GetCharges(); + if (usedcharges > move_in->number_in_stack) + usedcharges = move_in->number_in_stack; + + dst_inst->SetCharges(dst_inst->GetCharges() + usedcharges); + src_inst->SetCharges(src_inst->GetCharges() - usedcharges); + + // Depleted all charges? + if (src_inst->GetCharges() < 1) + { + mlog(INVENTORY__SLOTS, "Dest (%d) now has %d charges, source (%d) was entirely consumed. (%d moved)", dst_slot_id, dst_inst->GetCharges(), src_slot_id, usedcharges); + database.SaveInventory(CharacterID(),NULL,src_slot_id); + m_inv.DeleteItem(src_slot_id); + } else { + mlog(INVENTORY__SLOTS, "Dest (%d) now has %d charges, source (%d) has %d (%d moved)", dst_slot_id, dst_inst->GetCharges(), src_slot_id, src_inst->GetCharges(), usedcharges); + } + } else { + mlog(INVENTORY__ERROR, "Move from %d to %d with stack size %d. Exceeds dest maximum stack size: %d/%d", src_slot_id, dst_slot_id, move_in->number_in_stack, (src_inst->GetCharges()+dst_inst->GetCharges()), dst_inst->GetItem()->StackSize); + return false; + } + } + else { + // Nothing in destination slot: split stack into two + if ((int16)move_in->number_in_stack >= src_inst->GetCharges()) { + // Move entire stack + if(!m_inv.SwapItem(src_slot_id, dst_slot_id)) { return false; } + mlog(INVENTORY__SLOTS, "Move entire stack from %d to %d with stack size %d. Dest empty.", src_slot_id, dst_slot_id, move_in->number_in_stack); + } + else { + // Split into two + src_inst->SetCharges(src_inst->GetCharges() - move_in->number_in_stack); + mlog(INVENTORY__SLOTS, "Split stack of %s (%d) from slot %d to %d with stack size %d. Src keeps %d.", src_inst->GetItem()->Name, src_inst->GetItem()->ID, src_slot_id, dst_slot_id, move_in->number_in_stack, src_inst->GetCharges()); + ItemInst* inst = database.CreateItem(src_inst->GetItem(), move_in->number_in_stack); + m_inv.PutItem(dst_slot_id, *inst); + safe_delete(inst); + } + } + } + else { + // Not dealing with charges - just do direct swap + if(src_inst && (dst_slot_id < 22 || dst_slot_id == 9999) && dst_slot_id >= 0) { + if (src_inst->GetItem()->Attuneable) { + src_inst->SetInstNoDrop(true); + } + if (src_inst->IsAugmented()) { + for(int i = 0; i < MAX_AUGMENT_SLOTS; i++) { + if (src_inst->GetAugment(i)) { + if (src_inst->GetAugment(i)->GetItem()->Attuneable) { + src_inst->GetAugment(i)->SetInstNoDrop(true); + } + } + } + } + SetMaterial(dst_slot_id,src_inst->GetItem()->ID); + } + if(!m_inv.SwapItem(src_slot_id, dst_slot_id)) { return false; } + mlog(INVENTORY__SLOTS, "Moving entire item from slot %d to slot %d", src_slot_id, dst_slot_id); + } + + int matslot = SlotConvert2(dst_slot_id); + if (dst_slot_id<22 && matslot != 0) { + SendWearChange(matslot); + } + + // Step 7: Save change to the database + if (src_slot_id==SLOT_CURSOR){ + list::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end(); + database.SaveCursor(character_id, s, e); + } else + database.SaveInventory(character_id, m_inv.GetItem(src_slot_id), src_slot_id); + if (dst_slot_id==SLOT_CURSOR) { + list::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end(); + database.SaveCursor(character_id, s, e); + } else + database.SaveInventory(character_id, m_inv.GetItem(dst_slot_id), dst_slot_id); + + if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in, true); } // QS Audit + + // Step 8: Re-calc stats + CalcBonuses(); + return true; +} + +void Client::SwapItemResync(MoveItem_Struct* move_slots) { + // resync the 'from' and 'to' slots on an as-needed basis + // Not as effective as the full process, but less intrusive to gameplay -U + mlog(INVENTORY__ERROR, "Inventory desyncronization. (charname: %s, source: %i, destination: %i)", GetName(), move_slots->from_slot, move_slots->to_slot); + Message(15, "Inventory Desyncronization detected: Resending slot data..."); + + if((move_slots->from_slot >= 0 && move_slots->from_slot <= 340) || move_slots->from_slot == 9999) { + int16 resync_slot = (Inventory::CalcSlotId(move_slots->from_slot) == SLOT_INVALID) ? move_slots->from_slot : Inventory::CalcSlotId(move_slots->from_slot); + if(IsValidSlot(resync_slot) && resync_slot != SLOT_INVALID) { + // This prevents the client from crashing when closing any 'phantom' bags -U + const Item_Struct* token_struct = database.GetItem(22292); // 'Copper Coin' + ItemInst* token_inst = database.CreateItem(token_struct, 1); + + SendItemPacket(resync_slot, token_inst, ItemPacketTrade); + + if(m_inv[resync_slot]) { SendItemPacket(resync_slot, m_inv[resync_slot], ItemPacketTrade); } + else { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_DeleteItem, sizeof(DeleteItem_Struct)); + DeleteItem_Struct* delete_slot = (DeleteItem_Struct*)outapp->pBuffer; + delete_slot->from_slot = resync_slot; + delete_slot->to_slot = 0xFFFFFFFF; + delete_slot->number_in_stack = 0xFFFFFFFF; + + QueuePacket(outapp); + safe_delete(outapp); + } + Message(14, "Source slot %i resyncronized.", move_slots->from_slot); + } + else { Message(13, "Could not resyncronize source slot %i.", move_slots->from_slot); } + } + else { + int16 resync_slot = (Inventory::CalcSlotId(move_slots->from_slot) == SLOT_INVALID) ? move_slots->from_slot : Inventory::CalcSlotId(move_slots->from_slot); + if(IsValidSlot(resync_slot) && resync_slot != SLOT_INVALID) { + if(m_inv[resync_slot]) { + const Item_Struct* token_struct = database.GetItem(22292); // 'Copper Coin' + ItemInst* token_inst = database.CreateItem(token_struct, 1); + + SendItemPacket(resync_slot, token_inst, ItemPacketTrade); + SendItemPacket(resync_slot, m_inv[resync_slot], ItemPacketTrade); + + Message(14, "Source slot %i resyncronized.", move_slots->from_slot); + } + else { Message(13, "Could not resyncronize source slot %i.", move_slots->from_slot); } + } + else { Message(13, "Could not resyncronize source slot %i.", move_slots->from_slot); } + } + + if((move_slots->to_slot >= 0 && move_slots->to_slot <= 340) || move_slots->to_slot == 9999) { + int16 resync_slot = (Inventory::CalcSlotId(move_slots->to_slot) == SLOT_INVALID) ? move_slots->to_slot : Inventory::CalcSlotId(move_slots->to_slot); + if(IsValidSlot(resync_slot) && resync_slot != SLOT_INVALID) { + const Item_Struct* token_struct = database.GetItem(22292); // 'Copper Coin' + ItemInst* token_inst = database.CreateItem(token_struct, 1); + + SendItemPacket(resync_slot, token_inst, ItemPacketTrade); + + if(m_inv[resync_slot]) { SendItemPacket(resync_slot, m_inv[resync_slot], ItemPacketTrade); } + else { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_DeleteItem, sizeof(DeleteItem_Struct)); + DeleteItem_Struct* delete_slot = (DeleteItem_Struct*)outapp->pBuffer; + delete_slot->from_slot = resync_slot; + delete_slot->to_slot = 0xFFFFFFFF; + delete_slot->number_in_stack = 0xFFFFFFFF; + + QueuePacket(outapp); + safe_delete(outapp); + } + Message(14, "Destination slot %i resyncronized.", move_slots->to_slot); + } + else { Message(13, "Could not resyncronize destination slot %i.", move_slots->to_slot); } + } + else { + int16 resync_slot = (Inventory::CalcSlotId(move_slots->to_slot) == SLOT_INVALID) ? move_slots->to_slot : Inventory::CalcSlotId(move_slots->to_slot); + if(IsValidSlot(resync_slot) && resync_slot != SLOT_INVALID) { + if(m_inv[resync_slot]) { + const Item_Struct* token_struct = database.GetItem(22292); // 'Copper Coin' + ItemInst* token_inst = database.CreateItem(token_struct, 1); + + SendItemPacket(resync_slot, token_inst, ItemPacketTrade); + SendItemPacket(resync_slot, m_inv[resync_slot], ItemPacketTrade); + + Message(14, "Destination slot %i resyncronized.", move_slots->to_slot); + } + else { Message(13, "Could not resyncronize destination slot %i.", move_slots->to_slot); } + } + else { Message(13, "Could not resyncronize destination slot %i.", move_slots->to_slot); } + } +} + +void Client::QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call) { + int16 from_slot_id = static_cast(move_in->from_slot); + int16 to_slot_id = static_cast(move_in->to_slot); + int16 move_amount = static_cast(move_in->number_in_stack); + + if(!m_inv[from_slot_id] && !m_inv[to_slot_id]) { return; } + + uint16 move_count = 0; + + if(m_inv[from_slot_id]) { move_count += m_inv[from_slot_id]->GetTotalItemCount(); } + if(to_slot_id != from_slot_id) { if(m_inv[to_slot_id]) { move_count += m_inv[to_slot_id]->GetTotalItemCount(); } } + + ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMoves, sizeof(QSPlayerLogMove_Struct) + (sizeof(QSMoveItems_Struct) * move_count)); + QSPlayerLogMove_Struct* qsaudit = (QSPlayerLogMove_Struct*)qspack->pBuffer; + + qsaudit->char_id = character_id; + qsaudit->stack_size = move_amount; + qsaudit->char_count = move_count; + qsaudit->postaction = postaction_call; + qsaudit->from_slot = from_slot_id; + qsaudit->to_slot = to_slot_id; + + move_count = 0; + + const ItemInst* from_inst = m_inv[postaction_call?to_slot_id:from_slot_id]; + + if(from_inst) { + qsaudit->items[move_count].from_slot = from_slot_id; + qsaudit->items[move_count].to_slot = to_slot_id; + qsaudit->items[move_count].item_id = from_inst->GetID(); + qsaudit->items[move_count].charges = from_inst->GetCharges(); + qsaudit->items[move_count].aug_1 = from_inst->GetAugmentItemID(1); + qsaudit->items[move_count].aug_2 = from_inst->GetAugmentItemID(2); + qsaudit->items[move_count].aug_3 = from_inst->GetAugmentItemID(3); + qsaudit->items[move_count].aug_4 = from_inst->GetAugmentItemID(4); + qsaudit->items[move_count++].aug_5 = from_inst->GetAugmentItemID(5); + + if(from_inst->IsType(ItemClassContainer)) { + for(uint8 bag_idx = 0; bag_idx < from_inst->GetItem()->BagSlots; bag_idx++) { + const ItemInst* from_baginst = from_inst->GetItem(bag_idx); + + if(from_baginst) { + qsaudit->items[move_count].from_slot = Inventory::CalcSlotId(from_slot_id, bag_idx); + qsaudit->items[move_count].to_slot = Inventory::CalcSlotId(to_slot_id, bag_idx); + qsaudit->items[move_count].item_id = from_baginst->GetID(); + qsaudit->items[move_count].charges = from_baginst->GetCharges(); + qsaudit->items[move_count].aug_1 = from_baginst->GetAugmentItemID(1); + qsaudit->items[move_count].aug_2 = from_baginst->GetAugmentItemID(2); + qsaudit->items[move_count].aug_3 = from_baginst->GetAugmentItemID(3); + qsaudit->items[move_count].aug_4 = from_baginst->GetAugmentItemID(4); + qsaudit->items[move_count++].aug_5 = from_baginst->GetAugmentItemID(5); + } + } + } + } + + if(to_slot_id != from_slot_id) { + const ItemInst* to_inst = m_inv[postaction_call?from_slot_id:to_slot_id]; + + if(to_inst) { + qsaudit->items[move_count].from_slot = to_slot_id; + qsaudit->items[move_count].to_slot = from_slot_id; + qsaudit->items[move_count].item_id = to_inst->GetID(); + qsaudit->items[move_count].charges = to_inst->GetCharges(); + qsaudit->items[move_count].aug_1 = to_inst->GetAugmentItemID(1); + qsaudit->items[move_count].aug_2 = to_inst->GetAugmentItemID(2); + qsaudit->items[move_count].aug_3 = to_inst->GetAugmentItemID(3); + qsaudit->items[move_count].aug_4 = to_inst->GetAugmentItemID(4); + qsaudit->items[move_count++].aug_5 = to_inst->GetAugmentItemID(5); + + if(to_inst->IsType(ItemClassContainer)) { + for(uint8 bag_idx = 0; bag_idx < to_inst->GetItem()->BagSlots; bag_idx++) { + const ItemInst* to_baginst = to_inst->GetItem(bag_idx); + + if(to_baginst) { + qsaudit->items[move_count].from_slot = Inventory::CalcSlotId(to_slot_id, bag_idx); + qsaudit->items[move_count].to_slot = Inventory::CalcSlotId(from_slot_id, bag_idx); + qsaudit->items[move_count].item_id = to_baginst->GetID(); + qsaudit->items[move_count].charges = to_baginst->GetCharges(); + qsaudit->items[move_count].aug_1 = to_baginst->GetAugmentItemID(1); + qsaudit->items[move_count].aug_2 = to_baginst->GetAugmentItemID(2); + qsaudit->items[move_count].aug_3 = to_baginst->GetAugmentItemID(3); + qsaudit->items[move_count].aug_4 = to_baginst->GetAugmentItemID(4); + qsaudit->items[move_count++].aug_5 = to_baginst->GetAugmentItemID(5); + } + } + } + } + } + + if(move_count && worldserver.Connected()) { + qspack->Deflate(); + worldserver.SendPacket(qspack); + } + + safe_delete(qspack); +} + +void Client::DyeArmor(DyeStruct* dye){ + int16 slot=0; + for(int i=0;i<7;i++){ + if(m_pp.item_tint[i].rgb.blue!=dye->dye[i].rgb.blue || + m_pp.item_tint[i].rgb.red!=dye->dye[i].rgb.red || + m_pp.item_tint[i].rgb.green != dye->dye[i].rgb.green){ + slot = m_inv.HasItem(32557, 1, invWherePersonal); + if(slot != SLOT_INVALID){ + DeleteItemInInventory(slot,1,true); + 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)); + database.SaveInventory(CharacterID(),inst,slot2); + if(dye->dye[i].rgb.use_tint) + m_pp.item_tint[i].rgb.use_tint = 0xFF; + else + m_pp.item_tint[i].rgb.use_tint=0x00; + } + m_pp.item_tint[i].rgb.blue=dye->dye[i].rgb.blue; + m_pp.item_tint[i].rgb.red=dye->dye[i].rgb.red; + m_pp.item_tint[i].rgb.green=dye->dye[i].rgb.green; + SendWearChange(i); + } + else{ + Message(13,"Could not locate A Vial of Prismatic Dye."); + return; + } + } + } + EQApplicationPacket* outapp=new EQApplicationPacket(OP_Dye,0); + QueuePacket(outapp); + safe_delete(outapp); + Save(); +} + +/*bool Client::DecreaseByItemType(uint32 type, uint8 amt) { + const Item_Struct* TempItem = 0; + ItemInst* ins; + int x; + for(x=0; x <= 30; x++) + { + TempItem = 0; + ins = GetInv().GetItem(x); + if (ins) + TempItem = ins->GetItem(); + if (TempItem && TempItem->ItemType == type) + { + if (ins->GetCharges() < amt) + { + amt -= ins->GetCharges(); + DeleteItemInInventory(x,amt,true); + } + else + { + DeleteItemInInventory(x,amt,true); + amt = 0; + } + if (amt < 1) + return true; + } + } + for(x=251; x < 331; x++) + { + TempItem = 0; + ins = GetInv().GetItem(x); + if (ins) + TempItem = ins->GetItem(); + if (TempItem && TempItem->ItemType == type) + { + if (ins->GetCharges() < amt) + { + amt -= ins->GetCharges(); + DeleteItemInInventory(x,amt,true); + } + else + { + DeleteItemInInventory(x,amt,true); + amt = 0; + } + if (amt < 1) + return true; + } + } + return false; +}*/ + +bool Client::DecreaseByID(uint32 type, uint8 amt) { + const Item_Struct* TempItem = 0; + ItemInst* ins; + int x; + int num = 0; + for(x=0; x < 331; x++) + { + if (x == 31) + x = 251; + TempItem = 0; + ins = GetInv().GetItem(x); + if (ins) + TempItem = ins->GetItem(); + if (TempItem && TempItem->ID == type) + { + num += ins->GetCharges(); + if (num >= amt) + break; + } + } + if (num < amt) + return false; + for(x=0; x < 331; x++) + { + if (x == 31) + x = 251; + TempItem = 0; + ins = GetInv().GetItem(x); + if (ins) + TempItem = ins->GetItem(); + if (TempItem && TempItem->ID == type) + { + if (ins->GetCharges() < amt) + { + amt -= ins->GetCharges(); + DeleteItemInInventory(x,amt,true); + } + else + { + DeleteItemInInventory(x,amt,true); + amt = 0; + } + if (amt < 1) + break; + } + } + return true; +} + +void Client::RemoveNoRent(bool client_update) { + + int16 slot_id; + + // personal + for(slot_id = 0; slot_id <= 30; 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[9999]; + if(inst && !inst->GetItem()->NoRent) { + mlog(INVENTORY__SLOTS, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + DeleteItemInInventory(9999, 0, (GetClientVersion() >= EQClientSoF) ? client_update : false); // Ti slot non-existent + } + + // containers + for(slot_id = 251; slot_id <= 340; 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); + } + } + + // bank + for(slot_id = 2000; slot_id <= 2023; 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, false); // Can't delete from client Bank slots + } + } + + // bank containers + for(slot_id = 2031; slot_id <= 2270; 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, false); // Can't delete from client Bank Container slots + } + } + + // shared bank + for(slot_id = 2500; slot_id <= 2501; 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, false); // Can't delete from client Shared Bank slots + } + } + + // shared bank containers + for(slot_id = 2531; slot_id <= 2550; 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, false); // Can't delete from client Shared Bank Container slots + } + } +} + +// 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; + + // personal + for(slot_id = 0; slot_id <= 30; 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, NULL, slot_id); + } + else { + m_inv.PutItem(slot_id, *inst); + } + safe_delete(inst); + } + } + + // power source + ItemInst* inst = m_inv.PopItem(9999); + 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, NULL, 9999); + } + else { + m_inv.PutItem(9999, *inst); + } + safe_delete(inst); + } + + // containers + for(slot_id = 251; slot_id <= 340; 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, NULL, slot_id); + } + else { + m_inv.PutItem(slot_id, *inst); + } + safe_delete(inst); + } + } + + // bank + for(slot_id = 2000; slot_id <= 2023; 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, NULL, slot_id); + } + else { + m_inv.PutItem(slot_id, *inst); + } + safe_delete(inst); + } + } + + // bank containers + for(slot_id = 2031; slot_id <= 2270; 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, NULL, slot_id); + } + else { + m_inv.PutItem(slot_id, *inst); + } + safe_delete(inst); + } + } + + // Shared Bank and Shared Bank Containers are not checked due to their allowing duplicate lore items -U +} + +void Client::MoveSlotNotAllowed(bool client_update) { + + int16 slot_id; + + // equipment + for(slot_id = 0; slot_id <= 21; slot_id++) { + if(m_inv[slot_id] && !m_inv[slot_id]->IsSlotAllowed(slot_id)) { + ItemInst* inst = m_inv.PopItem(slot_id); + bool is_arrow = (inst->GetItem()->ItemType == ItemTypeArrow) ? true : false; + int16 free_slot_id = m_inv.FindFreeSlot(inst->IsType(ItemClassContainer), true, inst->GetItem()->Size, is_arrow); + mlog(INVENTORY__ERROR, "Slot Assignment Error: Moving %s from slot %i to %i", inst->GetItem()->Name, slot_id, free_slot_id); + PutItemInInventory(free_slot_id, *inst, client_update); + database.SaveInventory(character_id, NULL, slot_id); + safe_delete(inst); + } + } + + // power source + slot_id = 9999; + if(m_inv[slot_id] && !m_inv[slot_id]->IsSlotAllowed(slot_id)) { + ItemInst* inst = m_inv.PopItem(slot_id); + bool is_arrow = (inst->GetItem()->ItemType == ItemTypeArrow) ? true : false; + int16 free_slot_id = m_inv.FindFreeSlot(inst->IsType(ItemClassContainer), true, inst->GetItem()->Size, is_arrow); + mlog(INVENTORY__ERROR, "Slot Assignment Error: Moving %s from slot %i to %i", inst->GetItem()->Name, slot_id, free_slot_id); + PutItemInInventory(free_slot_id, *inst, (GetClientVersion() >= EQClientSoF) ? client_update : false); + database.SaveInventory(character_id, NULL, slot_id); + safe_delete(inst); + } + + // No need to check inventory, cursor, bank or shared bank since they allow max item size and containers -U + // Code can be added to check item size vs. container size, but it is left to attrition for now. +} + +// these functions operate with a material slot, which is from 0 to 8 +uint32 Client::GetEquipment(uint8 material_slot) const +{ + int invslot; + const ItemInst *item; + + if(material_slot > 8) + { + return 0; + } + + invslot = Inventory::CalcSlotFromMaterial(material_slot); + if(invslot == -1) + { + return 0; + } + + item = m_inv.GetItem(invslot); + + if(item && item->GetItem()) + { + return item->GetItem()->ID; + } + + return 0; +} + +/* +int32 Client::GetEquipmentMaterial(uint8 material_slot) +{ + const Item_Struct *item; + + item = database.GetItem(GetEquipment(material_slot)); + if(item != 0) + { + return item->Material; + } + + return 0; +} +*/ + +uint32 Client::GetEquipmentColor(uint8 material_slot) const +{ + const Item_Struct *item; + + if(material_slot > 8) + { + return 0; + } + + item = database.GetItem(GetEquipment(material_slot)); + if(item != 0) + { + return m_pp.item_tint[material_slot].rgb.use_tint ? + m_pp.item_tint[material_slot].color : + item->Color; + } + + return 0; +} + +bool Client::LootToStack(uint32 itemid) { //Loots stackable items to existing stacks - Wiz + // @merth: Need to do loot code with new inventory struct + /* + const Item_Struct* item; + int i; + for (i=22; i<=29; i++) { + item = GetItemAt(i); + if (item) { + if (m_pp.invitemproperties[i].charges < 20 && item->ID == itemid) + { + m_pp.invitemproperties[i].charges += 1; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PlaceItem, sizeof(Item_Struct)); + memcpy(outapp->pBuffer, item, outapp->size); + Item_Struct* outitem = (Item_Struct*) outapp->pBuffer; + outitem->equipSlot = i; + outitem->charges = m_pp.invitemproperties[i].charges; + QueuePacket(outapp); + safe_delete(outapp); + return true; + } + } + } + for (i=0; i<=pp_containerinv_size; i++) { + if (m_pp.containerinv[i] != 0xFFFF) { + item = database.GetItem(m_pp.containerinv[i]); + if (m_pp.bagitemproperties[i].charges < 20 && item->ID == itemid) + { + m_pp.bagitemproperties[i].charges += 1; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PlaceItem, sizeof(Item_Struct)); + memcpy(outapp->pBuffer, item, outapp->size); + Item_Struct* outitem = (Item_Struct*) outapp->pBuffer; + outitem->equipSlot = 250+i; + outitem->charges = m_pp.bagitemproperties[i].charges; + QueuePacket(outapp); + safe_delete(outapp); + return true; + } + } + } + */ + return false; +} + +// Send an item packet (including all subitems of the item) +void Client::SendItemPacket(int16 slot_id, const ItemInst* inst, ItemPacketType packet_type) +{ + if (!inst) + return; + + // Serialize item into |-delimited string + string packet = inst->Serialize(slot_id); + + EmuOpcode opcode = OP_Unknown; + EQApplicationPacket* outapp = NULL; + ItemPacket_Struct* itempacket = NULL; + + // Construct packet + opcode = (packet_type==ItemPacketViewLink) ? OP_ItemLinkResponse : OP_ItemPacket; + outapp = new EQApplicationPacket(opcode, packet.length()+sizeof(ItemPacket_Struct)); + itempacket = (ItemPacket_Struct*)outapp->pBuffer; + memcpy(itempacket->SerializedItem, packet.c_str(), packet.length()); + itempacket->PacketType = packet_type; + +#if EQDEBUG >= 9 + DumpPacket(outapp); +#endif + //DumpPacket(outapp); + FastQueuePacket(&outapp); +} + +EQApplicationPacket* Client::ReturnItemPacket(int16 slot_id, const ItemInst* inst, ItemPacketType packet_type) +{ + if (!inst) + return 0; + + // Serialize item into |-delimited string + string packet = inst->Serialize(slot_id); + + EmuOpcode opcode = OP_Unknown; + EQApplicationPacket* outapp = NULL; + BulkItemPacket_Struct* itempacket = NULL; + + // Construct packet + opcode = OP_ItemPacket; + outapp = new EQApplicationPacket(opcode, packet.length()+1); + itempacket = (BulkItemPacket_Struct*)outapp->pBuffer; + memcpy(itempacket->SerializedItem, packet.c_str(), packet.length()); + +#if EQDEBUG >= 9 + DumpPacket(outapp); +#endif + + return outapp; +} + +static int16 BandolierSlotToWeaponSlot(int BandolierSlot) { + + switch(BandolierSlot) { + case bandolierMainHand: + return SLOT_PRIMARY; + case bandolierOffHand: + return SLOT_SECONDARY; + case bandolierRange: + return SLOT_RANGE; + default: + return SLOT_AMMO; + } +} + +void Client::CreateBandolier(const EQApplicationPacket *app) { + + // Store bandolier set with the number and name passed by the client, along with the items that are currently + // in the players weapon slots. + + BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer; + + _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; + + int16 WeaponSlot; + + for(int BandolierSlot = bandolierMainHand; BandolierSlot <= bandolierAmmo; BandolierSlot++) { + WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot); + InvItem = GetInv()[WeaponSlot]; + if(InvItem) { + BaseItem = InvItem->GetItem(); + _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; + } + else { + _log(INVENTORY__BANDOLIER, "Char: %s no item in slot %i", GetName(), WeaponSlot); + m_pp.bandoliers[bs->number].items[BandolierSlot].item_id = 0; + 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; + } + Save(); +} + +void Client::SetBandolier(const EQApplicationPacket *app) { + + // Swap the weapons in the given bandolier set into the character's weapon slots and return + // any items currently in the weapon slots to inventory. + + BandolierSet_Struct *bss = (BandolierSet_Struct*)app->pBuffer; + _log(INVENTORY__BANDOLIER, "Char: %s activating set %i", GetName(), bss->number); + int16 slot; + int16 WeaponSlot; + ItemInst *BandolierItems[4]; // Temporary holding area for the weapons we pull out of their inventory + + // First we pull the items for this bandolier set out of their inventory, this makes space to put the + // currently equipped items back. + for(int BandolierSlot = bandolierMainHand; BandolierSlot <= bandolierAmmo; BandolierSlot++) { + // If this bandolier set has an item in this position + if(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id) { + WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot); + + // Check if the player has the item specified in the bandolier set on them. + // + slot = m_inv.HasItem(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id, 1, + invWhereWorn|invWherePersonal); + + // removed 'invWhereCursor' argument from above and implemented slots 30, 331-340 checks here + if (slot == SLOT_INVALID) { + if (m_inv.GetItem(SLOT_CURSOR)) { + if (m_inv.GetItem(SLOT_CURSOR)->GetItem()->ID == m_pp.bandoliers[bss->number].items[BandolierSlot].item_id && + m_inv.GetItem(SLOT_CURSOR)->GetCharges() >= 1) // '> 0' the same, but this matches Inventory::_HasItem conditional check + slot = SLOT_CURSOR; + else if (m_inv.GetItem(SLOT_CURSOR)->GetItem()->ItemClass == 1) { + for(int16 CursorBagSlot = 331; CursorBagSlot <= 340; CursorBagSlot++) { + if (m_inv.GetItem(CursorBagSlot)) { + if (m_inv.GetItem(CursorBagSlot)->GetItem()->ID == m_pp.bandoliers[bss->number].items[BandolierSlot].item_id && + m_inv.GetItem(CursorBagSlot)->GetCharges() >= 1) { // ditto + slot = CursorBagSlot; + break; + } + } + } + } + } + } + + // if the player has this item in their inventory, + if(slot != SLOT_INVALID) { + // Pull the item out of the inventory + BandolierItems[BandolierSlot] = m_inv.PopItem(slot); + // If ammo with charges, only take one charge out to put in the range slot, that is what + // the client does. + + if(((BandolierSlot == bandolierAmmo) || (BandolierSlot == bandolierRange)) && + BandolierItems[BandolierSlot] && BandolierItems[BandolierSlot]->IsStackable()){ + int Charges = BandolierItems[BandolierSlot]->GetCharges(); + // If there is more than one charge + if(Charges > 1) { + BandolierItems[BandolierSlot]->SetCharges(Charges-1); + // Take one charge out and put the rest back + m_inv.PutItem(slot, *BandolierItems[BandolierSlot]); + database.SaveInventory(character_id, BandolierItems[BandolierSlot], slot); + BandolierItems[BandolierSlot]->SetCharges(1); + } + else // Remove the item from the inventory + database.SaveInventory(character_id, 0, slot); + } + else // Remove the item from the inventory + database.SaveInventory(character_id, 0, slot); + } + else { // The player doesn't have the required weapon with them. + BandolierItems[BandolierSlot] = 0; + if(slot == SLOT_INVALID) { + _log(INVENTORY__BANDOLIER, "Character does not have required bandolier item for slot %i", WeaponSlot); + ItemInst *InvItem = m_inv.PopItem(WeaponSlot); + if(InvItem) { + // If there was an item in that weapon slot, put it in the inventory + _log(INVENTORY__BANDOLIER, "returning item %s in weapon slot %i to inventory", + InvItem->GetItem()->Name, WeaponSlot); + if(MoveItemToInventory(InvItem)) + database.SaveInventory(character_id, 0, WeaponSlot); + else + _log(INVENTORY__BANDOLIER, "Char: %s, ERROR returning %s to inventory", GetName(), + InvItem->GetItem()->Name); + safe_delete(InvItem); + } + + } + } + } + } + + // Now we move the required weapons into the character weapon slots, and return any items we are replacing + // back to inventory. + // + for(int BandolierSlot = bandolierMainHand; BandolierSlot <= bandolierAmmo; BandolierSlot++) { + + // Find the inventory slot corresponding to this bandolier slot + + WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot); + + // if there is an item in this Bandolier slot ? + if(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id) { + // if the player has this item in their inventory, and it is not already where it needs to be + if(BandolierItems[BandolierSlot]) { + // Pull the item that we are going to replace + ItemInst *InvItem = m_inv.PopItem(WeaponSlot); + // Put the item specified in the bandolier where it needs to be + m_inv.PutItem(WeaponSlot, *BandolierItems[BandolierSlot]); + + safe_delete(BandolierItems[BandolierSlot]); + // Update the database, save the item now in the weapon slot + database.SaveInventory(character_id, m_inv.GetItem(WeaponSlot), WeaponSlot); + + if(InvItem) { + // If there was already an item in that weapon slot that we replaced, find a place to put it + if(!MoveItemToInventory(InvItem)) + _log(INVENTORY__BANDOLIER, "Char: %s, ERROR returning %s to inventory", GetName(), + InvItem->GetItem()->Name); + safe_delete(InvItem); + } + } + } + else { + // This bandolier set has no item for this slot, so take whatever is in the weapon slot and + // put it in the player's inventory. + ItemInst *InvItem = m_inv.PopItem(WeaponSlot); + if(InvItem) { + _log(INVENTORY__BANDOLIER, "Bandolier has no item for slot %i, returning item %s to inventory", + WeaponSlot, InvItem->GetItem()->Name); + // If there was an item in that weapon slot, put it in the inventory + if(MoveItemToInventory(InvItem)) + database.SaveInventory(character_id, 0, WeaponSlot); + else + _log(INVENTORY__BANDOLIER, "Char: %s, ERROR returning %s to inventory", GetName(), + InvItem->GetItem()->Name); + safe_delete(InvItem); + } + } + } + // finally, recalculate any stat bonuses from the item change + CalcBonuses(); +} + +bool Client::MoveItemToInventory(ItemInst *ItemToReturn, bool UpdateClient) { + + // This is a support function for Client::SetBandolier, however it can be used anywhere it's functionality is required. + // + // When the client moves items around as Bandolier sets are activated, it does not send details to the + // server of what item it has moved to which slot. It assumes the server knows what it will do. + // + // The standard EQEmu auto inventory routines do not behave as the client does when manipulating bandoliers. + // The client will look in each main inventory slot. If it finds a bag in a slot, it will then look inside + // the bag for a free slot. + // + // This differs from the standard EQEmu method of looking in all 8 inventory slots first to find an empty slot, and + // then going back and looking in bags. There are also other differences related to how it moves stackable items back + // to inventory. + // + // Rather than alter the current auto inventory behaviour, just in case something + // depends on current behaviour, this routine operates the same as the client when moving items back to inventory when + // swapping bandolier sets. + + if(!ItemToReturn) return false; + + _log(INVENTORY__SLOTS,"Char: %s Returning %s to inventory", GetName(), ItemToReturn->GetItem()->Name); + + uint32 ItemID = ItemToReturn->GetItem()->ID; + + // If the item is stackable (ammo in range slot), try stacking it with other items of the same type + // + if(ItemToReturn->IsStackable()) { + + for (int16 i=22; i<=30; i++) { // changed slot max to 30 from 29. client will stack into slot 30 (bags too) before moving. + + ItemInst* InvItem = m_inv.GetItem(i); + + if(InvItem && (InvItem->GetItem()->ID == ItemID) && (InvItem->GetCharges() < InvItem->GetItem()->StackSize)) { + + int ChargeSlotsLeft = InvItem->GetItem()->StackSize - InvItem->GetCharges(); + + int ChargesToMove = ItemToReturn->GetCharges() < ChargeSlotsLeft ? ItemToReturn->GetCharges() : + ChargeSlotsLeft; + + InvItem->SetCharges(InvItem->GetCharges() + ChargesToMove); + + if(UpdateClient) + SendItemPacket(i, InvItem, ItemPacketTrade); + + database.SaveInventory(character_id, m_inv.GetItem(i), i); + + ItemToReturn->SetCharges(ItemToReturn->GetCharges() - ChargesToMove); + + if(!ItemToReturn->GetCharges()) + return true; + } + // If there is a bag in this slot, look inside it. + // + if (InvItem && InvItem->IsType(ItemClassContainer)) { + + int16 BaseSlotID = Inventory::CalcSlotId(i, 0); + + uint8 BagSize=InvItem->GetItem()->BagSlots; + + uint8 BagSlot; + for (BagSlot=0; BagSlotGetItem()->ID == ItemID) && + (InvItem->GetCharges() < InvItem->GetItem()->StackSize)) { + + int ChargeSlotsLeft = InvItem->GetItem()->StackSize - InvItem->GetCharges(); + + int ChargesToMove = ItemToReturn->GetCharges() < ChargeSlotsLeft + ? ItemToReturn->GetCharges() : ChargeSlotsLeft; + + InvItem->SetCharges(InvItem->GetCharges() + ChargesToMove); + + if(UpdateClient) + SendItemPacket(BaseSlotID + BagSlot, m_inv.GetItem(BaseSlotID + BagSlot), + ItemPacketTrade); + + database.SaveInventory(character_id, m_inv.GetItem(BaseSlotID + BagSlot), + BaseSlotID + BagSlot); + + ItemToReturn->SetCharges(ItemToReturn->GetCharges() - ChargesToMove); + + if(!ItemToReturn->GetCharges()) + return true; + } + } + } + } + } + + // We have tried stacking items, now just try and find an empty slot. + + for (int16 i=22; i<=30; i++) { // changed slot max to 30 from 29. client will move into slot 30 (bags too) before pushing onto cursor. + + ItemInst* InvItem = m_inv.GetItem(i); + + if (!InvItem) { + // Found available slot in personal inventory + m_inv.PutItem(i, *ItemToReturn); + + if(UpdateClient) + SendItemPacket(i, ItemToReturn, ItemPacketTrade); + + database.SaveInventory(character_id, m_inv.GetItem(i), i); + + _log(INVENTORY__SLOTS, "Char: %s Storing in main inventory slot %i", GetName(), i); + + return true; + } + if(InvItem->IsType(ItemClassContainer) && Inventory::CanItemFitInContainer(ItemToReturn->GetItem(), InvItem->GetItem())) { + + int16 BaseSlotID = Inventory::CalcSlotId(i, 0); + + uint8 BagSize=InvItem->GetItem()->BagSlots; + + for (uint8 BagSlot=0; BagSlotSetCustomData(identifier, value); + database.SaveInventory(character_id, inst, slot_id); + } +} + +void Inventory::SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, int value) { + ItemInst *inst = GetItem(slot_id); + if(inst) { + inst->SetCustomData(identifier, value); + database.SaveInventory(character_id, inst, slot_id); + } +} + +void Inventory::SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, float value) { + ItemInst *inst = GetItem(slot_id); + if(inst) { + inst->SetCustomData(identifier, value); + database.SaveInventory(character_id, inst, slot_id); + } +} + +void Inventory::SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, bool value) { + ItemInst *inst = GetItem(slot_id); + if(inst) { + inst->SetCustomData(identifier, value); + database.SaveInventory(character_id, inst, slot_id); + } +} + +std::string Inventory::GetCustomItemData(int16 slot_id, std::string identifier) { + ItemInst *inst = GetItem(slot_id); + if(inst) { + return inst->GetCustomData(identifier); + } + return ""; +} diff --git a/zone/loottable.h b/zone/loottable.h new file mode 100644 index 000000000..a06d71cc9 --- /dev/null +++ b/zone/loottable.h @@ -0,0 +1,81 @@ +/* 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 +*/ +/* +alter table npc_types add column loottable_id int(11) unsigned not null; + +create table loottable (id int(11) unsigned auto_increment primary key, name varchar(255) not null unique, +mincash int(11) unsigned not null, maxcash int(11) unsigned not null, avgcoin smallint(4) unsigned not null default 0); + +create table loottable_entries (loottable_id int(11) unsigned not null, lootdrop_id int(11) unsigned not null, +multiplier tinyint(2) unsigned default 1 not null, probability tinyint(2) unsigned default 100 not null, +primary key (loottable_id, lootdrop_id)); + +create table lootdrop (id int(11) unsigned auto_increment primary key, name varchar(255) not null unique); + +create table lootdrop_entries (lootdrop_id int(11) unsigned not null, item_id int(11) not null, +item_charges tinyint(2) default 1 not null, equip_item tinyint(2) unsigned not null, +chance tinyint(2) unsigned default 1 not null, primary key (lootdrop_id, item_id)); + +ALTER TABLE `loottable_entries` ADD `probability` FLOAT NOT NULL DEFAULT '100'; +*/ + + +#ifndef LOOTTABLE_H +#define LOOTTABLE_H +#include "zonedump.h" +#include "../common/linked_list.h" + +#include +using namespace std; + +#pragma pack(1) +struct LootTableEntries_Struct { + uint32 lootdrop_id; + uint8 droplimit; + uint8 mindrop; + uint8 multiplier; + float probability; +}; + +struct LootTable_Struct { + uint32 mincash; + uint32 maxcash; + uint32 avgcoin; + uint32 NumEntries; + LootTableEntries_Struct Entries[0]; +}; + +struct LootDropEntries_Struct { + uint32 item_id; + int8 item_charges; + uint8 equip_item; + float chance; + uint8 minlevel; + uint8 maxlevel; + uint8 multiplier; +}; + +struct LootDrop_Struct { + uint32 NumEntries; + LootDropEntries_Struct Entries[0]; +}; +#pragma pack() + +typedef list ItemList; + +#endif diff --git a/zone/loottables.cpp b/zone/loottables.cpp new file mode 100644 index 000000000..db321d27a --- /dev/null +++ b/zone/loottables.cpp @@ -0,0 +1,606 @@ +/* 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 "../common/debug.h" +#include +#include +using namespace std; +#include +#include "npc.h" +#include "masterentity.h" +#include "zonedb.h" +#include "../common/MiscFunctions.h" +#ifdef _WINDOWS +#define snprintf _snprintf +#endif + +#include "../common/EMuShareMem.h" +extern LoadEMuShareMemDLL EMuShareMemDLL; +bool SharedDatabase::extDBLoadLoot() { + return s_usedb->DBLoadLoot(); +} + +bool SharedDatabase::LoadLoot() { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint32 tmpLootTableCount = 0; + uint32 tmpLootTableEntriesCount = 0; + uint32 tmpLootDropCount = 0; + uint32 tmpLootDropEntriesCount = 0; + if (RunQuery(query, MakeAnyLenString(&query, "SELECT max(id), count(*) FROM loottable"), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + if (row[0]) + loottable_max = atoi(row[0]); + else + loottable_max = 0; + tmpLootTableCount = atoi(row[1]); + } + else { + mysql_free_result(result); + return false; + } + mysql_free_result(result); + } + else { + cerr << "Error in LoadLoot query, loottable part: '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(*) FROM loottable_entries"), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + tmpLootTableEntriesCount = atoi(row[0]); + } + else { + mysql_free_result(result); + return false; + } + mysql_free_result(result); + } + else { + cerr << "Error in LoadLoot query, loottable2 part: '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT max(id), count(*) FROM lootdrop"), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + if (row[0]) + lootdrop_max = atoi(row[0]); + else + lootdrop_max = 0; + tmpLootDropCount = atoi(row[1]); + } + else { + mysql_free_result(result); + return false; + } + mysql_free_result(result); + } + else { + cerr << "Error in LoadLoot query, lootdrop1 part: '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + if (RunQuery(query, MakeAnyLenString(&query, "SELECT max(lootdrop_id), count(*) FROM lootdrop_entries"), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + tmpLootDropEntriesCount = atoi(row[1]); + } + else { + mysql_free_result(result); + return false; + } + mysql_free_result(result); + } + else { + cerr << "Error in LoadLoot query, lootdrop part: '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + return EMuShareMemDLL.Loot.DLLLoadLoot(&extDBLoadLoot, + sizeof(LootTable_Struct), tmpLootTableCount, loottable_max, + sizeof(LootTableEntries_Struct), tmpLootTableEntriesCount, + sizeof(LootDrop_Struct), tmpLootDropCount, lootdrop_max, + sizeof(LootDropEntries_Struct), tmpLootDropEntriesCount); +} + +bool SharedDatabase::DBLoadLoot() { + LogFile->write(EQEMuLog::Status, "Loading Loot tables from database..."); + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + MYSQL_RES *result2; + uint32 i, tmpid = 0, tmpmincash = 0, tmpmaxcash = 0, tmpavgcoin = 0; + if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, mincash, maxcash, avgcoin FROM loottable"), errbuf, &result)) { + safe_delete_array(query); + LootTable_Struct* tmpLT = 0; + while ((row = mysql_fetch_row(result))) { + tmpid = atoi(row[0]); + tmpmincash = atoi(row[1]); + tmpmaxcash = atoi(row[2]); + tmpavgcoin = atoi(row[3]); + if (RunQuery(query, MakeAnyLenString(&query, "SELECT loottable_id, lootdrop_id, droplimit, mindrop, multiplier, probability FROM loottable_entries WHERE loottable_id=%i", tmpid), errbuf, &result2)) { + safe_delete_array(query); + tmpLT = (LootTable_Struct*) new uchar[sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * mysql_num_rows(result2))]; + memset(tmpLT, 0, sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * mysql_num_rows(result2))); + tmpLT->NumEntries = mysql_num_rows(result2); + tmpLT->mincash = tmpmincash; + tmpLT->maxcash = tmpmaxcash; + tmpLT->avgcoin = tmpavgcoin; + i=0; + while ((row = mysql_fetch_row(result2))) { + if (i >= tmpLT->NumEntries) { + mysql_free_result(result); + mysql_free_result(result2); + safe_delete_array(tmpLT); + cerr << "Error in ZoneDatabase::DBLoadLoot, i >= NumEntries" << endl; + return false; + } + tmpLT->Entries[i].lootdrop_id = atoi(row[1]); + tmpLT->Entries[i].droplimit = atoi(row[2]); + tmpLT->Entries[i].mindrop = atoi(row[3]); + tmpLT->Entries[i].multiplier = atoi(row[4]); + tmpLT->Entries[i].probability = atof(row[5]); + i++; + } + if (!EMuShareMemDLL.Loot.cbAddLootTable(tmpid, tmpLT)) { + mysql_free_result(result); + mysql_free_result(result2); + safe_delete_array(tmpLT); + cout << "Error in ZoneDatabase::DBLoadLoot: !cbAddLootTable(" << tmpid << ")" << endl; + return false; + } + safe_delete_array(tmpLT); + mysql_free_result(result2); + } + else { + mysql_free_result(result); + cerr << "Error in LoadLoot (memshare) #1 query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + } + mysql_free_result(result); + } + else { + cerr << "Error in LoadLoot (memshare) #2 query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM lootdrop", tmpid), errbuf, &result)) { + safe_delete_array(query); + LootDrop_Struct* tmpLD = 0; + while ((row = mysql_fetch_row(result))) { + tmpid = atoi(row[0]); + if (RunQuery(query, MakeAnyLenString(&query, "SELECT lootdrop_id, item_id, item_charges, equip_item, chance, minlevel, maxlevel, multiplier FROM lootdrop_entries WHERE lootdrop_id=%i order by chance desc", tmpid), errbuf, &result2)) { + safe_delete_array(query); + tmpLD = (LootDrop_Struct*) new uchar[sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * mysql_num_rows(result2))]; + memset(tmpLD, 0, sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * mysql_num_rows(result2))); + tmpLD->NumEntries = mysql_num_rows(result2); + i=0; + while ((row = mysql_fetch_row(result2))) { + if (i >= tmpLD->NumEntries) { + mysql_free_result(result); + mysql_free_result(result2); + safe_delete_array(tmpLD); + cerr << "Error in ZoneDatabase::DBLoadLoot, i >= NumEntries" << endl; + return false; + } + tmpLD->Entries[i].item_id = atoi(row[1]); + tmpLD->Entries[i].item_charges = atoi(row[2]); + tmpLD->Entries[i].equip_item = atoi(row[3]); + tmpLD->Entries[i].chance = atof(row[4]); + tmpLD->Entries[i].minlevel = atoi(row[5]); + tmpLD->Entries[i].maxlevel = atoi(row[6]); + tmpLD->Entries[i].multiplier = atoi(row[7]); + i++; + } + if (!EMuShareMemDLL.Loot.cbAddLootDrop(tmpid, tmpLD)) { + mysql_free_result(result); + mysql_free_result(result2); + safe_delete_array(tmpLD); + cout << "Error in ZoneDatabase::DBLoadLoot: !cbAddLootDrop(" << tmpid << ")" << endl; + return false; + } + safe_delete_array(tmpLD); + mysql_free_result(result2); + } + else { + cerr << "Error in LoadLoot (memshare) #3 query '" << query << "' " << errbuf << endl; + mysql_free_result(result); + safe_delete_array(query); + return false; + } + } + mysql_free_result(result); + } + else { + cerr << "Error in LoadLoot (memshare) #4 query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + return true; +} + +const LootTable_Struct* SharedDatabase::GetLootTable(uint32 loottable_id) { + return EMuShareMemDLL.Loot.GetLootTable(loottable_id); +} + +const LootDrop_Struct* SharedDatabase::GetLootDrop(uint32 lootdrop_id) { + return EMuShareMemDLL.Loot.GetLootDrop(lootdrop_id); +} + +// Queries the loottable: adds item & coin to the npc +void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat) { + _ZP(Database_AddLootTableToNPC); +//if (loottable_id == 178190) +//DebugBreak(); + const LootTable_Struct* lts = 0; + *copper = 0; + *silver = 0; + *gold = 0; + *plat = 0; + + lts = database.GetLootTable(loottable_id); + if (!lts) + return; + + // do coin + if (lts->mincash > lts->maxcash) { + cerr << "Error in loottable #" << loottable_id << ": mincash > maxcash" << endl; + } + else if (lts->maxcash != 0) { + uint32 cash = 0; + if (lts->mincash == lts->maxcash) + cash = lts->mincash; + else + cash = MakeRandomInt(lts->mincash, lts->maxcash); + if (cash != 0) { + if (lts->avgcoin != 0) { + //this is some crazy ass stuff... and makes very little sense... dont use it, k? + uint32 mincoin = (uint32) (lts->avgcoin * 0.75 + 1); + uint32 maxcoin = (uint32) (lts->avgcoin * 1.25 + 1); + *copper = MakeRandomInt(mincoin, maxcoin); + *silver = MakeRandomInt(mincoin, maxcoin); + *gold = MakeRandomInt(mincoin, maxcoin); + if(*copper > cash) { *copper = cash; } + cash -= *copper; + if(*silver>(cash/10)) { *silver = (cash/10); } + cash -= *silver*10; + if(*gold > (cash/100)) { *gold = (cash/100); } + cash -= *gold*100; + } + if (cash < 0) { + cash = 0; + } + *plat = cash / 1000; + cash -= *plat * 1000; + uint32 gold2 = cash / 100; + cash -= gold2 * 100; + uint32 silver2 = cash / 10; + cash -= silver2 * 10; + *gold += gold2; + *silver += silver2; + *copper += cash; + } + } + + // Do items + for (uint32 i=0; iNumEntries; i++) { + for (uint32 k = 1; k <= lts->Entries[i].multiplier; k++) { + uint8 droplimit = lts->Entries[i].droplimit; + uint8 mindrop = lts->Entries[i].mindrop; + + //LootTable Entry probability + float ltchance = 0.0f; + ltchance = lts->Entries[i].probability; + + float drop_chance = 0.0f; + if(ltchance > 0.0 && ltchance < 100.0) { + drop_chance = MakeRandomFloat(0.0, 100.0); + } + + if (ltchance != 0.0 && (ltchance == 100.0 || drop_chance < ltchance)) { + AddLootDropToNPC(npc,lts->Entries[i].lootdrop_id, itemlist, droplimit, mindrop); + } + } + } +} + +// Called by AddLootTableToNPC +// maxdrops = size of the array npcd +void ZoneDatabase::AddLootDropToNPC(NPC* npc,uint32 lootdrop_id, ItemList* itemlist, uint8 droplimit, uint8 mindrop) { + const LootDrop_Struct* lds = GetLootDrop(lootdrop_id); + if (!lds) { + return; + } + if(lds->NumEntries == 0) //nothing possible to add + return; + + // Too long a list needs to be limited. + if(lds->NumEntries > 99 && droplimit < 1) + droplimit = lds->NumEntries/100; + + uint8 limit = 0; + // Start at a random point in itemlist. + uint32 item = MakeRandomInt(0, lds->NumEntries-1); + // Main loop. + for (uint32 i=0; iNumEntries;) + { + //Force the itemlist back to beginning. + if (item > (lds->NumEntries-1)) + item = 0; + + uint8 charges = lds->Entries[item].multiplier; + uint8 pickedcharges = 0; + // Loop to check multipliers. + for (uint32 x=1; x<=charges; x++) + { + // Actual roll. + float thischance = 0.0; + thischance = lds->Entries[item].chance; + + float drop_chance = 0.0; + if(thischance != 100.0) + drop_chance = MakeRandomFloat(0.0, 100.0); + +#if EQDEBUG>=11 + LogFile->write(EQEMuLog::Debug, "Drop chance for npc: %s, this chance:%f, drop roll:%f", npc->GetName(), thischance, drop_chance); +#endif + if (thischance == 100.0 || drop_chance < thischance) + { + uint32 itemid = lds->Entries[item].item_id; + + const Item_Struct* dbitem = GetItem(itemid); + npc->AddLootDrop(dbitem, itemlist, lds->Entries[item].item_charges, lds->Entries[item].minlevel, lds->Entries[item].maxlevel, lds->Entries[item].equip_item, false); + pickedcharges++; + } + } + // Items with multipliers only count as 1 towards the limit. + if(pickedcharges > 0) + limit++; + + // If true, limit reached. + if(limit >= droplimit && droplimit > 0) + break; + + item++; + i++; + + // We didn't reach our minimium, run loop again. + if(i == lds->NumEntries){ + if(limit < mindrop){ + i = 0; + } + } + } // We either ran out of items or reached our limit. +} + +//if itemlist is null, just send wear changes +void NPC::AddLootDrop(const Item_Struct *item2, ItemList* itemlist, int16 charges, uint8 minlevel, uint8 maxlevel, bool equipit, bool wearchange) { + if(item2 == NULL) + return; + + //make sure we are doing something... + if(!itemlist && !wearchange) + return; + + ServerLootItem_Struct* item = new ServerLootItem_Struct; +#if EQDEBUG>=11 + LogFile->write(EQEMuLog::Debug, "Adding drop to npc: %s, Item: %i", GetName(), item2->ID); +#endif + + EQApplicationPacket* outapp = NULL; + WearChange_Struct* wc = NULL; + if(wearchange) { + outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct)); + wc = (WearChange_Struct*)outapp->pBuffer; + wc->spawn_id = GetID(); + wc->material=0; + } + + 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; + if (equipit) { + uint8 eslot = 0xFF; + char newid[20]; + const Item_Struct* compitem = NULL; + bool found = false; // track if we found an empty slot we fit into + int32 foundslot = -1; // for multi-slot items + + // Equip rules are as follows: + // If the item has the NoPet flag set it will not be equipped. + // An empty slot takes priority. The first empty one that an item can + // fit into will be the one picked for the item. + // AC is the primary choice for which item gets picked for a slot. + // If AC is identical HP is considered next. + // If an item can fit into multiple slots we'll pick the last one where + // it is an improvement. + + if (!item2->NoPet) { + for (int i=0; !found && iSlots & slots) { + if(equipment[i]) + { + compitem = database.GetItem(equipment[i]); + if (item2->AC > compitem->AC || + (item2->AC == compitem->AC && item2->HP > compitem->HP)) + { + // item would be an upgrade + // check if we're multi-slot, if yes then we have to keep + // looking in case any of the other slots we can fit into are empty. + if (item2->Slots != slots) { + foundslot = i; + } + else { + equipment[i] = item2->ID; + foundslot = i; + found = true; + } + } // end if ac + } + else + { + equipment[i] = item2->ID; + foundslot = i; + found = true; + } + } // end if (slots) + } // end for + } // end if NoPet + + // Possible slot was found but not selected. Pick it now. + if (!found && foundslot >= 0) { + equipment[foundslot] = item2->ID; + found = true; + } + + // @merth: IDFile size has been increased, this needs to change + uint16 emat; + if(item2->Material <= 0 + || item2->Slots & (1 << SLOT_PRIMARY | 1 << SLOT_SECONDARY)) { + memset(newid, 0, sizeof(newid)); + for(int i=0;i<7;i++){ + if (!isalpha(item2->IDFile[i])){ + strn0cpy(newid, &item2->IDFile[i],6); + i=8; + } + } + + emat = atoi(newid); + } else { + emat = item2->Material; + } + + if (foundslot == SLOT_PRIMARY) { + if (item2->Proc.Effect != 0) + CastToMob()->AddProcToWeapon(item2->Proc.Effect, true); + + eslot = MATERIAL_PRIMARY; + } + else if (foundslot == SLOT_SECONDARY + && (GetOwner() != NULL || (GetLevel() >= 13 && MakeRandomInt(0,99) < NPC_DW_CHANCE) || (item2->Damage==0)) && + (item2->ItemType == ItemType1HS || item2->ItemType == ItemType1HB || item2->ItemType == ItemTypeShield || + item2->ItemType == ItemTypePierce)) + { + if (item2->Proc.Effect!=0) + CastToMob()->AddProcToWeapon(item2->Proc.Effect, true); + + eslot = MATERIAL_SECONDARY; + } + else if (foundslot == SLOT_HEAD) { + eslot = MATERIAL_HEAD; + } + else if (foundslot == SLOT_CHEST) { + eslot = MATERIAL_CHEST; + } + else if (foundslot == SLOT_ARMS) { + eslot = MATERIAL_ARMS; + } + else if (foundslot == SLOT_BRACER01 || foundslot == SLOT_BRACER02) { + eslot = MATERIAL_BRACER; + } + else if (foundslot == SLOT_HANDS) { + eslot = MATERIAL_HANDS; + } + else if (foundslot == SLOT_LEGS) { + eslot = MATERIAL_LEGS; + } + else if (foundslot == SLOT_FEET) { + eslot = MATERIAL_FEET; + } + + /* + what was this about??? + + if (((npc->GetRace()==127) && (npc->CastToMob()->GetOwnerID()!=0)) && (item2->Slots==24576) || (item2->Slots==8192) || (item2->Slots==16384)){ + npc->d_meele_texture2=atoi(newid); + wc->wear_slot_id=8; + if (item2->Material >0) + wc->material=item2->Material; + else + wc->material=atoi(newid); + npc->AC+=item2->AC; + npc->STR+=item2->STR; + npc->INT+=item2->INT; + } + */ + + //if we found an open slot it goes in... + if(eslot != 0xFF) { + if(wearchange) { + wc->wear_slot_id = eslot; + wc->material = emat; + } + + } + if (found) { + CalcBonuses(); // This is less than ideal for bulk adding of items + } + item->equipSlot = item2->Slots; + } + + if(itemlist != NULL) + itemlist->push_back(item); + else + safe_delete(item); + + if(wearchange && outapp) { + entity_list.QueueClients(this, outapp); + safe_delete(outapp); + } +} + +void NPC::AddItem(const Item_Struct* item, uint16 charges, bool equipitem) { + //slot isnt needed, its determined from the item. + AddLootDrop(item, &itemlist, charges, 1, 127, equipitem, equipitem); +} + +void NPC::AddItem(uint32 itemid, uint16 charges, bool equipitem) { + //slot isnt needed, its determined from the item. + const Item_Struct * i = database.GetItem(itemid); + if(i == NULL) + return; + AddLootDrop(i, &itemlist, charges, 1, 127, equipitem, equipitem); +} + +void NPC::AddLootTable() { + if (npctype_id != 0) { // check if it's a GM spawn + database.AddLootTableToNPC(this,loottable_id, &itemlist, &copper, &silver, &gold, &platinum); + } +} + diff --git a/zone/map.h b/zone/map.h new file mode 100644 index 000000000..93abb051a --- /dev/null +++ b/zone/map.h @@ -0,0 +1,190 @@ +/* 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 MAP_H +#define MAP_H + +#include + +//this is the current version number to expect from the map header +#define MAP_VERSION 0x01000000 + +#define BEST_Z_INVALID -999999 + +#pragma pack(1) + +typedef struct _vertex{ +// unsigned long order; + float x; + float y; + float z; + + _vertex() + { + x = y = z = 0.0f; + }; + + _vertex(float ix, float iy, float iz) + { + x = ix; + y = iy; + z = iz; + } + bool operator==(const _vertex &v1) const + { + return((v1.x == x) && (v1.y == y) && (v1.z ==z)); + } + +}VERTEX, *PVERTEX; + +typedef struct _face{ +// unsigned long a, b, c; //vertexs + VERTEX a; + VERTEX b; + VERTEX c; + float nx, ny, nz, nd; +}FACE, *PFACE; + +typedef struct _mapHeader { + uint32 version; + uint32 face_count; + uint16 node_count; + uint32 facelist_count; +} mapHeader; + +/* + This is used for the recursive node structure + unsigned shorts are adequate because, worst case + even in a zone that is 6000x6000 with a small node + size of 30x30, there are only 40000 nodes. + + quadrent definitions: + quad 1 (nodes[0]): + x>=0, y>=0 + quad 2 (nodes[1]): + x<0, y>=0 + quad 3 (nodes[2]): + x<0, y<0 + quad 4 (nodes[3]): + x>=0, y<0 + + */ +enum { //node flags + nodeFinal = 0x01 + //7 more bits if theres something to use them for... +}; +typedef struct _nodeHeader { + //bounding box of this node + //there is no reason that these could not be unsigned + //shorts other than we have to compare them to floats + //all over the place, so they stay floats for now. + //changing it could save 8 bytes per node record (~320k for huge maps) + float minx; + float miny; + float maxx; + float maxy; + + uint8 flags; + union { + uint16 nodes[4]; //index 0 means NULL, not root + struct { + uint32 count; + uint32 offset; + } faces; + }; +} nodeHeader, NODE, *PNODE; + +#pragma pack() + +//special value returned as 'not found' +#define NODE_NONE 65534 +#define MAP_ROOT_NODE 0 + +typedef uint16 NodeRef; + +/*typedef struct _node { + nodeHeader head; + unsigned int * pfaces; + char mask; + struct _node * node1, *node2, *node3, *node4; +}NODE, *PNODE;*/ + +class Map { +public: + static Map* LoadMapfile(const char* in_zonename, const char *directory = NULL); + + Map(); + ~Map(); + + bool loadMap(FILE *fp); + + //the result is always final, except special NODE_NONE + NodeRef SeekNode( NodeRef _node, float x, float y ) const; + + //these are untested since rewrite: + int *SeekFace( NodeRef _node, float x, float y ); + float GetFaceHeight( int _idx, float x, float y ) const; + + bool LocWithinNode( NodeRef _node, float x, float y ) const; + + //nodes to these functions must be final + bool LineIntersectsNode( NodeRef _node, VERTEX start, VERTEX end, VERTEX *result, FACE **on = NULL) const; + bool LineIntersectsFace( PFACE cface, VERTEX start, VERTEX end, VERTEX *result) const; + float FindBestZ( NodeRef _node, VERTEX start, VERTEX *result, FACE **on = NULL) const; + bool LineIntersectsZone(VERTEX start, VERTEX end, float step, VERTEX *result, FACE **on = NULL) const; + +// inline unsigned int GetVertexNumber( ) {return m_Vertex; } + inline uint32 GetFacesNumber( ) const { return m_Faces; } +// inline PVERTEX GetVertex( int _idx ) {return mFinalVertex + _idx; } + inline PFACE GetFace( int _idx) {return mFinalFaces + _idx; } + inline PFACE GetFaceFromlist( int _idx) {return &mFinalFaces[ mFaceLists[_idx] ]; } + inline NodeRef GetRoot( ) const { return MAP_ROOT_NODE; } + inline PNODE GetNode( NodeRef r ) { return( mNodes + r ); } + + inline float GetMinX() const { return(_minx); } + inline float GetMaxX() const { return(_maxx); } + inline float GetMinY() const { return(_miny); } + inline float GetMaxY() const { return(_maxy); } + inline float GetMinZ() const { return(_minz); } + inline float GetMaxZ() const { return(_maxz); } + bool LineIntersectsZoneNoZLeaps(VERTEX start, VERTEX end, float step_mag, VERTEX *result, FACE **on); + float FindClosestZ(VERTEX p ) const; + +private: +// unsigned long m_Vertex; + uint32 m_Faces; + uint32 m_Nodes; + uint32 m_FaceLists; +// PVERTEX mFinalVertex; + PFACE mFinalFaces; + PNODE mNodes; + uint32 *mFaceLists; + + + int mCandFaces[100]; + + float _minz, _maxz; + float _minx, _miny, _maxx, _maxy; + + static void Normalize(VERTEX *p); + +// void RecLoadNode( PNODE _node, FILE *l_f ); +// void RecFreeNode( PNODE _node ); +}; + +#endif + diff --git a/zone/masterentity.h b/zone/masterentity.h new file mode 100644 index 000000000..0a8372dea --- /dev/null +++ b/zone/masterentity.h @@ -0,0 +1,19 @@ +//Trumpcard: EntityLists are composed of multiple list types. This is the +//master that includes all types. When entity.h is required, many of these are as well. + +#include "entity.h" +#include "groups.h" +#include "raids.h" +#include "client.h" +#include "object.h" +#include "PlayerCorpse.h" +#include "doors.h" +#include "mob.h" +#include "trap.h" +#include "beacon.h" +#include "horse.h" + +#ifdef BOTS +#include "bot.h" +#endif + diff --git a/zone/maxskill.h b/zone/maxskill.h new file mode 100644 index 000000000..446b79c2e --- /dev/null +++ b/zone/maxskill.h @@ -0,0 +1,2338 @@ +uint16 Mob::MaxSkill_weapon(uint16 skillid, uint16 class_, uint16 level) const{ + if (skillid > HIGHEST_SKILL) + return 0; + uint16 r_value = 0; + switch(skillid) { + case _1H_BLUNT: + case _2H_BLUNT: + case PIERCING: + case HAND_TO_HAND: + case _1H_SLASHING: + case _2H_SLASHING:{ + switch (class_) { + // Pure melee classes + case WARRIOR: case WARRIORGM:{ + r_value = 5 + (level*5); + if ( level < 51 && r_value > 200) + r_value = 200; + if ( level > 50 && 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; + 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 && r_value > 250 ) + r_value = 250; + if ( level < 51 ){ + if ( r_value > 200 && skillid != PIERCING ) + r_value = 200; + if ( r_value > 210 && skillid == PIERCING ) + r_value = 210; + } + if (skillid == HAND_TO_HAND && r_value > 100) + r_value = 100; + break; + } + case BERSERKER: case BERSERKERGM:{ + r_value = 5 + (level*5); + if ( level < 51 && r_value > 240) + r_value = 240; + switch (skillid) { + case HAND_TO_HAND:{ + if ( r_value > 198) + r_value = 198; + break; + } + case PIERCING:{ + if ( r_value > 240) + r_value = 240; + break; + } + case _2H_BLUNT: + case _2H_SLASHING:{ + if ( r_value > 252 ) + r_value = 252; + break; + } + default: + r_value = 0; + break; + } + break; + } + // 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; + } + // 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 && 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; + } + 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 ) + r_value = 225; + } + if ( level < 51 && r_value > 200 ) + r_value = 200; + switch (skillid) { + case HAND_TO_HAND:{ + r_value = 5 + (level*5); // Beastlords use different max skill formula only for h2h 200/250 + if ( level < 51 ) + r_value = 200; + break; + } + case _1H_SLASHING: + case _2H_SLASHING:{ + r_value = 0; + break; + } + default: break; + } + if ( r_value > 250 ) + r_value = 250; + break; + } + // 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; + } + case _1H_SLASHING: + case _2H_SLASHING:{ + r_value = 0; + break; + } + default: break; + } + break; + } + default: +#if EQDEBUG + cout<<"MaxSkill_Weapon() Unknown class: "< 252) + r_value = 252; + return r_value; +} + +uint16 Mob::MaxSkill_offensive(uint16 skillid, uint16 class_, uint16 level) const{ + uint16 r_value = 0; + switch(skillid) { + + case OFFENSE:{ + switch (class_) { + // Melee + case WARRIOR: case WARRIORGM: + case BERSERKER: case BERSERKERGM: + 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: break; + } + break; + } + case THROWING:{ + switch (class_) { + // Melee + case BERSERKER: case BERSERKERGM: + 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; + } + break; + } +///////////////////////////////////////////////// + case ARCHERY:{ + switch (class_) { + // 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; + } + break; + } +///////////////////////////////////////////////// + case DOUBLE_ATTACK:{ + switch (class_) { + // 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 BERSERKER: case BERSERKERGM: + 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; + } + break; + } +///////////////////////////////////////////////// + case DUAL_WIELD: { + switch (class_) { + // 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 < 13 ) + 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 DUAL_WIELD: +//////////////////////////////////////////////////////// + case KICK:{ + switch (class_) { + // Melee + case BERSERKER: case BERSERKERGM: + 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; + } + break; + } +//////////////////////////////////////////////////////// + case BASH:{ + r_value = ((level*5)+5); + switch (class_) { + // 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; + //} + r_value = 0; + break; + } + } + break; + } +//////////////////////////////////////////////////////// + default: +#if EQDEBUG >= 1 + cout<<"Unknown Offensive skill: "< 252) + r_value = 252; + return r_value; +} + +uint16 Mob::MaxSkill_defensive(uint16 skillid, uint16 class_, uint16 level) const{ + uint16 r_value = 0; + switch(skillid) { + case DEFENSE:{ + switch (class_) { + // 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; + } + case BERSERKER: case BERSERKERGM:{ + // 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; + } + break; + } + case PARRY:{ + switch (class_) { + // 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; + } + case BERSERKER: case BERSERKERGM:{ + r_value = ((level*5) + 5); + if ( level < 10 ) + r_value = 0; + if (r_value > 175) + r_value = 175; + 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; + } + break; + } + case RIPOSTE:{ + switch (class_) { + // Melee + case BERSERKER: case BERSERKERGM: + 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; + + } + break; + } + case DODGE:{ + switch (class_) { + // Melee + case BERSERKER: case BERSERKERGM: + 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: break; + } + break; + } + // Other + case TAUNT:{ + switch (class_) { + // 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: break; + } + break; + } + case DISARM:{ + switch (class_) { + // 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; + } + case BERSERKER: case BERSERKERGM:{ + // 35 65 65 + r_value = ((level*5) + 5); + if (level < 35) + r_value = 0; + if (r_value > 65) + r_value = 65; + 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: break; + } + break; + } +//////////////////////////////////////////////////////// + + default: +#if EQDEBUG + cout<<"Unknown Defensive skill: "< 252) + r_value = 252; + return r_value; +} + +uint16 Mob::MaxSkill_arcane(uint16 skillid, uint16 class_, uint16 level) const{ + uint16 r_value = 0; + switch(skillid) { + case MEDITATE: + case ABJURE: + case ALTERATION: + case CHANNELING: + case CONJURATION: + case DIVINATION: + case EVOCATION:{ + r_value = ((level*5) + 5); + switch(class_){ + // 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; + } + + case SPECIALIZE_ABJURE: + case SPECIALIZE_ALTERATION: + case SPECIALIZE_CONJURATION: + case SPECIALIZE_DIVINATION: + case SPECIALIZE_EVOCATION: + { + r_value = ((level*5) + 5); + switch(class_){ + // Non-int casters + case CLERIC: case CLERICGM: + case DRUID: case DRUIDGM: + case SHAMAN: case SHAMANGM: + if(level < 30) { + r_value = 0; + break; + } + + // Int caster + case ENCHANTER: case ENCHANTERGM: + case MAGICIAN: case MAGICIANGM: + case NECROMANCER: case NECROMANCERGM: + case WIZARD: case WIZARDGM:{ + if (level < 20) { + r_value = 0; + break; + } + + //make sure only 1 skill can be over 50 + uint16 hskill = 0; + uint16 high = 0; + uint16 cur; + cur = GetSkill(SPECIALIZE_ABJURE); + if(cur > high) { + hskill = SPECIALIZE_ABJURE; + high = cur; + } + cur = GetSkill(SPECIALIZE_ALTERATION); + if(cur > high) { + hskill = SPECIALIZE_ALTERATION; + high = cur; + } + cur = GetSkill(SPECIALIZE_CONJURATION); + if(cur > high) { + hskill = SPECIALIZE_CONJURATION; + high = cur; + } + cur = GetSkill(SPECIALIZE_DIVINATION); + if(cur > high) { + hskill = SPECIALIZE_DIVINATION; + high = cur; + } + cur = GetSkill(SPECIALIZE_EVOCATION); + if(cur > high) { + hskill = SPECIALIZE_EVOCATION; + high = cur; + } + if(high > 50 && hskill != skillid) { + r_value = 50; + break; + } + + if (r_value > 200) + r_value = 200; + break; + } + default:{ + r_value = 0; + break; + } + }// Class Switch + break; + } + case RESEARCH:{ + r_value = ((level*5) + 5); + switch(class_){ + // 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) + r_value = 0; + if (r_value > 200) + r_value = 200; + // FIXME Only let one SPEC go above what ever limit theres supposed to be + break; + } + default:{ + r_value = 0; + break; + } + }// Class Switch + break; + } + + case BRASS_INSTRUMENTS: + case SINGING: + case STRINGED_INSTRUMENTS: + case WIND_INSTRUMENTS: + case PERCUSSION_INSTRUMENTS:{ + switch(class_){ + 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; + } +//////////////////////////////////////////////////////// + default: +#if EQDEBUG + cout<<"Unknown arcane skill: "< 252) + r_value = 252; + return r_value; +} + +uint16 Mob::MaxSkill_class(uint16 skillid, uint16 class_, uint16 level) const{ + uint16 r_value = 0; + switch(skillid) { + // Rogue + case APPLY_POISON: + case MAKE_POISON: + case PICK_POCKETS: + case BACKSTAB:{ + switch (class_) { + // 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; + } + break; + } + default: + r_value = 0; + break; + }// Class Switch + break; + } + // Monk + case BLOCKSKILL: { + switch(class_){ + case BEASTLORD: case BEASTLORDGM:{ + r_value = (((level-25)*5) + 5); + // 12 200 230 + if (level < 25) + r_value = 0; + if (level < 51 && r_value > 200) + r_value = 200; + if (r_value > 230) + r_value = 230; + break; + } + case MONK: case MONKGM:{ + r_value = ((level*5) + 5); + // 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; + } + } + break; + } + case FEIGN_DEATH: + case MEND: + case DRAGON_PUNCH: + case EAGLE_STRIKE: + case FLYING_KICK: + case ROUND_KICK: + case TIGER_CLAW:{ + switch(class_){ + 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 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; + } + break; + } + default: + r_value = 0; + break; + }// Class Switch + break; + } + + //Berzerkers + case BERSERKING: { + switch(class_){ + case BERSERKER: case BERSERKERGM: { + r_value = ((level*5) + 5); + if(r_value > 200) + r_value = 200; + } + default: + r_value = 0; + break; + } + break; + } + + // Shaman + case ALCHEMY:{ + switch(class_){ + 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; + } +/////////////////////////////////////////// +////////////////////////////////////////// +// Shared skill + // Shared Rogue + case HIDE:{ + switch(class_){ + // True class + case ROGUE: case ROGUEGM:{ + r_value = ((level*5) + 5); + if(r_value > 200) + r_value = 200; + break; + } + // Hybrids + case RANGER: case RANGERGM: + case SHADOWKNIGHT: case SHADOWKNIGHTGM:{ //75 cap + if(level >= 35) { + r_value = (((level-35)*5) + 5); + if(r_value > 75) + r_value = 75; + } + break; + } + case BARD: case BARDGM:{ //40 cap + if(level > 25) { + r_value = (((level-25)*5) + 5); + if(r_value > 40) + r_value = 40; + } + break; + } + default: + r_value = 0; + break; + }// Class Switch + break; + } + + case SNEAK:{ + switch(class_){ + // True class + case ROGUE: case ROGUEGM:{ + r_value = ((level*5) + 5); + if(r_value > 200) + r_value = 200; + break; + } + // Hybrids + case MONK: case MONKGM:{ //113 cap + if(level >= 8) { + r_value = (((level-8)*5) + 5); + if(r_value > 113) + r_value = 113; + } + break; + } + case RANGER: case RANGERGM:{ //75 cap + if(level >= 10) { + r_value = (((level-10)*5) + 5); + if(r_value > 75) + r_value = 75; + } + break; + } + case BARD: case BARDGM:{ //75 cap + if(level >= 17) { + r_value = (((level-17)*5) + 5); + if(r_value > 75) + r_value = 75; + } + break; + } + case BEASTLORD: case BEASTLORDGM:{ //50 cap + if(level >= 50) { + r_value = (((level-50)*5) + 5); + if(r_value > 50) + r_value = 50; + } + break; + } + default: + r_value = 0; + break; + }// Class Switch + break; + } + + case SENSE_TRAPS: + case PICK_LOCK: + case DISARM_TRAPS:{ + switch(class_){ + // True class + case ROGUE: case ROGUEGM:{ + r_value = ((level*5) + 5); + if(r_value > 200) + r_value = 200; + break; + } + // Hybrids + case BARD: case BARDGM:{ //100 cap + if(level >= 30) { //this is wrong I think... + r_value = (((level-30)*5) + 5); + if(r_value > 100) + r_value = 100; + } + break; + } + default: + r_value = 0; + break; + }// Class Switch + break; + } + case SAFE_FALL:{ + switch(class_){ + // Hybrids + case BARD: case BARDGM:{ //40 cap + if(level >= 24) { + r_value = (((level-24)*5) + 5); + if(r_value > 40) + r_value = 40; + } + break; + } + // Melee + case MONK: case MONKGM:{ + if(level >= 3) { + r_value = (((level-3)*5) + 5); + if(r_value > 200) + r_value = 200; + } + break; + } + case ROGUE: case ROGUEGM:{ //100 cap + if(level >= 12) { + r_value = (((level-12)*5) + 5); + if(r_value > 100) + r_value = 100; + } + break; + } + default: + r_value = 0; + break; + }// Class Switch + break; + } + case INTIMIDATION:{ + switch(class_){ + case BARD: case BARDGM:{ //100 cap + if(level >= 26) { + r_value = (((level-26)*5) + 5); + if(r_value > 100) + r_value = 100; + } + break; + } + // Melee + case MONK: case MONKGM:{ + if(level >= 18) { + r_value = (((level-18)*5) + 5); + if(r_value > 200) + r_value = 200; + } + break; + } + case BERSERKER: case BERSERKERGM:{ + if(level >= 20) { + r_value = (((level-20)*5) + 5); + if(r_value > 200) + r_value = 200; + } + break; + } + case ROGUE: case ROGUEGM:{ + if(level >= 22) { + r_value = (((level-22)*5) + 5); + if(r_value > 200) + r_value = 200; + } + break; + } + default: + r_value = 0; + break; + }// Class Switch + break; + } + // Druid/Ranger/Bard + case FORAGE:{ + switch(class_) { + case RANGER: case RANGERGM:{ + if(level > 3) { + r_value = (((level-3)*5) + 5); + if (r_value > 200) + r_value = 200; + } + break; + } + case DRUID: case DRUIDGM:{ + r_value = ((level*5) + 5); + if (r_value > 200) + r_value = 200; + break; + } + case MONK: case MONKGM: + case BARD: case BARDGM: + r_value = 55; + break; + default: + r_value = 50; + break; + }// Class Switch + break; + } + case TRACKING:{ + switch(class_){ + case RANGER: case RANGERGM: + case BARD: case BARDGM: + case DRUID: case DRUIDGM: + r_value=200; + break; + default: + r_value = 0; + break; + }// Class Switch + break; + } +//////////////////////////////////////////////////////// + default: +#if EQDEBUG + cout<<"Unknown class skill: "< 252) + r_value = 252; + return r_value; +} + +uint16 Mob::MaxSkill(uint16 skillid, uint16 class_, uint16 level) const { + uint16 r_value = 0; + switch (skillid) { + case _1H_BLUNT: + case _2H_BLUNT: + case PIERCING: + case HAND_TO_HAND: + case _1H_SLASHING: + case _2H_SLASHING:{ + r_value = MaxSkill_weapon(skillid, class_, level); + break; + } + case OFFENSE: + case THROWING: + case ARCHERY: + case DOUBLE_ATTACK: + case DUAL_WIELD: + case KICK: + case BASH:{ + r_value = MaxSkill_offensive(skillid, class_, level); + break; + } + case DEFENSE: + case PARRY: + case RIPOSTE: + case DODGE: + case TAUNT: + case DISARM:{ + r_value = MaxSkill_defensive(skillid,class_,level); + break; + } + case MEDITATE: + case ABJURE: + case ALTERATION: + case CHANNELING: + case CONJURATION: + case DIVINATION: + case EVOCATION: + case SPECIALIZE_ABJURE: + case SPECIALIZE_ALTERATION: + case SPECIALIZE_CONJURATION: + case SPECIALIZE_DIVINATION: + case SPECIALIZE_EVOCATION: + case RESEARCH: + case BRASS_INSTRUMENTS: + case SINGING: + case STRINGED_INSTRUMENTS: + case WIND_INSTRUMENTS: + case PERCUSSION_INSTRUMENTS:{ + r_value = MaxSkill_arcane(skillid,class_,level); + break; + } +/////////////////////////////////////////// +/////////////////////////////////////////// +// Class skills + // Rogue + case APPLY_POISON: + case MAKE_POISON: + case PICK_POCKETS: + case BACKSTAB: + // Monk + case FEIGN_DEATH: + case MEND: + case DRAGON_PUNCH: + case EAGLE_STRIKE: + case FLYING_KICK: + case ROUND_KICK: + case TIGER_CLAW: + case BLOCKSKILL: + case ALCHEMY: + case HIDE: + case SNEAK: + case SENSE_TRAPS: + case PICK_LOCK: + case DISARM_TRAPS: + case SAFE_FALL: + case INTIMIDATION: + // Druid/Ranger/Bard + case FORAGE: + case TRACKING:{ + r_value = MaxSkill_class(skillid,class_,level); + break; + } +/////////////////////////////////////////// +/////////////////////////////////////////// +// 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 = 250; + break; + } +///////////////////////////////////// +///////////////////////////////////// + // Gnome + case TINKERING:{ + if ( race == GNOME && level > 24 ) { + r_value = ((level*5)+5); + break; + } + r_value = 0; + break; + } + +///////////////////////////////////////// +// Common +///////////////////////////////////////// + case BIND_WOUND:{ + switch(class_){ + case BARD: case BARDGM:{ + r_value = ((level*5)+5); + if(level >= 50) { + if(r_value > 210) + r_value = 210; + } else { + if(r_value > 200) + r_value = 200; + } + break; + } + case CLERIC: case CLERICGM:{ + r_value = ((level*5)+5); + if(level >= 50) { + if(r_value > 201) + r_value = 201; + } else { + if(r_value > 200) + r_value = 200; + } + break; + } + + case DRUID: case DRUIDGM: + case SHAMAN: case SHAMANGM:{ + r_value = ((level*5) + 5); + if(r_value > 200) + r_value = 200; + break; + } + case MAGICIAN: case MAGICIANGM: + case ENCHANTER: case ENCHANTERGM: + case NECROMANCER: case NECROMANCERGM: + case WIZARD: case WIZARDGM:{ + r_value = ((level*5) + 5); + if(r_value > 100) + r_value = 100; + break; + } + case BEASTLORD: case BEASTLORDGM: + case BERSERKER: case BERSERKERGM: + case MONK: case MONKGM: { + r_value = ((level*5)+5); + if(level >= 50) { + if(r_value > 210) + r_value = 210; + } else { + if(r_value > 200) + r_value = 200; + } + break; + } + case PALADIN: case PALADINGM: { + if (level > 10) { + r_value = (((level-10)*5)+5); + if(level >= 50) { + if(r_value > 210) + r_value = 210; + } else { + if(r_value > 200) + r_value = 200; + } + } + break; + } + case RANGER: case RANGERGM: { + if (level > 15) { + r_value = (((level-15)*5)+5); + if(level >= 50) { + if(r_value > 200) + r_value = 200; + } else { + if(r_value > 150) + r_value = 150; + } + } + break; + } + + case ROGUE: case ROGUEGM: { + r_value = ((level*5)+5); + if(level >= 50) { + if(r_value > 210) + r_value = 210; + } else { + if(r_value > 176) + r_value = 176; + } + break; + } + case SHADOWKNIGHT: case SHADOWKNIGHTGM: { + r_value = ((level*5)+5); + if(level >= 50) { + if(r_value > 200) + r_value = 200; + } else { + if(r_value > 150) + r_value = 150; + } + break; + } + case WARRIOR: case WARRIORGM: { + if (level > 5) { + r_value = (((level-5)*5)+5); + if(level >= 50) { + if(r_value > 210) + r_value = 210; + } else { + if(r_value > 175) + r_value = 175; + } + } + break; + } + + default: r_value = 0; + break; + } + break; + } + 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; + } + + } + if (r_value >= 253) + r_value = 252; + return r_value; +} + diff --git a/zone/merc.cpp b/zone/merc.cpp new file mode 100644 index 000000000..1c168885b --- /dev/null +++ b/zone/merc.cpp @@ -0,0 +1,4507 @@ +#include "merc.h" +#include "masterentity.h" +#include "NpcAI.h" +#include "../common/packet_dump.h" +#include "../common/eq_packet_structs.h" +#include "../common/eq_constants.h" +#include "../common/skills.h" +#include "spdat.h" +#include "zone.h" +#include "StringIDs.h" +#include "../common/MiscFunctions.h" +#include "../common/rulesys.h" +#include "QuestParserCollection.h" +#include "watermap.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) +{ + _baseAC = d->AC; + _baseSTR = d->STR; + _baseSTA = d->STA; + _baseDEX = d->DEX; + _baseAGI = d->AGI; + _baseINT = d->INT; + _baseWIS = d->WIS; + _baseCHA = d->CHA; + _baseATK = d->ATK; + _baseRace = d->race; + _baseGender = d->gender; + _baseMR = d->MR; + _baseCR = d->CR; + _baseDR = d->DR; + _baseFR = d->FR; + _basePR = d->PR; + _baseCorrup = d->Corrup; + + _medding = false; + _suspended = false; + p_depop = false; + + ourNPCData = d; + + LoadMercSpells(); + SetStance(MercStancePassive); + rest_timer.Disable(); + + int r; + for(r = 0; r <= HIGHEST_SKILL; r++) { + skills[r] = database.GetSkillCap(GetClass(),(SkillType)r,GetLevel()); + } + + CalcBonuses(); + + SetHP(GetMaxHP()); + SetMana(GetMaxMana()); + SetEndurance(GetMaxEndurance()); + + AI_Init(); + AI_Start(); +} + +Merc::~Merc() { + safe_delete(ourNPCData); //Since mercs are dynamically alloc'd we should probably safe_delete the data they were made from. I'm not entirely sure this is safe to delete a const. + entity_list.RemoveMerc(this->GetID()); + UninitializeBuffSlots(); +} + +void Merc::CalcBonuses() +{ + //_ZP(Merc_CalcBonuses); + GenerateBaseStats(); + memset(&itembonuses, 0, sizeof(StatBonuses)); + memset(&aabonuses, 0, sizeof(StatBonuses)); + CalcItemBonuses(&itembonuses); + + CalcSpellBonuses(&spellbonuses); + + //_log(AA__BONUSES, "Calculating AA Bonuses for %s.", this->GetCleanName()); + //CalcAABonuses(&aabonuses); //we're not quite ready for this + //_log(AA__BONUSES, "Finished calculating AA Bonuses for %s.", this->GetCleanName()); + + CalcAC(); + CalcATK(); + //CalcHaste(); + + CalcSTR(); + CalcSTA(); + CalcDEX(); + CalcAGI(); + CalcINT(); + CalcWIS(); + CalcCHA(); + + CalcMR(); + CalcFR(); + CalcDR(); + CalcPR(); + CalcCR(); + CalcCorrup(); + + CalcMaxHP(); + CalcMaxMana(); + CalcMaxEndurance(); + + rooted = FindType(SE_Root); +} + +void Merc::GenerateBaseStats() { + + // 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; + uint16 MagicResist = _baseMR; + uint16 FireResist = _baseFR; + uint16 DiseaseResist = _baseDR; + uint16 PoisonResist = _basePR; + uint16 ColdResist = _baseCR; + uint16 CorruptionResist = _baseCorrup; + + switch(this->GetClass()) { + case 1: // Warrior + Strength += 10; + Stamina += 20; + Agility += 10; + Dexterity += 10; + Attack += 12; + break; + case 2: // Cleric + Strength += 5; + Stamina += 5; + Agility += 10; + Wisdom += 30; + Attack += 8; + break; + case 4: // Ranger + Strength += 15; + Stamina += 10; + Agility += 10; + Wisdom += 15; + Attack += 17; + break; + case 9: // Rogue + Strength += 10; + Stamina += 20; + Agility += 10; + Dexterity += 10; + Attack += 12; + break; + case 12: // Wizard + Stamina += 20; + Intelligence += 30; + Attack += 5; + break; + } + + 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; + } + + this->_baseSTR = Strength; + this->_baseSTA = Stamina; + this->_baseDEX = Dexterity; + this->_baseAGI = Agility; + this->_baseWIS = Wisdom; + this->_baseINT = Intelligence; + this->_baseCHA = Charisma; + this->_baseATK = Attack; + this->_baseMR = MagicResist; + this->_baseFR = FireResist; + this->_baseDR = DiseaseResist; + this->_basePR = PoisonResist; + this->_baseCR = ColdResist; + this->_baseCorrup = CorruptionResist; + 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; +} + +int Merc::CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat) +{ + if( (reclevel > 0) && (level < reclevel) ) + { + int32 statmod = (level * 10000 / reclevel) * basestat; + + if( statmod < 0 ) + { + statmod -= 5000; + return (statmod/10000); + } + else + { + statmod += 5000; + return (statmod/10000); + } + } + + return 0; +} + +void Merc::CalcItemBonuses(StatBonuses* newbon) { + //memset assumed to be done by caller. + + + unsigned int i; + //should not include 21 (SLOT_AMMO) + for (i=0; i= EQClientSoF) + { + const ItemInst* inst = m_inv[9999]; + if(inst) + AddItemBonuses(inst, newbon); + } + + //tribute items + for (i = 0; i < MAX_PLAYER_TRIBUTES; i++) { + const ItemInst* inst = m_inv[TRIBUTE_SLOT_START + i]; + if(inst == 0) + continue; + AddItemBonuses(inst, newbon, false, true); + } + // Caps + if(newbon->HPRegen > CalcHPRegenCap()) + newbon->HPRegen = CalcHPRegenCap(); + + if(newbon->ManaRegen > CalcManaRegenCap()) + newbon->ManaRegen = CalcManaRegenCap(); + + if(newbon->EnduranceRegen > CalcEnduranceRegenCap()) + newbon->EnduranceRegen = CalcEnduranceRegenCap(); + + SetAttackTimer(); +} + +void Merc::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug, bool isTribute) { + if(!inst || !inst->IsType(ItemClassCommon)) + { + return; + } + + if(inst->GetAugmentType()==0 && isAug == true) + { + return; + } + + const Item_Struct *item = inst->GetItem(); + + if(!isTribute && !inst->IsEquipable(GetBaseRace(),GetClass())) + { + if(item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink) + return; + } + + if(GetLevel() < item->ReqLevel) + { + return; + } + + if(GetLevel() >= item->RecLevel) + { + newbon->AC += item->AC; + newbon->HP += item->HP; + newbon->Mana += item->Mana; + newbon->Endurance += item->Endur; + newbon->STR += (item->AStr + item->HeroicStr); + newbon->STA += (item->ASta + item->HeroicSta); + newbon->DEX += (item->ADex + item->HeroicDex); + newbon->AGI += (item->AAgi + item->HeroicAgi); + newbon->INT += (item->AInt + item->HeroicInt); + newbon->WIS += (item->AWis + item->HeroicWis); + newbon->CHA += (item->ACha + item->HeroicCha); + + newbon->MR += (item->MR + item->HeroicMR); + newbon->FR += (item->FR + item->HeroicFR); + newbon->CR += (item->CR + item->HeroicCR); + newbon->PR += (item->PR + item->HeroicPR); + newbon->DR += (item->DR + item->HeroicDR); + newbon->Corrup += (item->SVCorruption + item->HeroicSVCorrup); + + newbon->STRCapMod += item->HeroicStr; + newbon->STACapMod += item->HeroicSta; + newbon->DEXCapMod += item->HeroicDex; + newbon->AGICapMod += item->HeroicAgi; + newbon->INTCapMod += item->HeroicInt; + newbon->WISCapMod += item->HeroicWis; + newbon->CHACapMod += item->HeroicCha; + newbon->MRCapMod += item->HeroicMR; + newbon->CRCapMod += item->HeroicFR; + newbon->FRCapMod += item->HeroicCR; + newbon->PRCapMod += item->HeroicPR; + newbon->DRCapMod += item->HeroicDR; + newbon->CorrupCapMod += item->HeroicSVCorrup; + + newbon->HeroicSTR += item->HeroicStr; + newbon->HeroicSTA += item->HeroicSta; + newbon->HeroicDEX += item->HeroicDex; + newbon->HeroicAGI += item->HeroicAgi; + newbon->HeroicINT += item->HeroicInt; + newbon->HeroicWIS += item->HeroicWis; + newbon->HeroicCHA += item->HeroicCha; + newbon->HeroicMR += item->HeroicMR; + newbon->HeroicFR += item->HeroicFR; + newbon->HeroicCR += item->HeroicCR; + newbon->HeroicPR += item->HeroicPR; + newbon->HeroicDR += item->HeroicDR; + newbon->HeroicCorrup += item->HeroicSVCorrup; + + } + else + { + int lvl = GetLevel(); + int reclvl = item->RecLevel; + + newbon->AC += CalcRecommendedLevelBonus( lvl, reclvl, item->AC ); + newbon->HP += CalcRecommendedLevelBonus( lvl, reclvl, item->HP ); + newbon->Mana += CalcRecommendedLevelBonus( lvl, reclvl, item->Mana ); + newbon->Endurance += CalcRecommendedLevelBonus( lvl, reclvl, item->Endur ); + newbon->STR += CalcRecommendedLevelBonus( lvl, reclvl, (item->AStr + item->HeroicStr) ); + newbon->STA += CalcRecommendedLevelBonus( lvl, reclvl, (item->ASta + item->HeroicSta) ); + newbon->DEX += CalcRecommendedLevelBonus( lvl, reclvl, (item->ADex + item->HeroicDex) ); + newbon->AGI += CalcRecommendedLevelBonus( lvl, reclvl, (item->AAgi + item->HeroicAgi) ); + newbon->INT += CalcRecommendedLevelBonus( lvl, reclvl, (item->AInt + item->HeroicInt) ); + newbon->WIS += CalcRecommendedLevelBonus( lvl, reclvl, (item->AWis + item->HeroicWis) ); + newbon->CHA += CalcRecommendedLevelBonus( lvl, reclvl, (item->ACha + item->HeroicCha) ); + + newbon->MR += CalcRecommendedLevelBonus( lvl, reclvl, (item->MR + item->HeroicMR) ); + newbon->FR += CalcRecommendedLevelBonus( lvl, reclvl, (item->FR + item->HeroicFR) ); + newbon->CR += CalcRecommendedLevelBonus( lvl, reclvl, (item->CR + item->HeroicCR) ); + newbon->PR += CalcRecommendedLevelBonus( lvl, reclvl, (item->PR + item->HeroicPR) ); + newbon->DR += CalcRecommendedLevelBonus( lvl, reclvl, (item->DR + item->HeroicDR) ); + newbon->Corrup += CalcRecommendedLevelBonus( lvl, reclvl, (item->SVCorruption + item->HeroicSVCorrup) ); + + newbon->STRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicStr ); + newbon->STACapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSta ); + newbon->DEXCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDex ); + newbon->AGICapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicAgi ); + newbon->INTCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicInt ); + newbon->WISCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicWis ); + newbon->CHACapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCha ); + newbon->MRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicMR ); + newbon->CRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicFR ); + newbon->FRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCR ); + newbon->PRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicPR ); + newbon->DRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDR ); + newbon->CorrupCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSVCorrup ); + + newbon->HeroicSTR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicStr ); + newbon->HeroicSTA += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSta ); + newbon->HeroicDEX += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDex ); + newbon->HeroicAGI += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicAgi ); + newbon->HeroicINT += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicInt ); + newbon->HeroicWIS += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicWis ); + newbon->HeroicCHA += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCha ); + newbon->HeroicMR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicMR ); + newbon->HeroicFR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicFR ); + newbon->HeroicCR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCR ); + newbon->HeroicPR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicPR ); + newbon->HeroicDR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDR ); + newbon->HeroicCorrup += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSVCorrup ); + } + + //FatherNitwit: New style haste, shields, and regens + if(newbon->haste < (int16)item->Haste) { + newbon->haste = item->Haste; + } + if(item->Regen > 0) + newbon->HPRegen += item->Regen; + + if(item->ManaRegen > 0) + newbon->ManaRegen += item->ManaRegen; + + if(item->EnduranceRegen > 0) + newbon->EnduranceRegen += item->EnduranceRegen; + + if(item->Attack > 0) { + + int cap = RuleI(Character, ItemATKCap); + cap += itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap; + + if((newbon->ATK + item->Attack) > cap) + newbon->ATK = RuleI(Character, ItemATKCap); + else + newbon->ATK += item->Attack; + } + if(item->DamageShield > 0) { + if((newbon->DamageShield + item->DamageShield) > RuleI(Character, ItemDamageShieldCap)) + newbon->DamageShield = RuleI(Character, ItemDamageShieldCap); + else + newbon->DamageShield += item->DamageShield; + } + if(item->SpellShield > 0) { + if((newbon->SpellShield + item->SpellShield) > RuleI(Character, ItemSpellShieldingCap)) + newbon->SpellShield = RuleI(Character, ItemSpellShieldingCap); + else + newbon->SpellShield += item->SpellShield; + } + if(item->Shielding > 0) { + if((newbon->MeleeMitigation + item->Shielding) > RuleI(Character, ItemShieldingCap)) + newbon->MeleeMitigation = RuleI(Character, ItemShieldingCap); + else + newbon->MeleeMitigation += item->Shielding; + } + if(item->StunResist > 0) { + if((newbon->StunResist + item->StunResist) > RuleI(Character, ItemStunResistCap)) + newbon->StunResist = RuleI(Character, ItemStunResistCap); + else + newbon->StunResist += item->StunResist; + } + if(item->StrikeThrough > 0) { + if((newbon->StrikeThrough + item->StrikeThrough) > RuleI(Character, ItemStrikethroughCap)) + newbon->StrikeThrough = RuleI(Character, ItemStrikethroughCap); + else + newbon->StrikeThrough += item->StrikeThrough; + } + if(item->Avoidance > 0) { + if((newbon->AvoidMeleeChance + item->Avoidance) > RuleI(Character, ItemAvoidanceCap)) + newbon->AvoidMeleeChance = RuleI(Character, ItemAvoidanceCap); + else + newbon->AvoidMeleeChance += item->Avoidance; + } + if(item->Accuracy > 0) { + if((newbon->HitChance + item->Accuracy) > RuleI(Character, ItemAccuracyCap)) + newbon->HitChance = RuleI(Character, ItemAccuracyCap); + else + newbon->HitChance += item->Accuracy; + } + if(item->CombatEffects > 0) { + if((newbon->ProcChance + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap)) + newbon->ProcChance = RuleI(Character, ItemCombatEffectsCap); + else + newbon->ProcChance += item->CombatEffects; + } + if(item->DotShielding > 0) { + if((newbon->DoTShielding + item->DotShielding) > RuleI(Character, ItemDoTShieldingCap)) + newbon->DoTShielding = RuleI(Character, ItemDoTShieldingCap); + else + newbon->DoTShielding += item->DotShielding; + } + + if(item->HealAmt > 0) { + if((newbon->HealAmt + item->HealAmt) > RuleI(Character, ItemHealAmtCap)) + newbon->HealAmt = RuleI(Character, ItemHealAmtCap); + else + newbon->HealAmt += item->HealAmt; + } + if(item->SpellDmg > 0) { + if((newbon->SpellDmg + item->SpellDmg) > RuleI(Character, ItemSpellDmgCap)) + newbon->SpellDmg = RuleI(Character, ItemSpellDmgCap); + else + newbon->SpellDmg += item->SpellDmg; + } + if(item->Clairvoyance > 0) { + if((newbon->Clairvoyance + item->Clairvoyance) > RuleI(Character, ItemClairvoyanceCap)) + newbon->Clairvoyance = RuleI(Character, ItemClairvoyanceCap); + else + newbon->Clairvoyance += item->Clairvoyance; + } + + if(item->DSMitigation > 0) { + if((newbon->DSMitigation + item->DSMitigation) > RuleI(Character, ItemDSMitigationCap)) + newbon->DSMitigation = RuleI(Character, ItemDSMitigationCap); + else + newbon->DSMitigation += item->DSMitigation; + } + if (item->Worn.Effect>0 && (item->Worn.Type == ET_WornEffect)) { // latent effects + ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, true); + } + + if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects + ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0, true); + } + + switch(item->BardType) + { + case 51: /* All (e.g. Singing Short Sword) */ + { + if(item->BardValue > newbon->singingMod) + newbon->singingMod = item->BardValue; + if(item->BardValue > newbon->brassMod) + newbon->brassMod = item->BardValue; + if(item->BardValue > newbon->stringedMod) + newbon->stringedMod = item->BardValue; + if(item->BardValue > newbon->percussionMod) + newbon->percussionMod = item->BardValue; + if(item->BardValue > newbon->windMod) + newbon->windMod = item->BardValue; + break; + } + case 50: /* Singing */ + { + if(item->BardValue > newbon->singingMod) + newbon->singingMod = item->BardValue; + break; + } + case 23: /* Wind */ + { + if(item->BardValue > newbon->windMod) + newbon->windMod = item->BardValue; + break; + } + case 24: /* stringed */ + { + if(item->BardValue > newbon->stringedMod) + newbon->stringedMod = item->BardValue; + break; + } + case 25: /* brass */ + { + if(item->BardValue > newbon->brassMod) + newbon->brassMod = item->BardValue; + break; + } + case 26: /* Percussion */ + { + if(item->BardValue > newbon->percussionMod) + newbon->percussionMod = item->BardValue; + break; + } + } + + if (item->SkillModValue != 0 && item->SkillModType <= HIGHEST_SKILL){ + if ((item->SkillModValue > 0 && newbon->skillmod[item->SkillModType] < item->SkillModValue) || + (item->SkillModValue < 0 && newbon->skillmod[item->SkillModType] > item->SkillModValue)) + { + newbon->skillmod[item->SkillModType] = item->SkillModValue; + } + } + + // Add Item Faction Mods + if (item->FactionMod1) + { + if (item->FactionAmt1 > 0 && item->FactionAmt1 > GetItemFactionBonus(item->FactionMod1)) + { + AddItemFactionBonus(item->FactionMod1, item->FactionAmt1); + } + else if (item->FactionAmt1 < 0 && item->FactionAmt1 < GetItemFactionBonus(item->FactionMod1)) + { + AddItemFactionBonus(item->FactionMod1, item->FactionAmt1); + } + } + if (item->FactionMod2) + { + if (item->FactionAmt2 > 0 && item->FactionAmt2 > GetItemFactionBonus(item->FactionMod2)) + { + AddItemFactionBonus(item->FactionMod2, item->FactionAmt2); + } + else if (item->FactionAmt2 < 0 && item->FactionAmt2 < GetItemFactionBonus(item->FactionMod2)) + { + AddItemFactionBonus(item->FactionMod2, item->FactionAmt2); + } + } + if (item->FactionMod3) + { + if (item->FactionAmt3 > 0 && item->FactionAmt3 > GetItemFactionBonus(item->FactionMod3)) + { + AddItemFactionBonus(item->FactionMod3, item->FactionAmt3); + } + else if (item->FactionAmt3 < 0 && item->FactionAmt3 < GetItemFactionBonus(item->FactionMod3)) + { + AddItemFactionBonus(item->FactionMod3, item->FactionAmt3); + } + } + if (item->FactionMod4) + { + if (item->FactionAmt4 > 0 && item->FactionAmt4 > GetItemFactionBonus(item->FactionMod4)) + { + AddItemFactionBonus(item->FactionMod4, item->FactionAmt4); + } + else if (item->FactionAmt4 < 0 && item->FactionAmt4 < GetItemFactionBonus(item->FactionMod4)) + { + AddItemFactionBonus(item->FactionMod4, item->FactionAmt4); + } + } + + if (item->ExtraDmgSkill != 0 && item->ExtraDmgSkill <= HIGHEST_SKILL) { + if((newbon->SkillDamageAmount[item->ExtraDmgSkill] + item->ExtraDmgAmt) > RuleI(Character, ItemExtraDmgCap)) + newbon->SkillDamageAmount[item->ExtraDmgSkill] = RuleI(Character, ItemExtraDmgCap); + else + newbon->SkillDamageAmount[item->ExtraDmgSkill] += item->ExtraDmgAmt; + } + + if (!isAug) + { + int i; + for(i = 0; i < MAX_AUGMENT_SLOTS; i++) { + AddItemBonuses(inst->GetAugment(i),newbon,true); + } + } + +} + +int Merc::GroupLeadershipAAHealthEnhancement() +{ + Group *g = GetGroup(); + + if(!g || (g->GroupCount() < 3)) + return 0; + + switch(g->GetLeadershipAA(groupAAHealthEnhancement)) + { + case 0: + return 0; + case 1: + return 30; + case 2: + return 60; + case 3: + return 100; + } + + return 0; +} + +int Merc::GroupLeadershipAAManaEnhancement() +{ + Group *g = GetGroup(); + + if(!g || (g->GroupCount() < 3)) + return 0; + + switch(g->GetLeadershipAA(groupAAManaEnhancement)) + { + case 0: + return 0; + case 1: + return 30; + case 2: + return 60; + case 3: + return 100; + } + + return 0; +} + +int Merc::GroupLeadershipAAHealthRegeneration() +{ + Group *g = GetGroup(); + + if(!g || (g->GroupCount() < 3)) + return 0; + + switch(g->GetLeadershipAA(groupAAHealthRegeneration)) + { + case 0: + return 0; + case 1: + return 4; + case 2: + return 6; + case 3: + return 8; + } + + return 0; +} + +int Merc::GroupLeadershipAAOffenseEnhancement() +{ + Group *g = GetGroup(); + + if(!g || (g->GroupCount() < 3)) + return 0; + + 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; + } + return 0; +} + +int16 Merc::CalcSTR() { + int16 val = _baseSTR + itembonuses.STR + spellbonuses.STR; + + int16 mod = aabonuses.STR; + + STR = val + mod; + + if(STR < 1) + STR = 1; + + return(STR); +} + +int16 Merc::CalcSTA() { + int16 val = _baseSTA + itembonuses.STA + spellbonuses.STA; + + int16 mod = aabonuses.STA; + + STA = val + mod; + + if(STA < 1) + STA = 1; + + return(STA); +} + +int16 Merc::CalcAGI() { + int16 val = _baseAGI + itembonuses.AGI + spellbonuses.AGI; + int16 mod = aabonuses.AGI; + + int16 str = GetSTR(); + + AGI = val + mod; + + if(AGI < 1) + AGI = 1; + + return(AGI); +} + +int16 Merc::CalcDEX() { + int16 val = _baseDEX + itembonuses.DEX + spellbonuses.DEX; + + int16 mod = aabonuses.DEX; + + DEX = val + mod; + + if(DEX < 1) + DEX = 1; + + return(DEX); +} + +int16 Merc::CalcINT() { + int16 val = _baseINT + itembonuses.INT + spellbonuses.INT; + + int16 mod = aabonuses.INT; + + INT = val + mod; + + if(INT < 1) + INT = 1; + + return(INT); +} + +int16 Merc::CalcWIS() { + int16 val = _baseWIS + itembonuses.WIS + spellbonuses.WIS; + + int16 mod = aabonuses.WIS; + + WIS = val + mod; + + if(WIS < 1) + WIS = 1; + + return(WIS); +} + +int16 Merc::CalcCHA() { + int16 val = _baseCHA + itembonuses.CHA + spellbonuses.CHA; + + int16 mod = aabonuses.CHA; + + CHA = val + mod; + + if(CHA < 1) + CHA = 1; + + return(CHA); +} + +//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() +{ + MR = _baseMR + itembonuses.MR + spellbonuses.MR + aabonuses.MR; + + if(MR < 1) + MR = 1; + + return(MR); +} + +int16 Merc::CalcFR() +{ + FR = _baseFR + itembonuses.FR + spellbonuses.FR + aabonuses.FR; + + if(FR < 1) + FR = 1; + + return(FR); +} + +int16 Merc::CalcDR() +{ + DR = _baseDR + itembonuses.DR + spellbonuses.DR + aabonuses.DR; + + if(DR < 1) + DR = 1; + + return(DR); +} + +int16 Merc::CalcPR() +{ + PR = _basePR + itembonuses.PR + spellbonuses.PR + aabonuses.PR; + + if(PR < 1) + PR = 1; + + return(PR); +} + +int16 Merc::CalcCR() +{ + CR = _baseCR + itembonuses.CR + spellbonuses.CR + aabonuses.CR; + + if(CR < 1) + CR = 1; + + return(CR); +} + +int16 Merc::CalcCorrup() +{ + Corrup = _baseCorrup + itembonuses.Corrup + spellbonuses.Corrup + aabonuses.Corrup; + + return(Corrup); +} + +int16 Merc::CalcATK() { + ATK = _baseATK + itembonuses.ATK + spellbonuses.ATK + aabonuses.ATK + GroupLeadershipAAOffenseEnhancement() + ((GetSTR() + GetSkill(OFFENSE)) * 9 / 10); + return(ATK); +} + +int16 Merc::CalcAC() { + //spell AC bonuses are added directly to natural total + AC = _baseAC + spellbonuses.AC; + return(AC); +} + +int32 Merc::CalcHPRegen() { + int32 regen = hp_regen + itembonuses.HPRegen + spellbonuses.HPRegen; + + regen += aabonuses.HPRegen + GroupLeadershipAAHealthRegeneration(); + + return (regen * RuleI(Character, HPRegenMultiplier) / 100); +} + +int32 Merc::CalcHPRegenCap() +{ + int cap = RuleI(Character, ItemHealthRegenCap) + itembonuses.HeroicSTA/25; + + cap += aabonuses.ItemHPRegenCap + spellbonuses.ItemHPRegenCap + itembonuses.ItemHPRegenCap; + + return (cap * RuleI(Character, HPRegenMultiplier) / 100); +} + +int32 Merc::CalcMaxHP() { + float nd = 10000; + max_hp = (CalcBaseHP() + itembonuses.HP); + + //The AA desc clearly says it only applies to base hp.. + //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 + + max_hp = (float)max_hp * (float)nd / (float)10000; //this is to fix the HP-above-495k issue + max_hp += spellbonuses.HP + aabonuses.HP; + + max_hp += GroupLeadershipAAHealthEnhancement(); + + max_hp += max_hp * (spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000; + + if (cur_hp > max_hp) + cur_hp = max_hp; + + int hp_perc_cap = spellbonuses.HPPercCap; + if(hp_perc_cap) { + int curHP_cap = (max_hp * hp_perc_cap) / 100; + if (cur_hp > curHP_cap) + cur_hp = curHP_cap; + } + + return max_hp; +} + +int32 Merc::CalcBaseHP() +{ + return base_hp; +} + +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; + } + } + if (max_mana < 0) { + max_mana = 0; + } + + if (cur_mana > max_mana) { + cur_mana = max_mana; + } + + int mana_perc_cap = spellbonuses.ManaPercCap; + if(mana_perc_cap) { + int curMana_cap = (max_mana * mana_perc_cap) / 100; + if (cur_mana > curMana_cap) + cur_mana = curMana_cap; + } + +#if EQDEBUG >= 11 + LogFile->write(EQEMuLog::Debug, "Merc::CalcMaxMana() called for %s - returning %d", GetName(), max_mana); +#endif + return max_mana; +} + +int32 Merc::CalcBaseMana() +{ + return base_mana; +} + +int32 Merc::CalcBaseManaRegen() +{ + uint8 clevel = GetLevel(); + int32 regen = 0; + if (IsSitting()) + { + if(HasSkill(MEDITATE)) + regen = (((GetSkill(MEDITATE) / 10) + (clevel - (clevel / 4))) / 4) + 4; + else + regen = 2; + } + else { + regen = 2; + } + return regen; +} + +int32 Merc::CalcManaRegen() +{ + int32 regen = 0; + //this should be changed so we dont med while camping, etc... + if (IsSitting()) + { + BuffFadeBySitModifier(); + if(HasSkill(MEDITATE)) { + this->_medding = true; + regen = ((GetSkill(MEDITATE) / 10) + mana_regen); + regen += spellbonuses.ManaRegen + itembonuses.ManaRegen; + } + else + regen = mana_regen + spellbonuses.ManaRegen + itembonuses.ManaRegen; + } + else { + this->_medding = false; + regen = mana_regen + spellbonuses.ManaRegen + itembonuses.ManaRegen; + } + + //AAs + regen += aabonuses.ManaRegen; + + return (regen * RuleI(Character, ManaRegenMultiplier) / 100); +} + +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; + } + + return (cap * RuleI(Character, ManaRegenMultiplier) / 100); +} + +void Merc::CalcMaxEndurance() +{ + max_end = CalcBaseEndurance() + spellbonuses.Endurance + itembonuses.Endurance; + + if (max_end < 0) { + max_end = 0; + } + + if (cur_end > max_end) { + cur_end = max_end; + } + + int end_perc_cap = spellbonuses.EndPercCap; + if(end_perc_cap) { + int curEnd_cap = (max_end * end_perc_cap) / 100; + if (cur_end > curEnd_cap) + cur_end = curEnd_cap; + } +} + +int32 Merc::CalcBaseEndurance() +{ + int32 base_end = 0; + int32 base_endurance = 0; + int32 ConvertedStats = 0; + int32 sta_end = 0; + int Stats = 0; + + if(GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { + int HeroicStats = 0; + + Stats = ((GetSTR() + GetSTA() + GetDEX() + GetAGI()) / 4); + HeroicStats = ((GetHeroicSTR() + GetHeroicSTA() + GetHeroicDEX() + GetHeroicAGI()) / 4); + + if (Stats > 100) { + ConvertedStats = (((Stats - 100) * 5 / 2) + 100); + if (Stats > 201) { + ConvertedStats -= ((Stats - 201) * 5 / 4); + } + } + else { + ConvertedStats = Stats; + } + + if (GetLevel() < 41) { + sta_end = (GetLevel() * 75 * ConvertedStats / 1000); + base_endurance = (GetLevel() * 15); + } + else if (GetLevel() < 81) { + sta_end = ((3 * ConvertedStats) + ((GetLevel() - 40) * 15 * ConvertedStats / 100)); + base_endurance = (600 + ((GetLevel() - 40) * 30)); + } + else { + sta_end = (9 * ConvertedStats); + base_endurance = (1800 + ((GetLevel() - 80) * 18)); + } + base_end = (base_endurance + sta_end + (HeroicStats * 10)); + } + else + { + Stats = GetSTR()+GetSTA()+GetDEX()+GetAGI(); + int LevelBase = GetLevel() * 15; + + int at_most_800 = Stats; + if(at_most_800 > 800) + at_most_800 = 800; + + int Bonus400to800 = 0; + int HalfBonus400to800 = 0; + int Bonus800plus = 0; + int HalfBonus800plus = 0; + + int BonusUpto800 = int( at_most_800 / 4 ) ; + if(Stats > 400) { + Bonus400to800 = int( (at_most_800 - 400) / 4 ); + HalfBonus400to800 = int( max( ( at_most_800 - 400 ), 0 ) / 8 ); + + if(Stats > 800) { + Bonus800plus = int( (Stats - 800) / 8 ) * 2; + HalfBonus800plus = int( (Stats - 800) / 16 ); + } + } + int bonus_sum = BonusUpto800 + Bonus400to800 + HalfBonus400to800 + Bonus800plus + HalfBonus800plus; + + base_end = LevelBase; + + //take all of the sums from above, then multiply by level*0.075 + base_end += ( bonus_sum * 3 * GetLevel() ) / 40; + } + return base_end; +} + +int32 Merc::CalcEnduranceRegen() { + int32 regen = int32(GetLevel() * 4 / 10) + 2; + regen += aabonuses.EnduranceRegen + spellbonuses.EnduranceRegen + itembonuses.EnduranceRegen; + + return (regen * RuleI(Character, EnduranceRegenMultiplier) / 100); +} + +int32 Merc::CalcEnduranceRegenCap() { + int cap = (RuleI(Character, ItemEnduranceRegenCap) + itembonuses.HeroicSTR/25 + itembonuses.HeroicDEX/25 + itembonuses.HeroicAGI/25 + itembonuses.HeroicSTA/25); + + return (cap * RuleI(Character, EnduranceRegenMultiplier) / 100); +} + +void Merc::SetEndurance(int32 newEnd) +{ + /*Endurance can't be less than 0 or greater than max*/ + if(newEnd < 0) + newEnd = 0; + else if(newEnd > GetMaxEndurance()){ + newEnd = GetMaxEndurance(); + } + + cur_end = newEnd; +} + +void Merc::DoEnduranceUpkeep() { + int upkeep_sum = 0; + + int cost_redux = spellbonuses.EnduranceReduction + itembonuses.EnduranceReduction; + + 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) { + if(cost_redux > 0) { + if(upkeep <= cost_redux) + continue; //reduced to 0 + upkeep -= cost_redux; + } + if((upkeep+upkeep_sum) > GetEndurance()) { + //they do not have enough to keep this one going. + BuffFadeBySlot(buffs_i); + } else { + upkeep_sum += upkeep; + } + } + } + } + + if(upkeep_sum != 0) + SetEndurance(GetEndurance() - upkeep_sum); +} + +void Merc::CalcRestState() { + + // This method calculates rest state HP and mana regeneration. + // The bot must have been out of combat for RuleI(Character, RestRegenTimeToActivate) seconds, + // must be sitting down, and must not have any detrimental spells affecting them. + // + if(!RuleI(Character, RestRegenPercent)) + return; + + RestRegenHP = RestRegenMana = RestRegenEndurance = 0; + + if(IsEngaged() || !IsSitting()) + return; + + if(!rest_timer.Check(false)) + return; + + uint32 buff_count = GetMaxTotalSlots(); + for (unsigned int j = 0; j < buff_count; j++) { + if(buffs[j].spellid != SPELL_UNKNOWN) { + if(IsDetrimentalSpell(buffs[j].spellid) && (buffs[j].ticsremaining > 0)) + if(!DetrimentalSpellAllowsRest(buffs[j].spellid)) + return; + } + } + + RestRegenHP = (GetMaxHP() * RuleI(Character, RestRegenPercent) / 100); + + RestRegenMana = (GetMaxMana() * RuleI(Character, RestRegenPercent) / 100); + + if(RuleB(Character, RestRegenEndurance)) + RestRegenEndurance = (GetMaxEndurance() * RuleI(Character, RestRegenPercent) / 100); +} + +bool Merc::HasSkill(SkillType skill_id) const { + return((GetSkill(skill_id) > 0) && CanHaveSkill(skill_id)); +} + +bool Merc::CanHaveSkill(SkillType skill_id) const { + return(database.GetSkillCap(GetClass(), skill_id, RuleI(Character, MaxLevel)) > 0); + //if you don't have it by max level, then odds are you never will? +} + +uint16 Merc::MaxSkill(SkillType skillid, uint16 class_, uint16 level) const { + return(database.GetSkillCap(class_, skillid, level)); +} + +void Merc::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { + if(ns) { + Mob::FillSpawnStruct(ns, ForWho); + + ns->spawn.afk = 0; + 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.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.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); + } + } + + 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); + } + }*/ + } +} + +bool Merc::Process() +{ + if(IsStunned() && stunned_timer.Check()) + { + this->stunned = false; + this->stunned_timer.Disable(); + } + + if (p_depop) + { + SetOwnerID(0); + SetMercID(0); + return false; + } + + 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 + } + + if(IsSuspended()) { + //return false; + } + + if (HasGroup() && GetFollowID() == 0) { + SetFollowID(GetMercOwner()->GetID()); + } + + + SpellProcess(); + + if(tic_timer.Check()) + { + //6 seconds, or whatever the rule is set to has passed, send this position to everyone to avoid ghosting + if(!IsMoving() && !IsEngaged()) + { + SendPosition(); + if(IsSitting()) { + if(!rest_timer.Enabled()) { + rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000); + } + } + } + + BuffProcess(); + + CalcRestState(); + + if(GetHP() < GetMaxHP()) + SetHP(GetHP() + CalcHPRegen()); + + if(GetMana() < GetMaxMana()) + SetMana(GetMana() + CalcManaRegen()); + + if(GetEndurance() < GetMaxEndurance()) + SetEndurance(GetEndurance() + CalcEnduranceRegen()); + } + + if (sendhpupdate_timer.Check()) { + SendHPUpdate(); + } + + if (endupkeep_timer.Check() && GetHP() > 0){ + DoEnduranceUpkeep(); + } + + if (IsStunned() || IsMezzed()) + return true; + + // Merc AI + AI_Process(); + + return true; +} + +bool Merc::IsMercCasterCombatRange(Mob *target) { + bool result = false; + + if(target) { + float range = MercAISpellRange; + + range *= range; + + // half the max so the bot doesn't always stop at max range to allow combat movement + range *= .5; + + float targetDistance = DistNoRootNoZ(*target); + + if(targetDistance > range) + result = false; + else + result = true; + } + + return result; +} + +void Merc::AI_Process() { + if(!IsAIControlled()) + return; + + if(IsCasting()) + return; + + // A bot wont start its AI if not grouped + if(!GetOwner() || !HasGroup()) { + return; + } + + if(GetAppearance() == eaDead) + return; + + Mob* MercOwner = GetOwner(); + + // The bots need an owner + if(!MercOwner) + return; + + try { + if(MercOwner->CastToClient()->IsDead()) { + SetTarget(0); + SetOwnerID(0); + return; + } + } + catch(...) { + SetTarget(0); + SetOwnerID(0); + return; + } + + if(!IsEngaged()) { + if(GetFollowID()) { + Group* g = GetGroup(); + if(g) { + 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); + } + } + else { + for(int counter = 0; counter < g->GroupCount(); counter++) { + if(g->members[counter]) { + if(g->members[counter]->GetTarget() && g->members[counter]->GetTarget()->IsNPC() && g->members[counter]->GetTarget()->GetHateAmount(MercOwner) && IsAttackAllowed(MercOwner->GetTarget())) { + float range = g->HasRole(g->members[counter], RolePuller) ? RuleI(Mercs, AggroRadiusPuller) : RuleI(Mercs, AggroRadius); + range = range * range; + if(g->members[counter]->GetTarget()->DistNoRootNoZ(*this) < range) { + AddToHateList(g->members[counter]->GetTarget(), 1); + } + } + } + } + } + } + } + } + + if(IsEngaged()) + { + _ZP(Mob_BOT_Process_IsEngaged); + + if(rest_timer.Enabled()) + rest_timer.Disable(); + + if(IsRooted()) + SetTarget(hate_list.GetClosest(this)); + else + FindTarget(); + + if(!GetTarget()) + return; + + if(HasPet()) + GetPet()->SetTarget(GetTarget()); + + if(!IsSitting()) + FaceTarget(GetTarget()); + + if(DivineAura()) + return; + + // Let's check if we have a los with our target. + // If we don't, our hate_list is wiped. + // Else, it was causing the bot to aggro behind wall etc... causing massive trains. + if(!CheckLosFN(GetTarget()) || GetTarget()->IsMezzed() || !IsAttackAllowed(GetTarget())) { + WipeHateList(); + + if(IsMoving()) { + SetHeading(0); + SetRunAnimSpeed(0); + + if(moved) { + moved = false; + SendPosition(); + SetMoving(false); + } + } + + return; + } + + bool atCombatRange = false; + + float meleeDistance = GetMaxMeleeRangeToTarget(GetTarget()); + + if(GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || GetClass() == WARRIOR) { + meleeDistance = meleeDistance * .30; + } + else { + meleeDistance *= (float)MakeRandomFloat(.50, .85); + } + if(IsMercCaster() && GetLevel() > 12) { + if(IsMercCasterCombatRange(GetTarget())) + atCombatRange = true; + } + else if(DistNoRoot(*GetTarget()) <= meleeDistance) { + atCombatRange = true; + } + + if(atCombatRange) { + if(IsMoving()) { + SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); + SetRunAnimSpeed(0); + + if(moved) { + moved = false; + SendPosition(); + SetMoving(false); + } + } + + 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)) { + CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); + return; + } + } + 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) { + CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); + return; + } + } + + if(IsMoving()) + SendPosUpdate(); + else + SendPosition(); + } + + 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;*/ + + // First, special attack per class (kick, backstab etc..) + DoClassAttacks(GetTarget()); + + //try main hand first + if(attack_timer.Check()) { + Attack(GetTarget(), SLOT_PRIMARY); + + bool tripleSuccess = false; + + if(GetOwner() && GetTarget() && CanThisClassDoubleAttack()) { + + if(GetOwner()) { + Attack(GetTarget(), SLOT_PRIMARY, true); + } + + if(GetOwner() && GetTarget() && SpecAttacks[SPECATK_TRIPLE]) { + tripleSuccess = true; + Attack(GetTarget(), SLOT_PRIMARY, true); + } + + //quad attack, does this belong here?? + if(GetOwner() && GetTarget() && SpecAttacks[SPECATK_QUAD]) { + Attack(GetTarget(), SLOT_PRIMARY, true); + } + } + + //Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack). + int16 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; + + if (GetTarget() && flurrychance) + { + if(MakeRandomInt(0, 100) < flurrychance) + { + Message_StringID(MT_NPCFlurry, 128); + Attack(GetTarget(), SLOT_PRIMARY, false); + Attack(GetTarget(), SLOT_PRIMARY, false); + } + } + + int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; + + if (GetTarget() && ExtraAttackChanceBonus) { + if(MakeRandomInt(0, 100) < ExtraAttackChanceBonus) + { + Attack(GetTarget(), SLOT_PRIMARY, 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; + // } + //} + + //now off hand + if(GetTarget() && attack_dw_timer.Check() && CanThisClassDualWield()) { + + int weapontype = NULL; + bool bIsFist = true; + + if(bIsFist || ((weapontype != ItemType2HS) && (weapontype != ItemType2HPierce) && (weapontype != ItemType2HB))) { + float DualWieldProbability = 0.0f; + + int16 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity; + DualWieldProbability = (GetSkill(DUAL_WIELD) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max + int16 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance; + DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f; + + float random = MakeRandomFloat(0, 1); + + if (random < DualWieldProbability){ // Max 78% of DW + + Attack(GetTarget(), SLOT_SECONDARY); // Single attack with offhand + + if( CanThisClassDoubleAttack()) { + if(GetTarget() && GetTarget()->GetHP() > -10) + Attack(GetTarget(), SLOT_SECONDARY); // 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(GetBotStance() != BotStancePassive) + 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(GetBotStance() == BotStancePassive) + // return; + + if(AI_EngagedCastCheck()) { + MercMeditate(false); + } + else if(GetArchetype() == ARCHETYPE_CASTER) + MercMeditate(true); + } + } // end IsEngaged() + else { + // Not engaged in combat + SetTarget(0); + + if(!IsMoving() && AIthink_timer->Check() && !spellend_timer.Enabled()) { + + //TODO: Implement passive stances. + //if(GetBotStance() != BotStancePassive) { + if(!AI_IdleCastCheck() && !IsCasting()) + MercMeditate(true); + } + + if(AImovement_timer->Check()) { + if(GetFollowID()) { + Mob* follow = entity_list.GetMob(GetFollowID()); + + if(follow) { + float dist = DistNoRoot(*follow); + float speed = GetRunspeed(); + + if(dist < GetFollowDistance() + 1000) + speed = GetWalkspeed(); + + SetRunAnimSpeed(0); + + if(dist > GetFollowDistance()) { + CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed); + if(rest_timer.Enabled()) + rest_timer.Disable(); + return; + } + else + { + if(moved) + { + moved=false; + SendPosition(); + SetMoving(false); + } + } + } + } + } + } +} + +void Merc::AI_Start(int32 iMoveDelay) { + Mob::AI_Start(iMoveDelay); + if (!pAIControlled) + return; + + if (merc_spells.size() == 0) { + AIautocastspell_timer = new Timer(1000); + AIautocastspell_timer->Disable(); + } else { + AIautocastspell_timer = new Timer(750); + AIautocastspell_timer->Start(RandomTimer(0, 2000), false); + } + + if (ourNPCData) { + //AI_AddNPCSpells(ourNPCData->npc_spells_id); + NPCSpecialAttacks(ourNPCData->npc_attacks,0); + } + + SendTo(GetX(), GetY(), GetZ()); + SetChanged(); + SaveGuardSpot(); +} + +void Merc::AI_Stop() { + NPC::AI_Stop(); + Mob::AI_Stop(); +} + +bool Merc::AI_EngagedCastCheck() { + bool result = false; + bool failedToCast = false; + + if (GetTarget() && AIautocastspell_timer->Check(false)) { + _ZP(Merc_AI_Process_engaged_cast); + + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. + + mlog(AI__SPELLS, "Engaged autocast check triggered (MERCS)."); + + int8 mercClass = GetClass(); + + switch(mercClass) + { + 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_Nuke), SpellType_Nuke)) { + if (!AICastSpell(GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) { + failedToCast = true; + } + } + break; + case CASTERDPS: + failedToCast = true; + break; + } + + if(!AIautocastspell_timer->Enabled()) { + AIautocastspell_timer->Start(RandomTimer(100, 250), false); + } + + if(!failedToCast) + result = true; + } + + return result; +} + +bool Merc::AI_IdleCastCheck() { + bool result = false; + bool failedToCast = false; + + if (AIautocastspell_timer->Check(false)) { + _ZP(Merc_AI_IdleCastCheck); +#if MobAI_DEBUG_Spells >= 25 + cout << "Non-Engaged autocast check triggered: " << this->GetCleanName() << endl; +#endif + 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; + break; + case HEALER: + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Heal)) { + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Buff)) { + failedToCast = true; + } + } + result = true; + break; + case MELEEDPS: + failedToCast = true; + break; + case CASTERDPS: + failedToCast = true; + break; + } + + if(!AIautocastspell_timer->Enabled()) + AIautocastspell_timer->Start(RandomTimer(500, 1000), false); + + if(!failedToCast) + result = true; + } + + return result; +} + +bool EntityList::Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint16 iSpellTypes) { + _ZP(EntityList_Merc_AICheckCloseBeneficialSpells); + + if((iSpellTypes&SpellTypes_Detrimental) != 0) { + //according to live, you can buff and heal through walls... + //now with PCs, this only applies if you can TARGET the target, but + // according to Rogean, Live NPCs will just cast through walls/floors, no problem.. + // + // This check was put in to address an idle-mob CPU issue + _log(AI__ERROR, "Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); + return(false); + } + + if(!caster) + return false; + + if(!caster->AI_HasSpells()) + return false; + + if (iChance < 100) { + int8 tmp = MakeRandomInt(1, 100); + if (tmp > iChance) + return false; + } + + int8 mercCasterClass = caster->GetClass(); + + if( iSpellTypes == SpellType_Heal ) { + if( mercCasterClass == HEALER) { + // check in group + if(caster->HasGroup()) { + if(caster->AICastSpell(100, SpellType_Heal)) + return true; + } + } + } + + //Ok for the buffs.. + if( iSpellTypes == SpellType_Buff) { + if(caster->HasGroup()) { + if(caster->AICastSpell(100, SpellType_Buff)) + return true; + } + } + + return false; +} + +bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore) { + bool result = false; + MercSpell mercSpell = GetMercSpellBySpellID(this, spellid); + + // manacost has special values, -1 is no mana cost, -2 is instant cast (no mana) + int32 manaCost = mana_cost; + + if (manaCost == -1) + manaCost = spells[spellid].mana; + else if (manaCost == -2) + manaCost = 0; + + int32 extraMana = 0; + int32 hasMana = GetMana(); + + float dist2 = 0; + + if (mercSpell.type & SpellType_Escape) { + dist2 = 0; + } else + 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())) + { + SetRunAnimSpeed(0); + SendPosition(); + SetMoving(false); + + result = CastSpell(spellid, tar->GetID(), 1, -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0); + + if(IsCasting() && IsSitting()) + Stand(); + } + + // if the spell wasn't casted, then take back any extra mana that was given to the bot to cast that spell + if(!result) { + SetMana(hasMana); + extraMana = false; + } + else { //handle spell recast and recast timers + mercSpell.time_cancast = Timer::GetCurrentTime() + spells[spellid].recast_time; + + if(spells[spellid].EndurTimerIndex > 0) { + //SetSpellRecastTimer(spells[spellid].EndurTimerIndex, spells[spellid].recast_time); + } + } + + return result; +} + +bool Merc::AICastSpell(int8 iChance, int16 iSpellTypes) { + _ZP(Bot_AICastSpell); + + if(!AI_HasSpells()) + return false; + + if (iChance < 100) { + if (MakeRandomInt(0, 100) > iChance){ + return false; + } + } + + 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 castedSpell = false; + + if(HasGroup()) { + Group *g = GetGroup(); + + if(g) { + MercSpell selectedMercSpell; + selectedMercSpell.spellid = 0; + selectedMercSpell.stance = 0; + selectedMercSpell.type = 0; + selectedMercSpell.slot = 0; + selectedMercSpell.proc_chance = 0; + selectedMercSpell.time_cancast = 0; + + switch (iSpellTypes) { + case SpellType_Heal: { + Mob* tar = NULL; + int8 numToHeal = g->GetNumberNeedingHealedInGroup(IsEngaged() ? 75 : 95, true); + int8 checkHPR = IsEngaged() ? 95 : 99; + + //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(); + + if(hpr > checkHPR) { + //they don't need to be healed + 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(g->members[i] == GetMercOwner()) { + if(!tar || hpr < tar->GetHPRatio()) + tar = g->members[i]; //check owner first + } + else if(g->HasRole(g->members[i], RoleTank) && g->members[i]->GetHPRatio() < checkHPR){ + if(!tar || hpr < tar->GetHPRatio()) + tar = g->members[i]; + } + else if(!tar || hpr < tar->GetHPRatio()) { + tar = g->members[i]; + } + + if(g->members[i]->HasPet() && g->members[i]->GetPet()->GetHPRatio() < 50) { + if(!tar || ((g->members[i]->GetPet()->GetHPRatio() + 25) < tar->GetHPRatio())) { + tar = g->members[i]->GetPet(); + } + } + } + } + + if(numToHeal > 2) { + selectedMercSpell = GetBestMercSpellForGroupHeal(this); + + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(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() < 75) { + 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 = GetBestMercSpellForRegularSingleTargetHeal(this); + } + + //get regular heal + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); + } + } + + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetFirstMercSpellForSingleTargetHeal(this); + } + } + + if(selectedMercSpell.spellid > 0) { + if(AIDoSpellCast(selectedMercSpell.spellid, tar, -1)) + castedSpell = true; + } + + 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); + } + } + + break; + } + case SpellType_Root: { + break; + } + case SpellType_Buff: { + + if(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 ))) { + continue; + } + + for( int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g->members[i]) { + int32 oDontDoAgainBefore; + 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; + } + + int32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); + + if(AIDoSpellCast(selectedMercSpell.spellid, tar, -1)) + castedSpell = true; + } + + if(!castedSpell && tar->GetPet()) { + + if(!tar->GetPet()->IsImmuneToSpell(selectedMercSpell.spellid, this) + && (tar->GetPet()->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { + + int32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); + + if(AIDoSpellCast(selectedMercSpell.spellid, tar->GetPet(), -1)) + castedSpell = true; + } + } + } + } + } + break; + } + case SpellType_Nuke: { + bool isDiscipline = false; + 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); + } + + isDiscipline = true; + break; + case HEALER: + + break; + case MELEEDPS: + isDiscipline = true; + break; + case CASTERDPS: + + break; + } + + if(selectedMercSpell.spellid > 0) { + if(isDiscipline) { + if(UseDiscipline(selectedMercSpell.spellid, GetTarget()->GetID())) { + castedSpell = true; + } + } + else { + if(AIDoSpellCast(selectedMercSpell.spellid, GetTarget(), -1)) { + castedSpell = true; + } + } + } + + break; + } + case SpellType_InCombatBuff: { + break; + } + } + } + } + + return castedSpell; +} + +int8 Merc::GetChanceToCastBySpellType(int16 spellType) { + int mercStance = (int)GetStance(); + int8 mercClass = GetClass(); + int8 chance = 0; + + switch (spellType) { + case SpellType_Nuke: { + switch(mercClass) + { + case TANK: { + chance = 100; + break; + } + case HEALER:{ + break; + } + case MELEEDPS:{ + break; + } + case CASTERDPS:{ + chance = 100; + break; + } + } + 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) + { + case TANK: { + break; + } + case HEALER:{ + break; + } + case MELEEDPS:{ + break; + } + case CASTERDPS:{ + break; + } + } + 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) + { + case TANK: { + chance = 50; + break; + } + case HEALER:{ + break; + } + case MELEEDPS:{ + break; + } + case CASTERDPS:{ + break; + } + } + break; + } + default: + chance = 0; + break; + } + + return chance; +} + +bool Merc::CheckStance(int16 stance) { + + if(stance == 0 + || (stance > 0 && stance == GetStance()) + || (stance < 0 && abs(stance) != GetStance())) { + return true; + } + + return false; +} + +std::list Merc::GetMercSpellsBySpellType(Merc* caster, int spellType) { + std::list result; + + if(caster && caster->AI_HasSpells()) { + std::vector mercSpellList = caster->GetMercSpells(); + + for (int i = mercSpellList.size() - 1; i >= 0; i--) { + if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if((mercSpellList[i].type & spellType) && caster->CheckStance(mercSpellList[i].stance)) { + MercSpell mercSpell; + mercSpell.spellid = mercSpellList[i].spellid; + mercSpell.stance = mercSpellList[i].stance; + mercSpell.type = mercSpellList[i].type; + mercSpell.slot = mercSpellList[i].slot; + mercSpell.proc_chance = mercSpellList[i].proc_chance; + mercSpell.time_cancast = mercSpellList[i].time_cancast; + + result.push_back(mercSpell); + } + } + } + + return result; +} + +MercSpell Merc::GetFirstMercSpellBySpellType(Merc* caster, int spellType) { + MercSpell result; + + result.spellid = 0; + result.stance = 0; + result.type = 0; + result.slot = 0; + result.proc_chance = 0; + result.time_cancast = 0; + + if(caster && caster->AI_HasSpells()) { + std::vector mercSpellList = caster->GetMercSpells(); + + for (int i = mercSpellList.size() - 1; i >= 0; i--) { + if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + 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; + + break; + } + } + } + + return result; +} + +MercSpell Merc::GetMercSpellBySpellID(Merc* caster, uint16 spellid) { + MercSpell result; + + result.spellid = 0; + result.stance = 0; + result.type = 0; + result.slot = 0; + result.proc_chance = 0; + result.time_cancast = 0; + + if(caster && caster->AI_HasSpells()) { + std::vector mercSpellList = caster->GetMercSpells(); + + for (int i = mercSpellList.size() - 1; i >= 0; i--) { + if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + 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; + + break; + } + } + } + + return result; +} + +std::list Merc::GetMercSpellsForSpellEffect(Merc* caster, int spellEffect) { + std::list result; + + if(caster && caster->AI_HasSpells()) { + std::vector mercSpellList = caster->GetMercSpells(); + + for (int i = mercSpellList.size() - 1; i >= 0; i--) { + if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if(IsEffectInSpell(mercSpellList[i].spellid, spellEffect)) { + MercSpell MercSpell; + MercSpell.spellid = mercSpellList[i].spellid; + MercSpell.stance = mercSpellList[i].stance; + MercSpell.type = mercSpellList[i].type; + MercSpell.slot = mercSpellList[i].slot; + MercSpell.proc_chance = mercSpellList[i].proc_chance; + MercSpell.time_cancast = mercSpellList[i].time_cancast; + + result.push_back(MercSpell); + } + } + } + + return result; +} + +std::list Merc::GetMercSpellsForSpellEffectAndTargetType(Merc* caster, int spellEffect, SpellTargetType targetType) { + std::list result; + + if(caster && caster->AI_HasSpells()) { + std::vector mercSpellList = caster->GetMercSpells(); + + for (int i = mercSpellList.size() - 1; i >= 0; i--) { + if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if(IsEffectInSpell(mercSpellList[i].spellid, spellEffect)) { + if(spells[mercSpellList[i].spellid].targettype == targetType) { + MercSpell MercSpell; + MercSpell.spellid = mercSpellList[i].spellid; + MercSpell.stance = mercSpellList[i].stance; + MercSpell.type = mercSpellList[i].type; + MercSpell.slot = mercSpellList[i].slot; + MercSpell.proc_chance = mercSpellList[i].proc_chance; + MercSpell.time_cancast = mercSpellList[i].time_cancast; + + result.push_back(MercSpell); + } + } + } + } + + return result; +} + +MercSpell Merc::GetBestMercSpellForVeryFastHeal(Merc* caster) { + MercSpell result; + + result.spellid = 0; + result.stance = 0; + result.type = 0; + result.slot = 0; + result.proc_chance = 0; + result.time_cancast = 0; + + if(caster) { + std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_CurrentHP); + + 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(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; + + break; + } + } + } + + return result; +} + +MercSpell Merc::GetBestMercSpellForFastHeal(Merc* caster) { + MercSpell result; + + result.spellid = 0; + result.stance = 0; + result.type = 0; + result.slot = 0; + result.proc_chance = 0; + result.time_cancast = 0; + + if(caster) { + std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_CurrentHP); + + 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(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; + + break; + } + } + } + + return result; +} + +MercSpell Merc::GetBestMercSpellForHealOverTime(Merc* caster) { + MercSpell result; + + result.spellid = 0; + result.stance = 0; + result.type = 0; + result.slot = 0; + result.proc_chance = 0; + result.time_cancast = 0; + + if(caster) { + std::list botHoTSpellList = GetMercSpellsForSpellEffect(caster, SE_HealOverTime); + std::vector mercSpellList = caster->GetMercSpells(); + + for(std::list::iterator mercSpellListItr = botHoTSpellList.begin(); mercSpellListItr != botHoTSpellList.end(); mercSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + if(IsHealOverTimeSpell(mercSpellListItr->spellid)) { + + for (int i = mercSpellList.size() - 1; i >= 0; i--) { + if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if(mercSpellList[i].spellid == mercSpellListItr->spellid + && (mercSpellList[i].type & SpellType_Heal) + && CheckSpellRecastTimers(caster, mercSpellListItr->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; + } + } + + break; + } + } + } + + return result; +} + +MercSpell Merc::GetBestMercSpellForPercentageHeal(Merc* caster) { + MercSpell result; + + result.spellid = 0; + result.stance = 0; + result.type = 0; + result.slot = 0; + result.proc_chance = 0; + result.time_cancast = 0; + + if(caster && caster->AI_HasSpells()) { + std::vector mercSpellList = caster->GetMercSpells(); + + for (int i = mercSpellList.size() - 1; i >= 0; i--) { + if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if(IsCompleteHealSpell(mercSpellList[i].spellid) + && 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; + + break; + } + } + } + + return result; +} + +MercSpell Merc::GetBestMercSpellForRegularSingleTargetHeal(Merc* caster) { + MercSpell result; + + result.spellid = 0; + result.stance = 0; + result.type = 0; + result.slot = 0; + result.proc_chance = 0; + result.time_cancast = 0; + + if(caster) { + std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_CurrentHP); + + 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(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; + + break; + } + } + } + + return result; +} + +MercSpell Merc::GetFirstMercSpellForSingleTargetHeal(Merc* caster) { + MercSpell result; + + result.spellid = 0; + result.stance = 0; + result.type = 0; + result.slot = 0; + result.proc_chance = 0; + result.time_cancast = 0; + + if(caster) { + std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_CurrentHP); + + 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((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; + + break; + } + } + } + + return result; +} + +MercSpell Merc::GetBestMercSpellForGroupHeal(Merc* caster) { + MercSpell result; + + result.spellid = 0; + result.stance = 0; + result.type = 0; + result.slot = 0; + result.proc_chance = 0; + result.time_cancast = 0; + + if(caster) { + std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_CurrentHP); + + 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(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; + + break; + } + } + } + + return result; +} + +MercSpell Merc::GetBestMercSpellForGroupHealOverTime(Merc* caster) { + MercSpell result; + + result.spellid = 0; + result.stance = 0; + result.type = 0; + result.slot = 0; + result.proc_chance = 0; + result.time_cancast = 0; + + if(caster) { + std::list botHoTSpellList = GetMercSpellsForSpellEffect(caster, SE_HealOverTime); + std::vector mercSpellList = caster->GetMercSpells(); + + for(std::list::iterator mercSpellListItr = botHoTSpellList.begin(); mercSpellListItr != botHoTSpellList.end(); mercSpellListItr++) { + // Assuming all the spells have been loaded into this list by level and in descending order + if(IsGroupHealOverTimeSpell(mercSpellListItr->spellid)) { + + for (int i = mercSpellList.size() - 1; i >= 0; i--) { + if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) { + // this is both to quit early to save cpu and to avoid casting bad spells + // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + continue; + } + + if(mercSpellList[i].spellid == mercSpellListItr->spellid + && (mercSpellList[i].type & SpellType_Heal) + && CheckSpellRecastTimers(caster, mercSpellListItr->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; + } + } + + break; + } + } + } + + return result; +} + +MercSpell Merc::GetBestMercSpellForGroupCompleteHeal(Merc* caster) { + MercSpell result; + + result.spellid = 0; + result.stance = 0; + result.type = 0; + result.slot = 0; + result.proc_chance = 0; + result.time_cancast = 0; + + if(caster) { + std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_CompleteHeal); + + 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(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; + + break; + } + } + } + + return result; +} + +MercSpell Merc::GetBestMercSpellForAETaunt(Merc* caster) { + MercSpell result; + + result.spellid = 0; + result.stance = 0; + result.type = 0; + result.slot = 0; + result.proc_chance = 0; + result.time_cancast = 0; + + if(caster) { + std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_Taunt); + + 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((spells[mercSpellListItr->spellid].targettype == ST_AECaster + || 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; + + break; + } + } + } + + return result; +} + +MercSpell Merc::GetBestMercSpellForTaunt(Merc* caster) { + MercSpell result; + + result.spellid = 0; + result.stance = 0; + result.type = 0; + result.slot = 0; + result.proc_chance = 0; + result.time_cancast = 0; + + if(caster) { + std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_Taunt); + + 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((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; + + break; + } + } + } + + return result; +} + +MercSpell Merc::GetBestMercSpellForHate(Merc* caster) { + MercSpell result; + + result.spellid = 0; + result.stance = 0; + result.type = 0; + result.slot = 0; + result.proc_chance = 0; + result.time_cancast = 0; + + if(caster) { + std::list mercSpellList = GetMercSpellsForSpellEffect(caster, SE_Calm); + + 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(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; + } + } + } + + return result; +} + +void Merc::MercGroupSay(Mob *speaker, const char *msg, ...) +{ + + char buf[1000]; + va_list ap; + + va_start(ap, msg); + vsnprintf(buf, 1000, msg, ap); + va_end(ap); + + if(speaker->HasGroup()) { + Group *g = speaker->GetGroup(); + + if(g) + g->GroupMessage(speaker->CastToMob(), 0, 100, buf); + } +} + +bool Merc::UseDiscipline(int32 spell_id, int32 target) { + // Dont let client waste a reuse timer if they can't use the disc + if (IsStunned() || IsFeared() || IsMezzed() || IsAmnesiad()) + { + return(false); + } + + //make sure we can use it.. + if(!IsValidSpell(spell_id)) { + return(false); + } + + //can we use the spell? + const SPDat_Spell_Struct &spell = spells[spell_id]; + int8 level_to_use = spell.classes[GetClass() - 1]; + if(level_to_use == 255) { + return(false); + } + + if(level_to_use > GetLevel()) { + return(false); + } + + if(GetEndurance() > spell.EndurCost) { + SetEndurance(GetEndurance() - spell.EndurCost); + } else { + //too fatigued to use this skill right now. + return(false); + } + + if(spell.recast_time > 0) + { + if(CheckDisciplineRecastTimers(this, spells[spell_id].EndurTimerIndex)) { + if(spells[spell_id].EndurTimerIndex > 0 && spells[spell_id].EndurTimerIndex < MAX_DISCIPLINE_TIMERS) { + SetDisciplineRecastTimer(spells[spell_id].EndurTimerIndex, spell_id, spell.recast_time); + } + } + else { + return(false); + } + } + + if(IsCasting()) + InterruptSpell(); + + CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT); + + return(true); +} + +void Merc::SetSpellRecastTimer(uint16 timer_id, uint16 spellid, uint32 recast_delay) { + if(timer_id > 0) { + MercTimer timer; + timer.timerid = timer_id; + timer.timertype = 1; + timer.spellid = spellid; + timer.time_cancast = Timer::GetCurrentTime() + recast_delay; + timers[timer_id] = timer; + } +} + +int32 Merc::GetSpellRecastTimer(Merc *caster, uint16 timer_id) { + int32 result = 0; + if(caster && timer_id > 0) { + if(caster->timers.find(timer_id) != caster->timers.end()) { + result = caster->timers[timer_id].time_cancast; + } + } + return result; +} + +bool Merc::CheckSpellRecastTimers(Merc *caster, uint16 spell_id) { + if(caster) { + MercSpell mercSpell = GetMercSpellBySpellID(caster, spell_id); + if(mercSpell.spellid > 0 && mercSpell.time_cancast < Timer::GetCurrentTime()) { //checks spell recast + if(GetSpellRecastTimer(caster, spells[spell_id].EndurTimerIndex) < Timer::GetCurrentTime()) { //checks for spells on the same timer + return true; //can cast spell + } + } + } + return false; +} + +void Merc::SetDisciplineRecastTimer(uint16 timer_id, uint16 spellid, uint32 recast_delay) { + if(timer_id > 0) { + MercTimer timer; + timer.timerid = timer_id; + timer.timertype = 2; + timer.spellid = spellid; + timer.time_cancast = Timer::GetCurrentTime() + recast_delay; + timers[timer_id] = timer; + } +} + +int32 Merc::GetDisciplineRecastTimer(Merc *caster, uint16 timer_id) { + int32 result = 0; + if(caster && timer_id > 0) { + if(caster->timers.find(timer_id) != caster->timers.end()) { + result = caster->timers[timer_id].time_cancast; + } + } + return result; +} + +int32 Merc::GetDisciplineRemainingTime(Merc *caster, uint16 timer_id) { + int32 result = 0; + if(caster && timer_id > 0) { + int32 time_cancast = GetDisciplineRecastTimer(caster, timer_id); + if(time_cancast > Timer::GetCurrentTime()) + result = time_cancast - Timer::GetCurrentTime(); + } + return result; +} + +bool Merc::CheckDisciplineRecastTimers(Merc *caster, uint16 spell_id) { + if(caster) { + MercSpell mercSpell = GetMercSpellBySpellID(caster, spell_id); + if(mercSpell.spellid > 0 && mercSpell.time_cancast < Timer::GetCurrentTime()) { //checks spell recast + if(GetDisciplineRecastTimer(caster, spells[spell_id].EndurTimerIndex) < Timer::GetCurrentTime()) { //checks for spells on the same timer + return true; //can cast spell + } + } + } + return false; +} + +bool Merc::CheckTaunt() { + Mob* tar = GetTarget(); + //Only taunt if we are not top on target's hate list + //This ensures we have taunt available to regain aggro if needed + if(tar && tar->GetHateTop() && tar->GetHateTop() != this) { + return true; + } + return false; +} + +bool Merc::CheckAETaunt() { + //need to check area for mobs needing taunted + return false; +} + +void Merc::MercMeditate(bool isSitting) { + if(isSitting) { + // If the bot is a caster has less than 99% mana while its not engaged, he needs to sit to meditate + if(GetManaRatio() < 99.0f) + { + if(!IsSitting()) + Sit(); + } + else + { + if(IsSitting()) + Stand(); + } + } + else + { + if(IsSitting()) + Stand(); + } + + if(IsSitting()) { + if(!rest_timer.Enabled()) { + rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000); + } + } + else { + rest_timer.Disable(); + } +} + + +void Merc::Sit() { + if(IsMoving()) { + moved = false; + // SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); + SendPosition(); + SetMoving(false); + tar_ndx = 0; + } + + SetAppearance(eaSitting); +} + +void Merc::Stand() { + SetAppearance(eaStanding); +} + +bool Merc::IsSitting() { + bool result = false; + + if(GetAppearance() == eaSitting && !IsMoving()) + result = true; + + return result; +} + +bool Merc::IsStanding() { + bool result = false; + + if(GetAppearance() == eaStanding) + result = true; + + return result; +} + +float Merc::GetMaxMeleeRangeToTarget(Mob* target) { + float result = 0; + + if(target) { + float size_mod = GetSize(); + float other_size_mod = target->GetSize(); + + if(GetRace() == 49 || GetRace() == 158 || GetRace() == 196) //For races with a fixed size + size_mod = 60.0f; + else if (size_mod < 6.0) + size_mod = 8.0f; + + if(target->GetRace() == 49 || target->GetRace() == 158 || target->GetRace() == 196) //For races with a fixed size + other_size_mod = 60.0f; + else if (other_size_mod < 6.0) + other_size_mod = 8.0f; + + if (other_size_mod > size_mod) { + size_mod = other_size_mod; + } + + // this could still use some work, but for now it's an improvement.... + + if (size_mod > 29) + size_mod *= size_mod; + else if (size_mod > 19) + size_mod *= size_mod * 2; + else + size_mod *= size_mod * 4; + + // prevention of ridiculously sized hit boxes + if (size_mod > 10000) + size_mod = size_mod / 7; + + result = size_mod; + } + + return result; +} + +void Merc::DoClassAttacks(Mob *target) { + if(target == NULL) + return; //gotta have a target for all these + + bool ca_time = classattack_timer.Check(false); + + //only check attack allowed if we are going to do something + if(ca_time && !IsAttackAllowed(target)) + return; + + if(!ca_time) + return; + + float HasteModifier = 0; + if(GetHaste() > 0) + HasteModifier = 10000 / (100 + GetHaste()); + else if(GetHaste() < 0) + HasteModifier = (100 - GetHaste()); + else + HasteModifier = 100; + + int level = GetLevel(); + 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*)NULL) <= 0){ + dmg = -5; + } + else{ + if(target->CheckHitChance(this, KICK, 0)) { + if(RuleB(Combat, UseIntervalAC)) + dmg = GetKickDamage(); + else + dmg = MakeRandomInt(1, GetKickDamage()); + + } + } + + reuse = KickReuseTime * 1000; + DoSpecialAttackDamage(target, KICK, dmg, 1, -1, reuse); + did_attack = true; + } + else + { + DoAnim(animTailRake); + int32 dmg = 0; + + if(GetWeaponDamage(target, (const Item_Struct*)NULL) <= 0){ + dmg = -5; + } + else{ + if(target->CheckHitChance(this, BASH, 0)) { + if(RuleB(Combat, UseIntervalAC)) + dmg = GetBashDamage(); + else + dmg = MakeRandomInt(1, GetBashDamage()); + } + } + + reuse = BashReuseTime * 1000; + DoSpecialAttackDamage(target, BASH, dmg, 1, -1, reuse); + did_attack = true; + } + } + break; + } + } + + classattack_timer.Start(reuse*HasteModifier/100); +} + +bool Merc::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell) +{ + + _ZP(Client_Attack); + + if (!other) { + SetTarget(NULL); + LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Merc::Attack() for evaluation!"); + return false; + } + + return NPC::Attack(other, Hand, bRiposte, IsStrikethrough, IsFromSpell); +} + +void Merc::Damage(Mob* other, int32 damage, uint16 spell_id, SkillType attack_skill, bool avoidable, int8 buffslot, bool iBuffTic) +{ + if(IsDead() || IsCorpse()) + return; + + if(spell_id==0) + spell_id = SPELL_UNKNOWN; + + NPC::Damage(other, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic); + + //Not needed since we're using NPC damage. + //CommonDamage(other, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic); +} + +bool Merc::FindTarget() { + bool found = false; + Mob* target = GetHateTop(); + + if(target) { + found = true; + SetTarget(target); + } + + return found; +} + +void Merc::SetTarget(Mob* mob) { + NPC::SetTarget(mob); +} + +Mob* Merc::GetOwnerOrSelf() { + Mob* Result = 0; + + if(this->GetMercOwner()) + Result = GetMercOwner(); + else + Result = this; + + return Result; +} + +void Merc::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_skill) +{ + NPC::Death(killerMob, damage, spell, attack_skill); + Save(); + + Mob *give_exp = hate_list.GetDamageTop(this); + Client *give_exp_client = NULL; + + if(give_exp && give_exp->IsClient()) + give_exp_client = give_exp->CastToClient(); + + bool IsLdonTreasure = (this->GetClass() == LDON_TREASURE); + + //if (give_exp_client && !IsCorpse() && MerchantType == 0) + //{ + // Group *kg = entity_list.GetGroupByClient(give_exp_client); + // Raid *kr = entity_list.GetRaidByClient(give_exp_client); + + // if(!kr && give_exp_client->IsClient() && give_exp_client->GetBotRaidID() > 0) { + // BotRaids *br = entity_list.GetBotRaidByMob(give_exp_client->CastToMob()); + // if(br) { + // if(!IsLdonTreasure) + // br->SplitExp((EXP_FORMULA), this); + + // if(br->GetBotMainTarget() == this) + // br->SetBotMainTarget(NULL); + + // /* Send the EVENT_KILLED_MERIT event for all raid members */ + // if(br->BotRaidGroups[0]) { + // for(int j=0; jBotRaidGroups[0]->members[j] && br->BotRaidGroups[0]->members[j]->IsClient()) { + // parse->Event(EVENT_KILLED_MERIT, GetNPCTypeID(), "killed", this, br->BotRaidGroups[0]->members[j]); + // if(RuleB(TaskSystem, EnableTaskSystem)) { + // br->BotRaidGroups[0]->members[j]->CastToClient()->UpdateTasksOnKill(GetNPCTypeID()); + // } + // } + // } + // } + // } + // } + //} + + //corpse->Depop(); + + //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(Suspend()) + { + //todo: perl event? + } +} + +Client* Merc::GetMercOwner() { + Client* mercOwner = 0; + + if(GetOwner()) + { + if(GetOwner()->IsClient()) + { + mercOwner = GetOwner()->CastToClient(); + } + } + + return mercOwner; +} + +Mob* Merc::GetOwner() { + Mob* Result = 0; + + Result = entity_list.GetMob(GetOwnerID()); + + if(!Result) { + this->SetOwnerID(0); + } + + return Result->CastToMob(); +} + +const char* Merc::GetRandomName(){ + // creates up to a 10 char name + static char name[17]; + char vowels[18]="aeiouyaeiouaeioe"; + char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd"; + char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr"; + bool valid = false; + + while(!valid) { + int rndnum=MakeRandomInt(0, 75),n=1; + bool dlc=false; + 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" + rndname[0]=paircons[rndnum]; + rndname[1]=paircons[rndnum+1]; + n=2; + } + else if (rndnum>16) + { + rndnum-=17; + rndname[0]=cons[rndnum]; + } + else + { + rndname[0]=vowels[rndnum]; + vwl=true; + } + int namlen=MakeRandomInt(5, 10); + 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" + rndnum=MakeRandomInt(0, 7)*2; + } + else + { // 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 + i+=1; + } + else + { // select a single cons + rndname[i]=cons[rndnum]; + } + } + else + { // 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 + { + rndname[i+1]=rndname[i]; + dbl=true; + i+=1; + } + } + } + + rndname[0]=toupper(rndname[0]); + + if(!database.CheckNameFilter(rndname)) { + valid = false; + } + else if(rndname[0] < 'A' && rndname[0] > 'Z') { + //name must begin with an upper-case letter. + valid = false; + } + else if (database.CheckUsedName(rndname)) { + valid = true; + } + else { + valid = false; + } + } + + memset(name, 0, 17); + strcpy(name, rndname); + return name; +} + + +bool Compare_Merc_Spells(MercSpell i, MercSpell j); + +bool Compare_Merc_Spells(MercSpell i, MercSpell j) +{ + return(i.slot > j.slot); +} + +bool Merc::LoadMercSpells() { + // loads mercs spells into list + merc_spells.clear(); + + std::list spellList = zone->merc_spells_list[GetClass()]; + + if (spellList.size() == 0) { + AIautocastspell_timer->Disable(); + return false; + } + + uint32 proficiency_id = GetProficiencyID(); + int16 attack_proc_spell = -1; + int8 proc_chance = 0; + + for (std::list::iterator mercSpellEntryItr = spellList.begin(); mercSpellEntryItr != spellList.end(); mercSpellEntryItr++) { + if (proficiency_id == mercSpellEntryItr->proficiencyid && GetLevel() >= mercSpellEntryItr->minlevel && GetLevel() <= mercSpellEntryItr->maxlevel && mercSpellEntryItr->spellid > 0) { + MercSpell mercSpell; + + mercSpell.spellid = mercSpellEntryItr->spellid; + mercSpell.type = mercSpellEntryItr->type; + mercSpell.stance = mercSpellEntryItr->stance; + mercSpell.slot = mercSpellEntryItr->slot; + mercSpell.proc_chance = mercSpellEntryItr->proc_chance; + mercSpell.time_cancast = 0; + + merc_spells.push_back(mercSpell); + + if(mercSpellEntryItr->proc_chance > 0) + AddProcToWeapon(mercSpellEntryItr->spellid, true, mercSpellEntryItr->proc_chance); + } + } + std::sort(merc_spells.begin(), merc_spells.end(), Compare_Merc_Spells); + + if (merc_spells.size() == 0) + AIautocastspell_timer->Disable(); + else { + HasAISpell = true; + AIautocastspell_timer->Trigger(); + } + + if(MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Loaded %i spells for merc.", merc_spells.size()); + + return true; +} + +Merc* Merc::LoadMerc(Client *c, MercTemplate* merc_template, uint32 merchant_id) { + Merc* merc; + + if(c) + { + if(c->GetMercID()) { + merc_template = zone->GetMercTemplate(c->GetEPP().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(npc_type_to_copy != NULL) + { + 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. + memset(npc_type, 0, sizeof(NPCType)); + memcpy(npc_type, npc_type_to_copy, sizeof(NPCType)); + if(c) + { + if(c->GetEPP().merc_name[0] == 0) + { + snprintf(c->GetEPP().merc_name, 64, "%s", GetRandomName()); //sanity check. + } + snprintf(npc_type->name, 64, "%s", c->GetEPP().merc_name); + } + uint8 gender; + if(merchant_id > 0) { + NPC* tar = entity_list.GetNPCByID(merchant_id); + if(tar) { + gender = Mob::GetDefaultGender(npc_type->race, tar->GetGender()); + } + } + else { + gender = c->GetEPP().mercGender; + } + + sprintf(npc_type->lastname, "%s's %s", c->GetName(), "Mercenary"); + npc_type->gender = gender; + 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); + merc->SetMercData( merc_template->MercTemplateID ); + merc->UpdateMercStats(c); + return merc; + } + } + + return 0; +} + +void Merc::UpdateMercStats(Client *c) { + if(c->GetEPP().mercTemplateID >0) + { + const NPCType* npc_type = database.GetMercType( zone->GetMercTemplate(c->GetEPP().mercTemplateID)->MercNPCID, GetRace(), c->GetLevel()); + if (npc_type) + { + max_hp = (npc_type->max_hp * npc_type->scalerate) / 100; + base_hp = (npc_type->max_hp * npc_type->scalerate) / 100; + max_mana = (npc_type->max_hp * npc_type->scalerate) / 100; + base_mana = (npc_type->max_hp * npc_type->scalerate) / 100; + hp_regen = (npc_type->hp_regen * npc_type->scalerate) / 100; + mana_regen = (npc_type->mana_regen * npc_type->scalerate) / 100; + level = npc_type->level; + max_dmg = (npc_type->max_dmg * npc_type->scalerate) / 100; + min_dmg = (npc_type->min_dmg * npc_type->scalerate) / 100; + _baseSTR = (npc_type->STR * npc_type->scalerate) / 100; + _baseSTA = (npc_type->STA * npc_type->scalerate) / 100; + _baseDEX = (npc_type->DEX * npc_type->scalerate) / 100; + _baseAGI = (npc_type->AGI * npc_type->scalerate) / 100; + _baseWIS = (npc_type->WIS * npc_type->scalerate) / 100; + _baseINT = (npc_type->INT * npc_type->scalerate) / 100; + _baseCHA = (npc_type->CHA * npc_type->scalerate) / 100; + _baseATK = (npc_type->ATK * npc_type->scalerate) / 100; + _baseMR = (npc_type->MR * npc_type->scalerate) / 100; + _baseFR = (npc_type->FR * npc_type->scalerate) / 100; + _baseDR = (npc_type->DR * npc_type->scalerate) / 100; + _basePR = (npc_type->PR * npc_type->scalerate) / 100; + _baseCR = (npc_type->CR * npc_type->scalerate) / 100; + _baseCorrup = (npc_type->Corrup * npc_type->scalerate) / 100; + _baseAC = (npc_type->AC * npc_type->scalerate) / 100; + attack_speed = npc_type->attack_speed; + attack_count = npc_type->attack_count; + spellscale = npc_type->spellscale; + healscale = npc_type->healscale; + + CalcBonuses(); + + CalcMaxEndurance(); + CalcMaxHP(); + CalcMaxMana(); + } + } +} + +void Merc::UpdateMercAppearance(Client *c) { +} + +bool Merc::Spawn(Client *owner) { + if(!RuleB(Mercs, AllowMercs)) + return false; + + if(!owner) + return false; + + MercTemplate* merc_template = zone->GetMercTemplate(GetMercTemplateID()); + + if(!merc_template) + return false; + + entity_list.AddMerc(this, true, true); + + SendPosition(); + + //printf("Spawned Merc with ID %i\n", npc->GetID()); fflush(stdout); + + /* + uint32 itemID = 0; + uint8 materialFromSlot = 0xFF; + for(int i=0; i<22; ++i) { + itemID = GetMercItemBySlot(i); + if(itemID != 0) { + materialFromSlot = Inventory::CalcMaterialFromSlot(i); + if(materialFromSlot != 0xFF) { + this->SendWearChange(materialFromSlot); + } + } + } + */ + + return true; +} + +void Client::UpdateMercTimer() +{ + Merc *merc = GetMerc(); + + if(merc && !merc->IsSuspended()) + { + if(merc_timer.Check()) + { + uint32 upkeep = Merc::CalcUpkeepCost(merc->GetMercTemplateID(), GetLevel()); + //TakeMoneyFromPP((upkeep * 100), true); // Upkeep is in gold + GetEPP().mercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); + SendMercTimerPacket(GetMercID(), 5, 0, RuleI(Mercs, UpkeepIntervalMS), RuleI(Mercs, SuspendIntervalMS)); + merc_timer.Start(RuleI(Mercs, UpkeepIntervalMS)); + + // Send upkeep charge message and reset the upkeep timer + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(10); + else + SendMercMerchantResponsePacket(11); + + /* + 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); + */ + + // 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)); + } + } +} + +void Client::CheckMercSuspendTimer() +{ + if(GetEPP().mercSuspendedTime != 0) { + if(time(NULL) >= GetEPP().mercSuspendedTime){ + GetEPP().mercSuspendedTime = 0; + SendMercSuspendResponsePacket(GetEPP().mercSuspendedTime); + p_timers.Start(pTimerMercSuspend, RuleI(Mercs, SuspendIntervalS)); + } + } +} + +void Client::SuspendMercCommand() +{ + bool ExistsMerc = GetEPP().mercTemplateID != 0; + if(ExistsMerc == true) + { + if(GetEPP().mercIsSuspended) { + //p_timers.Enable(pTimerMercReuse); + + // Set time remaining to max on unsuspend - there is a charge for unsuspending as well + // GetEPP().mercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); + + // Get merc, assign it to client & spawn + Merc* merc = Merc::LoadMerc(this, &zone->merc_templates[GetEPP().mercTemplateID], 0); + SpawnMerc(merc); + } + else + { + Merc* CurrentMerc = GetMerc(); + + if(CurrentMerc && GetMercID()) { + CurrentMerc->Suspend(); + } + } + } +} + + +// Handles all client zone change event +void Merc::ProcessClientZoneChange(Client* mercOwner) { + if(mercOwner) + { + Zone(); + } +} + +void Client::SpawnMercOnZone() +{ + if(!RuleB(Mercs, AllowMercs)) + return; + + if (GetMerc()) + return; + + bool ExistsMerc = GetEPP().mercTemplateID != 0; + if(ExistsMerc == true) + { + if(!GetEPP().mercIsSuspended) { + GetEPP().mercSuspendedTime = 0; + // Get merc, assign it to client & spawn + Merc* merc = Merc::LoadMerc(this, &zone->merc_templates[GetEPP().mercTemplateID], 0); + SpawnMerc(merc); + } + else + { + // Send Mercenary Status/Timer packet + SendMercTimerPacket(0, 1, GetEPP().mercSuspendedTime, RuleI(Mercs, UpkeepIntervalMS), RuleI(Mercs, SuspendIntervalMS)); + + SendMercPersonalInfo(); + + if(GetEPP().mercSuspendedTime != 0) { + if(time(NULL) >= GetEPP().mercSuspendedTime){ + GetEPP().mercSuspendedTime = 0; + } + } + SendMercSuspendResponsePacket(GetEPP().mercSuspendedTime); + } + } +} + +void Client::SpawnMerc(Merc* merc) +{ + if(!RuleB(Mercs, AllowMercs)) + return; + + if(merc) { + merc->Spawn(this); + merc->SetSuspended(false); + SetMerc(merc); + merc->Unsuspend(); + } +} + +bool Merc::Suspend() { + Client* mercOwner; + + if(GetMercOwner()) { + mercOwner = GetMercOwner(); + } + + if(!mercOwner) + return false; + + SetSuspended(true); + + if(HasGroup()) { + RemoveMercFromGroup(this, GetGroup()); + } + + //Save(); + + mercOwner->GetEPP().mercIsSuspended = true; + mercOwner->GetEPP().mercSuspendedTime = time(NULL) + RuleI(Mercs, SuspendIntervalS); + mercOwner->GetEPP().mercTimerRemaining = mercOwner->GetMercTimer().GetRemainingTime(); + mercOwner->GetMercTimer().Disable(); + //mercOwner->UpdateMercTimer(); + mercOwner->SendMercSuspendResponsePacket(mercOwner->GetEPP().mercSuspendedTime); + + Depop(); + + return true; +} + +bool Merc::Unsuspend() { + Client* mercOwner; + + if(GetMercOwner()) { + mercOwner = GetMercOwner(); + } + + if(!mercOwner) + return false; + + if(GetMercID()) { + uint32 entityID = 0; + uint32 mercState = 5; + uint32 suspendedTime = 0; + + SetSuspended(false); + mercOwner->GetEPP().mercIsSuspended = false; + mercOwner->GetEPP().mercSuspendedTime = 0; + + mercOwner->SendMercenaryUnsuspendPacket(0); + mercOwner->SendMercenaryUnknownPacket(1); + + mercOwner->SendMercTimerPacket(GetMercID(), mercState, suspendedTime, RuleI(Mercs, UpkeepIntervalMS), RuleI(Mercs, SuspendIntervalMS)); + + mercOwner->GetMercTimer().Start(mercOwner->GetEPP().mercTimerRemaining); + + if(!mercOwner->GetPTimers().Expired(&database, pTimerMercSuspend, false)) + mercOwner->GetPTimers().Clear(&database, pTimerMercSuspend); + + mercOwner->SendMercPersonalInfo(); + + if(!mercOwner->IsGrouped()) + { + Group *g = new Group(mercOwner); + if(AddMercToGroup(this, g)) + { + entity_list.AddGroup(g); + 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(); + } + } + else if (AddMercToGroup(this, mercOwner->GetGroup())) + { + database.SetGroupID(GetName(), mercOwner->GetGroup()->GetID(), mercOwner->CharacterID(), true); + database.RefreshGroupFromDB(mercOwner); + } + else + { + if(MERC_DEBUG > 0) + mercOwner->Message(7, "Mercenary failed to join the group - Suspending"); + + Suspend(); + } + + LoadMercSpells(); + } + + return true; +} + +bool Merc::Dismiss() { + Client* mercOwner; + + if(GetMercOwner()) { + mercOwner = GetMercOwner(); + } + + if(!mercOwner) + return false; + + mercOwner->SendClearMercInfo(); + mercOwner->SendMercTimerPacket(GetMercID(), 5, 0, RuleI(Mercs, UpkeepIntervalMS), RuleI(Mercs, SuspendIntervalMS)); + + SetMercID(0); + + mercOwner->SetMerc(0); + + Depop(); + + return true; +} + +void Merc::Zone() { + //Save(); + Depop(); +} + +void Merc::Depop() { + WipeHateList(); + + entity_list.RemoveFromHateLists(this); + + if(HasGroup()) + RemoveMercFromGroup(this, GetGroup()); + + if(HasPet()) { + GetPet()->Depop(); + } + + SetOwnerID(0); + + p_depop = true; + + NPC::Depop(false); +} + +bool Merc::RemoveMercFromGroup(Merc* merc, Group* group) { + bool Result = false; + + if(merc && group) { + if(merc->HasGroup()) { + if(!group->IsLeader(merc)) { + merc->SetFollowID(0); + + if(group->DelMember(merc)) { + if(merc->GetMercCharacterID() != 0) + database.SetGroupID(merc->GetName(), 0, merc->GetMercCharacterID(), true); + } + + if(group->GroupCount() <= 1 && ZoneLoaded) + { + group->DisbandGroup(); + } + } + else { + 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->DisbandGroup(); + database.SetGroupID(merc->GetCleanName(), 0, merc->GetMercCharacterID(), true); + } + + Result = true; + } + } + + return Result; +} + +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()); + } + // Add merc to this group + if(group->AddMember(merc)) { + merc->SetFollowID(merc->GetMercOwner()->GetID()); + Result = true; + } + else + { + merc->Suspend(); + } + + } + + return Result; +} + +Merc* Client::GetMerc() { + if(GetMercID() == 0) + return(NULL); + + Merc* tmp = entity_list.GetMercByID(GetMercID()); + if(tmp == NULL) { + SetMercID(0); + return(NULL); + } + + if(tmp->GetOwnerID() != GetID()) { + SetMercID(0); + return(NULL); + } + + return(tmp); +} + +void Merc::SetMercData( uint32 template_id ) { + MercTemplate* merc_template = zone->GetMercTemplate(template_id); + SetMercTemplateID( merc_template->MercTemplateID ); + SetMercType( merc_template->MercType ); + SetMercSubType( merc_template->MercSubType ); + SetProficiencyID( merc_template->ProficiencyID ); + SetCostFormula( merc_template->CostFormula ); + SetMercNameType( merc_template->MercNameType ); +} + +MercTemplate* Zone::GetMercTemplate( uint32 template_id ) { + return &merc_templates[template_id]; +} + +void Client::SetMerc(Merc* newmerc) { + Merc* oldmerc = GetMerc(); + if (oldmerc) { + oldmerc->SetOwnerID(0); + } + if (!newmerc) { + SetMercID(0); + GetEPP().mercTemplateID = 0; + GetEPP().mercIsSuspended = false; + GetEPP().mercSuspendedTime = 0; + GetEPP().mercGender = 0; + GetEPP().mercState = 0; + GetEPP().mercTimerRemaining = 0; + memset(GetEPP().merc_name, 0, 64); + } else { + SetMercID(newmerc->GetID()); + newmerc->SetMercID(newmerc->GetID()); + //Client* oldowner = entity_list.GetClientByID(newmerc->GetOwnerID()); + newmerc->SetOwnerID(this->GetID()); + newmerc->SetMercCharacterID(this->CharacterID()); + newmerc->SetClientVersion((uint8)this->GetClientVersion()); + GetEPP().mercTemplateID = newmerc->GetMercTemplateID(); + GetEPP().mercIsSuspended = newmerc->IsSuspended(); + GetEPP().mercSuspendedTime = 0; + GetEPP().mercGender = newmerc->GetGender(); + } +} + +void Client::UpdateMercLevel() { + Merc* merc = GetMerc(); + if (merc) { + merc->UpdateMercStats(this); + } +} + +void Client::SendMercMerchantResponsePacket(int32 response_type) { + // This response packet brings up the Mercenary Manager window + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryHire, sizeof(MercenaryMerchantResponse_Struct)); + MercenaryMerchantResponse_Struct* mmr = (MercenaryMerchantResponse_Struct*)outapp->pBuffer; + mmr->ResponseType = response_type; // send specified response type + + DumpPacket(outapp); + FastQueuePacket(&outapp); +} + +void Client::SendMercenaryUnknownPacket(uint8 type) { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryUnknown1, 1); + outapp->WriteUInt8(type); + + DumpPacket(outapp); + FastQueuePacket(&outapp); +} + +void Client::SendMercenaryUnsuspendPacket(uint8 type) { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryUnsuspendResponse, 1); + outapp->WriteUInt8(type); + + DumpPacket(outapp); + FastQueuePacket(&outapp); +} + +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 + + DumpPacket(outapp); + FastQueuePacket(&outapp); +} + +void Client::SendMercTimerPacket(int32 entity_id, int32 merc_state, int32 suspended_time, int32 update_interval, int32 unk01) { + + if (GetClientVersion() == EQClientSoD) { + update_interval = GetEPP().mercTimerRemaining; + } + + // Send Mercenary Status/Timer packet + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryTimer, sizeof(MercenaryStatus_Struct)); + MercenaryStatus_Struct* mss = (MercenaryStatus_Struct*)outapp->pBuffer; + mss->MercEntityID = entity_id; // Seen 0 (no merc spawned) or unknown value when merc is spawned + 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 + mss->MercState = merc_state; // Seen 5 (normal) or 1 (suspended) + mss->SuspendedTime = suspended_time; // Seen 0 for not suspended or Unix Timestamp for suspended merc + + DumpPacket(outapp); + FastQueuePacket(&outapp); +} + +void Client::SendMercAssignPacket(uint32 entityID, uint32 unk01, uint32 unk02) { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryAssign, sizeof(MercenaryAssign_Struct)); + MercenaryAssign_Struct* mas = (MercenaryAssign_Struct*)outapp->pBuffer; + mas->MercEntityID = entityID; + mas->MercUnk01 = unk01; + mas->MercUnk02 = unk02; + FastQueuePacket(&outapp); +} + +void NPC::LoadMercTypes(){ + std::string errorMessage; + char* Query = 0; + char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + MYSQL_RES* DatasetResult; + MYSQL_ROW DataRow; + + 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(Query); + Query = 0; + + if(!errorMessage.empty()) { + LogFile->write(EQEMuLog::Error, "Error in NPC::LoadMercTypes()"); + } +} + +void NPC::LoadMercs(){ + + std::string errorMessage; + char* Query = 0; + char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + MYSQL_RES* DatasetResult; + MYSQL_ROW DataRow; + + 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(Query); + Query = 0; + + if(!errorMessage.empty()) { + LogFile->write(EQEMuLog::Error, "Error in NPC::LoadMercTypes()"); + } +} + +int NPC::GetNumMercTypes(uint32 clientVersion) +{ + int count = 0; + std::list mercTypeList = GetMercTypesList(); + + for(std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); mercTypeListItr++) { + if(mercTypeListItr->ClientVersion <= clientVersion) + count++; + } + + return count; +} + +int NPC::GetNumMercs(uint32 clientVersion) +{ + int count = 0; + std::list mercDataList = GetMercsList(); + + for(std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); mercListItr++) { + if(mercListItr->ClientVersion <= clientVersion) + count++; + } + + return count; +} + +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) { + MercType mercType; + mercType.Type = mercTypeListItr->Type; + mercType.ClientVersion = mercTypeListItr->ClientVersion; + result.push_back(mercType); + } + } + } + + return result; +} + +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) { + MercTemplate *merc_template = zone->GetMercTemplate(mercListItr->MercTemplateID); + + if(merc_template) { + MercData mercData; + mercData.MercTemplateID = mercListItr->MercTemplateID; + mercData.MercType = merc_template->MercType; + mercData.MercSubType = merc_template->MercSubType; + mercData.CostFormula = merc_template->CostFormula; + mercData.ClientVersion = merc_template->ClientVersion; + mercData.NPCID = merc_template->MercNPCID; + result.push_back(mercData); + } + } + } + } + + return result; +} + +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 + 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 = 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 + cost = 1; + } + } + + return cost/100; +} \ No newline at end of file diff --git a/zone/merc.h b/zone/merc.h new file mode 100644 index 000000000..f5ef75360 --- /dev/null +++ b/zone/merc.h @@ -0,0 +1,352 @@ +#ifndef MERC_H +#define MERC_H +#include "mob.h" +#include "zonedb.h" +#include "npc.h" +using namespace std; + +#define MERC_DEBUG 0 +#define TANK 1 +#define HEALER 2 +#define MELEEDPS 9 +#define CASTERDPS 12 +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 { + MercStancePassive = 1, + MercStanceBalanced, + MercStanceEfficient, + MercStanceReactive, + MercStanceAggressive, + MercStanceAssist, + MercStanceBurn, + MercStanceEfficient2, + MercStanceBurnAE +}; + +struct MercSpell { + uint16 spellid; // <= 0 = no spell + uint16 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 +}; + +class Merc : public NPC { +public: + Merc(const NPCType* d, float x, float y, float z, float heading); + virtual ~Merc(); + + //abstract virtual function implementations requird by base abstract class + virtual void Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillType attack_skill); + virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false); + virtual bool Attack(Mob* other, int Hand = SLOT_PRIMARY, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false); + virtual bool HasRaid() { return false; } + virtual bool HasGroup() { return (GetGroup() ? true : false); } + virtual Raid* GetRaid() { return 0; } + virtual Group* GetGroup() { return entity_list.GetGroupByMob(this); } + + // Mob AI Virtual Override Methods + virtual void AI_Start(int32 iMoveDelay = 0); + virtual void AI_Stop(); + virtual void AI_Process(); + + //virtual bool AICastSpell(Mob* tar, int8 iChance, int16 iSpellTypes); + virtual bool AICastSpell(int8 iChance, int16 iSpellTypes); + virtual bool AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0); + virtual bool AI_EngagedCastCheck(); + //virtual bool AI_PursueCastCheck(); + virtual bool AI_IdleCastCheck(); + + virtual bool Process(); + + // Static Merc Group Methods + static bool AddMercToGroup(Merc* merc, Group* group); + static bool RemoveMercFromGroup(Merc* merc, Group* group); + void ProcessClientZoneChange(Client* mercOwner); + static void MercGroupSay(Mob *speaker, const char *msg, ...); + + // Merc Spell Casting Methods + int8 GetChanceToCastBySpellType(int16 spellType); + void SetSpellRecastTimer(uint16 timer_id, uint16 spellid, uint32 recast_delay); + void SetDisciplineRecastTimer(uint16 timer_id, uint16 spellid, uint32 recast_delay); + static int32 GetSpellRecastTimer(Merc *caster, uint16 timer_id); + static bool CheckSpellRecastTimers(Merc *caster, uint16 spellid); + static int32 GetDisciplineRecastTimer(Merc *caster, uint16 timer_id); + static bool CheckDisciplineRecastTimers(Merc *caster, uint16 spellid); + static int32 GetDisciplineRemainingTime(Merc *caster, uint16 timer_id); + static std::list GetMercSpellsForSpellEffect(Merc* caster, int spellEffect); + static std::list GetMercSpellsForSpellEffectAndTargetType(Merc* caster, int spellEffect, SpellTargetType targetType); + static std::list GetMercSpellsBySpellType(Merc* caster, int spellType); + static MercSpell GetFirstMercSpellBySpellType(Merc* caster, int spellType); + static MercSpell GetFirstMercSpellForSingleTargetHeal(Merc* caster); + static MercSpell GetMercSpellBySpellID(Merc* caster, uint16 spellid); + static MercSpell GetBestMercSpellForVeryFastHeal(Merc* caster); + static MercSpell GetBestMercSpellForFastHeal(Merc* caster); + static MercSpell GetBestMercSpellForHealOverTime(Merc* caster); + static MercSpell GetBestMercSpellForPercentageHeal(Merc* caster); + static MercSpell GetBestMercSpellForRegularSingleTargetHeal(Merc* caster); + static MercSpell GetBestMercSpellForGroupHealOverTime(Merc* caster); + static MercSpell GetBestMercSpellForGroupCompleteHeal(Merc* caster); + static MercSpell GetBestMercSpellForGroupHeal(Merc* caster); + static MercSpell GetBestMercSpellForAETaunt(Merc* caster); + static MercSpell GetBestMercSpellForTaunt(Merc* caster); + static MercSpell GetBestMercSpellForHate(Merc* caster); + bool UseDiscipline(int32 spell_id, int32 target); + + virtual bool IsMerc() const { return true; } + + virtual void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); + static Merc* LoadMerc(Client *c, MercTemplate* merc_template, uint32 merchant_id); + void UpdateMercStats(Client *c); + void UpdateMercAppearance(Client *c); + static const char *GetRandomName(); + bool Spawn(Client *owner); + bool Dismiss(); + bool Suspend(); + bool Unsuspend(); + void Zone(); + virtual void Depop(); + bool GetDepop() { return p_depop; } + + bool IsDead() { return GetHP() < 0;}; + 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); + + // "GET" Class Methods + virtual Mob* GetOwner(); + Client* GetMercOwner(); + virtual Mob* GetOwnerOrSelf(); + uint32 GetMercID() { return _MercID; } + uint32 GetMercCharacterID( ) { return owner_char_id; } + uint32 GetMercTemplateID() { return _MercTemplateID; } + uint32 GetMercType() { return _MercType; } + uint32 GetMercSubType() { return _MercSubType; } + uint32 GetProficiencyID() { return _ProficiencyID; } + uint32 GetCostFormula() { return _CostFormula; } + uint32 GetMercNameType() { return _NameType; } + uint32 GetStance() { return _currentStance; } + + inline const uint8 GetClientVersion() const { return _OwnerClientVersion; } + + virtual void SetTarget(Mob* mob); + bool HasSkill(SkillType skill_id) const; + bool CanHaveSkill(SkillType skill_id) const; + uint16 MaxSkill(SkillType skillid, uint16 class_, uint16 level) const; + inline uint16 MaxSkill(SkillType skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } + virtual void DoClassAttacks(Mob *target); + bool CheckTaunt(); + bool CheckAETaunt(); + + // stat functions + virtual void CalcBonuses(); + 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; + + 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 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; } + // 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; } + // 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 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 int16 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; } + + // "SET" Class Methods + void SetMercData (uint32 templateID ); + void SetMercID( uint32 mercID ) { _MercID = mercID; } + void SetMercCharacterID( uint32 mercID ) { owner_char_id = mercID; } + void SetMercTemplateID( uint32 templateID ) { _MercTemplateID = templateID; } + void SetMercType( uint32 type ) { _MercType = type; } + void SetMercSubType( uint32 subtype ) { _MercSubType = subtype; } + void SetProficiencyID( uint8 proficiency_id ) { _ProficiencyID = proficiency_id; } + void SetCostFormula( uint8 costformula ) { _CostFormula = costformula; } + void SetMercNameType( uint8 nametype ) { _NameType = nametype; } + void SetClientVersion(uint8 clientVersion) { _OwnerClientVersion = clientVersion; } + void SetSuspended(bool suspended) { _suspended = suspended; } + void SetStance( uint32 stance ) { _currentStance = stance; } + + void Sit(); + void Stand(); + bool IsSitting(); + bool IsStanding(); + + // Merc-specific functions + bool IsMercCaster() { return (GetClass() == CLERIC || GetClass() == DRUID || GetClass() == SHAMAN || GetClass() == NECROMANCER || GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == ENCHANTER); } + bool IsMercCasterCombatRange(Mob *target); + virtual float GetMaxMeleeRangeToTarget(Mob* target); + virtual void MercMeditate(bool isSitting); + bool FindTarget(); + +protected: + void CalcItemBonuses(StatBonuses* newbon); + void AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug = false, bool isTribute = false); + int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat); + + std::vector merc_spells; + std::map timers; + + uint16 skills[HIGHEST_SKILL+1]; + uint32 equipment[MAX_WORN_INVENTORY]; //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(); + + 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(); + 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 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 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(); + + void GenerateBaseStats(); + void GenerateAppearance(); + + bool LoadMercSpells(); + bool CheckStance(int16 stance); + std::vector GetMercSpells() { return merc_spells; } + + // 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; + uint32 RestRegenHP; + uint32 RestRegenMana; + uint32 RestRegenEndurance; + + uint32 _MercID; + uint32 _MercTemplateID; + uint32 _MercType; + uint32 _MercSubType; + uint8 _ProficiencyID; + uint8 _CostFormula; + uint8 _NameType; + uint8 _OwnerClientVersion; + uint32 _currentStance; + + Inventory m_inv; + int32 max_end; + int32 cur_end; + bool _medding; + bool _suspended; + bool p_depop; + uint32 owner_char_id; + const NPCType* ourNPCData; + + Timer endupkeep_timer; + Timer rest_timer; +}; + +#endif // MERC_H \ No newline at end of file diff --git a/zone/message.h b/zone/message.h new file mode 100644 index 000000000..8f8dd5c8f --- /dev/null +++ b/zone/message.h @@ -0,0 +1,7 @@ +struct Msg { + int id; + char* Sender; + char* Subject; + char* Body; + char* Date; +}; diff --git a/zone/mob.cpp b/zone/mob.cpp new file mode 100644 index 000000000..410285e63 --- /dev/null +++ b/zone/mob.cpp @@ -0,0 +1,4479 @@ +/* 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 "../common/debug.h" +#include "masterentity.h" +#include "spdat.h" +#include "StringIDs.h" +#include "worldserver.h" +#include "QuestParserCollection.h" + +#include +#include +#include + +extern EntityList entity_list; +#if !defined(NEW_LoadSPDat) && !defined(DB_LoadSPDat) + extern SPDat_Spell_Struct spells[SPDAT_RECORDS]; +#endif +extern bool spells_loaded; + +extern Zone* zone; +extern WorldServer worldserver; + +Mob::Mob(const char* in_name, + const char* in_lastname, + int32 in_cur_hp, + int32 in_max_hp, + uint8 in_gender, + uint16 in_race, + uint8 in_class, + bodyType in_bodytype, + uint8 in_deity, + uint8 in_level, + uint32 in_npctype_id, + float in_size, + float in_runspeed, + float in_heading, + float in_x_pos, + float in_y_pos, + float in_z_pos, + + uint8 in_light, + uint8 in_texture, + uint8 in_helmtexture, + uint16 in_ac, + uint16 in_atk, + uint16 in_str, + uint16 in_sta, + uint16 in_dex, + uint16 in_agi, + uint16 in_int, + uint16 in_wis, + uint16 in_cha, + uint8 in_haircolor, + uint8 in_beardcolor, + uint8 in_eyecolor1, // the eyecolors always seem to be the same, maybe left and right eye? + uint8 in_eyecolor2, + uint8 in_hairstyle, + uint8 in_luclinface, + uint8 in_beard, + uint32 in_drakkin_heritage, + uint32 in_drakkin_tattoo, + uint32 in_drakkin_details, + uint32 in_armor_tint[MAX_MATERIALS], + + uint8 in_aa_title, + uint8 in_see_invis, // see through invis/ivu + uint8 in_see_invis_undead, + uint8 in_see_hide, + uint8 in_see_improved_hide, + int32 in_hp_regen, + int32 in_mana_regen, + uint8 in_qglobal, + uint8 in_maxlevel, + uint32 in_scalerate + ) : + attack_timer(2000), + attack_dw_timer(2000), + ranged_timer(2000), + tic_timer(6000), + mana_timer(2000), + spellend_timer(0), + rewind_timer(30000), //Timer used for determining amount of time between actual player position updates for /rewind. + stunned_timer(0), + spun_timer(0), + bardsong_timer(6000), + flee_timer(FLEE_CHECK_TIMER), + bindwound_timer(10000), + gravity_timer(1000), + viral_timer(0) + +{ + targeted = 0; + tar_ndx=0; + tar_vector=0; + tar_vx=0; + tar_vy=0; + tar_vz=0; + tarx=0; + tary=0; + tarz=0; + fear_walkto_x = -999999; + fear_walkto_y = -999999; + fear_walkto_z = -999999; + curfp = false; + + AI_Init(); + SetMoving(false); + moved=false; + rewind_x = 0; //Stored x_pos for /rewind + rewind_y = 0; //Stored y_pos for /rewind + rewind_z = 0; //Stored z_pos for /rewind + move_tic_count = 0; + + _egnode = NULL; + adverrorinfo = 0; + name[0]=0; + orig_name[0]=0; + clean_name[0]=0; + lastname[0]=0; + if(in_name) { + strn0cpy(name,in_name,64); + strn0cpy(orig_name,in_name,64); + } + if(in_lastname) + strn0cpy(lastname,in_lastname,64); + cur_hp = in_cur_hp; + max_hp = in_max_hp; + base_hp = in_max_hp; + gender = in_gender; + race = in_race; + base_gender = in_gender; + base_race = in_race; + class_ = in_class; + bodytype = in_bodytype; + orig_bodytype = in_bodytype; + deity = in_deity; + level = in_level; + npctype_id = in_npctype_id; + size = in_size; + base_size = size; + runspeed = in_runspeed; + + + // sanity check + if (runspeed < 0 || runspeed > 20) + runspeed = 1.25f; + + heading = in_heading; + x_pos = in_x_pos; + y_pos = in_y_pos; + z_pos = in_z_pos; + light = in_light; + texture = in_texture; + helmtexture = in_helmtexture; + haircolor = in_haircolor; + beardcolor = in_beardcolor; + eyecolor1 = in_eyecolor1; + eyecolor2 = in_eyecolor2; + hairstyle = in_hairstyle; + luclinface = in_luclinface; + beard = in_beard; + drakkin_heritage = in_drakkin_heritage; + drakkin_tattoo = in_drakkin_tattoo; + drakkin_details = in_drakkin_details; + attack_speed= 0; + slow_mitigation= 0; + findable = false; + trackable = true; + + if(in_aa_title>0) + aa_title = in_aa_title; + else + aa_title =0xFF; + AC = in_ac; + ATK = in_atk; + STR = in_str; + STA = in_sta; + DEX = in_dex; + AGI = in_agi; + INT = in_int; + WIS = in_wis; + CHA = in_cha; + MR = CR = FR = DR = PR = Corrup = 0; + + ExtraHaste = 0; + bEnraged = false; + + shield_target = NULL; + cur_mana = 0; + max_mana = 0; + hp_regen = in_hp_regen; + mana_regen = in_mana_regen; + oocregen = RuleI(NPC, OOCRegen); //default Out of Combat Regen + maxlevel = in_maxlevel; + scalerate = in_scalerate; + invisible = false; + invisible_undead = false; + invisible_animals = false; + sneaking = false; + hidden = false; + improved_hidden = false; + invulnerable = false; + IsFullHP = (cur_hp == max_hp); + qglobal=0; + + InitializeBuffSlots(); + + // clear the proc arrays + int i; + int j; + for (j = 0; j < MAX_PROCS; j++) + { + PermaProcs[j].spellID = SPELL_UNKNOWN; + PermaProcs[j].chance = 0; + PermaProcs[j].base_spellID = SPELL_UNKNOWN; + SpellProcs[j].spellID = SPELL_UNKNOWN; + + DefensiveProcs[j].spellID = SPELL_UNKNOWN; + DefensiveProcs[j].chance = 0; + DefensiveProcs[j].base_spellID = SPELL_UNKNOWN; + RangedProcs[j].spellID = SPELL_UNKNOWN; + RangedProcs[j].chance = 0; + RangedProcs[j].base_spellID = SPELL_UNKNOWN; + SkillProcs[j].spellID = SPELL_UNKNOWN; + SkillProcs[j].chance = 0; + SkillProcs[j].base_spellID = SPELL_UNKNOWN; + } + + for (i = 0; i < MAX_MATERIALS; i++) + { + if (in_armor_tint) + { + armor_tint[i] = in_armor_tint[i]; + } + else + { + armor_tint[i] = 0; + } + } + + delta_heading = 0; + delta_x = 0; + delta_y = 0; + delta_z = 0; + animation = 0; + + logging_enabled = false; + isgrouped = false; + israidgrouped = false; + islooting = false; + _appearance = eaStanding; + pRunAnimSpeed = 0; + + spellend_timer.Disable(); + bardsong_timer.Disable(); + bardsong = 0; + bardsong_target_id = 0; + casting_spell_id = 0; + casting_spell_timer = 0; + casting_spell_timer_duration = 0; + casting_spell_type = 0; + target = 0; + + memset(&itembonuses, 0, sizeof(StatBonuses)); + memset(&spellbonuses, 0, sizeof(StatBonuses)); + //memset(&aabonuses, 0, sizeof(StatBonuses)); //don't need this until we start using Client::CalcAABonuses() + spellbonuses.AggroRange = -1; + spellbonuses.AssistRange = -1; + pLastChange = 0; + SetPetID(0); + SetOwnerID(0); + typeofpet = petCharmed; //default to charmed... + petpower = 0; + held = false; + nocast = false; + focused = false; + + attacked_count = 0; + mezzed = false; + stunned = false; + silenced = false; + amnesiad = false; + inWater = false; + int m; + for (m = 0; m < MAX_SHIELDERS; m++) + { + shielder[m].shielder_id = 0; + shielder[m].shielder_bonus = 0; + } + for (i=0; i 0) ? false : true; + + movetimercompleted = false; + roamer = false; + rooted = false; + charmed = false; + has_virus = false; + for (i=0; i 0) + entity_list.RemoveMob(GetID()); + + else + entity_list.RemoveMob(this); + + AI_Stop(); + if (GetPet()) { + if (GetPet()->Charmed()) + GetPet()->BuffFadeByEffect(SE_Charm); + else + SetPet(0); + } + for (int i=0; iIsPlayerCorpse())) + entity_list.QueueClients(this, &app, true); + + entity_list.RemoveFromTargets(this, true); + + if(trade) { + Mob *with = trade->With(); + if(with && with->IsClient()) { + with->CastToClient()->FinishTrade(with); + with->trade->Reset(); + } + delete trade; + } + + if(HadTempPets()){ + entity_list.DestroyTempPets(this); + } + entity_list.UnMarkNPC(GetID()); + safe_delete(PathingLOSCheckTimer); + safe_delete(PathingRouteUpdateTimerShort); + safe_delete(PathingRouteUpdateTimerLong); + UninitializeBuffSlots(); +} + +uint32 Mob::GetAppearanceValue(EmuAppearance iAppearance) { + switch (iAppearance) { + // 0 standing, 1 sitting, 2 ducking, 3 lieing down, 4 looting + case eaStanding: { + return ANIM_STAND; + } + case eaSitting: { + return ANIM_SIT; + } + case eaCrouching: { + return ANIM_CROUCH; + } + case eaDead: { + return ANIM_DEATH; + } + case eaLooting: { + return ANIM_LOOT; + } + //to shup up compiler: + case _eaMaxAppearance: + break; + } + return(ANIM_STAND); +} + +void Mob::SetInvisible(uint8 state) +{ + invisible = state; + SendAppearancePacket(AT_Invis, invisible); + // Invis and hide breaks charms + + if ((this->GetPetType() == petCharmed) && (invisible || hidden || improved_hidden)) + { + Mob* formerpet = this->GetPet(); + + if(formerpet) + formerpet->BuffFadeByEffect(SE_Charm); + } +} + +//check to see if `this` is invisible to `other` +bool Mob::IsInvisible(Mob* other) const +{ + if(!other) + return(false); + + uint8 SeeInvisBonus = 0; + if (IsClient()) + SeeInvisBonus = aabonuses.SeeInvis; + + //check regular invisibility + if (invisible && invisible > (other->SeeInvisible())) + return true; + + //check invis vs. undead + if (other->GetBodyType() == BT_Undead || other->GetBodyType() == BT_SummonedUndead) { + if(invisible_undead && !other->SeeInvisibleUndead()) + return true; + } + + //check invis vs. animals... + if (other->GetBodyType() == BT_Animal){ + if(invisible_animals && !other->SeeInvisible()) + return true; + } + + if(hidden){ + if(!other->see_hide && !other->see_improved_hide){ + return true; + } + } + + if(improved_hidden){ + if(!other->see_improved_hide){ + return true; + } + } + + //handle sneaking + if(sneaking) { + if(BehindMob(other, GetX(), GetY()) ) + return true; + } + + return(false); +} + +float Mob::_GetMovementSpeed(int mod) const { + // List of movement speed modifiers, including AAs & spells: + // http://everquest.allakhazam.com/db/item.html?item=1721;page=1;howmany=50#m10822246245352 + if (IsRooted()) + return 0.0f; + + float aa_mod = 0.0f; + float speed_mod = runspeed; + bool has_horse = false; + if (IsClient()) + { + if(CastToClient()->GetGMSpeed()) + { + speed_mod = 3.125f; + } + else + { + Mob* horse = entity_list.GetMob(CastToClient()->GetHorseId()); + if(horse) + { + speed_mod = horse->GetBaseRunspeed(); + has_horse = true; + } + } + } + + aa_mod += itembonuses.BaseMovementSpeed + spellbonuses.BaseMovementSpeed + aabonuses.BaseMovementSpeed; + + int spell_mod = spellbonuses.movementspeed + itembonuses.movementspeed; + int movemod = 0; + + if(spell_mod < 0) + { + movemod += spell_mod; + } + else if(spell_mod > (aa_mod)) + { + movemod = spell_mod; + } + else + { + movemod = static_cast(aa_mod); + } + + if(movemod < -85) //cap it at moving very very slow + movemod = -85; + + if (!has_horse && movemod != 0) + speed_mod += (speed_mod * float(movemod) / 100.0f); + + if(mod != 0) + speed_mod += (speed_mod * (float)mod / 100.0f); + + if(speed_mod <= 0.0f) + return(0.0001f); + + //runspeed cap. + if(IsClient()) + { + if (speed_mod > 1.58){ + uint8 bonus_IncreaseRunSpeedCap = itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap; + if (bonus_IncreaseRunSpeedCap){ + speed_mod += float(bonus_IncreaseRunSpeedCap)/100.0f; + if(speed_mod > 1.74) + speed_mod = 1.74; + } + else + speed_mod = 1.58; + } + } + + return speed_mod; +} + +int32 Mob::CalcMaxMana() { + switch (GetCasterClass()) { + case 'I': + max_mana = (((GetINT()/2)+1) * GetLevel()) + spellbonuses.Mana + itembonuses.Mana; + break; + case 'W': + max_mana = (((GetWIS()/2)+1) * GetLevel()) + spellbonuses.Mana + itembonuses.Mana; + break; + case 'N': + default: + max_mana = 0; + break; + } + if (max_mana < 0) { + max_mana = 0; + } + + return max_mana; +} + +int32 Mob::CalcMaxHP() { + max_hp = (base_hp + itembonuses.HP + spellbonuses.HP); + max_hp += max_hp * (aabonuses.MaxHPChange + spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000; + return max_hp; +} + +int32 Mob::GetItemHPBonuses() { + int32 item_hp = 0; + item_hp = itembonuses.HP; + item_hp += item_hp * itembonuses.MaxHPChange / 10000; + return item_hp; +} + +int32 Mob::GetSpellHPBonuses() { + int32 spell_hp = 0; + spell_hp = spellbonuses.HP; + spell_hp += spell_hp * spellbonuses.MaxHPChange / 10000; + return spell_hp; +} + +char Mob::GetCasterClass() const { + switch(class_) + { + case CLERIC: + case PALADIN: + case RANGER: + case DRUID: + case SHAMAN: + case BEASTLORD: + case CLERICGM: + case PALADINGM: + case RANGERGM: + case DRUIDGM: + case SHAMANGM: + case BEASTLORDGM: + return 'W'; + break; + + case SHADOWKNIGHT: + case BARD: + case NECROMANCER: + case WIZARD: + case MAGICIAN: + case ENCHANTER: + case SHADOWKNIGHTGM: + case BARDGM: + case NECROMANCERGM: + case WIZARDGM: + case MAGICIANGM: + case ENCHANTERGM: + return 'I'; + break; + + default: + return 'N'; + break; + } +} + +uint8 Mob::GetArchetype() const { + switch(class_) + { + case PALADIN: + case RANGER: + case SHADOWKNIGHT: + case BARD: + case BEASTLORD: + case PALADINGM: + case RANGERGM: + case SHADOWKNIGHTGM: + case BARDGM: + case BEASTLORDGM: + return ARCHETYPE_HYBRID; + break; + case CLERIC: + case DRUID: + case SHAMAN: + case NECROMANCER: + case WIZARD: + case MAGICIAN: + case ENCHANTER: + case CLERICGM: + case DRUIDGM: + case SHAMANGM: + case NECROMANCERGM: + case WIZARDGM: + case MAGICIANGM: + case ENCHANTERGM: + return ARCHETYPE_CASTER; + break; + case WARRIOR: + case MONK: + case ROGUE: + case BERSERKER: + case WARRIORGM: + case MONKGM: + case ROGUEGM: + case BERSERKERGM: + return ARCHETYPE_MELEE; + break; + default: + return ARCHETYPE_HYBRID; + break; + } +} + +void Mob::CreateSpawnPacket(EQApplicationPacket* app, Mob* ForWho) { + app->SetOpcode(OP_NewSpawn); + app->size = sizeof(NewSpawn_Struct); + app->pBuffer = new uchar[app->size]; + memset(app->pBuffer, 0, app->size); + NewSpawn_Struct* ns = (NewSpawn_Struct*)app->pBuffer; + FillSpawnStruct(ns, ForWho); +} + +void Mob::CreateSpawnPacket(EQApplicationPacket* app, NewSpawn_Struct* ns) { + app->SetOpcode(OP_NewSpawn); + app->size = sizeof(NewSpawn_Struct); + + app->pBuffer = new uchar[sizeof(NewSpawn_Struct)]; + + // Copy ns directly into packet + memcpy(app->pBuffer, ns, sizeof(NewSpawn_Struct)); + + // Custom packet data + NewSpawn_Struct* ns2 = (NewSpawn_Struct*)app->pBuffer; + strcpy(ns2->spawn.name, ns->spawn.name); + /*if (ns->spawn.class_==MERCHANT) + strcpy(ns2->spawn.lastName, "EQEmu Shopkeeper"); + else*/ if (ns->spawn.class_==TRIBUTE_MASTER) + strcpy(ns2->spawn.lastName, "Tribute Master"); + else if (ns->spawn.class_==ADVENTURERECRUITER) + strcpy(ns2->spawn.lastName, "Adventure Recruiter"); + else if (ns->spawn.class_==BANKER) + strcpy(ns2->spawn.lastName, "Banker"); + else if (ns->spawn.class_==ADVENTUREMERCHANT) + strcpy(ns->spawn.lastName,"Adventure Merchant"); + else if (ns->spawn.class_==WARRIORGM) + strcpy(ns2->spawn.lastName, "GM Warrior"); + else if (ns->spawn.class_==PALADINGM) + strcpy(ns2->spawn.lastName, "GM Paladin"); + else if (ns->spawn.class_==RANGERGM) + strcpy(ns2->spawn.lastName, "GM Ranger"); + else if (ns->spawn.class_==SHADOWKNIGHTGM) + strcpy(ns2->spawn.lastName, "GM Shadowknight"); + else if (ns->spawn.class_==DRUIDGM) + strcpy(ns2->spawn.lastName, "GM Druid"); + else if (ns->spawn.class_==BARDGM) + strcpy(ns2->spawn.lastName, "GM Bard"); + else if (ns->spawn.class_==ROGUEGM) + strcpy(ns2->spawn.lastName, "GM Rogue"); + else if (ns->spawn.class_==SHAMANGM) + strcpy(ns2->spawn.lastName, "GM Shaman"); + else if (ns->spawn.class_==NECROMANCERGM) + strcpy(ns2->spawn.lastName, "GM Necromancer"); + else if (ns->spawn.class_==WIZARDGM) + strcpy(ns2->spawn.lastName, "GM Wizard"); + else if (ns->spawn.class_==MAGICIANGM) + strcpy(ns2->spawn.lastName, "GM Magician"); + else if (ns->spawn.class_==ENCHANTERGM) + strcpy(ns2->spawn.lastName, "GM Enchanter"); + else if (ns->spawn.class_==BEASTLORDGM) + strcpy(ns2->spawn.lastName, "GM Beastlord"); + else if (ns->spawn.class_==BERSERKERGM) + strcpy(ns2->spawn.lastName, "GM Berserker"); + else + strcpy(ns2->spawn.lastName, ns->spawn.lastName); + memset(&app->pBuffer[sizeof(Spawn_Struct)-7],0xFF,7); +} + +void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) +{ + int i; + + strcpy(ns->spawn.name, name); + if(IsClient()) + { + strn0cpy(ns->spawn.lastName,lastname,sizeof(ns->spawn.lastName)); + } + ns->spawn.heading = FloatToEQ19(heading); + ns->spawn.x = FloatToEQ19(x_pos);//((int32)x_pos)<<3; + ns->spawn.y = FloatToEQ19(y_pos);//((int32)y_pos)<<3; + ns->spawn.z = FloatToEQ19(z_pos);//((int32)z_pos)<<3; + ns->spawn.spawnId = GetID(); + ns->spawn.curHp = static_cast(GetHPRatio()); + ns->spawn.max_hp = 100; //this field needs a better name + ns->spawn.race = race; + ns->spawn.runspeed = runspeed; + ns->spawn.walkspeed = runspeed * 0.5f; + ns->spawn.class_ = class_; + ns->spawn.gender = gender; + ns->spawn.level = level; + ns->spawn.deity = deity; + ns->spawn.animation = 0; + ns->spawn.findable = findable?1:0; +// vesuvias - appearence fix + ns->spawn.light = light; + ns->spawn.showhelm = 1; + + ns->spawn.invis = (invisible || hidden) ? 1 : 0; // TODO: load this before spawning players + ns->spawn.NPC = IsClient() ? 0 : 1; + ns->spawn.IsMercenary = IsMerc() ? 1 : 0; + ns->spawn.petOwnerId = ownerid; + + ns->spawn.haircolor = haircolor; + ns->spawn.beardcolor = beardcolor; + ns->spawn.eyecolor1 = eyecolor1; + ns->spawn.eyecolor2 = eyecolor2; + ns->spawn.hairstyle = hairstyle; + ns->spawn.face = luclinface; + ns->spawn.beard = beard; + ns->spawn.StandState = GetAppearanceValue(_appearance); + ns->spawn.drakkin_heritage = drakkin_heritage; + ns->spawn.drakkin_tattoo = drakkin_tattoo; + ns->spawn.drakkin_details = drakkin_details; + ns->spawn.equip_chest2 = texture; + +// ns->spawn.invis2 = 0xff;//this used to be labeled beard.. if its not FF it will turn + //mob invis + + if(helmtexture && helmtexture != 0xFF) + { + ns->spawn.helm=helmtexture; + } else { + ns->spawn.helm = 0; + } + + ns->spawn.guildrank = 0xFF; + 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 + // 1 - Same as #flymode 1 + // 2 - Same as #flymode 2 + // 3 - Mobs in water do not sink. A value of 3 in this field appears to be the default setting for all mobs + // (in water or not) according to 6.2 era packet collects. + if(IsClient()) + { + ns->spawn.flymode = FindType(SE_Levitate) ? 2 : 0; + } + else + ns->spawn.flymode = flymode; + + ns->spawn.lastName[0] = '\0'; + + strn0cpy(ns->spawn.lastName, lastname, sizeof(ns->spawn.lastName)); + + for(i = 0; i < MAX_MATERIALS; i++) + { + ns->spawn.equipment[i] = GetEquipmentMaterial(i); + if (armor_tint[i]) + { + ns->spawn.colors[i].color = armor_tint[i]; + } + else + { + ns->spawn.colors[i].color = GetEquipmentColor(i); + } + } + + memset(ns->spawn.set_to_0xFF, 0xFF, sizeof(ns->spawn.set_to_0xFF)); + if(IsNPC() && IsDestructibleObject()) + { + ns->spawn.DestructibleObject = true; + + // Changing the first string made it vanish, so it has some significance. + if(lastname) + sprintf(ns->spawn.DestructibleModel, lastname); + // Changing the second string made no visible difference + sprintf(ns->spawn.DestructibleName2, "%s", ns->spawn.name); + // Putting a string in the final one that was previously empty had no visible effect. + sprintf(ns->spawn.DestructibleString, ""); + + // Sets damage appearance level of the object. + ns->spawn.DestructibleAppearance = luclinface; // Was 0x00000000 + //ns->spawn.DestructibleAppearance = static_cast(_appearance); + // #appearance 44 1 makes it jump but no visible damage + // #appearance 44 2 makes it look completely broken but still visible + // #appearnace 44 3 makes it jump but not visible difference to 3 + // #appearance 44 4 makes it disappear altogether + // #appearance 44 5 makes the client crash. + + ns->spawn.DestructibleUnk1 = 0x00000224; // Was 0x000001f5; + // These next 4 are mostly always sequential + // Originally they were 633, 634, 635, 636 + // Changing them all to 633 - no visible effect. + // Changing them all to 636 - no visible effect. + // Reversing the order of these four numbers and then using #appearance gain had no visible change. + // Setting these four ids to zero had no visible effect when the catapult spawned, nor when #appearance was used. + ns->spawn.DestructibleID1 = 1968; + ns->spawn.DestructibleID2 = 1969; + ns->spawn.DestructibleID3 = 1970; + ns->spawn.DestructibleID4 = 1971; + // Next one was originally 0x1ce45008, changing it to 0x00000000 made no visible difference + ns->spawn.DestructibleUnk2 = 0x13f79d00; + // Next one was originally 0x1a68fe30, changing it to 0x00000000 made no visible difference + ns->spawn.DestructibleUnk3 = 0x00000000; + // Next one was already 0x00000000 + ns->spawn.DestructibleUnk4 = 0x13f79d58; + // Next one was originally 0x005a69ec, changing it to 0x00000000 made no visible difference. + ns->spawn.DestructibleUnk5 = 0x13c55b00; + // Next one was originally 0x1a68fe30, changing it to 0x00000000 made no visible difference. + ns->spawn.DestructibleUnk6 = 0x00128860; + // Next one was originally 0x0059de6d, changing it to 0x00000000 made no visible difference. + ns->spawn.DestructibleUnk7 = 0x005a8f66; + // Next one was originally 0x00000201, changing it to 0x00000000 made no visible difference. + // For the Minohten tents, 0x00000000 had them up in the air, while 0x201 put them on the ground. + // Changing it it 0x00000001 makes the tent sink into the ground. + ns->spawn.DestructibleUnk8 = 0x01; // Needs to be 1 for tents? + ns->spawn.DestructibleUnk9 = 0x00000002; // Needs to be 2 for tents? + + ns->spawn.flymode = 0; + } +} + +void Mob::CreateDespawnPacket(EQApplicationPacket* app, bool Decay) +{ + app->SetOpcode(OP_DeleteSpawn); + app->size = sizeof(DeleteSpawn_Struct); + app->pBuffer = new uchar[app->size]; + memset(app->pBuffer, 0, app->size); + DeleteSpawn_Struct* ds = (DeleteSpawn_Struct*)app->pBuffer; + ds->spawn_id = GetID(); + // The next field only applies to corpses. If 0, they vanish instantly, otherwise they 'decay' + ds->Decay = Decay ? 1 : 0; +} + +void Mob::CreateHPPacket(EQApplicationPacket* app) +{ + this->IsFullHP=(cur_hp>=max_hp); + app->SetOpcode(OP_MobHealth); + app->size = sizeof(SpawnHPUpdate_Struct2); + app->pBuffer = new uchar[app->size]; + memset(app->pBuffer, 0, sizeof(SpawnHPUpdate_Struct2)); + SpawnHPUpdate_Struct2* ds = (SpawnHPUpdate_Struct2*)app->pBuffer; + + ds->spawn_id = GetID(); + // they don't need to know the real hp + ds->hp = (int)GetHPRatio(); + + // hp event + if (IsNPC() && (GetNextHPEvent() > 0)) + { + if (ds->hp < GetNextHPEvent()) + { + char buf[10]; + snprintf(buf, 9, "%i", GetNextHPEvent()); + buf[9] = '\0'; + SetNextHPEvent(-1); + parse->EventNPC(EVENT_HP, CastToNPC(), NULL, buf, 0); + } + } + + if (IsNPC() && (GetNextIncHPEvent() > 0)) + { + if (ds->hp > GetNextIncHPEvent()) + { + char buf[10]; + snprintf(buf, 9, "%i", GetNextIncHPEvent()); + buf[9] = '\0'; + SetNextIncHPEvent(-1); + parse->EventNPC(EVENT_HP, CastToNPC(), NULL, buf, 1); + } + } +} + +// sends hp update of this mob to people who might care +void Mob::SendHPUpdate() +{ + EQApplicationPacket hp_app; + Group *group; + + // destructor will free the pBuffer + CreateHPPacket(&hp_app); + +#ifdef MANAGE_HP_UPDATES + entity_list.QueueManaged(this, &hp_app, true); +#else + // send to people who have us targeted + entity_list.QueueClientsByTarget(this, &hp_app, false, 0, false, true, BIT_AllClients); + entity_list.QueueClientsByXTarget(this, &hp_app, false); + entity_list.QueueToGroupsForNPCHealthAA(this, &hp_app); + + // send to group + if(IsGrouped()) + { + group = entity_list.GetGroupByMob(this); + if(group) //not sure why this might be null, but it happens + group->SendHPPacketsFrom(this); + } + + if(IsClient()){ + Raid *r = entity_list.GetRaidByClient(CastToClient()); + if(r){ + r->SendHPPacketsFrom(this); + } + } + + // send to master + if(GetOwner() && GetOwner()->IsClient()) + { + GetOwner()->CastToClient()->QueuePacket(&hp_app, false); + group = entity_list.GetGroupByClient(GetOwner()->CastToClient()); + if(group) + group->SendHPPacketsFrom(this); + Raid *r = entity_list.GetRaidByClient(GetOwner()->CastToClient()); + if(r) + r->SendHPPacketsFrom(this); + } + + // send to pet + if(GetPet() && GetPet()->IsClient()) + { + GetPet()->CastToClient()->QueuePacket(&hp_app, false); + } +#endif //MANAGE_HP_PACKETS + + // Update the damage state of destructible objects + if(IsNPC() && IsDestructibleObject()) + { + if (GetHPRatio() > 74) + { + if (GetAppearance() != eaStanding) + { + SendAppearancePacket(AT_DamageState, eaStanding); + _appearance = eaStanding; + } + } + else if (GetHPRatio() > 49) + { + if (GetAppearance() != eaSitting) + { + SendAppearancePacket(AT_DamageState, eaSitting); + _appearance = eaSitting; + } + } + else if (GetHPRatio() > 24) + { + if (GetAppearance() != eaCrouching) + { + SendAppearancePacket(AT_DamageState, eaCrouching); + _appearance = eaCrouching; + } + } + else if (GetHPRatio() > 0) + { + if (GetAppearance() != eaDead) + { + SendAppearancePacket(AT_DamageState, eaDead); + _appearance = eaDead; + } + } + else if (GetAppearance() != eaLooting) + { + SendAppearancePacket(AT_DamageState, eaLooting); + _appearance = eaLooting; + } + } + + // send to self - we need the actual hps here + if(IsClient()) + { + EQApplicationPacket* hp_app2 = new EQApplicationPacket(OP_HPUpdate,sizeof(SpawnHPUpdate_Struct)); + SpawnHPUpdate_Struct* ds = (SpawnHPUpdate_Struct*)hp_app2->pBuffer; + ds->cur_hp = CastToClient()->GetHP() - itembonuses.HP; + ds->spawn_id = GetID(); + ds->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP; + CastToClient()->QueuePacket(hp_app2); + safe_delete(hp_app2); + } +} + +// this one just warps the mob to the current location +void Mob::SendPosition() +{ + EQApplicationPacket* app = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)app->pBuffer; + MakeSpawnUpdateNoDelta(spu); + move_tic_count = 0; + entity_list.QueueClients(this, app, true); + safe_delete(app); +} + +// this one is for mobs on the move, with deltas - this makes them walk +void Mob::SendPosUpdate(uint8 iSendToSelf) { + EQApplicationPacket* app = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)app->pBuffer; + MakeSpawnUpdate(spu); + + if (iSendToSelf == 2) { + if (this->IsClient()) + this->CastToClient()->FastQueuePacket(&app,false); + } + else + { +#ifdef PACKET_UPDATE_MANAGER + entity_list.QueueManaged(this, app, (iSendToSelf==0),false); +#else + if(move_tic_count == RuleI(Zone, NPCPositonUpdateTicCount)) + { + entity_list.QueueClients(this, app, (iSendToSelf==0), false); + move_tic_count = 0; + } + else + { + entity_list.QueueCloseClients(this, app, (iSendToSelf==0), 800, NULL, false); + move_tic_count++; + } +#endif + } + safe_delete(app); +} + +// this is for SendPosition() +void Mob::MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct *spu){ + memset(spu,0xff,sizeof(PlayerPositionUpdateServer_Struct)); + spu->spawn_id = GetID(); + spu->x_pos = FloatToEQ19(x_pos); + spu->y_pos = FloatToEQ19(y_pos); + spu->z_pos = FloatToEQ19(z_pos); + spu->delta_x = NewFloatToEQ13(0); + spu->delta_y = NewFloatToEQ13(0); + spu->delta_z = NewFloatToEQ13(0); + spu->heading = FloatToEQ19(heading); + spu->animation = 0; + spu->delta_heading = NewFloatToEQ13(0); + spu->padding0002 =0; + spu->padding0006 =7; + spu->padding0014 =0x7f; + spu->padding0018 =0x5df27; + +} + +// this is for SendPosUpdate() +void Mob::MakeSpawnUpdate(PlayerPositionUpdateServer_Struct* spu) { + spu->spawn_id = GetID(); + spu->x_pos = FloatToEQ19(x_pos); + spu->y_pos = FloatToEQ19(y_pos); + spu->z_pos = FloatToEQ19(z_pos); + spu->delta_x = NewFloatToEQ13(delta_x); + spu->delta_y = NewFloatToEQ13(delta_y); + spu->delta_z = NewFloatToEQ13(delta_z); + spu->heading = FloatToEQ19(heading); + spu->padding0002 =0; + spu->padding0006 =7; + spu->padding0014 =0x7f; + spu->padding0018 =0x5df27; + if(this->IsClient()) + spu->animation = animation; + else + spu->animation = pRunAnimSpeed;//animation; + spu->delta_heading = NewFloatToEQ13(static_cast(delta_heading)); +} + +void Mob::ShowStats(Client* client) +{ + if (IsClient()) { + CastToClient()->SendStatsWindow(client, RuleB(Character, UseNewStatsWindow)); + } + else if (IsCorpse()) { + if (IsPlayerCorpse()) { + client->Message(0, " CharID: %i PlayerCorpse: %i", CastToCorpse()->GetCharID(), CastToCorpse()->GetDBID()); + } + else { + client->Message(0, " NPCCorpse", GetID()); + } + } + else { + client->Message(0, " Level: %i AC: %i Class: %i Size: %1.1f Haste: %i", GetLevel(), GetAC(), GetClass(), GetSize(), GetHaste()); + client->Message(0, " HP: %i Max HP: %i",GetHP(), GetMaxHP()); + client->Message(0, " Mana: %i Max Mana: %i", GetMana(), GetMaxMana()); + client->Message(0, " Total ATK: %i Worn/Spell ATK (Cap %i): %i", GetATK(), RuleI(Character, ItemATKCap), GetATKBonus()); + client->Message(0, " STR: %i STA: %i DEX: %i AGI: %i INT: %i WIS: %i CHA: %i", GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA()); + client->Message(0, " MR: %i PR: %i FR: %i CR: %i DR: %i Corruption: %i", GetMR(), GetPR(), GetFR(), GetCR(), GetDR(), GetCorrup()); + client->Message(0, " Race: %i BaseRace: %i Texture: %i HelmTexture: %i Gender: %i BaseGender: %i", GetRace(), GetBaseRace(), GetTexture(), GetHelmTexture(), GetGender(), GetBaseGender()); + if (client->Admin() >= 100) + client->Message(0, " EntityID: %i PetID: %i OwnerID: %i AIControlled: %i Targetted: %i", GetID(), GetPetID(), GetOwnerID(), IsAIControlled(), targeted); + + if (IsNPC()) { + NPC *n = CastToNPC(); + uint32 spawngroupid = 0; + if(n->respawn2 != 0) + spawngroupid = n->respawn2->SpawnGroupID(); + client->Message(0, " NPCID: %u SpawnGroupID: %u Grid: %i LootTable: %u FactionID: %i SpellsID: %u ", GetNPCTypeID(),spawngroupid, n->GetGrid(), n->GetLoottableID(), n->GetNPCFactionID(), n->GetNPCSpellsID()); + client->Message(0, " Accuracy: %i MerchantID: %i EmoteID: %i Runspeed: %f Walkspeed: %f", n->GetAccuracyRating(), n->MerchantType, n->GetNPCEmoteID(), n->GetRunspeed(), n->GetWalkspeed()); + n->QueryLoot(client); + } + if (IsAIControlled()) { + client->Message(0, " AggroRange: %1.0f AssistRange: %1.0f", GetAggroRange(), GetAssistRange()); + } + } +} + +void Mob::DoAnim(const int animnum, int type, bool ackreq, eqFilterType filter) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Animation, sizeof(Animation_Struct)); + Animation_Struct* anim = (Animation_Struct*)outapp->pBuffer; + anim->spawnid = GetID(); + if(type == 0){ + anim->action = 10; + anim->value=animnum; + } + else{ + anim->action = animnum; + anim->value=type; + } + entity_list.QueueCloseClients(this, outapp, false, 200, 0, ackreq, filter); + safe_delete(outapp); +} + +void Mob::ShowBuffs(Client* client) { + if (!spells_loaded) + return; + client->Message(0, "Buffs on: %s", this->GetName()); + uint32 i; + uint32 buff_count = GetMaxTotalSlots(); + for (i=0; i < buff_count; i++) { + if (buffs[i].spellid != SPELL_UNKNOWN) { + if (spells[buffs[i].spellid].buffdurationformula == DF_Permanent) + client->Message(0, " %i: %s: Permanent", i, spells[buffs[i].spellid].name); + else + client->Message(0, " %i: %s: %i tics left", i, spells[buffs[i].spellid].name, buffs[i].ticsremaining); + + } + } + if (IsClient()){ + client->Message(0, "itembonuses:"); + client->Message(0, "Atk:%i Ac:%i HP(%i):%i Mana:%i", itembonuses.ATK, itembonuses.AC, itembonuses.HPRegen, itembonuses.HP, itembonuses.Mana); + client->Message(0, "Str:%i Sta:%i Dex:%i Agi:%i Int:%i Wis:%i Cha:%i", + itembonuses.STR,itembonuses.STA,itembonuses.DEX,itembonuses.AGI,itembonuses.INT,itembonuses.WIS,itembonuses.CHA); + client->Message(0, "SvMagic:%i SvFire:%i SvCold:%i SvPoison:%i SvDisease:%i", + itembonuses.MR,itembonuses.FR,itembonuses.CR,itembonuses.PR,itembonuses.DR); + client->Message(0, "DmgShield:%i Haste:%i", itembonuses.DamageShield, itembonuses.haste ); + client->Message(0, "spellbonuses:"); + client->Message(0, "Atk:%i Ac:%i HP(%i):%i Mana:%i", spellbonuses.ATK, spellbonuses.AC, spellbonuses.HPRegen, spellbonuses.HP, spellbonuses.Mana); + client->Message(0, "Str:%i Sta:%i Dex:%i Agi:%i Int:%i Wis:%i Cha:%i", + spellbonuses.STR,spellbonuses.STA,spellbonuses.DEX,spellbonuses.AGI,spellbonuses.INT,spellbonuses.WIS,spellbonuses.CHA); + client->Message(0, "SvMagic:%i SvFire:%i SvCold:%i SvPoison:%i SvDisease:%i", + spellbonuses.MR,spellbonuses.FR,spellbonuses.CR,spellbonuses.PR,spellbonuses.DR); + client->Message(0, "DmgShield:%i Haste:%i", spellbonuses.DamageShield, spellbonuses.haste ); + } +} + +void Mob::ShowBuffList(Client* client) { + if (!spells_loaded) + return; + + client->Message(0, "Buffs on: %s", this->GetCleanName()); + uint32 i; + uint32 buff_count = GetMaxTotalSlots(); + for (i=0; i < buff_count; i++) { + if (buffs[i].spellid != SPELL_UNKNOWN) { + if (spells[buffs[i].spellid].buffdurationformula == DF_Permanent) + client->Message(0, " %i: %s: Permanent", i, spells[buffs[i].spellid].name); + else + client->Message(0, " %i: %s: %i tics left", i, spells[buffs[i].spellid].name, buffs[i].ticsremaining); + } + } +} + +void Mob::GMMove(float x, float y, float z, float heading, bool SendUpdate) { + + Route.clear(); + + x_pos = x; + y_pos = y; + z_pos = z; + if (heading != 0.01) + this->heading = heading; + if(IsNPC()) + CastToNPC()->SaveGuardSpot(true); + if(SendUpdate) + SendPosition(); + //SendPosUpdate(1); +#ifdef PACKET_UPDATE_MANAGER + if(IsClient()) { + CastToClient()->GetUpdateManager()->FlushQueues(); + } +#endif +} + +void Mob::SendIllusionPacket(uint16 in_race, uint8 in_gender, uint8 in_texture, uint8 in_helmtexture, uint8 in_haircolor, uint8 in_beardcolor, uint8 in_eyecolor1, uint8 in_eyecolor2, uint8 in_hairstyle, uint8 in_luclinface, uint8 in_beard, uint8 in_aa_title, uint32 in_drakkin_heritage, uint32 in_drakkin_tattoo, uint32 in_drakkin_details, float in_size) { + + uint16 BaseRace = GetBaseRace(); + + if (in_race == 0) { + this->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(); + } + 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; + + 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; + else + this->helmtexture = GetHelmTexture(); + } + else + this->helmtexture = in_helmtexture; + + if (in_haircolor == 0xFF) + this->haircolor = GetHairColor(); + else + this->haircolor = in_haircolor; + + if (in_beardcolor == 0xFF) + this->beardcolor = GetBeardColor(); + else + this->beardcolor = in_beardcolor; + + if (in_eyecolor1 == 0xFF) + this->eyecolor1 = GetEyeColor1(); + else + this->eyecolor1 = in_eyecolor1; + + if (in_eyecolor2 == 0xFF) + this->eyecolor2 = GetEyeColor2(); + else + this->eyecolor2 = in_eyecolor2; + + if (in_hairstyle == 0xFF) + this->hairstyle = GetHairStyle(); + else + this->hairstyle = in_hairstyle; + + if (in_luclinface == 0xFF) + this->luclinface = GetLuclinFace(); + else + this->luclinface = in_luclinface; + + if (in_beard == 0xFF) + this->beard = GetBeard(); + else + this->beard = in_beard; + + this->aa_title = 0xFF; + + if (in_drakkin_heritage == 0xFFFFFFFF) + this->drakkin_heritage = GetDrakkinHeritage(); + else + this->drakkin_heritage = in_drakkin_heritage; + + if (in_drakkin_tattoo == 0xFFFFFFFF) + this->drakkin_tattoo = GetDrakkinTattoo(); + else + this->drakkin_tattoo = in_drakkin_tattoo; + + if (in_drakkin_details == 0xFFFFFFFF) + this->drakkin_details = GetDrakkinDetails(); + else + this->drakkin_details = in_drakkin_details; + + if (in_size == 0xFFFFFFFF) + this->size = GetSize(); + else + this->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(); + switch(race){ + case OGRE: + this->size = 9; + break; + case TROLL: + this->size = 8; + break; + case VAHSHIR: + case BARBARIAN: + this->size = 7; + break; + case HALF_ELF: + case WOOD_ELF: + case DARK_ELF: + case FROGLOK: + this->size = 5; + break; + case DWARF: + this->size = 4; + break; + case HALFLING: + case GNOME: + this->size = 3; + break; + default: + this->size = 6; + break; + } + } + + 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(); + 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; + + 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); +} + +uint8 Mob::GetDefaultGender(uint16 in_race, uint8 in_gender) { +//cout << "Gender in: " << (int)in_gender << endl; + 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 (in_gender >= 2) { + // Female default for PC Races + return 1; + } + else + return in_gender; + } + else if (in_race == 44 || in_race == 52 || in_race == 55 || in_race == 65 || in_race == 67 || in_race == 88 || in_race == 117 || in_race == 127 || + in_race == 77 || in_race == 78 || in_race == 81 || in_race == 90 || in_race == 92 || in_race == 93 || in_race == 94 || in_race == 106 || in_race == 112 || in_race == 471) { + // Male only races + return 0; + + } + else if (in_race == 25 || in_race == 56) { + // Female only races + return 1; + } + else { + // Neutral default for NPC Races + return 2; + } +} + +void Mob::SendAppearancePacket(uint32 type, uint32 value, bool WholeZone, bool iIgnoreSelf, Client *specific_target) { + if (!GetID()) + return; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* appearance = (SpawnAppearance_Struct*)outapp->pBuffer; + appearance->spawn_id = this->GetID(); + appearance->type = type; + appearance->parameter = value; + if (WholeZone) + entity_list.QueueClients(this, outapp, iIgnoreSelf); + else if(specific_target != NULL) + specific_target->QueuePacket(outapp, false, Client::CLIENT_CONNECTED); + else if (this->IsClient()) + this->CastToClient()->QueuePacket(outapp, false, Client::CLIENT_CONNECTED); + safe_delete(outapp); +} + +void Mob::SendLevelAppearance(){ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_LevelAppearance, sizeof(LevelAppearance_Struct)); + LevelAppearance_Struct* la = (LevelAppearance_Struct*)outapp->pBuffer; + la->parm1 = 0x4D; + la->parm2 = la->parm1 + 1; + la->parm3 = la->parm2 + 1; + la->parm4 = la->parm3 + 1; + la->parm5 = la->parm4 + 1; + la->spawn_id = GetID(); + la->value1a = 1; + la->value2a = 2; + la->value3a = 1; + la->value3b = 1; + la->value4a = 1; + la->value4b = 1; + la->value5a = 2; + entity_list.QueueCloseClients(this,outapp); + safe_delete(outapp); +} + +void Mob::SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5, Client *specific_target){ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_LevelAppearance, sizeof(LevelAppearance_Struct)); + LevelAppearance_Struct* la = (LevelAppearance_Struct*)outapp->pBuffer; + la->spawn_id = GetID(); + la->parm1 = parm1; + la->parm2 = parm2; + la->parm3 = parm3; + la->parm4 = parm4; + la->parm5 = parm5; + // Note that setting the b values to 0 will disable the related effect from the corresponding parameter. + // Setting the a value appears to have no affect at all. + la->value1a = 1; + la->value1b = 1; + la->value2a = 1; + la->value2b = 1; + la->value3a = 1; + la->value3b = 1; + la->value4a = 1; + la->value4b = 1; + la->value5a = 1; + la->value5b = 1; + if(specific_target == NULL) { + entity_list.QueueClients(this,outapp); + } + else if (specific_target->IsClient()) { + specific_target->CastToClient()->QueuePacket(outapp, false); + } + safe_delete(outapp); +} + +void Mob::SendTargetable(bool on, Client *specific_target) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Untargetable, sizeof(Untargetable_Struct)); + Untargetable_Struct *ut = (Untargetable_Struct*)outapp->pBuffer; + ut->id = GetID(); + ut->targetable_flag = on == true ? 1 : 0; + + if(specific_target == NULL) { + entity_list.QueueClients(this, outapp); + } + else if (specific_target->IsClient()) { + specific_target->CastToClient()->QueuePacket(outapp, false); + } + safe_delete(outapp); +} + +void Mob::QuestReward(Client *c, uint32 silver, uint32 gold, uint32 platinum) { + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Sound, sizeof(QuestReward_Struct)); + memset(outapp->pBuffer, 0, sizeof(outapp->pBuffer)); + QuestReward_Struct* qr = (QuestReward_Struct*) outapp->pBuffer; + + qr->from_mob = GetID(); // Entity ID for the from mob name + qr->silver = silver; + qr->gold = gold; + qr->platinum = platinum; + + if(c) + c->QueuePacket(outapp, false, Client::CLIENT_CONNECTED); + + safe_delete(outapp); +} + +void Mob::CameraEffect(uint32 duration, uint32 intensity, Client *c, bool global) { + + + if(global == true) + { + ServerPacket* pack = new ServerPacket(ServerOP_CameraShake, sizeof(ServerCameraShake_Struct)); + memset(pack->pBuffer, 0, sizeof(pack->pBuffer)); + ServerCameraShake_Struct* scss = (ServerCameraShake_Struct*) pack->pBuffer; + scss->duration = duration; + scss->intensity = intensity; + worldserver.SendPacket(pack); + safe_delete(pack); + return; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_CameraEffect, sizeof(Camera_Struct)); + memset(outapp->pBuffer, 0, sizeof(outapp->pBuffer)); + Camera_Struct* cs = (Camera_Struct*) outapp->pBuffer; + cs->duration = duration; // Duration in milliseconds + cs->intensity = ((intensity * 6710886) + 1023410176); // Intensity ranges from 1023410176 to 1090519040, so simplify it from 0 to 10. + + if(c) + c->QueuePacket(outapp, false, Client::CLIENT_CONNECTED); + else + entity_list.QueueClients(this, outapp); + + safe_delete(outapp); +} + +void Mob::SendSpellEffect(uint32 effectid, uint32 duration, uint32 finish_delay, bool zone_wide, uint32 unk020, bool perm_effect, Client *c) { + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpellEffect, sizeof(SpellEffect_Struct)); + memset(outapp->pBuffer, 0, sizeof(outapp->pBuffer)); + SpellEffect_Struct* se = (SpellEffect_Struct*) outapp->pBuffer; + se->EffectID = effectid; // ID of the Particle Effect + se->EntityID = GetID(); + se->EntityID2 = GetID(); // EntityID again + se->Duration = duration; // In Milliseconds + se->FinishDelay = finish_delay; // Seen 0 + se->Unknown020 = unk020; // Seen 3000 + se->Unknown024 = 1; // Seen 1 for SoD + se->Unknown025 = 1; // Seen 1 for Live + se->Unknown026 = 0; // Seen 1157 + + if(c) + c->QueuePacket(outapp, false, Client::CLIENT_CONNECTED); + else if(zone_wide) + entity_list.QueueClients(this, outapp); + else + entity_list.QueueCloseClients(this, outapp); + + safe_delete(outapp); + + if (perm_effect) { + if(!IsNimbusEffectActive(effectid)) { + SetNimbusEffect(effectid); + } + } + +} + +void Mob::TempName(const char *newname) +{ + char temp_name[64]; + char old_name[64]; + strn0cpy(old_name, GetName(), 64); + + if(newname) + strn0cpy(temp_name, newname, 64); + + // Reset the name to the original if left null. + if(!newname) { + strn0cpy(temp_name, GetOrigName(), 64); + SetName(temp_name); + //CleanMobName(GetName(), temp_name); + strn0cpy(temp_name, GetCleanName(), 64); + } + + // Make the new name unique and set it + strn0cpy(temp_name, entity_list.MakeNameUnique(temp_name), 64); + + + // Send the new name to all clients + EQApplicationPacket* outapp = new EQApplicationPacket(OP_MobRename, sizeof(MobRename_Struct)); + memset(outapp->pBuffer, 0, sizeof(outapp->pBuffer)); + MobRename_Struct* mr = (MobRename_Struct*) outapp->pBuffer; + strn0cpy(mr->old_name, old_name, 64); + strn0cpy(mr->old_name_again, old_name, 64); + strn0cpy(mr->new_name, temp_name, 64); + mr->unknown192 = 0; + mr->unknown196 = 1; + entity_list.QueueClients(this, outapp); + safe_delete(outapp); + + SetName(temp_name); +} + +void Mob::SetTargetable(bool on) { + if(m_targetable != on) { + m_targetable = on; + SendTargetable(on); + } +} + +const int32& Mob::SetMana(int32 amount) +{ + CalcMaxMana(); + int32 mmana = GetMaxMana(); + cur_mana = amount < 0 ? 0 : (amount > mmana ? mmana : amount); +/* + if(IsClient()) + LogFile->write(EQEMuLog::Debug, "Setting mana for %s to %d (%4.1f%%)", GetName(), amount, GetManaRatio()); +*/ + + return cur_mana; +} + + +void Mob::SetAppearance(EmuAppearance app, bool iIgnoreSelf) { + if (_appearance != app) { + _appearance = app; + SendAppearancePacket(AT_Anim, GetAppearanceValue(app), true, iIgnoreSelf); + if (this->IsClient() && this->IsAIControlled()) + SendAppearancePacket(AT_Anim, ANIM_FREEZE, false, false); + } +} + +void Mob::ChangeSize(float in_size = 0, bool bNoRestriction) { + // Size Code + if (!bNoRestriction) + { + if (this->IsClient() || this->petid != 0) + if (in_size < 3.0) + in_size = 3.0; + + + if (this->IsClient() || this->petid != 0) + if (in_size > 15.0) + in_size = 15.0; + } + + + if (in_size < 1.0) + in_size = 1.0; + + if (in_size > 255.0) + in_size = 255.0; + //End of Size Code + this->size = in_size; + SendAppearancePacket(AT_Size, (uint32) in_size); +} + +Mob* Mob::GetOwnerOrSelf() { + if (!GetOwnerID()) + return this; + Mob* owner = entity_list.GetMob(this->GetOwnerID()); + if (!owner) { + SetOwnerID(0); + return(this); + } + if (owner->GetPetID() == this->GetID()) { + return owner; + } + if(IsNPC() && CastToNPC()->GetSwarmInfo()){ + return (CastToNPC()->GetSwarmInfo()->GetOwner()); + } + SetOwnerID(0); + return this; +} + +Mob* Mob::GetOwner() { + Mob* owner = entity_list.GetMob(this->GetOwnerID()); + if (owner && owner->GetPetID() == this->GetID()) { + + return owner; + } + if(IsNPC() && CastToNPC()->GetSwarmInfo()){ + return (CastToNPC()->GetSwarmInfo()->GetOwner()); + } + SetOwnerID(0); + return 0; +} + +Mob* Mob::GetUltimateOwner() +{ + Mob* Owner = GetOwner(); + + if(!Owner) + return this; + + while(Owner && Owner->HasOwner()) + Owner = Owner->GetOwner(); + + return Owner ? Owner : this; +} + +void Mob::SetOwnerID(uint16 NewOwnerID) { + if (NewOwnerID == GetID() && NewOwnerID != 0) // ok, no charming yourself now =p + return; + ownerid = NewOwnerID; + if (ownerid == 0 && this->IsNPC() && this->GetPetType() != petCharmed) + this->Depop(); +} + +//heko: for backstab +bool Mob::BehindMob(Mob* other, float playerx, float playery) const { + if (!other) + return true; // sure your behind your invisible friend?? (fall thru for sneak) + //see if player is behind mob + float angle, lengthb, vectorx, vectory; + float mobx = -(other->GetX()); // mob xlocation (inverse because eq is confused) + float moby = other->GetY(); // mobylocation + float heading = other->GetHeading(); // mob heading + heading = (heading * 360.0f) / 256.0f; // convert to degrees + if (heading < 270) + heading += 90; + else + heading -= 270; + heading = heading * 3.1415f / 180.0f; // convert to radians + vectorx = mobx + (10.0f * cosf(heading)); // create a vector based on heading + vectory = moby + (10.0f * sinf(heading)); // of mob length 10 + + //length of mob to player vector + //lengthb = (float)sqrtf(pow((-playerx-mobx),2) + pow((playery-moby),2)); + lengthb = (float) sqrtf( ( (-playerx-mobx) * (-playerx-mobx) ) + ( (playery-moby) * (playery-moby) ) ); + + // calculate dot product to get angle + angle = acosf(((vectorx-mobx)*(-playerx-mobx)+(vectory-moby)*(playery-moby)) / (10 * lengthb)); + angle = angle * 180.0f / 3.1415f; + if (angle > 90.0f) //not sure what value to use (90*2=180 degrees is front) + return true; + else + return false; +} + +void Mob::SetZone(uint32 zone_id, uint32 instance_id) +{ + if(IsClient()) + { + CastToClient()->GetPP().zone_id = zone_id; + CastToClient()->GetPP().zoneInstance = instance_id; + } + Save(); +} + +void Mob::Kill() { + Death(this, 0, SPELL_UNKNOWN, HAND_TO_HAND); +} + +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 = NULL; + const Item_Struct* PrimaryWeapon = NULL; + + for (int i=SLOT_RANGE; i<=SLOT_SECONDARY; i++) { + + //pick a timer + if (i == SLOT_PRIMARY) + TimerToUse = &attack_timer; + else if (i == SLOT_RANGE) + TimerToUse = &ranged_timer; + else if(i == SLOT_SECONDARY) + TimerToUse = &attack_dw_timer; + else //invalid slot (hands will always hit this) + continue; + + const Item_Struct* ItemToUse = NULL; + + //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 = NULL; + } + + //special offhand stuff + if(i == SLOT_SECONDARY) { + //if we have a 2H weapon in our main hand, no dual + if(PrimaryWeapon != NULL) { + if( PrimaryWeapon->ItemClass == ItemClassCommon + && (PrimaryWeapon->ItemType == ItemType2HS + || PrimaryWeapon->ItemType == ItemType2HB + || PrimaryWeapon->ItemType == ItemType2HPierce)) { + 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 != NULL) { + //check type and damage/delay + if(ItemToUse->ItemClass != ItemClassCommon + || ItemToUse->Damage == 0 + || ItemToUse->Delay == 0) { + //no weapon + ItemToUse = NULL; + } + // Check to see if skill is valid + else if((ItemToUse->ItemType > ItemTypeThrowing) && (ItemToUse->ItemType != ItemTypeHand2Hand) && (ItemToUse->ItemType != ItemType2HPierce)) { + //no weapon + ItemToUse = NULL; + } + } + + int16 DelayMod = itembonuses.HundredHands + spellbonuses.HundredHands; + if (DelayMod < -99) + DelayMod = -99; + + //if we have no weapon.. + if (ItemToUse == NULL) { + //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 == ItemTypeThrowing)) + { + 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); + } + + if(i == SLOT_PRIMARY) + PrimaryWeapon = ItemToUse; + } + +} + +bool Mob::CanThisClassDualWield(void) const +{ + if (!IsClient()) { + return(GetSkill(DUAL_WIELD) > 0); + } else { + const ItemInst* inst = CastToClient()->GetInv().GetItem(SLOT_PRIMARY); + // 2HS, 2HB, or 2HP + if (inst && inst->IsType(ItemClassCommon)) { + const Item_Struct* item = inst->GetItem(); + if ((item->ItemType == ItemType2HB) || (item->ItemType == ItemType2HS) || (item->ItemType == ItemType2HPierce)) + return false; + } else { + //No weapon in hand... using hand-to-hand... + //only monks and beastlords? can dual wield their fists. + if(class_ != MONK && class_ != MONKGM && class_ != BEASTLORD && class_ != BEASTLORDGM) { + return false; + } + } + + return (CastToClient()->HasSkill(DUAL_WIELD)); // No skill = no chance + } +} + +bool Mob::CanThisClassDoubleAttack(void) const +{ + if(!IsClient()) { + return(GetSkill(DOUBLE_ATTACK) > 0); + } else { + if(aabonuses.GiveDoubleAttack || itembonuses.GiveDoubleAttack || spellbonuses.GiveDoubleAttack) { + return true; + } + return(CastToClient()->HasSkill(DOUBLE_ATTACK)); + } +} + +bool Mob::IsWarriorClass(void) const +{ + switch(GetClass()) + { + case WARRIOR: + case WARRIORGM: + case ROGUE: + case ROGUEGM: + case MONK: + case MONKGM: + case PALADIN: + case PALADINGM: + case SHADOWKNIGHT: + case SHADOWKNIGHTGM: + case RANGER: + case RANGERGM: + case BEASTLORD: + case BEASTLORDGM: + case BERSERKER: + case BERSERKERGM: + case BARD: + case BARDGM: + { + return true; + } + default: + { + return false; + } + } + +} + +bool Mob::CanThisClassParry(void) const +{ + if(!IsClient()) { + return(GetSkill(PARRY) > 0); + } else { + return(CastToClient()->HasSkill(PARRY)); + } +} + +bool Mob::CanThisClassDodge(void) const +{ + if(!IsClient()) { + return(GetSkill(DODGE) > 0); + } else { + return(CastToClient()->HasSkill(DODGE)); + } +} + +bool Mob::CanThisClassRiposte(void) const +{ + if(!IsClient()) { + return(GetSkill(RIPOSTE) > 0); + } else { + return(CastToClient()->HasSkill(RIPOSTE)); + } +} + +bool Mob::CanThisClassBlock(void) const +{ + if(!IsClient()) { + return(GetSkill(BLOCKSKILL) > 0); + } else { + return(CastToClient()->HasSkill(BLOCKSKILL)); + } +} + +float Mob::Dist(const Mob &other) const { + _ZP(Mob_Dist); + float xDiff = other.x_pos - x_pos; + float yDiff = other.y_pos - y_pos; + float zDiff = other.z_pos - z_pos; + + return sqrtf( (xDiff * xDiff) + + (yDiff * yDiff) + + (zDiff * zDiff) ); +} + +float Mob::DistNoZ(const Mob &other) const { + _ZP(Mob_DistNoZ); + float xDiff = other.x_pos - x_pos; + float yDiff = other.y_pos - y_pos; + + return sqrtf( (xDiff * xDiff) + + (yDiff * yDiff) ); +} + +float Mob::DistNoRoot(const Mob &other) const { + _ZP(Mob_DistNoRoot); + float xDiff = other.x_pos - x_pos; + float yDiff = other.y_pos - y_pos; + float zDiff = other.z_pos - z_pos; + + return ( (xDiff * xDiff) + + (yDiff * yDiff) + + (zDiff * zDiff) ); +} + +float Mob::DistNoRoot(float x, float y, float z) const { + _ZP(Mob_DistNoRoot); + float xDiff = x - x_pos; + float yDiff = y - y_pos; + float zDiff = z - z_pos; + + return ( (xDiff * xDiff) + + (yDiff * yDiff) + + (zDiff * zDiff) ); +} + +float Mob::DistNoRootNoZ(float x, float y) const { + _ZP(Mob_DistNoRoot); + float xDiff = x - x_pos; + float yDiff = y - y_pos; + + return ( (xDiff * xDiff) + + (yDiff * yDiff) ); +} + +float Mob::DistNoRootNoZ(const Mob &other) const { + _ZP(Mob_DistNoRootNoZ); + float xDiff = other.x_pos - x_pos; + float yDiff = other.y_pos - y_pos; + + return ( (xDiff * xDiff) + (yDiff * yDiff) ); +} + +float Mob::GetReciprocalHeading(Mob* target) { + float Result = 0; + + if(target) { + // Convert to radians + float h = (target->GetHeading() / 256.0f) * 6.283184f; + + // Calculate the reciprocal heading in radians + Result = h + 3.141592f; + + // Convert back to eq heading from radians + Result = (Result / 6.283184f) * 256.0f; + } + + return Result; +} + +bool Mob::PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, float &z_dest, bool lookForAftArc) { + bool Result = false; + + if(target) { + float look_heading = 0; + + if(lookForAftArc) + look_heading = GetReciprocalHeading(target); + else + look_heading = target->GetHeading(); + + // Convert to sony heading to radians + look_heading = (look_heading / 256.0f) * 6.283184f; + + float tempX = 0; + float tempY = 0; + float tempZ = 0; + float tempSize = 0; + const float rangeCreepMod = 0.25; + const uint8 maxIterationsAllowed = 4; + uint8 counter = 0; + float rangeReduction= 0; + + tempSize = target->GetSize(); + rangeReduction = (tempSize * rangeCreepMod); + + while(tempSize > 0 && counter != maxIterationsAllowed) { + tempX = GetX() + (tempSize * static_cast(sin(double(look_heading)))); + tempY = GetY() + (tempSize * static_cast(cos(double(look_heading)))); + tempZ = target->GetZ(); + + if(!CheckLosFN(tempX, tempY, tempZ, tempSize)) { + tempSize -= rangeReduction; + } + else { + Result = true; + break; + } + + counter++; + } + + if(!Result) { + // Try to find an attack arc to position at from the opposite direction. + look_heading += (3.141592 / 2); + + tempSize = target->GetSize(); + counter = 0; + + while(tempSize > 0 && counter != maxIterationsAllowed) { + tempX = GetX() + (tempSize * static_cast(sin(double(look_heading)))); + tempY = GetY() + (tempSize * static_cast(cos(double(look_heading)))); + tempZ = target->GetZ(); + + if(!CheckLosFN(tempX, tempY, tempZ, tempSize)) { + tempSize -= rangeReduction; + } + else { + Result = true; + break; + } + + counter++; + } + } + + if(Result) { + x_dest = tempX; + y_dest = tempY; + z_dest = tempZ; + } + } + + return Result; +} + +bool Mob::HateSummon() { + // check if mob has ability to summon + // 97% is the offical % that summoning starts on live, not 94 + // if the mob can summon and is charmed, it can only summon mobs it has LoS to + Mob* mob_owner = NULL; + if(GetOwnerID()) + mob_owner = entity_list.GetMob(GetOwnerID()); + + if (GetHPRatio() >= 98 || SpecAttacks[SPECATK_SUMMON] == false || !GetTarget() || + (mob_owner && mob_owner->IsClient() && !CheckLosFN(GetTarget()))) + return false; + + // now validate the timer + if (!SpecAttackTimers[SPECATK_SUMMON]) + { + SpecAttackTimers[SPECATK_SUMMON] = new Timer(6000); + SpecAttackTimers[SPECATK_SUMMON]->Start(); + } + + // now check the timer + if (!SpecAttackTimers[SPECATK_SUMMON]->Check()) + return false; + + // get summon target + SetTarget(GetHateTop()); + if(target) + { + if (target->IsClient()) + target->CastToClient()->Message(15,"You have been summoned!"); + entity_list.MessageClose(this, true, 500, 10, "%s says,'You will not evade me, %s!' ", GetCleanName(), target->GetCleanName() ); + + // RangerDown - GMMove doesn't seem to be working well with players, so use MovePC for them, GMMove for NPC's + if (target->IsClient()) { + target->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), x_pos, y_pos, z_pos, target->GetHeading(), 0, SummonPC); + } + else { +#ifdef BOTS + if(target && target->IsBot()) { + // set pre summoning info to return to (to get out of melee range for caster) + target->CastToBot()->SetHasBeenSummoned(true); + 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; + } + return false; +} + +void Mob::FaceTarget(Mob* MobToFace) { + Mob* facemob = MobToFace; + if(!facemob) { + if(!GetTarget()) { + return; + } + else { + facemob = GetTarget(); + } + } + + float oldheading = GetHeading(); + float newheading = CalculateHeadingToTarget(facemob->GetX(), facemob->GetY()); + if(oldheading != newheading) { + SetHeading(newheading); + if(moving) + SendPosUpdate(); + else + { + SendPosition(); + } + } + + if(IsNPC() && !IsEngaged()) { + CastToNPC()->GetRefaceTimer()->Start(15000); + CastToNPC()->GetRefaceTimer()->Enable(); + } +} + +bool Mob::RemoveFromHateList(Mob* mob) +{ + SetRunAnimSpeed(0); + bool bFound = false; + if(IsEngaged()) + { + bFound = hate_list.RemoveEnt(mob); + if(hate_list.IsEmpty()) + { + AI_Event_NoLongerEngaged(); + zone->DelAggroMob(); + } + } + if(GetTarget() == mob) + { + SetTarget(hate_list.GetTop(this)); + } + + return bFound; +} +void Mob::WipeHateList() +{ + if(IsEngaged()) + { + AI_Event_NoLongerEngaged(); + } + hate_list.Wipe(); +} + +uint32 Mob::RandomTimer(int min,int max) { + int r = 14000; + if(min != 0 && max != 0 && min < max) + { + r = MakeRandomInt(min, max); + } + return r; +} + +uint32 NPC::GetEquipment(uint8 material_slot) const +{ + if(material_slot > 8) + return 0; + int invslot = Inventory::CalcSlotFromMaterial(material_slot); + if (invslot == -1) + return 0; + return equipment[invslot]; +} + +void Mob::SendWearChange(uint8 material_slot) +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct)); + WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer; + + wc->spawn_id = GetID(); + wc->material = GetEquipmentMaterial(material_slot); + wc->elite_material = IsEliteMaterialItem(material_slot); + wc->color.color = GetEquipmentColor(material_slot); + wc->wear_slot_id = material_slot; + + entity_list.QueueClients(this, outapp); + safe_delete(outapp); +} + +void Mob::SendTextureWC(uint8 slot, uint16 texture, uint32 hero_forge_model, uint32 elite_material, uint32 unknown06, uint32 unknown18) +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct)); + WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer; + + wc->spawn_id = this->GetID(); + wc->material = texture; + if (this->IsClient()) + wc->color.color = GetEquipmentColor(slot); + else + wc->color.color = this->GetArmorTint(slot); + wc->wear_slot_id = slot; + + wc->unknown06 = unknown06; + wc->elite_material = elite_material; + wc->hero_forge_model = hero_forge_model; + wc->unknown18 = unknown18; + + + entity_list.QueueClients(this, outapp); + safe_delete(outapp); +} + +void Mob::SetSlotTint(uint8 material_slot, uint8 red_tint, uint8 green_tint, uint8 blue_tint) +{ + uint32 color; + color = (red_tint & 0xFF) << 16; + color |= (green_tint & 0xFF) << 8; + color |= (blue_tint & 0xFF); + color |= (color) ? (0xFF << 24) : 0; + armor_tint[material_slot] = color; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct)); + WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer; + + wc->spawn_id = this->GetID(); + wc->material = GetEquipmentMaterial(material_slot); + wc->color.color = color; + wc->wear_slot_id = material_slot; + + entity_list.QueueClients(this, outapp); + safe_delete(outapp); +} + +void Mob::WearChange(uint8 material_slot, uint16 texture, uint32 color) +{ + armor_tint[material_slot] = color; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct)); + WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer; + + wc->spawn_id = this->GetID(); + wc->material = texture; + wc->color.color = color; + wc->wear_slot_id = material_slot; + + entity_list.QueueClients(this, outapp); + safe_delete(outapp); +} + +int32 Mob::GetEquipmentMaterial(uint8 material_slot) const +{ + const Item_Struct *item; + + item = database.GetItem(GetEquipment(material_slot)); + if(item != 0) + { + if // for primary and secondary we need the model, not the material + ( + material_slot == MATERIAL_PRIMARY || + material_slot == MATERIAL_SECONDARY + ) + { + 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 + { + return item->Material; + } + } + + return 0; +} + +uint32 Mob::GetEquipmentColor(uint8 material_slot) const +{ + const Item_Struct *item; + + item = database.GetItem(GetEquipment(material_slot)); + if(item != 0) + { + return item->Color; + } + + return 0; +} + +uint32 Mob::IsEliteMaterialItem(uint8 material_slot) const +{ + const Item_Struct *item; + + item = database.GetItem(GetEquipment(material_slot)); + if(item != 0) + { + return item->EliteMaterial; + } + + return 0; +} + +// works just like a printf +void Mob::Say(const char *format, ...) +{ + char buf[1000]; + va_list ap; + + va_start(ap, format); + vsnprintf(buf, 1000, format, ap); + va_end(ap); + + Mob* talker = this; + if(spellbonuses.VoiceGraft != 0) { + if(spellbonuses.VoiceGraft == GetPetID()) + talker = entity_list.GetMob(spellbonuses.VoiceGraft); + else + spellbonuses.VoiceGraft = 0; + } + + if(!talker) + talker = this; + + entity_list.MessageClose_StringID(talker, false, 200, 10, + GENERIC_SAY, GetCleanName(), buf); +} + +// +// solar: this is like the above, but the first parameter is a string id +// +void Mob::Say_StringID(uint32 string_id, const char *message3, const char *message4, const char *message5, const char *message6, const char *message7, const char *message8, const char *message9) +{ + char string_id_str[10]; + + snprintf(string_id_str, 10, "%d", string_id); + + entity_list.MessageClose_StringID(this, false, 200, 10, + GENERIC_STRINGID_SAY, GetCleanName(), string_id_str, message3, message4, message5, + message6, message7, message8, message9 + ); +} + +void Mob::Shout(const char *format, ...) +{ + char buf[1000]; + va_list ap; + + va_start(ap, format); + vsnprintf(buf, 1000, format, ap); + va_end(ap); + + entity_list.Message_StringID(this, false, MT_Shout, + GENERIC_SHOUT, GetCleanName(), buf); +} + +void Mob::Emote(const char *format, ...) +{ + char buf[1000]; + va_list ap; + + va_start(ap, format); + vsnprintf(buf, 1000, format, ap); + va_end(ap); + + entity_list.MessageClose_StringID(this, false, 200, 10, + GENERIC_EMOTE, GetCleanName(), buf); +} + +void Mob::QuestJournalledSay(Client *QuestInitiator, const char *str) +{ + entity_list.QuestJournalledSayClose(this, QuestInitiator, 200, GetCleanName(), str); +} + +const char *Mob::GetCleanName() +{ + if(!strlen(clean_name)) + { + CleanMobName(GetName(), clean_name); + } + + return clean_name; +} + +// hp event +void Mob::SetNextHPEvent( int hpevent ) +{ + nexthpevent = hpevent; +} + +void Mob::SetNextIncHPEvent( int inchpevent ) +{ + nextinchpevent = inchpevent; +} +//warp for quest function,from sandy +void Mob::Warp( float x, float y, float z ) +{ + x_pos = x; + y_pos = y; + z_pos = z; + + Mob* target = GetTarget(); + if ( target ) { + FaceTarget( target ); + } + + SendPosition(); + +} + +bool Mob::DivineAura() const +{ + uint32 l; + uint32 buff_count = GetMaxTotalSlots(); + for (l = 0; l < buff_count; l++) + { + if (buffs[l].spellid != SPELL_UNKNOWN) + { + for (int k = 0; k < EFFECT_COUNT; k++) + { + if (spells[buffs[l].spellid].effectid[k] == SE_DivineAura) + { + return true; + } + } + } + } + return false; +} + +int16 Mob::GetResist(uint8 type) const +{ + if (IsNPC()) + { + if (type == 1) + return MR + spellbonuses.MR + itembonuses.MR; + else if (type == 2) + return FR + spellbonuses.FR + itembonuses.FR; + else if (type == 3) + return CR + spellbonuses.CR + itembonuses.CR; + else if (type == 4) + return PR + spellbonuses.PR + itembonuses.PR; + else if (type == 5) + return DR + spellbonuses.DR + itembonuses.DR; + } + else if (IsClient()) + { + if (type == 1) + return CastToClient()->GetMR(); + else if (type == 2) + return CastToClient()->GetFR(); + else if (type == 3) + return CastToClient()->GetCR(); + else if (type == 4) + return CastToClient()->GetPR(); + else if (type == 5) + return CastToClient()->GetDR(); + } + return 25; +} + +uint32 Mob::GetLevelHP(uint8 tlevel) +{ + //cout<<"Tlevel: "<<(int)tlevel<= 60 && casttime > 1000) + { + casttime = casttime / 2; + if (casttime < 1000) + casttime = 1000; + } else if (level >= 50 && casttime > 1000) { + int32 cast_deduction = (casttime*(level - 49))/5; + if (cast_deduction > casttime/2) + casttime /= 2; + else + casttime -= cast_deduction; + } + return(casttime); +} + +void Mob::ExecWeaponProc(uint16 spell_id, Mob *on) { + // Changed proc targets to look up based on the spells goodEffect flag. + // This should work for the majority of weapons. + if(spell_id == SPELL_UNKNOWN || on->SpecAttacks[NO_HARM_FROM_CLIENT]){ //This is so 65535 doesn't get passed to the client message and to logs because it is not relavant information for debugging. + return; + } + + if (IsNoCast()) + return; + + if(!IsValidSpell(spell_id)){ // Check for a valid spell otherwise it will crash through the function + if(this->IsClient()){ + this->Message(0, "Invalid spell proc %u", spell_id); + mlog(CLIENT__SPELLS, "Player %s, Weapon Procced invalid spell %u", this->GetName(), spell_id); + } + return; + } + /* + + int twinproc_chance = itembonuses.TwinProc + spellbonuses.TwinProc; + if(IsClient()) + twinproc_chance += aabonuses.TwinProc; + */ + bool twinproc = false; + int32 twinproc_chance = 0; + + if(IsClient()) + twinproc_chance = CastToClient()->GetFocusEffect(focusTwincast, spell_id); + + if(twinproc_chance && (MakeRandomInt(0,99) < twinproc_chance)) + twinproc = true; + + if (IsBeneficialSpell(spell_id)) { + SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff, true); + if(twinproc) + SpellOnTarget(spell_id, this, false, false, 0, true); + } + else if(!(on->IsClient() && on->CastToClient()->dead)) { //dont proc on dead clients + SpellFinished(spell_id, on, 10, 0, -1, spells[spell_id].ResistDiff, true); + if(twinproc) + SpellOnTarget(spell_id, on, false, false, 0, true); + } + return; +} + +uint32 Mob::GetZoneID() const { + return(zone->GetZoneID()); +} + +int Mob::GetHaste() { + int h = spellbonuses.haste + spellbonuses.hastetype2 + itembonuses.haste; + int cap = 0; + int level = GetLevel(); + + if(level < 30) { // Rogean: Are these caps correct? Will use for now. + cap = 50; + } else if(level < 50) { + cap = 74; + } else if(level < 55) { + cap = 84; + } else if(level < 60) { + cap = 94; + } else { + cap = RuleI(Character, HasteCap); + } + + if(h > cap) h = cap; + + h += spellbonuses.hastetype3; + h += ExtraHaste; //GM granted haste. + + if (spellbonuses.inhibitmelee){ + if (h >= 0) + h -= spellbonuses.inhibitmelee; + + else + h -=((100+h)*spellbonuses.inhibitmelee/100); + } + + return(h); +} + +void Mob::SetTarget(Mob* mob) { + if (target == mob) return; + target = mob; + entity_list.UpdateHoTT(this); + if(IsNPC()) + parse->EventNPC(EVENT_TARGET_CHANGE, CastToNPC(), mob, "", 0); //parse->Event(EVENT_TARGET_CHANGE, this->GetNPCTypeID(), 0, this->CastToNPC(), mob); + else if (IsClient()) + parse->EventPlayer(EVENT_TARGET_CHANGE, CastToClient(), "", 0); //parse->Event(EVENT_TARGET_CHANGE, 0, "", (NPC*)NULL, this->CastToClient()); + + if(IsPet() && GetOwner() && GetOwner()->IsClient()) + GetOwner()->CastToClient()->UpdateXTargetType(MyPetTarget, mob); +} + +float Mob::FindGroundZ(float new_x, float new_y, float z_offset) +{ + float ret = -999999; + if (zone->zonemap != 0) + { + NodeRef pnode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), new_x, new_y ); + if (pnode != NODE_NONE) + { + VERTEX me; + me.x = new_x; + me.y = new_y; + me.z = z_pos+z_offset; + VERTEX hit; + FACE *onhit; + float best_z = zone->zonemap->FindBestZ(pnode, me, &hit, &onhit); + if (best_z != -999999) + { + ret = best_z; + } + } + } + return ret; +} + +// Copy of above function that isn't protected to be exported to Perl::Mob +float Mob::GetGroundZ(float new_x, float new_y, float z_offset) +{ + float ret = -999999; + if (zone->zonemap != 0) + { + NodeRef pnode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), new_x, new_y ); + if (pnode != NODE_NONE) + { + VERTEX me; + me.x = new_x; + me.y = new_y; + me.z = z_pos+z_offset; + VERTEX hit; + FACE *onhit; + float best_z = zone->zonemap->FindBestZ(pnode, me, &hit, &onhit); + if (best_z != -999999) + { + ret = best_z; + } + } + } + return ret; +} + +//helper function for npc AI; needs to be mob:: cause we need to be able to count buffs on other clients and npcs +int Mob::CountDispellableBuffs() +{ + int val = 0; + int buff_count = GetMaxTotalSlots(); + for(int x = 0; x < buff_count; x++) + { + if(!IsValidSpell(buffs[x].spellid)) + continue; + + if(buffs[x].counters) + continue; + + if(spells[buffs[x].spellid].goodEffect == 0) + continue; + + if(buffs[x].spellid != SPELL_UNKNOWN && spells[buffs[x].spellid].buffdurationformula != DF_Permanent) + val++; + } + return val; +} + +// Returns the % that a mob is snared (as a positive value). -1 means not snared +int Mob::GetSnaredAmount() +{ + int worst_snare = -1; + + int buff_count = GetMaxTotalSlots(); + for (int i = 0; i < buff_count; i++) + { + if (!IsValidSpell(buffs[i].spellid)) + continue; + + for(int j = 0; j < EFFECT_COUNT; j++) + { + if (spells[buffs[i].spellid].effectid[j] == SE_MovementSpeed) + { + int val = CalcSpellEffectValue_formula(spells[buffs[i].spellid].formula[j], spells[buffs[i].spellid].base[j], spells[buffs[i].spellid].max[j], buffs[i].casterlevel, buffs[i].spellid); + //int effect = CalcSpellEffectValue(buffs[i].spellid, spells[buffs[i].spellid].effectid[j], buffs[i].casterlevel); + if (val < 0 && abs(val) > worst_snare) + worst_snare = abs(val); + } + } + } + + return worst_snare; +} + +void Mob::TriggerDefensiveProcs(const ItemInst* weapon, Mob *on, uint16 hand, int damage) +{ + if (!on) + return; + + on->TryDefensiveProc(weapon, this, hand, damage); +} + +void Mob::SetDeltas(float dx, float dy, float dz, float dh) { + delta_x = dx; + delta_y = dy; + delta_z = dz; + delta_heading = static_cast(dh); +} + + +bool Mob::HasBuffIcon(Mob *caster, Mob *target, uint16 spell_id) +{ + if((caster->CalcBuffDuration(caster, target, spell_id)-1) > 0) + return true; + else + return false; +} + +void Mob::SetEntityVariable(const char *id, const char *m_var) +{ + std::string n_m_var = m_var; + m_EntityVariables[id] = n_m_var; +} + +const char* Mob::GetEntityVariable(const char *id) +{ + std::map::iterator iter = m_EntityVariables.find(id); + if(iter != m_EntityVariables.end()) + { + return iter->second.c_str(); + } + return NULL; +} + +bool Mob::EntityVariableExists(const char *id) +{ + std::map::iterator iter = m_EntityVariables.find(id); + if(iter != m_EntityVariables.end()) + { + return true; + } + return false; +} + +void Mob::SetFlyMode(uint8 flymode) +{ + if(IsClient() && flymode >= 0 && flymode < 3) + { + this->SendAppearancePacket(AT_Levitate, flymode); + } + else if(IsNPC() && flymode >= 0 && flymode <= 3) + { + this->SendAppearancePacket(AT_Levitate, flymode); + this->CastToNPC()->SetFlyMode(flymode); + } +} + +bool Mob::IsNimbusEffectActive(uint32 nimbus_effect) +{ + if(nimbus_effect1 == nimbus_effect || nimbus_effect2 == nimbus_effect || nimbus_effect3 == nimbus_effect) + { + return true; + } + return false; +} + +void Mob::SetNimbusEffect(uint32 nimbus_effect) +{ + if(nimbus_effect1 == 0) + { + nimbus_effect1 = nimbus_effect; + } + else if(nimbus_effect2 == 0) + { + nimbus_effect2 = nimbus_effect; + } + else + { + nimbus_effect3 = nimbus_effect; + } +} + +void Mob::TryTriggerOnCast(uint32 spell_id, bool aa_trigger) +{ + if(!IsValidSpell(spell_id)) + return; + + if (aabonuses.SpellTriggers[0] || spellbonuses.SpellTriggers[0] || itembonuses.SpellTriggers[0]){ + + for(int i = 0; i < MAX_SPELL_TRIGGER; i++){ + + if(aabonuses.SpellTriggers[i] && IsClient()) + TriggerOnCast(aabonuses.SpellTriggers[i], spell_id,1); + + if(spellbonuses.SpellTriggers[i]) + TriggerOnCast(spellbonuses.SpellTriggers[i], spell_id,0); + + if(itembonuses.SpellTriggers[i]) + TriggerOnCast(spellbonuses.SpellTriggers[i], spell_id,0); + } + } +} + + +void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger) +{ + if(!IsValidSpell(focus_spell) || !IsValidSpell(spell_id)) + return; + + uint32 trigger_spell_id = 0; + + if (aa_trigger && IsClient()){ + //focus_spell = aaid + trigger_spell_id = CastToClient()->CalcAAFocus(focusTriggerOnCast, focus_spell, spell_id); + + if(IsValidSpell(trigger_spell_id) && GetTarget()) + SpellFinished(trigger_spell_id, GetTarget()); + } + + else{ + trigger_spell_id = CalcFocusEffect(focusTriggerOnCast, focus_spell, spell_id); + + if(IsValidSpell(trigger_spell_id) && GetTarget()){ + SpellFinished(trigger_spell_id, GetTarget()); + CheckHitsRemaining(0, false,false, 0, focus_spell); + } + } +} + +void Mob::TrySpellTrigger(Mob *target, uint32 spell_id) +{ + if(target == NULL || !IsValidSpell(spell_id)) + { + return; + } + int spell_trig = 0; + // Count all the percentage chances to trigger for all effects + for(int i = 0; i < EFFECT_COUNT; i++) + { + if (spells[spell_id].effectid[i] == SE_SpellTrigger) + spell_trig += spells[spell_id].base[i]; + } + // If all the % add to 100, then only one of the effects can fire but one has to fire. + if (spell_trig == 100) + { + int trig_chance = 100; + for(int i = 0; i < EFFECT_COUNT; i++) + { + if (spells[spell_id].effectid[i] == SE_SpellTrigger) + { + 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); + break; + } + else + { + // Increase the chance to fire for the next effect, if all effects fail, the final effect will fire. + trig_chance -= spells[spell_id].base[i]; + } + } + + } + } + // 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 (spells[spell_id].effectid[i] == SE_SpellTrigger) + { + if(MakeRandomInt(0, 100) <= spells[spell_id].base[i]) + { + SpellFinished(spells[spell_id].base2[i], target); + } + } + } + } +} + +void Mob::TryApplyEffect(Mob *target, uint32 spell_id) +{ + if(target == NULL || !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); + } + } + } +} +//Twincast Focus effects should stack across different types (Spell, AA - when implemented ect) +void Mob::TryTwincast(Mob *caster, Mob *target, uint32 spell_id) +{ + if(!IsValidSpell(spell_id)) + return; + + if(IsClient()) + { + int32 focus = CastToClient()->GetFocusEffect(focusTwincast, spell_id); + + if (focus > 0) + { + if(MakeRandomInt(0, 100) <= focus) + { + Message(MT_Spells,"You twincast %s!",spells[spell_id].name); + SpellFinished(spell_id, target); + } + } + } + + //Retains function for non clients + else if (spellbonuses.FocusEffects[focusTwincast] || itembonuses.FocusEffects[focusTwincast]) + { + int buff_count = GetMaxTotalSlots(); + for(int i = 0; i < buff_count; i++) + { + if(IsEffectInSpell(buffs[i].spellid, SE_Twincast)) + { + int32 focus = CalcFocusEffect(focusTwincast, buffs[i].spellid, spell_id); + if(focus > 0) + { + if(MakeRandomInt(0, 100) <= focus) + { + SpellFinished(spell_id, target); + } + } + } + } + } +} + +int32 Mob::GetVulnerability(int32 damage, Mob *caster, uint32 spell_id, uint32 ticsremaining) +{ + if (!caster) + return damage; + //Apply innate vulnerabilities + if (Vulnerability_Mod[GetSpellResistType(spell_id)] != 0) + damage += damage * Vulnerability_Mod[GetSpellResistType(spell_id)] / 100; + + + else if (Vulnerability_Mod[HIGHEST_RESIST+1] != 0) + damage += damage * Vulnerability_Mod[HIGHEST_RESIST+1] / 100; + + //Apply spell derived vulnerabilities + if (spellbonuses.FocusEffects[focusSpellVulnerability]){ + + int32 tmp_focus = 0; + int tmp_buffslot = -1; + + int buff_count = GetMaxTotalSlots(); + for(int i = 0; i < buff_count; i++) { + + if((IsValidSpell(buffs[i].spellid) && IsEffectInSpell(buffs[i].spellid, SE_SpellVulnerability))){ + + int32 focus = caster->CalcFocusEffect(focusSpellVulnerability, buffs[i].spellid, spell_id); + + if (!focus) + continue; + + if (tmp_focus && focus > tmp_focus){ + tmp_focus = focus; + tmp_buffslot = i; + } + + else if (!tmp_focus){ + tmp_focus = focus; + tmp_buffslot = i; + } + + } + } + + if (tmp_focus < -99) + tmp_focus = -99; + + damage += damage * tmp_focus / 100; + + if (tmp_buffslot >= 0) + CheckHitsRemaining(tmp_buffslot); + } + return damage; +} + +int16 Mob::GetSkillDmgTaken(const SkillType skill_used) +{ + int skilldmg_mod = 0; + + // All skill dmg mod + Skill specific + skilldmg_mod += itembonuses.SkillDmgTaken[HIGHEST_SKILL+1] + spellbonuses.SkillDmgTaken[HIGHEST_SKILL+1] + + itembonuses.SkillDmgTaken[skill_used] + spellbonuses.SkillDmgTaken[skill_used]; + + //Innate SetSkillDamgeTaken(skill,value) + if ((SkillDmgTaken_Mod[skill_used]) || (SkillDmgTaken_Mod[HIGHEST_SKILL+1])) + skilldmg_mod += SkillDmgTaken_Mod[skill_used] + SkillDmgTaken_Mod[HIGHEST_SKILL+1]; + + if(skilldmg_mod < -100) + skilldmg_mod = -100; + + if (spellbonuses.SkillDmgTaken[HIGHEST_SKILL+1] || spellbonuses.SkillDmgTaken[skill_used]) + CheckHitsRemaining(0, false,false, SE_SkillDamageTaken,0,true,skill_used); + + return skilldmg_mod; +} + +int16 Mob::GetHealRate(uint16 spell_id) +{ + Mob* target = GetTarget(); + + int16 heal_rate = 0; + + if (target){ + heal_rate = target->itembonuses.HealRate + target->spellbonuses.HealRate; + + if (target->IsClient()) + heal_rate += target->CastToClient()->GetFocusEffect(focusHealRate, spell_id); + + if(heal_rate < -99) + heal_rate = -99; + } + + return heal_rate; +} + +int16 Mob::GetCriticalHealRate(uint16 spell_id) +{ + Mob* target = GetTarget(); + + int16 critical_heal_rate = 0; + + if (target && target->IsClient()) + critical_heal_rate = target->CastToClient()->GetFocusEffect(focusCriticalHealRate, spell_id); + + return critical_heal_rate; +} + +bool Mob::TryFadeEffect(int slot) +{ + if(IsValidSpell(buffs[slot].spellid)) + { + for(int i = 0; i < EFFECT_COUNT; i++) + { + if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnWearoff || spells[buffs[slot].spellid].effectid[i] == SE_EffectOnFade) + { + uint16 spell_id = spells[buffs[slot].spellid].base[i]; + BuffFadeBySlot(slot); + + if(spell_id) + { + + if(spell_id == SPELL_UNKNOWN) + return false; + + if(IsValidSpell(spell_id)) + { + if (IsBeneficialSpell(spell_id)) { + SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff); + } + else if(!(IsClient() && CastToClient()->dead)) { + SpellFinished(spell_id, this, 10, 0, -1, spells[spell_id].ResistDiff); + } + return true; + } + } + } + } + } + return false; +} + +void Mob::TrySympatheticProc(Mob *target, uint32 spell_id) +{ + if(target == NULL || !IsValidSpell(spell_id)) + return; + + int focus_spell = CastToClient()->GetSympatheticFocusEffect(focusSympatheticProc,spell_id); + + if(IsValidSpell(focus_spell)){ + int focus_trigger = spells[focus_spell].base2[0]; + // For beneficial spells, if the triggered spell is also beneficial then proc it on the target + // if the triggered spell is detrimental, then it will trigger on the caster(ie cursed items) + if(IsBeneficialSpell(spell_id)) + { + if(IsBeneficialSpell(focus_trigger)) + SpellFinished(focus_trigger, target); + + else + SpellFinished(focus_trigger, this); + } + // For detrimental spells, if the triggered spell is beneficial, then it will land on the caster + // if the triggered spell is also detrimental, then it will land on the target + else + { + if(IsBeneficialSpell(focus_trigger)) + SpellFinished(focus_trigger, this); + + else + SpellFinished(focus_trigger, target); + } + CheckHitsRemaining(0, false,false, 0, focus_spell); + } +} + +uint32 Mob::GetItemStat(uint32 itemid, const char *identifier) +{ + const ItemInst* inst = database.CreateItem(itemid); + if (!inst) + return 0; + + const Item_Struct* item = inst->GetItem(); + if (!item) + return 0; + + if (!identifier) + return 0; + + uint32 stat = 0; + + std::string id = identifier; + for(int i = 0; i < id.length(); ++i) + { + id[i] = tolower(id[i]); + } + + if (id == "itemclass") + stat = uint32(item->ItemClass); + if (id == "id") + stat = uint32(item->ID); + if (id == "weight") + stat = uint32(item->Weight); + if (id == "norent") + stat = uint32(item->NoRent); + if (id == "nodrop") + stat = uint32(item->NoDrop); + if (id == "size") + stat = uint32(item->Size); + if (id == "slots") + stat = uint32(item->Slots); + if (id == "price") + stat = uint32(item->Price); + if (id == "icon") + stat = uint32(item->Icon); + if (id == "loregroup") + stat = uint32(item->LoreGroup); + if (id == "loreflag") + stat = uint32(item->LoreFlag); + if (id == "pendingloreflag") + stat = uint32(item->PendingLoreFlag); + if (id == "artifactflag") + stat = uint32(item->ArtifactFlag); + if (id == "summonedflag") + stat = uint32(item->SummonedFlag); + if (id == "fvnodrop") + stat = uint32(item->FVNoDrop); + if (id == "favor") + stat = uint32(item->Favor); + if (id == "guildfavor") + stat = uint32(item->GuildFavor); + if (id == "pointtype") + stat = uint32(item->PointType); + if (id == "bagtype") + stat = uint32(item->BagType); + if (id == "bagslots") + stat = uint32(item->BagSlots); + if (id == "bagsize") + stat = uint32(item->BagSize); + if (id == "bagwr") + stat = uint32(item->BagWR); + if (id == "benefitflag") + stat = uint32(item->BenefitFlag); + if (id == "tradeskills") + stat = uint32(item->Tradeskills); + if (id == "cr") + stat = uint32(item->CR); + if (id == "dr") + stat = uint32(item->DR); + if (id == "pr") + stat = uint32(item->PR); + if (id == "mr") + stat = uint32(item->MR); + if (id == "fr") + stat = uint32(item->FR); + if (id == "astr") + stat = uint32(item->AStr); + if (id == "asta") + stat = uint32(item->ASta); + if (id == "aagi") + stat = uint32(item->AAgi); + if (id == "adex") + stat = uint32(item->ADex); + if (id == "acha") + stat = uint32(item->ACha); + if (id == "aint") + stat = uint32(item->AInt); + if (id == "awis") + stat = uint32(item->AWis); + if (id == "hp") + stat = uint32(item->HP); + if (id == "mana") + stat = uint32(item->Mana); + if (id == "ac") + stat = uint32(item->AC); + if (id == "deity") + stat = uint32(item->Deity); + if (id == "skillmodvalue") + stat = uint32(item->SkillModValue); + if (id == "skillmodtype") + stat = uint32(item->SkillModType); + if (id == "banedmgrace") + stat = uint32(item->BaneDmgRace); + if (id == "banedmgamt") + stat = uint32(item->BaneDmgAmt); + if (id == "banedmgbody") + stat = uint32(item->BaneDmgBody); + if (id == "magic") + stat = uint32(item->Magic); + if (id == "casttime_") + stat = uint32(item->CastTime_); + if (id == "reqlevel") + stat = uint32(item->ReqLevel); + if (id == "bardtype") + stat = uint32(item->BardType); + if (id == "bardvalue") + stat = uint32(item->BardValue); + if (id == "light") + stat = uint32(item->Light); + if (id == "delay") + stat = uint32(item->Delay); + if (id == "reclevel") + stat = uint32(item->RecLevel); + if (id == "recskill") + stat = uint32(item->RecSkill); + if (id == "elemdmgtype") + stat = uint32(item->ElemDmgType); + if (id == "elemdmgamt") + stat = uint32(item->ElemDmgAmt); + if (id == "range") + stat = uint32(item->Range); + if (id == "damage") + stat = uint32(item->Damage); + if (id == "color") + stat = uint32(item->Color); + if (id == "classes") + stat = uint32(item->Classes); + if (id == "races") + stat = uint32(item->Races); + if (id == "maxcharges") + stat = uint32(item->MaxCharges); + if (id == "itemtype") + stat = uint32(item->ItemType); + if (id == "material") + stat = uint32(item->Material); + if (id == "casttime") + stat = uint32(item->CastTime); + if (id == "elitematerial") + stat = uint32(item->EliteMaterial); + if (id == "procrate") + stat = uint32(item->ProcRate); + if (id == "combateffects") + stat = uint32(item->CombatEffects); + if (id == "shielding") + stat = uint32(item->Shielding); + if (id == "stunresist") + stat = uint32(item->StunResist); + if (id == "strikethrough") + stat = uint32(item->StrikeThrough); + if (id == "extradmgskill") + stat = uint32(item->ExtraDmgSkill); + if (id == "extradmgamt") + stat = uint32(item->ExtraDmgAmt); + if (id == "spellshield") + stat = uint32(item->SpellShield); + if (id == "avoidance") + stat = uint32(item->Avoidance); + if (id == "accuracy") + stat = uint32(item->Accuracy); + if (id == "charmfileid") + stat = uint32(item->CharmFileID); + if (id == "factionmod1") + stat = uint32(item->FactionMod1); + if (id == "factionmod2") + stat = uint32(item->FactionMod2); + if (id == "factionmod3") + stat = uint32(item->FactionMod3); + if (id == "factionmod4") + stat = uint32(item->FactionMod4); + if (id == "factionamt1") + stat = uint32(item->FactionAmt1); + if (id == "factionamt2") + stat = uint32(item->FactionAmt2); + if (id == "factionamt3") + stat = uint32(item->FactionAmt3); + if (id == "factionamt4") + stat = uint32(item->FactionAmt4); + if (id == "augtype") + stat = uint32(item->AugType); + if (id == "ldontheme") + stat = uint32(item->LDoNTheme); + if (id == "ldonprice") + stat = uint32(item->LDoNPrice); + if (id == "ldonsold") + stat = uint32(item->LDoNSold); + if (id == "banedmgraceamt") + stat = uint32(item->BaneDmgRaceAmt); + if (id == "augrestrict") + stat = uint32(item->AugRestrict); + if (id == "endur") + stat = uint32(item->Endur); + if (id == "dotshielding") + stat = uint32(item->DotShielding); + if (id == "attack") + stat = uint32(item->Attack); + if (id == "regen") + stat = uint32(item->Regen); + if (id == "manaregen") + stat = uint32(item->ManaRegen); + if (id == "enduranceregen") + stat = uint32(item->EnduranceRegen); + if (id == "haste") + stat = uint32(item->Haste); + if (id == "damageshield") + stat = uint32(item->DamageShield); + if (id == "recastdelay") + stat = uint32(item->RecastDelay); + if (id == "recasttype") + stat = uint32(item->RecastType); + if (id == "augdistiller") + stat = uint32(item->AugDistiller); + if (id == "attuneable") + stat = uint32(item->Attuneable); + if (id == "nopet") + stat = uint32(item->NoPet); + if (id == "potionbelt") + stat = uint32(item->PotionBelt); + if (id == "stackable") + stat = uint32(item->Stackable); + if (id == "notransfer") + stat = uint32(item->NoTransfer); + if (id == "questitemflag") + stat = uint32(item->QuestItemFlag); + if (id == "stacksize") + stat = uint32(item->StackSize); + if (id == "potionbeltslots") + stat = uint32(item->PotionBeltSlots); + if (id == "book") + stat = uint32(item->Book); + if (id == "booktype") + stat = uint32(item->BookType); + if (id == "svcorruption") + stat = uint32(item->SVCorruption); + if (id == "purity") + stat = uint32(item->Purity); + if (id == "backstabdmg") + stat = uint32(item->BackstabDmg); + if (id == "dsmitigation") + stat = uint32(item->DSMitigation); + if (id == "heroicstr") + stat = uint32(item->HeroicStr); + if (id == "heroicint") + stat = uint32(item->HeroicInt); + if (id == "heroicwis") + stat = uint32(item->HeroicWis); + if (id == "heroicagi") + stat = uint32(item->HeroicAgi); + if (id == "heroicdex") + stat = uint32(item->HeroicDex); + if (id == "heroicsta") + stat = uint32(item->HeroicSta); + if (id == "heroiccha") + stat = uint32(item->HeroicCha); + if (id == "heroicmr") + stat = uint32(item->HeroicMR); + if (id == "heroicfr") + stat = uint32(item->HeroicFR); + if (id == "heroiccr") + stat = uint32(item->HeroicCR); + if (id == "heroicdr") + stat = uint32(item->HeroicDR); + if (id == "heroicpr") + stat = uint32(item->HeroicPR); + if (id == "heroicsvcorrup") + stat = uint32(item->HeroicSVCorrup); + if (id == "healamt") + stat = uint32(item->HealAmt); + if (id == "spelldmg") + stat = uint32(item->SpellDmg); + if (id == "ldonsellbackrate") + stat = uint32(item->LDoNSellBackRate); + if (id == "scriptfileid") + stat = uint32(item->ScriptFileID); + if (id == "expendablearrow") + stat = uint32(item->ExpendableArrow); + if (id == "clairvoyance") + stat = uint32(item->Clairvoyance); + // Begin Effects + if (id == "clickeffect") + stat = uint32(item->Click.Effect); + if (id == "clicktype") + stat = uint32(item->Click.Type); + if (id == "clicklevel") + stat = uint32(item->Click.Level); + if (id == "clicklevel2") + stat = uint32(item->Click.Level2); + if (id == "proceffect") + stat = uint32(item->Proc.Effect); + if (id == "proctype") + stat = uint32(item->Proc.Type); + if (id == "proclevel") + stat = uint32(item->Proc.Level); + if (id == "proclevel2") + stat = uint32(item->Proc.Level2); + if (id == "worneffect") + stat = uint32(item->Worn.Effect); + if (id == "worntype") + stat = uint32(item->Worn.Type); + if (id == "wornlevel") + stat = uint32(item->Worn.Level); + if (id == "wornlevel2") + stat = uint32(item->Worn.Level2); + if (id == "focuseffect") + stat = uint32(item->Focus.Effect); + if (id == "focustype") + stat = uint32(item->Focus.Type); + if (id == "focuslevel") + stat = uint32(item->Focus.Level); + if (id == "focuslevel2") + stat = uint32(item->Focus.Level2); + if (id == "scrolleffect") + stat = uint32(item->Scroll.Effect); + if (id == "scrolltype") + stat = uint32(item->Scroll.Type); + if (id == "scrolllevel") + stat = uint32(item->Scroll.Level); + if (id == "scrolllevel2") + stat = uint32(item->Scroll.Level2); + + safe_delete(inst); + return stat; +} + +void Mob::SetGlobal(const char *varname, const char *newvalue, int options, const char *duration, Mob *other) { + + int qgZoneid = zone->GetZoneID(); + int qgCharid = 0; + int qgNpcid = 0; + + if (this->IsNPC()) + { + qgNpcid = this->GetNPCTypeID(); + } + else if (other && other->IsNPC()) + { + qgNpcid = other->GetNPCTypeID(); + } + + if (this->IsClient()) + { + qgCharid = this->CastToClient()->CharacterID(); + } + else if (other && other->IsClient()) + { + qgCharid = other->CastToClient()->CharacterID(); + } + else + { + qgCharid = -qgNpcid; // make char id negative npc id as a fudge + } + + if (options < 0 || options > 7) + { + //cerr << "Invalid options for global var " << varname << " using defaults" << endl; + options = 0; // default = 0 (only this npcid,player and zone) + } + else + { + if (options & 1) + qgNpcid=0; + if (options & 2) + qgCharid=0; + if (options & 4) + qgZoneid=0; + } + + InsertQuestGlobal(qgCharid, qgNpcid, qgZoneid, varname, newvalue, QGVarDuration(duration)); +} + +void Mob::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 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); + + if(zone) + { + ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct)); + ServerQGlobalDelete_Struct *qgu = (ServerQGlobalDelete_Struct*)pack->pBuffer; + + 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); + } +} + +// 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" + 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); + + 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); + + 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); + + worldserver.SendPacket(pack); + safe_delete(pack); + + //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); + strcpy((char*)qgu->value, varvalue); + qgu->id = last_id; + 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); + } + +} + +// Converts duration string to duration value (in seconds) +// Return of INT_MAX indicates infinite duration +int Mob::QGVarDuration(const char *fmt) +{ + int duration = 0; + + // format: Y#### or D## or H## or M## or S## or T###### or C####### + + int len = static_cast(strlen(fmt)); + + // Default to no duration + if (len < 1) + return 0; + + // Set val to value after type character + // e.g., for "M3924", set to 3924 + int val = atoi(&fmt[0] + 1); + + switch (fmt[0]) + { + // Forever + case 'F': + case 'f': + duration = INT_MAX; + break; + // Years + case 'Y': + case 'y': + duration = val * 31556926; + break; + case 'D': + case 'd': + duration = val * 86400; + break; + // Hours + case 'H': + case 'h': + duration = val * 3600; + break; + // Minutes + case 'M': + case 'm': + duration = val * 60; + break; + // Seconds + case 'S': + case 's': + duration = val; + break; + // Invalid + default: + duration = 0; + break; + } + + return duration; +} + +void Mob::DoKnockback(Mob *caster, uint32 pushback, uint32 pushup) +{ + if(IsClient()) + { + CastToClient()->SetKnockBackExemption(true); + + EQApplicationPacket* outapp_push = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer; + + double look_heading = caster->CalculateHeadingToTarget(GetX(), GetY()); + look_heading /= 256; + look_heading *= 360; + if(look_heading > 360) + look_heading -= 360; + + //x and y are crossed mkay + double new_x = pushback * sin(double(look_heading * 3.141592 / 180.0)); + double new_y = pushback * cos(double(look_heading * 3.141592 / 180.0)); + + spu->spawn_id = GetID(); + spu->x_pos = FloatToEQ19(GetX()); + spu->y_pos = FloatToEQ19(GetY()); + spu->z_pos = FloatToEQ19(GetZ()); + spu->delta_x = NewFloatToEQ13(static_cast(new_x)); + spu->delta_y = NewFloatToEQ13(static_cast(new_y)); + spu->delta_z = NewFloatToEQ13(static_cast(pushup)); + spu->heading = FloatToEQ19(GetHeading()); + spu->padding0002 =0; + spu->padding0006 =7; + spu->padding0014 =0x7f; + spu->padding0018 =0x5df27; + spu->animation = 0; + spu->delta_heading = NewFloatToEQ13(0); + outapp_push->priority = 6; + entity_list.QueueClients(this, outapp_push, true); + CastToClient()->FastQueuePacket(&outapp_push); + } +} + +void Mob::TrySpellOnKill(uint8 level, uint16 spell_id) +{ + if (spell_id != SPELL_UNKNOWN) + { + if(IsEffectInSpell(spell_id, SE_SpellOnKill2)) { + for (int i = 0; i < EFFECT_COUNT; i++) { + if (spells[spell_id].effectid[i] == SE_SpellOnKill2) + { + if (spells[spell_id].max[i] <= level) + { + if(MakeRandomInt(0,99) < spells[spell_id].base[i]) + SpellFinished(spells[spell_id].base2[i], this); + } + } + } + } + } + + if (!aabonuses.SpellOnKill[0] && !itembonuses.SpellOnKill[0] && !spellbonuses.SpellOnKill[0]) + return; + + // Allow to check AA, items and buffs in all cases. Base2 = Spell to fire | Base1 = % chance | Base3 = min level + for(int i = 0; i < MAX_SPELL_TRIGGER*3; i+=3) { + + if(aabonuses.SpellOnKill[i] && (level >= aabonuses.SpellOnKill[i + 2])) { + if(MakeRandomInt(0, 99) < static_cast(aabonuses.SpellOnKill[i + 1])) + SpellFinished(aabonuses.SpellOnKill[i], this); + } + + if(itembonuses.SpellOnKill[i] && (level >= itembonuses.SpellOnKill[i + 2])){ + if(MakeRandomInt(0, 99) < static_cast(itembonuses.SpellOnKill[i + 1])) + SpellFinished(itembonuses.SpellOnKill[i], this); + } + + if(spellbonuses.SpellOnKill[i] && (level >= spellbonuses.SpellOnKill[i + 2])) { + if(MakeRandomInt(0, 99) < static_cast(spellbonuses.SpellOnKill[i + 1])) + SpellFinished(spellbonuses.SpellOnKill[i], this); + } + + } +} + +bool Mob::TrySpellOnDeath() +{ + if (IsNPC() && !spellbonuses.SpellOnDeath[0] && !itembonuses.SpellOnDeath[0]) + return false; + + if (IsClient() && !aabonuses.SpellOnDeath[0] && !spellbonuses.SpellOnDeath[0] && !itembonuses.SpellOnDeath[0]) + return false; + + for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) { + if(IsClient() && aabonuses.SpellOnDeath[i]) { + if(MakeRandomInt(0, 99) < static_cast(aabonuses.SpellOnDeath[i + 1])) { + SpellFinished(aabonuses.SpellOnDeath[i], this); + } + } + + if(itembonuses.SpellOnDeath[i]) { + if(MakeRandomInt(0, 99) < static_cast(itembonuses.SpellOnDeath[i + 1])) { + SpellFinished(itembonuses.SpellOnDeath[i], this); + } + } + + if(spellbonuses.SpellOnDeath[i]) { + if(MakeRandomInt(0, 99) < static_cast(spellbonuses.SpellOnDeath[i + 1])) { + SpellFinished(spellbonuses.SpellOnDeath[i], this); + } + } + } + + BuffFadeAll(); + return false; + //You should not be able to use this effect and survive (ALWAYS return false), + //attempting to place a heal in these effects will still result + //in death because the heal will not register before the script kills you. +} + +int16 Mob::GetCritDmgMob(uint16 skill) +{ + int critDmg_mod = 0; + + // All skill dmg mod + Skill specific + critDmg_mod += itembonuses.CritDmgMob[HIGHEST_SKILL+1] + spellbonuses.CritDmgMob[HIGHEST_SKILL+1] + aabonuses.CritDmgMob[HIGHEST_SKILL+1] + + itembonuses.CritDmgMob[skill] + spellbonuses.CritDmgMob[skill] + aabonuses.CritDmgMob[skill]; + + if(critDmg_mod < -100) + critDmg_mod = -100; + + return critDmg_mod; +} + +void Mob::SetGrouped(bool v) +{ + if(v) + { + israidgrouped = false; + } + isgrouped = v; + + if(IsClient()) + { + parse->EventPlayer(EVENT_GROUP_CHANGE, CastToClient(), "", 0); + + if(!v) + CastToClient()->RemoveGroupXTargets(); + } +} + +void Mob::SetRaidGrouped(bool v) +{ + if(v) + { + isgrouped = false; + } + israidgrouped = v; + + if(IsClient()) + { + parse->EventPlayer(EVENT_GROUP_CHANGE, CastToClient(), "", 0); + } +} + +int16 Mob::GetCriticalChanceBonus(uint16 skill) +{ + int critical_chance = 0; + + // All skills + Skill specific + critical_chance += itembonuses.CriticalHitChance[HIGHEST_SKILL+1] + spellbonuses.CriticalHitChance[HIGHEST_SKILL+1] + aabonuses.CriticalHitChance[HIGHEST_SKILL+1] + + itembonuses.CriticalHitChance[skill] + spellbonuses.CriticalHitChance[skill] + aabonuses.CriticalHitChance[skill]; + + if(critical_chance < -100) + critical_chance = -100; + + return critical_chance; +} + +int16 Mob::GetMeleeDamageMod_SE(uint16 skill) +{ + int dmg_mod = 0; + + // All skill dmg mod + Skill specific + dmg_mod += itembonuses.DamageModifier[HIGHEST_SKILL+1] + spellbonuses.DamageModifier[HIGHEST_SKILL+1] + aabonuses.DamageModifier[HIGHEST_SKILL+1] + + itembonuses.DamageModifier[skill] + spellbonuses.DamageModifier[skill] + aabonuses.DamageModifier[skill]; + + if(dmg_mod < -100) + dmg_mod = -100; + + if (spellbonuses.DamageModifier[HIGHEST_SKILL+1] || spellbonuses.DamageModifier[skill]) + CheckHitsRemaining(0, false, false, SE_DamageModifier,0,true,skill); + + return dmg_mod; +} + +int16 Mob::GetMeleeMinDamageMod_SE(uint16 skill) +{ + int dmg_mod = 0; + + dmg_mod = itembonuses.MinDamageModifier[skill] + spellbonuses.MinDamageModifier[skill] + + itembonuses.MinDamageModifier[HIGHEST_SKILL+1] + spellbonuses.MinDamageModifier[HIGHEST_SKILL+1]; + + if(dmg_mod < -100) + dmg_mod = -100; + + return dmg_mod; +} + +int16 Mob::GetCrippBlowChance() +{ + int16 crip_chance = 0; + + crip_chance += itembonuses.CrippBlowChance + spellbonuses.CrippBlowChance + aabonuses.CrippBlowChance; + + if(crip_chance < 0) + crip_chance = 0; + + return crip_chance; +} + +int16 Mob::GetSkillReuseTime(uint16 skill) +{ + int skill_reduction = this->itembonuses.SkillReuseTime[skill] + this->spellbonuses.SkillReuseTime[skill] + this->aabonuses.SkillReuseTime[skill]; + + return skill_reduction; +} + +int16 Mob::GetSkillDmgAmt(uint16 skill) +{ + int skill_dmg = 0; + + // All skill dmg(only spells do this) + Skill specific + skill_dmg += spellbonuses.SkillDamageAmount[HIGHEST_SKILL+1] + itembonuses.SkillDamageAmount[HIGHEST_SKILL+1] + aabonuses.SkillDamageAmount[HIGHEST_SKILL+1] + + itembonuses.SkillDamageAmount[skill] + spellbonuses.SkillDamageAmount[skill] + aabonuses.SkillDamageAmount[skill]; + + skill_dmg += spellbonuses.SkillDamageAmount2[HIGHEST_SKILL+1] + itembonuses.SkillDamageAmount2[HIGHEST_SKILL+1] + + itembonuses.SkillDamageAmount2[skill] + spellbonuses.SkillDamageAmount2[skill]; + + // Deplete the buff if needed + if (spellbonuses.SkillDamageAmount[HIGHEST_SKILL+1] || spellbonuses.SkillDamageAmount[skill]) + CheckHitsRemaining(0, false,false, SE_SkillDamageAmount,0,true,skill); + + if (spellbonuses.SkillDamageAmount2[HIGHEST_SKILL+1] || spellbonuses.SkillDamageAmount2[skill]) + CheckHitsRemaining(0, false,false, SE_SkillDamageAmount2,0,true,skill); + + return skill_dmg; +} + +bool Mob::TryReflectSpell(uint32 spell_id) +{ + if(!GetTarget()) + return false; + + if(GetTarget()->spellbonuses.reflect_chance) + CheckHitsRemaining(0, false, false, SE_Reflect); + + if(MakeRandomInt(0, 99) < (GetTarget()->itembonuses.reflect_chance + GetTarget()->spellbonuses.reflect_chance)) + return true; + + return false; +} + +void Mob::DoGravityEffect() +{ + Mob *caster = NULL; + int away = -1; + float caster_x, caster_y, amount, value, cur_x, my_x, cur_y, my_y, x_vector, y_vector, hypot; + + // Set values so we can run through all gravity effects and then apply the culmative move at the end + // instead of many small moves if the mob/client had more than 1 gravity effect on them + cur_x = my_x = GetX(); + cur_y = my_y = GetY(); + + int buff_count = GetMaxTotalSlots(); + for (int slot = 0; slot < buff_count; slot++) + { + if (buffs[slot].spellid != SPELL_UNKNOWN && IsEffectInSpell(buffs[slot].spellid, SE_GravityEffect)) + { + for (int i = 0; i < EFFECT_COUNT; i++) + { + if(spells[buffs[slot].spellid].effectid[i] == SE_GravityEffect) { + + int casterId = buffs[slot].casterid; + if(casterId) + caster = entity_list.GetMob(casterId); + + if(!caster || casterId == this->GetID()) + continue; + + caster_x = caster->GetX(); + caster_y = caster->GetY(); + + value = static_cast(spells[buffs[slot].spellid].base[i]); + if(value == 0) + continue; + + if(value > 0) + away = 1; + + amount = fabs(value) / (100.0f); // to bring the values in line, arbitarily picked + + x_vector = cur_x - caster_x; + y_vector = cur_y - caster_y; + hypot = sqrt(x_vector*x_vector + y_vector*y_vector); + + if(hypot <= 5) // dont want to be inside the mob, even though we can, it looks bad + continue; + + x_vector /= hypot; + y_vector /= hypot; + + cur_x = cur_x + (x_vector * amount * away); + cur_y = cur_y + (y_vector * amount * away); + } + } + } + } + + if((fabs(my_x - cur_x) > 0.01) || (fabs(my_y - cur_y) > 0.01)) { + float new_ground = GetGroundZ(cur_x, cur_y); + // If we cant get LoS on our new spot then keep checking up to 5 units up. + if(!CheckLosFN(cur_x, cur_y, new_ground, GetSize())) { + for(float z_adjust = 0.1f; z_adjust < 5; z_adjust += 0.1f) { + if(CheckLosFN(cur_x, cur_y, new_ground+z_adjust, GetSize())) { + new_ground += z_adjust; + break; + } + } + // If we still fail, then lets only use the x portion(ie sliding around a wall) + if(!CheckLosFN(cur_x, my_y, new_ground, GetSize())) { + // If that doesnt work, try the y + if(!CheckLosFN(my_x, cur_y, new_ground, GetSize())) { + // If everything fails, then lets do nothing + return; + } + else { + cur_x = my_x; + } + } + else { + cur_y = my_y; + } + } + + if(IsClient()) + this->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), cur_x, cur_y, new_ground, GetHeading()*2); // I know the heading thing is weird(chance of movepc to halve the heading value, too lazy to figure out why atm) + else + this->GMMove(cur_x, cur_y, new_ground, GetHeading()); + } +} + +void Mob::SpreadVirus(uint16 spell_id, uint16 casterID) +{ + int num_targs = spells[spell_id].viral_targets; + + Mob* caster = entity_list.GetMob(casterID); + Mob* target = NULL; + // Only spread in zones without perm buffs + if(!zone->BuffTimersSuspended()) { + for(int i = 0; i < num_targs; i++) { + target = entity_list.GetTargetForVirus(this); + if(target) { + // Only spreads to the uninfected + if(!target->FindBuff(spell_id)) { + if(caster) + caster->SpellOnTarget(spell_id, target); + + } + } + } + } +} + +void Mob::RemoveNimbusEffect(int effectid) +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RemoveNimbusEffect, sizeof(RemoveNimbusEffect_Struct)); + RemoveNimbusEffect_Struct* rne = (RemoveNimbusEffect_Struct*)outapp->pBuffer; + rne->spawnid = GetID(); + rne->nimbus_effect = effectid; + entity_list.QueueClients(this, outapp); + safe_delete(outapp); +} + +bool Mob::IsBoat() const { + return (race == 72 || race == 73 || race == 114 || race == 404 || race == 550 || race == 551 || race == 552); +} + +void Mob::SetBodyType(bodyType new_body, bool overwrite_orig) { + bool needs_spawn_packet = false; + if(bodytype == 11 || bodytype >= 65 || new_body == 11 || new_body >= 65) { + needs_spawn_packet = true; + } + + if(overwrite_orig) { + orig_bodytype = new_body; + } + bodytype = new_body; + + if(needs_spawn_packet) { + EQApplicationPacket* app = new EQApplicationPacket; + CreateDespawnPacket(app, true); + entity_list.QueueClients(this, app); + CreateSpawnPacket(app, this); + entity_list.QueueClients(this, app); + safe_delete(app); + } +} + + +void Mob::ModSkillDmgTaken(SkillType skill_num, int value) +{ + if (skill_num <= HIGHEST_SKILL) + SkillDmgTaken_Mod[skill_num] = value; + + + else if (skill_num == 255) + SkillDmgTaken_Mod[HIGHEST_SKILL+1] = value; +} + +int16 Mob::GetModSkillDmgTaken(const SkillType skill_num) +{ + if (skill_num <= HIGHEST_SKILL) + return SkillDmgTaken_Mod[skill_num]; + + else if (skill_num == 255) + return SkillDmgTaken_Mod[HIGHEST_SKILL+1]; + + return 0; +} + +void Mob::ModVulnerability(uint8 resist, int16 value) +{ + if (resist < HIGHEST_RESIST+1) + Vulnerability_Mod[resist] = value; + + else if (resist == 255) + Vulnerability_Mod[HIGHEST_RESIST+1] = value; +} + +int16 Mob::GetModVulnerability(const uint8 resist) +{ + if (resist < HIGHEST_RESIST+1) + return Vulnerability_Mod[resist]; + + else if (resist == 255) + return Vulnerability_Mod[HIGHEST_RESIST+1]; + + return 0; +} + +void Mob::CastOnCurer(uint32 spell_id) +{ + for(int i = 0; i < EFFECT_COUNT; i++) + { + if (spells[spell_id].effectid[i] == SE_CastOnCurer) + { + if(IsValidSpell(spells[spell_id].base[i])) + { + SpellFinished(spells[spell_id].base[i], this); + } + } + } +} + +void Mob::CastOnCure(uint32 spell_id) +{ + for(int i = 0; i < EFFECT_COUNT; i++) + { + if (spells[spell_id].effectid[i] == SE_CastOnCure) + { + if(IsValidSpell(spells[spell_id].base[i])) + { + SpellFinished(spells[spell_id].base[i], this); + } + } + } +} + +void Mob::CastOnNumHitFade(uint32 spell_id) +{ + if(!IsValidSpell(spell_id)) + return; + + uint32 buff_max = GetMaxTotalSlots(); + + for(int i = 0; i < EFFECT_COUNT; i++) + { + if (spells[spell_id].effectid[i] == SE_CastonNumHitFade) + { + if(IsValidSpell(spells[spell_id].base[i])) + { + SpellFinished(spells[spell_id].base[i], this); + } + } + } +} + +int Mob::SlowMitigation(bool slow_msg, Mob *caster, int slow_value) +{ + float int_slow_mitigation = slow_mitigation * 100.0f; + + if (int_slow_mitigation > 100.0f) + return 0; + + if (slow_msg) + { + if (caster && caster->IsClient()) + { + if ((int_slow_mitigation > 0.0f) && (int_slow_mitigation < 26.0f)) + caster->Message(262, "Your spell was mostly successful"); + + else if ((int_slow_mitigation > 26.0f) && (int_slow_mitigation < 74.0f)) + caster->Message(262, "Your spell was partially successful"); + + else if ((int_slow_mitigation > 74.0f) && (int_slow_mitigation < 101.0f)) + caster->Message(262, "Your spell was slightly successful"); + } + return 0; + } + + else + { + slow_value -= (slow_value * static_cast(int_slow_mitigation) / 100); + return slow_value; + } +} + +uint16 Mob::GetSkillByItemType(int ItemType) +{ + switch (ItemType) + { + case ItemType1HS: + return _1H_SLASHING; + case ItemType2HS: + return _2H_SLASHING; + case ItemTypePierce: + return PIERCING; + case ItemType1HB: + return _1H_BLUNT; + case ItemType2HB: + return _2H_BLUNT; + case ItemType2HPierce: + return PIERCING; + case ItemTypeHand2Hand: + return HAND_TO_HAND; + default: + return HAND_TO_HAND; + } + return HAND_TO_HAND; + } + + +bool Mob::PassLimitToSkill(uint16 spell_id, uint16 skill) { + + if (!IsValidSpell(spell_id)) + return false; + + if (!IsEffectInSpell(spell_id, SE_LimitToSkill)) + return false; + + for (int i = 0; i < EFFECT_COUNT; i++) { + if (spells[spell_id].effectid[i] == SE_LimitToSkill){ + if (spells[spell_id].base[i] == skill){ + return true; + } + } + } + return false; +} diff --git a/zone/mob.h b/zone/mob.h new file mode 100644 index 000000000..11c3b1ae9 --- /dev/null +++ b/zone/mob.h @@ -0,0 +1,1120 @@ +/* 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 MOB_H +#define MOB_H + +#include "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); + +class EGNode; +class MobFearState; +class Mob : public Entity { +public: + enum CLIENT_CONN_STATUS { CLIENT_CONNECTING, CLIENT_CONNECTED, CLIENT_LINKDEAD, + CLIENT_KICKED, DISCONNECTED, CLIENT_ERROR, CLIENT_CONNECTINGALL }; + enum eStandingPetOrder { SPO_Follow, SPO_Sit, SPO_Guard }; + + Mob(const char* in_name, + const char* in_lastname, + int32 in_cur_hp, + int32 in_max_hp, + uint8 in_gender, + uint16 in_race, + uint8 in_class, + bodyType in_bodytype, + uint8 in_deity, + uint8 in_level, + uint32 in_npctype_id, + float in_size, + float in_runspeed, + float in_heading, + float in_x_pos, + float in_y_pos, + float in_z_pos, + uint8 in_light, + uint8 in_texture, + uint8 in_helmtexture, + uint16 in_ac, + uint16 in_atk, + uint16 in_str, + uint16 in_sta, + uint16 in_dex, + uint16 in_agi, + uint16 in_int, + uint16 in_wis, + uint16 in_cha, + uint8 in_haircolor, + uint8 in_beardcolor, + uint8 in_eyecolor1, // the eyecolors always seem to be the same, maybe left and right eye? + uint8 in_eyecolor2, + uint8 in_hairstyle, + uint8 in_luclinface, + uint8 in_beard, + uint32 in_drakkin_heritage, + uint32 in_drakkin_tattoo, + uint32 in_drakkin_details, + uint32 in_armor_tint[MAX_MATERIALS], + uint8 in_aa_title, + uint8 in_see_invis, // see through invis + uint8 in_see_invis_undead, // see through invis vs. undead + uint8 in_see_hide, + uint8 in_see_improved_hide, + int32 in_hp_regen, + int32 in_mana_regen, + uint8 in_qglobal, + uint8 in_maxlevel, + uint32 in_scalerate + ); + virtual ~Mob(); + + inline virtual bool IsMob() const { return true; } + inline virtual bool InZone() const { return true; } + + //Somewhat sorted: needs documenting! + + //Attack + virtual void RogueBackstab(Mob* other, bool min_damage = false, int ReuseTime = 10); + virtual void RogueAssassinate(Mob* other); // solar + bool BehindMob(Mob* other = 0, float playerx = 0.0f, float playery = 0.0f) const; + virtual void RangedAttack(Mob* other) { } + virtual void ThrowingAttack(Mob* other) { } + uint16 GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg); + // 13 = Primary (default), 14 = secondary + virtual bool Attack(Mob* other, int Hand = 13, bool FromRiposte = false, bool IsStrikethrough = false, + bool IsFromSpell = false) = 0; + int MonkSpecialAttack(Mob* other, uint8 skill_used); + virtual void TryBackstab(Mob *other,int ReuseTime = 10); + void TriggerDefensiveProcs(const ItemInst* weapon, Mob *on, uint16 hand = 13, int damage = 0); + virtual bool AvoidDamage(Mob* attacker, int32 &damage, bool CanRiposte = true); + virtual bool CheckHitChance(Mob* attacker, SkillType skillinuse, int Hand, int16 chance_mod = 0); + virtual void TryCriticalHit(Mob *defender, uint16 skill, int32 &damage); + void TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage); + virtual bool TryFinishingBlow(Mob *defender, SkillType skillinuse); + virtual bool TryHeadShot(Mob* defender, SkillType skillInUse); + virtual void DoRiposte(Mob* defender); + void ApplyMeleeDamageBonus(uint16 skill, int32 &damage); + virtual void MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit); + bool CombatRange(Mob* other); + + //Appearance + void SendLevelAppearance(); + void SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5, + Client *specific_target=NULL); + void SendTargetable(bool on, Client *specific_target = NULL); + virtual void SendWearChange(uint8 material_slot); + virtual void SendTextureWC(uint8 slot, uint16 texture, uint32 hero_forge_model = 0, uint32 elite_material = 0, + uint32 unknown06 = 0, uint32 unknown18 = 0); + virtual void SetSlotTint(uint8 material_slot, uint8 red_tint, uint8 green_tint, uint8 blue_tint); + virtual void WearChange(uint8 material_slot, uint16 texture, uint32 color); + void DoAnim(const int animnum, int type=0, bool ackreq = true, eqFilterType filter = FilterNone); + void ProjectileAnimation(Mob* to, uint16 item_id, bool IsArrow = false, float speed = 0, + float angle = 0, float tilt = 0, float arc = 0); + void ChangeSize(float in_size, bool bNoRestriction = false); + inline uint8 SeeInvisible() const { return see_invis; } + inline bool SeeInvisibleUndead() const { return see_invis_undead; } + inline bool SeeHide() const { return see_hide; } + inline bool SeeImprovedHide() const { return see_improved_hide; } + bool IsInvisible(Mob* other = 0) const; + void SetInvisible(uint8 state); + bool AttackAnimation(SkillType &skillinuse, int Hand, const ItemInst* weapon); + + //Song + bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1); + bool ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, uint16 slot); + void BardPulse(uint16 spell_id, Mob *caster); + + //Spell + void SendSpellEffect(uint32 effectid, uint32 duration, uint32 finish_delay, bool zone_wide, + uint32 unk020, bool perm_effect = false, Client *c = NULL); + bool IsBeneficialAllowed(Mob *target); + virtual int GetCasterLevel(uint16 spell_id); + void ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterID = 0, + bool item_bonus = false, uint32 ticsremaining = 0, int buffslot = -1); + void NegateSpellsBonuses(uint16 spell_id); + virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false) { return range;} + virtual int32 GetActSpellDamage(uint16 spell_id, int32 value) { return value; } + virtual int32 GetActSpellHealing(uint16 spell_id, int32 value) { return value; } + virtual int32 GetActSpellCost(uint16 spell_id, int32 cost){ return cost;} + virtual int32 GetActSpellDuration(uint16 spell_id, int32 duration){ return duration;} + virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime); + float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false, + int resist_override = 0, bool CharismaCheck = false); + uint16 GetSpecializeSkillValue(uint16 spell_id) const; + void SendSpellBarDisable(); + void SendSpellBarEnable(uint16 spellid); + void ZeroCastingVars(); + virtual void SpellProcess(); + 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, + uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, uint32 type = 0, int16 *resist_adjust = NULL); + 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, + uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, uint32 type = 0, int16 resist_adjust = 0); + void CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, uint16 mana_used, + uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0); + bool SpellFinished(uint16 spell_id, Mob *target, uint16 slot = 10, uint16 mana_used = 0, + uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false); + virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect = false, + bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false); + virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100); + virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, + CastAction_type &CastAction); + virtual bool CheckFizzle(uint16 spell_id); + virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster); + virtual float GetAOERange(uint16 spell_id); + void InterruptSpell(uint16 spellid = SPELL_UNKNOWN); + void InterruptSpell(uint16, uint16, uint16 spellid = SPELL_UNKNOWN); + inline bool IsCasting() const { return((casting_spell_id != 0)); } + uint16 CastingSpellID() const { return casting_spell_id; } + + //Buff + void BuffProcess(); + virtual void DoBuffTic(uint16 spell_id, uint32 ticsremaining, uint8 caster_level, Mob* caster = 0); + void BuffFadeBySpellID(uint16 spell_id); + void BuffFadeByEffect(int effectid, int skipslot = -1); + void BuffFadeAll(); + void BuffFadeDetrimental(); + void BuffFadeBySlot(int slot, bool iRecalcBonuses = true); + void BuffFadeDetrimentalByCaster(Mob *caster); + void BuffFadeBySitModifier(); + void BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration); + int AddBuff(Mob *caster, const uint16 spell_id, int duration = 0, int32 level_override = -1); + int CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite = false); + int CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caster_level_override = -1); + void SendPetBuffsToClient(); + virtual int GetCurrentBuffSlots() const { return 0; } + virtual int GetCurrentSongSlots() const { return 0; } + virtual int GetCurrentDiscSlots() const { return 0; } + virtual int GetMaxBuffSlots() const { return 0; } + virtual int GetMaxSongSlots() const { return 0; } + virtual int GetMaxDiscSlots() const { return 0; } + virtual int GetMaxTotalSlots() const { return 0; } + virtual void InitializeBuffSlots() { buffs = NULL; current_buff_count = 0; } + virtual void UninitializeBuffSlots() { } + inline bool HasRune() const { return m_hasRune; } + inline bool HasSpellRune() const { return m_hasSpellRune; } + inline bool HasPartialMeleeRune() const { return m_hasPartialMeleeRune; } + inline bool HasPartialSpellRune() const { return m_hasPartialSpellRune; } + inline void SetHasRune(bool hasRune) { m_hasRune = hasRune; } + inline void SetHasSpellRune(bool hasSpellRune) { m_hasSpellRune = hasSpellRune; } + inline void SetHasPartialMeleeRune(bool hasPartialMeleeRune) { m_hasPartialMeleeRune = hasPartialMeleeRune; } + inline void SetHasPartialSpellRune(bool hasPartialSpellRune) { m_hasPartialSpellRune = hasPartialSpellRune; } + EQApplicationPacket *MakeBuffsPacket(bool for_target = true); + void SendBuffsToClient(Client *c); + inline Buffs_Struct* GetBuffs() { return buffs; } + void DoGravityEffect(); + void DamageShield(Mob* other, bool spell_ds = false); + int32 RuneAbsorb(int32 damage, uint16 type); + bool FindBuff(uint16 spellid); + bool FindType(uint16 type, bool bOffensive = false, uint16 threshold = 100); + int16 GetBuffSlotFromType(uint16 type); + uint16 GetSpellIDFromSlot(uint8 slot); + int CountDispellableBuffs(); + bool HasBuffIcon(Mob* caster, Mob* target, uint16 spell_id); + bool CheckHitsRemaining(uint32 buff_slot, bool when_spell_done=false, bool negate=false,uint16 type=0, + uint16 spell_id=0, bool use_skill=false,uint16 skill=0); + void SpreadVirus(uint16 spell_id, uint16 casterID); + bool IsNimbusEffectActive(uint32 nimbus_effect); + void SetNimbusEffect(uint32 nimbus_effect); + inline virtual uint32 GetNimbusEffect1() const { return nimbus_effect1; } + inline virtual uint32 GetNimbusEffect2() const { return nimbus_effect2; } + inline virtual uint32 GetNimbusEffect3() const { return nimbus_effect3; } + void RemoveNimbusEffect(int effectid); + + //Basic Stats/Inventory + virtual void SetLevel(uint8 in_level, bool command = false) { level = in_level; } + void TempName(const char *newname = NULL); + void SetTargetable(bool on); + bool IsTargetable() const { return m_targetable; } + virtual uint16 GetSkill(SkillType skill_num) const { return 0; } + virtual uint32 GetEquipment(uint8 material_slot) const { return(0); } + virtual int32 GetEquipmentMaterial(uint8 material_slot) const; + virtual uint32 GetEquipmentColor(uint8 material_slot) const; + virtual uint32 IsEliteMaterialItem(uint8 material_slot) const; + bool AffectedBySpellExcludingSlot(int slot, int effect); + virtual void Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillType attack_skill) = 0; + virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillType attack_skill, + bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false) = 0; + inline virtual void SetHP(int32 hp) { if (hp >= max_hp) cur_hp = max_hp; else cur_hp = hp;} + bool ChangeHP(Mob* other, int32 amount, uint16 spell_id = 0, int8 buffslot = -1, bool iBuffTic = false); + inline void SetOOCRegen(int32 newoocregen) {oocregen = newoocregen;} + virtual void Heal(); + virtual void HealDamage(uint32 ammount, Mob* caster = NULL); + virtual void SetMaxHP() { cur_hp = max_hp; } + virtual inline uint16 GetBaseRace() const { return base_race; } + virtual inline uint8 GetBaseGender() const { return base_gender; } + virtual inline uint16 GetDeity() const { return deity; } + inline uint16 GetRace() const { return race; } + inline uint8 GetGender() const { return gender; } + inline uint8 GetTexture() const { return texture; } + inline uint8 GetHelmTexture() const { return helmtexture; } + inline uint8 GetHairColor() const { return haircolor; } + inline uint8 GetBeardColor() const { return beardcolor; } + inline uint8 GetEyeColor1() const { return eyecolor1; } + inline uint8 GetEyeColor2() const { return eyecolor2; } + inline uint8 GetHairStyle() const { return hairstyle; } + inline uint8 GetLuclinFace() const { return luclinface; } + inline uint8 GetBeard() const { return beard; } + inline uint8 GetDrakkinHeritage() const { return drakkin_heritage; } + inline uint8 GetDrakkinTattoo() const { return drakkin_tattoo; } + inline uint8 GetDrakkinDetails() const { return drakkin_details; } + inline uint32 GetArmorTint(uint8 i) const { return armor_tint[(i < MAX_MATERIALS) ? i : 0]; } + inline uint8 GetClass() const { return class_; } + inline uint8 GetLevel() const { return level; } + inline const char* GetName() const { return name; } + inline const char* GetOrigName() const { return orig_name; } + inline const char* GetLastName() const { return lastname; } + const char *GetCleanName(); + virtual void SetName(const char *new_name = NULL) { new_name ? strn0cpy(name, new_name, 64) : + strn0cpy(name, GetName(), 64); return; }; + 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 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 int32 GetHP() const { return cur_hp; } + inline int32 GetMaxHP() const { return max_hp; } + virtual int32 CalcMaxHP(); + inline int32 GetMaxMana() const { return max_mana; } + inline int32 GetMana() const { return cur_mana; } + int32 GetItemHPBonuses(); + int32 GetSpellHPBonuses(); + virtual const int32& SetMana(int32 amount); + inline float GetManaRatio() const { return max_mana == 0 ? 100 : + ((static_cast(cur_mana) / max_mana) * 100); } + virtual int32 CalcMaxMana(); + uint32 GetNPCTypeID() const { return npctype_id; } + inline const float GetX() const { return x_pos; } + inline const float GetY() const { return y_pos; } + inline const float GetZ() const { return z_pos; } + inline const float GetHeading() const { return heading; } + inline const float GetSize() const { return size; } + inline const float GetBaseSize() const { return base_size; } + inline const float GetTarX() const { return tarx; } + inline const float GetTarY() const { return tary; } + inline const float GetTarZ() const { return tarz; } + inline const float GetTarVX() const { return tar_vx; } + inline const float GetTarVY() const { return tar_vy; } + inline const float GetTarVZ() const { return tar_vz; } + inline const float GetTarVector() const { return tar_vector; } + inline const uint8 GetTarNDX() const { return tar_ndx; } + bool IsBoat() const; + + //Group + virtual bool HasRaid() = 0; + virtual bool HasGroup() = 0; + virtual Raid* GetRaid() = 0; + virtual Group* GetGroup() = 0; + + //Faction + virtual inline int32 GetPrimaryFaction() const { return 0; } + + //Movement + void Warp( float x, float y, float z ); + inline bool IsMoving() const { return moving; } + virtual void SetMoving(bool move) { moving = move; delta_x = 0; delta_y = 0; delta_z = 0; delta_heading = 0; } + virtual void GoToBind(uint8 bindnum = 0) { } + virtual void Gate(); + float GetWalkspeed() const { return(_GetMovementSpeed(-47)); } + float GetRunspeed() const { return(_GetMovementSpeed(0)); } + float GetBaseRunspeed() const { return runspeed; } + float GetMovespeed() const { return IsRunning() ? GetRunspeed() : GetWalkspeed(); } + bool IsRunning() const { return m_is_running; } + void SetRunning(bool val) { m_is_running = val; } + virtual void GMMove(float x, float y, float z, float heading = 0.01, bool SendUpdate = true); + void SetDeltas(float delta_x, float delta_y, float delta_z, float delta_h); + void SetTargetDestSteps(uint8 target_steps) { tar_ndx = target_steps; } + void SendPosUpdate(uint8 iSendToSelf = 0); + void MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct* spu); + void MakeSpawnUpdate(PlayerPositionUpdateServer_Struct* spu); + void SendPosition(); + void SetFlyMode(uint8 flymode); + inline void Teleport(VERTEX NewPosition) { x_pos = NewPosition.x; y_pos = NewPosition.y; + z_pos = NewPosition.z; }; + + //AI + static uint32 GetLevelCon(uint8 mylevel, uint8 iOtherLevel); + inline uint32 GetLevelCon(uint8 iOtherLevel) const { + return this ? GetLevelCon(GetLevel(), iOtherLevel) : CON_GREEN; } + virtual void AddToHateList(Mob* other, int32 hate = 0, int32 damage = 0, bool iYellForHelp = true, + bool bFrenzy = false, bool iBuffTic = false); + bool RemoveFromHateList(Mob* mob); + void SetHate(Mob* other, int32 hate = 0, int32 damage = 0) { hate_list.Set(other,hate,damage);} + uint32 GetHateAmount(Mob* tmob, bool is_dam = false) { return hate_list.GetEntHate(tmob,is_dam);} + uint32 GetDamageAmount(Mob* tmob) { return hate_list.GetEntHate(tmob, true);} + Mob* GetHateTop() { return hate_list.GetTop(this);} + Mob* GetHateDamageTop(Mob* other) { return hate_list.GetDamageTop(other);} + Mob* GetHateRandom() { return hate_list.GetRandom();} + Mob* GetHateMost() { return hate_list.GetMostHate();} + bool IsEngaged() { return(!hate_list.IsEmpty()); } + bool HateSummon(); + void FaceTarget(Mob* MobToFace = 0); + void SetHeading(float iHeading) { if(heading != iHeading) { pLastChange = Timer::GetCurrentTime(); + heading = iHeading; } } + void WipeHateList(); + void AddFeignMemory(Client* attacker); + void RemoveFromFeignMemory(Client* attacker); + void ClearFeignMemory(); + void PrintHateListToClient(Client *who) { hate_list.PrintToClient(who); } + void GetHateList(std::list &h_list) { return hate_list.GetHateList(h_list); } + bool CheckLos(Mob* other); + bool CheckLosFN(Mob* other); + bool CheckLosFN(float posX, float posY, float posZ, float mobSize); + inline void SetChanged() { pLastChange = Timer::GetCurrentTime(); } + inline const uint32 LastChange() const { return pLastChange; } + + //Quest + void QuestReward(Client *c = NULL, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0); + void CameraEffect(uint32 duration, uint32 intensity, Client *c = NULL, bool global = false); + inline bool GetQglobal() const { return qglobal; } + + //Other Packet + void CreateDespawnPacket(EQApplicationPacket* app, bool Decay); + void CreateHorseSpawnPacket(EQApplicationPacket* app, const char* ownername, uint16 ownerid, Mob* ForWho = 0); + void CreateSpawnPacket(EQApplicationPacket* app, Mob* ForWho = 0); + virtual void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); + void CreateHPPacket(EQApplicationPacket* app); + void SendHPUpdate(); + + //Util + static uint32 RandomTimer(int min, int max); + static uint8 GetDefaultGender(uint16 in_race, uint8 in_gender = 0xFF); + static void CreateSpawnPacket(EQApplicationPacket* app, NewSpawn_Struct* ns); + uint16 GetSkillByItemType(int ItemType); + virtual void MakePet(uint16 spell_id, const char* pettype, const char *petname = NULL); + virtual void MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, const char *petname = NULL); + bool IsWarriorClass() const; + char GetCasterClass() const; + uint8 GetArchetype() const; + void SetZone(uint32 zone_id, uint32 instance_id); + void ShowStats(Client* client); + void ShowBuffs(Client* client); + void ShowBuffList(Client* client); + float Dist(const Mob &) const; + float DistNoZ(const Mob &) const; + float DistNoRoot(const Mob &) const; + float DistNoRoot(float x, float y, float z) const; + float DistNoRootNoZ(float x, float y) const; + float DistNoRootNoZ(const Mob &) const; + static float GetReciprocalHeading(Mob* target); + bool PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, float &z_dest, + bool lookForAftArc = true); + + //Procs + bool AddRangedProc(uint16 spell_id, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN); + bool RemoveRangedProc(uint16 spell_id, bool bAll = false); + bool HasRangedProcs() const; + bool AddDefensiveProc(uint16 spell_id, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN); + bool RemoveDefensiveProc(uint16 spell_id, bool bAll = false); + bool HasDefensiveProcs() const; + bool AddSkillProc(uint16 spell_id, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN); + bool RemoveSkillProc(uint16 spell_id, bool bAll = false); + bool HasSkillProcs() const; + bool AddProcToWeapon(uint16 spell_id, bool bPerma = false, uint16 iChance = 3); + bool RemoveProcFromWeapon(uint16 spell_id, bool bAll = false); + bool HasProcs() const; + + //Logging + bool IsLoggingEnabled() const { return(logging_enabled); } + void EnableLogging() { logging_enabled = true; } + void DisableLogging() { logging_enabled = false; } + + + //More stuff to sort: + 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;} + void SetFollowID(uint32 id) { follow = id; } + void SetFollowDistance(uint32 dist) { follow_dist = dist; } + uint32 GetFollowID() const { return follow; } + uint32 GetFollowDistance() const { return follow_dist; } + + virtual void Message(uint32 type, const char* message, ...) { } + virtual void Message_StringID(uint32 type, uint32 string_id, uint32 distance = 0) { } + virtual 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) { } + void Say(const char *format, ...); + void Say_StringID(uint32 string_id, 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); + void Shout(const char *format, ...); + void Emote(const char *format, ...); + void QuestJournalledSay(Client *QuestInitiator, const char *str); + uint32 GetItemStat(uint32 itemid, const char *identifier); + + + int16 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false); + uint8 IsFocusEffect(uint16 spellid, int effect_index, bool AA=false,uint32 aa_effect=0); + void SendIllusionPacket(uint16 in_race, uint8 in_gender = 0xFF, uint8 in_texture = 0xFF, uint8 in_helmtexture = 0xFF, 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); + 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 = NULL, uint32 duration_override = 0); + void TypesTemporaryPets(uint32 typesid, Mob *target, const char *name_override = NULL, uint32 duration_override = 0, bool followme = false); + void WakeTheDead(uint16 spell_id, Mob *target, uint32 duration); + void Spin(); + void Kill(); + bool PassCharismaCheck(Mob* caster, Mob* spellTarget, uint16 spell_id); + bool TryDeathSave(); + bool TryDivineSave(); + 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); + void TryTwincast(Mob *caster, Mob *target, uint32 spell_id); + void TrySympatheticProc(Mob *target, uint32 spell_id); + bool TryFadeEffect(int slot); + uint16 GetSpellEffectResistChance(uint16 spell_id); + int16 GetHealRate(uint16 spell_id); + int16 GetCriticalHealRate(uint16 spell_id); + int32 GetVulnerability(int32 damage, Mob *caster, uint32 spell_id, uint32 ticsremaining); + int32 GetAdditionalDamage(Mob *caster, uint32 spell_id, bool use_skill = false, uint16 skill=0); + int16 GetSkillDmgTaken(const SkillType skill_used); + void DoKnockback(Mob *caster, uint32 pushback, uint32 pushup); + int16 CalcResistChanceBonus(); + int16 CalcFearResistChance(); + void TrySpellOnKill(uint8 level, uint16 spell_id); + bool TrySpellOnDeath(); + void CastOnCurer(uint32 spell_id); + void CastOnCure(uint32 spell_id); + void CastOnNumHitFade(uint32 spell_id); + int SlowMitigation(bool slow_msg=false, Mob *caster = NULL,int slow_value = 0); + int16 GetCritDmgMob(uint16 skill); + int16 GetMeleeDamageMod_SE(uint16 skill); + int16 GetMeleeMinDamageMod_SE(uint16 skill); + int16 GetCrippBlowChance(); + int16 GetSkillReuseTime(uint16 skill); + int16 GetCriticalChanceBonus(uint16 skill); + int16 GetSkillDmgAmt(uint16 skill); + bool TryReflectSpell(uint32 spell_id); + bool CanBlockSpell() const { return(spellbonuses.BlockNextSpell); } + bool DoHPToManaCovert(uint16 mana_cost = 0); + int32 ApplySpellEffectiveness(Mob* caster, int16 spell_id, int32 value, bool IsBard = false); + + void ModSkillDmgTaken(SkillType skill_num, int value); + int16 GetModSkillDmgTaken(const SkillType skill_num); + void ModVulnerability(uint8 resist, int16 value); + int16 GetModVulnerability(const uint8 resist); + + void SetAllowBeneficial(bool value) { m_AllowBeneficial = value; } + bool GetAllowBeneficial() { return m_AllowBeneficial; } + void SetDisableMelee(bool value) { m_DisableMelee = value; } + bool IsMeleeDisabled() { return m_DisableMelee; } + + bool IsOffHandAtk() const { return offhand; } + inline void OffHandAtk(bool val) { offhand = val; } + + inline void SetFlurryChance(uint8 value) { NPC_FlurryChance = value;} + uint8 GetFlurryChance() { return NPC_FlurryChance; } + + static uint32 GetAppearanceValue(EmuAppearance iAppearance); + void SendAppearancePacket(uint32 type, uint32 value, bool WholeZone = true, bool iIgnoreSelf = false, Client *specific_target=NULL); + void SetAppearance(EmuAppearance app, bool iIgnoreSelf = true); + inline EmuAppearance GetAppearance() const { return _appearance; } + inline const uint8 GetRunAnimSpeed() const { return pRunAnimSpeed; } + inline void SetRunAnimSpeed(int8 in) { if (pRunAnimSpeed != in) { pRunAnimSpeed = in; pLastChange = Timer::GetCurrentTime(); } } + bool IsDestructibleObject() { return destructibleobject; } + void SetDestructibleObject(bool in) { destructibleobject = in; } + + Mob* GetPet(); + void SetPet(Mob* newpet); + virtual Mob* GetOwner(); + virtual Mob* GetOwnerOrSelf(); + Mob* GetUltimateOwner(); + void SetPetID(uint16 NewPetID); + inline uint16 GetPetID() const { return petid; } + inline PetType GetPetType() const { return typeofpet; } + void SetPetType(PetType p) { typeofpet = p; } + inline int16 GetPetPower() const { return (petpower < 0) ? 0 : petpower; } + void SetPetPower(int16 p) { if (p < 0) petpower = 0; else petpower = p; } + bool IsFamiliar() const { return(typeofpet == petFamiliar); } + bool IsAnimation() const { return(typeofpet == petAnimation); } + bool IsCharmed() const { return(typeofpet == petCharmed); } + void SetOwnerID(uint16 NewOwnerID); + inline uint16 GetOwnerID() const { return ownerid; } + 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; } + bool HasPetAffinity() { if (aabonuses.GivePetGroupTarget || itembonuses.GivePetGroupTarget || spellbonuses.GivePetGroupTarget) return true; return false; } + + inline const bodyType GetBodyType() const { return bodytype; } + inline const bodyType GetOrigBodyType() const { return orig_bodytype; } + void SetBodyType(bodyType new_body, bool overwrite_orig); + + uint8 invisible, see_invis; + bool invulnerable, invisible_undead, invisible_animals, sneaking, hidden, improved_hidden; + bool see_invis_undead, see_hide, see_improved_hide; + bool qglobal; + + virtual void SetAttackTimer(); + inline void SetInvul(bool invul) { invulnerable=invul; } + inline bool GetInvul(void) { return invulnerable; } + inline void SetExtraHaste(int Haste) { ExtraHaste = Haste; } + virtual int GetHaste(); + + uint8 GetWeaponDamageBonus(const Item_Struct* Weapon); + uint16 GetDamageTable(SkillType skillinuse); + virtual int GetMonkHandToHandDamage(void); + + bool CanThisClassDoubleAttack(void) const; + bool CanThisClassDualWield(void) const; + bool CanThisClassRiposte(void) const; + bool CanThisClassDodge(void) const; + bool CanThisClassParry(void) const; + bool CanThisClassBlock(void) const; + + int GetMonkHandToHandDelay(void); + uint16 GetClassLevelFactor(); + void Mesmerize(); + inline bool IsMezzed() const { return mezzed; } + inline bool IsStunned() const { return stunned; } + inline bool IsSilenced() const { return silenced; } + inline bool IsAmnesiad() const { return amnesiad; } + inline uint16 GetErrorNumber() const {return adverrorinfo;} + + int32 ReduceDamage(int32 damage); + int32 AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTic, Mob* attacker); + + virtual void DoSpecialAttackDamage(Mob *who, SkillType skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance=false); + virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=NULL, const Item_Struct* item=NULL, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0); + virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillType skillinuse, int16 chance_mod=0, int16 focus=0, bool CanRiposte=false); + virtual void DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon=NULL, const ItemInst* Ammo=NULL, uint16 weapon_damage=0, int16 chance_mod=0, int16 focus=0); + bool CanDoSpecialAttack(Mob *other); + bool Flurry(); + bool Rampage(); + bool AddRampage(Mob*); + void ClearRampage(); + void AreaRampage(); + + void StartEnrage(); + void ProcessEnrage(); + bool IsEnraged(); + void Taunt(NPC* who, bool always_succeed, float chance_bonus = 0); + + virtual void AI_Init(); + virtual void AI_Start(uint32 iMoveDelay = 0); + virtual void AI_Stop(); + virtual void AI_Process(); + + const char* GetEntityVariable(const char *id); + void SetEntityVariable(const char *id, const char *m_var); + bool EntityVariableExists(const char *id); + + void AI_Event_Engaged(Mob* attacker, bool iYellForHelp = true); + void AI_Event_NoLongerEngaged(); + + FACTION_VALUE GetSpecialFactionCon(Mob* iOther); + inline const bool IsAIControlled() const { return pAIControlled; } + inline const float GetAggroRange() const { return (spellbonuses.AggroRange == -1) ? pAggroRange : spellbonuses.AggroRange; } + inline const float GetAssistRange() const { return (spellbonuses.AssistRange == -1) ? pAssistRange : spellbonuses.AssistRange; } + + + inline void SetPetOrder(eStandingPetOrder i) { pStandingPetOrder = i; } + inline const eStandingPetOrder GetPetOrder() const { return pStandingPetOrder; } + inline void SetHeld(bool nState) { held = nState; } + inline const bool IsHeld() const { return held; } + inline void SetNoCast(bool nState) { nocast = nState; } + inline const bool IsNoCast() const { return nocast; } + inline void SetFocused(bool nState) { focused = nState; } + inline const bool IsFocused() const { return focused; } + inline const bool IsRoamer() const { return roamer; } + inline const bool IsRooted() const { return rooted || permarooted; } + inline const bool HasVirus() const { return has_virus; } + int GetSnaredAmount(); + + + int GetCurWp() { return cur_wp; } + + //old fear function + //void SetFeared(Mob *caster, uint32 duration, bool flee = false); + float GetFearSpeed(); + bool IsFeared() { return curfp; } // This returns true if the mob is feared or fleeing due to low HP + //old fear: inline void StartFleeing() { SetFeared(GetHateTop(), FLEE_RUN_DURATION, true); } + inline void StartFleeing() { flee_mode = true; CalculateNewFearpoint(); } + void ProcessFlee(); + void CheckFlee(); + + inline bool CheckAggro(Mob* other) {return hate_list.IsOnHateList(other);} + float CalculateHeadingToTarget(float in_x, float in_y); + bool CalculateNewPosition(float x, float y, float z, float speed, bool checkZ = false); + virtual bool CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ = true); + float CalculateDistance(float x, float y, float z); + float GetGroundZ(float new_x, float new_y, float z_offset=0.0); + void SendTo(float new_x, float new_y, float new_z); + void SendToFixZ(float new_x, float new_y, float new_z); + void NPCSpecialAttacks(const char* parse, int permtag, bool reset = true, bool remove = false); + inline uint32 DontHealMeBefore() const { return pDontHealMeBefore; } + inline uint32 DontBuffMeBefore() const { return pDontBuffMeBefore; } + inline uint32 DontDotMeBefore() const { return pDontDotMeBefore; } + inline uint32 DontRootMeBefore() const { return pDontRootMeBefore; } + inline uint32 DontSnareMeBefore() const { return pDontSnareMeBefore; } + inline uint32 DontCureMeBefore() const { return pDontCureMeBefore; } + void SetDontRootMeBefore(uint32 time) { pDontRootMeBefore = time; } + void SetDontHealMeBefore(uint32 time) { pDontHealMeBefore = time; } + void SetDontBuffMeBefore(uint32 time) { pDontBuffMeBefore = time; } + void SetDontDotMeBefore(uint32 time) { pDontDotMeBefore = time; } + void SetDontSnareMeBefore(uint32 time) { pDontSnareMeBefore = time; } + void SetDontCureMeBefore(uint32 time) { pDontCureMeBefore = time; } + + // calculate interruption of spell via movement of mob + void SaveSpellLoc() {spell_x = x_pos; spell_y = y_pos; spell_z = z_pos; } + inline float GetSpellX() const {return spell_x;} + inline float GetSpellY() const {return spell_y;} + inline float GetSpellZ() const {return spell_z;} + inline bool IsGrouped() const { return isgrouped; } + 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; } + + bool CheckWillAggro(Mob *mob); + + void InstillDoubt(Mob *who); + int16 GetResist(uint8 type) const; + Mob* GetShieldTarget() const { return shield_target; } + void SetShieldTarget(Mob* mob) { shield_target = mob; } + bool HasActiveSong() const { return(bardsong != 0); } + bool Charmed() const { return charmed; } + static uint32 GetLevelHP(uint8 tlevel); + uint32 GetZoneID() const; //for perl + virtual int32 CheckAggroAmount(uint16 spellid, bool isproc = false); + virtual int32 CheckHealAggroAmount(uint16 spellid, uint32 heal_possible = 0); + virtual uint32 GetAA(uint32 aa_id) const { return(0); } + + uint16 GetInstrumentMod(uint16 spell_id) const; + int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, Mob *caster = NULL, 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 = NULL, Mob* caster2 = NULL); + uint32 GetCastedSpellInvSlot() const { return casting_spell_inventory_slot; } + + // HP Event + inline int GetNextHPEvent() const { return nexthpevent; } + void SetNextHPEvent( int hpevent ); + void SendItemAnimation(Mob *to, const Item_Struct *item, SkillType skillInUse); + inline int& GetNextIncHPEvent() { return nextinchpevent; } + void SetNextIncHPEvent( int inchpevent ); + + bool DivineAura() const; + bool SpecAttacks[SPECATK_MAXNUM]; + bool HasNPCSpecialAtk(const char* parse); + Shielders_Struct shielder[MAX_SHIELDERS]; + Trade* trade; + + + inline float GetCWPX() const { return(cur_wp_x); } + inline float GetCWPY() const { return(cur_wp_y); } + inline float GetCWPZ() const { return(cur_wp_z); } + inline float GetCWPH() const { return(cur_wp_heading); } + inline float GetCWPP() const { return(static_cast(cur_wp_pause)); } + inline int GetCWP() const { return(cur_wp); } + void SetCurrentWP(uint16 waypoint) { cur_wp = waypoint; } + virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther) { return FACTION_INDIFFERENT; } + + inline bool IsTrackable() const { return(trackable); } + Timer* GetAIThinkTimer() { return AIthink_timer; } + Timer* GetAIMovementTimer() { return AImovement_timer; } + Timer GetAttackTimer() { return attack_timer; } + Timer GetAttackDWTimer() { return attack_dw_timer; } + inline bool IsFindable() { return findable; } + inline uint8 GetManaPercent() { return (uint8)((float)cur_mana / (float)max_mana * 100.0f); } + virtual uint8 GetEndurancePercent() { return 0; } + + inline virtual bool IsBlockedBuff(int16 SpellID) { return false; } + inline virtual bool IsBlockedPetBuff(int16 SpellID) { return false; } + + void SetGlobal(const char *varname, const char *newvalue, int options, const char *duration, Mob *other = NULL); + void TarGlobal(const char *varname, const char *value, const char *duration, int npcid, int charid, int zoneid); + void DelGlobal(const char *varname); + +protected: + void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillType attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic); + static uint16 GetProcID(uint16 spell_id, uint8 effect_index); + float _GetMovementSpeed(int mod) const; + virtual bool MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, bool checkZ); + + virtual bool AI_EngagedCastCheck() { return(false); } + virtual bool AI_PursueCastCheck() { return(false); } + virtual bool AI_IdleCastCheck() { return(false); } + + + bool IsFullHP; + bool moved; + + std::vector RampageArray; + std::map m_EntityVariables; + + int16 SkillDmgTaken_Mod[HIGHEST_SKILL+2]; + int16 Vulnerability_Mod[HIGHEST_RESIST+2]; + bool m_AllowBeneficial; + bool m_DisableMelee; + uint8 NPC_FlurryChance; + + bool isgrouped; + bool israidgrouped; + bool pendinggroup; + bool islooting; + 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; + bool moving; + int targeted; + bool findable; + bool trackable; + int32 cur_hp; + int32 max_hp; + int32 base_hp; + int32 cur_mana; + int32 max_mana; + int32 hp_regen; + int32 mana_regen; + int32 oocregen; + uint8 maxlevel; + uint32 scalerate; + Buffs_Struct *buffs; + uint32 current_buff_count; + Timer *buff_tic_timer; + StatBonuses itembonuses; + StatBonuses spellbonuses; + StatBonuses aabonuses; + uint16 petid; + uint16 ownerid; + PetType typeofpet; + int16 petpower; + uint32 follow; + uint32 follow_dist; + + uint8 gender; + uint16 race; + uint8 base_gender; + uint16 base_race; + uint8 class_; + bodyType bodytype; + bodyType orig_bodytype; + uint16 deity; + uint8 level; + uint32 npctype_id; + float x_pos; + float y_pos; + float z_pos; + float heading; + uint16 animation; + float base_size; + float size; + float runspeed; + uint32 pLastChange; + bool held; + bool nocast; + bool focused; + void CalcSpellBonuses(StatBonuses* newbon); + virtual void CalcBonuses(); + void TrySkillProc(Mob *on, uint16 skill, float chance); + bool PassLimitToSkill(uint16 spell_id, uint16 skill); + bool PassLimitClass(uint32 Classes_, uint16 Class_); + void TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand = 13, int damage=0); + void TryWeaponProc(const Item_Struct* weapon, Mob *on, uint16 hand = 13); + void TryWeaponProc(const ItemInst* weapon, Mob *on, uint16 hand = 13); + void ExecWeaponProc(uint16 spell_id, Mob *on); + virtual float GetProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13); + virtual float GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed = 30, uint16 hand = 13); + int GetWeaponDamage(Mob *against, const Item_Struct *weapon_item); + int GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate = NULL); + int GetKickDamage(); + int GetBashDamage(); + virtual void ApplySpecialAttackMod(SkillType skill, int32 &dmg, int32 &mindmg); + bool HasDied(); + void CalculateNewFearpoint(); + float FindGroundZ(float new_x, float new_y, float z_offset=0.0); + VERTEX UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChange, bool &NodeReached); + void PrintRoute(); + void UpdateRuneFlags(); + + virtual float GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod); + + enum {MAX_PROCS = 4}; + tProc PermaProcs[MAX_PROCS]; + tProc SpellProcs[MAX_PROCS]; + tProc DefensiveProcs[MAX_PROCS]; + tProc RangedProcs[MAX_PROCS]; + tProc SkillProcs[MAX_PROCS]; + + char name[64]; + char orig_name[64]; + char clean_name[64]; + char lastname[64]; + + bool bEnraged; + Timer *SpecAttackTimers[SPECATK_MAXNUM]; + bool destructibleobject; + + int32 delta_heading; + float delta_x; + float delta_y; + float delta_z; + + uint8 light; + + float fixedZ; + EmuAppearance _appearance; + uint8 pRunAnimSpeed; + bool m_is_running; + + + Timer attack_timer; + 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 based on a % in decimal form. IE, 1 = 100% mitigation, .5 is 50% + Timer tic_timer; + Timer mana_timer; + + float rewind_x; + float rewind_y; + float rewind_z; + Timer rewind_timer; + + //spell casting vars + Timer spellend_timer; + uint16 casting_spell_id; + float spell_x, spell_y, spell_z; + int attacked_count; + bool delaytimer; + uint16 casting_spell_targetid; + uint16 casting_spell_slot; + uint16 casting_spell_mana; + uint32 casting_spell_inventory_slot; + uint32 casting_spell_timer; + uint32 casting_spell_timer_duration; + uint32 casting_spell_type; + int16 casting_spell_resist_adjust; + uint16 bardsong; + uint8 bardsong_slot; + uint32 bardsong_target_id; + + // Currently 3 max nimbus particle effects at a time + uint32 nimbus_effect1; + uint32 nimbus_effect2; + uint32 nimbus_effect3; + + uint8 haircolor; + uint8 beardcolor; + uint8 eyecolor1; // the eyecolors always seem to be the same, maybe left and right eye? + uint8 eyecolor2; + uint8 hairstyle; + uint8 luclinface; // + uint8 beard; + uint32 drakkin_heritage; + uint32 drakkin_tattoo; + uint32 drakkin_details; + uint32 armor_tint[MAX_MATERIALS]; + + uint8 aa_title; + + Mob* shield_target; + + int ExtraHaste; // for the #haste command + bool mezzed; + bool stunned; + bool charmed; //this isnt fully implemented yet + bool rooted; + bool silenced; + bool amnesiad; + bool inWater; // Set to true or false by Water Detection code if enabled by rules + bool has_virus; // whether this mob has a viral spell on them + uint16 viral_spells[MAX_SPELL_TRIGGER*2]; // Stores the spell ids of the viruses on target and caster ids + int16 rooted_mod; //Modifier to root break chance, defined when root is cast on a target. + bool offhand; + + Timer stunned_timer; + Timer spun_timer; + Timer bardsong_timer; + Timer gravity_timer; + Timer viral_timer; + uint8 viral_timer_counter; + uint16 adverrorinfo; + + // MobAI stuff + eStandingPetOrder pStandingPetOrder; + uint32 minLastFightingDelayMoving; + uint32 maxLastFightingDelayMoving; + float pAggroRange; + float pAssistRange; + Timer* AIthink_timer; + Timer* AImovement_timer; + Timer* AItarget_check_timer; + bool movetimercompleted; + bool permarooted; + Timer* AIscanarea_timer; + Timer* AIwalking_timer; + Timer* AIfeignremember_timer; + uint32 pLastFightingDelayMoving; + HateList hate_list; + std::set feign_memory_list; + // This is to keep track of mobs we cast faction mod spells on + std::map faction_bonuses; // Primary FactionID, Bonus + void AddFactionBonus(uint32 pFactionID,int32 bonus); + int32 GetFactionBonus(uint32 pFactionID); + // This is to keep track of item faction modifiers + std::map item_faction_bonuses; // Primary FactionID, Bonus + void AddItemFactionBonus(uint32 pFactionID,int32 bonus); + int32 GetItemFactionBonus(uint32 pFactionID); + void ClearItemFactionBonuses(); + + void CalculateFearPosition(); + uint32 move_tic_count; + + bool flee_mode; + Timer flee_timer; + + bool pAIControlled; + bool roamer; + bool logging_enabled; + + int wandertype; + int pausetype; + + int cur_wp; + float cur_wp_x; + float cur_wp_y; + float cur_wp_z; + int cur_wp_pause; + float cur_wp_heading; + + int patrol; + float fear_walkto_x; + float fear_walkto_y; + float fear_walkto_z; + bool curfp; + + // Pathing + // + VERTEX PathingDestination; + VERTEX PathingLastPosition; + int PathingLoopCount; + int PathingLastNodeVisited; + list Route; + LOSType PathingLOSState; + Timer *PathingLOSCheckTimer; + Timer *PathingRouteUpdateTimerShort; + Timer *PathingRouteUpdateTimerLong; + bool DistractedFromGrid; + int PathingTraversedNodes; + + uint32 pDontHealMeBefore; + uint32 pDontBuffMeBefore; + uint32 pDontDotMeBefore; + uint32 pDontRootMeBefore; + uint32 pDontSnareMeBefore; + uint32 pDontCureMeBefore; + + // Bind wound + Timer bindwound_timer; + Mob* bindwound_target; + // hp event + int nexthpevent; + int nextinchpevent; + + //temppet + bool hasTempPet; + + EGNode *_egnode; //the EG node we are in + float tarx; + float tary; + float tarz; + uint8 tar_ndx; + float tar_vector; + float tar_vx; + float tar_vy; + float tar_vz; + float test_vector; + + bool m_hasRune; + bool m_hasSpellRune; + bool m_hasPartialMeleeRune; + bool m_hasPartialSpellRune; + bool m_hasDeathSaveChance; + 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; + int QGVarDuration(const char *fmt); + void InsertQuestGlobal(int charid, int npcid, int zoneid, const char *name, const char *value, int expdate); + +private: + void _StopSong(); //this is not what you think it is + Mob* target; +}; + +#endif + diff --git a/zone/net.cpp b/zone/net.cpp new file mode 100644 index 000000000..b1832f48b --- /dev/null +++ b/zone/net.cpp @@ -0,0 +1,1385 @@ +#define DONT_SHARED_OPCODES +/* 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 "../common/debug.h" +#include "features.h" +#include +using namespace std; +#include +#include +#include +#include +#include + #ifdef _CRTDBG_MAP_ALLOC + #undef new + #endif +#include + #ifdef _CRTDBG_MAP_ALLOC + #define new new(_NORMAL_BLOCK, __FILE__, __LINE__) + #endif +using namespace std; +#ifdef _WINDOWS +#include +#define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +bool spells_loaded = false; +volatile bool RunLoops = true; +extern volatile bool ZoneLoaded; +#ifdef SHAREMEM + #include "../common/EMuShareMem.h" + extern LoadEMuShareMemDLL EMuShareMemDLL; + #ifndef WIN32 + #include + #include + #include + #include +#ifndef FREEBSD + union semun { + int val; + struct semid_ds *buf; + ushort *array; + struct seminfo *__buf; + void *__pad; + }; +#endif + #endif +#endif + + + + +#include "../common/queue.h" +#include "../common/timer.h" +#include "../common/EQStream.h" +#include "../common/EQStreamFactory.h" +#include "../common/eq_packet_structs.h" +#include "../common/Mutex.h" +#include "../common/version.h" +#include "../common/EQEMuError.h" +#include "ZoneConfig.h" +#include "../common/packet_dump_file.h" +#include "../common/opcodemgr.h" +#include "../common/guilds.h" +#include "../common/EQStreamIdent.h" +#include "../common/patches/patches.h" +#include "../common/rulesys.h" +#include "../common/MiscFunctions.h" +#include "../common/platform.h" +#include "../common/crash.h" + +#include "masterentity.h" +#include "worldserver.h" +#include "net.h" +#include "spdat.h" +#include "zone.h" +#include "command.h" +#include "parser.h" +#include "embparser.h" +#include "perlparser.h" +#include "client_logs.h" +#include "questmgr.h" +#include "titles.h" +#include "guild_mgr.h" +#include "tasks.h" +#include "QuestParserCollection.h" + +TimeoutManager timeout_manager; +NetConnection net; +EntityList entity_list; +WorldServer worldserver; +uint32 numclients = 0; +#ifdef CATCH_CRASH +uint8 error = 0; +#endif +char errorname[32]; +uint16 adverrornum = 0; +extern Zone* zone; +EQStreamFactory eqsf(ZoneStream); +npcDecayTimes_Struct npcCorpseDecayTimes[100]; +TitleManager title_manager; +DBAsyncFinishedQueue MTdbafq; +DBAsync *dbasync = NULL; +RuleManager *rules = new RuleManager(); +TaskManager *taskmanager = 0; +QuestParserCollection *parse = 0; + +bool zoneprocess; + +#if defined(NEW_LoadSPDat) || defined(DB_LoadSPDat) + // For NewLoadSPDat function + const SPDat_Spell_Struct* spells; + SPDat_Spell_Struct* spells_delete; + int32 GetMaxSpellID(); + + + void LoadSPDat(); + bool FileLoadSPDat(SPDat_Spell_Struct* sp, int32 iMaxSpellID); + int32 SPDAT_RECORDS = -1; +#else + #define SPDat_Location "spdat.eff" + SPDat_Spell_Struct spells[SPDAT_RECORDS]; + void LoadSPDat(SPDat_Spell_Struct** SpellsPointer = 0); + + +#endif + +#ifdef _WINDOWS +#include +#else +#include +#include "../common/unix.h" +#endif + +void Shutdown(); +extern void MapOpcodes(); + +//bool ZoneBootup(uint32 iZoneID, bool iStaticZone = false); +//char *strsep(char **stringp, const char *delim); + +#ifdef ADDONCMD +#include "addoncmd.h" +extern AddonCmd addonCmd; +#endif + +int main(int argc, char** argv) { + RegisterExecutablePlatform(ExePlatformZone); + set_exception_handler(); + + const char *zone_name; + + if(argc == 3) { + worldserver.SetLauncherName(argv[2]); + worldserver.SetLaunchedName(argv[1]); + if(strncmp(argv[1], "dynamic_", 8) == 0) { + //dynamic zone with a launcher name correlation + zone_name = "."; + } else { + zone_name = argv[1]; + worldserver.SetLaunchedName(zone_name); + } + } else if (argc == 2) { + worldserver.SetLauncherName("NONE"); + worldserver.SetLaunchedName(argv[1]); + if(strncmp(argv[1], "dynamic_", 8) == 0) { + //dynamic zone with a launcher name correlation + zone_name = "."; + } else { + zone_name = argv[1]; + worldserver.SetLaunchedName(zone_name); + } + } else { + zone_name = "."; + worldserver.SetLaunchedName("."); + worldserver.SetLauncherName("NONE"); + } + + _log(ZONE__INIT, "Loading server configuration.."); + if (!ZoneConfig::LoadConfig()) { + _log(ZONE__INIT_ERR, "Loading server configuration failed."); + return(1); + } + const ZoneConfig *Config=ZoneConfig::get(); + + if(!load_log_settings(Config->LogSettingsFile.c_str())) + _log(ZONE__INIT, "Warning: Unable to read %s", Config->LogSettingsFile.c_str()); + else + _log(ZONE__INIT, "Log settings loaded from %s", Config->LogSettingsFile.c_str()); + + worldserver.SetPassword(Config->SharedKey.c_str()); + + _log(ZONE__INIT, "Connecting to MySQL..."); + if (!database.Connect( + Config->DatabaseHost.c_str(), + Config->DatabaseUsername.c_str(), + Config->DatabasePassword.c_str(), + Config->DatabaseDB.c_str(), + Config->DatabasePort)) { + _log(ZONE__INIT_ERR, "Cannot continue without a database connection."); + return(1); + } + dbasync = new DBAsync(&database); + dbasync->AddFQ(&MTdbafq); + guild_mgr.SetDatabase(&database); + + GuildBanks = NULL; + +#ifdef _EQDEBUG + _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +// _crtBreakAlloc = 2025; +#endif + + _log(ZONE__INIT, "CURRENT_ZONE_VERSION: %s", CURRENT_ZONE_VERSION); + + /* + * Setup nice signal handlers + */ + if (signal(SIGINT, CatchSignal) == SIG_ERR) { + _log(ZONE__INIT_ERR, "Could not set signal handler"); + return 0; + } + if (signal(SIGTERM, CatchSignal) == SIG_ERR) { + _log(ZONE__INIT_ERR, "Could not set signal handler"); + return 0; + } + #ifndef WIN32 + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { + _log(ZONE__INIT_ERR, "Could not set signal handler"); + return 0; + } + #endif + + const char *log_ini_file = "./log.ini"; + if(!load_log_settings(log_ini_file)) + _log(ZONE__INIT, "Warning: Unable to read %s", log_ini_file); + else + _log(ZONE__INIT, "Log settings loaded from %s", log_ini_file); + + _log(ZONE__INIT, "Mapping Incoming Opcodes"); + MapOpcodes(); + _log(ZONE__INIT, "Loading Variables"); + database.LoadVariables(); + _log(ZONE__INIT, "Loading zone names"); + database.LoadZoneNames(); + _log(ZONE__INIT, "Loading items"); + if (!database.LoadItems()) { + _log(ZONE__INIT_ERR, "Loading items FAILED!"); + _log(ZONE__INIT, "Failed. But ignoring error and going on..."); + } + database.LoadItemStatus(); + _log(ZONE__INIT, "Loading npc faction lists"); + if (!database.LoadNPCFactionLists()) { + _log(ZONE__INIT_ERR, "Loading npcs faction lists FAILED!"); + CheckEQEMuErrorAndPause(); + return 0; + } + _log(ZONE__INIT, "Loading loot tables"); + if (!database.LoadLoot()) { + _log(ZONE__INIT_ERR, "Loading loot FAILED!"); + CheckEQEMuErrorAndPause(); + return 0; + } + _log(ZONE__INIT, "Loading skill caps"); + if (!database.LoadSkillCaps()) { + _log(ZONE__INIT_ERR, "Loading skill caps FAILED!"); + CheckEQEMuErrorAndPause(); + return 0; + } + _log(ZONE__INIT, "Loading spells"); + LoadSPDat(); + + // New Load function. keeping it commented till I figure out why its not working correctly in linux. Trump. + // NewLoadSPDat(); + _log(ZONE__INIT, "Loading guilds"); + guild_mgr.LoadGuilds(); + _log(ZONE__INIT, "Loading factions"); + database.LoadFactionData(); + _log(ZONE__INIT, "Loading titles"); + title_manager.LoadTitles(); + _log(ZONE__INIT, "Loading AA effects"); + database.LoadAAEffects(); + _log(ZONE__INIT, "Loading tributes"); + database.LoadTributes(); + _log(ZONE__INIT, "Loading corpse timers"); + database.GetDecayTimes(npcCorpseDecayTimes); + _log(ZONE__INIT, "Loading commands"); + int retval=command_init(); + if(retval<0) + _log(ZONE__INIT_ERR, "Command loading FAILED"); + else + _log(ZONE__INIT, "%d commands loaded", retval); + + //rules: + { + char tmp[64]; + if (database.GetVariable("RuleSet", tmp, sizeof(tmp)-1)) { + _log(ZONE__INIT, "Loading rule set '%s'", tmp); + if(!rules->LoadRules(&database, tmp)) { + _log(ZONE__INIT_ERR, "Failed to load ruleset '%s', falling back to defaults.", tmp); + } + } else { + if(!rules->LoadRules(&database, "default")) { + _log(ZONE__INIT, "No rule set configured, using default rules"); + } else { + _log(ZONE__INIT, "Loaded default rule set 'default'", tmp); + } + } + } + + if(RuleB(TaskSystem, EnableTaskSystem)) { + _log(ZONE__INIT, "Loading Tasks"); + taskmanager = new TaskManager; + taskmanager->LoadTasks(); + } + + parse = new QuestParserCollection(); + PerlXSParser *pxs = new PerlXSParser(); + Parser *ps = new Parser(); + parse->RegisterQuestInterface(pxs, "pl"); + //parse->RegisterQuestInterface(ps, "qst"); + + + //now we have our parser, load the quests + _log(ZONE__INIT, "Loading quests"); + parse->ReloadQuests(); + + +#ifdef ADDONCMD + _log(ZONE__INIT, "Looding addon commands from dll"); + if ( !addonCmd.openLib() ) { + _log(ZONE__INIT_ERR, "Loading addons failed =("); + } +#endif +#ifdef CLIENT_LOGS + LogFile->SetAllCallbacks(ClientLogs::EQEmuIO_buf); + LogFile->SetAllCallbacks(ClientLogs::EQEmuIO_fmt); + LogFile->SetAllCallbacks(ClientLogs::EQEmuIO_pva); +#endif + if (!worldserver.Connect()) { + _log(ZONE__INIT_ERR, "worldserver.Connect() FAILED!"); + } + + Timer InterserverTimer(INTERSERVER_TIMER); // does MySQL pings and auto-reconnect +#ifdef EQPROFILE +#ifdef PROFILE_DUMP_TIME + Timer profile_dump_timer(PROFILE_DUMP_TIME*1000); + profile_dump_timer.Start(); +#endif +#endif + if (!strlen(zone_name) || !strcmp(zone_name,".")) { + _log(ZONE__INIT, "Entering sleep mode"); + } else if (!Zone::Bootup(database.GetZoneID(zone_name), 0, true)) { //todo: go above and fix this to allow cmd line instance + _log(ZONE__INIT_ERR, "Zone bootup FAILED!"); + zone = 0; + } + + //register all the patches we have avaliable with the stream identifier. + EQStreamIdentifier stream_identifier; + RegisterAllPatches(stream_identifier); + +#ifndef WIN32 + _log(COMMON__THREADS, "Main thread running with thread id %d", pthread_self()); +#endif + + Timer quest_timers(100); + UpdateWindowTitle(); + bool worldwasconnected = worldserver.Connected(); + EQStream* eqss; + EQStreamInterface *eqsi; + Timer temp_timer(10); + temp_timer.Start(); + while(RunLoops) { + { //profiler block to omit the sleep from times + _ZP(net_main); + + //Advance the timer to our current point in time + Timer::SetCurrentTime(); + + //process stuff from world +#ifdef CATCH_CRASH + try{ +#endif + worldserver.Process(); +#ifdef CATCH_CRASH + } + catch(...){ + error = 1; + worldserver.Disconnect(); + worldwasconnected = false; + } +#endif + if (!eqsf.IsOpen() && Config->ZonePort!=0) { + _log(ZONE__INIT, "Starting EQ Network server on port %d",Config->ZonePort); + if (!eqsf.Open(Config->ZonePort)) { + _log(ZONE__INIT_ERR, "Failed to open port %d",Config->ZonePort); + ZoneConfig::SetZonePort(0); + worldserver.Disconnect(); + worldwasconnected = false; + } + } + + //check the factory for any new incoming streams. + while ((eqss = eqsf.Pop())) { + //pull the stream out of the factory and give it to the stream identifier + //which will figure out what patch they are running, and set up the dynamic + //structures and opcodes for that patch. + struct in_addr in; + in.s_addr = eqss->GetRemoteIP(); + _log(WORLD__CLIENT, "New connection from %s:%d", inet_ntoa(in),ntohs(eqss->GetRemotePort())); + stream_identifier.AddStream(eqss); //takes the stream + } + + //give the stream identifier a chance to do its work.... + stream_identifier.Process(); + + //check the stream identifier for any now-identified streams + while((eqsi = stream_identifier.PopIdentified())) { + //now that we know what patch they are running, start up their client object + struct in_addr in; + in.s_addr = eqsi->GetRemoteIP(); + _log(WORLD__CLIENT, "New client from %s:%d", inet_ntoa(in), ntohs(eqsi->GetRemotePort())); + Client* client = new Client(eqsi); + entity_list.AddClient(client); + } + + + //check for timeouts in other threads + timeout_manager.CheckTimeouts(); + + if (worldserver.Connected()) { + worldwasconnected = true; + } + else { + if (worldwasconnected && ZoneLoaded) + entity_list.ChannelMessageFromWorld(0, 0, 6, 0, 0, "WARNING: World server connection lost"); + worldwasconnected = false; + } + if (ZoneLoaded && temp_timer.Check()) { + { + uint8 error2 = 4; +#ifdef CATCH_CRASH + try{ +#endif + if(net.group_timer.Enabled() && net.group_timer.Check()) + entity_list.GroupProcess(); + error2 = 99; + if(net.door_timer.Enabled() && net.door_timer.Check()) + entity_list.DoorProcess(); + error2 = 98; + if(net.object_timer.Enabled() && net.object_timer.Check()) + entity_list.ObjectProcess(); + error2 = 97; + if(net.corpse_timer.Enabled() && net.corpse_timer.Check()) + entity_list.CorpseProcess(); + if(net.trap_timer.Enabled() && net.trap_timer.Check()) + entity_list.TrapProcess(); + if(net.raid_timer.Enabled() && net.raid_timer.Check()) + entity_list.RaidProcess(); + error2 = 98; + error2 = 96; + entity_list.Process(); + error2 = 95; +#ifdef CATCH_CRASH + try{ + entity_list.MobProcess(); + } + catch(...){ + printf("Catching Mob Crash...\n"); + } +#else + entity_list.MobProcess(); +#endif + error2 = 94; + entity_list.BeaconProcess(); +#ifdef CATCH_CRASH + } + catch(...){ + error=error2; + } + try{ +#endif + if (zone) { + zoneprocess= zone->Process(); + if (!zoneprocess) { + Zone::Shutdown(); + } + } +#ifdef CATCH_CRASH + } + catch(...){ + error = 2; + } + try{ +#endif + if(quest_timers.Check()) + quest_manager.Process(); +#ifdef CATCH_CRASH + } + catch(...){ + error = 77777; + } +#endif + } + } + DBAsyncWork* dbaw = 0; + while ((dbaw = MTdbafq.Pop())) { + DispatchFinishedDBAsync(dbaw); + } + if (InterserverTimer.Check() +#ifdef CATCH_CRASH + && !error +#endif + ) { +#ifdef CATCH_CRASH + try{ +#endif + InterserverTimer.Start(); + database.ping(); + AsyncLoadVariables(dbasync, &database); +// NPC::GetAILevel(true); + entity_list.UpdateWho(); + if (worldserver.TryReconnect() && (!worldserver.Connected())) + worldserver.AsyncConnect(); +#ifdef CATCH_CRASH + } + catch(...) + { + error = 16; + RunLoops = false; + } +#endif + } +#ifdef CATCH_CRASH + if (error){ + RunLoops = false; + } +#endif +#if defined(_EQDEBUG) && defined(DEBUG_PC) + QueryPerformanceCounter(&tmp3); + mainloop_time += tmp3.QuadPart - tmp2.QuadPart; + if (!--tmp0) { + tmp0 = 200; + printf("Elapsed Tics : %9.0f (%1.4f sec)\n", (double)mainloop_time, ((double)mainloop_time/tmp.QuadPart)); + printf("NPCAI Tics : %9.0f (%1.2f%%)\n", (double)npcai_time, ((double)npcai_time/mainloop_time)*100); + printf("FindSpell Tics: %9.0f (%1.2f%%)\n", (double)findspell_time, ((double)findspell_time/mainloop_time)*100); + printf("AtkAllowd Tics: %9.0f (%1.2f%%)\n", (double)IsAttackAllowed_time, ((double)IsAttackAllowed_time/mainloop_time)*100); + printf("ClientPro Tics: %9.0f (%1.2f%%)\n", (double)clientprocess_time, ((double)clientprocess_time/mainloop_time)*100); + printf("ClientAtk Tics: %9.0f (%1.2f%%)\n", (double)clientattack_time, ((double)clientattack_time/mainloop_time)*100); + mainloop_time = 0; + npcai_time = 0; + findspell_time = 0; + IsAttackAllowed_time = 0; + clientprocess_time = 0; + clientattack_time = 0; + } +#endif +#ifdef EQPROFILE +#ifdef PROFILE_DUMP_TIME + if(profile_dump_timer.Check()) { + DumpZoneProfile(); + } +#endif +#endif + } //end extra profiler block + Sleep(ZoneTimerResolution); + } + + safe_delete(parse); + safe_delete(pxs); + safe_delete(ps); + +#ifdef CATCH_CRASH + if (error) + FilePrint("eqemudebug.log",true,true,"Zone %i crashed. Errorcode: %i/%i. Current zone loaded:%s. Current clients:%i. Caused by: %s",Config->ZonePort, error,adverrornum, zone->GetShortName(), numclients,errorname); + try{ + entity_list.Message(0, 15, "ZONEWIDE_MESSAGE: This zone caused a fatal error and will shut down now. Your character will be restored to the last saved status. We are sorry for any inconvenience!"); + } + catch(...){} + if (error){ +#ifdef _WINDOWS + ExitProcess(error); +#else + entity_list.Clear(); + safe_delete(zone); +#endif + } +#endif + + entity_list.Clear(); + if (zone != 0 +#ifdef CATCH_CRASH + & !error +#endif + ) + Zone::Shutdown(true); + //Fix for Linux world server problem. + eqsf.Close(); + worldserver.Disconnect(); + dbasync->CommitWrites(); + dbasync->StopThread(); +#if defined(NEW_LoadSPDat) || defined(DB_LoadSPDat) + safe_delete(spells_delete); +#endif + safe_delete(taskmanager); + command_deinit(); + + CheckEQEMuErrorAndPause(); + _log(ZONE__INIT, "Proper zone shutdown complete."); + return 0; +} + +void CatchSignal(int sig_num) { +#ifdef _WINDOWS + _log(ZONE__INIT, "Recieved signal: %i", sig_num); +#else + _log(ZONE__INIT, "Recieved signal: %i in thread %d", sig_num, pthread_self()); +#endif + RunLoops = false; +} + +void Shutdown() +{ + Zone::Shutdown(true); + RunLoops = false; + worldserver.Disconnect(); + // safe_delete(worldserver); + _log(ZONE__INIT, "Shutting down..."); +} + +uint32 NetConnection::GetIP() +{ + char name[255+1]; + size_t len = 0; + hostent* host = 0; + + if (gethostname(name, len) < 0 || len <= 0) + { + return 0; + } + + host = (hostent*)gethostbyname(name); + if (host == 0) + { + return 0; + } + + return inet_addr(host->h_addr); +} + +uint32 NetConnection::GetIP(char* name) +{ + hostent* host = 0; + + host = (hostent*)gethostbyname(name); + if (host == 0) + { + return 0; + } + + return inet_addr(host->h_addr); + +} + +void NetConnection::SaveInfo(char* address, uint32 port, char* waddress, char* filename) { + + ZoneAddress = new char[strlen(address)+1]; + strcpy(ZoneAddress, address); + ZonePort = port; + WorldAddress = new char[strlen(waddress)+1]; + strcpy(WorldAddress, waddress); + strn0cpy(ZoneFileName, filename, sizeof(ZoneFileName)); +} + +NetConnection::NetConnection() +: + object_timer(5000), + door_timer(5000), + corpse_timer(2000), + group_timer(1000), + raid_timer(1000), + trap_timer(1000) +{ + ZonePort = 0; + ZoneAddress = 0; + WorldAddress = 0; + group_timer.Disable(); + raid_timer.Disable(); + corpse_timer.Disable(); + door_timer.Disable(); + object_timer.Disable(); + trap_timer.Disable(); +} + +NetConnection::~NetConnection() { + if (ZoneAddress != 0) + safe_delete_array(ZoneAddress); + if (WorldAddress != 0) + safe_delete_array(WorldAddress); +} +bool chrcmpI(const char* a, const char* b) { +#if EQDEBUG >= 11 + _log(EQEMuLog::Debug, "crhcmpl() a:%i b:%i", (int*) a, (int*) b); +#endif + if(((int)* a)==((int)* b)) + return false; + else + return true; +} + +#if defined(NEW_LoadSPDat) || defined(DB_LoadSPDat) +int32 GetMaxSpellID() { +#ifdef NEW_LoadSPDat + int tempid=0, oldid=-1; + char spell_line_start[2048]; + char* spell_line = spell_line_start; + char token[64]=""; + char seps[] = "^"; + const char *spells_file=ZoneConfig::get()->SpellsFile.c_str(); + //ifstream in(spells_file); + + /*struct stat s; + if(stat(spells_file, &s) != 0) { + _log(SPELLS__LOAD_ERR, "File '%s' not found (stat failed), spell loading FAILED!", spells_file); + return(-1); + } + */ + + FILE *sf = fopen(spells_file, "r"); + + if(sf == NULL) { + _log(SPELLS__LOAD_ERR, "File '%s' not found, spell loading FAILED!", spells_file); + return -1; + } + + fgets(spell_line, sizeof(spell_line_start), sf); + while(!feof(sf)) { + strcpy(token,strtok(spell_line, seps)); + if(token!=NULL); + { + tempid = atoi(token); + if(tempid>oldid) + oldid = tempid; + else + break; + } + fgets(spell_line, sizeof(spell_line_start), sf); + } + + fclose(sf); + + /*ifstream in(spells_file); + + if(!in) { + _log(SPELLS__LOAD_ERR, "File '%s' not found, spell loading FAILED!", spells_file); + return -1; + } + + in.getline(spell_line, sizeof(spell_line_start)); + while(strlen(spell_line)>1) + { + strcpy(token,strtok(spell_line, seps)); + if(token!=NULL); + { + tempid = atoi(token); + if(tempid>oldid) + oldid = tempid; + else + break; + } + in.getline(spell_line, sizeof(spell_line_start)); + }*/ + + + return oldid; + +#else // defined(DB_LoadSPDat) + //load from DB + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + int32 ret = 0; + if (database.RunQuery(query, MakeAnyLenString(&query, + "SELECT MAX(id) FROM spells_new"), + errbuf, &result)) { + safe_delete_array(query); + row = mysql_fetch_row(result); + ret = atoi(row[0]); + mysql_free_result(result); + } else { + _log(SPELLS__LOAD_ERR, "Error in GetMaxSpellID query '%s' %s", query, errbuf); + safe_delete_array(query); + ret = -1; + } + return ret; +#endif +} +#ifdef SHAREMEM +extern "C" bool extFileLoadSPDat(void* sp, int32 iMaxSpellID) { return FileLoadSPDat((SPDat_Spell_Struct*) sp, iMaxSpellID); } +#endif + +void LoadSPDat() { + if (SPDAT_RECORDS != -1) { + _log(SPELLS__LOAD, "LoadSPDat() SPDAT_RECORDS:%i != -1, spells already loaded?", SPDAT_RECORDS); + } + int32 MaxSpellID = GetMaxSpellID(); + if (MaxSpellID == -1) { +#ifdef NEW_LoadSPDat + _log(SPELLS__LOAD, "LoadSPDat() MaxSpellID == -1, %s missing?", ZoneConfig::get()->SpellsFile.c_str()); +#else // defined(DB_LoadSPDat) + _log(SPELLS__LOAD, "LoadSPDat() MaxSpellID == -1, error in GetMaxSpellID()?"); +#endif + return; + } +#ifdef SHAREMEM + if (!EMuShareMemDLL.Load()) + return; + SPDAT_RECORDS = MaxSpellID+1; + if (EMuShareMemDLL.Spells.DLLLoadSPDat((const CALLBACK_FileLoadSPDat)&extFileLoadSPDat, (const void**) &spells, &SPDAT_RECORDS, sizeof(SPDat_Spell_Struct))) { + spells_loaded = true; + } + else { + SPDAT_RECORDS = 0; + _log(SPELLS__LOAD_ERR, "LoadSPDat() EMuShareMemDLL.Spells.DLLLoadSPDat() returned false"); + return; + } +#else + spells_delete = new SPDat_Spell_Struct[MaxSpellID+1]; + if (FileLoadSPDat(spells_delete, MaxSpellID)) { + spells = spells_delete; + SPDAT_RECORDS = MaxSpellID+1; + spells_loaded = true; + } + else { + safe_delete(spells_delete); + _log(SPELLS__LOAD_ERR, "LoadSPDat() FileLoadSPDat() returned false"); + return; + } +#endif +} + +bool FileLoadSPDat(SPDat_Spell_Struct* sp, int32 iMaxSpellID) { +#ifdef NEW_LoadSPDat + int tempid=0; + uint16 counter=0; + char spell_line[2048]; + const char *spells_file=ZoneConfig::get()->SpellsFile.c_str(); + _log(SPELLS__LOAD,"FileLoadSPDat() Loading spells from %s", spells_file); + + FILE *sf = fopen(spells_file, "r"); + + if(sf == NULL) { + _log(SPELLS__LOAD_ERR, "File '%s' not found, spell loading FAILED!", spells_file); + return false; + } +/* ifstream in(spells_file); + if(!in) { + _log(SPELLS__LOAD_ERR, "File '%s' not found, spell loading FAILED!", spells_file); + return false; + } + */ + if (iMaxSpellID < 0) { + _log(SPELLS__LOAD_ERR,"FileLoadSPDat() Loading spells FAILED! iMaxSpellID:%i < 0", iMaxSpellID); + return false; + } +/* + +This is hanging on freebsd for me, not sure why... + +//#if EQDEBUG >= 1 + else { + _log(SPELLS__LOAD,"FileLoadSPDat() Highest spell ID:%i", iMaxSpellID); + } +//#endif +*/ +/* in.close(); + in.open(spells_file); + if(!in) { + _log(SPELLS__LOAD_ERR, "File '%s' not found, spell loading FAILED!", spells_file); + return false; + } + while(!in.eof()) { + in.getline(spell_line, sizeof(spell_line)); + Seperator sep(spell_line, '^', 200, 100, false, 0, 0, false); + + if(spell_line[0]=='\0') + break; + + tempid = atoi(sep.arg[0]); + if (tempid > iMaxSpellID) { + _log(SPELLS__LOAD_ERR, "FATAL FileLoadSPDat() tempid:%i >= iMaxSpellID:%i", tempid, iMaxSpellID); + return false; + } + */ + + while(!feof(sf)) { + if(fgets(spell_line, sizeof(spell_line), sf) == NULL) + break; + if(spell_line[0]=='\0') + continue; + + Seperator sep(spell_line, '^', 220, 100, false, 0, 0, false); + + + tempid = atoi(sep.arg[0]); + if (tempid > iMaxSpellID) { + _log(SPELLS__LOAD_ERR, "FATAL FileLoadSPDat() tempid:%i >= iMaxSpellID:%i", tempid, iMaxSpellID); + return false; + } + + counter++; + strcpy(sp[tempid].name, sep.arg[1]); + strcpy(sp[tempid].player_1, sep.arg[2]); + strcpy(sp[tempid].teleport_zone, sep.arg[3]); + strcpy(sp[tempid].you_cast, sep.arg[4]); + strcpy(sp[tempid].other_casts, sep.arg[5]); + strcpy(sp[tempid].cast_on_you, sep.arg[6]); + strcpy(sp[tempid].cast_on_other, sep.arg[7]); + strcpy(sp[tempid].spell_fades, sep.arg[8]); + + sp[tempid].range=atof(sep.arg[9]); + sp[tempid].aoerange=atof(sep.arg[10]); + sp[tempid].pushback=atof(sep.arg[11]); + sp[tempid].pushup=atof(sep.arg[12]); + sp[tempid].cast_time=atoi(sep.arg[13]); + sp[tempid].recovery_time=atoi(sep.arg[14]); + sp[tempid].recast_time=atoi(sep.arg[15]); + sp[tempid].buffdurationformula=atoi(sep.arg[16]); + sp[tempid].buffduration=atoi(sep.arg[17]); + sp[tempid].AEDuration=atoi(sep.arg[18]); + sp[tempid].mana=atoi(sep.arg[19]); + + int y=0; + for(y=0; y< EFFECT_COUNT;y++) + sp[tempid].base[y]=atoi(sep.arg[20+y]); + for(y=0; y < EFFECT_COUNT; y++) + sp[tempid].base2[y]=atoi(sep.arg[32+y]); + for(y=0; y< EFFECT_COUNT;y++) + sp[tempid].max[y]=atoi(sep.arg[44+y]); + + sp[tempid].icon=atoi(sep.arg[56]); + sp[tempid].memicon=atoi(sep.arg[57]); + + for(y=0; y< 4;y++) + sp[tempid].components[y]=atoi(sep.arg[58+y]); + + for(y=0; y< 4;y++) + sp[tempid].component_counts[y]=atoi(sep.arg[62+y]); + + for(y=0; y< 4;y++) + sp[tempid].NoexpendReagent[y]=atoi(sep.arg[66+y]); + + for(y=0; y< EFFECT_COUNT;y++) + sp[tempid].formula[y]=atoi(sep.arg[70+y]); + + sp[tempid].LightType=atoi(sep.arg[82]); + sp[tempid].goodEffect=atoi(sep.arg[83]); + sp[tempid].Activated=atoi(sep.arg[84]); + sp[tempid].resisttype=atoi(sep.arg[85]); + + for(y=0; y< 12;y++) + sp[tempid].effectid[y]=atoi(sep.arg[86+y]); + + sp[tempid].targettype = (SpellTargetType) atoi(sep.arg[98]); + sp[tempid].basediff=atoi(sep.arg[99]); + int tmp_skill = atoi(sep.arg[100]);; + if(tmp_skill < 0 || tmp_skill > HIGHEST_SKILL) + sp[tempid].skill = BEGGING; /* not much better we can do. */ + else + sp[tempid].skill = (SkillType) tmp_skill; + sp[tempid].zonetype=atoi(sep.arg[101]); + sp[tempid].EnvironmentType=atoi(sep.arg[102]); + sp[tempid].TimeOfDay=atoi(sep.arg[103]); + + for(y=0; y < PLAYER_CLASS_COUNT;y++) + sp[tempid].classes[y]=atoi(sep.arg[104+y]); + + sp[tempid].CastingAnim=atoi(sep.arg[120]); + sp[tempid].TargetAnim=atoi(sep.arg[121]); + sp[tempid].TravelType=atoi(sep.arg[122]); + sp[tempid].SpellAffectIndex=atoi(sep.arg[123]); + sp[tempid].disallow_sit = atoi(sep.arg[124]); + sp[tempid].spacing125=atoi(sep.arg[125]); + + for (y = 0; y < 16; y++) + sp[tempid].deities[y]=atoi(sep.arg[126+y]); + + for (y = 0; y < 2; y++) + sp[tempid].spacing142[y]=atoi(sep.arg[142+y]); + + sp[tempid].new_icon=atoi(sep.arg[144]); + sp[tempid].spellanim=atoi(sep.arg[145]); + sp[tempid].uninterruptable=atoi(sep.arg[146]); + sp[tempid].ResistDiff=atoi(sep.arg[147]); + sp[tempid].dot_stacking_exempt=atoi(sep.arg[148]); + sp[tempid].deletable=atoi(sep.arg[149]); + sp[tempid].RecourseLink = atoi(sep.arg[150]); + + for(y = 0; y < 3;y++) + sp[tempid].spacing151[y]=atoi(sep.arg[151+y]); + + sp[tempid].short_buff_box = atoi(sep.arg[154]); + sp[tempid].descnum = atoi(sep.arg[155]); + sp[tempid].typedescnum = atoi(sep.arg[156]); + sp[tempid].effectdescnum = atoi(sep.arg[157]); + + for(y = 0; y < 4;y++) + sp[tempid].spacing158[y]=atoi(sep.arg[158+y]); + + sp[tempid].bonushate=atoi(sep.arg[162]); + + for(y = 0; y < 3;y++) + sp[tempid].spacing163[y]=atoi(sep.arg[163+y]); + + sp[tempid].EndurCost=atoi(sep.arg[166]); + sp[tempid].EndurTimerIndex=atoi(sep.arg[167]); + sp[tempid].IsDisciplineBuff=atoi(sep.arg[168]); + + for(y = 0; y < 4;y++) + sp[tempid].spacing169[y]=atoi(sep.arg[169+y]); + + sp[tempid].HateAdded=atoi(sep.arg[173]); + sp[tempid].EndurUpkeep=atoi(sep.arg[174]); + + sp[tempid].spacing175=atoi(sep.arg[175]); + sp[tempid].numhits = atoi(sep.arg[176]); + + sp[tempid].pvpresistbase=atoi(sep.arg[177]); + sp[tempid].pvpresistcalc=atoi(sep.arg[178]); + sp[tempid].pvpresistcap=atoi(sep.arg[179]); + sp[tempid].spell_category=atoi(sep.arg[180]); + + for(y = 0; y < 4;y++) + sp[tempid].spacing181[y]=atoi(sep.arg[181+y]); + + sp[tempid].can_mgb=atoi(sep.arg[185]); + sp[tempid].dispel_flag = atoi(sep.arg[186]); + sp[tempid].MinResist = atoi(sep.arg[189]); + sp[tempid].MaxResist = atoi(sep.arg[190]); + sp[tempid].viral_targets = atoi(sep.arg[191]); + sp[tempid].viral_timer = atoi(sep.arg[192]); + sp[tempid].NimbusEffect = atoi(sep.arg[193]); + sp[tempid].directional_start = (float)atoi(sep.arg[194]); + sp[tempid].directional_end = (float)atoi(sep.arg[195]); + sp[tempid].spellgroup=atoi(row[207]); + sp[tempid].field209=atoi(row[209]); + sp[tempid].CastRestriction = atoi(sep.arg[211]); + sp[tempid].AllowRest = atoi(sep.arg[212]); + + // May crash zone + /* + sp[tempid].nodispell=atoi(row[186]); + sp[tempid].npc_category=atoi(row[187]); + sp[tempid].npc_usefulness=atoi(row[188]); + + for (y = 0; y < 18; y++) + sp[tempid].spacing189[y]=atoi(row[189+y]); + + sp[tempid].spellgroup=atoi(row[207]); + + for (y = 0; y < 18; y++) + sp[tempid].spacing208[y]=atoi(row[208+y]); + */ + sp[tempid].DamageShieldType = 0; + + } + _log(SPELLS__LOAD, "FileLoadSPDat() spells loaded: %i", counter); + //in.close(); + fclose(sf); + // Now fill in the DamageShieldType from the damageshieldtypes table, if it exists. + // + database.DBLoadDamageShieldTypes(sp, iMaxSpellID); + + return true; + +#else // defined(DB_LoadSPDat) + //load from db + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + _log(SPELLS__LOAD,"FileLoadSPDat() Loading spells from database"); + + if (iMaxSpellID < 0) { + _log(SPELLS__LOAD_ERR,"FileLoadSPDat() Loading spells FAILED! iMaxSpellID:%i < 0", iMaxSpellID); + return false; + } else + _log(SPELLS__LOAD,"FileLoadSPDat() Highest spell ID:%i", iMaxSpellID); + + if (database.RunQuery(query, MakeAnyLenString(&query, + "SELECT * FROM spells_new ORDER BY id ASC"), + errbuf, &result)) { + safe_delete_array(query); + + int tempid = 0; + uint16 counter = 0; + + while (row = mysql_fetch_row(result)) { + + tempid = atoi(row[0]); + if (tempid > iMaxSpellID) { // Is this really needed? + _log(SPELLS__LOAD_ERR, "FATAL FileLoadSPDat() tempid:%i >= iMaxSpellID:%i", tempid, iMaxSpellID); + return false; + } + + counter++; + // String fields + 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)); + + // Numeric fields (everything else) + sp[tempid].range=atof(row[9]); + sp[tempid].aoerange=atof(row[10]); + sp[tempid].pushback=atof(row[11]); + sp[tempid].pushup=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]); + + sp[tempid].icon=atoi(row[56]); + sp[tempid].memicon=atoi(row[57]); + + 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].LightType=atoi(row[82]); + 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 = BEGGING; /* not much better we can do. */ + else + sp[tempid].skill = (SkillType) 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].TargetAnim=atoi(row[121]); + sp[tempid].TravelType=atoi(row[122]); + sp[tempid].SpellAffectIndex=atoi(row[123]); + sp[tempid].disallow_sit=atoi(row[124]); + sp[tempid].spacing125=atoi(row[125]); + + for (y = 0; y < 16; y++) + sp[tempid].deities[y]=atoi(row[126+y]); + + for (y = 0; y < 2; y++) + sp[tempid].spacing142[y]=atoi(row[142+y]); + + sp[tempid].new_icon=atoi(row[144]); + sp[tempid].spellanim=atoi(row[145]); + sp[tempid].uninterruptable=atoi(row[146]); + sp[tempid].ResistDiff=atoi(row[147]); + sp[tempid].dot_stacking_exempt=atoi(row[148]); + sp[tempid].deletable=atoi(row[149]); + sp[tempid].RecourseLink = atoi(row[150]); + + for(y = 0; y < 3;y++) + sp[tempid].spacing151[y]=atoi(row[151+y]); + + sp[tempid].short_buff_box = atoi(row[154]); + sp[tempid].descnum = atoi(row[155]); + sp[tempid].typedescnum = atoi(row[156]); + sp[tempid].effectdescnum = atoi(row[157]); + + for(y = 0; y < 4;y++) + sp[tempid].spacing158[y]=atoi(row[158+y]); + + sp[tempid].bonushate=atoi(row[162]); + + for(y = 0; y < 3;y++) + sp[tempid].spacing163[y]=atoi(row[163+y]); + + sp[tempid].EndurCost=atoi(row[166]); + sp[tempid].EndurTimerIndex=atoi(row[167]); + sp[tempid].IsDisciplineBuff=atoi(row[168]); + + for(y = 0; y < 4; y++) + sp[tempid].spacing169[y]=atoi(row[169+y]); + + sp[tempid].HateAdded=atoi(row[173]); + sp[tempid].EndurUpkeep=atoi(row[174]); + + sp[tempid].spacing175=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]); + + for(y = 0; y < 4;y++) + sp[tempid].spacing181[y]=atoi(row[181+y]); + + 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 = (float)atoi(row[194]); + sp[tempid].directional_end = (float)atoi(row[195]); + sp[tempid].spellgroup=atoi(row[207]); + sp[tempid].field209=atoi(row[209]); + sp[tempid].CastRestriction = atoi(row[211]); + sp[tempid].AllowRest = atoi(row[212]); + + // May crash zone +/* + sp[tempid].nodispell=atoi(row[186]); + sp[tempid].npc_category=atoi(row[187]); + sp[tempid].npc_usefulness=atoi(row[188]); + + for (y = 0; y < 18; y++) + sp[tempid].spacing189[y]=atoi(row[189+y]); + + sp[tempid].spellgroup=atoi(row[207]); + + for (y = 0; y < 18; y++) + sp[tempid].spacing208[y]=atoi(row[208+y]); +*/ + sp[tempid].DamageShieldType = 0; + + } + mysql_free_result(result); + _log(SPELLS__LOAD, "FileLoadSPDat() spells loaded: %i", counter); + // Now fill in the DamageShieldType from the damageshieldtypes table, if it exists. + // + database.DBLoadDamageShieldTypes(sp, iMaxSpellID); + + return true; + } else { + _log(SPELLS__LOAD_ERR, "Error in FileLoadSPDat query '%s' %s", query, errbuf); + safe_delete_array(query); + return false; + } +#endif + +} + +#endif //from just above GetMaxSpellID(): #if defined(NEW_LoadSPDat) || defined(DB_LoadSPDat) + +void UpdateWindowTitle(char* iNewTitle) { +#ifdef _WINDOWS + char tmp[500]; + if (iNewTitle) { + snprintf(tmp, sizeof(tmp), "%i: %s", ZoneConfig::get()->ZonePort, iNewTitle); + } + else { + if (zone) { + #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); + #endif + } + else { + #if defined(GOTFRAGS) || defined(_EQDEBUG) + snprintf(tmp, sizeof(tmp), "%i: sleeping, %i", ZoneConfig::get()->ZonePort, getpid()); + #else + snprintf(tmp, sizeof(tmp), "%i: sleeping", ZoneConfig::get()->ZonePort); + #endif + } + } + SetConsoleTitle(tmp); +#endif +} + +/*bool ZoneBootup(uint32 iZoneID, bool iStaticZone) { + const char* zonename = database.GetZoneName(iStaticZone); + if (iZoneID == 0 || zonename == 0) + return false; + if (zone != 0 || ZoneLoaded) { + cerr << "Error: Zone::Bootup call when zone already booted!" << endl; + worldserver.SetZone(0); + return false; + } + numclients = 0; + zone = new Zone(iZoneID, zonename, net.GetZoneAddress(), net.GetZonePort()); + if (!zone->Init(iStaticZone)) { + safe_delete(zone); + cerr << "Zone->Init failed" << endl; + worldserver.SetZone(0); + return false; + } + if (!eqns.Open(net.GetZonePort())) { + safe_delete(zone); + cerr << "NetConnection::Init failed" << endl; + worldserver.SetZone(0); + return false; + } + if (!zone->LoadZoneCFG(zone->GetShortName(), true)) // try loading the zone name... + zone->LoadZoneCFG(zone->GetFileName()); // if that fails, try the file name, then load defaults + + //petition_list.ClearPetitions(); + //petition_list.ReadDatabase(); + ZoneLoaded = true; + worldserver.SetZone(iZoneID); + zone->GetTimeSync(); + cout << "-----------" << endl << "Zone server '" << zonename << "' listening on port:" << net.GetZonePort() << endl << "-----------" << endl; + //entity_list.WriteEntityIDs(); + UpdateWindowTitle(); + return true; +}*/ + +// Original source found at http://www.castaglia.org/proftpd/doc/devel-guide/src/lib/strsep.c.html +char *strsep(char **stringp, const char *delim) +{ + char *res; + + if(!stringp || !*stringp || !**stringp) + return (char*)0; + + res = *stringp; + while(**stringp && !strchr(delim,**stringp)) + (*stringp)++; + + if(**stringp) { + **stringp = '\0'; + (*stringp)++; + } + + return res; +} diff --git a/zone/net.h b/zone/net.h new file mode 100644 index 000000000..9249606a3 --- /dev/null +++ b/zone/net.h @@ -0,0 +1,61 @@ +/* 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 +*/ +#ifdef _WINDOWS + #include + #include +#else + #include + #include + #include + #include + #include +#endif + + +#include +#include +#include "../common/types.h" +#include "../common/timer.h" +void CatchSignal(int); +void UpdateWindowTitle(char* iNewTitle = 0); + +class NetConnection +{ +public: + ~NetConnection(); + NetConnection(); + + uint32 GetIP(); + uint32 GetIP(char* name); + void SaveInfo(char* address, uint32 port, char* waddress,char* filename); + char* GetWorldAddress() { return WorldAddress; } + char* GetZoneAddress() { return ZoneAddress; } + char* GetZoneFileName() { return ZoneFileName; } + uint32 GetZonePort() { return ZonePort; } + Timer object_timer; + Timer door_timer; + Timer corpse_timer; + Timer group_timer; + Timer raid_timer; + Timer trap_timer; +private: + uint16 ZonePort; + char* ZoneAddress; + char* WorldAddress; + char ZoneFileName[50]; +}; diff --git a/zone/npc.cpp b/zone/npc.cpp new file mode 100644 index 000000000..add9e39ff --- /dev/null +++ b/zone/npc.cpp @@ -0,0 +1,2350 @@ +/* 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 "../common/debug.h" +#include +#include +#include +using namespace std; +#include +#include "../common/moremath.h" +#include +#include "../common/packet_dump_file.h" +#include "zone.h" +#ifdef _WINDOWS +#define snprintf _snprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#else +#include +#include +#endif + +#include "npc.h" +#include "map.h" +#include "entity.h" +#include "masterentity.h" +#include "spdat.h" +#include "../common/bodytypes.h" +#include "spawngroup.h" +#include "../common/MiscFunctions.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; +#if !defined(NEW_LoadSPDat) && !defined(DB_LoadSPDat) + extern SPDat_Spell_Struct spells[SPDAT_RECORDS]; +#endif + +#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, + d->max_hp, + d->max_hp, + d->gender, + d->race, + d->class_, + (bodyType)d->bodytype, + d->deity, + d->level, + d->npc_id, + d->size, + d->runspeed, + heading, + x, + y, + z, + d->light, + d->texture, + d->helmtexture, + d->AC, + d->ATK, + d->STR, + d->STA, + d->DEX, + d->AGI, + d->INT, + d->WIS, + d->CHA, + d->haircolor, + d->beardcolor, + d->eyecolor1, + d->eyecolor2, + d->hairstyle, + d->luclinface, + d->beard, + d->drakkin_heritage, + d->drakkin_tattoo, + d->drakkin_details, + (uint32*)d->armor_tint, + 0, + d->see_invis, // pass see_invis/see_ivu flags to mob constructor + d->see_invis_undead, + d->see_hide, + d->see_improved_hide, + d->hp_regen, + d->mana_regen, + d->qglobal, + d->maxlevel, + d->scalerate ), + attacked_timer(CombatEventTimer_expire), + swarm_timer(100), + classattack_timer(1000), + knightattack_timer(1000), + assist_timer(AIassistcheck_delay), + sendhpupdate_timer(1000), + enraged_timer(1000), + taunt_timer(TauntReuseTime * 1000), + qglobal_purge_timer(30000) +{ + //What is the point of this, since the names get mangled.. + Mob* mob = entity_list.GetMob(name); + if(mob != 0) + entity_list.RemoveEntity(mob->GetID()); + + int moblevel=GetLevel(); + + NPCTypedata = d; + NPCTypedata_ours = NULL; + respawn2 = in_respawn; + swarm_timer.Disable(); + + taunting = false; + proximity = NULL; + copper = 0; + silver = 0; + gold = 0; + platinum = 0; + max_dmg = d->max_dmg; + min_dmg = d->min_dmg; + attack_count = d->attack_count; + grid = 0; + wp_m = 0; + max_wp=0; + save_wp = 0; + spawn_group = 0; + swarmInfoPtr = NULL; + spellscale = d->spellscale; + healscale = d->healscale; + + logging_enabled = NPC_DEFAULT_LOGGING_ENABLED; + + pAggroRange = d->aggroradius; + pAssistRange = GetAggroRange(); + findable = d->findable; + trackable = d->trackable; + + MR = d->MR; + CR = d->CR; + DR = d->DR; + FR = d->FR; + PR = d->PR; + Corrup = d->Corrup; + + STR = d->STR; + STA = d->STA; + AGI = d->AGI; + DEX = d->DEX; + INT = d->INT; + WIS = d->WIS; + CHA = d->CHA; + npc_mana = d->Mana; + + //quick fix of ordering if they screwed it up in the DB + if(max_dmg < min_dmg) { + int tmp = min_dmg; + min_dmg = max_dmg; + max_dmg = tmp; + } + + // Max Level and Stat Scaling if maxlevel is set + if(maxlevel > level) + { + LevelScale(); + } + + // Set Resists if they are 0 in the DB + CalcNPCResists(); + + // Set Mana and HP Regen Rates if they are 0 in the DB + CalcNPCRegen(); + + // Set Min and Max Damage if they are 0 in the DB + if(max_dmg == 0){ + CalcNPCDamage(); + } + + accuracy_rating = d->accuracy_rating; + ATK = d->ATK; + + CalcMaxMana(); + SetMana(GetMaxMana()); + + MerchantType = d->merchanttype; + adventure_template_id = d->adventure_template; + org_x = x; + org_y = y; + org_z = z; + flymode = iflymode; + guard_x = -1; //just some value we might be able to recongize as "unset" + guard_y = -1; + guard_z = -1; + guard_heading = 0; + guard_anim = eaStanding; + roambox_distance = 0; + roambox_max_x = -2; + roambox_max_y = -2; + roambox_min_x = -2; + roambox_min_y = -2; + roambox_movingto_x = -2; + roambox_movingto_y = -2; + roambox_delay = 1000; + org_heading = heading; + p_depop = false; + loottable_id = d->loottable_id; + + primary_faction = 0; + SetNPCFactionID(d->npc_faction_id); + + npc_spells_id = 0; + HasAISpell = false; + + if(GetClass() == MERCERNARY_MASTER && RuleB(Mercs, AllowMercs)) + { + LoadMercTypes(); + LoadMercs(); + } + + SpellFocusDMG = 0; + SpellFocusHeal = 0; + + pet_spell_id = 0; + + delaytimer = false; + combat_event = false; + attack_speed = d->attack_speed; + slow_mitigation = d->slow_mitigation; + + EntityList::RemoveNumbers(name); + entity_list.MakeNameUnique(name); + + npc_aggro = d->npc_aggro; + + AI_Start(); + + d_meele_texture1 = d->d_meele_texture1; + d_meele_texture2 = d->d_meele_texture2; + memset(equipment, 0, sizeof(equipment)); + prim_melee_type = d->prim_melee_type; + sec_melee_type = d->sec_melee_type; + + // If Melee Textures are not set, set attack type to Hand to Hand as default + if(!d_meele_texture1) + prim_melee_type = 28; + if(!d_meele_texture2) + sec_melee_type = 28; + + //give NPCs skill values... + int r; + for(r = 0; r <= HIGHEST_SKILL; r++) { + skills[r] = database.GetSkillCap(GetClass(),(SkillType)r,moblevel); + } + + if(d->trap_template > 0) + { + std::map >::iterator trap_ent_iter; + std::list trap_list; + + trap_ent_iter = zone->ldon_trap_entry_list.find(d->trap_template); + if(trap_ent_iter != zone->ldon_trap_entry_list.end()) + { + trap_list = trap_ent_iter->second; + if(trap_list.size() > 0) + { + uint16 count = MakeRandomInt(0, (trap_list.size()-1)); + std::list::iterator trap_list_iter = trap_list.begin(); + for(int x = 0; x < count; ++x) + { + trap_list_iter++; + } + LDoNTrapTemplate* tt = (*trap_list_iter); + if(tt) + { + if((uint8)tt->spell_id > 0) + { + ldon_trapped = true; + ldon_spell_id = tt->spell_id; + } + else + { + ldon_trapped = false; + ldon_spell_id = 0; + } + + ldon_trap_type = (uint8)tt->type; + if(tt->locked > 0) + { + ldon_locked = true; + ldon_locked_skill = tt->skill; + } + else + { + ldon_locked = false; + ldon_locked_skill = 0; + } + ldon_trap_detected = 0; + } + } + else + { + ldon_trapped = false; + ldon_trap_type = 0; + ldon_spell_id = 0; + ldon_locked = false; + ldon_locked_skill = 0; + ldon_trap_detected = 0; + } + } + else + { + ldon_trapped = false; + ldon_trap_type = 0; + ldon_spell_id = 0; + ldon_locked = false; + ldon_locked_skill = 0; + ldon_trap_detected = 0; + } + } + else + { + ldon_trapped = false; + ldon_trap_type = 0; + ldon_spell_id = 0; + ldon_locked = false; + ldon_locked_skill = 0; + ldon_trap_detected = 0; + } + reface_timer = new Timer(15000); + reface_timer->Disable(); + qGlobals = NULL; + guard_x_saved = 0; + guard_y_saved = 0; + guard_z_saved = 0; + guard_heading_saved = 0; + InitializeBuffSlots(); + CalcBonuses(); +} + +NPC::~NPC() +{ + ClearQuestLists(); + entity_list.RemoveNPC(GetID()); + AI_Stop(); + + if(proximity != NULL) { + entity_list.RemoveProximity(GetID()); + safe_delete(proximity); + } + + //clear our spawn limit record if we had one. + entity_list.LimitRemoveNPC(this); + + safe_delete(NPCTypedata_ours); + + { + ItemList::iterator cur,end; + cur = itemlist.begin(); + end = itemlist.end(); + for(; cur != end; cur++) { + ServerLootItem_Struct* item = *cur; + safe_delete(item); + } + itemlist.clear(); + } + + { + list::iterator cur,end; + cur = faction_list.begin(); + end = faction_list.end(); + for(; cur != end; cur++) { + struct NPCFaction* fac = *cur; + safe_delete(fac); + } + faction_list.clear(); + } + + safe_delete(reface_timer); + safe_delete(swarmInfoPtr); + safe_delete(qGlobals); + UninitializeBuffSlots(); +} + +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)) { + Mob *targ = entity_list.GetMob(GetSwarmInfo()->target); + if(targ != mob){ + return; + } + } + + if (mob) { + SetAttackTimer(); + } else { + ranged_timer.Disable(); + //attack_timer.Disable(); + attack_dw_timer.Disable(); + } + Mob::SetTarget(mob); +} + +ServerLootItem_Struct* NPC::GetItem(int slot_id) { + ItemList::iterator cur,end; + cur = itemlist.begin(); + end = itemlist.end(); + for(; cur != end; cur++) { + ServerLootItem_Struct* item = *cur; + if (item->equipSlot == slot_id) { + return item; + } + } + return(NULL); +} + +void NPC::RemoveItem(uint32 item_id, uint16 quantity, uint16 slot) { + ItemList::iterator cur,end; + cur = itemlist.begin(); + end = itemlist.end(); + for(; cur != end; cur++) { + ServerLootItem_Struct* item = *cur; + if (item->item_id == item_id && slot <= 0 && quantity <= 0) { + itemlist.erase(cur); + return; + } + else if (item->item_id == item_id && item->equipSlot == slot && quantity >= 1) { + //cout<<"NPC::RemoveItem"<<" equipSlot:"<equipSlot<<" quantity:"<< quantity<charges <= quantity) + itemlist.erase(cur); + else + item->charges -= quantity; + return; + } + } +} + +void NPC::CheckMinMaxLevel(Mob *them) +{ + if(them == NULL || !them->IsClient()) + return; + + uint16 themlevel = them->GetLevel(); + uint8 material; + + list::iterator cur = itemlist.begin(); + while(cur != itemlist.end()) + { + if(!(*cur)) + return; + + if(themlevel < (*cur)->minlevel || themlevel > (*cur)->maxlevel) + { + material = Inventory::CalcMaterialFromSlot((*cur)->equipSlot); + if(material != 0xFF) + SendWearChange(material); + + cur = itemlist.erase(cur); + continue; + } + cur++; + } + +} + +void NPC::ClearItemList() { + ItemList::iterator cur,end; + cur = itemlist.begin(); + end = itemlist.end(); + for(; cur != end; cur++) { + ServerLootItem_Struct* item = *cur; + safe_delete(item); + } + itemlist.clear(); +} + +void NPC::QueryLoot(Client* to) { + int x = 0; + to->Message(0, "Coin: %ip %ig %is %ic", platinum, gold, silver, copper); + + ItemList::iterator cur,end; + cur = itemlist.begin(); + end = itemlist.end(); + for(; cur != end; cur++) { + const Item_Struct* item = database.GetItem((*cur)->item_id); + 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); + } + 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); + } + 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); + } + else + LogFile->write(EQEMuLog::Error, "Database error, invalid item"); + x++; + } + to->Message(0, "%i items on %s.", x, GetName()); +} + +void NPC::AddCash(uint16 in_copper, uint16 in_silver, uint16 in_gold, uint16 in_platinum) { + if(in_copper >= 0) + copper = in_copper; + else + copper = 0; + + if(in_silver >= 0) + silver = in_silver; + else + silver = 0; + + if(in_gold >= 0) + gold = in_gold; + else + gold = 0; + + if(in_platinum >= 0) + platinum = in_platinum; + else + platinum = 0; +} + +void NPC::AddCash() { + copper = MakeRandomInt(1, 100); + silver = MakeRandomInt(1, 50); + gold = MakeRandomInt(1, 10); + platinum = MakeRandomInt(1, 5); +} + +void NPC::RemoveCash() { + copper = 0; + silver = 0; + gold = 0; + platinum = 0; +} + +bool NPC::Process() +{ + _ZP(NPC_Process); + + adverrorinfo = 1; + if (IsStunned() && stunned_timer.Check()) + { + this->stunned = false; + this->stunned_timer.Disable(); + this->spun_timer.Disable(); + } + + if (p_depop) + { + Mob* owner = entity_list.GetMob(this->ownerid); + if (owner != 0) + { + //if(GetBodyType() != BT_SwarmPet) + // owner->SetPetID(0); + this->ownerid = 0; + this->petid = 0; + } + return false; + } + + adverrorinfo = 2; + + SpellProcess(); + + if(tic_timer.Check()) + { + BuffProcess(); + + if(curfp) + ProcessFlee(); + + uint32 bonus = 0; + + if(GetAppearance() == eaSitting) + bonus+=3; + + int32 OOCRegen = 0; + if(oocregen > 0){ //should pull from Mob class + OOCRegen += GetMaxHP() * oocregen / 100; + } + //Lieka Edit: Fixing NPC regen. NPCs should regen to full during a set duration, not based on their HPs. Increase NPC's HPs by % of total HPs / tick. + if((GetHP() < GetMaxHP()) && !IsPet()) { + if(!IsEngaged()) {//NPC out of combat + if(hp_regen > OOCRegen) + SetHP(GetHP() + hp_regen); + else + SetHP(GetHP() + OOCRegen); + } else + SetHP(GetHP()+hp_regen); + } else if(GetHP() < GetMaxHP() && GetOwnerID() !=0) { + if(!IsEngaged()) //pet + SetHP(GetHP()+hp_regen+bonus+(GetLevel()/5)); + else + SetHP(GetHP()+hp_regen+bonus); + } else + SetHP(GetHP()+hp_regen); + + if(GetMana() < GetMaxMana()) { + SetMana(GetMana()+mana_regen+bonus); + } + + + if(zone->adv_data && !p_depop) + { + ServerZoneAdventureDataReply_Struct* ds = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; + if(ds->type == Adventure_Rescue && ds->data_id == GetNPCTypeID()) + { + Mob *o = GetOwner(); + if(o && o->IsClient()) + { + float x_diff = ds->dest_x - GetX(); + float y_diff = ds->dest_y - GetY(); + float z_diff = ds->dest_z - GetZ(); + float dist = ((x_diff * x_diff) + (y_diff * y_diff) + (z_diff * z_diff)); + if(dist < RuleR(Adventure, DistanceForRescueComplete)) + { + zone->DoAdventureCountIncrease(); + Say("You don't know what this means to me. Thank you so much for finding and saving me from" + " this wretched place. I'll find my way from here."); + Depop(); + } + } + } + } + } + + if (sendhpupdate_timer.Check() && (IsTargeted() || (IsPet() && GetOwner() && GetOwner()->IsClient()))) { + if(!IsFullHP || cur_hp 999) + viral_timer_counter = 0; + } + + if(spellbonuses.GravityEffect == 1) { + if(gravity_timer.Check()) + DoGravityEffect(); + } + + if(reface_timer->Check() && !IsEngaged() && (guard_x == GetX() && guard_y == GetY() && guard_z == GetZ())) { + SetHeading(guard_heading); + SendPosition(); + reface_timer->Disable(); + } + + if (IsMezzed()) + return true; + + if(IsStunned()) { + if(spun_timer.Check()) + Spin(); + return true; + } + + if (enraged_timer.Check()){ + ProcessEnrage(); + } + + //Handle assists... + if(assist_timer.Check() && IsEngaged() && !Charmed()) { + entity_list.AIYellForHelp(this, GetTarget()); + } + + if(qGlobals) + { + if(qglobal_purge_timer.Check()) + { + qGlobals->PurgeExpiredGlobals(); + } + } + + AI_Process(); + + return true; +} + +uint32 NPC::CountLoot() { + return(itemlist.size()); +} + +void NPC::DumpLoot(uint32 npcdump_index, ZSDump_NPC_Loot* npclootdump, uint32* NPCLootindex) { + ItemList::iterator cur,end; + cur = itemlist.begin(); + end = itemlist.end(); + for(; cur != end; cur++) { + ServerLootItem_Struct* item = *cur; + npclootdump[*NPCLootindex].npc_dump_index = npcdump_index; + npclootdump[*NPCLootindex].itemid = item->item_id; + npclootdump[*NPCLootindex].charges = item->charges; + npclootdump[*NPCLootindex].equipSlot = item->equipSlot; + npclootdump[*NPCLootindex].minlevel = item->minlevel; + npclootdump[*NPCLootindex].maxlevel = item->maxlevel; + (*NPCLootindex)++; + } + ClearItemList(); +} + +void NPC::Depop(bool StartSpawnTimer) { + uint16 emoteid = this->GetNPCEmoteID(); + if(emoteid != 0) + this->DoNPCEmote(ONDESPAWN,emoteid); + p_depop = true; + if (StartSpawnTimer) { + if (respawn2 != 0) { + respawn2->DeathReset(); + } + } +} + +bool NPC::DatabaseCastAccepted(int spell_id) { + for (int i=0; i < 12; i++) { + switch(spells[spell_id].effectid[i]) { + case SE_Stamina: { + if(IsEngaged() && GetHPRatio() < 100) + return true; + else + return false; + break; + } + case SE_CurrentHPOnce: + case SE_CurrentHP: { + if(this->GetHPRatio() < 100 && spells[spell_id].buffduration == 0) + return true; + else + return false; + break; + } + + case SE_HealOverTime: { + if(this->GetHPRatio() < 100) + return true; + else + return false; + break; + } + case SE_DamageShield: { + return true; + } + case SE_NecPet: + case SE_SummonPet: { + if(GetPet()){ +#ifdef SPELLQUEUE + printf("%s: Attempted to make a second pet, denied.\n",GetName()); +#endif + return false; + } + break; + } + case SE_LocateCorpse: + case SE_SummonCorpse: { + return false; //Pfft, npcs don't need to summon corpses/locate corpses! + break; + } + default: + if(spells[spell_id].goodEffect == 1 && !(spells[spell_id].buffduration == 0 && this->GetHPRatio() == 100) && !IsEngaged()) + return true; + return false; + } + } + return false; +} + +NPC* NPC::SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z, float in_heading, Client* client) { + if(spawncommand == 0 || spawncommand[0] == 0) { + return 0; + } + else { + Seperator sep(spawncommand); + //Lets see if someone didn't fill out the whole #spawn function properly + if (!sep.IsNumber(1)) + sprintf(sep.arg[1],"1"); + if (!sep.IsNumber(2)) + sprintf(sep.arg[2],"1"); + if (!sep.IsNumber(3)) + sprintf(sep.arg[3],"0"); + if (atoi(sep.arg[4]) > 2100000000 || atoi(sep.arg[4]) <= 0) + sprintf(sep.arg[4]," "); + if (!strcmp(sep.arg[5],"-")) + sprintf(sep.arg[5]," "); + if (!sep.IsNumber(5)) + sprintf(sep.arg[5]," "); + if (!sep.IsNumber(6)) + sprintf(sep.arg[6],"1"); + if (!sep.IsNumber(8)) + sprintf(sep.arg[8],"0"); + if (!sep.IsNumber(9)) + sprintf(sep.arg[9], "0"); + if (!sep.IsNumber(7)) + sprintf(sep.arg[7],"0"); + if (!strcmp(sep.arg[4],"-")) + sprintf(sep.arg[4]," "); + if (!sep.IsNumber(10)) // bodytype + sprintf(sep.arg[10], "0"); + //Calc MaxHP if client neglected to enter it... + if (!sep.IsNumber(4)) { + //Stolen from Client::GetMaxHP... + uint8 multiplier = 0; + int tmplevel = atoi(sep.arg[2]); + switch(atoi(sep.arg[5])) + { + case WARRIOR: + if (tmplevel < 20) + multiplier = 22; + else if (tmplevel < 30) + multiplier = 23; + else if (tmplevel < 40) + multiplier = 25; + else if (tmplevel < 53) + multiplier = 27; + else if (tmplevel < 57) + multiplier = 28; + else + multiplier = 30; + break; + + case DRUID: + case CLERIC: + case SHAMAN: + multiplier = 15; + break; + + case PALADIN: + case SHADOWKNIGHT: + if (tmplevel < 35) + multiplier = 21; + else if (tmplevel < 45) + multiplier = 22; + else if (tmplevel < 51) + multiplier = 23; + else if (tmplevel < 56) + multiplier = 24; + else if (tmplevel < 60) + multiplier = 25; + else + multiplier = 26; + break; + + case MONK: + case BARD: + case ROGUE: + //case BEASTLORD: + if (tmplevel < 51) + multiplier = 18; + else if (tmplevel < 58) + multiplier = 19; + else + multiplier = 20; + break; + + case RANGER: + if (tmplevel < 58) + multiplier = 20; + else + multiplier = 21; + break; + + case MAGICIAN: + case WIZARD: + case NECROMANCER: + case ENCHANTER: + multiplier = 12; + break; + + default: + if (tmplevel < 35) + multiplier = 21; + else if (tmplevel < 45) + multiplier = 22; + else if (tmplevel < 51) + multiplier = 23; + else if (tmplevel < 56) + multiplier = 24; + else if (tmplevel < 60) + multiplier = 25; + else + multiplier = 26; + break; + } + sprintf(sep.arg[4],"%i",5+multiplier*atoi(sep.arg[2])+multiplier*atoi(sep.arg[2])*75/300); + } + + // Autoselect NPC Gender + if (sep.arg[5][0] == 0) { + sprintf(sep.arg[5], "%i", (int) Mob::GetDefaultGender(atoi(sep.arg[1]))); + } + + //Time to create the NPC!! + NPCType* npc_type = new NPCType; + memset(npc_type, 0, sizeof(NPCType)); + + strncpy(npc_type->name, sep.arg[0], 60); + npc_type->cur_hp = atoi(sep.arg[4]); + npc_type->max_hp = atoi(sep.arg[4]); + npc_type->race = atoi(sep.arg[1]); + npc_type->gender = atoi(sep.arg[5]); + npc_type->class_ = atoi(sep.arg[6]); + npc_type->deity = 1; + npc_type->level = atoi(sep.arg[2]); + npc_type->npc_id = 0; + npc_type->loottable_id = 0; + npc_type->texture = atoi(sep.arg[3]); + npc_type->light = 0; + npc_type->runspeed = 1.25; + npc_type->d_meele_texture1 = atoi(sep.arg[7]); + npc_type->d_meele_texture2 = atoi(sep.arg[8]); + npc_type->merchanttype = atoi(sep.arg[9]); + npc_type->bodytype = atoi(sep.arg[10]); + + npc_type->STR = 150; + npc_type->STA = 150; + npc_type->DEX = 150; + npc_type->AGI = 150; + npc_type->INT = 150; + npc_type->WIS = 150; + npc_type->CHA = 150; + + npc_type->prim_melee_type = 28; + npc_type->sec_melee_type = 28; + + NPC* npc = new NPC(npc_type, 0, in_x, in_y, in_z, in_heading/8, FlyMode3); + npc->GiveNPCTypeData(npc_type); + + entity_list.AddNPC(npc); + + if (client) { + // Notify client of spawn data + client->Message(0, "New spawn:"); + client->Message(0, "Name: %s", npc->name); + client->Message(0, "Race: %u", npc->race); + client->Message(0, "Level: %u", npc->level); + client->Message(0, "Material: %u", npc->texture); + client->Message(0, "Current/Max HP: %i", npc->max_hp); + client->Message(0, "Gender: %u", npc->gender); + client->Message(0, "Class: %u", npc->class_); + client->Message(0, "Weapon Item Number: %u/%u", npc->d_meele_texture1, npc->d_meele_texture2); + client->Message(0, "MerchantID: %u", npc->MerchantType); + client->Message(0, "Bodytype: %u", npc->bodytype); + } + + return npc; + } +} + +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 last_insert_id = 0; + 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 NULL - 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)) { + 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)) { + 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)) { + 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)) { + 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)) { + safe_delete(query); + return false; + } + if(c) c->LogSQL(query); + safe_delete_array(query); + return true; + break; + } + 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)) { + printf("ReturnFalse: spawngroup query in NPCSpawnDB() (query: %s)\n",query); + 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)) { + safe_delete(query); + printf("ReturnFalse: spawn2 query in NPCSpawnDB()\n"); + 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)) { + safe_delete(query); + printf("ReturnFalse: spawnentry query in NPCSpawnDB()\n"); + return false; + } + if(c) c->LogSQL(query); + safe_delete_array(query); + return spawnid; + break; + } + 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; + } + 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 == NULL) 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; + } + 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 == NULL) 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; + } + 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; + } + 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 false; +} + +int32 NPC::GetEquipmentMaterial(uint8 material_slot) const +{ + if (material_slot >= MAX_MATERIALS) + return 0; + + int inv_slot = Inventory::CalcSlotFromMaterial(material_slot); + if (inv_slot == -1) + return 0; + if(equipment[inv_slot] == 0) { + switch(material_slot) { + case MATERIAL_HEAD: + return helmtexture; + case MATERIAL_CHEST: + return texture; + case MATERIAL_PRIMARY: + return d_meele_texture1; + case MATERIAL_SECONDARY: + return d_meele_texture2; + default: + //they have nothing in the slot, and its not a special slot... they get nothing. + return(0); + } + } + + //they have some loot item in this slot, pass it up to the default handler + return(Mob::GetEquipmentMaterial(material_slot)); +} + +uint32 NPC::GetMaxDamage(uint8 tlevel) +{ + uint32 dmg = 0; + if (tlevel < 40) + dmg = tlevel*2+2; + else if (tlevel < 50) + dmg = level*25/10+2; + else if (tlevel < 60) + dmg = (tlevel*3+2)+((tlevel-50)*30); + else + dmg = (tlevel*3+2)+((tlevel-50)*35); + return dmg; +} + +void NPC::PickPocket(Client* thief) { + + thief->CheckIncreaseSkill(PICK_POCKETS, NULL, 5); + + //make sure were allowed to targte them: + int olevel = GetLevel(); + if(olevel > (thief->GetLevel() + THIEF_PICKPOCKET_OVER)) { + thief->Message(13, "You are too inexperienced to pick pocket this target"); + thief->SendPickPocketResponse(this, 0, PickPocketFailed); + //should we check aggro + return; + } + + if(MakeRandomInt(0, 100) > 95){ + AddToHateList(thief, 50); + Say("Stop thief!"); + thief->Message(13, "You are noticed trying to steal!"); + thief->SendPickPocketResponse(this, 0, PickPocketFailed); + return; + } + + int steal_skill = thief->GetSkill(PICK_POCKETS); + int stealchance = steal_skill*100/(5*olevel+5); + ItemInst* inst = 0; + int x = 0; + int slot[50]; + int steal_items[50]; + int charges[50]; + int money[4]; + money[0] = GetPlatinum(); + money[1] = GetGold(); + money[2] = GetSilver(); + money[3] = GetCopper(); + if (steal_skill < 125) + money[0] = 0; + if (steal_skill < 60) + money[1] = 0; + memset(slot,0,50); + memset(steal_items,0,50); + memset(charges,0,50); + //Determine wheter to steal money or an item. + bool no_coin = ((money[0] + money[1] + money[2] + money[3]) == 0); + bool steal_item = (MakeRandomInt(0, 99) < 50 || no_coin); + if (steal_item) + { + ItemList::iterator cur,end; + cur = itemlist.begin(); + end = itemlist.end(); + for(; cur != end && x < 49; cur++) { + ServerLootItem_Struct* citem = *cur; + const Item_Struct* item = database.GetItem(citem->item_id); + if (item) + { + inst = database.CreateItem(item, citem->charges); + bool is_arrow = (item->ItemType == ItemTypeArrow) ? true : false; + int slot_id = thief->GetInv().FindFreeSlot(false, true, inst->GetItem()->Size, is_arrow); + if (/*!Equipped(item->ID) &&*/ + !item->Magic && item->NoDrop != 0 && !inst->IsType(ItemClassContainer) && slot_id != SLOT_INVALID + /*&& steal_skill > item->StealSkill*/ ) + { + slot[x] = slot_id; + steal_items[x] = item->ID; + if (inst->IsStackable()) + charges[x] = 1; + else + charges[x] = citem->charges; + x++; + } + } + } + if (x > 0) + { + int random = MakeRandomInt(0, x-1); + inst = database.CreateItem(steal_items[random], charges[random]); + if (inst) + { + const Item_Struct* item = inst->GetItem(); + if (item) + { + if (/*item->StealSkill || */steal_skill >= stealchance) + { + thief->PutItemInInventory(slot[random], *inst); + thief->SendItemPacket(slot[random], inst, ItemPacketTrade); + RemoveItem(item->ID); + thief->SendPickPocketResponse(this, 0, PickPocketItem, item); + } + else + steal_item = false; + } + else + steal_item = false; + } + else + steal_item = false; + } + else if (!no_coin) + { + steal_item = false; + } + else + { + thief->Message(0, "This target's pockets are empty"); + thief->SendPickPocketResponse(this, 0, PickPocketFailed); + } + } + if (!steal_item) //Steal money + { + uint32 amt = MakeRandomInt(1, (steal_skill/25)+1); + int steal_type = 0; + if (!money[0]) + { + steal_type = 1; + if (!money[1]) + { + steal_type = 2; + if (!money[2]) + { + steal_type = 3; + } + } + } + + if (MakeRandomInt(0, 100) <= stealchance) + { + switch (steal_type) + { + case 0:{ + if (amt > GetPlatinum()) + amt = GetPlatinum(); + SetPlatinum(GetPlatinum()-amt); + thief->AddMoneyToPP(0,0,0,amt,false); + thief->SendPickPocketResponse(this, amt, PickPocketPlatinum); + break; + } + case 1:{ + if (amt > GetGold()) + amt = GetGold(); + SetGold(GetGold()-amt); + thief->AddMoneyToPP(0,0,amt,0,false); + thief->SendPickPocketResponse(this, amt, PickPocketGold); + break; + } + case 2:{ + if (amt > GetSilver()) + amt = GetSilver(); + SetSilver(GetSilver()-amt); + thief->AddMoneyToPP(0,amt,0,0,false); + thief->SendPickPocketResponse(this, amt, PickPocketSilver); + break; + } + case 3:{ + if (amt > GetCopper()) + amt = GetCopper(); + SetCopper(GetCopper()-amt); + thief->AddMoneyToPP(amt,0,0,0,false); + thief->SendPickPocketResponse(this, amt, PickPocketCopper); + break; + } + } + } + else + { + thief->SendPickPocketResponse(this, 0, PickPocketFailed); + } + } + safe_delete(inst); +} + +void Mob::NPCSpecialAttacks(const char* parse, int permtag, bool reset, bool remove) { + if(reset) + { + for(int i = 0; i < SPECATK_MAXNUM; i++) + { + SpecAttacks[i] = false; + safe_delete(SpecAttackTimers[i]); + } + } + + const char* orig_parse = parse; + while (*parse) + { + switch(*parse) + { + case 'E': + SpecAttacks[SPECATK_ENRAGE] = (remove ? false : true); + break; + case 'F': + SpecAttacks[SPECATK_FLURRY] = (remove ? false : true); + break; + case 'R': + SpecAttacks[SPECATK_RAMPAGE] = (remove ? false : true); + break; + case 'r': + SpecAttacks[SPECATK_AREA_RAMPAGE] = (remove ? false : true); + break; + case 'S': + if(remove) { + SpecAttacks[SPECATK_SUMMON] = false; + safe_delete(SpecAttackTimers[SPECATK_SUMMON]); + } else { + SpecAttacks[SPECATK_SUMMON] = true; + safe_delete(SpecAttackTimers[SPECATK_SUMMON]); + SpecAttackTimers[SPECATK_SUMMON] = new Timer(6000); + SpecAttackTimers[SPECATK_SUMMON]->Start(); + } + break; + case 'T': + SpecAttacks[SPECATK_TRIPLE] = (remove ? false : true); + break; + case 'Q': + //quad requires triple to work properly + if(remove) { + SpecAttacks[SPECATK_QUAD] = false; + } else { + SpecAttacks[SPECATK_TRIPLE] = true; + SpecAttacks[SPECATK_QUAD] = true; + } + break; + case 'b': + SpecAttacks[SPECATK_BANE] = (remove ? false : true); + break; + case 'm': + SpecAttacks[SPECATK_MAGICAL] = (remove ? false : true); + break; + case 'U': + SpecAttacks[UNSLOWABLE] = (remove ? false : true); + break; + case 'M': + SpecAttacks[UNMEZABLE] = (remove ? false : true); + break; + case 'C': + SpecAttacks[UNCHARMABLE] = (remove ? false : true); + break; + case 'N': + SpecAttacks[UNSTUNABLE] = (remove ? false : true); + break; + case 'I': + SpecAttacks[UNSNAREABLE] = (remove ? false : true); + break; + case 'D': + SpecAttacks[UNFEARABLE] = (remove ? false : true); + break; + case 'K': + SpecAttacks[UNDISPELLABLE] = (remove ? false : true); + break; + case 'A': + SpecAttacks[IMMUNE_MELEE] = (remove ? false : true); + break; + case 'B': + SpecAttacks[IMMUNE_MAGIC] = (remove ? false : true); + break; + case 'f': + SpecAttacks[IMMUNE_FLEEING] = (remove ? false : true); + break; + case 'O': + SpecAttacks[IMMUNE_MELEE_EXCEPT_BANE] = (remove ? false : true); + break; + case 'W': + SpecAttacks[IMMUNE_MELEE_NONMAGICAL] = (remove ? false : true); + break; + case 'H': + SpecAttacks[IMMUNE_AGGRO] = (remove ? false : true); + break; + case 'G': + SpecAttacks[IMMUNE_AGGRO_ON] = (remove ? false : true); + break; + case 'g': + SpecAttacks[IMMUNE_CASTING_FROM_RANGE] = (remove ? false : true); + break; + case 'd': + SpecAttacks[IMMUNE_FEIGN_DEATH] = (remove ? false : true); + break; + case 'Y': + SpecAttacks[SPECATK_RANGED_ATK] = (remove ? false : true); + break; + case 'L': + SpecAttacks[SPECATK_INNATE_DW] = (remove ? false : true); + break; + case 't': + SpecAttacks[NPC_TUNNELVISION] = (remove ? false : true); + break; + case 'n': + SpecAttacks[NPC_NO_BUFFHEAL_FRIENDS] = (remove ? false : true); + break; + case 'p': + SpecAttacks[IMMUNE_PACIFY] = (remove ? false : true); + break; + case 'J': + SpecAttacks[LEASH] = (remove ? false : true); + break; + case 'j': + SpecAttacks[TETHER] = (remove ? false : true); + break; + case 'o': + SpecAttacks[DESTRUCTIBLE_OBJECT] = (remove ? false : true); + SetDestructibleObject(true); + break; + case 'Z': + SpecAttacks[NO_HARM_FROM_CLIENT] = (remove ? false : true); + break; + case 'i': + SpecAttacks[IMMUNE_TAUNT] = (remove ? false : true); + break; + + default: + break; + } + parse++; + } + + if(permtag == 1 && this->GetNPCTypeID() > 0) + { + if(database.SetSpecialAttkFlag(this->GetNPCTypeID(), orig_parse)) + { + LogFile->write(EQEMuLog::Normal, "NPCTypeID: %i flagged to '%s' for Special Attacks.\n",this->GetNPCTypeID(),orig_parse); + } + } +} + +bool Mob::HasNPCSpecialAtk(const char* parse) { + + bool HasAllAttacks = true; + + while (*parse && HasAllAttacks == true) + { + switch(*parse) + { + case 'E': + if (!SpecAttacks[SPECATK_ENRAGE]) + HasAllAttacks = false; + break; + case 'F': + if (!SpecAttacks[SPECATK_FLURRY]) + HasAllAttacks = false; + break; + case 'R': + if (!SpecAttacks[SPECATK_RAMPAGE]) + HasAllAttacks = false; + break; + case 'r': + if (!SpecAttacks[SPECATK_AREA_RAMPAGE]) + HasAllAttacks = false; + break; + case 'S': + if (!SpecAttacks[SPECATK_SUMMON]) + HasAllAttacks = false; + break; + case 'T': + if (!SpecAttacks[SPECATK_TRIPLE]) + HasAllAttacks = false; + break; + case 'Q': + if (!SpecAttacks[SPECATK_QUAD]) + HasAllAttacks = false; + break; + case 'b': + if (!SpecAttacks[SPECATK_BANE]) + HasAllAttacks = false; + break; + case 'm': + if (!SpecAttacks[SPECATK_MAGICAL]) + HasAllAttacks = false; + break; + case 'U': + if (!SpecAttacks[UNSLOWABLE]) + HasAllAttacks = false; + break; + case 'M': + if (!SpecAttacks[UNMEZABLE]) + HasAllAttacks = false; + break; + case 'C': + if (!SpecAttacks[UNCHARMABLE]) + HasAllAttacks = false; + break; + case 'N': + if (!SpecAttacks[UNSTUNABLE]) + HasAllAttacks = false; + break; + case 'I': + if (!SpecAttacks[UNSNAREABLE]) + HasAllAttacks = false; + break; + case 'D': + if (!SpecAttacks[UNFEARABLE]) + HasAllAttacks = false; + break; + case 'A': + if (!SpecAttacks[IMMUNE_MELEE]) + HasAllAttacks = false; + break; + case 'B': + if (!SpecAttacks[IMMUNE_MAGIC]) + HasAllAttacks = false; + break; + case 'f': + if (!SpecAttacks[IMMUNE_FLEEING]) + HasAllAttacks = false; + break; + case 'O': + if (!SpecAttacks[IMMUNE_MELEE_EXCEPT_BANE]) + HasAllAttacks = false; + break; + case 'W': + if (!SpecAttacks[IMMUNE_MELEE_NONMAGICAL]) + HasAllAttacks = false; + break; + case 'H': + if (!SpecAttacks[IMMUNE_AGGRO]) + HasAllAttacks = false; + break; + case 'G': + if (!SpecAttacks[IMMUNE_AGGRO_ON]) + HasAllAttacks = false; + break; + case 'g': + if (!SpecAttacks[IMMUNE_CASTING_FROM_RANGE]) + HasAllAttacks = false; + break; + case 'd': + if (!SpecAttacks[IMMUNE_FEIGN_DEATH]) + HasAllAttacks = false; + break; + case 'Y': + if (!SpecAttacks[SPECATK_RANGED_ATK]) + HasAllAttacks = false; + break; + case 'L': + if (!SpecAttacks[SPECATK_INNATE_DW]) + HasAllAttacks = false; + break; + case 't': + if (!SpecAttacks[NPC_TUNNELVISION]) + HasAllAttacks = false; + break; + case 'n': + if (!SpecAttacks[NPC_NO_BUFFHEAL_FRIENDS]) + HasAllAttacks = false; + break; + case 'p': + if(!SpecAttacks[IMMUNE_PACIFY]) + HasAllAttacks = false; + break; + case 'J': + if(!SpecAttacks[LEASH]) + HasAllAttacks = false; + break; + case 'j': + if(!SpecAttacks[TETHER]) + HasAllAttacks = false; + break; + case 'o': + if(!SpecAttacks[DESTRUCTIBLE_OBJECT]) + { + HasAllAttacks = false; + SetDestructibleObject(false); + } + break; + case 'Z': + if(!SpecAttacks[NO_HARM_FROM_CLIENT]){ + HasAllAttacks = false; + } + break; + + default: + HasAllAttacks = false; + break; + } + parse++; + } + + return HasAllAttacks; +} + +void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) +{ + Mob::FillSpawnStruct(ns, ForWho); + if (RuleB(Pets, UnTargetableSwarmPet)) { + if(GetOwnerID() || GetSwarmOwner()) { + ns->spawn.is_pet = 1; + if (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; + Client *c = entity_list.GetClientByID(GetSwarmOwner()); + if(c) + sprintf(ns->spawn.lastName, "%s's Pet", c->GetName()); + } + } + } else { + if(GetOwnerID()) { + ns->spawn.is_pet = 1; + if (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; +} + +void NPC::SetLevel(uint8 in_level, bool command) +{ + if(in_level > level) + SendLevelAppearance(); + level = in_level; + SendAppearancePacket(AT_WhoLevel, in_level); +} + +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) + { + 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 == "runspeed") + { + runspeed = (float)atof(val.c_str()); + CalcBonuses(); + return; + } + + if(id == "special_attacks") + { + NPCSpecialAttacks(val.c_str(), 0); + 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 == "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 = atof(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; + } +} + +void NPC::LevelScale() { + + uint8 random_level = (MakeRandomInt(level, maxlevel)); + + float scaling = (((random_level / (float)level) - 1) * (scalerate / 100.0f)); + + // Compensate for scale rates at low levels so they don't add too much + uint8 scale_adjust = 1; + if(level > 0 && level <= 5) + scale_adjust = 10; + if(level > 5 && level <= 10) + scale_adjust = 5; + if(level > 10 && level <= 15) + scale_adjust = 3; + if(level > 15 && level <= 25) + scale_adjust = 2; + + base_hp += (int)(base_hp * scaling); + max_hp += (int)(max_hp * scaling); + cur_hp = max_hp; + STR += (int)(STR * scaling / scale_adjust); + STA += (int)(STA * scaling / scale_adjust); + AGI += (int)(AGI * scaling / scale_adjust); + DEX += (int)(DEX * scaling / scale_adjust); + INT += (int)(INT * scaling / scale_adjust); + WIS += (int)(WIS * scaling / scale_adjust); + CHA += (int)(CHA * scaling / scale_adjust); + if (MR) + MR += (int)(MR * scaling / scale_adjust); + if (CR) + CR += (int)(CR * scaling / scale_adjust); + if (DR) + DR += (int)(DR * scaling / scale_adjust); + if (FR) + FR += (int)(FR * scaling / scale_adjust); + if (PR) + PR += (int)(PR * scaling / scale_adjust); + + if (max_dmg) + { + max_dmg += (int)(max_dmg * scaling / scale_adjust); + min_dmg += (int)(min_dmg * scaling / scale_adjust); + } + + level = random_level; + + return; +} + +void NPC::CalcNPCResists() { + + if (!MR) + MR = (GetLevel() * 11)/10; + if (!CR) + CR = (GetLevel() * 11)/10; + if (!DR) + DR = (GetLevel() * 11)/10; + if (!FR) + FR = (GetLevel() * 11)/10; + if (!PR) + PR = (GetLevel() * 11)/10; + if (!Corrup) + Corrup = 15; + return; +} + +void NPC::CalcNPCRegen() { + + // Fix for lazy db-updaters (regen values left at 0) + if (GetCasterClass() != 'N' && mana_regen == 0) + mana_regen = (GetLevel() / 10) + 4; + else if(mana_regen < 0) + mana_regen = 0; + else + mana_regen = mana_regen; + + // Gives low end monsters no regen if set to 0 in database. Should make low end monsters killable + // Might want to lower this to /5 rather than 10. + if(hp_regen == 0) + { + if(GetLevel() <= 6) + hp_regen = 1; + else if(GetLevel() > 6 && GetLevel() <= 10) + hp_regen = 2; + else if(GetLevel() > 10 && GetLevel() <= 15) + hp_regen = 3; + else if(GetLevel() > 15 && GetLevel() <= 20) + hp_regen = 5; + else if(GetLevel() > 20 && GetLevel() <= 30) + hp_regen = 7; + else if(GetLevel() > 30 && GetLevel() <= 35) + hp_regen = 9; + else if(GetLevel() > 35 && GetLevel() <= 40) + hp_regen = 12; + else if(GetLevel() > 40 && GetLevel() <= 45) + hp_regen = 18; + else if(GetLevel() > 45 && GetLevel() <= 50) + hp_regen = 21; + else + hp_regen = 30; + } else if(hp_regen < 0) { + hp_regen = 0; + } else + hp_regen = hp_regen; + + return; +} + +void NPC::CalcNPCDamage() { + + int AC_adjust=12; + + if (GetLevel() >= 66) { + if (min_dmg==0) + min_dmg = 220; + if (max_dmg==0) + max_dmg = ((((99000)*(GetLevel()-64))/400)*AC_adjust/10); + } + else if (GetLevel() >= 60 && GetLevel() <= 65){ + if(min_dmg==0) + min_dmg = (GetLevel()+(GetLevel()/3)); + if(max_dmg==0) + max_dmg = (GetLevel()*3)*AC_adjust/10; + } + else if (GetLevel() >= 51 && GetLevel() <= 59){ + if(min_dmg==0) + min_dmg = (GetLevel()+(GetLevel()/3)); + if(max_dmg==0) + max_dmg = (GetLevel()*3)*AC_adjust/10; + } + else if (GetLevel() >= 40 && GetLevel() <= 50) { + if (min_dmg==0) + min_dmg = GetLevel(); + if(max_dmg==0) + max_dmg = (GetLevel()*3)*AC_adjust/10; + } + else if (GetLevel() >= 28 && GetLevel() <= 39) { + if (min_dmg==0) + min_dmg = GetLevel() / 2; + if (max_dmg==0) + max_dmg = ((GetLevel()*2)+2)*AC_adjust/10; + } + else if (GetLevel() <= 27) { + if (min_dmg==0) + min_dmg=1; + if (max_dmg==0) + max_dmg = (GetLevel()*2)*AC_adjust/10; + } + + int clfact = GetClassLevelFactor(); + min_dmg = (min_dmg * clfact) / 220; + max_dmg = (max_dmg * clfact) / 220; + + return; +} + + +uint32 NPC::GetSpawnPointID() const +{ + if(respawn2) + { + return respawn2->GetID(); + } + return 0; +} + +void NPC::NPCSlotTexture(uint8 slot, uint16 texture) +{ + if (slot == 7) { + d_meele_texture1 = texture; + } + else if (slot == 8) { + d_meele_texture2 = texture; + } + else if (slot < 6) { + // Reserved for texturing individual armor slots + } + return; +} + +uint32 NPC::GetSwarmOwner() +{ + if(GetSwarmInfo() != NULL) + { + return GetSwarmInfo()->owner_id; + } + return 0; +} + +uint32 NPC::GetSwarmTarget() +{ + if(GetSwarmInfo() != NULL) + { + return GetSwarmInfo()->target; + } + return 0; +} + +void NPC::SetSwarmTarget(int target_id) +{ + if(GetSwarmInfo() != NULL) + { + GetSwarmInfo()->target = target_id; + } + return; +} + +int32 NPC::CalcMaxMana() { + if(npc_mana == 0) { + switch (GetCasterClass()) { + case 'I': + max_mana = (((GetINT()/2)+1) * GetLevel()) + spellbonuses.Mana + itembonuses.Mana; + break; + case 'W': + max_mana = (((GetWIS()/2)+1) * GetLevel()) + spellbonuses.Mana + itembonuses.Mana; + break; + case 'N': + default: + max_mana = 0; + break; + } + if (max_mana < 0) { + max_mana = 0; + } + + return max_mana; + } else { + switch (GetCasterClass()) { + case 'I': + max_mana = npc_mana + spellbonuses.Mana + itembonuses.Mana; + break; + case 'W': + max_mana = npc_mana + spellbonuses.Mana + itembonuses.Mana; + break; + case 'N': + default: + max_mana = 0; + break; + } + if (max_mana < 0) { + max_mana = 0; + } + + return max_mana; + } +} + +void NPC::SignalNPC(int _signal_id) +{ + signal_q.push_back(_signal_id); +} + +NPC_Emote_Struct* NPC::GetNPCEmote(uint16 emoteid, uint8 event_) { + LinkedListIterator iterator(zone->NPCEmoteList); + iterator.Reset(); + while(iterator.MoreElements()) + { + NPC_Emote_Struct* nes = iterator.GetData(); + if (emoteid == nes->emoteid && event_ == nes->event_) { + return (nes); + } + iterator.Advance(); + } + return (NULL); +} + +void NPC::DoNPCEmote(uint8 event_, uint16 emoteid) +{ + if(this == NULL || emoteid == 0) + { + return; + } + + NPC_Emote_Struct* nes = GetNPCEmote(emoteid,event_); + if(nes == NULL) + { + return; + } + + if(emoteid == nes->emoteid) + { + if(nes->type == 1) + this->Emote("%s",nes->text); + else if(nes->type == 2) + this->Shout("%s",nes->text); + else if(nes->type == 3) + entity_list.MessageClose_StringID(this, true, 200, 10, GENERIC_STRING, nes->text); + else + this->Say("%s",nes->text); + } +} + +bool NPC::CanTalk() +{ + //Races that should be able to talk. (Races up to Titanium) + + uint16 TalkRace[473] = + {1,2,3,4,5,6,7,8,9,10,11,12,0,0,15,16,0,18,19,20,0,0,23,0,25,0,0,0,0,0,0, + 32,0,0,0,0,0,0,39,40,0,0,0,44,0,0,0,0,49,0,51,0,53,54,55,56,57,58,0,0,0, + 62,0,64,65,66,67,0,0,70,71,0,0,0,0,0,77,78,79,0,81,82,0,0,0,86,0,0,0,90, + 0,92,93,94,95,0,0,98,99,0,101,0,103,0,0,0,0,0,0,110,111,112,0,0,0,0,0,0, + 0,0,0,0,123,0,0,126,0,128,0,130,131,0,0,0,0,136,137,0,139,140,0,0,0,144, + 0,0,0,0,0,150,151,152,153,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,183,184,0,0,187,188,189,0,0,0,0,0,195,196,0,198,0,0,0,202,0, + 0,205,0,0,208,0,0,0,0,0,0,0,0,217,0,219,0,0,0,0,0,0,226,0,0,229,230,0,0, + 0,0,235,236,0,238,239,240,241,242,243,244,0,246,247,0,0,0,251,0,0,254,255, + 256,257,0,0,0,0,0,0,0,0,266,267,0,0,270,271,0,0,0,0,0,277,278,0,0,0,0,283, + 284,0,286,0,288,289,290,0,0,0,0,295,296,297,298,299,300,0,0,0,304,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,320,0,322,323,324,325,0,0,0,0,330,331,332,333,334,335, + 336,337,338,339,340,341,342,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,359,360,361,362, + 0,364,365,366,0,368,369,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,385,386,0,0,0,0,0,392, + 393,394,395,396,397,398,0,400,402,0,0,0,0,406,0,408,0,0,411,0,413,0,0,0,417, + 0,0,420,0,0,0,0,425,0,0,0,0,0,0,0,433,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,458,0,0,0,0,0,0,0,0,467,0,0,470,0,0,473}; + + int talk_check = TalkRace[GetRace() - 1]; + + if (TalkRace[GetRace() - 1] > 0) + return true; + + return false; +} + +void NPC::PrintOutQuestItems(Client* c){ + c->Message(4,"Quest Items currently awaiting completion on %s",GetName()); + + LinkedListIterator iterator(questItems); + iterator.Reset(); + + while(iterator.MoreElements()) + { + c->Message(5,"ItemName: %s (%d) | Charges: %i",iterator.GetData()->GetItem()->Name,iterator.GetData()->GetItem()->ID,iterator.GetData()->GetCharges()); + iterator.Advance(); + } + + c->Message(4,"End of quest items list."); +} \ No newline at end of file diff --git a/zone/npc.h b/zone/npc.h new file mode 100644 index 000000000..30585d1fa --- /dev/null +++ b/zone/npc.h @@ -0,0 +1,562 @@ +/* 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 NPC_H +#define NPC_H + +class NPC; +#include "zonedb.h" +#include "mob.h" +//#include "spawn.h" + +#include +#include +using namespace std; + +#include "spawn2.h" +#include "loottable.h" +#include "zonedump.h" +#include "QGlobals.h" +#include "../common/rulesys.h" + +#ifdef _WINDOWS + #define M_PI 3.141592 +#endif + +#define LEAVECOMBAT 0 +#define ENTERCOMBAT 1 +#define ONDEATH 2 +#define AFTERDEATH 3 +#define HAILED 4 +#define KILLEDPC 5 +#define KILLEDNPC 6 +#define ONSPAWN 7 +#define ONDESPAWN 8 + +typedef struct { + float min_x; + float max_x; + float min_y; + float max_y; + float min_z; + float max_z; + bool say; +} NPCProximity; + +struct AISpells_Struct { + uint16 type; // 0 = never, must be one (and only one) of the defined values + uint16 spellid; // <= 0 = no spell + int16 manacost; // -1 = use spdat, -2 = no cast time + uint32 time_cancast; // when we can cast this spell next + int32 recast_delay; + int16 priority; + int16 resist_adjust; +}; + +class AA_SwarmPetInfo; + +class NPC : public Mob +{ +public: + static NPC* SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z, float in_heading = 0, Client* client = 0); + static int8 GetAILevel(bool iForceReRead = false); + + NPC(const NPCType* data, Spawn2* respawn, float x, float y, float z, float heading, int iflymode, bool IsCorpse = false); + + virtual ~NPC(); + + //abstract virtual function implementations requird by base abstract class + virtual void Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillType attack_skill); + virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false); + virtual bool Attack(Mob* other, int Hand = 13, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false); + virtual bool HasRaid() { return false; } + virtual bool HasGroup() { return false; } + virtual Raid* GetRaid() { return 0; } + virtual Group* GetGroup() { return 0; } + + virtual bool IsNPC() const { return true; } + + virtual bool Process(); + virtual void AI_Init(); + virtual void AI_Start(uint32 iMoveDelay = 0); + virtual void AI_Stop(); + void AI_DoMovement(); + bool AI_AddNPCSpells(uint32 iDBSpellsID); + virtual bool AI_EngagedCastCheck(); + bool AI_HasSpells() { return HasAISpell; } + + virtual bool AI_PursueCastCheck(); + virtual bool AI_IdleCastCheck(); + virtual void AI_Event_SpellCastFinished(bool iCastSucceeded, uint8 slot); + + void LevelScale(); + void CalcNPCResists(); + void CalcNPCRegen(); + void CalcNPCDamage(); + + + int32 GetActSpellDamage(uint16 spell_id, int32 value); + int32 GetActSpellHealing(uint16 spell_id, int32 value); + 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(SkillType skill_num) const { if (skill_num <= HIGHEST_SKILL) { return skills[skill_num]; } return 0; } + + void CalcItemBonuses(StatBonuses *newbon); + virtual void CalcBonuses(); + virtual int GetCurrentBuffSlots() const { return RuleI(Spells, MaxBuffSlotsNPC); } + virtual int GetCurrentSongSlots() const { return RuleI(Spells, MaxSongSlotsNPC); } + virtual int GetCurrentDiscSlots() const { return RuleI(Spells, MaxDiscSlotsNPC); } + virtual int GetMaxBuffSlots() const { return RuleI(Spells, MaxBuffSlotsNPC); } + virtual int GetMaxSongSlots() const { return RuleI(Spells, MaxSongSlotsNPC); } + virtual int GetMaxDiscSlots() const { return RuleI(Spells, MaxDiscSlotsNPC); } + virtual int GetMaxTotalSlots() const { return RuleI(Spells, MaxTotalSlotsNPC); } + virtual int GetPetMaxTotalSlots() const { return RuleI(Spells, MaxTotalSlotsPET); } + virtual void InitializeBuffSlots(); + virtual void UninitializeBuffSlots(); + + virtual void RangedAttack(Mob* other); + virtual void ThrowingAttack(Mob* other) { } + int32 GetNumberOfAttacks() const { return attack_count; } + + bool DatabaseCastAccepted(int spell_id); + bool IsFactionListAlly(uint32 other_faction); + FACTION_VALUE CheckNPCFactionAlly(int32 other_faction); + virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther); + + void GoToBind(uint8 bindnum = 0) { GMMove(org_x, org_y, org_z, org_heading); } + void Gate(); + + void GetPetState(SpellBuff_Struct *buffs, uint32 *items, char *name); + void SetPetState(SpellBuff_Struct *buffs, uint32 *items); + void InteractiveChat(uint8 chan_num, uint8 language, const char * message, const char* targetname,Mob* sender); + void TakenAction(uint8 action,Mob* actiontaker); + virtual void SpellProcess(); + virtual void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); + + void AddItem(const Item_Struct* item, uint16 charges, bool equipitem = true); + void AddItem(uint32 itemid, uint16 charges, bool equipitem = true); + void AddLootTable(); + + void DescribeAggro(Client *towho, Mob *mob, bool verbose); + void RemoveItem(uint32 item_id, uint16 quantity = 0, uint16 slot = 0); + void CheckMinMaxLevel(Mob *them); + void ClearItemList(); + ServerLootItem_Struct* GetItem(int slot_id); + void AddCash(uint16 in_copper, uint16 in_silver, uint16 in_gold, uint16 in_platinum); + void AddCash(); + void RemoveCash(); + void QueryLoot(Client* to); + uint32 CountLoot(); + void DumpLoot(uint32 npcdump_index, ZSDump_NPC_Loot* npclootdump, uint32* NPCLootindex); + inline uint32 GetLoottableID() const { return loottable_id; } + + inline uint32 GetCopper() const { return copper; } + inline uint32 GetSilver() const { return silver; } + inline uint32 GetGold() const { return gold; } + inline uint32 GetPlatinum() const { return platinum; } + + inline void SetCopper(uint32 amt) { copper = amt; } + inline void SetSilver(uint32 amt) { silver = amt; } + inline void SetGold(uint32 amt) { gold = amt; } + inline void SetPlatinum(uint32 amt) { platinum = amt; } + + + virtual int32 CalcMaxMana(); + void SetGrid(int32 grid_){ grid=grid_; } + void SetSp2(uint32 sg2){ spawn_group=sg2; } + void SetWaypointMax(uint16 wp_){ wp_m=wp_; } + void SetSaveWaypoint(uint16 wp_){ save_wp=wp_; } + + uint16 GetWaypointMax() const { return wp_m; } + int32 GetGrid() const { return grid; } + uint32 GetSp2() const { return spawn_group; } + uint32 GetSpawnPointID() const; + + float GetSpawnPointX() const { return org_x; } + float GetSpawnPointY() const { return org_y; } + float GetSpawnPointZ() const { return org_z; } + float GetSpawnPointH() const { return org_heading; } + float GetGuardPointX() const { return guard_x; } + float GetGuardPointY() const { return guard_y; } + float GetGuardPointZ() const { return guard_z; } + float GetGuardPointH() const { return guard_heading; } + EmuAppearance GetGuardPointAnim() const { return guard_anim; } + void SaveGuardPointAnim(EmuAppearance anim) { guard_anim = anim; } + + void SetFlyMode(uint8 FlyMode){ flymode=FlyMode; } + uint32 GetFlyMode() const { return flymode; } + + uint8 GetPrimSkill() const { return prim_melee_type; } + uint8 GetSecSkill() const { return sec_melee_type; } + void SetPrimSkill(uint8 skill_type) { prim_melee_type = skill_type; } + void SetSecSkill(uint8 skill_type) { sec_melee_type = skill_type; } + + uint32 MerchantType; + void Depop(bool StartSpawnTimer = false); + void Stun(int duration); + void UnStun(); + uint32 GetSwarmOwner(); + uint32 GetSwarmTarget(); + void SetSwarmTarget(int target_id = 0); + + void SignalNPC(int _signal_id); + + inline int32 GetNPCFactionID() const { return npc_faction_id; } + inline int32 GetPrimaryFaction() const { return primary_faction; } + int32 GetNPCHate(Mob* in_ent) {return hate_list.GetEntHate(in_ent);} + bool IsOnHatelist(Mob*p) { return hate_list.IsOnHateList(p);} + + void SetNPCFactionID(int32 in) { npc_faction_id = in; database.GetFactionIdsForNPC(npc_faction_id, &faction_list, &primary_faction); } + + float org_x, org_y, org_z, org_heading; + + uint32 GetMaxDMG() const {return max_dmg;} + uint32 GetMinDMG() const {return min_dmg;} + float GetSlowMitigation() const {return slow_mitigation;} + float GetAttackSpeed() const {return attack_speed;} + bool IsAnimal() const { return(bodytype == BT_Animal); } + uint16 GetPetSpellID() const {return pet_spell_id;} + void SetPetSpellID(uint16 amt) {pet_spell_id = amt;} + uint32 GetMaxDamage(uint8 tlevel); + void SetTaunting(bool tog) {taunting = tog;} + void PickPocket(Client* thief); + void StartSwarmTimer(uint32 duration) { swarm_timer.Start(duration); } + void AddLootDrop(const Item_Struct*dbitem, ItemList* itemlistconst, int16 charges, uint8 minlevel, uint8 maxlevel, bool equipit, bool wearchange = false); + virtual void DoClassAttacks(Mob *target); + void CheckSignal(); + + //waypoint crap + int GetMaxWp() const { return max_wp; } + void DisplayWaypointInfo(Client *to); + void CalculateNewWaypoint(); + void AssignWaypoints(int32 grid); + void SetWaypointPause(); + void UpdateWaypoint(int wp_index); + + // quest wandering commands + void StopWandering(); + void ResumeWandering(); + void PauseWandering(int pausetime); + void MoveTo(float mtx, float mty, float mtz, float mth, bool saveguardspot); + void GetClosestWaypoint(list &wp_list, int count, float m_x, float m_y, float m_z); + + uint32 GetEquipment(uint8 material_slot) const; // returns item id + int32 GetEquipmentMaterial(uint8 material_slot) const; + + void NextGuardPosition(); + void SaveGuardSpot(bool iClearGuardSpot = false); + inline bool IsGuarding() const { return(guard_heading != 0); } + void SaveGuardSpotCharm(); + void RestoreGuardSpotCharm(); + void AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay = 2500); + void AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay = 2500); + + //mercenary stuff + void LoadMercTypes(); + void LoadMercs(); + std::list GetMercTypesList() {return mercTypeList; }; + std::list GetMercTypesList( uint32 expansion ); + std::list GetMercsList() {return mercDataList; }; + std::list GetMercsList( uint32 expansion ); + int GetNumMercTypes() { return static_cast(mercTypeList.size()); }; + int GetNumMercTypes( uint32 expansion ); + int GetNumMercs() { return static_cast(mercDataList.size()); }; + int GetNumMercs( uint32 expansion ); + + inline bool WillAggroNPCs() const { return(npc_aggro); } + + inline void GiveNPCTypeData(NPCType *ours) { NPCTypedata_ours = ours; } + inline const uint32 GetNPCSpellsID() const { return npc_spells_id; } + + ItemList itemlist; //kathgar - why is this public? Doing other things or I would check the code + + NPCProximity* proximity; + Spawn2* respawn2; + QGlobalCache *GetQGlobals() { return qGlobals; } + QGlobalCache *CreateQGlobals() { qGlobals = new QGlobalCache(); return qGlobals; } + + AA_SwarmPetInfo *GetSwarmInfo() { return (swarmInfoPtr); } + void SetSwarmInfo(AA_SwarmPetInfo *mSwarmInfo) { swarmInfoPtr = mSwarmInfo; } + + int32 GetAccuracyRating() const { return (accuracy_rating); } + void SetAccuracyRating(int32 d) { accuracy_rating = d;} + int32 GetRawAC() const { return AC; } + + void ModifyNPCStat(const char *identifier, const char *newValue); + virtual void SetLevel(uint8 in_level, bool command = false); + + bool IsLDoNTrapped() const { return (ldon_trapped); } + void SetLDoNTrapped(bool n) { ldon_trapped = n; } + + uint8 GetLDoNTrapType() const { return (ldon_trap_type); } + void SetLDoNTrapType(uint8 n) { ldon_trap_type = n; } + + uint16 GetLDoNTrapSpellID() const { return (ldon_spell_id); } + void SetLDoNTrapSpellID(uint16 n) { ldon_spell_id = n; } + + bool IsLDoNLocked() const { return (ldon_locked); } + void SetLDoNLocked(bool n) { ldon_locked = n; } + + uint16 GetLDoNLockedSkill() const { return (ldon_locked_skill); } + void SetLDoNLockedSkill(uint16 n) { ldon_locked_skill = n; } + + bool IsLDoNTrapDetected() const { return (ldon_trap_detected); } + void SetLDoNTrapDetected(bool n) { ldon_trap_detected = n; } + + 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 + const bool HasPrivateCorpse() const { return NPCTypedata->private_corpse; } + const bool IsUnderwaterOnly() const { return NPCTypedata->underwater; } + const uint32 GetNPCEmoteID() const { return NPCTypedata->emoteid; } + const char* GetRawNPCTypeName() const { return NPCTypedata->name; } + + bool GetDepop() { return p_depop; } + + void NPCSlotTexture(uint8 slot, uint16 texture); // Sets new material values for slots + + uint32 GetAdventureTemplate() const { return adventure_template_id; } + void AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint16 iType, int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust); + void RemoveSpellFromNPCList(int16 spell_id); + Timer *GetRefaceTimer() const { return reface_timer; } + const uint32 GetAltCurrencyType() const { return NPCTypedata->alt_currency_type; } + + NPC_Emote_Struct* GetNPCEmote(uint16 emoteid, uint8 event_); + void DoNPCEmote(uint8 event_, uint16 emoteid); + bool CanTalk(); + + inline void SetSpellScale(float amt) { spellscale = amt; } + inline float GetSpellScale() { return spellscale; } + + inline void SetHealScale(float amt) { healscale = amt; } + inline float GetHealScale() { return healscale; } + + void AddQuestItem(ItemInst* inst) { questItems.Insert(inst); } + + void ClearQuestLists() + { + ClearQuestItems(true); + ClearQuestDeleteItems(true); + } + + void ResetQuestDeleteList() + { + ClearQuestDeleteItems(true); + } + + + void ClearQuestItems(bool delete_=false) + { + LinkedListIterator iterator(questItems); + iterator.Reset(); + while(iterator.MoreElements()) + { + iterator.RemoveCurrent(delete_); + } + + questItems.Clear(); + } + + void ClearQuestDeleteItems(bool delete_=false) + { + LinkedListIterator iterator(questDeletionItems); + iterator.Reset(); + while(iterator.MoreElements()) + { + iterator.RemoveCurrent(delete_); + } + + questDeletionItems.Clear(); + } + + ItemInst* FindQuestItemByID(uint32 itmID, int charges, bool flagItemForDeletion=false) + { + LinkedListIterator iterator(questItems); + iterator.Reset(); + int totalCharges = 0; + while(iterator.MoreElements()) + { + if ( iterator.GetData()->GetItem()->ID == itmID ) + { + totalCharges += 1; + + if ( flagItemForDeletion ) + questDeletionItems.Insert(iterator.GetData()->Clone()); + if ( charges > totalCharges ) + { + iterator.Advance(); + continue; + } + + return iterator.GetData(); + } + iterator.Advance(); + } + return NULL; + } + + bool DoesQuestItemExist(uint32 itmID, int charges, bool flagItemForDeletion=false) { + ItemInst* inst = FindQuestItemByID(itmID,charges,flagItemForDeletion); + if ( inst != NULL ) + { + return true; + } + else + return false; + } + + void ClearQuestItem(ItemInst* inst, bool delete_=true) + { + LinkedListIterator iterator(questItems); + iterator.Reset(); + + while(iterator.MoreElements()) + { + if ( iterator.GetData ()->GetItem()->ID == inst->GetItem()->ID ) + { + iterator.RemoveCurrent(delete_); + break; + } + iterator.Advance(); + } + } + + void RemoveQuestDeleteItems() + { + LinkedListIterator iterator(questDeletionItems); + iterator.Reset(); + while(iterator.MoreElements()) + { + ClearQuestItem(iterator.GetData(),true); + iterator.RemoveCurrent(true); + } + + questDeletionItems.Clear(); + } + + void PrintOutQuestItems(Client* c); + +protected: + + const NPCType* NPCTypedata; + NPCType* NPCTypedata_ours; //special case for npcs with uniquely created data. + + friend class EntityList; + list faction_list; + uint32 copper; + uint32 silver; + uint32 gold; + uint32 platinum; + int32 grid; + uint32 spawn_group; + uint16 wp_m; + + int32 npc_faction_id; + int32 primary_faction; + + Timer attacked_timer; //running while we are being attacked (damaged) + Timer swarm_timer; + Timer classattack_timer; + Timer knightattack_timer; + Timer assist_timer; //ask for help from nearby mobs + Timer qglobal_purge_timer; + + bool combat_event; //true if we are in combat, false otherwise + Timer sendhpupdate_timer; + Timer enraged_timer; + Timer *reface_timer; + + uint32 npc_spells_id; + uint8 casting_spell_AIindex; + Timer* AIautocastspell_timer; + uint32* pDontCastBefore_casting_spell; + std::vector AIspells; + bool HasAISpell; + virtual bool AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes); + virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0); + + + uint32 max_dmg; + uint32 min_dmg; + int32 accuracy_rating; + int16 attack_count; + uint32 npc_mana; + float spellscale; + float healscale; + + //pet crap: + uint16 pet_spell_id; + bool taunting; + Timer taunt_timer; //for pet taunting + + bool npc_aggro; + + deque signal_q; + + //waypoint crap: + vector Waypoints; + void _ClearWaypints(); + int max_wp; + int save_wp; + float guard_x, guard_y, guard_z, guard_heading; + float guard_x_saved, guard_y_saved, guard_z_saved, guard_heading_saved; + EmuAppearance guard_anim; + float roambox_max_x; + float roambox_max_y; + float roambox_min_x; + float roambox_min_y; + float roambox_distance; + float roambox_movingto_x; + float roambox_movingto_y; + uint32 roambox_delay; + + uint16 skills[HIGHEST_SKILL+1]; + uint32 equipment[MAX_WORN_INVENTORY]; //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 + AA_SwarmPetInfo *swarmInfoPtr; + + bool ldon_trapped; + uint8 ldon_trap_type; + uint16 ldon_spell_id; + bool ldon_locked; + uint16 ldon_locked_skill; + bool ldon_trap_detected; + QGlobalCache *qGlobals; + uint32 adventure_template_id; + + LinkedList questItems; + LinkedList questDeletionItems; + + //mercenary stuff + std::list mercTypeList; + std::list mercDataList; + +private: + uint32 loottable_id; + bool p_depop; +}; + +#endif + diff --git a/zone/object.h b/zone/object.h new file mode 100644 index 000000000..a39708a7e --- /dev/null +++ b/zone/object.h @@ -0,0 +1,231 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 OBJECT_H +#define OBJECT_H + +// Object Class: +// Represents Zone Objects (forges, ovens, brew barrels, items dropped to ground, etc) + +#include "../common/types.h" +#include "../common/linked_list.h" +#include "../common/emu_opcodes.h" +#include "../common/eq_packet_structs.h" +#include "../common/Item.h" +#include "client.h" +#include "mob.h" +#include "npc.h" +#include "entity.h" +#include "../common/timer.h" + +/* +item icon numbers (showeq) +IT1_ACTORDEF=Long Sword +IT5_ACTORDEF=Throwing Knife +IT8_ACTORDEF=Basic Staff +IT10_ACTORDEF=Arrow +IT14_ACTORDEF=Basic Hammer +IT16_ACTORDEF=Basic Spear +IT27_ACTORDEF=Book +IT35_ACTORDEF=Mod Rod +IT62_ACTORDEF=Flaming Sword +IT63_ACTORDEF=Small Bag +IT64_ACTORDEF=Large Bag +IT65_ACTORDEF=Scroll +IT66_ACTORDEF=Forge +IT67_ACTORDEF=Voodoo Doll +IT68_ACTORDEF=Glowing Black Stone +IT69_ACTORDEF=Oven +IT70_ACTORDEF=Brew Barrel +IT73_ACTORDEF=Kiln +IT74_ACTORDEF=Pottery Wheel +IT78_ACTORDEF=Campfire (Oven) +IT128_ACTORDEF=Loom +IT177_ACTORDEF=Shattering Hammer +IT203_ACTORDEF=Round Shield +IT210_ACTORDEF=Shimmering Orb +IT400_ACTORDEF=Globe of Slush Water +IT401_ACTORDEF=Red Mushroom +IT402_ACTORDEF=Blue Mushroom +IT403_ACTORDEF=Yew Leaf +IT10511_ACTORDEF=A Soulstone Shard +IT10512_ACTORDEF=Orb of Exploration +IT10630_ACTORDEF=Fish Sword +IT10661_ACTORDEF=Blade of Walnan +IT10714_ACTORDEF=Augmentation Sealer +IT10725_ACTORDEF=Shuriken +*/ + +// Object Types +#define OT_DROPPEDITEM 0x01 +#define OT_MEDICINEBAG 0x09 +#define OT_TOOLBOX 0x0A +#define OT_OVEN 0x0F +#define OT_SEWINGKIT 0x10 //and loom +#define OT_FORGE 0x11 +#define OT_FLETCHINGKIT 0x12 +#define OT_BREWBARREL 0x13 +#define OT_JEWELERSKIT 0x14 +#define OT_POTTERYWHEEL 0x15 +#define OT_KILN 0x16 +#define OT_KEYMAKER 0x17 +#define OT_WIZARDLEX 0x18 +#define OT_MAGELEX 0x19 +#define OT_NECROLEX 0x1A +#define OT_ENCHLEX 0x1B +// high elf forge is 0x1F (Koada'dal forge) +#define OT_TEIRDALFORGE 0x20 //dark elf +#define OT_OGGOKFORGE 0x21 //ogre +#define OT_STORMGUARDF 0x22 //dwarven +#define OT_VALEFORGE 0x31 //halfling +// gnome forge 0x23 (ak'anon forge) +// barbarian forge 0x24 (northman forge) +// +// iksar forge 0x26 (cabilis forge) +// human forge 0x27 (qeynos or freeport?) (royal qeynos forge or freeport forge) +// human forge 0x28 (qeynos or freeport?) +// halfling tailoring kit 0x29 +// erudite tailoring kit 0x2A +// wood elf tailoring kit 0x2B +// wood elf fletching kit 0x2C +// iksar pottery wheel 0x2D +#define OT_TACKLEBOX 0x2e +// troll forge 0x2F (grobb forge) +#define OT_FIERDALFFORGE 0x30 +// erudite forge 0x32 (erud forge) +#define OT_AUGMENT 0x35 +//... (shar vahl forge) +//... wood elf (fier'dal forge) +//... (froglok forge) + +// Icon values: +//0x0453 a pie +//0x0454 cookies? +//0x0455 is a piece of meat? +//0x0456 is fletching sticks +//0x0457 looks like a burnt cookie or something :/ +//0x0458 is a pottery wheel +//0x0459 is a oven +//0x045A is an oven +//0x045B is a forge +//0x045C is brewing barrel +//0x045D is a hammer +//0x045E is a wierd rope shape + +class Object: public Entity +{ +public: + // Loading object from database + Object(uint32 id, uint32 type, uint32 icon, const Object_Struct& data, const ItemInst* inst); + Object(const ItemInst* inst, char* name,float max_x,float min_x,float max_y,float min_y,float z,float heading,uint32 respawntimer); + // Loading object from client dropping item on ground + Object(Client* client, const ItemInst* inst); + Object(const ItemInst *inst, float x, float y, float z, float heading, uint32 decay_time = 300000); + Object(const char *model, float x, float y, float z, float heading, uint8 type, uint32 decay_time = 0); + + // Destructor + ~Object(); + bool Process(); + bool IsGroundSpawn() { return m_ground_spawn; } + // Event handlers + bool HandleClick(Client* sender, const ClickObject_Struct* click_object); + void Close(); + void Delete(bool reset_state=false); // Object itself + static void HandleCombine(Client* user, const NewCombine_Struct* in_combine, Object *worldo); + static void HandleAugmentation(Client* user, const AugmentItem_Struct* in_augment, Object *worldo); + static void HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac); + + static SkillType TypeToSkill(uint32 type); + + // Packet functions + void CreateSpawnPacket(EQApplicationPacket* app); + void CreateDeSpawnPacket(EQApplicationPacket* app); + void Depop(); + void Repop(); + + //Decay functions + void StartDecay() {decay_timer.Start();} + + // Container functions + void PutItem(uint8 index, const ItemInst* inst); + void DeleteItem(uint8 index); // Item inside container + ItemInst* PopItem(uint8 index); // Pop item out of container + + // Override base class implementations + virtual bool IsObject() const { return true; } + virtual bool Save(); + virtual uint16 VarSave(); + virtual void SetID(uint16 set_id); + + void ClearUser() { user = NULL; } + + uint32 GetDBID(); + uint32 GetType(); + void SetType(uint32 type); + void SetDBID(uint32 dbid); + uint32 GetIcon(); + void SetIcon(uint32 icon); + uint32 GetItemID(); + void SetItemID(uint32 itemid); + void GetObjectData(Object_Struct* Data); + void SetObjectData(Object_Struct* Data); + void GetLocation(float* x, float* y, float* z); + void SetLocation(float x, float y, float z); + void GetHeading(float* heading); + void SetHeading(float heading); + float GetX(); + float GetY(); + float GetZ(); + float GetHeadingData(); + void SetX(float pos); + void SetY(float pos); + void SetZ(float pos); + void SetModelName(const char* modelname); + const char* GetModelName(); + + const char* GetEntityVariable(const char *id); + void SetEntityVariable(const char *id, const char *m_var); + bool EntityVariableExists(const char *id); + +protected: + void ResetState(); // Set state back to original + void RandomSpawn(bool send_packet = false); //spawn this ground spawn at a random place + + Object_Struct m_data; // Packet data + ItemInst* m_inst; // Item representing object + bool m_inuse; // Currently in use by a client? + uint32 m_id; // Database key, different than drop_id + uint32 m_type; // Object Type, ie, forge, oven, dropped item, etc + uint32 m_icon; // Icon to use for forge, oven, etc + float m_max_x; + float m_max_y; + float m_min_x; + float m_min_y; + float m_z; + float m_heading; + bool m_ground_spawn; + + std::map o_EntityVariables; + + Client *user; + Client *last_user; + + Timer respawn_timer; + Timer decay_timer; +}; + +#endif diff --git a/zone/oldcode.cpp b/zone/oldcode.cpp new file mode 100644 index 000000000..becf920eb --- /dev/null +++ b/zone/oldcode.cpp @@ -0,0 +1,1859 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "../common/debug.h" +#include +using namespace std; +#include +#include +#include +#include +#include + #ifdef _CRTDBG_MAP_ALLOC + #undef new + #endif +#include + #ifdef _CRTDBG_MAP_ALLOC + #define new new(_NORMAL_BLOCK, __FILE__, __LINE__) + #endif +using namespace std; +#ifdef WIN32 +#include +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +bool spells_loaded = false; +volatile bool RunLoops = true; +extern volatile bool ZoneLoaded; +#ifdef SHAREMEM + #include "../common/EMuShareMem.h" + extern LoadEMuShareMemDLL EMuShareMemDLL; + #ifndef WIN32 + #include + #include + #include + #include +#ifndef FREEBSD + union semun { + int val; + struct semid_ds *buf; + ushort *array; + struct seminfo *__buf; + void *__pad; + }; +#endif + #endif +#endif + + + + +#include "../common/queue.h" +#include "../common/timer.h" +#include "../common/EQStream.h" +#include "../common/eq_packet_structs.h" +#include "../common/Mutex.h" +#include "../common/version.h" +#include "../common/files.h" +#include "../common/EQEMuError.h" +#include "../common/packet_dump_file.h" + +#include "masterentity.h" +#include "worldserver.h" +#include "net.h" +#include "spdat.h" +#include "zone.h" +#include "command.h" +#include "parser.h" +#include "embparser.h" + + +#ifndef NEW_LoadSPDat +void LoadSPDat(SPDat_Spell_Struct** SpellsPointer) { + //FILE *fp; + //cout << "Beginning Spells memset." << endl; + int u; + //for (u = 0; u < SPDAT_RECORDS; u++) + //{ //cout << u << ' '; + memset((char*) &spells,0,sizeof(SPDat_Spell_Struct)*SPDAT_RECORDS); + //} + //cout << "Memset finished\n"; + char temp=' '; + int tempid=0; + char token[64]=""; + int a = 0; + char sep='^'; + LogFile->write(EQEMuLog::Normal, "If this is the last message you see, you forgot to move spells_en.txt from your EQ dir to this dir."); + +#ifdef FREEBSD +#error ifstreams seem to break BSD... +#endif + ifstream in;in.open(SPELLS_FILE); + + if(!in.is_open()){ + LogFile->write(EQEMuLog::Error, "File '%s' not found in same directory as zone.exe, spell loading FAILED!", SPELLS_FILE); + return; + } + //while(!in.eof()) + //{in >> temp;} + //for(int x =0; x< spellsen_size; x++) + // memset((char*) &spells[x],0,sizeof(SPDat_Spell_Struct)); + while(tempid <= SPDAT_RECORDS-1) + { + //if(tempid>3490) + //{ + // cout << "BLEH"; + // getch(); + //} + + //in.getline(&temp, 624); + in.get(temp); + while(chrcmpI(&temp, &sep)) + { + strncat(token,&temp,1); + a++;//cout << temp<< ' '; + in.get(temp); + } + tempid=atoi(token); + if(tempid>=SPDAT_RECORDS) + break; + //cout << "TempID: " << tempid << endl; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + strncpy(spells[tempid].name,token,a); + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + //cout << spells[tempid].name << '^'; + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + strncpy(spells[tempid].player_1,token,a); + //cout << spells[tempid].player_1 << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + strncpy(spells[tempid].teleport_zone,token,a); + //cout << spells[tempid].teleport_zone << '^'; + a=0; + + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + strncpy(spells[tempid].you_cast,token,a); + //cout << spells[tempid].you_cast << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + strncpy(spells[tempid].other_casts,token,a); + //cout << spells[tempid].other_casts << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + strncpy(spells[tempid].cast_on_you,token,a); + //cout << spells[tempid].cast_on_you << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + strncpy(spells[tempid].cast_on_other,token,a); + //cout << spells[tempid].cast_on_other << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + strncpy(spells[tempid].spell_fades,token,a); + //cout << spells[tempid].spell_fades << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + in.get(temp); + } + spells[tempid].range=atof(token); + //cout << spells[tempid].range << '^'; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].aoerange=atof(token); + //cout << spells[tempid].aoerange << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].pushback=atof(token); + //cout << spells[tempid].pushback << '^'; + a=0; + + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].pushup=atof(token); + //cout << spells[tempid].pushup << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].cast_time=atoi(token); + + //cout << spells[tempid].cast_time << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].recovery_time=atoi(token); + //cout << spells[tempid].recovery_time << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].recast_time=atoi(token); + //cout << spells[tempid].recast_time << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].buffdurationformula=atoi(token); + //cout << spells[tempid].buffdurationformula << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].buffduration=atoi(token); + //cout << spells[tempid].buffduration << '^'; + + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].ImpactDuration=atoi(token); + //cout << spells[tempid].ImpactDuration<< '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].mana=atoi(token); + //cout << spells[tempid].mana << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + int y; + for(y=0; y< 12;y++) + { + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].base[y]=atoi(token); + //cout << spells[tempid].base[y] << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + + } + for(y=0; y< 12;y++) + { + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].max[y]=atoi(token); + //cout << spells[tempid].max[y] << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + } + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].icon=atoi(token); + //cout << spells[tempid].icon << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].memicon=atoi(token); + //cout << spells[tempid].memicon << '^'; + + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + for(y=0; y< 4;y++) + { + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].components[y]=atoi(token); + //cout << spells[tempid].components[y] << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + } + for(y=0; y< 4;y++) + { + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].component_counts[y]=atoi(token);//atoi(token); + //cout << spells[tempid].component_counts[y] << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + } + for(y=0; y< 4;y++) + { + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].NoexpendReagent[y]=atoi(token); //NoExpend Reagent + //cout << spells[tempid].NoexpendReagent[y] << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + } + for(y=0; y< 12;y++) + { + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].formula[y]=atoi(token); + //cout << spells[tempid].formula[y] << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + } + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + + spells[tempid].LightType=atoi(token); + //cout << spells[tempid].LightType << '^'; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].goodEffect=atoi(token); + //cout << spells[tempid].goodEffect << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].Activated=atoi(token); + //cout << spells[tempid].Activated << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].resisttype=atoi(token); + //cout << spells[tempid].resisttype << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + for(y=0; y< 12;y++) + { + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].effectid[y]=atoi(token); + //cout << spells[tempid].effectid[y] << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + } + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].targettype=atoi(token); + //cout << spells[tempid].targettype << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].basediff=atoi(token); + //cout << spells[tempid].basediff<< '^'; + a=0; + for(u=0;u<64;u++) + + token[u]=(char)0; + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].skill=atoi(token); + //cout << spells[tempid].skill << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].zonetype=atoi(token); + //cout << spells[tempid].zonetype << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].EnvironmentType=atoi(token); + //cout << spells[tempid].EnvironmentType << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + + in.get(temp); + } + spells[tempid].TimeOfDay=atoi(token); + //cout << spells[tempid].TimeOfDay << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + for(y=0; y< 15;y++) + { + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].classes[y]= atoi(token); + //cout << spells[tempid].classes[y] << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + } //cout << "end class"; + /*for(y=0; y< 3;y++) + { + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].unknown1[y]=atoi(token); + cout << spells[tempid].unknown1[y] << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + } + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].unknown2=atoi(token); + cout << spells[tempid].unknown2 << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + */ + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].CastingAnim=atoi(token); + //cout << spells[tempid].CastingAnim << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].TargetAnim=atoi(token); + //cout << spells[tempid].TargetAnim << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].TravelType=atoi(token); + //cout << spells[tempid].TravelType << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].SpellAffectIndex=atoi(token); + //cout << spells[tempid].SpellAffectIndex << '^'; + + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + for(y=0; y< 23;y++) + { + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].Spacing2[y]=atoi(token); + //cout << spells[tempid].base[y] << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + } + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].ResistDiff=atoi(token); + //cout << spells[tempid].ResistDiff << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + in.get(temp); + for(y=0; y< 2;y++) + { + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].Spacing3[y]=atoi(token); + //cout << spells[tempid].base[y] << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + } + + in.get(temp); + while(chrcmpI(&temp,&sep)) + { + strncat(token,&temp,1); + a++; + in.get(temp); + } + spells[tempid].RecourseLink = atoi(token); + //cout << spells[tempid].RecourseLink << '^'; + a=0; + for(u=0;u<64;u++) + token[u]=(char)0; + + while(temp!='\n') + in.get(temp); + + //cout << endl; + if(tempid==SPDAT_RECORDS-1) break; + } + //for(u=0;u< SPDAT_RECORDS;u++) + // cout << u << ' ' << spells[u].name << '^'; + + spells_loaded = true; + cout << "Spells loaded.\n"; + in.close(); + +} +#endif + + +/*void EntityList::SendAATimer(uint32 charid,UseAA_Struct* uaa){ + Client* client2=this->GetClientByCharID(charid); + if(!client2){ + LogFile->write(EQEMuLog::Error, "Error in SendAATimer: Couldnt find character!"); + return; + } + client2->SendAATimer(uaa); +} + +void ZoneDatabase::UpdateAndDeleteAATimers(uint32 charid){ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + char *query2 = 0; + + if (!RunQuery(query, MakeAnyLenString(&query, "delete from aa_timers where charid=%i and UNIX_TIMESTAMP(now())>=end",charid), errbuf)) { + LogFile->write(EQEMuLog::Error, "UpdateAATimers query '%s' %s", query, errbuf); + } + if (!RunQuery(query2, MakeAnyLenString(&query2, "update aa_timers set end=end-(UNIX_TIMESTAMP(now())-begin),begin=UNIX_TIMESTAMP(now()) where charid=%i",charid), errbuf)) { + LogFile->write(EQEMuLog::Error, "UpdateAATimers query '%s' %s", query2, errbuf); + } + safe_delete_array(query); + safe_delete_array(query2); +} + +void ZoneDatabase::UpdateTimersClientConnected(uint32 charid){ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + if (!RunQuery(query, MakeAnyLenString(&query, "update aa_timers set end=(UNIX_TIMESTAMP(now())+(end-begin)),begin=UNIX_TIMESTAMP(now()) where charid=%i",charid), errbuf)) { + LogFile->write(EQEMuLog::Error, "UpdateAATimers query '%s' %s", query, errbuf); + } + safe_delete_array(query); +} + +void ZoneDatabase::GetAATimers(uint32 charid){ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT ability,begin,end from aa_timers WHERE charid=%i", charid), errbuf, &result)) { + while( ( row = mysql_fetch_row(result) ) ){ + UseAA_Struct* uaa=new UseAA_Struct(); + uaa->ability=atoi(row[0]); + uaa->begin=atoi(row[1]); + uaa->end=atoi(row[2]); + entity_list.SendAATimer(charid,uaa); + safe_delete(uaa); + } + mysql_free_result(result); + } + else { + LogFile->write(EQEMuLog::Error, "Database::GetAATimers query '%s' %s", query, errbuf); + } + safe_delete_array(query); +} + +uint32 ZoneDatabase::GetTimerRemaining(uint32 charid,uint32 ability){ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint32 remain=0; + if (RunQuery(query, MakeAnyLenString(&query, "SELECT end-begin from aa_timers WHERE charid=%i and ability=%i", charid,ability), errbuf, &result)) { + if((row=mysql_fetch_row(result))){ + remain=atoi(row[0]); + } + mysql_free_result(result); + } + else { + LogFile->write(EQEMuLog::Error, "Database::GetTimerRemaining query '%s' %s", query, errbuf); + } + safe_delete_array(query); + return remain; +} + +void ZoneDatabase::UpdateAATimers(uint32 charid,uint32 endtime,uint32 begintime,uint32 ability){ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + if(begintime==0){ + if (!RunQuery(query, MakeAnyLenString(&query, "replace into aa_timers (charid,end,begin,ability) values(%i,UNIX_TIMESTAMP(now())+%i,UNIX_TIMESTAMP(now()),%i)",charid,endtime,ability), errbuf)) { + LogFile->write(EQEMuLog::Error, "UpdateAATimers query '%s' %s", query, errbuf); + } + } + else{ + if (!RunQuery(query, MakeAnyLenString(&query, "replace into aa_timers (charid,end,begin,ability) values(%i,%i,%i,%i)",charid,endtime,begintime,ability), errbuf)) { + LogFile->write(EQEMuLog::Error, "UpdateAATimers query '%s' %s", query, errbuf); + } + } + safe_delete_array(query); +}*/ + +/* +uint16 Client::GetCombinedAC_TEST() { + int ac1; + + ac1 = GetRawItemAC(); + if (m_pp.class_ != WIZARD && m_pp.class_ != MAGICIAN && m_pp.class_ != NECROMANCER && m_pp.class_ != ENCHANTER) { + ac1 = ac1*4/3; + } + ac1 += GetSkill(DEFENSE)/3; + if (GetAGI() > 70) { + ac1 += GetAGI()/20; + } + + int ac2; + + ac2 = GetRawItemAC(); + if (m_pp.class_ != WIZARD && m_pp.class_ != MAGICIAN && m_pp.class_ != NECROMANCER && m_pp.class_ != ENCHANTER) { + ac2 = ac2*4/3; + } + ac2 += GetSkill(DEFENSE)*400/255; + + int combined_ac = (ac1+ac2)*1000/847; + return combined_ac; + float combined_ac = ((float)ac1+(float)ac2)*1000.0f/847.0f; + return (uint16) combined_ac;//*10.0f)-10; +} +*/ + + +/*bool Client::GetIncreaseSpellDurationItem(uint16 &spell_id, char *itemname) +{ + for (int i=0; i<22; i++) { + const ItemInst* inst = m_inv[i]; + if (!inst || !inst->IsType(ItemTypeCommon)) + continue; + + const Item_Struct* item = inst->GetItem(); + if (item->FocusId && (item->FocusId != 0xFFFF)) { + if (IsIncreaseDurationSpell(item->FocusId)) { + spell_id = item->FocusId; + if (itemname) + strcpy(itemname, item->Name); + return true; + } + } + } + return false; +} + +bool Client::GetReduceManaCostItem(uint16 &spell_id, char *itemname) +{ + for (int i=0; i<22; i++) { + const ItemInst* inst = m_inv[i]; + if (!inst || !inst->IsType(ItemTypeCommon)) + continue; + + const Item_Struct* item = inst->GetItem(); + if (item->FocusId && (item->FocusId != 0xFFFF)) { + if (IsReduceManaSpell(item->FocusId)) { + spell_id = item->FocusId; + if (itemname) + strcpy(itemname, item->Name); + return true; + } + } + } + return false; +} + +bool Client::GetReduceCastTimeItem(uint16 &spell_id, char *itemname) +{ + for (int i=0; i<22; i++) { + const ItemInst* inst = m_inv[i]; + if (!inst || !inst->IsType(ItemTypeCommon)) + continue; + + const Item_Struct* item = inst->GetItem(); + if (item->FocusId && (item->FocusId != 0xFFFF)) { + if (IsReduceCastTimeSpell(item->FocusId)) { + spell_id = item->FocusId; + if (itemname) + strcpy(itemname, item->Name); + return true; + } + } + } + return false; +} + +bool Client::GetExtendedRangeItem(uint16 &spell_id, char *itemname) +{ + for (int i=0; i<22; i++) { + const ItemInst* inst = m_inv[i]; + if (!inst || !inst->IsType(ItemTypeCommon)) + continue; + + const Item_Struct* item = inst->GetItem(); + if (item->FocusId && (item->FocusId != 0xFFFF)) { + if (IsExtRangeSpell(item->FocusId)) { + spell_id = item->FocusId; + if (itemname) + strcpy(itemname, item->Name); + return true; + } + } + } + return false; +} + +bool Client::GetImprovedHealingItem(uint16 &spell_id, char *itemname) +{ + for (int i=0; i<22; i++) { + const ItemInst* inst = m_inv[i]; + if (!inst || !inst->IsType(ItemTypeCommon)) + continue; + + const Item_Struct* item = inst->GetItem(); + if (item->FocusId && (item->FocusId != 0xFFFF)) { + if (IsImprovedHealingSpell(item->FocusId)) { + spell_id = item->FocusId; + if (itemname) + strcpy(itemname, item->Name); + return true; + } + } + } + return false; +} + +bool Client::GetImprovedDamageItem(uint16 &spell_id, char *itemname) +{ + for (int i=0; i<22; i++) { + const ItemInst* inst = m_inv[i]; + if (!inst || !inst->IsType(ItemTypeCommon)) + continue; + + const Item_Struct* item = inst->GetItem(); + if (item->FocusId && (item->FocusId != 0xFFFF)) { + if (IsImprovedDamageSpell(item->FocusId)) { + spell_id = item->FocusId; + if (itemname) + strcpy(itemname, item->Name); + return true; + } + } + } + return false; +} + +int32 Client::GenericFocus(uint16 spell_id, uint16 modspellid) +{ + int modifier = 100, i; + const SPDat_Spell_Struct &spell = spells[spell_id]; + const SPDat_Spell_Struct &modspell = spells[modspellid]; + + for (i = 0; i < EFFECT_COUNT; i++) + { + if(IsBlankSpellEffect(modspellid, i)) + continue; + switch( spells[modspellid].effectid[i] ) + { + case SE_LimitMaxLevel: + if (spell.classes[(GetClass()%16) - 1] > modspell.base[i]) + return 100; + break; + case SE_LimitMinLevel: + if (spell.classes[(GetClass()%16) - 1] < modspell.base[i]) + return 100; + break; + case SE_IncreaseRange: + modifier += modspell.base[i]; + break; + case SE_IncreaseSpellHaste: + modifier -= modspell.base[i]; + break; + case SE_IncreaseSpellDuration: + modifier += modspell.base[i]; + break; + case SE_LimitSpell: + // negative sign means exclude + // positive sign means include + if (modspell.base[i] < 0) + { + if (modspell.base[i] * (-1) == spell_id) + return 100; + } + else + { + if (spells[modspellid].base[i] != spell_id) + return 100; + } + break; + case SE_LimitEffect: + switch( spells[modspellid].base[i] ) + { + case -147: + if (IsPercentalHealSpell(spell_id)) + return 100; + break; + case -101: + if (IsCHDurationSpell(spell_id)) + return 100; + break; + case -40: + if (IsInvulnerabilitySpell(spell_id)) + return 100; + break; + case -32: + if (IsSummonItemSpell(spell_id)) + return 100; + break; + case 0: + if (!IsEffectHitpointsSpell(spell_id)) + return 100; + break; + case 33: + if (!IsSummonPetSpell(spell_id)) + return 100; + break; + case 36: + if (!IsPoisonCounterSpell(spell_id)) + return 100; + break; + case 71: + if (!IsSummonSkeletonSpell(spell_id)) + return 100; + break; + default: + LogFile->write(EQEMuLog::Normal, "GenericFocus: unknown limit effect %d", spells[modspellid].base[i]); + } + break; + case SE_LimitCastTime: + if (modspell.base[i] > (int16)spell.cast_time) + return 100; + break; + case SE_LimitSpellType: + switch( spells[modspellid].base[i] ) + { + case 0: + if (!IsDetrimentalSpell(spell_id)) + return 100; + break; + case 1: + if (!IsBeneficialSpell(spell_id)) + return 100; + break; + default: + LogFile->write(EQEMuLog::Normal, "GenericFocus: unknown limit spelltype %d", spells[modspellid].base[i]); + } + break; + case SE_LimitMinDur: + if (modspell.base[i] > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) + return 100; + break; + case SE_ImprovedDamage: + case SE_ImprovedHeal: + modifier += modspell.base[i]; + break; + case SE_ReduceManaCost: + modifier -= modspell.base[i]; + break; + default: + LogFile->write(EQEMuLog::Normal, "GenericFocus: unknown effectid %d", modspell.effectid[i]); + } + } + + return modifier; +} +*/ + + + +/*void Client::Discipline(ClientDiscipline_Struct* disc_in, Mob* tar) { +Message(0, "Disc packet id=%d, %x,%x,%x", disc_in->disc_id, disc_in->unknown3[0], disc_in->unknown3[1], disc_in->unknown3[2]); + if (!p_timers.Expired(&database, pTimerDisciplineReuse)) { + char val1[20]={0}; + char val2[20]={0}; + uint32 remain = p_timers.GetRemainingTime(pTimerDisciplineReuse); + Message_StringID(0,DISCIPLINE_CANUSEIN,ConvertArray((remain)/60,val1),ConvertArray(remain%60,val2)); + //Message(0,"You can use a new discipline in %i minutes %i seconds.", (disc_timer.GetRemainingTime()/1000)/60, disc_timer.GetRemainingTime()/1000%60); + return; + } + + //reuse times are a little messes up, they should scale down somehow + //as you gain in levels, but im not sure how, so its just a lvl 60 bonus right now + + //should change this to check classes better. + + //both in seconds, converted at the end. + uint32 duration = 0; + uint32 reuse = 0; + + switch(disc_in->disc_id){ + // Shared? + case discResistant: { // Resistant + // 1 minute duration + // 1 hour reuse + // +3 to +10 to resists + if (GetLevel()<=29) + return; + duration = 60; + reuse = 60*60; + entity_list.MessageClose(this, false, 100, 0, "%s has become more resistant!", GetName()); + break; + } + case discFearless: { // Fearless + // 11 second duration + // 1 hour reuse + // 100% fear immunity + if (GetLevel()<=39) + return; + duration = 11; + reuse = 60*60; + entity_list.MessageClose_StringID(this, false, 100, 0, DISCIPLINE_FEARLESS, GetName()); + //entity_list.MessageClose(this, false, 100, 0, "%s becomes fearless!", GetName()); + break; + } + case discWhirlwind: { // Counterattack/Whirlwind/Furious + // warrior level 56 + // rogue/monk level 53 + // 9 second duration + // 1 hour reuse + if ( (GetClass() == WARRIOR && GetLevel() <= 56) + ||(GetLevel() <= 53) + ) return; + duration = 9; + reuse = 60*60; + entity_list.MessageClose(this, false, 100, 0, "%s\'s face becomes twisted with fury!", GetName()); + break; + } + case discFellstrike: { // Duelist/Innerflame/Fellstrike + // monk level 56 + // rogue level 59 + // warrior level 58 + // 12 second duration + // 30 minute reuse + // min 4*base hand/weapon damage + if ( (GetClass() == MONK && GetLevel() <= 55) + ||(GetClass() == WARRIOR && GetLevel() <= 58) + ||(GetClass() == ROGUE && GetLevel() <= 59) + ) return; + duration = 12; + reuse = 60*30; + entity_list.MessageClose(this, false, 100, 0, "%s\'s muscles bulge with force of will!", GetName()); + break; + } + case discBlindingSpeed: { // Blindingspeed/Hundredfist + // rogue level 58 + // monk level 57 + // 15 second duration + // 30 minute reuse + if ( (GetClass() == MONK && GetLevel() <= 58) + ||(GetClass() == ROGUE && GetLevel() <= 57) + ) return; + //disc_timer.Start(1000*60*30); + //disc_elapse.Start(1000*15); + duration = 15; + reuse = 60*30; + Message(0, "This discipline not implemented.."); + break; + } + case discDeadeye: { // Deadeye/Charge + // warrior level 53 + // rogue level 54 + // 14 second duration + // 30 minute reuse + if ( (GetClass() == WARRIOR && GetLevel() <= 53) + ||(GetClass() == ROGUE && GetLevel() <= 54) + ) return; + duration = 14; + reuse = 60*30; + entity_list.MessageClose(this, false, 100, 0, "%s feels unstopable!", GetName()); + break; + } + // Warrior + case discEvasive: { // Evasive + // level 52 + // 3 minute duration + // 15 minute reuse + // +35% avoidance + // -15% out + duration = 3*60; + reuse = 15*60; + break; + } + case discMightystrike: { // Mightystrike + // level 54 + // 10 second duration + // 1 hour reuse + // Auto crit + duration = 10; + reuse = 60*60; + break; + } + case discDefensive: { // Defensive + // level 55 + // 3 minute duration + // 15 minute reuse, 10 after 60 + // +35% mitigation + // -15% out + duration = 3*60; + if(level > 59) + reuse = 10*60; + else + reuse = 15*60; + break; + } + case discPrecise: { // Precise + // level 57 + // 3 minute duration + // 30 minute reuse + // -15% avoidance + // +35% out + duration = 3*60; + reuse = 30*60; + break; + } + case discAggressive: { // Aggressive + // level 60 + // 3 minute duration + // 27 minute reuse + // -15% mitigation + // +35% out + duration = 3*60; + reuse = 27*60; + break; + } + // Monk + case discStonestance: { // Stonestance + // level 51 + duration = 12; + if(level > 59) + reuse = 3*60; + else + reuse = 12*60; + break; + } + case discThunderkick: { // Thunderkick + // level 52 + if(level > 59) + reuse = 1*60; + else + reuse = 7*60; + duration = 5*60; //hack for now, checked in combat and expired once used. + break; + } + case discVoidance: { // Voidance + // level 54 + if(level > 59) + reuse = 54*60; + else + reuse = 60*60; + duration = 8; + break; + } + case discSilentfist: { // Silentfist + // level 59 + // 6-7 minute reuse + // Dragon punch damage bonus + // Chance to stun + if(level > 59) + reuse = 6*60; + else + reuse = 7*60; + duration = 5*60; //hack for now, checked in combat and expired once used. + break; + } + case discAshenhand: { // Ashenhand + // level 60 + // 72 minute reuse + // Eagle Strike damage bonus + // Chance to slay + reuse = 72*60; + duration = 5*60; //hack for now, checked in combat and expired once used. + break; + } + // Rogue + case discNimble: { // Nimble + // level 55 + // 12 second duration + // 30 minute reuse + // Auto dodge + if(level > 59) + reuse = 25*60; + else + reuse = 30*60; + duration = 12; + break; + } + case discKinesthetics: { // Kinesthetics + // level 57 + // 18 second duration + // 30,27 minute reuse + // Auto dualwield + // Auto double attack + if(level > 59) + reuse = 27*60; + else + reuse = 30*60; + duration = 18; + break; + } + // Paladin + case discHolyforge: { // Holyforge + // level 55 + // 2 minute duration + // 72 minute reuse + // Crit/Crip undead + // +15% to crit chance + if(level > 59) + reuse = 65*60; + else + reuse = 72*60; + duration = 5*60; + break; + } + case discSanctification: { // Sanctification + // level 60 + // 10 second duration + // 72 minute reuse + // Spell immunity + reuse = 72*60; + duration = 15; + break; + } + // Ranger + case discTrueshot: { // Trueshot + // level 55 + // 2 minute duration + // 72 minute reuse + // Max to two times max bow damage + // +15% to hit + if(level > 59) + reuse = 67*60; + else + reuse = 72*60; + duration = 2*60; + break; + } + case discWeaponshield: { // Weaponshield + // level 60 + // 15 second duration + // 72 minute reuse + // auto parry + reuse = 72*60; + duration = 20; + break; + } + // Bard + case discDeftdance: { // Deftdance + // level 55 + // 10 second duration + // 72 minute reuse + // auto dodge + // auto dualwield + if(level > 59) + reuse = 67*60; + else + reuse = 72*60; + duration = 10; + break; + } + case discPuretone: { // Puretone + // level 60 + // 2 minute duration + // 72 minute reuse + // Auto instrument + reuse = 72*60; + duration = 4*60; + break; + } + // Shadow knight + case discUnholyAura: { // Unholy + // level 55 + // 72 minute reuse + // +25% to harmtouch + // -300 to resist + if(level > 59) + reuse = 67*60; + else + reuse = 72*60; + duration = 5*60; //hack for now, checked in combat and expired once used. + break; + } + case discLeechCurse: { // Leech curse + // level 60 + // 15 second duration + // 72 minute reuse + // Heal self for each point of melee damage done + reuse = 72*60; + duration = 15; + break; + } + // Default + case 0:{ // Timer request + break; + } + default: + LogFile->write(EQEMuLog::Error, "Unknown Discipline requested by client: %s class: %i Disciline:%i", GetName(), class_,disc_in->disc_id); + return; + } + + if(reuse != 0) { + p_timers.Start(pTimerDisciplineReuse, reuse); + //nonpersistent timer for the 'discipline ready' message + disc_timer.Start(1000*reuse); + } + if(duration != 0) + disc_elapse.Start(1000*duration); + + disc_inuse = disc_in->disc_id; +}*/ + +#if 0 // solar: this is old code +/*void EntityList::AESpell(Mob* caster, Mob* center, float dist, uint16 spell_id, bool group) +{ + LinkedListIterator iterator(mob_list); + iterator.Reset(); + while(iterator.MoreElements()) { + Mob* mob = iterator.GetData(); + if (group){ + // Client casting group spell with out target group buffs enabled + // Skip non group members + if ( caster->IsClient() + && !caster->CastToClient()->TGB() + && GetGroupByMob(mob) != 0 + && !GetGroupByMob(mob)->IsGroupMember(caster) + ) { + LogFile->write(EQEMuLog::Debug, "Group spell skipping %s", mob->GetName()); + iterator.Advance(); + continue; + } + // Client casting group spell with target group buffs enabled + else if ( caster->IsClient() + && caster->CastToClient()->TGB() + && GetGroupByMob(mob) != 0 + && GetGroupByMob(mob)->IsGroupMember(caster) + ){ + LogFile->write(EQEMuLog::Debug, "Group spell TGB on %s's Group", mob->GetName()); + GetGroupByMob(mob)->CastGroupSpell(caster, spell_id); + iterator.Advance(); + continue; + } + else if ( caster->IsClient() + && caster->CastToClient()->TGB() + && GetGroupByMob(mob) == 0 + && mob == center + ){ + LogFile->write(EQEMuLog::Debug, "Group spell TGB on %s", mob->GetName()); + caster->SpellOnTarget(spell_id, mob); + return; + } + } + if ( + mob->DistNoZ(*center) <= dist + && !(mob->IsClient() && mob->CastToClient()->GMHideMe()) + && !mob->IsCorpse() + ) { + //cout << "AE Spell Hit: t=" << iterator.GetData()->GetName() << ", d=" << iterator.GetData()->CastToMob()->DistNoRoot(center) << ", x=" << iterator.GetData()->CastToMob()->GetX() << ", y=" << iterator.GetData()->CastToMob()->GetY() << endl; + if (caster == mob) { + // Caster gets the first hit, already handled in spells.cpp + } + #ifdef IPC + else if(caster->IsNPC() && !caster->CastToNPC()->IsInteractive()) { + #else + else if(caster->IsNPC()) { + #endif + // Npc + if (caster->IsAttackAllowed(mob) && spells[spell_id].targettype != ST_AEBard) { + // printf("NPC Spell casted on %s\n", mob->GetName()); + caster->SpellOnTarget(spell_id, mob); + } + else if (mob->IsAIControlled() && spells[spell_id].targettype == ST_AEBard) { + // printf("NPC mgb/aebard spell casted on %s\n", mob->GetName()); + caster->SpellOnTarget(spell_id, mob); + } + else { + // printf("NPC AE, fall thru. spell_id:%i, Target type:%x\n", spell_id, spells[spell_id].targettype); + } + } + #ifdef IPC + else if(caster->IsNPC() && caster->CastToNPC()->IsInteractive()) { + // Interactive npc + if (caster->IsAttackAllowed(mob) && spells[spell_id].targettype != ST_AEBard && spells[spell_id].targettype != ST_GroupTeleport) { + // printf("IPC Spell casted on %s\n", mob->GetName()); + caster->SpellOnTarget(spell_id, mob); + } + else if (!mob->IsAIControlled() && (spells[spell_id].targettype == ST_AEBard||group) && mob->CastToClient()->GetPVP() == caster->CastToClient()->GetPVP()) { + if (group && GetGroupByMob(mob) != GetGroupByMob(caster)) { + iterator.Advance(); + continue; + } + // printf("IPC mgb/aebard spell casted on %s\n", mob->GetName()); + caster->SpellOnTarget(spell_id, mob); + } + else { + // printf("NPC AE, fall thru. spell_id:%i, Target type:%x\n", spell_id, spells[spell_id].targettype); + } + } + #endif + else if (caster->IsClient() && !(caster->CastToClient()->IsBecomeNPC())) { + // Client + if (caster->IsAttackAllowed(mob) && spells[spell_id].targettype != ST_AEBard){ + // printf("Client Spell casted on %s\n", mob->GetName()); + caster->SpellOnTarget(spell_id, mob); + } + else if(spells[spell_id].targettype == ST_GroupTeleport && mob->IsClient() && mob->isgrouped && caster->isgrouped && entity_list.GetGroupByMob(caster)) + { + Group* caster_group = entity_list.GetGroupByMob(caster); + if(caster_group != 0 && caster_group->IsGroupMember(mob)) + caster->SpellOnTarget(spell_id,mob); + } + else if (mob->IsClient() && (spells[spell_id].targettype == ST_AEBard||group) && mob->CastToClient()->GetPVP() == caster->CastToClient()->GetPVP()) { + if (group && GetGroupByMob(mob) != GetGroupByMob(caster)) { + iterator.Advance(); + continue; + } + else if (mob->IsClient() && spells[spell_id].targettype == ST_AEBard && mob->CastToClient()->GetPVP() == caster->CastToClient()->GetPVP()) + caster->SpellOnTarget(spell_id, mob); + #ifdef IPC + else if (mob->IsNPC() && mob->CastToNPC()->IsInteractive()) { + if (group && GetGroupByMob(mob) != GetGroupByMob(caster)) + continue; + caster->SpellOnTarget(spell_id, mob); + } + #endif + } + } + else if (caster->IsClient()) { + // Client BecomeNPC + caster->SpellOnTarget(spell_id, mob); + } + } + iterator.Advance(); + } +}*/ +#endif // solar: old code + +/*#if 0 +// Queries the loottable: adds item & coin to the npc +void ZoneDatabase::AddLootTableToNPC(uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + *copper = 0; + *silver = 0; + *gold = 0; + *plat = 0; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, mincash, maxcash, avgcoin FROM loottable WHERE id=%i", loottable_id), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + uint32 mincash = atoi(row[1]); + uint32 maxcash = atoi(row[2]); + if (mincash > maxcash) { + cerr << "Error in loottable #" << row[0] << ": mincash > maxcash" << endl; + } + else if (maxcash != 0) { + uint32 cash = 0; + if (mincash == maxcash) + cash = mincash; + else + cash = (rand() % (maxcash - mincash)) + mincash; + if (cash != 0) { + uint32 coinavg = atoi(row[3]); + if (coinavg != 0) { + uint32 mincoin = (uint32) (coinavg * 0.75 + 1); + uint32 maxcoin = (uint32) (coinavg * 1.25 + 1); + *copper = (rand() % (maxcoin - mincoin)) + mincoin - 1; + *silver = (rand() % (maxcoin - mincoin)) + mincoin - 1; + *gold = (rand() % (maxcoin - mincoin)) + mincoin - 1; + cash -= *copper; + cash -= *silver * 10; + cash -= *gold * 10; + } + *plat = cash / 1000; + cash -= *plat * 1000; + uint32 gold2 = cash / 100; + cash -= gold2 * 100; + uint32 silver2 = cash / 10; + cash -= silver2 * 10; + *gold += gold2; + *silver += silver2; + *copper += cash; + } + } + } + else { + mysql_free_result(result); + return; + } + mysql_free_result(result); + } + else + { + cerr << "Error in AddLootTableToNPC get coin query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return; + } + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT loottable_id, lootdrop_id, multiplier, probability FROM loottable_entries WHERE loottable_id=%i", loottable_id), errbuf, &result)) { + safe_delete_array(query); + while ((row = mysql_fetch_row(result))) { + int multiplier = atoi(row[2]); + for (int i = 1; i <= multiplier; i++) { + if ( ((rand()%1)*100) < atoi(row[3])) { + AddLootDropToNPC(atoi(row[1]), itemlist); + } + } + } + mysql_free_result(result); + } + else { + cerr << "Error in AddLootTableToNPC get items query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return; + } + + return; +} + +// Called by AddLootTableToNPC +// maxdrops = size of the array npcd +void ZoneDatabase::AddLootDropToNPC(uint32 lootdrop_id, ItemList* itemlist) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + +// This is Wiz's updated Pool Looting functionality. Eventually, the database format should be moved over to use this +// or implemented to support both methods. (A unique identifier in lootable_entries indicates to roll for a pool item +// in another table. +#ifdef POOLLOOTING + uint32 chancepool = 0; + uint32 items[50]; + uint32 itemchance[50]; + uint16 itemcharges[50]; + uint8 i = 0; + + for (int m=0;m < 50;m++) + { + items[m]=0; + itemchance[m]=0; + itemcharges[m]=0; + } + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT lootdrop_id, item_id, item_charges, equip_item, chance FROM lootdrop_entries WHERE lootdrop_id=%i order by chance desc", lootdrop_id), errbuf, &result)) + { + safe_delete_array(query); + while (row = mysql_fetch_row(result)) + { + items[i] = atoi(row[1]); + itemchance[i] = atoi(row[4]) + chancepool; + itemcharges[i] = atoi(row[2]); + chancepool += atoi(row[4]); + i++; + } + uint32 res; + i = 0; + + if (chancepool!=0) //avoid divide by zero if some mobs have 0 for chancepool + { + res = rand()%chancepool; + } + else + { + res = 0; + } + + while (items[i] != 0) + { + if (res <= itemchance[i]) + break; + else + i++; + } + const Item_Struct* dbitem = database.GetItem(items[i]); + if (dbitem == 0) + { + LogFile->write(EQEMuLog::Error, "AddLootDropToNPC: dbitem=0, item#=%i, lootdrop_id=%i", items[i], lootdrop_id); + } + else + { + //printf("Adding item2: %i",item->item_id); + //cout << "Adding item to Mob" << endl; + ServerLootItem_Struct* item = new ServerLootItem_Struct; + item->item_id = dbitem->ItemNumber; + item->charges = itemcharges[i]; + item->equipSlot = 0; + (*itemlist).Append(item); + } + mysql_free_result(result); + } +#else + if (RunQuery(query, MakeAnyLenString(&query, "SELECT lootdrop_id, item_id, item_charges, equip_item, chance FROM lootdrop_entries WHERE lootdrop_id=%i order by chance desc", lootdrop_id), errbuf, &result)) + { + safe_delete_array(query); + while ((row = mysql_fetch_row(result))) + { + uint8 LootDropMod=1; // place holder till I put it in a database variable to make it configurable. + if( (rand()%100) < ((atoi(row[4]) * LootDropMod)) ) + { + uint32 itemid = atoi(row[1]); + const Item_Struct* dbitem = database.GetItem(itemid); + if (dbitem == 0) + { + LogFile->write(EQEMuLog::Error, "AddLootDropToNPC: dbitem=0, item#=%i, lootdrop_id=%i", itemid, lootdrop_id); + } + else + { + printf("Adding item: %i",item->ItemNumber); + ServerLootItem_Struct* item = new ServerLootItem_Struct; + item->item_id = dbitem->item_id; + item->charges = atoi(row[2]); + item->equipSlot = 0; + (*itemlist).Append(item); + } + + //mysql_free_result(result); + //return; + } + } + mysql_free_result(result); + } +#endif + else + { + LogFile->write(EQEMuLog::Error, "Error in AddLootDropToNPC query '%s' %s", query, errbuf); + safe_delete_array(query); + return; + } + + return; +} +#endif*/ diff --git a/zone/oldcode.h b/zone/oldcode.h new file mode 100644 index 000000000..3ec6f1ec4 --- /dev/null +++ b/zone/oldcode.h @@ -0,0 +1,25 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 OLDCODE_H +#define OLDCODE_H + + + + +#endif + diff --git a/zone/parser.cpp b/zone/parser.cpp new file mode 100644 index 000000000..b2265d23a --- /dev/null +++ b/zone/parser.cpp @@ -0,0 +1,1698 @@ +// Quest Parser written by Wes, Leave this here =P +//Variables causing crash with linked lists (Bad pointers being added to the lists) - Npcs causing crashes with linked lists + + +#include +#include +using namespace std; +#include +using namespace std; +#include +#include +#include + +using namespace std; +#include "../common/debug.h" +#include "entity.h" +#include "masterentity.h" + +#include "worldserver.h" +#include "net.h" +#include "../common/skills.h" +#include "../common/classes.h" +#include "../common/races.h" +#include "zonedb.h" +#include "spdat.h" +#include "../common/packet_functions.h" +#include "spawn2.h" +#include "zone.h" +#include "event_codes.h" +#include +#include "parser.h" +#include "basic_functions.h" +#include "questmgr.h" + + +extern Zone* zone; +extern WorldServer worldserver; +extern EntityList entity_list; + + +#define Parser_DEBUG 1 + +int mindex1 = 0; + +const char * charIn3 = "`~1234567890-=!@#$%^&*_+qwertyuiop[]asdfghjkl'zxcvbnm,./QWERTYUIOP|ASDFGHJKL:ZXCVBNM<>?\"\\"; + +// MYRA - restore missing commands for qst type files & add itemlink +string cmds("if 0|break 1|spawn 6|spawn2 7|settimer 2|stoptimer 1|rebind 4|echo 1|summonitem 1|selfcast 1|zone 1|castspell 2|say 1|emote 1|shout 1|shout2 1|depop 1|exp 1|level 1|safemove 1|rain 1|snow 1|givecash 4|pvp 1|doanim 1|addskill 2|flagcheck 1|me 1|write 2|settarget 2|follow 1|sfollow 1|save 1|setallskill 1|faction 2|settime 2|setguild 2|setsky 1|setstat 2|movepc 5|gmmove 3|movegrp 4|signal 1|attack 1|itemlink 1|setglobal 4|delglobal 1|targlobal 6|setskill 2|setlanguage 2|stop 0|resume 0|start 1|moveto 3|pause 1|addldonpoints 2|"); + + +//end Myra + + +int GetArgs(string command) +{ + string::iterator iterator = cmds.begin(); + string buffer; + string cmd; + string argnum; + while (*iterator) { + if (*iterator == ' ') + { + if (buffer.compare(command)==0) { + cmd = buffer; + buffer=""; + } + } + else { + if (*iterator != '|') + buffer += *iterator; + } + if (*iterator == '|') + { + if (!cmd.empty()) { + int argnums = atoi(buffer.c_str()); + return argnums; + } + else { + buffer=""; + } + } + iterator++; + } + return -1; +} + +int calc(string calc) +{ + string::iterator iterator = calc.begin(); + string buffer; + string integer1; + char op = 0; + int returnvalue = 0; + + while (*iterator) { + char ch = *iterator; + + if(ch >= '0' && ch <= '9') { //If this character is numeric add it to the buffer + buffer += ch; + } + else if(ch == '+' || ch == '-' || ch == '/' || ch == '*') { //otherwise, are we an operator? + int val = atoi(buffer.c_str()); + if (!op) { //if this is the first time through, set returnvalue to what we have so far + returnvalue = val; + } + else { //otherwise we've got returnvalue initialized, perform operation on previous numbers + if (buffer.length() == 0) { //Do we have a value? + printf("Parser::calc() Error in syntax: '%s'.\n", calc.c_str()); + return 0; + } + + //what was the previous op + switch(op) + { + case '+': { + returnvalue += val; + break; + } + case '-': { + returnvalue -= val; + break; + } + case '/': { + if(val == 0)//can't divide by zero + { + printf("Parser::calc() Error, Divide by zero '%s'.\n", calc.c_str()); + return 0; + } + returnvalue /= val; + break; + } + case '*': { + returnvalue *= val; + break; + } + }; + + op = ch; //save current operator and continue parsing + } + buffer=""; //clear buffer now that we're starting on a new number + op = ch; + } + else { + printf("Parser::calc() Error processing '%c'.\n", ch); + return 0; + } + } + return returnvalue; +} + +int Parser::numtok(const char *text, char character) { + int returnvalue=0; + for(; *text != '\0'; text++) { + if(*text == character) returnvalue++; + } + return returnvalue; +} + +string strlwr(string tmp) { + string res; + transform(tmp.begin(), tmp.end(), res.begin(), (int(*)(int))tolower); + return(res); +} + +int strcmp(const string &com, const string &com2) { + return strcmp(com.c_str(),com2.c_str()); +} + +string gettok(const char *text, char character, int index) +{ + string buffer; + int find=0; + for(; *text != '\0'; *text++) + { + if (*text != character) + buffer += *text; + else { + if (find == index) + break; + buffer = ""; + find++; + } + } + return buffer; +} + +void Parser::MakeVars(string text, uint32 npcid) { + string buffer; + string temp; + int pos = numtok(text.c_str(),' ')+1; + for(int i=0;i10 + printf("Buffer: %s, temp: %s\n",buffer.c_str(),temp.c_str()); +#endif +AddVar(temp,buffer); + temp=""; + temp=(string)itoa(i+1) + "-." + (string)itoa(npcid); + AddVar(temp,strstr(text.c_str(),buffer.c_str())); + buffer=""; + } + +} + + +int Parser::CheckAliases(const char * alias, uint32 npcid, Mob* npcmob, Mob* mob) +{ +/* MyListItem * Ptr = AliasList.First; + + while (Ptr) { + if ( (uint32)Ptr->Data->npcid == npcid) { + for (int i=0; i <= Ptr->Data->index; i++) { + if (!strcmp(strlwr(Ptr->Data->name[i]),alias)) { + CommandEx(Ptr->Data->command[i], npcid, npcmob, mob); + return 1; + } + } + } + Ptr = Ptr->Next; + } + return 0;*/ + return 0; +} + + +int Parser::pcalc(const char * string) { + char temp[100]; + memset(temp, 0, sizeof(temp)); + char temp2[100]; + memset(temp2, 0, sizeof(temp2)); + int p =0; + char temp3[100]; + memset(temp3, 0, sizeof(temp3)); + char temp4[100]; + memset(temp4, 0, sizeof(temp4)); + while (strrchr(string,'(')) { + strn0cpy(temp,strrchr(string,'('), sizeof(temp)); + for ( unsigned int i=0;i < strlen(temp); i++ ) { + if (temp[i] != '(' && temp[i] != ')') { + temp2[p] = temp[i]; + p++; + } + else if (temp[i] == ')') { + snprintf(temp3, sizeof(temp3), "(%s)", temp2); +// Replace(string,temp3,itoa(calc(temp2),temp4,10),0); + memset(temp, 0, sizeof(temp)); + memset(temp2, 0, sizeof(temp2)); + memset(temp3, 0, sizeof(temp3)); + memset(temp4, 0, sizeof(temp4)); + p=0; + } + } + } + return calc(string); +} + +void Parser::MakeParms(const char * str, uint32 npcid) { + char temp[100]; + memset(temp, 0, sizeof(temp)); + char temp2[100]; + memset(temp2, 0, sizeof(temp2)); + char temp3[100]; + memset(temp3, 0, sizeof(temp3)); + + int tmpfor = numtok(str, ',')+1; + for ( int i=0; i < tmpfor; i++) { + memset(temp2, 0, sizeof(temp2)); + strn0cpy(temp2, gettok(str, ',', i).c_str(), sizeof(temp2)); + snprintf(temp, sizeof(temp), "param%s.%d", itoa(i+1 ,temp3, 10),npcid); + AddVar(temp, temp2); + } +} + +int Parser::GetItemCount(string itemid, uint32 npcid) +{ + string temp; + int a=0; + for (int i=1;i<5;i++) + { + temp = (string)"item" + (string)itoa(i); + if (!GetVar(temp,npcid).compare(itemid)) + a++; + temp=""; + } + return a; +} + +int Parser::HasQuestFile(uint32 npcid) +{ + int32 qstID = GetNPCqstID(npcid); + int success=1; + if (qstID==-1) + success = LoadScript(npcid, zone->GetShortName()); + if (!success) + return false; + +return true; +} + +void Parser::Event(QuestEventID event, uint32 npcid, const char * data, NPC* npcmob, Mob* mob, uint32 extradata) { + if (npcid == 0) + return; + if(event >= _LargestEventID) + return; + int32 qstID = GetNPCqstID(npcid); + int success=1; + if (qstID==-1) + success = LoadScript(npcid, zone->GetShortName(),mob); + if (!success) + return; + else + qstID = GetNPCqstID(npcid); + +// SCORPIOUS2K - load global variables + + if (npcmob->GetQglobal()) + { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + char tmpname[65]; + int charid=0; + + if (mob && mob->IsClient()) // some events like waypoint and spawn don't have a player involved + { + charid=mob->CastToClient()->CharacterID(); + } + + else + { + charid=0-npcmob->GetNPCTypeID(); // make char id negative npc id as a fudge + } + + AddVar("charid.g",itoa(charid)); + + database.RunQuery(query, MakeAnyLenString(&query, + "SELECT name,value FROM quest_globals WHERE (npcid=%i || npcid=0) && (charid=%i || charid=0) && (zoneid=%i || zoneid=0) && expdate >= unix_timestamp(now())", + npcmob->GetNPCTypeID(),charid,zone->GetZoneID()), errbuf, &result); + printf("%s\n",query); + printf("%s\n",errbuf); + if (result) + { + printf("Loading global variables for %s\n",npcmob->GetName()); + while ((row = mysql_fetch_row(result))) + { + sprintf(tmpname,"%s.g",row[0]); + AddVar(tmpname, row[1]); + } + mysql_free_result(result); + } + if (query) + { + safe_delete_array(query); + query=0; + } + } + + if (event == EVENT_TIMER) + { + AddVar("timername.g",data); + } + if (event == EVENT_SIGNAL) + { + AddVar("signal.g",data); + } + uint8 fac = 0; + if (mob && mob->IsClient()) { + AddVar("uguild_id.g", itoa(mob->CastToClient()->GuildID())); + AddVar("uguildrank.g", itoa(mob->CastToClient()->GuildRank())); + } + + if (mob && npcmob && mob->IsClient() && npcmob->IsNPC()) { + Client* client = mob->CastToClient(); + NPC* npc = npcmob->CastToNPC(); + + // Need to figure out why one of these casts would fail.. + if (client && npc) { + fac = client->GetFactionLevel(client->GetID(), npcmob->GetID(), client->GetRace(), client->GetClass(), DEITY_AGNOSTIC, npc->GetPrimaryFaction(), npcmob); + } + else if (!client) { + cerr << "WARNING: cast failure on mob->CastToClient()" << endl; + } + else if (!npc) { + cerr << "WARNING: cast failure on npcmob->CastToNPC()" << endl; + } + } + if (mob) { + AddVar("name.g", mob->GetName()); + AddVar("race.g", GetRaceName(mob->GetRace())); + AddVar("class.g", GetEQClassName(mob->GetClass())); + AddVar("ulevel.g", itoa(mob->GetLevel())); + AddVar("userid.g", itoa(mob->GetID())); + } + if (npcmob) + { + AddVar("mname.g",npcmob->GetName()); + } + + if (fac) { + AddVar("faction.g", itoa(fac)); + } + + if (zone) { +// SCORPIOUS2K- added variable zoneid + AddVar("zoneid.g",itoa(zone->GetZoneID())); + AddVar("zoneln.g",zone->GetLongName()); + AddVar("zonesn.g",zone->GetShortName()); + } + + string temp; +#if Parser_DEBUG>10 + printf("Data: %s,Id: %i\n",data,npcid); +#endif + switch (event) { + case EVENT_SAY: { + MakeVars(data, npcid); + npcmob->FaceTarget(mob); + SendCommands("event_say", qstID, npcmob, mob); + break; + } + case EVENT_TIMER: { + SendCommands("event_timer", qstID, npcmob, mob); + break; + } + case EVENT_DEATH: { + SendCommands("event_death", qstID, npcmob, mob); + break; + } + case EVENT_ITEM: { + npcmob->FaceTarget(mob); + SendCommands("event_item", qstID, npcmob, mob); + break; + } + case EVENT_SPAWN: { + SendCommands("event_spawn", qstID, npcmob, mob); + break; + } + case EVENT_ATTACK: { + SendCommands("event_attack", qstID, npcmob, mob); + break; + } + case EVENT_COMBAT: { + SendCommands("event_combat", qstID, npcmob, mob); + break; + } + case EVENT_SLAY: { + SendCommands("event_slay", qstID, npcmob, mob); + break; + } + case EVENT_NPC_SLAY: { + SendCommands("event_npc_slay", qstID, npcmob, mob); + break; + } + case EVENT_WAYPOINT_ARRIVE: { + temp = "wp." + (string)itoa(npcid); + AddVar(temp,data); + SendCommands("event_waypoint_arrive", qstID, npcmob, mob); + break; + } + case EVENT_WAYPOINT_DEPART: { + temp = "wp." + (string)itoa(npcid); + AddVar(temp,data); + SendCommands("event_waypoint_depart", qstID, npcmob, mob); + break; + } + case EVENT_SIGNAL: { + SendCommands("event_signal", qstID, npcmob, mob); + break; + } + case EVENT_AGGRO: { + SendCommands("event_aggro", qstID, npcmob, mob); + break; + } + case EVENT_ENTER: { + SendCommands("event_enter", qstID, npcmob, mob); + break; + } + case EVENT_EXIT: { + SendCommands("event_exit", qstID, npcmob, mob); + break; + } + case EVENT_AGGRO_SAY: { + MakeVars(data, npcid); + SendCommands("event_aggro_say", qstID, npcmob, mob); + break; + } + default: { + // should we do anything here? + break; + } + } + DelChatAndItemVars(npcid); + +} + +Parser::Parser() : DEFAULT_QUEST_PREFIX("default") { + MainList.clear(); + pMaxNPCID = database.GetMaxNPCType(); + /*pNPCqstID = new int32[pMaxNPCID+1]; + for (uint32 i=0; i pMaxNPCID) + return false; + + return (bool) (FindNPCQuestID(iNPCID) != 0); +} +uint32 Parser::FindNPCQuestID(int32 npcid){ + for(uint32 i=0;i pMaxNPCID) + return false; + uint32 idx = FindNPCQuestID(iNPCID); + if(idx) + pNPCqstID[idx] = iValue; + else{ + idx=AddNPCQuestID(iNPCID); + pNPCqstID[idx] = iValue; + } + return true; +} + +int32 Parser::GetNPCqstID(uint32 iNPCID) { + if (iNPCID > pMaxNPCID || iNPCID == 0) + return -1; + if(uint32 idx=FindNPCQuestID(iNPCID)) + return pNPCqstID[idx]; + else + return -1; +} + +void Parser::ClearCache() { +#if Parser_DEBUG >= 2 + cout << "Parser::ClearCache" << endl; +#endif + //for (uint32 i=0; iEvent.begin(); + if ( p->npcid == npcid ) { + if(mob && mob->IsClient()) + quest_manager.StartQuest(npcmob, mob->CastToClient()); + else + quest_manager.StartQuest(npcmob, NULL); + while (listIt2 != p->Event.end()) + { + pp = *listIt2; + if (pp && !strcmp(strlwr(pp->event.c_str()),strlwr(event))) { +#if Parser_DEBUG>10 + printf("PP command: %s\n",pp->command.c_str()); +#endif + ParseCommands(pp->command,0,0,npcid,npcmob,mob); + } + listIt2++; + } + quest_manager.EndQuest(); + return; + } + listIt++; + } +} + +void Parser::scanformat(char *string, const char *format, char arg[10][1024]) +{ + int increment_arglist = 0; + int argnum = 0; + int i = 0; + char lookfor = 0; + + // someone forgot to set string or format + if(!format) + return; + if(!string) + return; + + for(;;) + { + // increment while they're the same (and not NULL) + while(*format && *string && *format == *string) { + format++; + string++; + } + + // format string is gone + if(!format) + break; + // string is gone while the format string is still there (ERRor) + if(!string) + return; + + // the format string HAS to be equal to ÿ or else things are messed up + if(*format != 'ÿ') + return; + + format++; + lookfor = *format; // copy until we find char after 'y' + format++; + + if(!lookfor) + break; + + // start on a new arg + if(increment_arglist) { + arg[argnum][i] = 0; + argnum++; + } + + increment_arglist = 1; // we found the first crazy y + i = 0; // reset index + + while(*string && *string != lookfor) + arg[argnum][i++] = *string++; + string++; + } + + // final part of the string + if(increment_arglist) { + arg[argnum][i] = 0; + argnum++; + i = 0; + } + + while(*string) + arg[argnum][i++] = *string++; + + arg[argnum][i] = 0; +} + +void Parser::ClearEventsByNPCID(uint32 iNPCID) { + list::iterator iterator = MainList.begin(); + Events* p; + while (iterator != MainList.end()) + { + p = *iterator; + if (p->npcid == iNPCID) { + p->Event.clear(); + return; + } + iterator++; + } +} + +void Parser::ClearAliasesByNPCID(uint32 iNPCID) { +/* MyListItem* Ptr = AliasList.First; + MyListItem* next = 0; + while (Ptr) { + next = Ptr->Next; + if ( (uint32)Ptr->Data->npcid == iNPCID) { + AliasList.DeleteItemAndData(Ptr); + } + Ptr = next; + }*/ +} + +void Parser::ExCommands(string o_command, string parms, int argnums, uint32 npcid, Mob* other, Mob* mob ) +{ + char arglist[10][1024]; + //Work out the argument list, if there needs to be one +#if Parser_DEBUG>10 + printf("Parms: %s\n", parms.c_str()); +#endif + if (argnums > 1) { + string buffer; + string::iterator iterator = parms.begin(); + int quote=0,alist=0,ignore=0; + while (1) { + if (*iterator == '"') { + if (quote) quote--; + else { quote++; if (buffer.empty()) ignore=1; } + } + if (((*iterator != '"' && *iterator != ',' && *iterator != ' ' && *iterator != ')') || quote) && !ignore) { + buffer+=*iterator; + } + if (ignore && *iterator == '"')ignore--; + if ((*iterator == ',' && !quote)) { + strcpy(arglist[alist],buffer.c_str()); + alist++; + buffer=""; + } + if (iterator == parms.end()) + { + strcpy(arglist[alist],buffer.c_str()); + alist++; + break; + } + iterator++; + } + } + else { + string::iterator iterator = parms.begin(); + if (*iterator == '"') + { + int quote=0,ignore=0; //once=0 + string tmp; + while (iterator != parms.end()) + { + if (*iterator == '"') + { + if (quote)quote--; + else quote++; + ignore++; + } + + if (!ignore && (quote || (*iterator != '"'))) + tmp+=*iterator; + + else if (*iterator == '"' && ignore) + ignore--; + iterator++; + } + strcpy(arglist[0],tmp.c_str()); + } + else + strcpy(arglist[0],parms.c_str()); + } +#if Parser_DEBUG>10 + printf("After: %s\n", arglist[0]); +#endif + + char command[256]; + strncpy(command, o_command.c_str(), 255); + command[255] = 0; + char *cptr = command; + while(*cptr != '\0') { + if(*cptr >= 'A' && *cptr <= 'Z') + *cptr = *cptr + ('a' - 'A'); + *cptr++; + } + + + if (!strcmp(command,"write")) { + quest_manager.write(arglist[0], arglist[1]); + } + else if (!strcmp(command,"me")) { +// MYRA - fixed comma bug for me command + quest_manager.me(parms.c_str()); +//end Myra + } + else if (!strcmp(command,"spawn") || !strcmp(command,"spawn2")) + { + + float hdng; + if (!strcmp(command,"spawn")) + { + hdng=mob->CastToClient()->GetHeading(); + } + else + { + hdng=atof(arglist[6]); + } + quest_manager.spawn2(atoi(arglist[0]), atoi(arglist[1]), 0, + atof(arglist[3]), atof(arglist[4]), atof(arglist[5]), hdng); + } + else if (!strcmp(command,"unique_spawn")) + { + + float hdng; + hdng=mob->CastToClient()->GetHeading(); + quest_manager.unique_spawn(atoi(arglist[0]), atoi(arglist[1]), 0, + atof(arglist[3]), atof(arglist[4]), atof(arglist[5]), hdng); + } + else if (!strcmp(command,"echo")) { + quest_manager.echo(atoi(arglist[0]), parms.c_str()); + } + else if (!strcmp(command,"summonitem")) { + quest_manager.summonitem(atoi(arglist[0])); + } + else if (!strcmp(command,"setstat")) { + quest_manager.setstat(atoi(arglist[0]), atoi(arglist[1])); + } + else if (!strcmp(command,"setanim")) { + quest_manager.setanim(atoi(arglist[0]), atoi(arglist[1])); + } + else if (!strcmp(command,"castspell")) { + quest_manager.castspell(atoi(arglist[1]), atoi(arglist[0])); + } + else if (!strcmp(command,"selfcast")) { + quest_manager.selfcast(atoi(arglist[0])); + } + else if (!strcmp(command,"addloot")) {//Cofruben: add an item to the mob. + quest_manager.addloot(atoi(arglist[0]),atoi(arglist[1])); + } + else if (!strcmp(command,"zone")) { + quest_manager.Zone(arglist[0]); + } + else if (!strcmp(command,"settimer")) { + quest_manager.settimer(arglist[0], atoi(arglist[1])); + } + else if (!strcmp(command,"say")) { + quest_manager.say(parms.c_str()); + } + else if (!strcmp(command,"stoptimer")) { + quest_manager.stoptimer(arglist[0]); + } + else if (!strcmp(command,"emote")) { + quest_manager.emote(parms.c_str()); + } + else if (!strcmp(command,"shout2")) { + quest_manager.shout2(parms.c_str()); + } + else if (!strcmp(command,"shout")) { + quest_manager.shout(parms.c_str()); + } + else if (!strcmp(command,"gmsay")) { + quest_manager.shout2(parms.c_str()); + } + else if (!strcmp(command,"depop")) { + quest_manager.depop(atoi(arglist[0])); + } + else if (!strcmp(command,"settarget")) { + quest_manager.settarget(arglist[0], atoi(arglist[1])); + } + else if (!strcmp(command,"follow")) { + quest_manager.follow(atoi(arglist[0]), atoi(arglist[1])); + } + else if (!strcmp(command,"sfollow")) { + quest_manager.sfollow(); + } + else if (!strcmp(command,"changedeity")) { + quest_manager.changedeity(atoi(arglist[0])); + } + else if (!strcmp(command,"exp")) { + quest_manager.exp(atoi(arglist[0])); + } + else if (!strcmp(command,"level")) { + quest_manager.level(atoi(arglist[0])); + } + else if (!strcmp(command,"traindisc")) { + quest_manager.traindisc(atoi(arglist[0])); + } + else if (!strcmp(command,"safemove")) { + quest_manager.safemove(); + } + else if (!strcmp(command,"rain")) { + quest_manager.rain(atoi(arglist[0])); + } + else if (!strcmp(command,"snow")) { + quest_manager.snow(atoi(arglist[0])); + } + else if (!strcmp(command,"surname")) { + quest_manager.surname(arglist[0]); + } + else if (!strcmp(command,"permaclass")) { + quest_manager.permaclass(atoi(arglist[0])); + } + else if (!strcmp(command,"permarace")) { + quest_manager.permarace(atoi(arglist[0])); + } + else if (!strcmp(command,"permagender")) { + quest_manager.permagender(atoi(arglist[0])); + } + else if (!strcmp(command,"scribespells")) { + quest_manager.scribespells(atoi(arglist[0])); + } + else if (!strcmp(command,"traindiscs")) { + quest_manager.traindiscs(atoi(arglist[0])); + } + else if (!strcmp(command,"givecash")) { + quest_manager.givecash(atoi(arglist[0]), atoi(arglist[1]), atoi(arglist[2]), atoi(arglist[3])); + } + else if (!strcmp(command,"pvp")) { + quest_manager.pvp(arglist[0]); + } + else if (!strcmp(command,"movepc")) { + quest_manager.movepc((atoi(arglist[0])),(atof(arglist[1])),(atof(arglist[2])),(atof(arglist[3])),(atof(arglist[4]))); + } + else if (!strcmp(command,"gmmove")) { + quest_manager.gmmove(atof(arglist[0]), atof(arglist[1]), atof(arglist[2])); + } + else if (!strcmp(command,"movegrp")) { + quest_manager.movegrp((atoi(arglist[0])),(atof(arglist[1])),(atof(arglist[2])),(atof(arglist[3]))); + } + else if (!strcmp(command,"doanim")) { + quest_manager.doanim(atoi(arglist[0])); + } + else if (!strcmp(command,"addskill")) { + quest_manager.addskill(atoi(arglist[0]), atoi(arglist[1])); + } + else if (!strcmp(command,"setlanguage")) { + quest_manager.setlanguage(atoi(arglist[0]), atoi(arglist[1])); + } + else if (!strcmp(command,"setskill")) { + quest_manager.setskill(atoi(arglist[0]), atoi(arglist[1])); + } + else if (!strcmp(command,"setallskill")) { + quest_manager.setallskill(atoi(arglist[0])); + } + else if (!strcmp(command,"attack")) { + quest_manager.attack(arglist[0]); + } + else if (!strcmp(command,"save")) { + quest_manager.save(); + } + /*else if (!strcmp(command,"flagcheck")) { + quest_manager.flagcheck(atoi(arglist[0]), atoi(arglist[1])); + }*/ + else if (!strcmp(command,"faction")) { + quest_manager.faction(atoi(arglist[0]), atoi(arglist[1]), atoi(arglist[2])); + } + else if (!strcmp(command,"setsky")) { + quest_manager.setsky(atoi(arglist[0])); + } + else if (!strcmp(command,"setguild")) { + quest_manager.setguild(atoi(arglist[0]), atoi(arglist[1])); + } + else if (!strcmp(command,"settime")) { + quest_manager.settime(atoi(arglist[0]), atoi(arglist[1])); + } + else if (!strcmp(command,"itemlink")) { + quest_manager.itemlink(atoi(arglist[0])); + } + else if (!strcmp(command,"signal")) { + quest_manager.signal(atoi(arglist[0])); + } + else if (!strcmp(command,"setglobal")) { + quest_manager.setglobal(arglist[0], arglist[1], atoi(arglist[2]), arglist[3]); + } + else if (!strcmp(command,"targlobal")) { + quest_manager.targlobal(arglist[0], arglist[1], arglist[2], atoi(arglist[3]), atoi(arglist[4]), atoi(arglist[5])); + } + else if (!strcmp(command,"ding")) { + quest_manager.ding(); + } + else if (!strcmp(command,"delglobal")) { + quest_manager.delglobal(arglist[0]); + } + else if (!strcmp(command,"rebind")) { + quest_manager.rebind((atoi(arglist[0])),(atof(arglist[1])),(atof(arglist[2])),(atof(arglist[3]))); + } + else if (!strcmp(command,"stop")) { + quest_manager.stop(); + } + else if (!strcmp(command,"pause")) { + quest_manager.pause(atoi(arglist[0])); + } + else if (!strcmp(command,"moveto")) { + quest_manager.moveto(atof(arglist[0]), atof(arglist[1]), atof(arglist[2]), atof(arglist[3]), atoi(arglist[4])); + } + else if (!strcmp(command,"pathto")) { + quest_manager.pathto(atof(arglist[0]), atof(arglist[1]), atof(arglist[2])); + } + else if (!strcmp(command,"showpath")) { + quest_manager.showpath(atof(arglist[0]), atof(arglist[1]), atof(arglist[2])); + } + else if (!strcmp(command,"showgrid")) { + quest_manager.showgrid(atoi(arglist[0])); + } + else if (!strcmp(command,"toggle_spawn_event")) { + quest_manager.toggle_spawn_event(atoi(arglist[0]),(atoi(arglist[1])!=0),(atoi(arglist[2])!=0)); + } + else if (!strcmp(command,"spawn_condition")) { + quest_manager.spawn_condition(arglist[0], 0, atoi(arglist[1]), atoi(arglist[2])); + } + else if (!strcmp(command,"resume")) { + quest_manager.resume(); + } + else if (!strcmp(command,"start")) { + quest_manager.start(atoi(arglist[0])); + } + else if (!strcmp(command,"addldonpoints")) { + quest_manager.addldonpoints(atoi(arglist[0]), atoi(arglist[1])); + } + else if (!strcmp(command,"setnexthpevent")) { + quest_manager.setnexthpevent(atoi(arglist[0])); + } + else if (!strcmp(command,"setnextinchpevent")) { + quest_manager.setnextinchpevent(atoi(arglist[0])); + } + else if (!strcmp(command,"clear_zone_flag")) { + quest_manager.clear_zone_flag(atoi(arglist[0])); + } + else if (!strcmp(command,"set_zone_flag")) { + quest_manager.set_zone_flag(atoi(arglist[0])); + } + else if (!strcmp(command,"set_proximity")) { + float v1 = atof(arglist[4]); + float v2 = atof(arglist[5]); + if(v1 == v2) //omitted, or wrong, either way, skip them + quest_manager.set_proximity(atof(arglist[0]), atof(arglist[1]), atof(arglist[2]), atof(arglist[3])); + else + quest_manager.set_proximity(atof(arglist[0]), atof(arglist[1]), atof(arglist[2]), atof(arglist[3]), v1, v2); + } + else if (!strcmp(command,"clear_proximity")) { + quest_manager.clear_proximity(); + } + else if (!strcmp(command,"respawn")) + { + quest_manager.respawn(atoi(arglist[0]), atoi(arglist[1])); + } + else + printf("\nUnknown perl function used:%s",command); + + +} + +int Parser::LoadScript(int npcid, const char * zone, Mob* activater) +{ + SetNPCqstID(npcid, npcid); + ClearEventsByNPCID(npcid); + ClearAliasesByNPCID(npcid); + + //string strnpcid = ("default"); + string strnpcid = (DEFAULT_QUEST_PREFIX); + + if (npcid) + strnpcid = itoa(npcid); + string filename; + filename = "./quests/" + (string)zone + "/" + (string)strnpcid + ".qst"; + string line,buffer,temp; + ifstream file( filename.c_str() ); + if (!file) + { + if (npcid) { + SetNPCqstID(npcid, 0); + LoadScript(0, zone); + } + else + SetNPCqstID(0, -1); + } + + int quote=0,ignore=0,bracket=0,line_num=0,paren=0; + EventList* event1 = new EventList; + Events * NewEventList = new Events; + while (file && !file.eof()) + { + getline(file,line); + string::iterator iterator = line.begin(); + while (*iterator) + { + if (iterator[0] == '/' && iterator[1] == '/') break; + if (!ignore && *iterator == '/' && iterator[1] == '*') { ignore++; iterator++; iterator++; } + if (*iterator == '*' && iterator[1] == '/') { ignore--; iterator++; iterator++; } + if (!ignore && (strchr(charIn,*iterator) || quote || paren)) + buffer+=*iterator; + if (!ignore) + { + if (*iterator == '{') + { + bracket++; + if (bracket == 1) + { + event1 = new EventList; + NewEventList->npcid = npcid; + buffer.replace(buffer.length()-1,buffer.length(),""); + event1->event = buffer; + buffer=""; + } + } + if (*iterator == '}') + { + bracket--; + if (bracket == 0) + { + buffer.replace(buffer.length()-1,buffer.length(),""); + int heh = ParseCommands(buffer,line_num,0,0,0,0,filename); + if (!heh){ + safe_delete_array(NewEventList); + return 0; + } + event1->command = buffer; + buffer=""; + NewEventList->Event.push_back(event1); + } + if (bracket==-1) + { + if(activater && activater->IsClient()) + activater->CastToClient()->Message(10,"Line: %d,File: %s | error C0006: syntax error : too many ')'s",line_num,filename.c_str()); + return 0; + } + } + if (*iterator == '"')if(quote)quote--;else quote++; + if (*iterator == '(')paren++; + if (*iterator == ')')paren--; + } + iterator++; + } + line_num++; + + } + MainList.push_back(NewEventList); + return 1; +} + + +void Parser::Replace(string& string1, string repstr, string rep, int all) { + while (string1.find(repstr.c_str()) != string::npos) { + string1.replace(string1.find(repstr.c_str()),repstr.length(),rep.c_str()); + if (!all) + break; + } +} + +string Parser::GetVar(string varname, uint32 npcid) +{ + list::iterator iterator = varlist.begin(); + vars * p; + string checkfirst; + string checksecond; + checkfirst = varname + (string)"." + (string)itoa(npcid); + checksecond = varname + (string)".g"; + + while(iterator != varlist.end()) + { + p = *iterator; + if (!strcasecmp(p->name.c_str(), checkfirst.c_str()) || !strcasecmp(p->name.c_str(),checksecond.c_str())) + { +printf("GetVar(%s) = '%s'\n", varname.c_str(), p->value.c_str()); + return p->value; + } + iterator++; + } + checkfirst=""; + checkfirst = "NULL"; + return checkfirst; +} + +void Parser::DeleteVar(string name) +{ + list::iterator iterator = varlist.begin(); + vars* p; + while(iterator != varlist.end()) + { + p = *iterator; + if (!p->name.compare(name)) + { + varlist.erase(iterator); + return; + } + iterator++; + } +} + +void Parser::DelChatAndItemVars(uint32 npcid) +{ +// MyListItem * Ptr; + string temp; + int i=0; + for (i=0;i<10;i++) + { + temp = (string)itoa(i) + "." + (string)itoa(npcid); + DeleteVar(temp); + temp = (string)itoa(i) + "-." + (string)itoa(npcid); + DeleteVar(temp); + } + for (i=1;i<5;i++) + { + temp = "item"+(string)itoa(i) + "." + (string)itoa(npcid); + DeleteVar(temp); + temp = "item"+(string)itoa(i) + ".stack." + (string)itoa(npcid); + DeleteVar(temp); + } +} + +void Parser::AddVar(string varname, string varval) +{ + list::iterator iterator = varlist.begin(); + vars* p; + while(iterator != varlist.end()) + { + p = *iterator; + if (!p->name.compare(varname)) + { + p->value=""; + p->value = varval; + return; + } + iterator++; + } + vars * newvar = new vars; + newvar->name = varname; + newvar->value = varval; + varlist.push_back(newvar); +} + +void Parser::HandleVars(string varname, string varparms, string& origstring, string format, uint32 npcid, Mob* mob) +{ + string tempvar; + tempvar = GetVar(varname,npcid); + char arglist[10][1024]; + string::iterator iterator = varparms.begin(); + string buffer; + int quote=0; + int alist=0; + while (*iterator) + { + if (*iterator != '"' && *iterator != ',' && *iterator != ' ' || (quote && *iterator != '"')) + buffer+=*iterator; + if (*iterator == '"') + { + if (quote)quote--; + else quote++; + } + if (*iterator == ',' && !quote) + { + strcpy(arglist[alist],buffer.c_str()); + alist++; + buffer=""; + } + iterator++; + } + strcpy(arglist[alist],buffer.c_str()); + if (!strcmp(strlwr((const char*)varname.c_str()),"mid")) { + int pos=0; + int one = atoi(arglist[1]); + int two = atoi(arglist[2]); + string buffer2; + string find = arglist[0]; + string::iterator iterator = find.begin(); + while (*iterator) + { + pos++; + if (pos>=one) + buffer2+=*iterator; + if (pos==two) + break; + iterator++; + } + Replace(origstring,format,buffer2.c_str()); + } + else if (!strcmp(strlwr((const char*)varname.c_str()),"+")) { + string temp; + temp = (string)" "+format+(string)" "; + Replace(origstring, temp, " REPLACETHISSHIT ",1); + } + else if (!strcmp(strlwr((const char*)varname.c_str()),"replace")) { + string temp; + temp = (string)arglist[0]; + Replace(temp, arglist[1], arglist[2],1); + Replace(origstring, format, temp); + } + else if (!strcmp(strlwr(varname.c_str()),"itemcount")) { + string temp; + int o=0; + o = GetItemCount(varparms,npcid); + temp = (string)itoa(o); + Replace(origstring,format,temp,1); + } + else if (!strcmp(strlwr(varname.c_str()),"calc")) { + Replace(origstring,format,itoa(pcalc(varparms.c_str()))); + } + else if (!strcmp(strlwr(varname.c_str()),"status") && mob && mob->IsClient()) { + Replace(origstring,format,itoa(mob->CastToClient()->Admin())); + } + else if (!strcmp(strlwr(varname.c_str()),"hasitem") && mob && mob->IsClient()) { + int has=0; + for (int i=0; i<=30;i++) { + if (mob->CastToClient()->GetItemIDAt(i) == (uint32) atoi(varparms.c_str())) { + Replace(origstring,format,"true"); + has = 1; + break; + } + } + if (!has) + Replace(origstring,format,"false"); + } + else if (!strcmp(strlwr((const char*)varname.c_str()),"read")) { + ifstream file(arglist[0]); + if (file) + { + string line; + int index=0,stop=atoi(arglist[1]); + while (!file.eof()) + { + getline(file,line); + if (index == stop)break; + index++; + } + Replace(origstring,format,line); + } + } + else if (!strcmp(strlwr((const char*)varname.c_str()),"npc_status")) { + Mob * tmp; + if (!atoi(varparms.c_str())) { + tmp = entity_list.GetMob(varparms.c_str()); + } + else { + tmp = entity_list.GetMobByNpcTypeID(atoi(varparms.c_str())); + } + if (tmp && tmp->GetHP() > 0) Replace(origstring,format,"up"); + else Replace(origstring,format,"down"); + } + else if (!strcmp(strlwr((const char*)varname.c_str()),"strlen")) { + Replace(origstring,format,itoa(varparms.length()-1)); + } + else if (!strcmp(strlwr((const char*)varname.c_str()),"chr")) { + char temp[4]; + memset(temp, 0x0, 4); + temp[0] = atoi(varparms.c_str()); + Replace(origstring,format,temp); + } + //used_pawn - random implementation. + else if (!strcmp(strlwr((const char*)varname.c_str()),"random")) { + Replace(origstring,format,itoa(MakeRandomInt(0, varparms[0]-1))); + } + else if (!strcmp(strlwr((const char*)varname.c_str()),"asc")) { + Replace(origstring,format,itoa(varparms[0])); + } + else if (!strcmp(strlwr((const char*)varname.c_str()),"gettok")) { + Replace(origstring,format,gettok(arglist[0],arglist[1][0],atoi(arglist[2]))); + } + else { + Replace(origstring,format,tempvar); + } + gClient = 0; +} + +void Parser::ParseVars(string& text, uint32 npcid, Mob* mob) +{ + if (text.find("$") == string::npos && text.find("%") == string::npos) + return; + string buffer2; + string fname; + string parms; + while (text.find("%") != string::npos) + { + string temp; + temp = (string)text.substr(text.find("%")).c_str(); + string::iterator iterator = temp.begin(); + string buffer; + while (*iterator) + { + if (!strrchr(notin,*iterator)) + buffer+=*iterator; + else + { + HandleVars(buffer,0,text,buffer,npcid,mob); + Replace(text,buffer,"testing",0); + break; + } + iterator++; + } + } + while (text.find("$") != string::npos) + { + string temp; + temp = (string)text.substr(text.rfind("$")).c_str(); + string::iterator iterator = temp.begin(); + int paren=0; + int fin=0; + string buffer; + while (iterator != temp.end()) + { + if (!strrchr(notin,*iterator) || paren) + { + if (*iterator != '(' && *iterator != ')') + buffer+=*iterator; + } + else + { + buffer.replace(0,1,"",0); + HandleVars(buffer,"",text,buffer2,npcid,mob); + buffer=""; + buffer2=""; + break; + } + buffer2+=*iterator; + if (*iterator == '(') + { + paren++; + if (paren == 1) + { + fname = buffer; + buffer=""; + } + } + if (*iterator == ')') + { + paren--; + if (paren == 0) + { + parms = buffer; + fname.replace(0,1,"",0); + HandleVars(fname,parms,text,buffer2,npcid,mob); + buffer=""; + buffer2=""; + parms=""; + fname=""; + fin=1; + break; + } + } + iterator++; + } + } +} + +/* +char * fixstring(char * string) +{ + char tmp[255]; + memset(tmp,0x0,255); + + static char tmp2[255]; + memset(tmp2,0x0,255); + int quote=0; + int o=0; + strcpy(tmp,string); + uint32 len = strlen(tmp); + for (uint32 i=0;i10 + printf("compare1: %s,sign: %s, compare2: %s\n",compare1.c_str(),sign.c_str(),compare2.c_str()); +#endif + if (!strcmp(sign.c_str(),"==")) { + if (strcmp(strlwr((const char*)compare1.c_str()),strlwr((const char*)compare2.c_str()))) + return 0; + } + else if (!strcmp(sign.c_str(),"!=")) { + if (!strcmp(strlwr((const char*)compare1.c_str()),strlwr((const char*)compare2.c_str()))) + return 0; + } + else if (!strcmp(sign.c_str(),"=~")) { + if (!strstr(strlwr(compare1.c_str()).c_str(),strlwr(compare2.c_str()).c_str())) + return 0; + } + else if (!strcmp(sign.c_str(),"!~")) { + if (strstr(strlwr(compare1.c_str()).c_str(),strlwr(compare2.c_str()).c_str())) + return 0; + } + else if (!strcmp(sign.c_str(),"<")) { + if (atoi(compare1.c_str()) > atoi(compare2.c_str()) || atoi(compare1.c_str()) == atoi(compare2.c_str())) + return 0; + } + else if (!strcmp(sign.c_str(),">")) { + if (atoi(compare1.c_str()) < atoi(compare2.c_str()) || atoi(compare1.c_str()) == atoi(compare2.c_str())) + return 0; + } + else if (!strcmp(sign.c_str(),"<=")) { + if (atoi(compare1.c_str()) > atoi(compare2.c_str()) || atoi(compare1.c_str()) != atoi(compare2.c_str())) + return 0; + } + else if (!strcmp(sign.c_str(),">=")) { + if (atoi(compare1.c_str()) < atoi(compare2.c_str()) || atoi(compare1.c_str()) != atoi(compare2.c_str())) + return 0; + } + return 1; +} + +int Parser::ParseIf(string text) +{ + string::iterator iterator = text.begin(); + string com1,com2,sign,next,buffer; + while (*iterator) + { + if (!strchr(notin,*iterator)) + buffer+=*iterator; + if (*iterator == '=' || *iterator == '!' || *iterator == '~') + { + sign+=*iterator; + if (sign.length() == 1) + { + com1 = buffer; + buffer=""; + next=""; + } + } + if ((*iterator == '<' || *iterator == '>') && iterator[1] != '=') + { + sign+=*iterator; + if (sign.length() == 1) + { + com1 = buffer; + buffer=""; + next=""; + } + iterator++; + } + if (*iterator == '&' || *iterator == '|') + { + next+=*iterator; + if (next.length() == 1) + { + com2 = buffer; + buffer=""; + } + if (next.length() == 2) + { + if (!DoCompare(com1,sign,com2) && strcmp(next.c_str(),"||")) + return 0; + com1=""; + sign=""; + com2=""; + } + } + iterator++; + if (iterator == text.end()) { + com2 = buffer; + //com2.replace(0,1,""); + buffer=""; + if (!DoCompare(com1,sign,com2)) + return 0; + } + } + return 1; +} + +int Parser::ParseCommands(string text, int line, int justcheck, uint32 npcid, Mob* other, Mob* mob, std::string filename) +{ + string buffer,command,parms,temp,temp2; + temp2 = text; + ParseVars(temp2,npcid,mob); + int bracket=0,paren=0,lastif=0,last_finished=0,quote=0,escape=0,ignore=0,argnums=0,argit=1; + string::iterator iterator = temp2.begin(); + while (iterator != temp2.end()) + { + if (*iterator == '\\' && !escape) { + escape++; + } + //"`~1234567890-=!@#$%^&*_+qwertyuiop[]asdfghjkl'zxcvbnm,./QWERTYUIOP|ASDFGHJKL:ZXCVBNM<>?\"\\" + if (!ignore && *iterator != ')' && (strchr(charIn3,*iterator) || quote || escape || paren)) + { + buffer+=*iterator; + } + if (*iterator == '"' && !escape && !ignore) + if (quote)quote--; + else quote++; + + if (*iterator == ',' && !ignore && !quote && !escape && paren) + argit++; + + if (*iterator == '(' && !ignore && !quote && !escape) + { + paren++; + if (paren == 1) + { + if (last_finished) + { + if(mob && mob->IsClient()) + mob->CastToClient()->Message(10,"Line: %d,File: %s | error C0008: syntax error : missing ';' before function '%s'", line, filename.c_str(), command.c_str()); + return 0; + } + command = buffer; + if(!strcmp(strlwr(command.c_str()),"break") && other) + return 1; + buffer=""; + argnums = GetArgs(command); +#if Parser_DEBUG>10 + if(mob && mob->IsClient()) + mob->CastToClient()->Message(10,"Command: %s, Num Args: %i\n",command.c_str(),argnums); +#endif + if (argnums == -1) + { + if(mob && mob->IsClient()) + mob->CastToClient()->Message(10,"Line: %d,File: %s | error C0007: '%s' : Unknown function", line, filename.c_str(), command.c_str()); + return 0; + } + } + } + else if (*iterator == ')' && !ignore && !quote && !escape) + { + paren--; + if (paren == 0) + { + lastif=0,quote=0,escape=0,ignore=0; + parms = buffer; + buffer=""; + if (!strcmp(strlwr((const char*)command.c_str()),"if")) { last_finished=0; } + else { + last_finished=1; + } + } + if (paren<0) + { + if(mob && mob->IsClient()) + mob->CastToClient()->Message(10,"Line: %d,File: %s | error C0006: syntax error : too many ')'s",line,filename.c_str() ); + return 0; + } + } + else if (*iterator == '{' && !escape) + { + bracket++; + if (!ignore) { + if (!strcmp(strlwr((const char*)command.c_str()),"if")) { + lastif = ParseIf(parms); +#if Parser_DEBUG>10 + if(mob && mob->IsClient()) + mob->CastToClient()->Message(10,"Parms: %s\n",parms.c_str()); +#endif + if (!lastif) ignore=1; + else ignore=0; + } + } + } + else if (*iterator == '}' && !escape) + { + bracket--; + if (last_finished) + { + if(mob && mob->IsClient()) + mob->CastToClient()->Message(10,"Line: %d,File: %s | error C0008: syntax error : missing ';' before '}'", line,filename.c_str() ); + return 0; + } + if (bracket<0) + { + if(mob && mob->IsClient()) + mob->CastToClient()->Message(10,"Line: %d,File: %s | error C0006: syntax error : too many '}'s",line, filename.c_str() ); + return 0; + } + if (bracket == 0) + { + if (!ignore) lastif=1; + else lastif=0; + lastif=0,last_finished=0,quote=0,escape=0,ignore=0,argnums=0,argit=1; + } + } + else if (*iterator == ';' && !escape && !ignore && !quote) + { + if (last_finished) + { + last_finished=0; + if (argnums != 1 && argnums!=argit) + { + if(mob && mob->IsClient()) + mob->CastToClient()->Message(10,"Line: %d, File: %s | error C0001: '%s' : function does not take %d parameter(s)", line, filename.c_str(), command.c_str(), argit); + return 0; + } + if (!justcheck) + ExCommands((const char*)command.c_str(),(const char*)parms.c_str(),argnums, npcid, other, mob); + argit=1; + } + else { + if(mob && mob->IsClient()) + mob->CastToClient()->Message(10,"Line: %d,File: %s | error C0002: '%s' :syntax error : '(' %d '('s still not closed.", line, filename.c_str(), command.c_str(), paren); + } + } + if (escape) escape--; + iterator++; + } + if (last_finished) + { + if(mob && mob->IsClient()) + mob->CastToClient()->Message(10,"Line: %d,File: %s | error C0008: syntax error : missing ';' before '}'", line, filename.c_str() ); + return 0; + } + return 1; +} + +void Parser::ReloadQuests(bool with_timers) { + ClearCache(); +} + diff --git a/zone/parser.h b/zone/parser.h new file mode 100644 index 000000000..3e2cb539f --- /dev/null +++ b/zone/parser.h @@ -0,0 +1,124 @@ +#ifndef PARSER_H +#define PARSER_H + +#define Parser_MaxVars 1024 +#include "../common/timer.h" +#include +#include +#include "event_codes.h" +#include "QuestInterface.h" + + +struct EventList { + std::string event; + std::string command; +}; + +struct Events { +uint32 npcid; +std::list Event; +}; + +struct Alias { + int index; + uint32 npcid; + char name[100][100]; + char command[100][1024]; +}; + +struct vars { + std::string name; + std::string value; +}; + +struct command_list { + char command_name[100]; + int param_amount[17]; +}; + + +class Parser : public QuestInterface +{ +public: + Parser(); + virtual ~Parser(); + int mindex; + const std::string DEFAULT_QUEST_PREFIX; + + typedef list::iterator iter_events; + typedef list::iterator iter_eventlist; + std::list MainList; + std::list varlist; + std::list AliasList; + uint32 npcarrayindex; + + uint32 AddNPCQuestID(uint32 npcid); + uint32 FindNPCQuestID(int32 npcid); + int CheckAliases(const char * alias, uint32 npcid, Mob* npcmob, Mob* mob); + void ClearAliasesByNPCID(uint32 iNPCID); + void ClearCache(); + void ClearEventsByNPCID(uint32 iNPCID); + + void DelChatAndItemVars(uint32 npcid); + void DeleteVar(std::string name); + + void ExCommands(std::string command, std::string parms, int argnums, uint32 npcid, Mob* other, Mob* mob ); + + void GetCommandName(char * command1, char * arg); + int GetFreeID(); + int GetItemCount(std::string itemid, uint32 npcid); + int32 GetNPCqstID(uint32 iNPCID); + std::string GetVar(std::string varname, uint32 npcid); + + void HandleVars(std::string varname, std::string varparms, std::string& origstring, std::string format, uint32 npcid, Mob* mob); + + bool LoadAttempted(uint32 iNPCID); + void LoadCommands(const char * filename); + virtual int LoadScript(int npcid, const char * zone, Mob* activater=0); + + void MakeParms(const char * string, uint32 npcid); + void MakeVars(std::string text, uint32 npcid); + + int numtok(const char *text, char character); + + int ParseCommands(std::string text, int line, int justcheck, uint32 npcid, Mob* other, Mob* mob, std::string filename=string("none")); + int ParseIf(std::string text); + int pcalc(const char * string); + void ParseVars(std::string& text, uint32 npcid, Mob* mob); + + void Replace(std::string& string1, std::string repstr, std::string rep, int all=0); + + void scanformat(char *string, const char *format, char arg[10][1024]); + bool SetNPCqstID(uint32 iNPCID, int32 iValue); + char * strrstr(char* string, const char * sub); + virtual void SendCommands(const char * event, uint32 npcid, NPC* npcmob, Mob* mob); + + int HasQuestFile(uint32 npcid); + + //interface stuff + virtual void EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data) {} + virtual void EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data) {} + virtual void EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data) {} + virtual void EventItem(QuestEventID evt, Client *client, ItemInst *item, uint32 objid, uint32 extra_data) {} + virtual void EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data) {} + virtual bool HasQuestSub(uint32 npcid, const char *subname) { return HasQuestFile(npcid) != 0; } + virtual bool PlayerHasQuestSub(const char *subname) { return true; } + virtual bool GlobalPlayerHasQuestSub(const char *subname) { return true; } + virtual bool SpellHasQuestSub(uint32 spell_id, const char *subname) { return true; } + virtual bool ItemHasQuestSub(ItemInst *itm, const char *subname) { return true; } + virtual void AddVar(std::string varname, std::string varval); + virtual void ReloadQuests(bool with_timers = false); + virtual uint32 GetIdentifier() { return 0x04629fff; } + +private: + //void Event(int event, uint32 npcid, const char * data, Mob* npcmob, Mob* mob); + //changed - Eglin. more reasonable (IMHO) than changing every single referance to the global pointer. + //that's what you get for using globals! :) + virtual void Event(QuestEventID event, uint32 npcid, const char * data, NPC* npcmob, Mob* mob, uint32 extradata = 0); + + uint32 pMaxNPCID; + int32* pNPCqstID; +}; + +#endif + diff --git a/zone/pathing.cpp b/zone/pathing.cpp new file mode 100644 index 000000000..8c79cabf0 --- /dev/null +++ b/zone/pathing.cpp @@ -0,0 +1,2297 @@ +#include "../common/debug.h" +#include +#include +#include +#include +#include +#include +#include "pathing.h" +#include "watermap.h" +#include "../common/MiscFunctions.h" +#include "doors.h" +#include "client.h" +#include "zone.h" + +#ifdef _WINDOWS +#define snprintf _snprintf +#endif + +//#define PATHDEBUG +#define ABS(x) ((x)<0?-(x):(x)) + +extern Zone *zone; + +float VertexDistance(VERTEX a, VERTEX b) +{ + _ZP(Pathing_VertexDistance); + + float xdist = a.x - b.x; + float ydist = a.y - b.y; + float zdist = a.z - b.z; + return sqrtf(xdist * xdist + ydist * ydist + zdist * zdist); +} + +float VertexDistanceNoRoot(VERTEX a, VERTEX b) +{ + _ZP(Pathing_VertexDistanceNoRoot); + + float xdist = a.x - b.x; + float ydist = a.y - b.y; + float zdist = a.z - b.z; + return xdist * xdist + ydist * ydist + zdist * zdist; + +} + +PathManager* PathManager::LoadPathFile(const char* ZoneName) +{ + + FILE *PathFile = NULL; + + char LowerCaseZoneName[64]; + + char ZonePathFileName[256]; + + PathManager* Ret = NULL; + + strn0cpy(LowerCaseZoneName, ZoneName, 64); + + strlwr(LowerCaseZoneName); + + snprintf(ZonePathFileName, 250, MAP_DIR "/%s.path", LowerCaseZoneName); + + if((PathFile = fopen(ZonePathFileName, "rb"))) + { + Ret = new PathManager(); + + if(Ret->loadPaths(PathFile)) + { + LogFile->write(EQEMuLog::Status, "Path File %s loaded.", ZonePathFileName); + + } + else + { + LogFile->write(EQEMuLog::Error, "Path File %s failed to load.", ZonePathFileName); + safe_delete(Ret); + } + fclose(PathFile); + } + else + { + LogFile->write(EQEMuLog::Error, "Path File %s not found.", ZonePathFileName); + } + + return Ret; +} + +PathManager::PathManager() +{ + PathNodes = NULL; + ClosedListFlag = NULL; + Head.PathNodeCount = 0; + Head.version = 2; + QuickConnectTarget = -1; +} + +PathManager::~PathManager() +{ + safe_delete_array(PathNodes); + safe_delete_array(ClosedListFlag); +} + +bool PathManager::loadPaths(FILE *PathFile) +{ + + char Magic[10]; + + fread(&Magic, 9, 1, PathFile); + + if(strncmp(Magic, "EQEMUPATH", 9)) + { + LogFile->write(EQEMuLog::Error, "Bad Magic String in .path file."); + return false; + } + + fread(&Head, sizeof(Head), 1, PathFile); + + LogFile->write(EQEMuLog::Status, "Path File Header: Version %ld, PathNodes %ld", + (long)Head.version, (long)Head.PathNodeCount); + + if(Head.version != 2) + { + LogFile->write(EQEMuLog::Error, "Unsupported path file version."); + return false; + } + + PathNodes = new PathNode[Head.PathNodeCount]; + + fread(PathNodes, sizeof(PathNode), Head.PathNodeCount, PathFile); + + ClosedListFlag = new int[Head.PathNodeCount]; + +#ifdef PATHDEBUG + PrintPathing(); +#endif + + int MaxNodeID = Head.PathNodeCount - 1; + + bool PathFileValid = true; + + for(uint32 i = 0; i < Head.PathNodeCount; ++i) + { + for(uint32 j = 0; j < PATHNODENEIGHBOURS; ++j) + { + if(PathNodes[i].Neighbours[j].id > MaxNodeID) + { + LogFile->write(EQEMuLog::Error, "Path Node %i, Neighbour %i (%i) out of range.", i, j, PathNodes[i].Neighbours[j].id); + + PathFileValid = false; + } + } + } + + if(!PathFileValid) + { + safe_delete_array(PathNodes); + } + + return PathFileValid; +} + +void PathManager::PrintPathing() +{ + + for(uint32 i = 0; i < Head.PathNodeCount; ++i) + { + printf("PathNode: %2d id %2d. (%8.3f, %8.3f, %8.3f), BestZ: %8.3f\n", + i, PathNodes[i].id, PathNodes[i].v.x, PathNodes[i].v.y, PathNodes[i].v.z, PathNodes[i].bestz); + + + if(PathNodes[i].Neighbours[0].id == -1) + { + printf(" NO NEIGHBOURS.\n"); + continue; + } + + for(int j=0; j= 0) + printf(" ***** via door %i *****", PathNodes[i].Neighbours[j].DoorID); + + printf("\n"); + } + } +} + +VERTEX PathManager::GetPathNodeCoordinates(int NodeNumber, bool BestZ) +{ + VERTEX Result; + + if(NodeNumber < Head.PathNodeCount) + { + Result = PathNodes[NodeNumber].v; + + if(!BestZ) + return Result; + + Result.z = PathNodes[NodeNumber].bestz; + } + + return Result; + +} + +list PathManager::FindRoute(int startID, int endID) +{ + _ZP(Pathing_FindRoute_FromNodes); + + _log(PATHING__DEBUG, "FindRoute from node %i to %i", startID, endID); + + memset(ClosedListFlag, 0, sizeof(int) * Head.PathNodeCount); + + list OpenList, ClosedList; + + listRoute; + + AStarNode AStarEntry, CurrentNode; + + AStarEntry.PathNodeID = startID; + AStarEntry.Parent = -1; + AStarEntry.HCost = 0; + AStarEntry.GCost = 0; + AStarEntry.Teleport = false; + + OpenList.push_back(AStarEntry); + + while(OpenList.size() > 0) + { + // The OpenList is maintained in sorted order, lowest to highest cost. + + CurrentNode = (*OpenList.begin()); + + ClosedList.push_back(CurrentNode); + + ClosedListFlag[CurrentNode.PathNodeID] = true; + + OpenList.pop_front(); + + for(int i = 0; i < PATHNODENEIGHBOURS; ++i) + { + if(PathNodes[CurrentNode.PathNodeID].Neighbours[i].id == -1) + break; + + if(PathNodes[CurrentNode.PathNodeID].Neighbours[i].id == CurrentNode.Parent) + continue; + + if(PathNodes[CurrentNode.PathNodeID].Neighbours[i].id == endID) + { + Route.push_back(CurrentNode.PathNodeID); + + Route.push_back(endID); + + list::iterator RouteIterator; + + while(CurrentNode.PathNodeID != startID) + { + for(RouteIterator = ClosedList.begin(); RouteIterator != ClosedList.end(); ++RouteIterator) + { + if((*RouteIterator).PathNodeID == CurrentNode.Parent) + { + if(CurrentNode.Teleport) + Route.insert(Route.begin(), -1); + + CurrentNode = (*RouteIterator); + + Route.insert(Route.begin(), CurrentNode.PathNodeID); + + break; + } + } + } + + return Route; + } + if(ClosedListFlag[PathNodes[CurrentNode.PathNodeID].Neighbours[i].id]) + continue; + + AStarEntry.PathNodeID = PathNodes[CurrentNode.PathNodeID].Neighbours[i].id; + + AStarEntry.Parent = CurrentNode.PathNodeID; + + AStarEntry.Teleport = PathNodes[CurrentNode.PathNodeID].Neighbours[i].Teleport; + + // HCost is the estimated cost to get from this node to the end. + AStarEntry.HCost = VertexDistance(PathNodes[PathNodes[CurrentNode.PathNodeID].Neighbours[i].id].v, + PathNodes[endID].v); + + AStarEntry.GCost = CurrentNode.GCost + PathNodes[CurrentNode.PathNodeID].Neighbours[i].distance; + + float FCost = AStarEntry.HCost + AStarEntry.GCost; +#ifdef PATHDEBUG + printf("Node: %i, Open Neighbour %i has HCost %8.3f, GCost %8.3f (Total Cost: %8.3f)\n", + CurrentNode.PathNodeID, + PathNodes[CurrentNode.PathNodeID].Neighbours[i].id, + AStarEntry.HCost, + AStarEntry.GCost, + AStarEntry.HCost + AStarEntry.GCost); +#endif + + bool AlreadyInOpenList = false; + + list::iterator OpenListIterator, InsertionPoint = OpenList.end(); + + for(OpenListIterator = OpenList.begin(); OpenListIterator != OpenList.end(); ++OpenListIterator) + { + if((*OpenListIterator).PathNodeID == PathNodes[CurrentNode.PathNodeID].Neighbours[i].id) + { + AlreadyInOpenList = true; + + float GCostToNode = CurrentNode.GCost + PathNodes[CurrentNode.PathNodeID].Neighbours[i].distance; + + if(GCostToNode < (*OpenListIterator).GCost) + { + (*OpenListIterator).Parent = CurrentNode.PathNodeID; + + (*OpenListIterator).GCost = GCostToNode; + + (*OpenListIterator).Teleport = PathNodes[CurrentNode.PathNodeID].Neighbours[i].Teleport; + } + break; + } + else if((InsertionPoint == OpenList.end()) && (((*OpenListIterator).HCost + (*OpenListIterator).GCost) > FCost)) + { + InsertionPoint = OpenListIterator; + } + } + if(!AlreadyInOpenList) + OpenList.insert(InsertionPoint, AStarEntry); + } + + } + _log(PATHING__DEBUG, "Unable to find a route."); + return Route; + +} + +bool CheckLOSBetweenPoints(VERTEX start, VERTEX end) { + + VERTEX hit; + FACE *face; + + if((zone->zonemap) && (zone->zonemap->LineIntersectsZone(start, end, 1, &hit, &face))) return false; + + return true; +} + +bool SortPathNodesByDistance(PathNodeSortStruct n1, PathNodeSortStruct n2) +{ + return n1.Distance < n2.Distance; +} + +list PathManager::FindRoute(VERTEX Start, VERTEX End) +{ + + _ZP(Pathing_FindRoute_FromVertices); + + _log(PATHING__DEBUG, "FindRoute(%8.3f, %8.3f, %8.3f, %8.3f, %8.3f, %8.3f)", Start.x, Start.y, Start.z, End.x, End.y, End.z); + + list noderoute; + + float CandidateNodeRangeXY = RuleR(Pathing, CandidateNodeRangeXY); + + float CandidateNodeRangeZ = RuleR(Pathing, CandidateNodeRangeZ); + + // Find the nearest PathNode the Start has LOS to. + // + // + int ClosestPathNodeToStart = -1; + + list SortedByDistance; + + PathNodeSortStruct TempNode; + + for(uint32 i = 0 ; i < Head.PathNodeCount; ++i) + { + if((ABS(Start.x - PathNodes[i].v.x) <= CandidateNodeRangeXY) && + (ABS(Start.y - PathNodes[i].v.y) <= CandidateNodeRangeXY) && + (ABS(Start.z - PathNodes[i].v.z) <= CandidateNodeRangeZ)) + { + TempNode.id = i; + TempNode.Distance = VertexDistanceNoRoot(Start, PathNodes[i].v); + SortedByDistance.push_back(TempNode); + + } + } + + SortedByDistance.sort(SortPathNodesByDistance); + + for(list::iterator Iterator = SortedByDistance.begin(); Iterator != SortedByDistance.end(); ++Iterator) + { + _log(PATHING__DEBUG, "Checking Reachability of Node %i from Start Position.", PathNodes[(*Iterator).id].id); + + if(!zone->zonemap->LineIntersectsZone(Start, PathNodes[(*Iterator).id].v, 1.0f, NULL, NULL)) + { + ClosestPathNodeToStart = (*Iterator).id; + break; + } + } + + if(ClosestPathNodeToStart <0 ) { + _log(PATHING__DEBUG, "No LOS to any starting Path Node within range."); + return noderoute; + } + + _log(PATHING__DEBUG, "Closest Path Node To Start: %2d", ClosestPathNodeToStart); + + // Find the nearest PathNode the end point has LOS to + + int ClosestPathNodeToEnd = -1; + + SortedByDistance.clear(); + + for(uint32 i = 0 ; i < Head.PathNodeCount; ++i) + { + if((ABS(End.x - PathNodes[i].v.x) <= CandidateNodeRangeXY) && + (ABS(End.y - PathNodes[i].v.y) <= CandidateNodeRangeXY) && + (ABS(End.z - PathNodes[i].v.z) <= CandidateNodeRangeZ)) + { + TempNode.id = i; + TempNode.Distance = VertexDistanceNoRoot(End, PathNodes[i].v); + SortedByDistance.push_back(TempNode); + } + } + + SortedByDistance.sort(SortPathNodesByDistance); + + for(list::iterator Iterator = SortedByDistance.begin(); Iterator != SortedByDistance.end(); ++Iterator) + { + _log(PATHING__DEBUG, "Checking Reachability of Node %i from End Position.", PathNodes[(*Iterator).id].id); + _log(PATHING__DEBUG, " (%8.3f, %8.3f, %8.3f) to (%8.3f, %8.3f, %8.3f)", + End.x, End.y, End.z, + PathNodes[(*Iterator).id].v.x, PathNodes[(*Iterator).id].v.y, PathNodes[(*Iterator).id].v.z); + + if(!zone->zonemap->LineIntersectsZone(End, PathNodes[(*Iterator).id].v, 1.0f, NULL, NULL)) + { + ClosestPathNodeToEnd = (*Iterator).id; + break; + } + } + + if(ClosestPathNodeToEnd < 0) { + _log(PATHING__DEBUG, "No LOS to any end Path Node within range."); + return noderoute; + } + + _log(PATHING__DEBUG, "Closest Path Node To End: %2d", ClosestPathNodeToEnd); + + if(ClosestPathNodeToStart == ClosestPathNodeToEnd) + { + noderoute.push_back(ClosestPathNodeToStart); + return noderoute; + } + noderoute = FindRoute(ClosestPathNodeToStart, ClosestPathNodeToEnd); + + int NodesToAttemptToCull = RuleI(Pathing, CullNodesFromStart); + + if(NodesToAttemptToCull > 0) + { + int CulledNodes = 0; + + list::iterator First, Second; + + while((noderoute.size() >= 2) && (CulledNodes < NodesToAttemptToCull)) + { + First = noderoute.begin(); + + Second = First; + + ++Second; + + if((*Second) < 0) + break; + + if(!zone->zonemap->LineIntersectsZone(Start, PathNodes[(*Second)].v, 1.0f, NULL, NULL) + && zone->pathing->NoHazards(Start, PathNodes[(*Second)].v)) + { + noderoute.erase(First); + + ++CulledNodes; + } + else + break; + } + } + + NodesToAttemptToCull = RuleI(Pathing, CullNodesFromEnd); + + if(NodesToAttemptToCull > 0) + { + int CulledNodes = 0; + + list::iterator First, Second; + + while((noderoute.size() >= 2) && (CulledNodes < NodesToAttemptToCull)) + { + First = noderoute.end(); + + --First; + + Second = First; + + --Second; + + if((*Second) < 0) + break; + + if(!zone->zonemap->LineIntersectsZone(End, PathNodes[(*Second)].v, 1.0f, NULL, NULL) + && zone->pathing->NoHazards(End, PathNodes[(*Second)].v)) + { + noderoute.erase(First); + + ++CulledNodes; + } + else + break; + } + } + + return noderoute; +} + +const char* DigitToWord(int i) +{ + switch(i) { + case 0: + return "zero"; + case 1: + return "one"; + case 2: + return "two"; + case 3: + return "three"; + case 4: + return "four"; + case 5: + return "five"; + case 6: + return "six"; + case 7: + return "seven"; + case 8: + return "eight"; + case 9: + return "nine"; + } + return ""; +} + +void PathManager::SpawnPathNodes() +{ + + for(uint32 i = 0; i < Head.PathNodeCount; ++i) + { + NPCType* npc_type = new NPCType; + memset(npc_type, 0, sizeof(NPCType)); + + if(PathNodes[i].id < 10) + sprintf(npc_type->name, "%s", DigitToWord(PathNodes[i].id)); + else if(PathNodes[i].id < 100) + sprintf(npc_type->name, "%s_%s", DigitToWord(PathNodes[i].id/10), DigitToWord(PathNodes[i].id % 10)); + else + sprintf(npc_type->name, "%s_%s_%s", DigitToWord(PathNodes[i].id/100), DigitToWord((PathNodes[i].id % 100)/10), + DigitToWord(((PathNodes[i].id % 100) %10))); + + sprintf(npc_type->lastname, "%i", PathNodes[i].id); + npc_type->cur_hp = 4000000; + npc_type->max_hp = 4000000; + npc_type->race = 151; + npc_type->gender = 2; + npc_type->class_ = 9; + npc_type->deity= 1; + npc_type->level = 75; + npc_type->npc_id = 0; + npc_type->loottable_id = 0; + npc_type->texture = 1; + npc_type->light = 0; + npc_type->runspeed = 0; + npc_type->d_meele_texture1 = 1; + npc_type->d_meele_texture2 = 1; + npc_type->merchanttype = 1; + npc_type->bodytype = 1; + + npc_type->STR = 150; + npc_type->STA = 150; + npc_type->DEX = 150; + npc_type->AGI = 150; + npc_type->INT = 150; + npc_type->WIS = 150; + npc_type->CHA = 150; + + npc_type->findable = 1; + + NPC* npc = new NPC(npc_type, 0, PathNodes[i].v.x, PathNodes[i].v.y, PathNodes[i].v.z, 0, FlyMode1); + npc->GiveNPCTypeData(npc_type); + + entity_list.AddNPC(npc, true, true); + } +} + +void PathManager::MeshTest() +{ + // This will test connectivity between all path nodes + + int TotalTests = 0; + int NoConnections = 0; + + printf("Beginning Pathmanager connectivity tests.\n"); fflush(stdout); + + for(uint32 i = 0; i < Head.PathNodeCount; ++i) + { + for(uint32 j = 0; j < Head.PathNodeCount; ++j) + { + if(j == i) + continue; + + list Route = FindRoute(PathNodes[i].id, PathNodes[j].id); + + if(Route.size() == 0) + { + ++NoConnections; + printf("FindRoute(%i, %i) **** NO ROUTE FOUND ****\n", PathNodes[i].id, PathNodes[j].id); + } + ++TotalTests; + } + } + printf("Executed %i route searches.\n", TotalTests); + printf("Failed to find %i routes.\n", NoConnections); + fflush(stdout); +} + +void PathManager::SimpleMeshTest() +{ + // This will test connectivity between the first path node and all other nodes + + int TotalTests = 0; + int NoConnections = 0; + + printf("Beginning Pathmanager connectivity tests.\n"); + fflush(stdout); + + for(uint32 j = 1; j < Head.PathNodeCount; ++j) + { + list Route = FindRoute(PathNodes[0].id, PathNodes[j].id); + + if(Route.size() == 0) + { + ++NoConnections; + printf("FindRoute(%i, %i) **** NO ROUTE FOUND ****\n", PathNodes[0].id, PathNodes[j].id); + } + ++TotalTests; + } + printf("Executed %i route searches.\n", TotalTests); + printf("Failed to find %i routes.\n", NoConnections); + fflush(stdout); +} + +VERTEX Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChanged, bool &NodeReached) +{ + _ZP(Pathing_UpdatePath); + + WaypointChanged = false; + + NodeReached = false; + + VERTEX NodeLoc; + + VERTEX From(GetX(), GetY(), GetZ()); + + VERTEX HeadPosition(From.x, From.y, From.z + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION); + + VERTEX To(ToX, ToY, ToZ); + + bool SameDestination = (To == PathingDestination); + + int NextNode; + + if(To == From) + return To; + + mlog(PATHING__DEBUG, "UpdatePath. From(%8.3f, %8.3f, %8.3f) To(%8.3f, %8.3f, %8.3f)", From.x, From.y, From.z, To.x, To.y, To.z); + + if(From == PathingLastPosition) + { + ++PathingLoopCount; + + if((PathingLoopCount > 5) && !IsRooted()) + { + mlog(PATHING__DEBUG, "appears to be stuck. Teleporting them to next position.", GetName()); + + if(Route.size() == 0) + { + Teleport(To); + + WaypointChanged = true; + + PathingLoopCount = 0; + + return To; + } + NodeLoc = zone->pathing->GetPathNodeCoordinates(Route.front()); + + Route.pop_front(); + + ++PathingTraversedNodes; + + Teleport(NodeLoc); + + WaypointChanged = true; + + PathingLoopCount = 0; + + return NodeLoc; + } + } + else + { + PathingLoopCount = 0; + + PathingLastPosition = From; + } + + if(Route.size() > 0) + { + + // If we are already pathing, and the destination is the same as before ... + if(SameDestination) + { + mlog(PATHING__DEBUG, " Still pathing to the same destination."); + + // Get the coordinates of the first path node we are going to. + NextNode = Route.front(); + + NodeLoc = zone->pathing->GetPathNodeCoordinates(NextNode); + + // May need to refine this as rounding errors may mean we never have equality + // We have reached the path node. + if(NodeLoc == From) + { + mlog(PATHING__DEBUG, " Arrived at node %i", NextNode); + + NodeReached = true; + + PathingLastNodeVisited = Route.front(); + // We only check for LOS again after traversing more than 1 node, otherwise we can get into + // a loop where we have a hazard and so run to a path node far enough away from the hazard, and + // then run right back towards the same hazard again. + // + // An exception is when we are about to head for the last node. We always check LOS then. This + // is because we are seeking a path to the node nearest to our target. This node may be behind the + // target, and we may run past the target if we don't check LOS at this point. + int RouteSize = Route.size(); + + mlog(PATHING__DEBUG, "Route size is %i", RouteSize); + + if((RouteSize == 2) + || ((PathingTraversedNodes >= RuleI(Pathing, MinNodesTraversedForLOSCheck)) + && (RouteSize <= RuleI(Pathing, MinNodesLeftForLOSCheck)) + && PathingLOSCheckTimer->Check())) + { + mlog(PATHING__DEBUG, " Checking distance to target."); + float Distance = VertexDistanceNoRoot(From, To); + + mlog(PATHING__DEBUG, " Distance between From and To (NoRoot) is %8.3f", Distance); + + if((Distance <= RuleR(Pathing, MinDistanceForLOSCheckShort)) + && (ABS(From.z - To.z) <= RuleR(Pathing, ZDiffThreshold))) + { + if(!zone->zonemap->LineIntersectsZone(HeadPosition, To, 1.0f, NULL, NULL)) + PathingLOSState = HaveLOS; + else + PathingLOSState = NoLOS; + mlog(PATHING__DEBUG, " LOS stats is %s", (PathingLOSState == HaveLOS) ? "HaveLOS" : "NoLOS"); + + if((PathingLOSState == HaveLOS) && zone->pathing->NoHazards(From, To)) + { + mlog(PATHING__DEBUG, " No hazards. Running directly to target."); + Route.clear(); + + return To; + } + else + { + mlog(PATHING__DEBUG, " Continuing on node path."); + } + } + else + PathingLOSState = UnknownLOS; + } + // We are on the same route, no LOS (or not checking this time, so pop off the node we just reached + // + Route.pop_front(); + + ++PathingTraversedNodes; + + WaypointChanged = true; + + // If there are more nodes on the route, return the coords of the next node + if(Route.size() > 0) + { + NextNode = Route.front(); + + if(NextNode == -1) + { + // -1 indicates a teleport to the next node + Route.pop_front(); + + if(Route.size() == 0) + { + mlog(PATHING__DEBUG, "Missing node after teleport."); + return To; + } + + NextNode = Route.front(); + + NodeLoc = zone->pathing->GetPathNodeCoordinates(NextNode); + + Teleport(NodeLoc); + + mlog(PATHING__DEBUG, " TELEPORTED to %8.3f, %8.3f, %8.3f\n", NodeLoc.x, NodeLoc.y, NodeLoc.z); + + Route.pop_front(); + + if(Route.size() == 0) + return To; + + NextNode = Route.front(); + } + zone->pathing->OpenDoors(PathingLastNodeVisited, NextNode, this); + + mlog(PATHING__DEBUG, " Now moving to node %i", NextNode); + + return zone->pathing->GetPathNodeCoordinates(NextNode); + } + else + { + // we have run all the nodes, all that is left is the direct path from the last node + // to the destination + mlog(PATHING__DEBUG, " Reached end of node path, running direct to target."); + + return To; + } + } + // At this point, we are still on the previous path, but not reached a node yet. + // The route shouldn't be empty, but check anyway. + // + int RouteSize = Route.size(); + + if((PathingTraversedNodes >= RuleI(Pathing, MinNodesTraversedForLOSCheck)) + && (RouteSize <= RuleI(Pathing, MinNodesLeftForLOSCheck)) + && PathingLOSCheckTimer->Check()) + { + mlog(PATHING__DEBUG, " Checking distance to target."); + + float Distance = VertexDistanceNoRoot(From, To); + + mlog(PATHING__DEBUG, " Distance between From and To (NoRoot) is %8.3f", Distance); + + if((Distance <= RuleR(Pathing, MinDistanceForLOSCheckShort)) + && (ABS(From.z - To.z) <= RuleR(Pathing, ZDiffThreshold))) + { + if(!zone->zonemap->LineIntersectsZone(HeadPosition, To, 1.0f, NULL, NULL)) + PathingLOSState = HaveLOS; + else + PathingLOSState = NoLOS; + mlog(PATHING__DEBUG, " LOS stats is %s", (PathingLOSState == HaveLOS) ? "HaveLOS" : "NoLOS"); + + if((PathingLOSState == HaveLOS) && zone->pathing->NoHazards(From, To)) + { + mlog(PATHING__DEBUG, " No hazards. Running directly to target."); + Route.clear(); + + return To; + } + else + { + mlog(PATHING__DEBUG, " Continuing on node path."); + } + } + else + PathingLOSState = UnknownLOS; + } + return NodeLoc; + } + else + { + // We get here if we were already pathing, but our destination has now changed. + // + mlog(PATHING__DEBUG, " Target has changed position."); + // Update our record of where we are going to. + PathingDestination = To; + // Check if we now have LOS etc to the new destination. + if(PathingLOSCheckTimer->Check()) + { + float Distance = VertexDistanceNoRoot(From, To); + + if((Distance <= RuleR(Pathing, MinDistanceForLOSCheckShort)) + && (ABS(From.z - To.z) <= RuleR(Pathing, ZDiffThreshold))) + { + mlog(PATHING__DEBUG, " Checking for short LOS at distance %8.3f.", Distance); + if(!zone->zonemap->LineIntersectsZone(HeadPosition, To, 1.0f, NULL, NULL)) + PathingLOSState = HaveLOS; + else + PathingLOSState = NoLOS; + + mlog(PATHING__DEBUG, " LOS stats is %s", (PathingLOSState == HaveLOS) ? "HaveLOS" : "NoLOS"); + + if((PathingLOSState == HaveLOS) && zone->pathing->NoHazards(From, To)) + { + mlog(PATHING__DEBUG, " No hazards. Running directly to target."); + Route.clear(); + return To; + } + else + { + mlog(PATHING__DEBUG, " Continuing on node path."); + } + } + } + + // If the player is moving, we don't want to recalculate our route too frequently. + // + if(static_cast(Route.size()) <= RuleI(Pathing, RouteUpdateFrequencyNodeCount)) + { + if(!PathingRouteUpdateTimerShort->Check()) + { + mlog(PATHING__DEBUG, "Short route update timer not yet expired."); + return zone->pathing->GetPathNodeCoordinates(Route.front()); + } + mlog(PATHING__DEBUG, "Short route update timer expired."); + } + else + { + if(!PathingRouteUpdateTimerLong->Check()) + { + mlog(PATHING__DEBUG, "Long route update timer not yet expired."); + return zone->pathing->GetPathNodeCoordinates(Route.front()); + } + mlog(PATHING__DEBUG, "Long route update timer expired."); + } + + // We are already pathing, destination changed, no LOS. Find the nearest node to our destination. + int DestinationPathNode= zone->pathing->FindNearestPathNode(To); + + // Destination unreachable via pathing, return direct route. + if(DestinationPathNode == -1) + { + mlog(PATHING__DEBUG, " Unable to find path node for new destination. Running straight to target."); + Route.clear(); + return To; + } + // If the nearest path node to our new destination is the same as for the previous + // one, we will carry on on our path. + if(DestinationPathNode == Route.back()) + { + mlog(PATHING__DEBUG, " Same destination Node (%i). Continue with current path.", DestinationPathNode); + + NodeLoc = zone->pathing->GetPathNodeCoordinates(Route.front()); + + // May need to refine this as rounding errors may mean we never have equality + // Check if we have reached a path node. + if(NodeLoc == From) + { + mlog(PATHING__DEBUG, " Arrived at node %i, moving to next one.\n", Route.front()); + + NodeReached = true; + + PathingLastNodeVisited = Route.front(); + + Route.pop_front(); + + ++PathingTraversedNodes; + + WaypointChanged = true; + + if(Route.size() > 0) + { + NextNode = Route.front(); + + if(NextNode == -1) + { + // -1 indicates a teleport to the next node + Route.pop_front(); + + if(Route.size() == 0) + { + mlog(PATHING__DEBUG, "Missing node after teleport."); + return To; + } + + NextNode = Route.front(); + + NodeLoc = zone->pathing->GetPathNodeCoordinates(NextNode); + + Teleport(NodeLoc); + + mlog(PATHING__DEBUG, " TELEPORTED to %8.3f, %8.3f, %8.3f\n", NodeLoc.x, NodeLoc.y, NodeLoc.z); + + Route.pop_front(); + + if(Route.size() == 0) + return To; + + NextNode = Route.front(); + } + // Return the coords of our next path node on the route. + mlog(PATHING__DEBUG, " Now moving to node %i", NextNode); + + zone->pathing->OpenDoors(PathingLastNodeVisited, NextNode, this); + + return zone->pathing->GetPathNodeCoordinates(NextNode); + } + else + { + mlog(PATHING__DEBUG, " Reached end of path grid. Running direct to target."); + return To; + } + } + return NodeLoc; + } + else + { + mlog(PATHING__DEBUG, " Target moved. End node is different. Clearing route."); + + Route.clear(); + // We will now fall through to get a new route. + } + + } + + + } + mlog(PATHING__DEBUG, " Our route list is empty."); + + if((SameDestination) && !PathingLOSCheckTimer->Check()) + { + mlog(PATHING__DEBUG, " Destination same as before, LOS check timer not reached. Returning To."); + return To; + } + + PathingLOSState = UnknownLOS; + + PathingDestination = To; + + WaypointChanged = true; + + float Distance = VertexDistanceNoRoot(From, To); + + if((Distance <= RuleR(Pathing, MinDistanceForLOSCheckLong)) + && (ABS(From.z - To.z) <= RuleR(Pathing, ZDiffThreshold))) + { + mlog(PATHING__DEBUG, " Checking for long LOS at distance %8.3f.", Distance); + + if(!zone->zonemap->LineIntersectsZone(HeadPosition, To, 1.0f, NULL, NULL)) + PathingLOSState = HaveLOS; + else + PathingLOSState = NoLOS; + + mlog(PATHING__DEBUG, " LOS stats is %s", (PathingLOSState == HaveLOS) ? "HaveLOS" : "NoLOS"); + + if((PathingLOSState == HaveLOS) && zone->pathing->NoHazards(From, To)) + { + mlog(PATHING__DEBUG, "Target is reachable. Running directly there."); + return To; + } + } + mlog(PATHING__DEBUG, " Calculating new route to target."); + + Route = zone->pathing->FindRoute(From, To); + + PathingTraversedNodes = 0; + + if(Route.size() == 0) + { + mlog(PATHING__DEBUG, " No route available, running direct."); + + return To; + } + + if(SameDestination && (Route.front() == PathingLastNodeVisited)) + { + mlog(PATHING__DEBUG, " Probable loop detected. Same destination and Route.front() == PathingLastNodeVisited."); + + Route.clear(); + + return To; + } + NodeLoc = zone->pathing->GetPathNodeCoordinates(Route.front()); + + mlog(PATHING__DEBUG, " New route determined, heading for node %i", Route.front()); + + PathingLoopCount = 0; + + return NodeLoc; + +} + +int PathManager::FindNearestPathNode(VERTEX Position) +{ + + // Find the nearest PathNode we have LOS to. + // + // + + float CandidateNodeRangeXY = RuleR(Pathing, CandidateNodeRangeXY); + + float CandidateNodeRangeZ = RuleR(Pathing, CandidateNodeRangeZ); + + int ClosestPathNodeToStart = -1; + + list SortedByDistance; + + PathNodeSortStruct TempNode; + + for(uint32 i = 0 ; i < Head.PathNodeCount; ++i) + { + if((ABS(Position.x - PathNodes[i].v.x) <= CandidateNodeRangeXY) && + (ABS(Position.y - PathNodes[i].v.y) <= CandidateNodeRangeXY) && + (ABS(Position.z - PathNodes[i].v.z) <= CandidateNodeRangeZ)) + { + TempNode.id = i; + TempNode.Distance = VertexDistanceNoRoot(Position, PathNodes[i].v); + SortedByDistance.push_back(TempNode); + + } + } + + SortedByDistance.sort(SortPathNodesByDistance); + + for(list::iterator Iterator = SortedByDistance.begin(); Iterator != SortedByDistance.end(); ++Iterator) + { + _log(PATHING__DEBUG, "Checking Reachability of Node %i from Start Position.", PathNodes[(*Iterator).id].id); + + if(!zone->zonemap->LineIntersectsZone(Position, PathNodes[(*Iterator).id].v, 1.0f, NULL, NULL)) + { + ClosestPathNodeToStart = (*Iterator).id; + break; + } + } + + if(ClosestPathNodeToStart <0 ) { + _log(PATHING__DEBUG, "No LOS to any starting Path Node within range."); + return -1; + } + return ClosestPathNodeToStart; +} + +bool PathManager::NoHazards(VERTEX From, VERTEX To) +{ + _ZP(Pathing_NoHazards); + + // Test the Z coordinate at the mid point. + // + VERTEX MidPoint((From.x + To.x) / 2, (From.y + To.y) / 2, From.z); + + float NewZ = zone->zonemap->FindBestZ(MAP_ROOT_NODE, MidPoint, NULL, NULL); + + if(ABS(NewZ - From.z) > RuleR(Pathing, ZDiffThreshold)) + { + _log(PATHING__DEBUG, " HAZARD DETECTED moving from %8.3f, %8.3f, %8.3f to %8.3f, %8.3f, %8.3f. Z Change is %8.3f", + From.x, From.y, From.z, MidPoint.x, MidPoint.y, MidPoint.z, NewZ - From.z); + + return false; + } + else + { + _log(PATHING__DEBUG, "No HAZARD DETECTED moving from %8.3f, %8.3f, %8.3f to %8.3f, %8.3f, %8.3f. Z Change is %8.3f", + From.x, From.y, From.z, MidPoint.x, MidPoint.y, MidPoint.z, NewZ - From.z); + } + + return true; +} + +bool PathManager::NoHazardsAccurate(VERTEX From, VERTEX To) +{ + float stepx, stepy, stepz, curx, cury, curz; + VERTEX cur = From; + float last_z = From.z; + float step_size = 1.0; + + curx = From.x; + cury = From.y; + curz = From.z; + + do + { + stepx = (float)To.x - curx; + stepy = (float)To.y - cury; + stepz = (float)To.z - curz; + float factor = sqrt(stepx*stepx + stepy*stepy + stepz*stepz); + stepx = (stepx/factor)*step_size; + stepy = (stepy/factor)*step_size; + stepz = (stepz/factor)*step_size; + + VERTEX TestPoint(curx, cury, curz); + float NewZ = zone->zonemap->FindBestZ(MAP_ROOT_NODE, TestPoint, NULL, NULL); + if(ABS(NewZ - last_z) > 5.0) + { + _log(PATHING__DEBUG, " HAZARD DETECTED moving from %8.3f, %8.3f, %8.3f to %8.3f, %8.3f, %8.3f. Best Z %8.3f, Z Change is %8.3f", + From.x, From.y, From.z, TestPoint.x, TestPoint.y, TestPoint.z, NewZ, NewZ - From.z); + return false; + } + last_z = NewZ; + + if(zone->watermap) + { + NodeRef n = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), TestPoint.x, TestPoint.y); + if(n != NODE_NONE) + { + if(zone->watermap->InLiquid(From.x, From.y, From.z) || zone->watermap->InLiquid(To.x, To.y, To.z)) + { + break; + } + + if(zone->watermap->InLiquid(TestPoint.x, TestPoint.y, NewZ)) + { + VERTEX TestPointWater(TestPoint.x, TestPoint.y, NewZ-0.5); + VERTEX TestPointWaterDest(TestPointWater); + VERTEX hit; + TestPointWaterDest.z -= 500; + float best_z2 = -999990; + if(zone->zonemap->LineIntersectsNode(n, TestPointWater, TestPointWaterDest, &hit, NULL)) + { + best_z2 = hit.z; + } + if(best_z2 == -999990) + { + _log(PATHING__DEBUG, " HAZARD DETECTED, really deep water/lava!"); + return false; + } + else + { + if(ABS(NewZ - best_z2) > RuleR(Pathing, ZDiffThreshold)) + { + _log(PATHING__DEBUG, " HAZARD DETECTED, water is fairly deep at %8.3f units deep", ABS(NewZ - best_z2)); + return false; + } + else + { + _log(PATHING__DEBUG, " HAZARD NOT DETECTED, water is shallow at %8.3f units deep", ABS(NewZ - best_z2)); + } + } + } + else + { + _log(PATHING__DEBUG, "Hazard point not in water or lava!"); + } + } + } + else + { + _log(PATHING__DEBUG, "No water map loaded for hazards!"); + } + + curx += stepx; + cury += stepy; + curz += stepz; + + cur.x = curx; + cur.y = cury; + cur.z = curz; + + if(ABS(curx - To.x) < step_size) cur.x = To.x; + if(ABS(cury - To.y) < step_size) cur.y = To.y; + if(ABS(curz - To.z) < step_size) cur.z = To.z; + + } + while(cur.x != To.x || cur.y != To.y || cur.z != To.z); + return true; +} + +void Mob::PrintRoute() +{ + + printf("Route is : "); + + list::iterator Iterator; + + for(Iterator = Route.begin(); Iterator !=Route.end(); ++Iterator) + { + printf("%i, ", (*Iterator)); + } + + printf("\n"); + +} + +void PathManager::OpenDoors(int Node1, int Node2, Mob *ForWho) +{ + + _ZP(Pathing_OpenDoors); + + if(!ForWho || (Node1 >= Head.PathNodeCount) || (Node2 >= Head.PathNodeCount) || (Node1 < 0) || (Node2 < 0)) + return; + + for(int i = 0; i < PATHNODENEIGHBOURS; ++i) + { + if(PathNodes[Node1].Neighbours[i].id == -1) + return; + + if(PathNodes[Node1].Neighbours[i].id != Node2) + continue; + + if(PathNodes[Node1].Neighbours[i].DoorID >= 0) + { + Doors *d = entity_list.FindDoor(PathNodes[Node1].Neighbours[i].DoorID); + + if(d && !d->IsDoorOpen() ) + { + _log(PATHING__DEBUG, "Opening door %i for %s", PathNodes[Node1].Neighbours[i].DoorID, ForWho->GetName()); + + d->ForceOpen(ForWho); + } + return; + } + } +} + +//this assumes that the first point in the list is the player's +//current position, I dont know how well it works if its not. +void Client::SendPathPacket(vector &points) { + if(points.size() < 2) { + //empty length packet == not found. + EQApplicationPacket outapp(OP_FindPersonReply, 0); + QueuePacket(&outapp); + return; + } + + int len = sizeof(FindPersonResult_Struct) + (points.size()+1) * sizeof(FindPerson_Point); + EQApplicationPacket *outapp = new EQApplicationPacket(OP_FindPersonReply, len); + FindPersonResult_Struct* fpr=(FindPersonResult_Struct*)outapp->pBuffer; + + vector::iterator cur, end; + cur = points.begin(); + end = points.end(); + unsigned int r; + for(r = 0; cur != end; cur++, r++) { + fpr->path[r] = *cur; + + } + //put the last element into the destination field + cur--; + fpr->path[r] = *cur; + fpr->dest = *cur; + + FastQueuePacket(&outapp); + + +} + +PathNode* PathManager::FindPathNodeByCoordinates(float x, float y, float z) +{ + for(uint32 i = 0; i < Head.PathNodeCount; ++i) + if((PathNodes[i].v.x == x) && (PathNodes[i].v.y == y) && (PathNodes[i].v.z == z)) + return &PathNodes[i]; + + return NULL; +} + +int PathManager::GetRandomPathNode() +{ + return MakeRandomInt(0, Head.PathNodeCount - 1); + +} + +void PathManager::ShowPathNodeNeighbours(Client *c) +{ + if(!c || !c->GetTarget()) + return; + + + PathNode *Node = zone->pathing->FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ()); + + if(!Node) + { + c->Message(0, "Unable to find path node."); + return; + } + c->Message(0, "Path node %4i", Node->id); + + for(uint32 i = 0; i < Head.PathNodeCount; ++i) + { + char Name[64]; + + if(PathNodes[i].id < 10) + sprintf(Name, "%s000", DigitToWord(PathNodes[i].id)); + else if(PathNodes[i].id < 100) + sprintf(Name, "%s_%s000", DigitToWord(PathNodes[i].id / 10), DigitToWord(PathNodes[i].id % 10)); + else + sprintf(Name, "%s_%s_%s000", DigitToWord(PathNodes[i].id/100), DigitToWord((PathNodes[i].id % 100)/10), + DigitToWord(((PathNodes[i].id % 100) %10))); + + Mob *m = entity_list.GetMob(Name); + + if(m) + m->SendIllusionPacket(151); + } + + std::stringstream Neighbours; + + for(int i = 0; i < PATHNODENEIGHBOURS; ++i) + { + if(Node->Neighbours[i].id == -1) + break; + Neighbours << Node->Neighbours[i].id << ", "; + + char Name[64]; + + if(Node->Neighbours[i].id < 10) + sprintf(Name, "%s000", DigitToWord(Node->Neighbours[i].id)); + else if(Node->Neighbours[i].id < 100) + sprintf(Name, "%s_%s000", DigitToWord(Node->Neighbours[i].id / 10), DigitToWord(Node->Neighbours[i].id % 10)); + else + sprintf(Name, "%s_%s_%s000", DigitToWord(Node->Neighbours[i].id/100), DigitToWord((Node->Neighbours[i].id % 100)/10), + DigitToWord(((Node->Neighbours[i].id % 100) %10))); + + Mob *m = entity_list.GetMob(Name); + + if(m) + m->SendIllusionPacket(46); + } + c->Message(0, "Neighbours: %s", Neighbours.str().c_str()); +} + +void PathManager::NodeInfo(Client *c) +{ + if(!c) + { + return; + } + + if(!c->GetTarget()) + { + c->Message(0, "You must target a node."); + return; + } + + PathNode *Node = zone->pathing->FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ()); + if(!Node) + { + return; + } + + c->Message(0, "Pathing node: %i at (%.2f, %.2f, %.2f) with bestz %.2f", + Node->id, Node->v.x, Node->v.y, Node->v.z, Node->bestz); + + bool neighbour = false; + for(int x = 0; x < 50; ++x) + { + if(Node->Neighbours[x].id != -1) + { + if(!neighbour) + { + c->Message(0, "Neighbours found:"); + neighbour = true; + } + c->Message(0, "id: %i, distance: %.2f, door id: %i, is teleport: %i", + Node->Neighbours[x].id, Node->Neighbours[x].distance, + Node->Neighbours[x].DoorID, Node->Neighbours[x].Teleport); + } + } + + if(!neighbour) + { + c->Message(0, "No neighbours found!"); + } + return; +} + +void PathManager::DumpPath(string filename) +{ + ofstream o_file; + o_file.open(filename.c_str(), ios_base::binary | ios_base::trunc | ios_base::out); + o_file.write("EQEMUPATH", 9); + o_file.write((const char*)&Head, sizeof(Head)); + o_file.write((const char*)PathNodes, (sizeof(PathNode)*Head.PathNodeCount)); + o_file.close(); +} + +int32 PathManager::AddNode(float x, float y, float z, float best_z, int32 requested_id) +{ + int32 new_id = -1; + if(requested_id != 0) + { + new_id = requested_id; + for(uint32 i = 0; i < Head.PathNodeCount; ++i) + { + if(PathNodes[i].id == requested_id) + { + new_id = -1; + break; + } + } + } + + if(new_id == -1) + { + for(uint32 i = 0; i < Head.PathNodeCount; ++i) + { + if(PathNodes[i].id > new_id) + new_id = PathNodes[i].id; + } + new_id++; + } + + PathNode new_node; + new_node.v.x = x; + new_node.v.y = y; + new_node.v.z = z; + new_node.bestz = best_z; + new_node.id = (uint16)new_id; + for(int x = 0; x < PATHNODENEIGHBOURS; ++x) + { + new_node.Neighbours[x].id = -1; + new_node.Neighbours[x].distance = 0.0; + new_node.Neighbours[x].DoorID = -1; + new_node.Neighbours[x].Teleport = 0; + } + + Head.PathNodeCount++; + if(Head.PathNodeCount > 1) + { + PathNode *t_PathNodes = new PathNode[Head.PathNodeCount]; + for(uint32 x = 0; x < (Head.PathNodeCount - 1); ++x) + { + t_PathNodes[x].v.x = PathNodes[x].v.x; + t_PathNodes[x].v.y = PathNodes[x].v.y; + t_PathNodes[x].v.z = PathNodes[x].v.z; + t_PathNodes[x].bestz = PathNodes[x].bestz; + t_PathNodes[x].id = PathNodes[x].id; + for(int n = 0; n < PATHNODENEIGHBOURS; ++n) + { + t_PathNodes[x].Neighbours[n].distance = PathNodes[x].Neighbours[n].distance; + t_PathNodes[x].Neighbours[n].DoorID = PathNodes[x].Neighbours[n].DoorID; + t_PathNodes[x].Neighbours[n].id = PathNodes[x].Neighbours[n].id; + t_PathNodes[x].Neighbours[n].Teleport = PathNodes[x].Neighbours[n].Teleport; + } + + } + + int32 index = (Head.PathNodeCount - 1); + t_PathNodes[index].v.x = new_node.v.x; + t_PathNodes[index].v.y = new_node.v.y; + t_PathNodes[index].v.z = new_node.v.z; + t_PathNodes[index].bestz = new_node.bestz; + t_PathNodes[index].id = new_node.id; + for(int n = 0; n < PATHNODENEIGHBOURS; ++n) + { + t_PathNodes[index].Neighbours[n].distance = new_node.Neighbours[n].distance; + t_PathNodes[index].Neighbours[n].DoorID = new_node.Neighbours[n].DoorID; + t_PathNodes[index].Neighbours[n].id = new_node.Neighbours[n].id; + t_PathNodes[index].Neighbours[n].Teleport = new_node.Neighbours[n].Teleport; + } + + delete[] PathNodes; + PathNodes = t_PathNodes; + + NPCType* npc_type = new NPCType; + memset(npc_type, 0, sizeof(NPCType)); + if(new_id < 10) + sprintf(npc_type->name, "%s", DigitToWord(new_id)); + else if(new_id < 100) + sprintf(npc_type->name, "%s_%s", DigitToWord(new_id/10), DigitToWord(new_id % 10)); + else + sprintf(npc_type->name, "%s_%s_%s", DigitToWord(new_id/100), DigitToWord((new_id % 100)/10), + DigitToWord(((new_id % 100) %10))); + + sprintf(npc_type->lastname, "%i", new_id); + npc_type->cur_hp = 4000000; + npc_type->max_hp = 4000000; + npc_type->race = 151; + npc_type->gender = 2; + npc_type->class_ = 9; + npc_type->deity= 1; + npc_type->level = 75; + npc_type->npc_id = 0; + npc_type->loottable_id = 0; + npc_type->texture = 1; + npc_type->light = 0; + npc_type->runspeed = 0; + npc_type->d_meele_texture1 = 1; + npc_type->d_meele_texture2 = 1; + npc_type->merchanttype = 1; + npc_type->bodytype = 1; + npc_type->STR = 150; + npc_type->STA = 150; + npc_type->DEX = 150; + npc_type->AGI = 150; + npc_type->INT = 150; + npc_type->WIS = 150; + npc_type->CHA = 150; + npc_type->findable = 1; + + NPC* npc = new NPC(npc_type, 0, new_node.v.x, new_node.v.y, new_node.v.z, 0, FlyMode1); + npc->GiveNPCTypeData(npc_type); + entity_list.AddNPC(npc, true, true); + + safe_delete_array(ClosedListFlag); + ClosedListFlag = new int[Head.PathNodeCount]; + return new_id; + } + else + { + PathNodes = new PathNode[Head.PathNodeCount]; + PathNodes[0].v.x = new_node.v.x; + PathNodes[0].v.y = new_node.v.y; + PathNodes[0].v.z = new_node.v.z; + PathNodes[0].bestz = new_node.bestz; + PathNodes[0].id = new_node.id; + for(int n = 0; n < PATHNODENEIGHBOURS; ++n) + { + PathNodes[0].Neighbours[n].distance = new_node.Neighbours[n].distance; + PathNodes[0].Neighbours[n].DoorID = new_node.Neighbours[n].DoorID; + PathNodes[0].Neighbours[n].id = new_node.Neighbours[n].id; + PathNodes[0].Neighbours[n].Teleport = new_node.Neighbours[n].Teleport; + } + + NPCType* npc_type = new NPCType; + memset(npc_type, 0, sizeof(NPCType)); + if(new_id < 10) + sprintf(npc_type->name, "%s", DigitToWord(new_id)); + else if(new_id < 100) + sprintf(npc_type->name, "%s_%s", DigitToWord(new_id/10), DigitToWord(new_id % 10)); + else + sprintf(npc_type->name, "%s_%s_%s", DigitToWord(new_id/100), DigitToWord((new_id % 100)/10), + DigitToWord(((new_id % 100) %10))); + + sprintf(npc_type->lastname, "%i", new_id); + npc_type->cur_hp = 4000000; + npc_type->max_hp = 4000000; + npc_type->race = 151; + npc_type->gender = 2; + npc_type->class_ = 9; + npc_type->deity= 1; + npc_type->level = 75; + npc_type->npc_id = 0; + npc_type->loottable_id = 0; + npc_type->texture = 1; + npc_type->light = 0; + npc_type->runspeed = 0; + npc_type->d_meele_texture1 = 1; + npc_type->d_meele_texture2 = 1; + npc_type->merchanttype = 1; + npc_type->bodytype = 1; + npc_type->STR = 150; + npc_type->STA = 150; + npc_type->DEX = 150; + npc_type->AGI = 150; + npc_type->INT = 150; + npc_type->WIS = 150; + npc_type->CHA = 150; + npc_type->findable = 1; + + NPC* npc = new NPC(npc_type, 0, new_node.v.x, new_node.v.y, new_node.v.z, 0, FlyMode1); + npc->GiveNPCTypeData(npc_type); + entity_list.AddNPC(npc, true, true); + + ClosedListFlag = new int[Head.PathNodeCount]; + + return new_id; + } +} + +bool PathManager::DeleteNode(Client *c) +{ + if(!c) + { + return false; + } + + if(!c->GetTarget()) + { + c->Message(0, "You must target a node."); + return false; + } + + PathNode *Node = zone->pathing->FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ()); + if(!Node) + { + return false; + } + + return DeleteNode(Node->id); +} + +bool PathManager::DeleteNode(int32 id) +{ + //if the current list is > 1 in size create a new list of size current size - 1 + //transfer all but the current node to this new list and delete our current list + //set this new list to be our current list + //else if the size is 1 just delete our current list and set it to zero. + //go through and delete all ref in neighbors... + + if(Head.PathNodeCount > 1) + { + PathNode *t_PathNodes = new PathNode[Head.PathNodeCount-1]; + uint32 index = 0; + for(uint32 x = 0; x < Head.PathNodeCount; x++) + { + if(PathNodes[x].id != id) + { + t_PathNodes[index].id = PathNodes[x].id; + t_PathNodes[index].v.x = PathNodes[x].v.x; + t_PathNodes[index].v.y = PathNodes[x].v.y; + t_PathNodes[index].v.z = PathNodes[x].v.z; + t_PathNodes[index].bestz = PathNodes[x].bestz; + for(int n = 0; n < PATHNODENEIGHBOURS; ++n) + { + t_PathNodes[index].Neighbours[n].distance = PathNodes[x].Neighbours[n].distance; + t_PathNodes[index].Neighbours[n].DoorID = PathNodes[x].Neighbours[n].DoorID; + t_PathNodes[index].Neighbours[n].id = PathNodes[x].Neighbours[n].id; + t_PathNodes[index].Neighbours[n].Teleport = PathNodes[x].Neighbours[n].Teleport; + } + index++; + } + } + Head.PathNodeCount--; + delete[] PathNodes; + PathNodes = t_PathNodes; + + for(uint32 y = 0; y < Head.PathNodeCount; ++y) + { + for(int n = 0; n < PATHNODENEIGHBOURS; ++n) + { + if(PathNodes[y].Neighbours[n].id == id) + { + PathNodes[y].Neighbours[n].Teleport = 0; + PathNodes[y].Neighbours[n].DoorID = -1; + PathNodes[y].Neighbours[n].distance = 0.0; + PathNodes[y].Neighbours[n].id = -1; + } + } + } + safe_delete_array(ClosedListFlag); + ClosedListFlag = new int[Head.PathNodeCount]; + } + else + { + delete[] PathNodes; + PathNodes = NULL; + } + return true; +} + +void PathManager::ConnectNodeToNode(Client *c, int32 Node2, int32 teleport, int32 doorid) +{ + if(!c) + { + return; + } + + if(!c->GetTarget()) + { + c->Message(0, "You must target a node."); + return; + } + + PathNode *Node = zone->pathing->FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ()); + if(!Node) + { + return; + } + + c->Message(0, "Connecting %i to %i", Node->id, Node2); + + if(doorid == 0) + ConnectNodeToNode(Node->id, Node2, teleport); + else + ConnectNodeToNode(Node->id, Node2, teleport, doorid); +} + +void PathManager::ConnectNodeToNode(int32 Node1, int32 Node2, int32 teleport, int32 doorid) +{ + PathNode *a = NULL; + PathNode *b = NULL; + for(uint32 x = 0; x < Head.PathNodeCount; ++x) + { + if(PathNodes[x].id == Node1) + { + a = &PathNodes[x]; + if(b) + break; + } + else if(PathNodes[x].id == Node2) + { + b = &PathNodes[x]; + if(a) + break; + } + } + + if(a == NULL || b == NULL) + return; + + bool connect_a_to_b = true; + if(NodesConnected(a, b)) + connect_a_to_b = false; + + bool connect_b_to_a = true; + if(NodesConnected(b, a)) + connect_b_to_a = false; + + + if(connect_a_to_b) + { + for(int a_i = 0; a_i < PATHNODENEIGHBOURS; ++a_i) + { + if(a->Neighbours[a_i].id == -1) + { + a->Neighbours[a_i].id = b->id; + a->Neighbours[a_i].DoorID = doorid; + a->Neighbours[a_i].Teleport = teleport; + a->Neighbours[a_i].distance = VertexDistance(a->v, b->v); + break; + } + } + } + + if(connect_b_to_a) + { + for(int b_i = 0; b_i < PATHNODENEIGHBOURS; ++b_i) + { + if(b->Neighbours[b_i].id == -1) + { + b->Neighbours[b_i].id = a->id; + b->Neighbours[b_i].DoorID = doorid; + b->Neighbours[b_i].Teleport = teleport; + b->Neighbours[b_i].distance = VertexDistance(a->v, b->v); + break; + } + } + } +} + +void PathManager::ConnectNode(Client *c, int32 Node2, int32 teleport, int32 doorid) +{ + if(!c) + { + return; + } + + if(!c->GetTarget()) + { + c->Message(0, "You must target a node."); + return; + } + + PathNode *Node = zone->pathing->FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ()); + if(!Node) + { + return; + } + + c->Message(0, "Connecting %i to %i", Node->id, Node2); + + if(doorid == 0) + ConnectNode(Node->id, Node2, teleport); + else + ConnectNode(Node->id, Node2, teleport, doorid); +} + +void PathManager::ConnectNode(int32 Node1, int32 Node2, int32 teleport, int32 doorid) +{ + PathNode *a = NULL; + PathNode *b = NULL; + for(uint32 x = 0; x < Head.PathNodeCount; ++x) + { + if(PathNodes[x].id == Node1) + { + a = &PathNodes[x]; + if(b) + break; + } + else if(PathNodes[x].id == Node2) + { + b = &PathNodes[x]; + if(a) + break; + } + } + + if(a == NULL || b == NULL) + return; + + bool connect_a_to_b = true; + if(NodesConnected(a, b)) + connect_a_to_b = false; + + if(connect_a_to_b) + { + for(int a_i = 0; a_i < PATHNODENEIGHBOURS; ++a_i) + { + if(a->Neighbours[a_i].id == -1) + { + a->Neighbours[a_i].id = b->id; + a->Neighbours[a_i].DoorID = doorid; + a->Neighbours[a_i].Teleport = teleport; + a->Neighbours[a_i].distance = VertexDistance(a->v, b->v); + break; + } + } + } +} + +void PathManager::DisconnectNodeToNode(Client *c, int32 Node2) +{ + if(!c) + { + return; + } + + if(!c->GetTarget()) + { + c->Message(0, "You must target a node."); + return; + } + + PathNode *Node = zone->pathing->FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ()); + if(!Node) + { + return; + } + + DisconnectNodeToNode(Node->id, Node2); +} + +void PathManager::DisconnectNodeToNode(int32 Node1, int32 Node2) +{ + PathNode *a = NULL; + PathNode *b = NULL; + for(uint32 x = 0; x < Head.PathNodeCount; ++x) + { + if(PathNodes[x].id == Node1) + { + a = &PathNodes[x]; + if(b) + break; + } + else if(PathNodes[x].id == Node2) + { + b = &PathNodes[x]; + if(a) + break; + } + } + + if(a == NULL || b == NULL) + return; + + bool disconnect_a_from_b = false; + if(NodesConnected(a, b)) + disconnect_a_from_b = true; + + bool disconnect_b_from_a = false; + if(NodesConnected(b, a)) + disconnect_b_from_a = true; + + if(disconnect_a_from_b) + { + for(int a_i = 0; a_i < PATHNODENEIGHBOURS; ++a_i) + { + if(a->Neighbours[a_i].id == b->id) + { + a->Neighbours[a_i].distance = 0.0; + a->Neighbours[a_i].DoorID = -1; + a->Neighbours[a_i].id = -1; + a->Neighbours[a_i].Teleport = 0; + break; + } + } + } + + if(disconnect_b_from_a) + { + for(int b_i = 0; b_i < PATHNODENEIGHBOURS; ++b_i) + { + if(b->Neighbours[b_i].id == a->id) + { + b->Neighbours[b_i].distance = 0.0; + b->Neighbours[b_i].DoorID = -1; + b->Neighbours[b_i].id = -1; + b->Neighbours[b_i].Teleport = 0; + break; + } + } + } +} + +void PathManager::MoveNode(Client *c) +{ + if(!c) + { + return; + } + + if(!c->GetTarget()) + { + c->Message(0, "You must target a node."); + return; + } + + PathNode *Node = zone->pathing->FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ()); + if(!Node) + { + return; + } + + Node->v.x = c->GetX(); + Node->v.y = c->GetY(); + Node->v.z = c->GetZ(); + + if(zone->zonemap) + { + VERTEX loc(c->GetX(), c->GetY(), c->GetZ()); + Node->bestz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, loc, NULL, NULL); + } + else + { + Node->bestz = Node->v.z; + } +} + +void PathManager::DisconnectAll(Client *c) +{ + if(!c) + { + return; + } + + if(!c->GetTarget()) + { + c->Message(0, "You must target a node."); + return; + } + + PathNode *Node = zone->pathing->FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ()); + if(!Node) + { + return; + } + + for(int x = 0; x < PATHNODENEIGHBOURS; ++x) + { + Node->Neighbours[x].distance = 0; + Node->Neighbours[x].Teleport = 0; + Node->Neighbours[x].DoorID = -1; + Node->Neighbours[x].id = -1; + } + + for(uint32 i = 0; i < Head.PathNodeCount; ++i) + { + if(PathNodes[i].id == Node->id) + continue; + + for(int ix = 0; ix < PATHNODENEIGHBOURS; ++ix) + { + if(PathNodes[i].Neighbours[ix].id == Node->id) + { + PathNodes[i].Neighbours[ix].distance = 0; + PathNodes[i].Neighbours[ix].Teleport = 0; + PathNodes[i].Neighbours[ix].id = -1; + PathNodes[i].Neighbours[ix].DoorID = -1; + } + } + } +} + +//checks if anything in a points to b +bool PathManager::NodesConnected(PathNode *a, PathNode *b) +{ + if(!a) + return false; + + if(!b) + return false; + + for(int x = 0; x < PATHNODENEIGHBOURS; ++x) + { + if(a->Neighbours[x].id == b->id) + return true; + } + return false; +} + +bool PathManager::CheckLosFN(VERTEX a, VERTEX b) +{ + if(zone->zonemap) + { + VERTEX hit; + + VERTEX myloc; + VERTEX oloc; + + myloc.x = a.x; + myloc.y = a.y; + myloc.z = a.z; + + oloc.x = b.x; + oloc.y = b.y; + oloc.z = b.z; + + + if(zone->zonemap->LineIntersectsZone(myloc, oloc, 1.0f, NULL, NULL)) + { + return false; + } + } + return true; +} + +void PathManager::ProcessNodesAndSave(string filename) +{ + if(zone->zonemap) + { + for(uint32 i = 0; i < Head.PathNodeCount; ++i) + { + for(int in = 0; in < PATHNODENEIGHBOURS; ++in) + { + PathNodes[i].Neighbours[in].distance = 0.0; + PathNodes[i].Neighbours[in].DoorID = -1; + PathNodes[i].Neighbours[in].id = -1; + PathNodes[i].Neighbours[in].Teleport = 0; + } + } + + for(uint32 x = 0; x < Head.PathNodeCount; ++x) + { + for(uint32 y = 0; y < Head.PathNodeCount; ++y) + { + if(y == x) //can't connect to ourselves. + continue; + + if(!NodesConnected(&PathNodes[x], &PathNodes[y])) + { + if(VertexDistance(PathNodes[x].v, PathNodes[y].v) <= 200) + { + if(CheckLosFN(PathNodes[x].v, PathNodes[y].v)) + { + if(NoHazardsAccurate(PathNodes[x].v, PathNodes[y].v)) + { + ConnectNodeToNode(PathNodes[x].id, PathNodes[y].id, 0, 0); + } + } + } + } + } + } + } + DumpPath(filename); +} + +void PathManager::ResortConnections() +{ + NeighbourNode Neigh[PATHNODENEIGHBOURS]; + for(uint32 x = 0; x < Head.PathNodeCount; ++x) + { + int index = 0; + for(int y = 0; y < PATHNODENEIGHBOURS; ++y) + { + Neigh[y].distance = 0; + Neigh[y].DoorID = -1; + Neigh[y].id = -1; + Neigh[y].Teleport = 0; + } + + for(int z = 0; z < PATHNODENEIGHBOURS; ++z) + { + if(PathNodes[x].Neighbours[z].id != -1) + { + Neigh[index].id = PathNodes[x].Neighbours[z].id; + Neigh[index].distance = PathNodes[x].Neighbours[z].distance; + Neigh[index].DoorID = PathNodes[x].Neighbours[z].DoorID; + Neigh[index].Teleport = PathNodes[x].Neighbours[z].Teleport; + index++; + } + } + + for(int i = 0; i < PATHNODENEIGHBOURS; ++i) + { + PathNodes[x].Neighbours[i].distance = 0; + PathNodes[x].Neighbours[i].DoorID = -1; + PathNodes[x].Neighbours[i].id = -1; + PathNodes[x].Neighbours[i].Teleport = 0; + } + + for(int z = 0; z < PATHNODENEIGHBOURS; ++z) + { + PathNodes[x].Neighbours[z].distance = Neigh[z].distance; + PathNodes[x].Neighbours[z].DoorID = Neigh[z].DoorID; + PathNodes[x].Neighbours[z].id = Neigh[z].id; + PathNodes[x].Neighbours[z].Teleport = Neigh[z].Teleport; + } + } +} + +void PathManager::QuickConnect(Client *c, bool set) +{ + if(!c) + { + return; + } + + if(!c->GetTarget()) + { + c->Message(0, "You must target a node."); + return; + } + + PathNode *Node = zone->pathing->FindPathNodeByCoordinates(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ()); + if(!Node) + { + return; + } + + if(set) + { + c->Message(0, "Setting %i to the quick connect target", Node->id); + QuickConnectTarget = Node->id; + } + else + { + if(QuickConnectTarget >= 0) + { + ConnectNodeToNode(QuickConnectTarget, Node->id); + } + } +} + +struct InternalPathSort +{ + int16 old_id; + int16 new_id; +}; + +void PathManager::SortNodes() +{ + std::vector sorted_vals; + for(uint32 x = 0; x < Head.PathNodeCount; ++x) + { + InternalPathSort tmp; + tmp.old_id = PathNodes[x].id; + sorted_vals.push_back(tmp); + } + + PathNode *t_PathNodes = new PathNode[Head.PathNodeCount]; + memcpy(t_PathNodes, PathNodes, sizeof(PathNode)*Head.PathNodeCount); + for(uint32 i = 0; i < Head.PathNodeCount; ++i) + { + for(size_t j = 0; j < sorted_vals.size(); ++j) + { + if(sorted_vals[j].old_id == PathNodes[i].id) + { + if(i != PathNodes[i].id) + { + printf("Assigning new id of index %i differs from old id %i\n", i, PathNodes[i].id); + } + sorted_vals[j].new_id = i; + } + } + t_PathNodes[i].id = i; + } + + for(uint32 y = 0; y < Head.PathNodeCount; ++y) + { + for(int z = 0; z < PATHNODENEIGHBOURS; ++z) + { + if(PathNodes[y].Neighbours[z].id != -1) + { + int new_val = -1; + for(size_t c = 0; c < sorted_vals.size(); ++c) + { + if(PathNodes[y].Neighbours[z].id == sorted_vals[c].old_id) + { + new_val = sorted_vals[c].new_id; + break; + } + } + if(new_val != -1) + { + if(t_PathNodes[y].Neighbours[z].id != new_val) + { + printf("changing neighbor value to %i from %i\n", new_val, t_PathNodes[y].Neighbours[z].id); + } + t_PathNodes[y].Neighbours[z].id = new_val; + } + } + } + } + safe_delete_array(PathNodes); + PathNodes = t_PathNodes; +} diff --git a/zone/pathing.h b/zone/pathing.h new file mode 100644 index 000000000..e47377f50 --- /dev/null +++ b/zone/pathing.h @@ -0,0 +1,112 @@ +#ifndef PATHING_H +#define PATHING_H +#include +#include "map.h" +#include "../common/timer.h" +#include +#include +#include + +using namespace std; + +class Client; + +#define PATHNODENEIGHBOURS 50 + +#pragma pack(1) + +struct AStarNode +{ + int PathNodeID; + int Parent; + float HCost; + float GCost; + bool Teleport; +}; + +struct NeighbourNode { + int16 id; + float distance; + uint8 Teleport; + int16 DoorID; +}; + +struct PathNode { + uint16 id; + VERTEX v; + float bestz; + NeighbourNode Neighbours[PATHNODENEIGHBOURS]; +}; + +struct PathFileHeader { + uint32 version; + uint32 PathNodeCount; +}; + +#pragma pack() + +struct PathNodeSortStruct +{ + int id; + float Distance; +}; + +enum LOSType{ UnknownLOS, HaveLOS, NoLOS }; + +class PathManager { + +public: + PathManager(); + ~PathManager(); + + + static PathManager *LoadPathFile(const char *ZoneName); + bool loadPaths(FILE *fp); + void PrintPathing(); + list FindRoute(VERTEX Start, VERTEX End); + list FindRoute(int startID, int endID); + + VERTEX GetPathNodeCoordinates(int NodeNumber, bool BestZ = true); + bool CheckLosFN(VERTEX a, VERTEX b); + void SpawnPathNodes(); + void MeshTest(); + void SimpleMeshTest(); + int FindNearestPathNode(VERTEX Position); + bool NoHazards(VERTEX From, VERTEX To); + bool NoHazardsAccurate(VERTEX From, VERTEX To); + void OpenDoors(int Node1, int Node2, Mob* ForWho); + + PathNode* FindPathNodeByCoordinates(float x, float y, float z); + void ShowPathNodeNeighbours(Client *c); + int GetRandomPathNode(); + + void NodeInfo(Client *c); + int32 AddNode(float x, float y, float z, float best_z, int32 requested_id = 0); //return -1 on failure, else returns the id of this node + bool DeleteNode(Client *c); + bool DeleteNode(int32 id); //returns true on success, false on failure, tries to delete a node from this map + void ConnectNodeToNode(Client *c, int32 Node2, int32 teleport = 0, int32 doorid = -1); //connects a node both ways + void ConnectNodeToNode(int32 Node1, int32 Node2, int32 teleport = 0, int32 doorid = -1); + void ConnectNode(Client *c, int32 Node2, int32 teleport = 0, int32 doorid = -1); //connects a node one way + void ConnectNode(int32 Node1, int32 Node2, int32 teleport = 0, int32 doorid = -1); + void DisconnectNodeToNode(Client *c, int32 Node2); + void DisconnectNodeToNode(int32 Node1, int32 Node2); + void MoveNode(Client *c); + void DisconnectAll(Client *c); + bool NodesConnected(PathNode *a, PathNode *b); + void DumpPath(string filename); + void ProcessNodesAndSave(string filename); + void ResortConnections(); + void QuickConnect(Client *c, bool set = false); + void SortNodes(); + +private: + PathFileHeader Head; + PathNode *PathNodes; + int QuickConnectTarget; + + int *ClosedListFlag; +}; + + +#endif + diff --git a/zone/perl_PlayerCorpse.cpp b/zone/perl_PlayerCorpse.cpp new file mode 100644 index 000000000..fd68a4961 --- /dev/null +++ b/zone/perl_PlayerCorpse.cpp @@ -0,0 +1,839 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of tmp. Do not edit this file, edit tmp instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +#include "PlayerCorpse.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +XS(XS_Corpse_GetCharID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_GetCharID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::GetCharID(THIS)"); + { + Corpse * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCharID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Corpse_GetDecayTime); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_GetDecayTime) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::GetDecayTime(THIS)"); + { + Corpse * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDecayTime(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Corpse_Lock); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_Lock) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::Lock(THIS)"); + { + Corpse * THIS; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Lock(); + } + XSRETURN_EMPTY; +} + +XS(XS_Corpse_UnLock); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_UnLock) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::UnLock(THIS)"); + { + Corpse * THIS; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->UnLock(); + } + XSRETURN_EMPTY; +} + +XS(XS_Corpse_IsLocked); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_IsLocked) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::IsLocked(THIS)"); + { + Corpse * THIS; + bool RETVAL; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsLocked(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Corpse_ResetLooter); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_ResetLooter) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::ResetLooter(THIS)"); + { + Corpse * THIS; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->ResetLooter(); + } + XSRETURN_EMPTY; +} + +XS(XS_Corpse_GetDBID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_GetDBID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::GetDBID(THIS)"); + { + Corpse * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDBID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Corpse_GetOwnerName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_GetOwnerName) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::GetOwnerName(THIS)"); + { + Corpse * THIS; + char * RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetOwnerName(); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Corpse_SetDecayTimer); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_SetDecayTimer) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Corpse::SetDecayTimer(THIS, decaytime)"); + { + Corpse * THIS; + uint32 decaytime = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetDecayTimer(decaytime); + } + XSRETURN_EMPTY; +} + +XS(XS_Corpse_IsEmpty); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_IsEmpty) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::IsEmpty(THIS)"); + { + Corpse * THIS; + bool RETVAL; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsEmpty(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Corpse_AddItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_AddItem) +{ + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: Corpse::AddItem(THIS, itemnum, charges, slot= 0)"); + { + Corpse * THIS; + uint32 itemnum = (uint32)SvUV(ST(1)); + uint16 charges = (uint16)SvUV(ST(2)); + int16 slot; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 4) + slot = 0; + else { + slot = (int16)SvIV(ST(3)); + } + + THIS->AddItem(itemnum, charges, slot); + } + XSRETURN_EMPTY; +} + +XS(XS_Corpse_GetWornItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_GetWornItem) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Corpse::GetWornItem(THIS, equipSlot)"); + { + Corpse * THIS; + uint32 RETVAL; + dXSTARG; + int16 equipSlot = (int16)SvIV(ST(1)); + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetWornItem(equipSlot); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Corpse_RemoveItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_RemoveItem) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Corpse::RemoveItem(THIS, lootslot)"); + { + Corpse * THIS; + uint16 lootslot = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveItem(lootslot); + } + XSRETURN_EMPTY; +} + +XS(XS_Corpse_SetCash); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_SetCash) +{ + dXSARGS; + if (items != 5) + Perl_croak(aTHX_ "Usage: Corpse::SetCash(THIS, in_copper, in_silver, in_gold, in_platinum)"); + { + Corpse * THIS; + uint16 in_copper = (uint16)SvUV(ST(1)); + uint16 in_silver = (uint16)SvUV(ST(2)); + uint16 in_gold = (uint16)SvUV(ST(3)); + uint16 in_platinum = (uint16)SvUV(ST(4)); + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetCash(in_copper, in_silver, in_gold, in_platinum); + } + XSRETURN_EMPTY; +} + +XS(XS_Corpse_RemoveCash); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_RemoveCash) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::RemoveCash(THIS)"); + { + Corpse * THIS; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveCash(); + } + XSRETURN_EMPTY; +} + +XS(XS_Corpse_CountItems); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_CountItems) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::CountItems(THIS)"); + { + Corpse * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CountItems(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Corpse_Delete); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_Delete) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::Delete(THIS)"); + { + Corpse * THIS; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Delete(); + } + XSRETURN_EMPTY; +} + +XS(XS_Corpse_GetCopper); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_GetCopper) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::GetCopper(THIS)"); + { + Corpse * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCopper(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Corpse_GetSilver); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_GetSilver) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::GetSilver(THIS)"); + { + Corpse * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetSilver(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Corpse_GetGold); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_GetGold) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::GetGold(THIS)"); + { + Corpse * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetGold(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Corpse_GetPlatinum); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_GetPlatinum) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::GetPlatinum(THIS)"); + { + Corpse * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetPlatinum(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Corpse_Summon); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_Summon) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Corpse::Summon(THIS, client, spell)"); + { + Corpse * THIS; + Client* client; + bool spell = (bool)SvTRUE(ST(2)); + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + client = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "client is not of type Client"); + if(client == NULL) + Perl_croak(aTHX_ "client is NULL, avoiding crash."); + + THIS->Summon(client, spell, true); + } + XSRETURN_EMPTY; +} + +XS(XS_Corpse_CastRezz); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_CastRezz) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Corpse::CastRezz(THIS, spellid, Caster)"); + { + Corpse * THIS; + uint16 spellid = (uint16)SvUV(ST(1)); + Mob* Caster; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(2), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(2))); + Caster = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "Caster is not of type Mob"); + if(Caster == NULL) + Perl_croak(aTHX_ "Caster is NULL, avoiding crash."); + + THIS->CastRezz(spellid, Caster); + } + XSRETURN_EMPTY; +} + +XS(XS_Corpse_CompleteRezz); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_CompleteRezz) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::CompleteRezz(THIS)"); + { + Corpse * THIS; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->CompleteRezz(); + } + XSRETURN_EMPTY; +} + +XS(XS_Corpse_CanMobLoot); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_CanMobLoot) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Corpse::CanMobLoot(THIS, charid)"); + { + Corpse * THIS; + bool RETVAL; + int charid = (int)SvIV(ST(1)); + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CanMobLoot(charid); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Corpse_AllowMobLoot); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_AllowMobLoot) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Corpse::AllowMobLoot(THIS, them, slot)"); + { + Corpse * THIS; + Mob * them; + uint8 slot = (uint8)SvUV(ST(2)); + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + them = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "them is not of type Mob"); + if(them == NULL) + Perl_croak(aTHX_ "them is NULL, avoiding crash."); + + THIS->AllowMobLoot(them, slot); + } + XSRETURN_EMPTY; +} + +XS(XS_Corpse_AddLooter); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_AddLooter) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Corpse::AddLooter(THIS, who)"); + { + Corpse * THIS; + Mob * who; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + who = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "who is not of type Mob"); + if(who == NULL) + Perl_croak(aTHX_ "who is NULL, avoiding crash."); + + THIS->AddLooter(who); + } + XSRETURN_EMPTY; +} + +XS(XS_Corpse_IsRezzed); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Corpse_IsRezzed) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Corpse::IsRezzed(THIS)"); + { + Corpse * THIS; + bool RETVAL; + + if (sv_derived_from(ST(0), "Corpse")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Corpse *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Corpse"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->Rezzed(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_Corpse); /* prototype to pass -Wmissing-prototypes */ +XS(boot_Corpse) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "GetCharID"), XS_Corpse_GetCharID, file, "$"); + newXSproto(strcpy(buf, "GetDecayTime"), XS_Corpse_GetDecayTime, file, "$"); + newXSproto(strcpy(buf, "Lock"), XS_Corpse_Lock, file, "$"); + newXSproto(strcpy(buf, "UnLock"), XS_Corpse_UnLock, file, "$"); + newXSproto(strcpy(buf, "IsLocked"), XS_Corpse_IsLocked, file, "$"); + newXSproto(strcpy(buf, "ResetLooter"), XS_Corpse_ResetLooter, file, "$"); + newXSproto(strcpy(buf, "GetDBID"), XS_Corpse_GetDBID, file, "$"); + newXSproto(strcpy(buf, "GetOwnerName"), XS_Corpse_GetOwnerName, file, "$"); + newXSproto(strcpy(buf, "SetDecayTimer"), XS_Corpse_SetDecayTimer, file, "$$"); + newXSproto(strcpy(buf, "IsEmpty"), XS_Corpse_IsEmpty, file, "$"); + newXSproto(strcpy(buf, "AddItem"), XS_Corpse_AddItem, file, "$$$;$"); + newXSproto(strcpy(buf, "GetWornItem"), XS_Corpse_GetWornItem, file, "$$"); + newXSproto(strcpy(buf, "RemoveItem"), XS_Corpse_RemoveItem, file, "$$"); + newXSproto(strcpy(buf, "SetCash"), XS_Corpse_SetCash, file, "$$$$$"); + newXSproto(strcpy(buf, "RemoveCash"), XS_Corpse_RemoveCash, file, "$"); + newXSproto(strcpy(buf, "CountItems"), XS_Corpse_CountItems, file, "$"); + newXSproto(strcpy(buf, "Delete"), XS_Corpse_Delete, file, "$"); + newXSproto(strcpy(buf, "GetCopper"), XS_Corpse_GetCopper, file, "$"); + newXSproto(strcpy(buf, "GetSilver"), XS_Corpse_GetSilver, file, "$"); + newXSproto(strcpy(buf, "GetGold"), XS_Corpse_GetGold, file, "$"); + newXSproto(strcpy(buf, "GetPlatinum"), XS_Corpse_GetPlatinum, file, "$"); + newXSproto(strcpy(buf, "Summon"), XS_Corpse_Summon, file, "$$$"); + newXSproto(strcpy(buf, "CastRezz"), XS_Corpse_CastRezz, file, "$$$"); + newXSproto(strcpy(buf, "CompleteRezz"), XS_Corpse_CompleteRezz, file, "$"); + newXSproto(strcpy(buf, "CanMobLoot"), XS_Corpse_CanMobLoot, file, "$$"); + newXSproto(strcpy(buf, "AllowMobLoot"), XS_Corpse_AllowMobLoot, file, "$$$"); + newXSproto(strcpy(buf, "AddLooter"), XS_Corpse_AddLooter, file, "$$"); + newXSproto(strcpy(buf, "IsRezzed"), XS_Corpse_IsRezzed, file, "$"); + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES + diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp new file mode 100644 index 000000000..4b06b716f --- /dev/null +++ b/zone/perl_client.cpp @@ -0,0 +1,5879 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of tmp. Do not edit this file, edit tmp instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +#include "client.h" +#include "titles.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + +XS(XS_Client_SendSound); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SendSound) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::SendSound(THIS)"); + { + Client * THIS; + + 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->SendSound(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_Save); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_Save) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::Save(THIS, iCommitNow)"); + { + Client * THIS; + bool RETVAL; + uint8 iCommitNow = (uint8)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->Save(iCommitNow); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_SaveBackup); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SaveBackup) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::SaveBackup(THIS)"); + { + Client * THIS; + + 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->SaveBackup(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_Connected); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_Connected) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::Connected(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->Connected(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_InZone); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_InZone) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::InZone(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->InZone(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_Kick); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_Kick) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::Kick(THIS)"); + { + Client * THIS; + + 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->Kick(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_Disconnect); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_Disconnect) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::Disconnect(THIS)"); + { + Client * THIS; + + 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->Disconnect(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_IsLD); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IsLD) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::IsLD(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->IsLD(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_WorldKick); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_WorldKick) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::WorldKick(THIS)"); + { + Client * THIS; + + 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->WorldKick(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetAnon); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetAnon) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetAnon(THIS)"); + { + Client * THIS; + uint8 RETVAL; + 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."); + + RETVAL = THIS->GetAnon(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_Duck); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_Duck) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::Duck(THIS)"); + { + Client * THIS; + + 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->Duck(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_Stand); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_Stand) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::Stand(THIS)"); + { + Client * THIS; + + 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->Stand(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SetGM); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetGM) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetGM(THIS, toggle)"); + { + Client * THIS; + bool toggle = (bool)SvTRUE(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetGM(toggle); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SetPVP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetPVP) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetPVP(THIS, toggle)"); + { + Client * THIS; + bool toggle = (bool)SvTRUE(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetPVP(toggle); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetPVP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetPVP) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetPVP(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->GetPVP(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_GetGM); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetGM) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetGM(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->GetGM(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_SetBaseClass); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetBaseClass) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetBaseClass(THIS, i)"); + { + Client * THIS; + uint32 i = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetBaseClass(i); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SetBaseRace); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetBaseRace) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetBaseRace(THIS, i)"); + { + Client * THIS; + uint32 i = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetBaseRace(i); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SetBaseGender); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetBaseGender) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetBaseGender(THIS, i)"); + { + Client * THIS; + uint32 i = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetBaseGender(i); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetBaseFace); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetBaseFace) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetBaseFace(THIS)"); + { + Client * THIS; + uint8 RETVAL; + 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."); + + RETVAL = THIS->GetBaseFace(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetLanguageSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetLanguageSkill) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetLanguageSkill(THIS, n)"); + { + Client * THIS; + uint8 RETVAL; + dXSTARG; + uint16 n = (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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetLanguageSkill(n); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetLastName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetLastName) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetLastName(THIS)"); + { + Client * THIS; + Const_char * RETVAL; + 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."); + + RETVAL = THIS->GetLastName(); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Client_GetLDoNPointsTheme); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetLDoNPointsTheme) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetLDoNPointsTheme(THIS, theme)"); + { + Client * THIS; + uint32 RETVAL; + dXSTARG; + int32 theme_out = (int32)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetLDoNPointsTheme(theme_out); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetBaseSTR); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetBaseSTR) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetBaseSTR(THIS)"); + { + Client * THIS; + uint8 RETVAL; + 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."); + + RETVAL = THIS->GetBaseSTR(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetBaseSTA); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetBaseSTA) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetBaseSTA(THIS)"); + { + Client * THIS; + uint8 RETVAL; + 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."); + + RETVAL = THIS->GetBaseSTA(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetBaseCHA); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetBaseCHA) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetBaseCHA(THIS)"); + { + Client * THIS; + uint8 RETVAL; + 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."); + + RETVAL = THIS->GetBaseCHA(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetBaseDEX); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetBaseDEX) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetBaseDEX(THIS)"); + { + Client * THIS; + uint8 RETVAL; + 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."); + + RETVAL = THIS->GetBaseDEX(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetBaseINT); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetBaseINT) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetBaseINT(THIS)"); + { + Client * THIS; + uint8 RETVAL; + 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."); + + RETVAL = THIS->GetBaseINT(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetBaseAGI); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetBaseAGI) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetBaseAGI(THIS)"); + { + Client * THIS; + uint8 RETVAL; + 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."); + + RETVAL = THIS->GetBaseAGI(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetBaseWIS); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetBaseWIS) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetBaseWIS(THIS)"); + { + Client * THIS; + uint8 RETVAL; + 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."); + + RETVAL = THIS->GetBaseWIS(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetWeight); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetWeight) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetWeight(THIS)"); + { + Client * THIS; + uint16 RETVAL; + 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."); + + RETVAL = THIS->GetWeight(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetEXP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetEXP) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetEXP(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetEXP(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetAAExp); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetAAExp) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetAAExp(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetAAXP(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetTotalSecondsPlayed); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetTotalSecondsPlayed) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetTotalSecondsPlayed(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetTotalSecondsPlayed(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_UpdateLDoNPoints); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_UpdateLDoNPoints) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::UpdateLDoNPoints(THIS, points, theme)"); + { + Client * THIS; + bool RETVAL; + int32 points = (int32)SvIV(ST(1)); + uint32 theme = (uint32)SvUV(ST(2)); + + 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."); + + RETVAL = THIS->UpdateLDoNPoints(points, theme); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_SetDeity); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetDeity) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetDeity(THIS, i)"); + { + Client * THIS; + uint32 i = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetDeity(i); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_AddEXP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_AddEXP) +{ + dXSARGS; + if (items < 2 || items > 4) + Perl_croak(aTHX_ "Usage: Client::AddEXP(THIS, add_exp, conlevel= 0xFF, resexp= false)"); + { + Client * THIS; + uint32 add_exp = (uint32)SvUV(ST(1)); + uint8 conlevel; + bool resexp; + + 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."); + + if (items < 3) + conlevel = 0xFF; + else { + conlevel = (uint8)SvUV(ST(2)); + } + + if (items < 4) + resexp = false; + else { + resexp = (bool)SvTRUE(ST(3)); + } + + THIS->AddEXP(add_exp, conlevel, resexp); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SetEXP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetEXP) +{ + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: Client::SetEXP(THIS, set_exp, set_aaxp, resexp=false)"); + { + Client * THIS; + uint32 set_exp = (uint32)SvUV(ST(1)); + uint32 set_aaxp = (uint32)SvUV(ST(2)); + bool resexp; + + 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."); + + if (items < 4) + resexp = false; + else { + resexp = (bool)SvTRUE(ST(3)); + } + + THIS->SetEXP(set_exp, set_aaxp, resexp); + } + XSRETURN_EMPTY; +} + +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)"); + { + Client * THIS; + int to_zone; + float new_x; + float new_y; + float new_z; + + 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."); + + if (items < 2) + to_zone = -1; + else { + to_zone = (int)SvIV(ST(1)); + } + + if (items < 3) + new_x = 0.0f; + else { + new_x = (float)SvNV(ST(2)); + } + + if (items < 4) + new_y = 0.0f; + else { + new_y = (float)SvNV(ST(3)); + } + + if (items < 5) + new_z = 0.0f; + else { + new_z = (float)SvNV(ST(4)); + } + + THIS->SetBindPoint(to_zone, new_x, new_y, new_z); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetBindX); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetBindX) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Client::GetBindX(index)"); + { + Client * THIS; + int index = 0; + float RETVAL; + 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."); + + if (items == 1) + index = 0; + else if (items == 2) { + index = (uint32)SvUV(ST(1)); + } + + RETVAL = THIS->GetBindX(index); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetBindY); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetBindY) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Client::GetBindY(index)"); + { + Client * THIS; + int index = 0; + float RETVAL; + 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."); + + if (items == 1) + index = 0; + else if (items == 2) { + index = (uint32)SvUV(ST(1));; + } + + RETVAL = THIS->GetBindY(index); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetBindZ); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetBindZ) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Client::GetBindZ(index)"); + { + Client * THIS; + int index = 0; + float RETVAL; + 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."); + + if (items == 1) + index = 0; + else if (items == 2) { + index = (uint32)SvUV(ST(1)); + } + + RETVAL = THIS->GetBindZ(index); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetBindHeading); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetBindHeading) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Client::GetBindHeading(index)"); + { + Client * THIS; + int index = 0; + float RETVAL; + 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."); + + if (items == 1) + index = 0; + else if (items == 2) { + index = (uint32)SvUV(ST(1)); + } + + RETVAL = THIS->GetBindHeading(index); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetBindZoneID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetBindZoneID) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Client::GetBindZoneID(index)"); + { + Client * THIS; + uint32 index = 0; + uint32 RETVAL; + 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."); + + if (items == 1) + index = 0; + else if (items == 2) { + index = (uint32)SvUV(ST(1)); + } + + RETVAL = THIS->GetBindZoneID(index); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + + +XS(XS_Client_MovePC); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_MovePC) +{ + dXSARGS; + if (items != 6) + Perl_croak(aTHX_ "Usage: Client::MovePC(THIS, zoneID, x, y, z, heading)"); + { + Client * THIS; + uint32 zoneID = (uint32)SvUV(ST(1)); + float x = (float)SvNV(ST(2)); + float y = (float)SvNV(ST(3)); + float z = (float)SvNV(ST(4)); + float heading = (float)SvNV(ST(5)); + + 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->MovePC(zoneID, x, y, z, heading); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_MovePCInstance); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_MovePCInstance) +{ + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: Client::MovePCInstance(THIS, zoneID, instanceID, x, y, z, heading)"); + { + Client * THIS; + uint32 zoneID = (uint32)SvUV(ST(1)); + uint32 instanceID = (uint32)SvUV(ST(2)); + float x = (float)SvNV(ST(3)); + float y = (float)SvNV(ST(4)); + float z = (float)SvNV(ST(5)); + float heading = (float)SvNV(ST(6)); + + 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->MovePC(zoneID, instanceID, x, y, z, heading); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_ChangeLastName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_ChangeLastName) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::ChangeLastName(THIS, in_lastname)"); + { + Client * THIS; + char* in_lastname = (char *)SvPV_nolen(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->ChangeLastName(in_lastname); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetFactionLevel); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetFactionLevel) +{ + dXSARGS; + if (items != 8) + Perl_croak(aTHX_ "Usage: Client::GetFactionLevel(THIS, char_id, npc_id, p_race, p_class, p_deity, pFaction, tnpc)"); + { + Client * THIS; + FACTION_VALUE RETVAL; + dXSTARG; + uint32 char_id = (uint32)SvUV(ST(1)); + uint32 npc_id = (uint32)SvUV(ST(2)); + uint32 p_race = (uint32)SvUV(ST(3)); + uint32 p_class = (uint32)SvUV(ST(4)); + uint32 p_deity = (uint32)SvUV(ST(5)); + int32 pFaction = (int32)SvIV(ST(6)); + Mob* tnpc; + + 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."); + + if (sv_derived_from(ST(7), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(7))); + tnpc = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "tnpc is not of type Mob"); + if(tnpc == NULL) + Perl_croak(aTHX_ "tnpc is NULL, avoiding crash."); + + RETVAL = THIS->GetFactionLevel(char_id, npc_id, p_race, p_class, p_deity, pFaction, tnpc); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_SetFactionLevel); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetFactionLevel) +{ + dXSARGS; + if (items != 6) + Perl_croak(aTHX_ "Usage: Client::SetFactionLevel(THIS, char_id, npc_id, char_class, char_race, char_deity)"); + { + Client * THIS; + uint32 char_id = (uint32)SvUV(ST(1)); + uint32 npc_id = (uint32)SvUV(ST(2)); + uint8 char_class = (uint8)SvUV(ST(3)); + uint8 char_race = (uint8)SvUV(ST(4)); + uint8 char_deity = (uint8)SvUV(ST(5)); + + 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->SetFactionLevel(char_id, npc_id, char_class, char_race, char_deity); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SetFactionLevel2); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetFactionLevel2) +{ + dXSARGS; + if (items < 7 || items > 8) + Perl_croak(aTHX_ "Usage: Client::SetFactionLevel2(THIS, char_id, faction_id, char_class, char_race, char_deity, value, temp)"); + { + Client * THIS; + uint32 char_id = (uint32)SvUV(ST(1)); + int32 faction_id = (int32)SvIV(ST(2)); + uint8 char_class = (uint8)SvUV(ST(3)); + uint8 char_race = (uint8)SvUV(ST(4)); + uint8 char_deity = (uint8)SvUV(ST(5)); + int32 value = (int32)SvIV(ST(6)); + uint8 temp; + + 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."); + + if (items == 7) + temp = 0; + else { + temp = (uint8)SvUV(ST(7)); + } + + THIS->SetFactionLevel2(char_id, faction_id, char_class, char_race, char_deity, value, temp); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetRawItemAC); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetRawItemAC) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetRawItemAC(THIS)"); + { + Client * THIS; + int16 RETVAL; + 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."); + + RETVAL = THIS->GetRawItemAC(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_AccountID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_AccountID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::AccountID(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->AccountID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_AccountName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_AccountName) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::AccountName(THIS)"); + { + Client * THIS; + Const_char * RETVAL; + 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."); + + RETVAL = THIS->AccountName(); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Client_Admin); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_Admin) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::Admin(THIS)"); + { + Client * THIS; + int16 RETVAL; + 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."); + + RETVAL = THIS->Admin(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_CharacterID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_CharacterID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::CharacterID(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->CharacterID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_UpdateAdmin); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_UpdateAdmin) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Client::UpdateAdmin(THIS, iFromDB= true)"); + { + Client * THIS; + bool iFromDB; + + 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."); + + if (items < 2) + iFromDB = true; + else { + iFromDB = (bool)SvTRUE(ST(1)); + } + + THIS->UpdateAdmin(iFromDB); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_UpdateWho); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_UpdateWho) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Client::UpdateWho(THIS, remove= 0)"); + { + Client * THIS; + uint8 remove; + + 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."); + + if (items < 2) + remove = 0; + else { + remove = (uint8)SvUV(ST(1)); + } + + THIS->UpdateWho(remove); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GuildRank); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GuildRank) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GuildRank(THIS)"); + { + Client * THIS; + uint8 RETVAL; + 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."); + + RETVAL = THIS->GuildRank(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GuildID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GuildID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GuildID(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GuildID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetFace); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetFace) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetFace(THIS)"); + { + Client * THIS; + uint8 RETVAL; + 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."); + + RETVAL = THIS->GetFace(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_TakeMoneyFromPP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_TakeMoneyFromPP) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Client::TakeMoneyFromPP(THIS, copper, updateclient=false)"); + { + Client * THIS; + bool RETVAL; + bool updateclient = false; + uint32 copper = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items > 2) + updateclient = (bool)SvTRUE(ST(2)); + + RETVAL = THIS->TakeMoneyFromPP(copper, updateclient); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_AddMoneyToPP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_AddMoneyToPP) +{ + dXSARGS; + if (items != 6) + Perl_croak(aTHX_ "Usage: Client::AddMoneyToPP(THIS, copper, silver, gold, platinum, updateclient)"); + { + Client * THIS; + uint32 copper = (uint32)SvUV(ST(1)); + uint32 silver = (uint32)SvUV(ST(2)); + uint32 gold = (uint32)SvUV(ST(3)); + uint32 platinum = (uint32)SvUV(ST(4)); + bool updateclient = (bool)SvTRUE(ST(5)); + + 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->AddMoneyToPP(copper, silver, gold, platinum, updateclient); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_TGB); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_TGB) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::TGB(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->TGB(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_GetSkillPoints); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetSkillPoints) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetSkillPoints(THIS)"); + { + Client * THIS; + uint16 RETVAL; + 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."); + + RETVAL = THIS->GetSkillPoints(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_SetSkillPoints); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetSkillPoints) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetSkillPoints(THIS, inp)"); + { + Client * THIS; + int inp = (int)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetSkillPoints(inp); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_IncreaseSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IncreaseSkill) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Client::IncreaseSkill(THIS, skill_id, value= 1)"); + { + Client * THIS; + int skill_id = (int)SvIV(ST(1)); + int value; + + 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."); + + if (items < 3) + value = 1; + else { + value = (int)SvIV(ST(2)); + } + + THIS->IncreaseSkill(skill_id, value); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_IncreaseLanguageSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IncreaseLanguageSkill) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Client::IncreaseLanguageSkill(THIS, skill_id, value= 1)"); + { + Client * THIS; + int skill_id = (int)SvIV(ST(1)); + int value; + + 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."); + + if (items < 3) + value = 1; + else { + value = (int)SvIV(ST(2)); + } + + THIS->IncreaseLanguageSkill(skill_id, value); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetSkill) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetSkill(THIS, skill_id)"); + { + Client * THIS; + uint16 RETVAL; + dXSTARG; + SkillType skill_id = (SkillType)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetSkill(skill_id); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetRawSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetRawSkill) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetRawSkill(THIS, skill_id)"); + { + Client * THIS; + uint32 RETVAL; + dXSTARG; + SkillType skill_id = (SkillType)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetRawSkill(skill_id); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_HasSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_HasSkill) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::HasSkill(THIS, skill_id)"); + { + Client * THIS; + bool RETVAL; + SkillType skill_id = (SkillType)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->HasSkill(skill_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_CanHaveSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_CanHaveSkill) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::CanHaveSkill(THIS, skill_id)"); + { + Client * THIS; + bool RETVAL; + SkillType skill_id = (SkillType)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CanHaveSkill(skill_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_SetSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetSkill) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::SetSkill(THIS, skill_num, value)"); + { + Client * THIS; + SkillType skill_num = (SkillType)SvUV(ST(1)); + uint16 value = (uint16)SvUV(ST(2)); + + 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->SetSkill(skill_num, value); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_AddSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_AddSkill) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::AddSkill(THIS, skillid, value)"); + { + Client * THIS; + SkillType skillid = (SkillType)SvUV(ST(1)); + uint16 value = (uint16)SvUV(ST(2)); + + 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->AddSkill(skillid, value); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_CheckSpecializeIncrease); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_CheckSpecializeIncrease) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::CheckSpecializeIncrease(THIS, spell_id)"); + { + Client * THIS; + uint16 spell_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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->CheckSpecializeIncrease(spell_id); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_CheckIncreaseSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_CheckIncreaseSkill) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Client::CheckIncreaseSkill(THIS, skillid, chancemodi= 0)"); + { + Client * THIS; + bool RETVAL; + SkillType skillid = (SkillType)SvUV(ST(1)); + int chancemodi; + + 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."); + + if (items < 3) + chancemodi = 0; + else { + chancemodi = (int)SvIV(ST(2)); + } + + RETVAL = THIS->CheckIncreaseSkill(skillid, NULL, chancemodi); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_SetLanguageSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetLanguageSkill) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::SetLanguageSkill(THIS, langid, value)"); + { + Client * THIS; + int langid = (int)SvIV(ST(1)); + int value = (int)SvIV(ST(2)); + + 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->SetLanguageSkill(langid, value); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_MaxSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_MaxSkill) +{ + dXSARGS; + if (items < 2 || items > 4) + Perl_croak(aTHX_ "Usage: Client::MaxSkill(THIS, skillid, class, level)"); + { + Client * THIS; + uint16 RETVAL; + SkillType skillid = (SkillType)SvUV(ST(1)); + uint16 class_ = 0; + uint16 level = 0; + 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."); + + if(items > 2) + class_ = (uint16)SvUV(ST(2)); + else + class_ = THIS->GetClass(); + + if(items > 3) + level = (uint16)SvUV(ST(3)); + else + level = THIS->GetLevel(); + + RETVAL = THIS->MaxSkill(skillid, class_, level); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GMKill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GMKill) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GMKill(THIS)"); + { + Client * THIS; + + 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->GMKill(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_IsMedding); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IsMedding) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::IsMedding(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->IsMedding(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_GetDuelTarget); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetDuelTarget) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetDuelTarget(THIS)"); + { + Client * THIS; + uint16 RETVAL; + 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."); + + RETVAL = THIS->GetDuelTarget(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_IsDueling); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IsDueling) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::IsDueling(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->IsDueling(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_SetDuelTarget); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetDuelTarget) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetDuelTarget(THIS, set_id)"); + { + Client * THIS; + uint16 set_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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetDuelTarget(set_id); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SetDueling); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetDueling) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetDueling(THIS, duel)"); + { + Client * THIS; + bool duel = (bool)SvTRUE(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetDueling(duel); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_ResetAA); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_ResetAA) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::ResetAA(THIS)"); + { + Client * THIS; + + 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->ResetAA(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_MemSpell); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_MemSpell) +{ + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: Client::MemSpell(THIS, spell_id, slot, update_client= true)"); + { + Client * THIS; + uint16 spell_id = (uint16)SvUV(ST(1)); + int slot = (int)SvIV(ST(2)); + bool update_client; + + 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."); + + if (items < 4) + update_client = true; + else { + update_client = (bool)SvTRUE(ST(3)); + } + + THIS->MemSpell(spell_id, slot, update_client); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_UnmemSpell); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_UnmemSpell) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Client::UnmemSpell(THIS, slot, update_client= true)"); + { + Client * THIS; + int slot = (int)SvIV(ST(1)); + bool update_client; + + 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."); + + if (items < 3) + update_client = true; + else { + update_client = (bool)SvTRUE(ST(2)); + } + + THIS->UnmemSpell(slot, update_client); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_UnmemSpellAll); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_UnmemSpellAll) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Client::UnmemSpellAll(THIS, update_client= true)"); + { + Client * THIS; + bool update_client; + + 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."); + + if (items < 2) + update_client = true; + else { + update_client = (bool)SvTRUE(ST(1)); + } + + THIS->UnmemSpellAll(update_client); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_ScribeSpell); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_ScribeSpell) +{ + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: Client::ScribeSpell(THIS, spell_id, slot, update_client= true)"); + { + Client * THIS; + uint16 spell_id = (uint16)SvUV(ST(1)); + int slot = (int)SvIV(ST(2)); + bool update_client; + + 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."); + + if (items < 4) + update_client = true; + else { + update_client = (bool)SvTRUE(ST(3)); + } + + THIS->ScribeSpell(spell_id, slot, update_client); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_UnscribeSpell); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_UnscribeSpell) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Client::UnscribeSpell(THIS, slot, update_client= true)"); + { + Client * THIS; + int slot = (int)SvIV(ST(1)); + bool update_client; + + 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."); + + if (items < 3) + update_client = true; + else { + update_client = (bool)SvTRUE(ST(2)); + } + + THIS->UnscribeSpell(slot, update_client); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_UnscribeSpellAll); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_UnscribeSpellAll) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Client::UnscribeSpellAll(THIS, update_client= true)"); + { + Client * THIS; + bool update_client; + + 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."); + + if (items < 2) + update_client = true; + else { + update_client = (bool)SvTRUE(ST(1)); + } + + THIS->UnscribeSpellAll(update_client); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_UntrainDisc); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_UntrainDisc) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Client::UntrainDisc(THIS, slot, update_client= true)"); + { + Client * THIS; + int slot = (int)SvIV(ST(1)); + bool update_client; + + 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."); + + if (items < 3) + update_client = true; + else { + update_client = (bool)SvTRUE(ST(2)); + } + + THIS->UntrainDisc(slot, update_client); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_UntrainDiscAll); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_UntrainDiscAll) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Client::UntrainDiscAll(THIS, update_client= true)"); + { + Client * THIS; + bool update_client; + + 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."); + + if (items < 2) + update_client = true; + else { + update_client = (bool)SvTRUE(ST(1)); + } + + THIS->UntrainDiscAll(update_client); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_IsSitting); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IsSitting) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::IsSitting(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->IsSitting(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_IsBecomeNPC); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IsBecomeNPC) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::IsBecomeNPC(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->IsBecomeNPC(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_GetBecomeNPCLevel); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetBecomeNPCLevel) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetBecomeNPCLevel(THIS)"); + { + Client * THIS; + uint8 RETVAL; + 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."); + + RETVAL = THIS->GetBecomeNPCLevel(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_SetBecomeNPC); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetBecomeNPC) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetBecomeNPC(THIS, flag)"); + { + Client * THIS; + bool flag = (bool)SvTRUE(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetBecomeNPC(flag); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SetBecomeNPCLevel); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetBecomeNPCLevel) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetBecomeNPCLevel(THIS, level)"); + { + Client * THIS; + uint8 level = (uint8)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetBecomeNPCLevel(level); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_LootToStack); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_LootToStack) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::LootToStack(THIS, itemid)"); + { + Client * THIS; + bool RETVAL; + uint32 itemid = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->LootToStack(itemid); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_SetFeigned); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetFeigned) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetFeigned(THIS, in_feigned)"); + { + Client * THIS; + bool in_feigned = (bool)SvTRUE(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetFeigned(in_feigned); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetFeigned); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetFeigned) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetFeigned(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->GetFeigned(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_AutoSplitEnabled); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_AutoSplitEnabled) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::AutoSplitEnabled(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->AutoSplitEnabled(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_SetHorseId); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetHorseId) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetHorseId(THIS, horseid_in)"); + { + Client * THIS; + uint16 horseid_in = (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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetHorseId(horseid_in); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetHorseId); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetHorseId) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetHorseId(THIS)"); + { + Client * THIS; + uint16 RETVAL; + 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."); + + RETVAL = THIS->GetHorseId(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_NukeItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_NukeItem) +{ + dXSARGS; + if (items != 3 && items != 2) + Perl_croak(aTHX_ "Usage: Client::NukeItem(THIS, itemnum, [where_to_check])"); + { + Client * THIS; + uint32 RETVAL; + dXSTARG; + uint32 itemnum = (uint32)SvUV(ST(1)); + uint8 where_to_check; + + 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."); + + if(items < 3){ + where_to_check = 0xFF; + } + if(items == 3){ + where_to_check = (uint8)SvUV(ST(2)); + } + + RETVAL = THIS->NukeItem(itemnum, where_to_check); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_SetTint); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetTint) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::SetTint(THIS, slot_id, color)"); + { + Client * THIS; + int16 slot_id = (int16)SvIV(ST(1)); + uint32 color = (uint32)SvUV(ST(2)); + + 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->SetTint(slot_id, color); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SetMaterial); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetMaterial) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::SetMaterial(THIS, slot_id, item_id)"); + { + Client * THIS; + int16 slot_id = (int16)SvIV(ST(1)); + uint32 item_id = (uint32)SvUV(ST(2)); + + 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->SetMaterial(slot_id, item_id); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_Undye); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_Undye) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::Undye(THIS)"); + { + Client * THIS; + + 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->Undye(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetItemIDAt); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetItemIDAt) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetItemIDAt(THIS, slot_id)"); + { + Client * THIS; + uint32 RETVAL; + dXSTARG; + int16 slot_id = (int16)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetItemIDAt(slot_id); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetAugmentIDAt); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetAugmentIDAt) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::GetAugmentIDAt(THIS, slot_id, augslot)"); + { + Client * THIS; + uint32 RETVAL; + dXSTARG; + int16 slot_id = (int16)SvIV(ST(1)); + int16 augslot = (uint8)SvIV(ST(2)); + + 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."); + + RETVAL = THIS->GetAugmentIDAt(slot_id, augslot); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_DeleteItemInInventory); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_DeleteItemInInventory) +{ + dXSARGS; + if (items < 2 || items > 4) + Perl_croak(aTHX_ "Usage: Client::DeleteItemInInventory(THIS, slot_id, quantity= 0, client_update= false)"); + { + Client * THIS; + int16 slot_id = (int16)SvIV(ST(1)); + int8 quantity; + bool client_update; + + 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."); + + if (items < 3) + quantity = 0; + else { + quantity = (int8)SvIV(ST(2)); + } + + if (items < 4) + client_update = false; + else { + client_update = (bool)SvTRUE(ST(3)); + } + + THIS->DeleteItemInInventory(slot_id, quantity, client_update); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SummonItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SummonItem) +{ + dXSARGS; + if (items < 2 || items > 10) + Perl_croak(aTHX_ "Usage: Client::SummonItem(THIS, item_id, charges=0, attune=0, aug1=0, aug2=0, aug3=0, aug4=0, aug5=0, slot_id=30)"); + { + Client * THIS; + uint32 item_id = (uint32)SvUV(ST(1)); + int16 charges = 0; + bool attune = false; + uint32 aug1 = 0; + uint32 aug2 = 0; + uint32 aug3 = 0; + uint32 aug4 = 0; + uint32 aug5 = 0; + uint16 slot_id = 30; + + 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."); + + if (items > 2) { + charges = (int16)SvIV(ST(2)); + } + if (items > 3) { + attune = (bool)SvTRUE(ST(3)); + } + if (items > 4) { + aug1 = (uint32)SvUV(ST(4)); + } + if (items > 5) { + aug2 = (uint32)SvUV(ST(5)); + } + if (items > 6) { + aug3 = (uint32)SvUV(ST(6)); + } + if (items > 7) { + aug4 = (uint32)SvUV(ST(7)); + } + if (items > 8) { + aug5 = (uint32)SvUV(ST(8)); + } + if (items > 9) { + slot_id = (uint16)SvUV(ST(9)); + } + + THIS->SummonItem(item_id, charges, aug1, aug2, aug3, aug4, aug5, attune, slot_id); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SetStats); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetStats) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::SetStats(THIS, type, increase_val)"); + { + Client * THIS; + uint8 type = (uint8)SvUV(ST(1)); + int16 increase_val = (int16)SvIV(ST(2)); + + 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->SetStats(type, increase_val); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_IncStats); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IncStats) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::IncStats(THIS, type, increase_val)"); + { + Client * THIS; + uint8 type = (uint8)SvUV(ST(1)); + int16 increase_val = (int16)SvIV(ST(2)); + + 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->IncStats(type, increase_val); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_DropItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_DropItem) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::DropItem(THIS, slot_id)"); + { + Client * THIS; + int16 slot_id = (int16)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->DropItem(slot_id); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_BreakInvis); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_BreakInvis) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::BreakInvis(THIS)"); + { + Client * THIS; + + 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->BreakInvis(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetGroup); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetGroup) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetGroup(THIS)"); + { + Client * THIS; + Group * RETVAL; + + 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."); + + RETVAL = THIS->GetGroup(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Group", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_LeaveGroup); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_LeaveGroup) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::LeaveGroup(THIS)"); + { + Client * THIS; + + 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->LeaveGroup(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetRaid); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetRaid) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetRaid(THIS)"); + { + Client * THIS; + Raid * RETVAL; + + 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."); + + RETVAL = THIS->GetRaid(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Raid", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_IsGrouped); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IsGrouped) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::IsGrouped(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->IsGrouped(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_IsRaidGrouped); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IsRaidGrouped) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::IsRaidGrouped(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->IsRaidGrouped(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_Hungry); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_Hungry) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::Hungry(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->Hungry(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_Thirsty); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_Thirsty) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::Thirsty(THIS)"); + { + Client * THIS; + bool RETVAL; + + 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."); + + RETVAL = THIS->Thirsty(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_GetInstrumentMod); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetInstrumentMod) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetInstrumentMod(THIS, spell_id)"); + { + Client * THIS; + uint16 RETVAL; + dXSTARG; + uint16 spell_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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetInstrumentMod(spell_id); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_DecreaseByID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_DecreaseByID) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::DecreaseByID(THIS, type, amt)"); + { + Client * THIS; + bool RETVAL; + uint32 type = (uint32)SvUV(ST(1)); + uint8 amt = (uint8)SvUV(ST(2)); + + 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."); + + RETVAL = THIS->DecreaseByID(type, amt); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_SlotConvert2); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SlotConvert2) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SlotConvert2(THIS, slot)"); + { + Client * THIS; + uint8 RETVAL; + dXSTARG; + uint8 slot = (uint8)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->SlotConvert2(slot); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_Escape); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_Escape) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::Escape(THIS)"); + { + Client * THIS; + + 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->Escape(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_RemoveNoRent); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_RemoveNoRent) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::RemoveNoRent(THIS)"); + { + Client * THIS; + + 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->RemoveNoRent(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GoFish); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GoFish) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GoFish(THIS)"); + { + Client * THIS; + + 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->GoFish(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_ForageItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_ForageItem) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::ForageItem(THIS)"); + { + Client * THIS; + + 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->ForageItem(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_CalcPriceMod); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_CalcPriceMod) +{ + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: Client::CalcPriceMod(THIS, other= 0, reverse= false)"); + { + Client * THIS; + float RETVAL; + dXSTARG; + Mob* other; + bool reverse; + + 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."); + + if (items < 2) + other = 0; + else { + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + } + + if (items < 3) + reverse = false; + else { + reverse = (bool)SvTRUE(ST(2)); + } + + RETVAL = THIS->CalcPriceMod(other, reverse); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_ResetTrade); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_ResetTrade) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::ResetTrade(THIS)"); + { + Client * THIS; + + 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->ResetTrade(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_UseDiscipline); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_UseDiscipline) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::UseDiscipline(THIS, spell_id, target)"); + { + Client * THIS; + bool RETVAL; + uint32 spell_id = (uint32)SvUV(ST(1)); + uint32 target = (uint32)SvUV(ST(2)); + + 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."); + + RETVAL = THIS->UseDiscipline(spell_id, target); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_GetCharacterFactionLevel); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetCharacterFactionLevel) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetCharacterFactionLevel(THIS, faction_id)"); + { + Client * THIS; + int32 RETVAL; + dXSTARG; + int32 faction_id = (int32)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCharacterFactionLevel(faction_id); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_SetZoneFlag); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetZoneFlag) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetZoneFlag(THIS, zone_id)"); + { + Client * THIS; + uint32 zone_id = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetZoneFlag(zone_id); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_ClearZoneFlag); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_ClearZoneFlag) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::ClearZoneFlag(THIS, zone_id)"); + { + Client * THIS; + uint32 zone_id = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->ClearZoneFlag(zone_id); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_HasZoneFlag); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_HasZoneFlag) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::HasZoneFlag(THIS, zone_id)"); + { + Client * THIS; + bool RETVAL; + uint32 zone_id = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->HasZoneFlag(zone_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_SendZoneFlagInfo); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SendZoneFlagInfo) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SendZoneFlagInfo(THIS, to)"); + { + Client * THIS; + Client * to; + + 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."); + + if (sv_derived_from(ST(1), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + to = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "to is not of type Client"); + if(to == NULL) + Perl_croak(aTHX_ "to is NULL, avoiding crash."); + + THIS->SendZoneFlagInfo(to); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_LoadZoneFlags); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_LoadZoneFlags) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::LoadZoneFlags(THIS)"); + { + Client * THIS; + + 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->LoadZoneFlags(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SetAATitle); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetAATitle) +{ + dXSARGS; + if ((items < 2) || (items > 3)) + Perl_croak(aTHX_ "Usage: Client::SetAATitle(THIS, txt, [save])"); + { + Client * THIS; + char * txt = (char *)SvPV_nolen(ST(1)); + bool SaveTitle = false; + + 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."); + + if(strlen(txt) > 31) + Perl_croak(aTHX_ "Title must be 31 characters or less"); + + if(items == 3) + SaveTitle = (SvIV(ST(2)) != 0); + + if(!SaveTitle) + THIS->SetAATitle(txt); + else + title_manager.CreateNewPlayerTitle(THIS, txt); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetClientVersion); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetClientVersion) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetClientVersion(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetClientVersion(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetClientVersionBit); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetClientVersionBit) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetClientVersionBit(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetClientVersionBit(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_SetTitleSuffix); +XS(XS_Client_SetTitleSuffix) { + dXSARGS; + if ((items < 2) || (items > 3)) + Perl_croak(aTHX_ "Usage: Client::SetTitleSuffix(THIS, txt, [save])"); + { + Client * THIS; + char * txt = (char *)SvPV_nolen(ST(1)); + bool SaveSuffix = false; + + 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."); + + if(strlen(txt) > 31) + Perl_croak(aTHX_ "Title must be 31 characters or less"); + + if(items == 3) + SaveSuffix = (SvIV(ST(2)) != 0); + + if(!SaveSuffix) + THIS->SetTitleSuffix(txt); + else + title_manager.CreateNewPlayerSuffix(THIS, txt); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SetAAPoints); +XS(XS_Client_SetAAPoints) { + dXSARGS; + if(items != 2) + Perl_croak(aTHX_ "Usage: Client::SetAAPoints(THIS, points)"); + { + Client * THIS; + uint32 points = 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->GetPP().aapoints = points; + THIS->SendAAStats(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetAAPoints); +XS(XS_Client_GetAAPoints) { + dXSARGS; + if(items != 1) + Perl_croak(aTHX_ "Usage: Client::GetAAPoints(THIS)"); + dXSTARG; + { + Client * THIS; + uint32 RETVAL; + + 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."); + + RETVAL = THIS->GetPP().aapoints; + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetSpentAA); +XS(XS_Client_GetSpentAA) { + dXSARGS; + if(items != 1) + Perl_croak(aTHX_ "Usage: Client::GetSpentAA(THIS)"); + dXSTARG; + { + Client * THIS; + uint32 RETVAL; + + 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."); + + RETVAL = THIS->GetPP().aapoints_spent; + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_AddAAPoints); +XS(XS_Client_AddAAPoints) { + dXSARGS; + if(items != 2) + Perl_croak(aTHX_ "Usage: Client::AddAAPoints(THIS, number)"); + { + Client * THIS; + uint32 points = 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->GetPP().aapoints += points; + THIS->SendAAStats(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_RefundAA); +XS(XS_Client_RefundAA) { + dXSARGS; + if(items != 1) + Perl_croak(aTHX_ "Usage: Client::RefundAA(THIS)"); + { + Client * THIS; + + 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."); + + int curpt = 0; + bool refunded = false; + + for(int x1=0;x1GetAA(x1); + if(curpt > 0){ + SendAA_Struct* curaa = zone->FindAA(x1); + if(curaa){ + THIS->SetAA(x1, 0); + for(int x2=0;x2GetPP().aapoints += curaa->cost + (curaa->cost_inc * x2); + refunded = true; + } + } + else //aa doesn't exist.. but if they bought it then it had at least a cost of 1 point each + { //so give back what we can + THIS->GetPP().aapoints += curpt; + THIS->SetAA(x1, 0); + refunded = true; + } + } + } + + if(refunded){ + THIS->Save(); //save of course + THIS->Kick(); //client gets all buggy if we don't immediatly relog so just force it on them + } + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetModCharacterFactionLevel); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetModCharacterFactionLevel) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetModCharacterFactionLevel(THIS, faction_id)"); + { + Client * THIS; + int32 RETVAL; + dXSTARG; + int32 faction_id = (int32)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetModCharacterFactionLevel(faction_id); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetLDoNWins); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetLDoNWins) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetLDoNWins(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetLDoNWins(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetLDoNLosses); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetLDoNLosses) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetLDoNLosses(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetLDoNLosses(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetLDoNWinsTheme); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetLDoNWinsTheme) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetLDoNWinsTheme(THIS, theme)"); + { + Client * THIS; + uint32 RETVAL; + dXSTARG; + int32 theme_out = (int32)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetLDoNWinsTheme(theme_out); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetLDoNLossesTheme); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetLDoNLossesTheme) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetLDoNLossesTheme(THIS, theme)"); + { + Client * THIS; + uint32 RETVAL; + dXSTARG; + int32 theme_out = (int32)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetLDoNLossesTheme(theme_out); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetItemAt); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetItemAt) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetItemAt(THIS, slot)"); + { + Client * THIS; + ItemInst * RETVAL; + uint32 slot = (int32)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetInv().GetItem(slot); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "QuestItem", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetAugmentAt); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetAugmentAt) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::GetAugmentAt(THIS, slot, aug_slot)"); + { + Client * THIS; + ItemInst * RETVAL; + uint32 slot = (int32)SvIV(ST(1)); + uint32 aug_slot = (int32)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + ItemInst * inst = THIS->GetInv().GetItem(slot); + if(inst) + { + RETVAL = inst->GetAugment(aug_slot); + } + else + { + RETVAL = NULL; + } + + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "QuestItem", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetStartZone); +XS(XS_Client_GetStartZone) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetStartZone(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetStartZone(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_SetStartZone); +XS(XS_Client_SetStartZone) +{ + dXSARGS; + if (items != 2 && items != 5) + Perl_croak(aTHX_ "Usage: Client::SetStartZone(THIS, zoneid [, x, y, z])"); + { + Client * THIS; + uint32 zoneid = (uint32)SvUV(ST(1)); + float x = 0; + float y = 0; + float z = 0; + + 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."); + + if(items == 5) { + x = SvNV(ST(2)); + y = SvNV(ST(3)); + z = SvNV(ST(4)); + } + + THIS->SetStartZone(zoneid,x,y,z); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_KeyRingAdd); +XS(XS_Client_KeyRingAdd) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::KeyRingAdd(THIS, item_id)"); + { + Client * THIS; + uint32 item_id = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->KeyRingAdd(item_id);; + } + XSRETURN_EMPTY; +} + +XS(XS_Client_KeyRingCheck); +XS(XS_Client_KeyRingCheck) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::KeyRingCheck(THIS, item_id)"); + { + Client * THIS; + bool RETVAL; + uint32 item_id = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->KeyRingCheck(item_id);; + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_AddPVPPoints); +XS(XS_Client_AddPVPPoints) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::AddPVPPoints(THIS, Points)"); + { + Client * THIS; + uint32 Points = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->AddPVPPoints(Points); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_AddCrystals); +XS(XS_Client_AddCrystals) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::AddCrystals(THIS, RadiantCount, EbonCount)"); + { + Client * THIS; + uint32 Radiant = (uint32)SvUV(ST(1)); + uint32 Ebon = (uint32)SvUV(ST(2)); + + 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->AddCrystals(Radiant, Ebon); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetPVPPoints); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetPVPPoints) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetPVPPoints(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetPVPPoints(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetRadiantCrystals); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetRadiantCrystals) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetRadiantCrystals(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetRadiantCrystals(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetEbonCrystals); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetEbonCrystals) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetEbonCrystals(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetEbonCrystals(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_ReadBook); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_ReadBook) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::ReadBook(THIS, Book Text, Type)"); + { + Client * THIS; + char* in_txt = (char *)SvPV_nolen(ST(1)); + uint8 type = (uint8)SvUV(ST(2)); + + 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->QuestReadBook(in_txt, type); + } +XSRETURN_EMPTY; +} + +XS(XS_Client_UpdateGroupAAs); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_UpdateGroupAAs) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::UpdateGroupAAs(THIS, points, type)"); + { + Client * THIS; + int32 points = (int32)SvIV(ST(1)); + uint32 type = (uint32)SvUV(ST(2)); + + 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->UpdateGroupAAs(points, type); + } + XSRETURN(1); +} + +XS(XS_Client_GetGroupPoints); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetGroupPoints) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetGroupPoints(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetGroupPoints(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetRaidPoints); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetRaidPoints) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetRaidPoints(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetRaidPoints(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_LearnRecipe); +XS(XS_Client_LearnRecipe) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::LearnRecipe(THIS, recipe_id)"); + { + Client * THIS; + uint32 recipe_id = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->LearnRecipe(recipe_id);; + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetEndurance); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetEndurance) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetEndurance(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetEndurance(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetMaxEndurance); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetMaxEndurance) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetMaxEndurance(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetMaxEndurance(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetEnduranceRatio); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetEnduranceRatio) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetEnduranceRatio(THIS)"); + { + Client * THIS; + uint8 RETVAL; + 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."); + + RETVAL = THIS->GetEndurancePercent(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_SetEndurance); +XS(XS_Client_SetEndurance) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SetEndurance(THIS, Endurance)"); + { + Client * THIS; + int32 Endurance = (int32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetEndurance(Endurance); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SendOPTranslocateConfirm); +XS(XS_Client_SendOPTranslocateConfirm) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::SendOPTranslocateConfirm(THIS, Caster, SpellID)"); + { + Client * THIS; + Mob * caster = NULL; + int32 spell_id = (int32)SvUV(ST(2)); + + 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."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + caster = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "caster is not of type Mob"); + if(caster == NULL) + Perl_croak(aTHX_ "caster is NULL, avoiding crash."); + + THIS->SendOPTranslocateConfirm(caster, spell_id); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_NPCSpawn); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_NPCSpawn) +{ + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: Client::NPCSpawn(THIS, target_npc, option, respawntime=1200)"); + { + Client * THIS; + NPC * target_npc = NULL; + Const_char * option = (Const_char *)SvPV_nolen(ST(2)); + uint32 respawntime = 1200; + + 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."); + + if (sv_derived_from(ST(1), "NPC")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + target_npc = INT2PTR(NPC *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if(target_npc == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items > 3) + respawntime = (uint32)SvUV(ST(3)); + + THIS->NPCSpawn(target_npc, option, respawntime); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetIP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetIP) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetIP(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = THIS->GetIP(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_AddLevelBasedExp); +XS(XS_Client_AddLevelBasedExp) +{ + dXSARGS; + if (items < 2 || items > 3 ) + Perl_croak(aTHX_ "Usage: Client::AddLevelBasedExp(THIS, exp_percentage, max_level=0)"); + { + Client * THIS; + uint8 exp_percentage = (uint8)SvUV(ST(1)); + uint8 max_level = 0; + + 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."); + + if (items > 2) + max_level = (uint8)SvUV(ST(2)); + + THIS->AddLevelBasedExp(exp_percentage, max_level); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_IncrementAA); +XS(XS_Client_IncrementAA) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::IncrementAA(THIS, aaskillid)"); + { + Client * THIS; + uint32 aaskillid = 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + SendAA_Struct* aa2 = zone->FindAA(aaskillid); + + if(aa2 == NULL) + Perl_croak(aTHX_ "Invalid AA."); + + if(THIS->GetAA(aaskillid) == aa2->max_level) + Perl_croak(aTHX_ "AA at Max already."); + + THIS->SetAA(aaskillid, THIS->GetAA(aaskillid)+1); + + THIS->Save(); + + THIS->SendAA(aaskillid); + THIS->SendAATable(); + THIS->SendAAStats(); + THIS->CalcBonuses(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetAALevel); +XS(XS_Client_GetAALevel) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetAALevel(THIS, aaskillid)"); + { + Client * THIS; + uint32 RETVAL; + dXSTARG; + uint32 aaskillid = 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetAA(aaskillid); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_MarkCompassLoc); +XS(XS_Client_MarkCompassLoc) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: Client::MarkCompassLoc(THIS, x, y, z)"); + { + Client * THIS; + float x = SvNV(ST(1)); + float y = SvNV(ST(2)); + float z = SvNV(ST(3)); + + 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->MarkSingleCompassLoc(x,y,z); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_ClearCompassMark); +XS(XS_Client_ClearCompassMark) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::ClearCompassMark(THIS)"); + { + Client * THIS; + + 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->MarkSingleCompassLoc(0,0,0,0); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetFreeSpellBookSlot); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetFreeSpellBookSlot) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Client::GetFreeSpellBookSlot(THIS, start_slot=0)"); + { + Client * THIS; + int RETVAL; + uint32 start_slot = 0; + 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."); + + if (items > 1) + start_slot = SvUV(ST(1)); + + RETVAL = THIS->GetNextAvailableSpellBookSlot(start_slot); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetSpellBookSlotBySpellID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetSpellBookSlotBySpellID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetSpellBookSlotBySpellID(THIS, spell_id)"); + { + Client * THIS; + int RETVAL; + uint32 spell_id = SvUV(ST(1)); + 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."); + + RETVAL = THIS->FindSpellBookSlotBySpellID(spell_id); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_UpdateTaskActivity); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_UpdateTaskActivity) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: Client::UpdateTaskActivity(THIS, TaskID, ActivityID, Count)"); + { + Client * THIS; + int TaskID = (int)SvIV(ST(1)); + int ActivityID = (int)SvIV(ST(2)); + int Count = (int)SvUV(ST(3)); + + 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->UpdateTaskActivity(TaskID, ActivityID, Count); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_AssignTask); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_AssignTask) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::AssignTask(THIS, TaskID, NPCID)"); + { + Client * THIS; + int TaskID = (int)SvIV(ST(1)); + int NPCID = (int)SvIV(ST(2)); + + 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->AssignTask(TaskID, NPCID); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_FailTask); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_FailTask) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::FailTask(THIS, TaskID)"); + { + Client * THIS; + int TaskID = (int)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->FailTask(TaskID); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_IsTaskCompleted); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IsTaskCompleted) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::IsTaskCompleted(THIS, TaskID)"); + { + Client * THIS; + int RETVAL; + int TaskID = (int)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsTaskCompleted(TaskID); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_IsTaskActive); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IsTaskActive) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::IsTaskActive(THIS, TaskID)"); + { + Client * THIS; + bool RETVAL; + int TaskID = (int)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsTaskActive(TaskID); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_IsTaskActivityActive); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_IsTaskActivityActive) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::IsTaskActivityActive(THIS, TaskID, ActivityID)"); + { + Client * THIS; + bool RETVAL; + int TaskID = (int)SvIV(ST(1)); + int ActivityID = (int)SvIV(ST(2)); + + 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."); + + RETVAL = THIS->IsTaskActivityActive(TaskID, ActivityID); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Client_GetCorpseCount); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetCorpseCount) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetCorpseCount(THIS)"); + { + Client * THIS; + uint32 RETVAL; + 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."); + + RETVAL = database.GetPlayerCorpseCount(THIS->CharacterID()); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetCorpseID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetCorpseID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetCorpseID(THIS, corpse)"); + { + Client * THIS; + uint8 corpse = (uint8)SvIV(ST(1)); + uint32 RETVAL; + 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."); + + RETVAL = database.GetPlayerCorpseID(THIS->CharacterID(), corpse); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_GetCorpseItemAt); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetCorpseItemAt) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::GetCorpseItemAt(THIS, corpse_id, slotid)"); + { + Client * THIS; + uint32 corpse_id = (uint32)SvIV(ST(1)); + uint16 slotid = (uint16)SvIV(ST(2)); + uint32 RETVAL; + 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."); + + RETVAL = database.GetPlayerCorpseItemAt(corpse_id, slotid); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_AssignToInstance); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_AssignToInstance) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::AssignToInstance(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->AssignToInstance(instance_id); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_Freeze); +XS(XS_Client_Freeze) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client:Freeze(THIS)"); + { + Client * THIS; + + 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->SendAppearancePacket(AT_Anim, ANIM_FREEZE); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_UnFreeze); +XS(XS_Client_UnFreeze) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client:UnFreeze(THIS)"); + { + Client * THIS; + + 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->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + XSRETURN_EMPTY; +} + + +XS(XS_Client_GetAggroCount); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetAggroCount) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetAggroCount(THIS)"); + { + Client * THIS; + int RETVAL; + 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."); + + RETVAL = THIS->GetAggroCount(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + + +XS(XS_Client_GetCarriedMoney); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetCarriedMoney) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetCarriedMoney(THIS)"); + { + Client * THIS; + int RETVAL; + 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."); + + RETVAL = THIS->GetCarriedMoney(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + + +XS(XS_Client_GetAllMoney); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetAllMoney) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetAllMoney(THIS)"); + { + Client * THIS; + int RETVAL; + 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."); + + RETVAL = THIS->GetAllMoney(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + + +XS(XS_Client_GetItemInInventory); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetItemInInventory) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::GetItemInInventory(THIS, slot_id)"); + { + Client * THIS; + int16 slot_id = (int16)SvIV(ST(1)); + ItemInst *RETVAL = NULL; + + 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."); + + RETVAL = THIS->GetInv().GetItem(slot_id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "QuestItem", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Client_SetCustomItemData); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SetCustomItemData) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: Client::SetCustomItemData(THIS, slot_id, identifier, value)"); + { + Client * THIS; + int16 slot_id = (int16)SvIV(ST(1)); + Const_char* identifier = SvPV_nolen(ST(2)); + Const_char* value = SvPV_nolen(ST(3)); + + 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->GetInv().SetCustomItemData(THIS->CharacterID(), slot_id, std::string(identifier), std::string(value)); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetCustomItemData); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetCustomItemData) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::GetCustomItemData(THIS, slot_id, identifier)"); + { + Client * THIS; + int16 slot_id = (int16)SvIV(ST(1)); + Const_char* identifier = SvPV_nolen(ST(2)); + Const_char * RETVAL; + 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."); + + std::string ret_val = THIS->GetInv().GetCustomItemData(slot_id, std::string(identifier)); + RETVAL = ret_val.c_str(); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Client_OpenLFGuildWindow); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_OpenLFGuildWindow) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::OpenLFGuildWindow(THIS)"); + { + Client * THIS; + + 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->OpenLFGuildWindow(); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SignalClient); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SignalClient) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::SignalClient(THIS, data)"); + { + Client * THIS; + uint32 data = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Signal(data); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_AddAlternateCurrencyValue); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_AddAlternateCurrencyValue) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::AddAlternateCurrencyValue(THIS, uint32 currency_id, int32 amount)"); + { + Client * THIS; + uint32 currency_id = (uint32)SvUV(ST(1)); + int32 amount = (int32)SvUV(ST(2)); + + 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->AddAlternateCurrencyValue(currency_id, amount); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SendWebLink); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SendWebLink) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Client::SendWebLink(THIS, website)"); + { + Client * THIS; + char * website = NULL; + + 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 Mob"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items > 1) { website = (char *)SvPV_nolen(ST(1)); } + + THIS->SendWebLink(website); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_GetInstanceID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetInstanceID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::GetInstanceID(THIS)"); + { + Client * THIS; + int8 RETVAL; + 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."); + + RETVAL = THIS->GetInstanceID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_Client); /* prototype to pass -Wmissing-prototypes */ +XS(boot_Client) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "SendSound"), XS_Client_SendSound, file, "$"); + newXSproto(strcpy(buf, "Save"), XS_Client_Save, file, "$$"); + newXSproto(strcpy(buf, "SaveBackup"), XS_Client_SaveBackup, file, "$"); + newXSproto(strcpy(buf, "Connected"), XS_Client_Connected, file, "$"); + newXSproto(strcpy(buf, "InZone"), XS_Client_InZone, file, "$"); + newXSproto(strcpy(buf, "Kick"), XS_Client_Kick, file, "$"); + newXSproto(strcpy(buf, "Disconnect"), XS_Client_Disconnect, file, "$"); + newXSproto(strcpy(buf, "IsLD"), XS_Client_IsLD, file, "$"); + newXSproto(strcpy(buf, "WorldKick"), XS_Client_WorldKick, file, "$"); + newXSproto(strcpy(buf, "GetAnon"), XS_Client_GetAnon, file, "$"); + newXSproto(strcpy(buf, "Duck"), XS_Client_Duck, file, "$"); + newXSproto(strcpy(buf, "Stand"), XS_Client_Stand, file, "$"); + newXSproto(strcpy(buf, "SetGM"), XS_Client_SetGM, file, "$$"); + newXSproto(strcpy(buf, "SetPVP"), XS_Client_SetPVP, file, "$$"); + newXSproto(strcpy(buf, "GetPVP"), XS_Client_GetPVP, file, "$"); + newXSproto(strcpy(buf, "GetGM"), XS_Client_GetGM, file, "$"); + newXSproto(strcpy(buf, "SetBaseClass"), XS_Client_SetBaseClass, file, "$$"); + newXSproto(strcpy(buf, "SetBaseRace"), XS_Client_SetBaseRace, file, "$$"); + newXSproto(strcpy(buf, "SetBaseGender"), XS_Client_SetBaseGender, file, "$$"); + newXSproto(strcpy(buf, "GetBaseFace"), XS_Client_GetBaseFace, file, "$"); + newXSproto(strcpy(buf, "GetLanguageSkill"), XS_Client_GetLanguageSkill, file, "$$"); + newXSproto(strcpy(buf, "GetLastName"), XS_Client_GetLastName, file, "$"); + newXSproto(strcpy(buf, "GetLDoNPointsTheme"), XS_Client_GetLDoNPointsTheme, file, "$"); + newXSproto(strcpy(buf, "GetBaseSTR"), XS_Client_GetBaseSTR, file, "$"); + newXSproto(strcpy(buf, "GetBaseSTA"), XS_Client_GetBaseSTA, file, "$"); + newXSproto(strcpy(buf, "GetBaseCHA"), XS_Client_GetBaseCHA, file, "$"); + newXSproto(strcpy(buf, "GetBaseDEX"), XS_Client_GetBaseDEX, file, "$"); + newXSproto(strcpy(buf, "GetBaseINT"), XS_Client_GetBaseINT, file, "$"); + newXSproto(strcpy(buf, "GetBaseAGI"), XS_Client_GetBaseAGI, file, "$"); + newXSproto(strcpy(buf, "GetBaseWIS"), XS_Client_GetBaseWIS, file, "$"); + newXSproto(strcpy(buf, "GetWeight"), XS_Client_GetWeight, file, "$"); + newXSproto(strcpy(buf, "GetEXP"), XS_Client_GetEXP, file, "$"); + newXSproto(strcpy(buf, "GetAAExp"), XS_Client_GetAAExp, file, "$"); + newXSproto(strcpy(buf, "GetTotalSecondsPlayed"), XS_Client_GetTotalSecondsPlayed, file, "$"); + newXSproto(strcpy(buf, "UpdateLDoNPoints"), XS_Client_UpdateLDoNPoints, file, "$$$"); + 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, "GetBindX"), XS_Client_GetBindX, file, "$$"); + newXSproto(strcpy(buf, "GetBindY"), XS_Client_GetBindY, file, "$$"); + newXSproto(strcpy(buf, "GetBindZ"), XS_Client_GetBindZ, file, "$$"); + newXSproto(strcpy(buf, "GetBindHeading"), XS_Client_GetBindHeading, file, "$$"); + newXSproto(strcpy(buf, "GetBindZoneID"), XS_Client_GetBindZoneID, file, "$$"); + newXSproto(strcpy(buf, "MovePC"), XS_Client_MovePC, file, "$$$$$$"); + newXSproto(strcpy(buf, "MovePCInstance"), XS_Client_MovePCInstance, file, "$$$$$$$"); + newXSproto(strcpy(buf, "ChangeLastName"), XS_Client_ChangeLastName, file, "$$"); + newXSproto(strcpy(buf, "GetFactionLevel"), XS_Client_GetFactionLevel, file, "$$$$$$$$"); + newXSproto(strcpy(buf, "SetFactionLevel"), XS_Client_SetFactionLevel, file, "$$$$$$"); + newXSproto(strcpy(buf, "SetFactionLevel2"), XS_Client_SetFactionLevel2, file, "$$$$$$$"); + newXSproto(strcpy(buf, "GetRawItemAC"), XS_Client_GetRawItemAC, file, "$"); + newXSproto(strcpy(buf, "AccountID"), XS_Client_AccountID, file, "$"); + newXSproto(strcpy(buf, "AccountName"), XS_Client_AccountName, file, "$"); + newXSproto(strcpy(buf, "Admin"), XS_Client_Admin, file, "$"); + newXSproto(strcpy(buf, "CharacterID"), XS_Client_CharacterID, file, "$"); + newXSproto(strcpy(buf, "UpdateAdmin"), XS_Client_UpdateAdmin, file, "$;$"); + newXSproto(strcpy(buf, "UpdateWho"), XS_Client_UpdateWho, file, "$;$"); + newXSproto(strcpy(buf, "GuildRank"), XS_Client_GuildRank, file, "$"); + newXSproto(strcpy(buf, "GuildID"), XS_Client_GuildID, file, "$"); + newXSproto(strcpy(buf, "GetFace"), XS_Client_GetFace, file, "$"); + newXSproto(strcpy(buf, "TakeMoneyFromPP"), XS_Client_TakeMoneyFromPP, file, "$$;$"); + newXSproto(strcpy(buf, "AddMoneyToPP"), XS_Client_AddMoneyToPP, file, "$$$$$$"); + newXSproto(strcpy(buf, "TGB"), XS_Client_TGB, file, "$"); + newXSproto(strcpy(buf, "GetSkillPoints"), XS_Client_GetSkillPoints, file, "$"); + newXSproto(strcpy(buf, "SetSkillPoints"), XS_Client_SetSkillPoints, file, "$$"); + newXSproto(strcpy(buf, "IncreaseSkill"), XS_Client_IncreaseSkill, file, "$$;$"); + newXSproto(strcpy(buf, "IncreaseLanguageSkill"), XS_Client_IncreaseLanguageSkill, file, "$$;$"); + newXSproto(strcpy(buf, "GetSkill"), XS_Client_GetSkill, file, "$$"); + newXSproto(strcpy(buf, "GetRawSkill"), XS_Client_GetRawSkill, file, "$$"); + newXSproto(strcpy(buf, "HasSkill"), XS_Client_HasSkill, file, "$$"); + newXSproto(strcpy(buf, "CanHaveSkill"), XS_Client_CanHaveSkill, file, "$$"); + newXSproto(strcpy(buf, "SetSkill"), XS_Client_SetSkill, file, "$$$"); + newXSproto(strcpy(buf, "AddSkill"), XS_Client_AddSkill, file, "$$$"); + newXSproto(strcpy(buf, "CheckSpecializeIncrease"), XS_Client_CheckSpecializeIncrease, file, "$$"); + newXSproto(strcpy(buf, "CheckIncreaseSkill"), XS_Client_CheckIncreaseSkill, file, "$$;$"); + newXSproto(strcpy(buf, "SetLanguageSkill"), XS_Client_SetLanguageSkill, file, "$$$"); + newXSproto(strcpy(buf, "MaxSkill"), XS_Client_MaxSkill, file, "$$;$$"); + newXSproto(strcpy(buf, "GMKill"), XS_Client_GMKill, file, "$"); + newXSproto(strcpy(buf, "IsMedding"), XS_Client_IsMedding, file, "$"); + newXSproto(strcpy(buf, "GetDuelTarget"), XS_Client_GetDuelTarget, file, "$"); + newXSproto(strcpy(buf, "IsDueling"), XS_Client_IsDueling, file, "$"); + newXSproto(strcpy(buf, "SetDuelTarget"), XS_Client_SetDuelTarget, file, "$$"); + newXSproto(strcpy(buf, "SetDueling"), XS_Client_SetDueling, file, "$$"); + newXSproto(strcpy(buf, "ResetAA"), XS_Client_ResetAA, file, "$"); + newXSproto(strcpy(buf, "MemSpell"), XS_Client_MemSpell, file, "$$$;$"); + newXSproto(strcpy(buf, "UnmemSpell"), XS_Client_UnmemSpell, file, "$$;$"); + newXSproto(strcpy(buf, "UnmemSpellAll"), XS_Client_UnmemSpellAll, file, "$;$"); + newXSproto(strcpy(buf, "ScribeSpell"), XS_Client_ScribeSpell, file, "$$$;$"); + newXSproto(strcpy(buf, "UnscribeSpell"), XS_Client_UnscribeSpell, file, "$$;$"); + newXSproto(strcpy(buf, "UnscribeSpellAll"), XS_Client_UnscribeSpellAll, file, "$;$"); + newXSproto(strcpy(buf, "UntrainDisc"), XS_Client_UntrainDisc, file, "$$;$"); + newXSproto(strcpy(buf, "UntrainDiscAll"), XS_Client_UntrainDiscAll, file, "$;$"); + newXSproto(strcpy(buf, "IsSitting"), XS_Client_IsSitting, file, "$"); + newXSproto(strcpy(buf, "IsBecomeNPC"), XS_Client_IsBecomeNPC, file, "$"); + newXSproto(strcpy(buf, "GetBecomeNPCLevel"), XS_Client_GetBecomeNPCLevel, file, "$"); + newXSproto(strcpy(buf, "SetBecomeNPC"), XS_Client_SetBecomeNPC, file, "$$"); + newXSproto(strcpy(buf, "SetBecomeNPCLevel"), XS_Client_SetBecomeNPCLevel, file, "$$"); + newXSproto(strcpy(buf, "LootToStack"), XS_Client_LootToStack, file, "$$"); + newXSproto(strcpy(buf, "SetFeigned"), XS_Client_SetFeigned, file, "$$"); + newXSproto(strcpy(buf, "GetFeigned"), XS_Client_GetFeigned, file, "$"); + newXSproto(strcpy(buf, "AutoSplitEnabled"), XS_Client_AutoSplitEnabled, file, "$"); + newXSproto(strcpy(buf, "SetHorseId"), XS_Client_SetHorseId, file, "$$"); + newXSproto(strcpy(buf, "GetHorseId"), XS_Client_GetHorseId, file, "$"); + newXSproto(strcpy(buf, "NukeItem"), XS_Client_NukeItem, file, "$$;$"); + newXSproto(strcpy(buf, "SetTint"), XS_Client_SetTint, file, "$$$"); + newXSproto(strcpy(buf, "SetMaterial"), XS_Client_SetMaterial, file, "$$$"); + newXSproto(strcpy(buf, "Undye"), XS_Client_Undye, file, "$"); + newXSproto(strcpy(buf, "GetItemIDAt"), XS_Client_GetItemIDAt, file, "$$"); + newXSproto(strcpy(buf, "GetAugmentIDAt"), XS_Client_GetAugmentIDAt, file, "$$$"); + newXSproto(strcpy(buf, "DeleteItemInInventory"), XS_Client_DeleteItemInInventory, file, "$$;$$"); + newXSproto(strcpy(buf, "SummonItem"), XS_Client_SummonItem, file, "$$;$$$$$$$$"); + newXSproto(strcpy(buf, "SetStats"), XS_Client_SetStats, file, "$$$"); + newXSproto(strcpy(buf, "IncStats"), XS_Client_IncStats, file, "$$$"); + newXSproto(strcpy(buf, "DropItem"), XS_Client_DropItem, file, "$$"); + newXSproto(strcpy(buf, "BreakInvis"), XS_Client_BreakInvis, file, "$"); + newXSproto(strcpy(buf, "GetGroup"), XS_Client_GetGroup, file, "$"); + newXSproto(strcpy(buf, "LeaveGroup"), XS_Client_LeaveGroup, file, "$"); + newXSproto(strcpy(buf, "GetRaid"), XS_Client_GetRaid, file, "$"); + newXSproto(strcpy(buf, "IsGrouped"), XS_Client_IsGrouped, file, "$"); + newXSproto(strcpy(buf, "IsRaidGrouped"), XS_Client_IsRaidGrouped, file, "$"); + newXSproto(strcpy(buf, "Hungry"), XS_Client_Hungry, file, "$"); + newXSproto(strcpy(buf, "Thirsty"), XS_Client_Thirsty, file, "$"); + newXSproto(strcpy(buf, "GetInstrumentMod"), XS_Client_GetInstrumentMod, file, "$$"); + newXSproto(strcpy(buf, "DecreaseByID"), XS_Client_DecreaseByID, file, "$$$"); + newXSproto(strcpy(buf, "SlotConvert2"), XS_Client_SlotConvert2, file, "$$"); + newXSproto(strcpy(buf, "Escape"), XS_Client_Escape, file, "$"); + newXSproto(strcpy(buf, "RemoveNoRent"), XS_Client_RemoveNoRent, file, "$"); + newXSproto(strcpy(buf, "GoFish"), XS_Client_GoFish, file, "$"); + newXSproto(strcpy(buf, "ForageItem"), XS_Client_ForageItem, file, "$"); + newXSproto(strcpy(buf, "CalcPriceMod"), XS_Client_CalcPriceMod, file, "$;$$"); + newXSproto(strcpy(buf, "ResetTrade"), XS_Client_ResetTrade, file, "$"); + newXSproto(strcpy(buf, "UseDiscipline"), XS_Client_UseDiscipline, file, "$$$"); + newXSproto(strcpy(buf, "GetCharacterFactionLevel"), XS_Client_GetCharacterFactionLevel, file, "$$"); + newXSproto(strcpy(buf, "SetZoneFlag"), XS_Client_SetZoneFlag, file, "$$"); + newXSproto(strcpy(buf, "ClearZoneFlag"), XS_Client_ClearZoneFlag, file, "$$"); + newXSproto(strcpy(buf, "HasZoneFlag"), XS_Client_HasZoneFlag, file, "$$"); + newXSproto(strcpy(buf, "SendZoneFlagInfo"), XS_Client_SendZoneFlagInfo, file, "$$"); + newXSproto(strcpy(buf, "LoadZoneFlags"), XS_Client_LoadZoneFlags, file, "$"); + newXSproto(strcpy(buf, "SetAATitle"), XS_Client_SetAATitle, file, "$$;$"); + newXSproto(strcpy(buf, "GetClientVersion"), XS_Client_GetClientVersion, file, "$"); + newXSproto(strcpy(buf, "GetClientVersionBit"), XS_Client_GetClientVersionBit, file, "$"); + newXSproto(strcpy(buf, "SetTitleSuffix"), XS_Client_SetTitleSuffix, file, "$$;$"); + newXSproto(strcpy(buf, "SetAAPoints"), XS_Client_SetAAPoints, file, "$$"); + newXSproto(strcpy(buf, "GetAAPoints"), XS_Client_GetAAPoints, file, "$$"); + newXSproto(strcpy(buf, "GetSpentAA"), XS_Client_GetSpentAA, file, "$$"); + newXSproto(strcpy(buf, "AddAAPoints"), XS_Client_AddAAPoints, file, "$$"); + newXSproto(strcpy(buf, "RefundAA"), XS_Client_RefundAA, file, "$$"); + newXSproto(strcpy(buf, "GetModCharacterFactionLevel"), XS_Client_GetModCharacterFactionLevel, file, "$$"); + newXSproto(strcpy(buf, "GetLDoNWins"), XS_Client_GetLDoNWins, file, "$"); + newXSproto(strcpy(buf, "GetLDoNLosses"), XS_Client_GetLDoNLosses, file, "$"); + newXSproto(strcpy(buf, "GetLDoNWinsTheme"), XS_Client_GetLDoNWinsTheme, file, "$$"); + newXSproto(strcpy(buf, "GetLDoNLossesTheme"), XS_Client_GetLDoNLossesTheme, file, "$$"); + newXSproto(strcpy(buf, "GetItemAt"), XS_Client_GetItemAt, file, "$$"); + newXSproto(strcpy(buf, "GetAugmentAt"), XS_Client_GetAugmentAt, file, "$$$"); + newXSproto(strcpy(buf, "GetStartZone"), XS_Client_GetStartZone, file, "$"); + newXSproto(strcpy(buf, "SetStartZone"), XS_Client_SetStartZone, file, "$$"); + newXSproto(strcpy(buf, "KeyRingAdd"), XS_Client_KeyRingAdd, file, "$$"); + newXSproto(strcpy(buf, "KeyRingCheck"), XS_Client_KeyRingCheck, file, "$$"); + newXSproto(strcpy(buf, "AddPVPPoints"), XS_Client_AddPVPPoints, file, "$$"); + newXSproto(strcpy(buf, "AddCrystals"), XS_Client_AddCrystals, file, "$$"); + newXSproto(strcpy(buf, "GetPVPPoints"), XS_Client_GetPVPPoints, file, "$"); + newXSproto(strcpy(buf, "GetRadiantCrystals"), XS_Client_GetRadiantCrystals, file, "$"); + newXSproto(strcpy(buf, "GetEbonCrystals"), XS_Client_GetEbonCrystals, file, "$"); + newXSproto(strcpy(buf, "ReadBook"), XS_Client_ReadBook, file, "$$$"); + newXSproto(strcpy(buf, "UpdateGroupAAs"), XS_Client_UpdateGroupAAs, file, "$$$"); + newXSproto(strcpy(buf, "GetGroupPoints"), XS_Client_GetGroupPoints, file, "$"); + newXSproto(strcpy(buf, "GetRaidPoints"), XS_Client_GetRaidPoints, file, "$"); + newXSproto(strcpy(buf, "LearnRecipe"), XS_Client_LearnRecipe, file, "$$"); + newXSproto(strcpy(buf, "GetEndurance"), XS_Client_GetEndurance, file, "$"); + newXSproto(strcpy(buf, "GetMaxEndurance"), XS_Client_GetMaxEndurance, file, "$"); + newXSproto(strcpy(buf, "GetEnduranceRatio"), XS_Client_GetEnduranceRatio, file, "$"); + newXSproto(strcpy(buf, "SetEndurance"), XS_Client_SetEndurance, file, "$$"); + newXSproto(strcpy(buf, "SendOPTranslocateConfirm"), XS_Client_SendOPTranslocateConfirm, file, "$$$"); + newXSproto(strcpy(buf, "NPCSpawn"), XS_Client_NPCSpawn, file, "$$$;$"); + newXSproto(strcpy(buf, "GetIP"), XS_Client_GetIP, file, "$"); + newXSproto(strcpy(buf, "AddLevelBasedExp"), XS_Client_AddLevelBasedExp, file, "$$;$"); + newXSproto(strcpy(buf, "IncrementAA"), XS_Client_IncrementAA, file, "$$"); + newXSproto(strcpy(buf, "GetAALevel"), XS_Client_GetAALevel, file, "$$"); + newXSproto(strcpy(buf, "MarkCompassLoc"), XS_Client_MarkCompassLoc, file, "$$$$"); + newXSproto(strcpy(buf, "ClearCompassMark"), XS_Client_ClearCompassMark, file, "$"); + newXSproto(strcpy(buf, "GetFreeSpellBookSlot"), XS_Client_GetFreeSpellBookSlot, file, "$;$"); + newXSproto(strcpy(buf, "GetSpellBookSlotBySpellID"), XS_Client_GetSpellBookSlotBySpellID, file, "$$"); + newXSproto(strcpy(buf, "UpdateTaskActivity"), XS_Client_UpdateTaskActivity, file, "$$$$"); + newXSproto(strcpy(buf, "AssignTask"), XS_Client_AssignTask, file, "$$$"); + newXSproto(strcpy(buf, "FailTask"), XS_Client_FailTask, file, "$$"); + 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, "GetCorpseCount"), XS_Client_GetCorpseCount, file, "$"); + newXSproto(strcpy(buf, "GetCorpseID"), XS_Client_GetCorpseID, file, "$$"); + newXSproto(strcpy(buf, "GetCorpseItemAt"), XS_Client_GetCorpseItemAt, file, "$$$"); + newXSproto(strcpy(buf, "AssignToInstance"), XS_Client_AssignToInstance, file, "$$"); + newXSproto(strcpy(buf, "Freeze"), XS_Client_Freeze, file, "$"); + newXSproto(strcpy(buf, "UnFreeze"), XS_Client_UnFreeze, file, "$"); + newXSproto(strcpy(buf, "GetAggroCount"), XS_Client_GetAggroCount, file, "$"); + newXSproto(strcpy(buf, "GetCarriedMoney"), XS_Client_GetCarriedMoney, file, "$"); + newXSproto(strcpy(buf, "GetAllMoney"), XS_Client_GetAllMoney, file, "$"); + newXSproto(strcpy(buf, "GetItemInInventory"), XS_Client_GetItemInInventory, file, "$$"); + newXSproto(strcpy(buf, "SetCustomItemData"), XS_Client_SetCustomItemData, file, "$$$$"); + newXSproto(strcpy(buf, "GetCustomItemData"), XS_Client_GetCustomItemData, file, "$$$"); + newXSproto(strcpy(buf, "OpenLFGuildWindow"), XS_Client_OpenLFGuildWindow, file, "$"); + newXSproto(strcpy(buf, "SignalClient"), XS_Client_SignalClient, file, "$"); + newXSproto(strcpy(buf, "AddAlternateCurrencyValue"), XS_Client_AddAlternateCurrencyValue, file, "$$$"); + newXSproto(strcpy(buf, "SendWebLink"), XS_Client_SendWebLink, file, "$:$"); + newXSproto(strcpy(buf, "GetInstanceID"), XS_Client_GetInstanceID, file, "$$"); + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES diff --git a/zone/perl_doors.cpp b/zone/perl_doors.cpp new file mode 100644 index 000000000..92f314fc9 --- /dev/null +++ b/zone/perl_doors.cpp @@ -0,0 +1,768 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of tmp. Do not edit this file, edit tmp instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +#include "doors.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + +XS(XS_Doors_GetDoorDBID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_GetDoorDBID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Doors::GetDoorDBID(THIS)"); + { + Doors * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDoorDBID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Doors_GetDoorID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_GetDoorID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Doors::GetDoorID(THIS)"); + { + Doors * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDoorID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Doors_GetID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_GetID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Doors::GetID(THIS)"); + { + Doors * THIS; + uint16 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetEntityID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Doors_GetX); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_GetX) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Doors::GetX(THIS)"); + { + Doors * THIS; + float RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetX(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Doors_GetY); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_GetY) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Doors::GetY(THIS)"); + { + Doors * THIS; + float RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetY(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Doors_GetZ); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_GetZ) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Doors::GetZ(THIS)"); + { + Doors * THIS; + float RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetZ(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Doors_GetHeading); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_GetHeading) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Doors::GetHeading(THIS)"); + { + Doors * THIS; + float RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetHeading(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Doors_GetOpenType); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_GetOpenType) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Doors::GetOpenType(THIS)"); + { + Doors * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetOpenType(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Doors_GetLockpick); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_GetLockpick) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Doors::GetLockpick(THIS)"); + { + Doors * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetLockpick(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Doors_GetKeyItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_GetKeyItem) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Doors::GetKeyItem(THIS)"); + { + Doors * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetKeyItem(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Doors_GetNoKeyring); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_GetNoKeyring) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Doors::GetNoKeyring(THIS, type)"); + { + Doors * THIS; + uint8 type = (uint8)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->GetNoKeyring(); + } + XSRETURN_EMPTY; +} + +XS(XS_Doors_GetIncline); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_GetIncline) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Doors::GetIncline(THIS)"); + { + Doors * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetIncline(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Doors_GetSize); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_GetSize) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Doors::GetIncline(THIS)"); + { + Doors * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetSize(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + + + +XS(XS_Doors_SetOpenType); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_SetOpenType) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Doors::SetOpenType(THIS, type)"); + { + Doors * THIS; + uint32 type = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetOpenType(type); + } + XSRETURN_EMPTY; +} + +XS(XS_Doors_SetLockpick); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_SetLockpick) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Doors::SetLockpick(THIS, type)"); + { + Doors * THIS; + uint32 type = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetLockpick(type); + } + XSRETURN_EMPTY; +} + +XS(XS_Doors_SetKeyItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_SetKeyItem) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Doors::SetKeyItem(THIS, type)"); + { + Doors * THIS; + uint32 type = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetKeyItem(type); + } + XSRETURN_EMPTY; +} + +XS(XS_Doors_SetNoKeyring); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_SetNoKeyring) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Doors::SetNoKeyring(THIS, type)"); + { + Doors * THIS; + uint8 type = (uint8)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetNoKeyring(type); + } + XSRETURN_EMPTY; +} + +XS(XS_Doors_SetIncline); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_SetIncline) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Doors::SetIncline(THIS, type)"); + { + Doors * THIS; + uint32 type = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetIncline(type); + } + XSRETURN_EMPTY; +} + +XS(XS_Doors_SetSize); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_SetSize) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Doors::SetSize(THIS, size)"); + { + Doors * THIS; + uint32 type = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetSize(type); + } + XSRETURN_EMPTY; +} + +XS(XS_Doors_SetLocation); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_SetLocation) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: Doors::SetLocation(THIS, x, y, z)"); + { + Doors * THIS; + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(ST(3)); + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetLocation(x, y, z); + } + XSRETURN_EMPTY; +} + +XS(XS_Doors_SetX); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_SetX) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Doors::SetX(THIS, XPos)"); + { + Doors * THIS; + float pos = (float)SvNV(ST(1)); + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetX(pos); + } + XSRETURN_EMPTY; +} + +XS(XS_Doors_SetY); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_SetY) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Doors::SetY(THIS, YPos)"); + { + Doors * THIS; + float pos = (float)SvNV(ST(1)); + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetY(pos); + } + XSRETURN_EMPTY; +} + +XS(XS_Doors_SetZ); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_SetZ) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Doors::SetZ(THIS, ZPos)"); + { + Doors * THIS; + float pos = (float)SvNV(ST(1)); + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetZ(pos); + } + XSRETURN_EMPTY; +} + +XS(XS_Doors_SetHeading); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_SetHeading) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Doors::SetHeading(THIS, heading)"); + { + Doors * THIS; + float heading = (float)SvNV(ST(1)); + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetHeading(heading); + } + XSRETURN_EMPTY; +} + +XS(XS_Doors_SetModelName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_SetModelName) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Doors::SetModelName(THIS, name)"); + { + Doors * THIS; + char * name = NULL; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items > 1) { name = (char *)SvPV_nolen(ST(1)); } + + THIS->SetDoorName(name); + } + XSRETURN_EMPTY; +} +XS(XS_Doors_GetModelName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_GetModelName) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Doors::GetModelName(THIS)"); + { + Doors * THIS; + Const_char * RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDoorName(); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Doors_CreateDatabaseEntry); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Doors_CreateDatabaseEntry) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Doors::InsertDoor(THIS)"); + { + Doors * THIS; + + if (sv_derived_from(ST(0), "Doors")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Doors *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Doors"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->CreateDatabaseEntry(); + } + XSRETURN_EMPTY; +} + + + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_Doors); /* prototype to pass -Wmissing-prototypes */ +XS(boot_Doors) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + newXSproto(strcpy(buf, "GetID"),XS_Doors_GetID, file, "$"); + newXSproto(strcpy(buf, "SetModelName"),XS_Doors_SetModelName, file, "$$"); + newXSproto(strcpy(buf, "GetModelName"),XS_Doors_GetModelName, file, "$"); + newXSproto(strcpy(buf, "GetX"),XS_Doors_GetX, file, "$"); + newXSproto(strcpy(buf, "GetY"),XS_Doors_GetY, file, "$"); + newXSproto(strcpy(buf, "GetZ"),XS_Doors_GetZ, file, "$"); + newXSproto(strcpy(buf, "GetHeading"),XS_Doors_GetHeading, file, "$"); + newXSproto(strcpy(buf, "SetX"),XS_Doors_SetX, file, "$$"); + newXSproto(strcpy(buf, "SetY"),XS_Doors_SetY, file, "$$"); + newXSproto(strcpy(buf, "SetZ"),XS_Doors_SetZ, file, "$$"); + newXSproto(strcpy(buf, "SetHeading"),XS_Doors_SetHeading, file, "$$"); + newXSproto(strcpy(buf, "SetLocation"),XS_Doors_SetLocation, file, "$$$$"); + newXSproto(strcpy(buf, "GetDoorDBID"),XS_Doors_GetDoorDBID, file, "$"); + newXSproto(strcpy(buf, "GetDoorID"),XS_Doors_GetDoorID, file, "$"); + newXSproto(strcpy(buf, "SetSize"),XS_Doors_SetSize, file, "$$"); + newXSproto(strcpy(buf, "GetSize"),XS_Doors_GetSize, file, "$"); + newXSproto(strcpy(buf, "SetIncline"),XS_Doors_SetIncline, file, "$$"); + newXSproto(strcpy(buf, "GetIncline"),XS_Doors_GetIncline, file, "$"); + newXSproto(strcpy(buf, "SetOpenType"),XS_Doors_SetOpenType, file, "$$"); + newXSproto(strcpy(buf, "GetOpenType"),XS_Doors_GetOpenType, file, "$"); + newXSproto(strcpy(buf, "SetLockPick"),XS_Doors_SetLockpick, file, "$$"); + newXSproto(strcpy(buf, "GetLockPick"),XS_Doors_GetLockpick, file, "$"); + newXSproto(strcpy(buf, "SetKeyItem"),XS_Doors_SetKeyItem, file, "$$"); + newXSproto(strcpy(buf, "GetKeyItem"),XS_Doors_GetKeyItem, file, "$"); + newXSproto(strcpy(buf, "SetNoKeyring"),XS_Doors_SetNoKeyring, file, "$$"); + newXSproto(strcpy(buf, "GetNoKeyring"),XS_Doors_GetNoKeyring, file, "$"); + newXSproto(strcpy(buf, "CreateDatabaseEntry"),XS_Doors_CreateDatabaseEntry, file, "$"); + XSRETURN_YES; +} +#endif //EMBPERL_XS_CLASSES diff --git a/zone/perl_entity.cpp b/zone/perl_entity.cpp new file mode 100644 index 000000000..1102f76db --- /dev/null +++ b/zone/perl_entity.cpp @@ -0,0 +1,2197 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of tmp. Do not edit this file, edit tmp instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include +#include "embperl.h" + +#include "entity.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +XS(XS_EntityList_GetMobID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetMobID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetMobID(THIS, id)"); + { + EntityList * THIS; + Mob * RETVAL; + uint16 id = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMobID(id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Mob", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetMob); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetMob) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetMob(THIS, name)"); + { + EntityList * THIS; + Mob * RETVAL; + char* name = (char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMob(name); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Mob", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetMobByID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetMobByID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetMobByID(THIS, id)"); + { + EntityList * THIS; + Mob * RETVAL; + uint16 id = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMob(id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Mob", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetMobByNpcTypeID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetMobByNpcTypeID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetMobByNpcTypeID(THIS, get_id)"); + { + EntityList * THIS; + Mob * RETVAL; + uint32 get_id = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMobByNpcTypeID(get_id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Mob", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetNPCByID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetNPCByID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetNPCByID(THIS, id)"); + { + EntityList * THIS; + NPC * RETVAL; + uint16 id = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetNPCByID(id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "NPC", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetNPCByNPCTypeID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetNPCByNPCTypeID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetNPCByNPCTypeID(THIS, npc_id)"); + { + EntityList * THIS; + NPC * RETVAL; + uint32 npc_id = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetNPCByNPCTypeID(npc_id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "NPC", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetClientByName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetClientByName) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetClientByName(THIS, name)"); + { + EntityList * THIS; + Client * RETVAL; + char * name = (char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetClientByName(name); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Client", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetClientByAccID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetClientByAccID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetClientByAccID(THIS, accid)"); + { + EntityList * THIS; + Client * RETVAL; + uint32 accid = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetClientByAccID(accid); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Client", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetClientByID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetClientByID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetClientByID(THIS, id)"); + { + EntityList * THIS; + Client * RETVAL; + uint16 id = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetClientByID(id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Client", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetClientByCharID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetClientByCharID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetClientByCharID(THIS, iCharID)"); + { + EntityList * THIS; + Client * RETVAL; + uint32 iCharID = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetClientByCharID(iCharID); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Client", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetClientByWID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetClientByWID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetClientByWID(THIS, iWID)"); + { + EntityList * THIS; + Client * RETVAL; + uint32 iWID = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetClientByWID(iWID); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Client", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetObjectByDBID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetObjectByDBID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetObjectByDBID(THIS, id)"); + { + EntityList * THIS; + Object * RETVAL; + uint32 id = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetObjectByDBID(id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Object", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetObjectByID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetObjectByID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetObjectByID(THIS, id)"); + { + EntityList * THIS; + Object * RETVAL; + uint32 id = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetObjectByID(id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Object", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetDoorsByDBID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetDoorsByDBID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetDoorsByDBID(THIS, id)"); + { + EntityList * THIS; + Doors * RETVAL; + uint32 id = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDoorsByDBID(id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Doors", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetDoorsByDoorID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetDoorsByDoorID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetDoorsByDoorID(THIS, id)"); + { + EntityList * THIS; + Doors * RETVAL; + uint32 id = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDoorsByDoorID(id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Doors", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetDoorsByID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetDoorsByID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetDoorsByID(THIS, id)"); + { + EntityList * THIS; + Doors * RETVAL; + uint32 id = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDoorsByID(id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Doors", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_FindDoor); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_FindDoor) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::FindDoor(THIS, id)"); + { + EntityList * THIS; + Doors * RETVAL; + uint32 id = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->FindDoor(id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Doors", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetGroupByMob); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetGroupByMob) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetGroupByMob(THIS, mob)"); + { + EntityList * THIS; + Group * RETVAL; + Mob* mob; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + mob = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "mob is not of type Mob"); + if(mob == NULL) + Perl_croak(aTHX_ "mob is NULL, avoiding crash."); + + RETVAL = THIS->GetGroupByMob(mob); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Group", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetGroupByClient); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetGroupByClient) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetGroupByClient(THIS, client)"); + { + EntityList * THIS; + Group * RETVAL; + Client* client; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + client = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "client is not of type Client"); + if(client == NULL) + Perl_croak(aTHX_ "client is NULL, avoiding crash."); + + RETVAL = THIS->GetGroupByClient(client); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Group", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetGroupByID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetGroupByID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetGroupByID(THIS, id)"); + { + EntityList * THIS; + Group * RETVAL; + uint32 id = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetGroupByID(id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Group", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetGroupByLeaderName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetGroupByLeaderName) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetGroupByLeaderName(THIS, leader)"); + { + EntityList * THIS; + Group * RETVAL; + char* leader = (char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetGroupByLeaderName(leader); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Group", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetRaidByID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetRaidByID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetRaidByID(THIS, id)"); + { + EntityList * THIS; + Raid * RETVAL; + uint32 id = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetRaidByID(id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Raid", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetRaidByClient); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetRaidByClient) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetRaidByClient(THIS, client)"); + { + EntityList * THIS; + Raid * RETVAL; + Client* client; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + client = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "client is not of type Client"); + if(client == NULL) + Perl_croak(aTHX_ "client is NULL, avoiding crash."); + + RETVAL = THIS->GetRaidByClient(client); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Raid", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetCorpseByOwner); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetCorpseByOwner) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetCorpseByOwner(THIS, client)"); + { + EntityList * THIS; + Corpse * RETVAL; + Client* client; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + client = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "client is not of type Client"); + if(client == NULL) + Perl_croak(aTHX_ "client is NULL, avoiding crash."); + + RETVAL = THIS->GetCorpseByOwner(client); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Corpse", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetCorpseByID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetCorpseByID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetCorpseByID(THIS, id)"); + { + EntityList * THIS; + Corpse * RETVAL; + uint16 id = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCorpseByID(id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Corpse", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetCorpseByName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetCorpseByName) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::GetCorpseByName(THIS, name)"); + { + EntityList * THIS; + Corpse * RETVAL; + char* name = (char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCorpseByName(name); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Corpse", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_ClearClientPetitionQueue); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_ClearClientPetitionQueue) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::ClearClientPetitionQueue(THIS)"); + { + EntityList * THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->ClearClientPetitionQueue(); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_CanAddHateForMob); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_CanAddHateForMob) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::CanAddHateForMob(THIS, p)"); + { + EntityList * THIS; + bool RETVAL; + Mob * p; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + p = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "p is not of type Mob"); + if(p == NULL) + Perl_croak(aTHX_ "p is NULL, avoiding crash."); + + RETVAL = THIS->CanAddHateForMob(p); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EntityList_Clear); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_Clear) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::Clear(THIS)"); + { + EntityList * THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Clear(); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_RemoveMob); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveMob) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::RemoveMob(THIS, delete_id)"); + { + EntityList * THIS; + bool RETVAL; + uint16 delete_id = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->RemoveMob(delete_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EntityList_RemoveClient); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveClient) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::RemoveClient(THIS, delete_id)"); + { + EntityList * THIS; + bool RETVAL; + uint16 delete_id = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->RemoveClient(delete_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EntityList_RemoveNPC); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveNPC) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::RemoveNPC(THIS, delete_id)"); + { + EntityList * THIS; + bool RETVAL; + uint16 delete_id = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->RemoveNPC(delete_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EntityList_RemoveGroup); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveGroup) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::RemoveGroup(THIS, delete_id)"); + { + EntityList * THIS; + bool RETVAL; + uint32 delete_id = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->RemoveGroup(delete_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EntityList_RemoveCorpse); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveCorpse) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::RemoveCorpse(THIS, delete_id)"); + { + EntityList * THIS; + bool RETVAL; + uint16 delete_id = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->RemoveCorpse(delete_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EntityList_RemoveDoor); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveDoor) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::RemoveDoor(THIS, delete_id)"); + { + EntityList * THIS; + bool RETVAL; + uint16 delete_id = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->RemoveDoor(delete_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EntityList_RemoveTrap); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveTrap) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::RemoveTrap(THIS, delete_id)"); + { + EntityList * THIS; + bool RETVAL; + uint16 delete_id = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->RemoveTrap(delete_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EntityList_RemoveObject); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveObject) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::RemoveObject(THIS, delete_id)"); + { + EntityList * THIS; + bool RETVAL; + uint16 delete_id = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->RemoveObject(delete_id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EntityList_RemoveAllMobs); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveAllMobs) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::RemoveAllMobs(THIS)"); + { + EntityList * THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveAllMobs(); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_RemoveAllClients); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveAllClients) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::RemoveAllClients(THIS)"); + { + EntityList * THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveAllClients(); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_RemoveAllNPCs); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveAllNPCs) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::RemoveAllNPCs(THIS)"); + { + EntityList * THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveAllNPCs(); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_RemoveAllGroups); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveAllGroups) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::RemoveAllGroups(THIS)"); + { + EntityList * THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveAllGroups(); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_RemoveAllCorpses); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveAllCorpses) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::RemoveAllCorpses(THIS)"); + { + EntityList * THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveAllCorpses(); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_RemoveAllDoors); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveAllDoors) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::RemoveAllDoors(THIS)"); + { + EntityList * THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveAllDoors(); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_RemoveAllTraps); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveAllTraps) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::RemoveAllTraps(THIS)"); + { + EntityList * THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveAllTraps(); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_RemoveAllObjects); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveAllObjects) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::RemoveAllObjects(THIS)"); + { + EntityList * THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveAllObjects(); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_Message); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_Message) +{ + dXSARGS; + if (items < 4) + Perl_croak(aTHX_ "Usage: EntityList::Message(THIS, to_guilddbid, type, message, ...)"); + { + EntityList * THIS; + uint32 to_guilddbid = (uint32)SvUV(ST(1)); + uint32 type = (uint32)SvUV(ST(2)); + char* message = (char *)SvPV_nolen(ST(3)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Message(to_guilddbid, type, message); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_MessageStatus); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_MessageStatus) +{ + dXSARGS; + if (items < 5) + Perl_croak(aTHX_ "Usage: EntityList::MessageStatus(THIS, to_guilddbid, to_minstatus, type, message, ...)"); + { + EntityList * THIS; + uint32 to_guilddbid = (uint32)SvUV(ST(1)); + int to_minstatus = (int)SvIV(ST(2)); + uint32 type = (uint32)SvUV(ST(3)); + char* message = (char *)SvPV_nolen(ST(4)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->MessageStatus(to_guilddbid, to_minstatus, type, message); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_MessageClose); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_MessageClose) +{ + dXSARGS; + if (items < 6) + Perl_croak(aTHX_ "Usage: EntityList::MessageClose(THIS, sender, skipsender, dist, type, message, ...)"); + { + EntityList * THIS; + Mob* sender; + bool skipsender = (bool)SvTRUE(ST(2)); + float dist = (float)SvNV(ST(3)); + uint32 type = (uint32)SvUV(ST(4)); + char* message = (char *)SvPV_nolen(ST(5)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + sender = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "sender is not of type Mob"); + if(sender == NULL) + Perl_croak(aTHX_ "sender is NULL, avoiding crash."); + + THIS->MessageClose(sender, skipsender, dist, type, message); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_RemoveFromTargets); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveFromTargets) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::RemoveFromTargets(THIS, mob)"); + { + EntityList * THIS; + Mob* mob; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + mob = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "mob is not of type Mob"); + if(mob == NULL) + Perl_croak(aTHX_ "mob is NULL, avoiding crash."); + + THIS->RemoveFromTargets(mob); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_ReplaceWithTarget); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_ReplaceWithTarget) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: EntityList::ReplaceWithTarget(THIS, pOldMob, pNewTarget)"); + { + EntityList * THIS; + Mob* pOldMob; + Mob* pNewTarget; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + pOldMob = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "pOldMob is not of type Mob"); + if(pOldMob == NULL) + Perl_croak(aTHX_ "pOldMob is NULL, avoiding crash."); + + if (sv_derived_from(ST(2), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(2))); + pNewTarget = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "pNewTarget is not of type Mob"); + if(pNewTarget == NULL) + Perl_croak(aTHX_ "pNewTarget is NULL, avoiding crash."); + + THIS->ReplaceWithTarget(pOldMob, pNewTarget); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_OpenDoorsNear); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_OpenDoorsNear) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::OpenDoorsNear(THIS, opener)"); + { + EntityList * THIS; + NPC* opener; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "NPC")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + opener = INT2PTR(NPC *,tmp); + } + else + Perl_croak(aTHX_ "opener is not of type NPC"); + if(opener == NULL) + Perl_croak(aTHX_ "opener is NULL, avoiding crash."); + + THIS->OpenDoorsNear(opener); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_MakeNameUnique); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_MakeNameUnique) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::MakeNameUnique(THIS, name)"); + { + EntityList * THIS; + char * RETVAL; + dXSTARG; + char* name = (char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->MakeNameUnique(name); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_EntityList_RemoveNumbers); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveNumbers) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::RemoveNumbers(CLASS, name)"); + { + char * RETVAL; + dXSTARG; + char* name = (char *)SvPV_nolen(ST(1)); + + RETVAL = EntityList::RemoveNumbers(name); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_EntityList_SignalMobsByNPCID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_SignalMobsByNPCID) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: EntityList::SignalMobsByNPCID(THIS, npc_type, signal_id)"); + { + EntityList * THIS; + uint32 npc_type = (uint32)SvUV(ST(1)); + int signal_id = (int)SvIV(ST(2)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SignalMobsByNPCID(npc_type, signal_id); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_RemoveEntity); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveEntity) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::RemoveEntity(THIS, id)"); + { + EntityList * THIS; + uint16 id = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveEntity(id); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_DeleteNPCCorpses); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_DeleteNPCCorpses) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::DeleteNPCCorpses(THIS)"); + { + EntityList * THIS; + int32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->DeleteNPCCorpses(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_DeletePlayerCorpses); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_DeletePlayerCorpses) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::DeletePlayerCorpses(THIS)"); + { + EntityList * THIS; + int32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->DeletePlayerCorpses(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_HalveAggro); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_HalveAggro) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::HalveAggro(THIS, who)"); + { + EntityList * THIS; + Mob* who; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + who = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "who is not of type Mob"); + if(who == NULL) + Perl_croak(aTHX_ "who is NULL, avoiding crash."); + + THIS->HalveAggro(who); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_DoubleAggro); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_DoubleAggro) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::DoubleAggro(THIS, who)"); + { + EntityList * THIS; + Mob* who; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + who = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "who is not of type Mob"); + if(who == NULL) + Perl_croak(aTHX_ "who is NULL, avoiding crash."); + + THIS->DoubleAggro(who); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_ClearFeignAggro); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_ClearFeignAggro) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::ClearFeignAggro(THIS, targ)"); + { + EntityList * THIS; + Mob* targ; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + targ = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "targ is not of type Mob"); + if(targ == NULL) + Perl_croak(aTHX_ "targ is NULL, avoiding crash."); + + THIS->ClearFeignAggro(targ); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_Fighting); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_Fighting) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::Fighting(THIS, targ)"); + { + EntityList * THIS; + bool RETVAL; + Mob* targ; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + targ = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "targ is not of type Mob"); + if(targ == NULL) + Perl_croak(aTHX_ "targ is NULL, avoiding crash."); + + RETVAL = THIS->Fighting(targ); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_EntityList_RemoveFromHateLists); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_RemoveFromHateLists) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: EntityList::RemoveFromHateLists(THIS, mob, settoone= false)"); + { + EntityList * THIS; + Mob* mob; + bool settoone; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + mob = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "mob is not of type Mob"); + if(mob == NULL) + Perl_croak(aTHX_ "mob is NULL, avoiding crash."); + + if (items < 3) + settoone = false; + else { + settoone = (bool)SvTRUE(ST(2)); + } + + THIS->RemoveFromHateLists(mob, settoone); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_MessageGroup); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_MessageGroup) +{ + dXSARGS; + if (items < 5) + Perl_croak(aTHX_ "Usage: EntityList::MessageGroup(THIS, sender, skipclose, type, message, ...)"); + { + EntityList * THIS; + Mob* sender; + bool skipclose = (bool)SvTRUE(ST(2)); + uint32 type = (uint32)SvUV(ST(3)); + char* message = (char *)SvPV_nolen(ST(4)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + sender = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "sender is not of type Mob"); + if(sender == NULL) + Perl_croak(aTHX_ "sender is NULL, avoiding crash."); + + THIS->MessageGroup(sender, skipclose, type, message); + } + XSRETURN_EMPTY; +} + +XS(XS_EntityList_GetRandomClient); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetRandomClient) +{ + dXSARGS; + if ((items < 5) || (items > 6)) + Perl_croak(aTHX_ "Usage: EntityList::GetRandomClient(THIS, x, y, z, distance, excludeclient = NULL)"); + { + EntityList *THIS; + Client *RETVAL, *c = NULL; + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(ST(3)); + float d = (float)SvNV(ST(4)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if(items == 6) + { + if (sv_derived_from(ST(5), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(5))); + c = INT2PTR(Client *,tmp); + } + } + RETVAL = entity_list.GetRandomClient(x, y, z, d * d, c); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Client", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_EntityList_GetMobList); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetMobList) +{ + dXSARGS; + int num_mobs = 0; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::GetMobList(THIS)"); + { + EntityList *THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + std::list mob_list; + entity_list.GetMobList(mob_list); + std::list::iterator iter = mob_list.begin(); + + while(iter != mob_list.end()) + { + Mob *entry = (*iter); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Mob", (void*)entry); + XPUSHs(ST(0)); + num_mobs++; + iter++; + } + } + XSRETURN(num_mobs); +} + +XS(XS_EntityList_GetClientList); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetClientList) +{ + dXSARGS; + int num_clients = 0; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::GetClientList(THIS)"); + { + EntityList *THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + std::list client_list; + entity_list.GetClientList(client_list); + std::list::iterator iter = client_list.begin(); + + while(iter != client_list.end()) + { + Client *entry = (*iter); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Client", (void*)entry); + XPUSHs(ST(0)); + num_clients++; + iter++; + } + } + XSRETURN(num_clients); +} + +XS(XS_EntityList_GetNPCList); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetNPCList) +{ + dXSARGS; + int num_npcs = 0; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::GetNPCList(THIS)"); + { + EntityList *THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + std::list npc_list; + entity_list.GetNPCList(npc_list); + std::list::iterator iter = npc_list.begin(); + + while(iter != npc_list.end()) + { + NPC *entry = (*iter); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "NPC", (void*)entry); + XPUSHs(ST(0)); + num_npcs++; + iter++; + } + } + XSRETURN(num_npcs); +} + +XS(XS_EntityList_GetCorpseList); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetCorpseList) +{ + dXSARGS; + int num_corpses = 0; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::GetCorpseList(THIS)"); + { + EntityList *THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + std::list corpse_list; + entity_list.GetCorpseList(corpse_list); + std::list::iterator iter = corpse_list.begin(); + + while(iter != corpse_list.end()) + { + Corpse *entry = (*iter); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Corpse", (void*)entry); + XPUSHs(ST(0)); + num_corpses++; + iter++; + } + } + XSRETURN(num_corpses); +} + +XS(XS_EntityList_GetObjectList); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetObjectList) +{ + dXSARGS; + int num_objects = 0; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::GetObjectList(THIS)"); + { + EntityList *THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + std::list object_list; + entity_list.GetObjectList(object_list); + std::list::iterator iter = object_list.begin(); + + while(iter != object_list.end()) + { + Object *entry = (*iter); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Object", (void*)entry); + XPUSHs(ST(0)); + num_objects++; + iter++; + } + } + XSRETURN(num_objects); +} + +XS(XS_EntityList_GetDoorsList); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_GetDoorsList) +{ + dXSARGS; + int num_objects = 0; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::GetDoorsList(THIS)"); + { + EntityList *THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + std::list door_list; + entity_list.GetDoorsList(door_list); + std::list::iterator iter = door_list.begin(); + + while(iter != door_list.end()) + { + Doors *entry = (*iter); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Doors", (void*)entry); + XPUSHs(ST(0)); + num_objects++; + iter++; + } + } + XSRETURN(num_objects); +} + +XS(XS_EntityList_SignalAllClients); /* prototype to pass -Wmissing-prototypes */ +XS(XS_EntityList_SignalAllClients) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: EntityList::SignalAllClients(THIS, data)"); + { + EntityList *THIS; + uint32 data = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + entity_list.SignalAllClients(data); + } + XSRETURN_EMPTY; +} + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_EntityList); /* prototype to pass -Wmissing-prototypes */ +XS(boot_EntityList) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "GetMobID"), XS_EntityList_GetMobID, file, "$$"); + newXSproto(strcpy(buf, "GetMob"), XS_EntityList_GetMob, file, "$$"); + newXSproto(strcpy(buf, "GetMobByID"), XS_EntityList_GetMobByID, file, "$$"); + newXSproto(strcpy(buf, "GetMobByNpcTypeID"), XS_EntityList_GetMobByNpcTypeID, file, "$$"); + newXSproto(strcpy(buf, "GetNPCByID"), XS_EntityList_GetNPCByID, file, "$$"); + newXSproto(strcpy(buf, "GetNPCByNPCTypeID"), XS_EntityList_GetNPCByNPCTypeID, file, "$$"); + newXSproto(strcpy(buf, "GetClientByName"), XS_EntityList_GetClientByName, file, "$$"); + newXSproto(strcpy(buf, "GetClientByAccID"), XS_EntityList_GetClientByAccID, file, "$$"); + newXSproto(strcpy(buf, "GetClientByID"), XS_EntityList_GetClientByID, file, "$$"); + newXSproto(strcpy(buf, "GetClientByCharID"), XS_EntityList_GetClientByCharID, file, "$$"); + newXSproto(strcpy(buf, "GetClientByWID"), XS_EntityList_GetClientByWID, file, "$$"); + newXSproto(strcpy(buf, "GetObjectByID"), XS_EntityList_GetObjectByID, file, "$"); + newXSproto(strcpy(buf, "GetObjectByDBID"), XS_EntityList_GetObjectByDBID, file, "$"); + newXSproto(strcpy(buf, "GetDoorsByID"), XS_EntityList_GetDoorsByID, file, "$$"); + newXSproto(strcpy(buf, "GetDoorsByDBID"), XS_EntityList_GetDoorsByDBID, file, "$$"); + newXSproto(strcpy(buf, "GetDoorsByDoorID"), XS_EntityList_GetDoorsByDoorID, file, "$$"); + newXSproto(strcpy(buf, "FindDoor"), XS_EntityList_FindDoor, file, "$$"); + newXSproto(strcpy(buf, "GetGroupByMob"), XS_EntityList_GetGroupByMob, file, "$$"); + newXSproto(strcpy(buf, "GetGroupByClient"), XS_EntityList_GetGroupByClient, file, "$$"); + newXSproto(strcpy(buf, "GetGroupByID"), XS_EntityList_GetGroupByID, file, "$$"); + newXSproto(strcpy(buf, "GetGroupByLeaderName"), XS_EntityList_GetGroupByLeaderName, file, "$$"); + newXSproto(strcpy(buf, "GetRaidByID"), XS_EntityList_GetRaidByID, file, "$$"); + newXSproto(strcpy(buf, "GetRaidByClient"), XS_EntityList_GetRaidByClient, file, "$$"); + newXSproto(strcpy(buf, "GetCorpseByOwner"), XS_EntityList_GetCorpseByOwner, file, "$$"); + newXSproto(strcpy(buf, "GetCorpseByID"), XS_EntityList_GetCorpseByID, file, "$$"); + newXSproto(strcpy(buf, "GetCorpseByName"), XS_EntityList_GetCorpseByName, file, "$$"); + newXSproto(strcpy(buf, "ClearClientPetitionQueue"), XS_EntityList_ClearClientPetitionQueue, file, "$"); + newXSproto(strcpy(buf, "CanAddHateForMob"), XS_EntityList_CanAddHateForMob, file, "$$"); + newXSproto(strcpy(buf, "Clear"), XS_EntityList_Clear, file, "$"); + newXSproto(strcpy(buf, "RemoveMob"), XS_EntityList_RemoveMob, file, "$$"); + newXSproto(strcpy(buf, "RemoveClient"), XS_EntityList_RemoveClient, file, "$$"); + newXSproto(strcpy(buf, "RemoveNPC"), XS_EntityList_RemoveNPC, file, "$$"); + newXSproto(strcpy(buf, "RemoveGroup"), XS_EntityList_RemoveGroup, file, "$$"); + newXSproto(strcpy(buf, "RemoveCorpse"), XS_EntityList_RemoveCorpse, file, "$$"); + newXSproto(strcpy(buf, "RemoveDoor"), XS_EntityList_RemoveDoor, file, "$$"); + newXSproto(strcpy(buf, "RemoveTrap"), XS_EntityList_RemoveTrap, file, "$$"); + newXSproto(strcpy(buf, "RemoveObject"), XS_EntityList_RemoveObject, file, "$$"); + newXSproto(strcpy(buf, "RemoveAllMobs"), XS_EntityList_RemoveAllMobs, file, "$"); + newXSproto(strcpy(buf, "RemoveAllClients"), XS_EntityList_RemoveAllClients, file, "$"); + newXSproto(strcpy(buf, "RemoveAllNPCs"), XS_EntityList_RemoveAllNPCs, file, "$"); + newXSproto(strcpy(buf, "RemoveAllGroups"), XS_EntityList_RemoveAllGroups, file, "$"); + newXSproto(strcpy(buf, "RemoveAllCorpses"), XS_EntityList_RemoveAllCorpses, file, "$"); + newXSproto(strcpy(buf, "RemoveAllDoors"), XS_EntityList_RemoveAllDoors, file, "$"); + newXSproto(strcpy(buf, "RemoveAllTraps"), XS_EntityList_RemoveAllTraps, file, "$"); + newXSproto(strcpy(buf, "RemoveAllObjects"), XS_EntityList_RemoveAllObjects, file, "$"); + newXSproto(strcpy(buf, "Message"), XS_EntityList_Message, file, "$$$$;@"); + newXSproto(strcpy(buf, "MessageStatus"), XS_EntityList_MessageStatus, file, "$$$$$;@"); + newXSproto(strcpy(buf, "MessageClose"), XS_EntityList_MessageClose, file, "$$$$$$;@"); + newXSproto(strcpy(buf, "RemoveFromTargets"), XS_EntityList_RemoveFromTargets, file, "$$"); + newXSproto(strcpy(buf, "ReplaceWithTarget"), XS_EntityList_ReplaceWithTarget, file, "$$$"); + newXSproto(strcpy(buf, "OpenDoorsNear"), XS_EntityList_OpenDoorsNear, file, "$$"); + newXSproto(strcpy(buf, "MakeNameUnique"), XS_EntityList_MakeNameUnique, file, "$$"); + newXSproto(strcpy(buf, "RemoveNumbers"), XS_EntityList_RemoveNumbers, file, "$$"); + newXSproto(strcpy(buf, "SignalMobsByNPCID"), XS_EntityList_SignalMobsByNPCID, file, "$$$"); + newXSproto(strcpy(buf, "RemoveEntity"), XS_EntityList_RemoveEntity, file, "$$"); + newXSproto(strcpy(buf, "DeleteNPCCorpses"), XS_EntityList_DeleteNPCCorpses, file, "$"); + newXSproto(strcpy(buf, "DeletePlayerCorpses"), XS_EntityList_DeletePlayerCorpses, file, "$"); + newXSproto(strcpy(buf, "HalveAggro"), XS_EntityList_HalveAggro, file, "$$"); + newXSproto(strcpy(buf, "DoubleAggro"), XS_EntityList_DoubleAggro, file, "$$"); + newXSproto(strcpy(buf, "ClearFeignAggro"), XS_EntityList_ClearFeignAggro, file, "$$"); + newXSproto(strcpy(buf, "Fighting"), XS_EntityList_Fighting, file, "$$"); + newXSproto(strcpy(buf, "RemoveFromHateLists"), XS_EntityList_RemoveFromHateLists, file, "$$;$"); + newXSproto(strcpy(buf, "MessageGroup"), XS_EntityList_MessageGroup, file, "$$$$$;@"); + newXSproto(strcpy(buf, "GetRandomClient"), XS_EntityList_GetRandomClient, file, "$$$$$;$"); + newXSproto(strcpy(buf, "GetMobList"), XS_EntityList_GetMobList, file, "$"); + newXSproto(strcpy(buf, "GetClientList"), XS_EntityList_GetClientList, file, "$"); + newXSproto(strcpy(buf, "GetNPCList"), XS_EntityList_GetNPCList, file, "$"); + newXSproto(strcpy(buf, "GetCorpseList"), XS_EntityList_GetCorpseList, file, "$"); + newXSproto(strcpy(buf, "GetObjectList"), XS_EntityList_GetObjectList, file, "$"); + newXSproto(strcpy(buf, "GetDoorsList"), XS_EntityList_GetDoorsList, file, "$"); + newXSproto(strcpy(buf, "SignalAllClients"), XS_EntityList_SignalAllClients, file, "$$"); + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES + diff --git a/zone/perl_groups.cpp b/zone/perl_groups.cpp new file mode 100644 index 000000000..54cb3b836 --- /dev/null +++ b/zone/perl_groups.cpp @@ -0,0 +1,657 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of tmp. Do not edit this file, edit tmp instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +#include "groups.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +XS(XS_Group_DisbandGroup); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_DisbandGroup) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Group::DisbandGroup(THIS)"); + { + Group * THIS; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->DisbandGroup(); + } + XSRETURN_EMPTY; +} + +XS(XS_Group_IsGroupMember); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_IsGroupMember) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Group::IsGroupMember(THIS, client)"); + { + Group * THIS; + bool RETVAL; + Mob* client; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + client = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "client is not of type Mob"); + if(client == NULL) + Perl_croak(aTHX_ "client is NULL, avoiding crash."); + + RETVAL = THIS->IsGroupMember(client); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Group_CastGroupSpell); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_CastGroupSpell) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Group::CastGroupSpell(THIS, caster, spellid)"); + { + Group * THIS; + Mob* caster; + uint16 spellid = (uint16)SvUV(ST(2)); + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + caster = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "caster is not of type Mob"); + if(caster == NULL) + Perl_croak(aTHX_ "caster is NULL, avoiding crash."); + + THIS->CastGroupSpell(caster, spellid); + } + XSRETURN_EMPTY; +} + +XS(XS_Group_SplitExp); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_SplitExp) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Group::SplitExp(THIS, exp, other)"); + { + Group * THIS; + uint32 exp = (uint32)SvUV(ST(1)); + Mob* other; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(2), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(2))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + + THIS->SplitExp(exp, other); + } + XSRETURN_EMPTY; +} + +XS(XS_Group_GroupMessage); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_GroupMessage) +{ + dXSARGS; + if ((items != 3) && (items != 4)) // the 3 item version is kept for backwards compatability + Perl_croak(aTHX_ "Usage: Group::GroupMessage(THIS, sender, language, message)"); + { + Group * THIS; + Mob* sender; + uint8 language; + char* message; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + sender = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "sender is not of type Mob"); + if(sender == NULL) + Perl_croak(aTHX_ "sender is NULL, avoiding crash."); + + if (items == 4) { + language = (uint8)SvUV(ST(2)); + if ((language >= MAX_PP_LANGUAGE) || (language < 0)) + language = 0; + message = (char *)SvPV_nolen(ST(3)); + THIS->GroupMessage(sender, language, 100, message); + } + else { // if no language is specificed, send it in common + message = (char *)SvPV_nolen(ST(2)); + THIS->GroupMessage(sender,0, 100, message); + } + } + XSRETURN_EMPTY; +} + +XS(XS_Group_GetTotalGroupDamage); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_GetTotalGroupDamage) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Group::GetTotalGroupDamage(THIS, other)"); + { + Group * THIS; + uint32 RETVAL; + dXSTARG; + Mob* other; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + + RETVAL = THIS->GetTotalGroupDamage(other); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Group_SplitMoney); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_SplitMoney) +{ + dXSARGS; + if (items != 5) + Perl_croak(aTHX_ "Usage: Group::SplitMoney(THIS, copper, silver, gold, platinum)"); + { + Group * THIS; + uint32 copper = (uint32)SvUV(ST(1)); + uint32 silver = (uint32)SvUV(ST(2)); + uint32 gold = (uint32)SvUV(ST(3)); + uint32 platinum = (uint32)SvUV(ST(4)); + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SplitMoney(copper, silver, gold, platinum); + } + XSRETURN_EMPTY; +} + +XS(XS_Group_SetLeader); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_SetLeader) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Group::SetLeader(THIS, newleader)"); + { + Group * THIS; + Mob* newleader; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + newleader = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "newleader is not of type Mob"); + if(newleader == NULL) + Perl_croak(aTHX_ "newleader is NULL, avoiding crash."); + + THIS->SetLeader(newleader); + } + XSRETURN_EMPTY; +} + +XS(XS_Group_GetLeader); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_GetLeader) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Group::GetLeader(THIS)"); + { + Group * THIS; + Mob * RETVAL; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetLeader(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Mob", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Group_GetLeaderName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_GetLeaderName) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Group::GetLeaderName(THIS)"); + { + Group * THIS; + char * RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetLeaderName(); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Group_SendHPPacketsTo); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_SendHPPacketsTo) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Group::SendHPPacketsTo(THIS, newmember)"); + { + Group * THIS; + Mob* newmember; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + newmember = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "newmember is not of type Mob"); + if(newmember == NULL) + Perl_croak(aTHX_ "newmember is NULL, avoiding crash."); + + THIS->SendHPPacketsTo(newmember); + } + XSRETURN_EMPTY; +} + +XS(XS_Group_SendHPPacketsFrom); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_SendHPPacketsFrom) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Group::SendHPPacketsFrom(THIS, newmember)"); + { + Group * THIS; + Mob* newmember; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + newmember = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "newmember is not of type Mob"); + if(newmember == NULL) + Perl_croak(aTHX_ "newmember is NULL, avoiding crash."); + + THIS->SendHPPacketsFrom(newmember); + } + XSRETURN_EMPTY; +} + +XS(XS_Group_IsLeader); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_IsLeader) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Group::IsLeader(THIS, leadertest)"); + { + Group * THIS; + bool RETVAL; + Mob* leadertest; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + leadertest = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "leadertest is not of type Mob"); + if(leadertest == NULL) + Perl_croak(aTHX_ "leadertest is NULL, avoiding crash."); + + RETVAL = THIS->IsLeader(leadertest); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Group_GroupCount); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_GroupCount) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Group::GroupCount(THIS)"); + { + Group * THIS; + uint8 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GroupCount(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Group_GetHighestLevel); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_GetHighestLevel) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Group::GetHighestLevel(THIS)"); + { + Group * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetHighestLevel(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Group_TeleportGroup); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_TeleportGroup) +{ + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: Group::TeleportGroup(THIS, sender, zoneID, x, y, z, heading)"); + { + Group * THIS; + Mob* sender; + uint32 zoneID = (uint32)SvUV(ST(2)); + float x = (float)SvNV(ST(3)); + float y = (float)SvNV(ST(4)); + float z = (float)SvNV(ST(5)); + float heading = (float)SvNV(ST(6)); + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + sender = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "sender is not of type Mob"); + if(sender == NULL) + Perl_croak(aTHX_ "sender is NULL, avoiding crash."); + + THIS->TeleportGroup(sender, zoneID, 0, x, y, z, heading); + } + XSRETURN_EMPTY; +} + +XS(XS_Group_GetID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Group_GetID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Group::GetID(THIS)"); + { + Group * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Group_GetMember); +XS(XS_Group_GetMember) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Group::GetMember(THIS, index)"); + { + Group * THIS; + Mob* member; + Client* RETVAL = NULL; + dXSTARG; + + if (sv_derived_from(ST(0), "Group")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Group *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Group"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + int index = (int)SvUV(ST(1)); + if (index < 0 || index > 5) + RETVAL = NULL; + else { + member = THIS->members[index]; + if (member != NULL) + RETVAL = member->CastToClient(); + } + + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Client", (void*)RETVAL); + } + XSRETURN(1); +} + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_Group); /* prototype to pass -Wmissing-prototypes */ +XS(boot_Group) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "DisbandGroup"), XS_Group_DisbandGroup, file, "$"); + newXSproto(strcpy(buf, "IsGroupMember"), XS_Group_IsGroupMember, file, "$$"); + newXSproto(strcpy(buf, "CastGroupSpell"), XS_Group_CastGroupSpell, file, "$$$"); + newXSproto(strcpy(buf, "SplitExp"), XS_Group_SplitExp, file, "$$$"); + newXSproto(strcpy(buf, "GroupMessage"), XS_Group_GroupMessage, file, "$$$"); + newXSproto(strcpy(buf, "GetTotalGroupDamage"), XS_Group_GetTotalGroupDamage, file, "$$"); + newXSproto(strcpy(buf, "SplitMoney"), XS_Group_SplitMoney, file, "$$$$$"); + newXSproto(strcpy(buf, "SetLeader"), XS_Group_SetLeader, file, "$$"); + newXSproto(strcpy(buf, "GetLeader"), XS_Group_GetLeader, file, "$"); + newXSproto(strcpy(buf, "GetLeaderName"), XS_Group_GetLeaderName, file, "$"); + newXSproto(strcpy(buf, "SendHPPacketsTo"), XS_Group_SendHPPacketsTo, file, "$$"); + newXSproto(strcpy(buf, "SendHPPacketsFrom"), XS_Group_SendHPPacketsFrom, file, "$$"); + newXSproto(strcpy(buf, "IsLeader"), XS_Group_IsLeader, file, "$$"); + newXSproto(strcpy(buf, "GroupCount"), XS_Group_GroupCount, file, "$"); + newXSproto(strcpy(buf, "GetHighestLevel"), XS_Group_GetHighestLevel, file, "$"); + newXSproto(strcpy(buf, "TeleportGroup"), XS_Group_TeleportGroup, file, "$$$$$$$"); + newXSproto(strcpy(buf, "GetID"), XS_Group_GetID, file, "$"); + newXSproto(strcpy(buf, "GetMember"), XS_Group_GetMember, file, "$$"); + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES + diff --git a/zone/perl_hateentry.cpp b/zone/perl_hateentry.cpp new file mode 100644 index 000000000..4d30af0ac --- /dev/null +++ b/zone/perl_hateentry.cpp @@ -0,0 +1,137 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2009 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 "features.h" +#include "client.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +#include "../common/linked_list.h" +#include "hate_list.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + +XS(XS_HateEntry_GetEnt); /* prototype to pass -Wmissing-prototypes */ +XS(XS_HateEntry_GetEnt) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: HateEntry::GetData(THIS)"); + { + tHateEntry * THIS; + Mob * RETVAL; + + if (sv_derived_from(ST(0), "HateEntry")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(tHateEntry *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type tHateEntry"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->ent; + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Mob", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_HateEntry_GetHate); /* prototype to pass -Wmissing-prototypes */ +XS(XS_HateEntry_GetHate) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: HateEntry::GetHate(THIS)"); + { + tHateEntry * THIS; + int32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "HateEntry")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(tHateEntry *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type tHateEntry"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->hate; + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_HateEntry_GetDamage); /* prototype to pass -Wmissing-prototypes */ +XS(XS_HateEntry_GetDamage) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: HateEntry::GetDamage(THIS)"); + { + tHateEntry * THIS; + int32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "HateEntry")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(tHateEntry *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type tHateEntry"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->damage; + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +#ifdef __cplusplus +extern "C" +#endif + +XS(boot_HateEntry); +XS(boot_HateEntry) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "GetEnt"), XS_HateEntry_GetEnt, file, "$"); + newXSproto(strcpy(buf, "GetDamage"), XS_HateEntry_GetDamage, file, "$"); + newXSproto(strcpy(buf, "GetHate"), XS_HateEntry_GetHate, file, "$"); + + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES \ No newline at end of file diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp new file mode 100644 index 000000000..fb05e4c9b --- /dev/null +++ b/zone/perl_mob.cpp @@ -0,0 +1,8351 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of tmp. Do not edit this file, edit tmp instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +typedef const char Const_char; + +#include "mob.h" +#include "client.h" +#include "spdat.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +XS(XS_Mob_IsClient); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsClient) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsClient(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsClient(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_IsNPC); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsNPC) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsNPC(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsNPC(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_IsMob); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsMob) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsMob(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsMob(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_IsCorpse); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsCorpse) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsCorpse(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsCorpse(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_IsPlayerCorpse); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsPlayerCorpse) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsPlayerCorpse(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsPlayerCorpse(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_IsNPCCorpse); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsNPCCorpse) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsNPCCorpse(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsNPCCorpse(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_IsObject); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsObject) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsObject(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsObject(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_IsDoor); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsDoor) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsDoor(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsDoor(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_IsTrap); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsTrap) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsTrap(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsTrap(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_IsBeacon); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsBeacon) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsBeacon(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsBeacon(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_CastToClient); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CastToClient) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::CastToClient(THIS)"); + { + Mob * THIS; + Client * RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CastToClient(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Client", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_CastToNPC); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CastToNPC) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::CastToNPC(THIS)"); + { + Mob * THIS; + NPC * RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CastToNPC(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "NPC", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_CastToMob); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CastToMob) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::CastToMob(THIS)"); + { + Mob * THIS; + Mob * RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CastToMob(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Mob", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_CastToCorpse); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CastToCorpse) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::CastToCorpse(THIS)"); + { + Mob * THIS; + Corpse * RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CastToCorpse(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Corpse", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetID(THIS)"); + { + Mob * THIS; + uint16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetName) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetName(THIS)"); + { + Mob * THIS; + Const_char * RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetName(); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Mob_Depop); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Depop) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Mob::Depop(THIS, StartSpawnTimer = true)"); + { + Mob * THIS; + bool StartSpawnTimer; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 2) + StartSpawnTimer = true; + else { + StartSpawnTimer = (bool)SvTRUE(ST(1)); + } + + THIS->Depop(StartSpawnTimer); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_RogueAssassinate); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_RogueAssassinate) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::RogueAssassinate(THIS, other)"); + { + Mob * THIS; + Mob* other; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + + THIS->RogueAssassinate(other); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_BehindMob); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_BehindMob) +{ + dXSARGS; + if (items < 1 || items > 4) + Perl_croak(aTHX_ "Usage: Mob::BehindMob(THIS, other= 0, playerx= 0.0f, playery= 0.0f)"); + { + Mob * THIS; + bool RETVAL; + Mob* other; + float playerx; + float playery; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 2) + other = 0; + else { + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + } + + if (items < 3) + playerx = 0.0f; + else { + playerx = (float)SvNV(ST(2)); + } + + if (items < 4) + playery = 0.0f; + else { + playery = (float)SvNV(ST(3)); + } + + RETVAL = THIS->BehindMob(other, playerx, playery); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_SetLevel); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetLevel) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Mob::SetLevel(THIS, in_level, command= false)"); + { + Mob * THIS; + uint8 in_level = (uint8)SvUV(ST(1)); + bool command; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 3) + command = false; + else { + command = (bool)SvTRUE(ST(2)); + } + + THIS->SetLevel(in_level, command); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetSkill) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetSkill(THIS, skill_num)"); + { + Mob * THIS; + uint32 RETVAL; + dXSTARG; + SkillType skill_num = (SkillType)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetSkill(skill_num); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SendWearChange); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SendWearChange) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SendWearChange(THIS, material_slot)"); + { + Mob * THIS; + uint8 material_slot = (uint8)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendWearChange(material_slot); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetEquipment); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetEquipment) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetEquipment(THIS, material_slot)"); + { + Mob * THIS; + int32 RETVAL; + dXSTARG; + uint8 material_slot = (uint8)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetEquipment(material_slot); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetEquipmentMaterial); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetEquipmentMaterial) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetEquipmentMaterial(THIS, material_slot)"); + { + Mob * THIS; + int32 RETVAL; + dXSTARG; + uint8 material_slot = (uint8)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetEquipmentMaterial(material_slot); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetEquipmentColor); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetEquipmentColor) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetEquipmentColor(THIS, material_slot)"); + { + Mob * THIS; + int32 RETVAL; + dXSTARG; + uint8 material_slot = (uint8)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetEquipmentColor(material_slot); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetArmorTint); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetArmorTint) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetArmorTint(THIS, material_slot)"); + { + Mob * THIS; + int32 RETVAL; + dXSTARG; + uint8 material_slot = (uint8)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetArmorTint(material_slot); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_IsMoving); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsMoving) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsMoving(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsMoving(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_GoToBind); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GoToBind) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GoToBind(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->GoToBind(); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_Gate); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Gate) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::Gate(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Gate(); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_Attack); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Attack) +{ + dXSARGS; + if (items < 2 || items > 4) + Perl_croak(aTHX_ "Usage: Mob::Attack(THIS, other, Hand= 13, FromRiposte= false)"); + { + Mob * THIS; + bool RETVAL; + Mob* other; + int Hand; + bool FromRiposte; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + + if (items < 3) + Hand = 13; + else { + Hand = (int)SvIV(ST(2)); + } + + if (items < 4) + FromRiposte = false; + else { + FromRiposte = (bool)SvTRUE(ST(3)); + } + + RETVAL = THIS->Attack(other, Hand, FromRiposte); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_Damage); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Damage) +{ + dXSARGS; + if (items < 5 || items > 8) + Perl_croak(aTHX_ "Usage: Mob::Damage(THIS, from, damage, spell_id, attack_skill, avoidable= true, buffslot= -1, iBuffTic= false)"); + { + Mob * THIS; + Mob* from; + int32 damage = (int32)SvIV(ST(2)); + uint16 spell_id = (uint16)SvUV(ST(3)); + SkillType attack_skill = (SkillType)SvUV(ST(4)); + bool avoidable; + int8 buffslot; + bool iBuffTic; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + from = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "from is not of type Mob"); + if(from == NULL) + Perl_croak(aTHX_ "from is NULL, avoiding crash."); + + if (items < 6) + avoidable = true; + else { + avoidable = (bool)SvTRUE(ST(5)); + } + + if (items < 7) + buffslot = -1; + else { + buffslot = (int8)SvIV(ST(6)); + } + + if (items < 8) + iBuffTic = false; + else { + iBuffTic = (bool)SvTRUE(ST(7)); + } + + THIS->Damage(from, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_RangedAttack); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_RangedAttack) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::RangedAttack(THIS, other)"); + { + Mob * THIS; + Mob* other; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + + THIS->RangedAttack(other); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_ThrowingAttack); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_ThrowingAttack) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::ThrowingAttack(THIS, other)"); + { + Mob * THIS; + Mob* other; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + + THIS->ThrowingAttack(other); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_Heal); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Heal) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::Heal(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Heal(); + } + XSRETURN_EMPTY; +} + + +XS(XS_Mob_HealDamage); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_HealDamage) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Mob::HealDamage(THIS, amount, caster = 0)"); + { + Mob * THIS; + int32 heal_amt = (int32)SvIV(ST(1)); + Mob * caster = NULL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if(items == 3) + { + if (sv_derived_from(ST(2), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(2))); + caster = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "caster is not of type Mob"); + if(caster == NULL) + Perl_croak(aTHX_ "caster is NULL, avoiding crash."); + } + + THIS->HealDamage(heal_amt, caster); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetMaxHP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetMaxHP) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::SetMaxHP(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetMaxHP(); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetLevelCon); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetLevelCon) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetLevelCon(THIS, iOtherLevel)"); + { + Mob * THIS; + uint32 RETVAL; + dXSTARG; + uint8 iOtherLevel = (uint8)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetLevelCon(iOtherLevel); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SetHP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetHP) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetHP(THIS, hp)"); + { + Mob * THIS; + int32 hp = (int32)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetHP(hp); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_DoAnim); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_DoAnim) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Mob::DoAnim(THIS, animnum, type=0)"); + { + Mob * THIS; + int animnum = (int)SvIV(ST(1)); + int type; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 3) + type = 0; + else { + type = (int)SvIV(ST(2)); + } + + THIS->DoAnim(animnum, type); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_ChangeSize); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_ChangeSize) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Mob::ChangeSize(THIS, in_size, bNoRestriction= false)"); + { + Mob * THIS; + float in_size = (float)SvNV(ST(1)); + bool bNoRestriction; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 3) + bNoRestriction = false; + else { + bNoRestriction = (bool)SvTRUE(ST(2)); + } + + THIS->ChangeSize(in_size, bNoRestriction); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GMMove); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GMMove) +{ + dXSARGS; + if (items < 4 || items > 5) + Perl_croak(aTHX_ "Usage: Mob::GMMove(THIS, x, y, z, heading= 0.01)"); + { + Mob * THIS; + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(ST(3)); + float heading; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 5) + heading = 0.01; + else { + heading = (float)SvNV(ST(4)); + } + + THIS->GMMove(x, y, z, heading); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SendPosUpdate); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SendPosUpdate) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Mob::SendPosUpdate(THIS, iSendToSelf= 0)"); + { + Mob * THIS; + uint8 iSendToSelf; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 2) + iSendToSelf = 0; + else { + iSendToSelf = (uint8)SvUV(ST(1)); + } + + THIS->SendPosUpdate(iSendToSelf); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SendPosition); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SendPosition) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::SendPosition(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendPosition(); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_HasProcs); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_HasProcs) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::HasProcs(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->HasProcs(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_IsInvisible); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsInvisible) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Mob::IsInvisible(THIS, other= 0)"); + { + Mob * THIS; + bool RETVAL; + Mob * other; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 2) + other = 0; + else { + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + } + + RETVAL = THIS->IsInvisible(other); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_SetInvisible); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetInvisible) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetInvisible(THIS, state)"); + { + Mob * THIS; + uint8 state = (uint8)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetInvisible(state); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_FindBuff); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_FindBuff) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::FindBuff(THIS, spellid)"); + { + Mob * THIS; + bool RETVAL; + uint16 spellid = (uint16)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->FindBuff(spellid); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_FindType); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_FindType) +{ + dXSARGS; + if (items < 2 || items > 4) + Perl_croak(aTHX_ "Usage: Mob::FindType(THIS, type, bOffensive= false, threshold= 100)"); + { + Mob * THIS; + bool RETVAL; + uint8 type = (uint8)SvUV(ST(1)); + bool bOffensive; + uint16 threshold; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 3) + bOffensive = false; + else { + bOffensive = (bool)SvTRUE(ST(2)); + } + + if (items < 4) + threshold = 100; + else { + threshold = (uint16)SvUV(ST(3)); + } + + RETVAL = THIS->FindType(type, bOffensive, threshold); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_GetBuffSlotFromType); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetBuffSlotFromType) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetBuffSlotFromType(THIS, type)"); + { + Mob * THIS; + int8 RETVAL; + dXSTARG; + uint8 type = (uint8)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetBuffSlotFromType(type); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_MakePet); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_MakePet) +{ + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: Mob::MakePet(THIS, spell_id, pettype, name=NULL)"); + { + Mob * THIS; + uint16 spell_id = (uint16)SvUV(ST(1)); + char* pettype = (char *)SvPV_nolen(ST(2)); + char * name; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 4) + name = NULL; + else { + name = (char *)SvPV_nolen(ST(3)); + } + + THIS->MakePet(spell_id, pettype, name); + } + XSRETURN_EMPTY; +} + +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=NULL, duration=0, target=NULL)"); + { + Mob * THIS; + uint16 spell_id = (uint16)SvUV(ST(1)); + char * name; + uint32 duration; + Mob * target; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 3) + name = NULL; + else + name = (char *)SvPV_nolen(ST(2)); + + if (items < 4) + duration = 0; + else + duration = (uint32)SvUV(ST(3)); + + if (items < 5) + target = NULL; + else if (sv_derived_from(ST(4), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(4))); + target = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "owner is not of type Mob"); + + THIS->TemporaryPets(spell_id, target, name, duration); + } + XSRETURN_EMPTY; +} + +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=NULL, duration=0, target=NULL, follow=0)"); + { + Mob * THIS; + uint32 typesid = (uint32)SvUV(ST(1)); + char * name; + uint32 duration; + Mob * target; + bool follow; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 3) + name = NULL; + else + name = (char *)SvPV_nolen(ST(2)); + + if (items < 4) + duration = 0; + else + duration = (uint32)SvUV(ST(3)); + + if (items < 5) + target = NULL; + else if (sv_derived_from(ST(4), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(4))); + target = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "target is not of type Mob"); + + if (items < 6) + follow = false; + else { + follow = (bool)SvTRUE(ST(5)); + } + + THIS->TypesTemporaryPets(typesid, target, name, duration, follow); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetBaseRace); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetBaseRace) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetBaseRace(THIS)"); + { + Mob * THIS; + uint16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetBaseRace(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetBaseGender); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetBaseGender) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetBaseGender(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetBaseGender(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetDeity); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetDeity) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetDeity(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDeity(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetRace); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetRace) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetRace(THIS)"); + { + Mob * THIS; + uint16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetRace(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetGender); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetGender) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetGender(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetGender(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetTexture); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetTexture) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetTexture(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetTexture(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetHelmTexture); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHelmTexture) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetHelmTexture(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetHelmTexture(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetHairColor); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHairColor) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetHairColor(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetHairColor(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetBeardColor); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetBeardColor) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetBeardColor(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetBeardColor(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetEyeColor1); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetEyeColor1) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetEyeColor1(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetEyeColor1(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetEyeColor2); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetEyeColor2) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetEyeColor2(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetEyeColor2(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetHairStyle); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHairStyle) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetHairStyle(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetHairStyle(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetLuclinFace); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetLuclinFace) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetLuclinFace(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetLuclinFace(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetBeard); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetBeard) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetBeard(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetBeard(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetDrakkinHeritage); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetDrakkinHeritage) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetDrakkinHeritage(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDrakkinHeritage(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetDrakkinTattoo); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetDrakkinTattoo) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetDrakkinTattoo(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDrakkinTattoo(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetDrakkinDetails); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetDrakkinDetails) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetDrakkinDetails(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDrakkinDetails(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetClass); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetClass) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetClass(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetClass(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetLevel); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetLevel) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetLevel(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetLevel(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetCleanName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetCleanName) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetCleanName(THIS)"); + { + Mob * THIS; + Const_char * RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCleanName(); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Mob_GetTarget); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetTarget) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetTarget(THIS)"); + { + Mob * THIS; + Mob * RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetTarget(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Mob", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SetTarget); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetTarget) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetTarget(THIS, mob)"); + { + Mob * THIS; + Mob* mob; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + mob = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "mob is not of type Mob"); + if(mob == NULL) + Perl_croak(aTHX_ "mob is NULL, avoiding crash."); + + THIS->SetTarget(mob); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetHPRatio); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHPRatio) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetHPRatio(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetHPRatio(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_IsWarriorClass); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsWarriorClass) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsWarriorClass(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsWarriorClass(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_GetHP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHP) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetHP(THIS)"); + { + Mob * THIS; + int32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetHP(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetMaxHP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetMaxHP) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetMaxHP(THIS)"); + { + Mob * THIS; + int32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMaxHP(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetItemHPBonuses); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetItemHPBonuses) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetItemHPBonuses(THIS)"); + { + Mob * THIS; + int32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetItemHPBonuses(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetSpellHPBonuses); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetSpellHPBonuses) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetSpellHPBonuses(THIS)"); + { + Mob * THIS; + int32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetSpellHPBonuses(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetWalkspeed); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetWalkspeed) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetWalkspeed(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetWalkspeed(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetRunspeed); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetRunspeed) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetRunspeed(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetRunspeed(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetCasterLevel); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetCasterLevel) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetCasterLevel(THIS, spell_id)"); + { + Mob * THIS; + int RETVAL; + dXSTARG; + uint16 spell_id = (uint16)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCasterLevel(spell_id); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetMaxMana); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetMaxMana) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetMaxMana(THIS)"); + { + Mob * THIS; + int32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMaxMana(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetMana); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetMana) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetMana(THIS)"); + { + Mob * THIS; + int32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMana(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SetMana); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetMana) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetMana(THIS, amount)"); + { + Mob * THIS; + int32 amount = (int32)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetMana(amount); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetManaRatio); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetManaRatio) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetManaRatio(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetManaRatio(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetAC); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetAC) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetAC(THIS)"); + { + Mob * THIS; + uint16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetAC(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetATK); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetATK) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetATK(THIS)"); + { + Mob * THIS; + uint16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetATK(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetSTR); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetSTR) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetSTR(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetSTR(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetSTA); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetSTA) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetSTA(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetSTA(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetDEX); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetDEX) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetDEX(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDEX(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetAGI); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetAGI) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetAGI(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetAGI(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetINT); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetINT) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetINT(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetINT(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetWIS); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetWIS) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetWIS(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetWIS(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetCHA); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetCHA) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetCHA(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCHA(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetMR); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetMR) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetMR(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMR(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetFR); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetFR) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetFR(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetFR(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetDR); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetDR) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetDR(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDR(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetPR); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetPR) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetPR(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetPR(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetCR); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetCR) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetCR(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCR(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetCorruption); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetCorruption) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetCorruption(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCorrup(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetMaxSTR); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetMaxSTR) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetMaxSTR(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMaxSTR(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetMaxSTA); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetMaxSTA) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetMaxSTA(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMaxSTA(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetMaxDEX); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetMaxDEX) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetMaxDEX(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMaxDEX(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetMaxAGI); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetMaxAGI) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetMaxAGI(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMaxAGI(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetMaxINT); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetMaxINT) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetMaxINT(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMaxINT(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetMaxWIS); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetMaxWIS) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetMaxWIS(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMaxWIS(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetMaxCHA); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetMaxCHA) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetMaxCHA(THIS)"); + { + Mob * THIS; + int16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMaxCHA(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetActSpellRange); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetActSpellRange) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Mob::GetActSpellRange(THIS, spell_id, range)"); + { + Mob * THIS; + float RETVAL; + dXSTARG; + uint16 spell_id = (uint16)SvUV(ST(1)); + float range = (float)SvNV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetActSpellRange(spell_id, range); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetActSpellDamage); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetActSpellDamage) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Mob::GetActSpellDamage(THIS, spell_id, value)"); + { + Mob * THIS; + int32 RETVAL; + dXSTARG; + uint16 spell_id = (uint16)SvUV(ST(1)); + int32 value = (int32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetActSpellDamage(spell_id, value); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetActSpellHealing); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetActSpellHealing) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Mob::GetActSpellHealing(THIS, spell_id, value)"); + { + Mob * THIS; + int32 RETVAL; + dXSTARG; + uint16 spell_id = (uint16)SvUV(ST(1)); + int32 value = (int32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetActSpellHealing(spell_id, value); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetActSpellCost); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetActSpellCost) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Mob::GetActSpellCost(THIS, spell_id, cost)"); + { + Mob * THIS; + int32 RETVAL; + dXSTARG; + uint16 spell_id = (uint16)SvUV(ST(1)); + int32 cost = (int32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetActSpellCost(spell_id, cost); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetActSpellDuration); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetActSpellDuration) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Mob::GetActSpellDuration(THIS, spell_id, duration)"); + { + Mob * THIS; + int32 RETVAL; + dXSTARG; + uint16 spell_id = (uint16)SvUV(ST(1)); + int32 duration = (int32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetActSpellDuration(spell_id, duration); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetActSpellCasttime); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetActSpellCasttime) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Mob::GetActSpellCasttime(THIS, spell_id, casttime)"); + { + Mob * THIS; + int32 RETVAL; + dXSTARG; + uint16 spell_id = (uint16)SvUV(ST(1)); + int32 casttime = (int32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetActSpellCasttime(spell_id, casttime); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_ResistSpell); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_ResistSpell) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: Mob::ResistSpell(THIS, ressit_type, spell_id, caster)"); + { + Mob * THIS; + double RETVAL; + dXSTARG; + uint8 ressit_type = (uint8)SvUV(ST(1)); + uint16 spell_id = (uint16)SvUV(ST(2)); + Mob * caster; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(3), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(3))); + caster = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "caster is not of type Mob"); + if(caster == NULL) + Perl_croak(aTHX_ "caster is NULL, avoiding crash."); + + RETVAL = THIS->ResistSpell(ressit_type, spell_id, caster); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetSpecializeSkillValue); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetSpecializeSkillValue) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetSpecializeSkillValue(THIS, spell_id)"); + { + Mob * THIS; + uint16 RETVAL; + dXSTARG; + uint16 spell_id = (uint16)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetSpecializeSkillValue(spell_id); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetNPCTypeID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetNPCTypeID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetNPCTypeID(THIS)"); + { + Mob * THIS; + uint32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetNPCTypeID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_IsTargeted); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsTargeted) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsTargeted(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsTargeted(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_GetX); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetX) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetX(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetX(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetY); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetY) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetY(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetY(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetZ); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetZ) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetZ(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetZ(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetHeading); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHeading) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetHeading(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetHeading(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + + +XS(XS_Mob_GetWaypointX); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetWaypointX) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetWaypointX(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCWPX(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetWaypointY); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetWaypointY) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetWaypointY(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCWPY(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetWaypointZ); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetWaypointZ) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetWaypointZ(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCWPZ(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetWaypointH); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetWaypointH) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetWaypointH(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCWPH(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetWaypointPause); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetWaypointPause) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetWaypointPause(THIS)"); + { + Mob * THIS; + uint32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCWPP(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetWaypointID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetWaypointID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetWaypointID(THIS)"); + { + Mob * THIS; + uint32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCWP(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SetCurrentWP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetCurrentWP) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetCurrentWP(THIS, waypoint)"); + { + Mob * THIS; + uint16 waypoint = (uint16)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetCurrentWP(waypoint); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetSize); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetSize) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetSize(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetSize(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SetFollowID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetFollowID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetFollowID(THIS, id)"); + { + Mob * THIS; + uint32 id = (uint32)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetFollowID(id); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetFollowID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetFollowID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetFollowID(THIS)"); + { + Mob * THIS; + uint32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetFollowID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_Message); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Message) +{ + dXSARGS; + if (items < 3) + Perl_croak(aTHX_ "Usage: Mob::Message(THIS, type, message, ...)"); + { + Mob * THIS; + uint32 type = (uint32)SvUV(ST(1)); + char* message = (char *)SvPV_nolen(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Message(type, message); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_Message_StringID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Message_StringID) +{ + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: Mob::Message_StringID(THIS, type, string_id, distance= 0)"); + { + Mob * THIS; + uint32 type = (uint32)SvUV(ST(1)); + uint32 string_id = (uint32)SvUV(ST(2)); + uint32 distance; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 4) + distance = 0; + else { + distance = (uint32)SvUV(ST(3)); + } + + THIS->Message_StringID(type, string_id, distance); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_Say); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Say) +{ + dXSARGS; + if (items < 2) + Perl_croak(aTHX_ "Usage: Mob::Say(THIS, format, ...)"); + { + Mob * THIS; + char * format = (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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Say(format); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_Shout); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Shout) +{ + dXSARGS; + if (items < 2) + Perl_croak(aTHX_ "Usage: Mob::Shout(THIS, format, ...)"); + { + Mob * THIS; + char * format = (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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Shout(format); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_Emote); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Emote) +{ + dXSARGS; + if (items < 2) + Perl_croak(aTHX_ "Usage: Mob::Emote(THIS, format, ...)"); + { + Mob * THIS; + char * format = (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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Emote(format); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_InterruptSpell); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_InterruptSpell) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Mob::InterruptSpell(THIS, spellid= 0xFFFF)"); + { + Mob * THIS; + uint16 spellid; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 2) + spellid = 0xFFFF; + else { + spellid = (uint16)SvUV(ST(1)); + } + + THIS->InterruptSpell(spellid); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_CastSpell); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CastSpell) +{ + dXSARGS; + if (items < 3 || items > 6) + Perl_croak(aTHX_ "Usage: Mob::CastSpell(THIS, spell_id, target_id, slot= 10, casttime= -1, mana_cost= -1)"); + { + Mob * THIS; + uint16 spell_id = (uint16)SvUV(ST(1)); + uint16 target_id = (uint16)SvUV(ST(2)); + uint16 slot; + int32 casttime; + int32 mana_cost; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 4) + slot = 10; + else { + slot = (uint16)SvUV(ST(3)); + } + + if (items < 5) + casttime = -1; + else { + casttime = (int32)SvIV(ST(4)); + } + + if (items < 6) + mana_cost = -1; + else { + mana_cost = (int32)SvIV(ST(5)); + } + + THIS->CastSpell(spell_id, target_id, slot, casttime, mana_cost); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SpellFinished); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SpellFinished) +{ + dXSARGS; + if (items < 2 || items > 4) + Perl_croak(aTHX_ "Usage: Mob::SpellFinished(spell_id, spell_target = this, mana_cost = 0)"); + { + Mob * THIS; + uint16 spell_id = (uint16)SvUV(ST(1)); + Mob * spell_target; + uint16 mana_cost = 0; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + spell_target = THIS; + + if (items > 2) + { + if (sv_derived_from(ST(2), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(2))); + spell_target = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "spell_target is not of type Mob"); + if(spell_target == NULL) + Perl_croak(aTHX_ "spell_target is NULL, avoiding crash."); + + } + + if (items > 3) + mana_cost = (uint16)SvUV(ST(3)); + + THIS->SpellFinished(spell_id, spell_target, 10, mana_cost, -1, spells[spell_id].ResistDiff); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_IsImmuneToSpell); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsImmuneToSpell) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Mob::IsImmuneToSpell(THIS, spell_id, caster)"); + { + Mob * THIS; + bool RETVAL; + uint16 spell_id = (uint16)SvUV(ST(1)); + Mob * caster; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(2), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(2))); + caster = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "caster is not of type Mob"); + if(caster == NULL) + Perl_croak(aTHX_ "caster is NULL, avoiding crash."); + + RETVAL = THIS->IsImmuneToSpell(spell_id, caster); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_BuffFadeBySpellID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_BuffFadeBySpellID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::BuffFadeBySpellID(THIS, spell_id)"); + { + Mob * THIS; + uint16 spell_id = (uint16)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->BuffFadeBySpellID(spell_id); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_BuffFadeByEffect); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_BuffFadeByEffect) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Mob::BuffFadeByEffect(THIS, effectid, skipslot= -1)"); + { + Mob * THIS; + int effectid = (int)SvIV(ST(1)); + int skipslot; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 3) + skipslot = -1; + else { + skipslot = (int)SvIV(ST(2)); + } + + THIS->BuffFadeByEffect(effectid, skipslot); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_BuffFadeAll); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_BuffFadeAll) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::BuffFadeAll(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->BuffFadeAll(); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_BuffFadeBySlot); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_BuffFadeBySlot) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Mob::BuffFadeBySlot(THIS, slot, iRecalcBonuses= true)"); + { + Mob * THIS; + int slot = (int)SvIV(ST(1)); + bool iRecalcBonuses; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 3) + iRecalcBonuses = true; + else { + iRecalcBonuses = (bool)SvTRUE(ST(2)); + } + + THIS->BuffFadeBySlot(slot, iRecalcBonuses); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_CanBuffStack); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CanBuffStack) +{ + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: Mob::CanBuffStack(THIS, spellid, caster_level, iFailIfOverwrite= false)"); + { + Mob * THIS; + int RETVAL; + dXSTARG; + uint16 spellid = (uint16)SvUV(ST(1)); + uint8 caster_level = (uint8)SvUV(ST(2)); + bool iFailIfOverwrite; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 4) + iFailIfOverwrite = false; + else { + iFailIfOverwrite = (bool)SvTRUE(ST(3)); + } + + RETVAL = THIS->CanBuffStack(spellid, caster_level, iFailIfOverwrite); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_IsCasting); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsCasting) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsCasting(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsCasting(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_CastingSpellID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CastingSpellID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::CastingSpellID(THIS)"); + { + Mob * THIS; + uint16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CastingSpellID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SetAppearance); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetAppearance) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Mob::SetAppearance(THIS, app, iIgnoreSelf= true)"); + { + Mob * THIS; + EmuAppearance app = (EmuAppearance)SvUV(ST(1)); + bool iIgnoreSelf; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 3) + iIgnoreSelf = true; + else { + iIgnoreSelf = (bool)SvTRUE(ST(2)); + } + + THIS->SetAppearance(app, iIgnoreSelf); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetAppearance); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetAppearance) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetAppearance(THIS)"); + { + Mob * THIS; + EmuAppearance RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetAppearance(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetRunAnimSpeed); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetRunAnimSpeed) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetRunAnimSpeed(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetRunAnimSpeed(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SetRunAnimSpeed); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetRunAnimSpeed) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetRunAnimSpeed(THIS, in)"); + { + Mob * THIS; + int8 in = (int8)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetRunAnimSpeed(in); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetPetID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetPetID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetPetID(THIS, NewPetID)"); + { + Mob * THIS; + uint16 NewPetID = (uint16)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetPetID(NewPetID); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetPetID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetPetID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetPetID(THIS)"); + { + Mob * THIS; + uint16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetPetID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SetOwnerID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetOwnerID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetOwnerID(THIS, NewOwnerID)"); + { + Mob * THIS; + uint16 NewOwnerID = (uint16)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetOwnerID(NewOwnerID); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetOwnerID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetOwnerID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetOwnerID(THIS)"); + { + Mob * THIS; + uint16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetOwnerID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetPetType); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetPetType) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetPetType(THIS)"); + { + Mob * THIS; + uint16 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetPetType(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetBodyType); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetBodyType) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetBodyType(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetBodyType(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_Stun); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Stun) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::Stun(THIS, duration)"); + { + Mob * THIS; + int duration = (int)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Stun(duration); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_Spin); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Spin) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::Spin(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Spin(); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_Kill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Kill) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::Kill(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Kill(); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetInvul); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetInvul) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetInvul(THIS, invul)"); + { + Mob * THIS; + bool invul = (bool)SvTRUE(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetInvul(invul); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetInvul); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetInvul) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetInvul(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetInvul(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_SetExtraHaste); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetExtraHaste) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetExtraHaste(THIS, Haste)"); + { + Mob * THIS; + int Haste = (int)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetExtraHaste(Haste); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetHaste); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHaste) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetHaste(THIS)"); + { + Mob * THIS; + int RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetHaste(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetMonkHandToHandDamage); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetMonkHandToHandDamage) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetMonkHandToHandDamage(THIS)"); + { + Mob * THIS; + int RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMonkHandToHandDamage(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_CanThisClassDoubleAttack); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CanThisClassDoubleAttack) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::CanThisClassDoubleAttack(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CanThisClassDoubleAttack(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_CanThisClassDualWield); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CanThisClassDualWield) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::CanThisClassDualWield(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CanThisClassDualWield(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_CanThisClassRiposte); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CanThisClassRiposte) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::CanThisClassRiposte(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CanThisClassRiposte(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_CanThisClassDodge); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CanThisClassDodge) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::CanThisClassDodge(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CanThisClassDodge(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_CanThisClassParry); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CanThisClassParry) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::CanThisClassParry(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CanThisClassParry(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_GetMonkHandToHandDelay); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetMonkHandToHandDelay) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetMonkHandToHandDelay(THIS)"); + { + Mob * THIS; + int RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMonkHandToHandDelay(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetClassLevelFactor); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetClassLevelFactor) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetClassLevelFactor(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetClassLevelFactor(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_Mesmerize); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Mesmerize) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::Mesmerize(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Mesmerize(); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_IsMezzed); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsMezzed) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsMezzed(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsMezzed(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_IsStunned); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsStunned) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsStunned(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsStunned(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + + +XS(XS_Mob_StartEnrage); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_StartEnrage) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::StartEnrage(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->StartEnrage(); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_IsEnraged); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsEnraged) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsEnraged(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsEnraged(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_GetReverseFactionCon); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetReverseFactionCon) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetReverseFactionCon(THIS, iOther)"); + { + Mob * THIS; + FACTION_VALUE RETVAL; + dXSTARG; + Mob* iOther; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + iOther = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "iOther is not of type Mob"); + if(iOther == NULL) + Perl_croak(aTHX_ "iOther is NULL, avoiding crash."); + + RETVAL = THIS->GetReverseFactionCon(iOther); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_IsAIControlled); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsAIControlled) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsAIControlled(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsAIControlled(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_GetAggroRange); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetAggroRange) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetAggroRange(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetAggroRange(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetAssistRange); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetAssistRange) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetAssistRange(THIS)"); + { + Mob * THIS; + float RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetAssistRange(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SetPetOrder); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetPetOrder) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetPetOrder(THIS, i)"); + { + Mob * THIS; + Mob::eStandingPetOrder i = (Mob::eStandingPetOrder)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetPetOrder(i); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetPetOrder); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetPetOrder) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetPetOrder(THIS)"); + { + Mob * THIS; + Mob::eStandingPetOrder RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetPetOrder(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_IsRoamer); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsRoamer) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsRoamer(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsRoamer(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_IsRooted); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsRooted) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsRooted(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsRooted(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_AddToHateList); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_AddToHateList) +{ + dXSARGS; + if (items < 2 || items > 7) + Perl_croak(aTHX_ "Usage: Mob::AddToHateList(THIS, other, hate= 0, damage= 0, iYellForHelp= true, bFrenzy= false, iBuffTic= false)"); + { + Mob * THIS; + Mob* other; + int32 hate; + int32 damage; + bool iYellForHelp; + bool bFrenzy; + bool iBuffTic; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + + if (items < 3) + hate = 0; + else { + hate = (int32)SvIV(ST(2)); + } + + if (items < 4) + damage = 0; + else { + damage = (int32)SvIV(ST(3)); + } + + if (items < 5) + iYellForHelp = true; + else { + iYellForHelp = (bool)SvTRUE(ST(4)); + } + + if (items < 6) + bFrenzy = false; + else { + bFrenzy = (bool)SvTRUE(ST(5)); + } + + if (items < 7) + iBuffTic = false; + else { + iBuffTic = (bool)SvTRUE(ST(6)); + } + + THIS->AddToHateList(other, hate, damage, iYellForHelp, bFrenzy, iBuffTic); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetHate); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetHate) +{ + dXSARGS; + if (items < 2 || items > 4) + Perl_croak(aTHX_ "Usage: Mob::SetHate(THIS, other, hate= 0, damage= 0)"); + { + Mob * THIS; + Mob* other; + int32 hate; + int32 damage; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + + if (items < 3) + hate = 0; + else { + hate = (int32)SvIV(ST(2)); + } + + if (items < 4) + damage = 0; + else { + damage = (int32)SvIV(ST(3)); + } + + THIS->SetHate(other, hate, damage); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetHateAmount); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHateAmount) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Mob::GetHateAmount(THIS, tmob, is_dam= false)"); + { + Mob * THIS; + uint32 RETVAL; + dXSTARG; + Mob* tmob; + bool is_dam; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + tmob = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "tmob is not of type Mob"); + if(tmob == NULL) + Perl_croak(aTHX_ "tmob is NULL, avoiding crash."); + + if (items < 3) + is_dam = false; + else { + is_dam = (bool)SvTRUE(ST(2)); + } + + RETVAL = THIS->GetHateAmount(tmob, is_dam); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetDamageAmount); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetDamageAmount) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetDamageAmount(THIS, tmob)"); + { + Mob * THIS; + uint32 RETVAL; + dXSTARG; + Mob* tmob; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + tmob = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "tmob is not of type Mob"); + if(tmob == NULL) + Perl_croak(aTHX_ "tmob is NULL, avoiding crash."); + + RETVAL = THIS->GetDamageAmount(tmob); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetHateTop); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHateTop) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetHateTop(THIS)"); + { + Mob * THIS; + Mob * RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetHateTop(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Mob", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetHateDamageTop); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHateDamageTop) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetHateDamageTop(THIS, other)"); + { + Mob * THIS; + Mob * RETVAL; + Mob* other; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + + RETVAL = THIS->GetHateDamageTop(other); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Mob", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetHateRandom); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHateRandom) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetHateRandom(THIS)"); + { + Mob * THIS; + Mob * RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetHateRandom(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Mob", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_IsEngaged); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsEngaged) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsEngaged(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsEngaged(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_HateSummon); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_HateSummon) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::HateSummon(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->HateSummon(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_FaceTarget); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_FaceTarget) +{ + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: Mob::FaceTarget(THIS, MobToFace= 0)"); + { + Mob * THIS; + Mob* MobToFace; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 2) + MobToFace = 0; + else { + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + MobToFace = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "MobToFace is not of type Mob"); + if(MobToFace == NULL) + Perl_croak(aTHX_ "MobToFace is NULL, avoiding crash."); + } + + THIS->FaceTarget(MobToFace); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetHeading); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetHeading) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetHeading(THIS, iHeading)"); + { + Mob * THIS; + float iHeading = (float)SvNV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetHeading(iHeading); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_WipeHateList); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_WipeHateList) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::WipeHateList(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->WipeHateList(); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_CheckAggro); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CheckAggro) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::CheckAggro(THIS, other)"); + { + Mob * THIS; + bool RETVAL; + Mob* other; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + + RETVAL = THIS->CheckAggro(other); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_CalculateHeadingToTarget); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CalculateHeadingToTarget) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Mob::CalculateHeadingToTarget(THIS, in_x, in_y)"); + { + Mob * THIS; + int8 RETVAL; + dXSTARG; + float in_x = (float)SvNV(ST(1)); + float in_y = (float)SvNV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CalculateHeadingToTarget(in_x, in_y); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_CalculateNewPosition); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CalculateNewPosition) +{ + dXSARGS; + if (items < 5 || items > 6) + Perl_croak(aTHX_ "Usage: Mob::CalculateNewPosition(THIS, x, y, z, speed, checkZ= false)"); + { + Mob * THIS; + bool RETVAL; + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(ST(3)); + float speed = (float)SvNV(ST(4)); + bool checkZ; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 6) + checkZ = false; + else { + checkZ = (bool)SvTRUE(ST(5)); + } + + RETVAL = THIS->CalculateNewPosition(x, y, z, speed, checkZ); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_CalculateNewPosition2); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CalculateNewPosition2) +{ + dXSARGS; + if (items < 5 || items > 6) + Perl_croak(aTHX_ "Usage: Mob::CalculateNewPosition2(THIS, x, y, z, speed, checkZ= false)"); + { + Mob * THIS; + bool RETVAL; + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(ST(3)); + float speed = (float)SvNV(ST(4)); + bool checkZ; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 6) + checkZ = false; + else { + checkZ = (bool)SvTRUE(ST(5)); + } + + RETVAL = THIS->CalculateNewPosition2(x, y, z, speed, checkZ); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_CalculateDistance); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CalculateDistance) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: Mob::CalculateDistance(THIS, x, y, z)"); + { + Mob * THIS; + float RETVAL; + dXSTARG; + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CalculateDistance(x, y, z); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SendTo); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SendTo) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: Mob::SendTo(THIS, new_x, new_y, new_z)"); + { + Mob * THIS; + float new_x = (float)SvNV(ST(1)); + float new_y = (float)SvNV(ST(2)); + float new_z = (float)SvNV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendTo(new_x, new_y, new_z); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SendToFixZ); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SendToFixZ) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: Mob::SendToFixZ(THIS, new_x, new_y, new_z)"); + { + Mob * THIS; + float new_x = (float)SvNV(ST(1)); + float new_y = (float)SvNV(ST(2)); + float new_z = (float)SvNV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendToFixZ(new_x, new_y, new_z); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_NPCSpecialAttacks); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_NPCSpecialAttacks) +{ + dXSARGS; + if (items < 3 || items > 5) + Perl_croak(aTHX_ "Usage: Mob::NPCSpecialAttacks(THIS, parse, permtag, [reset], [remove])"); + { + Mob * THIS; + char* parse = (char *)SvPV_nolen(ST(1)); + int permtag = (int)SvIV(ST(2)); + bool reset = items == 4 ? (bool)SvTRUE(ST(3)) : true; + bool remove = items == 5 ? (bool)SvTRUE(ST(4)) : false; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->NPCSpecialAttacks(parse, permtag, reset, remove); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_DontHealMeBefore); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_DontHealMeBefore) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::DontHealMeBefore(THIS)"); + { + Mob * THIS; + uint32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->DontHealMeBefore(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_DontBuffMeBefore); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_DontBuffMeBefore) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::DontBuffMeBefore(THIS)"); + { + Mob * THIS; + uint32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->DontBuffMeBefore(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_DontDotMeBefore); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_DontDotMeBefore) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::DontDotMeBefore(THIS)"); + { + Mob * THIS; + uint32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->DontDotMeBefore(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_DontRootMeBefore); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_DontRootMeBefore) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::DontRootMeBefore(THIS)"); + { + Mob * THIS; + uint32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->DontRootMeBefore(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_DontSnareMeBefore); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_DontSnareMeBefore) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::DontSnareMeBefore(THIS)"); + { + Mob * THIS; + uint32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->DontSnareMeBefore(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetResist); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetResist) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetResist(THIS, type)"); + { + Mob * THIS; + int16 RETVAL; + dXSTARG; + uint8 type = (uint8)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetResist(type); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetShieldTarget); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetShieldTarget) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetShieldTarget(THIS)"); + { + Mob * THIS; + Mob * RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetShieldTarget(); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Mob", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SetShieldTarget); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetShieldTarget) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetShieldTarget(THIS, mob)"); + { + Mob * THIS; + Mob* mob; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + mob = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "mob is not of type Mob"); + if(mob == NULL) + Perl_croak(aTHX_ "mob is NULL, avoiding crash."); + + THIS->SetShieldTarget(mob); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_Charmed); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_Charmed) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::Charmed(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->Charmed(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_GetLevelHP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetLevelHP) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetLevelHP(THIS, tlevel)"); + { + Mob * THIS; + uint32 RETVAL; + dXSTARG; + uint8 tlevel = (uint8)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetLevelHP(tlevel); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetZoneID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetZoneID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetZoneID(THIS)"); + { + Mob * THIS; + uint32 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetZoneID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_CheckAggroAmount); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CheckAggroAmount) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::CheckAggroAmount(THIS, spellid)"); + { + Mob * THIS; + uint16 RETVAL; + dXSTARG; + uint16 spellid = (uint16)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CheckAggroAmount(spellid); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_CheckHealAggroAmount); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CheckHealAggroAmount) +{ + dXSARGS; + if (items != 2 && items != 3) + Perl_croak(aTHX_ "Usage: Mob::CheckHealAggroAmount(THIS, spellid, possible_heal_amt)"); + { + Mob * THIS; + uint16 RETVAL; + dXSTARG; + uint16 spellid = (uint16)SvUV(ST(1)); + uint32 possible = 0; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if(items == 3) + { + possible = (uint32)SvUV(ST(2)); + } + + RETVAL = THIS->CheckHealAggroAmount(spellid, possible); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetAA); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetAA) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetAA(THIS, aa_id)"); + { + Mob * THIS; + uint32 RETVAL; + dXSTARG; + uint32 aa_id = (uint32)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetAA(aa_id); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_DivineAura); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_DivineAura) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::DivineAura(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->DivineAura(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_AddFeignMemory); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_AddFeignMemory) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::AddFeignMemory(THIS, attacker)"); + { + Mob * THIS; + Client* attacker; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + attacker = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "attacker is not of type Client"); + if(attacker == NULL) + Perl_croak(aTHX_ "attacker is NULL, avoiding crash."); + + THIS->AddFeignMemory(attacker); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_RemoveFromFeignMemory); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_RemoveFromFeignMemory) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::RemoveFromFeignMemory(THIS, attacker)"); + { + Mob * THIS; + Client* attacker; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + attacker = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "attacker is not of type Client"); + if(attacker == NULL) + Perl_croak(aTHX_ "attacker is NULL, avoiding crash."); + + THIS->RemoveFromFeignMemory(attacker); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_ClearFeignMemory); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_ClearFeignMemory) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::ClearFeignMemory(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->ClearFeignMemory(); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetOOCRegen); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetOOCRegen) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetOOCRegen(THIS, newoocregen)"); + { + Mob * THIS; + int32 newoocregen = (int32)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetOOCRegen(newoocregen); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetEntityVariable); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetEntityVariable) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetEntityVariable(THIS, id)"); + { + Mob * THIS; + Const_char * id = SvPV_nolen(ST(1)); + Const_char * RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetEntityVariable(id); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Mob_EntityVariableExists); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_EntityVariableExists) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::EntityVariableExists(THIS, id)"); + { + Mob * THIS; + Const_char * id = SvPV_nolen(ST(1)); + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->EntityVariableExists(id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_SetEntityVariable); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetEntityVariable) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Mob::SetEntityVariable(THIS, id, var)"); + { + Mob * THIS; + Const_char * id = SvPV_nolen(ST(1)); + const char * var = (const char *)SvPV_nolen(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetEntityVariable(id, var); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetHateList); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetHateList) +{ + dXSARGS; + int num_entries = 0; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetHateList(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + std::list hate_list; + THIS->GetHateList(hate_list); + std::list::iterator iter = hate_list.begin(); + + while(iter != hate_list.end()) + { + tHateEntry *entry = (*iter); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "HateEntry", (void*)entry); + XPUSHs(ST(0)); + num_entries++; + iter++; + } + } + XSRETURN(num_entries); +} + +/* + dXSARGS; + int num_mobs = 0; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::GetCorpseList(THIS)"); + { + EntityList *THIS; + + if (sv_derived_from(ST(0), "EntityList")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(EntityList *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type EntityList"); + + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + std::list corpse_list = entity_list.GetCorpseList(); + std::list::iterator iter = corpse_list.begin(); + + while(iter != corpse_list.end()) + { + Corpse *entry = (*iter); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Corpse", (void*)entry); + XPUSHs(ST(0)); + num_mobs++; + iter++; + } + } + XSRETURN(num_mobs); +*/ + +XS(XS_Mob_SignalClient); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SignalClient) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Mob::SignalClient(THIS, client, data)"); + { + Mob * THIS; + Client* client = NULL; + uint32 data = (uint32)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + client = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "client is not of type Client"); + if(client == NULL) + Perl_croak(aTHX_ "client is NULL, avoiding crash."); + + client->Signal(data); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_CombatRange); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CombatRange) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::CombatRange(THIS, target)"); + { + Mob * THIS; + Mob * target = NULL; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + target = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "target is not of type Mob"); + if(target == NULL) + Perl_croak(aTHX_ "target is NULL, avoiding crash."); + + RETVAL = THIS->CombatRange(target); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_DoSpecialAttackDamage); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_DoSpecialAttackDamage) +{ + dXSARGS; + if (items < 4 || items > 6) + Perl_croak(aTHX_ "Usage: Mob::DoSpecialAttackDamage(THIS, target, skill, max_damage, min_damage = 1, hate_override = -1)"); + { + Mob * THIS; + Mob* target; + SkillType attack_skill = (SkillType)SvUV(ST(2)); + int32 max_damage = (int32)SvIV(ST(3)); + int32 min_damage = 1; + int32 hate_override = -11; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + target = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "target is not of type Mob"); + if(target == NULL) + Perl_croak(aTHX_ "target is NULL, avoiding crash."); + + if (items > 4) + { + min_damage = (int32)SvIV(ST(4)); + } + + if (items == 6) + { + hate_override = (int32)SvIV(ST(5)); + } + + THIS->DoSpecialAttackDamage(target, attack_skill, max_damage, min_damage, hate_override); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_CheckLoS); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CheckLoS) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::CheckLoS(THIS, mob)"); + { + Mob * THIS; + Mob* mob; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + mob = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "mob is not of type Mob"); + if(mob == NULL) + Perl_croak(aTHX_ "mob is NULL, avoiding crash."); + + RETVAL = THIS->CheckLosFN(mob); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_CheckLoSToLoc); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CheckLoSToLoc) +{ + dXSARGS; + if (items != 4 && items != 5) + Perl_croak(aTHX_ "Usage: Mob::CheckLoSToLoc(THIS, loc_x, loc_y, loc_z, mob_size)"); + { + Mob * THIS; + float loc_x = (float)SvNV(ST(1)); + float loc_y = (float)SvNV(ST(2)); + float loc_z = (float)SvNV(ST(3)); + float mob_size; + bool RETVAL; + + if (items == 5) { + mob_size = (float)SvNV(ST(4)); + } + else { + mob_size = 6; + } + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CheckLosFN(loc_x, loc_y, loc_z, mob_size); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_FindGroundZ); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_FindGroundZ) +{ + dXSARGS; + if (items != 3 && items != 4) + Perl_croak(aTHX_ "Usage: Mob::FindGroundZ(THIS, new_x, new_y, z_offset)"); + { + Mob * THIS; + float new_x = (float)SvNV(ST(1)); + float new_y = (float)SvNV(ST(2)); + float z_offset; + float RETVAL; + dXSTARG; + + if (items == 4) { + z_offset = (float)SvNV(ST(3)); + } + else { + z_offset = 10; + } + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetGroundZ(new_x, new_y, z_offset); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_ProjectileAnim); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_ProjectileAnim) +{ + dXSARGS; + if (items < 3 || items > 8) + Perl_croak(aTHX_ "Usage: Mob::ProjectileAnim(THIS, mob, item_id, IsArrow?, speed, angle, tilt, arc)"); + + { + Mob * THIS; + Mob* mob; + uint16 item_id = (uint16)SvUV(ST(2)); + bool IsArrow = false; + float speed = 0; + float angle = 0; + float tilt = 0; + float arc = 0; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + mob = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "mob is not of type Mob"); + if(mob == NULL) + Perl_croak(aTHX_ "mob is NULL, avoiding crash."); + + if(items > 3){ + IsArrow = (bool)SvTRUE(ST(3)); + } + if(items > 4){ + speed = (float)SvNV(ST(4)); + } + if(items > 5){ + angle = (float)SvNV(ST(5)); + } + if(items > 6){ + tilt = (float)SvNV(ST(6)); + } + if(items > 7){ + arc = (float)SvNV(ST(7)); + } + + THIS->ProjectileAnimation(mob, item_id, IsArrow, speed, angle, tilt, arc); + + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_HasNPCSpecialAtk); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_HasNPCSpecialAtk) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::HasNPCSpecialAtk(THIS, parse)"); + { + Mob * THIS; + char* parse = (char *)SvPV_nolen(ST(1)); + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->HasNPCSpecialAtk(parse); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_SendAppearanceEffect); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SendAppearanceEffect) +{ + dXSARGS; + if (items < 2 || items > 7) + Perl_croak(aTHX_ "Usage: Mob::SendAppearanceEffect(THIS, parm1, parm2, parm3, parm4, parm5, singleclient)"); + { + Mob * THIS; + int32 parm1 = (int32)SvIV(ST(1)); + int32 parm2 = 0; + int32 parm3 = 0; + int32 parm4 = 0; + int32 parm5 = 0; + Client* client = NULL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items > 2) { parm2 = (int32)SvIV(ST(2)); } + if (items > 3) { parm3 = (int32)SvIV(ST(3)); } + if (items > 4) { parm4 = (int32)SvIV(ST(4)); } + if (items > 5) { parm5 = (int32)SvIV(ST(5)); } + if (items > 6) { + if (sv_derived_from(ST(6), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(6))); + client = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "client is not of type Client"); + if(client == NULL) + Perl_croak(aTHX_ "client is NULL, avoiding crash."); + } + + THIS->SendAppearanceEffect(parm1, parm2, parm3, parm4, parm5, client); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_QuestReward); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_QuestReward) +{ + dXSARGS; + if (items < 1 || items > 5) + Perl_croak(aTHX_ "Usage: Mob::QuestReward(THIS, client, silver, gold, platinum)"); + { + Mob * THIS; + Client* client = NULL; + int32 silver = 0; + int32 gold = 0; + int32 platinum = 0; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items > 1) { + if (sv_derived_from(ST(1), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + client = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "client is not of type Client"); + if(client == NULL) + Perl_croak(aTHX_ "client is NULL, avoiding crash."); + } + if (items > 2) { silver = (int32)SvIV(ST(2)); } + if (items > 3) { gold = (int32)SvIV(ST(3)); } + if (items > 4) { platinum = (int32)SvIV(ST(4)); } + + THIS->QuestReward(client, silver, gold, platinum); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetFlyMode); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetFlyMode) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetFlyMode(THIS, 0|1|2|3)"); + { + Mob * THIS; + uint8 flymode = (uint8)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetFlyMode(flymode); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetTexture); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetTexture) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetTexture(THIS, texture)"); + { + Mob * THIS; + int32 texture = (int32)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendIllusionPacket(THIS->GetRace(), 0xFF, texture); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetRace); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetRace) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetRace(THIS, race)"); + { + Mob * THIS; + int32 race = (int32)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendIllusionPacket(race); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetGender); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetGender) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetGender(THIS, gender)"); + { + Mob * THIS; + int32 gender = (int32)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendIllusionPacket(THIS->GetRace(),gender); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SendIllusion); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SendIllusion) +{ + dXSARGS; + if (items < 2 || items > 14) + Perl_croak(aTHX_ "Usage: Mob::SendIllusion(THIS,race,gender,texture,helmtexture,face,hairstyle,haircolor,beard,beardcolor,drakkin_heritage,drakkin_tattoo,drakkin_details,size)"); + { + Mob * THIS; + uint16 race = (uint16)SvIV(ST(1)); + uint8 gender = 0xFF; + uint8 texture = 0xFF; + uint8 helmtexture = 0xFF; + uint8 face = 0xFF; + uint8 hairstyle = 0xFF; + uint8 haircolor = 0xFF; + uint8 beard = 0xFF; + uint8 beardcolor = 0xFF; + uint32 drakkin_heritage = 0xFFFFFFFF; + uint32 drakkin_tattoo = 0xFFFFFFFF; + uint32 drakkin_details = 0xFFFFFFFF; + float size = 0xFFFFFFFF; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if(items > 2) {gender = (uint8)SvIV(ST(2));} + if(items > 3) {texture = (uint8)SvIV(ST(3));} + if(items > 4) {helmtexture = (uint8)SvIV(ST(4));} + if(items > 5) {face = (uint8)SvIV(ST(5));} + if(items > 6) {hairstyle = (uint8)SvIV(ST(6));} + if(items > 7) {haircolor = (uint8)SvIV(ST(7));} + if(items > 8) {beard = (uint8)SvIV(ST(8));} + if(items > 9) {beardcolor = (uint8)SvIV(ST(9));} + if(items > 10) {drakkin_heritage = (uint32)SvIV(ST(10));} + if(items > 11) {drakkin_tattoo = (uint32)SvIV(ST(11));} + if(items > 12) {drakkin_details = (uint32)SvIV(ST(12));} + if(items > 13) {size = (float)SvNV(ST(13));} + + THIS->SendIllusionPacket(race,gender,texture,helmtexture,haircolor,beardcolor,0xFF,0xFF, + hairstyle,face,beard,0xFF,drakkin_heritage,drakkin_tattoo,drakkin_details,size); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_CameraEffect); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_CameraEffect) +{ + dXSARGS; + if (items < 2 || items > 5) + Perl_croak(aTHX_ "Usage: Mob::CameraEffect(THIS, duration, intensity, singleclient, global)"); + { + Mob * THIS; + uint32 duration = (uint32)SvUV(ST(1)); + uint32 intensity = 0; + Client* client = NULL; + bool global = false; + bool nullcli = false; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items > 2) { intensity = (uint32)SvUV(ST(2)); } + if (items > 3) { + if (sv_derived_from(ST(3), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(3))); + client = INT2PTR(Client *,tmp); + } + else + nullcli = true; + if(client == NULL) + nullcli = true; + //Perl_croak(aTHX_ "client is NULL, avoiding crash."); + } + if (items > 4) { global = (bool)SvTRUE(ST(4)); } + + if(nullcli) + THIS->CameraEffect(duration, intensity, 0, global); + else + THIS->CameraEffect(duration, intensity, client, global); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SpellEffect); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SpellEffect) +{ + dXSARGS; + if (items < 2 || items > 8) + Perl_croak(aTHX_ "Usage: Mob::SpellEffect(THIS, effect, [duration, finish_delay, zone_wide, unk20, perm_effect, client])"); + { + Mob * THIS; + uint32 effect = (uint32)SvUV(ST(1)); + uint32 duration = 5000; + uint32 finish_delay = 0; + bool zone_wide = true; + uint32 unk20 = 3000; + bool perm_effect = false; + Client* client = NULL; + + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items > 2) { duration = (uint32)SvUV(ST(2)); } + if (items > 3) { finish_delay = (uint32)SvUV(ST(3)); } + if (items > 4) { zone_wide = (bool)SvTRUE(ST(4)); } + if (items > 5) { unk20 = (uint32)SvUV(ST(5)); } + if (items > 6) { perm_effect = (bool)SvTRUE(ST(6)); } + if (items > 7) { + if (sv_derived_from(ST(7), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(7))); + client = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "client is not of type Client"); + if(client == NULL) + Perl_croak(aTHX_ "client is NULL, avoiding crash."); + } + + + THIS->SendSpellEffect(effect, duration, finish_delay, zone_wide, unk20, perm_effect, client); + } + XSRETURN_EMPTY; +} + + +XS(XS_Mob_TempName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_TempName) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Mob::TempName(THIS, name)"); + { + Mob * THIS; + char * name = NULL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items > 1) { name = (char *)SvPV_nolen(ST(1)); } + + THIS->TempName(name); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetItemStat); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetItemStat) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Mob::GetItemStat(THIS, itemid, stat)"); + { + Mob * THIS; + uint32 RETVAL; + uint32 itemid = (uint32)SvUV(ST(1)); + Const_char * stat = (Const_char *)SvPV_nolen(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetItemStat(itemid, stat); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SetGlobal); +XS(XS_Mob_SetGlobal) +{ + dXSARGS; + if (items < 5 || items > 6) + Perl_croak(aTHX_ "Usage: SetGlobal(THIS, varname, newvalue, options, duration, other=NULL)"); + { + Mob * THIS; + char * varname = (char *)SvPV_nolen(ST(1)); + char * newvalue = (char *)SvPV_nolen(ST(2)); + int options = (int)SvIV(ST(3)); + char * duration = (char *)SvPV_nolen(ST(4)); + Mob * other = NULL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items > 5) { + if (sv_derived_from(ST(5), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(5))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + } + + THIS->SetGlobal(varname, newvalue, options, duration, other); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_TarGlobal); +XS(XS_Mob_TarGlobal) +{ + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: TarGlobal(THIS, varname, value, duration, npcid, charid, zoneid)"); + { + Mob * THIS; + char * varname = (char *)SvPV_nolen(ST(1)); + char * value = (char *)SvPV_nolen(ST(2)); + char * duration = (char *)SvPV_nolen(ST(3)); + int npcid = (int)SvIV(ST(4)); + int charid = (int)SvIV(ST(5)); + int zoneid = (int)SvIV(ST(6)); + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->TarGlobal(varname, value, duration, npcid, charid, zoneid); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_DelGlobal); +XS(XS_Mob_DelGlobal) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: DelGlobal(THIS, varname)"); + { + Mob * THIS; + char * varname = (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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->DelGlobal(varname); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetSlotTint); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetSlotTint) +{ + dXSARGS; + if (items != 5) + Perl_croak(aTHX_ "Usage: Mob::SetSlotTint(THIS, material_slot, red_tint, green_tint, blue_tint)"); + { + Mob * THIS; + uint8 material_slot = (uint8)SvIV(ST(1)); + uint8 red_tint = (uint8)SvIV(ST(2)); + uint8 green_tint = (uint8)SvIV(ST(3)); + uint8 blue_tint = (uint8)SvIV(ST(4)); + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetSlotTint(material_slot, red_tint, green_tint, blue_tint); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_WearChange); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_WearChange) +{ + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: Mob::WearChange(THIS, material_slot, texture, color)"); + { + Mob * THIS; + uint8 material_slot = (uint8)SvIV(ST(1)); + uint16 texture = (uint16)SvUV(ST(2)); + uint32 color = 0; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items > 3) { + color = (uint32)SvUV(ST(3)); + } + + THIS->WearChange(material_slot, texture, color); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_DoKnockback); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_DoKnockback) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: Mob::DoKnockback(THIS, caster, pushback, pushup)"); + { + Mob * THIS; + Mob * caster; + uint32 pushback = (uint16)SvUV(ST(2)); + uint32 pushup = (uint16)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + caster = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "caster is not of type Mob"); + if(caster == NULL) + Perl_croak(aTHX_ "caster is NULL, avoiding crash."); + + THIS->DoKnockback(caster, pushback, pushup); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_RemoveNimbusEffect); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_RemoveNimbusEffect) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::RemoveNimbusEffect(THIS, effectid)"); + { + Mob * THIS; + int32 effectid = (int32)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveNimbusEffect(effectid); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetRunning); +XS(XS_Mob_SetRunning) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetRunning(THIS, value)"); + { + Mob * THIS; + bool value = (bool)SvTRUE(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetRunning(value); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_IsRunning); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsRunning) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob:::IsRunning(THIS)"); + { + Mob * THIS; + bool RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsRunning(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_SetBodyType); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetBodyType) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Mob::SetBodyType(THIS, type, overwrite_orig = false)"); + { + Mob * THIS; + int32 type = (int32)SvIV(ST(1)); + bool overwrite_orig = false; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if(items == 3) { + overwrite_orig = (bool)SvTRUE(ST(2)); + } + + THIS->SetBodyType((bodyType)type, overwrite_orig); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetDeltas); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetDeltas) +{ + dXSARGS; + if (items != 5) + Perl_croak(aTHX_ "Usage: Mob::SetDeltas(THIS, delta_x, delta_y, delta_z, delta_h)"); + { + Mob * THIS; + float delta_x = (float)SvNV(ST(1)); + float delta_y = (float)SvNV(ST(2)); + float delta_z = (float)SvNV(ST(3)); + float delta_h = (float)SvNV(ST(4)); + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetDeltas(delta_x, delta_y, delta_z, delta_h); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetLD); +XS(XS_Mob_SetLD) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetLD(THIS, value)"); + { + Mob * THIS; + bool value = (bool)SvTRUE(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendAppearancePacket(AT_Linkdead, value); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetTargetDestSteps); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetTargetDestSteps) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetTargetDestSteps(THIS, target_steps)"); + { + Mob * THIS; + uint8 target_steps = (uint8)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetTargetDestSteps(target_steps); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetTargetable); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetTargetable) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetTargetable(THIS, on)"); + { + Mob * THIS; + bool on = (bool)SvTRUE(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetTargetable(on); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_ModSkillDmgTaken); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_ModSkillDmgTaken) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Mob::ModSkillDmgTaken(THIS, skill, value)"); + { + Mob * THIS; + SkillType skill_num = (SkillType)SvUV(ST(1)); + int16 value = (int16)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->ModSkillDmgTaken(skill_num,value); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetModSkillDmgTaken); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetModSkillDmgTaken) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetModSkillDmgTaken(THIS, skill_num)"); + { + Mob * THIS; + uint32 RETVAL; + dXSTARG; + SkillType skill_num = (SkillType)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetModSkillDmgTaken(skill_num); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetSkillDmgTaken); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetSkillDmgTaken) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetSkillDmgTaken(THIS, skill_num)"); + { + Mob * THIS; + int32 RETVAL; + dXSTARG; + SkillType skill_num = (SkillType)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetSkillDmgTaken(skill_num); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SetAllowBeneficial); +XS(XS_Mob_SetAllowBeneficial) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetAllowBeneficial(THIS, value)"); + { + Mob * THIS; + bool value = (bool)SvTRUE(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetAllowBeneficial(value); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetAllowBeneficial); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetAllowBeneficial) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetAllowBeneficial(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetAllowBeneficial(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_IsBeneficialAllowed); +XS(XS_Mob_IsBeneficialAllowed) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::IsBeneficialAllowed(THIS, target)"); + { + dXSTARG; + Mob * THIS; + Mob * target; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + target = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "target is not of type Mob"); + if(target == NULL) + Perl_croak(aTHX_ "target is NULL, avoiding crash."); + + RETVAL = THIS->IsBeneficialAllowed(target); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_ModVulnerability); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_ModVulnerability) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Mob::ModVulnerability(THIS, resist, value)"); + { + Mob * THIS; + uint8 resist = (uint8)SvIV(ST(1)); + int16 value = (int16)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->ModVulnerability(resist, value); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetModVulnerability); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetModVulnerability) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetModVulnerability(THIS, resist)"); + { + Mob * THIS; + int32 RETVAL; + dXSTARG; + uint8 resist = (uint8)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetModVulnerability(resist); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_DoMeleeSkillAttackDmg); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_DoMeleeSkillAttackDmg) +{ + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: Mob::DoMeleeSkillAttackDmg(THIS, target, weapon_damage, skill, chance_mod, focus, CanRiposte)"); + { + Mob * THIS; + Mob* target; + uint16 weapon_damage = (uint16)SvIV(ST(2)); + SkillType skill = (SkillType)SvUV(ST(3)); + int16 chance_mod = (int16)SvIV(ST(4)); + int16 focus = (int16)SvIV(ST(5)); + uint8 CanRiposte = (uint8)SvIV(ST(6)); + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + target = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "target is not of type Mob"); + if(target == NULL) + Perl_croak(aTHX_ "target is NULL, avoiding crash."); + + THIS->DoMeleeSkillAttackDmg(target, weapon_damage, skill, chance_mod, focus, CanRiposte); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_DoArcheryAttackDmg); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_DoArcheryAttackDmg) +{ + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: Mob::DoArcheryAttackDmg(THIS, target, RangeWeapon=NULL, Ammo=NULL, weapon_damage, chance_mod, focus)"); + { + Mob * THIS; + Mob* target; + ItemInst* RangeWeapon = NULL; + ItemInst* Ammo = NULL; + uint16 weapon_damage = (uint16)SvIV(ST(4)); + int16 chance_mod = (int16)SvIV(ST(5)); + int16 focus = (int16)SvIV(ST(6)); + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + target = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "target is not of type Mob"); + if(target == NULL) + Perl_croak(aTHX_ "target is NULL, avoiding crash."); + + THIS->DoArcheryAttackDmg(target, RangeWeapon, Ammo, weapon_damage, chance_mod, focus); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_DoThrowingAttackDmg); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_DoThrowingAttackDmg) +{ + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: Mob::DoThrowingAttackDmg(THIS, target, RangeWeapon=NULL,item=NULL,weapon_damage, chance_mod, focus)"); + { + Mob * THIS; + Mob* target; + ItemInst* RangeWeapon = NULL; + Item_Struct* item = NULL; + uint16 weapon_damage = (uint16)SvIV(ST(4)); + int16 chance_mod = (int16)SvIV(ST(5)); + int16 focus = (int16)SvIV(ST(6)); + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + target = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "target is not of type Mob"); + if(target == NULL) + Perl_croak(aTHX_ "target is NULL, avoiding crash."); + + THIS->DoThrowingAttackDmg(target, RangeWeapon, item, weapon_damage, chance_mod, focus); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetDisableMelee); +XS(XS_Mob_SetDisableMelee) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetDisableMelee(THIS, value)"); + { + Mob * THIS; + bool value = (bool)SvTRUE(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetDisableMelee(value); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_IsMeleeDisabled); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_IsMeleeDisabled) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::IsMeleeDisabled(THIS)"); + { + Mob * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsMeleeDisabled(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Mob_SetFlurryChance); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetFlurryChance) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Mob::SetFlurryChance(THIS, value)"); + { + Mob * THIS; + uint8 value = (uint8)SvIV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetFlurryChance(value); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_GetFlurryChance); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetFlurryChance) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Mob::GetFlurryChance(THIS)"); + { + Mob * THIS; + uint8 RETVAL; + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetFlurryChance(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_Mob); /* prototype to pass -Wmissing-prototypes */ +XS(boot_Mob) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "IsClient"), XS_Mob_IsClient, file, "$"); + newXSproto(strcpy(buf, "IsNPC"), XS_Mob_IsNPC, file, "$"); + newXSproto(strcpy(buf, "IsMob"), XS_Mob_IsMob, file, "$"); + newXSproto(strcpy(buf, "IsCorpse"), XS_Mob_IsCorpse, file, "$"); + newXSproto(strcpy(buf, "IsPlayerCorpse"), XS_Mob_IsPlayerCorpse, file, "$"); + newXSproto(strcpy(buf, "IsNPCCorpse"), XS_Mob_IsNPCCorpse, file, "$"); + newXSproto(strcpy(buf, "IsObject"), XS_Mob_IsObject, file, "$"); + newXSproto(strcpy(buf, "IsDoor"), XS_Mob_IsDoor, file, "$"); + newXSproto(strcpy(buf, "IsTrap"), XS_Mob_IsTrap, file, "$"); + newXSproto(strcpy(buf, "IsBeacon"), XS_Mob_IsBeacon, file, "$"); + newXSproto(strcpy(buf, "CastToClient"), XS_Mob_CastToClient, file, "$"); + newXSproto(strcpy(buf, "CastToNPC"), XS_Mob_CastToNPC, file, "$"); + newXSproto(strcpy(buf, "CastToMob"), XS_Mob_CastToMob, file, "$"); + newXSproto(strcpy(buf, "CastToCorpse"), XS_Mob_CastToCorpse, file, "$"); + newXSproto(strcpy(buf, "GetID"), XS_Mob_GetID, file, "$"); + newXSproto(strcpy(buf, "GetName"), XS_Mob_GetName, file, "$"); + newXSproto(strcpy(buf, "Depop"), XS_Mob_Depop, file, "$;$"); + newXSproto(strcpy(buf, "RogueAssassinate"), XS_Mob_RogueAssassinate, file, "$$"); + newXSproto(strcpy(buf, "BehindMob"), XS_Mob_BehindMob, file, "$;$$$"); + newXSproto(strcpy(buf, "SetLevel"), XS_Mob_SetLevel, file, "$$;$"); + newXSproto(strcpy(buf, "GetSkill"), XS_Mob_GetSkill, file, "$$"); + newXSproto(strcpy(buf, "SendWearChange"), XS_Mob_SendWearChange, file, "$$"); + newXSproto(strcpy(buf, "GetEquipment"), XS_Mob_GetEquipment, file, "$$"); + newXSproto(strcpy(buf, "GetEquipmentMaterial"), XS_Mob_GetEquipmentMaterial, file, "$$"); + newXSproto(strcpy(buf, "GetEquipmentColor"), XS_Mob_GetEquipmentColor, file, "$$"); + newXSproto(strcpy(buf, "GetArmorTint"), XS_Mob_GetArmorTint, file, "$$"); + newXSproto(strcpy(buf, "IsMoving"), XS_Mob_IsMoving, file, "$"); + newXSproto(strcpy(buf, "GoToBind"), XS_Mob_GoToBind, file, "$"); + newXSproto(strcpy(buf, "Gate"), XS_Mob_Gate, file, "$"); + newXSproto(strcpy(buf, "Attack"), XS_Mob_Attack, file, "$$;$$"); + newXSproto(strcpy(buf, "Damage"), XS_Mob_Damage, file, "$$$$$;$$$"); + newXSproto(strcpy(buf, "RangedAttack"), XS_Mob_RangedAttack, file, "$$"); + newXSproto(strcpy(buf, "ThrowingAttack"), XS_Mob_ThrowingAttack, file, "$$"); + newXSproto(strcpy(buf, "Heal"), XS_Mob_Heal, file, "$"); + newXSproto(strcpy(buf, "HealDamage"), XS_Mob_HealDamage, file, "$$;$"); + newXSproto(strcpy(buf, "SetMaxHP"), XS_Mob_SetMaxHP, file, "$"); + newXSproto(strcpy(buf, "GetLevelCon"), XS_Mob_GetLevelCon, file, "$$"); + newXSproto(strcpy(buf, "SetHP"), XS_Mob_SetHP, file, "$$"); + newXSproto(strcpy(buf, "DoAnim"), XS_Mob_DoAnim, file, "$$;$"); + newXSproto(strcpy(buf, "ChangeSize"), XS_Mob_ChangeSize, file, "$$;$"); + newXSproto(strcpy(buf, "GMMove"), XS_Mob_GMMove, file, "$$$$;$"); + newXSproto(strcpy(buf, "SendPosUpdate"), XS_Mob_SendPosUpdate, file, "$;$"); + newXSproto(strcpy(buf, "SendPosition"), XS_Mob_SendPosition, file, "$"); + newXSproto(strcpy(buf, "HasProcs"), XS_Mob_HasProcs, file, "$"); + newXSproto(strcpy(buf, "IsInvisible"), XS_Mob_IsInvisible, file, "$;$"); + newXSproto(strcpy(buf, "SetInvisible"), XS_Mob_SetInvisible, file, "$$"); + newXSproto(strcpy(buf, "FindBuff"), XS_Mob_FindBuff, file, "$$"); + newXSproto(strcpy(buf, "FindType"), XS_Mob_FindType, file, "$$;$$"); + newXSproto(strcpy(buf, "GetBuffSlotFromType"), XS_Mob_GetBuffSlotFromType, file, "$$"); + newXSproto(strcpy(buf, "MakePet"), XS_Mob_MakePet, file, "$$$;$"); + newXSproto(strcpy(buf, "GetBaseRace"), XS_Mob_GetBaseRace, file, "$"); + newXSproto(strcpy(buf, "GetBaseGender"), XS_Mob_GetBaseGender, file, "$"); + newXSproto(strcpy(buf, "GetDeity"), XS_Mob_GetDeity, file, "$"); + newXSproto(strcpy(buf, "GetRace"), XS_Mob_GetRace, file, "$"); + newXSproto(strcpy(buf, "GetGender"), XS_Mob_GetGender, file, "$"); + newXSproto(strcpy(buf, "GetTexture"), XS_Mob_GetTexture, file, "$"); + newXSproto(strcpy(buf, "GetHelmTexture"), XS_Mob_GetHelmTexture, file, "$"); + newXSproto(strcpy(buf, "GetHairColor"), XS_Mob_GetHairColor, file, "$"); + newXSproto(strcpy(buf, "GetBeardColor"), XS_Mob_GetBeardColor, file, "$"); + newXSproto(strcpy(buf, "GetEyeColor1"), XS_Mob_GetEyeColor1, file, "$"); + newXSproto(strcpy(buf, "GetEyeColor2"), XS_Mob_GetEyeColor2, file, "$"); + newXSproto(strcpy(buf, "GetHairStyle"), XS_Mob_GetHairStyle, file, "$"); + newXSproto(strcpy(buf, "GetLuclinFace"), XS_Mob_GetLuclinFace, file, "$"); + newXSproto(strcpy(buf, "GetBeard"), XS_Mob_GetBeard, file, "$"); + newXSproto(strcpy(buf, "GetDrakkinHeritage"), XS_Mob_GetDrakkinHeritage, file, "$"); + newXSproto(strcpy(buf, "GetDrakkinTattoo"), XS_Mob_GetDrakkinTattoo, file, "$"); + newXSproto(strcpy(buf, "GetDrakkinDetails"), XS_Mob_GetDrakkinDetails, file, "$"); + newXSproto(strcpy(buf, "GetClass"), XS_Mob_GetClass, file, "$"); + newXSproto(strcpy(buf, "GetLevel"), XS_Mob_GetLevel, file, "$"); + newXSproto(strcpy(buf, "GetCleanName"), XS_Mob_GetCleanName, file, "$"); + newXSproto(strcpy(buf, "GetTarget"), XS_Mob_GetTarget, file, "$"); + newXSproto(strcpy(buf, "SetTarget"), XS_Mob_SetTarget, file, "$$"); + newXSproto(strcpy(buf, "GetHPRatio"), XS_Mob_GetHPRatio, file, "$"); + newXSproto(strcpy(buf, "IsWarriorClass"), XS_Mob_IsWarriorClass, file, "$"); + newXSproto(strcpy(buf, "GetHP"), XS_Mob_GetHP, file, "$"); + newXSproto(strcpy(buf, "GetMaxHP"), XS_Mob_GetMaxHP, file, "$"); + newXSproto(strcpy(buf, "GetItemHPBonuses"), XS_Mob_GetItemHPBonuses, file, "$"); + newXSproto(strcpy(buf, "GetSpellHPBonuses"), XS_Mob_GetSpellHPBonuses, file, "$"); + newXSproto(strcpy(buf, "GetWalkspeed"), XS_Mob_GetWalkspeed, file, "$"); + newXSproto(strcpy(buf, "GetRunspeed"), XS_Mob_GetRunspeed, file, "$"); + newXSproto(strcpy(buf, "GetCasterLevel"), XS_Mob_GetCasterLevel, file, "$$"); + newXSproto(strcpy(buf, "GetMaxMana"), XS_Mob_GetMaxMana, file, "$"); + newXSproto(strcpy(buf, "GetMana"), XS_Mob_GetMana, file, "$"); + newXSproto(strcpy(buf, "SetMana"), XS_Mob_SetMana, file, "$$"); + newXSproto(strcpy(buf, "GetManaRatio"), XS_Mob_GetManaRatio, file, "$"); + newXSproto(strcpy(buf, "GetAC"), XS_Mob_GetAC, file, "$"); + newXSproto(strcpy(buf, "GetATK"), XS_Mob_GetATK, file, "$"); + newXSproto(strcpy(buf, "GetSTR"), XS_Mob_GetSTR, file, "$"); + newXSproto(strcpy(buf, "GetSTA"), XS_Mob_GetSTA, file, "$"); + newXSproto(strcpy(buf, "GetDEX"), XS_Mob_GetDEX, file, "$"); + newXSproto(strcpy(buf, "GetAGI"), XS_Mob_GetAGI, file, "$"); + newXSproto(strcpy(buf, "GetINT"), XS_Mob_GetINT, file, "$"); + newXSproto(strcpy(buf, "GetWIS"), XS_Mob_GetWIS, file, "$"); + newXSproto(strcpy(buf, "GetCHA"), XS_Mob_GetCHA, file, "$"); + newXSproto(strcpy(buf, "GetMR"), XS_Mob_GetMR, file, "$"); + newXSproto(strcpy(buf, "GetFR"), XS_Mob_GetFR, file, "$"); + newXSproto(strcpy(buf, "GetDR"), XS_Mob_GetDR, file, "$"); + newXSproto(strcpy(buf, "GetPR"), XS_Mob_GetPR, file, "$"); + newXSproto(strcpy(buf, "GetCR"), XS_Mob_GetCR, file, "$"); + newXSproto(strcpy(buf, "GetCorruption"), XS_Mob_GetCR, file, "$"); + newXSproto(strcpy(buf, "GetMaxSTR"), XS_Mob_GetMaxSTR, file, "$"); + newXSproto(strcpy(buf, "GetMaxSTA"), XS_Mob_GetMaxSTA, file, "$"); + newXSproto(strcpy(buf, "GetMaxDEX"), XS_Mob_GetMaxDEX, file, "$"); + newXSproto(strcpy(buf, "GetMaxAGI"), XS_Mob_GetMaxAGI, file, "$"); + newXSproto(strcpy(buf, "GetMaxINT"), XS_Mob_GetMaxINT, file, "$"); + newXSproto(strcpy(buf, "GetMaxWIS"), XS_Mob_GetMaxWIS, file, "$"); + newXSproto(strcpy(buf, "GetMaxCHA"), XS_Mob_GetMaxCHA, file, "$"); + newXSproto(strcpy(buf, "GetActSpellRange"), XS_Mob_GetActSpellRange, file, "$$$"); + newXSproto(strcpy(buf, "GetActSpellDamage"), XS_Mob_GetActSpellDamage, file, "$$$"); + newXSproto(strcpy(buf, "GetActSpellHealing"), XS_Mob_GetActSpellHealing, file, "$$$"); + newXSproto(strcpy(buf, "GetActSpellCost"), XS_Mob_GetActSpellCost, file, "$$$"); + newXSproto(strcpy(buf, "GetActSpellDuration"), XS_Mob_GetActSpellDuration, file, "$$$"); + newXSproto(strcpy(buf, "GetActSpellCasttime"), XS_Mob_GetActSpellCasttime, file, "$$$"); + newXSproto(strcpy(buf, "ResistSpell"), XS_Mob_ResistSpell, file, "$$$$"); + newXSproto(strcpy(buf, "GetSpecializeSkillValue"), XS_Mob_GetSpecializeSkillValue, file, "$$"); + newXSproto(strcpy(buf, "GetNPCTypeID"), XS_Mob_GetNPCTypeID, file, "$"); + newXSproto(strcpy(buf, "IsTargeted"), XS_Mob_IsTargeted, file, "$"); + newXSproto(strcpy(buf, "GetX"), XS_Mob_GetX, file, "$"); + newXSproto(strcpy(buf, "GetY"), XS_Mob_GetY, file, "$"); + newXSproto(strcpy(buf, "GetZ"), XS_Mob_GetZ, file, "$"); + newXSproto(strcpy(buf, "GetHeading"), XS_Mob_GetHeading, file, "$"); + newXSproto(strcpy(buf, "GetWaypointX"), XS_Mob_GetWaypointX, file, "$"); + newXSproto(strcpy(buf, "GetWaypointY"), XS_Mob_GetWaypointY, file, "$"); + newXSproto(strcpy(buf, "GetWaypointZ"), XS_Mob_GetWaypointZ, file, "$"); + newXSproto(strcpy(buf, "GetWaypointH"), XS_Mob_GetWaypointH, file, "$"); + newXSproto(strcpy(buf, "GetWaypointPause"), XS_Mob_GetWaypointPause, file, "$"); + newXSproto(strcpy(buf, "GetWaypointID"), XS_Mob_GetWaypointID, file, "$"); + newXSproto(strcpy(buf, "SetCurrentWP"), XS_Mob_SetCurrentWP, file, "$$"); + newXSproto(strcpy(buf, "GetSize"), XS_Mob_GetSize, file, "$"); + newXSproto(strcpy(buf, "SetFollowID"), XS_Mob_SetFollowID, file, "$$"); + newXSproto(strcpy(buf, "GetFollowID"), XS_Mob_GetFollowID, file, "$"); + newXSproto(strcpy(buf, "Message"), XS_Mob_Message, file, "$$$;@"); + newXSproto(strcpy(buf, "Message_StringID"), XS_Mob_Message_StringID, file, "$$$;$"); + newXSproto(strcpy(buf, "Say"), XS_Mob_Say, file, "$$;@"); + newXSproto(strcpy(buf, "Shout"), XS_Mob_Shout, file, "$$;@"); + newXSproto(strcpy(buf, "Emote"), XS_Mob_Emote, file, "$$;@"); + newXSproto(strcpy(buf, "InterruptSpell"), XS_Mob_InterruptSpell, file, "$;$"); + newXSproto(strcpy(buf, "CastSpell"), XS_Mob_CastSpell, file, "$$$;$$$"); + newXSproto(strcpy(buf, "SpellFinished"), XS_Mob_SpellFinished, file, "$$;$$"); + newXSproto(strcpy(buf, "IsImmuneToSpell"), XS_Mob_IsImmuneToSpell, file, "$$$"); + newXSproto(strcpy(buf, "BuffFadeBySpellID"), XS_Mob_BuffFadeBySpellID, file, "$$"); + newXSproto(strcpy(buf, "BuffFadeByEffect"), XS_Mob_BuffFadeByEffect, file, "$$;$"); + newXSproto(strcpy(buf, "BuffFadeAll"), XS_Mob_BuffFadeAll, file, "$"); + newXSproto(strcpy(buf, "BuffFadeBySlot"), XS_Mob_BuffFadeBySlot, file, "$$;$"); + newXSproto(strcpy(buf, "CanBuffStack"), XS_Mob_CanBuffStack, file, "$$$;$"); + newXSproto(strcpy(buf, "IsCasting"), XS_Mob_IsCasting, file, "$"); + newXSproto(strcpy(buf, "CastingSpellID"), XS_Mob_CastingSpellID, file, "$"); + newXSproto(strcpy(buf, "SetAppearance"), XS_Mob_SetAppearance, file, "$$;$"); + newXSproto(strcpy(buf, "GetAppearance"), XS_Mob_GetAppearance, file, "$"); + newXSproto(strcpy(buf, "GetRunAnimSpeed"), XS_Mob_GetRunAnimSpeed, file, "$"); + newXSproto(strcpy(buf, "SetRunAnimSpeed"), XS_Mob_SetRunAnimSpeed, file, "$$"); + newXSproto(strcpy(buf, "SetPetID"), XS_Mob_SetPetID, file, "$$"); + newXSproto(strcpy(buf, "GetPetID"), XS_Mob_GetPetID, file, "$"); + newXSproto(strcpy(buf, "SetOwnerID"), XS_Mob_SetOwnerID, file, "$$"); + newXSproto(strcpy(buf, "GetOwnerID"), XS_Mob_GetOwnerID, file, "$"); + newXSproto(strcpy(buf, "GetPetType"), XS_Mob_GetPetType, file, "$"); + newXSproto(strcpy(buf, "GetBodyType"), XS_Mob_GetBodyType, file, "$"); + newXSproto(strcpy(buf, "Stun"), XS_Mob_Stun, file, "$$"); + newXSproto(strcpy(buf, "Spin"), XS_Mob_Spin, file, "$"); + newXSproto(strcpy(buf, "Kill"), XS_Mob_Kill, file, "$"); + newXSproto(strcpy(buf, "SetInvul"), XS_Mob_SetInvul, file, "$$"); + newXSproto(strcpy(buf, "GetInvul"), XS_Mob_GetInvul, file, "$"); + newXSproto(strcpy(buf, "SetExtraHaste"), XS_Mob_SetExtraHaste, file, "$$"); + newXSproto(strcpy(buf, "GetHaste"), XS_Mob_GetHaste, file, "$"); + newXSproto(strcpy(buf, "GetMonkHandToHandDamage"), XS_Mob_GetMonkHandToHandDamage, file, "$"); + newXSproto(strcpy(buf, "CanThisClassDoubleAttack"), XS_Mob_CanThisClassDoubleAttack, file, "$"); + newXSproto(strcpy(buf, "CanThisClassDualWield"), XS_Mob_CanThisClassDualWield, file, "$"); + newXSproto(strcpy(buf, "CanThisClassRiposte"), XS_Mob_CanThisClassRiposte, file, "$"); + newXSproto(strcpy(buf, "CanThisClassDodge"), XS_Mob_CanThisClassDodge, file, "$"); + newXSproto(strcpy(buf, "CanThisClassParry"), XS_Mob_CanThisClassParry, file, "$"); + newXSproto(strcpy(buf, "GetMonkHandToHandDelay"), XS_Mob_GetMonkHandToHandDelay, file, "$"); + newXSproto(strcpy(buf, "GetClassLevelFactor"), XS_Mob_GetClassLevelFactor, file, "$"); + newXSproto(strcpy(buf, "Mesmerize"), XS_Mob_Mesmerize, file, "$"); + newXSproto(strcpy(buf, "IsMezzed"), XS_Mob_IsMezzed, file, "$"); + newXSproto(strcpy(buf, "IsStunned"), XS_Mob_IsStunned, file, "$"); + newXSproto(strcpy(buf, "StartEnrage"), XS_Mob_StartEnrage, file, "$"); + newXSproto(strcpy(buf, "IsEnraged"), XS_Mob_IsEnraged, file, "$"); + newXSproto(strcpy(buf, "GetReverseFactionCon"), XS_Mob_GetReverseFactionCon, file, "$$"); + newXSproto(strcpy(buf, "IsAIControlled"), XS_Mob_IsAIControlled, file, "$"); + newXSproto(strcpy(buf, "GetAggroRange"), XS_Mob_GetAggroRange, file, "$"); + newXSproto(strcpy(buf, "GetAssistRange"), XS_Mob_GetAssistRange, file, "$"); + newXSproto(strcpy(buf, "SetPetOrder"), XS_Mob_SetPetOrder, file, "$$"); + newXSproto(strcpy(buf, "GetPetOrder"), XS_Mob_GetPetOrder, file, "$"); + newXSproto(strcpy(buf, "IsRoamer"), XS_Mob_IsRoamer, file, "$"); + newXSproto(strcpy(buf, "IsRooted"), XS_Mob_IsRooted, file, "$"); + newXSproto(strcpy(buf, "AddToHateList"), XS_Mob_AddToHateList, file, "$$;$$$$$"); + newXSproto(strcpy(buf, "SetHate"), XS_Mob_SetHate, file, "$$;$$"); + newXSproto(strcpy(buf, "GetHateAmount"), XS_Mob_GetHateAmount, file, "$$;$"); + newXSproto(strcpy(buf, "GetDamageAmount"), XS_Mob_GetDamageAmount, file, "$$"); + newXSproto(strcpy(buf, "GetHateTop"), XS_Mob_GetHateTop, file, "$"); + newXSproto(strcpy(buf, "GetHateDamageTop"), XS_Mob_GetHateDamageTop, file, "$$"); + newXSproto(strcpy(buf, "GetHateRandom"), XS_Mob_GetHateRandom, file, "$"); + newXSproto(strcpy(buf, "IsEngaged"), XS_Mob_IsEngaged, file, "$"); + newXSproto(strcpy(buf, "HateSummon"), XS_Mob_HateSummon, file, "$"); + newXSproto(strcpy(buf, "FaceTarget"), XS_Mob_FaceTarget, file, "$;$$"); + newXSproto(strcpy(buf, "SetHeading"), XS_Mob_SetHeading, file, "$$"); + newXSproto(strcpy(buf, "WipeHateList"), XS_Mob_WipeHateList, file, "$"); + newXSproto(strcpy(buf, "CheckAggro"), XS_Mob_CheckAggro, file, "$$"); + newXSproto(strcpy(buf, "CalculateHeadingToTarget"), XS_Mob_CalculateHeadingToTarget, file, "$$$"); + newXSproto(strcpy(buf, "CalculateNewPosition"), XS_Mob_CalculateNewPosition, file, "$$$$$;$"); + newXSproto(strcpy(buf, "CalculateNewPosition2"), XS_Mob_CalculateNewPosition2, file, "$$$$$;$"); + newXSproto(strcpy(buf, "CalculateDistance"), XS_Mob_CalculateDistance, file, "$$$$"); + newXSproto(strcpy(buf, "SendTo"), XS_Mob_SendTo, file, "$$$$"); + newXSproto(strcpy(buf, "SendToFixZ"), XS_Mob_SendToFixZ, file, "$$$$"); + newXSproto(strcpy(buf, "NPCSpecialAttacks"), XS_Mob_NPCSpecialAttacks, file, "$$$;$$"); + newXSproto(strcpy(buf, "DontHealMeBefore"), XS_Mob_DontHealMeBefore, file, "$"); + newXSproto(strcpy(buf, "DontBuffMeBefore"), XS_Mob_DontBuffMeBefore, file, "$"); + newXSproto(strcpy(buf, "DontDotMeBefore"), XS_Mob_DontDotMeBefore, file, "$"); + newXSproto(strcpy(buf, "DontRootMeBefore"), XS_Mob_DontRootMeBefore, file, "$"); + newXSproto(strcpy(buf, "DontSnareMeBefore"), XS_Mob_DontSnareMeBefore, file, "$"); + newXSproto(strcpy(buf, "GetResist"), XS_Mob_GetResist, file, "$$"); + newXSproto(strcpy(buf, "GetShieldTarget"), XS_Mob_GetShieldTarget, file, "$"); + newXSproto(strcpy(buf, "SetShieldTarget"), XS_Mob_SetShieldTarget, file, "$$"); + newXSproto(strcpy(buf, "Charmed"), XS_Mob_Charmed, file, "$"); + newXSproto(strcpy(buf, "GetLevelHP"), XS_Mob_GetLevelHP, file, "$$"); + newXSproto(strcpy(buf, "GetZoneID"), XS_Mob_GetZoneID, file, "$"); + newXSproto(strcpy(buf, "CheckAggroAmount"), XS_Mob_CheckAggroAmount, file, "$$"); + newXSproto(strcpy(buf, "CheckHealAggroAmount"), XS_Mob_CheckHealAggroAmount, file, "$$"); + newXSproto(strcpy(buf, "GetAA"), XS_Mob_GetAA, file, "$$"); + newXSproto(strcpy(buf, "DivineAura"), XS_Mob_DivineAura, file, "$"); + newXSproto(strcpy(buf, "AddFeignMemory"), XS_Mob_AddFeignMemory, file, "$$"); + newXSproto(strcpy(buf, "RemoveFromFeignMemory"), XS_Mob_RemoveFromFeignMemory, file, "$$"); + newXSproto(strcpy(buf, "ClearFeignMemory"), XS_Mob_ClearFeignMemory, file, "$"); + newXSproto(strcpy(buf, "SetOOCRegen"), XS_Mob_SetOOCRegen, file, "$$"); + newXSproto(strcpy(buf, "GetEntityVariable"), XS_Mob_GetEntityVariable, file, "$$"); + newXSproto(strcpy(buf, "SetEntityVariable"), XS_Mob_SetEntityVariable, file, "$$$"); + newXSproto(strcpy(buf, "EntityVariableExists"), XS_Mob_EntityVariableExists, file, "$$"); + newXSproto(strcpy(buf, "GetHateList"), XS_Mob_GetHateList, file, "$"); + newXSproto(strcpy(buf, "SignalClient"), XS_Mob_SignalClient, file, "$$$"); + newXSproto(strcpy(buf, "CombatRange"), XS_Mob_CombatRange, file, "$$"); + newXSproto(strcpy(buf, "DoSpecialAttackDamage"), XS_Mob_DoSpecialAttackDamage, file, "$$$$;$$"); + newXSproto(strcpy(buf, "CheckLoS"), XS_Mob_CheckLoS, file, "$$"); + newXSproto(strcpy(buf, "CheckLoSToLoc"), XS_Mob_CheckLoSToLoc, file, "$$$$;$"); + newXSproto(strcpy(buf, "FindGroundZ"), XS_Mob_FindGroundZ, file, "$$$;$"); + newXSproto(strcpy(buf, "ProjectileAnim"), XS_Mob_ProjectileAnim, file, "$$$;$$$$$"); + newXSproto(strcpy(buf, "HasNPCSpecialAtk"), XS_Mob_HasNPCSpecialAtk, file, "$$"); + newXSproto(strcpy(buf, "SendAppearanceEffect"), XS_Mob_SendAppearanceEffect, file, "$$;$$$$"); + newXSproto(strcpy(buf, "SetFlyMode"), XS_Mob_SetFlyMode, file, "$$"); + newXSproto(strcpy(buf, "SetTexture"), XS_Mob_SetTexture, file, "$$"); + 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, "QuestReward"), XS_Mob_QuestReward, file, "$$;$$$"); + newXSproto(strcpy(buf, "CameraEffect"), XS_Mob_CameraEffect, file, "$$;$$$"); + newXSproto(strcpy(buf, "SpellEffect"), XS_Mob_SpellEffect, file, "$$;$$$$$$"); + newXSproto(strcpy(buf, "TempName"), XS_Mob_TempName, file, "$:$"); + newXSproto(strcpy(buf, "GetItemStat"), XS_Mob_GetItemStat, file, "$$$"); + newXSproto(strcpy(buf, "SetGlobal"), XS_Mob_SetGlobal, file, "$$$$$;$"); + newXSproto(strcpy(buf, "TarGlobal"), XS_Mob_TarGlobal, file, "$$$$$$$"); + newXSproto(strcpy(buf, "DelGlobal"), XS_Mob_DelGlobal, file, "$$"); + newXSproto(strcpy(buf, "SetSlotTint"), XS_Mob_SetSlotTint, file, "$$$$$"); + newXSproto(strcpy(buf, "WearChange"), XS_Mob_WearChange, file, "$$$;$"); + newXSproto(strcpy(buf, "DoKnockback"), XS_Mob_DoKnockback, file, "$$$$"); + newXSproto(strcpy(buf, "RemoveNimbusEffect"), XS_Mob_RemoveNimbusEffect, file, "$$"); + newXSproto(strcpy(buf, "IsRunning"), XS_Mob_IsRunning, file, "$"); + newXSproto(strcpy(buf, "SetRunning"), XS_Mob_SetRunning, file, "$$"); + newXSproto(strcpy(buf, "SetBodyType"), XS_Mob_SetBodyType, file, "$$;$"); + newXSproto(strcpy(buf, "SetDeltas"), XS_Mob_SetDeltas, file, "$$$$$"); + newXSproto(strcpy(buf, "SetLD"), XS_Mob_SetLD, file, "$$"); + newXSproto(strcpy(buf, "SetTargetDestSteps"), XS_Mob_SetTargetDestSteps, file, "$$"); + newXSproto(strcpy(buf, "SetTargetable"), XS_Mob_SetTargetable, file, "$$"); + newXSproto(strcpy(buf, "MakeTempPet"), XS_Mob_MakeTempPet, file, "$$;$$$$"); + newXSproto(strcpy(buf, "ModSkillDmgTaken"), XS_Mob_ModSkillDmgTaken, file, "$$$"); + newXSproto(strcpy(buf, "GetModSkillDmgTaken"), XS_Mob_GetModSkillDmgTaken, file, "$$"); + newXSproto(strcpy(buf, "GetSkillDmgTaken"), XS_Mob_GetSkillDmgTaken, file, "$$"); + newXSproto(strcpy(buf, "SetAllowBeneficial"), XS_Mob_SetAllowBeneficial, file, "$$"); + newXSproto(strcpy(buf, "GetAllowBeneficial"), XS_Mob_GetAllowBeneficial, file, "$$"); + newXSproto(strcpy(buf, "IsBeneficialAllowed"), XS_Mob_IsBeneficialAllowed, file, "$$"); + newXSproto(strcpy(buf, "ModVulnerability"), XS_Mob_ModVulnerability, file, "$$$"); + newXSproto(strcpy(buf, "GetModVulnerability"), XS_Mob_GetModVulnerability, file, "$$"); + newXSproto(strcpy(buf, "DoMeleeSkillAttackDmg"), XS_Mob_DoMeleeSkillAttackDmg, file, "$$$$$$$"); + 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, "SetFlurryChance"), XS_Mob_SetFlurryChance, file, "$$"); + newXSproto(strcpy(buf, "GetFlurryChance"), XS_Mob_GetFlurryChance, file, "$"); + + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES \ No newline at end of file diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp new file mode 100644 index 000000000..86f0fa039 --- /dev/null +++ b/zone/perl_npc.cpp @@ -0,0 +1,2177 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of tmp. Do not edit this file, edit tmp instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +typedef const char Const_char; + +#include "npc.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +XS(XS_NPC_SignalNPC); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SignalNPC) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SignalNPC(THIS, _signal_id)"); + { + NPC * THIS; + int _signal_id = (int)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SignalNPC(_signal_id); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_CheckNPCFactionAlly); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_CheckNPCFactionAlly) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::CheckNPCFactionAlly(THIS, other_faction)"); + { + NPC * THIS; + FACTION_VALUE RETVAL; + dXSTARG; + int32 other_faction = (int32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->CheckNPCFactionAlly(other_faction); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_AddItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_AddItem) +{ + dXSARGS; + if (items < 2 || items > 4) + Perl_croak(aTHX_ "Usage: NPC::AddItem(THIS, itemid, charges = 0, equipitem = true)"); + { + NPC * THIS; + uint32 itemid = (uint32)SvUV(ST(1)); + uint16 charges = 0; + bool equipitem = true; + + 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."); + + if (items > 2) + charges = (uint16)SvUV(ST(2)); + if (items > 3) + equipitem = (bool)SvTRUE(ST(3)); + + THIS->AddItem(itemid, charges, equipitem); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_AddLootTable); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_AddLootTable) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::AddLootTable(THIS)"); + { + NPC * THIS; + + 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."); + + THIS->AddLootTable(); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_RemoveItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_RemoveItem) +{ + dXSARGS; + if (items < 2 || items > 4) + Perl_croak(aTHX_ "Usage: NPC::RemoveItem(THIS, item_id, quantity= 0, slot= 0)"); + { + NPC * THIS; + uint32 item_id = (uint32)SvUV(ST(1)); + uint16 quantity; + uint16 slot; + + 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."); + + if (items < 3) + quantity = 0; + else { + quantity = (uint16)SvUV(ST(2)); + } + + if (items < 4) + slot = 0; + else { + slot = (uint16)SvUV(ST(3)); + } + + THIS->RemoveItem(item_id, quantity, slot); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_ClearItemList); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_ClearItemList) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::ClearItemList(THIS)"); + { + NPC * THIS; + + 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."); + + THIS->ClearItemList(); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_AddCash); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_AddCash) +{ + dXSARGS; + if (items != 5) + Perl_croak(aTHX_ "Usage: NPC::AddCash(THIS, in_copper, in_silver, in_gold, in_platinum)"); + { + NPC * THIS; + uint16 in_copper = (uint16)SvUV(ST(1)); + uint16 in_silver = (uint16)SvUV(ST(2)); + uint16 in_gold = (uint16)SvUV(ST(3)); + uint16 in_platinum = (uint16)SvUV(ST(4)); + + 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."); + + THIS->AddCash(in_copper, in_silver, in_gold, in_platinum); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_RemoveCash); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_RemoveCash) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::RemoveCash(THIS)"); + { + NPC * THIS; + + 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."); + + THIS->RemoveCash(); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_CountLoot); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_CountLoot) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::CountLoot(THIS)"); + { + NPC * THIS; + uint32 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->CountLoot(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetLoottableID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetLoottableID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetLoottableID(THIS)"); + { + NPC * THIS; + uint32 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->GetLoottableID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetCopper); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetCopper) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetCopper(THIS)"); + { + NPC * THIS; + uint32 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->GetCopper(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetSilver); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSilver) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSilver(THIS)"); + { + NPC * THIS; + uint32 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->GetSilver(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetGold); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetGold) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetGold(THIS)"); + { + NPC * THIS; + uint32 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->GetGold(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetPlatinum); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetPlatinum) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetPlatinum(THIS)"); + { + NPC * THIS; + uint32 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->GetPlatinum(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_SetCopper); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetCopper) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetCopper(THIS, amt)"); + { + NPC * THIS; + uint32 amt = (uint32)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetCopper(amt); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_SetSilver); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetSilver) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetSilver(THIS, amt)"); + { + NPC * THIS; + uint32 amt = (uint32)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetSilver(amt); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_SetGold); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetGold) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetGold(THIS, amt)"); + { + NPC * THIS; + uint32 amt = (uint32)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetGold(amt); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_SetPlatinum); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetPlatinum) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetPlatinum(THIS, amt)"); + { + NPC * THIS; + uint32 amt = (uint32)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetPlatinum(amt); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_SetGrid); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetGrid) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetGrid(THIS, grid_)"); + { + NPC * THIS; + int32 grid_ = (int32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetGrid(grid_); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_SetSaveWaypoint); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetSaveWaypoint) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetSaveWaypoint(THIS, waypoint)"); + { + NPC * THIS; + uint16 waypoint = (uint16)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetSaveWaypoint(waypoint); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_SetSp2); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetSp2) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetSp2(THIS, sg2)"); + { + NPC * THIS; + uint32 sg2 = (uint32)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetSp2(sg2); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_GetWaypointMax); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetWaypointMax) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetWaypointMax(THIS)"); + { + NPC * THIS; + uint16 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->GetWaypointMax(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetGrid); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetGrid) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetGrid(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetGrid(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetSp2); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSp2) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSp2(THIS)"); + { + NPC * THIS; + uint32 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->GetSp2(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetNPCFactionID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetNPCFactionID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetNPCFactionID(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetNPCFactionID(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetPrimaryFaction); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetPrimaryFaction) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetPrimaryFaction(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetPrimaryFaction(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetNPCHate); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetNPCHate) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::GetNPCHate(THIS, in_ent)"); + { + NPC * THIS; + int32 RETVAL; + dXSTARG; + Mob* in_ent; + + 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."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + in_ent = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "in_ent is not of type Mob"); + if(in_ent == NULL) + Perl_croak(aTHX_ "in_ent is NULL, avoiding crash."); + + RETVAL = THIS->GetNPCHate(in_ent); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_IsOnHatelist); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_IsOnHatelist) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::IsOnHatelist(THIS, p)"); + { + NPC * THIS; + bool RETVAL; + Mob* p; + + 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."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + p = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "p is not of type Mob"); + if(p == NULL) + Perl_croak(aTHX_ "p is NULL, avoiding crash."); + + RETVAL = THIS->IsOnHatelist(p); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_NPC_SetNPCFactionID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetNPCFactionID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetNPCFactionID(THIS, in)"); + { + NPC * THIS; + int32 in = (int32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetNPCFactionID(in); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_GetMaxDMG); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetMaxDMG) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetMaxDMG(THIS)"); + { + NPC * THIS; + uint32 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->GetMaxDMG(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetMinDMG); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetMinDMG) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetMinDMG(THIS)"); + { + NPC * THIS; + uint32 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->GetMinDMG(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + + +XS(XS_NPC_IsAnimal); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_IsAnimal) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::IsAnimal(THIS)"); + { + NPC * THIS; + bool RETVAL; + + 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->IsAnimal(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_NPC_GetPetSpellID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetPetSpellID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetPetSpellID(THIS)"); + { + NPC * THIS; + uint16 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->GetPetSpellID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_SetPetSpellID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetPetSpellID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetPetSpellID(THIS, amt)"); + { + NPC * THIS; + uint16 amt = (uint16)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetPetSpellID(amt); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_GetMaxDamage); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetMaxDamage) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::GetMaxDamage(THIS, tlevel)"); + { + NPC * THIS; + uint32 RETVAL; + dXSTARG; + uint8 tlevel = (uint8)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMaxDamage(tlevel); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_SetTaunting); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetTaunting) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetTaunting(THIS, tog)"); + { + NPC * THIS; + bool tog = (bool)SvTRUE(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetTaunting(tog); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_PickPocket); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_PickPocket) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::PickPocket(THIS, thief)"); + { + NPC * THIS; + Client* thief; + + 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."); + + if (sv_derived_from(ST(1), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + thief = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "thief is not of type Client"); + if(thief == NULL) + Perl_croak(aTHX_ "thief is NULL, avoiding crash."); + + THIS->PickPocket(thief); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_StartSwarmTimer); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_StartSwarmTimer) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::StartSwarmTimer(THIS, duration)"); + { + NPC * THIS; + uint32 duration = (uint32)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->StartSwarmTimer(duration); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_DoClassAttacks); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_DoClassAttacks) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::DoClassAttacks(THIS, target)"); + { + NPC * THIS; + Mob * target; + + 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."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + target = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "target is not of type Mob"); + if(target == NULL) + Perl_croak(aTHX_ "target is NULL, avoiding crash."); + + THIS->DoClassAttacks(target); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_GetMaxWp); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetMaxWp) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetMaxWp(THIS)"); + { + NPC * THIS; + int 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->GetMaxWp(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_DisplayWaypointInfo); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_DisplayWaypointInfo) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::DisplayWaypointInfo(THIS, to)"); + { + NPC * THIS; + Client * to; + + 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."); + + if (sv_derived_from(ST(1), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + to = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "to is not of type Client"); + if(to == NULL) + Perl_croak(aTHX_ "to is NULL, avoiding crash."); + + THIS->DisplayWaypointInfo(to); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_CalculateNewWaypoint); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_CalculateNewWaypoint) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::CalculateNewWaypoint(THIS)"); + { + NPC * THIS; + + 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."); + + THIS->CalculateNewWaypoint(); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_AssignWaypoints); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_AssignWaypoints) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::AssignWaypoints(THIS, grid)"); + { + NPC * THIS; + uint32 grid = (uint32)SvUV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->AssignWaypoints(grid); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_SetWaypointPause); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetWaypointPause) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::SetWaypointPause(THIS)"); + { + NPC * THIS; + + 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."); + + THIS->SetWaypointPause(); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_UpdateWaypoint); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_UpdateWaypoint) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::UpdateWaypoint(THIS, wp_index)"); + { + NPC * THIS; + int wp_index = (int)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->UpdateWaypoint(wp_index); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_StopWandering); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_StopWandering) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::StopWandering(THIS)"); + { + NPC * THIS; + + 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."); + + THIS->StopWandering(); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_ResumeWandering); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_ResumeWandering) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::ResumeWandering(THIS)"); + { + NPC * THIS; + + 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."); + + THIS->ResumeWandering(); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_PauseWandering); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_PauseWandering) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::PauseWandering(THIS, pausetime)"); + { + NPC * THIS; + int pausetime = (int)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->PauseWandering(pausetime); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_MoveTo); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_MoveTo) +{ + dXSARGS; + if (items != 4 && items != 5 && items != 6) + Perl_croak(aTHX_ "Usage: NPC::MoveTo(THIS, mtx, mty, mtz, [mth, saveguard?])"); + { + NPC * THIS; + float mtx = (float)SvNV(ST(1)); + float mty = (float)SvNV(ST(2)); + float mtz = (float)SvNV(ST(3)); + float mth; + bool saveguard; + + if(items > 4) + mth = (float)SvNV(ST(4)); + else + mth = 0; + + if(items > 5) + saveguard = (bool)SvTRUE(ST(5)); + else + saveguard = false; + + 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."); + + THIS->MoveTo(mtx, mty, mtz, mth, saveguard); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_NextGuardPosition); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_NextGuardPosition) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::NextGuardPosition(THIS)"); + { + NPC * THIS; + + 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."); + + THIS->NextGuardPosition(); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_SaveGuardSpot); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SaveGuardSpot) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: NPC::SaveGuardSpot(THIS, iClearGuardSpot= false)"); + { + NPC * THIS; + bool iClearGuardSpot; + + 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."); + + if (items < 2) + iClearGuardSpot = false; + else { + iClearGuardSpot = (bool)SvTRUE(ST(1)); + } + + THIS->SaveGuardSpot(iClearGuardSpot); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_IsGuarding); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_IsGuarding) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::IsGuarding(THIS)"); + { + NPC * THIS; + bool RETVAL; + + 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->IsGuarding(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_NPC_AI_SetRoambox); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_AI_SetRoambox) +{ + dXSARGS; + if (items < 6 || items > 7) + Perl_croak(aTHX_ "Usage: NPC::AI_SetRoambox(THIS, iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay= 2500)"); + { + NPC * THIS; + float iDist = (float)SvNV(ST(1)); + float iMaxX = (float)SvNV(ST(2)); + float iMinX = (float)SvNV(ST(3)); + float iMaxY = (float)SvNV(ST(4)); + float iMinY = (float)SvNV(ST(5)); + uint32 iDelay; + + 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."); + + if (items < 7) + iDelay = 2500; + else { + iDelay = (uint32)SvUV(ST(6)); + } + + THIS->AI_SetRoambox(iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_GetNPCSpellsID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetNPCSpellsID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetNPCSpellsID(THIS)"); + { + NPC * THIS; + uint32 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->GetNPCSpellsID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetSpawnPointID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSpawnPointID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSpawnPointID(THIS)"); + { + NPC * THIS; + uint32 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->GetSpawnPointID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetSpawnPointX); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSpawnPointX) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSpawnPointX(THIS)"); + { + NPC * THIS; + float 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->GetSpawnPointX(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetSpawnPointY); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSpawnPointY) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSpawnPointY(THIS)"); + { + NPC * THIS; + float 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->GetSpawnPointY(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetSpawnPointZ); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSpawnPointZ) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSpawnPointZ(THIS)"); + { + NPC * THIS; + float 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->GetSpawnPointZ(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetSpawnPointH); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSpawnPointH) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSpawnPointH(THIS)"); + { + NPC * THIS; + float 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->GetSpawnPointH(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetGuardPointX); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetGuardPointX) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetGuardPointX(THIS)"); + { + NPC * THIS; + float 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->GetGuardPointX(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetGuardPointY); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetGuardPointY) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetGuardPointY(THIS)"); + { + NPC * THIS; + float 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->GetGuardPointY(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetGuardPointZ); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetGuardPointZ) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetGuardPointZ(THIS)"); + { + NPC * THIS; + float 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->GetGuardPointZ(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_SetPrimSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetPrimSkill) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetPrimSkill(THIS, skill_id)"); + { + NPC * THIS; + int skill_id = (int)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetPrimSkill(skill_id); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_SetSecSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetSecSkill) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetSecSkill(THIS, skill_id)"); + { + NPC * THIS; + int skill_id = (int)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetSecSkill(skill_id); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_GetPrimSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetPrimSkill) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetPrimSkill(THIS)"); + { + NPC * THIS; + uint32 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->GetPrimSkill(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetSecSkill); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSecSkill) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSecSkill(THIS)"); + { + NPC * THIS; + uint32 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->GetSecSkill(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetSwarmOwner); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSwarmOwner) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSwarmOwner(THIS)"); + { + NPC * THIS; + uint32 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->GetSwarmOwner(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetSwarmTarget); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSwarmTarget) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSwarmTarget(THIS)"); + { + NPC * THIS; + uint32 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->GetSwarmTarget(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_SetSwarmTarget); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetSwarmTarget) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetSwarmTarget(THIS, target_id)"); + { + NPC * THIS; + int target_id = (int)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetSwarmTarget(target_id); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_ModifyNPCStat); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_ModifyNPCStat) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: NPC::ModifyNPCStat(THIS, identifier, newValue)"); + { + NPC * THIS; + Const_char * identifier = (Const_char *)SvPV_nolen(ST(1)); + Const_char * newValue = (Const_char *)SvPV_nolen(ST(2)); + + 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."); + + THIS->ModifyNPCStat(identifier, newValue); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_AddSpellToNPCList); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_AddSpellToNPCList) +{ + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: NPC::AddAISpell(THIS, priority, spell_id, type, mana_cost, recast_delay, resist_adjust)"); + { + NPC * THIS; + int priority = (int)SvIV(ST(1)); + int spell_id = (int)SvIV(ST(2)); + int type = (int)SvIV(ST(3)); + int mana_cost = (int)SvIV(ST(4)); + int recast_delay = (int)SvIV(ST(5)); + int resist_adjust = (int)SvIV(ST(6)); + + 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."); + + THIS->AddSpellToNPCList(priority, spell_id, type, mana_cost, recast_delay, resist_adjust); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_RemoveSpellFromNPCList); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_RemoveSpellFromNPCList) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::RemoveAISpell(THIS, spell_id)"); + { + NPC * THIS; + int spell_id = (int)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->RemoveSpellFromNPCList(spell_id); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_SetSpellFocusDMG); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetSpellFocusDMG) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetSpellFocusDMG(THIS, NewSpellFocusDMG)"); + { + NPC * THIS; + int32 NewSpellFocusDMG = (int32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetSpellFocusDMG(NewSpellFocusDMG); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_SetSpellFocusHeal); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_SetSpellFocusHeal) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetSpellFocusHeal(THIS, NewSpellFocusHeal)"); + { + NPC * THIS; + int32 NewSpellFocusHeal = (int32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetSpellFocusHeal(NewSpellFocusHeal); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_GetSlowMitigation); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSlowMitigation) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSlowMitigation(THIS)"); + { + NPC * THIS; + float 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->GetSlowMitigation(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetAttackSpeed); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetAttackSpeed) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetAttackSpeed(THIS)"); + { + NPC * THIS; + float 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->GetAttackSpeed(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_NPC_GetAccuracyRating); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetAccuracyRating) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetAccuracyRating(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetAccuracyRating(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_NPC); /* prototype to pass -Wmissing-prototypes */ +XS(boot_NPC) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "SignalNPC"), XS_NPC_SignalNPC, file, "$$"); + newXSproto(strcpy(buf, "CheckNPCFactionAlly"), XS_NPC_CheckNPCFactionAlly, file, "$$"); + newXSproto(strcpy(buf, "AddItem"), XS_NPC_AddItem, file, "$$;$$"); + newXSproto(strcpy(buf, "AddLootTable"), XS_NPC_AddLootTable, file, "$"); + newXSproto(strcpy(buf, "RemoveItem"), XS_NPC_RemoveItem, file, "$$;$$"); + newXSproto(strcpy(buf, "ClearItemList"), XS_NPC_ClearItemList, file, "$"); + newXSproto(strcpy(buf, "AddCash"), XS_NPC_AddCash, file, "$$$$$"); + newXSproto(strcpy(buf, "RemoveCash"), XS_NPC_RemoveCash, file, "$"); + newXSproto(strcpy(buf, "CountLoot"), XS_NPC_CountLoot, file, "$"); + newXSproto(strcpy(buf, "GetLoottableID"), XS_NPC_GetLoottableID, file, "$"); + newXSproto(strcpy(buf, "GetCopper"), XS_NPC_GetCopper, file, "$"); + newXSproto(strcpy(buf, "GetSilver"), XS_NPC_GetSilver, file, "$"); + newXSproto(strcpy(buf, "GetGold"), XS_NPC_GetGold, file, "$"); + newXSproto(strcpy(buf, "GetPlatinum"), XS_NPC_GetPlatinum, file, "$"); + newXSproto(strcpy(buf, "SetCopper"), XS_NPC_SetCopper, file, "$$"); + newXSproto(strcpy(buf, "SetSilver"), XS_NPC_SetSilver, file, "$$"); + newXSproto(strcpy(buf, "SetGold"), XS_NPC_SetGold, file, "$$"); + newXSproto(strcpy(buf, "SetPlatinum"), XS_NPC_SetPlatinum, file, "$$"); + newXSproto(strcpy(buf, "SetGrid"), XS_NPC_SetGrid, file, "$$"); + newXSproto(strcpy(buf, "SetSaveWaypoint"), XS_NPC_SetSaveWaypoint, file, "$$"); + newXSproto(strcpy(buf, "SetSp2"), XS_NPC_SetSp2, file, "$$"); + newXSproto(strcpy(buf, "GetWaypointMax"), XS_NPC_GetWaypointMax, file, "$"); + newXSproto(strcpy(buf, "GetGrid"), XS_NPC_GetGrid, file, "$"); + newXSproto(strcpy(buf, "GetSp2"), XS_NPC_GetSp2, file, "$"); + newXSproto(strcpy(buf, "GetNPCFactionID"), XS_NPC_GetNPCFactionID, file, "$"); + newXSproto(strcpy(buf, "GetPrimaryFaction"), XS_NPC_GetPrimaryFaction, file, "$"); + newXSproto(strcpy(buf, "GetNPCHate"), XS_NPC_GetNPCHate, file, "$$"); + newXSproto(strcpy(buf, "IsOnHatelist"), XS_NPC_IsOnHatelist, file, "$$"); + newXSproto(strcpy(buf, "SetNPCFactionID"), XS_NPC_SetNPCFactionID, file, "$$"); + newXSproto(strcpy(buf, "GetMaxDMG"), XS_NPC_GetMaxDMG, file, "$"); + newXSproto(strcpy(buf, "GetMinDMG"), XS_NPC_GetMinDMG, file, "$"); + newXSproto(strcpy(buf, "IsAnimal"), XS_NPC_IsAnimal, file, "$"); + newXSproto(strcpy(buf, "GetPetSpellID"), XS_NPC_GetPetSpellID, file, "$"); + newXSproto(strcpy(buf, "SetPetSpellID"), XS_NPC_SetPetSpellID, file, "$$"); + newXSproto(strcpy(buf, "GetMaxDamage"), XS_NPC_GetMaxDamage, file, "$$"); + newXSproto(strcpy(buf, "SetTaunting"), XS_NPC_SetTaunting, file, "$$"); + newXSproto(strcpy(buf, "PickPocket"), XS_NPC_PickPocket, file, "$$"); + newXSproto(strcpy(buf, "StartSwarmTimer"), XS_NPC_StartSwarmTimer, file, "$$"); + newXSproto(strcpy(buf, "DoClassAttacks"), XS_NPC_DoClassAttacks, file, "$$"); + newXSproto(strcpy(buf, "GetMaxWp"), XS_NPC_GetMaxWp, file, "$"); + newXSproto(strcpy(buf, "DisplayWaypointInfo"), XS_NPC_DisplayWaypointInfo, file, "$$"); + newXSproto(strcpy(buf, "CalculateNewWaypoint"), XS_NPC_CalculateNewWaypoint, file, "$"); + newXSproto(strcpy(buf, "AssignWaypoints"), XS_NPC_AssignWaypoints, file, "$$"); + newXSproto(strcpy(buf, "SetWaypointPause"), XS_NPC_SetWaypointPause, file, "$"); + newXSproto(strcpy(buf, "UpdateWaypoint"), XS_NPC_UpdateWaypoint, file, "$$"); + newXSproto(strcpy(buf, "StopWandering"), XS_NPC_StopWandering, file, "$"); + newXSproto(strcpy(buf, "ResumeWandering"), XS_NPC_ResumeWandering, file, "$"); + newXSproto(strcpy(buf, "PauseWandering"), XS_NPC_PauseWandering, file, "$$"); + newXSproto(strcpy(buf, "MoveTo"), XS_NPC_MoveTo, file, "$$$$"); + newXSproto(strcpy(buf, "NextGuardPosition"), XS_NPC_NextGuardPosition, file, "$"); + newXSproto(strcpy(buf, "SaveGuardSpot"), XS_NPC_SaveGuardSpot, file, "$;$"); + newXSproto(strcpy(buf, "IsGuarding"), XS_NPC_IsGuarding, file, "$"); + newXSproto(strcpy(buf, "AI_SetRoambox"), XS_NPC_AI_SetRoambox, file, "$$$$$$;$"); + newXSproto(strcpy(buf, "GetNPCSpellsID"), XS_NPC_GetNPCSpellsID, file, "$"); + newXSproto(strcpy(buf, "GetSpawnPointID"), XS_NPC_GetSpawnPointID, file, "$"); + newXSproto(strcpy(buf, "GetSpawnPointX"), XS_NPC_GetSpawnPointX, file, "$"); + newXSproto(strcpy(buf, "GetSpawnPointY"), XS_NPC_GetSpawnPointY, file, "$"); + newXSproto(strcpy(buf, "GetSpawnPointZ"), XS_NPC_GetSpawnPointZ, file, "$"); + newXSproto(strcpy(buf, "GetSpawnPointH"), XS_NPC_GetSpawnPointH, file, "$"); + newXSproto(strcpy(buf, "GetGuardPointX"), XS_NPC_GetGuardPointX, file, "$"); + newXSproto(strcpy(buf, "GetGuardPointY"), XS_NPC_GetGuardPointY, file, "$"); + newXSproto(strcpy(buf, "GetGuardPointZ"), XS_NPC_GetGuardPointZ, file, "$"); + newXSproto(strcpy(buf, "SetPrimSkill"), XS_NPC_SetPrimSkill, file, "$$"); + newXSproto(strcpy(buf, "SetSecSkill"), XS_NPC_SetSecSkill, file, "$$"); + newXSproto(strcpy(buf, "GetPrimSkill"), XS_NPC_GetPrimSkill, file, "$"); + newXSproto(strcpy(buf, "GetSecSkill"), XS_NPC_GetSecSkill, file, "$"); + newXSproto(strcpy(buf, "GetSwarmOwner"), XS_NPC_GetSwarmOwner, file, "$"); + newXSproto(strcpy(buf, "GetSwarmTarget"), XS_NPC_GetSwarmTarget, file, "$"); + newXSproto(strcpy(buf, "SetSwarmTarget"), XS_NPC_SetSwarmTarget, file, "$$"); + newXSproto(strcpy(buf, "ModifyNPCStat"), XS_NPC_ModifyNPCStat, file, "$$$"); + newXSproto(strcpy(buf, "AddAISpell"), XS_NPC_AddSpellToNPCList, file, "$$$$$$$"); + 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, "GetAccuracyRating"), XS_NPC_GetAccuracyRating, file, "$"); + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES + diff --git a/zone/perl_object.cpp b/zone/perl_object.cpp new file mode 100644 index 000000000..1458ddd59 --- /dev/null +++ b/zone/perl_object.cpp @@ -0,0 +1,966 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of tmp. Do not edit this file, edit tmp instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +#include "object.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + +XS(XS_Object_IsGroundSpawn); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_IsGroundSpawn) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::IsGroundSpawn(THIS)"); + { + Object * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsGroundSpawn(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + + + +XS(XS_Object_Close); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_Close) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::Close(THIS)"); + { + Object * THIS; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Close(); + } + XSRETURN_EMPTY; +} + + +XS(XS_Object_Delete); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_Delete) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Object::Delete(THIS, reset_state=false)"); + { + Object * THIS; + bool reset_state; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items < 2) + reset_state = false; + else { + reset_state = (bool)SvTRUE(ST(1)); + } + + THIS->Delete(reset_state); + } + XSRETURN_EMPTY; +} +XS(XS_Object_StartDecay); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_StartDecay) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::StartDecay(THIS)"); + { + Object * THIS; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->StartDecay(); + } + XSRETURN_EMPTY; +} + + +XS(XS_Object_DeleteItem); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_DeleteItem) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Object::DeleteItem(THIS, index)"); + { + Object * THIS; + uint8 index = (uint8)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->DeleteItem(index); + } + XSRETURN_EMPTY; +} + +XS(XS_Object_IsObject); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_IsObject) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::IsObject(THIS)"); + { + Object * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsObject(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + + +XS(XS_Object_Save); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_Save) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::Save(THIS)"); + { + Object * THIS; + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->Save(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + + +XS(XS_Object_SetID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_SetID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Object::SetID(THIS, set_id)"); + { + Object * THIS; + uint16 set_id = (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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetID(set_id); + } + XSRETURN_EMPTY; +} + + +XS(XS_Object_ClearUser); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_ClearUser) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::ClearUser(THIS)"); + { + Object * THIS; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->ClearUser(); + } + XSRETURN_EMPTY; +} + + +XS(XS_Object_GetDBID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_GetDBID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::GetDBID(THIS)"); + { + Object * THIS; + uint32 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetDBID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Object_GetID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_GetID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::GetID(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Object_GetX); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_GetX) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::GetX(THIS)"); + { + Object * THIS; + float 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetX(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Object_GetY); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_GetY) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::GetY(THIS)"); + { + Object * THIS; + float 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetY(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Object_GetZ); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_GetZ) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::GetZ(THIS)"); + { + Object * THIS; + float 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetZ(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Object_GetHeading); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_GetHeading) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::GetHeading(THIS)"); + { + Object * THIS; + float 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetHeadingData(); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Object_VarSave); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_VarSave) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::VarSave(THIS)"); + { + Object * THIS; + uint32 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->VarSave(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + + +XS(XS_Object_GetType); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_GetType) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::GetType(THIS)"); + { + Object * THIS; + uint32 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetType(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + + +XS(XS_Object_SetType); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_SetType) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Object::SetType(THIS, type)"); + { + Object * THIS; + uint32 type = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetType(type); + } + XSRETURN_EMPTY; +} + + +XS(XS_Object_GetIcon); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_GetIcon) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::GetIcon(THIS)"); + { + Object * THIS; + uint32 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetIcon(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + + +XS(XS_Object_SetIcon); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_SetIcon) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Object::SetIcon(THIS, icon)"); + { + Object * THIS; + uint32 icon = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetIcon(icon); + } + XSRETURN_EMPTY; +} + + +XS(XS_Object_GetItemID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_GetItemID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::GetItemID(THIS)"); + { + Object * THIS; + uint32 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetItemID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + + +XS(XS_Object_SetItemID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_SetItemID) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Object::SetItemID(THIS, itemid)"); + { + Object * THIS; + uint32 itemid = (uint32)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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetItemID(itemid); + } + XSRETURN_EMPTY; +} + +XS(XS_Object_SetLocation); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_SetLocation) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: Object::SetLocation(THIS, x, y, z)"); + { + Object * THIS; + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(ST(3)); + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetLocation(x, y, z); + } + XSRETURN_EMPTY; +} + +XS(XS_Object_SetX); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_SetX) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Object::SetX(THIS, XPos)"); + { + Object * THIS; + float pos = (float)SvNV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetX(pos); + } + XSRETURN_EMPTY; +} + +XS(XS_Object_SetY); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_SetY) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Object::SetY(THIS, YPos)"); + { + Object * THIS; + float pos = (float)SvNV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetY(pos); + } + XSRETURN_EMPTY; +} + +XS(XS_Object_SetZ); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_SetZ) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Object::SetZ(THIS, ZPos)"); + { + Object * THIS; + float pos = (float)SvNV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetZ(pos); + } + XSRETURN_EMPTY; +} + +XS(XS_Object_SetHeading); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_SetHeading) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Object::SetHeading(THIS, heading)"); + { + Object * THIS; + float heading = (float)SvNV(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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetHeading(heading); + } + XSRETURN_EMPTY; +} + +XS(XS_Object_SetModelName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_SetModelName) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: Object::SetModelName(THIS, name)"); + { + Object * THIS; + char * name = NULL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (items > 1) { name = (char *)SvPV_nolen(ST(1)); } + + THIS->SetModelName(name); + } + XSRETURN_EMPTY; +} +XS(XS_Object_GetModelName); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_GetModelName) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::GetModelName(THIS)"); + { + Object * THIS; + Const_char * 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetModelName(); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Object_Repop); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_Repop) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::Repop(THIS)"); + { + Object * THIS; + Const_char * 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + THIS->Repop(); + } + XSRETURN_EMPTY; +} + +XS(XS_Object_Depop); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_Depop) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::Depop(THIS)"); + { + Object * THIS; + Const_char * 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + THIS->Depop(); + } + XSRETURN_EMPTY; +} + + +XS(XS_Object_GetEntityVariable); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_GetEntityVariable) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Object::GetEntityVariable(THIS, id)"); + { + Object * THIS; + Const_char *id = SvPV_nolen(ST(1)); + Const_char * 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetEntityVariable(id); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_Object_EntityVariableExists); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_EntityVariableExists) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Object::EntityVariableExists(THIS, id)"); + { + Object * THIS; + Const_char *id = SvPV_nolen(ST(1)); + bool RETVAL; + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->EntityVariableExists(id); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Object_SetEntityVariable); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_SetEntityVariable) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Object::SetEntityVariable(THIS, id, var)"); + { + Object * THIS; + Const_char *id = SvPV_nolen(ST(1)); + const char * var = (const char *)SvPV_nolen(ST(2)); + + 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetEntityVariable(id, var); + } + XSRETURN_EMPTY; +} + + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_Object); /* prototype to pass -Wmissing-prototypes */ +XS(boot_Object) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + newXSproto(strcpy(buf, "Depop"),XS_Object_Depop, file, "$"); + newXSproto(strcpy(buf, "Repop"),XS_Object_Repop, file, "$"); + newXSproto(strcpy(buf, "SetModelName"),XS_Object_SetModelName, file, "$$"); + newXSproto(strcpy(buf, "GetModelName"),XS_Object_GetModelName, file, "$"); + newXSproto(strcpy(buf, "GetX"),XS_Object_GetX, file, "$"); + newXSproto(strcpy(buf, "GetY"),XS_Object_GetY, file, "$"); + newXSproto(strcpy(buf, "GetZ"),XS_Object_GetZ, file, "$"); + newXSproto(strcpy(buf, "GetHeading"),XS_Object_GetHeading, file, "$"); + newXSproto(strcpy(buf, "SetX"),XS_Object_SetX, file, "$$"); + newXSproto(strcpy(buf, "SetY"),XS_Object_SetY, file, "$$"); + newXSproto(strcpy(buf, "SetZ"),XS_Object_SetZ, file, "$$"); + newXSproto(strcpy(buf, "SetHeading"),XS_Object_SetHeading, file, "$$"); + newXSproto(strcpy(buf, "SetLocation"),XS_Object_SetLocation, file, "$$$$"); + newXSproto(strcpy(buf, "SetItemID"),XS_Object_SetItemID, file, "$$"); + newXSproto(strcpy(buf, "GetItemID"),XS_Object_GetItemID, file, "$"); + newXSproto(strcpy(buf, "SetIcon"),XS_Object_SetIcon, file, "$$"); + newXSproto(strcpy(buf, "GetIcon"),XS_Object_GetIcon, file, "$"); + newXSproto(strcpy(buf, "SetType"),XS_Object_SetType, file, "$$"); + newXSproto(strcpy(buf, "GetType"),XS_Object_GetType, file, "$"); + newXSproto(strcpy(buf, "GetDBID"),XS_Object_GetDBID, file, "$"); + newXSproto(strcpy(buf, "ClearUser"),XS_Object_ClearUser, file, "$"); + newXSproto(strcpy(buf, "SetID"),XS_Object_SetID, file, "$$"); + newXSproto(strcpy(buf, "GetID"),XS_Object_GetID, file, "$"); + newXSproto(strcpy(buf, "Save"),XS_Object_Save, file, "$"); + newXSproto(strcpy(buf, "VarSave"),XS_Object_VarSave, file, "$"); + newXSproto(strcpy(buf, "DeleteItem"),XS_Object_DeleteItem, file, "$$"); + newXSproto(strcpy(buf, "StartDecay"),XS_Object_StartDecay, file, "$$"); + newXSproto(strcpy(buf, "Delete"),XS_Object_Delete, file, "$$"); + newXSproto(strcpy(buf, "IsGroundSpawn"),XS_Object_IsGroundSpawn, file, "$"); + newXSproto(strcpy(buf, "Close"),XS_Object_Close, file, "$"); + newXSproto(strcpy(buf, "GetEntityVariable"), XS_Object_GetEntityVariable, file, "$$"); + newXSproto(strcpy(buf, "SetEntityVariable"), XS_Object_SetEntityVariable, file, "$$$"); + newXSproto(strcpy(buf, "EntityVariableExists"), XS_Object_EntityVariableExists, file, "$$"); + XSRETURN_YES; +} +#endif //EMBPERL_XS_CLASSES diff --git a/zone/perl_perlpacket.cpp b/zone/perl_perlpacket.cpp new file mode 100644 index 000000000..7ec92ba58 --- /dev/null +++ b/zone/perl_perlpacket.cpp @@ -0,0 +1,594 @@ +/* + * This file was generated automatically by xsubpp version 1.9508 from the + * contents of tmp. Do not edit this file, edit tmp instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +#include "perlpacket.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + +XS(XS_PerlPacket_new); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_new) +{ + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: PerlPacket::new(CLASS, opcode= \"OP_Unknown\", len= 0)"); + { + char * CLASS = (char *)SvPV_nolen(ST(0)); + PerlPacket * RETVAL; + char * opcode; + uint32 len; + + if (items < 2) + opcode = "OP_Unknown"; + else { + opcode = (char *)SvPV_nolen(ST(1)); + } + + if (items < 3) + len = 0; + else { + len = (uint32)SvUV(ST(2)); + } + + RETVAL = new PerlPacket(opcode, len); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "PerlPacket", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_PerlPacket_DESTROY); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_DESTROY) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: PerlPacket::DESTROY(THIS)"); + { + PerlPacket * THIS; + + if (SvROK(ST(0))) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not a reference"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + delete THIS; + } + XSRETURN_EMPTY; +} + +XS(XS_PerlPacket_SetOpcode); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_SetOpcode) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: PerlPacket::SetOpcode(THIS, opcode)"); + { + PerlPacket * THIS; + bool RETVAL; + char * opcode = (char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->SetOpcode(opcode); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_PerlPacket_Resize); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_Resize) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: PerlPacket::Resize(THIS, len)"); + { + PerlPacket * THIS; + uint32 len = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Resize(len); + } + XSRETURN_EMPTY; +} + +XS(XS_PerlPacket_SendTo); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_SendTo) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: PerlPacket::SendTo(THIS, who)"); + { + PerlPacket * THIS; + Client * who; + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + who = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "who is not of type Client"); + if(who == NULL) + Perl_croak(aTHX_ "who is NULL, avoiding crash."); + + THIS->SendTo(who); + } + XSRETURN_EMPTY; +} + +XS(XS_PerlPacket_SendToAll); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_SendToAll) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: PerlPacket::SendToAll(THIS)"); + { + PerlPacket * THIS; + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendToAll(); + } + XSRETURN_EMPTY; +} + +XS(XS_PerlPacket_Zero); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_Zero) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: PerlPacket::Zero(THIS)"); + { + PerlPacket * THIS; + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->Zero(); + } + XSRETURN_EMPTY; +} + +XS(XS_PerlPacket_FromArray); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_FromArray) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: PerlPacket::FromArray(THIS, numbers, length)"); + { + PerlPacket * THIS; + int * numbers; + uint32 length = (uint32)SvUV(ST(2)); + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + AV *av_numbers; + if (SvROK(ST(1)) && SvTYPE(SvRV(ST(1)))==SVt_PVAV) + av_numbers = (AV*)SvRV(ST(1)); + else + Perl_croak(aTHX_ "numbers is not an array reference"); + I32 len_numbers = av_len(av_numbers) + 1; + I32 ix_numbers; + numbers = new int[len_numbers]; + for(ix_numbers = 0; ix_numbers < len_numbers; ix_numbers ++) { + SV **tmp = av_fetch(av_numbers, ix_numbers, 0); + if(tmp == NULL || *tmp == NULL) { + numbers[ix_numbers] = 0; + continue; + } + numbers[ix_numbers] = (int)SvIV(*tmp); + }; + + THIS->FromArray(numbers, length); + } + XSRETURN_EMPTY; +} + +XS(XS_PerlPacket_SetByte); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_SetByte) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: PerlPacket::SetByte(THIS, pos, val)"); + { + PerlPacket * THIS; + uint32 pos = (uint32)SvUV(ST(1)); + uint8 val = (uint8)SvUV(ST(2)); + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetByte(pos, val); + } + XSRETURN_EMPTY; +} + +XS(XS_PerlPacket_SetShort); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_SetShort) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: PerlPacket::SetShort(THIS, pos, val)"); + { + PerlPacket * THIS; + uint32 pos = (uint32)SvUV(ST(1)); + uint16 val = (uint16)SvUV(ST(2)); + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetShort(pos, val); + } + XSRETURN_EMPTY; +} + +XS(XS_PerlPacket_SetLong); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_SetLong) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: PerlPacket::SetLong(THIS, pos, val)"); + { + PerlPacket * THIS; + uint32 pos = (uint32)SvUV(ST(1)); + uint32 val = (uint32)SvUV(ST(2)); + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetLong(pos, val); + } + XSRETURN_EMPTY; +} + +XS(XS_PerlPacket_SetFloat); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_SetFloat) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: PerlPacket::SetFloat(THIS, pos, val)"); + { + PerlPacket * THIS; + uint32 pos = (uint32)SvUV(ST(1)); + float val = (float)SvNV(ST(2)); + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetFloat(pos, val); + } + XSRETURN_EMPTY; +} + +XS(XS_PerlPacket_SetString); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_SetString) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: PerlPacket::SetString(THIS, pos, str)"); + { + PerlPacket * THIS; + uint32 pos = (uint32)SvUV(ST(1)); + char * str = (char *)SvPV_nolen(ST(2)); + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetString(pos, str); + } + XSRETURN_EMPTY; +} + +XS(XS_PerlPacket_SetEQ1319); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_SetEQ1319) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: PerlPacket::SetEQ1319(THIS, pos, part13, part19)"); + { + PerlPacket * THIS; + uint32 pos = (uint32)SvUV(ST(1)); + float part13 = (float)SvNV(ST(2)); + float part19 = (float)SvNV(ST(3)); + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetEQ1319(pos, part13, part19); + } + XSRETURN_EMPTY; +} + +XS(XS_PerlPacket_SetEQ1913); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_SetEQ1913) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: PerlPacket::SetEQ1913(THIS, pos, part19, part13)"); + { + PerlPacket * THIS; + uint32 pos = (uint32)SvUV(ST(1)); + float part19 = (float)SvNV(ST(2)); + float part13 = (float)SvNV(ST(3)); + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SetEQ1913(pos, part19, part13); + } + XSRETURN_EMPTY; +} + +XS(XS_PerlPacket_GetByte); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_GetByte) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: PerlPacket::GetByte(THIS, pos)"); + { + PerlPacket * THIS; + uint8 RETVAL; + dXSTARG; + uint32 pos = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetByte(pos); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_PerlPacket_GetShort); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_GetShort) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: PerlPacket::GetShort(THIS, pos)"); + { + PerlPacket * THIS; + uint16 RETVAL; + dXSTARG; + uint32 pos = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetShort(pos); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_PerlPacket_GetLong); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_GetLong) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: PerlPacket::GetLong(THIS, pos)"); + { + PerlPacket * THIS; + uint32 RETVAL; + dXSTARG; + uint32 pos = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetLong(pos); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_PerlPacket_GetFloat); /* prototype to pass -Wmissing-prototypes */ +XS(XS_PerlPacket_GetFloat) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: PerlPacket::GetFloat(THIS, pos)"); + { + PerlPacket * THIS; + float RETVAL; + dXSTARG; + uint32 pos = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "PerlPacket")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(PerlPacket *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type PerlPacket"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetFloat(pos); + XSprePUSH; PUSHn((double)RETVAL); + } + XSRETURN(1); +} + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_PerlPacket); /* prototype to pass -Wmissing-prototypes */ +XS(boot_PerlPacket) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "new"), XS_PerlPacket_new, file, "$;$$"); + newXSproto(strcpy(buf, "DESTROY"), XS_PerlPacket_DESTROY, file, "$"); + newXSproto(strcpy(buf, "SetOpcode"), XS_PerlPacket_SetOpcode, file, "$$"); + newXSproto(strcpy(buf, "Resize"), XS_PerlPacket_Resize, file, "$$"); + newXSproto(strcpy(buf, "SendTo"), XS_PerlPacket_SendTo, file, "$$"); + newXSproto(strcpy(buf, "SendToAll"), XS_PerlPacket_SendToAll, file, "$"); + newXSproto(strcpy(buf, "Zero"), XS_PerlPacket_Zero, file, "$"); + newXSproto(strcpy(buf, "FromArray"), XS_PerlPacket_FromArray, file, "$$$"); + newXSproto(strcpy(buf, "SetByte"), XS_PerlPacket_SetByte, file, "$$$"); + newXSproto(strcpy(buf, "SetShort"), XS_PerlPacket_SetShort, file, "$$$"); + newXSproto(strcpy(buf, "SetLong"), XS_PerlPacket_SetLong, file, "$$$"); + newXSproto(strcpy(buf, "SetFloat"), XS_PerlPacket_SetFloat, file, "$$$"); + newXSproto(strcpy(buf, "SetString"), XS_PerlPacket_SetString, file, "$$$"); + newXSproto(strcpy(buf, "SetEQ1319"), XS_PerlPacket_SetEQ1319, file, "$$$$"); + newXSproto(strcpy(buf, "SetEQ1913"), XS_PerlPacket_SetEQ1913, file, "$$$$"); + newXSproto(strcpy(buf, "GetByte"), XS_PerlPacket_GetByte, file, "$$"); + newXSproto(strcpy(buf, "GetShort"), XS_PerlPacket_GetShort, file, "$$"); + newXSproto(strcpy(buf, "GetLong"), XS_PerlPacket_GetLong, file, "$$"); + newXSproto(strcpy(buf, "GetFloat"), XS_PerlPacket_GetFloat, file, "$$"); + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES + diff --git a/zone/perl_questitem.cpp b/zone/perl_questitem.cpp new file mode 100644 index 000000000..12e5f38c6 --- /dev/null +++ b/zone/perl_questitem.cpp @@ -0,0 +1,277 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#include "client.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +#include "../common/Item.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + +XS(XS_QuestItem_GetName); +XS(XS_QuestItem_GetName) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: QuestItem::GetName(THIS)"); + { + ItemInst * THIS; + Const_char * RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "QuestItem")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(ItemInst *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type ItemInst"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetItem()->Name; + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + } + XSRETURN(1); +} + +XS(XS_QuestItem_SetScale); +XS(XS_QuestItem_SetScale) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: QuestItem::SetScale(THIS, scale factor)"); + { + ItemInst * THIS; + float Mult; + + if (sv_derived_from(ST(0), "QuestItem")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(ItemInst *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type ItemInst"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + Mult = (float)SvNV(ST(1)); + + if(THIS->IsScaling()) { + ((EvoItemInst*)THIS)->SetExp((int)(Mult*10000+.5)); + } + } + XSRETURN_EMPTY; +} + +XS(XS_QuestItem_ItemSay); +XS(XS_QuestItem_ItemSay) +{ + dXSARGS; + if (items != 2 && items != 3) + Perl_croak(aTHX_ "Usage: QuestItem::ItemSay(THIS, text [, language])"); + { + ItemInst* THIS; + Const_char* text; + int lang = 0; + + if (sv_derived_from(ST(0), "QuestItem")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(ItemInst *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type ItemInst"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + text = SvPV_nolen(ST(1)); + if(items == 3) + lang = (int)SvUV(ST(2)); + + quest_manager.GetInitiator()->ChannelMessageSend(THIS->GetItem()->Name, 0, 8, lang, 100, text); + } + XSRETURN_EMPTY; +} + +XS(XS_QuestItem_IsType); /* prototype to pass -Wmissing-prototypes */ +XS(XS_QuestItem_IsType) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: QuestItem::IsType(THIS, type)"); + { + ItemInst* THIS; + bool RETVAL; + uint32 type = (int32)SvIV(ST(1)); + + if (sv_derived_from(ST(0), "QuestItem")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(ItemInst *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type ItemInst"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsType((ItemClass)type); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_QuestItem_IsAttuned); /* prototype to pass -Wmissing-prototypes */ +XS(XS_QuestItem_IsAttuned) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: QuestItem::IsAttuned(THIS)"); + { + ItemInst* THIS; + bool RETVAL; + + if (sv_derived_from(ST(0), "QuestItem")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(ItemInst *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type ItemInst"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsInstNoDrop(); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_QuestItem_GetCharges); /* prototype to pass -Wmissing-prototypes */ +XS(XS_QuestItem_GetCharges) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: QuestItem::GetCharges(THIS)"); + { + ItemInst* THIS; + int16 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "QuestItem")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(ItemInst *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type ItemInst"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetCharges(); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_QuestItem_GetAugment); /* prototype to pass -Wmissing-prototypes */ +XS(XS_QuestItem_GetAugment) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: QuestItem::GetAugment(THIS, augment_id)"); + { + ItemInst* THIS; + int16 slot_id = (int16)SvIV(ST(1)); + ItemInst* RETVAL; + + if (sv_derived_from(ST(0), "QuestItem")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(ItemInst *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type ItemInst"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetAugment(slot_id); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "QuestItem", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_QuestItem_GetID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_QuestItem_GetID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: QuestItem::GetID(THIS)"); + { + ItemInst* THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "QuestItem")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(ItemInst *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type ItemInst"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetItem()->ID; + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +#ifdef __cplusplus +extern "C" +#endif + +XS(boot_QuestItem); +XS(boot_QuestItem) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "GetName"), XS_QuestItem_GetName, file, "$"); + newXSproto(strcpy(buf, "SetScale"), XS_QuestItem_SetScale, file, "$"); + newXSproto(strcpy(buf, "ItemSay"), XS_QuestItem_ItemSay, file, "$"); + newXSproto(strcpy(buf, "IsType"), XS_QuestItem_IsType, file, "$$"); + newXSproto(strcpy(buf, "IsAttuned"), XS_QuestItem_IsAttuned, file, "$"); + newXSproto(strcpy(buf, "GetCharges"), XS_QuestItem_GetCharges, file, "$"); + newXSproto(strcpy(buf, "GetAugment"), XS_QuestItem_GetAugment, file, "$$"); + newXSproto(strcpy(buf, "GetID"), XS_QuestItem_GetID, file, "$"); + + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES diff --git a/zone/perl_raids.cpp b/zone/perl_raids.cpp new file mode 100644 index 000000000..b44f39668 --- /dev/null +++ b/zone/perl_raids.cpp @@ -0,0 +1,618 @@ +/* + * This file was generated automatically by ExtUtils::ParseXS version 2.18 from the + * contents of raids.h.xs. Do not edit this file, edit raids.h.xs instead. + * + * ANY CHANGES MADE HERE WILL BE LOST! + * + */ + + +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/debug.h" +#include "embperl.h" + +#include "raids.h" +#include "client.h" + +#ifdef THIS /* this macro seems to leak out on some systems */ +#undef THIS +#endif + + +XS(XS_Raid_IsRaidMember); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_IsRaidMember) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Raid::IsRaidMember(THIS, name)"); + { + Raid * THIS; + bool RETVAL; + const char* name = (char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsRaidMember(name); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Raid_CastGroupSpell); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_CastGroupSpell) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: Raid::CastGroupSpell(THIS, caster, spellid, gid)"); + { + Raid * THIS; + Mob* caster; + uint16 spellid = (uint16)SvUV(ST(2)); + uint32 gid = (uint32)SvUV(ST(3)); + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + caster = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "caster is not of type Mob"); + if(caster == NULL) + Perl_croak(aTHX_ "caster is NULL, avoiding crash."); + + THIS->CastGroupSpell(caster, spellid, gid); + } + XSRETURN_EMPTY; +} + +XS(XS_Raid_GroupCount); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_GroupCount) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Raid::GroupCount(THIS, gid)"); + { + Raid * THIS; + uint8 RETVAL; + dXSTARG; + uint32 gid = (uint32)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GroupCount(gid); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Raid_RaidCount); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_RaidCount) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Raid::RaidCount(THIS)"); + { + Raid * THIS; + uint8 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->RaidCount(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Raid_GetGroup); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_GetGroup) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Raid::GetGroup(THIS, name)"); + { + Raid * THIS; + uint32 RETVAL; + dXSTARG; + const char* name = (char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetGroup(name); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Raid_SplitExp); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_SplitExp) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Raid::SplitExp(THIS, exp, other)"); + { + Raid * THIS; + uint32 exp = (uint32)SvUV(ST(1)); + Mob* other; + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(2), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(2))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + + THIS->SplitExp(exp, other); + } + XSRETURN_EMPTY; +} + +XS(XS_Raid_GetTotalRaidDamage); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_GetTotalRaidDamage) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Raid::GetTotalRaidDamage(THIS, other)"); + { + Raid * THIS; + uint32 RETVAL; + dXSTARG; + Mob* other; + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + other = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "other is not of type Mob"); + if(other == NULL) + Perl_croak(aTHX_ "other is NULL, avoiding crash."); + + RETVAL = THIS->GetTotalRaidDamage(other); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Raid_SplitMoney); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_SplitMoney) +{ + dXSARGS; + if (items != 5) + Perl_croak(aTHX_ "Usage: Raid::SplitMoney(THIS, copper, silver, gold, platinum)"); + { + Raid * THIS; + uint32 copper = (uint32)SvUV(ST(1)); + uint32 silver = (uint32)SvUV(ST(2)); + uint32 gold = (uint32)SvUV(ST(3)); + uint32 platinum = (uint32)SvUV(ST(4)); + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SplitMoney(copper, silver, gold, platinum); + } + XSRETURN_EMPTY; +} + +XS(XS_Raid_BalanceHP); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_BalanceHP) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Raid::BalanceHP(THIS, penalty, gid)"); + { + Raid * THIS; + int32 penalty = (int32)SvUV(ST(1)); + uint32 gid = (uint32)SvUV(ST(2)); + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->BalanceHP(penalty, gid); + } + XSRETURN_EMPTY; +} + +XS(XS_Raid_IsLeader); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_IsLeader) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Raid::IsLeader(THIS, name)"); + { + Raid * THIS; + bool RETVAL; + const char* name = (char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsLeader(name); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Raid_IsGroupLeader); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_IsGroupLeader) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Raid::IsGroupLeader(THIS, who)"); + { + Raid * THIS; + bool RETVAL; + const char* who = (char *)SvPV_nolen(ST(1)); + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->IsGroupLeader(who); + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + } + XSRETURN(1); +} + +XS(XS_Raid_GetHighestLevel); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_GetHighestLevel) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Raid::GetHighestLevel(THIS)"); + { + Raid * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetHighestLevel(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Raid_GetLowestLevel); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_GetLowestLevel) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Raid::GetLowestLevel(THIS)"); + { + Raid * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetLowestLevel(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Raid_GetClientByIndex); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_GetClientByIndex) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Raid::GetClientByIndex(THIS, index)"); + { + Raid * THIS; + Client * RETVAL; + uint16 index = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetClientByIndex(index); + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Client", (void*)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Raid_TeleportGroup); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_TeleportGroup) +{ + dXSARGS; + if (items != 8) + Perl_croak(aTHX_ "Usage: Raid::TeleportGroup(THIS, sender, zoneID, x, y, z, heading, gid)"); + { + Raid * THIS; + Mob* sender; + uint32 zoneID = (uint32)SvUV(ST(2)); + float x = (float)SvNV(ST(3)); + float y = (float)SvNV(ST(4)); + float z = (float)SvNV(ST(5)); + float heading = (float)SvNV(ST(6)); + uint32 gid = (uint32)SvUV(ST(7)); + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + sender = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "sender is not of type Mob"); + if(sender == NULL) + Perl_croak(aTHX_ "sender is NULL, avoiding crash."); + + THIS->TeleportGroup(sender, zoneID, 0, x, y, z, heading, gid); + } + XSRETURN_EMPTY; +} + +XS(XS_Raid_TeleportRaid); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_TeleportRaid) +{ + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: Raid::TeleportRaid(THIS, sender, zoneID, x, y, z, heading)"); + { + Raid * THIS; + Mob* sender; + uint32 zoneID = (uint32)SvUV(ST(2)); + float x = (float)SvNV(ST(3)); + float y = (float)SvNV(ST(4)); + float z = (float)SvNV(ST(5)); + float heading = (float)SvNV(ST(6)); + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + if (sv_derived_from(ST(1), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(1))); + sender = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "sender is not of type Mob"); + if(sender == NULL) + Perl_croak(aTHX_ "sender is NULL, avoiding crash."); + + THIS->TeleportRaid(sender, zoneID, 0, x, y, z, heading); + } + XSRETURN_EMPTY; +} + +XS(XS_Raid_GetID); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Raid_GetID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Raid::GetID(THIS)"); + { + Raid * THIS; + uint32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetID(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Raid_GetMember); +XS(XS_Raid_GetMember) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Raid::GetMember(THIS, index)"); + { + Raid * THIS; + Client* RETVAL = NULL; + dXSTARG; + + if (sv_derived_from(ST(0), "Raid")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Raid *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Raid"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + int index = (int)SvUV(ST(1)); + if (index < 0 || index > 71) + RETVAL = NULL; + else { + if(THIS->members[index].member != NULL) + RETVAL = THIS->members[index].member->CastToClient(); + } + + ST(0) = sv_newmortal(); + sv_setref_pv(ST(0), "Client", (void*)RETVAL); + } + XSRETURN(1); +} + +#ifdef __cplusplus +extern "C" +#endif +XS(boot_Raid); /* prototype to pass -Wmissing-prototypes */ +XS(boot_Raid) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = 0; + + if(items != 1) + fprintf(stderr, "boot_quest does not take any arguments."); + char buf[128]; + + //add the strcpy stuff to get rid of const warnings.... + + + + XS_VERSION_BOOTCHECK ; + + newXSproto(strcpy(buf, "IsRaidMember"), XS_Raid_IsRaidMember, file, "$$"); + newXSproto(strcpy(buf, "CastGroupSpell"), XS_Raid_CastGroupSpell, file, "$$$$"); + newXSproto(strcpy(buf, "GroupCount"), XS_Raid_GroupCount, file, "$$"); + newXSproto(strcpy(buf, "RaidCount"), XS_Raid_RaidCount, file, "$"); + newXSproto(strcpy(buf, "GetGroup"), XS_Raid_GetGroup, file, "$$"); + newXSproto(strcpy(buf, "SplitExp"), XS_Raid_SplitExp, file, "$$$"); + newXSproto(strcpy(buf, "GetTotalRaidDamage"), XS_Raid_GetTotalRaidDamage, file, "$$"); + newXSproto(strcpy(buf, "SplitMoney"), XS_Raid_SplitMoney, file, "$$$$$"); + newXSproto(strcpy(buf, "BalanceHP"), XS_Raid_BalanceHP, file, "$$$"); + newXSproto(strcpy(buf, "IsLeader"), XS_Raid_IsLeader, file, "$$"); + newXSproto(strcpy(buf, "IsGroupLeader"), XS_Raid_IsGroupLeader, file, "$$"); + newXSproto(strcpy(buf, "GetHighestLevel"), XS_Raid_GetHighestLevel, file, "$"); + newXSproto(strcpy(buf, "GetLowestLevel"), XS_Raid_GetLowestLevel, file, "$"); + newXSproto(strcpy(buf, "GetClientByIndex"), XS_Raid_GetClientByIndex, file, "$$"); + newXSproto(strcpy(buf, "TeleportGroup"), XS_Raid_TeleportGroup, file, "$$$$$$$$"); + newXSproto(strcpy(buf, "TeleportRaid"), XS_Raid_TeleportRaid, file, "$$$$$$$"); + newXSproto(strcpy(buf, "GetID"), XS_Raid_GetID, file, "$"); + newXSproto(strcpy(buf, "GetMember"), XS_Raid_GetMember, file, "$$"); + XSRETURN_YES; +} + +#endif //EMBPERL_XS_CLASSES + diff --git a/zone/perlpacket.cpp b/zone/perlpacket.cpp new file mode 100644 index 000000000..2e77a479b --- /dev/null +++ b/zone/perlpacket.cpp @@ -0,0 +1,201 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 +#include "perlpacket.h" +#include "client.h" +#include "entity.h" +#include "../common/opcodemgr.h" +#include "../common/packet_dump.h" +#include "../common/MiscFunctions.h" + +PerlPacket::PerlPacket(const char *opcode, uint32 length) { + SetOpcode(opcode); + packet = NULL; + len = 0; + Resize(length); +} + +PerlPacket::~PerlPacket() { + if(packet != NULL) + safe_delete_array(packet); +} + +bool PerlPacket::SetOpcode(const char *opcode) { +#ifndef WIN32 +#warning Rewrite this! +#endif + op = OP_Unknown; +// op = ZoneOpcodeManager->NameSearch(opcode); + return(op != OP_Unknown); +} + +void PerlPacket::Resize(uint32 length) { + Zero(); + if(len == length) + return; + if(packet != NULL) + safe_delete_array(packet); + len = length; + if(len == 0) + packet = NULL; + else { + packet = new unsigned char[len]; + Zero(); + } +} + +//sending functions +void PerlPacket::SendTo(Client *who) { + if(!who || op == OP_Unknown || (len > 0 && packet == NULL)) + return; + + EQApplicationPacket *outapp = new EQApplicationPacket(op, len); + if(len > 0) + memcpy(outapp->pBuffer, packet, len); + +#ifndef WIN32 +#warning Rewrite this! +#endif + // printf("Created this packet with PerlPacket: OP: %s\n", ZoneOpcodeManager->EmuToName(op)); + DumpPacket(outapp); + + who->FastQueuePacket(&outapp); +} + +void PerlPacket::SendToAll() { + if(op == OP_Unknown || (len > 0 && packet == NULL)) + return; + + EQApplicationPacket *outapp = new EQApplicationPacket(op, len); + if(len > 0) + memcpy(outapp->pBuffer, packet, len); + entity_list.QueueClients(NULL, outapp, false); + safe_delete(outapp); +} + +//editing +void PerlPacket::Zero() { + if(len == 0 || packet == NULL) + return; + memset(packet, 0, len); +} + +void PerlPacket::FromArray(int numbers[], uint32 length) { + if(length == 0) + return; + Resize(length); + uint32 r; + for(r = 0; r < length; r++) { + packet[r] = numbers[r] & 0xFF; + } +} + +void PerlPacket::SetByte(uint32 pos, uint8 val) { + if(pos + sizeof(val) > len || packet == NULL) + return; + uint8 *p = (uint8 *) (packet + pos); + *p = val; +} + +void PerlPacket::SetShort(uint32 pos, uint16 val) { + if(pos + sizeof(val) > len || packet == NULL) + return; + uint16 *p = (uint16 *) (packet + pos); + *p = val; +} + +void PerlPacket::SetLong(uint32 pos, uint32 val) { + if(pos + sizeof(val) > len || packet == NULL) + return; + uint32 *p = (uint32 *) (packet + pos); + *p = val; +} + +void PerlPacket::SetFloat(uint32 pos, float val) { + if(pos + sizeof(val) > len || packet == NULL) + return; + float *p = (float *) (packet + pos); + *p = val; +} + +void PerlPacket::SetString(uint32 pos, char *str) { + int slen = strlen(str); + if(pos + slen > len || packet == NULL) + return; + strcpy((char *)(packet+pos), str); +} + +#pragma pack(1) +struct EQ1319 { + int32 part13:13, + part19:19; +}; +struct EQ1913 { + int32 part19:19, + part13:13; +}; +#pragma pack() + +void PerlPacket::SetEQ1319(uint32 pos, float part13, float part19) { + if(pos + sizeof(EQ1319) > len || packet == NULL) + return; + EQ1319 *p = (EQ1319 *) (packet + pos); + p->part19 = FloatToEQ19(part19); + p->part13 = FloatToEQ13(part13); +} + +void PerlPacket::SetEQ1913(uint32 pos, float part19, float part13) { + if(pos + sizeof(EQ1913) > len || packet == NULL) + return; + EQ1913 *p = (EQ1913 *) (packet + pos); + p->part19 = FloatToEQ19(part19); + p->part13 = FloatToEQ13(part13); +} + +//reading +uint8 PerlPacket::GetByte(uint32 pos) { + if(pos + sizeof(uint8) > len || packet == NULL) + return(0); + uint8 *p = (uint8 *) (packet + pos); + return(*p); +} + +uint16 PerlPacket::GetShort(uint32 pos) { + if(pos + sizeof(uint16) > len || packet == NULL) + return(0); + uint16 *p = (uint16 *) (packet + pos); + return(*p); +} + +uint32 PerlPacket::GetLong(uint32 pos) { + if(pos + sizeof(uint32) > len || packet == NULL) + return(0); + uint32 *p = (uint32 *) (packet + pos); + return(*p); +} + +float PerlPacket::GetFloat(uint32 pos) { + if(pos + sizeof(float) > len || packet == NULL) + return(0); + float *p = (float *) (packet + pos); + return(*p); +} + + + diff --git a/zone/perlpacket.h b/zone/perlpacket.h new file mode 100644 index 000000000..a7ed9bca3 --- /dev/null +++ b/zone/perlpacket.h @@ -0,0 +1,68 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 PERLPACKET_H +#define PERLPACKET_H + +#include +#include +using namespace std; + +#include "../common/emu_opcodes.h" + +class Client; + +class PerlPacket { +public: + PerlPacket(const char *opcode = "OP_Unknown", uint32 len = 0); + ~PerlPacket(); + + bool SetOpcode(const char *opcode); + void Resize(uint32 len); + + //sending functions + void SendTo(Client *who); + void SendToAll(); + + //editing + void Zero(); + void FromArray(int numbers[], uint32 length); + void SetByte(uint32 pos, uint8 val); + void SetShort(uint32 pos, uint16 val); + void SetLong(uint32 pos, uint32 val); + void SetFloat(uint32 pos, float val); + void SetString(uint32 pos, char *str); + + void SetEQ1319(uint32 pos, float part13, float part19); + void SetEQ1913(uint32 pos, float part19, float part13); + + //reading + uint8 GetByte(uint32 pos); + uint16 GetShort(uint32 pos); + uint32 GetLong(uint32 pos); + float GetFloat(uint32 pos); + +protected: + EmuOpcode op; + uint32 len; + unsigned char *packet; +}; + + + + +#endif diff --git a/zone/perlparser.cpp b/zone/perlparser.cpp new file mode 100644 index 000000000..54fecaab4 --- /dev/null +++ b/zone/perlparser.cpp @@ -0,0 +1,3718 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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 "features.h" + +#ifdef EMBPERL +#ifdef EMBPERL_XS + +#include "../common/debug.h" +#include "perlparser.h" +#include "questmgr.h" +#include "embxs.h" +#include "entity.h" +#include "../common/MiscFunctions.h" +#include "zone.h" +extern Zone* zone; + +/* + +Some useful perl API info: + +SvUV == string to unsigned value (char->ulong) +SvIV == string to signed value (char->long) +SvNV == string to real value (float,double) +SvPV_nolen == string with no length restriction + + +*/ + +PerlXSParser::PerlXSParser() : PerlembParser() { + //we cannot rely on PerlembParser to call the right map_funs because + //our virtual table is not set up until after we call them, so we need to move + //the call to ReloadQuests out of the constructor. +} + +void PerlXSParser::map_funs() { + _empty_sv = newSV(0); + + perl->eval( + "{" + "package quest;" + "&boot_quest;" //load our quest XS +#ifdef EMBPERL_XS_CLASSES + "package Mob;" + "&boot_Mob;" //load our Mob XS + + "package Client;" + "our @ISA = qw(Mob);" //client inherits mob. + "&boot_Mob;" //load our Mob XS + "&boot_Client;" //load our Client XS + + "package NPC;" + "our @ISA = qw(Mob);" //NPC inherits mob. + "&boot_Mob;" //load our Mob XS + "&boot_NPC;" //load our NPC XS + + "package Corpse;" + "our @ISA = qw(Mob);" //Corpse inherits mob. + "&boot_Mob;" //load our Mob XS + "&boot_Corpse;" //load our Mob XS + + "package EntityList;" + "&boot_EntityList;" //load our EntityList XS + + "package PerlPacket;" + "&boot_PerlPacket;" //load our PerlPacket XS + + "package Group;" + "&boot_Group;" //load our Group XS + + "package Raid;" + "&boot_Raid;" //load our Raid XS + + "package QuestItem;" + "&boot_QuestItem;" // load quest Item XS + + "package HateEntry;" + "&boot_HateEntry;" // load quest Hate XS + + "package Object;" + "&boot_Object;" // load quest Object XS + + "package Doors;" + "&boot_Doors;" // load quest Doors XS + +#endif + "package main;" + "}" + );//eval +} + +void PerlXSParser::SendCommands(const char * pkgprefix, const char *event, uint32 npcid, Mob* other, Mob* mob, ItemInst* iteminst) +{ + if(!perl) + return; + _ZP(PerlXSParser_SendCommands); + + if(mob && mob->IsClient()) + quest_manager.StartQuest(other, mob->CastToClient(), iteminst); + else + quest_manager.StartQuest(other, NULL, NULL); + + try { + + std::string cmd = "package " + (std::string)(pkgprefix) + (std::string)(";"); + perl->eval(cmd.c_str()); + +#ifdef EMBPERL_XS_CLASSES + char namebuf[64]; + + //init a couple special vars: client, npc, entity_list + Client *curc = quest_manager.GetInitiator(); + snprintf(namebuf, 64, "%s::client", pkgprefix); + SV *client = get_sv(namebuf, true); + if(curc != NULL) { + sv_setref_pv(client, "Client", curc); + } else { + //clear out the value, mainly to get rid of blessedness + sv_setsv(client, _empty_sv); + } + + //only export NPC if it's a npc quest + if(!other->IsClient()){ + NPC *curn = quest_manager.GetNPC(); + snprintf(namebuf, 64, "%s::npc", pkgprefix); + SV *npc = get_sv(namebuf, true); + sv_setref_pv(npc, "NPC", curn); + } + + //only export QuestItem if it's an item quest + if(iteminst) { + ItemInst* curi = quest_manager.GetQuestItem(); + snprintf(namebuf, 64, "%s::questitem", pkgprefix); + SV *questitem = get_sv(namebuf, true); + sv_setref_pv(questitem, "QuestItem", curi); + } + + snprintf(namebuf, 64, "%s::entity_list", pkgprefix); + SV *el = get_sv(namebuf, true); + sv_setref_pv(el, "EntityList", &entity_list); +#endif + + //now call the requested sub + perl->dosub(std::string(pkgprefix).append("::").append(event).c_str()); + + } catch(const char * err) { + + //try to reduce some of the console spam... + //todo: tweak this to be more accurate at deciding what to filter (we don't want to gag legit errors) + if(!strstr(err,"Undefined subroutine")) + LogFile->write(EQEMuLog::Status, "Script error: %s::%s - %s", pkgprefix, event, err); + } + + quest_manager.EndQuest(); +} + + +#ifdef EMBPERL_XS_CLASSES + +//Any creation of new Client objects gets the current quest Client +XS(XS_Client_new); +XS(XS_Client_new) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Client::new()"); + { + Client * RETVAL; + + RETVAL = quest_manager.GetInitiator(); + ST(0) = sv_newmortal(); + if(RETVAL) + sv_setref_pv(ST(0), "Client", (void*)RETVAL); + } + XSRETURN(1); +} + +//Any creation of new NPC objects gets the current quest NPC +XS(XS_NPC_new); +XS(XS_NPC_new) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::new()"); + { + NPC * RETVAL; + + RETVAL = quest_manager.GetNPC(); + ST(0) = sv_newmortal(); + if(RETVAL) + sv_setref_pv(ST(0), "NPC", (void*)RETVAL); + } + XSRETURN(1); +} + +//Any creation of new NPC objects gets the current quest NPC +XS(XS_EntityList_new); +XS(XS_EntityList_new) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: EntityList::new()"); + { + EntityList * RETVAL; + + RETVAL = &entity_list; + ST(0) = sv_newmortal(); + if(RETVAL) + sv_setref_pv(ST(0), "EntityList", (void*)RETVAL); + } + XSRETURN(1); +} + +//Any creation of new quest items gets the current quest item +XS(XS_QuestItem_new); +XS(XS_QuestItem_new) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: QuestItem::new()"); + + ItemInst* RETVAL; + + RETVAL = quest_manager.GetQuestItem(); + ST(0) = sv_newmortal(); + if(RETVAL) + sv_setref_pv(ST(0), "QuestItem", (void*)RETVAL); + + XSRETURN(1); +} + +//Any creation of new quest items gets the current quest item +XS(XS_MobList_new); +XS(XS_MobList_new) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: MobList::new()"); + + ListElement* RETVAL; + + RETVAL = NULL; + ST(0) = sv_newmortal(); + if(RETVAL) + sv_setref_pv(ST(0), "MobList", (void*)RETVAL); + + XSRETURN(1); +} + +#endif //EMBPERL_XS_CLASSES + + +XS(XS__echo); // prototype to pass -Wmissing-prototypes +XS(XS__echo) { + dXSARGS; + + if (items != 2) + Perl_croak(aTHX_ "Usage: echo(id#, str)"); + + quest_manager.echo(SvUV(ST(0)), SvPV_nolen(ST(1))); + + XSRETURN_EMPTY; +} + +XS(XS__say); // prototype to pass -Wmissing-prototypes +XS(XS__say) { + dXSARGS; + + if (items == 1) + quest_manager.say(SvPV_nolen(ST(0))); + else if (items == 2) + quest_manager.say(SvPV_nolen(ST(0)), (int)SvIV(ST(1))); + else + Perl_croak(aTHX_ "Usage: say(str [, language])"); + + XSRETURN_EMPTY; +} + +XS(XS__me); // prototype to pass -Wmissing-prototypes +XS(XS__me) { + dXSARGS; + + if (items != 1) + Perl_croak(aTHX_ "Usage: %s(str)", "me"); + + quest_manager.me(SvPV_nolen(ST(0))); + + XSRETURN_EMPTY; +} + +XS(XS__summonitem); // prototype to pass -Wmissing-prototypes +XS(XS__summonitem) +{ + dXSARGS; + if (items == 1) + quest_manager.summonitem(SvUV(ST(0))); + else if(items == 2) + quest_manager.summonitem(SvUV(ST(0)), SvUV(ST(1))); + else + Perl_croak(aTHX_ "Usage: summonitem(itemid, [charges])"); + XSRETURN_EMPTY; +} + +XS(XS__write); +XS(XS__write) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: write(file, str)"); + + char * file = (char *)SvPV_nolen(ST(0)); + char * str = (char *)SvPV_nolen(ST(1)); + + quest_manager.write(file, str); + + XSRETURN_EMPTY; +} + +XS(XS__spawn); +XS(XS__spawn) +{ + dXSARGS; + if (items != 6) + Perl_croak(aTHX_ "Usage: spawn(npc_type, grid, unused, x, y, z)"); + + uint16 RETVAL; + dXSTARG; + + int npc_type = (int)SvIV(ST(0)); + int grid = (int)SvIV(ST(1)); + int unused = (int)SvIV(ST(2)); + float x = (float)SvNV(ST(3)); + float y = (float)SvNV(ST(4)); + float z = (float)SvNV(ST(5)); + + RETVAL = quest_manager.spawn2(npc_type, grid, unused, x, y, z, 0); + XSprePUSH; PUSHu((UV)RETVAL); + + XSRETURN(1); +} + +XS(XS__spawn2); +XS(XS__spawn2) +{ + dXSARGS; + if (items != 7) + Perl_croak(aTHX_ "Usage: spawn2(npc_type, grid, unused, x, y, z, heading)"); + + uint16 RETVAL; + dXSTARG; + + int npc_type = (int)SvIV(ST(0)); + int grid = (int)SvIV(ST(1)); + int unused = (int)SvIV(ST(2)); + float x = (float)SvNV(ST(3)); + float y = (float)SvNV(ST(4)); + float z = (float)SvNV(ST(5)); + float heading = (float)SvNV(ST(6)); + + RETVAL = quest_manager.spawn2(npc_type, grid, unused, x, y, z, heading); + XSprePUSH; PUSHu((UV)RETVAL); + + XSRETURN(1); +} + +XS(XS__unique_spawn); +XS(XS__unique_spawn) +{ + dXSARGS; + if (items != 6 && items != 7) + Perl_croak(aTHX_ "Usage: unique_spawn(npc_type, grid, unused, x, y, z, [heading])"); + + uint16 RETVAL; + dXSTARG; + + int npc_type = (int)SvIV(ST(0)); + int grid = (int)SvIV(ST(1)); + int unused = (int)SvIV(ST(2)); + float x = (float)SvNV(ST(3)); + float y = (float)SvNV(ST(4)); + float z = (float)SvNV(ST(5)); + float heading = 0; + if(items == 7) + heading = (float)SvNV(ST(6)); + + RETVAL = quest_manager.unique_spawn(npc_type, grid, unused, x, y, z, heading); + XSprePUSH; PUSHu((UV)RETVAL); + + XSRETURN(1); +} + +XS(XS__spawn_from_spawn2); +XS(XS__spawn_from_spawn2) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: spawn_from_spawn2(spawn2_id)"); + + uint16 RETVAL; + dXSTARG; + + int spawn2_id = (int)SvIV(ST(0)); + + RETVAL = quest_manager.spawn_from_spawn2(spawn2_id); + XSprePUSH; PUSHu((UV)RETVAL); + + XSRETURN(1); +} + +XS(XS__enable_spawn2); +XS(XS__enable_spawn2) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: enable_spawn2(spawn2_id)"); + + int spawn2_id = (int)SvIV(ST(0)); + + quest_manager.enable_spawn2(spawn2_id); + XSRETURN_EMPTY; +} + +XS(XS__disable_spawn2); +XS(XS__disable_spawn2) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: disable_spawn2(spawn2_id)"); + + int spawn2_id = (int)SvIV(ST(0)); + + quest_manager.disable_spawn2(spawn2_id); + XSRETURN_EMPTY; +} + +XS(XS__setstat); +XS(XS__setstat) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: setstat(stat, value)"); + + int stat = (int)SvIV(ST(0)); + int value = (int)SvIV(ST(1)); + + quest_manager.setstat(stat, value); + + XSRETURN_EMPTY; +} + +XS(XS__incstat); //old setstat command aza +XS(XS__incstat) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: incstat(stat, value)"); + + int stat = (int)SvIV(ST(0)); + int value = (int)SvIV(ST(1)); + + quest_manager.incstat(stat, value); + + XSRETURN_EMPTY; +} + +XS(XS__castspell); +XS(XS__castspell) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: castspell(spell_id, target_id)"); + + int spell_id = (int)SvIV(ST(0)); + int target_id = (int)SvIV(ST(1)); + + quest_manager.castspell(spell_id, target_id); + + XSRETURN_EMPTY; +} + +XS(XS__selfcast); +XS(XS__selfcast) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: selfcast(spell_id)"); + + int spell_id = (int)SvIV(ST(0)); + + quest_manager.selfcast(spell_id); + + XSRETURN_EMPTY; +} + +XS(XS__addloot); +XS(XS__addloot) +{ + dXSARGS; + if(items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: addloot(item_id, charges = 0, equipitem = true)"); + + uint32 itemid = (uint32)SvUV(ST(0)); + uint16 charges = 0; + bool equipitem = true; + + if (items > 1) + charges = (uint16)SvUV(ST(1)); + if (items > 2) + equipitem = (bool)SvTRUE(ST(2)); + + quest_manager.addloot(itemid, charges, equipitem); + + XSRETURN_EMPTY; +} + +XS(XS__zone); +XS(XS__zone) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: zone(zone_name)"); + + char * zone_name = (char *)SvPV_nolen(ST(0)); + + quest_manager.Zone(zone_name); + + XSRETURN_EMPTY; +} + +XS(XS__settimer); +XS(XS__settimer) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: settimer(timer_name, seconds)"); + + char * timer_name = (char *)SvPV_nolen(ST(0)); + int seconds = (int)SvIV(ST(1)); + + quest_manager.settimer(timer_name, seconds); + + XSRETURN_EMPTY; +} + +XS(XS__settimerMS); +XS(XS__settimerMS) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: settimerMS(timer_name, milliseconds)"); + + char * timer_name = (char *)SvPV_nolen(ST(0)); + int milliseconds = (int)SvIV(ST(1)); + + quest_manager.settimerMS(timer_name, milliseconds); + + XSRETURN_EMPTY; +} + +XS(XS__stoptimer); +XS(XS__stoptimer) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: stoptimer(timer_name)"); + + char * timer_name = (char *)SvPV_nolen(ST(0)); + + quest_manager.stoptimer(timer_name); + + XSRETURN_EMPTY; +} + +XS(XS__stopalltimers); +XS(XS__stopalltimers) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: stopalltimers()"); + + quest_manager.stopalltimers(); + + XSRETURN_EMPTY; +} + +XS(XS__emote); +XS(XS__emote) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: emote(str)"); + + char * str = (char *)SvPV_nolen(ST(0)); + + quest_manager.emote(str); + + XSRETURN_EMPTY; +} + +XS(XS__shout); +XS(XS__shout) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: shout(str)"); + + char * str = (char *)SvPV_nolen(ST(0)); + + quest_manager.shout(str); + + XSRETURN_EMPTY; +} + +XS(XS__shout2); +XS(XS__shout2) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: shout2(str)"); + + char * str = (char *)SvPV_nolen(ST(0)); + + quest_manager.shout2(str); + + XSRETURN_EMPTY; +} + +XS(XS__gmsay); +XS(XS__gmsay) +{ + dXSARGS; + if ((items < 1) || (items > 4)) + Perl_croak(aTHX_ "Usage: gmsay(str, color, send_to_world?)"); + + char * str = (char *)SvPV_nolen(ST(0)); + int color = 7; + bool send_to_world = 0; + uint32 to_guilddbid = 0; + + if (items > 1) { + color = (int)SvIV(ST(1)); + } + + if (items > 2) { + send_to_world = ((int)SvIV(ST(2))) == 0?false:true; + } + + if (items > 3) { + to_guilddbid = (int)SvIV(ST(3)); + } + + quest_manager.gmsay(str, color, send_to_world, to_guilddbid); + + XSRETURN_EMPTY; +} + +XS(XS__depop); +XS(XS__depop) +{ + dXSARGS; + if (items < 0 || items > 1) + Perl_croak(aTHX_ "Usage: depop(npc_type= 0)"); + + int npc_type; + + if (items < 1) + npc_type = 0; + else + npc_type = (int)SvIV(ST(0)); + + + quest_manager.depop(npc_type); + + XSRETURN_EMPTY; +} + +XS(XS__depop_withtimer); +XS(XS__depop_withtimer) +{ + dXSARGS; + if (items < 0 || items > 1) + Perl_croak(aTHX_ "Usage: depop_withtimer(npc_type= 0)"); + + int npc_type; + + if (items < 1) + npc_type = 0; + else + npc_type = (int)SvIV(ST(0)); + + + quest_manager.depop_withtimer(npc_type); + + XSRETURN_EMPTY; +} + +XS(XS__depopall); +XS(XS__depopall) +{ + dXSARGS; + if (items < 0 || items > 1) + Perl_croak(aTHX_ "Usage: depopall(npc_type= 0)"); + + int npc_type; + + if (items < 1) + npc_type = 0; + else + npc_type = (int)SvIV(ST(0)); + + + quest_manager.depopall(npc_type); + + XSRETURN_EMPTY; +} + +XS(XS__settarget); +XS(XS__settarget) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: settarget(type, target_id)"); + + char * type = (char *)SvPV_nolen(ST(0)); + int target_id = (int)SvIV(ST(1)); + + quest_manager.settarget(type, target_id); + + XSRETURN_EMPTY; +} + +XS(XS__follow); +XS(XS__follow) +{ + dXSARGS; + if (items != 1 && items != 2) + Perl_croak(aTHX_ "Usage: follow(entity_id, [distance])"); + + int entity_id = (int)SvIV(ST(0)); + int distance; + + if (items == 2) + distance = (int)SvIV(ST(1)); + else + distance = 10; + + quest_manager.follow(entity_id, distance); + + XSRETURN_EMPTY; +} + +XS(XS__sfollow); +XS(XS__sfollow) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: sfollow()"); + + + quest_manager.sfollow(); + + XSRETURN_EMPTY; +} + +XS(XS__changedeity); +XS(XS__changedeity) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: changedeity(diety_id)"); + + int diety_id = (int)SvIV(ST(0)); + + quest_manager.changedeity(diety_id); + + XSRETURN_EMPTY; +} + +XS(XS__exp); +XS(XS__exp) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: exp(amt)"); + + int amt = (int)SvIV(ST(0)); + + quest_manager.exp(amt); + + XSRETURN_EMPTY; +} + +XS(XS__level); +XS(XS__level) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: level(newlevel)"); + + int newlevel = (int)SvIV(ST(0)); + + quest_manager.level(newlevel); + + XSRETURN_EMPTY; +} + +XS(XS__traindisc); +XS(XS__traindisc) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: traindisc(discipline_tome_item_id)"); + + int discipline_tome_item_id = (int)SvIV(ST(0)); + + quest_manager.traindisc(discipline_tome_item_id); + + XSRETURN_EMPTY; +} + +XS(XS__isdisctome); +XS(XS__isdisctome) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: isdisctome(item_id)"); + + bool RETVAL; + int item_id = (int)SvIV(ST(0)); + + RETVAL = quest_manager.isdisctome(item_id); + + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + XSRETURN(1); +} + +XS(XS__safemove); +XS(XS__safemove) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: safemove()"); + + + quest_manager.safemove(); + + XSRETURN_EMPTY; +} + +XS(XS__rain); +XS(XS__rain) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: rain(weather)"); + + int weather = (int)SvIV(ST(0)); + + quest_manager.rain(weather); + + XSRETURN_EMPTY; +} + +XS(XS__snow); +XS(XS__snow) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: snow(weather)"); + + int weather = (int)SvIV(ST(0)); + + quest_manager.snow(weather); + + XSRETURN_EMPTY; +} + +XS(XS__surname); +XS(XS__surname) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: surname(name)"); + + char * name = (char *)SvPV_nolen(ST(0)); + + quest_manager.surname(name); + + XSRETURN_EMPTY; +} + +XS(XS__permaclass); +XS(XS__permaclass) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: permaclass(class_id)"); + + int class_id = (int)SvIV(ST(0)); + + quest_manager.permaclass(class_id); + + XSRETURN_EMPTY; +} + +XS(XS__permarace); +XS(XS__permarace) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: permarace(race_id)"); + + int race_id = (int)SvIV(ST(0)); + + quest_manager.permarace(race_id); + + XSRETURN_EMPTY; +} + +XS(XS__permagender); +XS(XS__permagender) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: permagender(gender_id)"); + + int gender_id = (int)SvIV(ST(0)); + + quest_manager.permagender(gender_id); + + XSRETURN_EMPTY; +} + +XS(XS__scribespells); +XS(XS__scribespells) +{ + dXSARGS; + if (items < 1) + Perl_croak(aTHX_ "Usage: scribespells(max_level, min_level = 1)"); + + uint16 RETVAL; + dXSTARG; + + uint8 max_level = (uint8)SvIV(ST(0)); + uint8 min_level = (uint8)SvIV(ST(1)); + + if (min_level) + RETVAL = quest_manager.scribespells(max_level, min_level); + else + RETVAL = quest_manager.scribespells(max_level); + + XSprePUSH; PUSHu((IV)RETVAL); + XSRETURN(1); +} + +XS(XS__traindiscs); +XS(XS__traindiscs) +{ + dXSARGS; + if (items < 1) + Perl_croak(aTHX_ "Usage: traindiscs(max_level, min_level = 1)"); + + uint16 RETVAL; + dXSTARG; + + uint8 max_level = (uint8)SvIV(ST(0)); + uint8 min_level = (uint8)SvIV(ST(1)); + + if (min_level) + RETVAL = quest_manager.traindiscs(max_level, min_level); + else + RETVAL = quest_manager.traindiscs(max_level); + + XSprePUSH; PUSHu((IV)RETVAL); + XSRETURN(1); +} + +XS(XS__unscribespells); +XS(XS__unscribespells) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: unscribespells()"); + + + quest_manager.unscribespells(); + + XSRETURN_EMPTY; +} + +XS(XS__untraindiscs); +XS(XS__untraindiscs) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: untraindiscs()"); + + + quest_manager.untraindiscs(); + + XSRETURN_EMPTY; +} + +XS(XS__givecash); +XS(XS__givecash) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: givecash(copper, silver, gold, platinum)"); + + int copper = (int)SvIV(ST(0)); + int silver = (int)SvIV(ST(1)); + int gold = (int)SvIV(ST(2)); + int platinum = (int)SvIV(ST(3)); + + quest_manager.givecash(copper, silver, gold, platinum); + + XSRETURN_EMPTY; +} + +XS(XS__pvp); +XS(XS__pvp) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: pvp(mode)"); + + char * mode = (char *)SvPV_nolen(ST(0)); + + quest_manager.pvp(mode); + + XSRETURN_EMPTY; +} + +XS(XS__movepc); +XS(XS__movepc) +{ + dXSARGS; + if (items != 4 && items != 5) + Perl_croak(aTHX_ "Usage: movepc(zone_id, x, y, z [,heading])"); + + int zoneid = (int)SvIV(ST(0)); + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(ST(3)); + + if (items == 4) + + quest_manager.movepc(zoneid, x, y, z, 0.0f); + + else { + float heading = (float)SvNV(ST(4)); + quest_manager.movepc(zoneid, x, y, z, heading); + } + + XSRETURN_EMPTY; +} + +XS(XS__gmmove); +XS(XS__gmmove) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: gmmove(x, y, z)"); + + float x = (float)SvNV(ST(0)); + float y = (float)SvNV(ST(1)); + float z = (float)SvNV(ST(2)); + + quest_manager.gmmove(x, y, z); + + XSRETURN_EMPTY; +} + +XS(XS__movegrp); +XS(XS__movegrp) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: movegrp(zoneid, x, y, z)"); + + int zoneid = (int)SvIV(ST(0)); + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(ST(3)); + + quest_manager.movegrp(zoneid, x, y, z); + + XSRETURN_EMPTY; +} + +XS(XS__doanim); +XS(XS__doanim) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: doanim(anim_id)"); + + int anim_id = (int)SvIV(ST(0)); + + quest_manager.doanim(anim_id); + + XSRETURN_EMPTY; +} + +XS(XS__addskill); +XS(XS__addskill) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: addskill(skill_id, value)"); + + int skill_id = (int)SvIV(ST(0)); + int value = (int)SvIV(ST(1)); + + quest_manager.addskill(skill_id, value); + + XSRETURN_EMPTY; +} + +XS(XS__setlanguage); +XS(XS__setlanguage) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: setlanguage(skill_id, value)"); + + int skill_id = (int)SvIV(ST(0)); + int value = (int)SvIV(ST(1)); + + quest_manager.setlanguage(skill_id, value); + + XSRETURN_EMPTY; +} + +XS(XS__setskill); +XS(XS__setskill) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: setskill(skill_id, value)"); + + int skill_id = (int)SvIV(ST(0)); + int value = (int)SvIV(ST(1)); + + quest_manager.setskill(skill_id, value); + + XSRETURN_EMPTY; +} + +XS(XS__setallskill); +XS(XS__setallskill) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: setallskill(value)"); + + int value = (int)SvIV(ST(0)); + + quest_manager.setallskill(value); + + XSRETURN_EMPTY; +} + +XS(XS__attack); +XS(XS__attack) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: attack(client_name)"); + + char * client_name = (char *)SvPV_nolen(ST(0)); + + quest_manager.attack(client_name); + + XSRETURN_EMPTY; +} + +XS(XS__attacknpc); +XS(XS__attacknpc) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: attacknpc(npc_entity_id)"); + + int npc_entity_id = (int)SvIV(ST(0)); + + quest_manager.attacknpc(npc_entity_id); + + XSRETURN_EMPTY; +} + +XS(XS__attacknpctype); +XS(XS__attacknpctype) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: attacknpctype(npc_type_id)"); + + int npc_type_id = (int)SvIV(ST(0)); + + quest_manager.attacknpctype(npc_type_id); + + XSRETURN_EMPTY; +} + +XS(XS__save); +XS(XS__save) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: save()"); + + + quest_manager.save(); + + XSRETURN_EMPTY; +} + +XS(XS__faction); +XS(XS__faction) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: faction(faction_id, faction_value, temp)"); + + int faction_id = (int)SvIV(ST(0)); + int faction_value = (int)SvIV(ST(1)); + int temp; + + if(items == 2) + temp = 0; + else + temp = (int)SvIV(ST(2)); + + quest_manager.faction(faction_id, faction_value, temp); + + XSRETURN_EMPTY; +} + +XS(XS__setsky); +XS(XS__setsky) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: setsky(new_sky)"); + + unsigned char new_sky = (unsigned char)SvUV(ST(0)); + + quest_manager.setsky(new_sky); + + XSRETURN_EMPTY; +} + +XS(XS__setguild); +XS(XS__setguild) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: setguild(new_guild_id, new_rank)"); + + unsigned long new_guild_id = (unsigned long)SvUV(ST(0)); + int new_rank = (int)SvIV(ST(1)); + + quest_manager.setguild(new_guild_id, new_rank); + + XSRETURN_EMPTY; +} + +XS(XS__createguild); +XS(XS__createguild) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: createguild(guild_name, leader)"); + + char * guild_name = (char *)SvPV_nolen(ST(0)); + char * leader = (char *)SvPV_nolen(ST(1)); + + quest_manager.CreateGuild(guild_name, leader); + + XSRETURN_EMPTY; +} + +XS(XS__settime); +XS(XS__settime) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: settime(new_hour, new_min)"); + + int new_hour = (int)SvIV(ST(0)); + int new_min = (int)SvIV(ST(1)); + + quest_manager.settime(new_hour, new_min); + + XSRETURN_EMPTY; +} + +XS(XS__itemlink); +XS(XS__itemlink) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: itemlink(item_id)"); + + int item_id = (int)SvIV(ST(0)); + + quest_manager.itemlink(item_id); + + XSRETURN_EMPTY; +} + +XS(XS__signalwith); +XS(XS__signalwith) +{ + dXSARGS; + + if (items == 2) { + int npc_id = (int)SvIV(ST(0)); + int signal_id = (int)SvIV(ST(1)); + quest_manager.signalwith(npc_id, signal_id); + } else if(items == 3) { + int npc_id = (int)SvIV(ST(0)); + int signal_id = (int)SvIV(ST(1)); + int wait = (int)SvIV(ST(2)); + quest_manager.signalwith(npc_id, signal_id, wait); + } else { + Perl_croak(aTHX_ "Usage: signalwith(npc_id,signal_id[,wait_ms])"); + } + + XSRETURN_EMPTY; +} + +XS(XS__signal); +XS(XS__signal) +{ + dXSARGS; + + if (items == 1) { + int npc_id = (int)SvIV(ST(0)); + quest_manager.signal(npc_id); + } else if(items == 2) { + int npc_id = (int)SvIV(ST(0)); + int wait = (int)SvIV(ST(1)); + quest_manager.signal(npc_id, wait); + } else { + Perl_croak(aTHX_ "Usage: signal(npc_id[,wait_ms])"); + } + + XSRETURN_EMPTY; +} + +XS(XS__setglobal); +XS(XS__setglobal) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: setglobal(varname, newvalue, options, duration)"); + + char * varname = (char *)SvPV_nolen(ST(0)); + char * newvalue = (char *)SvPV_nolen(ST(1)); + int options = (int)SvIV(ST(2)); + char * duration = (char *)SvPV_nolen(ST(3)); + + quest_manager.setglobal(varname, newvalue, options, duration); + + XSRETURN_EMPTY; +} + +XS(XS__targlobal); +XS(XS__targlobal) +{ + dXSARGS; + if (items != 6) + Perl_croak(aTHX_ "Usage: targlobal(varname, value, duration, npcid, charid, zoneid)"); + + char * varname = (char *)SvPV_nolen(ST(0)); + char * value = (char *)SvPV_nolen(ST(1)); + char * duration = (char *)SvPV_nolen(ST(2)); + int npcid = (int)SvIV(ST(3)); + int charid = (int)SvIV(ST(4)); + int zoneid = (int)SvIV(ST(5)); + + quest_manager.targlobal(varname, value, duration, npcid, charid, zoneid); + + XSRETURN_EMPTY; +} + +XS(XS__delglobal); +XS(XS__delglobal) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: delglobal(varname)"); + + char * varname = (char *)SvPV_nolen(ST(0)); + + quest_manager.delglobal(varname); + + XSRETURN_EMPTY; +} + +XS(XS__ding); +XS(XS__ding) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: ding()"); + + + quest_manager.ding(); + + XSRETURN_EMPTY; +} + +XS(XS__rebind); +XS(XS__rebind) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: rebind(zoneid, x, y, z)"); + + int zoneid = (int)SvIV(ST(0)); + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(ST(3)); + + quest_manager.rebind(zoneid, x, y, z); + + XSRETURN_EMPTY; +} + +XS(XS__start); +XS(XS__start) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: start(wp)"); + + int wp = (int)SvIV(ST(0)); + + quest_manager.start(wp); + + XSRETURN_EMPTY; +} + +XS(XS__stop); +XS(XS__stop) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: stop()"); + + + quest_manager.stop(); + + XSRETURN_EMPTY; +} + +XS(XS__pause); +XS(XS__pause) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: pause(duration)"); + + int duration = (int)SvIV(ST(0)); + + quest_manager.pause(duration); + + XSRETURN_EMPTY; +} + +XS(XS__moveto); +XS(XS__moveto) +{ + dXSARGS; + if (items != 3 && items != 4 && items != 5) + Perl_croak(aTHX_ "Usage: moveto(x, y, z, [mth, saveguard?])"); + + float x = (float)SvNV(ST(0)); + float y = (float)SvNV(ST(1)); + float z = (float)SvNV(ST(2)); + float h; + bool saveguard; + + if(items > 3) + h = (float)SvNV(ST(3)); + else + h = 0; + + if(items > 4) + saveguard = (bool)SvTRUE(ST(4)); + else + saveguard = false; + + quest_manager.moveto(x, y, z, h, saveguard); + + XSRETURN_EMPTY; +} + +XS(XS__resume); +XS(XS__resume) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: resume()"); + + + quest_manager.resume(); + + XSRETURN_EMPTY; +} + +XS(XS__addldonpoints); +XS(XS__addldonpoints) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: addldonpoints(points, theme)"); + + long points = (long)SvIV(ST(0)); + unsigned long theme = (unsigned long)SvUV(ST(1)); + + quest_manager.addldonpoints(points, theme); + + XSRETURN_EMPTY; +} + +XS(XS__addldonwin); +XS(XS__addldonwin) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: addldonwin(wins, theme)"); + + long wins = (long)SvIV(ST(0)); + unsigned long theme = (unsigned long)SvUV(ST(1)); + + quest_manager.addldonwin(wins, theme); + + XSRETURN_EMPTY; +} + +XS(XS__addldonloss); +XS(XS__addldonloss) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: addldonloss(losses, theme)"); + + long losses = (long)SvIV(ST(0)); + unsigned long theme = (unsigned long)SvUV(ST(1)); + + quest_manager.addldonloss(losses, theme); + + XSRETURN_EMPTY; +} + +XS(XS__setnexthpevent); +XS(XS__setnexthpevent) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: setnexthpevent(at)"); + + int at = (int)SvIV(ST(0)); + + quest_manager.setnexthpevent(at); + + XSRETURN_EMPTY; +} + +XS(XS__setnextinchpevent); +XS(XS__setnextinchpevent) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: setnextinchpevent(at)"); + + int at = (int)SvIV(ST(0)); + + quest_manager.setnextinchpevent(at); + + XSRETURN_EMPTY; +} + +XS(XS__sethp); +XS(XS__sethp) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: sethp(percentage)"); + + int hpperc = (int)SvIV(ST(0)); + + quest_manager.sethp(hpperc); + + XSRETURN_EMPTY; +} + +XS(XS__respawn); +XS(XS__respawn) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: respawn(npc_type, grid)"); + + int npc_type = (int)SvIV(ST(0)); + int grid = (int)SvIV(ST(1)); + + quest_manager.respawn(npc_type, grid); + + XSRETURN_EMPTY; +} + +XS(XS__ChooseRandom); +XS(XS__ChooseRandom) +{ + dXSARGS; + if (items < 1) + Perl_croak(aTHX_ "Usage: ChooseRandom(... list ...)"); + + int index = MakeRandomInt(0, items-1); + + SV *tmp = ST(0); + ST(0) = ST(index); + ST(index) = tmp; + + XSRETURN(1); //return 1 element from the stack (ST(0)) +} + +XS(XS__set_proximity); +XS(XS__set_proximity) +{ + dXSARGS; + if (items != 4 && items != 6) + Perl_croak(aTHX_ "Usage: set_proximity(minx, maxx, miny, maxy [, minz, maxz])"); + + float minx = (float)SvNV(ST(0)); + float maxx = (float)SvNV(ST(1)); + float miny = (float)SvNV(ST(2)); + float maxy = (float)SvNV(ST(3)); + + if(items == 4) + quest_manager.set_proximity(minx, maxx, miny, maxy); + else { + float minz = (float)SvNV(ST(4)); + float maxz = (float)SvNV(ST(5)); + quest_manager.set_proximity(minx, maxx, miny, maxy, minz, maxz); + } + + XSRETURN_EMPTY; +} + +XS(XS__clear_proximity); +XS(XS__clear_proximity) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: clear_proximity()"); + + quest_manager.clear_proximity(); + + XSRETURN_EMPTY; +} + +XS(XS__setanim); +XS(XS__setanim) //Cisyouc: mob->setappearance() addition +{ + dXSARGS; + if(items != 2) + Perl_croak(aTHX_ "Usage: quest::setanim(npc_type, animnum);"); + + quest_manager.setanim(SvUV(ST(0)), SvUV(ST(1))); + + XSRETURN_EMPTY; +} + +XS(XS__showgrid); +XS(XS__showgrid) +{ + dXSARGS; + if(items != 1) + Perl_croak(aTHX_ "Usage: quest::showgrid(grid_id);"); + + quest_manager.showgrid(SvUV(ST(0))); + + XSRETURN_EMPTY; +} + +XS(XS__showpath); +XS(XS__showpath) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: showpath(x, y, z)"); + + float x = (float)SvNV(ST(0)); + float y = (float)SvNV(ST(1)); + float z = (float)SvNV(ST(2)); + + quest_manager.showpath(x, y, z); + + XSRETURN_EMPTY; +} + +XS(XS__pathto); +XS(XS__pathto) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: pathto(x, y, z)"); + + float x = (float)SvNV(ST(0)); + float y = (float)SvNV(ST(1)); + float z = (float)SvNV(ST(2)); + + quest_manager.pathto(x, y, z); + + XSRETURN_EMPTY; +} + +XS(XS__spawn_condition); +XS(XS__spawn_condition) +{ + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: spawn_condition(zone_short, [instance_id], condition_id, value)"); + + if(items == 3) + { + char * zone_short = (char *)SvPV_nolen(ST(0)); + uint16 cond_id = (int)SvUV(ST(1)); + int16 value = (int)SvIV(ST(2)); + + quest_manager.spawn_condition(zone_short, zone->GetInstanceID(), cond_id, value); + } + else + { + char * zone_short = (char *)SvPV_nolen(ST(0)); + uint32 instance_id = (int)SvUV(ST(1)); + uint16 cond_id = (int)SvUV(ST(2)); + int16 value = (int)SvIV(ST(3)); + + quest_manager.spawn_condition(zone_short, instance_id, cond_id, value); + } + XSRETURN_EMPTY; +} + +XS(XS__get_spawn_condition); +XS(XS__get_spawn_condition) +{ + dXSARGS; + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: get_spawn_condition(zone_short, [instance_id], condition_id)"); + + if(items == 2) + { + int16 RETVAL; + dXSTARG; + + char * zone_short = (char *)SvPV_nolen(ST(0)); + uint16 cond_id = (int)SvIV(ST(1)); + + RETVAL = quest_manager.get_spawn_condition(zone_short, zone->GetInstanceID(), cond_id); + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); + } + else + { + int16 RETVAL; + dXSTARG; + + char * zone_short = (char *)SvPV_nolen(ST(0)); + uint16 instance_id = (int)SvIV(ST(1)); + uint16 cond_id = (int)SvIV(ST(2)); + + RETVAL = quest_manager.get_spawn_condition(zone_short, instance_id, cond_id); + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); + } +} + +XS(XS__toggle_spawn_event); +XS(XS__toggle_spawn_event) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: toggle_spawn_event(event_id, enabled?, reset_base)"); + + uint32 event_id = (int)SvIV(ST(0)); + bool enabled = ((int)SvIV(ST(1))) == 0?false:true; + bool reset_base = ((int)SvIV(ST(1))) == 0?false:true; + + quest_manager.toggle_spawn_event(event_id, enabled, reset_base); + + XSRETURN_EMPTY; +} + +XS(XS__has_zone_flag); +XS(XS__has_zone_flag) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: has_zone_flag(zone_id)"); + + int16 RETVAL; + dXSTARG; + + uint32 zone_id = (int)SvIV(ST(0)); + + RETVAL = quest_manager.has_zone_flag(zone_id); + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); + +} + +XS(XS__set_zone_flag); +XS(XS__set_zone_flag) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: set_zone_flag(zone_id)"); + + uint32 zone_id = (int)SvIV(ST(0)); + + quest_manager.set_zone_flag(zone_id); + + XSRETURN_EMPTY; +} + +XS(XS__clear_zone_flag); +XS(XS__clear_zone_flag) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: clear_zone_flag(zone_id)"); + + uint32 zone_id = (int)SvIV(ST(0)); + + quest_manager.clear_zone_flag(zone_id); + + XSRETURN_EMPTY; +} + +XS(XS__summonburriedplayercorpse); +XS(XS__summonburriedplayercorpse) +{ + dXSARGS; + if (items != 5) + Perl_croak(aTHX_ "Usage: summonburriedplayercorpse(char_id,dest_x,dest_y,dest_z,dest_heading)"); + + bool RETVAL; + uint32 char_id = (int)SvIV(ST(0)); + float dest_x = (float)SvIV(ST(1)); + float dest_y = (float)SvIV(ST(2)); + float dest_z = (float)SvIV(ST(3)); + float dest_heading = (float)SvIV(ST(4)); + + RETVAL = quest_manager.summonburriedplayercorpse(char_id, dest_x, dest_y, dest_z, dest_heading); + + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + XSRETURN(1); +} + +XS(XS__summonallplayercorpses); +XS(XS__summonallplayercorpses) +{ + dXSARGS; + if (items != 5) + Perl_croak(aTHX_ "Usage: summonallplayercorpses(char_id,dest_x,dest_y,dest_z,dest_heading)"); + + bool RETVAL; + uint32 char_id = (int)SvIV(ST(0)); + float dest_x = (float)SvIV(ST(1)); + float dest_y = (float)SvIV(ST(2)); + float dest_z = (float)SvIV(ST(3)); + float dest_heading = (float)SvIV(ST(4)); + + RETVAL = quest_manager.summonallplayercorpses(char_id, dest_x, dest_y, dest_z, dest_heading); + + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + XSRETURN(1); +} + +XS(XS__getplayerburriedcorpsecount); +XS(XS__getplayerburriedcorpsecount) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: getplayerburriedcorpsecount(char_id)"); + + uint32 RETVAL; + dXSTARG; + + uint32 char_id = (int)SvIV(ST(0)); + + RETVAL = quest_manager.getplayerburriedcorpsecount(char_id); + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__buryplayercorpse); +XS(XS__buryplayercorpse) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: buryplayercorpse(char_id)"); + + uint32 RETVAL; + dXSTARG; + + uint32 char_id = (int)SvIV(ST(0)); + + RETVAL = quest_manager.buryplayercorpse(char_id); + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__forcedooropen); +XS(XS__forcedooropen) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: forcedooropen(doorid [, altmode=0])"); + + if (items == 1) + { + uint32 did = (int)SvIV(ST(0)); + + quest_manager.forcedooropen(did, false); + + XSRETURN_EMPTY; + } + else + { + uint32 did = (int)SvIV(ST(0)); + bool am = (int)SvIV(ST(1)) == 0?false:true; + + quest_manager.forcedooropen(did, am); + + XSRETURN_EMPTY; + } +} + +XS(XS__forcedoorclose); +XS(XS__forcedoorclose) +{ + dXSARGS; + if (items < 1 || items > 2) + Perl_croak(aTHX_ "Usage: forcedoorclose(doorid [, altmode=0])"); + + if (items == 1) + { + uint32 did = (int)SvIV(ST(0)); + + quest_manager.forcedoorclose(did, false); + + XSRETURN_EMPTY; + } + else + { + uint32 did = (int)SvIV(ST(0)); + bool am = (int)SvIV(ST(1)) == 0?false:true; + + quest_manager.forcedoorclose(did, am); + + XSRETURN_EMPTY; + } +} + +XS(XS__toggledoorstate); +XS(XS__toggledoorstate) +{ + dXSARGS; + if (items !=1) + Perl_croak(aTHX_ "Usage: toggledoorstate(doorid)"); + + uint32 did = (int)SvIV(ST(0)); + + quest_manager.toggledoorstate(did); + + XSRETURN_EMPTY; +} + +XS(XS__isdooropen); +XS(XS__isdooropen) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: isdooropen(doorid)"); + + bool RETVAL; + dXSTARG; + + uint32 doorid = (int)SvIV(ST(0)); + + RETVAL = quest_manager.isdooropen(doorid); + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__depopzone); +XS(XS__depopzone) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: depopzone(StartSpawnStatus)"); + + bool StartSpawnStatus = ((int)SvIV(ST(0))) == 0?false:true; + + quest_manager.depopzone(StartSpawnStatus); + + XSRETURN_EMPTY; +} + +XS(XS__repopzone); +XS(XS__repopzone) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: repopzone()"); + + quest_manager.repopzone(); + + XSRETURN_EMPTY; +} + +XS(XS__npcrace); +XS(XS__npcrace) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: npcrace(race_id)"); + + int race_id = (int)SvIV(ST(0)); + + quest_manager.npcrace(race_id); + + XSRETURN_EMPTY; +} + +XS(XS__npcgender); +XS(XS__npcgender) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: npcgender(gender_id)"); + + int gender_id= (int)SvIV(ST(0)); + + quest_manager.npcgender(gender_id); + + XSRETURN_EMPTY; +} + +XS(XS__npcsize); +XS(XS__npcsize) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: npcsize(newsize)"); + + int newsize = (int)SvIV(ST(0)); + + quest_manager.npcsize(newsize); + + XSRETURN_EMPTY; +} + +XS(XS__npctexture); +XS(XS__npctexture) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: npctexture(newtexture)"); + + int newtexture = (int)SvIV(ST(0)); + + quest_manager.npctexture(newtexture); + + XSRETURN_EMPTY; +} + +XS(XS__playerrace); +XS(XS__playerrace) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: playerrace(race_id)"); + + int race_id = (int)SvIV(ST(0)); + + quest_manager.playerrace(race_id); + + XSRETURN_EMPTY; +} + +XS(XS__playergender); +XS(XS__playergender) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: playergender(gender_id)"); + + int gender_id= (int)SvIV(ST(0)); + + quest_manager.playergender(gender_id); + + XSRETURN_EMPTY; +} + +XS(XS__playersize); +XS(XS__playersize) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: playersize(newsize)"); + + int newsize = (int)SvIV(ST(0)); + + quest_manager.playersize(newsize); + + XSRETURN_EMPTY; +} + +XS(XS__playertexture); +XS(XS__playertexture) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: playertexture(newtexture)"); + + int newtexture = (int)SvIV(ST(0)); + + quest_manager.playertexture(newtexture); + + XSRETURN_EMPTY; +} + +XS(XS__playerfeature); +XS(XS__playerfeature) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: playerfeature(feature, setting)"); + + char * feature = (char *)SvPV_nolen(ST(0)); + int setting = (int)SvIV(ST(1)); + + quest_manager.playerfeature(feature, setting); + + XSRETURN_EMPTY; +} + +XS(XS__npcfeature); +XS(XS__npcfeature) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: npcfeature(feature, setting)"); + + char * feature = (char *)SvPV_nolen(ST(0)); + int setting = (int)SvIV(ST(1)); + + quest_manager.npcfeature(feature, setting); + + XSRETURN_EMPTY; +} + +#ifdef BOTS + +XS(XS__createbotcount); +XS(XS__createbotcount) +{ + dXSARGS; + int RETVAL; + dXSTARG; + + RETVAL = quest_manager.createbotcount(); + XSprePUSH; PUSHi((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__spawnbotcount); +XS(XS__spawnbotcount) +{ + dXSARGS; + int RETVAL; + dXSTARG; + + RETVAL = quest_manager.spawnbotcount(); + XSprePUSH; PUSHi((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__botquest); +XS(XS__botquest) +{ + dXSARGS; + bool RETVAL; + dXSTARG; + + RETVAL = quest_manager.botquest(); + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__createBot); +XS(XS__createBot) +{ + dXSARGS; + bool RETVAL; + dXSTARG; + + if(items != 6) + { + Perl_croak(aTHX_ "Usage: createBot(firstname, lastname, level, race, class, gender)"); + } + + char *firstname = (char *)SvPV_nolen(ST(0)); + char *lastname = (char *)SvPV_nolen(ST(1)); + int level = (int) SvIV(ST(2)); + int race = (int) SvIV(ST(3)); + int botclass = (int) SvIV(ST(4)); + int gender = (int) SvIV(ST(5)); + + RETVAL = quest_manager.createBot(firstname, lastname, level, race, botclass, gender); + XSprePUSH; PUSHu((IV)RETVAL); + XSRETURN(1); +} + +#endif //BOTS + +XS(XS__taskselector); +XS(XS__taskselector) +{ + dXSARGS; + if((items >= 1) && (items <=MAXCHOOSERENTRIES)) { + int tasks[MAXCHOOSERENTRIES]; + for(int i=0; i< items; i++) { + tasks[i] = (int)SvIV(ST(i)); + } + quest_manager.taskselector(items, tasks); + } else { + Perl_croak(aTHX_ "Usage: taskselector(taskid1, taskid2, ..., taskid%i)", MAXCHOOSERENTRIES); + } + + XSRETURN_EMPTY; +} +XS(XS__tasksetselector); +XS(XS__tasksetselector) +{ + dXSARGS; + if(items == 1) { + int tasksetid = (int)SvIV(ST(0)); + quest_manager.tasksetselector(tasksetid); + } else { + Perl_croak(aTHX_ "Usage: tasksetselector(tasksetid)"); + } + + XSRETURN_EMPTY; +} +XS(XS__enabletask); +XS(XS__enabletask) +{ + dXSARGS; + if((items >= 1) && (items <=10)) { + int tasks[10]; + for(int i=0; i< items; i++) { + tasks[i] = (int)SvIV(ST(i)); + } + quest_manager.enabletask(items, tasks); + } else { + Perl_croak(aTHX_ "Usage: enabletask(taskid1, taskid2, ..., taskid10"); + } + + XSRETURN_EMPTY; +} +XS(XS__disabletask); +XS(XS__disabletask) +{ + dXSARGS; + if((items >= 1) && (items <=10)) { + int tasks[10]; + for(int i=0; i< items; i++) { + tasks[i] = (int)SvIV(ST(i)); + } + quest_manager.disabletask(items, tasks); + } else { + Perl_croak(aTHX_ "Usage: disabletask(taskid1, taskid2, ..., taskid10"); + } + + XSRETURN_EMPTY; +} + +XS(XS__istaskenabled); +XS(XS__istaskenabled) +{ + dXSARGS; + bool RETVAL; + dXSTARG; + + if(items == 1) { + unsigned int taskid = (int)SvIV(ST(0)); + RETVAL = quest_manager.istaskenabled(taskid); + } else { + Perl_croak(aTHX_ "Usage: istaskenabled(taskid)"); + } + + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); +} +XS(XS__istaskactive); +XS(XS__istaskactive) +{ + dXSARGS; + bool RETVAL; + dXSTARG; + + if(items == 1) { + unsigned int task = (int)SvIV(ST(0)); + RETVAL = quest_manager.istaskactive(task); + } else { + Perl_croak(aTHX_ "Usage: istaskactive(task)"); + } + + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); +} +XS(XS__istaskactivityactive); +XS(XS__istaskactivityactive) +{ + dXSARGS; + bool RETVAL; + dXSTARG; + + if(items == 2) { + unsigned int task = (int)SvIV(ST(0)); + unsigned int activity = (int)SvIV(ST(1)); + RETVAL = quest_manager.istaskactivityactive(task, activity); + } else { + Perl_croak(aTHX_ "Usage: istaskactivityactive(task,activity)"); + } + + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); +} +XS(XS__gettaskactivitydonecount); +XS(XS__gettaskactivitydonecount) +{ + dXSARGS; + uint32 RETVAL; + dXSTARG; + + if(items == 2) { + unsigned int task = (int)SvIV(ST(0)); + unsigned int activity = (int)SvIV(ST(1)); + RETVAL = quest_manager.gettaskactivitydonecount(task, activity); + XSprePUSH; PUSHu((UV)RETVAL); + } else { + Perl_croak(aTHX_ "Usage: gettaskactivitydonecount(task,activity)"); + } + + XSRETURN(1); +} +XS(XS__updatetaskactivity); +XS(XS__updatetaskactivity) +{ + dXSARGS; + unsigned int task, activity; + int count = 1; + if(items == 2) { + task = (int)SvIV(ST(0)); + activity = (int)SvIV(ST(1)); + quest_manager.updatetaskactivity(task, activity, count); + } + else if(items == 3) { + task = (int)SvIV(ST(0)); + activity = (int)SvIV(ST(1)); + count = (int)SvIV(ST(2)); + quest_manager.updatetaskactivity(task, activity, count); + } else { + Perl_croak(aTHX_ "Usage: updatetaskactivity(task, activity [,count])"); + } + + XSRETURN_EMPTY; +} + +XS(XS__resettaskactivity); +XS(XS__resettaskactivity) +{ + dXSARGS; + unsigned int task, activity; + if(items == 2) { + task = (int)SvIV(ST(0)); + activity = (int)SvIV(ST(1)); + quest_manager.resettaskactivity(task, activity); + } else { + Perl_croak(aTHX_ "Usage: resettaskactivity(task, activity)"); + } + + XSRETURN_EMPTY; +} + +XS(XS__taskexploredarea); +XS(XS__taskexploredarea) +{ + dXSARGS; + unsigned int exploreid; + if(items == 1) { + exploreid = (int)SvIV(ST(0)); + quest_manager.taskexploredarea(exploreid); + } else { + Perl_croak(aTHX_ "Usage: taskexplorearea(exploreid)"); + } + + XSRETURN_EMPTY; +} + +XS(XS__assigntask); +XS(XS__assigntask) +{ + dXSARGS; + unsigned int taskid; + if(items == 1) { + taskid = (int)SvIV(ST(0)); + quest_manager.assigntask(taskid); + } else { + Perl_croak(aTHX_ "Usage: assigntask(taskid)"); + } + + XSRETURN_EMPTY; +} + +XS(XS__failtask); +XS(XS__failtask) +{ + dXSARGS; + unsigned int taskid; + if(items == 1) { + taskid = (int)SvIV(ST(0)); + quest_manager.failtask(taskid); + } else { + Perl_croak(aTHX_ "Usage: failtask(taskid)"); + } + + XSRETURN_EMPTY; +} + +XS(XS__tasktimeleft); +XS(XS__tasktimeleft) +{ + dXSARGS; + int RETVAL; + dXSTARG; + + if(items == 1) { + unsigned int taskid = (int)SvIV(ST(0)); + RETVAL = quest_manager.tasktimeleft(taskid); + } else { + Perl_croak(aTHX_ "Usage: tasktimeleft(taskid)"); + } + + XSprePUSH; PUSHi((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__istaskcompleted); +XS(XS__istaskcompleted) +{ + dXSARGS; + int RETVAL; + dXSTARG; + + if(items == 1) { + unsigned int taskid = (int)SvIV(ST(0)); + RETVAL = quest_manager.istaskcompleted(taskid); + } else { + Perl_croak(aTHX_ "Usage: istaskcompleted(taskid)"); + } + + XSprePUSH; PUSHi((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__enabledtaskcount); +XS(XS__enabledtaskcount) +{ + dXSARGS; + int RETVAL; + dXSTARG; + + if(items == 1) { + unsigned int taskset = (int)SvIV(ST(0)); + RETVAL = quest_manager.enabledtaskcount(taskset); + } else { + Perl_croak(aTHX_ "Usage: enabledtaskcount(taskset)"); + } + + XSprePUSH; PUSHi((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__firsttaskinset); +XS(XS__firsttaskinset) +{ + dXSARGS; + int RETVAL; + dXSTARG; + + if(items == 1) { + unsigned int taskset = (int)SvIV(ST(0)); + RETVAL = quest_manager.firsttaskinset(taskset); + } else { + Perl_croak(aTHX_ "Usage: firsttaskinset(taskset)"); + } + + XSprePUSH; PUSHi((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__lasttaskinset); +XS(XS__lasttaskinset) +{ + dXSARGS; + int RETVAL; + dXSTARG; + + if(items == 1) { + unsigned int taskset = (int)SvIV(ST(0)); + RETVAL = quest_manager.lasttaskinset(taskset); + } else { + Perl_croak(aTHX_ "Usage: lasttaskinset(taskset)"); + } + + XSprePUSH; PUSHi((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__nexttaskinset); +XS(XS__nexttaskinset) +{ + dXSARGS; + int RETVAL; + dXSTARG; + + if(items == 2) { + unsigned int taskset = (int)SvIV(ST(0)); + unsigned int taskid = (int)SvIV(ST(1)); + RETVAL = quest_manager.nexttaskinset(taskset, taskid); + } else { + Perl_croak(aTHX_ "Usage: nexttaskinset(taskset, taskid)"); + } + + XSprePUSH; PUSHi((IV)RETVAL); + + XSRETURN(1); +} +XS(XS__activespeaktask); +XS(XS__activespeaktask) +{ + dXSARGS; + int RETVAL; + dXSTARG; + + if(items == 0) { + RETVAL = quest_manager.activespeaktask(); + } else { + Perl_croak(aTHX_ "Usage: activespeaktask()"); + } + + XSprePUSH; PUSHi((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__activespeakactivity); +XS(XS__activespeakactivity) +{ + dXSARGS; + int RETVAL; + dXSTARG; + + if(items == 1) { + unsigned int taskid = (int)SvIV(ST(0)); + RETVAL = quest_manager.activespeakactivity(taskid); + } else { + Perl_croak(aTHX_ "Usage: activespeakactivity(taskid)"); + } + + XSprePUSH; PUSHi((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__activetasksinset); +XS(XS__activetasksinset) +{ + dXSARGS; + int RETVAL; + dXSTARG; + + if(items == 1) { + unsigned int taskset = (int)SvIV(ST(0)); + RETVAL = quest_manager.activetasksinset(taskset); + } else { + Perl_croak(aTHX_ "Usage: activetasksinset(taskset)"); + } + + XSprePUSH; PUSHi((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__completedtasksinset); +XS(XS__completedtasksinset) +{ + dXSARGS; + int RETVAL; + dXSTARG; + + if(items == 1) { + unsigned int taskset = (int)SvIV(ST(0)); + RETVAL = quest_manager.completedtasksinset(taskset); + } else { + Perl_croak(aTHX_ "Usage: completedtasksinset(taskset)"); + } + + XSprePUSH; PUSHi((IV)RETVAL); + + XSRETURN(1); +} + + +XS(XS__istaskappropriate); +XS(XS__istaskappropriate) +{ + dXSARGS; + bool RETVAL; + dXSTARG; + + if(items == 1) { + unsigned int task = (int)SvIV(ST(0)); + RETVAL = quest_manager.istaskappropriate(task); + } else { + Perl_croak(aTHX_ "Usage: istaskaappropriate(task)"); + } + + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); +} + + XS(XS__popup); // prototype to pass -Wmissing-prototypes + XS(XS__popup) { + dXSARGS; + int popupid = 0; + int buttons = 0; + int duration = 0; + + if((items < 2) || (items > 5)) + Perl_croak(aTHX_ "Usage: popup(windowtitle, text, popupid, buttons, duration)"); + + if(items >= 3) + popupid = (int)SvIV(ST(2)); + + if(items >= 4) + buttons = (int)SvIV(ST(3)); + + if(items == 5) + duration = (int)SvIV(ST(4)); + + quest_manager.popup(SvPV_nolen(ST(0)), SvPV_nolen(ST(1)), popupid, buttons, duration); + + XSRETURN_EMPTY; + } +XS(XS__clearspawntimers); +XS(XS__clearspawntimers) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: clearspawntimers()"); + + quest_manager.clearspawntimers(); + + XSRETURN_EMPTY; +} +XS(XS__ze); +XS(XS__ze) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: ze(type, str)"); + + int type = (int)SvIV(ST(0)); + char * str = (char *)SvPV_nolen(ST(1)); + + quest_manager.ze(type, str); + + XSRETURN_EMPTY; +} + +XS(XS__we); +XS(XS__we) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: we(type, str)"); + + int type = (int)SvIV(ST(0)); + char * str = (char *)SvPV_nolen(ST(1)); + + quest_manager.we(type, str); + + XSRETURN_EMPTY; +} +XS(XS__getlevel); +XS(XS__getlevel) +{ + dXSARGS; + if (items > 1) + Perl_croak(aTHX_ "Usage: getlevel(type)"); + + int RETVAL; + dXSTARG; + + int type; + if (items == 1) + type = (int)SvIV(ST(0)); + else + type = 0; + + RETVAL = quest_manager.getlevel(type); + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__CreateGroundObject); +XS(XS__CreateGroundObject) +{ + dXSARGS; + if (items != 5 && items != 6) + Perl_croak(aTHX_ "Usage: creategroundobject(itemid, x, y, z, heading, [decay_time])"); + + int itemid = (int)SvIV(ST(0)); + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(ST(3)); + float heading = (float)SvNV(ST(4)); + uint16 id = 0; + + if(items == 5) + id = quest_manager.CreateGroundObject(itemid, x, y, z, heading); + else{ + uint32 decay_time = (uint32)SvIV(ST(5)); + id = quest_manager.CreateGroundObject(itemid, x, y, z, heading, decay_time); + } + + XSRETURN_IV(id); +} + +XS(XS__CreateGroundObjectFromModel); +XS(XS__CreateGroundObjectFromModel) +{ + dXSARGS; + if (items < 5 || items > 7) + Perl_croak(aTHX_ "Usage: creategroundobjectfrommodel(modelname, x, y, z, heading, [type], [decay_time])"); + + char * modelname = (char *)SvPV_nolen(ST(0)); + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(ST(3)); + float heading = (float)SvNV(ST(4)); + uint32 type = 0; + uint32 decay_time = 0; + uint16 id = 0; + + if (items > 5) + type = (uint32)SvIV(ST(5)); + + if (items > 6) + decay_time = (uint32)SvIV(ST(6)); + + id = quest_manager.CreateGroundObjectFromModel(modelname, x, y, z, heading, type, decay_time); + XSRETURN_IV(id); +} + +XS(XS__CreateDoor); +XS(XS__CreateDoor) +{ + dXSARGS; + if (items < 5 || items > 7) + Perl_croak(aTHX_ "Usage: createdoor(modelname, x, y, z, heading, [type], [size])"); + + char * modelname = (char *)SvPV_nolen(ST(0)); + float x = (float)SvNV(ST(1)); + float y = (float)SvNV(ST(2)); + float z = (float)SvNV(ST(3)); + float heading = (float)SvNV(ST(4)); + uint32 type = 58; + uint32 size = 100; + uint16 id = 0; + + if (items > 5) + type = (uint32)SvIV(ST(5)); + + if (items > 6) + size = (uint32)SvIV(ST(6)); + + id = quest_manager.CreateDoor(modelname, x, y, z, heading, type, size); + XSRETURN_IV(id); +} + +XS(XS__ModifyNPCStat); +XS(XS__ModifyNPCStat) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: ModifyNPCStat(identifier, newValue)"); + + quest_manager.ModifyNPCStat(SvPV_nolen(ST(0)), SvPV_nolen(ST(1))); + + XSRETURN_EMPTY; +} + +XS(XS__collectitems); +XS(XS__collectitems) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: collectitems(item_id, remove?)"); + + uint32 item_id = (int)SvIV(ST(0)); + bool remove = ((int)SvIV(ST(1))) == 0?false:true; + + int quantity = + quest_manager.collectitems(item_id, remove); + + XSRETURN_IV(quantity); +} + +XS(XS__UpdateSpawnTimer); +XS(XS__UpdateSpawnTimer) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: UpdateSpawnTimer(Spawn2_ID, Updated_Time_Till_Repop)"); + + uint32 id = (int)SvIV(ST(0)); + uint32 duration = (int)SvIV(ST(1)); + + quest_manager.UpdateSpawnTimer(id, duration); + + XSRETURN_EMPTY; +} + +XS(XS__MerchantSetItem); +XS(XS__MerchantSetItem) { + dXSARGS; + if (items != 2 && items != 3) + Perl_croak(aTHX_ "Usage: MerchantSetItem(NPCid, itemid [, quantity])"); + + uint32 NPCid = (int)SvUV(ST(0)); + uint32 itemid = (int)SvUV(ST(1)); + uint32 quantity = 0; + if (items == 3) + quantity = (int)SvUV(ST(2)); + + quest_manager.MerchantSetItem(NPCid, itemid, quantity); + + XSRETURN_EMPTY; +} + +XS(XS__MerchantCountItem); +XS(XS__MerchantCountItem) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: MerchantCountItem(NPCid, itemid)"); + + uint32 NPCid = (int)SvUV(ST(0)); + uint32 itemid = (int)SvUV(ST(1)); + uint32 quantity = quest_manager.MerchantCountItem(NPCid, itemid); + + XSRETURN_UV(quantity); +} + +XS(XS__varlink); +XS(XS__varlink) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: varlink(itemID)"); + dXSTARG; + + Const_char * RETVAL; + char text[250]; + uint32 itemID; + itemID = (int)SvUV(ST(0)); + + RETVAL = quest_manager.varlink(text, itemID); + + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + XSRETURN(1); +} + +XS(XS__CreateInstance); +XS(XS__CreateInstance) { + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: CreateInstance(zone_name, version, duration)"); + + char * zone = (char *)SvPV_nolen(ST(0)); + uint16 version = (int)SvUV(ST(1)); + uint32 duration = (int)SvUV(ST(2)); + uint32 id = quest_manager.CreateInstance(zone, version, duration); + + XSRETURN_UV(id); +} + +XS(XS__DestroyInstance); +XS(XS__DestroyInstance) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: DestroyInstance(id)"); + + uint16 id = (int)SvUV(ST(0)); + quest_manager.DestroyInstance(id); + + XSRETURN_EMPTY; +} + +XS(XS__GetInstanceID); +XS(XS__GetInstanceID) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: GetInstanceID(zone_name, version)"); + + char * zone = (char *)SvPV_nolen(ST(0)); + uint16 version = (int)SvUV(ST(1)); + uint16 id = quest_manager.GetInstanceID(zone, version); + + XSRETURN_UV(id); +} + +XS(XS__AssignToInstance); +XS(XS__AssignToInstance) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: AssignToInstance(id)"); + + uint16 id = (int)SvUV(ST(0)); + quest_manager.AssignToInstance(id); + + XSRETURN_EMPTY; +} + +XS(XS__AssignGroupToInstance); +XS(XS__AssignGroupToInstance) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: AssignGroupToInstance(id)"); + + uint16 id = (int)SvUV(ST(0)); + quest_manager.AssignGroupToInstance(id); + + XSRETURN_EMPTY; +} + +XS(XS__AssignRaidToInstance); +XS(XS__AssignRaidToInstance) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: AssignRaidToInstance(id)"); + + uint16 id = (int)SvUV(ST(0)); + quest_manager.AssignRaidToInstance(id); + + XSRETURN_EMPTY; +} + +XS(XS__MovePCInstance); +XS(XS__MovePCInstance) +{ + dXSARGS; + if (items != 5 && items != 6) + Perl_croak(aTHX_ "Usage: MovePCInstance(zone_id, instance_id, x, y, z [,heading])"); + + int zoneid = (int)SvIV(ST(0)); + int instanceid = (int)SvIV(ST(1)); + float x = (float)SvNV(ST(2)); + float y = (float)SvNV(ST(3)); + float z = (float)SvNV(ST(4)); + + if (items == 4) + { + quest_manager.MovePCInstance(zoneid, instanceid, x, y, z, 0.0f); + } + else + { + float heading = (float)SvNV(ST(5)); + quest_manager.MovePCInstance(zoneid, instanceid, x, y, z, heading); + } + + XSRETURN_EMPTY; +} + +XS(XS__FlagInstanceByGroupLeader); +XS(XS__FlagInstanceByGroupLeader) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: FlagInstanceByGroupLeader(zone, version)"); + + uint32 zone = (int)SvUV(ST(0)); + uint16 version = (int)SvUV(ST(1)); + quest_manager.FlagInstanceByGroupLeader(zone, version); + + XSRETURN_EMPTY; +} + +XS(XS__FlagInstanceByRaidLeader); +XS(XS__FlagInstanceByRaidLeader) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: FlagInstanceByRaidLeader(zone, version)"); + + uint32 zone = (int)SvUV(ST(0)); + uint16 version = (int)SvUV(ST(1)); + quest_manager.FlagInstanceByRaidLeader(zone, version); + + XSRETURN_EMPTY; +} + +XS(XS__saylink); +XS(XS__saylink) { + dXSARGS; + if (items < 1 || items > 3) + Perl_croak(aTHX_ "Usage: saylink(phrase,[silent?],[linkname])"); + dXSTARG; + + Const_char * RETVAL; + char text[250]; + char text2[250]; + bool silent = false; + strcpy(text,(char *)SvPV_nolen(ST(0))); + if(items >= 2) { + silent = ((int)SvIV(ST(1))) == 0 ? false : true; + } + if (items == 3) + strcpy(text2,(char *)SvPV_nolen(ST(2))); + else + strcpy(text2,text); + + RETVAL = quest_manager.saylink(text, silent, text2); + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + XSRETURN(1); +} + +XS(XS__getguildnamebyid); +XS(XS__getguildnamebyid) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: getguildnamebyid(guild_id)"); + dXSTARG; + + Const_char * RETVAL; + uint32 guild_id = (int)SvUV(ST(0)); + + RETVAL = quest_manager.getguildnamebyid(guild_id); + + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + XSRETURN(1); +} + +XS(XS__SetRunning); +XS(XS__SetRunning) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: SetRunning(val)"); + + bool val = ((int)SvIV(ST(0))) == 0?false:true; + + quest_manager.SetRunning(val); + + XSRETURN_EMPTY; +} + +XS(XS__IsRunning); +XS(XS__IsRunning) +{ + dXSARGS; + if (items >= 1) + Perl_croak(aTHX_ "Usage: IsRunning()"); + + bool RETVAL; + dXSTARG; + + + RETVAL = quest_manager.IsRunning(); + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__IsEffectInSpell); +XS(XS__IsEffectInSpell) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: IsEffectInSpell(spell_id, effect_id)"); + + uint32 spell_id = (uint32)SvUV(ST(0)); + uint32 effect_id = (uint32)SvUV(ST(1)); + bool RETVAL; + dXSTARG; + + + RETVAL = IsEffectInSpell(spell_id, effect_id); + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__IsBeneficialSpell); +XS(XS__IsBeneficialSpell) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: IsBeneficialSpell(spell_id)"); + + uint32 spell_id = (uint32)SvUV(ST(0)); + bool RETVAL; + dXSTARG; + + + RETVAL = BeneficialSpell(spell_id); + XSprePUSH; PUSHu((IV)RETVAL); + + XSRETURN(1); +} + +XS(XS__GetSpellResistType); +XS(XS__GetSpellResistType) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: GetSpellResistType(spell_id)"); + + uint32 spell_id = (uint32)SvUV(ST(0)); + int32 spell_val = 0; + dXSTARG; + + spell_val = GetSpellResistType(spell_id); + XSRETURN_UV(spell_val); +} + +XS(XS__GetSpellTargetType); +XS(XS__GetSpellTargetType) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: GetSpellTargetType(spell_id)"); + + uint32 spell_id = (uint32)SvUV(ST(0)); + int32 spell_val = 0; + dXSTARG; + + spell_val = GetSpellTargetType(spell_id); + XSRETURN_UV(spell_val); +} + +XS(XS__FlyMode); +XS(XS__FlyMode) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: FlyMode([0/1/2])"); + + uint8 flymode = (int)SvUV(ST(0)); + quest_manager.FlyMode(flymode); + + XSRETURN_EMPTY; +} + +XS(XS_FactionValue); +XS(XS_FactionValue) { + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: factionvalue()"); + + uint8 fac = quest_manager.FactionValue(); + XSRETURN_UV(fac); +} + +XS(XS__enabletitle); +XS(XS__enabletitle) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: enabletitle(titleset)"); + + int titleset = (int)SvIV(ST(0)); + + quest_manager.enabletitle(titleset); + + XSRETURN_EMPTY; +} + +XS(XS__checktitle); +XS(XS__checktitle) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: checktitle(titleset)"); + + bool RETVAL; + int titleset = (int)SvIV(ST(0)); + + RETVAL = quest_manager.checktitle(titleset); + + ST(0) = boolSV(RETVAL); + sv_2mortal(ST(0)); + XSRETURN(1); +} + +XS(XS__removetitle); +XS(XS__removetitle) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: removetitle(titleset)"); + + int titleset = (int)SvIV(ST(0)); + + quest_manager.removetitle(titleset); + + XSRETURN_EMPTY; +} + +XS(XS__wearchange); +XS(XS__wearchange) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: wearchange(slot, texture)"); + + uint8 slot = (int)SvUV(ST(0)); + uint16 texture = (int)SvUV(ST(1)); + + quest_manager.wearchange(slot, texture); + + XSRETURN_EMPTY; +} + +XS(XS__voicetell); +XS(XS__voicetell) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: voicetell(clientname, type, race, gender)"); + + char * str = (char *)SvPV_nolen(ST(0)); + int macronum = (int)SvIV(ST(1)); + int racenum = (int)SvIV(ST(2)); + int gendernum = (int)SvIV(ST(3)); + + quest_manager.voicetell(str, macronum, racenum, gendernum); + + XSRETURN_EMPTY; +} + +XS(XS__LearnRecipe); +XS(XS__LearnRecipe) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: LearnRecipe(recipe_id)"); + + uint32 recipe_id = (uint32)SvIV(ST(0)); + + quest_manager.LearnRecipe(recipe_id); + + XSRETURN_EMPTY; +} + +XS(XS__SendMail); +XS(XS__SendMail) +{ + dXSARGS; + if (items != 4) + Perl_croak(aTHX_ "Usage: SendMail(to, from, subject, message)"); + + char *to = (char *)SvPV_nolen(ST(0)); + char *from = (char *)SvPV_nolen(ST(1)); + char *subject = (char *)SvPV_nolen(ST(2)); + char *message = (char *)SvPV_nolen(ST(3)); + + quest_manager.SendMail(to, from, subject, message); + + XSRETURN_EMPTY; +} + +XS(XS__GetZoneID); +XS(XS__GetZoneID) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: GetZoneID(zone)"); + + char *zone = (char *)SvPV_nolen(ST(0)); + int32 id = quest_manager.GetZoneID(zone); + + XSRETURN_IV(id); +} + +XS(XS__GetZoneLongName); +XS(XS__GetZoneLongName) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: GetZoneLongName(zone)"); + dXSTARG; + char *zone = (char *)SvPV_nolen(ST(0)); + Const_char* RETVAL = quest_manager.GetZoneLongName(zone); + + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + XSRETURN(1); +} + +XS(XS__GetTimeSeconds); +XS(XS__GetTimeSeconds) +{ + dXSARGS; + if (items != 0) + Perl_croak(aTHX_ "Usage: GetTimeSeconds()"); + + uint32 seconds = 0; + dXSTARG; + + seconds = Timer::GetTimeSeconds(); + XSRETURN_UV(seconds); +} + +XS(XS__handleturnin); // prototype to pass -Wmissing-prototypes +XS(XS__handleturnin) { + dXSARGS; + + if (items != 2) + Perl_croak(aTHX_ "Usage: handleturnin(itemid, itemcharges)"); + int itemid = (int)SvIV(ST(0)); + int charges = (int)SvIV(ST(1)); + + bool returnVal = quest_manager.TurnInItem(itemid,charges); + + ST(0) = boolSV(returnVal); + sv_2mortal(ST(0)); + XSRETURN(1); +} + +XS(XS__completehandin); // prototype to pass -Wmissing-prototypes +XS(XS__completehandin) { + dXSARGS; + + if (items != 0) + Perl_croak(aTHX_ "Usage: completehandin()"); + + quest_manager.CompleteHandIn(); + + XSRETURN_EMPTY; +} + +XS(XS__resethandin); // prototype to pass -Wmissing-prototypes +XS(XS__resethandin) { + dXSARGS; + + if (items != 0) + Perl_croak(aTHX_ "Usage: resethandin()"); + + quest_manager.ResetHandIn(); + + XSRETURN_EMPTY; +} + +XS(XS__clearhandin); // prototype to pass -Wmissing-prototypes +XS(XS__clearhandin) { + dXSARGS; + + if (items != 0) + Perl_croak(aTHX_ "Usage: clearhandin()"); + + quest_manager.ClearHandIn(); + + XSRETURN_EMPTY; +} + +XS(XS__crosszonesignalclientbycharid); +XS(XS__crosszonesignalclientbycharid) +{ + dXSARGS; + + if (items != 2) + Perl_croak(aTHX_ "Usage: crosszonesignalclientbycharid(char_id, data)"); + + if (items == 2) { + int char_id = (int)SvIV(ST(0)); + uint32 data = (uint32)SvIV(ST(1)); + quest_manager.CrossZoneSignalPlayerByCharID(char_id, data); + } else { + Perl_croak(aTHX_ "Usage: crosszonesignalclientbycharid(char_id, data)"); + } + + XSRETURN_EMPTY; +} + +XS(XS__crosszonesignalclientbyname); +XS(XS__crosszonesignalclientbyname) +{ + dXSARGS; + + if (items != 2) + Perl_croak(aTHX_ "Usage: crosszonesignalclientbycharid(Name, data)"); + + if (items == 2) { + char *Name = (char *)SvPV_nolen(ST(0)); + uint32 data = (uint32)SvIV(ST(1)); + quest_manager.CrossZoneSignalPlayerByName(Name, data); + } else { + Perl_croak(aTHX_ "Usage: crosszonesignalclientbycharid(Name, data)"); + } + + XSRETURN_EMPTY; +} + + +XS(XS__crosszonemessageplayerbyname); +XS(XS__crosszonemessageplayerbyname) +{ + dXSARGS; + + if (items != 3) + Perl_croak(aTHX_ "Usage: crosszonemessageplayerbyname(Type, Name, Message)"); + + if (items == 3) { + uint32 Type = (uint32)SvIV(ST(0)); + char *Name = (char *)SvPV_nolen(ST(1)); + char *Message = (char *)SvPV_nolen(ST(2)); + quest_manager.CrossZoneMessagePlayerByName(Type, Name, Message); + } else { + Perl_croak(aTHX_ "Usage: crosszonemessageplayerbyname(Type, Name, Message)"); + } + + XSRETURN_EMPTY; +} + +/* +This is the callback perl will look for to setup the +quest package's XSUBs +*/ +EXTERN_C XS(boot_quest); // prototype to pass -Wmissing-prototypes +EXTERN_C XS(boot_quest) +{ + dXSARGS; + char file[256]; + strncpy(file, __FILE__, 256); + file[255] = '\0'; + + if(items != 1) + LogFile->write(EQEMuLog::Error, "boot_quest does not take any arguments."); + + char buf[128]; //shouldent have any function names longer than this. + + //add the strcpy stuff to get rid of const warnings.... + + XS_VERSION_BOOTCHECK ; + newXS(strcpy(buf, "echo"), XS__echo, file); + newXS(strcpy(buf, "say"), XS__say, file); + newXS(strcpy(buf, "me"), XS__me, file); + newXS(strcpy(buf, "summonitem"), XS__summonitem, file); + newXS(strcpy(buf, "write"), XS__write, file); + newXS(strcpy(buf, "spawn"), XS__spawn, file); + newXS(strcpy(buf, "spawn2"), XS__spawn2, file); + newXS(strcpy(buf, "unique_spawn"), XS__unique_spawn, file); + newXS(strcpy(buf, "spawn_from_spawn2"), XS__spawn_from_spawn2, file); + newXS(strcpy(buf, "enable_spawn2"), XS__enable_spawn2, file); + newXS(strcpy(buf, "disable_spawn2"), XS__disable_spawn2, file); + newXS(strcpy(buf, "setstat"), XS__setstat, file); + newXS(strcpy(buf, "incstat"), XS__incstat, file); + newXS(strcpy(buf, "castspell"), XS__castspell, file); + newXS(strcpy(buf, "selfcast"), XS__selfcast, file); + newXS(strcpy(buf, "addloot"), XS__addloot, file); + newXS(strcpy(buf, "zone"), XS__zone, file); + newXS(strcpy(buf, "settimer"), XS__settimer, file); + newXS(strcpy(buf, "settimerMS"), XS__settimerMS, file); + newXS(strcpy(buf, "stoptimer"), XS__stoptimer, file); + newXS(strcpy(buf, "stopalltimers"), XS__stopalltimers, file); + newXS(strcpy(buf, "emote"), XS__emote, file); + newXS(strcpy(buf, "shout"), XS__shout, file); + newXS(strcpy(buf, "shout2"), XS__shout2, file); + newXS(strcpy(buf, "gmsay"), XS__gmsay, file); + newXS(strcpy(buf, "depop"), XS__depop, file); + newXS(strcpy(buf, "depop_withtimer"), XS__depop_withtimer, file); + newXS(strcpy(buf, "settarget"), XS__settarget, file); + newXS(strcpy(buf, "follow"), XS__follow, file); + newXS(strcpy(buf, "sfollow"), XS__sfollow, file); + newXS(strcpy(buf, "changedeity"), XS__changedeity, file); + newXS(strcpy(buf, "exp"), XS__exp, file); + newXS(strcpy(buf, "level"), XS__level, file); + newXS(strcpy(buf, "traindisc"), XS__traindisc, file); + newXS(strcpy(buf, "isdisctome"), XS__isdisctome, file); + newXS(strcpy(buf, "safemove"), XS__safemove, file); + newXS(strcpy(buf, "rain"), XS__rain, file); + newXS(strcpy(buf, "snow"), XS__snow, file); + newXS(strcpy(buf, "surname"), XS__surname, file); + newXS(strcpy(buf, "permaclass"), XS__permaclass, file); + newXS(strcpy(buf, "permarace"), XS__permarace, file); + newXS(strcpy(buf, "permagender"), XS__permagender, file); + newXS(strcpy(buf, "scribespells"), XS__scribespells, file); + newXS(strcpy(buf, "traindiscs"), XS__traindiscs, file); + newXS(strcpy(buf, "unscribespells"), XS__unscribespells, file); + newXS(strcpy(buf, "untraindiscs"), XS__untraindiscs, file); + newXS(strcpy(buf, "givecash"), XS__givecash, file); + newXS(strcpy(buf, "pvp"), XS__pvp, file); + newXS(strcpy(buf, "movepc"), XS__movepc, file); + newXS(strcpy(buf, "gmmove"), XS__gmmove, file); + newXS(strcpy(buf, "movegrp"), XS__movegrp, file); + newXS(strcpy(buf, "doanim"), XS__doanim, file); + newXS(strcpy(buf, "addskill"), XS__addskill, file); + newXS(strcpy(buf, "setlanguage"), XS__setlanguage, file); + newXS(strcpy(buf, "setskill"), XS__setskill, file); + newXS(strcpy(buf, "setallskill"), XS__setallskill, file); + newXS(strcpy(buf, "attack"), XS__attack, file); + newXS(strcpy(buf, "attacknpc"), XS__attacknpc, file); + newXS(strcpy(buf, "attacknpctype"), XS__attacknpctype, file); + newXS(strcpy(buf, "save"), XS__save, file); + newXS(strcpy(buf, "faction"), XS__faction, file); + newXS(strcpy(buf, "setsky"), XS__setsky, file); + newXS(strcpy(buf, "setguild"), XS__setguild, file); + newXS(strcpy(buf, "createguild"), XS__createguild, file); + newXS(strcpy(buf, "settime"), XS__settime, file); + newXS(strcpy(buf, "itemlink"), XS__itemlink, file); + newXS(strcpy(buf, "signal"), XS__signal, file); + newXS(strcpy(buf, "signalwith"), XS__signalwith, file); + newXS(strcpy(buf, "setglobal"), XS__setglobal, file); + newXS(strcpy(buf, "targlobal"), XS__targlobal, file); + newXS(strcpy(buf, "delglobal"), XS__delglobal, file); + newXS(strcpy(buf, "ding"), XS__ding, file); + newXS(strcpy(buf, "rebind"), XS__rebind, file); + newXS(strcpy(buf, "start"), XS__start, file); + newXS(strcpy(buf, "stop"), XS__stop, file); + newXS(strcpy(buf, "pause"), XS__pause, file); + newXS(strcpy(buf, "moveto"), XS__moveto, file); + newXS(strcpy(buf, "resume"), XS__resume, file); + newXS(strcpy(buf, "addldonpoints"), XS__addldonpoints, file); + newXS(strcpy(buf, "addldonwin"), XS__addldonpoints, file); + newXS(strcpy(buf, "addldonloss"), XS__addldonpoints, file); + newXS(strcpy(buf, "setnexthpevent"), XS__setnexthpevent, file); + newXS(strcpy(buf, "setnextinchpevent"), XS__setnextinchpevent, file); + newXS(strcpy(buf, "sethp"), XS__sethp, file); + newXS(strcpy(buf, "respawn"), XS__respawn, file); + newXS(strcpy(buf, "getItemName"), XS_qc_getItemName, file); + newXS(strcpy(buf, "ChooseRandom"), XS__ChooseRandom, file); + newXS(strcpy(buf, "set_proximity"), XS__set_proximity, file); + newXS(strcpy(buf, "clear_proximity"), XS__clear_proximity, file); + newXS(strcpy(buf, "setanim"), XS__setanim, file); + newXS(strcpy(buf, "showgrid"), XS__showgrid, file); + newXS(strcpy(buf, "showpath"), XS__showpath, file); + newXS(strcpy(buf, "pathto"), XS__pathto, file); + newXS(strcpy(buf, "spawn_condition"), XS__spawn_condition, file); + newXS(strcpy(buf, "get_spawn_condition"), XS__get_spawn_condition, file); + newXS(strcpy(buf, "toggle_spawn_event"), XS__toggle_spawn_event, file); + newXS(strcpy(buf, "has_zone_flag"), XS__has_zone_flag, file); + newXS(strcpy(buf, "set_zone_flag"), XS__set_zone_flag, file); + newXS(strcpy(buf, "clear_zone_flag"), XS__clear_zone_flag, file); + newXS(strcpy(buf, "summonburriedplayercorpse"), XS__summonburriedplayercorpse, file); + newXS(strcpy(buf, "summonallplayercorpses"), XS__summonallplayercorpses, file); + newXS(strcpy(buf, "getplayerburriedcorpsecount"), XS__getplayerburriedcorpsecount, file); + newXS(strcpy(buf, "buryplayercorpse"), XS__buryplayercorpse, file); + newXS(strcpy(buf, "forcedooropen"), XS__forcedooropen, file); + newXS(strcpy(buf, "forcedoorclose"), XS__forcedoorclose, file); + newXS(strcpy(buf, "toggledoorstate"), XS__toggledoorstate, file); + newXS(strcpy(buf, "isdooropen"), XS__isdooropen, file); + newXS(strcpy(buf, "depopall"), XS__depopall, file); + newXS(strcpy(buf, "depopzone"), XS__depopzone, file); + newXS(strcpy(buf, "repopzone"), XS__repopzone, file); + newXS(strcpy(buf, "npcrace"), XS__npcrace, file); + newXS(strcpy(buf, "npcgender"), XS__npcgender, file); + newXS(strcpy(buf, "npcsize"), XS__npcsize, file); + newXS(strcpy(buf, "npctexture"), XS__npctexture, file); + newXS(strcpy(buf, "playerrace"), XS__playerrace, file); + newXS(strcpy(buf, "playergender"), XS__playergender, file); + newXS(strcpy(buf, "playersize"), XS__playersize, file); + newXS(strcpy(buf, "playertexture"), XS__playertexture, file); + newXS(strcpy(buf, "playerfeature"), XS__playerfeature, file); + newXS(strcpy(buf, "npcfeature"), XS__npcfeature, file); + +#ifdef BOTS + newXS(strcpy(buf, "botquest"), XS__botquest, file); + newXS(strcpy(buf, "spawnbotcount"), XS__spawnbotcount, file); + newXS(strcpy(buf, "createbotcount"), XS__createbotcount, file); + newXS(strcpy(buf, "createBot"), XS__createBot, file); +#endif //BOTS + + newXS(strcpy(buf, "taskselector"), XS__taskselector, file); + newXS(strcpy(buf, "tasksetselector"), XS__tasksetselector, file); + newXS(strcpy(buf, "enabletask"), XS__enabletask, file); + newXS(strcpy(buf, "disabletask"), XS__disabletask, file); + newXS(strcpy(buf, "istaskenabled"), XS__istaskenabled, file); + newXS(strcpy(buf, "istaskactive"), XS__istaskactive, file); + newXS(strcpy(buf, "istaskactivityactive"), XS__istaskactivityactive, file); + newXS(strcpy(buf, "gettaskactivitydonecount"), XS__gettaskactivitydonecount, file); + newXS(strcpy(buf, "updatetaskactivity"), XS__updatetaskactivity, file); + newXS(strcpy(buf, "resettaskactivity"), XS__resettaskactivity, file); + newXS(strcpy(buf, "taskexploredarea"), XS__taskexploredarea, file); + newXS(strcpy(buf, "assigntask"), XS__assigntask, file); + newXS(strcpy(buf, "failtask"), XS__failtask, file); + newXS(strcpy(buf, "tasktimeleft"), XS__tasktimeleft, file); + newXS(strcpy(buf, "istaskcompleted"), XS__istaskcompleted, file); + newXS(strcpy(buf, "enabledtaskcount"), XS__enabledtaskcount, file); + newXS(strcpy(buf, "firsttaskinset"), XS__firsttaskinset, file); + newXS(strcpy(buf, "lasttaskinset"), XS__lasttaskinset, file); + newXS(strcpy(buf, "nexttaskinset"), XS__nexttaskinset, file); + newXS(strcpy(buf, "activespeaktask"), XS__activespeaktask, file); + newXS(strcpy(buf, "activespeakactivity"), XS__activespeakactivity, file); + newXS(strcpy(buf, "activetasksinset"), XS__activetasksinset, file); + newXS(strcpy(buf, "completedtasksinset"), XS__completedtasksinset, file); + newXS(strcpy(buf, "istaskappropriate"), XS__istaskappropriate, file); + newXS(strcpy(buf, "popup"), XS__popup, file); + newXS(strcpy(buf, "clearspawntimers"), XS__clearspawntimers, file); + newXS(strcpy(buf, "ze"), XS__ze, file); + newXS(strcpy(buf, "we"), XS__we, file); + newXS(strcpy(buf, "getlevel"), XS__getlevel, file); + newXS(strcpy(buf, "creategroundobject"), XS__CreateGroundObject, file); + newXS(strcpy(buf, "creategroundobjectfrommodel"), XS__CreateGroundObjectFromModel, file); + newXS(strcpy(buf, "createdoor"), XS__CreateDoor, file); + newXS(strcpy(buf, "modifynpcstat"), XS__ModifyNPCStat, file); + newXS(strcpy(buf, "collectitems"), XS__collectitems, file); + newXS(strcpy(buf, "updatespawntimer"), XS__UpdateSpawnTimer, file); + newXS(strcpy(buf, "MerchantSetItem"), XS__MerchantSetItem, file); + newXS(strcpy(buf, "MerchantCountItem"), XS__MerchantCountItem, file); + newXS(strcpy(buf, "varlink"), XS__varlink, file); + newXS(strcpy(buf, "saylink"), XS__saylink, file); + newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file); + newXS(strcpy(buf, "CreateInstance"), XS__CreateInstance, file); + newXS(strcpy(buf, "DestroyInstance"), XS__DestroyInstance, file); + newXS(strcpy(buf, "GetInstanceID"), XS__GetInstanceID, 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, "MovePCInstance"), XS__MovePCInstance, file); + newXS(strcpy(buf, "FlagInstanceByGroupLeader"), XS__FlagInstanceByGroupLeader, file); + newXS(strcpy(buf, "FlagInstanceByRaidLeader"), XS__FlagInstanceByRaidLeader, file); + newXS(strcpy(buf, "SetRunning"), XS__SetRunning, file); + newXS(strcpy(buf, "IsRunning"), XS__IsRunning, file); + newXS(strcpy(buf, "IsEffectInSpell"), XS__IsEffectInSpell, file); + newXS(strcpy(buf, "IsBeneficialSpell"), XS__IsBeneficialSpell, file); + newXS(strcpy(buf, "GetSpellResistType"), XS__GetSpellResistType, file); + newXS(strcpy(buf, "GetSpellTargetType"), XS__GetSpellTargetType, file); + newXS(strcpy(buf, "FlyMode"), XS__FlyMode, file); + newXS(strcpy(buf, "factionvalue"), XS_FactionValue, file); + newXS(strcpy(buf, "checktitle"), XS__checktitle, file); + newXS(strcpy(buf, "enabletitle"), XS__enabletitle, file); + newXS(strcpy(buf, "removetitle"), XS__removetitle, file); + newXS(strcpy(buf, "wearchange"), XS__wearchange, file); + newXS(strcpy(buf, "voicetell"), XS__voicetell, file); + newXS(strcpy(buf, "LearnRecipe"), XS__LearnRecipe, file); + newXS(strcpy(buf, "SendMail"), XS__SendMail, file); + newXS(strcpy(buf, "GetZoneID"), XS__GetZoneID, file); + newXS(strcpy(buf, "GetZoneLongName"), XS__GetZoneLongName, file); + newXS(strcpy(buf, "GetTimeSeconds"), XS__GetTimeSeconds, file); + newXS(strcpy(buf, "handleturnin"), XS__handleturnin, file); + newXS(strcpy(buf, "completehandin"), XS__completehandin, file); + newXS(strcpy(buf, "resethandin"), XS__resethandin, file); + newXS(strcpy(buf, "clearhandin"), XS__clearhandin, file); + newXS(strcpy(buf, "crosszonesignalclientbycharid"), XS__crosszonesignalclientbycharid, file); + newXS(strcpy(buf, "crosszonesignalclientbyname"), XS__crosszonesignalclientbyname, file); + newXS(strcpy(buf, "crosszonemessageplayerbyname"), XS__crosszonemessageplayerbyname, file); + XSRETURN_YES; +} + +#endif +#endif diff --git a/zone/perlparser.h b/zone/perlparser.h new file mode 100644 index 000000000..c0260b82a --- /dev/null +++ b/zone/perlparser.h @@ -0,0 +1,46 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.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 +*/ + +//extends the perl parser to use C methods +//instead of the command queue. + +#ifndef PERLPARSER_H +#define PERLPARSER_H + +#ifdef EMBPERL +#ifdef EMBPERL_XS + +#include "embparser.h" + +class PerlXSParser : public PerlembParser { +public: + PerlXSParser(); +// ~PerlXSParser(); + + virtual void SendCommands(const char * pkgprefix, const char *event, uint32 npcid, Mob* other, Mob* mob, ItemInst* iteminst); +protected: + void map_funs(); + + SV *_empty_sv; +}; + + +#endif //EMBPERL_XS +#endif //EMBPERL + +#endif //PERLPARSER_H diff --git a/zone/petitions.cpp b/zone/petitions.cpp new file mode 100644 index 000000000..13954df17 --- /dev/null +++ b/zone/petitions.cpp @@ -0,0 +1,317 @@ +/* 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 "../common/debug.h" +#include +#include +#include +#include +#include +#ifdef _WINDOWS +#include +#else +#include +#endif + +#ifdef _WINDOWS +#define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif +#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 "entity.h" +#include "masterentity.h" + +#include "petitions.h" +#include "worldserver.h" + +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; + strcpy(pet->accountid,this->GetAccountName()); + strcpy(pet->lastgm,this->GetLastGM()); + strcpy(pet->charname,this->GetCharName()); + pet->petnumber = this->petid; + pet->charclass = this->GetCharClass(); + pet->charlevel = this->GetCharLevel(); + pet->charrace = this->GetCharRace(); + pet->zone = this->GetZone(); + //strcpy(pet->zone,this->GetZone()); + strcpy(pet->petitiontext,this->GetPetitionText()); + pet->checkouts = this->GetCheckouts(); + pet->unavail = this->GetUnavails(); + pet->senttime = this->GetSentTime(); + //memset(pet->unknown5, 0, sizeof(pet->unknown5)); + //pet->unknown5[3] = 0x1f; + pet->urgency = this->GetUrgency(); + strcpy(pet->gmtext, this->GetGMText()); + clientto->QueuePacket(outapp); + safe_delete(outapp); + return; +} + +Petition::Petition(uint32 id) +{ + petid = id; + charclass = 0; + charrace = 0; + charlevel = 0; + checkouts = 0; + unavailables = 0; + urgency = 0; + time(&senttime); + ischeckedout = false; + memset(accountname, 0, sizeof(accountname)); + memset(charname, 0, sizeof(charname)); + memset(lastgm, 0, sizeof(lastgm)); + memset(petitiontext, 0, sizeof(petitiontext)); + memset(gmtext, 0, sizeof(gmtext)); + + //memset(this->zone, 0, sizeof(this->zone)); + zone = 1; +} +Petition* PetitionList::GetPetitionByID(uint32 id_in) { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetID() == id_in) + return iterator.GetData(); + iterator.Advance(); + } + return 0; +} +uint32 PetitionList::GetTotalPetitions(){ + LinkedListIterator iterator(list); + iterator.Reset(); + uint32 total=0; + while(iterator.MoreElements()) { + total++; + iterator.Advance(); + } + return total; +} +bool PetitionList::FindPetitionByAccountName(const char* acctname) { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (!strcmp(acctname,iterator.GetData()->GetAccountName())) + return true; + iterator.Advance(); + } + return false; +} +bool PetitionList::DeletePetitionByCharName(const char* charname) { + LinkedListIterator iterator(list); + + iterator.Reset(); + while(iterator.MoreElements()) { + if (!strcmp(charname,iterator.GetData()->GetCharName())) { + if(DeletePetition(iterator.GetData()->GetID())==0) + return true; + else + return false; + } + iterator.Advance(); + } + return false; +} +void PetitionList::UpdateZoneListQueue() { + ServerPacket* pack = new ServerPacket(ServerOP_Petition, sizeof(ServerPetitionUpdate_Struct)); + ServerPetitionUpdate_Struct* pupdate = (ServerPetitionUpdate_Struct*) pack->pBuffer; + pupdate->petid = 0x00; + pupdate->status = 0x00; + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void PetitionList::AddPetition(Petition* pet) { + list.Insert(pet); + return; +} + +//Return Values: 0 = Ok ; -1 = Error deleting petition. +int PetitionList::DeletePetition(uint32 petnumber) { + LinkedListIterator iterator(list); + iterator.Reset(); + LockMutex lock(&PList_Mutex); + while(iterator.MoreElements()) { + if (iterator.GetData()->GetID() == petnumber) { + database.DeletePetitionFromDB(iterator.GetData()); + iterator.RemoveCurrent(); + return 0; + break; + } + else { + iterator.Advance(); + } + } + return -1; +} + +void PetitionList::UpdateGMQueue() { + LinkedListIterator iterator(list); + iterator.Reset(); + uint32 total=0; + while(iterator.MoreElements()) { + total++; + entity_list.SendPetitionToAdmins(iterator.GetData()); + iterator.Advance(); + } + if(total==0) + entity_list.SendPetitionToAdmins(); + return; +} + +void PetitionList::ClearPetitions() { + // entity_list.ClearClientPetitionQueue(); + LinkedListIterator iterator(list); + iterator.Reset(); + while(iterator.MoreElements()) + { + iterator.RemoveCurrent(true); + iterator.Advance(); + } + return; +} + +void PetitionList::ReadDatabase() { + LockMutex lock(&PList_Mutex); + ClearPetitions(); + database.RefreshPetitionsFromDB(); + UpdateGMQueue(); + return; +} + +void PetitionList::UpdatePetition(Petition* pet) { + LockMutex lock(&PList_Mutex); + database.UpdatePetitionToDB(pet); + return; +} + +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; +} + +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; +} + + + +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); + } + + 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]); + cout << "Petition " << row[0] << " pettime = " << newpet->GetSentTime() << 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); + return; + } + + return; +} diff --git a/zone/petitions.h b/zone/petitions.h new file mode 100644 index 000000000..2a5d5649e --- /dev/null +++ b/zone/petitions.h @@ -0,0 +1,118 @@ +/* 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 PETITIONS_H +#define PETITIONS_H + +#include "../common/linked_list.h" +#include "../common/types.h" +#include "zonedb.h" +#include "client.h" +#include "../common/Mutex.h" +#include "../common/MiscFunctions.h" + +class Petition +{ +public: + Petition(uint32 id); + ~Petition() {} + void SendPetitionToPlayer(Client* clientto); + + + uint32 GetID() { return this->petid; } + uint32 GetUnavails() { return unavailables; } + uint32 GetCheckouts() { return checkouts; } + uint32 GetCharLevel() { return charlevel; } + uint32 GetCharRace() { return charrace; } + uint32 GetCharClass() { return charclass; } + uint32 GetUrgency() { return urgency; } + //char* GetZone() { return this->zone; } + uint32 GetZone() { return this->zone; } + char* GetCharName() { return charname; } + char* GetAccountName() { return accountname; } + char* GetLastGM() { return lastgm; } + time_t GetSentTime() { return senttime; } + char* GetPetitionText() { return petitiontext; } + char* GetGMText() { return gmtext; } + bool CheckedOut() { return ischeckedout; } + + +// Set Petition Stuff Functions + void SetCheckedOut(bool status_in) { ischeckedout = status_in; } + void SetUrgency(uint32 urgency_in) { urgency = urgency_in; } + void SetClass(uint32 class_in) { charclass = class_in; } + void SetRace(uint32 race_in) { charrace = race_in; } + void SetLevel(uint32 level_in) { charlevel = level_in; } + void AddCheckout() { checkouts = checkouts + 1; } + void AddUnavails() { unavailables = unavailables + 1; } + //void SetZone(char* zone_in) { strcpy(this->zone, zone_in); } + void SetZone(uint32 zone_in) { this->zone = zone_in; } + void SetCName(const char* name_in) { strcpy(charname, name_in); } + void SetAName(const char* name_in) { strcpy(accountname, name_in); } + void SetLastGM(const char* gm_in) { strcpy(lastgm, gm_in); } + void SetGMText(const char* gmtext_in) { if(gmtext_in) strcpy(gmtext, gmtext_in); } + void SetPetitionText(char* pet_in) { strn0cpy(petitiontext, pet_in, sizeof(petitiontext)); } + void SetPetID(uint32 id_in) { petid = id_in; } + void SetCheckouts(uint32 checks_in) { checkouts = checks_in; } + void SetUnavails(uint32 unavails_in) { unavailables = unavails_in; } + void SetSentTime() { time(&senttime); } + void SetSentTime2(time_t senttime_in) { senttime = senttime_in; } + +protected: + + uint32 petid; + char charname[64]; + char accountname[32]; + char lastgm[64]; + char petitiontext[1024]; + char gmtext[1024]; + //char zone[32]; + uint32 zone; + uint32 urgency; // 0 = green, 1 = yellow, 2 = red + uint32 charclass; + uint32 charrace; + uint32 charlevel; + uint32 checkouts; + uint32 unavailables; + time_t senttime; + bool ischeckedout; +}; + +class PetitionList +{ +public: + int DeletePetition(uint32 petnumber); + void UpdateGMQueue(); + PetitionList() {} + ~PetitionList() {} + void AddPetition(Petition* pet); + Petition* GetPetitionByID(uint32 id_in); + uint32 GetTotalPetitions(); + void ClearPetitions(); + void ReadDatabase(); + void UpdatePetition(Petition* pet); + void UpdateZoneListQueue(); + bool FindPetitionByAccountName(const char* acctname); + bool DeletePetitionByCharName(const char* charname); + +private: + LinkedList list; + uint32 last_insert_id; + Mutex PList_Mutex; +}; + +#endif diff --git a/zone/pets.cpp b/zone/pets.cpp new file mode 100644 index 000000000..1817698a3 --- /dev/null +++ b/zone/pets.cpp @@ -0,0 +1,722 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2004 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 "../common/debug.h" +#include "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/MiscFunctions.h" +#include "pets.h" +#include +#include +#ifndef WIN32 +#include +#include "../common/unix.h" +#endif + +#include "StringIDs.h" + +/////////////////////////////////////////////////////////////////////////////// +// pet related functions + +const char *GetRandPetName() +{ + static const char *petnames[] = { "Gabaner","Gabann","Gabantik","Gabarab","Gabarer","Gabarn","Gabartik", + "Gabekab","Gabeker","Gabekn","Gaber","Gabn","Gabobab","Gabobn","Gabtik", + "Ganer","Gann","Gantik","Garab","Garaner","Garann","Garantik","Gararn", + "Garekn","Garer","Garn","Gartik","Gasaner","Gasann","Gasantik","Gasarer", + "Gasartik","Gasekn","Gaser","Gebann","Gebantik","Gebarer","Gebarn","Gebartik", + "Gebeker","Gebekn","Gebn","Gekab","Geker","Gekn","Genaner","Genann","Genantik", + "Genarer","Genarn","Gener","Genn","Genobtik","Gibaner","Gibann","Gibantik", + "Gibarn","Gibartik","Gibekn","Giber","Gibn","Gibobtik","Gibtik","Gobaber", + "Gobaner","Gobann","Gobarn","Gobartik","Gober","Gobn","Gobober","Gobobn", + "Gobobtik","Gobtik","Gonaner","Gonann","Gonantik","Gonarab","Gonarer", + "Gonarn","Gonartik","Gonekab","Gonekn","Goner","Gonobtik","Gontik","Gotik", + "Jabaner","Jabann","Jabantik","Jabarab","Jabarer","Jabarn","Jabartik", + "Jabekab","Jabeker","Jabekn","Jaber","Jabn","Jabobtik","Jabtik","Janab", + "Janer","Jann","Jantik","Jarab","Jaranab","Jaraner","Jararer","Jararn", + "Jarartik","Jareker","Jarekn","Jarer","Jarn","Jarobn","Jarobtik","Jartik", + "Jasab","Jasaner","Jasantik","Jasarer","Jasartik","Jasekab","Jaseker", + "Jasekn","Jaser","Jasn","Jasobab","Jasober","Jastik","Jebanab","Jebann", + "Jebantik","Jebarab","Jebarar","Jebarer","Jebarn","Jebartik","Jebeker", + "Jebekn","Jeber","Jebobn","Jebtik","Jekab","Jeker","Jekn","Jenann", + "Jenantik","Jenarer","Jeneker","Jenekn","Jentik","Jibaner","Jibann", + "Jibantik","Jibarer","Jibarn","Jibartik","Jibeker","Jibn","Jibobn", + "Jibtik","Jobab","Jobaner","Jobann","Jobantik","Jobarn","Jobartik", + "Jobekab","Jobeker","Jober","Jobn","Jobtik","Jonanab","Jonaner", + "Jonann","Jonantik","Jonarer","Jonarn","Jonartik","Jonekab","Joneker", + "Jonekn","Joner","Jonn","Jonnarn","Jonober","Jonobn","Jonobtik","Jontik", + "Kabanab","Kabaner","Kabann","Kabantik","Kabarer","Kabarn","Kabartik", + "Kabeker","Kabekn","Kaber","Kabn","Kabober","Kabobn","Kabobtik","Kabtik", + "Kanab","Kaner","Kann","Kantik","Karab","Karanab","Karaner","Karann", + "Karantik","Kararer","Karartik","Kareker","Karer","Karn","Karobab","Karobn", + "Kartik","Kasaner","Kasann","Kasarer","Kasartik","Kaseker","Kasekn","Kaser", + "Kasn","Kasober","Kastik","Kebann","Kebantik","Kebarab","Kebartik","Kebeker", + "Kebekn","Kebn","Kebobab","Kebtik","Kekab","Keker","Kekn","Kenab","Kenaner", + "Kenantik","Kenarer","Kenarn","Keneker","Kener","Kenn","Kenobn","Kenobtik", + "Kentik","Kibab","Kibaner","Kibantik","Kibarn","Kibartik","Kibekab","Kibeker", + "Kibekn","Kibn","Kibobn","Kibobtik","Kobab","Kobanab","Kobaner","Kobann", + "Kobantik","Kobarer","Kobarn","Kobartik","Kobeker","Kobekn","Kober","Kobn", + "Kobober","Kobobn","Kobtik","Konanab","Konaner","Konann","Konantik","Konarab", + "Konarer","Konarn","Konekab","Koneker","Konekn","Koner","Konn","Konobn", + "Konobtik","Kontik","Labanab","Labaner","Labann","Labarab","Labarer", + "Labarn","Labartik","Labeker","Labekn","Laner","Lann","Larab","Larantik", + "Lararer","Lararn","Larartik","Lareker","Larer","Larn","Lartik","Lasaner", + "Lasann","Lasarer","Laseker","Laser","Lasik","Lasn","Lastik","Lebaner", + "Lebarer","Lebartik","Lebekn","Lebtik","Lekab","Lekn","Lenanab","Lenaner", + "Lenann","Lenartik","Lenekab","Leneker","Lenekn","Lentik","Libab","Libaner", + "Libann","Libantik","Libarer","Libarn","Libartik","Libeker","Libekn","Lobann", + "Lobarab","Lobarn","Lobartik","Lobekn","Lobn","Lobober","Lobobn","Lobtik", + "Lonaner","Lonann","Lonantik","Lonarab","Lonarer","Lonarn","Lonartik","Lonekn", + "Loner","Lonobtik","Lontik","Vabanab","Vabaner","Vabann","Vabantik","Vabarer", + "Vabarn","Vabartik","Vabeker","Vabekn","Vabtik","Vanikk","Vann","Varartik","Varn", + "Vartik","Vasann","Vasantik","Vasarab","Vasarer","Vaseker","Vebaner","Vebantik", + "Vebarab","Vebeker","Vebekn","Vebobn","Vekab","Veker","Venaner","Venantik","Venar", + "Venarn","Vener","Ventik","Vibann","Vibantik","Viber","Vibobtik","Vobann", + "Vobarer","Vobartik","Vobekn","Vober","Vobn","Vobtik","Vonaner","Vonann", + "Vonantik","Vonarab","Vonarn","Vonartik","Voneker","Vonn","Xabanab","Xabaner", + "Xabarer","Xabarn","Xabartik","Xabekab","Xabeker","Xabekn","Xaber","Xabober", + "Xaner","Xann","Xarab","Xaranab","Xarann","Xarantik","Xararer","Xarartik","Xarer", + "Xarn","Xartik","Xasaner","Xasann","Xasarab","Xasarn","Xasekab","Xaseker", + "Xebarer","Xebarn","Xebeker","Xeber","Xebober","Xebtik","Xekab","Xeker", + "Xekn","Xenann","Xenantik","Xenarer","Xenartik","Xenekn","Xener","Xenober", + "Xentik","Xibantik","Xibarer","Xibekab","Xibeker","Xibobab","Xibober","Xibobn", + "Xobaner","Xobann","Xobarab","Xobarn","Xobekab","Xobeker","Xobekn","Xober", + "Xobn","Xobobn","Xobtik","Xonaner","Xonann","Xonantik","Xonarer","Xonartik", + "Xonekab","Xoneker","Xonekn","Xoner","Xonober","Xtik","Zabaner","Zabantik", + "Zabarab","Zabekab","Zabekn","Zaber","Zabn","Zabobab","Zabober","Zabtik", + "Zaner","Zantik","Zarann","Zarantik","Zararn","Zarartik","Zareker","Zarekn", + "Zarer","Zarn","Zarober","Zartik","Zasaner","Zasarer","Zaseker","Zasekn","Zasn", + "Zebantik","Zebarer","Zebarn","Zebartik","Zebobab","Zekab","Zekn","Zenann", + "Zenantik","Zenarer","Zenarn","Zenekab","Zeneker","Zenobtik","Zibanab","Zibaner", + "Zibann","Zibarer","Zibartik","Zibekn","Zibn","Zibobn","Zobaner","Zobann", + "Zobarn","Zober","Zobn","Zonanab","Zonaner","Zonann","Zonantik","Zonarer", + "Zonartik","Zonobn","Zonobtik","Zontik","Ztik" }; + int r = MakeRandomInt(0, (sizeof(petnames)/sizeof(const char *))-1); + printf("Pet being created: %s\n",petnames[r]); // DO NOT COMMENT THIS OUT! + return petnames[r]; +} + +//not used anymore +/*int CalcPetHp(int levelb, int classb, int STA) +{ + int multiplier = 0; + int base_hp = 0; + switch(classb) { + case WARRIOR:{ + if (levelb < 20) + multiplier = 22; + else if (levelb < 30) + multiplier = 23; + else if (levelb < 40) + multiplier = 25; + else if (levelb < 53) + multiplier = 27; + else if (levelb < 57) + multiplier = 28; + else + multiplier = 30; + break; + } + case DRUID: + case CLERIC: + case SHAMAN:{ + multiplier = 15; + break; + } + case PALADIN: + case SHADOWKNIGHT:{ + if (levelb < 35) + multiplier = 21; + else if (levelb < 45) + multiplier = 22; + else if (levelb < 51) + multiplier = 23; + else if (levelb < 56) + multiplier = 24; + else if (levelb < 60) + multiplier = 25; + else + multiplier = 26; + break; + } + case MONK: + case BARD: + case ROGUE: + case BEASTLORD:{ + if (levelb < 51) + multiplier = 18; + else if (levelb < 58) + multiplier = 19; + else + multiplier = 20; + break; + } + case RANGER:{ + if (levelb < 58) + multiplier = 20; + else + multiplier = 21; + break; + } + case MAGICIAN: + case WIZARD: + case NECROMANCER: + case ENCHANTER:{ + multiplier = 12; + break; + } + default:{ + if (levelb < 35) + multiplier = 21; + else if (levelb < 45) + multiplier = 22; + else if (levelb < 51) + multiplier = 23; + else if (levelb < 56) + multiplier = 24; + else if (levelb < 60) + multiplier = 25; + else + multiplier = 26; + break; + } + } + + if (multiplier == 0) + { + LogFile->write(EQEMuLog::Error, "Multiplier == 0 in CalcPetHp,using Generic....");; + multiplier=12; + } + + base_hp = 5 + (multiplier*levelb) + ((multiplier*levelb*STA) + 1)/300; + return base_hp; +} +*/ + +void Mob::MakePet(uint16 spell_id, const char* pettype, const char *petname) { + // petpower of -1 is used to get the petpower based on whichever focus is currently + // equipped. This should replicate the old functionality for the most part. + MakePoweredPet(spell_id, pettype, -1, petname); +} + +// Split from the basic MakePet to allow backward compatiblity with existing code while also +// making it possible for petpower to be retained without the focus item having to +// stay equipped when the character zones. petpower of -1 means that the currently equipped petfocus +// of a client is searched for and used instead. +void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, const char *petname) { + // Sanity and early out checking first. + if(HasPet() || pettype == NULL) + return; + + int16 act_power = 0; // The actual pet power we'll use. + if (petpower == -1) { + if (this->IsClient()) + act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id); + } + else if (petpower > 0) + act_power = petpower; + + // optional rule: classic style variance in pets. Achieve this by + // adding a random 0-4 to pet power, since it only comes in increments + // of five from focus effects. + + //lookup our pets table record for this type + PetRecord record; + if(!database.GetPoweredPetEntry(pettype, act_power, &record)) { + Message(13, "Unable to find data for pet %s", pettype); + LogFile->write(EQEMuLog::Error, "Unable to find data for pet %s, check pets table.", pettype); + return; + } + + //find the NPC data for the specified NPC type + const NPCType *base = database.GetNPCType(record.npc_type); + if(base == NULL) { + Message(13, "Unable to load NPC data for pet %s", pettype); + LogFile->write(EQEMuLog::Error, "Unable to load NPC data for pet %s (NPC ID %d), check pets and npc_types tables.", pettype, record.npc_type); + return; + } + + //we copy the npc_type data because we need to edit it a bit + NPCType *npc_type = new NPCType; + memcpy(npc_type, base, sizeof(NPCType)); + + // If pet power is set to -1 in the DB, use stat scaling + if (this->IsClient() && record.petpower == -1) + { + float scale_power = (float)act_power / 100.0f; + if(scale_power > 0) + { + npc_type->max_hp *= (1 + scale_power); + npc_type->cur_hp = npc_type->max_hp; + npc_type->AC *= (1 + scale_power); + npc_type->level += 1 + ((int)act_power / 25); // gains an additional level for every 25 pet power + npc_type->min_dmg = (npc_type->min_dmg * (1 + (scale_power / 2))); + npc_type->max_dmg = (npc_type->max_dmg * (1 + (scale_power / 2))); + npc_type->size *= (1 + (scale_power / 2)); + } + record.petpower = act_power; + } + + //Live AA - Elemental Durability + int16 MaxHP = aabonuses.PetMaxHP + itembonuses.PetMaxHP + spellbonuses.PetMaxHP; + + if (MaxHP){ + npc_type->max_hp += (npc_type->max_hp*MaxHP)/100; + npc_type->cur_hp = npc_type->max_hp; + } + + //TODO: think about regen (engaged vs. not engaged) + + // Pet naming: + // 0 - `s pet + // 1 - `s familiar + // 2 - `s Warder + // 3 - Random name if client, `s pet for others + // 4 - Keep DB name + + + if (petname != NULL) { + // Name was provided, use it. + strn0cpy(npc_type->name, petname, 64); + } else if (record.petnaming == 0) { + strcpy(npc_type->name, this->GetCleanName()); + npc_type->name[25] = '\0'; + strcat(npc_type->name, "`s_pet"); + } else if (record.petnaming == 1) { + strcpy(npc_type->name, this->GetName()); + npc_type->name[19] = '\0'; + strcat(npc_type->name, "`s_familiar"); + } else if (record.petnaming == 2) { + strcpy(npc_type->name, this->GetName()); + npc_type->name[21] = 0; + strcat(npc_type->name, "`s_Warder"); + } else if (record.petnaming == 4) { + // Keep the DB name + } else if (record.petnaming == 3 && IsClient()) { + strcpy(npc_type->name, GetRandPetName()); + } else { + strcpy(npc_type->name, this->GetCleanName()); + npc_type->name[25] = '\0'; + strcat(npc_type->name, "`s_pet"); + } + + //handle beastlord pet appearance + if(record.petnaming == 2) + { + switch(GetBaseRace()) + { + case VAHSHIR: + npc_type->race = TIGER; + npc_type->size *= 0.8f; + break; + case TROLL: + npc_type->race = ALLIGATOR; + npc_type->size *= 2.5f; + break; + case OGRE: + npc_type->race = BEAR; + npc_type->texture = 3; + npc_type->gender = 2; + break; + case BARBARIAN: + npc_type->race = WOLF; + npc_type->texture = 2; + break; + case IKSAR: + npc_type->race = WOLF; + npc_type->texture = 0; + npc_type->gender = 1; + npc_type->size *= 2.0f; + npc_type->luclinface = 0; + break; + default: + npc_type->race = WOLF; + npc_type->texture = 0; + } + } + + // handle monster summoning pet appearance + if(record.monsterflag) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + MYSQL_RES *result = NULL; + MYSQL_ROW row = NULL; + uint32 monsterid; + + // 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,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; + } + + // give the summoned pet the attributes of the monster we found + const NPCType* monster = database.GetNPCType(monsterid); + if(monster) { + npc_type->race = monster->race; + npc_type->size = monster->size; + npc_type->texture = monster->texture; + npc_type->gender = monster->gender; + } + 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 + Pet *npc = new Pet(npc_type, this, (PetType)record.petcontrol, spell_id, record.petpower); + + // Now that we have an actual object to interact with, load + // the base items for the pet. These are always loaded + // so that a rank 1 suspend minion does not kill things + // like the special back items some focused pets may receive. + uint32 petinv[MAX_WORN_INVENTORY]; + memset(petinv, 0, sizeof(petinv)); + const Item_Struct *item = 0; + + if (database.GetBasePetItems(record.equipmentset, petinv)) { + for (int i=0; iAddLootDrop(item, &npc->itemlist, 0, 1, 127, true, true); + } + } + + + entity_list.AddNPC(npc, true, true); + SetPetID(npc->GetID()); + // We need to handle PetType 5 (petHatelist), add the current target to the hatelist of the pet +} +/* This is why the pets ghost - pets were being spawned too far away from its npc owner and some +into walls or objects (+10), this sometimes creates the "ghost" effect. I changed to +2 (as close as I +could get while it still looked good). I also noticed this can happen if an NPC is spawned on the same spot of another or in a related bad spot.*/ +Pet::Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 power) +: NPC(type_data, 0, owner->GetX()+2, owner->GetY()+2, owner->GetZ(), owner->GetHeading(), FlyMode3) +{ + GiveNPCTypeData(type_data); + typeofpet = type; + petpower = power; + SetOwnerID(owner->GetID()); + SetPetSpellID(spell_id); + taunting = true; +} + +bool ZoneDatabase::GetPetEntry(const char *pet_type, PetRecord *into) { + return GetPoweredPetEntry(pet_type, 0, 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; + + 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 (RunQuery(query, querylen, errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + + 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]); + + 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); +} + +Mob* Mob::GetPet() { + if(GetPetID() == 0) + return(NULL); + + Mob* tmp = entity_list.GetMob(GetPetID()); + if(tmp == NULL) { + SetPetID(0); + return(NULL); + } + + if(tmp->GetOwnerID() != GetID()) { + SetPetID(0); + return(NULL); + } + + return(tmp); +} + +void Mob::SetPet(Mob* newpet) { + Mob* oldpet = GetPet(); + if (oldpet) { + oldpet->SetOwnerID(0); + } + if (newpet == NULL) { + SetPetID(0); + } else { + SetPetID(newpet->GetID()); + Mob* oldowner = entity_list.GetMob(newpet->GetOwnerID()); + if (oldowner) + oldowner->SetPetID(0); + newpet->SetOwnerID(this->GetID()); + } +} + +void Mob::SetPetID(uint16 NewPetID) { + if (NewPetID == GetID() && NewPetID != 0) + return; + petid = NewPetID; + + if(IsClient()) + { + Mob* NewPet = entity_list.GetMob(NewPetID); + CastToClient()->UpdateXTargetType(MyPet, NewPet); + } +} + +void NPC::GetPetState(SpellBuff_Struct *pet_buffs, uint32 *items, char *name) { + //save the pet name + strn0cpy(name, GetName(), 64); + + //save their items, we only care about what they are actually wearing + memcpy(items, equipment, sizeof(uint32)*MAX_WORN_INVENTORY); + + //save their buffs. + for (int i=0; i < GetPetMaxTotalSlots(); i++) { + if (buffs[i].spellid != SPELL_UNKNOWN) { + pet_buffs[i].spellid = buffs[i].spellid; + pet_buffs[i].slotid = i+1; + pet_buffs[i].duration = buffs[i].ticsremaining; + pet_buffs[i].level = buffs[i].casterlevel; + pet_buffs[i].effect = 10; + pet_buffs[i].counters = buffs[i].counters; + } + else { + pet_buffs[i].spellid = SPELL_UNKNOWN; + pet_buffs[i].duration = 0; + pet_buffs[i].level = 0; + pet_buffs[i].effect = 0; + pet_buffs[i].counters = 0; + } + } +} + +void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) { + //restore their buffs... + + int i; + for (i = 0; i < GetPetMaxTotalSlots(); i++) { + for(int z = 0; z < GetPetMaxTotalSlots(); z++) { + // check for duplicates + if(buffs[z].spellid != SPELL_UNKNOWN && buffs[z].spellid == pet_buffs[i].spellid) { + buffs[z].spellid = SPELL_UNKNOWN; + pet_buffs[i].spellid = 0xFFFFFFFF; + } + } + + if (pet_buffs[i].spellid <= (uint32)SPDAT_RECORDS && pet_buffs[i].spellid != 0 && pet_buffs[i].duration > 0) { + if(pet_buffs[i].level == 0 || pet_buffs[i].level > 100) + pet_buffs[i].level = 1; + buffs[i].spellid = pet_buffs[i].spellid; + buffs[i].ticsremaining = pet_buffs[i].duration; + buffs[i].casterlevel = pet_buffs[i].level; + buffs[i].casterid = 0; + buffs[i].counters = pet_buffs[i].counters; + buffs[i].numhits = spells[pet_buffs[i].spellid].numhits; + } + else { + buffs[i].spellid = SPELL_UNKNOWN; + pet_buffs[i].spellid = 0xFFFFFFFF; + pet_buffs[i].slotid = 0; + pet_buffs[i].level = 0; + pet_buffs[i].duration = 0; + pet_buffs[i].effect = 0; + } + } + for (int j1=0; j1 < GetPetMaxTotalSlots(); j1++) { + if (buffs[j1].spellid <= (uint32)SPDAT_RECORDS) { + for (int x1=0; x1 < EFFECT_COUNT; x1++) { + switch (spells[buffs[j1].spellid].effectid[x1]) { + case SE_WeaponProc: + // We need to reapply buff based procs + // We need to do this here so suspended pets also regain their procs. + if (spells[buffs[j1].spellid].base2[x1] == 0) { + AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100); + } else { + AddProcToWeapon(GetProcID(buffs[j1].spellid,x1), false, 100+spells[buffs[j1].spellid].base2[x1]); + } + break; + case SE_Charm: + case SE_Rune: + case SE_NegateAttacks: + case SE_Illusion: + buffs[j1].spellid = SPELL_UNKNOWN; + pet_buffs[j1].spellid = SPELLBOOK_UNKNOWN; + pet_buffs[j1].slotid = 0; + pet_buffs[j1].level = 0; + pet_buffs[j1].duration = 0; + pet_buffs[j1].effect = 0; + x1 = EFFECT_COUNT; + break; + // We can't send appearance packets yet, put down at CompleteConnect + } + } + } + } + UpdateRuneFlags(); + + //restore their equipment... + for(i = 0; i < MAX_WORN_INVENTORY; i++) { + if(items[i] == 0) + continue; + + const Item_Struct* item2 = database.GetItem(items[i]); + if (item2 && item2->NoDrop != 0) { + //dont bother saving item charges for now, NPCs never use them + //and nobody should be able to get them off the corpse..? + AddLootDrop(item2, &itemlist, 0, 1, 127, true, true); + } + } +} + +// Load the equipmentset from the DB. Might be worthwhile to load these into +// shared memory at some point due to the number of queries needed to load a +// nested set. +bool ZoneDatabase::GetBasePetItems(int32 equipmentset, uint32 *items) { + if (equipmentset < 0 || items == NULL) + return false; + + // Equipment sets can be nested. We start with the top-most one and + // add all items in it to the items array. Referenced equipmentsets + // are loaded after that, up to a max depth of 5. (Arbitrary limit + // so we don't go into an endless loop if the DB data is cyclic for + // some reason.) + // 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; + uint32 slot; + + // outline: + // get equipmentset from DB. (Mainly check if we exist and get the + // nested ID) + // 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 >= MAX_WORN_INVENTORY) + 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); + return false; + } + } // end while + + return true; +} + diff --git a/zone/pets.h b/zone/pets.h new file mode 100644 index 000000000..420e69699 --- /dev/null +++ b/zone/pets.h @@ -0,0 +1,64 @@ +#ifndef PETS_H +#define PETS_H + + #define PET_BACKOFF 1 + #define PET_GETLOST 2 + #define PET_HEALTHREPORT 4 + #define PET_GUARDHERE 5 + #define PET_GUARDME 6 + #define PET_ATTACK 7 + #define PET_FOLLOWME 8 + #define PET_SITDOWN 9 + #define PET_STANDUP 10 + #define PET_TAUNT 11 + #define PET_HOLD 12 + #define PET_NOTAUNT 14 + #define PET_LEADER 16 + #define PET_SLUMBER 17 + #define PET_NOCAST 18 + #define PET_FOCUS 19 + #define PET_FOCUS_ON 25 + #define PET_FOCUS_OFF 26 + + class Pet : public NPC { + public: + Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 power); + + }; +/* + * I dont have the patience to take on this project today.... +class Pet : public NPC { +public: + enum eStandingPetOrder { SPO_Follow, SPO_Sit, SPO_Guard }; + + + const uint16 pet_spell_id; + + inline void SetPetOrder(eStandingPetOrder i) { pStandingPetOrder = i; } + inline eStandingPetOrder GetPetOrder() const { return pStandingPetOrder; } +// void SetPetType(uint16 in_type) { typeofpet = in_type; } // put this here because only NPCs can be anything but charmed pets + void GetPetState(SpellBuff_Struct *buffs, uint32 *items, char *name); + void SetPetState(SpellBuff_Struct *buffs, uint32 *items); +protected: +// uint16 typeofpet; // 0xFF = charmed + + eStandingPetOrder pStandingPetOrder; + bool taunting; + Timer taunt_timer; //for pet taunting +};*/ + +#endif + + + + + + + + + + + + + + diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp new file mode 100644 index 000000000..e7cb7df85 --- /dev/null +++ b/zone/questmgr.cpp @@ -0,0 +1,2737 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 +*/ + +/* + +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 +using namespace std; + +#include "worldserver.h" +#include "net.h" +#include "../common/skills.h" +#include "../common/classes.h" +#include "../common/races.h" +#include "zonedb.h" +#include "spdat.h" +#include "../common/packet_functions.h" +#include "../common/MiscFunctions.h" +#include "spawn2.h" +#include "zone.h" +#include "parser.h" +#include "event_codes.h" +#include "guild_mgr.h" +#include "../common/rulesys.h" +#include "QGlobals.h" +#include "spdat.h" +#include "QuestParserCollection.h" + +#ifdef BOTS +#include "bot.h" +#endif + + +extern Zone* zone; +extern WorldServer worldserver; +extern EntityList entity_list; + +#include "questmgr.h" + +//declare our global instance +QuestManager quest_manager; + +QuestManager::QuestManager() { + depop_npc = false; + HaveProximitySays = false; +} + +QuestManager::~QuestManager() { +} + +void QuestManager::Process() { + list::iterator cur = QTimerList.begin(), end, tmp; + + end = QTimerList.end(); + while (cur != end) { + if (cur->Timer_.Enabled() && cur->Timer_.Check()) { + //make sure the mob is still in zone. + if(entity_list.IsMobInZone(cur->mob)){ + if(cur->mob->IsNPC()) { + parse->EventNPC(EVENT_TIMER, cur->mob->CastToNPC(), NULL, cur->name, 0); + } + else { + //this is inheriently unsafe if we ever make it so more than npc/client start timers + parse->EventPlayer(EVENT_TIMER, cur->mob->CastToClient(), cur->name, 0); + } + + //we MUST reset our iterator since the quest could have removed/added any + //number of timers... worst case we have to check a bunch of timers twice + cur = QTimerList.begin(); + end = QTimerList.end(); //dunno if this is needed, cant hurt... + } else { + tmp = cur; + tmp++; + QTimerList.erase(cur); + cur = tmp; + } + } else + cur++; + } + + list::iterator curS, endS, tmpS; + + curS = STimerList.begin(); + endS = STimerList.end(); + while (curS != endS) { + if(!curS->Timer_.Enabled()) { + //remove the timer + tmpS = curS; + tmpS++; + STimerList.erase(curS); + curS = tmpS; + } else if(curS->Timer_.Check()) { + //disable the timer so it gets deleted. + curS->Timer_.Disable(); + + //signal the event... + entity_list.SignalMobsByNPCID(curS->npc_id, curS->signal_id); + + //restart for the same reasons as above. + curS = STimerList.begin(); + endS = STimerList.end(); + } else + curS++; + } +} + +void QuestManager::StartQuest(Mob *_owner, Client *_initiator, ItemInst* _questitem) { + quest_mutex.lock(); + owner = _owner; + initiator = _initiator; + questitem = _questitem; + depop_npc = false; +} + +void QuestManager::EndQuest() { + quest_mutex.unlock(); + + if(depop_npc && owner->IsNPC()) { + //clear out any timers for them... + list::iterator cur = QTimerList.begin(), end, tmp; + + end = QTimerList.end(); + while (cur != end) { + if(cur->mob == owner) { + tmp = cur; + tmp++; + QTimerList.erase(cur); + cur = tmp; + } else { + cur++; + } + } + + owner->Depop(); + owner = NULL; //just to be safe + } +} + +void QuestManager::ClearAllTimers() { + + list::iterator cur = QTimerList.begin(), end, tmp; + + end = QTimerList.end(); + while (cur != end) + { + tmp = cur; + tmp++; + QTimerList.erase(cur); + cur = tmp; + } +} + +//quest perl functions +void QuestManager::echo(int colour, const char *str) { + entity_list.MessageClose(initiator, false, 200, colour, str); +} + +void QuestManager::say(const char *str) { + + if(RuleB(NPC, EnableNPCQuestJournal) && initiator) + owner->QuestJournalledSay(initiator, str); + else + owner->Say(str); +} + +void QuestManager::say(const char *str, uint8 language) { + entity_list.ChannelMessage(owner, 8, language, str); +} + +void QuestManager::me(const char *str) { + if (!initiator) + return; + entity_list.MessageClose(initiator, false, 200, 10, str); +} + +void QuestManager::summonitem(uint32 itemid, int16 charges) { + if(!initiator) + return; + initiator->SummonItem(itemid, charges); +} + +void QuestManager::write(const char *file, const char *str) { + FILE * pFile; + pFile = fopen (file, "a"); + if(!pFile) + return; + fprintf(pFile, "%s\n", str); + fclose (pFile); +} + +uint16 QuestManager::spawn2(int npc_type, int grid, int unused, float x, float y, float z, float heading) { + const NPCType* tmp = 0; + if ((tmp = database.GetNPCType(npc_type))) + { + NPC* npc = new NPC(tmp, 0, x, y, z, heading, FlyMode3); + npc->AddLootTable(); + entity_list.AddNPC(npc,true,true); + // Quag: Sleep in main thread? ICK! + // Sleep(200); + // Quag: check is irrelevent, it's impossible for npc to be 0 here + // (we're in main thread, nothing else can possibly modify it) + if(grid > 0) + { + npc->AssignWaypoints(grid); + } + npc->SendPosUpdate(); + return(npc->GetID()); + } + return(0); +} + +uint16 QuestManager::unique_spawn(int npc_type, int grid, int unused, float x, float y, float z, float heading) { + Mob *other = entity_list.GetMobByNpcTypeID(npc_type); + if(other != NULL) { + return(other->GetID()); + } + + const NPCType* tmp = 0; + if ((tmp = database.GetNPCType(npc_type))) + { + NPC* npc = new NPC(tmp, 0, x, y, z, heading, FlyMode3); + npc->AddLootTable(); + entity_list.AddNPC(npc,true,true); + // Quag: Sleep in main thread? ICK! + // Sleep(200); + // Quag: check is irrelevent, it's impossible for npc to be 0 here + // (we're in main thread, nothing else can possibly modify it) + if(grid > 0) + { + npc->AssignWaypoints(grid); + } + npc->SendPosUpdate(); + return(npc->GetID()); + } + return(0); +} + +uint16 QuestManager::spawn_from_spawn2(uint32 spawn2_id) +{ + LinkedListIterator iterator(zone->spawn2_list); + iterator.Reset(); + Spawn2 *found_spawn = NULL; + + while(iterator.MoreElements()) + { + Spawn2* cur = iterator.GetData(); + iterator.Advance(); + if(cur->GetID() == spawn2_id) + { + found_spawn = cur; + break; + } + } + + if(found_spawn) + { + SpawnGroup* sg = zone->spawn_group_list.GetSpawnGroup(found_spawn->SpawnGroupID()); + if(!sg) + { + database.LoadSpawnGroupsByID(found_spawn->SpawnGroupID(),&zone->spawn_group_list); + sg = zone->spawn_group_list.GetSpawnGroup(found_spawn->SpawnGroupID()); + if(!sg) + { + return 0; + } + } + uint32 npcid = sg->GetNPCType(); + if(npcid == 0) + { + return 0; + } + + const NPCType* tmp = database.GetNPCType(npcid); + if(!tmp) + { + return 0; + } + + if(tmp->unique_spawn_by_name) + { + if(!entity_list.LimitCheckName(tmp->name)) + { + return 0; + } + } + + if(tmp->spawn_limit > 0) + { + if(!entity_list.LimitCheckType(npcid, tmp->spawn_limit)) + { + return 0; + } + } + + database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), 0); + found_spawn->SetCurrentNPCID(npcid); + + NPC* npc = new NPC(tmp, found_spawn, found_spawn->GetX(), found_spawn->GetY(), found_spawn->GetZ(), + found_spawn->GetHeading(), FlyMode3); + + found_spawn->SetNPCPointer(npc); + npc->AddLootTable(); + npc->SetSp2(found_spawn->SpawnGroupID()); + entity_list.AddNPC(npc); + entity_list.LimitAddNPC(npc); + + if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay) + npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay); + if(zone->InstantGrids()) + { + found_spawn->LoadGrid(); + } + + return npc->GetID(); + } + return 0; +} + +void QuestManager::enable_spawn2(uint32 spawn2_id) +{ + database.UpdateSpawn2Status(spawn2_id, 1); + ServerPacket* pack = new ServerPacket(ServerOP_SpawnStatusChange, sizeof(ServerSpawnStatusChange_Struct)); + ServerSpawnStatusChange_Struct* ssc = (ServerSpawnStatusChange_Struct*) pack->pBuffer; + ssc->id = spawn2_id; + ssc->new_status = 1; + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::disable_spawn2(uint32 spawn2_id) +{ + database.UpdateSpawn2Status(spawn2_id, 0); + ServerPacket* pack = new ServerPacket(ServerOP_SpawnStatusChange, sizeof(ServerSpawnStatusChange_Struct)); + ServerSpawnStatusChange_Struct* ssc = (ServerSpawnStatusChange_Struct*) pack->pBuffer; + ssc->id = spawn2_id; + ssc->new_status = 0; + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::setstat(int stat, int value) { + if (initiator) + initiator->SetStats(stat, value); +} + +void QuestManager::incstat(int stat, int value) { //old setstat command aza + if (initiator) + initiator->IncStats(stat, value); +} + +void QuestManager::castspell(int spell_id, int target_id) { + if (owner) { + Mob *tgt = entity_list.GetMob(target_id); + if(tgt != NULL) + owner->SpellFinished(spell_id, tgt, 10, 0, -1, spells[spell_id].ResistDiff); + } +} + +void QuestManager::selfcast(int spell_id) { + if (initiator) + initiator->SpellFinished(spell_id, initiator, 10, 0, -1, spells[spell_id].ResistDiff); +} + +void QuestManager::addloot(int item_id, int charges, bool equipitem) { + if(item_id != 0){ + if(owner->IsNPC()) + owner->CastToNPC()->AddItem(item_id, charges, equipitem); + } +} + +void QuestManager::Zone(const char *zone_name) { + if (initiator && initiator->IsClient()) + { + ServerPacket* pack = new ServerPacket(ServerOP_ZoneToZoneRequest, sizeof(ZoneToZone_Struct)); + ZoneToZone_Struct* ztz = (ZoneToZone_Struct*) pack->pBuffer; + ztz->response = 0; + ztz->current_zone_id = zone->GetZoneID(); + ztz->current_instance_id = zone->GetInstanceID(); + ztz->requested_zone_id = database.GetZoneID(zone_name); + ztz->admin = initiator->Admin(); + strcpy(ztz->name, initiator->GetName()); + ztz->guild_id = initiator->GuildID(); + ztz->ignorerestrictions = 3; + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +void QuestManager::settimer(const char *timer_name, int seconds) { + + list::iterator cur = QTimerList.begin(), end; + + end = QTimerList.end(); + while (cur != end) { + if (cur->mob == owner && cur->name == timer_name) { + cur->mob = owner; + cur->Timer_.Enable(); + cur->Timer_.Start(seconds * 1000, false); + printf("Resetting: %s for %d seconds\n", cur->name.c_str(), seconds); + return; + } + cur++; + } + + QTimerList.push_back(QuestTimer(seconds * 1000, owner, timer_name)); +} + +void QuestManager::settimerMS(const char *timer_name, int milliseconds) { + + list::iterator cur = QTimerList.begin(), end; + + end = QTimerList.end(); + while (cur != end) { + if (cur->mob == owner && cur->name == timer_name) { + cur->mob = owner; + cur->Timer_.Enable(); + cur->Timer_.Start(milliseconds, false); + printf("Resetting: %s for %d seconds\n", cur->name.c_str(), milliseconds); + return; + } + cur++; + } + + QTimerList.push_back(QuestTimer(milliseconds, owner, timer_name)); +} + +void QuestManager::stoptimer(const char *timer_name) { + + list::iterator cur = QTimerList.begin(), end; + + end = QTimerList.end(); + while (cur != end) + { + if(cur->mob == owner && cur->name == timer_name) + { + QTimerList.erase(cur); + return; + } + cur++; + } +} + +void QuestManager::stopalltimers() { + + list::iterator cur = QTimerList.begin(), end, tmp; + + end = QTimerList.end(); + while (cur != end) + { + if(cur->mob == owner) + { + tmp = cur; + tmp++; + QTimerList.erase(cur); + cur = tmp; + } + else + { + cur++; + } + } +} + +void QuestManager::emote(const char *str) { + owner->Emote(str); +} + +void QuestManager::shout(const char *str) { + owner->Shout(str); +} + +void QuestManager::shout2(const char *str) { + worldserver.SendEmoteMessage(0,0,0,13, "%s shouts, '%s'", owner->GetCleanName(), str); +} + +void QuestManager::gmsay(const char *str, uint32 color, bool send_to_world, uint32 to_guilddbid) { + if(send_to_world) { + worldserver.SendEmoteMessage(0, to_guilddbid, 80, color, "%s", str); + } + else { + entity_list.MessageStatus(to_guilddbid, 80, color, "%s", str); + } +} + +void QuestManager::depop(int npc_type) { // depop NPC and don't start spawn timer + if (!owner->IsNPC()) + return; + if (npc_type != 0) { + Mob * tmp = entity_list.GetMobByNpcTypeID(npc_type); + if (tmp) { + if (tmp != owner) { + tmp->CastToNPC()->Depop(); + } + else { + depop_npc = true; + } + } + } + else { //depop self + depop_npc = true; + } +} + +void QuestManager::depop_withtimer(int npc_type) { // depop NPC and start spawn timer + if (!owner->IsNPC()) + return; + if (npc_type != 0) { + Mob * tmp = entity_list.GetMobByNpcTypeID(npc_type); + if (tmp) { + if (tmp != owner) { + tmp->CastToNPC()->Depop(true); + } + else { + owner->Depop(true); + } + } + } + else { //depop self + owner->Depop(true); + } +} + +void QuestManager::depopall(int npc_type) { + if(owner->IsNPC() && npc_type > 0) + entity_list.DepopAll(npc_type); +} + +void QuestManager::depopzone(bool StartSpawnTimer) { + if(zone) + zone->Depop(StartSpawnTimer); +} + +void QuestManager::repopzone() { + if(zone) + zone->Repop(); +} + +void QuestManager::settarget(const char *type, int target_id) { + if(!owner->IsNPC()) + return; + Mob* tmp = NULL; + if (!strcasecmp(type,"npctype")) { + tmp = entity_list.GetMobByNpcTypeID(target_id); + } + else if (!strcasecmp(type, "entity")) { + tmp = entity_list.GetMob(target_id); + } + if(tmp != NULL) { + owner->SetTarget(tmp); + } +} + +void QuestManager::follow(int entity_id, int distance) { + if(!owner->IsNPC()) + return; + owner->SetFollowID(entity_id); + owner->SetFollowDistance(distance * distance); +} + +void QuestManager::sfollow() { + if(!owner->IsNPC()) + return; + owner->SetFollowID(0); +} + +void QuestManager::changedeity(int diety_id) { + //Cofruben:-Changes the deity. + if(initiator) + { + if(initiator->IsClient()) + { + initiator->SetDeity(diety_id); + initiator->Message(15,"Your Deity has been changed/set to: %i", diety_id); + initiator->Save(1); + initiator->Kick(); + } + else + { + initiator->Message(15,"Error changing Deity"); + } + } +} + +void QuestManager::exp(int amt) { + if (initiator && initiator->IsClient()) + initiator->AddEXP(amt); +} + +void QuestManager::level(int newlevel) { + if (initiator && initiator->IsClient()) + initiator->SetLevel(newlevel, true); +} + +void QuestManager::traindisc(int discipline_tome_item_id) { + if (initiator && initiator->IsClient()) + initiator->TrainDiscipline(discipline_tome_item_id); +} + +bool QuestManager::isdisctome(int item_id) { +//get the item info + const Item_Struct *item = database.GetItem(item_id); + if(item == NULL) { + return(false); + } + + if(item->ItemClass != ItemClassCommon || item->ItemType != ItemTypeSpell) { + return(false); + } + + //Need a way to determine the difference between a spell and a tome + //so they cant turn in a spell and get it as a discipline + //this is kinda a hack: + if(!( + item->Name[0] == 'T' && + item->Name[1] == 'o' && + item->Name[2] == 'm' && + item->Name[3] == 'e' && + item->Name[4] == ' ' + )) { + return(false); + } + + //we know for sure none of the int casters get disciplines + uint32 cbit = 0; + cbit |= 1 << (WIZARD-1); + cbit |= 1 << (ENCHANTER-1); + cbit |= 1 << (MAGICIAN-1); + cbit |= 1 << (NECROMANCER-1); + if(item->Classes & cbit) { + return(false); + } + + uint32 spell_id = item->Scroll.Effect; + if(!IsValidSpell(spell_id)) { + return(false); + } + + //we know for sure none of the int casters get disciplines + const SPDat_Spell_Struct &spell = spells[spell_id]; + if( + spell.classes[WIZARD - 1] != 255 && + spell.classes[ENCHANTER - 1] != 255 && + spell.classes[MAGICIAN - 1] != 255 && + spell.classes[NECROMANCER - 1] != 255 + ) { + return(false); + } + + return(true); +} + +void QuestManager::safemove() { + if (initiator && initiator->IsClient()) + initiator->GoToSafeCoords(zone->GetZoneID(), 0); +} + +void QuestManager::rain(int weather) { + zone->zone_weather = weather; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Weather, 8); + *((uint32*) &outapp->pBuffer[4]) = (uint32) weather; // Why not just use 0x01/2/3? + entity_list.QueueClients(owner, outapp); + safe_delete(outapp); +} + +void QuestManager::snow(int weather) { + zone->zone_weather = weather + 1; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Weather, 8); + outapp->pBuffer[0] = 0x01; + *((uint32*) &outapp->pBuffer[4]) = (uint32)weather; + entity_list.QueueClients(initiator, outapp); + safe_delete(outapp); +} + +void QuestManager::surname(const char *name) { + //Cofruben:-Changes the last name. + if(initiator) + { + if(initiator->IsClient()) + { + initiator->ChangeLastName(name); + initiator->Message(15,"Your surname has been changed/set to: %s", name); + } + else + { + initiator->Message(15,"Error changing/setting surname"); + } + } +} + +void QuestManager::permaclass(int class_id) { + //Cofruben:-Makes the client the class specified + initiator->SetBaseClass(class_id); + initiator->Save(2); + initiator->Kick(); +} + +void QuestManager::permarace(int race_id) { + //Cofruben:-Makes the client the race specified + initiator->SetBaseRace(race_id); + initiator->Save(2); + initiator->Kick(); +} + +void QuestManager::permagender(int gender_id) { + //Cofruben:-Makes the client the gender specified + initiator->SetBaseGender(gender_id); + initiator->Save(2); + initiator->Kick(); +} + +uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) { + uint16 book_slot, count; + uint16 curspell; + + uint16 Char_ID = initiator->CharacterID(); + bool SpellGlobalRule = RuleB(Spells, EnableSpellGlobals); + bool SpellGlobalCheckResult = 0; + + + for(curspell = 0, book_slot = initiator->GetNextAvailableSpellBookSlot(), count = 0; curspell < SPDAT_RECORDS && book_slot < MAX_PP_SPELLBOOK; curspell++, book_slot = initiator->GetNextAvailableSpellBookSlot(book_slot)) + { + if + ( + spells[curspell].classes[WARRIOR] != 0 && //check if spell exists + spells[curspell].classes[initiator->GetPP().class_-1] <= max_level && //maximum level + spells[curspell].classes[initiator->GetPP().class_-1] >= min_level && //minimum level + spells[curspell].skill != 52 + ) + { + if (book_slot == -1) //no more book slots + break; + if(!IsDiscipline(curspell) && !initiator->HasSpellScribed(curspell)) { //isn't a discipline & we don't already have it scribed + if (SpellGlobalRule) { + // Bool to see if the character has the required QGlobal to scribe it if one exists in the Spell_Globals table + SpellGlobalCheckResult = initiator->SpellGlobalCheck(curspell, Char_ID); + if (SpellGlobalCheckResult) { + initiator->ScribeSpell(curspell, book_slot); + count++; + } + } + else { + initiator->ScribeSpell(curspell, book_slot); + count++; + } + } + } + } + return count; //how many spells were scribed successfully +} + +uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) { + uint16 count; + uint16 curspell; + + uint16 Char_ID = initiator->CharacterID(); + bool SpellGlobalRule = RuleB(Spells, EnableSpellGlobals); + bool SpellGlobalCheckResult = 0; + + for(curspell = 0, count = 0; curspell < SPDAT_RECORDS; curspell++) + { + if + ( + spells[curspell].classes[WARRIOR] != 0 && //check if spell exists + spells[curspell].classes[initiator->GetPP().class_-1] <= max_level && //maximum level + spells[curspell].classes[initiator->GetPP().class_-1] >= min_level && //minimum level + spells[curspell].skill != 52 + ) + { + 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++) { + if(initiator->GetPP().disciplines.values[r] == curspell) { + initiator->Message(13, "You already know this discipline."); + break; //continue the 1st loop + } + else if(initiator->GetPP().disciplines.values[r] == 0) { + if (SpellGlobalRule) { + // Bool to see if the character has the required QGlobal to train it if one exists in the Spell_Globals table + SpellGlobalCheckResult = initiator->SpellGlobalCheck(curspell, Char_ID); + if (SpellGlobalCheckResult) { + 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 + } + 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 + } + } //if we get to this point, there's already a discipline in this slot, so we skip it + } + } + } + } + return count; //how many disciplines were learned successfully +} + +void QuestManager::unscribespells() { + initiator->UnscribeSpellAll(); + } + +void QuestManager::untraindiscs() { + initiator->UntrainDiscAll(); +} + +void QuestManager::givecash(int copper, int silver, int gold, int platinum) { + if (initiator && initiator->IsClient() && ((copper + silver + gold + platinum) > 0)) + { + initiator->AddMoneyToPP(copper, silver, gold, platinum, true); + + string tmp; + if (platinum > 0) + { + tmp = "You receive "; + tmp += itoa(platinum); + tmp += " platinum"; + } + if (gold > 0) + { + if (tmp.length() == 0) + tmp = "You receive "; + else + tmp += ","; + + tmp += itoa(gold); + tmp += " gold"; + } + if(silver > 0) + { + if (tmp.length() == 0) + tmp = "You receive "; + else + tmp += ","; + + tmp += itoa(silver); + tmp += " silver"; + } + if(copper > 0) + { + if (tmp.length() == 0) + tmp = "You receive "; + else + tmp += ","; + + tmp += itoa(copper); + tmp += " copper"; + } + tmp += " pieces."; + if (initiator) + initiator->Message(MT_OOC, tmp.c_str()); + } +} + +void QuestManager::pvp(const char *mode) { + if (!strcasecmp(mode,"on")) + { + if (initiator) + initiator->SetPVP(true); + } + else + if (initiator) + initiator->SetPVP(false); +} + +void QuestManager::movepc(int zone_id, float x, float y, float z, float heading) { + if (initiator && initiator->IsClient()) + initiator->MovePC(zone_id, x, y, z, heading); +} + +void QuestManager::gmmove(float x, float y, float z) { + if (initiator && initiator->IsClient()) + initiator->GMMove(x, y, z); +} + +void QuestManager::movegrp(int zoneid, float x, float y, float z) { + if (initiator && initiator->IsClient()) + { + Group *g = entity_list.GetGroupByClient(initiator); + if (g != NULL){ + g->TeleportGroup(owner, zoneid, 0, x, y, z, 0.0f); + } else { + Raid *r = entity_list.GetRaidByClient(initiator); + if (r != NULL){ + uint32 gid = r->GetGroup(initiator); + if (gid >= 0 && gid < 12) { + r->TeleportGroup(owner, zoneid, 0, x, y, z, 0.0f, gid); + } else { + initiator->MovePC(zoneid, x, y, z, 0.0f); + } + } else { + initiator->MovePC(zoneid, x, y, z, 0.0f); + } + } + } +} + +void QuestManager::doanim(int anim_id) { + owner->DoAnim(anim_id); +} + +void QuestManager::addskill(int skill_id, int value) { + if(skill_id < 0 || skill_id > HIGHEST_SKILL) //must check before casting. + return; + if (initiator && initiator->IsClient()) + initiator->AddSkill((SkillType) skill_id, value); +} + +void QuestManager::setlanguage(int skill_id, int value) { + if (initiator && initiator->IsClient()) + initiator->SetLanguageSkill(skill_id, value); +} + +void QuestManager::setskill(int skill_id, int value) { + if(skill_id < 0 || skill_id > HIGHEST_SKILL) //must check before casting. + return; + if (initiator && initiator->IsClient()) + initiator->SetSkill((SkillType) skill_id, value); +} + +void QuestManager::setallskill(int value) { + if (!initiator) + return; + if (initiator && initiator->IsClient()) { + SkillType sk; + for (sk = _1H_BLUNT; sk <= HIGHEST_SKILL; sk = (SkillType)(sk+1)) { + initiator->SetSkill(sk, value); + } + } +} + +void QuestManager::attack(const char *client_name) { + if(!owner->IsNPC()) + return; + Client* getclient = entity_list.GetClientByName(client_name); + if(getclient && owner->IsAttackAllowed(getclient)) { + owner->AddToHateList(getclient,1); + } else { + owner->Say("I am unable to attack %s.", client_name); + } +} + +void QuestManager::attacknpc(int npc_entity_id) { + if(!owner->IsNPC()) + return; + Mob *it = entity_list.GetMob(npc_entity_id); + if(it && owner->IsAttackAllowed(it)) { + owner->AddToHateList(it,1); + } else { + if(it) + owner->Say("I am unable to attack %s.", it->GetName()); + else + owner->Say("I am unable to locate NPC entity %i", npc_entity_id); + } +} + +void QuestManager::attacknpctype(int npc_type_id) { + if(!owner->IsNPC()) + return; + Mob *it = entity_list.GetMobByNpcTypeID(npc_type_id); + if(it && owner->IsAttackAllowed(it)) { + owner->AddToHateList(it,1); + } else { + if(it) + owner->Say("I am unable to attack %s.", it->GetName()); + else + owner->Say("I am unable to locate NPC type %i", npc_type_id); + } +} + +void QuestManager::save() { + if (initiator && initiator->IsClient()) + initiator->Save(); +} + +void QuestManager::faction(int faction_id, int faction_value, int temp) { + if (initiator && initiator->IsClient()) { + if(faction_id != 0 && faction_value != 0) { + // SCORPIOUS2K - fixed faction command + //Client *p; + initiator->SetFactionLevel2( + initiator->CharacterID(), + faction_id, + initiator->GetBaseClass(), + initiator->GetBaseRace(), + initiator->GetDeity(), + faction_value, + temp); + + } + } +} + +void QuestManager::setsky(uint8 new_sky) { + if (zone) + zone->newzone_data.sky = new_sky; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_NewZone, sizeof(NewZone_Struct)); + memcpy(outapp->pBuffer, &zone->newzone_data, outapp->size); + entity_list.QueueClients(initiator, outapp); + safe_delete(outapp); +} + +void QuestManager::setguild(uint32 new_guild_id, uint8 new_rank) { + if (initiator && initiator->IsClient()) { + guild_mgr.SetGuild(initiator->CharacterID(), new_guild_id, new_rank); + } +} + +void QuestManager::CreateGuild(const char *guild_name, const char *leader) { + uint32 cid = database.GetCharacterID(leader); + char hString[250]; + if (cid == 0) { + worldserver.SendEmoteMessage(0, 0, 80, 15, "%s", "Guild Creation: Guild leader not found."); + return; + } + + uint32 tmp = guild_mgr.FindGuildByLeader(cid); + if (tmp != GUILD_NONE) { + sprintf(hString, "Guild Creation: Error: %s already is the leader of DB# %i '%s'.", leader, tmp, guild_mgr.GetGuildName(tmp)); + worldserver.SendEmoteMessage(0, 0, 80, 15, "%s", hString); + } + else { + uint32 gid = guild_mgr.CreateGuild(guild_name, cid); + if (gid == GUILD_NONE) + worldserver.SendEmoteMessage(0, 0, 80, 15, "%s", "Guild Creation: Guild creation failed"); + else { + sprintf(hString, "Guild Creation: Guild created: Leader: %i, number %i: %s", cid, gid, leader); + worldserver.SendEmoteMessage(0, 0, 80, 15, "%s", hString); + if(!guild_mgr.SetGuild(cid, gid, GUILD_LEADER)) + worldserver.SendEmoteMessage(0, 0, 80, 15, "%s", "Unable to set guild leader's guild in the database. Your going to have to run #guild set"); + } + + } +} + +void QuestManager::settime(uint8 new_hour, uint8 new_min) { + if (zone) + zone->SetTime(new_hour + 1, new_min); +} + +void QuestManager::itemlink(int item_id) { + const ItemInst* inst = database.CreateItem(item_id); + char* link = 0; + if (initiator->MakeItemLink(link, inst)) + initiator->Message(0, "%s tells you, %c%s%s%c", owner->GetCleanName(), 0x12, link, inst->GetItem()->Name, 0x12); + safe_delete_array(link); + safe_delete(inst); +} + +void QuestManager::signalwith(int npc_id, int signal_id, int wait_ms) { +// SCORPIOUS2K - signal command + // signal(npcid) - generates EVENT_SIGNAL on specified npc + if(wait_ms > 0) { + STimerList.push_back(SignalTimer(wait_ms, npc_id, signal_id)); + return; + } + + if (npc_id<1) + { + printf("signal() bad npcid=%i\n",npc_id); + } + else + { + //initiator* signalnpc=0; + entity_list.SignalMobsByNPCID(npc_id, signal_id); + } +} + +void QuestManager::signal(int npc_id, int wait_ms) { + signalwith(npc_id, 0, wait_ms); +} + +void QuestManager::setglobal(const char *varname, const char *newvalue, int options, const char *duration) { +// SCORPIOUS2K - qglobal variable commands + // setglobal(varname,value,options,duration) + //MYSQL_ROW row; + int qgZoneid=zone->GetZoneID(); + int qgCharid=0; + int qgNpcid = owner->GetNPCTypeID(); + + /* 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 + */ + 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 + } + if (options < 0 || options > 7) + { + cerr << "Invalid options for global var " << varname << " using defaults" << endl; + } // default = 0 (only this npcid,player and zone) + else + { + if (options & 1) + qgNpcid=0; + if (options & 2) + qgCharid=0; + if (options & 4) + qgZoneid=0; + } + + InsertQuestGlobal(qgCharid, qgNpcid, qgZoneid, varname, newvalue, QGVarDuration(duration)); +} + +/* Inserts global variable into quest_globals table */ +int QuestManager::InsertQuestGlobal( + int charid, int npcid, int zoneid, + const char *varname, const char *varvalue, + int duration) +{ + char *query = 0; + char errbuf[MYSQL_ERRMSG_SIZE]; + + // Make duration string either "unix_timestamp(now()) + xxx" or "NULL" + 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, NULL, NULL, &last_id)) + { + cerr << "setglobal error inserting " << varname << " : " << errbuf << endl; + } + safe_delete_array(query); + + 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); + + 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); + + worldserver.SendPacket(pack); + safe_delete(pack); + + //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(); + + 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) +{ + InsertQuestGlobal(qgCharid, qgNpcid, qgZoneid, varname, value, QGVarDuration(duration)); +} + +void QuestManager::delglobal(const char *varname) { + // delglobal(varname) + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + int qgZoneid=zone->GetZoneID(); + int qgCharid=0; + int qgNpcid=owner->GetNPCTypeID(); + if (initiator && initiator->IsClient()) // some events like waypoint and spawn don't have a player involved + { + qgCharid=initiator->CharacterID(); + } + + else + { + 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)) + { + cerr << "delglobal error deleting " << varname << " : " << errbuf << endl; + } + safe_delete_array(query); + + if(zone) + { + ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct)); + ServerQGlobalDelete_Struct *qgu = (ServerQGlobalDelete_Struct*)pack->pBuffer; + + 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) +// Return of INT_MAX indicates infinite duration +int QuestManager::QGVarDuration(const char *fmt) +{ + int duration = 0; + + // format: Y#### or D## or H## or M## or S## or T###### or C####### + + int len = strlen(fmt); + + // Default to no duration + if (len < 1) + return 0; + + // Set val to value after type character + // e.g., for "M3924", set to 3924 + int val = atoi(&fmt[0] + 1); + + switch (fmt[0]) + { + // Forever + case 'F': + case 'f': + duration = INT_MAX; + break; + // Years + case 'Y': + case 'y': + duration = val * 31556926; + break; + case 'D': + case 'd': + duration = val * 86400; + break; + // Hours + case 'H': + case 'h': + duration = val * 3600; + break; + // Minutes + case 'M': + case 'm': + duration = val * 60; + break; + // Seconds + case 'S': + case 's': + duration = val; + break; + // Invalid + default: + duration = 0; + break; + } + + return duration; +} + +void QuestManager::ding() { + //-Cofruben:makes a sound. + if (initiator && initiator->IsClient()) + initiator->SendSound(); + +} + +void QuestManager::rebind(int zoneid, float x, float y, float z) { + if(initiator && initiator->IsClient()) { + initiator->SetBindPoint(zoneid, x, y, z); + } +} + +void QuestManager::start(int32 wp) { + if(!owner->IsNPC()) + return; + owner->CastToNPC()->AssignWaypoints(wp); +} + +void QuestManager::stop() { + if(!owner->IsNPC()) + return; + owner->CastToNPC()->StopWandering(); +} + +void QuestManager::pause(int duration) { + if(!owner->IsNPC()) + return; + owner->CastToNPC()->PauseWandering(duration); +} + +void QuestManager::moveto(float x, float y, float z, float h, bool saveguardspot) { + if(!owner->IsNPC()) + return; + owner->CastToNPC()->MoveTo(x, y, z, h, saveguardspot); +} + +void QuestManager::resume() { + if(!owner->IsNPC()) + return; + owner->CastToNPC()->ResumeWandering(); +} + +void QuestManager::addldonpoints(int32 points, uint32 theme) { + if(initiator) + initiator->UpdateLDoNPoints(points, theme); +} + +void QuestManager::addldonwin(int32 wins, uint32 theme) { + if(initiator) + initiator->UpdateLDoNWins(theme, wins); +} + +void QuestManager::addldonloss(int32 losses, uint32 theme) { + if(initiator) + initiator->UpdateLDoNLosses(theme, losses); +} + +void QuestManager::setnexthpevent(int at) { + owner->SetNextHPEvent( at ); +} + +void QuestManager::setnextinchpevent(int at) { + owner->SetNextIncHPEvent( at ); +} + +void QuestManager::respawn(int npc_type, int grid) { + if(!owner->IsNPC()) + return; + //char tempa[100]; + float x,y,z,h; + if ( !owner ) + return; + + x = owner->GetX(); + y = owner->GetY(); + z = owner->GetZ(); + h = owner->GetHeading(); + depop_npc = true; + + const NPCType* tmp = 0; + if ((tmp = database.GetNPCType(npc_type))) + { + owner = new NPC(tmp, 0, x, y, z, h, FlyMode3); + owner->CastToNPC()->AddLootTable(); + entity_list.AddNPC(owner->CastToNPC(),true,true); + if(grid > 0) + owner->CastToNPC()->AssignWaypoints(grid); + + owner->SendPosUpdate(); + } +} + +void QuestManager::set_proximity(float minx, float maxx, float miny, float maxy, float minz, float maxz) { + if(!owner->IsNPC()) + return; + + entity_list.AddProximity(owner->CastToNPC()); + + owner->CastToNPC()->proximity->min_x = minx; + owner->CastToNPC()->proximity->max_x = maxx; + owner->CastToNPC()->proximity->min_y = miny; + owner->CastToNPC()->proximity->max_y = maxy; + owner->CastToNPC()->proximity->min_z = minz; + owner->CastToNPC()->proximity->max_z = maxz; + + owner->CastToNPC()->proximity->say = parse->HasQuestSub(owner->CastToNPC()->GetNPCTypeID(),"EVENT_PROXIMITY_SAY"); + + if(owner->CastToNPC()->proximity->say) + HaveProximitySays = true; +} + +void QuestManager::clear_proximity() { + if(!owner->IsNPC()) + return; + entity_list.RemoveProximity(owner->GetID()); + safe_delete(owner->CastToNPC()->proximity); +} + +void QuestManager::setanim(int npc_type, int animnum) { + //Cisyouc: adds appearance changes + Mob* thenpc = entity_list.GetMobByNpcTypeID(npc_type); + if(animnum < 0 || animnum >= _eaMaxAppearance) + return; + thenpc->SetAppearance(EmuAppearance(animnum)); +} + + +//displays an in game path based on a waypoint grid +void QuestManager::showgrid(int grid) { + if(initiator == NULL) + return; + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + FindPerson_Point pt; + vector pts; + + pt.x = initiator->GetX(); + pt.y = initiator->GetY(); + pt.z = initiator->GetZ(); + 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); + return; + } + safe_delete_array(query); +} + +//displays an in game path based on path finding. +void QuestManager::showpath(float x, float y, float z) { + say("showpath not implemented yet."); +} + +//causes the npc to use path finding to walk to x,y,z +void QuestManager::pathto(float x, float y, float z) { + say("pathto not implemented yet."); +} + +//change the value of a spawn condition +void QuestManager::spawn_condition(const char *zone_short, uint32 instance_id, uint16 condition_id, short new_value) { + zone->spawn_conditions.SetCondition(zone_short, instance_id, condition_id, new_value); +} + +//get the value of a spawn condition +short QuestManager::get_spawn_condition(const char *zone_short, uint32 instance_id, uint16 condition_id) { + return(zone->spawn_conditions.GetCondition(zone_short, instance_id, condition_id)); +} + +//toggle a spawn event +void QuestManager::toggle_spawn_event(int event_id, bool enable, bool reset_base) { + zone->spawn_conditions.ToggleEvent(event_id, enable, reset_base); +} + +bool QuestManager::has_zone_flag(int zone_id) { + return(initiator->HasZoneFlag(zone_id)); +} + +void QuestManager::set_zone_flag(int zone_id) { + initiator->SetZoneFlag(zone_id); +} + +void QuestManager::clear_zone_flag(int zone_id) { + initiator->ClearZoneFlag(zone_id); +} + +void QuestManager::sethp(int hpperc) { + int newhp; + + newhp = (owner->GetMaxHP()*(100-hpperc))/100; + owner->Damage(owner, newhp, SPELL_UNKNOWN, HAND_TO_HAND, false, 0, false); +} + +bool QuestManager::summonburriedplayercorpse(uint32 char_id, float dest_x, float dest_y, float dest_z, float dest_heading) { + 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); + if(PlayerCorpse) { + Result = true; + } + } + return Result; +} + +bool QuestManager::summonallplayercorpses(uint32 char_id, float dest_x, float dest_y, float dest_z, float dest_heading) { + bool Result = false; + + if(char_id > 0) { + Client* c = entity_list.GetClientByCharID(char_id); + c->SummonAllCorpses(dest_x, dest_y, dest_z, dest_heading); + Result = true; + } + return Result; +} + +uint32 QuestManager::getplayerburriedcorpsecount(uint32 char_id) { + uint32 Result = 0; + + if(char_id > 0) { + Result = database.GetPlayerBurriedCorpseCount(char_id); + } + return Result; +} + +bool QuestManager::buryplayercorpse(uint32 char_id) +{ + bool Result = false; + + if(char_id > 0) + { + uint32 PlayerCorpse = database.GetFirstCorpseID(char_id); + if(PlayerCorpse > 0) + { + database.BuryPlayerCorpse(PlayerCorpse); + Corpse* corpse = entity_list.GetCorpseByDBID(PlayerCorpse); + if(corpse) + { + corpse->Save(); + corpse->DepopCorpse(); + } + else + { + Client *c = entity_list.GetClientByCharID(char_id); + c->DepopPlayerCorpse(PlayerCorpse); + } + Result = true; + } + } + return Result; +} + +void QuestManager::forcedooropen(uint32 doorid, bool altmode) { + Doors* d = entity_list.FindDoor(doorid); + if(d){ + if(GetInitiator()) + d->ForceOpen(GetInitiator(), altmode); + else if(GetOwner()) + d->ForceOpen(GetOwner(), altmode); + } +} + +void QuestManager::forcedoorclose(uint32 doorid, bool altmode) { + Doors* d = entity_list.FindDoor(doorid); + if(d){ + if(GetInitiator()) + d->ForceClose(GetInitiator(), altmode); + else if(GetOwner()) + d->ForceClose(GetOwner(), altmode); + } +} + +void QuestManager::toggledoorstate(uint32 doorid) { + Doors* d = entity_list.FindDoor(doorid); + if(d){ + if(GetInitiator()) + d->ToggleState(GetInitiator()); + else if(GetOwner()) + d->ToggleState(GetOwner()); + } +} + +bool QuestManager::isdooropen(uint32 doorid) { + Doors* d = entity_list.FindDoor(doorid); + if(d){ + return d->IsDoorOpen(); + } + return false; +} +void QuestManager::npcrace(int race_id) +{ + owner->SendIllusionPacket(race_id); +} + +void QuestManager::npcgender(int gender_id) +{ + owner->SendIllusionPacket(owner->GetRace(), gender_id); +} +void QuestManager::npcsize(int newsize) +{ + owner->ChangeSize(newsize, true); +} +void QuestManager::npctexture(int newtexture) +{ + owner->SendIllusionPacket(owner->GetRace(), 0xFF, newtexture); +} + +void QuestManager::playerrace(int race_id) +{ + initiator->SendIllusionPacket(race_id); +} + +void QuestManager::playergender(int gender_id) +{ + initiator->SendIllusionPacket(initiator->GetRace(), gender_id); +} + +void QuestManager::playersize(int newsize) +{ + initiator->ChangeSize(newsize, true); +} + +void QuestManager::playertexture(int newtexture) +{ + initiator->SendIllusionPacket(initiator->GetRace(), 0xFF, newtexture); +} + +void QuestManager::playerfeature(char *feature, int setting) +{ + uint16 Race = initiator->GetRace(); + uint8 Gender = initiator->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = initiator->GetHairColor(); + uint8 BeardColor = initiator->GetBeardColor(); + uint8 EyeColor1 = initiator->GetEyeColor1(); + uint8 EyeColor2 = initiator->GetEyeColor2(); + uint8 HairStyle = initiator->GetHairStyle(); + uint8 LuclinFace = initiator->GetLuclinFace(); + uint8 Beard = initiator->GetBeard(); + uint32 DrakkinHeritage = initiator->GetDrakkinHeritage(); + uint32 DrakkinTattoo = initiator->GetDrakkinTattoo(); + uint32 DrakkinDetails = initiator->GetDrakkinDetails(); + float Size = initiator->GetSize(); + + if (!strcasecmp(feature,"race")) + Race = setting; + else if (!strcasecmp(feature,"gender")) + Gender = setting; + else if (!strcasecmp(feature,"texture")) + Texture = setting; + else if (!strcasecmp(feature,"helm")) + HelmTexture = setting; + else if (!strcasecmp(feature,"haircolor")) + HairColor = setting; + else if (!strcasecmp(feature,"beardcolor")) + BeardColor = setting; + else if (!strcasecmp(feature,"eyecolor1")) + EyeColor1 = setting; + else if (!strcasecmp(feature,"eyecolor2")) + EyeColor2 = setting; + else if (!strcasecmp(feature,"hair")) + HairStyle = setting; + else if (!strcasecmp(feature,"face")) + LuclinFace = setting; + else if (!strcasecmp(feature,"beard")) + Beard = setting; + else if (!strcasecmp(feature,"heritage")) + DrakkinHeritage = setting; + else if (!strcasecmp(feature,"tattoo")) + DrakkinTattoo = setting; + else if (!strcasecmp(feature,"details")) + DrakkinDetails = setting; + else if (!strcasecmp(feature,"size")) + Size = (float)setting / 10; //dividing by 10 to allow 1 decimal place for adjusting size + else + return; + + initiator->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails, Size); +} + +void QuestManager::npcfeature(char *feature, int setting) +{ + uint16 Race = owner->GetRace(); + uint8 Gender = owner->GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = owner->GetHairColor(); + uint8 BeardColor = owner->GetBeardColor(); + uint8 EyeColor1 = owner->GetEyeColor1(); + uint8 EyeColor2 = owner->GetEyeColor2(); + uint8 HairStyle = owner->GetHairStyle(); + uint8 LuclinFace = owner->GetLuclinFace(); + uint8 Beard = owner->GetBeard(); + uint32 DrakkinHeritage = owner->GetDrakkinHeritage(); + uint32 DrakkinTattoo = owner->GetDrakkinTattoo(); + uint32 DrakkinDetails = owner->GetDrakkinDetails(); + float Size = owner->GetSize(); + + if (!strcasecmp(feature,"race")) + Race = setting; + else if (!strcasecmp(feature,"gender")) + Gender = setting; + else if (!strcasecmp(feature,"texture")) + Texture = setting; + else if (!strcasecmp(feature,"helm")) + HelmTexture = setting; + else if (!strcasecmp(feature,"haircolor")) + HairColor = setting; + else if (!strcasecmp(feature,"beardcolor")) + BeardColor = setting; + else if (!strcasecmp(feature,"eyecolor1")) + EyeColor1 = setting; + else if (!strcasecmp(feature,"eyecolor2")) + EyeColor2 = setting; + else if (!strcasecmp(feature,"hair")) + HairStyle = setting; + else if (!strcasecmp(feature,"face")) + LuclinFace = setting; + else if (!strcasecmp(feature,"beard")) + Beard = setting; + else if (!strcasecmp(feature,"heritage")) + DrakkinHeritage = setting; + else if (!strcasecmp(feature,"tattoo")) + DrakkinTattoo = setting; + else if (!strcasecmp(feature,"details")) + DrakkinDetails = setting; + else if (!strcasecmp(feature,"size")) + Size = (float)setting / 10; //dividing by 10 to allow 1 decimal place for adjusting size + else + return; + + owner->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, + DrakkinHeritage, DrakkinTattoo, DrakkinDetails, Size); +} + +void QuestManager::popup(char *title, char *text, uint32 popupid, uint32 buttons, uint32 Duration) +{ + if(initiator) initiator->SendPopupToClient(title, text, popupid, buttons, Duration); +} + +#ifdef BOTS + +int QuestManager::createbotcount() { + return RuleI(Bots, CreateBotCount); +} + +int QuestManager::spawnbotcount() { + return RuleI(Bots, SpawnBotCount); +} + +bool QuestManager::botquest() +{ + return RuleB(Bots, BotQuest); +} + +bool QuestManager::createBot(const char *name, const char *lastname, uint8 level, uint16 race, uint8 botclass, uint8 gender) +{ + std::string TempErrorMessage; + uint32 MaxBotCreate = RuleI(Bots, CreateBotCount); + + if (initiator && initiator->IsClient()) + { + if(Bot::SpawnedBotCount(initiator->CharacterID(), &TempErrorMessage) >= MaxBotCreate) + { + initiator->Message(15,"You have the maximum number of bots allowed."); + return false; + } + + if(!TempErrorMessage.empty()) + { + initiator->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return false; + } + + NPCType DefaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(name, lastname, level, race, botclass, gender); + Bot* NewBot = new Bot(DefaultNPCTypeStruct, initiator); + + if(NewBot) + { + if(!NewBot->IsValidRaceClassCombo()) { + initiator->Message(0, "That Race/Class combination cannot be created."); + return false; + } + + if(!NewBot->IsValidName()) { + initiator->Message(0, "%s has invalid characters. You can use only the A-Z, a-z and _ characters in a bot name.", NewBot->GetCleanName()); + return false; + } + + if(!NewBot->IsBotNameAvailable(&TempErrorMessage)) { + initiator->Message(0, "The name %s is already being used. Please choose a different name.", NewBot->GetCleanName()); + return false; + } + + if(!TempErrorMessage.empty()) { + initiator->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + return false; + } + + // Now that all validation is complete, we can save our newly created bot + if(!NewBot->Save()) + { + initiator->Message(0, "Unable to save %s as a bot.", NewBot->GetCleanName()); + } + else + { + initiator->Message(0, "%s saved as bot %u.", NewBot->GetCleanName(), NewBot->GetBotID()); + return true; + } + } + } + return false; +} + +#endif //BOTS + +void QuestManager::taskselector(int taskcount, int *tasks) { + if(RuleB(TaskSystem, EnableTaskSystem) && initiator && owner && taskmanager) + taskmanager->SendTaskSelector(initiator, owner, taskcount, tasks); +} +void QuestManager::enabletask(int taskcount, int *tasks) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator && taskmanager) + initiator->EnableTask(taskcount, tasks); +} + +void QuestManager::disabletask(int taskcount, int *tasks) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator && taskmanager) + initiator->DisableTask(taskcount, tasks); +} + +bool QuestManager::istaskenabled(int taskid) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator && taskmanager) + return initiator->IsTaskEnabled(taskid); + + return false; +} + +void QuestManager::tasksetselector(int tasksetid) { + _log(TASKS__UPDATE, "TaskSetSelector called for task set %i", tasksetid); + if(RuleB(TaskSystem, EnableTaskSystem) && initiator && owner && taskmanager) + initiator->TaskSetSelector(owner, tasksetid); +} + +bool QuestManager::istaskactive(int task) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator) + return initiator->IsTaskActive(task); + + return false; +} +bool QuestManager::istaskactivityactive(int task, int activity) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator) + return initiator->IsTaskActivityActive(task, activity); + + return false; +} +int QuestManager::gettaskactivitydonecount(int task, int activity) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator) + return initiator->GetTaskActivityDoneCountFromTaskID(task, activity); + + return 0; //improper args + +} +void QuestManager::updatetaskactivity(int task, int activity, int count) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator) + initiator->UpdateTaskActivity(task, activity, count); +} + +void QuestManager::resettaskactivity(int task, int activity) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator) + initiator->ResetTaskActivity(task, activity); +} + +void QuestManager::taskexploredarea(int exploreid) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator) + initiator->UpdateTasksOnExplore(exploreid); +} + +void QuestManager::assigntask(int taskid) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator && owner) + initiator->AssignTask(taskid, owner->GetID()); +} + +void QuestManager::failtask(int taskid) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator) + initiator->FailTask(taskid); +} + +int QuestManager::tasktimeleft(int taskid) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator) + return initiator->TaskTimeLeft(taskid); + + return -1; +} + +int QuestManager::enabledtaskcount(int taskset) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator) + return initiator->EnabledTaskCount(taskset); + + return -1; +} +int QuestManager::firsttaskinset(int taskset) { + + if(RuleB(TaskSystem, EnableTaskSystem) && taskmanager) + return taskmanager->FirstTaskInSet(taskset); + + return -1; +} +int QuestManager::lasttaskinset(int taskset) { + + if(RuleB(TaskSystem, EnableTaskSystem) && taskmanager) + return taskmanager->LastTaskInSet(taskset); + + return -1; +} +int QuestManager::nexttaskinset(int taskset, int taskid) { + + if(RuleB(TaskSystem, EnableTaskSystem) && taskmanager) + return taskmanager->NextTaskInSet(taskset, taskid); + + return -1; +} +int QuestManager::activespeaktask() { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator && owner) + return initiator->ActiveSpeakTask(owner->GetNPCTypeID()); + return 0; +} +int QuestManager::activespeakactivity(int taskid) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator && owner) + return initiator->ActiveSpeakActivity(owner->GetNPCTypeID(), taskid); + + return 0; +} +int QuestManager::istaskcompleted(int taskid) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator) + return initiator->IsTaskCompleted(taskid); + + return -1; +} +int QuestManager::activetasksinset(int taskset) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator) + return initiator->ActiveTasksInSet(taskset); + + return -1; +} +int QuestManager::completedtasksinset(int taskset) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator) + return initiator->CompletedTasksInSet(taskset); + + return -1; +} + +bool QuestManager::istaskappropriate(int task) { + + if(RuleB(TaskSystem, EnableTaskSystem) && initiator && taskmanager) + return taskmanager->AppropriateLevel(task, initiator->GetLevel()); + + return false; +} +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(); + } + } +} +void QuestManager::ze(int type, const char *str) { + entity_list.Message(0, type, str); +} + +void QuestManager::we(int type, const char *str) { + worldserver.SendEmoteMessage(0, 0, type, str); +} + +int QuestManager::getlevel(uint8 type) +{ + if (type == 0) + { + return (initiator->GetLevel()); + } + else if(type == 1) + { + Group *g = entity_list.GetGroupByClient(initiator); + if (g != NULL) + return (g->GetAvgLevel()); + else + return 0; + } + else if(type == 2) + { + Raid *r = entity_list.GetRaidByClient(initiator); + if (r != NULL) + return (r->GetAvgLevel()); + else + return 0; + } + else if(type == 3) + { + Raid *r = entity_list.GetRaidByClient(initiator); + if(r != NULL) + { + return (r->GetAvgLevel()); + } + Group *g = entity_list.GetGroupByClient(initiator); + if(g != NULL) + { + return (g->GetAvgLevel()); + } + else + return (initiator->GetLevel()); + } + else if(type == 4 && initiator->IsClient()) + { + return (initiator->CastToClient()->GetLevel2()); + } + else + return 0; +} + +uint16 QuestManager::CreateGroundObject(uint32 itemid, float x, float y, float z, float heading, uint32 decay_time) +{ + uint16 entid = 0; //safety check + entid = entity_list.CreateGroundObject(itemid, x, y, z, heading, decay_time); + return entid; +} + +uint16 QuestManager::CreateGroundObjectFromModel(const char *model, float x, float y, float z, float heading, uint8 type, uint32 decay_time) +{ + uint16 entid = 0; //safety check + entid = entity_list.CreateGroundObjectFromModel(model, x, y, z, heading, type, decay_time); + return entid; +} + +void QuestManager::ModifyNPCStat(const char *identifier, const char *newValue) +{ + if(owner){ + if(owner->IsNPC()) + { + owner->CastToNPC()->ModifyNPCStat(identifier, newValue); + } + } +} + +int QuestManager::collectitems_processSlot(int16 slot_id, uint32 item_id, + bool remove) +{ + ItemInst *item; + int quantity = 0; + + item = initiator->GetInv().GetItem(slot_id); + + // If we have found matching item, add quantity + if (item && item->GetID() == item_id) + { + // If item is stackable, add its charges (quantity) + if (item->IsStackable()) + { + quantity = item->GetCharges(); + } + else + { + quantity = 1; + } + + // Remove item from inventory + if (remove) + { + initiator->DeleteItemInInventory(slot_id, 0, true); + } + } + + return quantity; +} + +// Returns number of item_id that exist in inventory +// If remove is true, items are removed as they are counted. +int QuestManager::collectitems(uint32 item_id, bool remove) +{ + int quantity = 0; + int slot_id; + + for (slot_id = 22; slot_id <= 29; ++slot_id) + { + quantity += collectitems_processSlot(slot_id, item_id, remove); + } + + for (slot_id = 251; slot_id <= 330; ++slot_id) + { + quantity += collectitems_processSlot(slot_id, item_id, remove); + } + + return quantity; +} + +void QuestManager::UpdateSpawnTimer(uint32 id, uint32 newTime) +{ + bool found = false; + + database.UpdateSpawn2Timeleft(id, 0, (newTime/1000)); + LinkedListIterator iterator(zone->spawn2_list); + iterator.Reset(); + while (iterator.MoreElements()) + { + if(iterator.GetData()->GetID() == id) + { + if(!iterator.GetData()->NPCPointerValid()) + { + iterator.GetData()->SetTimer(newTime); + } + found = true; + break; + } + iterator.Advance(); + } + + if(!found) + { + //Spawn wasn't in this zone... + //Tell the other zones to update their spawn time for this spawn point + ServerPacket *pack = new ServerPacket(ServerOP_UpdateSpawn, sizeof(UpdateSpawnTimer_Struct)); + UpdateSpawnTimer_Struct *ust = (UpdateSpawnTimer_Struct*)pack->pBuffer; + ust->id = id; + ust->duration = newTime; + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +// used to set the number of an item in the selected merchant's temp item list. Defaults to zero if no quantity is specified. +void QuestManager::MerchantSetItem(uint32 NPCid, uint32 itemid, uint32 quantity) { + Mob* merchant = entity_list.GetMobByNpcTypeID(NPCid); + + if (merchant == 0 || !merchant->IsNPC() || (merchant->GetClass() != MERCHANT)) + return; // don't do anything if NPCid isn't a merchant + + const Item_Struct* item = NULL; + item = database.GetItem(itemid); + if (!item) return; // if the item id doesn't correspond to a real item, do nothing + + zone->SaveTempItem(merchant->CastToNPC()->MerchantType, NPCid, itemid, quantity); +} + +uint32 QuestManager::MerchantCountItem(uint32 NPCid, uint32 itemid) { + Mob* merchant = entity_list.GetMobByNpcTypeID(NPCid); + + if (merchant == 0 || !merchant->IsNPC() || (merchant->GetClass() != MERCHANT)) + return 0; // if it isn't a merchant, it doesn't have any items + + const Item_Struct* item = NULL; + item = database.GetItem(itemid); + if (!item) return 0; // likewise, if it isn't a valid item, the merchant doesn't have any + + // look for the item in the merchant's temporary list + std::list MerchList = zone->tmpmerchanttable[NPCid]; + std::list::const_iterator itr; + TempMerchantList ml; + uint32 Quant = 0; + + for(itr = MerchList.begin(); itr != MerchList.end(); itr++){ + ml = *itr; + if (ml.item == itemid) { // if this is the item we're looking for + Quant = ml.charges; + break; + } + } + + return Quant; // return the quantity of itemid (0 if it was never found) +} + +// Item Link for use in Variables - "my $example_link = quest::varlink(item_id);" +const char* QuestManager::varlink(char* perltext, int item_id) { + const ItemInst* inst = database.CreateItem(item_id); + if (!inst) + return "INVALID ITEM ID IN VARLINK"; + char* link = 0; + char* tempstr = 0; + if (initiator->MakeItemLink(link, inst)) { // make a link to the item + MakeAnyLenString(&tempstr, "%c%s%s%c", 0x12, link, inst->GetItem()->Name, 0x12); + strn0cpy(perltext, tempstr,250); // the perl string is only 250 chars, so make sure the link isn't too large + safe_delete_array(tempstr); // MakeAnyLenString() uses new, so clean up after it + } + safe_delete_array(link); // MakeItemLink() uses new also + safe_delete(inst); + return perltext; +} + +uint16 QuestManager::CreateInstance(const char *zone, int16 version, uint32 duration) +{ + if(initiator) + { + uint32 zone_id = database.GetZoneID(zone); + if(zone_id == 0) + return 0; + + uint16 id = 0; + if(!database.GetUnusedInstanceID(id)) + { + initiator->Message(13, "Server was unable to find a free instance id."); + return 0; + } + + if(!database.CreateInstance(id, zone_id, version, duration)) + { + initiator->Message(13, "Server was unable to create a new instance."); + return 0; + } + return id; + } + return 0; +} + +void QuestManager::DestroyInstance(uint16 instance_id) +{ + database.DeleteInstance(instance_id); +} + +uint16 QuestManager::GetInstanceID(const char *zone, int16 version) +{ + if(initiator) + { + return database.GetInstanceID(zone, initiator->CharacterID(), version); + } + return 0; +} + +void QuestManager::AssignToInstance(uint16 instance_id) +{ + if(initiator) + { + database.AddClientToInstance(instance_id, initiator->CharacterID()); + } +} + +void QuestManager::AssignGroupToInstance(uint16 instance_id) +{ + if(initiator) + { + Group *g = initiator->GetGroup(); + if(g) + { + uint32 gid = g->GetID(); + database.AssignGroupToInstance(gid, instance_id); + } + } +} + +void QuestManager::AssignRaidToInstance(uint16 instance_id) +{ + if(initiator) + { + Raid *r = initiator->GetRaid(); + if(r) + { + uint32 rid = r->GetID(); + database.AssignRaidToInstance(rid, instance_id); + } + } +} + +void QuestManager::MovePCInstance(int zone_id, int instance_id, float x, float y, float z, float heading) +{ + if(initiator) + { + initiator->MovePC(zone_id, instance_id, x, y, z, heading); + } +} + +void QuestManager::FlagInstanceByGroupLeader(uint32 zone, int16 version) +{ + if(initiator) + { + Group *g = initiator->GetGroup(); + if(g){ + database.FlagInstanceByGroupLeader(zone, version, initiator->CharacterID(), g->GetID()); + } + } +} + +void QuestManager::FlagInstanceByRaidLeader(uint32 zone, int16 version) +{ + if(initiator) + { + Raid *r = initiator->GetRaid(); + if(r) + { + database.FlagInstanceByRaidLeader(zone, version, initiator->CharacterID(), r->GetID()); + } + } +} + +const char* QuestManager::saylink(char* Phrase, bool silent, char* LinkName) { + + 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); + char *escaped_string = new char[sz * 2]; + 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))) + { + 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 + { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); + } + safe_delete_array(query); + } + } + safe_delete_array(query); + safe_delete_array(escaped_string); + + if(silent) + sayid = sayid + 750000; + else + sayid = sayid + 500000; + + //Create the say link as an item link hash + char linktext[250]; + + 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. + sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"0000000000000000000000000000000000000000000000000",LinkName,0x12); + } + strcpy(Phrase,linktext); + return Phrase; + +} + +const char* QuestManager::getguildnamebyid(int guild_id) { + if (guild_id > 0) + return guild_mgr.GetGuildName(guild_id); + else + return(""); +} + +void QuestManager::SetRunning(bool val) +{ + if(!owner) + return; + owner->SetRunning(val); +} + +bool QuestManager::IsRunning() +{ + if(!owner) + return false; + return owner->IsRunning(); +} + +void QuestManager::FlyMode(uint8 flymode) +{ + if(initiator) + { + if (flymode >= 0 && flymode < 3) { + initiator->SendAppearancePacket(AT_Levitate, flymode); + return; + } + } + if(owner) + { + if (flymode >= 0 && flymode < 3) { + owner->SendAppearancePacket(AT_Levitate, flymode); + return; + } + } +} + +uint8 QuestManager::FactionValue() +{ + FACTION_VALUE oldfac; + uint8 newfac = 0; + if(initiator && owner->IsNPC()) { + oldfac = initiator->GetFactionLevel(initiator->GetID(), owner->GetID(), initiator->GetRace(), initiator->GetClass(), initiator->GetDeity(), owner->GetPrimaryFaction(), owner); + + // now, reorder the faction to have it make sense (higher values are better) + switch (oldfac) { + case FACTION_SCOWLS: + newfac = 1; + break; + case FACTION_THREATENLY: + newfac = 2; + break; + case FACTION_DUBIOUS: + newfac = 3; + break; + case FACTION_APPREHENSIVE: + newfac = 4; + break; + case FACTION_INDIFFERENT: + newfac = 5; + break; + case FACTION_AMIABLE: + newfac = 6; + break; + case FACTION_KINDLY: + newfac = 7; + break; + case FACTION_WARMLY: + newfac = 8; + break; + case FACTION_ALLY: + newfac = 9; + break; + } + } + + return newfac; +} + +void QuestManager::enabletitle(int titleset) { + initiator->EnableTitle(titleset); +} + + + +bool QuestManager::checktitle(int titleset) { + return initiator->CheckTitle(titleset); +} + +void QuestManager::removetitle(int titleset) { + initiator->RemoveTitle(titleset); +} + +void QuestManager::wearchange(uint8 slot, uint16 texture) +{ + if(owner){ + owner->SendTextureWC(slot, texture); + if(owner->IsNPC()) { + owner->CastToNPC()->NPCSlotTexture(slot, texture); + } + } +} + +void QuestManager::voicetell(char *str, int macronum, int racenum, int gendernum) +{ + if(owner && str) + { + Client *c = entity_list.GetClientByName(str); + + if(c) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_VoiceMacroOut, sizeof(VoiceMacroOut_Struct)); + + VoiceMacroOut_Struct* vmo = (VoiceMacroOut_Struct*)outapp->pBuffer; + + strn0cpy(vmo->From, owner->GetCleanName(), sizeof(vmo->From)); + + vmo->Type = 1; + + vmo->Voice = (racenum * 2) + gendernum; + + vmo->MacroNumber = macronum; + + c->QueuePacket(outapp); + + safe_delete(outapp); + } + else + LogFile->write(EQEMuLog::Quest, "QuestManager::voicetell from %s. Client %s not found.", owner->GetName(), str); + } +} + +void QuestManager::LearnRecipe(uint32 recipe_id) { + if(!initiator) + return; + initiator->LearnRecipe(recipe_id); +} + +void QuestManager::SendMail(const char *to, const char *from, const char *subject, const char *message) { + if(to == NULL || from == NULL || subject == NULL || message == NULL) { + return; + } + + uint32 message_len = strlen(message) + 1; + ServerPacket* pack = new ServerPacket(ServerOP_UCSMailMessage, sizeof(ServerMailMessageHeader_Struct) + message_len); + ServerMailMessageHeader_Struct* mail = (ServerMailMessageHeader_Struct*) pack->pBuffer; + + strn0cpy(mail->to, to, 64); + strn0cpy(mail->from, from, 64); + strn0cpy(mail->subject, subject, 128); + strcpy(mail->message, message); + + worldserver.SendPacket(pack); + safe_delete(pack); +} + +uint16 QuestManager::CreateDoor(const char* model, float x, float y, float z, float heading, uint8 opentype, uint16 size) +{ + uint16 entid = 0; //safety check + entid = entity_list.CreateDoor(model, x, y, z, heading, opentype, size); + return entid; +} + +int32 QuestManager::GetZoneID(const char *zone) { + return static_cast(database.GetZoneID(zone)); +} + +const char* QuestManager::GetZoneLongName(const char *zone) { + char *long_name; + database.GetZoneLongName(zone, &long_name); + std::string ln = long_name; + safe_delete_array(long_name); + + return ln.c_str(); +} + +bool QuestManager::TurnInItem(uint32 itm, int charges) +{ + if ( owner && owner->IsNPC() ) + { + if ( owner->CastToNPC()->DoesQuestItemExist(itm, charges, true) ) + return true; + } + + return false; +} + +void QuestManager::CompleteHandIn() +{ + if ( owner && owner->IsNPC() ) + { + owner->CastToNPC()->RemoveQuestDeleteItems(); + } +} + +void QuestManager::ResetHandIn() +{ + if ( owner && owner->IsNPC() ) + { + owner->CastToNPC()->ResetQuestDeleteList(); + } +} + +void QuestManager::ClearHandIn() +{ + if ( owner && owner->IsNPC() ) + { + owner->CastToNPC()->ClearQuestLists(); + } +} + +void QuestManager::CrossZoneSignalPlayerByCharID(int charid, uint32 data){ + ServerPacket* pack = new ServerPacket(ServerOP_CZSignalClient, sizeof(CZClientSignal_Struct)); + CZClientSignal_Struct* CZSC = (CZClientSignal_Struct*) pack->pBuffer; + CZSC->charid = charid; + CZSC->data = data; + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::CrossZoneSignalPlayerByName(const char *CharName, uint32 data){ + uint32 message_len = strlen(CharName) + 1; + ServerPacket* pack = new ServerPacket(ServerOP_CZSignalClientByName, sizeof(CZClientSignalByName_Struct) + message_len); + CZClientSignalByName_Struct* CZSC = (CZClientSignalByName_Struct*) pack->pBuffer; + strn0cpy(CZSC->Name, CharName, 64); + 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; + uint32 message_len2 = strlen(Message) + 1; + ServerPacket* pack = new ServerPacket(ServerOP_CZMessagePlayer, sizeof(CZMessagePlayer_Struct) + message_len + message_len2); + CZMessagePlayer_Struct* CZSC = (CZMessagePlayer_Struct*) pack->pBuffer; + CZSC->Type = Type; + strn0cpy(CZSC->CharName, CharName, 64); + strn0cpy(CZSC->Message, Message, 512); + worldserver.SendPacket(pack); + safe_delete(pack); +} diff --git a/zone/questmgr.h b/zone/questmgr.h new file mode 100644 index 000000000..0c9be74fb --- /dev/null +++ b/zone/questmgr.h @@ -0,0 +1,295 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 __QUEST_MANAGER_H__ +#define __QUEST_MANAGER_H__ + +#include "../common/Mutex.h" +#include "../common/timer.h" +#include "tasks.h" + +#include +#include +using namespace std; + +class NPC; +class Client; + +class QuestManager { +public: + QuestManager(); + virtual ~QuestManager(); + + void StartQuest(Mob *_owner, Client *_initiator = NULL, ItemInst* _questitem = NULL); + void EndQuest(); + + void Process(); + + void ClearTimers(Mob *who); + void ClearAllTimers(); + + //quest perl functions + void echo(int colour, const char *str); + void say(const char *str); + void say(const char *str, uint8 language); + void me(const char *str); + void summonitem(uint32 itemid, int16 charges = 0); + //getZoneID(const char *short_name) + void write(const char *file, const char *str); + uint16 spawn2(int npc_type, int grid, int unused, float x, float y, float z, float heading); + uint16 unique_spawn(int npc_type, int grid, int unused, float x, float y, float z, float heading = 0); + uint16 spawn_from_spawn2(uint32 spawn2_id); + void enable_spawn2(uint32 spawn2_id); + void disable_spawn2(uint32 spawn2_id); + void setstat(int stat, int value); + void incstat(int stat, int value); //old setstat command + void castspell(int spell_id, int target_id); + void selfcast(int spell_id); + void addloot(int item_id, int charges = 0, bool equipitem = true); + void Zone(const char *zone_name); + void settimer(const char *timer_name, int seconds); + void settimerMS(const char *timer_name, int milliseconds); + void stoptimer(const char *timer_name); + void stopalltimers(); + void emote(const char *str); + void shout(const char *str); + void shout2(const char *str); + void gmsay(const char *str, uint32 color, bool send_to_world, uint32 to_guilddbid); + void depop(int npc_type = 0); // depop NPC and don't start spawn timer + void depop_withtimer(int npc_type = 0); // depop NPC and start spawn timer + void depopall(int npc_type = 0); + void depopzone(bool StartSpawnTimer = true); + void repopzone(); + void settarget(const char *type, int target_id); + void follow(int entity_id, int distance); + void sfollow(); +// void cumflag(); +// void flagnpc(uint32 flag_num, uint8 flag_value); +// void flagcheck(uint32 flag_to_check, uint32 flag_to_set); +// bool isflagset(int flag_num); + void changedeity(int diety_id); + void exp(int amt); + void level(int newlevel); + void traindisc(int discipline_tome_item_id); + bool isdisctome(int item_id); + void safemove(); + void rain(int weather); + void snow(int weather); + void surname(const char *name); + void permaclass(int class_id); + void permarace(int race_id); + void permagender(int gender_id); + uint16 scribespells(uint8 max_level, uint8 min_level = 1); + uint16 traindiscs(uint8 max_level, uint8 min_level = 1); + void unscribespells(); + void untraindiscs(); + void givecash(int copper, int silver, int gold, int platinum); + void pvp(const char *mode); + void movepc(int zone_id, float x, float y, float z, float heading); + void gmmove(float x, float y, float z); + void movegrp(int zoneid, float x, float y, float z); + void doanim(int anim_id); + void addskill(int skill_id, int value); + void setlanguage(int skill_id, int value); + void setskill(int skill_id, int value); + void setallskill(int value); + void attack(const char *client_name); + void attacknpc(int npc_entity_id); + void attacknpctype(int npc_type_id); + void save(); + void faction(int faction_id, int faction_value, int temp); + void setsky(uint8 new_sky); + void setguild(uint32 new_guild_id, uint8 new_rank); + void CreateGuild(const char *guild_name, const char *leader); + void settime(uint8 new_hour, uint8 new_min); + void itemlink(int item_id); + void signal(int npc_id, int wait_ms = 0); + void signalwith(int npc_id, int signal_id, int wait_ms = 0); + void setglobal(const char *varname, const char *newvalue, int options, const char *duration); + void targlobal(const char *varname, const char *value, const char *duration, int npcid, int charid, int zoneid); + void delglobal(const char *varname); + void ding(); + void rebind(int zoneid, float x, float y, float z); + void start(int wp); + void stop(); + void pause(int duration); + void moveto(float x, float y, float z, float h, bool saveguardspot); + void resume(); + void addldonpoints(int32 points, uint32 theme); + void addldonwin(int32 wins, uint32 theme); + void addldonloss(int32 losses, uint32 theme); + void setnexthpevent(int at); + void setnextinchpevent(int at); + void respawn(int npc_type, int grid); + void set_proximity(float minx, float maxx, float miny, float maxy, float minz=-999999, float maxz=999999); + void clear_proximity(); + void setanim(int npc_type, int animnum); + void showgrid(int gridid); + void showpath(float x, float y, float z); + void pathto(float x, float y, float z); + void spawn_condition(const char *zone_short, uint32 instance_id, uint16 condition_id, short new_value); + short get_spawn_condition(const char *zone_short, uint32 instance_id, uint16 condition_id); + void toggle_spawn_event(int event_id, bool enable, bool reset_base); + bool has_zone_flag(int zone_id); + void set_zone_flag(int zone_id); + void clear_zone_flag(int zone_id); + void sethp(int hpperc); + bool summonburriedplayercorpse(uint32 char_id, float dest_x, float dest_y, float dest_z, float dest_heading); + bool summonallplayercorpses(uint32 char_id, float dest_x, float dest_y, float dest_z, float dest_heading); + uint32 getplayerburriedcorpsecount(uint32 char_id); + bool buryplayercorpse(uint32 char_id); + void forcedooropen(uint32 doorid, bool altmode); + void forcedoorclose(uint32 doorid, bool altmode); + void toggledoorstate(uint32 doorid); + bool isdooropen(uint32 doorid); + void npcrace(int race_id); + void npcgender(int gender_id); void npcsize(int newsize); + void npctexture(int newtexture); + void playerrace(int race_id); + void playergender(int gender_id); + void playersize(int newsize); + void playertexture(int newtexture); + void playerfeature(char *feature, int setting); + void npcfeature(char *feature, int setting); + void popup(char *title, char *text, uint32 popupid, uint32 buttons, uint32 Duration); + void taskselector(int taskcount, int *tasks); + void tasksetselector(int tasksettid); + void enabletask(int taskcount, int *tasks); + void disabletask(int taskcount, int *tasks); + bool istaskenabled(int taskid); + bool istaskactive(int task); + bool istaskactivityactive(int task, int activity); + int gettaskactivitydonecount(int task, int activity); + void updatetaskactivity(int task, int activity, int count); + void resettaskactivity(int task, int activity); + void taskexploredarea(int exploreid); + void assigntask(int taskid); + void failtask(int taskid); + int tasktimeleft(int taskid); + int istaskcompleted(int taskid); + int enabledtaskcount(int taskset); + int firsttaskinset(int taskset); + int lasttaskinset(int taskset); + int nexttaskinset(int taskset, int taskid); + int activespeaktask(); + int activespeakactivity(int taskid); + int activetasksinset(int taskset); + int completedtasksinset(int taskset); + bool istaskappropriate(int task); + void clearspawntimers(); + void ze(int type, const char *str); + void we(int type, const char *str); + int getlevel(uint8 type); + int collectitems(uint32 item_id, bool remove); + int collectitems_processSlot(int16 slot_id, uint32 item_id, bool remove); + void enabletitle(int titleset); + bool checktitle(int titlecheck); + void removetitle(int titlecheck); + + uint16 CreateGroundObject(uint32 itemid, float x, float y, float z, float heading, uint32 decay_time = 300000); + uint16 CreateGroundObjectFromModel(const char* model, float x, float y, float z, float heading, uint8 type = 0x00, uint32 decay_time = 0); + void ModifyNPCStat(const char *identifier, const char *newValue); + void UpdateSpawnTimer(uint32 id, uint32 newTime); + + void MerchantSetItem(uint32 NPCid, uint32 itemid, uint32 quantity = 0); + uint32 MerchantCountItem(uint32 NPCid, uint32 itemid); + + uint16 CreateInstance(const char *zone, int16 version, uint32 duration); + void DestroyInstance(uint16 instance_id); + uint16 GetInstanceID(const char *zone, int16 version); + void AssignToInstance(uint16 instance_id); + void AssignGroupToInstance(uint16 instance_id); + void AssignRaidToInstance(uint16 instance_id); + void MovePCInstance(int zone_id, int instance_id, float x, float y, float z, float heading); + void FlagInstanceByGroupLeader(uint32 zone, int16 version); + void FlagInstanceByRaidLeader(uint32 zone, int16 version); + + const char* varlink(char* perltext, int item_id); + const char* saylink(char* Phrase, bool silent, char* LinkName); + const char* getguildnamebyid(int guild_id); + void SetRunning(bool val); + bool IsRunning(); + void FlyMode(uint8 flymode); + uint8 FactionValue(); + void wearchange(uint8 slot, uint16 texture); + void voicetell(char *str, int macronum, int racenum, int gendernum); + void LearnRecipe(uint32 recipe_id); + void SendMail(const char *to, const char *from, const char *subject, const char *message); + 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); + + //not in here because it retains perl types + //thing ChooseRandom(array_of_things) + + inline Client *GetInitiator() const { return(initiator); } + inline NPC *GetNPC() const { return(owner->IsNPC()?owner->CastToNPC():NULL); } + inline Mob *GetOwner() const { return(owner); } + inline ItemInst *GetQuestItem() const {return questitem; } + inline bool ProximitySayInUse() { return HaveProximitySays; } + + bool TurnInItem(uint32 itm, int charges); + void CompleteHandIn(); + void ResetHandIn(); + void ClearHandIn(); + void CrossZoneSignalPlayerByCharID(int charid, uint32 data); + void CrossZoneSignalPlayerByName(const char *CharName, uint32 data); + void CrossZoneMessagePlayerByName(uint32 Type, const char *CharName, const char *Message); + +#ifdef BOTS + int createbotcount(); + int spawnbotcount(); + bool botquest(); + bool createBot(const char *name, const char *lastname, uint8 level, uint16 race, uint8 botclass, uint8 gender); +#endif + +protected: + Mob *owner; //NPC is never NULL when functions are called. + Client *initiator; //this can be null. + ItemInst* questitem; // this is usually NULL. + + bool depop_npc; //true if EndQuest should depop the NPC + + Mutex quest_mutex; + bool HaveProximitySays; + + int QGVarDuration(const char *fmt); + int InsertQuestGlobal(int charid, int npcid, int zoneid, const char *name, const char *value, int expdate); + + class QuestTimer { + public: + inline QuestTimer(int duration, Mob *_mob, string _name) : mob(_mob), name(_name), Timer_(duration) { Timer_.Start(duration, false); } + Mob* mob; + string name; + Timer Timer_; + }; + class SignalTimer { + public: + inline SignalTimer(int duration, int _npc_id, int _signal_id) : npc_id(_npc_id), signal_id(_signal_id), Timer_(duration) { Timer_.Start(duration, false); } + int npc_id; + int signal_id; + Timer Timer_; + }; + list QTimerList; + list STimerList; + +}; + +extern QuestManager quest_manager; + +#endif + diff --git a/zone/queues.h b/zone/queues.h new file mode 100644 index 000000000..66125e650 --- /dev/null +++ b/zone/queues.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA */ + +/* + Code for generell handling of priority Queues. + Implemention of queues from "Algoritms in C" by Robert Sedgewick. + Copyright Monty Program KB. + By monty. +*/ + +#ifndef _queues_h +#define _queues_h +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct st_queue { + byte **root; + void *first_cmp_arg; + uint elements; + uint max_elements; + uint offset_to_key; /* compare is done on element+offset */ + int max_at_top; /* Set if queue_top gives max */ + int (*compare)(void *, byte *,byte *); +} QUEUE; + +#define queue_top(queue) ((queue)->root[1]) +#define queue_element(queue,index) ((queue)->root[index+1]) +#define queue_end(queue) ((queue)->root[(queue)->elements]) +#define queue_replaced(queue) _downheap(queue,1) + +int init_queue(QUEUE *queue,uint max_elements,uint offset_to_key, + pbool max_at_top, int (*compare)(void *,byte *, byte *), + void *first_cmp_arg); +int reinit_queue(QUEUE *queue,uint max_elements,uint offset_to_key, + pbool max_at_top, int (*compare)(void *,byte *, byte *), + void *first_cmp_arg); +void delete_queue(QUEUE *queue); +void queue_insert(QUEUE *queue,byte *element); +byte *queue_remove(QUEUE *queue,uint idx); +void _downheap(QUEUE *queue,uint idx); +#define is_queue_inited(queue) ((queue)->root != 0) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/zone/raid.h b/zone/raid.h new file mode 100644 index 000000000..8cbd0f1a4 --- /dev/null +++ b/zone/raid.h @@ -0,0 +1,161 @@ +/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA */ + +/* Parser needs these defines always, even if USE_RAID is not defined */ +#define RAID_TYPE_0 1 /* Striping */ +#define RAID_TYPE_x 2 /* Some new modes */ +#define RAID_TYPE_y 3 + +#define RAID_DEFAULT_CHUNKS 4 +#define RAID_DEFAULT_CHUNKSIZE 256*1024 /* 256kB */ + +extern const char *raid_type_string[]; + +#ifdef __cplusplus +extern "C" { +#endif +const char *my_raid_type(int raid_type); +#ifdef __cplusplus +} +#endif + +#if defined(USE_RAID) && !defined(DONT_USE_RAID) + +#ifdef __GNUC__ +#pragma interface /* gcc class implementation */ +#endif +#include "my_dir.h" + +/* Trap all occurences of my_...() in source and use our wrapper around this function */ + +#ifdef MAP_TO_USE_RAID +#define my_read(A,B,C,D) my_raid_read(A,B,C,D) +#define my_write(A,B,C,D) my_raid_write(A,B,C,D) +#define my_pwrite(A,B,C,D,E) my_raid_pwrite(A,B,C,D,E) +#define my_pread(A,B,C,D,E) my_raid_pread(A,B,C,D,E) +#define my_chsize(A,B,C) my_raid_chsize(A,B,C) +#define my_close(A,B) my_raid_close(A,B) +#define my_tell(A,B) my_raid_tell(A,B) +#define my_seek(A,B,C,D) my_raid_seek(A,B,C,D) +#define my_lock(A,B,C,D,E) my_raid_lock(A,B,C,D,E) +#define my_fstat(A,B,C) my_raid_fstat(A,B,C) +#endif /* MAP_TO_USE_RAID */ + +#ifdef __cplusplus +extern "C" { +#endif + + void init_raid(void); + void end_raid(void); + + bool is_raid(File fd); + File my_raid_create(const char *FileName, int CreateFlags, int access_flags, + uint raid_type, uint raid_chunks, ulong raid_chunksize, + myf MyFlags); + File my_raid_open(const char *FileName, int Flags, + uint raid_type, uint raid_chunks, ulong raid_chunksize, + myf MyFlags); + int my_raid_rename(const char *from, const char *to, uint raid_chunks, + myf MyFlags); + int my_raid_delete(const char *from, uint raid_chunks, myf MyFlags); + int my_raid_redel(const char *old_name, const char *new_name, + uint raid_chunks, myf MyFlags); + + my_off_t my_raid_seek(File fd, my_off_t pos, int whence, myf MyFlags); + my_off_t my_raid_tell(File fd, myf MyFlags); + + uint my_raid_write(File,const byte *Buffer, uint Count, myf MyFlags); + uint my_raid_read(File Filedes, byte *Buffer, uint Count, myf MyFlags); + + uint my_raid_pread(File Filedes, byte *Buffer, uint Count, my_off_t offset, + myf MyFlags); + uint my_raid_pwrite(int Filedes, const byte *Buffer, uint Count, + my_off_t offset, myf MyFlags); + + int my_raid_lock(File,int locktype, my_off_t start, my_off_t length, + myf MyFlags); + int my_raid_chsize(File fd, my_off_t newlength, myf MyFlags); + int my_raid_close(File, myf MyFlags); + int my_raid_fstat(int Filedes, struct stat *buf, myf MyFlags); + +#ifdef __cplusplus +} + +class RaidName { + public: + RaidName(const char *FileName); + ~RaidName(); + bool IsRaid(); + int Rename(const char * from, const char * to, myf MyFlags); + private: + uint _raid_type; /* RAID_TYPE_0 or RAID_TYPE_1 or RAID_TYPE_5 */ + uint _raid_chunks; /* 1..n */ + ulong _raid_chunksize; /* 1..n in bytes */ +}; + +class RaidFd { + public: + RaidFd(uint raid_type, uint raid_chunks , ulong raid_chunksize); + ~RaidFd(); + File Create(const char *FileName, int CreateFlags, int access_flags, + myf MyFlags); + File Open(const char *FileName, int Flags, myf MyFlags); + my_off_t Seek(my_off_t pos,int whence,myf MyFlags); + my_off_t Tell(myf MyFlags); + int Write(const byte *Buffer, uint Count, myf MyFlags); + int Read(const byte *Buffer, uint Count, myf MyFlags); + int Lock(int locktype, my_off_t start, my_off_t length, myf MyFlags); + int Chsize(File fd, my_off_t newlength, myf MyFlags); + int Fstat(int fd, MY_STAT *stat_area, myf MyFlags ); + int Close(myf MyFlags); + static bool IsRaid(File fd); + static DYNAMIC_ARRAY _raid_map; /* Map of RaidFD* */ + private: + + uint _raid_type; /* RAID_TYPE_0 or RAID_TYPE_1 or RAID_TYPE_5 */ + uint _raid_chunks; /* 1..n */ + ulong _raid_chunksize; /* 1..n in bytes */ + + ulong _total_block; /* We are operating with block no x (can be 0..many). */ + uint _this_block; /* can be 0.._raid_chunks */ + uint _remaining_bytes; /* Maximum bytes that can be written in this block */ + + my_off_t _position; + my_off_t _size; /* Cached file size for faster seek(SEEK_END) */ + File _fd; + File *_fd_vector; /* Array of File */ + off_t *_seek_vector; /* Array of cached seek positions */ + + inline void Calculate() + { + DBUG_ENTER("RaidFd::_Calculate"); + DBUG_PRINT("info",("_position: %lu _raid_chunksize: %d, _size: %lu", + (ulong) _position, _raid_chunksize, (ulong) _size)); + + _total_block = (ulong) (_position / _raid_chunksize); + _this_block = _total_block % _raid_chunks; /* can be 0.._raid_chunks */ + _remaining_bytes = (uint) (_raid_chunksize - + (_position - _total_block * _raid_chunksize)); + DBUG_PRINT("info", + ("_total_block: %d this_block: %d _remaining_bytes:%d", + _total_block, _this_block, _remaining_bytes)); + DBUG_VOID_RETURN; + } +}; + +#endif /* __cplusplus */ +#endif /* USE_RAID */ diff --git a/zone/raids.cpp b/zone/raids.cpp new file mode 100644 index 000000000..5f87d11c3 --- /dev/null +++ b/zone/raids.cpp @@ -0,0 +1,1465 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 "masterentity.h" +#include "NpcAI.h" +#include "../common/packet_functions.h" +#include "../common/packet_dump.h" +#include "../common/MiscFunctions.h" +#include "worldserver.h" +extern EntityList entity_list; +extern WorldServer worldserver; + +Raid::Raid(uint32 raidID) +: GroupIDConsumer(raidID) +{ + memset(members ,0, (sizeof(RaidMember)*MAX_RAID_MEMBERS)); + leader = NULL; + memset(leadername, 0, 64); + locked = false; + LootType = 4; +} + +Raid::Raid(Client* nLeader) +: GroupIDConsumer() +{ + memset(members ,0, (sizeof(RaidMember)*MAX_RAID_MEMBERS)); + leader = nLeader; + memset(leadername, 0, 64); + strn0cpy(leadername, nLeader->GetName(), 64); + locked = false; + LootType = 4; +} + +Raid::~Raid() +{ +} + +bool Raid::Process() +{ + if(forceDisband) + return false; + if(disbandCheck) + { + int count = 0; + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strlen(members[x].membername) == 0) + continue; + else + count++; + } + if(count == 0) + return false; + } + return true; +} + +void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bool looter){ + 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); + } + + safe_delete_array(query); + LearnMembers(); + VerifyRaid(); + if(group < 12) + GroupUpdate(group); + SendRaidAddAll(c->GetName()); + + c->SetRaidGrouped(true); + + ServerPacket *pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = GetID(); + strn0cpy(rga->playername, c->GetName(), 64); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void Raid::RemoveMember(const char *c) +{ + 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); + } + + Client *m = entity_list.GetClientByName(c); + safe_delete_array(query); + disbandCheck = true; + SendRaidRemoveAll(c); + SendRaidDisband(m); + LearnMembers(); + VerifyRaid(); + + if(m){ + m->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); + rga->zoneid = zone->GetZoneID(); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +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); + } + + safe_delete_array(query); + LearnMembers(); + VerifyRaid(); + SendRaidDisbandAll(); + + ServerPacket *pack = new ServerPacket(ServerOP_RaidDisband, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = GetID(); + strn0cpy(rga->playername, " ", 64); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); + + forceDisband = true; +} + +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); + } + + safe_delete_array(query); + LearnMembers(); + VerifyRaid(); + SendRaidMoveAll(name); + + ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = GetID(); + strn0cpy(rga->playername, name, 64); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +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); + } + + 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(); + strn0cpy(rga->playername, who, 64); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +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); + + 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); + + strn0cpy(leadername, name, 64); + + Client *c = entity_list.GetClientByName(name); + if(c) + SetLeader(c); + + LearnMembers(); + VerifyRaid(); + SendMakeLeaderPacket(name); + + ServerPacket *pack = new ServerPacket(ServerOP_RaidLeader, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = GetID(); + strn0cpy(rga->playername, name, 64); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +bool Raid::IsGroupLeader(const char *who) +{ + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strcmp(who, members[x].membername) == 0){ + return members[x].IsGroupLeader; + } + } + + return false; +} + +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); + } + + safe_delete_array(query); + LearnMembers(); + VerifyRaid(); +} + +uint32 Raid::GetFreeGroup() +{ + //check each group return the first one with 0 members assigned... + for(int x = 0; x < MAX_RAID_GROUPS; x++) + { + int count = 0; + for(int y = 0; y < MAX_RAID_MEMBERS; y++) + { + if(members[y].GroupNumber == x && (strlen(members[y].membername)>0)) + count++; + } + if(count == 0) + return x; + } + //if we get to here then there were no free groups so we added the group as free floating members. + return 0xFFFFFFFF; +} + +uint8 Raid::GroupCount(uint32 gid) +{ + uint8 count = 0; + if(gid < 12) + { + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(members[x].GroupNumber == gid && strlen(members[x].membername)>0) + { + count++; + } + } + } + else + { + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(members[x].GroupNumber > 11 && strlen(members[x].membername)>0) + { + count++; + } + } + } + return count; +} + +uint8 Raid::RaidCount() +{ + int count = 0; + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strlen(members[x].membername) > 0) + count++; + } + return count; +} + +uint32 Raid::GetGroup(const char *name) +{ + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strcmp(members[x].membername, name) == 0) + return members[x].GroupNumber; + } + return 0xFFFFFFFF; +} + +uint32 Raid::GetGroup(Client *c) +{ + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(members[x].member == c) + return members[x].GroupNumber; + } + return 0xFFFFFFFF; +} + +void Raid::RaidSay(const char *msg, Client *c) +{ + if(!c) + return; + + ServerPacket *pack = new ServerPacket(ServerOP_RaidSay, sizeof(ServerRaidMessage_Struct) + strlen(msg) + 1); + ServerRaidMessage_Struct *rga = (ServerRaidMessage_Struct*)pack->pBuffer; + rga->rid = GetID(); + rga->gid = 0xFFFFFFFF; + strn0cpy(rga->from, c->GetName(), 64); + + strcpy(rga->message, msg); // this is safe because we are allocating enough space for the entire msg above + + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void Raid::RaidGroupSay(const char *msg, Client *c) +{ + if(!c) + return; + + uint32 groupToUse = GetGroup(c->GetName()); + + if(groupToUse > 11) + return; + + ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupSay, sizeof(ServerRaidMessage_Struct) + strlen(msg) + 1); + ServerRaidMessage_Struct *rga = (ServerRaidMessage_Struct*)pack->pBuffer; + rga->rid = GetID(); + rga->gid = groupToUse; + strn0cpy(rga->from, c->GetName(), 64); + + strcpy(rga->message, msg); // this is safe because we are allocating enough space for the entire msg above + + worldserver.SendPacket(pack); + safe_delete(pack); +} + +uint32 Raid::GetPlayerIndex(const char *name){ + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strcmp(name, members[x].membername) == 0){ + return x; + } + } + return 0; //should never get to here if we do everything else right, set it to 0 so we never crash things that rely on it. +} + +Client *Raid::GetClientByIndex(uint16 index) +{ + if(index > MAX_RAID_MEMBERS) + return NULL; + + return members[index].member; +} + +void Raid::CastGroupSpell(Mob* caster, uint16 spellid, uint32 gid) +{ + float range, distance; + + if(!caster) + return; + + range = caster->GetAOERange(spellid); + + float range2 = range*range; + + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(members[x].member == caster) { + caster->SpellOnTarget(spellid, caster); +#ifdef GROUP_BUFF_PETS + if(caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) + caster->SpellOnTarget(spellid, caster->GetPet()); +#endif + } + else if(members[x].member != NULL) + { + if(members[x].GroupNumber == gid){ + distance = caster->DistNoRoot(*members[x].member); + 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()) + caster->SpellOnTarget(spellid, members[x].member->GetPet()); +#endif + } + else{ + _log(SPELLS__CASTING, "Raid spell: %s is out of range %f at distance %f from %s", members[x].member->GetName(), range, distance, caster->GetName()); + } + } + } + } +} + + +uint32 Raid::GetTotalRaidDamage(Mob* other) +{ + uint32 total = 0; + + for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++) { + if(!members[i].member) + continue; + if (other->CheckAggro(members[i].member)) + total += other->GetHateAmount(members[i].member,true); + } + return total; +} + +void Raid::HealGroup(uint32 heal_amt, Mob* caster, uint32 gid) +{ + if (!caster) + return; + + int numMem = 0; + unsigned int gi = 0; + for(; gi < MAX_RAID_MEMBERS; gi++) + { + if(members[gi].member){ + if(members[gi].GroupNumber == gid) + { + numMem += 1; + } + } + } + + heal_amt /= numMem; + for(gi = 0; gi < MAX_RAID_MEMBERS; gi++) + { + if(members[gi].member){ + if(members[gi].GroupNumber == gid) + { + members[gi].member->SetHP(members[gi].member->GetHP() + heal_amt); + members[gi].member->SendHPUpdate(); + } + } + } +} + + +void Raid::BalanceHP(int32 penalty, uint32 gid) +{ + int dmgtaken = 0, numMem = 0; + int gi = 0; + for(; gi < MAX_RAID_MEMBERS; gi++) + { + if(members[gi].member){ + if(members[gi].GroupNumber == gid) + { + dmgtaken += (members[gi].member->GetMaxHP() - members[gi].member->GetHP()); + numMem += 1; + } + } + } + + dmgtaken += dmgtaken * penalty / 100; + dmgtaken /= numMem; + for(gi = 0; gi < MAX_RAID_MEMBERS; gi++) + { + if(members[gi].member){ + if(members[gi].GroupNumber == gid) + { + if((members[gi].member->GetMaxHP() - dmgtaken) < 1){ //this way the ability will never kill someone + members[gi].member->SetHP(1); //but it will come darn close + members[gi].member->SendHPUpdate(); + } + else{ + members[gi].member->SetHP(members[gi].member->GetMaxHP() - dmgtaken); + members[gi].member->SendHPUpdate(); + } + } + } + } +} + +void Raid::BalanceMana(int32 penalty, uint32 gid) +{ + int manataken = 0, numMem = 0; + int gi = 0; + for(; gi < MAX_RAID_MEMBERS; gi++) + { + if(members[gi].member){ + if(members[gi].GroupNumber == gid) + { + manataken += (members[gi].member->GetMaxMana() - members[gi].member->GetMana()); + numMem += 1; + } + } + } + + manataken += manataken * penalty / 100; + manataken /= numMem; + for(gi = 0; gi < MAX_RAID_MEMBERS; gi++) + { + if(members[gi].member){ + if(members[gi].GroupNumber == gid) + { + if((members[gi].member->GetMaxMana() - manataken) < 1){ + members[gi].member->SetMana(1); + if (members[gi].member->IsClient()) + members[gi].member->CastToClient()->SendManaUpdate(); + } + else{ + members[gi].member->SetMana(members[gi].member->GetMaxMana() - manataken); + if (members[gi].member->IsClient()) + members[gi].member->CastToClient()->SendManaUpdate(); + } + } + } + } +} + +//basically the same as Group's version just with more people like a lot of non group specific raid stuff +void Raid::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter){ + //avoid unneeded work + if(copper == 0 && silver == 0 && gold == 0 && platinum == 0) + return; + + uint32 i; + uint8 membercount = 0; + for (i = 0; i < MAX_RAID_MEMBERS; i++) { + if (members[i].member != NULL) { + + membercount++; + } + } + + if (membercount == 0) + return; + + uint32 mod; + //try to handle round off error a little better + if(membercount > 1) { + mod = platinum % membercount; + if((mod) > 0) { + platinum -= mod; + gold += 10 * mod; + } + mod = gold % membercount; + if((mod) > 0) { + gold -= mod; + silver += 10 * mod; + } + mod = silver % membercount; + if((mod) > 0) { + silver -= mod; + copper += 10 * mod; + } + } + + //calculate the splits + //We can still round off copper pieces, but I dont care + uint32 sc; + uint32 cpsplit = copper / membercount; + sc = copper % membercount; + uint32 spsplit = silver / membercount; + uint32 gpsplit = gold / membercount; + uint32 ppsplit = platinum / membercount; + + char buf[128]; + buf[63] = '\0'; + string msg = "You receive"; + bool one = false; + + if(ppsplit > 0) { + snprintf(buf, 63, " %u platinum", ppsplit); + msg += buf; + one = true; + } + if(gpsplit > 0) { + if(one) + msg += ","; + snprintf(buf, 63, " %u gold", gpsplit); + msg += buf; + one = true; + } + if(spsplit > 0) { + if(one) + msg += ","; + snprintf(buf, 63, " %u silver", spsplit); + msg += buf; + one = true; + } + if(cpsplit > 0) { + if(one) + msg += ","; + //this message is not 100% accurate for the splitter + //if they are receiving any roundoff + snprintf(buf, 63, " %u copper", cpsplit); + msg += buf; + one = true; + } + msg += " as your split"; + + for (i = 0; i < MAX_RAID_MEMBERS; i++) { + if (members[i].member != NULL) { // If Group Member is Client + //I could not get MoneyOnCorpse to work, so we use this + members[i].member->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true); + + members[i].member->Message(2, msg.c_str()); + } + } +} + +void Raid::GroupBardPulse(Mob* caster, uint16 spellid, uint32 gid){ + uint32 z; + float range, distance; + + if(!caster) + return; + + range = caster->GetAOERange(spellid); + + float range2 = range*range; + + for(z=0; z < MAX_RAID_MEMBERS; z++) { + if(members[z].member == caster) { + caster->BardPulse(spellid, caster); +#ifdef GROUP_BUFF_PETS + if(caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) + caster->BardPulse(spellid, caster->GetPet()); +#endif + } + else if(members[z].member != NULL) + { + if(members[z].GroupNumber == gid){ + distance = caster->DistNoRoot(*members[z].member); + if(distance <= range2) { + members[z].member->BardPulse(spellid, caster); +#ifdef GROUP_BUFF_PETS + if(members[z].member->GetPet() && members[z].member->HasPetAffinity() && !members[z].member->GetPet()->IsCharmed()) + members[z].member->GetPet()->BardPulse(spellid, caster); +#endif + } else + _log(SPELLS__BARDS, "Group bard pulse: %s is out of range %f at distance %f from %s", members[z].member->GetName(), range, distance, caster->GetName()); + } + } + } +} + +void Raid::TeleportGroup(Mob* sender, uint32 zoneID, uint16 instance_id, float x, float y, float z, float heading, uint32 gid) +{ + for(int i = 0; i < MAX_RAID_MEMBERS; i++) + { + if(members[i].member) + { + if(members[i].GroupNumber == gid) + { + members[i].member->MovePC(zoneID, instance_id, x, y, z, heading, 0, ZoneSolicited); + } + } + + } +} + +void Raid::TeleportRaid(Mob* sender, uint32 zoneID, uint16 instance_id, float x, float y, float z, float heading) +{ + for(int i = 0; i < MAX_RAID_MEMBERS; i++) + { + if(members[i].member) + { + members[i].member->MovePC(zoneID, instance_id, x, y, z, heading, 0, ZoneSolicited); + } + } +} + +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); + 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); + + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strcmp(looter, members[x].membername) == 0) + { + members[x].IsLooter = 1; + break; + } + } + ServerPacket *pack = new ServerPacket(ServerOP_DetailsChange, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = GetID(); + rga->zoneid = zone->GetZoneID(); + 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); + + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + 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(); + rga->zoneid = zone->GetZoneID(); + 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){ + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strcmp(name, members[x].membername) == 0) + return true; + } + return false; +} + +uint32 Raid::GetHighestLevel() +{ + uint32 highlvl = 0; + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strlen(members[x].membername)) + { + if(members[x].level > highlvl) + highlvl = members[x].level; + } + } + return highlvl; +} + +uint32 Raid::GetLowestLevel() +{ + uint32 lowlvl = 1000; + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strlen(members[x].membername)) + { + if(members[x].level < lowlvl) + lowlvl = members[x].level; + } + } + return lowlvl; +} + +/* + * Packet Functions Start + */ +void Raid::SendRaidCreate(Client *to){ + if(!to) + return; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidJoin,sizeof(RaidCreate_Struct)); + RaidCreate_Struct *rc = (RaidCreate_Struct*)outapp->pBuffer; + rc->action = raidCreate; + strn0cpy(rc->leader_name, leadername, 64); + rc->leader_id = (GetLeader()?GetLeader()->GetID():0); + to->QueuePacket(outapp); + safe_delete(outapp); +} + +void Raid::SendRaidAdd(const char *who, Client *to) +{ + if(!to) + return; + + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strcmp(members[x].membername, who) == 0) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidAddMember_Struct)); + RaidAddMember_Struct *ram = (RaidAddMember_Struct*)outapp->pBuffer; + ram->raidGen.action = raidAdd; + ram->raidGen.parameter = members[x].GroupNumber; + strn0cpy(ram->raidGen.leader_name, members[x].membername, 64); + strn0cpy(ram->raidGen.player_name, members[x].membername, 64); + ram->_class = members[x]._class; + ram->level = members[x].level; + ram->isGroupLeader = members[x].IsGroupLeader; + to->QueuePacket(outapp); + safe_delete(outapp); + return; + } + } +} + +void Raid::SendRaidAddAll(const char *who) +{ + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strcmp(members[x].membername, who) == 0) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidAddMember_Struct)); + RaidAddMember_Struct *ram = (RaidAddMember_Struct*)outapp->pBuffer; + ram->raidGen.action = raidAdd; + ram->raidGen.parameter = members[x].GroupNumber; + strcpy(ram->raidGen.leader_name, members[x].membername); + strcpy(ram->raidGen.player_name, members[x].membername); + ram->_class = members[x]._class; + ram->level = members[x].level; + ram->isGroupLeader = members[x].IsGroupLeader; + this->QueuePacket(outapp); + safe_delete(outapp); + return; + } + } +} + +void Raid::SendRaidRemove(const char *who, Client *to) +{ + if(!to) + return; + + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strcmp(members[x].membername, who) == 0) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + rg->action = raidRemove2; + strn0cpy(rg->leader_name, who, 64); + strn0cpy(rg->player_name, who, 64); + rg->parameter = 0; + to->QueuePacket(outapp); + safe_delete(outapp); + return; + } + } +} + +void Raid::SendRaidRemoveAll(const char *who) +{ + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strcmp(members[x].membername, who) == 0) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + rg->action = raidRemove2; + strn0cpy(rg->leader_name, who, 64); + strn0cpy(rg->player_name, who, 64); + rg->parameter = 0; + QueuePacket(outapp); + safe_delete(outapp); + return; + } + } +} + +void Raid::SendRaidDisband(Client *to) +{ + if(!to) + return; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + rg->action = raidDisband; + strn0cpy(rg->leader_name, to->GetName(), 64); + strn0cpy(rg->player_name, to->GetName(), 64); + rg->parameter = 0; + to->QueuePacket(outapp); + safe_delete(outapp); +} + +void Raid::SendRaidDisbandAll() +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + rg->action = raidDisband; + strn0cpy(rg->leader_name, "RaidMember", 64); + strn0cpy(rg->player_name, "RaidMember", 64); + rg->parameter = 0; + QueuePacket(outapp); + safe_delete(outapp); +} + +void Raid::SendRaidMove(const char* who, Client *to) +{ + if(!to) + return; + + Client *c = entity_list.GetClientByName(who); + if(c && c == to){ + SendRaidCreate(c); + SendMakeLeaderPacketTo(leadername, c); + } + SendRaidRemove(who, to); + SendRaidAdd(who, to); + if(c && c == to){ + SendBulkRaid(c); + if(IsLocked()) { + SendRaidLockTo(c); + } + } +} + +void Raid::SendRaidMoveAll(const char* who) +{ + Client *c = entity_list.GetClientByName(who); + SendRaidRemoveAll(who); + if(c) + SendRaidCreate(c); + SendMakeLeaderPacket(leadername); + SendRaidAddAll(who); + if(c){ + SendBulkRaid(c); + if(IsLocked()) { SendRaidLockTo(c); } + } +} + +void Raid::SendBulkRaid(Client *to) +{ + if(!to) + return; + + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strlen(members[x].membername) > 0 && (strcmp(members[x].membername, to->GetName()) != 0)) //don't send ourself + { + SendRaidAdd(members[x].membername, to); + } + } +} + +void Raid::QueuePacket(const EQApplicationPacket *app, bool ack_req) +{ + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(members[x].member) + { + members[x].member->QueuePacket(app, 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; + rg->action = raidMakeLeader; + strn0cpy(rg->leader_name, who, 64); + strn0cpy(rg->player_name, who, 64); + rg->parameter = 0; + QueuePacket(outapp); + safe_delete(outapp); +} + +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; + rg->action = raidMakeLeader; + strn0cpy(rg->leader_name, who, 64); + strn0cpy(rg->player_name, who, 64); + rg->parameter = 0; + to->QueuePacket(outapp); + safe_delete(outapp); +} + +//nyi +void Raid::SendMakeGroupLeaderPacketAll() +{ +} + +void Raid::SendMakeGroupLeaderPacket(const char *who) //13 +{ +} + +void Raid::SendMakeGroupLeaderPacketTo(const char *who, Client *to) +{ + if(!to) + return; +} + +void Raid::SendGroupUpdate(Client *to) +{ + if(!to) + return; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupUpdate2_Struct)); + GroupUpdate2_Struct* gu = (GroupUpdate2_Struct*)outapp->pBuffer; + gu->action = groupActUpdate; + int index = 0; + uint32 grp = GetGroup(to->GetName()); + if(grp > 11) + { + safe_delete(outapp); + return; + } + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(members[x].GroupNumber == grp && strlen(members[x].membername) > 0) + { + if(members[x].IsGroupLeader){ + strn0cpy(gu->leadersname, members[x].membername, 64); + if(strcmp(to->GetName(), members[x].membername) != 0){ + strn0cpy(gu->membername[index], members[x].membername, 64); + index++; + } + } + else{ + if(strcmp(to->GetName(), members[x].membername) != 0){ + strn0cpy(gu->membername[index], members[x].membername, 64); + index++; + } + } + } + } + if(strlen(gu->leadersname) < 1){ + strn0cpy(gu->leadersname, to->GetName(), 64); + } + strn0cpy(gu->yourname, to->GetName(), 64); + + to->FastQueuePacket(&outapp); +} + +void Raid::GroupUpdate(uint32 gid, bool initial) +{ + if(gid > 11) //ungrouped member doesn't need grouping. + return; + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strlen(members[x].membername) > 0){ + if(members[x].GroupNumber == gid){ + if(members[x].member) + SendGroupUpdate(members[x].member); + } + } + } + if(initial){ + ServerPacket *pack = new ServerPacket(ServerOP_UpdateGroup, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->gid = gid; + rga->rid = GetID(); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +void Raid::SendRaidLock() +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + rg->action = raidLock; + strn0cpy(rg->leader_name, leadername, 64); + strn0cpy(rg->player_name, leadername, 64); + QueuePacket(outapp); + safe_delete(outapp); +} + +void Raid::SendRaidUnlock() +{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + rg->action = raidUnlock; + strn0cpy(rg->leader_name, leadername, 64); + strn0cpy(rg->player_name, leadername, 64); + QueuePacket(outapp); + safe_delete(outapp); +} + +void Raid::SendRaidLockTo(Client *c) +{ + if(!c) + return; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + rg->action = raidLock; + strn0cpy(rg->leader_name, c->GetName(), 64); + strn0cpy(rg->player_name, c->GetName(), 64); + c->QueuePacket(outapp); + safe_delete(outapp); +} + +void Raid::SendRaidUnlockTo(Client *c) +{ + if(!c) + return; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + rg->action = raidUnlock; + strn0cpy(rg->leader_name, c->GetName(), 64); + strn0cpy(rg->player_name, c->GetName(), 64); + c->QueuePacket(outapp); + safe_delete(outapp); +} + +void Raid::SendGroupDisband(Client *to) +{ + if(!to) + return; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupUpdate_Struct)); + GroupUpdate_Struct* gu = (GroupUpdate_Struct*) outapp->pBuffer; + gu->action = groupActDisband; + strn0cpy(gu->leadersname, leadername, 64); + strn0cpy(gu->yourname, to->GetName(), 64); + to->FastQueuePacket(&outapp); +} + +void Raid::SendRaidGroupAdd(const char *who, uint32 gid) +{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupAdd, sizeof(ServerRaidGroupAction_Struct)); + ServerRaidGroupAction_Struct * rga = (ServerRaidGroupAction_Struct*)pack->pBuffer; + rga->rid = GetID(); + rga->gid = gid; + strn0cpy(rga->membername, who, 64); + safe_delete(pack); +} + +void Raid::SendRaidGroupRemove(const char *who, uint32 gid) +{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupRemove, sizeof(ServerRaidGroupAction_Struct)); + ServerRaidGroupAction_Struct * rga = (ServerRaidGroupAction_Struct*)pack->pBuffer; + rga->rid = GetID(); + rga->gid = gid; + strn0cpy(rga->membername, who, 64); + safe_delete(pack); +} + +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); + } + + safe_delete_array(query); + locked = lockFlag; + if(lockFlag) + SendRaidLock(); + else + SendRaidUnlock(); + + ServerPacket *pack = new ServerPacket(ServerOP_RaidLockFlag, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = GetID(); + rga->zoneid = zone->GetZoneID(); + rga->gid = lockFlag; + rga->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +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); +} + +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); + } +} + +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 = NULL; + 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); +} + +void Raid::VerifyRaid() +{ + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(strlen(members[x].membername) == 0){ + members[x].member = NULL; + } + else{ + Client *c = entity_list.GetClientByName(members[x].membername); + if(c){ + members[x].member = c; + } + else{ + members[x].member = NULL; + } + } + if(members[x].IsRaidLeader){ + if(strlen(members[x].membername) > 0){ + SetLeader(members[x].member); + strn0cpy(leadername, members[x].membername, 64); + } + } + } +} + +void Raid::MemberZoned(Client *c) +{ + if(!c) + return; + + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(members[x].member == c) + { + members[x].member = NULL; + } + } +} + +void Raid::SendHPPacketsTo(Client *c) +{ + if(!c) + return; + + uint32 gid = this->GetGroup(c); + EQApplicationPacket hpapp; + EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct)); + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(members[x].member) + { + if((members[x].member != c) && (members[x].GroupNumber == gid)) + { + members[x].member->CreateHPPacket(&hpapp); + c->QueuePacket(&hpapp, false); + if(c->GetClientVersion() >= EQClientSoD) + { + outapp.SetOpcode(OP_MobManaUpdate); + MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp.pBuffer; + mmus->spawn_id = members[x].member->GetID(); + mmus->mana = members[x].member->GetManaPercent(); + c->QueuePacket(&outapp, false); + outapp.SetOpcode(OP_MobEnduranceUpdate); + MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp.pBuffer; + meus->endurance = members[x].member->GetEndurancePercent(); + c->QueuePacket(&outapp, false); + } + } + } + } +} + +void Raid::SendHPPacketsFrom(Mob *m) +{ + if(!m) + return; + + uint32 gid = 0; + if(m->IsClient()) + gid = this->GetGroup(m->CastToClient()); + EQApplicationPacket hpapp; + EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct)); + + m->CreateHPPacket(&hpapp); + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(members[x].member) + { + if(!m->IsClient() || ((members[x].member != m->CastToClient()) && (members[x].GroupNumber == gid))) + { + members[x].member->QueuePacket(&hpapp, false); + if(members[x].member->GetClientVersion() >= EQClientSoD) + { + outapp.SetOpcode(OP_MobManaUpdate); + MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp.pBuffer; + mmus->spawn_id = m->GetID(); + mmus->mana = m->GetManaPercent(); + members[x].member->QueuePacket(&outapp, false); + outapp.SetOpcode(OP_MobEnduranceUpdate); + MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp.pBuffer; + meus->endurance = m->GetEndurancePercent(); + members[x].member->QueuePacket(&outapp, false); + } + } + } + } +} + +uint16 Raid::GetAvgLevel() +{ + double levelHolder = 0; + uint8 i = 0; + uint8 numMem = 0; + while(i < MAX_RAID_MEMBERS) + { + if(strlen(members[i].membername)) + { + levelHolder = levelHolder + members[i].level; + numMem++; + } + i++; + } + levelHolder = ((levelHolder/(numMem))+.5); // total levels divided by num of characters + return (uint16(levelHolder)); +} + +const char *Raid::GetClientNameByIndex(uint8 index) +{ + return members[index].membername; +} + +void Raid::RaidMessage_StringID(Mob* sender, uint32 type, uint32 string_id, const char* message,const char* message2,const char* message3,const char* message4,const char* message5,const char* message6,const char* message7,const char* message8,const char* message9, uint32 distance) { + uint32 i; + for (i = 0; i < MAX_RAID_MEMBERS; i++) { + if(members[i].member) { + if(members[i].member != sender) + members[i].member->Message_StringID(type, string_id, message, message2, message3, message4, message5, message6, message7, message8, message9, distance); + } + } +} + diff --git a/zone/raids.h b/zone/raids.h new file mode 100644 index 000000000..dd34c0a4d --- /dev/null +++ b/zone/raids.h @@ -0,0 +1,217 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 RAIDS_H +#define RAIDS_H + +#include "../common/types.h" +#include "../common/linked_list.h" +#include "groups.h" +#include +#include +#include + +class Client; +class EQApplicationPacket; + +using namespace std; + +enum { //raid packet types: + raidAdd = 0, + raidRemove2 = 1, //parameter=0 + raidMemberNameChange = 2, + raidRemove1 = 3, //parameter=0xFFFFFFFF + raidNoLongerLeader = 4, + raidDisband = 5, + raidMembers = 6, //len 395+, details + members list + raidNoAssignLeadership = 7, + raidCreate = 8, //len 72 + raidUnknown = 9, + raidNoRaid = 10, //parameter=0 + raidChangeLootType = 11, + raidStringID = 12, + raidChangeGroupLeader = 13, //136 raid leader, new group leader, group_id? + raidBecomeGroupLeader = 14, //472 + raidUnknown2 = 15, + 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 + raidRedStringID = 19, + raidSetLeader = 20, //len 388, contains 'details' struct without members; also used for "invite to raid" + raidMakeLeader = 30, + raidSetMotd = 35, + raidSetNote = 36, +}; + + +enum { //raid command types + RaidCommandInviteIntoExisting = 0, //in use + RaidCommandAcceptInvite = 1, //in use + RaidCommandInvite = 3, //in use + RaidCommandDisband = 5, //in use + RaidCommandMoveGroup = 6, //in use + RaidCommandRemoveGroupLeader = 7, + RaidCommandRaidLock = 8, //in use + RaidCommandRaidUnlock = 9, //in use + RaidCommandLootType = 20, //in use + RaidCommandAddLooter = 21, //in use + RaidCommandRemoveLooter = 22, //in use + RaidCommandMakeLeader = 30, + RaidCommandInviteFail = 31, //already in raid, waiting on invite from other raid, etc + RaidCommandLootType2 = 32, //in use + RaidCommandAddLooter2 = 33, //in use + RaidCommandRemoveLooter2 = 34, //in use + RaidCommandSetMotd = 35, + RaidCommandSetNote = 36, +}; + +#define MAX_RAID_GROUPS 12 +#define MAX_RAID_MEMBERS 72 + +struct RaidMember{ + char membername[64]; + Client *member; + uint32 GroupNumber; + uint8 _class; + uint8 level; + bool IsGroupLeader; + bool IsRaidLeader; + bool IsLooter; +}; + +class Raid : public GroupIDConsumer { +public: + Raid(Client *nLeader); + Raid(uint32 raidID); + ~Raid(); + + void SetLeader(Client *newLeader) { leader = newLeader; } + Client* GetLeader() { return leader; } + bool IsLeader(Client *c) { return leader==c; } + bool IsLeader(const char* name) { return (strcmp(leadername, name)==0); } + void SetRaidLeader(const char *wasLead, const char *name); + + bool Process(); + bool IsRaid() { return true; } + + void AddMember(Client *c, uint32 group = 0xFFFFFFFF, bool rleader=false, bool groupleader=false, bool looter=false); + void RemoveMember(const char *c); + void DisbandRaid(); + void MoveMember(const char *name, uint32 newGroup); + void SetGroupLeader(const char *who, bool glFlag = true); + void RemoveGroupLeader(const char *who); + bool IsGroupLeader(const char *who); + bool IsRaidMember(const char *name); + void UpdateLevel(const char *name, int newLevel); + + uint32 GetFreeGroup(); + uint8 GroupCount(uint32 gid); + uint8 RaidCount(); + uint32 GetHighestLevel(); + uint32 GetLowestLevel(); + uint32 GetGroup(const char *name); + uint32 GetGroup(Client *c); + uint16 GetAvgLevel(); + + uint32 GetLootType() { return LootType; } + void ChangeLootType(uint32 type); + void AddRaidLooter(const char* looter); + void RemoveRaidLooter(const char* looter); + + //util func + //keeps me from having to keep iterating through the list + //when I want lots of data from the same entry + uint32 GetPlayerIndex(const char *name); + //for perl interface + Client *GetClientByIndex(uint16 index); + const char *GetClientNameByIndex(uint8 index); + + void LockRaid(bool lockFlag); + bool IsLocked() { return locked; } + + /* + * Actual Implementation Stuff + */ + + void RaidMessage_StringID(Mob* sender, 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); + void CastGroupSpell(Mob* caster,uint16 spellid, uint32 gid); + void SplitExp(uint32 exp, Mob* other); + uint32 GetTotalRaidDamage(Mob* other); + void BalanceHP(int32 penalty, uint32 gid); + void BalanceMana(int32 penalty, uint32 gid); + void HealGroup(uint32 heal_amt, Mob* caster, uint32 gid); + void SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter = NULL); + void GroupBardPulse(Mob* caster, uint16 spellid, uint32 gid); + + void TeleportGroup(Mob* sender, uint32 zoneID, uint16 instance_id, float x, float y, float z, float heading, uint32 gid); + void TeleportRaid(Mob* sender, uint32 zoneID, uint16 instance_id, float x, float y, float z, float heading); + + //updates the list of Client* objects based on who's in and not in the zone. + //also learns raid structure based on db. + void SetRaidDetails(); + void GetRaidDetails(); + bool LearnMembers(); + void VerifyRaid(); + void MemberZoned(Client *c); + void SendHPPacketsTo(Client *c); + void SendHPPacketsFrom(Mob *m); + void RaidSay(const char *msg, Client *c); + void RaidGroupSay(const char *msg, Client *c); + + //Packet Functions + void SendRaidCreate(Client *to); + void SendRaidAdd(const char *who, Client *to); + void SendRaidAddAll(const char *who); + void SendRaidRemove(const char *who, Client *to); + void SendRaidRemoveAll(const char *who); + void SendRaidDisband(Client *to); + void SendRaidDisbandAll(); + void SendRaidMove(const char* who, Client *to); + void SendRaidMoveAll(const char* who); + void SendBulkRaid(Client *to); + + void GroupUpdate(uint32 gid, bool initial = true); + void SendGroupUpdate(Client *to); + void SendGroupDisband(Client *to); + void SendRaidLock(); + void SendRaidUnlock(); + void SendRaidLockTo(Client *c); + void SendRaidUnlockTo(Client *c); + void SendRaidGroupAdd(const char *who, uint32 gid); + void SendRaidGroupRemove(const char *who, uint32 gid); + void SendMakeLeaderPacket(const char *who); //30 + void SendMakeLeaderPacketTo(const char *who, Client *to); + void SendMakeGroupLeaderPacketAll(); + void SendMakeGroupLeaderPacket(const char *who); //13 + void SendMakeGroupLeaderPacketTo(const char *who, Client *to); + + void QueuePacket(const EQApplicationPacket *app, bool ack_req = true); + + RaidMember members[MAX_RAID_MEMBERS]; + char leadername[64]; +protected: + Client *leader; + bool locked; + uint16 numMembers; + uint32 LootType; + bool disbandCheck; + bool forceDisband; +}; + + +#endif + diff --git a/zone/skills.h b/zone/skills.h new file mode 100644 index 000000000..2ea5a7b9f --- /dev/null +++ b/zone/skills.h @@ -0,0 +1,101 @@ +/* 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 SKILLS_H +#define SKILLS_H + +#define HIGHEST_SKILL 74 + + +// Correct Skill Numbers as of 4-14-2002 +#define _1H_BLUNT 0 +#define _1H_SLASHING 1 +#define _2H_BLUNT 2 +#define _2H_SLASHING 3 +#define ABJURE 4 +#define ALTERATION 5 +#define APPLY_POISON 6 +#define ARCHERY 7 +#define BACKSTAB 8 +#define BIND_WOUND 9 +#define BASH 10 +#define BLOCKSKILL 11 +#define BRASS_INSTRUMENTS 12 +#define CHANNELING 13 +#define CONJURATION 14 +#define DEFENSE 15 +#define DISARM 16 +#define DISARM_TRAPS 17 +#define DIVINATION 18 +#define DODGE 19 +#define DOUBLE_ATTACK 20 +#define DRAGON_PUNCH 21 +#define DUAL_WIELD 22 +#define EAGLE_STRIKE 23 +#define EVOCATION 24 +#define FEIGN_DEATH 25 +#define FLYING_KICK 26 +#define FORAGE 27 +#define HAND_TO_HAND 28 +#define HIDE 29 +#define KICK 30 +#define MEDITATE 31 +#define MEND 32 +#define OFFENSE 33 +#define PARRY 34 +#define PICK_LOCK 35 +#define PIERCING 36 +#define RIPOSTE 37 +#define ROUND_KICK 38 +#define SAFE_FALL 39 +#define SENSE_HEADING 40 +#define SINGING 41 +#define SNEAK 42 +#define SPECIALIZE_ABJURE 43 +#define SPECIALIZE_ALTERATION 44 +#define SPECIALIZE_CONJURATION 45 +#define SPECIALIZE_DIVINATION 46 +#define SPECIALIZE_EVOCATION 47 +#define PICK_POCKETS 48 +#define STRINGED_INSTRUMENTS 49 +#define SWIMMING 50 +#define THROWING 51 +#define TIGER_CLAW 52 +#define TRACKING 53 +#define WIND_INSTRUMENTS 54 +#define FISHING 55 +#define MAKE_POISON 56 +#define TINKERING 57 +#define RESEARCH 58 +#define ALCHEMY 59 +#define BAKING 60 +#define TAILORING 61 +#define SENSE_TRAPS 62 +#define BLACKSMITHING 63 +#define FLETCHING 64 +#define BREWING 65 +#define ALCOHOL_TOLERANCE 66 +#define BEGGING 67 +#define JEWELRY_MAKING 68 +#define POTTERY 69 +#define PERCUSSION_INSTRUMENTS 70 +#define INTIMIDATION 71 +#define BERSERKING 72 +#define TAUNT 73 +#define FRENZY 74 + +#endif diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp new file mode 100644 index 000000000..e08bc572a --- /dev/null +++ b/zone/spawn2.cpp @@ -0,0 +1,1201 @@ +/* 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 "../common/debug.h" +#include +#include "spawn2.h" +#include "entity.h" +#include "masterentity.h" +#include "zone.h" +#include "spawngroup.h" +#include "zonedb.h" +#include "worldserver.h" + +extern EntityList entity_list; +extern Zone* zone; + +extern WorldServer worldserver; + +/* + +CREATE TABLE spawn_conditions ( + zone VARCHAR(16) NOT NULL, + id MEDIUMINT UNSIGNED NOT NULL DEFAULT '1', + value MEDIUMINT NOT NULL DEFAULT '0', + onchange TINYINT UNSIGNED NOT NULL DEFAULT '0', + name VARCHAR(255) NOT NULL DEFAULT '', + PRIMARY KEY(zone,id) +); + +CREATE TABLE spawn_events ( + #identifiers + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + zone VARCHAR(16) NOT NULL, + cond_id MEDIUMINT UNSIGNED NOT NULL, + name VARCHAR(255) NOT NULL DEFAULT '', + + #timing information + period INT UNSIGNED NOT NULL, + next_minute TINYINT UNSIGNED NOT NULL, + next_hour TINYINT UNSIGNED NOT NULL, + next_day TINYINT UNSIGNED NOT NULL, + next_month TINYINT UNSIGNED NOT NULL, + next_year INT UNSIGNED NOT NULL, + enabled TINYINT NOT NULL DEFAULT '1', + + #action: + action TINYINT UNSIGNED NOT NULL DEFAULT '0', + argument MEDIUMINT NOT NULL DEFAULT '0' +); + +*/ + +Spawn2::Spawn2(uint32 in_spawn2_id, uint32 spawngroup_id, + float in_x, float in_y, float in_z, float in_heading, + uint32 respawn, uint32 variance, uint32 timeleft, uint32 grid, + uint16 in_cond_id, int16 in_min_value, bool in_enabled, EmuAppearance anim) +: timer(100000) +{ + spawn2_id = in_spawn2_id; + spawngroup_id_ = spawngroup_id; + x = in_x; + y = in_y; + z = in_z; + heading = in_heading; + respawn_ = respawn; + variance_ = variance; + grid_ = grid; + condition_id = in_cond_id; + condition_min_value = in_min_value; + npcthis = NULL; + enabled = in_enabled; + this->anim = anim; + + if(timeleft == 0xFFFFFFFF) { + //special disable timeleft + timer.Disable(); + } else if(timeleft != 0){ + //we have a timeleft from the DB or something + timer.Start(timeleft); + } else { + //no timeleft at all, reset to + timer.Start(resetTimer()); + timer.Trigger(); + } +} + +Spawn2::~Spawn2() +{ +} + +uint32 Spawn2::resetTimer() +{ + uint32 rspawn = respawn_ * 1000; + + if (variance_ != 0) { + int var_over_2 = (variance_ * 1000) / 2; + rspawn = MakeRandomInt(rspawn - var_over_2, rspawn + var_over_2); + + //put a lower bound on it, not a lot of difference below 100, so set that as the bound. + if(rspawn < 100) + rspawn = 100; + } + + return (rspawn); + +} + +uint32 Spawn2::despawnTimer(uint32 despawn_timer) +{ + uint32 dspawn = despawn_timer * 1000; + + if (variance_ != 0) { + int var_over_2 = (variance_ * 1000) / 2; + dspawn = MakeRandomInt(dspawn - var_over_2, dspawn + var_over_2); + + //put a lower bound on it, not a lot of difference below 100, so set that as the bound. + if(dspawn < 100) + dspawn = 100; + } + + return (dspawn); + +} + +bool Spawn2::Process() { + _ZP(Spawn2_Process); + + IsDespawned = false; + + if(!Enabled()) + return true; + + //grab our spawn group + SpawnGroup* sg = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_); + + if(NPCPointerValid() && (sg->despawn == 0 || condition_id != 0)) + return true; + + if (timer.Check()) { + timer.Disable(); + + _log(SPAWNS__MAIN, "Spawn2 %d: Timer has triggered", spawn2_id); + + //first check our spawn condition, if this isnt active + //then we reset the timer and try again next time. + if(condition_id != SC_AlwaysEnabled + && !zone->spawn_conditions.Check(condition_id, condition_min_value)) { + _log(SPAWNS__CONDITIONS, "Spawn2 %d: spawning prevented by spawn condition %d", spawn2_id, condition_id); + Reset(); + return(true); + } + + if (sg == NULL) { + database.LoadSpawnGroupsByID(spawngroup_id_,&zone->spawn_group_list); + sg = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_); + } + + if (sg == NULL) { + _log(SPAWNS__MAIN, "Spawn2 %d: Unable to locate spawn group %d. Disabling.", spawn2_id, spawngroup_id_); + return false; + } + + //have the spawn group pick an NPC for us + uint32 npcid = sg->GetNPCType(); + if (npcid == 0) { + _log(SPAWNS__MAIN, "Spawn2 %d: Spawn group %d did not yeild an NPC! not spawning.", spawn2_id, spawngroup_id_); + Reset(); //try again later (why?) + return(true); + } + + //try to find our NPC type. + const NPCType* tmp = database.GetNPCType(npcid); + if (tmp == NULL) { + _log(SPAWNS__MAIN, "Spawn2 %d: Spawn group %d yeilded an invalid NPC type %d", spawn2_id, spawngroup_id_, npcid); + Reset(); //try again later + return(true); + } + + if(tmp->unique_spawn_by_name) + { + if(!entity_list.LimitCheckName(tmp->name)) + { + _log(SPAWNS__MAIN, "Spawn2 %d: Spawn group %d yeilded NPC type %d, which is unique and one already exists.", spawn2_id, spawngroup_id_, npcid); + timer.Start(5000); //try again in five seconds. + return(true); + } + } + + if(tmp->spawn_limit > 0) { + if(!entity_list.LimitCheckType(npcid, tmp->spawn_limit)) { + _log(SPAWNS__MAIN, "Spawn2 %d: Spawn group %d yeilded NPC type %d, which is over its spawn limit (%d)", spawn2_id, spawngroup_id_, npcid, tmp->spawn_limit); + timer.Start(5000); //try again in five seconds. + return(true); + } + } + + if(sg->despawn != 0 && condition_id == 0) + zone->Despawn(spawn2_id); + + if(IsDespawned) + return true; + + if(spawn2_id) + database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), 0); + + currentnpcid = npcid; + NPC* npc = new NPC(tmp, this, x, y, z, heading, FlyMode3); + npcthis = npc; + npc->AddLootTable(); + npc->SetSp2(spawngroup_id_); + npc->SaveGuardPointAnim(anim); + npc->SetAppearance((EmuAppearance)anim); + entity_list.AddNPC(npc); + //this limit add must be done after the AddNPC since we need the entity ID. + entity_list.LimitAddNPC(npc); + if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay) + npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay); + if(zone->InstantGrids()) { + _log(SPAWNS__MAIN, "Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f).", spawn2_id, spawngroup_id_, npc->GetName(), npcid, x, y, z); + LoadGrid(); + } else { + _log(SPAWNS__MAIN, "Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f). Grid loading delayed.", spawn2_id, spawngroup_id_, tmp->name, npcid, x, y, z); + } + } + return true; +} + +void Spawn2::Disable() +{ + if(npcthis) + { + npcthis->Depop(); + } + enabled = false; +} + +void Spawn2::LoadGrid() { + if(!npcthis) + return; + if(grid_ < 1) + return; + if(!entity_list.IsMobInZone(npcthis)) + return; + //dont set an NPC's grid until its loaded for them. + npcthis->SetGrid(grid_); + npcthis->AssignWaypoints(grid_); + _log(SPAWNS__MAIN, "Spawn2 %d: Loading grid %d for %s", spawn2_id, grid_, npcthis->GetName()); +} + + +/* + All three of these actions basically say that the mob which was + associated with this spawn point is no longer relavent. +*/ +void Spawn2::Reset() { + timer.Start(resetTimer()); + npcthis = NULL; + _log(SPAWNS__MAIN, "Spawn2 %d: Spawn reset, repop in %d ms", spawn2_id, timer.GetRemainingTime()); +} + +void Spawn2::Depop() { + timer.Disable(); + _log(SPAWNS__MAIN, "Spawn2 %d: Spawn reset, repop disabled", spawn2_id); + npcthis = NULL; +} + +void Spawn2::Repop(uint32 delay) { + if (delay == 0) { + timer.Trigger(); + _log(SPAWNS__MAIN, "Spawn2 %d: Spawn reset, repop immediately.", spawn2_id); + } else { + _log(SPAWNS__MAIN, "Spawn2 %d: Spawn reset for repop, repop in %d ms", spawn2_id, delay); + timer.Start(delay); + } + npcthis = NULL; +} + +void Spawn2::ForceDespawn() +{ + SpawnGroup* sg = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_); + + if(npcthis != NULL) + { + if(!npcthis->IsEngaged()) + { + if(sg->despawn == 3 || sg->despawn == 4) + { + npcthis->Depop(true); + IsDespawned = true; + return; + } + else + { + npcthis->Depop(false); + } + } + } + + uint32 cur = 100000; + uint32 dtimer = sg->despawn_timer; + + if(sg->despawn == 1 || sg->despawn == 3) + { + cur = resetTimer(); + } + + if(sg->despawn == 2 || sg->despawn == 4) + { + cur = despawnTimer(dtimer); + } + + _log(SPAWNS__MAIN, "Spawn2 %d: Spawn group %d set despawn timer to %d ms.", spawn2_id, spawngroup_id_, cur); + timer.Start(cur); +} + +//resets our spawn as if we just died +void Spawn2::DeathReset() +{ + //get our reset based on variance etc and store it locally + uint32 cur = resetTimer(); + //set our timer to our reset local + timer.Start(cur); + + //zero out our NPC since he is now gone + npcthis = NULL; + + //if we have a valid spawn id + if(spawn2_id) + { + database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), (cur/1000)); + _log(SPAWNS__MAIN, "Spawn2 %d: Spawn reset by death, repop in %d ms", spawn2_id, timer.GetRemainingTime()); + //store it to database too + } +} + +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); + return false; + } + + 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); + } + + LogFile->write(EQEMuLog::Error, "Error in LoadSpawn2 query '%s': %s", query, errbuf); + safe_delete_array(query); + return 0; +} + +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) +{ + 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); + return false; + } + + return false; +} + +uint32 Zone::CountSpawn2() { + LinkedListIterator iterator(spawn2_list); + uint32 count = 0; + + iterator.Reset(); + while(iterator.MoreElements()) + { + count++; + iterator.Advance(); + } + return count; +} + +uint32 Zone::DumpSpawn2(ZSDump_Spawn2* spawn2dump, uint32* spawn2index, Spawn2* spawn2) { + if (spawn2 == 0) + return 0; + LinkedListIterator iterator(spawn2_list); + // uint32 index = 0; + + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData() == spawn2) { + spawn2dump[*spawn2index].spawn2_id = iterator.GetData()->spawn2_id; + spawn2dump[*spawn2index].time_left = iterator.GetData()->timer.GetRemainingTime(); + iterator.RemoveCurrent(); + return (*spawn2index)++; + } + iterator.Advance(); + } + return 0xFFFFFFFF; +} + +void Zone::DumpAllSpawn2(ZSDump_Spawn2* spawn2dump, uint32* spawn2index) { + LinkedListIterator iterator(spawn2_list); + // uint32 index = 0; + + iterator.Reset(); + while(iterator.MoreElements()) + { + spawn2dump[*spawn2index].spawn2_id = iterator.GetData()->spawn2_id; + spawn2dump[*spawn2index].time_left = iterator.GetData()->timer.GetRemainingTime(); + (*spawn2index)++; + iterator.RemoveCurrent(); + + } +} + +void Zone::Despawn(uint32 spawn2ID) { + LinkedListIterator iterator(spawn2_list); + + iterator.Reset(); + while(iterator.MoreElements()) { + Spawn2 *cur = iterator.GetData(); + if(spawn2ID == cur->spawn2_id) + cur->ForceDespawn(); + iterator.Advance(); + } +} + +void Spawn2::SpawnConditionChanged(const SpawnCondition &c, int16 old_value) { + if(GetSpawnCondition() != c.condition_id) + return; + + _log(SPAWNS__CONDITIONS, "Spawn2 %d: Notified that our spawn condition %d has changed from %d to %d. Our min value is %d.", spawn2_id, c.condition_id, old_value, c.value, condition_min_value); + + bool old_state = (old_value >= condition_min_value); + bool new_state = (c.value >= condition_min_value); + if(old_state == new_state) { + _log(SPAWNS__CONDITIONS, "Spawn2 %d: Our threshold for this condition was not crossed. Doing nothing.", spawn2_id); + return; //no change + } + + switch(c.on_change) { + case SpawnCondition::DoNothing: + //that was easy. + _log(SPAWNS__CONDITIONS, "Spawn2 %d: Our condition is now %s. Taking no action on existing spawn.", spawn2_id, new_state?"enabed":"disabled"); + break; + case SpawnCondition::DoDepop: + _log(SPAWNS__CONDITIONS, "Spawn2 %d: Our condition is now %s. Depoping our mob.", spawn2_id, new_state?"enabed":"disabled"); + if(npcthis != NULL) + npcthis->Depop(false); //remove the current mob + Reset(); //reset our spawn timer + break; + case SpawnCondition::DoRepop: + _log(SPAWNS__CONDITIONS, "Spawn2 %d: Our condition is now %s. Preforming a repop.", spawn2_id, new_state?"enabed":"disabled"); + if(npcthis != NULL) + npcthis->Depop(false); //remove the current mob + Repop(); //repop + break; + default: + if(c.on_change < SpawnCondition::DoSignalMin) { + _log(SPAWNS__CONDITIONS, "Spawn2 %d: Our condition is now %s. Invalid on-change action %d.", spawn2_id, new_state?"enabed":"disabled", c.on_change); + return; //unknown onchange action + } + int signal_id = c.on_change - SpawnCondition::DoSignalMin; + _log(SPAWNS__CONDITIONS, "Spawn2 %d: Our condition is now %s. Signaling our mob with %d.", spawn2_id, new_state?"enabed":"disabled", signal_id); + if(npcthis != NULL) + npcthis->SignalNPC(signal_id); + } +} + +void Zone::SpawnConditionChanged(const SpawnCondition &c, int16 old_value) { + _log(SPAWNS__CONDITIONS, "Zone notified that spawn condition %d has changed from %d to %d. Notifying all spawn points.", c.condition_id, old_value, c.value); + + LinkedListIterator iterator(spawn2_list); + + iterator.Reset(); + while(iterator.MoreElements()) { + Spawn2 *cur = iterator.GetData(); + if(cur->GetSpawnCondition() == c.condition_id) + cur->SpawnConditionChanged(c, old_value); + iterator.Advance(); + } +} + +SpawnCondition::SpawnCondition() { + condition_id = 0; + value = 0; + on_change = DoNothing; +} + +SpawnEvent::SpawnEvent() { + id = 0; + condition_id = 0; + enabled = false; + action = ActionSet; + argument = 0; + period = 0xFFFFFFFF; + memset(&next, 0, sizeof(next)); +} + +SpawnConditionManager::SpawnConditionManager() + : minute_timer(3000) //1 eq minute +{ + memset(&next_event, 0, sizeof(next_event)); +} + +void SpawnConditionManager::Process() { + if(spawn_events.empty()) + return; + + if(minute_timer.Check()) { + //check each spawn event. + + //get our current time + TimeOfDay_Struct tod; + zone->zone_time.getEQTimeOfDay(&tod); + + //see if time is past our nearest event. + if(EQTime::IsTimeBefore(&next_event, &tod)) + return; + + //at least one event should get triggered, + vector::iterator cur,end; + cur = spawn_events.begin(); + end = spawn_events.end(); + for(; cur != end; cur++) { + SpawnEvent &cevent = *cur; + + if(!cevent.enabled) + continue; + + if(EQTime::IsTimeBefore(&tod, &cevent.next)) { + //this event has been triggered. + //execute the event + ExecEvent(cevent, true); + //add the period of the event to the trigger time + EQTime::AddMinutes(cevent.period, &cevent.next); + string t; + EQTime::ToString(&cevent.next, t); + _log(SPAWNS__CONDITIONS, "Event %d: Will trigger again in %d EQ minutes at %s.", cevent.id, cevent.period, t.c_str()); + //save the next event time in the DB + UpdateDBEvent(cevent); + //find the next closest event timer. + FindNearestEvent(); + //minor optimization, if theres no more possible events, + //then stop trying... I dunno how worth while this is. + if(EQTime::IsTimeBefore(&next_event, &tod)) + return; + } + } + } +} + +void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) { + map::iterator condi; + condi = spawn_conditions.find(event.condition_id); + if(condi == spawn_conditions.end()) { + _log(SPAWNS__CONDITIONS, "Event %d: Unable to find condition %d to execute on.", event.id, event.condition_id); + return; //unable to find the spawn condition to operate on + } + + SpawnCondition &cond = condi->second; + + int16 new_value = cond.value; + + //we have our event and our condition, do our stuff. + switch(event.action) { + case SpawnEvent::ActionSet: + new_value = event.argument; + _log(SPAWNS__CONDITIONS, "Event %d: Executing. Setting condition %d to %d.", event.id, event.condition_id, event.argument); + break; + case SpawnEvent::ActionAdd: + new_value += event.argument; + _log(SPAWNS__CONDITIONS, "Event %d: Executing. Adding %d to condition %d, yeilding %d.", event.id, event.argument, event.condition_id, new_value); + break; + case SpawnEvent::ActionSubtract: + new_value -= event.argument; + _log(SPAWNS__CONDITIONS, "Event %d: Executing. Subtracting %d from condition %d, yeilding %d.", event.id, event.argument, event.condition_id, new_value); + break; + case SpawnEvent::ActionMultiply: + new_value *= event.argument; + _log(SPAWNS__CONDITIONS, "Event %d: Executing. Multiplying condition %d by %d, yeilding %d.", event.id, event.condition_id, event.argument, new_value); + break; + case SpawnEvent::ActionDivide: + new_value /= event.argument; + _log(SPAWNS__CONDITIONS, "Event %d: Executing. Dividing condition %d by %d, yeilding %d.", event.id, event.condition_id, event.argument, new_value); + break; + default: + _log(SPAWNS__CONDITIONS, "Event %d: Invalid event action type %d", event.id, event.action); + return; + } + + //now set the condition to the new value + if(send_update) //full blown update + SetCondition(zone->GetShortName(), zone->GetInstanceID(), cond.condition_id, new_value); + else //minor update done while loading + cond.value = new_value; +} + +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 " + "WHERE id=%d", + event.next.minute, event.next.hour, event.next.day, event.next.month, + event.next.year, event.enabled?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); +} + +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); +} + +bool SpawnConditionManager::LoadDBEvent(uint32 event_id, SpawnEvent &event, 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,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]); + zone_name = row[11]; + + string t; + EQTime::ToString(&event.next, t); + _log(SPAWNS__CONDITIONS, "Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d. Will trigger at %s", + event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, 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); + } + return(ret); +} + +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); + return false; + } + + //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); + spawn_conditions.clear(); + return false; + } + + //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 " + "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]); + spawn_events.push_back(event); + + _log(SPAWNS__CONDITIONS, "Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d", + event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument); + } + mysql_free_result(result); + } else { + LogFile->write(EQEMuLog::Error, "Error in LoadSpawnConditions events query '%s': %s", query, errbuf); + safe_delete_array(query); + return false; + } + + //now we need to catch up on events that happened while we were away + //and use them to alter just the condition variables. + + //each spawn2 will then use its correct condition value when + //it decides what to do. This essentially forces a 'depop' action + //on spawn points which are turned off, and a 'repop' action on + //spawn points which get turned on. Im too lazy to figure out a + //better solution, and I just dont care thats much. + //get our current time + TimeOfDay_Struct tod; + zone->zone_time.getEQTimeOfDay(&tod); + + vector::iterator cur,end; + cur = spawn_events.begin(); + end = spawn_events.end(); + bool ran; + for(; cur != end; cur++) { + SpawnEvent &cevent = *cur; + + if(!cevent.enabled) + continue; + + //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. + } + + 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 + 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); + } + } + + //now our event timers are all up to date, find our closest event. + FindNearestEvent(); + + return(true); +} + +void SpawnConditionManager::FindNearestEvent() { + //set a huge year which should never get reached normally + next_event.year = 0xFFFFFF; + + vector::iterator cur,end; + cur = spawn_events.begin(); + end = spawn_events.end(); + int next_id = -1; + for(; cur != end; cur++) { + SpawnEvent &cevent = *cur; + + if(!cevent.enabled) + continue; + + //see if this event is before our last nearest + if(EQTime::IsTimeBefore(&next_event, &cevent.next)) { + memcpy(&next_event, &cevent.next, sizeof(next_event)); + next_id = cevent.id; + } + } + if(next_id == -1) + _log(SPAWNS__CONDITIONS, "No spawn events enabled. Disabling next event."); + else + _log(SPAWNS__CONDITIONS, "Next event determined to be event %d", next_id); +} + +void SpawnConditionManager::SetCondition(const char *zone_short, uint32 instance_id, uint16 condition_id, int16 new_value, bool world_update) +{ + if(world_update) { + //this is an update coming from another zone, they + //have allready updated the DB, just need to update our + //memory, and check for condition changes + map::iterator condi; + condi = spawn_conditions.find(condition_id); + if(condi == spawn_conditions.end()) { + _log(SPAWNS__CONDITIONS, "Condition update received from world for %d, but we do not have that conditon.", condition_id); + return; //unable to find the spawn condition + } + + SpawnCondition &cond = condi->second; + + if(cond.value == new_value) { + _log(SPAWNS__CONDITIONS, "Condition update received from world for %d with value %d, which is what we already have.", condition_id, new_value); + return; + } + + int16 old_value = cond.value; + + //set our local value + cond.value = new_value; + + _log(SPAWNS__CONDITIONS, "Condition update received from world for %d with value %d", condition_id, new_value); + + //now we have to test each spawn point to see if it changed. + zone->SpawnConditionChanged(cond, old_value); + } else if(!strcasecmp(zone_short, zone->GetShortName()) && instance_id == zone->GetInstanceID()) + { + //this is a local spawn condition, we need to update the DB, + //our memory, then notify spawn points of the change. + map::iterator condi; + condi = spawn_conditions.find(condition_id); + if(condi == spawn_conditions.end()) { + _log(SPAWNS__CONDITIONS, "Local Condition update requested for %d, but we do not have that conditon.", condition_id); + return; //unable to find the spawn condition + } + + SpawnCondition &cond = condi->second; + + if(cond.value == new_value) { + _log(SPAWNS__CONDITIONS, "Local Condition update requested for %d with value %d, which is what we already have.", condition_id, new_value); + return; + } + + int16 old_value = cond.value; + + //set our local value + cond.value = new_value; + //save it in the DB too + UpdateDBCondition(zone_short, instance_id, condition_id, new_value); + + _log(SPAWNS__CONDITIONS, "Local Condition update requested for %d with value %d", condition_id, new_value); + + //now we have to test each spawn point to see if it changed. + zone->SpawnConditionChanged(cond, old_value); + } + else + { + //this is a remote spawn condition, update the DB and send + //an update packet to the zone if its up + + _log(SPAWNS__CONDITIONS, "Remote spawn condition %d set to %d. Updating DB and notifying world.", condition_id, new_value); + + UpdateDBCondition(zone_short, instance_id, condition_id, new_value); + + ServerPacket* pack = new ServerPacket(ServerOP_SpawnCondition, sizeof(ServerSpawnCondition_Struct)); + ServerSpawnCondition_Struct* ssc = (ServerSpawnCondition_Struct*)pack->pBuffer; + + ssc->zoneID = database.GetZoneID(zone_short); + ssc->instanceID = instance_id; + ssc->condition_id = condition_id; + ssc->value = new_value; + + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +void SpawnConditionManager::ReloadEvent(uint32 event_id) { + string zone_short_name; + + _log(SPAWNS__CONDITIONS, "Requested to reload event %d from the database.", event_id); + + //first look for the event in our local event list + vector::iterator cur,end; + cur = spawn_events.begin(); + end = spawn_events.end(); + for(; cur != end; cur++) { + SpawnEvent &cevent = *cur; + + if(cevent.id == event_id) { + //load the event into the old event slot + if(!LoadDBEvent(event_id, cevent, zone_short_name)) { + //unable to find the event in the database... + _log(SPAWNS__CONDITIONS, "Failed to reload event %d from the database.", event_id); + return; + } + //sync up our nearest event + FindNearestEvent(); + return; + } + } + + //if we get here, it is a new event... + SpawnEvent e; + if(!LoadDBEvent(event_id, e, zone_short_name)) { + //unable to find the event in the database... + _log(SPAWNS__CONDITIONS, "Failed to reload event %d from the database.", event_id); + return; + } + + //we might want to check the next timer like we do on + //regular load events, but we are assuming this is a new event + //and anyways, this will get handled (albeit not optimally) + //naturally by the event handling code. + + spawn_events.push_back(e); + + //sync up our nearest event + FindNearestEvent(); +} + +void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool reset_base) { + + _log(SPAWNS__CONDITIONS, "Request to %s spawn event %d %sresetting trigger time", enabled?"enable":"disable", event_id, reset_base?"":"without "); + + //first look for the event in our local event list + vector::iterator cur,end; + cur = spawn_events.begin(); + end = spawn_events.end(); + for(; cur != end; cur++) { + SpawnEvent &cevent = *cur; + + if(cevent.id == event_id) { + //make sure were actually changing something + if(cevent.enabled != enabled || reset_base) { + cevent.enabled = enabled; + if(reset_base) { + _log(SPAWNS__CONDITIONS, "Spawn event %d located in this zone. State set. Trigger time reset (period %d).", event_id, cevent.period); + //start with the time now + zone->zone_time.getEQTimeOfDay(&cevent.next); + //advance the next time by our period + EQTime::AddMinutes(cevent.period, &cevent.next); + } else { + _log(SPAWNS__CONDITIONS, "Spawn event %d located in this zone. State changed.", event_id); + } + + //save the event in the DB + UpdateDBEvent(cevent); + + //sync up our nearest event + FindNearestEvent(); + } else { + _log(SPAWNS__CONDITIONS, "Spawn event %d located in this zone but no change was needed.", event_id); + } + //even if we dont change anything, we still found it + return; + } + } + + //if we get here, the condition must be in another zone. + //first figure out what zone it applies to. + //update the DB and send and send an update packet to + //the zone if its up + + //we need to load the event from the DB and then update + //the values in the DB, because we do not know if the zone + //is up or not. The message to the zone will just tell it to + //update its in-memory event list + SpawnEvent e; + string zone_short_name; + if(!LoadDBEvent(event_id, e, zone_short_name)) { + _log(SPAWNS__CONDITIONS, "Unable to find spawn event %d in the database.", event_id); + //unable to find the event in the database... + return; + } + if(e.enabled == enabled && !reset_base) { + _log(SPAWNS__CONDITIONS, "Spawn event %d is not located in this zone but no change was needed.", event_id); + return; //no changes. + } + + e.enabled = enabled; + if(reset_base) { + _log(SPAWNS__CONDITIONS, "Spawn event %d is in zone %s. State set. Trigger time reset (period %d). Notifying world.", event_id, zone_short_name.c_str(), e.period); + //start with the time now + zone->zone_time.getEQTimeOfDay(&e.next); + //advance the next time by our period + EQTime::AddMinutes(e.period, &e.next); + } else { + _log(SPAWNS__CONDITIONS, "Spawn event %d is in zone %s. State changed. Notifying world.", event_id, zone_short_name.c_str(), e.period); + } + //save the event in the DB + UpdateDBEvent(e); + + + //now notify the zone + ServerPacket* pack = new ServerPacket(ServerOP_SpawnEvent, sizeof(ServerSpawnEvent_Struct)); + ServerSpawnEvent_Struct* sse = (ServerSpawnEvent_Struct*)pack->pBuffer; + + sse->zoneID = database.GetZoneID(zone_short_name.c_str()); + sse->event_id = event_id; + + worldserver.SendPacket(pack); + safe_delete(pack); +} + +int16 SpawnConditionManager::GetCondition(const char *zone_short, uint32 instance_id, uint16 condition_id) { + if(!strcasecmp(zone_short, zone->GetShortName()) && instance_id == zone->GetInstanceID()) + { + //this is a local spawn condition + map::iterator condi; + condi = spawn_conditions.find(condition_id); + if(condi == spawn_conditions.end()) + { + _log(SPAWNS__CONDITIONS, "Unable to find local condition %d in Get request.", condition_id); + return(0); //unable to find the spawn condition + } + + 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); + } +} + +bool SpawnConditionManager::Check(uint16 condition, int16 min_value) { + map::iterator condi; + condi = spawn_conditions.find(condition); + if(condi == spawn_conditions.end()) + return(false); //unable to find the spawn condition + + SpawnCondition &cond = condi->second; + + return(cond.value >= min_value); +} + + + + + + + + + + diff --git a/zone/spawn2.h b/zone/spawn2.h new file mode 100644 index 000000000..4956e13d0 --- /dev/null +++ b/zone/spawn2.h @@ -0,0 +1,167 @@ +/* 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 SPAWN2_H +#define SPAWN2_H + +#include "../common/timer.h" +#include "npc.h" + +#include +using namespace std; + +#define SC_AlwaysEnabled 0 + +class SpawnCondition; +class NPC; + +class Spawn2 +{ +public: + Spawn2(uint32 spawn2_id, uint32 spawngroup_id, + float x, float y, float z, float heading, + uint32 respawn, uint32 variance, + uint32 timeleft = 0, uint32 grid = 0, + uint16 cond_id = SC_AlwaysEnabled, int16 min_value = 0, bool in_enabled = true, EmuAppearance anim = eaStanding); + ~Spawn2(); + + void LoadGrid(); + void Enable() { enabled = true; } + void Disable(); + bool Enabled() { return enabled; } + bool Process(); + void Reset(); + void Depop(); + void Repop(uint32 delay = 0); + void ForceDespawn(); + + void DeathReset(); //resets the spawn in the case the npc dies, also updates db if needed + + void SpawnConditionChanged(const SpawnCondition &c, int16 old_value); + uint32 GetID() { return spawn2_id; } + float GetX() { return x; } + float GetY() { return y; } + float GetZ() { return z; } + float GetHeading() { return heading; } + void SetRespawnTimer(uint32 newrespawntime) { respawn_ = newrespawntime; }; + void SetVariance(uint32 newvariance) { variance_ = newvariance; } + const uint32 GetVariance() const { return variance_; } + uint32 RespawnTimer() { return respawn_; } + uint32 SpawnGroupID() { return spawngroup_id_; } + uint32 CurrentNPCID() { return currentnpcid; } + void SetCurrentNPCID(uint32 nid) { currentnpcid = nid; } + uint32 GetSpawnCondition() { return condition_id; } + uint32 spawn2_id; + uint32 respawn_; + + bool NPCPointerValid() { return (npcthis!=NULL); } + void SetNPCPointer(NPC* n) { npcthis = n; } + void SetTimer(uint32 duration) { timer.Start(duration); } +protected: + friend class Zone; + Timer timer; +private: + uint32 resetTimer(); + uint32 despawnTimer(uint32 despawn_timer); + + uint32 spawngroup_id_; + uint32 currentnpcid; + NPC* npcthis; + float x; + float y; + float z; + float heading; + uint32 variance_; + uint32 grid_; + uint16 condition_id; + int16 condition_min_value; + bool enabled; + EmuAppearance anim; + bool IsDespawned; +}; + +class SpawnCondition { +public: + typedef enum { + DoNothing = 0, + DoDepop = 1, + DoRepop = 2, + //... 3...9 reserved for future use + DoSignalMin = 10 //any number above this value is used as + //a base for the signal ID sent. e.g. + // value 12 sends signal id 2 + } OnChange; + + SpawnCondition(); + + uint16 condition_id; + int16 value; + OnChange on_change; +}; + +class SpawnEvent { +public: + typedef enum { + ActionSet = 0, + ActionAdd = 1, + ActionSubtract = 2, + ActionMultiply = 3, + ActionDivide = 4 + } Action; + + SpawnEvent(); + + uint32 id; + uint16 condition_id; + string zone_name; + + bool enabled; + Action action; + int16 argument; + + uint32 period; //eq minutes (3 seconds) between events + TimeOfDay_Struct next; //next time this event triggers +}; + +class SpawnConditionManager { +public: + SpawnConditionManager(); + + void Process(); + bool LoadSpawnConditions(const char* zone_name, uint32 instance_id); + + int16 GetCondition(const char *zone_short, uint32 instance_id, uint16 condition_id); + void SetCondition(const char *zone_short, uint32 instance_id, uint16 condition_id, int16 new_value, bool world_update = false); + void ToggleEvent(uint32 event_id, bool enabled, bool reset_base); + bool Check(uint16 condition, int16 min_value); + void ReloadEvent(uint32 event_id); + +protected: + map spawn_conditions; + vector spawn_events; + + void ExecEvent(SpawnEvent &e, bool send_update); + void UpdateDBEvent(SpawnEvent &e); + bool LoadDBEvent(uint32 event_id, SpawnEvent &e, string &zone_name); + void UpdateDBCondition(const char* zone_name, uint32 instance_id, uint16 cond_id, int16 value); + void FindNearestEvent(); + + Timer minute_timer; + TimeOfDay_Struct next_event; +}; + +#endif diff --git a/zone/spawngroup.cpp b/zone/spawngroup.cpp new file mode 100644 index 000000000..e6adf29b5 --- /dev/null +++ b/zone/spawngroup.cpp @@ -0,0 +1,248 @@ +/* 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 "../common/debug.h" +#include "spawngroup.h" +#include "entity.h" +#include +#include +#include +using namespace std; +#include "../common/types.h" +#include "zonedb.h" +#include "../common/MiscFunctions.h" + +extern EntityList entity_list; + +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; +} + +SpawnGroup::SpawnGroup( uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in ) { + id = in_id; + strn0cpy( name_, name, 120); + group_spawn_limit = in_group_spawn_limit; + roambox[0]=maxx; + roambox[1]=minx; + roambox[2]=maxy; + roambox[3]=miny; + roamdist=dist; + delay=delay_in; + despawn=despawn_in; + despawn_timer=despawn_timer_in; +} + +uint32 SpawnGroup::GetNPCType() { +#if EQDEBUG >= 10 + LogFile->write(EQEMuLog::Debug, "SpawnGroup[%08x]::GetNPCType()", (uint32) this); +#endif + 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); + + list::iterator cur,end; + list possible; + cur = list_.begin(); + end = list_.end(); + 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; + + totalchance += se->chance; + possible.push_back(se); + } + if(totalchance == 0) + return 0; + + + int32 roll = 0; + roll = MakeRandomInt(0, totalchance-1); + + cur = possible.begin(); + end = possible.end(); + for(; cur != end; cur++) { + SpawnEntry *se = *cur; + if (roll < se->chance) { + npcType = se->NPCType; + break; + } else { + roll -= se->chance; + } + } + //CODER implement random table + return npcType; +} + +void SpawnGroup::AddSpawnEntry( SpawnEntry* newEntry ) { + list_.push_back( newEntry ); +} + +SpawnGroup::~SpawnGroup() { + list::iterator cur,end; + cur = list_.begin(); + end = list_.end(); + for(; cur != end; cur++) { + SpawnEntry* tmp = *cur; + safe_delete(tmp); + } + list_.clear(); +} + +SpawnGroupList::~SpawnGroupList() { + map::iterator cur,end; + cur = groups.begin(); + end = groups.end(); + for(; cur != end; cur++) { + SpawnGroup* tmp = cur->second; + safe_delete(tmp); + } + groups.clear(); +} + +void SpawnGroupList::AddSpawnGroup(SpawnGroup* newGroup) { + if(newGroup == NULL) + return; + groups[newGroup->id] = newGroup; +} + +SpawnGroup* SpawnGroupList::GetSpawnGroup(uint32 in_id) { + if(groups.count(in_id) != 1) + return(false); + return(groups[in_id]); +} + +bool SpawnGroupList::RemoveSpawnGroup(uint32 in_id) { + if(groups.count(in_id) != 1) + return(false); + + groups.erase(in_id); + return(true); +} + +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 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])); + spawn_group_list->AddSpawnGroup(newSpawnGroup); + } + mysql_free_result(result); + } + else + { + cerr << "Error2 in PopulateZoneLists query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + 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 + cout << "Error in SpawngroupID: " << row[0] << endl; + } + mysql_free_result(result); + } + else + { + cerr << "Error3 in PopulateZoneLists query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + // 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 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])); + spawn_group_list->AddSpawnGroup(newSpawnGroup); + } + mysql_free_result(result); + } + else + { + cerr << "Error2 in PopulateZoneLists query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + 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 + cout << "Error in SpawngroupID: " << row[0] << endl; + } + mysql_free_result(result); + } + else + { + cerr << "Error3 in PopulateZoneLists query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + // CODER end new spawn code + return true; +} diff --git a/zone/spawngroup.h b/zone/spawngroup.h new file mode 100644 index 000000000..6c36c0b11 --- /dev/null +++ b/zone/spawngroup.h @@ -0,0 +1,73 @@ +/* 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 SPAWNGROUP_H +#define SPAWNGROUP_H + +#include "../common/linked_list.h" +#include "../common/types.h" + +#include +#include +using namespace std; + +class SpawnEntry +{ +public: + SpawnEntry(uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_limit ); + ~SpawnEntry() { } + uint32 NPCType; + int chance; + + //this is a cached value from npc_types, for speed + uint8 npc_spawn_limit; //max # of this entry which can be spawned in this zone +}; + +class SpawnGroup +{ +public: + SpawnGroup(uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in ); + ~SpawnGroup(); + uint32 GetNPCType(); + void AddSpawnEntry( SpawnEntry* newEntry ); + uint32 id; + float roamdist; + float roambox[4]; + int delay; + int despawn; + uint32 despawn_timer; +private: + char name_[120]; + list list_; + uint8 group_spawn_limit; //max # of this entry which can be spawned by this group +}; + +class SpawnGroupList +{ +public: + SpawnGroupList() { } + ~SpawnGroupList(); + + void AddSpawnGroup(SpawnGroup* newGroup); + SpawnGroup* GetSpawnGroup(uint32 id); + bool RemoveSpawnGroup(uint32 in_id); +private: +// LinkedList list_; + map groups; +}; + +#endif diff --git a/zone/spdat.cpp b/zone/spdat.cpp new file mode 100644 index 000000000..d3a9997ca --- /dev/null +++ b/zone/spdat.cpp @@ -0,0 +1,1094 @@ +/* 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 +*/ + + +/* + + solar: General outline of spell casting process + + 1. + a) Client clicks a spell bar gem, ability, or item. client_process.cpp + gets the op and calls CastSpell() with all the relevant info including + cast time. + + b) NPC does CastSpell() from AI + + 2. + a) CastSpell() determines there is a cast time and sets some state keeping + flags to be used to check the progress of casting and finish it later. + + b) CastSpell() sees there's no cast time, and calls CastedSpellFinished() + Go to step 4. + + 3. + SpellProcess() notices that the spell casting timer which was set by + CastSpell() is expired, and calls CastedSpellFinished() + + 4. + CastedSpellFinished() checks some timed spell specific things, like + wether to interrupt or not, due to movement or melee. If successful + SpellFinished() is called. + + 5. + SpellFinished() checks some things like LoS, reagents, target and + figures out what's going to get hit by this spell based on its type. + + 6. + a) Single target spell, SpellOnTarget() is called. + + b) AE spell, Entity::AESpell() is called. + + c) Group spell, Group::CastGroupSpell()/SpellOnTarget() is called as + needed. + + 7. + SpellOnTarget() may or may not call SpellEffect() to cause effects to + the target + + 8. + If this was timed, CastedSpellFinished() will restore the client's + spell bar gems. + + + Most user code should call CastSpell(), with a 0 casting time if needed, + and not SpellFinished(). + +*/ + + + +#include "../common/debug.h" +#include "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 +#ifndef WIN32 +#include +#include "../common/unix.h" +#endif +#if !defined(NEW_LoadSPDat) && !defined(DB_LoadSPDat) + extern SPDat_Spell_Struct spells[SPDAT_RECORDS]; +#endif +extern bool spells_loaded; + +/////////////////////////////////////////////////////////////////////////////// +// spell property testing functions + +bool IsTargetableAESpell(uint16 spell_id) { + bool bResult = false; + + if (IsValidSpell(spell_id) && spells[spell_id].targettype == ST_AETarget) { + bResult = true; + } + + return bResult; +} + +bool IsSacrificeSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_Sacrifice); +} + +bool IsLifetapSpell(uint16 spell_id) +{ + return + ( + IsValidSpell(spell_id) && + ( + spells[spell_id].targettype == ST_Tap || + ( + spell_id == 2115 // Ancient: Lifebane + ) + ) + ); +} + +bool IsMezSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_Mez); +} + +bool IsStunSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_Stun); +} + +bool IsSummonSpell(uint16 spellid) { + for (int o = 0; o < EFFECT_COUNT; o++) + { + uint32 tid = spells[spellid].effectid[o]; + if(tid == SE_SummonPet || tid == SE_SummonItem || tid == SE_SummonPC) + { + return true; + } + } + return false; +} + +bool IsEvacSpell(uint16 spellid) { + return IsEffectInSpell(spellid, SE_Succor); +} + +bool IsDamageSpell(uint16 spellid) { + for (int o = 0; o < EFFECT_COUNT; o++) + { + uint32 tid = spells[spellid].effectid[o]; + if((tid == SE_CurrentHPOnce || tid == SE_CurrentHP) && spells[spellid].targettype != ST_Tap && spells[spellid].buffduration < 1 && spells[spellid].base[o] < 0) + { + return true; + } + } + return false; +} + + +bool IsFearSpell(uint16 spell_id) { + return IsEffectInSpell(spell_id, SE_Fear); +} + +bool IsSlowSpell(uint16 spell_id) +{ + int i; + const SPDat_Spell_Struct &sp = spells[spell_id]; + + for(i = 0; i < EFFECT_COUNT; i++) + { + if ((sp.effectid[i] == SE_AttackSpeed && sp.base[i] < 100) || (sp.effectid[i] == SE_AttackSpeed4)) + return true; + } + + return false; +} + +bool IsHasteSpell(uint16 spell_id) +{ + int i; + const SPDat_Spell_Struct &sp = spells[spell_id]; + + for(i = 0; i < EFFECT_COUNT; i++) + { + if(sp.effectid[i] == SE_AttackSpeed) + return(sp.base[i] < 100); + } + + return false; +} + +bool IsHarmonySpell(uint16 spell_id) +{ + // IsEffectInSpell(spell_id, SE_Lull) - Lull is not calculated anywhere atm + return (IsEffectInSpell(spell_id, SE_Harmony) || IsEffectInSpell(spell_id, SE_ChangeFrenzyRad)); +} + +bool IsPercentalHealSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_PercentalHeal); +} + +bool IsGroupOnlySpell(uint16 spell_id) +{ + return IsValidSpell(spell_id) && spells[spell_id].goodEffect == 2; +} + +bool IsBeneficialSpell(uint16 spell_id) +{ + if(!IsValidSpell(spell_id)) + return false; + + if(spells[spell_id].goodEffect == 1){ + SpellTargetType tt = spells[spell_id].targettype; + if(tt != ST_Self && tt != ST_Pet){ + if(IsEffectInSpell(spell_id, SE_CancelMagic)) + return false; + } + if(tt == ST_Target || tt == ST_AETarget || tt == ST_Animal || tt == ST_Undead || tt == ST_Pet) { + uint16 sai = spells[spell_id].SpellAffectIndex; + if(spells[spell_id].resisttype == RESIST_MAGIC){ + if(sai == SAI_Calm || sai == SAI_Dispell_Sight || sai == SAI_Memory_Blur || sai == SAI_Calm_Song) + return false; + }else{ + // Bind Sight and Cast Sight + if(sai == SAI_Dispell_Sight && spells[spell_id].skill == 18 && !IsEffectInSpell(spell_id, SE_VoiceGraft)) + return false; + } + } + } + return spells[spell_id].goodEffect != 0 || IsGroupSpell(spell_id); +} + +bool IsDetrimentalSpell(uint16 spell_id) +{ + return !IsBeneficialSpell(spell_id); +} + +bool IsInvulnerabilitySpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_DivineAura); +} + +bool IsCHDurationSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_CompleteHeal); +} + +bool IsPoisonCounterSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_PoisonCounter); +} + +bool IsDiseaseCounterSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_DiseaseCounter); +} + +bool IsSummonItemSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_SummonItem); +} + +bool IsSummonSkeletonSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_NecPet); +} + +bool IsSummonPetSpell(uint16 spell_id) +{ + return + ( + IsEffectInSpell(spell_id, SE_SummonPet) || + IsEffectInSpell(spell_id, SE_SummonBSTPet) + ); +} + +bool IsSummonPCSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_SummonPC); +} + +bool IsCharmSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_Charm); +} + +bool IsBlindSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_Blind); +} + +bool IsEffectHitpointsSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_CurrentHP); +} + +bool IsReduceCastTimeSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_IncreaseSpellHaste); +} + +bool IsIncreaseDurationSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_IncreaseSpellDuration); +} + +bool IsReduceManaSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_ReduceManaCost); +} + +bool IsExtRangeSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_IncreaseRange); +} + +bool IsImprovedHealingSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_ImprovedHeal); +} + +bool IsImprovedDamageSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_ImprovedDamage); +} + +bool IsAEDurationSpell(uint16 spell_id) +{ + return IsValidSpell(spell_id) && + (spells[spell_id].targettype == ST_AETarget || spells[spell_id].targettype == ST_UndeadAE ) + && spells[spell_id].AEDuration !=0; +} + +bool IsPureNukeSpell(uint16 spell_id) +{ + int i, effect_count = 0; + + if(!IsValidSpell(spell_id)) + return false; + + for(i = 0; i < EFFECT_COUNT; i++) + { + if(!IsBlankSpellEffect(spell_id, i)) + effect_count++; + } + + return + ( + effect_count == 1 && IsEffectInSpell(spell_id, SE_CurrentHP) && + spells[spell_id].buffduration == 0 && IsDamageSpell(spell_id) + ); +} + +bool IsPartialCapableSpell(uint16 spell_id) +{ + if(IsPureNukeSpell(spell_id) || + IsFearSpell(spell_id) || + IsEffectInSpell(spell_id, SE_Root) || + IsEffectInSpell(spell_id, SE_Charm)) + { + return true; + } + + return false; +} + +bool IsResistableSpell(uint16 spell_id) +{ + // solar: for now only detrimental spells are resistable. later on i will + // add specific exceptions for the beneficial spells that are resistable + if(IsDetrimentalSpell(spell_id)) + { + return true; + } + + return false; +} + +// solar: checks if this spell affects your group +bool IsGroupSpell(uint16 spell_id) +{ + return + ( + IsValidSpell(spell_id) && + ( + spells[spell_id].targettype == ST_AEBard || + spells[spell_id].targettype == ST_Group || + spells[spell_id].targettype == ST_GroupTeleport + ) + ); +} + +// solar: checks if this spell can be targeted +bool IsTGBCompatibleSpell(uint16 spell_id) +{ + return + ( + IsValidSpell(spell_id) && + ( + !IsDetrimentalSpell(spell_id) && + spells[spell_id].buffduration != 0 && + !IsBardSong(spell_id) && + !IsEffectInSpell(spell_id, SE_Illusion) + ) + ); +} + +bool IsBardSong(uint16 spell_id) +{ + return + ( + IsValidSpell(spell_id) && + spells[spell_id].classes[BARD - 1] < 255 + ); +} + +bool IsEffectInSpell(uint16 spellid, int effect) +{ + int j; + + if(!IsValidSpell(spellid)) + return false; + + for(j = 0; j < EFFECT_COUNT; j++) + if(spells[spellid].effectid[j] == effect) + return true; + + return false; +} + +// solar: arguments are spell id and the index of the effect to check. +// this is used in loops that process effects inside a spell to skip +// the blanks +bool IsBlankSpellEffect(uint16 spellid, int effect_index) +{ + int effect, base, formula; + + effect = spells[spellid].effectid[effect_index]; + base = spells[spellid].base[effect_index]; + formula = spells[spellid].formula[effect_index]; + + return + ( + effect == SE_Blank || // blank marker + ( // spacer + effect == SE_CHA && + base == 0 && + formula == 100 + ) + || + effect == SE_StackingCommand_Block || // these are only used by stacking code + effect == SE_StackingCommand_Overwrite + ); +} + +// solar: checks some things about a spell id, to see if we can proceed +bool IsValidSpell(uint32 spellid) +{ + return + ( + spells_loaded && + spellid != 0 && + spellid != 1 && + spellid != 0xFFFFFFFF && + spellid < SPDAT_RECORDS && + spells[spellid].player_1[0] + ); +} + +//returns the lowest level of any caster which can use the spell +int GetMinLevel(uint16 spell_id) { + int r; + int min = 255; + const SPDat_Spell_Struct &spell = spells[spell_id]; + for(r = 0; r < PLAYER_CLASS_COUNT; r++) { + if(spell.classes[r] < min) + min = spell.classes[r]; + } + + //if we can't cast the spell return 0 + //just so it wont screw up calculations used in other areas of the code + if(min == 255) + return 0; + else + return(min); +} + +int GetSpellLevel(uint16 spell_id, int classa) { + if(classa >= PLAYER_CLASS_COUNT) { + return 255; + } + + const SPDat_Spell_Struct &spell = spells[spell_id]; + return spell.classes[classa - 1]; +} + +// solar: this will find the first occurance of effect. this is handy +// for spells like mez and charm, but if the effect appears more than once +// in a spell this will just give back the first one. +int GetSpellEffectIndex(uint16 spell_id, int effect) +{ + int i; + + if(!IsValidSpell(spell_id)) + return -1; + + for(i = 0; i < EFFECT_COUNT; i++) + { + if(spells[spell_id].effectid[i] == effect) + return i; + } + + return -1; +} + +// solar: returns the level required to use the spell if that class/level +// can use it, 0 otherwise +// note: this isn't used by anything right now +int CanUseSpell(uint16 spellid, int classa, int level) +{ + int level_to_use; + + if(!IsValidSpell(spellid) || classa >= PLAYER_CLASS_COUNT) + return 0; + + level_to_use = spells[spellid].classes[classa - 1]; + + if + ( + level_to_use && + level_to_use != 255 && + level >= level_to_use + ) + return level_to_use; + + return 0; +} + + + +bool BeneficialSpell(uint16 spell_id) +{ + if (spell_id <= 0 || spell_id >= SPDAT_RECORDS + /*|| spells[spell_id].stacking == 27*/ ) + { + return true; + } + switch (spells[spell_id].goodEffect) + { + case 1: + case 3: + return true; + } + return false; +} + +bool GroupOnlySpell(uint16 spell_id) +{ + switch (spells[spell_id].goodEffect) + { + case 2: + case 3: + return true; + } + switch (spell_id) + { + case 1771: + return true; + } + return false; +} + +int32 CalculatePoisonCounters(uint16 spell_id){ + if(!IsValidSpell(spell_id)) + return 0; + + int32 Counters = 0; + for(int i = 0; i < EFFECT_COUNT; i++) + { + if(spells[spell_id].effectid[i] == SE_PoisonCounter && spells[spell_id].base[i] > 0){ + Counters += spells[spell_id].base[i]; + } + } + return Counters; +} + +int32 CalculateDiseaseCounters(uint16 spell_id){ + if(!IsValidSpell(spell_id)) + return 0; + + int32 Counters = 0; + for(int i = 0; i < EFFECT_COUNT; i++) + { + if(spells[spell_id].effectid[i] == SE_DiseaseCounter && spells[spell_id].base[i] > 0){ + Counters += spells[spell_id].base[i]; + } + } + return Counters; +} + +int32 CalculateCurseCounters(uint16 spell_id){ + if(!IsValidSpell(spell_id)) + return 0; + + int32 Counters = 0; + for(int i = 0; i < EFFECT_COUNT; i++) + { + if(spells[spell_id].effectid[i] == SE_CurseCounter && spells[spell_id].base[i] > 0){ + Counters += spells[spell_id].base[i]; + } + } + return Counters; +} + +int32 CalculateCorruptionCounters(uint16 spell_id){ + if(!IsValidSpell(spell_id)) + return 0; + + int32 Counters = 0; + for(int i = 0; i < EFFECT_COUNT; i++) + { + if(spells[spell_id].effectid[i] == SE_CorruptionCounter && spells[spell_id].base[i] > 0){ + Counters += spells[spell_id].base[i]; + } + } + return Counters; +} + +int32 CalculateCounters(uint16 spell_id) { + int32 counter = CalculatePoisonCounters(spell_id); + if(counter != 0) { + return counter; + } + + counter = CalculateDiseaseCounters(spell_id); + if(counter != 0) { + return counter; + } + + counter = CalculateCurseCounters(spell_id); + if(counter != 0) { + return counter; + } + + counter = CalculateCorruptionCounters(spell_id); + return counter; +} + +bool IsDisciplineBuff(uint16 spell_id) +{ + if(!IsValidSpell(spell_id)) + return false; + + if(spells[spell_id].mana == 0 + && spells[spell_id].short_buff_box == 0 + && (spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep) + && spells[spell_id].targettype == ST_Self) + { + return true; + } + return false; +} + +bool IsDiscipline(uint16 spell_id) +{ + if(!IsValidSpell(spell_id)) + return false; + + if(spells[spell_id].mana == 0 && (spells[spell_id].EndurCost || spells[spell_id].EndurUpkeep)) + { + return true; + } + return false; +} + +bool IsResurrectionEffects(uint16 spell_id) { + bool Result = false; + + if(IsValidSpell(spell_id) && spell_id == 756) // spell id 756 is Resurrection Effects spell + Result = true; + + return Result; +} + +bool IsRuneSpell(uint16 spell_id) { + bool Result = false; + + if(IsValidSpell(spell_id)) { + for(int i = 0; i < EFFECT_COUNT; i++) { + if(spells[spell_id].effectid[i] == SE_Rune) { + Result = true; + break; + } + } + } + + return Result; +} + +bool IsMagicRuneSpell(uint16 spell_id) { + bool Result = false; + + if(IsValidSpell(spell_id)) { + for(int i = 0; i < EFFECT_COUNT; i++) { + if(spells[spell_id].effectid[i] == SE_AbsorbMagicAtt) { + Result = true; + break; + } + } + } + + return Result; +} + +bool IsManaTapSpell(uint16 spell_id) { + bool Result = false; + + if(IsValidSpell(spell_id)) { + for(int i = 0; i < EFFECT_COUNT; i++) { + if(spells[spell_id].effectid[i] == SE_CurrentMana && spells[spell_id].targettype == ST_Tap) { + Result = true; + break; + } + } + } + + return Result; +} + +bool IsAllianceSpellLine(uint16 spell_id) { + bool Result = false; + + if(IsValidSpell(spell_id)) + Result = IsEffectInSpell(spell_id, SE_AddFaction); + + return Result; +} + +bool IsDeathSaveSpell(uint16 spell_id) { + bool Result = false; + + if(IsValidSpell(spell_id)) + Result = IsEffectInSpell(spell_id, SE_DeathSave); + + return Result; +} + +bool IsPartialDeathSaveSpell(uint16 spell_id) { + bool Result = false; + + // Death Pact + if(spell_id == 1547) + Result = true; + + return Result; +} + +bool IsFullDeathSaveSpell(uint16 spell_id) { + bool Result = false; + + // Divine Intervention + if(spell_id == 1546) + Result = true; + + return Result; +} + +bool IsShadowStepSpell(uint16 spell_id) { + if (IsEffectInSpell(spell_id, SE_ShadowStep)){ + return true; + + } + else { + return false; + } +} + +bool IsSuccorSpell(uint16 spell_id) { + if (IsEffectInSpell(spell_id, SE_Succor)){ + return true; + } + else { + return false; + } +} + +bool IsTeleportSpell(uint16 spell_id) { + if (IsEffectInSpell(spell_id, SE_Teleport)){ + return true; + } + else { + return false; + } +} + +bool IsGateSpell(uint16 spell_id) { + if (IsEffectInSpell(spell_id, SE_Gate)){ + return true; + } + else { + return false; + } +} +// seveian 2008-09-23 +bool IsPlayerIllusionSpell(uint16 spell_id) { + if(IsEffectInSpell(spell_id, SE_Illusion) && spells[spell_id].targettype == ST_Self) + return true; + else + return false; + +} + +int GetSpellEffectDescNum(uint16 spell_id) +{ + if( (spell_id > 0) && (spell_id < SPDAT_RECORDS) ){ + return spells[spell_id].effectdescnum; + } else { + return -1; + } +} + +DmgShieldType GetDamageShieldType(uint16 spell_id) +{ + + // If we have a DamageShieldType for this spell from the damageshieldtypes table, return that, + // else, make a guess, based on the resist type. Default return value is DS_THORNS + // + if( (spell_id > 0) && (spell_id < SPDAT_RECORDS) ){ + + _log(SPELLS__EFFECT_VALUES, "DamageShieldType for spell %i (%s) is %X\n", spell_id, + spells[spell_id].name, spells[spell_id].DamageShieldType); + + if(spells[spell_id].DamageShieldType) + return (DmgShieldType) spells[spell_id].DamageShieldType; + + switch(spells[spell_id].resisttype) { + case RESIST_COLD: + return DS_TORMENT; + case RESIST_FIRE: + return DS_BURN; + case RESIST_DISEASE: + return DS_DECAY; + default: + return DS_THORNS; + } + } + + return DS_THORNS; +} + +bool IsLDoNObjectSpell(uint16 spell_id) +{ + if(IsEffectInSpell(spell_id, SE_AppraiseLDonChest) || + IsEffectInSpell(spell_id, SE_DisarmLDoNTrap) || + IsEffectInSpell(spell_id, SE_UnlockLDoNChest)) + { + return true; + } + else + { + return false; + } + +} + +int32 GetSpellResistType(uint16 spell_id) +{ + return spells[spell_id].resisttype; +} + +int32 GetSpellTargetType(uint16 spell_id) +{ + return (int32)spells[spell_id].targettype; +} + +bool IsHealOverTimeSpell(uint16 spell_id) { + if(IsEffectInSpell(spell_id, SE_HealOverTime) && !IsGroupSpell(spell_id)) + return true; + else + return false; +} + +bool IsCompleteHealSpell(uint16 spell_id) { + + if(spell_id == 13 || IsEffectInSpell(spell_id, SE_CompleteHeal) || IsPercentalHealSpell(spell_id) && !IsGroupSpell(spell_id)) + return true; + else + return false; +} + +bool IsFastHealSpell(uint16 spell_id) { + const int MaxFastHealCastingTime = 2000; + + if(spells[spell_id].cast_time <= MaxFastHealCastingTime && spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 && !IsGroupSpell(spell_id)) + return true; + else + return false; +} + +bool IsVeryFastHealSpell(uint16 spell_id) { + const int MaxFastHealCastingTime = 1000; + + if(spells[spell_id].cast_time <= MaxFastHealCastingTime && spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 && !IsGroupSpell(spell_id)) + return true; + else + return false; +} + +bool IsRegularSingleTargetHealSpell(uint16 spell_id) { + bool result = false; + + if(spells[spell_id].effectid[0] == 0 && spells[spell_id].base[0] > 0 && spells[spell_id].targettype == ST_Target && spells[spell_id].buffduration == 0 + && !IsFastHealSpell(spell_id) && !IsCompleteHealSpell(spell_id) && !IsHealOverTimeSpell(spell_id) && !IsGroupSpell(spell_id)) { + result = true; + } + + return result; +} + +bool IsRegularGroupHealSpell(uint16 spell_id) { + + if(IsGroupSpell(spell_id) && !IsCompleteHealSpell(spell_id) && !IsHealOverTimeSpell(spell_id)) + return true; + else + return false; +} + +bool IsGroupCompleteHealSpell(uint16 spell_id) { + + if(IsGroupSpell(spell_id) && IsCompleteHealSpell(spell_id)) + return true; + else + return false; +} + +bool IsGroupHealOverTimeSpell(uint16 spell_id) { + + if(IsGroupSpell(spell_id) && IsHealOverTimeSpell(spell_id) && spells[spell_id].buffduration < 10) + return true; + else + return false; +} + +bool IsDebuffSpell(uint16 spell_id) { + + if(IsBeneficialSpell(spell_id) || IsEffectHitpointsSpell(spell_id) || IsStunSpell(spell_id) || IsMezSpell(spell_id) + || IsCharmSpell(spell_id) || IsSlowSpell(spell_id) || IsEffectInSpell(spell_id, SE_Root) || IsEffectInSpell(spell_id, SE_CancelMagic) + || IsEffectInSpell(spell_id, SE_MovementSpeed) || IsFearSpell(spell_id) || IsEffectInSpell(spell_id, SE_Calm)) + return false; + else + return true; +} + +bool IsResistDebuffSpell(uint16 spell_id) { + + if((IsEffectInSpell(spell_id, SE_ResistFire) || IsEffectInSpell(spell_id, SE_ResistCold) || IsEffectInSpell(spell_id, SE_ResistPoison) + || IsEffectInSpell(spell_id, SE_ResistDisease) || IsEffectInSpell(spell_id, SE_ResistMagic) || IsEffectInSpell(spell_id, SE_ResistAll) + || IsEffectInSpell(spell_id, SE_ResistCorruption)) && !IsBeneficialSpell(spell_id)) + return true; + else + return false; +} + +bool IsSelfConversionSpell(uint16 spell_id) { + + if(GetSpellTargetType(spell_id) == ST_Self && IsEffectInSpell(spell_id, SE_CurrentMana) && IsEffectInSpell(spell_id, SE_CurrentHP) + && spells[spell_id].base[GetSpellEffectIndex(spell_id, SE_CurrentMana)] > 0 && spells[spell_id].base[GetSpellEffectIndex(spell_id, SE_CurrentHP)] < 0) + return true; + else + return false; +} + +uint32 GetMorphTrigger(uint32 spell_id) +{ + for(int i = 0; i < EFFECT_COUNT; ++i) + { + if(spells[spell_id].effectid[i] == SE_ImprovedSpellEffect) + { + return spells[spell_id].base[i]; + } + } + return 0; +} + +uint32 GetPartialMeleeRuneReduction(uint32 spell_id) +{ + for(int i = 0; i < EFFECT_COUNT; ++i) + { + if(spells[spell_id].effectid[i] == SE_MitigateMeleeDamage) + { + return spells[spell_id].base[i]; + } + } + return 0; +} + +uint32 GetPartialMagicRuneReduction(uint32 spell_id) +{ + for(int i = 0; i < EFFECT_COUNT; ++i) + { + if(spells[spell_id].effectid[i] == SE_MitigateSpellDamage) + { + return spells[spell_id].base[i]; + } + } + return 0; +} + +uint32 GetPartialMeleeRuneAmount(uint32 spell_id) +{ + for(int i = 0; i < EFFECT_COUNT; ++i) + { + if(spells[spell_id].effectid[i] == SE_MitigateMeleeDamage) + { + return spells[spell_id].max[i]; + } + } + return 0; +} + +uint32 GetPartialMagicRuneAmount(uint32 spell_id) +{ + for(int i = 0; i < EFFECT_COUNT; ++i) + { + if(spells[spell_id].effectid[i] == SE_MitigateSpellDamage) + { + return spells[spell_id].max[i]; + } + } + return 0; +} + + +bool DetrimentalSpellAllowsRest(uint16 spell_id) +{ + if((spell_id > 0) && (spell_id < SPDAT_RECORDS)) + return spells[spell_id].AllowRest; + + return false; +} + +uint32 GetNimbusEffect(uint16 spell_id) +{ + if((spell_id > 0) && (spell_id < SPDAT_RECORDS)) + return (int32)spells[spell_id].NimbusEffect; + + return 0; +} + +int32 GetFuriousBash(uint16 spell_id) +{ + if(!IsValidSpell(spell_id)) + return 0; + + bool found_effect_limit = false; + int32 mod = 0; + + for(int i = 0; i < EFFECT_COUNT; ++i) + { + if(spells[spell_id].effectid[i] == SE_SpellHateMod) + { + mod = spells[spell_id].base[i]; + } + else if(spells[spell_id].effectid[i] == SE_LimitEffect) + { + if(spells[spell_id].base[i] == 999) + { + found_effect_limit = true; + } + } + } + + if(found_effect_limit) + return mod; + else + return 0; +} + +bool IsShortDurationBuff(uint16 spell_id) +{ + if((spell_id > 0) && (spell_id < SPDAT_RECORDS)) { + if(spells[spell_id].short_buff_box != 0) + return true; + } + return false; +} + diff --git a/zone/spdat.h b/zone/spdat.h new file mode 100644 index 000000000..90b01e3d8 --- /dev/null +++ b/zone/spdat.h @@ -0,0 +1,830 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 SPDAT_H +#define SPDAT_H + +#include "../common/classes.h" +#include "../common/skills.h" + +#define SPELL_UNKNOWN 0xFFFF +#define SPELLBOOK_UNKNOWN 0xFFFFFFFF //player profile spells are 32 bit + +//some spell IDs which will prolly change, but are needed +#define SPELL_LEECH_TOUCH 2766 +#define SPELL_LAY_ON_HANDS 87 +#define SPELL_HARM_TOUCH 88 +#define SPELL_HARM_TOUCH2 2821 +#define SPELL_IMP_HARM_TOUCH 2774 +#define SPELL_NPC_HARM_TOUCH 929 + + +//#define SPDAT_SIZE 1824000 +/* + solar: look at your spells_en.txt and find the id of the last spell. + this number has to be 1 more than that. if it's higher, your zone will + NOT start up. gonna autodetect this later.. +*/ +//#define NEW_LoadSPDat +#define DB_LoadSPDat //load from DB vs spells_us.txt. for now, we're piggybacking NEW_LoadSPDat, so it will take precedence + +#define EFFECT_COUNT 12 +#define MAX_SPELL_TRIGGER 12 // One for each slot(only 6 for AA since AA use 2) +#define MAX_RESISTABLE_EFFECTS 12 // Number of effects that are typcially checked agianst resists. + +const int Z_AGGRO=10; + +const int MobAISpellRange=100; // max range of buffs +const int SpellType_Nuke=1; +const int SpellType_Heal=2; +const int SpellType_Root=4; +const int SpellType_Buff=8; +const int SpellType_Escape=16; +const int SpellType_Pet=32; +const int SpellType_Lifetap=64; +const int SpellType_Snare=128; +const int SpellType_DOT=256; +const int SpellType_Dispel=512; +const int SpellType_InCombatBuff=1024; +const int SpellType_Mez=2048; +const int SpellType_Charm=4096; +const int SpellType_Slow = 8192; +const int SpellType_Debuff = 16384; +const int SpellType_Cure = 32768; + +const int SpellTypes_Detrimental = SpellType_Nuke|SpellType_Root|SpellType_Lifetap|SpellType_Snare|SpellType_DOT|SpellType_Dispel|SpellType_Mez|SpellType_Charm|SpellType_Debuff|SpellType_Slow; +const int SpellTypes_Beneficial = SpellType_Heal|SpellType_Buff|SpellType_Escape|SpellType_Pet|SpellType_InCombatBuff|SpellType_Cure; + +#define SpellType_Any 0xFFFF + +enum SpellAffectIndex { + SAI_Calm = 12, // Lull and Alliance Spells + SAI_Dispell_Sight = 14, // Dispells and Spells like Bind Sight + SAI_Memory_Blur = 27, + SAI_Calm_Song = 43 // Lull and Alliance Songs +}; +enum RESISTTYPE +{ + RESIST_NONE = 0, + RESIST_MAGIC = 1, + RESIST_FIRE = 2, + RESIST_COLD = 3, + RESIST_POISON = 4, + RESIST_DISEASE = 5, + RESIST_CHROMATIC = 6, + RESIST_PRISMATIC = 7, + RESIST_PHYSICAL = 8, // see Muscle Shock, Back Swing + RESIST_CORRUPTION = 9 +}; + +//Target Type IDs +typedef enum { +/* 01 */ ST_TargetOptional = 0x01, +/* 02 */ ST_AEClientV1 = 0x02, +/* 03 */ ST_GroupTeleport = 0x03, +/* 04 */ ST_AECaster = 0x04, +/* 05 */ ST_Target = 0x05, +/* 06 */ ST_Self = 0x06, +/* 07 */ // NOT USED +/* 08 */ ST_AETarget = 0x08, +/* 09 */ ST_Animal = 0x09, +/* 10 */ ST_Undead = 0x0a, +/* 11 */ ST_Summoned = 0x0b, +/* 12 */ // NOT USED +/* 13 */ ST_Tap = 0x0d, +/* 14 */ ST_Pet = 0x0e, +/* 15 */ ST_Corpse = 0x0f, +/* 16 */ ST_Plant = 0x10, +/* 17 */ ST_Giant = 0x11, //special giant +/* 18 */ ST_Dragon = 0x12, //special dragon +/* 19 */ // NOT USED +/* 20 */ ST_TargetAETap = 0x14, +/* 21 */ // NOT USED +/* 22 */ // NOT USED +/* 23 */ // NOT USED +/* 24 */ ST_UndeadAE = 0x18, +/* 25 */ ST_SummonedAE = 0x19, +/* 26 */ // NOT USED +/* 27 */ // NOT USED +/* 28 */ // NOT USED +/* 29 */ // NOT USED +/* 30 */ // NOT USED +/* 31 */ // NOT USED +/* 32 */ ST_AECaster2 = 0x20, //ae caster hatelist maybe? +/* 33 */ ST_HateList = 0x21, +/* 34 */ ST_LDoNChest_Cursed = 0x22, +/* 35 */ ST_Muramite = 0x23, //only works on special muramites +/* 36 */ ST_AreaClientOnly = 0x24, +/* 37 */ ST_AreaNPCOnly = 0x25, +/* 38 */ ST_SummonedPet = 0x26, +/* 39 */ ST_GroupNoPets = 0x27, +/* 40 */ ST_AEBard = 0x28, +/* 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) +/* 46 */ ST_TargetsTarget = 0x2e, // uses the target of your target +/* 47 */ //ST_PetMaster = 0x2e, // uses the master as target +} SpellTargetType; + +typedef enum { + DS_DECAY = 244, + DS_CHILLED = 245, + DS_FREEZING = 246, + DS_TORMENT = 247, + DS_BURN = 248, + DS_THORNS = 249 +} DmgShieldType; + +//Spell Effect IDs +#define SE_CurrentHP 0 // implemented - Heals and nukes, repeates every tic if in a buff +#define SE_ArmorClass 1 // implemented +#define SE_ATK 2 // implemented +#define SE_MovementSpeed 3 // implemented - SoW, SoC, etc +#define SE_STR 4 // implemented +#define SE_DEX 5 // implemented +#define SE_AGI 6 // implemented +#define SE_STA 7 // implemented +#define SE_INT 8 // implemented +#define SE_WIS 9 // implemented +#define SE_CHA 10 // implemented - used as a spacer +#define SE_AttackSpeed 11 // implemented +#define SE_Invisibility 12 // implemented +#define SE_SeeInvis 13 // implemented +#define SE_WaterBreathing 14 // implemented +#define SE_CurrentMana 15 // implemented +//#define SE_Unknown16 16 // not used +//#define SE_Unknown17 17 // not used +#define SE_Lull 18 // implemented - Reaction Radius +#define SE_AddFaction 19 // implemented - Alliance line +#define SE_Blind 20 // implemented +#define SE_Stun 21 // implemented +#define SE_Charm 22 // implemented +#define SE_Fear 23 // implemented +#define SE_Stamina 24 // implemented - Invigor and such +#define SE_BindAffinity 25 // implemented +#define SE_Gate 26 // implemented - Gate to bind point +#define SE_CancelMagic 27 // implemented +#define SE_InvisVsUndead 28 // implemented +#define SE_InvisVsAnimals 29 // implemented +#define SE_ChangeFrenzyRad 30 // implemented - Pacify +#define SE_Mez 31 // implemented +#define SE_SummonItem 32 // implemented +#define SE_SummonPet 33 // implemented +//#define SE_Unknown34 34 // not used (Nimbus of Temporal Rifting) ? +#define SE_DiseaseCounter 35 // implemented +#define SE_PoisonCounter 36 // implemented +//#define SE_Unknown37 37 // not used +//#define SE_Unknown38 38 // not used +//#define SE_Unknown39 39 // not used +#define SE_DivineAura 40 // implemented +#define SE_Destroy 41 // implemented - Disintegrate, Banishment of Shadows +#define SE_ShadowStep 42 // implemented +//#define SE_Unknown43 43 // not used +#define SE_Lycanthropy 44 // implemented +//#define SE_Unknown45 45 // not used +#define SE_ResistFire 46 // implemented +#define SE_ResistCold 47 // implemented +#define SE_ResistPoison 48 // implemented +#define SE_ResistDisease 49 // implemented +#define SE_ResistMagic 50 // implemented +//#define SE_Unknown51 51 // not used +#define SE_SenseDead 52 // implemented +#define SE_SenseSummoned 53 // implemented +#define SE_SenseAnimals 54 // implemented +#define SE_Rune 55 // implemented +#define SE_TrueNorth 56 // implemented +#define SE_Levitate 57 // implemented +#define SE_Illusion 58 // implemented +#define SE_DamageShield 59 // implemented +//#define SE_Unknown60 60 // not used +#define SE_Identify 61 // implemented +//#define SE_Unknown62 62 // not used +#define SE_WipeHateList 63 // implemented +#define SE_SpinTarget 64 // implemented +#define SE_InfraVision 65 // implemented +#define SE_UltraVision 66 // implemented +#define SE_EyeOfZomm 67 // implemented +#define SE_ReclaimPet 68 // implemented +#define SE_TotalHP 69 // implemented +//#define SE_Unknown70 70 // not used +#define SE_NecPet 71 // implemented +//#define SE_Unknown72 72 // not used +#define SE_BindSight 73 // implemented +#define SE_FeignDeath 74 // implemented +#define SE_VoiceGraft 75 // implemented +#define SE_Sentinel 76 // *not implemented?(just seems to send a message) +#define SE_LocateCorpse 77 // implemented +#define SE_AbsorbMagicAtt 78 // implemented - Rune for spells +#define SE_CurrentHPOnce 79 // implemented - Heals and nukes, non-repeating if in a buff +//#define SE_Unknown80 80 // not used +#define SE_Revive 81 // implemented - Resurrect +#define SE_SummonPC 82 // implemented +#define SE_Teleport 83 // implemented +#define SE_TossUp 84 // implemented - Gravity Flux +#define SE_WeaponProc 85 // implemented - i.e. Call of Fire +#define SE_Harmony 86 // implemented +#define SE_MagnifyVision 87 // implemented - Telescope +#define SE_Succor 88 // implemented - Evacuate/Succor lines +#define SE_ModelSize 89 // implemented - Shrink, Growth +#define SE_Cloak 90 // *not implemented - Used in only 2 spells +#define SE_SummonCorpse 91 // implemented +#define SE_Calm 92 // implemented - Hate modifier stuff(poorly named) +#define SE_StopRain 93 // implemented - Wake of Karana +#define SE_NegateIfCombat 94 // *not implemented? - Works client side but there is comment todo in spell effects...Component of Spirit of Scale +#define SE_Sacrifice 95 // implemented +#define SE_Silence 96 // implemented +#define SE_ManaPool 97 // implemented +#define SE_AttackSpeed2 98 // implemented - Melody of Ervaj +#define SE_Root 99 // implemented +#define SE_HealOverTime 100 // implemented +#define SE_CompleteHeal 101 // implemented +#define SE_Fearless 102 // implemented - Valiant Companion +#define SE_CallPet 103 // implemented - Summon Companion +#define SE_Translocate 104 // implemented +#define SE_AntiGate 105 // implemented - Translocational Anchor +#define SE_SummonBSTPet 106 // implemented +//#define SE_Unknown107 107 // not used +#define SE_Familiar 108 // implemented +#define SE_SummonItemIntoBag 109 // implemented - summons stuff into container +//#define SE_Unknown110 110 // not used +#define SE_ResistAll 111 // implemented +#define SE_CastingLevel 112 // implemented +#define SE_SummonHorse 113 // implemented +#define SE_ChangeAggro 114 // implemented - Hate modifing buffs(ie horrifying visage) +#define SE_Hunger 115 // implemented - Song of Sustenance +#define SE_CurseCounter 116 // implemented +#define SE_MagicWeapon 117 // implemented - makes weapon magical +#define SE_SingingSkill 118 // *implemented - needs AA conversion +#define SE_AttackSpeed3 119 // implemented +#define SE_HealRate 120 // implemented - reduces healing by a % +#define SE_ReverseDS 121 // implemented +//#define SE_Unknown122 122 // not used +#define SE_Screech 123 // implemented? Spell Blocker(can only have one buff with this effect at one time) +#define SE_ImprovedDamage 124 // implemented +#define SE_ImprovedHeal 125 // implemented +#define SE_SpellResistReduction 126 // implemented +#define SE_IncreaseSpellHaste 127 // implemented +#define SE_IncreaseSpellDuration 128 // implemented +#define SE_IncreaseRange 129 // implemented +#define SE_SpellHateMod 130 // implemented +#define SE_ReduceReagentCost 131 // implemented +#define SE_ReduceManaCost 132 // implemented +//#define SE_Unknown133 133 // not used +#define SE_LimitMaxLevel 134 // implemented +#define SE_LimitResist 135 // implemented +#define SE_LimitTarget 136 // implemented +#define SE_LimitEffect 137 // implemented +#define SE_LimitSpellType 138 // implemented +#define SE_LimitSpell 139 // implemented +#define SE_LimitMinDur 140 // implemented +#define SE_LimitInstant 141 // implemented +#define SE_LimitMinLevel 142 // implemented +#define SE_LimitCastTime 143 // implemented +//#define SE_Unknown144 144 // not used +#define SE_Teleport2 145 // implemented - Banishment of the Pantheon +//#define SE_Unknown146 146 // not used (Lightning Rod) Electrical Resist? (exp. VoA) +#define SE_PercentalHeal 147 // implemented +#define SE_StackingCommand_Block 148 // implemented? +#define SE_StackingCommand_Overwrite 149 // implemented? +#define SE_DeathSave 150 // implemented +#define SE_SuspendPet 151 // *not implemented as bonus +#define SE_TemporaryPets 152 // implemented +#define SE_BalanceHP 153 // implemented +#define SE_DispelDetrimental 154 // implemented +#define SE_SpellCritDmgIncrease 155 // implemented +#define SE_IllusionCopy 156 // implemented - Deception +#define SE_SpellDamageShield 157 // implemented - Petrad's Protection +#define SE_Reflect 158 // implemented +#define SE_AllStats 159 // implemented +#define SE_MakeDrunk 160 // implemented - poorly though, should check against tolerance +#define SE_MitigateSpellDamage 161 // implemented - rune with max value +#define SE_MitigateMeleeDamage 162 // implemented - rune with max value +#define SE_NegateAttacks 163 // implemented +#define SE_AppraiseLDonChest 164 // implemented +#define SE_DisarmLDoNTrap 165 // implemented +#define SE_UnlockLDoNChest 166 // implemented +#define SE_PetPowerIncrease 167 // implemented +#define SE_MeleeMitigation 168 // implemented +#define SE_CriticalHitChance 169 // implemented +#define SE_SpellCritChance 170 // implemented +#define SE_CrippBlowChance 171 // implemented +#define SE_AvoidMeleeChance 172 // implemented +#define SE_RiposteChance 173 // implemented +#define SE_DodgeChance 174 // implemented +#define SE_ParryChance 175 // implemented +#define SE_DualWieldChance 176 // implemented +#define SE_DoubleAttackChance 177 // implemented +#define SE_MeleeLifetap 178 // implemented +#define SE_AllInstrumentMod 179 // implemented +#define SE_ResistSpellChance 180 // implemented +#define SE_ResistFearChance 181 // implemented +#define SE_HundredHands 182 // implemented +#define SE_MeleeSkillCheck 183 // implemented +#define SE_HitChance 184 // implemented +#define SE_DamageModifier 185 // implemented +#define SE_MinDamageModifier 186 // implemented +#define SE_BalanceMana 187 // implemented - Balances party mana +#define SE_IncreaseBlockChance 188 // implemented +#define SE_CurrentEndurance 189 // implemented +#define SE_EndurancePool 190 // implemented +#define SE_Amnesia 191 // implemented - Silence vs Melee Effect +#define SE_Hate2 192 // implemented +#define SE_SkillAttack 193 // implemented +#define SE_FadingMemories 194 // implemented +#define SE_StunResist 195 // implemented +#define SE_Strikethrough 196 // implemented +#define SE_SkillDamageTaken 197 // implemented +#define SE_CurrentEnduranceOnce 198 // implemented +#define SE_Taunt 199 // implemented - % chance to taunt the target +#define SE_ProcChance 200 // implemented +#define SE_RangedProc 201 // implemented +#define SE_IllusionOther 202 // *not implemented as bonus(Project Illusion) +#define SE_MassGroupBuff 203 // *not implemented as bonus +#define SE_GroupFearImmunity 204 // *not implemented as bonus +#define SE_Rampage 205 // implemented +#define SE_AETaunt 206 // implemented +#define SE_FleshToBone 207 // implemented +//#define SE_Unknown208 208 // not used +#define SE_DispelBeneficial 209 // implemented +#define SE_PetShield 210 // *not implemented +#define SE_AEMelee 211 // implemented +#define SE_CastingSkills 212 // *not implemented -Include/Exclude Casting Skill type. (*no longer used on live) +#define SE_PetMaxHP 213 // implemented[AA] - increases the maximum hit points of your pet +#define SE_MaxHPChange 214 // implemented +#define SE_PetAvoidance 215 // implemented[AA] - increases pet ability to avoid melee damage +#define SE_Accuracy 216 // implemented +#define SE_HeadShot 217 // not implemented as bonus - ability to head shot (base2 = damage) +#define SE_PetCriticalHit 218 // implemented[AA] - gives pets a baseline critical hit chance +#define SE_SlayUndead 219 // implemented - Allow extra damage against undead (base1 = rate, base2 = damage mod). +#define SE_SkillDamageAmount 220 // implemented +#define SE_Packrat 221 // not implemented as bonus +#define SE_BlockBehind 222 // implemented - Chance to block from behind (with our without Shield) +//#define SE_Unknown223 223 // not used +#define SE_GiveDoubleRiposte 224 // implemented[AA] +#define SE_GiveDoubleAttack 225 // implemented[AA] - Allow any class to double attack with set chance. +#define SE_TwoHandBash 226 // not implemented as bonus +#define SE_ReduceSkillTimer 227 // implemented +#define SE_ReduceFallDamage 228 // not implented as bonus - reduce the damage that you take from falling +#define SE_PersistantCasting 229 // implemented +#define SE_ExtendedShielding 230 // not used as bonus - increase range of /shield ability +//#define SE_Unknown231 231 // not used *Unknown limit used in AA Overpowering Strikes (Decrease chance stun resist) +#define SE_DivineSave 232 // implemented (base1 == % chance on death to insta-res) (base2 == spell cast on save) +#define SE_Metabolism 233 // *not implemented - (Crown of Feathers) Increase metabolism? +#define SE_ReduceApplyPoisonTime 234 // not implemented as bonus - reduces the time to apply poison +#define SE_ChannelChanceSpells 235 // implemented[AA] - chance to channel from SPELLS *No longer used on live. +//#define SE_Unknown236 236 // not used +#define SE_GivePetGroupTarget 237 // implemented[AA] - (Pet Affinity) +#define SE_IllusionPersistence 238 // *not implemented - lends persistence to your illusionary disguises, causing them to last until you die or the illusion is forcibly removed. +#define SE_FeignedCastOnChance 239 // *not implemented as bonus - ability gives you an increasing chance for your feigned deaths to not be revealed by spells cast upon you. +//#define SE_Unknown240 240 // not used [Likely related to above - you become immune to feign breaking on a resisted spell and have a good chance of feigning through a spell that successfully lands upon you.] +#define SE_ImprovedReclaimEnergy 241 // not implemented as bonus - increase the amount of mana returned to you when reclaiming your pet. +#define SE_ChanceWipeHateList 242 // *not implemented - increases the chance to wipe hate with memory blurr +#define SE_CharmBreakChance 243 // implemented - Total Domination +#define SE_RootBreakChance 244 // implemented[AA] reduce the chance that your root will break. +#define SE_TrapCircumvention 245 // *not implemented[AA] - decreases the chance that you will set off a trap when opening a chest +#define SE_SetBreathLevel 246 // not implemented as bonus +#define SE_RaiseSkillCap 247 // *not implemented[AA] - adds skill over the skill cap. +#define SE_SecondaryForte 248 // not implemented as bonus(gives you a 2nd specialize skill that can go past 50 to 100) +#define SE_SecondaryDmgInc 249 // implemented[AA] Allows off hand weapon to recieve a damage bonus (Sinister Strikes) +#define SE_SpellProcChance 250 // implemented - Increase chance to sympathetic proc by % +#define SE_ConsumeProjectile 251 // implemented[AA] - chance to not consume an arrow (ConsumeProjectile = 100) +#define SE_FrontalBackstabChance 252 // implemented[AA] - chance to perform a full damage backstab from front. +#define SE_FrontalBackstabMinDmg 253 // implemented[AA] - allow a frontal backstab for mininum damage. +#define SE_Blank 254 // implemented +#define SE_ShieldDuration 255 // not implemented as bonus - increases duration of /shield +#define SE_ShroudofStealth 256 // not implemented as bonus - rogue improved invs +#define SE_PetDiscipline 257 // not implemented as bonus - /pet hold +#define SE_TripleBackstab 258 // implemented[AA] - chance to perform a triple backstab +#define SE_CombatStability 259 // implemented[AA] - damage mitigation +#define SE_AddSingingMod 260 // *not implemented +//#define SE_Unknown261 261 // not used +#define SE_RaiseStatCap 262 // implemented +#define SE_TradeSkillMastery 263 // implemented - lets you raise more than one tradeskill above master. +#define SE_HastenedAASkill 264 // not implemented as bonus - Use redux field in aa_actions table for this effect +#define SE_MasteryofPast 265 // implemented[AA] - Spells less than effect values level can not be fizzled +#define SE_ExtraAttackChance 266 // implemented - increase chance to score an extra attack with a 2-Handed Weapon. +#define SE_PetDiscipline2 267 // *not implemented - /pet focus, /pet no cast +#define SE_ReduceTradeskillFail 268 // *not implemented? - reduces chance to fail with given tradeskill by a percent chance +#define SE_MaxBindWound 269 // implemented[AA] - Increase max HP you can bind wound. +#define SE_BardSongRange 270 // implemented[AA] - increase range of beneficial bard songs (Sionachie's Crescendo) +#define SE_BaseMovementSpeed 271 // implemented[AA] - mods basemove speed, doesn't stack with other move mods +#define SE_CastingLevel2 272 // implemented +#define SE_CriticalDoTChance 273 // implemented +#define SE_CriticalHealChance 274 // implemented +//#define SE_Unknown275 275 // not used +#define SE_Ambidexterity 276 // implemented[AA] - increase chance to duel weild by adding bonus 'skill' +#define SE_UnfailingDivinity 277 // implemented[AA] - ability grants your Death Pact-type spells a second chance to successfully heal their target, also can cause said spells to do a portion of their healing value even on a complete failure. +#define SE_FinishingBlow 278 // implemented[AA] - chance to do massive damage under 10% HP (base1 = chance, base2 = damage) +#define SE_Flurry 279 // implemented +#define SE_PetFlurry 280 // implemented[AA] +#define SE_FeignedMinion 281 // *not implemented[AA] ability allows you to instruct your pet to feign death via the '/pet feign' command. value = succeed chance +#define SE_ImprovedBindWound 282 // implemented[AA] - increase bind wound amount by percent. +#define SE_DoubleSpecialAttack 283 // implemented[AA] - Chance to perform second special attack as monk +//#define SE_Unknown284 284 // not used +#define SE_NimbleEvasion 285 // *not implemented - base1 = 100 for max +#define SE_SpellDamage 286 // implemented - adds direct spell damage +#define SE_SpellDurationIncByTic 287 // implemented +#define SE_SpecialAttackKBProc 288 // implemented[AA] - Chance to to do a knockback from special attacks [AA Dragon Punch]. +#define SE_ImprovedSpellEffect 289 // implemented +#define SE_IncreaseRunSpeedCap 290 // implemented[AA] - increases run speed over the hard cap +#define SE_Purify 291 // implemented - Removes determental effects +#define SE_StrikeThrough2 292 // implemented[AA] - increasing chance of bypassing an opponent's special defenses, such as dodge, block, parry, and riposte. +#define SE_FrontalStunResist 293 // implemented[AA] - Reduce chance to be stunned from front. +#define SE_CriticalSpellChance 294 // implemented +//#define SE_Unknown295 295 // not used +#define SE_SpellVulnerability 296 // implemented - increase in incoming spell damage +#define SE_Empathy 297 // implemented - debuff that adds points damage to spells cast on target (focus effect). +#define SE_ChangeHeight 298 // implemented +#define SE_WakeTheDead 299 // implemented +#define SE_Doppelganger 300 // implemented +#define SE_ArcheryDamageModifier 301 // implemented[AA] - increase archery damage by percent +#define SE_ImprovedDamage2 302 // implemented - spell damage focus +#define SE_FF_Damage_Amount 303 // implemented - adds direct spell damage +#define SE_OffhandRiposteFail 304 // not implemented as bonus - enemy cannot riposte offhand attacks +#define SE_MitigateDamageShield 305 // implemented - off hand attacks only (Shielding Resistance) +#define SE_ArmyOfTheDead 306 // *not implemented NecroAA - This ability calls up to five shades of nearby corpses back to life to serve the necromancer. The soulless abominations will mindlessly fight the target until called back to the afterlife some time later. The first rank summons up to three shades that serve for 60 seconds, and each additional rank adds one more possible shade and increases their duration by 15 seconds +#define SE_Appraisal 307 // *not implemented Rogue AA - This ability allows you to estimate the selling price of an item you are holding on your cursor. +#define SE_SuspendMinion 308 // not implemented as bonus +#define SE_YetAnotherGate 309 // implemented +#define SE_ReduceReuseTimer 310 // implemented +#define SE_CombatSkills 311 // implemented +#define SE_Sanctuary 312 // *not implemented +#define SE_ForageAdditionalItems 313 // *not implemented[AA] - chance to forage additional items +#define SE_Invisibility2 314 // implemented - fixed duration invisible +#define SE_InvisVsUndead2 315 // implemented - fixed duration ITU +//#define SE_Unknown316 316 // not used +#define SE_ItemHPRegenCapIncrease 317 // implemented[AA] - increases amount of health regen gained via items +#define SE_ItemManaRegenCapIncrease 318 // implemented - increases amount of mana regen you can gain via items +#define SE_CriticalHealOverTime 319 // implemented +#define SE_ShieldBlock 320 // implemented - Block attacks with shield +#define SE_ReduceHate 321 // implemented +#define SE_GateToHomeCity 322 // implemented +#define SE_DefensiveProc 323 // implemented +#define SE_HPToMana 324 // implemented +#define SE_ChanceInvsBreakToAoE 325 // *not implemented[AA] - [AA Nerves of Steel] increasing chance to remain hidden when they are an indirect target of an AoE spell. +#define SE_SpellSlotIncrease 326 // not implemented as bonus - increases your spell slot availability +#define SE_MysticalAttune 327 // implemented - increases amount of buffs that a player can have +#define SE_DelayDeath 328 // implemented - increases how far you can fall below 0 hp before you die +#define SE_ManaAbsorbPercentDamage 329 // implemented +#define SE_CriticalDamageMob 330 // implemented +#define SE_Salvage 331 // *not implemented - chance to recover items that would be destroyed in failed tradeskill combine +#define SE_SummonToCorpse 332 // *not implemented AA - Call of the Wild (Druid/Shaman Res spell with no exp) +#define SE_EffectOnFade 333 // implemented +#define SE_BardAEDot 334 // implemented +#define SE_BlockNextSpellFocus 335 // implemented - base1 chance to block next spell ie Puratus (8494) +//#define SE_Unknown336 336 // not used +#define SE_PercentXPIncrease 337 // implemented +#define SE_SummonAndResAllCorpses 338 // implemented +#define SE_TriggerOnCast 339 // implemented +#define SE_SpellTrigger 340 // implemented - chance to trigger spell +#define SE_ItemAttackCapIncrease 341 // implemented[AA] - increases the maximum amount of attack you can gain from items. +#define SE_ImmuneFleeing 342 // implemented - stop mob from fleeing +#define SE_InterruptCasting 343 // implemented - % chance to interrupt spells being cast every tic. Cacophony (8272) +#define SE_ChannelChanceItems 344 // implemented[AA] - chance to not have ITEM effects interrupted when you take damage. +#define SE_AssassinationLevel 345 // not implemented as bonus - AA Assisination max level to kill +#define SE_HeadShotLevel 346 // not implemented as bonus - AA HeadShot max level to kill +#define SE_ExtraArcheryAttack 347 // not implemented - chance at an additional archery attack (consumes arrow) +#define SE_LimitManaCost 348 // implemented +#define SE_ShieldEquipHateMod 349 // *not implemented[AA] ie. used to increase melee hate when wearing a shield w/ Shield Specialist AA. +#define SE_ManaBurn 350 // implemented - Drains mana for damage/heal at a defined ratio up to a defined maximum amount of mana. +#define SE_PersistentEffect 351 // *not implemented. creates a trap/totem that casts a spell (spell id + base1?) when anything comes near it. can probably make a beacon for this +#define SE_Unknown352 352 // *not implemented - looks to be some type of invulnerability? Test ITC (8755) +#define SE_AdditionalAura 353 // *not implemented - allows use of more than 1 aura, aa effect +#define SE_Unknown354 354 // *not implemented - looks to be some type of invulnerability? Test DAT (8757) +#define SE_Unknown355 355 // *not implemented - looks to be some type of invulnerability? Test LT (8758) +//#define SE_Unknown356 356 // not used +//#define SE_Unknown357 357 // *not implemented - (Stunted Growth) Something to do with negate effects? Focus? Chance? +#define SE_CurrentManaOnce 358 // implemented +#define SE_Invulnerabilty 359 // *not implemented - Invulnerability (Brell's Blessing) +#define SE_SpellOnKill 360 // implemented - a buff that has a base1 % to cast spell base2 when you kill a "challenging foe" base3 min level +#define SE_SpellOnDeath 361 // implemented - casts spell on death of buffed +#define SE_PotionBeltSlots 362 // *not implemented[AA] 'Quick Draw' expands the potion belt by one additional available item slot per rank. +#define SE_BandolierSlots 363 // *not implemented[AA] 'Battle Ready' expands the bandolier by one additional save slot per rank. +#define SE_TripleAttackChance 364 // implemented +#define SE_SpellOnKill2 365 // implemented - chance to trigger a spell on kill when the kill is caused by a specific spell with this effect in it (10470 Venin) +#define SE_ShieldEquipDmgMod 366 // *not implemented - [AA Shield Specialist] - damage bonus to weapon if shield equiped. +#define SE_SetBodyType 367 // implemented - set body type of base1 so it can be affected by spells that are limited to that type (Plant, Animal, Undead, etc) +#define SE_FactionMod 368 // *not implemented - increases faction with base1 (faction id, live won't match up w/ ours) by base2 +#define SE_CorruptionCounter 369 // implemented +#define SE_ResistCorruption 370 // implemented +#define SE_AttackSpeed4 371 // implemented - stackable slow effect 'Inhibit Melee' +#define SE_ForageSkill 372 // *not implemented[AA] Will increase the skill cap for those that have the Forage skill and grant the skill and raise the cap to those that do not. +#define SE_CastOnWearoff 373 // implemented +#define SE_ApplyEffect 374 // implemented +#define SE_DotCritDmgIncrease 375 // implemented - Increase damage of DoT critical amount +//#define SE_Unknown376 376 // *not implemented - used in 2 spells +#define SE_BossSpellTrigger 377 // implemented - spell is cast on fade +#define SE_SpellEffectResistChance 378 // implemented - Increase chance to resist specific spell effect (base1=value, base2=spell effect id) +#define SE_ShadowStepDirectional 379 // implemented - handled by client +#define SE_Knockdown 380 // implemented - small knock back(handled by client) +#define SE_KnockTowardCaster 381 // *not implemented (Call of Hither) knocks you back to caster (value) distance units infront +#define SE_NegateSpellEffect 382 // implemented - negates specific spell bonuses for duration of the debuff. +#define SE_SympatheticProc 383 // implemented - focus on items that has chance to proc a spell when you cast +#define SE_Leap 384 // implemented - Leap effect, ie stomping leap +#define SE_LimitSpellGroup 385 // implemented - Limits to spell group(ie type 3 reuse reduction augs that are class specific and thus all share s SG) +#define SE_CastOnCurer 386 // implemented - Casts a spell on the person curing +#define SE_CastOnCure 387 // implemented - Casts a spell on the cured person +#define SE_SummonCorpseZone 388 // *not implemented - summons a corpse from any zone(nec AA) +#define SE_Forceful_Rejuv 389 // Refresh spell icons +#define SE_CastResistRestrict 390 // *not implemented - some sort of restriction of what resist spells you can cast +//#define SE_Unknown391 391 // not used (Warlord's Fury/Twinproc) likely a focus limit +#define SE_AdditionalHeal2 392 // implemented - Adds or removes healing from spells +#define SE_HealRate2 393 // implemented - HealRate with focus restrictions. +//#define SE_Unknown394 394 // *not implemented - (Diminishing Presence) Adds or removes healing from spells +#define SE_CriticalHealRate 395 // implemented[AA] - Increases chance of having a heal crit when cast on you. [focus limited] +#define SE_AdditionalHeal 396 // implemented - Adds a direct healing amount to spells +#define SE_PetMeleeMitigation 397 // *not implemented[AA] - additional mitigation to your pets. +#define SE_SwarmPetDuration 398 // implemented - Affects the duration of swarm pets +#define SE_Twincast 399 // implemented - cast 2 spells for every 1 +#define SE_HealGroupFromMana 400 // implemented - Drains mana and heals for each point of mana drained +#define SE_ManaDrainWithDmg 401 // implemented - Deals damage based on the amount of mana drained +#define SE_EndDrainWithDmg 402 // implemented - Deals damage for the amount of endurance drained +#define SE_ReluctantBene 403 // *not implemented - Reluctant Benevolence(21662) +#define SE_LimitExcludeSkill 404 // implemented - Limit a focus to exclude spells cast using a specific skill. +#define SE_TwoHandBluntBlock 405 // implemented - chance to block attacks when using two hand blunt weapons (similiar to shield block) +#define SE_CastonNumHitFade 406 // implemented - casts a spell when a buff fades due to its numhits being depleted +//#define SE_Unknown397 407 // *not implemented (Diminished Presence) Triggerable spell effect +#define SE_LimitHPPercent 408 // implemented - limited to a certain percent of your hp(ie heals up to 50%) +#define SE_LimitManaPercent 409 // implemented - limited to a certain percent of your mana +#define SE_LimitEndPercent 410 // implemented - limited to a certain percent of your end +#define SE_LimitClass 411 // implemented - Limits to spells of a certain class (Note: The class value in dbase is +1 in relation to item class value) +//#define SE_Unknown412 412 // not used +#define SE_IncreaseSpellPower 413 // implemented - Increases the power of bard songs, skill attacks, runes, bard allowed foci, damage/heal +#define SE_LimitSpellSkill 414 // implemented - Limit a focus to include spells cast using a specific skill. +//#define SE_Unknown415 415 // not used +#define SE_ACv2 416 // implemented - New AC spell effect +#define SE_ManaRegen_v2 417 // implemented - New mana regen effect +#define SE_SkillDamageAmount2 418 // implemented - adds skill damage directly to certain attacks +#define SE_AddMeleeProc 419 // implemented - Adds a proc +//#define SE_Unknown420 420 // *not used +#define SE_IncreaseNumHits 421 // implemented[AA] - increases number of hits a buff has till fade. (focus) +//#define SE_Unknown422 422 // not used - Seen in Lasting Bravery likely a focus limit +//#define SE_Unknown423 423 // not used - Seen in Lasting Bravery likely a focus limit +#define SE_GravityEffect 424 // implemented - Pulls/pushes you toward/away the mob at a set pace +#define SE_Display 425 // *not implemented - Illusion: Flying Dragon(21626) +#define SE_IncreaseExtTargetWindow 426 // *not implmented[AA] - increases the capacity of your extended target window +#define SE_SkillProc 427 // implemented - chance to proc when using a skill(ie taunt) +#define SE_LimitToSkill 428 // implemented - limits what skills will effect a skill proc +#define SE_SkillProc2 429 // implemented - chance to proc when using a skill (most have hit limits) +//#define SE_Unknown430 430 // *not implemented - Fear of the Dark(27641) +//#define SE_Unknown431 431 // *not implemented - Fear of the Dark(27641) +//#define SE_Unknown432 432 // not used +//#define SE_Uknonwn433 433 // not used +#define SE_CriticalHealChance2 434 // implemented - increase critical heal chance +#define SE_CriticalHealOverTime2 435 // implemented - increase critical heal over time chance +//#define SE_Unknown432 436 // not used +#define SE_Anchor 437 // *not implemented - Teleport Guild Hall Anchor(33099) +//#define SE_Unknown438 438 // not used +#define SE_IncreaseAssassinationLvl 439 // *not implemented[AA] - increases the maximum level of humanoid that can be affected by assassination +#define SE_FinishingBlowLvl 440 // implemented[AA] - Sets the level Finishing blow can be triggered on an NPC +#define SE_MovementSpeed2 441 // *not implemented - new snare effect +#define SE_TriggerOnHPAmount 442 // *not implemented - triggers a spell which a certain hp level is reached +//#define SE_Unknown443 443 // *not implemented - related to Finishing Blow AA +#define SE_AggroLock 444 // *not implemented - target will ignore all but caster for duration +#define SE_AdditionalMercenary 445 // *not implemented[AA] - [Hero's Barracks] Allows you to conscript additional mercs. +// LAST + + +#define DF_Permanent 50 + +// solar: note this struct is historical, we don't actually need it to be +// aligned to anything, but for maintaining it it is kept in the order that +// the fields in the text file are. the numbering is not offset, but field +// 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 +// +struct SPDat_Spell_Struct +{ +/* 000 */ int id; // not used +/* 001 */ char name[64]; // Name of the spell +/* 002 */ char player_1[32]; // "PLAYER_1" +/* 003 */ char teleport_zone[64]; // Teleport zone, pet name summoned, or item summoned +/* 004 */ char you_cast[64]; // Message when you cast +/* 005 */ char other_casts[64]; // Message when other casts +/* 006 */ char cast_on_you[64]; // Message when spell is cast on you +/* 007 */ char cast_on_other[64]; // Message when spell is cast on someone else +/* 008 */ char spell_fades[64]; // Spell fades +/* 009 */ float range; +/* 010 */ float aoerange; +/* 011 */ float pushback; +/* 012 */ float pushup; +/* 013 */ uint32 cast_time; // Cast time +/* 014 */ uint32 recovery_time; // Recovery time +/* 015 */ uint32 recast_time; // Recast same spell time +/* 016 */ uint32 buffdurationformula; +/* 017 */ uint32 buffduration; +/* 018 */ uint32 AEDuration; // sentinel, rain of something +/* 019 */ uint16 mana; // Mana Used +/* 020 */ int32 base[EFFECT_COUNT]; //various purposes +/* 032 */ int base2[EFFECT_COUNT]; //various purposes +/* 044 */ int16 max[EFFECT_COUNT]; +/* 056 */ uint16 icon; // Spell icon +/* 057 */ uint16 memicon; // Icon on membarthing +/* 058 */ int32 components[4]; // reagents +/* 062 */ int component_counts[4]; // amount of regents used +/* 066 */ signed NoexpendReagent[4]; // focus items (Need but not used; Flame Lick has a Fire Beetle Eye focus.) + // If it is a number between 1-4 it means components[number] is a focus and not to expend it + // If it is a valid itemid it means this item is a focus as well +/* 070 */ uint16 formula[EFFECT_COUNT]; // Spell's value formula +/* 082 */ int LightType; // probaly another effecttype flag +/* 083 */ int goodEffect; //0=detrimental, 1=Beneficial, 2=Beneficial, Group Only +/* 084 */ int Activated; // probaly another effecttype flag +/* 085 */ int resisttype; +/* 086 */ int effectid[EFFECT_COUNT]; // Spell's effects +/* 098 */ SpellTargetType targettype; // Spell's Target +/* 099 */ int basediff; // base difficulty fizzle adjustment +/* 100 */ SkillType skill; +/* 101 */ int16 zonetype; // 01=Outdoors, 02=dungeons, ff=Any +/* 102 */ uint16 EnvironmentType; +/* 103 */ int TimeOfDay; +/* 104 */ uint8 classes[PLAYER_CLASS_COUNT]; // Classes, and their min levels +/* 120 */ uint8 CastingAnim; +/* 121 */ uint8 TargetAnim; +/* 122 */ uint32 TravelType; +/* 123 */ uint16 SpellAffectIndex; +/* 124 */ int disallow_sit; // 124: high-end Yaulp spells (V, VI, VII, VIII [Rk 1, 2, & 3], & Gallenite's Bark of Fury +/* 125 */ int spacing125; // 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 */ int spacing142[2]; // 142: between 0 & 100 + // 143: always set to 0 +/* 144 */ int16 new_icon; // Spell icon used by the client in uifiles/default/spells??.tga, both for spell gems & buff window. Looks to depreciate icon & memicon +/* 145 */ int16 spellanim; // Doesn't look like it's the same as #doanim, so not sure what this is +/* 146 */ int8 uninterruptable; // Looks like anything != 0 is uninterruptable. Values are mostly -1, 0, & 1 (Fetid Breath = 90?) +/* 147 */ int16 ResistDiff; +/* 148 */ int dot_stacking_exempt; +/* 149 */ int deletable; +/* 150 */ uint16 RecourseLink; +/* 151 */ int spacing151[3]; // 151: -1, 0, or 1 + // 152 & 153: all set to 0 +/* 154 */ int8 short_buff_box; // != 0, goes to short buff box. Not really supported in the server code +/* 155 */ int descnum; // eqstr of description of spell +/* 156 */ int typedescnum; // eqstr of type description +/* 157 */ int effectdescnum; // eqstr of effect description +/* 158 */ int spacing158[4]; +/* 162 */ int bonushate; +/* 163 */ int spacing163[3]; +/* 166 */ int EndurCost; +/* 167 */ int EndurTimerIndex; +/* 168 */ int IsDisciplineBuff; //Will goto the combat window when cast +/* 169 */ int spacing169[4]; +/* 173 */ int HateAdded; +/* 174 */ int EndurUpkeep; +/* 175 */ int spacing175; +/* 176 */ int numhits; +/* 177 */ int pvpresistbase; +/* 178 */ int pvpresistcalc; +/* 179 */ int pvpresistcap; +/* 180 */ int spell_category; +/* 181 */ int spacing181[4]; +/* 185 */ int can_mgb; // 0=no, -1 or 1 = yes +/* 186 */ int dispel_flag; +/* 189 */ int MinResist; +/* 190 */ int MaxResist; +/* 191 */ int viral_targets; +/* 192 */ int viral_timer; +/* 193 */ int NimbusEffect; +/* 194 */ float directional_start; +/* 195 */ float directional_end; +/* 207 */ int spellgroup; +/* 209 */ int field209; // 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 +/* 211 */ int CastRestriction; //Various restriction categories for spells most seem targetable race related but have also seen others for instance only castable if target hp 20% or lower or only if target out of combat +/* 212 */ bool AllowRest; +/* 219 */ int maxtargets; // not in DB yet, is used for beam and ring spells for target # limits + +//shared memory errors +/* 186 */ /*int8 nodispell;*/ // 0=can be dispelled, -1=can't be dispelled at all, 1=most can be cancelled w/ a cure but not dispelled +/* 187 */ /*uint8 npc_category;*/ // 0=not used, 1=AoE Detrimental, 2=DD, 3=Buffs, 4=Pets, 5=Healing, 6=Gate, 7=Debuff, 8=Dispell +/* 188 */ /*uint32 npc_usefulness;*/ // higher number = more useful, lower number = less useful +/* 189 */ /*int spacing189[18];*/ +/* 207 */ /*uint32 spellgroup;*/ +/* 208 */ /*int spacing208[7];*/ +// Might be newer fields in the live version, which is what some of the last fields are + + + uint8 DamageShieldType; // This field does not exist in spells_us.txt +}; + +#if defined(NEW_LoadSPDat) || defined(DB_LoadSPDat) + extern const SPDat_Spell_Struct* spells; + extern int32 SPDAT_RECORDS; +#else + #define SPDAT_RECORDS 3602 +#endif + +bool IsTargetableAESpell(uint16 spell_id); +bool IsSacrificeSpell(uint16 spell_id); +bool IsLifetapSpell(uint16 spell_id); +bool IsMezSpell(uint16 spell_id); +bool IsStunSpell(uint16 spell_id); +bool IsSlowSpell(uint16 spell_id); +bool IsHasteSpell(uint16 spell_id); +bool IsHarmonySpell(uint16 spell_id); +bool IsPercentalHealSpell(uint16 spell_id); +bool IsGroupOnlySpell(uint16 spell_id); +bool IsBeneficialSpell(uint16 spell_id); +bool IsDetrimentalSpell(uint16 spell_id); +bool IsInvulnerabilitySpell(uint16 spell_id); +bool IsCHDurationSpell(uint16 spell_id); +bool IsPoisonCounterSpell(uint16 spell_id); +bool IsDiseaseCounterSpell(uint16 spell_id); +bool IsSummonItemSpell(uint16 spell_id); +bool IsSummonSkeletonSpell(uint16 spell_id); +bool IsSummonPetSpell(uint16 spell_id); +bool IsSummonPCSpell(uint16 spell_id); +bool IsCharmSpell(uint16 spell_id); +bool IsBlindSpell(uint16 spell_id); +bool IsEffectHitpointsSpell(uint16 spell_id); +bool IsReduceCastTimeSpell(uint16 spell_id); +bool IsIncreaseDurationSpell(uint16 spell_id); +bool IsReduceManaSpell(uint16 spell_id); +bool IsExtRangeSpell(uint16 spell_id); +bool IsImprovedHealingSpell(uint16 spell_id); +bool IsImprovedDamageSpell(uint16 spell_id); +bool IsAEDurationSpell(uint16 spell_id); +bool IsPureNukeSpell(uint16 spell_id); +bool IsPartialCapableSpell(uint16 spell_id); +bool IsResistableSpell(uint16 spell_id); +bool IsGroupSpell(uint16 spell_id); +bool IsTGBCompatibleSpell(uint16 spell_id); +bool IsBardSong(uint16 spell_id); +bool IsEffectInSpell(uint16 spellid, int effect); +bool IsBlankSpellEffect(uint16 spellid, int effect_index); +bool IsValidSpell(uint32 spellid); +bool IsSummonSpell(uint16 spellid); +bool IsEvacSpell(uint16 spellid); +bool IsDamageSpell(uint16 spellid); +bool IsFearSpell(uint16 spellid); +bool BeneficialSpell(uint16 spell_id); +bool GroupOnlySpell(uint16 spell_id); +int GetSpellEffectIndex(uint16 spell_id, int effect); +int CanUseSpell(uint16 spellid, int classa, int level); +int GetMinLevel(uint16 spell_id); +int GetSpellLevel(uint16 spell_id, int classa); +int CalcBuffDuration_formula(int level, int formula, int duration); +int32 CalculatePoisonCounters(uint16 spell_id); +int32 CalculateDiseaseCounters(uint16 spell_id); +int32 CalculateCurseCounters(uint16 spell_id); +int32 CalculateCorruptionCounters(uint16 spell_id); +int32 CalculateCounters(uint16 spell_id); +bool IsDisciplineBuff(uint16 spell_id); +bool IsDiscipline(uint16 spell_id); +bool IsResurrectionEffects(uint16 spell_id); +bool IsRuneSpell(uint16 spell_id); +bool IsMagicRuneSpell(uint16 spell_id); +bool IsManaTapSpell(uint16 spell_id); +bool IsAllianceSpellLine(uint16 spell_id); +bool IsDeathSaveSpell(uint16 spell_id); +bool IsFullDeathSaveSpell(uint16 spell_id); +bool IsPartialDeathSaveSpell(uint16 spell_id); +bool IsShadowStepSpell(uint16 spell_id); +bool IsSuccorSpell(uint16 spell_id); +bool IsTeleportSpell(uint16 spell_id); +bool IsGateSpell(uint16 spell_id); +bool IsPlayerIllusionSpell(uint16 spell_id); // seveian 2008-09-23 +bool IsLDoNObjectSpell(uint16 spell_id); +int32 GetSpellResistType(uint16 spell_id); +int32 GetSpellTargetType(uint16 spell_id); +bool IsHealOverTimeSpell(uint16 spell_id); +bool IsCompleteHealSpell(uint16 spell_id); +bool IsFastHealSpell(uint16 spell_id); +bool IsVeryFastHealSpell(uint16 spell_id); +bool IsRegularSingleTargetHealSpell(uint16 spell_id); +bool IsRegularGroupHealSpell(uint16 spell_id); +bool IsGroupCompleteHealSpell(uint16 spell_id); +bool IsGroupHealOverTimeSpell(uint16 spell_id); +bool IsDebuffSpell(uint16 spell_id); +bool IsResistDebuffSpell(uint16 spell_id); +bool IsSelfConversionSpell(uint16 spell_id); +uint32 GetMorphTrigger(uint32 spell_id); +uint32 GetPartialMeleeRuneReduction(uint32 spell_id); +uint32 GetPartialMagicRuneReduction(uint32 spell_id); +uint32 GetPartialMeleeRuneAmount(uint32 spell_id); +uint32 GetPartialMagicRuneAmount(uint32 spell_id); + +int CalcPetHp(int levelb, int classb, int STA = 75); +const char *GetRandPetName(); +int GetSpellEffectDescNum(uint16 spell_id); +DmgShieldType GetDamageShieldType(uint16 spell_id); +bool DetrimentalSpellAllowsRest(uint16 spell_id); +uint32 GetNimbusEffect(uint16 spell_id); +int32 GetFuriousBash(uint16 spell_id); +bool IsShortDurationBuff(uint16 spell_id); + +#endif diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp new file mode 100644 index 000000000..8d0a33ca0 --- /dev/null +++ b/zone/special_attacks.cpp @@ -0,0 +1,2188 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2002 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 +#include +#include +#include + +#include "masterentity.h" +#include "StringIDs.h" +#include "../common/MiscFunctions.h" +#include "../common/rulesys.h" + + + + +int Mob::GetKickDamage() { + int multiple=(GetLevel()*100/5); + multiple += 100; + int32 dmg=( + ( + (GetSkill(KICK) + GetSTR() + GetLevel())*100 / 9000 + ) * multiple + ) + + 600; //Set a base of 6 damage, 1 seemed too low at the sub level 30 level. + if(GetClass() == WARRIOR || GetClass() == WARRIORGM + ||GetClass() == BERSERKER || GetClass() == BERSERKERGM) { + dmg*=12/10;//small increase for warriors + } + dmg /= 100; + + int32 mindmg = 1; + ApplySpecialAttackMod(KICK, dmg,mindmg); + + return(dmg); +} + +int Mob::GetBashDamage() { + int multiple=(GetLevel()*100/5); + multiple += 100; + + //this is complete shite + int32 dmg=( + ( + ((GetSkill(BASH) + GetSTR())*100 + GetLevel()*100/2) / 10000 + ) * multiple + ) + + 600; //Set a base of 6 damage, 1 seemed too low at the sub level 30 level. + dmg /= 100; + + int32 mindmg = 1; + ApplySpecialAttackMod(BASH, dmg, mindmg); + + return(dmg); +} + +void Mob::ApplySpecialAttackMod(SkillType 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 FLYING_KICK: + case ROUND_KICK: + case KICK: + item_slot = SLOT_FEET; + break; + + case BASH: + item_slot = SLOT_SECONDARY; + break; + + case DRAGON_PUNCH: + case EAGLE_STRIKE: + case TIGER_CLAW: + item_slot = SLOT_HANDS; + break; + } + + if (item_slot >= 0){ + const ItemInst* itm = NULL; + itm = CastToClient()->GetInv().GetItem(item_slot); + if(itm) + dmg += itm->GetItem()->AC * (RuleI(Combat, SpecialAttackACBonus))/100; + } + } +} + +void Mob::DoSpecialAttackDamage(Mob *who, SkillType skill, int32 max_damage, int32 min_damage, int32 hate_override,int ReuseTime, bool HitChance) { + //this really should go through the same code as normal melee damage to + //pick up all the special behavior there + + int32 hate = max_damage; + if(hate_override > -1) + hate = hate_override; + + if(skill == BASH) + { + if(IsClient()) + { + ItemInst *item = CastToClient()->GetInv().GetItem(SLOT_SECONDARY); + if(item) + { + if(item->GetItem()->ItemType == ItemTypeShield) + { + hate += item->GetItem()->AC; + } + const Item_Struct *itm = item->GetItem(); + hate = hate * (100 + GetFuriousBash(itm->Focus.Effect)) / 100; + } + } + } + + min_damage += min_damage * GetMeleeMinDamageMod_SE(skill) / 100; + + if(HitChance && !who->CheckHitChance(this, skill, 13)) + max_damage = 0; + + else{ + bool CanRiposte = true; + if(skill == THROWING && skill == ARCHERY) + CanRiposte = false; + + who->AvoidDamage(this, max_damage, CanRiposte); + who->MeleeMitigation(this, max_damage, min_damage); + + if(max_damage > 0) { + ApplyMeleeDamageBonus(skill, max_damage); + max_damage += who->GetAdditionalDamage(this, 0, true, skill); + max_damage += (itembonuses.HeroicSTR / 10) + (max_damage * who->GetSkillDmgTaken(skill) / 100) + GetSkillDmgAmt(skill); + TryCriticalHit(who, skill, max_damage); + } + } + + if(max_damage >= 0) //You should probably get aggro no matter what, but unclear why it was set like this. + who->AddToHateList(this, hate); + + who->Damage(this, max_damage, SPELL_UNKNOWN, skill, false); + + //Make sure 'this' has not killed the target and 'this' is not dead (Damage shield ect). + if(!GetTarget())return; + if (HasDied()) return; + + //[AA Dragon Punch] value[0] = 100 for 25%, chance value[1] = skill + if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skill){ + int kb_chance = 25; + kb_chance += kb_chance*(100-aabonuses.SpecialAttackKBProc[0])/100; + + if (MakeRandomInt(0, 99) < kb_chance) + SpellFinished(904, who, 10, 0, -1, spells[904].ResistDiff); + //who->Stun(100); Kayen: This effect does not stun on live, it only moves the NPC. + } + + if (HasSkillProcs()){ + float chance = (float)ReuseTime*RuleR(Combat, AvgProcsPerMinute)/60000.0f; + TrySkillProc(who, skill, chance); + } + + if(max_damage == -3 && !who->HasDied()) + DoRiposte(who); +} + + +void Client::OPCombatAbility(const EQApplicationPacket *app) { + if(!GetTarget()) + return; + //make sure were actually able to use such an attack. + if(spellend_timer.Enabled() || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || dead) + return; + + CombatAbility_Struct* ca_atk = (CombatAbility_Struct*) app->pBuffer; + + if(GetTarget()->GetID() != ca_atk->m_target) + return; //invalid packet. + + if(!IsAttackAllowed(GetTarget())) + return; + + //These two are not subject to the combat ability timer, as they + //allready do their checking in conjunction with the attack timer + //throwing weapons + if(ca_atk->m_atk == 11) { + if (ca_atk->m_skill == THROWING) { + SetAttackTimer(); + ThrowingAttack(GetTarget()); + return; + } + //ranged attack (archery) + if (ca_atk->m_skill == ARCHERY) { + SetAttackTimer(); + RangedAttack(GetTarget()); + return; + } + //could we return here? Im not sure is m_atk 11 is used for real specials + } + + //check range for all these abilities, they are all close combat stuff + if(!CombatRange(GetTarget())) + return; + + if(!p_timers.Expired(&database, pTimerCombatAbility, false)) { + Message(13,"Ability recovery time not yet met."); + return; + } + + int ReuseTime = 0; + int ClientHaste = GetHaste(); + int HasteMod = 0; + + 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; + + int32 skill_reduction = this->GetSkillReuseTime(ca_atk->m_skill); + + if ((ca_atk->m_atk == 100) + && (ca_atk->m_skill == BASH)) { // SLAM - Bash without a shield equipped + if (GetTarget() != this) { + + CheckIncreaseSkill(BASH, GetTarget(), 10); + DoAnim(animTailRake); + + int32 ht = 0; + if(GetWeaponDamage(GetTarget(), GetInv().GetItem(SLOT_SECONDARY)) <= 0 && + GetWeaponDamage(GetTarget(), GetInv().GetItem(SLOT_SHOULDER)) <= 0){ + dmg = -5; + } + else{ + if(!GetTarget()->CheckHitChance(this, BASH, 0)) { + dmg = 0; + ht = GetBashDamage(); + } + else{ + if(RuleB(Combat, UseIntervalAC)) + ht = dmg = GetBashDamage(); + else + ht = dmg = MakeRandomInt(1, GetBashDamage()); + + } + } + + ReuseTime = BashReuseTime-1-skill_reduction; + ReuseTime = (ReuseTime*HasteMod)/100; + DoSpecialAttackDamage(GetTarget(), BASH, dmg, 1, ht, ReuseTime); + if(ReuseTime > 0) + { + p_timers.Start(pTimerCombatAbility, ReuseTime); + } + } + return; + } + + if ((ca_atk->m_atk == 100) && (ca_atk->m_skill == FRENZY)) + { + CheckIncreaseSkill(FRENZY, GetTarget(), 10); + int AtkRounds = 3; + int skillmod = 100*GetSkill(FRENZY)/MaxSkill(FRENZY); + int32 max_dmg = (26 + ((((GetLevel()-6) * 2)*skillmod)/100)) * ((100+RuleI(Combat, FrenzyBonus))/100); + int32 min_dmg = 0; + DoAnim(anim2HSlashing); + + if (GetLevel() < 51) + min_dmg = 1; + else + min_dmg = GetLevel()*8/10; + + if (min_dmg > max_dmg) + max_dmg = min_dmg; + + ReuseTime = FrenzyReuseTime-1-skill_reduction; + ReuseTime = (ReuseTime*HasteMod)/100; + + //Live parses show around 55% Triple 35% Double 10% Single, you will always get first hit. + while(AtkRounds > 0) { + + if (GetTarget() && (AtkRounds == 1 || MakeRandomInt(0,100) < 75)){ + DoSpecialAttackDamage(GetTarget(), FRENZY, max_dmg, min_dmg, max_dmg , ReuseTime, true); + } + AtkRounds--; + } + + if(ReuseTime > 0) { + p_timers.Start(pTimerCombatAbility, ReuseTime); + } + return; + } + + switch(GetClass()) + { + case BERSERKER: + case WARRIOR: + case RANGER: + case BEASTLORD: + if (ca_atk->m_atk != 100 || ca_atk->m_skill != KICK) { + break; + } + if (GetTarget() != this) { + CheckIncreaseSkill(KICK, GetTarget(), 10); + DoAnim(animKick); + + int32 ht = 0; + if(GetWeaponDamage(GetTarget(), GetInv().GetItem(SLOT_FEET)) <= 0){ + dmg = -5; + } + else{ + if(!GetTarget()->CheckHitChance(this, KICK, 0)) { + dmg = 0; + ht = GetKickDamage(); + } + else{ + if(RuleB(Combat, UseIntervalAC)) + ht = dmg = GetKickDamage(); + else + ht = dmg = MakeRandomInt(1, GetKickDamage()); + } + } + + ReuseTime = KickReuseTime-1-skill_reduction; + DoSpecialAttackDamage(GetTarget(), KICK, dmg, 1, ht, ReuseTime); + + } + break; + case MONK: { + 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,100)) ) { + + int MonkSPA [5] = { FLYING_KICK, DRAGON_PUNCH, EAGLE_STRIKE, TIGER_CLAW, ROUND_KICK }; + MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0,4)]); + + int TripleChance = 25; + + if (bDoubleSpecialAttack > 100) + TripleChance += TripleChance*(100-bDoubleSpecialAttack)/100; + + if(TripleChance > MakeRandomInt(0,100)) { + MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0,4)]); + } + } + + if(ReuseTime < 100) { + //hackish... but we return a huge reuse time if this is an + // invalid skill, otherwise, we can safely assume it is a + // valid monk skill and just cast it to a SkillType + CheckIncreaseSkill((SkillType) ca_atk->m_skill, GetTarget(), 10); + } + break; + } + case ROGUE: { + if (ca_atk->m_atk != 100 || ca_atk->m_skill != BACKSTAB) { + break; + } + TryBackstab(GetTarget(), ReuseTime); + ReuseTime = BackstabReuseTime-1 - skill_reduction; + break; + } + default: + //they have no abilities... wtf? make em wait a bit + ReuseTime = 9 - skill_reduction; + break; + } + + ReuseTime = (ReuseTime*HasteMod)/100; + if(ReuseTime > 0) + { + p_timers.Start(pTimerCombatAbility, ReuseTime); + } +} + +//returns the reuse time in sec for the special attack used. +int Mob::MonkSpecialAttack(Mob* other, uint8 unchecked_type) +{ + if(!other) + return 0; + + int32 ndamage = 0; + int32 max_dmg = 0; + int32 min_dmg = 1; + int reuse = 0; + SkillType skill_type; //to avoid casting... even though it "would work" + uint8 itemslot = SLOT_FEET; + + switch(unchecked_type) + { + case FLYING_KICK:{ + skill_type = FLYING_KICK; + max_dmg = ((GetSTR()+GetSkill(skill_type)) * RuleI(Combat, FlyingKickBonus) / 100) + 35; + min_dmg = ((level*8)/10); + ApplySpecialAttackMod(skill_type, max_dmg, min_dmg); + DoAnim(animFlyingKick); + reuse = FlyingKickReuseTime; + break; + } + case DRAGON_PUNCH:{ + skill_type = DRAGON_PUNCH; + max_dmg = ((GetSTR()+GetSkill(skill_type)) * RuleI(Combat, DragonPunchBonus) / 100) + 26; + itemslot = SLOT_HANDS; + ApplySpecialAttackMod(skill_type, max_dmg, min_dmg); + DoAnim(animTailRake); + reuse = TailRakeReuseTime; + break; + } + + case EAGLE_STRIKE:{ + skill_type = EAGLE_STRIKE; + max_dmg = ((GetSTR()+GetSkill(skill_type)) * RuleI(Combat, EagleStrikeBonus) / 100) + 19; + itemslot = SLOT_HANDS; + ApplySpecialAttackMod(skill_type, max_dmg, min_dmg); + DoAnim(animEagleStrike); + reuse = EagleStrikeReuseTime; + break; + } + + case TIGER_CLAW:{ + skill_type = TIGER_CLAW; + max_dmg = ((GetSTR()+GetSkill(skill_type)) * RuleI(Combat, TigerClawBonus) / 100) + 12; + itemslot = SLOT_HANDS; + ApplySpecialAttackMod(skill_type, max_dmg, min_dmg); + DoAnim(animTigerClaw); + reuse = TigerClawReuseTime; + break; + } + + case ROUND_KICK:{ + skill_type = ROUND_KICK; + max_dmg = ((GetSTR()+GetSkill(skill_type)) * RuleI(Combat, RoundKickBonus) / 100) + 10; + ApplySpecialAttackMod(skill_type, max_dmg, min_dmg); + DoAnim(animRoundKick); + reuse = RoundKickReuseTime; + break; + } + + case KICK:{ + skill_type = KICK; + max_dmg = GetKickDamage(); + DoAnim(animKick); + reuse = KickReuseTime; + break; + } + default: + mlog(CLIENT__ERROR, "Invalid special attack type %d attempted", unchecked_type); + return(1000); /* nice long delay for them, the caller depends on this! */ + } + + if(IsClient()){ + if(GetWeaponDamage(other, CastToClient()->GetInv().GetItem(itemslot)) <= 0){ + ndamage = -5; + } + } + else{ + if(GetWeaponDamage(other, (const Item_Struct*)NULL) <= 0){ + ndamage = -5; + } + } + + int32 ht = 0; + if(ndamage == 0){ + if(other->CheckHitChance(this, skill_type, 0)){ + if(RuleB(Combat, UseIntervalAC)) + ht = ndamage = max_dmg; + else + ht = ndamage = MakeRandomInt(min_dmg, max_dmg); + } + else + { + ht = max_dmg; + } + } + + DoSpecialAttackDamage(other, skill_type, ndamage, min_dmg, ht, reuse); + + return(reuse); +} + +void Mob::TryBackstab(Mob *other, int ReuseTime) { + if(!other) + return; + + bool bIsBehind = false; + bool bCanFrontalBS = false; + + //make sure we have a proper weapon if we are a client. + if(IsClient()) { + const ItemInst *wpn = CastToClient()->GetInv().GetItem(SLOT_PRIMARY); + if(!wpn || (wpn->GetItem()->ItemType != ItemTypePierce)){ + Message_StringID(13, BACKSTAB_WEAPON); + return; + } + } + + //Live AA - Triple Backstab + int tripleChance = itembonuses.TripleBackstab + spellbonuses.TripleBackstab + aabonuses.TripleBackstab; + + if (BehindMob(other, GetX(), GetY())) + bIsBehind = true; + + else { + //Live AA - Seized Opportunity + int FrontalBSChance = itembonuses.FrontalBackstabChance + spellbonuses.FrontalBackstabChance + aabonuses.FrontalBackstabChance; + + if (FrontalBSChance && (FrontalBSChance > MakeRandomInt(0, 100))) + bCanFrontalBS = true; + } + + if (bIsBehind || bCanFrontalBS){ // Player is behind other OR can do Frontal Backstab + + if (bCanFrontalBS) { + CastToClient()->Message(0,"Your fierce attack is executed with such grace, your target did not see it coming!"); + } + + // solar - chance to assassinate + int chance = 10 + (GetDEX()/10) + (itembonuses.HeroicDEX/10); //18.5% chance at 85 dex 40% chance at 300 dex + if( + level >= 60 && // player is 60 or higher + other->GetLevel() <= 45 && // mob 45 or under + !other->CastToNPC()->IsEngaged() && // not aggro + other->GetHP()<=32000 + && other->IsNPC() + && MakeRandomFloat(0, 99) < chance // chance + ) { + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, ASSASSINATES, GetName()); + if(IsClient()) + CastToClient()->CheckIncreaseSkill(BACKSTAB, other, 10); + RogueAssassinate(other); + } + else { + RogueBackstab(other); + if (level > 54) { + float DoubleAttackProbability = (GetSkill(DOUBLE_ATTACK) + GetLevel()) / 500.0f; // 62.4 max + // Check for double attack with main hand assuming maxed DA Skill (MS) + + if(MakeRandomFloat(0, 1) < DoubleAttackProbability) // Max 62.4 % chance of DA + { + if(other->GetHP() > 0) + RogueBackstab(other,false,ReuseTime); + + if (tripleChance && other->GetHP() > 0 && tripleChance > MakeRandomInt(0, 100)) + RogueBackstab(other,false,ReuseTime); + } + } + if(IsClient()) + CastToClient()->CheckIncreaseSkill(BACKSTAB, other, 10); + } + } + //Live AA - Chaotic Backstab + else if(aabonuses.FrontalBackstabMinDmg || itembonuses.FrontalBackstabMinDmg || spellbonuses.FrontalBackstabMinDmg) { + + //we can stab from any angle, we do min damage though. + RogueBackstab(other, true); + if (level > 54) { + float DoubleAttackProbability = (GetSkill(DOUBLE_ATTACK) + GetLevel()) / 500.0f; // 62.4 max + if(IsClient()) + CastToClient()->CheckIncreaseSkill(BACKSTAB, other, 10); + // Check for double attack with main hand assuming maxed DA Skill (MS) + if(MakeRandomFloat(0, 1) < DoubleAttackProbability) // Max 62.4 % chance of DA + if(other->GetHP() > 0) + RogueBackstab(other,true, ReuseTime); + + if (tripleChance && other->GetHP() > 0 && tripleChance > MakeRandomInt(0, 100)) + RogueBackstab(other,false,ReuseTime); + } + } + else { //We do a single regular attack if we attack from the front without chaotic stab + Attack(other, 13); + } +} + +//heko: backstab +void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) +{ + int32 ndamage = 0; + int32 max_hit = 0; + int32 min_hit = 0; + int32 hate = 0; + int32 primaryweapondamage = 0; + int32 backstab_dmg = 0; + + if(IsClient()){ + const ItemInst *wpn = NULL; + wpn = CastToClient()->GetInv().GetItem(SLOT_PRIMARY); + primaryweapondamage = GetWeaponDamage(other, wpn); + backstab_dmg = wpn->GetItem()->BackstabDmg; + for(int i = 0; i < MAX_AUGMENT_SLOTS; ++i) + { + ItemInst *aug = wpn->GetAugment(i); + if(aug) + { + backstab_dmg += aug->GetItem()->BackstabDmg; + } + } + } + else{ + primaryweapondamage = (GetLevel()/7)+1; // fallback incase it's a npc without a weapon, 2 dmg at 10, 10 dmg at 65 + backstab_dmg = primaryweapondamage; + } + + if(primaryweapondamage > 0){ + if(level > 25){ + max_hit = (((2*backstab_dmg) * GetDamageTable(BACKSTAB) / 100) * 10 * GetSkill(BACKSTAB) / 355) + ((level-25)/3) + 1; + hate = 20 * backstab_dmg * GetSkill(BACKSTAB) / 355; + } + else{ + max_hit = (((2*backstab_dmg) * GetDamageTable(BACKSTAB) / 100) * 10 * GetSkill(BACKSTAB) / 355) + 1;; + hate = 20 * backstab_dmg * GetSkill(BACKSTAB) / 355; + } + + // determine minimum hits + if (level < 51) + { + min_hit = (level*15/10); + } + 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; + } + + if(!other->CheckHitChance(this, BACKSTAB, 0)) { + ndamage = 0; + } + else{ + if(min_damage){ + ndamage = min_hit; + } + else + { + if (max_hit < min_hit) + max_hit = min_hit; + + if(RuleB(Combat, UseIntervalAC)) + ndamage = max_hit; + else + ndamage = MakeRandomInt(min_hit, max_hit); + + } + } + } + else{ + ndamage = -5; + } + + DoSpecialAttackDamage(other, BACKSTAB, ndamage, min_hit, hate, ReuseTime); + DoAnim(animPiercing); +} + +// solar - assassinate +void Mob::RogueAssassinate(Mob* other) +{ + //can you dodge, parry, etc.. an assassinate?? + //if so, use DoSpecialAttackDamage(other, BACKSTAB, 32000); instead + if(GetWeaponDamage(other, IsClient()?CastToClient()->GetInv().GetItem(SLOT_PRIMARY):(const ItemInst*)NULL) > 0){ + other->Damage(this, 32000, SPELL_UNKNOWN, BACKSTAB); + }else{ + other->Damage(this, -5, SPELL_UNKNOWN, BACKSTAB); + } + DoAnim(animPiercing); //piercing animation +} + +void Client::RangedAttack(Mob* other) { + //conditions to use an attack checked before we are called + + //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())) { + mlog(COMBAT__RANGED, "Throwing attack canceled. Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); + // The server and client timers are not exact matches currently, so this would spam too often if enabled + //Message(0, "Error: Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); + return; + } + const ItemInst* RangeWeapon = m_inv[SLOT_RANGE]; + + //locate ammo + int ammo_slot = SLOT_AMMO; + const ItemInst* Ammo = m_inv[SLOT_AMMO]; + + if (!RangeWeapon || !RangeWeapon->IsType(ItemClassCommon)) { + mlog(COMBAT__RANGED, "Ranged attack canceled. Missing or invalid ranged weapon (%d) in slot %d", GetItemIDAt(SLOT_RANGE), SLOT_RANGE); + Message(0, "Error: Rangeweapon: GetItem(%i)==0, you have no bow!", GetItemIDAt(SLOT_RANGE)); + return; + } + if (!Ammo || !Ammo->IsType(ItemClassCommon)) { + mlog(COMBAT__RANGED, "Ranged attack canceled. Missing or invalid ammo item (%d) in slot %d", GetItemIDAt(SLOT_AMMO), SLOT_AMMO); + Message(0, "Error: Ammo: GetItem(%i)==0, you have no ammo!", GetItemIDAt(SLOT_AMMO)); + return; + } + + const Item_Struct* RangeItem = RangeWeapon->GetItem(); + const Item_Struct* AmmoItem = Ammo->GetItem(); + + if(RangeItem->ItemType != ItemTypeBow) { + mlog(COMBAT__RANGED, "Ranged attack canceled. Ranged item is not a bow. type %d.", RangeItem->ItemType); + Message(0, "Error: Rangeweapon: Item %d is not a bow.", RangeWeapon->GetID()); + return; + } + if(AmmoItem->ItemType != ItemTypeArrow) { + mlog(COMBAT__RANGED, "Ranged attack canceled. Ammo item is not an arrow. type %d.", AmmoItem->ItemType); + Message(0, "Error: Ammo: type %d != %d, you have the wrong type of ammo!", AmmoItem->ItemType, ItemTypeArrow); + return; + } + + mlog(COMBAT__RANGED, "Shooting %s with bow %s (%d) and arrow %s (%d)", GetTarget()->GetName(), RangeItem->Name, RangeItem->ID, AmmoItem->Name, AmmoItem->ID); + + //look for ammo in inventory if we only have 1 left... + if(Ammo->GetCharges() == 1) { + //first look for quivers + int r; + bool found = false; + for(r = SLOT_PERSONAL_BEGIN; r <= SLOT_PERSONAL_END; r++) { + const ItemInst *pi = m_inv[r]; + if(pi == NULL || !pi->IsType(ItemClassContainer)) + continue; + const Item_Struct* bagitem = pi->GetItem(); + if(!bagitem || bagitem->BagType != bagTypeQuiver) + continue; + + //we found a quiver, look for the ammo in it + int i; + for (i = 0; i < bagitem->BagSlots; i++) { + ItemInst* baginst = pi->GetItem(i); + if(!baginst) + continue; //empty + if(baginst->GetID() == Ammo->GetID()) { + //we found it... use this stack + //the item wont change, but the instance does + Ammo = baginst; + ammo_slot = m_inv.CalcSlotId(r, i); + found = true; + mlog(COMBAT__RANGED, "Using ammo from quiver stack at slot %d. %d in stack.", ammo_slot, Ammo->GetCharges()); + break; + } + } + if(found) + break; + } + + if(!found) { + //if we dont find a quiver, look through our inventory again + //not caring if the thing is a quiver. + int32 aslot = m_inv.HasItem(AmmoItem->ID, 1, invWherePersonal); + if(aslot != SLOT_INVALID) { + ammo_slot = aslot; + Ammo = m_inv[aslot]; + mlog(COMBAT__RANGED, "Using ammo from inventory stack at slot %d. %d in stack.", ammo_slot, Ammo->GetCharges()); + } + } + } + + 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 + mlog(COMBAT__RANGED, "Calculated bow range to be %.1f", range); + range *= range; + if(DistNoRootNoZ(*GetTarget()) > range) { + mlog(COMBAT__RANGED, "Ranged attack out of range... client should catch this. (%f > %f).\n", DistNoRootNoZ(*GetTarget()), range); + //target is out of range, client does a message + return; + } + else if(DistNoRootNoZ(*GetTarget()) < (RuleI(Combat, MinRangedAttackDist)*RuleI(Combat, MinRangedAttackDist))){ + return; + } + + if(!IsAttackAllowed(GetTarget()) || + IsCasting() || + IsSitting() || + (DivineAura() && !GetGM()) || + IsStunned() || + IsFeared() || + IsMezzed() || + (GetAppearance() == eaDead)){ + return; + } + + SendItemAnimation(GetTarget(), AmmoItem, ARCHERY); + + DoArcheryAttackDmg(GetTarget(), RangeWeapon, Ammo); + + //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)){ + + 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(ARCHERY, GetTarget(), -15); + + //break invis when you attack + if(invisible) { + mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack."); + BuffFadeByEffect(SE_Invisibility); + BuffFadeByEffect(SE_Invisibility2); + invisible = false; + } + if(invisible_undead) { + mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack."); + BuffFadeByEffect(SE_InvisVsUndead); + BuffFadeByEffect(SE_InvisVsUndead2); + invisible_undead = false; + } + if(invisible_animals){ + mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack."); + 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); + } +} + +void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus) +{ + if (!CanDoSpecialAttack(other)) + return; + + if (!other->CheckHitChance(this, ARCHERY, 13,chance_mod)) { + mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName()); + other->Damage(this, 0, SPELL_UNKNOWN, ARCHERY); + } else { + mlog(COMBAT__RANGED, "Ranged attack hit %s.", other->GetName()); + + if(!TryHeadShot(other, ARCHERY)) + { + 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((WDmg > 0) || (ADmg > 0)) + { + if(WDmg < 0) + WDmg = 0; + if(ADmg < 0) + ADmg = 0; + uint32 MaxDmg = (RuleR(Combat, ArcheryBaseDamageBonus)*(WDmg+ADmg)*GetDamageTable(ARCHERY)) / 100; + int32 hate = ((WDmg+ADmg)); + + 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); + + if(GetClass() == RANGER && GetLevel() > 50) + { + if(RuleB(Combat, ArcheryBonusRequiresStationary)) + { + if(other->IsNPC() && !other->IsMoving() && !other->IsRooted()) + { + MaxDmg *= (float)2; + hate *= (float)2; + mlog(COMBAT__RANGED, "Ranger. Double damage success roll, doubling damage to %d", MaxDmg); + Message_StringID(MT_CritMelee, BOW_DOUBLE_DAMAGE); + } + } + else + { + MaxDmg *= (float)2; + hate *= (float)2; + 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(ARCHERY) / 100; + hate += (2*((GetLevel()-25)/3)); + } + + other->AvoidDamage(this, TotalDmg, false); //CanRiposte=false - Can not riposte archery attacks. + other->MeleeMitigation(this, TotalDmg, minDmg); + if(TotalDmg > 0) + { + TotalDmg += TotalDmg*focus/100; + ApplyMeleeDamageBonus(ARCHERY, TotalDmg); + TotalDmg += other->GetAdditionalDamage(this, 0, true, ARCHERY); + TotalDmg += (itembonuses.HeroicDEX / 10) + (TotalDmg * other->GetSkillDmgTaken(ARCHERY) / 100) + GetSkillDmgAmt(ARCHERY); + TryCriticalHit(other, ARCHERY, TotalDmg); + other->AddToHateList(this, hate, 0, false); + } + } + else + TotalDmg = -5; + + other->Damage(this, TotalDmg, SPELL_UNKNOWN, ARCHERY); + } + } + + //try proc on hits and misses + if((RangeWeapon != NULL) && GetTarget() && other && (other->GetHP() > -10)) + { + TryWeaponProc(RangeWeapon, other, 11); + } +} + +void NPC::RangedAttack(Mob* other) +{ + //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())) + { + 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 = NULL; + const Item_Struct* ammo = NULL; + if(!SpecAttacks[SPECATK_RANGED_ATK]) + { + //find our bow and ammo return if we can't find them... + return; + } + + float range = 250; // needs to be longer than 200(most spells) + mlog(COMBAT__RANGED, "Calculated bow range to be %.1f", range); + range *= range; + if(DistNoRootNoZ(*GetTarget()) > range) { + mlog(COMBAT__RANGED, "Ranged attack out of range...%.2f vs %.2f", DistNoRootNoZ(*GetTarget()), range); + //target is out of range, client does a message + return; + } + else if(DistNoRootNoZ(*GetTarget()) < (RuleI(Combat, MinRangedAttackDist)*RuleI(Combat, MinRangedAttackDist))){ + return; + } + + if(!IsAttackAllowed(GetTarget()) || + IsCasting() || + DivineAura() || + IsStunned() || + IsFeared() || + IsMezzed() || + (GetAppearance() == eaDead)){ + return; + } + + if(!ammo) + { + ammo = database.GetItem(8005); + } + + if(ammo) + SendItemAnimation(GetTarget(), ammo, ARCHERY); + + // Face the Target + FaceTarget(GetTarget()); + + // Hit? + if (!GetTarget()->CheckHitChance(this, ARCHERY, 13)) + { + mlog(COMBAT__RANGED, "Ranged attack missed %s.", GetTarget()->GetName()); + GetTarget()->Damage(this, 0, SPELL_UNKNOWN, ARCHERY); + } + else + { + int16 WDmg = GetWeaponDamage(GetTarget(), weapon); + int16 ADmg = GetWeaponDamage(GetTarget(), ammo); + if(WDmg > 0 || ADmg > 0) + { + mlog(COMBAT__RANGED, "Ranged attack hit %s.", GetTarget()->GetName()); + int32 TotalDmg = 0; + + 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); + + int32 hate = TotalDmg; + + GetTarget()->MeleeMitigation(this, TotalDmg, MinDmg); + ApplyMeleeDamageBonus(ARCHERY, TotalDmg); + TryCriticalHit(GetTarget(), ARCHERY, TotalDmg); + GetTarget()->AddToHateList(this, hate, 0, false); + GetTarget()->Damage(this, TotalDmg, SPELL_UNKNOWN, ARCHERY); + } + else + { + GetTarget()->Damage(this, -5, SPELL_UNKNOWN, ARCHERY); + } + } + + //break invis when you attack + if(invisible) { + mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack."); + BuffFadeByEffect(SE_Invisibility); + BuffFadeByEffect(SE_Invisibility2); + invisible = false; + } + if(invisible_undead) { + mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack."); + BuffFadeByEffect(SE_InvisVsUndead); + BuffFadeByEffect(SE_InvisVsUndead2); + invisible_undead = false; + } + if(invisible_animals){ + mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack."); + 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); + } +} + +uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg) +{ + + uint16 MaxDmg = (((2 * wDmg) * GetDamageTable(THROWING)) / 100); + + if (MaxDmg == 0) + MaxDmg = 1; + + if(RuleB(Combat, UseIntervalAC)) + TotalDmg = MaxDmg; + else + TotalDmg = MakeRandomInt(1, MaxDmg); + + minDmg = 1; + if(GetLevel() > 25) + { + TotalDmg += ((GetLevel()-25)/3); + minDmg += ((GetLevel()-25)/3); + minDmg += minDmg * GetMeleeMinDamageMod_SE(THROWING) / 100; + } + + if(MaxDmg < minDmg) + MaxDmg = minDmg; + + return MaxDmg; +} + +void Client::ThrowingAttack(Mob* other) { //old was 51 + //conditions to use an attack checked before we are called + + //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())) { + mlog(COMBAT__RANGED, "Throwing attack canceled. Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); + // The server and client timers are not exact matches currently, so this would spam too often if enabled + //Message(0, "Error: Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); + return; + } + + int ammo_slot = SLOT_RANGE; + const ItemInst* RangeWeapon = m_inv[SLOT_RANGE]; + + if (!RangeWeapon || !RangeWeapon->IsType(ItemClassCommon)) { + mlog(COMBAT__RANGED, "Ranged attack canceled. Missing or invalid ranged weapon (%d) in slot %d", GetItemIDAt(SLOT_RANGE), SLOT_RANGE); + Message(0, "Error: Rangeweapon: GetItem(%i)==0, you have nothing to throw!", GetItemIDAt(SLOT_RANGE)); + return; + } + + const Item_Struct* item = RangeWeapon->GetItem(); + if(item->ItemType != ItemTypeThrowing && item->ItemType != ItemTypeThrowingv2) { + mlog(COMBAT__RANGED, "Ranged attack canceled. Ranged item %d is not a throwing weapon. type %d.", item->ItemType); + Message(0, "Error: Rangeweapon: GetItem(%i)==0, you have nothing useful to throw!", GetItemIDAt(SLOT_RANGE)); + return; + } + + mlog(COMBAT__RANGED, "Throwing %s (%d) at %s", item->Name, item->ID, GetTarget()->GetName()); + + if(RangeWeapon->GetCharges() == 1) { + //first check ammo + const ItemInst* AmmoItem = m_inv[SLOT_AMMO]; + if(AmmoItem != NULL && AmmoItem->GetID() == RangeWeapon->GetID()) { + //more in the ammo slot, use it + RangeWeapon = AmmoItem; + ammo_slot = SLOT_AMMO; + mlog(COMBAT__RANGED, "Using ammo from ammo slot, stack at slot %d. %d in stack.", ammo_slot, RangeWeapon->GetCharges()); + } else { + //look through our inventory for more + int32 aslot = m_inv.HasItem(item->ID, 1, invWherePersonal); + if(aslot != SLOT_INVALID) { + //the item wont change, but the instance does, not that it matters + ammo_slot = aslot; + RangeWeapon = m_inv[aslot]; + mlog(COMBAT__RANGED, "Using ammo from inventory slot, stack at slot %d. %d in stack.", ammo_slot, RangeWeapon->GetCharges()); + } + } + } + + int range = item->Range +50/*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) { + mlog(COMBAT__RANGED, "Throwing attack out of range... client should catch this. (%f > %f).\n", DistNoRootNoZ(*GetTarget()), range); + //target is out of range, client does a message + return; + } + else if(DistNoRootNoZ(*GetTarget()) < (RuleI(Combat, MinRangedAttackDist)*RuleI(Combat, MinRangedAttackDist))){ + return; + } + + if(!IsAttackAllowed(GetTarget()) || + IsCasting() || + IsSitting() || + (DivineAura() && !GetGM()) || + IsStunned() || + IsFeared() || + IsMezzed() || + (GetAppearance() == eaDead)){ + return; + } + //send item animation, also does the throw animation + SendItemAnimation(GetTarget(), item, THROWING); + + DoThrowingAttackDmg(GetTarget(), RangeWeapon, item); + + //consume ammo + DeleteItemInInventory(ammo_slot, 1, true); + CheckIncreaseSkill(THROWING, GetTarget()); + + //break invis when you attack + if(invisible) { + mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack."); + BuffFadeByEffect(SE_Invisibility); + BuffFadeByEffect(SE_Invisibility2); + invisible = false; + } + if(invisible_undead) { + mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack."); + BuffFadeByEffect(SE_InvisVsUndead); + BuffFadeByEffect(SE_InvisVsUndead2); + invisible_undead = false; + } + if(invisible_animals){ + mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack."); + 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); + } +} + +void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* item, uint16 weapon_damage, int16 chance_mod,int16 focus) +{ + if (!CanDoSpecialAttack(other)) + return; + + if (!other->CheckHitChance(this, THROWING, 13, chance_mod)){ + mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName()); + other->Damage(this, 0, SPELL_UNKNOWN, THROWING); + } else { + mlog(COMBAT__RANGED, "Throwing attack hit %s.", other->GetName()); + + int16 WDmg = 0; + + if (!weapon_damage && item != NULL) + WDmg = GetWeaponDamage(other, item); + else + WDmg = weapon_damage; + + int32 TotalDmg = 0; + + if(WDmg > 0) + { + int minDmg = 1; + uint16 MaxDmg = GetThrownDamage(WDmg, TotalDmg, minDmg); + + mlog(COMBAT__RANGED, "Item DMG %d. Max Damage %d. Hit for damage %d", WDmg, MaxDmg, TotalDmg); + other->AvoidDamage(this, TotalDmg, false); //CanRiposte=false - Can not riposte throw attacks. + other->MeleeMitigation(this, TotalDmg, minDmg); + if(TotalDmg > 0) + { + TotalDmg += TotalDmg*focus/100; + ApplyMeleeDamageBonus(THROWING, TotalDmg); + TotalDmg += other->GetAdditionalDamage(this, 0, true, THROWING); + TotalDmg += (itembonuses.HeroicDEX / 10) + (TotalDmg * other->GetSkillDmgTaken(THROWING) / 100) + GetSkillDmgAmt(THROWING); + TryCriticalHit(other, THROWING, TotalDmg); + int32 hate = (2*WDmg); + other->AddToHateList(this, hate, 0, false); + } + } + + else + TotalDmg = -5; + + other->Damage(this, TotalDmg, SPELL_UNKNOWN, THROWING); + } + + if((RangeWeapon != NULL) && GetTarget() && other && (other->GetHP() > -10)) + TryWeaponProc(RangeWeapon, other, 11); +} + +void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillType skillInUse) { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SomeItemPacketMaybe, sizeof(Arrow_Struct)); + Arrow_Struct *as = (Arrow_Struct *) outapp->pBuffer; + as->type = 1; + as->src_x = GetX(); + as->src_y = GetY(); + as->src_z = GetZ(); + as->source_id = GetID(); + as->target_id = to->GetID(); + as->item_id = item->ID; + + as->item_type = item->ItemType; + as->skill = (uint8)skillInUse; + + strn0cpy(as->model_name, item->IDFile, 16); + + + /* + The angular field affects how the object flies towards the target. + A low angular (10) makes it circle the target widely, where a high + angular (20000) makes it go straight at them. + + The tilt field causes the object to be tilted flying through the air + and also seems to have an effect on how it behaves when circling the + target based on the angular field. + + Arc causes the object to form an arc in motion. A value too high will + */ + as->velocity = 4.0; + + //these angle and tilt used together seem to make the arrow/knife throw as straight as I can make it + + as->launch_angle = CalculateHeadingToTarget(to->GetX(), to->GetY()) * 2; + as->tilt = 125; + as->arc = 50; + + + //fill in some unknowns, we dont know their meaning yet + //neither of these seem to change the behavior any + as->unknown088 = 125; + as->unknown092 = 16; + + entity_list.QueueCloseClients(this, outapp); + safe_delete(outapp); +} + +void Mob::ProjectileAnimation(Mob* to, uint16 item_id, bool IsArrow, float speed, float angle, float tilt, float arc) { + + const Item_Struct* item = NULL; + uint8 item_type = 0; + + if(!item_id) { + item = database.GetItem(8005); // Arrow will be default + } + else { + item = database.GetItem(item_id); // Use the item input into the command + } + + if(!item) { + return; + } + if(IsArrow) { + item_type = 27; + } + if(!item_type) { + item_type = item->ItemType; + } + if(!speed) { + speed = 4.0; + } + if(!angle) { + angle = CalculateHeadingToTarget(to->GetX(), to->GetY()) * 2; + } + if(!tilt) { + tilt = 125; + } + if(!arc) { + arc = 50; + } + + + // See SendItemAnimation() for some notes on this struct + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SomeItemPacketMaybe, sizeof(Arrow_Struct)); + Arrow_Struct *as = (Arrow_Struct *) outapp->pBuffer; + as->type = 1; + as->src_x = GetX(); + as->src_y = GetY(); + as->src_z = GetZ(); + as->source_id = GetID(); + as->target_id = to->GetID(); + as->item_id = item->ID; + as->item_type = item_type; + as->skill = 0; // Doesn't seem to have any effect + strn0cpy(as->model_name, item->IDFile, 16); + as->velocity = speed; + as->launch_angle = angle; + as->tilt = tilt; + as->arc = arc; + as->unknown088 = 125; + as->unknown092 = 16; + + entity_list.QueueCloseClients(this, outapp); + safe_delete(outapp); + +} + + +void NPC::DoClassAttacks(Mob *target) { + if(target == NULL) + return; //gotta have a target for all these + + bool taunt_time = taunt_timer.Check(); + bool ca_time = classattack_timer.Check(false); + bool ka_time = knightattack_timer.Check(false); + + //only check attack allowed if we are going to do something + if((taunt_time || ca_time || ka_time) && !IsAttackAllowed(target)) + return; + + if(ka_time){ + int knightreuse = 1000; //lets give it a small cooldown actually. + switch(GetClass()){ + case SHADOWKNIGHT: case SHADOWKNIGHTGM:{ + CastSpell(SPELL_NPC_HARM_TOUCH, target->GetID()); + knightreuse = HarmTouchReuseTime * 1000; + break; + } + case PALADIN: case PALADINGM:{ + if(GetHPRatio() < 20) { + CastSpell(SPELL_LAY_ON_HANDS, GetID()); + knightreuse = LayOnHandsReuseTime * 1000; + } else { + knightreuse = 2000; //Check again in two seconds. + } + break; + } + } + knightattack_timer.Start(knightreuse); + } + + //general stuff, for all classes.... + //only gets used when their primary ability get used too + if (taunting && HasOwner() && target->IsNPC() && target->GetBodyType() != BT_Undead && taunt_time) { + Taunt(target->CastToNPC(), false); + } + + if(!ca_time) + return; + + float HasteModifier = 0; + if(GetHaste() > 0) + HasteModifier = 10000 / (100 + GetHaste()); + else if(GetHaste() < 0) + HasteModifier = (100 - GetHaste()); + else + HasteModifier = 100; + + int level = GetLevel(); + 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 ROGUE: case ROGUEGM: + if(level >= 10) { + reuse = BackstabReuseTime * 1000; + TryBackstab(target, reuse); + did_attack = true; + } + break; + case MONK: case MONKGM: { + uint8 satype = KICK; + if(level > 29) { + satype = FLYING_KICK; + } else if(level > 24) { + satype = DRAGON_PUNCH; + } else if(level > 19) { + satype = EAGLE_STRIKE; + } else if(level > 9) { + satype = TIGER_CLAW; + } else if(level > 4) { + satype = ROUND_KICK; + } + reuse = MonkSpecialAttack(target, satype); + + reuse *= 1000; + did_attack = true; + break; + } + 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. + { + DoAnim(animKick); + int32 dmg = 0; + + if(GetWeaponDamage(target, (const Item_Struct*)NULL) <= 0){ + dmg = -5; + } + else{ + if(target->CheckHitChance(this, KICK, 0)) { + if(RuleB(Combat, UseIntervalAC)) + dmg = GetKickDamage(); + else + dmg = MakeRandomInt(1, GetKickDamage()); + + } + } + + reuse = KickReuseTime * 1000; + DoSpecialAttackDamage(target, KICK, dmg, 1, -1, reuse); + did_attack = true; + } + else + { + DoAnim(animTailRake); + int32 dmg = 0; + + if(GetWeaponDamage(target, (const Item_Struct*)NULL) <= 0){ + dmg = -5; + } + else{ + if(target->CheckHitChance(this, BASH, 0)) { + if(RuleB(Combat, UseIntervalAC)) + dmg = GetBashDamage(); + else + dmg = MakeRandomInt(1, GetBashDamage()); + } + } + + reuse = BashReuseTime * 1000; + DoSpecialAttackDamage(target, BASH, dmg, 1, -1, reuse); + did_attack = true; + } + } + break; + } + case BERSERKER: case BERSERKERGM: + { + int AtkRounds = 3; + int32 max_dmg = 26 + ((GetLevel()-6) * 2); + int32 min_dmg = 0; + DoAnim(anim2HSlashing); + + if (GetLevel() < 51) + min_dmg = 1; + else + min_dmg = GetLevel()*8/10; + + if (min_dmg > max_dmg) + max_dmg = min_dmg; + + reuse = FrenzyReuseTime * 1000; + + while(AtkRounds > 0) { + + if (GetTarget() && (AtkRounds == 1 || MakeRandomInt(0,100) < 75)){ + DoSpecialAttackDamage(GetTarget(), FRENZY, max_dmg, min_dmg, -1 , reuse, true); + } + AtkRounds--; + } + + + did_attack = true; + break; + } + case RANGER: case RANGERGM: + case BEASTLORD: case BEASTLORDGM: { + //kick + if(level >= RuleI(Combat, NPCBashKickLevel)){ + DoAnim(animKick); + int32 dmg = 0; + + if(GetWeaponDamage(target, (const Item_Struct*)NULL) <= 0){ + dmg = -5; + } + else{ + if(target->CheckHitChance(this, KICK, 0)) { + if(RuleB(Combat, UseIntervalAC)) + dmg = GetKickDamage(); + else + dmg = MakeRandomInt(1, GetKickDamage()); + } + } + + reuse = KickReuseTime * 1000; + DoSpecialAttackDamage(target, KICK, dmg, 1, -1, reuse); + did_attack = true; + } + break; + } + case CLERIC: case CLERICGM: //clerics can bash too. + case SHADOWKNIGHT: case SHADOWKNIGHTGM: + case PALADIN: case PALADINGM: + { + if(level >= RuleI(Combat, NPCBashKickLevel)){ + DoAnim(animTailRake); + int32 dmg = 0; + + if(GetWeaponDamage(target, (const Item_Struct*)NULL) <= 0){ + dmg = -5; + } + else{ + if(target->CheckHitChance(this, BASH, 0)) { + if(RuleB(Combat, UseIntervalAC)) + dmg = GetBashDamage(); + else + dmg = MakeRandomInt(1, GetBashDamage()); + } + } + + reuse = BashReuseTime * 1000; + DoSpecialAttackDamage(target, BASH, dmg, 1, -1, reuse); + did_attack = true; + } + break; + } + } + + classattack_timer.Start(reuse*HasteModifier/100); +} + +void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) +{ + if(!ca_target) + return; + + if(spellend_timer.Enabled() || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || dead) + return; + + if(!IsAttackAllowed(ca_target)) + return; + + //check range for all these abilities, they are all close combat stuff + if(!CombatRange(ca_target)) + { + return; + } + + if(!IsRiposte && (!p_timers.Expired(&database, pTimerCombatAbility, false))) { + return; + } + + int ReuseTime = 0; + int ClientHaste = GetHaste(); + int HasteMod = 0; + + 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()) + { + case WARRIOR: + case RANGER: + case BEASTLORD: + skill_to_use = KICK; + break; + case BERSERKER: + skill_to_use = FRENZY; + break; + case SHADOWKNIGHT: + case PALADIN: + skill_to_use = BASH; + break; + case MONK: + if(GetLevel() >= 30) + { + skill_to_use = FLYING_KICK; + } + else if(GetLevel() >= 25) + { + skill_to_use = DRAGON_PUNCH; + } + else if(GetLevel() >= 20) + { + skill_to_use = EAGLE_STRIKE; + } + else if(GetLevel() >= 10) + { + skill_to_use = TIGER_CLAW; + } + else if(GetLevel() >= 5) + { + skill_to_use = ROUND_KICK; + } + else + { + skill_to_use = KICK; + } + break; + case ROGUE: + skill_to_use = BACKSTAB; + break; + } + } + + else + skill_to_use = skill; + + if(skill_to_use == -1) + return; + + if(skill_to_use == BASH) + { + if (ca_target!=this) + { + DoAnim(animTailRake); + + if(GetWeaponDamage(ca_target, GetInv().GetItem(SLOT_SECONDARY)) <= 0 && + GetWeaponDamage(ca_target, GetInv().GetItem(SLOT_SHOULDER)) <= 0){ + dmg = -5; + } + else{ + if(!ca_target->CheckHitChance(this, BASH, 0)) { + dmg = 0; + } + else{ + if(RuleB(Combat, UseIntervalAC)) + dmg = GetBashDamage(); + else + dmg = MakeRandomInt(1, GetBashDamage()); + + } + } + + ReuseTime = BashReuseTime-1; + ReuseTime = (ReuseTime*HasteMod)/100; + + DoSpecialAttackDamage(ca_target, BASH, dmg, 1,-1,ReuseTime); + + if(ReuseTime > 0 && !IsRiposte) + { + p_timers.Start(pTimerCombatAbility, ReuseTime); + } + } + return; + } + + if(skill_to_use == FRENZY) + { + CheckIncreaseSkill(FRENZY, GetTarget(), 10); + int AtkRounds = 3; + int skillmod = 100*GetSkill(FRENZY)/MaxSkill(FRENZY); + int32 max_dmg = (26 + ((((GetLevel()-6) * 2)*skillmod)/100)) * ((100+RuleI(Combat, FrenzyBonus))/100); + int32 min_dmg = 0; + DoAnim(anim2HSlashing); + + if (GetLevel() < 51) + min_dmg = 1; + else + min_dmg = GetLevel()*8/10; + + if (min_dmg > max_dmg) + max_dmg = min_dmg; + + ReuseTime = FrenzyReuseTime-1; + ReuseTime = (ReuseTime*HasteMod)/100; + + //Live parses show around 55% Triple 35% Double 10% Single, you will always get first hit. + while(AtkRounds > 0) { + + if (GetTarget() && (AtkRounds == 1 || MakeRandomInt(0,100) < 75)){ + DoSpecialAttackDamage(GetTarget(), FRENZY, max_dmg, min_dmg, max_dmg , ReuseTime, true); + } + AtkRounds--; + } + + if(ReuseTime > 0 && !IsRiposte) { + p_timers.Start(pTimerCombatAbility, ReuseTime); + } + return; + } + + if(skill_to_use == KICK) + { + if(ca_target!=this) + { + DoAnim(animKick); + + if(GetWeaponDamage(ca_target, GetInv().GetItem(SLOT_FEET)) <= 0){ + dmg = -5; + } + else{ + if(!ca_target->CheckHitChance(this, KICK, 0)) { + dmg = 0; + } + else{ + if(RuleB(Combat, UseIntervalAC)) + dmg = GetKickDamage(); + else + dmg = MakeRandomInt(1, GetKickDamage()); + } + } + + ReuseTime = KickReuseTime-1; + + DoSpecialAttackDamage(ca_target, KICK, dmg, 1,-1, ReuseTime); + } + } + + if(skill_to_use == FLYING_KICK || + skill_to_use == DRAGON_PUNCH || + skill_to_use == EAGLE_STRIKE || + skill_to_use == TIGER_CLAW || + skill_to_use == ROUND_KICK) + { + ReuseTime = MonkSpecialAttack(ca_target, skill_to_use) - 1; + MonkSpecialAttack(ca_target, skill_to_use); + + if (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] = { FLYING_KICK, DRAGON_PUNCH, EAGLE_STRIKE, TIGER_CLAW, ROUND_KICK }; + 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)]); + } + } + } + + if(skill_to_use == BACKSTAB) + { + ReuseTime = BackstabReuseTime-1; + + if (IsRiposte) + ReuseTime=0; + + TryBackstab(ca_target,ReuseTime); + } + + ReuseTime = (ReuseTime*HasteMod)/100; + if(ReuseTime > 0 && !IsRiposte) + { + p_timers.Start(pTimerCombatAbility, ReuseTime); + } +} + +/* +void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) { + + if (who == NULL) + return; + + if(DivineAura()) + return; + + if(!CombatRange(who)) + return; + + if(!always_succeed && IsClient()) + CastToClient()->CheckIncreaseSkill(TAUNT, who, 10); + + int level = GetLevel(); + Mob *hate_top = who->GetHateMost(); + + // Check to see if we're already at the top of the target's hate list + // a mob will not be taunted if its target's health is below 20% + if ((hate_top != this) + && (who->GetLevel() < level) + && (hate_top == NULL || hate_top->GetHPRatio() >= 20) ) { + int32 newhate, tauntvalue; + + float tauntchance; + if(always_succeed) { + tauntchance = 101; + } else { + + // no idea how taunt success is actually calculated + // TODO: chance for level 50+ mobs should be lower + int level_difference = level - who->GetLevel(); + if (level_difference <= 5) { + tauntchance = 25.0; // minimum + tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0; // skill modifier + if (tauntchance > 65.0) + tauntchance = 65.0; + } + else if (level_difference <= 10) { + tauntchance = 30.0; // minimum + tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0; // skill modifier + if (tauntchance > 85.0) + tauntchance = 85.0; + } + else if (level_difference <= 15) { + tauntchance = 40.0; // minimum + tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0; // skill modifier + if (tauntchance > 90.0) + tauntchance = 90.0; + } + else { + tauntchance = 50.0; // minimum + tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0; // skill modifier + if (tauntchance > 95.0) + tauntchance = 95.0; + } + } + + if (chance_bonus) + tauntchance = tauntchance + (tauntchance*chance_bonus/100.0f); + + if (tauntchance > MakeRandomFloat(0, 100)) { + // this is the max additional hate added per succesfull taunt + tauntvalue = (MakeRandomInt(2, 4) * level); + //tauntvalue = (int32) ((float)level * 10.0 * (float)rand()/(float)RAND_MAX + 1); + // new hate: find diff of player's hate and whoever's at top of list, add that plus tauntvalue to players hate + newhate = who->GetNPCHate(hate_top) - who->GetNPCHate(this) + tauntvalue; + // add the hate + who->CastToNPC()->AddToHateList(this, newhate); + } + else{ + //generate at least some hate reguardless of the outcome. + who->CastToNPC()->AddToHateList(this, (MakeRandomInt(2, 4)*level)); + } + } + + //generate at least some hate reguardless of the outcome. + who->CastToNPC()->AddToHateList(this, (MakeRandomInt(2, 4)*level)); + if (HasSkillProcs()){ + float chance = (float)TauntReuseTime*RuleR(Combat, AvgProcsPerMinute)/60000.0f; + TrySkillProc(who, TAUNT, chance); + } +} +*/ + +void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) { + + if (who == NULL) + return; + + if(DivineAura()) + return; + + if(!CombatRange(who)) + return; + + if(!always_succeed && IsClient()) + CastToClient()->CheckIncreaseSkill(TAUNT, who, 10); + + Mob *hate_top = who->GetHateMost(); + + float level_difference = GetLevel() - who->GetLevel(); + + //Support for how taunt worked pre 2000 on LIVE - Can not taunt NPC over your level. + if ((RuleB(Combat,TauntOverLevel) == false) && (level_difference < 0) || who->SpecAttacks[IMMUNE_TAUNT]){ + Message_StringID(MT_SpellFailure,FAILED_TAUNT); + return; + } + + //All values used based on live parses after taunt was updated in 2006. + if ((hate_top && hate_top->GetHPRatio() >= 20) || hate_top == NULL) { + + int32 newhate = 0; + float tauntchance = 50.0f; + + if(always_succeed) + tauntchance = 101.0f; + + else { + + if (level_difference < 0){ + tauntchance += level_difference*3; + if (tauntchance < 20) + tauntchance = 20.0f; + } + + else { + tauntchance += level_difference*5; + if (tauntchance > 65) + tauntchance = 65.0f; + } + } + + //TauntSkillFalloff rate is not based on any real data. Default of 33% gives a reasonable result. + if (IsClient() && !always_succeed) + tauntchance -= (RuleR(Combat,TauntSkillFalloff) * (CastToClient()->MaxSkill(TAUNT) - GetSkill(TAUNT))); + + //From SE_Taunt (Does a taunt with a chance modifier) + if (chance_bonus) + tauntchance += tauntchance*chance_bonus/100.0f; + + if (tauntchance < 1) + tauntchance = 1.0f; + + tauntchance /= 100.0f; + + 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); + } + else + who->CastToNPC()->AddToHateList(this,12); + + if (who->CanTalk()) + who->Say_StringID(SUCCESSFUL_TAUNT,GetCleanName()); + } + else{ + Message_StringID(MT_SpellFailure,FAILED_TAUNT); + } + } + + else + Message_StringID(MT_SpellFailure,FAILED_TAUNT); + + if (HasSkillProcs()){ + float chance = (float)TauntReuseTime*RuleR(Combat, AvgProcsPerMinute)/60000.0f; + TrySkillProc(who, TAUNT, chance); + } +} + + +void Mob::InstillDoubt(Mob *who) { + //make sure we can use this skill + /*int skill = GetSkill(INTIMIDATION);*/ //unused + + //make sure our target is an NPC + if(!who || !who->IsNPC()) + return; + + if(DivineAura()) + return; + + //range check + if(!CombatRange(who)) + return; + + if(IsClient()) + { + CastToClient()->CheckIncreaseSkill(INTIMIDATION, who, 10); + } + + //I think this formula needs work + int value = 0; + + //user's bonus + value += GetSkill(INTIMIDATION) + GetCHA()/4; + + //target's counters + value -= target->GetLevel()*4 + who->GetWIS()/4; + + if (MakeRandomInt(0,99) < value) { + //temporary hack... + //cast fear on them... should prolly be a different spell + //and should be un-resistable. + SpellOnTarget(229, who, false, true, -2000); + //is there a success message? + } else { + Message_StringID(4,NOT_SCARING); + //Idea from WR: + /* if (target->IsNPC() && MakeRandomInt(0,99) < 10 ) { + entity_list.MessageClose(target, false, 50, MT_NPCRampage, "%s lashes out in anger!",target->GetName()); + //should we actually do this? and the range is completely made up, unconfirmed + entity_list.AEAttack(target, 50); + }*/ + } +} + +bool Mob::TryHeadShot(Mob* defender, SkillType skillInUse) { + bool Result = false; + + if(defender && skillInUse == ARCHERY) { + if(GetAA(aaHeadshot) && defender->GetBodyType() == BT_Humanoid) { + if((GetLevelCon(GetLevel(), defender->GetLevel()) == CON_LIGHTBLUE || GetLevelCon(GetLevel(), defender->GetLevel()) == CON_GREEN) && defender->GetLevel() <= 60 && !defender->IsClient()) { + // WildcardX: These chance formula's below are arbitrary. If someone has a better formula that is more + // consistent with live, feel free to update these. + int AttackerChance = 20 + ((GetLevel() - 51) / 2) + (itembonuses.HeroicDEX / 10); + int DefenderChance = MakeRandomInt(0, 100); + if(AttackerChance > DefenderChance) { + mlog(COMBAT__ATTACKS, "Landed a headshot: Attacker chance was %f and Defender chance was %f.", AttackerChance, DefenderChance); + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FATAL_BOW_SHOT, GetName()); + defender->Damage(this, 32000, SPELL_UNKNOWN, skillInUse); + Result = true; + } + else { + mlog(COMBAT__ATTACKS, "FAILED a headshot: Attacker chance was %f and Defender chance was %f.", AttackerChance, DefenderChance); + } + } + } + } + + return Result; +} + +void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillType skillinuse, int16 chance_mod, int16 focus, bool CanRiposte) +{ + 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. + if (skillinuse == BEGGING) + skillinuse = OFFENSE; + + int damage = 0; + uint32 hate = 0; + int Hand = 13; + if (hate == 0 && weapon_damage > 1) hate = weapon_damage; + + if(weapon_damage > 0){ + + if(GetClass() == BERSERKER){ + int bonus = 3 + GetLevel()/10; + weapon_damage = weapon_damage * (100+bonus) / 100; + } + + int32 min_hit = 1; + int32 max_hit = (2*weapon_damage*GetDamageTable(skillinuse)) / 100; + + if(GetLevel() >= 28 && IsWarriorClass() ) + { + int ucDamageBonus = GetWeaponDamageBonus((const Item_Struct*) NULL ); + + min_hit += (int) ucDamageBonus; + max_hit += (int) ucDamageBonus; + hate += ucDamageBonus; + } + + ApplySpecialAttackMod(skillinuse, max_hit, min_hit); + + min_hit += min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100; + + if(max_hit < min_hit) + max_hit = min_hit; + + if(RuleB(Combat, UseIntervalAC)) + damage = max_hit; + else + damage = MakeRandomInt(min_hit, max_hit); + + if(!other->CheckHitChance(this, skillinuse, Hand, chance_mod)) { + damage = 0; + } else { + other->AvoidDamage(this, damage, CanRiposte); + other->MeleeMitigation(this, damage, min_hit); + if(damage > 0) { + damage += damage*focus/100; + ApplyMeleeDamageBonus(skillinuse, damage); + damage += other->GetAdditionalDamage(this, 0, true, skillinuse); + damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse); + TryCriticalHit(other, skillinuse, damage); + } + } + + if (damage == -3) { + DoRiposte(other); + if (HasDied()) + return; + } + } + + else + damage = -5; + + if(skillinuse == BASH){ + if(IsClient()){ + ItemInst *item = CastToClient()->GetInv().GetItem(SLOT_SECONDARY); + if(item){ + if(item->GetItem()->ItemType == ItemTypeShield) { + hate += item->GetItem()->AC; + } + const Item_Struct *itm = item->GetItem(); + hate = hate * (100 + GetFuriousBash(itm->Focus.Effect)) / 100; + } + } + } + + other->AddToHateList(this, hate); + + bool CanSkillProc = true; + if (skillinuse == OFFENSE){ //Hack to allow damage to display. + skillinuse = TIGER_CLAW; //'strike' your opponent - Arbitrary choice for message. + CanSkillProc = false; //Disable skill procs + } + + other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); + + if (HasDied()) + return; + + if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skillinuse){ + int kb_chance = 25; + kb_chance += kb_chance*(100-aabonuses.SpecialAttackKBProc[0])/100; + + if (MakeRandomInt(0, 99) < kb_chance) + SpellFinished(904, other, 10, 0, -1, spells[904].ResistDiff); + } + + if (CanSkillProc && HasSkillProcs()){ + float chance = 10.0f*RuleR(Combat, AvgProcsPerMinute)/60000.0f; + TrySkillProc(other, skillinuse, chance); + } +} + +bool Mob::CanDoSpecialAttack(Mob *other) +{ + //Make sure everything is valid before doing any attacks. + if (!other) { + SetTarget(NULL); + return false; + } + + if(!GetTarget()) + SetTarget(other); + + if ((other == NULL || ((IsClient() && CastToClient()->dead) || (other->IsClient() && other->CastToClient()->dead)) + || HasDied() || (!IsAttackAllowed(other)))) { + return false; + } + + if(other->GetInvul() || other->SpecAttacks[IMMUNE_MELEE]) + return false; + + return true; +} diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp new file mode 100644 index 000000000..cd3a1b6a8 --- /dev/null +++ b/zone/spell_effects.cpp @@ -0,0 +1,5394 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2004 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 "../common/debug.h" +#include "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/rulesys.h" +#include +#include +#ifndef WIN32 +#include +#include "../common/unix.h" +#endif + +#include "StringIDs.h" +#include "QuestParserCollection.h" + +extern Zone* zone; +extern volatile bool ZoneLoaded; +#if !defined(NEW_LoadSPDat) && !defined(DB_LoadSPDat) + extern SPDat_Spell_Struct spells[SPDAT_RECORDS]; +#endif +extern bool spells_loaded; +extern WorldServer worldserver; +//uchar blah[]={0x0D,0x00,0x00,0x00,0x01,0x00,0x00,0x00}; +//uchar blah2[]={0x12,0x00,0x00,0x00,0x16,0x01,0x00,0x00}; + + +// the spell can still fail here, if the buff can't stack +// in this case false will be returned, true otherwise +bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) +{ + _ZP(Mob_SpellEffect); + + int caster_level, buffslot, effect, effect_value, i; + ItemInst *SummonedItem=NULL; +#ifdef SPELL_EFFECT_SPAM +#define _EDLEN 200 + char effect_desc[_EDLEN]; +#endif + + if(!IsValidSpell(spell_id)) + return false; + + const SPDat_Spell_Struct &spell = spells[spell_id]; + + bool c_override = false; + if(caster && caster->IsClient() && GetCastedSpellInvSlot() > 0) + { + const ItemInst* inst = caster->CastToClient()->GetInv().GetItem(GetCastedSpellInvSlot()); + if(inst) + { + if(inst->GetItem()->Click.Level > 0) + { + caster_level = inst->GetItem()->Click.Level; + c_override = true; + } + else + { + caster_level = caster ? caster->GetCasterLevel(spell_id) : GetCasterLevel(spell_id); + } + } + else + caster_level = caster ? caster->GetCasterLevel(spell_id) : GetCasterLevel(spell_id); + } + else + caster_level = caster ? caster->GetCasterLevel(spell_id) : GetCasterLevel(spell_id); + + if(c_override) + { + int durat = CalcBuffDuration(caster, this, spell_id, caster_level); + if((durat-1) > 0) + { + buffslot = AddBuff(caster, spell_id, durat, caster_level); + if(buffslot == -1) // stacking failure + return false; + } + else + { + buffslot = -2; + } + } + else + { + if((CalcBuffDuration(caster,this,spell_id)-1) > 0){ + if(IsEffectInSpell(spell_id, SE_BindSight)) + { + if(caster) + { + buffslot = caster->AddBuff(caster, spell_id); + } + else + buffslot = -1; + } + else + { + buffslot = AddBuff(caster, spell_id); + } + if(buffslot == -1) // stacking failure + return false; + } else { + buffslot = -2; //represents not a buff I guess + } + } + +#ifdef SPELL_EFFECT_SPAM + Message(0, "You are affected by spell '%s' (id %d)", spell.name, spell_id); + if(buffslot >= 0) + { + Message(0, "Buff slot: %d Duration: %d tics", buffslot, buffs[buffslot].ticsremaining); + } +#endif + + if(buffslot >= 0) + { + buffs[buffslot].melee_rune = 0; + buffs[buffslot].magic_rune = 0; + buffs[buffslot].numhits = 0; + + if(IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) + { + EQApplicationPacket *outapp = MakeBuffsPacket(false); + CastToClient()->FastQueuePacket(&outapp); + } + } + + if(IsNPC()) + { + if(parse->SpellHasQuestSub(spell_id, "EVENT_SPELL_EFFECT_NPC")) + { + parse->EventSpell(EVENT_SPELL_EFFECT_NPC, CastToNPC(), NULL, spell_id, caster ? caster->GetID() : 0); + CalcBonuses(); + return true; + } + } + else if(IsClient()) + { + if(parse->SpellHasQuestSub(spell_id, "EVENT_SPELL_EFFECT_CLIENT")) + { + parse->EventSpell(EVENT_SPELL_EFFECT_CLIENT, NULL, CastToClient(), spell_id, caster ? caster->GetID() : 0); + CalcBonuses(); + return true; + } + } + + if(spells[spell_id].viral_targets > 0) { + if(!viral_timer.Enabled()) + viral_timer.Start(1000); + + has_virus = true; + for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) + { + if(!viral_spells[i]) + { + viral_spells[i] = spell_id; + viral_spells[i+1] = caster->GetID(); + break; + } + } + } + + if(spells[spell_id].numhits > 0 && buffslot >= 0){ + + int numhit = spells[spell_id].numhits; + + if (caster && caster->IsClient()) + numhit += caster->CastToClient()->GetFocusEffect(focusIncreaseNumHits, spell_id); + buffs[buffslot].numhits = numhit; + } + + // iterate through the effects in the spell + for (i = 0; i < EFFECT_COUNT; i++) + { + if(IsBlankSpellEffect(spell_id, i)) + continue; + + effect = spell.effectid[i]; + effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster ? caster : this); + + if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands)) + effect_value = GetMaxHP(); + +#ifdef SPELL_EFFECT_SPAM + effect_desc[0] = 0; +#endif + + switch(effect) + { + case SE_CurrentHP: // nukes, heals; also regen/dot if a buff + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Current Hitpoints: %+i", effect_value); +#endif + // SE_CurrentHP is calculated at first tick if its a dot/buff + if (buffslot >= 0) + break; + + // for offensive spells check if we have a spell rune on + int32 dmg = effect_value; + if(dmg < 0) + { + // take partial damage into account + dmg = (int32) (dmg * partial / 100); + + //handles AAs and what not... + if(caster) + { + dmg = GetVulnerability(dmg, caster, spell_id, 0); + dmg -= GetAdditionalDamage(caster, spell_id); + dmg = caster->GetActSpellDamage(spell_id, dmg); + } + dmg = -dmg; + Damage(caster, dmg, spell_id, spell.skill, false, buffslot, false); + } + else if(dmg > 0) { + //healing spell... + if(caster) + dmg = caster->GetActSpellHealing(spell_id, dmg); + + HealDamage(dmg, caster); + } + +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Current Hitpoints: %+i actual: %+i", effect_value, dmg); +#endif + break; + } + + case SE_CurrentHPOnce: // used in buffs usually, see Courage + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Current Hitpoints Once: %+i", effect_value); +#endif + + int32 dmg = effect_value; + if (spell_id == 2751 && caster) //Manaburn + { + dmg = caster->GetMana()*-3; + caster->SetMana(0); + } else if (spell_id == 2755 && caster) //Lifeburn + { + dmg = caster->GetHP()*-15/10; + caster->SetHP(1); + if(caster->IsClient()){ + caster->CastToClient()->SetFeigned(true); + caster->SendAppearancePacket(AT_Anim, 115); + } + } + + //do any AAs apply to these spells? + if(dmg < 0) { + dmg = -dmg; + Damage(caster, dmg, spell_id, spell.skill, false, buffslot, false); + } else { + HealDamage(dmg, caster); + } + break; + } + + case SE_PercentalHeal: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Percental Heal: %+i (%d%% max)", spell.max[i], effect_value); +#endif + //im not 100% sure about this implementation. + //the spell value forumula dosent work for these... at least spell 3232 anyways + int32 val = spell.max[i]; + + if(caster) + val = caster->GetActSpellHealing(spell_id, val); + + int32 mhp = GetMaxHP(); + int32 cap = mhp * spell.base[i] / 100; + + if(cap < val) + val = cap; + + if(val > 0) + HealDamage(val, caster); + + break; + } + + case SE_CompleteHeal: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Complete Heal"); +#endif + //make sure they are not allready affected by this... + //I think that is the point of making this a buff. + //this is in the wrong spot, it should be in the immune + //section so the buff timer does not get refreshed! + + int i; + bool inuse = false; + uint32 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."); + inuse = true; + break; + } + } + if(inuse) + break; + + Heal(); + break; + } + + case SE_CurrentMana: + { + if(IsManaTapSpell(spell_id)) { + if(GetCasterClass() != 'N') { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Current Mana: %+i", effect_value); +#endif + SetMana(GetMana() + effect_value); + caster->SetMana(caster->GetMana() + abs(effect_value)); +#ifdef SPELL_EFFECT_SPAM + caster->Message(0, "You have gained %+i mana!", effect_value); +#endif + } + } + else { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Current Mana: %+i", effect_value); +#endif + if (buffslot >= 0) + break; + + SetMana(GetMana() + effect_value); + } + + break; + } + + case SE_CurrentManaOnce: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Current Mana Once: %+i", effect_value); +#endif + SetMana(GetMana() + effect_value); + break; + } + + case SE_Translocate: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Translocate: %s %d %d %d heading %d", + spell.teleport_zone, spell.base[1], spell.base[0], + spell.base[2], spell.base[3] + ); +#endif + if(IsClient()) + { + + if(caster) + CastToClient()->SendOPTranslocateConfirm(caster, spell_id); + + } + break; + } + + case SE_Succor: + { + 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]; + + if(!strcmp(spell.teleport_zone, "same")) + { + target_zone = 0; + } + else + { + target_zone = spell.teleport_zone; + if(IsNPC() && target_zone != zone->GetShortName()){ + if(!GetOwner()){ + CastToNPC()->Depop(); + break; + }else{ + if(!GetOwner()->IsClient()) + CastToNPC()->Depop(); + break; + } + } + } + + if(IsClient()) + { + // Below are the spellid's for known evac/succor spells that send player + // to the current zone's safe points. + + // Succor = 1567 + // Lesser Succor = 2183 + // Evacuate = 1628 + // Lesser Evacuate = 2184 + // Decession = 2558 + // Greater Decession = 3244 + // Egress = 1566 + + if(!target_zone) { +#ifdef SPELL_EFFECT_SPAM + LogFile->write(EQEMuLog::Debug, "Succor/Evacuation Spell In Same Zone."); +#endif + if(IsClient()) + CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), x, y, z, heading, 0, EvacToSafeCoords); + else + GMMove(x, y, z, heading); + } + else { +#ifdef SPELL_EFFECT_SPAM + LogFile->write(EQEMuLog::Debug, "Succor/Evacuation Spell To Another Zone."); +#endif + if(IsClient()) + CastToClient()->MovePC(target_zone, x, y, z, heading); + } + } + + break; + } + case SE_YetAnotherGate: //Shin: Used on Teleport Bind. + case SE_Teleport: // gates, rings, circles, etc + case SE_Teleport2: + { + 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]; + + if(!strcmp(spell.teleport_zone, "same")) + { + target_zone = 0; + } + else + { + target_zone = spell.teleport_zone; + + if(IsNPC() && target_zone != zone->GetShortName()){ + if(!GetOwner()){ + CastToNPC()->Depop(); + break; + }else{ + if(!GetOwner()->IsClient()) + CastToNPC()->Depop(); + break; + } + } + } + + if (effect == SE_YetAnotherGate && caster->IsClient()) + { //Shin: Teleport Bind uses caster's bind point + x = caster->CastToClient()->GetBindX(); + y = caster->CastToClient()->GetBindY(); + z = caster->CastToClient()->GetBindZ(); + heading = caster->CastToClient()->GetBindHeading(); + //target_zone = caster->CastToClient()->GetBindZoneId(); target_zone doesn't work due to const char + CastToClient()->MovePC(caster->CastToClient()->GetBindZoneID(), 0, x, y, z, heading); + break; + } + +#ifdef SPELL_EFFECT_SPAM + const char *efstr = "Teleport"; + if(effect == SE_Teleport) + efstr = "Teleport v1"; + else if(effect == SE_Teleport2) + efstr = "Teleport v2"; + else if(effect == SE_Succor) + efstr = "Succor"; + + snprintf(effect_desc, _EDLEN, + "%s: %0.2f, %0.2f, %0.2f heading %0.2f in %s", + efstr, x, y, z, heading, target_zone ? target_zone : "same zone" + ); +#endif + if(IsClient()) + { + if(!target_zone) + CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), x, y, z, heading); + else + CastToClient()->MovePC(target_zone, x, y, z, heading); + } + else{ + if(!target_zone) + GMMove(x, y, z, heading); + } + break; + } + + case SE_Invisibility: + case SE_Invisibility2: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Invisibility"); +#endif + SetInvisible(spell.base[i]); + break; + } + + case SE_InvisVsAnimals: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Invisibility to Animals"); +#endif + invisible_animals = true; + break; + } + + case SE_InvisVsUndead2: + case SE_InvisVsUndead: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Invisibility to Undead"); +#endif + invisible_undead = true; + break; + } + case SE_SeeInvis: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "See Invisible"); +#endif + see_invis = spell.base[i]; + break; + } + + case SE_FleshToBone: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Flesh To Bone"); +#endif + if(IsClient()){ + ItemInst* transI = CastToClient()->GetInv().GetItem(SLOT_CURSOR); + if(transI && transI->IsType(ItemClassCommon) && transI->IsStackable()){ + uint32 fcharges = transI->GetCharges(); + //Does it sound like meat... maybe should check if it looks like meat too... + if(strstr(transI->GetItem()->Name, "meat") || + strstr(transI->GetItem()->Name, "Meat") || + strstr(transI->GetItem()->Name, "flesh") || + strstr(transI->GetItem()->Name, "Flesh") || + strstr(transI->GetItem()->Name, "parts") || + strstr(transI->GetItem()->Name, "Parts")){ + CastToClient()->DeleteItemInInventory(SLOT_CURSOR, fcharges, true); + CastToClient()->SummonItem(13073, fcharges); + } + else{ + Message(13, "You can only transmute flesh to bone."); + } + } + else{ + Message(13, "You can only transmute flesh to bone."); + } + } + break; + } + + case SE_GroupFearImmunity:{ +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Group Fear Immunity"); +#endif + //Added client messages to give some indication this effect is active. + uint32 group_id_caster = 0; + uint32 time = spell.base[i]*10; + if(caster->IsClient()) + { + if(caster->IsGrouped()) + { + group_id_caster = GetGroup()->GetID(); + } + else if(caster->IsRaidGrouped()) + { + group_id_caster = (GetRaid()->GetGroup(CastToClient()) == 0xFFFF) ? 0 : (GetRaid()->GetGroup(CastToClient()) + 1); + } + } + if(group_id_caster){ + Group *g = entity_list.GetGroupByID(group_id_caster); + uint32 time = spell.base[i]*10; + if(g){ + for(int gi=0; gi < 6; gi++){ + if(g->members[gi] && g->members[gi]->IsClient()) + { + g->members[gi]->CastToClient()->EnableAAEffect(aaEffectWarcry , time); + if (g->members[gi]->GetID() != caster->GetID()) + g->members[gi]->Message(13, "You hear the war cry."); + else + Message(13, "You let loose a fierce war cry."); + } + } + } + } + + else{ + CastToClient()->EnableAAEffect(aaEffectWarcry , time); + Message(13, "You let loose a fierce war cry."); + } + + break; + } + + case SE_AddFaction: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Faction Mod: %+i", effect_value); +#endif + // EverHood + if(caster && GetPrimaryFaction()>0) { + caster->AddFactionBonus(GetPrimaryFaction(),spell.base[0]); + } + break; + } + + case SE_Stun: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Stun: %d msec", effect_value); +#endif + //Typically we check for immunities else where but since stun immunities are different and only + //Block the stun part and not the whole spell, we do it here, also do the message here so we wont get the message on a resist + int max_level = spell.max[i]; + //max_level of 0 means we assume a default of 55. + if (max_level == 0) + max_level = RuleI(Spells, BaseImmunityLevel); + // NPCs get to ignore max_level for their spells. + if(SpecAttacks[UNSTUNABLE] || + ((GetLevel() > max_level) + && caster && (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity))))) + { + caster->Message_StringID(MT_SpellFailure, IMMUNE_STUN); + } + else + { + int stun_resist = itembonuses.StunResist+spellbonuses.StunResist; + if(IsClient()) + stun_resist += aabonuses.StunResist; + + if(stun_resist <= 0 || MakeRandomInt(0,99) >= stun_resist) + { + mlog(COMBAT__HITS, "Stunned. We had %d percent resist chance.", stun_resist); + Stun(effect_value); + } + else { + if(IsClient()) + Message_StringID(MT_Stun, SHAKE_OFF_STUN); + + mlog(COMBAT__HITS, "Stun Resisted. We had %d percent resist chance.", stun_resist); + } + } + break; + } + + case SE_Charm: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Charm: %+i (up to lvl %d)", effect_value, spell.max[i]); +#endif + + if (!caster) // can't be someone's pet unless we know who that someone is + break; + + if(IsNPC()) + { + CastToNPC()->SaveGuardSpotCharm(); + } + InterruptSpell(); + entity_list.RemoveDebuffs(this); + entity_list.RemoveFromTargets(this); + WipeHateList(); + + if (IsClient() && caster->IsClient()) { + caster->Message(0, "Unable to cast charm on a fellow player."); + BuffFadeByEffect(SE_Charm); + break; + } else if(IsCorpse()) { + caster->Message(0, "Unable to cast charm on a corpse."); + BuffFadeByEffect(SE_Charm); + break; + } else if(caster->GetPet() != NULL && caster->IsClient()) { + caster->Message(0, "You cannot charm something when you already have a pet."); + BuffFadeByEffect(SE_Charm); + break; + } else if(GetOwner()) { + caster->Message(0, "You cannot charm someone else's pet!"); + BuffFadeByEffect(SE_Charm); + break; + } + + Mob *my_pet = GetPet(); + if(my_pet) + { + my_pet->Kill(); + } + + caster->SetPet(this); + SetOwnerID(caster->GetID()); + SetPetOrder(SPO_Follow); + + if(caster->IsClient()){ + EQApplicationPacket *app = new EQApplicationPacket(OP_Charm, sizeof(Charm_Struct)); + Charm_Struct *ps = (Charm_Struct*)app->pBuffer; + ps->owner_id = caster->GetID(); + ps->pet_id = this->GetID(); + ps->command = 1; + entity_list.QueueClients(this, app); + safe_delete(app); + SendPetBuffsToClient(); + } + + if (IsClient()) + { + AI_Start(); + SendAppearancePacket(14, 100, true, true); + } else if(IsNPC()) { + CastToNPC()->SetPetSpellID(0); //not a pet spell. + } + + bool bBreak = false; + + // define spells with fixed duration + // charm spells with -1 in field 209 are all of fixed duration, so lets use that instead of spell_ids + if(spells[spell_id].field209 == -1) + bBreak = true; + + if (!bBreak) + { + int resistMod = partial + (GetCHA()/25); + resistMod = resistMod > 100 ? 100 : resistMod; + buffs[buffslot].ticsremaining = resistMod * buffs[buffslot].ticsremaining / 100; + } + + if(IsClient()) + { + if(buffs[buffslot].ticsremaining > RuleI(Character, MaxCharmDurationForPlayerCharacter)) + buffs[buffslot].ticsremaining = RuleI(Character, MaxCharmDurationForPlayerCharacter); + } + + break; + } + + + case SE_SenseDead: + case SE_SenseSummoned: + case SE_SenseAnimals: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Sense Target: %+i", effect_value); +#endif + if(IsClient()) + { + CastToClient()->SetSenseExemption(true); + + if(CastToClient()->GetClientVersionBit() & BIT_SoDAndLater) + { + bodyType bt = BT_Undead; + + int MessageID = SENSE_UNDEAD; + + if(effect == SE_SenseSummoned) + { + bt = BT_Summoned; + MessageID = SENSE_SUMMONED; + } + else if(effect == SE_SenseAnimals) + { + bt = BT_Animal; + MessageID = SENSE_ANIMAL; + } + + Mob *ClosestMob = entity_list.GetClosestMobByBodyType(this, bt); + + if(ClosestMob) + { + Message_StringID(MT_Spells, MessageID); + SetHeading(CalculateHeadingToTarget(ClosestMob->GetX(), ClosestMob->GetY())); + SetTarget(ClosestMob); + CastToClient()->SendTargetCommand(ClosestMob->GetID()); + SendPosUpdate(2); + } + else + Message_StringID(clientMessageError, SENSE_NOTHING); + } + } + break; + } + + case SE_Fear: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Fear: %+i", effect_value); +#endif + //use resistance value for duration... + buffs[buffslot].ticsremaining = ((buffs[buffslot].ticsremaining * partial) / 100); + + if(IsClient()) + { + if(buffs[buffslot].ticsremaining > RuleI(Character, MaxFearDurationForPlayerCharacter)) + buffs[buffslot].ticsremaining = RuleI(Character, MaxFearDurationForPlayerCharacter); + } + + if(RuleB(Combat, EnableFearPathing)){ + if(IsClient()) + { + AI_Start(); + animation = GetRunspeed() * 21; //set our animation to match our speed about + } + + CalculateNewFearpoint(); + if(curfp) + { + break; + } + } + else + { + Stun(buffs[buffslot].ticsremaining * 6000 - (6000 - tic_timer.GetRemainingTime())); + } + break; + } + + case SE_BindAffinity: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Bind Affinity"); +#endif + if (IsClient()) + { + if(CastToClient()->GetGM() || RuleB(Character, BindAnywhere)) + { + EQApplicationPacket *action_packet = new EQApplicationPacket(OP_Action, sizeof(Action_Struct)); + Action_Struct* action = (Action_Struct*) action_packet->pBuffer; + EQApplicationPacket *message_packet = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); + CombatDamage_Struct *cd = (CombatDamage_Struct *)message_packet->pBuffer; + + action->target = GetID(); + action->source = caster ? caster->GetID() : GetID(); + action->level = 65; + action->instrument_mod = 10; + action->sequence = (GetHeading() * 12345 / 2); + action->type = 231; + action->spell = spell_id; + action->buff_unknown = 4; + + cd->target = action->target; + cd->source = action->source; + cd->type = action->type; + cd->spellid = action->spell; + cd->sequence = action->sequence; + + CastToClient()->QueuePacket(action_packet); + if(caster->IsClient() && caster != this) + caster->CastToClient()->QueuePacket(action_packet); + + CastToClient()->QueuePacket(message_packet); + if(caster->IsClient() && caster != this) + caster->CastToClient()->QueuePacket(message_packet); + + CastToClient()->SetBindPoint(); + Save(); + safe_delete(action_packet); + safe_delete(message_packet); + } + else + { + if(!zone->CanBind()) + { + Message_StringID(MT_SpellFailure, CANNOT_BIND); + break; + } + if(!zone->IsCity()) + { + if(caster != this) + { + Message_StringID(MT_SpellFailure, CANNOT_BIND); + break; + } + else + { + EQApplicationPacket *action_packet = new EQApplicationPacket(OP_Action, sizeof(Action_Struct)); + Action_Struct* action = (Action_Struct*) action_packet->pBuffer; + EQApplicationPacket *message_packet = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); + CombatDamage_Struct *cd = (CombatDamage_Struct *)message_packet->pBuffer; + + action->target = GetID(); + action->source = caster ? caster->GetID() : GetID(); + action->level = 65; + action->instrument_mod = 10; + action->sequence = (GetHeading() * 12345 / 2); + action->type = 231; + action->spell = spell_id; + action->buff_unknown = 4; + + cd->target = action->target; + cd->source = action->source; + cd->type = action->type; + cd->spellid = action->spell; + cd->sequence = action->sequence; + + CastToClient()->QueuePacket(action_packet); + if(caster->IsClient() && caster != this) + caster->CastToClient()->QueuePacket(action_packet); + + CastToClient()->QueuePacket(message_packet); + if(caster->IsClient() && caster != this) + caster->CastToClient()->QueuePacket(message_packet); + + CastToClient()->SetBindPoint(); + Save(); + safe_delete(action_packet); + safe_delete(message_packet); + } + } + else + { + EQApplicationPacket *action_packet = new EQApplicationPacket(OP_Action, sizeof(Action_Struct)); + Action_Struct* action = (Action_Struct*) action_packet->pBuffer; + EQApplicationPacket *message_packet = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); + CombatDamage_Struct *cd = (CombatDamage_Struct *)message_packet->pBuffer; + + action->target = GetID(); + action->source = caster ? caster->GetID() : GetID(); + action->level = 65; + action->instrument_mod = 10; + action->sequence = (GetHeading() * 12345 / 2); + action->type = 231; + action->spell = spell_id; + action->buff_unknown = 4; + + cd->target = action->target; + cd->source = action->source; + cd->type = action->type; + cd->spellid = action->spell; + cd->sequence = action->sequence; + + CastToClient()->QueuePacket(action_packet); + if(caster->IsClient() && caster != this) + caster->CastToClient()->QueuePacket(action_packet); + + CastToClient()->QueuePacket(message_packet); + if(caster->IsClient() && caster != this) + caster->CastToClient()->QueuePacket(message_packet); + + CastToClient()->SetBindPoint(); + Save(); + safe_delete(action_packet); + safe_delete(message_packet); + } + } + } + break; + } + + case SE_Gate: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Gate"); +#endif + if(!spellbonuses.AntiGate) + Gate(); + break; + } + + case SE_CancelMagic: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Cancel Magic: %d", effect_value); +#endif + if(SpecAttacks[UNDISPELLABLE]){ + caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name); + break; + } + + uint32 buff_count = GetMaxTotalSlots(); + for(int slot = 0; slot < buff_count; slot++) { + if( buffs[slot].spellid != SPELL_UNKNOWN && + spells[buffs[slot].spellid].buffdurationformula != DF_Permanent && + spells[buffs[slot].spellid].dispel_flag < 1 && + !IsDiscipline(buffs[slot].spellid)) + { + BuffFadeBySlot(slot); + slot = buff_count; + } + } + break; + } + + case SE_DispelDetrimental: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Dispel Detrimental: %d", effect_value); +#endif + if(SpecAttacks[UNDISPELLABLE]){ + caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name); + break; + } + + uint32 buff_count = GetMaxTotalSlots(); + for(int slot = 0; slot < buff_count; slot++) { + if (buffs[slot].spellid != SPELL_UNKNOWN && + spells[buffs[slot].spellid].buffdurationformula != DF_Permanent && + IsDetrimentalSpell(buffs[slot].spellid) && + spells[buffs[slot].spellid].dispel_flag < 1) + { + BuffFadeBySlot(slot); + slot = buff_count; + } + } + break; + } + + case SE_DispelBeneficial: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Dispel Beneficial: %d", effect_value); +#endif + if(SpecAttacks[UNDISPELLABLE]){ + caster->Message_StringID(MT_SpellFailure, SPELL_NO_EFFECT, spells[spell_id].name); + break; + } + + uint32 buff_count = GetMaxTotalSlots(); + for(int slot = 0; slot < buff_count; slot++) { + if (buffs[slot].spellid != SPELL_UNKNOWN && + spells[buffs[slot].spellid].buffdurationformula != DF_Permanent && + IsBeneficialSpell(buffs[slot].spellid) && + spells[buffs[slot].spellid].dispel_flag < 1) + { + BuffFadeBySlot(slot); + slot = buff_count; + } + } + break; + } + + case SE_Mez: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Mesmerize"); +#endif + Mesmerize(); + break; + } + + case SE_SummonItem: + { + const Item_Struct *item = database.GetItem(spell.base[i]); +#ifdef SPELL_EFFECT_SPAM + const char *itemname = item ? item->Name : "*Unknown Item*"; + snprintf(effect_desc, _EDLEN, "Summon Item: %s (id %d)", itemname, spell.base[i]); +#endif + if(!item) + { + Message(13, "Unable to summon item %d. Item not found.", spell.base[i]); + } + else if(IsClient()){ + Client *c=CastToClient(); + if (c->CheckLoreConflict(item)) { + c->DuplicateLoreMessage(spell.base[i]); + } else { + int charges; + if (spell.formula[i] < 100) + { + charges = spell.formula[i]; + } + else // variable charges + { + charges = CalcSpellEffectValue_formula(spell.formula[i], 0, 20, caster_level, spell_id); + } + charges = (spell.formula[i] < 100) ? charges : (charges > 20) ? 20 : (spell.max[i] < 1) ? item->MaxCharges : spell.max[i]; + if (SummonedItem) { + c->PushItemOnCursor(*SummonedItem); + c->SendItemPacket(SLOT_CURSOR, SummonedItem, ItemPacketSummonItem); + safe_delete(SummonedItem); + } + SummonedItem=database.CreateItem(spell.base[i],charges); + } + } + + break; + } + case SE_SummonItemIntoBag: + { + const Item_Struct *item = database.GetItem(spell.base[i]); +#ifdef SPELL_EFFECT_SPAM + const char *itemname = item ? item->Name : "*Unknown Item*"; + snprintf(effect_desc, _EDLEN, "Summon Item In Bag: %s (id %d)", itemname, spell.base[i]); +#endif + uint8 slot; + + if (!SummonedItem || !SummonedItem->IsType(ItemClassContainer)) { + if(caster) caster->Message(13,"SE_SummonItemIntoBag but no bag has been summoned!"); + } else if ((slot=SummonedItem->FirstOpenSlot())==0xff) { + if(caster) caster->Message(13,"SE_SummonItemIntoBag but no room in summoned bag!"); + } else if (IsClient()) { + if (CastToClient()->CheckLoreConflict(item)) { + CastToClient()->DuplicateLoreMessage(spell.base[i]); + } else { + int charges; + if (spell.formula[i] < 100) + { + charges = spell.formula[i]; + } + else // variable charges + { + charges = CalcSpellEffectValue_formula(spell.formula[i], 0, 20, caster_level, spell_id); + } + charges = charges < 1 ? 1 : (charges > 20 ? 20 : charges); + ItemInst *SubItem=database.CreateItem(spell.base[i],charges); + if (SubItem!=NULL) { + SummonedItem->PutItem(slot,*SubItem); + safe_delete(SubItem); + } + } + } + + break; + } + + case SE_SummonBSTPet: + case SE_NecPet: + case SE_SummonPet: + case SE_Familiar: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Summon %s: %s", (effect==SE_Familiar)?"Familiar":"Pet", spell.teleport_zone); +#endif + if(GetPet()) + { + Message_StringID(MT_Shout, ONLY_ONE_PET); + } + else + { + MakePet(spell_id, spell.teleport_zone); + } + break; + } + + case SE_DivineAura: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Invulnerability"); +#endif + if(spell_id==4789) // Touch of the Divine - Divine Save + buffs[buffslot].ticsremaining = spells[spell_id].buffduration; // Prevent focus/aa buff extension + + SetInvul(true); + break; + } + + case SE_ShadowStep: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Shadow Step: %d", effect_value); +#endif + if(IsNPC()) // see Song of Highsun - sends mob home + { + Gate(); + } + // solar: shadow step is handled by client already, nothing required + break; + } + + case SE_Blind: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Blind: %+i", effect_value); +#endif + if (spells[spell_id].base[i] == 1) + BuffFadeByEffect(SE_Blind); + // solar: handled by client + // TODO: blind flag? + break; + } + + case SE_Rune: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Melee Absorb Rune: %+i", effect_value); +#endif + effect_value = ApplySpellEffectiveness(caster, spell_id, effect_value); + buffs[buffslot].melee_rune = effect_value; + SetHasRune(true); + break; + } + + case SE_AbsorbMagicAtt: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Spell Absorb Rune: %+i", effect_value); +#endif + if(effect_value > 0) { + buffs[buffslot].magic_rune = effect_value; + SetHasSpellRune(true); + } + break; + } + + case SE_MitigateMeleeDamage: + { + buffs[buffslot].melee_rune = GetPartialMeleeRuneAmount(spell_id); + SetHasPartialMeleeRune(true); + break; + } + + case SE_MitigateSpellDamage: + { + buffs[buffslot].magic_rune = GetPartialMagicRuneAmount(spell_id); + SetHasPartialSpellRune(true); + break; + } + + case SE_Levitate: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Levitate"); +#endif + //this sends the levitate packet to everybody else + //who does not otherwise receive the buff packet. + SendAppearancePacket(AT_Levitate, 2, true, true); + break; + } + + case SE_Illusion: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Illusion: race %d", effect_value); +#endif + // Gender Illusions + if(spell.base[i] == -1) { + // Specific Gender Illusions + if(spell_id == 1732 || spell_id == 1731) { + int specific_gender = -1; + // Male + if(spell_id == 1732) + specific_gender = 0; + // Female + else if (spell_id == 1731) + specific_gender = 1; + if(specific_gender > -1) { + if(caster && caster->GetTarget()) { + SendIllusionPacket + ( + caster->GetTarget()->GetBaseRace(), + specific_gender, + caster->GetTarget()->GetTexture() + ); + } + } + } + // Change Gender Illusions + else { + if(caster && caster->GetTarget()) { + int opposite_gender = 0; + if(caster->GetTarget()->GetGender() == 0) + opposite_gender = 1; + + SendIllusionPacket + ( + caster->GetTarget()->GetRace(), + opposite_gender, + caster->GetTarget()->GetTexture() + ); + } + } + } + // Racial Illusions + else { + SendIllusionPacket + ( + spell.base[i], + Mob::GetDefaultGender(spell.base[i], GetGender()), + spell.base2[i] + ); + if(spell.base[i] == OGRE){ + SendAppearancePacket(AT_Size, 9); + } + else if(spell.base[i] == TROLL){ + SendAppearancePacket(AT_Size, 8); + } + else if(spell.base[i] == VAHSHIR || spell.base[i] == BARBARIAN){ + SendAppearancePacket(AT_Size, 7); + } + else if(spell.base[i] == HALF_ELF || spell.base[i] == WOOD_ELF || spell.base[i] == DARK_ELF || spell.base[i] == FROGLOK){ + SendAppearancePacket(AT_Size, 5); + } + else if(spell.base[i] == DWARF){ + SendAppearancePacket(AT_Size, 4); + } + else if(spell.base[i] == HALFLING || spell.base[i] == GNOME){ + SendAppearancePacket(AT_Size, 3); + } + else if(spell.base[i] == WOLF) { + SendAppearancePacket(AT_Size, 2); + } + else{ + SendAppearancePacket(AT_Size, 6); + } + } + for(int x = 0; x < 7; x++){ + SendWearChange(x); + } + if(caster && caster->GetAA(aaPermanentIllusion)) + buffs[buffslot].persistant_buff = 1; + else + buffs[buffslot].persistant_buff = 0; + break; + } + + case SE_IllusionCopy: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Illusion Copy"); +#endif + if(caster && caster->GetTarget()){ + SendIllusionPacket + ( + caster->GetTarget()->GetRace(), + caster->GetTarget()->GetGender(), + caster->GetTarget()->GetTexture() + ); + caster->SendAppearancePacket(AT_Size, caster->GetTarget()->GetSize()); + for(int x = 0; x < 7; x++){ + caster->SendWearChange(x); + } + } + } + + case SE_WipeHateList: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Memory Blur: %d", effect_value); +#endif + int wipechance = spells[spell_id].base[i]; + if(MakeRandomInt(0, 100) < wipechance) + { + if(IsAIControlled()) + { + WipeHateList(); + } + Message(13, "Your mind fogs. Who are my friends? Who are my enemies?... it was all so clear a moment ago..."); + } + break; + } + + case SE_SpinTarget: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Spin: %d", effect_value); +#endif + // solar: the spinning is handled by the client + int max_level = spells[spell_id].max[i]; + if(max_level == 0) + max_level = RuleI(Spells, BaseImmunityLevel); // Default max is 55 level limit + + // NPCs ignore level limits in their spells + if(SpecAttacks[UNSTUNABLE] || + ((GetLevel() > max_level) + && caster && (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity))))) + { + caster->Message_StringID(MT_Shout, IMMUNE_STUN); + } + else + { + // solar: the spinning is handled by the client + // Stun duration is based on the effect_value, not the buff duration(alot don't have buffs) + Stun(effect_value); + if(!IsClient()) { + Spin(); + spun_timer.Start(100); // spins alittle every 100 ms + } + } + break; + } + + case SE_EyeOfZomm: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Eye of Zomm"); +#endif + if(caster && caster->IsClient()) { + char eye_name[64]; + snprintf(eye_name, sizeof(eye_name), "Eye_of_%s", caster->GetCleanName()); + int duration = CalcBuffDuration(caster, this, spell_id) * 6; + caster->TemporaryPets(spell_id, NULL, eye_name, duration); + } + break; + } + + case SE_ReclaimPet: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Reclaim Pet"); +#endif + if + ( + IsNPC() && + GetOwnerID() && // I'm a pet + caster && // there's a caster + caster->GetID() == GetOwnerID() && // and it's my master + GetPetType() != petCharmed + ) + { + int lvlmod = 4; + if(caster->IsClient() && caster->CastToClient()->GetAA(aaImprovedReclaimEnergy)) + lvlmod = 8; //this is an unconfirmed number, I made it up + if(caster->IsClient() && caster->CastToClient()->GetAA(aaImprovedReclaimEnergy2)) + lvlmod = 8; //this is an unconfirmed number, I made it up + caster->SetMana(caster->GetMana()+(GetLevel()*lvlmod)); + + if(caster->IsClient()) + caster->CastToClient()->SetPet(0); + SetOwnerID(0); // this will kill the pet + } + break; + } + + case SE_BindSight: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Bind Sight"); +#endif + if(caster && caster->IsClient()) + { + caster->CastToClient()->SetBindSightTarget(this); + } + break; + } + + case SE_FeignDeath: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Feign Death"); +#endif + //todo, look up spell ID in DB + if(spell_id == 2488) //Dook- Lifeburn fix + break; + + if(IsClient()) + CastToClient()->SetFeigned(true); + break; + } + + case SE_Sentinel: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Sentinel"); +#endif + if(caster) + { + if(caster == this) + { + Message_StringID(MT_Spells, + SENTINEL_TRIG_YOU); + } + else + { + caster->Message_StringID(MT_Spells, + SENTINEL_TRIG_OTHER, GetCleanName()); + } + } + break; + } + + case SE_LocateCorpse: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Locate Corpse"); +#endif + // This is handled by the client prior to SoD. + // + if(IsClient() && (CastToClient()->GetClientVersionBit() & BIT_SoDAndLater)) + CastToClient()->LocateCorpse(); + + break; + } + + case SE_Revive: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Revive"); // heh the corpse won't see this +#endif + if (IsCorpse() && CastToCorpse()->IsPlayerCorpse()) { + + if(caster) + mlog(SPELLS__REZ, " corpse being rezzed using spell %i by %s", + spell_id, caster->GetName()); + + CastToCorpse()->CastRezz(spell_id, caster); + } + break; + } + + case SE_ModelSize: + case SE_ChangeHeight: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Model Size: %d%%", effect_value); +#endif + ChangeSize(GetSize() * (effect_value / 100.0)); + break; + } + + case SE_Root: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Root: %+i", effect_value); +#endif + rooted = true; + rooted_mod = 0; + + if (caster){ + rooted_mod = caster->aabonuses.RootBreakChance + + caster->itembonuses.RootBreakChance + + caster->spellbonuses.RootBreakChance; + } + + break; + } + + case SE_SummonHorse: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Summon Mount: %s", spell.teleport_zone); +#endif + if(IsClient()) // NPCs can't ride + { + CastToClient()->SummonHorse(spell_id); + } + break; + } + + case SE_SummonCorpse: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Summon Corpse: %d", effect_value); +#endif + // can only summon corpses of clients + if(!IsNPC()) { + Client* TargetClient = 0; + if(this->GetTarget()) + TargetClient = this->GetTarget()->CastToClient(); + else + TargetClient = this->CastToClient(); + + // We now have a valid target for this spell. Either the caster himself or a targetted player. Lets see if the target is in the group. + Group* group = entity_list.GetGroupByClient(TargetClient); + if(group) { + if(!group->IsGroupMember(TargetClient)) { + Message(13, "Your target must be a group member for this spell."); + break; + } + } + else { + Raid *r = entity_list.GetRaidByClient(caster->CastToClient()); + if(r) + { + uint32 gid = 0xFFFFFFFF; + gid = r->GetGroup(caster->GetName()); + if(gid < 11) + { + if(r->GetGroup(TargetClient->GetName()) != gid) { + Message(13, "Your target must be a group member for this spell."); + break; + } + } + } else { + if(TargetClient != this->CastToClient()) { + Message(13, "Your target must be a group member for this spell."); + break; + } + } + } + + // Now we should either be casting this on self or its being cast on a valid group member + if(TargetClient) { + Corpse *corpse = entity_list.GetCorpseByOwner(TargetClient); + if(corpse) { + if(TargetClient == this->CastToClient()) + Message_StringID(4, SUMMONING_CORPSE, TargetClient->CastToMob()->GetCleanName()); + else + Message_StringID(4, SUMMONING_CORPSE_OTHER, TargetClient->CastToMob()->GetCleanName()); + + corpse->Summon(CastToClient(), true, true); + } + else { + // No corpse found in the zone + Message_StringID(4, CORPSE_CANT_SENSE); + } + } + else { + Message_StringID(4, TARGET_NOT_FOUND); + LogFile->write(EQEMuLog::Error, "%s attempted to cast spell id %u with spell effect SE_SummonCorpse, but could not cast target into a Client object.", GetCleanName(), spell_id); + } + } + + break; + } + case SE_AddMeleeProc: + case SE_WeaponProc: + { + uint16 procid = GetProcID(spell_id, i); +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Weapon Proc: %s (id %d)", spells[effect_value].name, procid); +#endif + + if(spells[spell_id].base2[i] == 0) + AddProcToWeapon(procid, false, 100); + else + AddProcToWeapon(procid, false, spells[spell_id].base2[i]+100); + break; + } + + case SE_SkillProc2: + case SE_SkillProc: + { + uint16 procid = GetProcID(spell_id, i); +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Weapon Proc: %s (id %d)", spells[effect_value].name, procid); +#endif + if(spells[spell_id].base2[i] == 0) + AddSkillProc(procid, 100, spell_id); + else + AddSkillProc(procid, spells[spell_id].base2[i]+100, spell_id); + break; + } + + case SE_NegateAttacks: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Melee Negate Attack Rune: %+i", effect_value); +#endif + if(buffslot >= 0) + buffs[buffslot].numhits = effect_value; + break; + } + case SE_AppraiseLDonChest: + { + if(IsNPC()) + { + int check = spell.max[0]; + int target = spell.targettype; + if(target == ST_LDoNChest_Cursed) + { + if(caster && caster->IsClient()) + { + caster->CastToClient()->HandleLDoNSenseTraps(CastToNPC(), check, LDoNTypeCursed); + } + } + else if(target == ST_Target) + { + if(caster && caster->IsClient()) + { + caster->CastToClient()->HandleLDoNSenseTraps(CastToNPC(), check, LDoNTypeMagical); + } + } + } + break; + } + + case SE_DisarmLDoNTrap: + { + if(IsNPC()) + { + int check = spell.max[0]; + int target = spell.targettype; + if(target == ST_LDoNChest_Cursed) + { + if(caster && caster->IsClient()) + { + caster->CastToClient()->HandleLDoNDisarm(CastToNPC(), check, LDoNTypeCursed); + } + } + else if(target == ST_Target) + { + if(caster && caster->IsClient()) + { + caster->CastToClient()->HandleLDoNDisarm(CastToNPC(), check, LDoNTypeMagical); + } + } + } + break; + } + + case SE_UnlockLDoNChest: + { + if(IsNPC()) + { + int check = spell.max[0]; + int target = spell.targettype; + if(target == ST_LDoNChest_Cursed) + { + if(caster && caster->IsClient()) + { + caster->CastToClient()->HandleLDoNPickLock(CastToNPC(), check, LDoNTypeCursed); + } + } + else if(target == ST_Target) + { + if(caster && caster->IsClient()) + { + caster->CastToClient()->HandleLDoNPickLock(CastToNPC(), check, LDoNTypeMagical); + } + } + } + break; + } + + case SE_Lull: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Lull"); +#endif + // TODO: check vs. CHA when harmony effect failed, if caster is to be added to hatelist + break; + } + + case SE_PoisonCounter: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Poison Counter: %+i", effect_value); +#endif + if (effect_value < 0) + { + effect_value = 0 - effect_value; + uint32 buff_count = GetMaxTotalSlots(); + for (int j=0; j < buff_count; j++) { + if (buffs[j].spellid >= (uint16)SPDAT_RECORDS) + continue; + if (CalculatePoisonCounters(buffs[j].spellid) == 0) + continue; + if (effect_value >= 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); + CastOnCure(buffs[j].spellid); + effect_value -= buffs[j].counters; + buffs[j].counters = 0; + BuffFadeBySlot(j); + } else { + buffs[j].counters -= effect_value; + effect_value = 0; + break; + } + } + } + break; + } + + case SE_DiseaseCounter: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Disease Counter: %+i", effect_value); +#endif + if (effect_value < 0) + { + effect_value = 0 - effect_value; + uint32 buff_count = GetMaxTotalSlots(); + for (int j=0; j < buff_count; j++) { + if (buffs[j].spellid >= (uint16)SPDAT_RECORDS) + continue; + if (CalculateDiseaseCounters(buffs[j].spellid) == 0) + continue; + if (effect_value >= 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); + CastOnCure(buffs[j].spellid); + effect_value -= buffs[j].counters; + buffs[j].counters = 0; + BuffFadeBySlot(j); + } + else + { + buffs[j].counters -= effect_value; + effect_value = 0; + break; + } + } + } + break; + } + + case SE_CurseCounter: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Curse Counter: %+i", effect_value); +#endif + if (effect_value < 0) + { + effect_value = 0 - effect_value; + uint32 buff_count = GetMaxTotalSlots(); + for (int j=0; j < buff_count; j++) { + if (buffs[j].spellid >= (uint16)SPDAT_RECORDS) + continue; + if (CalculateCurseCounters(buffs[j].spellid) == 0) + continue; + if (effect_value >= 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); + CastOnCure(buffs[j].spellid); + effect_value -= buffs[j].counters; + buffs[j].counters = 0; + BuffFadeBySlot(j); + } + else + { + buffs[j].counters -= effect_value; + effect_value = 0; + break; + } + } + } + break; + } + + case SE_CorruptionCounter: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Corruption Counter: %+i", effect_value); +#endif + if (effect_value < 0) + { + effect_value = -effect_value; + uint32 buff_count = GetMaxTotalSlots(); + for (int j=0; j < buff_count; j++) { + if (buffs[j].spellid >= (uint16)SPDAT_RECORDS) + continue; + if (CalculateCorruptionCounters(buffs[j].spellid) == 0) + continue; + if (effect_value >= 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); + CastOnCure(buffs[j].spellid); + effect_value -= buffs[j].counters; + buffs[j].counters = 0; + BuffFadeBySlot(j); + } else { + buffs[j].counters -= effect_value; + effect_value = 0; + break; + } + } + } + break; + } + + case SE_Destroy: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Destroy"); +#endif + if(IsNPC()) { + if(GetLevel() <= 52) + CastToNPC()->Depop(); + else + Message(13, "Your target is too high level to be affected by this spell."); + } + break; + } + + case SE_TossUp: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Toss Up: %d", effect_value); +#endif + double toss_amt = (double)spells[spell_id].base[i]; + if(toss_amt < 0) + toss_amt = -toss_amt; + + if(IsNPC()) + { + Stun(toss_amt); + } + toss_amt = sqrt(toss_amt)-2.0; + + if(toss_amt < 0.0) + toss_amt = 0.0; + + if(toss_amt > 20.0) + toss_amt = 20.0; + + if(IsClient()) + { + CastToClient()->SetKnockBackExemption(true); + } + + double look_heading = GetHeading(); + look_heading /= 256; + look_heading *= 360; + look_heading += 180; + if(look_heading > 360) + look_heading -= 360; + + //x and y are crossed mkay + double new_x = spells[spell_id].pushback * sin(double(look_heading * 3.141592 / 180.0)); + double new_y = spells[spell_id].pushback * cos(double(look_heading * 3.141592 / 180.0)); + + EQApplicationPacket* outapp_push = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer; + + spu->spawn_id = GetID(); + spu->x_pos = FloatToEQ19(GetX()); + spu->y_pos = FloatToEQ19(GetY()); + spu->z_pos = FloatToEQ19(GetZ()); + spu->delta_x = NewFloatToEQ13(new_x); + spu->delta_y = NewFloatToEQ13(new_y); + spu->delta_z = NewFloatToEQ13(toss_amt); + spu->heading = FloatToEQ19(GetHeading()); + spu->padding0002 =0; + spu->padding0006 =7; + spu->padding0014 =0x7f; + spu->padding0018 =0x5df27; + spu->animation = 0; + spu->delta_heading = NewFloatToEQ13(0); + outapp_push->priority = 5; + entity_list.QueueClients(this, outapp_push, true); + if(IsClient()) + CastToClient()->FastQueuePacket(&outapp_push); + + break; + } + + case SE_StopRain: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Stop Rain"); +#endif + zone->zone_weather = 0; + zone->weatherSend(); + break; + } + + case SE_Sacrifice: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Sacrifice"); +#endif + if(!IsClient() || !caster->IsClient()){ + break; + } + CastToClient()->SacrificeConfirm(caster->CastToClient()); + break; + } + + case SE_SummonPC: + { + if(IsClient()){ + CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), caster->GetX(), caster->GetY(), caster->GetZ(), caster->GetHeading(), 2, SummonPC); + Message(15, "You have been summoned!"); + entity_list.ClearAggro(this); + } + else + caster->Message(13, "This spell can only be cast on players."); + + break; + } + + case SE_Silence: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Silence"); +#endif + Silence(true); + break; + } + + case SE_Amnesia: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Amnesia"); +#endif + Amnesia(true); + break; + } + + case SE_CallPet: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Call Pet"); +#endif + // solar: this is cast on self, not on the pet + if(GetPet() && GetPet()->IsNPC()) + { + GetPet()->CastToNPC()->GMMove(GetX(), GetY(), GetZ(), GetHeading()); + } + break; + } + + case SE_StackingCommand_Block: + case SE_StackingCommand_Overwrite: + { + // solar: these are special effects used by the buff stuff + break; + } + + + case SE_TemporaryPets: //Dook- swarms and wards: + { + // EverHood - 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]; + snprintf(pet_name, sizeof(pet_name), "%s`s pet", caster->GetCleanName()); + caster->TemporaryPets(spell_id, this, pet_name); + } + else + caster->TemporaryPets(spell_id, this, NULL); + break; + } + + case SE_FadingMemories: //Dook- escape etc + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Fading Memories"); +#endif + entity_list.RemoveFromTargets(caster); + SetInvisible(1); + break; + } + + case SE_RangedProc: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Ranged Proc: %+i", effect_value); +#endif + uint16 procid = GetProcID(spell_id, i); + + if(spells[spell_id].base2[i] == 0) + AddRangedProc(procid, 100, spell_id); + else + AddRangedProc(procid, spells[spell_id].base2[i]+100, spell_id); + break; + } + + case SE_Rampage: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Rampage"); +#endif + if(caster) + entity_list.AEAttack(caster, 30, 13, 0, true); // on live wars dont get a duration ramp, its a one shot deal + + break; + } + + case SE_AEMelee: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Duration Rampage"); +#endif + if (caster && caster->IsClient()) { // will tidy this up later so that NPCs can duration ramp from spells too + CastToClient()->DurationRampage(effect_value*12); + } + break; + } + + case SE_AETaunt://Dook- slapped it in the spell effect so client does the animations + { // and incase there are similar spells we havent found yet +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "AE Taunt"); +#endif + if(caster && caster->IsClient()) + entity_list.AETaunt(caster->CastToClient()); + break; + } + + case SE_SkillAttack: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Skill Attack"); +#endif + /*Kayen: + Weapon Damage = spells[spell_id].base[i] + Chance to Hit Bonus = spells[spell_id].base2[i] + ???? = spells[spell_id].max[i] - MOST of the effects have this value. + *Max is lower value then Weapon base, possibly min hit vs Weapon Damage range ie. MakeRandInt(max,base) + */ + int16 focus = 0; + + if(caster->IsClient()) + focus = caster->CastToClient()->GetFocusEffect(focusSpellEffectiveness, spell_id); + + switch(spells[spell_id].skill) + { + case THROWING: + caster->DoThrowingAttackDmg(this, NULL, NULL, spells[spell_id].base[i],spells[spell_id].base2[i], focus); + break; + + case ARCHERY: + caster->DoArcheryAttackDmg(this, NULL, NULL, spells[spell_id].base[i],spells[spell_id].base2[i],focus); + break; + + default: + caster->DoMeleeSkillAttackDmg(this, spells[spell_id].base[i], spells[spell_id].skill, spells[spell_id].base2[i], focus); + break; + } + break; + } + + case SE_WakeTheDead: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Wake The Dead"); +#endif + //meh dupe issue with npc casting this + if(caster->IsClient()){ + //this spell doesn't appear to actually contain the information on duration inside of it oddly + int dur = 60; + if(spell_id == 3269) + dur += 15; + else if(spell_id == 3270) + dur += 30; + + caster->WakeTheDead(spell_id, caster->GetTarget(), dur); + } + break; + } + + case SE_Doppelganger: + { + if(caster && caster->IsClient()) { + char pet_name[64]; + snprintf(pet_name, sizeof(pet_name), "%s`s doppelganger", caster->GetCleanName()); + int pet_count = spells[spell_id].base[i]; + int pet_duration = spells[spell_id].max[i]; + caster->CastToClient()->Doppelganger(spell_id, this, pet_name, pet_count, pet_duration); + } + break; + } + + case SE_DefensiveProc: + { + uint16 procid = GetProcID(spell_id, i); +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Defensive Proc: %s (id %d)", spells[effect_value].name, procid); +#endif + if(spells[spell_id].base2[i] == 0) + AddDefensiveProc(procid, 100,spell_id); + else + AddDefensiveProc(procid, spells[spell_id].base2[i]+100,spell_id); + break; + + break; + } + + case SE_BardAEDot: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Bard AE Dot: %+i", effect_value); +#endif + // SE_CurrentHP is calculated at first tick if its a dot/buff + if (buffslot >= 0) + break; + + // for offensive spells check if we have a spell rune on + int32 dmg = effect_value; + if(dmg < 0) + { + // take partial damage into account + dmg = (int32) (dmg * partial / 100); + + //handles AAs and what not... + //need a bard version of this prolly... + //if(caster) + // dmg = caster->GetActSpellDamage(spell_id, dmg); + + dmg = -dmg; + Damage(caster, dmg, spell_id, spell.skill, false, buffslot, false); + } else if(dmg > 0) { + //healing spell... + if(caster) + dmg = caster->GetActSpellHealing(spell_id, dmg); + HealDamage(dmg, caster); + } +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Current Hitpoints: %+i actual: %+i", effect_value, dmg); +#endif + break; + } + + case SE_CurrentEndurance: { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Current Endurance: %+i", effect_value); +#endif + if(IsClient()) { + CastToClient()->SetEndurance(CastToClient()->GetEndurance() + effect_value); + } + break; + } + + case SE_CurrentEnduranceOnce: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Current Endurance Once: %+i", effect_value); +#endif + + if(IsClient()) { + CastToClient()->SetEndurance(CastToClient()->GetEndurance() + effect_value); + } + break; + } + + case SE_BalanceHP: { + if(!caster) + break; + + if(!caster->IsClient()) + break; + + Raid *r = entity_list.GetRaidByClient(caster->CastToClient()); + if(r) + { + uint32 gid = 0xFFFFFFFF; + gid = r->GetGroup(caster->GetName()); + if(gid < 11) + { + r->BalanceHP(spell.base[i], gid); + break; + } + } + + Group *g = entity_list.GetGroupByClient(caster->CastToClient()); + + if(!g) + break; + + g->BalanceHP(spell.base[i]); + break; + } + + case SE_BalanceMana: { + if(!caster) + break; + + if(!caster->IsClient()) + break; + + Raid *r = entity_list.GetRaidByClient(caster->CastToClient()); + if(r) + { + uint32 gid = 0xFFFFFFFF; + gid = r->GetGroup(caster->GetName()); + if(gid < 11) + { + r->BalanceMana(spell.base[i], gid); + break; + } + } + + Group *g = entity_list.GetGroupByClient(caster->CastToClient()); + + if(!g) + break; + + g->BalanceMana(spell.base[i]); + break; + } + + case SE_DeathSave: { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Death Save: %+i", effect_value); +#endif + uint8 BonusChance = 0; + if(caster) { + + BonusChance = caster->aabonuses.UnfailingDivinity + + caster->itembonuses.UnfailingDivinity + + caster->spellbonuses.UnfailingDivinity; + } + +#ifdef SPELL_EFFECT_SPAM + //snprintf(effect_desc, _EDLEN, "Death Save Chance: %+i", SuccessChance); +#endif + //buffs[buffslot].deathSaveSuccessChance = SuccessChance; + //buffs[buffslot].deathsaveCasterAARank = caster->GetAA(aaUnfailingDivinity); + buffs[buffslot].deathsaveCasterAARank = BonusChance; + //SetDeathSaveChance(true); + + + break; + } + + case SE_SummonAndResAllCorpses: + { + if(IsClient()) + CastToClient()->SummonAndRezzAllCorpses(); + + break; + } + + case SE_GateToHomeCity: + { + if(IsClient()) + CastToClient()->GoToBind(4); + break; + } + + case SE_SuspendMinion: + case SE_SuspendPet: + { + if(IsClient()) + CastToClient()->SuspendMinion(); + + break; + } + + case SE_Forceful_Rejuv: + { + if(IsClient()) { + for(unsigned int i =0 ; i < MAX_PP_MEMSPELL; ++i) { + if(IsValidSpell(CastToClient()->m_pp.mem_spells[i])) { + CastToClient()->m_pp.spellSlotRefresh[i] = 1; + CastToClient()->GetPTimers().Clear(&database, (pTimerSpellStart + CastToClient()->m_pp.mem_spells[i])); + } + } + SetMana(GetMana()); + } + break; + } + + case SE_HealGroupFromMana: { + if(!caster) + break; + + if(!caster->IsClient()) + break; + + uint32 max_mana = spell.base[i]; + int ratio = spell.base2[i]; + uint32 heal_amt = 0; + + if (caster->GetMana() <= max_mana){ + heal_amt = ratio*caster->GetMana()/10; + caster->SetMana(0); + } + + else { + heal_amt = ratio*max_mana/10; + caster->SetMana(caster->GetMana() - max_mana); + } + + Raid *r = entity_list.GetRaidByClient(caster->CastToClient()); + if(r) + { + uint32 gid = 0xFFFFFFFF; + gid = r->GetGroup(caster->GetName()); + if(gid < 11) + { + r->HealGroup(heal_amt,caster, gid); + break; + } + } + + Group *g = entity_list.GetGroupByClient(caster->CastToClient()); + + if(!g){ + caster->HealDamage(heal_amt); + break; + } + + g->HealGroup(heal_amt, caster); + break; + } + + case SE_ManaDrainWithDmg: + { + int mana_damage = 0; + int32 mana_to_use = GetMana() - spell.base[i]; + if(mana_to_use > -1) { + SetMana(GetMana() - spell.base[i]); + // we take full dmg(-10 to make the damage the right sign) + mana_damage = spell.base[i] / -10 * spell.base2[i]; + Damage(caster, mana_damage, spell_id, spell.skill, false, i, true); + } + else { + mana_damage = GetMana() / -10 * spell.base2[i]; + SetMana(0); + Damage(caster, mana_damage, spell_id, spell.skill, false, i, true); + } + break; + } + + case SE_EndDrainWithDmg: + { + if(IsClient()) { + int end_damage = 0; + int32 end_to_use = CastToClient()->GetEndurance() - spell.base[i]; + if(end_to_use > -1) { + CastToClient()->SetEndurance(CastToClient()->GetEndurance() - spell.base[i]); + // we take full dmg(-10 to make the damage the right sign) + end_damage = spell.base[i] / -10 * spell.base2[i]; + Damage(caster, end_damage, spell_id, spell.skill, false, i, true); + } + else { + end_damage = CastToClient()->GetEndurance() / -10 * spell.base2[i]; + CastToClient()->SetEndurance(0); + Damage(caster, end_damage, spell_id, spell.skill, false, i, true); + } + } + break; + } + + case SE_SetBodyType: + { + SetBodyType((bodyType)spell.base[i], false); + break; + } + + case SE_Leap: + { + // These effects remove lev and only work a certain distance away. + BuffFadeByEffect(SE_Levitate); + if (caster && caster->GetTarget()) { + float my_x = caster->GetX(); + float my_y = caster->GetY(); + float my_z = caster->GetZ(); + float target_x = GetX(); + float target_y = GetY(); + float target_z = GetZ(); + if ((CalculateDistance(my_x, my_y, my_z) > 10) && + (CalculateDistance(my_x, my_y, my_z) < 75) && + (caster->CheckLosFN(caster->GetTarget()))) + { + float value, x_vector, y_vector, hypot; + + value = (float)spell.base[i]; // distance away from target + + x_vector = target_x - my_x; + y_vector = target_y - my_y; + hypot = sqrt(x_vector*x_vector + y_vector*y_vector); + + x_vector /= hypot; + y_vector /= hypot; + + my_x = target_x - (x_vector * value); + my_y = target_y - (y_vector * value); + + float new_ground = GetGroundZ(my_x, my_y); + + if(caster->IsClient()) + caster->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), my_x, my_y, new_ground, GetHeading()*2); + else + caster->GMMove(my_x, my_y, new_ground, GetHeading()); + } + } + break; + } + case SE_VoiceGraft: + { + if(caster && caster->GetPet()) + caster->spellbonuses.VoiceGraft = caster->GetPetID(); + + break; + } + + case SE_ManaBurn: + { + uint32 max_mana = spell.base[i]; + int ratio = spell.base2[i]; + int32 dmg = 0; + + if (caster){ + if (caster->GetMana() <= max_mana){ + dmg = ratio*caster->GetMana()/10; + caster->SetMana(0); + } + + else { + dmg = ratio*max_mana/10; + caster->SetMana(caster->GetMana() - max_mana); + } + + if(IsDetrimentalSpell(spell_id)) { + dmg = -dmg; + Damage(caster, dmg, spell_id, spell.skill, false, buffslot, false); + } else { + HealDamage(dmg, caster); + } + } + } + + case SE_Taunt: + { + if (IsNPC()) + caster->Taunt(this->CastToNPC(), false, spell.base[i]); + } + + case SE_Purify: + { + /* + Guessing as to exactly how this effect works. + All spells that utilize it 'remove all determental effects' + with a value of (20). Lets assume the value determines + how many determental effects can be removed. + */ + uint32 buff_count = GetMaxTotalSlots(); + int FadeCount = 0; + + for (int j = 0; j <= buff_count; j++) { + if(buffs[j].spellid != SPELL_UNKNOWN) { + if((FadeCount <= spell.base[i]) && IsDetrimentalSpell(buffs[j].spellid)){ + BuffFadeBySlot(j, false); + FadeCount++; + } + } + } + } + + // Handled Elsewhere + case SE_ImmuneFleeing: + case SE_NegateSpellEffect: + case SE_Knockdown: // handled by client + case SE_ShadowStepDirectional: // handled by client + case SE_SpellOnDeath: + case SE_BlockNextSpellFocus: + case SE_ReduceReuseTimer: + case SE_SwarmPetDuration: + case SE_LimitHPPercent: + case SE_LimitManaPercent: + case SE_LimitEndPercent: + case SE_ExtraAttackChance: + case SE_ProcChance: + case SE_StunResist: + case SE_MinDamageModifier: + case SE_DamageModifier: + case SE_HitChance: + case SE_MeleeSkillCheck: + case SE_HundredHands: + case SE_ResistFearChance: + case SE_ResistSpellChance: + case SE_AllInstrumentMod: + case SE_MeleeLifetap: + case SE_DoubleAttackChance: + case SE_TripleAttackChance: + case SE_DualWieldChance: + case SE_ParryChance: + case SE_DodgeChance: + case SE_RiposteChance: + case SE_AvoidMeleeChance: + case SE_CrippBlowChance: + case SE_CriticalHitChance: + case SE_MeleeMitigation: + case SE_Reflect: + case SE_Screech: + case SE_SingingSkill: + case SE_MagicWeapon: + case SE_Hunger: + case SE_MagnifyVision: + case SE_Lycanthropy: + case SE_NegateIfCombat: + case SE_CastingLevel: + case SE_CastingLevel2: + case SE_RaiseStatCap: + case SE_ResistAll: + case SE_ResistMagic: + case SE_ResistDisease: + case SE_ResistPoison: + case SE_ResistCold: + case SE_ResistFire: + case SE_AllStats: + case SE_CHA: + case SE_WIS: + case SE_INT: + case SE_STA: + case SE_AGI: + case SE_DEX: + case SE_STR: + case SE_ATK: + case SE_ArmorClass: + case SE_EndurancePool: + case SE_Stamina: + case SE_UltraVision: + case SE_InfraVision: + case SE_ManaPool: + case SE_TotalHP: + case SE_ChangeFrenzyRad: + case SE_Harmony: + case SE_ChangeAggro: + case SE_Hate2: + case SE_Identify: + case SE_Calm: + case SE_ReduceHate: + case SE_SpellDamageShield: + case SE_ReverseDS: + case SE_DamageShield: + case SE_TrueNorth: + case SE_WaterBreathing: + case SE_MovementSpeed: + case SE_HealOverTime: + case SE_PercentXPIncrease: + case SE_DivineSave: + case SE_Accuracy: + case SE_Flurry: + case SE_AttackSpeed: + case SE_AttackSpeed2: + case SE_AttackSpeed3: + case SE_AttackSpeed4: + case SE_ImprovedDamage: + case SE_ImprovedHeal: + case SE_IncreaseSpellHaste: + case SE_IncreaseSpellDuration: + case SE_IncreaseRange: + case SE_SpellHateMod: + case SE_ReduceReagentCost: + case SE_ReduceManaCost: + case SE_LimitMaxLevel: + case SE_LimitResist: + case SE_LimitTarget: + case SE_LimitEffect: + case SE_LimitSpellType: + case SE_LimitSpell: + case SE_LimitMinDur: + case SE_LimitInstant: + case SE_LimitMinLevel: + case SE_LimitCastTime: + case SE_LimitManaCost: + case SE_CombatSkills: + case SE_SpellDurationIncByTic: + case SE_TriggerOnCast: + case SE_HealRate: + case SE_SkillDamageTaken: + case SE_SpellVulnerability: + case SE_SpellTrigger: + case SE_ApplyEffect: + case SE_Twincast: + case SE_DelayDeath: + case SE_InterruptCasting: + case SE_ImprovedSpellEffect: + case SE_BossSpellTrigger: + case SE_CastOnWearoff: + case SE_EffectOnFade: + case SE_MaxHPChange: + case SE_SympatheticProc: + case SE_SpellDamage: + case SE_CriticalSpellChance: + case SE_SpellCritChance: + case SE_SpellCritDmgIncrease: + case SE_DotCritDmgIncrease: + case SE_CriticalHealChance: + case SE_CriticalHealOverTime: + case SE_CriticalDoTChance: + case SE_SpellOnKill: + case SE_SpellOnKill2: + case SE_CriticalDamageMob: + case SE_LimitSpellGroup: + case SE_ResistCorruption: + case SE_ReduceSkillTimer: + case SE_HPToMana: + case SE_ManaAbsorbPercentDamage: + case SE_SkillDamageAmount: + case SE_SkillDamageAmount2: + case SE_GravityEffect: + case SE_IncreaseBlockChance: + case SE_AntiGate: + case SE_Fearless: + case SE_FF_Damage_Amount: + case SE_AdditionalHeal: + case SE_CastOnCurer: + case SE_CastOnCure: + case SE_CastonNumHitFade: + case SE_LimitToSkill: + case SE_SpellProcChance: + case SE_CharmBreakChance: + case SE_BardSongRange: + case SE_ACv2: + case SE_ManaRegen_v2: + case SE_ImprovedDamage2: + case SE_AdditionalHeal2: + case SE_HealRate2: + case SE_CriticalHealChance2: + case SE_CriticalHealOverTime2: + case SE_Empathy: + case SE_LimitSpellSkill: + case SE_MitigateDamageShield: + case SE_IncreaseSpellPower: + case SE_LimitClass: + case SE_LimitExcludeSkill: + case SE_BlockBehind: + case SE_ShieldBlock: + case SE_PetCriticalHit: + case SE_SlayUndead: + case SE_GiveDoubleAttack: + case SE_StrikeThrough2: + case SE_SecondaryDmgInc: + case SE_ArcheryDamageModifier: + case SE_ConsumeProjectile: + case SE_FrontalBackstabChance: + case SE_FrontalBackstabMinDmg: + case SE_TripleBackstab: + case SE_DoubleSpecialAttack: + case SE_IncreaseRunSpeedCap: + case SE_BaseMovementSpeed: + case SE_FrontalStunResist: + case SE_ImprovedBindWound: + case SE_MaxBindWound: + case SE_CombatStability: + case SE_PetAvoidance: + case SE_GiveDoubleRiposte: + case SE_Ambidexterity: + case SE_PetMaxHP: + case SE_PetFlurry: + case SE_MasteryofPast: + case SE_GivePetGroupTarget: + case SE_RootBreakChance: + case SE_UnfailingDivinity: + case SE_ChannelChanceSpells: + case SE_ChannelChanceItems: + case SE_CriticalHealRate: + case SE_IncreaseNumHits: + { + break; + } + + default: + { +#ifdef SPELL_EFFECT_SPAM + snprintf(effect_desc, _EDLEN, "Unknown Effect ID %d", effect); +#else + Message(0, "Unknown spell effect %d in spell %s (id %d)", effect, spell.name, spell_id); +#endif + } + } +#ifdef SPELL_EFFECT_SPAM + Message(0, ". . . Effect #%i: %s", i + 1, (effect_desc && effect_desc[0]) ? effect_desc : "Unknown"); +#endif + } + + CalcBonuses(); + + if (SummonedItem) { + Client *c=CastToClient(); + c->PushItemOnCursor(*SummonedItem); + c->SendItemPacket(SLOT_CURSOR, SummonedItem, ItemPacketSummonItem); + safe_delete(SummonedItem); + } + + return true; +} + +int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, Mob *caster, int ticsremaining) +{ + int formula, base, max, effect_value; + + if + ( + !IsValidSpell(spell_id) || + effect_id < 0 || + effect_id >= EFFECT_COUNT + ) + return 0; + + formula = spells[spell_id].formula[effect_id]; + base = spells[spell_id].base[effect_id]; + max = spells[spell_id].max[effect_id]; + + if(IsBlankSpellEffect(spell_id, effect_id)) + return 0; + + effect_value = CalcSpellEffectValue_formula(formula, base, max, caster_level, spell_id, ticsremaining); + + if(caster && IsBardSong(spell_id) && + (spells[spell_id].effectid[effect_id] != SE_AttackSpeed) && + (spells[spell_id].effectid[effect_id] != SE_AttackSpeed2) && + (spells[spell_id].effectid[effect_id] != SE_AttackSpeed3) && + (spells[spell_id].effectid[effect_id] != SE_Lull) && + (spells[spell_id].effectid[effect_id] != SE_ChangeFrenzyRad) && + (spells[spell_id].effectid[effect_id] != SE_Harmony) && + (spells[spell_id].effectid[effect_id] != SE_CurrentMana)&& + (spells[spell_id].effectid[effect_id] != SE_ManaRegen_v2)) + { + + int oval = effect_value; + int mod = caster->GetInstrumentMod(spell_id); + mod = ApplySpellEffectiveness(caster, spell_id, mod, true); + effect_value = effect_value * mod / 10; + mlog(SPELLS__BARDS, "Effect value %d altered with bard modifier of %d to yeild %d", oval, mod, effect_value); + } + + return(effect_value); +} + +// solar: generic formula calculations +int Mob::CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining) +{ +/* +neotokyo: i need those formulas checked!!!! + +0 = base +1 - 99 = base + level * formulaID +100 = base +101 = base + level / 2 +102 = base + level +103 = base + level * 2 +104 = base + level * 3 +105 = base + level * 4 +106 ? base + level * 5 +107 ? min + level / 2 +108 = min + level / 3 +109 = min + level / 4 +110 = min + level / 5 +119 ? min + level / 8 +121 ? min + level / 4 +122 = splurt +123 ? +203 = stacking issues ? max +205 = stacking issues ? 105 + + + 0x77 = min + level / 8 +*/ + + int result = 0, updownsign = 1, ubase = base; + if(ubase < 0) + ubase = 0 - ubase; + + // solar: this updown thing might look messed up but if you look at the + // spells it actually looks like some have a positive base and max where + // the max is actually less than the base, hence they grow downward +/* +This seems to mainly catch spells where both base and max are negative. +Strangely, damage spells have a negative base and positive max, but +snare has both of them negative, yet their range should work the same: +(meaning they both start at a negative value and the value gets lower) +*/ + if (max < base && max != 0) + { + // values are calculated down + updownsign = -1; + } + else + { + // values are calculated up + updownsign = 1; + } + + mlog(SPELLS__EFFECT_VALUES, "CSEV: spell %d, formula %d, base %d, max %d, lvl %d. Up/Down %d", + spell_id, formula, base, max, caster_level, updownsign); + + switch(formula) + { + case 60: //used in stun spells..? + case 70: + result = ubase/100; break; + case 0: + case 100: // solar: confirmed 2/6/04 + result = ubase; break; + case 101: // solar: confirmed 2/6/04 + result = updownsign * (ubase + (caster_level / 2)); break; + case 102: // solar: confirmed 2/6/04 + result = updownsign * (ubase + caster_level); break; + case 103: // solar: confirmed 2/6/04 + result = updownsign * (ubase + (caster_level * 2)); break; + case 104: // solar: confirmed 2/6/04 + result = updownsign * (ubase + (caster_level * 3)); break; + case 105: // solar: confirmed 2/6/04 + result = updownsign * (ubase + (caster_level * 4)); break; + + case 107: + //Used on Reckless Strength, I think it should decay over time + result = updownsign * (ubase + (caster_level / 2)); break; + case 108: + result = updownsign * (ubase + (caster_level / 3)); break; + case 109: // solar: confirmed 2/6/04 + result = updownsign * (ubase + (caster_level / 4)); break; + + case 110: // solar: confirmed 2/6/04 + //is there a reason we dont use updownsign here??? + result = ubase + (caster_level / 5); break; + + case 111: + result = updownsign * (ubase + 6 * (caster_level - GetMinLevel(spell_id))); break; + case 112: + result = updownsign * (ubase + 8 * (caster_level - GetMinLevel(spell_id))); break; + case 113: + result = updownsign * (ubase + 10 * (caster_level - GetMinLevel(spell_id))); break; + case 114: + result = updownsign * (ubase + 15 * (caster_level - GetMinLevel(spell_id))); break; + + //these formula were updated according to lucy 10/16/04 + case 115: // solar: this is only in symbol of transal + result = ubase + 6 * (caster_level - GetMinLevel(spell_id)); break; + case 116: // solar: this is only in symbol of ryltan + result = ubase + 8 * (caster_level - GetMinLevel(spell_id)); break; + case 117: // solar: this is only in symbol of pinzarn + result = ubase + 12 * (caster_level - GetMinLevel(spell_id)); break; + case 118: // solar: used in naltron and a few others + result = ubase + 20 * (caster_level - GetMinLevel(spell_id)); break; + + case 119: // solar: confirmed 2/6/04 + result = ubase + (caster_level / 8); break; + case 121: // solar: corrected 2/6/04 + result = ubase + (caster_level / 3); break; + case 122: + { + // May need to account for duration focus effects + int ticdif = spells[spell_id].buffduration - (ticsremaining - 1); + if(ticdif < 0) + ticdif = 0; + + result = updownsign * (ubase - (12 * ticdif)); + break; + } + case 123: // solar: added 2/6/04 + result = MakeRandomInt(ubase, abs(max)); + break; + + //these are used in stacking effects... formula unknown + case 201: + case 203: + result = max; + break; + default: + { + if (formula < 100) + result = ubase + (caster_level * formula); + else if((formula > 1000) && (formula < 1999)) + { + // These work like splurt, accept instead of being hard coded to 12, it is formula - 1000. + // Formula 1999 seems to have a slightly different effect, so is not included here + int ticdif = spells[spell_id].buffduration - (ticsremaining - 1); + if(ticdif < 0) + ticdif = 0; + + result = updownsign * (ubase - ((formula - 1000) * ticdif)); + } + else if((formula >= 2000) && (formula <= 2650)) + { + // Source: http://crucible.samanna.net/viewtopic.php?f=38&t=6259 + result = ubase * (caster_level * (formula - 2000) + 1); + } + else + LogFile->write(EQEMuLog::Debug, "Unknown spell effect value forumula %d", formula); + } + } + + int oresult = result; + + // now check result against the allowed maximum + if (max != 0) + { + if (updownsign == 1) + { + if (result > max) + result = max; + } + else + { + if (result < max) + result = max; + } + } + + // if base is less than zero, then the result need to be negative too + if (base < 0 && result > 0) + result *= -1; + + mlog(SPELLS__EFFECT_VALUES, "Result: %d (orig %d), cap %d %s", result, oresult, max, (base < 0 && result > 0)?"Inverted due to negative base":""); + + return result; +} + + +void Mob::BuffProcess() +{ + uint32 buff_count = GetMaxTotalSlots(); + + for (int buffs_i = 0; buffs_i < buff_count; ++buffs_i) + { + if (buffs[buffs_i].spellid != SPELL_UNKNOWN) + { + DoBuffTic(buffs[buffs_i].spellid, buffs[buffs_i].ticsremaining, buffs[buffs_i].casterlevel, entity_list.GetMob(buffs[buffs_i].casterid)); + // If the Mob died during DoBuffTic, then the buff we are currently processing will have been removed + if(buffs[buffs_i].spellid == SPELL_UNKNOWN) + continue; + + if(spells[buffs[buffs_i].spellid].buffdurationformula != DF_Permanent) + { + if(!zone->BuffTimersSuspended() || IsDetrimentalSpell(buffs[buffs_i].spellid)) + { + --buffs[buffs_i].ticsremaining; + + if (buffs[buffs_i].ticsremaining == 0) { + if (!IsShortDurationBuff(buffs[buffs_i].spellid) || + IsFearSpell(buffs[buffs_i].spellid) || + IsCharmSpell(buffs[buffs_i].spellid) || + IsMezSpell(buffs[buffs_i].spellid) || + IsBlindSpell(buffs[buffs_i].spellid)) + { + mlog(SPELLS__BUFFS, "Buff %d in slot %d has expired. Fading.", buffs[buffs_i].spellid, buffs_i); + BuffFadeBySlot(buffs_i); + } + } + else if (buffs[buffs_i].ticsremaining < 0) + { + mlog(SPELLS__BUFFS, "Buff %d in slot %d has expired. Fading.", buffs[buffs_i].spellid, buffs_i); + BuffFadeBySlot(buffs_i); + } + else + { + mlog(SPELLS__BUFFS, "Buff %d in slot %d has %d tics remaining.", buffs[buffs_i].spellid, buffs_i, buffs[buffs_i].ticsremaining); + } + } + else if(IsClient() && !(CastToClient()->GetClientVersionBit() & BIT_SoFAndLater)) + { + buffs[buffs_i].UpdateClient = true; + } + } + + if(IsClient()) + { + if(buffs[buffs_i].UpdateClient == true) + { + CastToClient()->SendBuffDurationPacket(buffs[buffs_i].spellid, buffs[buffs_i].ticsremaining, buffs[buffs_i].casterlevel); + buffs[buffs_i].UpdateClient = false; + } + } + } + } +} + +void Mob::DoBuffTic(uint16 spell_id, uint32 ticsremaining, uint8 caster_level, Mob* caster) { + _ZP(Mob_DoBuffTic); + + int effect, effect_value; + + if(!IsValidSpell(spell_id)) + return; + + const SPDat_Spell_Struct &spell = spells[spell_id]; + + if (spell_id == SPELL_UNKNOWN) + return; + + if(IsNPC()) + { + if(parse->SpellHasQuestSub(spell_id, "EVENT_SPELL_EFFECT_BUFF_TIC_NPC")) + { + parse->EventSpell(EVENT_SPELL_EFFECT_BUFF_TIC_NPC, CastToNPC(), NULL, spell_id, caster ? caster->GetID() : 0); + return; + } + } + else + { + if(parse->SpellHasQuestSub(spell_id, "EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT")) + { + parse->EventSpell(EVENT_SPELL_EFFECT_BUFF_TIC_CLIENT, NULL, CastToClient(), spell_id, caster ? caster->GetID() : 0); + return; + } + } + + // Check for non buff spell effects to fade + // AE melee effects + if(IsClient()) + CastToClient()->CheckAAEffect(aaEffectRampage); + + for (int i=0; i < EFFECT_COUNT; i++) + { + if(IsBlankSpellEffect(spell_id, i)) + continue; + + effect = spell.effectid[i]; + //I copied the calculation into each case which needed it instead of + //doing it every time up here, since most buff effects dont need it + + switch(effect) + { + case SE_CurrentHP: + { + effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster, ticsremaining); + //Handle client cast DOTs here. + if (caster && caster->IsClient() && IsDetrimentalSpell(spell_id) && effect_value < 0) { + effect_value = GetVulnerability(effect_value, caster, spell_id, ticsremaining); + effect_value = caster->CastToClient()->GetActDoTDamage(spell_id, effect_value); + + if (!caster->CastToClient()->GetFeigned()) + AddToHateList(caster, -effect_value); + } + + if(effect_value < 0) + { + if(caster) + { + if(!caster->IsClient()){ + effect_value = GetVulnerability(effect_value, caster, spell_id, ticsremaining); + if (!IsClient()) //Allow NPC's to generate hate if casted on other NPC's. + AddToHateList(caster, -effect_value); + } + + if(caster->IsNPC()) + effect_value = caster->CastToNPC()->GetActSpellDamage(spell_id, effect_value); + } + + effect_value = -effect_value; + Damage(caster, effect_value, spell_id, spell.skill, false, i, true); + } else if(effect_value > 0) { + // Regen spell... + // handled with bonuses + } + break; + } + case SE_HealOverTime: + { + effect_value = CalcSpellEffectValue(spell_id, i, caster_level); + if(caster) + effect_value = caster->GetActSpellHealing(spell_id, effect_value); + effect_value += effect_value * (itembonuses.HealRate + spellbonuses.HealRate) / 100; + HealDamage(effect_value, caster); + //healing aggro would go here; removed for now + break; + } + + case SE_CurrentEndurance: { + // Handled with bonuses + break; + } + + case SE_BardAEDot: + { + effect_value = CalcSpellEffectValue(spell_id, i, caster_level, caster); + + if (invulnerable || /*effect_value > 0 ||*/ DivineAura()) + break; + + if(effect_value < 0) { + effect_value = -effect_value; + if(caster){ + if(caster->IsClient() && !caster->CastToClient()->GetFeigned()){ + AddToHateList(caster, effect_value); + } + else if(!caster->IsClient()) + AddToHateList(caster, effect_value); + } + Damage(caster, effect_value, spell_id, spell.skill, false, i, true); + } else if(effect_value > 0) { + //healing spell... + HealDamage(effect_value, caster); + //healing aggro would go here; removed for now + } + break; + } + + case SE_Hate2:{ + effect_value = CalcSpellEffectValue(spell_id, i, caster_level); + if(caster){ + if(effect_value > 0){ + if(caster){ + if(caster->IsClient() && !caster->CastToClient()->GetFeigned()){ + AddToHateList(caster, effect_value); + } + else if(!caster->IsClient()) + AddToHateList(caster, effect_value); + } + }else{ + int32 newhate = GetHateAmount(caster) + effect_value; + if (newhate < 1) { + SetHate(caster,1); + } else { + SetHate(caster,newhate); + } + } + } + break; + } + + case SE_Charm: { + if (!caster || !PassCharismaCheck(caster, this, spell_id)) { + BuffFadeByEffect(SE_Charm); + } + + break; + } + + case SE_Root: { + float SpellEffectiveness = ResistSpell(spells[spell_id].resisttype, spell_id, caster); + if(SpellEffectiveness < 25) { + BuffFadeByEffect(SE_Root); + } + + break; + } + + case SE_Hunger: { + // this procedure gets called 7 times for every once that the stamina update occurs so we add 1/7 of the subtraction. + // It's far from perfect, but works without any unnecessary buff checks to bog down the server. + if(IsClient()) { + CastToClient()->m_pp.hunger_level += 5; + CastToClient()->m_pp.thirst_level += 5; + } + break; + } + case SE_Invisibility: + case SE_InvisVsAnimals: + case SE_InvisVsUndead: + { + if(ticsremaining > 3) + { + if(!IsBardSong(spell_id)) + { + double break_chance = 2.0; + if(caster) + { + break_chance -= (2 * (((double)caster->GetSkill(DIVINATION) + ((double)caster->GetLevel() * 3.0)) / 650.0)); + } + else + { + break_chance -= (2 * (((double)GetSkill(DIVINATION) + ((double)GetLevel() * 3.0)) / 650.0)); + } + + if(MakeRandomFloat(0.0, 100.0) < break_chance) + { + BuffModifyDurationBySpellID(spell_id, 3); + } + } + } + } + case SE_Invisibility2: + case SE_InvisVsUndead2: + { + if(ticsremaining <= 3 && ticsremaining > 1) + { + Message_StringID(MT_Spells, INVIS_BEGIN_BREAK); + } + break; + } + case SE_InterruptCasting: + { + if(IsCasting()) + { + if(MakeRandomInt(0, 100) <= spells[spell_id].base[i]) + { + InterruptSpell(); + } + } + break; + } + // These effects always trigger when they fade. + case SE_ImprovedSpellEffect: + case SE_BossSpellTrigger: + case SE_CastOnWearoff: + { + if (ticsremaining == 1) + { + SpellOnTarget(spells[spell_id].base[i], this); + } + break; + } + case SE_LocateCorpse: + { + // This is handled by the client prior to SoD. + + if(IsClient() && (CastToClient()->GetClientVersionBit() & BIT_SoDAndLater)) + CastToClient()->LocateCorpse(); + } + case SE_TotalHP: + { + if (spell.formula[i] > 1000 && spell.formula[i] < 1999) + { + // These formulas can affect Max HP each tick + // Maybe there is a more efficient way to recalculate this for just Max HP each tic... + //CalcBonuses(); + CalcSpellBonuses(&spellbonuses); + CalcMaxHP(); + } + break; + } + default: + { + // do we need to do anyting here? + } + } + } +} + +// solar: removes the buff in the buff slot 'slot' +void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) +{ + if(slot < 0 || slot > GetMaxTotalSlots()) + return; + + if(!IsValidSpell(buffs[slot].spellid)) + return; + + if (IsClient() && !CastToClient()->IsDead()) + CastToClient()->MakeBuffFadePacket(buffs[slot].spellid, slot); + + mlog(SPELLS__BUFFS, "Fading buff %d from slot %d", buffs[slot].spellid, slot); + + if(spells[buffs[slot].spellid].viral_targets > 0) { + bool last_virus = true; + for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) + { + if(viral_spells[i] && viral_spells[i] != buffs[slot].spellid) + { + // If we have a virus that doesn't match this one then don't stop the viral timer + last_virus = false; + } + } + // This is the last virus on us so lets stop timer + if(last_virus) { + viral_timer.Disable(); + has_virus = false; + } + } + + for (int i=0; i < EFFECT_COUNT; i++) + { + if(IsBlankSpellEffect(buffs[slot].spellid, i)) + continue; + + switch (spells[buffs[slot].spellid].effectid[i]) + { + case SE_AddMeleeProc: + case SE_WeaponProc: + { + uint16 procid = GetProcID(buffs[slot].spellid, i); + RemoveProcFromWeapon(procid, false); + break; + } + + case SE_SkillProc2: + case SE_SkillProc: + { + uint16 procid = GetProcID(buffs[slot].spellid, i); + RemoveSkillProc(procid); + break; + } + + case SE_DefensiveProc: + { + uint16 procid = GetProcID(buffs[slot].spellid, i); + RemoveDefensiveProc(procid); + break; + } + + case SE_RangedProc: + { + uint16 procid = GetProcID(buffs[slot].spellid, i); + RemoveRangedProc(procid); + break; + } + + case SE_SummonHorse: + { + if(IsClient()) + { + /*Mob* horse = entity_list.GetMob(this->CastToClient()->GetHorseId()); + if (horse) horse->Depop(); + CastToClient()->SetHasMount(false);*/ + CastToClient()->SetHorseId(0); + } + break; + } + + case SE_IllusionCopy: + case SE_Illusion: + { + SendIllusionPacket(0, GetBaseGender()); + if(GetRace() == OGRE){ + SendAppearancePacket(AT_Size, 9); + } + else if(GetRace() == TROLL){ + SendAppearancePacket(AT_Size, 8); + } + else if(GetRace() == VAHSHIR || GetRace() == FROGLOK || GetRace() == BARBARIAN){ + SendAppearancePacket(AT_Size, 7); + } + else if(GetRace() == HALF_ELF || GetRace() == WOOD_ELF || GetRace() == DARK_ELF){ + SendAppearancePacket(AT_Size, 5); + } + else if(GetRace() == DWARF){ + SendAppearancePacket(AT_Size, 4); + } + else if(GetRace() == HALFLING || GetRace() == GNOME){ + SendAppearancePacket(AT_Size, 3); + } + else{ + SendAppearancePacket(AT_Size, 6); + } + for(int x = 0; x < 7; x++){ + SendWearChange(x); + } + break; + } + + case SE_Levitate: + { + if (!AffectedBySpellExcludingSlot(slot, SE_Levitate)) + SendAppearancePacket(AT_Levitate, 0); + break; + } + + case SE_Invisibility2: + case SE_Invisibility: + { + SetInvisible(0); + break; + } + + case SE_InvisVsUndead2: + case SE_InvisVsUndead: + { + invisible_undead = false; // Mongrel: No longer IVU + break; + } + + case SE_InvisVsAnimals: + { + invisible_animals = false; + break; + } + + case SE_SeeInvis: + { + see_invis = 0; + break; + } + + case SE_Silence: + { + Silence(false); + break; + } + + case SE_Amnesia: + { + Amnesia(false); + break; + } + + case SE_DivineAura: + { + SetInvul(false); + break; + } + + case SE_Rune: + { + buffs[slot].melee_rune = 0; + break; + } + + case SE_AbsorbMagicAtt: + { + buffs[slot].magic_rune = 0; + break; + } + + case SE_Familiar: + { + Mob *mypet = GetPet(); + if (mypet){ + if(mypet->IsNPC()) + mypet->CastToNPC()->Depop(); + SetPetID(0); + } + break; + } + + case SE_Mez: + { + SendAppearancePacket(AT_Anim, ANIM_STAND); // unfreeze + this->mezzed = false; + break; + } + + case SE_Charm: + { + if(IsNPC()) + { + CastToNPC()->RestoreGuardSpotCharm(); + } + + Mob* tempmob = GetOwner(); + SetOwnerID(0); + if(tempmob) + { + tempmob->SetPet(0); + } + if (IsAIControlled()) + { + // clear the hate list of the mobs + entity_list.ReplaceWithTarget(this, tempmob); + WipeHateList(); + if(tempmob) + AddToHateList(tempmob, 1, 0); + SendAppearancePacket(AT_Anim, ANIM_STAND); + } + if(tempmob && tempmob->IsClient()) + { + EQApplicationPacket *app = new EQApplicationPacket(OP_Charm, sizeof(Charm_Struct)); + Charm_Struct *ps = (Charm_Struct*)app->pBuffer; + ps->owner_id = tempmob->GetID(); + ps->pet_id = this->GetID(); + ps->command = 0; + entity_list.QueueClients(this, app); + safe_delete(app); + } + if(IsClient()) + { + InterruptSpell(); + if (this->CastToClient()->IsLD()) + AI_Start(CLIENT_LD_TIMEOUT); + else + { + bool feared = FindType(SE_Fear); + if(!feared) + AI_Stop(); + } + } + break; + } + + case SE_Root: + { + rooted = false; + rooted_mod = 0; + break; + } + + case SE_Fear: + { + if(RuleB(Combat, EnableFearPathing)){ + if(IsClient()) + { + bool charmed = FindType(SE_Charm); + if(!charmed) + AI_Stop(); + } + + if(curfp) { + curfp = false; + break; + } + } + else + { + UnStun(); + } + break; + } + + case SE_ImmuneFleeing: + { + if(RuleB(Combat, EnableFearPathing)){ + if(flee_mode) { + curfp = true; + CheckFlee(); + break; + } + } + } + + case SE_BindSight: + { + if(IsClient()) + { + CastToClient()->SetBindSightTarget(NULL); + } + break; + } + + case SE_SetBodyType: + { + SetBodyType(GetOrigBodyType(), false); + break; + } + + case SE_MovementSpeed: + { + if(IsClient()) + { + Client *my_c = CastToClient(); + uint32 cur_time = Timer::GetCurrentTime(); + if((cur_time - my_c->m_TimeSinceLastPositionCheck) > 1000) + { + float speed = (my_c->m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - my_c->m_TimeSinceLastPositionCheck); + float runs = my_c->GetRunspeed(); + if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) + { + if(!my_c->GetGMSpeed() && (runs >= my_c->GetBaseRunspeed() || (speed > (my_c->GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) + { + printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__, + my_c->m_DistanceSinceLastPositionCheck, (cur_time - my_c->m_TimeSinceLastPositionCheck), speed); + if(my_c->IsShadowStepExempted()) + { + if(my_c->m_DistanceSinceLastPositionCheck > 800) + { + my_c->CheatDetected(MQWarpShadowStep, my_c->GetX(), my_c->GetY(), my_c->GetZ()); + } + } + else if(my_c->IsKnockBackExempted()) + { + //still potential to trigger this if you're knocked back off a + //HUGE fall that takes > 2.5 seconds + if(speed > 30.0f) + { + my_c->CheatDetected(MQWarpKnockBack, my_c->GetX(), my_c->GetY(), my_c->GetZ()); + } + } + else if(!my_c->IsPortExempted()) + { + if(!my_c->IsMQExemptedArea(zone->GetZoneID(), my_c->GetX(), my_c->GetY(), my_c->GetZ())) + { + if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor))) + { + my_c->m_TimeSinceLastPositionCheck = cur_time; + my_c->m_DistanceSinceLastPositionCheck = 0.0f; + my_c->CheatDetected(MQWarp, my_c->GetX(), my_c->GetY(), my_c->GetZ()); + //my_c->Death(my_c, 10000000, SPELL_UNKNOWN, _1H_BLUNT); + } + else + { + my_c->CheatDetected(MQWarpLight, my_c->GetX(), my_c->GetY(), my_c->GetZ()); + } + } + } + } + } + } + my_c->m_TimeSinceLastPositionCheck = cur_time; + my_c->m_DistanceSinceLastPositionCheck = 0.0f; + } + } + } + } + + // notify caster (or their master) of buff that it's worn off + Mob *p = entity_list.GetMob(buffs[slot].casterid); + if (p && p != this && !IsBardSong(buffs[slot].spellid)) + { + Mob *notify = p; + if(p->IsPet()) + notify = p->GetOwner(); + if(p) { + notify->Message_StringID(MT_WornOff, SPELL_WORN_OFF_OF, + spells[buffs[slot].spellid].name, GetCleanName()); + } + } + + buffs[slot].spellid = SPELL_UNKNOWN; + if(IsPet() && GetOwner() && GetOwner()->IsClient()) { + SendPetBuffsToClient(); + } + if((IsClient() && !CastToClient()->GetPVP()) || (IsPet() && GetOwner() && GetOwner()->IsClient() && !GetOwner()->CastToClient()->GetPVP()) || + (IsMerc() && GetOwner() && GetOwner()->IsClient() && !GetOwner()->CastToClient()->GetPVP())) + { + EQApplicationPacket *outapp = MakeBuffsPacket(); + + entity_list.QueueClientsByTarget(this, outapp, false, NULL, true, false, BIT_SoDAndLater); + if(GetTarget() == this) { + CastToClient()->QueuePacket(outapp); + } + + safe_delete(outapp); + } + + if(IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) + { + EQApplicationPacket *outapp = MakeBuffsPacket(false); + CastToClient()->FastQueuePacket(&outapp); + } + + if (iRecalcBonuses) + CalcBonuses(); +} + +/* No longer used. +int16 Client::CalcAAFocusEffect(focusType type, uint16 focus_spell, uint16 spell_id) +{ + uint32 slots = 0; + uint32 aa_AA = 0; + uint32 aa_value = 0; + + int32 value = 0; + // Iterate through all of the client's AAs + 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 > 0 || aa_value > 0) + { + slots = zone->GetTotalAALevels(aa_AA); + if (slots > 0) + for(int j = 1;j <= slots; j++) + { + switch (aa_effects[aa_AA][j].skill_id) + { + case SE_TriggerOnCast: + // If focus_spell matches the spell listed in the DB, load these restrictions + if(type == focusTriggerOnCast && focus_spell == aa_effects[aa_AA][j].base1) + value = CalcAAFocus(type, aa_AA, spell_id); + break; + } + } + } + } + return value; +} +*/ + + +int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) +{ + const SPDat_Spell_Struct &spell = spells[spell_id]; + + int16 value = 0; + int lvlModifier = 100; + int spell_level = 0; + int lvldiff = 0; + bool LimitSpellSkill = false; + bool SpellSkill_Found = false; + uint32 effect = 0; + int32 base1 = 0; + int32 base2 = 0; + uint32 slot = 0; + + bool LimitFound = false; + int FocusCount = 0; + + std::map >::const_iterator find_iter = aa_effects.find(aa_ID); + if(find_iter == aa_effects.end()) + { + return 0; + } + + for (map::const_iterator iter = aa_effects[aa_ID].begin(); iter != aa_effects[aa_ID].end(); ++iter) + { + effect = iter->second.skill_id; + base1 = iter->second.base1; + base2 = iter->second.base2; + slot = iter->second.slot; + + //AA Foci's can contain multiple focus effects within the same AA. + //To handle this we will not automatically return zero if a limit is found. + //Instead if limit is found and multiple effects, we will reset the limit check + //when the next valid focus effect is found. + if (IsFocusEffect(0, 0, true,effect) || (effect == SE_TriggerOnCast)){ + FocusCount++; + //If limit found on prior check next, else end loop. + if (FocusCount > 1){ + if (LimitFound){ + value = 0; + LimitFound = false; + } + + else{ + break; + } + } + } + + + switch (effect) + { + case SE_Blank: + break; + + //Handle Focus Limits + case SE_LimitResist: + if(base1) + { + if(spell.resisttype != base1) + LimitFound = true; + } + break; + case SE_LimitInstant: + if(spell.buffduration) + LimitFound = true; + break; + case SE_LimitMaxLevel: + spell_level = spell.classes[(GetClass()%16) - 1]; + lvldiff = spell_level - base1; + //every level over cap reduces the effect by base2 percent unless from a clicky when ItemCastsUseFocus is true + if(lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)) + { + if(base2 > 0) + { + lvlModifier -= base2*lvldiff; + if(lvlModifier < 1) + LimitFound = true; + } + else { + LimitFound = true; + } + } + break; + case SE_LimitMinLevel: + if((spell.classes[(GetClass()%16) - 1]) < base1) + LimitFound = true; + break; + case SE_LimitCastTime: + if (spell.cast_time < base1) + LimitFound = true; + break; + case SE_LimitSpell: + // Exclude spell(any but this) + if(base1 < 0) { + if (spell_id == (base1*-1)) + LimitFound = true; + } + else { + // Include Spell(only this) + if (spell_id != base1) + LimitFound = true; + } + break; + case SE_LimitMinDur: + if (base1 > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) + LimitFound = true; + break; + case SE_LimitEffect: + // Exclude effect(any but this) + if(base1 < 0) { + if(IsEffectInSpell(spell_id,(base1*-1))) + LimitFound = true; + } + else { + // Include effect(only this) + if(!IsEffectInSpell(spell_id,base1)) + LimitFound = true; + } + break; + case SE_LimitSpellType: + switch(base1) + { + case 0: + if (!IsDetrimentalSpell(spell_id)) + LimitFound = true; + break; + case 1: + if (!IsBeneficialSpell(spell_id)) + LimitFound = true; + break; + } + break; + + case SE_LimitManaCost: + if(spell.mana < base1) + LimitFound = true; + break; + + case SE_LimitTarget: + // Exclude + if(base1 < 0){ + if(-base1 == spell.targettype) + LimitFound = true; + } + // Include + else { + if(base1 != spell.targettype) + LimitFound = true; + } + break; + + case SE_CombatSkills: + // 1 is for disciplines only + if(base1 == 1 && !IsDiscipline(spell_id)) + LimitFound = true; + // 0 is spells only + else if(base1 == 0 && IsDiscipline(spell_id)) + LimitFound = true; + break; + + case SE_LimitSpellGroup: + if(base1 > 0 && base1 != spell.spellgroup) + LimitFound = true; + else if(base1 < 0 && base1 == spell.spellgroup) + LimitFound = true; + break; + + + case SE_LimitSpellSkill: + LimitSpellSkill = true; + if(base1 == spell.skill) + SpellSkill_Found = true; + break; + + case SE_LimitExcludeSkill:{ + int16 spell_skill = spell.skill * -1; + if(base1 == spell_skill) + LimitFound = true; + break; + } + + case SE_LimitClass: + //Do not use this limit more then once per spell. If multiple class, treat value like items would. + if (!PassLimitClass(base1, GetClass())) + LimitFound = true; + break; + + + //Handle Focus Effects + case SE_ImprovedDamage: + if (type == focusImprovedDamage && base1 > value) + value = base1; + break; + + case SE_ImprovedHeal: + if (type == focusImprovedHeal && base1 > value) + value = base1; + break; + + case SE_ReduceManaCost: + if (type == focusManaCost ) + value = base1; + break; + + case SE_IncreaseSpellHaste: + if (type == focusSpellHaste && base1 > value) + value = base1; + break; + + case SE_IncreaseSpellDuration: + if (type == focusSpellDuration && base1 > value) + value = base1; + break; + + case SE_SpellDurationIncByTic: + if (type == focusSpellDurByTic && base1 > value) + value = base1; + break; + + case SE_SwarmPetDuration: + if (type == focusSwarmPetDuration && base1 > value) + value = base1; + break; + + case SE_IncreaseRange: + if (type == focusRange && base1 > value) + value = base1; + break; + + case SE_ReduceReagentCost: + if (type == focusReagentCost && base1 > value) + value = base1; + break; + + case SE_PetPowerIncrease: + if (type == focusPetPower && base1 > value) + value = base1; + break; + + case SE_SpellResistReduction: + if (type == focusResistRate && base1 > value) + value = base1; + break; + + case SE_SpellHateMod: + if (type == focusSpellHateMod) + { + if(value != 0) + { + if(value > 0) + { + if(base1 > value) + { + value = base1; + } + } + else + { + if(base1 < value) + { + value = base1; + } + } + } + else + value = base1; + } + break; + + case SE_ReduceReuseTimer: + { + if(type == focusReduceRecastTime) + value = base1 / 1000; + + break; + } + + case SE_TriggerOnCast: + { + if(type == focusTriggerOnCast) + { + if(MakeRandomInt(0, 100) <= base1){ + value = base2; + } + + else{ + value = 0; + LimitFound = true; + } + } + break; + } + case SE_SpellVulnerability: + { + if(type == focusSpellVulnerability) + { + value = base1; + } + break; + } + case SE_BlockNextSpellFocus: + { + if(type == focusBlockNextSpell) + { + if(MakeRandomInt(1, 100) <= base1) + value = 1; + } + break; + } + case SE_Twincast: + { + if(type == focusTwincast) + { + value = base1; + } + break; + } + + /* + case SE_SympatheticProc: + { + if(type == focusSympatheticProc) + { + float ProcChance, ProcBonus; + int16 ProcRateMod = base1; //Baseline is 100 for most Sympathetic foci + int32 cast_time = GetActSpellCasttime(spell_id, spells[spell_id].cast_time); + GetSympatheticProcChances(ProcBonus, ProcChance, cast_time, ProcRateMod); + + if(MakeRandomFloat(0, 1) <= ProcChance) + value = focus_id; + + else + value = 0; + } + break; + } + */ + case SE_SpellDamage: + { + if(type == focusSpellDamage) + value = base1; + + break; + } + + case SE_FF_Damage_Amount: + { + if(type == focusFF_Damage_Amount) + value = base1; + + break; + } + + case SE_Empathy: + { + if(type == focusAdditionalDamage) + value = base1; + + break; + } + + case SE_CriticalHealRate: + { + if (type == focusCriticalHealRate) + value = base1; + + break; + } + + case SE_AdditionalHeal: + { + if(type == focusAdditionalHeal) + value = base1; + + break; + } + + case SE_AdditionalHeal2: + { + if(type == focusAdditionalHeal2) + value = base1; + + break; + } + + case SE_HealRate2: + { + if(type == focusHealRate) + value = base1; + + break; + } + + case SE_IncreaseSpellPower: + { + if (type == focusSpellEffectiveness) + value = base1; + + break; + } + case SE_ImprovedDamage2: + { + if(type == focusImprovedDamage2) + value = base1; + + break; + } + + case SE_IncreaseNumHits: + { + if(type == focusIncreaseNumHits) + value = base1; + + break; + } + + //Check for spell skill limits. + if ((LimitSpellSkill) && (!SpellSkill_Found)) + return 0; + + } + } + + if (LimitFound){ + return 0; + } + + return(value*lvlModifier/100); +} + +//given an item/spell's focus ID and the spell being cast, determine the focus ammount, if any +//assumes that spell_id is not a bard spell and that both ids are valid spell ids +int16 Mob::CalcFocusEffect(focusType type, 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; + int lvlModifier = 100; + int spell_level = 0; + int lvldiff = 0; + bool LimitSpellSkill = false; + bool SpellSkill_Found = false; + + for (int i = 0; i < EFFECT_COUNT; i++) { + + switch (focus_spell.effectid[i]) { + case SE_Blank: + break; + //check limits + + case SE_LimitResist:{ + if(focus_spell.base[i]){ + if(spell.resisttype != focus_spell.base[i]) + return(0); + } + break; + } + case SE_LimitInstant:{ + if(spell.buffduration) + return(0); + break; + } + + case SE_LimitMaxLevel:{ + if (IsNPC()) + break; + spell_level = spell.classes[(GetClass()%16) - 1]; + lvldiff = spell_level - focus_spell.base[i]; + //every level over cap reduces the effect by focus_spell.base2[i] percent unless from a clicky when ItemCastsUseFocus is true + if(lvldiff > 0 && (spell_level <= RuleI(Character, MaxLevel) || RuleB(Character, ItemCastsUseFocus) == false)) + { + if(focus_spell.base2[i] > 0) + { + lvlModifier -= focus_spell.base2[i]*lvldiff; + if(lvlModifier < 1) + return 0; + } + else + { + return 0; + } + } + break; + } + + case SE_LimitMinLevel: + if (IsNPC()) + break; + if (spell.classes[(GetClass()%16) - 1] < focus_spell.base[i]) + return(0); + break; + + case SE_LimitCastTime: + if (spells[spell_id].cast_time < (uint16)focus_spell.base[i]) + return(0); + break; + + case SE_LimitSpell: + if(focus_spell.base[i] < 0) { //exclude spell + if (spell_id == (focus_spell.base[i]*-1)) + return(0); + } else { + //this makes the assumption that only one spell can be explicitly included... + if (spell_id != focus_spell.base[i]) + return(0); + } + break; + + case SE_LimitMinDur: + if (focus_spell.base[i] > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration)) + return(0); + break; + + case SE_LimitEffect: + if(focus_spell.base[i] < 0){ + if(IsEffectInSpell(spell_id,focus_spell.base[i])){ //we limit this effect, can't have + return 0; + } + } + else{ + if(focus_spell.base[i] == SE_SummonPet) //summoning haste special case + { //must have one of the three pet effects to qualify + if(!IsEffectInSpell(spell_id, SE_SummonPet) && + !IsEffectInSpell(spell_id, SE_NecPet) && + !IsEffectInSpell(spell_id, SE_SummonBSTPet)) + { + return 0; + } + } + else if(!IsEffectInSpell(spell_id,focus_spell.base[i])){ //we limit this effect, must have + return 0; + } + } + break; + + + case SE_LimitSpellType: + switch( focus_spell.base[i] ) + { + case 0: + if (!IsDetrimentalSpell(spell_id)) + return 0; + break; + case 1: + if (!IsBeneficialSpell(spell_id)) + return 0; + break; + default: + LogFile->write(EQEMuLog::Normal, "CalcFocusEffect: unknown limit spelltype %d", focus_spell.base[i]); + } + break; + + case SE_LimitManaCost: + if(spell.mana < focus_spell.base[i]) + return 0; + break; + + case SE_LimitTarget: + // Exclude + if((focus_spell.base[i] < 0) && -focus_spell.base[i] == spell.targettype) + return 0; + // Include + else if (focus_spell.base[i] > 0 && focus_spell.base[i] != spell.targettype) + return 0; + + break; + + case SE_CombatSkills: + // 1 is for disciplines only + if(focus_spell.base[i] == 1 && !IsDiscipline(spell_id)) + return 0; + // 0 is for spells only + else if(focus_spell.base[i] == 0 && IsDiscipline(spell_id)) + return 0; + break; + + case SE_LimitSpellGroup: + if(focus_spell.base[i] > 0 && focus_spell.base[i] != spell.spellgroup) + return 0; + else if(focus_spell.base[i] < 0 && focus_spell.base[i] == spell.spellgroup) + return 0; + break; + + case SE_LimitSpellSkill: + LimitSpellSkill = true; + if(focus_spell.base[i] == spell.skill) + SpellSkill_Found = true; + break; + + case SE_LimitExcludeSkill:{ + int16 spell_skill = spell.skill * -1; + if(focus_spell.base[i] == spell_skill) + return 0; + break; + } + + case SE_LimitClass: + //Do not use this limit more then once per spell. If multiple class, treat value like items would. + if (!PassLimitClass(focus_spell.base[i], GetClass())) + return 0; + break; + + //handle effects + case SE_ImprovedDamage: + // No Spell used this, its handled by different spell effect IDs. + if (type == focusImprovedDamage) { + // This is used to determine which focus should be used for the random calculation + if(best_focus) { + // If the spell contains a value in the base2 field then that is the max value + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + // If the spell does not contain a base2 value, then its a straight non random value + else { + value = focus_spell.base[i]; + } + } + // Actual focus calculation starts here + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = MakeRandomInt(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + case SE_ImprovedHeal: + if (type == focusImprovedHeal) { + if(best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + else { + value = focus_spell.base[i]; + } + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = MakeRandomInt(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + case SE_ReduceManaCost: + if (type == focusManaCost) { + if(best_focus) { + if (focus_spell.base2[i] != 0) { + value = focus_spell.base2[i]; + } + else { + value = focus_spell.base[i]; + } + } + else if (focus_spell.base2[i] == 0 || focus_spell.base[i] == focus_spell.base2[i]) { + value = focus_spell.base[i]; + } + else { + value = MakeRandomInt(focus_spell.base[i], focus_spell.base2[i]); + } + } + break; + + case SE_IncreaseSpellHaste: + if (type == focusSpellHaste && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_IncreaseSpellDuration: + if (type == focusSpellDuration && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_SpellDurationIncByTic: + if (type == focusSpellDurByTic && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_SwarmPetDuration: + if (type == focusSwarmPetDuration && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_IncreaseRange: + if (type == focusRange && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_ReduceReagentCost: + if (type == focusReagentCost && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_PetPowerIncrease: + if (type == focusPetPower && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_SpellResistReduction: + if (type == focusResistRate && focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + break; + case SE_SpellHateMod: + if (type == focusSpellHateMod) + { + if(value != 0) + { + if(value > 0) + { + if(focus_spell.base[i] > value) + { + value = focus_spell.base[i]; + } + } + else + { + if(focus_spell.base[i] < value) + { + value = focus_spell.base[i]; + } + } + } + else + value = focus_spell.base[i]; + } + break; + + case SE_ReduceReuseTimer: + { + if(type == focusReduceRecastTime) + value = focus_spell.base[i] / 1000; + + break; + } + + case SE_TriggerOnCast: + { + if(type == focusTriggerOnCast) + + if(MakeRandomInt(0, 100) <= focus_spell.base[i]) + value = focus_spell.base2[i]; + + else + value = 0; + + break; + } + case SE_SpellVulnerability: + { + if(type == focusSpellVulnerability) + { + value = focus_spell.base[i]; + } + break; + } + case SE_BlockNextSpellFocus: + { + if(type == focusBlockNextSpell) + { + if(MakeRandomInt(1, 100) <= focus_spell.base[i]) + value = 1; + } + break; + } + case SE_Twincast: + { + if(type == focusTwincast) + { + value = focus_spell.base[i]; + } + break; + } + case SE_SympatheticProc: + { + if(type == focusSympatheticProc) + { + float ProcChance, ProcBonus; + int16 ProcRateMod = focus_spell.base[i]; //Baseline is 100 for most Sympathetic foci + int32 cast_time = GetActSpellCasttime(spell_id, spells[spell_id].cast_time); + GetSympatheticProcChances(ProcBonus, ProcChance, cast_time, ProcRateMod); + + if(MakeRandomFloat(0, 1) <= ProcChance) + value = focus_id; + + else + value = 0; + } + break; + } + case SE_SpellDamage: + { + if(type == focusSpellDamage) + value = focus_spell.base[i]; + + break; + } + + case SE_FF_Damage_Amount: + { + if(type == focusFF_Damage_Amount) + value = focus_spell.base[i]; + + break; + } + + case SE_Empathy: + { + if(type == focusAdditionalDamage) + value = focus_spell.base[i]; + + break; + } + + case SE_CriticalHealRate: + { + if (type == focusCriticalHealRate) + value = focus_spell.base[i]; + + break; + } + + case SE_AdditionalHeal: + { + if(type == focusAdditionalHeal) + value = focus_spell.base[i]; + + break; + } + + case SE_AdditionalHeal2: + { + if(type == focusAdditionalHeal2) + value = focus_spell.base[i]; + + break; + } + + case SE_HealRate2: + { + if(type == focusHealRate) + value = focus_spell.base[i]; + + break; + } + + case SE_IncreaseSpellPower: + { + if (type == focusSpellEffectiveness) + value = focus_spell.base[i]; + + break; + } + case SE_ImprovedDamage2: + { + if(type == focusImprovedDamage2) + value = focus_spell.base[i]; + + break; + } + + case SE_IncreaseNumHits: + { + if(type == focusIncreaseNumHits) + value = focus_spell.base[i]; + + break; + } + +#if EQDEBUG >= 6 + //this spits up a lot of garbage when calculating spell focuses + //since they have all kinds of extra effects on them. + default: + LogFile->write(EQEMuLog::Normal, "CalcFocusEffect: unknown effectid %d", focus_spell.effectid[i]); +#endif + } + } + //Check for spell skill limits. + if ((LimitSpellSkill) && (!SpellSkill_Found)) + return 0; + + return(value*lvlModifier/100); +} + +int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { + + if (IsBardSong(spell_id)) + return 0; + + uint16 proc_spellid = 0; + uint8 SizeProcList = 0; + uint8 MAX_SYMPATHETIC = 10; + + vector SympatheticProcList; + + //item focus + if (itembonuses.FocusEffects[type]){ + + const Item_Struct* TempItem = 0; + + for(int x=0; x<=21; x++) + { + if (SizeProcList > MAX_SYMPATHETIC) + continue; + + TempItem = NULL; + ItemInst* ins = GetInv().GetItem(x); + if (!ins) + continue; + TempItem = ins->GetItem(); + if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) { + + proc_spellid = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id); + + if (proc_spellid > 0) + { + SympatheticProcList.push_back(proc_spellid); + SizeProcList = SympatheticProcList.size(); + } + } + + for(int y = 0; y < MAX_AUGMENT_SLOTS; ++y) + { + if (SizeProcList > MAX_SYMPATHETIC) + continue; + + ItemInst *aug = NULL; + aug = ins->GetAugment(y); + if(aug) + { + const Item_Struct* TempItemAug = aug->GetItem(); + if (TempItemAug && TempItemAug->Focus.Effect > 0 && TempItemAug->Focus.Effect != SPELL_UNKNOWN) { + + proc_spellid = CalcFocusEffect(type, TempItemAug->Focus.Effect, spell_id); + + if (proc_spellid > 0) + { + SympatheticProcList.push_back(proc_spellid); + SizeProcList = SympatheticProcList.size(); + } + } + } + } + } + } + + //Spell Focus + if (spellbonuses.FocusEffects[type]){ + int buff_slot = 0; + uint16 focusspellid = 0; + uint32 buff_max = GetMaxTotalSlots(); + for (buff_slot = 0; buff_slot < buff_max; buff_slot++) { + + if (SizeProcList > MAX_SYMPATHETIC) + continue; + + focusspellid = buffs[buff_slot].spellid; + if (focusspellid == 0 || focusspellid >= SPDAT_RECORDS) + continue; + + proc_spellid = CalcFocusEffect(type, focusspellid, spell_id); + + if (proc_spellid > 0) + { + SympatheticProcList.push_back(proc_spellid); + SizeProcList = SympatheticProcList.size(); + } + } + } + + if (SizeProcList > 0) + { + uint8 random = MakeRandomInt(0, SizeProcList-1); + int FinalSympatheticProc = SympatheticProcList[random]; + SympatheticProcList.clear(); + return FinalSympatheticProc; + } + + return 0; +} + +int16 Client::GetFocusEffect(focusType type, uint16 spell_id) { + + if (IsBardSong(spell_id) && type != focusSpellEffectiveness) + return 0; + + int16 realTotal = 0; + int16 realTotal2 = 0; + int16 realTotal3 = 0; + bool rand_effectiveness = false; + + //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages + //In these cases we need to find the most powerful effect, so that each piece of gear wont get its own chance + if((type == focusManaCost || type == focusImprovedHeal || type == focusImprovedDamage) + && RuleB(Spells, LiveLikeFocusEffects)) + { + rand_effectiveness = true; + } + + //Check if item focus effect exists for the client. + if (itembonuses.FocusEffects[type]){ + + const Item_Struct* TempItem = 0; + const Item_Struct* UsedItem = 0; + uint16 UsedFocusID = 0; + int16 Total = 0; + int16 focus_max = 0; + int16 focus_max_real = 0; + + //item focus + for(int x=0; x<=21; x++) + { + TempItem = NULL; + ItemInst* ins = GetInv().GetItem(x); + if (!ins) + continue; + TempItem = ins->GetItem(); + if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) { + if(rand_effectiveness) { + focus_max = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id, true); + if (focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) { + focus_max_real = focus_max; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } else if (focus_max < 0 && focus_max < focus_max_real) { + focus_max_real = focus_max; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } + } + else { + Total = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id); + if (Total > 0 && realTotal >= 0 && Total > realTotal) { + realTotal = Total; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } else if (Total < 0 && Total < realTotal) { + realTotal = Total; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } + } + } + + for(int y = 0; y < MAX_AUGMENT_SLOTS; ++y) + { + ItemInst *aug = NULL; + aug = ins->GetAugment(y); + if(aug) + { + const Item_Struct* TempItemAug = aug->GetItem(); + if (TempItemAug && TempItemAug->Focus.Effect > 0 && TempItemAug->Focus.Effect != SPELL_UNKNOWN) { + if(rand_effectiveness) { + focus_max = CalcFocusEffect(type, TempItemAug->Focus.Effect, spell_id, true); + if (focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) { + focus_max_real = focus_max; + UsedItem = TempItem; + UsedFocusID = TempItemAug->Focus.Effect; + } else if (focus_max < 0 && focus_max < focus_max_real) { + focus_max_real = focus_max; + UsedItem = TempItem; + UsedFocusID = TempItemAug->Focus.Effect; + } + } + else { + Total = CalcFocusEffect(type, TempItemAug->Focus.Effect, spell_id); + if (Total > 0 && realTotal >= 0 && Total > realTotal) { + realTotal = Total; + UsedItem = TempItem; + UsedFocusID = TempItemAug->Focus.Effect; + } else if (Total < 0 && Total < realTotal) { + realTotal = Total; + UsedItem = TempItem; + UsedFocusID = TempItemAug->Focus.Effect; + } + } + } + } + } + } + + //Tribute Focus + for(int x = TRIBUTE_SLOT_START; x < (TRIBUTE_SLOT_START + MAX_PLAYER_TRIBUTES); ++x) + { + TempItem = NULL; + ItemInst* ins = GetInv().GetItem(x); + if (!ins) + continue; + TempItem = ins->GetItem(); + if (TempItem && TempItem->Focus.Effect > 0 && TempItem->Focus.Effect != SPELL_UNKNOWN) { + if(rand_effectiveness) { + focus_max = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id, true); + if (focus_max > 0 && focus_max_real >= 0 && focus_max > focus_max_real) { + focus_max_real = focus_max; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } else if (focus_max < 0 && focus_max < focus_max_real) { + focus_max_real = focus_max; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } + } + else { + Total = CalcFocusEffect(type, TempItem->Focus.Effect, spell_id); + if (Total > 0 && realTotal >= 0 && Total > realTotal) { + realTotal = Total; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } + else if (Total < 0 && Total < realTotal) { + realTotal = Total; + UsedItem = TempItem; + UsedFocusID = TempItem->Focus.Effect; + } + } + } + } + + if(UsedItem && rand_effectiveness && focus_max_real != 0) + realTotal = CalcFocusEffect(type, UsedFocusID, spell_id); + + if (realTotal != 0 && UsedItem) + Message_StringID(MT_Spells, BEGINS_TO_GLOW, UsedItem->Name); + } + + //Check if spell focus effect exists for the client. + if (spellbonuses.FocusEffects[type]){ + + //Spell Focus + int16 Total2 = 0; + int16 focus_max2 = 0; + int16 focus_max_real2 = 0; + + int buff_tracker = -1; + int buff_slot = 0; + uint16 focusspellid = 0; + uint16 focusspell_tracker = 0; + uint32 buff_max = GetMaxTotalSlots(); + for (buff_slot = 0; buff_slot < buff_max; buff_slot++) { + focusspellid = buffs[buff_slot].spellid; + if (focusspellid == 0 || focusspellid >= SPDAT_RECORDS) + continue; + + if(rand_effectiveness) { + focus_max2 = CalcFocusEffect(type, focusspellid, spell_id, true); + if (focus_max2 > 0 && focus_max_real2 >= 0 && focus_max2 > focus_max_real2) { + focus_max_real2 = focus_max2; + buff_tracker = buff_slot; + focusspell_tracker = focusspellid; + } else if (focus_max2 < 0 && focus_max2 < focus_max_real2) { + focus_max_real2 = focus_max2; + buff_tracker = buff_slot; + focusspell_tracker = focusspellid; + } + } + else { + Total2 = CalcFocusEffect(type, focusspellid, spell_id); + if (Total2 > 0 && realTotal2 >= 0 && Total2 > realTotal2) { + realTotal2 = Total2; + buff_tracker = buff_slot; + focusspell_tracker = focusspellid; + } else if (Total2 < 0 && Total2 < realTotal2) { + realTotal2 = Total2; + buff_tracker = buff_slot; + focusspell_tracker = focusspellid; + } + } + } + + if(focusspell_tracker && rand_effectiveness && focus_max_real2 != 0) + realTotal2 = CalcFocusEffect(type, focusspell_tracker, spell_id); + + // For effects like gift of mana that only fire once, save the spellid into an array that consists of all available buff slots. + if(buff_tracker >= 0 && buffs[buff_tracker].numhits > 0) { + m_spellHitsLeft[buff_tracker] = focusspell_tracker; + } + } + + + // AA Focus + if (aabonuses.FocusEffects[type]){ + + 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; + + 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)) + return 100; + + if(type == focusReagentCost && (IsEffectInSpell(spell_id, SE_SummonItem) || IsSacrificeSpell(spell_id))) + return 0; + //Summon Spells that require reagents are typically imbue type spells, enchant metal, sacrifice and shouldn't be affected + //by reagent conservation for obvious reasons. + + return realTotal + realTotal2 + realTotal3; +} + +bool Mob::CheckHitsRemaining(uint32 buff_slot, bool when_spell_done, bool negate, uint16 type, uint16 spell_id,bool use_skill,uint16 skill) +{ + bool bDepleted = false; + //Effects: Cast: SE_ResistSpellChance, SE_Reflect, SE_SpellDamageShield + //Effects: Attack: SE_MeleeLifetap : SE_DamageShield, SE_AvoidMeleeChance, SE_SkillProc + //Effects: Skill: SE_DamageModifier, SE_SkillDamageTaken, SE_SkillDamageAmount, SE_HitChance + //For spell buffs that are limited typically when you are attacked or are subject to an attack/cast and we do not know the buff slot. + if (type){ + uint32 buff_max = GetMaxTotalSlots(); + for(uint32 d = 0; d < buff_max; d++) { + if((buffs[d].spellid != SPELL_UNKNOWN) && (buffs[d].numhits > 0) && IsEffectInSpell(buffs[d].spellid, type)){ + if (!use_skill){ + if(--buffs[d].numhits == 0) { + if(!TryFadeEffect(d)){ + CastOnNumHitFade(buffs[d].spellid); + BuffFadeBySlot(d, true); + } + } + } + else{ + bDepleted = false; + for (int j = 0; j < EFFECT_COUNT; j++) { + if (bDepleted) + continue; + if ((buffs[d].spellid != SPELL_UNKNOWN) && (spells[buffs[d].spellid].effectid[j] == type)) { + if(spells[buffs[d].spellid].base2[j] == -1 || spells[buffs[d].spellid].base2[j] == skill) { + bDepleted = true; + if(--buffs[d].numhits == 0) { + if(!TryFadeEffect(d)){ + CastOnNumHitFade(buffs[d].spellid); + BuffFadeBySlot(d, true); + continue; + } + } + } + } + } + } + } + } + return false; + } + + // For spell buffs that are limited by the number of times it can successfully trigger a spell. + // Effects: SE_TriggerOnCast, SE_SympatheticProc,SE_DefensiveProc, SE_SkillProc, SE_RangedProc + if(spell_id){ + uint32 buff_count = GetMaxTotalSlots(); + for(uint32 d = 0; d < buff_count; d++){ + if((buffs[d].spellid != SPELL_UNKNOWN) && (buffs[d].numhits > 0) && buffs[d].spellid == spell_id){ + if(--buffs[d].numhits == 0) { + if(!TryFadeEffect(d)){ + CastOnNumHitFade(buffs[d].spellid); + BuffFadeBySlot(d, true); + return false; + } + } + } + } + return false; + } + + // For focusTypes that limit the number of spell casts it will effect. + // Effect: Focus effects ie SE_ImprovedDamage ect + if(when_spell_done) { + uint32 buff_max = GetMaxTotalSlots(); + // Go through all possible saved spells with limited hits, the place in the array is the same as the buff slot + for(int d = 0; d < buff_max; d++) { + if(!m_spellHitsLeft[d]) + continue; + // Double check to make sure the saved spell matches the buff in that slot + if (m_spellHitsLeft[d] == buffs[d].spellid) { + if(buffs[d].numhits > 1) { + buffs[d].numhits--; + return true; + } + else { + if(!TryFadeEffect(d)) + BuffFadeBySlot(d, true); + CastOnNumHitFade(m_spellHitsLeft[d]); + m_spellHitsLeft[d] = 0; + } + } + } + return false; + } + + // For lowering numhits when we already know the effects buff_slot + // Effects: SE_SpellVulnerability,SE_MitigateMeleeDamage,SE_NegateAttacks,SE_MitigateSpellDamage,SE_ManaAbsorbPercentDamage + if(spells[buffs[buff_slot].spellid].numhits > 0 || negate) { + if(buffs[buff_slot].numhits > 1) { + buffs[buff_slot].numhits--; + return true; + } + else if(!TryFadeEffect(buff_slot)) { + CastOnNumHitFade(buffs[buff_slot].spellid); + BuffFadeBySlot(buff_slot, true); + return false; + } + } + return false; +} + +//for some stupid reason SK procs return theirs one base off... +uint16 Mob::GetProcID(uint16 spell_id, uint8 effect_index) { + bool sk = false; + bool other = false; + 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) + other = true; + } + } + + if(sk && !other) + { + return(spells[spell_id].base[effect_index] + 1); + } + else{ + return(spells[spell_id].base[effect_index]); + } +} + +bool Mob::TryDivineSave() +{ + /* + How Touch of the Divine AA works: + -Gives chance to avoid death when client is killed. + -Chance is determined by the sum of AA/item/spell chances. + -If the chance is met a divine aura like effect 'Touch of the Divine' is applied to the client removing detrimental spell effects. + -If desired, additional spells can be triggered from the AA/item/spell effect, generally a heal. + */ + + int32 SuccessChance = aabonuses.DivineSaveChance[0] + itembonuses.DivineSaveChance[0] + spellbonuses.DivineSaveChance[0]; + if (SuccessChance && MakeRandomInt(0, 100) <= SuccessChance) + { + SetHP(1); + + uint16 EffectsToTry[] = + { + aabonuses.DivineSaveChance[1], + itembonuses.DivineSaveChance[1], + spellbonuses.DivineSaveChance[1] + }; + //Fade the divine save effect here after saving the old effects off. + //That way, if desired, the effect could apply SE_DivineSave again. + BuffFadeByEffect(SE_DivineSave); + for(size_t i = 0; i < ( sizeof(EffectsToTry) / sizeof(EffectsToTry[0]) ); ++i) + { + if( EffectsToTry[i] ) + { + SpellOnTarget(EffectsToTry[i], this); + } + } + + SpellOnTarget(4789, this); //Touch of the Divine=4789, an Invulnerability/HoT/Purify effect + SendHPUpdate(); + return true; + } + return false; +} + +bool Mob::TryDeathSave() { + + /* + How Death Save works: + -Chance for Death Save to fire is the same for Death Pact/Divine Intervention + -Base value of these determines amount healed (1=partial(300HP), 2='full (8000HP)) HARD CODED + -Charisma of client who has the effect determines fire rate, parses show this clearly with ratio used. + -Unfailing Divinity AA - Allows for a chance to give a heal for a percentage of the orginal value + when your innate chance fails and removes the buff. The spell effect for Unfailing Divinity gives + the a value of a heal modifier of the base effects heal. + Ie. Divine Intervention is 8000 HP Max UD1=20, therefore heal is 8000*20/100 + -No evidence of chance rate increasing between UD1-3, numbers indicate it uses same CHA rate as first DI. + -In later expansions this SE_DeathSave was given a level limit and a heal value in its effect data. + */ + + if (spellbonuses.DeathSave[0]){ + + int SuccessChance = 0; + int buffSlot = spellbonuses.DeathSave[1]; + uint8 UD_HealMod = buffs[buffSlot].deathsaveCasterAARank; //Contains value of UD heal modifier. + uint32 HealAmt = 300; //Death Pact max Heal + + if(buffSlot >= 0){ + + SuccessChance = ( (GetCHA() * (RuleI(Spells, DeathSaveCharismaMod))) + 1) / 10; //(CHA Mod Default = 3) + + if (SuccessChance > 95) + SuccessChance = 95; + + if(SuccessChance >= MakeRandomInt(0, 100)) { + + if(spellbonuses.DeathSave[0] == 2) + HealAmt = RuleI(Spells, DivineInterventionHeal); //8000HP is how much LIVE Divine Intervention max heals + + //Check if bonus Heal amount can be applied ([3] Bonus Heal [2] Level limit) + if (spellbonuses.DeathSave[3] && (GetLevel() >= spellbonuses.DeathSave[2])) + HealAmt += spellbonuses.DeathSave[3]; + + if ((GetMaxHP() - GetHP()) < HealAmt) + HealAmt = GetMaxHP() - GetHP(); + + SetHP((GetHP()+HealAmt)); + Message(263, "The gods have healed you for %i points of damage.", HealAmt); + + if(spellbonuses.DeathSave[0] == 2) + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, DIVINE_INTERVENTION, GetCleanName()); + else + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, DEATH_PACT, GetCleanName()); + + SendHPUpdate(); + BuffFadeBySlot(buffSlot); + return true; + } + else if (UD_HealMod) { + + SuccessChance = ((GetCHA() * (RuleI(Spells, DeathSaveCharismaMod))) + 1) / 10; + + if (SuccessChance > 95) + SuccessChance = 95; + + if(SuccessChance >= MakeRandomInt(0, 100)) { + + if(spellbonuses.DeathSave[0] == 2) + HealAmt = RuleI(Spells, DivineInterventionHeal); + + //Check if bonus Heal amount can be applied ([3] Bonus Heal [2] Level limit) + if (spellbonuses.DeathSave[3] && (GetLevel() >= spellbonuses.DeathSave[2])) + HealAmt += spellbonuses.DeathSave[3]; + + HealAmt = HealAmt*UD_HealMod/100; + + if ((GetMaxHP() - GetHP()) < HealAmt) + HealAmt = GetMaxHP() - GetHP(); + + SetHP((GetHP()+HealAmt)); + Message(263, "The gods have healed you for %i points of damage.", HealAmt); + + if(spellbonuses.DeathSave[0] == 2) + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, DIVINE_INTERVENTION, GetCleanName()); + else + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, DEATH_PACT, GetCleanName()); + + SendHPUpdate(); + BuffFadeBySlot(buffSlot); + return true; + } + } + } + } + return false; +} + +bool Mob::AffectedBySpellExcludingSlot(int slot, int effect) +{ + for (int i = 0; i <= EFFECT_COUNT; i++) + { + if (i == slot) + continue; + + if (IsEffectInSpell(buffs[i].spellid, effect)) + return true; + } + return false; +} + +float Mob::GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod) { + + ProcBonus = spellbonuses.SpellProcChance + itembonuses.SpellProcChance; + ProcChance = 0; + + if(cast_time > 0) + { + ProcChance = ((float)cast_time * RuleR(Casting, AvgSpellProcsPerMinute) / 60000.0f); + ProcChance = ProcChance * (float)(ProcRateMod/100); + ProcChance = ProcChance+(ProcChance*ProcBonus/100); + } + return ProcChance; +} + +bool Mob::DoHPToManaCovert(uint16 mana_cost) +{ + if (spellbonuses.HPToManaConvert){ + int hp_cost = spellbonuses.HPToManaConvert * mana_cost / 100; + if(hp_cost) { + SetHP(GetHP()-hp_cost); + return true; + } + return false; + } + + return false; +} + +int32 Mob::GetAdditionalDamage(Mob *caster, uint32 spell_id, bool use_skill, uint16 skill ) +{ + //Used to check focus derived from SE_Empathy which adds direct damage to Spells or Skill based attacks. + int32 dmg = 0; + bool limit_exists = false; + bool skill_found = false; + + if (!caster) + return 0; + + if (spellbonuses.FocusEffects[focusAdditionalDamage]){ + uint32 buff_count = GetMaxTotalSlots(); + for(int i = 0; i < buff_count; i++){ + + if( (IsValidSpell(buffs[i].spellid) && (IsEffectInSpell(buffs[i].spellid, SE_Empathy))) ){ + + if (use_skill){ + int32 temp_dmg = 0; + for (int e = 0; e < EFFECT_COUNT; e++) { + + if (spells[buffs[i].spellid].effectid[e] == SE_Empathy){ + temp_dmg += spells[buffs[i].spellid].base[e]; + continue; + } + + if (!skill_found){ + if ((spells[buffs[i].spellid].effectid[e] == SE_LimitToSkill) || + (spells[buffs[i].spellid].effectid[e] == SE_LimitSpellSkill)){ + limit_exists = true; + + if (spells[buffs[i].spellid].base[e] == skill) + skill_found = true; + } + } + } + if ((!limit_exists) || (limit_exists && skill_found)){ + dmg += temp_dmg; + CheckHitsRemaining(i); + } + } + + else{ + int32 focus = caster->CalcFocusEffect(focusAdditionalDamage, buffs[i].spellid, spell_id); + if(focus){ + dmg += focus; + CheckHitsRemaining(i); + } + } + } + } + } + return dmg; +} + +int32 Mob::ApplySpellEffectiveness(Mob* caster, int16 spell_id, int32 value, bool IsBard) { + + //Kayen 9-17-12: This is likely causing crashes, disabled till can resolve. + if (IsBard) + return value; + + if (!caster) + return value; + + if (caster->IsClient()){ + int16 focus = caster->CastToClient()->GetFocusEffect(focusSpellEffectiveness, spell_id); + + if (IsBard) + value += focus; + + else + value += value*focus/100; + } + return value; +} + +bool Mob::PassLimitClass(uint32 Classes_, uint16 Class_) +{ + //The class value for SE_LimitClass is +1 to its equivelent value in item dbase + //Example Bard on items is '128' while Bard on SE_LimitClass is '256', keep this in mind if making custom spells. + if (Class_ > 16) + return false; + + Class_ += 1; + + for (int CurrentClass = 1; CurrentClass <= PLAYER_CLASS_COUNT; ++CurrentClass){ + if (Classes_ % 2 == 1){ + if (CurrentClass == Class_) + return true; + } + Classes_ >>= 1; + } + return false; +} + +uint16 Mob::GetSpellEffectResistChance(uint16 spell_id) +{ + + if(!IsValidSpell(spell_id)) + return 0; + + if (!aabonuses.SEResist[0] && !spellbonuses.SEResist[0] && !itembonuses.SEResist[0]) + return 0; + + uint16 resist_chance = 0; + + for(int i = 0; i < EFFECT_COUNT; ++i) + { + bool found = false; + + for(int d = 0; d < MAX_RESISTABLE_EFFECTS*2; d+=2) + { + if (spells[spell_id].effectid[i] == aabonuses.SEResist[d]){ + resist_chance += aabonuses.SEResist[d+1]; + found = true; + } + + if (spells[spell_id].effectid[i] == itembonuses.SEResist[d]){ + resist_chance += itembonuses.SEResist[d+1]; + found = true; + } + + + if (spells[spell_id].effectid[i] == spellbonuses.SEResist[d]){ + resist_chance += spellbonuses.SEResist[d+1]; + found = true; + } + + if (found) + continue; + } + } + return resist_chance; +} + diff --git a/zone/spells.cpp b/zone/spells.cpp new file mode 100644 index 000000000..b73a968e4 --- /dev/null +++ b/zone/spells.cpp @@ -0,0 +1,5408 @@ +/* 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 +*/ + + +/* + + General outline of spell casting process + + 1. + a) Client clicks a spell bar gem, ability, or item. client_process.cpp + gets the op and calls CastSpell() with all the relevant info including + cast time. + + b) NPC does CastSpell() from AI + + 2. + a) CastSpell() determines there is a cast time and sets some state keeping + flags to be used to check the progress of casting and finish it later. + + b) CastSpell() sees there's no cast time, and calls CastedSpellFinished() + Go to step 4. + + 3. + SpellProcess() notices that the spell casting timer which was set by + CastSpell() is expired, and calls CastedSpellFinished() + + 4. + CastedSpellFinished() checks some timed spell specific things, like + wether to interrupt or not, due to movement or melee. If successful + SpellFinished() is called. + + 5. + SpellFinished() checks some things like LoS, reagents, target and + figures out what's going to get hit by this spell based on its type. + + 6. + a) Single target spell, SpellOnTarget() is called. + + b) AE spell, Entity::AESpell() is called. + + c) Group spell, Group::CastGroupSpell()/SpellOnTarget() is called as + needed. + + 7. + SpellOnTarget() may or may not call SpellEffect() to cause effects to + the target + + 8. + If this was timed, CastedSpellFinished() will restore the client's + spell bar gems. + + + Most user code should call CastSpell(), with a 0 casting time if needed, + and not SpellFinished(). + +*/ + + + +#include "../common/debug.h" +#include "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/rulesys.h" +#include +#include +#ifndef WIN32 +// #include +#include +#include "../common/unix.h" +#endif +#ifdef _GOTFRAGS + #include "../common/packet_dump_file.h" +#endif + +#include "StringIDs.h" +#include "QuestParserCollection.h" + +extern Zone* zone; +extern volatile bool ZoneLoaded; +#if !defined(NEW_LoadSPDat) && !defined(DB_LoadSPDat) + extern SPDat_Spell_Struct spells[SPDAT_RECORDS]; +#endif +extern bool spells_loaded; +extern WorldServer worldserver; +uchar blah[]={0x0D,0x00,0x00,0x00,0x01,0x00,0x00,0x00}; +uchar blah2[]={0x12,0x00,0x00,0x00,0x16,0x01,0x00,0x00}; + + +// this is run constantly for every mob +void Mob::SpellProcess() +{ + // check the rapid recast prevention timer + if(delaytimer == true && spellend_timer.Check()) + { + spellend_timer.Disable(); + delaytimer = false; + return; + } + + // a timed spell is finished casting + if (casting_spell_id != 0 && spellend_timer.Check()) + { + spellend_timer.Disable(); + delaytimer = false; + CastedSpellFinished(casting_spell_id, casting_spell_targetid, casting_spell_slot, + casting_spell_mana, casting_spell_inventory_slot, casting_spell_resist_adjust); + } + +} + +void NPC::SpellProcess() +{ + Mob::SpellProcess(); + + if(GetSwarmInfo()){ + Mob *swp_o = GetSwarmInfo()->GetOwner(); + if(!swp_o) + { + Depop(); + } + + if(GetSwarmInfo()->duration->Check(false)) + { + Depop(); + } + + Mob *targMob = entity_list.GetMob(GetSwarmInfo()->target); + if(GetSwarmInfo()->target != 0) + { + if(!targMob || (targMob && targMob->IsCorpse())) + Depop(); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// functions related to begin/finish casting, fizzling etc + +// +// only CastSpell and DoCastSpell should be setting casting_spell_id. +// basically casting_spell_id is only set when casting a triggered spell from +// the spell bar gems, an ability, or an item. note that it's actually set +// even if it's a 0 cast time, but then the spell is finished right after and +// it's unset. this is ok, since the 0 cast time spell is still a triggered +// one. +// the rule is you can cast one triggered (usually timed) spell at a time +// but things like SpellFinished() can run concurrent with a triggered cast +// to allow procs to work +bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, + int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot, + uint32 timer, uint32 timer_duration, uint32 type, int16 *resist_adjust) +{ + _ZP(Mob_CastSpell); + + mlog(SPELLS__CASTING, "CastSpell called for spell %s (%d) on entity %d, slot %d, time %d, mana %d, from item slot %d", + spells[spell_id].name, spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot); + + if(casting_spell_id == spell_id) + ZeroCastingVars(); + + if + ( + !IsValidSpell(spell_id) || + casting_spell_id || + delaytimer || + spellend_timer.Enabled() || + IsStunned() || + IsFeared() || + IsMezzed() || + (IsSilenced() && !IsDiscipline(spell_id)) || + (IsAmnesiad() && IsDiscipline(spell_id)) + ) + { + mlog(SPELLS__CASTING_ERR, "Spell casting canceled: not able to cast now. Valid? %d, casting %d, waiting? %d, spellend? %d, stunned? %d, feared? %d, mezed? %d, silenced? %d, amnesiad? %d", + IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled(), IsStunned(), IsFeared(), IsMezzed(), IsSilenced(), IsAmnesiad() ); + if(IsSilenced() && !IsDiscipline(spell_id)) + Message_StringID(13, SILENCED_STRING); + if(IsAmnesiad() && IsDiscipline(spell_id)) + Message_StringID(13, MELEE_SILENCE); + if(IsClient()) + CastToClient()->SendSpellBarEnable(spell_id); + if(casting_spell_id && IsNPC()) + CastToNPC()->AI_Event_SpellCastFinished(false, casting_spell_slot); + return(false); + } + + if(IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()){ + Message_StringID(13, SPELL_WOULDNT_HOLD); + if(IsClient()) + CastToClient()->SendSpellBarEnable(spell_id); + if(casting_spell_id && IsNPC()) + CastToNPC()->AI_Event_SpellCastFinished(false, casting_spell_slot); + return(false); + } + + //cannot cast under divine aura + if(DivineAura()) { + mlog(SPELLS__CASTING_ERR, "Spell casting canceled: cannot cast while Divine Aura is in effect."); + InterruptSpell(173, 0x121, false); + return(false); + } + + if(IsClient() && GetTarget() && IsHarmonySpell(spell_id)) + { + for(int i = 0; i < EFFECT_COUNT; i++) { + // not important to check limit on SE_Lull as it doesnt have one and if the other components won't land, then SE_Lull wont either + if (spells[spell_id].effectid[i] == SE_ChangeFrenzyRad || spells[spell_id].effectid[i] == SE_Harmony) { + if((spells[spell_id].max[i] != 0 && GetTarget()->GetLevel() > spells[spell_id].max[i]) || GetTarget()->SpecAttacks[IMMUNE_PACIFY]) { + InterruptSpell(CANNOT_AFFECT_NPC, 0x121, spell_id); + return(false); + } + } + } + } + + // check for fizzle + // note that CheckFizzle itself doesn't let NPCs fizzle, + // but this code allows for it. + if(slot < MAX_PP_MEMSPELL && !CheckFizzle(spell_id)) + { + int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE; + InterruptSpell(fizzle_msg, 0x121, spell_id); + + uint32 use_mana = ((spells[spell_id].mana) / 4); + mlog(SPELLS__CASTING_ERR, "Spell casting canceled: fizzled. %d mana has been consumed", use_mana); + + // fizzle 1/4 the mana away + SetMana(GetMana() - use_mana); + return(false); + } + + if (HasActiveSong() && IsBardSong(spell_id)) { + mlog(SPELLS__BARDS, "Casting a new song while singing a song. Killing old song %d.", bardsong); + //Note: this does NOT tell the client + _StopSong(); + } + + //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))) + { + ItemInst *itm = CastToClient()->GetInv().GetItem(item_slot); + int bitmask = 1; + bitmask = bitmask << (CastToClient()->GetClass() - 1); + if( itm && itm->GetItem()->Classes != 65535 ) { + if ((itm->GetItem()->Click.Type == ET_EquipClick) && !(itm->GetItem()->Classes & bitmask)) { + if (CastToClient()->GetClientVersion() < EQClientSoF) { + // They are casting a spell from an item that requires equipping but shouldn't let them equip it + LogFile->write(EQEMuLog::Error, "HACKER: %s (account: %s) attempted to click an equip-only effect on item %s (id: %d) which they shouldn't be able to equip!", + CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID); + database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item with an invalid class"); + } + else { + Message_StringID(13, MUST_EQUIP_ITEM); + } + return(false); + } + if ((itm->GetItem()->Click.Type == ET_ClickEffect2) && !(itm->GetItem()->Classes & bitmask)) { + if (CastToClient()->GetClientVersion() < EQClientSoF) { + // They are casting a spell from an item that they don't meet the race/class requirements to cast + LogFile->write(EQEMuLog::Error, "HACKER: %s (account: %s) attempted to click a race/class restricted effect on item %s (id: %d) which they shouldn't be able to click!", + CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID); + database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking race/class restricted item with an invalid class"); + } + else { + Message_StringID(13, CANNOT_USE_ITEM); + } + return(false); + } + } + if( itm && (itm->GetItem()->Click.Type == ET_EquipClick) && !(item_slot < 22 || item_slot == 9999) ){ + if (CastToClient()->GetClientVersion() < EQClientSoF) { + // They are attempting to cast a must equip clicky without having it equipped + LogFile->write(EQEMuLog::Error, "HACKER: %s (account: %s) attempted to click an equip-only effect on item %s (id: %d) without equiping it!", CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID); + database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item without equiping it"); + } + else { + Message_StringID(13, MUST_EQUIP_ITEM); + } + return(false); + } + } + + if(resist_adjust) + { + return(DoCastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot, timer, timer_duration, type, *resist_adjust)); + } + else + { + return(DoCastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot, timer, timer_duration, type, spells[spell_id].ResistDiff)); + } +} + +// +// the order of things here is intentional and important. make sure you +// understand the whole spell casting process and the flags that are passed +// around if you're gonna modify this +// +// this is the 2nd phase of CastSpell, broken up like this to make it easier +// to repeat a spell for bard songs +// +bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, + int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, + uint32 item_slot, uint32 timer, uint32 timer_duration, uint32 type, + int16 resist_adjust) +{ + _ZP(Mob_DoCastSpell); + + Mob* pMob = NULL; + int32 orgcasttime; + EQApplicationPacket *outapp = NULL; + + if(!IsValidSpell(spell_id)) { + InterruptSpell(); + return(false); + } + + const SPDat_Spell_Struct &spell = spells[spell_id]; + + mlog(SPELLS__CASTING, "DoCastSpell called for spell %s (%d) on entity %d, slot %d, time %d, mana %d, from item %d", + spell.name, spell_id, target_id, slot, cast_time, mana_cost, item_slot==0xFFFFFFFF?999:item_slot); + + + casting_spell_id = spell_id; + casting_spell_slot = slot; + casting_spell_inventory_slot = item_slot; + if(casting_spell_timer != 0xFFFFFFFF) + { + casting_spell_timer = timer; + casting_spell_timer_duration = timer_duration; + } + casting_spell_type = type; + + SaveSpellLoc(); + mlog(SPELLS__CASTING, "Casting %d Started at (%.3f,%.3f,%.3f)", spell_id, spell_x, spell_y, spell_z); + + // if this spell doesn't require a target, or if it's an optional target + // and a target wasn't provided, then it's us; unless TGB is on and this + // is a TGB compatible spell. + if((IsGroupSpell(spell_id) || + spell.targettype == ST_Self || + spell.targettype == ST_AECaster || + 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); + target_id = GetID(); + } + + if(cast_time <= -1) { + // save the non-reduced cast time to use in the packet + cast_time = orgcasttime = spell.cast_time; + // if there's a cast time, check if they have a modifier for it + if(cast_time) { + cast_time = GetActSpellCasttime(spell_id, cast_time); + } + } + else + orgcasttime = cast_time; + + // we checked for spells not requiring targets above + if(target_id == 0) { + mlog(SPELLS__CASTING_ERR, "Spell Error: no target. spell=%d\n", GetName(), spell_id); + if(IsClient()) { + //clients produce messages... npcs should not for this case + Message_StringID(13, SPELL_NEED_TAR); + InterruptSpell(); + } else { + InterruptSpell(0, 0, 0); //the 0 args should cause no messages + } + return(false); + } + + // ok now we know the target + casting_spell_targetid = target_id; + + if (mana_cost == -1) { + mana_cost = spell.mana; + mana_cost = GetActSpellCost(spell_id, mana_cost); + } + + if(IsClient() && CastToClient()->CheckAAEffect(aaEffectMassGroupBuff) && spells[spell_id].can_mgb) + mana_cost *= 2; + + // mana is checked for clients on the frontend. we need to recheck it for NPCs though + // fix: items dont need mana :-/ + // If you're at full mana, let it cast even if you dont have enough mana + + // we calculated this above, now enforce it + if(mana_cost > 0 && slot != 10) + { + int my_curmana = GetMana(); + int my_maxmana = GetMaxMana(); + if(my_curmana < spell.mana) // not enough mana + { + //this is a special case for NPCs with no mana... + if(IsNPC() && my_curmana == my_maxmana) + { + mana_cost = 0; + } else { + mlog(SPELLS__CASTING_ERR, "Spell Error not enough mana spell=%d mymana=%d cost=%d\n", GetName(), spell_id, my_curmana, mana_cost); + if(IsClient()) { + //clients produce messages... npcs should not for this case + Message_StringID(13, INSUFFICIENT_MANA); + InterruptSpell(); + } else { + InterruptSpell(0, 0, 0); //the 0 args should cause no messages + } + return(false); + } + } + } + + if(mana_cost > GetMana()) + mana_cost = GetMana(); + + // we know our mana cost now + casting_spell_mana = mana_cost; + + casting_spell_resist_adjust = resist_adjust; + + mlog(SPELLS__CASTING, "Spell %d: Casting time %d (orig %d), mana cost %d", orgcasttime, cast_time, mana_cost); + + // cast time is 0, just finish it right now and be done with it + if(cast_time == 0) { + CastedSpellFinished(spell_id, target_id, slot, mana_cost, item_slot, resist_adjust); + return(true); + } + + // ok we know it has a cast time so we can start the timer now + spellend_timer.Start(cast_time); + + if (IsAIControlled()) + { + SetRunAnimSpeed(0); + if(this != pMob) + this->FaceTarget(pMob); + } + + // if we got here we didn't fizzle, and are starting our cast + if (oSpellWillFinish) + *oSpellWillFinish = Timer::GetCurrentTime() + cast_time + 100; + + // now tell the people in the area + outapp = new EQApplicationPacket(OP_BeginCast,sizeof(BeginCast_Struct)); + BeginCast_Struct* begincast = (BeginCast_Struct*)outapp->pBuffer; + begincast->caster_id = GetID(); + begincast->spell_id = spell_id; + begincast->cast_time = orgcasttime; // client calculates reduced time by itself + outapp->priority = 3; + entity_list.QueueCloseClients(this, outapp, false, 200, 0, true); //IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS); + safe_delete(outapp); + outapp = NULL; + return(true); +} + +uint16 Mob::GetSpecializeSkillValue(uint16 spell_id) const { + switch(spells[spell_id].skill) { + case ABJURE: + return(GetSkill(SPECIALIZE_ABJURE)); + case ALTERATION: + return(GetSkill(SPECIALIZE_ALTERATION)); + case CONJURATION: + return(GetSkill(SPECIALIZE_CONJURATION)); + case DIVINATION: + return(GetSkill(SPECIALIZE_DIVINATION)); + case EVOCATION: + return(GetSkill(SPECIALIZE_EVOCATION)); + default: + //wtf... + break; + } + return(0); +} + +void Client::CheckSpecializeIncrease(uint16 spell_id) { + switch(spells[spell_id].skill) { + case ABJURE: + CheckIncreaseSkill(SPECIALIZE_ABJURE, NULL); + break; + case ALTERATION: + CheckIncreaseSkill(SPECIALIZE_ALTERATION, NULL); + break; + case CONJURATION: + CheckIncreaseSkill(SPECIALIZE_CONJURATION, NULL); + break; + case DIVINATION: + CheckIncreaseSkill(SPECIALIZE_DIVINATION, NULL); + break; + case EVOCATION: + CheckIncreaseSkill(SPECIALIZE_EVOCATION, NULL); + break; + default: + //wtf... + break; + } +} + +void Client::CheckSongSkillIncrease(uint16 spell_id){ + switch(spells[spell_id].skill) + { + case SINGING: + CheckIncreaseSkill(SINGING, NULL, -15); + break; + case PERCUSSION_INSTRUMENTS: + if(this->itembonuses.percussionMod > 0) { + if(GetRawSkill(PERCUSSION_INSTRUMENTS) > 0) // no skill increases if not trained in the instrument + CheckIncreaseSkill(PERCUSSION_INSTRUMENTS, NULL, -15); + else + Message_StringID(13,NO_INSTRUMENT_SKILL); // tell the client that they need instrument training + } + else + CheckIncreaseSkill(SINGING, NULL, -15); + break; + case STRINGED_INSTRUMENTS: + if(this->itembonuses.stringedMod > 0) { + if(GetRawSkill(STRINGED_INSTRUMENTS) > 0) + CheckIncreaseSkill(STRINGED_INSTRUMENTS, NULL, -15); + else + Message_StringID(13,NO_INSTRUMENT_SKILL); + } + else + CheckIncreaseSkill(SINGING, NULL, -15); + break; + case WIND_INSTRUMENTS: + if(this->itembonuses.windMod > 0) { + if(GetRawSkill(WIND_INSTRUMENTS) > 0) + CheckIncreaseSkill(WIND_INSTRUMENTS, NULL, -15); + else + Message_StringID(13,NO_INSTRUMENT_SKILL); + } + else + CheckIncreaseSkill(SINGING, NULL, -15); + break; + case BRASS_INSTRUMENTS: + if(this->itembonuses.brassMod > 0) { + if(GetRawSkill(BRASS_INSTRUMENTS) > 0) + CheckIncreaseSkill(BRASS_INSTRUMENTS, NULL, -15); + else + Message_StringID(13,NO_INSTRUMENT_SKILL); + } + else + CheckIncreaseSkill(SINGING, NULL, -15); + break; + default: + break; + } +} + +/* +returns true if spell is successful, false if it fizzled. +only works for clients, npcs shouldn't be fizzling.. +new algorithm thats closer to live eq (i hope) +TODO: Add aa skills, item mods, reduced the chance to fizzle +*/ +bool Mob::CheckFizzle(uint16 spell_id) +{ + return(true); +} + +bool Client::CheckFizzle(uint16 spell_id) +{ + // GMs don't fizzle + if (GetGM()) return(true); + + uint8 no_fizzle_level = 0; + + //Live AA - Spell Casting Expertise, Mastery of the Past + no_fizzle_level = aabonuses.MasteryofPast + itembonuses.MasteryofPast + spellbonuses.MasteryofPast; + + if (spells[spell_id].classes[GetClass()-1] < no_fizzle_level) + return true; + + //is there any sort of focus that affects fizzling? + + int par_skill; + int act_skill; + + par_skill = spells[spell_id].classes[GetClass()-1] * 5 - 10;//IIRC even if you are lagging behind the skill levels you don't fizzle much + if (par_skill > 235) + par_skill = 235; + + par_skill += spells[spell_id].classes[GetClass()-1]; // maximum of 270 for level 65 spell + + act_skill = GetSkill(spells[spell_id].skill); + act_skill += GetLevel(); // maximum of whatever the client can cheat + + //spell specialization + float specialize = GetSpecializeSkillValue(spell_id); + if(specialize > 0) { + switch(GetAA(aaSpellCastingMastery)){ + case 1: + specialize = specialize * 1.05; + break; + case 2: + specialize = specialize * 1.15; + break; + case 3: + specialize = specialize * 1.3; + break; + } + if(((specialize/6.0f) + 15.0f) < MakeRandomFloat(0, 100)) { + specialize *= SPECIALIZE_FIZZLE / 200; + } else { + specialize = 0.0f; + } + } + + // == 0 --> on par + // > 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; + + // if you have high int/wis you fizzle less, you fizzle more if you are stupid + if(GetClass() == BARD) + { + diff -= (GetCHA() - 110) / 20.0; + } + else if (GetCasterClass() == 'W') + { + diff -= (GetWIS() - 125) / 20.0; + } + else if (GetCasterClass() == 'I') + { + diff -= (GetINT() - 125) / 20.0; + } + + // base fizzlechance is lets say 5%, we can make it lower for AA skills or whatever + float basefizzle = 10; + float fizzlechance = basefizzle - specialize + diff / 5.0; + + // always at least 1% chance to fail or 5% to succeed + fizzlechance = fizzlechance < 1 ? 1 : (fizzlechance > 95 ? 95 : fizzlechance); + + /* + if(IsBardSong(spell_id)) + { + //This was a channel chance modifier - no evidence for fizzle reduction + fizzlechance -= GetAA(aaInternalMetronome) * 1.5f; + } + */ + + float fizzle_roll = MakeRandomFloat(0, 100); + + mlog(SPELLS__CASTING, "Check Fizzle %s spell %d fizzlechance: %0.2f%% diff: %0.2f roll: %0.2f", GetName(), spell_id, fizzlechance, diff, fizzle_roll); + + if(fizzle_roll > fizzlechance) + return(true); + return(false); +} + +void Mob::ZeroCastingVars() +{ + // zero out the state keeping vars + attacked_count = 0; + spellend_timer.Disable(); + casting_spell_id = 0; + casting_spell_targetid = 0; + casting_spell_slot = 0; + casting_spell_mana = 0; + casting_spell_inventory_slot = 0; + casting_spell_timer = 0; + casting_spell_timer_duration = 0; + casting_spell_type = 0; + casting_spell_resist_adjust = 0; + delaytimer = false; +} + +void Mob::InterruptSpell(uint16 spellid) +{ + if (spellid == SPELL_UNKNOWN) + spellid = casting_spell_id; + + InterruptSpell(0, 0x121, spellid); +} + +// color not used right now +void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid) +{ + EQApplicationPacket *outapp; + uint16 message_other; + bool bard_song_mode = false; //has the bard song gone to auto repeat mode + if (spellid == SPELL_UNKNOWN) { + if(bardsong) { + spellid = bardsong; + bard_song_mode = true; + } else { + spellid = casting_spell_id; + } + } + + if(casting_spell_id && IsNPC()) { + CastToNPC()->AI_Event_SpellCastFinished(false, casting_spell_slot); + } + + if(casting_spell_type == 1 && IsClient()) //Rest AA Timer on failed cast + CastToClient()->GetPTimers().Clear(&database, casting_spell_timer); + + ZeroCastingVars(); // resets all the state keeping stuff + + mlog(SPELLS__CASTING, "Spell %d has been interrupted.", spellid); + + if(!spellid) + return; + + if (bardsong || IsBardSong(casting_spell_id)) + _StopSong(); + + if(bard_song_mode) { + return; + } + + if(!message) + message = IsBardSong(spellid) ? SONG_ENDS_ABRUPTLY : INTERRUPT_SPELL; + + // clients need some packets + if (IsClient() && message != SONG_ENDS) + { + // the interrupt message + outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct)); + InterruptCast_Struct* ic = (InterruptCast_Struct*) outapp->pBuffer; + ic->messageid = message; + ic->spawnid = GetID(); + outapp->priority = 5; + CastToClient()->QueuePacket(outapp); + safe_delete(outapp); + + SendSpellBarEnable(spellid); + } + + // notify people in the area + + // first figure out what message others should get + switch(message) + { + case SONG_ENDS: + message_other = SONG_ENDS_OTHER; + break; + case SONG_ENDS_ABRUPTLY: + message_other = SONG_ENDS_ABRUPTLY_OTHER; + break; + case MISS_NOTE: + message_other = MISSED_NOTE_OTHER; + break; + case SPELL_FIZZLE: + message_other = SPELL_FIZZLE_OTHER; + break; + default: + message_other = INTERRUPT_SPELL_OTHER; + } + + // this is the actual message, it works the same as a formatted message + outapp = new EQApplicationPacket(OP_InterruptCast, sizeof(InterruptCast_Struct) + strlen(GetCleanName()) + 1); + InterruptCast_Struct* ic = (InterruptCast_Struct*) outapp->pBuffer; + ic->messageid = message_other; + ic->spawnid = GetID(); + strcpy(ic->message, GetCleanName()); + entity_list.QueueCloseClients(this, outapp, true, 200, 0, true, IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS); + safe_delete(outapp); + +} + +// this is called after the timer is up and the spell is finished +// casting. everything goes through here, including items with zero cast time +// only to be used from SpellProcess +// NOTE: do not put range checking, etc into this function. this should +// just check timed spell specific things before passing off to SpellFinished +// which figures out proper targets etc +void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, + uint16 mana_used, uint32 inventory_slot, int16 resist_adjust) +{ + _ZP(Mob_CastedSpellFinished); + + 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(!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); + mlog(SPELLS__CASTING_ERR, "Casting of %d canceled: spell reuse timer not expired", spell_id); + InterruptSpell(); + return; + } + } + + if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT))) + { + IsFromItem = true; + ItemInst *itm = CastToClient()->GetInv().GetItem(inventory_slot); + if(itm && itm->GetItem()->RecastDelay > 0) + { + if(!CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + itm->GetItem()->RecastType), false)) { + Message_StringID(13, SPELL_RECAST); + mlog(SPELLS__CASTING_ERR, "Casting of %d canceled: item spell reuse timer not expired", spell_id); + InterruptSpell(); + return; + } + } + } + + if(!IsValidSpell(spell_id)) + { + mlog(SPELLS__CASTING_ERR, "Casting of %d canceled: invalid spell id", spell_id); + InterruptSpell(); + return; + } + + // prevent rapid recast - this can happen if somehow the spell gems + // become desynced and the player casts again. + if(IsClient()) + { + if(delaytimer) + { + mlog(SPELLS__CASTING_ERR, "Casting of %d canceled: recast too quickly", spell_id); + Message(13, "You are unable to focus."); + InterruptSpell(); + return; + } + } + + // make sure they aren't somehow casting 2 timed spells at once + if (casting_spell_id != spell_id) + { + mlog(SPELLS__CASTING_ERR, "Casting of %d canceled: already casting", spell_id); + Message_StringID(13,ALREADY_CASTING); + InterruptSpell(); + return; + } + + bool bard_song_mode = false; + bool regain_conc = false; + Mob *spell_target = entity_list.GetMob(target_id); + // here we do different things if this is a bard casting a bard song from + // a spell bar slot + if(GetClass() == BARD) // bard's can move when casting any spell... + { + if (IsBardSong(spell_id)) { + if(spells[spell_id].buffduration == 0xFFFF || spells[spell_id].recast_time != 0) { + mlog(SPELLS__BARDS, "Bard song %d not applying bard logic because duration or recast is wrong: dur=%d, recast=%d", spells[spell_id].buffduration, spells[spell_id].recast_time); + } else { + bardsong = spell_id; + bardsong_slot = slot; + //NOTE: theres a lot more target types than this to think about... + if (spell_target == NULL || (spells[spell_id].targettype != ST_Target && spells[spell_id].targettype != ST_AETarget)) + bardsong_target_id = GetID(); + else + bardsong_target_id = spell_target->GetID(); + bardsong_timer.Start(6000); + mlog(SPELLS__BARDS, "Bard song %d started: slot %d, target id %d", bardsong, bardsong_slot, bardsong_target_id); + bard_song_mode = true; + } + } + } + else // not bard, check movement + { + // if has been attacked, or moved while casting + // check for regain concentration + if + ( + attacked_count > 0 || + GetX() != GetSpellX() || + GetY() != GetSpellY() + ) + { + // modify the chance based on how many times they were hit + // but cap it so it's not that large a factor + if(attacked_count > 15) attacked_count = 15; + + float channelchance, distance_moved, d_x, d_y, distancemod; + + if(IsClient()) + { + float channelbonuses = 0.0f; + //AA that effect Spell channel chance are no longer on live. http://everquest.allakhazam.com/history/patches-2006-2.html + //No harm in maintaining the effects regardless, since we do check for channel chance. + if (IsFromItem) + channelbonuses += spellbonuses.ChannelChanceItems + itembonuses.ChannelChanceItems + aabonuses.ChannelChanceItems; + else + channelbonuses += spellbonuses.ChannelChanceSpells + itembonuses.ChannelChanceSpells + aabonuses.ChannelChanceSpells; + + // max 93% chance at 252 skill + channelchance = 30 + GetSkill(CHANNELING) / 400.0f * 100; + channelchance -= attacked_count * 2; + channelchance += channelchance * channelbonuses / 100.0f; + } +#ifdef BOTS + else if(IsBot()) { + float channelbonuses = 0.0f; + + if (IsFromItem) + channelbonuses += spellbonuses.ChannelChanceItems + itembonuses.ChannelChanceItems + aabonuses.ChannelChanceItems; + else + channelbonuses += spellbonuses.ChannelChanceSpells + itembonuses.ChannelChanceSpells + aabonuses.ChannelChanceSpells; + + // max 93% chance at 252 skill + channelchance = 30 + GetSkill(CHANNELING) / 400.0f * 100; + channelchance -= attacked_count * 2; + channelchance += channelchance * channelbonuses / 100.0f; + } +#endif //BOTS + else { + // NPCs are just hard to interrupt, otherwise they get pwned + channelchance = 85; + channelchance -= attacked_count; + } + + // as you get farther from your casting location, + // it gets squarely harder to regain concentration + if(GetX() != GetSpellX() || GetY() != GetSpellY()) + { + d_x = fabs(fabs(GetX()) - fabs(GetSpellX())); + d_y = fabs(fabs(GetY()) - fabs(GetSpellY())); + if(d_x < 5 && d_y < 5) + { + //avoid the square root... + distance_moved = d_x * d_x + d_y * d_y; + // if you moved 1 unit, that's 25% off your chance to regain. + // if you moved 2, you lose 100% off your chance + distancemod = distance_moved * 25; + channelchance -= distancemod; + } + else + { + channelchance = 0; + } + } + + mlog(SPELLS__CASTING, "Checking Interruption: spell x: %f spell y: %f cur x: %f cur y: %f channelchance %f channeling skill %d\n", GetSpellX(), GetSpellY(), GetX(), GetY(), channelchance, GetSkill(CHANNELING)); + + if(MakeRandomFloat(0, 100) > channelchance) { + mlog(SPELLS__CASTING_ERR, "Casting of %d canceled: interrupted.", spell_id); + InterruptSpell(); + return; + } + // if we got here, we regained concentration + regain_conc = true; + Message_StringID(MT_Spells,REGAIN_AND_CONTINUE); + entity_list.MessageClose_StringID(this, true, 200, MT_Spells, OTHER_REGAIN_CAST, this->GetCleanName()); + } + } + + // Check for consumables and Reagent focus items + // first check for component reduction + if(IsClient()) { + int reg_focus = CastToClient()->GetFocusEffect(focusReagentCost,spell_id); + if(MakeRandomInt(1, 100) <= reg_focus) { + mlog(SPELLS__CASTING, "Spell %d: Reagent focus item prevented reagent consumption (%d chance)", spell_id, reg_focus); + } else { + if(reg_focus > 0) + mlog(SPELLS__CASTING, "Spell %d: Reagent focus item failed to prevent reagent consumption (%d chance)", spell_id, reg_focus); + Client *c = this->CastToClient(); + int component, component_count, inv_slot_id; + bool missingreags = false; + for(int t_count = 0; t_count < 4; t_count++) { + component = spells[spell_id].components[t_count]; + component_count = spells[spell_id].component_counts[t_count]; + + if (component == -1) + continue; + + // bard components are requirements for a certain instrument type, not a specific item + if(bard_song_mode) { + bool HasInstrument = true; + int InstComponent = spells[spell_id].NoexpendReagent[0]; + + switch (InstComponent) { + case -1: + continue; // no instrument required, go to next component + + // percussion songs (13000 = hand drum) + case 13000: + if(itembonuses.percussionMod == 0) { // check for the appropriate instrument type + HasInstrument = false; + c->Message_StringID(13, SONG_NEEDS_DRUM); // send an error message if missing + } + break; + + // wind songs (13001 = wooden flute) + case 13001: + if(itembonuses.windMod == 0) { + HasInstrument = false; + c->Message_StringID(13, SONG_NEEDS_WIND); + } + break; + + // string songs (13011 = lute) + case 13011: + if(itembonuses.stringedMod == 0) { + HasInstrument = false; + c->Message_StringID(13, SONG_NEEDS_STRINGS); + } + break; + + // brass songs (13012 = horn) + case 13012: + if(itembonuses.brassMod == 0) { + HasInstrument = false; + c->Message_StringID(13, SONG_NEEDS_BRASS); + } + break; + + default: // some non-instrument component. Let it go, but record it in the log + mlog(SPELLS__CASTING_ERR, "Something odd happened: Song %d required component %s", spell_id, component); + } + + if(!HasInstrument) { // if the instrument is missing, log it and interrupt the song + mlog(SPELLS__CASTING_ERR, "Song %d: Canceled. Missing required instrument %s", spell_id, component); + if(c->GetGM()) + c->Message(0, "Your GM status allows you to finish casting even though you're missing a required instrument."); + else { + InterruptSpell(); + return; + } + } + } // end bard component section + + // handle the components for traditional casters + else { + if(c->GetInv().HasItem(component, component_count, invWhereWorn|invWherePersonal) == -1) // item not found + { + if (!missingreags) + { + c->Message_StringID(13, MISSING_SPELL_COMP); + missingreags=true; + } + + const Item_Struct *item = database.GetItem(component); + if(item) { + c->Message_StringID(13, MISSING_SPELL_COMP_ITEM, item->Name); + mlog(SPELLS__CASTING_ERR, "Spell %d: Canceled. Missing required reagent %s (%d)", spell_id, item->Name, component); + } + else { + char TempItemName[64]; + strcpy((char*)&TempItemName, "UNKNOWN"); + mlog(SPELLS__CASTING_ERR, "Spell %d: Canceled. Missing required reagent %s (%d)", spell_id, TempItemName, component); + } + } + } // end bard/not bard ifs + } // end reagent loop + + if (missingreags) { + if(c->GetGM()) + c->Message(0, "Your GM status allows you to finish casting even though you're missing required components."); + else { + InterruptSpell(); + return; + } + } + else if (!bard_song_mode) + { + for(int t_count = 0; t_count < 4; t_count++) { + component = spells[spell_id].components[t_count]; + if (component == -1) + continue; + component_count = spells[spell_id].component_counts[t_count]; + mlog(SPELLS__CASTING_ERR, "Spell %d: Consuming %d of spell component item id %d", spell_id, component, component_count); + // Components found, Deleting + // now we go looking for and deleting the items one by one + for(int s = 0; s < component_count; s++) + { + inv_slot_id = c->GetInv().HasItem(component, 1, invWhereWorn|invWherePersonal); + if(inv_slot_id != -1) + { + c->DeleteItemInInventory(inv_slot_id, 1, true); + } + else + { // some kind of error in the code if this happens + c->Message(13, "ERROR: reagent item disappeared while processing?"); + } + } + } + } // end missingreags/consumption + } // end `focus did not help us` + } // end IsClient() for reagents + + // this is common to both bard and non bard + + // if this was cast from an inventory slot, check out the item that's there + + int16 DeleteChargeFromSlot = -1; + + if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT)) + && inventory_slot != 0xFFFFFFFF) // 10 is an item + { + const ItemInst* inst = CastToClient()->GetInv()[inventory_slot]; + if (inst && inst->IsType(ItemClassCommon) && (inst->GetItem()->Click.Effect == spell_id) && inst->GetCharges()) + { + //const Item_Struct* item = inst->GetItem(); + int16 charges = inst->GetItem()->MaxCharges; + if(charges > -1) { // charged item, expend a charge + mlog(SPELLS__CASTING, "Spell %d: Consuming a charge from item %s (%d) which had %d/%d charges.", spell_id, inst->GetItem()->Name, inst->GetItem()->ID, inst->GetCharges(), inst->GetItem()->MaxCharges); + DeleteChargeFromSlot = inventory_slot; + } else { + mlog(SPELLS__CASTING, "Spell %d: Cast from unlimited charge item %s (%d) (%d charges)", spell_id, inst->GetItem()->Name, inst->GetItem()->ID, inst->GetItem()->MaxCharges); + } + } + else + { + mlog(SPELLS__CASTING_ERR, "Item used to cast spell %d was missing from inventory slot %d after casting!", spell_id, inventory_slot); + Message(13, "Casting Error: Active casting item not found in inventory slot %i", inventory_slot); + InterruptSpell(); + return; + } + } + if(IsClient()) { + uint32 buff_max = GetMaxTotalSlots(); + for (int buffSlot = 0; buffSlot < buff_max; buffSlot++) { + if (buffs[buffSlot].spellid == 0 || buffs[buffSlot].spellid >= SPDAT_RECORDS) + continue; + + if(spells[buffs[buffSlot].spellid].numhits > 0) + CheckHitsRemaining(buffSlot, true); + } + + TrySympatheticProc(target, spell_id); + } + + TryTwincast(this, target, spell_id); + + TryTriggerOnCast(spell_id, 0); + + // we're done casting, now try to apply the spell + if( !SpellFinished(spell_id, spell_target, slot, mana_used, inventory_slot, resist_adjust) ) + { + mlog(SPELLS__CASTING_ERR, "Casting of %d canceled: SpellFinished returned false.", spell_id); + InterruptSpell(); + return; + } + + if(DeleteChargeFromSlot >= 0) + CastToClient()->DeleteItemInInventory(DeleteChargeFromSlot, 1, true); + + // + // at this point the spell has successfully been cast + // + + // if the spell is cast by a client, trigger the EVENT_CAST player quest + if(this->IsClient()) { + if(parse->PlayerHasQuestSub("EVENT_CAST") ) { + char temp[64]; + sprintf(temp, "%d", spell_id); + parse->EventPlayer(EVENT_CAST, CastToClient(), temp, 0); + } + } + + if(bard_song_mode) + { + if(IsClient()) + { + this->CastToClient()->CheckSongSkillIncrease(spell_id); + this->CastToClient()->MemorizeSpell(slot, spell_id, memSpellSpellbar); + } + // go again in 6 seconds + //this is handled with bardsong_timer + // DoCastSpell(casting_spell_id, casting_spell_targetid, casting_spell_slot, 6000, casting_spell_mana); + + mlog(SPELLS__CASTING, "Bard song %d should be started", spell_id); + } + else + { + if(IsClient()) + { + Client *c = CastToClient(); + SendSpellBarEnable(spell_id); + + // this causes the delayed refresh of the spell bar gems + c->MemorizeSpell(slot, spell_id, memSpellSpellbar); + + // this tells the client that casting may happen again + SetMana(GetMana()); + + // skills + if(slot < MAX_PP_MEMSPELL) + { + c->CheckIncreaseSkill(spells[spell_id].skill, NULL); + + // increased chance of gaining channel skill if you regained concentration + c->CheckIncreaseSkill(CHANNELING, NULL, regain_conc ? 5 : 0); + + c->CheckSpecializeIncrease(spell_id); + } + } + } + + // there should be no casting going on now + ZeroCastingVars(); + + // set the rapid recast timer for next time around + delaytimer = true; + spellend_timer.Start(400,true); + + mlog(SPELLS__CASTING, "Spell casting of %d is finished.", spell_id); + +} + +bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction) { + +/* + The basic types of spells: + + Single target - some might be undead only, self only, etc, but these + all affect the target of the caster. + + AE around caster - these affect entities close to the caster, and have + no target. + + AE around target - these have a target, and affect the target as well as + entities close to the target. + + AE on location - this is a tricky one that is cast on a mob target but + has a special AE duration that keeps it recasting every 2.5 sec on the + same location. These work the same as AE around target spells, except + the target is a special beacon that's created when the spell is cast + + Group - the caster is always affected, but there's more + targetgroupbuffs on - these affect the target and the target's group. + targetgroupbuffs off - no target, affects the caster's group. + + Group Teleport - the caster plus his group are affected. these cannot + be targeted. + + I think the string ID SPELL_NEED_TAR is wrong, it dosent seem to show up. +*/ + + // during this switch, this variable gets set to one of these things + // and that causes the spell to be executed differently + + bodyType target_bt = BT_Humanoid; + SpellTargetType targetType = spells[spell_id].targettype; + bodyType mob_body = spell_target ? spell_target->GetBodyType() : BT_Humanoid; + + if(IsPlayerIllusionSpell(spell_id) + && spell_target != NULL // null ptr crash safeguard + && !spell_target->IsNPC() // still self only if NPC targetted + && IsClient() + && (IsGrouped() // still self only if not grouped + || IsRaidGrouped()) + && CastToClient()->CheckAAEffect(aaEffectProjectIllusion)){ + mlog(AA__MESSAGE, "Project Illusion overwrote target caster: %s spell id: %d was ON", GetName(), spell_id); + targetType = ST_GroupClientAndPet; + } + + switch (targetType) + { +// single target spells + case ST_Self: + { + spell_target = this; + CastAction = SingleTarget; + break; + } + + case ST_TargetOptional: + { + if(!spell_target) + spell_target = this; + CastAction = SingleTarget; + break; + } + + // target required for these + case ST_Undead: { + if(!spell_target || ( + spell_target->GetBodyType() != BT_SummonedUndead + && spell_target->GetBodyType() != BT_Undead + && spell_target->GetBodyType() != BT_Vampire + ) + ) + { + //invalid target + mlog(SPELLS__CASTING_ERR, "Spell %d canceled: invalid target of body type %d (undead)", spell_id, spell_target->GetBodyType()); + Message_StringID(13,SPELL_NEED_TAR); + return false; + } + CastAction = SingleTarget; + break; + } + + case ST_Summoned: { + uint8 body_type = spell_target?spell_target->GetBodyType():0; + if(!spell_target || (body_type != BT_Summoned && body_type != BT_Summoned2 && body_type != BT_Summoned3)) + { + //invalid target + mlog(SPELLS__CASTING_ERR, "Spell %d canceled: invalid target of body type %d (summoned)", spell_id, body_type); + Message_StringID(13,SPELL_NEED_TAR); + return false; + } + CastAction = SingleTarget; + break; + } + + case ST_SummonedPet: + { + uint8 body_type = spell_target ? spell_target->GetBodyType() : 0; + if(!spell_target || (spell_target != GetPet()) || + (body_type != BT_Summoned && body_type != BT_Summoned2 && body_type != BT_Summoned3 && body_type != BT_Animal)) + { + mlog(SPELLS__CASTING_ERR, "Spell %d canceled: invalid target of body type %d (summoned pet)", + spell_id, body_type); + + Message_StringID(13, SPELL_NEED_TAR); + + return false; + } + CastAction = SingleTarget; + break; + } + //single body type target spells... + //this is a little hackish, but better than duplicating code IMO + case ST_Plant: if(target_bt == BT_Humanoid) target_bt = BT_Plant; + case ST_Dragon: if(target_bt == BT_Humanoid) target_bt = BT_Dragon; + case ST_Giant: if(target_bt == BT_Humanoid) target_bt = BT_Giant; + case ST_Animal: if(target_bt == BT_Humanoid) target_bt = BT_Animal; + + // check for special case body types (Velious dragons/giants) + if(mob_body == BT_RaidGiant) mob_body = BT_Giant; + if(mob_body == BT_VeliousDragon) mob_body = BT_Dragon; + + { + if(!spell_target || mob_body != target_bt) + { + //invalid target + mlog(SPELLS__CASTING_ERR, "Spell %d canceled: invalid target of body type %d (want body Type %d)", spell_id, spell_target->GetBodyType(), target_bt); + if(!spell_target) + Message_StringID(13,SPELL_NEED_TAR); + else + Message_StringID(13,CANNOT_AFFECT_NPC); + return false; + } + CastAction = SingleTarget; + break; + } + + case ST_Tap: + case ST_LDoNChest_Cursed: + case ST_Target: { + if(IsLDoNObjectSpell(spell_id)) + { + if(!spell_target) + { + mlog(SPELLS__CASTING_ERR, "Spell %d canceled: invalid target (ldon object)", spell_id); + Message_StringID(13,SPELL_NEED_TAR); + return false; + } + else + { + if(!spell_target->IsNPC()) + { + mlog(SPELLS__CASTING_ERR, "Spell %d canceled: invalid target (normal)", spell_id); + Message_StringID(13,SPELL_NEED_TAR); + return false; + } + + if(spell_target->GetClass() != LDON_TREASURE) + { + mlog(SPELLS__CASTING_ERR, "Spell %d canceled: invalid target (normal)", spell_id); + Message_StringID(13,SPELL_NEED_TAR); + return false; + } + } + } + + if(!spell_target) + { + mlog(SPELLS__CASTING_ERR, "Spell %d canceled: invalid target (normal)", spell_id); + Message_StringID(13,SPELL_NEED_TAR); + return false; // can't cast these unless we have a target + } + CastAction = SingleTarget; + break; + } + + case ST_Corpse: + { + if(!spell_target || !spell_target->IsPlayerCorpse()) + { + mlog(SPELLS__CASTING_ERR, "Spell %d canceled: invalid target (corpse)", spell_id); + uint32 message = ONLY_ON_CORPSES; + if(!spell_target) message = SPELL_NEED_TAR; + else if(!spell_target->IsCorpse()) message = ONLY_ON_CORPSES; + else if(!spell_target->IsPlayerCorpse()) message = CORPSE_NOT_VALID; + Message_StringID(13, message); + return false; + } + CastAction = SingleTarget; + break; + } + case ST_Pet: + { + spell_target = GetPet(); + if(!spell_target) + { + mlog(SPELLS__CASTING_ERR, "Spell %d canceled: invalid target (no pet)", spell_id); + Message_StringID(13,NO_PET); + return false; // can't cast these unless we have a target + } + CastAction = SingleTarget; + break; + } + + case ST_AEBard: + case ST_AECaster: + { + spell_target = NULL; + ae_center = this; + CastAction = AECaster; + break; + } + + case ST_HateList: + { + spell_target = NULL; + ae_center = this; + CastAction = CAHateList; + break; + } + + case ST_UndeadAE: //should only affect undead... + case ST_AETarget: + { + if(!spell_target) + { + mlog(SPELLS__CASTING_ERR, "Spell %d canceled: invalid target (AOE)", spell_id); + Message_StringID(13,SPELL_NEED_TAR); + return false; + } + ae_center = spell_target; + CastAction = AETarget; + break; + } + + // Group spells + case ST_GroupTeleport: + case ST_Group: + { + if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id)) { + if( (!target) || + (target->IsNPC() && !(target->GetOwner() && target->GetOwner()->IsClient())) || + (target->IsCorpse()) ) + spell_target = this; + else + spell_target = target; + } else { + spell_target = this; + } + CastAction = GroupSpell; + break; + } + case ST_GroupClientAndPet: + { + if(!spell_target) + { + mlog(SPELLS__CASTING_ERR, "Spell %d canceled: invalid target (Group Required: Single Target)", spell_id); + Message_StringID(13,SPELL_NEED_TAR); + return false; + } + + if(spell_target != this) + { + if(spell_target == GetPet()) + { + CastAction = SingleTarget; + } + else if(spell_target == GetOwner()) + { + CastAction = SingleTarget; + } + else + { + uint32 group_id_caster = 0; + uint32 group_id_target = 0; + if(IsClient()) + { + if(IsGrouped()) + { + group_id_caster = GetGroup()->GetID(); + } + else if(IsRaidGrouped()) + { + group_id_caster = (GetRaid()->GetGroup(CastToClient()) == 0xFFFF) ? 0 : (GetRaid()->GetGroup(CastToClient()) + 1); + } + } + else if(IsPet()) + { + Mob *owner = GetOwner(); + if(owner->IsGrouped()) + { + group_id_caster = owner->GetGroup()->GetID(); + } + else if(owner->IsRaidGrouped()) + { + group_id_caster = (owner->GetRaid()->GetGroup(CastToClient()) == 0xFFFF) ? 0 : (owner->GetRaid()->GetGroup(CastToClient()) + 1); + } + } +#ifdef BOTS + else if(IsBot()) + { + if(IsGrouped()) + { + group_id_caster = GetGroup()->GetID(); + } + else if(IsRaidGrouped()) + { + if(GetOwner()) + group_id_caster = (GetRaid()->GetGroup(GetOwner()->CastToClient()) == 0xFFFF) ? 0 : (GetRaid()->GetGroup(GetOwner()->CastToClient()) + 1); + } + } +#endif //BOTS + + if(spell_target->IsClient()) + { + if(spell_target->IsGrouped()) + { + group_id_target = spell_target->GetGroup()->GetID(); + } + else if(spell_target->IsRaidGrouped()) + { + group_id_target = (spell_target->GetRaid()->GetGroup(CastToClient()) == 0xFFFF) ? 0 : (spell_target->GetRaid()->GetGroup(CastToClient()) + 1); + } + } + else if(spell_target->IsPet()) + { + Mob *owner = spell_target->GetOwner(); + if(owner->IsGrouped()) + { + group_id_target = owner->GetGroup()->GetID(); + } + else if(owner->IsRaidGrouped()) + { + group_id_target = (owner->GetRaid()->GetGroup(CastToClient()) == 0xFFFF) ? 0 : (owner->GetRaid()->GetGroup(CastToClient()) + 1); + } + } +#ifdef BOTS + else if(spell_target->IsBot()) + { + if(spell_target->IsGrouped()) + { + group_id_target = spell_target->GetGroup()->GetID(); + } + else if(spell_target->IsRaidGrouped()) + { + if(spell_target->GetOwner()) + group_id_target = (spell_target->GetRaid()->GetGroup(spell_target->GetOwner()->CastToClient()) == 0xFFFF) ? 0 : (spell_target->GetRaid()->GetGroup(spell_target->GetOwner()->CastToClient()) + 1); + } + } +#endif //BOTS + + if(group_id_caster == 0 || group_id_target == 0) + { + mlog(SPELLS__CASTING_ERR, "Spell %d canceled: Attempted to cast a Single Target Group spell on a ungrouped member.", spell_id); + Message_StringID(13, TARGET_GROUP_MEMBER); + return false; + } + + if(group_id_caster != group_id_target) + { + mlog(SPELLS__CASTING_ERR, "Spell %d canceled: Attempted to cast a Single Target Group spell on a ungrouped member.", spell_id); + Message_StringID(13, TARGET_GROUP_MEMBER); + return false; + } + + CastAction = SingleTarget; + } + } + else + { + CastAction = SingleTarget; + } + break; + } + + case ST_Directional: + CastAction = DirectionalAE; + spell_target = NULL; + ae_center = NULL; + break; + + case ST_TargetsTarget: + { + Mob *spell_target_tot = spell_target ? spell_target->GetTarget() : NULL; + if(!spell_target_tot) + return false; + //Verfied from live - Target's Target needs to be in combat range to recieve the effect + if (!this->CombatRange(spell_target)) + return false; + + spell_target = spell_target_tot; + CastAction = SingleTarget; + 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); + Message(0, "I dont know Target Type: %d Spell: (%d) %s", spells[spell_id].targettype, spell_id, spells[spell_id].name); + CastAction = CastActUnknown; + break; + } + } + return(true); +} + +// only used from CastedSpellFinished, and procs +// we can't interrupt in this, or anything called from this! +// if you need to abort the casting, return false +bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 mana_used, + uint32 inventory_slot, int16 resist_adjust, bool isproc) +{ + _ZP(Mob_SpellFinished); + + //EQApplicationPacket *outapp = NULL; + Mob *ae_center = NULL; + + if(!IsValidSpell(spell_id)) + return false; + + if( spells[spell_id].zonetype == 1 && !zone->CanCastOutdoor()){ + if(IsClient()){ + if(!CastToClient()->GetGM()){ + Message_StringID(13, CAST_OUTDOORS); + return false; + } + } + } + + if(IsEffectInSpell(spell_id, SE_Levitate) && !zone->CanLevitate()){ + if(IsClient()){ + if(!CastToClient()->GetGM()){ + Message(13, "You can't levitate in this zone."); + return false; + } + } + } + + if(IsClient() && !CastToClient()->GetGM()){ + + if(zone->IsSpellBlocked(spell_id, GetX(), GetY(), GetZ())){ + const char *msg = zone->GetSpellBlockedMessage(spell_id, GetX(), GetY(), GetZ()); + if(msg){ + Message(13, msg); + return false; + } + else{ + Message(13, "You can't cast this spell here."); + return false; + } + + } + } + + if + ( + this->IsClient() && + (zone->GetZoneID() == 183 || zone->GetZoneID() == 184) && // load + CastToClient()->Admin() < 80 + ) + { + if + ( + IsEffectInSpell(spell_id, SE_Gate) || + IsEffectInSpell(spell_id, SE_Translocate) || + IsEffectInSpell(spell_id, SE_Teleport) + ) + { + Message(0, "The Gods brought you here, only they can send you away."); + return false; + } + } + + + //determine the type of spell target we have + CastAction_type CastAction; + if(!DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction)) + return(false); + + mlog(SPELLS__CASTING, "Spell %d: target type %d, target %s, AE center %s", spell_id, CastAction, spell_target?spell_target->GetName():"NONE", ae_center?ae_center->GetName():"NONE"); + + // if a spell has the AEDuration flag, it becomes an AE on target + // spell that's recast every 2500 msec for AEDuration msec. There are + // spells of all kinds of target types that do this, strangely enough + // TODO: finish this + if(IsAEDurationSpell(spell_id)) { + // the spells are AE target, but we aim them on a beacon + Mob *beacon_loc = spell_target ? spell_target : this; + Beacon *beacon = new Beacon(beacon_loc, spells[spell_id].AEDuration); + entity_list.AddBeacon(beacon); + mlog(SPELLS__CASTING, "Spell %d: AE duration beacon created, entity id %d", spell_id, beacon->GetName()); + spell_target = NULL; + ae_center = beacon; + CastAction = AECaster; + } + + // check line of sight to target if it's a detrimental spell + if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id)) + { + mlog(SPELLS__CASTING, "Spell %d: cannot see target %s", spell_target->GetName()); + Message_StringID(13,CANT_SEE_TARGET); + return false; + } + + // check to see if target is a caster mob before performing a mana tap + if(spell_target && IsManaTapSpell(spell_id)) { + if(spell_target->GetCasterClass() == 'N') { + Message_StringID(13, TARGET_NO_MANA); + return false; + } + } + + //range check our target, if we have one and it is not us + float range = spells[spell_id].range; + if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id)) + range = spells[spell_id].aoerange; + + range = GetActSpellRange(spell_id, range); + if(IsPlayerIllusionSpell(spell_id) + && IsClient() + && CastToClient()->CheckAAEffect(aaEffectProjectIllusion)){ + range = 100; + } + if(spell_target != NULL && spell_target != this) { + //casting a spell on somebody but ourself, make sure they are in range + float dist2 = DistNoRoot(*spell_target); + float range2 = range * range; + if(dist2 > range2) { + //target is out of range. + mlog(SPELLS__CASTING, "Spell %d: Spell target is out of range (squared: %f > %f)", spell_id, dist2, range2); + Message_StringID(13, TARGET_OUT_OF_RANGE); + return(false); + } + } + + // + // Switch #2 - execute the spell + // + switch(CastAction) + { + default: + case CastActUnknown: + case SingleTarget: + { + +#ifdef BOTS + if(IsBot()) { + bool StopLogic = false; + if(!this->CastToBot()->DoFinishedSpellSingleTarget(spell_id, spell_target, slot, StopLogic)) + return false; + if(StopLogic) + break; + } +#endif //BOTS + + if(spell_target == NULL) { + mlog(SPELLS__CASTING, "Spell %d: Targeted spell, but we have no target", spell_id); + return(false); + } + if (isproc) { + SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, true); + } else { + SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false); + } + if(IsPlayerIllusionSpell(spell_id) + && IsClient() + && CastToClient()->CheckAAEffect(aaEffectProjectIllusion)){ + mlog(AA__MESSAGE, "Effect Project Illusion for %s on spell id: %d was ON", GetName(), spell_id); + CastToClient()->DisableAAEffect(aaEffectProjectIllusion); + } + else{ + mlog(AA__MESSAGE, "Effect Project Illusion for %s on spell id: %d was OFF", GetName(), spell_id); + } + break; + } + + case AECaster: + case AETarget: + { +#ifdef BOTS + if(IsBot()) { + bool StopLogic = false; + if(!this->CastToBot()->DoFinishedSpellAETarget(spell_id, spell_target, slot, StopLogic)) + return false; + if(StopLogic) + break; + } +#endif //BOTS + + // we can't cast an AE spell without something to center it on + assert(ae_center != NULL); + + if(ae_center->IsBeacon()) { + // special ae duration spell + ae_center->CastToBeacon()->AELocationSpell(this, spell_id, resist_adjust); + } else { + // regular PB AE or targeted AE spell - spell_target is null if PB + if(spell_target) // this must be an AETarget spell + { + // affect the target too + 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); + } + break; + } + + case GroupSpell: + { +#ifdef BOTS + if(IsBot()) { + bool StopLogic = false; + if(!this->CastToBot()->DoFinishedSpellGroupTarget(spell_id, spell_target, slot, StopLogic)) + return false; + if(StopLogic) + break; + } +#endif //BOTS + + if(spells[spell_id].can_mgb && IsClient() && CastToClient()->CheckAAEffect(aaEffectMassGroupBuff)) + { + SpellOnTarget(spell_id, this); + entity_list.MassGroupBuff(this, this, spell_id, true); + CastToClient()->DisableAAEffect(aaEffectMassGroupBuff); + } + else + { + // at this point spell_target is a member of the other group, or the + // caster if they're not using TGB + // NOTE: this will always hit the caster, plus the target's group so + // it can affect up to 7 people if the targeted group is not our own + if(spell_target->IsGrouped()) + { + Group *target_group = entity_list.GetGroupByMob(spell_target); + if(target_group) + { + target_group->CastGroupSpell(this, spell_id); + } + } + else if(spell_target->IsRaidGrouped() && spell_target->IsClient()) + { + Raid *target_raid = entity_list.GetRaidByClient(spell_target->CastToClient()); + uint32 gid = 0xFFFFFFFF; + if(target_raid){ + gid = target_raid->GetGroup(spell_target->GetName()); + if(gid < 12) + target_raid->CastGroupSpell(this, spell_id, gid); + else + SpellOnTarget(spell_id, spell_target); + } + } + else + { + // if target is grouped, CastGroupSpell will cast it on the caster + // too, but if not then we have to do that here. + + if(spell_target != this){ + SpellOnTarget(spell_id, this); + #ifdef GROUP_BUFF_PETS + //pet too + if (GetPet() && HasPetAffinity() && !GetPet()->IsCharmed()) + SpellOnTarget(spell_id, GetPet()); + #endif + } + + SpellOnTarget(spell_id, spell_target); + #ifdef GROUP_BUFF_PETS + //pet too + if (spell_target->GetPet() && HasPetAffinity() && !spell_target->GetPet()->IsCharmed()) + SpellOnTarget(spell_id, spell_target->GetPet()); + #endif + } + } + break; + } + + case CAHateList: + { + if(!IsClient()) + { + hate_list.SpellCast(this, spell_id, spells[spell_id].range > spells[spell_id].aoerange ? spells[spell_id].range : spells[spell_id].aoerange); + } + break; + } + + 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); + + while(angle_start > 360.0f) + angle_start -= 360.0f; + + while(angle_end > 360.0f) + angle_end -= 360.0f; + + list targets_in_range; + list::iterator iter; + + entity_list.GetTargetsForConeArea(this, 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(spell_target)) + SpellOnTarget(spell_id, spell_target, false, true, resist_adjust); + } + } + else + { + if(heading_to_target >= angle_start && heading_to_target <= angle_end) + { + if(CheckLosFN((*iter))) + SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); + } + } + iter++; + } + break; + } + } + + DoAnim(spells[spell_id].CastingAnim, 0, true, IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS); + + // Set and send the nimbus effect if this spell has one + int NimbusEffect = GetNimbusEffect(spell_id); + if(NimbusEffect) { + if(!IsNimbusEffectActive(NimbusEffect)) { + SendSpellEffect(NimbusEffect, 500, 0, 1, 3000, true); + } + } + + // 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) + { + mlog(SPELLS__CASTING, "Spell %d: consuming %d mana", spell_id, mana_used); + if (!DoHPToManaCovert(mana_used)) + SetMana(GetMana() - mana_used); + } + + //set our reuse timer on long ass reuse_time spells... + if(IsClient()) + { + if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) + { + 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); + if(casting_spell_type == 1) //AA + { + time_t timestamp = time(NULL); + CastToClient()->SendAATimer((casting_spell_timer - pTimerAAStart), timestamp, timestamp); + } + } + else if(spells[spell_id].recast_time > 1000) { + int recast = spells[spell_id].recast_time/1000; + if (spell_id == SPELL_LAY_ON_HANDS) //lay on hands + { + recast -= GetAA(aaFervrentBlessing) * 420; + } + else if (spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2) //harm touch + { + recast -= GetAA(aaTouchoftheWicked) * 420; + } + int reduction = CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id); + if(reduction) + recast -= reduction; + + mlog(SPELLS__CASTING, "Spell %d: Setting long reuse timer to %d s (orig %d)", spell_id, recast, spells[spell_id].recast_time); + CastToClient()->GetPTimers().Start(pTimerSpellStart + spell_id, recast); + } + } + + if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_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); + } + } + + if(IsNPC()) + CastToNPC()->AI_Event_SpellCastFinished(true, slot); + + return true; +} + +/* + * handle bard song pulses... + * + * we make several assumptions that SpellFinished does not: + * - there are no AEDuration (beacon) bard songs + * - there are no recourse spells on bard songs + * - there is no long recast delay on bard songs + * + * return false to stop the song + */ +bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, uint16 slot) { + if(slot == USE_ITEM_SPELL_SLOT) { + //bard songs should never come from items... + mlog(SPELLS__BARDS, "Bard Song Pulse %d: Supposidly cast from an item. Killing song.", spell_id); + return(false); + } + + //determine the type of spell target we have + Mob *ae_center = NULL; + CastAction_type CastAction; + if(!DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction)) { + mlog(SPELLS__BARDS, "Bard Song Pulse %d: was unable to determine target. Stopping.", spell_id); + return(false); + } + + if(ae_center != NULL && ae_center->IsBeacon()) { + mlog(SPELLS__BARDS, "Bard Song Pulse %d: Unsupported Beacon NPC AE spell", spell_id); + return(false); + } + + //use mana, if this spell has a mana cost + int mana_used = spells[spell_id].mana; + if(mana_used > 0) { + if(mana_used > GetMana()) { + //ran out of mana... this calls StopSong() for us + mlog(SPELLS__BARDS, "Ran out of mana while singing song %d", spell_id); + return(false); + } + + mlog(SPELLS__CASTING, "Bard Song Pulse %d: consuming %d mana (have %d)", spell_id, mana_used, GetMana()); + SetMana(GetMana() - mana_used); + } + + + // check line of sight to target if it's a detrimental spell + if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target)) + { + mlog(SPELLS__CASTING, "Bard Song Pulse %d: cannot see target %s", spell_target->GetName()); + Message_StringID(13, CANT_SEE_TARGET); + return(false); + } + + //range check our target, if we have one and it is not us + float range = 0.00f; + + range = GetActSpellRange(spell_id, spells[spell_id].range, true); + if(spell_target != NULL && spell_target != this) { + //casting a spell on somebody but ourself, make sure they are in range + float dist2 = DistNoRoot(*spell_target); + float range2 = range * range; + if(dist2 > range2) { + //target is out of range. + mlog(SPELLS__BARDS, "Bard Song Pulse %d: Spell target is out of range (squared: %f > %f)", spell_id, dist2, range2); + Message_StringID(13, TARGET_OUT_OF_RANGE); + return(false); + } + } + + // + // Switch #2 - execute the spell + // + switch(CastAction) + { + default: + case CastActUnknown: + case SingleTarget: + { + if(spell_target == NULL) { + mlog(SPELLS__BARDS, "Bard Song Pulse %d: Targeted spell, but we have no target", spell_id); + return(false); + } + mlog(SPELLS__BARDS, "Bard Song Pulse: Targeted. spell %d, target %s", spell_id, spell_target->GetName()); + spell_target->BardPulse(spell_id, this); + break; + } + + case AECaster: + { + if(IsBeneficialSpell(spell_id)) + SpellOnTarget(spell_id, this); + + bool affect_caster = !IsNPC(); //NPC AE spells do not affect the NPC caster + entity_list.AEBardPulse(this, this, spell_id, affect_caster); + break; + } + case AETarget: + { + // we can't cast an AE spell without something to center it on + if(ae_center == NULL) { + mlog(SPELLS__BARDS, "Bard Song Pulse %d: AE Targeted spell, but we have no target", spell_id); + return(false); + } + + // regular PB AE or targeted AE spell - spell_target is null if PB + if(spell_target) { // this must be an AETarget spell + // affect the target too + spell_target->BardPulse(spell_id, this); + mlog(SPELLS__BARDS, "Bard Song Pulse: spell %d, AE target %s", spell_id, spell_target->GetName()); + } else { + mlog(SPELLS__BARDS, "Bard Song Pulse: spell %d, AE with no target", spell_id); + } + bool affect_caster = !IsNPC(); //NPC AE spells do not affect the NPC caster + entity_list.AEBardPulse(this, ae_center, spell_id, affect_caster); + break; + } + + case GroupSpell: + { + if(spell_target->IsGrouped()) { + mlog(SPELLS__BARDS, "Bard Song Pulse: spell %d, Group targeting group of %s", spell_id, spell_target->GetName()); + Group *target_group = entity_list.GetGroupByMob(spell_target); + if(target_group) + target_group->GroupBardPulse(this, spell_id); + } + else if(spell_target->IsRaidGrouped() && spell_target->IsClient()) { + mlog(SPELLS__BARDS, "Bard Song Pulse: spell %d, Raid group targeting raid group of %s", spell_id, spell_target->GetName()); + Raid *r = entity_list.GetRaidByClient(spell_target->CastToClient()); + if(r){ + uint32 gid = r->GetGroup(spell_target->GetName()); + if(gid < 12){ + r->GroupBardPulse(this, spell_id, gid); + } + else{ + BardPulse(spell_id, this); +#ifdef GROUP_BUFF_PETS + if (GetPet() && HasPetAffinity() && !GetPet()->IsCharmed()) + GetPet()->BardPulse(spell_id, this); +#endif + } + } + } + else { + mlog(SPELLS__BARDS, "Bard Song Pulse: spell %d, Group target without group. Affecting caster.", spell_id); + BardPulse(spell_id, this); +#ifdef GROUP_BUFF_PETS + if (GetPet() && HasPetAffinity() && !GetPet()->IsCharmed()) + GetPet()->BardPulse(spell_id, this); +#endif + } + break; + } + } + + //do we need to do this??? + DoAnim(spells[spell_id].CastingAnim, 0, true, IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS); + if(IsClient()) + CastToClient()->CheckSongSkillIncrease(spell_id); + + return(true); +} + +void Mob::BardPulse(uint16 spell_id, Mob *caster) { + int buffs_i; + uint32 buff_count = GetMaxTotalSlots(); + for (buffs_i = 0; buffs_i < buff_count; buffs_i++) { + if(buffs[buffs_i].spellid != spell_id) + continue; + if(buffs[buffs_i].casterid != caster->GetID()) { + mlog(SPELLS__BARDS, "Bard Pulse for %d: found buff from caster %d and we are pulsing for %d... are there two bards playing the same song???", spell_id, buffs[buffs_i].casterid, caster->GetID()); + return; + } + //extend the spell if it will expire before the next pulse + if(buffs[buffs_i].ticsremaining <= 3) { + buffs[buffs_i].ticsremaining += 3; + mlog(SPELLS__BARDS, "Bard Song Pulse %d: extending duration in slot %d to %d tics", spell_id, buffs_i, buffs[buffs_i].ticsremaining); + } + + //should we send this buff update to the client... seems like it would + //be a lot of traffic for no reason... +//this may be the wrong packet... + if(IsClient()) { + EQApplicationPacket *packet = new EQApplicationPacket(OP_Action, sizeof(Action_Struct)); + + Action_Struct* action = (Action_Struct*) packet->pBuffer; + action->source = caster->GetID(); + action->target = GetID(); + action->spell = spell_id; + action->sequence = (uint32) (GetHeading() * 2); // just some random number + action->instrument_mod = caster->GetInstrumentMod(spell_id); + action->buff_unknown = 0; + action->level = buffs[buffs_i].casterlevel; + action->type = SpellDamageType; + entity_list.QueueCloseClients(this, packet, false, 200, 0, true, IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS); + + action->buff_unknown = 4; + + if(IsEffectInSpell(spell_id, SE_TossUp)) + { + action->buff_unknown = 0; + } + else if(spells[spell_id].pushback > 0 || spells[spell_id].pushup > 0) + { + if(IsClient()) + { + if(HasBuffIcon(caster, this, spell_id) == false) + { + CastToClient()->SetKnockBackExemption(true); + + action->buff_unknown = 0; + EQApplicationPacket* outapp_push = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer; + + double look_heading = caster->CalculateHeadingToTarget(GetX(), GetY()); + look_heading /= 256; + look_heading *= 360; + if(look_heading > 360) + look_heading -= 360; + + //x and y are crossed mkay + double new_x = spells[spell_id].pushback * sin(double(look_heading * 3.141592 / 180.0)); + double new_y = spells[spell_id].pushback * cos(double(look_heading * 3.141592 / 180.0)); + + spu->spawn_id = GetID(); + spu->x_pos = FloatToEQ19(GetX()); + spu->y_pos = FloatToEQ19(GetY()); + spu->z_pos = FloatToEQ19(GetZ()); + spu->delta_x = NewFloatToEQ13(new_x); + spu->delta_y = NewFloatToEQ13(new_y); + spu->delta_z = NewFloatToEQ13(spells[spell_id].pushup); + spu->heading = FloatToEQ19(GetHeading()); + spu->padding0002 =0; + spu->padding0006 =7; + spu->padding0014 =0x7f; + spu->padding0018 =0x5df27; + spu->animation = 0; + spu->delta_heading = NewFloatToEQ13(0); + outapp_push->priority = 6; + entity_list.QueueClients(this, outapp_push, true); + CastToClient()->FastQueuePacket(&outapp_push); + } + } + } + + if(IsClient() && IsEffectInSpell(spell_id, SE_ShadowStep)) + { + CastToClient()->SetShadowStepExemption(true); + } + + if(!IsEffectInSpell(spell_id, SE_BindAffinity)) + { + CastToClient()->QueuePacket(packet); + } + + EQApplicationPacket *message_packet = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); + CombatDamage_Struct *cd = (CombatDamage_Struct *)message_packet->pBuffer; + cd->target = action->target; + cd->source = action->source; + cd->type = SpellDamageType; + cd->spellid = action->spell; + cd->sequence = action->sequence; + cd->damage = 0; + if(!IsEffectInSpell(spell_id, SE_BindAffinity)) + { + entity_list.QueueCloseClients(this, message_packet, false, 200, 0, true, IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS); + } + safe_delete(message_packet); + safe_delete(packet); + + } + //we are done... + return; + } + mlog(SPELLS__BARDS, "Bard Song Pulse %d: Buff not found, reapplying spell.", spell_id); + //this spell is not affecting this mob, apply it. + caster->SpellOnTarget(spell_id, this); +} + +/////////////////////////////////////////////////////////////////////////////// +// buff related functions + +// returns how many _ticks_ the buff will last. +// a tick is 6 seconds +// this is the place to figure out random duration buffs like fear and charm. +// both the caster and target mobs are passed in, so different behavior can +// even be created depending on the types of mobs involved +// +// right now this is just an outline, working on this.. +int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caster_level_override) +{ + int formula, duration; + + if(!IsValidSpell(spell_id) || (!caster && !target)) + return 0; + + if(!caster && !target) + return 0; + + // if we have at least one, we can make do, we'll just pretend they're the same + if(!caster) + caster = target; + if(!target) + target = caster; + + formula = spells[spell_id].buffdurationformula; + duration = spells[spell_id].buffduration; + + int castlevel = caster->GetCasterLevel(spell_id); + if(caster_level_override > 0) + castlevel = caster_level_override; + + int res = CalcBuffDuration_formula(castlevel, formula, duration); + // Only need this for clients, since the change was for bard songs, I assume we should keep non bard songs getting +1 + // However if its bard or not and is mez, charm or fear, we need to add 1 so that client is in sync + if(!IsShortDurationBuff(spell_id) || + IsFearSpell(spell_id) || + IsCharmSpell(spell_id) || + IsMezSpell(spell_id) || + IsBlindSpell(spell_id)) + res += 1; + + mlog(SPELLS__CASTING, "Spell %d: Casting level %d, formula %d, base_duration %d: result %d", + spell_id, castlevel, formula, duration, res); + + return(res); +} + +// the generic formula calculations +int CalcBuffDuration_formula(int level, int formula, int duration) +{ + int i; // temp variable + + switch(formula) + { + case 0: // not a buff + return 0; + + case 1: + i = (int)ceil(level / 2.0f); + return i < duration ? (i < 1 ? 1 : i) : duration; + + case 2: + i = (int)ceil(duration / 5.0f * 3); + return i < duration ? (i < 1 ? 1 : i) : duration; + + case 3: + i = level * 30; + return i < duration ? (i < 1 ? 1 : i) : duration; + + case 4: // only used by 'LowerElement' + return ((duration != 0) ? duration : 50); + + case 5: + i = duration; + // 0 value results in a 3 tick spell, else its between 1-3 ticks. + return i < 3 ? (i < 1 ? 3 : i) : 3; + + case 6: + i = (int)ceil(level / 2.0f); + return i < duration ? (i < 1 ? 1 : i) : duration; + + case 7: + i = level; + return (duration == 0) ? (i < 1 ? 1 : i) : duration; + + case 8: + i = level + 10; + return i < duration ? (i < 1 ? 1 : i) : duration; + + case 9: + i = level * 2 + 10; + return i < duration ? (i < 1 ? 1 : i) : duration; + + case 10: + i = level * 3 + 10; + return i < duration ? (i < 1 ? 1 : i) : duration; + + case 11: + return duration; + + case 12: + return duration; + + case 15: // Don't know what the real formula for this should be. Used by Skinspikes potion. + return duration; + + case 50: // lucy says this is unlimited? + return 72000; // 5 days + + case 3600: + return duration ? duration : 3600; + + default: + LogFile->write(EQEMuLog::Debug, "CalcBuffDuration_formula: unknown formula %d", formula); + return 0; + } +} + +// helper function for AddBuff to determine stacking +// spellid1 is the spell already worn, spellid2 is the one trying to be cast +// returns: +// 0 if not the same type, no action needs to be taken +// 1 if spellid1 should be removed (overwrite) +// -1 if they can't stack and spellid2 should be stopped +//currently, a spell will not land if it would overwrite a better spell on any effect +//if all effects are better or the same, we overwrite, else we do nothing +int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, int caster_level2, Mob* caster1, Mob* caster2) +{ + const SPDat_Spell_Struct &sp1 = spells[spellid1]; + const SPDat_Spell_Struct &sp2 = spells[spellid2]; + + int i, effect1, effect2, sp1_value, sp2_value; + int blocked_effect, blocked_below_value, blocked_slot; + int overwrite_effect, overwrite_below_value, overwrite_slot; + + mlog(SPELLS__STACKING, "Check Stacking on old %s (%d) @ lvl %d (by %s) vs. new %s (%d) @ lvl %d (by %s)", sp1.name, spellid1, caster_level1, (caster1==NULL)?"Nobody":caster1->GetName(), sp2.name, spellid2, caster_level2, (caster2==NULL)?"Nobody":caster2->GetName()); + + if(((spellid1 == spellid2) && (spellid1 == 2751)) || //special case spells that will block each other no matter what + ((spellid1 == spellid2) && (spellid1 == 2755)) //manaburn / lifeburn + ){ + mlog(SPELLS__STACKING, "Blocking spell because manaburn/lifeburn does not stack with itself"); + return -1; + } + + //resurrection effects wont count for overwrite/block stacking + switch(spellid1) + { + case 756: + case 757: + case 5249: + return (0); + } + + switch (spellid2) + { + case 756: + case 757: + case 5249: + return (0); + } + + /* + One of these is a bard song and one isn't and they're both beneficial so they should stack. + */ + if(IsBardSong(spellid1) != IsBardSong(spellid2)) + { + if(!IsDetrimentalSpell(spellid1) && !IsDetrimentalSpell(spellid2)) + { + mlog(SPELLS__STACKING, "%s and %s are beneficial, and one is a bard song, no action needs to be taken", sp1.name, sp2.name); + return (0); + } + } + + + // check for special stacking block command in spell1 against spell2 + for(i = 0; i < EFFECT_COUNT; i++) + { + effect1 = sp1.effectid[i]; + if(effect1 == SE_StackingCommand_Block) + { + /* + The logic here is if you're comparing the same spells they can't block each other + from refreshing + */ + if(spellid1 == spellid2) + continue; + + blocked_effect = sp1.base[i]; + blocked_slot = sp1.formula[i] - 201; //they use base 1 for slots, we use base 0 + blocked_below_value = sp1.max[i]; + + if(sp2.effectid[blocked_slot] == blocked_effect) + { + sp2_value = CalcSpellEffectValue(spellid2, blocked_slot, caster_level2); + + mlog(SPELLS__STACKING, "%s (%d) blocks effect %d on slot %d below %d. New spell has value %d on that slot/effect. %s.", + sp1.name, spellid1, blocked_effect, blocked_slot, blocked_below_value, sp2_value, (sp2_value < blocked_below_value)?"Blocked":"Not blocked"); + + if(sp2_value < blocked_below_value) + { + mlog(SPELLS__STACKING, "Blocking spell because sp2_value < blocked_below_value"); + return -1; // blocked + } + } else { + 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); + } + } + } + + // check for special stacking overwrite in spell2 against effects in spell1 + for(i = 0; i < EFFECT_COUNT; i++) + { + effect2 = sp2.effectid[i]; + if(effect2 == SE_StackingCommand_Overwrite) + { + overwrite_effect = sp2.base[i]; + overwrite_slot = sp2.formula[i] - 201; //they use base 1 for slots, we use base 0 + overwrite_below_value = sp2.max[i]; + if(sp1.effectid[overwrite_slot] == overwrite_effect) + { + sp1_value = CalcSpellEffectValue(spellid1, overwrite_slot, caster_level1); + + mlog(SPELLS__STACKING, "%s (%d) overwrites existing spell if effect %d on slot %d is below %d. Old spell has value %d on that slot/effect. %s.", + sp2.name, spellid2, overwrite_effect, overwrite_slot, overwrite_below_value, sp1_value, (sp1_value < overwrite_below_value)?"Overwriting":"Not overwriting"); + + if(sp1_value < overwrite_below_value) + { + mlog(SPELLS__STACKING, "Overwrite spell because sp1_value < overwrite_below_value"); + return 1; // overwrite spell if its value is less + } + } else { + mlog(SPELLS__STACKING, "%s (%d) overwrites existing spell if effect %d on slot %d is below %d, but we do not have that effect on that slot. Ignored.", + sp2.name, spellid2, overwrite_effect, overwrite_slot, overwrite_below_value); + + } + } + } + + bool sp1_detrimental = IsDetrimentalSpell(spellid1); + bool sp2_detrimental = IsDetrimentalSpell(spellid2); + bool sp_det_mismatch; + + if(sp1_detrimental == sp2_detrimental) + sp_det_mismatch = false; + else + sp_det_mismatch = true; + + // now compare matching effects + // arbitration takes place if 2 spells have the same effect at the same + // effect slot, otherwise they're stackable, even if it's the same effect + bool will_overwrite = false; + for(i = 0; i < EFFECT_COUNT; i++) + { + if(IsBlankSpellEffect(spellid1, i) || IsBlankSpellEffect(spellid2, i)) + continue; + + effect1 = sp1.effectid[i]; + effect2 = sp2.effectid[i]; + + //Effects which really aren't going to affect stacking. + if(effect1 == SE_CurrentHPOnce || + effect1 == SE_CurseCounter || + effect1 == SE_DiseaseCounter || + effect1 == SE_PoisonCounter){ + continue; + } + + /* + Quick check, are the effects the same, if so then + keep going else ignore it for stacking purposes. + */ + if(effect1 != effect2) + continue; + + /* + If target is a npc and caster1 and caster2 exist + If Caster1 isn't the same as Caster2 and the effect is a DoT then ignore it. + */ + if(IsNPC() && caster1 && caster2 && caster1 != caster2) { + if(effect1 == SE_CurrentHP && sp1_detrimental && sp2_detrimental) { + continue; + mlog(SPELLS__STACKING, "Both casters exist and are not the same, the effect is a detrimental dot, moving on"); + } + } + + if(effect1 == SE_CompleteHeal){ //SE_CompleteHeal never stacks or overwrites ever, always block. + mlog(SPELLS__STACKING, "Blocking spell because complete heal never stacks or overwries"); + return (-1); + } + + /* + If the effects are the same and + sp1 = beneficial & sp2 = detrimental or + sp1 = detrimental & sp2 = beneficial + Then this effect should be ignored for stacking purposes. + */ + if(sp_det_mismatch) + { + mlog(SPELLS__STACKING, "The effects are the same but the spell types are not, passing the effect"); + continue; + } + + /* + If the spells aren't the same + and the effect is a dot we can go ahead and stack it + */ + if(effect1 == SE_CurrentHP && spellid1 != spellid2 && sp1_detrimental && sp2_detrimental) { + mlog(SPELLS__STACKING, "The spells are not the same and it is a detrimental dot, passing"); + continue; + } + + sp1_value = CalcSpellEffectValue(spellid1, i, caster_level1); + sp2_value = CalcSpellEffectValue(spellid2, i, caster_level2); + + // some spells are hard to compare just on value. attack speed spells + // have a value that's a percentage for instance + if + ( + effect1 == SE_AttackSpeed || + effect1 == SE_AttackSpeed2 || + effect1 == SE_AttackSpeed3 + ) + { + sp1_value -= 100; + sp2_value -= 100; + } + + if(sp1_value < 0) + sp1_value = 0 - sp1_value; + if(sp2_value < 0) + sp2_value = 0 - sp2_value; + + if(sp2_value < sp1_value) { + mlog(SPELLS__STACKING, "Spell %s (value %d) is not as good as %s (value %d). Rejecting %s.", + sp2.name, sp2_value, sp1.name, sp1_value, sp2.name); + return -1; // can't stack + } + //we dont return here... a better value on this one effect dosent mean they are + //all better... + + mlog(SPELLS__STACKING, "Spell %s (value %d) is not as good as %s (value %d). We will overwrite %s if there are no other conflicts.", + sp1.name, sp1_value, sp2.name, sp2_value, sp1.name); + will_overwrite = true; + } + + //if we get here, then none of the values on the new spell are "worse" + //so now we see if this new spell is any better, or if its not related at all + if(will_overwrite) { + mlog(SPELLS__STACKING, "Stacking code decided that %s should overwrite %s.", sp2.name, sp1.name); + return(1); + } + + mlog(SPELLS__STACKING, "Stacking code decided that %s is not affected by %s.", sp2.name, sp1.name); + return 0; +} + + +// returns the slot the buff was added to, -1 if it wasn't added due to +// stacking problems, and -2 if this is not a buff +// if caster is null, the buff will be added with the caster level being +// the level of the mob +int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_override) +{ + + int buffslot, ret, caster_level, emptyslot = -1; + bool will_overwrite = false; + vector overwrite_slots; + + if(level_override > 0) + caster_level = level_override; + else + caster_level = caster ? caster->GetCasterLevel(spell_id) : GetCasterLevel(spell_id); + + if(duration == 0) + { + duration = CalcBuffDuration(caster, this, spell_id); + + if(caster) + duration = caster->GetActSpellDuration(spell_id, duration); + } + + if(duration == 0) { + mlog(SPELLS__BUFFS, "Buff %d failed to add because its duration came back as 0.", spell_id); + return -2; // no duration? this isn't a buff + } + + mlog(SPELLS__BUFFS, "Trying to add buff %d cast by %s (cast level %d) with duration %d", + spell_id, caster?caster->GetName():"UNKNOWN", caster_level, duration); + + // first we loop through everything checking that the spell + // can stack with everything. this is to avoid stripping the spells + // 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(); + uint32 start_slot = 0; + uint32 end_slot = 0; + if(IsDisciplineBuff(spell_id)) + { + start_slot = GetMaxBuffSlots() + GetMaxSongSlots(); + end_slot = start_slot + GetCurrentDiscSlots(); + } + else if(spells[spell_id].short_buff_box) + { + start_slot = GetMaxBuffSlots(); + end_slot = start_slot + GetCurrentSongSlots(); + } + else + { + start_slot = 0; + end_slot = GetCurrentBuffSlots(); + } + + for(buffslot = 0; buffslot < buff_count; buffslot++) + { + const Buffs_Struct &curbuf = buffs[buffslot]; + + if(curbuf.spellid != SPELL_UNKNOWN) + { + // there's a buff in this slot + ret = CheckStackConflict(curbuf.spellid, curbuf.casterlevel, spell_id, caster_level, entity_list.GetMobID(curbuf.casterid), caster); + if(ret == -1) { // stop the spell + mlog(SPELLS__BUFFS, "Adding buff %d failed: stacking prevented by spell %d in slot %d with caster level %d", spell_id, curbuf.spellid, buffslot, curbuf.casterlevel); + return -1; + } + if(ret == 1) { // set a flag to indicate that there will be overwriting + mlog(SPELLS__BUFFS, "Adding buff %d will overwrite spell %d in slot %d with caster level %d", spell_id, curbuf.spellid, buffslot, curbuf.casterlevel); + will_overwrite = true; + overwrite_slots.push_back(buffslot); + } + } + else + { + if(emptyslot == -1) + { + if(buffslot >= start_slot && buffslot < end_slot) + { + emptyslot = buffslot; + } + } + } + } + + // we didn't find an empty slot to put it in, and it's not overwriting + // anything so there must not be any room left. + if(emptyslot == -1 && !will_overwrite) + // return -1; + { + if(IsDetrimentalSpell(spell_id)) //Sucks to be you, bye bye one of your buffs + { + for(buffslot = 0; buffslot < buff_count; buffslot++) + { + const Buffs_Struct &curbuf = buffs[buffslot]; + if(IsBeneficialSpell(curbuf.spellid)) + { + mlog(SPELLS__BUFFS, "No slot for detrimental buff %d, so we are overwriting a beneficial buff %d in slot %d", spell_id, curbuf.spellid, buffslot); + BuffFadeBySlot(buffslot,false); + emptyslot = buffslot; + break; + } + } + if(emptyslot == -1) { + mlog(SPELLS__BUFFS, "Unable to find a buff slot for detrimental buff %d", spell_id); + return(-1); + } + } + else { + mlog(SPELLS__BUFFS, "Unable to find a buff slot for beneficial buff %d", spell_id); + return -1; + } + } + + // at this point we know that this buff will stick, but we have + // to remove some other buffs already worn if will_overwrite is true + if(will_overwrite) + { + vector::iterator cur, end; + cur = overwrite_slots.begin(); + end = overwrite_slots.end(); + for(; cur != end; cur++) { + // strip spell + BuffFadeBySlot(*cur, false); + + // if we hadn't found a free slot before, or if this is earlier + // we use it + if(emptyslot == -1 || *cur < emptyslot) + emptyslot = *cur; + } + } + + // now add buff at emptyslot + assert(buffs[emptyslot].spellid == SPELL_UNKNOWN); // sanity check + + buffs[emptyslot].spellid = spell_id; + buffs[emptyslot].casterlevel = caster_level; + if(caster && caster->IsClient()) { + strcpy(buffs[emptyslot].caster_name, caster->GetName()); + } else { + memset(buffs[emptyslot].caster_name, 0, 64); + } + buffs[emptyslot].casterid = caster ? caster->GetID() : 0; + buffs[emptyslot].ticsremaining = duration; + buffs[emptyslot].counters = CalculateCounters(spell_id); + buffs[emptyslot].numhits = spells[spell_id].numhits; + buffs[emptyslot].client = caster ? caster->IsClient() : 0; + buffs[emptyslot].persistant_buff = 0; + buffs[emptyslot].deathsaveCasterAARank = 0; + buffs[emptyslot].deathSaveSuccessChance = 0; + + if(level_override > 0) + { + buffs[emptyslot].UpdateClient = true; + } + else{ + if(buffs[emptyslot].ticsremaining > (1+CalcBuffDuration_formula(caster_level, spells[spell_id].buffdurationformula, spells[spell_id].buffduration))) + buffs[emptyslot].UpdateClient = true; + } + + mlog(SPELLS__BUFFS, "Buff %d added to slot %d with caster level %d", spell_id, emptyslot, caster_level); + if(IsPet() && GetOwner() && GetOwner()->IsClient()) { + SendPetBuffsToClient(); + } + + + if((IsClient() && !CastToClient()->GetPVP()) || (IsPet() && GetOwner() && GetOwner()->IsClient() && !GetOwner()->CastToClient()->GetPVP()) || + (IsMerc() && GetOwner() && GetOwner()->IsClient() && !GetOwner()->CastToClient()->GetPVP())) + { + EQApplicationPacket *outapp = MakeBuffsPacket(); + + entity_list.QueueClientsByTarget(this, outapp, false, NULL, true, false, BIT_SoDAndLater); + + if(GetTarget() == this) { + CastToClient()->QueuePacket(outapp); + } + + safe_delete(outapp); + } + + // recalculate bonuses since we stripped/added buffs + CalcBonuses(); + + return emptyslot; +} + +// used by some MobAI stuff +// NOT USED BY SPELL CODE +// note that this should not be used for determining which slot to place a +// buff into +// returns -1 on stack failure, -2 if all slots full, the slot number if the buff should overwrite another buff, or a free buff slot +int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) +{ + int i, ret, firstfree = -2; + + 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(); + for (i=0; i < buff_count; i++) + { + const Buffs_Struct &curbuf = buffs[i]; + + // no buff in this slot + if (curbuf.spellid == SPELL_UNKNOWN) + { + // if we haven't found a free slot, this is the first one so save it + if(firstfree == -2) + firstfree = i; + continue; + } + + if(curbuf.spellid == spellid) + return(-1); //do not recast a buff we already have on, we recast fast enough that we dont need to refresh our buffs + + // there's a buff in this slot + ret = CheckStackConflict(curbuf.spellid, curbuf.casterlevel, spellid, caster_level); + if(ret == 1) { + // should overwrite current slot + if(iFailIfOverwrite) { + mlog(AI__BUFFS, "Buff %d would overwrite %d in slot %d, reporting stack failure", spellid, curbuf.spellid, i); + return(-1); + } + if(firstfree == -2) + firstfree = i; + } + if(ret == -1) { + mlog(AI__BUFFS, "Buff %d would conflict with %d in slot %d, reporting stack failure", spellid, curbuf.spellid, i); + return -1; // stop the spell, can't stack it + } + } + + mlog(AI__BUFFS, "Reporting that buff %d could successfully be placed into slot %d", spellid, firstfree); + + return firstfree; +} + +/////////////////////////////////////////////////////////////////////////////// +// spell effect related functions +// +// this is actually applying a spell cast from 'this' on 'spelltar' +// it performs pvp checking and applies resists, etc then it +// passes it to SpellEffect which causes effects to the target +// +// this is called by these functions: +// Mob::SpellFinished +// Entity::AESpell (called by Mob::SpellFinished) +// Group::CastGroupSpell (called by Mob::SpellFinished) +// +// also note you can't interrupt the spell here. at this point it's going +// and if you don't want effects just return false. interrupting here will +// break stuff +// +bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_resist_adjust, int16 resist_adjust, bool isproc) +{ + + // well we can't cast a spell on target without a target + if(!spelltar) + { + mlog(SPELLS__CASTING_ERR, "Unable to apply spell %d without a target", spell_id); + Message(13, "SOT: You must have a target for this spell."); + return false; + } + + if(spelltar->IsClient() && spelltar->CastToClient()->IsHoveringForRespawn()) + return false; + + if(IsDetrimentalSpell(spell_id) && !IsAttackAllowed(spelltar) && !IsResurrectionEffects(spell_id)) { + if(!IsClient() || !CastToClient()->GetGM()) { + Message_StringID(MT_Shout, SPELL_NO_HOLD); + return false; + } + } + + if(RuleB(Spells, EnableBlockedBuffs)) + { + if(spelltar->IsBlockedBuff(spell_id)) + { + mlog(SPELLS__BUFFS, "Spell %i not applied to %s as it is a Blocked Buff.", spell_id, spelltar->GetName()); + return false; + } + + if(spelltar->IsPet() && spelltar->GetOwner() && spelltar->GetOwner()->IsBlockedPetBuff(spell_id)) + { + mlog(SPELLS__BUFFS, "Spell %i not applied to %s (%s's pet) as it is a Pet Blocked Buff.", spell_id, spelltar->GetName(), + spelltar->GetOwner()->GetName()); + return false; + } + } + + EQApplicationPacket *action_packet, *message_packet; + float spell_effectiveness; + + if(!IsValidSpell(spell_id)) + return false; + + uint16 caster_level = GetCasterLevel(spell_id); + + mlog(SPELLS__CASTING, "Casting spell %d on %s with effective caster level %d", spell_id, spelltar->GetName(), caster_level); + + // Actual cast action - this causes the caster animation and the particles + // around the target + // we do this first, that way we get the particles even if the spell + // doesn't land due to pvp protection + // note: this packet is sent again if the spell is successful, with a flag + // set + action_packet = new EQApplicationPacket(OP_Action, sizeof(Action_Struct)); + Action_Struct* action = (Action_Struct*) action_packet->pBuffer; + + // select source + if(IsClient() && CastToClient()->GMHideMe()) + { + action->source = spelltar->GetID(); + } + else + { + action->source = GetID(); + // this is a hack that makes detrimental buffs work client to client + // TODO figure out how to do this right + if + ( + IsDetrimentalSpell(spell_id) && + IsClient() && + spelltar->IsClient() + ) + { + action->source = spelltar->GetID(); + } + } + + // select target + if // Bind Sight line of spells + ( + spell_id == 500 || // bind sight + spell_id == 407 // cast sight + ) + { + action->target = GetID(); + } + else + { + action->target = spelltar->GetID(); + } + + action->level = caster_level; // caster level, for animation only + action->type = 231; // 231 means a spell + action->spell = spell_id; + action->sequence = (uint32) (GetHeading() * 2); // just some random number + action->instrument_mod = GetInstrumentMod(spell_id); + action->buff_unknown = 0; + + if(spelltar != this && spelltar->IsClient()) // send to target + spelltar->CastToClient()->QueuePacket(action_packet); + if(IsClient()) // send to caster + CastToClient()->QueuePacket(action_packet); + // send to people in the area, ignoring caster and target + entity_list.QueueCloseClients(spelltar, action_packet, true, 200, this, true, spelltar->IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS); + + + /* Send the EVENT_CAST_ON event */ + if(spelltar->IsNPC()) + { char temp1[100]; + sprintf(temp1, "%d", spell_id); + parse->EventNPC(EVENT_CAST_ON, spelltar->CastToNPC(), this, temp1, 0); + } + + // now check if the spell is allowed to land + + // invuln mobs can't be affected by any spells, good or bad + if(spelltar->GetInvul() || spelltar->DivineAura()) { + mlog(SPELLS__CASTING_ERR, "Casting spell %d on %s aborted: they are invulnerable.", spell_id, spelltar->GetName()); + safe_delete(action_packet); + return false; + } + + //cannot hurt untargetable mobs + bodyType bt = spelltar->GetBodyType(); + if(bt == BT_NoTarget || bt == BT_NoTarget2) { + if (RuleB(Pets, UnTargetableSwarmPet)) { + if (spelltar->IsNPC()) { + if (!spelltar->CastToNPC()->GetSwarmOwner()) { + mlog(SPELLS__CASTING_ERR, "Casting spell %d on %s aborted: they are untargetable", spell_id, spelltar->GetName()); + safe_delete(action_packet); + return(false); + } + } else { + mlog(SPELLS__CASTING_ERR, "Casting spell %d on %s aborted: they are untargetable", spell_id, spelltar->GetName()); + safe_delete(action_packet); + return(false); + } + } else { + mlog(SPELLS__CASTING_ERR, "Casting spell %d on %s aborted: they are untargetable", spell_id, spelltar->GetName()); + safe_delete(action_packet); + return(false); + } + } + + // Prevent double invising, which made you uninvised + // Not sure if all 3 should be stacking + if(IsEffectInSpell(spell_id, SE_Invisibility)) + { + if(spelltar->invisible) + { + spelltar->Message_StringID(MT_Shout, ALREADY_INVIS, GetCleanName()); + safe_delete(action_packet); + return false; + } + } + + if(IsEffectInSpell(spell_id, SE_InvisVsUndead)) + { + if(spelltar->invisible_undead) + { + spelltar->Message_StringID(MT_Shout, ALREADY_INVIS, GetCleanName()); + safe_delete(action_packet); + return false; + } + } + + if(IsEffectInSpell(spell_id, SE_InvisVsAnimals)) + { + if(spelltar->invisible_animals) + { + spelltar->Message_StringID(MT_Shout, ALREADY_INVIS, GetCleanName()); + safe_delete(action_packet); + return false; + } + } + + if(!(IsClient() && CastToClient()->GetGM()) && !IsHarmonySpell(spell_id)) // GMs can cast on anything + { + // Beneficial spells check + if(IsBeneficialSpell(spell_id)) + { + if(IsClient() && //let NPCs do beneficial spells on anybody if they want, should be the job of the AI, not the spell code to prevent this from going wrong + spelltar != this) + { + + Client* pClient = 0; + Raid* pRaid = 0; + Group* pBasicGroup = 0; + uint32 nGroup = 0; //raid group + + Client* pClientTarget = 0; + Raid* pRaidTarget = 0; + Group* pBasicGroupTarget = 0; + uint32 nGroupTarget = 0; //raid group + + Client* pClientTargetPet = 0; + Raid* pRaidTargetPet = 0; + Group* pBasicGroupTargetPet = 0; + uint32 nGroupTargetPet = 0; //raid group + + const uint32 cnWTF = 0xFFFFFFFF + 1; //this should be zero unless on 64bit? forced uint64? + + //Caster client pointers + pClient = this->CastToClient(); + pRaid = entity_list.GetRaidByClient(pClient); + pBasicGroup = entity_list.GetGroupByMob(this); + if(pRaid) + nGroup = pRaid->GetGroup(pClient) + 1; + + //Target client pointers + if(spelltar->IsClient()) + { + pClientTarget = spelltar->CastToClient(); + pRaidTarget = entity_list.GetRaidByClient(pClientTarget); + pBasicGroupTarget = entity_list.GetGroupByMob(spelltar); + if(pRaidTarget) + nGroupTarget = pRaidTarget->GetGroup(pClientTarget) + 1; + } + + if(spelltar->IsPet()) + { + Mob *owner = spelltar->GetOwner(); + if(owner->IsClient()) + { + pClientTargetPet = owner->CastToClient(); + pRaidTargetPet = entity_list.GetRaidByClient(pClientTargetPet); + pBasicGroupTargetPet = entity_list.GetGroupByMob(owner); + if(pRaidTargetPet) + nGroupTargetPet = pRaidTargetPet->GetGroup(pClientTargetPet) + 1; + } + + } + + + if(!IsBeneficialAllowed(spelltar) || + (IsGroupOnlySpell(spell_id) && + !( + (pBasicGroup && ((pBasicGroup == pBasicGroupTarget) || (pBasicGroup == pBasicGroupTargetPet))) || //Basic Group + + ((nGroup != cnWTF) && ((nGroup == nGroupTarget) || (nGroup == nGroupTargetPet))) || //Raid group + + (spelltar == GetPet()) //should be able to cast grp spells on self and pet despite grped status. + ) + ) + ) + { + if(spells[spell_id].targettype == ST_AEBard) { + //if it was a beneficial AE bard song don't spam the window that it would not hold + mlog(SPELLS__CASTING_ERR, "Beneficial ae bard song %d can't take hold %s -> %s, IBA? %d", spell_id, GetName(), spelltar->GetName(), IsBeneficialAllowed(spelltar)); + } else { + mlog(SPELLS__CASTING_ERR, "Beneficial spell %d can't take hold %s -> %s, IBA? %d", spell_id, GetName(), spelltar->GetName(), IsBeneficialAllowed(spelltar)); + Message_StringID(MT_Shout, SPELL_NO_HOLD); + } + safe_delete(action_packet); + return false; + } + } + } + else if ( !IsAttackAllowed(spelltar, true) && !IsResurrectionEffects(spell_id)) // Detrimental spells - PVP check + { + mlog(SPELLS__CASTING_ERR, "Detrimental spell %d can't take hold %s -> %s", spell_id, GetName(), spelltar->GetName()); + spelltar->Message_StringID(MT_Shout, YOU_ARE_PROTECTED, GetCleanName()); + safe_delete(action_packet); + return false; + } + } + + // ok at this point the spell is permitted to affect the target, + // but we need to check special cases and resists + + + // check immunities + if(spelltar->IsImmuneToSpell(spell_id, this)) + { + //the above call does the message to the client if needed + mlog(SPELLS__RESISTS, "Spell %d can't take hold due to immunity %s -> %s", spell_id, GetName(), spelltar->GetName()); + safe_delete(action_packet); + return false; + } + + //check for AE_Undead + if(spells[spell_id].targettype == ST_UndeadAE){ + if(spelltar->GetBodyType() != BT_SummonedUndead && + spelltar->GetBodyType() != BT_Undead && + spelltar->GetBodyType() != BT_Vampire) + { + return false; + } + } + // Block next spell effect should be used up first(since its blocking the next spell) + if(CanBlockSpell()) { + uint32 buff_count = GetMaxTotalSlots(); + int focus = 0; + for (int b=0; b < buff_count; b++) { + if(IsEffectInSpell(buffs[b].spellid, SE_BlockNextSpellFocus)) { + focus = CalcFocusEffect(focusBlockNextSpell, buffs[b].spellid, spell_id); + if(focus) { + CheckHitsRemaining(b); + Message_StringID(MT_Shout, SPELL_WOULDNT_HOLD); + return false; + } + } + } + } + // Reflect + if(TryReflectSpell(spell_id) && spelltar && !reflect && IsDetrimentalSpell(spell_id) && this != spelltar) { + int reflect_chance = 0; + switch(RuleI(Spells, ReflectType)) + { + case 0: + break; + + case 1: + { + if(spells[spell_id].targettype == ST_Target) { + for(int y = 0; y < 16; y++) { + if(spells[spell_id].classes[y] < 255) + reflect_chance = 1; + } + } + break; + } + case 2: + { + for(int y = 0; y < 16; y++) { + if(spells[spell_id].classes[y] < 255) + reflect_chance = 1; + } + break; + } + case 3: + { + if(spells[spell_id].targettype == ST_Target) + reflect_chance = 1; + + break; + } + case 4: + reflect_chance = 1; + + default: + break; + } + if(reflect_chance) { + Message_StringID(MT_Spells, SPELL_REFLECT, GetCleanName(), spelltar->GetCleanName()); + SpellOnTarget(spell_id, this, true, use_resist_adjust, resist_adjust); + return false; + } + } + + // resist check - every spell can be resisted, beneficial or not + // add: ok this isn't true, eqlive's spell data is fucked up, buffs are + // not all unresistable, so changing this to only check certain spells + if(IsResistableSpell(spell_id)) + { + spell_effectiveness = spelltar->ResistSpell(spells[spell_id].resisttype, spell_id, this, use_resist_adjust, resist_adjust); + if(spell_effectiveness < 100) + { + if(spell_effectiveness == 0 || !IsPartialCapableSpell(spell_id) ) + { + mlog(SPELLS__RESISTS, "Spell %d was completely resisted by %s", spell_id, spelltar->GetName()); + Message_StringID(MT_SpellFailure, TARGET_RESISTED, spells[spell_id].name); + spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name); + + if(spelltar->IsAIControlled()){ + int32 aggro = CheckAggroAmount(spell_id); + if(aggro > 0) { + if(!IsHarmonySpell(spell_id)) + spelltar->AddToHateList(this, aggro); + else + if(!PassCharismaCheck(this, spelltar, spell_id)) + spelltar->AddToHateList(this, aggro); + } + else{ + int32 newhate = spelltar->GetHateAmount(this) + aggro; + if (newhate < 1) { + spelltar->SetHate(this,1); + } else { + spelltar->SetHate(this,newhate); + } + } + } + + safe_delete(action_packet); + return false; + } + } + } + else + { + 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); + spelltar->CheckHitsRemaining(0, false, false, SE_DamageShield); + } + + TrySpellTrigger(spelltar, spell_id); + TryApplyEffect(spelltar, spell_id); + + + if(spell_id == 982) // Cazic Touch, hehe =P + { + char target_name[64]; + strcpy(target_name, spelltar->GetCleanName()); + strupr(target_name); + Shout("%s!", target_name); + } + + 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); + if(aggro_amount > 0) + spelltar->AddToHateList(this, aggro_amount); else{ + int32 newhate = spelltar->GetHateAmount(this) + aggro_amount; + if (newhate < 1) { + spelltar->SetHate(this,1); + } else { + spelltar->SetHate(this,newhate); + } + } + } + else if (IsBeneficialSpell(spell_id) && !IsSummonPCSpell(spell_id)) + entity_list.AddHealAggro(spelltar, this, CheckHealAggroAmount(spell_id, (spelltar->GetMaxHP() - spelltar->GetHP()))); + + // cause the effects to the target + if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness)) + { + // if SpellEffect returned false there's a problem applying the + // spell. It's most likely a buff that can't stack. + mlog(SPELLS__CASTING_ERR, "Spell %d could not apply its effects %s -> %s\n", spell_id, GetName(), spelltar->GetName()); + Message_StringID(MT_Shout, SPELL_NO_HOLD); + safe_delete(action_packet); + return false; + } + + // send the action packet again now that the spell is successful + // NOTE: this is what causes the buff icon to appear on the client, if + // this is a buff - but it sortof relies on the first packet. + // the complete sequence is 2 actions and 1 damage message + action->buff_unknown = 0x04; // this is a success flag + + if(IsEffectInSpell(spell_id, SE_TossUp)) + { + action->buff_unknown = 0; + } + else if(spells[spell_id].pushback > 0 || spells[spell_id].pushup > 0) + { + if(spelltar->IsClient()) + { + if(HasBuffIcon(this, spelltar, spell_id) == false) + { + spelltar->CastToClient()->SetKnockBackExemption(true); + + action->buff_unknown = 0; + EQApplicationPacket* outapp_push = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct)); + PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer; + + double look_heading = CalculateHeadingToTarget(spelltar->GetX(), spelltar->GetY()); + look_heading /= 256; + look_heading *= 360; + if(look_heading > 360) + look_heading -= 360; + + //x and y are crossed mkay + double new_x = spells[spell_id].pushback * sin(double(look_heading * 3.141592 / 180.0)); + double new_y = spells[spell_id].pushback * cos(double(look_heading * 3.141592 / 180.0)); + + spu->spawn_id = spelltar->GetID(); + spu->x_pos = FloatToEQ19(spelltar->GetX()); + spu->y_pos = FloatToEQ19(spelltar->GetY()); + spu->z_pos = FloatToEQ19(spelltar->GetZ()); + spu->delta_x = NewFloatToEQ13(new_x); + spu->delta_y = NewFloatToEQ13(new_y); + spu->delta_z = NewFloatToEQ13(spells[spell_id].pushup); + spu->heading = FloatToEQ19(spelltar->GetHeading()); + spu->padding0002 =0; + spu->padding0006 =7; + spu->padding0014 =0x7f; + spu->padding0018 =0x5df27; + spu->animation = 0; + spu->delta_heading = NewFloatToEQ13(0); + outapp_push->priority = 6; + entity_list.QueueClients(this, outapp_push, true); + spelltar->CastToClient()->FastQueuePacket(&outapp_push); + } + } + } + + if(spelltar->IsClient() && IsEffectInSpell(spell_id, SE_ShadowStep)) + { + spelltar->CastToClient()->SetShadowStepExemption(true); + } + + if(!IsEffectInSpell(spell_id, SE_BindAffinity)) + { + if(spelltar != this && spelltar->IsClient()) // send to target + spelltar->CastToClient()->QueuePacket(action_packet); + if(IsClient()) // send to caster + CastToClient()->QueuePacket(action_packet); + } + // send to people in the area, ignoring caster and target + //live dosent send this to anybody but the caster + //entity_list.QueueCloseClients(spelltar, action_packet, true, 200, this, true, spelltar->IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS); + + // TEMPORARY - this is the message for the spell. + // double message on effects that use ChangeHP - working on this + message_packet = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); + CombatDamage_Struct *cd = (CombatDamage_Struct *)message_packet->pBuffer; + cd->target = action->target; + cd->source = action->source; + cd->type = action->type; + cd->spellid = action->spell; + cd->sequence = action->sequence; + cd->damage = 0; + if(!IsEffectInSpell(spell_id, SE_BindAffinity)) + { + entity_list.QueueCloseClients(spelltar, message_packet, false, 200, 0, true, spelltar->IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS); + } + safe_delete(action_packet); + safe_delete(message_packet); + + mlog(SPELLS__CASTING, "Cast of %d by %s on %s complete successfully.", spell_id, GetName(), spelltar->GetName()); + + return true; +} + +void Corpse::CastRezz(uint16 spellid, Mob* Caster) +{ + _log(SPELLS__REZ, "Corpse::CastRezz spellid %i, Rezzed() is %i, rezzexp is %i", spellid,Rezzed(),rezzexp); + + if(Rezzed()){ + if(Caster && Caster->IsClient()) + Caster->Message(13,"This character has already been resurrected."); + + return; + } + /* + if(!can_rez) { + if(Caster && Caster->IsClient()) + Caster->Message_StringID(0, CORPSE_TOO_OLD); + return; + } + */ + + 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->corpse_name,this->name,30); + memcpy(rezz->rezzer_name,Caster->GetName(),30); + rezz->zone_id = zone->GetZoneID(); + rezz->instance_id = zone->GetInstanceID(); + rezz->spellid = spellid; + rezz->x = this->x_pos; + rezz->y = this->y_pos; + rezz->z = this->z_pos; + rezz->unknown000 = 0x00000000; + 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); + _pkt(SPELLS__REZ, outapp); + safe_delete(outapp); +} + +bool Mob::FindBuff(uint16 spellid) +{ + int i; + + uint32 buff_count = GetMaxTotalSlots(); + for(i = 0; i < buff_count; i++) + if(buffs[i].spellid == spellid) + return true; + + return false; +} + +// removes all buffs +void Mob::BuffFadeAll() +{ + uint32 buff_count = GetMaxTotalSlots(); + for (int j = 0; j < buff_count; j++) { + if(buffs[j].spellid != SPELL_UNKNOWN) + BuffFadeBySlot(j, false); + } + //we tell BuffFadeBySlot not to recalc, so we can do it only once when were done + CalcBonuses(); +} + +void Mob::BuffFadeDetrimental() { + uint32 buff_count = GetMaxTotalSlots(); + for (int j = 0; j < buff_count; j++) { + if(buffs[j].spellid != SPELL_UNKNOWN) { + if(IsDetrimentalSpell(buffs[j].spellid)) + BuffFadeBySlot(j, false); + } + } +} + +void Mob::BuffFadeDetrimentalByCaster(Mob *caster) +{ + if(!caster) + return; + + uint32 buff_count = GetMaxTotalSlots(); + for (int j = 0; j < buff_count; j++) { + if(buffs[j].spellid != SPELL_UNKNOWN) { + if(IsDetrimentalSpell(buffs[j].spellid)) + { + //this is a pretty terrible way to do this but + //there really isn't another way till I rewrite the basics + Mob * c = entity_list.GetMob(buffs[j].casterid); + if(c && c == caster) + BuffFadeBySlot(j, false); + } + } + } +} + +void Mob::BuffFadeBySitModifier() +{ + bool r_bonus = false; + uint32 buff_count = GetMaxTotalSlots(); + for(uint32 j = 0; j < buff_count; ++j) + { + if(buffs[j].spellid != SPELL_UNKNOWN) + { + if(spells[buffs[j].spellid].disallow_sit) + { + BuffFadeBySlot(j, false); + r_bonus = true; + } + } + } + + if(r_bonus) + { + CalcBonuses(); + } +} + +// removes the buff matching spell_id +void Mob::BuffFadeBySpellID(uint16 spell_id) +{ + uint32 buff_count = GetMaxTotalSlots(); + for (int j = 0; j < buff_count; j++) + { + if (buffs[j].spellid == spell_id) + BuffFadeBySlot(j, false); + } + + //we tell BuffFadeBySlot not to recalc, so we can do it only once when were done + CalcBonuses(); +} + +// removes buffs containing effectid, skipping skipslot +void Mob::BuffFadeByEffect(int effectid, int skipslot) +{ + int i; + + uint32 buff_count = GetMaxTotalSlots(); + for(i = 0; i < buff_count; i++) + { + if(buffs[i].spellid == SPELL_UNKNOWN) + continue; + if(IsEffectInSpell(buffs[i].spellid, effectid) && i != skipslot) + BuffFadeBySlot(i, false); + } + + //we tell BuffFadeBySlot not to recalc, so we can do it only once when were done + CalcBonuses(); +} + +// checks if 'this' can be affected by spell_id from caster +// returns true if the spell should fail, false otherwise +bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster) +{ + _ZP(Mob_IsImmuneToSpell); + int effect_index; + + if(caster == NULL) + return(false); + + //TODO: this function loops through the effect list for + //this spell like 10 times, this could easily be consolidated + //into one loop through with a switch statement. + + mlog(SPELLS__RESISTS, "Checking to see if we are immune to spell %d cast by %s", spell_id, caster->GetName()); + + if(!IsValidSpell(spell_id)) + return true; + + if(IsMezSpell(spell_id)) + { + if(SpecAttacks[UNMEZABLE]) { + mlog(SPELLS__RESISTS, "We are immune to Mez spells."); + caster->Message_StringID(MT_Shout, CANNOT_MEZ); + int32 aggro = CheckAggroAmount(spell_id); + if(aggro > 0) { + AddToHateList(caster, aggro); + } else { + AddToHateList(caster, 1); + } + return true; + } + + // check max level for spell + effect_index = GetSpellEffectIndex(spell_id, SE_Mez); + assert(effect_index >= 0); + // NPCs get to ignore the max level + if((GetLevel() > spells[spell_id].max[effect_index]) && + (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity)))) + { + mlog(SPELLS__RESISTS, "Our level (%d) is higher than the limit of this Mez spell (%d)", GetLevel(), spells[spell_id].max[effect_index]); + caster->Message_StringID(MT_Shout, CANNOT_MEZ_WITH_SPELL); + return true; + } + } + + // slow and haste spells + if(SpecAttacks[UNSLOWABLE] && IsEffectInSpell(spell_id, SE_AttackSpeed)) + { + mlog(SPELLS__RESISTS, "We are immune to Slow spells."); + caster->Message_StringID(MT_Shout, IMMUNE_ATKSPEED); + int32 aggro = CheckAggroAmount(spell_id); + if(aggro > 0) { + AddToHateList(caster, aggro); + } else { + AddToHateList(caster, 1); + } + return true; + } + + // client vs client fear + if(IsEffectInSpell(spell_id, SE_Fear)) + { + effect_index = GetSpellEffectIndex(spell_id, SE_Fear); + if(SpecAttacks[UNFEARABLE]) { + mlog(SPELLS__RESISTS, "We are immune to Fear spells."); + caster->Message_StringID(MT_Shout, IMMUNE_FEAR); + int32 aggro = CheckAggroAmount(spell_id); + if(aggro > 0) { + AddToHateList(caster, aggro); + } else { + AddToHateList(caster, 1); + } + return true; + } else if(IsClient() && caster->IsClient() && (caster->CastToClient()->GetGM() == false)) + { + mlog(SPELLS__RESISTS, "Clients cannot fear eachother!"); + caster->Message_StringID(MT_Shout, IMMUNE_FEAR); + return true; + } + else if(GetLevel() > spells[spell_id].max[effect_index] && spells[spell_id].max[effect_index] != 0) + { + mlog(SPELLS__RESISTS, "Level is %d, cannot be feared by this spell.", GetLevel()); + caster->Message_StringID(MT_Shout, SPELL_NO_EFFECT); + return true; + } + + else if (IsClient() && CastToClient()->CheckAAEffect(aaEffectWarcry)) + { + Message(13, "Your are immune to fear."); + mlog(SPELLS__RESISTS, "Clients has WarCry effect, immune to fear!"); + caster->Message_StringID(MT_Shout, IMMUNE_FEAR); + return true; + } + } + + if(IsCharmSpell(spell_id)) + { + if(SpecAttacks[UNCHARMABLE]) + { + mlog(SPELLS__RESISTS, "We are immune to Charm spells."); + caster->Message_StringID(MT_Shout, CANNOT_CHARM); + int32 aggro = CheckAggroAmount(spell_id); + if(aggro > 0) { + AddToHateList(caster, aggro); + } else { + AddToHateList(caster, 1); + } + return true; + } + + if(this == caster) + { + mlog(SPELLS__RESISTS, "You are immune to your own charms."); + caster->Message(MT_Shout, "You cannot charm yourself."); + return true; + } + + //let npcs cast whatever charm on anyone + if(!caster->IsNPC()) + { + // check level limit of charm spell + effect_index = GetSpellEffectIndex(spell_id, SE_Charm); + assert(effect_index >= 0); + if(GetLevel() > spells[spell_id].max[effect_index] && spells[spell_id].max[effect_index] != 0) + { + mlog(SPELLS__RESISTS, "Our level (%d) is higher than the limit of this Charm spell (%d)", GetLevel(), spells[spell_id].max[effect_index]); + caster->Message_StringID(MT_Shout, CANNOT_CHARM_YET); + return true; + } + } + } + + if + ( + IsEffectInSpell(spell_id, SE_Root) || + IsEffectInSpell(spell_id, SE_MovementSpeed) + ) + { + if(SpecAttacks[UNSNAREABLE]) { + mlog(SPELLS__RESISTS, "We are immune to Snare spells."); + caster->Message_StringID(MT_Shout, IMMUNE_MOVEMENT); + int32 aggro = CheckAggroAmount(spell_id); + if(aggro > 0) { + AddToHateList(caster, aggro); + } else { + AddToHateList(caster, 1); + } + return true; + } + } + + if(IsLifetapSpell(spell_id)) + { + if(this == caster) + { + mlog(SPELLS__RESISTS, "You cannot lifetap yourself."); + caster->Message_StringID(MT_Shout, CANT_DRAIN_SELF); + return true; + } + } + + if(IsSacrificeSpell(spell_id)) + { + if(this == caster) + { + mlog(SPELLS__RESISTS, "You cannot sacrifice yourself."); + caster->Message_StringID(MT_Shout, CANNOT_SAC_SELF); + return true; + } + } + + mlog(SPELLS__RESISTS, "No immunities to spell %d found.", spell_id); + + return false; +} + +// +// Spell resists: +// returns an effectiveness index from 0 to 100. for most spells, 100 means +// it landed, and anything else means it was resisted; however there are some +// spells that can be partially effective, and this value can be used there. +// TODO: we need to figure out how the following pvp values work and implement them +// pvp_resist_base +// pvp_resist_calc +// pvp_resist_cap +float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override, int resist_override, bool CharismaCheck) +{ + + if(!caster) + { + return 100; + } + + if(spell_id != 0 && !IsValidSpell(spell_id)) + { + return 0; + } + + if(SpecAttacks[IMMUNE_CASTING_FROM_RANGE]) + { + if(!caster->CombatRange(this)) + { + return(0); + } + } + + if(SpecAttacks[IMMUNE_MAGIC]) + { + mlog(SPELLS__RESISTS, "We are immune to magic, so we fully resist the spell %d", spell_id); + return(0); + } + + //Get resist modifier and adjust it based on focus 2 resist about eq to 1% resist chance + int resist_modifier = (use_resist_override) ? resist_override : spells[spell_id].ResistDiff; + if(caster->IsClient()) + { + if(IsValidSpell(spell_id)) + { + int focus_resist = caster->CastToClient()->GetFocusEffect(focusResistRate, spell_id); + resist_modifier -= 2 * focus_resist; + } + } + + //Check for fear resist + if(IsFearSpell(spell_id)) + { + int fear_resist_bonuses = CalcFearResistChance(); + if(MakeRandomInt(0, 99) < fear_resist_bonuses) + { + mlog(SPELLS__RESISTS, "Resisted spell in fear resistance, had %d chance to resist", fear_resist_bonuses); + return 0; + } + } + + + if (!CharismaCheck){ + + //Check for Spell Effect specific resistance chances (ie AA Mental Fortitude) + int se_resist_bonuses = GetSpellEffectResistChance(spell_id); + if(se_resist_bonuses && (MakeRandomInt(0, 99) < se_resist_bonuses)) + { + return 0; + } + + // Check for Chance to Resist Spell bonuses (ie Sanctification Discipline) + int resist_bonuses = CalcResistChanceBonus(); + if(resist_bonuses && (MakeRandomInt(0, 99) < resist_bonuses)) + { + mlog(SPELLS__RESISTS, "Resisted spell in sanctification, had %d chance to resist", resist_bonuses); + return 0; + } + } + + //Get the resist chance for the target + if(resist_type == RESIST_NONE) + { + mlog(SPELLS__RESISTS, "Spell was unresistable"); + return 100; + } + + int target_resist; + switch(resist_type) + { + case RESIST_FIRE: + target_resist = GetFR(); + break; + case RESIST_COLD: + target_resist = GetCR(); + break; + case RESIST_MAGIC: + target_resist = GetMR(); + break; + case RESIST_DISEASE: + target_resist = GetDR(); + break; + case RESIST_POISON: + target_resist = GetPR(); + break; + case RESIST_CORRUPTION: + target_resist = GetCorrup(); + break; + case RESIST_PRISMATIC: + target_resist = (GetFR() + GetCR() + GetMR() + GetDR() + GetPR()) / 5; + break; + case RESIST_CHROMATIC: + { + target_resist = GetFR(); + int temp = GetCR(); + if(temp < target_resist) + { + target_resist = temp; + } + + temp = GetMR(); + if(temp < target_resist) + { + target_resist = temp; + } + + temp = GetDR(); + if(temp < target_resist) + { + target_resist = temp; + } + + temp = GetPR(); + if(temp < target_resist) + { + target_resist = temp; + } + } + break; + case RESIST_PHYSICAL: + default: + //This is guessed but the others are right + target_resist = (GetSTA() / 4); + } + + //Setup our base resist chance. + //Lulls have a slightly higher chance to resist than normal 15/200 or ~ 7.5% + int resist_chance; + if(IsHarmonySpell(spell_id)) + { + resist_chance = 15; + } + else + { + resist_chance = 0; + } + + //Adjust our resist chance based on level modifiers + int temp_level_diff = GetLevel() - caster->GetLevel(); + if(IsNPC() && GetLevel() >= RuleI(Casting, ResistFalloff)) + { + int a = (RuleI(Casting, ResistFalloff) - 1) - caster->GetLevel(); + if(a > 0) + { + temp_level_diff = a; + } + else + { + temp_level_diff = 0; + } + } + + if(IsClient() && GetLevel() >= 21 && temp_level_diff > 15) + { + temp_level_diff = 15; + } + + if(IsNPC() && temp_level_diff < -9) + { + temp_level_diff = -9; + } + + int level_mod = temp_level_diff * temp_level_diff / 2; + if(temp_level_diff < 0) + { + level_mod = -level_mod; + } + + if(IsNPC() && (caster->GetLevel() - GetLevel()) < -20) + { + level_mod = 1000; + } + + //Add our level, resist and -spell resist modifier to our roll chance + resist_chance += level_mod; + resist_chance += resist_modifier; + resist_chance += target_resist; + + if (CharismaCheck) + { + //For charm chance to break checks, Default 10 CHA = -1 resist mod. + int16 cha_resist_modifier = 0; + cha_resist_modifier = caster->GetCHA()/RuleI(Spells, CharismaEffectiveness); + resist_chance -= cha_resist_modifier; + } + + //Even more level stuff this time dealing with damage spells + if(IsNPC() && IsDamageSpell(spell_id) && GetLevel() >= 17) + { + int level_diff; + if(GetLevel() >= RuleI(Casting, ResistFalloff)) + { + level_diff = (RuleI(Casting, ResistFalloff) - 1) - caster->GetLevel(); + if(level_diff < 0) + { + level_diff = 0; + } + } + else + { + level_diff = GetLevel() - caster->GetLevel(); + } + resist_chance += (2 * level_diff); + } + + //Do our min and max resist checks. + if(resist_chance > spells[spell_id].MaxResist && spells[spell_id].MaxResist != 0) + { + resist_chance = spells[spell_id].MaxResist; + } + + if(resist_chance < spells[spell_id].MinResist && spells[spell_id].MinResist != 0) + { + resist_chance = spells[spell_id].MinResist; + } + + //Finally our roll + int roll = MakeRandomInt(0, 200); + if(roll > resist_chance) + { + return 100; + } + else + { + //This is confusing but it's basically right + //It skews partial resists up over 100 more often than not + if(!IsPartialCapableSpell(spell_id)) + { + return 0; + } + else + { + resist_chance -= roll; + if(resist_chance < 1) + { + resist_chance = 1; + } + + int partial_modifier = ((150 * (roll - resist_chance)) / resist_chance); + + if(IsNPC()) + { + if(GetLevel() > caster->GetLevel() && GetLevel() >= 17 && caster->GetLevel() <= 50) + { + partial_modifier += 5; + } + + if(GetLevel() >= 30 && caster->GetLevel() < 50) + { + partial_modifier += (caster->GetLevel() - 25); + } + + if(GetLevel() < 15) + { + partial_modifier -= 5; + } + } + + if(caster->IsNPC()) + { + if((GetLevel() - caster->GetLevel()) >= 20) + { + partial_modifier += (GetLevel() - caster->GetLevel()) * 1.5; + } + } + + if(partial_modifier < 0) + { + return 0; + } + + if(partial_modifier > 100) + { + return 100; + } + + return partial_modifier; + } + } +} + +int16 Mob::CalcResistChanceBonus() +{ + int resistchance = spellbonuses.ResistSpellChance + itembonuses.ResistSpellChance; + + if(IsClient()) + resistchance += aabonuses.ResistSpellChance; + + if (spellbonuses.ResistSpellChance) + CheckHitsRemaining(0, false, false, SE_ResistSpellChance); + + return resistchance; +} + +int16 Mob::CalcFearResistChance() +{ + int resistchance = spellbonuses.ResistFearChance + itembonuses.ResistFearChance; + if(this->IsClient()) { + resistchance += aabonuses.ResistFearChance; + if(aabonuses.Fearless == true) + resistchance = 100; + } + if(spellbonuses.Fearless == true || itembonuses.Fearless == true) + resistchance = 100; + + return resistchance; +} + +float Mob::GetAOERange(uint16 spell_id) { + float range; + + range = spells[spell_id].aoerange; + if(range == 0) //for TGB spells, they prolly do not have an aoe range + range = spells[spell_id].range; + if(range == 0) + range = 10; //something.... + + if (IsClient()) { + + if(IsBardSong(spell_id) && IsBeneficialSpell(spell_id)) { + //Live AA - Extended Notes, SionachiesCrescendo + float song_bonus = aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange; + range += range*song_bonus /100.0f; + } + + range = CastToClient()->GetActSpellRange(spell_id, range); + } + + return(range); +} + +/////////////////////////////////////////////////////////////////////////////// +// 'other' functions + +void Mob::Spin() { + if(IsClient()) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Action, sizeof(Action_Struct)); + outapp->pBuffer[0] = 0x0B; + outapp->pBuffer[1] = 0x0A; + outapp->pBuffer[2] = 0x0B; + outapp->pBuffer[3] = 0x0A; + outapp->pBuffer[4] = 0xE7; + outapp->pBuffer[5] = 0x00; + outapp->pBuffer[6] = 0x4D; + outapp->pBuffer[7] = 0x04; + outapp->pBuffer[8] = 0x00; + outapp->pBuffer[9] = 0x00; + outapp->pBuffer[10] = 0x00; + outapp->pBuffer[11] = 0x00; + outapp->pBuffer[12] = 0x00; + outapp->pBuffer[13] = 0x00; + outapp->pBuffer[14] = 0x00; + outapp->pBuffer[15] = 0x00; + outapp->pBuffer[16] = 0x00; + outapp->pBuffer[17] = 0x00; + outapp->pBuffer[18] = 0xD4; + outapp->pBuffer[19] = 0x43; + outapp->pBuffer[20] = 0x00; + outapp->pBuffer[21] = 0x00; + outapp->pBuffer[22] = 0x00; + outapp->priority = 5; + CastToClient()->QueuePacket(outapp); + safe_delete(outapp); + } + else { + GMMove(GetX(), GetY(), GetZ(), GetHeading()+5); + } +} + +void Mob::SendSpellBarDisable() +{ + if (!IsClient()) + return; + + CastToClient()->MemorizeSpell(0, SPELLBAR_UNLOCK, memSpellSpellbar); +} + +// this puts the spell bar back into a usable state fast +void Mob::SendSpellBarEnable(uint16 spell_id) +{ + if(!IsClient()) + return; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ManaChange, sizeof(ManaChange_Struct)); + ManaChange_Struct* manachange = (ManaChange_Struct*)outapp->pBuffer; + manachange->new_mana = GetMana(); + manachange->spell_id = spell_id; + manachange->stamina = CastToClient()->GetEndurance(); + outapp->priority = 6; + CastToClient()->QueuePacket(outapp); + safe_delete(outapp); +} + +void Mob::Stun(int duration) +{ + //make sure a shorter stun does not overwrite a longer one. + if(stunned && stunned_timer.GetRemainingTime() > uint32(duration)) + return; + + if(casting_spell_id) { + int persistent_casting = spellbonuses.PersistantCasting + itembonuses.PersistantCasting; + if(IsClient()) + persistent_casting += aabonuses.PersistantCasting; + + if(MakeRandomInt(1,99) > persistent_casting) + InterruptSpell(); + } + + if(duration > 0) + { + stunned = true; + stunned_timer.Start(duration); + } +} + +void Mob::UnStun() { + if(stunned && stunned_timer.Enabled()) { + stunned = false; + stunned_timer.Disable(); + } +} + +// Stuns "this" +void Client::Stun(int duration) +{ + Mob::Stun(duration); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Stun, sizeof(Stun_Struct)); + Stun_Struct* stunon = (Stun_Struct*) outapp->pBuffer; + stunon->duration = duration; + outapp->priority = 5; + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::UnStun() { + Mob::UnStun(); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Stun, sizeof(Stun_Struct)); + Stun_Struct* stunon = (Stun_Struct*) outapp->pBuffer; + stunon->duration = 0; + outapp->priority = 5; + QueuePacket(outapp); + safe_delete(outapp); +} + +void NPC::Stun(int duration) { + Mob::Stun(duration); + SetRunAnimSpeed(0); + SendPosition(); +} + +void NPC::UnStun() { + Mob::UnStun(); + SetRunAnimSpeed(this->GetRunspeed()); + SendPosition(); +} + +void Mob::Mesmerize() +{ + mezzed = true; + + if (casting_spell_id) + InterruptSpell(); + + SendPosition(); +/* this stuns the client for max time, with no way to break it + if (this->IsClient()){ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Stun, sizeof(Stun_Struct)); + Stun_Struct* stunon = (Stun_Struct*) outapp->pBuffer; + stunon->duration = 0xFFFF; + this->CastToClient()->QueuePacket(outapp); + safe_delete(outapp); + } else { + SetRunAnimSpeed(0); + } +*/ +} + +void Client::MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message) +{ + EQApplicationPacket* outapp; + + outapp = new EQApplicationPacket(OP_Buff, sizeof(SpellBuffFade_Struct)); + SpellBuffFade_Struct* sbf = (SpellBuffFade_Struct*) outapp->pBuffer; + + sbf->entityid=GetID(); + // i dont know why but this works.. for now + sbf->slot=2; +// sbf->slot=m_pp.buffs[slot_id].slotid; +// sbf->level=m_pp.buffs[slot_id].level; +// sbf->effect=m_pp.buffs[slot_id].effect; + sbf->spellid=spell_id; + sbf->slotid=slot_id; + sbf->bufffade = 1; +#if EQDEBUG >= 11 + printf("Sending SBF 1 from server:\n"); + DumpPacket(outapp); +#endif + QueuePacket(outapp); + +/* + sbf->effect=0; + sbf->level=0; + sbf->slot=0; +*/ + sbf->spellid=0xffffffff; +#if EQDEBUG >= 11 + printf("Sending SBF 2 from server:\n"); + DumpPacket(outapp); +#endif + QueuePacket(outapp); + safe_delete(outapp); + + 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; + bfm->color = MT_Spells; + memcpy(bfm->msg, fadetext, strlen(fadetext)); + QueuePacket(outapp); + safe_delete(outapp); + } + +} + +void Client::MemSpell(uint16 spell_id, int slot, bool update_client) +{ + if(slot >= MAX_PP_MEMSPELL || slot < 0) + return; + + if(update_client) + { + if(m_pp.mem_spells[slot] != 0xFFFFFFFF) + UnmemSpell(slot, update_client); + } + + m_pp.mem_spells[slot] = spell_id; + mlog(CLIENT__SPELLS, "Spell %d memorized into slot %d", spell_id, slot); + + if(update_client) + { + MemorizeSpell(slot, spell_id, memSpellMemorize); + } +} + +void Client::UnmemSpell(int slot, bool update_client) +{ + if(slot > MAX_PP_MEMSPELL || slot < 0) + return; + + mlog(CLIENT__SPELLS, "Spell %d forgotten from slot %d", m_pp.mem_spells[slot], slot); + m_pp.mem_spells[slot] = 0xFFFFFFFF; + + if(update_client) + { + MemorizeSpell(slot, m_pp.mem_spells[slot], memSpellForget); + } +} + +void Client::UnmemSpellAll(bool update_client) +{ + int i; + + for(i = 0; i < MAX_PP_MEMSPELL; i++) + if(m_pp.mem_spells[i] != 0xFFFFFFFF) + UnmemSpell(i, update_client); +} + +void Client::ScribeSpell(uint16 spell_id, int slot, bool update_client) +{ + if(slot >= MAX_PP_SPELLBOOK || slot < 0) + return; + + if(update_client) + { + if(m_pp.spell_book[slot] != 0xFFFFFFFF) + UnscribeSpell(slot, update_client); + } + + m_pp.spell_book[slot] = spell_id; + mlog(CLIENT__SPELLS, "Spell %d scribed into spell book slot %d", spell_id, slot); + + if(update_client) + { + MemorizeSpell(slot, spell_id, memSpellScribing); + } +} + +void Client::UnscribeSpell(int slot, bool update_client) +{ + if(slot >= MAX_PP_SPELLBOOK || slot < 0) + return; + + mlog(CLIENT__SPELLS, "Spell %d erased from spell book slot %d", m_pp.spell_book[slot], slot); + m_pp.spell_book[slot] = 0xFFFFFFFF; + + if(update_client) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_DeleteSpell, sizeof(DeleteSpell_Struct)); + DeleteSpell_Struct* del = (DeleteSpell_Struct*)outapp->pBuffer; + del->spell_slot = slot; + del->success = 1; + QueuePacket(outapp); + safe_delete(outapp); + } +} + +void Client::UnscribeSpellAll(bool update_client) +{ + int i; + + for(i = 0; i < MAX_PP_SPELLBOOK; i++) + { + if(m_pp.spell_book[i] != 0xFFFFFFFF) + UnscribeSpell(i, update_client); + } +} + +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); + m_pp.disciplines.values[slot] = 0; + + if(update_client) + { + SendDisciplineUpdate(); + } +} + +void Client::UntrainDiscAll(bool update_client) +{ + int i; + + for(i = 0; i < MAX_PP_DISCIPLINES; i++) + { + if(m_pp.disciplines.values[i] != 0) + UntrainDisc(i, update_client); + } +} + +int Client::GetNextAvailableSpellBookSlot(int starting_slot) { + for (int i = starting_slot; i < MAX_PP_SPELLBOOK; i++) { //using starting_slot should help speed this up when we're iterating through a bunch of spells + if (!IsValidSpell(GetSpellByBookSlot(i))) + return i; + } + + return -1; //default +} + +int Client::FindSpellBookSlotBySpellID(uint16 spellid) { + for(int i = 0; i < MAX_PP_SPELLBOOK; i++) { + if(m_pp.spell_book[i] == spellid) + return i; + } + + return -1; //default +} + +bool Client::SpellGlobalCheck(uint16 Spell_ID, uint16 Char_ID) { + + 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); + return false; // Query failed, so prevent spell from scribing just in case + } + return false; // Default is false +} + +//this is one nasty function... FindType and FindSpell are rather complex operations... +/*void Mob::CheckBuffs() { + if (!IsCasting()) { + + //try to summon a pet if we havent yet + CheckPet(); + + uint8 newtype[15] = { SE_ArmorClass, SE_STR, SE_DEX, SE_AGI, SE_WIS, + SE_INT, SE_CHA, SE_AttackSpeed, SE_MovementSpeed, + SE_DamageShield, SE_ResistFire, SE_ResistCold, + SE_ResistMagic, SE_ResistPoison, SE_ResistDisease }; + for (int h=0; h<15; h++) { + if (!this->FindType(newtype[h])) { + uint16 buffid = FindSpell(this->class_, this->level, + newtype[h], SPELLTYPE_SELF, 0, + GetMana()); + if (buffid != 0) { + this->CastSpell(buffid, this->GetID()); + } + } + } + } +} + +void Mob::CheckPet() { + if(HasPet()) + return; + uint16 buffid = 0; + if ((GetClass() == NECROMANCER || GetClass() == MAGICIAN)) { + if (this->GetClass() == MAGICIAN) { + buffid = FindSpell(class_, level, + SE_SummonPet, SPELLTYPE_OTHER, 0, + GetMana()); + } else if (GetClass() == NECROMANCER) { + buffid = FindSpell(class_, level, + SE_NecPet, SPELLTYPE_OTHER, 0, + GetMana()); + } + if (buffid != 0) { + CastSpell(buffid, GetID()); + } + } +} + +uint16 Mob::FindSpell(uint16 classp, uint16 level, int type, + FindSpellType spelltype, float distance, + int32 mana_avail) { + int i,j; + + int bestvalue = -1; + int bestid = 0; + + if (classp < 1) + return 0; + if (level < 1) + return 0; + classp = GetEQArrayEQClass(classp); + + // purpose: find a suited spell for a class and level and type + // the if's are here to filter out anything which isnt normal. + // its possible that we miss some valid spells, but who cares. + + for (i = 0; i < SPDAT_RECORDS; i++) { + if(!IsValidSpell(i)) + continue; + // Filter all spells that should never be used + if (spells[i].effectid[0] == SE_NegateIfCombat) + continue; + if (spells[i].targettype == ST_Group) + continue; + if (i == 2632) // fix for obsolete BST pet summon spell + continue; + if (i == 1576) // fix for torpor + continue; + if (spells[i].cast_time < 11) + continue; + if (spells[i].mana == 0) + continue; + + // now for closer checks + if (spelltype == SPELLTYPE_SELF) { + if ( i == 357) // fix for dark empathy + continue; + // check buffs 12 would be max, but 90% of all effects are in the first 4 slots + for (j = 0; j < 5; j++) { + // fix for pets + if ( spells[i].effectid[j] == SE_Illusion && + type != SE_Illusion) // only let illusions thru if explicitly requested + continue; + if ( spells[i].effectid[j] == type && + spells[i].goodEffect != 0 && + spells[i].classes[classp] <= level && + spells[i].classes[classp] <= 65 && + (spells[i].recast_time < 10000 || + type == SE_SummonPet || + type == SE_SummonBSTPet) && // fix for druid pets + (type == SE_AbsorbMagicAtt || type == SE_Rune || + type == SE_NecPet || type == SE_SummonPet || + spells[i].components[0] == -1 ) && + spells[i].targettype != ST_Undead && // for necro mend series + spells[i].targettype != ST_Group && // fix for group spells + spells[i].targettype != ST_Pet && // fix for beastlords casting pet heals on self + spells[i].targettype != ST_Summoned && // fix for vs. summoned spells on normal npcs + spells[i].targettype != ST_AETarget && // dont let em cast AEtarget spells + spells[i].mana <= mana_avail && + spells[i].range >= distance) { + int32 spellvalue; + + // lets assume pet is always better if higher, so no formula needed + if (type == SE_NecPet || + type == SE_SummonPet || + type == SE_SummonBSTPet) { + spellvalue = spells[i].classes[classp]; + } else { + spellvalue = CalcSpellEffectValue_formula(spells[i].formula[j], + spells[i].base[j], + spells[i].max[j], + level, i); + } + + if (abs(spellvalue) > bestvalue) { + bestvalue = abs(spellvalue); + bestid = i; + } + } + } + } else if (spelltype == SPELLTYPE_OFFENSIVE) { + // check offensive spells + for (j = 0; j < 5; j++) { + if (spells[i].effectid[j] == SE_Illusion && + type != SE_Illusion) // only let illusions thru if explicitly requested + continue; + if (spells[i].effectid[j] == type && + spells[i].goodEffect == 0 && + spells[i].classes[classp] <= level && + spells[i].classes[classp] <= 65 && + spells[i].recast_time < 10000 && + spells[i].components[0] == -1 && + spells[i].mana <= mana_avail && + spells[i].targettype != ST_Undead && // thats for the necro mend series + spells[i].targettype != ST_Group && // fix for group spells + spells[i].targettype != ST_Pet && // fix for beastlords casting pet heals on self + spells[i].targettype != ST_Summoned && // fix for vs. summoned spells on normal npcs + spells[i].targettype != ST_AETarget && // dont let em cast AEtarget spells + spells[i].range >= distance) { + int32 spellvalue = CalcSpellEffectValue_formula(spells[i].formula[j], + spells[i].base[j], + spells[i].max[j], + level, i); + if ( abs(spellvalue) > bestvalue ) { + bestvalue = abs(spellvalue); + bestid = i; + } + } + } + } else if (spelltype == SPELLTYPE_OTHER) { + if ( i == 357) // fix for dark empathy + continue; + // healing and such + for (j = 0; j < 5; j++) { + if (spells[i].effectid[j] == SE_Illusion && + type != SE_Illusion) // only let illusions thru if explicitly requested + continue; + if (spells[i].effectid[j] == type && + spells[i].targettype != ST_Self && + spells[i].goodEffect != 0 && + spells[i].classes[classp] <= level && + spells[i].classes[classp] <= 65 && + spells[i].recast_time < 10000 && + spells[i].components[0] == -1 && + spells[i].targettype != ST_Undead && // thats for the necro mend series + spells[i].targettype != ST_Group && // fix for group spells + spells[i].targettype != ST_Pet && // fix for beastlords casting pet heals on self + spells[i].targettype != ST_Summoned && // fix for vs. summoned spells on normal npcs + spells[i].targettype != ST_AETarget && // dont let em cast AEtarget spells + spells[i].mana <= mana_avail && + spells[i].range >= distance) { + int32 spellvalue = CalcSpellEffectValue_formula(spells[i].formula[j], + spells[i].base[j], + spells[i].max[j], + level, i); + if ( abs(spellvalue) > bestvalue ) { + bestvalue = abs(spellvalue); + bestid = i; + } + } + } + } + } // for i + +// g_LogFile.write("for combination [class %02d][level %02d][SE_type %02d][type %02d] i selected the spell: %s", +// classp, level, (uint16)type, uint16(spelltype), spells[bestid].name); + return bestid; +} + +#if 0 +uint16 Mob::FindSpell(uint16 classp, uint16 level, uint8 type, uint8 spelltype) { + if (this->casting_spell_id != 0) + return 0; + + if (spelltype == 2) // for future use + spelltype = 0; + + //int count=0; + uint16 bestsofar = 0; + uint16 bestspellid = 0; + for (int i = 0; i < SPDAT_RECORDS; i++) { + if ((IsLifetapSpell(i) && spelltype == 1) || (spells[i].targettype != ST_Group && spells[i].targettype != ST_Undead && spells[i].targettype != ST_Summoned && spells[i].targettype != ST_Pet && strstr(spells[i].name,"Summoning") == NULL)) { + int Canuse = CanUseSpell(i, classp, level); + if (Canuse != 0) { + for (int z=0; z < 12; z++) { + int spfo = CalcSpellValue(spells[i].formula[z], spells[i].base[z], spells[i].max[z], this->GetLevel()); + if (spells[i].effectid[z] == SE_ArmorClass && type == SE_ArmorClass && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + if (spells[i].effectid[z] == SE_TotalHP && type == SE_TotalHP && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + if (spells[i].effectid[z] == SE_STR && type == SE_STR && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + + bestspellid = i; + } + } + if (spells[i].effectid[z] == SE_DEX && type == SE_DEX && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + + if (spells[i].effectid[z] == SE_AGI && type == SE_AGI && !FindBuff(i)) { + + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + + if (spells[i].effectid[z] == SE_WIS && type == SE_WIS && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + + if (spells[i].effectid[z] == SE_INT && type == SE_INT && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + if (spells[i].effectid[z] == SE_CHA && type == SE_CHA && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + + if (spells[i].effectid[z] == SE_MovementSpeed && type == SE_MovementSpeed && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + + if (spells[i].effectid[z] == SE_AttackSpeed && type == SE_AttackSpeed && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + if (spells[i].effectid[z] == SE_ResistFire && type == SE_ResistFire && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + if (spells[i].effectid[z] == SE_ResistCold && type == SE_ResistCold && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + if (spells[i].effectid[z] == SE_ResistMagic && type == SE_ResistMagic && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + if (spells[i].effectid[z] == SE_ResistDisease && type == SE_ResistDisease && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + + } + } + if (spells[i].effectid[z] == SE_ResistPoison && type == SE_ResistPoison && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + if (spells[i].effectid[z] == SE_DamageShield && type == SE_DamageShield && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + if (spells[i].effectid[z] == SE_CurrentHPOnce && type == SE_CurrentHPOnce && !FindBuff(i)) { + if (spfo > 0 && (spfo + spells[i].buffduration) > bestsofar) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + } + if (spells[i].effectid[z] == SE_SummonPet && type == SE_SummonPet && !FindBuff(i)) { + if (Canuse > bestsofar) { + bestsofar = Canuse; + bestspellid = i; + } + } + if (spells[i].effectid[z] == SE_NecPet && type == SE_NecPet && !FindBuff(i)) { + if (Canuse > bestsofar) { + bestsofar = Canuse; + bestspellid = i; + } + } + if (spells[i].effectid[z] == SE_CurrentHP && type == SE_CurrentHP && !FindBuff(i)) { + if (spfo < 0 && (spells[i].buffduration + spfo) < bestsofar && spelltype == 1) { + bestsofar = ((spells[i].buffduration * -1) + spfo); + bestspellid = i; + } + if ((spfo + spells[i].buffduration) > bestsofar && spfo > 0 && spelltype == 0) { + bestsofar = spfo + spells[i].buffduration; + bestspellid = i; + } + + } + } + } + } + } + + return bestspellid; +} +#endif +*/ + +// TODO get rid of this +int16 Mob::GetBuffSlotFromType(uint16 type) { + uint32 buff_count = GetMaxTotalSlots(); + for (int i = 0; i < buff_count; i++) { + if (buffs[i].spellid != SPELL_UNKNOWN) { + for (int j = 0; j < EFFECT_COUNT; j++) { + if (spells[buffs[i].spellid].effectid[j] == type ) + return i; + } + } + } + return -1; +} + +uint16 Mob::GetSpellIDFromSlot(uint8 slot) +{ + if (buffs[slot].spellid != SPELL_UNKNOWN) + return buffs[slot].spellid; + return 0; +} + + +bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) { + uint32 buff_count = GetMaxTotalSlots(); + for (int i = 0; i < buff_count; i++) { + if (buffs[i].spellid != SPELL_UNKNOWN) { + + for (int j = 0; j < EFFECT_COUNT; j++) { + // adjustments necessary for offensive npc casting behavior + if (bOffensive) { + if (spells[buffs[i].spellid].effectid[j] == type) { + int16 value = + CalcSpellEffectValue_formula(spells[buffs[i].spellid].buffdurationformula, + spells[buffs[i].spellid].base[j], + spells[buffs[i].spellid].max[j], + buffs[i].casterlevel, buffs[i].spellid); + LogFile->write(EQEMuLog::Normal, + "FindType: type = %d; value = %d; threshold = %d", + type, value, threshold); + if (value < threshold) + return true; + } + } else { + if (spells[buffs[i].spellid].effectid[j] == type ) + return true; + } + } + } + } + return false; +} + +bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance) { + if(spell_id == SPELL_UNKNOWN) + return(false); + + int i; + if (bPerma) { + for (i = 0; i < MAX_PROCS; i++) { + if (PermaProcs[i].spellID == SPELL_UNKNOWN) { + PermaProcs[i].spellID = spell_id; + PermaProcs[i].chance = iChance; + PermaProcs[i].base_spellID = SPELL_UNKNOWN; + mlog(SPELLS__PROCS, "Added permanent proc spell %d with chance %d to slot %d", spell_id, iChance, i); + + return true; + } + } + mlog(SPELLS__PROCS, "Too many perma procs for %s", GetName()); + } else { + for (i = 0; i < MAX_PROCS; i++) { + if (SpellProcs[i].spellID == SPELL_UNKNOWN) { + SpellProcs[i].spellID = spell_id; + SpellProcs[i].chance = iChance; + SpellProcs[i].base_spellID = SPELL_UNKNOWN;; + mlog(SPELLS__PROCS, "Added spell-granted proc spell %d with chance %d to slot %d", spell_id, iChance, i); + return true; + } + } + mlog(SPELLS__PROCS, "Too many procs for %s", GetName()); + } + return false; +} + +bool Mob::RemoveProcFromWeapon(uint16 spell_id, bool bAll) { + for (int i = 0; i < MAX_PROCS; i++) { + if (bAll || SpellProcs[i].spellID == spell_id) { + SpellProcs[i].spellID = SPELL_UNKNOWN; + SpellProcs[i].chance = 0; + SpellProcs[i].base_spellID = SPELL_UNKNOWN; + mlog(SPELLS__PROCS, "Removed proc %d from slot %d", spell_id, i); + } + } + return true; +} + +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) { + DefensiveProcs[i].spellID = spell_id; + DefensiveProcs[i].chance = iChance; + DefensiveProcs[i].base_spellID = base_spell_id; + mlog(SPELLS__PROCS, "Added spell-granted defensive proc spell %d with chance %d to slot %d", spell_id, iChance, i); + return true; + } + } + + return false; +} + +bool Mob::RemoveDefensiveProc(uint16 spell_id, bool bAll) +{ + for (int i = 0; i < MAX_PROCS; i++) { + if (bAll || DefensiveProcs[i].spellID == spell_id) { + DefensiveProcs[i].spellID = SPELL_UNKNOWN; + DefensiveProcs[i].chance = 0; + DefensiveProcs[i].base_spellID = SPELL_UNKNOWN; + mlog(SPELLS__PROCS, "Removed defensive proc %d from slot %d", spell_id, i); + } + } + return true; +} + +bool Mob::AddSkillProc(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 (SkillProcs[i].spellID == SPELL_UNKNOWN) { + SkillProcs[i].spellID = spell_id; + SkillProcs[i].chance = iChance; + SkillProcs[i].base_spellID = base_spell_id; + mlog(SPELLS__PROCS, "Added spell-granted skill proc spell %d with chance %d to slot %d", spell_id, iChance, i); + return true; + } + } + return false; +} + +bool Mob::RemoveSkillProc(uint16 spell_id, bool bAll) +{ + for (int i = 0; i < MAX_PROCS; i++) { + if (bAll || SkillProcs[i].spellID == spell_id) { + SkillProcs[i].spellID = SPELL_UNKNOWN; + SkillProcs[i].chance = 0; + SkillProcs[i].base_spellID = SPELL_UNKNOWN; + mlog(SPELLS__PROCS, "Removed Skill proc %d from slot %d", spell_id, i); + } + } + return true; +} + +bool Mob::AddRangedProc(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 (RangedProcs[i].spellID == SPELL_UNKNOWN) { + RangedProcs[i].spellID = spell_id; + RangedProcs[i].chance = iChance; + RangedProcs[i].base_spellID = base_spell_id; + mlog(SPELLS__PROCS, "Added spell-granted ranged proc spell %d with chance %d to slot %d", spell_id, iChance, i); + return true; + } + } + + return false; +} + +bool Mob::RemoveRangedProc(uint16 spell_id, bool bAll) +{ + for (int i = 0; i < MAX_PROCS; i++) { + if (bAll || RangedProcs[i].spellID == spell_id) { + RangedProcs[i].spellID = SPELL_UNKNOWN; + RangedProcs[i].chance = 0; + RangedProcs[i].base_spellID = SPELL_UNKNOWN;; + mlog(SPELLS__PROCS, "Removed ranged proc %d from slot %d", spell_id, i); + } + } + return true; +} + +// this is checked in a few places to decide wether special bard +// behavior should be used. +bool Mob::UseBardSpellLogic(uint16 spell_id, int slot) +{ + if(spell_id == SPELL_UNKNOWN) + spell_id = casting_spell_id; + + if(slot == -1) + slot = casting_spell_slot; + + // should we treat this as a bard singing? + return + ( + spell_id != 0 && + spell_id != SPELL_UNKNOWN && + slot != -1 && + GetClass() == BARD && + slot <= MAX_PP_MEMSPELL && + IsBardSong(spell_id) + ); +} + +int Mob::GetCasterLevel(uint16 spell_id) { + int level = GetLevel(); + level += itembonuses.effective_casting_level + spellbonuses.effective_casting_level + aabonuses.effective_casting_level; + mlog(SPELLS__CASTING, "Determined effective casting level %d+%d+%d=%d", GetLevel(), spellbonuses.effective_casting_level, itembonuses.effective_casting_level, level); + return(level); +} + + +//this method does NOT tell the client to stop singing the song. +//this is NOT the right way to stop a mob from singing, use InterruptSpell +//you should really know what your doing before you call this +void Mob::_StopSong() +{ + bardsong = 0; + bardsong_target_id = 0; + bardsong_slot = 0; + bardsong_timer.Disable(); +} + +//This member function sets the buff duration on the client +//however it does not work if sent quickly after an action packets, which is what one might perfer to do +//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) +{ + 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->bufffade = 0; + sbf->duration = duration; + FastQueuePacket(&outapp); +} + +void Mob::SendPetBuffsToClient() +{ + // Don't really need this check, as it should be checked before this method is called, but it doesn't hurt + // too much to check again. + if(!(GetOwner() && GetOwner()->IsClient())) + return; + + int PetBuffCount = 0; + + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PetBuffWindow,sizeof(PetBuff_Struct)); + PetBuff_Struct* pbs=(PetBuff_Struct*)outapp->pBuffer; + memset(outapp->pBuffer,0,outapp->size); + pbs->petid=GetID(); + + int MaxSlots = GetMaxTotalSlots(); + + if(MaxSlots > BUFF_COUNT) + MaxSlots = BUFF_COUNT; + + for(int buffslot = 0; buffslot < MaxSlots; buffslot++) + { + if(buffs[buffslot].spellid != SPELL_UNKNOWN) { + pbs->spellid[buffslot] = buffs[buffslot].spellid; + pbs->ticsremaining[buffslot] = buffs[buffslot].ticsremaining; + PetBuffCount++; + } + } + + pbs->buffcount=PetBuffCount; + GetOwner()->CastToClient()->QueuePacket(outapp); + safe_delete(outapp); +} + +void Mob::SendBuffsToClient(Client *c) +{ + if(!c) + return; + + if(c->GetClientVersionBit() & BIT_SoDAndLater) + { + EQApplicationPacket *outapp = MakeBuffsPacket(); + c->FastQueuePacket(&outapp); + } +} + +EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target) +{ + uint32 count = 0; + uint32 buff_count = GetMaxTotalSlots(); + for(unsigned int i = 0; i < buff_count; ++i) + { + if(buffs[i].spellid != SPELL_UNKNOWN) + { + ++count; + } + } + + EQApplicationPacket* outapp = NULL; + + //Create it for a targeting window, else create it for a create buff packet. + if(for_target) + { + outapp = new EQApplicationPacket(OP_TargetBuffs, sizeof(BuffIcon_Struct) + sizeof(BuffIconEntry_Struct) * count); + } + else + { + outapp = new EQApplicationPacket(OP_BuffCreate, sizeof(BuffIcon_Struct) + sizeof(BuffIconEntry_Struct) * count); + } + BuffIcon_Struct *buff = (BuffIcon_Struct*)outapp->pBuffer; + buff->entity_id = GetID(); + buff->count = count; + + uint32 index = 0; + for(unsigned int i = 0; i < buff_count; ++i) + { + if(buffs[i].spellid != SPELL_UNKNOWN) + { + buff->entries[index].buff_slot = i; + buff->entries[index].spell_id = buffs[i].spellid; + buff->entries[index].tics_remaining = buffs[i].ticsremaining; + ++index; + } + } + + return outapp; +} + + + +void Mob::BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration) +{ + uint32 buff_count = GetMaxTotalSlots(); + for(int i = 0; i < buff_count; ++i) + { + if (buffs[i].spellid == spell_id) + { + buffs[i].ticsremaining = newDuration; + if(IsClient()) + { + CastToClient()->SendBuffDurationPacket(buffs[i].spellid, buffs[i].ticsremaining, buffs[i].casterlevel); + } + } + } +} +void Mob::UpdateRuneFlags() +{ + bool Has_SE_Rune = false, Has_SE_AbsorbMagicAtt = false, Has_SE_MitigateMeleeDamage = false, Has_SE_MitigateSpellDamage = false; + uint32 buff_count = GetMaxTotalSlots(); + for (unsigned int i = 0; i < buff_count; ++i) + { + if (buffs[i].spellid != SPELL_UNKNOWN) + { + for (int j = 0; j < EFFECT_COUNT; ++j) + { + switch(spells[buffs[i].spellid].effectid[j]) + { + case SE_Rune: + { + Has_SE_Rune = true; + break; + } + case SE_AbsorbMagicAtt: + { + Has_SE_AbsorbMagicAtt = true; + break; + } + case SE_MitigateMeleeDamage: + { + Has_SE_MitigateMeleeDamage = true; + break; + } + case SE_MitigateSpellDamage: + { + Has_SE_MitigateSpellDamage = true; + break; + } + + default: + break; + } + } + } + } + + SetHasRune(Has_SE_Rune); + SetHasSpellRune(Has_SE_AbsorbMagicAtt); + SetHasPartialMeleeRune(Has_SE_MitigateMeleeDamage); + SetHasPartialSpellRune(Has_SE_MitigateSpellDamage); +} + +int Client::GetCurrentBuffSlots() const +{ + if(15 + aabonuses.BuffSlotIncrease > 25) + return 25; + else + return 15 + aabonuses.BuffSlotIncrease; +} + +int Client::GetCurrentSongSlots() const +{ + return 12; // AAs dont affect this +} + +void Client::InitializeBuffSlots() +{ + int max_slots = GetMaxTotalSlots(); + buffs = new Buffs_Struct[max_slots]; + for(int x = 0; x < max_slots; ++x) + { + buffs[x].spellid = SPELL_UNKNOWN; + } + current_buff_count = 0; + buff_tic_timer = NULL; +} + +void Client::UninitializeBuffSlots() +{ + safe_delete_array(buffs); +} + +void NPC::InitializeBuffSlots() +{ + int max_slots = GetMaxTotalSlots(); + buffs = new Buffs_Struct[max_slots]; + for(int x = 0; x < max_slots; ++x) + { + buffs[x].spellid = SPELL_UNKNOWN; + } + current_buff_count = 0; + buff_tic_timer = NULL; +} + +void NPC::UninitializeBuffSlots() +{ + safe_delete_array(buffs); +} diff --git a/zone/tasks.cpp b/zone/tasks.cpp new file mode 100644 index 000000000..ad89aaa35 --- /dev/null +++ b/zone/tasks.cpp @@ -0,0 +1,3558 @@ + +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2008 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 "tasks.h" + +#include +#include + +#ifdef _WINDOWS +#define strcasecmp _stricmp +#endif + +#include "../common/MiscFunctions.h" +#include "../common/rulesys.h" +#include "masterentity.h" +#include "features.h" +#include "QuestParserCollection.h" + + +TaskManager::TaskManager() { + + for(int i=0; iActivityCount; j++) { + safe_delete_array(Tasks[i]->Activity[j].Text1); + safe_delete_array(Tasks[i]->Activity[j].Text2); + safe_delete_array(Tasks[i]->Activity[j].Text3); + } + safe_delete_array(Tasks[i]->Title); + safe_delete_array(Tasks[i]->Description); + safe_delete_array(Tasks[i]->Reward); + safe_delete(Tasks[i]); + } + } +} + +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); + return false; + } + + return true; + +} + +bool TaskManager::LoadSingleTask(int TaskID) { + + _log(TASKS__GLOBALLOAD, "TaskManager::LoadSingleTask(%i)", TaskID); + + if((TaskID <= 0) || (TaskID >= MAXTASKS)) return false; + + // If this task already exists in memory, free all the dynamically allocated strings. + if(Tasks[TaskID]) { + + for(int j=0; jActivityCount; j++) { + safe_delete_array(Tasks[TaskID]->Activity[j].Text1); + safe_delete_array(Tasks[TaskID]->Activity[j].Text2); + safe_delete_array(Tasks[TaskID]->Activity[j].Text3); + } + safe_delete_array(Tasks[TaskID]->Title); + safe_delete_array(Tasks[TaskID]->Description); + safe_delete_array(Tasks[TaskID]->Reward); + safe_delete(Tasks[TaskID]); + } + + return LoadTasks(TaskID); +} + +void TaskManager::ReloadGoalLists() { + + if(!GoalListManager.LoadLists()) + _log(TASKS__GLOBALLOAD,"TaskManager::LoadTasks LoadLists failed"); +} + +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) { + if(!GoalListManager.LoadLists()) + _log(TASKS__GLOBALLOAD,"TaskManager::LoadTasks LoadLists failed"); + + if(!LoadTaskSets()) + _log(TASKS__GLOBALLOAD,"TaskManager::LoadTasks LoadTaskSets failed"); + + QueryLength = MakeAnyLenString(&query,AllTaskQuery,MAXTASKS); + } + else + QueryLength = MakeAnyLenString(&query,SingleTaskQuery,SingleTask); + + if(database.RunQuery(query,QueryLength,errbuf,&result)) { + + 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); + return false; + } + + if(SingleTask==0) + QueryLength = MakeAnyLenString(&query,AllActivityQuery,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]==NULL) { + 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 NULL. 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] = NULL; + 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); + return false; + } + return true; +} + +bool TaskManager::SaveClientState(Client *c, ClientTaskState *state) { + + // I am saving the slot in the ActiveTasks table, because unless a Task is cancelled/completed, the client doesn't + // seem to like tasks moving slots between zoning and you can end up with 'bogus' activities if the task previously + // 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)"; + + const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::SaveClientState %s"; + + if(!c || !state) return false; + + int CharacterID = c->CharacterID(); + + _log(TASKS__CLIENTSAVE,"TaskManager::SaveClientState for character ID %d", CharacterID); + + + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + + if(state->ActiveTaskCount > 0) { + for(int Task=0; TaskActiveTasks[Task].TaskID; + if(TaskID==TASKSLOTEMPTY) continue; + if(state->ActiveTasks[Task].Updated) { + + _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientState for character ID %d, Updating TaskIndex %i TaskID %i", + CharacterID, Task, TaskID); + + if(!database.RunQuery(query,MakeAnyLenString(&query, TaskQuery, + CharacterID, + TaskID, + Task, + state->ActiveTasks[Task].AcceptedTime), errbuf)) { + + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); + } + else + state->ActiveTasks[Task].Updated = false; + + safe_delete_array(query); + } + + int UpdatedActivityCount = 0; + string UpdateActivityQuery = ActivityQuery; + char *buf = 0; + + for(int Activity=0; ActivityActivityCount; Activity++) { + + if(state->ActiveTasks[Task].Activity[Activity].Updated) { + + _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientSate for character ID %d, " + "Updating Activity %i, %i", + CharacterID, Task, Activity); + + 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) { + _log(TASKS__CLIENTSAVE, "Executing query %s", UpdateActivityQuery.c_str()); + if(!database.RunQuery(query,MakeAnyLenString(&query, UpdateActivityQuery.c_str()), + errbuf)) { + + 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; + + } + + 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]==NULL)) 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(); + } + + return true; +} + + +void Client::LoadClientTaskState() { + + if(RuleB(TaskSystem, EnableTaskSystem) && taskmanager) { + if(taskstate) + safe_delete(taskstate); + + taskstate = new ClientTaskState; + if(!taskmanager->LoadClientState(this, taskstate)) { + safe_delete(taskstate); + } + else { + taskmanager->SendActiveTasksToClient(this); + taskmanager->SendCompletedTasksToClient(this, taskstate); + } + } + +} + +void Client::RemoveClientTaskState() { + + if(taskstate) { + taskstate->CancelAllTasks(this); + safe_delete(taskstate); + } +} + +bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) { + + const char *TaskQuery = "SELECT `taskid`, `slot`, `acceptedtime` from `character_tasks` " + "WHERE `charid` = %i ORDER BY acceptedtime"; + + 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(); + + state->ActiveTaskCount = 0; + + _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientSate 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); + safe_delete(state); + return false; + } + + // Load Activities + + 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); + safe_delete(state); + return false; + } + + const char *CompletedTaskQuery = "SELECT `taskid`, `activityid`, `completedtime` from `completed_tasks` " + "WHERE `charid` = %i ORDER BY completedtime, taskid, activityid"; + + const char *ERR_TASK_OOR3 = "[TASKS]Task ID %i out of range while loading completed tasks from database"; + + const char *ERR_ACTIVITY_OOR2 = "[TASKS]Activity ID %i out of range while loading completed tasks from database"; + + const char *ERR_MYSQLERROR3 = "[TASKS]Error in TaskManager::LoadClientState load completed tasks: %s"; + + + + 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); + safe_delete(state); + return false; + } + } + + 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]) { + 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); + + LogFile->write(EQEMuLog::Error, ERR_NOTASK2, CharacterID, TaskID); + state->ActiveTasks[i].TaskID=TASKSLOTEMPTY; + continue; + + } + 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); + + LogFile->write(EQEMuLog::Error, ERR_INCONSISTENT, CharacterID, j, TaskID); + state->ActiveTasks[i].TaskID=TASKSLOTEMPTY; + break; + } + } + } + + + for(int i=0; iActiveTasks[i].TaskID != TASKSLOTEMPTY) + state->UnlockActivities(CharacterID, i); + + _log(TASKS__CLIENTLOAD, "LoadClientState for Character ID %d DONE!", CharacterID); + return true; +} + +void ClientTaskState::EnableTask(int CharID, int TaskCount, int *TaskList) { + + // Check if the Task is already enabled for this client + // + vector TasksEnabled; + vector::iterator Iterator; + + for(int i=0; i TaskList[i]) break; + Iterator++; + } + if(AddTask) { + EnabledTasks.insert(Iterator, TaskList[i]); + // Make a note of the task we enabled, for later SQL generation + TasksEnabled.push_back(TaskList[i]); + } + } + + _log(TASKS__UPDATE, "New enabled task list "); + 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) { + + // Check if the Task is enabled for this client + // + vector TasksDisabled; + vector::iterator Iterator; + + for(int i=0; i TaskList[i]) break; + Iterator++; + } + if(RemoveTask) { + EnabledTasks.erase(Iterator); + TasksDisabled.push_back(TaskList[i]); + } + } + + _log(TASKS__UPDATE, "New enabled task list "); + for(unsigned int i=0; iwrite(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); + } + + safe_delete_array(query); +} + +bool ClientTaskState::IsTaskEnabled(int TaskID) { + + vector::iterator Iterator; + + Iterator = EnabledTasks.begin(); + + while(Iterator != EnabledTasks.end()) { + if((*Iterator) == TaskID) return true; + if((*Iterator) > TaskID) break; + Iterator++; + } + + return false; +} + +int ClientTaskState::EnabledTaskCount(int TaskSetID) { + + // Return the number of tasks in TaskSet that this character is enabled for. + + unsigned int EnabledTaskIndex = 0; + unsigned int TaskSetIndex = 0; + int EnabledTaskCount = 0; + + if((TaskSetID<=0) || (TaskSetID>=MAXTASKSETS)) return -1; + + while((EnabledTaskIndex < EnabledTasks.size()) && (TaskSetIndex < taskmanager->TaskSets[TaskSetID].size())) { + + if(EnabledTasks[EnabledTaskIndex] == taskmanager->TaskSets[TaskSetID][TaskSetIndex]) { + + EnabledTaskCount++; + EnabledTaskIndex++; + TaskSetIndex++; + continue; + } + + if(EnabledTasks[EnabledTaskIndex] < taskmanager->TaskSets[TaskSetID][TaskSetIndex]) + EnabledTaskIndex++; + else + TaskSetIndex++; + + } + + return EnabledTaskCount; +} +int ClientTaskState::ActiveTasksInSet(int TaskSetID) { + + + if((TaskSetID<=0) || (TaskSetID>=MAXTASKSETS)) return -1; + + int Count = 0; + + for(unsigned int i=0; iTaskSets[TaskSetID].size(); i++) + if(IsTaskActive(taskmanager->TaskSets[TaskSetID][i])) + Count++; + + return Count; +} + +int ClientTaskState::CompletedTasksInSet(int TaskSetID) { + + + if((TaskSetID<=0) || (TaskSetID>=MAXTASKSETS)) return -1; + + int Count = 0; + + for(unsigned int i=0; iTaskSets[TaskSetID].size(); i++) + if(IsTaskCompleted(taskmanager->TaskSets[TaskSetID][i])) + Count++; + + return Count; +} + +int TaskManager::FirstTaskInSet(int TaskSetID) { + + if((TaskSetID<=0) || (TaskSetID>=MAXTASKSETS)) return 0; + + if(TaskSets[TaskSetID].size() == 0) return 0; + + vector::iterator Iterator = TaskSets[TaskSetID].begin(); + + while(Iterator != TaskSets[TaskSetID].end()) { + if((*Iterator) > 0) + return (*Iterator); + Iterator++; + } + + return 0; +} + +int TaskManager::LastTaskInSet(int TaskSetID) { + + if((TaskSetID<=0) || (TaskSetID>=MAXTASKSETS)) return 0; + + if(TaskSets[TaskSetID].size() == 0) return 0; + + return TaskSets[TaskSetID][TaskSets[TaskSetID].size()-1]; +} + +int TaskManager::NextTaskInSet(int TaskSetID, int TaskID) { + + if((TaskSetID<=0) || (TaskSetID>=MAXTASKSETS)) return 0; + + if(TaskSets[TaskSetID].size() == 0) return 0; + + for(unsigned int i=0; i TaskID) return TaskSets[TaskSetID][i]; + } + + return 0; +} + +bool TaskManager::AppropriateLevel(int TaskID, int PlayerLevel) { + + if(Tasks[TaskID] == NULL) return false; + + if(Tasks[TaskID]->MinLevel && (PlayerLevel < Tasks[TaskID]->MinLevel)) return false; + + if(Tasks[TaskID]->MaxLevel && (PlayerLevel > Tasks[TaskID]->MaxLevel)) return false; + + return true; + +} + +void TaskManager::TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, int TaskSetID) { + + unsigned int EnabledTaskIndex = 0; + unsigned int TaskSetIndex = 0; + int TaskList[MAXCHOOSERENTRIES]; + int TaskListIndex = 0; + int PlayerLevel = c->GetLevel(); + + _log(TASKS__UPDATE, "TaskSetSelector called for taskset %i. EnableTaskSize is %i", TaskSetID, + state->EnabledTasks.size()); + if((TaskSetID<=0) || (TaskSetID>=MAXTASKSETS)) return; + + if(TaskSets[TaskSetID].size() > 0) { + + // A TaskID of 0 in a TaskSet indicates that all Tasks in the set are enabled for all players. + + if(TaskSets[TaskSetID][0] == 0) { + + _log(TASKS__UPDATE, "TaskSets[%i][0] == 0. All Tasks in Set enabled.", TaskSetID); + vector::iterator Iterator = TaskSets[TaskSetID].begin(); + + while((Iterator != TaskSets[TaskSetID].end()) && (TaskListIndex < MAXCHOOSERENTRIES)) { + if(AppropriateLevel((*Iterator), PlayerLevel) && !state->IsTaskActive((*Iterator)) && + (IsTaskRepeatable((*Iterator)) || !state->IsTaskCompleted((*Iterator)))) + TaskList[TaskListIndex++] = (*Iterator); + + Iterator++; + } + if(TaskListIndex > 0) + { + SendTaskSelector(c, mob, TaskListIndex, TaskList); + } + + return; + } + } + + + while((EnabledTaskIndex < state->EnabledTasks.size()) && (TaskSetIndex < TaskSets[TaskSetID].size()) && + (TaskListIndex < MAXCHOOSERENTRIES)) { + + _log(TASKS__UPDATE, "Comparing EnabledTasks[%i] (%i) with TaskSets[%i][%i] (%i)", + EnabledTaskIndex, state->EnabledTasks[EnabledTaskIndex], TaskSetID, TaskSetIndex, + TaskSets[TaskSetID][TaskSetIndex]); + + if((TaskSets[TaskSetID][TaskSetIndex] > 0) && + (state->EnabledTasks[EnabledTaskIndex] == TaskSets[TaskSetID][TaskSetIndex])) { + + if(AppropriateLevel(TaskSets[TaskSetID][TaskSetIndex], PlayerLevel) && + !state->IsTaskActive(TaskSets[TaskSetID][TaskSetIndex]) && + (IsTaskRepeatable(TaskSets[TaskSetID][TaskSetIndex]) || + !state->IsTaskCompleted(TaskSets[TaskSetID][TaskSetIndex]))) { + + TaskList[TaskListIndex++] = TaskSets[TaskSetID][TaskSetIndex]; + + EnabledTaskIndex++; + TaskSetIndex++; + continue; + } + } + + if(state->EnabledTasks[EnabledTaskIndex] < TaskSets[TaskSetID][TaskSetIndex]) + EnabledTaskIndex++; + else + TaskSetIndex++; + } + + if(TaskListIndex == 0) return; + + SendTaskSelector(c, mob, TaskListIndex, TaskList); + +} + +void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *TaskList) { + + if (c->GetClientVersion() >= EQClientRoF) + { + SendTaskSelectorNew(c, mob, TaskCount, TaskList); + return; + } + // Titanium OpCode: 0x5e7c + _log(TASKS__UPDATE, "TaskSelector for %i Tasks", TaskCount); + char *Ptr; + int PlayerLevel = c->GetLevel(); + + AvailableTaskHeader_Struct* AvailableTaskHeader; + AvailableTaskData1_Struct* AvailableTaskData1; + AvailableTaskData2_Struct* AvailableTaskData2; + AvailableTaskTrailer_Struct* AvailableTaskTrailer; + + // Check if any of the tasks exist + + + for(int i=0; iIsTaskActive(TaskList[i])) continue; + + if(!IsTaskRepeatable(TaskList[i]) && c->IsTaskCompleted(TaskList[i])) continue; + + ValidTasks++; + + PacketLength = PacketLength + sizeof(AvailableTaskData1_Struct) + strlen(Tasks[TaskList[i]]->Title) + 1 + + strlen(Tasks[TaskList[i]]->Description) + 1 + sizeof(AvailableTaskData2_Struct) + 10 + + sizeof(AvailableTaskTrailer_Struct) + 5; + } + + if(ValidTasks == 0) return; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_OpenNewTasksWindow, PacketLength); + + AvailableTaskHeader = (AvailableTaskHeader_Struct*)outapp->pBuffer; + + AvailableTaskHeader->TaskCount = ValidTasks; + + // unknown1 is always 2 in the packets I have ssen. It may be a 'Task Type'. Given that the + // task system was apparently first introduced for LDoN missions, type 1 may be for missions. + // + AvailableTaskHeader->unknown1 = 2; + AvailableTaskHeader->TaskGiver = mob->GetID(); + + Ptr = (char *) AvailableTaskHeader + sizeof(AvailableTaskHeader_Struct); + + for(int i=0; iIsTaskActive(TaskList[i])) continue; + + if(!IsTaskRepeatable(TaskList[i]) && c->IsTaskCompleted(TaskList[i])) continue; + + AvailableTaskData1 = (AvailableTaskData1_Struct*)Ptr; + + AvailableTaskData1->TaskID = TaskList[i]; + + AvailableTaskData1->TimeLimit = Tasks[TaskList[i]]->Duration; + + AvailableTaskData1->unknown2 = 0; + + Ptr = (char *)AvailableTaskData1 + sizeof(AvailableTaskData1_Struct); + + sprintf(Ptr, "%s", Tasks[TaskList[i]]->Title); + + Ptr = Ptr + strlen(Ptr) + 1; + + sprintf(Ptr, "%s", Tasks[TaskList[i]]->Description); + + Ptr = Ptr + strlen(Ptr) + 1; + + AvailableTaskData2 = (AvailableTaskData2_Struct*)Ptr; + + AvailableTaskData2->unknown1 = 1; + AvailableTaskData2->unknown2 = 0; + AvailableTaskData2->unknown3 = 1; + AvailableTaskData2->unknown4 = 0; + + Ptr = (char *)AvailableTaskData2 + sizeof(AvailableTaskData2_Struct); + + // 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, "NULL"); + Ptr = Ptr + strlen(Ptr) + 1; + sprintf(Ptr, "NULL"); + Ptr = Ptr + strlen(Ptr) + 1; + + AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)Ptr; + + // The name of this ItemCount field may be incorrect, but 1 works. + AvailableTaskTrailer->ItemCount = 1; + AvailableTaskTrailer->unknown1 = 0xFFFFFFFF; + AvailableTaskTrailer->unknown2 = 0xFFFFFFFF; + AvailableTaskTrailer->StartZone = Tasks[TaskList[i]]->StartZone; + + Ptr = (char *)AvailableTaskTrailer + sizeof(AvailableTaskTrailer_Struct); + + // In some packets, this next string looks like a short task summary, however it doesn't + // appear anywhere in the client window. + sprintf(Ptr, "NULL"); + Ptr = Ptr + strlen(Ptr) + 1; + } + + _pkt(TASKS__PACKETS, outapp); + + c->QueuePacket(outapp); + safe_delete(outapp); + +} + +void TaskManager::SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *TaskList) { + + _log(TASKS__UPDATE, "TaskSelector for %i Tasks", TaskCount); + + int PlayerLevel = c->GetLevel(); + + // Check if any of the tasks exist + for(int i=0; iIsTaskActive(TaskList[i])) continue; + + if(!IsTaskRepeatable(TaskList[i]) && c->IsTaskCompleted(TaskList[i])) continue; + + ValidTasks++; + + PacketLength += 21; // Task Data - strings + PacketLength += strlen(Tasks[TaskList[i]]->Title) + 1 + + strlen(Tasks[TaskList[i]]->Description) + 1; + + sprintf(StartZone, "%i", Tasks[TaskList[i]]->StartZone); + /* + PacketLength += strlen(Tasks[TaskList[i]]->Activity[ActivityID].Text1) + 1 + + strlen(Tasks[TaskList[i]]->Activity[ActivityID].Text2) + + strlen(Tasks[TaskList[i]]->Activity[ActivityID].Text3) + 1 + + strlen(itoa(Tasks[TaskList[i]]->Activity[ActivityID].ZoneID)) + 1 + + 3 + 3 + 5; // Other strings (Hard set for now) + */ + PacketLength += 11 + 11 + 11 + 3 + 3 + (strlen(StartZone) * 2) + 2; // Other strings (Hard set for now) + PacketLength += 28; // Activity Data - strings (Hard set for 1 activity per task for now) + } + + if(ValidTasks == 0) return; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_OpenNewTasksWindow, PacketLength); + + outapp->WriteUInt32(ValidTasks); // TaskCount + outapp->WriteUInt32(2); // Unknown2 + outapp->WriteUInt32(mob->GetID()); // TaskGiver + + for(int i=0; iIsTaskActive(TaskList[i])) continue; + + if(!IsTaskRepeatable(TaskList[i]) && c->IsTaskCompleted(TaskList[i])) continue; + + outapp->WriteUInt32(TaskList[i]); // TaskID + outapp->WriteFloat(1.0f); + outapp->WriteUInt32(Tasks[TaskList[i]]->Duration); + outapp->WriteUInt32(0); // Unknown7 + + outapp->WriteString(Tasks[TaskList[i]]->Title); + outapp->WriteString(Tasks[TaskList[i]]->Description); + + outapp->WriteUInt8(0); // Unknown10 - Empty string ? + outapp->WriteUInt32(1); // ActivityCount - Hard set to 1 for now + + // Activity stuff below - Will need to iterate through each task + // Currently hard set for testing + + + sprintf(StartZone, "%i", Tasks[TaskList[i]]->StartZone); + + outapp->WriteUInt32(0); // ActivityNumber + outapp->WriteUInt32(1); // ActivityType + outapp->WriteUInt32(0); // Unknown14 + outapp->WriteString("Text1 Test"); + outapp->WriteUInt32(11); // Text2Len + outapp->WriteString("Text2 Test"); + outapp->WriteUInt32(1); // GoalCount + outapp->WriteUInt32(3); // NumString1Len + outapp->WriteString("-1"); + outapp->WriteUInt32(3); // NumString2Len + outapp->WriteString("-1"); + //outapp->WriteString(itoa(Tasks[TaskList[i]]->Activity[ActivityID].ZoneID)); + outapp->WriteString(StartZone); // Zone number in ascii + outapp->WriteString("Text3 Test"); + outapp->WriteString(StartZone); // Zone number in ascii + } + _pkt(TASKS__PACKETS, outapp); + + c->QueuePacket(outapp); + safe_delete(outapp); + +} + +int TaskManager::GetActivityCount(int TaskID) { + + // Return the total number of activities in a particular task. + + if((TaskID>0) && (TaskIDActivityCount; + + return 0; + +} + +void TaskManager::ExplainTask(Client*c, int TaskID) { + + // TODO: This method is not finished (hardly started). It was intended to + // explain in English, what each activity did, conditions for step unlocking, etc. + // + return; + + if(!c) return; + + if((TaskID<=0) || (TaskID>=MAXTASKS)) { + c->Message(0, "TaskID out-of-range."); + return; + } + + if(Tasks[TaskID] == NULL) { + c->Message(0, "Task does not exist."); + return; + } + + char Explanation[1000], *ptr; + c->Message(0, "Task %4i: Title: %s", TaskID, Tasks[TaskID]->Description); + c->Message(0, "%3i Activities", Tasks[TaskID]->ActivityCount); + ptr = Explanation; + for(int i=0; iActivityCount; i++) { + + sprintf(ptr, "Act: %3i: ", i); ptr = ptr + strlen(ptr); + switch(Tasks[TaskID]->Activity[i].Type) { + case ActivityDeliver: + sprintf(ptr, "Deliver"); + break; + } + + } +} + +ClientTaskState::ClientTaskState() { + + ActiveTaskCount = 0; + LastCompletedTaskLoaded = 0; + CheckedTouchActivities = false; + + for(int i=0; i=MAXACTIVETASKS)) return 0; + + return ActiveTasks[index].TaskID; +} + +static void DeleteCompletedTaskFromDatabase(int CharID, int TaskID) { + + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + + 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); + return; + } + _log(TASKS__UPDATE, "Delete query %s", query); + safe_delete_array(query); +} + +bool ClientTaskState::UnlockActivities(int CharID, int TaskIndex) { + + bool AllActivitiesComplete = true; + + TaskInformation* Task = taskmanager->Tasks[ActiveTasks[TaskIndex].TaskID]; + + if(Task==NULL) return true; + + // On loading the client state, all activities that are not completed, are + // marked as hidden. For Sequential (non-stepped) mode, we mark the first + // activity as active if not complete. + _log(TASKS__UPDATE, "CharID: %i Task: %i Sequence mode is %i", + CharID, ActiveTasks[TaskIndex].TaskID, Task->SequenceMode); + if(Task->SequenceMode == ActivitiesSequential) { + + if(ActiveTasks[TaskIndex].Activity[0].State != ActivityCompleted) + ActiveTasks[TaskIndex].Activity[0].State = ActivityActive; + + // Enable the next Hidden task. + for(int i=0; iActivityCount; i++) { + if((ActiveTasks[TaskIndex].Activity[i].State == ActivityActive) && + (!Task->Activity[i].Optional)) { + AllActivitiesComplete = false; + break; + } + if(ActiveTasks[TaskIndex].Activity[i].State == ActivityHidden) { + ActiveTasks[TaskIndex].Activity[i].State = ActivityActive; + AllActivitiesComplete = false; + break; + } + } + if(AllActivitiesComplete && RuleB(TaskSystem, RecordCompletedTasks)) { + if(RuleB(TasksSystem, KeepOneRecordPerCompletedTask)) { + _log(TASKS__UPDATE, "KeepOneRecord enabled"); + vector::iterator Iterator = CompletedTasks.begin(); + int ErasedElements = 0; + while(Iterator != CompletedTasks.end()) { + int TaskID = (*Iterator).TaskID; + if(TaskID == ActiveTasks[TaskIndex].TaskID) { + Iterator = CompletedTasks.erase(Iterator); + ErasedElements++; + } + else + Iterator++; + } + _log(TASKS__UPDATE, "Erased Element count is %i", ErasedElements); + if(ErasedElements) { + LastCompletedTaskLoaded -= ErasedElements; + DeleteCompletedTaskFromDatabase(CharID, ActiveTasks[TaskIndex].TaskID); + } + + } + + CompletedTaskInformation cti; + cti.TaskID = ActiveTasks[TaskIndex].TaskID; + cti.CompletedTime = time(NULL); + + for(int i=0; iActivityCount; i++) + cti.ActivityDone[i] = (ActiveTasks[TaskIndex].Activity[i].State == ActivityCompleted); + + CompletedTasks.push_back(cti); + } + _log(TASKS__UPDATE, "Returning sequential task, AllActivitiesComplete is %i", AllActivitiesComplete); + return AllActivitiesComplete; + } + + // Stepped Mode + // TODO: This code is probably more complex than it needs to be + + bool CurrentStepComplete = true; + + _log(TASKS__UPDATE, "Current Step is %i, Last Step is %i", ActiveTasks[TaskIndex].CurrentStep, Task->LastStep); + // If CurrentStep is -1, this is the first call to this method since loading the + // client state. Unlock all activities with a step number of 0 + if(ActiveTasks[TaskIndex].CurrentStep == -1) { + for(int i=0; iActivityCount; i++) { + + if((Task->Activity[i].StepNumber == 0) && + (ActiveTasks[TaskIndex].Activity[i].State == ActivityHidden)) { + ActiveTasks[TaskIndex].Activity[i].State = ActivityActive; + //ActiveTasks[TaskIndex].Activity[i].Updated=true; + } + } + ActiveTasks[TaskIndex].CurrentStep = 0; + } + + for(int Step=ActiveTasks[TaskIndex].CurrentStep; Step<=Task->LastStep; Step++) { + for(int Activity=0; ActivityActivityCount; Activity++) { + if(Task->Activity[Activity].StepNumber == (int)ActiveTasks[TaskIndex].CurrentStep) { + if((ActiveTasks[TaskIndex].Activity[Activity].State != ActivityCompleted) && + (!Task->Activity[Activity].Optional)) { + CurrentStepComplete = false; + AllActivitiesComplete = false; + break; + } + } + } + if(!CurrentStepComplete) break; + ActiveTasks[TaskIndex].CurrentStep++; + } + + if(AllActivitiesComplete) { + if(RuleB(TaskSystem, RecordCompletedTasks)) { + // If we are only keeping one completed record per task, and the player has done + // the same task again, erase the previous completed entry for this task. + if(RuleB(TasksSystem, KeepOneRecordPerCompletedTask)) { + _log(TASKS__UPDATE, "KeepOneRecord enabled"); + vector::iterator Iterator = CompletedTasks.begin(); + int ErasedElements = 0; + while(Iterator != CompletedTasks.end()) { + int TaskID = (*Iterator).TaskID; + if(TaskID == ActiveTasks[TaskIndex].TaskID) { + Iterator = CompletedTasks.erase(Iterator); + ErasedElements++; + } + else + Iterator++; + } + _log(TASKS__UPDATE, "Erased Element count is %i", ErasedElements); + if(ErasedElements) { + LastCompletedTaskLoaded -= ErasedElements; + DeleteCompletedTaskFromDatabase(CharID, ActiveTasks[TaskIndex].TaskID); + } + + } + CompletedTaskInformation cti; + cti.TaskID = ActiveTasks[TaskIndex].TaskID; + cti.CompletedTime = time(NULL); + + for(int i=0; iActivityCount; i++) + cti.ActivityDone[i] = (ActiveTasks[TaskIndex].Activity[i].State == ActivityCompleted); + + CompletedTasks.push_back(cti); + } + return true; + } + + // Mark all non-completed tasks in the current step as active + // + for(int Activity=0; ActivityActivityCount; Activity++) { + if((Task->Activity[Activity].StepNumber == (int)ActiveTasks[TaskIndex].CurrentStep) && + (ActiveTasks[TaskIndex].Activity[Activity].State == ActivityHidden)) { + ActiveTasks[TaskIndex].Activity[Activity].State = ActivityActive; + ActiveTasks[TaskIndex].Activity[Activity].Updated=true; + } + } + + return false; + +} + + +void ClientTaskState::UpdateTasksOnKill(Client *c, int NPCTypeID) { + + UpdateTasksByNPC(c, ActivityKill, NPCTypeID); + +} + +bool ClientTaskState::UpdateTasksOnSpeakWith(Client *c, int NPCTypeID) { + + return UpdateTasksByNPC(c, ActivitySpeakWith, NPCTypeID); + +} + +bool ClientTaskState::UpdateTasksByNPC(Client *c, int ActivityType, int NPCTypeID) { + + int Ret = false; + + _log(TASKS__UPDATE, "ClientTaskState::UpdateTasks for NPCTypeID: %d", NPCTypeID); + + // If the client has no tasks, there is nothing further to check. + + if(!taskmanager || ActiveTaskCount == 0) return false; + + for(int i=0; iTasks[ActiveTasks[i].TaskID]; + + if(Task == NULL) return false; + + for(int j=0; jActivityCount; j++) { + // We are not interested in completed or hidden activities + if(ActiveTasks[i].Activity[j].State != ActivityActive) continue; + // We are only interested in Kill activities + if(Task->Activity[j].Type != ActivityType) continue; + // Is there a zone restriction on the activity ? + if((Task->Activity[j].ZoneID >0) && (Task->Activity[j].ZoneID != (int)zone->GetZoneID())) { + _log(TASKS__UPDATE, "Char: %s Task: %i, Activity %i, Activity type %i for NPC %i failed zone check", + c->GetName(), ActiveTasks[i].TaskID, j, ActivityType, NPCTypeID); + continue; + } + // Is the activity to kill this type of NPC ? + switch(Task->Activity[j].GoalMethod) { + + case METHODSINGLEID: + if(Task->Activity[j].GoalID != NPCTypeID) continue; + break; + + case METHODLIST: + if(!taskmanager->GoalListManager.IsInList(Task->Activity[j].GoalID, + NPCTypeID)) continue; + break; + + default: + // If METHODQUEST, don't update the activity here + continue; + } + // We found an active task to kill this type of NPC, so increment the done count + _log(TASKS__UPDATE, "Calling increment done count ByNPC"); + IncrementDoneCount(c, Task, i, j); + Ret = true; + } + } + + return Ret; +} + +int ClientTaskState::ActiveSpeakTask(int NPCTypeID) { + + // This method is to be used from Perl quests only and returns the TaskID of the first + // active task found which has an active SpeakWith activity for this NPC. + + if(!taskmanager || ActiveTaskCount == 0) return 0; + + for(int i=0; iTasks[ActiveTasks[i].TaskID]; + + if(Task == NULL) continue; + + for(int j=0; jActivityCount; j++) { + // We are not interested in completed or hidden activities + if(ActiveTasks[i].Activity[j].State != ActivityActive) continue; + if(Task->Activity[j].Type != ActivitySpeakWith) continue; + // Is there a zone restriction on the activity ? + if((Task->Activity[j].ZoneID >0) && (Task->Activity[j].ZoneID != (int)zone->GetZoneID())) { + continue; + } + // Is the activity to speak with this type of NPC ? + if((Task->Activity[j].GoalMethod == METHODQUEST) && + (Task->Activity[j].GoalID == NPCTypeID)) return ActiveTasks[i].TaskID; + } + } + return 0; +} + +int ClientTaskState::ActiveSpeakActivity(int NPCTypeID, int TaskID) { + + // This method is to be used from Perl quests only and returns the ActivityID of the first + // active activity found in the specified task which is to SpeakWith this NPC. + + if(!taskmanager || ActiveTaskCount == 0) return -1; + if((TaskID<=0) || (TaskID>=MAXTASKS)) return -1; + + for(int i=0; iTasks[ActiveTasks[i].TaskID]; + + if(Task == NULL) continue; + + for(int j=0; jActivityCount; j++) { + // We are not interested in completed or hidden activities + if(ActiveTasks[i].Activity[j].State != ActivityActive) continue; + if(Task->Activity[j].Type != ActivitySpeakWith) continue; + // Is there a zone restriction on the activity ? + if((Task->Activity[j].ZoneID >0) && (Task->Activity[j].ZoneID != (int)zone->GetZoneID())) { + continue; + } + // Is the activity to speak with this type of NPC ? + if((Task->Activity[j].GoalMethod == METHODQUEST) && + (Task->Activity[j].GoalID == NPCTypeID)) return j; + } + return 0; + } + return 0; +} +void ClientTaskState::UpdateTasksForItem(Client *c, ActivityType Type, int ItemID, int Count) { + + // This method updates the client's task activities of the specified type which relate + // to the specified item. + // + // Type should be one of ActivityLoot, ActivityTradeSkill, ActivityFish or ActivityForage + + // If the client has no tasks, there is nothing further to check. + + _log(TASKS__UPDATE, "ClientTaskState::UpdateTasksForItem(%d,%d)", Type, ItemID); + + if(ActiveTaskCount == 0) return; + + for(int i=0; iTasks[ActiveTasks[i].TaskID]; + + if(Task == NULL) return; + + for(int j=0; jActivityCount; j++) { + // We are not interested in completed or hidden activities + if(ActiveTasks[i].Activity[j].State != ActivityActive) continue; + // We are only interested in the ActivityType we were called with + if(Task->Activity[j].Type != (int)Type) continue; + // Is there a zone restriction on the activity ? + if((Task->Activity[j].ZoneID >0) && (Task->Activity[j].ZoneID != (int)zone->GetZoneID())) { + _log(TASKS__UPDATE, "Char: %s Activity type %i for Item %i failed zone check", + c->GetName(), Type, ItemID); + continue; + } + // Is the activity related to this item ? + // + switch(Task->Activity[j].GoalMethod) { + + case METHODSINGLEID: + if(Task->Activity[j].GoalID != ItemID) continue; + break; + + case METHODLIST: + if(!taskmanager->GoalListManager.IsInList(Task->Activity[j].GoalID, ItemID)) continue; + break; + + default: + // If METHODQUEST, don't update the activity here + continue; + } + // We found an active task related to this item, so increment the done count + _log(TASKS__UPDATE, "Calling increment done count ForItem"); + IncrementDoneCount(c, Task, i, j, Count); + } + } + + return; +} + +void ClientTaskState::UpdateTasksOnExplore(Client *c, int ExploreID) { + + // If the client has no tasks, there is nothing further to check. + + _log(TASKS__UPDATE, "ClientTaskState::UpdateTasksOnExplore(%i)", ExploreID); + if(ActiveTaskCount == 0) return; + + for(int i=0; iTasks[ActiveTasks[i].TaskID]; + + if(Task == NULL) return; + + for(int j=0; jActivityCount; j++) { + // We are not interested in completed or hidden activities + if(ActiveTasks[i].Activity[j].State != ActivityActive) continue; + // We are only interested in explore activities + if(Task->Activity[j].Type != ActivityExplore) continue; + if((Task->Activity[j].ZoneID >0) && (Task->Activity[j].ZoneID != (int)zone->GetZoneID())) { + _log(TASKS__UPDATE, "Char: %s Explore exploreid %i failed zone check", + c->GetName(), ExploreID); + continue; + } + // Is the activity to explore this area id ? + switch(Task->Activity[j].GoalMethod) { + + case METHODSINGLEID: + if(Task->Activity[j].GoalID != ExploreID) continue; + break; + + case METHODLIST: + if(!taskmanager->GoalListManager.IsInList(Task->Activity[j].GoalID, + ExploreID)) continue; + break; + + default: + // If METHODQUEST, don't update the activity here + continue; + } + // We found an active task to explore this area, so set done count to goal count + // (Only a goal count of 1 makes sense for explore activities?) + _log(TASKS__UPDATE, "Increment on explore"); + IncrementDoneCount(c, Task, i, j, + Task->Activity[j].GoalCount - ActiveTasks[i].Activity[j].DoneCount); + + } + } + + return; +} + +bool ClientTaskState::UpdateTasksOnDeliver(Client *c, uint32 *Items, int Cash, int NPCTypeID) { + + bool Ret = false; + + _log(TASKS__UPDATE, "ClientTaskState::UpdateTasksForOnDeliver(%d)", NPCTypeID); + + if(ActiveTaskCount == 0) return false; + + for(int i=0; iTasks[ActiveTasks[i].TaskID]; + + if(Task == NULL) return false; + + for(int j=0; jActivityCount; j++) { + // We are not interested in completed or hidden activities + if(ActiveTasks[i].Activity[j].State != ActivityActive) continue; + // We are only interested in Deliver activities + if((Task->Activity[j].Type != ActivityDeliver) && + (Task->Activity[j].Type != ActivityGiveCash)) continue; + // Is there a zone restriction on the activity ? + if((Task->Activity[j].ZoneID >0) && (Task->Activity[j].ZoneID != (int)zone->GetZoneID())) { + _log(TASKS__UPDATE, "Char: %s Deliver activity failed zone check (current zone %i, need zone %i", + c->GetName(), zone->GetZoneID(), Task->Activity[j].ZoneID); + continue; + } + // Is the activity to deliver to this NPCTypeID ? + if(Task->Activity[j].DeliverToNPC != NPCTypeID) continue; + // Is the activity related to these items ? + // + if((Task->Activity[j].Type == ActivityGiveCash) && Cash) { + _log(TASKS__UPDATE, "Increment on GiveCash"); + IncrementDoneCount(c, Task, i, j, Cash); + Ret = true; + } + else { + for(int k=0; k<4; k++) { + if(Items[k]==0) continue; + switch(Task->Activity[j].GoalMethod) { + + case METHODSINGLEID: + if(Task->Activity[j].GoalID != (int)Items[k]) continue; + break; + + case METHODLIST: + if(!taskmanager->GoalListManager.IsInList(Task->Activity[j].GoalID, + Items[k])) + continue; + break; + + default: + // If METHODQUEST, don't update the activity here + continue; + } + // We found an active task related to this item, so increment the done count + _log(TASKS__UPDATE, "Increment on GiveItem"); + IncrementDoneCount(c, Task, i, j, 1); + Ret = true; + } + } + } + } + + return Ret; +} + +void ClientTaskState::UpdateTasksOnTouch(Client *c, int ZoneID) { + + // If the client has no tasks, there is nothing further to check. + + _log(TASKS__UPDATE, "ClientTaskState::UpdateTasksOnTouch(%i)", ZoneID); + if(ActiveTaskCount == 0) return; + + for(int i=0; iTasks[ActiveTasks[i].TaskID]; + + if(Task == NULL) return; + + for(int j=0; jActivityCount; j++) { + // We are not interested in completed or hidden activities + if(ActiveTasks[i].Activity[j].State != ActivityActive) continue; + // We are only interested in touch activities + if(Task->Activity[j].Type != ActivityTouch) continue; + if(Task->Activity[j].GoalMethod != METHODSINGLEID) continue; + if(Task->Activity[j].ZoneID != ZoneID) { + _log(TASKS__UPDATE, "Char: %s Touch activity failed zone check", + c->GetName()); + continue; + } + // We found an active task to zone into this zone, so set done count to goal count + // (Only a goal count of 1 makes sense for touch activities?) + _log(TASKS__UPDATE, "Increment on Touch"); + IncrementDoneCount(c, Task, i, j, + Task->Activity[j].GoalCount - ActiveTasks[i].Activity[j].DoneCount); + } + } + + return; +} +void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int TaskIndex, int ActivityID, int Count) { + + _log(TASKS__UPDATE, "IncrementDoneCount"); + + ActiveTasks[TaskIndex].Activity[ActivityID].DoneCount += Count; + + if(ActiveTasks[TaskIndex].Activity[ActivityID].DoneCount > Task->Activity[ActivityID].GoalCount) + ActiveTasks[TaskIndex].Activity[ActivityID].DoneCount = Task->Activity[ActivityID].GoalCount; + + char buf[24]; + snprintf(buf, 23, "%d %d %d", ActiveTasks[TaskIndex].Activity[ActivityID].DoneCount, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID, ActiveTasks[TaskIndex].TaskID); + buf[23] = '\0'; + parse->EventPlayer(EVENT_TASK_UPDATE, c, buf, 0); + + ActiveTasks[TaskIndex].Activity[ActivityID].Updated=true; + // Have we reached the goal count for this activity ? + if(ActiveTasks[TaskIndex].Activity[ActivityID].DoneCount >= Task->Activity[ActivityID].GoalCount) { + _log(TASKS__UPDATE, "Done (%i) = Goal (%i) for Activity %i", + ActiveTasks[TaskIndex].Activity[ActivityID].DoneCount, + Task->Activity[ActivityID].GoalCount, + ActivityID); + + 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); + } + + // Flag the activity as complete + ActiveTasks[TaskIndex].Activity[ActivityID].State = ActivityCompleted; + // Unlock subsequent activities for this task + bool TaskComplete = UnlockActivities(c->CharacterID(), TaskIndex); + _log(TASKS__UPDATE, "TaskCompleted is %i", TaskComplete); + // and by the 'Task Stage Completed' message + c->SendTaskActivityComplete(ActiveTasks[TaskIndex].TaskID, ActivityID, TaskIndex); + // Send the updated task/activity list to the client + 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 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. + if(TaskComplete) { + char buf[24]; + snprintf(buf, 23, "%d %d %d", ActiveTasks[TaskIndex].Activity[ActivityID].DoneCount, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID, ActiveTasks[TaskIndex].TaskID); + buf[23] = '\0'; + parse->EventPlayer(EVENT_TASK_COMPLETE, c, buf, 0); + + taskmanager->SendCompletedTasksToClient(c, this); + c->SendTaskActivityComplete(ActiveTasks[TaskIndex].TaskID, 0, TaskIndex, false); + taskmanager->SaveClientState(c, this); + //c->SendTaskComplete(TaskIndex); + c->CancelTask(TaskIndex); + //if(Task->RewardMethod != METHODQUEST) RewardTask(c, Task); + // If Experience and/or cash rewards are set, reward them from the task even if RewardMethod is METHODQUEST + RewardTask(c, Task); + //RemoveTask(c, TaskIndex); + } + + } + else + // Send an update packet for this single activity + taskmanager->SendTaskActivityLong(c, ActiveTasks[TaskIndex].TaskID, ActivityID, + TaskIndex, Task->Activity[ActivityID].Optional); +} + +void ClientTaskState::RewardTask(Client *c, TaskInformation *Task) { + + if(!Task || !c) return; + + const Item_Struct* Item; + vector RewardList; + + switch(Task->RewardMethod) { + + case METHODSINGLEID: + { + if(Task->RewardID) { + c->SummonItem(Task->RewardID); + Item = database.GetItem(Task->RewardID); + if(Item) + c->Message(15, "You receive %s as a reward.", Item->Name); + } + break; + } + case METHODLIST: + { + RewardList = taskmanager->GoalListManager.GetListContents(Task->RewardID); + for(unsigned int i=0; iSummonItem(RewardList[i]); + Item = database.GetItem(RewardList[i]); + if(Item) + c->Message(15, "You receive %s as a reward.", Item->Name); + } + break; + } + default: + { + // Nothing special done for METHODQUEST + break; + } + } + if(Task->CashReward) { + int Plat, Gold, Silver, Copper; + + Copper = Task->CashReward; + c->AddMoneyToPP(Copper, true); + + Plat = Copper / 1000; + Copper = Copper - (Plat * 1000); + Gold = Copper / 100; + Copper = Copper - (Gold * 100); + Silver = Copper / 10; + Copper = Copper - (Silver * 10); + + string CashMessage; + + if (Plat>0){ + CashMessage = "You receive "; + CashMessage += itoa(Plat); + CashMessage += " platinum"; + } + if (Gold>0){ + if (CashMessage.length()==0){ + CashMessage = "You receive "; + } + else{ + CashMessage += ","; + } + CashMessage += itoa(Gold); + CashMessage += " gold"; + } + if(Silver>0){ + if (CashMessage.length()==0){ + CashMessage = "You receive "; + } + else{ + CashMessage += ","; + } + CashMessage += itoa(Silver); + CashMessage += " silver"; + } + if(Copper>0){ + if (CashMessage.length()==0){ + CashMessage = "You receive "; + } + else{ + CashMessage += ","; + } + CashMessage += itoa(Copper); + CashMessage += " copper"; + } + CashMessage += " pieces."; + c->Message(15,CashMessage.c_str()); + } + int32 EXPReward = Task->XPReward; + if(EXPReward > 0) { + c->AddEXP(EXPReward); + } + if(EXPReward < 0) { + uint32 PosReward = EXPReward * -1; + // Minimal Level Based Exp Reward Setting is 101 (1% exp at level 1) + if (PosReward > 100 && PosReward < 25700) { + uint8 MaxLevel = PosReward / 100; + uint8 ExpPercent = PosReward - (MaxLevel * 100); + c->AddLevelBasedExp(ExpPercent, MaxLevel); + } + } + + c->SendSound(); +} + +bool ClientTaskState::IsTaskActive(int TaskID) { + + if((ActiveTaskCount == 0) || (TaskID == 0)) return false; + + for(int i=0; iSendTaskFailed(ActiveTasks[i].TaskID, i); + // Remove the task from the client + c->CancelTask(i); + return; + } + + } + +} + +bool ClientTaskState::IsTaskActivityActive(int TaskID, int ActivityID) { + + _log(TASKS__UPDATE, "ClientTaskState IsTaskActivityActive(%i, %i).", TaskID, ActivityID); + // Quick sanity check + if(ActivityID<0) return false; + if(ActiveTaskCount == 0) return false; + + int ActiveTaskIndex = -1; + + for(int i=0; iTasks[ActiveTasks[ActiveTaskIndex].TaskID]; + + // The task is invalid + if(Task==NULL) return false; + + // The ActivityID is out of range + if(ActivityID >= Task->ActivityCount) return false; + + _log(TASKS__UPDATE, "ClientTaskState IsTaskActivityActive(%i, %i). State is %i ", TaskID, ActivityID, + ActiveTasks[ActiveTaskIndex].Activity[ActivityID].State); + + + return (ActiveTasks[ActiveTaskIndex].Activity[ActivityID].State == ActivityActive); + +} + +void ClientTaskState::UpdateTaskActivity(Client *c, int TaskID, int ActivityID, int Count) { + + _log(TASKS__UPDATE, "ClientTaskState UpdateTaskActivity(%i, %i, %i).", TaskID, ActivityID, Count); + + // Quick sanity check + if((ActivityID<0) || (ActiveTaskCount==0)) return; + + int ActiveTaskIndex = -1; + + for(int i=0; iTasks[ActiveTasks[ActiveTaskIndex].TaskID]; + + // The task is invalid + if(Task==NULL) return; + + // The ActivityID is out of range + if(ActivityID >= Task->ActivityCount) return; + + // The Activity is not currently active + if(ActiveTasks[ActiveTaskIndex].Activity[ActivityID].State != ActivityActive) return; + _log(TASKS__UPDATE, "Increment done count on UpdateTaskActivity"); + IncrementDoneCount(c, Task, ActiveTaskIndex, ActivityID, Count); + +} + +void ClientTaskState::ResetTaskActivity(Client *c, int TaskID, int ActivityID) { + + _log(TASKS__UPDATE, "ClientTaskState UpdateTaskActivity(%i, %i, 0).", TaskID, ActivityID); + + // Quick sanity check + if((ActivityID<0) || (ActiveTaskCount==0)) return; + + int ActiveTaskIndex = -1; + + for(int i=0; iTasks[ActiveTasks[ActiveTaskIndex].TaskID]; + + // The task is invalid + if(Task==NULL) return; + + // The ActivityID is out of range + if(ActivityID >= Task->ActivityCount) return; + + // The Activity is not currently active + if(ActiveTasks[ActiveTaskIndex].Activity[ActivityID].State != ActivityActive) return; + + _log(TASKS__UPDATE, "ResetTaskActivityCount"); + + ActiveTasks[ActiveTaskIndex].Activity[ActivityID].DoneCount = 0; + + ActiveTasks[ActiveTaskIndex].Activity[ActivityID].Updated=true; + + // Send an update packet for this single activity + taskmanager->SendTaskActivityLong(c, ActiveTasks[ActiveTaskIndex].TaskID, ActivityID, + ActiveTaskIndex, Task->Activity[ActivityID].Optional); +} + +void ClientTaskState::ShowClientTasks(Client *c) { + + c->Message(0, "Task Information:"); + //for(int i=0; iMessage(0, "Task: %i %s", ActiveTasks[i].TaskID, taskmanager->Tasks[ActiveTasks[i].TaskID]->Title); + c->Message(0, " Description: [%s]\n", taskmanager->Tasks[ActiveTasks[i].TaskID]->Description); + for(int j=0; jGetActivityCount(ActiveTasks[i].TaskID); j++) { + c->Message(0, " Activity: %2d, DoneCount: %2d, Status: %d (0=Hidden, 1=Active, 2=Complete)", + ActiveTasks[i].Activity[j].ActivityID, + ActiveTasks[i].Activity[j].DoneCount, + ActiveTasks[i].Activity[j].State); + } + } + +} + +int ClientTaskState::TaskTimeLeft(int TaskID) { + + if(ActiveTaskCount == 0) return -1; + + for(int i=0; iTasks[ActiveTasks[i].TaskID]; + + if(Task == NULL) return -1; + + if(!Task->Duration) return -1; + + int TimeLeft = (ActiveTasks[i].AcceptedTime + Task->Duration - Now); + + // If Timeleft is negative, return 0, else return the number of seconds left + + return (TimeLeft>0 ? TimeLeft : 0); + } + + return -1; +} + +int ClientTaskState::IsTaskCompleted(int TaskID) { + + // Returns: -1 if RecordCompletedTasks is not true + // +1 if the task has been completed + // 0 if the task has not been completed + + if(!(RuleB(TaskSystem, RecordCompletedTasks))) return -1; + + for(unsigned int i=0; i= MAXTASKS)) return false; + + TaskInformation* Task = taskmanager->Tasks[TaskID]; + + if(Task == NULL) return false; + + return Task->Repeatable; +} + +bool ClientTaskState::TaskOutOfTime(int Index) { + + // Returns true if the Task in the specified slot has a time limit that has been exceeded. + + if((Index < 0) || (Index>=MAXACTIVETASKS)) return false; + + if((ActiveTasks[Index].TaskID <= 0) || (ActiveTasks[Index].TaskID >= MAXTASKS)) return false; + + int Now = time(NULL); + + TaskInformation* Task = taskmanager->Tasks[ActiveTasks[Index].TaskID]; + + if(Task == NULL) return false; + + return (Task->Duration && (ActiveTasks[Index].AcceptedTime + Task->Duration <= Now)); + +} + +void ClientTaskState::TaskPeriodicChecks(Client *c) { + + if(ActiveTaskCount == 0) return; + + // Check for tasks that have failed because they have not been completed in the specified time + // + for(int i=0; iSendTaskFailed(ActiveTasks[i].TaskID, i); + // Remove the task from the client + c->CancelTask(i); + // It is a conscious decision to only fail one task per call to this method, + // otherwise the player will not see all the failed messages where multiple + // tasks fail at the same time. + break; + + } + } + + // Check for activities that require zoning into a specific zone. + // This is done in this method because it gives an extra few seconds for the client screen to display + // the zone before we send the 'Task Activity Completed' message. + // + if(!CheckedTouchActivities) { + UpdateTasksOnTouch(c, zone->GetZoneID()); + CheckedTouchActivities = true; + } +} + + +#if 0 +void Client::SendTaskComplete(int TaskIndex) { + + // 0x4c8c + + TaskComplete_Struct* tcs; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TaskComplete, sizeof(TaskComplete_Struct)); + + tcs = (TaskComplete_Struct*)outapp->pBuffer; + + // I have seen unknown0 as non-zero. It always seems to match the value in the first word of the + // Task Activity Complete packet sent immediately prior to it. + //tcs->unknown00 = 0x00000000; + tcs->unknown00 = TaskIndex; + // I have only seen 0x00000002 in the next field. This is a common 'unknown' value in the task packets. + // I suspect this is the type field to indicate this is a quest task, as opposed to other types. + tcs->unknown04 = 0x00000002; + + _log("[TASKS]SendTasksComplete"); + DumpPacket(outapp); fflush(stdout); + + QueuePacket(outapp); + safe_delete(outapp); + + + +} +#endif + +void ClientTaskState::SendTaskHistory(Client *c, int TaskIndex) { + + _log(TASKS__UPDATE, "Task History Requested for Completed Task Index %i", TaskIndex); + + // We only sent the most recent 50 completed tasks, so we need to offset the Index the client sent to us. + + int AdjustedTaskIndex = TaskIndex; + + if(CompletedTasks.size() > 50) + AdjustedTaskIndex += (CompletedTasks.size() - 50); + + if((AdjustedTaskIndex < 0) || (AdjustedTaskIndex >= (int)CompletedTasks.size())) return; + + int TaskID = CompletedTasks[AdjustedTaskIndex].TaskID; + + if((TaskID < 0) || (TaskID > MAXTASKS)) return; + + TaskInformation* Task = taskmanager->Tasks[TaskID]; + + if(Task == NULL) return; + + TaskHistoryReplyHeader_Struct* ths; + TaskHistoryReplyData1_Struct* thd1; + TaskHistoryReplyData2_Struct* thd2; + + char *Ptr; + + int CompletedActivityCount = 0;; + + int PacketLength = sizeof(TaskHistoryReplyHeader_Struct); + + for(int i=0; iActivityCount; i++) { + if(CompletedTasks[AdjustedTaskIndex].ActivityDone[i]) { + CompletedActivityCount++; + PacketLength = PacketLength + sizeof(TaskHistoryReplyData1_Struct) + + strlen(Task->Activity[i].Text1) + 1 + + strlen(Task->Activity[i].Text2) + 1 + + sizeof(TaskHistoryReplyData2_Struct) + + strlen(Task->Activity[i].Text3) + 1; + } + } + + + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TaskHistoryReply, PacketLength); + + ths = (TaskHistoryReplyHeader_Struct*)outapp->pBuffer; + + // We use the TaskIndex the client sent in the request + ths->TaskID = TaskIndex; + + ths->ActivityCount = CompletedActivityCount; + + Ptr = (char *) ths + sizeof(TaskHistoryReplyHeader_Struct); + + for(int i=0; iActivityCount; i++) { + if(CompletedTasks[AdjustedTaskIndex].ActivityDone[i]) { + thd1 = (TaskHistoryReplyData1_Struct*)Ptr; + thd1->ActivityType = Task->Activity[i].Type; + Ptr = (char *)thd1 + sizeof(TaskHistoryReplyData1_Struct); + sprintf(Ptr, Task->Activity[i].Text1); + Ptr = Ptr + strlen(Ptr) + 1; + sprintf(Ptr, Task->Activity[i].Text2); + Ptr = Ptr + strlen(Ptr) + 1; + thd2 = (TaskHistoryReplyData2_Struct*)Ptr; + thd2->GoalCount = Task->Activity[i].GoalCount; + thd2->unknown04 = 0xffffffff; + thd2->unknown08 = 0xffffffff; + thd2->ZoneID = Task->Activity[i].ZoneID; + thd2->unknown16 = 0x00000000; + Ptr = (char *)thd2 + sizeof(TaskHistoryReplyData2_Struct); + sprintf(Ptr, Task->Activity[i].Text3); + Ptr = Ptr + strlen(Ptr) + 1; + } + } + + _pkt(TASKS__PACKETS, outapp); + + c->QueuePacket(outapp); + safe_delete(outapp); + + + +} + +void Client::SendTaskActivityComplete(int TaskID, int ActivityID, int TaskIndex, int TaskIncomplete) { + + // 0x54eb + + TaskActivityComplete_Struct* tac; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TaskActivityComplete, sizeof(TaskActivityComplete_Struct)); + + tac = (TaskActivityComplete_Struct*)outapp->pBuffer; + + //tac->unknown1 = 0x00000000; + tac->TaskIndex = TaskIndex; + tac->unknown2 = 0x00000002; + //tac->unknown3 = 0x00000000; + tac->unknown3 = TaskID; // Correct ? + tac->ActivityID = ActivityID; + tac->unknown4 = 0x00000001; + //tac->unknown5 = 0x00000001; + tac->unknown5 = TaskIncomplete; + + _pkt(TASKS__PACKETS, outapp); + + QueuePacket(outapp); + safe_delete(outapp); +} + + +void Client::SendTaskFailed(int TaskID, int TaskIndex) { + + // 0x54eb + char buf[24]; + snprintf(buf, 23, "%d", TaskID); + buf[23] = '\0'; + parse->EventPlayer(EVENT_TASK_FAIL, this, buf, 0); + + TaskActivityComplete_Struct* tac; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TaskActivityComplete, sizeof(TaskActivityComplete_Struct)); + + tac = (TaskActivityComplete_Struct*)outapp->pBuffer; + + //tac->unknown1 = 0x00000000; + tac->TaskIndex = TaskIndex; + tac->unknown2 = 0x00000002; + //tac->unknown3 = 0x00000000; + tac->unknown3 = TaskID; // Correct ? + tac->ActivityID = 0; + tac->unknown4 = 0x00000000; //Fail + //tac->unknown5 = 0x00000001; + tac->unknown5 = 0; // 0 for task complete or failed. + + _log(TASKS__UPDATE, "TaskFailed"); + //DumpPacket(outapp); fflush(stdout); + _pkt(TASKS__PACKETS, outapp); + + QueuePacket(outapp); + safe_delete(outapp); +} + +void TaskManager::SendCompletedTasksToClient(Client *c, ClientTaskState *State) { + + int PacketLength = 4; + + + + //vector::const_iterator iterator; + + // The client only display the first 50 Completed Tasks send, so send the 50 most recent + int FirstTaskToSend = 0; + int LastTaskToSend = State->CompletedTasks.size(); + + if(State->CompletedTasks.size() > 50) + FirstTaskToSend = State->CompletedTasks.size() - 50; + + _log(TASKS__UPDATE, "Completed Task Count: %i, First Task to send is %i, Last is %i", + State->CompletedTasks.size(), FirstTaskToSend, LastTaskToSend); + /* + for(iterator=State->CompletedTasks.begin(); iterator!=State->CompletedTasks.end(); iterator++) { + int TaskID = (*iterator).TaskID; + if(Tasks[TaskID] == NULL) continue; + PacketLength = PacketLength + 8 + strlen(Tasks[TaskID]->Title) + 1; + } + */ + for(int i = FirstTaskToSend; iCompletedTasks[i].TaskID; + if(Tasks[TaskID] == NULL) continue; + PacketLength = PacketLength + 8 + strlen(Tasks[TaskID]->Title) + 1; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_CompletedTasks, PacketLength); + char *buf = (char*)outapp->pBuffer; + + //*(uint32 *)buf = State->CompletedTasks.size(); + *(uint32 *)buf = LastTaskToSend - FirstTaskToSend; + buf = buf + 4; + //for(iterator=State->CompletedTasks.begin(); iterator!=State->CompletedTasks.end(); iterator++) { + // int TaskID = (*iterator).TaskID; + for(int i = FirstTaskToSend; iCompletedTasks[i].TaskID; + if(Tasks[TaskID] == NULL) continue; + *(uint32 *)buf = TaskID; + buf = buf + 4; + + sprintf(buf, "%s", Tasks[TaskID]->Title); + buf = buf + strlen(buf) + 1; + //*(uint32 *)buf = (*iterator).CompletedTime; + *(uint32 *)buf = State->CompletedTasks[i].CompletedTime; + buf = buf + 4; + } + + _pkt(TASKS__PACKETS, outapp); + + c->QueuePacket(outapp); + safe_delete(outapp); +} + + + +void TaskManager::SendTaskActivityShort(Client *c, int TaskID, int ActivityID, int ClientTaskIndex) +{ + // This Activity Packet is sent for activities that have not yet been unlocked and appear as ??? + // in the client. + + TaskActivityShort_Struct* tass; + + if(c->GetClientVersionBit() & BIT_RoFAndLater) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TaskActivity, 25); + outapp->WriteUInt32(ClientTaskIndex); + outapp->WriteUInt32(2); + outapp->WriteUInt32(TaskID); + outapp->WriteUInt32(ActivityID); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0xffffffff); + outapp->WriteUInt8(0); + c->FastQueuePacket(&outapp); + + return; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TaskActivity, sizeof(TaskActivityShort_Struct)); + + tass = (TaskActivityShort_Struct*)outapp->pBuffer; + + tass->TaskSequenceNumber = ClientTaskIndex; + tass->unknown2 = 0x00000002; + tass->TaskID = TaskID; + tass->ActivityID = ActivityID; + tass->unknown3 = 0x000000; + tass->ActivityType = 0xffffffff; + tass->unknown4 = 0x00000000; + + _pkt(TASKS__PACKETS, outapp); + + c->QueuePacket(outapp); + safe_delete(outapp); +} + + + +void TaskManager::SendTaskActivityLong(Client *c, int TaskID, int ActivityID, int ClientTaskIndex, bool Optional, bool TaskComplete) { + + if (c->GetClientVersion() >= EQClientRoF) + { + SendTaskActivityNew(c, TaskID, ActivityID, ClientTaskIndex, Optional, TaskComplete); + return; + } + + char *Ptr; + + TaskActivityHeader_Struct* tah; + TaskActivityData1_Struct* tad1; + TaskActivityTrailer_Struct* tat; + + long PacketLength = sizeof(TaskActivityHeader_Struct) + +sizeof(TaskActivityData1_Struct) + sizeof(TaskActivityTrailer_Struct); + PacketLength = PacketLength + strlen(Tasks[TaskID]->Activity[ActivityID].Text1) + 1 + + strlen(Tasks[TaskID]->Activity[ActivityID].Text2) + 1 + + strlen(Tasks[TaskID]->Activity[ActivityID].Text3) + 1; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TaskActivity, PacketLength); + + tah = (TaskActivityHeader_Struct*)outapp->pBuffer; + + tah->TaskSequenceNumber = ClientTaskIndex; + tah->unknown2 = 0x00000002; + tah->TaskID = TaskID; + tah->ActivityID = ActivityID; + tah->unknown3 = 0x00000000; + + // We send our 'internal' types as ActivityUse1. text3 should be set to the activity description, so it makes + // no difference to the client. All activity updates will be done based on our interal activity types. + if((Tasks[TaskID]->Activity[ActivityID].Type > 0) && Tasks[TaskID]->Activity[ActivityID].Type < 100) + tah->ActivityType = Tasks[TaskID]->Activity[ActivityID].Type; + else + tah->ActivityType = ActivityUse1; + + tah->Optional = Optional; + tah->unknown5 = 0x00000000; + // One of these unknown fields maybe related to the 'Use On' activity types + Ptr = (char *) tah + sizeof(TaskActivityHeader_Struct); + sprintf(Ptr, "%s", Tasks[TaskID]->Activity[ActivityID].Text1); + Ptr = Ptr + strlen(Ptr) + 1; + + sprintf(Ptr, "%s", Tasks[TaskID]->Activity[ActivityID].Text2); + Ptr = Ptr + strlen(Ptr) + 1; + + tad1 = (TaskActivityData1_Struct*)Ptr; + tat = (TaskActivityTrailer_Struct*)Ptr; + + if(Tasks[TaskID]->Activity[ActivityID].Type != ActivityGiveCash) + tad1->GoalCount = Tasks[TaskID]->Activity[ActivityID].GoalCount; + else + // For our internal type GiveCash, where the goal count has the amount of cash that must be given, + // we don't want the donecount and goalcount fields cluttered up with potentially large numbers, so we just + // send a goalcount of 1, and a bit further down, a donecount of 1 if the activity is complete, 0 otherwise. + // The text3 field should decribe the exact activity goal, e.g. give 3500gp to Hasten Bootstrutter. + tad1->GoalCount = 1; + + tad1->unknown1 = 0xffffffff; + if(!TaskComplete) tad1->unknown2 = 0xffffffff; + else + tad1->unknown2 = 0xcaffffff; + + tad1->ZoneID = Tasks[TaskID]->Activity[ActivityID].ZoneID; + tad1->unknown3 = 0x00000000; + + Ptr = (char *) tad1 + sizeof(TaskActivityData1_Struct); + sprintf(Ptr, "%s", Tasks[TaskID]->Activity[ActivityID].Text3); + Ptr = Ptr + strlen(Ptr) + 1; + + tat = (TaskActivityTrailer_Struct*)Ptr; + + if(Tasks[TaskID]->Activity[ActivityID].Type != ActivityGiveCash) + tat->DoneCount = c->GetTaskActivityDoneCount(ClientTaskIndex, ActivityID); + else + // For internal activity types, DoneCount is either 1 if the activity is complete, 0 otherwise. + tat->DoneCount = (c->GetTaskActivityDoneCount(ClientTaskIndex, ActivityID) >= Tasks[TaskID]->Activity[ActivityID].GoalCount); + + tat->unknown1 = 0x00000001; + + _pkt(TASKS__PACKETS, outapp); + + c->QueuePacket(outapp); + safe_delete(outapp); + +} + +// Used only by RoF+ Clients +void TaskManager::SendTaskActivityNew(Client *c, int TaskID, int ActivityID, int ClientTaskIndex, bool Optional, bool TaskComplete) { + + uint32 String2Len = 3; + if(TaskComplete) + String2Len = 4; + + long PacketLength = 29 + 4 + 8 + 4 + 4 + 5; + PacketLength = PacketLength + strlen(Tasks[TaskID]->Activity[ActivityID].Text1) + 1 + + strlen(Tasks[TaskID]->Activity[ActivityID].Text2) + 1 + + strlen(Tasks[TaskID]->Activity[ActivityID].Text3) + 1 + + ((strlen(itoa(Tasks[TaskID]->Activity[ActivityID].ZoneID)) + 1) * 2) + + 3 + String2Len; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TaskActivity, PacketLength); + + outapp->WriteUInt32(ClientTaskIndex); // TaskSequenceNumber + outapp->WriteUInt32(2); // unknown2 + outapp->WriteUInt32(TaskID); + outapp->WriteUInt32(ActivityID); + outapp->WriteUInt32(0); // unknown3 + + // We send our 'internal' types as ActivityUse1. text3 should be set to the activity description, so it makes + // no difference to the client. All activity updates will be done based on our interal activity types. + if((Tasks[TaskID]->Activity[ActivityID].Type > 0) && Tasks[TaskID]->Activity[ActivityID].Type < 100) + outapp->WriteUInt32(Tasks[TaskID]->Activity[ActivityID].Type); + else + outapp->WriteUInt32(ActivityUse1); + + outapp->WriteUInt32(Optional); + outapp->WriteUInt8(0); // unknown5 + + // One of these unknown fields maybe related to the 'Use On' activity types + outapp->WriteString(Tasks[TaskID]->Activity[ActivityID].Text1); + + outapp->WriteUInt32((strlen(Tasks[TaskID]->Activity[ActivityID].Text2) + 1)); // String Length - Add in null terminator + outapp->WriteString(Tasks[TaskID]->Activity[ActivityID].Text2); + + // Goal Count + if(Tasks[TaskID]->Activity[ActivityID].Type != ActivityGiveCash) + outapp->WriteUInt32(Tasks[TaskID]->Activity[ActivityID].GoalCount); + else + outapp->WriteUInt32(1); // GoalCount + + outapp->WriteUInt32(3); // String Length - Add in null terminator + outapp->WriteString("-1"); + + if(!TaskComplete) { + outapp->WriteUInt32(3); // String Length - Add in null terminator + outapp->WriteString("-1"); + } + else + { + outapp->WriteUInt32(4); // String Length - Add in null terminator + outapp->WriteString("-54"); + } + + outapp->WriteString(itoa(Tasks[TaskID]->Activity[ActivityID].ZoneID)); + outapp->WriteUInt32(0); // unknown7 + + outapp->WriteString(Tasks[TaskID]->Activity[ActivityID].Text3); + + if(Tasks[TaskID]->Activity[ActivityID].Type != ActivityGiveCash) + outapp->WriteUInt32(c->GetTaskActivityDoneCount(ClientTaskIndex, ActivityID)); // DoneCount + else + // For internal activity types, DoneCount is either 1 if the activity is complete, 0 otherwise. + outapp->WriteUInt32((c->GetTaskActivityDoneCount(ClientTaskIndex, ActivityID) >= Tasks[TaskID]->Activity[ActivityID].GoalCount)); + + outapp->WriteUInt8(1); // unknown9 + + outapp->WriteString(itoa(Tasks[TaskID]->Activity[ActivityID].ZoneID)); + + _pkt(TASKS__PACKETS, outapp); + + c->QueuePacket(outapp); + safe_delete(outapp); + +} + +void TaskManager::SendActiveTasksToClient(Client *c, bool TaskComplete) { + + //for(int TaskIndex=0; TaskIndexGetActiveTaskCount(); TaskIndex++) { + for(int TaskIndex=0; TaskIndexGetActiveTaskID(TaskIndex); + if((TaskID==0) || (Tasks[TaskID] ==0)) continue; + int StartTime = c->GetTaskStartTime(TaskIndex); + + SendActiveTaskDescription(c, TaskID, TaskIndex, StartTime, Tasks[TaskID]->Duration, false); + _log(TASKS__UPDATE, "SendActiveTasksToClient: Task %i, Activities: %i", TaskID, GetActivityCount(TaskID)); + + int Sequence = 0; + for(int Activity=0; ActivityGetTaskActivityState(TaskIndex, Activity) != ActivityHidden) { + _log(TASKS__UPDATE, " Long: %i, %i, %i Complete=%i", TaskID, Activity, TaskIndex, TaskComplete); + if(Activity==GetActivityCount(TaskID)-1) + SendTaskActivityLong(c, TaskID, Activity, TaskIndex, + Tasks[TaskID]->Activity[Activity].Optional, + TaskComplete); + else + SendTaskActivityLong(c, TaskID, Activity, TaskIndex, + Tasks[TaskID]->Activity[Activity].Optional, 0); + } + else { + _log(TASKS__UPDATE, " Short: %i, %i, %i", TaskID, Activity, TaskIndex); + SendTaskActivityShort(c, TaskID, Activity, TaskIndex); + } + Sequence++; + } + + + } +} + + +void TaskManager::SendSingleActiveTaskToClient(Client *c, int TaskIndex, bool TaskComplete, bool BringUpTaskJournal) { + + if((TaskIndex < 0) || (TaskIndex >= MAXACTIVETASKS)) return; + + int TaskID = c->GetActiveTaskID(TaskIndex); + + if((TaskID==0) || (Tasks[TaskID] ==0)) return; + + int StartTime = c->GetTaskStartTime(TaskIndex); + SendActiveTaskDescription(c, TaskID, TaskIndex, StartTime, Tasks[TaskID]->Duration, BringUpTaskJournal); + _log(TASKS__UPDATE, "SendSingleActiveTasksToClient: Task %i, Activities: %i", TaskID, GetActivityCount(TaskID)); + + int Sequence = 0; + + for(int Activity=0; ActivityGetTaskActivityState(TaskIndex, Activity) != ActivityHidden) { + _log(TASKS__UPDATE, " Long: %i, %i, %i Complete=%i", TaskID, Activity, TaskIndex, TaskComplete); + if(Activity==GetActivityCount(TaskID)-1) + SendTaskActivityLong(c, TaskID, Activity, TaskIndex, + Tasks[TaskID]->Activity[Activity].Optional, TaskComplete); + else + SendTaskActivityLong(c, TaskID, Activity, TaskIndex, + Tasks[TaskID]->Activity[Activity].Optional, 0); + } + else { + _log(TASKS__UPDATE, " Short: %i, %i, %i", TaskID, Activity, TaskIndex); + SendTaskActivityShort(c, TaskID, Activity, TaskIndex); + } + Sequence++; + } +} + +void TaskManager::SendActiveTaskDescription(Client *c, int TaskID, int SequenceNumber, int StartTime, int Duration, bool BringUpTaskJournal) { + + + if((TaskID<1) || (TaskID>=MAXTASKS) || !Tasks[TaskID]) return; + + int PacketLength = sizeof(TaskDescriptionHeader_Struct) + strlen(Tasks[TaskID]->Title) + 1 + + sizeof(TaskDescriptionData1_Struct) + strlen(Tasks[TaskID]->Description) + 1 + + sizeof(TaskDescriptionData2_Struct) + 1 + sizeof(TaskDescriptionTrailer_Struct); + + string RewardText; + int ItemID = 0; + + // If there is an item make the Reward text into a link to the item (only the first item if a list + // is specified). I have been unable to get multiple item links to work. + // + if(Tasks[TaskID]->RewardID) { + // If the reward is a list of items, and the first entry on the list is valid + if(Tasks[TaskID]->RewardMethod==METHODSINGLEID) { + ItemID = Tasks[TaskID]->RewardID; + } + else if(Tasks[TaskID]->RewardMethod==METHODLIST) { + ItemID = GoalListManager.GetFirstEntry(Tasks[TaskID]->RewardID); + if(ItemID < 0) + ItemID = 0; + } + if(ItemID) { + char *RewardTmp = 0; + if(strlen(Tasks[TaskID]->Reward) != 0) { + + switch(c->GetClientVersion()) { + + case EQClient62: + { + MakeAnyLenString(&RewardTmp, "%c%07i-00001-00001-00001-00001-000013E0ABA6B%s%c", + 0x12, ItemID, Tasks[TaskID]->Reward,0x12); + break; + } + case EQClientTitanium: + { + MakeAnyLenString(&RewardTmp, "%c%06X000000000000000000000000000000014505DC2%s%c", + 0x12, ItemID, Tasks[TaskID]->Reward,0x12); + break; + } + case EQClientRoF: + { + MakeAnyLenString(&RewardTmp, "%c%06X0000000000000000000000000000000000000000014505DC2%s%c", + 0x12, ItemID, Tasks[TaskID]->Reward,0x12); + break; + } + default: + { + // All clients after Titanium + MakeAnyLenString(&RewardTmp, "%c%06X00000000000000000000000000000000000014505DC2%s%c", + 0x12, ItemID, Tasks[TaskID]->Reward,0x12); + } + } + + } + else { + const Item_Struct *Item = database.GetItem(ItemID); + + if(Item) { + + switch(c->GetClientVersion()) { + + case EQClient62: + { + MakeAnyLenString(&RewardTmp, "%c%07i-00001-00001-00001-00001-000013E0ABA6B%s%c", + 0x12, ItemID, Item->Name,0x12); + break; + } + case EQClientTitanium: + { + MakeAnyLenString(&RewardTmp, "%c%06X000000000000000000000000000000014505DC2%s%c", + 0x12, ItemID, Item->Name ,0x12); + break; + } + case EQClientRoF: + { + MakeAnyLenString(&RewardTmp, "%c%06X0000000000000000000000000000000000000000014505DC2%s%c", + 0x12, ItemID, Item->Name ,0x12); + break; + } + default: + { + // All clients after Titanium + MakeAnyLenString(&RewardTmp, "%c%06X00000000000000000000000000000000000014505DC2%s%c", + 0x12, ItemID, Item->Name ,0x12); + } + } + } + } + + if(RewardTmp) RewardText += RewardTmp; + safe_delete_array(RewardTmp); + } + else { + RewardText += Tasks[TaskID]->Reward; + } + + } + else { + RewardText += Tasks[TaskID]->Reward; + } + PacketLength += strlen(RewardText.c_str()) + 1; + + char *Ptr; + TaskDescriptionHeader_Struct* tdh; + TaskDescriptionData1_Struct* tdd1; + TaskDescriptionData2_Struct* tdd2; + TaskDescriptionTrailer_Struct* tdt; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TaskDescription, PacketLength); + + tdh = (TaskDescriptionHeader_Struct*)outapp->pBuffer; + + tdh->SequenceNumber = SequenceNumber; + tdh->TaskID = TaskID; + + if(BringUpTaskJournal) + tdh->unknown2 = 0x00000201; + else + tdh->unknown2 = 0x00000200; + //tdh->unknown2 = 0x00000100; // This makes the Task Description have an S instead of Q, but the description doesn't show + + tdh->unknown3 = 0x00000000; + tdh->unknown4 = 0x00; + + Ptr = (char *) tdh + sizeof(TaskDescriptionHeader_Struct); + + sprintf(Ptr, "%s", Tasks[TaskID]->Title); + Ptr = Ptr + strlen(Ptr) + 1; + + tdd1 = (TaskDescriptionData1_Struct*)Ptr; + + tdd1->Duration = Duration; + tdd1->unknown2 = 0x00000000; + + tdd1->StartTime = StartTime; + + Ptr = (char *) tdd1 + sizeof(TaskDescriptionData1_Struct); + + sprintf(Ptr, "%s", Tasks[TaskID]->Description); + Ptr = Ptr + strlen(Ptr) + 1; + + tdd2 = (TaskDescriptionData2_Struct*)Ptr; + + // This next field may not be a reward count. It is always 1 in the packets I have seen. Setting it to 2 and trying + // to include multiple item links has so far proven futile. Setting it to 0 ends the packet after the next byte. + tdd2->RewardCount = 1; + + if(Tasks[TaskID]->XPReward) + tdd2->unknown1 = 0x00000100; + else + tdd2->unknown1 = 0x00000000; + + tdd2->unknown2 = 0x00000000; + tdd2->unknown3 = 0x0000; + Ptr = (char *) tdd2 + sizeof(TaskDescriptionData2_Struct); + + sprintf(Ptr, "%s", RewardText.c_str()); + Ptr = Ptr + strlen(Ptr) + 1; + + tdt = (TaskDescriptionTrailer_Struct*)Ptr; + tdt->Points = 0x00000000; // Points Count + + _pkt(TASKS__PACKETS, outapp); + + c->QueuePacket(outapp); + safe_delete(outapp); + +} + +bool ClientTaskState::IsTaskActivityCompleted(int index, int ActivityID) { + + return (ActiveTasks[index].Activity[ActivityID].State == ActivityCompleted); +} + +ActivityState ClientTaskState::GetTaskActivityState(int index, int ActivityID) { + + + return ActiveTasks[index].Activity[ActivityID].State; +} + +int ClientTaskState::GetTaskActivityDoneCount(int index, int ActivityID) { + + return ActiveTasks[index].Activity[ActivityID].DoneCount; + +} + +int ClientTaskState::GetTaskActivityDoneCountFromTaskID(int TaskID, int ActivityID){ + int ActiveTaskIndex = -1; + for(int i=0; ipBuffer; + cts->SequenceNumber = SequenceNumber; + cts->unknown4 = 0x00000002; + + _log(TASKS__UPDATE, "CancelTask"); + _pkt(TASKS__PACKETS, outapp); + + c->QueuePacket(outapp); + safe_delete(outapp); + + if(RemoveFromDB) + RemoveTask(c, SequenceNumber); +} + +void ClientTaskState::RemoveTask(Client *c, int SequenceNumber) { + + int CharacterID = c->CharacterID(); + + 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); + return; + } + _log(TASKS__UPDATE, "CancelTask: %s", query); + safe_delete_array(query); + if(!database.RunQuery(query,MakeAnyLenString(&query, TaskQuery, + CharacterID, + ActiveTasks[SequenceNumber].TaskID), errbuf)) { + + LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s", errbuf); + } + + _log(TASKS__UPDATE, "CancelTask: %s", query); + safe_delete_array(query); + + ActiveTasks[SequenceNumber].TaskID = TASKSLOTEMPTY; + ActiveTaskCount--; +} + + +void ClientTaskState::AcceptNewTask(Client *c, int TaskID, int NPCID) { + + if(!taskmanager || TaskID<0 || TaskID>=MAXTASKS) { + c->Message(13, "Task system not functioning, or TaskID %i out of range.", TaskID); + return; + + } + + if(taskmanager->Tasks[TaskID] == NULL) { + c->Message(13, "Invalid TaskID %i", TaskID); + return; + } + + if(ActiveTaskCount==MAXACTIVETASKS) { + c->Message(13, "You already have the maximum allowable number of active tasks (%i)", MAXACTIVETASKS); + return; + } + + for(int i=0; iMessage(13, "You have already been assigned this task."); + return; + } + } + + if(!taskmanager->IsTaskRepeatable(TaskID) && IsTaskCompleted(TaskID)) return; + + // We do it this way, because when the Client cancels a task, it retains the sequence number of the remaining + // tasks in it's window, until something causes the TaskDescription packets to be sent again. We could just + // resend all the active task data to the client when it cancels a task, but that could be construed as a + // waste of bandwidth. + // + int FreeSlot = -1; + for(int i=0; iMessage(13, "You already have the maximum allowable number of active tasks (%i)", MAXACTIVETASKS); + return; + } + + + ActiveTasks[FreeSlot].TaskID = TaskID; + ActiveTasks[FreeSlot].AcceptedTime = time(NULL); + ActiveTasks[FreeSlot].Updated = true; + ActiveTasks[FreeSlot].CurrentStep = -1; + + for(int i=0; iTasks[TaskID]->ActivityCount; i++) { + ActiveTasks[FreeSlot].Activity[i].ActivityID = i; + ActiveTasks[FreeSlot].Activity[i].DoneCount = 0; + ActiveTasks[FreeSlot].Activity[i].State = ActivityHidden; + ActiveTasks[FreeSlot].Activity[i].Updated = true; + } + UnlockActivities(c->CharacterID(), FreeSlot); + ActiveTaskCount++; + taskmanager->SendSingleActiveTaskToClient(c, FreeSlot, false, true); + c->Message(0, "You have been assigned the task '%s'.", taskmanager->Tasks[TaskID]->Title); + + char *buf = 0; + MakeAnyLenString(&buf, "%d", TaskID); + + NPC *npc = entity_list.GetID(NPCID)->CastToNPC(); + if(!npc) { + c->Message(clientMessageYellow, "Task Giver ID is %i", NPCID); + c->Message(clientMessageError, "Unable to find NPC to send EVENT_TASKACCEPTD to. Report this bug."); + safe_delete_array(buf); + return; + } + taskmanager->SaveClientState(c, this); + parse->EventNPC(EVENT_TASKACCEPTED, npc, c, buf, 0); + safe_delete_array(buf); + +} + +void ClientTaskState::ProcessTaskProximities(Client *c, float X, float Y, float Z) { + + float LastX = c->ProximityX(); + float LastY = c->ProximityY(); + float LastZ = c->ProximityZ(); + + //_log(TASKS__PROXIMITY, "Checing proximities for Position %8.3f, %8.3f, %8.3f Last: %8.3f, %8.3f, %8.3f\n", X, Y, Z, LastX, LastY, LastZ); + if((LastX==X) && (LastY==Y) && (LastZ==Z)) return; + + _log(TASKS__PROXIMITY, "Checing proximities for Position %8.3f, %8.3f, %8.3f\n", X, Y, Z); + int ExploreID = taskmanager->ProximityManager.CheckProximities(X, Y, Z); + + if(ExploreID>0) { + _log(TASKS__PROXIMITY, "Position %8.3f, %8.3f, %8.3f is within proximity %i\n", X, Y, Z, ExploreID); + UpdateTasksOnExplore(c, ExploreID); + } +} + + + + +TaskGoalListManager::TaskGoalListManager() { + + TaskGoalLists = NULL; + NumberOfLists = 0; + +} + +TaskGoalListManager::~TaskGoalListManager() { + + for(int i=0; i< NumberOfLists; i++) { + + safe_delete_array(TaskGoalLists[i].GoalItemEntries); + + } + safe_delete_array(TaskGoalLists); +} + +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++) { + + safe_delete_array(TaskGoalLists[i].GoalItemEntries); + + } + safe_delete_array(TaskGoalLists); + + 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); + return false; + } + + 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; + +} + +int TaskGoalListManager::GetListByID(int ListID) { + + // Find the list with the specified ListID and return the index + + int FirstEntry = 0; + int LastEntry = NumberOfLists - 1; + + while(FirstEntry <= LastEntry) { + int MiddleEntry = (FirstEntry + LastEntry) / 2; + + if(ListID > TaskGoalLists[MiddleEntry].ListID) + FirstEntry = MiddleEntry + 1; + else if(ListID < TaskGoalLists[MiddleEntry].ListID) + LastEntry = MiddleEntry - 1; + else + return MiddleEntry; + + } + + return -1; + +} + +int TaskGoalListManager::GetFirstEntry(int ListID) { + + int ListIndex = GetListByID(ListID); + + if((ListIndex < 0) || (ListIndex >= NumberOfLists)) return -1; + + if(TaskGoalLists[ListIndex].Size == 0) return -1; + + return TaskGoalLists[ListIndex].GoalItemEntries[0]; +} + +vector TaskGoalListManager::GetListContents(int ListID) { + + vector ListContents; + + int ListIndex = GetListByID(ListID); + + if((ListIndex < 0) || (ListIndex >= NumberOfLists)) return ListContents; + + for(int i=0; i= NumberOfLists)) return false; + + if((Entry < TaskGoalLists[ListIndex].Min) || + (Entry > TaskGoalLists[ListIndex].Max)) + return false; + + int FirstEntry = 0; + int LastEntry = TaskGoalLists[ListIndex].Size - 1; + + while(FirstEntry <= LastEntry) { + int MiddleEntry = (FirstEntry + LastEntry) / 2; + + if(Entry > TaskGoalLists[ListIndex].GoalItemEntries[MiddleEntry]) + FirstEntry = MiddleEntry + 1; + else if(Entry < TaskGoalLists[ListIndex].GoalItemEntries[MiddleEntry]) + LastEntry = MiddleEntry - 1; + else { + _log(TASKS__UPDATE, "TaskGoalListManager::IsInList(%i, %i) returning true", ListIndex, Entry); + return true; + } + + } + + return false; + +} + +TaskProximityManager::TaskProximityManager() { + + +} + +TaskProximityManager::~TaskProximityManager() { + + +} + +bool TaskProximityManager::LoadProximities(int ZoneID) { + + 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); + 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); + return false; + } + safe_delete_array(query); + + return true; + +} + +int TaskProximityManager::CheckProximities(float X, float Y, float Z) { + + for(unsigned int i=0; iMinX, P->MaxX, P->MinY, P->MaxY, P->MinZ, P->MaxZ); + + if(X < P->MinX || X > P->MaxX || Y < P->MinY || Y > P->MaxY || + Z < P->MinZ || Z > P->MaxZ) continue; + + return P->ExploreID; + + } + + return 0; +} + + diff --git a/zone/tasks.h b/zone/tasks.h new file mode 100644 index 000000000..8397f5c5e --- /dev/null +++ b/zone/tasks.h @@ -0,0 +1,262 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2004 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 TASKS_H +#define TASKS_H + +#include "../common/types.h" +#include "mob.h" +#include +#include + +using namespace std; + + +#define MAXTASKS 10000 +#define MAXTASKSETS 1000 +// The Client has a hard cap of 19 active tasks +#define MAXACTIVETASKS 19 +// The Max Chooser (Task Selector entries) is capped at 40 in the Titanium Client. +#define MAXCHOOSERENTRIES 40 +// The Client has a hard cap of 20 activities per task. +#define MAXACTIVITIESPERTASK 20 +// This is used to determine if a client's active task slot is empty. +#define TASKSLOTEMPTY 0 + +// Command Codes for worldserver ServerOP_ReloadTasks +// +#define RELOADTASKS 0 +#define RELOADTASKGOALLISTS 1 +#define RELOADTASKPROXIMITIES 2 +#define RELOADTASKSETS 3 + +class Client; + +struct TaskGoalList_Struct { + int ListID; + int Size; + int Min, Max; + int *GoalItemEntries; +}; + +// This is used for handling lists, loading them from the database, searching them. +// Used for lists of NPCs to kill, items to loot, etc, as well as lists of items to +// reward the player with on completion of the task. +// +class TaskGoalListManager { + +public: + TaskGoalListManager(); + ~TaskGoalListManager(); + bool LoadLists(); + int GetListByID(int ListID); + bool IsInList(int ListID, int Entry); + int GetFirstEntry(int ListID); + vector GetListContents(int ListIndex); + +private: + + TaskGoalList_Struct *TaskGoalLists; + int NumberOfLists; +}; + +typedef struct { + int ExploreID; + float MinX, MaxX, MinY, MaxY, MinZ, MaxZ; +} TaskProximity; + +// This class is used for managing proximities so that Quest NPC proximities don't need to be used. +class TaskProximityManager { + +public: + TaskProximityManager(); + ~TaskProximityManager(); + bool LoadProximities(int ZoneID); + int CheckProximities(float X, float Y, float Z); + +private: + vector TaskProximities; +}; + +typedef enum { METHODSINGLEID = 0, METHODLIST = 1, METHODQUEST = 2 } TaskMethodType; + +struct ActivityInformation { + int StepNumber; + int Type; + char *Text1; + char *Text2; + char *Text3; + int GoalID; + TaskMethodType GoalMethod; + int GoalCount; + int DeliverToNPC; + int ZoneID; + bool Optional; +}; + +typedef enum { ActivitiesSequential = 0, ActivitiesStepped = 1 } SequenceType; + +struct TaskInformation { + int Duration; + char *Title; + char *Description; + char *Reward; + int RewardID; + int CashReward; // Expressed in copper + int XPReward; + TaskMethodType RewardMethod; + int StartZone; + int ActivityCount; + SequenceType SequenceMode; + int LastStep; + short MinLevel; + short MaxLevel; + bool Repeatable; + ActivityInformation Activity[MAXACTIVITIESPERTASK]; +}; + +typedef enum { ActivityHidden = 0, ActivityActive = 1, ActivityCompleted = 2 } ActivityState; + +typedef enum { ActivityDeliver = 1, ActivityKill = 2, ActivityLoot = 3, ActivitySpeakWith = 4, ActivityExplore = 5, + ActivityTradeSkill = 6, ActivityFish = 7, ActivityForage = 8, ActivityUse1 = 9, ActivityUse2 = 10, + ActivityTouch = 11, ActivityGiveCash = 100 } ActivityType; + + +struct ClientActivityInformation { + int ActivityID; + int DoneCount; + ActivityState State; + bool Updated; // Flag so we know if we need to update the database +}; + +struct ClientTaskInformation { + int TaskID; + int CurrentStep; + int AcceptedTime; + bool Updated; + ClientActivityInformation Activity[MAXACTIVITIESPERTASK]; +}; + +struct CompletedTaskInformation { + int TaskID; + int CompletedTime; + bool ActivityDone[MAXACTIVITIESPERTASK]; +}; + +class ClientTaskState { + +public: + ClientTaskState(); + ~ClientTaskState(); + void ShowClientTasks(Client *c); + inline int GetActiveTaskCount() { return ActiveTaskCount; } + int GetActiveTaskID(int index); + bool IsTaskActivityCompleted(int index, int ActivityID); + int GetTaskActivityDoneCount(int index, int ActivityID); + int GetTaskActivityDoneCountFromTaskID(int TaskID, int ActivityID); + int GetTaskStartTime(int index); + void AcceptNewTask(Client *c, int TaskID, int NPCID); + void FailTask(Client *c, int TaskID); + int TaskTimeLeft(int TaskID); + int IsTaskCompleted(int TaskID); + bool IsTaskActive(int TaskID); + bool IsTaskActivityActive(int TaskID, int ActivityID); + ActivityState GetTaskActivityState(int index, int ActivityID); + void UpdateTaskActivity(Client *c, int TaskID, int ActivityID, int Count); + void ResetTaskActivity(Client *c, int TaskID, int ActivityID); + void CancelTask(Client *c, int SequenceNumber, bool RemoveFromDB = true); + void CancelAllTasks(Client *c); + void RemoveTask(Client *c, int SequenceNumber); + bool UpdateTasksByNPC(Client *c, int ActivityType, int NPCTypeID); + void UpdateTasksOnKill(Client *c, int NPCTypeID); + void UpdateTasksForItem(Client *c, ActivityType Type, int ItemID, int Count=1); + void UpdateTasksOnExplore(Client *c, int ExploreID); + bool UpdateTasksOnSpeakWith(Client *c, int NPCTypeID); + bool UpdateTasksOnDeliver(Client *c, uint32 *Items, int Cash, int NPCTypeID); + void UpdateTasksOnTouch(Client *c, int ZoneID); + void ProcessTaskProximities(Client *c, float X, float Y, float Z); + bool TaskOutOfTime(int Index); + void TaskPeriodicChecks(Client *c); + void SendTaskHistory(Client *c, int TaskIndex); + void RewardTask(Client *c, TaskInformation *Task); + void EnableTask(int CharID, int TaskCount, int *TaskList); + void DisableTask(int CharID, int TaskCount, int *TaskList); + bool IsTaskEnabled(int TaskID); + int EnabledTaskCount(int TaskSetID); + int ActiveSpeakTask(int NPCID); + int ActiveSpeakActivity(int NPCID, int TaskID); + int ActiveTasksInSet(int TaskSetID); + int CompletedTasksInSet(int TaskSetID); + friend class TaskManager; + +private: + bool UnlockActivities(int CharID, int TaskIndex); + void IncrementDoneCount(Client *c, TaskInformation *Task, int TaskIndex, int ActivityID, int Count=1); + int ActiveTaskCount; + ClientTaskInformation ActiveTasks[MAXACTIVETASKS]; + vectorEnabledTasks; + vector CompletedTasks; + int LastCompletedTaskLoaded; + bool CheckedTouchActivities; +}; + + +class TaskManager { + +public: + TaskManager(); + ~TaskManager(); + int GetActivityCount(int TaskID); + bool LoadSingleTask(int TaskID); + bool LoadTasks(int SingleTask=0); + void ReloadGoalLists(); + inline void LoadProximities(int ZoneID) { ProximityManager.LoadProximities(ZoneID); } + bool LoadTaskSets(); + bool LoadClientState(Client *c, ClientTaskState *state); + bool SaveClientState(Client *c, ClientTaskState *state); + void SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *TaskList); + void SendTaskSelectorNew(Client *c, Mob *mob, int TaskCount, int *TaskList); + bool AppropriateLevel(int TaskID, int PlayerLevel); + void TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, int TaskSetID); + void SendActiveTasksToClient(Client *c, bool TaskComplete=false); + void SendSingleActiveTaskToClient(Client *c, int TaskIndex, bool TaskComplete, bool BringUpTaskJournal=false); + void SendTaskActivityShort(Client *c, int TaskID, int ActivityID, int ClientTaskIndex); + void SendTaskActivityLong(Client *c, int TaskID, int ActivityID, int ClientTaskIndex, + bool Optional, bool TaskComplete=false); + void SendTaskActivityNew(Client *c, int TaskID, int ActivityID, int ClientTaskIndex, + bool Optional, bool TaskComplete=false); + void SendCompletedTasksToClient(Client *c, ClientTaskState *state); + void ExplainTask(Client *c, int TaskID); + int FirstTaskInSet(int TaskSet); + int LastTaskInSet(int TaskSet); + int NextTaskInSet(int TaskSet, int TaskID); + bool IsTaskRepeatable(int TaskID); + friend class ClientTaskState; + + +private: + TaskGoalListManager GoalListManager; + TaskProximityManager ProximityManager; + TaskInformation* Tasks[MAXTASKS]; + vector TaskSets[MAXTASKSETS]; + void SendActiveTaskDescription(Client *c, int TaskID, int SequenceNumber, int StartTime, int Duration, bool BringUpTaskJournal=false); + +}; + +#endif diff --git a/zone/titles.cpp b/zone/titles.cpp new file mode 100644 index 000000000..4956e3648 --- /dev/null +++ b/zone/titles.cpp @@ -0,0 +1,437 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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/eq_packet_structs.h" +#include "masterentity.h" +#include "titles.h" +#include "../common/MiscFunctions.h" +#include "worldserver.h" + +extern WorldServer worldserver; + +TitleManager::TitleManager() { +} + +bool TitleManager::LoadTitles() +{ + Titles.clear(); + + TitleEntry Title; + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = NULL; + 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); + } + + safe_delete_array(query); + + while ((row = mysql_fetch_row(result))) { + Title.TitleID = atoi(row[0]); + Title.SkillID = (SkillType) atoi(row[1]); + Title.MinSkillValue = atoi(row[2]); + Title.MaxSkillValue = atoi(row[3]); + Title.MinAAPoints = atoi(row[4]); + Title.MaxAAPoints = atoi(row[5]); + Title.Class = atoi(row[6]); + Title.Gender = atoi(row[7]); + Title.CharID = atoi(row[8]); + Title.Status = atoi(row[9]); + Title.ItemID = atoi(row[10]); + Title.Prefix = row[11]; + Title.Suffix = row[12]; + Title.TitleSet = atoi(row[13]); + Titles.push_back(Title); + } + mysql_free_result(result); + + return(true); +} + +EQApplicationPacket *TitleManager::MakeTitlesPacket(Client *c) +{ + vector::iterator Iterator; + + vector AvailableTitles; + + uint32 Length = 4; + + Iterator = Titles.begin(); + + while(Iterator != Titles.end()) + { + if(!IsClientEligibleForTitle(c, Iterator)) + { + ++Iterator; + continue; + } + + AvailableTitles.push_back((*Iterator)); + + Length += Iterator->Prefix.length() + Iterator->Suffix.length() + 6; + + ++Iterator; + + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendTitleList, Length); + + char *Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, AvailableTitles.size()); + + Iterator = AvailableTitles.begin(); + + while(Iterator != AvailableTitles.end()) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, Iterator->TitleID); + + VARSTRUCT_ENCODE_STRING(Buffer, Iterator->Prefix.c_str()); + + VARSTRUCT_ENCODE_STRING(Buffer, Iterator->Suffix.c_str()); + + ++Iterator; + } + return(outapp); +} + +int TitleManager::NumberOfAvailableTitles(Client *c) +{ + int Count = 0; + + vector::iterator Iterator; + + Iterator = Titles.begin(); + + while(Iterator != Titles.end()) + { + if(IsClientEligibleForTitle(c, Iterator)) + ++Count; + + ++Iterator; + } + + return Count; +} + +string TitleManager::GetPrefix(int TitleID) +{ + vector::iterator Iterator; + + Iterator = Titles.begin(); + + while(Iterator != Titles.end()) + { + if((*Iterator).TitleID == TitleID) + return (*Iterator).Prefix; + + ++Iterator; + } + + return ""; +} + +string TitleManager::GetSuffix(int TitleID) +{ + vector::iterator Iterator; + + Iterator = Titles.begin(); + + while(Iterator != Titles.end()) + { + if((*Iterator).TitleID == TitleID) + return (*Iterator).Suffix; + + Iterator++; + } + + return ""; +} + +bool TitleManager::IsClientEligibleForTitle(Client *c, vector::iterator Title) +{ + if((Title->CharID >= 0) && (c->CharacterID() != static_cast(Title->CharID))) + return false; + + if((Title->Status >= 0) && (c->Admin() < Title->Status)) + return false; + + if((Title->Gender >= 0) && (c->GetBaseGender() != Title->Gender)) + return false; + + if((Title->Class >= 0) && (c->GetBaseClass() != Title->Class)) + return false; + + if((Title->MinAAPoints >= 0) && (c->GetAAPointsSpent() < static_cast(Title->MinAAPoints))) + return false; + + if((Title->MaxAAPoints >= 0) && (c->GetAAPointsSpent() > static_cast(Title->MaxAAPoints))) + return false; + + if(Title->SkillID >= 0) + { + if((Title->MinSkillValue >= 0) + && (c->GetRawSkill(static_cast(Title->SkillID)) < static_cast(Title->MinSkillValue))) + return false; + + if((Title->MaxSkillValue >= 0) + && (c->GetRawSkill(static_cast(Title->SkillID)) > static_cast(Title->MaxSkillValue))) + return false; + + } + + if((Title->ItemID >= 1) && (c->GetInv().HasItem(Title->ItemID, 0, 0xFF) == SLOT_INVALID)) + return false; + + if((Title->TitleSet > 0) && (!c->CheckTitle(Title->TitleSet))) + return false; + + return true; +} + +bool TitleManager::IsNewAATitleAvailable(int AAPoints, int Class) +{ + vector::iterator Iterator; + + Iterator = Titles.begin(); + + while(Iterator != Titles.end()) + { + if((((*Iterator).Class == -1) || ((*Iterator).Class == Class)) && ((*Iterator).MinAAPoints == AAPoints)) + return true; + + ++Iterator; + } + + return false; +} + +bool TitleManager::IsNewTradeSkillTitleAvailable(int SkillID, int SkillValue) +{ + vector::iterator Iterator; + + Iterator = Titles.begin(); + + while(Iterator != Titles.end()) + { + if(((*Iterator).SkillID == SkillID) && ((*Iterator).MinSkillValue == SkillValue)) + return true; + + ++Iterator; + } + + return false; +} + +void TitleManager::CreateNewPlayerTitle(Client *c, const char *Title) +{ + if(!c || !Title) + return; + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = NULL; + MYSQL_RES *result; + + char *EscTitle = new char[strlen(Title) * 2 + 1]; + + 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); + } + + 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); + +} + +void TitleManager::CreateNewPlayerSuffix(Client *c, const char *Suffix) +{ + if(!c || !Suffix) + return; + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = NULL; + MYSQL_RES *result; + + char *EscSuffix = new char[strlen(Suffix) * 2 + 1]; + + 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); + 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); + +} + +void Client::SetAATitle(const char *Title) +{ + strn0cpy(m_pp.title, Title, sizeof(m_pp.title)); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct)); + + SetTitleReply_Struct *strs = (SetTitleReply_Struct *)outapp->pBuffer; + + strn0cpy(strs->title, Title, sizeof(strs->title)); + + strs->entity_id = GetID(); + + entity_list.QueueClients(this, outapp, false); + + safe_delete(outapp); +} + +void Client::SetTitleSuffix(const char *Suffix) +{ + strn0cpy(m_pp.suffix, Suffix, sizeof(m_pp.suffix)); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct)); + + SetTitleReply_Struct *strs = (SetTitleReply_Struct *)outapp->pBuffer; + + strs->is_suffix = 1; + + strn0cpy(strs->title, Suffix, sizeof(strs->title)); + + strs->entity_id = GetID(); + + entity_list.QueueClients(this, outapp, false); + + safe_delete(outapp); +} + +void Client::EnableTitle(int titleset) { + + if (CheckTitle(titleset)) { + return; + } + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + 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) { + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + 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); + } + + else { + LogFile->write(EQEMuLog::Error, "Error in CheckTitle query '%s': %s", query, errbuf); + safe_delete_array(query); + } + + return(false); +} + +void Client::RemoveTitle(int titleset) { + + if (!CheckTitle(titleset)) { + return; + } + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + 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/titles.h b/zone/titles.h new file mode 100644 index 000000000..190b2f445 --- /dev/null +++ b/zone/titles.h @@ -0,0 +1,72 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 TITLES_H +#define TITLES_H + +#include "../common/types.h" +#include +#include + +class Client; +class EQApplicationPacket; + +using namespace std; + +struct TitleEntry +{ + int TitleID; + int SkillID; + int MinSkillValue; + int MaxSkillValue; + int MinAAPoints; + int MaxAAPoints; + int Class; + int Gender; + int CharID; + int Status; + int ItemID; + string Prefix; + string Suffix; + int TitleSet; +}; + +class TitleManager +{ +public: + TitleManager(); + + bool LoadTitles(); + + EQApplicationPacket *MakeTitlesPacket(Client *c); + string GetPrefix(int TitleID); + string GetSuffix(int TitleID); + int NumberOfAvailableTitles(Client *c); + bool IsClientEligibleForTitle(Client *c, vector::iterator Title); + bool IsNewAATitleAvailable(int AAPoints, int Class); + bool IsNewTradeSkillTitleAvailable(int SkillID, int SkillValue); + void CreateNewPlayerTitle(Client *c, const char *Title); + void CreateNewPlayerSuffix(Client *c, const char *Suffix); + +protected: + vector Titles; +}; + +extern TitleManager title_manager; + +#endif + diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp new file mode 100644 index 000000000..48d097d9a --- /dev/null +++ b/zone/tradeskills.cpp @@ -0,0 +1,1523 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 +#include + +#ifndef WIN32 +#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/rulesys.h" +#include "QuestParserCollection.h" + +static const SkillType TradeskillUnknown = _1H_BLUNT; /* an arbitrary non-tradeskill */ + +void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augment, Object *worldo) +{ + if (!user || !in_augment) + { + LogFile->write(EQEMuLog::Error, "Client or AugmentItem_Struct not set in Object::HandleAugmentation"); + return; + } + + ItemInst* container = NULL; + + if (worldo) + { + container = worldo->m_inst; + } + else + { + // Check to see if they have an inventory container type 53 that is used for this. + Inventory& user_inv = user->GetInv(); + ItemInst* inst = NULL; + + inst = user_inv.GetItem(in_augment->container_slot); + if (inst) + { + const Item_Struct* item = inst->GetItem(); + if (item && inst->IsType(ItemClassContainer) && item->BagType == 53) + { + // We have found an appropriate inventory augmentation sealer + container = inst; + + // Verify that no more than two items are in container to guarantee no inadvertant wipes. + uint8 itemsFound = 0; + for (uint8 i=0; i<10; i++) + { + const ItemInst* inst = container->GetItem(i); + if (inst) + { + itemsFound++; + } + } + + if (itemsFound != 2) + { + user->Message(13, "Error: Too many/few items in augmentation container."); + return; + } + } + } + } + + if(!container) + { + LogFile->write(EQEMuLog::Error, "Player tried to augment an item without a container set."); + user->Message(13, "Error: This item is not a container!"); + return; + } + + ItemInst *tobe_auged, *auged_with = NULL; + int8 slot=-1; + + // Verify 2 items in the augmentation device + if (container->GetItem(0) && container->GetItem(1)) + { + // Verify 1 item is augmentable and the other is not + if (container->GetItem(0)->IsAugmentable() && !container->GetItem(1)->IsAugmentable()) + { + tobe_auged = container->GetItem(0); + auged_with = container->GetItem(1); + } + else if (!container->GetItem(0)->IsAugmentable() && container->GetItem(1)->IsAugmentable()) + { + tobe_auged = container->GetItem(1); + auged_with = container->GetItem(0); + } + else + { + // Either 2 augmentable items found or none found + // This should never occur due to client restrictions, but prevent in case of a hack + user->Message(13, "Error: Must be 1 augmentable item in the sealer"); + return; + } + } + else + { + // This happens if the augment button is clicked more than once quickly while augmenting + if (!container->GetItem(0)) + { + user->Message(13, "Error: No item in slot 0 of sealer"); + } + if (!container->GetItem(1)) + { + user->Message(13, "Error: No item in slot 1 of sealer"); + } + return; + } + + bool deleteItems = false; + + ItemInst *itemOneToPush = NULL, *itemTwoToPush = NULL; + + // Adding augment + if (in_augment->augment_slot == -1) + { + if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) && (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) + { + tobe_auged->PutAugment(slot,*auged_with); + itemOneToPush = tobe_auged->Clone(); + deleteItems = true; + } + else + { + user->Message(13, "Error: No available slot for augment"); + } + } + else + { + ItemInst *old_aug=NULL; + const uint32 id=auged_with->GetID(); + if (id==40408 || id==40409 || id==40410) + tobe_auged->DeleteAugment(in_augment->augment_slot); + else + old_aug=tobe_auged->RemoveAugment(in_augment->augment_slot); + + itemOneToPush = tobe_auged->Clone(); + if (old_aug) + itemTwoToPush = old_aug->Clone(); + + deleteItems = true; + } + + if (deleteItems) + { + if (worldo) + { + container->Clear(); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClearObject, sizeof(ClearObject_Struct)); + ClearObject_Struct *cos = (ClearObject_Struct *)outapp->pBuffer; + cos->Clear = 1; + user->QueuePacket(outapp); + safe_delete(outapp); + database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID()); + } + else + { + // Delete items in our inventory container... + for (uint8 i=0; i<10; i++) + { + const ItemInst* inst = container->GetItem(i); + if (inst) + { + user->DeleteItemInInventory(Inventory::CalcSlotId(in_augment->container_slot,i),0,true); + } + } + // Explicitly mark container as cleared. + container->Clear(); + } + } + + // Must push items after the items in inventory are deleted - necessary due to lore items... + if (itemOneToPush) + { + user->PushItemOnCursor(*itemOneToPush,true); + } + if (itemTwoToPush) + { + user->PushItemOnCursor(*itemTwoToPush,true); + } + +} + +// Perform tradeskill combine +void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Object *worldo) +{ + if (!user || !in_combine) { + LogFile->write(EQEMuLog::Error, "Client or NewCombine_Struct not set in Object::HandleCombine"); + return; + } + + Inventory& user_inv = user->GetInv(); + PlayerProfile_Struct& user_pp = user->GetPP(); + ItemInst* container = NULL; + ItemInst* inst = NULL; + uint8 c_type = 0xE8; + uint32 some_id = 0; + bool worldcontainer=false; + + if (in_combine->container_slot == SLOT_TRADESKILL) { + if(!worldo) { + user->Message(13, "Error: Server is not aware of the tradeskill container you are attempting to use"); + return; + } + c_type = worldo->m_type; + inst = worldo->m_inst; + worldcontainer=true; + } + else { + inst = user_inv.GetItem(in_combine->container_slot); + if (inst) { + const Item_Struct* item = inst->GetItem(); + if (item && inst->IsType(ItemClassContainer)) { + c_type = item->BagType; + some_id = item->ID; + } + } + } + + if (!inst || !inst->IsType(ItemClassContainer)) { + user->Message(13, "Error: Server does not recognize specified tradeskill container"); + return; + } + + container = inst; + + DBTradeskillRecipe_Struct spec; + if (!database.GetTradeRecipe(container, c_type, some_id, user->CharacterID(), &spec)) { + user->Message_StringID(MT_Emote,TRADESKILL_NOCOMBINE); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0); + user->QueuePacket(outapp); + safe_delete(outapp); + return; + } + + // Character hasn't learnt the recipe yet. + // must_learn: + // bit 1 (0x01): recipe can't be experimented + // bit 2 (0x02): can try to experiment but not useable for auto-combine until learnt + // bit 5 (0x10): no learn message, use unlisted flag to prevent it showing up on search + // bit 6 (0x20): unlisted recipe flag + if ((spec.must_learn&0xF) == 1 && !spec.has_learnt) { + // Made up message for the client. Just giving a DNC is the other option. + user->Message(4, "You need to learn how to combine these first."); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0); + user->QueuePacket(outapp); + safe_delete(outapp); + return; + } + // Character does not have the required skill. + if(spec.skill_needed > 0 && user->GetSkill(spec.tradeskill) < spec.skill_needed ) { + // Notify client. + user->Message(4, "You are not skilled enough."); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0); + user->QueuePacket(outapp); + safe_delete(outapp); + return; + } + + //changing from a switch to string of if's since we don't need to iterate through all of the skills in the SkillType enum + if (spec.tradeskill == ALCHEMY) { + if (user_pp.class_ != SHAMAN) { + user->Message(13, "This tradeskill can only be performed by a shaman."); + return; + } + else if (user_pp.level < MIN_LEVEL_ALCHEMY) { + user->Message(13, "You cannot perform alchemy until you reach level %i.", MIN_LEVEL_ALCHEMY); + return; + } + } + else if (spec.tradeskill == TINKERING) { + if (user_pp.race != GNOME) { + user->Message(13, "Only gnomes can tinker."); + return; + } + } + else if (spec.tradeskill == MAKE_POISON) { + if (user_pp.class_ != ROGUE) { + user->Message(13, "Only rogues can mix poisons."); + return; + } + } + + // Send acknowledgement packets to client + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0); + user->QueuePacket(outapp); + safe_delete(outapp); + + //now clean out the containers. + if(worldcontainer){ + container->Clear(); + outapp = new EQApplicationPacket(OP_ClearObject, sizeof(ClearObject_Struct)); + ClearObject_Struct *cos = (ClearObject_Struct *)outapp->pBuffer; + cos->Clear = 1; + user->QueuePacket(outapp); + safe_delete(outapp); + database.DeleteWorldContainer(worldo->m_id, zone->GetZoneID()); + } else{ + for (uint8 i=0; i<10; i++){ + const ItemInst* inst = container->GetItem(i); + if (inst) { + user->DeleteItemInInventory(Inventory::CalcSlotId(in_combine->container_slot,i),0,true); + } + } + container->Clear(); + } + //do the check and send results... + bool success = user->TradeskillExecute(&spec); + + // Learn new recipe message + // Update Made count + if (success) { + if (!spec.has_learnt && ((spec.must_learn&0x10) != 0x10)) { + user->Message_StringID(4, TRADESKILL_LEARN_RECIPE, spec.name.c_str()); + } + database.UpdateRecipeMadecount(spec.recipe_id, user->CharacterID(), spec.madecount+1); + } + + // Replace the container on success if required. + // + + if(success && spec.replace_container) { + if(worldcontainer){ + //should report this error, but we dont have the recipe ID, so its not very useful + LogFile->write(EQEMuLog::Error, "Replace container combine executed in a world container."); + } + else + user->DeleteItemInInventory(in_combine->container_slot, 0, true); + } + if (success) + parse->EventPlayer(EVENT_COMBINE_SUCCESS, user, spec.name.c_str(), spec.recipe_id); + else + parse->EventPlayer(EVENT_COMBINE_FAILURE, user, spec.name.c_str(), spec.recipe_id); +} + +void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac) { + + //get our packet ready, gotta send one no matter what... + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RecipeAutoCombine, sizeof(RecipeAutoCombine_Struct)); + RecipeAutoCombine_Struct *outp = (RecipeAutoCombine_Struct *)outapp->pBuffer; + outp->object_type = rac->object_type; + outp->some_id = rac->some_id; + outp->unknown1 = rac->unknown1; + outp->recipe_id = rac->recipe_id; + outp->reply_code = 0xFFFFFFF5; //default fail. + + + //ask the database for the recipe to make sure it exists... + DBTradeskillRecipe_Struct spec; + if (!database.GetTradeRecipe(rac->recipe_id, rac->object_type, rac->some_id, user->CharacterID(), &spec)) { + LogFile->write(EQEMuLog::Error, "Unknown recipe for HandleAutoCombine: %u\n", rac->recipe_id); + user->QueuePacket(outapp); + safe_delete(outapp); + return; + } + + // Character hasn't learnt the recipe yet. + // This shouldn't happen. + if ((spec.must_learn&0xf) && !spec.has_learnt) { + // Made up message for the client. Just giving a DNC is the other option. + user->Message(4, "You need to learn how to combine these first."); + user->QueuePacket(outapp); + safe_delete(outapp); + 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); + user->QueuePacket(outapp); + safe_delete(outapp); + return; + } + safe_delete_array(query); + + qcount = mysql_num_rows(result); + if(qcount < 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); + user->QueuePacket(outapp); + safe_delete(outapp); + return; + } + + uint32 items[10]; + memset(items, 0, sizeof(items)); + 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); + uint32 item = (uint32)atoi(row[0]); + uint8 num = (uint8) atoi(row[1]); + + needcount += num; + + //because a HasItem on items with num > 1 only returns the + //last-most slot... the results of this are useless to us + //when we go to delete them because we cannot assume it is in a single stack. + if(user_inv.HasItem(item, num, invWherePersonal) != SLOT_INVALID) + count += num; + else + MissingItems.push_back(item); + + //dont start deleting anything until we have found it all. + items[r] = item; + counts[r] = num; + } + mysql_free_result(result); + + //make sure we found it all... + if(count != needcount) + { + user->QueuePacket(outapp); + + safe_delete(outapp); + + user->Message_StringID(MT_Skills, TRADESKILL_MISSING_COMPONENTS); + + for(std::list::iterator it = MissingItems.begin(); it != MissingItems.end(); ++it) + { + const Item_Struct* item = database.GetItem(*it); + + if(item) + user->Message_StringID(MT_Skills, TRADESKILL_MISSING_ITEM, item->Name); + } + + return; + } + + //now we know they have everything... + + //remove all the items from the players inventory, with updates... + int16 slot; + for(r = 0; r < qcount; 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++) { + slot = user_inv.HasItem(items[r], 1, invWherePersonal); + if(slot == SLOT_INVALID) { + //WTF... I just checked this above, but just to be sure... + //we cant undo the previous deletes without a lot of work. + //so just call it quits, this shouldent ever happen anyways. + user->QueuePacket(outapp); + safe_delete(outapp); + return; + } + + 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); + //DumpPacket(outapp); + safe_delete(outapp); + + + //now actually try to make something... + + bool success = user->TradeskillExecute(&spec); + + if (success) { + if (!spec.has_learnt && ((spec.must_learn & 0x10) != 0x10)) { + user->Message_StringID(4, TRADESKILL_LEARN_RECIPE, spec.name.c_str()); + } + database.UpdateRecipeMadecount(spec.recipe_id, user->CharacterID(), spec.madecount+1); + } + + + //TODO: find in-pack containers in inventory, make sure they are really + //there, and then use that slot to handle replace_container too. + if(success && spec.replace_container) { +// user->DeleteItemInInventory(in_combine->container_slot, 0, true); + } + if (success) + parse->EventPlayer(EVENT_COMBINE_SUCCESS, user, spec.name.c_str(), spec.recipe_id); + else + parse->EventPlayer(EVENT_COMBINE_FAILURE, user, spec.name.c_str(), spec.recipe_id); +} + +SkillType Object::TypeToSkill(uint32 type) { + SkillType tradeskill = TradeskillUnknown; + switch (type) { + case OT_MEDICINEBAG: { + tradeskill = ALCHEMY; + break; + } + case OT_SEWINGKIT: { + tradeskill = TAILORING; + break; + } + case OT_FORGE: + case OT_TEIRDALFORGE: + case OT_OGGOKFORGE: + case OT_FIERDALFFORGE: + case OT_STORMGUARDF: + case OT_VALEFORGE: { + tradeskill = BLACKSMITHING; + break; + } + case OT_FLETCHINGKIT: { + tradeskill = FLETCHING; + break; + } + case OT_BREWBARREL: { + tradeskill = BREWING; + break; + } + case OT_JEWELERSKIT: { + tradeskill = JEWELRY_MAKING; + break; + } + case OT_POTTERYWHEEL: + case OT_KILN: { + tradeskill = POTTERY; + break; + } + case OT_OVEN: { + tradeskill = BAKING; + break; + } + case OT_TACKLEBOX: { + tradeskill = FISHING; + break; + } + case OT_KEYMAKER: { //unknown for now... + tradeskill = TradeskillUnknown; + break; + } + case OT_TOOLBOX: { + tradeskill = TINKERING; + break; + } + case OT_WIZARDLEX: + case OT_MAGELEX: + case OT_NECROLEX: + case OT_ENCHLEX: { + tradeskill = RESEARCH; + break; + } + } + return(tradeskill); +} + +void Client::TradeskillSearchResults(const char *query, unsigned long qlen, + 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); + return; + } + + uint8 qcount = 0; + + 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); + return; + } + + uint8 r; + for(r = 0; r < qcount; r++) { + row = mysql_fetch_row(result); + if(row == NULL || row[0] == NULL || row[1] == NULL || row[2] == NULL || row[3] == NULL || row[5] == NULL) + continue; + uint32 recipe = (uint32)atoi(row[0]); + const char *name = row[1]; + uint32 trivial = (uint32) atoi(row[2]); + uint32 comp_count = (uint32) atoi(row[3]); + uint32 tradeskill = (uint16) atoi(row[5]); + + // 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((SkillType)tradeskill)) > RuleI(Skills, MaxTradeskillSearchSkillDiff) + && row[4] == NULL) + { + continue; + } + } + + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RecipeReply, sizeof(RecipeReply_Struct)); + RecipeReply_Struct *reply = (RecipeReply_Struct *) outapp->pBuffer; + + reply->object_type = objtype; + reply->some_id = someid; + reply->component_count = comp_count; + reply->recipe_id = recipe; + reply->trivial = trivial; + strn0cpy(reply->recipe_name, name, sizeof(reply->recipe_name)); + + //DumpPacket(outapp); + 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); + return; + } + safe_delete_array(query); + + qcount = mysql_num_rows(result); + if(qcount < 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); + return; + } + + //biggest this packet can ever be: + // 64 * 10 + 8 * 10 + 4 + 4 * 10 = 764 + char *buf = new char[775]; //dynamic so we can just give it to EQApplicationPacket + uint8 r,k; + + uint32 *header = (uint32 *) buf; + //Hell if I know why this is in the wrong byte order.... + *header = htonl(recipe_id); + + char *startblock = buf; + startblock += sizeof(uint32); + + uint32 *ffff_start = (uint32 *) startblock; + //fill in the FFFF's as if there were 0 items + for(r = 0; r < 10; r++) { + *ffff_start = 0xFFFFFFFF; + ffff_start++; + } + char * datastart = (char *) ffff_start; + char * cblock = (char *) ffff_start; + + uint32 *itemptr; + uint32 *iconptr; + uint32 len; + uint32 datalen = 0; + uint8 count = 0; + for(r = 0; r < qcount; r++) { + row = mysql_fetch_row(result); + + //watch for references to items which are not in the + //items table, which the left join will make NULL... + if(row[2] == NULL || row[3] == NULL) { + 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) + len = 63; + + //Hell if I know why these are in the wrong byte order.... + item = htonl(item); + icon = htonl(icon); + + //if we get more than 10 items, just start skipping them... + for(k = 0; k < num && count < 10; k++) { + itemptr = (uint32 *) cblock; + cblock += sizeof(uint32); + datalen += sizeof(uint32); + iconptr = (uint32 *) cblock; + cblock += sizeof(uint32); + datalen += sizeof(uint32); + + *itemptr = item; + *iconptr = icon; + strncpy(cblock, name, len); + + cblock[len] = '\0'; //just making sure. + cblock += len + 1; //get the null + datalen += len + 1; //get the null + count++; + } + + } + mysql_free_result(result); + + //now move the item data over top of the FFFFs + uint8 dist = sizeof(uint32) * (10 - count); + startblock += dist; + memmove(startblock, datastart, datalen); + + uint32 total = sizeof(uint32) + dist + datalen; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RecipeDetails); + outapp->size = total; + outapp->pBuffer = (uchar*) buf; + QueuePacket(outapp); + DumpPacket(outapp); + safe_delete(outapp); +} + +//returns true on success +bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { + if(spec == NULL) + return(false); + + uint16 user_skill = GetSkill(spec->tradeskill); + float chance = 0.0; + float skillup_modifier = 0.0; + int16 thirdstat = 0; + int16 stat_modifier = 15; + uint16 success_modifier = 0; + + // Rework based on the info on eqtraders.com + // http://mboards.eqtraders.com/eq/showthread.php?t=22246 + // 09/10/2006 v0.1 (eq4me) + // 09/11/2006 v0.2 (eq4me) + // Todo: + // Implementing AAs + // Success modifiers based on recipes + // Skillup modifiers based on the rarity of the ingredients + + // Some tradeskills are more eqal then others. ;-) + // If you want to customize the stage1 success rate do it here. + // Remember: skillup_modifier is (float). Lower is better + switch(spec->tradeskill) { + case FLETCHING: + case ALCHEMY: + case JEWELRY_MAKING: + case POTTERY: + skillup_modifier = 4; + break; + case BAKING: + case BREWING: + skillup_modifier = 3; + break; + case RESEARCH: + skillup_modifier = 1; + break; + default: + skillup_modifier = 2; + break; + } + + // Some tradeskills take the higher of one additional stat beside INT and WIS + // to determine the skillup rate. Additionally these tradeskills do not have an + // -15 modifier on their statbonus. + if (spec->tradeskill == FLETCHING || spec->tradeskill == MAKE_POISON) { + thirdstat = GetDEX(); + stat_modifier = 0; + } else if (spec->tradeskill == BLACKSMITHING) { + thirdstat = GetSTR(); + stat_modifier = 0; + } + + int16 higher_from_int_wis = (GetINT() > GetWIS()) ? GetINT() : GetWIS(); + int16 bonusstat = (higher_from_int_wis > thirdstat) ? higher_from_int_wis : thirdstat; + + vector< pair >::iterator itr; + + + //calculate the base success chance + // For trivials over 68 the chance is (skill - 0.75*trivial) +51.5 + // For trivial up to 68 the chance is (skill - trivial) + 66 + if (spec->trivial >= 68) { + chance = (user_skill - (0.75*spec->trivial)) + 51.5; + } else { + chance = (user_skill - spec->trivial) + 66; + } + + int16 over_trivial = (int16)GetRawSkill(spec->tradeskill) - (int16)spec->trivial; + + //handle caps + if(spec->nofail) { + chance = 100; //cannot fail. + _log(TRADESKILLS__TRACE, "...This combine cannot fail."); + } else if(over_trivial >= 0) { + // At reaching trivial the chance goes to 95% going up an additional + // percent for every 40 skillpoints above the trivial. + // The success rate is not modified through stats. + // Mastery AAs are unaccounted for so far. + // chance_AA = chance + ((100 - chance) * mastery_modifier) + // But the 95% limit with an additional 1% for every 40 skill points + // above critical still stands. + // Mastery modifier is: 10%/25%/50% for rank one/two/three + chance = 95.0f + (float(user_skill - spec->trivial) / 40.0f); + Message_StringID(MT_Emote, TRADESKILL_TRIVIAL); + } else if(chance < 5) { + // Minimum chance is always 5 + chance = 5; + } else if(chance > 95) { + //cap is 95, shouldent reach this before trivial, but just in case. + chance = 95; + } + + _log(TRADESKILLS__TRACE, "...Current skill: %d , Trivial: %d , Success chance: %f percent", user_skill , spec->trivial , chance); + _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; + + //AA modifiers + //can we do this with nested switches? + if(spec->tradeskill == ALCHEMY){ + switch(GetAA(aaAlchemyMastery)){ + case 1: + AAChance = 10; + break; + case 2: + AAChance = 25; + break; + case 3: + AAChance = 50; + break; + } + } + + if(spec->tradeskill == JEWELRY_MAKING){ + switch(GetAA(aaJewelCraftMastery)){ + case 1: + AAChance = 10; + break; + case 2: + AAChance = 25; + break; + case 3: + AAChance = 50; + break; + } + } + const Item_Struct* item = NULL; + + if (spec->tradeskill == BLACKSMITHING) { + switch(GetAA(aaBlacksmithingMastery)) { + case 1: + AAChance = 10; + break; + case 2: + AAChance = 25; + break; + case 3: + AAChance = 50; + break; + } + } + + if (spec->tradeskill == BAKING) { + switch(GetAA(aaBakingMastery)) { + case 1: + AAChance = 10; + break; + case 2: + AAChance = 25; + break; + case 3: + AAChance = 50; + break; + } + } + + if (spec->tradeskill == BREWING) { + switch(GetAA(aaBrewingMastery)) { + case 1: + AAChance = 10; + break; + case 2: + AAChance = 25; + break; + case 3: + AAChance = 50; + break; + } + } + + if (spec->tradeskill == FLETCHING) { + switch(GetAA(aaFletchingMastery2)) { + case 1: + AAChance = 10; + break; + case 2: + AAChance = 25; + break; + case 3: + AAChance = 50; + break; + } + } + + if (spec->tradeskill == POTTERY) { + switch(GetAA(aaPotteryMastery)) { + case 1: + AAChance = 10; + break; + case 2: + AAChance = 25; + break; + case 3: + AAChance = 50; + break; + } + } + + if (spec->tradeskill == TAILORING) { + switch(GetAA(aaTailoringMastery)) { + case 1: + AAChance = 10; + break; + case 2: + AAChance = 25; + break; + case 3: + AAChance = 50; + break; + } + } + + if (spec->tradeskill == RESEARCH) { + switch(GetAA(aaArcaneTongues)) { + case 1: + AAChance = 10; + break; + case 2: + AAChance = 25; + break; + case 3: + AAChance = 50; + break; + } + } + + if (((spec->tradeskill==75) || GetGM() || (chance > res)) || MakeRandomInt(0, 99) < AAChance){ + 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()); + + _log(TRADESKILLS__TRACE, "Tradeskill success"); + + itr = spec->onsuccess.begin(); + while(itr != spec->onsuccess.end() && !spec->quest) { + //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(RuleB(TaskSystem, EnableTaskSystem)) + UpdateTasksForItem(ActivityTradeSkill, itr->first, itr->second); + itr++; + } + return(true); + } else { + success_modifier = 2; // Halves the chance + + if(over_trivial < 0) + CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill); + + Message_StringID(MT_Emote,TRADESKILL_FAILED); + + _log(TRADESKILLS__TRACE, "Tradeskill failed"); + 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"); + } + + itr = spec->onfail.begin(); + while(itr != spec->onfail.end()) { + //should we check these arguments? + SummonItem(itr->first, itr->second); + itr++; + } + } + return(false); +} + +void Client::CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, SkillType tradeskill) +{ + uint16 current_raw_skill = GetRawSkill(tradeskill); + + if(!CanIncreaseTradeskill(tradeskill)) + return; //not allowed to go higher. + + float chance_stage2 = 0; + + //A successfull combine doubles the stage1 chance for an skillup + //Some tradeskill are harder than others. See above for more. + float chance_stage1 = (bonusstat - stat_modifier) / (skillup_modifier * success_modifier); + + //In stage2 the only thing that matters is your current unmodified skill. + //If you want to customize here you probbably need to implement your own + //formula instead of tweaking the below one. + if (chance_stage1 > MakeRandomFloat(0, 99)) { + if (current_raw_skill < 15) { + //Always succeed + chance_stage2 = 100; + } else if (current_raw_skill < 175) { + //From skill 16 to 174 your chance of success falls linearly from 92% to 13%. + chance_stage2 = (200 - current_raw_skill) / 2; + } else { + //At skill 175, your chance of success falls linearly from 12.5% to 2.5% at skill 300. + chance_stage2 = 12.5 - (.08 * (current_raw_skill - 175)); + } + } + + if (chance_stage2 > MakeRandomFloat(0, 99)) { + //Only if stage1 and stage2 succeeded you get a skillup. + SetSkill(tradeskill, current_raw_skill + 1); + + if(title_manager.IsNewTradeSkillTitleAvailable(tradeskill, current_raw_skill + 1)) + NotifyNewTitlesAvailable(); + } + + _log(TRADESKILLS__TRACE, "...skillup_modifier: %f , success_modifier: %d , stat modifier: %d", skillup_modifier , success_modifier , stat_modifier); + _log(TRADESKILLS__TRACE, "...Stage1 chance was: %f percent", chance_stage1); + _log(TRADESKILLS__TRACE, "...Stage2 chance was: %f percent. 0 percent means stage1 failed", chance_stage2); +} + +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'; + + //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++) { + 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(count < 1) { + return(false); //no items == no recipe + } + + qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id " + " FROM tradeskill_recipe_entries AS tre" + " WHERE ( 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 (!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); + } + + if(qcount < 1) + return(false); + + if(qcount > 1) + { + //The recipe is not unique, so we need to compare the container were using. + + uint32 containerId = 0; + + if(some_id) { //Standard container + containerId = some_id; + } + else if(c_type) { //World container + containerId = c_type; + } + else { //Invalid container + 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 + 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); + } + } + + row = mysql_fetch_row(result); + 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))!=NULL) { + int ccnt = 0; + for(int x = 0; x < 10; 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){ + + 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); + } + + 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" + " GROUP BY tr.id", char_id, (unsigned long)recipe_id, containers); + + 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 = (SkillType)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] == NULL) { + spec->has_learnt = false; + spec->madecount = 0; + } else { + spec->has_learnt = true; + 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); + } + safe_delete_array(query); + + qcount = mysql_num_rows(result); + if(qcount < 1) { + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success: no success items returned"); + return(false); + } + uint8 r; + spec->onsuccess.clear(); + for(r = 0; r < qcount; r++) { + row = mysql_fetch_row(result); + uint32 item = (uint32)atoi(row[0]); + uint8 num = (uint8) atoi(row[1]); + spec->onsuccess.push_back(pair(item, num)); + } + mysql_free_result(result); + + //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); + uint32 item = (uint32)atoi(row[0]); + uint8 num = (uint8) atoi(row[1]); + spec->onfail.push_back(pair(item, num)); + } + mysql_free_result(result); + } + safe_delete_array(query); + + return(true); +} + +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); +} + +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); + 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); + return; + } + safe_delete_array(query); + + row = mysql_fetch_row(result); + + if (row != NULL && row[0] != NULL) { + // Only give Learn message if character doesn't know the recipe + if (row[1] == NULL) { + 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 (!database.RunQuery(query, qlen, errbuf)) { + LogFile->write(EQEMuLog::Error, "Error in LearnRecipe query '%s': %s", query, errbuf); + } + safe_delete_array(query); + } + } + + mysql_free_result(result); + +} + +bool Client::CanIncreaseTradeskill(SkillType tradeskill) { + uint32 rawskill = GetRawSkill(tradeskill); + uint16 maxskill = MaxSkill(tradeskill); + + if (rawskill >= maxskill) //Max skill sanity check + return false; + + uint8 Baking = (GetRawSkill(BAKING) > 200) ? 1 : 0; + uint8 Smithing = (GetRawSkill(BLACKSMITHING) > 200) ? 1 : 0; + uint8 Brewing = (GetRawSkill(BREWING) > 200) ? 1 : 0; + uint8 Fletching = (GetRawSkill(FLETCHING) > 200) ? 1 : 0; + uint8 Jewelry = (GetRawSkill(JEWELRY_MAKING) > 200) ? 1 : 0; + uint8 Pottery = (GetRawSkill(POTTERY) > 200) ? 1 : 0; + uint8 Tailoring = (GetRawSkill(TAILORING) > 200) ? 1 : 0; + uint8 SkillTotal = Baking + Smithing + Brewing + Fletching + Jewelry + Pottery + Tailoring; //Tradeskills above 200 + uint32 aaLevel = GetAA(aaNewTanaanCraftingMastery); //New Tanaan AA: Each level allows an additional tradeskill above 200 (first one is free) + + switch (tradeskill) { + case BAKING: + case BLACKSMITHING: + case BREWING: + case FLETCHING: + case JEWELRY_MAKING: + case POTTERY: + case TAILORING: + if (aaLevel == 6) + break; //Maxed AA + if (SkillTotal == 0) + break; //First tradeskill freebie + if ((SkillTotal == (aaLevel + 1)) && (rawskill > 200)) + break; //One of the tradeskills already allowed to go over 200 + if ((SkillTotal >= (aaLevel + 1)) && (rawskill >= 200)) + return false; //One or more tradeskills already at or beyond limit + break; + default: + break; //Other skills unchecked and ability to increase assumed true + } + return true; +} \ No newline at end of file diff --git a/zone/trading.cpp b/zone/trading.cpp new file mode 100644 index 000000000..e7530685b --- /dev/null +++ b/zone/trading.cpp @@ -0,0 +1,2740 @@ +/* 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 "../common/debug.h" +#include "masterentity.h" +#include "StringIDs.h" +#include "../common/MiscFunctions.h" +#include "../common/rulesys.h" +#include "QuestParserCollection.h" +#include "worldserver.h" +extern WorldServer worldserver; + +// The maximum amount of a single bazaar/barter transaction expressed in copper. +// Equivalent to 2 Million plat +#define MAX_TRANSACTION_VALUE 2000000000 +// ########################################## +// Trade implementation +// ########################################## + +Trade::Trade(Mob* in_owner) +{ + owner = in_owner; + Reset(); +} + +Trade::~Trade() +{ + Reset(); +} + +void Trade::Reset() +{ + state = TradeNone; + with_id = 0; + pp=0; gp=0; sp=0; cp=0; +} + +void Trade::SetTradeCash(uint32 in_pp, uint32 in_gp, uint32 in_sp, uint32 in_cp) +{ + pp=in_pp; gp=in_gp; sp=in_sp; cp=in_cp; +} + +// Initiate a trade with another mob +// initiate_with specifies whether to start trade with other mob as well +void Trade::Start(uint32 mob_id, bool initiate_with) +{ + Reset(); + state = Trading; + with_id = mob_id; + + // Autostart on other mob? + if (initiate_with) { + Mob* with = With(); + if (with) + with->trade->Start(owner->GetID(), false); + } +} + +// 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) +{ + if (!owner || !owner->IsClient()) { + // This should never happen + LogFile->write(EQEMuLog::Debug, "Programming error: NPC's should not call Trade::AddEntity()"); + return; + } + + // If one party accepted the trade then an item was added, their state needs to be reset + owner->trade->state = Trading; + Mob* with = With(); + if (with) + with->trade->state = Trading; + + // Item always goes into trade bucket from cursor + Client* client = owner->CastToClient(); + const ItemInst* inst = client->GetInv().GetItem(SLOT_CURSOR); + 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(SLOT_CURSOR)->GetID() != client->GetInv().GetItem(trade_slot_id)->GetID()) { + 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 + inst2->SetCharges(inst2->GetItem()->StackSize); + } + 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); +} + +// Retrieve mob the owner is trading with +// Done like this in case 'with' mob goes LD and Mob* becomes invalid +Mob* Trade::With() +{ + return entity_list.GetMob(with_id); +} + +// Private Method: Send item data for trade item to other person involved in trade +void Trade::SendItemData(const ItemInst* inst, int16 dest_slot_id) +{ + // @merth: This needs to be redone with new item classes + Mob* mob = With(); + if (!mob->IsClient()) + return; // Not sending packets to NPCs! + + Client* with = mob->CastToClient(); + Client* trader = owner->CastToClient(); + if (with && with->IsClient()) { + with->SendItemPacket(dest_slot_id -IDX_TRADE,inst,ItemPacketTradeView); + if (inst->GetItem()->ItemClass == 1) { + for (uint16 i=0; i<10; i++) { + uint16 bagslot_id = Inventory::CalcSlotId(dest_slot_id, i); + const ItemInst* bagitem = trader->GetInv().GetItem(bagslot_id); + if (bagitem) { + with->SendItemPacket(bagslot_id-IDX_TRADE,bagitem,ItemPacketTradeView); + } + } + } + + //safe_delete(outapp); + } +} + +// Audit trade: The part logged is what travels owner -> with +void Trade::LogTrade() +{ + Mob* with = With(); + if (!owner->IsClient() || !with) + return; // Should never happen + + Client* trader = owner->CastToClient(); + bool logtrade = false; + int admin_level = 0; + uint8 item_count = 0; + + if (zone->tradevar != 0) { + for (uint16 i=3000; i<=3007; i++) { + if (trader->GetInv().GetItem(i)) + item_count++; + } + + if (((this->cp + this->sp + this->gp + this->pp)>0) || (item_count>0)) + admin_level = trader->Admin(); + else + admin_level = 999; + + if (zone->tradevar == 7) { + logtrade = true; + } + else if ((admin_level>=10) && (admin_level<20)) { + if ((zone->tradevar<8) && (zone->tradevar>5)) + logtrade = true; + } + else if (admin_level<=20) { + if ((zone->tradevar<8) && (zone->tradevar>4)) + logtrade = true; + } + else if (admin_level<=80) { + if ((zone->tradevar<8) && (zone->tradevar>3)) + logtrade = true; + } + else if (admin_level<=100){ + if ((zone->tradevar<9) && (zone->tradevar>2)) + logtrade = true; + } + else if (admin_level<=150){ + if (((zone->tradevar<8) && (zone->tradevar>1)) || (zone->tradevar==9)) + logtrade = true; + } + else if (admin_level<=255){ + if ((zone->tradevar<8) && (zone->tradevar>0)) + logtrade = true; + } + } + + if (logtrade == true) { + char logtext[1000] = {0}; + uint32 cash = 0; + bool comma = false; + + // Log items offered by owner + cash = this->cp + this->sp + this->gp + this->pp; + if ((cash>0) || (item_count>0)) { + sprintf(logtext, "%s gave %s ", trader->GetName(), with->GetName()); + + if (item_count > 0) { + strcat(logtext, "items {"); + + for (uint16 i=3000; i<=3007; i++) { + const ItemInst* inst = trader->GetInv().GetItem(i); + + if (!comma) + comma = true; + else { + if (inst) + strcat(logtext, ","); + } + + if (inst) { + char item_num[15] = {0}; + sprintf(item_num, "%i", inst->GetItem()->ID); + strcat(logtext, item_num); + + if (inst->IsType(ItemClassContainer)) { + for (uint8 j=0; j<10; j++) { + inst = trader->GetInv().GetItem(i, j); + if (inst) { + strcat(logtext, ","); + sprintf(item_num, "%i", inst->GetItem()->ID); + strcat(logtext, item_num); + } + } + } + } + } + } + + if (cash > 0) { + char money[100] = {0}; + sprintf(money, " %ipp, %igp, %isp, %icp", trader->trade->pp, trader->trade->gp, trader->trade->sp, trader->trade->cp); + strcat(logtext, money); + } + + database.logevents(trader->AccountName(), trader->AccountID(), + trader->Admin(), trader->GetName(), with->GetName(), "Trade", logtext, 6); + } + } +} + +#if (EQDEBUG >= 9) +void Trade::DumpTrade() +{ + Mob* with = With(); + LogFile->write(EQEMuLog::Debug, "Dumping trade data: '%s' in TradeState %i with '%s'", + this->owner->GetName(), state, ((with==NULL)?"(null)":with->GetName())); + + if (!owner->IsClient()) + return; + + Client* trader = owner->CastToClient(); + for (uint16 i=3000; i<=3007; i++) { + const ItemInst* inst = trader->GetInv().GetItem(i); + + if (inst) { + LogFile->write(EQEMuLog::Debug, "Item %i (Charges=%i, Slot=%i, IsBag=%s)", + inst->GetItem()->ID, inst->GetCharges(), + i, ((inst->IsType(ItemClassContainer)) ? "True" : "False")); + + if (inst->IsType(ItemClassContainer)) { + for (uint8 j=0; j<10; j++) { + inst = trader->GetInv().GetItem(i, j); + if (inst) { + LogFile->write(EQEMuLog::Debug, "\tBagItem %i (Charges=%i, Slot=%i)", + inst->GetItem()->ID, inst->GetCharges(), + Inventory::CalcSlotId(i, j)); + } + } + } + } + } + + LogFile->write(EQEMuLog::Debug, "\tpp:%i, gp:%i, sp:%i, cp:%i", pp, gp, sp, cp); +} +#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=3000; x <= 3007; 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 == SLOT_INVALID) + { + DropInst(ins); + } + else + { + PutItemInInventory(freeslotid, *ins); + SendItemPacket(freeslotid, ins, ItemPacketTrade); + } + DeleteItemInInventory(x); + } + } +} + +void Client::FinishTrade(Mob* tradingWith, ServerPacket* qspack, bool finalizer) { + + if(tradingWith && tradingWith->IsClient()) { + Client* other = tradingWith->CastToClient(); + + if(other) { + mlog(TRADING__CLIENT, "Finishing trade with client %s", other->GetName()); + + int16 slot_id; + const Item_Struct* item = NULL; + QSPlayerLogTrade_Struct* qsaudit = NULL; + bool QSPLT = false; + + // QS code + if(qspack && RuleB(QueryServ, PlayerLogTrades)) { + qsaudit = (QSPlayerLogTrade_Struct*) qspack->pBuffer; + QSPLT = true; + + if(finalizer) { qsaudit->char2_id = this->character_id; } + else { qsaudit->char1_id = this->character_id; } + } + + // Move each trade slot into free inventory slot + for(int16 i = 3000; i <= 3007; i++){ + const ItemInst* inst = m_inv[i]; + uint16 parent_offset = 0; + + if(inst == NULL) { continue; } + + mlog(TRADING__CLIENT, "Giving %s (%d) in slot %d to %s", inst->GetItem()->Name, inst->GetItem()->ID, i, other->GetName()); + + /// Log Player Trades through QueryServ if Rule Enabled + if(QSPLT) { + uint16 item_count = qsaudit->char1_count + qsaudit->char2_count; + parent_offset = item_count; + + 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); + + if(finalizer) { qsaudit->char2_count++; } + else { qsaudit->char1_count++; } + + if(inst->IsType(ItemClassContainer)) { + // Pseudo-Slot ID's are generated based on how the db saves bag items... + for(uint8 j = 0; j < inst->GetItem()->BagSlots; j++) { + const ItemInst* baginst = inst->GetItem(j); + + if(baginst == NULL) { continue; } + + int16 k=Inventory::CalcSlotId(i, j); + item_count = qsaudit->char1_count + qsaudit->char2_count; + + 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); + + if(finalizer) { qsaudit->char2_count++; } + else { qsaudit->char1_count++; } + } + } + } + + 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 = 0; bagslot_idx < inst->GetItem()->BagSlots; bagslot_idx++) { + const ItemInst* bag_inst = inst->GetItem(bagslot_idx); + + if(bag_inst == NULL) { continue; } + int16 to_bagslot_id = Inventory::CalcSlotId(slot_id, bagslot_idx); + + qsaudit->items[++parent_offset].to_slot = to_bagslot_id; + } + } + } + } + else { + 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 = SLOT_CURSOR; + + if(inst->IsType(ItemClassContainer)) { + for(uint8 bagslot_idx = 0; bagslot_idx < inst->GetItem()->BagSlots; bagslot_idx++) { + const ItemInst* bag_inst = inst->GetItem(bagslot_idx); + + if(bag_inst == NULL) { continue; } + int16 to_bagslot_id = Inventory::CalcSlotId(SLOT_CURSOR, bagslot_idx); + + qsaudit->items[++parent_offset].to_id = this->character_id; + qsaudit->items[parent_offset].to_slot = to_bagslot_id; + } + } + } + } + + DeleteItemInInventory(i); + } + else { + PushItemOnCursor(*inst, true); + DeleteItemInInventory(i); + + if(QSPLT) { + qsaudit->items[parent_offset].to_id = this->character_id; + qsaudit->items[parent_offset].to_slot = SLOT_CURSOR; + + if(inst->IsType(ItemClassContainer)) { + for(uint8 bagslot_idx = 0; bagslot_idx < inst->GetItem()->BagSlots; bagslot_idx++) { + const ItemInst* bag_inst = inst->GetItem(bagslot_idx); + + if(bag_inst == NULL) { continue; } + int16 to_bagslot_id = Inventory::CalcSlotId(SLOT_CURSOR, bagslot_idx); + + qsaudit->items[++parent_offset].to_id = this->character_id; + qsaudit->items[parent_offset].to_slot = to_bagslot_id; + } + } + } + } + } + + // Money - look into how NPC's receive cash + this->AddMoneyToPP(other->trade->cp, other->trade->sp, other->trade->gp, other->trade->pp, true); + + // 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; + } + 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; + } + } + + //Do not reset the trade here, done by the caller. + } + } + else if(tradingWith && tradingWith->IsNPC()) { + QSPlayerLogHandin_Struct* qsaudit = NULL; + bool QSPLH = false; + + // QS code + if(qspack && RuleB(QueryServ, PlayerLogTrades)) { + // 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; + + 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; + } + + if(QSPLH) { // This can be incoporated below when revisions are made -U + for(int16 slot_id = 3000; slot_id <= 3003; slot_id++) { + const ItemInst* trade_inst = m_inv[slot_id]; + + if(trade_inst) { + strcpy(qsaudit->items[qsaudit->char_count].action_type, "HANDIN"); + + 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); + + if(trade_inst->IsType(ItemClassContainer)) { + for(uint8 bag_idx = 0; bag_idx < trade_inst->GetItem()->BagSlots; bag_idx++) { + const ItemInst* trade_baginst = trade_inst->GetItem(bag_idx); + + if(trade_baginst) { + strcpy(qsaudit->items[qsaudit->char_count].action_type, "HANDIN"); + + 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); + } + } + } + } + } + } + + bool quest_npc = false; + if(parse->HasQuestSub(tradingWith->GetNPCTypeID(), "EVENT_ITEM")) { + // This is a quest NPC + quest_npc = true; + if(RuleB(NPC, UseMultiQuest)){ + StoreTurnInItems(tradingWith); + } + } + + uint32 items[4]={0}; + uint8 charges[4]={0}; + bool attuned[4]={0}; + + for (int16 i=3000; i<=3003; i++) { + const ItemInst* inst = m_inv[i]; + if (inst) { + items[i-3000]=inst->GetItem()->ID; + charges[i-3000]=inst->GetCharges(); + attuned[i-3000]=inst->IsInstNoDrop(); + const Item_Struct* item2 = database.GetItem(items[i-3000]); + // Handle non-quest NPC trading + if (item2 && quest_npc == false) { + // if it was not a NO DROP or Attuned item (or if a GM is trading), let the NPC have it + if(GetGM() || (item2->NoDrop != 0 && inst->IsInstNoDrop() == false)) { + // pets need to look inside bags and try to equip items found there + if (item2->ItemClass == ItemClassContainer && item2->BagSlots > 0) { + for(int16 bslot=0; bslot < item2->BagSlots; bslot++) { + const ItemInst* baginst = inst->GetItem(bslot); + if (baginst) { + const Item_Struct* bagitem = database.GetItem(baginst->GetItem()->ID); + if (bagitem && (GetGM() || (bagitem->NoDrop != 0 && baginst->IsInstNoDrop() == false))) { + tradingWith->CastToNPC()->AddLootDrop(bagitem, &tradingWith->CastToNPC()->itemlist, baginst->GetCharges(), 1, 127, true, true); + } + else if (RuleB(NPC, ReturnNonQuestNoDropItems)) { + PushItemOnCursor(*baginst, true); + } + } + } + } + + tradingWith->CastToNPC()->AddLootDrop(item2, &tradingWith->CastToNPC()->itemlist, charges[i-3000], 1, 127, true, true); + } + // Return NO DROP and Attuned items being handed into a non-quest NPC if the rule is true + else if (RuleB(NPC, ReturnNonQuestNoDropItems)) { + PushItemOnCursor(*inst, true); + } + } + DeleteItemInInventory(i); + } + } + + if(RuleB(TaskSystem, EnableTaskSystem)) { + int Cash = trade->cp + (trade->sp * 10) + (trade->gp * 100) + (trade->pp * 1000); + if(UpdateTasksOnDeliver(items, Cash, tradingWith->GetNPCTypeID())) { + if(!tradingWith->IsMoving()) + tradingWith->FaceTarget(this); + } + } + + //dont bother with this crap unless we have a quest... + //pets can have quests! (especially charmed NPCs) + if (quest_npc) { + + char temp1[100]; + memset(temp1,0x0,100); + char temp2[100]; + memset(temp2,0x0,100); + for ( int z=0; z < 4; z++ ) { + snprintf(temp1, 100, "item%d.%d", z+1,tradingWith->GetNPCTypeID()); + snprintf(temp2, 100, "%d",items[z]); + parse->AddVar(temp1,temp2); + // memset(temp1,0x0,100); + // memset(temp2,0x0,100); + snprintf(temp1, 100, "item%d.charges.%d", z+1,tradingWith->GetNPCTypeID()); + snprintf(temp2, 100, "%d",charges[z]); + parse->AddVar(temp1,temp2); + // memset(temp1,0x0,100); + // memset(temp2,0x0,100); + snprintf(temp1, 100, "item%d.attuned.%d", z+1,tradingWith->GetNPCTypeID()); + snprintf(temp2, 100, "%d",attuned[z]); + parse->AddVar(temp1,temp2); + // memset(temp1,0x0,100); + // memset(temp2,0x0,100); + } + snprintf(temp1, 100, "copper.%d",tradingWith->GetNPCTypeID()); + snprintf(temp2, 100, "%i",trade->cp); + parse->AddVar(temp1,temp2); + // memset(temp1,0x0,100); + // memset(temp2,0x0,100); + snprintf(temp1, 100, "silver.%d",tradingWith->GetNPCTypeID()); + snprintf(temp2, 100, "%i",trade->sp); + parse->AddVar(temp1,temp2); + // memset(temp1,0x0,100); + // memset(temp2,0x0,100); + snprintf(temp1, 100, "gold.%d",tradingWith->GetNPCTypeID()); + snprintf(temp2, 100, "%i",trade->gp); + parse->AddVar(temp1,temp2); + // memset(temp1,0x0,100); + // memset(temp2,0x0,100); + snprintf(temp1, 100, "platinum.%d",tradingWith->GetNPCTypeID()); + snprintf(temp2, 100, "%i",trade->pp); + parse->AddVar(temp1,temp2); + // memset(temp1,0x0,100); + // memset(temp2,0x0,100); + parse->EventNPC(EVENT_ITEM, tradingWith->CastToNPC(), this, "", 0); + } + } +} + +bool Client::CheckTradeLoreConflict(Client* other) +{ + if (!other) + return true; + // Move each trade slot into free inventory slot + for (int16 i=3000; i<=3179; i++){ + if(i == 3008) { i = 3100; } + const ItemInst* inst = m_inv[i]; + + if (inst && inst->GetItem()) { + if (other->CheckLoreConflict(inst->GetItem())) + return true; + } + } + return false; +} + +void Client::Trader_ShowItems(){ + EQApplicationPacket* outapp= new EQApplicationPacket(OP_Trader, sizeof(Trader_Struct)); + + Trader_Struct* outints = (Trader_Struct*)outapp->pBuffer; + Trader_Struct* TraderItems = database.LoadTraderItem(this->CharacterID()); + + for(int i = 0; i < 80; i++){ + outints->ItemCost[i] = TraderItems->ItemCost[i]; + outints->Items[i] = TraderItems->Items[i]; + } + outints->Code = BazaarTrader_ShowItems; + + QueuePacket(outapp); + _pkt(TRADING__PACKETS, outapp); + safe_delete(outapp); + safe_delete(TraderItems); +} + +void Client::SendTraderPacket(Client* Trader, uint32 Unknown72) +{ + if(!Trader) + return; + + EQApplicationPacket* outapp= new EQApplicationPacket(OP_BecomeTrader, sizeof(BecomeTrader_Struct)); + + BecomeTrader_Struct* bts = (BecomeTrader_Struct*)outapp->pBuffer; + + bts->Code = BazaarTrader_StartTraderMode; + + bts->ID = Trader->GetID(); + + strn0cpy(bts->Name, Trader->GetName(), sizeof(bts->Name)); + + bts->Unknown072 = Unknown72; + + QueuePacket(outapp); + + _pkt(TRADING__PACKETS, outapp); + + safe_delete(outapp); +} + +void Client::Trader_CustomerBrowsing(Client *Customer) { + + EQApplicationPacket* outapp= new EQApplicationPacket(OP_Trader, sizeof(Trader_ShowItems_Struct)); + + Trader_ShowItems_Struct* sis = (Trader_ShowItems_Struct*)outapp->pBuffer; + + sis->Code = BazaarTrader_CustomerBrowsing; + + sis->TraderID = Customer->GetID(); + + QueuePacket(outapp); +} + + +void Client::Trader_StartTrader() { + + Trader=true; + + EQApplicationPacket* outapp= new EQApplicationPacket(OP_Trader, sizeof(Trader_ShowItems_Struct)); + + Trader_ShowItems_Struct* sis = (Trader_ShowItems_Struct*)outapp->pBuffer; + + sis->Code = BazaarTrader_StartTraderMode; + + sis->TraderID = this->GetID(); + + QueuePacket(outapp); + + _pkt(TRADING__PACKETS, outapp); + + safe_delete(outapp); + + // Notify other clients we are now in trader mode + SendTraderPacket(this, 0); +} + +void Client::Trader_EndTrader() { + + // If someone is looking at our wares, remove all the items from the window. + // + if(CustomerID) { + Client* Customer = entity_list.GetClientByID(CustomerID); + GetItems_Struct* gis=GetTraderItems(); + + if(Customer && gis) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderDelItem,sizeof(TraderDelItem_Struct)); + TraderDelItem_Struct* tdis = (TraderDelItem_Struct*)outapp->pBuffer; + + tdis->Unknown000 = 0; + tdis->TraderID = Customer->GetID(); + tdis->Unknown012 = 0; + Customer->Message(13, "The Trader is no longer open for business"); + + for(int i = 0; i < 80; i++) { + if(gis->Items[i] != 0) { + + tdis->ItemID = gis->SerialNumber[i]; + + Customer->QueuePacket(outapp); + } + } + + safe_delete(outapp); + safe_delete(gis); + } + } + + database.DeleteTraderItem(this->CharacterID()); + + // Notify other clients we are no longer in trader mode. + // + EQApplicationPacket* outapp= new EQApplicationPacket(OP_BecomeTrader, sizeof(BecomeTrader_Struct)); + + BecomeTrader_Struct* bts = (BecomeTrader_Struct*)outapp->pBuffer; + + bts->Code = 0; + + bts->ID = this->GetID(); + + strn0cpy(bts->Name, GetName(), sizeof(bts->Name)); + + entity_list.QueueClients(this, outapp, false); + + _pkt(TRADING__PACKETS, outapp); + + safe_delete(outapp); + + outapp= new EQApplicationPacket(OP_Trader, sizeof(Trader_ShowItems_Struct)); + + Trader_ShowItems_Struct* sis = (Trader_ShowItems_Struct*)outapp->pBuffer; + + sis->Code = BazaarTrader_EndTraderMode; + + sis->TraderID = BazaarTrader_EndTraderMode; + + QueuePacket(outapp); + + _pkt(TRADING__PACKETS, outapp); + + safe_delete(outapp); + + WithCustomer(0); + + this->Trader = false; +} + +void Client::SendTraderItem(uint32 ItemID, uint16 Quantity) { + + string Packet; + int16 FreeSlotID=0; + + const Item_Struct* item = database.GetItem(ItemID); + + if(!item){ + _log(TRADING__CLIENT, "Bogus item deleted in Client::SendTraderItem!\n"); + return; + } + + ItemInst* inst = database.CreateItem(item, Quantity); + + if (inst) + { + bool is_arrow = (inst->GetItem()->ItemType == ItemTypeArrow) ? true : false; + FreeSlotID = m_inv.FindFreeSlot(false, true, inst->GetItem()->Size, is_arrow); + + PutItemInInventory(FreeSlotID, *inst); + Save(); + + SendItemPacket(FreeSlotID, inst, ItemPacketTrade); + + safe_delete(inst); + } +} + +void Client::SendSingleTraderItem(uint32 CharID, int SerialNumber) { + + ItemInst* inst= database.LoadSingleTraderItem(CharID, SerialNumber); + if(inst) { + SendItemPacket(30, inst, ItemPacketMerchant); + safe_delete(inst); + } + +} + +void Client::BulkSendTraderInventory(uint32 char_id) { + const Item_Struct *item; + + TraderCharges_Struct* TraderItems = database.LoadTraderItemWithCharges(char_id); + + for (uint8 i = 0;i < 80; i++) { + if((TraderItems->ItemID[i] == 0) || (TraderItems->ItemCost[i] <= 0)) { + continue; + } + else + item=database.GetItem(TraderItems->ItemID[i]); + + if (item && (item->NoDrop!=0)) { + ItemInst* inst = database.CreateItem(item); + if (inst) { + inst->SetSerialNumber(TraderItems->SerialNumber[i]); + if(TraderItems->Charges[i] > 0) + inst->SetCharges(TraderItems->Charges[i]); + + if(inst->IsStackable()) { + inst->SetMerchantCount(TraderItems->Charges[i]); + inst->SetMerchantSlot(TraderItems->SerialNumber[i]); + } + + inst->SetPrice(TraderItems->ItemCost[i]); + SendItemPacket(30, inst, ItemPacketMerchant); + safe_delete(inst); + } + else + _log(TRADING__CLIENT, "Client::BulkSendTraderInventory NULL inst pointer"); + } + else + _log(TRADING__CLIENT, "Client::BulkSendTraderInventory NULL item pointer or item is NODROP %8X",item); + } + safe_delete(TraderItems); +} + +ItemInst* Client::FindTraderItemBySerialNumber(int32 SerialNumber){ + + ItemInst* item = NULL; + uint16 SlotID = 0; + for(int i = 0; i < 8;i++){ + item = this->GetInv().GetItem(22 + i); + if(item && item->GetItem()->ID == 17899){ //Traders Satchel + for(int x = 0; x < 10; x++){ + SlotID = (((22 + i + 3) * 10) + x + 1); + item = this->GetInv().GetItem(SlotID); + if(item) { + if(item->GetSerialNumber() == SerialNumber) + return item; + } + } + } + } + _log(TRADING__CLIENT, "Client::FindTraderItemBySerialNumber Couldn't find item! Serial No. was %i", SerialNumber); + + return NULL; +} + + +GetItems_Struct* Client::GetTraderItems(){ + + const ItemInst* item = NULL; + uint16 SlotID = 0; + + GetItems_Struct* gis= new GetItems_Struct; + + memset(gis,0,sizeof(GetItems_Struct)); + + uint8 ndx = 0; + + for(int i = 0; i < 8; i++){ + item = this->GetInv().GetItem(22 + i); + if(item && item->GetItem()->ID == 17899){ //Traders Satchel + for(int x = 0; x < 10; x++){ + SlotID = (((22 + i +3 ) *10) + x + 1); + + item = this->GetInv().GetItem(SlotID); + + if(item){ + gis->Items[ndx] = item->GetItem()->ID; + gis->SerialNumber[ndx] = item->GetSerialNumber(); + gis->Charges[ndx] = item->GetCharges(); + ndx++; + } + } + } + } + return gis; +} + +uint16 Client::FindTraderItem(int32 SerialNumber, uint16 Quantity){ + + const ItemInst* item= NULL; + uint16 SlotID = 0; + for(int i = 0; i < 8;i++){ + item = this->GetInv().GetItem(22+i); + if(item && item->GetItem()->ID == 17899){ //Traders Satchel + for(int x = 0; x < 10; x++){ + SlotID= (((22 + i + 3) * 10) + x + 1); + + item = this->GetInv().GetItem(SlotID); + + if(item && item->GetSerialNumber() == SerialNumber && + (item->GetCharges() >= Quantity || (item->GetCharges() <= 0 && Quantity == 1))){ + + return SlotID; + } + } + } + } + _log(TRADING__CLIENT, "Could NOT find a match for Item: %i with a quantity of: %i on Trader: %s\n", + SerialNumber , Quantity, this->GetName()); + + return 0; +} + +void Client::NukeTraderItem(uint16 Slot,int16 Charges,uint16 Quantity,Client* Customer,uint16 TraderSlot, int SerialNumber) { + + if(!Customer) return; + _log(TRADING__CLIENT, "NukeTraderItem(Slot %i, Charges %i, Quantity %i", Slot, Charges, Quantity); + if(Quantity < Charges) { + Customer->SendSingleTraderItem(this->CharacterID(), SerialNumber); + m_inv.DeleteItem(Slot, Quantity); + } + else { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderDelItem,sizeof(TraderDelItem_Struct)); + TraderDelItem_Struct* tdis = (TraderDelItem_Struct*)outapp->pBuffer; + + tdis->Unknown000 = 0; + tdis->TraderID = Customer->GetID(); + tdis->ItemID = SerialNumber; + tdis->Unknown012 = 0; + + _pkt(TRADING__PACKETS, outapp); + + Customer->QueuePacket(outapp); + safe_delete(outapp); + + m_inv.DeleteItem(Slot); + + } + // This updates the trader. Removes it from his trading bags. + // + const ItemInst* Inst = m_inv[Slot]; + + database.SaveInventory(CharacterID(), Inst, Slot); + + EQApplicationPacket* outapp2; + + if(Quantity < Charges) + outapp2 = new EQApplicationPacket(OP_DeleteItem,sizeof(MoveItem_Struct)); + else + outapp2 = new EQApplicationPacket(OP_MoveItem,sizeof(MoveItem_Struct)); + + MoveItem_Struct* mis = (MoveItem_Struct*)outapp2->pBuffer; + mis->from_slot = Slot; + mis->to_slot = 0xFFFFFFFF; + mis->number_in_stack = 0xFFFFFFFF; + + if(Quantity >= Charges) + Quantity = 1; + + for(int i = 0; i < Quantity; i++) { + _pkt(TRADING__PACKETS, outapp2); + + this->QueuePacket(outapp2); + } + safe_delete(outapp2); + +} +void Client::TraderUpdate(uint16 SlotID,uint32 TraderID){ + // This method is no longer used. + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderItemUpdate,sizeof(TraderItemUpdate_Struct)); + TraderItemUpdate_Struct* tus=(TraderItemUpdate_Struct*)outapp->pBuffer; + tus->Charges = 0xFFFF; + tus->FromSlot = SlotID; + tus->ToSlot = 0xFF; + tus->TraderID = TraderID; + tus->Unknown000 = 0; + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::FindAndNukeTraderItem(int32 SerialNumber, uint16 Quantity, Client* Customer, uint16 TraderSlot){ + + const ItemInst* item= NULL; + bool Stackable = false; + int16 Charges=0; + + uint16 SlotID = FindTraderItem(SerialNumber, Quantity); + if(SlotID > 0){ + + item = this->GetInv().GetItem(SlotID); + + if(item) { + Charges = this->GetInv().GetItem(SlotID)->GetCharges(); + + Stackable = item->IsStackable(); + + if(!Stackable) + Quantity = (Charges > 0) ? Charges : 1; + + _log(TRADING__CLIENT, "FindAndNuke %s, Charges %i, Quantity %i", item->GetItem()->Name, Charges, Quantity); + } + if(item && (Charges <= Quantity || (Charges <= 0 && Quantity==1) || !Stackable)){ + this->DeleteItemInInventory(SlotID, Quantity); + + TraderCharges_Struct* GetSlot = database.LoadTraderItemWithCharges(this->CharacterID()); + + uint8 Count = 0; + + bool TestSlot = true; + + for(int y = 0;y < 80;y++){ + + if(TestSlot && GetSlot->SerialNumber[y] == SerialNumber){ + + database.DeleteTraderItem(this->CharacterID(),y); + NukeTraderItem(SlotID, Charges, Quantity, Customer, TraderSlot, GetSlot->SerialNumber[y]); + TestSlot=false; + } + else if(GetSlot->ItemID[y] > 0) + Count++; + } + if(Count == 0) + Trader_EndTrader(); + + return; + } + else if(item) { + database.UpdateTraderItemCharges(this->CharacterID(), item->GetSerialNumber(), Charges-Quantity); + + NukeTraderItem(SlotID, Charges, Quantity, Customer, TraderSlot, item->GetSerialNumber()); + + return; + + } + } + _log(TRADING__CLIENT, "Could NOT find a match for Item: %i with a quantity of: %i on Trader: %s\n",SerialNumber, + Quantity,this->GetName()); +} + +void Client::ReturnTraderReq(const EQApplicationPacket* app, int16 TraderItemCharges){ + + TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderBuy, sizeof(TraderBuy_Struct)); + + TraderBuy_Struct* outtbs = (TraderBuy_Struct*)outapp->pBuffer; + + memcpy(outtbs, tbs, app->size); + + outtbs->Price = (tbs->Price * static_cast(TraderItemCharges)); + + outtbs->Quantity = TraderItemCharges; + + outtbs->TraderID = this->GetID(); + + outtbs->AlreadySold = 0; + + QueuePacket(outapp); + + safe_delete(outapp); +} + +void Client::TradeRequestFailed(const EQApplicationPacket* app) { + + TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderBuy, sizeof(TraderBuy_Struct)); + + TraderBuy_Struct* outtbs = (TraderBuy_Struct*)outapp->pBuffer; + + memcpy(outtbs, tbs, app->size); + + outtbs->AlreadySold = 0xFFFFFFFF; + + outtbs->TraderID = 0xFFFFFFFF;; + + QueuePacket(outapp); + + safe_delete(outapp); +} + + +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)"; + + 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); +} + + + +void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicationPacket* app){ + + if(!Trader) return; + + if(!Trader->IsTrader()) { + TradeRequestFailed(app); + return; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Trader,sizeof(TraderBuy_Struct)); + + TraderBuy_Struct* outtbs = (TraderBuy_Struct*)outapp->pBuffer; + + outtbs->ItemID = tbs->ItemID; + + const ItemInst* BuyItem = Trader->FindTraderItemBySerialNumber(tbs->ItemID); + + if(!BuyItem) { + _log(TRADING__CLIENT, "Unable to find item on trader."); + TradeRequestFailed(app); + safe_delete(outapp); + return; + } + + _log(TRADING__CLIENT, "Buyitem: Name: %s, IsStackable: %i, Requested Quantity: %i, Charges on Item %i", + BuyItem->GetItem()->Name, BuyItem->IsStackable(), tbs->Quantity, BuyItem->GetCharges()); + // If the item is not stackable, then we can only be buying one of them. + if(!BuyItem->IsStackable()) + outtbs->Quantity = tbs->Quantity; + else { + // Stackable items, arrows, diamonds, etc + int ItemCharges = BuyItem->GetCharges(); + // ItemCharges for stackables should not be <= 0 + if(ItemCharges <= 0) + outtbs->Quantity = 1; + // If the purchaser requested more than is in the stack, just sell them how many are actually in the stack. + else if(ItemCharges < (int16)tbs->Quantity) + outtbs->Quantity = ItemCharges; + else + outtbs->Quantity = tbs->Quantity; + } + + _log(TRADING__CLIENT, "Actual quantity that will be traded is %i", outtbs->Quantity); + + if((tbs->Price * outtbs->Quantity) <= 0) { + Message(13, "Internal error. Aborting trade. Please report this to the ServerOP. Error code is 1"); + Trader->Message(13, "Internal error. Aborting trade. Please report this to the ServerOP. Error code is 1"); + LogFile->write(EQEMuLog::Error, "Bazaar: Zero price transaction between %s and %s aborted." + "Item: %s, Charges: %i, TBS: Qty %i, Price: %i", + GetName(), Trader->GetName(), + BuyItem->GetItem()->Name, BuyItem->GetCharges(), tbs->Quantity, tbs->Price); + TradeRequestFailed(app); + safe_delete(outapp); + return; + } + + uint64 TotalTransactionValue = static_cast(tbs->Price) * static_cast(outtbs->Quantity); + + if(TotalTransactionValue > MAX_TRANSACTION_VALUE) { + Message(13, "That would exceed the single transaction limit of %u platinum.", MAX_TRANSACTION_VALUE / 1000); + TradeRequestFailed(app); + safe_delete(outapp); + return; + } + + ReturnTraderReq(app, outtbs->Quantity); + + outtbs->TraderID = this->GetID(); + + outtbs->Action = BazaarBuyItem; + + strn0cpy(outtbs->ItemName, BuyItem->GetItem()->Name, 64); + + int TraderSlot = 0; + + if(BuyItem->IsStackable()) + SendTraderItem(BuyItem->GetItem()->ID, outtbs->Quantity); + else + SendTraderItem(BuyItem->GetItem()->ID, BuyItem->GetCharges()); + + + EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_MoneyUpdate,sizeof(MoneyUpdate_Struct)); + + MoneyUpdate_Struct* mus= (MoneyUpdate_Struct*)outapp2->pBuffer; + + // This cannot overflow assuming MAX_TRANSACTION_VALUE, checked above, is the default of 2000000000 + uint32 TotalCost = tbs->Price * outtbs->Quantity; + + outtbs->Price = TotalCost; + + this->TakeMoneyFromPP(TotalCost); + + mus->platinum = TotalCost / 1000; + + TotalCost -= (mus->platinum * 1000); + + mus->gold = TotalCost / 100; + + TotalCost -= (mus->gold * 100); + + mus->silver = TotalCost / 10; + + TotalCost -= (mus->silver * 10); + + mus->copper = TotalCost; + + Trader->AddMoneyToPP(mus->copper,mus->silver,mus->gold,mus->platinum,false); + + mus->platinum = Trader->GetPlatinum(); + mus->gold = Trader->GetGold(); + mus->silver = Trader->GetSilver(); + mus->copper = Trader->GetCopper(); + + TraderSlot = Trader->FindTraderItem(tbs->ItemID, outtbs->Quantity); + + Trader->QueuePacket(outapp2); + + _pkt(TRADING__PACKETS, outapp2); + + if(RuleB(Bazaar, AuditTrail)) + BazaarAuditTrail(Trader->GetName(), GetName(), BuyItem->GetItem()->Name, outtbs->Quantity, outtbs->Price, 0); + + Trader->FindAndNukeTraderItem(tbs->ItemID, outtbs->Quantity, this, 0); + + Trader->QueuePacket(outapp); + + _pkt(TRADING__PACKETS, outapp); + + safe_delete(outapp); + safe_delete(outapp2); + +} + +void Client::SendBazaarWelcome(){ + + char errbuf[MYSQL_ERRMSG_SIZE]; + + char* query = 0; + + MYSQL_RES *result; + + MYSQL_ROW row; + + if (database.RunQuery(query,MakeAnyLenString(&query, "select count(distinct char_id),count(char_id) from trader"),errbuf,&result)){ + if(mysql_num_rows(result)==1){ + + row = mysql_fetch_row(result); + + 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_array(query); + + if (database.RunQuery(query,MakeAnyLenString(&query, "select count(distinct charid) from buyer"),errbuf,&result)){ + if(mysql_num_rows(result)==1) { + + 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); +} + +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; + string Search, Values; + MYSQL_RES *Result; + MYSQL_ROW Row; + char Tmp[100] = {0}; + + Values.append("count(item_id),trader.*,items.name"); + + Search.append("where trader.item_id=items.id"); + + if(TraderID > 0){ + Client* Trader = entity_list.GetClientByID(TraderID); + + if(Trader){ + sprintf(Tmp," and trader.char_id=%i",Trader->CharacterID()); + Search.append(Tmp); + } + + } + 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){ + + switch(Type){ + + 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); + } + } + + switch(ItemStat) { + + case STAT_AC: + Search.append(" and items.ac>0"); + Values.append(",items.ac"); + break; + + case STAT_AGI: + Search.append(" and items.aagi>0"); + Values.append(",items.aagi"); + break; + + case STAT_CHA: + Search.append(" and items.acha>0"); + Values.append(",items.acha"); + break; + + case STAT_DEX: + Search.append(" and items.adex>0"); + Values.append(",items.adex"); + break; + + case STAT_INT: + Search.append(" and items.aint>0"); + Values.append(",items.aint"); + break; + + case STAT_STA: + Search.append(" and items.asta>0"); + Values.append(",items.asta"); + break; + + case STAT_STR: + Search.append(" and items.astr>0"); + Values.append(",items.astr"); + break; + + case STAT_WIS: + Search.append(" and items.awis>0"); + Values.append(",items.awis"); + break; + + case STAT_COLD: + Search.append(" and items.cr>0"); + Values.append(",items.cr"); + break; + + case STAT_DISEASE: + Search.append(" and items.dr>0"); + Values.append(",items.dr"); + break; + + case STAT_FIRE: + Search.append(" and items.fr>0"); + Values.append(",items.fr"); + break; + + case STAT_MAGIC: + Values.append(",items.mr"); + Search.append(" and items.mr>0"); + break; + + case STAT_POISON: + Search.append(" and items.pr>0"); + Values.append(",items.pr"); + break; + + case STAT_HP: + Search.append(" and items.hp>0"); + Values.append(",items.hp"); + break; + + case STAT_MANA: + Search.append(" and items.mana>0"); + Values.append(",items.mana"); + break; + + case STAT_ENDURANCE: + Search.append(" and items.endur>0"); + Values.append(",items.endur"); + break; + + case STAT_ATTACK: + Search.append(" and items.attack>0"); + Values.append(",items.attack"); + break; + + case STAT_HP_REGEN: + Search.append(" and items.regen>0"); + Values.append(",items.regen"); + break; + + case STAT_MANA_REGEN: + Search.append(" and items.manaregen>0"); + Values.append(",items.manaregen"); + break; + + case STAT_HASTE: + Search.append(" and items.haste>0"); + Values.append(",items.haste"); + break; + + case STAT_DAMAGE_SHIELD: + Search.append(" and items.damageshield>00"); + Values.append(",items.damageshield"); + break; + + default: + Values.append(",0"); + break; + } + + Values.append(",sum(charges), items.stackable "); + + 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); + safe_delete_array(Query); + + int Size = 0; + uint32 ID = 0; + + if(mysql_num_rows(Result) == 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); + + 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; + } +} + +static void UpdateTraderCustomerItemsAdded(uint32 CustomerID, TraderCharges_Struct* gis, uint32 ItemID) { + + // Send Item packets to the customer to update the Merchant window with the + // new items for sale, and give them a message in their chat window. + + Client* Customer = entity_list.GetClientByID(CustomerID); + + if(!Customer) return; + + const Item_Struct *item = database.GetItem(ItemID); + + if(!item) return; + + ItemInst* inst = database.CreateItem(item); + + if(!inst) return; + + Customer->Message(13, "The Trader has put up %s for sale.", item->Name); + + for(int i = 0; i < 80; i++) { + + if(gis->ItemID[i] == ItemID) { + + inst->SetCharges(gis->Charges[i]); + + inst->SetPrice(gis->ItemCost[i]); + + inst->SetSerialNumber(gis->SerialNumber[i]); + + inst->SetMerchantSlot(gis->SerialNumber[i]); + + if(inst->IsStackable()) + inst->SetMerchantCount(gis->Charges[i]); + + _log(TRADING__CLIENT, "Sending price update for %s, Serial No. %i with %i charges", + item->Name, gis->SerialNumber[i], gis->Charges[i]); + + Customer->SendItemPacket(30, inst, ItemPacketMerchant); + } + } + + safe_delete(inst); +} + +static void UpdateTraderCustomerPriceChanged(uint32 CustomerID, TraderCharges_Struct* gis, uint32 ItemID, int32 Charges, uint32 NewPrice) { + + // Send ItemPackets to update the customer's Merchant window with the new price (or remove the item if + // the new price is 0) and inform them with a chat message. + + Client* Customer = entity_list.GetClientByID(CustomerID); + + if(!Customer) return; + + const Item_Struct *item = database.GetItem(ItemID); + + if(!item) return; + + if(NewPrice == 0) { + // If the new price is 0, remove the item(s) from the window. + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderDelItem,sizeof(TraderDelItem_Struct)); + TraderDelItem_Struct* tdis = (TraderDelItem_Struct*)outapp->pBuffer; + + tdis->Unknown000 = 0; + tdis->TraderID = Customer->GetID(); + tdis->Unknown012 = 0; + Customer->Message(13, "The Trader has withdrawn the %s from sale.", item->Name); + + for(int i = 0; i < 80; i++) { + + if(gis->ItemID[i] == ItemID) { + + tdis->ItemID = gis->SerialNumber[i]; + _log(TRADING__CLIENT, "Telling customer to remove item %i with %i charges and S/N %i", + ItemID, Charges, gis->SerialNumber[i]); + + _pkt(TRADING__PACKETS, outapp); + + Customer->QueuePacket(outapp); + } + } + + safe_delete(outapp); + return; + } + + _log(TRADING__CLIENT, "Sending price updates to customer %s", Customer->GetName()); + + ItemInst* inst = database.CreateItem(item); + + if(!inst) return; + + if(Charges > 0) + inst->SetCharges(Charges); + + inst->SetPrice(NewPrice); + + if(inst->IsStackable()) + inst->SetMerchantCount(Charges); + + // Let the customer know the price in the window has suddenly just changed on them. + Customer->Message(13, "The Trader has changed the price of %s.", item->Name); + + for(int i = 0; i < 80; i++) { + if((gis->ItemID[i] != ItemID) || + ((!item->Stackable) && (gis->Charges[i] != Charges))) + continue; + + inst->SetSerialNumber(gis->SerialNumber[i]); + + inst->SetMerchantSlot(gis->SerialNumber[i]); + + _log(TRADING__CLIENT, "Sending price update for %s, Serial No. %i with %i charges", + item->Name, gis->SerialNumber[i], gis->Charges[i]); + + Customer->SendItemPacket(30, inst, ItemPacketMerchant); + } + safe_delete(inst); +} + +void Client::HandleTraderPriceUpdate(const EQApplicationPacket *app) { + + // Handle price updates from the Trader and update a customer browsing our stuff if necessary + // This method also handles removing items from sale and adding them back up whilst still in + // Trader mode. + // + TraderPriceUpdate_Struct* tpus = (TraderPriceUpdate_Struct*)app->pBuffer; + + _log(TRADING__CLIENT, "Received Price Update for %s, Item Serial No. %i, New Price %i", + GetName(), tpus->SerialNumber, tpus->NewPrice); + + // Pull the items this Trader currently has for sale from the trader table. + // + TraderCharges_Struct* gis = database.LoadTraderItemWithCharges(CharacterID()); + + if(!gis) { + _log(TRADING__CLIENT, "Error retrieving Trader items details to update price."); + return; + } + + // The client only sends a single update with the Serial Number of the item whose price has been updated. + // We must update the price for all the Trader's items that are identical to that one item, i.e. + // if it is a stackable item like arrows, update the price for all stacks. If it is not stackable, then + // update the prices for all items that have the same number of charges. + // + uint32 IDOfItemToUpdate = 0; + + int32 ChargesOnItemToUpdate = 0; + + uint32 OldPrice = 0; + + for(int i = 0; i < 80; i++) { + + if((gis->ItemID[i] > 0) && (gis->SerialNumber[i] == tpus->SerialNumber)) { + // We found the item that the Trader wants to change the price of (or add back up for sale). + // + _log(TRADING__CLIENT, "ItemID is %i, Charges is %i", gis->ItemID[i], gis->Charges[i]); + + IDOfItemToUpdate = gis->ItemID[i]; + + ChargesOnItemToUpdate = gis->Charges[i]; + + OldPrice = gis->ItemCost[i]; + + break; + } + } + + if(IDOfItemToUpdate == 0) { + + // If the item is not currently in the trader table for this Trader, then they must have removed it from sale while + // still in Trader mode. Check if the item is in their Trader Satchels, and if so, put it back up. + + // Quick Sanity check. If the item is not currently up for sale, and the new price is zero, just ack the packet + // and do nothing. + if(tpus->NewPrice == 0) { + tpus->SubAction = BazaarPriceChange_RemoveItem; + QueuePacket(app); + safe_delete(gis); + return ; + } + + _log(TRADING__CLIENT, "Unable to find item to update price for. Rechecking trader satchels"); + + // Find what is in their Trader Satchels + GetItems_Struct* newgis=GetTraderItems(); + + uint32 IDOfItemToAdd = 0; + + int32 ChargesOnItemToAdd = 0; + + for(int i = 0; i < 80; i++) { + + if((newgis->Items[i] > 0) && (newgis->SerialNumber[i] == tpus->SerialNumber)) { + + _log(TRADING__CLIENT, "Found new Item to Add, ItemID is %i, Charges is %i", newgis->Items[i], + newgis->Charges[i]); + + IDOfItemToAdd = newgis->Items[i]; + ChargesOnItemToAdd = newgis->Charges[i]; + + break; + } + } + + + const Item_Struct *item = 0; + + if(IDOfItemToAdd) + item = database.GetItem(IDOfItemToAdd); + + if(!IDOfItemToAdd || !item) { + + _log(TRADING__CLIENT, "Item not found in Trader Satchels either."); + tpus->SubAction = BazaarPriceChange_Fail; + QueuePacket(app); + Trader_EndTrader(); + safe_delete(gis); + safe_delete(newgis); + return; + } + + // It is a limitation of the client that if you have multiple of the same item, but with different charges, + // although you can set different prices for them before entering Trader mode. If you Remove them and then + // add them back whilst still in Trader mode, they all go up for the same price. We check for this situation + // and give the Trader a warning message. + // + if(!item->Stackable) { + + bool SameItemWithDifferingCharges = false; + + for(int i = 0; i < 80; i++) { + if((newgis->Items[i] == IDOfItemToAdd) && (newgis->Charges[i] != ChargesOnItemToAdd)) { + + SameItemWithDifferingCharges = true; + break; + } + } + + if(SameItemWithDifferingCharges) + Message(13, "Warning: You have more than one %s with different charges. They have all been added for sale " + "at the same price.", item->Name); + } + + // Now put all Items with a matching ItemID up for trade. + // + for(int i = 0; i < 80; i++) { + + if(newgis->Items[i] == IDOfItemToAdd) { + + database.SaveTraderItem(CharacterID(), newgis->Items[i], newgis->SerialNumber[i], newgis->Charges[i], + tpus->NewPrice, i); + + gis->ItemID[i] = newgis->Items[i]; + gis->Charges[i] = newgis->Charges[i]; + gis->SerialNumber[i] = newgis->SerialNumber[i]; + gis->ItemCost[i] = tpus->NewPrice; + + _log(TRADING__CLIENT, "Adding new item for %s. ItemID %i, SerialNumber %i, Charges %i, Price: %i, Slot %i", + GetName(), newgis->Items[i], newgis->SerialNumber[i], newgis->Charges[i], + tpus->NewPrice, i); + } + } + + // If we have a customer currently browsing, update them with the new items. + // + if(CustomerID) + UpdateTraderCustomerItemsAdded(CustomerID, gis, IDOfItemToAdd); + + safe_delete(gis); + safe_delete(newgis); + + // Acknowledge to the client. + tpus->SubAction = BazaarPriceChange_AddItem; + QueuePacket(app); + + return; + } + + // This is a safeguard against a Trader increasing the price of an item while a customer is browsing and + // unwittingly buying it at a higher price than they were expecting to. + // + if((OldPrice != 0) && (tpus->NewPrice > OldPrice) && CustomerID) { + + tpus->SubAction = BazaarPriceChange_Fail; + QueuePacket(app); + Trader_EndTrader(); + Message(13, "You must remove the item from sale before you can increase the price while a customer is browsing."); + Message(13, "Click 'Begin Trader' to restart Trader mode with the increased price for this item."); + safe_delete(gis); + return; + } + + // Send Acknowledgement back to the client. + if(OldPrice == 0) + tpus->SubAction = BazaarPriceChange_AddItem; + else if(tpus->NewPrice != 0) + tpus->SubAction = BazaarPriceChange_UpdatePrice; + else + tpus->SubAction = BazaarPriceChange_RemoveItem; + + QueuePacket(app); + + if(OldPrice == tpus->NewPrice) { + _log(TRADING__CLIENT, "The new price is the same as the old one."); + safe_delete(gis); + return; + } + // Update the price for all items we have for sale that have this ItemID and number of charges, or remove + // them from the trader table if the new price is zero. + // + database.UpdateTraderItemPrice(CharacterID(), IDOfItemToUpdate, ChargesOnItemToUpdate, tpus->NewPrice); + + // If a customer is browsing our goods, send them the updated prices / remove the items from the Merchant window + if(CustomerID) + UpdateTraderCustomerPriceChanged(CustomerID, gis, IDOfItemToUpdate, ChargesOnItemToUpdate, tpus->NewPrice); + + safe_delete(gis); + +} + +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); + + char errbuf[MYSQL_ERRMSG_SIZE]; + char* Query = 0; + char ItemName[64]; + string Search, Values; + MYSQL_RES *Result; + MYSQL_ROW Row; + + char*EscSearchString = new char[strlen(SearchString) * 2 + 1]; + database.DoEscapeString(EscSearchString, SearchString, strlen(SearchString)); + + 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 = 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 == 0) { + mysql_free_result(Result); + safe_delete_array(Query); + return; + } + + uint32 LastCharID = 0; + Client *Buyer = NULL; + + while ((Row = mysql_fetch_row(Result))) { + + 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(!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) { + + BuyerInspectRequest_Struct* bir = ( BuyerInspectRequest_Struct*)app->pBuffer; + + Client *Buyer = entity_list.GetClientByID(bir->BuyerID); + + if(!Buyer) { + bir->Approval = 0; // Tell the client that the Buyer is unavailable + QueuePacket(app); + Message(13, "The Buyer has gone away."); + return; + } + + bir->Approval = Buyer->WithCustomer(GetID()); + + QueuePacket(app); + + if(bir->Approval == 0) { + Message_StringID(clientMessageYellow, TRADER_BUSY); + return; + } + + const char *WelcomeMessagePointer = Buyer->GetBuyerWelcomeMessage(); + + if(strlen(WelcomeMessagePointer) > 0) + Message(10, "%s greets you, '%s'.", Buyer->GetName(), WelcomeMessagePointer); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, sizeof(BuyerBrowsing_Struct)); + + BuyerBrowsing_Struct* bb = (BuyerBrowsing_Struct*)outapp->pBuffer; + + // This packet produces the SoandSo is browsing your Buy Lines message + bb->Action = Barter_SellerBrowsing; + + sprintf(bb->PlayerName, GetName()); + + Buyer->QueuePacket(outapp); + + safe_delete(outapp); + + char errbuf[MYSQL_ERRMSG_SIZE]; + char* Query = 0; + char ItemName[64]; + string Search, Values; + MYSQL_RES *Result; + MYSQL_ROW Row; + + if (database.RunQuery(Query,MakeAnyLenString(&Query, "select * from buyer where charid = %i", + Buyer->CharacterID()),errbuf,&Result)){ + + if(mysql_num_rows(Result) == 0) { + + safe_delete_array(Query); + + mysql_free_result(Result); + + return; + } + + while ((Row = mysql_fetch_row(Result))) { + + 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); +} + +void Client::SellToBuyer(const EQApplicationPacket *app) { + + char* Buf = (char *)app->pBuffer; + + char ItemName[64]; + + /*uint32 Action =*/ VARSTRUCT_SKIP_TYPE(uint32, Buf); //unused + uint32 Quantity = VARSTRUCT_DECODE_TYPE(uint32, Buf); + uint32 BuyerID = VARSTRUCT_DECODE_TYPE(uint32, Buf); + uint32 BuySlot = VARSTRUCT_DECODE_TYPE(uint32, Buf); + uint32 UnknownByte = VARSTRUCT_DECODE_TYPE(uint8, Buf); + uint32 ItemID = VARSTRUCT_DECODE_TYPE(uint32, Buf); + /* ItemName */ VARSTRUCT_DECODE_STRING(ItemName, Buf); + /*uint32 Unknown2 =*/ VARSTRUCT_SKIP_TYPE(uint32, Buf); //unused + uint32 QtyBuyerWants = VARSTRUCT_DECODE_TYPE(uint32, Buf); + UnknownByte = VARSTRUCT_DECODE_TYPE(uint8, Buf); + uint32 Price = VARSTRUCT_DECODE_TYPE(uint32, Buf); + /*uint32 BuyerID2 =*/ VARSTRUCT_SKIP_TYPE(uint32, Buf); //unused + /*uint32 Unknown3 =*/ VARSTRUCT_SKIP_TYPE(uint32, Buf); //unused + + const Item_Struct *item = database.GetItem(ItemID); + + if(!item || !Quantity || !Price || !QtyBuyerWants) return; + + if(m_inv.HasItem(ItemID, Quantity, invWhereWorn|invWherePersonal|invWhereCursor) == SLOT_INVALID) { + + Message(13, "You do not have %i %s on you.", Quantity, item->Name); + + return; + } + + + Client *Buyer = entity_list.GetClientByID(BuyerID); + + if(!Buyer || !Buyer->IsBuyer()) { + Message(13, "The Buyer has gone away."); + return; + } + + // For Stackable items, HasSpaceForItem will try check if there is space to stack with existing stacks in + // the buyer inventory. + if(!(Buyer->GetInv().HasSpaceForItem(item, Quantity))) { + + Message(13, "The Buyer does not have space for %i %s", Quantity, item->Name); + + return; + } + + if((static_cast(Quantity) * static_cast(Price)) > MAX_TRANSACTION_VALUE) { + Message(13, "That would exceed the single transaction limit of %u platinum.", MAX_TRANSACTION_VALUE / 1000); + return; + } + + if(!Buyer->HasMoney(Quantity * Price)) { + Message(13, "The Buyer does not have sufficient money to purchase that quantity of %s.", item->Name); + Buyer->Message(13, "%s tried to sell you %i %s, but you have insufficient funds.", GetName(), Quantity, item->Name); + return; + } + + if(Buyer->CheckLoreConflict(item)) { + Message(13, "That item is LORE and the Buyer already has one."); + Buyer->Message(13, "%s tried to sell you %s but this item is LORE and you already have one.", + GetName(), item->Name); + return; + } + + if(item->NoDrop == 0) { + Message(13, "That item is NODROP."); + return; + } + + if(!item->Stackable) { + + for(uint32 i = 0; i < Quantity; i++) { + + int16 SellerSlot = m_inv.HasItem(ItemID, 1, invWhereWorn|invWherePersonal|invWhereCursor); + + // This shouldn't happen, as we already checked there was space in the Buyer's inventory + if(SellerSlot == SLOT_INVALID) { + + if(i > 0) { + // Set the Quantity to the actual number we successfully transferred. + Quantity = i; + break; + } + _log(TRADING__BARTER, "Unexpected error while moving item from seller to buyer."); + Message(13, "Internal error while processing transaction."); + return; + } + + ItemInst* ItemToTransfer = m_inv.PopItem(SellerSlot); + + if(!ItemToTransfer || !Buyer->MoveItemToInventory(ItemToTransfer, true)) { + _log(TRADING__BARTER, "Unexpected error while moving item from seller to buyer."); + Message(13, "Internal error while processing transaction."); + + if(ItemToTransfer) + safe_delete(ItemToTransfer); + + return; + } + + database.SaveInventory(CharacterID(), 0, SellerSlot); + + safe_delete(ItemToTransfer); + + // Remove the item from inventory, clientside + // + EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_MoveItem,sizeof(MoveItem_Struct)); + + MoveItem_Struct* mis = (MoveItem_Struct*)outapp2->pBuffer; + mis->from_slot = SellerSlot; + mis->to_slot = 0xFFFFFFFF; + mis->number_in_stack = 0xFFFFFFFF; + + QueuePacket(outapp2); + safe_delete(outapp2); + + } + } + else { + // Stackable + // + uint32 QuantityMoved = 0; + + while(QuantityMoved < Quantity) { + + // Find the slot on the seller that has a stack of at least 1 of the item + int16 SellerSlot = m_inv.HasItem(ItemID, 1, invWhereWorn|invWherePersonal|invWhereCursor); + + if(SellerSlot == SLOT_INVALID) { + _log(TRADING__BARTER, "Unexpected error while moving item from seller to buyer."); + Message(13, "Internal error while processing transaction."); + return; + } + + ItemInst* ItemToTransfer = m_inv.PopItem(SellerSlot); + + if(!ItemToTransfer) { + _log(TRADING__BARTER, "Unexpected error while moving item from seller to buyer."); + Message(13, "Internal error while processing transaction."); + return; + } + + // If the stack we found has less than the quantity we are selling ... + if(ItemToTransfer->GetCharges() <= (Quantity - QuantityMoved)) { + // Transfer the entire stack + + QuantityMoved += ItemToTransfer->GetCharges(); + + if(!Buyer->MoveItemToInventory(ItemToTransfer, true)) { + _log(TRADING__BARTER, "Unexpected error while moving item from seller to buyer."); + Message(13, "Internal error while processing transaction."); + safe_delete(ItemToTransfer); + return; + } + // Delete the entire stack from the seller's inventory + database.SaveInventory(CharacterID(), 0, SellerSlot); + + safe_delete(ItemToTransfer); + + // and tell the client to do the same. + EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_MoveItem,sizeof(MoveItem_Struct)); + + MoveItem_Struct* mis = (MoveItem_Struct*)outapp2->pBuffer; + mis->from_slot = SellerSlot; + mis->to_slot = 0xFFFFFFFF; + mis->number_in_stack = 0xFFFFFFFF; + + QueuePacket(outapp2); + safe_delete(outapp2); + } + else { + //Move the amount we need, and put the rest of the stack back in the seller's inventory + // + int QuantityToRemoveFromStack = Quantity - QuantityMoved; + + ItemToTransfer->SetCharges(ItemToTransfer->GetCharges() - QuantityToRemoveFromStack); + + m_inv.PutItem(SellerSlot, *ItemToTransfer); + + database.SaveInventory(CharacterID(), ItemToTransfer, SellerSlot); + + ItemToTransfer->SetCharges(QuantityToRemoveFromStack); + + if(!Buyer->MoveItemToInventory(ItemToTransfer, true)) { + _log(TRADING__BARTER, "Unexpected error while moving item from seller to buyer."); + Message(13, "Internal error while processing transaction."); + safe_delete(ItemToTransfer); + return; + } + + safe_delete(ItemToTransfer); + + EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_DeleteItem,sizeof(MoveItem_Struct)); + + MoveItem_Struct* mis = (MoveItem_Struct*)outapp2->pBuffer; + mis->from_slot = SellerSlot; + mis->to_slot = 0xFFFFFFFF; + mis->number_in_stack = 0xFFFFFFFF; + + for(int i = 0; i < QuantityToRemoveFromStack; i++) + QueuePacket(outapp2); + + safe_delete(outapp2); + + QuantityMoved = Quantity; + } + } + + } + + Buyer->TakeMoneyFromPP(Quantity * Price); + + AddMoneyToPP(Quantity * Price, false); + + if(RuleB(Bazaar, AuditTrail)) + BazaarAuditTrail(GetName(), Buyer->GetName(), ItemName, Quantity, Quantity * Price, 1); + + // We now send a packet to the Seller, which causes it to display 'You have sold to for ' + // + // The PacketLength of 1016 is from the only instance of this packet I have seen, which is from Live, November 2008 + // The Titanium/6.2 struct is slightly different in that it appears to use fixed length strings instead of variable + // length as used on Live. The extra space in the packet is also likely to be used for Item compensation, if we ever + // implement that. + // + uint32 PacketLength = 1016; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, PacketLength); + + Buf = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_SellerTransactionComplete); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Quantity); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Quantity * Price); + + if(GetClientVersion() >= EQClientSoD) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0); // Think this is the upper 32 bits of a 64 bit price + } + + sprintf(Buf, "%s", Buyer->GetName()); Buf += 64; + + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0x00); + VARSTRUCT_ENCODE_TYPE(uint8, Buf, 0x01); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0x00); + + sprintf(Buf, "%s", ItemName); Buf += 64; + + _pkt(TRADING__BARTER, outapp); + QueuePacket(outapp); + + // This next packet goes to the Buyer and produces the 'You've bought from for ' + // + + Buf = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerTransactionComplete); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Quantity); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Quantity * Price); + + if(Buyer->GetClientVersion() >= EQClientSoD) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0); // Think this is the upper 32 bits of a 64 bit price + } + + sprintf(Buf, "%s", GetName()); Buf += 64; + + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0x00); + VARSTRUCT_ENCODE_TYPE(uint8, Buf, 0x01); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0x00); + + sprintf(Buf, "%s", ItemName); Buf += 64; + + Buyer->QueuePacket(outapp); + + safe_delete(outapp); + + // Next we update the buyer table in the database to reflect the reduced quantity the Buyer wants to buy. + // + database.UpdateBuyLine(Buyer->CharacterID(), BuySlot, QtyBuyerWants - Quantity); + + // Next we update the Seller's Barter Window to reflect the reduced quantity the Buyer is now looking to buy. + // + EQApplicationPacket* outapp3 = new EQApplicationPacket(OP_Barter, 936); + + Buf = (char *)outapp3->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerInspectWindow); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, BuySlot); + VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, Buf, ItemID); + VARSTRUCT_ENCODE_STRING( Buf, ItemName); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, item->Icon); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, QtyBuyerWants - Quantity); + + // If the amount we have just sold completely satisfies the quantity the Buyer was looking for, + // setting the next byte to 0 will remove the item from the Barter Window. + // + if(QtyBuyerWants - Quantity > 0) { + VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // 0 = Toggle Off, 1 = Toggle On + } + else { + VARSTRUCT_ENCODE_TYPE(uint8, Buf, 0); // 0 = Toggle Off, 1 = Toggle On + } + + 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, outapp3); + QueuePacket(outapp3); + safe_delete(outapp3); + + // The next packet updates the /buyer window with the reduced quantity, and toggles the buy line off if the + // quantity they wanted to buy has been met. + // + EQApplicationPacket* outapp4 = new EQApplicationPacket(OP_Barter, 936); + + Buf = (char*)outapp4->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerItemUpdate); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, BuySlot); + VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, ItemID); + VARSTRUCT_ENCODE_STRING( Buf, ItemName); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, item->Icon); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, QtyBuyerWants - Quantity); + + if((QtyBuyerWants - Quantity) > 0) { + + VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // 0 = Toggle Off, 1 = Toggle On + } + else { + VARSTRUCT_ENCODE_TYPE(uint8, Buf, 0); // 0 = Toggle Off, 1 = Toggle On + } + + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Price); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0x08f4); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0); + VARSTRUCT_ENCODE_STRING( Buf, Buyer->GetName()); + + _pkt(TRADING__BARTER, outapp4); + Buyer->QueuePacket(outapp4); + safe_delete(outapp4); + + return; +} + +void Client::SendBuyerPacket(Client* Buyer) { + + // This is the Buyer Appearance packet. This method is called for each Buyer when a Client connects to the zone. + // + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 13 + strlen(GetName())); + + char* Buf = (char*)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerAppearance); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Buyer->GetID()); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0x01); + VARSTRUCT_ENCODE_STRING(Buf, GetName()); + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::ToggleBuyerMode(bool TurnOn) { + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 13 + strlen(GetName())); + + char* Buf = (char*)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerAppearance); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, GetID()); + + if(TurnOn) { + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0x01); + } + else { + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0x00); + database.DeleteBuyLines(CharacterID()); + CustomerID = 0; + } + + VARSTRUCT_ENCODE_STRING(Buf, GetName()); + + entity_list.QueueClients(this, outapp, false); + + _pkt(TRADING__BARTER, outapp); + safe_delete(outapp); + + Buyer = TurnOn; +} + +void Client::UpdateBuyLine(const EQApplicationPacket *app) { + + // This method is called when: + // + // /buyer mode is first turned on, once for each item + // A BuyLine is toggled on or off in the/buyer window. + // + char* Buf = (char*)app->pBuffer; + + char ItemName[64]; + + /*uint32 Action =*/ VARSTRUCT_SKIP_TYPE(uint32, Buf); //unused + uint32 BuySlot = VARSTRUCT_DECODE_TYPE(uint32, Buf); + uint8 Unknown009 = VARSTRUCT_DECODE_TYPE(uint8, Buf); + uint32 ItemID = VARSTRUCT_DECODE_TYPE(uint32, Buf); + /* ItemName */ VARSTRUCT_DECODE_STRING(ItemName, Buf); + uint32 Icon = VARSTRUCT_DECODE_TYPE(uint32, Buf); + uint32 Quantity = VARSTRUCT_DECODE_TYPE(uint32, Buf); + uint8 ToggleOnOff = VARSTRUCT_DECODE_TYPE(uint8, Buf); + uint32 Price = VARSTRUCT_DECODE_TYPE(uint32, Buf); + /*uint32 UnknownZ =*/ VARSTRUCT_SKIP_TYPE(uint32, Buf); //unused + uint32 ItemCount = VARSTRUCT_DECODE_TYPE(uint32, Buf); + + const Item_Struct *item = database.GetItem(ItemID); + + if(!item) return; + + bool LoreConflict = CheckLoreConflict(item); + + _log(TRADING__BARTER, "UpdateBuyLine: Char: %s BuySlot: %i ItemID %i %s Quantity %i Toggle: %i Price %i ItemCount %i LoreConflict %i", + GetName(), BuySlot, ItemID, item->Name, Quantity, ToggleOnOff, Price, ItemCount, LoreConflict); + + if((item->NoDrop != 0) && !LoreConflict && (Quantity > 0) && HasMoney(Quantity * Price) && ToggleOnOff && (ItemCount == 0)) { + _log(TRADING__BARTER, "Adding to database"); + database.AddBuyLine(CharacterID(), BuySlot, ItemID, ItemName, Quantity, Price); + QueuePacket(app); + } + else { + if(ItemCount > 0) + Message(13, "Buy line %s disabled as Item Compensation is not currently supported.", ItemName); + + else if(Quantity <= 0) + Message(13, "Buy line %s disabled as the quantity is invalid.", ItemName); + + else if(LoreConflict) + Message(13, "Buy line %s disabled as the item is LORE and you have one already.", ItemName); + + else if(item->NoDrop == 0) + Message(13, "Buy line %s disabled as the item is NODROP.", ItemName); + + else if(ToggleOnOff) + Message(13, "Buy line %s disabled due to insufficient funds.", ItemName); + + else + database.RemoveBuyLine(CharacterID(), BuySlot); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 936); + + Buf = (char*)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerItemUpdate); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, BuySlot); + VARSTRUCT_ENCODE_TYPE(uint8, Buf, Unknown009); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, ItemID); + VARSTRUCT_ENCODE_STRING( Buf, ItemName); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Icon); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Quantity); + VARSTRUCT_ENCODE_TYPE(uint8, Buf, 0); // Toggle the Buy Line off in the client + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Price); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0x08f4); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0); + VARSTRUCT_ENCODE_STRING( Buf, GetName()); + + _pkt(TRADING__BARTER, outapp); + QueuePacket(outapp); + safe_delete(outapp); + } + +} + +void Client::BuyerItemSearch(const EQApplicationPacket *app) { + + BuyerItemSearch_Struct* bis = (BuyerItemSearch_Struct*)app->pBuffer; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, sizeof(BuyerItemSearchResults_Struct)); + + BuyerItemSearchResults_Struct* bisr = (BuyerItemSearchResults_Struct*)outapp->pBuffer; + + const Item_Struct* item = 0; + + int Count=0; + + char Name[64]; + char Criteria[255]; + + strn0cpy(Criteria, bis->SearchString, sizeof(Criteria)); + + strupr(Criteria); + + char* pdest; + + uint32 it = 0; + + while ((item = database.IterateItems(&it))) { + + strn0cpy(Name, item->Name, sizeof(Name)); + + strupr(Name); + + pdest = strstr(Name, Criteria); + + if (pdest != NULL) { + sprintf(bisr->Results[Count].ItemName, item->Name); + bisr->Results[Count].ItemID = item->ID; + bisr->Results[Count].Unknown068 = item->Icon; + bisr->Results[Count].Unknown072 = 0x00000000; + Count++; + } + if (Count == MAX_BUYER_ITEMSEARCH_RESULTS) + break; + } + if (Count == MAX_BUYER_ITEMSEARCH_RESULTS) + Message(15, "Your search returned more than %i results. Only the first %i are displayed.", + MAX_BUYER_ITEMSEARCH_RESULTS, MAX_BUYER_ITEMSEARCH_RESULTS); + + bisr->Action = Barter_BuyerSearch; + bisr->ResultCount = Count; + + _pkt(TRADING__BARTER, outapp); + QueuePacket(outapp); + safe_delete(outapp); +} + +bool Client::StoreTurnInItems(Mob* tradingWith) { + + if ( !tradingWith || !tradingWith->IsNPC() ) + return false; + + for (int16 i=3000; i<=3003; i++) { + const ItemInst* inst = m_inv[i]; + if (inst) { + database.logevents(AccountName(),AccountID(),admin,GetName(),tradingWith->GetName(),"Quest Turn In Attempt",inst->GetItem()->Name,22); + + tradingWith->CastToNPC()->AddQuestItem(inst->Clone()); + } + } + + return true; +} + diff --git a/zone/trap.cpp b/zone/trap.cpp new file mode 100644 index 000000000..9357add0e --- /dev/null +++ b/zone/trap.cpp @@ -0,0 +1,339 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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/types.h" +#include "entity.h" +#include "masterentity.h" +#include "spdat.h" +#include "../common/MiscFunctions.h" + +/* + +Schema: +CREATE TABLE traps ( + id int(11) NOT NULL auto_increment, + zone varchar(16) NOT NULL default '', + x int(11) NOT NULL default '0', + y int(11) NOT NULL default '0', + z int(11) NOT NULL default '0', + chance tinyint NOT NULL default '0', + maxzdiff float NOT NULL default '0', + radius float NOT NULL default '0', + effect int(11) NOT NULL default '0', + effectvalue int(11) NOT NULL default '0', + effectvalue2 int(11) NOT NULL default '0', + message varcahr(200) NOT NULL; + skill int(11) NOT NULL default '0', + spawnchance int(11) NOT NULL default '0', + PRIMARY KEY (id) +) TYPE=MyISAM; + + +*/ + +Trap::Trap() : + Entity(), + respawn_timer(600000), + chkarea_timer(500) +{ + trap_id = 0; + x = 0; + y = 0; + z = 0; + maxzdiff = 0; + radius = 0; + effect = 0; + effectvalue = 0; + effectvalue2 = 0; + skill = 0; + level = 0; + respawn_timer.Disable(); + detected = false; + disarmed = false; + respawn_time = 0; + respawn_var = 0; + hiddenTrigger = NULL; + ownHiddenTrigger = false; +} + +Trap::~Trap() +{ + //don't need to clean up mob as traps are always cleaned up same time as NPCs + //cleaning up mob here can actually cause a crash via race condition +} + +bool Trap::Process() +{ + if (chkarea_timer.Enabled() && chkarea_timer.Check() + /*&& zone->GetClientCount() > 0*/ ) + { + Mob* trigger = entity_list.GetTrapTrigger(this); + if (trigger && !(trigger->IsClient() && trigger->CastToClient()->GetGM())) + { + Trigger(trigger); + } + } + if (respawn_timer.Enabled() && respawn_timer.Check()) + { + detected = false; + disarmed = false; + chkarea_timer.Enable(); + respawn_timer.Disable(); + } + return true; +} + +void Trap::Trigger(Mob* trigger) +{ + int i = 0; + const NPCType* tmp = 0; + switch (effect) + { + case trapTypeDebuff: + if(message.empty()) + { + entity_list.MessageClose(trigger,false,100,13,"%s triggers a trap!",trigger->GetName()); + } + else + { + entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str()); + } + if(hiddenTrigger){ + hiddenTrigger->SpellFinished(effectvalue, trigger, 10, 0, -1, spells[effectvalue].ResistDiff); + } + break; + case trapTypeAlarm: + if (message.empty()) + { + entity_list.MessageClose(trigger,false,effectvalue,13,"A loud alarm rings out through the air..."); + } + else + { + entity_list.MessageClose(trigger,false,effectvalue,13,"%s",message.c_str()); + } + + entity_list.SendAlarm(this,trigger,effectvalue); + break; + case trapTypeMysticSpawn: + if (message.empty()) + { + entity_list.MessageClose(trigger,false,100,13,"The air shimmers..."); + } + else + { + entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str()); + } + + for (i = 0; i < effectvalue2; i++) + { + if ((tmp = database.GetNPCType(effectvalue))) + { + NPC* new_npc = new NPC(tmp, 0, x-5+MakeRandomInt(0, 10), y-5+MakeRandomInt(0, 10), z-5+MakeRandomInt(0, 10), MakeRandomInt(0, 249), FlyMode3); + new_npc->AddLootTable(); + entity_list.AddNPC(new_npc); + new_npc->AddToHateList(trigger,1); + } + } + break; + case trapTypeBanditSpawn: + if (message.empty()) + { + entity_list.MessageClose(trigger,false,100,13,"A bandit leaps out from behind a tree!"); + } + else + { + entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str()); + } + + for (i = 0; i < effectvalue2; i++) + { + if ((tmp = database.GetNPCType(effectvalue))) + { + NPC* new_npc = new NPC(tmp, 0, x-2+MakeRandomInt(0, 5), y-2+MakeRandomInt(0, 5), z-2+MakeRandomInt(0, 5), MakeRandomInt(0, 249), FlyMode3); + new_npc->AddLootTable(); + entity_list.AddNPC(new_npc); + new_npc->AddToHateList(trigger,1); + } + } + break; + case trapTypeDamage: + if (message.empty()) + { + entity_list.MessageClose(trigger,false,100,13,"%s triggers a trap!",trigger->GetName()); + } + else + { + entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str()); + } + if(trigger->IsClient()) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); + CombatDamage_Struct* a = (CombatDamage_Struct*)outapp->pBuffer; + int dmg = MakeRandomInt(effectvalue, effectvalue2); + trigger->SetHP(trigger->GetHP() - dmg); + a->damage = dmg; + a->sequence = MakeRandomInt(0, 1234567); + a->source = GetHiddenTrigger()!=NULL ? GetHiddenTrigger()->GetID() : trigger->GetID(); + a->spellid = 0; + a->target = trigger->GetID(); + a->type = 253; + trigger->CastToClient()->QueuePacket(outapp); + safe_delete(outapp); + } + } + respawn_timer.Start((respawn_time + MakeRandomInt(0, respawn_var)) * 1000); + chkarea_timer.Disable(); + disarmed = true; +} + +Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist) { + LinkedListIterator iterator(trap_list); + iterator.Reset(); + float dist = 999999; + Trap* current_trap = NULL; + + float max_dist2 = max_dist*max_dist; + Trap *cur; + while(iterator.MoreElements()) + { + cur = iterator.GetData(); + if(!cur->disarmed) { + float curdist = 0; + float tmp = searcher->GetX() - cur->x; + curdist += tmp*tmp; + tmp = searcher->GetY() - cur->y; + curdist += tmp*tmp; + tmp = searcher->GetZ() - cur->z; + curdist += tmp*tmp; + + if (curdist < max_dist2 && curdist < dist) + { + dist = curdist; + current_trap = cur; + } + } + iterator.Advance(); + } + return current_trap; +} + +Mob* EntityList::GetTrapTrigger(Trap* trap) { + LinkedListIterator iterator(client_list); + + Mob* savemob = 0; + iterator.Reset(); + + float xdiff, ydiff, zdiff; + + float maxdist = trap->radius * trap->radius; + + while(iterator.MoreElements()) { + Client* cur = iterator.GetData(); + zdiff = cur->GetZ() - trap->z; + if(zdiff < 0) + zdiff = 0 - zdiff; + + xdiff = cur->GetX() - trap->x; + ydiff = cur->GetY() - trap->y; + if ((xdiff*xdiff + ydiff*ydiff) <= maxdist + && zdiff < trap->maxzdiff) + { + if (MakeRandomInt(0,100) < trap->chance) + return(cur); + else + savemob = cur; + } + iterator.Advance(); + } + return savemob; +} + +//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); + return false; + } + + return true; +} + +void Trap::CreateHiddenTrigger() +{ + if(hiddenTrigger) + return; + + const NPCType *base_type = database.GetNPCType(500); + NPCType *make_npc = new NPCType; + memcpy(make_npc, base_type, sizeof(NPCType)); + make_npc->max_hp = 100000; + make_npc->cur_hp = 100000; + strcpy(make_npc->name, "a_trap"); + make_npc->runspeed = 0.0f; + make_npc->bodytype = BT_Special; + make_npc->race = 127; + make_npc->gender = 0; + make_npc->loottable_id = 0; + make_npc->npc_spells_id = 0; + make_npc->d_meele_texture1 = 0; + make_npc->d_meele_texture2 = 0; + make_npc->trackable = 0; + make_npc->level = level; + strcpy(make_npc->npc_attacks, "ABHG"); + NPC* npca = new NPC(make_npc, 0, x, y, z, 0, FlyMode3); + npca->GiveNPCTypeData(make_npc); + entity_list.AddNPC(npca); + + hiddenTrigger = npca; + ownHiddenTrigger = true; +} diff --git a/zone/trap.h b/zone/trap.h new file mode 100644 index 000000000..d8570a633 --- /dev/null +++ b/zone/trap.h @@ -0,0 +1,79 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 _TRAP_H +#define _TRAP_H + +#include "../common/debug.h" + +#include "entity.h" + +//ID of the NPC type to spawn when a trap is set off, to do the damage +#define TRAP_NPC_TYPE 1586 + +enum TrapTypes +{ + trapTypeDebuff = 0, + trapTypeAlarm = 1, + trapTypeMysticSpawn = 2, + trapTypeBanditSpawn = 3, + trapTypeDamage = 4, +}; + +class Trap: public Entity +{ +public: + Trap(); + virtual ~Trap(); + virtual bool Process(); + virtual bool IsTrap() const { return true; } + void Trigger(Mob* trigger); + + void SpellOnTarget(Mob* trigger, uint32 spell_id); + + NPC * GetHiddenTrigger() { return hiddenTrigger; } + void SetHiddenTrigger(NPC* n) { hiddenTrigger = n; } + void CreateHiddenTrigger(); + + //Trap data, leave this unprotected + Timer respawn_timer; //Respawn Time when Trap's been disarmed + Timer chkarea_timer; + uint32 trap_id; //Database ID of trap + float x; //X position + float y; //Y position + float z; //Z position + float maxzdiff; //maximum z diff to be triggerable + float radius; //radius around trap to be triggerable + uint8 chance; //%chance that the trap is triggered each 'tick' + uint8 effect; //Effect ID + int32 effectvalue; //Value of Effect + int32 effectvalue2; //Value of Effect + uint8 skill; //Skill to detect/disarm with rogue. + uint8 level; + bool detected; + bool disarmed; + uint32 respawn_time; + uint32 respawn_var; + + std::string message; +protected: + NPC *hiddenTrigger; + bool ownHiddenTrigger; +}; + +#endif + diff --git a/zone/tribute.cpp b/zone/tribute.cpp new file mode 100644 index 000000000..d0989c6b4 --- /dev/null +++ b/zone/tribute.cpp @@ -0,0 +1,710 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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/eq_packet_structs.h" +#include "features.h" +#include "masterentity.h" +#include "../common/packet_dump.h" +#include "../common/MiscFunctions.h" +#include +#include + +using namespace std; + +#ifdef _WINDOWS +#include +#include +#include + +#define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#else +#include +#include +#include +#include "../common/unix.h" +#endif + +/* + +The server periodicly sends tribute timer updates to the client on live, +but I dont see a point to that right now, so I dont do it. + +*/ + + +class TributeData { +public: + //this level data stored in regular byte order and must be flipped before sending + TributeLevel_Struct tiers[MAX_TRIBUTE_TIERS]; + uint8 tier_count; + uint32 unknown; + string name; + string description; + bool is_guild; //is a guild tribute item +}; + +map tribute_list; + +void Client::ToggleTribute(bool enabled) { + if(enabled) { + //make sure they have enough points to be activating this... + int r; + uint32 cost = 0; + uint32 level = GetLevel(); + for(r = 0; r < MAX_PLAYER_TRIBUTES; r++) { + uint32 tid = m_pp.tributes[r].tribute; + if(tid == TRIBUTE_NONE) + continue; + + if(tribute_list.count(tid) != 1) + continue; + + if(m_pp.tributes[r].tier >= MAX_TRIBUTE_TIERS) { + m_pp.tributes[r].tier = 0; //sanity check. + continue; + } + + TributeData &d = tribute_list[tid]; + + TributeLevel_Struct &tier = d.tiers[m_pp.tributes[r].tier]; + + if(level < tier.level) { + Message(0, "You are not high enough level to activate this tribute!"); + ToggleTribute(false); + continue; + } + + cost += tier.cost; + } + + if(cost > m_pp.tribute_points) { + Message(13, "You do not have enough tribute points to activate your tribute!"); + ToggleTribute(false); + return; + } + AddTributePoints(0-cost); + + //reset their timer, since they just paid for a full duration + m_pp.tribute_time_remaining = Tribute_duration; //full duration + tribute_timer.Start(m_pp.tribute_time_remaining); + + m_pp.tribute_active = 1; + } else { + m_pp.tribute_active = 0; + tribute_timer.Disable(); + } + DoTributeUpdate(); +} + +void Client::DoTributeUpdate() { + EQApplicationPacket outapp(OP_TributeUpdate, sizeof(TributeInfo_Struct)); + TributeInfo_Struct *tis = (TributeInfo_Struct *) outapp.pBuffer; + + tis->active = m_pp.tribute_active ? 1 : 0; + tis->tribute_master_id = tribute_master_id; //Dont know what this is for + + int r; + for(r = 0; r < MAX_PLAYER_TRIBUTES; r++) { + if(m_pp.tributes[r].tribute != TRIBUTE_NONE) { + tis->tributes[r] = m_pp.tributes[r].tribute; + tis->tiers[r] = m_pp.tributes[r].tier; + } else { + tis->tributes[r] = TRIBUTE_NONE; + tis->tiers[r] = 0; + } + } + QueuePacket(&outapp); + + SendTributeTimer(); + + if(m_pp.tribute_active) { + //send and equip tribute items... + for(r = 0; r < MAX_PLAYER_TRIBUTES; r++) { + uint32 tid = m_pp.tributes[r].tribute; + if(tid == TRIBUTE_NONE) { + if(m_inv[TRIBUTE_SLOT_START+r]) + DeleteItemInInventory(TRIBUTE_SLOT_START+r, 0, false); + continue; + } + + if(tribute_list.count(tid) != 1) { + if(m_inv[TRIBUTE_SLOT_START+r]) + DeleteItemInInventory(TRIBUTE_SLOT_START+r, 0, false); + continue; + } + + //sanity check + if(m_pp.tributes[r].tier >= MAX_TRIBUTE_TIERS) { + if(m_inv[TRIBUTE_SLOT_START+r]) + DeleteItemInInventory(TRIBUTE_SLOT_START+r, 0, false); + m_pp.tributes[r].tier = 0; + continue; + } + + TributeData &d = tribute_list[tid]; + TributeLevel_Struct &tier = d.tiers[m_pp.tributes[r].tier]; + uint32 item_id = tier.tribute_item_id; + + //summon the item for them + const ItemInst* inst = database.CreateItem(item_id, 1); + if(inst == NULL) + continue; + + PutItemInInventory(TRIBUTE_SLOT_START+r, *inst, false); + SendItemPacket(TRIBUTE_SLOT_START+r, inst, ItemPacketTributeItem); + safe_delete(inst); + } + } else { + //unequip tribute items... + for(r = 0; r < MAX_PLAYER_TRIBUTES; r++) { + if(m_inv[TRIBUTE_SLOT_START+r]) + DeleteItemInInventory(TRIBUTE_SLOT_START+r, 0, false); + } + } + CalcBonuses(); +} + +void Client::SendTributeTimer() { + //update their timer. + EQApplicationPacket outapp2(OP_TributeTimer, sizeof(uint32)); + uint32 *timeleft = (uint32 *) outapp2.pBuffer; + if(m_pp.tribute_active) + *timeleft = m_pp.tribute_time_remaining; + else + *timeleft = Tribute_duration; //full duration + QueuePacket(&outapp2); +} + +void Client::ChangeTributeSettings(TributeInfo_Struct *t) { + int r; + for(r = 0; r < MAX_PLAYER_TRIBUTES; r++) { + + m_pp.tributes[r].tribute = TRIBUTE_NONE; + + uint32 tid = t->tributes[r]; + if(tid == TRIBUTE_NONE) + continue; + + if(tribute_list.count(tid) != 1) + continue; //print a cheater warning? + + TributeData &d = tribute_list[tid]; + + //make sure they chose a valid tier + if(t->tiers[r] >= d.tier_count) + continue; //print a cheater warning? + + //might want to check required level, even though its checked before activate + + m_pp.tributes[r].tribute = tid; + m_pp.tributes[r].tier = t->tiers[r]; + } + + DoTributeUpdate(); +} + +void Client::SendTributeDetails(uint32 client_id, uint32 tribute_id) { + if(tribute_list.count(tribute_id) != 1) { + LogFile->write(EQEMuLog::Error, "Details request for invalid tribute %lu", (unsigned long)tribute_id); + return; + } + TributeData &td = tribute_list[tribute_id]; + + int len = td.description.length(); + EQApplicationPacket outapp(OP_SelectTribute, sizeof(SelectTributeReply_Struct)+len+1); + SelectTributeReply_Struct *t = (SelectTributeReply_Struct *) outapp.pBuffer; + + t->client_id = client_id; + t->tribute_id = tribute_id; + memcpy(t->desc, td.description.c_str(), len); + t->desc[len] = '\0'; + + QueuePacket(&outapp); +} + +//returns the number of points received from the tribute +int32 Client::TributeItem(uint32 slot, uint32 quantity) { + const ItemInst*inst = m_inv[slot]; + + if(inst == NULL) + return(0); + + //figure out what its worth + int32 pts = inst->GetItem()->Favor; + if(pts < 1) { + Message(13, "This item is worthless for favor."); + return(0); + } + + //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); + DeleteItemInInventory(slot, quantity, false); + } else { + quantity = 1; + DeleteItemInInventory(slot, 0, false); + } + + pts *= quantity; + + //add the tribute value in points + AddTributePoints(pts); + return(pts); +} + +//returns the number of points received from the tribute +int32 Client::TributeMoney(uint32 platinum) { + if(!TakeMoneyFromPP(platinum * 1000)) { + Message(13, "You do not have that much money!"); + return(0); + } + + //add the tribute value in points + AddTributePoints(platinum); + return(platinum); +} + +void Client::AddTributePoints(int32 ammount) { + EQApplicationPacket outapp(OP_TributePointUpdate, sizeof(TributePoint_Struct)); + TributePoint_Struct *t = (TributePoint_Struct *) outapp.pBuffer; + + //change the point values. + m_pp.tribute_points += ammount; + + //career only tracks points earned, not spent. + if(ammount > 0) + m_pp.career_tribute_points += ammount; + + //fill in the packet. + t->career_tribute_points = m_pp.career_tribute_points; + t->tribute_points = m_pp.tribute_points; + + QueuePacket(&outapp); +} + +void Client::SendTributes() { + + map::iterator cur,end; + cur = tribute_list.begin(); + end = tribute_list.end(); + + for(; cur != end; cur++) { + if(cur->second.is_guild) + continue; //skip guild tributes here + int len = cur->second.name.length(); + EQApplicationPacket outapp(OP_TributeInfo, sizeof(TributeAbility_Struct) + len + 1); + TributeAbility_Struct* tas = (TributeAbility_Struct*)outapp.pBuffer; + + tas->tribute_id = htonl(cur->first); + tas->tier_count = htonl(cur->second.unknown); + + //gotta copy over the data from tiers, and flip all the + //byte orders, no idea why its flipped here + uint32 r, c; + c = cur->second.tier_count; + TributeLevel_Struct *dest = tas->tiers; + TributeLevel_Struct *src = cur->second.tiers; + for(r = 0; r < c; r++, dest++, src++) { + dest->cost = htonl(src->cost); + dest->level = htonl(src->level); + dest->tribute_item_id = htonl(src->tribute_item_id); + } + + memcpy(tas->name, cur->second.name.c_str(), len); + tas->name[len] = '\0'; + QueuePacket(&outapp); + } +} + +void Client::SendGuildTributes() { + + map::iterator cur,end; + cur = tribute_list.begin(); + end = tribute_list.end(); + + for(; cur != end; cur++) { + if(!cur->second.is_guild) + continue; //skip guild tributes here + int len = cur->second.name.length(); + + //guild tribute has an unknown uint32 at its begining, guild ID? + EQApplicationPacket outapp(OP_TributeInfo, sizeof(TributeAbility_Struct) + len + 1 + 4); + uint32 *unknown = (uint32 *) outapp.pBuffer; + TributeAbility_Struct* tas = (TributeAbility_Struct*) (outapp.pBuffer+4); + + //this is prolly wrong in general, prolly for one specific guild + *unknown = 0x8A110000; + + tas->tribute_id = htonl(cur->first); + tas->tier_count = htonl(cur->second.unknown); + + //gotta copy over the data from tiers, and flip all the + //byte orders, no idea why its flipped here + uint32 r, c; + c = cur->second.tier_count; + TributeLevel_Struct *dest = tas->tiers; + TributeLevel_Struct *src = cur->second.tiers; + for(r = 0; r < c; r++, dest++, src++) { + dest->cost = htonl(src->cost); + dest->level = htonl(src->level); + dest->tribute_item_id = htonl(src->tribute_item_id); + } + + memcpy(tas->name, cur->second.name.c_str(), len); + tas->name[len] = '\0'; + + QueuePacket(&outapp); + } +} + +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; + + 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++], NULL, 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); + return false; + } + + + 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++]); + + 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); + return false; + } + + return true; +} + + +/* + +64.37.149.6:1353 == server +66.90.221.245:3173 == client + +84 01 00 00 == NPC ID of tribute master +08 0B 00 00 == Client ID + + [64.37.149.6:1353 -> +2004/08/19 04:09:07 GMT: 66.90.221.245:3173] +2004/08/19 04:09:07 GMT: Child Packet: + [OPCode: OP_Tribute] [Raw OPCode: OP_Tribute] [Size: 144] + 0: 00 00 00 00 00 00 00 05 - 00 00 00 14 00 00 DB EC | ................ + 16: 00 00 00 05 00 00 00 1E - 00 00 DB ED 00 00 00 0C | ................ + 32: 00 00 00 28 00 00 DB EE - 00 00 00 14 00 00 00 32 | ...(...........2 + 48: 00 00 DB EF 00 00 00 1D - 00 00 00 3C 00 00 DB F0 | ...........<.... + 64: 00 00 00 25 00 00 00 00 - 00 00 00 00 00 00 00 00 | ...%............ + 80: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 96: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 112: 00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00 | ................ + 128: 41 75 72 61 20 6F 66 20 - 43 6C 61 72 69 74 79 00 | Aura of Clarity. + +... many of those ... + + +2004/08/19 04:09:20 GMT: OPCode OP_StartTribute [Raw OPCode: OP_StartTribute] [Size: 12] + [66.90.221.245:3173 -> +2004/08/19 04:09:20 GMT: 64.37.149.6:1353] + 0: 08 0B 00 00 84 01 00 00 - A9 E1 94 42 | ...........B + + +2004/08/19 04:09:20 GMT: OPCode 0x02f2 [Raw OPCode: 0x72f2] [Size: 0] [Compressed Size: 65] + [64.37.149.6:1353 -> +2004/08/19 04:09:20 GMT: 66.90.221.245:3173] + [Packet is compressed] + [Packet is encrypted] + [Packet is packed] +2004/08/19 04:09:20 GMT: Child Packet: + [OPCode: 0x02f2] [Raw OPCode: 0x02f2] [Size: 48] + 0: 00 00 00 00 FF FF FF FF - FF FF FF FF FF FF FF FF | ................ + 16: FF FF FF FF FF FF FF FF - 00 00 00 00 00 00 00 00 | ................ + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 FF FF FF FF | ................ + +2004/08/19 04:09:20 GMT: Child Packet: + [OPCode: 0x02f8] [Raw OPCode: 0x02f8] [Size: 4] + 0: C0 27 09 00 | .'.. + +2004/08/19 04:09:20 GMT: Child Packet: + [OPCode: OP_StartTribute] [Raw OPCode: OP_StartTribute] [Size: 12] + 0: 08 0B 00 00 84 01 00 00 - 01 00 00 00 | ............ + + + +2004/08/19 04:09:23 GMT: OPCode OP_SelectTribute [Raw OPCode: OP_SelectTribute] [Size: 12] + [66.90.221.245:3173 -> +2004/08/19 04:09:23 GMT: 64.37.149.6:1353] + 0: 01 00 00 00 04 00 00 00 - E3 00 00 00 | ............ + +2004/08/19 04:09:23 GMT: Child Packet: + [OPCode: OP_SelectTribute] [Raw OPCode: OP_SelectTribute] [Size: 100] + 0: 01 00 00 00 04 00 00 00 - 4F 75 72 20 67 72 65 61 | ........Our grea + 16: 74 65 73 74 20 77 61 72 - 72 69 6F 72 73 20 66 6F | test warriors fo + 32: 63 75 73 20 74 6F 20 69 - 6E 63 72 65 61 73 65 20 | cus to increase + 48: 79 6F 75 72 20 73 74 72 - 65 6E 67 74 68 2E 3C 62 | your strength.Benefit -
2 + 80: 20 53 74 72 65 6E 67 74 - 68 20 70 65 72 20 74 69 | Strength per ti + 96: 65 72 2E 00 | er.. + +2004/08/19 04:09:28 GMT: OPCode OP_SelectTribute [Raw OPCode: OP_SelectTribute] [Size: 12] + [66.90.221.245:3173 -> +2004/08/19 04:09:28 GMT: 64.37.149.6:1353] + 0: 01 00 00 00 17 00 00 00 - E3 00 00 00 | ............ + +2004/08/19 04:09:29 GMT: OPCode OP_SelectTribute [Raw OPCode: 0x72f7] [Size: 0] [Compressed Size: 205] + [64.37.149.6:1353 -> +2004/08/19 04:09:29 GMT: 66.90.221.245:3173] + [Packet is compressed] + [Packet is encrypted] + [Packet is packed] +2004/08/19 04:09:29 GMT: Child Packet: + [OPCode: OP_SelectTribute] [Raw OPCode: OP_SelectTribute] [Size: 232] + 0: 01 00 00 00 17 00 00 00 - 54 68 65 20 63 68 61 72 | ........The char + 16: 69 74 61 62 6C 65 20 74 - 61 6C 65 73 20 6F 66 20 | itable tales of + 32: 61 20 74 61 6C 65 6E 74 - 65 64 20 70 6F 65 74 20 | a talented poet + 48: 6C 65 6E 64 20 65 66 66 - 69 63 69 65 6E 63 79 20 | lend efficiency + 64: 74 6F 20 79 6F 75 72 20 - 62 65 6E 65 66 69 63 69 | to your benefici + 80: 61 6C 20 73 70 65 6C 6C - 73 2E 3C 62 72 3E 42 65 | al spells.
Be + 96: 6E 65 66 69 74 20 2D 3C - 62 72 3E 54 69 65 72 20 | nefit -
Tier + 112: 31 3A 20 45 6E 68 61 6E - 63 65 6D 65 6E 74 20 48 | 1: Enhancement H + 128: 61 73 74 65 20 49 3C 62 - 72 3E 54 69 65 72 20 32 | aste I
Tier 2 + 144: 3A 20 45 6E 68 61 6E 63 - 65 6D 65 6E 74 20 48 61 | : Enhancement Ha + 160: 73 74 65 20 49 49 3C 62 - 72 3E 54 69 65 72 20 33 | ste II
Tier 3 + 176: 3A 20 45 6E 68 61 6E 63 - 65 6D 65 6E 74 20 48 61 | : Enhancement Ha + 192: 73 74 65 20 49 49 49 3C - 62 72 3E 54 69 65 72 20 | ste III
Tier + 208: 34 3A 20 45 6E 68 61 6E - 63 65 6D 65 6E 74 20 48 | 4: Enhancement H + 224: 61 73 74 65 20 49 56 00 | aste IV. + + + + +Donating shoulderpads: + + +2004/08/19 04:09:50 GMT: OPCode 0x02f3 [Raw OPCode: 0x02f3] [Size: 16] + [66.90.221.245:3173 -> +2004/08/19 04:09:50 GMT: 64.37.149.6:1353] + 0: 1D 00 00 00 01 00 00 00 - 84 01 00 00 60 A9 D9 32 | ............`..2 + + [64.37.149.6:1353 -> +2004/08/19 04:09:50 GMT: 66.90.221.245:3173] +2004/08/19 04:09:50 GMT: Child Packet: + [OPCode: 0x02f3] [Raw OPCode: 0x02f3] [Size: 16] + 0: 1D 00 00 00 01 00 00 00 - 84 01 00 00 02 00 00 00 | ................ +2004/08/19 04:09:50 GMT: Child Packet: + [OPCode: 0x02f4] [Raw OPCode: 0x02f4] [Size: 16] + 0: 02 00 00 00 00 00 00 00 - 02 00 00 00 00 00 00 00 | ................ + + + + + +Donating Platinum: + +2004/08/19 04:10:34 GMT: OPCode 0x02fe [Raw OPCode: 0x02fe] [Size: 12] + [66.90.221.245:3173 -> +2004/08/19 04:10:34 GMT: 64.37.149.6:1353] + 0: 12 00 00 00 84 01 00 00 - 5F A6 E7 77 | ........_..w + + [64.37.149.6:1353 -> +2004/08/19 04:10:34 GMT: 66.90.221.245:3173] +2004/08/19 04:10:34 GMT: Child Packet: + [OPCode: 0x02fe] [Raw OPCode: 0x02fe] [Size: 12] + 0: 12 00 00 00 84 01 00 00 - 12 00 00 00 | ............ +2004/08/19 04:10:34 GMT: Child Packet: + [OPCode: 0x02f4] [Raw OPCode: 0x02f4] [Size: 16] + 0: 14 00 00 00 00 00 00 00 - 14 00 00 00 00 00 00 00 | ................ + + + + +Upgrading: + +2004/08/19 04:10:37 GMT: OPCode OP_SelectTribute [Raw OPCode: OP_SelectTribute] [Size: 12] + [66.90.221.245:3173 -> +2004/08/19 04:10:37 GMT: 64.37.149.6:1353] + 0: 01 00 00 00 28 00 00 00 - E3 00 00 00 | ....(....... + +server -> client +2004/08/19 04:10:38 GMT: Child Packet: + [OPCode: OP_SelectTribute] [Raw OPCode: OP_SelectTribute] [Size: 128] + 0: 01 00 00 00 28 00 00 00 - 4F 75 72 20 68 65 61 6C | ....(...Our heal + 16: 65 72 73 20 67 69 76 65 - 20 79 6F 75 20 61 6E 20 | ers give you an + 32: 61 6E 74 69 62 6F 64 79 - 2C 20 77 68 69 63 68 20 | antibody, which + 48: 68 65 6C 70 73 20 70 72 - 6F 74 65 63 74 20 79 6F | helps protect yo + 64: 75 20 66 72 6F 6D 20 64 - 69 73 65 61 73 65 73 2E | u from diseases. + 80: 3C 62 72 3E 42 65 6E 65 - 66 69 74 20 2D 3C 62 72 |
Benefit -
5 Disease Resis + 112: 74 61 6E 63 65 20 70 65 - 72 20 74 69 65 72 2E 00 | tance per tier.. + +request tribute +2004/08/19 04:10:49 GMT: OPCode 0x02f2 [Raw OPCode: 0x02f2] [Size: 48] + [66.90.221.245:3173 -> +2004/08/19 04:10:49 GMT: 64.37.149.6:1353] + 0: A1 F9 41 00 28 00 00 00 - FF FF FF FF FF FF FF FF | ..A.(........... + 16: FF FF FF FF FF FF FF FF - 00 00 00 00 00 00 00 00 | ................ + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 84 01 00 00 | ................ + +2004/08/19 04:10:49 GMT: OPCode 0x02fa [Raw OPCode: 0x02fa] [Size: 12] + [66.90.221.245:3173 -> +2004/08/19 04:10:49 GMT: 64.37.149.6:1353] + 0: 08 0B 00 00 84 01 00 00 - F4 DE 12 00 | ............ + +ack tribute + [64.37.149.6:1353 -> +2004/08/19 04:10:50 GMT: 66.90.221.245:3173] +2004/08/19 04:10:50 GMT: Child Packet: + [OPCode: 0x02f2] [Raw OPCode: 0x02f2] [Size: 48] + 0: 00 00 00 00 28 00 00 00 - FF FF FF FF FF FF FF FF | ....(........... + 16: FF FF FF FF FF FF FF FF - 00 00 00 00 00 00 00 00 | ................ + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 FF FF FF FF | ................ + + + +Activate Tribute: + +2004/08/19 04:11:05 GMT: OPCode 0x0365 [Raw OPCode: 0x0365] [Size: 4] + [CLIENT:3173 -> +2004/08/19 04:11:05 GMT: SERVER:1353] + 0: 01 00 00 00 | .... + + + [64.37.149.6:1353 -> +2004/08/19 04:11:05 GMT: 66.90.221.245:3173] +2004/08/19 04:11:05 GMT: Child Packet: + [OPCode: 0x02f8] [Raw OPCode: 0x02f8] [Size: 4] + 0: C0 27 09 00 | .'.. + +2004/08/19 04:11:05 GMT: Child Packet: + [OPCode: 0x02f4] [Raw OPCode: 0x02f4] [Size: 16] + 0: 0D 00 00 00 00 00 00 00 - 14 00 00 00 00 00 00 00 | ................ + +2004/08/19 04:11:05 GMT: Child Packet: + [OPCode: 0x02f2] [Raw OPCode: 0x02f2] [Size: 48] + 0: 01 00 00 00 28 00 00 00 - FF FF FF FF FF FF FF FF | ....(........... + 16: FF FF FF FF FF FF FF FF - 00 00 00 00 00 00 00 00 | ................ + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 FF FF FF FF | ................ + +2004/08/19 04:11:05 GMT: Child Packet: + [OPCode: OP_ItemPacket] [Raw OPCode: OP_ItemPacket] [Size: 350] + 0: 6C 00 00 00 31 7C 30 7C - 34 30 30 7C 30 7C 30 7C | l...1|0|400|0|0| + 16: 31 30 39 32 36 36 32 7C - 30 7C 2D 31 7C 22 30 7C | 1092662|0|-1|"0| + 32: 52 65 73 69 73 74 20 44 - 69 73 65 61 73 65 20 35 | Resist Disease 5 + 48: 20 42 65 6E 65 66 69 74 - 7C 52 65 73 69 73 74 20 | Benefit|Resist + 64: 44 69 73 65 61 73 65 20 - 35 20 42 65 6E 65 66 69 | Disease 5 Benefi + 80: 74 7C 30 7C 35 36 34 36 - 35 7C 30 7C 32 35 35 7C | t|0|56465|0|255| + 96: 32 35 35 7C 30 7C 30 7C - 31 7C 39 34 37 7C 2D 31 | 255|0|0|1|947|-1 + 112: 7C 30 7C 31 7C 30 7C 30 - 7C 35 7C 30 7C 30 7C 30 | |0|1|0|0|5|0|0|0 + 128: 7C 30 7C 30 7C 30 7C 30 - 7C 30 7C 30 7C 30 7C 30 | |0|0|0|0|0|0|0|0 + 144: 7C 30 7C 30 7C 30 7C 30 - 7C 30 7C 30 7C 30 7C 30 | |0|0|0|0|0|0|0|0 + 160: 7C 30 7C 30 7C 30 7C 30 - 7C 30 7C 30 7C 30 7C 30 | |0|0|0|0|0|0|0|0 + 176: 7C 30 7C 30 7C 30 7C 30 - 7C 30 7C 30 7C 30 7C 30 | |0|0|0|0|0|0|0|0 + 192: 7C 30 7C 30 7C 30 7C 2D - 31 7C 2D 31 7C 30 7C 30 | |0|0|0|-1|-1|0|0 + 208: 7C 31 2E 30 30 30 30 30 - 30 7C 30 7C 30 7C 30 7C | |1.000000|0|0|0| + 224: 30 7C 2D 31 7C 30 7C 30 - 7C 30 7C 30 7C 30 7C 30 | 0|-1|0|0|0|0|0|0 + 240: 7C 30 7C 30 7C 30 7C 30 - 7C 30 7C 30 7C 30 7C 30 | |0|0|0|0|0|0|0|0 + 256: 7C 30 7C 30 7C 30 7C 30 - 7C 7C 30 7C 30 7C 30 7C | |0|0|0|0||0|0|0| + 272: 30 7C 30 7C 30 7C 30 7C - 30 7C 30 7C 30 7C 30 7C | 0|0|0|0|0|0|0|0| + 288: 30 7C 30 7C 30 7C 30 7C - 7C 30 7C 30 7C 31 7C 30 | 0|0|0|0||0|0|1|0 + 304: 7C 30 7C 30 7C 30 7C 30 - 7C 30 7C 30 7C 30 7C 30 | |0|0|0|0|0|0|0|0 + 320: 7C 30 7C 30 7C 30 7C 30 - 7C 2D 31 7C 30 7C 30 7C | |0|0|0|0|-1|0|0| + 336: 2D 31 22 7C 7C 7C 7C 7C - 7C 7C 7C 7C 7C 00 | -1"||||||||||. + + + +Deactivate Tribute: + + +2004/08/19 04:11:08 GMT: OPCode 0x0365 [Raw OPCode: 0x0365] [Size: 4] + [CLIENT:3173 -> +2004/08/19 04:11:08 GMT: SERVER:1353] + 0: 00 00 00 00 | .... + + [64.37.149.6:1353 -> +2004/08/19 04:11:09 GMT: 66.90.221.245:3173] + [Packet is compressed] + [Packet is encrypted] + [Packet is packed] +2004/08/19 04:11:09 GMT: Child Packet: + [OPCode: 0x02f2] [Raw OPCode: 0x02f2] [Size: 48] + 0: 00 00 00 00 28 00 00 00 - FF FF FF FF FF FF FF FF | ....(........... + 16: FF FF FF FF FF FF FF FF - 00 00 00 00 00 00 00 00 | ................ + 32: 00 00 00 00 00 00 00 00 - 00 00 00 00 FF FF FF FF | ................ + +2004/08/19 04:11:09 GMT: Child Packet: + [OPCode: 0x02f8] [Raw OPCode: 0x02f8] [Size: 4] + 0: C0 27 09 00 | .'.. + +*/ + + + + diff --git a/zone/updatemgr.cpp b/zone/updatemgr.cpp new file mode 100644 index 000000000..16b9e3ca3 --- /dev/null +++ b/zone/updatemgr.cpp @@ -0,0 +1,194 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" + +#ifdef PACKET_UPDATE_MANAGER +#include "updatemgr.h" +#include "mob.h" +#include "../common/EQStream.h" + +//squared distances for each level +//these values are pulled out of my ass, should be tuned some day +const float UpdateManager::level_distances2[UPDATE_LEVELS] + = { 50*50, 250*250, 500*500, 800*800 }; + +//delay between sending packets in each level, in ms +//its best if they are all multiples of UPDATE_RESOLUTION +//these values are pulled out of my ass, should be tuned some day +const uint32 UpdateManager::level_timers[UPDATE_LEVELS+1] + = { UPDATE_RESOLUTION, //.3s + 2*UPDATE_RESOLUTION, //.6s + 3*UPDATE_RESOLUTION, //.9s + 9*UPDATE_RESOLUTION, //~2s + 34*UPDATE_RESOLUTION //~10s + }; + + +/* + This system assumes that two packets sent by a mob with the same + opcodes contain the same info at different times, and will prefer + to send only the most recent packet. If this is bad, then this + thing needs a redesign. + +*/ + +//build a unique ID based on opcode and mob id.. +#define MakeUpdateID(mob, app) (((mob->GetID())<<12) | (app->GetOpcode()&0xFFF)) + +UpdateManager::UpdateManager(EQStream *c) + : limiter(UPDATE_RESOLUTION) +{ + net = c; + int r; + for(r = 0; r <= UPDATE_LEVELS; r++) { + timers[r] = new Timer(level_timers[r]); + } +} + +UpdateManager::~UpdateManager() { + int r; + UMMap::iterator cur,end; + for(r = 0; r <= UPDATE_LEVELS; r++) { + safe_delete(timers[r]); + cur = levels[r].begin(); + end = levels[r].end(); + for(; cur != end; cur++) { + EQApplicationPacket *tmp = cur->second.app; + EQApplicationPacket::PacketUsed(&tmp); + } + levels[r].clear(); + } +} + +/* + Puts a packet into its proper spacial queue +*/ +void UpdateManager::QueuePacket(EQApplicationPacket *app, bool ack_req, Mob *from, float range2) { + int r = UPDATE_LEVELS; + UMMap *cur = levels; + const float *cur_d = level_distances2; + cur += UPDATE_LEVELS; //move to the end. + cur_d += UPDATE_LEVELS - 1; + //work backwards since mobs are more likely to be further away + for(r = UPDATE_LEVELS; r >= 0; r--, cur--, cur_d--) { + if(range2 < *cur_d) + continue; + //this packet falls into this queue... + uint32 id = MakeUpdateID(from, app); +// if(r < 2) +// net->QueuePacket(app, ack_req); +//LogFile->write(EQEMuLog::Debug, "Queueing packet from %s (0x%.4x) id=0x%x at level %d\n", from->GetName(), app->GetOpcode(), id, r); + app->PacketReferenced(); + //reference decrementing is taken care of my UMType destructor + //if anything is overwritten + (*cur)[id] = UMType(app, ack_req); +// (*cur)[id] = UMType(app->Copy(), ack_req); + return; + } + //if we get here, were in trouble... +} + +void UpdateManager::Process() { + if(!limiter.Check()) + return; + Timer **curt = timers; + int r; + for(r = 0; r <= UPDATE_LEVELS; r++, curt++) { + if(!(*curt)->Check()) + continue; + _SendLevel(r); + } +} + +void UpdateManager::FlushQueues() { + limiter.Start(); + Timer **curt = timers; + int r; + for(r = 0; r <= UPDATE_LEVELS; r++, curt++) { + (*curt)->Start(); + _SendLevel(r); + } +} + +void UpdateManager::_SendLevel(int level) { +/*LogFile->write(EQEMuLog::Error, "Sending for level %d", level); +if(level > 0) +{ + int r; + for(r = 0; r <= UPDATE_LEVELS; r++) { + LogFile->write(EQEMuLog::Error, "Level %d: %d", r, levels[r].size()); + } + { + float range2 = 5; + int r = UPDATE_LEVELS; + UMMap *cur = levels; + const float *cur_d = level_distances2; + cur += UPDATE_LEVELS; //move to the end. + cur_d += UPDATE_LEVELS - 1; + //work backwards since mobs are more likely to be further away + for(r = UPDATE_LEVELS; r >= 0; r--, cur--, cur_d--) { + LogFile->write(EQEMuLog::Error, "If they are less than %f away, they dont go in level %d", *cur_d, r); + if(range2 < *cur_d) + continue; + LogFile->write(EQEMuLog::Error, "A mob %f away gets put into level %d", range2, r); + } + } +}*/ + + UMMap::iterator cur,end; + UMMap *curm; + UMMap *om = levels + level; + cur = om->begin(); + end = om->end(); + uint32 key; + int r; + + while(cur != end) { + key = cur->first; + //relies on fast queue setting .app to null if it eats it +//LogFile->write(EQEMuLog::Debug, "Sending id 0x%x for level %d\n", key, level); + net->FastQueuePacket(&cur->second.app, cur->second.ack); +//EQApplicationPacket::PacketUsed(&cur->second.app); + cur++; + om->erase(key); + + //need to clear our any updates in slower levels + //so mobs dont jump backwards from old updates + curm = om + 1; + for(r = level+1; r <= UPDATE_LEVELS; r++, curm++) { + //do we need this count check? + if(curm->count(key) != 0) { + //reference decrementing is taken care of my UMType destructor + EQApplicationPacket *tmp = (*curm)[key].app; + curm->erase(key); + EQApplicationPacket::PacketUsed(&tmp); + } + } + } +} + + +#endif //PACKET_UPDATE_MANAGER + + + + + + + diff --git a/zone/updatemgr.h b/zone/updatemgr.h new file mode 100644 index 000000000..4c2733e70 --- /dev/null +++ b/zone/updatemgr.h @@ -0,0 +1,90 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 UPDATE_MANAGER_H +#define UPDATE_MANAGER_H + +#include "features.h" +#ifdef PACKET_UPDATE_MANAGER + +#include "../common/timer.h" +#include +using namespace std; + +/*typedef enum { + updateMobPOS = 1, + updateMobHP +} updateType;*/ + +//the number of different queue levels to use +#define UPDATE_LEVELS 4 +#define UPDATE_RESOLUTION 300 +#define UPDATE_DROP_RANGE 800 + +//if the player moves more than this ammount, all queues are flushed. +#define UPDATE_JUMP_FLUSH 200 // + +class EQStream; +class EQApplicationPacket; +class Mob; + +class UMType { +public: + UMType() { + app = NULL; ack = false; + } + UMType(EQApplicationPacket *_app, bool _ack) { + app = _app; ack = _ack; + } + + EQApplicationPacket *app; + bool ack; +}; + +typedef map UMMap; + +class UpdateManager { +protected: + //squared distances for each level + static const float level_distances2[UPDATE_LEVELS]; + + //delay between sending packets in each level, in ms + static const uint32 level_timers[UPDATE_LEVELS+1]; + +public: + UpdateManager(EQStream *c); + ~UpdateManager(); + + //range2 is the range of 'from' to this client, squared + void QueuePacket(EQApplicationPacket *app, bool ack_req, Mob *from, float range2); + void Process(); + void FlushQueues(); + +protected: + void _SendLevel(int level); + + EQStream *net; + + UMMap levels[UPDATE_LEVELS+1]; + Timer *timers[UPDATE_LEVELS+1]; + Timer limiter; +}; + +#endif //PACKET_UPDATE_MANAGER + +#endif + diff --git a/zone/watermap.cpp b/zone/watermap.cpp new file mode 100644 index 000000000..3cf36a283 --- /dev/null +++ b/zone/watermap.cpp @@ -0,0 +1,195 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2008 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 "../common/debug.h" +#include +#include +#include +#include + +#include "watermap.h" +#include "../common/MiscFunctions.h" +#ifdef _WINDOWS +#define snprintf _snprintf +#endif + + + + +WaterMap::WaterMap() +: BSP_Root(NULL) +{ +} + +WaterMap::~WaterMap() { + safe_delete_array(BSP_Root); +} + + +WaterRegionType WaterMap::BSPReturnRegionType(int32 node_number, float y, float x, float z) const +{ + float distance; + + const ZBSP_Node *current_node = &BSP_Root[node_number-1]; + + // Are we at a leaf + + if((current_node->left==0)&& + (current_node->right==0)) { + return (WaterRegionType) current_node->special; + } + + + // No, so determine which side of the split plane we are on + // + + distance = (x * current_node->normal[0]) + + (y * current_node->normal[1]) + + (z * current_node->normal[2]) + + current_node->splitdistance; + + // If we are exactly on the split plane, I don't know what should happen. + // + if(distance == 0.0f) { + return(RegionTypeNormal); + } + if(distance >0.0f) { + if(current_node->left==0) { + // This shouldn't happen + return(RegionTypeNormal); + } + return BSPReturnRegionType( current_node->left, + y, x, z); + } + if(current_node->right==0) { + // This should't happen + return(RegionTypeNormal); + } + + return BSPReturnRegionType(current_node->right, + y, x, z); +} + +bool WaterMap::InWater(float y, float x, float z) const { + if(BSP_Root == NULL) { + return false; + } + return(BSPReturnRegionType(1, y, x, z) == RegionTypeWater); +} + +bool WaterMap::InVWater(float y, float x, float z) const { + if(BSP_Root == NULL) { + return false; + } + return(BSPReturnRegionType(1, y, x, z) == RegionTypeVWater); +} + +bool WaterMap::InLava(float y, float x, float z) const { + if(BSP_Root == NULL) { + return false; + } + return(BSPReturnRegionType(1, y, x, z) == RegionTypeLava); +} + +bool WaterMap::InLiquid(float y, float x, float z) const { + if(BSP_Root == NULL) //if the water map isn't loaded, this will save ~1 CPU cycle + return false; + return (InWater(y, x, z) || InLava(y, x, z)); +} + +WaterMap* WaterMap::LoadWaterMapfile(const char* in_zonename, const char *directory) { + FILE *fp; + char zBuf[64]; + char cWork[256]; + WaterMap* ret = NULL; + + + //have to convert to lower because the short names im getting + //are not all lower anymore, copy since strlwr edits the str. + strn0cpy(zBuf, in_zonename, 64); + + if(directory == NULL) + directory = MAP_DIR; + snprintf(cWork, 250, "%s/%s.wtr", directory, strlwr(zBuf)); + + if ((fp = fopen( cWork, "rb" ))) { + ret = new WaterMap(); + if(ret != NULL) { + if(ret->loadWaterMap(fp)) { + printf("Water Map %s loaded.\n", cWork); + } else { + safe_delete(ret); + printf("Water Map %s exists but could not be processed.\n", cWork); + return NULL; + } + } else { + printf("Water Map %s loading failed.\n", cWork); + } + fclose(fp); + } + else { + printf("Water Map %s not found.\n", cWork); + } + return ret; +} + +bool WaterMap::loadWaterMap(FILE *fp) { + char EQWMagic[10]; + uint32 BSPTreeSize; + uint32 EQWVersion; + + if(fread(EQWMagic, 10, 1, fp)!=1) { + printf("Error reading Water region map.\n"); + return(false); + } + if(strncmp(EQWMagic,"EQEMUWATER",10)) { + printf("Bad header in Water region map.\n"); + return(false); + } + if(fread(&EQWVersion, sizeof(EQWVersion), 1, fp)!=1) { + printf("Error reading Water region map.\n"); + return(false); + } + if(EQWVersion!=WATERMAP_VERSION) { + printf("Incompatible Water region map version.\n"); + return(false); + } + + if(fread(&BSPTreeSize, sizeof(BSPTreeSize), 1, fp)!=1) { + printf("Error reading Water region map.\n"); + return(false); + } + + BSP_Root = (ZBSP_Node *) new ZBSP_Node[BSPTreeSize]; + + if(BSP_Root==NULL) { + printf("Memory allocation failed while reading water map.\n"); + return(false); + } + + if(fread(BSP_Root, sizeof(ZBSP_Node), BSPTreeSize, fp) != BSPTreeSize) { + printf("Error reading Water region map.\n"); + safe_delete_array(BSP_Root); + return(false); + } + + printf("Water region map has %d nodes.\n", BSPTreeSize); + return(true); +} + + diff --git a/zone/watermap.h b/zone/watermap.h new file mode 100644 index 000000000..1820bf925 --- /dev/null +++ b/zone/watermap.h @@ -0,0 +1,68 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2008 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 WATERMAP_H +#define WATERMAP_H + +#define WATERMAP_VERSION 1 + +#pragma pack(1) +typedef struct ZBSP_Node { + int32 node_number; + float normal[3], splitdistance; + int32 region; + int32 special; + int32 left, right; +} ZBSP_Node; +#pragma pack() + +typedef enum { + RegionTypeUnsupported = -2, + RegionTypeUntagged = -1, + RegionTypeNormal = 0, + RegionTypeWater = 1, + RegionTypeLava = 2, + RegionTypeZoneLine = 3, + RegionTypePVP = 4, + RegionTypeSlime = 5, + RegionTypeIce = 6, + RegionTypeVWater =7 +} WaterRegionType; + +class WaterMap { + +public: + static WaterMap* LoadWaterMapfile(const char* in_zonename, const char *directory = NULL); + WaterRegionType BSPReturnRegionType(int32 node_number, float y, float x, float z) const; + bool InWater(float y, float x, float z) const; + bool InVWater(float y, float x, float z) const; + bool InLava(float y, float x, float z) const; + bool InLiquid(float y, float x, float z) const; + + WaterMap(); + ~WaterMap(); + +private: + bool loadWaterMap(FILE *fp); + + ZBSP_Node* BSP_Root; +}; + +#endif + + + diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp new file mode 100644 index 000000000..7855e4b35 --- /dev/null +++ b/zone/waypoints.cpp @@ -0,0 +1,1408 @@ +/* EQEMu: Everquest Server Emulator +Copyright (C) 2001-2004 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 "../common/debug.h" +#ifdef _EQDEBUG +#include +using namespace std; +#endif +//#include +#include +#include +#include "npc.h" +#include "masterentity.h" +#include "NpcAI.h" +#include "map.h" +#include "watermap.h" +#include "../common/moremath.h" +#include "parser.h" +#include "StringIDs.h" +#include "../common/MiscFunctions.h" +#include "../common/rulesys.h" +#include "features.h" +#include "QuestParserCollection.h" + +struct wp_distance +{ + float dist; + int index; +}; + +static inline float ABS(float x) { + if(x < 0) + return(-x); + return(x); +} + +void NPC::AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay) { + AI_SetRoambox(iDist, GetX()+iRoamDist, GetX()-iRoamDist, GetY()+iRoamDist, GetY()-iRoamDist, iDelay); +} + +void NPC::AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay) { + roambox_distance = iDist; + roambox_max_x = iMaxX; + roambox_min_x = iMinX; + roambox_max_y = iMaxY; + roambox_min_y = iMinY; + roambox_movingto_x = roambox_max_x + 1; // this will trigger a recalc + roambox_delay = iDelay; +} + +void NPC::DisplayWaypointInfo(Client *c) { + + c->Message(0, "Mob is on grid %d, in spawn group %d, on waypoint %d/%d", + GetGrid(), + GetSp2(), + GetCurWp(), + GetMaxWp() ); + + + vector::iterator cur, end; + cur = Waypoints.begin(); + end = Waypoints.end(); + for(; cur != end; cur++) { + c->Message(0,"Waypoint %d: (%.2f,%.2f,%.2f,%.2f) pause %d", + cur->index, + cur->x, + cur->y, + cur->z, + cur->heading, + cur->pause ); + } +} + +void NPC::StopWandering() +{ // stops a mob from wandering, takes him off grid and sends him back to spawn point + roamer=false; + CastToNPC()->SetGrid(0); + SendPosition(); + mlog(QUESTS__PATHING, "Stop Wandering requested."); + return; +} + +void NPC::ResumeWandering() +{ // causes wandering to continue - overrides waypoint pause timer and PauseWandering() + if(!IsNPC()) + return; + if (GetGrid() != 0) + { + if (GetGrid() < 0) + { // we were paused by a quest + AIwalking_timer->Disable(); + SetGrid( 0 - GetGrid()); + if (cur_wp==-1) + { // got here by a MoveTo() + cur_wp=save_wp; + UpdateWaypoint(cur_wp); // have him head to last destination from here + } + mlog(QUESTS__PATHING, "Resume Wandering requested. Grid %d, wp %d", GetGrid(), cur_wp); + } + else if (AIwalking_timer->Enabled()) + { // we are at a waypoint paused normally + mlog(QUESTS__PATHING, "Resume Wandering on timed pause. Grid %d, wp %d", GetGrid(), cur_wp); + AIwalking_timer->Trigger(); // disable timer to end pause now + } + else + { + LogFile->write(EQEMuLog::Error, "NPC not paused - can't resume wandering: %lu", (unsigned long)GetNPCTypeID()); + return; + } + + if (cur_wp_x == GetX() && cur_wp_y == GetY()) + { // are we we at a waypoint? if so, trigger event and start to next + char temp[100]; + itoa(cur_wp,temp,10); //do this before updating to next waypoint + CalculateNewWaypoint(); + SetAppearance(eaStanding, false); + parse->EventNPC(EVENT_WAYPOINT_DEPART, this, NULL, temp, 0); + } // if not currently at a waypoint, we continue on to the one we were headed to before the stop + } + else + { + LogFile->write(EQEMuLog::Error, "NPC not on grid - can't resume wandering: %lu", (unsigned long)GetNPCTypeID()); + } + return; +} + +void NPC::PauseWandering(int pausetime) +{ // causes wandering to stop but is resumable + // 0 pausetime means pause until resumed + // otherwise automatically resume when time is up + if (GetGrid() != 0) + { + DistractedFromGrid = true; + mlog(QUESTS__PATHING, "Paused Wandering requested. Grid %d. Resuming in %d ms (0=not until told)", GetGrid(), pausetime); + SendPosition(); + if (pausetime<1) + { // negative grid number stops him dead in his tracks until ResumeWandering() + SetGrid( 0 - GetGrid()); + } + else + { // specified waiting time, he'll resume after that + AIwalking_timer->Start(pausetime*1000); // set the timer + } + } else { + LogFile->write(EQEMuLog::Error, "NPC not on grid - can't pause wandering: %lu", (unsigned long)GetNPCTypeID()); + } + return; +} + +void NPC::MoveTo(float mtx, float mty, float mtz, float mth, bool saveguardspot) +{ // makes mob walk to specified location + if (IsNPC() && GetGrid() != 0) + { // he is on a grid + if (GetGrid() < 0) + { // currently stopped by a quest command + SetGrid( 0 - GetGrid()); // get him moving again + mlog(AI__WAYPOINTS, "MoveTo during quest wandering. Canceling quest wandering and going back to grid %d when MoveTo is done.", GetGrid()); + } + AIwalking_timer->Disable(); // disable timer in case he is paused at a wp + if (cur_wp>=0) + { // we've not already done a MoveTo() + save_wp=cur_wp; // save the current waypoint + cur_wp=-1; // flag this move as quest controlled + } + mlog(AI__WAYPOINTS, "MoveTo (%.3f, %.3f, %.3f), pausing regular grid wandering. Grid %d, save_wp %d", mtx, mty, mtz, -GetGrid(), save_wp); + } + else + { // not on a grid + roamer=true; + save_wp=0; + cur_wp=-2; // flag as quest controlled w/no grid + mlog(AI__WAYPOINTS, "MoveTo (%.3f, %.3f, %.3f) without a grid.", mtx, mty, mtz); + } + if (saveguardspot) + { + guard_x = mtx; + guard_y = mty; + guard_z = mtz; + guard_heading = mth; + + if(guard_heading == 0) + guard_heading = 0.0001; //hack to make IsGuarding simpler + + if(guard_heading == -1) + guard_heading = this->CalculateHeadingToTarget(mtx, mty); + + mlog(AI__WAYPOINTS, "Setting guard position to (%.3f, %.3f, %.3f)", guard_x, guard_y, guard_z); + } + + cur_wp_x = mtx; + cur_wp_y = mty; + cur_wp_z = mtz; + cur_wp_pause = 0; + cur_wp_heading = mth; + pLastFightingDelayMoving = 0; + if(AIwalking_timer->Enabled()) + AIwalking_timer->Start(100); +} + +void NPC::UpdateWaypoint(int wp_index) +{ + if(wp_index >= static_cast(Waypoints.size())) { + mlog(AI__WAYPOINTS, "Update to waypoint %d failed. Not found.", wp_index); + return; + } + vector::iterator cur; + cur = Waypoints.begin(); + cur += wp_index; + + cur_wp_x = cur->x; + cur_wp_y = cur->y; + cur_wp_z = cur->z; + cur_wp_pause = cur->pause; + cur_wp_heading = cur->heading; + mlog(AI__WAYPOINTS, "Next waypoint %d: (%.3f, %.3f, %.3f, %.3f)", wp_index, cur_wp_x, cur_wp_y, cur_wp_z, cur_wp_heading); + + //fix up pathing Z + if(zone->HasMap() && RuleB(Map, FixPathingZAtWaypoints)) + { + + if(!RuleB(Watermap, CheckForWaterAtWaypoints) || !zone->HasWaterMap() || + (zone->HasWaterMap() && !zone->watermap->InWater(cur_wp_x, cur_wp_y, cur_wp_z))) + { + VERTEX dest(cur_wp_x, cur_wp_y, cur_wp_z); + + float newz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, dest, NULL, NULL); + + if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaWaypoint)) + cur_wp_z = newz + 1; + } + } + +} + +void NPC::CalculateNewWaypoint() +{ + int old_wp = cur_wp; + bool reached_end = false; + bool reached_beginning = false; + if (cur_wp == max_wp) + reached_end = true; + if (cur_wp == 0) + reached_beginning = true; + + switch(wandertype) + { + case 0: //circle + { + if (reached_end) + cur_wp = 0; + else + cur_wp = cur_wp + 1; + break; + } + case 1: //10 closest + { + list closest; + GetClosestWaypoint(closest, 10, GetX(), GetY(), GetZ()); + list::iterator iter = closest.begin(); + if(closest.size() != 0) + { + int idx = MakeRandomInt(0, closest.size() - 1); + iter = closest.begin(); + for(int i = 0; i < idx; ++i) + { + iter++; + } + cur_wp = (*iter).index; + } + + break; + } + case 2: //random + { + cur_wp = MakeRandomInt(0, Waypoints.size() - 1); + if(cur_wp == old_wp) + { + if(cur_wp == (Waypoints.size() - 1)) + { + if(cur_wp > 0) + { + cur_wp--; + } + } + else if(cur_wp == 0) + { + if((Waypoints.size() - 1) > 0) + { + cur_wp++; + } + } + } + + break; + } + case 3: //patrol + { + if(reached_end) + patrol = 1; + else if(reached_beginning) + patrol = 0; + if(patrol == 1) + cur_wp = cur_wp - 1; + else + cur_wp = cur_wp + 1; + + break; + } + case 4: //goto the end and depop with spawn timer + case 6: //goto the end and depop without spawn timer + { + cur_wp = cur_wp + 1; + break; + } + case 5: //pick random closest 5 and pick one that's in sight + { + list closest; + GetClosestWaypoint(closest, 5, GetX(), GetY(), GetZ()); + + list::iterator iter = closest.begin(); + while(iter != closest.end()) + { + if(CheckLosFN((*iter).x, (*iter).y, (*iter).z, GetSize())) + { + iter++; + } + else + { + iter = closest.erase(iter); + } + } + + if(closest.size() != 0) + { + int idx = MakeRandomInt(0, closest.size() - 1); + iter = closest.begin(); + for(int i = 0; i < idx; ++i) + { + iter++; + } + cur_wp = (*iter).index; + } + break; + } + } + + tar_ndx = 52; + + // Preserve waypoint setting for quest controlled NPCs + if (cur_wp < 0) + cur_wp = old_wp; + + // Check to see if we need to update the waypoint. + if (cur_wp != old_wp) + UpdateWaypoint(cur_wp); +} + +bool wp_distance_pred(const wp_distance& left, const wp_distance& right) +{ + return left.dist < right.dist; +} + +void NPC::GetClosestWaypoint(list &wp_list, int count, float m_x, float m_y, float m_z) +{ + wp_list.clear(); + if(Waypoints.size() <= count) + { + for(int i = 0; i < Waypoints.size(); ++i) + { + wp_list.push_back(Waypoints[i]); + } + return; + } + + list distances; + for(int i = 0; i < Waypoints.size(); ++i) + { + float cur_x = (Waypoints[i].x - m_x); + cur_x *= cur_x; + float cur_y = (Waypoints[i].y - m_y); + cur_y *= cur_y; + float cur_z = (Waypoints[i].z - m_z); + cur_z *= cur_z; + float cur_dist = cur_x + cur_y + cur_z; + wp_distance w_dist; + w_dist.dist = cur_dist; + w_dist.index = i; + distances.push_back(w_dist); + } + distances.sort(wp_distance_pred); + + list::iterator iter = distances.begin(); + for(int i = 0; i < count; ++i) + { + wp_list.push_back(Waypoints[(*iter).index]); + iter++; + } +} + +void NPC::SetWaypointPause() +{ + //Declare time to wait on current WP + + if (cur_wp_pause == 0) { + AIwalking_timer->Start(100); + } + else + { + + switch (pausetype) + { + case 0: //Random Half + AIwalking_timer->Start((cur_wp_pause - MakeRandomInt(0, cur_wp_pause-1)/2)*1000); + break; + case 1: //Full + AIwalking_timer->Start(cur_wp_pause*1000); + break; + case 2: //Random Full + AIwalking_timer->Start(MakeRandomInt(0, cur_wp_pause-1)*1000); + break; + } + } +} + +void NPC::SaveGuardSpot(bool iClearGuardSpot) { + if (iClearGuardSpot) { + mlog(AI__WAYPOINTS, "Clearing guard order."); + guard_x = 0; + guard_y = 0; + guard_z = 0; + guard_heading = 0; + } + else { + guard_x = x_pos; + guard_y = y_pos; + guard_z = z_pos; + guard_heading = heading; + if(guard_heading == 0) + guard_heading = 0.0001; //hack to make IsGuarding simpler + mlog(AI__WAYPOINTS, "Setting guard position to (%.3f, %.3f, %.3f)", guard_x, guard_y, guard_z); + } +} + +void NPC::NextGuardPosition() { + if (!CalculateNewPosition2(guard_x, guard_y, guard_z, GetMovespeed())) { + SetHeading(guard_heading); + mlog(AI__WAYPOINTS, "Unable to move to next guard position. Probably rooted."); + } + else if((x_pos == guard_x) && (y_pos == guard_y) && (z_pos == guard_z)) + { + if(moved) + { + moved=false; + SetMoving(false); + SendPosition(); + } + } +} + +/* +// we need this for charmed NPCs +void Mob::SaveSpawnSpot() { + spawn_x = x_pos; + spawn_y = y_pos; + spawn_z = z_pos; + spawn_heading = heading; +}*/ + + + + +/*float Mob::CalculateDistanceToNextWaypoint() { + return CalculateDistance(cur_wp_x, cur_wp_y, cur_wp_z); +}*/ + +float Mob::CalculateDistance(float x, float y, float z) { + return (float)sqrtf( ((x_pos-x)*(x_pos-x)) + ((y_pos-y)*(y_pos-y)) + ((z_pos-z)*(z_pos-z)) ); +} + +/* +uint8 NPC::CalculateHeadingToNextWaypoint() { + return CalculateHeadingToTarget(cur_wp_x, cur_wp_y); +} +*/ +float Mob::CalculateHeadingToTarget(float in_x, float in_y) { + float angle; + + if (in_x-x_pos > 0) + angle = - 90 + atan((float)(in_y-y_pos) / (float)(in_x-x_pos)) * 180 / M_PI; + else if (in_x-x_pos < 0) + angle = + 90 + atan((float)(in_y-y_pos) / (float)(in_x-x_pos)) * 180 / M_PI; + else // Added? + { + if (in_y-y_pos > 0) + angle = 0; + else + angle = 180; + } + if (angle < 0) + angle += 360; + if (angle > 360) + angle -= 360; + return (256*(360-angle)/360.0f); +} + +bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, bool checkZ) { + if(GetID()==0) + return true; + + _ZP(Mob_CalculateNewPosition2); + + if ((x_pos-x == 0) && (y_pos-y == 0)) {//spawn is at target coords + if(z_pos-z != 0) { + z_pos = z; + mlog(AI__WAYPOINTS, "Calc Position2 (%.3f, %.3f, %.3f): Jumping pure Z.", x, y, z); + return true; + } + mlog(AI__WAYPOINTS, "Calc Position2 (%.3f, %.3f, %.3f) inWater=%d: We are there.", x, y, z, inWater); + return false; + } + else if ((ABS(x_pos - x) < 0.1) && (ABS(y_pos - y) < 0.1)) + { + mlog(AI__WAYPOINTS, "Calc Position2 (%.3f, %.3f, %.3f): X/Y difference <0.1, Jumping to target.", x, y, z); + x_pos = x; + y_pos = y; + z_pos = z; + return true; + } + + int compare_steps = IsBoat() ? 1 : 20; + if(tar_ndx < compare_steps && tarx==x && tary==y){ + x_pos = x_pos + tar_vx*tar_vector; + y_pos = y_pos + tar_vy*tar_vector; + z_pos = z_pos + tar_vz*tar_vector; + + mlog(AI__WAYPOINTS, "Calculating new position2 to (%.3f, %.3f, %.3f), old vector (%.3f, %.3f, %.3f)", x, y, z, tar_vx, tar_vy, tar_vz); + + uint8 NPCFlyMode = 0; + + if(IsNPC()) { + if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) + NPCFlyMode = 1; + } + + //fix up pathing Z + if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) + { + if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || + (zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos))) + { + VERTEX dest(x_pos, y_pos, z_pos); + + float newz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, dest, NULL, NULL); + + mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); + + if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. + { + if((ABS(x - x_pos) < 0.5) && (ABS(y - y_pos) < 0.5)) + { + if(ABS(z-z_pos) <= RuleR(Map, FixPathingZMaxDeltaMoving)) + z_pos = z; + else + z_pos = newz + 1; + } + else + z_pos = newz + 1; + } + } + } + + tar_ndx++; + return true; + } + + + if (tar_ndx>50) { + tar_ndx--; + } else { + tar_ndx=0; + } + tarx=x; + tary=y; + tarz=z; + + float nx = this->x_pos; + float ny = this->y_pos; + float nz = this->z_pos; +// float nh = this->heading; + + tar_vx = x - nx; + tar_vy = y - ny; + tar_vz = z - nz; + + //pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO); + //speed *= NPC_SPEED_MULTIPLIER; + + mlog(AI__WAYPOINTS, "Calculating new position2 to (%.3f, %.3f, %.3f), new vector (%.3f, %.3f, %.3f) rate %.3f, RAS %d", x, y, z, tar_vx, tar_vy, tar_vz, speed, pRunAnimSpeed); + + // -------------------------------------------------------------------------- + // 2: get unit vector + // -------------------------------------------------------------------------- + float mag = sqrtf (tar_vx*tar_vx + tar_vy*tar_vy + tar_vz*tar_vz); + tar_vector = speed / mag; + +// mob move fix + int numsteps = (int) ( mag * 20 / speed) + 1; + + +// mob move fix + + if (numsteps<20) + { + if (numsteps>1) + { + tar_vector=1.0f ; + tar_vx = tar_vx/numsteps; + tar_vy = tar_vy/numsteps; + tar_vz = tar_vz/numsteps; + x_pos = x_pos + tar_vx; + y_pos = y_pos + tar_vy; + z_pos = z_pos + tar_vz; + tar_ndx=22-numsteps; + heading = CalculateHeadingToTarget(x, y); + mlog(AI__WAYPOINTS, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", x_pos, y_pos, z_pos, numsteps); + } + else + { + x_pos = x; + y_pos = y; + z_pos = z; + + mlog(AI__WAYPOINTS, "Only a single step to get there... jumping."); + + } + } + + else { + tar_vector/=20; + x_pos = x_pos + tar_vx*tar_vector; + y_pos = y_pos + tar_vy*tar_vector; + z_pos = z_pos + tar_vz*tar_vector; + heading = CalculateHeadingToTarget(x, y); + mlog(AI__WAYPOINTS, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", x_pos, y_pos, z_pos, numsteps); + } + + uint8 NPCFlyMode = 0; + + if(IsNPC()) { + if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) + NPCFlyMode = 1; + } + + //fix up pathing Z + if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) { + + if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || + (zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos))) + { + VERTEX dest(x_pos, y_pos, z_pos); + + float newz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, dest, NULL, NULL); + + mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); + + if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. + { + if(ABS(x - x_pos) < 0.5 && ABS(y - y_pos) < 0.5) + { + if(ABS(z - z_pos) <= RuleR(Map, FixPathingZMaxDeltaMoving)) + z_pos = z; + else + z_pos = newz + 1; + } + else + z_pos = newz+1; + } + } + } + + SetMoving(true); + moved=true; + + delta_x=x_pos-nx; + delta_y=y_pos-ny; + delta_z=z_pos-nz; + delta_heading=0; + + if (IsClient()) + SendPosUpdate(1); + else + SendPosUpdate(); + + SetAppearance(eaStanding, false); + pLastChange = Timer::GetCurrentTime(); + return true; +} + +bool Mob::CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ) { + if(IsNPC() || IsClient() || IsPet()) { + pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO); + speed *= NPC_SPEED_MULTIPLIER; + } + + return MakeNewPositionAndSendUpdate(x, y, z, speed, checkZ); +} + +bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool checkZ) { + if(GetID()==0) + return true; + + _ZP(Mob_CalculateNewPosition); + + float nx = x_pos; + float ny = y_pos; + float nz = z_pos; +// float nh = heading; + + // if NPC is rooted + if (speed == 0.0) { + SetHeading(CalculateHeadingToTarget(x, y)); + if(moved){ + SendPosition(); + SetMoving(false); + moved=false; + } + SetRunAnimSpeed(0); + mlog(AI__WAYPOINTS, "Rooted while calculating new position to (%.3f, %.3f, %.3f)", x, y, z); + return true; + } + + float old_test_vector=test_vector; + tar_vx = x - nx; + tar_vy = y - ny; + tar_vz = z - nz; + + if (tar_vx == 0 && tar_vy == 0) + return false; + pRunAnimSpeed = (uint8)(speed*NPC_RUNANIM_RATIO); + speed *= NPC_SPEED_MULTIPLIER; + + mlog(AI__WAYPOINTS, "Calculating new position to (%.3f, %.3f, %.3f) vector (%.3f, %.3f, %.3f) rate %.3f RAS %d", x, y, z, tar_vx, tar_vy, tar_vz, speed, pRunAnimSpeed); + + // -------------------------------------------------------------------------- + // 2: get unit vector + // -------------------------------------------------------------------------- + test_vector=sqrtf (x*x + y*y + z*z); + tar_vector = speed / sqrtf (tar_vx*tar_vx + tar_vy*tar_vy + tar_vz*tar_vz); + heading = CalculateHeadingToTarget(x, y); + + if (tar_vector >= 1.0) { + x_pos = x; + y_pos = y; + z_pos = z; + mlog(AI__WAYPOINTS, "Close enough, jumping to waypoint"); + } + else { + x_pos = x_pos + tar_vx*tar_vector; + y_pos = y_pos + tar_vy*tar_vector; + z_pos = z_pos + tar_vz*tar_vector; + mlog(AI__WAYPOINTS, "Next position (%.3f, %.3f, %.3f)", x_pos, y_pos, z_pos); + } + + uint8 NPCFlyMode = 0; + + if(IsNPC()) { + if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) + NPCFlyMode = 1; + } + + //fix up pathing Z + if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) + { + if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || + (zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos))) + { + VERTEX dest(x_pos, y_pos, z_pos); + + float newz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, dest, NULL, NULL); + + mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); + + if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. + { + if(ABS(x - x_pos) < 0.5 && ABS(y - y_pos) < 0.5) + { + if(ABS(z - z_pos) <= RuleR(Map, FixPathingZMaxDeltaMoving)) + z_pos = z; + else + z_pos = newz + 1; + } + else + z_pos = newz+1; + } + } + } + + //OP_MobUpdate + if((old_test_vector!=test_vector) || tar_ndx>20){ //send update + tar_ndx=0; + this->SetMoving(true); + moved=true; + delta_x=(x_pos-nx); + delta_y=(y_pos-ny); + delta_z=(z_pos-nz); + delta_heading=0;//(heading-nh)*8; + SendPosUpdate(); + } + tar_ndx++; + + // now get new heading + SetAppearance(eaStanding, false); // make sure they're standing + pLastChange = Timer::GetCurrentTime(); + return true; +} + +void NPC::AssignWaypoints(int32 grid) { + if(grid == 0) + return; //grid ID 0 not supported + + if(grid < 0) { + // Allow setting negative grid values for pausing pathing + this->CastToNPC()->SetGrid(grid); + return; + } + + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + bool GridErr = false, WPErr = false; + Waypoints.clear(); + + // 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); + } + 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 + adverrorinfo = 7561; + + // 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 + adverrorinfo = 7564; + + 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]); + + if(zone->HasMap() && RuleB(Map, FixPathingZWhenLoading) ) + { + if(!RuleB(Watermap, CheckWaypointsInWaterWhenLoading) || !zone->HasWaterMap() || + (zone->HasWaterMap() && !zone->watermap->InWater(newwp.x, newwp.y, newwp.z))) + { + VERTEX dest(newwp.x, newwp.y, newwp.z); + + float newz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, dest, NULL, NULL); + + 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); + } + } + 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; + } +} + +void Mob::SendTo(float new_x, float new_y, float new_z) { + x_pos = new_x; + y_pos = new_y; + z_pos = new_z; + mlog(AI__WAYPOINTS, "Sent To (%.3f, %.3f, %.3f)", new_x, new_y, new_z); + + if(flymode == FlyMode1) + return; + + //fix up pathing Z, this shouldent be needed IF our waypoints + //are corrected instead + if(zone->HasMap() && RuleB(Map, FixPathingZOnSendTo) ) + { + if(!RuleB(Watermap, CheckForWaterOnSendTo) || !zone->HasWaterMap() || + (zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos))) + { + VERTEX dest(x_pos, y_pos, z_pos); + + float newz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, dest, NULL, NULL); + + mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); + + if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaSendTo)) // Sanity check. + z_pos = newz + 1; + } + } + else + z_pos += 0.1; +} + +void Mob::SendToFixZ(float new_x, float new_y, float new_z) { + x_pos = new_x; + y_pos = new_y; + z_pos = new_z + 0.1; + + //fix up pathing Z, this shouldent be needed IF our waypoints + //are corrected instead + + if(zone->HasMap() && RuleB(Map, FixPathingZOnSendTo)) + { + if(!RuleB(Watermap, CheckForWaterOnSendTo) || !zone->HasWaterMap() || + (zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos))) + { + VERTEX dest(x_pos, y_pos, z_pos); + + float newz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, dest, NULL, NULL); + + mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); + + if( (newz > -2000) && ABS(newz-dest.z) < RuleR(Map, FixPathingZMaxDeltaSendTo)) // Sanity check. + z_pos = newz + 1; + } + } +} + +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); +} + +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); +} + +bool ZoneDatabase::GetWaypoints(uint32 grid, uint16 zoneid, uint32 num, wplist* wp) { + _CP(Database_GetWaypoints); + 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; +} + +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; + } + 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 + { + 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); + return; + } + safe_delete_array(query); + fuzzy = 1; + if(!(matches = mysql_num_rows(result))) + mysql_free_result(result); + } + if(matches) + { + 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"); + } +} + +/****************** +* ModifyGrid - Either adds an empty grid, or removes a grid and all its waypoints, for a particular zone. +* remove: TRUE if we are deleting the specified grid, FALSE if we are adding it +* id: The ID# of the grid to add or delete +* 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 *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); + } + 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() ***/ + +/************************************** +* 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) +{ + 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); + } + safe_delete_array(query); +} /*** END ZoneDatabase::AddWP() ***/ + + +/********** +* ModifyWP() has been obsoleted. The #wp command either uses AddWP() or DeleteWaypoint() +***********/ + +/****************** +* DeleteWaypoint - Removes a specific waypoint from the grid +* grid_id: The ID number of the grid whose wp is being deleted +* 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) +{ + 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); + } + safe_delete_array(query); +} /*** END ZoneDatabase::DeleteWaypoint() ***/ + + +/****************** +* AddWPForSpawn - Used by the #wpadd command - for a given spawn, this will add a new waypoint to whatever grid that spawn is assigned to. +* If there is currently no grid assigned to the spawn, a new grid will be created using the next available Grid ID number for the zone +* the spawn is in. +* 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 *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]; + + // 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); + 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(!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); + + 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); + } + else // NPC had a grid assigned to it + 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; + + mysql_free_result(result); + } + else { // Query error + LogFile->write(EQEMuLog::Error, "Error getting next waypoint id '%s': '%s'", query, errbuf); + 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); + + if(CreatedNewGrid) + return grid_num; + + return 0; +} /*** END ZoneDatabase::AddWPForSpawn() ***/ + + +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); + } + else { + LogFile->write(EQEMuLog::Error, "Error in GetFreeGrid query '%s': %s", query, errbuf); + safe_delete_array(query); + } + return 0; +} + +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); + } + + return(res); +} + +void NPC::SaveGuardSpotCharm() +{ + guard_x_saved = guard_x; + guard_y_saved = guard_y; + guard_z_saved = guard_z; + guard_heading_saved = guard_heading; +} + +void NPC::RestoreGuardSpotCharm() +{ + guard_x = guard_x_saved; + guard_y = guard_y_saved; + guard_z = guard_z_saved; + guard_heading = guard_heading_saved; +} diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp new file mode 100644 index 000000000..7059365f5 --- /dev/null +++ b/zone/worldserver.cpp @@ -0,0 +1,2198 @@ +/* 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 "../common/debug.h" +#include +using namespace std; +#include +#include +#include +#include +#include +#include + +#ifdef _WINDOWS + #include + + #define snprintf _snprintf +#if (_MSC_VER < 1500) + #define vsnprintf _vsnprintf +#endif + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#endif + +#include "../common/servertalk.h" +#include "worldserver.h" +#include "../common/eq_packet_structs.h" +#include "../common/packet_dump.h" +#include "../common/MiscFunctions.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 "guild_mgr.h" +#include "../common/rulesys.h" +#include "titles.h" +#include "QGlobals.h" + + +extern EntityList entity_list; +extern Zone* zone; +extern volatile bool ZoneLoaded; +extern void CatchSignal(int); +extern WorldServer worldserver; +extern NetConnection net; +extern PetitionList petition_list; +extern uint32 numclients; +extern volatile bool RunLoops; + +WorldServer::WorldServer() +: WorldConnection(EmuTCPConnection::packetModeZone) +{ + cur_groupid = 0; + last_groupid = 0; + oocmuted = false; +} + +WorldServer::~WorldServer() { +} + +/*void WorldServer::SendGuildJoin(GuildJoin_Struct* gj){ + ServerPacket* pack = new ServerPacket(ServerOP_GuildJoin, sizeof(GuildJoin_Struct)); + GuildJoin_Struct* wgj = (GuildJoin_Struct*)pack->pBuffer; + wgj->class_=gj->class_; + wgj->guild_id=gj->guild_id; + wgj->level=gj->level; + strcpy(wgj->name,gj->name); + wgj->rank=gj->rank; + wgj->zoneid=gj->zoneid; + SendPacket(pack); + safe_delete(pack); +}*/ + +void WorldServer::SetZone(uint32 iZoneID, uint32 iInstanceID) { + ServerPacket* pack = new ServerPacket(ServerOP_SetZone, sizeof(SetZone_Struct)); + SetZone_Struct* szs = (SetZone_Struct*) pack->pBuffer; + szs->zoneid = iZoneID; + szs->instanceid = iInstanceID; + if (zone) { + szs->staticzone = zone->IsStaticZone(); + } + SendPacket(pack); + safe_delete(pack); +} + +void WorldServer::OnConnected() { + WorldConnection::OnConnected(); + + ServerPacket* pack; + + //tell the launcher what name we were started with. + pack = new ServerPacket(ServerOP_SetLaunchName,sizeof(LaunchName_Struct)); + LaunchName_Struct* ln = (LaunchName_Struct*)pack->pBuffer; + strn0cpy(ln->launcher_name, m_launcherName.c_str(), 32); + strn0cpy(ln->zone_name, m_launchedName.c_str(), 16); + SendPacket(pack); + safe_delete(pack); + + pack = new ServerPacket(ServerOP_SetConnectInfo, sizeof(ServerConnectInfo)); + ServerConnectInfo* sci = (ServerConnectInfo*) pack->pBuffer; + sci->port = ZoneConfig::get()->ZonePort; + SendPacket(pack); + safe_delete(pack); + + if (ZoneLoaded) { + this->SetZone(zone->GetZoneID(), zone->GetInstanceID()); + entity_list.UpdateWho(true); + this->SendEmoteMessage(0, 0, 15, "Zone connect: %s", zone->GetLongName()); + zone->GetTimeSync(); + } else { + this->SetZone(0); + } + + pack = new ServerPacket(ServerOP_LSZoneBoot,sizeof(ZoneBoot_Struct)); + ZoneBoot_Struct* zbs = (ZoneBoot_Struct*)pack->pBuffer; + strcpy(zbs->compile_time,LAST_MODIFIED); + SendPacket(pack); + safe_delete(pack); +} + +void WorldServer::Process() { + _ZP(WorldServer_Process); + + WorldConnection::Process(); + + if (!Connected()) + return; + + ServerPacket *pack = 0; + while((pack = tcpc.PopPacket())) { + _log(ZONE__WORLD_TRACE,"Got 0x%04x from world:",pack->opcode); + _hex(ZONE__WORLD_TRACE,pack->pBuffer,pack->size); + switch(pack->opcode) { + case 0: { + break; + } + case ServerOP_KeepAlive: { + // ignore this + break; + } + // World is tellins us what port to use. + case ServerOP_SetConnectInfo: { + if (pack->size != sizeof(ServerConnectInfo)) + break; + ServerConnectInfo* sci = (ServerConnectInfo*) pack->pBuffer; + _log(ZONE__WORLD,"World indicated port %d for this zone.",sci->port); + ZoneConfig::SetZonePort(sci->port); + break; + } + case ServerOP_ZAAuthFailed: { + cout << "World server responded 'Not Authorized', disabling reconnect" << endl; + pTryReconnect = false; + Disconnect(); + break; + } + case ServerOP_ChannelMessage: { + 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) { + 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 it's a tell, echo back so it shows up + scm->noreply = true; + scm->chan_num = 14; + memset(scm->deliverto, 0, sizeof(scm->deliverto)); + strcpy(scm->deliverto, scm->from); + pack->Deflate(); + SendPacket(pack); + } + } + } + } + break; + } + case ServerOP_VoiceMacro: { + + if (!ZoneLoaded) + break; + + ServerVoiceMacro_Struct* svm = (ServerVoiceMacro_Struct*) pack->pBuffer; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_VoiceMacroOut,sizeof(VoiceMacroOut_Struct)); + VoiceMacroOut_Struct* vmo = (VoiceMacroOut_Struct*)outapp->pBuffer; + + strcpy(vmo->From, svm->From); + vmo->Type = svm->Type; + vmo->Voice =svm->Voice; + vmo->MacroNumber = svm->MacroNumber; + + switch(svm->Type) { + case VoiceMacroTell: { + Client* c = entity_list.GetClientByName(svm->To); + if(!c) + break; + + c->QueuePacket(outapp); + break; + } + + case VoiceMacroGroup: { + Group* g = entity_list.GetGroupByID(svm->GroupID); + + if(!g) + break; + + for(unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g->members[i] && g->members[i]->IsClient()) + g->members[i]->CastToClient()->QueuePacket(outapp); + + } + break; + } + + case VoiceMacroRaid: { + Raid *r = entity_list.GetRaidByID(svm->RaidID); + + if(!r) + break; + + for(int i = 0; i < MAX_RAID_MEMBERS; i++) + if(r->members[i].member) + r->members[i].member->QueuePacket(outapp); + + break; + } + } + safe_delete(outapp); + break; + } + + case ServerOP_SpawnCondition: { + if(pack->size != sizeof(ServerSpawnCondition_Struct)) + break; + if (!ZoneLoaded) + break; + ServerSpawnCondition_Struct* ssc = (ServerSpawnCondition_Struct*) pack->pBuffer; + + zone->spawn_conditions.SetCondition(zone->GetShortName(), zone->GetInstanceID(), ssc->condition_id, ssc->value, true); + break; + } + case ServerOP_SpawnEvent: { + if(pack->size != sizeof(ServerSpawnEvent_Struct)) + break; + if (!ZoneLoaded) + break; + ServerSpawnEvent_Struct* sse = (ServerSpawnEvent_Struct*) pack->pBuffer; + + zone->spawn_conditions.ReloadEvent(sse->event_id); + + break; + } + case ServerOP_AcceptWorldEntrance: { + if(pack->size != sizeof(WorldToZone_Struct)) + break; + if (!ZoneLoaded) + break; + WorldToZone_Struct* wtz = (WorldToZone_Struct*) pack->pBuffer; + + if(zone->GetMaxClients() != 0 && numclients >= zone->GetMaxClients()) + wtz->response = -1; + else + wtz->response = 1; + + SendPacket(pack); + break; + } + case ServerOP_ZoneToZoneRequest: { + if(pack->size != sizeof(ZoneToZone_Struct)) + break; + if (!ZoneLoaded) + break; + ZoneToZone_Struct* ztz = (ZoneToZone_Struct*) pack->pBuffer; + + if(ztz->current_zone_id == zone->GetZoneID() + && ztz->current_instance_id == zone->GetInstanceID()) { + // it's a response + Entity* entity = entity_list.GetClientByName(ztz->name); + if(entity == 0) + break; + + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_ZoneChange,sizeof(ZoneChange_Struct)); + ZoneChange_Struct* zc2=(ZoneChange_Struct*)outapp->pBuffer; + + if(ztz->response <= 0) { + zc2->success = ZONE_ERROR_NOTREADY; + entity->CastToMob()->SetZone(ztz->current_zone_id, ztz->current_instance_id); + } + else { + entity->CastToClient()->UpdateWho(1); + strn0cpy(zc2->char_name,entity->CastToMob()->GetName(),64); + zc2->zoneID=ztz->requested_zone_id; + zc2->instanceID=ztz->requested_instance_id; + zc2->success = 1; + + // This block is necessary to clean up any merc objects owned by a Client. Maybe we should do this for bots, too? + if(entity->CastToClient()->GetMerc() != NULL) + { + entity->CastToClient()->GetMerc()->ProcessClientZoneChange(entity->CastToClient()); + } + + entity->CastToMob()->SetZone(ztz->requested_zone_id, ztz->requested_instance_id); + + if(ztz->ignorerestrictions == 3) + entity->CastToClient()->GoToSafeCoords(ztz->requested_zone_id, ztz->requested_instance_id); + } + + outapp->priority = 6; + entity->CastToClient()->QueuePacket(outapp); + safe_delete(outapp); + + switch(ztz->response) + { + case -2: { + entity->CastToClient()->Message(13,"You do not own the required locations to enter this zone."); + break; + } + case -1: { + entity->CastToClient()->Message(13,"The zone is currently full, please try again later."); + break; + } + case 0: { + entity->CastToClient()->Message(13,"All zone servers are taken at this time, please try again later."); + break; + } + } + } + else { + // it's a request + ztz->response = 0; + + if(zone->GetMaxClients() != 0 && numclients >= zone->GetMaxClients()) + ztz->response = -1; + else { + ztz->response = 1; + // since they asked about comming, lets assume they are on their way and not shut down. + zone->StartShutdownTimer(AUTHENTICATION_TIMEOUT * 1000); + } + + SendPacket(pack); + break; + } + break; + } + case ServerOP_WhoAllReply:{ + if(!ZoneLoaded) + break; + + + WhoAllReturnStruct* wars= (WhoAllReturnStruct*)pack->pBuffer; + if (wars && wars->id!=0 && wars->id<0xFFFFFFFF){ + Client* client = entity_list.GetClientByID(wars->id); + if (client) { + if(pack->size==64)//no results + client->Message_StringID(0,WHOALL_NO_RESULTS); + else{ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_WhoAllResponse, pack->size); + memcpy(outapp->pBuffer, pack->pBuffer, pack->size); + client->QueuePacket(outapp); + safe_delete(outapp); + } + } + else { + #ifdef _EQDEBUG + _log(ZONE__WORLD, "Error: WhoAllReturnStruct did not point to a valid client! " + "id=%i, playerineqstring=%i, playersinzonestring=%i. Dumping WhoAllReturnStruct:", + wars->id, wars->playerineqstring, wars->playersinzonestring); + //DumpPacket(pack); + #endif + } + } + else + _log(ZONE__WORLD_ERR, "WhoAllReturnStruct: Could not get return struct!"); + break; + } + case ServerOP_EmoteMessage: { + if (!ZoneLoaded) + break; + ServerEmoteMessage_Struct* sem = (ServerEmoteMessage_Struct*) pack->pBuffer; + if (sem->to[0] != 0) { + if (strcasecmp(sem->to, zone->GetShortName()) == 0) + entity_list.MessageStatus(sem->guilddbid, sem->minstatus, sem->type, (char*)sem->message); + else { + Client* client = entity_list.GetClientByName(sem->to); + if (client != 0){ + char* newmessage=0; + if(strstr(sem->message,"^")==0) + client->Message(sem->type, (char*)sem->message); + else{ + for(newmessage = strtok((char*)sem->message,"^");newmessage!=NULL;newmessage=strtok(NULL, "^")) + client->Message(sem->type, newmessage); + } + } + } + } + else{ + char* newmessage=0; + if(strstr(sem->message,"^")==0) + entity_list.MessageStatus(sem->guilddbid, sem->minstatus, sem->type, sem->message); + else{ + for(newmessage = strtok((char*)sem->message,"^");newmessage!=NULL;newmessage=strtok(NULL, "^")) + entity_list.MessageStatus(sem->guilddbid, sem->minstatus, sem->type, newmessage); + } + } + break; + } + case ServerOP_Motd: { + ServerMotd_Struct* smotd = (ServerMotd_Struct*) pack->pBuffer; + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_MOTD); + char tmp[500] = {0}; + sprintf(tmp, "%s", smotd->motd); + + outapp->size = strlen(tmp)+1; + outapp->pBuffer = new uchar[outapp->size]; + memset(outapp->pBuffer,0,outapp->size); + strcpy((char*)outapp->pBuffer, tmp); + + entity_list.QueueClients(0, outapp); + safe_delete(outapp); + + break; + } + case ServerOP_ShutdownAll: { + entity_list.Save(); + CatchSignal(2); + break; + } + case ServerOP_ZoneShutdown: { + if (pack->size != sizeof(ServerZoneStateChange_struct)) { + cout << "Wrong size on ServerOP_ZoneShutdown. Got: " << pack->size << ", Expected: " << sizeof(ServerZoneStateChange_struct) << endl; + break; + } + // Annouce the change to the world + if (!ZoneLoaded) { + SetZone(0); + } + else { + SendEmoteMessage(0, 0, 15, "Zone shutdown: %s", zone->GetLongName()); + + ServerZoneStateChange_struct* zst = (ServerZoneStateChange_struct *) pack->pBuffer; + cout << "Zone shutdown by " << zst->adminname << endl; + Zone::Shutdown(); + } + break; + } + case ServerOP_ZoneBootup: { + if (pack->size != sizeof(ServerZoneStateChange_struct)) { + cout << "Wrong size on ServerOP_ZoneBootup. Got: " << pack->size << ", Expected: " << sizeof(ServerZoneStateChange_struct) << endl; + break; + } + ServerZoneStateChange_struct* zst = (ServerZoneStateChange_struct *) pack->pBuffer; + if (ZoneLoaded) { + SetZone(zone->GetZoneID(), zone->GetInstanceID()); + if (zst->zoneid == zone->GetZoneID()) { + // This packet also doubles as "incomming client" notification, lets not shut down before they get here + zone->StartShutdownTimer(AUTHENTICATION_TIMEOUT * 1000); + } + else { + SendEmoteMessage(zst->adminname, 0, 0, "Zone bootup failed: Already running '%s'", zone->GetShortName()); + } + break; + } + + if (zst->adminname[0] != 0) + cout << "Zone bootup by " << zst->adminname << endl; + + if (!(Zone::Bootup(zst->zoneid, zst->instanceid, zst->makestatic))) { + SendChannelMessage(0, 0, 10, 0, 0, "%s:%i Zone::Bootup failed: %s", net.GetZoneAddress(), net.GetZonePort(), database.GetZoneName(zst->zoneid)); + } + // Moved annoucement to ZoneBootup() - Quagmire + // else + // SendEmoteMessage(0, 0, 15, "Zone bootup: %s", zone->GetLongName()); + break; + } + case ServerOP_ZoneIncClient: { + if (pack->size != sizeof(ServerZoneIncommingClient_Struct)) { + cout << "Wrong size on ServerOP_ZoneIncClient. Got: " << pack->size << ", Expected: " << sizeof(ServerZoneIncommingClient_Struct) << endl; + break; + } + ServerZoneIncommingClient_Struct* szic = (ServerZoneIncommingClient_Struct*) pack->pBuffer; + if (ZoneLoaded) { + SetZone(zone->GetZoneID(), zone->GetInstanceID()); + if (szic->zoneid == zone->GetZoneID()) { + zone->AddAuth(szic); + // This packet also doubles as "incomming client" notification, lets not shut down before they get here + zone->StartShutdownTimer(AUTHENTICATION_TIMEOUT * 1000); + } + } + else { + if ((Zone::Bootup(szic->zoneid, szic->instanceid))) { + zone->AddAuth(szic); + } + else + SendEmoteMessage(0, 0, 100, 0, "%s:%i Zone::Bootup failed: %s (%i)", net.GetZoneAddress(), net.GetZonePort(), database.GetZoneName(szic->zoneid, true), szic->zoneid); + } + break; + } + case ServerOP_ZonePlayer: { + ServerZonePlayer_Struct* szp = (ServerZonePlayer_Struct*) pack->pBuffer; + Client* client = entity_list.GetClientByName(szp->name); + printf("Zoning %s to %s(%u) - %u\n", client != NULL ? client->GetCleanName() : "Unknown", szp->zone, database.GetZoneID(szp->zone), szp->instance_id); + if (client != 0) { + if (strcasecmp(szp->adminname, szp->name) == 0) + client->Message(0, "Zoning to: %s", szp->zone); + else if (client->GetAnon() == 1 && client->Admin() > szp->adminrank) + break; + else { + SendEmoteMessage(szp->adminname, 0, 0, "Summoning %s to %s %1.1f, %1.1f, %1.1f", szp->name, szp->zone, szp->x_pos, szp->y_pos, szp->z_pos); + } + client->MovePC(database.GetZoneID(szp->zone), szp->instance_id, szp->x_pos, szp->y_pos, szp->z_pos, client->GetHeading(), szp->ignorerestrictions, GMSummon); + } + break; + } + case ServerOP_KickPlayer: { + ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; + Client* client = entity_list.GetClientByName(skp->name); + if (client != 0) { + if (skp->adminrank >= client->Admin()) { + client->WorldKick(); + if (ZoneLoaded) + SendEmoteMessage(skp->adminname, 0, 0, "Remote Kick: %s booted in zone %s.", skp->name, zone->GetShortName()); + else + SendEmoteMessage(skp->adminname, 0, 0, "Remote Kick: %s booted.", skp->name); + } + else if (client->GetAnon() != 1) + SendEmoteMessage(skp->adminname, 0, 0, "Remote Kick: Your avatar level is not high enough to kick %s", skp->name); + } + break; + } + case ServerOP_KillPlayer: { + ServerKillPlayer_Struct* skp = (ServerKillPlayer_Struct*) pack->pBuffer; + Client* client = entity_list.GetClientByName(skp->target); + if (client != 0) { + if (skp->admin >= client->Admin()) { + client->GMKill(); + if (ZoneLoaded) + SendEmoteMessage(skp->gmname, 0, 0, "Remote Kill: %s killed in zone %s.", skp->target, zone->GetShortName()); + else + SendEmoteMessage(skp->gmname, 0, 0, "Remote Kill: %s killed.", skp->target); + } + else if (client->GetAnon() != 1) + SendEmoteMessage(skp->gmname, 0, 0, "Remote Kill: Your avatar level is not high enough to kill %s", skp->target); + } + break; + } + + //hand all the guild related packets to the guild manager for processing. + case ServerOP_OnlineGuildMembersResponse: + case ServerOP_RefreshGuild: +// case ServerOP_GuildInvite: + case ServerOP_DeleteGuild: + case ServerOP_GuildCharRefresh: + case ServerOP_GuildMemberUpdate: + case ServerOP_GuildRankUpdate: + case ServerOP_LFGuildUpdate: +// case ServerOP_GuildGMSet: +// case ServerOP_GuildGMSetRank: +// case ServerOP_GuildJoin: + guild_mgr.ProcessWorldPacket(pack); + break; + + case ServerOP_FlagUpdate: { + Client* client = entity_list.GetClientByAccID(*((uint32*) pack->pBuffer)); + if (client != 0) { + client->UpdateAdmin(); + } + break; + } + case ServerOP_GMGoto: { + if (pack->size != sizeof(ServerGMGoto_Struct)) { + cout << "Wrong size on ServerOP_GMGoto. Got: " << pack->size << ", Expected: " << sizeof(ServerGMGoto_Struct) << endl; + break; + } + if (!ZoneLoaded) + break; + ServerGMGoto_Struct* gmg = (ServerGMGoto_Struct*) pack->pBuffer; + Client* client = entity_list.GetClientByName(gmg->gotoname); + if (client != 0) { + SendEmoteMessage(gmg->myname, 0, 13, "Summoning you to: %s @ %s, %1.1f, %1.1f, %1.1f", client->GetName(), zone->GetShortName(), client->GetX(), client->GetY(), client->GetZ()); + ServerPacket* outpack = new ServerPacket(ServerOP_ZonePlayer, sizeof(ServerZonePlayer_Struct)); + ServerZonePlayer_Struct* szp = (ServerZonePlayer_Struct*) outpack->pBuffer; + strcpy(szp->adminname, gmg->myname); + strcpy(szp->name, gmg->myname); + strcpy(szp->zone, zone->GetShortName()); + szp->instance_id = zone->GetInstanceID(); + szp->x_pos = client->GetX(); + szp->y_pos = client->GetY(); + szp->z_pos = client->GetZ(); + SendPacket(outpack); + safe_delete(outpack); + } + else { + SendEmoteMessage(gmg->myname, 0, 13, "Error: %s not found", gmg->gotoname); + } + break; + } + case ServerOP_MultiLineMsg: { + ServerMultiLineMsg_Struct* mlm = (ServerMultiLineMsg_Struct*) pack->pBuffer; + Client* client = entity_list.GetClientByName(mlm->to); + if (client) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_MultiLineMsg, strlen(mlm->message)); + strcpy((char*) outapp->pBuffer, mlm->message); + client->QueuePacket(outapp); + safe_delete(outapp); + } + break; + } + case ServerOP_Uptime: { + if (pack->size != sizeof(ServerUptime_Struct)) { + cout << "Wrong size on ServerOP_Uptime. Got: " << pack->size << ", Expected: " << sizeof(ServerUptime_Struct) << endl; + break; + } + ServerUptime_Struct* sus = (ServerUptime_Struct*) pack->pBuffer; + uint32 ms = Timer::GetCurrentTime(); + uint32 d = ms / 86400000; + ms -= d * 86400000; + uint32 h = ms / 3600000; + ms -= h * 3600000; + uint32 m = ms / 60000; + ms -= m * 60000; + uint32 s = ms / 1000; + if (d) + this->SendEmoteMessage(sus->adminname, 0, 0, "Zone #%i Uptime: %02id %02ih %02im %02is", sus->zoneserverid, d, h, m, s); + else if (h) + this->SendEmoteMessage(sus->adminname, 0, 0, "Zone #%i Uptime: %02ih %02im %02is", sus->zoneserverid, h, m, s); + else + this->SendEmoteMessage(sus->adminname, 0, 0, "Zone #%i Uptime: %02im %02is", sus->zoneserverid, m, s); + } + case ServerOP_Petition: { + cout << "Got Server Requested Petition List Refresh" << endl; + ServerPetitionUpdate_Struct* sus = (ServerPetitionUpdate_Struct*) pack->pBuffer; + // solar: this was typoed to = instead of ==, not that it acts any different now though.. + if (sus->status == 0) petition_list.ReadDatabase(); + else if (sus->status == 1) petition_list.ReadDatabase(); // Until I fix this to be better.... + break; + } + case ServerOP_RezzPlayer: { + RezzPlayer_Struct* srs = (RezzPlayer_Struct*) pack->pBuffer; + if (srs->rezzopcode == OP_RezzRequest) + { + // The Rezz request has arrived in the zone the player to be rezzed is currently in, + // so we send the request to their client which will bring up the confirmation box. + Client* client = entity_list.GetClientByName(srs->rez.your_name); + if (client) + { + if(client->IsRezzPending()) + { + ServerPacket * Response = new ServerPacket(ServerOP_RezzPlayerReject, strlen(srs->rez.rezzer_name) + 1); + + char *Buffer = (char *)Response->pBuffer; + sprintf(Buffer, "%s", srs->rez.rezzer_name); + worldserver.SendPacket(Response); + safe_delete(Response); + break; + } + //pendingrezexp is the amount of XP on the corpse. Setting it to a value >= 0 + //also serves to inform Client::OPRezzAnswer to expect a packet. + client->SetPendingRezzData(srs->exp, srs->dbid, srs->rez.spellid, srs->rez.corpse_name); + _log(SPELLS__REZ, "OP_RezzRequest in zone %s for %s, spellid:%i", + zone->GetShortName(), client->GetName(), srs->rez.spellid); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RezzRequest, + sizeof(Resurrect_Struct)); + memcpy(outapp->pBuffer, &srs->rez, sizeof(Resurrect_Struct)); + client->QueuePacket(outapp); + _pkt(SPELLS__REZ, outapp); + safe_delete(outapp); + break; + } + } + if (srs->rezzopcode == OP_RezzComplete){ + // We get here when the Rezz complete packet has come back via the world server + // to the zone that the corpse is in. + Corpse* corpse = entity_list.GetCorpseByName(srs->rez.corpse_name); + if (corpse && corpse->IsCorpse()) { + _log(SPELLS__REZ, "OP_RezzComplete received in zone %s for corpse %s", + zone->GetShortName(), srs->rez.corpse_name); + + _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(); + } + } + + break; + } + case ServerOP_RezzPlayerReject: + { + char *Rezzer = (char *)pack->pBuffer; + + Client* c = entity_list.GetClientByName(Rezzer); + + if (c) + c->Message_StringID(MT_WornOff, REZZ_ALREADY_PENDING); + + break; + } + case ServerOP_ZoneReboot: { + cout << "Got Server Requested Zone reboot" << endl; + ServerZoneReboot_Struct* zb = (ServerZoneReboot_Struct*) pack->pBuffer; + // printf("%i\n",zb->zoneid); + struct in_addr in; + in.s_addr = GetIP(); +#ifdef _WINDOWS + char buffer[200]; + snprintf(buffer,200,". %s %i %s",zb->ip2, zb->port, inet_ntoa(in)); + if(zb->zoneid != 0) { + snprintf(buffer,200,"%s %s %i %s",database.GetZoneName(zb->zoneid),zb->ip2, zb->port ,inet_ntoa(in)); + cout << "executing: " << buffer; + ShellExecute(0,"Open",net.GetZoneFileName(), buffer, 0, SW_SHOWDEFAULT); + } + else + { + cout << "executing: " << net.GetZoneFileName() << " " << buffer; + ShellExecute(0,"Open",net.GetZoneFileName(), buffer, 0, SW_SHOWDEFAULT); + } +#else + char buffer[5]; + snprintf(buffer,5,"%i",zb->port); //just to be sure that it will work on linux + if(zb->zoneid != 0) + execl(net.GetZoneFileName(),net.GetZoneFileName(),database.GetZoneName(zb->zoneid),zb->ip2, buffer,inet_ntoa(in), NULL); + else + execl(net.GetZoneFileName(),net.GetZoneFileName(),".",zb->ip2, buffer,inet_ntoa(in), NULL); +#endif + break; + } + case ServerOP_SyncWorldTime: { + if(zone!=0) { + cout << "Received Message SyncWorldTime" << endl; + eqTimeOfDay* newtime = (eqTimeOfDay*) pack->pBuffer; + zone->zone_time.setEQTimeOfDay(newtime->start_eqtime, newtime->start_realtime); + 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(0, outapp, false); + safe_delete(outapp); + //TEST + char timeMessage[255]; + time_t timeCurrent = time(NULL); + TimeOfDay_Struct eqTime; + zone->zone_time.getEQTimeOfDay( timeCurrent, &eqTime); + //if ( eqTime.hour >= 0 && eqTime.minute >= 0 ) + //{ + sprintf(timeMessage,"EQTime [%02d:%s%d %s]", + ((eqTime.hour - 1) % 12) == 0 ? 12 : ((eqTime.hour - 1) % 12), + (eqTime.minute < 10) ? "0" : "", + eqTime.minute, + (eqTime.hour >= 13) ? "pm" : "am" + ); + cout << "Time Broadcast Packet: " << timeMessage << endl; + zone->GotCurTime(true); + //} + //Test + } + break; + } + case ServerOP_ChangeWID: { + if (pack->size != sizeof(ServerChangeWID_Struct)) { + cout << "Wrong size on ServerChangeWID_Struct. Got: " << pack->size << ", Expected: " << sizeof(ServerChangeWID_Struct) << endl; + break; + } + ServerChangeWID_Struct* scw = (ServerChangeWID_Struct*) pack->pBuffer; + Client* client = entity_list.GetClientByCharID(scw->charid); + if (client) + client->SetWID(scw->newwid); + break; + } + case ServerOP_ItemStatus: { + if (pack->size != 5) { + cout << "Wrong size on ServerChangeWID_Struct. Got: " << pack->size << ", Expected: 5" << endl; + break; + } + database.SetItemStatus(*((uint32*) &pack->pBuffer[0]), *((uint8*) &pack->pBuffer[4])); + break; + } + case ServerOP_OOCMute: { + oocmuted = *(pack->pBuffer); + break; + } + case ServerOP_Revoke: { + RevokeStruct* rev = (RevokeStruct*) pack->pBuffer; + Client* client = entity_list.GetClientByName(rev->name); + if (client) + { + SendEmoteMessage(rev->adminname, 0, 0, "%s: %srevoking %s", zone->GetShortName(), rev->toggle?"":"un", client->GetName()); + client->SetRevoked(rev->toggle); + } +#if EQDEBUG >= 6 + else + SendEmoteMessage(rev->adminname, 0, 0, "%s: Can't find %s", zone->GetShortName(), rev->name); +#endif + break; + } + case ServerOP_GroupIDReply: { + ServerGroupIDReply_Struct* ids = (ServerGroupIDReply_Struct*) pack->pBuffer; + cur_groupid = ids->start; + last_groupid = ids->end; +#ifdef _EQDEBUG + printf("Got new group id set: %lu -> %lu\n", (unsigned long)cur_groupid, (unsigned long)last_groupid); +#endif + break; + } + case ServerOP_GroupLeave: { + ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer; + if(zone){ + if(gl->zoneid == zone->GetZoneID() && gl->instance_id == zone->GetInstanceID()) + break; + + entity_list.SendGroupLeave(gl->gid, gl->member_name); + } + break; + } + 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()) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupInvite, sizeof(GroupInvite_Struct)); + memcpy(outapp->pBuffer, gis, sizeof(GroupInvite_Struct)); + Invitee->CastToClient()->QueuePacket(outapp); + safe_delete(outapp); + } + + break; + } + 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); + + if(Inviter && Inviter->IsClient()) + { + Group* group = entity_list.GetGroupByClient(Inviter->CastToClient()); + + if(!group){ + + group = new Group(Inviter); + + if(!group) + break; + + entity_list.AddGroup(group); + + if(group->GetID() == 0) { + Inviter->Message(13, "Unable to get new group id. Cannot create group."); + break; + } + + database.SetGroupID(Inviter->GetName(), group->GetID(), Inviter->CastToClient()->CharacterID()); + + database.SetGroupLeaderName(group->GetID(), Inviter->GetName()); + + group->UpdateGroupAAs(); + + 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) + 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(NULL, sgfs->gf.name2, sgfs->CharacterID)) + break; + + if(Inviter->CastToClient()->IsLFP()) + 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); + + if(!c) + break; + + uint32 groupid = database.GetGroupID(c->GetName()); + + Group* group = NULL; + + if(groupid > 0) + { + group = entity_list.GetGroupByID(groupid); + + if(!group) + { //nobody from our group is here... start a new group + group = new Group(groupid); + + if(group->GetID() != 0) + entity_list.AddGroup(group, groupid); + else + group = NULL; + } //else, somebody from our group is already here... + + if(group) + group->UpdatePlayer(c); + 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(group) + { + database.RefreshGroupFromDB(c); + + group->SendHPPacketsTo(c); + + // 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 *lc = entity_list.GetClientByName(ln); + if(lc) + group->SetLeader(lc); + + group->SetMainTank(MainTankName); + group->SetMainAssist(AssistName); + group->SetPuller(PullerName); + group->SetNPCMarker(NPCMarkerName); + group->SetGroupAAs(&GLAA); + + } + } + break; + + } + case ServerOP_GroupCancelInvite: { + + GroupCancel_Struct* sgcs = (GroupCancel_Struct*) pack->pBuffer; + + Mob* Inviter = entity_list.GetClientByName(sgcs->name1); + + if(Inviter && Inviter->IsClient()) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupCancelInvite, sizeof(GroupCancel_Struct)); + memcpy(outapp->pBuffer, sgcs, sizeof(GroupCancel_Struct)); + Inviter->CastToClient()->QueuePacket(outapp); + safe_delete(outapp); + } + break; + } + case ServerOP_GroupJoin: { + ServerGroupJoin_Struct* gj = (ServerGroupJoin_Struct*)pack->pBuffer; + if(zone){ + if(gj->zoneid == zone->GetZoneID() && gj->instance_id == zone->GetInstanceID()) + break; + + Group* g = entity_list.GetGroupByID(gj->gid); + if(g) + g->AddMember(gj->member_name); + + entity_list.SendGroupJoin(gj->gid, gj->member_name); + } + break; + } + + case ServerOP_ForceGroupUpdate: { + ServerForceGroupUpdate_Struct* fgu = (ServerForceGroupUpdate_Struct*)pack->pBuffer; + if(zone){ + if(fgu->origZoneID == zone->GetZoneID() && fgu->instance_id == zone->GetInstanceID()) + break; + + entity_list.ForceGroupUpdate(fgu->gid); + } + break; + } + + case ServerOP_OOZGroupMessage: { + ServerGroupChannelMessage_Struct* gcm = (ServerGroupChannelMessage_Struct*)pack->pBuffer; + if(zone){ + if(gcm->zoneid == zone->GetZoneID() && gcm->instanceid == zone->GetInstanceID()) + break; + + entity_list.GroupMessage(gcm->groupid, gcm->from, gcm->message); + } + break; + } + case ServerOP_DisbandGroup: { + ServerDisbandGroup_Struct* sd = (ServerDisbandGroup_Struct*)pack->pBuffer; + if(zone){ + if(sd->zoneid == zone->GetZoneID() && sd->instance_id == zone->GetInstanceID()) + break; + + Group *g = entity_list.GetGroupByID(sd->groupid); + if(g) + g->DisbandGroup(); + } + break; + } + case ServerOP_RaidAdd:{ + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + if(zone){ + if(rga->zoneid == zone->GetZoneID() && rga->instance_id == zone->GetInstanceID()) + break; + + Raid *r = entity_list.GetRaidByID(rga->rid); + if(r){ + r->LearnMembers(); + r->VerifyRaid(); + r->SendRaidAddAll(rga->playername); + } + } + break; + } + + case ServerOP_RaidRemove:{ + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + if(zone){ + if(rga->zoneid == zone->GetZoneID() && rga->instance_id == zone->GetInstanceID()) + break; + + Raid *r = entity_list.GetRaidByID(rga->rid); + if(r){ + r->SendRaidRemoveAll(rga->playername); + Client *rem = entity_list.GetClientByName(rga->playername); + if(rem){ + r->SendRaidDisband(rem); + } + r->LearnMembers(); + r->VerifyRaid(); + } + } + break; + } + + case ServerOP_RaidDisband:{ + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + if(zone){ + if(rga->zoneid == zone->GetZoneID() && rga->instance_id == zone->GetInstanceID()) + break; + + Raid *r = entity_list.GetRaidByID(rga->rid); + if(r){ + r->SendRaidDisbandAll(); + r->LearnMembers(); + r->VerifyRaid(); + } + } + break; + } + + case ServerOP_RaidLockFlag:{ + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + if(zone){ + if(rga->zoneid == zone->GetZoneID() && rga->instance_id == zone->GetInstanceID()) + break; + + Raid *r = entity_list.GetRaidByID(rga->rid); + if(r){ + r->GetRaidDetails(); //update our details + if(rga->gid) + r->SendRaidLock(); + else + r->SendRaidUnlock(); + } + } + break; + } + + case ServerOP_RaidChangeGroup:{ + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + if(zone){ + if(rga->zoneid == zone->GetZoneID() && rga->instance_id == zone->GetInstanceID()) + break; + + Raid *r = entity_list.GetRaidByID(rga->rid); + if(r){ + r->LearnMembers(); + r->VerifyRaid(); + Client *c = entity_list.GetClientByName(rga->playername); + if(c){ + r->SendRaidDisband(c); + r->SendRaidRemoveAll(rga->playername); + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->SendBulkRaid(c); + r->SendRaidAddAll(rga->playername); + if(r->IsLocked()) { r->SendRaidLockTo(c); } + } + else{ + r->SendRaidRemoveAll(rga->playername); + r->SendRaidAddAll(rga->playername); + } + } + } + break; + } + + case ServerOP_UpdateGroup:{ + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + if(zone){ + if(rga->zoneid == zone->GetZoneID() && rga->instance_id == zone->GetInstanceID()) + break; + + Raid *r = entity_list.GetRaidByID(rga->rid); + if(r){ + r->GroupUpdate(rga->gid, false); + } + } + break; + } + + case ServerOP_RaidGroupLeader:{ + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + if(zone){ + if(rga->zoneid == zone->GetZoneID() && rga->instance_id == zone->GetInstanceID()) + break; + } + break; + } + + case ServerOP_RaidLeader:{ + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + if(zone){ + if(rga->zoneid == zone->GetZoneID() && rga->instance_id == zone->GetInstanceID()) + break; + + Raid *r = entity_list.GetRaidByID(rga->rid); + if(r){ + Client *c = entity_list.GetClientByName(rga->playername); + strn0cpy(r->leadername, rga->playername, 64); + if(c){ + r->SetLeader(c); + } + r->LearnMembers(); + r->VerifyRaid(); + r->SendMakeLeaderPacket(rga->playername); + } + } + break; + } + + case ServerOP_DetailsChange:{ + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + if(zone){ + if(rga->zoneid == zone->GetZoneID() && rga->instance_id == zone->GetInstanceID()) + break; + + Raid *r = entity_list.GetRaidByID(rga->rid); + if(r){ + r->GetRaidDetails(); + r->LearnMembers(); + r->VerifyRaid(); + } + } + break; + } + + case ServerOP_RaidGroupDisband:{ + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + if(zone){ + if(rga->zoneid == zone->GetZoneID() && rga->instance_id == zone->GetInstanceID()) + break; + + Client *c = entity_list.GetClientByName(rga->playername); + if(c) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupUpdate_Struct)); + GroupUpdate_Struct* gu = (GroupUpdate_Struct*) outapp->pBuffer; + gu->action = groupActDisband; + strn0cpy(gu->leadersname, c->GetName(), 64); + strn0cpy(gu->yourname, c->GetName(), 64); + c->FastQueuePacket(&outapp); + } + } + break; + } + + case ServerOP_RaidGroupAdd:{ + ServerRaidGroupAction_Struct* rga = (ServerRaidGroupAction_Struct*)pack->pBuffer; + if(zone){ + Raid *r = entity_list.GetRaidByID(rga->rid); + if(r){ + r->LearnMembers(); + r->VerifyRaid(); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); + GroupJoin_Struct* gj = (GroupJoin_Struct*) outapp->pBuffer; + strn0cpy(gj->membername, rga->membername, 64); + gj->action = groupActJoin; + + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(r->members[x].member) + { + if(strcmp(r->members[x].member->GetName(), rga->membername) != 0){ + if((rga->gid < 12) && rga->gid == r->members[x].GroupNumber) + { + strn0cpy(gj->yourname, r->members[x].member->GetName(), 64); + r->members[x].member->QueuePacket(outapp); + } + } + } + } + safe_delete(outapp); + } + } + break; + } + + case ServerOP_RaidGroupRemove:{ + ServerRaidGroupAction_Struct* rga = (ServerRaidGroupAction_Struct*)pack->pBuffer; + if(zone){ + Raid *r = entity_list.GetRaidByID(rga->rid); + if(r){ + r->LearnMembers(); + r->VerifyRaid(); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); + GroupJoin_Struct* gj = (GroupJoin_Struct*) outapp->pBuffer; + strn0cpy(gj->membername, rga->membername, 64); + gj->action = groupActLeave; + + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(r->members[x].member) + { + if(strcmp(r->members[x].member->GetName(), rga->membername) != 0){ + if((rga->gid < 12) && rga->gid == r->members[x].GroupNumber) + { + strn0cpy(gj->yourname, r->members[x].member->GetName(), 64); + r->members[x].member->QueuePacket(outapp); + } + } + } + } + safe_delete(outapp); + } + } + break; + } + + case ServerOP_RaidGroupSay:{ + ServerRaidMessage_Struct* rmsg = (ServerRaidMessage_Struct*)pack->pBuffer; + if(zone){ + Raid *r = entity_list.GetRaidByID(rmsg->rid); + if(r) + { + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(r->members[x].member){ + if(strcmp(rmsg->from, r->members[x].member->GetName()) != 0) + { + if(r->members[x].GroupNumber == rmsg->gid){ + if(r->members[x].member->GetFilter(FILTER_GROUP)!=0) + { + r->members[x].member->ChannelMessageSend(rmsg->from, r->members[x].member->GetName(), 2, 0, rmsg->message); + } + } + } + } + } + } + } + break; + } + + case ServerOP_RaidSay:{ + ServerRaidMessage_Struct* rmsg = (ServerRaidMessage_Struct*)pack->pBuffer; + if(zone) + { + Raid *r = entity_list.GetRaidByID(rmsg->rid); + if(r) + { + for(int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if(r->members[x].member){ + if(strcmp(rmsg->from, r->members[x].member->GetName()) != 0) + { + if(r->members[x].member->GetFilter(FILTER_GROUP)!=0) + { + r->members[x].member->ChannelMessageSend(rmsg->from, r->members[x].member->GetName(), 15, 0, rmsg->message); + } + } + } + } + } + } + break; + } + + case ServerOP_SpawnPlayerCorpse: { + SpawnPlayerCorpse_Struct* s = (SpawnPlayerCorpse_Struct*)pack->pBuffer; + Corpse* NewCorpse = database.LoadPlayerCorpse(s->player_corpse_id); + if(NewCorpse) + NewCorpse->Spawn(); + else + LogFile->write(EQEMuLog::Error,"Unable to load player corpse id %u for zone %s.", s->player_corpse_id, zone->GetShortName()); + + break; + } + case ServerOP_Consent: { + ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; + Client* client = entity_list.GetClientByName(s->grantname); + if(client) { + if(s->permission == 1) + client->consent_list.push_back(s->ownername); + else + client->consent_list.remove(s->ownername); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ConsentResponse, sizeof(ConsentResponse_Struct)); + ConsentResponse_Struct* crs = (ConsentResponse_Struct*)outapp->pBuffer; + strcpy(crs->grantname, s->grantname); + strcpy(crs->ownername, s->ownername); + crs->permission = s->permission; + strcpy(crs->zonename,"all zones"); + client->QueuePacket(outapp); + safe_delete(outapp); + } + else { + // target not found + + // Message string id's likely to be used here are: + // CONSENT_YOURSELF = 399 + // CONSENT_INVALID_NAME = 397 + // TARGET_NOT_FOUND = 101 + + safe_delete(pack); + pack = new ServerPacket(ServerOP_Consent_Response, sizeof(ServerOP_Consent_Struct)); + ServerOP_Consent_Struct* scs = (ServerOP_Consent_Struct*)pack->pBuffer; + strcpy(scs->grantname, s->grantname); + strcpy(scs->ownername, s->ownername); + scs->permission = s->permission; + scs->zone_id = s->zone_id; + scs->instance_id = s->instance_id; + scs->message_string_id = TARGET_NOT_FOUND; + worldserver.SendPacket(pack); + safe_delete(pack); + } + break; + } + case ServerOP_Consent_Response: { + ServerOP_Consent_Struct* s = (ServerOP_Consent_Struct*)pack->pBuffer; + Client* client = entity_list.GetClientByName(s->ownername); + if(client) { + client->Message_StringID(0, s->message_string_id); + } + break; + } + case ServerOP_ReloadTasks: { + if(RuleB(Tasks,EnableTaskSystem)) { + HandleReloadTasks(pack); + } + break; + } + case ServerOP_LFGMatches: { + HandleLFGMatches(pack); + break; + } + case ServerOP_LFPMatches: { + HandleLFPMatches(pack); + break; + } + + case ServerOP_UpdateSpawn: { + if(zone) + { + UpdateSpawnTimer_Struct *ust = (UpdateSpawnTimer_Struct*)pack->pBuffer; + LinkedListIterator iterator(zone->spawn2_list); + iterator.Reset(); + while (iterator.MoreElements()) + { + if(iterator.GetData()->GetID() == ust->id) + { + if(!iterator.GetData()->NPCPointerValid()) + { + iterator.GetData()->SetTimer(ust->duration); + } + break; + } + iterator.Advance(); + } + } + break; + } + + case ServerOP_InstanceUpdateTime: + { + ServerInstanceUpdateTime_Struct *iut = (ServerInstanceUpdateTime_Struct*)pack->pBuffer; + if(zone) + { + if(zone->GetInstanceID() == iut->instance_id) + { + zone->SetInstanceTimer(iut->new_duration); + } + } + break; + } + + case ServerOP_DepopAllPlayersCorpses: + { + ServerDepopAllPlayersCorpses_Struct *sdapcs = (ServerDepopAllPlayersCorpses_Struct *)pack->pBuffer; + + if(zone && !((zone->GetZoneID() == sdapcs->ZoneID) && (zone->GetInstanceID() == sdapcs->InstanceID))) + entity_list.RemoveAllCorpsesByCharID(sdapcs->CharacterID); + + break; + + } + + case ServerOP_DepopPlayerCorpse: + { + ServerDepopPlayerCorpse_Struct *sdpcs = (ServerDepopPlayerCorpse_Struct *)pack->pBuffer; + + if(zone && !((zone->GetZoneID() == sdpcs->ZoneID) && (zone->GetInstanceID() == sdpcs->InstanceID))) + entity_list.RemoveCorpseByDBID(sdpcs->DBID); + + break; + + } + + case ServerOP_ReloadTitles: + { + title_manager.LoadTitles(); + break; + } + + case ServerOP_SpawnStatusChange: + { + if(zone) + { + ServerSpawnStatusChange_Struct *ssc = (ServerSpawnStatusChange_Struct*)pack->pBuffer; + LinkedListIterator iterator(zone->spawn2_list); + iterator.Reset(); + Spawn2 *found_spawn = NULL; + while(iterator.MoreElements()) + { + Spawn2* cur = iterator.GetData(); + if(cur->GetID() == ssc->id) + { + found_spawn = cur; + break; + } + iterator.Advance(); + } + + if(found_spawn) + { + if(ssc->new_status == 0) + { + found_spawn->Disable(); + } + else + { + found_spawn->Enable(); + } + } + } + break; + } + + case ServerOP_QGlobalUpdate: + { + if(pack->size != sizeof(ServerQGlobalUpdate_Struct)) + { + break; + } + + if(zone) + { + ServerQGlobalUpdate_Struct *qgu = (ServerQGlobalUpdate_Struct*)pack->pBuffer; + if(qgu->from_zone_id != zone->GetZoneID() || qgu->from_instance_id != zone->GetInstanceID()) + { + QGlobal temp; + temp.npc_id = qgu->npc_id; + temp.char_id = qgu->char_id; + temp.zone_id = qgu->zone_id; + 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); + } + } + break; + } + + case ServerOP_QGlobalDelete: + { + if(pack->size != sizeof(ServerQGlobalDelete_Struct)) + { + break; + } + + if(zone) + { + ServerQGlobalDelete_Struct *qgd = (ServerQGlobalDelete_Struct*)pack->pBuffer; + if(qgd->from_zone_id != zone->GetZoneID() || qgd->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); + } + } + break; + } + + case ServerOP_AdventureRequestAccept: + { + ServerAdventureRequestAccept_Struct *ars = (ServerAdventureRequestAccept_Struct*)pack->pBuffer; + Client *c = entity_list.GetClientByName(ars->leader); + if(c) + { + c->NewAdventure(ars->id, ars->theme, ars->text, ars->member_count, (const char*)(pack->pBuffer + sizeof(ServerAdventureRequestAccept_Struct))); + c->ClearPendingAdventureRequest(); + } + break; + } + + case ServerOP_AdventureRequestDeny: + { + ServerAdventureRequestDeny_Struct *ars = (ServerAdventureRequestDeny_Struct*)pack->pBuffer; + Client *c = entity_list.GetClientByName(ars->leader); + if(c) + { + c->SendAdventureError(ars->reason); + c->ClearPendingAdventureRequest(); + } + break; + } + + case ServerOP_AdventureCreateDeny: + { + Client *c = entity_list.GetClientByName((const char*)pack->pBuffer); + if(c) + { + c->ClearPendingAdventureData(); + c->ClearPendingAdventureCreate(); + } + break; + } + + case ServerOP_AdventureData: + { + Client *c = entity_list.GetClientByName((const char*)pack->pBuffer); + if(c) + { + c->ClearAdventureData(); + char * adv_data = new char[pack->size]; + memcpy(adv_data, pack->pBuffer, pack->size); + c->SetAdventureData(adv_data); + c->ClearPendingAdventureData(); + c->ClearPendingAdventureCreate(); + c->SendAdventureDetails(); + } + break; + } + + case ServerOP_AdventureDataClear: + { + Client *c = entity_list.GetClientByName((const char*)pack->pBuffer); + if(c) + { + if(c->HasAdventureData()) + { + c->ClearAdventureData(); + c->SendAdventureError("You are not currently assigned to an adventure."); + } + } + break; + } + + case ServerOP_AdventureClickDoorReply: + { + ServerPlayerClickedAdventureDoorReply_Struct *adr = (ServerPlayerClickedAdventureDoorReply_Struct*)pack->pBuffer; + Client *c = entity_list.GetClientByName(adr->player); + if(c) + { + c->ClearPendingAdventureDoorClick(); + c->MovePC(adr->zone_id, adr->instance_id, adr->x, adr->y, adr->z, adr->h, 0); + } + break; + } + + case ServerOP_AdventureClickDoorError: + { + Client *c = entity_list.GetClientByName((const char*)pack->pBuffer); + if(c) + { + c->ClearPendingAdventureDoorClick(); + c->Message_StringID(13, 5141); + } + break; + } + + case ServerOP_AdventureLeaveReply: + { + Client *c = entity_list.GetClientByName((const char*)pack->pBuffer); + if(c) + { + c->ClearPendingAdventureLeave(); + c->ClearCurrentAdventure(); + } + break; + } + + case ServerOP_AdventureLeaveDeny: + { + Client *c = entity_list.GetClientByName((const char*)pack->pBuffer); + if(c) + { + c->ClearPendingAdventureLeave(); + c->Message(13, "You cannot leave this adventure at this time."); + } + break; + } + + case ServerOP_AdventureCountUpdate: + { + ServerAdventureCountUpdate_Struct *ac = (ServerAdventureCountUpdate_Struct*)pack->pBuffer; + Client *c = entity_list.GetClientByName(ac->player); + if(c) + { + c->SendAdventureCount(ac->count, ac->total); + } + break; + } + + case ServerOP_AdventureZoneData: + { + if(zone) + { + safe_delete(zone->adv_data); + zone->adv_data = new char[pack->size]; + memcpy(zone->adv_data, pack->pBuffer, pack->size); + ServerZoneAdventureDataReply_Struct* ds = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; + } + break; + } + + case ServerOP_AdventureFinish: + { + ServerAdventureFinish_Struct *af = (ServerAdventureFinish_Struct*)pack->pBuffer; + Client *c = entity_list.GetClientByName(af->player); + if(c) + { + c->AdventureFinish(af->win, af->theme, af->points); + } + break; + } + + case ServerOP_AdventureLeaderboard: + { + Client *c = entity_list.GetClientByName((const char*)pack->pBuffer); + if(c) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureLeaderboardReply, sizeof(AdventureLeaderboard_Struct)); + memcpy(outapp->pBuffer, pack->pBuffer+64, sizeof(AdventureLeaderboard_Struct)); + c->FastQueuePacket(&outapp); + } + break; + } + case ServerOP_ReloadRules: + { + rules->LoadRules(&database, rules->GetActiveRuleset()); + break; + } + case ServerOP_CameraShake: + { + if(zone) + { + ServerCameraShake_Struct *scss = (ServerCameraShake_Struct*)pack->pBuffer; + entity_list.CameraEffect(scss->duration, scss->intensity); + } + break; + } + case ServerOP_QueryServGeneric: + { + pack->SetReadPosition(8); + char From[64]; + pack->ReadString(From); + + Client *c = entity_list.GetClientByName(From); + + if(!c) + return; + + uint32 Type = pack->ReadUInt32();; + + switch(Type) + { + case QSG_LFGuild: + { + c->HandleLFGuildResponse(pack); + break; + } + + default: + break; + } + + break; + } + case ServerOP_CZSignalClient: + { + CZClientSignal_Struct* CZCS = (CZClientSignal_Struct*) pack->pBuffer; + Client* client = entity_list.GetClientByCharID(CZCS->charid); + if (client != 0) { + client->Signal(CZCS->data); + } + break; + } + case ServerOP_CZSignalClientByName: + { + CZClientSignalByName_Struct* CZCS = (CZClientSignalByName_Struct*) pack->pBuffer; + Client* client = entity_list.GetClientByName(CZCS->Name); + if (client != 0) { + client->Signal(CZCS->data); + } + break; + } + case ServerOP_CZMessagePlayer: + { + CZMessagePlayer_Struct* CZCS = (CZMessagePlayer_Struct*) pack->pBuffer; + Client* client = entity_list.GetClientByName(CZCS->CharName); + if (client != 0) { + client->Message(CZCS->Type, CZCS->Message); + } + break; + } + case ServerOP_ReloadWorld: + { + ReloadWorld_Struct* RW = (ReloadWorld_Struct*) pack->pBuffer; + if(zone){ + zone->ReloadWorld(RW->Option); + } + break; + } + default: { + cout << " Unknown ZSopcode:" << (int)pack->opcode; + cout << " size:" << pack->size << endl; + //DumpPacket(pack->pBuffer, pack->size); + break; + } + } + safe_delete(pack); + } + + return; +} + +bool WorldServer::SendChannelMessage(Client* from, const char* to, uint8 chan_num, uint32 guilddbid, uint8 language, const char* message, ...) { + if(!worldserver.Connected()) + return false; + va_list argptr; + char buffer[512]; + + va_start(argptr, message); + vsnprintf(buffer, 512, message, argptr); + va_end(argptr); + buffer[511] = '\0'; + + ServerPacket* pack = new ServerPacket(ServerOP_ChannelMessage, sizeof(ServerChannelMessage_Struct) + strlen(buffer) + 1); + ServerChannelMessage_Struct* scm = (ServerChannelMessage_Struct*) pack->pBuffer; + + if (from == 0) { + strcpy(scm->from, "ZServer"); + scm->fromadmin = 0; + } else { + strcpy(scm->from, from->GetName()); + scm->fromadmin = from->Admin(); + } + if (to == 0) { + scm->to[0] = 0; + scm->deliverto[0] = '\0'; + } else { + strn0cpy(scm->to, to, sizeof(scm->to)); + strn0cpy(scm->deliverto, to, sizeof(scm->deliverto)); + } + scm->noreply = false; + scm->chan_num = chan_num; + scm->guilddbid = guilddbid; + scm->language = language; + strcpy(scm->message, buffer); + + pack->Deflate(); + bool ret = SendPacket(pack); + safe_delete(pack); + return ret; +} + +bool WorldServer::SendEmoteMessage(const char* to, uint32 to_guilddbid, uint32 type, const char* message, ...) { + va_list argptr; + char buffer[256]; + + va_start(argptr, message); + vsnprintf(buffer, 256, message, argptr); + va_end(argptr); + + return SendEmoteMessage(to, to_guilddbid, 0, type, buffer); +} + +bool WorldServer::SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...) { + va_list argptr; + char buffer[256]; + + va_start(argptr, message); + vsnprintf(buffer, 256, message, argptr); + va_end(argptr); + + if (!Connected() && to == 0) { + entity_list.MessageStatus(to_guilddbid, to_minstatus, type, buffer); + return false; + } + + ServerPacket* pack = new ServerPacket(ServerOP_EmoteMessage, sizeof(ServerEmoteMessage_Struct)+strlen(buffer)+1); + ServerEmoteMessage_Struct* sem = (ServerEmoteMessage_Struct*) pack->pBuffer; + sem->type = type; + if (to != 0) + strcpy(sem->to, to); + sem->guilddbid = to_guilddbid; + sem->minstatus = to_minstatus; + strcpy(sem->message, buffer); + + pack->Deflate(); + bool ret = SendPacket(pack); + safe_delete(pack); + return ret; +} + +bool WorldServer::SendVoiceMacro(Client* From, uint32 Type, char* Target, uint32 MacroNumber, uint32 GroupOrRaidID) { + + if(!worldserver.Connected() || !From) + return false; + + ServerPacket* pack = new ServerPacket(ServerOP_VoiceMacro, sizeof(ServerVoiceMacro_Struct)); + + ServerVoiceMacro_Struct* svm = (ServerVoiceMacro_Struct*) pack->pBuffer; + + strcpy(svm->From, From->GetName()); + + switch(Type) { + + case VoiceMacroTell: + strcpy(svm->To, Target); + break; + + case VoiceMacroGroup: + svm->GroupID = GroupOrRaidID; + break; + + case VoiceMacroRaid: + svm->RaidID = GroupOrRaidID; + break; + } + + svm->Type = Type; + + svm->Voice = (GetArrayRace(From->GetRace()) * 2) + From->GetGender(); + + svm->MacroNumber = MacroNumber; + + pack->Deflate(); + + bool Ret = SendPacket(pack); + + safe_delete(pack); + + return Ret; +} + +bool WorldServer::RezzPlayer(EQApplicationPacket* rpack, uint32 rezzexp, uint32 dbid, uint16 opcode) +{ + _log(SPELLS__REZ, "WorldServer::RezzPlayer rezzexp is %i (0 is normal for RezzComplete", rezzexp); + ServerPacket* pack = new ServerPacket(ServerOP_RezzPlayer, sizeof(RezzPlayer_Struct)); + RezzPlayer_Struct* sem = (RezzPlayer_Struct*) pack->pBuffer; + sem->rezzopcode = opcode; + sem->rez = *(Resurrect_Struct*) rpack->pBuffer; + sem->exp = rezzexp; + sem->dbid = dbid; + bool ret = SendPacket(pack); + if (ret) + _log(SPELLS__REZ, "Sending player rezz packet to world spellid:%i", sem->rez.spellid); + else + _log(SPELLS__REZ, "NOT Sending player rezz packet to world"); + + safe_delete(pack); + return ret; +} + +void WorldServer::SendReloadTasks(int Command, int TaskID) { + ServerPacket* pack = new ServerPacket(ServerOP_ReloadTasks, sizeof(ReloadTasks_Struct)); + ReloadTasks_Struct* rts = (ReloadTasks_Struct*) pack->pBuffer; + + rts->Command = Command; + rts->Parameter = TaskID; + + SendPacket(pack); +} + +void WorldServer::HandleReloadTasks(ServerPacket *pack) +{ + ReloadTasks_Struct* rts = (ReloadTasks_Struct*) pack->pBuffer; + + _log(TASKS__GLOBALLOAD, "Zone received ServerOP_ReloadTasks from World, Command %i", rts->Command); + + switch(rts->Command) { + case RELOADTASKS: + entity_list.SaveAllClientsTaskState(); + + if(rts->Parameter == 0) { + _log(TASKS__GLOBALLOAD, "Reload ALL tasks"); + safe_delete(taskmanager); + taskmanager = new TaskManager; + taskmanager->LoadTasks(); + if(zone) + taskmanager->LoadProximities(zone->GetZoneID()); + entity_list.ReloadAllClientsTaskState(); + } + else { + _log(TASKS__GLOBALLOAD, "Reload only task %i", rts->Parameter); + taskmanager->LoadTasks(rts->Parameter); + entity_list.ReloadAllClientsTaskState(rts->Parameter); + } + + break; + + case RELOADTASKPROXIMITIES: + if(zone) { + _log(TASKS__GLOBALLOAD, "Reload task proximities"); + taskmanager->LoadProximities(zone->GetZoneID()); + } + break; + + case RELOADTASKGOALLISTS: + _log(TASKS__GLOBALLOAD, "Reload task goal lists"); + taskmanager->ReloadGoalLists(); + break; + + case RELOADTASKSETS: + _log(TASKS__GLOBALLOAD, "Reload task sets"); + taskmanager->LoadTaskSets(); + break; + + default: + _log(TASKS__GLOBALLOAD, "Unhandled ServerOP_ReloadTasks command %i", rts->Command); + + } + + +} + + +uint32 WorldServer::NextGroupID() { + //this system wastes a lot of potential group IDs (~5%), but + //if you are creating 2 billion groups in 1 run of the emu, + //something else is wrong... + if(cur_groupid >= last_groupid) { + //this is an error... This means that 50 groups were created before + //1 packet could make the zone->world->zone trip... so let it error. + _log(ZONE__WORLD_ERR, "Ran out of group IDs before the server sent us more."); + return(0); + } + if(cur_groupid > (last_groupid - /*50*/995)) { + //running low, request more + ServerPacket* pack = new ServerPacket(ServerOP_GroupIDReq); + SendPacket(pack); + safe_delete(pack); + } + printf("Handing out new group id %d\n", cur_groupid); + return(cur_groupid++); +} + +void WorldServer::UpdateLFP(uint32 LeaderID, uint8 Action, uint8 MatchFilter, uint32 FromLevel, uint32 ToLevel, uint32 Classes, + const char *Comments, GroupLFPMemberEntry *LFPMembers) { + + ServerPacket* pack = new ServerPacket(ServerOP_LFPUpdate, sizeof(ServerLFPUpdate_Struct)); + ServerLFPUpdate_Struct* sus = (ServerLFPUpdate_Struct*) pack->pBuffer; + + sus->LeaderID = LeaderID; + sus->Action = Action; + sus->MatchFilter = MatchFilter; + sus->FromLevel = FromLevel; + sus->ToLevel = ToLevel; + sus->Classes = Classes; + strcpy(sus->Comments, Comments); + memcpy(sus->Members, LFPMembers, sizeof(sus->Members)); + SendPacket(pack); + safe_delete(pack); + +} + +void WorldServer::UpdateLFP(uint32 LeaderID, GroupLFPMemberEntry *LFPMembers) { + + UpdateLFP(LeaderID, LFPMemberUpdate, 0, 0, 0, 0, "", LFPMembers); +} + +void WorldServer::StopLFP(uint32 LeaderID) { + + GroupLFPMemberEntry LFPMembers; + UpdateLFP(LeaderID, LFPOff, 0, 0, 0, 0, "", &LFPMembers); +} + +void WorldServer::HandleLFGMatches(ServerPacket *pack) { + + char *Buffer = (char *)pack->pBuffer; + + int PacketLength = 4; + + int Entries = (pack->size - 4) / sizeof(ServerLFGMatchesResponse_Struct); + + uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + + Client* client = entity_list.GetClientByID(EntityID); + + if(client) { + ServerLFGMatchesResponse_Struct* smrs = (ServerLFGMatchesResponse_Struct*)Buffer; + + for(int i=0; iName) + strlen(smrs->Comments); + smrs++; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_LFGGetMatchesResponse, PacketLength); + + smrs = (ServerLFGMatchesResponse_Struct*)Buffer; + + char *OutBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0x00074af); // Unknown + + for(int i=0; iComments); + VARSTRUCT_ENCODE_STRING(OutBuffer, smrs->Name); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, smrs->Class_); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, smrs->Level); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, smrs->Zone); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, smrs->GuildID); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, smrs->Anon); + smrs++; + } + client->QueuePacket(outapp); + safe_delete(outapp); + } +} + +void WorldServer::HandleLFPMatches(ServerPacket *pack) { + + char *Buffer = (char *)pack->pBuffer; + + int PacketLength = 4; + + int Entries = (pack->size - 4) / sizeof(ServerLFPMatchesResponse_Struct); + + uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + + ServerLFPMatchesResponse_Struct* smrs = (ServerLFPMatchesResponse_Struct*)Buffer; + + Client* client = entity_list.GetClientByID(EntityID); + + if(client) { + for(int i=0; iComments) + 11; + for(unsigned int j=0; jMembers[j].Name[0] != '\0') + PacketLength += strlen(smrs->Members[j].Name) + 9; + } + smrs++; + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_LFPGetMatchesResponse, PacketLength); + + smrs = (ServerLFPMatchesResponse_Struct*)Buffer; + + char *OutBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0x00074af); // Unknown + + for(int i=0; iMembers[j].Name[0] != '\0') + MemberCount++; + + VARSTRUCT_ENCODE_STRING(OutBuffer, smrs->Comments); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, smrs->FromLevel); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, smrs->ToLevel); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, smrs->Classes); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, MemberCount); + + for(unsigned int j=0; jMembers[j].Name[0] != '\0') { + VARSTRUCT_ENCODE_STRING(OutBuffer, smrs->Members[j].Name); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, smrs->Members[j].Class); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, smrs->Members[j].Level); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, smrs->Members[j].Zone); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, smrs->Members[j].GuildID); + } + } + smrs++; + } + client->QueuePacket(outapp); + safe_delete(outapp); + } +} diff --git a/zone/worldserver.h b/zone/worldserver.h new file mode 100644 index 000000000..ab332bf74 --- /dev/null +++ b/zone/worldserver.h @@ -0,0 +1,72 @@ +/* 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 WORLDSERVER_H +#define WORLDSERVER_H + +#include "../common/worldconn.h" +#include "../common/eq_packet_structs.h" +#include + +struct GuildJoin_Struct; +class EQApplicationPacket; +class Client; +class Database; + +class WorldServer : public WorldConnection { +public: + WorldServer(); + virtual ~WorldServer(); + + virtual void Process(); + + void SendGuildJoin(GuildJoin_Struct* gj); + bool SendChannelMessage(Client* from, const char* to, uint8 chan_num, uint32 guilddbid, uint8 language, const char* message, ...); + bool SendEmoteMessage(const char* to, uint32 to_guilddbid, uint32 type, const char* message, ...); + bool SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...); + bool SendVoiceMacro(Client* From, uint32 Type, char* Target, uint32 MacroNumber, uint32 GroupOrRaidID = 0); + void SetZone(uint32 iZoneID, uint32 iInstanceID = 0); + uint32 SendGroupIdRequest(); + bool RezzPlayer(EQApplicationPacket* rpack, uint32 rezzexp, uint32 dbid, uint16 opcode); + bool IsOOCMuted() const { return(oocmuted); } + + uint32 NextGroupID(); + + void SetLaunchedName(const char *n) { m_launchedName = n; } + void SetLauncherName(const char *n) { m_launcherName = n; } + void SendReloadTasks(int Command, int TaskID=0); + void HandleReloadTasks(ServerPacket *pack); + void UpdateLFP(uint32 LeaderID, uint8 Action, uint8 MatchFilter, uint32 FromLevel, uint32 ToLevel, uint32 Classes, const char *Comments, + GroupLFPMemberEntry *LFPMembers); + void UpdateLFP(uint32 LeaderID, GroupLFPMemberEntry *LFPMembers); + void StopLFP(uint32 LeaderID); + void HandleLFGMatches(ServerPacket *pack); + void HandleLFPMatches(ServerPacket *pack); + +private: + virtual void OnConnected(); + + std::string m_launchedName; + std::string m_launcherName; + + bool oocmuted; + + uint32 cur_groupid; + uint32 last_groupid; +}; +#endif + diff --git a/zone/zone.cpp b/zone/zone.cpp new file mode 100644 index 000000000..e6f63bb6b --- /dev/null +++ b/zone/zone.cpp @@ -0,0 +1,2651 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2003 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 +using namespace std; +#include +#include +#include +#include +#include +#include + +#ifdef _WINDOWS +#include +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#else +#include +#include "../common/unix.h" +#endif + +#include "masterentity.h" +#include "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/MiscFunctions.h" +#include "ZoneConfig.h" +#include "../common/breakdowns.h" +#include "map.h" +#include "watermap.h" +#include "object.h" +#include "petitions.h" +#include "pathing.h" +#include "parser.h" +#include "event_codes.h" +#include "client_logs.h" +#include "../common/rulesys.h" +#include "guild_mgr.h" +#include "QuestParserCollection.h" + +#ifdef _WINDOWS +#define snprintf _snprintf +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + + +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; +void CleanupLoadZoneState(uint32 spawn2_count, ZSDump_Spawn2** spawn2_dump, ZSDump_NPC** npc_dump, ZSDump_NPC_Loot** npcloot_dump, NPCType** gmspawntype_dump, Spawn2*** spawn2_loaded, NPC*** npc_loaded, MYSQL_RES** result); + + + +bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { + _ZP(Zone_Bootup); + const char* zonename = database.GetZoneName(iZoneID); + + if (iZoneID == 0 || zonename == 0) + return false; + if (zone != 0 || ZoneLoaded) { + cerr << "Error: Zone::Bootup call when zone already booted!" << endl; + worldserver.SetZone(0); + return false; + } + + LogFile->write(EQEMuLog::Status, "Booting %s (%d:%d)", zonename, iZoneID, iInstanceID); + + numclients = 0; + zone = new Zone(iZoneID, iInstanceID, zonename); + + //init the zone, loads all the data, etc + if (!zone->Init(iStaticZone)) { + safe_delete(zone); + cerr << "Zone->Init failed" << endl; + worldserver.SetZone(0); + return false; + } + zone->zonemap = Map::LoadMapfile(zone->map_name); + zone->watermap = WaterMap::LoadWaterMapfile(zone->map_name); + zone->pathing = PathManager::LoadPathFile(zone->map_name); + + char tmp[10]; + //PlayerProfile_Struct* pp; + //int char_num = 0; + //unsigned long* lengths; + if (database.GetVariable("loglevel",tmp, 9)) { + int blah[4]; + if (atoi(tmp)>9){ //Server is using the new code + for(int i=0;i<4;i++){ + if (((int)tmp[i]>=48) && ((int)tmp[i]<=57)) + blah[i]=(int)tmp[i]-48; //get the value to convert it to an int from the ascii value + else + blah[i]=0; //set to zero on a bogue char + } + zone->loglevelvar = blah[0]; + LogFile->write(EQEMuLog::Status, "General logging level: %i", zone->loglevelvar); + zone->merchantvar = blah[1]; + LogFile->write(EQEMuLog::Status, "Merchant logging level: %i", zone->merchantvar); + zone->tradevar = blah[2]; + LogFile->write(EQEMuLog::Status, "Trade logging level: %i", zone->tradevar); + zone->lootvar = blah[3]; + LogFile->write(EQEMuLog::Status, "Loot logging level: %i", zone->lootvar); + } + else { + zone->loglevelvar = uint8(atoi(tmp)); //continue supporting only command logging (for now) + zone->merchantvar = 0; + zone->tradevar = 0; + zone->lootvar = 0; + } + } + + zone->weather_type = database.GetZoneWeather(iZoneID, zone->GetInstanceVersion()); + + LogFile->write(EQEMuLog::Debug, "Zone: %s has weather of type %i.", zonename, zone->weather_type); + + if(zone->weather_type > 3 || zone->weather_type == 0) { + zone->zone_weather = 0; + zone->Weather_Timer->Disable(); + LogFile->write(EQEMuLog::Debug, "Zone: %s(%i) has no weather type. The weather timer has been disabled.", zonename, iZoneID); + } + else { + zone->zone_weather = 0; + LogFile->write(EQEMuLog::Debug, "Zone: %s(%i) has weather type = %i. The weather timer has been enabled.", zonename, iZoneID, zone->weather_type); + } + + ZoneLoaded = true; + + worldserver.SetZone(iZoneID, iInstanceID); + if(iInstanceID != 0) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureZoneData, sizeof(uint16)); + *((uint16*)pack->pBuffer) = iInstanceID; + worldserver.SendPacket(pack); + delete pack; + } + + LogFile->write(EQEMuLog::Normal, "---- Zone server %s, listening on port:%i ----", zonename, ZoneConfig::get()->ZonePort); + LogFile->write(EQEMuLog::Status, "Zone Bootup: %s (%i: %i)", zonename, iZoneID, iInstanceID); + UpdateWindowTitle(); + zone->GetTimeSync(); + + return true; +} + +//this really loads the objects into entity_list +bool Zone::LoadZoneObjects() { + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = NULL; + 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); + + 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 + + if (shortname) + { + Door d; + memset(&d, 0, sizeof(d)); + + 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 + + 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'; + } + + memcpy(d.dest_zone, "NONE", 5); + + if ((d.size = atoi(row[11])) == 0) // unknown08 = optional size percentage + { + d.size = 100; + } + + 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; + } + + d.incline = atoi(row[13]); // unknown20 = optional model incline value + d.client_version_mask = 0xFFFFFFFF; //We should load the mask from the zone. + + Doors* door = new Doors(&d); + entity_list.AddDoor(door); + } + + continue; + } + Object_Struct data = {0}; + uint32 id = 0; + uint32 icon = 0; + uint32 type = 0; + uint32 itemid = 0; + uint32 idx = 0; + int16 charges = 0; + + id = (uint32)atoi(row[idx++]); + data.zone_id = atoi(row[idx++]); + data.x = atof(row[idx++]); + data.y = atof(row[idx++]); + data.z = atof(row[idx++]); + data.heading = atof(row[idx++]); + itemid = (uint32)atoi(row[idx++]); + charges = (int16)atoi(row[idx++]); + strcpy(data.object_name, row[idx++]); + type = (uint8)atoi(row[idx++]); + icon = (uint32)atoi(row[idx++]); + data.object_type = (uint32)atoi(row[idx++]); + data.linked_list_addr[0] = 0; + data.linked_list_addr[1] = 0; + data.unknown008[0] = (uint32)atoi(row[idx++]); + data.unknown008[1] = (uint32)atoi(row[idx++]); + data.unknown020 = (uint32)atoi(row[idx++]); + data.unknown024 = (uint32)atoi(row[idx++]); + data.unknown076 = (uint32)atoi(row[idx++]); + data.unknown084 = 0; + + ItemInst* inst = NULL; + //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(ItemUseWorldContainer); + } + else { + // Groundspawn object + inst = database.CreateItem(itemid); + } + + //Father Nitwit's fix... not perfect... + if(inst == NULL && type != OT_DROPPEDITEM) { + inst = new ItemInst(ItemUseWorldContainer); + } + + // Load child objects if container + if (inst && inst->IsType(ItemClassContainer)) { + database.LoadWorldContainer(id, inst); + } + + 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); + } + mysql_free_result(result); + } + else { + safe_delete_array(query); + LogFile->write(EQEMuLog::Error, "Error Loading Objects from DB: %s",errbuf); + return(false); + } + return(true); +} + +//this also just loads into entity_list, not really into zone +bool Zone::LoadGroundSpawns() { + Ground_Spawns groundspawn; + + memset(&groundspawn, 0, sizeof(groundspawn)); + int gsindex=0; + LogFile->write(EQEMuLog::Status, "Loading Ground Spawns from DB..."); + database.LoadGroundSpawns(zoneid, GetInstanceVersion(), &groundspawn); + uint32 ix=0; + char* name=0; + uint32 gsnumber=0; + for(gsindex=0;gsindex<50;gsindex++){ + if(groundspawn.spawn[gsindex].item>0 && groundspawn.spawn[gsindex].item<500000){ + ItemInst* inst = NULL; + inst = database.CreateItem(groundspawn.spawn[gsindex].item); + gsnumber=groundspawn.spawn[gsindex].max_allowed; + ix=0; + if(inst){ + name = groundspawn.spawn[gsindex].name; + for(ix=0;ix 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) + return 0; + + // Account for merchant lists with gaps in them. + 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){ + freeslot = i; + for(tmp_itr = tmp_merlist.begin();tmp_itr != tmp_merlist.end();tmp_itr++){ + ml = *tmp_itr; + if(ml.item == item){ + update_charges = true; + freeslot = 0; + break; + } + if((ml.slot == i) || (ml.origslot==i)) { + freeslot=0; + } + } + i++; + } + 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++){ + TempMerchantList ml2 = *tmp_itr; + if(ml2.item != item) + tmp_merlist.push_back(ml2); + } + if(sold) + ml.charges = ml.charges + charges; + else + ml.charges = charges; + if(!ml.origslot) + ml.origslot = ml.slot; + if(charges>0){ + database.SaveMerchantTemp(npcid, ml.origslot, item, ml.charges); + tmp_merlist.push_back(ml); + } + else{ + database.DeleteMerchantTemp(npcid,ml.origslot); + } + tmpmerchanttable[npcid] = tmp_merlist; + + if(sold) + return ml.slot; + + } + if(freeslot){ + if(charges<0) //sanity check only, shouldnt happen + charges = 0x7FFF; + database.SaveMerchantTemp(npcid, freeslot, item, charges); + tmp_merlist = tmpmerchanttable[npcid]; + TempMerchantList ml2; + ml2.charges = charges; + ml2.item = item; + ml2.npcid = npcid; + ml2.slot = freeslot; + ml2.origslot = ml2.slot; + tmp_merlist.push_back(ml2); + tmpmerchanttable[npcid] = tmp_merlist; + } + return freeslot; +} + +uint32 Zone::GetTempMerchantQuantity(uint32 NPCID, uint32 Slot) { + + std::list TmpMerchantList = tmpmerchanttable[NPCID]; + std::list::const_iterator Iterator; + + for(Iterator = TmpMerchantList.begin(); Iterator != TmpMerchantList.end(); Iterator++) + if((*Iterator).slot == Slot) + return (*Iterator).charges; + + return 0; +} + +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"); + return; + } +/* char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + std::list merlist; + if (database.RunQuery(query, MakeAnyLenString(&query, "select ml.npcid,ml.slot,ml.itemid,ml.charges from merchantlist_temp ml, npc_types nt, spawnentry se, spawn2 s2 where nt.id=ml.npcid and nt.id=se.npcid and se.spawngroupid=s2.spawngroupid and s2.zone='%s' group by ml.npcid,slot order by npcid,slot asc", GetShortName()), errbuf, &result)) { + uint32 npcid = 0; + while((row = mysql_fetch_row(result))) { + if(npcid != atoul(row[0])){ + if(npcid > 0) + tmpmerchanttable[npcid] = merlist; + npcid = atoul(row[0]); + merlist.clear(); + } + TempMerchantList ml; + ml.npcid = npcid; + ml.slot = atoul(row[1]); + ml.item = atoul(row[2]); + ml.charges = atoul(row[3]); + ml.origslot = ml.slot; + merlist.push_back(ml); + } + if(npcid > 0) + tmpmerchanttable[npcid] = merlist; + mysql_free_result(result); + } + else + cerr << "Error in LoadTempMerchantData query '" << query << "' " << errbuf << endl; + safe_delete_array(query); +*/ +} + +void Zone::LoadTempMerchantData_result(MYSQL_RES* result) { + MYSQL_ROW row; + std::map >::iterator cur; + uint32 npcid = 0; + while((row = mysql_fetch_row(result))) { + TempMerchantList ml; + ml.npcid = atoul(row[0]); + if(npcid != ml.npcid){ + cur = tmpmerchanttable.find(ml.npcid); + if(cur == tmpmerchanttable.end()) { + std::list empty; + tmpmerchanttable[ml.npcid] = empty; + cur = tmpmerchanttable.find(ml.npcid); + } + npcid = ml.npcid; + } + ml.slot = atoul(row[1]); + ml.item = atoul(row[2]); + ml.charges = atoul(row[3]); + ml.origslot = ml.slot; + cur->second.push_back(ml); + } + //mysql_free_result(result); + //LogFile->write(EQEMuLog::Status, "Finished Loading Temporary Merchant Lists..."); +} + +//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; + std::list merlist; + if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT item, slot, faction_required, level_required, alt_currency_cost 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]); + 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); +} + +void Zone::LoadMerchantData_result(MYSQL_RES* result) { + MYSQL_ROW row; + std::map >::iterator cur; + uint32 npcid = 0; + while((row = mysql_fetch_row(result))) { + MerchantList ml; + ml.id = atoul(row[0]); + if(npcid != ml.id){ + cur = merchanttable.find(ml.id); + if(cur == merchanttable.end()) { + std::list empty; + merchanttable[ml.id] = empty; + cur = merchanttable.find(ml.id); + } + npcid = ml.id; + } + + std::list::iterator iter = cur->second.begin(); + bool found = false; + while(iter != cur->second.end()) { + if((*iter).item == ml.id) { + found = true; + break; + } + iter++; + } + + if(found) { + continue; + } + + ml.slot = atoul(row[1]); + ml.item = atoul(row[2]); + ml.faction_required = atoul(row[3]); + ml.level_required = atoul(row[4]); + ml.alt_currency_cost = atoul(row[5]); + 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 " + "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; + } +/* char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + std::list merlist; + if (database.RunQuery(query, MakeAnyLenString(&query, "select ml.merchantid,ml.slot,ml.item 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' group by ml.merchantid,slot order by merchantid,slot asc", GetShortName()), errbuf, &result)) { + uint32 npcid = 0; + while((row = mysql_fetch_row(result))) { + if(npcid != atoul(row[0])){ + if(npcid > 0) + merchanttable[npcid] = merlist; + npcid = atoul(row[0]); + merlist.clear(); + } + MerchantList ml; + ml.id = npcid; + ml.slot = atoul(row[1]); + ml.item = atoul(row[2]); + merlist.push_back(ml); + } + if(npcid > 0) + merchanttable[npcid] = merlist; + mysql_free_result(result); + } + else + cerr << "Error in GetMerchantDataForZoneLoad query '" << query << "' " << errbuf << endl; + safe_delete_array(query); +*/ +} + +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); + } + else { + while(DataRow = mysql_fetch_row(DatasetResult)) { + MercStanceInfo tempMercStanceInfo; + + tempMercStanceInfo.ClassID = atoi(DataRow[0]); + tempMercStanceInfo.ProficiencyID = atoi(DataRow[1]); + tempMercStanceInfo.StanceID = atoi(DataRow[2]); + tempMercStanceInfo.IsDefault = atoi(DataRow[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, 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)) { + 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.CostFormula = atoi(DataRow[6]); + tempMercTemplate.ClientVersion = atoi(DataRow[7]); + tempMercTemplate.MercNPCID = atoi(DataRow[8]); + + 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)); + } + } + + merc_templates[tempMercTemplate.MercTemplateID] = tempMercTemplate; + } + + mysql_free_result(DatasetResult); + } + + safe_delete(Query); + Query = 0; + + 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(); + + 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); + } + + safe_delete(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(); + + 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; + + 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]); + + 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()); + } + + safe_delete(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 == NULL) { + LogFile->write(EQEMuLog::Error, "NULL 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 == NULL) { + LogFile->write(EQEMuLog::Error, "NULL 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; + } + } +} + +void Zone::Shutdown(bool quite) +{ + if (!ZoneLoaded) + return; + + std::map::iterator itr; + while(zone->npctable.size()) { + itr=zone->npctable.begin(); + delete itr->second; + zone->npctable.erase(itr); + } + + while(zone->merctable.size()) { + itr=zone->merctable.begin(); + delete itr->second; + zone->merctable.erase(itr); + } + + zone->adventure_entry_list_flavor.clear(); + + std::map::iterator itr4; + while(zone->ldon_trap_list.size()) + { + itr4 = zone->ldon_trap_list.begin(); + delete itr4->second; + zone->ldon_trap_list.erase(itr4); + } + zone->ldon_trap_entry_list.clear(); + + LogFile->write(EQEMuLog::Status, "Zone Shutdown: %s (%i)", zone->GetShortName(), zone->GetZoneID()); + petition_list.ClearPetitions(); + zone->GotCurTime(false); + if (!quite) + LogFile->write(EQEMuLog::Normal, "Zone shutdown: going to sleep"); + ZoneLoaded = false; + char pzs[3] = ""; + if (database.GetVariable("PersistentZoneState", pzs, 2)) { + if (pzs[0] == '1') { + Sleep(100); + LogFile->write(EQEMuLog::Normal, "Saving zone state"); + database.DumpZoneState(); + } + } + zone->ResetAuth(); + safe_delete(zone); + dbasync->CommitWrites(); + if(parse) { parse->ReloadQuests(true); } + UpdateWindowTitle(); +} + +void Zone::LoadZoneDoors(const char* zone, int16 version) +{ + LogFile->write(EQEMuLog::Status, "Loading doors for %s ...", zone); + + uint32 maxid; + int32 count = database.GetDoorsCount(&maxid, zone, version); + if(count < 1) { + LogFile->write(EQEMuLog::Status, "... No doors loaded."); + return; + } + + Door *dlist = new Door[count]; + + if(!database.LoadDoors(count, dlist, zone, version)) { + LogFile->write(EQEMuLog::Error, "... Failed to load doors."); + delete[] dlist; + return; + } + + int r; + Door *d = dlist; + for(r = 0; r < count; r++, d++) { + Doors* newdoor = new Doors(d); + entity_list.AddDoor(newdoor); + } + delete[] dlist; +} + +Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) +: initgrids_timer(10000), + autoshutdown_timer((RuleI(Zone, AutoShutdownDelay))), + clientauth_timer(AUTHENTICATION_TIMEOUT * 1000), + spawn2_timer(1000), + qglobal_purge_timer(30000) +{ + zoneid = in_zoneid; + instanceid = in_instanceid; + instanceversion = database.GetInstanceVersion(instanceid); + zonemap = NULL; + watermap = NULL; + pathing = NULL; + qGlobals = NULL; + default_ruleset = 0; + + if(RuleB(TaskSystem, EnableTaskSystem)) { + taskmanager->LoadProximities(zoneid); + } + + short_name = strcpy(new char[strlen(in_short_name)+1], in_short_name); + strlwr(short_name); + memset(file_name, 0, sizeof(file_name)); + long_name = 0; + aggroedmobs =0; + + psafe_x = 0; + psafe_y = 0; + psafe_z = 0; + pgraveyard_id = 0; + pgraveyard_zoneid = 0; + pgraveyard_x = 0; + pgraveyard_y = 0; + pgraveyard_z = 0; + pgraveyard_heading = 0; + pMaxClients = 0; + pQueuedMerchantsWorkID = 0; + pvpzone = false; + if(database.GetServerType() == 1) + pvpzone = true; + database.GetZoneLongName(short_name, &long_name, file_name, &psafe_x, &psafe_y, &psafe_z, &pgraveyard_id, &pMaxClients); + if(graveyard_id() > 0) + { + LogFile->write(EQEMuLog::Debug, "Graveyard ID is %i.", graveyard_id()); + bool GraveYardLoaded = database.GetZoneGraveyard(graveyard_id(), &pgraveyard_zoneid, &pgraveyard_x, &pgraveyard_y, &pgraveyard_z, &pgraveyard_heading); + if(GraveYardLoaded) + LogFile->write(EQEMuLog::Debug, "Loaded a graveyard for zone %s: graveyard zoneid is %u x is %f y is %f z is %f heading is %f.", short_name, graveyard_zoneid(), graveyard_x(), graveyard_y(), graveyard_z(), graveyard_heading()); + else + LogFile->write(EQEMuLog::Error, "Unable to load the graveyard id %i for zone %s.", graveyard_id(), short_name); + } + if (long_name == 0) { + long_name = strcpy(new char[18], "Long zone missing"); + } + autoshutdown_timer.Start(AUTHENTICATION_TIMEOUT * 1000, false); + Weather_Timer = new Timer((MakeRandomInt(1800, 7200) + 30) * 2000); + Weather_Timer->Start(); + LogFile->write(EQEMuLog::Debug, "The next weather check for zone: %s will be in %i seconds.", short_name, Weather_Timer->GetRemainingTime()/1000); + weather_type = 0; + zone_weather = 0; + blocked_spells = NULL; + totalBS = 0; + aas = NULL; + totalAAs = 0; + gottime = false; + + Instance_Shutdown_Timer = NULL; + bool is_perma = false; + if(instanceid > 0) + { + uint32 rem = database.GetTimeRemainingInstance(instanceid, is_perma); + + 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; + Instance_Timer = new Timer(rem * 1000); + } + else + { + Instance_Timer = NULL; + } + } + else + { + Instance_Timer = NULL; + } + adv_data = NULL; + map_name = NULL; + Instance_Warning_timer = NULL; + did_adventure_actions = false; + database.QGlobalPurge(); + + if(zoneid == RuleI(World, GuildBankZoneID)) + GuildBanks = new GuildBankManager; + else + GuildBanks = NULL; +} + +Zone::~Zone() { + if(pQueuedMerchantsWorkID != 0) + dbasync->CancelWork(pQueuedMerchantsWorkID); + spawn2_list.Clear(); + safe_delete(zonemap); + safe_delete(watermap); + safe_delete(pathing); + if (worldserver.Connected()) { + worldserver.SetZone(0); + } + safe_delete_array(short_name); + safe_delete_array(long_name); + safe_delete(Weather_Timer); + NPCEmoteList.Clear(); + zone_point_list.Clear(); + entity_list.Clear(); + ClearBlockedSpells(); + + safe_delete(Instance_Timer); + safe_delete(Instance_Shutdown_Timer); + safe_delete(Instance_Warning_timer); + safe_delete(qGlobals); + safe_delete_array(adv_data); + safe_delete_array(map_name); + + if(aas != NULL) { + int r; + for(r = 0; r < totalAAs; r++) { + uchar *data = (uchar *) aas[r]; + safe_delete_array(data); + } + safe_delete_array(aas); + } +#ifdef CLIENT_LOGS + client_logs.clear(); +#endif + + safe_delete(GuildBanks); +} + +//Modified for timezones. +bool Zone::Init(bool iStaticZone) { + SetStaticZone(iStaticZone); + + LogFile->write(EQEMuLog::Status, "Loading spawn conditions..."); + if(!spawn_conditions.LoadSpawnConditions(short_name, instanceid)) { + LogFile->write(EQEMuLog::Error, "Loading spawn conditions failed, continuing without them."); + } + + LogFile->write(EQEMuLog::Status, "Loading static zone points..."); + if (!database.LoadStaticZonePoints(&zone_point_list, short_name, GetInstanceVersion())) { + LogFile->write(EQEMuLog::Error, "Loading static zone points failed."); + return false; + } + + LogFile->write(EQEMuLog::Status, "Loading spawn groups..."); + if (!database.LoadSpawnGroups(short_name, GetInstanceVersion(), &spawn_group_list)) { + LogFile->write(EQEMuLog::Error, "Loading spawn groups failed."); + return false; + } + + LogFile->write(EQEMuLog::Status, "Loading spawn2 points..."); + if (!database.PopulateZoneSpawnList(zoneid, spawn2_list, GetInstanceVersion())) + { + LogFile->write(EQEMuLog::Error, "Loading spawn2 points failed."); + return false; + } + + LogFile->write(EQEMuLog::Status, "Loading player corpses..."); + if (!database.LoadPlayerCorpses(zoneid, instanceid)) { + LogFile->write(EQEMuLog::Error, "Loading player corpses failed."); + return false; + } + + LogFile->write(EQEMuLog::Status, "Loading traps..."); + if (!database.LoadTraps(short_name, GetInstanceVersion())) + { + LogFile->write(EQEMuLog::Error, "Loading traps failed."); + return false; + } + + LogFile->write(EQEMuLog::Status, "Loading adventure flavor text..."); + LoadAdventureFlavor(); + + LogFile->write(EQEMuLog::Status, "Loading ground spawns..."); + if (!LoadGroundSpawns()) + { + LogFile->write(EQEMuLog::Error, "Loading ground spawns failed. continuing."); + } + + LogFile->write(EQEMuLog::Status, "Loading World Objects from DB..."); + if (!LoadZoneObjects()) + { + LogFile->write(EQEMuLog::Error, "Loading World Objects failed. continuing."); + } + + //load up the zone's doors (prints inside) + zone->LoadZoneDoors(zone->GetShortName(), zone->GetInstanceVersion()); + zone->LoadBlockedSpells(zone->GetZoneID()); + + //clear trader items if we are loading the bazaar + if(strncasecmp(short_name,"bazaar",6)==0) { + database.DeleteTraderItem(0); + database.DeleteBuyLines(0); + } + + zone->LoadLDoNTraps(); + zone->LoadLDoNTrapEntries(); + zone->LoadVeteranRewards(); + zone->LoadAlternateCurrencies(); + zone->LoadNPCEmotes(&NPCEmoteList); + + //Load AA information + adverrornum = 500; + LoadAAs(); + + //Load merchant data + adverrornum = 501; + zone->GetMerchantDataForZoneLoad(); + + //Load temporary merchant data + adverrornum = 502; + zone->LoadTempMerchantData(); + + // Merc data + if (RuleB(Mercs, AllowMercs)) { + zone->LoadMercTemplates(); + zone->LoadMercSpells(); + } + + if (RuleB(Zone, LevelBasedEXPMods)) + zone->LoadLevelEXPMods(); + + adverrornum = 503; + petition_list.ClearPetitions(); + petition_list.ReadDatabase(); + + //load the zone config file. + if (!LoadZoneCFG(zone->GetShortName(), zone->GetInstanceVersion(), true)) // try loading the zone name... + LoadZoneCFG(zone->GetFileName(), zone->GetInstanceVersion()); // if that fails, try the file name, then load defaults + + if(rules->GetActiveRulesetID() != default_ruleset) + { + string r_name = rules->GetRulesetName(&database, default_ruleset); + if(r_name.size() > 0) + { + rules->LoadRules(&database, r_name.c_str()); + } + } + + LogFile->write(EQEMuLog::Status, "Loading timezone data..."); + zone->zone_time.setEQTimeZone(database.GetZoneTZ(zoneid, GetInstanceVersion())); + + LogFile->write(EQEMuLog::Status, "Init Finished: ZoneID = %d, Time Offset = %d", zoneid, zone->zone_time.getEQTimeZone()); + return true; +} + +void Zone::ReloadStaticData() { + LogFile->write(EQEMuLog::Status, "Reloading Zone Static Data..."); + + LogFile->write(EQEMuLog::Status, "Reloading static zone points..."); + zone_point_list.Clear(); + if (!database.LoadStaticZonePoints(&zone_point_list, GetShortName(), GetInstanceVersion())) { + LogFile->write(EQEMuLog::Error, "Loading static zone points failed."); + } + + LogFile->write(EQEMuLog::Status, "Reloading traps..."); + entity_list.RemoveAllTraps(); + if (!database.LoadTraps(GetShortName(), GetInstanceVersion())) + { + LogFile->write(EQEMuLog::Error, "Reloading traps failed."); + } + + LogFile->write(EQEMuLog::Status, "Reloading ground spawns..."); + if (!LoadGroundSpawns()) + { + LogFile->write(EQEMuLog::Error, "Reloading ground spawns failed. continuing."); + } + + entity_list.RemoveAllObjects(); + LogFile->write(EQEMuLog::Status, "Reloading World Objects from DB..."); + if (!LoadZoneObjects()) + { + LogFile->write(EQEMuLog::Error, "Reloading World Objects failed. continuing."); + } + + entity_list.RemoveAllDoors(); + zone->LoadZoneDoors(zone->GetShortName(), zone->GetInstanceVersion()); + entity_list.RespawnAllDoors(); + + zone->LoadVeteranRewards(); + zone->LoadAlternateCurrencies(); + NPCEmoteList.Clear(); + zone->LoadNPCEmotes(&NPCEmoteList); + + //load the zone config file. + if (!LoadZoneCFG(zone->GetShortName(), zone->GetInstanceVersion(), true)) // try loading the zone name... + LoadZoneCFG(zone->GetFileName(), zone->GetInstanceVersion()); // if that fails, try the file name, then load defaults + + LogFile->write(EQEMuLog::Status, "Zone Static Data Reloaded."); +} + +bool Zone::LoadZoneCFG(const char* filename, uint16 instance_id, bool DontLoadDefault) +{ + memset(&newzone_data, 0, sizeof(NewZone_Struct)); + if(instance_id == 0) + { + map_name = NULL; + if(!database.GetZoneCFG(database.GetZoneID(filename), 0, &newzone_data, can_bind, + can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, default_ruleset, &map_name)) + { + LogFile->write(EQEMuLog::Error, "Error loading the Zone Config."); + return false; + } + } + else + { + //Fall back to base zone if we don't find the instance version. + map_name = NULL; + if(!database.GetZoneCFG(database.GetZoneID(filename), instance_id, &newzone_data, can_bind, + can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, default_ruleset, &map_name)) + { + safe_delete_array(map_name); + if(!database.GetZoneCFG(database.GetZoneID(filename), 0, &newzone_data, can_bind, + can_combat, can_levitate, can_castoutdoor, is_city, is_hotzone, default_ruleset, &map_name)) + { + LogFile->write(EQEMuLog::Error, "Error loading the Zone Config."); + return false; + } + } + } + + //overwrite with our internal variables + strcpy(newzone_data.zone_short_name, GetShortName()); + strcpy(newzone_data.zone_long_name, GetLongName()); + strcpy(newzone_data.zone_short_name2, GetShortName()); + + LogFile->write(EQEMuLog::Status, "Successfully loaded Zone Config."); + return true; +} + +bool Zone::SaveZoneCFG() { + return database.SaveZoneCFG(GetZoneID(), GetInstanceVersion(), &newzone_data); +} + +void Zone::AddAuth(ServerZoneIncommingClient_Struct* szic) { + ZoneClientAuth_Struct* zca = new ZoneClientAuth_Struct; + memset(zca, 0, sizeof(ZoneClientAuth_Struct)); + zca->ip = szic->ip; + zca->wid = szic->wid; + zca->accid = szic->accid; + zca->admin = szic->admin; + zca->charid = szic->charid; + zca->tellsoff = szic->tellsoff; + strn0cpy(zca->charname, szic->charname, sizeof(zca->charname)); + strn0cpy(zca->lskey, szic->lskey, sizeof(zca->lskey)); + zca->stale = false; + client_auth_list.Insert(zca); +} + +void Zone::RemoveAuth(const char* iCharName) +{ + LinkedListIterator iterator(client_auth_list); + + iterator.Reset(); + while (iterator.MoreElements()) { + ZoneClientAuth_Struct* zca = iterator.GetData(); + if (strcasecmp(zca->charname, iCharName) == 0) { + iterator.RemoveCurrent(); + return; + } + iterator.Advance(); + } +} + +void Zone::ResetAuth() +{ + LinkedListIterator iterator(client_auth_list); + + iterator.Reset(); + while (iterator.MoreElements()) { + iterator.RemoveCurrent(); + } +} + +bool Zone::GetAuth(uint32 iIP, const char* iCharName, uint32* oWID, uint32* oAccID, uint32* oCharID, int16* oStatus, char* oLSKey, bool* oTellsOff) { + LinkedListIterator iterator(client_auth_list); + + iterator.Reset(); + while (iterator.MoreElements()) { + ZoneClientAuth_Struct* zca = iterator.GetData(); + if (strcasecmp(zca->charname, iCharName) == 0) { + if(oWID) + *oWID = zca->wid; + if(oAccID) + *oAccID = zca->accid; + if(oCharID) + *oCharID = zca->charid; + if(oStatus) + *oStatus = zca->admin; + if(oTellsOff) + *oTellsOff = zca->tellsoff; + zca->stale = true; + return true; + } + iterator.Advance(); + } + return false; +} + +uint32 Zone::CountAuth() { + LinkedListIterator iterator(client_auth_list); + + int x = 0; + iterator.Reset(); + while (iterator.MoreElements()) { + x++; + iterator.Advance(); + } + return x; +} + +bool Zone::Process() { + LockMutex lock(&MZoneLock); + _ZP(Zone_Process); + + + spawn_conditions.Process(); + + if(spawn2_timer.Check()) { + LinkedListIterator iterator(spawn2_list); + + iterator.Reset(); + while (iterator.MoreElements()) { + if (iterator.GetData()->Process()) { + iterator.Advance(); + } + else { + iterator.RemoveCurrent(); + } + } + if(adv_data && !did_adventure_actions) + { + DoAdventureActions(); + } + } + if(initgrids_timer.Check()) { + //delayed grid loading stuff. + initgrids_timer.Disable(); + LinkedListIterator iterator(spawn2_list); + + iterator.Reset(); + while (iterator.MoreElements()) { + iterator.GetData()->LoadGrid(); + iterator.Advance(); + } + } + + if(!staticzone) { + if (autoshutdown_timer.Check()) { + StartShutdownTimer(); + if (numclients == 0) { + return false; + } + } + } + + if(GetInstanceID() > 0) + { + if(Instance_Timer != NULL && Instance_Shutdown_Timer == NULL) + { + if(Instance_Timer->Check()) + { + entity_list.GateAllClients(); + database.DeleteInstance(GetInstanceID()); + Instance_Shutdown_Timer = new Timer(20000); //20 seconds + } + + if(adv_data == NULL) + { + if(Instance_Warning_timer == NULL) + { + uint32 rem_time = Instance_Timer->GetRemainingTime(); + if(rem_time < 60000 && rem_time > 55000) + { + entity_list.ExpeditionWarning(1); + Instance_Warning_timer = new Timer(10000); + } + else if(rem_time < 300000 && rem_time > 295000) + { + entity_list.ExpeditionWarning(5); + Instance_Warning_timer = new Timer(10000); + } + else if(rem_time < 900000 && rem_time > 895000) + { + entity_list.ExpeditionWarning(15); + Instance_Warning_timer = new Timer(10000); + } + } + else if(Instance_Warning_timer->Check()) + { + safe_delete(Instance_Warning_timer); + } + } + } + else if(Instance_Shutdown_Timer != NULL) + { + if(Instance_Shutdown_Timer->Check()) + { + StartShutdownTimer(); + return false; + } + } + } + + if(Weather_Timer->Check()){ + Weather_Timer->Disable(); + uint16 tmpweather = MakeRandomInt(0, 100); + + if(zone->weather_type != 0) + { + if(tmpweather >= 80) + { + // A change in the weather.... + uint8 tmpOldWeather = zone_weather; + + if(zone->zone_weather == 0) + zone->zone_weather = zone->weather_type; + else + zone->zone_weather = 0; + + LogFile->write(EQEMuLog::Debug, "The weather for zone: %s has changed. Old weather was = %i. New weather is = %i", zone->GetShortName(), tmpOldWeather, zone_weather); + + this->weatherSend(); + } + else + LogFile->write(EQEMuLog::Debug, "The weather for zone: %s is not going to change. Chance was = %i percent.", zone->GetShortName(), tmpweather); + + uint32 weatherTime = 0; + + if(zone->zone_weather != zone->weather_type) + weatherTime = (MakeRandomInt(1800, 7200) + 30) * 2000; + else + weatherTime = (MakeRandomInt(900, 2700) + 30) * 1000; + + Weather_Timer->Start(weatherTime); + + LogFile->write(EQEMuLog::Debug, "The next weather check for zone: %s will be in %i seconds.", zone->GetShortName(), Weather_Timer->GetRemainingTime()/1000); + } + } + + if(qGlobals) + { + if(qglobal_purge_timer.Check()) + { + qGlobals->PurgeExpiredGlobals(); + } + } + + if (clientauth_timer.Check()) { + LinkedListIterator iterator2(client_auth_list); + + iterator2.Reset(); + while (iterator2.MoreElements()) { + if (iterator2.GetData()->stale) + iterator2.RemoveCurrent(); + else { + iterator2.GetData()->stale = true; + iterator2.Advance(); + } + } + } + + return true; +} + +void Zone::StartShutdownTimer(uint32 set_time) { + MZoneLock.lock(); + if (set_time > autoshutdown_timer.GetRemainingTime()) { + if (set_time == (RuleI(Zone, AutoShutdownDelay))) + { + set_time = database.getZoneShutDownDelay(GetZoneID(), GetInstanceVersion()); + } + autoshutdown_timer.Start(set_time, false); + } + MZoneLock.unlock(); +} + +bool Zone::Depop(bool StartSpawnTimer) { +std::map::iterator itr; + entity_list.Depop(StartSpawnTimer); + + // Refresh npctable, getting current info from database. + while(npctable.size()) { + itr=npctable.begin(); + delete itr->second; + npctable.erase(itr); + } + + return true; +} + +void Zone::Repop(uint32 delay) { + + if(!Depop()) + return; + + LinkedListIterator iterator(spawn2_list); + + MZoneLock.lock(); + iterator.Reset(); + while (iterator.MoreElements()) { + iterator.RemoveCurrent(); + } + + if (!database.PopulateZoneSpawnList(zoneid, spawn2_list, GetInstanceVersion(), delay)) + LogFile->write(EQEMuLog::Debug, "Error in Zone::Repop: database.PopulateZoneSpawnList failed"); + + MZoneLock.unlock(); + + initgrids_timer.Start(); +} + +void Zone::GetTimeSync() +{ + if (worldserver.Connected() && !gottime) { + ServerPacket* pack = new ServerPacket(ServerOP_GetWorldTime, 0); + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +void Zone::SetDate(uint16 year, uint8 month, uint8 day, uint8 hour, uint8 minute) +{ + if (worldserver.Connected()) { + ServerPacket* pack = new ServerPacket(ServerOP_SetWorldTime, sizeof(eqTimeOfDay)); + eqTimeOfDay* eqtod = (eqTimeOfDay*)pack->pBuffer; + eqtod->start_eqtime.minute=minute; + eqtod->start_eqtime.hour=hour; + eqtod->start_eqtime.day=day; + eqtod->start_eqtime.month=month; + eqtod->start_eqtime.year=year; + eqtod->start_realtime=time(0); + printf("Setting master date on world server to: %d-%d-%d %d:%d (%d)\n", year, month, day, hour, minute, (int)eqtod->start_realtime); + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +void Zone::SetTime(uint8 hour, uint8 minute) +{ + if (worldserver.Connected()) { + ServerPacket* pack = new ServerPacket(ServerOP_SetWorldTime, sizeof(eqTimeOfDay)); + eqTimeOfDay* eqtod = (eqTimeOfDay*)pack->pBuffer; + zone_time.getEQTimeOfDay(time(0), &eqtod->start_eqtime); + eqtod->start_eqtime.minute=minute; + eqtod->start_eqtime.hour=hour; + eqtod->start_realtime=time(0); + printf("Setting master time on world server to: %d:%d (%d)\n", hour, minute, (int)eqtod->start_realtime); + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +ZonePoint* Zone::GetClosestZonePoint(float x, float y, float z, uint32 to, Client* client, float max_distance) { + LinkedListIterator iterator(zone_point_list); + ZonePoint* closest_zp = 0; + float closest_dist = FLT_MAX; + float max_distance2 = max_distance * max_distance; + iterator.Reset(); + while(iterator.MoreElements()) + { + ZonePoint* zp = iterator.GetData(); + uint32 mask_test = client->GetClientVersionBit(); + if(!(zp->client_version_mask & mask_test)) + { + iterator.Advance(); + continue; + } + + if (zp->target_zone_id == to) + { + float delta_x = zp->x - x; + float delta_y = zp->y - y; + if(zp->x == 999999 || zp->x == -999999) + delta_x = 0; + if(zp->y == 999999 || zp->y == -999999) + delta_y = 0; + + float dist = sqrt(delta_x * delta_x + delta_y * delta_y); + if (dist < closest_dist) + { + closest_zp = zp; + closest_dist = dist; + } + } + iterator.Advance(); + } + + if(closest_dist > 400.0f && closest_dist < max_distance2) + { + if(client) + client->CheatDetected(MQZoneUnknownDest, x, y, z); // Someone is trying to use /zone + LogFile->write(EQEMuLog::Status, "WARNING: Closest zone point for zone id %d is %f, you might need to update your zone_points table if you dont arrive at the right spot.", to, closest_dist); + LogFile->write(EQEMuLog::Status, ". %f x %f y %f z ", x, y, z); + } + + if(closest_dist > max_distance2) + closest_zp = NULL; + + if(!closest_zp) + closest_zp = GetClosestZonePointWithoutZone(x, y, z, client); + + return closest_zp; +} + +ZonePoint* Zone::GetClosestZonePoint(float x, float y, float z, const char* to_name, Client* client, float max_distance) { + if(to_name == NULL) + return GetClosestZonePointWithoutZone(x,y,z, client, max_distance); + return GetClosestZonePoint(x, y, z, database.GetZoneID(to_name), client, max_distance); +} + +ZonePoint* Zone::GetClosestZonePointWithoutZone(float x, float y, float z, Client* client, float max_distance) { + LinkedListIterator iterator(zone_point_list); + ZonePoint* closest_zp = 0; + float closest_dist = FLT_MAX; + float max_distance2 = max_distance*max_distance; + iterator.Reset(); + while(iterator.MoreElements()) + { + ZonePoint* zp = iterator.GetData(); + uint32 mask_test = client->GetClientVersionBit(); + + if(!(zp->client_version_mask & mask_test)) + { + iterator.Advance(); + continue; + } + + float delta_x = zp->x - x; + float delta_y = zp->y - y; + if(zp->x == 999999 || zp->x == -999999) + delta_x = 0; + if(zp->y == 999999 || zp->y == -999999) + delta_y = 0; + + float dist = delta_x*delta_x+delta_y*delta_y;///*+(zp->z-z)*(zp->z-z)*/; + if (dist < closest_dist) + { + closest_zp = zp; + closest_dist = dist; + } + iterator.Advance(); + } + if(closest_dist > max_distance2) + closest_zp = NULL; + + return closest_zp; +} + +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], NULL, 0); + zone_point_list->Insert(zp); + zone->numzonepoints++; + } + mysql_free_result(result); + } + else + { + cerr << "Error1 in LoadStaticZonePoints query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } +return true; +} + +bool ZoneDatabase::DumpZoneState() { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM zone_state_dump WHERE zonename='%s'", zone->GetShortName()), errbuf)) { + cerr << "Error in DumpZoneState query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + safe_delete_array(query); + + + + uint32 spawn2_count = zone->CountSpawn2(); + uint32 npc_count = 0; + uint32 npcloot_count = 0; + uint32 gmspawntype_count = 0; + entity_list.CountNPC(&npc_count, &npcloot_count, &gmspawntype_count); + + cout << "DEBUG: spawn2count=" << spawn2_count << ", npc_count=" << npc_count << ", npcloot_count=" << npcloot_count << ", gmspawntype_count=" << gmspawntype_count << endl; + + ZSDump_Spawn2* spawn2_dump = 0; + ZSDump_NPC* npc_dump = 0; + ZSDump_NPC_Loot* npcloot_dump = 0; + NPCType* gmspawntype_dump = 0; + if (spawn2_count > 0) { + spawn2_dump = (ZSDump_Spawn2*) new uchar[spawn2_count * sizeof(ZSDump_Spawn2)]; + memset(spawn2_dump, 0, sizeof(ZSDump_Spawn2) * spawn2_count); + } + if (npc_count > 0) { + npc_dump = (ZSDump_NPC*) new uchar[npc_count * sizeof(ZSDump_NPC)]; + memset(npc_dump, 0, sizeof(ZSDump_NPC) * npc_count); + for (unsigned int i=0; i < npc_count; i++) { + npc_dump[i].spawn2_dump_index = 0xFFFFFFFF; + npc_dump[i].gmspawntype_index = 0xFFFFFFFF; + } + } + if (npcloot_count > 0) { + npcloot_dump = (ZSDump_NPC_Loot*) new uchar[npcloot_count * sizeof(ZSDump_NPC_Loot)]; + memset(npcloot_dump, 0, sizeof(ZSDump_NPC_Loot) * npcloot_count); + for (unsigned int k=0; k < npcloot_count; k++) + npcloot_dump[k].npc_dump_index = 0xFFFFFFFF; + } + if (gmspawntype_count > 0) { + gmspawntype_dump = (NPCType*) new uchar[gmspawntype_count * sizeof(NPCType)]; + memset(gmspawntype_dump, 0, sizeof(NPCType) * gmspawntype_count); + } + + entity_list.DoZoneDump(spawn2_dump, npc_dump, npcloot_dump, gmspawntype_dump); + query = new char[512 + ((sizeof(ZSDump_Spawn2) * spawn2_count + sizeof(ZSDump_NPC) * npc_count + sizeof(ZSDump_NPC_Loot) * npcloot_count + sizeof(NPCType) * gmspawntype_count) * 2)]; + char* end = query; + + end += sprintf(end, "Insert Into zone_state_dump (zonename, spawn2_count, npc_count, npcloot_count, gmspawntype_count, spawn2, npcs, npc_loot, gmspawntype) values ('%s', %i, %i, %i, %i, ", zone->GetShortName(), spawn2_count, npc_count, npcloot_count, gmspawntype_count); + *end++ = '\''; + if (spawn2_dump != 0) { + end += DoEscapeString(end, (char*)spawn2_dump, sizeof(ZSDump_Spawn2) * spawn2_count); + safe_delete_array(spawn2_dump); + } + *end++ = '\''; + end += sprintf(end, ", "); + *end++ = '\''; + if (npc_dump != 0) { + end += DoEscapeString(end, (char*)npc_dump, sizeof(ZSDump_NPC) * npc_count); + safe_delete_array(npc_dump); + } + *end++ = '\''; + end += sprintf(end, ", "); + *end++ = '\''; + if (npcloot_dump != 0) { + end += DoEscapeString(end, (char*)npcloot_dump, sizeof(ZSDump_NPC_Loot) * npcloot_count); + safe_delete_array(npcloot_dump); + } + *end++ = '\''; + end += sprintf(end, ", "); + *end++ = '\''; + if (gmspawntype_dump != 0) { + end += DoEscapeString(end, (char*)gmspawntype_dump, sizeof(NPCType) * gmspawntype_count); + safe_delete_array(gmspawntype_dump); + } + *end++ = '\''; + end += sprintf(end, ")"); + + uint32 affected_rows = 0; + if (!RunQuery(query, (uint32) (end - query), errbuf, 0, &affected_rows)) { + // if (DoEscapeString(query, (unsigned int) (end - query))) { + safe_delete_array(query); + cerr << "Error in ZoneDump query " << errbuf << endl; + return false; + } + safe_delete_array(query); + + if (affected_rows == 0) { + cerr << "Zone dump failed. (affected rows = 0)" << endl; + return false; + } + return true; +} + +int8 ZoneDatabase::LoadZoneState(const char* zonename, LinkedList& spawn2_list) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + uint32 i; + unsigned long* lengths; + uint32 elapsedtime = 0; + uint32 spawn2_count = 0; + uint32 npc_count = 0; + uint32 npcloot_count = 0; + uint32 gmspawntype_count = 0; + ZSDump_Spawn2* spawn2_dump = 0; + ZSDump_NPC* npc_dump = 0; + ZSDump_NPC_Loot* npcloot_dump = 0; + NPCType* gmspawntype_dump = 0; + Spawn2** spawn2_loaded = 0; + NPC** npc_loaded = 0; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT spawn2_count, npc_count, npcloot_count, gmspawntype_count, spawn2, npcs, npc_loot, gmspawntype, (UNIX_TIMESTAMP()-UNIX_TIMESTAMP(time)) as elapsedtime FROM zone_state_dump WHERE zonename='%s'", zonename), errbuf, &result)) { + safe_delete_array(query); + if (mysql_num_rows(result) == 1) { + row = mysql_fetch_row(result); + cout << "Elapsed time: " << row[8] << endl; + elapsedtime = atoi(row[8]) * 1000; + lengths = mysql_fetch_lengths(result); + spawn2_count = atoi(row[0]); + cout << "Spawn2count: " << spawn2_count << endl; + if (lengths[4] != (sizeof(ZSDump_Spawn2) * spawn2_count)) { + cerr << "Error in LoadZoneState: spawn2_dump length mismatch l=" << lengths[4] << ", e=" << (sizeof(ZSDump_Spawn2) * spawn2_count) << endl; + CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result); + return -1; + } + else if (spawn2_count > 0) { + spawn2_dump = new ZSDump_Spawn2[spawn2_count]; + spawn2_loaded = new Spawn2*[spawn2_count]; + memcpy(spawn2_dump, row[4], lengths[4]); + for (i=0; i < spawn2_count; i++) { + if (spawn2_dump[i].time_left == 0xFFFFFFFF) // npc spawned, timer should be disabled + spawn2_loaded[i] = LoadSpawn2(spawn2_list, spawn2_dump[i].spawn2_id, 0xFFFFFFFF); + else if (spawn2_dump[i].time_left <= elapsedtime) + spawn2_loaded[i] = LoadSpawn2(spawn2_list, spawn2_dump[i].spawn2_id, 0); + else + spawn2_loaded[i] = LoadSpawn2(spawn2_list, spawn2_dump[i].spawn2_id, spawn2_dump[i].time_left - elapsedtime); + if (spawn2_loaded[i] == 0) { + cerr << "Error in LoadZoneState: spawn2_loaded[" << i << "] == 0" << endl; + CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result); + return -1; + + } + } + } + + gmspawntype_count = atoi(row[3]); + cout << "gmspawntype_count: " << gmspawntype_count << endl; + if (lengths[7] != (sizeof(NPCType) * gmspawntype_count)) { + cerr << "Error in LoadZoneState: gmspawntype_dump length mismatch l=" << lengths[7] << ", e=" << (sizeof(NPCType) * gmspawntype_count) << endl; + CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result); + return -1; + } + else if (gmspawntype_count > 0) { + gmspawntype_dump = new NPCType[gmspawntype_count]; + memcpy(gmspawntype_dump, row[7], lengths[7]); + } + + npc_count = atoi(row[1]); + cout << "npc_count: " << npc_count << endl; + if (lengths[5] != (sizeof(ZSDump_NPC) * npc_count)) { + cerr << "Error in LoadZoneState: npc_dump length mismatch l=" << lengths[5] << ", e=" << (sizeof(ZSDump_NPC) * npc_count) << endl; + CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result); + return -1; + } + else if (npc_count > 0) { + npc_dump = new ZSDump_NPC[npc_count]; + npc_loaded = new NPC*[npc_count]; + for (i=0; i < npc_count; i++) { + npc_loaded[i] = 0; + } + memcpy(npc_dump, row[5], lengths[5]); + for (i=0; i < npc_count; i++) { + if (npc_loaded[i] != 0) { + cerr << "Error in LoadZoneState: npc_loaded[" << i << "] != 0" << endl; + CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result); + return -1; + } + Spawn2* tmp = 0; + if (!npc_dump[i].corpse && npc_dump[i].spawn2_dump_index != 0xFFFFFFFF) { + if (spawn2_loaded == 0 || npc_dump[i].spawn2_dump_index >= spawn2_count) { + cerr << "Error in LoadZoneState: (spawn2_loaded == 0 || index >= count) && npc_dump[" << i << "].spawn2_dump_index != 0xFFFFFFFF" << endl; + CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result); + return -1; + } + tmp = spawn2_loaded[npc_dump[i].spawn2_dump_index]; + spawn2_loaded[npc_dump[i].spawn2_dump_index] = 0; + } + if (npc_dump[i].npctype_id == 0) { + if (npc_dump[i].gmspawntype_index == 0xFFFFFFFF) { + cerr << "Error in LoadZoneState: gmspawntype index invalid" << endl; + safe_delete(tmp); + + CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result); + return -1; + } + else { + if (gmspawntype_dump == 0 || npc_dump[i].gmspawntype_index >= gmspawntype_count) { + cerr << "Error in LoadZoneState: (gmspawntype_dump == 0 || index >= count) && npc_dump[" << i << "].npctype_id == 0" << endl; + safe_delete(tmp); + + CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result); + return -1; + } + npc_loaded[i] = new NPC(&gmspawntype_dump[npc_dump[i].gmspawntype_index], tmp, npc_dump[i].x, npc_dump[i].y, npc_dump[i].z, npc_dump[i].heading, FlyMode3, npc_dump[i].corpse); + } + } + else { + const NPCType* crap = database.GetNPCType(npc_dump[i].npctype_id); + if (crap != 0) + npc_loaded[i] = new NPC(crap, tmp, npc_dump[i].x, npc_dump[i].y, npc_dump[i].z, npc_dump[i].heading, FlyMode3, npc_dump[i].corpse); + else { + cerr << "Error in LoadZoneState: Unknown npctype_id: " << npc_dump[i].npctype_id << endl; + safe_delete(tmp); + } + } + if (npc_loaded[i] != 0) { + npc_loaded[i]->AddCash(npc_dump[i].copper, npc_dump[i].silver, npc_dump[i].gold, npc_dump[i].platinum); + // if (npc_dump[i].corpse) { + // if (npc_dump[i].decay_time_left <= elapsedtime) + // npc_loaded[i]->SetDecayTimer(0); + // else + // npc_loaded[i]->SetDecayTimer(npc_dump[i].decay_time_left - elapsedtime); + // } + entity_list.AddNPC(npc_loaded[i]); + } + } + } + + npcloot_count = atoi(row[2]); + cout << "npcloot_count: " << npcloot_count << endl; + if (lengths[6] != (sizeof(ZSDump_NPC_Loot) * npcloot_count)) { + cerr << "Error in LoadZoneState: npcloot_dump length mismatch l=" << lengths[6] << ", e=" << (sizeof(ZSDump_NPC_Loot) * npcloot_count) << endl; + CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result); + return -1; + } + else if (npcloot_count > 0) { + if (npc_loaded == 0) { + cerr << "Error in LoadZoneState: npcloot_count > 0 && npc_loaded == 0" << endl; + CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result); + return -1; + } + npcloot_dump = new ZSDump_NPC_Loot[npcloot_count]; + memcpy(npcloot_dump, row[6], lengths[6]); + for (i=0; i < npcloot_count; i++) { + if (npcloot_dump[i].npc_dump_index >= npc_count) { + cerr << "Error in LoadZoneState: npcloot_dump[" << i << "].npc_dump_index >= npc_count" << endl; + CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result); + return -1; + } + if (npc_loaded[npcloot_dump[i].npc_dump_index] != 0) { + npc_loaded[npcloot_dump[i].npc_dump_index]->AddItem(npcloot_dump[i].itemid, npcloot_dump[i].charges, npcloot_dump[i].equipSlot); + } + } + } + CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result); + } + else { + CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result); + return 0; + } + CleanupLoadZoneState(spawn2_count, &spawn2_dump, &npc_dump, &npcloot_dump, &gmspawntype_dump, &spawn2_loaded, &npc_loaded, &result); + } + else { + cerr << "Error in LoadZoneState query '" << query << "' " << errbuf << endl; + + safe_delete_array(query); + return -1; + } + + return 1; +} + +void CleanupLoadZoneState(uint32 spawn2_count, ZSDump_Spawn2** spawn2_dump, ZSDump_NPC** npc_dump, ZSDump_NPC_Loot** npcloot_dump, NPCType** gmspawntype_dump, Spawn2*** spawn2_loaded, NPC*** npc_loaded, MYSQL_RES** result) { + safe_delete(*spawn2_dump); + safe_delete(*spawn2_loaded); + safe_delete(*gmspawntype_dump); + safe_delete(*npc_dump); + safe_delete(*npc_loaded); + safe_delete(*npcloot_dump); + if (*result) { + mysql_free_result(*result); + *result = 0; + } +} + +void Zone::SpawnStatus(Mob* client) { + LinkedListIterator iterator(spawn2_list); + + uint32 x = 0; + iterator.Reset(); + while(iterator.MoreElements()) + { + if (iterator.GetData()->timer.GetRemainingTime() == 0xFFFFFFFF) + client->Message(0, " %d: %1.1f, %1.1f, %1.1f: disabled", iterator.GetData()->GetID(), iterator.GetData()->GetX(), iterator.GetData()->GetY(), iterator.GetData()->GetZ()); + else + client->Message(0, " %d: %1.1f, %1.1f, %1.1f: %1.2f", iterator.GetData()->GetID(), iterator.GetData()->GetX(), iterator.GetData()->GetY(), iterator.GetData()->GetZ(), (float)iterator.GetData()->timer.GetRemainingTime() / 1000); + + x++; + iterator.Advance(); + } + client->Message(0, "%i spawns listed.", x); +} + +void Zone::ShowEnabledSpawnStatus(Mob* client) +{ + LinkedListIterator iterator(spawn2_list); + int x = 0; + int iEnabledCount = 0; + + iterator.Reset(); + + while(iterator.MoreElements()) + { + if (iterator.GetData()->timer.GetRemainingTime() != 0xFFFFFFFF) + { + client->Message(0, " %d: %1.1f, %1.1f, %1.1f: %1.2f", iterator.GetData()->GetID(), iterator.GetData()->GetX(), iterator.GetData()->GetY(), iterator.GetData()->GetZ(), (float)iterator.GetData()->timer.GetRemainingTime() / 1000); + iEnabledCount++; + } + + x++; + iterator.Advance(); + } + + client->Message(0, "%i of %i spawns listed.", iEnabledCount, x); +} + +void Zone::ShowDisabledSpawnStatus(Mob* client) +{ + LinkedListIterator iterator(spawn2_list); + int x = 0; + int iDisabledCount = 0; + + iterator.Reset(); + + while(iterator.MoreElements()) + { + if (iterator.GetData()->timer.GetRemainingTime() == 0xFFFFFFFF) + { + client->Message(0, " %d: %1.1f, %1.1f, %1.1f: disabled", iterator.GetData()->GetID(), iterator.GetData()->GetX(), iterator.GetData()->GetY(), iterator.GetData()->GetZ()); + iDisabledCount++; + } + + x++; + iterator.Advance(); + } + + client->Message(0, "%i of %i spawns listed.", iDisabledCount, x); +} + +void Zone::ShowSpawnStatusByID(Mob* client, uint32 spawnid) +{ + LinkedListIterator iterator(spawn2_list); + int x = 0; + int iSpawnIDCount = 0; + + iterator.Reset(); + + while(iterator.MoreElements()) + { + if (iterator.GetData()->GetID() == spawnid) + { + if (iterator.GetData()->timer.GetRemainingTime() == 0xFFFFFFFF) + client->Message(0, " %d: %1.1f, %1.1f, %1.1f: disabled", iterator.GetData()->GetID(), iterator.GetData()->GetX(), iterator.GetData()->GetY(), iterator.GetData()->GetZ()); + else + client->Message(0, " %d: %1.1f, %1.1f, %1.1f: %1.2f", iterator.GetData()->GetID(), iterator.GetData()->GetX(), iterator.GetData()->GetY(), iterator.GetData()->GetZ(), (float)iterator.GetData()->timer.GetRemainingTime() / 1000); + + iSpawnIDCount++; + + break; + } + + x++; + iterator.Advance(); + } + + if(iSpawnIDCount > 0) + client->Message(0, "%i of %i spawns listed.", iSpawnIDCount, x); + else + client->Message(0, "No matching spawn id was found in this zone."); +} + + +bool Zone::RemoveSpawnEntry(uint32 spawnid) +{ + LinkedListIterator iterator(spawn2_list); + + + iterator.Reset(); + while(iterator.MoreElements()) + { + if(iterator.GetData()->GetID() == spawnid) + { + iterator.RemoveCurrent(); + return true; + } + else + iterator.Advance(); + } +return false; +} + +bool Zone::RemoveSpawnGroup(uint32 in_id) { + if(spawn_group_list.RemoveSpawnGroup(in_id)) + return true; + else + return false; +} + + +// 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; + } + return true; +}// Added By Hogie -- End + +void Zone::weatherSend() +{ + /*switch(zone_weather) + { + case 0: + entity_list.Message(0, 0, "The sky clears."); + break; + case 1: + entity_list.Message(0, 0, "Raindrops begin to fall from the sky."); + break; + case 2: + entity_list.Message(0, 0, "Snowflakes begin to fall from the sky."); + break; + default: + entity_list.Message(0, 0, "Strange weather patterns form in the sky. (%i)", zone_weather); + break; + }*/ + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Weather, 8); + if(zone_weather>0) + outapp->pBuffer[0] = zone_weather-1; + if(zone_weather>0) + outapp->pBuffer[4] = 0x10+MakeRandomInt(0, 9); // This number changes in the packets, intensity? + entity_list.QueueClients(0, outapp); + safe_delete(outapp); +} + +bool Zone::HasGraveyard() { + bool Result = false; + + if(graveyard_zoneid() > 0) + Result = true; + + return Result; +} + +void Zone::SetGraveyard(uint32 zoneid, uint32 x, uint32 y, uint32 z, uint32 heading) { + pgraveyard_zoneid = zoneid; + pgraveyard_x = x; + pgraveyard_y = y; + pgraveyard_z = z; + pgraveyard_heading = heading; +} + +void Zone::LoadBlockedSpells(uint32 zoneid) +{ + if(!blocked_spells) + { + totalBS = database.GetBlockedSpellsCount(zoneid); + if(totalBS > 0){ + blocked_spells = new ZoneSpellsBlocked[totalBS]; + if(!database.LoadBlockedSpells(totalBS, blocked_spells, zoneid)) + { + LogFile->write(EQEMuLog::Error, "... Failed to load blocked spells."); + ClearBlockedSpells(); + } + } + } +} + +void Zone::ClearBlockedSpells() +{ + if(blocked_spells){ + safe_delete_array(blocked_spells); + totalBS = 0; + } +} + +bool Zone::IsSpellBlocked(uint32 spell_id, float nx, float ny, float nz) +{ + if (blocked_spells) + { + bool exception = false; + bool block_all = false; + for (int x = 0; x < totalBS; x++) + { + if (blocked_spells[x].spellid == spell_id) + { + exception = true; + } + + if (blocked_spells[x].spellid == 0) + { + block_all = true; + } + } + + for (int x = 0; x < totalBS; x++) + { + // If spellid is 0, block all spells in the zone + if (block_all) + { + // If the same zone has entries other than spellid 0, they act as exceptions and are allowed + if (exception) + { + return false; + } + else + { + return true; + } + } + else + { + if (spell_id != blocked_spells[x].spellid) + { + continue; + } + + switch (blocked_spells[x].type) + { + case 1: + { + return true; + break; + } + case 2: + { + if ((( nx >= (blocked_spells[x].x-blocked_spells[x].xdiff)) && (nx <= (blocked_spells[x].x+blocked_spells[x].xdiff))) && + (( ny >= (blocked_spells[x].y-blocked_spells[x].ydiff)) && (ny <= (blocked_spells[x].y+blocked_spells[x].ydiff))) && + (( nz >= (blocked_spells[x].z-blocked_spells[x].zdiff)) && (nz <= (blocked_spells[x].z+blocked_spells[x].zdiff)))) + { + return true; + } + break; + } + default: + { + continue; + break; + } + } + } + } + } + return false; +} + +const char* Zone::GetSpellBlockedMessage(uint32 spell_id, float nx, float ny, float nz) +{ + if(blocked_spells) + { + for(int x = 0; x < totalBS; x++) + { + if(spell_id != blocked_spells[x].spellid && blocked_spells[x].spellid != 0) + continue; + + switch(blocked_spells[x].type) + { + case 1: + { + return blocked_spells[x].message; + break; + } + case 2: + { + if((( nx > (blocked_spells[x].x-blocked_spells[x].xdiff)) && (nx < (blocked_spells[x].x+blocked_spells[x].xdiff))) && + (( ny > (blocked_spells[x].y-blocked_spells[x].ydiff)) && (ny < (blocked_spells[x].y+blocked_spells[x].ydiff))) && + (( nz > (blocked_spells[x].z-blocked_spells[x].zdiff)) && (nz < (blocked_spells[x].z+blocked_spells[x].zdiff)))) + { + return blocked_spells[x].message; + } + break; + } + default: + { + continue; + break; + } + } + } + } + return "Error: Message String Not Found\0"; +} + +void Zone::SetInstanceTimer(uint32 new_duration) +{ + if(Instance_Timer) + { + Instance_Timer->Start(new_duration * 1000); + } +} + +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); + return; + } +} + +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 = NULL; + 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); + return; + } +} + +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; + + current_reward.claim_id = 0; + + + 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; + } + + 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(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); + } + + 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) +{ + if(newGlobal.npc_id != 0) + return; + + if(newGlobal.char_id != 0) + return; + + if(newGlobal.zone_id == GetZoneID() || newGlobal.zone_id == 0) + { + if(qGlobals) + { + qGlobals->AddGlobal(qid, newGlobal); + } + else + { + qGlobals = new QGlobalCache(); + qGlobals->AddGlobal(qid, newGlobal); + } + } +} + +void Zone::DeleteQGlobal(std::string name, uint32 npcID, uint32 charID, uint32 zoneID) +{ + if(qGlobals) + { + qGlobals->RemoveGlobal(name, npcID, charID, zoneID); + } +} + +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); + return; + } +} + +void Zone::DoAdventureCountIncrease() +{ + ServerZoneAdventureDataReply_Struct *sr = (ServerZoneAdventureDataReply_Struct*)adv_data; + if(sr->count < sr->total) + { + sr->count++; + ServerPacket *pack = new ServerPacket(ServerOP_AdventureCountUpdate, sizeof(uint16)); + *((uint16*)pack->pBuffer) = instanceid; + worldserver.SendPacket(pack); + delete pack; + } +} + +void Zone::DoAdventureAssassinationCountIncrease() +{ + ServerZoneAdventureDataReply_Struct *sr = (ServerZoneAdventureDataReply_Struct*)adv_data; + if(sr->assa_count < RuleI(Adventure, NumberKillsForBossSpawn)) + { + sr->assa_count++; + ServerPacket *pack = new ServerPacket(ServerOP_AdventureAssaCountUpdate, sizeof(uint16)); + *((uint16*)pack->pBuffer) = instanceid; + worldserver.SendPacket(pack); + delete pack; + } +} + +void Zone::DoAdventureActions() +{ + ServerZoneAdventureDataReply_Struct* ds = (ServerZoneAdventureDataReply_Struct*)adv_data; + if(ds->type == Adventure_Collect) + { + int count = (ds->total - ds->count) * 25 / 10; + entity_list.AddLootToNPCS(ds->data_id, count); + did_adventure_actions = true; + } + else if(ds->type == Adventure_Assassinate) + { + if(ds->assa_count >= RuleI(Adventure, NumberKillsForBossSpawn)) + { + const NPCType* tmp = database.GetNPCType(ds->data_id); + if(tmp) + { + NPC* npc = new NPC(tmp, 0, ds->assa_x, ds->assa_y, ds->assa_z, ds->assa_h, FlyMode3); + npc->AddLootTable(); + entity_list.AddNPC(npc); + npc->Shout("Rarrrgh!"); + did_adventure_actions = true; + } + } + } + else + { + did_adventure_actions = true; + } + +} + +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); + } +} + +void Zone::ReloadWorld(uint32 Option){ + if(Option == 1){ + zone->Repop(0); + parse->ReloadQuests(); + } +} \ No newline at end of file diff --git a/zone/zone.h b/zone/zone.h new file mode 100644 index 000000000..903390e2f --- /dev/null +++ b/zone/zone.h @@ -0,0 +1,310 @@ +/* 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 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/rulesys.h" +#include "../common/eq_packet_structs.h" +#include "features.h" +#include "spawngroup.h" +//#include "mob.h" +#include "zonedump.h" +#include "spawn2.h" +#include "tasks.h" +#include "pathing.h" +#include "QGlobals.h" + +class Map; +class WaterMap; + +struct ZonePoint +{ + float x; + float y; + float z; + float heading; + uint16 number; + float target_x; + float target_y; + float target_z; + float target_heading; + uint16 target_zone_id; + int32 target_zone_instance; + uint32 client_version_mask; +}; +struct ZoneClientAuth_Struct { + uint32 ip; // client's IP address + uint32 wid; // client's WorldID# + uint32 accid; + int16 admin; + uint32 charid; + bool tellsoff; + char charname[64]; + char lskey[30]; + bool stale; +}; + +struct ZoneEXPModInfo { + float ExpMod; + float AAExpMod; +}; + +extern EntityList entity_list; +class database; +class PathManager; +struct SendAA_Struct; + +class database; + +class Zone +{ +public: + static bool Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone = false); + static void Shutdown(bool quite = false); + + Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name); + ~Zone(); + bool Init(bool iStaticZone); + bool LoadZoneCFG(const char* filename, uint16 instance_id, bool DontLoadDefault = false); + bool SaveZoneCFG(); + bool IsLoaded(); + bool IsPVPZone() { return pvpzone; } + inline const char* GetLongName() { return long_name; } + inline const char* GetFileName() { return file_name; } + inline const char* GetShortName() { return short_name; } + inline const uint32 GetZoneID() const { return zoneid; } + inline const uint32 GetInstanceID() const { return instanceid; } + inline const uint16 GetInstanceVersion() const { return instanceversion; } + + inline Timer* GetInstanceTimer() { return Instance_Timer; } + + inline const float& safe_x() { return psafe_x; } + inline const float& safe_y() { return psafe_y; } + inline const float& safe_z() { return psafe_z; } + inline const uint32& graveyard_zoneid() { return pgraveyard_zoneid; } + inline const float& graveyard_x() { return pgraveyard_x; } + inline const float& graveyard_y() { return pgraveyard_y; } + inline const float& graveyard_z() { return pgraveyard_z; } + inline const float& graveyard_heading() { return pgraveyard_heading; } + inline const uint32& graveyard_id() { return pgraveyard_id; } + + inline const uint32& GetMaxClients() { return pMaxClients; } + + void LoadAAs(); + int GetTotalAAs() { return totalAAs; } + SendAA_Struct* GetAABySequence(uint32 seq) { return aas[seq]; } + SendAA_Struct* FindAA(uint32 id); + uint8 GetTotalAALevels(uint32 skill_id); + void LoadZoneDoors(const char* zone, int16 version); + bool LoadZoneObjects(); + bool LoadGroundSpawns(); + void ReloadStaticData(); + + uint32 CountSpawn2(); + ZonePoint* GetClosestZonePoint(float x, float y, float z, const char* to_name, Client *client, float max_distance = 40000.0f); + ZonePoint* GetClosestZonePoint(float x, float y, float z, uint32 to, Client *client, float max_distance = 40000.0f); + ZonePoint* GetClosestZonePointWithoutZone(float x, float y, float z, Client *client, float max_distance = 40000.0f); + SpawnGroupList spawn_group_list; + + bool RemoveSpawnEntry(uint32 spawnid); + bool RemoveSpawnGroup(uint32 in_id); + + bool Process(); + void DumpAllSpawn2(ZSDump_Spawn2* spawn2dump, uint32* spawn2index); + uint32 DumpSpawn2(ZSDump_Spawn2* spawn2dump, uint32* spawn2index, Spawn2* spawn2); + void Despawn(uint32 spawngroupID); + + bool Depop(bool StartSpawnTimer = false); + void Repop(uint32 delay = 0); + void SpawnStatus(Mob* client); + void ShowEnabledSpawnStatus(Mob* client); + void ShowDisabledSpawnStatus(Mob* client); + void ShowSpawnStatusByID(Mob* client, uint32 spawnid); + void StartShutdownTimer(uint32 set_time = (RuleI(Zone, AutoShutdownDelay))); + void AddAuth(ServerZoneIncommingClient_Struct* szic); + void RemoveAuth(const char* iCharName); + void ResetAuth(); + bool GetAuth(uint32 iIP, const char* iCharName, uint32* oWID = 0, uint32* oAccID = 0, uint32* oCharID = 0, int16* oStatus = 0, char* oLSKey = 0, bool* oTellsOff = 0); + uint32 CountAuth(); + + void AddAggroMob() { aggroedmobs++; } + void DelAggroMob() { aggroedmobs--; } + bool AggroLimitReached() { return (aggroedmobs>10)?true:false; } // change this value, to allow more NPCs to autoaggro + int32 MobsAggroCount() { return aggroedmobs; } + inline bool InstantGrids() { return(!initgrids_timer.Enabled()); } + 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); + + void GetMerchantDataForZoneLoad(); + 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(); + void LoadLevelEXPMods(); + MercTemplate* GetMercTemplate( uint32 template_id ); + + void SetInstanceTimer(uint32 new_duration); + void LoadLDoNTraps(); + void LoadLDoNTrapEntries(); + void LoadAdventureFlavor(); + + map npctable; + map merctable; + map > merchanttable; + map > tmpmerchanttable; + map adventure_entry_list_flavor; + map ldon_trap_list; + map > ldon_trap_entry_list; + map > merc_stance_list; + map merc_templates; + map > merc_spells_list; + map level_exp_mod; + list VeteranRewards; + list AlternateCurrencies; + char *adv_data; + bool did_adventure_actions; + + void DoAdventureCountIncrease(); + void DoAdventureAssassinationCountIncrease(); + void DoAdventureActions(); + void LoadVeteranRewards(); + void LoadAlternateCurrencies(); + void LoadNPCEmotes(LinkedList* NPCEmoteList); + void ReloadWorld(uint32 Option); + + Map* zonemap; + WaterMap* watermap; + PathManager *pathing; + NewZone_Struct newzone_data; + uint8 zone_weather; + + SpawnConditionManager spawn_conditions; + + EQTime zone_time; + void GetTimeSync(); + void SetDate(uint16 year, uint8 month, uint8 day, uint8 hour, uint8 minute); + void SetTime(uint8 hour, uint8 minute); + + void weatherSend(); + bool CanBind() const { return(can_bind); } + bool IsCity() const { return(is_city); } + bool CanDoCombat() const { return(can_combat); } + bool CanLevitate() const {return(can_levitate); } // Magoth78 + bool CanCastOutdoor() const {return(can_castoutdoor);} //qadar + bool IsHotzone() const { return(is_hotzone); } + inline bool BuffTimersSuspended() const { return newzone_data.SuspendBuffs != 0; }; + + time_t weather_timer; + uint8 weather_type; + + uint8 loglevelvar; + uint8 merchantvar; + uint8 tradevar; + uint8 lootvar; + + bool HasGraveyard(); + void SetGraveyard(uint32 zoneid, uint32 x, uint32 y, uint32 z, uint32 heading); + + void LoadBlockedSpells(uint32 zoneid); + void ClearBlockedSpells(); + bool IsSpellBlocked(uint32 spell_id, float nx, float ny, float nz); + const char *GetSpellBlockedMessage(uint32 spell_id, float nx, float ny, float nz); + int GetTotalBlockedSpells() { return totalBS; } + inline bool HasMap() { return zonemap != NULL; } + inline bool HasWaterMap() { return watermap != NULL; } + + QGlobalCache *GetQGlobals() { return qGlobals; } + QGlobalCache *CreateQGlobals() { qGlobals = new QGlobalCache(); return qGlobals; } + void UpdateQGlobal(uint32 qid, QGlobal newGlobal); + void DeleteQGlobal(std::string name, uint32 npcID, uint32 charID, uint32 zoneID); + + LinkedList spawn2_list; + LinkedList zone_point_list; + uint32 numzonepoints; + + LinkedList NPCEmoteList; + +private: + uint32 zoneid; + uint32 instanceid; + uint16 instanceversion; + char* short_name; + char file_name[16]; + char* long_name; + char* map_name; + bool pvpzone; + float psafe_x, psafe_y, psafe_z; + uint32 pMaxClients; + bool can_bind; + bool is_city; + bool can_combat; + bool can_castoutdoor; + bool can_levitate; + bool is_hotzone; + uint32 pgraveyard_id, pgraveyard_zoneid; + float pgraveyard_x, pgraveyard_y, pgraveyard_z, pgraveyard_heading; + int default_ruleset; + + int totalBS; + ZoneSpellsBlocked *blocked_spells; + + int totalAAs; + SendAA_Struct **aas; //array of AA structs + + /* + Spawn related things + */ + int32 aggroedmobs; + Timer initgrids_timer; //delayed loading of initial grids. + + + bool staticzone; + bool gottime; + + uint32 pQueuedMerchantsWorkID; + uint32 pQueuedTempMerchantsWorkID; + + Timer autoshutdown_timer; + Timer clientauth_timer; + Timer spawn2_timer; + Timer qglobal_purge_timer; + Timer* Weather_Timer; + Timer* Instance_Timer; + Timer* Instance_Shutdown_Timer; + Timer* Instance_Warning_timer; + LinkedList client_auth_list; + QGlobalCache *qGlobals; + + Mutex MZoneLock; +}; + +#endif + diff --git a/zone/zone_logsys.cpp b/zone/zone_logsys.cpp new file mode 100644 index 000000000..efa09a855 --- /dev/null +++ b/zone/zone_logsys.cpp @@ -0,0 +1,78 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2006 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/logsys.h" +#include "../common/BasePacket.h" +#include "mob.h" +#include +#include + +void log_message_mob(LogType type, Mob *who, const char *fmt, ...) { + if(!who->IsLoggingEnabled()) + return; //could prolly put this in the macro, but it feels even dirtier than prototyping this in common + + char prefix_buffer[256]; + snprintf(prefix_buffer, 255, "[%s] %s: ", log_type_info[type].name, who->GetName()); + prefix_buffer[255] = '\0'; + + va_list args; + va_start(args, fmt); + LogFile->writePVA(EQEMuLog::Debug, prefix_buffer, fmt, args); + va_end(args); +} + +void log_message_mobVA(LogType type, Mob *who, const char *fmt, va_list args) { + if(!who->IsLoggingEnabled()) + return; //could prolly put this in the macro, but it feels even dirtier than prototyping this in common + + char prefix_buffer[256]; + snprintf(prefix_buffer, 255, "[%s] %s: ", log_type_info[type].name, who->GetName()); + prefix_buffer[255] = '\0'; + + LogFile->writePVA(EQEMuLog::Debug, prefix_buffer, fmt, args); +} + +void log_hex_mob(LogType type, Mob *who, const char *data, uint32 length, uint8 padding) { + if(!who->IsLoggingEnabled()) + return; //could prolly put this in the macro, but it feels even dirtier than prototyping this in common + + log_hex(type,data,length,padding); +} + +void log_packet_mob(LogType type, Mob *who, const BasePacket *p) { + if(!who->IsLoggingEnabled()) + return; //could prolly put this in the macro, but it feels even dirtier than prototyping this in common + + char buffer[80]; + p->build_header_dump(buffer); + log_message(type,"[%s] %s: %s", log_type_info[type].name, who->GetName(), buffer); + log_hex(type,(const char *)p->pBuffer,p->size); +} + + + + + + + + + + + + diff --git a/zone/zone_profile.cpp b/zone/zone_profile.cpp new file mode 100644 index 000000000..1c7ce9f6f --- /dev/null +++ b/zone/zone_profile.cpp @@ -0,0 +1,303 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 "features.h" + +#ifdef EQPROFILE +#include "zone_profile.h" +#include "../common/common_profile.h" +#include "../common/timer.h" +#include "../common/rdtsc.h" + +#include +#include +#ifdef WIN32 + #include + + #define snprintf _snprintf + #define vsnprintf _vsnprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#else + #include + #include + #include +#endif + +#include +#include + +using namespace std; + +#ifdef COMMON_PROFILE +CommonProfiler _cp; +#endif +#ifdef ZONE_PROFILE +ZoneProfiler _zp; +#endif + +class _DZP_Data { +public: + _DZP_Data(const char *_str, unsigned long long _count, double _dur) { + str = _str; + count = _count; + dur = _dur; + } + + const char *str; + unsigned long long count; + double dur; +}; + +bool operator<(const _DZP_Data &l, const _DZP_Data &r) { + return(l.dur < r.dur); +} + +RDTSC_Collector __DZP_timer(true); + +#ifdef COMMON_PROFILE +/* + +*/ +const char *__cp_names[CommonProfiler::MaxCommonProfilerId] = { + "Database_SaveInventory = 0", + "Database_StoreCharacter", + "Database_GetCharacterInfoForLogin", + "Database_GetCharacterInfoForLogin_result", + "Database_GetPlayerProfile", + "Database_GetInventory", + "Database_GetInventory_name", + "Database_SetPlayerProfile", + "Database_DBLoadItems", + "Database_GetWaypoints", + "Database_DBLoadNPCFactionLists", + + "DBcore_RunQuery", + + "DBAsync_ProcessWork", + "DBAsync_DispatchWork", + "DBAsyncLoop_loop", + + "EQStreamServer_Process", + + "EQStream_Process", + + "EQStreamServerLoop", + "EQStreamInLoop", + "EQStreamOutLoop", + "TCPServerLoop", + "TCPConnectionLoop", + + "Inventory_GetItem", + "Inventory_HasItem", + + "MakeRandomFloat", + + "Mutex_lock", + "Timer_Check", + + "WorldConnection_Process" +}; +#endif + +#ifdef ZONE_PROFILE +const char *__zp_names[ZoneProfiler::MaxZoneProfilerId] = { + "Client_Process = 0", + "Client_HandlePacket", + "Client_QueuePacket", + "Client_Save", + "Client_Attack", + "Client_CalcBonuses", + "Client_GetFactionLevel", + "Client_SetFactionLevel", + "Client_SetFactionLevel2", + + "NPC_Attack", + "NPC_GetReverseFactionCon", + "NPC_Process", + "NPC_Death", +#ifdef EQBOTS + "NPC_BotAttackMelee", +#endif + + "EntityList_TrapProcess", + "EntityList_GroupProcess", + "EntityList_QueueToGroupsForNPCHealthAA", + "EntityList_DoorProcess", + "EntityList_ObjectProcess", + "EntityList_CorpseProcess", + "EntityList_MobProcess", + "EntityList_BeaconProcess", + "EntityList_Process", + "EntityList_RaidProcess", + "EntityList_AICheckCloseAggro", + "EntityList_AICheckCloseBeneficialSpells", + "EntityList_CheckClientAggro", + "EntityList_CheckClientAggro_Loop", + "EntityList_AIYellForHelp", +#ifdef EQBOTS + "EntityList_Bot_AICheckCloseBeneficialSpells", +#endif + + "HateList_Find", + "HateList_GetDamageTop", + "HateList_GetClosest", + "HateList_DoFactionHits", + "HateList_GetTop", + "HateList_IsEmpty", + "HateList_GetMostHate", + + "Mob_CheckWillAggro", + "Mob_CheckLosFN", + "Mob_Dist", + "Mob_DistNoZ", + "Mob_DistNoRoot", + "Mob_DistNoRootNoZ", + "Mob_AICastSpell", + "Mob_SpellEffect", + "Mob_DoBuffTic", + "Mob_CastSpell", + "Mob_DoCastSpell", + "Mob_CastedSpellFinished", + "Mob_SpellFinished", + "Mob_IsImmuneToSpell", + "Mob_CalculateNewPosition", + "Mob_CalculateNewPosition2", + "Mob_GetWeaponDamageA", + "Mob_GetWeaponDamage", + "Mob_GetWeaponDamageBonus", + "Mob_TryWeaponProcA", + "Mob_TryWeaponProcB", +#ifdef EQBOTS + "NPC_Bot_AICastSpell", + "Bot_AI_Process_pursue_cast", + "Bot_PET_Process_IsEngaged", +#endif + + "Map_LineIntersectsZone", + "Map_LineIntersectsNode", + "Map_FindBestZ", + + "Pathing_FindRoute_FromNodes", + "Pathing_CheckTerrainPassable", + "Pathing_FindRoute_FromVertices", + "Pathing_UpdatePath", + "Pathing_NoHazards", + "Pathing_OpenDoors", + "Pathing_VertexDistance", + "Pathing_VertexDistanceNoRoot", + + "Mob_AI_Process", + "Mob_AI_Process_engaged", + "Mob_AI_Process_engaged_cast", + "Mob_AI_Process_pursue_cast", + "Mob_AI_Process_autocast", + "Mob_AI_Process_scanarea", + "Mob_AI_Process_move", + "Mob_AI_Process_pet", + "Mob_AI_Process_roambox", + "Mob_AI_Process_roamer", + "Mob_AI_Process_guard", +#ifdef EQBOTS + "Mob_BOT_Process", + "Mob_BOT_Process_IsEngaged", + "Bot_AI_Process_engaged_cast", + "NPC_Bot_AI_IdleCastCheck", +#endif + + "Database_AddLootTableToNPC", + + "Zone_Bootup", + "Zone_Process", + + "WorldServer_Process", + + "Spawn2_Process", + + "PerlembParser_SendCommands", + "PerlXSParser_SendCommands", + + "command_realdispatch", + "net_main" +}; +#endif + +void DumpZoneProfile() { + __DZP_timer.stop(); + time_t aclock; + struct tm *newtime; + + time( &aclock ); /* Get time in seconds */ + newtime = localtime( &aclock ); /* Convert time to struct */ + LogFile->write(EQEMuLog::Debug, "Profiling dump at: [%02d/%02d - %02d:%02d:%02d] (%.2f ms of data)", + newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec, + __DZP_timer.getTotalDuration()); + + vector<_DZP_Data> data; + + + int r; +#ifdef COMMON_PROFILE + //Dump common profile + for(r = 0; r < CommonProfiler::MaxCommonProfilerId; r++) { + data.push_back(_DZP_Data(__cp_names[r], _cp.getCount(r), _cp.getTotalDuration(r))); + } +#endif + +#ifdef ZONE_PROFILE + //Dump zone profile + for(r = 0; r < ZoneProfiler::MaxZoneProfilerId; r++) { + data.push_back(_DZP_Data(__zp_names[r], _zp.getCount(r), _zp.getTotalDuration(r))); + } +#endif + + sort(data.begin(), data.end()); + + vector<_DZP_Data>::iterator cur,end; + cur = data.begin(); + end = data.end(); + + for(; cur != end; cur++) { + if(cur->count == 0) + continue; //dont print empty timers. + + LogFile->write(EQEMuLog::Debug, "..%s: %llu calls, %.4fms", cur->str, (unsigned long long)cur->count, cur->dur); + } + + LogFile->write(EQEMuLog::Debug, "End Profiling dump at: [%02d/%02d - %02d:%02d:%02d] (%.2f ms of data)", + newtime->tm_mon+1, newtime->tm_mday, newtime->tm_hour, newtime->tm_min, newtime->tm_sec, + __DZP_timer.getTotalDuration()); + + __DZP_timer.start(); +} + +void ResetZoneProfile() { + LogFile->write(EQEMuLog::Debug, "Profiling information reset..."); +#ifdef COMMON_PROFILE + _cp.reset(); +#endif +#ifdef ZONE_PROFILE + _zp.reset(); +#endif + __DZP_timer.reset(); +} + +#endif //EQPROFILE + + + diff --git a/zone/zone_profile.h b/zone/zone_profile.h new file mode 100644 index 000000000..59fcc7a3c --- /dev/null +++ b/zone/zone_profile.h @@ -0,0 +1,165 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2004 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 ZONE_PROFILE_H +#define ZONE_PROFILE_H + +#ifdef ZONE //only possibly profile if we are building zone +#include "features.h" +#endif + +#ifdef EQPROFILE + extern void DumpZoneProfile(); + extern void ResetZoneProfile(); + #ifdef PROFILE_DUMP_TIME + extern void ProfilerProcess(); + #endif +#else + #ifdef ZONE_PROFILE //no EQPROFILE == no zone profile + #undef ZONE_PROFILE + #endif +#endif + +#ifdef ZONE_PROFILE + +#include "../common/profiler.h" + +class ZoneProfiler : public GeneralProfiler { +public: + enum { + Client_Process = 0, + Client_HandlePacket, + Client_QueuePacket, + Client_Save, + Client_Attack, + Client_CalcBonuses, + Client_GetFactionLevel, + Client_SetFactionLevel, + Client_SetFactionLevel2, + + NPC_Attack, + NPC_GetReverseFactionCon, + NPC_Process, + NPC_Death, + + EntityList_TrapProcess, + EntityList_GroupProcess, + EntityList_QueueToGroupsForNPCHealthAA, + EntityList_DoorProcess, + EntityList_ObjectProcess, + EntityList_CorpseProcess, + EntityList_MobProcess, + EntityList_BeaconProcess, + EntityList_Process, + EntityList_RaidProcess, + EntityList_AICheckCloseAggro, + EntityList_AICheckCloseBeneficialSpells, + EntityList_CheckClientAggro, + EntityList_CheckClientAggro_Loop, + EntityList_AIYellForHelp, + + HateList_Find, + HateList_GetDamageTop, + HateList_GetClosest, + HateList_DoFactionHits, + HateList_GetTop, + HateList_IsEmpty, + HateList_GetMostHate, + + Mob_CheckWillAggro, + Mob_CheckLosFN, + Mob_Dist, + Mob_DistNoZ, + Mob_DistNoRoot, + Mob_DistNoRootNoZ, + Mob_AICastSpell, + Mob_SpellEffect, + Mob_DoBuffTic, + Mob_CastSpell, + Mob_DoCastSpell, + Mob_CastedSpellFinished, + Mob_SpellFinished, + Mob_IsImmuneToSpell, + Mob_CalculateNewPosition, + Mob_CalculateNewPosition2, + Mob_GetWeaponDamageA, + Mob_GetWeaponDamageB, + Mob_GetWeaponDamageBonus, + Mob_TryWeaponProcA, + Mob_TryWeaponProcB, + + Map_LineIntersectsZone, + Map_LineIntersectsNode, + Map_FindBestZ, + + Pathing_FindRoute_FromNodes, + Pathing_CheckTerrainPassable, + Pathing_FindRoute_FromVertices, + Pathing_UpdatePath, + Pathing_NoHazards, + Pathing_OpenDoors, + Pathing_VertexDistance, + Pathing_VertexDistanceNoRoot, + + Mob_AI_Process, + Mob_AI_Process_engaged, + Mob_AI_Process_engaged_cast, + Mob_AI_Process_pursue_cast, + Mob_AI_Process_autocast, + Mob_AI_Process_scanarea, + Mob_AI_Process_move, + Mob_AI_Process_pet, + Mob_AI_Process_roambox, + Mob_AI_Process_roamer, + Mob_AI_Process_guard, + + + + Database_AddLootTableToNPC, + + Zone_Bootup, + Zone_Process, + + WorldServer_Process, + + Spawn2_Process, + + PerlembParser_SendCommands, + PerlXSParser_SendCommands, + + command_realdispatch, + + net_main, + + MaxZoneProfilerId + }; + + inline ZoneProfiler() : GeneralProfiler(MaxZoneProfilerId) { } + +}; + +extern ZoneProfiler _zp; + +#define _ZP(name) _GP(_zp, ZoneProfiler, name) +#else + //no zone profiling, dummy functions +#define _ZP(name) ; + +#endif //ZONE_PROFILE + +#endif + diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp new file mode 100644 index 000000000..f955b1cd4 --- /dev/null +++ b/zone/zonedb.cpp @@ -0,0 +1,2614 @@ + +#include "zonedb.h" +#include "../common/Item.h" +#include "../common/MiscFunctions.h" +#include "../common/extprofile.h" +#include "../common/guilds.h" +#include "../common/rulesys.h" +#include "zone.h" +#include "client.h" +#include "groups.h" +#include "raids.h" +#include +#include +#include + +using namespace std; + +extern Zone* zone; + +ZoneDatabase database; + +ZoneDatabase::ZoneDatabase() +: SharedDatabase() +{ + ZDBInitVars(); +} + +ZoneDatabase::ZoneDatabase(const char* host, const char* user, const char* passwd, const char* database, uint32 port) +: SharedDatabase(host, user, passwd, database, port) +{ + ZDBInitVars(); +} + +void ZoneDatabase::ZDBInitVars() { + memset(item_minstatus, 0, sizeof(item_minstatus)); + memset(door_isopen_array, 0, sizeof(door_isopen_array)); + npc_spells_maxid = 0; + npc_spells_cache = 0; + npc_spells_loadtried = 0; + max_faction = 0; + faction_array = NULL; +} + +ZoneDatabase::~ZoneDatabase() { + unsigned int x; + if (npc_spells_cache) { + for (x=0; x<=npc_spells_maxid; x++) { + safe_delete_array(npc_spells_cache[x]); + } + safe_delete_array(npc_spells_cache); + } + safe_delete_array(npc_spells_loadtried); + + if (faction_array != NULL) { + for (x=0; x <= max_faction; x++) { + if (faction_array[x] != 0) + safe_delete(faction_array[x]); + } + safe_delete_array(faction_array); + } +} + +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; + } + 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, 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" + " 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; + 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++]); + } + 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); +} + +//updates or clears the respawn time in the database for the current spawn id +void ZoneDatabase::UpdateSpawn2Timeleft(uint32 id, uint16 instance_id, uint32 timeleft) +{ + timeval tv; + gettimeofday(&tv, NULL); + 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); + } + 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, NULL); + 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); + return 0; + } + return 0; +} + +void ZoneDatabase::UpdateSpawn2Status(uint32 id, uint8 new_status) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + 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]; + char* targetarr = new char[2*len2+1]; + memset(descriptiontext, 0, 2*len+1); + 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)) { + cerr << "Error in logevents" << query << "' " << errbuf << endl; + safe_delete_array(query); + 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; + + + uint32 len = strlen(bug->bug); + char* bugtext = NULL; + if(len > 0) + { + bugtext = new char[2*len+1]; + memset(bugtext, 0, 2*len+1); + DoEscapeString(bugtext, bug->bug, len); + } + + len = strlen(bug->ui); + char* uitext = NULL; + if(len > 0) + { + uitext = new char[2*len+1]; + memset(uitext, 0, 2*len+1); + DoEscapeString(uitext, bug->ui, len); + } + + len = strlen(bug->target_name); + char* targettext = NULL; + if(len > 0) + { + targettext = new char[2*len+1]; + memset(targettext, 0, 2*len+1); + DoEscapeString(targettext, bug->target_name, len); + } + + //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==NULL?"":uitext, bug->y, bug->x, bug->z, bug->chartype, bug->type, targettext==NULL?"Unknown Target":targettext, + bugtext==NULL?"":bugtext), errbuf)) { + cerr << "Error in UpdateBug" << query << "' " << errbuf << endl; + } + safe_delete_array(query); + safe_delete_array(bugtext); + safe_delete_array(uitext); + safe_delete_array(targettext); +} + +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)) { + cerr << "Error in UpdateBug" << query << "' " << errbuf << endl; + } + safe_delete_array(query); + safe_delete_array(bugtext); +} + + +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); + return false; + } + safe_delete_array(query); + + if (affected_rows == 0) { + return false; + } + + return true; +} + +bool ZoneDatabase::DoorIsOpen(uint8 door_id,const char* zone_name) +{ + if(door_isopen_array[door_id] == 0) { + SetDoorPlace(1,door_id,zone_name); + return false; + } + else { + SetDoorPlace(0,door_id,zone_name); + return true; + } +} + +void ZoneDatabase::SetDoorPlace(uint8 value,uint8 door_id,const char* zone_name) +{ + door_isopen_array[door_id] = value; +} + +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); + else if(account_id != 0) + sprintf(modifications,"accountid=%i",account_id); + + if(strlen(target) != 0) + sprintf(modifications,"%s AND target like \'%%%s%%\'",modifications,target); + + if(strlen(detail) != 0) + sprintf(modifications,"%s AND description like \'%%%s%%\'",modifications,detail); + + if(strlen(timestamp) != 0) + 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); + } +} + +// Load child objects for a world container (i.e., forge, bag dropped to ground, etc) +void ZoneDatabase::LoadWorldContainer(uint32 parentid, ItemInst* container) +{ + if (!container) { + LogFile->write(EQEMuLog::Error, "Programming error: LoadWorldContainer passed NULL pointer"); + return; + } + + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + //const Item_Struct* item = NULL; + //ItemInst* inst = NULL; + + uint32 len_query = MakeAnyLenString(&query, "select " + "bagidx,itemid,charges,augslot1,augslot2,augslot3,augslot4,augslot5 from object_contents where parentid=%i", parentid); + + 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[5]; + 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) { + if (inst->GetItem()->ItemClass == ItemClassCommon) { + for(int i=0;i<5;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) { + return; + } + //Delete all items from container + DeleteWorldContainer(parent_id,zone_id); + // Save all 10 items, if they exist + for (uint8 index=0; index<10; index++) { + ItemInst* inst = container->GetItem(index); + if (inst) { + uint32 item_id = inst->GetItem()->ID; + uint32 augslot[5] = { 0, 0, 0, 0, 0 }; + if (inst->IsType(ItemClassCommon)) { + for(int i=0;i<5;i++) { + ItemInst *auginst=inst->GetAugment(i); + augslot[i]=(auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; + } + } + uint32 len_query = MakeAnyLenString(&query, + + "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]); + + if (!RunQuery(query, len_query, errbuf)) { + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::SaveWorldContainer: %s", errbuf); + } + safe_delete_array(query); + } + + } +} + +// 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) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + + 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* 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); + _log(TRADING__CLIENT, "Failed to load trader information!\n"); + } + 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* 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); + _log(TRADING__CLIENT, "Failed to load trader information!\n"); + } + return loadti; +} + +ItemInst* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int SerialNumber) { + + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if (RunQuery(query,MakeAnyLenString(&query, "select * from trader where char_id=%i and serialnumber=%i order by slot_id limit 80", + CharID, SerialNumber),errbuf,&result)){ + safe_delete_array(query); + + if (mysql_num_rows(result) != 1) { + _log(TRADING__CLIENT, "Bad result from query\n"); fflush(stdout); + return NULL; + } + row = mysql_fetch_row(result); + int ItemID = atoi(row[1]); + int Charges = atoi(row[3]); + int Cost = atoi(row[4]); + + const Item_Struct *item=database.GetItem(ItemID); + + if(!item) { + _log(TRADING__CLIENT, "Unable to create item\n"); fflush(stdout); + return NULL; + } + + if (item && (item->NoDrop!=0)) { + ItemInst* inst = database.CreateItem(item); + if(!inst) { + _log(TRADING__CLIENT, "Unable to create item instance\n"); fflush(stdout); + return NULL; + } + + inst->SetCharges(Charges); + inst->SetSerialNumber(SerialNumber); + inst->SetMerchantSlot(SerialNumber); + inst->SetPrice(Cost); + if(inst->IsStackable()) + inst->SetMerchantCount(Charges); + + return inst; + } + } + + return NULL; + + +} + +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); + + safe_delete_array(query); +} + +void ZoneDatabase::UpdateTraderItemCharges(int CharID, uint32 SerialNumber, int32 Charges) { + + _log(TRADING__CLIENT, "ZoneDatabase::UpdateTraderItemCharges(%i, %i, %i)", CharID, SerialNumber, Charges); + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + if (!(RunQuery(query,MakeAnyLenString(&query, "update trader set charges=%i where char_id=%i and serialnumber=%i", + Charges, CharID, SerialNumber),errbuf))) + _log(TRADING__CLIENT, "Failed to update charges for trader item: %i for char_id: %i, the error was: %s\n", + SerialNumber, CharID, errbuf); + + safe_delete_array(query); + +} + +void ZoneDatabase::UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charges, uint32 NewPrice) { + + _log(TRADING__CLIENT, "ZoneDatabase::UpdateTraderPrice(%i, %i, %i, %i)", CharID, ItemID, Charges, NewPrice); + + const Item_Struct *item = database.GetItem(ItemID); + + 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); + + 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))) + + _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); + } + +} + +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); + } + else{ + if (!(RunQuery(query,MakeAnyLenString(&query, "delete from trader where char_id=%i",char_id),errbuf))) + _log(TRADING__CLIENT, "Failed to delete trader item data for char_id: %i, the error was: %s\n",char_id,errbuf); + } + safe_delete_array(query); +} +void ZoneDatabase::DeleteTraderItem(uint32 CharID,uint16 SlotID){ + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + if (!(RunQuery(query,MakeAnyLenString(&query, "delete from trader where char_id=%i and slot_id=%i",CharID, SlotID),errbuf))) + _log(TRADING__CLIENT, "Failed to delete trader item data for char_id: %i, the error was: %s\n",CharID, errbuf); + safe_delete_array(query); +} + +void ZoneDatabase::DeleteBuyLines(uint32 CharID){ + + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + if(CharID==0){ + if (!(RunQuery(query,MakeAnyLenString(&query, "delete from buyer"),errbuf))) + _log(TRADING__CLIENT, "Failed to delete all buyer items data, the error was: %s\n",errbuf); + } + 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) { + + 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) { + + if(Quantity <= 0) { + RemoveBuyLine(CharID, BuySlot); + return; + } + + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + + if (!(RunQuery(query,MakeAnyLenString(&query, "update buyer set quantity=%i where charid=%i and buyslot=%i", + Quantity, CharID, BuySlot), errbuf))) + _log(TRADING__CLIENT, "Failed to update quantity in buyslot %i for charid: %i, the error was: %s\n", BuySlot, CharID, errbuf); + + safe_delete_array(query); + +} + +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) { + _CP(Database_GetCharacterInfoForLogin); + 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) { + _CP(Database_GetCharacterInfoForLogin_result); + + MYSQL_ROW row; + unsigned long* lengths; + + 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(NULL); + + if (pp->x == -1 && pp->y == -1 && pp->z == -1) + GetSafePoints(pp->zone_id, database.GetInstanceVersion(pp->zoneInstance), &pp->x, &pp->y, &pp->z); + } + + 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]); + + if (guilddbid) { + if(row[6] != NULL) + *guilddbid = atoi(row[6]); + else + *guilddbid = GUILD_NONE; + } + if (guildrank) { + if(row[7] != NULL) + *guildrank = atoi(row[7]); + else + *guildrank = GUILD_RANK_NONE; + } + + if(ext) { + //SetExtendedProfile handles any conversion + SetExtendedProfile(ext, row[8], lengths[8]); + } + + if(class_) + *class_ = atoi(row[9]); + + if(level) + *level = atoi(row[10]); + + if(LFP) + *LFP = atoi(row[11]); + + if(LFG) + *LFG = atoi(row[12]); + + if(NumXTargets) + { + *NumXTargets = atoi(row[14]); + } + + + 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 = 0; i<9; 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; +} + +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; +} + + +void ZoneDatabase::LoadItemStatus() { + memset(item_minstatus, 0, sizeof(item_minstatus)); + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + uint32 tmp; + if (RunQuery(query, MakeAnyLenString(&query, "Select id, minstatus from items where minstatus > 0"), errbuf, &result)) { + safe_delete_array(query); + while ((row = mysql_fetch_row(result)) && row[0] && row[1]) { + tmp = atoi(row[0]); + if (tmp < MAX_ITEM_ID) + item_minstatus[tmp] = atoi(row[1]); + } + mysql_free_result(result); + } + else { + cout << "Error in LoadItemStatus query: '" << query << "'" << endl; + safe_delete_array(query); + } +} + +bool ZoneDatabase::DBSetItemStatus(uint32 id, uint8 status) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + if (!RunQuery(query, MakeAnyLenString(&query, "Update items set minstatus=%u where id=%u", status, id), errbuf, 0, &affected_rows)) { + cout << "Error in LoadItemStatus query: '" << query << "'" << endl; + } + safe_delete_array(query); + return (bool) (affected_rows == 1); +} + + +/* Searches npctable for matching id, and returns the item if found, + * or NULL otherwise. If id passed is 0, loads all npc_types for + * the current zone, returning the last item added. + */ +const NPCType* ZoneDatabase::GetNPCType (uint32 id) { + const NPCType *npc=NULL; + map::iterator itr; + + // If NPC is already in tree, return it. + if((itr = zone->npctable.find(id)) != 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; + + + // 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.mindmg," + "npc_types.maxdmg," + "npc_types.attack_count," + "npc_types.npcspecialattks," + "npc_types.npc_spells_id," + "npc_types.d_meele_texture1," + "npc_types.d_meele_texture2," + "npc_types.prim_melee_type," + "npc_types.sec_melee_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.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.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"; + + MakeAnyLenString(&query, "%s FROM npc_types WHERE id=%d", basic_query, id); + + 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++]); + + strn0cpy(tmpNPCType->name, row[r++], 50); + + 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++]); + strcpy(tmpNPCType->npc_attacks,row[r++]); + 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->aggroradius = (int32)atoi(row[r++]); + // set defaultvalue for aggroradius + if (tmpNPCType->aggroradius <= 0) + tmpNPCType->aggroradius = 70; + + if (row[r] && strlen(row[r])) + tmpNPCType->bodytype = (uint8)atoi(row[r]); + else + tmpNPCType->bodytype = 0; + r++; + + tmpNPCType->npc_faction_id = atoi(row[r++]); + + 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 = NULL; + MYSQL_RES *at_result = NULL; + MYSQL_ROW at_row; + + 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); + + if (RunQuery(at_query, strlen(at_query), at_errbuf, &at_result)) + { + if ((at_row = mysql_fetch_row(at_result))) + { + for (i = 0; i < MAX_MATERIALS; 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 (at_result) + { + mysql_free_result(at_result); + } + + safe_delete_array(at_query); + } + else + { + armor_tint_id = 0; + } + } + + if (armor_tint_id == 0) + { + for (i = 1; i < MAX_MATERIALS; 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] != NULL) + 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 = 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++]); + + // 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()) + { + cerr << "Error loading duplicate NPC " << tmpNPCType->npc_id << endl; + delete tmpNPCType; + npc = NULL; + } else { + zone->npctable[tmpNPCType->npc_id]=tmpNPCType; + npc = tmpNPCType; + } + +// Sleep(0); + } + + if (result) { + mysql_free_result(result); + } + } else + cerr << "Error loading NPCs from database. Bad query: " << errbuf << endl; + safe_delete_array(query); + + return npc; +} + + +const NPCType* ZoneDatabase::GetMercType(uint32 id, uint16 raceid, uint32 clientlevel) { + const NPCType *npc=NULL; + 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()) + return itr->second; + //If the NPC type is 0, return NULL. (sanity check) + if(id == 0) + return NULL; + + // 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. + 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.npcspecialattks," + // "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"; + + 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. + + 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++]); + + strn0cpy(tmpNPCType->name, row[r++], 50); + + 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++]); + strcpy(tmpNPCType->npc_attacks,row[r++]); + //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->aggroradius = (int32)atoi(row[r++]); + tmpNPCType->aggroradius = RuleI(Mercs, AggroRadius); + // set defaultvalue for aggroradius + //if (tmpNPCType->aggroradius <= 0) + // tmpNPCType->aggroradius = 70; + + if (row[r] && strlen(row[r])) + tmpNPCType->bodytype = (uint8)atoi(row[r]); + else + tmpNPCType->bodytype = 1; + r++; + + //tmpNPCType->npc_faction_id = atoi(row[r++]); + + //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 = NULL; + MYSQL_RES *at_result = NULL; + MYSQL_ROW at_row; + + 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); + + if (RunQuery(at_query, strlen(at_query), at_errbuf, &at_result)) + { + if ((at_row = mysql_fetch_row(at_result))) + { + for (i = 0; i < MAX_MATERIALS; 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 (at_result) + { + mysql_free_result(at_result); + } + + safe_delete_array(at_query); + } + else + { + armor_tint_id = 0; + } + } + + if (armor_tint_id == 0) + { + for (i = 1; i < MAX_MATERIALS; 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] != NULL) + // 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 = NULL; + } else { + zone->merctable[tmpNPCType->npc_id * 100 + clientlevel]=tmpNPCType; + npc = tmpNPCType; + } + +// Sleep(0); + } + + if (result) { + mysql_free_result(result); + } + } else + cerr << "Error loading NPCs from database. Bad query: " << errbuf << endl; + safe_delete_array(query); + + return npc; +} + + +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 { + cerr << "Error in GetGridType query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } + return type; +} + + + +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)) { + cerr << "Error in SaveMerchantTemp query '" << query << "' " << errbuf << endl; + } + safe_delete_array(query); +} +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)) { + cerr << "Error in DeleteMerchantTemp query '" << query << "' " << errbuf << endl; + } + safe_delete_array(query); +} + + +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); + 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 + { + + cerr << "Error in GetUseCFGSafeCoords query '" << query << "' " << errbuf << 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 { + cerr << "Player profile length mismatch in ServerSideFilters" << 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 { + cerr << "Error in ServerSideFilters query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return 0; + } + + return 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)) { + cerr << "Error in SetServerSideFilters query " << errbuf << 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 { + cerr << "Error in GetZoneTZ query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } + return 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 { + cerr << "Error in SetZoneTZ query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + return false; +} +//End new timezone functions. + + +//Functions for weather +uint8 ZoneDatabase::GetZoneWeather(uint32 zoneid, uint32 version) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + if (RunQuery(query, MakeAnyLenString(&query, "SELECT weather FROM zone WHERE zoneidnumber=%i AND (version=%i OR version=0) ORDER BY version DESC", zoneid), errbuf, &result)) + { + safe_delete_array(query); + if (mysql_num_rows(result) > 0) { + row = mysql_fetch_row(result); + uint8 tmp = atoi(row[0]); + mysql_free_result(result); + return tmp; + } + mysql_free_result(result); + } + + else { + cerr << "Error in GetZoneWeather query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } + return 0; +} + +bool ZoneDatabase::SetZoneWeather(uint32 zoneid, uint32 version, uint8 w) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + + if (RunQuery(query, MakeAnyLenString(&query, "UPDATE zone SET weather=%i WHERE zoneidnumber=%i AND version=%i", w, zoneid, version), errbuf, 0, &affected_rows)) { + safe_delete_array(query); + if (affected_rows == 1) + return true; + else + return false; + } + else { + cerr << "Error in SetZoneWeather query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + return false; +} +//End weather 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 + { + cerr << "Error in GetAccountInfoForLogin query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + + return false; +} + +void ZoneDatabase::RefreshGroupFromDB(Client *c){ + if(!c){ + return; + } + + Group *g = c->GetGroup(); + + if(!g){ + 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, NULL, NULL, NULL, NULL, &gu->leader_aas); + gu->NPCMarkerID = g->GetNPCMarkerID(); + + int index = 0; + if (RunQuery(query, MakeAnyLenString(&query, "SELECT name from group_id where groupid=%d", g->GetID()), errbuf, &result)) { + while((row = mysql_fetch_row(result))){ + if(index < 6){ + if(strcmp(c->GetName(), row[0]) != 0){ + strcpy(gu->membername[index], row[0]); + index++; + } + } + } + mysql_free_result(result); + } + else + { + printf("Error in group update query: %s\n", errbuf); + } + safe_delete_array(query); + + c->QueuePacket(outapp); + safe_delete(outapp); + + if(c->GetClientVersion() >= EQClientSoD) { + g->NotifyMainTank(c, 1); + g->NotifyPuller(c, 1); + } + + g->NotifyMainAssist(c, 1); + + g->NotifyMarkNPC(c); + g->NotifyAssistTarget(c); + g->NotifyTankTarget(c); + g->NotifyPullerTarget(c); + g->SendMarkedNPCsToMember(c); + +} + +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))!=NULL) + 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::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))!=NULL) + 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; + } + +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 != NULL && row[0] != 0) { + int32 ret = atoi(row[0]); + mysql_free_result(result); + return ret; + } + mysql_free_result(result); + } + else { + cerr << "Error in GetBlockedSpellsCount query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return -1; + } + + return -1; +} + +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) { + cerr << "Error, Blocked Spells Count of " << blockedSpellsCount << " exceeded." << 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 + { + cerr << "Error in LoadBlockedSpells query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return false; + } + return true; +} + +int ZoneDatabase::getZoneShutDownDelay(uint32 zoneID, uint32 version) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + 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]); + + mysql_free_result(result); + safe_delete_array(query); + return (retVal); + } + else { + cerr << "Error in getZoneShutDownDelay no result '" << query << "' " << errbuf << endl; + mysql_free_result(result); + safe_delete_array(query); + return (RuleI(Zone, AutoShutdownDelay)); + } + } + else { + cerr << "Error in getZoneShutDownDelay query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } + return (RuleI(Zone, AutoShutdownDelay)); +} + +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); + return 0; + } + + safe_delete_array(query); + row = mysql_fetch_row(result); + + ret_val = atoi(row[0]); + + mysql_free_result(result); + + return ret_val; +} + +void ZoneDatabase::UpdateKarma(uint32 acct_id, uint32 amount) +{ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + uint32 affected_rows = 0; + + 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 { + cerr << "Error in UpdateKarma query '" << query << "' " << errbuf << endl; + safe_delete_array(query); + } +} + +void ZoneDatabase::ListAllInstances(Client* c, uint32 charid) +{ + if(!c) + return; + + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + MYSQL_RES *result; + MYSQL_ROW row; + + + if (RunQuery(query,MakeAnyLenString(&query, "SELECT instance_lockout.id, zone, version FROM instance_lockout JOIN" + " instance_lockout_player ON instance_lockout.id = instance_lockout_player.id" + " WHERE instance_lockout_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])), + (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); +} + +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; + uint32 maxid; + 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)) { + cerr << "Error in InsertDoor" << query << "' " << errbuf << endl; + } + safe_delete_array(query); +} + +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); + } +} + +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); +} + +void ZoneDatabase::SaveBuffs(Client *c) { + char errbuf[MYSQL_ERRMSG_SIZE]; + char* query = 0; + + database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM `character_buffs` WHERE `character_id`='%u'", c->CharacterID()), + errbuf); + + 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, death_save_chance, " + "death_save_aa_chance) VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')", + 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].deathSaveSuccessChance, buffs[i].deathsaveCasterAARank), + 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; + } + + + 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, death_save_chance, death_save_aa_chance 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; + } + + 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 death_save_chance = atoul(row[10]); + uint32 death_save_aa_chance = atoul(row[11]); + + 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].deathSaveSuccessChance = death_save_chance; + buffs[slot_id].deathsaveCasterAARank = death_save_aa_chance; + buffs[slot_id].UpdateClient = false; + if(IsRuneSpell(spell_id)) { + c->SetHasRune(true); + } + else if(IsMagicRuneSpell(spell_id)) { + c->SetHasSpellRune(true); + } + + /* + if(IsDeathSaveSpell(spell_id)) { + c->SetDeathSaveChance(true); + } + */ + } + mysql_free_result(result); + } + else { + LogFile->write(EQEMuLog::Error, "Error in LoadBuffs query '%s': %s", query, errbuf); + safe_delete_array(query); + return; + } + + max_slots = c->GetMaxBuffSlots(); + for(int i = 0; i < max_slots; ++i) { + if(!IsValidSpell(buffs[i].spellid)) { + 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; + } + + if(cont) { + 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); + + if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM `character_pet_buffs` WHERE `char_id`=%u", c->CharacterID()), + errbuf)) { + safe_delete_array(query); + 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 + 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`) " + "values (%u, 0, '%s', %i, %u, %u, %u) " + "ON DUPLICATE KEY UPDATE `petname`='%s', `petpower`=%i, `spell_id`=%u, `hp`=%u, `mana`=%u", + c->CharacterID(), petinfo->Name, petinfo->petpower, petinfo->SpellID, petinfo->HP, petinfo->Mana, + petinfo->Name, petinfo->petpower, petinfo->SpellID, petinfo->HP, petinfo->Mana), + errbuf)) + { + safe_delete_array(query); + return; + } + safe_delete_array(query); + + for(i=0; i < RuleI(Spells, MaxTotalSlotsPET); i++) { + if (petinfo->Buffs[i].spellid != SPELL_UNKNOWN && petinfo->Buffs[i].spellid != 0) { + database.RunQuery(query, MakeAnyLenString(&query, + "INSERT INTO `character_pet_buffs` (`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, " + "`ticsremaining`, `counters`) values " + "(%u, 0, %u, %u, %u, %u, %d)", + c->CharacterID(), i, petinfo->Buffs[i].spellid, petinfo->Buffs[i].level, petinfo->Buffs[i].duration, + petinfo->Buffs[i].counters), + errbuf); + safe_delete_array(query); + } + if (suspended->Buffs[i].spellid != SPELL_UNKNOWN && suspended->Buffs[i].spellid != 0) { + database.RunQuery(query, MakeAnyLenString(&query, + "INSERT INTO `character_pet_buffs` (`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, " + "`ticsremaining`, `counters`) values " + "(%u, 1, %u, %u, %u, %u, %d)", + c->CharacterID(), i, suspended->Buffs[i].spellid, suspended->Buffs[i].level, suspended->Buffs[i].duration, + suspended->Buffs[i].counters), + errbuf); + safe_delete_array(query); + } + } + + for(i=0; iItems[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); + } + } + + + if(!database.RunQuery(query, MakeAnyLenString(&query, + "INSERT INTO `character_pet_info` (`char_id`, `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`) " + "values (%u, 1, '%s', %u, %u, %u, %u) " + "ON DUPLICATE KEY UPDATE `petname`='%s', `petpower`=%i, `spell_id`=%u, `hp`=%u, `mana`=%u", + c->CharacterID(), suspended->Name, suspended->petpower, suspended->SpellID, suspended->HP, suspended->Mana, + suspended->Name, suspended->petpower, suspended->SpellID, suspended->HP, suspended->Mana), + errbuf)) + { + safe_delete_array(query); + return; + } + safe_delete_array(query); + + for(i=0; iItems[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); + } + } + +} + +void ZoneDatabase::RemoveTempFactions(Client *c){ + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + + if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM faction_values WHERE temp = 1 AND char_id=%u", c->CharacterID()), errbuf)) { + cerr << "Error in RemoveTempFactions query '" << query << "' " << errbuf << 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; + + memset(petinfo, 0, sizeof(PetInfo)); + memset(suspended, 0, sizeof(PetInfo)); + + if(database.RunQuery(query, MakeAnyLenString(&query, + "SELECT `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana` 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]); + } + mysql_free_result(result); + } + else + { + LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query, errbuf); + safe_delete_array(query); + return; + } + + + 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; + + 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); + } + else { + LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query, errbuf); + safe_delete_array(query); + 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 < 0 || slot > MAX_WORN_INVENTORY) + continue; + + 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; + } + +} \ No newline at end of file diff --git a/zone/zonedb.h b/zone/zonedb.h new file mode 100644 index 000000000..84ba55af2 --- /dev/null +++ b/zone/zonedb.h @@ -0,0 +1,478 @@ +#ifndef ZONEDB_H_ +#define ZONEDB_H_ + +#include "../common/shareddb.h" +#include "../common/eq_packet_structs.h" +#include "loottable.h" +#include "faction.h" +//#include "doors.h" + +struct wplist { + int index; + float x; + float y; + float z; + int pause; + float heading; +}; + +#pragma pack(1) +struct DBnpcspells_entries_Struct { + int16 spellid; + uint16 type; + uint8 minlevel; + uint8 maxlevel; + int16 manacost; + int32 recast_delay; + int16 priority; + int16 resist_adjust; +}; +#pragma pack() + +struct DBnpcspells_Struct { + uint32 parent_list; + int16 attack_proc; + uint8 proc_chance; + uint32 numentries; + DBnpcspells_entries_Struct entries[0]; +}; + +struct DBTradeskillRecipe_Struct { + SkillType tradeskill; + int16 skill_needed; + uint16 trivial; + bool nofail; + bool replace_container; + vector< pair > onsuccess; + vector< pair > onfail; + string name; + uint8 must_learn; + bool has_learnt; + uint32 madecount; + uint32 recipe_id; + bool quest; +}; + +struct PetRecord { + uint32 npc_type; // npc_type id for the pet data to use + bool temporary; + int16 petpower; + uint8 petcontrol; // What kind of control over the pet is possible (Animation, familiar, ...) + uint8 petnaming; // How to name the pet (Warder, pet, random name, familiar, ...) + bool monsterflag; // flag for if a random monster appearance should get picked + uint32 equipmentset; // default equipment for the pet +}; + +// Actual pet info for a client. +struct PetInfo { + uint16 SpellID; + int16 petpower; + uint32 HP; + uint32 Mana; + SpellBuff_Struct Buffs[BUFF_COUNT]; + uint32 Items[MAX_WORN_INVENTORY]; + char Name[64]; +}; + +struct ZoneSpellsBlocked { + uint32 spellid; + int8 type; + float x; + float y; + float z; + float xdiff; + float ydiff; + float zdiff; + char message[256]; +}; + +struct TraderCharges_Struct { + uint32 ItemID[80]; + int32 SerialNumber[80]; + uint32 ItemCost[80]; + int32 Charges[80]; +}; + +const int MaxMercStanceID = 9; + +struct MercStanceInfo { + uint8 ProficiencyID; + uint8 ClassID; + uint32 StanceID; + uint8 IsDefault; +}; + +struct MercTemplate { + uint32 MercTemplateID; + uint32 MercType; // From dbstr_us.txt - Apprentice (330000100), Journeyman (330000200), Master (330000300) + uint32 MercSubType; // From dbstr_us.txt - 330020105^23^Race: Guktan
Type: Healer
Confidence: High
Proficiency: Apprentice, Tier V... + uint16 RaceID; + uint8 ClassID; + uint32 MercNPCID; + uint8 ProficiencyID; + uint8 CostFormula; // To determine cost to client + uint32 ClientVersion; // Only send valid mercs per expansion + uint8 MercNameType; // Determines if merc gets random name or default text + char MercNamePrefix[25]; + char MercNameSuffix[25]; + uint32 Stances[MaxMercStanceID]; +}; + +struct MercInfo { + MercTemplate myTemplate; + uint32 SuspendedTime; + bool IsSuspended; + uint32 MercTimerRemaining; + uint8 Gender; + int32 State; +}; + +struct MercSpellEntry { + uint8 proficiencyid; + uint16 spellid; // <= 0 = no spell + uint16 type; // 0 = never, must be one (and only one) of the defined values + int16 stance; // 0 = all, + = only this stance, - = all except this stance + uint8 minlevel; + uint8 maxlevel; + int16 slot; + uint16 proc_chance; + uint32 time_cancast; // when we can cast this spell next +}; + +struct ClientMercEntry { + uint32 id; + uint32 npcid; +}; + +class ItemInst; +struct FactionMods; +struct FactionValue; +struct LootTable_Struct; + + +class ZoneDatabase : public SharedDatabase { +public: + ZoneDatabase(); + ZoneDatabase(const char* host, const char* user, const char* passwd, const char* database,uint32 port); + virtual ~ZoneDatabase(); + + /* + * 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); + uint32 AddObject(uint32 type, uint32 icon, const Object_Struct& object, const ItemInst* inst); + void UpdateObject(uint32 id, uint32 type, uint32 icon, const Object_Struct& object, const ItemInst* inst); + void DeleteObject(uint32 id); + Ground_Spawns* LoadGroundSpawns(uint32 zone_id, int16 version, Ground_Spawns* gs); + + /* + * 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 DeleteTraderItem(uint32 char_id); + void DeleteTraderItem(uint32 char_id,uint16 slot_id); + Trader_Struct* LoadTraderItem(uint32 char_id); + TraderCharges_Struct* LoadTraderItemWithCharges(uint32 char_id); + + // 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 + */ + 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 = false, + 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 + */ + 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); + + /* + * 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, list *faction_list, int32* primary_faction = 0); // neotokyo: improve faction handling + bool SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list); // rembrant, needed for factions Dec, 16 2001 + bool LoadFactionData(); + bool LoadFactionValues(uint32 char_id, faction_map & val_list); + bool LoadFactionValues_result(MYSQL_RES* result, faction_map & val_list); + + /* + * AAs + */ + bool LoadAAEffects(); + bool LoadAAEffects2(); + bool LoadSwarmSpells(); +// uint32 GetPlayerAlternateAdv(uint32 account_id, char* name, PlayerAA_Struct* aa); + 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, int &ruleset, char **map_filename); + bool SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct* zd); + bool DumpZoneState(); + int8 LoadZoneState(const char* zonename, LinkedList& spawn2_list); + 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); + + /* + * Item + */ + void LoadItemStatus(); + inline uint8 GetItemStatus(uint32 id) { if (id < MAX_ITEM_ID) { return item_minstatus[id]; } return 0; } + inline void SetItemStatus(uint32 id, uint8 status) { if (id < MAX_ITEM_ID) { item_minstatus[id] = status; } } + bool DBSetItemStatus(uint32 id, uint8 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); + + /* + * NPCs + */ + const NPCType* GetNPCType(uint32 id); + const NPCType* GetMercType(uint32 id, uint16 raceid, uint32 clientlevel); + 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(); + DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID); + + /* + * Petitions + */ + void UpdateBug(BugStruct* bug); + void UpdateBug(PetitionBug_Struct* bug); + void DeletePetitionFromDB(Petition* wpet); + void UpdatePetitionToDB(Petition* wpet); + void InsertPetitionToDB(Petition* wpet); + void RefreshPetitionsFromDB(); + + + /* + * Merchants + */ + void SaveMerchantTemp(uint32 npcid, uint32 slot, uint32 item, uint32 charges); + void DeleteMerchantTemp(uint32 npcid, uint32 slot); + + /* + * 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 - BoB */ + uint32 GetZoneFishing(uint32 ZoneID, uint8 skill, uint32 &npc_id, uint8 &npc_chance); + void UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madecount); + + /* + * Tribute + */ + bool LoadTributes(); + + /* + * Doors + */ + uint32 MaxDoors() { return max_door_type; } + 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); + bool CheckGuildDoor(uint8 doorid,uint16 guild_id, const char* zone); + bool SetGuildDoor(uint8 doorid,uint16 guild_id, const char* zone); + uint32 GetGuildEQID(uint32 guilddbid); + void UpdateDoorGuildID(int doorid, int guild_id); + 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 + */ + + int32 GetBlockedSpellsCount(uint32 zoneid); + bool LoadBlockedSpells(int32 blockedSpellsCount, ZoneSpellsBlocked* into, uint32 zoneid); + + /* + * Traps + */ + bool LoadTraps(const char* zonename, int16 version); + char* GetTrapMessage(uint32 trap_id); + + /* + * Time + */ + uint32 GetZoneTZ(uint32 zoneid, uint32 version); + bool SetZoneTZ(uint32 zoneid, uint32 version, uint32 tz); + + /* + * Weather + */ + uint8 GetZoneWeather(uint32 zoneid, uint32 version); + bool SetZoneWeather(uint32 zoneid, uint32 version, uint8 w); + /* + * Group + */ + void RefreshGroupFromDB(Client *c); + uint8 GroupCount(uint32 groupid); + /* + * Raid + */ + uint8 RaidGroupCount(uint32 raidid, uint32 groupid); + + /* + * Instancing + */ + void ListAllInstances(Client* c, uint32 charid); + + /* + * QGlobals + */ + void QGlobalPurge(); + + /* + * 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 + */ + 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... + */ + int16 CommandRequirement(const char* commandname); + +protected: + void ZDBInitVars(); + + uint32 max_faction; + Faction** faction_array; + + uint32 npc_spells_maxid; + DBnpcspells_Struct** npc_spells_cache; + bool* npc_spells_loadtried; + uint8 item_minstatus[MAX_ITEM_ID]; + uint8 door_isopen_array[255]; +}; + +extern ZoneDatabase database; + +#endif /*ZONEDB_H_*/ + + + + + + + + + + + + diff --git a/zone/zonedbasync.cpp b/zone/zonedbasync.cpp new file mode 100644 index 000000000..f41dfccfd --- /dev/null +++ b/zone/zonedbasync.cpp @@ -0,0 +1,128 @@ +#include "../common/debug.h" +#include +using namespace std; +#include "entity.h" +#include "masterentity.h" +#include "../common/MiscFunctions.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 + cout << "Async DB.LoadVariables() failed: '" << errbuf << "'" << endl; + break; + } + default: { + cout << "Error: DispatchFinishedDBAsync(): Unknown workpt.b4" << endl; + break; + } + } + }*/ + case DBA_b4_Zone: { + if(zone == NULL) + 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: { + cout << "Error: DispatchFinishedDBAsync(): Unknown workpt.b4: " << (int) workpt.b4() << ", workpt=" << workpt << 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)) { + cout << "Error in DBAsyncCB_CharacterBackup query3 '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return true; + } + safe_delete_array(query); + } + } + else { +// cout << "Error in DBAsyncCB_CharacterBackup query1 '" << query << "' " << errbuf << endl; + safe_delete_array(query); + return true; + } + return true; +} diff --git a/zone/zonedbasync.h b/zone/zonedbasync.h new file mode 100644 index 000000000..b91389322 --- /dev/null +++ b/zone/zonedbasync.h @@ -0,0 +1,22 @@ +#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 new file mode 100644 index 000000000..a237143bd --- /dev/null +++ b/zone/zonedump.h @@ -0,0 +1,287 @@ +/* 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 +*/ +/* +Below are the blob structures for zone state dumping to the database +-Quagmire + +create table zone_state_dump (zonename varchar(16) not null primary key, spawn2_count int unsigned not null default 0, +npc_count int unsigned not null default 0, npcloot_count int unsigned not null default 0, gmspawntype_count int unsigned not null default 0, +spawn2 mediumblob, npcs mediumblob, npc_loot mediumblob, gmspawntype mediumblob, time timestamp(14)); +*/ + +#ifndef ZONEDUMP_H +#define ZONEDUMP_H +#include "faction.h" +#include "../common/eq_packet_structs.h" +#include "../common/Item.h" + +#pragma pack(1) + +struct NPCType +{ + char name[64]; + char lastname[70]; + + int32 cur_hp; + int32 max_hp; + + float size; + float runspeed; + uint8 gender; + uint16 race; + uint8 class_; + uint8 bodytype; // added for targettype support + uint8 deity; //not loaded from DB + uint8 level; + uint32 npc_id; + uint8 texture; + uint8 helmtexture; + uint32 loottable_id; + uint32 npc_spells_id; + int32 npc_faction_id; + uint32 merchanttype; + uint32 alt_currency_type; + uint32 adventure_template; + uint32 trap_template; + uint8 light; //not loaded from DB + uint16 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; + uint8 haircolor; + uint8 beardcolor; + uint8 eyecolor1; // the eyecolors always seem to be the same, maybe left and right eye? + uint8 eyecolor2; + uint8 hairstyle; + uint8 luclinface; // + uint8 beard; // + uint32 drakkin_heritage; + uint32 drakkin_tattoo; + uint32 drakkin_details; + uint32 armor_tint[MAX_MATERIALS]; + uint32 min_dmg; + uint32 max_dmg; + int16 attack_count; + char npc_attacks[30]; + uint16 d_meele_texture1; + uint16 d_meele_texture2; + uint8 prim_melee_type; + uint8 sec_melee_type; + int32 hp_regen; + int32 mana_regen; + int32 aggroradius; // added for AI improvement - neotokyo + uint8 see_invis; // See Invis flag added + bool see_invis_undead; // See Invis vs. Undead flag added + bool see_hide; + bool see_improved_hide; + bool qglobal; + bool npc_aggro; + 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. + int accuracy_rating; //10 = 1% accuracy + bool findable; //can be found with find command + bool trackable; + float slow_mitigation; // Slow mitigation % in decimal form. + uint8 maxlevel; + uint32 scalerate; + bool private_corpse; + bool unique_spawn_by_name; + bool underwater; + uint32 emoteid; + float spellscale; + float healscale; +}; + +struct ZSDump_Spawn2 { + uint32 spawn2_id; + uint32 time_left; +}; + +struct ZSDump_NPC { + uint32 spawn2_dump_index; + uint32 gmspawntype_index; + uint32 npctype_id; + int32 cur_hp; + uint8 corpse; // 0=no, 1=yes, 2=yes and locked + uint32 decay_time_left; +// needatype buffs; // decided not to save these, would be hard because if expired them on bootup, wouldnt take into account the npcai refreshing them, etc + float x; + float y; + float z; + float heading; + uint32 copper; + uint32 silver; + uint32 gold; + uint32 platinum; +}; + +struct ZSDump_NPC_Loot { + uint32 npc_dump_index; + uint16 itemid; + int8 charges; + int16 equipSlot; + uint8 minlevel; + uint8 maxlevel; +}; + +/* +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)); +*/ + +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; +}; + +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; + }; +} + +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; + uint32 drakkin_heritage; + uint32 drakkin_tattoo; + uint32 drakkin_details; + player_lootitem::ServerLootItem_Struct items[0]; +}; + +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; + char zone_name[16]; + char door_name[32]; + float pos_x; + float pos_y; + float pos_z; + float heading; + int incline; + uint8 opentype; + uint32 guild_id; + uint16 lockpick; + uint32 keyitem; + uint8 nokeyring; + uint8 trigger_door; + uint8 trigger_type; + uint32 door_param; + int invert_state; + uint16 size; + char dest_zone[16]; + uint32 dest_instance_id; + float dest_x; + float dest_y; + float dest_z; + float dest_heading; + uint8 is_ldon_door; + uint32 client_version_mask; +}; + +#pragma pack() + +#endif diff --git a/zone/zoning.cpp b/zone/zoning.cpp new file mode 100644 index 000000000..674a36e81 --- /dev/null +++ b/zone/zoning.cpp @@ -0,0 +1,857 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2005 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 "zone.h" +#include "worldserver.h" +#include "masterentity.h" +#include "../common/packet_dump.h" +#include "../common/rulesys.h" +#include "StringIDs.h" +#include "QuestParserCollection.h" + +extern WorldServer worldserver; +extern Zone* zone; + + +void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { +#ifdef BOTS + // This block is necessary to clean up any bot objects owned by a Client + Bot::ProcessClientZoneChange(this); +#endif + + zoning = true; + if (app->size != sizeof(ZoneChange_Struct)) { + LogFile->write(EQEMuLog::Debug, "Wrong size: OP_ZoneChange, size=%d, expected %d", app->size, sizeof(ZoneChange_Struct)); + return; + } + +#if EQDEBUG >= 5 + LogFile->write(EQEMuLog::Debug, "Zone request from %s", GetName()); + DumpPacket(app); +#endif + ZoneChange_Struct* zc=(ZoneChange_Struct*)app->pBuffer; + + uint16 target_zone_id = 0; + uint16 target_instance_id = zc->instanceID; + ZonePoint* zone_point = NULL; + + //figure out where they are going. + if(zc->zoneID == 0) { + //client dosent know where they are going... + //try to figure it out for them. + + switch(zone_mode) { + case EvacToSafeCoords: + case ZoneToSafeCoords: + //going to safe coords, but client dosent know where? + //assume it is this zone for now. + target_zone_id = zone->GetZoneID(); + break; + case GMSummon: + target_zone_id = zonesummon_id; + break; + case GateToBindPoint: + target_zone_id = m_pp.binds[0].zoneId; + break; + case ZoneToBindPoint: + target_zone_id = m_pp.binds[0].zoneId; + break; + case ZoneSolicited: //we told the client to zone somewhere, so we know where they are going. + target_zone_id = zonesummon_id; + break; + case ZoneUnsolicited: //client came up with this on its own. + zone_point = zone->GetClosestZonePointWithoutZone(GetX(), GetY(), GetZ(), this, ZONEPOINT_NOZONE_RANGE); + if(zone_point) { + //we found a zone point, which is a reasonable distance away + //assume that is the one were going with. + target_zone_id = zone_point->target_zone_id; + target_instance_id = zone_point->target_zone_instance; + } else { + //unable to find a zone point... is there anything else + //that can be a valid un-zolicited zone request? + + CheatDetected(MQZone, zc->x, zc->y, zc->z); + Message(13, "Invalid unsolicited zone request."); + LogFile->write(EQEMuLog::Error, "Zoning %s: Invalid unsolicited zone request to zone id '%d'.", GetName(), target_zone_id); + SendZoneCancel(zc); + return; + } + break; + }; + } + else { + // This is to allow both 6.2 and Titanium clients to perform a proper zoning of the client when evac/succor + // WildcardX 27 January 2008 + if(zone_mode == EvacToSafeCoords && zonesummon_id > 0) + target_zone_id = zonesummon_id; + else + target_zone_id = zc->zoneID; + + //if we are zoning to a specific zone unsolicied, + //then until otherwise determined, they must be zoning + //on a zone line. + if(zone_mode == ZoneUnsolicited) + { + if(target_zone_id == zone->GetZoneID()) + { + SendZoneCancel(zc); + return; + } + + zone_point = zone->GetClosestZonePoint(GetX(), GetY(), GetZ(), target_zone_id, this, ZONEPOINT_ZONE_RANGE); + //if we didnt get a zone point, or its to a different zone, + //then we assume this is invalid. + if(!zone_point || zone_point->target_zone_id != target_zone_id) { + LogFile->write(EQEMuLog::Error, "Zoning %s: Invalid unsolicited zone request to zone id '%d'.", GetName(), target_zone_id); + CheatDetected(MQGate, zc->x, zc->y, zc->z); + SendZoneCancel(zc); + return; + } + } + } + + if(target_instance_id > 0) + { + //make sure we are in it and it's unexpired. + if(!database.VerifyInstanceAlive(target_instance_id, CharacterID())) + { + Message(13, "Instance ID was expired or you were not in it."); + SendZoneCancel(zc); + return; + } + + if(!database.VerifyZoneInstance(target_zone_id, target_instance_id)) + { + Message(13, "Instance ID was %u does not go with zone id %u", target_instance_id, target_zone_id); + SendZoneCancel(zc); + return; + } + } + + //make sure its a valid zone. + const char *target_zone_name = database.GetZoneName(target_zone_id); + if(target_zone_name == NULL) { + //invalid zone... + Message(13, "Invalid target zone ID."); + LogFile->write(EQEMuLog::Error, "Zoning %s: Unable to get zone name for zone id '%d'.", GetName(), target_zone_id); + SendZoneCancel(zc); + return; + } + + //load up the safe coords, restrictions, and verify the zone name + float safe_x, safe_y, safe_z; + int16 minstatus = 0; + uint8 minlevel = 0; + char flag_needed[128]; + if(!database.GetSafePoints(target_zone_name, database.GetInstanceVersion(target_instance_id), &safe_x, &safe_y, &safe_z, &minstatus, &minlevel, flag_needed)) { + //invalid zone... + Message(13, "Invalid target zone while getting safe points."); + LogFile->write(EQEMuLog::Error, "Zoning %s: Unable to get safe coordinates for zone '%s'.", GetName(), target_zone_name); + SendZoneCancel(zc); + return; + } + + char buf[10]; + snprintf(buf, 9, "%d", target_zone_id); + buf[9] = '\0'; + parse->EventPlayer(EVENT_ZONE, this, buf, 0); + + + //handle circumvention of zone restrictions + //we need the value when creating the outgoing packet as well. + uint8 ignorerestrictions = zonesummon_ignorerestrictions; + zonesummon_ignorerestrictions = 0; + + float dest_x=0, dest_y=0, dest_z=0, dest_h; + dest_h = GetHeading(); + switch(zone_mode) { + case EvacToSafeCoords: + case ZoneToSafeCoords: + LogFile->write(EQEMuLog::Debug, "Zoning %s to safe coords (%f,%f,%f) in %s (%d)", GetName(), safe_x, safe_y, safe_z, target_zone_name, target_zone_id); + dest_x = safe_x; + dest_y = safe_y; + dest_z = safe_z; + break; + case GMSummon: + dest_x = zonesummon_x; + dest_y = zonesummon_y; + dest_z = zonesummon_z; + ignorerestrictions = 1; + break; + case GateToBindPoint: + dest_x = m_pp.binds[0].x; + dest_y = m_pp.binds[0].y; + dest_z = m_pp.binds[0].z; + break; + case ZoneToBindPoint: + dest_x = m_pp.binds[0].x; + dest_y = m_pp.binds[0].y; + dest_z = m_pp.binds[0].z; + ignorerestrictions = 1; //can always get to our bind point? seems exploitable + break; + case ZoneSolicited: //we told the client to zone somewhere, so we know where they are going. + //recycle zonesummon variables + dest_x = zonesummon_x; + dest_y = zonesummon_y; + dest_z = zonesummon_z; + break; + case ZoneUnsolicited: //client came up with this on its own. + //client requested a zoning... what are the cases when this could happen? + + //Handle zone point case: + if(zone_point != NULL) { + //they are zoning using a valid zone point, figure out coords + + //999999 is a placeholder for 'same as where they were from' + if(zone_point->target_x == 999999) + dest_x = GetX(); + else + dest_x = zone_point->target_x; + if(zone_point->target_y == 999999) + dest_y = GetY(); + else + dest_y = zone_point->target_y; + if(zone_point->target_z == 999999) + dest_z=GetZ(); + else + dest_z = zone_point->target_z; + if(zone_point->target_heading == 999) + dest_h = GetHeading(); + else + dest_h = zone_point->target_heading; + + break; + } + + //for now, there are no other cases... + + //could not find a valid reason for them to be zoning, stop it. + CheatDetected(MQZoneUnknownDest, 0.0, 0.0, 0.0); + LogFile->write(EQEMuLog::Error, "Zoning %s: Invalid unsolicited zone request to zone id '%s'. Not near a zone point.", GetName(), target_zone_name); + SendZoneCancel(zc); + return; + }; + + //OK, now we should know where were going... + + //Check some rules first. + int8 myerror = 1; //1 is succes + + //not sure when we would use ZONE_ERROR_NOTREADY + + //enforce min status and level + if (!ignorerestrictions && (Admin() < minstatus || GetLevel() < minlevel)) + { + myerror = ZONE_ERROR_NOEXPERIENCE; + } + + if(!ignorerestrictions && flag_needed[0] != '\0') { + //the flag needed string is not empty, meaning a flag is required. + if(Admin() < minStatusToIgnoreZoneFlags && !HasZoneFlag(target_zone_id)) + { + Message(13, "You do not have the flag to enter %s.", target_zone_name); + myerror = ZONE_ERROR_NOEXPERIENCE; + } + } + + //TODO: ADVENTURE ENTRANCE CHECK + + if(myerror == 1) { + //we have successfully zoned + DoZoneSuccess(zc, target_zone_id, target_instance_id, dest_x, dest_y, dest_z, dest_h, ignorerestrictions); + } else { + LogFile->write(EQEMuLog::Error, "Zoning %s: Rules prevent this char from zoning into '%s'", GetName(), target_zone_name); + SendZoneError(zc, myerror); + } +} + +void Client::SendZoneCancel(ZoneChange_Struct *zc) { + //effectively zone them right back to where they were + //unless we find a better way to stop the zoning process. + SetPortExemption(true); + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_ZoneChange, sizeof(ZoneChange_Struct)); + ZoneChange_Struct *zc2 = (ZoneChange_Struct*)outapp->pBuffer; + strcpy(zc2->char_name, zc->char_name); + zc2->zoneID = zone->GetZoneID(); + zc2->success = 1; + outapp->priority = 6; + FastQueuePacket(&outapp); + + //reset to unsolicited. + zone_mode = ZoneUnsolicited; +} + +void Client::SendZoneError(ZoneChange_Struct *zc, int8 err) +{ + LogFile->write(EQEMuLog::Error, "Zone %i is not available because target wasn't found or character insufficent level", zc->zoneID); + + SetPortExemption(true); + + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_ZoneChange, sizeof(ZoneChange_Struct)); + ZoneChange_Struct *zc2 = (ZoneChange_Struct*)outapp->pBuffer; + strcpy(zc2->char_name, zc->char_name); + zc2->zoneID = zc->zoneID; + zc2->success = err; + outapp->priority = 6; + FastQueuePacket(&outapp); + + //reset to unsolicited. + zone_mode = ZoneUnsolicited; +} + +void Client::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) { + //this is called once the client is fully allowed to zone here + //it takes care of all the activities which occur when a client zones out + + SendLogoutPackets(); + + //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); + + //set the player's coordinates in the new zone so they have them + //when they zone into it + x_pos = dest_x; //these coordinates will now be saved when ~client is called + y_pos = dest_y; + z_pos = dest_z; + heading = dest_h; // Cripp: fix for zone heading + m_pp.heading = dest_h; + m_pp.zone_id = zone_id; + m_pp.zoneInstance = instance_id; + + //Force a save so its waiting for them when they zone + Save(2); + + if (zone_id == zone->GetZoneID() && instance_id == zone->GetInstanceID()) { + // No need to ask worldserver if we're zoning to ourselves (most + // likely to a bind point), also fixes a bug since the default response was failure + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZoneChange,sizeof(ZoneChange_Struct)); + ZoneChange_Struct* zc2 = (ZoneChange_Struct*) outapp->pBuffer; + strcpy(zc2->char_name, GetName()); + zc2->zoneID = zone_id; + zc2->instanceID = instance_id; + zc2->success = 1; + outapp->priority = 6; + FastQueuePacket(&outapp); + + zone->StartShutdownTimer(AUTHENTICATION_TIMEOUT * 1000); + } else { + // vesuvias - zoneing to another zone so we need to the let the world server + //handle things with the client for a while + ServerPacket* pack = new ServerPacket(ServerOP_ZoneToZoneRequest, sizeof(ZoneToZone_Struct)); + ZoneToZone_Struct* ztz = (ZoneToZone_Struct*) pack->pBuffer; + ztz->response = 0; + ztz->current_zone_id = zone->GetZoneID(); + ztz->current_instance_id = zone->GetInstanceID(); + ztz->requested_zone_id = zone_id; + ztz->requested_instance_id = instance_id; + ztz->admin = admin; + ztz->ignorerestrictions = ignore_r; + strcpy(ztz->name, GetName()); + ztz->guild_id = GuildID(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + + //reset to unsolicited. + zone_mode = ZoneUnsolicited; + zonesummon_x = 0; + zonesummon_y = 0; + zonesummon_z = 0; + zonesummon_id = 0; + zonesummon_ignorerestrictions = 0; +} + +void Client::MovePC(const char* zonename, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm) { + ProcessMovePC(database.GetZoneID(zonename), 0, x, y, z, heading, ignorerestrictions, zm); +} + +//designed for in zone moving +void Client::MovePC(float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm) { + ProcessMovePC(zone->GetZoneID(), zone->GetInstanceID(), x, y, z, heading, ignorerestrictions, zm); +} + +void Client::MovePC(uint32 zoneID, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm) { + ProcessMovePC(zoneID, 0, x, y, z, heading, ignorerestrictions, zm); +} + +void Client::MovePC(uint32 zoneID, uint32 instanceID, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm){ + ProcessMovePC(zoneID, instanceID, x, y, z, heading, ignorerestrictions, zm); +} + + +void Client::ProcessMovePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm) +{ + // From what I have read, dragged corpses should stay with the player for Intra-zone summons etc, but we can implement that later. + ClearDraggedCorpses(); + + if(zoneID == 0) + zoneID = zone->GetZoneID(); + + if(zoneID == zone->GetZoneID() && instance_id == zone->GetInstanceID()) { + // TODO: Determine if this condition is necessary. + if(IsAIControlled()) { + GMMove(x, y, z); + return; + } + + if(GetPetID() != 0) { + //if they have a pet and they are staying in zone, move with them + Mob *p = GetPet(); + if(p != NULL){ + p->SetPetOrder(SPO_Follow); + p->GMMove(x+15, y, z); //so it dosent have to run across the map. + } + } + } + + switch(zm) { + case GateToBindPoint: + ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); + break; + case EvacToSafeCoords: + case ZoneToSafeCoords: + ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); + break; + case GMSummon: + Message(15, "You have been summoned by a GM!"); + ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); + break; + case ZoneToBindPoint: + ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); + break; + case ZoneSolicited: + ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); + break; + case SummonPC: + Message(15, "You have been summoned!"); + ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); + break; + case Rewind: + Message(15, "Rewinding to previous location."); + ZonePC(zoneID, instance_id, x, y, z, heading, ignorerestrictions, zm); + break; + default: + LogFile->write(EQEMuLog::Error, "Client::ProcessMovePC received a reguest to perform an unsupported client zone operation."); + break; + } +} + +void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm) { + bool ReadyToZone = true; + int iZoneNameLength = 0; + const char* pShortZoneName = NULL; + char* pZoneName = NULL; + + pShortZoneName = database.GetZoneName(zoneID); + database.GetZoneLongName(pShortZoneName, &pZoneName); + + SetPortExemption(true); + + if(!pZoneName) { + Message(13, "Invalid zone number specified"); + safe_delete_array(pZoneName); + return; + } + iZoneNameLength = strlen(pZoneName); + + switch(zm) { + case EvacToSafeCoords: + case ZoneToSafeCoords: + x = zone->safe_x(); + y = zone->safe_y(); + z = zone->safe_z(); + SetHeading(heading); + break; + case GMSummon: + zonesummon_x = x_pos = x; + zonesummon_y = y_pos = y; + zonesummon_z = z_pos = z; + SetHeading(heading); + + zonesummon_id = zoneID; + zonesummon_ignorerestrictions = 1; + break; + case ZoneSolicited: + zonesummon_x = x; + zonesummon_y = y; + zonesummon_z = z; + SetHeading(heading); + + zonesummon_id = zoneID; + zonesummon_ignorerestrictions = ignorerestrictions; + break; + case GateToBindPoint: + x = x_pos = m_pp.binds[0].x; + y = y_pos = m_pp.binds[0].y; + z = z_pos = m_pp.binds[0].z; + heading = m_pp.binds[0].heading; + break; + case ZoneToBindPoint: + x = x_pos = m_pp.binds[0].x; + y = y_pos = m_pp.binds[0].y; + z = z_pos = m_pp.binds[0].z; + heading = m_pp.binds[0].heading; + + zonesummon_ignorerestrictions = 1; + LogFile->write(EQEMuLog::Debug, "Player %s has died and will be zoned to bind point in zone: %s at LOC x=%f, y=%f, z=%f, heading=%f", GetName(), pZoneName, m_pp.binds[0].x, m_pp.binds[0].y, m_pp.binds[0].z, m_pp.binds[0].heading); + break; + case SummonPC: + zonesummon_x = x_pos = x; + zonesummon_y = y_pos = y; + zonesummon_z = z_pos = z; + SetHeading(heading); + break; + case Rewind: + LogFile->write(EQEMuLog::Debug, "%s has requested a /rewind from %f, %f, %f, to %f, %f, %f in %s", GetName(), x_pos, y_pos, z_pos, rewind_x, rewind_y, rewind_z, zone->GetShortName()); + zonesummon_x = x_pos = x; + zonesummon_y = y_pos = y; + zonesummon_z = z_pos = z; + SetHeading(heading); + break; + default: + LogFile->write(EQEMuLog::Error, "Client::ZonePC() received a reguest to perform an unsupported client zone operation."); + ReadyToZone = false; + break; + } + + if(ReadyToZone) { + zone_mode = zm; + if(zm == ZoneToBindPoint) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZonePlayerToBind, sizeof(ZonePlayerToBind_Struct) + iZoneNameLength); + ZonePlayerToBind_Struct* gmg = (ZonePlayerToBind_Struct*) outapp->pBuffer; + + // If we are SoF and later and are respawning from hover, we want the real zone ID, else zero to use the old hack. + // + if((GetClientVersionBit() & BIT_SoFAndLater) && (!RuleB(Character, RespawnFromHover) || !IsHoveringForRespawn())) + gmg->bind_zone_id = 0; + else + gmg->bind_zone_id = zoneID; + + gmg->x = x; + gmg->y = y; + gmg->z = z; + gmg->heading = heading; + strcpy(gmg->zone_name, pZoneName); + + outapp->priority = 6; + FastQueuePacket(&outapp); + safe_delete(outapp); + } + else if(zm == ZoneSolicited || zm == ZoneToSafeCoords) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RequestClientZoneChange, sizeof(RequestClientZoneChange_Struct)); + RequestClientZoneChange_Struct* gmg = (RequestClientZoneChange_Struct*) outapp->pBuffer; + + gmg->zone_id = zoneID; + gmg->x = x; + gmg->y = y; + gmg->z = z; + gmg->heading = heading; + gmg->instance_id = instance_id; + gmg->type = 0x01; //an observed value, not sure of meaning + + outapp->priority = 6; + FastQueuePacket(&outapp); + safe_delete(outapp); + } + else if(zm == EvacToSafeCoords) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RequestClientZoneChange, sizeof(RequestClientZoneChange_Struct)); + RequestClientZoneChange_Struct* gmg = (RequestClientZoneChange_Struct*) outapp->pBuffer; + + // if we are in the same zone we want to evac to, client will not send OP_ZoneChange back to do an actual + // zoning of the client, so we have to send a viable zoneid that the client *could* zone to to make it believe + // we are leaving the zone, even though we are not. We have to do this because we are missing the correct op code + // and struct that should be used for evac/succor. + // 213 is Plane of War + // 76 is orignial Plane of Hate + // WildcardX 27 January 2008. Tested this for 6.2 and Titanium clients. + + if(this->GetZoneID() == 1) + gmg->zone_id = 2; + else if(this->GetZoneID() == 2) + gmg->zone_id = 1; + else + gmg->zone_id = 1; + + gmg->x = x; + gmg->y = y; + gmg->z = z; + gmg->heading = heading; + gmg->instance_id = instance_id; + gmg->type = 0x01; // '0x01' was an observed value for the type field, not sure of meaning + + // we hide the real zoneid we want to evac/succor to here + zonesummon_id = zoneID; + + outapp->priority = 6; + FastQueuePacket(&outapp); + safe_delete(outapp); + } + else { + if(zoneID == this->GetZoneID()) { + //properly handle proximities + entity_list.ProcessMove(this, x_pos, y_pos, z_pos); + proximity_x = x_pos; + proximity_y = y_pos; + proximity_z = z_pos; + + //send out updates to people in zone. + SendPosition(); + +#ifdef PACKET_UPDATE_MANAGER + //flush our position queues because we dont know where we will end up + update_manager.FlushQueues(); +#endif + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RequestClientZoneChange, sizeof(RequestClientZoneChange_Struct)); + RequestClientZoneChange_Struct* gmg = (RequestClientZoneChange_Struct*) outapp->pBuffer; + + gmg->zone_id = zoneID; + gmg->x = x; + gmg->y = y; + gmg->z = z; + gmg->heading = heading; + gmg->instance_id = instance_id; + gmg->type = 0x01; //an observed value, not sure of meaning + outapp->priority = 6; + FastQueuePacket(&outapp); + safe_delete(outapp); + } + + LogFile->write(EQEMuLog::Debug, "Player %s has requested a zoning to LOC x=%f, y=%f, z=%f, heading=%f in zoneid=%i", GetName(), x, y, z, heading, zoneID); + //Clear zonesummon variables if we're zoning to our own zone + //Client wont generate a zone change packet to the server in this case so + //They aren't needed and it keeps behavior on next zone attempt from being undefined. + if(zoneID == zone->GetZoneID() && instance_id == zone->GetInstanceID()) + { + if(zm != EvacToSafeCoords && zm != ZoneToSafeCoords && zm != ZoneToBindPoint) + { + zonesummon_x = 0; + zonesummon_y = 0; + zonesummon_z = 0; + zonesummon_id = 0; + zonesummon_ignorerestrictions = 0; + zone_mode = ZoneUnsolicited; + } + } + } + + safe_delete_array(pZoneName); +} + +void Client::GoToSafeCoords(uint16 zone_id, uint16 instance_id) { + if(zone_id == 0) + zone_id = zone->GetZoneID(); + + MovePC(zone_id, instance_id, 0.0f, 0.0f, 0.0f, 0.0f, 0, ZoneToSafeCoords); +} + + +void Mob::Gate() { + GoToBind(); +} + +void Client::Gate() { + Mob::Gate(); +} + +void NPC::Gate() { + entity_list.MessageClose_StringID(this, true, 200, MT_Spells, GATES, GetCleanName()); + + Mob::Gate(); +} + +void Client::SetBindPoint(int to_zone, float new_x, float new_y, float new_z) { + if (to_zone == -1) { + m_pp.binds[0].zoneId = zone->GetZoneID(); + 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].x = new_x; + m_pp.binds[0].y = new_y; + m_pp.binds[0].z = new_z; + } +} + +void Client::GoToBind(uint8 bindnum) { + // if the bind number is invalid, use the primary bind + if(bindnum > 4) + bindnum = 0; + + // 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); + 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); +} + +void Client::GoToDeath() { + MovePC(m_pp.binds[0].zoneId, 0.0f, 0.0f, 0.0f, 0.0f, 1, ZoneToBindPoint); +} + +void Client::SetZoneFlag(uint32 zone_id) { + if(HasZoneFlag(zone_id)) + return; + + 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); + } +} + +void Client::ClearZoneFlag(uint32 zone_id) { + if(!HasZoneFlag(zone_id)) + return; + + 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); + } +} + +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); +} + +bool Client::HasZoneFlag(uint32 zone_id) const { + return(zone_flags.find(zone_id) != zone_flags.end()); +} + +void Client::SendZoneFlagInfo(Client *to) const { + if(zone_flags.empty()) { + to->Message(0, "%s has no zone flags.", GetName()); + return; + } + + set::const_iterator cur, end; + cur = zone_flags.begin(); + end = zone_flags.end(); + char empty[1] = { '\0' }; + + to->Message(0, "Flags for %s:", GetName()); + + for(; cur != end; cur++) { + uint32 zoneid = *cur; + + const char *short_name = database.GetZoneName(zoneid); + + char *long_name = NULL; + database.GetZoneLongName(short_name, &long_name); + if(long_name == NULL) + long_name = empty; + + float safe_x, safe_y, safe_z; + int16 minstatus = 0; + uint8 minlevel = 0; + char flag_name[128]; + if(!database.GetSafePoints(short_name, 0, &safe_x, &safe_y, &safe_z, &minstatus, &minlevel, flag_name)) { + strcpy(flag_name, "(ERROR GETTING NAME)"); + } + + to->Message(0, "Has Flag %s for zone %s (%d,%s)", flag_name, long_name, zoneid, short_name); + if(long_name != empty) + delete[] long_name; + } +} + +bool Client::CanBeInZone() { + //check some critial rules to see if this char needs to be booted from the zone + //only enforce rules here which are serious enough to warrant being kicked from + //the zone + + if(Admin() >= RuleI(GM, MinStatusToZoneAnywhere)) + return(true); + + float safe_x, safe_y, safe_z; + int16 minstatus = 0; + uint8 minlevel = 0; + char flag_needed[128]; + if(!database.GetSafePoints(zone->GetShortName(), zone->GetInstanceVersion(), &safe_x, &safe_y, &safe_z, &minstatus, &minlevel, flag_needed)) { + //this should not happen... + _log(CLIENT__ERROR, "Unable to query zone info for ourself '%s'", zone->GetShortName()); + return(false); + } + + if(GetLevel() < minlevel) { + _log(CLIENT__ERROR, "Character does not meet min level requirement (%d < %d)!", GetLevel(), minlevel); + return(false); + } + if(Admin() < minstatus) { + _log(CLIENT__ERROR, "Character does not meet min status requirement (%d < %d)!", Admin(), minstatus); + return(false); + } + + if(flag_needed[0] != '\0') { + //the flag needed string is not empty, meaning a flag is required. + if(Admin() < minStatusToIgnoreZoneFlags && !HasZoneFlag(zone->GetZoneID())) { + _log(CLIENT__ERROR, "Character does not have the flag to be in this zone (%s)!", flag_needed); + return(false); + } + } + + return(true); +}